Abit.Counter (Abit v1.0.0)

Copy Markdown View Source

Uses :atomics as an array of counters with N bits per counter. An :atomics reference stores an array of 64-bit integers.

Supported counter widths and value ranges:

bits | unsigned value range | signed value range
2    | 0..3                 | -2..1
4    | 0..15                | -8..7
8    | 0..255               | -128..127
16   | 0..65535             | -32768..32767
32   | 0..4294967295        | -2147483648..2147483647

If you need 64-bit counters, use Erlang counters.

The :wrap_around option defaults to false, which is a safe default for these small counters. When :wrap_around is false and an operation would produce an out-of-bounds value, put/3 and add/3 return {:error, :value_out_of_bounds} without changing the stored value.

While Erlang :atomics elements are 1-indexed, Abit.Counter counters are 0-indexed.

Enumerable protocol

Abit.Counter implements the Enumerable protocol, so all Enum functions can be used:

iex> c = Abit.Counter.new(1000, 16, signed: false)
iex> c |> Abit.Counter.put(700, 54321)
iex> c |> Enum.max()
54321

Examples

iex> c = Abit.Counter.new(1000, 8, signed: false)
iex> c |> Abit.Counter.put(0, 100)
{:ok, {0, 100}}
iex> c |> Abit.Counter.add(0, 100)
{:ok, {0, 200}}
iex> c |> Abit.Counter.add(0, 100)
{:error, :value_out_of_bounds}

Summary

Functions

Increments the value of the counter at index by incr.

Sets all elements in the given counter array to 0.

Returns the value of the counter at index.

Returns all counters packed into the atomics element at index.

Returns true if any counter has the value integer, false otherwise.

Returns a new %Abit.Counter{} struct.

Puts the value into the counter at index.

Types

t()

@type t() :: %Abit.Counter{
  atomics_ref: reference(),
  counters_bit_size: 2 | 4 | 8 | 16 | 32,
  max: pos_integer(),
  min: integer(),
  signed: boolean(),
  size: pos_integer(),
  wrap_around: boolean()
}

Functions

add(counter, index, incr)

@spec add(t(), non_neg_integer(), integer()) ::
  {:ok, {non_neg_integer(), integer()}} | {:error, :value_out_of_bounds}

Increments the value of the counter at index by incr.

Returns {:ok, {index, final_value}}, or {:error, :value_out_of_bounds} if wrap_around is false and the value is out of bounds.

Examples

iex> c = Abit.Counter.new(10, 8)
iex> c |> Abit.Counter.add(7, -12)
{:ok, {7, -12}}
iex> c |> Abit.Counter.add(7, 36)
{:ok, {7, 24}}
iex> c |> Abit.Counter.put(1, 1000)
{:error, :value_out_of_bounds}

clear(counter)

(since 0.4.0)
@spec clear(t()) :: t()

Sets all elements in the given counter array to 0.

Returns counter.

Examples

iex> c = Abit.Counter.new(100, 8)
iex> c |> Abit.Counter.put(3, 70)
iex> c |> Abit.Counter.clear()
iex> c |> Abit.Counter.get(3)
0

get(counter, index)

@spec get(t(), non_neg_integer()) :: integer()

Returns the value of the counter at index.

Examples

iex> c = Abit.Counter.new(10, 8)
iex> c |> Abit.Counter.get(7)
0

get_all_at_atomic(counter, atomic_index)

(since 0.2.4)
@spec get_all_at_atomic(t(), pos_integer()) :: [integer()]

Returns all counters packed into the atomics element at index.

Indices in :atomics are one-based.

Examples

iex> c = Abit.Counter.new(100, 8)
iex> c |> Abit.Counter.put(3, -70)
iex> c |> Abit.Counter.get_all_at_atomic(1)
[0, 0, 0, -70, 0, 0, 0, 0]

member?(counter, int)

(since 0.2.4)
@spec member?(t(), integer()) :: boolean()

Returns true if any counter has the value integer, false otherwise.

Examples

iex> c = Abit.Counter.new(100, 8)
iex> c |> Abit.Counter.member?(0)
true
iex> c |> Abit.Counter.member?(80)
false

new(size, counters_bit_size, options \\ [])

@spec new(non_neg_integer(), 2 | 4 | 8 | 16 | 32, keyword()) :: t()

Returns a new %Abit.Counter{} struct.

  • size - the minimum number of counters. Counters fully fill the :atomics array, so check the :size key in the returned %Abit.Counter{} for the exact count.
  • counters_bit_size - how many bits a counter should use

Options

  • :signed - whether to have signed or unsigned counters. Defaults to true.
  • :wrap_around - whether counters should wrap around. Defaults to false.

Examples

Abit.Counter.new(100, 8) # minimum 100 counters; 8 bits signed
Abit.Counter.new(10_000, 16, signed: false) # minimum 10_000 counters; 16 bits unsigned
Abit.Counter.new(10_000, 16, wrap_around: false) # don't wrap around

put(counter, index, value)

@spec put(t(), non_neg_integer(), integer()) ::
  {:ok, {non_neg_integer(), integer()}} | {:error, :value_out_of_bounds}

Puts the value into the counter at index.

Returns {:ok, {index, final_value}}, or {:error, :value_out_of_bounds} if wrap_around is false and the value is out of bounds.

Examples

iex> c = Abit.Counter.new(10, 8)
iex> c |> Abit.Counter.put(7, -12)
{:ok, {7, -12}}
iex> c |> Abit.Counter.get(7)
-12
iex> c |> Abit.Counter.put(7, 128)
{:error, :value_out_of_bounds}