about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--btpd/cli_if.c86
-rw-r--r--btpd/tlib.c6
-rw-r--r--cli/add.c12
-rw-r--r--misc/btpd_if.c66
-rw-r--r--misc/iobuf.c77
-rw-r--r--misc/iobuf.h21
6 files changed, 149 insertions, 119 deletions
diff --git a/btpd/cli_if.c b/btpd/cli_if.c
index 50e9d7a..100c6b4 100644
--- a/btpd/cli_if.c
+++ b/btpd/cli_if.c
@@ -11,7 +11,7 @@ struct cli {
 static struct event m_cli_incoming;
 
 static int
-write_buffer(struct cli *cli, struct io_buffer *iob)
+write_buffer(struct cli *cli, struct iobuf *iob)
 {
     int err = 0;
     if (!iob->error) {
@@ -20,107 +20,107 @@ write_buffer(struct cli *cli, struct io_buffer *iob)
         err = write_fully(cli->sd, iob->buf, iob->off);
     } else
         btpd_err("Out of memory.\n");
-    buf_free(iob);
+    iobuf_free(iob);
     return err;
 }
 
 static int
 write_code_buffer(struct cli *cli, enum ipc_err code)
 {
-    struct io_buffer iob = buf_init(16);
-    buf_print(&iob, "d4:codei%uee", code);
+    struct iobuf iob = iobuf_init(16);
+    iobuf_print(&iob, "d4:codei%uee", code);
     return write_buffer(cli, &iob);
 }
 
 static int
 write_add_buffer(struct cli *cli, unsigned num)
 {
-    struct io_buffer iob = buf_init(32);
-    buf_print(&iob, "d4:codei%ue3:numi%uee", IPC_OK, num);
+    struct iobuf iob = iobuf_init(32);
+    iobuf_print(&iob, "d4:codei%ue3:numi%uee", IPC_OK, num);
     return write_buffer(cli, &iob);
 }
 
 static void
-write_ans(struct io_buffer *iob, struct tlib *tl, enum ipc_tval val)
+write_ans(struct iobuf *iob, struct tlib *tl, enum ipc_tval val)
 {
     enum ipc_tstate ts = IPC_TSTATE_INACTIVE;
     switch (val) {
     case IPC_TVAL_CGOT:
-        buf_print(iob, "i%dei%llde", IPC_TYPE_NUM,
+        iobuf_print(iob, "i%dei%llde", IPC_TYPE_NUM,
             tl->tp == NULL ? tl->content_have : (long long)cm_content(tl->tp));
         return;
     case IPC_TVAL_CSIZE:
-        buf_print(iob, "i%dei%llde", IPC_TYPE_NUM,
+        iobuf_print(iob, "i%dei%llde", IPC_TYPE_NUM,
             (long long)tl->content_size);
         return;
     case IPC_TVAL_PCCOUNT:
         if (tl->tp == NULL)
-            buf_print(iob, "i%dei%de", IPC_TYPE_ERR, IPC_ETINACTIVE);
+            iobuf_print(iob, "i%dei%de", IPC_TYPE_ERR, IPC_ETINACTIVE);
         else
-            buf_print(iob, "i%dei%lue", IPC_TYPE_NUM,
+            iobuf_print(iob, "i%dei%lue", IPC_TYPE_NUM,
                 (unsigned long)tl->tp->npieces);
         return;
     case IPC_TVAL_PCGOT:
         if (tl->tp == NULL)
-            buf_print(iob, "i%dei%de", IPC_TYPE_ERR, IPC_ETINACTIVE);
+            iobuf_print(iob, "i%dei%de", IPC_TYPE_ERR, IPC_ETINACTIVE);
         else
-            buf_print(iob, "i%dei%lue", IPC_TYPE_NUM,
+            iobuf_print(iob, "i%dei%lue", IPC_TYPE_NUM,
                 (unsigned long)cm_pieces(tl->tp));
         return;
     case IPC_TVAL_PCSEEN:
         if (tl->tp == NULL)
-            buf_print(iob, "i%dei%de", IPC_TYPE_NUM, 0);
+            iobuf_print(iob, "i%dei%de", IPC_TYPE_NUM, 0);
         else {
             unsigned long pcseen = 0;
             for (unsigned long i = 0; i < tl->tp->npieces; i++)
                 if (tl->tp->net->piece_count[i] > 0)
                     pcseen++;
-            buf_print(iob, "i%dei%lue", IPC_TYPE_NUM, pcseen);
+            iobuf_print(iob, "i%dei%lue", IPC_TYPE_NUM, pcseen);
         }
         return;
     case IPC_TVAL_RATEDWN:
-        buf_print(iob, "i%dei%lue", IPC_TYPE_NUM,
+        iobuf_print(iob, "i%dei%lue", IPC_TYPE_NUM,
             tl->tp == NULL ? 0UL : tl->tp->net->rate_dwn / RATEHISTORY);
         return;
     case IPC_TVAL_RATEUP:
-        buf_print(iob, "i%dei%lue", IPC_TYPE_NUM,
+        iobuf_print(iob, "i%dei%lue", IPC_TYPE_NUM,
             tl->tp == NULL ? 0UL : tl->tp->net->rate_up / RATEHISTORY);
         return;
     case IPC_TVAL_SESSDWN:
-        buf_print(iob, "i%dei%llde", IPC_TYPE_NUM,
+        iobuf_print(iob, "i%dei%llde", IPC_TYPE_NUM,
             tl->tp == NULL ? 0LL : tl->tp->net->downloaded);
         return;
     case IPC_TVAL_SESSUP:
-        buf_print(iob, "i%dei%llde", IPC_TYPE_NUM,
+        iobuf_print(iob, "i%dei%llde", IPC_TYPE_NUM,
             tl->tp == NULL ? 0LL : tl->tp->net->uploaded);
         return;
     case IPC_TVAL_DIR:
         if (tl->dir != NULL)
-            buf_print(iob, "i%de%d:%s", IPC_TYPE_STR, (int)strlen(tl->dir),
+            iobuf_print(iob, "i%de%d:%s", IPC_TYPE_STR, (int)strlen(tl->dir),
                 tl->dir);
         else
-            buf_print(iob, "i%dei%de", IPC_TYPE_ERR, IPC_EBADTENT);
+            iobuf_print(iob, "i%dei%de", IPC_TYPE_ERR, IPC_EBADTENT);
         return;
     case IPC_TVAL_NAME:
         if (tl->name != NULL)
-            buf_print(iob, "i%de%d:%s", IPC_TYPE_STR, (int)strlen(tl->name),
+            iobuf_print(iob, "i%de%d:%s", IPC_TYPE_STR, (int)strlen(tl->name),
                 tl->name);
         else
-            buf_print(iob, "i%dei%de", IPC_TYPE_ERR, IPC_EBADTENT);
+            iobuf_print(iob, "i%dei%de", IPC_TYPE_ERR, IPC_EBADTENT);
         return;
     case IPC_TVAL_IHASH:
-        buf_print(iob, "i%de20:", IPC_TYPE_BIN);
-        buf_write(iob, tl->hash, 20);
+        iobuf_print(iob, "i%de20:", IPC_TYPE_BIN);
+        iobuf_write(iob, tl->hash, 20);
         return;
     case IPC_TVAL_NUM:
-        buf_print(iob, "i%dei%ue", IPC_TYPE_NUM, tl->num);
+        iobuf_print(iob, "i%dei%ue", IPC_TYPE_NUM, tl->num);
         return;
     case IPC_TVAL_PCOUNT:
-        buf_print(iob, "i%dei%ue", IPC_TYPE_NUM,
+        iobuf_print(iob, "i%dei%ue", IPC_TYPE_NUM,
             tl->tp == NULL ? 0 : tl->tp->net->npeers);
         return;
     case IPC_TVAL_STATE:
-        buf_print(iob, "i%de", IPC_TYPE_NUM);
+        iobuf_print(iob, "i%de", IPC_TYPE_NUM);
         if (tl->tp != NULL) {
             switch (tl->tp->state) {
             case T_STARTING:
@@ -137,24 +137,24 @@ write_ans(struct io_buffer *iob, struct tlib *tl, enum ipc_tval val)
                 break;
             }
         }
-        buf_print(iob, "i%de", ts);
+        iobuf_print(iob, "i%de", ts);
         return;
     case IPC_TVAL_TOTDWN:
-        buf_print(iob, "i%dei%llde", IPC_TYPE_NUM, tl->tot_down +
+        iobuf_print(iob, "i%dei%llde", IPC_TYPE_NUM, tl->tot_down +
             (tl->tp == NULL ? 0 : tl->tp->net->downloaded));
         return;
     case IPC_TVAL_TOTUP:
-        buf_print(iob, "i%dei%llde", IPC_TYPE_NUM, tl->tot_up +
+        iobuf_print(iob, "i%dei%llde", IPC_TYPE_NUM, tl->tot_up +
             (tl->tp == NULL ? 0 : tl->tp->net->uploaded));
         return;
     case IPC_TVAL_TRERR:
-        buf_print(iob, "i%dei%ue", IPC_TYPE_NUM,
+        iobuf_print(iob, "i%dei%ue", IPC_TYPE_NUM,
             tl->tp == NULL ? 0 : tr_errors(tl->tp));
         return;
     case IPC_TVALCOUNT:
         break;
     }
-    buf_print(iob, "i%dei%de", IPC_TYPE_ERR, IPC_ENOKEY);
+    iobuf_print(iob, "i%dei%de", IPC_TYPE_ERR, IPC_ENOKEY);
 }
 
 static int
@@ -166,7 +166,7 @@ cmd_tget(struct cli *cli, int argc, const char *args)
     size_t nkeys;
     const char *keys, *p;
     enum ipc_tval *opts;
-    struct io_buffer iob;
+    struct iobuf iob;
 
     if ((keys = benc_dget_lst(args, "keys")) == NULL)
         return IPC_COMMERR;
@@ -178,8 +178,8 @@ cmd_tget(struct cli *cli, int argc, const char *args)
     for (int i = 0; i < nkeys; i++)
         opts[i] = benc_int(p, &p);
 
-    iob = buf_init(1 << 15);
-    buf_swrite(&iob, "d4:codei0e6:resultl");
+    iob = iobuf_init(1 << 15);
+    iobuf_swrite(&iob, "d4:codei0e6:resultl");
     p = benc_dget_any(args, "from");
     if (benc_isint(p)) {
         enum ipc_twc from = benc_int(p, NULL);
@@ -189,10 +189,10 @@ cmd_tget(struct cli *cli, int argc, const char *args)
             if ((from == IPC_TWC_ALL ||
                     (tlv[i]->tp == NULL && from == IPC_TWC_INACTIVE) ||
                     (tlv[i]->tp != NULL && from == IPC_TWC_ACTIVE))) {
-                buf_swrite(&iob, "l");
+                iobuf_swrite(&iob, "l");
                 for (int k = 0; k < nkeys; k++)
                     write_ans(&iob, tlv[i], opts[k]);
-                buf_swrite(&iob, "e");
+                iobuf_swrite(&iob, "e");
             }
         }
     } else if (benc_islst(p)) {
@@ -203,20 +203,20 @@ cmd_tget(struct cli *cli, int argc, const char *args)
             else if (benc_isstr(p) && benc_strlen(p) == 20)
                 tl = tlib_by_hash(benc_mem(p, NULL, NULL));
             else {
-                buf_free(&iob);
+                iobuf_free(&iob);
                 free(opts);
                 return IPC_COMMERR;
             }
             if (tl != NULL) {
-                buf_swrite(&iob, "l");
+                iobuf_swrite(&iob, "l");
                 for (int i = 0; i < nkeys; i++)
                     write_ans(&iob, tl, opts[i]);
-                buf_swrite(&iob, "e");
+                iobuf_swrite(&iob, "e");
             } else
-                buf_print(&iob, "i%de", IPC_ENOTENT);
+                iobuf_print(&iob, "i%de", IPC_ENOTENT);
         }
     }
-    buf_swrite(&iob, "ee");
+    iobuf_swrite(&iob, "ee");
     free(opts);
     return write_buffer(cli, &iob);
 }
diff --git a/btpd/tlib.c b/btpd/tlib.c
index 9a2c3af..362139d 100644
--- a/btpd/tlib.c
+++ b/btpd/tlib.c
@@ -156,9 +156,9 @@ save_info(struct tlib *tl)
 {
     FILE *fp;
     char relpath[SHAHEXSIZE], path[PATH_MAX], wpath[PATH_MAX];
-    struct io_buffer iob = buf_init(1 << 10);
+    struct iobuf iob = iobuf_init(1 << 10);
 
-    buf_print(&iob,
+    iobuf_print(&iob,
         "d4:infod"
         "12:content havei%llde12:content sizei%llde"
         "3:dir%d:%s4:name%d:%s"
@@ -177,7 +177,7 @@ save_info(struct tlib *tl)
     if ((fp = fopen(wpath, "w")) == NULL)
         btpd_err("failed to open '%s' (%s).\n", wpath, strerror(errno));
     dct_subst_save(fp, "de", iob.buf);
-    buf_free(&iob);
+    iobuf_free(&iob);
     if ((fflush(fp) == EOF || fsync(fileno(fp)) != 0
             || ferror(fp) || fclose(fp) != 0))
         btpd_err("failed to write '%s'.\n", wpath);
diff --git a/cli/add.c b/cli/add.c
index 4f55b9d..c91bf41 100644
--- a/cli/add.c
+++ b/cli/add.c
@@ -74,21 +74,21 @@ cmd_add(int argc, char **argv)
     size_t mi_size;
     enum ipc_err code;
     char dpath[PATH_MAX];
-    struct io_buffer iob;
+    struct iobuf iob;
 
     if ((mi = mi_load(argv[0], &mi_size)) == NULL)
         err(1, "error loading '%s'", argv[0]);
 
-    iob = buf_init(PATH_MAX);
-    buf_write(&iob, dir, dirlen);
+    iob = iobuf_init(PATH_MAX);
+    iobuf_write(&iob, dir, dirlen);
     if (topdir && !mi_simple(mi)) {
         size_t tdlen;
         const char *td =
             benc_dget_mem(benc_dget_dct(mi, "info"), "name", &tdlen);
-        buf_swrite(&iob, "/");
-        buf_write(&iob, td, tdlen);
+        iobuf_swrite(&iob, "/");
+        iobuf_write(&iob, td, tdlen);
     }
-    buf_swrite(&iob, "\0");
+    iobuf_swrite(&iob, "\0");
     if ((errno = make_abs_path(iob.buf, dpath)) != 0)
         err(1, "make_abs_path '%s'", dpath);
     code = btpd_add(ipc, mi, mi_size, dpath, name);
diff --git a/misc/btpd_if.c b/misc/btpd_if.c
index 6705963..0d43924 100644
--- a/misc/btpd_if.c
+++ b/misc/btpd_if.c
@@ -129,7 +129,7 @@ ipc_req_res(struct ipc *ipc, const char *req, uint32_t qlen, char **res,
 }
 
 static enum ipc_err
-ipc_buf_req_res(struct ipc *ipc, struct io_buffer *iob, char **res,
+ipc_buf_req_res(struct ipc *ipc, struct iobuf *iob, char **res,
     uint32_t *rlen)
 {
     enum ipc_err err;
@@ -137,12 +137,12 @@ ipc_buf_req_res(struct ipc *ipc, struct io_buffer *iob, char **res,
         err = IPC_COMMERR;
     else
         err = ipc_req_res(ipc, iob->buf, iob->off, res, rlen);
-    buf_free(iob);
+    iobuf_free(iob);
     return err;
 }
 
 static enum ipc_err
-ipc_buf_req_code(struct ipc *ipc, struct io_buffer *iob)
+ipc_buf_req_code(struct ipc *ipc, struct iobuf *iob)
 {
     enum ipc_err err;
     char *res;
@@ -158,11 +158,11 @@ ipc_buf_req_code(struct ipc *ipc, struct io_buffer *iob)
 enum ipc_err
 btpd_die(struct ipc *ipc, int seconds)
 {
-    struct io_buffer iob = buf_init(16);
+    struct iobuf iob = iobuf_init(16);
     if (seconds >= 0)
-        buf_print(&iob, "l3:diei%dee", seconds);
+        iobuf_print(&iob, "l3:diei%dee", seconds);
     else
-        buf_swrite(&iob, "l3:diee");
+        iobuf_swrite(&iob, "l3:diee");
     return ipc_buf_req_code(ipc, &iob);
 }
 
@@ -219,24 +219,24 @@ btpd_tget(struct ipc *ipc, struct ipc_torrent *tps, size_t ntps,
     char *res;
     uint32_t rlen;
     enum ipc_err err;
-    struct io_buffer iob;
+    struct iobuf iob;
 
     if (nkeys == 0 || ntps == 0)
         return IPC_COMMERR;
 
-    iob = buf_init(1 << 14);
-    buf_swrite(&iob, "l4:tgetd4:froml");
+    iob = iobuf_init(1 << 14);
+    iobuf_swrite(&iob, "l4:tgetd4:froml");
     for (int i = 0; i < ntps; i++) {
         if (tps[i].by_hash) {
-            buf_swrite(&iob, "20:");
-            buf_write(&iob, tps[i].u.hash, 20);
+            iobuf_swrite(&iob, "20:");
+            iobuf_write(&iob, tps[i].u.hash, 20);
         } else
-            buf_print(&iob, "i%ue", tps[i].u.num);
+            iobuf_print(&iob, "i%ue", tps[i].u.num);
     }
-    buf_swrite(&iob, "e4:keysl");
+    iobuf_swrite(&iob, "e4:keysl");
     for (int k = 0; k < nkeys; k++)
-        buf_print(&iob, "i%de", keys[k]);
-    buf_swrite(&iob, "eee");
+        iobuf_print(&iob, "i%de", keys[k]);
+    iobuf_swrite(&iob, "eee");
 
     if ((err = ipc_buf_req_res(ipc, &iob, &res, &rlen)) == 0)
         err = tget_common(res, keys, nkeys, cb, arg);
@@ -249,17 +249,17 @@ btpd_tget_wc(struct ipc *ipc, enum ipc_twc twc, enum ipc_tval *keys,
 {
     char *res;
     uint32_t rlen;
-    struct io_buffer iob;
+    struct iobuf iob;
     enum ipc_err err;
 
     if (nkeys == 0)
         return IPC_COMMERR;
 
-    iob = buf_init(1 << 14);
-    buf_print(&iob, "l4:tgetd4:fromi%de4:keysl", twc);
+    iob = iobuf_init(1 << 14);
+    iobuf_print(&iob, "l4:tgetd4:fromi%de4:keysl", twc);
     for (int i = 0; i < nkeys; i++)
-        buf_print(&iob, "i%de", keys[i]);
-    buf_swrite(&iob, "eee");
+        iobuf_print(&iob, "i%de", keys[i]);
+    iobuf_swrite(&iob, "eee");
 
     if ((err = ipc_buf_req_res(ipc, &iob, &res, &rlen)) == 0)
         err = tget_common(res, keys, nkeys, cb, arg);
@@ -270,27 +270,27 @@ enum ipc_err
 btpd_add(struct ipc *ipc, const char *mi, size_t mi_size, const char *content,
     const char *name)
 {
-    struct io_buffer iob = buf_init(1 << 10);
-    buf_print(&iob, "l3:addd7:content%d:%s", (int)strlen(content),
+    struct iobuf iob = iobuf_init(1 << 10);
+    iobuf_print(&iob, "l3:addd7:content%d:%s", (int)strlen(content),
         content);
     if (name != NULL)
-        buf_print(&iob, "4:name%d:%s", (int)strlen(name), name);
-    buf_print(&iob, "7:torrent%lu:", (unsigned long)mi_size);
-    buf_write(&iob, mi, mi_size);
-    buf_swrite(&iob, "ee");
+        iobuf_print(&iob, "4:name%d:%s", (int)strlen(name), name);
+    iobuf_print(&iob, "7:torrent%lu:", (unsigned long)mi_size);
+    iobuf_write(&iob, mi, mi_size);
+    iobuf_swrite(&iob, "ee");
     return ipc_buf_req_code(ipc, &iob);
 }
 
 static enum ipc_err
 simple_treq(struct ipc *ipc, char *cmd, struct ipc_torrent *tp)
 {
-    struct io_buffer iob = buf_init(32);
+    struct iobuf iob = iobuf_init(32);
     if (tp->by_hash) {
-        buf_print(&iob, "l%d:%s20:", (int)strlen(cmd), cmd);
-        buf_write(&iob, tp->u.hash, 20);
-        buf_swrite(&iob, "e");
+        iobuf_print(&iob, "l%d:%s20:", (int)strlen(cmd), cmd);
+        iobuf_write(&iob, tp->u.hash, 20);
+        iobuf_swrite(&iob, "e");
     } else
-        buf_print(&iob, "l%d:%si%uee", (int)strlen(cmd), cmd, tp->u.num);
+        iobuf_print(&iob, "l%d:%si%uee", (int)strlen(cmd), cmd, tp->u.num);
     return ipc_buf_req_code(ipc, &iob);
 }
 
@@ -315,7 +315,7 @@ btpd_stop(struct ipc *ipc, struct ipc_torrent *tp)
 enum ipc_err
 btpd_stop_all(struct ipc *ipc)
 {
-    struct io_buffer iob = buf_init(16);
-    buf_swrite(&iob, "l8:stop-alle");
+    struct iobuf iob = iobuf_init(16);
+    iobuf_swrite(&iob, "l8:stop-alle");
     return ipc_buf_req_code(ipc, &iob);
 }
diff --git a/misc/iobuf.c b/misc/iobuf.c
index 45918ab..fa5e33f 100644
--- a/misc/iobuf.c
+++ b/misc/iobuf.c
@@ -1,3 +1,4 @@
+#include <assert.h>
 #include <inttypes.h>
 #include <stdarg.h>
 #include <stdio.h>
@@ -5,44 +6,66 @@
 #include <string.h>
 
 #include "iobuf.h"
+#include "subr.h"
 
-#define GROWLEN (1 << 14)
-
-struct io_buffer
-buf_init(size_t size)
+struct iobuf
+iobuf_init(size_t size)
 {
-    struct io_buffer iob;
+    struct iobuf iob;
+    iob.size = size;
     iob.off = 0;
-    iob.len = size;
+    iob.skip = 0;
     iob.error = (iob.buf = malloc(size)) == NULL ? 1 : 0;
     return iob;
 }
 
 void
-buf_free(struct io_buffer *iob)
+iobuf_free(struct iobuf *iob)
 {
     if (iob->buf != NULL)
-        free(iob->buf);
+        free(iob->buf - iob->skip);
+    iob->buf = NULL;
+    iob->error = 1;
+}
+
+void
+iobuf_consumed(struct iobuf *iob, size_t count)
+{
+    if (iob->error)
+        return;
+    assert(count <= iob->off);
+    iob->skip += count;
+    iob->buf += count;
+    iob->off -= count;
 }
 
 int
-buf_grow(struct io_buffer *iob, size_t addlen)
+iobuf_accommodate(struct iobuf *iob, size_t count)
 {
     if (iob->error)
-        return iob->error;
-    char *nbuf = realloc(iob->buf, iob->len + addlen);
-    if (nbuf == NULL) {
-        iob->error = 1;
         return 0;
+    size_t esize = iob->size - (iob->skip + iob->off);
+    if (esize >= count)
+        return 1;
+    else if (esize + iob->skip >= count) {
+        bcopy(iob->buf, iob->buf - iob->skip, iob->off);
+        iob->buf -= iob->skip;
+        iob->skip = 0;
+        return 1;
     } else {
-        iob->buf = nbuf;
-        iob->len += addlen;
+        uint8_t *nbuf = realloc(iob->buf - iob->skip, iob->size + count);
+        if (nbuf == NULL) {
+            iob->error = 1;
+            return 0;
+        }
+        iob->buf = nbuf + iob->skip;
+        iob->size += count;
         return 1;
     }
 }
 
 int
-buf_print(struct io_buffer *iob, const char *fmt, ...)
+iobuf_print(struct iobuf *iob, const char *fmt, ...)
 {
     if (iob->error)
         return 0;
@@ -51,9 +74,8 @@ buf_print(struct io_buffer *iob, const char *fmt, ...)
     va_start(ap, fmt);
     np = vsnprintf(NULL, 0, fmt, ap);
     va_end(ap);
-    if (np + 1 > iob->len - iob->off)
-        if (!buf_grow(iob, (1 + (np + 1) / GROWLEN) * GROWLEN))
-            return 0;
+    if (!iobuf_accommodate(iob, np + 1))
+        return 0;
     va_start(ap, fmt);
     vsnprintf(iob->buf + iob->off, np + 1, fmt, ap);
     va_end(ap);
@@ -62,14 +84,19 @@ buf_print(struct io_buffer *iob, const char *fmt, ...)
 }
 
 int
-buf_write(struct io_buffer *iob, const void *buf, size_t len)
+iobuf_write(struct iobuf *iob, const void *buf, size_t count)
 {
     if (iob->error)
         return 0;
-    if (len > iob->len - iob->off)
-        if (!buf_grow(iob, (1 + len / GROWLEN) * GROWLEN))
-            return 0;
-    bcopy(buf, iob->buf + iob->off, len);
-    iob->off += len;
+    if (!iobuf_accommodate(iob, count))
+        return 0;
+    bcopy(buf, iob->buf + iob->off, count);
+    iob->off += count;
     return 1;
 }
+
+void *
+iobuf_find(struct iobuf *iob, const void *p, size_t plen)
+{
+    return iob->error ? NULL : memfind(p, plen, iob->buf, iob->off);
+}
diff --git a/misc/iobuf.h b/misc/iobuf.h
index ccd2028..fb14835 100644
--- a/misc/iobuf.h
+++ b/misc/iobuf.h
@@ -1,20 +1,23 @@
 #ifndef BTPD_IOBUF_H
 #define BTPD_IOBUF_H
 
-struct io_buffer {
+struct iobuf {
+    uint8_t *buf;
+    size_t size;
     size_t off;
-    size_t len;
-    char *buf;
+    size_t skip;
     int error;
 };
 
-struct io_buffer buf_init(size_t size);
-void buf_free(struct io_buffer *iob);
-int buf_grow(struct io_buffer *iob, size_t size);
-int buf_write(struct io_buffer *iob, const void *data, size_t size);
+struct iobuf iobuf_init(size_t size);
+void iobuf_free(struct iobuf *iob);
+int iobuf_accommodate(struct iobuf *iob, size_t size);
+int iobuf_write(struct iobuf *iob, const void *data, size_t size);
 __attribute__((format (printf, 2, 3)))
-int buf_print(struct io_buffer *iob, const char *fmt, ...);
+int iobuf_print(struct iobuf *iob, const char *fmt, ...);
+void *iobuf_find(struct iobuf *iob, const void *p, size_t plen);
+void iobuf_consumed(struct iobuf *iob, size_t count);
 
-#define buf_swrite(iob, s) buf_write(iob, s, sizeof(s) - 1)
+#define iobuf_swrite(iob, s) iobuf_write(iob, s, sizeof(s) - 1)
 
 #endif