defmodule Graph.Utils do @moduledoc false @compile {:inline, [vertex_id: 1, find_vertex: 2, edge_weight: 3]} @binary_heap_limit 64 @doc """ A large portion of the code for `sizeof/1` is based on `erlang_term` which can be found at [here](https://github.com/okeuday/erlang_term), authored by Michael Truog, and licensed under the MIT license. """ def sizeof(term) do sizeof(term, :erlang.system_info(:wordsize)) end defp sizeof(term, wordsize) do sizeof_term_local(term, wordsize) + sizeof_term(term) end defp sizeof_term(term) when is_list(term) do sizeof_list(term) end defp sizeof_term(term) when is_tuple(term) do sizeof_tuple(term) end defp sizeof_term(%{__struct__: _} = term) when is_map(term) do Enum.reduce(Map.from_struct(term), 0, fn {k, v}, size -> sizeof_term(k) + sizeof_term(v) + size end) end defp sizeof_term(term) do sizeof_term_global(term) end defp sizeof_term_local(term, wordsize) do # stack/register size + heap size (1 + :erts_debug.flat_size(term)) * wordsize end defp sizeof_term_global(term) when is_binary(term) do case :erlang.byte_size(term) do bsize when bsize > @binary_heap_limit -> # refc binary bsize _ -> # heap binary 0 end end defp sizeof_term_global(_term) do 0 end defp sizeof_list(l, size \\ 0) defp sizeof_list([], size), do: size defp sizeof_list([term|rest], size) do sizeof_list(rest, size+sizeof_term(term)) end defp sizeof_list(term, size) do # improper list size + sizeof_term(term) end defp sizeof_tuple(term) do sizeof_tuple(term, 1, :erlang.tuple_size(term), 0) end defp sizeof_tuple(term, n, n, size) do sizeof_term(:erlang.element(n, term)) + size end defp sizeof_tuple(term, i, n, size) do sizeof_tuple(term, i+1, n, size+sizeof_term(:erlang.element(i, term))) end def edge_weight(%Graph{edges_meta: em}, a, b) do case Map.get(em, {a, b}) do %{weight: w} -> w _ -> 0 end end @max_phash 4_294_967_296 # 2^32 def vertex_id(v), do: :erlang.phash2(v, @max_phash) def find_vertex(%Graph{vertices: vs}, v) do case Map.get(vs, vertex_id(v)) do nil -> nil v -> {:ok, v} end end end