Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: crystal-lang/crystal
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 49f52ea1e3ab
Choose a base ref
...
head repository: crystal-lang/crystal
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: e637f567784e
Choose a head ref
  • 4 commits
  • 6 files changed
  • 1 contributor

Commits on Dec 3, 2016

  1. Rename Set#merge to Set#merge!. Fixes #3625

    Ary Borenszweig committed Dec 3, 2016
    Copy the full SHA
    b8894e5 View commit details
  2. Set: use backticks in code examples, so the formatter is used. Also f…

    …avor Set{...} instead of Set.new([...]).
    Ary Borenszweig committed Dec 3, 2016
    Copy the full SHA
    50dd1fd View commit details
  3. Copy the full SHA
    fda7873 View commit details
  4. Copy the full SHA
    e637f56 View commit details
Showing with 163 additions and 82 deletions.
  1. +18 −0 spec/std/http/headers_spec.cr
  2. +2 −2 spec/std/set_spec.cr
  3. +1 −1 src/compiler/crystal/compiler.cr
  4. +15 −6 src/http/headers.cr
  5. +118 −70 src/set.cr
  6. +9 −3 src/slice.cr
18 changes: 18 additions & 0 deletions spec/std/http/headers_spec.cr
Original file line number Diff line number Diff line change
@@ -147,6 +147,24 @@ describe HTTP::Headers do
headers.includes_word?("foo", "ba").should be_false
end

it "matches word with comma separated value, case insensitive (#3626)" do
headers = HTTP::Headers{"foo" => "BaR, BAZ"}
headers.includes_word?("foo", "bar").should be_true
headers.includes_word?("foo", "baz").should be_true
headers.includes_word?("foo", "BAR").should be_true
headers.includes_word?("foo", "ba").should be_false
end

it "doesn't match empty string" do
headers = HTTP::Headers{"foo" => "bar, baz"}
headers.includes_word?("foo", "").should be_false
end

it "matches word with comma separated value, partial match" do
headers = HTTP::Headers{"foo" => "bar, bazo, baz"}
headers.includes_word?("foo", "baz").should be_true
end

it "matches word among headers" do
headers = HTTP::Headers.new
headers.add("foo", "bar")
4 changes: 2 additions & 2 deletions spec/std/set_spec.cr
Original file line number Diff line number Diff line change
@@ -104,13 +104,13 @@ describe "Set" do
describe "merge" do
it "adds all the other elements" do
set = Set{1, 4, 8}
set.merge [1, 9, 10]
set.merge! [1, 9, 10]
set.should eq(Set{1, 4, 8, 9, 10})
end

it "returns self" do
set = Set{1, 4, 8}
set.merge([1, 9, 10]).should eq(Set{1, 4, 8, 9, 10})
set.merge!([1, 9, 10]).should eq(Set{1, 4, 8, 9, 10})
end
end

2 changes: 1 addition & 1 deletion src/compiler/crystal/compiler.cr
Original file line number Diff line number Diff line change
@@ -152,7 +152,7 @@ module Crystal
program.cache_dir = CacheDir.instance.directory_for(sources)
program.target_machine = target_machine
program.flags << "release" if @release
program.flags.merge @flags
program.flags.merge! @flags
program.wants_doc = wants_doc?
program.color = color?
program.stdout = stdout
21 changes: 15 additions & 6 deletions src/http/headers.cr
Original file line number Diff line number Diff line change
@@ -79,14 +79,23 @@ struct HTTP::Headers
# headers.includes_word?("Connection", "Upgrade") # => true
# ```
def includes_word?(key, word)
return false if word.empty?

word = word.downcase
# iterates over all header values avoiding the concatenation
get?(key).try &.each do |value|
start = value.index(word)
next unless start
# check if the match is not surrounded by alphanumeric chars
next if start > 0 && value[start - 1].ascii_alphanumeric?
next if start + word.size < value.size && value[start + word.size].alphanumeric?
return true
value = value.downcase
offset = 0
while true
start = value.index(word, offset)
break unless start
offset = start + word.size

# check if the match is not surrounded by alphanumeric chars
next if start > 0 && value[start - 1].ascii_alphanumeric?
next if start + word.size < value.size && value[start + word.size].ascii_alphanumeric?
return true
end
end

false
188 changes: 118 additions & 70 deletions src/set.cr
Original file line number Diff line number Diff line change
@@ -4,31 +4,32 @@
#
# Set uses `Hash` as storage, so you must note the following points:
#
# * Equality of elements is determined according to `Object#==` and
# `Object#hash`.
# * Set assumes that the identity of each element does not change while it is
# stored. Modifying an element of a set will render the set to an unreliable
# state.
# * Equality of elements is determined according to `Object#==` and `Object#hash`.
# * Set assumes that the identity of each element does not change while it is stored. Modifying an element of a set will render the set to an unreliable state.
#
# ### Example
#
# s1 = Set{1, 2}
# s2 = [1, 2].to_set
# s3 = Set.new [1, 2]
# s1 == s2 # => true
# s1 == s3 # => true
# s1.add(2)
# s1.merge([6,8])
# s1.subset? s2 # => false
# s2.subset? s1 # => true
# ```
# s1 = Set{1, 2}
# s2 = [1, 2].to_set
# s3 = Set.new [1, 2]
# s1 == s2 # => true
# s1 == s3 # => true
# s1.add(2)
# s1.merge!([6, 8])
# s1.subset? s2 # => false
# s2.subset? s1 # => true
# ```
struct Set(T)
include Enumerable(T)
include Iterable(T)

# Creates a new, empty `Set`
#
# s = Set(Int32).new
# set.empty? # => true
# ```
# s = Set(Int32).new
# set.empty? # => true
# ```
#
# An initial capacity can be specified, and it will be set as the initial capacity
# of the internal Hash.
@@ -38,10 +39,13 @@ struct Set(T)

# Creates a new set from the elements in `enumerable`
#
# s = Set.new [1,3,5]
# s.empty? => false
# ```
# a = [1, 3, 5]
# s = Set.new a
# s.empty? # => false
# ```
def self.new(enumerable : Enumerable(T))
Set(T).new.merge(enumerable)
Set(T).new.merge!(enumerable)
end

# Alias for `add`
@@ -51,70 +55,86 @@ struct Set(T)

# Adds `object` to the set and returns `self`
#
# s = Set.new [1,5]
# s.includes? 8 # => false
# s << 8
# s.includes? 8 # => true
# ```
# s = Set{1, 5}
# s.includes? 8 # => false
# s << 8
# s.includes? 8 # => true
# ```
def add(object : T)
@hash[object] = nil
self
end

# Adds `#each` element of *elems* to the set and returns `self`.
#
# s = Set.new [1,5]
# s.merge [5,5,8,9]
# s.size # => 4
def merge(elems)
# ```
# s = Set{1, 5}
# s.merge! [5, 5, 8, 9]
# s.size # => 4
# ```
#
# See also `#|` to merge two sets and return a new one.
def merge!(elems)
elems.each { |elem| self << elem }
self
end

# Returns `true` if *object* exists in the set.
#
# s = Set.new [1,5]
# s.includes? 5 # => true
# s.includes? 9 # => false
# ```
# s = Set{1, 5}
# s.includes? 5 # => true
# s.includes? 9 # => false
# ```
def includes?(object)
@hash.has_key?(object)
end

# Removes the *object* from the set and returns `self`.
#
# s = Set.new [1,5]
# s.includes? 5 # => true
# s.delete 5
# s.includes? 5 # => false
# ```
# s = Set{1, 5}
# s.includes? 5 # => true
# s.delete 5
# s.includes? 5 # => false
# ```
def delete(object)
@hash.delete(object)
self
end

# Returns the number of elements in the set.
#
# s = Set.new [1,5]
# s.size # => 2
# ```
# s = Set{1, 5}
# s.size # => 2
# ```
def size
@hash.size
end

# Removes all elements in the set, and returns `self`.
#
# s = Set.new [1,5]
# s.size # => 2
# s.clear
# s.size # => 0
# ```
# s = Set{1, 5}
# s.size # => 2
# s.clear
# s.size # => 0
# ```
def clear
@hash.clear
self
end

# Returns `true` if the set is empty.
#
# s = Set(Int32).new
# s.empty? # => true
# s << 3
# s.empty? # => false
# ```
# s = Set(Int32).new
# s.empty? # => true
# s << 3
# s.empty? # => false
# ```
def empty?
@hash.empty?
end
@@ -134,8 +154,10 @@ struct Set(T)

# Intersection: returns a new set containing elements common to both sets.
#
# Set.new([1,1,3,5]) & Set.new([1,2,3]) #=> Set{1, 3}
# Set.new(['a','b','b','z']) & Set.new(['a','b','c']) #=> Set{'a', 'b'}
# ```
# Set{1, 1, 3, 5} & Set{1, 2, 3} # => Set{1, 3}
# Set{'a', 'b', 'b', 'z'} & Set{'a', 'b', 'c'} # => Set{'a', 'b'}
# ```
def &(other : Set)
set = Set(T).new
each do |value|
@@ -146,8 +168,12 @@ struct Set(T)

# Union: returns a new set containing all unique elements from both sets.
#
# Set.new([1,1,3,5]) | Set.new([1,2,3]) #=> Set{1, 3, 5, 2}
# Set.new(['a','b','b','z']) | Set.new(['a','b','c']) #=> Set{'a', 'b', 'z', 'c'}
# ```
# Set{1, 1, 3, 5} | Set{1, 2, 3} # => Set{1, 3, 5, 2}
# Set{'a', 'b', 'b', 'z'} | Set{'a', 'b', 'c'} # => Set{'a', 'b', 'z', 'c'}
# ```
#
# See also `#merge` to add elements from a set to `self`.
def |(other : Set(U)) forall U
set = Set(T | U).new
each { |value| set.add value }
@@ -158,8 +184,10 @@ struct Set(T)
# Difference: returns a new set containing elements in this set that are not
# present in the other.
#
# Set.new([1,2,3,4,5]) - Set.new([2,4]) #=> Set{1, 3, 5}
# Set.new(['a','b','b','z']) - Set.new(['a','b','c']) #=> Set{'z'}
# ```
# Set{1, 2, 3, 4, 5} - Set{2, 4} # => Set{1, 3, 5}
# Set{'a', 'b', 'b', 'z'} - Set{'a', 'b', 'c'} # => Set{'z'}
# ```
def -(other : Set)
set = Set(T).new
each do |value|
@@ -171,17 +199,21 @@ struct Set(T)
# Difference: returns a new set containing elements in this set that are not
# present in the other enumerable.
#
# Set.new([1,2,3,4,5]) - [2,4] #=> Set{1, 3, 5}
# Set.new(['a','b','b','z']) - ['a','b','c'] #=> Set{'z'}
# ```
# Set{1, 2, 3, 4, 5} - [2, 4] # => Set{1, 3, 5}
# Set{'a', 'b', 'b', 'z'} - ['a', 'b', 'c'] # => Set{'z'}
# ```
def -(other : Enumerable)
dup.subtract other
end

# Symmetric Difference: returns a new set `(self - other) | (other - self)`.
# Equivalently, returns `(self | other) - (self & other)`.
#
# Set.new([1,2,3,4,5]) ^ Set.new([2,4,6]) #=> Set{1, 3, 5, 6}
# Set.new(['a','b','b','z']) ^ Set.new(['a','b','c']) #=> Set{'z', 'c'}
# ```
# Set{1, 2, 3, 4, 5} ^ Set{2, 4, 6} # => Set{1, 3, 5, 6}
# Set{'a', 'b', 'b', 'z'} ^ Set{'a', 'b', 'c'} # => Set{'z', 'c'}
# ```
def ^(other : Set(U)) forall U
set = Set(T | U).new
each do |value|
@@ -196,10 +228,12 @@ struct Set(T)
# Symmetric Difference: returns a new set `(self - other) | (other - self)`.
# Equivalently, returns `(self | other) - (self & other)`.
#
# Set.new([1,2,3,4,5]) ^ [2,4,6] #=> Set{1, 3, 5, 6}
# Set.new(['a','b','b','z']) ^ ['a','b','c'] #=> Set{'z', 'c'}
# ```
# Set{1, 2, 3, 4, 5} ^ [2, 4, 6] # => Set{1, 3, 5, 6}
# Set{'a', 'b', 'b', 'z'} ^ ['a', 'b', 'c'] # => Set{'z', 'c'}
# ```
def ^(other : Enumerable(U)) forall U
set = Set(T | U).new.merge(self)
set = Set(T | U).new(self)
other.each do |value|
if includes?(value)
set.delete value
@@ -213,8 +247,10 @@ struct Set(T)
# Returns `self` after removing from it those elements that are present in
# the given enumerable.
#
# Set.new(['a','b','b','z']).subtract Set.new(['a','b','c']) #=> Set{'z'}
# Set.new([1,2,3,4,5]).subtract [2,4,6] #=> Set{1, 3, 5}
# ```
# Set{'a', 'b', 'b', 'z'}.subtract Set{'a', 'b', 'c'} # => Set{'z'}
# Set{1, 2, 3, 4, 5}.subtract [2, 4, 6] # => Set{1, 3, 5}
# ```
def subtract(other : Enumerable)
other.each do |value|
delete value
@@ -224,14 +260,16 @@ struct Set(T)

# Returns `true` if both sets have the same elements
#
# Set.new([1,5]) == Set.new([1,5]) # => true
# ```
# Set{1, 5} == Set{1, 5} # => true
# ```
def ==(other : Set)
same?(other) || @hash == other.@hash
end

# Returns a new set with all of the same elements
def dup
Set(T).new.merge(self)
Set.new(self)
end

# Returns a new set with all of the elements cloned.
@@ -245,7 +283,9 @@ struct Set(T)

# Returns the elements as an array
#
# Set.new([1,5]).to_a # => [1,5]
# ```
# Set{1, 5}.to_a # => [1,5]
# ```
def to_a
@hash.keys
end
@@ -290,8 +330,10 @@ struct Set(T)
# This set must have the same or fewer elements than the `other` set, and all
# of elements in this set must be present in the `other` set.
#
# Set.new([1,5]).subset? Set.new([1,3,5]) # => true
# Set.new([1,3,5]).subset? Set.new([1,3,5]) # => true
# ```
# Set{1, 5}.subset? Set{1, 3, 5} # => true
# Set{1, 3, 5}.subset? Set{1, 3, 5} # => true
# ```
def subset?(other : Set)
return false if other.size < size
all? { |value| other.includes?(value) }
@@ -302,8 +344,10 @@ struct Set(T)
# This set must have fewer elements than the `other` set, and all
# of elements in this set must be present in the `other` set.
#
# Set.new([1,5]).subset? Set.new([1,3,5]) # => true
# Set.new([1,3,5]).subset? Set.new([1,3,5]) # => false
# ```
# Set{1, 5}.subset? Set{1, 3, 5} # => true
# Set{1, 3, 5}.subset? Set{1, 3, 5} # => false
# ```
def proper_subset?(other : Set)
return false if other.size <= size
all? { |value| other.includes?(value) }
@@ -314,8 +358,10 @@ struct Set(T)
# The `other` must have the same or fewer elements than this set, and all of
# elements in the `other` set must be present in this set.
#
# Set.new([1,3,5]).superset? Set.new([1,5]) # => true
# Set.new([1,3,5]).superset? Set.new([1,3,5]) # => true
# ```
# Set{1, 3, 5}.superset? Set{1, 5} # => true
# Set{1, 3, 5}.superset? Set{1, 3, 5} # => true
# ```
def superset?(other : Set)
other.subset?(self)
end
@@ -325,8 +371,10 @@ struct Set(T)
# The `other` must have the same or fewer elements than this set, and all of
# elements in the `other` set must be present in this set.
#
# Set.new([1,3,5]).superset? Set.new([1,5]) # => true
# Set.new([1,3,5]).superset? Set.new([1,3,5]) # => false
# ```
# Set{1, 3, 5}.superset? Set{1, 5} # => true
# Set{1, 3, 5}.superset? Set{1, 3, 5} # => false
# ```
def proper_superset?(other : Set)
other.proper_subset?(self)
end
12 changes: 9 additions & 3 deletions src/slice.cr
Original file line number Diff line number Diff line change
@@ -31,11 +31,11 @@ struct Slice(T)
{% if @type.name != "Slice(T)" && T < Number %}
{{T}}.slice({{*args}})
{% else %}
%slice = Slice(typeof({{*args}})).new({{args.size}})
%ptr = Pointer(typeof({{*args}})).malloc({{args.size}})
{% for arg, i in args %}
%slice.to_unsafe[{{i}}] = {{arg}}
%ptr[{{i}}] = {{arg}}
{% end %}
%slice
Slice.new(%ptr, {{args.size}})
{% end %}
end

@@ -68,11 +68,17 @@ struct Slice(T)
# The memory is allocated by the `GC`, so when there are
# no pointers to this memory, it will be automatically freed.
#
# Only works for primitive integers and floats (UInt8, Int32, Float64, etc.)
#
# ```
# slice = Slice(UInt8).new(3)
# slice # => [0, 0, 0]
# ```
def self.new(size : Int)
{% unless T <= Int::Primitive || T <= Float::Primitive %}
{% raise "can only use primitive integers and floats with Slice.new(size), not #{T}" %}
{% end %}

pointer = Pointer(T).malloc(size)
new(pointer, size)
end