aboutsummaryrefslogtreecommitdiff
path: root/lib/data/merklelist.ex
diff options
context:
space:
mode:
Diffstat (limited to 'lib/data/merklelist.ex')
-rw-r--r--lib/data/merklelist.ex143
1 files changed, 0 insertions, 143 deletions
diff --git a/lib/data/merklelist.ex b/lib/data/merklelist.ex
deleted file mode 100644
index 9b44ee8..0000000
--- a/lib/data/merklelist.ex
+++ /dev/null
@@ -1,143 +0,0 @@
-defmodule SData.MerkleList do
- @moduledoc"""
- A simple Merkle list store.
-
- Future improvements:
- - When messages are inserted other than at the top, all intermediate hashes
- change. Keep track of the mapping from old hashes to new hashes so that get
- requests can work even for hashes that are not valid anymore.
- - group items in "pages" (bigger bundles)
- """
-
- defstruct [:root, :top, :cmp, :store]
-
- @doc"""
- Create a Merkle list store.
-
- `cmp` is a compare function that respects the interface defined in module `SData`.
- """
- def new(cmp) do
- root_item = :root
- root_hash = SData.term_hash root_item
- state = %SData.MerkleList{
- root: root_hash,
- top: root_hash,
- cmp: cmp,
- store: %{ root_hash => root_item }
- }
- state
- end
-
- defp push(state, item) do
- new_item = {item, state.top}
- new_item_hash = SData.term_hash new_item
- new_store = Map.put(state.store, new_item_hash, new_item)
- %{ state | :top => new_item_hash, :store => new_store }
- end
-
- defp pop(state) do
- if state.top == state.root do
- :error
- else
- {item, next} = Map.get(state.store, state.top)
- new_store = Map.delete(state.store, state.top)
- new_state = %{ state | :top => next, :store => new_store }
- {:ok, item, new_state}
- end
- end
-
- @doc"""
- Insert a list of items in the store.
-
- A callback function may be specified that is called on any item
- that is sucessfully added, i.e. that wasn't present in the store before.
- """
- def insert_many(state, items, callback \\ (fn _ -> nil end)) do
- items_sorted = Enum.sort(items, fn (x, y) -> state.cmp.(x, y) == :after end)
- insert_many_aux(state, items_sorted, callback)
- end
-
- defp insert_many_aux(state, [], _callback) do
- state
- end
-
- defp insert_many_aux(state, [item | rest], callback) do
- case pop(state) do
- :error ->
- new_state = push(insert_many_aux(state, rest, callback), item)
- callback.(item)
- new_state
- {:ok, front, state_rest} ->
- case state.cmp.(item, front) do
- :after ->
- new_state = push(insert_many_aux(state, rest, callback), item)
- callback.(item)
- new_state
- :duplicate -> insert_many_aux(state, rest, callback)
- :before -> push(insert_many_aux(state_rest, [item | rest], callback), front)
- end
- end
- end
-
- @doc"""
- Insert a single item in the store.
-
- A callback function may be specified that is called on the item
- if it is sucessfully added, i.e. it wasn't present in the store before.
- """
- def insert(state, item, callback \\ (fn _ -> nil end)) do
- insert_many(state, [item], callback)
- end
-
- @doc"""
- Read some items from the state.
-
- The two parameters are optional:
- - qbegin : hash of the first item to read
- - qlimit : number of items to read
- """
- def read(state, qbegin \\ nil, qlimit \\ nil) do
- begin = qbegin || state.top
- limit = qlimit || 20
- get_items_list(state, begin, limit)
- end
-
- @doc"""
- Get the hash of the last item
- """
- def top(state) do
- state.top
- end
-
- @doc"""
- Get the hash of the root item
- """
- def root(state) do
- state.root
- end
-
- @doc"""
- Check if the store holds a certain item
- """
- def has(state, hash) do
- Map.has_key?(state.store, hash)
- end
-
- defp get_items_list(state, begin, limit) do
- case limit do
- 0 -> {:ok, [], begin}
- _ ->
- case Map.fetch(state.store, begin) do
- {:ok, :root} ->
- {:ok, [], nil }
- {:ok, {item, next}} ->
- case get_items_list(state, next, limit - 1) do
- {:ok, rest, past} ->
- {:ok, [ item | rest ], past }
- {:error, reason} -> {:error, reason}
- end
- :error -> {:error, begin}
- end
- end
- end
-end