defmodule Exkad.Store do use GenServer require Logger def start_link(opts) do GenServer.start_link(__MODULE__, opts) end def init(%{parent: parent}) do Exkad.Node.register(parent, :store, self) Logger.debug("Store started in #{inspect self}") {:ok, %{values: %{}, meta: %{}}} end def put(server, value) do GenServer.call(server, {:put, value}) end def put_meta(server, term, ptr) do GenServer.call(server, {:put_meta, term, ptr}) end def search(server, key) do GenServer.call(server, {:get_meta, key}) end def get(server, key) do GenServer.call(server, {:get, key}) end #Streaming hash # def hash(s) do # ctx = :crypto.hash_init(:md5) # ctx = Enum.reduce(s, ctx, fn chunk, acc -> # :crypto.hash_update(acc, chunk) # end) # :crypto.hash_final(ctx) # |> :erlang.bitstring_to_list # |> Enum.map(&(:io_lib.format("~2.16.0b", [&1]))) # |> List.flatten # |> :erlang.list_to_bitstring # end def hash(bin) do :crypto.hash(:md5, bin) |> :erlang.bitstring_to_list |> Enum.map(&(:io_lib.format("~2.16.0b", [&1]))) |> List.flatten |> :erlang.list_to_bitstring end def handle_call({:put_meta, term, ptr}, _from, state) do key = hash(term) ptrs = case state.meta[key] do nil -> [ptr] existing -> [ptr | existing] end meta = Dict.put(state.meta, key, ptrs) state = Enum.into(%{meta: meta}, state) {:reply, {:ok, key}, state} end def handle_call({:get_meta, key}, _from, state) do res = Dict.get(state.meta, key, []) {:reply, {:ok, res}, state} end def handle_call({:put, value}, _from, state) do key = hash(value) values = Dict.put(state.values, key, value) state = Enum.into(%{values: values}, state) {:reply, {:ok, key}, state} end def handle_call({:get, key}, _from, state) do res = case state.values[key] do nil -> {:error, :not_found} val -> {:ok, val} end {:reply, res, state} end end