A poolable string builder (aka string buffer) for Zig
0

Configure Feed

Select the types of activity you want to include in your feed.

add an unsafe writeAt

Karl Seguin (Oct 13, 2023, 1:34 PM +0800) 6a59c76c 75bce949

+102 -84
+102 -84
src/buffer.zig
··· 110 110 // Two allocators! This is largely a feature meant to be used with the Pool. 111 111 // Imagine you have a pool of 100 StringBuilders. Each one has a static buffer 112 112 // of 2K, allocated with a general purpose allocator. We store that in _a. 113 - // Now you acquire an SB and start to write. You write more than 2K, so we 113 + // Now you acquire an w and start to write. You write more than 2K, so we 114 114 // need to allocate `dynamic`. Yes, we could use our general purpose allocator 115 115 // (aka _a), but what if the app would like to use a different allocator for 116 116 // that, like an Arena? ··· 197 197 self._view.write(data); 198 198 } 199 199 200 + // unsafe 201 + pub fn writeAt(self: *Buffer, data: []const u8, pos: usize) void { 202 + @memcpy(self._view.buf[pos..pos+data.len], data); 203 + } 204 + 200 205 pub fn writeAssumeCapacity(self: *Buffer, data: []const u8) void { 201 206 self._view.write(data); 202 207 } ··· 287 292 } 288 293 289 294 pub const Writer = struct { 290 - sb: *Buffer, 295 + w: *Buffer, 291 296 292 297 pub const Error = Allocator.Error; 293 298 pub const IOWriter = std.io.Writer(Writer, error{OutOfMemory}, Writer.write); 294 299 295 - fn init(sb: *Buffer) Writer { 296 - return .{.sb = sb}; 300 + fn init(w: *Buffer) Writer { 301 + return .{.w = w}; 297 302 } 298 303 299 304 pub fn write(self: Writer, data: []const u8) Allocator.Error!usize { 300 - try self.sb.write(data); 305 + try self.w.write(data); 301 306 return data.len; 302 307 } 303 308 }; ··· 309 314 } 310 315 311 316 test "growth" { 312 - var sb = try Buffer.init(t.allocator, 10); 313 - defer sb.deinit(); 317 + var w = try Buffer.init(t.allocator, 10); 318 + defer w.deinit(); 314 319 315 320 // we reset at the end of the loop, and things should work the exact same 316 321 // after a reset 317 322 for (0..5) |_| { 318 - try t.expectEqual(0, sb.len()); 319 - try sb.writeByte('o'); 320 - try t.expectEqual(1, sb.len()); 321 - try t.expectString("o", sb.string()); 322 - try t.expectEqual(true, sb.dynamic == null); 323 + try t.expectEqual(0, w.len()); 324 + try w.writeByte('o'); 325 + try t.expectEqual(1, w.len()); 326 + try t.expectString("o", w.string()); 327 + try t.expectEqual(true, w.dynamic == null); 323 328 324 329 // stays in static 325 - try sb.write("ver 9000!"); 326 - try t.expectEqual(10, sb.len()); 327 - try t.expectString("over 9000!", sb.string()); 328 - try t.expectEqual(true, sb.dynamic == null); 330 + try w.write("ver 9000!"); 331 + try t.expectEqual(10, w.len()); 332 + try t.expectString("over 9000!", w.string()); 333 + try t.expectEqual(true, w.dynamic == null); 329 334 330 335 // grows into dynamic 331 - try sb.write("!!!"); 332 - try t.expectEqual(13, sb.len()); 333 - try t.expectString("over 9000!!!!", sb.string()); 334 - try t.expectEqual(false, sb.dynamic == null); 336 + try w.write("!!!"); 337 + try t.expectEqual(13, w.len()); 338 + try t.expectString("over 9000!!!!", w.string()); 339 + try t.expectEqual(false, w.dynamic == null); 335 340 336 341 337 - try sb.write("If you were to run this code, you'd almost certainly see a segmentation fault (aka, segfault). We create a Response which involves creating an ArenaAllocator and from that, an Allocator. This allocator is then used to format our string. For the purpose of this example, we create a 2nd response and immediately free it. We need this for the same reason that warning1 in our first example printed an almost ok value: we want to re-initialize the memory in our init function stack."); 338 - try t.expectEqual(492, sb.len()); 339 - try t.expectString("over 9000!!!!If you were to run this code, you'd almost certainly see a segmentation fault (aka, segfault). We create a Response which involves creating an ArenaAllocator and from that, an Allocator. This allocator is then used to format our string. For the purpose of this example, we create a 2nd response and immediately free it. We need this for the same reason that warning1 in our first example printed an almost ok value: we want to re-initialize the memory in our init function stack.", sb.string()); 342 + try w.write("If you were to run this code, you'd almost certainly see a segmentation fault (aka, segfault). We create a Response which involves creating an ArenaAllocator and from that, an Allocator. This allocator is then used to format our string. For the purpose of this example, we create a 2nd response and immediately free it. We need this for the same reason that warning1 in our first example printed an almost ok value: we want to re-initialize the memory in our init function stack."); 343 + try t.expectEqual(492, w.len()); 344 + try t.expectString("over 9000!!!!If you were to run this code, you'd almost certainly see a segmentation fault (aka, segfault). We create a Response which involves creating an ArenaAllocator and from that, an Allocator. This allocator is then used to format our string. For the purpose of this example, we create a 2nd response and immediately free it. We need this for the same reason that warning1 in our first example printed an almost ok value: we want to re-initialize the memory in our init function stack.", w.string()); 340 345 341 - sb.reset(); 346 + w.reset(); 342 347 } 343 348 } 344 349 345 350 test "truncate" { 346 - var sb = try Buffer.init(t.allocator, 10); 347 - defer sb.deinit(); 351 + var w = try Buffer.init(t.allocator, 10); 352 + defer w.deinit(); 348 353 349 - sb.truncate(100); 350 - try t.expectEqual(0, sb.len()); 354 + w.truncate(100); 355 + try t.expectEqual(0, w.len()); 351 356 352 - try sb.write("hello world!1"); 357 + try w.write("hello world!1"); 353 358 354 - sb.truncate(0); 355 - try t.expectEqual(13, sb.len()); 356 - try t.expectString("hello world!1", sb.string()); 359 + w.truncate(0); 360 + try t.expectEqual(13, w.len()); 361 + try t.expectString("hello world!1", w.string()); 357 362 358 - sb.truncate(1); 359 - try t.expectEqual(12, sb.len()); 360 - try t.expectString("hello world!", sb.string()); 363 + w.truncate(1); 364 + try t.expectEqual(12, w.len()); 365 + try t.expectString("hello world!", w.string()); 361 366 362 - sb.truncate(5); 363 - try t.expectEqual(7, sb.len()); 364 - try t.expectString("hello w", sb.string()); 367 + w.truncate(5); 368 + try t.expectEqual(7, w.len()); 369 + try t.expectString("hello w", w.string()); 365 370 } 366 371 367 372 test "reset without clear" { 368 - var sb = try Buffer.init(t.allocator, 5); 369 - defer sb.deinit(); 373 + var w = try Buffer.init(t.allocator, 5); 374 + defer w.deinit(); 370 375 371 - try sb.write("hello world!1"); 372 - try t.expectString("hello world!1", sb.string()); 376 + try w.write("hello world!1"); 377 + try t.expectString("hello world!1", w.string()); 373 378 374 - sb.resetRetainingCapacity(); 375 - try t.expectEqual(0, sb.len()); 376 - try t.expectEqual(false, sb.dynamic == null); 377 - try sb.write("over 9000"); 378 - try sb.write("over 9000"); 379 + w.resetRetainingCapacity(); 380 + try t.expectEqual(0, w.len()); 381 + try t.expectEqual(false, w.dynamic == null); 382 + try w.write("over 9000"); 383 + try w.write("over 9000"); 379 384 } 380 385 381 386 test "fuzz" { ··· 391 396 const aa = arena.allocator(); 392 397 393 398 for (1..100) |_| { 394 - var sb = try Buffer.init(t.allocator, random.uintAtMost(u16, 1000) + 1); 395 - defer sb.deinit(); 399 + var w = try Buffer.init(t.allocator, random.uintAtMost(u16, 1000) + 1); 400 + defer w.deinit(); 396 401 397 402 for (1..100) |_| { 398 403 const input = testString(aa, random); 399 - try sb.write(input); 404 + try w.write(input); 400 405 try control.appendSlice(input); 401 - try t.expectString(control.items, sb.string()); 406 + try t.expectString(control.items, w.string()); 402 407 } 403 - sb.reset(); 408 + w.reset(); 404 409 control.clearRetainingCapacity(); 405 410 _ = arena.reset(.free_all); 406 411 } 407 412 } 408 413 409 414 test "writer" { 410 - var sb = try Buffer.init(t.allocator, 10); 411 - defer sb.deinit(); 415 + var w = try Buffer.init(t.allocator, 10); 416 + defer w.deinit(); 412 417 413 - try std.json.stringify(.{.over = 9000, .spice = "must flow", .ok = true}, .{}, sb.writer()); 414 - try t.expectString("{\"over\":9000,\"spice\":\"must flow\",\"ok\":true}", sb.string()); 418 + try std.json.stringify(.{.over = 9000, .spice = "must flow", .ok = true}, .{}, w.writer()); 419 + try t.expectString("{\"over\":9000,\"spice\":\"must flow\",\"ok\":true}", w.string()); 415 420 } 416 421 417 422 test "copy" { 418 - var sb = try Buffer.init(t.allocator, 10); 419 - defer sb.deinit(); 423 + var w = try Buffer.init(t.allocator, 10); 424 + defer w.deinit(); 420 425 421 - try sb.write("hello!!"); 422 - const c = try sb.copy(t.allocator); 426 + try w.write("hello!!"); 427 + const c = try w.copy(t.allocator); 423 428 defer t.allocator.free(c); 424 429 try t.expectString("hello!!", c); 425 430 } 426 431 427 432 test "write little" { 428 - var sb = try Buffer.init(t.allocator, 20); 429 - defer sb.deinit(); 430 - try sb.writeU64Little(11234567890123456789); 431 - try t.exectSlice(u8, &[_]u8{21, 129, 209, 7, 249, 51, 233, 155}, sb.string()); 433 + var w = try Buffer.init(t.allocator, 20); 434 + defer w.deinit(); 435 + try w.writeU64Little(11234567890123456789); 436 + try t.exectSlice(u8, &[_]u8{21, 129, 209, 7, 249, 51, 233, 155}, w.string()); 432 437 433 - try sb.writeU32Little(3283856184); 434 - try t.exectSlice(u8, &[_]u8{21, 129, 209, 7, 249, 51, 233, 155, 56, 171, 187, 195}, sb.string()); 438 + try w.writeU32Little(3283856184); 439 + try t.exectSlice(u8, &[_]u8{21, 129, 209, 7, 249, 51, 233, 155, 56, 171, 187, 195}, w.string()); 435 440 436 - try sb.writeU16Little(15000); 437 - try t.exectSlice(u8, &[_]u8{21, 129, 209, 7, 249, 51, 233, 155, 56, 171, 187, 195, 152, 58}, sb.string()); 441 + try w.writeU16Little(15000); 442 + try t.exectSlice(u8, &[_]u8{21, 129, 209, 7, 249, 51, 233, 155, 56, 171, 187, 195, 152, 58}, w.string()); 438 443 } 439 444 440 445 test "write big" { 441 - var sb = try Buffer.init(t.allocator, 20); 442 - defer sb.deinit(); 443 - try sb.writeU64Big(11234567890123456789); 444 - try t.exectSlice(u8, &[_]u8{155, 233, 51, 249, 7, 209, 129, 21}, sb.string()); 446 + var w = try Buffer.init(t.allocator, 20); 447 + defer w.deinit(); 448 + try w.writeU64Big(11234567890123456789); 449 + try t.exectSlice(u8, &[_]u8{155, 233, 51, 249, 7, 209, 129, 21}, w.string()); 445 450 446 - try sb.writeU32Big(3283856184); 447 - try t.exectSlice(u8, &[_]u8{155, 233, 51, 249, 7, 209, 129, 21, 195, 187, 171, 56}, sb.string()); 451 + try w.writeU32Big(3283856184); 452 + try t.exectSlice(u8, &[_]u8{155, 233, 51, 249, 7, 209, 129, 21, 195, 187, 171, 56}, w.string()); 448 453 449 - try sb.writeU16Big(15000); 450 - try t.exectSlice(u8, &[_]u8{155, 233, 51, 249, 7, 209, 129, 21, 195, 187, 171, 56, 58, 152}, sb.string()); 454 + try w.writeU16Big(15000); 455 + try t.exectSlice(u8, &[_]u8{155, 233, 51, 249, 7, 209, 129, 21, 195, 187, 171, 56, 58, 152}, w.string()); 451 456 } 452 457 453 458 test "skip & view" { 454 - var sb = try Buffer.init(t.allocator, 10); 455 - defer sb.deinit(); 459 + var w = try Buffer.init(t.allocator, 10); 460 + defer w.deinit(); 456 461 457 - const start = try sb.skip(4); 458 - try sb.write("hello world!!"); 462 + const start = try w.skip(4); 463 + try w.write("hello world!!"); 459 464 460 - var v = sb.view(start); 461 - v.writeU32Big(@intCast(sb.len() - 4)); 465 + var v = w.view(start); 466 + v.writeU32Big(@intCast(w.len() - 4)); 467 + 468 + try w.writeByte('\n'); 469 + try t.exectSlice(u8, &[_]u8{0, 0, 0, 13, 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', '!', '\n'}, w.string()); 470 + } 471 + 472 + test "writeAt" { 473 + var w = try Buffer.init(t.allocator, 200); 474 + defer w.deinit(); 475 + 476 + try w.write("hello"); 477 + try w.write(&.{0, 0, 0, 0, 0}); 478 + try w.write("world"); 462 479 463 - try sb.writeByte('\n'); 464 - try t.exectSlice(u8, &[_]u8{0, 0, 0, 13, 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', '!', '\n'}, sb.string()); 480 + w.writeAt(" ", 5); 481 + w.writeAt("123 ", 6); 482 + try t.expectString("hello 123 world", w.string()); 465 483 } 466 484 467 485 fn testString(allocator: Allocator, random: std.rand.Random) []const u8 {