From 0e3a2189f65354962fc71af41f5aa52261a782e8 Mon Sep 17 00:00:00 2001 From: Dirk Engling Date: Sun, 25 Oct 2026 22:30:04 +0100 Subject: Replace libowfat's iobatch with our own smaller (and probably only temporary) ot_batch --- ot_batch.c | 158 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 ot_batch.c (limited to 'ot_batch.c') diff --git a/ot_batch.c b/ot_batch.c new file mode 100644 index 0000000..84eb4a8 --- /dev/null +++ b/ot_batch.c @@ -0,0 +1,158 @@ +/* This software was written by Dirk Engling + It is considered beerware. Prost. Skol. Cheers or whatever. + + $id$ */ + +/* System */ +#include +#include +#include +#include +#include +#include +#include + +/* Opentracker */ +#include "ot_batch.h" + +#ifndef IOV_MAX +#define IOV_MAX 1024 +#endif + +#define OT_BATCH_MIN_ENTRIES 16 + +static int batch_room_for(ot_batch *batch, size_t wanted) { + size_t space = batch->space ? batch->space : OT_BATCH_MIN_ENTRIES; + struct iovec *grown; + + if (batch->count + wanted <= batch->space) + return 0; + + /* During a chunked transfer entries are appended while earlier ones are + still going out, so the front of the array frees up as we go. Slide the + rest down before asking for more. */ + if (batch->first && batch->count - batch->first + wanted <= batch->space) { + memmove(batch->entries, batch->entries + batch->first, (batch->count - batch->first) * sizeof(struct iovec)); + batch->count -= batch->first; + batch->first = 0; + return 0; + } + + while (space < batch->count + wanted) + space *= 2; + + if (!(grown = realloc(batch->entries, space * sizeof(struct iovec)))) + return -1; + + batch->entries = grown; + batch->space = space; + return 0; +} + +int batch_add(ot_batch *batch, void *buf, size_t len) { + if (batch_room_for(batch, 1)) { + free(buf); + return -1; + } + + batch->entries[batch->count].iov_base = buf; + batch->entries[batch->count].iov_len = len; + ++batch->count; + return 0; +} + +int batch_add_vector(ot_batch *batch, const struct iovec *entries, size_t count) { + if (batch_room_for(batch, count)) { + while (count--) + free(entries[count].iov_base); + return -1; + } + + memcpy(batch->entries + batch->count, entries, count * sizeof(struct iovec)); + batch->count += count; + return 0; +} + +/* Drop everything the kernel accepted, freeing each buffer as it goes */ +static void batch_consume(ot_batch *batch, size_t written) { + while (written) { + struct iovec *entry = batch->entries + batch->first; + size_t left = entry->iov_len - batch->offset; + + if (written < left) { + batch->offset += written; + return; + } + + free(entry->iov_base); + written -= left; + batch->offset = 0; + ++batch->first; + } + + /* Nothing is outstanding, so the array can start over from the front */ + if (batch->first == batch->count) + batch->first = batch->count = 0; +} + +/* A peer that has gone away must come back as an error rather than as a + signal that kills the tracker, so ask the kernel not to raise one where it + can be asked. macOS cannot, and relies on SIGPIPE being blocked. */ +static ssize_t batch_writev(int s, struct iovec *entries, int count) { +#ifdef MSG_NOSIGNAL + struct msghdr msg; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = entries; + msg.msg_iovlen = count; + return sendmsg(s, &msg, MSG_NOSIGNAL); +#else + return writev(s, entries, count); +#endif +} + +ssize_t batch_send(int s, ot_batch *batch) { + size_t entries = batch->count - batch->first; + ssize_t written; + + if (!batch_pending(batch)) + return BATCH_DONE; + + if (entries > IOV_MAX) + entries = IOV_MAX; + + /* The first entry may be half way out already */ + batch->entries[batch->first].iov_base = (char *)batch->entries[batch->first].iov_base + batch->offset; + batch->entries[batch->first].iov_len -= batch->offset; + written = batch_writev(s, batch->entries + batch->first, (int)entries); + batch->entries[batch->first].iov_base = (char *)batch->entries[batch->first].iov_base - batch->offset; + batch->entries[batch->first].iov_len += batch->offset; + + if (written < 0) + return (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) ? BATCH_AGAIN : BATCH_FAILED; + if (!written) + return BATCH_FAILED; + + batch_consume(batch, (size_t)written); + return written; +} + +void batch_reset(ot_batch *batch) { + while (batch->first < batch->count) + free(batch->entries[batch->first++].iov_base); + + free(batch->entries); + batch->entries = NULL; + batch->count = batch->first = batch->offset = batch->space = 0; +} + +/* For the few constants the chunked encoding needs, which the batch frees + along with everything else */ +int batch_add_copy(ot_batch *batch, const void *buf, size_t len) { + void *copy = malloc(len); + + if (!copy) + return -1; + memcpy(copy, buf, len); + return batch_add(batch, copy, len); +} -- cgit v1.3