evoq_bit_flags (evoq v1.14.1)

View Source

Bit flag manipulation for aggregate state management.

This module provides functions for working with bitwise flags, which are particularly useful in event-sourced systems where aggregate state can be represented as a set of flags (finite state machine).

Why Use Bit Flags?

  • Memory Efficiency: Store multiple boolean states in a single integer
  • Performance: Bitwise operations are extremely fast
  • Atomic Operations: Update multiple flags in a single operation
  • Event Sourcing: Efficiently represent aggregate state in event streams
  • Database Queries: Use bitwise operators in SQL/NoSQL queries

Flag Values

Flags must be powers of 2 to occupy unique bit positions:

  -define(NONE,       0).    % 2#00000000
  -define(CREATED,    1).    % 2#00000001
  -define(VALIDATED,  2).    % 2#00000010
  -define(PROCESSING, 4).    % 2#00000100
  -define(COMPLETED,  8).    % 2#00001000
  -define(CANCELLED, 16).    % 2#00010000
  -define(ARCHIVED,  32).    % 2#00100000

Example Usage

  %% Start with no flags
  State0 = 0,
 
  %% Set CREATED flag
  State1 = evoq_bit_flags:set(State0, 1),   % State1 = 1
 
  %% Set VALIDATED flag
  State2 = evoq_bit_flags:set(State1, 2),   % State2 = 3
 
  %% Check if CREATED is set
  true = evoq_bit_flags:has(State2, 1),
 
  %% Check if CANCELLED is set
  false = evoq_bit_flags:has(State2, 16).

Inspired by C# Flags enum attribute.

Summary

Functions

Decomposes a number into its power-of-2 components.

Returns true if the flag is set in the target state.

Returns true if ALL flags are set in the target state.

Returns true if ANY flag is set in the target state.

Returns true if the flag is NOT set in the target state.

Returns the description of the highest set flag.

Returns the description of the lowest set flag.

Sets a flag in the target state using bitwise OR.

Sets multiple flags in the target state.

Returns a list of flag descriptions that are set in the target state.

Returns a comma-separated string of flag descriptions.

Returns a string of flag descriptions with custom separator.

Unsets a flag in the target state using bitwise AND with NOT.

Unsets multiple flags in the target state.

Types

flag/0

-type flag() :: pos_integer().

flag_map/0

-type flag_map() :: #{non_neg_integer() => binary() | string()}.

flags/0

-type flags() :: non_neg_integer().

Functions

decompose(Target)

-spec decompose(flags()) -> [flag()].

Decomposes a number into its power-of-2 components.

Example:

  [4, 32, 64] = evoq_bit_flags:decompose(100).
  [1, 2, 4, 8] = evoq_bit_flags:decompose(15).

has(Target, Flag)

-spec has(flags(), flag()) -> boolean().

Returns true if the flag is set in the target state.

Example:

  true = evoq_bit_flags:has(100, 64).
  false = evoq_bit_flags:has(100, 8).

has_all(Target, Flags)

-spec has_all(flags(), [flag()]) -> boolean().

Returns true if ALL flags are set in the target state.

Example:

  true = evoq_bit_flags:has_all(100, [4, 32, 64]).
  false = evoq_bit_flags:has_all(100, [4, 8]).

has_any(Target, Flags)

-spec has_any(flags(), [flag()]) -> boolean().

Returns true if ANY flag is set in the target state.

Example:

  true = evoq_bit_flags:has_any(100, [8, 64]).
  false = evoq_bit_flags:has_any(100, [1, 2, 8]).

has_not(Target, Flag)

-spec has_not(flags(), flag()) -> boolean().

Returns true if the flag is NOT set in the target state.

Example:

  false = evoq_bit_flags:has_not(100, 64).
  true = evoq_bit_flags:has_not(100, 8).

highest(N, FlagMap)

-spec highest(flags(), flag_map()) -> binary() | string() | undefined.

Returns the description of the highest set flag.

Example:

  <<"Ready">> = evoq_bit_flags:highest(100, #{4 => <<"Completed">>, 32 => <<"Archived">>, 64 => <<"Ready">>}).

lowest(N, FlagMap)

-spec lowest(flags(), flag_map()) -> binary() | string() | undefined.

Returns the description of the lowest set flag.

Example:

  <<"Completed">> = evoq_bit_flags:lowest(100, #{4 => <<"Completed">>, 32 => <<"Archived">>, 64 => <<"Ready">>}).

set(Target, Flag)

-spec set(flags(), flag()) -> flags().

Sets a flag in the target state using bitwise OR.

Example:

  100 = evoq_bit_flags:set(36, 64).
  %% 36 = 2#00100100, 64 = 2#01000000
  %% Result: 2#01100100 = 100

set_all(Target, Flags)

-spec set_all(flags(), [flag()]) -> flags().

Sets multiple flags in the target state.

Example:

  228 = evoq_bit_flags:set_all(36, [64, 128]).

to_list(N, FlagMap)

-spec to_list(flags(), flag_map()) -> [binary() | string()].

Returns a list of flag descriptions that are set in the target state.

Example:

  FlagMap = #{0 => <<"None">>, 4 => <<"Completed">>, 32 => <<"Archived">>, 64 => <<"Ready">>},
  [<<"Completed">>, <<"Archived">>, <<"Ready">>] = evoq_bit_flags:to_list(100, FlagMap).

to_string(N, FlagMap)

-spec to_string(flags(), flag_map()) -> binary().

Returns a comma-separated string of flag descriptions.

Example:

  FlagMap = #{4 => <<"Completed">>, 32 => <<"Archived">>, 64 => <<"Ready">>},
  <<"Completed, Archived, Ready">> = evoq_bit_flags:to_string(100, FlagMap).

to_string(N, FlagMap, Separator)

-spec to_string(flags(), flag_map(), binary()) -> binary().

Returns a string of flag descriptions with custom separator.

Example:

  <<"Completed | Archived | Ready">> = evoq_bit_flags:to_string(100, FlagMap, <<" | ">>).

unset(Target, Flag)

-spec unset(flags(), flag()) -> flags().

Unsets a flag in the target state using bitwise AND with NOT.

Example:

  36 = evoq_bit_flags:unset(100, 64).
  %% 100 = 2#01100100, 64 = 2#01000000
  %% Result: 2#00100100 = 36

unset_all(Target, Flags)

-spec unset_all(flags(), [flag()]) -> flags().

Unsets multiple flags in the target state.

Example:

  36 = evoq_bit_flags:unset_all(228, [64, 128]).