-module(gpkm@utils@bytes). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([bits_to_bytes/1, bytes_to_bits/1, slice/3, at/2, ordered_slice/4, chunked_slice/5, to_int/1, take_upper_16_bits/1, i16_to_i8_bytes/2, is_bit_set/2]). -export_type([endian/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. -type endian() :: big_endian | little_endian. -file("src/gpkm/utils/bytes.gleam", 17). -spec bits_to_bytes(bitstring()) -> list(integer()). bits_to_bytes(Bits) -> erlang:binary_to_list(Bits). -file("src/gpkm/utils/bytes.gleam", 20). -spec bytes_to_bits(list(integer())) -> bitstring(). bytes_to_bits(Bytes) -> erlang:list_to_binary(Bytes). -file("src/gpkm/utils/bytes.gleam", 32). ?DOC( " Extracts a slice from a bytes list\n" "\n" " ```gleam\n" " [0,1,2,3,4,5] |> slice(1, 4)\n" " // -> [1,2,3,4]\n" "\n" " [0,1,2,3,4,5] |> slice(2, 2)\n" " // -> [2]\n" " ```\n" ). -spec slice(list(integer()), integer(), integer()) -> list(integer()). slice(Bs, Start, End) -> _pipe = Bs, _pipe@1 = gleam@list:take(_pipe, End + 1), gleam@list:drop(_pipe@1, Start). -file("src/gpkm/utils/bytes.gleam", 52). ?DOC( " Makes a single-element slice from a bytes list\n" " at a given index\n" "\n" " If the index is out of bounds, an empty bytes list is returned\n" " \n" " ```gleam\n" " [0,1,2,3,4,5] |> at(2)\n" " // -> [2]\n" "\n" " [0,1,2,3,4,5] |> at(42)\n" " // -> []\n" "\n" " [] |> at(42)\n" " // -> []\n" " ```\n" ). -spec at(list(integer()), integer()) -> list(integer()). at(Xs, Index) -> slice(Xs, Index, Index). -file("src/gpkm/utils/bytes.gleam", 56). -spec reorder_slice(list(integer()), endian()) -> list(integer()). reorder_slice(Xs, Endian) -> case Endian of big_endian -> Xs; little_endian -> lists:reverse(Xs) end. -file("src/gpkm/utils/bytes.gleam", 74). ?DOC( " Extracts a slice from a bytes list\n" " reordering it according to a target endian\n" "\n" " ```gleam\n" " [0,1,2,3,4,5] |> ordered_slice(1, 4, BigEndian)\n" " // -> [1,2,3,4]\n" "\n" " [0,1,2,3,4,5] |> ordered_slice(1, 4, LittleEndian)\n" " // -> [4,3,2,1]\n" " ```\n" ). -spec ordered_slice(list(integer()), integer(), integer(), endian()) -> list(integer()). ordered_slice(Xs, Start, End, Endian) -> _pipe = Xs, _pipe@1 = slice(_pipe, Start, End), reorder_slice(_pipe@1, Endian). -file("src/gpkm/utils/bytes.gleam", 89). ?DOC( " Makes sized chunks from a slice of a bytes list,\n" " reordering each chunk according to the target endian\n" "\n" " ```gleam\n" " [0,1,2,3,4,5,6,7,8,9] |> chunked_slice(1, 8, 2, BigEndian)\n" " // -> [[1,2], [3,4], [5,6], [7,8]]\n" "\n" " [0,1,2,3,4,5,6,7,8,9] |> chunked_slice(1, 8, 4, LittleEndian)\n" " // -> [[4,3,2,1], [8,7,6,5]]\n" " ```\n" ). -spec chunked_slice(list(integer()), integer(), integer(), integer(), endian()) -> list(list(integer())). chunked_slice(Xs, Start, End, Chunk_size, Endian) -> _pipe = Xs, _pipe@1 = slice(_pipe, Start, End), _pipe@2 = gleam@list:sized_chunk(_pipe@1, Chunk_size), gleam@list:map( _pipe@2, fun(_capture) -> reorder_slice(_capture, Endian) end ). -file("src/gpkm/utils/bytes.gleam", 114). ?DOC( " Converts a bytes list to a number, by concatenating the bytes\n" "\n" " ```gleam\n" " // example when: max_byte_value = 0xff\n" "\n" " [0x42, 0xff] |> int.undigits(0xff + 1)\n" " // -> 0x42ff\n" "\n" " [0xde, 0xad, 0xbe, 0xef] |> int.undigits(0xff + 1)\n" " // -> 0xdeadbeef\n" " ```\n" ). -spec to_int(list(integer())) -> integer(). to_int(Xs) -> Max_byte_value = 16#ff, _pipe = Xs, _pipe@1 = gleam@int:undigits(_pipe, Max_byte_value + 1), gleam@result:unwrap(_pipe@1, 0). -file("src/gpkm/utils/bytes.gleam", 147). ?DOC( " Takes the 16 leftmost bits from the 32 rightmost bits of a numbe\n" "\n" " To return the \"upper 16 bits\" of the rand number (that may be bigger than 32bits)\n" " the number should first be cast as a 32bits integer (using a 32bits bitmask)\n" " then we can take its first 16 bits (by shifting 16bits to the right)\n" "\n" " ```gleam\n" " take_upper_16_bits(0xdeadbeef)\n" " // -> 0xdead\n" "\n" " take_upper_16_bits(0xf00d1337deadbeef)\n" " // -> 0xdead\n" "\n" " take_upper_16_bits(0b00001111000011110000000000000000)\n" " // -> 0b0000111100001111\n" " ```\n" "\n" " A very uneffective implementation could also be:\n" "\n" " ```gleam\n" " number\n" " |> int.to_base2\n" " |> string.slice(-32, 16)\n" " |> int.base_parse(2)\n" " |> result.unwrap(0)\n" " ```\n" ). -spec take_upper_16_bits(integer()) -> integer(). take_upper_16_bits(Number) -> As_i32 = fun(_capture) -> erlang:'band'(_capture, 16#ffffffff) end, Get_first_16_bits = fun(_capture@1) -> erlang:'bsr'(_capture@1, 16) end, _pipe = Number, _pipe@1 = As_i32(_pipe), Get_first_16_bits(_pipe@1). -file("src/gpkm/utils/bytes.gleam", 164). ?DOC( " Converts a 16 bits int, to an 8 bits int, as a bytes list\n" "\n" " ```gleam\n" " 0x0123 |> i16_to_i8_bytes(BigEndian)\n" " // -> [0x01, 0x23]\n" "\n" " 0x0123 |> i16_to_i8_bytes(LittleEndian)\n" " // -> [0x23, 0x01]\n" " ```\n" ). -spec i16_to_i8_bytes(integer(), endian()) -> list(integer()). i16_to_i8_bytes(I16, Endian) -> I8_left = erlang:'bsr'(I16, 8), I8_right = erlang:'band'(I16, 16#ff), case Endian of little_endian -> [I8_right, I8_left]; big_endian -> [I8_left, I8_right] end. -file("src/gpkm/utils/bytes.gleam", 191). ?DOC( " Checks if nth bit (from right to left) of a number,\n" " is set (!= 0) using bitwise\n" " ```gleam\n" " let num = 10 // 10 == 0 b 1 0 1 0 // number (as bits)\n" " // ^ ^ ^ ^\n" " // 3 2 1 0 // bit_position\n" "\n" " is_bit_set(num, 3)\n" " // -> True\n" " \n" " is_bit_set(num, 2)\n" " // -> False\n" " \n" " is_bit_set(num, 1)\n" " // -> True\n" " ```\n" ). -spec is_bit_set(integer(), integer()) -> boolean(). is_bit_set(X, N) -> Mask = begin _pipe = 1, erlang:'bsl'(_pipe, N) end, Masked_nth_bit = begin _pipe@1 = X, erlang:'band'(_pipe@1, Mask) end, Masked_nth_bit /= 0.