-module(distribute@codec). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/distribute/codec.gleam"). -export([encode/2, decode/2, decode_sized/2, int_sized_decoder/0, int_decoder/0, int_encoder/0, int/0, to_decoder/1, string_encoder/0, string_sized_decoder/0, string_decoder/0, check_32bit_length/2, float_encoder/0, float_sized_decoder/0, float_decoder/0, bool_encoder/0, bool_sized_decoder/0, bool_decoder/0, bitarray_encoder/0, bitarray_sized_decoder/0, bitarray_decoder/0, list_sized_decoder/1, list_decoder/1, list/1, list_encoder/1, nil_encoder/0, nil_sized_decoder/0, nil_decoder/0, subject_encoder/0, subject_sized_decoder/0, subject_decoder/0, string/0, float/0, bool/0, bitarray/0, nil/0, subject/0, map/3, decode_error_to_string/1, encode_error_to_string/1]). -export_type([encode_error/0, decode_error/0, codec/1]). -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 encode_error() :: {invalid_value, binary()} | {encode_failed, binary()} | {value_too_large, binary()}. -type decode_error() :: {invalid_binary, binary()} | {type_mismatch, binary()} | {decode_failed, binary()} | {insufficient_data, binary()} | decode_timeout | {tag_mismatch, binary(), binary()} | {version_mismatch, integer(), integer()} | {payload_too_large, integer()} | {list_too_long, integer(), integer()}. -type codec(FIV) :: {codec, fun((FIV) -> {ok, bitstring()} | {error, encode_error()}), fun((bitstring()) -> {ok, FIV} | {error, decode_error()}), fun((bitstring()) -> {ok, {FIV, bitstring()}} | {error, decode_error()})}. -file("src/distribute/codec.gleam", 81). -spec encode(fun((FJF) -> {ok, bitstring()} | {error, encode_error()}), FJF) -> {ok, bitstring()} | {error, encode_error()}. encode(Encoder, Value) -> Encoder(Value). -file("src/distribute/codec.gleam", 85). -spec decode( fun((bitstring()) -> {ok, FJJ} | {error, decode_error()}), bitstring() ) -> {ok, FJJ} | {error, decode_error()}. decode(Decoder, Data) -> Decoder(Data). -file("src/distribute/codec.gleam", 89). -spec decode_sized( fun((bitstring()) -> {ok, {FJN, bitstring()}} | {error, decode_error()}), bitstring() ) -> {ok, {FJN, bitstring()}} | {error, decode_error()}. decode_sized(Decoder, Data) -> Decoder(Data). -file("src/distribute/codec.gleam", 227). -spec int_sized_decoder() -> fun((bitstring()) -> {ok, {integer(), bitstring()}} | {error, decode_error()}). int_sized_decoder() -> fun(Data) -> case Data of <> -> {ok, {I, Rest}}; _ -> {error, {invalid_binary, <<"int64"/utf8>>}} end end. -file("src/distribute/codec.gleam", 236). -spec int_decoder() -> fun((bitstring()) -> {ok, integer()} | {error, decode_error()}). int_decoder() -> to_decoder(int_sized_decoder()). -file("src/distribute/codec.gleam", 215). -spec int_encoder() -> fun((integer()) -> {ok, bitstring()} | {error, encode_error()}). int_encoder() -> fun(I) -> case (I > 9223372036854775807) orelse (I < -9223372036854775808) of true -> {error, {value_too_large, <<<<"int "/utf8, (erlang:integer_to_binary(I))/binary>>/binary, " out of signed 64-bit range"/utf8>>}}; false -> {ok, <>} end end. -file("src/distribute/codec.gleam", 534). -spec int() -> codec(integer()). int() -> {codec, int_encoder(), int_decoder(), int_sized_decoder()}. -file("src/distribute/codec.gleam", 106). ?DOC( " Turn a `SizedDecoder` into a top-level `Decoder` with strict parsing.\n" "\n" " The frame must consume the binary completely. Any unconsumed\n" " trailing bytes are a protocol violation (data smuggling, double\n" " payloads, framing bug) and surface as `Error(InvalidBinary(\"trailing\n" " bytes ...\"))`. Earlier drafts silently dropped the leftover.\n" "\n" " Composite codecs that need to chain frames within one binary (lists,\n" " tuples) call the underlying `SizedDecoder` directly, so they keep\n" " working as before. Only the top-level entry point is strict.\n" ). -spec to_decoder( fun((bitstring()) -> {ok, {FJR, bitstring()}} | {error, decode_error()}) ) -> fun((bitstring()) -> {ok, FJR} | {error, decode_error()}). to_decoder(Sized) -> fun(Data) -> case Sized(Data) of {ok, {Value, <<>>}} -> {ok, Value}; {ok, {_, Rest}} -> Leftover = erlang:byte_size(Rest), {error, {invalid_binary, <<<<"trailing bytes ("/utf8, (erlang:integer_to_binary(Leftover))/binary>>/binary, ") at top level"/utf8>>}}; {error, E} -> {error, E} end end. -file("src/distribute/codec.gleam", 125). -spec string_encoder() -> fun((binary()) -> {ok, bitstring()} | {error, encode_error()}). string_encoder() -> fun(S) -> Bytes = gleam_stdlib:identity(S), Len = erlang:byte_size(Bytes), case Len > 4294967295 of true -> {error, {value_too_large, <<<<"string of "/utf8, (erlang:integer_to_binary(Len))/binary>>/binary, " bytes exceeds 32-bit length prefix"/utf8>>}}; false -> {ok, <>} end end. -file("src/distribute/codec.gleam", 141). -spec string_sized_decoder() -> fun((bitstring()) -> {ok, {binary(), bitstring()}} | {error, decode_error()}). string_sized_decoder() -> fun(Data) -> case Data of <> -> Rest_size = erlang:byte_size(Rest), case Rest_size >= Len of false -> {error, {insufficient_data, <<"string"/utf8>>}}; true -> gleam@result:'try'( begin _pipe = gleam_stdlib:bit_array_slice( Rest, 0, Len ), _pipe@1 = gleam@result:replace_error( _pipe, {insufficient_data, <<"string slice"/utf8>>} ), gleam@result:map( _pipe@1, fun distribute_ffi_utils:binary_copy/1 ) end, fun(Str_bytes) -> gleam@result:'try'( begin _pipe@2 = gleam@bit_array:to_string( Str_bytes ), gleam@result:replace_error( _pipe@2, {invalid_binary, <<"invalid UTF-8"/utf8>>} ) end, fun(S) -> Remaining = case gleam_stdlib:bit_array_slice( Rest, Len, Rest_size - Len ) of {ok, R} -> R; {error, _} -> <<>> end, {ok, {S, Remaining}} end ) end ) end; _ -> {error, {invalid_binary, <<"missing string length prefix"/utf8>>}} end end. -file("src/distribute/codec.gleam", 171). -spec string_decoder() -> fun((bitstring()) -> {ok, binary()} | {error, decode_error()}). string_decoder() -> to_decoder(string_sized_decoder()). -file("src/distribute/codec.gleam", 195). ?DOC(false). -spec check_32bit_length(integer(), binary()) -> {ok, nil} | {error, encode_error()}. check_32bit_length(Len, What) -> case Len > 4294967295 of true -> {error, {value_too_large, <<<<<>/binary, (erlang:integer_to_binary(Len))/binary>>/binary, " bytes exceeds 32-bit length prefix"/utf8>>}}; false -> {ok, nil} end. -file("src/distribute/codec.gleam", 211). ?DOC( " Runtime cap for the element count a single `list_decoder` invocation will\n" " materialise. Bounds in-process memory and CPU cost of hostile frames\n" " independently of network payload size.\n" ). -spec decoded_list_elements_cap() -> integer(). decoded_list_elements_cap() -> erlang:element(6, distribute@config:get()). -file("src/distribute/codec.gleam", 244). -spec float_encoder() -> fun((float()) -> {ok, bitstring()} | {error, encode_error()}). float_encoder() -> fun(F) -> {ok, <>} end. -file("src/distribute/codec.gleam", 248). -spec float_sized_decoder() -> fun((bitstring()) -> {ok, {float(), bitstring()}} | {error, decode_error()}). float_sized_decoder() -> fun(Data) -> case Data of <> -> {ok, {F, Rest}}; _ -> {error, {invalid_binary, <<"float64"/utf8>>}} end end. -file("src/distribute/codec.gleam", 257). -spec float_decoder() -> fun((bitstring()) -> {ok, float()} | {error, decode_error()}). float_decoder() -> to_decoder(float_sized_decoder()). -file("src/distribute/codec.gleam", 265). -spec bool_encoder() -> fun((boolean()) -> {ok, bitstring()} | {error, encode_error()}). bool_encoder() -> fun(B) -> case B of true -> {ok, <<1>>}; false -> {ok, <<0>>} end end. -file("src/distribute/codec.gleam", 274). -spec bool_sized_decoder() -> fun((bitstring()) -> {ok, {boolean(), bitstring()}} | {error, decode_error()}). bool_sized_decoder() -> fun(Data) -> case Data of <<1, Rest/binary>> -> {ok, {true, Rest}}; <<0, Rest@1/binary>> -> {ok, {false, Rest@1}}; _ -> {error, {invalid_binary, <<"bool"/utf8>>}} end end. -file("src/distribute/codec.gleam", 284). -spec bool_decoder() -> fun((bitstring()) -> {ok, boolean()} | {error, decode_error()}). bool_decoder() -> to_decoder(bool_sized_decoder()). -file("src/distribute/codec.gleam", 292). -spec bitarray_encoder() -> fun((bitstring()) -> {ok, bitstring()} | {error, encode_error()}). bitarray_encoder() -> fun(Bytes) -> Len = erlang:byte_size(Bytes), case Len > 4294967295 of true -> {error, {value_too_large, <<<<"bitarray of "/utf8, (erlang:integer_to_binary(Len))/binary>>/binary, " bytes exceeds 32-bit length prefix"/utf8>>}}; false -> {ok, <>} end end. -file("src/distribute/codec.gleam", 307). -spec bitarray_sized_decoder() -> fun((bitstring()) -> {ok, {bitstring(), bitstring()}} | {error, decode_error()}). bitarray_sized_decoder() -> fun(Data) -> case Data of <> -> Rest_size = erlang:byte_size(Rest), case Rest_size >= Len of false -> {error, {insufficient_data, <<"bitarray"/utf8>>}}; true -> gleam@result:'try'( begin _pipe = gleam_stdlib:bit_array_slice( Rest, 0, Len ), _pipe@1 = gleam@result:replace_error( _pipe, {insufficient_data, <<"bitarray slice"/utf8>>} ), gleam@result:map( _pipe@1, fun distribute_ffi_utils:binary_copy/1 ) end, fun(Bytes) -> Remaining = case gleam_stdlib:bit_array_slice( Rest, Len, Rest_size - Len ) of {ok, R} -> R; {error, _} -> <<>> end, {ok, {Bytes, Remaining}} end ) end; _ -> {error, {invalid_binary, <<"bitarray length prefix"/utf8>>}} end end. -file("src/distribute/codec.gleam", 333). -spec bitarray_decoder() -> fun((bitstring()) -> {ok, bitstring()} | {error, decode_error()}). bitarray_decoder() -> to_decoder(bitarray_sized_decoder()). -file("src/distribute/codec.gleam", 404). -spec decode_list_elements( bitstring(), fun((bitstring()) -> {ok, {FLE, bitstring()}} | {error, decode_error()}), integer(), list(FLE) ) -> {ok, {list(FLE), bitstring()}} | {error, decode_error()}. decode_list_elements(Data, Dec, Remaining, Acc) -> case Remaining of 0 -> {ok, {lists:reverse(Acc), Data}}; _ -> case Dec(Data) of {ok, {Element, Rest}} -> decode_list_elements( Rest, Dec, Remaining - 1, [Element | Acc] ); {error, E} -> {error, E} end end. -file("src/distribute/codec.gleam", 370). -spec list_sized_decoder( fun((bitstring()) -> {ok, {FKP, bitstring()}} | {error, decode_error()}) ) -> fun((bitstring()) -> {ok, {list(FKP), bitstring()}} | {error, decode_error()}). list_sized_decoder(Element_decoder) -> fun(Data) -> Cap = decoded_list_elements_cap(), case Data of <> -> case Count > Cap of true -> {error, {list_too_long, Count, Cap}}; false -> decode_list_elements(Rest, Element_decoder, Count, []) end; _ -> {error, {invalid_binary, <<"list length prefix"/utf8>>}} end end. -file("src/distribute/codec.gleam", 386). -spec list_decoder( fun((bitstring()) -> {ok, {FKT, bitstring()}} | {error, decode_error()}) ) -> fun((bitstring()) -> {ok, list(FKT)} | {error, decode_error()}). list_decoder(Element_decoder) -> to_decoder(list_sized_decoder(Element_decoder)). -file("src/distribute/codec.gleam", 574). -spec list(codec(FMD)) -> codec(list(FMD)). list(Element) -> {codec, list_encoder(erlang:element(2, Element)), list_decoder(erlang:element(4, Element)), list_sized_decoder(erlang:element(4, Element))}. -file("src/distribute/codec.gleam", 390). -spec encode_list_elements( list(FKX), fun((FKX) -> {ok, bitstring()} | {error, encode_error()}), list(bitstring()) ) -> {ok, list(bitstring())} | {error, encode_error()}. encode_list_elements(Items, Enc, Acc) -> case Items of [] -> {ok, lists:reverse(Acc)}; [Head | Tail] -> gleam@result:'try'( Enc(Head), fun(Bytes) -> encode_list_elements(Tail, Enc, [Bytes | Acc]) end ) end. -file("src/distribute/codec.gleam", 346). -spec list_encoder(fun((FKL) -> {ok, bitstring()} | {error, encode_error()})) -> fun((list(FKL)) -> {ok, bitstring()} | {error, encode_error()}). list_encoder(Element_encoder) -> fun(Items) -> Cap = decoded_list_elements_cap(), Len = erlang:length(Items), case Len > Cap of true -> {error, {value_too_large, <<<<<<"list of "/utf8, (erlang:integer_to_binary(Len))/binary>>/binary, " elements exceeds decoder cap of "/utf8>>/binary, (erlang:integer_to_binary(Cap))/binary>>}}; false -> gleam@result:'try'( encode_list_elements(Items, Element_encoder, []), fun(Chunks) -> {ok, gleam_stdlib:bit_array_concat([<> | Chunks])} end ) end end. -file("src/distribute/codec.gleam", 426). -spec nil_encoder() -> fun((nil) -> {ok, bitstring()} | {error, encode_error()}). nil_encoder() -> fun(_) -> {ok, <<>>} end. -file("src/distribute/codec.gleam", 430). -spec nil_sized_decoder() -> fun((bitstring()) -> {ok, {nil, bitstring()}} | {error, decode_error()}). nil_sized_decoder() -> fun(Data) -> {ok, {nil, Data}} end. -file("src/distribute/codec.gleam", 439). ?DOC( " Strict top-level decoder for `Nil`: the wire form is zero bytes, and\n" " **any** trailing data is a protocol violation (either a bug in the\n" " sender or smuggled payload). Earlier drafts accepted the binary\n" " regardless, defeating the strict-top-level contract that\n" " `to_decoder` enforces for every other primitive.\n" ). -spec nil_decoder() -> fun((bitstring()) -> {ok, nil} | {error, decode_error()}). nil_decoder() -> fun(Data) -> case Data of <<>> -> {ok, nil}; _ -> {error, {invalid_binary, <<<<"trailing bytes ("/utf8, (erlang:integer_to_binary( erlang:byte_size(Data) ))/binary>>/binary, ") for nil top-level decode"/utf8>>}} end end. -file("src/distribute/codec.gleam", 470). ?DOC( " Encode a `Subject(BitArray)` via `term_to_binary`. The PID\n" " inside carries node info, so it routes back cross-node.\n" "\n" " Mirrors the 32-bit-prefix validation in `string_encoder` /\n" " `bitarray_encoder` / `list_encoder`. A Subject larger than 4 GiB\n" " is absurd in practice (it would imply a 4 GiB tag), but the bound\n" " is enforced for symmetry with the rest of the codec surface.\n" ). -spec subject_encoder() -> fun((gleam@erlang@process:subject(bitstring())) -> {ok, bitstring()} | {error, encode_error()}). subject_encoder() -> fun(Sub) -> Bytes = distribute_ffi_utils:encode_subject(Sub), Len = erlang:byte_size(Bytes), case Len > 4294967295 of true -> {error, {value_too_large, <<<<"subject encoded form of "/utf8, (erlang:integer_to_binary(Len))/binary>>/binary, " bytes exceeds 32-bit length prefix"/utf8>>}}; false -> {ok, <>} end end. -file("src/distribute/codec.gleam", 486). -spec subject_sized_decoder() -> fun((bitstring()) -> {ok, {gleam@erlang@process:subject(bitstring()), bitstring()}} | {error, decode_error()}). subject_sized_decoder() -> fun(Data) -> case Data of <> -> Rest_size = erlang:byte_size(Rest), case Rest_size >= Len of false -> {error, {insufficient_data, <<"subject"/utf8>>}}; true -> case gleam_stdlib:bit_array_slice(Rest, 0, Len) of {ok, Subject_bytes} -> case distribute_ffi_utils:decode_subject_safe( Subject_bytes ) of {ok, Sub} -> Remaining = case gleam_stdlib:bit_array_slice( Rest, Len, Rest_size - Len ) of {ok, R} -> R; {error, _} -> <<>> end, {ok, {Sub, Remaining}}; {error, nil} -> {error, {decode_failed, <<"invalid subject binary"/utf8>>}} end; {error, _} -> {error, {insufficient_data, <<"subject slice"/utf8>>}} end end; _ -> {error, {invalid_binary, <<"subject length prefix"/utf8>>}} end end. -file("src/distribute/codec.gleam", 518). -spec subject_decoder() -> fun((bitstring()) -> {ok, gleam@erlang@process:subject(bitstring())} | {error, decode_error()}). subject_decoder() -> to_decoder(subject_sized_decoder()). -file("src/distribute/codec.gleam", 526). -spec string() -> codec(binary()). string() -> {codec, string_encoder(), string_decoder(), string_sized_decoder()}. -file("src/distribute/codec.gleam", 542). -spec float() -> codec(float()). float() -> {codec, float_encoder(), float_decoder(), float_sized_decoder()}. -file("src/distribute/codec.gleam", 550). -spec bool() -> codec(boolean()). bool() -> {codec, bool_encoder(), bool_decoder(), bool_sized_decoder()}. -file("src/distribute/codec.gleam", 558). -spec bitarray() -> codec(bitstring()). bitarray() -> {codec, bitarray_encoder(), bitarray_decoder(), bitarray_sized_decoder()}. -file("src/distribute/codec.gleam", 566). -spec nil() -> codec(nil). nil() -> {codec, nil_encoder(), nil_decoder(), nil_sized_decoder()}. -file("src/distribute/codec.gleam", 583). ?DOC(" Subject codec, for the request/response pattern.\n"). -spec subject() -> codec(gleam@erlang@process:subject(bitstring())). subject() -> {codec, subject_encoder(), subject_decoder(), subject_sized_decoder()}. -file("src/distribute/codec.gleam", 592). ?DOC(" Transform a codec. wrap runs after decoding, unwrap before encoding.\n"). -spec map(codec(FMJ), fun((FMJ) -> FML), fun((FML) -> FMJ)) -> codec(FML). map(C, Wrap, Unwrap) -> {codec, fun(Value) -> (erlang:element(2, C))(Unwrap(Value)) end, fun(Data) -> case (erlang:element(3, C))(Data) of {ok, Val} -> {ok, Wrap(Val)}; {error, E} -> {error, E} end end, fun(Data@1) -> case (erlang:element(4, C))(Data@1) of {ok, {Val@1, Rest}} -> {ok, {Wrap(Val@1), Rest}}; {error, E@1} -> {error, E@1} end end}. -file("src/distribute/codec.gleam", 614). -spec decode_error_to_string(decode_error()) -> binary(). decode_error_to_string(Error) -> case Error of {invalid_binary, Msg} -> <<"Invalid binary: "/utf8, Msg/binary>>; {type_mismatch, Msg@1} -> <<"Type mismatch: "/utf8, Msg@1/binary>>; {decode_failed, Msg@2} -> <<"Decode failed: "/utf8, Msg@2/binary>>; {insufficient_data, Msg@3} -> <<"Insufficient data: "/utf8, Msg@3/binary>>; decode_timeout -> <<"Decode timeout"/utf8>>; {tag_mismatch, Expected, Got} -> <<<<<<<<"Tag mismatch: expected '"/utf8, Expected/binary>>/binary, "', got '"/utf8>>/binary, Got/binary>>/binary, "'"/utf8>>; {version_mismatch, Expected@1, Got@1} -> <<<<<<"Version mismatch: expected "/utf8, (erlang:integer_to_binary(Expected@1))/binary>>/binary, ", got "/utf8>>/binary, (erlang:integer_to_binary(Got@1))/binary>>; {payload_too_large, Size} -> <<<<"Payload too large: "/utf8, (erlang:integer_to_binary(Size))/binary>>/binary, " bytes"/utf8>>; {list_too_long, Count, Cap} -> <<<<<<"List too long: declared "/utf8, (erlang:integer_to_binary(Count))/binary>>/binary, " elements, cap is "/utf8>>/binary, (erlang:integer_to_binary(Cap))/binary>> end. -file("src/distribute/codec.gleam", 638). -spec encode_error_to_string(encode_error()) -> binary(). encode_error_to_string(Error) -> case Error of {invalid_value, Msg} -> <<"Invalid value: "/utf8, Msg/binary>>; {encode_failed, Msg@1} -> <<"Encode failed: "/utf8, Msg@1/binary>>; {value_too_large, Msg@2} -> <<"Value too large: "/utf8, Msg@2/binary>> end.