mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
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.
This commit is contained in:
parent
ad726587cc
commit
70f514f1ba
@ -246,18 +246,41 @@ pub fn appendRemaining(
|
|||||||
limit: Limit,
|
limit: Limit,
|
||||||
) LimitedAllocError!void {
|
) LimitedAllocError!void {
|
||||||
assert(r.buffer.len != 0); // Needed to detect limit exceeded without losing data.
|
assert(r.buffer.len != 0); // Needed to detect limit exceeded without losing data.
|
||||||
var remaining = limit;
|
const buffer_contents = r.buffer[r.seek..r.end];
|
||||||
while (remaining.nonzero()) {
|
const copy_len = limit.minInt(buffer_contents.len);
|
||||||
try list.ensureUnusedCapacity(gpa, r.bufferedLen() + 1);
|
try list.appendSlice(gpa, r.buffer[0..copy_len]);
|
||||||
const dest = remaining.slice(list.unusedCapacitySlice());
|
r.seek += copy_len;
|
||||||
const n = readVecLimit(r, &.{dest}, .unlimited) catch |err| switch (err) {
|
if (buffer_contents.len - copy_len != 0) return error.StreamTooLong;
|
||||||
error.EndOfStream => break,
|
r.seek = 0;
|
||||||
error.ReadFailed => return error.ReadFailed,
|
r.end = 0;
|
||||||
};
|
var remaining = @intFromEnum(limit) - copy_len;
|
||||||
list.items.len += n;
|
while (true) {
|
||||||
remaining = remaining.subtract(n).?;
|
try list.ensureUnusedCapacity(gpa, 1);
|
||||||
|
const cap = list.unusedCapacitySlice();
|
||||||
|
const dest = cap[0..@min(cap.len, remaining)];
|
||||||
|
if (remaining - dest.len == 0) {
|
||||||
|
// Additionally provides `buffer` to detect end.
|
||||||
|
const new_remaining = readVecInner(r, &.{}, dest, remaining) catch |err| switch (err) {
|
||||||
|
error.EndOfStream => {
|
||||||
|
if (r.bufferedLen() != 0) return error.StreamTooLong;
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
error.ReadFailed => return error.ReadFailed,
|
||||||
|
};
|
||||||
|
list.items.len += remaining - new_remaining;
|
||||||
|
remaining = new_remaining;
|
||||||
|
} else {
|
||||||
|
// Leave `buffer` empty, appending directly to `list`.
|
||||||
|
var dest_w: Writer = .fixed(dest);
|
||||||
|
const n = r.vtable.stream(r, &dest_w, .limited(dest.len)) catch |err| switch (err) {
|
||||||
|
error.WriteFailed => unreachable, // Prevented by the limit.
|
||||||
|
error.EndOfStream => return,
|
||||||
|
error.ReadFailed => return error.ReadFailed,
|
||||||
|
};
|
||||||
|
list.items.len += n;
|
||||||
|
remaining -= n;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (r.bufferedLen() != 0) return error.StreamTooLong;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes bytes from the internally tracked stream position to `data`.
|
/// Writes bytes from the internally tracked stream position to `data`.
|
||||||
@ -297,62 +320,68 @@ pub fn readVecLimit(r: *Reader, data: []const []u8, limit: Limit) Error!usize {
|
|||||||
// buffer capacity requirements met.
|
// buffer capacity requirements met.
|
||||||
r.seek = 0;
|
r.seek = 0;
|
||||||
r.end = 0;
|
r.end = 0;
|
||||||
const first = buf[copy_len..];
|
remaining = try readVecInner(r, data[i + 1 ..], buf[copy_len..], remaining);
|
||||||
const middle = data[i + 1 ..];
|
|
||||||
var wrapper: Writer.VectorWrapper = .{
|
|
||||||
.it = .{
|
|
||||||
.first = first,
|
|
||||||
.middle = middle,
|
|
||||||
.last = r.buffer,
|
|
||||||
},
|
|
||||||
.writer = .{
|
|
||||||
.buffer = if (first.len >= r.buffer.len) first else r.buffer,
|
|
||||||
.vtable = Writer.VectorWrapper.vtable,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
var n = r.vtable.stream(r, &wrapper.writer, .limited(remaining)) catch |err| switch (err) {
|
|
||||||
error.WriteFailed => {
|
|
||||||
assert(!wrapper.used);
|
|
||||||
if (wrapper.writer.buffer.ptr == first.ptr) {
|
|
||||||
remaining -= wrapper.writer.end;
|
|
||||||
} else {
|
|
||||||
assert(wrapper.writer.end <= r.buffer.len);
|
|
||||||
r.end = wrapper.writer.end;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
else => |e| return e,
|
|
||||||
};
|
|
||||||
if (!wrapper.used) {
|
|
||||||
if (wrapper.writer.buffer.ptr == first.ptr) {
|
|
||||||
remaining -= n;
|
|
||||||
} else {
|
|
||||||
assert(n <= r.buffer.len);
|
|
||||||
r.end = n;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (n < first.len) {
|
|
||||||
remaining -= n;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
remaining -= first.len;
|
|
||||||
n -= first.len;
|
|
||||||
for (middle) |mid| {
|
|
||||||
if (n < mid.len) {
|
|
||||||
remaining -= n;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
remaining -= mid.len;
|
|
||||||
n -= mid.len;
|
|
||||||
}
|
|
||||||
assert(n <= r.buffer.len);
|
|
||||||
r.end = n;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return @intFromEnum(limit) - remaining;
|
return @intFromEnum(limit) - remaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn readVecInner(r: *Reader, middle: []const []u8, first: []u8, remaining: usize) Error!usize {
|
||||||
|
var wrapper: Writer.VectorWrapper = .{
|
||||||
|
.it = .{
|
||||||
|
.first = first,
|
||||||
|
.middle = middle,
|
||||||
|
.last = r.buffer,
|
||||||
|
},
|
||||||
|
.writer = .{
|
||||||
|
.buffer = if (first.len >= r.buffer.len) first else r.buffer,
|
||||||
|
.vtable = Writer.VectorWrapper.vtable,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// If the limit may pass beyond user buffer into Reader buffer, use
|
||||||
|
// unlimited, allowing the Reader buffer to fill.
|
||||||
|
const limit: Limit = l: {
|
||||||
|
var n: usize = first.len;
|
||||||
|
for (middle) |m| n += m.len;
|
||||||
|
break :l if (remaining >= n) .unlimited else .limited(remaining);
|
||||||
|
};
|
||||||
|
var n = r.vtable.stream(r, &wrapper.writer, limit) catch |err| switch (err) {
|
||||||
|
error.WriteFailed => {
|
||||||
|
assert(!wrapper.used);
|
||||||
|
if (wrapper.writer.buffer.ptr == first.ptr) {
|
||||||
|
return remaining - wrapper.writer.end;
|
||||||
|
} else {
|
||||||
|
assert(wrapper.writer.end <= r.buffer.len);
|
||||||
|
r.end = wrapper.writer.end;
|
||||||
|
return remaining;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => |e| return e,
|
||||||
|
};
|
||||||
|
if (!wrapper.used) {
|
||||||
|
if (wrapper.writer.buffer.ptr == first.ptr) {
|
||||||
|
return remaining - n;
|
||||||
|
} else {
|
||||||
|
assert(n <= r.buffer.len);
|
||||||
|
r.end = n;
|
||||||
|
return remaining;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (n < first.len) return remaining - n;
|
||||||
|
var result = remaining - first.len;
|
||||||
|
n -= first.len;
|
||||||
|
for (middle) |mid| {
|
||||||
|
if (n < mid.len) {
|
||||||
|
return result - n;
|
||||||
|
}
|
||||||
|
result -= mid.len;
|
||||||
|
n -= mid.len;
|
||||||
|
}
|
||||||
|
assert(n <= r.buffer.len);
|
||||||
|
r.end = n;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn buffered(r: *Reader) []u8 {
|
pub fn buffered(r: *Reader) []u8 {
|
||||||
return r.buffer[r.seek..r.end];
|
return r.buffer[r.seek..r.end];
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user