Skip to content

Commit ef83905

Browse files
committed
http fixes
1 parent 9d4ce72 commit ef83905

File tree

7 files changed

+298
-245
lines changed

7 files changed

+298
-245
lines changed

lib/std/Io/Writer.zig

Lines changed: 77 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -191,29 +191,87 @@ pub fn writeSplatHeader(
191191
data: []const []const u8,
192192
splat: usize,
193193
) Error!usize {
194-
const new_end = w.end + header.len;
195-
if (new_end <= w.buffer.len) {
196-
@memcpy(w.buffer[w.end..][0..header.len], header);
197-
w.end = new_end;
198-
return header.len + try writeSplat(w, data, splat);
199-
}
200-
var vecs: [8][]const u8 = undefined; // Arbitrarily chosen size.
201-
var i: usize = 1;
202-
vecs[0] = header;
203-
for (data[0 .. data.len - 1]) |buf| {
204-
if (buf.len == 0) continue;
205-
vecs[i] = buf;
206-
i += 1;
207-
if (vecs.len - i == 0) break;
194+
return writeSplatHeaderLimit(w, header, data, splat, .unlimited);
195+
}
196+
197+
/// Equivalent to `writeSplatHeader` but writes at most `limit` bytes.
198+
pub fn writeSplatHeaderLimit(
199+
w: *Writer,
200+
header: []const u8,
201+
data: []const []const u8,
202+
splat: usize,
203+
limit: Limit,
204+
) Error!usize {
205+
var remaining = @intFromEnum(limit);
206+
{
207+
const copy_len = @min(header.len, w.buffer.len - w.end, remaining);
208+
if (header.len - copy_len != 0) return writeSplatHeaderLimitFinish(w, header, data, splat, remaining);
209+
@memcpy(w.buffer[w.end..][0..copy_len], header[0..copy_len]);
210+
w.end += copy_len;
211+
remaining -= copy_len;
212+
}
213+
for (data[0 .. data.len - 1], 0..) |buf, i| {
214+
const copy_len = @min(buf.len, w.buffer.len - w.end, remaining);
215+
if (buf.len - copy_len != 0) return @intFromEnum(limit) - remaining +
216+
try writeSplatHeaderLimitFinish(w, &.{}, data[i..], splat, remaining);
217+
@memcpy(w.buffer[w.end..][0..copy_len], buf[0..copy_len]);
218+
w.end += copy_len;
219+
remaining -= copy_len;
208220
}
209221
const pattern = data[data.len - 1];
210-
const new_splat = s: {
211-
if (pattern.len == 0 or vecs.len - i == 0) break :s 1;
222+
const splat_n = pattern.len * splat;
223+
if (splat_n > @min(w.buffer.len - w.end, remaining)) {
224+
const buffered_n = @intFromEnum(limit) - remaining;
225+
const written = try writeSplatHeaderLimitFinish(w, &.{}, data[data.len - 1 ..][0..1], splat, remaining);
226+
return buffered_n + written;
227+
}
228+
229+
for (0..splat) |_| {
230+
@memcpy(w.buffer[w.end..][0..pattern.len], pattern);
231+
w.end += pattern.len;
232+
}
233+
234+
remaining -= splat_n;
235+
return @intFromEnum(limit) - remaining;
236+
}
237+
238+
fn writeSplatHeaderLimitFinish(
239+
w: *Writer,
240+
header: []const u8,
241+
data: []const []const u8,
242+
splat: usize,
243+
limit: usize,
244+
) Error!usize {
245+
var remaining = limit;
246+
var vecs: [8][]const u8 = undefined;
247+
var i: usize = 0;
248+
v: {
249+
if (header.len != 0) {
250+
const copy_len = @min(header.len, remaining);
251+
vecs[i] = header[0..copy_len];
252+
i += 1;
253+
remaining -= copy_len;
254+
if (remaining == 0) break :v;
255+
}
256+
for (data[0 .. data.len - 1]) |buf| if (buf.len != 0) {
257+
const copy_len = @min(header.len, remaining);
258+
vecs[i] = buf;
259+
i += 1;
260+
remaining -= copy_len;
261+
if (remaining == 0) break :v;
262+
if (vecs.len - i == 0) break :v;
263+
};
264+
const pattern = data[data.len - 1];
265+
if (splat == 1) {
266+
vecs[i] = pattern[0..@min(remaining, pattern.len)];
267+
i += 1;
268+
break :v;
269+
}
212270
vecs[i] = pattern;
213271
i += 1;
214-
break :s splat;
215-
};
216-
return w.vtable.drain(w, vecs[0..i], new_splat);
272+
return w.vtable.drain(w, (&vecs)[0..i], @min(remaining / pattern.len, splat));
273+
}
274+
return w.vtable.drain(w, (&vecs)[0..i], 1);
217275
}
218276

219277
test "writeSplatHeader splatting avoids buffer aliasing temptation" {

0 commit comments

Comments
 (0)