tiny.trace.store.format.block
Defined in store.format.
API (15)
Actions
Public operations.
Builder.appendBuilder.canAppendBuilder.emptyBuilder.finishBuilder.initBuilder.resetView.advanceView.initView.peekcapacity
Types and contracts
Public types and contracts.
Values and defaults
Public values and defaults.
Source
Source: lib/trace/src/store/format/block.zig
zig
const std = @import("std");const event = @import("../../root.zig").event;const codec = @import("codec.zig");pub const target_bytes: usize = 256 * 1024;pub const header_bytes: usize = 16;pub const record_header_bytes: usize = 4;const magic = [_]u8{ 't', 'r', 'c', 'b' };const version: u8 = 1;const version_offset: usize = 4;const reserved_offset: usize = 5;const event_count_offset: usize = 8;const payload_bytes_offset: usize = 12;pub fn capacity(max_event_bytes: usize) error{CapacityOverflow}!usize { const event_record_bytes = std.math.add(usize, record_header_bytes, max_event_bytes) catch return error.CapacityOverflow; const minimum = std.math.add(usize, header_bytes, event_record_bytes) catch return error.CapacityOverflow; return @max(target_bytes, minimum);}pub const Builder = struct { bytes: []u8, len: usize = header_bytes, event_count: u32 = 0, pub fn init(bytes: []u8) Builder { std.debug.assert(bytes.len >= header_bytes + record_header_bytes); return .{ .bytes = bytes }; } pub fn empty(self: *const Builder) bool { return self.event_count == 0; } pub fn canAppend(self: *const Builder, event_bytes: usize) bool { const record_bytes = std.math.add(usize, record_header_bytes, event_bytes) catch return false; return record_bytes <= self.bytes.len - self.len; } pub fn append(self: *Builder, item: event.Event, event_bytes: usize) codec.Error![]u8 { std.debug.assert(event_bytes <= std.math.maxInt(u32)); std.debug.assert(self.canAppend(event_bytes)); if (self.event_count == std.math.maxInt(u32)) return error.CapacityOverflow; const prefix = self.bytes[self.len..][0..record_header_bytes]; std.mem.writeInt(u32, prefix, @intCast(event_bytes), .big); const start = self.len + record_header_bytes; const encoded = try codec.encode(self.bytes[start..][0..event_bytes], item); std.debug.assert(encoded.len == event_bytes); self.len = start + encoded.len; self.event_count += 1; return encoded; } pub fn finish(self: *Builder) []const u8 { std.debug.assert(!self.empty()); @memset(self.bytes[0..header_bytes], 0); @memcpy(self.bytes[0..magic.len], &magic); self.bytes[version_offset] = version; std.mem.writeInt(u32, self.bytes[event_count_offset..][0..4], self.event_count, .big); std.mem.writeInt(u32, self.bytes[payload_bytes_offset..][0..4], @intCast(self.len - header_bytes), .big); return self.bytes[0..self.len]; } pub fn reset(self: *Builder) void { self.len = header_bytes; self.event_count = 0; }};pub const View = struct { bytes: []u8, offset: usize = header_bytes, remaining: u32, pub fn init(bytes: []u8) error{InvalidBlock}!View { if (bytes.len < header_bytes) return error.InvalidBlock; if (!std.mem.eql(u8, bytes[0..magic.len], &magic)) return error.InvalidBlock; if (bytes[version_offset] != version) return error.InvalidBlock; for (bytes[reserved_offset..event_count_offset]) |byte| { if (byte != 0) return error.InvalidBlock; } const event_count = std.mem.readInt(u32, bytes[event_count_offset..][0..4], .big); const payload_bytes: usize = std.mem.readInt(u32, bytes[payload_bytes_offset..][0..4], .big); if (event_count == 0 or payload_bytes != bytes.len - header_bytes) return error.InvalidBlock; var offset = header_bytes; for (0..event_count) |_| { if (record_header_bytes > bytes.len - offset) return error.InvalidBlock; const event_bytes: usize = std.mem.readInt(u32, bytes[offset..][0..record_header_bytes], .big); offset += record_header_bytes; if (event_bytes == 0 or event_bytes > bytes.len - offset) return error.InvalidBlock; offset += event_bytes; } if (offset != bytes.len) return error.InvalidBlock; return .{ .bytes = bytes, .remaining = event_count }; } pub fn peek(self: *const View) ?[]u8 { if (self.remaining == 0) return null; const event_bytes: usize = std.mem.readInt( u32, self.bytes[self.offset..][0..record_header_bytes], .big, ); return self.bytes[self.offset + record_header_bytes ..][0..event_bytes]; } pub fn advance(self: *View) void { std.debug.assert(self.remaining != 0); const encoded = self.peek().?; self.offset += record_header_bytes + encoded.len; self.remaining -= 1; }};test "binary event block round trips bounded records" { var bytes: [512]u8 = undefined; var builder = Builder.init(&bytes); const first = event.Event.user(.{ .seq = 1 }, "first", "payload"); const second = event.Event.user(.{ .seq = 2 }, "second", ""); _ = try builder.append(first, try codec.encodedSize(first)); _ = try builder.append(second, try codec.encodedSize(second)); const encoded = builder.finish(); try std.testing.expectEqualSlices(u8, "trcb", encoded[0..magic.len]); var view = try View.init(@constCast(encoded)); try std.testing.expect(first.eqlForReplay(try codec.decode(view.peek().?))); view.advance(); try std.testing.expect(second.eqlForReplay(try codec.decode(view.peek().?))); view.advance(); try std.testing.expectEqual(@as(?[]u8, null), view.peek());}test "binary event block rejects truncation and trailing bytes" { var bytes: [256]u8 = undefined; var builder = Builder.init(&bytes); const item = event.Event.user(.{}, "event", "payload"); _ = try builder.append(item, try codec.encodedSize(item)); const encoded = builder.finish(); try std.testing.expectError(error.InvalidBlock, View.init(@constCast(encoded[0 .. encoded.len - 1]))); bytes[encoded.len] = 0; try std.testing.expectError(error.InvalidBlock, View.init(bytes[0 .. encoded.len + 1]));}Source: lib/trace/src/store/format/root.zig:1
zig
pub const block = @import("block.zig");Audit
| Definitions | 16 |
|---|---|
| Public names | 16 |
| Members | 6 |
| Version | 26.7.0 |
| Revision | daab053ee433 |