Skip to content

Commit de55303

Browse files
committed
feat: support vector encoding
1 parent 9c0caa6 commit de55303

File tree

4 files changed

+82
-53
lines changed

4 files changed

+82
-53
lines changed

src/decoder.zig

Lines changed: 43 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,15 @@ pub fn decodeAlloc(comptime T: type, allocator: std.mem.Allocator, data: []const
2828
},
2929
.optional => |info| decodeOption(info.child, allocator, data, decodeAlloc),
3030
.pointer => |info| {
31-
if (info.size == .slice and info.child == u8) {
32-
return decodeStringAlloc(allocator, data);
31+
if (info.size == .slice) {
32+
return decodeSlice(info.child, allocator, data, decodeAlloc);
3333
} else {
3434
return error.UnsupportedType;
3535
}
3636
},
37+
.array => |info| {
38+
return decodeSlice(info.child, allocator, data, decodeAlloc);
39+
},
3740
.@"struct" => {
3841
return decodeTuple(T, allocator, data);
3942
},
@@ -94,24 +97,46 @@ fn decodeCompact(comptime T: type, data: []const u8) !DecodeResult(T) {
9497
}
9598
}
9699

97-
fn decodeStringAlloc(allocator: std.mem.Allocator, data: []const u8) !DecodeResult([]const u8) {
98-
// Use u64 to handle larger string lengths, but ensure they fit in usize
99-
const length = try decodeCompact(u64, data);
100-
const start = length.bytes_read;
101-
102-
// Check if length fits in usize
103-
if (length.value > std.math.maxInt(usize)) return error.BigIntegerNotSupported;
104-
105-
const len: usize = @intCast(length.value);
106-
const end = start + len;
100+
fn decodeSlice(comptime T: type, allocator: std.mem.Allocator, data: []const u8, decoder: anytype) !DecodeResult(if (T == u8) []const u8 else []T) {
101+
const length = try decodeCompact(u32, data);
102+
var offset = length.bytes_read;
107103

108-
if (data.len < end) return error.InsufficientData;
104+
if (T == u8) {
105+
// For strings, just copy the raw bytes
106+
const end = offset + length.value;
107+
if (data.len < end) return error.InsufficientData;
108+
109+
const duped = try allocator.dupe(u8, data[offset..end]);
110+
return .{
111+
.value = duped,
112+
.bytes_read = end,
113+
};
114+
} else {
115+
// For other types, decode each element individually
116+
var items = try allocator.alloc(T, length.value);
117+
errdefer allocator.free(items);
118+
119+
var i: usize = 0;
120+
while (i < length.value) : (i += 1) {
121+
if (offset >= data.len) return error.UnexpectedEndOfData;
122+
// Use function parameter introspection to check the decoder signature
123+
const decoder_info = @typeInfo(@TypeOf(decoder));
124+
const is_allocator_first_param = decoder_info.@"fn".params.len == 2 and
125+
decoder_info.@"fn".params[0].type orelse void == std.mem.Allocator;
126+
const is_type_first_param = decoder_info.@"fn".params.len == 3 and
127+
decoder_info.@"fn".params[0].type orelse void == type;
128+
const result = if (is_allocator_first_param)
129+
try decoder(allocator, data[offset..])
130+
else if (is_type_first_param)
131+
try decoder(T, allocator, data[offset..])
132+
else
133+
try decoder(data[offset..]);
134+
items[i] = result.value;
135+
offset += result.bytes_read;
136+
}
109137

110-
const duped = try allocator.dupe(u8, data[start..end]);
111-
return .{
112-
.value = duped,
113-
.bytes_read = end,
114-
};
138+
return .{ .value = items, .bytes_read = offset };
139+
}
115140
}
116141

117142
fn decodeBool(data: []const u8) !DecodeResult(bool) {
@@ -148,31 +173,6 @@ fn decodeOption(comptime T: type, allocator: std.mem.Allocator, data: []const u8
148173
};
149174
}
150175

151-
fn decodeArray(comptime T: type, allocator: std.mem.Allocator, data: []const u8, decoder: anytype) !DecodeResult([]T) {
152-
const length = try decodeCompact(u32, data);
153-
var offset = length.bytes_read;
154-
155-
var items = try allocator.alloc(T, length.value);
156-
errdefer allocator.free(items);
157-
158-
var i: usize = 0;
159-
while (i < length.value) : (i += 1) {
160-
if (offset >= data.len) return error.UnexpectedEndOfData;
161-
// Use function parameter introspection to check if the first parameter is an allocator
162-
const decoder_info = @typeInfo(@TypeOf(decoder));
163-
const is_allocator_first_param = decoder_info.@"fn".params.len == 2 and
164-
decoder_info.@"fn".params[0].type orelse void == std.mem.Allocator;
165-
const result = if (is_allocator_first_param)
166-
try decoder(allocator, data[offset..])
167-
else
168-
try decoder(data[offset..]);
169-
items[i] = result.value;
170-
offset += result.bytes_read;
171-
}
172-
173-
return .{ .value = items, .bytes_read = offset };
174-
}
175-
176176
fn decodeUnsigned(comptime T: type, data: []const u8) !DecodeResult(T) {
177177
const size = @sizeOf(T);
178178
if (data.len < size) return error.InsufficientData;

src/encoder.zig

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -180,16 +180,14 @@ fn encode(value: anytype, buffer: []u8) !usize {
180180
}
181181
},
182182
.pointer => |info| {
183-
if (info.size == .slice and info.child == u8) {
184-
return encodeByteSlice(value, buffer);
183+
if (info.size == .slice) {
184+
return encodeArray(info.child, value, buffer, encode);
185185
} else if (info.size == .one and @typeInfo(info.child) == .array) {
186186
// Handle pointers to arrays (like string literals)
187187
const array_info = @typeInfo(info.child).array;
188-
if (array_info.child == u8) {
189-
// Convert pointer to array into a slice
190-
const slice = value[0..array_info.len];
191-
return encodeByteSlice(slice, buffer);
192-
}
188+
// Convert pointer to array into a slice
189+
const slice = value[0..array_info.len];
190+
return encodeFixedArray(info.child, info.len, slice, buffer, encode);
193191
}
194192

195193
std.debug.print("Unsupported type: {s}\n", .{@typeName(T)});

src/root.zig

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,31 @@
11
pub const encoder = @import("encoder.zig");
22
pub const decoder = @import("decoder.zig");
33

4+
test "encode-and-decode-array" {
5+
const std = @import("std");
6+
const allocator = std.testing.allocator;
7+
8+
const test_cases = [_]struct {
9+
value: []const u32,
10+
encoded: []const u8,
11+
}{
12+
.{ .value = &[_]u32{ 1, 2, 3 }, .encoded = &[_]u8{ 0x0c, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00 } },
13+
};
14+
15+
for (test_cases) |tc| {
16+
// Test encoding
17+
const buffer = try encoder.encodeAlloc(allocator, tc.value);
18+
defer allocator.free(buffer);
19+
try std.testing.expectEqualSlices(u8, tc.encoded, buffer);
20+
21+
// Test decoding
22+
const result = try decoder.decodeAlloc([]u32, allocator, tc.encoded);
23+
defer allocator.free(result.value);
24+
try std.testing.expectEqual(result.bytes_read, tc.encoded.len);
25+
try std.testing.expectEqualSlices(u32, tc.value, result.value);
26+
}
27+
}
28+
429
test "encode-and-decode-tuple" {
530
const std = @import("std");
631
const allocator = std.testing.allocator;

src/util.zig

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,23 @@ pub fn calculateEncodedSize(value: anytype) !usize {
1818
}
1919
},
2020
.pointer => |info| {
21-
if (info.size == .slice and info.child == u8) {
22-
return calculateCompactSize(@intCast(value.len)) + value.len;
21+
if (info.size == .slice) {
22+
if (info.child == u8) {
23+
return calculateCompactSize(@intCast(value.len)) + value.len;
24+
} else {
25+
return calculateCompactSize(@intCast(value.len)) + (value.len * @sizeOf(info.child));
26+
}
2327
} else if (info.size == .one and @typeInfo(info.child) == .array) {
2428
// Handle pointers to arrays (like string literals)
2529
const array_info = @typeInfo(info.child).array;
2630
if (array_info.child == u8) {
2731
return calculateCompactSize(@intCast(array_info.len)) + array_info.len;
32+
} else {
33+
return calculateCompactSize(@intCast(array_info.len)) + (array_info.len * @sizeOf(array_info.child));
2834
}
2935
}
3036

31-
std.debug.print("Unsupported type: {s}\n", .{@typeName(T)});
37+
std.debug.print("Unsupported type: {s}, {any}\n", .{ @typeName(T), @typeInfo(T) });
3238
return error.UnsupportedType;
3339
},
3440
.optional => {

0 commit comments

Comments
 (0)