128 lines
3.5 KiB
Zig
128 lines
3.5 KiB
Zig
|
|
/// Secondary index entry encoding
|
||
|
|
/// Index entries store pointers to primary keys, not full items
|
||
|
|
const std = @import("std");
|
||
|
|
|
||
|
|
/// Encode a primary key reference for storage in an index entry
|
||
|
|
/// Format: [pk_len:varint][pk:bytes][sk_len:varint][sk:bytes]?
|
||
|
|
/// Returns owned slice that caller must free
|
||
|
|
pub fn encodePrimaryKeyRef(
|
||
|
|
allocator: std.mem.Allocator,
|
||
|
|
pk_value: []const u8,
|
||
|
|
sk_value: ?[]const u8,
|
||
|
|
) ![]u8 {
|
||
|
|
var buf = std.ArrayList(u8).init(allocator);
|
||
|
|
errdefer buf.deinit();
|
||
|
|
const writer = buf.writer();
|
||
|
|
|
||
|
|
// Encode partition key
|
||
|
|
try encodeVarint(writer, pk_value.len);
|
||
|
|
try writer.writeAll(pk_value);
|
||
|
|
|
||
|
|
// Encode sort key if present
|
||
|
|
if (sk_value) |sk| {
|
||
|
|
try encodeVarint(writer, sk.len);
|
||
|
|
try writer.writeAll(sk);
|
||
|
|
}
|
||
|
|
|
||
|
|
return buf.toOwnedSlice();
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Decode a primary key reference from an index entry
|
||
|
|
/// Returns struct with owned slices that caller must free
|
||
|
|
pub fn decodePrimaryKeyRef(allocator: std.mem.Allocator, data: []const u8) !PrimaryKeyRef {
|
||
|
|
var decoder = BinaryDecoder.init(data);
|
||
|
|
|
||
|
|
// Decode partition key
|
||
|
|
const pk_len = try decoder.readVarint();
|
||
|
|
const pk = try decoder.readBytes(pk_len);
|
||
|
|
const owned_pk = try allocator.dupe(u8, pk);
|
||
|
|
errdefer allocator.free(owned_pk);
|
||
|
|
|
||
|
|
// Decode sort key if present
|
||
|
|
var owned_sk: ?[]u8 = null;
|
||
|
|
if (decoder.hasMore()) {
|
||
|
|
const sk_len = try decoder.readVarint();
|
||
|
|
const sk = try decoder.readBytes(sk_len);
|
||
|
|
owned_sk = try allocator.dupe(u8, sk);
|
||
|
|
}
|
||
|
|
|
||
|
|
return PrimaryKeyRef{
|
||
|
|
.pk = owned_pk,
|
||
|
|
.sk = owned_sk,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
pub const PrimaryKeyRef = struct {
|
||
|
|
pk: []u8,
|
||
|
|
sk: ?[]u8,
|
||
|
|
|
||
|
|
pub fn deinit(self: *PrimaryKeyRef, allocator: std.mem.Allocator) void {
|
||
|
|
allocator.free(self.pk);
|
||
|
|
if (self.sk) |sk| allocator.free(sk);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// ============================================================================
|
||
|
|
// Binary Decoder Helper
|
||
|
|
// ============================================================================
|
||
|
|
|
||
|
|
const BinaryDecoder = struct {
|
||
|
|
data: []const u8,
|
||
|
|
pos: usize,
|
||
|
|
|
||
|
|
pub fn init(data: []const u8) BinaryDecoder {
|
||
|
|
return .{ .data = data, .pos = 0 };
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn readBytes(self: *BinaryDecoder, len: usize) ![]const u8 {
|
||
|
|
if (self.pos + len > self.data.len) return error.UnexpectedEndOfData;
|
||
|
|
const bytes = self.data[self.pos .. self.pos + len];
|
||
|
|
self.pos += len;
|
||
|
|
return bytes;
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn readVarint(self: *BinaryDecoder) !usize {
|
||
|
|
var result: usize = 0;
|
||
|
|
var shift: u6 = 0;
|
||
|
|
|
||
|
|
while (self.pos < self.data.len) {
|
||
|
|
const byte = self.data[self.pos];
|
||
|
|
self.pos += 1;
|
||
|
|
|
||
|
|
result |= @as(usize, byte & 0x7F) << shift;
|
||
|
|
|
||
|
|
if ((byte & 0x80) == 0) {
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
shift += 7;
|
||
|
|
if (shift >= 64) return error.VarintOverflow;
|
||
|
|
}
|
||
|
|
|
||
|
|
return error.UnexpectedEndOfData;
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn hasMore(self: *BinaryDecoder) bool {
|
||
|
|
return self.pos < self.data.len;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// ============================================================================
|
||
|
|
// Varint encoding (consistent with key_codec and item_codec)
|
||
|
|
// ============================================================================
|
||
|
|
|
||
|
|
fn encodeVarint(writer: anytype, value: usize) !void {
|
||
|
|
var v = value;
|
||
|
|
while (true) {
|
||
|
|
const byte = @as(u8, @intCast(v & 0x7F));
|
||
|
|
v >>= 7;
|
||
|
|
|
||
|
|
if (v == 0) {
|
||
|
|
try writer.writeByte(byte);
|
||
|
|
return;
|
||
|
|
} else {
|
||
|
|
try writer.writeByte(byte | 0x80);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|