authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-04-03 18:13:25-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2019-04-03 18:13:25-04:00
log4795f161e689ef6bea563278c6a93c9ad2610ab7
tree0c2421ac03075f6881598dc0b2e0f37605ea5e71
parentb60f2d0c9fa1d31c817ed39f87cf7619ff742dd3
parentfe33d8ea146429af7db514621e25870508975d62
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #2175 from tgschultz/stdlib-serialization-minor_changes

Minor changes to serializer/deserializer

2 files changed, 58 insertions(+), 60 deletions(-)

std/io.zig+30-32
......@@ -1088,6 +1088,11 @@ test "io.readLineSliceFrom" {
10881088 testing.expectError(error.OutOfMemory, readLineSliceFrom(stream, buf[0..]));
10891089}
10901090
1091pub const Packing = enum {
1092 Byte, /// Pack data to byte alignment
1093 Bit, /// Pack data to bit alignment
1094};
1095
10911096/// Creates a deserializer that deserializes types from any stream.
10921097/// If `is_packed` is true, the data stream is treated as bit-packed,
10931098/// otherwise data is expected to be packed to the smallest byte.
......@@ -1097,18 +1102,18 @@ test "io.readLineSliceFrom" {
10971102/// which will be called when the deserializer is used to deserialize
10981103/// that type. It will pass a pointer to the type instance to deserialize
10991104/// into and a pointer to the deserializer struct.
1100pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime Error: type) type {
1105pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime Error: type) type {
11011106 return struct {
11021107 const Self = @This();
11031108
1104 in_stream: if (is_packed) BitInStream(endian, Stream.Error) else *Stream,
1109 in_stream: if (packing == .Bit) BitInStream(endian, Stream.Error) else *Stream,
11051110
11061111 pub const Stream = InStream(Error);
11071112
11081113 pub fn init(in_stream: *Stream) Self {
1109 return Self{ .in_stream = switch (is_packed) {
1110 true => BitInStream(endian, Stream.Error).init(in_stream),
1111 else => in_stream,
1114 return Self{ .in_stream = switch (packing) {
1115 .Bit => BitInStream(endian, Stream.Error).init(in_stream),
1116 .Byte => in_stream,
11121117 } };
11131118 }
11141119
......@@ -1128,7 +1133,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E
11281133 const Log2U = math.Log2Int(U);
11291134 const int_size = (U.bit_count + 7) / 8;
11301135
1131 if (is_packed) {
1136 if (packing == .Bit) {
11321137 const result = try self.in_stream.readBitsNoEof(U, t_bit_count);
11331138 return @bitCast(T, result);
11341139 }
......@@ -1211,8 +1216,8 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E
12111216 //custom deserializer: fn(self: *Self, deserializer: var) !void
12121217 if (comptime trait.hasFn("deserialize")(C)) return C.deserialize(ptr, self);
12131218
1214 if (comptime trait.isPacked(C) and !is_packed) {
1215 var packed_deserializer = Deserializer(endian, true, Error).init(self.in_stream);
1219 if (comptime trait.isPacked(C) and packing != .Bit) {
1220 var packed_deserializer = Deserializer(endian, .Bit, Error).init(self.in_stream);
12161221 return packed_deserializer.deserializeInto(ptr);
12171222 }
12181223
......@@ -1267,7 +1272,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E
12671272 return error.InvalidEnumTag;
12681273 }
12691274 @compileError("Cannot meaningfully deserialize " ++ @typeName(C) ++
1270 " because it is an untagged union Use a custom deserialize().");
1275 " because it is an untagged union. Use a custom deserialize().");
12711276 },
12721277 builtin.TypeId.Optional => {
12731278 const OC = comptime meta.Child(C);
......@@ -1276,14 +1281,10 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E
12761281 ptr.* = null;
12771282 return;
12781283 }
1279
1280 //The way non-pointer optionals are implemented ensures a pointer to them
1281 // will point to the value. The flag is stored at the end of that data.
1282 var val_ptr = @ptrCast(*OC, ptr);
1284
1285 ptr.* = OC(undefined); //make it non-null so the following .? is guaranteed safe
1286 const val_ptr = &ptr.*.?;
12831287 try self.deserializeInto(val_ptr);
1284 //This bit ensures the null flag isn't set. Any actual copying should be
1285 // optimized out... I hope.
1286 ptr.* = val_ptr.*;
12871288 },
12881289 builtin.TypeId.Enum => {
12891290 var value = try self.deserializeInt(@TagType(C));
......@@ -1310,24 +1311,24 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E
13101311/// which will be called when the serializer is used to serialize that type. It will
13111312/// pass a const pointer to the type instance to be serialized and a pointer
13121313/// to the serializer struct.
1313pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, comptime Error: type) type {
1314pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime Error: type) type {
13141315 return struct {
13151316 const Self = @This();
13161317
1317 out_stream: if (is_packed) BitOutStream(endian, Stream.Error) else *Stream,
1318 out_stream: if (packing == .Bit) BitOutStream(endian, Stream.Error) else *Stream,
13181319
13191320 pub const Stream = OutStream(Error);
13201321
13211322 pub fn init(out_stream: *Stream) Self {
1322 return Self{ .out_stream = switch (is_packed) {
1323 true => BitOutStream(endian, Stream.Error).init(out_stream),
1324 else => out_stream,
1323 return Self{ .out_stream = switch (packing) {
1324 .Bit => BitOutStream(endian, Stream.Error).init(out_stream),
1325 .Byte => out_stream,
13251326 } };
13261327 }
13271328
13281329 /// Flushes any unwritten bits to the stream
13291330 pub fn flush(self: *Self) Error!void {
1330 if (is_packed) return self.out_stream.flushBits();
1331 if (packing == .Bit) return self.out_stream.flushBits();
13311332 }
13321333
13331334 fn serializeInt(self: *Self, value: var) Error!void {
......@@ -1343,15 +1344,15 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, com
13431344
13441345 const u_value = @bitCast(U, value);
13451346
1346 if (is_packed) return self.out_stream.writeBits(u_value, t_bit_count);
1347 if (packing == .Bit) return self.out_stream.writeBits(u_value, t_bit_count);
13471348
13481349 var buffer: [int_size]u8 = undefined;
13491350 if (int_size == 1) buffer[0] = u_value;
13501351
13511352 for (buffer) |*byte, i| {
13521353 const idx = switch (endian) {
1353 builtin.Endian.Big => int_size - i - 1,
1354 builtin.Endian.Little => i,
1354 .Big => int_size - i - 1,
1355 .Little => i,
13551356 };
13561357 const shift = @intCast(Log2U, idx * u8_bit_count);
13571358 const v = u_value >> shift;
......@@ -1374,8 +1375,8 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, com
13741375 //custom serializer: fn(self: Self, serializer: var) !void
13751376 if (comptime trait.hasFn("serialize")(T)) return T.serialize(value, self);
13761377
1377 if (comptime trait.isPacked(T) and !is_packed) {
1378 var packed_serializer = Serializer(endian, true, Error).init(self.out_stream);
1378 if (comptime trait.isPacked(T) and packing != .Bit) {
1379 var packed_serializer = Serializer(endian, .Bit, Error).init(self.out_stream);
13791380 try packed_serializer.serialize(value);
13801381 try packed_serializer.flush();
13811382 return;
......@@ -1422,7 +1423,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, com
14221423 unreachable;
14231424 }
14241425 @compileError("Cannot meaningfully serialize " ++ @typeName(T) ++
1425 " because it is an untagged union Use a custom serialize().");
1426 " because it is an untagged union. Use a custom serialize().");
14261427 },
14271428 builtin.TypeId.Optional => {
14281429 if (value == null) {
......@@ -1432,10 +1433,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, com
14321433 try self.serializeInt(u1(@boolToInt(true)));
14331434
14341435 const OC = comptime meta.Child(T);
1435
1436 //The way non-pointer optionals are implemented ensures a pointer to them
1437 // will point to the value. The flag is stored at the end of that data.
1438 var val_ptr = @ptrCast(*const OC, &value);
1436 const val_ptr = &value.?;
14391437 try self.serialize(val_ptr.*);
14401438 },
14411439 builtin.TypeId.Enum => {
std/io_test.zig+28-28
......@@ -318,7 +318,7 @@ test "BitStreams with File Stream" {
318318 try os.deleteFile(tmp_file_name);
319319}
320320
321fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packed: bool) !void {
321fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
322322 //@NOTE: if this test is taking too long, reduce the maximum tested bitsize
323323 const max_test_bitsize = 128;
324324
......@@ -333,12 +333,12 @@ fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_pa
333333 var out = io.SliceOutStream.init(data_mem[0..]);
334334 const OutError = io.SliceOutStream.Error;
335335 var out_stream = &out.stream;
336 var serializer = io.Serializer(endian, is_packed, OutError).init(out_stream);
336 var serializer = io.Serializer(endian, packing, OutError).init(out_stream);
337337
338338 var in = io.SliceInStream.init(data_mem[0..]);
339339 const InError = io.SliceInStream.Error;
340340 var in_stream = &in.stream;
341 var deserializer = io.Deserializer(endian, is_packed, InError).init(in_stream);
341 var deserializer = io.Deserializer(endian, packing, InError).init(in_stream);
342342
343343 comptime var i = 0;
344344 inline while (i <= max_test_bitsize) : (i += 1) {
......@@ -366,21 +366,21 @@ fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_pa
366366 const extra_packed_byte = @boolToInt(total_bits % u8_bit_count > 0);
367367 const total_packed_bytes = (total_bits / u8_bit_count) + extra_packed_byte;
368368
369 expect(in.pos == if (is_packed) total_packed_bytes else total_bytes);
369 expect(in.pos == if (packing == .Bit) total_packed_bytes else total_bytes);
370370
371371 //Verify that empty error set works with serializer.
372372 //deserializer is covered by SliceInStream
373373 const NullError = io.NullOutStream.Error;
374374 var null_out = io.NullOutStream.init();
375375 var null_out_stream = &null_out.stream;
376 var null_serializer = io.Serializer(endian, is_packed, NullError).init(null_out_stream);
376 var null_serializer = io.Serializer(endian, packing, NullError).init(null_out_stream);
377377 try null_serializer.serialize(data_mem[0..]);
378378 try null_serializer.flush();
379379}
380380
381381test "Serializer/Deserializer Int" {
382 try testIntSerializerDeserializer(builtin.Endian.Big, false);
383 try testIntSerializerDeserializer(builtin.Endian.Little, false);
382 try testIntSerializerDeserializer(.Big, .Byte);
383 try testIntSerializerDeserializer(.Little, .Byte);
384384 // TODO these tests are disabled due to tripping an LLVM assertion
385385 // https://github.com/ziglang/zig/issues/2019
386386 //try testIntSerializerDeserializer(builtin.Endian.Big, true);
......@@ -389,7 +389,7 @@ test "Serializer/Deserializer Int" {
389389
390390fn testIntSerializerDeserializerInfNaN(
391391 comptime endian: builtin.Endian,
392 comptime is_packed: bool,
392 comptime packing: io.Packing,
393393) !void {
394394 const mem_size = (16 * 2 + 32 * 2 + 64 * 2 + 128 * 2) / comptime meta.bitCount(u8);
395395 var data_mem: [mem_size]u8 = undefined;
......@@ -397,12 +397,12 @@ fn testIntSerializerDeserializerInfNaN(
397397 var out = io.SliceOutStream.init(data_mem[0..]);
398398 const OutError = io.SliceOutStream.Error;
399399 var out_stream = &out.stream;
400 var serializer = io.Serializer(endian, is_packed, OutError).init(out_stream);
400 var serializer = io.Serializer(endian, packing, OutError).init(out_stream);
401401
402402 var in = io.SliceInStream.init(data_mem[0..]);
403403 const InError = io.SliceInStream.Error;
404404 var in_stream = &in.stream;
405 var deserializer = io.Deserializer(endian, is_packed, InError).init(in_stream);
405 var deserializer = io.Deserializer(endian, packing, InError).init(in_stream);
406406
407407 //@TODO: isInf/isNan not currently implemented for f128.
408408 try serializer.serialize(std.math.nan(f16));
......@@ -432,17 +432,17 @@ fn testIntSerializerDeserializerInfNaN(
432432}
433433
434434test "Serializer/Deserializer Int: Inf/NaN" {
435 try testIntSerializerDeserializerInfNaN(builtin.Endian.Big, false);
436 try testIntSerializerDeserializerInfNaN(builtin.Endian.Little, false);
437 try testIntSerializerDeserializerInfNaN(builtin.Endian.Big, true);
438 try testIntSerializerDeserializerInfNaN(builtin.Endian.Little, true);
435 try testIntSerializerDeserializerInfNaN(.Big, .Byte);
436 try testIntSerializerDeserializerInfNaN(.Little, .Byte);
437 try testIntSerializerDeserializerInfNaN(.Big, .Bit);
438 try testIntSerializerDeserializerInfNaN(.Little, .Bit);
439439}
440440
441441fn testAlternateSerializer(self: var, serializer: var) !void {
442442 try serializer.serialize(self.f_f16);
443443}
444444
445fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packed: bool) !void {
445fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
446446 const ColorType = enum(u4) {
447447 RGB8 = 1,
448448 RA16 = 2,
......@@ -529,12 +529,12 @@ fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packe
529529 var out = io.SliceOutStream.init(data_mem[0..]);
530530 const OutError = io.SliceOutStream.Error;
531531 var out_stream = &out.stream;
532 var serializer = io.Serializer(endian, is_packed, OutError).init(out_stream);
532 var serializer = io.Serializer(endian, packing, OutError).init(out_stream);
533533
534534 var in = io.SliceInStream.init(data_mem[0..]);
535535 const InError = io.SliceInStream.Error;
536536 var in_stream = &in.stream;
537 var deserializer = io.Deserializer(endian, is_packed, InError).init(in_stream);
537 var deserializer = io.Deserializer(endian, packing, InError).init(in_stream);
538538
539539 try serializer.serialize(my_inst);
540540
......@@ -543,13 +543,13 @@ fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packe
543543}
544544
545545test "Serializer/Deserializer generic" {
546 try testSerializerDeserializer(builtin.Endian.Big, false);
547 try testSerializerDeserializer(builtin.Endian.Little, false);
548 try testSerializerDeserializer(builtin.Endian.Big, true);
549 try testSerializerDeserializer(builtin.Endian.Little, true);
546 try testSerializerDeserializer(builtin.Endian.Big, .Byte);
547 try testSerializerDeserializer(builtin.Endian.Little, .Byte);
548 try testSerializerDeserializer(builtin.Endian.Big, .Bit);
549 try testSerializerDeserializer(builtin.Endian.Little, .Bit);
550550}
551551
552fn testBadData(comptime endian: builtin.Endian, comptime is_packed: bool) !void {
552fn testBadData(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
553553 const E = enum(u14) {
554554 One = 1,
555555 Two = 2,
......@@ -568,12 +568,12 @@ fn testBadData(comptime endian: builtin.Endian, comptime is_packed: bool) !void
568568 var out = io.SliceOutStream.init(data_mem[0..]);
569569 const OutError = io.SliceOutStream.Error;
570570 var out_stream = &out.stream;
571 var serializer = io.Serializer(endian, is_packed, OutError).init(out_stream);
571 var serializer = io.Serializer(endian, packing, OutError).init(out_stream);
572572
573573 var in = io.SliceInStream.init(data_mem[0..]);
574574 const InError = io.SliceInStream.Error;
575575 var in_stream = &in.stream;
576 var deserializer = io.Deserializer(endian, is_packed, InError).init(in_stream);
576 var deserializer = io.Deserializer(endian, packing, InError).init(in_stream);
577577
578578 try serializer.serialize(u14(3));
579579 expectError(error.InvalidEnumTag, deserializer.deserialize(A));
......@@ -584,8 +584,8 @@ fn testBadData(comptime endian: builtin.Endian, comptime is_packed: bool) !void
584584}
585585
586586test "Deserializer bad data" {
587 try testBadData(builtin.Endian.Big, false);
588 try testBadData(builtin.Endian.Little, false);
589 try testBadData(builtin.Endian.Big, true);
590 try testBadData(builtin.Endian.Little, true);
587 try testBadData(.Big, .Byte);
588 try testBadData(.Little, .Byte);
589 try testBadData(.Big, .Bit);
590 try testBadData(.Little, .Bit);
591591}