Commit d8a270fd authored by Rusty Russell's avatar Rusty Russell

rbuf: adapt to work on ccan/membuf.

Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent 86d30436
...@@ -23,19 +23,19 @@ ...@@ -23,19 +23,19 @@
* char *word; * char *word;
* *
* if (argv[1]) { * if (argv[1]) {
* if (!rbuf_open(&in, argv[1], NULL, 0)) * if (!rbuf_open(&in, argv[1], NULL, 0, membuf_realloc))
* err(1, "Failed opening %s", argv[1]); * err(1, "Failed opening %s", argv[1]);
* } else * } else
* rbuf_init(&in, STDIN_FILENO, NULL, 0); * rbuf_init(&in, STDIN_FILENO, NULL, 0, membuf_realloc);
* *
* while ((word = rbuf_read_str(&in, ' ', realloc)) != NULL) * while ((word = rbuf_read_str(&in, ' ')) != NULL)
* printf("%s*", word); * printf("%s*", word);
* *
* if (errno) * if (errno)
* err(1, "Reading %s", argv[1] ? argv[1] : "<stdin>"); * err(1, "Reading %s", argv[1] ? argv[1] : "<stdin>");
* *
* // Free the buffer, just because we can. * // Free the buffer, just because we can.
* free(in.buf); * free(rbuf_cleanup(&in));
* return 0; * return 0;
* } * }
*/ */
...@@ -46,6 +46,7 @@ int main(int argc, char *argv[]) ...@@ -46,6 +46,7 @@ int main(int argc, char *argv[])
return 1; return 1;
if (strcmp(argv[1], "depends") == 0) { if (strcmp(argv[1], "depends") == 0) {
printf("ccan/membuf\n");
return 0; return 0;
} }
......
...@@ -7,21 +7,17 @@ ...@@ -7,21 +7,17 @@
#include <string.h> #include <string.h>
#include <fcntl.h> #include <fcntl.h>
bool rbuf_open(struct rbuf *rbuf, const char *name, char *buf, size_t buf_max) bool rbuf_open(struct rbuf *rbuf, const char *name, char *buf, size_t buf_max,
void *(*expandfn)(struct membuf *, void *, size_t))
{ {
int fd = open(name, O_RDONLY); int fd = open(name, O_RDONLY);
if (fd >= 0) { if (fd >= 0) {
rbuf_init(rbuf, fd, buf, buf_max); rbuf_init(rbuf, fd, buf, buf_max, expandfn);
return true; return true;
} }
return false; 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) size_t rbuf_good_size(int fd)
{ {
struct stat st; struct stat st;
...@@ -31,100 +27,78 @@ size_t rbuf_good_size(int fd) ...@@ -31,100 +27,78 @@ size_t rbuf_good_size(int fd)
return 4096; return 4096;
} }
static bool enlarge_buf(struct rbuf *buf, size_t len, static ssize_t get_more(struct rbuf *rbuf)
void *(*resize)(void *buf, size_t len))
{ {
char *new; ssize_t r;
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, /* This is so we only call rbuf_good_size once. */
void *(*resize)(void *buf, size_t len)) if (tcon_unwrap(&rbuf->m)->max_elems == 0)
{ membuf_prepare_space(&rbuf->m, rbuf_good_size(rbuf->fd));
size_t r; else /* membuf doubles internally, so just ask for anything. */
membuf_prepare_space(&rbuf->m, 1);
if (rbuf->start + rbuf->len == rbuf->buf_end) { /* This happens if realloc fails (errno already ENOMEM) */
if (!enlarge_buf(rbuf, (rbuf->buf_end - rbuf->buf) * 2, resize)) if (!membuf_num_space(&rbuf->m))
return -1; return -1;
}
r = read(rbuf->fd, rbuf->start + rbuf->len, rem(rbuf)); r = read(rbuf->fd, membuf_space(&rbuf->m), membuf_num_space(&rbuf->m));
if (r <= 0) if (r <= 0)
return r; return r;
rbuf->len += r; membuf_add(&rbuf->m, r);
return r; return r;
} }
void *rbuf_fill_all(struct rbuf *rbuf, void *(*resize)(void *buf, size_t len)) void *rbuf_fill_all(struct rbuf *rbuf)
{ {
ssize_t r; ssize_t r;
/* Move back to start of buffer if we're empty. */ while ((r = get_more(rbuf)) != 0)
if (!rbuf->len)
rbuf->start = rbuf->buf;
while ((r = get_more(rbuf, resize)) != 0)
if (r < 0) if (r < 0)
return NULL; return NULL;
return rbuf->start; return rbuf_start(rbuf);
} }
void *rbuf_fill(struct rbuf *rbuf, void *(*resize)(void *buf, size_t len)) void *rbuf_fill(struct rbuf *rbuf)
{ {
if (!rbuf->len) { if (!rbuf_len(rbuf)) {
rbuf->start = rbuf->buf; if (get_more(rbuf) < 0)
if (get_more(rbuf, resize) < 0)
return NULL; return NULL;
} }
return rbuf->start; return rbuf_start(rbuf);
} }
char *rbuf_read_str(struct rbuf *rbuf, char term, char *rbuf_read_str(struct rbuf *rbuf, char term)
void *(*resize)(void *buf, size_t len))
{ {
char *p, *ret; char *p;
ssize_t r = 0; ssize_t r = 0;
size_t prev = 0; size_t prev = 0;
/* Move back to start of buffer if we're empty. */ while (!(p = memchr(membuf_elems(&rbuf->m) + prev,
if (!rbuf->len) term,
rbuf->start = rbuf->buf; membuf_num_elems(&rbuf->m) - prev))) {
while (!(p = memchr(rbuf->start + prev, term, rbuf->len - prev))) {
prev += r; prev += r;
r = get_more(rbuf, resize); r = get_more(rbuf);
if (r < 0) if (r < 0)
return NULL; return NULL;
/* EOF with no term. */ /* EOF with no term. */
if (r == 0) { if (r == 0) {
char *ret;
size_t len = rbuf_len(rbuf);
/* Nothing read at all? */ /* Nothing read at all? */
if (!rbuf->len && term) { if (!len && term) {
errno = 0; errno = 0;
return NULL; return NULL;
} }
/* Put term after input (get_more made room). */ /* Put term after input (get_more made room). */
assert(rbuf->start + rbuf->len < rbuf->buf_end); assert(membuf_num_space(&rbuf->m) > 0);
rbuf->start[rbuf->len] = '\0'; ret = membuf_consume(&rbuf->m, len);
ret = rbuf->start; ret[len] = '\0';
rbuf_consume(rbuf, rbuf->len);
return ret; return ret;
} }
} }
*p = '\0'; *p = '\0';
ret = rbuf->start; return membuf_consume(&rbuf->m, p + 1 - (char *)rbuf_start(rbuf));
rbuf_consume(rbuf, p + 1 - ret);
return ret;
} }
...@@ -5,41 +5,36 @@ ...@@ -5,41 +5,36 @@
#include <limits.h> // For UCHAR_MAX #include <limits.h> // For UCHAR_MAX
#include <assert.h> #include <assert.h>
#include <stdbool.h> #include <stdbool.h>
#include <ccan/membuf/membuf.h>
struct rbuf { struct rbuf {
int fd; int fd;
MEMBUF(char) m;
/* 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. * rbuf_init - set up a buffer.
* @buf: the struct rbuf. * @rbuf: the struct rbuf.
* @fd: the file descriptor. * @fd: the file descriptor.
* @buf: the buffer to use. * @buf: the buffer to use.
* @buf_max: the size of the buffer. * @buf_max: the size of the buffer.
* @expandfn: usually membuf_realloc.
*/ */
static inline void rbuf_init(struct rbuf *buf, static inline void rbuf_init(struct rbuf *rbuf,
int fd, char *buffer, size_t buf_max) int fd, char *buffer, size_t buf_max,
void *(*expandfn)(struct membuf *, void *, size_t))
{ {
buf->fd = fd; rbuf->fd = fd;
buf->start = buf->buf = buffer; membuf_init(&rbuf->m, buffer, buf_max, expandfn);
buf->len = 0;
buf->buf_end = buffer + buf_max;
} }
/** /**
* rbuf_open - set up a buffer by opening a file. * rbuf_open - set up a buffer by opening a file.
* @buf: the struct rbuf. * @rbuf: the struct rbuf.
* @filename: the filename * @filename: the filename
* @buf: the buffer to use. * @buf: the buffer to use.
* @buf_max: the size of the buffer. * @buf_max: the size of the buffer.
* @expandfn: usually membuf_realloc.
* *
* Returns false if the open fails. If @buf_max is 0, then 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. * will be resized to rbuf_good_size() on first rbuf_fill.
...@@ -47,10 +42,11 @@ static inline void rbuf_init(struct rbuf *buf, ...@@ -47,10 +42,11 @@ static inline void rbuf_init(struct rbuf *buf,
* Example: * Example:
* struct rbuf in; * struct rbuf in;
* *
* if (!rbuf_open(&in, "foo", NULL, 0)) * if (!rbuf_open(&in, "foo", NULL, 0, membuf_realloc))
* err(1, "Could not open foo"); * err(1, "Could not open foo");
*/ */
bool rbuf_open(struct rbuf *rbuf, const char *name, char *buf, size_t buf_max); bool rbuf_open(struct rbuf *rbuf, const char *name, char *buf, size_t buf_max,
void *(*expandfn)(struct membuf *, void *, size_t));
/** /**
* rbuf_good_size - get a good buffer size for this fd. * rbuf_good_size - get a good buffer size for this fd.
...@@ -62,73 +58,82 @@ size_t rbuf_good_size(int fd); ...@@ -62,73 +58,82 @@ size_t rbuf_good_size(int fd);
/** /**
* rbuf_fill - read into a buffer if it's empty. * rbuf_fill - read into a buffer if it's empty.
* @buf: the struct rbuf * @rbuf: the struct rbuf
* @resize: the call to resize the buffer.
* *
* If @resize is needed and is NULL, or returns false, rbuf_read will * If @expandfn fails, rbuf_fill will return NULL (with errno set to ENOMEM).
* return NULL (with errno set to ENOMEM). If a read fails, then NULL * If a read fails, then NULL is also returned. If there is nothing more to
* is also returned. If there is nothing more to read, it will return * read, it will return NULL with errno set to 0. Otherwise, returns first
* NULL with errno set to 0. Otherwise, returns @buf->start; @buf->len * populated bytes (aka. rbuf_start()); rbuf_len() is the valid length of the
* is the valid length of the buffer. * buffer.
* *
* You need to call rbuf_consume() to mark data in the buffer as * You need to call rbuf_consume() to mark data in the buffer as
* consumed. * consumed.
* *
* Example: * Example:
* while (rbuf_fill(&in, realloc)) { * while (rbuf_fill(&in)) {
* printf("%.*s\n", (int)in.len, in.start); * printf("%.*s\n", (int)rbuf_len(&in), rbuf_start(&in));
* rbuf_consume(&in, in.len); * rbuf_consume(&in, rbuf_len(&in));
* } * }
* if (errno) * if (errno)
* err(1, "reading foo"); * err(1, "reading foo");
*/ */
void *rbuf_fill(struct rbuf *rbuf, void *(*resize)(void *buf, size_t len)); void *rbuf_fill(struct rbuf *rbuf);
/** /**
* rbuf_consume - helper to use up data in a buffer. * rbuf_consume - helper to use up data in a buffer.
* @buf: the struct rbuf * @rbuf: the struct rbuf
* @len: the length (from @buf->start) you used. * @len: the length (from @buf->start) you used.
* *
* After rbuf_fill() you should indicate the data you've used with * After rbuf_fill() you should indicate the data you've used with
* rbuf_consume(). That way rbuf_fill() will know if it has anything * rbuf_consume(). That way rbuf_fill() will know if it has anything
* to do. * to do.
*/ */
static inline void rbuf_consume(struct rbuf *buf, size_t len) static inline void rbuf_consume(struct rbuf *rbuf, size_t len)
{
membuf_consume(&rbuf->m, len);
}
/**
* rbuf_len - helper to determine how many bytes in rbuf
* @rbuf: the struct rbuf
*/
static inline size_t rbuf_len(const struct rbuf *rbuf)
{
return membuf_num_elems(&rbuf->m);
}
/**
* rbuf_start - helper to get pointert to unconsumed bytes in rbuf
* @rbuf: the struct rbuf
*/
static inline char *rbuf_start(const struct rbuf *rbuf)
{ {
buf->len -= len; return membuf_elems(&rbuf->m);
buf->start += len;
} }
/** /**
* rbuf_fill_all - read rest of file into a buffer. * rbuf_fill_all - read rest of file into a buffer.
* @buf: the struct rbuf * @rbuf: the struct rbuf
* @resize: the call to resize the buffer.
* *
* If @resize is needed and is NULL, or returns false, rbuf_read_all * If a read or @expandfn fails then NULL returned, otherwise returns
* will return NULL (with errno set to ENOMEM). If a read fails, * @rbuf->start.
* then NULL is also returned, otherwise returns @buf->start.
* *
* Example: * Example:
* if (!rbuf_fill_all(&in, realloc)) { * if (!rbuf_fill_all(&in))
* if (errno) * err(1, "reading foo");
* err(1, "reading foo");
* }
*/ */
void *rbuf_fill_all(struct rbuf *rbuf, void *(*resize)(void *buf, size_t len)); void *rbuf_fill_all(struct rbuf *rbuf);
/** /**
* rbuf_read_str - fill into a buffer up to a terminator, and consume string. * rbuf_read_str - fill into a buffer up to a terminator, and consume string.
* @buf: the struct rbuf * @rbuf: the struct rbuf
* @term: the character to terminate the read. * @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 * If a read or @expandfn fails, then NULL is returned, otherwise the next
* will return NULL (with errno set to ENOMEM). If a read fails, * string. It replaces the terminator @term (if any) with NUL, otherwise NUL
* 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 * 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 * because the nul terminator will be at rbuf_start(@rbuf) (normally it will be
* be at @buf->start - 1). * at rbuf_start(@rbuf) - 1).
* *
* If there is nothing remaining to be read, NULL is returned with * 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 * errno set to 0, unless @term is NUL, in which case it returns the
...@@ -140,7 +145,7 @@ void *rbuf_fill_all(struct rbuf *rbuf, void *(*resize)(void *buf, size_t len)); ...@@ -140,7 +145,7 @@ void *rbuf_fill_all(struct rbuf *rbuf, void *(*resize)(void *buf, size_t len));
* Example: * Example:
* char *line; * char *line;
* *
* line = rbuf_read_str(&in, '\n', realloc); * line = rbuf_read_str(&in, '\n');
* if (!line) { * if (!line) {
* if (errno) * if (errno)
* err(1, "reading foo"); * err(1, "reading foo");
...@@ -150,7 +155,20 @@ void *rbuf_fill_all(struct rbuf *rbuf, void *(*resize)(void *buf, size_t len)); ...@@ -150,7 +155,20 @@ void *rbuf_fill_all(struct rbuf *rbuf, void *(*resize)(void *buf, size_t len));
* printf("First line is %s\n", line); * printf("First line is %s\n", line);
* *
*/ */
char *rbuf_read_str(struct rbuf *rbuf, char term, char *rbuf_read_str(struct rbuf *rbuf, char term);
void *(*resize)(void *buf, size_t len));
/**
* rbuf_cleanup - reset rbuf, return buffer for freeing.
* @rbuf: the struct rbuf
*
* The rbuf will be empty after this, and crash if you try to use it.
* You can rbuf_init() it again, however.
*
* Example:
* free(rbuf_cleanup(&in));
*/
static inline char *rbuf_cleanup(struct rbuf *rbuf)
{
return membuf_cleanup(&rbuf->m);
}
#endif /* CCAN_RBUF_H */ #endif /* CCAN_RBUF_H */
...@@ -7,6 +7,14 @@ ...@@ -7,6 +7,14 @@
#include <fcntl.h> #include <fcntl.h>
#include <stdlib.h> #include <stdlib.h>
static bool test_realloc_fail;
static void *test_realloc(struct membuf *mb, void *buf, size_t n)
{
if (test_realloc_fail)
return NULL;
return realloc(buf, n);
}
int main(void) int main(void)
{ {
struct rbuf in; struct rbuf in;
...@@ -24,25 +32,29 @@ int main(void) ...@@ -24,25 +32,29 @@ int main(void)
} }
close(fd); close(fd);
ok1(rbuf_open(&in, "run-all-file", NULL, 0)); ok1(rbuf_open(&in, "run-all-file", NULL, 0, test_realloc));
/* Can't fill without realloc. */ /* Can't fill if realloc fails. */
ok1(!rbuf_fill(&in, NULL)); test_realloc_fail = true;
ok1(!rbuf_fill(&in));
ok1(errno == ENOMEM); ok1(errno == ENOMEM);
ok1(rbuf_fill(&in, realloc)); test_realloc_fail = false;
ok1(rbuf_fill(&in));
/* But can't load in whole file. */ /* But can't load in whole file. */
ok1(!rbuf_fill_all(&in, NULL)); test_realloc_fail = true;
ok1(!rbuf_fill_all(&in));
ok1(errno == ENOMEM); ok1(errno == ENOMEM);
ok1(rbuf_fill_all(&in, realloc)); test_realloc_fail = false;
ok1(in.len == size); ok1(rbuf_fill_all(&in));
ok1(rbuf_len(&in) == size);
for (i = 0; i * sizeof(buf) < size; i++) { for (i = 0; i * sizeof(buf) < size; i++) {
memset(buf, 0x42 + i, sizeof(buf)); memset(buf, 0x42 + i, sizeof(buf));
if (memcmp(buf, in.start, sizeof(buf)) != 0) { if (memcmp(buf, rbuf_start(&in), sizeof(buf)) != 0) {
fail("Bad buffer contents"); fail("Bad buffer contents");
break; break;
} }
rbuf_consume(&in, sizeof(buf)); rbuf_consume(&in, sizeof(buf));
} }
free(in.buf); free(membuf_cleanup(&in.m));
/* This exits depending on whether all tests passed */ /* This exits depending on whether all tests passed */
return exit_status(); return exit_status();
......
...@@ -14,9 +14,9 @@ int main(void) ...@@ -14,9 +14,9 @@ int main(void)
/* This is how many tests you plan to run */ /* This is how many tests you plan to run */
plan_tests(5); plan_tests(5);
ok1(!rbuf_open(&in, "nonexistent-file", NULL, 0)); ok1(!rbuf_open(&in, "nonexistent-file", NULL, 0, NULL));
ok1(errno == ENOENT); ok1(errno == ENOENT);
ok1(rbuf_open(&in, "test/run-open.c", NULL, 0)); ok1(rbuf_open(&in, "test/run-open.c", NULL, 0, NULL));
ok1(close(in.fd) == 0); ok1(close(in.fd) == 0);
/* If this fails to stat, it should fall back */ /* If this fails to stat, it should fall back */
ok1(rbuf_good_size(in.fd) == 4096); ok1(rbuf_good_size(in.fd) == 4096);
......
...@@ -27,7 +27,7 @@ int main(void) ...@@ -27,7 +27,7 @@ int main(void)
int i, fd = open("test/run.c", O_RDONLY); int i, fd = open("test/run.c", O_RDONLY);
/* This is how many tests you plan to run */ /* This is how many tests you plan to run */
plan_tests(140); plan_tests(160);
/* Grab ourselves for comparison. */ /* Grab ourselves for comparison. */
buf[full_read(fd, buf, sizeof(buf))] = '\0'; buf[full_read(fd, buf, sizeof(buf))] = '\0';
...@@ -41,26 +41,26 @@ int main(void) ...@@ -41,26 +41,26 @@ int main(void)
} }
lines[i] = NULL; lines[i] = NULL;
rbuf_init(&in, fd, malloc(31), 31); rbuf_init(&in, fd, malloc(31), 31, membuf_realloc);
ok1(in.fd == fd); ok1(in.fd == fd);
ok1(in.buf_end - in.buf == 31); ok1(membuf_num_space(&in.m) == 31);
p = rbuf_read_str(&in, '\n', NULL); p = rbuf_read_str(&in, '\n');
ok1(p); ok1(p);
ok1(strcmp(p, lines[0]) == 0); ok1(strcmp(p, lines[0]) == 0);
p = rbuf_read_str(&in, '\n', realloc); p = rbuf_read_str(&in, '\n');
ok1(p); ok1(p);
ok1(strcmp(p, lines[1]) == 0); ok1(strcmp(p, lines[1]) == 0);
for (i = 2; lines[i]; i++) { for (i = 2; lines[i]; i++) {
ok1(p = rbuf_read_str(&in, '\n', realloc)); ok1(p = rbuf_read_str(&in, '\n'));
ok1(strcmp(p, lines[i]) == 0); ok1(strcmp(p, lines[i]) == 0);
} }
p = rbuf_read_str(&in, '\n', realloc); p = rbuf_read_str(&in, '\n');
ok1(errno == 0); ok1(errno == 0);
ok1(p == NULL); ok1(p == NULL);
free(in.buf); free(membuf_cleanup(&in.m));
/* This exits depending on whether all tests passed */ /* This exits depending on whether all tests passed */
return exit_status(); return exit_status();
......
...@@ -7,6 +7,14 @@ ...@@ -7,6 +7,14 @@
#include <fcntl.h> #include <fcntl.h>
#include <stdlib.h> #include <stdlib.h>
static bool test_realloc_fail;
static void *test_realloc(struct membuf *mb, void *buf, size_t n)
{
if (test_realloc_fail)
return NULL;
return realloc(buf, n);
}
int main(void) int main(void)
{ {
struct rbuf in; struct rbuf in;
...@@ -14,7 +22,7 @@ int main(void) ...@@ -14,7 +22,7 @@ int main(void)
int fd = open("test/run-term-eof.c", O_RDONLY), len; int fd = open("test/run-term-eof.c", O_RDONLY), len;
/* This is how many tests you plan to run */ /* This is how many tests you plan to run */
plan_tests(6); plan_tests(10);
/* Grab ourselves for comparison. */ /* Grab ourselves for comparison. */
len = read(fd, buf, sizeof(buf)); len = read(fd, buf, sizeof(buf));
...@@ -22,23 +30,35 @@ int main(void) ...@@ -22,23 +30,35 @@ int main(void)
lseek(fd, SEEK_SET, 0); lseek(fd, SEEK_SET, 0);
/* We have exact-size buffer, which causes problems adding term. */ /* We have exact-size buffer, which causes problems adding term. */
rbuf_init(&in, fd, malloc(len), len); rbuf_init(&in, fd, malloc(len), len, test_realloc);
p = rbuf_read_str(&in, 64, NULL); /* At symbol does not appear. */ test_realloc_fail = true;
p = rbuf_read_str(&in, 64); /* At symbol does not appear. */
ok1(errno == ENOMEM); ok1(errno == ENOMEM);
ok1(!p); ok1(!p);
/* This should succeed... */ /* This should succeed... */
p = rbuf_read_str(&in, 64, realloc); test_realloc_fail = false;
p = rbuf_read_str(&in, 64);
ok1(p); ok1(p);
ok1(strcmp(p, buf) == 0); ok1(strcmp(p, buf) == 0);
free(in.buf); ok1(rbuf_start(&in) == p + strlen(p));
free(rbuf_cleanup(&in));
/* Try again. */ /* Try again. */
lseek(fd, SEEK_SET, 0); lseek(fd, SEEK_SET, 0);
rbuf_init(&in, fd, malloc(len), len); rbuf_init(&in, fd, malloc(len), len, test_realloc);
p = rbuf_read_str(&in, 64, realloc); p = rbuf_read_str(&in, 64);
ok1(p); ok1(p);
ok1(strcmp(p, buf) == 0); ok1(strcmp(p, buf) == 0);
free(in.buf); ok1(rbuf_start(&in) == p + strlen(p));
free(rbuf_cleanup(&in));
/* Normal case, we get rbuf_start after nul */
lseek(fd, SEEK_SET, 0);
rbuf_init(&in, fd, NULL, 0, test_realloc);
p = rbuf_read_str(&in, '^');
ok1(p);
ok1(rbuf_start(&in) == p + strlen(p) + 1);
free(rbuf_cleanup(&in));
return exit_status(); return exit_status();
} }
...@@ -7,6 +7,14 @@ ...@@ -7,6 +7,14 @@
#include <fcntl.h> #include <fcntl.h>
#include <stdlib.h> #include <stdlib.h>
static bool test_realloc_fail;
static void *test_realloc(struct membuf *mb, void *buf, size_t n)
{
if (test_realloc_fail)
return NULL;
return realloc(buf, n);
}
int main(void) int main(void)
{ {
struct rbuf in; struct rbuf in;
...@@ -15,7 +23,7 @@ int main(void) ...@@ -15,7 +23,7 @@ int main(void)
int i, fd = open("test/run.c", O_RDONLY), len; int i, fd = open("test/run.c", O_RDONLY), len;
/* This is how many tests you plan to run */ /* This is how many tests you plan to run */
plan_tests(144); plan_tests(164);
/* Grab ourselves for comparison. */ /* Grab ourselves for comparison. */
len = read(fd, buf, sizeof(buf)); len = read(fd, buf, sizeof(buf));
...@@ -30,39 +38,41 @@ int main(void) ...@@ -30,39 +38,41 @@ int main(void)
} }
lines[i] = NULL; lines[i] = NULL;
rbuf_init(&in, fd, malloc(31), 31); rbuf_init(&in, fd, malloc(31), 31, test_realloc);
ok1(in.fd == fd); ok1(in.fd == fd);
ok1(in.buf_end - in.buf == 31); ok1(membuf_num_space(&in.m) == 31);
p = rbuf_read_str(&in, '\n', NULL); test_realloc_fail = true;
p = rbuf_read_str(&in, '\n');
ok1(p); ok1(p);
ok1(strcmp(p, lines[0]) == 0); ok1(strcmp(p, lines[0]) == 0);
p = rbuf_read_str(&in, '\n', realloc); test_realloc_fail = false;
p = rbuf_read_str(&in, '\n');
ok1(p); ok1(p);
ok1(strcmp(p, lines[1]) == 0); ok1(strcmp(p, lines[1]) == 0);
for (i = 2; lines[i]; i++) { for (i = 2; lines[i]; i++) {
ok1(p = rbuf_read_str(&in, '\n', realloc)); ok1(p = rbuf_read_str(&in, '\n'));
ok1(strcmp(p, lines[i]) == 0); ok1(strcmp(p, lines[i]) == 0);
} }
p = rbuf_read_str(&in, '\n', realloc); p = rbuf_read_str(&in, '\n');
ok1(errno == 0); ok1(errno == 0);
ok1(p == NULL); ok1(p == NULL);
free(in.buf); free(rbuf_cleanup(&in));
/* Another way of reading the entire (text) file. */ /* Another way of reading the entire (text) file. */
lseek(fd, SEEK_SET, 0); lseek(fd, SEEK_SET, 0);
rbuf_init(&in, fd, NULL, 0); rbuf_init(&in, fd, NULL, 0, test_realloc);
p = rbuf_read_str(&in, 0, realloc); p = rbuf_read_str(&in, 0);
ok1(p); ok1(p);
ok1(strlen(p) == len); ok1(strlen(p) == len);
close(fd); close(fd);
p = rbuf_read_str(&in, 0, realloc); p = rbuf_read_str(&in, 0);
ok1(errno == EBADF); ok1(errno == EBADF);
ok1(!p); ok1(!p);
free(in.buf); free(rbuf_cleanup(&in));
return exit_status(); return exit_status();
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment