-module(packkit@stream). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/packkit/stream.gleam"). -export([new_deflate_decoder/0, new_zlib_decoder/0, new_gzip_decoder/0, new_lz4_decoder/0, new_snappy_decoder/0, new_bzip2_decoder/0, new_lzw_decoder/0, new_xz_decoder/0, new_zstd_decoder/0, new_brotli_decoder/0, with_limits/2, push/2, finish/1, decode_chunks/2, new_deflate_encoder/0, new_zlib_encoder/0, new_gzip_encoder/0, new_lz4_encoder/0, new_snappy_encoder/0, new_bzip2_encoder/0, new_lzw_encoder/0, new_xz_encoder/0, new_zstd_encoder/0, new_brotli_encoder/0, encoder_with_limits/2, push_encoder/2, finish_encoder/1, encode_chunks/2]). -export_type([decoder/0, decoder_kind/0, encoder/0, encoder_kind/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. ?MODULEDOC( " Streaming helpers for codec families.\n" "\n" " `packkit/stream` exposes opaque decoder states that buffer input\n" " chunks and run a one-shot decode at `finish` time. The API matches\n" " the streaming shape the spec calls for - `new_*_decoder`, `push`,\n" " `finish` - so callers can already wire incremental pipelines even\n" " while the underlying codec decoders are still eager.\n" "\n" " **Relationship to `packkit/codec.Codec`.** The streaming\n" " constructors (`new_gzip_encoder`, `new_zstd_decoder`, ...) do NOT\n" " accept a `packkit/codec.Codec` value, so the per-codec `Level` /\n" " preset `Dictionary` settings carried by a `Codec` are not threaded\n" " through this API; every stream uses the codec's default encode\n" " strategy. This is intentional: a `Codec` is the config object\n" " `packkit.compress` / `packkit.decompress` (the one-shot facade)\n" " consumes, while `packkit/stream` only ever invokes the codec\n" " module's bare `encode/1` / `decode/1` entry points. Callers that\n" " need a non-default level or a preset dictionary should call the\n" " per-codec module directly (`gzip.encode_with_header`,\n" " `zlib.encode_with_dictionary`, `deflate.encode_dynamic`, …) and\n" " drive the chunking themselves until the streaming API grows\n" " codec-typed constructors. The `Limits` value, by contrast, IS\n" " honoured incrementally — see [push] / [push_encoder].\n" ). -opaque decoder() :: {decoder, decoder_kind(), list(bitstring()), integer(), packkit@limit:limits()}. -type decoder_kind() :: deflate | zlib | gzip | lz4 | snappy | bzip2 | lzw | xz | zstd | brotli. -opaque encoder() :: {encoder, encoder_kind(), list(bitstring()), integer(), packkit@limit:limits()}. -type encoder_kind() :: enc_deflate | enc_zlib | enc_gzip | enc_lz4 | enc_snappy | enc_bzip2 | enc_lzw | enc_xz | enc_zstd | enc_brotli. -file("src/packkit/stream.gleam", 123). -spec new_decoder(decoder_kind(), packkit@limit:limits()) -> decoder(). new_decoder(Kind, Limits) -> {decoder, Kind, [], 0, Limits}. -file("src/packkit/stream.gleam", 72). ?DOC(" Start a new incremental DEFLATE decoder using the default limits.\n"). -spec new_deflate_decoder() -> decoder(). new_deflate_decoder() -> new_decoder(deflate, packkit@limit:default()). -file("src/packkit/stream.gleam", 77). ?DOC(" Start a new incremental zlib decoder using the default limits.\n"). -spec new_zlib_decoder() -> decoder(). new_zlib_decoder() -> new_decoder(zlib, packkit@limit:default()). -file("src/packkit/stream.gleam", 82). ?DOC(" Start a new incremental gzip decoder using the default limits.\n"). -spec new_gzip_decoder() -> decoder(). new_gzip_decoder() -> new_decoder(gzip, packkit@limit:default()). -file("src/packkit/stream.gleam", 87). ?DOC(" Start a new incremental LZ4 frame decoder using the default limits.\n"). -spec new_lz4_decoder() -> decoder(). new_lz4_decoder() -> new_decoder(lz4, packkit@limit:default()). -file("src/packkit/stream.gleam", 93). ?DOC( " Start a new incremental Snappy (framed) decoder using the default\n" " limits. See [packkit/snappy] for the raw-block variants.\n" ). -spec new_snappy_decoder() -> decoder(). new_snappy_decoder() -> new_decoder(snappy, packkit@limit:default()). -file("src/packkit/stream.gleam", 98). ?DOC(" Start a new incremental bzip2 decoder using the default limits.\n"). -spec new_bzip2_decoder() -> decoder(). new_bzip2_decoder() -> new_decoder(bzip2, packkit@limit:default()). -file("src/packkit/stream.gleam", 104). ?DOC( " Start a new incremental Unix `.Z` (LZW) decoder using the default\n" " limits.\n" ). -spec new_lzw_decoder() -> decoder(). new_lzw_decoder() -> new_decoder(lzw, packkit@limit:default()). -file("src/packkit/stream.gleam", 109). ?DOC(" Start a new incremental xz decoder using the default limits.\n"). -spec new_xz_decoder() -> decoder(). new_xz_decoder() -> new_decoder(xz, packkit@limit:default()). -file("src/packkit/stream.gleam", 114). ?DOC(" Start a new incremental zstd decoder using the default limits.\n"). -spec new_zstd_decoder() -> decoder(). new_zstd_decoder() -> new_decoder(zstd, packkit@limit:default()). -file("src/packkit/stream.gleam", 119). ?DOC(" Start a new incremental brotli decoder using the default limits.\n"). -spec new_brotli_decoder() -> decoder(). new_brotli_decoder() -> new_decoder(brotli, packkit@limit:default()). -file("src/packkit/stream.gleam", 128). ?DOC(" Replace the limits used by an incremental decoder.\n"). -spec with_limits(decoder(), packkit@limit:limits()) -> decoder(). with_limits(Decoder, Limits) -> {decoder, erlang:element(2, Decoder), erlang:element(3, Decoder), erlang:element(4, Decoder), Limits}. -file("src/packkit/stream.gleam", 136). ?DOC( " Append a chunk of input bytes to the decoder, enforcing\n" " `max_input_bytes` incrementally. Returns a typed\n" " `CodecLimitExceeded` if the running buffered byte count would\n" " exceed the configured limit.\n" ). -spec push(decoder(), bitstring()) -> {ok, decoder()} | {error, packkit@error:codec_error()}. push(Decoder, Chunk) -> Chunk_size = erlang:byte_size(Chunk), New_total = erlang:element(4, Decoder) + Chunk_size, case New_total > packkit@limit:max_input_bytes(erlang:element(5, Decoder)) of true -> {error, {codec_limit_exceeded, <<"max_input_bytes"/utf8>>, New_total}}; false -> {ok, {decoder, erlang:element(2, Decoder), [Chunk | erlang:element(3, Decoder)], New_total, erlang:element(5, Decoder)}} end. -file("src/packkit/stream.gleam", 160). ?DOC(" Finalize the decoder and return the full decoded payload.\n"). -spec finish(decoder()) -> {ok, bitstring()} | {error, packkit@error:codec_error()}. finish(Decoder) -> Bytes = gleam_stdlib:bit_array_concat( lists:reverse(erlang:element(3, Decoder)) ), case erlang:element(2, Decoder) of deflate -> packkit@deflate:decode_with_limits( Bytes, erlang:element(5, Decoder) ); zlib -> packkit@zlib:decode_with_limits(Bytes, erlang:element(5, Decoder)); gzip -> _pipe = packkit@gzip:decode_with_limits( Bytes, erlang:element(5, Decoder) ), gleam@result:map( _pipe, fun(Decoded) -> erlang:element(3, Decoded) end ); lz4 -> packkit@lz4:decode_with_limits(Bytes, erlang:element(5, Decoder)); snappy -> packkit@snappy:decode_with_limits(Bytes, erlang:element(5, Decoder)); bzip2 -> packkit@bzip2:decode_with_limits(Bytes, erlang:element(5, Decoder)); lzw -> packkit@lzw:decode_with_limits(Bytes, erlang:element(5, Decoder)); xz -> packkit@xz:decode_with_limits(Bytes, erlang:element(5, Decoder)); zstd -> packkit@zstd:decode_with_limits(Bytes, erlang:element(5, Decoder)); brotli -> packkit@brotli:decode_with_limits(Bytes, erlang:element(5, Decoder)) end. -file("src/packkit/stream.gleam", 193). -spec feed_all(decoder(), list(bitstring())) -> {ok, decoder()} | {error, packkit@error:codec_error()}. feed_all(Decoder, Chunks) -> case Chunks of [] -> {ok, Decoder}; [Head | Rest] -> gleam@result:'try'( push(Decoder, Head), fun(Next) -> feed_all(Next, Rest) end ) end. -file("src/packkit/stream.gleam", 185). ?DOC( " Convenience helper that pushes every chunk through the decoder in\n" " order and returns the final decoded payload. Surfaces the same\n" " typed `CodecLimitExceeded` `push` would, so a long sequence of\n" " chunks cannot silently overrun the input budget.\n" ). -spec decode_chunks(decoder(), list(bitstring())) -> {ok, bitstring()} | {error, packkit@error:codec_error()}. decode_chunks(Decoder, Chunks) -> gleam@result:'try'(feed_all(Decoder, Chunks), fun(Fed) -> finish(Fed) end). -file("src/packkit/stream.gleam", 293). -spec new_encoder(encoder_kind(), packkit@limit:limits()) -> encoder(). new_encoder(Kind, Limits) -> {encoder, Kind, [], 0, Limits}. -file("src/packkit/stream.gleam", 239). ?DOC(" Start a new incremental DEFLATE encoder using the default limits.\n"). -spec new_deflate_encoder() -> encoder(). new_deflate_encoder() -> new_encoder(enc_deflate, packkit@limit:default()). -file("src/packkit/stream.gleam", 244). ?DOC(" Start a new incremental zlib encoder using the default limits.\n"). -spec new_zlib_encoder() -> encoder(). new_zlib_encoder() -> new_encoder(enc_zlib, packkit@limit:default()). -file("src/packkit/stream.gleam", 252). ?DOC( " Start a new incremental gzip encoder using the default limits.\n" " The output uses `gzip.default_header()` — callers that need\n" " per-stream metadata should keep using `gzip.encode` directly\n" " until the streaming API grows an explicit header constructor.\n" ). -spec new_gzip_encoder() -> encoder(). new_gzip_encoder() -> new_encoder(enc_gzip, packkit@limit:default()). -file("src/packkit/stream.gleam", 257). ?DOC(" Start a new incremental LZ4 frame encoder using the default limits.\n"). -spec new_lz4_encoder() -> encoder(). new_lz4_encoder() -> new_encoder(enc_lz4, packkit@limit:default()). -file("src/packkit/stream.gleam", 263). ?DOC( " Start a new incremental Snappy (framed) encoder using the default\n" " limits.\n" ). -spec new_snappy_encoder() -> encoder(). new_snappy_encoder() -> new_encoder(enc_snappy, packkit@limit:default()). -file("src/packkit/stream.gleam", 268). ?DOC(" Start a new incremental bzip2 encoder using the default limits.\n"). -spec new_bzip2_encoder() -> encoder(). new_bzip2_encoder() -> new_encoder(enc_bzip2, packkit@limit:default()). -file("src/packkit/stream.gleam", 274). ?DOC( " Start a new incremental Unix `.Z` (LZW) encoder using the default\n" " limits.\n" ). -spec new_lzw_encoder() -> encoder(). new_lzw_encoder() -> new_encoder(enc_lzw, packkit@limit:default()). -file("src/packkit/stream.gleam", 279). ?DOC(" Start a new incremental xz encoder using the default limits.\n"). -spec new_xz_encoder() -> encoder(). new_xz_encoder() -> new_encoder(enc_xz, packkit@limit:default()). -file("src/packkit/stream.gleam", 284). ?DOC(" Start a new incremental zstd encoder using the default limits.\n"). -spec new_zstd_encoder() -> encoder(). new_zstd_encoder() -> new_encoder(enc_zstd, packkit@limit:default()). -file("src/packkit/stream.gleam", 289). ?DOC(" Start a new incremental brotli encoder using the default limits.\n"). -spec new_brotli_encoder() -> encoder(). new_brotli_encoder() -> new_encoder(enc_brotli, packkit@limit:default()). -file("src/packkit/stream.gleam", 298). ?DOC(" Replace the limits used by an incremental encoder.\n"). -spec encoder_with_limits(encoder(), packkit@limit:limits()) -> encoder(). encoder_with_limits(Encoder, Limits) -> {encoder, erlang:element(2, Encoder), erlang:element(3, Encoder), erlang:element(4, Encoder), Limits}. -file("src/packkit/stream.gleam", 308). ?DOC( " Append a chunk of plaintext bytes to the encoder, enforcing\n" " `max_input_bytes` incrementally so an unbounded producer can't\n" " trigger an unbounded `encode` allocation at `finish_encoder` time.\n" ). -spec push_encoder(encoder(), bitstring()) -> {ok, encoder()} | {error, packkit@error:codec_error()}. push_encoder(Encoder, Chunk) -> Chunk_size = erlang:byte_size(Chunk), New_total = erlang:element(4, Encoder) + Chunk_size, case New_total > packkit@limit:max_input_bytes(erlang:element(5, Encoder)) of true -> {error, {codec_limit_exceeded, <<"max_input_bytes"/utf8>>, New_total}}; false -> {ok, {encoder, erlang:element(2, Encoder), [Chunk | erlang:element(3, Encoder)], New_total, erlang:element(5, Encoder)}} end. -file("src/packkit/stream.gleam", 336). ?DOC( " Finalize the encoder and return the compressed payload. Each\n" " codec is invoked through its default `encode/1` form; advanced\n" " per-codec options (gzip header metadata, deflate stored blocks,\n" " lz4 content size, bzip2 level) are not exposed through the\n" " streaming wrapper and remain accessible via the per-codec module.\n" ). -spec finish_encoder(encoder()) -> {ok, bitstring()} | {error, packkit@error:codec_error()}. finish_encoder(Encoder) -> Bytes = gleam_stdlib:bit_array_concat( lists:reverse(erlang:element(3, Encoder)) ), case erlang:element(2, Encoder) of enc_deflate -> packkit@deflate:encode(Bytes); enc_zlib -> packkit@zlib:encode(Bytes); enc_gzip -> packkit@gzip:encode(Bytes); enc_lz4 -> packkit@lz4:encode(Bytes); enc_snappy -> packkit@snappy:encode(Bytes); enc_bzip2 -> packkit@bzip2:encode(Bytes); enc_lzw -> packkit@lzw:encode(Bytes); enc_xz -> packkit@xz:encode(Bytes); enc_zstd -> packkit@zstd:encode(Bytes); enc_brotli -> packkit@brotli:encode(Bytes) end. -file("src/packkit/stream.gleam", 364). -spec feed_encoder_all(encoder(), list(bitstring())) -> {ok, encoder()} | {error, packkit@error:codec_error()}. feed_encoder_all(Encoder, Chunks) -> case Chunks of [] -> {ok, Encoder}; [Head | Rest] -> gleam@result:'try'( push_encoder(Encoder, Head), fun(Next) -> feed_encoder_all(Next, Rest) end ) end. -file("src/packkit/stream.gleam", 356). ?DOC( " Convenience helper that pushes every plaintext chunk through the\n" " encoder in order and returns the final compressed payload. Mirrors\n" " `decode_chunks` so callers can drive both directions with the same\n" " shape (list of input chunks → single output buffer).\n" ). -spec encode_chunks(encoder(), list(bitstring())) -> {ok, bitstring()} | {error, packkit@error:codec_error()}. encode_chunks(Encoder, Chunks) -> gleam@result:'try'( feed_encoder_all(Encoder, Chunks), fun(Fed) -> finish_encoder(Fed) end ).