Tidy up ringbuf and make tests
This commit is contained in:
@@ -0,0 +1,98 @@
|
|||||||
|
#ifndef UC_RINGBUG_H_
|
||||||
|
#define UC_RINGBUG_H_
|
||||||
|
#include <stdint.h>
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct UCRingBuf UCRingBuf;
|
||||||
|
struct UCRingBuf {
|
||||||
|
char *base;
|
||||||
|
uint32_t sz;
|
||||||
|
uint32_t read_idx;
|
||||||
|
uint32_t write_idx;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ringbuffer of at least @sz length.
|
||||||
|
* The length will be rounded up to the neares page size
|
||||||
|
*
|
||||||
|
* The ringbuffer is implementd by mapping the same physical memory
|
||||||
|
* twice into virtual memory adjacent to eachother, avoiding
|
||||||
|
* a splitting up read/writes when the ringbuffer wraps around.
|
||||||
|
*
|
||||||
|
* This means accessing rb.base[rb.len] up to rb.base[rb.len * 2 - 1]
|
||||||
|
* is accessing the same memory as rb.base[0] up to rb.base[rb.len -1]
|
||||||
|
*
|
||||||
|
* @param rb UCRingBuf to initialize
|
||||||
|
* @param sz minimum length of the ring buffer
|
||||||
|
* @returns 0 on success
|
||||||
|
*/
|
||||||
|
int uc_ringbuf_init(UCRingBuf *rb, uint32_t sz);
|
||||||
|
|
||||||
|
/** Free resources used by the ring buffer.
|
||||||
|
*
|
||||||
|
* @return 0 on success
|
||||||
|
*/
|
||||||
|
int uc_ringbuf_destroy(UCRingBuf *rb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data to the ringbuffer. The number of bytes to write must fit
|
||||||
|
* in the free space of the ringbuffer
|
||||||
|
*
|
||||||
|
* @param rb UCRingBuf to write to.
|
||||||
|
* @param data data to write
|
||||||
|
* @param sz number of bytes to write from data
|
||||||
|
* @return 0 on success
|
||||||
|
*/
|
||||||
|
int uc_ringbuf_write(UCRingBuf *rb, const void *restrict data, uint32_t sz);
|
||||||
|
|
||||||
|
/** Read data from the ringbuffer
|
||||||
|
*
|
||||||
|
* @param rb UCRingBuf to write to.
|
||||||
|
* @param data buffer to copy data into
|
||||||
|
* @param sz number of bytes to read, must not exceed available data
|
||||||
|
* in the buffer
|
||||||
|
* @return 0 on success
|
||||||
|
*/
|
||||||
|
int uc_ringbuf_read(UCRingBuf *rb, void *restrict data, uint32_t sz);
|
||||||
|
|
||||||
|
|
||||||
|
/** Inspect how much free space is available for writing to the ring buffer
|
||||||
|
*
|
||||||
|
* @param rb UCRingBuf to inspect
|
||||||
|
* @returns number of free bytes
|
||||||
|
*/
|
||||||
|
uint32_t uc_ringbuf_available(const UCRingBuf *rb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Length of available data in the ringbuffer
|
||||||
|
*
|
||||||
|
* @param rb UCRingBuf get length of
|
||||||
|
*
|
||||||
|
* @return length of data available to read
|
||||||
|
*/
|
||||||
|
static inline uint32_t uc_ringbuf_len(const UCRingBuf *rb)
|
||||||
|
{
|
||||||
|
return rb->write_idx - rb->read_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a pointer to the first unread byte
|
||||||
|
* Caller is responsible for reading no more than the available
|
||||||
|
* data
|
||||||
|
*
|
||||||
|
* @param rb UCRingBuf to peek into
|
||||||
|
*
|
||||||
|
* @returns pointer to first unread byte
|
||||||
|
*/
|
||||||
|
static inline void *uc_ringbuf_peek(const UCRingBuf *rb)
|
||||||
|
{
|
||||||
|
return &rb->base[rb->read_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
#ifdef __linux
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "ucore/ringbuf.h"
|
||||||
|
|
||||||
|
|
||||||
|
int uc_ringbuf_init(UCRingBuf *rb, uint32_t sz)
|
||||||
|
{
|
||||||
|
|
||||||
|
uint32_t pagesz = getpagesize();
|
||||||
|
// Round up to page size
|
||||||
|
sz = ((sz + pagesz - 1) / pagesz) * pagesz;
|
||||||
|
/* Reserve contiguous address space*/
|
||||||
|
void *base = mmap(NULL, sz * 2, PROT_NONE, MAP_POPULATE|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
||||||
|
if (base == NULL) {
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
/* MAP_PRIVATE doesn't work */
|
||||||
|
base = mmap(base, sz * 1, PROT_READ|PROT_WRITE, MAP_POPULATE|MAP_ANONYMOUS|MAP_FIXED|MAP_SHARED, -1, 0);
|
||||||
|
if (base == MAP_FAILED) {
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
void *mbase = mremap(base, sz * 1, sz, MREMAP_FIXED|MREMAP_MAYMOVE|MREMAP_DONTUNMAP, base + sz);
|
||||||
|
if (mbase == MAP_FAILED) {
|
||||||
|
munmap(base, sz);
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
rb->base = base;
|
||||||
|
rb->sz = sz;
|
||||||
|
rb->read_idx = 0;
|
||||||
|
rb->write_idx = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int uc_ringbuf_destroy(UCRingBuf *rb)
|
||||||
|
{
|
||||||
|
int rc = munmap(rb->base, rb->sz);
|
||||||
|
if (rc != 0) {
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = munmap(rb->base + rb->sz, rb->sz);
|
||||||
|
if (rc != 0) {
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
rb->base = NULL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t uc_ringbuf_available(const UCRingBuf *rb)
|
||||||
|
{
|
||||||
|
return rb->sz - rb->write_idx + rb->read_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
int uc_ringbuf_write(UCRingBuf *rb, const void *restrict data, uint32_t len)
|
||||||
|
{
|
||||||
|
if (rb->write_idx - rb->read_idx + len > rb->sz) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
memcpy(&rb->base[rb->write_idx], data, len);
|
||||||
|
rb->write_idx += len;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int uc_ringbuf_read(UCRingBuf *rb, void *restrict data, uint32_t len)
|
||||||
|
{
|
||||||
|
if (uc_ringbuf_len(rb) < len) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(data, &rb->base[rb->read_idx], len);
|
||||||
|
|
||||||
|
rb->read_idx += len;
|
||||||
|
if (rb->read_idx == rb->write_idx) {
|
||||||
|
rb->read_idx = rb->write_idx = 0;
|
||||||
|
} else if (rb->read_idx > rb->sz) {
|
||||||
|
rb->read_idx -= rb->sz;
|
||||||
|
rb->write_idx -= rb->sz;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#include <check.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "ucore/ringbuf.h"
|
||||||
|
|
||||||
|
START_TEST (test_ringbuf)
|
||||||
|
{
|
||||||
|
|
||||||
|
UCRingBuf rb;
|
||||||
|
int rc;
|
||||||
|
int pgsize = getpagesize();
|
||||||
|
rc = uc_ringbuf_init(&rb, pgsize);
|
||||||
|
ck_assert_int_eq(rc, 0);
|
||||||
|
ck_assert_int_eq(uc_ringbuf_available(&rb), pgsize);
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0 ; i < pgsize; i++) {
|
||||||
|
unsigned char data = i % 256;
|
||||||
|
rc = uc_ringbuf_write(&rb, &data, 1);
|
||||||
|
ck_assert_int_eq(rc, 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
ck_assert_int_eq(uc_ringbuf_available(&rb), 0);
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < pgsize; i++) {
|
||||||
|
unsigned char data = 0;
|
||||||
|
rc = uc_ringbuf_read(&rb, &data, 1);
|
||||||
|
ck_assert_int_eq(rc, 0);
|
||||||
|
ck_assert_int_eq(data, i % 256);
|
||||||
|
}
|
||||||
|
ck_assert_int_eq(uc_ringbuf_available(&rb), pgsize);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Suite *ringbuf_suite(void)
|
||||||
|
{
|
||||||
|
Suite *s = suite_create("ringbuf");
|
||||||
|
TCase *tc1 = tcase_create("ringbuf");
|
||||||
|
|
||||||
|
tcase_add_test(tc1, test_ringbuf);
|
||||||
|
#ifndef __APPLE__
|
||||||
|
#endif
|
||||||
|
|
||||||
|
suite_add_tcase(s, tc1);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
+6
-4
@@ -34,6 +34,7 @@ extern Suite *ticket_lock_suite(void);
|
|||||||
extern Suite *restart_counter_suite(void);
|
extern Suite *restart_counter_suite(void);
|
||||||
extern Suite *iomux_suite(void);
|
extern Suite *iomux_suite(void);
|
||||||
extern Suite *iomux_signal_suite(void);
|
extern Suite *iomux_signal_suite(void);
|
||||||
|
extern Suite *ringbuf_suite(void);
|
||||||
|
|
||||||
static suite_func suites[] = {
|
static suite_func suites[] = {
|
||||||
bitvec_suite,
|
bitvec_suite,
|
||||||
@@ -62,7 +63,8 @@ static suite_func suites[] = {
|
|||||||
ticket_lock_suite,
|
ticket_lock_suite,
|
||||||
restart_counter_suite,
|
restart_counter_suite,
|
||||||
iomux_suite,
|
iomux_suite,
|
||||||
iomux_signal_suite
|
iomux_signal_suite,
|
||||||
|
ringbuf_suite
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -105,7 +107,7 @@ main (int argc, char *argv[])
|
|||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
SRunner *sr = srunner_create (NULL);
|
SRunner *sr = srunner_create (NULL);
|
||||||
if (xml_output)
|
if (xml_output)
|
||||||
srunner_set_xml(sr, "result.xml");
|
srunner_set_xml(sr, "result.xml");
|
||||||
|
|
||||||
//set the environment variable CK_FORK=no which means don't fork().
|
//set the environment variable CK_FORK=no which means don't fork().
|
||||||
@@ -131,10 +133,10 @@ main (int argc, char *argv[])
|
|||||||
number_run = srunner_ntests_run(sr);
|
number_run = srunner_ntests_run(sr);
|
||||||
number_failed = srunner_ntests_failed (sr);
|
number_failed = srunner_ntests_failed (sr);
|
||||||
srunner_free (sr);
|
srunner_free (sr);
|
||||||
|
|
||||||
if (!number_run)
|
if (!number_run)
|
||||||
puts("ERROR: no tests were run");
|
puts("ERROR: no tests were run");
|
||||||
if (number_failed)
|
if (number_failed)
|
||||||
printf("ERROR: %d tests failed\n", number_failed);
|
printf("ERROR: %d tests failed\n", number_failed);
|
||||||
|
|
||||||
return (number_failed != 0) ;
|
return (number_failed != 0) ;
|
||||||
|
|||||||
Reference in New Issue
Block a user