From cc2f34dc189074e8a93c03ebc5c0790661353b86 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 23 Apr 2019 22:50:28 +0200 Subject: mbuf: add support for variable-size message headers --- src/mbuf.c | 195 +++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 145 insertions(+), 50 deletions(-) (limited to 'src/mbuf.c') diff --git a/src/mbuf.c b/src/mbuf.c index c7698bb..55624fb 100644 --- a/src/mbuf.c +++ b/src/mbuf.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 Richard Braun. + * Copyright (c) 2018-2019 Richard Braun. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -37,38 +38,100 @@ * * The size denotes the size of the data, without the header. */ -struct mbuf_hdr { - uint32_t size; +union mbuf_hdr { + uint8_t size1; + uint16_t size2; + uint32_t size4; +#ifdef __LP64__ + uint64_t size8; +#endif /* __LP64__ */ }; -static int -mbuf_hdr_init(struct mbuf_hdr *hdr, size_t size) +static size_t +mbuf_compute_hdr_size(unsigned int order) { - hdr->size = size; + return order / CHAR_BIT; +} - if (hdr->size != size) { - return EMSGSIZE; +static void * +mbuf_hdr_get_msg_size_addr(union mbuf_hdr *hdr, unsigned int order) +{ + switch (order) { + case 8: + return &hdr->size1; + case 16: + return &hdr->size2; + default: +#ifdef __LP64__ + return &hdr->size8; + case 32: +#endif /* __LP64__ */ + return &hdr->size4; } - - return 0; } static size_t -mbuf_hdr_total_size(const struct mbuf_hdr *hdr) +mbuf_hdr_get_msg_size(const union mbuf_hdr *hdr, unsigned int order) +{ + switch (order) { + case 8: + return hdr->size1; + case 16: + return hdr->size2; + default: +#ifdef __LP64__ + return hdr->size8; + case 32: +#endif /* __LP64__ */ + return hdr->size4; + } +} + +static void +mbuf_hdr_init(union mbuf_hdr *hdr, unsigned int order, size_t size) { - return sizeof(*hdr) + hdr->size; + switch (order) { + case 8: + assert(size <= UINT8_MAX); + hdr->size1 = size; + break; + case 16: + assert(size <= UINT16_MAX); + hdr->size2 = size; + break; + default: +#ifdef __LP64__ + hdr->size8 = size; + break; + case 32: + assert(size <= UINT32_MAX); +#endif /* __LP64__ */ + hdr->size4 = size; + } } static size_t -mbuf_hdr_msg_size(const struct mbuf_hdr *hdr) +mbuf_hdr_get_total_msg_size(const union mbuf_hdr *hdr, unsigned int order) +{ + return mbuf_compute_hdr_size(order) + mbuf_hdr_get_msg_size(hdr, order); +} + +static unsigned int +mbuf_compute_order(size_t max_msg_size) { - return hdr->size; + unsigned int order; + + assert(max_msg_size != 0); + order = (sizeof(max_msg_size) * CHAR_BIT) - __builtin_clzl(max_msg_size); + return P2ROUND(order, CHAR_BIT); } void -mbuf_init(struct mbuf *mbuf, void *buf, size_t capacity) +mbuf_init(struct mbuf *mbuf, void *buf, size_t capacity, size_t max_msg_size) { cbuf_init(&mbuf->cbuf, buf, capacity); + mbuf->max_msg_size = max_msg_size; + mbuf->order = mbuf_compute_order(max_msg_size); } void @@ -80,39 +143,47 @@ mbuf_clear(struct mbuf *mbuf) static void mbuf_clear_old_msgs(struct mbuf *mbuf, size_t total_size) { - struct mbuf_hdr hdr; - size_t size; - int error __unused; + union mbuf_hdr hdr; + void *msg_size_addr; + size_t hdr_size; + + hdr_size = mbuf_compute_hdr_size(mbuf->order); + msg_size_addr = mbuf_hdr_get_msg_size_addr(&hdr, mbuf->order); do { - size = sizeof(hdr); - error = cbuf_pop(&mbuf->cbuf, &hdr, &size); + size_t msg_size, size; + int error __unused; + + size = hdr_size; + error = cbuf_pop(&mbuf->cbuf, msg_size_addr, &size); assert(!error); if (size == 0) { break; } - size = mbuf_hdr_msg_size(&hdr); + msg_size = mbuf_hdr_get_msg_size(&hdr, mbuf->order); + size = msg_size; error = cbuf_pop(&mbuf->cbuf, NULL, &size); - assert(!error && (size == mbuf_hdr_msg_size(&hdr))); + assert(!error && (size == msg_size)); } while (cbuf_avail_size(&mbuf->cbuf) < total_size); } int mbuf_push(struct mbuf *mbuf, const void *buf, size_t size, bool erase) { - struct mbuf_hdr hdr; - size_t total_size; - int error; - - error = mbuf_hdr_init(&hdr, size); + union mbuf_hdr hdr; + void *msg_size_addr; + size_t hdr_size, total_size; + int error __unused; - if (error) { - return error; + if (size > mbuf->max_msg_size) { + return EINVAL; } - total_size = mbuf_hdr_total_size(&hdr); + mbuf_hdr_init(&hdr, mbuf->order, size); + + total_size = mbuf_hdr_get_total_msg_size(&hdr, mbuf->order); if (total_size > cbuf_avail_size(&mbuf->cbuf)) { if (!erase || (total_size > cbuf_capacity(&mbuf->cbuf))) { @@ -122,7 +193,10 @@ mbuf_push(struct mbuf *mbuf, const void *buf, size_t size, bool erase) mbuf_clear_old_msgs(mbuf, total_size); } - error = cbuf_push(&mbuf->cbuf, &hdr, sizeof(hdr), erase); + hdr_size = mbuf_compute_hdr_size(mbuf->order); + msg_size_addr = mbuf_hdr_get_msg_size_addr(&hdr, mbuf->order); + + error = cbuf_push(&mbuf->cbuf, msg_size_addr, hdr_size, erase); assert(!error); error = cbuf_push(&mbuf->cbuf, buf, size, erase); assert(!error); @@ -133,39 +207,60 @@ mbuf_push(struct mbuf *mbuf, const void *buf, size_t size, bool erase) int mbuf_pop(struct mbuf *mbuf, void *buf, size_t *sizep) { - struct mbuf_hdr hdr; - size_t start, size; + size_t start; int error; start = cbuf_start(&mbuf->cbuf); + error = mbuf_read(mbuf, &start, buf, sizep); - size = sizeof(hdr); - error = cbuf_read(&mbuf->cbuf, start, &hdr, &size); - assert(!error); + if (!error) { + cbuf_set_start(&mbuf->cbuf, start); + } + + return error; +} + +int +mbuf_read(const struct mbuf *mbuf, size_t *indexp, void *buf, size_t *sizep) +{ + union mbuf_hdr hdr; + void *msg_size_addr; + size_t hdr_size, msg_size, size; + int error; + + hdr_size = mbuf_compute_hdr_size(mbuf->order); + msg_size_addr = mbuf_hdr_get_msg_size_addr(&hdr, mbuf->order); + + size = hdr_size; + error = cbuf_read(&mbuf->cbuf, *indexp, msg_size_addr, &size); - if (size == 0) { + if (error) { + return error; + } else if (size == 0) { return EAGAIN; } - assert(size == sizeof(hdr)); - size = mbuf_hdr_msg_size(&hdr); + assert(size == hdr_size); + + msg_size = mbuf_hdr_get_msg_size(&hdr, mbuf->order); - if (size > *sizep) { + if (msg_size > *sizep) { error = EMSGSIZE; goto out; } - cbuf_set_start(&mbuf->cbuf, start + sizeof(hdr)); - error = cbuf_pop(&mbuf->cbuf, buf, &size); - assert(!error && (size == mbuf_hdr_msg_size(&hdr))); + size = msg_size; + error = cbuf_read(&mbuf->cbuf, *indexp + hdr_size, buf, &size); + + if (error) { + goto out; + } + + assert(size == msg_size); + + *indexp += hdr_size + size; out: - *sizep = size; + *sizep = msg_size; return error; } - -size_t -mbuf_avail_size(const struct mbuf *mbuf) -{ - return cbuf_avail_size(&mbuf->cbuf); -} -- cgit v1.2.3