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 }