summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRichard Braun <rbraun@sceen.net>2019-04-23 22:50:28 +0200
committerRichard Braun <rbraun@sceen.net>2019-04-24 01:17:49 +0200
commitcc2f34dc189074e8a93c03ebc5c0790661353b86 (patch)
treeb50c2e86e4b1d29bbc28ea52a9cff0de72a2df0b /src
parent9d0f14ed6aa75cf39b57c4e7efd5a1713e8ff27a (diff)
mbuf: add support for variable-size message headersHEADmaster
Diffstat (limited to 'src')
-rw-r--r--src/mbuf.c195
-rw-r--r--src/mbuf.h48
2 files changed, 188 insertions, 55 deletions
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 <assert.h>
#include <errno.h>
+#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
@@ -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);
-}
diff --git a/src/mbuf.h b/src/mbuf.h
index 458b52a..a5dbc39 100644
--- a/src/mbuf.h
+++ b/src/mbuf.h
@@ -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"),
@@ -39,18 +39,36 @@
*
* Message buffers are built on top of circular byte buffers. They provide
* discrete message transfer from a producer to a consumer.
+ *
+ * The order is computed from the maximum message size, and is used to
+ * determine a header size that can represent up to 2^order - 1 bytes.
*/
struct mbuf {
struct cbuf cbuf;
+ size_t max_msg_size;
+ unsigned int order;
};
+static inline size_t
+mbuf_start(const struct mbuf *mbuf)
+{
+ return cbuf_start(&mbuf->cbuf);
+}
+
+static inline size_t
+mbuf_end(const struct mbuf *mbuf)
+{
+ return cbuf_end(&mbuf->cbuf);
+}
+
/*
* Initialize a message buffer.
*
* The descriptor is set to use the given buffer for storage. Capacity
* must be a power-of-two.
*/
-void mbuf_init(struct mbuf *mbuf, void *buf, size_t capacity);
+void mbuf_init(struct mbuf *mbuf, void *buf, size_t capacity,
+ size_t max_msg_size);
/*
* Clear a message buffer.
@@ -63,7 +81,8 @@ void mbuf_clear(struct mbuf *mbuf);
* If the message doesn't fit in the message buffer, either because it is
* larger than the capacity, or because the function isn't allowed to erase
* old messages and the message buffer doesn't have enough available memory
- * for the new message, EMSGSIZE is returned.
+ * for the new message, EMSGSIZE is returned. If the message is larger than
+ * the maximum message size, EINVAL is returned.
*/
int mbuf_push(struct mbuf *mbuf, const void *buf, size_t size, bool erase);
@@ -85,8 +104,27 @@ int mbuf_push(struct mbuf *mbuf, const void *buf, size_t size, bool erase);
int mbuf_pop(struct mbuf *mbuf, void *buf, size_t *sizep);
/*
- * Get the number of availabe bytes in a message buffer.
+ * Read a message from a message buffer.
+ *
+ * On entry, the indexp argument points to the index of the message to
+ * read in the message buffer, and the sizep argument points to the size
+ * of the output buffer. On return, if successful, indexp is updated
+ * to the index of the next message, and sizep to the size of the
+ * message read.
+ *
+ * If the message doesn't fit in the output buffer, it is not read,
+ * EMSGSIZE is returned, and the sizep argument is updated nonetheless
+ * to let the user know the message size, to potentially retry with a
+ * larger buffer.
+ *
+ * If the given index refers to the end of the buffer, then EAGAIN is
+ * returned. If it's outside buffer boundaries, EINVAL is returned.
+ * Otherwise, if it doesn't point to the beginning of a message, the
+ * behavior is undefined.
+ *
+ * The message buffer isn't changed by this operation.
*/
-size_t mbuf_avail_size(const struct mbuf *mbuf);
+int mbuf_read(const struct mbuf *mbuf, size_t *indexp,
+ void *buf, size_t *sizep);
#endif /* MBUF_H */