Current section
Files
Jump to
Current section
Files
lib/rns/announce_hash_store.ex
defmodule RNS.AnnounceHashStore do
@moduledoc """
Stores the tags of all sent/received path requests and also stores the handlers.
This is so path requests are only answered to once, and so a message can be sent to a process with the answer to the path request.
The handlers are to send messages to a pid when an announce is received with a specific name hash.
"""
use GenServer
@spec start_link(any()) :: {:ok, pid()}
def start_link(opts) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end
# Client
@doc "Adds a packet hash."
@spec add(RNS.Packet.hash()) :: :ok
def add(hash) do
true = :ets.insert(:announces, {hash})
:ok
end
@doc "Checks if the packet hash is in the table."
@spec exists?(RNS.Destination.hash()) :: boolean()
def exists?(hash) do
:ets.member(:announces, hash)
end
@doc "Returns all handlers for a specific name hash."
@spec handlers(RNS.Destination.hash() | nil, RNS.Destination.name_hash() | nil) :: [pid()]
def handlers(destination_hash, name_hash) do
GenServer.call(__MODULE__, {:handlers, destination_hash, name_hash})
end
@doc "Returns all handlers for a specific name hash."
@spec add_handler(RNS.Destination.hash() | nil, RNS.Destination.name_hash() | nil, pid()) :: :ok
def add_handler(destination_hash, name_hash, pid \\ self()) do
handler = {destination_hash, name_hash, pid}
GenServer.cast(__MODULE__, {:add_handler, handler})
end
@doc "Returns all handlers for a specific name hash."
@spec remove_handler(RNS.Destination.hash() | nil, RNS.Destination.name_hash() | nil, pid()) ::
:ok
def remove_handler(destination_hash, name_hash, pid \\ self()) do
handler = {destination_hash, name_hash, pid}
GenServer.cast(__MODULE__, {:remove_handler, handler})
end
# Callbacks
@impl true
def init(_opts) do
:ets.new(:announces, [
{:read_concurrency, true},
{:write_concurrency, true},
:public,
:named_table
])
{:ok, []}
end
@impl true
def handle_call({:handlers, destination_hash, name_hash}, _from, handlers) do
these_handlers =
Enum.reduce(handlers, [], fn {handler_destination_hash, handler_name_hash, handler_pid},
these_handlers ->
if handler_destination_hash == nil or handler_destination_hash == destination_hash do
if handler_name_hash == nil or handler_name_hash == name_hash do
[handler_pid | these_handlers]
else
these_handlers
end
else
these_handlers
end
end)
{:reply, these_handlers, handlers}
end
@impl true
def handle_cast({:add_handler, handler}, handlers) do
{:noreply, [handler | handlers]}
end
@impl true
def handle_cast({:remove_handler, handler}, handlers) do
new_handlers =
Enum.filter(handlers, fn this_handler ->
if this_handler == handler do
false
else
true
end
end)
{:noreply, new_handlers}
end
end