ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
inflate.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file inflate.h
6 * @brief Bounded RFC 1951 DEFLATE decompressor (INFLATE) - no heap.
7 *
8 * A small, host-testable INFLATE used by WebSocket permessage-deflate
9 * (RFC 7692). It decompresses a *raw* DEFLATE stream (no zlib/gzip wrapper) into
10 * a caller buffer. LZ77 back-references read from the output buffer itself, so
11 * there is no separate sliding window - the output buffer *is* the window. That
12 * is correct for permessage-deflate's `no_context_takeover` mode (each message
13 * is independent) and bounds memory: a decompressed message must fit @p dst_cap.
14 *
15 * The only working memory is a Huffman-table scratch the caller supplies
16 * (INFLATE_SCRATCH_SIZE bytes); the WebSocket layer borrows it from the
17 * per-dispatch scratch arena, so INFLATE costs no dedicated buffer.
18 *
19 * Decoding terminates at a final block (BFINAL) or at a clean end-of-input on a
20 * block boundary - so it accepts a permessage-deflate payload, which carries no
21 * final block (the caller appends the 0x00 0x00 0xff 0xff marker per
22 * RFC 7692 ยง7.2.2 before calling).
23 *
24 * @author Douglas Quigg (dstroy0)
25 * @date 2026
26 */
27
28#ifndef PROTOCORE_INFLATE_H
29#define PROTOCORE_INFLATE_H
30
31#include "protocore_config.h" // the entry point: protocore_types.h for the widths
32
33#if PROTOCORE_ENABLE_WS_DEFLATE
34
36
37// This module holds nothing between calls, so it carves no borrow and states none. An entry
38// takes one all the same, and never reads it, so every namespace in the tree is invoked the
39// same way.
40
41/**
42 * @brief Working-memory bytes inflate_raw() needs for its Huffman tables.
43 *
44 * Pass a buffer at least this large as @p scratch. (Sized for the worst-case
45 * dynamic-block tables; an internal static_assert keeps it honest.)
46 */
47#define INFLATE_SCRATCH_SIZE 1536
48
49/** @brief inflate_raw() return codes. */
50typedef enum PROTO_ENUM_PACKED
51{
52 INFLATE_OK = 0, ///< success; *out_len holds the decompressed length
53 INFLATE_ERR_MALFORMED = -1, ///< invalid / truncated DEFLATE stream
54 INFLATE_ERR_OVERFLOW = -2, ///< output would exceed dst_cap
55 INFLATE_ERR_SCRATCH = -3 ///< scratch_len < INFLATE_SCRATCH_SIZE
56} InflateResult;
57
58/** @brief What raw takes: src, src_len, dst, dst_cap, out_len, ... */
59typedef struct
60{
61 const uint8_t *src;
62 size_t src_len;
63 uint8_t *dst;
64 size_t dst_cap;
65 size_t *out_len;
66 void *scratch;
67 size_t scratch_len;
68} InflateRawArgs;
69
70/**
71 * @brief Bounded RFC 1951 DEFLATE decompressor (INFLATE) - no heap.
72 *
73 * A caller sets the members a call takes, invokes it through ::Inflate with the bytes it runs
74 * out of, and reads the outcome off the same handle.
75 *
76 * Inflate.raw_args.src = ...;
77 * Inflate.raw_args.src_len = ...;
78 * Inflate.raw_args.dst = ...;
79 * Inflate.raw_args.dst_cap = ...;
80 * Inflate.raw_args.out_len = ...;
81 * Inflate.raw_args.scratch = ...;
82 * Inflate.raw_args.scratch_len = ...;
83 * Inflate.raw(work);
84 * // Inflate.value is what the call reports
85 *
86 * @var InflateNs::raw_args what raw takes: src, src_len, dst, dst_cap, out_len,
87 * @var InflateNs::ok a call's true/false outcome
88 * @var InflateNs::value the value a call reports
89 * @var InflateNs::raw decompress a raw DEFLATE (RFC 1951) stream. dst is also the window, ...
90 *
91 * @c work is bytes the CALLER holds. This module reads none of them: it carries nothing
92 * between calls, so there is no state to keep and nothing to wipe. The parameter is there so
93 * a caller drives every namespace the same way.
94 */
95typedef struct
96{
97 InflateRawArgs raw_args;
98 proto_bool ok;
99 InflateResult value;
100} InflateVars;
101
102/** @brief The operands and the outcome. */
103extern InflateVars InflateV;
104
105/** @brief The entries. */
106typedef struct
107{
108 void (*const raw)(uint8_t *work);
109} InflateNs;
110
111// What the table binds, defined once in the .c and taking one parameter each: everything
112// else an entry needs is an operand in InflateV or a region of the borrow at a fixed offset.
113void protocore_inflate_raw(uint8_t *work);
114
115// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
116// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
117// `Inflate.raw(work)` resolves to a named function and becomes a DIRECT call. An extern table
118// leaves the call indirect and the symbol live at every level, -O2 -flto included.
119static const InflateNs Inflate __attribute__((unused)) = {
120 .raw = protocore_inflate_raw,
121};
122
124
125#endif // PROTOCORE_ENABLE_WS_DEFLATE
126
127#endif // PROTOCORE_INFLATE_H
PROTO_ENUM_PACKED
Application protocol spoken on a listener port or connection slot.
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTOCORE_END_DECLS
Definition types.h:97