| author | |
| committer | |
| log | 4795f161e689ef6bea563278c6a93c9ad2610ab7 |
| tree | 0c2421ac03075f6881598dc0b2e0f37605ea5e71 |
| parent | b60f2d0c9fa1d31c817ed39f87cf7619ff742dd3 |
| parent | fe33d8ea146429af7db514621e25870508975d62 |
| signature |
Minor changes to serializer/deserializer2 files changed, 58 insertions(+), 60 deletions(-)
std/io.zig+30-32| ... | ... | @@ -1088,6 +1088,11 @@ test "io.readLineSliceFrom" { |
| 1088 | 1088 | testing.expectError(error.OutOfMemory, readLineSliceFrom(stream, buf[0..])); |
| 1089 | 1089 | } |
| 1090 | 1090 | |
| 1091 | pub const Packing = enum { | |
| 1092 | Byte, /// Pack data to byte alignment | |
| 1093 | Bit, /// Pack data to bit alignment | |
| 1094 | }; | |
| 1095 | ||
| 1091 | 1096 | /// Creates a deserializer that deserializes types from any stream. |
| 1092 | 1097 | /// If `is_packed` is true, the data stream is treated as bit-packed, |
| 1093 | 1098 | /// otherwise data is expected to be packed to the smallest byte. |
| ... | ... | @@ -1097,18 +1102,18 @@ test "io.readLineSliceFrom" { |
| 1097 | 1102 | /// which will be called when the deserializer is used to deserialize |
| 1098 | 1103 | /// that type. It will pass a pointer to the type instance to deserialize |
| 1099 | 1104 | /// into and a pointer to the deserializer struct. |
| 1100 | pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime Error: type) type { | |
| 1105 | pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime Error: type) type { | |
| 1101 | 1106 | return struct { |
| 1102 | 1107 | const Self = @This(); |
| 1103 | 1108 | |
| 1104 | in_stream: if (is_packed) BitInStream(endian, Stream.Error) else *Stream, | |
| 1109 | in_stream: if (packing == .Bit) BitInStream(endian, Stream.Error) else *Stream, | |
| 1105 | 1110 | |
| 1106 | 1111 | pub const Stream = InStream(Error); |
| 1107 | 1112 | |
| 1108 | 1113 | 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, | |
| 1112 | 1117 | } }; |
| 1113 | 1118 | } |
| 1114 | 1119 | |
| ... | ... | @@ -1128,7 +1133,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E |
| 1128 | 1133 | const Log2U = math.Log2Int(U); |
| 1129 | 1134 | const int_size = (U.bit_count + 7) / 8; |
| 1130 | 1135 | |
| 1131 | if (is_packed) { | |
| 1136 | if (packing == .Bit) { | |
| 1132 | 1137 | const result = try self.in_stream.readBitsNoEof(U, t_bit_count); |
| 1133 | 1138 | return @bitCast(T, result); |
| 1134 | 1139 | } |
| ... | ... | @@ -1211,8 +1216,8 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E |
| 1211 | 1216 | //custom deserializer: fn(self: *Self, deserializer: var) !void |
| 1212 | 1217 | if (comptime trait.hasFn("deserialize")(C)) return C.deserialize(ptr, self); |
| 1213 | 1218 | |
| 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); | |
| 1216 | 1221 | return packed_deserializer.deserializeInto(ptr); |
| 1217 | 1222 | } |
| 1218 | 1223 | |
| ... | ... | @@ -1267,7 +1272,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E |
| 1267 | 1272 | return error.InvalidEnumTag; |
| 1268 | 1273 | } |
| 1269 | 1274 | @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()."); | |
| 1271 | 1276 | }, |
| 1272 | 1277 | builtin.TypeId.Optional => { |
| 1273 | 1278 | const OC = comptime meta.Child(C); |
| ... | ... | @@ -1276,14 +1281,10 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E |
| 1276 | 1281 | ptr.* = null; |
| 1277 | 1282 | return; |
| 1278 | 1283 | } |
| 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.*.?; | |
| 1283 | 1287 | 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.*; | |
| 1287 | 1288 | }, |
| 1288 | 1289 | builtin.TypeId.Enum => { |
| 1289 | 1290 | var value = try self.deserializeInt(@TagType(C)); |
| ... | ... | @@ -1310,24 +1311,24 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E |
| 1310 | 1311 | /// which will be called when the serializer is used to serialize that type. It will |
| 1311 | 1312 | /// pass a const pointer to the type instance to be serialized and a pointer |
| 1312 | 1313 | /// to the serializer struct. |
| 1313 | pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, comptime Error: type) type { | |
| 1314 | pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime Error: type) type { | |
| 1314 | 1315 | return struct { |
| 1315 | 1316 | const Self = @This(); |
| 1316 | 1317 | |
| 1317 | out_stream: if (is_packed) BitOutStream(endian, Stream.Error) else *Stream, | |
| 1318 | out_stream: if (packing == .Bit) BitOutStream(endian, Stream.Error) else *Stream, | |
| 1318 | 1319 | |
| 1319 | 1320 | pub const Stream = OutStream(Error); |
| 1320 | 1321 | |
| 1321 | 1322 | 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, | |
| 1325 | 1326 | } }; |
| 1326 | 1327 | } |
| 1327 | 1328 | |
| 1328 | 1329 | /// Flushes any unwritten bits to the stream |
| 1329 | 1330 | 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(); | |
| 1331 | 1332 | } |
| 1332 | 1333 | |
| 1333 | 1334 | fn serializeInt(self: *Self, value: var) Error!void { |
| ... | ... | @@ -1343,15 +1344,15 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, com |
| 1343 | 1344 | |
| 1344 | 1345 | const u_value = @bitCast(U, value); |
| 1345 | 1346 | |
| 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); | |
| 1347 | 1348 | |
| 1348 | 1349 | var buffer: [int_size]u8 = undefined; |
| 1349 | 1350 | if (int_size == 1) buffer[0] = u_value; |
| 1350 | 1351 | |
| 1351 | 1352 | for (buffer) |*byte, i| { |
| 1352 | 1353 | 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, | |
| 1355 | 1356 | }; |
| 1356 | 1357 | const shift = @intCast(Log2U, idx * u8_bit_count); |
| 1357 | 1358 | const v = u_value >> shift; |
| ... | ... | @@ -1374,8 +1375,8 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, com |
| 1374 | 1375 | //custom serializer: fn(self: Self, serializer: var) !void |
| 1375 | 1376 | if (comptime trait.hasFn("serialize")(T)) return T.serialize(value, self); |
| 1376 | 1377 | |
| 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); | |
| 1379 | 1380 | try packed_serializer.serialize(value); |
| 1380 | 1381 | try packed_serializer.flush(); |
| 1381 | 1382 | return; |
| ... | ... | @@ -1422,7 +1423,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, com |
| 1422 | 1423 | unreachable; |
| 1423 | 1424 | } |
| 1424 | 1425 | @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()."); | |
| 1426 | 1427 | }, |
| 1427 | 1428 | builtin.TypeId.Optional => { |
| 1428 | 1429 | if (value == null) { |
| ... | ... | @@ -1432,10 +1433,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, com |
| 1432 | 1433 | try self.serializeInt(u1(@boolToInt(true))); |
| 1433 | 1434 | |
| 1434 | 1435 | 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.?; | |
| 1439 | 1437 | try self.serialize(val_ptr.*); |
| 1440 | 1438 | }, |
| 1441 | 1439 | builtin.TypeId.Enum => { |
std/io_test.zig+28-28| ... | ... | @@ -318,7 +318,7 @@ test "BitStreams with File Stream" { |
| 318 | 318 | try os.deleteFile(tmp_file_name); |
| 319 | 319 | } |
| 320 | 320 | |
| 321 | fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packed: bool) !void { | |
| 321 | fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void { | |
| 322 | 322 | //@NOTE: if this test is taking too long, reduce the maximum tested bitsize |
| 323 | 323 | const max_test_bitsize = 128; |
| 324 | 324 | |
| ... | ... | @@ -333,12 +333,12 @@ fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_pa |
| 333 | 333 | var out = io.SliceOutStream.init(data_mem[0..]); |
| 334 | 334 | const OutError = io.SliceOutStream.Error; |
| 335 | 335 | 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); | |
| 337 | 337 | |
| 338 | 338 | var in = io.SliceInStream.init(data_mem[0..]); |
| 339 | 339 | const InError = io.SliceInStream.Error; |
| 340 | 340 | 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); | |
| 342 | 342 | |
| 343 | 343 | comptime var i = 0; |
| 344 | 344 | inline while (i <= max_test_bitsize) : (i += 1) { |
| ... | ... | @@ -366,21 +366,21 @@ fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_pa |
| 366 | 366 | const extra_packed_byte = @boolToInt(total_bits % u8_bit_count > 0); |
| 367 | 367 | const total_packed_bytes = (total_bits / u8_bit_count) + extra_packed_byte; |
| 368 | 368 | |
| 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); | |
| 370 | 370 | |
| 371 | 371 | //Verify that empty error set works with serializer. |
| 372 | 372 | //deserializer is covered by SliceInStream |
| 373 | 373 | const NullError = io.NullOutStream.Error; |
| 374 | 374 | var null_out = io.NullOutStream.init(); |
| 375 | 375 | 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); | |
| 377 | 377 | try null_serializer.serialize(data_mem[0..]); |
| 378 | 378 | try null_serializer.flush(); |
| 379 | 379 | } |
| 380 | 380 | |
| 381 | 381 | test "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); | |
| 384 | 384 | // TODO these tests are disabled due to tripping an LLVM assertion |
| 385 | 385 | // https://github.com/ziglang/zig/issues/2019 |
| 386 | 386 | //try testIntSerializerDeserializer(builtin.Endian.Big, true); |
| ... | ... | @@ -389,7 +389,7 @@ test "Serializer/Deserializer Int" { |
| 389 | 389 | |
| 390 | 390 | fn testIntSerializerDeserializerInfNaN( |
| 391 | 391 | comptime endian: builtin.Endian, |
| 392 | comptime is_packed: bool, | |
| 392 | comptime packing: io.Packing, | |
| 393 | 393 | ) !void { |
| 394 | 394 | const mem_size = (16 * 2 + 32 * 2 + 64 * 2 + 128 * 2) / comptime meta.bitCount(u8); |
| 395 | 395 | var data_mem: [mem_size]u8 = undefined; |
| ... | ... | @@ -397,12 +397,12 @@ fn testIntSerializerDeserializerInfNaN( |
| 397 | 397 | var out = io.SliceOutStream.init(data_mem[0..]); |
| 398 | 398 | const OutError = io.SliceOutStream.Error; |
| 399 | 399 | 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); | |
| 401 | 401 | |
| 402 | 402 | var in = io.SliceInStream.init(data_mem[0..]); |
| 403 | 403 | const InError = io.SliceInStream.Error; |
| 404 | 404 | 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); | |
| 406 | 406 | |
| 407 | 407 | //@TODO: isInf/isNan not currently implemented for f128. |
| 408 | 408 | try serializer.serialize(std.math.nan(f16)); |
| ... | ... | @@ -432,17 +432,17 @@ fn testIntSerializerDeserializerInfNaN( |
| 432 | 432 | } |
| 433 | 433 | |
| 434 | 434 | test "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); | |
| 439 | 439 | } |
| 440 | 440 | |
| 441 | 441 | fn testAlternateSerializer(self: var, serializer: var) !void { |
| 442 | 442 | try serializer.serialize(self.f_f16); |
| 443 | 443 | } |
| 444 | 444 | |
| 445 | fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packed: bool) !void { | |
| 445 | fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void { | |
| 446 | 446 | const ColorType = enum(u4) { |
| 447 | 447 | RGB8 = 1, |
| 448 | 448 | RA16 = 2, |
| ... | ... | @@ -529,12 +529,12 @@ fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packe |
| 529 | 529 | var out = io.SliceOutStream.init(data_mem[0..]); |
| 530 | 530 | const OutError = io.SliceOutStream.Error; |
| 531 | 531 | 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); | |
| 533 | 533 | |
| 534 | 534 | var in = io.SliceInStream.init(data_mem[0..]); |
| 535 | 535 | const InError = io.SliceInStream.Error; |
| 536 | 536 | 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); | |
| 538 | 538 | |
| 539 | 539 | try serializer.serialize(my_inst); |
| 540 | 540 | |
| ... | ... | @@ -543,13 +543,13 @@ fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packe |
| 543 | 543 | } |
| 544 | 544 | |
| 545 | 545 | test "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); | |
| 550 | 550 | } |
| 551 | 551 | |
| 552 | fn testBadData(comptime endian: builtin.Endian, comptime is_packed: bool) !void { | |
| 552 | fn testBadData(comptime endian: builtin.Endian, comptime packing: io.Packing) !void { | |
| 553 | 553 | const E = enum(u14) { |
| 554 | 554 | One = 1, |
| 555 | 555 | Two = 2, |
| ... | ... | @@ -568,12 +568,12 @@ fn testBadData(comptime endian: builtin.Endian, comptime is_packed: bool) !void |
| 568 | 568 | var out = io.SliceOutStream.init(data_mem[0..]); |
| 569 | 569 | const OutError = io.SliceOutStream.Error; |
| 570 | 570 | 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); | |
| 572 | 572 | |
| 573 | 573 | var in = io.SliceInStream.init(data_mem[0..]); |
| 574 | 574 | const InError = io.SliceInStream.Error; |
| 575 | 575 | 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); | |
| 577 | 577 | |
| 578 | 578 | try serializer.serialize(u14(3)); |
| 579 | 579 | expectError(error.InvalidEnumTag, deserializer.deserialize(A)); |
| ... | ... | @@ -584,8 +584,8 @@ fn testBadData(comptime endian: builtin.Endian, comptime is_packed: bool) !void |
| 584 | 584 | } |
| 585 | 585 | |
| 586 | 586 | test "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); | |
| 591 | 591 | } |