a73x

9a6befa2

refactor: one spelling for "drop n bytes off the front of a buffer"

a73x   2026-08-29 10:01

Commit message
refactor: one spelling for "drop n bytes off the front of a buffer"

Five sites wrote the same three lines — compute the remainder,
`copyForwards` it down over the front, `shrinkRetainingCapacity` — where
`std.ArrayList.replaceRangeAssumeCapacity(0, n, &.{})` is the one call
that means it: `quic_client.Client.consume`, `client.Transport.flushQuic`,
the server's pending-flush compaction and `takeFrame`, and
quic_server's EchoOwner backlog flush.

Same semantics: the stdlib shrink path memmoves the tail down and drops
`items.len`, capacity retained, so no reallocation and no pointer
invalidation. `wall_pump.zig:241` keeps its `copyForwards` — that buffer
is a fixed array with a separate length, not an ArrayList.

`takeFrame`'s ownership note moves from its doc block to the alloc it is
about, which is where a reader meets it (and keeps server.zig on its
docscheck budget).

make check rc 0, 982 tests.

src/client/client.zig
Old New
@@ -647,9 +647,7 @@ pub const Transport = struct {
647 if (self.qout.items.len == 0) return; 647 if (self.qout.items.len == 0) return;
648 const n = cl.send(self.qout.items); 648 const n = cl.send(self.qout.items);
649 if (n == 0) return; 649 if (n == 0) return;
650 const rest = self.qout.items.len - n; 650 self.qout.replaceRangeAssumeCapacity(0, n, &.{});
651 std.mem.copyForwards(u8, self.qout.items[0..rest], self.qout.items[n..]);
652 self.qout.shrinkRetainingCapacity(rest);
653 } 651 }
654 652
655 /// The next whole frame, if there is one. See `Incoming` for why a 653 /// The next whole frame, if there is one. See `Incoming` for why a
src/client/quic_client.zig
Old New
@@ -418,10 +418,7 @@ pub const Client = struct {
418 418
419 /// Drop `n` bytes off the front of the inbound buffer. 419 /// Drop `n` bytes off the front of the inbound buffer.
420 pub fn consume(self: *Client, n: usize) void { 420 pub fn consume(self: *Client, n: usize) void {
421 const take = @min(n, self.in.items.len); 421 self.in.replaceRangeAssumeCapacity(0, @min(n, self.in.items.len), &.{});
422 const rest = self.in.items.len - take;
423 std.mem.copyForwards(u8, self.in.items[0..rest], self.in.items[take..]);
424 self.in.shrinkRetainingCapacity(rest);
425 } 422 }
426 }; 423 };
427 424
src/server/quic_server.zig
Old New
@@ -1267,9 +1267,7 @@ const EchoOwner = struct {
1267 if (self.backlog.items.len == 0) return; 1267 if (self.backlog.items.len == 0) return;
1268 const n = self.listener.send(self.id, self.backlog.items) catch return; 1268 const n = self.listener.send(self.id, self.backlog.items) catch return;
1269 if (n == 0) return; 1269 if (n == 0) return;
1270 const rest = self.backlog.items.len - n; 1270 self.backlog.replaceRangeAssumeCapacity(0, n, &.{});
1271 std.mem.copyForwards(u8, self.backlog.items[0..rest], self.backlog.items[n..]);
1272 self.backlog.shrinkRetainingCapacity(rest);
1273 } 1271 }
1274 fn handler(self: *EchoOwner) Handler { 1272 fn handler(self: *EchoOwner) Handler {
1275 return .{ .ctx = self, .onOpen = onOpen, .onData = onData, .onClose = onClose }; 1273 return .{ .ctx = self, .onOpen = onOpen, .onData = onData, .onClose = onClose };
src/server/server.zig
Old New
@@ -1491,9 +1491,7 @@ pub const Server = struct {
1491 // Compact once per flush rather than once per send, so 1491 // Compact once per flush rather than once per send, so
1492 // pending.items.len always means "bytes still owed" — which is 1492 // pending.items.len always means "bytes still owed" — which is
1493 // what the cap is checked against. 1493 // what the cap is checked against.
1494 const rest = slot.pending.items.len - off; 1494 slot.pending.replaceRangeAssumeCapacity(0, off, &.{});
1495 std.mem.copyForwards(u8, slot.pending.items[0..rest], slot.pending.items[off..]);
1496 slot.pending.shrinkRetainingCapacity(rest);
1497 } 1495 }
1498 } 1496 }
1499 1497
@@ -1868,18 +1866,17 @@ pub const Server = struct {
1868 } 1866 }
1869 1867
1870 /// The frame at the front of `buf`, or null while only part of one is 1868 /// The frame at the front of `buf`, or null while only part of one is
1871 /// here — the ordinary state of a byte stream, never an error. Copied 1869 /// here — the ordinary state of a byte stream, never an error. An error
1872 /// out and the buffer shifted BEFORE the caller dispatches, because a 1870 /// is a length no frame can carry, and every caller answers it the same
1873 /// handler can reallocate `buf` under a slice into it; the caller owns 1871 /// way: it has a connection, and drops it.
1874 /// the frame. An error is a length no frame can carry, and every caller
1875 /// answers it the same way: it has a connection, and drops it.
1876 fn takeFrame(alloc: std.mem.Allocator, buf: *std.ArrayList(u8)) !?proto.Frame { 1872 fn takeFrame(alloc: std.mem.Allocator, buf: *std.ArrayList(u8)) !?proto.Frame {
1877 const d = (try proto.delimitFrame(buf.items)) orelse return null; 1873 const d = (try proto.delimitFrame(buf.items)) orelse return null;
1874 // Copied out and `buf` shifted BEFORE the caller dispatches, because
1875 // a handler can reallocate `buf` under a slice into it; the payload
1876 // is then the caller's frame to free.
1878 const payload = try alloc.alloc(u8, d.payload.len); 1877 const payload = try alloc.alloc(u8, d.payload.len);
1879 @memcpy(payload, d.payload); 1878 @memcpy(payload, d.payload);
1880 const rest = buf.items.len - d.consumed; 1879 buf.replaceRangeAssumeCapacity(0, d.consumed, &.{});
1881 std.mem.copyForwards(u8, buf.items[0..rest], buf.items[d.consumed..]);
1882 buf.shrinkRetainingCapacity(rest);
1883 return .{ .type = d.type, .payload = payload }; 1880 return .{ .type = d.type, .payload = payload };
1884 } 1881 }
1885 1882