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: ed0e47b2c347
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: f104e7a85179
Choose a head ref
  • 4 commits
  • 7 files changed
  • 1 contributor

Commits on Aug 4, 2016

  1. Copy the full SHA
    11416be View commit details
  2. Indexable: added index overloads that accept an offset

    Ary Borenszweig committed Aug 4, 2016
    Copy the full SHA
    ebe9b7d View commit details
  3. String: optimize index and rindex for ascii char case (delegating…

    … to slice)
    Ary Borenszweig committed Aug 4, 2016
    Copy the full SHA
    25ff44b View commit details
  4. Added Char#ascii? and use it in a few places

    Ary Borenszweig committed Aug 4, 2016
    Copy the full SHA
    f104e7a View commit details
Showing with 110 additions and 17 deletions.
  1. +29 −0 spec/std/array_spec.cr
  2. +7 −0 spec/std/char_spec.cr
  3. +6 −0 src/char.cr
  4. +55 −4 src/indexable.cr
  5. +1 −1 src/io.cr
  6. +11 −11 src/string.cr
  7. +1 −1 src/uri.cr
29 changes: 29 additions & 0 deletions spec/std/array_spec.cr
Original file line number Diff line number Diff line change
@@ -620,12 +620,24 @@ describe "Array" do
a.index(4).should be_nil
end

it "performs without a block and offset" do
a = [1, 2, 3, 1, 2, 3]
a.index(3, offset: 3).should eq(5)
a.index(3, offset: -3).should eq(5)
end

it "performs with a block" do
a = [1, 2, 3]
a.index { |i| i > 1 }.should eq(1)
a.index { |i| i > 3 }.should be_nil
end

it "performs with a block and offset" do
a = [1, 2, 3, 1, 2, 3]
a.index(offset: 3) { |i| i > 1 }.should eq(4)
a.index(offset: -3) { |i| i > 1 }.should eq(4)
end

it "raises if out of bounds" do
expect_raises IndexError do
[1, 2, 3][4]
@@ -821,11 +833,28 @@ describe "Array" do
a.rindex(7).should be_nil
end

it "performs without a block and an offset" do
a = [1, 2, 3, 4, 5, 3, 6]
a.rindex(3, offset: 4).should eq(2)
a.rindex(6, offset: 4).should be_nil
a.rindex(3, offset: -2).should eq(5)
a.rindex(3, offset: -3).should eq(2)
a.rindex(3, offset: -100).should be_nil
end

it "performs with a block" do
a = [1, 2, 3, 4, 5, 3, 6]
a.rindex { |i| i > 1 }.should eq(6)
a.rindex { |i| i > 6 }.should be_nil
end

it "performs with a block and offset" do
a = [1, 2, 3, 4, 5, 3, 6]
a.rindex { |i| i > 1 }.should eq(6)
a.rindex { |i| i > 6 }.should be_nil
a.rindex(offset: 4) { |i| i == 3 }.should eq(2)
a.rindex(offset: -3) { |i| i == 3 }.should eq(2)
end
end

describe "sample" do
7 changes: 7 additions & 0 deletions spec/std/char_spec.cr
Original file line number Diff line number Diff line change
@@ -344,6 +344,13 @@ describe "Char" do
'\u0019'.control?.should be_true
end

it "does ascii?" do
'a'.ascii?.should be_true
127.chr.ascii?.should be_true
128.chr.ascii?.should be_false
'酒'.ascii?.should be_false
end

describe "clone" do
assert { 'a'.clone.should eq('a') }
end
6 changes: 6 additions & 0 deletions src/char.cr
Original file line number Diff line number Diff line change
@@ -98,6 +98,12 @@ struct Char
self - other
end

# Returns `true` if this char is an ASCII character
# (codepoint is in (0..127))
def ascii?
ord < 128
end

# Returns `true` if this char is an ASCII digit in specified base.
#
# Base can be from 0 to 36 with digits from '0' to '9' and 'a' to 'z' or 'A' to 'Z'.
59 changes: 55 additions & 4 deletions src/indexable.cr
Original file line number Diff line number Diff line change
@@ -271,6 +271,34 @@ module Indexable(T)
end
end

# Returns the index of the first appearance of *value* in `self`
# starting from the given *offset*, or `nil` if the value is not in `self`.
#
# ```
# [1, 2, 3, 1, 2, 3].index(2, offset: 2) # => 4
# ```
def index(object, offset : Int = 0)
index(offset) { |e| e == object }
end

# Returns the index of the first object in `self` for which the block
# returns `true`, starting from the given *offset*, or `nil` if no match
# is found.
#
# ```
# [1, 2, 3, 1, 2, 3].rindex(offset: 4) { |x| x < 2 } # => 4
# ```
def index(offset : Int = 0)
offset += size if offset < 0

offset.upto(size - 1) do |i|
if yield unsafe_at(i)
return i
end
end
nil
end

# Returns the last element of `self` if it's not empty, or raises `IndexError`.
#
# ```
@@ -314,12 +342,35 @@ module Indexable(T)
ReverseItemIterator(self, T).new(self)
end

def rindex(value)
rindex { |elem| elem == value }
# Returns the index of the last appearance of *value* in `self`, or
# `nil` if the value is not in `self`.
#
# If *offset* is given, it defines the position to _end_ the search
# (elements beyond this point are ignored).
#
# ```
# [1, 2, 3, 2, 3].rindex(2) # => 3
# [1, 2, 3, 2, 3].rindex(2, offset: 2) # => 1
# ```
def rindex(value, offset = size - 1)
rindex(offset) { |elem| elem == value }
end

def rindex
(size - 1).downto(0) do |i|
# Returns the index of the first object in `self` for which the block
# returns `true`, starting from the last object, or `nil` if no match
# is found.
#
# If *offset* is given, the search starts from that index towards the
# first elements in `self`.
#
# ```
# [1, 2, 3, 2, 3].rindex { |x| x < 3 } # => 3
# [1, 2, 3, 2, 3].rindex(offset: 2) { |x| x < 3 } # => 1
# ```
def rindex(offset = size - 1)
offset += size if offset < 0

offset.downto(0) do |i|
if yield unsafe_at(i)
return i
end
2 changes: 1 addition & 1 deletion src/io.cr
Original file line number Diff line number Diff line change
@@ -588,7 +588,7 @@ module IO

# # If the char's representation is a single byte and we have an encoding,
# search the delimiter in the buffer
if delimiter.ord < 0x80 && (decoder = decoder())
if delimiter.ascii? && (decoder = decoder())
return decoder.gets(self, delimiter.ord.to_u8, limit)
end

22 changes: 11 additions & 11 deletions src/string.cr
Original file line number Diff line number Diff line change
@@ -2075,6 +2075,11 @@ class String
# "Hello, World".index("H", 2) # => nil
# ```
def index(search : Char, offset = 0)
# If it's ASCII we can delegate to slice
if search.ascii?
return to_slice.index(search.ord.to_u8, offset)
end

offset += size if offset < 0
return nil if offset < 0

@@ -2119,19 +2124,14 @@ class String
# "Hello, World".rindex("H", 2) # => nil
# ```
def rindex(search : Char, offset = size - 1)
# If it's ASCII we can delegate to slice
if search.ascii?
return to_slice.rindex(search.ord.to_u8, offset)
end

offset += size if offset < 0
return nil if offset < 0

# If it's ASCII we can search from the end
if search.ord < 0x80
offset.downto(0) do |i|
if to_unsafe[i] == search.ord
return i
end
end
return nil
end

last_index = nil

each_char_with_index do |char, i|
@@ -3043,7 +3043,7 @@ class String
def ends_with?(char : Char)
return false unless bytesize > 0

if char.ord < 0x80 || ascii_only?
if char.ascii? || ascii_only?
return to_unsafe[bytesize - 1] == char.ord
end

2 changes: 1 addition & 1 deletion src/uri.cr
Original file line number Diff line number Diff line change
@@ -262,7 +262,7 @@ class URI
char = byte.unsafe_chr
if char == ' ' && space_to_plus
io.write_byte '+'.ord.to_u8
elsif byte < 0x80 && yield(byte) && (!space_to_plus || char != '+')
elsif char.ascii? && yield(byte) && (!space_to_plus || char != '+')
io.write_byte byte
else
io.write_byte '%'.ord.to_u8