summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--kern/cbuf.c77
-rw-r--r--kern/cbuf.h40
2 files changed, 88 insertions, 29 deletions
diff --git a/kern/cbuf.c b/kern/cbuf.c
index fcfcc1d5..fc59dc9e 100644
--- a/kern/cbuf.c
+++ b/kern/cbuf.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017 Richard Braun.
+ * Copyright (c) 2015-2018 Richard Braun.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -50,21 +50,28 @@ cbuf_index(const struct cbuf *cbuf, size_t abs_index)
static void
cbuf_update_start(struct cbuf *cbuf)
{
- /* Mind integer overflows */
if (cbuf_size(cbuf) > cbuf->capacity) {
cbuf->start = cbuf->end - cbuf->capacity;
}
}
+static void
+cbuf_update_end(struct cbuf *cbuf)
+{
+ if (cbuf_size(cbuf) > cbuf->capacity) {
+ cbuf->end = cbuf->start + cbuf->capacity;
+ }
+}
+
int
cbuf_push(struct cbuf *cbuf, const void *buf, size_t size, bool erase)
{
- size_t free_size;
-
if (!erase) {
- free_size = cbuf_capacity(cbuf) - cbuf_size(cbuf);
+ size_t avail_size;
+
+ avail_size = cbuf_avail_size(cbuf);
- if (size > free_size) {
+ if (size > avail_size) {
return EAGAIN;
}
}
@@ -90,12 +97,12 @@ cbuf_pop(struct cbuf *cbuf, void *buf, size_t *sizep)
int
cbuf_pushb(struct cbuf *cbuf, uint8_t byte, bool erase)
{
- size_t free_size;
-
if (!erase) {
- free_size = cbuf_capacity(cbuf) - cbuf_size(cbuf);
+ size_t avail_size;
+
+ avail_size = cbuf_avail_size(cbuf);
- if (free_size == 0) {
+ if (avail_size == 0) {
return EAGAIN;
}
}
@@ -116,7 +123,11 @@ cbuf_popb(struct cbuf *cbuf, void *bytep)
}
ptr = bytep;
- *ptr = cbuf->buf[cbuf_index(cbuf, cbuf->start)];
+
+ if (ptr) {
+ *ptr = cbuf->buf[cbuf_index(cbuf, cbuf->start)];
+ }
+
cbuf->start++;
return 0;
}
@@ -127,20 +138,21 @@ cbuf_write(struct cbuf *cbuf, size_t index, const void *buf, size_t size)
uint8_t *start, *end, *buf_end;
size_t new_end, skip;
- if (!cbuf_range_valid(cbuf, index, cbuf->end)) {
+ if (!cbuf_index_valid(cbuf, index)) {
return EINVAL;
}
new_end = index + size;
- if (!cbuf_range_valid(cbuf, cbuf->start, new_end)) {
+ if (!cbuf_index_valid(cbuf, new_end)) {
cbuf->end = new_end;
+ cbuf_update_start(cbuf);
- if (size > cbuf_capacity(cbuf)) {
- skip = size - cbuf_capacity(cbuf);
+ if (size > cbuf->capacity) {
+ skip = size - cbuf->capacity;
buf += skip;
index += skip;
- size = cbuf_capacity(cbuf);
+ size = cbuf->capacity;
}
}
@@ -148,7 +160,7 @@ cbuf_write(struct cbuf *cbuf, size_t index, const void *buf, size_t size)
end = start + size;
buf_end = cbuf->buf + cbuf->capacity;
- if ((end <= cbuf->buf) || (end > buf_end)) {
+ if ((end < cbuf->buf) || (end > buf_end)) {
skip = buf_end - start;
memcpy(start, buf, skip);
buf += skip;
@@ -157,7 +169,6 @@ cbuf_write(struct cbuf *cbuf, size_t index, const void *buf, size_t size)
}
memcpy(start, buf, size);
- cbuf_update_start(cbuf);
return 0;
}
@@ -167,8 +178,7 @@ cbuf_read(const struct cbuf *cbuf, size_t index, void *buf, size_t *sizep)
const uint8_t *start, *end, *buf_end;
size_t size;
- /* At least one byte must be available */
- if (!cbuf_range_valid(cbuf, index, index + 1)) {
+ if (!cbuf_index_valid(cbuf, index)) {
return EINVAL;
}
@@ -186,12 +196,33 @@ cbuf_read(const struct cbuf *cbuf, size_t index, void *buf, size_t *sizep)
size = *sizep;
} else {
size = buf_end - start;
- memcpy(buf, start, size);
- buf += size;
+
+ if (buf) {
+ memcpy(buf, start, size);
+ buf += size;
+ }
+
start = cbuf->buf;
size = *sizep - size;
}
- memcpy(buf, start, size);
+ if (buf) {
+ memcpy(buf, start, size);
+ }
+
return 0;
}
+
+void
+cbuf_set_start(struct cbuf *cbuf, size_t start)
+{
+ cbuf->start = start;
+ cbuf_update_end(cbuf);
+}
+
+void
+cbuf_set_end(struct cbuf *cbuf, size_t end)
+{
+ cbuf->end = end;
+ cbuf_update_start(cbuf);
+}
diff --git a/kern/cbuf.h b/kern/cbuf.h
index 38215c16..417a3b9f 100644
--- a/kern/cbuf.h
+++ b/kern/cbuf.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017 Richard Braun.
+ * Copyright (c) 2015-2018 Richard Braun.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,7 +18,7 @@
* http://git.sceen.net/rbraun/librbraun.git/
*
*
- * Circular byte buffer.
+ * FIFO circular byte buffer.
*/
#ifndef KERN_CBUF_H
@@ -85,6 +85,13 @@ cbuf_range_valid(const struct cbuf *cbuf, size_t start, size_t end)
&& ((cbuf->end - end) <= cbuf_size(cbuf)));
}
+static inline bool
+cbuf_index_valid(const struct cbuf *cbuf, size_t index)
+{
+ return ((index - cbuf->start) <= cbuf_size(cbuf))
+ && ((cbuf->end - index) <= cbuf_size(cbuf));
+}
+
/*
* Initialize a circular buffer.
*
@@ -105,10 +112,13 @@ int cbuf_push(struct cbuf *cbuf, const void *buf, size_t size, bool erase);
* Pop data from a circular buffer.
*
* On entry, the sizep argument points to the size of the output buffer.
- * On exit, it is updated to the number of bytes actually transferred.
+ * On return, it is updated to the number of bytes actually transferred.
+ *
+ * If the buffer is empty, EAGAIN is returned, and the size of the output
+ * buffer is unmodified.
*
- * If the buffer is empty, EAGAIN is returned, and the size of the
- * output buffer is undefined.
+ * The output buffer may be NULL, in which case this function acts as if
+ * it wasn't, but without writing output data.
*/
int cbuf_pop(struct cbuf *cbuf, void *buf, size_t *sizep);
@@ -124,6 +134,9 @@ int cbuf_pushb(struct cbuf *cbuf, uint8_t byte, bool erase);
* Pop a byte from a circular buffer.
*
* If the buffer is empty, EAGAIN is returned.
+ *
+ * The output byte pointer may be NULL, in which case this function acts
+ * as if it wasn't, but without writing output data.
*/
int cbuf_popb(struct cbuf *cbuf, void *bytep);
@@ -140,12 +153,27 @@ int cbuf_write(struct cbuf *cbuf, size_t index, const void *buf, size_t size);
* Read from a circular buffer at a specific location.
*
* On entry, the sizep argument points to the size of the output buffer.
- * On exit, it is updated to the number of bytes actually transferred.
+ * On return, it is updated to the number of bytes actually transferred.
*
* If the given index is outside buffer boundaries, EINVAL is returned.
*
* The circular buffer isn't changed by this operation.
+ *
+ * The output buffer may be NULL, in which case this function acts as if
+ * it wasn't, but without writing output data.
*/
int cbuf_read(const struct cbuf *cbuf, size_t index, void *buf, size_t *sizep);
+/*
+ * Set the value of the start/end index.
+ *
+ * These functions provide low level access to the circular buffer boundaries
+ * while making sure its size doesn't exceed its capacity.
+ *
+ * Users should try and find a higher level way to manipulate the circular
+ * buffer, and only resort to using these functions if there's no other choice.
+ */
+void cbuf_set_start(struct cbuf *cbuf, size_t start);
+void cbuf_set_end(struct cbuf *cbuf, size_t end);
+
#endif /* KERN_CBUF_H */