diff --git a/src/lib.zig b/src/lib.zig index 56a869e..5325d27 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -512,6 +512,14 @@ pub fn deserialize(T: type, serialized: []const u8, out: *T, allocator: ?Allocat try deserialize(ptr.child, serialized[i * pitch .. (i + 1) * pitch], &out.*[i], allocator); } } else { + // Empty list of variable-size elements is encoded as zero bytes + // (no offset table is needed when there are no elements). + if (serialized.len == 0) { + if (allocator) |alloc| { + out.* = try alloc.alloc(ptr.child, 0); + } + return; + } // read the first index, determine when the "variable size" list ends, // and determine the size of the item as a result. if (serialized.len < 4) return error.OffsetExceedsSize; diff --git a/src/tests.zig b/src/tests.zig index 5379013..ab62840 100644 --- a/src/tests.zig +++ b/src/tests.zig @@ -2429,6 +2429,24 @@ test "deserialize struct: fixed-field read past buffer" { try expectError(error.OffsetExceedsSize, deserialize(S, &buf, &out, std.testing.allocator)); } +test "deserialize: empty list of variable-size elements" { + const S = struct { + items: [][]const u8, + }; + const empty: [][]const u8 = &.{}; + const original = S{ .items = empty }; + + var list: ArrayList(u8) = .empty; + defer list.deinit(std.testing.allocator); + try serialize(S, original, &list, std.testing.allocator); + + var out: S = undefined; + try deserialize(S, list.items, &out, std.testing.allocator); + defer std.testing.allocator.free(out.items); + + try expect(out.items.len == 0); +} + test "deserialize struct: nested variable container truncated in outer slice" { const Inner = struct { x: u32,