Skip to content

Commit

Permalink
move IdDict, IdSet to their own files, rename table.c to iddict.c (Ju…
Browse files Browse the repository at this point in the history
  • Loading branch information
JeffBezanson committed Feb 7, 2020
1 parent 4ac9657 commit 8b6a8c2
Show file tree
Hide file tree
Showing 8 changed files with 199 additions and 197 deletions.
2 changes: 2 additions & 0 deletions base/Base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ include("baseext.jl")
include("ntuple.jl")

include("abstractdict.jl")
include("iddict.jl")
include("idset.jl")

include("iterators.jl")
using .Iterators: zip, enumerate, only
Expand Down
195 changes: 0 additions & 195 deletions base/abstractdict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -531,44 +531,6 @@ end
# hashing objects by identity
_tablesz(x::Integer) = x < 16 ? 16 : one(x)<<((sizeof(x)<<3)-leading_zeros(x-1))

"""
IdDict([itr])
`IdDict{K,V}()` constructs a hash table using object-id as hash and
`===` as equality with keys of type `K` and values of type `V`.
See [`Dict`](@ref) for further help.
"""
mutable struct IdDict{K,V} <: AbstractDict{K,V}
ht::Vector{Any}
count::Int
ndel::Int
IdDict{K,V}() where {K, V} = new{K,V}(Vector{Any}(undef, 32), 0, 0)

function IdDict{K,V}(itr) where {K, V}
d = IdDict{K,V}()
for (k,v) in itr; d[k] = v; end
d
end

function IdDict{K,V}(pairs::Pair...) where {K, V}
d = IdDict{K,V}()
sizehint!(d, length(pairs))
for (k,v) in pairs; d[k] = v; end
d
end

IdDict{K,V}(d::IdDict{K,V}) where {K, V} = new{K,V}(copy(d.ht), d.count, d.ndel)
end

IdDict() = IdDict{Any,Any}()
IdDict(kv::Tuple{}) = IdDict()

IdDict(ps::Pair{K,V}...) where {K,V} = IdDict{K,V}(ps)
IdDict(ps::Pair{K}...) where {K} = IdDict{K,Any}(ps)
IdDict(ps::(Pair{K,V} where K)...) where {V} = IdDict{Any,V}(ps)
IdDict(ps::Pair...) = IdDict{Any,Any}(ps)

TP{K,V} = Union{Type{Tuple{K,V}},Type{Pair{K,V}}}

dict_with_eltype(DT_apply, kv, ::TP{K,V}) where {K,V} = DT_apply(K, V)(kv)
Expand All @@ -584,163 +546,6 @@ function dict_with_eltype(DT_apply::F, kv::Generator, t) where F
return grow_to!(dict_with_eltype(DT_apply, T), kv)
end

function IdDict(kv)
try
dict_with_eltype((K, V) -> IdDict{K, V}, kv, eltype(kv))
catch
if !applicable(iterate, kv) || !all(x->isa(x,Union{Tuple,Pair}),kv)
throw(ArgumentError(
"IdDict(kv): kv needs to be an iterator of tuples or pairs"))
else
rethrow()
end
end
end

empty(d::IdDict, ::Type{K}, ::Type{V}) where {K, V} = IdDict{K,V}()

function rehash!(d::IdDict, newsz = length(d.ht))
d.ht = ccall(:jl_idtable_rehash, Vector{Any}, (Any, Csize_t), d.ht, newsz)
d
end

function sizehint!(d::IdDict, newsz)
newsz = _tablesz(newsz*2) # *2 for keys and values in same array
oldsz = length(d.ht)
# grow at least 25%
if newsz < (oldsz*5)>>2
return d
end
rehash!(d, newsz)
end

function setindex!(d::IdDict{K,V}, @nospecialize(val), @nospecialize(key)) where {K, V}
!isa(key, K) && throw(ArgumentError("$(limitrepr(key)) is not a valid key for type $K"))
if !(val isa V) # avoid a dynamic call
val = convert(V, val)
end
if d.ndel >= ((3*length(d.ht))>>2)
rehash!(d, max(length(d.ht)>>1, 32))
d.ndel = 0
end
inserted = RefValue{Cint}(0)
d.ht = ccall(:jl_eqtable_put, Array{Any,1}, (Any, Any, Any, Ptr{Cint}), d.ht, key, val, inserted)
d.count += inserted[]
return d
end

function get(d::IdDict{K,V}, @nospecialize(key), @nospecialize(default)) where {K, V}
val = ccall(:jl_eqtable_get, Any, (Any, Any, Any), d.ht, key, default)
val === default ? default : val::V
end
function getindex(d::IdDict{K,V}, @nospecialize(key)) where {K, V}
val = get(d, key, secret_table_token)
val === secret_table_token && throw(KeyError(key))
return val::V
end

function pop!(d::IdDict{K,V}, @nospecialize(key), @nospecialize(default)) where {K, V}
found = RefValue{Cint}(0)
val = ccall(:jl_eqtable_pop, Any, (Any, Any, Any, Ptr{Cint}), d.ht, key, default, found)
if found[] === Cint(0)
return default
else
d.count -= 1
d.ndel += 1
return val::V
end
end

function pop!(d::IdDict{K,V}, @nospecialize(key)) where {K, V}
val = pop!(d, key, secret_table_token)
val === secret_table_token && throw(KeyError(key))
return val::V
end

function delete!(d::IdDict{K}, @nospecialize(key)) where K
pop!(d, key, secret_table_token)
d
end

function empty!(d::IdDict)
resize!(d.ht, 32)
ccall(:memset, Ptr{Cvoid}, (Ptr{Cvoid}, Cint, Csize_t), d.ht, 0, sizeof(d.ht))
d.ndel = 0
d.count = 0
return d
end

_oidd_nextind(a, i) = reinterpret(Int, ccall(:jl_eqtable_nextind, Csize_t, (Any, Csize_t), a, i))

function iterate(d::IdDict{K,V}, idx=0) where {K, V}
idx = _oidd_nextind(d.ht, idx)
idx == -1 && return nothing
return (Pair{K, V}(d.ht[idx + 1]::K, d.ht[idx + 2]::V), idx + 2)
end

length(d::IdDict) = d.count

copy(d::IdDict) = typeof(d)(d)

get!(d::IdDict{K,V}, @nospecialize(key), @nospecialize(default)) where {K, V} = (d[key] = get(d, key, default))::V

function get(default::Callable, d::IdDict{K,V}, @nospecialize(key)) where {K, V}
val = get(d, key, secret_table_token)
if val === secret_table_token
val = default()
end
return val
end

function get!(default::Callable, d::IdDict{K,V}, @nospecialize(key)) where {K, V}
val = get(d, key, secret_table_token)
if val === secret_table_token
val = default()
setindex!(d, val, key)
end
return val
end

in(@nospecialize(k), v::KeySet{<:Any,<:IdDict}) = get(v.dict, k, secret_table_token) !== secret_table_token

# For some AbstractDict types, it is safe to implement filter!
# by deleting keys during iteration.
filter!(f, d::IdDict) = filter_in_one_pass!(f, d)

# Like Set, but using IdDict
mutable struct IdSet{T} <: AbstractSet{T}
dict::IdDict{T,Nothing}

IdSet{T}() where {T} = new(IdDict{T,Nothing}())
IdSet{T}(s::IdSet{T}) where {T} = new(copy(s.dict))
end

IdSet{T}(itr) where {T} = union!(IdSet{T}(), itr)
IdSet() = IdSet{Any}()

copymutable(s::IdSet) = typeof(s)(s)
copy(s::IdSet) = typeof(s)(s)

isempty(s::IdSet) = isempty(s.dict)
length(s::IdSet) = length(s.dict)
in(@nospecialize(x), s::IdSet) = haskey(s.dict, x)
push!(s::IdSet, @nospecialize(x)) = (s.dict[x] = nothing; s)
pop!(s::IdSet, @nospecialize(x)) = (pop!(s.dict, x); x)
pop!(s::IdSet, @nospecialize(x), @nospecialize(default)) = (x in s ? pop!(s, x) : default)
delete!(s::IdSet, @nospecialize(x)) = (delete!(s.dict, x); s)

sizehint!(s::IdSet, newsz) = (sizehint!(s.dict, newsz); s)
empty!(s::IdSet) = (empty!(s.dict); s)

filter!(f, d::IdSet) = unsafe_filter!(f, d)

function iterate(s::IdSet, state...)
y = iterate(s.dict, state...)
y === nothing && return nothing
((k, _), i) = y
return (k, i)
end

"""
map!(f, values(dict::AbstractDict))
Expand Down
2 changes: 2 additions & 0 deletions base/compiler/compiler.jl
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ include("abstractarray.jl")
include("bitarray.jl")
include("bitset.jl")
include("abstractdict.jl")
include("iddict.jl")
include("idset.jl")
include("abstractset.jl")
include("iterators.jl")
using .Iterators: zip, enumerate
Expand Down
160 changes: 160 additions & 0 deletions base/iddict.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
"""
IdDict([itr])
`IdDict{K,V}()` constructs a hash table using object-id as hash and
`===` as equality with keys of type `K` and values of type `V`.
See [`Dict`](@ref) for further help.
"""
mutable struct IdDict{K,V} <: AbstractDict{K,V}
ht::Vector{Any}
count::Int
ndel::Int
IdDict{K,V}() where {K, V} = new{K,V}(Vector{Any}(undef, 32), 0, 0)

function IdDict{K,V}(itr) where {K, V}
d = IdDict{K,V}()
for (k,v) in itr; d[k] = v; end
d
end

function IdDict{K,V}(pairs::Pair...) where {K, V}
d = IdDict{K,V}()
sizehint!(d, length(pairs))
for (k,v) in pairs; d[k] = v; end
d
end

IdDict{K,V}(d::IdDict{K,V}) where {K, V} = new{K,V}(copy(d.ht), d.count, d.ndel)
end

IdDict() = IdDict{Any,Any}()
IdDict(kv::Tuple{}) = IdDict()

IdDict(ps::Pair{K,V}...) where {K,V} = IdDict{K,V}(ps)
IdDict(ps::Pair{K}...) where {K} = IdDict{K,Any}(ps)
IdDict(ps::(Pair{K,V} where K)...) where {V} = IdDict{Any,V}(ps)
IdDict(ps::Pair...) = IdDict{Any,Any}(ps)

function IdDict(kv)
try
dict_with_eltype((K, V) -> IdDict{K, V}, kv, eltype(kv))
catch
if !applicable(iterate, kv) || !all(x->isa(x,Union{Tuple,Pair}),kv)
throw(ArgumentError(
"IdDict(kv): kv needs to be an iterator of tuples or pairs"))
else
rethrow()
end
end
end

empty(d::IdDict, ::Type{K}, ::Type{V}) where {K, V} = IdDict{K,V}()

function rehash!(d::IdDict, newsz = length(d.ht))
d.ht = ccall(:jl_idtable_rehash, Vector{Any}, (Any, Csize_t), d.ht, newsz)
d
end

function sizehint!(d::IdDict, newsz)
newsz = _tablesz(newsz*2) # *2 for keys and values in same array
oldsz = length(d.ht)
# grow at least 25%
if newsz < (oldsz*5)>>2
return d
end
rehash!(d, newsz)
end

function setindex!(d::IdDict{K,V}, @nospecialize(val), @nospecialize(key)) where {K, V}
!isa(key, K) && throw(ArgumentError("$(limitrepr(key)) is not a valid key for type $K"))
if !(val isa V) # avoid a dynamic call
val = convert(V, val)
end
if d.ndel >= ((3*length(d.ht))>>2)
rehash!(d, max(length(d.ht)>>1, 32))
d.ndel = 0
end
inserted = RefValue{Cint}(0)
d.ht = ccall(:jl_eqtable_put, Array{Any,1}, (Any, Any, Any, Ptr{Cint}), d.ht, key, val, inserted)
d.count += inserted[]
return d
end

function get(d::IdDict{K,V}, @nospecialize(key), @nospecialize(default)) where {K, V}
val = ccall(:jl_eqtable_get, Any, (Any, Any, Any), d.ht, key, default)
val === default ? default : val::V
end
function getindex(d::IdDict{K,V}, @nospecialize(key)) where {K, V}
val = get(d, key, secret_table_token)
val === secret_table_token && throw(KeyError(key))
return val::V
end

function pop!(d::IdDict{K,V}, @nospecialize(key), @nospecialize(default)) where {K, V}
found = RefValue{Cint}(0)
val = ccall(:jl_eqtable_pop, Any, (Any, Any, Any, Ptr{Cint}), d.ht, key, default, found)
if found[] === Cint(0)
return default
else
d.count -= 1
d.ndel += 1
return val::V
end
end

function pop!(d::IdDict{K,V}, @nospecialize(key)) where {K, V}
val = pop!(d, key, secret_table_token)
val === secret_table_token && throw(KeyError(key))
return val::V
end

function delete!(d::IdDict{K}, @nospecialize(key)) where K
pop!(d, key, secret_table_token)
d
end

function empty!(d::IdDict)
resize!(d.ht, 32)
ccall(:memset, Ptr{Cvoid}, (Ptr{Cvoid}, Cint, Csize_t), d.ht, 0, sizeof(d.ht))
d.ndel = 0
d.count = 0
return d
end

_oidd_nextind(a, i) = reinterpret(Int, ccall(:jl_eqtable_nextind, Csize_t, (Any, Csize_t), a, i))

function iterate(d::IdDict{K,V}, idx=0) where {K, V}
idx = _oidd_nextind(d.ht, idx)
idx == -1 && return nothing
return (Pair{K, V}(d.ht[idx + 1]::K, d.ht[idx + 2]::V), idx + 2)
end

length(d::IdDict) = d.count

copy(d::IdDict) = typeof(d)(d)

get!(d::IdDict{K,V}, @nospecialize(key), @nospecialize(default)) where {K, V} = (d[key] = get(d, key, default))::V

function get(default::Callable, d::IdDict{K,V}, @nospecialize(key)) where {K, V}
val = get(d, key, secret_table_token)
if val === secret_table_token
val = default()
end
return val
end

function get!(default::Callable, d::IdDict{K,V}, @nospecialize(key)) where {K, V}
val = get(d, key, secret_table_token)
if val === secret_table_token
val = default()
setindex!(d, val, key)
end
return val
end

in(@nospecialize(k), v::KeySet{<:Any,<:IdDict}) = get(v.dict, k, secret_table_token) !== secret_table_token

# For some AbstractDict types, it is safe to implement filter!
# by deleting keys during iteration.
filter!(f, d::IdDict) = filter_in_one_pass!(f, d)
Loading

0 comments on commit 8b6a8c2

Please sign in to comment.