lib/crypto/bit_utils.ex

defmodule BitcoinLib.Crypto.BitUtils do
  @moduledoc """
  Bitwise operations
  """

  @doc """
  Converts a binary into a list of smaller binaries according to the bit size specification

  ## Examples
      iex> 0x6C7AB2F961A
      ...> |> Binary.from_integer()
      ...> |> BitcoinLib.Crypto.BitUtils.split(11)
      [
        <<6, 6::size(3)>>,
        <<61, 2::size(3)>>,
        <<203, 7::size(3)>>,
        <<44, 1::size(3)>>,
        <<10::size(4)>>
      ]
  """
  @spec split(binary(), integer()) :: list(binary())
  def split(binary, bit_size) do
    split(binary, bit_size, [])
  end

  @doc """
  Regroups a list of binaries into a single one

  ## Examples
      iex> [<<6, 6::size(3)>>, <<61, 2::size(3)>>, <<203, 7::size(3)>>, <<44, 1::size(3)>>, <<10::size(4)>>]
      ...> |> BitcoinLib.Crypto.BitUtils.combine
      <<6, 199, 171, 47, 150, 26>>
  """
  @spec combine(list(binary())) :: binary()
  def combine(binary_list) do
    binary_list
    |> Enum.reduce(<<>>, fn binary, acc ->
      <<acc::bitstring, binary::bitstring>>
    end)
  end

  defp split(binary, bit_size, acc) when bit_size(binary) <= bit_size do
    Enum.reverse([binary | acc])
  end

  defp split(binary, bit_size, acc) do
    <<chunk::size(bit_size), rest::bitstring>> = binary
    split(rest, bit_size, [<<chunk::size(bit_size)>> | acc])
  end
end