Skip to content

Commit

Permalink
Add StringPool#get? (#14508)
Browse files Browse the repository at this point in the history
  • Loading branch information
HertzDevil committed Apr 23, 2024
1 parent 6a38569 commit 1f76cf6
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 8 deletions.
19 changes: 19 additions & 0 deletions spec/std/string_pool_spec.cr
Expand Up @@ -58,6 +58,25 @@ describe StringPool do
pool.size.should eq(1)
end

it "#get?" do
pool = StringPool.new
str = "foo"

pool.get?(str).should be_nil
pool.get?(str.to_slice).should be_nil
pool.get?(str.to_unsafe, str.bytesize).should be_nil
pool.get?(IO::Memory.new(str)).should be_nil

s = pool.get(str)

pool.get?(str).should be(s)
pool.get?(str.to_slice).should be(s)
pool.get?(str.to_unsafe, str.bytesize).should be(s)
pool.get?(IO::Memory.new(str)).should be(s)

pool.size.should eq(1)
end

it "puts many" do
pool = StringPool.new
10_000.times do |i|
Expand Down
89 changes: 81 additions & 8 deletions src/string_pool.cr
Expand Up @@ -86,6 +86,24 @@ class StringPool
get slice.to_unsafe, slice.size
end

# Returns a `String` with the contents of the given *slice*, or `nil` if no
# such string exists in the pool.
#
# ```
# require "string_pool"
#
# pool = StringPool.new
# bytes = "abc".to_slice
# pool.get?(bytes) # => nil
# pool.empty? # => true
# pool.get(bytes) # => "abc"
# pool.empty? # => false
# pool.get?(bytes) # => "abc"
# ```
def get?(slice : Bytes) : String?
get? slice.to_unsafe, slice.size
end

# Returns a `String` with the contents given by the pointer *str* of size *len*.
#
# If a string with those contents was already present in the pool, that one is returned.
Expand All @@ -100,12 +118,35 @@ class StringPool
# ```
def get(str : UInt8*, len) : String
hash = hash(str, len)
get(hash, str, len)
get(hash, str, len) do |index|
rehash if @size >= @capacity // 4 * 3
@size += 1
entry = String.new(str, len)
@hashes[index] = hash
@values[index] = entry
entry
end
end

private def get(hash : UInt64, str : UInt8*, len)
rehash if @size >= @capacity // 4 * 3
# Returns a `String` with the contents given by the pointer *str* of size
# *len*, or `nil` if no such string exists in the pool.
#
# ```
# require "string_pool"
#
# pool = StringPool.new
# pool.get?("hey".to_unsafe, 3) # => nil
# pool.empty? # => true
# pool.get("hey".to_unsafe, 3) # => "hey"
# pool.empty? # => false
# pool.get?("hey".to_unsafe, 3) # => "hey"
# ```
def get?(str : UInt8*, len) : String?
hash = hash(str, len)
get(hash, str, len) { nil }
end

private def get(hash : UInt64, str : UInt8*, len, &)
mask = (@capacity - 1).to_u64
index = hash & mask
next_probe_offset = 1_u64
Expand All @@ -119,11 +160,7 @@ class StringPool
next_probe_offset += 1_u64
end

@size += 1
entry = String.new(str, len)
@hashes[index] = hash
@values[index] = entry
entry
yield index
end

private def put_on_rehash(hash : UInt64, entry : String)
Expand Down Expand Up @@ -157,6 +194,24 @@ class StringPool
get(str.buffer, str.bytesize)
end

# Returns a `String` with the contents of the given `IO::Memory`, or `nil` if
# no such string exists in the pool.
#
# ```
# require "string_pool"
#
# pool = StringPool.new
# io = IO::Memory.new "crystal"
# pool.get?(io) # => nil
# pool.empty? # => true
# pool.get(io) # => "crystal"
# pool.empty? # => false
# pool.get?(io) # => "crystal"
# ```
def get?(str : IO::Memory) : String?
get?(str.buffer, str.bytesize)
end

# Returns a `String` with the contents of the given string.
#
# If a string with those contents was already present in the pool, that one is returned.
Expand All @@ -175,6 +230,24 @@ class StringPool
get(str.to_unsafe, str.bytesize)
end

# Returns a `String` with the contents of the given string, or `nil` if no
# such string exists in the pool.
#
# ```
# require "string_pool"
#
# pool = StringPool.new
# string = "crystal"
# pool.get?(string) # => nil
# pool.empty? # => true
# pool.get(string) # => "crystal"
# pool.empty? # => false
# pool.get?(string) # => "crystal"
# ```
def get?(str : String) : String?
get?(str.to_unsafe, str.bytesize)
end

# Rebuilds the hash based on the current hash values for each key,
# if values of key objects have changed since they were inserted.
#
Expand Down

0 comments on commit 1f76cf6

Please sign in to comment.