feat: add decode and putU16 to row
This commit is contained in:
1 parent
a4eaa8e896
commit
875d8fe62f
1 file changed
+157
+157
@@ -158,3 +158,160 @@ pub const Row = struct {
|
||||
return out.toOwnedSlice();
|
||||
}
|
||||
|
||||
pub fn decode(allocator: Allocator, data: []const u8) !Row {
|
||||
var row = Row.init(allocator);
|
||||
errdefer row.deinit();
|
||||
var pos: usize = 0;
|
||||
const fieldCount = try readU32(data, &pos);
|
||||
var i: u32 = 0;
|
||||
while (i < fieldCount) : (i += 1) {
|
||||
const nameLen = try readU16(data, &pos);
|
||||
if (pos + nameLen > data.len) return error.Malformed;
|
||||
const name = data[pos .. pos + nameLen];
|
||||
pos += nameLen;
|
||||
if (pos >= data.len) return error.Malformed;
|
||||
const tag: ValueTag = std.meta.intToEnum(ValueTag, data[pos]) catch return error.Malformed;
|
||||
pos += 1;
|
||||
const value: Value = switch (tag) {
|
||||
.none => .{ .none = {} },
|
||||
.boolean => blk: {
|
||||
if (pos >= data.len) return error.Malformed;
|
||||
const b = data[pos];
|
||||
pos += 1;
|
||||
break :blk .{ .boolean = b != 0 };
|
||||
},
|
||||
.int => .{ .int = @bitCast(try readU64(data, &pos)) },
|
||||
.float => .{ .float = @bitCast(try readU64(data, &pos)) },
|
||||
.text => blk: {
|
||||
const len = try readU32(data, &pos);
|
||||
if (pos + len > data.len) return error.Malformed;
|
||||
const s = data[pos .. pos + len];
|
||||
pos += len;
|
||||
break :blk .{ .text = s };
|
||||
},
|
||||
.bytes => blk: {
|
||||
const len = try readU32(data, &pos);
|
||||
if (pos + len > data.len) return error.Malformed;
|
||||
const s = data[pos .. pos + len];
|
||||
pos += len;
|
||||
break :blk .{ .bytes = s };
|
||||
},
|
||||
};
|
||||
try row.set(name, value);
|
||||
}
|
||||
if (pos != data.len) return error.Malformed;
|
||||
return row;
|
||||
}
|
||||
};
|
||||
|
||||
fn putU16(out: *std.ArrayList(u8), v: u16) !void {
|
||||
var b: [2]u8 = undefined;
|
||||
std.mem.writeInt(u16, &b, v, .little);
|
||||
try out.appendSlice(&b);
|
||||
}
|
||||
|
||||
fn putU32(out: *std.ArrayList(u8), v: u32) !void {
|
||||
var b: [4]u8 = undefined;
|
||||
std.mem.writeInt(u32, &b, v, .little);
|
||||
try out.appendSlice(&b);
|
||||
}
|
||||
|
||||
fn putU64(out: *std.ArrayList(u8), v: u64) !void {
|
||||
var b: [8]u8 = undefined;
|
||||
std.mem.writeInt(u64, &b, v, .little);
|
||||
try out.appendSlice(&b);
|
||||
}
|
||||
|
||||
fn readU16(data: []const u8, pos: *usize) !u16 {
|
||||
if (pos.* + 2 > data.len) return error.Malformed;
|
||||
const v = std.mem.readInt(u16, data[pos.*..][0..2], .little);
|
||||
pos.* += 2;
|
||||
return v;
|
||||
}
|
||||
|
||||
fn readU32(data: []const u8, pos: *usize) !u32 {
|
||||
if (pos.* + 4 > data.len) return error.Malformed;
|
||||
const v = std.mem.readInt(u32, data[pos.*..][0..4], .little);
|
||||
pos.* += 4;
|
||||
return v;
|
||||
}
|
||||
|
||||
fn readU64(data: []const u8, pos: *usize) !u64 {
|
||||
if (pos.* + 8 > data.len) return error.Malformed;
|
||||
const v = std.mem.readInt(u64, data[pos.*..][0..8], .little);
|
||||
pos.* += 8;
|
||||
return v;
|
||||
}
|
||||
|
||||
/// Appends an order-preserving byte encoding of `value` to `out`, such that
|
||||
/// lexicographic byte comparison of two encodings matches `compare()` of the
|
||||
/// original values. Used to build index keys and primary-key suffixes that
|
||||
/// must sort correctly inside the LSM tree's byte-ordered keyspace.
|
||||
///
|
||||
/// Text/bytes payloads are escaped (0x00 -> 0x00 0xFF) and terminated with
|
||||
/// 0x00 0x00 so that concatenating several encoded values back to back stays
|
||||
/// unambiguous and order-preserving (no payload can be a byte-for-byte prefix
|
||||
/// of another once escaped and terminated).
|
||||
pub fn appendOrdered(out: *std.ArrayList(u8), value: Value) !void {
|
||||
try out.append(@intFromEnum(@as(ValueTag, value)));
|
||||
switch (value) {
|
||||
.none => {},
|
||||
.boolean => |v| try out.append(if (v) 1 else 0),
|
||||
.int => |v| {
|
||||
const flipped: u64 = @as(u64, @bitCast(v)) ^ 0x8000000000000000;
|
||||
var b: [8]u8 = undefined;
|
||||
std.mem.writeInt(u64, &b, flipped, .big);
|
||||
try out.appendSlice(&b);
|
||||
},
|
||||
.float => |v| {
|
||||
var bits: u64 = @bitCast(v);
|
||||
if (bits & 0x8000000000000000 != 0) {
|
||||
bits = ~bits;
|
||||
} else {
|
||||
bits |= 0x8000000000000000;
|
||||
}
|
||||
var b: [8]u8 = undefined;
|
||||
std.mem.writeInt(u64, &b, bits, .big);
|
||||
try out.appendSlice(&b);
|
||||
},
|
||||
.text, .bytes => |s| {
|
||||
for (s) |byte| {
|
||||
if (byte == 0x00) {
|
||||
try out.appendSlice(&.{ 0x00, 0xFF });
|
||||
} else {
|
||||
try out.append(byte);
|
||||
}
|
||||
}
|
||||
try out.appendSlice(&.{ 0x00, 0x00 });
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encodeOrdered(allocator: Allocator, value: Value) ![]u8 {
|
||||
var out = std.ArrayList(u8).init(allocator);
|
||||
errdefer out.deinit();
|
||||
try appendOrdered(&out, value);
|
||||
return out.toOwnedSlice();
|
||||
}
|
||||
|
||||
/// Encodes a single standalone Value using the same (non order-preserving)
|
||||
/// tag+payload format as a Row field, minus the field name. Used to store
|
||||
/// primary keys as index-entry values, where later exact reconstruction
|
||||
/// matters more than byte ordering.
|
||||
pub fn encodeValue(allocator: Allocator, value: Value) ![]u8 {
|
||||
var out = std.ArrayList(u8).init(allocator);
|
||||
errdefer out.deinit();
|
||||
try out.append(@intFromEnum(@as(ValueTag, value)));
|
||||
switch (value) {
|
||||
.none => {},
|
||||
.boolean => |v| try out.append(if (v) 1 else 0),
|
||||
.int => |v| try putU64(&out, @bitCast(v)),
|
||||
.float => |v| try putU64(&out, @bitCast(v)),
|
||||
.text, .bytes => |s| {
|
||||
try putU32(&out, @intCast(s.len));
|
||||
try out.appendSlice(s);
|
||||
},
|
||||
}
|
||||
return out.toOwnedSlice();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user