authorgravatar for tgschultz@gmail.comtgschultz <tgschultz@gmail.com> 2018-11-23 10:02:14-06:00
committergravatar for tgschultz@gmail.comtgschultz <tgschultz@gmail.com> 2018-11-30 14:50:17-06:00
log1ab66f3b55bbeac3c6f4f02c86a688cdc1bb2476
tree959d637f5d2e8e2d8b8a201f91a09c63df8e4180
parent823969a5a47e054842056e3380b3987058ccd2dd

Added serialization, bitstreams, traits for integer sign, TagPayloadType


4 files changed, 1029 insertions(+), 8 deletions(-)

std/io.zig+634-5
......@@ -8,6 +8,8 @@ const debug = std.debug;
88const assert = debug.assert;
99const os = std.os;
1010const mem = std.mem;
11const meta = std.meta;
12const trait = meta.trait;
1113const Buffer = std.Buffer;
1214const fmt = std.fmt;
1315const File = std.os.File;
......@@ -444,6 +446,151 @@ pub const SliceInStream = struct {
444446 }
445447};
446448
449/// Creates a stream which allows for reading bit fields from another stream
450pub fn BitInStream(endian: builtin.Endian, comptime Error: type) type {
451 return struct {
452 const Self = @This();
453
454 in_stream: *Stream,
455 bit_buffer: u7,
456 bit_count: u3,
457 stream: Stream,
458
459 pub const Stream = InStream(Error);
460 const u8_bit_count = comptime meta.bitCount(u8);
461 const u7_bit_count = comptime meta.bitCount(u7);
462 const u4_bit_count = comptime meta.bitCount(u4);
463
464 pub fn init(in_stream: *Stream) Self {
465 return Self{
466 .in_stream = in_stream,
467 .bit_buffer = 0,
468 .bit_count = 0,
469 .stream = Stream{ .readFn = read },
470 };
471 }
472
473 /// Reads `bits` bits from the stream and returns a specified unsigned int type
474 /// containing them in the least significant end, returning an error if the
475 /// specified number of bits could not be read.
476 pub fn readBitsNoEof(self: *Self, comptime U: type, bits: usize) !U {
477 var n: usize = undefined;
478 const result = try self.readBits(U, bits, &n);
479 if (n < bits) return error.EndOfStream;
480 return result;
481 }
482
483 /// Reads `bits` bits from the stream and returns a specified unsigned int type
484 /// containing them in the least significant end. The number of bits successfully
485 /// read is placed in `out_bits`, as reaching the end of the stream is not an error.
486 pub fn readBits(self: *Self, comptime U: type, bits: usize, out_bits: *usize) Error!U {
487 debug.assert(trait.isUnsignedInt(U));
488
489 //by extending the buffer to a minimum of u8 we can cover a number of edge cases
490 // related to shifting and casting.
491 const u_bit_count = comptime meta.bitCount(U);
492 const buf_bit_count = bc: {
493 debug.assert(u_bit_count >= bits);
494 break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count;
495 };
496 const Buf = @IntType(false, buf_bit_count);
497 const BufShift = math.Log2Int(Buf);
498
499 out_bits.* = usize(0);
500 if (U == u0 or bits == 0) return 0;
501 var out_buffer = Buf(0);
502
503 if (self.bit_count > 0) {
504 const n = if (self.bit_count >= bits) @intCast(u3, bits) else self.bit_count;
505 const shift = u7_bit_count - n;
506 switch (endian) {
507 builtin.Endian.Big => {
508 out_buffer = Buf(self.bit_buffer >> shift);
509 self.bit_buffer <<= n;
510 },
511 builtin.Endian.Little => {
512 const value = (self.bit_buffer << shift) >> shift;
513 out_buffer = Buf(value);
514 self.bit_buffer >>= n;
515 },
516 }
517 self.bit_count -= n;
518 out_bits.* = n;
519 }
520 //at this point we know bit_buffer is empty
521
522 //copy bytes until we have enough bits, then leave the rest in bit_buffer
523 while (out_bits.* < bits) {
524 const n = bits - out_bits.*;
525 const next_byte = self.in_stream.readByte() catch |err| {
526 if (err == error.EndOfStream) {
527 return @intCast(U, out_buffer);
528 }
529 return err;
530 };
531
532 switch (endian) {
533 builtin.Endian.Big => {
534 if (n >= u8_bit_count) {
535 out_buffer <<= @intCast(u3, u8_bit_count - 1);
536 out_buffer <<= 1;
537 out_buffer |= Buf(next_byte);
538 out_bits.* += u8_bit_count;
539 continue;
540 }
541
542 const shift = @intCast(u3, u8_bit_count - n);
543 out_buffer <<= @intCast(BufShift, n);
544 out_buffer |= Buf(next_byte >> shift);
545 out_bits.* += n;
546 self.bit_buffer = @truncate(u7, next_byte << @intCast(u3, n - 1));
547 self.bit_count = shift;
548 },
549 builtin.Endian.Little => {
550 if (n >= u8_bit_count) {
551 out_buffer |= Buf(next_byte) << @intCast(BufShift, out_bits.*);
552 out_bits.* += u8_bit_count;
553 continue;
554 }
555
556 const shift = @intCast(u3, u8_bit_count - n);
557 const value = (next_byte << shift) >> shift;
558 out_buffer |= Buf(value) << @intCast(BufShift, out_bits.*);
559 out_bits.* += n;
560 self.bit_buffer = @truncate(u7, next_byte >> @intCast(u3, n));
561 self.bit_count = shift;
562 },
563 }
564 }
565
566 return @intCast(U, out_buffer);
567 }
568
569 pub fn alignToByte(self: *Self) void {
570 self.bit_buffer = 0;
571 self.bit_count = 0;
572 }
573
574 pub fn read(self_stream: *Stream, buffer: []u8) Error!usize {
575 var self = @fieldParentPtr(Self, "stream", self_stream);
576
577 var out_bits: usize = undefined;
578 var out_bits_total = usize(0);
579 //@NOTE: I'm not sure this is a good idea, maybe alignToByte should be forced
580 if (self.bit_count > 0) {
581 for (buffer) |*b, i| {
582 b.* = try self.readBits(u8, u8_bit_count, &out_bits);
583 out_bits_total += out_bits;
584 }
585 const incomplete_byte = @boolToInt(out_bits_total % u8_bit_count > 0);
586 return (out_bits_total / u8_bit_count) + incomplete_byte;
587 }
588
589 return self.in_stream.read(buffer);
590 }
591 };
592}
593
447594/// This is a simple OutStream that writes to a slice, and returns an error
448595/// when it runs out of space.
449596pub const SliceOutStream = struct {
......@@ -637,6 +784,137 @@ pub const BufferOutStream = struct {
637784 }
638785};
639786
787/// Creates a stream which allows for writing bit fields to another stream
788pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type {
789 return struct {
790 const Self = @This();
791
792 out_stream: *Stream,
793 bit_buffer: u8,
794 bit_count: u4,
795 stream: Stream,
796
797 pub const Stream = OutStream(Error);
798 const u8_bit_count = comptime meta.bitCount(u8);
799 const u4_bit_count = comptime meta.bitCount(u4);
800
801 pub fn init(out_stream: *Stream) Self {
802 return Self{
803 .out_stream = out_stream,
804 .bit_buffer = 0,
805 .bit_count = 0,
806 .stream = Stream{ .writeFn = write },
807 };
808 }
809
810 /// Write the specified number of bits to the stream from the least significant bits of
811 /// the specified unsigned int value. Bits will only be written to the stream when there
812 /// are enough to fill a byte.
813 pub fn writeBits(self: *Self, value: var, bits: usize) Error!void {
814 if (bits == 0) return;
815
816 const U = @typeOf(value);
817 debug.assert(trait.isUnsignedInt(U));
818
819 //by extending the buffer to a minimum of u8 we can cover a number of edge cases
820 // related to shifting and casting.
821 const u_bit_count = comptime meta.bitCount(U);
822 const buf_bit_count = bc: {
823 debug.assert(u_bit_count >= bits);
824 break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count;
825 };
826 const Buf = @IntType(false, buf_bit_count);
827 const BufShift = math.Log2Int(Buf);
828
829 const buf_value = @intCast(Buf, value);
830
831 const high_byte_shift = @intCast(BufShift, buf_bit_count - u8_bit_count);
832 var in_buffer = switch (endian) {
833 builtin.Endian.Big => buf_value << @intCast(BufShift, buf_bit_count - bits),
834 builtin.Endian.Little => buf_value,
835 };
836 var in_bits = bits;
837
838 if (self.bit_count > 0) {
839 const bits_remaining = u8_bit_count - self.bit_count;
840 const n = @intCast(u3, if (bits_remaining > bits) bits else bits_remaining);
841 switch (endian) {
842 builtin.Endian.Big => {
843 const shift = @intCast(BufShift, high_byte_shift + self.bit_count);
844 const v = @intCast(u8, in_buffer >> shift);
845 self.bit_buffer |= v;
846 in_buffer <<= n;
847 },
848 builtin.Endian.Little => {
849 const v = @truncate(u8, in_buffer) << @intCast(u3, self.bit_count);
850 self.bit_buffer |= v;
851 in_buffer >>= n;
852 },
853 }
854 self.bit_count += n;
855 in_bits -= n;
856
857 //if we didn't fill the buffer, it's because bits < bits_remaining;
858 if (self.bit_count != u8_bit_count) return;
859 try self.out_stream.writeByte(self.bit_buffer);
860 self.bit_buffer = 0;
861 self.bit_count = 0;
862 }
863 //at this point we know bit_buffer is empty
864
865 //copy bytes until we can't fill one anymore, then leave the rest in bit_buffer
866 while (in_bits >= u8_bit_count) {
867 switch (endian) {
868 builtin.Endian.Big => {
869 const v = @intCast(u8, in_buffer >> high_byte_shift);
870 try self.out_stream.writeByte(v);
871 in_buffer <<= @intCast(u3, u8_bit_count - 1);
872 in_buffer <<= 1;
873 },
874 builtin.Endian.Little => {
875 const v = @truncate(u8, in_buffer);
876 try self.out_stream.writeByte(v);
877 in_buffer >>= @intCast(u3, u8_bit_count - 1);
878 in_buffer >>= 1;
879 },
880 }
881 in_bits -= u8_bit_count;
882 }
883
884 if (in_bits > 0) {
885 self.bit_count = @intCast(u4, in_bits);
886 self.bit_buffer = switch (endian) {
887 builtin.Endian.Big => @truncate(u8, in_buffer >> high_byte_shift),
888 builtin.Endian.Little => @truncate(u8, in_buffer),
889 };
890 }
891 }
892
893 /// Flush any remaining bits to the stream.
894 pub fn flushBits(self: *Self) !void {
895 if (self.bit_count == 0) return;
896 try self.out_stream.writeByte(self.bit_buffer);
897 self.bit_buffer = 0;
898 self.bit_count = 0;
899 }
900
901 pub fn write(self_stream: *Stream, buffer: []const u8) Error!void {
902 var self = @fieldParentPtr(Self, "stream", self_stream);
903
904 //@NOTE: I'm not sure this is a good idea, maybe flushBits should be forced
905 if (self.bit_count > 0) {
906 for (buffer) |b, i|
907 try self.writeBits(b, u8_bit_count);
908 return;
909 }
910
911 return self.out_stream.write(buffer);
912 }
913 };
914}
915
916
917
640918pub const BufferedAtomicFile = struct {
641919 atomic_file: os.AtomicFile,
642920 file_stream: os.File.OutStream,
......@@ -677,11 +955,6 @@ pub const BufferedAtomicFile = struct {
677955 }
678956};
679957
680test "import io tests" {
681 comptime {
682 _ = @import("io_test.zig");
683 }
684}
685958
686959pub fn readLine(buf: *std.Buffer) ![]u8 {
687960 var stdin = try getStdIn();
......@@ -753,3 +1026,359 @@ test "io.readLineSliceFrom" {
7531026 debug.assert(mem.eql(u8, "Line 1", try readLineSliceFrom(stream, buf[0..])));
7541027 debug.assertError(readLineSliceFrom(stream, buf[0..]), error.OutOfMemory);
7551028}
1029
1030/// Creates a deserializer that deserializes types from any stream.
1031/// If `is_packed` is true, the data stream is treated as bit-packed,
1032/// otherwise data is expected to be packed to the smallest byte.
1033/// Types may implement a custom deserialization routine with a
1034/// function named `deserialize` in the form of:
1035/// pub fn deserialize(self: *Self, deserializer: var) !void
1036/// which will be called when the deserializer is used to deserialize
1037/// that type. It will pass a pointer to the type instance to deserialize
1038/// into and a pointer to the deserializer struct.
1039pub fn Deserializer(endian: builtin.Endian, is_packed: bool, comptime Error: type) type {
1040 return struct {
1041 const Self = @This();
1042
1043 in_stream: if (is_packed) BitInStream(endian, Stream.Error) else *Stream,
1044
1045 pub const Stream = InStream(Error);
1046
1047 pub fn init(in_stream: *Stream) Self {
1048 return Self{ .in_stream = switch (is_packed) {
1049 true => BitInStream(endian, Stream.Error).init(in_stream),
1050 else => in_stream,
1051 } };
1052 }
1053
1054 //@BUG: inferred error issue
1055 fn deserializeInt(self: *Self, comptime T: type) (Stream.Error || error{EndOfStream})!T {
1056 debug.assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T));
1057
1058 const u8_bit_count = comptime meta.bitCount(u8);
1059 const t_bit_count = comptime meta.bitCount(T);
1060
1061 const U = @IntType(false, t_bit_count);
1062 const Log2U = math.Log2Int(U);
1063 const int_size = @sizeOf(U);
1064
1065 if (is_packed) {
1066 const result = try self.in_stream.readBitsNoEof(U, t_bit_count);
1067 return @bitCast(T, result);
1068 }
1069
1070 var buffer: [int_size]u8 = undefined;
1071 const read_size = try self.in_stream.read(buffer[0..]);
1072 if (read_size < int_size) return error.EndOfStream;
1073
1074 if (int_size == 1) return @bitCast(T, buffer[0]);
1075
1076 var result = U(0);
1077 for (buffer) |byte, i| {
1078 switch (endian) {
1079 builtin.Endian.Big => {
1080 result = (result << @intCast(u4, u8_bit_count)) | byte;
1081 },
1082 builtin.Endian.Little => {
1083 result |= U(byte) << @intCast(Log2U, u8_bit_count * i);
1084 },
1085 }
1086 }
1087
1088 return @bitCast(T, result);
1089 }
1090
1091 //@TODO: Replace this with @unionInit or whatever when it is added
1092 // see: #1315
1093 fn setTag(ptr: var, tag: var) void {
1094 const T = @typeOf(ptr);
1095 comptime debug.assert(trait.isPtrTo(builtin.TypeId.Union)(T));
1096 const U = meta.Child(T);
1097
1098 const info = @typeInfo(U).Union;
1099 if (info.tag_type) |TagType| {
1100 debug.assert(TagType == @typeOf(tag));
1101
1102 var ptr_tag = ptr: {
1103 if (@alignOf(TagType) >= @alignOf(U)) break :ptr @ptrCast(*TagType, ptr);
1104 const offset = comptime max: {
1105 var max_field_size: comptime_int = 0;
1106 for (info.fields) |field_info| {
1107 const field_size = @sizeOf(field_info.field_type);
1108 max_field_size = math.max(max_field_size, field_size);
1109 }
1110 break :max math.max(max_field_size, @alignOf(U));
1111 };
1112 break :ptr @intToPtr(*TagType, @ptrToInt(ptr) + offset);
1113 };
1114 ptr_tag.* = tag;
1115 }
1116 }
1117
1118 /// Deserializes and returns data of the specified type from the stream
1119 pub fn deserialize(self: *Self, comptime T: type) !T {
1120 var value: T = undefined;
1121 try self.deserializeInto(&value);
1122 return value;
1123 }
1124
1125 /// Deserializes data into the type pointed to by `ptr`
1126 pub fn deserializeInto(self: *Self, ptr: var) !void {
1127 const T = @typeOf(ptr);
1128 debug.assert(trait.is(builtin.TypeId.Pointer)(T));
1129
1130 if (comptime trait.isSlice(T) or comptime trait.isPtrTo(builtin.TypeId.Array)(T)) {
1131 for (ptr) |*v|
1132 try self.deserializeInto(v);
1133 return;
1134 }
1135
1136 comptime debug.assert(trait.isSingleItemPtr(T));
1137
1138 const C = comptime meta.Child(T);
1139 const child_type_id = @typeId(C);
1140
1141 //custom deserializer: fn(self: *Self, deserializer: var) !void
1142 if (comptime trait.hasFn("deserialize")(C)) return ptr.deserialize(self);
1143
1144 if (comptime trait.isPacked(C) and !is_packed) {
1145 var packed_deserializer = Deserializer(endian, true, Error).init(self.in_stream);
1146 return packed_deserializer.deserializeInto(ptr);
1147 }
1148
1149 switch (child_type_id) {
1150 builtin.TypeId.Void => return,
1151 builtin.TypeId.Bool => ptr.* = (try self.deserializeInt(u1)) > 0,
1152 builtin.TypeId.Float, builtin.TypeId.Int => ptr.* = try self.deserializeInt(C),
1153 builtin.TypeId.Struct => {
1154 const info = @typeInfo(C).Struct;
1155
1156 inline for (info.fields) |*field_info| {
1157 const name = field_info.name;
1158 const FieldType = field_info.field_type;
1159
1160 if (FieldType == void or FieldType == u0) continue;
1161
1162 //it doesn't make any sense to read pointers
1163 if (comptime trait.is(builtin.TypeId.Pointer)(FieldType)) {
1164 @compileError("Will not " ++ "read field " ++ name ++ " of struct " ++
1165 @typeName(C) ++ " because it " ++ "is of pointer-type " ++
1166 @typeName(FieldType) ++ ".");
1167 }
1168
1169 try self.deserializeInto(&@field(ptr, name));
1170 }
1171 },
1172 builtin.TypeId.Union => {
1173 const info = @typeInfo(C).Union;
1174 if (info.tag_type) |TagType| {
1175 //we avoid duplicate iteration over the enum tags
1176 // by getting the int directly and casting it without
1177 // safety. If it is bad, it will be caught anyway.
1178 const TagInt = @TagType(TagType);
1179 const tag = try self.deserializeInt(TagInt);
1180
1181 {
1182 @setRuntimeSafety(false);
1183 //See: #1315
1184 setTag(ptr, @intToEnum(TagType, tag));
1185 }
1186
1187 inline for (info.fields) |field_info| {
1188 if (field_info.enum_field.?.value == tag) {
1189 const name = field_info.name;
1190 const FieldType = field_info.field_type;
1191 @field(ptr, name) = FieldType(undefined);
1192 try self.deserializeInto(&@field(ptr, name));
1193 return;
1194 }
1195 }
1196 //This is reachable if the enum data is bad
1197 return error.InvalidEnumTag;
1198 }
1199 @compileError("Cannot meaningfully deserialize " ++ @typeName(C) ++
1200 " because it is an untagged union Use a custom deserialize().");
1201 },
1202 builtin.TypeId.Optional => {
1203 const OC = comptime meta.Child(C);
1204 const exists = (try self.deserializeInt(u1)) > 0;
1205 if (!exists) {
1206 ptr.* = null;
1207 return;
1208 }
1209
1210 //The way non-pointer optionals are implemented ensures a pointer to them
1211 // will point to the value. The flag is stored at the end of that data.
1212 var val_ptr = @ptrCast(*OC, ptr);
1213 try self.deserializeInto(val_ptr);
1214 //This bit ensures the null flag isn't set. Any actual copying should be
1215 // optimized out... I hope.
1216 ptr.* = val_ptr.*;
1217 },
1218 builtin.TypeId.Enum => {
1219 var value = try self.deserializeInt(@TagType(C));
1220 ptr.* = try meta.intToEnum(C, value);
1221 },
1222 else => {
1223 @compileError("Cannot deserialize " ++ @tagName(child_type_id) ++ " types (unimplemented).");
1224 },
1225 }
1226 }
1227 };
1228}
1229
1230/// Creates a serializer that serializes types to any stream.
1231/// If `is_packed` is true, the data will be bit-packed into the stream.
1232/// Note that the you must call `serializer.flush()` when you are done
1233/// writing bit-packed data in order ensure any unwritten bits are committed.
1234/// If `is_packed` is false, data is packed to the smallest byte. In the case
1235/// of packed structs, the struct will written bit-packed and with the specified
1236/// endianess, after which data will resume being written at the next byte boundary.
1237/// Types may implement a custom serialization routine with a
1238/// function named `serialize` in the form of:
1239/// pub fn serialize(self: *const Self, serializer: var) !void
1240/// which will be called when the serializer is used to serialize that type. It will
1241/// pass a const pointer to the type instance to be serialized and a pointer
1242/// to the serializer struct.
1243pub fn Serializer(endian: builtin.Endian, is_packed: bool, comptime Error: type) type {
1244 return struct {
1245 const Self = @This();
1246
1247 out_stream: if (is_packed) BitOutStream(endian, Stream.Error) else *Stream,
1248
1249 pub const Stream = OutStream(Error);
1250
1251 pub fn init(out_stream: *Stream) Self {
1252 return Self{ .out_stream = switch (is_packed) {
1253 true => BitOutStream(endian, Stream.Error).init(out_stream),
1254 else => out_stream,
1255 } };
1256 }
1257
1258 /// Flushes any unwritten bits to the stream
1259 pub fn flush(self: *Self) Stream.Error!void {
1260 if (is_packed) return self.out_stream.flushBits();
1261 }
1262
1263 fn serializeInt(self: *Self, value: var) !void {
1264 const T = @typeOf(value);
1265 debug.assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T));
1266
1267 const t_bit_count = comptime meta.bitCount(T);
1268 const u8_bit_count = comptime meta.bitCount(u8);
1269
1270 const U = @IntType(false, t_bit_count);
1271 const Log2U = math.Log2Int(U);
1272 const int_size = @sizeOf(U);
1273
1274 const u_value = @bitCast(U, value);
1275
1276 if (is_packed) return self.out_stream.writeBits(u_value, t_bit_count);
1277
1278 var buffer: [int_size]u8 = undefined;
1279 if (int_size == 1) buffer[0] = u_value;
1280
1281 for (buffer) |*byte, i| {
1282 const idx = switch (endian) {
1283 builtin.Endian.Big => int_size - i - 1,
1284 builtin.Endian.Little => i,
1285 };
1286 const shift = @intCast(Log2U, idx * u8_bit_count);
1287 const v = u_value >> shift;
1288 byte.* = if (t_bit_count < u8_bit_count) v else @truncate(u8, v);
1289 }
1290
1291 try self.out_stream.write(buffer);
1292 }
1293
1294 /// Serializes the passed value into the stream
1295 pub fn serialize(self: *Self, value: var) !void {
1296 const T = comptime @typeOf(value);
1297
1298 if (comptime trait.isIndexable(T)) {
1299 for (value) |v|
1300 try self.serialize(v);
1301 return;
1302 }
1303
1304 //custom serializer: fn(self: *const Self, serializer: var) !void
1305 if (comptime trait.hasFn("serialize")(T)) return value.serialize(self);
1306
1307 if (comptime trait.isPacked(T) and !is_packed) {
1308 var packed_serializer = Serializer(endian, true, Error).init(self.out_stream);
1309 try packed_serializer.serialize(value);
1310 try packed_serializer.flush();
1311 return;
1312 }
1313
1314 switch (@typeId(T)) {
1315 builtin.TypeId.Void => return,
1316 builtin.TypeId.Bool => try self.serializeInt(u1(@boolToInt(value))),
1317 builtin.TypeId.Float, builtin.TypeId.Int => try self.serializeInt(value),
1318 builtin.TypeId.Struct => {
1319 const info = @typeInfo(T);
1320
1321 inline for (info.Struct.fields) |*field_info| {
1322 const name = field_info.name;
1323 const FieldType = field_info.field_type;
1324
1325 if (FieldType == void or FieldType == u0) continue;
1326
1327 //It doesn't make sense to write pointers
1328 if (comptime trait.is(builtin.TypeId.Pointer)(FieldType)) {
1329 @compileError("Will not " ++ "serialize field " ++ name ++
1330 " of struct " ++ @typeName(T) ++ " because it " ++
1331 "is of pointer-type " ++ @typeName(FieldType) ++ ".");
1332 }
1333 try self.serialize(@field(value, name));
1334 }
1335 },
1336 builtin.TypeId.Union => {
1337 const info = @typeInfo(T).Union;
1338 if (info.tag_type) |TagType| {
1339 const active_tag = meta.activeTag(value);
1340 try self.serialize(active_tag);
1341 //This inline loop is necessary because active_tag is a runtime
1342 // value, but @field requires a comptime value. Our alternative
1343 // is to check each field for a match
1344 inline for (info.fields) |field_info| {
1345 if (field_info.enum_field.?.value == @enumToInt(active_tag)) {
1346 const name = field_info.name;
1347 const FieldType = field_info.field_type;
1348 try self.serialize(@field(value, name));
1349 return;
1350 }
1351 }
1352 unreachable;
1353 }
1354 @compileError("Cannot meaningfully serialize " ++ @typeName(T) ++
1355 " because it is an untagged union Use a custom serialize().");
1356 },
1357 builtin.TypeId.Optional => {
1358 if (value == null) {
1359 try self.serializeInt(u1(@boolToInt(false)));
1360 return;
1361 }
1362 try self.serializeInt(u1(@boolToInt(true)));
1363
1364 const OC = comptime meta.Child(T);
1365
1366 //The way non-pointer optionals are implemented ensures a pointer to them
1367 // will point to the value. The flag is stored at the end of that data.
1368 var val_ptr = @ptrCast(*const OC, &value);
1369 try self.serialize(val_ptr.*);
1370 },
1371 builtin.TypeId.Enum => {
1372 try self.serializeInt(@enumToInt(value));
1373 },
1374 else => @compileError("Cannot serialize " ++ @tagName(@typeId(T)) ++ " types (unimplemented)."),
1375 }
1376 }
1377 };
1378}
1379
1380test "import io tests" {
1381 comptime {
1382 _ = @import("io_test.zig");
1383 }
1384}
std/io_test.zig+329
......@@ -1,5 +1,7 @@
11const std = @import("index.zig");
22const io = std.io;
3const meta = std.meta;
4const trait = std.trait;
35const DefaultPrng = std.rand.DefaultPrng;
46const assert = std.debug.assert;
57const assertError = std.debug.assertError;
......@@ -132,3 +134,330 @@ test "SliceOutStream" {
132134 assertError(ss.stream.write("Hello world!"), error.OutOfSpace);
133135 assert(mem.eql(u8, ss.getWritten(), "Hello worl"));
134136}
137
138test "BitInStream" {
139 const mem_be = []u8{ 0b11001101, 0b00001011 };
140 const mem_le = []u8{ 0b00011101, 0b10010101 };
141
142 var mem_in_be = io.SliceInStream.init(mem_be[0..]);
143 const InError = io.SliceInStream.Error;
144 var bit_stream_be = io.BitInStream(builtin.Endian.Big, InError).init(&mem_in_be.stream);
145
146 var out_bits: usize = undefined;
147
148 assert(1 == try bit_stream_be.readBits(u2, 1, &out_bits));
149 assert(out_bits == 1);
150 assert(2 == try bit_stream_be.readBits(u5, 2, &out_bits));
151 assert(out_bits == 2);
152 assert(3 == try bit_stream_be.readBits(u128, 3, &out_bits));
153 assert(out_bits == 3);
154 assert(4 == try bit_stream_be.readBits(u8, 4, &out_bits));
155 assert(out_bits == 4);
156 assert(5 == try bit_stream_be.readBits(u9, 5, &out_bits));
157 assert(out_bits == 5);
158 assert(1 == try bit_stream_be.readBits(u1, 1, &out_bits));
159 assert(out_bits == 1);
160
161 mem_in_be.pos = 0;
162 bit_stream_be.bit_count = 0;
163 assert(0b110011010000101 == try bit_stream_be.readBits(u15, 15, &out_bits));
164 assert(out_bits == 15);
165
166 mem_in_be.pos = 0;
167 bit_stream_be.bit_count = 0;
168 assert(0b1100110100001011 == try bit_stream_be.readBits(u16, 16, &out_bits));
169 assert(out_bits == 16);
170
171 _ = try bit_stream_be.readBits(u0, 0, &out_bits);
172
173 var mem_in_le = io.SliceInStream.init(mem_le[0..]);
174 var bit_stream_le = io.BitInStream(builtin.Endian.Little, InError).init(&mem_in_le.stream);
175
176 assert(1 == try bit_stream_le.readBits(u2, 1, &out_bits));
177 assert(out_bits == 1);
178 assert(2 == try bit_stream_le.readBits(u5, 2, &out_bits));
179 assert(out_bits == 2);
180 assert(3 == try bit_stream_le.readBits(u128, 3, &out_bits));
181 assert(out_bits == 3);
182 assert(4 == try bit_stream_le.readBits(u8, 4, &out_bits));
183 assert(out_bits == 4);
184 assert(5 == try bit_stream_le.readBits(u9, 5, &out_bits));
185 assert(out_bits == 5);
186 assert(1 == try bit_stream_le.readBits(u1, 1, &out_bits));
187 assert(out_bits == 1);
188
189 mem_in_le.pos = 0;
190 bit_stream_le.bit_count = 0;
191 assert(0b001010100011101 == try bit_stream_le.readBits(u15, 15, &out_bits));
192 assert(out_bits == 15);
193
194 mem_in_le.pos = 0;
195 bit_stream_le.bit_count = 0;
196 assert(0b1001010100011101 == try bit_stream_le.readBits(u16, 16, &out_bits));
197 assert(out_bits == 16);
198
199 _ = try bit_stream_le.readBits(u0, 0, &out_bits);
200}
201
202test "BitOutStream" {
203 var mem_be = []u8{0} ** 2;
204 var mem_le = []u8{0} ** 2;
205
206 var mem_out_be = io.SliceOutStream.init(mem_be[0..]);
207 const OutError = io.SliceOutStream.Error;
208 var bit_stream_be = io.BitOutStream(builtin.Endian.Big, OutError).init(&mem_out_be.stream);
209
210 try bit_stream_be.writeBits(u2(1), 1);
211 try bit_stream_be.writeBits(u5(2), 2);
212 try bit_stream_be.writeBits(u128(3), 3);
213 try bit_stream_be.writeBits(u8(4), 4);
214 try bit_stream_be.writeBits(u9(5), 5);
215 try bit_stream_be.writeBits(u1(1), 1);
216
217 assert(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001011);
218
219 mem_out_be.pos = 0;
220
221 try bit_stream_be.writeBits(u15(0b110011010000101), 15);
222 try bit_stream_be.flushBits();
223 assert(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001010);
224
225 mem_out_be.pos = 0;
226 try bit_stream_be.writeBits(u32(0b110011010000101), 16);
227 assert(mem_be[0] == 0b01100110 and mem_be[1] == 0b10000101);
228
229 try bit_stream_be.writeBits(u0(0), 0);
230
231 var mem_out_le = io.SliceOutStream.init(mem_le[0..]);
232 var bit_stream_le = io.BitOutStream(builtin.Endian.Little, OutError).init(&mem_out_le.stream);
233
234 try bit_stream_le.writeBits(u2(1), 1);
235 try bit_stream_le.writeBits(u5(2), 2);
236 try bit_stream_le.writeBits(u128(3), 3);
237 try bit_stream_le.writeBits(u8(4), 4);
238 try bit_stream_le.writeBits(u9(5), 5);
239 try bit_stream_le.writeBits(u1(1), 1);
240
241 assert(mem_le[0] == 0b00011101 and mem_le[1] == 0b10010101);
242
243 mem_out_le.pos = 0;
244 try bit_stream_le.writeBits(u15(0b110011010000101), 15);
245 try bit_stream_le.flushBits();
246 assert(mem_le[0] == 0b10000101 and mem_le[1] == 0b01100110);
247
248 mem_out_le.pos = 0;
249 try bit_stream_le.writeBits(u32(0b1100110100001011), 16);
250 assert(mem_le[0] == 0b00001011 and mem_le[1] == 0b11001101);
251
252 try bit_stream_le.writeBits(u0(0), 0);
253}
254
255fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packed: bool) !void {
256 const max_test_bitsize = 17;
257
258 const total_bytes = comptime blk: {
259 var bytes = 0;
260 comptime var i = 0;
261 while (i <= max_test_bitsize) : (i += 1) bytes += (i / 8) + @boolToInt(i % 8 > 0);
262 break :blk bytes * 2;
263 };
264
265 var data_mem: [total_bytes]u8 = undefined;
266 var out = io.SliceOutStream.init(data_mem[0..]);
267 const OutError = io.SliceOutStream.Error;
268 var out_stream = &out.stream;
269 var serializer = io.Serializer(endian, is_packed, OutError).init(out_stream);
270
271 var in = io.SliceInStream.init(data_mem[0..]);
272 const InError = io.SliceInStream.Error;
273 var in_stream = &in.stream;
274 var deserializer = io.Deserializer(endian, is_packed, InError).init(in_stream);
275
276 comptime var i = 0;
277 inline while (i <= max_test_bitsize) : (i += 1) {
278 const U = @IntType(false, i);
279 const S = @IntType(true, i);
280 try serializer.serializeInt(U(i));
281 if (i != 0) try serializer.serializeInt(S(-1));
282 }
283 try serializer.flush();
284
285 i = 0;
286 inline while (i <= max_test_bitsize) : (i += 1) {
287 const U = @IntType(false, i);
288 const S = @IntType(true, i);
289 const x = try deserializer.deserializeInt(U);
290 const y = if (i != 0) try deserializer.deserializeInt(S);
291 assert(x == U(i));
292 if (i != 0) assert(y == S(-1));
293 }
294
295 const u8_bit_count = comptime meta.bitCount(u8);
296 //0 + 1 + 2 + ... n = (n * (n + 1)) / 2
297 //and we have each for unsigned and signed, so * 2
298 const total_bits = (max_test_bitsize * (max_test_bitsize + 1));
299 const extra_packed_byte = @boolToInt(total_bits % u8_bit_count > 0);
300 const total_packed_bytes = (total_bits / u8_bit_count) + extra_packed_byte;
301
302
303
304 assert(in.pos == if (is_packed) total_packed_bytes else total_bytes);
305}
306
307test "Serializer/Deserializer Int" {
308 try testIntSerializerDeserializer(builtin.Endian.Big, false);
309 try testIntSerializerDeserializer(builtin.Endian.Little, false);
310 try testIntSerializerDeserializer(builtin.Endian.Big, true);
311 try testIntSerializerDeserializer(builtin.Endian.Little, true);
312}
313
314fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packed: bool) !void {
315 const ColorType = enum(u4) {
316 RGB8 = 1,
317 RA16 = 2,
318 R32 = 3,
319 };
320
321 const TagAlign = union(enum(u32)) {
322 A: u8,
323 B: u8,
324 C: u8,
325 };
326
327 const Color = union(ColorType) {
328 RGB8: struct {
329 r: u8,
330 g: u8,
331 b: u8,
332 a: u8,
333 },
334 RA16: struct {
335 r: u16,
336 a: u16,
337 },
338 R32: u32,
339 };
340
341 const PackedStruct = packed struct {
342 f_i3: i3,
343 f_u2: u2,
344 };
345
346 //to test custom serialization
347 const Custom = struct {
348 f_f16: f16,
349 f_unused_u32: u32,
350
351 pub fn deserialize(self: *@This(), deserializer: var) !void {
352 try deserializer.deserializeInto(&self.f_f16);
353 self.f_unused_u32 = 47;
354 }
355
356 pub fn serialize(self: *const @This(), serializer: var) !void {
357 try serializer.serialize(self.f_f16);
358 }
359 };
360
361 const MyStruct = struct {
362 f_i3: i3,
363 f_u8: u8,
364 f_tag_align: TagAlign,
365 f_u24: u24,
366 f_i19: i19,
367 f_void: void,
368 f_f32: f32,
369 f_f128: f128,
370 f_packed_0: PackedStruct,
371 f_i7arr: [10]i7,
372 f_of64n: ?f64,
373 f_of64v: ?f64,
374 f_color_type: ColorType,
375 f_packed_1: PackedStruct,
376 f_custom: Custom,
377 f_color: Color,
378 };
379
380 const my_inst = MyStruct{
381 .f_i3 = -1,
382 .f_u8 = 8,
383 .f_tag_align = TagAlign{ .B = 148 },
384 .f_u24 = 24,
385 .f_i19 = 19,
386 .f_void = {},
387 .f_f32 = 32.32,
388 .f_f128 = 128.128,
389 .f_packed_0 = PackedStruct{ .f_i3 = -1, .f_u2 = 2 },
390 .f_i7arr = [10]i7{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
391 .f_of64n = null,
392 .f_of64v = 64.64,
393 .f_color_type = ColorType.R32,
394 .f_packed_1 = PackedStruct{ .f_i3 = 1, .f_u2 = 1 },
395 .f_custom = Custom{ .f_f16 = 38.63, .f_unused_u32 = 47 },
396 .f_color = Color{ .R32 = 123822 },
397 };
398
399 var data_mem: [@sizeOf(MyStruct)]u8 = undefined;
400 var out = io.SliceOutStream.init(data_mem[0..]);
401 const OutError = io.SliceOutStream.Error;
402 var out_stream = &out.stream;
403 var serializer = io.Serializer(endian, is_packed, OutError).init(out_stream);
404
405 var in = io.SliceInStream.init(data_mem[0..]);
406 const InError = io.SliceInStream.Error;
407 var in_stream = &in.stream;
408 var deserializer = io.Deserializer(endian, is_packed, InError).init(in_stream);
409
410 try serializer.serialize(my_inst);
411
412 const my_copy = try deserializer.deserialize(MyStruct);
413
414 assert(meta.eql(my_copy, my_inst));
415}
416
417test "Serializer/Deserializer generic" {
418 try testSerializerDeserializer(builtin.Endian.Big, false);
419 try testSerializerDeserializer(builtin.Endian.Little, false);
420 try testSerializerDeserializer(builtin.Endian.Big, true);
421 try testSerializerDeserializer(builtin.Endian.Little, true);
422}
423
424fn testBadData(comptime endian: builtin.Endian, comptime is_packed: bool) !void {
425 const E = enum(u14) {
426 One = 1,
427 Two = 2,
428 };
429
430 const A = struct {
431 e: E,
432 };
433
434 const C = union(E) {
435 One: u14,
436 Two: f16,
437 };
438
439 var data_mem: [4]u8 = undefined;
440 var out = io.SliceOutStream.init(data_mem[0..]);
441 const OutError = io.SliceOutStream.Error;
442 var out_stream = &out.stream;
443 var serializer = io.Serializer(endian, is_packed, OutError).init(out_stream);
444
445 var in = io.SliceInStream.init(data_mem[0..]);
446 const InError = io.SliceInStream.Error;
447 var in_stream = &in.stream;
448 var deserializer = io.Deserializer(endian, is_packed, InError).init(in_stream);
449
450 try serializer.serialize(u14(3));
451 assertError(deserializer.deserialize(A), error.InvalidEnumTag);
452 out.pos = 0;
453 try serializer.serialize(u14(3));
454 try serializer.serialize(u14(88));
455 assertError(deserializer.deserialize(C), error.InvalidEnumTag);
456}
457
458test "Deserializer bad data" {
459 try testBadData(builtin.Endian.Big, false);
460 try testBadData(builtin.Endian.Little, false);
461 try testBadData(builtin.Endian.Big, true);
462 try testBadData(builtin.Endian.Little, true);
463}
\ No newline at end of file
std/meta/index.zig+35-3
......@@ -95,7 +95,7 @@ test "std.meta.stringToEnum" {
9595 debug.assert(null == stringToEnum(E1, "C"));
9696}
9797
98pub fn bitCount(comptime T: type) u32 {
98pub fn bitCount(comptime T: type) comptime_int {
9999 return switch (@typeInfo(T)) {
100100 TypeId.Int => |info| info.bits,
101101 TypeId.Float => |info| info.bits,
......@@ -108,7 +108,7 @@ test "std.meta.bitCount" {
108108 debug.assert(bitCount(f32) == 32);
109109}
110110
111pub fn alignment(comptime T: type) u29 {
111pub fn alignment(comptime T: type) comptime_int {
112112 //@alignOf works on non-pointer types
113113 const P = if (comptime trait.is(TypeId.Pointer)(T)) T else *T;
114114 return @typeInfo(P).Pointer.alignment;
......@@ -386,6 +386,33 @@ test "std.meta.activeTag" {
386386 debug.assert(activeTag(u) == UE.Float);
387387}
388388
389///Given a tagged union type, and an enum, return the type of the union
390/// field corresponding to the enum tag.
391pub fn TagPayloadType(comptime U: type, tag: var) type {
392 const Tag = @typeOf(tag);
393 debug.assert(trait.is(builtin.TypeId.Union)(U));
394 debug.assert(trait.is(builtin.TypeId.Enum)(Tag));
395
396 const info = @typeInfo(U).Union;
397
398 inline for (info.fields) |field_info| {
399 if (field_info.enum_field.?.value == @enumToInt(tag)) return field_info.field_type;
400 }
401 unreachable;
402}
403
404test "std.meta.TagPayloadType" {
405 const Event = union(enum) {
406 Moved: struct {
407 from: i32,
408 to: i32,
409 },
410 };
411 const MovedEvent = TagPayloadType(Event, Event.Moved);
412 var e: Event = undefined;
413 debug.assert(MovedEvent == @typeOf(e.Moved));
414}
415
389416///Compares two of any type for equality. Containers are compared on a field-by-field basis,
390417/// where possible. Pointers are not followed.
391418pub fn eql(a: var, b: @typeOf(a)) bool {
......@@ -439,6 +466,11 @@ pub fn eql(a: var, b: @typeOf(a)) bool {
439466 builtin.TypeInfo.Pointer.Size.Slice => return a.ptr == b.ptr and a.len == b.len,
440467 }
441468 },
469 builtin.TypeId.Optional => {
470 if(a == null and b == null) return true;
471 if(a == null or b == null) return false;
472 return eql(a.?, b.?);
473 },
442474 else => return a == b,
443475 }
444476}
......@@ -452,7 +484,7 @@ test "std.meta.eql" {
452484
453485 const U = union(enum) {
454486 s: S,
455 f: f32,
487 f: ?f32,
456488 };
457489
458490 const s_1 = S{
std/meta/trait.zig+31
......@@ -231,6 +231,37 @@ test "std.meta.trait.isPacked" {
231231 debug.assert(!isPacked(u8));
232232}
233233
234///
235pub fn isUnsignedInt(comptime T: type) bool {
236 return switch (@typeId(T)) {
237 builtin.TypeId.Int => !@typeInfo(T).Int.is_signed,
238 else => false,
239 };
240}
241
242test "isUnsignedInt" {
243 debug.assert(isUnsignedInt(u32) == true);
244 debug.assert(isUnsignedInt(comptime_int) == false);
245 debug.assert(isUnsignedInt(i64) == false);
246 debug.assert(isUnsignedInt(f64) == false);
247}
248
249///
250pub fn isSignedInt(comptime T: type) bool {
251 return switch (@typeId(T)) {
252 builtin.TypeId.ComptimeInt => true,
253 builtin.TypeId.Int => @typeInfo(T).Int.is_signed,
254 else => false,
255 };
256}
257
258test "isSignedInt" {
259 debug.assert(isSignedInt(u32) == false);
260 debug.assert(isSignedInt(comptime_int) == true);
261 debug.assert(isSignedInt(i64) == true);
262 debug.assert(isSignedInt(f64) == false);
263}
264
234265///
235266pub fn isSingleItemPtr(comptime T: type) bool {
236267 if (comptime is(builtin.TypeId.Pointer)(T)) {