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

2013-12-10 Thread Lennart Poettering
On Thu, 28.11.13 09:19, David Herrmann (dh.herrm...@gmail.com) wrote:

  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..

Well, you only need the wrapping over for the reading side, the writing
side never needs that.

int append(..., length, ...) {
 ...
 if (current_length + length  MAX_LENGTH)
return -ENOBUFS;
 ...
 current_length += length;
 ...
}

int remove(..., length, ...) {
 ...
 if (length  current_length)
return -EAGAIN;
 ...
 rindex = (rindex + length)  RING_MASK;
 current_length -= length;
 ...
}

Lennart

-- 
Lennart Poettering, Red Hat
___
systemd-devel mailing list
systemd-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/systemd-devel


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

2013-11-28 Thread David Herrmann
Hi

On Wed, Nov 27, 2013 at 10:53 PM, Lennart Poettering
lenn...@poettering.net wrote:
 On Wed, 27.11.13 19:48, David Herrmann (dh.herrm...@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
___
systemd-devel mailing list
systemd-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/systemd-devel


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

2013-11-28 Thread David Herrmann
Hi

On Thu, Nov 28, 2013 at 1:27 AM, Lucas De Marchi
lucas.de.mar...@gmail.com wrote:
 On Wed, Nov 27, 2013 at 4:48 PM, David Herrmann dh.herrm...@gmail.com wrote:
 This adds a very straightforward ring-buffer implementation to
 libsystemd-shared. Ring-buffers allow pushing data to, and pulling data
 from, both ends of a buffer without modifying existing/remaining buffer
 content. This is very useful for network buffers and asynchronous writes.

 This implementation only contains the most basic functions to push data
 onto the end of the buffer and pull data from the front. More helpers may
 be added once needed.
 ---
  Makefile.am   |   2 +
  src/shared/ring.c | 213 
 ++
  src/shared/ring.h |  54 ++
  3 files changed, 269 insertions(+)
  create mode 100644 src/shared/ring.c
  create mode 100644 src/shared/ring.h

 diff --git a/Makefile.am b/Makefile.am
 index 0119751..4aa2bdf 100644
 --- a/Makefile.am
 +++ b/Makefile.am
 @@ -768,6 +768,8 @@ libsystemd_shared_la_SOURCES = \
 src/shared/net-util.h \
 src/shared/errno-list.c \
 src/shared/errno-list.h \
 +   src/shared/ring.h \
 +   src/shared/ring.c \
 src/shared/syscall-list.c \
 src/shared/syscall-list.h

 diff --git a/src/shared/ring.c b/src/shared/ring.c
 new file mode 100644
 index 000..f60f098
 --- /dev/null
 +++ b/src/shared/ring.c
 @@ -0,0 +1,213 @@
 +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
 +
 +/***
 +  This file is part of systemd.
 +
 +  Copyright 2013 David Herrmann dh.herrm...@gmail.com
 +
 +  systemd is free software; you can redistribute it and/or modify it
 +  under the terms of the GNU Lesser General Public License as published by
 +  the Free Software Foundation; either version 2.1 of the License, or
 +  (at your option) any later version.
 +
 +  systemd is distributed in the hope that it will be useful, but
 +  WITHOUT ANY WARRANTY; without even the implied warranty of
 +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 +  Lesser General Public License for more details.
 +
 +  You should have received a copy of the GNU Lesser General Public License
 +  along with systemd; If not, see http://www.gnu.org/licenses/.
 +***/
 +
 +#include errno.h
 +#include stdlib.h
 +#include string.h
 +#include sys/uio.h
 +
 +#include ring.h
 +#include util.h
 +
 +#define RING_MASK(_r, _v) ((_v)  ((_r)-size - 1))
 +
 +void ring_flush(Ring *r) {
 +r-start = 0;
 +r-end = 0;
 +}
 +
 +void ring_clear(Ring *r) {
 +free(r-buf);
 +memset(r, 0, sizeof(*r));
 +}
 +
 +/*
 + * 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;
 +
 +buf = malloc(nsize);
 +if (!buf)
 +return -ENOMEM;
 +
 +if (r-end == r-start) {
 +r-end = 0;
 +r-start = 0;
 +} else if (r-end  r-start) {
 +memcpy(buf, r-buf[r-start], r-end - r-start);
 +
 +r-end -= r-start;
 +r-start = 0;
 +} else {
 +memcpy(buf, r-buf[r-start], r-size - r-start);
 +memcpy(buf[r-size - r-start], r-buf, r-end);
 +
 +r-end += r-size - r-start;
 +r-start = 0;
 +}
 +
 +free(r-buf);
 +r-buf = buf;
 +r-size = nsize;
 +
 +return 0;
 +}
 +
 +/* Compute next higher power-of-2 of @v. Returns 4096 in case v is 0. */
 +static size_t ring_pow2(size_t v) {
 +size_t i;
 +
 +if (!v)
 +return 4096;
 +
 +--v;
 +
 +for (i = 1; i  8 * sizeof(size_t); i *= 2)
 +v |= v  i;
 +
 +return ++v;

 If you are interested, take a look in
 https://git.kernel.org/cgit/utils/kernel/kmod/kmod.git/commit/?id=3ba7f59e84857eb4dbe56a68fc7a3ffe8a650393
 for a shorter and faster version of this.

Yepp, that one looks good. Didn't know there was a builtin to count
leading zeros.

Thanks
David
___
systemd-devel mailing list
systemd-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/systemd-devel


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

2013-11-27 Thread David Herrmann
This adds a very straightforward ring-buffer implementation to
libsystemd-shared. Ring-buffers allow pushing data to, and pulling data
from, both ends of a buffer without modifying existing/remaining buffer
content. This is very useful for network buffers and asynchronous writes.

This implementation only contains the most basic functions to push data
onto the end of the buffer and pull data from the front. More helpers may
be added once needed.
---
 Makefile.am   |   2 +
 src/shared/ring.c | 213 ++
 src/shared/ring.h |  54 ++
 3 files changed, 269 insertions(+)
 create mode 100644 src/shared/ring.c
 create mode 100644 src/shared/ring.h

diff --git a/Makefile.am b/Makefile.am
index 0119751..4aa2bdf 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -768,6 +768,8 @@ libsystemd_shared_la_SOURCES = \
src/shared/net-util.h \
src/shared/errno-list.c \
src/shared/errno-list.h \
+   src/shared/ring.h \
+   src/shared/ring.c \
src/shared/syscall-list.c \
src/shared/syscall-list.h
 
diff --git a/src/shared/ring.c b/src/shared/ring.c
new file mode 100644
index 000..f60f098
--- /dev/null
+++ b/src/shared/ring.c
@@ -0,0 +1,213 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2013 David Herrmann dh.herrm...@gmail.com
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see http://www.gnu.org/licenses/.
+***/
+
+#include errno.h
+#include stdlib.h
+#include string.h
+#include sys/uio.h
+
+#include ring.h
+#include util.h
+
+#define RING_MASK(_r, _v) ((_v)  ((_r)-size - 1))
+
+void ring_flush(Ring *r) {
+r-start = 0;
+r-end = 0;
+}
+
+void ring_clear(Ring *r) {
+free(r-buf);
+memset(r, 0, sizeof(*r));
+}
+
+/*
+ * 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;
+
+buf = malloc(nsize);
+if (!buf)
+return -ENOMEM;
+
+if (r-end == r-start) {
+r-end = 0;
+r-start = 0;
+} else if (r-end  r-start) {
+memcpy(buf, r-buf[r-start], r-end - r-start);
+
+r-end -= r-start;
+r-start = 0;
+} else {
+memcpy(buf, r-buf[r-start], r-size - r-start);
+memcpy(buf[r-size - r-start], r-buf, r-end);
+
+r-end += r-size - r-start;
+r-start = 0;
+}
+
+free(r-buf);
+r-buf = buf;
+r-size = nsize;
+
+return 0;
+}
+
+/* Compute next higher power-of-2 of @v. Returns 4096 in case v is 0. */
+static size_t ring_pow2(size_t v) {
+size_t i;
+
+if (!v)
+return 4096;
+
+--v;
+
+for (i = 1; i  8 * sizeof(size_t); i *= 2)
+v |= v  i;
+
+return ++v;
+}
+
+/*
+ * Resize ring-buffer to provide enough room for @add bytes of new data. This
+ * resizes the buffer if it is too small. It returns -ENOMEM on OOM and 0 on
+ * success.
+ */
+static int ring_grow(Ring *r, size_t add) {
+size_t len;
+
+/*
+ * Note that end == start means empty buffer. Hence, we can never
+ * fill the last byte of a buffer. That means, we must account for an
+ * additional byte here (end == start-byte).
+ */
+
+if (r-end  r-start)
+len = r-start - r-end;
+else
+len = r-start + r-size - r-end;
+
+/* don't use = as end == start would be ambigious */
+if (len  add)
+return 0;
+
+/* +1 for additional end == start byte */
+len = r-size + add - len + 1;
+len = ring_pow2(len);
+
+if (len = r-size)
+return -ENOMEM;
+
+return ring_resize(r, 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) {
+int err;
+size_t l;
+
+err = ring_grow(r, len);
+if (err  0)
+return err;
+
+if (r-start = r-end) {
+l = r-size - r-end;
+if (l  len)
+l = len;
+

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

2013-11-27 Thread Lennart Poettering
On Wed, 27.11.13 19:48, David Herrmann (dh.herrm...@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...

 +
 +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...

 + * 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...

Lennart

-- 
Lennart Poettering, Red Hat
___
systemd-devel mailing list
systemd-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/systemd-devel


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

2013-11-27 Thread Lucas De Marchi
On Wed, Nov 27, 2013 at 4:48 PM, David Herrmann dh.herrm...@gmail.com wrote:
 This adds a very straightforward ring-buffer implementation to
 libsystemd-shared. Ring-buffers allow pushing data to, and pulling data
 from, both ends of a buffer without modifying existing/remaining buffer
 content. This is very useful for network buffers and asynchronous writes.

 This implementation only contains the most basic functions to push data
 onto the end of the buffer and pull data from the front. More helpers may
 be added once needed.
 ---
  Makefile.am   |   2 +
  src/shared/ring.c | 213 
 ++
  src/shared/ring.h |  54 ++
  3 files changed, 269 insertions(+)
  create mode 100644 src/shared/ring.c
  create mode 100644 src/shared/ring.h

 diff --git a/Makefile.am b/Makefile.am
 index 0119751..4aa2bdf 100644
 --- a/Makefile.am
 +++ b/Makefile.am
 @@ -768,6 +768,8 @@ libsystemd_shared_la_SOURCES = \
 src/shared/net-util.h \
 src/shared/errno-list.c \
 src/shared/errno-list.h \
 +   src/shared/ring.h \
 +   src/shared/ring.c \
 src/shared/syscall-list.c \
 src/shared/syscall-list.h

 diff --git a/src/shared/ring.c b/src/shared/ring.c
 new file mode 100644
 index 000..f60f098
 --- /dev/null
 +++ b/src/shared/ring.c
 @@ -0,0 +1,213 @@
 +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
 +
 +/***
 +  This file is part of systemd.
 +
 +  Copyright 2013 David Herrmann dh.herrm...@gmail.com
 +
 +  systemd is free software; you can redistribute it and/or modify it
 +  under the terms of the GNU Lesser General Public License as published by
 +  the Free Software Foundation; either version 2.1 of the License, or
 +  (at your option) any later version.
 +
 +  systemd is distributed in the hope that it will be useful, but
 +  WITHOUT ANY WARRANTY; without even the implied warranty of
 +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 +  Lesser General Public License for more details.
 +
 +  You should have received a copy of the GNU Lesser General Public License
 +  along with systemd; If not, see http://www.gnu.org/licenses/.
 +***/
 +
 +#include errno.h
 +#include stdlib.h
 +#include string.h
 +#include sys/uio.h
 +
 +#include ring.h
 +#include util.h
 +
 +#define RING_MASK(_r, _v) ((_v)  ((_r)-size - 1))
 +
 +void ring_flush(Ring *r) {
 +r-start = 0;
 +r-end = 0;
 +}
 +
 +void ring_clear(Ring *r) {
 +free(r-buf);
 +memset(r, 0, sizeof(*r));
 +}
 +
 +/*
 + * 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;
 +
 +buf = malloc(nsize);
 +if (!buf)
 +return -ENOMEM;
 +
 +if (r-end == r-start) {
 +r-end = 0;
 +r-start = 0;
 +} else if (r-end  r-start) {
 +memcpy(buf, r-buf[r-start], r-end - r-start);
 +
 +r-end -= r-start;
 +r-start = 0;
 +} else {
 +memcpy(buf, r-buf[r-start], r-size - r-start);
 +memcpy(buf[r-size - r-start], r-buf, r-end);
 +
 +r-end += r-size - r-start;
 +r-start = 0;
 +}
 +
 +free(r-buf);
 +r-buf = buf;
 +r-size = nsize;
 +
 +return 0;
 +}
 +
 +/* Compute next higher power-of-2 of @v. Returns 4096 in case v is 0. */
 +static size_t ring_pow2(size_t v) {
 +size_t i;
 +
 +if (!v)
 +return 4096;
 +
 +--v;
 +
 +for (i = 1; i  8 * sizeof(size_t); i *= 2)
 +v |= v  i;
 +
 +return ++v;

If you are interested, take a look in
https://git.kernel.org/cgit/utils/kernel/kmod/kmod.git/commit/?id=3ba7f59e84857eb4dbe56a68fc7a3ffe8a650393
for a shorter and faster version of this.


Lucas De Marchi
___
systemd-devel mailing list
systemd-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/systemd-devel