[systemd-devel] [RFC 02/12] ring: add basic ring-buffer helper

David Herrmann dh.herrmann at gmail.com
Thu Nov 28 00:19:06 PST 2013


Hi

On Wed, Nov 27, 2013 at 10:53 PM, Lennart Poettering
<lennart at poettering.net> wrote:
> On Wed, 27.11.13 19:48, David Herrmann (dh.herrmann at gmail.com) wrote:
>
>> +void ring_flush(Ring *r) {
>> +        r->start = 0;
>> +        r->end = 0;
>> +}
>> +
>> +void ring_clear(Ring *r) {
>> +        free(r->buf);
>> +        memset(r, 0, sizeof(*r));
>
> zero(*r) is a tiny bit nicer.
>
>> +}
>> +
>> +/*
>> + * Resize ring-buffer to size @nsize. @nsize must be a power-of-2, otherwise
>> + * ring operations will behave incorrectly.
>> + */
>> +static int ring_resize(Ring *r, size_t nsize) {
>> +        char *buf;
>
> Hmm, "char" suggests a bit that this is about text. But it's mostly raw
> bytes, right? So I'd always use uint8_t for these things, it feels so
> much "rawer"... Not that it would matter much...

Yepp, uint8_t it is.

>> +
>> +        buf = malloc(nsize);
>> +        if (!buf)
>> +                return -ENOMEM;
>> +
>> +        if (r->end == r->start) {
>> +                r->end = 0;
>> +                r->start = 0;
>
> Hmm, so if end and start match the buffer is empty? So you can never
> fill it entirely? Or am I missing something?
>
> I'd always maintain start index + fill level instead of a start
> index + end index, to avoid the confusion regarding the fill-level...

Well, start+end is how ring-buffers were implemented historically. I
think the reason is that "wrapping over" is easier to calculate for
indices than for lengths (you cannot do: r->len = (r->len + 1) &
RING_MASK). Even all examples on wikipedia use two indices
(http://en.wikipedia.org/wiki/Circular_buffer). And all kernel
ring-buffers use start/end..

Also note that the one special byte is not "unused". It cannot be
filled, in case the buffer is full, that's right. But it's used during
normal buffer-operation if the occupied space "moves along" the
buffer.

If you can find me circular/ring-buffers that use start+len, I can fix
that up. Otherwise, I'd rather keep this close to existing
implementations. Besides, a lot of "wrapping calculations" get easier
if start/end are restricted by RING_MASK, which wouldn't be the case
for "len".

>> + * Push @len bytes from @u8 into the ring buffer. The buffer is resized if it
>> + * is too small. -ENOMEM is returned on OOM, 0 on success.
>> + */
>> +int ring_push(Ring *r, const char *u8, size_t len) {
>
> So, here you call the parameter u8 suggesting unsigned bytes, but you
> use char, which indicates a text string, and is
> signed... Confusing. I'd recommend using "const void *" here by default,
> since all types implicitly downgrade to "const void*" without casting...

I usually use "u8" for "utf8".. no idea where that came from. But yes,
void* makes sense for input/output and uint8_t* for internal
operations.

Thanks
David


More information about the systemd-devel mailing list