authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-16 17:04:55-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-16 17:20:03-07:00
log70f514f1ba961f05813fbe869a942a33113edf4c
treeb27b393ddc783f73759265ff2158870e1200e065
parentad726587cc66b1ae07a463e1d591362130090170

std.Io.Reader: fix appendRemaining harder

ensure that it issues a stream call that includes the buffer to detect the end when needed, but otherwise does not offer Reader buffer to append directly to the list.

1 files changed, 88 insertions(+), 59 deletions(-)

lib/std/Io/Reader.zig+88-59
...@@ -246,18 +246,41 @@ pub fn appendRemaining(...@@ -246,18 +246,41 @@ pub fn appendRemaining(
246 limit: Limit,246 limit: Limit,
247) LimitedAllocError!void {247) LimitedAllocError!void {
248 assert(r.buffer.len != 0); // Needed to detect limit exceeded without losing data.248 assert(r.buffer.len != 0); // Needed to detect limit exceeded without losing data.
249 var remaining = limit;249 const buffer_contents = r.buffer[r.seek..r.end];
250 while (remaining.nonzero()) {250 const copy_len = limit.minInt(buffer_contents.len);
251 try list.ensureUnusedCapacity(gpa, r.bufferedLen() + 1);251 try list.appendSlice(gpa, r.buffer[0..copy_len]);
252 const dest = remaining.slice(list.unusedCapacitySlice());252 r.seek += copy_len;
253 const n = readVecLimit(r, &.{dest}, .unlimited) catch |err| switch (err) {253 if (buffer_contents.len - copy_len != 0) return error.StreamTooLong;
254 error.EndOfStream => break,254 r.seek = 0;
255 error.ReadFailed => return error.ReadFailed,255 r.end = 0;
256 };256 var remaining = @intFromEnum(limit) - copy_len;
257 list.items.len += n;257 while (true) {
258 remaining = remaining.subtract(n).?;258 try list.ensureUnusedCapacity(gpa, 1);
259 const cap = list.unusedCapacitySlice();
260 const dest = cap[0..@min(cap.len, remaining)];
261 if (remaining - dest.len == 0) {
262 // Additionally provides `buffer` to detect end.
263 const new_remaining = readVecInner(r, &.{}, dest, remaining) catch |err| switch (err) {
264 error.EndOfStream => {
265 if (r.bufferedLen() != 0) return error.StreamTooLong;
266 return;
267 },
268 error.ReadFailed => return error.ReadFailed,
269 };
270 list.items.len += remaining - new_remaining;
271 remaining = new_remaining;
272 } else {
273 // Leave `buffer` empty, appending directly to `list`.
274 var dest_w: Writer = .fixed(dest);
275 const n = r.vtable.stream(r, &dest_w, .limited(dest.len)) catch |err| switch (err) {
276 error.WriteFailed => unreachable, // Prevented by the limit.
277 error.EndOfStream => return,
278 error.ReadFailed => return error.ReadFailed,
279 };
280 list.items.len += n;
281 remaining -= n;
282 }
259 }283 }
260 if (r.bufferedLen() != 0) return error.StreamTooLong;
261}284}
262285
263/// Writes bytes from the internally tracked stream position to `data`.286/// Writes bytes from the internally tracked stream position to `data`.
...@@ -297,60 +320,66 @@ pub fn readVecLimit(r: *Reader, data: []const []u8, limit: Limit) Error!usize {...@@ -297,60 +320,66 @@ pub fn readVecLimit(r: *Reader, data: []const []u8, limit: Limit) Error!usize {
297 // buffer capacity requirements met.320 // buffer capacity requirements met.
298 r.seek = 0;321 r.seek = 0;
299 r.end = 0;322 r.end = 0;
300 const first = buf[copy_len..];323 remaining = try readVecInner(r, data[i + 1 ..], buf[copy_len..], remaining);
301 const middle = data[i + 1 ..];324 break;
302 var wrapper: Writer.VectorWrapper = .{325 }
303 .it = .{326 return @intFromEnum(limit) - remaining;
304 .first = first,327}
305 .middle = middle,328
306 .last = r.buffer,329fn readVecInner(r: *Reader, middle: []const []u8, first: []u8, remaining: usize) Error!usize {
307 },330 var wrapper: Writer.VectorWrapper = .{
308 .writer = .{331 .it = .{
309 .buffer = if (first.len >= r.buffer.len) first else r.buffer,332 .first = first,
310 .vtable = Writer.VectorWrapper.vtable,333 .middle = middle,
311 },334 .last = r.buffer,
312 };335 },
313 var n = r.vtable.stream(r, &wrapper.writer, .limited(remaining)) catch |err| switch (err) {336 .writer = .{
314 error.WriteFailed => {337 .buffer = if (first.len >= r.buffer.len) first else r.buffer,
315 assert(!wrapper.used);338 .vtable = Writer.VectorWrapper.vtable,
316 if (wrapper.writer.buffer.ptr == first.ptr) {339 },
317 remaining -= wrapper.writer.end;340 };
318 } else {341 // If the limit may pass beyond user buffer into Reader buffer, use
319 assert(wrapper.writer.end <= r.buffer.len);342 // unlimited, allowing the Reader buffer to fill.
320 r.end = wrapper.writer.end;343 const limit: Limit = l: {
321 }344 var n: usize = first.len;
322 break;345 for (middle) |m| n += m.len;
323 },346 break :l if (remaining >= n) .unlimited else .limited(remaining);
324 else => |e| return e,347 };
325 };348 var n = r.vtable.stream(r, &wrapper.writer, limit) catch |err| switch (err) {
326 if (!wrapper.used) {349 error.WriteFailed => {
350 assert(!wrapper.used);
327 if (wrapper.writer.buffer.ptr == first.ptr) {351 if (wrapper.writer.buffer.ptr == first.ptr) {
328 remaining -= n;352 return remaining - wrapper.writer.end;
329 } else {353 } else {
330 assert(n <= r.buffer.len);354 assert(wrapper.writer.end <= r.buffer.len);
331 r.end = n;355 r.end = wrapper.writer.end;
356 return remaining;
332 }357 }
333 break;358 },
334 }359 else => |e| return e,
335 if (n < first.len) {360 };
336 remaining -= n;361 if (!wrapper.used) {
337 break;362 if (wrapper.writer.buffer.ptr == first.ptr) {
363 return remaining - n;
364 } else {
365 assert(n <= r.buffer.len);
366 r.end = n;
367 return remaining;
338 }368 }
339 remaining -= first.len;369 }
340 n -= first.len;370 if (n < first.len) return remaining - n;
341 for (middle) |mid| {371 var result = remaining - first.len;
342 if (n < mid.len) {372 n -= first.len;
343 remaining -= n;373 for (middle) |mid| {
344 break;374 if (n < mid.len) {
345 }375 return result - n;
346 remaining -= mid.len;
347 n -= mid.len;
348 }376 }
349 assert(n <= r.buffer.len);377 result -= mid.len;
350 r.end = n;378 n -= mid.len;
351 break;
352 }379 }
353 return @intFromEnum(limit) - remaining;380 assert(n <= r.buffer.len);
381 r.end = n;
382 return result;
354}383}
355384
356pub fn buffered(r: *Reader) []u8 {385pub fn buffered(r: *Reader) []u8 {