commit c07b6527d8b68986949d7889496b04f6756cbba1 from: Isaac Meerleo date: Wed Jul 15 16:08:24 2026 UTC centralize bounded imsg chunk emission commit - 6700e6d8bc89354c9145ee84ad4d8a3a666cc8c2 commit + c07b6527d8b68986949d7889496b04f6756cbba1 blob - fe102d4dafbcf9bd82f474cd2053e352e8bcc847 blob + a8022ac95b25226875c4ad607b6180a345ec96b0 --- regress/README +++ regress/README @@ -43,8 +43,9 @@ I9 privileges only narrow sandbox (each role reaches steady pledge before untrusted input; forbidden syscalls trip PLDG violations) -I10 mutual distrust across roles imsgev (partially consumed payloads - and checked-send failures fail closed); +I10 mutual distrust across roles imsgev (partially consumed payloads, + checked sends, and bounded chunk + failures fail closed); generation (canonical event ordering, bounds, and exactly one terminal); ui_config (typed ordering, bounded staging, @@ -187,7 +188,8 @@ src/fugu/turn_txn.c turn_txn (pure ordering plus rea integration); turn (cache/SIGINT path) src/fugu-tty/ui_config_rx.c ui_config; turn (full-palette PTY path) src/common/imsgev.c imsgev (whole-payload views, checked - normal/teardown sends, event failures); + normal/teardown sends, bounded chunk + emission, event failures); sandbox (event runtime) src/common/worker.c sandbox (worker runtime) src/fugu-api (request/stream) turn (correlated concurrent lead/agent blob - 6c3766888c8fd793a430fc901c944614bfa62815 blob + 76378954702734712cbd8768607882c56c6553d8 --- regress/imsgev/imsgev_test.c +++ regress/imsgev/imsgev_test.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -94,6 +95,57 @@ receive_one(struct imsgev *iev, struct imsgbuf *receiv return (imsgbuf_get(receiver, imsg)); } +static void +check_chunks(size_t len) +{ + struct imsgev_payload payload; + struct imsgbuf receiver; + struct imsgev iev; + struct imsg imsg; + u_char *data; + size_t frames, i, n, off; + int fds[2], space = 128 * 1024; + + data = malloc(len); + if (data == NULL) + err(1, "malloc chunk test"); + for (i = 0; i < len; i++) + data[i] = (u_char)((i * 37 + 11) & 0xff); + frames = 1 + (len - 1) / IMSGEV_CHUNK_MAX; + channel_init(&iev, &receiver, fds); + if (setsockopt(fds[0], SOL_SOCKET, SO_SNDBUF, &space, + sizeof(space)) == -1 || + setsockopt(fds[1], SOL_SOCKET, SO_RCVBUF, &space, + sizeof(space)) == -1) + err(1, "setsockopt chunk test"); + imsgev_regress_reset(); + imsgev_send_chunks(&iev, TEST_DATA, 0x12345678, data, len); + CHECK(imsgbuf_queuelen(&iev.ibuf) == frames); + CHECK(imsgev_regress_calls(IMSGEV_REGRESS_COMPOSE) == frames); + CHECK(imsgev_regress_calls(IMSGEV_REGRESS_EVENT_DEL) == frames); + CHECK(imsgev_regress_calls(IMSGEV_REGRESS_EVENT_ADD) == frames); + if (imsgbuf_flush(&iev.ibuf) == -1) + err(1, "imsgbuf_flush chunks"); + for (i = off = 0; i < frames; i++, off += n) { + while (imsgbuf_get(&receiver, &imsg) == 0) + if (imsgbuf_read(&receiver) != 1) + errx(1, "imsgbuf_read chunks"); + n = len - off; + if (n > IMSGEV_CHUNK_MAX) + n = IMSGEV_CHUNK_MAX; + CHECK(imsg.hdr.type == TEST_DATA && + imsg.hdr.peerid == 0x12345678); + CHECK(imsgev_get_payload(&imsg, &payload) == 0); + CHECK(payload.len == n && payload.data != NULL && + memcmp(payload.data, data + off, n) == 0); + imsg_free(&imsg); + } + CHECK(off == len); + CHECK(imsgbuf_get(&receiver, &imsg) == 0); + channel_clear(&iev, &receiver, fds); + free(data); +} + static int dies(void (*fn)(void)) { @@ -182,8 +234,10 @@ die_clear_del(void) } static int -failed_sequence(unsigned int fail_at, uint32_t *trace, size_t *trace_len) +failed_exchange(enum imsgev_regress_op op, unsigned int fail_at, + int null_payload, uint32_t *trace, size_t *trace_len) { + u_char data[2 * IMSGEV_CHUNK_MAX + 1]; struct imsgbuf receiver; struct imsgev iev; ssize_t n; @@ -204,9 +258,11 @@ failed_sequence(unsigned int fail_at, uint32_t *trace, channel_init(&iev, &receiver, fds); imsgev_regress_reset(); imsgev_regress_trace(pfd[1]); - imsgev_regress_fail(IMSGEV_REGRESS_COMPOSE, fail_at); - imsgev_send(&iev, TEST_DATA, 0, "a", 1); - imsgev_send(&iev, TEST_DATA, 0, "b", 1); + if (null_payload) + imsgev_send_chunks(&iev, TEST_DATA, 0, NULL, 1); + memset(data, 0xa5, sizeof(data)); + imsgev_regress_fail(op, fail_at); + imsgev_send_chunks(&iev, TEST_DATA, 0, data, sizeof(data)); imsgev_send(&iev, TEST_END, 0, NULL, 0); _exit(0); default: @@ -241,6 +297,28 @@ main(void) if (event_init() == NULL) errx(1, "event_init"); + /* Empty payloads emit no data frames; the exchange owns its terminal. */ + channel_init(&iev, &receiver, fds); + imsgev_regress_reset(); + imsgev_send_chunks(&iev, TEST_DATA, 7, NULL, 0); + imsgev_send_chunks(&iev, TEST_DATA, 7, binary, 0); + imsgev_send(&iev, TEST_END, 7, NULL, 0); + CHECK(imsgbuf_queuelen(&iev.ibuf) == 1); + CHECK(imsgev_regress_calls(IMSGEV_REGRESS_COMPOSE) == 1); + CHECK(receive_one(&iev, &receiver, &imsg) == 1); + CHECK(imsg.hdr.type == TEST_END && imsg.hdr.peerid == 7 && + imsg_get_len(&imsg) == 0); + imsg_free(&imsg); + channel_clear(&iev, &receiver, fds); + + /* Exact boundaries preserve bytes, frame size, type, and correlation. */ + check_chunks(1); + check_chunks(IMSGEV_CHUNK_MAX - 1); + check_chunks(IMSGEV_CHUNK_MAX); + check_chunks(IMSGEV_CHUNK_MAX + 1); + check_chunks(2 * IMSGEV_CHUNK_MAX); + check_chunks(2 * IMSGEV_CHUNK_MAX + 1); + make_imsg(NULL, 0, &receiver, &imsg); payload.data = (const u_char *)1; payload.len = 1; @@ -302,16 +380,38 @@ main(void) CHECK(dies(die_rearm_add)); CHECK(dies(die_clear_del)); - /* A failed chunk stops the exchange before any terminal send. */ - CHECK(failed_sequence(1, trace, &trace_len)); + /* NULL/nonzero is rejected before even the first composition. */ + CHECK(failed_exchange(IMSGEV_REGRESS_OPS, 0, 1, trace, &trace_len)); + CHECK(trace_len == 0); + + /* A failed data chunk stops the exchange before any later terminal. */ + CHECK(failed_exchange(IMSGEV_REGRESS_COMPOSE, 1, 0, + trace, &trace_len)); CHECK(trace_len == 1 && trace[0] == TEST_DATA); - CHECK(failed_sequence(2, trace, &trace_len)); + CHECK(failed_exchange(IMSGEV_REGRESS_COMPOSE, 2, 0, + trace, &trace_len)); CHECK(trace_len == 2 && trace[0] == TEST_DATA && trace[1] == TEST_DATA); - CHECK(failed_sequence(3, trace, &trace_len)); + CHECK(failed_exchange(IMSGEV_REGRESS_COMPOSE, 3, 0, + trace, &trace_len)); CHECK(trace_len == 3 && trace[0] == TEST_DATA && - trace[1] == TEST_DATA && trace[2] == TEST_END); + trace[1] == TEST_DATA && trace[2] == TEST_DATA); + CHECK(failed_exchange(IMSGEV_REGRESS_COMPOSE, 4, 0, + trace, &trace_len)); + CHECK(trace_len == 4 && trace[0] == TEST_DATA && + trace[1] == TEST_DATA && trace[2] == TEST_DATA && + trace[3] == TEST_END); + /* Every chunk inherits the checked event re-arm policy. */ + CHECK(failed_exchange(IMSGEV_REGRESS_EVENT_DEL, 2, 0, + trace, &trace_len)); + CHECK(trace_len == 2 && trace[0] == TEST_DATA && + trace[1] == TEST_DATA); + CHECK(failed_exchange(IMSGEV_REGRESS_EVENT_ADD, 2, 0, + trace, &trace_len)); + CHECK(trace_len == 2 && trace[0] == TEST_DATA && + trace[1] == TEST_DATA); + /* Teardown is synchronous, best effort, and never re-arms events. */ channel_init(&iev, &receiver, fds); imsgev_regress_reset(); blob - a871534c0365bae20aa5ea5ee4e649d5bc73f8dd blob + f08725e30d20ea6b7831cbda22421c098dea831d --- src/common/imsgev.c +++ src/common/imsgev.c @@ -68,6 +68,23 @@ imsgev_send(struct imsgev *iev, uint32_t type, uint32_ imsgev_add(iev); } +void +imsgev_send_chunks(struct imsgev *iev, uint32_t type, uint32_t peerid, + const void *data, size_t datalen) +{ + const u_char *p = data; + size_t off, n; + + if (data == NULL && datalen != 0) + fatalx("NULL chunk payload"); + for (off = 0; off < datalen; off += n) { + n = datalen - off; + if (n > IMSGEV_CHUNK_MAX) + n = IMSGEV_CHUNK_MAX; + imsgev_send(iev, type, peerid, p + off, n); + } +} + /* * Teardown has no event-loop future in which queued output can drain. * Attempt one synchronous empty frame and flush, but never turn cleanup blob - 17920eec05b8c5a24a21b4d3d47c72941dd0298f blob + 1ceb3f1eda3dd126f60dcacb3cb3e8137bf036e9 --- src/common/imsgev.h +++ src/common/imsgev.h @@ -20,6 +20,9 @@ #include #include +/* A conservative payload slice below imsg's 16 KiB frame ceiling. */ +#define IMSGEV_CHUNK_MAX (8 * 1024) + /* * The imsg-over-libevent wrapper, in the vmd proc.c lineage: one * struct per channel, write interest re-armed only when output is @@ -49,6 +52,12 @@ void imsgev_add(struct imsgev *); /* Normal sends are checked and own the no-fd/no-pid transport policy. */ void imsgev_send(struct imsgev *, uint32_t, uint32_t, const void *, size_t); +/* + * Emit a contiguous payload as checked bounded frames. A zero length emits + * none; a nonzero length requires data. Typed callers own terminal frames. + */ +void imsgev_send_chunks(struct imsgev *, uint32_t, uint32_t, const void *, + size_t); /* Teardown attempts one synchronous empty frame without re-arming events. */ void imsgev_send_teardown(struct imsgev *, uint32_t); int imsgev_get_payload(struct imsg *, struct imsgev_payload *); blob - b995cdd31bd24787f89b407a6beeb22d9b533c9f blob + 942a23cf5d400d1e79a25735bb7088635fd8235f --- src/fugu/coord.c +++ src/fugu/coord.c @@ -57,6 +57,7 @@ #define MAX_TOOL_CALLS 64 /* tool_use blocks per assistant msg */ #define MODELS_RELAY_MAX 4096 /* picker entries per listing (I10) */ #define AGENT_WEB_MEMORY_MAX (64 * 1024 * 1024) +#define CAPTURE_TEXT_MAX (8 * 1024) /* one bounded CTX_APPEND frame */ /* The /compact summarization instruction (behavior.md section 3). */ #define COMPACT_INSTR \ @@ -1833,7 +1834,6 @@ agent_web_start_next(struct coord *c) { struct subagent *a; struct tool_req req; - size_t off; int i; if (c->agent_web_active != NULL || c->web_busy_id != 0) @@ -1855,15 +1855,8 @@ agent_web_start_next(struct coord *c) c->web_busy_id = a->web_id; imsgev_send(&c->iev[ROLE_WEB], FUGU_IMSG_WEB_REQ, a->web_id, &req, sizeof(req)); - for (off = 0; off < a->web_arg.len; ) { - size_t k = a->web_arg.len - off; - - if (k > FUGU_CHUNK) - k = FUGU_CHUNK; - imsgev_send(&c->iev[ROLE_WEB], FUGU_IMSG_WEB_ARG, - a->web_id, a->web_arg.data + off, k); - off += k; - } + imsgev_send_chunks(&c->iev[ROLE_WEB], FUGU_IMSG_WEB_ARG, + a->web_id, a->web_arg.data, a->web_arg.len); imsgev_send(&c->iev[ROLE_WEB], FUGU_IMSG_WEB_RUN, a->web_id, NULL, 0); agent_web_arg_free(c, a); @@ -2093,17 +2086,9 @@ send_request(struct coord *c, struct buf *body, uint32 { struct imsgev *iev = &c->iev[ROLE_API]; struct api_request_end end; - size_t off; - for (off = 0; off < body->len; ) { - size_t n = body->len - off; - - if (n > FUGU_CHUNK) - n = FUGU_CHUNK; - imsgev_send(iev, FUGU_IMSG_REQUEST, id, - body->data + off, n); - off += n; - } + imsgev_send_chunks(iev, FUGU_IMSG_REQUEST, id, + body->data, body->len); memset(&end, 0, sizeof(end)); end.provider_slot = provider_slot; imsgev_send(iev, FUGU_IMSG_REQUEST_END, id, &end, @@ -2116,20 +2101,12 @@ run_tool(struct coord *c, struct tool_call *tc) { struct imsgev *iev = &c->iev[ROLE_TOOL]; struct tool_req req; - size_t off; memset(&req, 0, sizeof(req)); strlcpy(req.name, tc->name, sizeof(req.name)); imsgev_send(iev, FUGU_IMSG_TOOL_REQ, 0, &req, sizeof(req)); - for (off = 0; off < tc->input.len; ) { - size_t n = tc->input.len - off; - - if (n > FUGU_CHUNK) - n = FUGU_CHUNK; - imsgev_send(iev, FUGU_IMSG_TOOL_ARG, 0, - tc->input.data + off, n); - off += n; - } + imsgev_send_chunks(iev, FUGU_IMSG_TOOL_ARG, 0, + tc->input.data, tc->input.len); imsgev_send(iev, FUGU_IMSG_TOOL_RUN, 0, NULL, 0); buf_reset(&c->tool_out); @@ -2148,7 +2125,6 @@ run_web(struct coord *c, struct tool_call *tc) { struct imsgev *iev = &c->iev[ROLE_WEB]; struct tool_req req; - size_t off; int rc; buf_reset(&c->tool_out); @@ -2175,15 +2151,8 @@ run_web(struct coord *c, struct tool_call *tc) c->web_id = agent_unique_id(c); c->web_busy_id = c->web_id; imsgev_send(iev, FUGU_IMSG_WEB_REQ, c->web_id, &req, sizeof(req)); - for (off = 0; off < tc->input.len; ) { - size_t n = tc->input.len - off; - - if (n > FUGU_CHUNK) - n = FUGU_CHUNK; - imsgev_send(iev, FUGU_IMSG_WEB_ARG, c->web_id, - tc->input.data + off, n); - off += n; - } + imsgev_send_chunks(iev, FUGU_IMSG_WEB_ARG, c->web_id, + tc->input.data, tc->input.len); imsgev_send(iev, FUGU_IMSG_WEB_RUN, c->web_id, NULL, 0); c->wait = WAIT_WEB; @@ -2918,21 +2887,6 @@ agent_system(struct coord *c, struct buf *out) } static void -agent_compose_bytes(struct imsgev *iev, uint32_t type, const void *data, - size_t len) -{ - const u_char *p = data; - size_t off, n; - - for (off = 0; off < len; off += n) { - n = len - off; - if (n > FUGU_CHUNK) - n = FUGU_CHUNK; - imsgev_send(iev, type, 0, p + off, n); - } -} - -static void agent_start(struct coord *c, struct subagent *a, const struct buf *system) { struct agent_start start; @@ -2952,9 +2906,9 @@ agent_start(struct coord *c, struct subagent *a, const buf_addc(&b, '\0'); imsgev_send(&a->iev, FUGU_IMSG_AGENT_START, 0, b.data, b.len); buf_free(&b); - agent_compose_bytes(&a->iev, FUGU_IMSG_AGENT_SYSTEM, system->data, + imsgev_send_chunks(&a->iev, FUGU_IMSG_AGENT_SYSTEM, 0, system->data, system->len); - agent_compose_bytes(&a->iev, FUGU_IMSG_AGENT_PROMPT, a->prompt.data, + imsgev_send_chunks(&a->iev, FUGU_IMSG_AGENT_PROMPT, 0, a->prompt.data, a->prompt.len); imsgev_send(&a->iev, FUGU_IMSG_AGENT_RUN, 0, NULL, 0); } @@ -3577,7 +3531,7 @@ cmd_capture(struct coord *c, const char *text) return (1); } /* the whole append must fit one imsg frame; keep well under it */ - if (strlen(text) > FUGU_CHUNK) { + if (strlen(text) > CAPTURE_TEXT_MAX) { emit_error(c, "fugu: capture line too long"); return (1); } blob - 34516048ceae9b0faa99bce0196bb2d6681151d6 blob + 37523910880611bd05f9b3059637d3487170f0a5 --- src/fugu/proto.h +++ src/fugu/proto.h @@ -60,9 +60,6 @@ #define PROVIDER_ANTHROPIC 0 #define PROVIDER_OPENAI 1 -/* Body chunk size for the REQUEST stream (below the 16 KiB imsg cap). */ -#define FUGU_CHUNK (8 * 1024) - /* Credentials cross one HELLO frame and later become HTTP headers. */ #define FUGU_SECRET_MAX (4 * 1024) @@ -93,7 +90,7 @@ #define FUGU_TOOL_NAME_MAX 32 /* The network worker uses its own authority-separated exchange. */ -#define FUGU_WEB_HELLO_MAX FUGU_CHUNK +#define FUGU_WEB_HELLO_MAX (8 * 1024) #define FUGU_WEB_ARG_MAX FUGU_TOOL_ARG_MAX #define FUGU_WEB_OUT_MAX (256 * 1024) blob - c6f79ced52336f1652d68757b1b3409f1ade861f blob + 3b8b8723d9c7524969a9ff6212020e3950c0a91f --- src/fugu-api/main.c +++ src/fugu-api/main.c @@ -603,15 +603,7 @@ relay(struct api_request *r, uint32_t type, const void static void relay_bytes(struct api_request *r, uint32_t type, const void *data, size_t len) { - const u_char *p = data; - size_t off, n; - - for (off = 0; off < len; off += n) { - n = len - off; - if (n > FUGU_CHUNK) - n = FUGU_CHUNK; - relay(r, type, p + off, n); - } + imsgev_send_chunks(&r->worker->iev, type, r->id, data, len); } static void @@ -624,8 +616,8 @@ relay_tool_bytes(struct api_request *r, int index, con struct buf b; n = len - off; - if (n > FUGU_CHUNK) - n = FUGU_CHUNK; + if (n > IMSGEV_CHUNK_MAX) + n = IMSGEV_CHUNK_MAX; buf_init(&b); buf_add(&b, &index, sizeof(index)); buf_add(&b, p + off, n); @@ -727,8 +719,8 @@ on_event(const struct astream_event *ev, void *arg) for (off = 0; off < ev->textlen; ) { size_t n = ev->textlen - off; - if (n > FUGU_CHUNK) - n = FUGU_CHUNK; + if (n > IMSGEV_CHUNK_MAX) + n = IMSGEV_CHUNK_MAX; if (filter_text(r, ev->text + off, n, 0) == -1) { relay_error(r, "provider response contained an API credential"); @@ -787,8 +779,8 @@ on_event(const struct astream_event *ev, void *arg) for (off = 0; off < ev->partiallen; ) { size_t n = ev->partiallen - off; - if (n > FUGU_CHUNK) - n = FUGU_CHUNK; + if (n > IMSGEV_CHUNK_MAX) + n = IMSGEV_CHUNK_MAX; if (filter_tool(r, ts, ev->partial + off, n, 0) == -1) { relay_error(r, "provider response contained an API credential"); blob - a8a09dd372615a9daec10e4c7d8ee5ac4e2ecfdf blob + 069e63f2ee77a067e43d2b3a5a003715b89581dd --- src/fugu-editor/main.c +++ src/fugu-editor/main.c @@ -227,7 +227,7 @@ compose(struct worker *w) { struct stat st; char path[] = "/tmp/fugu-edit.XXXXXXXXXX"; - char tmp[FUGU_CHUNK]; + char tmp[IMSGEV_CHUNK_MAX]; ssize_t n; size_t off, sent; sigset_t oldmask; blob - 83c5b7c960279f612572e5e849cb4f2e3152ad0b blob + 1a4fe85c811b6807de7951646229f2ad3e684147 --- src/fugu-tool/agent.c +++ src/fugu-tool/agent.c @@ -258,8 +258,8 @@ agent_send_bytes(struct agent_state *a, uint32_t type, for (off = 0; off < len; off += n) { n = len - off; - if (n > FUGU_CHUNK) - n = FUGU_CHUNK; + if (n > IMSGEV_CHUNK_MAX) + n = IMSGEV_CHUNK_MAX; agent_send(a, type, p + off, n); } } blob - 07b7f6d8e57a4e1802416d7fe25b7a3b420d0546 blob + 7302d1885910203803ab6bdd772ce678a258f08b --- src/fugu-tool/main.c +++ src/fugu-tool/main.c @@ -358,27 +358,13 @@ send_result(struct worker *w, const struct buf *out, c int is_error) { struct tool_result r; - size_t off, dlen; + size_t dlen; - for (off = 0; off < out->len; ) { - size_t n = out->len - off; - - if (n > FUGU_CHUNK) - n = FUGU_CHUNK; - imsgev_send(&w->iev, FUGU_IMSG_TOOL_OUT, 0, - out->data + off, n); - off += n; - } + imsgev_send_chunks(&w->iev, FUGU_IMSG_TOOL_OUT, 0, + out->data, out->len); dlen = diff->len > FUGU_TOOL_DIFF_MAX ? FUGU_TOOL_DIFF_MAX : diff->len; - for (off = 0; off < dlen; ) { - size_t n = dlen - off; - - if (n > FUGU_CHUNK) - n = FUGU_CHUNK; - imsgev_send(&w->iev, FUGU_IMSG_TOOL_DIFF, 0, - diff->data + off, n); - off += n; - } + imsgev_send_chunks(&w->iev, FUGU_IMSG_TOOL_DIFF, 0, + diff->data, dlen); memset(&r, 0, sizeof(r)); r.is_error = is_error; imsgev_send(&w->iev, FUGU_IMSG_TOOL_RESULT, 0, &r, @@ -476,7 +462,6 @@ tool_dispatch(struct worker *w, struct imsg *imsg) case FUGU_IMSG_CTX_READ: { struct buf ctx; char path[1024]; - size_t off; /* the payload is a NUL-terminated path (coordinator-built) */ if (imsgev_get_payload(imsg, &payload) == -1) @@ -490,15 +475,8 @@ tool_dispatch(struct worker *w, struct imsg *imsg) path[len] = '\0'; buf_init(&ctx); ctx_file_read(path, &ctx); - for (off = 0; off < ctx.len; ) { /* chunk under the cap */ - size_t k = ctx.len - off; - - if (k > FUGU_CHUNK) - k = FUGU_CHUNK; - imsgev_send(&w->iev, FUGU_IMSG_CTX_DATA, 0, - ctx.data + off, k); - off += k; - } + imsgev_send_chunks(&w->iev, FUGU_IMSG_CTX_DATA, 0, + ctx.data, ctx.len); imsgev_send(&w->iev, FUGU_IMSG_CTX_END, 0, NULL, 0); buf_free(&ctx); return (0); blob - eece9db7071abfafc31067ad2192b24c8a84511a blob + 05f771be2db08fe8748ce411937e60884c6fc586 --- src/fugu-tty/ui.c +++ src/fugu-tty/ui.c @@ -886,7 +886,7 @@ scroll_by(struct ui *u, int delta) static void arm_esc(struct ui *); /* - * Send a message to the coordinator in FUGU_CHUNK slices terminated by + * Send a message to the coordinator in bounded slices terminated by * an end marker (behavior.md 2.1): a submitted line or a steered entry * can be a whole paste (up to 64 KiB), which does not fit one imsg * frame (16 KiB). The editor caps a message at ED_PASTE_MAX, so the @@ -896,16 +896,7 @@ static void send_chunked(struct ui *u, int chunk_type, int end_type, const char *p, size_t len) { - size_t off; - - for (off = 0; off < len; ) { - size_t n = len - off; - - if (n > FUGU_CHUNK) - n = FUGU_CHUNK; - imsgev_send(&u->w->iev, chunk_type, 0, p + off, n); - off += n; - } + imsgev_send_chunks(&u->w->iev, chunk_type, 0, p, len); imsgev_send(&u->w->iev, end_type, 0, NULL, 0); } @@ -987,7 +978,7 @@ dequeue(struct ui *u) static void compose_begin(struct ui *u) { - size_t len, off; + size_t len; const char *text = ed_text(&u->ed, &len); if (u->composing) @@ -1004,15 +995,7 @@ compose_begin(struct ui *u) event_del(&u->ev_in); (void)write(STDOUT_FILENO, "\033[?2004l", 8); /* paste off */ endwin(); - for (off = 0; off < len; ) { - size_t n = len - off; - - if (n > FUGU_CHUNK) - n = FUGU_CHUNK; - imsgev_send(&u->w->iev, FUGU_IMSG_UI_COMPOSE, 0, - text + off, n); - off += n; - } + imsgev_send_chunks(&u->w->iev, FUGU_IMSG_UI_COMPOSE, 0, text, len); imsgev_send(&u->w->iev, FUGU_IMSG_UI_COMPOSE_GO, 0, NULL, 0); u->composing = 1; blob - c2665884939d8193e3321c1b2f77f94048dae9bd blob + 5de80b1424edcb87c8f49ed39b04f63ac360650d --- src/fugu-web/main.c +++ src/fugu-web/main.c @@ -195,17 +195,9 @@ send_result(struct worker *w, uint32_t reqid, const st int is_error) { struct tool_result r; - size_t off; - for (off = 0; off < out->len; ) { - size_t n = out->len - off; - - if (n > FUGU_CHUNK) - n = FUGU_CHUNK; - imsgev_send(&w->iev, FUGU_IMSG_WEB_OUT, reqid, - out->data + off, n); - off += n; - } + imsgev_send_chunks(&w->iev, FUGU_IMSG_WEB_OUT, reqid, + out->data, out->len); memset(&r, 0, sizeof(r)); r.is_error = is_error; imsgev_send(&w->iev, FUGU_IMSG_WEB_RESULT, reqid, &r, sizeof(r));