feat(sstable): add block builder and writer
This commit is contained in:
1 parent
52ef9f0686
commit
7105d37bd5
1 file changed
+157
+157
@@ -0,0 +1,157 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const encode = @import("encode.zig");
|
||||||
|
const Bloom = @import("bloom.zig").Bloom;
|
||||||
|
const cache = @import("cache.zig");
|
||||||
|
const compress = @import("compress.zig");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const Kind = encode.Kind;
|
||||||
|
const Record = encode.Record;
|
||||||
|
const BlockCache = cache.BlockCache;
|
||||||
|
const BlockKey = cache.BlockKey;
|
||||||
|
const Codec = compress.Codec;
|
||||||
|
const readU32 = encode.readU32;
|
||||||
|
const readU64 = encode.readU64;
|
||||||
|
|
||||||
|
pub const OwnedHit = struct {
|
||||||
|
seq: u64,
|
||||||
|
kind: Kind,
|
||||||
|
value: []u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn order(a: []const u8, b: []const u8) std.math.Order {
|
||||||
|
return std.mem.order(u8, a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
const magic: u32 = 0xC0BA1701;
|
||||||
|
const version: u32 = 1;
|
||||||
|
const footerLen = 4 + 4 + 8 + 4 + 8 + 4 + 8 + 8;
|
||||||
|
const recordHeader = 1 + 8 + 4 + 4;
|
||||||
|
const defaultBlockTarget = 4096;
|
||||||
|
|
||||||
|
pub const Builder = struct {
|
||||||
|
allocator: Allocator,
|
||||||
|
file: std.fs.File,
|
||||||
|
offset: u64,
|
||||||
|
blockBuf: std.ArrayList(u8),
|
||||||
|
blockFirstKey: std.ArrayList(u8),
|
||||||
|
indexBuf: std.ArrayList(u8),
|
||||||
|
numEntries: u64,
|
||||||
|
logicalBytes: u64,
|
||||||
|
maxSeq: u64,
|
||||||
|
bloom: Bloom,
|
||||||
|
blockTarget: usize,
|
||||||
|
|
||||||
|
pub fn init(allocator: Allocator, dir: std.fs.Dir, name: []const u8, expectedKeys: usize) !Builder {
|
||||||
|
const file = try dir.createFile(name, .{ .truncate = true });
|
||||||
|
return .{
|
||||||
|
.allocator = allocator,
|
||||||
|
.file = file,
|
||||||
|
.offset = 0,
|
||||||
|
.blockBuf = std.ArrayList(u8).init(allocator),
|
||||||
|
.blockFirstKey = std.ArrayList(u8).init(allocator),
|
||||||
|
.indexBuf = std.ArrayList(u8).init(allocator),
|
||||||
|
.numEntries = 0,
|
||||||
|
.logicalBytes = 0,
|
||||||
|
.maxSeq = 0,
|
||||||
|
.bloom = try Bloom.init(allocator, expectedKeys, 0.01),
|
||||||
|
.blockTarget = defaultBlockTarget,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Builder) void {
|
||||||
|
self.blockBuf.deinit();
|
||||||
|
self.blockFirstKey.deinit();
|
||||||
|
self.indexBuf.deinit();
|
||||||
|
self.bloom.deinit();
|
||||||
|
self.file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(self: *Builder, record: Record) !void {
|
||||||
|
if (self.blockBuf.items.len == 0) {
|
||||||
|
self.blockFirstKey.clearRetainingCapacity();
|
||||||
|
try self.blockFirstKey.appendSlice(record.key);
|
||||||
|
}
|
||||||
|
const writer = encode.ByteWriter.init(&self.blockBuf);
|
||||||
|
try writer.byte(@intFromEnum(record.kind));
|
||||||
|
try writer.putU64(record.seq);
|
||||||
|
try writer.putU32(@intCast(record.key.len));
|
||||||
|
try writer.putU32(@intCast(record.value.len));
|
||||||
|
try writer.slice(record.key);
|
||||||
|
try writer.slice(record.value);
|
||||||
|
|
||||||
|
self.bloom.add(record.key);
|
||||||
|
self.numEntries += 1;
|
||||||
|
self.logicalBytes += recordHeader + record.key.len + record.value.len;
|
||||||
|
if (record.seq > self.maxSeq) self.maxSeq = record.seq;
|
||||||
|
|
||||||
|
if (self.blockBuf.items.len >= self.blockTarget) try self.flushBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flushBlock(self: *Builder) !void {
|
||||||
|
if (self.blockBuf.items.len == 0) return;
|
||||||
|
const originalLen: u32 = @intCast(self.blockBuf.items.len);
|
||||||
|
|
||||||
|
const encoded = try compress.encode(self.allocator, self.blockBuf.items);
|
||||||
|
defer encoded.deinit(self.allocator);
|
||||||
|
|
||||||
|
var physical = std.ArrayList(u8).init(self.allocator);
|
||||||
|
defer physical.deinit();
|
||||||
|
const physWriter = encode.ByteWriter.init(&physical);
|
||||||
|
try physWriter.byte(@intFromEnum(encoded.codec));
|
||||||
|
try physWriter.putU32(originalLen);
|
||||||
|
try physWriter.slice(encoded.bytes);
|
||||||
|
|
||||||
|
const physLen: u32 = @intCast(physical.items.len);
|
||||||
|
try self.file.writeAll(physical.items);
|
||||||
|
var crcBuf: [4]u8 = undefined;
|
||||||
|
std.mem.writeInt(u32, &crcBuf, encode.crc(physical.items), encode.endian);
|
||||||
|
try self.file.writeAll(&crcBuf);
|
||||||
|
|
||||||
|
const indexWriter = encode.ByteWriter.init(&self.indexBuf);
|
||||||
|
try indexWriter.putU64(self.offset);
|
||||||
|
try indexWriter.putU32(physLen);
|
||||||
|
try indexWriter.putU32(@intCast(self.blockFirstKey.items.len));
|
||||||
|
try indexWriter.slice(self.blockFirstKey.items);
|
||||||
|
|
||||||
|
self.offset += physLen + 4;
|
||||||
|
self.blockBuf.clearRetainingCapacity();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn finish(self: *Builder) !void {
|
||||||
|
try self.flushBlock();
|
||||||
|
|
||||||
|
var bloomBuf = std.ArrayList(u8).init(self.allocator);
|
||||||
|
defer bloomBuf.deinit();
|
||||||
|
try self.bloom.serialize(encode.ByteWriter.init(&bloomBuf));
|
||||||
|
const bloomOffset = self.offset;
|
||||||
|
const bloomLen: u32 = @intCast(bloomBuf.items.len);
|
||||||
|
try self.writeBlock(bloomBuf.items);
|
||||||
|
|
||||||
|
const indexOffset = self.offset;
|
||||||
|
const indexLen: u32 = @intCast(self.indexBuf.items.len);
|
||||||
|
try self.writeBlock(self.indexBuf.items);
|
||||||
|
|
||||||
|
var footer = std.ArrayList(u8).init(self.allocator);
|
||||||
|
defer footer.deinit();
|
||||||
|
const fw = encode.ByteWriter.init(&footer);
|
||||||
|
try fw.putU32(magic);
|
||||||
|
try fw.putU32(version);
|
||||||
|
try fw.putU64(indexOffset);
|
||||||
|
try fw.putU32(indexLen);
|
||||||
|
try fw.putU64(bloomOffset);
|
||||||
|
try fw.putU32(bloomLen);
|
||||||
|
try fw.putU64(self.numEntries);
|
||||||
|
try fw.putU64(self.maxSeq);
|
||||||
|
try self.file.writeAll(footer.items);
|
||||||
|
try self.file.sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn writeBlock(self: *Builder, bytes: []const u8) !void {
|
||||||
|
try self.file.writeAll(bytes);
|
||||||
|
var crcBuf: [4]u8 = undefined;
|
||||||
|
std.mem.writeInt(u32, &crcBuf, encode.crc(bytes), encode.endian);
|
||||||
|
try self.file.writeAll(&crcBuf);
|
||||||
|
self.offset += bytes.len + 4;
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in new issue
Block a user