Current section

Files

Jump to
ex_esdb lib flags.ex
Raw

lib/flags.ex

defmodule Flags do
@moduledoc """
This module is used to manipulate bitwise flags.
Inspired by: [Flags in C#](https://stackoverflow.com/questions/8447/what-does-the-flags-enum-attribute-mean-in-c)
Event souurced systems often rely on flags to indicate the state of the aggregate at any given time.
In this module, we define a set of functions that can be used to manipulate these flags.
"""
import Bitwise
@doc """
Returns the bitwise OR of two flags.
In other words, it sets the bit that corresopnds to the flag
GIVEN: original_state is `0b00100100` (integer: 36)
WHEN the flag to be set is `0b01000000` (integer: 64)
THEN the result is `0b01100100` (integer: 100)
Example:
iex> Flags.set(36, 64)
100
"""
def set(target, flag)
when is_integer(target)
and is_integer(flag),
do: target ||| flag
@doc """
Returns the bitwise AND of two flags.
In other words, it unsets the bit that corresopnds to the flag
GIVEN: original_state is `0b01100100` (integer: 100)
WHEN the flag to be unset is `0b01000000` (integer: 64)
THEN the result is `0b00100100` (integer: 36)
Example:
iex> Flags.unset(100, 64)
36
"""
def unset(target, flag)
when is_integer(target)
and is_integer(flag),
do: target &&& bnot(flag)
@doc """
Returns the bitwise OR of multiple flags against a given state.
In other words, it sets the bits that corresopnds to the flags
GIVEN: original_state is `0b00100100` (integer: 36)
WHEN the flags to be set are `[0b01000000, 0b10000000]` (integers: 64, 128)
THEN the result is `0b11100100` (integer: 228)
Example:
iex> Flags.set_all(36, [64, 128])
228
"""
def set_all(target, flags) do
Enum.reduce(flags, target, fn flag, acc ->
acc ||| flag
end)
end
def unset_all(target, flags) do
Enum.reduce(flags, target, fn flag, acc ->
acc &&& bnot(flag)
end)
end
def has?(target, flag), do: (target &&& flag) == flag
def has_not?(target, flag), do: (target &&& flag) != flag
def to_list(0, flag_map), do: [flag_map[0]]
def to_list(n, flag_map) when n > 0 do
# Extract keys (powers of 2) from the map and sort them
keys = Map.keys(flag_map) |> Enum.sort()
# Iterate through the keys and collect the flags that are set in `n`
flags =
Enum.reduce(keys, [], fn key, acc ->
if Bitwise.band(n, key) != 0 do
[flag_map[key] | acc]
else
acc
end
end)
# Since we collected the flags in reverse order, reverse the list before returning
Enum.reverse(flags)
end
def highest(n, flag_map) do
[head | _] =
to_list(n, flag_map)
|> Enum.reverse()
head
end
def has_all?(status, flags) do
flags |> Enum.all?(fn flag -> has?(status, flag) end)
end
def has_any?(status, flags) do
flags |> Enum.any?(fn flag -> has?(status, flag) end)
end
def lowest(n, flag_map) do
[head | _] =
to_list(n, flag_map)
head
end
def to_string(n, flag_map) do
to_list(n, flag_map)
|> Enum.join(", ")
end
def decompose(target) when target > 0 do
decompose(target, 1, [])
end
defp decompose(0, _, acc), do: Enum.reverse(acc)
defp decompose(target, power, acc) do
if Bitwise.band(target, power) != 0 do
decompose(target - power, power <<< 1, [power | acc])
else
decompose(target, power <<< 1, acc)
end
end
end