aboutsummaryrefslogtreecommitdiff
path: root/lib/data/merklesearchtree.ex
diff options
context:
space:
mode:
Diffstat (limited to 'lib/data/merklesearchtree.ex')
-rw-r--r--lib/data/merklesearchtree.ex189
1 files changed, 112 insertions, 77 deletions
diff --git a/lib/data/merklesearchtree.ex b/lib/data/merklesearchtree.ex
index 963653e..0554f82 100644
--- a/lib/data/merklesearchtree.ex
+++ b/lib/data/merklesearchtree.ex
@@ -7,8 +7,8 @@ defmodule SData.MerkleSearchTree do
level,
hash_of_node | nil,
[
- { k_low_bound, v, hash_of_node | nil },
- { k_low_bound, v, hash_of_node | nil },
+ { item_low_bound, hash_of_node | nil },
+ { item_low_bound, hash_of_node | nil },
...
}
}
@@ -16,8 +16,22 @@ defmodule SData.MerkleSearchTree do
alias SData.PageStore, as: Store
- defstruct [:root, :store]
+ @doc"""
+ Create a new Merkle search tree.
+ This structure can be used as a set with only true keys,
+ or as a map if a merge function is given.
+
+ `cmp` is a compare function for keys as defined in module `SData`.
+
+ `merge` is a function for merging two items that have the same key.
+ """
+ defstruct root: nil,
+ store: SData.LocalStore.new,
+ cmp: &SData.cmp_term/2,
+ merge: &SData.merge_true/2
+
+
defmodule Page do
defstruct [:level, :low, :list]
end
@@ -34,17 +48,10 @@ defmodule SData.MerkleSearchTree do
end
- def new(store \\ nil) do
- store = store || SData.LocalStore.new
- %SData.MerkleSearchTree{
- root: nil,
- store: store
- }
- end
- def insert(state, key, value) do
+ def insert(state, key, value \\ true) do
level = calc_level(key)
- {hash, store} = insert_at(state.store, state.root, key, level, value)
+ {hash, store} = insert_at(state, state.store, state.root, key, level, value)
%{ state | root: hash, store: store }
end
@@ -52,7 +59,7 @@ defmodule SData.MerkleSearchTree do
# TODO
end
- defp insert_at(store, root, key, level, value) do
+ defp insert_at(s, store, root, key, level, value) do
{new_page, store} = if root == nil do
{ %Page{ level: level, low: nil, list: [ { key, value, nil } ] }, store }
else
@@ -60,46 +67,48 @@ defmodule SData.MerkleSearchTree do
[ { k0, _, _} | _ ] = lst
cond do
plevel < level ->
- {low, high, store} = split(store, root, key)
+ {low, high, store} = split(s, store, root, key)
{ %Page{ level: level, low: low, list: [ { key, value, high } ] }, store }
plevel == level ->
store = Store.free(store, root)
- if key < k0 do
- {low2a, low2b, store} = split(store, low, key)
- { %Page{ level: level, low: low2a, list: [ { key, value, low2b } | lst] }, store }
- else
- {new_lst, store} = aux_insert_after_first(store, lst, key, value)
- { %Page{ level: plevel, low: low, list: new_lst }, store }
+ case s.cmp.(key, k0) do
+ :before ->
+ {low2a, low2b, store} = split(s, store, low, key)
+ { %Page{ level: level, low: low2a, list: [ { key, value, low2b } | lst] }, store }
+ _ ->
+ {new_lst, store} = aux_insert_after_first(s, store, lst, key, value)
+ { %Page{ level: plevel, low: low, list: new_lst }, store }
end
plevel > level ->
store = Store.free(store, root)
- if key < k0 do
- {new_low, store} = insert_at(store, low, key, level, value)
- { %Page{ level: plevel, low: new_low, list: lst }, store }
- else
- {new_lst, store} = aux_insert_sub_after_first(store, lst, key, level, value)
- { %Page{ level: plevel, low: low, list: new_lst }, store }
+ case s.cmp.(key, k0) do
+ :before ->
+ {new_low, store} = insert_at(s, store, low, key, level, value)
+ { %Page{ level: plevel, low: new_low, list: lst }, store }
+ :after ->
+ {new_lst, store} = aux_insert_sub_after_first(s, store, lst, key, level, value)
+ { %Page{ level: plevel, low: low, list: new_lst }, store }
end
end
end
Store.put(store, new_page) # returns {hash, store}
end
- defp split(store, hash, key) do
+ defp split(s, store, hash, key) do
if hash == nil do
{nil, nil, store}
else
%Page{ level: level, low: low, list: lst } = Store.get(store, hash)
store = Store.free(store, hash)
[ { k0, _, _} | _ ] = lst
- cond do
- key < k0 ->
- {lowlow, lowhi, store} = split(store, low, key)
+ case s.cmp.(key, k0) do
+ :before ->
+ {lowlow, lowhi, store} = split(s, store, low, key)
newp2 = %Page{ level: level, low: lowhi, list: lst }
{newp2h, store} = Store.put(store, newp2)
{lowlow, newp2h, store}
- key > k0 ->
- {lst1, p2, store} = split_aux(store, lst, key, level)
+ :after ->
+ {lst1, p2, store} = split_aux(s, store, lst, key, level)
newp1 = %Page{ level: level, low: low, list: lst1}
{newp1h, store} = Store.put(store, newp1)
{newp1h, p2, store}
@@ -107,79 +116,105 @@ defmodule SData.MerkleSearchTree do
end
end
- defp split_aux(store, lst, key, level) do
+ defp split_aux(s, store, lst, key, level) do
case lst do
- [ {k1, _, _} | _ ] when k1 == key ->
- raise "Bad logic"
[ {k1, v1, r1} ] ->
- {r1l, r1h, store} = split(store, r1, key)
+ if s.cmp.(k1, key) == :duplicate do
+ raise "Bad logic"
+ end
+ {r1l, r1h, store} = split(s, store, r1, key)
{ [{k1, v1, r1l}], r1h, store }
- [ {k1, v1, r1}, {k2, v2, r2} | rst ] when key < k2->
- {r1l, r1h, store} = split(store, r1, key)
- p2 = %Page{ level: level, low: r1h, list: [ {k2, v2, r2} | rst ] }
- {p2h, store} = Store.put(store, p2)
- { [{k1, v1, r1l}], p2h, store }
- [ fst | rst ] ->
- {rst2, hi, store} = split_aux(store, rst, key, level)
- { [ fst | rst2 ], hi, store }
+ [ {k1, v1, r1} = fst, {k2, v2, r2} = rst1 | rst ] ->
+ case s.cmp.(key, k2) do
+ :before ->
+ {r1l, r1h, store} = split(s, store, r1, key)
+ p2 = %Page{ level: level, low: r1h, list: [ {k2, v2, r2} | rst ] }
+ {p2h, store} = Store.put(store, p2)
+ { [{k1, v1, r1l}], p2h, store }
+ :after ->
+ {rst2, hi, store} = split_aux(s, store, [rst1 | rst], key, level)
+ { [ fst | rst2 ], hi, store }
+ :duplicate ->
+ raise "Bad logic"
+ end
end
end
- defp aux_insert_after_first(store, lst, key, value) do
+ defp aux_insert_after_first(s, store, lst, key, value) do
case lst do
- [ {k1, _old_value, r1} | rst ] when k1 == key ->
- { [ {k1, value, r1} | rst ], store }
[ {k1, v1, r1} ] ->
- {r1a, r1b, new_store} = split(store, r1, key)
- { [ {k1, v1, r1a}, {key, value, r1b} ], new_store }
- [ {k1, v1, r1}, {k2, v2, r2} | rst ] when key < k2->
- {r1a, r1b, new_store} = split(store, r1, key)
- { [ {k1, v1, r1a}, {key, value, r1b}, {k2, v2, r2} | rst ], new_store }
- [ fst | rst ] ->
- {rst2, new_store} = aux_insert_after_first(store, rst, key, value)
- { [ fst | rst2 ], new_store }
+ case s.cmp.(k1, key) do
+ :duplicate ->
+ { [ {k1, s.merge.(v1, value), r1} ], store }
+ :before ->
+ {r1a, r1b, new_store} = split(s, store, r1, key)
+ { [ {k1, v1, r1a}, {key, value, r1b} ], new_store }
+ end
+ [ {k1, v1, r1} = fst, {k2, v2, r2} = rst1 | rst ] ->
+ case s.cmp.(k1, key) do
+ :duplicate ->
+ { [ {k1, s.merge.(v1, value), r1}, rst1 | rst ], store }
+ :before ->
+ case s.cmp.(k2, key) do
+ :after ->
+ {r1a, r1b, new_store} = split(s, store, r1, key)
+ { [ {k1, v1, r1a}, {key, value, r1b}, {k2, v2, r2} | rst ], new_store }
+ _ ->
+ {rst2, new_store} = aux_insert_after_first(s, store, [rst1 | rst], key, value)
+ { [ fst | rst2 ], new_store }
+ end
+ end
+ end
end
- end
- defp aux_insert_sub_after_first(store, lst, key, level, value) do
+ defp aux_insert_sub_after_first(s, store, lst, key, level, value) do
case lst do
- [ {k1, _, _} | _ ] when k1 == key ->
- raise "Bad logic"
[ {k1, v1, r1} ] ->
- {r1new, store_new} = insert_at(store, r1, key, level, value)
+ if s.cmp.(k1, key) == :duplicate do
+ raise "Bad logic"
+ end
+ {r1new, store_new} = insert_at(s, store, r1, key, level, value)
{ [{k1, v1, r1new}], store_new }
- [ {k1, v1, r1}, {k2, v2, r2} | rst ] when key < k2->
- {r1new, store_new} = insert_at(store, r1, key, level, value)
- { [{k1, v1, r1new}, {k2, v2, r2} | rst], store_new }
- [ fst | rst ] ->
- {rst2, new_store} = aux_insert_sub_after_first(store, rst, key, level, value)
- { [ fst | rst2 ], new_store }
+ [ {k1, v1, r1} = fst, {k2, _, _} = rst1 | rst ] ->
+ if s.cmp.(k1, key) == :duplicate do
+ raise "Bad logic"
+ end
+ case s.cmp.(key, k2) do
+ :before ->
+ {r1new, store_new} = insert_at(s, store, r1, key, level, value)
+ { [{k1, v1, r1new}, rst1 | rst], store_new }
+ _ ->
+ {rst2, new_store} = aux_insert_sub_after_first(s, store, [rst1 |rst], key, level, value)
+ { [ fst | rst2 ], new_store }
+ end
end
end
def get(state, key) do
- get(state.store, state.root, key)
+ get(state, state.store, state.root, key)
end
- defp get(store, root, key) do
+ defp get(s, store, root, key) do
case root do
nil -> nil
_ ->
%Page{ level: _, low: low, list: lst } = Store.get(store, root)
- get_aux(store, low, lst, key)
+ get_aux(s, store, low, lst, key)
end
end
- defp get_aux(store, low, lst, key) do
+ defp get_aux(s, store, low, lst, key) do
case lst do
[] ->
- get(store, low, key)
- [ {k, v, _} | _ ] when key == k ->
- v
- [ {k, _, _} | _ ] when key < k ->
- get(store, low, key)
- [ {k, _, low2} | rst ] when key > k ->
- get_aux(store, low2, rst, key)
+ get(s, store, low, key)
+ [ {k, v, low2} | rst ] ->
+ case s.cmp.(key, k) do
+ :duplicate -> v
+ :before ->
+ get(s, store, low, key)
+ :after ->
+ get_aux(s, store, low2, rst, key)
+ end
end
end