defprotocol SData.Page do
@moduledoc"""
Protocol to be implemented by objects that are used as data pages
in a pagestore and that may reference other data pages by their hash.
"""
@fallback_to_any true
@doc"""
Get hashes of all pages referenced by this page.
"""
def refs(page)
end
defimpl SData.Page, for: Any do
def refs(_page), do: []
end
defprotocol SData.PageStore do
@moduledoc"""
Protocol to be implemented for page stores to allow their
manipulation.
This protocol may also be implemented by store proxies that track
operations and implement different synchronization or caching mechanisms.
A page store is an object that stores data pages (arbitrary Erlang terms) and
identifies them by their hash. Dependencies may exist between pages, in which
case they form a Merkle DAG.
"""
@doc"""
Put a page. Argument is the content of the page, returns the
hash that the store has associated to it.
Returns {hash, store}
"""
def put(store, page)
@doc"""
Get a page referenced by its hash.
Returns page
"""
def get(store, hash)
@doc"""
Copy to the store a page and all its references from the other store.
In the case of pages on the network in a distributed store, this may
be lazy.
Returns store
"""
def copy(store, other_store, hash)
@doc"""
Free a page referenced by its hash, marking it as no longer needed.
Returns store
"""
def free(store, hash)
end
defmodule SData.LocalStore do
@moduledoc"""
A page store that saves all pages locally in RAM. The store is basically a dictionnary
of hash to term mappings, which is mutated by put operations.
"""
defstruct [:pages]
@doc"""
Create empty LocalStore.
"""
def new() do
%SData.LocalStore{ pages: %{} }
end
end
defimpl SData.PageStore, for: SData.LocalStore do
def put(store, page) do
hash = SData.term_hash page
store = %{ store | pages: Map.put(store.pages, hash, page) }
{ hash, store }
end
def get(store, hash) do
store.pages[hash]
end
def copy(store, other_store, hash) do
page = SData.PageStore.get(other_store, hash)
refs = SData.Page.refs(page)
store = Enum.reduce(refs, store, fn x, acc -> copy(acc, other_store, x) end)
%{ store | pages: Map.put(store.pages, hash, page) }
end
def free(store, hash) do
%{ store | pages: Map.delete(store.pages, hash) }
end
end