lib/beaver/native.ex

defmodule Beaver.Native do
  @moduledoc """
  This module provide higher level interface to primitive data types and MemRefDescriptor C struct in [MemRef](https://mlir.llvm.org/docs/Dialects/MemRef/) dialect.
  It aims to manage memory with BEAM and help a Erlang/Elixir function play well with function generated by MLIR and other NIFs.
  """

  alias Beaver.MLIR.CAPI

  def ptr(%mod{ref: ref}) do
    %__MODULE__.Ptr{
      ref: apply(CAPI, Module.concat(mod, :ptr), [ref]) |> check!(),
      element_kind: mod
    }
  end

  def opaque_ptr(%mod{ref: ref}) do
    maker = Module.concat([mod, :opaque_ptr])

    %__MODULE__.OpaquePtr{
      ref: apply(CAPI, maker, [ref]) |> check!()
    }
  end

  def opaque_ptr(%__MODULE__.Array{} = array) do
    array
    |> __MODULE__.Array.as_opaque()
  end

  def array(data, module, opts \\ [mut: false])

  def array(data, module, opts) when is_binary(data) do
    mut = Keyword.get(opts, :mut) || false
    func = if mut, do: "mut_array", else: "array"

    ref =
      apply(CAPI, Module.concat([module, func]), [
        data
      ])
      |> check!()

    %__MODULE__.Array{ref: ref, element_kind: module}
  end

  def array(data, module, opts) when is_list(data) do
    mut = Keyword.get(opts, :mut) || false
    func = if mut, do: "mut_array", else: "array"

    ref =
      apply(CAPI, Module.concat([module, func]), [
        Enum.map(data, &Kinda.unwrap_ref/1)
      ])
      |> check!()

    %__MODULE__.Array{ref: ref, element_kind: module}
  end

  def to_term(%__MODULE__.Ptr{ref: ref, element_kind: __MODULE__.OpaquePtr}) do
    forward(__MODULE__.OpaquePtr, :primitive, [ref])
  end

  def to_term(%mod{ref: ref}) do
    forward(mod, :primitive, [ref])
  end

  def bag(%{bag: _bag} = v, nil) do
    v
  end

  def bag(%{bag: bag} = v, list) when is_list(list) do
    %{v | bag: MapSet.union(MapSet.new(list), bag)}
  end

  def bag(%{bag: bag} = v, item) do
    %{v | bag: MapSet.put(bag, item)}
  end

  def c_string(value) when is_binary(value) do
    %__MODULE__.C.String{ref: check!(CAPI.beaver_raw_get_resource_c_string(value))}
  end

  def check!(ref) do
    case ref do
      {:error, e} ->
        raise e

      ref ->
        ref
    end
  end

  def forward(
        element_kind,
        kind_func_name,
        args
      ) do
    apply(CAPI, Module.concat(element_kind, kind_func_name), args)
    |> check!()
  end

  def dump(%kind{ref: ref} = v) do
    Beaver.Native.forward(kind, "dump", [ref])
    v
  end
end