|
cwist
Modern C web framework with libttak runtime
|
TCP-like sequenced message fragmentation and reassembly. More...
#include <stdbool.h>#include <stddef.h>#include <stdint.h>

Go to the source code of this file.
Classes | |
| struct | cwist_seq_chunk |
| Parsed view of one sequenced chunk. More... | |
| struct | cwist_seq_message |
| Split a message into sequenced chunks. More... | |
Macros | |
| #define | CWIST_SEQ_HEADER_SIZE 8 |
| #define | CWIST_SEQ_DEFAULT_CHUNK_PAYLOAD 1024 |
| #define | CWIST_SEQ_MAX_CHUNKS 8192 |
| #define | CWIST_SEQ_MAX_REASSEMBLED_SIZE (64U * 1024U * 1024U) |
Typedefs | |
| typedef struct cwist_seq_chunk | cwist_seq_chunk_t |
| Parsed view of one sequenced chunk. | |
| typedef struct cwist_seq_message | cwist_seq_message_t |
| Split a message into sequenced chunks. | |
| typedef struct cwist_seq_assembler | cwist_seq_assembler_t |
| Opaque stateful assembler that reorders chunks and detects duplicates. | |
TCP-like sequenced message fragmentation and reassembly.
Splits large payloads into small numbered chunks, transmits them, and reassembles them even when they arrive out of order. It also exposes the exact missing sequence numbers so a transport can request retransmission rather than treating a partially assembled body as complete.
Chunk wire format (8 bytes, big-endian): seq uint16_t 1-based sequence number total uint16_t total number of chunks in this message payload_len uint16_t bytes in this chunk chunk_size uint16_t payload bytes in a full chunk
Definition in file seq.h.
| #define CWIST_SEQ_DEFAULT_CHUNK_PAYLOAD 1024 |
| #define CWIST_SEQ_HEADER_SIZE 8 |
| #define CWIST_SEQ_MAX_CHUNKS 8192 |
Hard resource ceilings for network-facing reassembly, including callers that use cwist_seq_assembler_create() without an explicit limit.
| typedef struct cwist_seq_assembler cwist_seq_assembler_t |
Parsed view of one sequenced chunk.
The payload pointer borrows the underlying buffer supplied to cwist_seq_chunk_parse().
Split a message into sequenced chunks.
Every chunk is an independent byte buffer prefixed with the 8-byte sequence header. The caller must free the returned buffers with cwist_seq_message_free().
| cwist_seq_assembler_t * cwist_seq_assembler_create | ( | void | ) |
Create an empty assembler.
Create an empty assembler.
Definition at line 196 of file seq.c.
References cwist_seq_assembler_create_limited().
Referenced by cwist_jwt_join_chunks(), and cwist_websocket_receive_sequenced().
| cwist_seq_assembler_t * cwist_seq_assembler_create_limited | ( | size_t | max_data_len | ) |
Create an assembler with a maximum possible reassembled size.
A non-zero limit rejects a sequence layout whose full chunks would exceed it before allocating its reassembly buffer. Use this for network-facing parsers. A zero limit has the same behaviour as cwist_seq_assembler_create.
Create an assembler with a maximum possible reassembled size.
Allocates an empty assembler state; buffers are allocated lazily on the first cwist_seq_assembler_feed() call. Destroy with cwist_seq_assembler_destroy().
| max_data_len | Maximum accepted reassembled size in bytes, or 0 to use the CWIST_SEQ_MAX_REASSEMBLED_SIZE default. |
Definition at line 211 of file seq.c.
References cap, and cwist_alloc().
Referenced by cwist_http2_serve_connection_ex(), and cwist_seq_assembler_create().
| void cwist_seq_assembler_destroy | ( | cwist_seq_assembler_t * | a | ) |
Destroy an assembler and its internal buffer.
Destroy an assembler and its internal buffer.
| a | Assembler to destroy; may be NULL. |
Definition at line 223 of file seq.c.
References cap, and cwist_free().
Referenced by cwist_jwt_join_chunks(), and cwist_websocket_receive_sequenced().
| bool cwist_seq_assembler_feed | ( | cwist_seq_assembler_t * | a, |
| const cwist_seq_chunk_t * | chunk | ||
| ) |
Feed one parsed chunk into the assembler.
Duplicate chunks are silently ignored. Chunks with missing or invalid sequence pairs are rejected.
| a | Assembler state. |
| chunk | Parsed chunk. |
Feed one parsed chunk into the assembler.
The first valid chunk establishes the message's total and chunk_size; subsequent chunks must match them or are rejected. Identical duplicates are accepted idempotently; a conflicting duplicate is flagged as contamination (retaining the original bytes) and never overwrites good data. The total message length is determined only when the final chunk (seq == total) arrives, so reordered delivery is tolerated. On allocation failure all state is cleared and false is returned.
| a | Assembler to feed. |
| chunk | Parsed chunk to accept; payload bytes are copied into the reassembly buffer. |
| true | The chunk was accepted (new data or an identical duplicate). |
| false | Invalid or inconsistent chunk, conflicting duplicate (contamination), size-limit exceeded, or allocation failure. |
Definition at line 268 of file seq.c.
References cap, cwist_alloc(), cwist_alloc_array(), cwist_free(), CWIST_SEQ_MAX_CHUNKS, and CWIST_SEQ_MAX_REASSEMBLED_SIZE.
Referenced by cwist_http2_serve_connection_ex(), cwist_jwt_join_chunks(), and cwist_websocket_receive_sequenced().
| bool cwist_seq_assembler_get_data | ( | cwist_seq_assembler_t * | a, |
| const uint8_t ** | out_data, | ||
| size_t * | out_len | ||
| ) |
Borrow the assembled message when complete.
The returned pointer is valid until the assembler is destroyed or reset.
| a | Assembler state. |
| out_data | Receives pointer to assembled bytes. |
| out_len | Receives assembled length. |
Borrow the assembled message when complete.
Succeeds only when cwist_seq_assembler_is_complete() is true. The returned pointer refers to the assembler's internal buffer, which remains valid until the assembler is reset, destroyed, or fed another chunk; ownership stays with the assembler.
| a | Assembler to query. |
| out_data | Receives a pointer to the reassembled bytes. |
| out_len | Receives the reassembled length in bytes. |
| true | The message is complete; outputs are populated. |
| false | The message is incomplete or an output pointer is NULL. |
Definition at line 390 of file seq.c.
References cap, and cwist_seq_assembler_is_complete().
Referenced by cwist_http2_serve_connection_ex(), cwist_jwt_join_chunks(), and cwist_websocket_receive_sequenced().
| bool cwist_seq_assembler_is_complete | ( | const cwist_seq_assembler_t * | a | ) |
Return true when every expected chunk has been received.
Return true when every expected chunk has been received.
Completion requires every chunk to be present, no conflicting duplicates (contamination), and the final chunk to have arrived so the total length is known.
| a | Assembler to query; may be NULL. |
| true | The message is fully reassembled and clean. |
| false | Not complete, or a is NULL. |
Definition at line 343 of file seq.c.
References cap.
Referenced by cwist_seq_assembler_get_data(), cwist_seq_assembler_recovery_targets(), and cwist_websocket_receive_sequenced().
| size_t cwist_seq_assembler_recovery_targets | ( | const cwist_seq_assembler_t * | a, |
| uint16_t * | out, | ||
| size_t | out_cap | ||
| ) |
Return the sequence numbers still required to complete a message.
This is the ARQ recovery side of the sequenced protocol: callers can use the returned 1-based values as retry targets. The return value is the total number of missing chunks, even when out is NULL or too small. The result is zero for a complete assembler or one that has not yet seen a valid chunk.
| a | Assembler state. |
| out | Optional output array for missing sequence numbers. |
| out_cap | Number of entries available in out. |
Return the sequence numbers still required to complete a message.
Returns the count of missing chunks and, when out has capacity, writes their 1-based sequence numbers in ascending order. Returns 0 if the assembler has no state or the message is already complete; in those cases nothing is written to out.
| a | Assembler to query; may be NULL. |
| out | Optional array to receive missing sequence numbers. |
| out_cap | Number of elements out can hold; ignored when out is NULL. |
out_cap). Definition at line 362 of file seq.c.
References cap, and cwist_seq_assembler_is_complete().
| void cwist_seq_assembler_reset | ( | cwist_seq_assembler_t * | a | ) |
Reset an assembler so it can receive a new message.
Reset an assembler so it can receive a new message.
Frees the reassembly buffers but retains the max_data_len configured at creation so the assembler can be reused for a new message. Does nothing if a is NULL.
| a | Assembler to reset; may be NULL. |
Definition at line 240 of file seq.c.
References cap, and cwist_free().
| void cwist_seq_chunk_build_header | ( | uint8_t | out[CWIST_SEQ_HEADER_SIZE], |
| uint16_t | seq, | ||
| uint16_t | total, | ||
| uint16_t | payload_len, | ||
| uint16_t | chunk_size | ||
| ) |
Write an 8-byte sequence header.
| out | Output buffer of at least CWIST_SEQ_HEADER_SIZE bytes. |
| seq | Sequence number. |
| total | Total chunks. |
| payload_len | Payload length in this chunk. |
| chunk_size | Full chunk payload size. |
Write an 8-byte sequence header.
Encodes seq, total, payload_len, and chunk_size as big-endian 16-bit fields. Performs no validation; callers must supply a consistent sequence pair (see cwist_seq_chunk_parse()).
| out | Buffer of CWIST_SEQ_HEADER_SIZE bytes to write. |
| seq | Chunk sequence number (1-based). |
| total | Total number of chunks in the message. |
| payload_len | Payload bytes in this chunk. |
| chunk_size | Full payload size of every non-final chunk. |
Definition at line 97 of file seq.c.
Referenced by cwist_seq_split().
| bool cwist_seq_chunk_parse | ( | const uint8_t * | data, |
| size_t | len, | ||
| cwist_seq_chunk_t * | out | ||
| ) |
Parse a sequence chunk from raw bytes.
| data | Raw bytes. |
| len | Length of data. |
| out | Receives parsed chunk fields. |
data contains a valid chunk header and payload.Parse a sequence chunk from raw bytes.
Reads the fixed-size chunk header from data and fills out. The payload pointer in out references memory inside data and is not copied. The chunk is rejected if the sequence pair (seq/total) is missing, out of range, or inconsistent, or if the payload length fields disagree with the buffer length.
| data | Buffer containing at least CWIST_SEQ_HEADER_SIZE bytes. |
| len | Length of data in bytes. |
| out | Receives the parsed chunk fields on success. |
| true | The chunk is valid and out is populated. |
| false | Invalid arguments or a malformed chunk; out is untouched. |
Definition at line 63 of file seq.c.
References cap, cwist_seq_chunk::chunk_size, CWIST_SEQ_HEADER_SIZE, CWIST_SEQ_MAX_CHUNKS, cwist_seq_chunk::payload, cwist_seq_chunk::payload_len, cwist_seq_chunk::seq, and cwist_seq_chunk::total.
Referenced by cwist_http2_serve_connection_ex(), cwist_jwt_join_chunks(), cwist_seq_auth_wrap(), and cwist_websocket_receive_sequenced().
| void cwist_seq_message_free | ( | cwist_seq_message_t * | msg | ) |
Free all buffers allocated by cwist_seq_split().
Free all buffers allocated by cwist_seq_split().
Releases each chunk buffer plus the chunk and length arrays, then zeroes the struct so it is safe to reuse. Does nothing if msg is NULL.
| msg | Message to free; may be NULL. |
Definition at line 178 of file seq.c.
References cap, and cwist_free().
Referenced by cwist_jwt_split_chunks(), and cwist_websocket_send_sequenced().
| bool cwist_seq_split | ( | const uint8_t * | data, |
| size_t | len, | ||
| uint16_t | chunk_payload_size, | ||
| cwist_seq_message_t * | out | ||
| ) |
Split data into sequenced chunks.
| data | Message bytes. |
| len | Message length. |
| chunk_payload_size | Maximum payload bytes per chunk (must be > 0). |
| out | Receives the split message. Caller must free with cwist_seq_message_free(). |
Split data into sequenced chunks.
Allocates each chunk with cwist_alloc() and writes it into out; the result is released with cwist_seq_message_free(). Every chunk except the last carries exactly chunk_payload_size payload bytes. out is zeroed before use and again on failure.
| data | Message bytes to split. |
| len | Length of data in bytes. |
| chunk_payload_size | Payload bytes per chunk; must be non-zero. |
| out | Receives the chunk array on success. |
| true | out holds out->count chunks. |
| false | Invalid arguments, or the message would exceed the chunk or reassembled-size limits, or allocation failed; out is zeroed. |
Definition at line 125 of file seq.c.
References cap, cwist_seq_message::chunk_lens, cwist_seq_message::chunks, cwist_seq_message::count, cwist_alloc(), cwist_alloc_array(), cwist_free(), cwist_seq_chunk_build_header(), CWIST_SEQ_HEADER_SIZE, CWIST_SEQ_MAX_CHUNKS, and CWIST_SEQ_MAX_REASSEMBLED_SIZE.
Referenced by cwist_jwt_split_chunks(), and cwist_websocket_send_sequenced().