Skip to content

Commit

Permalink
Add intern(::Type, ::AbstractString) to choose custom return type
Browse files Browse the repository at this point in the history
This allows returning standard String values from custom AbstractString
values, avoiding a copy. This is particularly useful for WeakRefString.
To achieve this, only call convert when the string is not found in the pool.
  • Loading branch information
nalimilan committed May 6, 2018
1 parent f3b0e04 commit 00df17e
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 6 deletions.
26 changes: 20 additions & 6 deletions src/corefunctionality.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@

# NOTE: This code is carefully optimised. Do not tweak it (for readability or otherwise) without benchmarking
@inline function intern!(wkd::WeakKeyDict{K}, key)::K where K
kk::K = convert(K, key)

lock(wkd.lock)
# hand positioning the locks and unlocks (rather than do block or try finally, seems to be faster)
index = Base.ht_keyindex2(wkd.ht, kk) # returns index if present, or -index if not
index = Base.ht_keyindex2(wkd.ht, key) # returns index if present, or -index if not
# note hash of weakref is equal to the hash of value, so avoid constructing it if not required
if index > 0
# found it
Expand All @@ -20,6 +18,7 @@
else
# Not found, so add it,
# and mark it as a reference we track to delete!
kk::K = convert(K, key)
finalizer(kk, wkd.finalizer) # finalizer is set on the strong ref
@inbounds Base._setindex!(wkd.ht, nothing, WeakRef(kk), -index)
unlock(wkd.lock)
Expand All @@ -40,19 +39,34 @@ end

###################################

"""
intern(s::T)
Return a reference to a interned instance of `s`,
adding it to the interning pool if it did not already exist.
"""
function intern(s::T)::T where T
intern!(get_pool(T), s)
intern(T, s)
end

intern(s::String)=intern!(get_pool(String), s) # Break stack-overflow
"""
intern(::Type{T}, s)
Intern `s` as if it were type `T`, converting it if required.
Note that this will lead to unexpected behavour if the type of `s`, and `T`,
do not have equivalent equality and hash functions
(i.e. this is not safe if `hash(s) != hash(convert(T, s))`).
"""
function intern(::Type{T}, s)::T where T
intern!(get_pool(T), s)
end


"""
Substrings are interned as their parent string type
"""
function intern(substr::SubString{T})::T where T
intern(T(substr))
intern(T, substr)
end


Expand Down
1 change: 1 addition & 0 deletions test/REQUIRE
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
WeakRefStrings 0.4.4
20 changes: 20 additions & 0 deletions test/all_kinds_of_types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@ using InternedStrings
@test !(ex1==="ex")
ex2 = intern("ex")
@test ex1===ex2
ex3 = intern(String, "ex")
@test ex1===ex3



@testset "type inference" begin
@test ex1 isa String
@test ex2 isa String
@inferred intern("ex")
@inferred intern(String, "ex")
end
end

Expand All @@ -21,17 +25,33 @@ end
@testset "SubString" begin
aa1, bb1, cc1 = intern.(split("aa bb cc"))
aa2, bb2, cc2 = intern.(split("aa bb cc"))
aa3, bb3, cc3 = intern.(String, split("aa bb cc"))

@test bb1=="bb"
@test !(bb1==="bb")
@test bb1===bb2
@test bb1===bb3

@testset "type inference" begin
@test intern(split("aa bb cc")[1]) isa String
@inferred intern(split("aa bb cc")[1])
@test intern(String, split("aa bb cc")[1]) isa String
@inferred intern(String, split("aa bb cc")[1])
end
end

@testset "WeakRefString" begin
using WeakRefStrings
s1 = "ex"
s2 = "ex"
ex1 = @inferred intern(String, WeakRefString(Vector{UInt8}(s1)))
@test ex1=="ex"
@test !(ex1===s1)
@test ex1 isa String
ex2 = @inferred intern(String, WeakRefString(Vector{UInt8}(s2)))
@test ex1===ex2
end

#== Uncomment when https://github.com/JuliaLang/julia/issues/26939 is fixed
@testset "BigFloat" begin let
pi1 = intern(BigFloat(π))
Expand Down

0 comments on commit 00df17e

Please sign in to comment.