-module(glriff). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/glriff.gleam"). -export([to_bit_array/1, from_bit_array/1]). -export_type([chunk/0, from_bit_array_error/0, to_chunk_list_error/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 chunk() :: {chunk, bitstring(), bitstring()} | {list_chunk, list(chunk())} | {riff_chunk, bitstring(), list(chunk())}. -type from_bit_array_error() :: {failed_to_create_chunk_list, to_chunk_list_error()} | invalid_format. -type to_chunk_list_error() :: invalid_id | invalid_size | invalid_data | {size_is_difference, integer(), integer()}. -file("src/glriff.gleam", 49). ?DOC( " Converts a RIFF chunk to its binary representation.\n" "\n" " This function serializes a chunk into the RIFF binary format, which consists of:\n" " - For basic Chunk: FourCC (4 bytes) + Size (4 bytes, little-endian) + Data\n" " - For ListChunk: \"LIST\" (4 bytes) + Size (4 bytes) + Concatenated sub-chunks\n" " - For RiffChunk: \"RIFF\" (4 bytes) + Size (4 bytes) + FourCC (4 bytes) + Concatenated sub-chunks\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let chunk = Chunk(four_cc: <<\"fmt \">>, data: <<\"EXAMPLE_DATA\">>)\n" " let binary = to_bit_array(chunk)\n" " // Returns: <<\"fmt \", 12:size(32)-little, \"EXAMPLE_DATA\">>\n" " ```\n" ). -spec to_bit_array(chunk()) -> bitstring(). to_bit_array(Chunk) -> case Chunk of {chunk, Four_cc, Data} -> Size = <<(erlang:byte_size(Data)):32/little>>, _pipe = [Four_cc, Size, Data], gleam_stdlib:bit_array_concat(_pipe); {list_chunk, Chunk_list} -> Id = <<"LIST"/utf8>>, Data@1 = begin _pipe@1 = Chunk_list, _pipe@3 = gleam@list:map(_pipe@1, fun(V) -> _pipe@2 = V, to_bit_array(_pipe@2) end), gleam_stdlib:bit_array_concat(_pipe@3) end, Size@1 = <<(erlang:byte_size(Data@1)):32/little>>, _pipe@4 = [Id, Size@1, Data@1], gleam_stdlib:bit_array_concat(_pipe@4); {riff_chunk, Four_cc@1, Chunk_list@1} -> Riff_header = <<"RIFF"/utf8>>, Data@2 = begin _pipe@5 = Chunk_list@1, _pipe@7 = gleam@list:map(_pipe@5, fun(V@1) -> _pipe@6 = V@1, to_bit_array(_pipe@6) end), gleam_stdlib:bit_array_concat(_pipe@7) end, Size@2 = <<(erlang:byte_size(Data@2)):32/little>>, _pipe@8 = [Riff_header, Size@2, Four_cc@1, Data@2], gleam_stdlib:bit_array_concat(_pipe@8) end. -file("src/glriff.gleam", 222). ?DOC( " Recursively parses a list of chunks from binary data starting at a given position.\n" "\n" " This internal helper function reads chunks sequentially from the binary data,\n" " starting at the specified byte position. It continues until all data has been\n" " consumed or an error occurs.\n" "\n" " Each chunk consists of:\n" " - 4 bytes: FourCC identifier\n" " - 4 bytes: Size (little-endian integer)\n" " - N bytes: Data (where N = size from previous field)\n" "\n" " ## Parameters\n" "\n" " - `bits`: The binary data containing RIFF chunks\n" " - `position`: The byte offset to start reading from\n" "\n" " ## Returns\n" "\n" " Returns `Ok(chunks)` with a list of successfully parsed chunks, or\n" " `Error(reason)` if parsing fails.\n" ). -spec to_chunk_list(bitstring(), integer()) -> {ok, list(chunk())} | {error, to_chunk_list_error()}. to_chunk_list(Bits, Position) -> Total_size = erlang:byte_size(Bits), case Position >= Total_size of true -> {ok, []}; false -> gleam@result:'try'( begin _pipe = gleam_stdlib:bit_array_slice(Bits, Position, 4), gleam@result:replace_error(_pipe, invalid_id) end, fun(Id) -> gleam@result:'try'( begin _pipe@1 = gleam_stdlib:bit_array_slice( Bits, Position + 4, 4 ), gleam@result:replace_error(_pipe@1, invalid_size) end, fun(Size_bits) -> Size@1 = case Size_bits of <> -> Size; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"glriff"/utf8>>, function => <<"to_chunk_list"/utf8>>, line => 242, value => _assert_fail, start => 7654, 'end' => 7701, pattern_start => 7665, pattern_end => 7689}) end, gleam@result:'try'( begin _pipe@2 = gleam_stdlib:bit_array_slice( Bits, Position + 8, Size@1 ), gleam@result:replace_error( _pipe@2, invalid_data ) end, fun(Data) -> gleam@bool:guard( Size@1 /= erlang:byte_size(Data), {error, {size_is_difference, Size@1, erlang:byte_size(Data)}}, fun() -> Next_position = (Position + 8) + Size@1, Chunk = {chunk, Id, Data}, Rest_chunks = to_chunk_list( Bits, Next_position ), case Rest_chunks of {ok, Values} -> {ok, [Chunk | Values]}; {error, Err} -> {error, Err} end end ) end ) end ) end ) end. -file("src/glriff.gleam", 120). ?DOC( " Parses a RIFF chunk from binary data.\n" "\n" " This function reads binary data in RIFF format and constructs the appropriate\n" " `Chunk` variant based on the chunk identifier:\n" " - \"RIFF\" → `RiffChunk`\n" " - \"LIST\" → `ListChunk`\n" " - Other FourCC → basic `Chunk`\n" "\n" " The function validates that:\n" " - The data contains at least 8 bytes (FourCC + size)\n" " - The declared size matches the actual data size\n" " - All sub-chunks (for RIFF and LIST) are valid\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let binary = <<\"fmt \", 12:size(32)-little, \"EXAMPLE_DATA\">>\n" " case from_bit_array(binary) {\n" " Ok(chunk) -> // Successfully parsed chunk\n" " Error(InvalidFormat) -> // Invalid RIFF data\n" " }\n" " ```\n" ). -spec from_bit_array(bitstring()) -> {ok, chunk()} | {error, from_bit_array_error()}. from_bit_array(Bits) -> gleam@result:'try'( begin _pipe = gleam_stdlib:bit_array_slice(Bits, 0, 4), gleam@result:replace_error(_pipe, invalid_format) end, fun(Id) -> gleam@result:'try'( begin _pipe@1 = gleam_stdlib:bit_array_slice(Bits, 4, 4), gleam@result:replace_error(_pipe@1, invalid_format) end, fun(Size_bits) -> Size@1 = case Size_bits of <> -> Size; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"glriff"/utf8>>, function => <<"from_bit_array"/utf8>>, line => 131, value => _assert_fail, start => 4439, 'end' => 4486, pattern_start => 4450, pattern_end => 4474}) end, case Id of <<"RIFF"/utf8>> -> gleam@result:'try'( begin _pipe@2 = gleam_stdlib:bit_array_slice( Bits, 8, 4 ), gleam@result:replace_error( _pipe@2, invalid_format ) end, fun(Four_cc) -> Last_index = erlang:byte_size(Bits) - 12, case Last_index of 0 -> {ok, {riff_chunk, Four_cc, []}}; _ -> gleam@result:'try'( begin _pipe@3 = to_chunk_list( Bits, 12 ), gleam@result:map_error( _pipe@3, fun(Field@0) -> {failed_to_create_chunk_list, Field@0} end ) end, fun(Chunks) -> {ok, {riff_chunk, Four_cc, Chunks}} end ) end end ); <<"LIST"/utf8>> -> Last_index@1 = erlang:byte_size(Bits) - 8, case Last_index@1 of 0 -> {ok, {list_chunk, []}}; _ -> gleam@result:'try'( begin _pipe@4 = to_chunk_list(Bits, 8), gleam@result:map_error( _pipe@4, fun(Field@0) -> {failed_to_create_chunk_list, Field@0} end ) end, fun(Chunks@1) -> {ok, {list_chunk, Chunks@1}} end ) end; _ -> Last_index@2 = erlang:byte_size(Bits) - 8, gleam@result:'try'( begin _pipe@5 = gleam_stdlib:bit_array_slice( Bits, 8, Last_index@2 ), gleam@result:replace_error( _pipe@5, invalid_format ) end, fun(Data) -> case Size@1 =:= erlang:byte_size(Data) of true -> {ok, {chunk, Id, Data}}; false -> {error, invalid_format} end end ) end end ) end ).