(De)serializer now uses enum instead of bool to determine packing mode (byte/bit).
Optional is initialized in a more straight-forward way by deserializer.
This commit is contained in:
parent
85edf55b73
commit
ba774c5697
50
std/io.zig
50
std/io.zig
@ -1088,6 +1088,11 @@ test "io.readLineSliceFrom" {
|
||||
testing.expectError(error.OutOfMemory, readLineSliceFrom(stream, buf[0..]));
|
||||
}
|
||||
|
||||
pub const Packing = enum {
|
||||
Byte, /// Pack data to byte alignment
|
||||
Bit, /// Pack data to bit alignment
|
||||
};
|
||||
|
||||
/// Creates a deserializer that deserializes types from any stream.
|
||||
/// If `is_packed` is true, the data stream is treated as bit-packed,
|
||||
/// otherwise data is expected to be packed to the smallest byte.
|
||||
@ -1097,18 +1102,18 @@ test "io.readLineSliceFrom" {
|
||||
/// which will be called when the deserializer is used to deserialize
|
||||
/// that type. It will pass a pointer to the type instance to deserialize
|
||||
/// into and a pointer to the deserializer struct.
|
||||
pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime Error: type) type {
|
||||
pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime Error: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
in_stream: if (is_packed) BitInStream(endian, Stream.Error) else *Stream,
|
||||
in_stream: if (packing == .Bit) BitInStream(endian, Stream.Error) else *Stream,
|
||||
|
||||
pub const Stream = InStream(Error);
|
||||
|
||||
pub fn init(in_stream: *Stream) Self {
|
||||
return Self{ .in_stream = switch (is_packed) {
|
||||
true => BitInStream(endian, Stream.Error).init(in_stream),
|
||||
else => in_stream,
|
||||
return Self{ .in_stream = switch (packing) {
|
||||
.Bit => BitInStream(endian, Stream.Error).init(in_stream),
|
||||
.Byte => in_stream,
|
||||
} };
|
||||
}
|
||||
|
||||
@ -1128,7 +1133,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E
|
||||
const Log2U = math.Log2Int(U);
|
||||
const int_size = (U.bit_count + 7) / 8;
|
||||
|
||||
if (is_packed) {
|
||||
if (packing == .Bit) {
|
||||
const result = try self.in_stream.readBitsNoEof(U, t_bit_count);
|
||||
return @bitCast(T, result);
|
||||
}
|
||||
@ -1211,8 +1216,8 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E
|
||||
//custom deserializer: fn(self: *Self, deserializer: var) !void
|
||||
if (comptime trait.hasFn("deserialize")(C)) return C.deserialize(ptr, self);
|
||||
|
||||
if (comptime trait.isPacked(C) and !is_packed) {
|
||||
var packed_deserializer = Deserializer(endian, true, Error).init(self.in_stream);
|
||||
if (comptime trait.isPacked(C) and packing != .Bit) {
|
||||
var packed_deserializer = Deserializer(endian, .Bit, Error).init(self.in_stream);
|
||||
return packed_deserializer.deserializeInto(ptr);
|
||||
}
|
||||
|
||||
@ -1276,14 +1281,13 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E
|
||||
ptr.* = null;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//This should ensure that the optional is set to non-null.
|
||||
ptr.* = OC(undefined);
|
||||
//The way non-pointer optionals are implemented ensures a pointer to them
|
||||
// will point to the value. The flag is stored at the end of that data.
|
||||
var val_ptr = @ptrCast(*OC, ptr);
|
||||
try self.deserializeInto(val_ptr);
|
||||
//This bit ensures the null flag isn't set. Any actual copying should be
|
||||
// optimized out... I hope.
|
||||
ptr.* = val_ptr.*;
|
||||
},
|
||||
builtin.TypeId.Enum => {
|
||||
var value = try self.deserializeInt(@TagType(C));
|
||||
@ -1310,24 +1314,24 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E
|
||||
/// which will be called when the serializer is used to serialize that type. It will
|
||||
/// pass a const pointer to the type instance to be serialized and a pointer
|
||||
/// to the serializer struct.
|
||||
pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, comptime Error: type) type {
|
||||
pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime Error: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
out_stream: if (is_packed) BitOutStream(endian, Stream.Error) else *Stream,
|
||||
out_stream: if (packing == .Bit) BitOutStream(endian, Stream.Error) else *Stream,
|
||||
|
||||
pub const Stream = OutStream(Error);
|
||||
|
||||
pub fn init(out_stream: *Stream) Self {
|
||||
return Self{ .out_stream = switch (is_packed) {
|
||||
true => BitOutStream(endian, Stream.Error).init(out_stream),
|
||||
else => out_stream,
|
||||
return Self{ .out_stream = switch (packing) {
|
||||
.Bit => BitOutStream(endian, Stream.Error).init(out_stream),
|
||||
.Byte => out_stream,
|
||||
} };
|
||||
}
|
||||
|
||||
/// Flushes any unwritten bits to the stream
|
||||
pub fn flush(self: *Self) Error!void {
|
||||
if (is_packed) return self.out_stream.flushBits();
|
||||
if (packing == .Bit) return self.out_stream.flushBits();
|
||||
}
|
||||
|
||||
fn serializeInt(self: *Self, value: var) Error!void {
|
||||
@ -1343,15 +1347,15 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, com
|
||||
|
||||
const u_value = @bitCast(U, value);
|
||||
|
||||
if (is_packed) return self.out_stream.writeBits(u_value, t_bit_count);
|
||||
if (packing == .Bit) return self.out_stream.writeBits(u_value, t_bit_count);
|
||||
|
||||
var buffer: [int_size]u8 = undefined;
|
||||
if (int_size == 1) buffer[0] = u_value;
|
||||
|
||||
for (buffer) |*byte, i| {
|
||||
const idx = switch (endian) {
|
||||
builtin.Endian.Big => int_size - i - 1,
|
||||
builtin.Endian.Little => i,
|
||||
.Big => int_size - i - 1,
|
||||
.Little => i,
|
||||
};
|
||||
const shift = @intCast(Log2U, idx * u8_bit_count);
|
||||
const v = u_value >> shift;
|
||||
@ -1374,8 +1378,8 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, com
|
||||
//custom serializer: fn(self: Self, serializer: var) !void
|
||||
if (comptime trait.hasFn("serialize")(T)) return T.serialize(value, self);
|
||||
|
||||
if (comptime trait.isPacked(T) and !is_packed) {
|
||||
var packed_serializer = Serializer(endian, true, Error).init(self.out_stream);
|
||||
if (comptime trait.isPacked(T) and packing != .Bit) {
|
||||
var packed_serializer = Serializer(endian, .Bit, Error).init(self.out_stream);
|
||||
try packed_serializer.serialize(value);
|
||||
try packed_serializer.flush();
|
||||
return;
|
||||
|
@ -318,7 +318,7 @@ test "BitStreams with File Stream" {
|
||||
try os.deleteFile(tmp_file_name);
|
||||
}
|
||||
|
||||
fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packed: bool) !void {
|
||||
fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
|
||||
//@NOTE: if this test is taking too long, reduce the maximum tested bitsize
|
||||
const max_test_bitsize = 128;
|
||||
|
||||
@ -333,12 +333,12 @@ fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_pa
|
||||
var out = io.SliceOutStream.init(data_mem[0..]);
|
||||
const OutError = io.SliceOutStream.Error;
|
||||
var out_stream = &out.stream;
|
||||
var serializer = io.Serializer(endian, is_packed, OutError).init(out_stream);
|
||||
var serializer = io.Serializer(endian, packing, OutError).init(out_stream);
|
||||
|
||||
var in = io.SliceInStream.init(data_mem[0..]);
|
||||
const InError = io.SliceInStream.Error;
|
||||
var in_stream = &in.stream;
|
||||
var deserializer = io.Deserializer(endian, is_packed, InError).init(in_stream);
|
||||
var deserializer = io.Deserializer(endian, packing, InError).init(in_stream);
|
||||
|
||||
comptime var i = 0;
|
||||
inline while (i <= max_test_bitsize) : (i += 1) {
|
||||
@ -366,21 +366,21 @@ fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_pa
|
||||
const extra_packed_byte = @boolToInt(total_bits % u8_bit_count > 0);
|
||||
const total_packed_bytes = (total_bits / u8_bit_count) + extra_packed_byte;
|
||||
|
||||
expect(in.pos == if (is_packed) total_packed_bytes else total_bytes);
|
||||
expect(in.pos == if (packing == .Bit) total_packed_bytes else total_bytes);
|
||||
|
||||
//Verify that empty error set works with serializer.
|
||||
//deserializer is covered by SliceInStream
|
||||
const NullError = io.NullOutStream.Error;
|
||||
var null_out = io.NullOutStream.init();
|
||||
var null_out_stream = &null_out.stream;
|
||||
var null_serializer = io.Serializer(endian, is_packed, NullError).init(null_out_stream);
|
||||
var null_serializer = io.Serializer(endian, packing, NullError).init(null_out_stream);
|
||||
try null_serializer.serialize(data_mem[0..]);
|
||||
try null_serializer.flush();
|
||||
}
|
||||
|
||||
test "Serializer/Deserializer Int" {
|
||||
try testIntSerializerDeserializer(builtin.Endian.Big, false);
|
||||
try testIntSerializerDeserializer(builtin.Endian.Little, false);
|
||||
try testIntSerializerDeserializer(.Big, .Byte);
|
||||
try testIntSerializerDeserializer(.Little, .Byte);
|
||||
// TODO these tests are disabled due to tripping an LLVM assertion
|
||||
// https://github.com/ziglang/zig/issues/2019
|
||||
//try testIntSerializerDeserializer(builtin.Endian.Big, true);
|
||||
@ -389,7 +389,7 @@ test "Serializer/Deserializer Int" {
|
||||
|
||||
fn testIntSerializerDeserializerInfNaN(
|
||||
comptime endian: builtin.Endian,
|
||||
comptime is_packed: bool,
|
||||
comptime packing: io.Packing,
|
||||
) !void {
|
||||
const mem_size = (16 * 2 + 32 * 2 + 64 * 2 + 128 * 2) / comptime meta.bitCount(u8);
|
||||
var data_mem: [mem_size]u8 = undefined;
|
||||
@ -397,12 +397,12 @@ fn testIntSerializerDeserializerInfNaN(
|
||||
var out = io.SliceOutStream.init(data_mem[0..]);
|
||||
const OutError = io.SliceOutStream.Error;
|
||||
var out_stream = &out.stream;
|
||||
var serializer = io.Serializer(endian, is_packed, OutError).init(out_stream);
|
||||
var serializer = io.Serializer(endian, packing, OutError).init(out_stream);
|
||||
|
||||
var in = io.SliceInStream.init(data_mem[0..]);
|
||||
const InError = io.SliceInStream.Error;
|
||||
var in_stream = &in.stream;
|
||||
var deserializer = io.Deserializer(endian, is_packed, InError).init(in_stream);
|
||||
var deserializer = io.Deserializer(endian, packing, InError).init(in_stream);
|
||||
|
||||
//@TODO: isInf/isNan not currently implemented for f128.
|
||||
try serializer.serialize(std.math.nan(f16));
|
||||
@ -432,17 +432,17 @@ fn testIntSerializerDeserializerInfNaN(
|
||||
}
|
||||
|
||||
test "Serializer/Deserializer Int: Inf/NaN" {
|
||||
try testIntSerializerDeserializerInfNaN(builtin.Endian.Big, false);
|
||||
try testIntSerializerDeserializerInfNaN(builtin.Endian.Little, false);
|
||||
try testIntSerializerDeserializerInfNaN(builtin.Endian.Big, true);
|
||||
try testIntSerializerDeserializerInfNaN(builtin.Endian.Little, true);
|
||||
try testIntSerializerDeserializerInfNaN(.Big, .Byte);
|
||||
try testIntSerializerDeserializerInfNaN(.Little, .Byte);
|
||||
try testIntSerializerDeserializerInfNaN(.Big, .Bit);
|
||||
try testIntSerializerDeserializerInfNaN(.Little, .Bit);
|
||||
}
|
||||
|
||||
fn testAlternateSerializer(self: var, serializer: var) !void {
|
||||
try serializer.serialize(self.f_f16);
|
||||
}
|
||||
|
||||
fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packed: bool) !void {
|
||||
fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
|
||||
const ColorType = enum(u4) {
|
||||
RGB8 = 1,
|
||||
RA16 = 2,
|
||||
@ -529,12 +529,12 @@ fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packe
|
||||
var out = io.SliceOutStream.init(data_mem[0..]);
|
||||
const OutError = io.SliceOutStream.Error;
|
||||
var out_stream = &out.stream;
|
||||
var serializer = io.Serializer(endian, is_packed, OutError).init(out_stream);
|
||||
var serializer = io.Serializer(endian, packing, OutError).init(out_stream);
|
||||
|
||||
var in = io.SliceInStream.init(data_mem[0..]);
|
||||
const InError = io.SliceInStream.Error;
|
||||
var in_stream = &in.stream;
|
||||
var deserializer = io.Deserializer(endian, is_packed, InError).init(in_stream);
|
||||
var deserializer = io.Deserializer(endian, packing, InError).init(in_stream);
|
||||
|
||||
try serializer.serialize(my_inst);
|
||||
|
||||
@ -543,13 +543,13 @@ fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packe
|
||||
}
|
||||
|
||||
test "Serializer/Deserializer generic" {
|
||||
try testSerializerDeserializer(builtin.Endian.Big, false);
|
||||
try testSerializerDeserializer(builtin.Endian.Little, false);
|
||||
try testSerializerDeserializer(builtin.Endian.Big, true);
|
||||
try testSerializerDeserializer(builtin.Endian.Little, true);
|
||||
try testSerializerDeserializer(builtin.Endian.Big, .Byte);
|
||||
try testSerializerDeserializer(builtin.Endian.Little, .Byte);
|
||||
try testSerializerDeserializer(builtin.Endian.Big, .Bit);
|
||||
try testSerializerDeserializer(builtin.Endian.Little, .Bit);
|
||||
}
|
||||
|
||||
fn testBadData(comptime endian: builtin.Endian, comptime is_packed: bool) !void {
|
||||
fn testBadData(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
|
||||
const E = enum(u14) {
|
||||
One = 1,
|
||||
Two = 2,
|
||||
@ -568,12 +568,12 @@ fn testBadData(comptime endian: builtin.Endian, comptime is_packed: bool) !void
|
||||
var out = io.SliceOutStream.init(data_mem[0..]);
|
||||
const OutError = io.SliceOutStream.Error;
|
||||
var out_stream = &out.stream;
|
||||
var serializer = io.Serializer(endian, is_packed, OutError).init(out_stream);
|
||||
var serializer = io.Serializer(endian, packing, OutError).init(out_stream);
|
||||
|
||||
var in = io.SliceInStream.init(data_mem[0..]);
|
||||
const InError = io.SliceInStream.Error;
|
||||
var in_stream = &in.stream;
|
||||
var deserializer = io.Deserializer(endian, is_packed, InError).init(in_stream);
|
||||
var deserializer = io.Deserializer(endian, packing, InError).init(in_stream);
|
||||
|
||||
try serializer.serialize(u14(3));
|
||||
expectError(error.InvalidEnumTag, deserializer.deserialize(A));
|
||||
@ -584,8 +584,8 @@ fn testBadData(comptime endian: builtin.Endian, comptime is_packed: bool) !void
|
||||
}
|
||||
|
||||
test "Deserializer bad data" {
|
||||
try testBadData(builtin.Endian.Big, false);
|
||||
try testBadData(builtin.Endian.Little, false);
|
||||
try testBadData(builtin.Endian.Big, true);
|
||||
try testBadData(builtin.Endian.Little, true);
|
||||
try testBadData(.Big, .Byte);
|
||||
try testBadData(.Little, .Byte);
|
||||
try testBadData(.Big, .Bit);
|
||||
try testBadData(.Little, .Bit);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user