11struct cwist_seq_assembler {
66 out->
seq = seq_read_u16(data + 0);
67 out->
total = seq_read_u16(data + 2);
99 seq_write_u16(out + 0, seq);
100 seq_write_u16(out + 2, total);
101 seq_write_u16(out + 4, payload_len);
102 seq_write_u16(out + 6, chunk_size);
128 memset(out, 0,
sizeof(*out));
141 memset(out, 0,
sizeof(*out));
147 size_t plen = len - offset;
156 memset(out, 0,
sizeof(*out));
215 a->max_data_len = max_data_len;
242 size_t max_data_len =
a->max_data_len;
247 a->max_data_len = max_data_len;
269 if (!
a || !
chunk)
return false;
275 if (
chunk->payload_len == 0 ||
chunk->chunk_size == 0)
return false;
276 if (
chunk->payload_len >
chunk->chunk_size)
return false;
278 if (!
chunk->payload)
return false;
280 if (!
a->have_state) {
282 a->chunk_size =
chunk->chunk_size;
297 if (!
a->data || !
a->received || !
a->payload_lens) {
304 a->have_state =
true;
306 if (
chunk->total !=
a->total ||
chunk->chunk_size !=
a->chunk_size)
return false;
310 size_t offset = index *
a->chunk_size;
311 if (
a->received[index]) {
320 if (offset >
a->data_cap ||
chunk->payload_len >
a->data_cap - offset)
return false;
323 a->received[index] =
true;
324 a->payload_lens[index] =
chunk->payload_len;
326 a->total_len = offset +
chunk->payload_len;
344 return a &&
a->have_state && !
a->contaminated &&
a->received_count ==
a->total &&
345 a->received[
a->total - 1] &&
a->total_len > 0;
368 if (!
a->received[
i]) {
394 *out_len =
a->total_len;
Memory allocation wrappers backed by libttak.
void cwist_free(void *ptr)
Free memory obtained via cwist_alloc/cwist_strdup/etc.
void * cwist_alloc_array(size_t count, size_t elem_size)
Allocate zeroed memory for count elements.
void * cwist_alloc(size_t size)
Allocate zeroed memory tracked by libttak.
cwist_seq_assembler_t * cwist_seq_assembler_create_limited(size_t max_data_len)
Create an assembler with a reassembled-size limit.
bool cwist_seq_assembler_is_complete(const cwist_seq_assembler_t *a)
Check whether all chunks of the message have been received intact.
bool cwist_seq_chunk_parse(const uint8_t *data, size_t len, cwist_seq_chunk_t *out)
Parse and validate a sequenced chunk header.
void cwist_seq_assembler_reset(cwist_seq_assembler_t *a)
Discard all received state, keeping the size limit.
cwist_seq_assembler_t * cwist_seq_assembler_create(void)
Create an assembler with no reassembled-size limit.
void cwist_seq_assembler_destroy(cwist_seq_assembler_t *a)
Destroy an assembler and free all its buffers.
size_t cwist_seq_assembler_recovery_targets(const cwist_seq_assembler_t *a, uint16_t *out, size_t out_cap)
List the sequence numbers of missing chunks.
void cwist_seq_message_free(cwist_seq_message_t *msg)
Free all memory owned by a cwist_seq_message_t.
bool cwist_seq_assembler_get_data(cwist_seq_assembler_t *a, const uint8_t **out_data, size_t *out_len)
Access the fully reassembled message.
bool cwist_seq_assembler_feed(cwist_seq_assembler_t *a, const cwist_seq_chunk_t *chunk)
Feed one sequenced chunk into the assembler.
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)
Serialize a chunk header into an 8-byte buffer.
bool cwist_seq_split(const uint8_t *data, size_t len, uint16_t chunk_payload_size, cwist_seq_message_t *out)
Split a message buffer into sequenced chunks.
TCP-like sequenced message fragmentation and reassembly.
#define CWIST_SEQ_MAX_CHUNKS
#define CWIST_SEQ_MAX_REASSEMBLED_SIZE
struct cwist_seq_assembler cwist_seq_assembler_t
Opaque stateful assembler that reorders chunks and detects duplicates.
#define CWIST_SEQ_HEADER_SIZE
Parsed view of one sequenced chunk.
uint16_t payload_len
Payload length in this chunk.
uint16_t total
Total chunks in the message.
uint16_t chunk_size
Payload length of a full chunk.
const uint8_t * payload
Pointer to payload bytes.
uint16_t seq
1-based sequence number.
Split a message into sequenced chunks.
size_t count
Number of chunks.
size_t * chunk_lens
Length of each chunk buffer.
uint8_t ** chunks
Array of chunk buffers.