defmodule Zig.Type.Cpointer do alias Zig.Type alias Zig.Type.Struct use Type import Type, only: :macros defstruct [:child] @type t :: %__MODULE__{child: Type.t()} def from_json(%{"child" => child}, module) do %__MODULE__{child: Type.from_json(child, module)} end def to_string(slice), do: "[*c]#{Kernel.to_string(slice.child)}" def to_call(slice), do: "[*c]#{Type.to_call(slice.child)}" def return_allowed?(pointer) do case pointer.child do ~t(u8) -> true # null-terminated list of pointers. %__MODULE__{child: child} -> Type.return_allowed?(child) # NB: we assume these are single pointer returns. struct = %Struct{} -> Type.return_allowed?(struct) _ -> false end end def spec(%{child: child}, :param, _opts) do has_solo? = match?(%Type.Struct{extern: true}, child) child_form = Type.spec(child, :param, []) case {has_solo?, binary_form(child)} do {false, nil} -> quote do [unquote(child_form)] | nil end {true, nil} -> quote do unquote(child_form) | [unquote(child_form)] | nil end {false, binary_form} -> quote do [unquote(child_form)] | unquote(binary_form) | nil end end end def spec(%{child: ~t(u8)}, :return, opts) do # assumed to be a null-terminated string case {opts[:length], Keyword.fetch!(opts, :type)} do {_, :list} -> quote context: Elixir do [0..255] end {length, type} when not is_nil(length) and type in ~w(default binary)a -> quote context: Elixir do <<_::unquote(length * 8)>> end {_, type} when type in ~w(default binary)a -> Type.spec(:binary) end end def spec(%{child: child}, :return, opts) do case {Keyword.fetch(opts, :length), Keyword.fetch!(opts, :type)} do {{:ok, _}, :default} -> [Type.spec(child, :return, opts)] {{:ok, {:arg, _}}, :binary} -> # this is the case where the length is drawn from one of the arguments # to the function. quote do <<_::_*unquote(chunk_size(child))>> end {{:ok, number}, :binary} -> quote do <<_::unquote(number * chunk_size(child))>> end {:error, _} when child.__struct__ == Type.Struct -> Type.spec(child, :return, opts) {:error, _} when child == %__MODULE__{child: ~t(u8)} -> quote do [binary()] end {:error, _} when child.__struct__ == __MODULE__ -> [Type.spec(child.child, :return, opts)] {:error, _} -> raise "missing length not allowed" end end def spec(%{child: child = %__MODULE__{}}, :return, opts) do [Type.spec(child, :return, opts)] end defp chunk_size(%type{bits: bits}) when type in [Type.Integer, Type.Float] do bits end defp chunk_size(_), do: raise("invalid type for binary *c output") defp binary_form(~t(u8)), do: Type.spec(:binary) defp binary_form(%Type.Integer{bits: bits}) do quote context: Elixir do <<_::_*unquote(Type.Integer._next_power_of_two_ceil(bits))>> end end defp binary_form(%Type.Float{bits: bits}) do quote context: Elixir do <<_::_*unquote(bits)>> end end defp binary_form(_), do: nil def missing_size?(_), do: true def of(child), do: %__MODULE__{child: child} end