diff --git a/std/c.zig b/std/c.zig index ecbc55890ad71848e744cf108bab260519abd08c..db28105eecefb5c7a657076057489bddd9a0b0dd 100644 --- a/std/c.zig +++ b/std/c.zig @@ -13,6 +13,8 @@ pub use switch (builtin.os) { // TODO https://github.com/ziglang/zig/issues/265 on this whole file pub const FILE = @OpaqueType(); +pub extern "c" fn fopen(filename: [*]const u8, modes: [*]const u8) ?*FILE; +pub extern "c" fn fclose(stream: *FILE) c_int; pub extern "c" fn fwrite(ptr: [*]const u8, size_of_type: usize, item_count: usize, stream: *FILE) usize; pub extern "c" fn fread(ptr: [*]u8, size_of_type: usize, item_count: usize, stream: *FILE) usize; diff --git a/std/io.zig b/std/io.zig index 5ad35c91fb6286fe8a9ea403942eeb418f6b6daf..48cec478e43472453f4cc8e5d9956caa0153e1cb 100644 --- a/std/io.zig +++ b/std/io.zig @@ -1092,6 +1092,7 @@ test "io.readLineSliceFrom" { pub const Packing = enum { /// Pack data to byte alignment Byte, + /// Pack data to bit alignment Bit, }; @@ -1454,6 +1455,6 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co test "import io tests" { comptime { - _ = @import("io_test.zig"); + _ = @import("io/test.zig"); } } diff --git a/std/io/c_out_stream.zig b/std/io/c_out_stream.zig index 398668979ef7eff91222ef996e29ca20e342426d..c66b342f1e6d8670a35ae6dbfdcc3477d29319e2 100644 --- a/std/io/c_out_stream.zig +++ b/std/io/c_out_stream.zig @@ -24,6 +24,10 @@ pub const COutStream = struct { const self = @fieldParentPtr(COutStream, "stream", out_stream); const amt_written = std.c.fwrite(bytes.ptr, 1, bytes.len, self.c_file); if (amt_written == bytes.len) return; + // TODO errno on windows. should we have a posix layer for windows? + if (builtin.os == .windows) { + return error.InputOutput; + } const errno = std.c._errno().*; switch (errno) { 0 => unreachable, diff --git a/std/io/test.zig b/std/io/test.zig new file mode 100644 index 0000000000000000000000000000000000000000..07a3c0e8dde926bad7797c5431b80a27088eac92 --- /dev/null +++ b/std/io/test.zig @@ -0,0 +1,602 @@ +const std = @import("../std.zig"); +const io = std.io; +const meta = std.meta; +const trait = std.trait; +const DefaultPrng = std.rand.DefaultPrng; +const expect = std.testing.expect; +const expectError = std.testing.expectError; +const mem = std.mem; +const os = std.os; +const builtin = @import("builtin"); + +test "write a file, read it, then delete it" { + var raw_bytes: [200 * 1024]u8 = undefined; + var allocator = &std.heap.FixedBufferAllocator.init(raw_bytes[0..]).allocator; + + var data: [1024]u8 = undefined; + var prng = DefaultPrng.init(1234); + prng.random.bytes(data[0..]); + const tmp_file_name = "temp_test_file.txt"; + { + var file = try os.File.openWrite(tmp_file_name); + defer file.close(); + + var file_out_stream = file.outStream(); + var buf_stream = io.BufferedOutStream(os.File.WriteError).init(&file_out_stream.stream); + const st = &buf_stream.stream; + try st.print("begin"); + try st.write(data[0..]); + try st.print("end"); + try buf_stream.flush(); + } + + { + // make sure openWriteNoClobber doesn't harm the file + if (os.File.openWriteNoClobber(tmp_file_name, os.File.default_mode)) |file| { + unreachable; + } else |err| { + std.debug.assert(err == os.File.OpenError.PathAlreadyExists); + } + } + + { + var file = try os.File.openRead(tmp_file_name); + defer file.close(); + + const file_size = try file.getEndPos(); + const expected_file_size = "begin".len + data.len + "end".len; + expect(file_size == expected_file_size); + + var file_in_stream = file.inStream(); + var buf_stream = io.BufferedInStream(os.File.ReadError).init(&file_in_stream.stream); + const st = &buf_stream.stream; + const contents = try st.readAllAlloc(allocator, 2 * 1024); + defer allocator.free(contents); + + expect(mem.eql(u8, contents[0.."begin".len], "begin")); + expect(mem.eql(u8, contents["begin".len .. contents.len - "end".len], data)); + expect(mem.eql(u8, contents[contents.len - "end".len ..], "end")); + } + try os.deleteFile(tmp_file_name); +} + +test "BufferOutStream" { + var bytes: [100]u8 = undefined; + var allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; + + var buffer = try std.Buffer.initSize(allocator, 0); + var buf_stream = &std.io.BufferOutStream.init(&buffer).stream; + + const x: i32 = 42; + const y: i32 = 1234; + try buf_stream.print("x: {}\ny: {}\n", x, y); + + expect(mem.eql(u8, buffer.toSlice(), "x: 42\ny: 1234\n")); +} + +test "SliceInStream" { + const bytes = []const u8{ 1, 2, 3, 4, 5, 6, 7 }; + var ss = io.SliceInStream.init(bytes); + + var dest: [4]u8 = undefined; + + var read = try ss.stream.read(dest[0..4]); + expect(read == 4); + expect(mem.eql(u8, dest[0..4], bytes[0..4])); + + read = try ss.stream.read(dest[0..4]); + expect(read == 3); + expect(mem.eql(u8, dest[0..3], bytes[4..7])); + + read = try ss.stream.read(dest[0..4]); + expect(read == 0); +} + +test "PeekStream" { + const bytes = []const u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; + var ss = io.SliceInStream.init(bytes); + var ps = io.PeekStream(2, io.SliceInStream.Error).init(&ss.stream); + + var dest: [4]u8 = undefined; + + ps.putBackByte(9); + ps.putBackByte(10); + + var read = try ps.stream.read(dest[0..4]); + expect(read == 4); + expect(dest[0] == 10); + expect(dest[1] == 9); + expect(mem.eql(u8, dest[2..4], bytes[0..2])); + + read = try ps.stream.read(dest[0..4]); + expect(read == 4); + expect(mem.eql(u8, dest[0..4], bytes[2..6])); + + read = try ps.stream.read(dest[0..4]); + expect(read == 2); + expect(mem.eql(u8, dest[0..2], bytes[6..8])); + + ps.putBackByte(11); + ps.putBackByte(12); + + read = try ps.stream.read(dest[0..4]); + expect(read == 2); + expect(dest[0] == 12); + expect(dest[1] == 11); +} + +test "SliceOutStream" { + var buffer: [10]u8 = undefined; + var ss = io.SliceOutStream.init(buffer[0..]); + + try ss.stream.write("Hello"); + expect(mem.eql(u8, ss.getWritten(), "Hello")); + + try ss.stream.write("world"); + expect(mem.eql(u8, ss.getWritten(), "Helloworld")); + + expectError(error.OutOfSpace, ss.stream.write("!")); + expect(mem.eql(u8, ss.getWritten(), "Helloworld")); + + ss.reset(); + expect(ss.getWritten().len == 0); + + expectError(error.OutOfSpace, ss.stream.write("Hello world!")); + expect(mem.eql(u8, ss.getWritten(), "Hello worl")); +} + +test "BitInStream" { + const mem_be = []u8{ 0b11001101, 0b00001011 }; + const mem_le = []u8{ 0b00011101, 0b10010101 }; + + var mem_in_be = io.SliceInStream.init(mem_be[0..]); + const InError = io.SliceInStream.Error; + var bit_stream_be = io.BitInStream(builtin.Endian.Big, InError).init(&mem_in_be.stream); + + var out_bits: usize = undefined; + + expect(1 == try bit_stream_be.readBits(u2, 1, &out_bits)); + expect(out_bits == 1); + expect(2 == try bit_stream_be.readBits(u5, 2, &out_bits)); + expect(out_bits == 2); + expect(3 == try bit_stream_be.readBits(u128, 3, &out_bits)); + expect(out_bits == 3); + expect(4 == try bit_stream_be.readBits(u8, 4, &out_bits)); + expect(out_bits == 4); + expect(5 == try bit_stream_be.readBits(u9, 5, &out_bits)); + expect(out_bits == 5); + expect(1 == try bit_stream_be.readBits(u1, 1, &out_bits)); + expect(out_bits == 1); + + mem_in_be.pos = 0; + bit_stream_be.bit_count = 0; + expect(0b110011010000101 == try bit_stream_be.readBits(u15, 15, &out_bits)); + expect(out_bits == 15); + + mem_in_be.pos = 0; + bit_stream_be.bit_count = 0; + expect(0b1100110100001011 == try bit_stream_be.readBits(u16, 16, &out_bits)); + expect(out_bits == 16); + + _ = try bit_stream_be.readBits(u0, 0, &out_bits); + + expect(0 == try bit_stream_be.readBits(u1, 1, &out_bits)); + expect(out_bits == 0); + expectError(error.EndOfStream, bit_stream_be.readBitsNoEof(u1, 1)); + + var mem_in_le = io.SliceInStream.init(mem_le[0..]); + var bit_stream_le = io.BitInStream(builtin.Endian.Little, InError).init(&mem_in_le.stream); + + expect(1 == try bit_stream_le.readBits(u2, 1, &out_bits)); + expect(out_bits == 1); + expect(2 == try bit_stream_le.readBits(u5, 2, &out_bits)); + expect(out_bits == 2); + expect(3 == try bit_stream_le.readBits(u128, 3, &out_bits)); + expect(out_bits == 3); + expect(4 == try bit_stream_le.readBits(u8, 4, &out_bits)); + expect(out_bits == 4); + expect(5 == try bit_stream_le.readBits(u9, 5, &out_bits)); + expect(out_bits == 5); + expect(1 == try bit_stream_le.readBits(u1, 1, &out_bits)); + expect(out_bits == 1); + + mem_in_le.pos = 0; + bit_stream_le.bit_count = 0; + expect(0b001010100011101 == try bit_stream_le.readBits(u15, 15, &out_bits)); + expect(out_bits == 15); + + mem_in_le.pos = 0; + bit_stream_le.bit_count = 0; + expect(0b1001010100011101 == try bit_stream_le.readBits(u16, 16, &out_bits)); + expect(out_bits == 16); + + _ = try bit_stream_le.readBits(u0, 0, &out_bits); + + expect(0 == try bit_stream_le.readBits(u1, 1, &out_bits)); + expect(out_bits == 0); + expectError(error.EndOfStream, bit_stream_le.readBitsNoEof(u1, 1)); +} + +test "BitOutStream" { + var mem_be = []u8{0} ** 2; + var mem_le = []u8{0} ** 2; + + var mem_out_be = io.SliceOutStream.init(mem_be[0..]); + const OutError = io.SliceOutStream.Error; + var bit_stream_be = io.BitOutStream(builtin.Endian.Big, OutError).init(&mem_out_be.stream); + + try bit_stream_be.writeBits(u2(1), 1); + try bit_stream_be.writeBits(u5(2), 2); + try bit_stream_be.writeBits(u128(3), 3); + try bit_stream_be.writeBits(u8(4), 4); + try bit_stream_be.writeBits(u9(5), 5); + try bit_stream_be.writeBits(u1(1), 1); + + expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001011); + + mem_out_be.pos = 0; + + try bit_stream_be.writeBits(u15(0b110011010000101), 15); + try bit_stream_be.flushBits(); + expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001010); + + mem_out_be.pos = 0; + try bit_stream_be.writeBits(u32(0b110011010000101), 16); + expect(mem_be[0] == 0b01100110 and mem_be[1] == 0b10000101); + + try bit_stream_be.writeBits(u0(0), 0); + + var mem_out_le = io.SliceOutStream.init(mem_le[0..]); + var bit_stream_le = io.BitOutStream(builtin.Endian.Little, OutError).init(&mem_out_le.stream); + + try bit_stream_le.writeBits(u2(1), 1); + try bit_stream_le.writeBits(u5(2), 2); + try bit_stream_le.writeBits(u128(3), 3); + try bit_stream_le.writeBits(u8(4), 4); + try bit_stream_le.writeBits(u9(5), 5); + try bit_stream_le.writeBits(u1(1), 1); + + expect(mem_le[0] == 0b00011101 and mem_le[1] == 0b10010101); + + mem_out_le.pos = 0; + try bit_stream_le.writeBits(u15(0b110011010000101), 15); + try bit_stream_le.flushBits(); + expect(mem_le[0] == 0b10000101 and mem_le[1] == 0b01100110); + + mem_out_le.pos = 0; + try bit_stream_le.writeBits(u32(0b1100110100001011), 16); + expect(mem_le[0] == 0b00001011 and mem_le[1] == 0b11001101); + + try bit_stream_le.writeBits(u0(0), 0); +} + +test "BitStreams with File Stream" { + const tmp_file_name = "temp_test_file.txt"; + { + var file = try os.File.openWrite(tmp_file_name); + defer file.close(); + + var file_out = file.outStream(); + var file_out_stream = &file_out.stream; + const OutError = os.File.WriteError; + var bit_stream = io.BitOutStream(builtin.endian, OutError).init(file_out_stream); + + try bit_stream.writeBits(u2(1), 1); + try bit_stream.writeBits(u5(2), 2); + try bit_stream.writeBits(u128(3), 3); + try bit_stream.writeBits(u8(4), 4); + try bit_stream.writeBits(u9(5), 5); + try bit_stream.writeBits(u1(1), 1); + try bit_stream.flushBits(); + } + { + var file = try os.File.openRead(tmp_file_name); + defer file.close(); + + var file_in = file.inStream(); + var file_in_stream = &file_in.stream; + const InError = os.File.ReadError; + var bit_stream = io.BitInStream(builtin.endian, InError).init(file_in_stream); + + var out_bits: usize = undefined; + + expect(1 == try bit_stream.readBits(u2, 1, &out_bits)); + expect(out_bits == 1); + expect(2 == try bit_stream.readBits(u5, 2, &out_bits)); + expect(out_bits == 2); + expect(3 == try bit_stream.readBits(u128, 3, &out_bits)); + expect(out_bits == 3); + expect(4 == try bit_stream.readBits(u8, 4, &out_bits)); + expect(out_bits == 4); + expect(5 == try bit_stream.readBits(u9, 5, &out_bits)); + expect(out_bits == 5); + expect(1 == try bit_stream.readBits(u1, 1, &out_bits)); + expect(out_bits == 1); + + expectError(error.EndOfStream, bit_stream.readBitsNoEof(u1, 1)); + } + try os.deleteFile(tmp_file_name); +} + +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; + + const total_bytes = comptime blk: { + var bytes = 0; + comptime var i = 0; + while (i <= max_test_bitsize) : (i += 1) bytes += (i / 8) + @boolToInt(i % 8 > 0); + break :blk bytes * 2; + }; + + var data_mem: [total_bytes]u8 = undefined; + var out = io.SliceOutStream.init(data_mem[0..]); + const OutError = io.SliceOutStream.Error; + var out_stream = &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, packing, InError).init(in_stream); + + comptime var i = 0; + inline while (i <= max_test_bitsize) : (i += 1) { + const U = @IntType(false, i); + const S = @IntType(true, i); + try serializer.serializeInt(U(i)); + if (i != 0) try serializer.serializeInt(S(-1)) else try serializer.serialize(S(0)); + } + try serializer.flush(); + + i = 0; + inline while (i <= max_test_bitsize) : (i += 1) { + const U = @IntType(false, i); + const S = @IntType(true, i); + const x = try deserializer.deserializeInt(U); + const y = try deserializer.deserializeInt(S); + expect(x == U(i)); + if (i != 0) expect(y == S(-1)) else expect(y == 0); + } + + const u8_bit_count = comptime meta.bitCount(u8); + //0 + 1 + 2 + ... n = (n * (n + 1)) / 2 + //and we have each for unsigned and signed, so * 2 + const total_bits = (max_test_bitsize * (max_test_bitsize + 1)); + 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 (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, packing, NullError).init(null_out_stream); + try null_serializer.serialize(data_mem[0..]); + try null_serializer.flush(); +} + +test "Serializer/Deserializer Int" { + 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); + //try testIntSerializerDeserializer(builtin.Endian.Little, true); +} + +fn testIntSerializerDeserializerInfNaN( + comptime endian: builtin.Endian, + 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; + + var out = io.SliceOutStream.init(data_mem[0..]); + const OutError = io.SliceOutStream.Error; + var out_stream = &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, packing, InError).init(in_stream); + + //@TODO: isInf/isNan not currently implemented for f128. + try serializer.serialize(std.math.nan(f16)); + try serializer.serialize(std.math.inf(f16)); + try serializer.serialize(std.math.nan(f32)); + try serializer.serialize(std.math.inf(f32)); + try serializer.serialize(std.math.nan(f64)); + try serializer.serialize(std.math.inf(f64)); + //try serializer.serialize(std.math.nan(f128)); + //try serializer.serialize(std.math.inf(f128)); + const nan_check_f16 = try deserializer.deserialize(f16); + const inf_check_f16 = try deserializer.deserialize(f16); + const nan_check_f32 = try deserializer.deserialize(f32); + const inf_check_f32 = try deserializer.deserialize(f32); + const nan_check_f64 = try deserializer.deserialize(f64); + const inf_check_f64 = try deserializer.deserialize(f64); + //const nan_check_f128 = try deserializer.deserialize(f128); + //const inf_check_f128 = try deserializer.deserialize(f128); + expect(std.math.isNan(nan_check_f16)); + expect(std.math.isInf(inf_check_f16)); + expect(std.math.isNan(nan_check_f32)); + expect(std.math.isInf(inf_check_f32)); + expect(std.math.isNan(nan_check_f64)); + expect(std.math.isInf(inf_check_f64)); + //expect(std.math.isNan(nan_check_f128)); + //expect(std.math.isInf(inf_check_f128)); +} + +test "Serializer/Deserializer Int: Inf/NaN" { + 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 packing: io.Packing) !void { + const ColorType = enum(u4) { + RGB8 = 1, + RA16 = 2, + R32 = 3, + }; + + const TagAlign = union(enum(u32)) { + A: u8, + B: u8, + C: u8, + }; + + const Color = union(ColorType) { + RGB8: struct { + r: u8, + g: u8, + b: u8, + a: u8, + }, + RA16: struct { + r: u16, + a: u16, + }, + R32: u32, + }; + + const PackedStruct = packed struct { + f_i3: i3, + f_u2: u2, + }; + + //to test custom serialization + const Custom = struct { + f_f16: f16, + f_unused_u32: u32, + + pub fn deserialize(self: *@This(), deserializer: var) !void { + try deserializer.deserializeInto(&self.f_f16); + self.f_unused_u32 = 47; + } + + pub const serialize = testAlternateSerializer; + }; + + const MyStruct = struct { + f_i3: i3, + f_u8: u8, + f_tag_align: TagAlign, + f_u24: u24, + f_i19: i19, + f_void: void, + f_f32: f32, + f_f128: f128, + f_packed_0: PackedStruct, + f_i7arr: [10]i7, + f_of64n: ?f64, + f_of64v: ?f64, + f_color_type: ColorType, + f_packed_1: PackedStruct, + f_custom: Custom, + f_color: Color, + }; + + const my_inst = MyStruct{ + .f_i3 = -1, + .f_u8 = 8, + .f_tag_align = TagAlign{ .B = 148 }, + .f_u24 = 24, + .f_i19 = 19, + .f_void = {}, + .f_f32 = 32.32, + .f_f128 = 128.128, + .f_packed_0 = PackedStruct{ .f_i3 = -1, .f_u2 = 2 }, + .f_i7arr = [10]i7{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, + .f_of64n = null, + .f_of64v = 64.64, + .f_color_type = ColorType.R32, + .f_packed_1 = PackedStruct{ .f_i3 = 1, .f_u2 = 1 }, + .f_custom = Custom{ .f_f16 = 38.63, .f_unused_u32 = 47 }, + .f_color = Color{ .R32 = 123822 }, + }; + + var data_mem: [@sizeOf(MyStruct)]u8 = undefined; + var out = io.SliceOutStream.init(data_mem[0..]); + const OutError = io.SliceOutStream.Error; + var out_stream = &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, packing, InError).init(in_stream); + + try serializer.serialize(my_inst); + + const my_copy = try deserializer.deserialize(MyStruct); + expect(meta.eql(my_copy, my_inst)); +} + +test "Serializer/Deserializer generic" { + 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 packing: io.Packing) !void { + const E = enum(u14) { + One = 1, + Two = 2, + }; + + const A = struct { + e: E, + }; + + const C = union(E) { + One: u14, + Two: f16, + }; + + var data_mem: [4]u8 = undefined; + var out = io.SliceOutStream.init(data_mem[0..]); + const OutError = io.SliceOutStream.Error; + var out_stream = &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, packing, InError).init(in_stream); + + try serializer.serialize(u14(3)); + expectError(error.InvalidEnumTag, deserializer.deserialize(A)); + out.pos = 0; + try serializer.serialize(u14(3)); + try serializer.serialize(u14(88)); + expectError(error.InvalidEnumTag, deserializer.deserialize(C)); +} + +test "Deserializer bad data" { + try testBadData(.Big, .Byte); + try testBadData(.Little, .Byte); + try testBadData(.Big, .Bit); + try testBadData(.Little, .Bit); +} + +test "c out stream" { + if (!builtin.link_libc) return error.SkipZigTest; + + const filename = c"tmp_io_test_file.txt"; + const out_file = std.c.fopen(filename, c"w") orelse return error.UnableToOpenTestFile; + defer std.os.deleteFileC(filename) catch {}; + + const out_stream = &io.COutStream.init(out_file).stream; + try out_stream.print("hi: {}\n", i32(123)); +} diff --git a/std/io_test.zig b/std/io_test.zig deleted file mode 100644 index d6f2264a562251ad1844fe4b96e939ceed8d8f7d..0000000000000000000000000000000000000000 --- a/std/io_test.zig +++ /dev/null @@ -1,591 +0,0 @@ -const std = @import("std.zig"); -const io = std.io; -const meta = std.meta; -const trait = std.trait; -const DefaultPrng = std.rand.DefaultPrng; -const expect = std.testing.expect; -const expectError = std.testing.expectError; -const mem = std.mem; -const os = std.os; -const builtin = @import("builtin"); - -test "write a file, read it, then delete it" { - var raw_bytes: [200 * 1024]u8 = undefined; - var allocator = &std.heap.FixedBufferAllocator.init(raw_bytes[0..]).allocator; - - var data: [1024]u8 = undefined; - var prng = DefaultPrng.init(1234); - prng.random.bytes(data[0..]); - const tmp_file_name = "temp_test_file.txt"; - { - var file = try os.File.openWrite(tmp_file_name); - defer file.close(); - - var file_out_stream = file.outStream(); - var buf_stream = io.BufferedOutStream(os.File.WriteError).init(&file_out_stream.stream); - const st = &buf_stream.stream; - try st.print("begin"); - try st.write(data[0..]); - try st.print("end"); - try buf_stream.flush(); - } - - { - // make sure openWriteNoClobber doesn't harm the file - if (os.File.openWriteNoClobber(tmp_file_name, os.File.default_mode)) |file| { - unreachable; - } else |err| { - std.debug.assert(err == os.File.OpenError.PathAlreadyExists); - } - } - - { - var file = try os.File.openRead(tmp_file_name); - defer file.close(); - - const file_size = try file.getEndPos(); - const expected_file_size = "begin".len + data.len + "end".len; - expect(file_size == expected_file_size); - - var file_in_stream = file.inStream(); - var buf_stream = io.BufferedInStream(os.File.ReadError).init(&file_in_stream.stream); - const st = &buf_stream.stream; - const contents = try st.readAllAlloc(allocator, 2 * 1024); - defer allocator.free(contents); - - expect(mem.eql(u8, contents[0.."begin".len], "begin")); - expect(mem.eql(u8, contents["begin".len .. contents.len - "end".len], data)); - expect(mem.eql(u8, contents[contents.len - "end".len ..], "end")); - } - try os.deleteFile(tmp_file_name); -} - -test "BufferOutStream" { - var bytes: [100]u8 = undefined; - var allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; - - var buffer = try std.Buffer.initSize(allocator, 0); - var buf_stream = &std.io.BufferOutStream.init(&buffer).stream; - - const x: i32 = 42; - const y: i32 = 1234; - try buf_stream.print("x: {}\ny: {}\n", x, y); - - expect(mem.eql(u8, buffer.toSlice(), "x: 42\ny: 1234\n")); -} - -test "SliceInStream" { - const bytes = []const u8{ 1, 2, 3, 4, 5, 6, 7 }; - var ss = io.SliceInStream.init(bytes); - - var dest: [4]u8 = undefined; - - var read = try ss.stream.read(dest[0..4]); - expect(read == 4); - expect(mem.eql(u8, dest[0..4], bytes[0..4])); - - read = try ss.stream.read(dest[0..4]); - expect(read == 3); - expect(mem.eql(u8, dest[0..3], bytes[4..7])); - - read = try ss.stream.read(dest[0..4]); - expect(read == 0); -} - -test "PeekStream" { - const bytes = []const u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; - var ss = io.SliceInStream.init(bytes); - var ps = io.PeekStream(2, io.SliceInStream.Error).init(&ss.stream); - - var dest: [4]u8 = undefined; - - ps.putBackByte(9); - ps.putBackByte(10); - - var read = try ps.stream.read(dest[0..4]); - expect(read == 4); - expect(dest[0] == 10); - expect(dest[1] == 9); - expect(mem.eql(u8, dest[2..4], bytes[0..2])); - - read = try ps.stream.read(dest[0..4]); - expect(read == 4); - expect(mem.eql(u8, dest[0..4], bytes[2..6])); - - read = try ps.stream.read(dest[0..4]); - expect(read == 2); - expect(mem.eql(u8, dest[0..2], bytes[6..8])); - - ps.putBackByte(11); - ps.putBackByte(12); - - read = try ps.stream.read(dest[0..4]); - expect(read == 2); - expect(dest[0] == 12); - expect(dest[1] == 11); -} - -test "SliceOutStream" { - var buffer: [10]u8 = undefined; - var ss = io.SliceOutStream.init(buffer[0..]); - - try ss.stream.write("Hello"); - expect(mem.eql(u8, ss.getWritten(), "Hello")); - - try ss.stream.write("world"); - expect(mem.eql(u8, ss.getWritten(), "Helloworld")); - - expectError(error.OutOfSpace, ss.stream.write("!")); - expect(mem.eql(u8, ss.getWritten(), "Helloworld")); - - ss.reset(); - expect(ss.getWritten().len == 0); - - expectError(error.OutOfSpace, ss.stream.write("Hello world!")); - expect(mem.eql(u8, ss.getWritten(), "Hello worl")); -} - -test "BitInStream" { - const mem_be = []u8{ 0b11001101, 0b00001011 }; - const mem_le = []u8{ 0b00011101, 0b10010101 }; - - var mem_in_be = io.SliceInStream.init(mem_be[0..]); - const InError = io.SliceInStream.Error; - var bit_stream_be = io.BitInStream(builtin.Endian.Big, InError).init(&mem_in_be.stream); - - var out_bits: usize = undefined; - - expect(1 == try bit_stream_be.readBits(u2, 1, &out_bits)); - expect(out_bits == 1); - expect(2 == try bit_stream_be.readBits(u5, 2, &out_bits)); - expect(out_bits == 2); - expect(3 == try bit_stream_be.readBits(u128, 3, &out_bits)); - expect(out_bits == 3); - expect(4 == try bit_stream_be.readBits(u8, 4, &out_bits)); - expect(out_bits == 4); - expect(5 == try bit_stream_be.readBits(u9, 5, &out_bits)); - expect(out_bits == 5); - expect(1 == try bit_stream_be.readBits(u1, 1, &out_bits)); - expect(out_bits == 1); - - mem_in_be.pos = 0; - bit_stream_be.bit_count = 0; - expect(0b110011010000101 == try bit_stream_be.readBits(u15, 15, &out_bits)); - expect(out_bits == 15); - - mem_in_be.pos = 0; - bit_stream_be.bit_count = 0; - expect(0b1100110100001011 == try bit_stream_be.readBits(u16, 16, &out_bits)); - expect(out_bits == 16); - - _ = try bit_stream_be.readBits(u0, 0, &out_bits); - - expect(0 == try bit_stream_be.readBits(u1, 1, &out_bits)); - expect(out_bits == 0); - expectError(error.EndOfStream, bit_stream_be.readBitsNoEof(u1, 1)); - - var mem_in_le = io.SliceInStream.init(mem_le[0..]); - var bit_stream_le = io.BitInStream(builtin.Endian.Little, InError).init(&mem_in_le.stream); - - expect(1 == try bit_stream_le.readBits(u2, 1, &out_bits)); - expect(out_bits == 1); - expect(2 == try bit_stream_le.readBits(u5, 2, &out_bits)); - expect(out_bits == 2); - expect(3 == try bit_stream_le.readBits(u128, 3, &out_bits)); - expect(out_bits == 3); - expect(4 == try bit_stream_le.readBits(u8, 4, &out_bits)); - expect(out_bits == 4); - expect(5 == try bit_stream_le.readBits(u9, 5, &out_bits)); - expect(out_bits == 5); - expect(1 == try bit_stream_le.readBits(u1, 1, &out_bits)); - expect(out_bits == 1); - - mem_in_le.pos = 0; - bit_stream_le.bit_count = 0; - expect(0b001010100011101 == try bit_stream_le.readBits(u15, 15, &out_bits)); - expect(out_bits == 15); - - mem_in_le.pos = 0; - bit_stream_le.bit_count = 0; - expect(0b1001010100011101 == try bit_stream_le.readBits(u16, 16, &out_bits)); - expect(out_bits == 16); - - _ = try bit_stream_le.readBits(u0, 0, &out_bits); - - expect(0 == try bit_stream_le.readBits(u1, 1, &out_bits)); - expect(out_bits == 0); - expectError(error.EndOfStream, bit_stream_le.readBitsNoEof(u1, 1)); -} - -test "BitOutStream" { - var mem_be = []u8{0} ** 2; - var mem_le = []u8{0} ** 2; - - var mem_out_be = io.SliceOutStream.init(mem_be[0..]); - const OutError = io.SliceOutStream.Error; - var bit_stream_be = io.BitOutStream(builtin.Endian.Big, OutError).init(&mem_out_be.stream); - - try bit_stream_be.writeBits(u2(1), 1); - try bit_stream_be.writeBits(u5(2), 2); - try bit_stream_be.writeBits(u128(3), 3); - try bit_stream_be.writeBits(u8(4), 4); - try bit_stream_be.writeBits(u9(5), 5); - try bit_stream_be.writeBits(u1(1), 1); - - expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001011); - - mem_out_be.pos = 0; - - try bit_stream_be.writeBits(u15(0b110011010000101), 15); - try bit_stream_be.flushBits(); - expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001010); - - mem_out_be.pos = 0; - try bit_stream_be.writeBits(u32(0b110011010000101), 16); - expect(mem_be[0] == 0b01100110 and mem_be[1] == 0b10000101); - - try bit_stream_be.writeBits(u0(0), 0); - - var mem_out_le = io.SliceOutStream.init(mem_le[0..]); - var bit_stream_le = io.BitOutStream(builtin.Endian.Little, OutError).init(&mem_out_le.stream); - - try bit_stream_le.writeBits(u2(1), 1); - try bit_stream_le.writeBits(u5(2), 2); - try bit_stream_le.writeBits(u128(3), 3); - try bit_stream_le.writeBits(u8(4), 4); - try bit_stream_le.writeBits(u9(5), 5); - try bit_stream_le.writeBits(u1(1), 1); - - expect(mem_le[0] == 0b00011101 and mem_le[1] == 0b10010101); - - mem_out_le.pos = 0; - try bit_stream_le.writeBits(u15(0b110011010000101), 15); - try bit_stream_le.flushBits(); - expect(mem_le[0] == 0b10000101 and mem_le[1] == 0b01100110); - - mem_out_le.pos = 0; - try bit_stream_le.writeBits(u32(0b1100110100001011), 16); - expect(mem_le[0] == 0b00001011 and mem_le[1] == 0b11001101); - - try bit_stream_le.writeBits(u0(0), 0); -} - -test "BitStreams with File Stream" { - const tmp_file_name = "temp_test_file.txt"; - { - var file = try os.File.openWrite(tmp_file_name); - defer file.close(); - - var file_out = file.outStream(); - var file_out_stream = &file_out.stream; - const OutError = os.File.WriteError; - var bit_stream = io.BitOutStream(builtin.endian, OutError).init(file_out_stream); - - try bit_stream.writeBits(u2(1), 1); - try bit_stream.writeBits(u5(2), 2); - try bit_stream.writeBits(u128(3), 3); - try bit_stream.writeBits(u8(4), 4); - try bit_stream.writeBits(u9(5), 5); - try bit_stream.writeBits(u1(1), 1); - try bit_stream.flushBits(); - } - { - var file = try os.File.openRead(tmp_file_name); - defer file.close(); - - var file_in = file.inStream(); - var file_in_stream = &file_in.stream; - const InError = os.File.ReadError; - var bit_stream = io.BitInStream(builtin.endian, InError).init(file_in_stream); - - var out_bits: usize = undefined; - - expect(1 == try bit_stream.readBits(u2, 1, &out_bits)); - expect(out_bits == 1); - expect(2 == try bit_stream.readBits(u5, 2, &out_bits)); - expect(out_bits == 2); - expect(3 == try bit_stream.readBits(u128, 3, &out_bits)); - expect(out_bits == 3); - expect(4 == try bit_stream.readBits(u8, 4, &out_bits)); - expect(out_bits == 4); - expect(5 == try bit_stream.readBits(u9, 5, &out_bits)); - expect(out_bits == 5); - expect(1 == try bit_stream.readBits(u1, 1, &out_bits)); - expect(out_bits == 1); - - expectError(error.EndOfStream, bit_stream.readBitsNoEof(u1, 1)); - } - try os.deleteFile(tmp_file_name); -} - -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; - - const total_bytes = comptime blk: { - var bytes = 0; - comptime var i = 0; - while (i <= max_test_bitsize) : (i += 1) bytes += (i / 8) + @boolToInt(i % 8 > 0); - break :blk bytes * 2; - }; - - var data_mem: [total_bytes]u8 = undefined; - var out = io.SliceOutStream.init(data_mem[0..]); - const OutError = io.SliceOutStream.Error; - var out_stream = &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, packing, InError).init(in_stream); - - comptime var i = 0; - inline while (i <= max_test_bitsize) : (i += 1) { - const U = @IntType(false, i); - const S = @IntType(true, i); - try serializer.serializeInt(U(i)); - if (i != 0) try serializer.serializeInt(S(-1)) else try serializer.serialize(S(0)); - } - try serializer.flush(); - - i = 0; - inline while (i <= max_test_bitsize) : (i += 1) { - const U = @IntType(false, i); - const S = @IntType(true, i); - const x = try deserializer.deserializeInt(U); - const y = try deserializer.deserializeInt(S); - expect(x == U(i)); - if (i != 0) expect(y == S(-1)) else expect(y == 0); - } - - const u8_bit_count = comptime meta.bitCount(u8); - //0 + 1 + 2 + ... n = (n * (n + 1)) / 2 - //and we have each for unsigned and signed, so * 2 - const total_bits = (max_test_bitsize * (max_test_bitsize + 1)); - 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 (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, packing, NullError).init(null_out_stream); - try null_serializer.serialize(data_mem[0..]); - try null_serializer.flush(); -} - -test "Serializer/Deserializer Int" { - 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); - //try testIntSerializerDeserializer(builtin.Endian.Little, true); -} - -fn testIntSerializerDeserializerInfNaN( - comptime endian: builtin.Endian, - 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; - - var out = io.SliceOutStream.init(data_mem[0..]); - const OutError = io.SliceOutStream.Error; - var out_stream = &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, packing, InError).init(in_stream); - - //@TODO: isInf/isNan not currently implemented for f128. - try serializer.serialize(std.math.nan(f16)); - try serializer.serialize(std.math.inf(f16)); - try serializer.serialize(std.math.nan(f32)); - try serializer.serialize(std.math.inf(f32)); - try serializer.serialize(std.math.nan(f64)); - try serializer.serialize(std.math.inf(f64)); - //try serializer.serialize(std.math.nan(f128)); - //try serializer.serialize(std.math.inf(f128)); - const nan_check_f16 = try deserializer.deserialize(f16); - const inf_check_f16 = try deserializer.deserialize(f16); - const nan_check_f32 = try deserializer.deserialize(f32); - const inf_check_f32 = try deserializer.deserialize(f32); - const nan_check_f64 = try deserializer.deserialize(f64); - const inf_check_f64 = try deserializer.deserialize(f64); - //const nan_check_f128 = try deserializer.deserialize(f128); - //const inf_check_f128 = try deserializer.deserialize(f128); - expect(std.math.isNan(nan_check_f16)); - expect(std.math.isInf(inf_check_f16)); - expect(std.math.isNan(nan_check_f32)); - expect(std.math.isInf(inf_check_f32)); - expect(std.math.isNan(nan_check_f64)); - expect(std.math.isInf(inf_check_f64)); - //expect(std.math.isNan(nan_check_f128)); - //expect(std.math.isInf(inf_check_f128)); -} - -test "Serializer/Deserializer Int: Inf/NaN" { - 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 packing: io.Packing) !void { - const ColorType = enum(u4) { - RGB8 = 1, - RA16 = 2, - R32 = 3, - }; - - const TagAlign = union(enum(u32)) { - A: u8, - B: u8, - C: u8, - }; - - const Color = union(ColorType) { - RGB8: struct { - r: u8, - g: u8, - b: u8, - a: u8, - }, - RA16: struct { - r: u16, - a: u16, - }, - R32: u32, - }; - - const PackedStruct = packed struct { - f_i3: i3, - f_u2: u2, - }; - - //to test custom serialization - const Custom = struct { - f_f16: f16, - f_unused_u32: u32, - - pub fn deserialize(self: *@This(), deserializer: var) !void { - try deserializer.deserializeInto(&self.f_f16); - self.f_unused_u32 = 47; - } - - pub const serialize = testAlternateSerializer; - }; - - const MyStruct = struct { - f_i3: i3, - f_u8: u8, - f_tag_align: TagAlign, - f_u24: u24, - f_i19: i19, - f_void: void, - f_f32: f32, - f_f128: f128, - f_packed_0: PackedStruct, - f_i7arr: [10]i7, - f_of64n: ?f64, - f_of64v: ?f64, - f_color_type: ColorType, - f_packed_1: PackedStruct, - f_custom: Custom, - f_color: Color, - }; - - const my_inst = MyStruct{ - .f_i3 = -1, - .f_u8 = 8, - .f_tag_align = TagAlign{ .B = 148 }, - .f_u24 = 24, - .f_i19 = 19, - .f_void = {}, - .f_f32 = 32.32, - .f_f128 = 128.128, - .f_packed_0 = PackedStruct{ .f_i3 = -1, .f_u2 = 2 }, - .f_i7arr = [10]i7{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, - .f_of64n = null, - .f_of64v = 64.64, - .f_color_type = ColorType.R32, - .f_packed_1 = PackedStruct{ .f_i3 = 1, .f_u2 = 1 }, - .f_custom = Custom{ .f_f16 = 38.63, .f_unused_u32 = 47 }, - .f_color = Color{ .R32 = 123822 }, - }; - - var data_mem: [@sizeOf(MyStruct)]u8 = undefined; - var out = io.SliceOutStream.init(data_mem[0..]); - const OutError = io.SliceOutStream.Error; - var out_stream = &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, packing, InError).init(in_stream); - - try serializer.serialize(my_inst); - - const my_copy = try deserializer.deserialize(MyStruct); - expect(meta.eql(my_copy, my_inst)); -} - -test "Serializer/Deserializer generic" { - 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 packing: io.Packing) !void { - const E = enum(u14) { - One = 1, - Two = 2, - }; - - const A = struct { - e: E, - }; - - const C = union(E) { - One: u14, - Two: f16, - }; - - var data_mem: [4]u8 = undefined; - var out = io.SliceOutStream.init(data_mem[0..]); - const OutError = io.SliceOutStream.Error; - var out_stream = &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, packing, InError).init(in_stream); - - try serializer.serialize(u14(3)); - expectError(error.InvalidEnumTag, deserializer.deserialize(A)); - out.pos = 0; - try serializer.serialize(u14(3)); - try serializer.serialize(u14(88)); - expectError(error.InvalidEnumTag, deserializer.deserialize(C)); -} - -test "Deserializer bad data" { - try testBadData(.Big, .Byte); - try testBadData(.Little, .Byte); - try testBadData(.Big, .Bit); - try testBadData(.Little, .Bit); -}