return copy_size;
}
+void *protolayer_buffer_list_add(struct protolayer_buffer_list *list, size_t n)
+{
+ struct protolayer_buffer_list_entry *e =
+ malloc(sizeof(struct protolayer_buffer_list_entry) + n);
+ if (!e)
+ return NULL;
+
+ if (!list->head) {
+ if (kr_fails_assert(!list->tail)) {
+ free(e);
+ return NULL;
+ }
+
+ list->head = list->tail = e;
+ return e->data;
+ }
+
+ if (kr_fails_assert(list->tail)) {
+ free(e);
+ return NULL;
+ }
+
+ list->tail->next = e;
+ list->tail = e;
+ return e->data;
+}
+
+void protolayer_buffer_list_deinit(struct protolayer_buffer_list *list)
+{
+ struct protolayer_buffer_list_entry *e = list->head;
+ struct protolayer_buffer_list_entry *next;
+ while (e) {
+ next = e->next;
+ free(e);
+ e = next;
+ }
+ list->head = NULL;
+ list->tail = NULL;
+}
+
size_t protolayer_payload_size(const struct protolayer_payload *payload)
{
switch (payload->type) {
ctx->finished_cb(ret, session, &ctx->comm,
ctx->finished_cb_baton);
- free(ctx->async_buffer);
+ protolayer_buffer_list_deinit(&ctx->async_buffer_list);
free(ctx);
return ret;
if (kr_fails_assert(buf_len))
return;
- void *buf = malloc(buf_len);
+ void *buf = protolayer_buffer_list_add(&ctx->async_buffer_list, buf_len);
kr_require(buf);
protolayer_payload_copy(buf, &ctx->payload, buf_len);
- ctx->async_buffer = buf;
ctx->payload = protolayer_payload_buffer(buf, buf_len, false);
}
};
};
+/** An entry in a linked list of buffers. The buffer data itself is allocated in
+ * the same object as the header. */
+struct protolayer_buffer_list_entry {
+ struct protolayer_buffer_list_entry *next;
+ alignas(CPU_STRUCT_ALIGN) char data[];
+};
+
+/** A linked list of buffers. */
+struct protolayer_buffer_list {
+ struct protolayer_buffer_list_entry *head;
+ struct protolayer_buffer_list_entry *tail;
+};
+
+/** Uses `malloc()` to allocate a new buffer of size `n` and adds it to the
+ * specified `list`. Returns a pointer to the buffer data (excl. the header) or
+ * `NULL` if the allocation fails. */
+void *protolayer_buffer_list_add(struct protolayer_buffer_list *list, size_t n);
+
+/** Frees the specified buffer list's entries (but not the list's control
+ * structure itself). */
+void protolayer_buffer_list_deinit(struct protolayer_buffer_list *list);
+
/** Context for protocol layer iterations, containing payload data,
* layer-specific data, and internal information for the protocol layer
* manager. */
struct protolayer_iter_ctx {
-/* read-write: */
+/* read-write for layers: */
/** The payload */
struct protolayer_payload payload;
/** Communication information. Typically written into by one of the
* first layers facilitating transport protocol processing. */
struct comm_info comm;
-/* callback for when the layer iteration has ended - read-only: */
+/* callback for when the layer iteration has ended - read-only for layers: */
protolayer_finished_cb finished_cb;
void *finished_cb_baton;
-/* internal information for the manager - private: */
+/* internal information for the manager - should only be used by the protolayer
+ * system, never by layers: */
enum protolayer_direction direction;
+ /** If `true`, the processing of layers has been paused and is waiting
+ * to be resumed or canceled. */
bool async_mode;
+ /** The index of the layer that is currently being (or has just been)
+ * processed. */
unsigned int layer_ix;
struct protolayer_manager *manager;
+ /** Status passed to the finish callback. */
int status;
enum protolayer_iter_action action;
- void *async_buffer;
+ /** Points to a buffers where data has been copied from short-lived
+ * payloads. Automatically freed together with the context. */
+ struct protolayer_buffer_list async_buffer_list;
/** Contains a sequence of variably-sized CPU-aligned layer-specific
* structs. See `struct protolayer_manager::data`. */