Skip to content

Commit

Permalink
Allow persistent map to use any given hash function (#1799)
Browse files Browse the repository at this point in the history
This change allows the persistent map to use any given hash function like the mutable hash map.
  • Loading branch information
Theodus authored Apr 4, 2017
1 parent fc859fa commit ca02aa2
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 57 deletions.
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))

0 comments on commit ca02aa2

Please sign in to comment.