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(
246246 limit: Limit,
247247) LimitedAllocError!void {
248248 assert(r.buffer.len != 0); // Needed to detect limit exceeded without losing data.
249 var remaining = limit;
250 while (remaining.nonzero()) {
251 try list.ensureUnusedCapacity(gpa, r.bufferedLen() + 1);
252 const dest = remaining.slice(list.unusedCapacitySlice());
253 const n = readVecLimit(r, &.{dest}, .unlimited) catch |err| switch (err) {
254 error.EndOfStream => break,
255 error.ReadFailed => return error.ReadFailed,
256 };
257 list.items.len += n;
258 remaining = remaining.subtract(n).?;
249 const buffer_contents = r.buffer[r.seek..r.end];
250 const copy_len = limit.minInt(buffer_contents.len);
251 try list.appendSlice(gpa, r.buffer[0..copy_len]);
252 r.seek += copy_len;
253 if (buffer_contents.len - copy_len != 0) return error.StreamTooLong;
254 r.seek = 0;
255 r.end = 0;
256 var remaining = @intFromEnum(limit) - copy_len;
257 while (true) {
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 }
259283 }
260 if (r.bufferedLen() != 0) return error.StreamTooLong;
261284}
262285
263286/// 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 {
297320 // buffer capacity requirements met.
298321 r.seek = 0;
299322 r.end = 0;
300 const first = buf[copy_len..];
301 const middle = data[i + 1 ..];
302 var wrapper: Writer.VectorWrapper = .{
303 .it = .{
304 .first = first,
305 .middle = middle,
306 .last = r.buffer,
307 },
308 .writer = .{
309 .buffer = if (first.len >= r.buffer.len) first else r.buffer,
310 .vtable = Writer.VectorWrapper.vtable,
311 },
312 };
313 var n = r.vtable.stream(r, &wrapper.writer, .limited(remaining)) catch |err| switch (err) {
314 error.WriteFailed => {
315 assert(!wrapper.used);
316 if (wrapper.writer.buffer.ptr == first.ptr) {
317 remaining -= wrapper.writer.end;
318 } else {
319 assert(wrapper.writer.end <= r.buffer.len);
320 r.end = wrapper.writer.end;
321 }
322 break;
323 },
324 else => |e| return e,
325 };
326 if (!wrapper.used) {
323 remaining = try readVecInner(r, data[i + 1 ..], buf[copy_len..], remaining);
324 break;
325 }
326 return @intFromEnum(limit) - remaining;
327}
328
329fn readVecInner(r: *Reader, middle: []const []u8, first: []u8, remaining: usize) Error!usize {
330 var wrapper: Writer.VectorWrapper = .{
331 .it = .{
332 .first = first,
333 .middle = middle,
334 .last = r.buffer,
335 },
336 .writer = .{
337 .buffer = if (first.len >= r.buffer.len) first else r.buffer,
338 .vtable = Writer.VectorWrapper.vtable,
339 },
340 };
341 // If the limit may pass beyond user buffer into Reader buffer, use
342 // unlimited, allowing the Reader buffer to fill.
343 const limit: Limit = l: {
344 var n: usize = first.len;
345 for (middle) |m| n += m.len;
346 break :l if (remaining >= n) .unlimited else .limited(remaining);
347 };
348 var n = r.vtable.stream(r, &wrapper.writer, limit) catch |err| switch (err) {
349 error.WriteFailed => {
350 assert(!wrapper.used);
327351 if (wrapper.writer.buffer.ptr == first.ptr) {
328 remaining -= n;
352 return remaining - wrapper.writer.end;
329353 } else {
330 assert(n <= r.buffer.len);
331 r.end = n;
354 assert(wrapper.writer.end <= r.buffer.len);
355 r.end = wrapper.writer.end;
356 return remaining;
332357 }
333 break;
334 }
335 if (n < first.len) {
336 remaining -= n;
337 break;
358 },
359 else => |e| return e,
360 };
361 if (!wrapper.used) {
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;
338368 }
339 remaining -= first.len;
340 n -= first.len;
341 for (middle) |mid| {
342 if (n < mid.len) {
343 remaining -= n;
344 break;
345 }
346 remaining -= mid.len;
347 n -= mid.len;
369 }
370 if (n < first.len) return remaining - n;
371 var result = remaining - first.len;
372 n -= first.len;
373 for (middle) |mid| {
374 if (n < mid.len) {
375 return result - n;
348376 }
349 assert(n <= r.buffer.len);
350 r.end = n;
351 break;
377 result -= mid.len;
378 n -= mid.len;
352379 }
353 return @intFromEnum(limit) - remaining;
380 assert(n <= r.buffer.len);
381 r.end = n;
382 return result;
354383}
355384
356385pub fn buffered(r: *Reader) []u8 {