defineAbbrev()
|
pub fn bitsVBR(_: *const BcWriter, value: anytype, comptime vbr_bits: usize) u16 {
comptime {
std.debug.assert(vbr_bits > 1);
if (@bitSizeOf(@TypeOf(value)) > 64) @compileError("Unsupported VBR block type: " ++ @typeName(@TypeOf(value)));
}
var bits: u16 = 0;
var in_buffer = bufValue(value, vbr_bits);
const continue_bit = @as(@TypeOf(in_buffer), 1) << @intCast(vbr_bits - 1);
const mask = continue_bit - 1;
// If input is larger than one VBR block can store
// then store vbr_bits - 1 bits and a continue bit
while (in_buffer > mask) {
bits += @intCast(vbr_bits);
in_buffer >>= @intCast(vbr_bits - 1);
}
// Store remaining bits
bits += @intCast(vbr_bits);
return bits;
}
pub fn write6BitChar(self: *BcWriter, c: u8) Error!void {
try self.writeBits(charTo6Bit(c), 6);
}
pub fn writeBlob(self: *BcWriter, blob: []const u8) Error!void {
const blob_word_size = std.mem.alignForward(usize, blob.len, 4);
try self.buffer.ensureUnusedCapacity(blob_word_size + 1);
self.alignTo32() catch unreachable;
const slice = self.buffer.addManyAsSliceAssumeCapacity(blob_word_size / 4);
const slice_bytes = std.mem.sliceAsBytes(slice);
@memcpy(slice_bytes[0..blob.len], blob);
@memset(slice_bytes[blob.len..], 0);
}
pub fn alignTo32(self: *BcWriter) Error!void {
if (self.bit_count == 0) return;
try self.buffer.append(std.mem.nativeToLittle(u32, self.bit_buffer));
self.bit_buffer = 0;
self.bit_count = 0;
}
pub fn enterTopBlock(self: *BcWriter, comptime SubBlock: type) Error!BlockWriter(SubBlock) {
return BlockWriter(SubBlock).init(self, 2, true);
}
fn BlockWriter(comptime Block: type) type {
return struct {
const Self = @This();
// The minimum abbrev id length based on the number of abbrevs present in the block
pub const abbrev_len = std.math.log2_int_ceil(
u6,
4 + (if (@hasDecl(Block, "abbrevs")) Block.abbrevs.len else 0),
);
start: usize,
bitcode: *BcWriter,
pub fn init(bitcode: *BcWriter, comptime parent_abbrev_len: u6, comptime define_abbrevs: bool) Error!Self {
try bitcode.writeBits(1, parent_abbrev_len);
try bitcode.writeVBR(Block.id, 8);
try bitcode.writeVBR(abbrev_len, 4);
try bitcode.alignTo32();
// We store the index of the block size and store a dummy value as the number of words in the block
const start = bitcode.length();
try bitcode.writeBits(0, 32);
var self = Self{
.start = start,
.bitcode = bitcode,
};
// Predefine all block abbrevs
if (define_abbrevs) {
inline for (Block.abbrevs) |Abbrev| {
try self.defineAbbrev(&Abbrev.ops);
}
}
return self;
}
pub fn enterSubBlock(self: Self, comptime SubBlock: type, comptime define_abbrevs: bool) Error!BlockWriter(SubBlock) {
return BlockWriter(SubBlock).init(self.bitcode, abbrev_len, define_abbrevs);
}
pub fn end(self: *Self) Error!void {
try self.bitcode.writeBits(0, abbrev_len);
try self.bitcode.alignTo32();
// Set the number of words in the block at the start of the block
self.bitcode.buffer.items[self.start] = std.mem.nativeToLittle(u32, @truncate(self.bitcode.length() - self.start - 1));
}
pub fn writeUnabbrev(self: *Self, code: u32, values: []const u64) Error!void {
try self.bitcode.writeBits(3, abbrev_len);
try self.bitcode.writeVBR(code, 6);
try self.bitcode.writeVBR(values.len, 6);
for (values) |val| {
try self.bitcode.writeVBR(val, 6);
}
}
pub fn writeAbbrev(self: *Self, params: anytype) Error!void {
return self.writeAbbrevAdapted(params, struct {
pub fn get(_: @This(), param: anytype, comptime _: []const u8) @TypeOf(param) {
return param;
}
}{});
}
pub fn abbrevId(comptime Abbrev: type) u32 {
inline for (Block.abbrevs, 0..) |abbrev, i| {
if (Abbrev == abbrev) return i + 4;
}
@compileError("Unknown abbrev: " ++ @typeName(Abbrev));
}
pub fn writeAbbrevAdapted(
self: *Self,
params: anytype,
adapter: anytype,
) Error!void {
const Abbrev = @TypeOf(params);
try self.bitcode.writeBits(comptime abbrevId(Abbrev), abbrev_len);
const fields = std.meta.fields(Abbrev);
// This abbreviation might only contain literals
if (fields.len == 0) return;
comptime var field_index: usize = 0;
inline for (Abbrev.ops) |ty| {
const field_name = fields[field_index].name;
const param = @field(params, field_name);
switch (ty) {
.literal => continue,
.fixed => |len| try self.bitcode.writeBits(adapter.get(param, field_name), len),
.fixed_runtime => |width_ty| try self.bitcode.writeBits(
adapter.get(param, field_name),
self.bitcode.getTypeWidth(width_ty),
),
.vbr => |len| try self.bitcode.writeVBR(adapter.get(param, field_name), len),
.char6 => try self.bitcode.write6BitChar(adapter.get(param, field_name)),
.blob => {
try self.bitcode.writeVBR(param.len, 6);
try self.bitcode.writeBlob(param);
},
.array_fixed => |len| {
try self.bitcode.writeVBR(param.len, 6);
for (param) |x| {
try self.bitcode.writeBits(adapter.get(x, field_name), len);
}
},
.array_fixed_runtime => |width_ty| {
try self.bitcode.writeVBR(param.len, 6);
for (param) |x| {
try self.bitcode.writeBits(
adapter.get(x, field_name),
self.bitcode.getTypeWidth(width_ty),
);
}
},
.array_vbr => |len| {
try self.bitcode.writeVBR(param.len, 6);
for (param) |x| {
try self.bitcode.writeVBR(adapter.get(x, field_name), len);
}
},
.array_char6 => {
try self.bitcode.writeVBR(param.len, 6);
for (param) |x| {
try self.bitcode.write6BitChar(adapter.get(x, field_name));
}
},
}
field_index += 1;
if (field_index == fields.len) break;
}
}
pub fn defineAbbrev(self: *Self, comptime ops: []const AbbrevOp) Error!void {
const bitcode = self.bitcode;
try bitcode.writeBits(2, abbrev_len);
// ops.len is not accurate because arrays are actually two ops
try bitcode.writeVBR(blk: {
var count: usize = 0;
inline for (ops) |op| {
count += switch (op) {
.literal, .fixed, .fixed_runtime, .vbr, .char6, .blob => 1,
.array_fixed, .array_fixed_runtime, .array_vbr, .array_char6 => 2,
};
}
break :blk count;
}, 5);
inline for (ops) |op| {
switch (op) {
.literal => |value| {
try bitcode.writeBits(1, 1);
try bitcode.writeVBR(value, 8);
},
.fixed => |width| {
try bitcode.writeBits(0, 1);
try bitcode.writeBits(1, 3);
try bitcode.writeVBR(width, 5);
},
.fixed_runtime => |width_ty| {
try bitcode.writeBits(0, 1);
try bitcode.writeBits(1, 3);
try bitcode.writeVBR(bitcode.getTypeWidth(width_ty), 5);
},
.vbr => |width| {
try bitcode.writeBits(0, 1);
try bitcode.writeBits(2, 3);
try bitcode.writeVBR(width, 5);
},
.char6 => {
try bitcode.writeBits(0, 1);
try bitcode.writeBits(4, 3);
},
.blob => {
try bitcode.writeBits(0, 1);
try bitcode.writeBits(5, 3);
},
.array_fixed => |width| {
// Array op
try bitcode.writeBits(0, 1);
try bitcode.writeBits(3, 3);
// Fixed or VBR op
try bitcode.writeBits(0, 1);
try bitcode.writeBits(1, 3);
try bitcode.writeVBR(width, 5);
},
.array_fixed_runtime => |width_ty| {
// Array op
try bitcode.writeBits(0, 1);
try bitcode.writeBits(3, 3);
// Fixed or VBR op
try bitcode.writeBits(0, 1);
try bitcode.writeBits(1, 3);
try bitcode.writeVBR(bitcode.getTypeWidth(width_ty), 5);
},
.array_vbr => |width| {
// Array op
try bitcode.writeBits(0, 1);
try bitcode.writeBits(3, 3);
// Fixed or VBR op
try bitcode.writeBits(0, 1);
try bitcode.writeBits(2, 3);
try bitcode.writeVBR(width, 5);
},
.array_char6 => {
// Array op
try bitcode.writeBits(0, 1);
try bitcode.writeBits(3, 3);
// Char6 op
try bitcode.writeBits(0, 1);
try bitcode.writeBits(4, 3);
},
}
}
}
};
}
};
}
fn charTo6Bit(c: u8) u8 {
return switch (c) {
'a'...'z' => c - 'a',
'A'...'Z' => c - 'A' + 26,
'0'...'9' => c - '0' + 52,
'.' => 62,
'_' => 63,
else => @panic("Failed to encode byte as 6-bit char"),
};
}
fn BufType(comptime T: type, comptime min_len: usize) type {
return std.meta.Int(.unsigned, @max(min_len, @bitSizeOf(switch (@typeInfo(T)) {
.comptime_int => u32,
.int => |info| if (info.signedness == .unsigned)
T
else
@compileError("Unsupported type: " ++ @typeName(T)),
.@"enum" => |info| info.tag_type,
.bool => u1,
.@"struct" => |info| switch (info.layout) {
.auto, .@"extern" => @compileError("Unsupported type: " ++ @typeName(T)),
.@"packed" => std.meta.Int(.unsigned, @bitSizeOf(T)),
},
else => @compileError("Unsupported type: " ++ @typeName(T)),
})));
}
fn bufValue(value: anytype, comptime min_len: usize) BufType(@TypeOf(value), min_len) {
return switch (@typeInfo(@TypeOf(value))) {
.comptime_int, .int => @intCast(value),
.@"enum" => @intFromEnum(value),
.bool => @intFromBool(value),
.@"struct" => @intCast(@as(std.meta.Int(.unsigned, @bitSizeOf(@TypeOf(value))), @bitCast(value))),
else => unreachable,
};
}
|