Current section

Files

Jump to
ex_ice lib checklist.ex
Raw

lib/checklist.ex

defmodule ExICE.Checklist do
@moduledoc false
alias ExICE.ICEAgent
alias ExICE.CandidatePair
alias ExICE.Candidate
@type t() :: map()
@spec get_next_pair(t()) :: CandidatePair.t() | nil
def get_next_pair(checklist) do
# FIXME correctly handle frozen pairs, according to sec 6.1.4.2
checklist
|> Enum.filter(fn {_id, pair} -> pair.state in [:frozen, :waiting] end)
|> Enum.max_by(fn {_id, pair} -> pair.priority end, fn -> {nil, nil} end)
|> elem(1)
end
@spec get_pair_for_nomination(t()) :: CandidatePair.t() | nil
def get_pair_for_nomination(checklist) do
checklist
|> Enum.filter(fn {_id, pair} -> pair.valid? end)
|> Enum.max_by(fn {_id, pair} -> pair.priority end, fn -> {nil, nil} end)
|> elem(1)
end
@spec get_valid_pair(t()) :: CandidatePair.t() | nil
def get_valid_pair(checklist) do
checklist
|> Enum.find({nil, nil}, fn {_id, pair} -> pair.valid? end)
|> elem(1)
end
@spec find_pair(t(), CandidatePair.t()) :: CandidatePair.t() | nil
def find_pair(checklist, pair) do
find_pair(checklist, pair.local_cand, pair.remote_cand)
end
@spec find_pair(t(), Candidate.t(), Candidate.t()) :: CandidatePair.t() | nil
def find_pair(checklist, local_cand, remote_cand) do
# TODO which pairs are actually the same?
checklist
|> Enum.find({nil, nil}, fn {_id, p} ->
p.local_cand.base_address == local_cand.base_address and
p.local_cand.base_port == local_cand.base_port and
p.local_cand.address == local_cand.address and
p.local_cand.port == local_cand.port and
p.remote_cand.address == remote_cand.address and
p.remote_cand.port == remote_cand.port
end)
|> elem(1)
end
@spec waiting?(t()) :: boolean()
def waiting?(checklist) do
Enum.any?(checklist, fn {_id, pair} -> pair.state in [:frozen, :waiting] end)
end
@spec in_progress?(t()) :: boolean()
def in_progress?(checklist) do
Enum.any?(checklist, fn {_id, pair} -> pair.state == :in_progress end)
end
@spec finished?(t()) :: boolean()
def finished?(checklist) do
not (waiting?(checklist) or in_progress?(checklist))
end
@spec get_foundations(t()) :: [{integer(), integer()}]
def get_foundations(checklist) do
for {_id, pair} <- checklist do
{pair.local_cand.foundation, pair.remote_cand.foundation}
end
end
@spec prune(t()) :: t()
def prune(checklist) do
# This is done according to RFC 8838 sec. 10
{waiting, in_flight_or_done} =
Enum.split_with(checklist, fn {_id, p} -> p.state in [:waiting, :frozen] end)
waiting =
waiting
|> Enum.sort_by(fn {_id, p} -> p.priority end, :desc)
|> Enum.uniq_by(fn {_id, p} ->
{p.local_cand.base_address, p.local_cand.base_port, p.remote_cand}
end)
Map.new(waiting ++ in_flight_or_done)
end
@spec timeout_pairs(t(), [integer()]) :: t()
def timeout_pairs(checklist, ids) do
for {_id, pair} <- checklist, into: %{} do
if pair.id in ids do
{pair.id, %CandidatePair{pair | valid?: false, state: :failed}}
else
{pair.id, pair}
end
end
end
@spec recompute_pair_prios(t(), ICEAgent.role()) :: t()
def recompute_pair_prios(checklist, role) do
Map.new(checklist, fn {pair_id, pair} ->
{pair_id, CandidatePair.recompute_priority(pair, role)}
end)
end
end