]> git.ozlabs.org Git - ccan/blobdiff - ccan/rbuf/rbuf.c
rbuf: new module.
[ccan] / ccan / rbuf / rbuf.c
diff --git a/ccan/rbuf/rbuf.c b/ccan/rbuf/rbuf.c
new file mode 100644 (file)
index 0000000..0e210a9
--- /dev/null
@@ -0,0 +1,130 @@
+/* Licensed under BSD-MIT - see LICENSE file for details */
+#include <ccan/rbuf/rbuf.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+
+bool rbuf_open(struct rbuf *rbuf, const char *name, char *buf, size_t buf_max)
+{
+       int fd = open(name, O_RDONLY);
+       if (fd >= 0) {
+               rbuf_init(rbuf, fd, buf, buf_max);
+               return true;
+       }
+       return false;
+}
+
+static size_t rem(const struct rbuf *buf)
+{
+       return buf->buf_end - (buf->start + buf->len);
+}
+
+size_t rbuf_good_size(int fd)
+{
+       struct stat st;
+
+       if (fstat(fd, &st) == 0 && st.st_blksize >= 4096)
+               return st.st_blksize;
+       return 4096;
+}
+
+static bool enlarge_buf(struct rbuf *buf, size_t len,
+                       void *(*resize)(void *buf, size_t len))
+{
+       char *new;
+       if (!resize) {
+               errno = ENOMEM;
+               return false;
+       }
+       if (!len)
+               len = rbuf_good_size(buf->fd);
+       new = resize(buf->buf, len);
+       if (!new)
+               return false;
+       buf->start += (new - buf->buf);
+       buf->buf = new;
+       buf->buf_end = new + len;
+       return true;
+}
+
+static ssize_t get_more(struct rbuf *rbuf,
+                       void *(*resize)(void *buf, size_t len))
+{
+       size_t r;
+
+       if (rbuf->start + rbuf->len == rbuf->buf_end) {
+               if (!enlarge_buf(rbuf, (rbuf->buf_end - rbuf->buf) * 2, resize))
+                       return -1;
+       }
+
+       r = read(rbuf->fd, rbuf->start + rbuf->len, rem(rbuf));
+       if (r <= 0)
+               return r;
+
+       rbuf->len += r;
+       return r;
+}
+
+void *rbuf_fill_all(struct rbuf *rbuf, void *(*resize)(void *buf, size_t len))
+{
+       ssize_t r;
+
+       /* Move back to start of buffer if we're empty. */
+       if (!rbuf->len)
+               rbuf->start = rbuf->buf;
+
+       while ((r = get_more(rbuf, resize)) != 0)
+               if (r < 0)
+                       return NULL;
+       return rbuf->start;
+}
+
+void *rbuf_fill(struct rbuf *rbuf, void *(*resize)(void *buf, size_t len))
+{
+       if (!rbuf->len) {
+               rbuf->start = rbuf->buf;
+               if (get_more(rbuf, resize) < 0)
+                       return NULL;
+       }
+       return rbuf->start;
+}
+
+char *rbuf_read_str(struct rbuf *rbuf, char term,
+                   void *(*resize)(void *buf, size_t len))
+{
+       char *p, *ret;
+       ssize_t r = 0;
+       size_t prev = 0;
+
+       /* Move back to start of buffer if we're empty. */
+       if (!rbuf->len)
+               rbuf->start = rbuf->buf;
+
+       while (!(p = memchr(rbuf->start + prev, term, rbuf->len - prev))) {
+               prev += r;
+               r = get_more(rbuf, resize);
+               if (r < 0)
+                       return NULL;
+               /* EOF with no term. */
+               if (r == 0) {
+                       /* Nothing read at all? */
+                       if (!rbuf->len && term) {
+                               errno = 0;
+                               return NULL;
+                       }
+                       /* Put term after input (get_more made room). */
+                       assert(rbuf->start + rbuf->len < rbuf->buf_end);
+                       rbuf->start[rbuf->len] = '\0';
+                       ret = rbuf->start;
+                       rbuf_consume(rbuf, rbuf->len);
+                       return ret;
+               }
+       }
+       *p = '\0';
+       ret = rbuf->start;
+       rbuf_consume(rbuf, p + 1 - ret);
+       return ret;
+}