]> git.ozlabs.org Git - ccan/blobdiff - ccan/rbuf/rbuf.h
rbuf: new module.
[ccan] / ccan / rbuf / rbuf.h
diff --git a/ccan/rbuf/rbuf.h b/ccan/rbuf/rbuf.h
new file mode 100644 (file)
index 0000000..ab1504a
--- /dev/null
@@ -0,0 +1,156 @@
+/* Licensed under BSD-MIT - see LICENSE file for details */
+#ifndef CCAN_RBUF_H
+#define CCAN_RBUF_H
+#include <stdio.h> // For size_t
+#include <limits.h> // For UCHAR_MAX
+#include <assert.h>
+#include <stdbool.h>
+
+struct rbuf {
+       int fd;
+
+       /* Where to read next. */
+       char *start;
+       /* How much of what is there is valid. */
+       size_t len;
+
+       /* The entire buffer memory we have to work with. */
+       char *buf, *buf_end;
+};
+
+/**
+ * rbuf_init - set up a buffer.
+ * @buf: the struct rbuf.
+ * @fd: the file descriptor.
+ * @buf: the buffer to use.
+ * @buf_max: the size of the buffer.
+ */
+static inline void rbuf_init(struct rbuf *buf,
+                            int fd, char *buffer, size_t buf_max)
+{
+       buf->fd = fd;
+       buf->start = buf->buf = buffer;
+       buf->len = 0;
+       buf->buf_end = buffer + buf_max;
+}
+
+/**
+ * rbuf_open - set up a buffer by opening a file.
+ * @buf: the struct rbuf.
+ * @filename: the filename
+ * @buf: the buffer to use.
+ * @buf_max: the size of the buffer.
+ *
+ * Returns false if the open fails.  If @buf_max is 0, then the buffer
+ * will be resized to rbuf_good_size() on first rbuf_fill.
+ *
+ * Example:
+ *     struct rbuf in;
+ *
+ *     if (!rbuf_open(&in, "foo", NULL, 0))
+ *             err(1, "Could not open foo");
+ */
+bool rbuf_open(struct rbuf *rbuf, const char *name, char *buf, size_t buf_max);
+
+/**
+ * rbuf_good_size - get a good buffer size for this fd.
+ * @fd: the file descriptor.
+ *
+ * If you don't know what size you want, try this.
+ */
+size_t rbuf_good_size(int fd);
+
+/**
+ * rbuf_fill - read into a buffer if it's empty.
+ * @buf: the struct rbuf
+ * @resize: the call to resize the buffer.
+ *
+ * If @resize is needed and is NULL, or returns false, rbuf_read will
+ * return NULL (with errno set to ENOMEM).  If a read fails, then NULL
+ * is also returned.  If there is nothing more to read, it will return
+ * NULL with errno set to 0.  Otherwise, returns @buf->start; @buf->len
+ * is the valid length of the buffer.
+ *
+ * You need to call rbuf_consume() to mark data in the buffer as
+ * consumed.
+ *
+ * Example:
+ *     while (rbuf_fill(&in, realloc)) {
+ *             printf("%.*s\n", (int)in.len, in.start);
+ *             rbuf_consume(&in, in.len);
+ *     }
+ *     if (errno)
+ *             err(1, "reading foo");
+ */
+void *rbuf_fill(struct rbuf *rbuf, void *(*resize)(void *buf, size_t len));
+
+/**
+ * rbuf_consume - helper to use up data in a buffer.
+ * @buf: the struct rbuf
+ * @len: the length (from @buf->start) you used.
+ *
+ * After rbuf_fill() you should indicate the data you've used with
+ * rbuf_consume().  That way rbuf_fill() will know if it has anything
+ * to do.
+ */
+static inline void rbuf_consume(struct rbuf *buf, size_t len)
+{
+       buf->len -= len;
+       buf->start += len;
+}
+
+/**
+ * rbuf_fill_all - read rest of file into a buffer.
+ * @buf: the struct rbuf
+ * @resize: the call to resize the buffer.
+ *
+ * If @resize is needed and is NULL, or returns false, rbuf_read_all
+ * will return NULL (with errno set to ENOMEM).  If a read fails,
+ * then NULL is also returned, otherwise returns @buf->start.
+ *
+ * Example:
+ *     if (!rbuf_fill_all(&in, realloc)) {
+ *             if (errno)
+ *                     err(1, "reading foo");
+ *     }
+ */
+void *rbuf_fill_all(struct rbuf *rbuf, void *(*resize)(void *buf, size_t len));
+
+/**
+ * rbuf_read_str - fill into a buffer up to a terminator, and consume string.
+ * @buf: the struct rbuf
+ * @term: the character to terminate the read.
+ * @resize: the call to resize the buffer.
+ *
+ * If @resize is needed and is NULL, or returns false, rbuf_read_str
+ * will return NULL (with errno set to ENOMEM).  If a read fails,
+ * then NULL is also returned, otherwise the next string.  It
+ * replaces the terminator @term (if any) with NUL, otherwise NUL
+ * is placed after EOF.  If you need to, you can tell this has happened
+ * because the nul terminator will be at @buf->start (normally it will
+ * be at @buf->start - 1).
+ *
+ * If there is nothing remaining to be read, NULL is returned with
+ * errno set to 0, unless @term is NUL, in which case it returns the
+ * empty string.
+ *
+ * Note: using @term set to NUL is a cheap way of getting an entire
+ * file into a C string, as long as the file doesn't contain NUL.
+ *
+ * Example:
+ *     char *line;
+ *
+ *     line = rbuf_read_str(&in, '\n', realloc);
+ *     if (!line) {
+ *             if (errno)
+ *                     err(1, "reading foo");
+ *             else
+ *                     printf("Empty file\n");
+ *     } else
+ *             printf("First line is %s\n", line);
+ *
+ */
+char *rbuf_read_str(struct rbuf *rbuf, char term,
+                   void *(*resize)(void *buf, size_t len));
+
+#endif /* CCAN_RBUF_H */