Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow persistent map to use any hash function #1799

Merged
merged 1 commit into from
Apr 4, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
59 changes: 31 additions & 28 deletions packages/collections/persistent/_map_node.pony
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
use mut = "collections"

type _MapCollisions[K: (mut.Hashable val & Equatable[K]), V: Any #share]
is Array[_MapLeaf[K, V]] val
type _MapCollisions[K: Any #share, V: Any #share, H: mut.HashFunction[K] val]
is Array[_MapLeaf[K, V, H]] val

type _MapLeaf[K: (mut.Hashable val & Equatable[K]), V: Any #share]
type _MapLeaf[K: Any #share, V: Any #share, H: mut.HashFunction[K] val]
is (K, V)

type _MapEntry[K: (mut.Hashable val & Equatable[K]), V: Any #share]
is (_MapNode[K, V] | _MapCollisions[K, V] | _MapLeaf[K, V])
type _MapEntry[K: Any #share, V: Any #share, H: mut.HashFunction[K] val]
is (_MapNode[K, V, H] | _MapCollisions[K, V, H] | _MapLeaf[K, V, H])

class val _MapNode[K: (mut.Hashable val & Equatable[K]), V: Any #share]
let _entries: Array[_MapEntry[K, V]] val
class val _MapNode[K: Any #share, V: Any #share, H: mut.HashFunction[K] val]
let _entries: Array[_MapEntry[K, V, H]] val
let _nodemap: U32
let _datamap: U32
let _level: U8

new val empty(l: U8) =>
_entries = recover val
match l
| 6 => Array[_MapEntry[K, V]](4)
| 6 => Array[_MapEntry[K, V, H]](4)
else
Array[_MapEntry[K, V]](32)
Array[_MapEntry[K, V, H]](32)
end
end
_nodemap = 0
_datamap = 0
_level = l

new val create(es: Array[_MapEntry[K, V]] iso, nm: U32, dm: U32, l: U8) =>
new val create(es: Array[_MapEntry[K, V, H]] iso, nm: U32, dm: U32, l: U8) =>
_entries = consume es
_nodemap = nm
_datamap = dm
Expand All @@ -39,16 +39,18 @@ class val _MapNode[K: (mut.Hashable val & Equatable[K]), V: Any #share]
if i == -1 then error end
match _entries(i.usize_unsafe())
| (_, let v: V) => v
| let sn: _MapNode[K, V] => sn(hash, key)
| let sn: _MapNode[K, V, H] => sn(hash, key)
else
let cn = _entries(i.usize_unsafe()) as _MapCollisions[K, V]
let cn = _entries(i.usize_unsafe()) as _MapCollisions[K, V, H]
for l in cn.values() do
if l._1 == key then return l._2 end
if H.eq(l._1, key) then return l._2 end
end
error
end

fun val update(hash: U32, leaf: _MapLeaf[K, V]): (_MapNode[K, V], Bool) ? =>
fun val update(hash: U32, leaf: _MapLeaf[K, V, H]):
(_MapNode[K, V, H], Bool) ?
=>
let idx = _Bits.mask(hash, _level)
let i = _compressed_index(_nodemap, _datamap, idx)
if i == -1 then
Expand All @@ -61,10 +63,10 @@ class val _MapNode[K: (mut.Hashable val & Equatable[K]), V: Any #share]
if _level == 6 then
// insert into collision node
let es = recover _entries.clone() end
let cs = _entries(i.usize_unsafe()) as _MapCollisions[K, V]
let cs = _entries(i.usize_unsafe()) as _MapCollisions[K, V, H]
let cs' = recover cs.clone() end
for (k, v) in cs.pairs() do
if v._1 == leaf._1 then
if H.eq(v._1, leaf._1) then
// update collision
cs'(k) = leaf
es(i.usize_unsafe()) = consume cs'
Expand All @@ -76,22 +78,22 @@ class val _MapNode[K: (mut.Hashable val & Equatable[K]), V: Any #share]
(create(consume es, _nodemap, _datamap, _level), true)
else
// insert into sub-node
let sn = _entries(i.usize_unsafe()) as _MapNode[K, V]
let sn = _entries(i.usize_unsafe()) as _MapNode[K, V, H]
let es = recover _entries.clone() end
(let sn', _) = sn.update(hash, leaf) as (_MapNode[K, V], Bool)
(let sn', _) = sn.update(hash, leaf) as (_MapNode[K, V, H], Bool)
es(i.usize_unsafe()) = sn'
(create(consume es, _nodemap, _datamap, _level), true)
end
else
let old = _entries(i.usize_unsafe()) as _MapLeaf[K, V]
if old._1 == leaf._1 then
let old = _entries(i.usize_unsafe()) as _MapLeaf[K, V, H]
if H.eq(old._1, leaf._1) then
// update leaf
let es = recover _entries.clone() end
es(i.usize()) = leaf
(create(consume es, _nodemap, _datamap, _level), false)
elseif _level == 6 then
// create collision node
let cn = recover Array[_MapLeaf[K, V]](2) end
let cn = recover Array[_MapLeaf[K, V, H]](2) end
cn.push(old)
cn.push(leaf)
let es = recover _entries.clone() end
Expand All @@ -104,8 +106,9 @@ class val _MapNode[K: (mut.Hashable val & Equatable[K]), V: Any #share]
else
// create new sub-node
var sn = empty(_level + 1)
(sn, _) = sn.update(old._1.hash().u32(), old) as (_MapNode[K, V], Bool)
(sn, _) = sn.update(hash, leaf) as (_MapNode[K, V], Bool)
(sn, _) =
sn.update(H.hash(old._1).u32(), old) as (_MapNode[K, V, H], Bool)
(sn, _) = sn.update(hash, leaf) as (_MapNode[K, V, H], Bool)
let es = recover _entries.clone() end
let nm = _Bits.set_bit(_nodemap, idx)
let dm = _Bits.clear_bit(_datamap, idx)
Expand All @@ -116,7 +119,7 @@ class val _MapNode[K: (mut.Hashable val & Equatable[K]), V: Any #share]
end
end

fun val remove(hash: U32, key: K): _MapNode[K, V] ? =>
fun val remove(hash: U32, key: K): _MapNode[K, V, H] ? =>
let idx = _Bits.mask(hash, _level)
let i = _compressed_index(_nodemap, _datamap, idx)
if i == -1 then error end
Expand All @@ -127,18 +130,18 @@ class val _MapNode[K: (mut.Hashable val & Equatable[K]), V: Any #share]
else
if _level == 6 then
let es = recover _entries.clone() end
let cs = _entries(i.usize_unsafe()) as _MapCollisions[K, V]
let cs = _entries(i.usize_unsafe()) as _MapCollisions[K, V, H]
let cs' = recover cs.clone() end
for (k, v) in cs.pairs() do
if v._1 == key then
if H.eq(v._1, key) then
cs'.delete(k)
es(i.usize_unsafe()) = consume cs'
return create(consume es, _nodemap, _datamap, _level)
end
end
error
else
var sn = _entries(i.usize_unsafe()) as _MapNode[K, V]
var sn = _entries(i.usize_unsafe()) as _MapNode[K, V, H]
sn = sn.remove(hash, key)
let es = recover _entries.clone() end
if (_nodemap.popcount() == 0) and (_datamap.popcount() == 1) then
Expand All @@ -162,4 +165,4 @@ class val _MapNode[K: (mut.Hashable val & Equatable[K]), V: Any #share]
let i = (np + dp).popcount()
if _Bits.has_bit(nm, idx) or _Bits.has_bit(dm, idx) then i else -1 end

fun entries(): Array[_MapEntry[K, V]] val => _entries
fun entries(): Array[_MapEntry[K, V, H]] val => _entries
69 changes: 40 additions & 29 deletions packages/collections/persistent/map.pony
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
use mut = "collections"

class val Map[K: (mut.Hashable val & Equatable[K]), V: Any #share]
type Map[K: (mut.Hashable val & Equatable[K]), V: Any #share] is
HashMap[K, V, mut.HashEq[K]]
"""
A map that uses structural equality on the key.
"""

type MapIs[K: Any #share, V: Any #share] is mut.HashMap[K, V, mut.HashIs[K]]
"""
A map that uses identity comparison on the key.
"""

class val HashMap[K: Any #share, V: Any #share, H: mut.HashFunction[K] val]
"""
A persistent map based on the Compressed Hash Array Mapped Prefix-tree from
'Optimizing Hash-Array Mapped Tries for Fast and Lean Immutable JVM
Expand All @@ -18,47 +29,47 @@ class val Map[K: (mut.Hashable val & Equatable[K]), V: Any #share]
let map = Maps.from[String,U32]([("a", 2), ("b", 3)]) // {a: 2, b: 3}
```
"""
let _root: _MapNode[K, V]
let _root: _MapNode[K, V, H]
let _size: USize

new val create() =>
_root = _MapNode[K, V].empty(0)
_root = _MapNode[K, V, H].empty(0)
_size = 0

new val _create(r: _MapNode[K, V], s: USize) =>
new val _create(r: _MapNode[K, V, H], s: USize) =>
_root = r
_size = s

fun val apply(k: K): val->V ? =>
"""
Attempt to get the value corresponding to k.
"""
_root(k.hash().u32(), k)
_root(H.hash(k).u32(), k)

fun val size(): USize =>
"""
Return the amount of key-value pairs in the Map.
"""
_size

fun val update(key: K, value: val->V): Map[K, V] =>
fun val update(key: K, value: val->V): HashMap[K, V, H] =>
"""
Update the value associated with the provided key.
"""
(let r, let insertion) =
try
_root.update(key.hash().u32(), (key, value))
_root.update(H.hash(key).u32(), (key, value))
else
(_root, false) // should not occur
end
let s = if insertion then _size + 1 else _size end
_create(r, s)

fun val remove(k: K): Map[K, V] ? =>
fun val remove(k: K): HashMap[K, V, H] ? =>
"""
Try to remove the provided key from the Map.
"""
_create(_root.remove(k.hash().u32(), k), _size - 1)
_create(_root.remove(H.hash(k).u32(), k), _size - 1)

fun val get_or_else(k: K, alt: val->V): val->V =>
"""
Expand All @@ -82,7 +93,7 @@ class val Map[K: (mut.Hashable val & Equatable[K]), V: Any #share]
false
end

fun val concat(iter: Iterator[(val->K, val->V)]): Map[K, V] =>
fun val concat(iter: Iterator[(val->K, val->V)]): HashMap[K, V, H] =>
"""
Add the K, V pairs from the given iterator to the map.
"""
Expand All @@ -92,42 +103,42 @@ class val Map[K: (mut.Hashable val & Equatable[K]), V: Any #share]
end
m

fun val keys(): MapKeys[K, V] => MapKeys[K, V](this)
fun val keys(): MapKeys[K, V, H] => MapKeys[K, V, H](this)

fun val values(): MapValues[K, V] => MapValues[K, V](this)
fun val values(): MapValues[K, V, H] => MapValues[K, V, H](this)

fun val pairs(): MapPairs[K, V] => MapPairs[K, V](this)
fun val pairs(): MapPairs[K, V, H] => MapPairs[K, V, H](this)

fun _root_node(): _MapNode[K, V] => _root
fun _root_node(): _MapNode[K, V, H] => _root

class MapKeys[K: (mut.Hashable val & Equatable[K]), V: Any #share]
embed _pairs: MapPairs[K, V]
class MapKeys[K: Any #share, V: Any #share, H: mut.HashFunction[K] val]
embed _pairs: MapPairs[K, V, H]

new create(m: Map[K, V]) => _pairs = MapPairs[K, V](m)
new create(m: HashMap[K, V, H]) => _pairs = MapPairs[K, V, H](m)

fun has_next(): Bool => _pairs.has_next()

fun ref next(): K ? => _pairs.next()._1

class MapValues[K: (mut.Hashable val & Equatable[K]), V: Any #share]
embed _pairs: MapPairs[K, V]
class MapValues[K: Any #share, V: Any #share, H: mut.HashFunction[K] val]
embed _pairs: MapPairs[K, V, H]

new create(m: Map[K, V]) => _pairs = MapPairs[K, V](m)
new create(m: HashMap[K, V, H]) => _pairs = MapPairs[K, V, H](m)

fun has_next(): Bool => _pairs.has_next()

fun ref next(): val->V ? => _pairs.next()._2

class MapPairs[K: (mut.Hashable val & Equatable[K]), V: Any #share]
embed _path: Array[_MapNode[K, V]]
class MapPairs[K: Any #share, V: Any #share, H: mut.HashFunction[K] val]
embed _path: Array[_MapNode[K, V, H]]
embed _idxs: Array[USize]
var _i: USize = 0
let _size: USize
var _ci: USize = 0

new create(m: Map[K, V]) =>
new create(m: HashMap[K, V, H]) =>
_size = m.size()
_path = Array[_MapNode[K, V]]
_path = Array[_MapNode[K, V, H]]
_path.push(m._root_node())
_idxs = Array[USize]
_idxs.push(0)
Expand All @@ -141,14 +152,14 @@ class MapPairs[K: (mut.Hashable val & Equatable[K]), V: Any #share]
return next()
end
match n.entries()(i)
| let l: _MapLeaf[K, V] =>
| let l: _MapLeaf[K, V, H] =>
_inc_i()
_i = _i + 1
l
| let sn: _MapNode[K, V] =>
| let sn: _MapNode[K, V, H] =>
_push(sn)
next()
| let cs: _MapCollisions[K, V] =>
| let cs: _MapCollisions[K, V, H] =>
if _ci < cs.size() then
let l = cs(_ci)
_ci = _ci + 1
Expand All @@ -162,7 +173,7 @@ class MapPairs[K: (mut.Hashable val & Equatable[K]), V: Any #share]
else error
end

fun ref _push(n: _MapNode[K, V]) =>
fun ref _push(n: _MapNode[K, V, H]) =>
_path.push(n)
_idxs.push(0)

Expand All @@ -175,6 +186,6 @@ class MapPairs[K: (mut.Hashable val & Equatable[K]), V: Any #share]
let i = _idxs.size() - 1
_idxs(i) = _idxs(i) + 1

fun _cur(): (_MapNode[K, V], USize) ? =>
fun _cur(): (_MapNode[K, V, H], USize) ? =>
let i = _idxs.size() - 1
(_path(i), _idxs(i))