Search code examples
zig

zig std.mem.indexOf modifies []const u8


I'm debugging an issue where code that worked fine with zig v0.10.x breaks (badly) with zig v0.11.0 and I can't see why:


    const packet = [_]u8{
        0x17, 0x20, 0x00, 0x00, 0x78, 0x37, 0x2a, 0x18, 0x4e, 0x00, 0x00, 0x00, 0x02, 0x01, 0x03, 0x01,
        ....
    };

    const offset = 20;
    const bcd: []const u8 = try bcd2string(packet[offset..][0..7]);
    const expected: []const u8 = "20220823094706";

    std.debug.print(">>> BCD:         {any}\n", .{ bcd });
    std.debug.print(">>> expected:    {any}\n", .{ expected });
    std.debug.print(">>> eql(before): {any}\n", .{ std.mem.eql(u8, expected,bcd) });
    
    if (std.mem.indexOfDiff(u8, bcd, expected)) |index| {
        std.debug.print(">>> indexOfDiff: {any} {any} {any}\n", .{ expected, bcd, index });
    }

    std.debug.print(">>> eql(after):  {any}\n", .{ std.mem.eql(u8, expected,bcd) });
fn bcd2string(slice: []const u8) ![]const u8 {
    var buffer: [64:0]u8 = undefined;

    return try std.fmt.bufPrint(&buffer, "{s}", .{ std.fmt.fmtSliceHexLower(slice)});
}

The output looks like:

>>> BCD:         { 50, 48, 50, 50, 48, 56, 50, 51, 48, 57, 52, 55, 48, 54 }
>>> expected:    { 50, 48, 50, 50, 48, 56, 50, 51, 48, 57, 52, 55, 48, 54 }
>>> eql(before): true
>>> indexOfDiff: { 50, 48, 50, 50, 48, 56, 50, 51, 48, 57, 52, 55, 48, 54 } { 136, 252, 94, 228, 254, 127, 0, 0, 136, 252, 94, 228, 254, 127 } 0
>>> eql(after):  false

It looks very much std.mem.indexOfDiff is corrupting the slices which would be .. unexpected.

There's nothing obvious as to why in the Zig source or documentation - is there something just plain wrong with the code?


Solution

  • As I mentioned in the comments. Your bcd2string function returns a slice that points to the stack memory of the bcd2string function itself. The slice becomes invalid as soon as the function exits.

    There are several ways to fix this:

    • Inline the logic of bcd2string.
    • Pass an allocator to the bcd2string and let it allocate the result. But you'll have to make sure to free it afterwards.
    • Pass the buffer to the bcd2string. This seems to make the most sense for the given code.