feat(bitfield): add bitfield command engine

This commit is contained in:
dualfroz committed 2026-08-24 20:23:00 +02:00
1 parent 28a19cb4a9
commit c501aba216
1 file changed
+94
+94
View File
@@ -0,0 +1,94 @@
defmodule Excache.Bitfield do
@moduledoc false
import Bitwise
def run(binary, operations) do
{finalBinary, reversed} =
Enum.reduce(operations, {binary, []}, fn operation, {bin, results} ->
{updated, result} = applyOne(bin, operation)
{updated, [result | results]}
end)
{finalBinary, Enum.reverse(reversed)}
end
defp applyOne(binary, {:get, signed, bits, offset}) do
{binary, interpret(readBits(binary, offset, bits), signed, bits)}
end
defp applyOne(binary, {:set, signed, bits, offset, value, overflow}) do
old = interpret(readBits(binary, offset, bits), signed, bits)
case clamp(value, signed, bits, overflow) do
:fail -> {binary, nil}
stored -> {writeBits(binary, offset, bits, stored), old}
end
end
defp applyOne(binary, {:incrby, signed, bits, offset, delta, overflow}) do
old = interpret(readBits(binary, offset, bits), signed, bits)
candidate = old + delta
case clamp(candidate, signed, bits, overflow) do
:fail ->
{binary, nil}
stored ->
{writeBits(binary, offset, bits, stored), interpret(rawOf(stored, bits), signed, bits)}
end
end
defp readBits(binary, offset, bits) do
padded = padToBits(binary, offset + bits)
<<_::size(offset), raw::size(bits), _::bitstring>> = padded
raw
end
defp writeBits(binary, offset, bits, value) do
padded = padToBits(binary, offset + bits)
raw = rawOf(value, bits)
<<head::size(offset), _::size(bits), tail::bitstring>> = padded
<<head::size(offset), raw::size(bits), tail::bitstring>>
end
defp padToBits(binary, neededBits) do
neededBytes = div(neededBits + 7, 8)
missing = neededBytes - byte_size(binary)
if missing > 0, do: binary <> :binary.copy(<<0>>, missing), else: binary
end
defp interpret(raw, false, _bits), do: raw
defp interpret(raw, true, bits) do
if raw >= 1 <<< (bits - 1), do: raw - (1 <<< bits), else: raw
end
defp rawOf(value, bits), do: value &&& (1 <<< bits) - 1
defp clamp(value, signed, bits, overflow) do
{low, high} = bounds(signed, bits)
cond do
value >= low and value <= high ->
value
overflow == :wrap ->
wrap(value, signed, bits)
overflow == :sat ->
if value < low, do: low, else: high
overflow == :fail ->
:fail
end
end
defp wrap(value, signed, bits) do
raw = rawOf(value, bits)
interpret(raw, signed, bits)
end
defp bounds(false, bits), do: {0, (1 <<< bits) - 1}
defp bounds(true, bits), do: {-(1 <<< (bits - 1)), (1 <<< (bits - 1)) - 1}
end