ztacacs/src/serde.zig
2023-09-21 10:54:27 -05:00

104 lines
3 KiB
Zig

const std = @import("std");
pub const SerdeError = error{
IllegalSize,
LeftoverBits,
NotEnoughBitsLeft,
NotEnoughBytesLeft,
};
pub const Reader = struct {
bytes: []const u8,
index: usize,
bits_left: usize,
pub fn init(bytes: []const u8) Reader {
return .{
.bytes = bytes,
.index = 0,
.bits_left = 8,
};
}
pub fn read(self: *Reader, comptime T: type) SerdeError!T {
if (self.index >= self.bytes.len) return error.NotEnoughBytesLeft;
return switch (@typeInfo(T)) {
.Int => try self.readInt(T),
.Bool => try self.readBool(T),
.Enum => try self.readEnum(T),
.Struct => try self.readStruct(T),
.Array => |array| {
var arr: [array.len]array.child = undefined;
var index: usize = 0;
while (index < array.len) : (index += 1) {
arr[index] = try self.read(array.child);
}
return arr;
},
else => @compileError("unsupported type"),
};
}
fn readInt(self: *Reader, comptime T: type) SerdeError!T {
const bits = @typeInfo(T).Int.bits;
if (bits < 8) {
if (bits > self.bits_left) return error.NotEnoughBitsLeft;
const shift: u3 = @truncate(self.bits_left - bits);
const mask = (1 << bits) - 1;
const b = (self.bytes[self.index] >> shift) & mask;
self.bits_left -= bits;
if (self.bits_left == 0) {
self.bits_left = 8;
self.index += 1;
}
return @intCast(b);
}
if (bits % 8 == 0 and self.bits_left != 8) return error.LeftoverBits;
if (bits % 8 != 0) return error.IllegalSize;
const size = bits / 8;
if (self.index + size > self.bytes.len) return error.NotEnoughBytesLeft;
const slice = self.bytes[self.index .. self.index + size];
const value = @as(*align(1) const T, @ptrCast(slice)).*;
self.index += size;
return std.mem.bigToNative(T, value);
}
fn readBool(self: *Reader, comptime T: type) SerdeError!T {
const x = try self.read(u1);
std.debug.print("bool {}\n", .{x});
return x == 1;
}
fn readEnum(self: *Reader, comptime T: type) SerdeError!T {
return @enumFromInt(try self.read(@typeInfo(T).Enum.tag_type));
}
fn readStruct(self: *Reader, comptime T: type) SerdeError!T {
if (@typeInfo(T).Struct.layout == .Packed) {
const value = try self.read(@typeInfo(T).Struct.backing_integer.?);
return @as(*const T, @ptrCast(&value)).*;
}
const fields = std.meta.fields(T);
var value: T = undefined;
inline for (fields) |field| {
@field(value, field.name) = try self.read(field.type);
}
return value;
}
pub fn isComplete(self: *Reader) bool {
return self.index >= self.bytes.len;
}
};