lib/sql/src/record.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2
3 pub const Error = error{
4 InvalidRecord,
5 ValueTooLarge,
6 };
7
8 pub const Kind = enum {
9 inline_value,
10 overflow,
11 };
12
13 pub const Overflow = struct {
14 len: u64,
15 first_page: u32,
16 };
17
18 pub const inline_tag: u8 = 0;
19 pub const overflow_tag: u8 = 1;
20 pub const overflow_size: usize = 13;
21
22 pub fn inlineSize(value: []const u8) Error!usize {
23 if (value.len == std.math.maxInt(usize)) return error.ValueTooLarge;
24 return value.len + 1;
25 }
26
27 pub fn encodeInline(target: []u8, value: []const u8) Error![]const u8 {
28 const len = try inlineSize(value);
29 if (target.len < len) return error.ValueTooLarge;
30 target[0] = inline_tag;
31 @memcpy(target[1..][0..value.len], value);
32 return target[0..len];
33 }
34
35 pub fn encodeOverflow(target: *[overflow_size]u8, value: Overflow) Error![]const u8 {
36 if (value.len == 0 or value.first_page == 0) return error.InvalidRecord;
37 target[0] = overflow_tag;
38 std.mem.writeInt(u64, target[1..][0..8], value.len, .big);
39 std.mem.writeInt(u32, target[9..][0..4], value.first_page, .big);
40 return target[0..];
41 }
42
43 pub fn kind(bytes: []const u8) Error!Kind {
44 if (bytes.len == 0) return error.InvalidRecord;
45 return switch (bytes[0]) {
46 inline_tag => .inline_value,
47 overflow_tag => .overflow,
48 else => error.InvalidRecord,
49 };
50 }
51
52 pub fn inlineValue(bytes: []const u8) Error![]const u8 {
53 if (bytes.len == 0 or bytes[0] != inline_tag) return error.InvalidRecord;
54 return bytes[1..];
55 }
56
57 pub fn overflow(bytes: []const u8) Error!Overflow {
58 if (bytes.len != overflow_size) return error.InvalidRecord;
59 if (bytes[0] != overflow_tag) return error.InvalidRecord;
60 const value = Overflow{
61 .len = std.mem.readInt(u64, bytes[1..][0..8], .big),
62 .first_page = std.mem.readInt(u32, bytes[9..][0..4], .big),
63 };
64 if (value.len == 0 or value.first_page == 0) return error.InvalidRecord;
65 return value;
66 }
67
68 test "record encodes inline values" {
69 var bytes: [16]u8 = undefined;
70 const encoded = try encodeInline(&bytes, "value");
71
72 try std.testing.expectEqual(Kind.inline_value, try kind(encoded));
73 try std.testing.expectEqualStrings("value", try inlineValue(encoded));
74 try std.testing.expectError(error.InvalidRecord, overflow(encoded));
75 }
76
77 test "record encodes overflow references" {
78 var bytes: [overflow_size]u8 = undefined;
79 const encoded = try encodeOverflow(&bytes, .{
80 .len = 9000,
81 .first_page = 42,
82 });
83 const decoded = try overflow(encoded);
84
85 try std.testing.expectEqual(Kind.overflow, try kind(encoded));
86 try std.testing.expectEqual(@as(u64, 9000), decoded.len);
87 try std.testing.expectEqual(@as(u32, 42), decoded.first_page);
88 try std.testing.expectError(error.InvalidRecord, inlineValue(encoded));
89 }