@@ -191,29 +191,87 @@ pub fn writeSplatHeader(
191
191
data : []const []const u8 ,
192
192
splat : usize ,
193
193
) 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 ;
208
220
}
209
221
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
+ }
212
270
vecs [i ] = pattern ;
213
271
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 );
217
275
}
218
276
219
277
test "writeSplatHeader splatting avoids buffer aliasing temptation" {
0 commit comments