104 lines
3 KiB
Zig
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;
|
|
}
|
|
};
|