I’m starting my zig journey with the win32 api (a horrible choice) and ran into a bit of confusion regarding resizing a buffer that something else filled with null-terminated data, assuming I want to keep it null-terminated for use in later calls. The method that felt right doesn’t work, and two that do don’t feel right (cases below) !!
I wasn’t able to find any past discussion around this, and it feels like a pretty obvious harsh edge when interacting with C APIs, so I think I might just be missing something. Is there a reason realloc
doesn’t preserve sentinels (or the Allocator interface provide a variant that does, like reallocSentinel
or reallocZ
)?
Thanks much !!
const std = @import("std");
const target = "Wow, nice and roomy in here!";
fn stringProvider(string_pointer: [*:0]u8) c_uint {
@memcpy(string_pointer, target[0..target.len+1]);
return target.len;
}
const alloc = std.testing.allocator;
test "method 1: alloc with sentinel for type consistency" {
const buffer = try alloc.allocSentinel(u8, 9000, 0);
errdefer alloc.free(buffer);
const length = stringProvider(buffer.ptr);
// BORKED: no sentinel on return type, despite input having one
const minimal = try alloc.realloc(buffer, length);
defer alloc.free(minimal);
try std.testing.expectEqualSentinel(u8, 0, target, minimal); // complie error!
}
test "method 2: ptrCast to sentinel" {
const buffer = try alloc.alloc(u8, 9000);
errdefer alloc.free(buffer);
const length = stringProvider(@ptrCast(buffer.ptr));
// GROSS: Have to adjust lengths both in realloc call (or else free != alloc) and in returned slice
var minimal: [:0]u8 = @ptrCast(try alloc.realloc(buffer, length + 1));
minimal.len -= 1;
defer alloc.free(minimal);
try std.testing.expectEqualSentinel(u8, 0, target, minimal);
}
test "method 3: duplicate" {
const buffer = try alloc.alloc(u8, 9000);
defer alloc.free(buffer);
const length = stringProvider(@ptrCast(buffer.ptr));
// GROSS: Could be less efficient than resize/realloc? (depending on allocator implimentation)??
const minimal = try alloc.dupeZ(u8, buffer[0..length]);
defer alloc.free(minimal);
try std.testing.expectEqualSentinel(u8, 0, target, minimal);
}