| author | |
| committer | |
| log | 18f1fef1426cb0405c733890b2e1d8d48627e4fe |
| tree | f019a3291b74a42206691a75e2832802bb50abc7 |
| parent | b6fbd524f122449e6e2bb4d73ce3f59b01286f50 |
| signature |
22 files changed, 1411 insertions(+), 1379 deletions(-)
lib/std/atomic/queue.zig+14-19| ... | @@ -104,21 +104,17 @@ pub fn Queue(comptime T: type) type { | ... | @@ -104,21 +104,17 @@ pub fn Queue(comptime T: type) type { |
| 104 | } | 104 | } |
| 105 | 105 | ||
| 106 | pub fn dump(self: *Self) void { | 106 | pub fn dump(self: *Self) void { |
| 107 | var stderr_file = std.io.getStdErr() catch return; | 107 | self.dumpToStream(std.io.getStdErr().outStream()) catch return; |
| 108 | const stderr = &stderr_file.outStream().stream; | ||
| 109 | const Error = @typeInfo(@TypeOf(stderr)).Pointer.child.Error; | ||
| 110 | |||
| 111 | self.dumpToStream(Error, stderr) catch return; | ||
| 112 | } | 108 | } |
| 113 | 109 | ||
| 114 | pub fn dumpToStream(self: *Self, comptime Error: type, stream: *std.io.OutStream(Error)) Error!void { | 110 | pub fn dumpToStream(self: *Self, stream: var) !void { |
| 115 | const S = struct { | 111 | const S = struct { |
| 116 | fn dumpRecursive( | 112 | fn dumpRecursive( |
| 117 | s: *std.io.OutStream(Error), | 113 | s: var, |
| 118 | optional_node: ?*Node, | 114 | optional_node: ?*Node, |
| 119 | indent: usize, | 115 | indent: usize, |
| 120 | comptime depth: comptime_int, | 116 | comptime depth: comptime_int, |
| 121 | ) Error!void { | 117 | ) !void { |
| 122 | try s.writeByteNTimes(' ', indent); | 118 | try s.writeByteNTimes(' ', indent); |
| 123 | if (optional_node) |node| { | 119 | if (optional_node) |node| { |
| 124 | try s.print("0x{x}={}\n", .{ @ptrToInt(node), node.data }); | 120 | try s.print("0x{x}={}\n", .{ @ptrToInt(node), node.data }); |
| ... | @@ -326,17 +322,16 @@ test "std.atomic.Queue single-threaded" { | ... | @@ -326,17 +322,16 @@ test "std.atomic.Queue single-threaded" { |
| 326 | 322 | ||
| 327 | test "std.atomic.Queue dump" { | 323 | test "std.atomic.Queue dump" { |
| 328 | const mem = std.mem; | 324 | const mem = std.mem; |
| 329 | const SliceOutStream = std.io.SliceOutStream; | ||
| 330 | var buffer: [1024]u8 = undefined; | 325 | var buffer: [1024]u8 = undefined; |
| 331 | var expected_buffer: [1024]u8 = undefined; | 326 | var expected_buffer: [1024]u8 = undefined; |
| 332 | var sos = SliceOutStream.init(buffer[0..]); | 327 | var fbs = std.io.fixedBufferStream(&buffer); |
| 333 | 328 | ||
| 334 | var queue = Queue(i32).init(); | 329 | var queue = Queue(i32).init(); |
| 335 | 330 | ||
| 336 | // Test empty stream | 331 | // Test empty stream |
| 337 | sos.reset(); | 332 | fbs.reset(); |
| 338 | try queue.dumpToStream(SliceOutStream.Error, &sos.stream); | 333 | try queue.dumpToStream(fbs.outStream()); |
| 339 | expect(mem.eql(u8, buffer[0..sos.pos], | 334 | expect(mem.eql(u8, buffer[0..fbs.pos], |
| 340 | \\head: (null) | 335 | \\head: (null) |
| 341 | \\tail: (null) | 336 | \\tail: (null) |
| 342 | \\ | 337 | \\ |
| ... | @@ -350,8 +345,8 @@ test "std.atomic.Queue dump" { | ... | @@ -350,8 +345,8 @@ test "std.atomic.Queue dump" { |
| 350 | }; | 345 | }; |
| 351 | queue.put(&node_0); | 346 | queue.put(&node_0); |
| 352 | 347 | ||
| 353 | sos.reset(); | 348 | fbs.reset(); |
| 354 | try queue.dumpToStream(SliceOutStream.Error, &sos.stream); | 349 | try queue.dumpToStream(fbs.outStream()); |
| 355 | 350 | ||
| 356 | var expected = try std.fmt.bufPrint(expected_buffer[0..], | 351 | var expected = try std.fmt.bufPrint(expected_buffer[0..], |
| 357 | \\head: 0x{x}=1 | 352 | \\head: 0x{x}=1 |
| ... | @@ -360,7 +355,7 @@ test "std.atomic.Queue dump" { | ... | @@ -360,7 +355,7 @@ test "std.atomic.Queue dump" { |
| 360 | \\ (null) | 355 | \\ (null) |
| 361 | \\ | 356 | \\ |
| 362 | , .{ @ptrToInt(queue.head), @ptrToInt(queue.tail) }); | 357 | , .{ @ptrToInt(queue.head), @ptrToInt(queue.tail) }); |
| 363 | expect(mem.eql(u8, buffer[0..sos.pos], expected)); | 358 | expect(mem.eql(u8, buffer[0..fbs.pos], expected)); |
| 364 | 359 | ||
| 365 | // Test a stream with two elements | 360 | // Test a stream with two elements |
| 366 | var node_1 = Queue(i32).Node{ | 361 | var node_1 = Queue(i32).Node{ |
| ... | @@ -370,8 +365,8 @@ test "std.atomic.Queue dump" { | ... | @@ -370,8 +365,8 @@ test "std.atomic.Queue dump" { |
| 370 | }; | 365 | }; |
| 371 | queue.put(&node_1); | 366 | queue.put(&node_1); |
| 372 | 367 | ||
| 373 | sos.reset(); | 368 | fbs.reset(); |
| 374 | try queue.dumpToStream(SliceOutStream.Error, &sos.stream); | 369 | try queue.dumpToStream(fbs.outStream()); |
| 375 | 370 | ||
| 376 | expected = try std.fmt.bufPrint(expected_buffer[0..], | 371 | expected = try std.fmt.bufPrint(expected_buffer[0..], |
| 377 | \\head: 0x{x}=1 | 372 | \\head: 0x{x}=1 |
| ... | @@ -381,5 +376,5 @@ test "std.atomic.Queue dump" { | ... | @@ -381,5 +376,5 @@ test "std.atomic.Queue dump" { |
| 381 | \\ (null) | 376 | \\ (null) |
| 382 | \\ | 377 | \\ |
| 383 | , .{ @ptrToInt(queue.head), @ptrToInt(queue.head.?.next), @ptrToInt(queue.tail) }); | 378 | , .{ @ptrToInt(queue.head), @ptrToInt(queue.head.?.next), @ptrToInt(queue.tail) }); |
| 384 | expect(mem.eql(u8, buffer[0..sos.pos], expected)); | 379 | expect(mem.eql(u8, buffer[0..fbs.pos], expected)); |
| 385 | } | 380 | } |
lib/std/buffer.zig+12| ... | @@ -219,3 +219,15 @@ test "Buffer.print" { | ... | @@ -219,3 +219,15 @@ test "Buffer.print" { |
| 219 | try buf.print("Hello {} the {}", .{ 2, "world" }); | 219 | try buf.print("Hello {} the {}", .{ 2, "world" }); |
| 220 | testing.expect(buf.eql("Hello 2 the world")); | 220 | testing.expect(buf.eql("Hello 2 the world")); |
| 221 | } | 221 | } |
| 222 | |||
| 223 | test "Buffer.outStream" { | ||
| 224 | var buffer = try Buffer.initSize(testing.allocator, 0); | ||
| 225 | defer buffer.deinit(); | ||
| 226 | const buf_stream = buffer.outStream(); | ||
| 227 | |||
| 228 | const x: i32 = 42; | ||
| 229 | const y: i32 = 1234; | ||
| 230 | try buf_stream.print("x: {}\ny: {}\n", .{ x, y }); | ||
| 231 | |||
| 232 | testing.expect(mem.eql(u8, buffer.toSlice(), "x: 42\ny: 1234\n")); | ||
| 233 | } |
lib/std/debug/leb128.zig+12-12| ... | @@ -121,18 +121,18 @@ pub fn readILEB128Mem(comptime T: type, ptr: *[*]const u8) !T { | ... | @@ -121,18 +121,18 @@ pub fn readILEB128Mem(comptime T: type, ptr: *[*]const u8) !T { |
| 121 | } | 121 | } |
| 122 | 122 | ||
| 123 | fn test_read_stream_ileb128(comptime T: type, encoded: []const u8) !T { | 123 | fn test_read_stream_ileb128(comptime T: type, encoded: []const u8) !T { |
| 124 | var in_stream = std.io.SliceInStream.init(encoded); | 124 | var in_stream = std.io.fixedBufferStream(encoded); |
| 125 | return try readILEB128(T, &in_stream.stream); | 125 | return try readILEB128(T, in_stream.inStream()); |
| 126 | } | 126 | } |
| 127 | 127 | ||
| 128 | fn test_read_stream_uleb128(comptime T: type, encoded: []const u8) !T { | 128 | fn test_read_stream_uleb128(comptime T: type, encoded: []const u8) !T { |
| 129 | var in_stream = std.io.SliceInStream.init(encoded); | 129 | var in_stream = std.io.fixedBufferStream(encoded); |
| 130 | return try readULEB128(T, &in_stream.stream); | 130 | return try readULEB128(T, in_stream.inStream()); |
| 131 | } | 131 | } |
| 132 | 132 | ||
| 133 | fn test_read_ileb128(comptime T: type, encoded: []const u8) !T { | 133 | fn test_read_ileb128(comptime T: type, encoded: []const u8) !T { |
| 134 | var in_stream = std.io.SliceInStream.init(encoded); | 134 | var in_stream = std.io.fixedBufferStream(encoded); |
| 135 | const v1 = readILEB128(T, &in_stream.stream); | 135 | const v1 = readILEB128(T, in_stream.inStream()); |
| 136 | var in_ptr = encoded.ptr; | 136 | var in_ptr = encoded.ptr; |
| 137 | const v2 = readILEB128Mem(T, &in_ptr); | 137 | const v2 = readILEB128Mem(T, &in_ptr); |
| 138 | testing.expectEqual(v1, v2); | 138 | testing.expectEqual(v1, v2); |
| ... | @@ -140,8 +140,8 @@ fn test_read_ileb128(comptime T: type, encoded: []const u8) !T { | ... | @@ -140,8 +140,8 @@ fn test_read_ileb128(comptime T: type, encoded: []const u8) !T { |
| 140 | } | 140 | } |
| 141 | 141 | ||
| 142 | fn test_read_uleb128(comptime T: type, encoded: []const u8) !T { | 142 | fn test_read_uleb128(comptime T: type, encoded: []const u8) !T { |
| 143 | var in_stream = std.io.SliceInStream.init(encoded); | 143 | var in_stream = std.io.fixedBufferStream(encoded); |
| 144 | const v1 = readULEB128(T, &in_stream.stream); | 144 | const v1 = readULEB128(T, in_stream.inStream()); |
| 145 | var in_ptr = encoded.ptr; | 145 | var in_ptr = encoded.ptr; |
| 146 | const v2 = readULEB128Mem(T, &in_ptr); | 146 | const v2 = readULEB128Mem(T, &in_ptr); |
| 147 | testing.expectEqual(v1, v2); | 147 | testing.expectEqual(v1, v2); |
| ... | @@ -149,22 +149,22 @@ fn test_read_uleb128(comptime T: type, encoded: []const u8) !T { | ... | @@ -149,22 +149,22 @@ fn test_read_uleb128(comptime T: type, encoded: []const u8) !T { |
| 149 | } | 149 | } |
| 150 | 150 | ||
| 151 | fn test_read_ileb128_seq(comptime T: type, comptime N: usize, encoded: []const u8) void { | 151 | fn test_read_ileb128_seq(comptime T: type, comptime N: usize, encoded: []const u8) void { |
| 152 | var in_stream = std.io.SliceInStream.init(encoded); | 152 | var in_stream = std.io.fixedBufferStream(encoded); |
| 153 | var in_ptr = encoded.ptr; | 153 | var in_ptr = encoded.ptr; |
| 154 | var i: usize = 0; | 154 | var i: usize = 0; |
| 155 | while (i < N) : (i += 1) { | 155 | while (i < N) : (i += 1) { |
| 156 | const v1 = readILEB128(T, &in_stream.stream); | 156 | const v1 = readILEB128(T, in_stream.inStream()); |
| 157 | const v2 = readILEB128Mem(T, &in_ptr); | 157 | const v2 = readILEB128Mem(T, &in_ptr); |
| 158 | testing.expectEqual(v1, v2); | 158 | testing.expectEqual(v1, v2); |
| 159 | } | 159 | } |
| 160 | } | 160 | } |
| 161 | 161 | ||
| 162 | fn test_read_uleb128_seq(comptime T: type, comptime N: usize, encoded: []const u8) void { | 162 | fn test_read_uleb128_seq(comptime T: type, comptime N: usize, encoded: []const u8) void { |
| 163 | var in_stream = std.io.SliceInStream.init(encoded); | 163 | var in_stream = std.io.fixedBufferStream(encoded); |
| 164 | var in_ptr = encoded.ptr; | 164 | var in_ptr = encoded.ptr; |
| 165 | var i: usize = 0; | 165 | var i: usize = 0; |
| 166 | while (i < N) : (i += 1) { | 166 | while (i < N) : (i += 1) { |
| 167 | const v1 = readULEB128(T, &in_stream.stream); | 167 | const v1 = readULEB128(T, in_stream.inStream()); |
| 168 | const v2 = readULEB128Mem(T, &in_ptr); | 168 | const v2 = readULEB128Mem(T, &in_ptr); |
| 169 | testing.expectEqual(v1, v2); | 169 | testing.expectEqual(v1, v2); |
| 170 | } | 170 | } |
lib/std/heap.zig+1| ... | @@ -10,6 +10,7 @@ const c = std.c; | ... | @@ -10,6 +10,7 @@ const c = std.c; |
| 10 | const maxInt = std.math.maxInt; | 10 | const maxInt = std.math.maxInt; |
| 11 | 11 | ||
| 12 | pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator; | 12 | pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator; |
| 13 | pub const loggingAllocator = @import("heap/logging_allocator.zig").loggingAllocator; | ||
| 13 | 14 | ||
| 14 | const Allocator = mem.Allocator; | 15 | const Allocator = mem.Allocator; |
| 15 | 16 |
lib/std/heap/logging_allocator.zig+51-45| ... | @@ -1,63 +1,69 @@ | ... | @@ -1,63 +1,69 @@ |
| 1 | const std = @import("../std.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const Allocator = std.mem.Allocator; | 2 | const Allocator = std.mem.Allocator; |
| 3 | 3 | ||
| 4 | const AnyErrorOutStream = std.io.OutStream(anyerror); | ||
| 5 | |||
| 6 | /// This allocator is used in front of another allocator and logs to the provided stream | 4 | /// This allocator is used in front of another allocator and logs to the provided stream |
| 7 | /// on every call to the allocator. Stream errors are ignored. | 5 | /// on every call to the allocator. Stream errors are ignored. |
| 8 | /// If https://github.com/ziglang/zig/issues/2586 is implemented, this API can be improved. | 6 | /// If https://github.com/ziglang/zig/issues/2586 is implemented, this API can be improved. |
| 9 | pub const LoggingAllocator = struct { | 7 | pub fn LoggingAllocator(comptime OutStreamType: type) type { |
| 10 | allocator: Allocator, | 8 | return struct { |
| 11 | parent_allocator: *Allocator, | 9 | allocator: Allocator, |
| 12 | out_stream: *AnyErrorOutStream, | 10 | parent_allocator: *Allocator, |
| 11 | out_stream: OutStreamType, | ||
| 13 | 12 | ||
| 14 | const Self = @This(); | 13 | const Self = @This(); |
| 15 | 14 | ||
| 16 | pub fn init(parent_allocator: *Allocator, out_stream: *AnyErrorOutStream) Self { | 15 | pub fn init(parent_allocator: *Allocator, out_stream: OutStreamType) Self { |
| 17 | return Self{ | 16 | return Self{ |
| 18 | .allocator = Allocator{ | 17 | .allocator = Allocator{ |
| 19 | .reallocFn = realloc, | 18 | .reallocFn = realloc, |
| 20 | .shrinkFn = shrink, | 19 | .shrinkFn = shrink, |
| 21 | }, | 20 | }, |
| 22 | .parent_allocator = parent_allocator, | 21 | .parent_allocator = parent_allocator, |
| 23 | .out_stream = out_stream, | 22 | .out_stream = out_stream, |
| 24 | }; | 23 | }; |
| 25 | } | ||
| 26 | |||
| 27 | fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { | ||
| 28 | const self = @fieldParentPtr(Self, "allocator", allocator); | ||
| 29 | if (old_mem.len == 0) { | ||
| 30 | self.out_stream.print("allocation of {} ", .{new_size}) catch {}; | ||
| 31 | } else { | ||
| 32 | self.out_stream.print("resize from {} to {} ", .{ old_mem.len, new_size }) catch {}; | ||
| 33 | } | 24 | } |
| 34 | const result = self.parent_allocator.reallocFn(self.parent_allocator, old_mem, old_align, new_size, new_align); | 25 | |
| 35 | if (result) |buff| { | 26 | fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { |
| 36 | self.out_stream.print("success!\n", .{}) catch {}; | 27 | const self = @fieldParentPtr(Self, "allocator", allocator); |
| 37 | } else |err| { | 28 | if (old_mem.len == 0) { |
| 38 | self.out_stream.print("failure!\n", .{}) catch {}; | 29 | self.out_stream.print("allocation of {} ", .{new_size}) catch {}; |
| 30 | } else { | ||
| 31 | self.out_stream.print("resize from {} to {} ", .{ old_mem.len, new_size }) catch {}; | ||
| 32 | } | ||
| 33 | const result = self.parent_allocator.reallocFn(self.parent_allocator, old_mem, old_align, new_size, new_align); | ||
| 34 | if (result) |buff| { | ||
| 35 | self.out_stream.print("success!\n", .{}) catch {}; | ||
| 36 | } else |err| { | ||
| 37 | self.out_stream.print("failure!\n", .{}) catch {}; | ||
| 38 | } | ||
| 39 | return result; | ||
| 39 | } | 40 | } |
| 40 | return result; | ||
| 41 | } | ||
| 42 | 41 | ||
| 43 | fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { | 42 | fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { |
| 44 | const self = @fieldParentPtr(Self, "allocator", allocator); | 43 | const self = @fieldParentPtr(Self, "allocator", allocator); |
| 45 | const result = self.parent_allocator.shrinkFn(self.parent_allocator, old_mem, old_align, new_size, new_align); | 44 | const result = self.parent_allocator.shrinkFn(self.parent_allocator, old_mem, old_align, new_size, new_align); |
| 46 | if (new_size == 0) { | 45 | if (new_size == 0) { |
| 47 | self.out_stream.print("free of {} bytes success!\n", .{old_mem.len}) catch {}; | 46 | self.out_stream.print("free of {} bytes success!\n", .{old_mem.len}) catch {}; |
| 48 | } else { | 47 | } else { |
| 49 | self.out_stream.print("shrink from {} bytes to {} bytes success!\n", .{ old_mem.len, new_size }) catch {}; | 48 | self.out_stream.print("shrink from {} bytes to {} bytes success!\n", .{ old_mem.len, new_size }) catch {}; |
| 49 | } | ||
| 50 | return result; | ||
| 50 | } | 51 | } |
| 51 | return result; | 52 | }; |
| 52 | } | 53 | } |
| 53 | }; | 54 | |
| 55 | pub fn loggingAllocator( | ||
| 56 | parent_allocator: *Allocator, | ||
| 57 | out_stream: var, | ||
| 58 | ) LoggingAllocator(@TypeOf(out_stream)) { | ||
| 59 | return LoggingAllocator(@TypeOf(out_stream)).init(parent_allocator, out_stream); | ||
| 60 | } | ||
| 54 | 61 | ||
| 55 | test "LoggingAllocator" { | 62 | test "LoggingAllocator" { |
| 56 | var buf: [255]u8 = undefined; | 63 | var buf: [255]u8 = undefined; |
| 57 | var slice_stream = std.io.SliceOutStream.init(buf[0..]); | 64 | var fbs = std.io.fixedBufferStream(&buf); |
| 58 | const stream = &slice_stream.stream; | ||
| 59 | 65 | ||
| 60 | const allocator = &LoggingAllocator.init(std.testing.allocator, @ptrCast(*AnyErrorOutStream, stream)).allocator; | 66 | const allocator = &loggingAllocator(std.testing.allocator, fbs.outStream()).allocator; |
| 61 | 67 | ||
| 62 | const ptr = try allocator.alloc(u8, 10); | 68 | const ptr = try allocator.alloc(u8, 10); |
| 63 | allocator.free(ptr); | 69 | allocator.free(ptr); |
| ... | @@ -66,5 +72,5 @@ test "LoggingAllocator" { | ... | @@ -66,5 +72,5 @@ test "LoggingAllocator" { |
| 66 | \\allocation of 10 success! | 72 | \\allocation of 10 success! |
| 67 | \\free of 10 bytes success! | 73 | \\free of 10 bytes success! |
| 68 | \\ | 74 | \\ |
| 69 | , slice_stream.getWritten()); | 75 | , fbs.getWritten()); |
| 70 | } | 76 | } |
lib/std/io.zig+25-739| ... | @@ -4,17 +4,13 @@ const root = @import("root"); | ... | @@ -4,17 +4,13 @@ const root = @import("root"); |
| 4 | const c = std.c; | 4 | const c = std.c; |
| 5 | 5 | ||
| 6 | const math = std.math; | 6 | const math = std.math; |
| 7 | const debug = std.debug; | 7 | const assert = std.debug.assert; |
| 8 | const assert = debug.assert; | ||
| 9 | const os = std.os; | 8 | const os = std.os; |
| 10 | const fs = std.fs; | 9 | const fs = std.fs; |
| 11 | const mem = std.mem; | 10 | const mem = std.mem; |
| 12 | const meta = std.meta; | 11 | const meta = std.meta; |
| 13 | const trait = meta.trait; | 12 | const trait = meta.trait; |
| 14 | const Buffer = std.Buffer; | ||
| 15 | const fmt = std.fmt; | ||
| 16 | const File = std.fs.File; | 13 | const File = std.fs.File; |
| 17 | const testing = std.testing; | ||
| 18 | 14 | ||
| 19 | pub const Mode = enum { | 15 | pub const Mode = enum { |
| 20 | /// I/O operates normally, waiting for the operating system syscalls to complete. | 16 | /// I/O operates normally, waiting for the operating system syscalls to complete. |
| ... | @@ -92,10 +88,9 @@ pub fn getStdIn() File { | ... | @@ -92,10 +88,9 @@ pub fn getStdIn() File { |
| 92 | }; | 88 | }; |
| 93 | } | 89 | } |
| 94 | 90 | ||
| 95 | pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream; | ||
| 96 | pub const InStream = @import("io/in_stream.zig").InStream; | 91 | pub const InStream = @import("io/in_stream.zig").InStream; |
| 97 | pub const OutStream = @import("io/out_stream.zig").OutStream; | 92 | pub const OutStream = @import("io/out_stream.zig").OutStream; |
| 98 | pub const BufferedAtomicFile = @import("io/buffered_atomic_file.zig").BufferedAtomicFile; | 93 | pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream; |
| 99 | 94 | ||
| 100 | pub const BufferedOutStream = @import("io/buffered_out_stream.zig").BufferedOutStream; | 95 | pub const BufferedOutStream = @import("io/buffered_out_stream.zig").BufferedOutStream; |
| 101 | pub const bufferedOutStream = @import("io/buffered_out_stream.zig").bufferedOutStream; | 96 | pub const bufferedOutStream = @import("io/buffered_out_stream.zig").bufferedOutStream; |
| ... | @@ -103,36 +98,33 @@ pub const bufferedOutStream = @import("io/buffered_out_stream.zig").bufferedOutS | ... | @@ -103,36 +98,33 @@ pub const bufferedOutStream = @import("io/buffered_out_stream.zig").bufferedOutS |
| 103 | pub const BufferedInStream = @import("io/buffered_in_stream.zig").BufferedInStream; | 98 | pub const BufferedInStream = @import("io/buffered_in_stream.zig").BufferedInStream; |
| 104 | pub const bufferedInStream = @import("io/buffered_in_stream.zig").bufferedInStream; | 99 | pub const bufferedInStream = @import("io/buffered_in_stream.zig").bufferedInStream; |
| 105 | 100 | ||
| 101 | pub const PeekStream = @import("io/peek_stream.zig").PeekStream; | ||
| 102 | pub const peekStream = @import("io/peek_stream.zig").peekStream; | ||
| 103 | |||
| 106 | pub const FixedBufferStream = @import("io/fixed_buffer_stream.zig").FixedBufferStream; | 104 | pub const FixedBufferStream = @import("io/fixed_buffer_stream.zig").FixedBufferStream; |
| 107 | pub const fixedBufferStream = @import("io/fixed_buffer_stream.zig").fixedBufferStream; | 105 | pub const fixedBufferStream = @import("io/fixed_buffer_stream.zig").fixedBufferStream; |
| 108 | 106 | ||
| 107 | pub const COutStream = @import("io/c_out_stream.zig").COutStream; | ||
| 108 | pub const cOutStream = @import("io/c_out_stream.zig").cOutStream; | ||
| 109 | |||
| 109 | pub const CountingOutStream = @import("io/counting_out_stream.zig").CountingOutStream; | 110 | pub const CountingOutStream = @import("io/counting_out_stream.zig").CountingOutStream; |
| 111 | pub const countingOutStream = @import("io/counting_out_stream.zig").countingOutStream; | ||
| 110 | 112 | ||
| 111 | pub fn cOutStream(c_file: *std.c.FILE) COutStream { | 113 | pub const BitInStream = @import("io/bit_in_stream.zig").BitInStream; |
| 112 | return .{ .context = c_file }; | 114 | pub const bitInStream = @import("io/bit_in_stream.zig").bitInStream; |
| 113 | } | ||
| 114 | 115 | ||
| 115 | pub const COutStream = OutStream(*std.c.FILE, std.fs.File.WriteError, cOutStreamWrite); | 116 | pub const BitOutStream = @import("io/bit_out_stream.zig").BitOutStream; |
| 116 | 117 | pub const bitOutStream = @import("io/bit_out_stream.zig").bitOutStream; | |
| 117 | pub fn cOutStreamWrite(c_file: *std.c.FILE, bytes: []const u8) std.fs.File.WriteError!usize { | 118 | |
| 118 | const amt_written = std.c.fwrite(bytes.ptr, 1, bytes.len, c_file); | 119 | pub const Packing = @import("io/serialization.zig").Packing; |
| 119 | if (amt_written >= 0) return amt_written; | 120 | |
| 120 | switch (std.c._errno().*) { | 121 | pub const Serializer = @import("io/serialization.zig").Serializer; |
| 121 | 0 => unreachable, | 122 | pub const serializer = @import("io/serialization.zig").serializer; |
| 122 | os.EINVAL => unreachable, | 123 | |
| 123 | os.EFAULT => unreachable, | 124 | pub const Deserializer = @import("io/serialization.zig").Deserializer; |
| 124 | os.EAGAIN => unreachable, // this is a blocking API | 125 | pub const deserializer = @import("io/serialization.zig").deserializer; |
| 125 | os.EBADF => unreachable, // always a race condition | 126 | |
| 126 | os.EDESTADDRREQ => unreachable, // connect was never called | 127 | pub const BufferedAtomicFile = @import("io/buffered_atomic_file.zig").BufferedAtomicFile; |
| 127 | os.EDQUOT => return error.DiskQuota, | ||
| 128 | os.EFBIG => return error.FileTooBig, | ||
| 129 | os.EIO => return error.InputOutput, | ||
| 130 | os.ENOSPC => return error.NoSpaceLeft, | ||
| 131 | os.EPERM => return error.AccessDenied, | ||
| 132 | os.EPIPE => return error.BrokenPipe, | ||
| 133 | else => |err| return os.unexpectedErrno(@intCast(usize, err)), | ||
| 134 | } | ||
| 135 | } | ||
| 136 | 128 | ||
| 137 | /// Deprecated; use `std.fs.Dir.writeFile`. | 129 | /// Deprecated; use `std.fs.Dir.writeFile`. |
| 138 | pub fn writeFile(path: []const u8, data: []const u8) !void { | 130 | pub fn writeFile(path: []const u8, data: []const u8) !void { |
| ... | @@ -144,249 +136,6 @@ pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 { | ... | @@ -144,249 +136,6 @@ pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 { |
| 144 | return fs.cwd().readFileAlloc(allocator, path, math.maxInt(usize)); | 136 | return fs.cwd().readFileAlloc(allocator, path, math.maxInt(usize)); |
| 145 | } | 137 | } |
| 146 | 138 | ||
| 147 | /// Creates a stream which supports 'un-reading' data, so that it can be read again. | ||
| 148 | /// This makes look-ahead style parsing much easier. | ||
| 149 | pub fn PeekStream(comptime buffer_type: std.fifo.LinearFifoBufferType, comptime InStreamError: type) type { | ||
| 150 | return struct { | ||
| 151 | const Self = @This(); | ||
| 152 | pub const Error = InStreamError; | ||
| 153 | pub const Stream = InStream(Error); | ||
| 154 | |||
| 155 | stream: Stream, | ||
| 156 | base: *Stream, | ||
| 157 | |||
| 158 | const FifoType = std.fifo.LinearFifo(u8, buffer_type); | ||
| 159 | fifo: FifoType, | ||
| 160 | |||
| 161 | pub usingnamespace switch (buffer_type) { | ||
| 162 | .Static => struct { | ||
| 163 | pub fn init(base: *Stream) Self { | ||
| 164 | return .{ | ||
| 165 | .base = base, | ||
| 166 | .fifo = FifoType.init(), | ||
| 167 | .stream = Stream{ .readFn = readFn }, | ||
| 168 | }; | ||
| 169 | } | ||
| 170 | }, | ||
| 171 | .Slice => struct { | ||
| 172 | pub fn init(base: *Stream, buf: []u8) Self { | ||
| 173 | return .{ | ||
| 174 | .base = base, | ||
| 175 | .fifo = FifoType.init(buf), | ||
| 176 | .stream = Stream{ .readFn = readFn }, | ||
| 177 | }; | ||
| 178 | } | ||
| 179 | }, | ||
| 180 | .Dynamic => struct { | ||
| 181 | pub fn init(base: *Stream, allocator: *mem.Allocator) Self { | ||
| 182 | return .{ | ||
| 183 | .base = base, | ||
| 184 | .fifo = FifoType.init(allocator), | ||
| 185 | .stream = Stream{ .readFn = readFn }, | ||
| 186 | }; | ||
| 187 | } | ||
| 188 | }, | ||
| 189 | }; | ||
| 190 | |||
| 191 | pub fn putBackByte(self: *Self, byte: u8) !void { | ||
| 192 | try self.putBack(&[_]u8{byte}); | ||
| 193 | } | ||
| 194 | |||
| 195 | pub fn putBack(self: *Self, bytes: []const u8) !void { | ||
| 196 | try self.fifo.unget(bytes); | ||
| 197 | } | ||
| 198 | |||
| 199 | fn readFn(in_stream: *Stream, dest: []u8) Error!usize { | ||
| 200 | const self = @fieldParentPtr(Self, "stream", in_stream); | ||
| 201 | |||
| 202 | // copy over anything putBack()'d | ||
| 203 | var dest_index = self.fifo.read(dest); | ||
| 204 | if (dest_index == dest.len) return dest_index; | ||
| 205 | |||
| 206 | // ask the backing stream for more | ||
| 207 | dest_index += try self.base.read(dest[dest_index..]); | ||
| 208 | return dest_index; | ||
| 209 | } | ||
| 210 | }; | ||
| 211 | } | ||
| 212 | |||
| 213 | pub const SliceInStream = struct { | ||
| 214 | const Self = @This(); | ||
| 215 | pub const Error = error{}; | ||
| 216 | pub const Stream = InStream(Error); | ||
| 217 | |||
| 218 | stream: Stream, | ||
| 219 | |||
| 220 | pos: usize, | ||
| 221 | slice: []const u8, | ||
| 222 | |||
| 223 | pub fn init(slice: []const u8) Self { | ||
| 224 | return Self{ | ||
| 225 | .slice = slice, | ||
| 226 | .pos = 0, | ||
| 227 | .stream = Stream{ .readFn = readFn }, | ||
| 228 | }; | ||
| 229 | } | ||
| 230 | |||
| 231 | fn readFn(in_stream: *Stream, dest: []u8) Error!usize { | ||
| 232 | const self = @fieldParentPtr(Self, "stream", in_stream); | ||
| 233 | const size = math.min(dest.len, self.slice.len - self.pos); | ||
| 234 | const end = self.pos + size; | ||
| 235 | |||
| 236 | mem.copy(u8, dest[0..size], self.slice[self.pos..end]); | ||
| 237 | self.pos = end; | ||
| 238 | |||
| 239 | return size; | ||
| 240 | } | ||
| 241 | }; | ||
| 242 | |||
| 243 | /// Creates a stream which allows for reading bit fields from another stream | ||
| 244 | pub fn BitInStream(endian: builtin.Endian, comptime Error: type) type { | ||
| 245 | return struct { | ||
| 246 | const Self = @This(); | ||
| 247 | |||
| 248 | in_stream: *Stream, | ||
| 249 | bit_buffer: u7, | ||
| 250 | bit_count: u3, | ||
| 251 | stream: Stream, | ||
| 252 | |||
| 253 | pub const Stream = InStream(Error); | ||
| 254 | const u8_bit_count = comptime meta.bitCount(u8); | ||
| 255 | const u7_bit_count = comptime meta.bitCount(u7); | ||
| 256 | const u4_bit_count = comptime meta.bitCount(u4); | ||
| 257 | |||
| 258 | pub fn init(in_stream: *Stream) Self { | ||
| 259 | return Self{ | ||
| 260 | .in_stream = in_stream, | ||
| 261 | .bit_buffer = 0, | ||
| 262 | .bit_count = 0, | ||
| 263 | .stream = Stream{ .readFn = read }, | ||
| 264 | }; | ||
| 265 | } | ||
| 266 | |||
| 267 | /// Reads `bits` bits from the stream and returns a specified unsigned int type | ||
| 268 | /// containing them in the least significant end, returning an error if the | ||
| 269 | /// specified number of bits could not be read. | ||
| 270 | pub fn readBitsNoEof(self: *Self, comptime U: type, bits: usize) !U { | ||
| 271 | var n: usize = undefined; | ||
| 272 | const result = try self.readBits(U, bits, &n); | ||
| 273 | if (n < bits) return error.EndOfStream; | ||
| 274 | return result; | ||
| 275 | } | ||
| 276 | |||
| 277 | /// Reads `bits` bits from the stream and returns a specified unsigned int type | ||
| 278 | /// containing them in the least significant end. The number of bits successfully | ||
| 279 | /// read is placed in `out_bits`, as reaching the end of the stream is not an error. | ||
| 280 | pub fn readBits(self: *Self, comptime U: type, bits: usize, out_bits: *usize) Error!U { | ||
| 281 | comptime assert(trait.isUnsignedInt(U)); | ||
| 282 | |||
| 283 | //by extending the buffer to a minimum of u8 we can cover a number of edge cases | ||
| 284 | // related to shifting and casting. | ||
| 285 | const u_bit_count = comptime meta.bitCount(U); | ||
| 286 | const buf_bit_count = bc: { | ||
| 287 | assert(u_bit_count >= bits); | ||
| 288 | break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count; | ||
| 289 | }; | ||
| 290 | const Buf = std.meta.IntType(false, buf_bit_count); | ||
| 291 | const BufShift = math.Log2Int(Buf); | ||
| 292 | |||
| 293 | out_bits.* = @as(usize, 0); | ||
| 294 | if (U == u0 or bits == 0) return 0; | ||
| 295 | var out_buffer = @as(Buf, 0); | ||
| 296 | |||
| 297 | if (self.bit_count > 0) { | ||
| 298 | const n = if (self.bit_count >= bits) @intCast(u3, bits) else self.bit_count; | ||
| 299 | const shift = u7_bit_count - n; | ||
| 300 | switch (endian) { | ||
| 301 | .Big => { | ||
| 302 | out_buffer = @as(Buf, self.bit_buffer >> shift); | ||
| 303 | self.bit_buffer <<= n; | ||
| 304 | }, | ||
| 305 | .Little => { | ||
| 306 | const value = (self.bit_buffer << shift) >> shift; | ||
| 307 | out_buffer = @as(Buf, value); | ||
| 308 | self.bit_buffer >>= n; | ||
| 309 | }, | ||
| 310 | } | ||
| 311 | self.bit_count -= n; | ||
| 312 | out_bits.* = n; | ||
| 313 | } | ||
| 314 | //at this point we know bit_buffer is empty | ||
| 315 | |||
| 316 | //copy bytes until we have enough bits, then leave the rest in bit_buffer | ||
| 317 | while (out_bits.* < bits) { | ||
| 318 | const n = bits - out_bits.*; | ||
| 319 | const next_byte = self.in_stream.readByte() catch |err| { | ||
| 320 | if (err == error.EndOfStream) { | ||
| 321 | return @intCast(U, out_buffer); | ||
| 322 | } | ||
| 323 | //@BUG: See #1810. Not sure if the bug is that I have to do this for some | ||
| 324 | // streams, or that I don't for streams with emtpy errorsets. | ||
| 325 | return @errSetCast(Error, err); | ||
| 326 | }; | ||
| 327 | |||
| 328 | switch (endian) { | ||
| 329 | .Big => { | ||
| 330 | if (n >= u8_bit_count) { | ||
| 331 | out_buffer <<= @intCast(u3, u8_bit_count - 1); | ||
| 332 | out_buffer <<= 1; | ||
| 333 | out_buffer |= @as(Buf, next_byte); | ||
| 334 | out_bits.* += u8_bit_count; | ||
| 335 | continue; | ||
| 336 | } | ||
| 337 | |||
| 338 | const shift = @intCast(u3, u8_bit_count - n); | ||
| 339 | out_buffer <<= @intCast(BufShift, n); | ||
| 340 | out_buffer |= @as(Buf, next_byte >> shift); | ||
| 341 | out_bits.* += n; | ||
| 342 | self.bit_buffer = @truncate(u7, next_byte << @intCast(u3, n - 1)); | ||
| 343 | self.bit_count = shift; | ||
| 344 | }, | ||
| 345 | .Little => { | ||
| 346 | if (n >= u8_bit_count) { | ||
| 347 | out_buffer |= @as(Buf, next_byte) << @intCast(BufShift, out_bits.*); | ||
| 348 | out_bits.* += u8_bit_count; | ||
| 349 | continue; | ||
| 350 | } | ||
| 351 | |||
| 352 | const shift = @intCast(u3, u8_bit_count - n); | ||
| 353 | const value = (next_byte << shift) >> shift; | ||
| 354 | out_buffer |= @as(Buf, value) << @intCast(BufShift, out_bits.*); | ||
| 355 | out_bits.* += n; | ||
| 356 | self.bit_buffer = @truncate(u7, next_byte >> @intCast(u3, n)); | ||
| 357 | self.bit_count = shift; | ||
| 358 | }, | ||
| 359 | } | ||
| 360 | } | ||
| 361 | |||
| 362 | return @intCast(U, out_buffer); | ||
| 363 | } | ||
| 364 | |||
| 365 | pub fn alignToByte(self: *Self) void { | ||
| 366 | self.bit_buffer = 0; | ||
| 367 | self.bit_count = 0; | ||
| 368 | } | ||
| 369 | |||
| 370 | pub fn read(self_stream: *Stream, buffer: []u8) Error!usize { | ||
| 371 | var self = @fieldParentPtr(Self, "stream", self_stream); | ||
| 372 | |||
| 373 | var out_bits: usize = undefined; | ||
| 374 | var out_bits_total = @as(usize, 0); | ||
| 375 | //@NOTE: I'm not sure this is a good idea, maybe alignToByte should be forced | ||
| 376 | if (self.bit_count > 0) { | ||
| 377 | for (buffer) |*b, i| { | ||
| 378 | b.* = try self.readBits(u8, u8_bit_count, &out_bits); | ||
| 379 | out_bits_total += out_bits; | ||
| 380 | } | ||
| 381 | const incomplete_byte = @boolToInt(out_bits_total % u8_bit_count > 0); | ||
| 382 | return (out_bits_total / u8_bit_count) + incomplete_byte; | ||
| 383 | } | ||
| 384 | |||
| 385 | return self.in_stream.read(buffer); | ||
| 386 | } | ||
| 387 | }; | ||
| 388 | } | ||
| 389 | |||
| 390 | /// An OutStream that doesn't write to anything. | 139 | /// An OutStream that doesn't write to anything. |
| 391 | pub const null_out_stream = @as(NullOutStream, .{ .context = {} }); | 140 | pub const null_out_stream = @as(NullOutStream, .{ .context = {} }); |
| 392 | 141 | ||
| ... | @@ -396,472 +145,9 @@ fn dummyWrite(context: void, data: []const u8) error{}!usize { | ... | @@ -396,472 +145,9 @@ fn dummyWrite(context: void, data: []const u8) error{}!usize { |
| 396 | } | 145 | } |
| 397 | 146 | ||
| 398 | test "null_out_stream" { | 147 | test "null_out_stream" { |
| 399 | null_out_stream.writeAll("yay" ** 1000) catch |err| switch (err) {}; | 148 | null_out_stream.writeAll("yay" ** 10) catch |err| switch (err) {}; |
| 400 | } | ||
| 401 | |||
| 402 | /// Creates a stream which allows for writing bit fields to another stream | ||
| 403 | pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type { | ||
| 404 | return struct { | ||
| 405 | const Self = @This(); | ||
| 406 | |||
| 407 | out_stream: *Stream, | ||
| 408 | bit_buffer: u8, | ||
| 409 | bit_count: u4, | ||
| 410 | stream: Stream, | ||
| 411 | |||
| 412 | pub const Stream = OutStream(Error); | ||
| 413 | const u8_bit_count = comptime meta.bitCount(u8); | ||
| 414 | const u4_bit_count = comptime meta.bitCount(u4); | ||
| 415 | |||
| 416 | pub fn init(out_stream: *Stream) Self { | ||
| 417 | return Self{ | ||
| 418 | .out_stream = out_stream, | ||
| 419 | .bit_buffer = 0, | ||
| 420 | .bit_count = 0, | ||
| 421 | .stream = Stream{ .writeFn = write }, | ||
| 422 | }; | ||
| 423 | } | ||
| 424 | |||
| 425 | /// Write the specified number of bits to the stream from the least significant bits of | ||
| 426 | /// the specified unsigned int value. Bits will only be written to the stream when there | ||
| 427 | /// are enough to fill a byte. | ||
| 428 | pub fn writeBits(self: *Self, value: var, bits: usize) Error!void { | ||
| 429 | if (bits == 0) return; | ||
| 430 | |||
| 431 | const U = @TypeOf(value); | ||
| 432 | comptime assert(trait.isUnsignedInt(U)); | ||
| 433 | |||
| 434 | //by extending the buffer to a minimum of u8 we can cover a number of edge cases | ||
| 435 | // related to shifting and casting. | ||
| 436 | const u_bit_count = comptime meta.bitCount(U); | ||
| 437 | const buf_bit_count = bc: { | ||
| 438 | assert(u_bit_count >= bits); | ||
| 439 | break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count; | ||
| 440 | }; | ||
| 441 | const Buf = std.meta.IntType(false, buf_bit_count); | ||
| 442 | const BufShift = math.Log2Int(Buf); | ||
| 443 | |||
| 444 | const buf_value = @intCast(Buf, value); | ||
| 445 | |||
| 446 | const high_byte_shift = @intCast(BufShift, buf_bit_count - u8_bit_count); | ||
| 447 | var in_buffer = switch (endian) { | ||
| 448 | .Big => buf_value << @intCast(BufShift, buf_bit_count - bits), | ||
| 449 | .Little => buf_value, | ||
| 450 | }; | ||
| 451 | var in_bits = bits; | ||
| 452 | |||
| 453 | if (self.bit_count > 0) { | ||
| 454 | const bits_remaining = u8_bit_count - self.bit_count; | ||
| 455 | const n = @intCast(u3, if (bits_remaining > bits) bits else bits_remaining); | ||
| 456 | switch (endian) { | ||
| 457 | .Big => { | ||
| 458 | const shift = @intCast(BufShift, high_byte_shift + self.bit_count); | ||
| 459 | const v = @intCast(u8, in_buffer >> shift); | ||
| 460 | self.bit_buffer |= v; | ||
| 461 | in_buffer <<= n; | ||
| 462 | }, | ||
| 463 | .Little => { | ||
| 464 | const v = @truncate(u8, in_buffer) << @intCast(u3, self.bit_count); | ||
| 465 | self.bit_buffer |= v; | ||
| 466 | in_buffer >>= n; | ||
| 467 | }, | ||
| 468 | } | ||
| 469 | self.bit_count += n; | ||
| 470 | in_bits -= n; | ||
| 471 | |||
| 472 | //if we didn't fill the buffer, it's because bits < bits_remaining; | ||
| 473 | if (self.bit_count != u8_bit_count) return; | ||
| 474 | try self.out_stream.writeByte(self.bit_buffer); | ||
| 475 | self.bit_buffer = 0; | ||
| 476 | self.bit_count = 0; | ||
| 477 | } | ||
| 478 | //at this point we know bit_buffer is empty | ||
| 479 | |||
| 480 | //copy bytes until we can't fill one anymore, then leave the rest in bit_buffer | ||
| 481 | while (in_bits >= u8_bit_count) { | ||
| 482 | switch (endian) { | ||
| 483 | .Big => { | ||
| 484 | const v = @intCast(u8, in_buffer >> high_byte_shift); | ||
| 485 | try self.out_stream.writeByte(v); | ||
| 486 | in_buffer <<= @intCast(u3, u8_bit_count - 1); | ||
| 487 | in_buffer <<= 1; | ||
| 488 | }, | ||
| 489 | .Little => { | ||
| 490 | const v = @truncate(u8, in_buffer); | ||
| 491 | try self.out_stream.writeByte(v); | ||
| 492 | in_buffer >>= @intCast(u3, u8_bit_count - 1); | ||
| 493 | in_buffer >>= 1; | ||
| 494 | }, | ||
| 495 | } | ||
| 496 | in_bits -= u8_bit_count; | ||
| 497 | } | ||
| 498 | |||
| 499 | if (in_bits > 0) { | ||
| 500 | self.bit_count = @intCast(u4, in_bits); | ||
| 501 | self.bit_buffer = switch (endian) { | ||
| 502 | .Big => @truncate(u8, in_buffer >> high_byte_shift), | ||
| 503 | .Little => @truncate(u8, in_buffer), | ||
| 504 | }; | ||
| 505 | } | ||
| 506 | } | ||
| 507 | |||
| 508 | /// Flush any remaining bits to the stream. | ||
| 509 | pub fn flushBits(self: *Self) Error!void { | ||
| 510 | if (self.bit_count == 0) return; | ||
| 511 | try self.out_stream.writeByte(self.bit_buffer); | ||
| 512 | self.bit_buffer = 0; | ||
| 513 | self.bit_count = 0; | ||
| 514 | } | ||
| 515 | |||
| 516 | pub fn write(self_stream: *Stream, buffer: []const u8) Error!usize { | ||
| 517 | var self = @fieldParentPtr(Self, "stream", self_stream); | ||
| 518 | |||
| 519 | // TODO: I'm not sure this is a good idea, maybe flushBits should be forced | ||
| 520 | if (self.bit_count > 0) { | ||
| 521 | for (buffer) |b, i| | ||
| 522 | try self.writeBits(b, u8_bit_count); | ||
| 523 | return buffer.len; | ||
| 524 | } | ||
| 525 | |||
| 526 | return self.out_stream.write(buffer); | ||
| 527 | } | ||
| 528 | }; | ||
| 529 | } | ||
| 530 | |||
| 531 | pub const Packing = enum { | ||
| 532 | /// Pack data to byte alignment | ||
| 533 | Byte, | ||
| 534 | |||
| 535 | /// Pack data to bit alignment | ||
| 536 | Bit, | ||
| 537 | }; | ||
| 538 | |||
| 539 | /// Creates a deserializer that deserializes types from any stream. | ||
| 540 | /// If `is_packed` is true, the data stream is treated as bit-packed, | ||
| 541 | /// otherwise data is expected to be packed to the smallest byte. | ||
| 542 | /// Types may implement a custom deserialization routine with a | ||
| 543 | /// function named `deserialize` in the form of: | ||
| 544 | /// pub fn deserialize(self: *Self, deserializer: var) !void | ||
| 545 | /// which will be called when the deserializer is used to deserialize | ||
| 546 | /// that type. It will pass a pointer to the type instance to deserialize | ||
| 547 | /// into and a pointer to the deserializer struct. | ||
| 548 | pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime Error: type) type { | ||
| 549 | return struct { | ||
| 550 | const Self = @This(); | ||
| 551 | |||
| 552 | in_stream: if (packing == .Bit) BitInStream(endian, Stream.Error) else *Stream, | ||
| 553 | |||
| 554 | pub const Stream = InStream(Error); | ||
| 555 | |||
| 556 | pub fn init(in_stream: *Stream) Self { | ||
| 557 | return Self{ | ||
| 558 | .in_stream = switch (packing) { | ||
| 559 | .Bit => BitInStream(endian, Stream.Error).init(in_stream), | ||
| 560 | .Byte => in_stream, | ||
| 561 | }, | ||
| 562 | }; | ||
| 563 | } | ||
| 564 | |||
| 565 | pub fn alignToByte(self: *Self) void { | ||
| 566 | if (packing == .Byte) return; | ||
| 567 | self.in_stream.alignToByte(); | ||
| 568 | } | ||
| 569 | |||
| 570 | //@BUG: inferred error issue. See: #1386 | ||
| 571 | fn deserializeInt(self: *Self, comptime T: type) (Error || error{EndOfStream})!T { | ||
| 572 | comptime assert(trait.is(.Int)(T) or trait.is(.Float)(T)); | ||
| 573 | |||
| 574 | const u8_bit_count = 8; | ||
| 575 | const t_bit_count = comptime meta.bitCount(T); | ||
| 576 | |||
| 577 | const U = std.meta.IntType(false, t_bit_count); | ||
| 578 | const Log2U = math.Log2Int(U); | ||
| 579 | const int_size = (U.bit_count + 7) / 8; | ||
| 580 | |||
| 581 | if (packing == .Bit) { | ||
| 582 | const result = try self.in_stream.readBitsNoEof(U, t_bit_count); | ||
| 583 | return @bitCast(T, result); | ||
| 584 | } | ||
| 585 | |||
| 586 | var buffer: [int_size]u8 = undefined; | ||
| 587 | const read_size = try self.in_stream.read(buffer[0..]); | ||
| 588 | if (read_size < int_size) return error.EndOfStream; | ||
| 589 | |||
| 590 | if (int_size == 1) { | ||
| 591 | if (t_bit_count == 8) return @bitCast(T, buffer[0]); | ||
| 592 | const PossiblySignedByte = std.meta.IntType(T.is_signed, 8); | ||
| 593 | return @truncate(T, @bitCast(PossiblySignedByte, buffer[0])); | ||
| 594 | } | ||
| 595 | |||
| 596 | var result = @as(U, 0); | ||
| 597 | for (buffer) |byte, i| { | ||
| 598 | switch (endian) { | ||
| 599 | .Big => { | ||
| 600 | result = (result << u8_bit_count) | byte; | ||
| 601 | }, | ||
| 602 | .Little => { | ||
| 603 | result |= @as(U, byte) << @intCast(Log2U, u8_bit_count * i); | ||
| 604 | }, | ||
| 605 | } | ||
| 606 | } | ||
| 607 | |||
| 608 | return @bitCast(T, result); | ||
| 609 | } | ||
| 610 | |||
| 611 | /// Deserializes and returns data of the specified type from the stream | ||
| 612 | pub fn deserialize(self: *Self, comptime T: type) !T { | ||
| 613 | var value: T = undefined; | ||
| 614 | try self.deserializeInto(&value); | ||
| 615 | return value; | ||
| 616 | } | ||
| 617 | |||
| 618 | /// Deserializes data into the type pointed to by `ptr` | ||
| 619 | pub fn deserializeInto(self: *Self, ptr: var) !void { | ||
| 620 | const T = @TypeOf(ptr); | ||
| 621 | comptime assert(trait.is(.Pointer)(T)); | ||
| 622 | |||
| 623 | if (comptime trait.isSlice(T) or comptime trait.isPtrTo(.Array)(T)) { | ||
| 624 | for (ptr) |*v| | ||
| 625 | try self.deserializeInto(v); | ||
| 626 | return; | ||
| 627 | } | ||
| 628 | |||
| 629 | comptime assert(trait.isSingleItemPtr(T)); | ||
| 630 | |||
| 631 | const C = comptime meta.Child(T); | ||
| 632 | const child_type_id = @typeInfo(C); | ||
| 633 | |||
| 634 | //custom deserializer: fn(self: *Self, deserializer: var) !void | ||
| 635 | if (comptime trait.hasFn("deserialize")(C)) return C.deserialize(ptr, self); | ||
| 636 | |||
| 637 | if (comptime trait.isPacked(C) and packing != .Bit) { | ||
| 638 | var packed_deserializer = Deserializer(endian, .Bit, Error).init(self.in_stream); | ||
| 639 | return packed_deserializer.deserializeInto(ptr); | ||
| 640 | } | ||
| 641 | |||
| 642 | switch (child_type_id) { | ||
| 643 | .Void => return, | ||
| 644 | .Bool => ptr.* = (try self.deserializeInt(u1)) > 0, | ||
| 645 | .Float, .Int => ptr.* = try self.deserializeInt(C), | ||
| 646 | .Struct => { | ||
| 647 | const info = @typeInfo(C).Struct; | ||
| 648 | |||
| 649 | inline for (info.fields) |*field_info| { | ||
| 650 | const name = field_info.name; | ||
| 651 | const FieldType = field_info.field_type; | ||
| 652 | |||
| 653 | if (FieldType == void or FieldType == u0) continue; | ||
| 654 | |||
| 655 | //it doesn't make any sense to read pointers | ||
| 656 | if (comptime trait.is(.Pointer)(FieldType)) { | ||
| 657 | @compileError("Will not " ++ "read field " ++ name ++ " of struct " ++ | ||
| 658 | @typeName(C) ++ " because it " ++ "is of pointer-type " ++ | ||
| 659 | @typeName(FieldType) ++ "."); | ||
| 660 | } | ||
| 661 | |||
| 662 | try self.deserializeInto(&@field(ptr, name)); | ||
| 663 | } | ||
| 664 | }, | ||
| 665 | .Union => { | ||
| 666 | const info = @typeInfo(C).Union; | ||
| 667 | if (info.tag_type) |TagType| { | ||
| 668 | //we avoid duplicate iteration over the enum tags | ||
| 669 | // by getting the int directly and casting it without | ||
| 670 | // safety. If it is bad, it will be caught anyway. | ||
| 671 | const TagInt = @TagType(TagType); | ||
| 672 | const tag = try self.deserializeInt(TagInt); | ||
| 673 | |||
| 674 | inline for (info.fields) |field_info| { | ||
| 675 | if (field_info.enum_field.?.value == tag) { | ||
| 676 | const name = field_info.name; | ||
| 677 | const FieldType = field_info.field_type; | ||
| 678 | ptr.* = @unionInit(C, name, undefined); | ||
| 679 | try self.deserializeInto(&@field(ptr, name)); | ||
| 680 | return; | ||
| 681 | } | ||
| 682 | } | ||
| 683 | //This is reachable if the enum data is bad | ||
| 684 | return error.InvalidEnumTag; | ||
| 685 | } | ||
| 686 | @compileError("Cannot meaningfully deserialize " ++ @typeName(C) ++ | ||
| 687 | " because it is an untagged union. Use a custom deserialize()."); | ||
| 688 | }, | ||
| 689 | .Optional => { | ||
| 690 | const OC = comptime meta.Child(C); | ||
| 691 | const exists = (try self.deserializeInt(u1)) > 0; | ||
| 692 | if (!exists) { | ||
| 693 | ptr.* = null; | ||
| 694 | return; | ||
| 695 | } | ||
| 696 | |||
| 697 | ptr.* = @as(OC, undefined); //make it non-null so the following .? is guaranteed safe | ||
| 698 | const val_ptr = &ptr.*.?; | ||
| 699 | try self.deserializeInto(val_ptr); | ||
| 700 | }, | ||
| 701 | .Enum => { | ||
| 702 | var value = try self.deserializeInt(@TagType(C)); | ||
| 703 | ptr.* = try meta.intToEnum(C, value); | ||
| 704 | }, | ||
| 705 | else => { | ||
| 706 | @compileError("Cannot deserialize " ++ @tagName(child_type_id) ++ " types (unimplemented)."); | ||
| 707 | }, | ||
| 708 | } | ||
| 709 | } | ||
| 710 | }; | ||
| 711 | } | ||
| 712 | |||
| 713 | /// Creates a serializer that serializes types to any stream. | ||
| 714 | /// If `is_packed` is true, the data will be bit-packed into the stream. | ||
| 715 | /// Note that the you must call `serializer.flush()` when you are done | ||
| 716 | /// writing bit-packed data in order ensure any unwritten bits are committed. | ||
| 717 | /// If `is_packed` is false, data is packed to the smallest byte. In the case | ||
| 718 | /// of packed structs, the struct will written bit-packed and with the specified | ||
| 719 | /// endianess, after which data will resume being written at the next byte boundary. | ||
| 720 | /// Types may implement a custom serialization routine with a | ||
| 721 | /// function named `serialize` in the form of: | ||
| 722 | /// pub fn serialize(self: Self, serializer: var) !void | ||
| 723 | /// which will be called when the serializer is used to serialize that type. It will | ||
| 724 | /// pass a const pointer to the type instance to be serialized and a pointer | ||
| 725 | /// to the serializer struct. | ||
| 726 | pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime Error: type) type { | ||
| 727 | return struct { | ||
| 728 | const Self = @This(); | ||
| 729 | |||
| 730 | out_stream: if (packing == .Bit) BitOutStream(endian, Stream.Error) else *Stream, | ||
| 731 | |||
| 732 | pub const Stream = OutStream(Error); | ||
| 733 | |||
| 734 | pub fn init(out_stream: *Stream) Self { | ||
| 735 | return Self{ | ||
| 736 | .out_stream = switch (packing) { | ||
| 737 | .Bit => BitOutStream(endian, Stream.Error).init(out_stream), | ||
| 738 | .Byte => out_stream, | ||
| 739 | }, | ||
| 740 | }; | ||
| 741 | } | ||
| 742 | |||
| 743 | /// Flushes any unwritten bits to the stream | ||
| 744 | pub fn flush(self: *Self) Error!void { | ||
| 745 | if (packing == .Bit) return self.out_stream.flushBits(); | ||
| 746 | } | ||
| 747 | |||
| 748 | fn serializeInt(self: *Self, value: var) Error!void { | ||
| 749 | const T = @TypeOf(value); | ||
| 750 | comptime assert(trait.is(.Int)(T) or trait.is(.Float)(T)); | ||
| 751 | |||
| 752 | const t_bit_count = comptime meta.bitCount(T); | ||
| 753 | const u8_bit_count = comptime meta.bitCount(u8); | ||
| 754 | |||
| 755 | const U = std.meta.IntType(false, t_bit_count); | ||
| 756 | const Log2U = math.Log2Int(U); | ||
| 757 | const int_size = (U.bit_count + 7) / 8; | ||
| 758 | |||
| 759 | const u_value = @bitCast(U, value); | ||
| 760 | |||
| 761 | if (packing == .Bit) return self.out_stream.writeBits(u_value, t_bit_count); | ||
| 762 | |||
| 763 | var buffer: [int_size]u8 = undefined; | ||
| 764 | if (int_size == 1) buffer[0] = u_value; | ||
| 765 | |||
| 766 | for (buffer) |*byte, i| { | ||
| 767 | const idx = switch (endian) { | ||
| 768 | .Big => int_size - i - 1, | ||
| 769 | .Little => i, | ||
| 770 | }; | ||
| 771 | const shift = @intCast(Log2U, idx * u8_bit_count); | ||
| 772 | const v = u_value >> shift; | ||
| 773 | byte.* = if (t_bit_count < u8_bit_count) v else @truncate(u8, v); | ||
| 774 | } | ||
| 775 | |||
| 776 | try self.out_stream.write(&buffer); | ||
| 777 | } | ||
| 778 | |||
| 779 | /// Serializes the passed value into the stream | ||
| 780 | pub fn serialize(self: *Self, value: var) Error!void { | ||
| 781 | const T = comptime @TypeOf(value); | ||
| 782 | |||
| 783 | if (comptime trait.isIndexable(T)) { | ||
| 784 | for (value) |v| | ||
| 785 | try self.serialize(v); | ||
| 786 | return; | ||
| 787 | } | ||
| 788 | |||
| 789 | //custom serializer: fn(self: Self, serializer: var) !void | ||
| 790 | if (comptime trait.hasFn("serialize")(T)) return T.serialize(value, self); | ||
| 791 | |||
| 792 | if (comptime trait.isPacked(T) and packing != .Bit) { | ||
| 793 | var packed_serializer = Serializer(endian, .Bit, Error).init(self.out_stream); | ||
| 794 | try packed_serializer.serialize(value); | ||
| 795 | try packed_serializer.flush(); | ||
| 796 | return; | ||
| 797 | } | ||
| 798 | |||
| 799 | switch (@typeInfo(T)) { | ||
| 800 | .Void => return, | ||
| 801 | .Bool => try self.serializeInt(@as(u1, @boolToInt(value))), | ||
| 802 | .Float, .Int => try self.serializeInt(value), | ||
| 803 | .Struct => { | ||
| 804 | const info = @typeInfo(T); | ||
| 805 | |||
| 806 | inline for (info.Struct.fields) |*field_info| { | ||
| 807 | const name = field_info.name; | ||
| 808 | const FieldType = field_info.field_type; | ||
| 809 | |||
| 810 | if (FieldType == void or FieldType == u0) continue; | ||
| 811 | |||
| 812 | //It doesn't make sense to write pointers | ||
| 813 | if (comptime trait.is(.Pointer)(FieldType)) { | ||
| 814 | @compileError("Will not " ++ "serialize field " ++ name ++ | ||
| 815 | " of struct " ++ @typeName(T) ++ " because it " ++ | ||
| 816 | "is of pointer-type " ++ @typeName(FieldType) ++ "."); | ||
| 817 | } | ||
| 818 | try self.serialize(@field(value, name)); | ||
| 819 | } | ||
| 820 | }, | ||
| 821 | .Union => { | ||
| 822 | const info = @typeInfo(T).Union; | ||
| 823 | if (info.tag_type) |TagType| { | ||
| 824 | const active_tag = meta.activeTag(value); | ||
| 825 | try self.serialize(active_tag); | ||
| 826 | //This inline loop is necessary because active_tag is a runtime | ||
| 827 | // value, but @field requires a comptime value. Our alternative | ||
| 828 | // is to check each field for a match | ||
| 829 | inline for (info.fields) |field_info| { | ||
| 830 | if (field_info.enum_field.?.value == @enumToInt(active_tag)) { | ||
| 831 | const name = field_info.name; | ||
| 832 | const FieldType = field_info.field_type; | ||
| 833 | try self.serialize(@field(value, name)); | ||
| 834 | return; | ||
| 835 | } | ||
| 836 | } | ||
| 837 | unreachable; | ||
| 838 | } | ||
| 839 | @compileError("Cannot meaningfully serialize " ++ @typeName(T) ++ | ||
| 840 | " because it is an untagged union. Use a custom serialize()."); | ||
| 841 | }, | ||
| 842 | .Optional => { | ||
| 843 | if (value == null) { | ||
| 844 | try self.serializeInt(@as(u1, @boolToInt(false))); | ||
| 845 | return; | ||
| 846 | } | ||
| 847 | try self.serializeInt(@as(u1, @boolToInt(true))); | ||
| 848 | |||
| 849 | const OC = comptime meta.Child(T); | ||
| 850 | const val_ptr = &value.?; | ||
| 851 | try self.serialize(val_ptr.*); | ||
| 852 | }, | ||
| 853 | .Enum => { | ||
| 854 | try self.serializeInt(@enumToInt(value)); | ||
| 855 | }, | ||
| 856 | else => @compileError("Cannot serialize " ++ @tagName(@typeInfo(T)) ++ " types (unimplemented)."), | ||
| 857 | } | ||
| 858 | } | ||
| 859 | }; | ||
| 860 | } | 149 | } |
| 861 | 150 | ||
| 862 | test "" { | 151 | test "" { |
| 863 | comptime { | 152 | _ = @import("io/test.zig"); |
| 864 | _ = @import("io/test.zig"); | ||
| 865 | } | ||
| 866 | std.meta.refAllDecls(@This()); | ||
| 867 | } | 153 | } |
lib/std/io/bit_in_stream.zig created+237| ... | @@ -0,0 +1,237 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const builtin = std.builtin; | ||
| 3 | const io = std.io; | ||
| 4 | const assert = std.debug.assert; | ||
| 5 | const testing = std.testing; | ||
| 6 | const trait = std.meta.trait; | ||
| 7 | const meta = std.meta; | ||
| 8 | const math = std.math; | ||
| 9 | |||
| 10 | /// Creates a stream which allows for reading bit fields from another stream | ||
| 11 | pub fn BitInStream(endian: builtin.Endian, comptime InStreamType: type) type { | ||
| 12 | return struct { | ||
| 13 | in_stream: InStreamType, | ||
| 14 | bit_buffer: u7, | ||
| 15 | bit_count: u3, | ||
| 16 | |||
| 17 | pub const Error = InStreamType.Error; | ||
| 18 | pub const InStream = io.InStream(*Self, Error, read); | ||
| 19 | |||
| 20 | const Self = @This(); | ||
| 21 | const u8_bit_count = comptime meta.bitCount(u8); | ||
| 22 | const u7_bit_count = comptime meta.bitCount(u7); | ||
| 23 | const u4_bit_count = comptime meta.bitCount(u4); | ||
| 24 | |||
| 25 | pub fn init(in_stream: InStreamType) Self { | ||
| 26 | return Self{ | ||
| 27 | .in_stream = in_stream, | ||
| 28 | .bit_buffer = 0, | ||
| 29 | .bit_count = 0, | ||
| 30 | }; | ||
| 31 | } | ||
| 32 | |||
| 33 | /// Reads `bits` bits from the stream and returns a specified unsigned int type | ||
| 34 | /// containing them in the least significant end, returning an error if the | ||
| 35 | /// specified number of bits could not be read. | ||
| 36 | pub fn readBitsNoEof(self: *Self, comptime U: type, bits: usize) !U { | ||
| 37 | var n: usize = undefined; | ||
| 38 | const result = try self.readBits(U, bits, &n); | ||
| 39 | if (n < bits) return error.EndOfStream; | ||
| 40 | return result; | ||
| 41 | } | ||
| 42 | |||
| 43 | /// Reads `bits` bits from the stream and returns a specified unsigned int type | ||
| 44 | /// containing them in the least significant end. The number of bits successfully | ||
| 45 | /// read is placed in `out_bits`, as reaching the end of the stream is not an error. | ||
| 46 | pub fn readBits(self: *Self, comptime U: type, bits: usize, out_bits: *usize) Error!U { | ||
| 47 | comptime assert(trait.isUnsignedInt(U)); | ||
| 48 | |||
| 49 | //by extending the buffer to a minimum of u8 we can cover a number of edge cases | ||
| 50 | // related to shifting and casting. | ||
| 51 | const u_bit_count = comptime meta.bitCount(U); | ||
| 52 | const buf_bit_count = bc: { | ||
| 53 | assert(u_bit_count >= bits); | ||
| 54 | break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count; | ||
| 55 | }; | ||
| 56 | const Buf = std.meta.IntType(false, buf_bit_count); | ||
| 57 | const BufShift = math.Log2Int(Buf); | ||
| 58 | |||
| 59 | out_bits.* = @as(usize, 0); | ||
| 60 | if (U == u0 or bits == 0) return 0; | ||
| 61 | var out_buffer = @as(Buf, 0); | ||
| 62 | |||
| 63 | if (self.bit_count > 0) { | ||
| 64 | const n = if (self.bit_count >= bits) @intCast(u3, bits) else self.bit_count; | ||
| 65 | const shift = u7_bit_count - n; | ||
| 66 | switch (endian) { | ||
| 67 | .Big => { | ||
| 68 | out_buffer = @as(Buf, self.bit_buffer >> shift); | ||
| 69 | self.bit_buffer <<= n; | ||
| 70 | }, | ||
| 71 | .Little => { | ||
| 72 | const value = (self.bit_buffer << shift) >> shift; | ||
| 73 | out_buffer = @as(Buf, value); | ||
| 74 | self.bit_buffer >>= n; | ||
| 75 | }, | ||
| 76 | } | ||
| 77 | self.bit_count -= n; | ||
| 78 | out_bits.* = n; | ||
| 79 | } | ||
| 80 | //at this point we know bit_buffer is empty | ||
| 81 | |||
| 82 | //copy bytes until we have enough bits, then leave the rest in bit_buffer | ||
| 83 | while (out_bits.* < bits) { | ||
| 84 | const n = bits - out_bits.*; | ||
| 85 | const next_byte = self.in_stream.readByte() catch |err| { | ||
| 86 | if (err == error.EndOfStream) { | ||
| 87 | return @intCast(U, out_buffer); | ||
| 88 | } | ||
| 89 | //@BUG: See #1810. Not sure if the bug is that I have to do this for some | ||
| 90 | // streams, or that I don't for streams with emtpy errorsets. | ||
| 91 | return @errSetCast(Error, err); | ||
| 92 | }; | ||
| 93 | |||
| 94 | switch (endian) { | ||
| 95 | .Big => { | ||
| 96 | if (n >= u8_bit_count) { | ||
| 97 | out_buffer <<= @intCast(u3, u8_bit_count - 1); | ||
| 98 | out_buffer <<= 1; | ||
| 99 | out_buffer |= @as(Buf, next_byte); | ||
| 100 | out_bits.* += u8_bit_count; | ||
| 101 | continue; | ||
| 102 | } | ||
| 103 | |||
| 104 | const shift = @intCast(u3, u8_bit_count - n); | ||
| 105 | out_buffer <<= @intCast(BufShift, n); | ||
| 106 | out_buffer |= @as(Buf, next_byte >> shift); | ||
| 107 | out_bits.* += n; | ||
| 108 | self.bit_buffer = @truncate(u7, next_byte << @intCast(u3, n - 1)); | ||
| 109 | self.bit_count = shift; | ||
| 110 | }, | ||
| 111 | .Little => { | ||
| 112 | if (n >= u8_bit_count) { | ||
| 113 | out_buffer |= @as(Buf, next_byte) << @intCast(BufShift, out_bits.*); | ||
| 114 | out_bits.* += u8_bit_count; | ||
| 115 | continue; | ||
| 116 | } | ||
| 117 | |||
| 118 | const shift = @intCast(u3, u8_bit_count - n); | ||
| 119 | const value = (next_byte << shift) >> shift; | ||
| 120 | out_buffer |= @as(Buf, value) << @intCast(BufShift, out_bits.*); | ||
| 121 | out_bits.* += n; | ||
| 122 | self.bit_buffer = @truncate(u7, next_byte >> @intCast(u3, n)); | ||
| 123 | self.bit_count = shift; | ||
| 124 | }, | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | return @intCast(U, out_buffer); | ||
| 129 | } | ||
| 130 | |||
| 131 | pub fn alignToByte(self: *Self) void { | ||
| 132 | self.bit_buffer = 0; | ||
| 133 | self.bit_count = 0; | ||
| 134 | } | ||
| 135 | |||
| 136 | pub fn read(self: *Self, buffer: []u8) Error!usize { | ||
| 137 | var out_bits: usize = undefined; | ||
| 138 | var out_bits_total = @as(usize, 0); | ||
| 139 | //@NOTE: I'm not sure this is a good idea, maybe alignToByte should be forced | ||
| 140 | if (self.bit_count > 0) { | ||
| 141 | for (buffer) |*b, i| { | ||
| 142 | b.* = try self.readBits(u8, u8_bit_count, &out_bits); | ||
| 143 | out_bits_total += out_bits; | ||
| 144 | } | ||
| 145 | const incomplete_byte = @boolToInt(out_bits_total % u8_bit_count > 0); | ||
| 146 | return (out_bits_total / u8_bit_count) + incomplete_byte; | ||
| 147 | } | ||
| 148 | |||
| 149 | return self.in_stream.read(buffer); | ||
| 150 | } | ||
| 151 | |||
| 152 | pub fn inStream(self: *Self) InStream { | ||
| 153 | return .{ .context = self }; | ||
| 154 | } | ||
| 155 | }; | ||
| 156 | } | ||
| 157 | |||
| 158 | pub fn bitInStream( | ||
| 159 | comptime endian: builtin.Endian, | ||
| 160 | underlying_stream: var, | ||
| 161 | ) BitInStream(endian, @TypeOf(underlying_stream)) { | ||
| 162 | return BitInStream(endian, @TypeOf(underlying_stream)).init(underlying_stream); | ||
| 163 | } | ||
| 164 | |||
| 165 | test "api coverage" { | ||
| 166 | const mem_be = [_]u8{ 0b11001101, 0b00001011 }; | ||
| 167 | const mem_le = [_]u8{ 0b00011101, 0b10010101 }; | ||
| 168 | |||
| 169 | var mem_in_be = io.fixedBufferStream(&mem_be); | ||
| 170 | var bit_stream_be = bitInStream(.Big, mem_in_be.inStream()); | ||
| 171 | |||
| 172 | var out_bits: usize = undefined; | ||
| 173 | |||
| 174 | const expect = testing.expect; | ||
| 175 | const expectError = testing.expectError; | ||
| 176 | |||
| 177 | expect(1 == try bit_stream_be.readBits(u2, 1, &out_bits)); | ||
| 178 | expect(out_bits == 1); | ||
| 179 | expect(2 == try bit_stream_be.readBits(u5, 2, &out_bits)); | ||
| 180 | expect(out_bits == 2); | ||
| 181 | expect(3 == try bit_stream_be.readBits(u128, 3, &out_bits)); | ||
| 182 | expect(out_bits == 3); | ||
| 183 | expect(4 == try bit_stream_be.readBits(u8, 4, &out_bits)); | ||
| 184 | expect(out_bits == 4); | ||
| 185 | expect(5 == try bit_stream_be.readBits(u9, 5, &out_bits)); | ||
| 186 | expect(out_bits == 5); | ||
| 187 | expect(1 == try bit_stream_be.readBits(u1, 1, &out_bits)); | ||
| 188 | expect(out_bits == 1); | ||
| 189 | |||
| 190 | mem_in_be.pos = 0; | ||
| 191 | bit_stream_be.bit_count = 0; | ||
| 192 | expect(0b110011010000101 == try bit_stream_be.readBits(u15, 15, &out_bits)); | ||
| 193 | expect(out_bits == 15); | ||
| 194 | |||
| 195 | mem_in_be.pos = 0; | ||
| 196 | bit_stream_be.bit_count = 0; | ||
| 197 | expect(0b1100110100001011 == try bit_stream_be.readBits(u16, 16, &out_bits)); | ||
| 198 | expect(out_bits == 16); | ||
| 199 | |||
| 200 | _ = try bit_stream_be.readBits(u0, 0, &out_bits); | ||
| 201 | |||
| 202 | expect(0 == try bit_stream_be.readBits(u1, 1, &out_bits)); | ||
| 203 | expect(out_bits == 0); | ||
| 204 | expectError(error.EndOfStream, bit_stream_be.readBitsNoEof(u1, 1)); | ||
| 205 | |||
| 206 | var mem_in_le = io.fixedBufferStream(&mem_le); | ||
| 207 | var bit_stream_le = bitInStream(.Little, mem_in_le.inStream()); | ||
| 208 | |||
| 209 | expect(1 == try bit_stream_le.readBits(u2, 1, &out_bits)); | ||
| 210 | expect(out_bits == 1); | ||
| 211 | expect(2 == try bit_stream_le.readBits(u5, 2, &out_bits)); | ||
| 212 | expect(out_bits == 2); | ||
| 213 | expect(3 == try bit_stream_le.readBits(u128, 3, &out_bits)); | ||
| 214 | expect(out_bits == 3); | ||
| 215 | expect(4 == try bit_stream_le.readBits(u8, 4, &out_bits)); | ||
| 216 | expect(out_bits == 4); | ||
| 217 | expect(5 == try bit_stream_le.readBits(u9, 5, &out_bits)); | ||
| 218 | expect(out_bits == 5); | ||
| 219 | expect(1 == try bit_stream_le.readBits(u1, 1, &out_bits)); | ||
| 220 | expect(out_bits == 1); | ||
| 221 | |||
| 222 | mem_in_le.pos = 0; | ||
| 223 | bit_stream_le.bit_count = 0; | ||
| 224 | expect(0b001010100011101 == try bit_stream_le.readBits(u15, 15, &out_bits)); | ||
| 225 | expect(out_bits == 15); | ||
| 226 | |||
| 227 | mem_in_le.pos = 0; | ||
| 228 | bit_stream_le.bit_count = 0; | ||
| 229 | expect(0b1001010100011101 == try bit_stream_le.readBits(u16, 16, &out_bits)); | ||
| 230 | expect(out_bits == 16); | ||
| 231 | |||
| 232 | _ = try bit_stream_le.readBits(u0, 0, &out_bits); | ||
| 233 | |||
| 234 | expect(0 == try bit_stream_le.readBits(u1, 1, &out_bits)); | ||
| 235 | expect(out_bits == 0); | ||
| 236 | expectError(error.EndOfStream, bit_stream_le.readBitsNoEof(u1, 1)); | ||
| 237 | } | ||
lib/std/io/bit_out_stream.zig created+197| ... | @@ -0,0 +1,197 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const builtin = std.builtin; | ||
| 3 | const io = std.io; | ||
| 4 | const testing = std.testing; | ||
| 5 | const assert = std.debug.assert; | ||
| 6 | const trait = std.meta.trait; | ||
| 7 | const meta = std.meta; | ||
| 8 | const math = std.math; | ||
| 9 | |||
| 10 | /// Creates a stream which allows for writing bit fields to another stream | ||
| 11 | pub fn BitOutStream(endian: builtin.Endian, comptime OutStreamType: type) type { | ||
| 12 | return struct { | ||
| 13 | out_stream: OutStreamType, | ||
| 14 | bit_buffer: u8, | ||
| 15 | bit_count: u4, | ||
| 16 | |||
| 17 | pub const Error = OutStreamType.Error; | ||
| 18 | pub const OutStream = io.OutStream(*Self, Error, write); | ||
| 19 | |||
| 20 | const Self = @This(); | ||
| 21 | const u8_bit_count = comptime meta.bitCount(u8); | ||
| 22 | const u4_bit_count = comptime meta.bitCount(u4); | ||
| 23 | |||
| 24 | pub fn init(out_stream: OutStreamType) Self { | ||
| 25 | return Self{ | ||
| 26 | .out_stream = out_stream, | ||
| 27 | .bit_buffer = 0, | ||
| 28 | .bit_count = 0, | ||
| 29 | }; | ||
| 30 | } | ||
| 31 | |||
| 32 | /// Write the specified number of bits to the stream from the least significant bits of | ||
| 33 | /// the specified unsigned int value. Bits will only be written to the stream when there | ||
| 34 | /// are enough to fill a byte. | ||
| 35 | pub fn writeBits(self: *Self, value: var, bits: usize) Error!void { | ||
| 36 | if (bits == 0) return; | ||
| 37 | |||
| 38 | const U = @TypeOf(value); | ||
| 39 | comptime assert(trait.isUnsignedInt(U)); | ||
| 40 | |||
| 41 | //by extending the buffer to a minimum of u8 we can cover a number of edge cases | ||
| 42 | // related to shifting and casting. | ||
| 43 | const u_bit_count = comptime meta.bitCount(U); | ||
| 44 | const buf_bit_count = bc: { | ||
| 45 | assert(u_bit_count >= bits); | ||
| 46 | break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count; | ||
| 47 | }; | ||
| 48 | const Buf = std.meta.IntType(false, buf_bit_count); | ||
| 49 | const BufShift = math.Log2Int(Buf); | ||
| 50 | |||
| 51 | const buf_value = @intCast(Buf, value); | ||
| 52 | |||
| 53 | const high_byte_shift = @intCast(BufShift, buf_bit_count - u8_bit_count); | ||
| 54 | var in_buffer = switch (endian) { | ||
| 55 | .Big => buf_value << @intCast(BufShift, buf_bit_count - bits), | ||
| 56 | .Little => buf_value, | ||
| 57 | }; | ||
| 58 | var in_bits = bits; | ||
| 59 | |||
| 60 | if (self.bit_count > 0) { | ||
| 61 | const bits_remaining = u8_bit_count - self.bit_count; | ||
| 62 | const n = @intCast(u3, if (bits_remaining > bits) bits else bits_remaining); | ||
| 63 | switch (endian) { | ||
| 64 | .Big => { | ||
| 65 | const shift = @intCast(BufShift, high_byte_shift + self.bit_count); | ||
| 66 | const v = @intCast(u8, in_buffer >> shift); | ||
| 67 | self.bit_buffer |= v; | ||
| 68 | in_buffer <<= n; | ||
| 69 | }, | ||
| 70 | .Little => { | ||
| 71 | const v = @truncate(u8, in_buffer) << @intCast(u3, self.bit_count); | ||
| 72 | self.bit_buffer |= v; | ||
| 73 | in_buffer >>= n; | ||
| 74 | }, | ||
| 75 | } | ||
| 76 | self.bit_count += n; | ||
| 77 | in_bits -= n; | ||
| 78 | |||
| 79 | //if we didn't fill the buffer, it's because bits < bits_remaining; | ||
| 80 | if (self.bit_count != u8_bit_count) return; | ||
| 81 | try self.out_stream.writeByte(self.bit_buffer); | ||
| 82 | self.bit_buffer = 0; | ||
| 83 | self.bit_count = 0; | ||
| 84 | } | ||
| 85 | //at this point we know bit_buffer is empty | ||
| 86 | |||
| 87 | //copy bytes until we can't fill one anymore, then leave the rest in bit_buffer | ||
| 88 | while (in_bits >= u8_bit_count) { | ||
| 89 | switch (endian) { | ||
| 90 | .Big => { | ||
| 91 | const v = @intCast(u8, in_buffer >> high_byte_shift); | ||
| 92 | try self.out_stream.writeByte(v); | ||
| 93 | in_buffer <<= @intCast(u3, u8_bit_count - 1); | ||
| 94 | in_buffer <<= 1; | ||
| 95 | }, | ||
| 96 | .Little => { | ||
| 97 | const v = @truncate(u8, in_buffer); | ||
| 98 | try self.out_stream.writeByte(v); | ||
| 99 | in_buffer >>= @intCast(u3, u8_bit_count - 1); | ||
| 100 | in_buffer >>= 1; | ||
| 101 | }, | ||
| 102 | } | ||
| 103 | in_bits -= u8_bit_count; | ||
| 104 | } | ||
| 105 | |||
| 106 | if (in_bits > 0) { | ||
| 107 | self.bit_count = @intCast(u4, in_bits); | ||
| 108 | self.bit_buffer = switch (endian) { | ||
| 109 | .Big => @truncate(u8, in_buffer >> high_byte_shift), | ||
| 110 | .Little => @truncate(u8, in_buffer), | ||
| 111 | }; | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | /// Flush any remaining bits to the stream. | ||
| 116 | pub fn flushBits(self: *Self) Error!void { | ||
| 117 | if (self.bit_count == 0) return; | ||
| 118 | try self.out_stream.writeByte(self.bit_buffer); | ||
| 119 | self.bit_buffer = 0; | ||
| 120 | self.bit_count = 0; | ||
| 121 | } | ||
| 122 | |||
| 123 | pub fn write(self: *Self, buffer: []const u8) Error!usize { | ||
| 124 | // TODO: I'm not sure this is a good idea, maybe flushBits should be forced | ||
| 125 | if (self.bit_count > 0) { | ||
| 126 | for (buffer) |b, i| | ||
| 127 | try self.writeBits(b, u8_bit_count); | ||
| 128 | return buffer.len; | ||
| 129 | } | ||
| 130 | |||
| 131 | return self.out_stream.write(buffer); | ||
| 132 | } | ||
| 133 | |||
| 134 | pub fn outStream(self: *Self) OutStream { | ||
| 135 | return .{ .context = self }; | ||
| 136 | } | ||
| 137 | }; | ||
| 138 | } | ||
| 139 | |||
| 140 | pub fn bitOutStream( | ||
| 141 | comptime endian: builtin.Endian, | ||
| 142 | underlying_stream: var, | ||
| 143 | ) BitOutStream(endian, @TypeOf(underlying_stream)) { | ||
| 144 | return BitOutStream(endian, @TypeOf(underlying_stream)).init(underlying_stream); | ||
| 145 | } | ||
| 146 | |||
| 147 | test "api coverage" { | ||
| 148 | var mem_be = [_]u8{0} ** 2; | ||
| 149 | var mem_le = [_]u8{0} ** 2; | ||
| 150 | |||
| 151 | var mem_out_be = io.fixedBufferStream(&mem_be); | ||
| 152 | var bit_stream_be = bitOutStream(.Big, mem_out_be.outStream()); | ||
| 153 | |||
| 154 | try bit_stream_be.writeBits(@as(u2, 1), 1); | ||
| 155 | try bit_stream_be.writeBits(@as(u5, 2), 2); | ||
| 156 | try bit_stream_be.writeBits(@as(u128, 3), 3); | ||
| 157 | try bit_stream_be.writeBits(@as(u8, 4), 4); | ||
| 158 | try bit_stream_be.writeBits(@as(u9, 5), 5); | ||
| 159 | try bit_stream_be.writeBits(@as(u1, 1), 1); | ||
| 160 | |||
| 161 | testing.expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001011); | ||
| 162 | |||
| 163 | mem_out_be.pos = 0; | ||
| 164 | |||
| 165 | try bit_stream_be.writeBits(@as(u15, 0b110011010000101), 15); | ||
| 166 | try bit_stream_be.flushBits(); | ||
| 167 | testing.expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001010); | ||
| 168 | |||
| 169 | mem_out_be.pos = 0; | ||
| 170 | try bit_stream_be.writeBits(@as(u32, 0b110011010000101), 16); | ||
| 171 | testing.expect(mem_be[0] == 0b01100110 and mem_be[1] == 0b10000101); | ||
| 172 | |||
| 173 | try bit_stream_be.writeBits(@as(u0, 0), 0); | ||
| 174 | |||
| 175 | var mem_out_le = io.fixedBufferStream(&mem_le); | ||
| 176 | var bit_stream_le = bitOutStream(.Little, mem_out_le.outStream()); | ||
| 177 | |||
| 178 | try bit_stream_le.writeBits(@as(u2, 1), 1); | ||
| 179 | try bit_stream_le.writeBits(@as(u5, 2), 2); | ||
| 180 | try bit_stream_le.writeBits(@as(u128, 3), 3); | ||
| 181 | try bit_stream_le.writeBits(@as(u8, 4), 4); | ||
| 182 | try bit_stream_le.writeBits(@as(u9, 5), 5); | ||
| 183 | try bit_stream_le.writeBits(@as(u1, 1), 1); | ||
| 184 | |||
| 185 | testing.expect(mem_le[0] == 0b00011101 and mem_le[1] == 0b10010101); | ||
| 186 | |||
| 187 | mem_out_le.pos = 0; | ||
| 188 | try bit_stream_le.writeBits(@as(u15, 0b110011010000101), 15); | ||
| 189 | try bit_stream_le.flushBits(); | ||
| 190 | testing.expect(mem_le[0] == 0b10000101 and mem_le[1] == 0b01100110); | ||
| 191 | |||
| 192 | mem_out_le.pos = 0; | ||
| 193 | try bit_stream_le.writeBits(@as(u32, 0b1100110100001011), 16); | ||
| 194 | testing.expect(mem_le[0] == 0b00001011 and mem_le[1] == 0b11001101); | ||
| 195 | |||
| 196 | try bit_stream_le.writeBits(@as(u0, 0), 0); | ||
| 197 | } | ||
lib/std/io/buffered_in_stream.zig+3-1| ... | @@ -1,7 +1,9 @@ | ... | @@ -1,7 +1,9 @@ |
| 1 | const std = @import("../std.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const io = std.io; | 2 | const io = std.io; |
| 3 | const assert = std.debug.assert; | ||
| 4 | const testing = std.testing; | ||
| 3 | 5 | ||
| 4 | pub fn BufferedInStream(comptime buffer_size: usize, comptime InStreamType) type { | 6 | pub fn BufferedInStream(comptime buffer_size: usize, comptime InStreamType: type) type { |
| 5 | return struct { | 7 | return struct { |
| 6 | unbuffered_in_stream: InStreamType, | 8 | unbuffered_in_stream: InStreamType, |
| 7 | fifo: FifoType = FifoType.init(), | 9 | fifo: FifoType = FifoType.init(), |
lib/std/io/c_out_stream.zig created+44| ... | @@ -0,0 +1,44 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const builtin = std.builtin; | ||
| 3 | const io = std.io; | ||
| 4 | const testing = std.testing; | ||
| 5 | |||
| 6 | pub const COutStream = io.OutStream(*std.c.FILE, std.fs.File.WriteError, cOutStreamWrite); | ||
| 7 | |||
| 8 | pub fn cOutStream(c_file: *std.c.FILE) COutStream { | ||
| 9 | return .{ .context = c_file }; | ||
| 10 | } | ||
| 11 | |||
| 12 | fn cOutStreamWrite(c_file: *std.c.FILE, bytes: []const u8) std.fs.File.WriteError!usize { | ||
| 13 | const amt_written = std.c.fwrite(bytes.ptr, 1, bytes.len, c_file); | ||
| 14 | if (amt_written >= 0) return amt_written; | ||
| 15 | switch (std.c._errno().*) { | ||
| 16 | 0 => unreachable, | ||
| 17 | os.EINVAL => unreachable, | ||
| 18 | os.EFAULT => unreachable, | ||
| 19 | os.EAGAIN => unreachable, // this is a blocking API | ||
| 20 | os.EBADF => unreachable, // always a race condition | ||
| 21 | os.EDESTADDRREQ => unreachable, // connect was never called | ||
| 22 | os.EDQUOT => return error.DiskQuota, | ||
| 23 | os.EFBIG => return error.FileTooBig, | ||
| 24 | os.EIO => return error.InputOutput, | ||
| 25 | os.ENOSPC => return error.NoSpaceLeft, | ||
| 26 | os.EPERM => return error.AccessDenied, | ||
| 27 | os.EPIPE => return error.BrokenPipe, | ||
| 28 | else => |err| return os.unexpectedErrno(@intCast(usize, err)), | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | test "" { | ||
| 33 | if (!builtin.link_libc) return error.SkipZigTest; | ||
| 34 | |||
| 35 | const filename = "tmp_io_test_file.txt"; | ||
| 36 | const out_file = std.c.fopen(filename, "w") orelse return error.UnableToOpenTestFile; | ||
| 37 | defer { | ||
| 38 | _ = std.c.fclose(out_file); | ||
| 39 | fs.cwd().deleteFileC(filename) catch {}; | ||
| 40 | } | ||
| 41 | |||
| 42 | const out_stream = &io.COutStream.init(out_file).stream; | ||
| 43 | try out_stream.print("hi: {}\n", .{@as(i32, 123)}); | ||
| 44 | } | ||
lib/std/io/counting_out_stream.zig+9-12| ... | @@ -1,5 +1,6 @@ | ... | @@ -1,5 +1,6 @@ |
| 1 | const std = @import("../std.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const io = std.io; | 2 | const io = std.io; |
| 3 | const testing = std.testing; | ||
| 3 | 4 | ||
| 4 | /// An OutStream that counts how many bytes has been written to it. | 5 | /// An OutStream that counts how many bytes has been written to it. |
| 5 | pub fn CountingOutStream(comptime OutStreamType: type) type { | 6 | pub fn CountingOutStream(comptime OutStreamType: type) type { |
| ... | @@ -12,13 +13,6 @@ pub fn CountingOutStream(comptime OutStreamType: type) type { | ... | @@ -12,13 +13,6 @@ pub fn CountingOutStream(comptime OutStreamType: type) type { |
| 12 | 13 | ||
| 13 | const Self = @This(); | 14 | const Self = @This(); |
| 14 | 15 | ||
| 15 | pub fn init(child_stream: OutStreamType) Self { | ||
| 16 | return Self{ | ||
| 17 | .bytes_written = 0, | ||
| 18 | .child_stream = child_stream, | ||
| 19 | }; | ||
| 20 | } | ||
| 21 | |||
| 22 | pub fn write(self: *Self, bytes: []const u8) Error!usize { | 16 | pub fn write(self: *Self, bytes: []const u8) Error!usize { |
| 23 | const amt = try self.child_stream.write(bytes); | 17 | const amt = try self.child_stream.write(bytes); |
| 24 | self.bytes_written += amt; | 18 | self.bytes_written += amt; |
| ... | @@ -31,12 +25,15 @@ pub fn CountingOutStream(comptime OutStreamType: type) type { | ... | @@ -31,12 +25,15 @@ pub fn CountingOutStream(comptime OutStreamType: type) type { |
| 31 | }; | 25 | }; |
| 32 | } | 26 | } |
| 33 | 27 | ||
| 28 | pub fn countingOutStream(child_stream: var) CountingOutStream(@TypeOf(child_stream)) { | ||
| 29 | return .{ .bytes_written = 0, .child_stream = child_stream }; | ||
| 30 | } | ||
| 31 | |||
| 34 | test "io.CountingOutStream" { | 32 | test "io.CountingOutStream" { |
| 35 | var counting_stream = CountingOutStream(NullOutStream.Error).init(std.io.null_out_stream); | 33 | var counting_stream = countingOutStream(std.io.null_out_stream); |
| 36 | const stream = &counting_stream.stream; | 34 | const stream = counting_stream.outStream(); |
| 37 | 35 | ||
| 38 | const bytes = "yay" ** 10000; | 36 | const bytes = "yay" ** 100; |
| 39 | stream.write(bytes) catch unreachable; | 37 | stream.writeAll(bytes) catch unreachable; |
| 40 | testing.expect(counting_stream.bytes_written == bytes.len); | 38 | testing.expect(counting_stream.bytes_written == bytes.len); |
| 41 | } | 39 | } |
| 42 |
lib/std/io/fixed_buffer_stream.zig+48-8| ... | @@ -1,9 +1,11 @@ | ... | @@ -1,9 +1,11 @@ |
| 1 | const std = @import("../std.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const io = std.io; | 2 | const io = std.io; |
| 3 | const testing = std.testing; | 3 | const testing = std.testing; |
| 4 | const mem = std.mem; | ||
| 5 | const assert = std.debug.assert; | ||
| 4 | 6 | ||
| 5 | /// This turns a slice into an `io.OutStream`, `io.InStream`, or `io.SeekableStream`. | 7 | /// This turns a byte buffer into an `io.OutStream`, `io.InStream`, or `io.SeekableStream`. |
| 6 | /// If the supplied slice is const, then `io.OutStream` is not available. | 8 | /// If the supplied byte buffer is const, then `io.OutStream` is not available. |
| 7 | pub fn FixedBufferStream(comptime Buffer: type) type { | 9 | pub fn FixedBufferStream(comptime Buffer: type) type { |
| 8 | return struct { | 10 | return struct { |
| 9 | /// `Buffer` is either a `[]u8` or `[]const u8`. | 11 | /// `Buffer` is either a `[]u8` or `[]const u8`. |
| ... | @@ -46,7 +48,7 @@ pub fn FixedBufferStream(comptime Buffer: type) type { | ... | @@ -46,7 +48,7 @@ pub fn FixedBufferStream(comptime Buffer: type) type { |
| 46 | const size = std.math.min(dest.len, self.buffer.len - self.pos); | 48 | const size = std.math.min(dest.len, self.buffer.len - self.pos); |
| 47 | const end = self.pos + size; | 49 | const end = self.pos + size; |
| 48 | 50 | ||
| 49 | std.mem.copy(u8, dest[0..size], self.buffer[self.pos..end]); | 51 | mem.copy(u8, dest[0..size], self.buffer[self.pos..end]); |
| 50 | self.pos = end; | 52 | self.pos = end; |
| 51 | 53 | ||
| 52 | if (size == 0) return error.EndOfStream; | 54 | if (size == 0) return error.EndOfStream; |
| ... | @@ -65,7 +67,7 @@ pub fn FixedBufferStream(comptime Buffer: type) type { | ... | @@ -65,7 +67,7 @@ pub fn FixedBufferStream(comptime Buffer: type) type { |
| 65 | else | 67 | else |
| 66 | self.buffer.len - self.pos; | 68 | self.buffer.len - self.pos; |
| 67 | 69 | ||
| 68 | std.mem.copy(u8, self.buffer[self.pos .. self.pos + n], bytes[0..n]); | 70 | mem.copy(u8, self.buffer[self.pos .. self.pos + n], bytes[0..n]); |
| 69 | self.pos += n; | 71 | self.pos += n; |
| 70 | 72 | ||
| 71 | if (n == 0) return error.OutOfMemory; | 73 | if (n == 0) return error.OutOfMemory; |
| ... | @@ -100,7 +102,7 @@ pub fn FixedBufferStream(comptime Buffer: type) type { | ... | @@ -100,7 +102,7 @@ pub fn FixedBufferStream(comptime Buffer: type) type { |
| 100 | } | 102 | } |
| 101 | 103 | ||
| 102 | pub fn getWritten(self: Self) []const u8 { | 104 | pub fn getWritten(self: Self) []const u8 { |
| 103 | return self.slice[0..self.pos]; | 105 | return self.buffer[0..self.pos]; |
| 104 | } | 106 | } |
| 105 | 107 | ||
| 106 | pub fn reset(self: *Self) void { | 108 | pub fn reset(self: *Self) void { |
| ... | @@ -110,16 +112,16 @@ pub fn FixedBufferStream(comptime Buffer: type) type { | ... | @@ -110,16 +112,16 @@ pub fn FixedBufferStream(comptime Buffer: type) type { |
| 110 | } | 112 | } |
| 111 | 113 | ||
| 112 | pub fn fixedBufferStream(buffer: var) FixedBufferStream(NonSentinelSpan(@TypeOf(buffer))) { | 114 | pub fn fixedBufferStream(buffer: var) FixedBufferStream(NonSentinelSpan(@TypeOf(buffer))) { |
| 113 | return .{ .buffer = std.mem.span(buffer), .pos = 0 }; | 115 | return .{ .buffer = mem.span(buffer), .pos = 0 }; |
| 114 | } | 116 | } |
| 115 | 117 | ||
| 116 | fn NonSentinelSpan(comptime T: type) type { | 118 | fn NonSentinelSpan(comptime T: type) type { |
| 117 | var ptr_info = @typeInfo(std.mem.Span(T)).Pointer; | 119 | var ptr_info = @typeInfo(mem.Span(T)).Pointer; |
| 118 | ptr_info.sentinel = null; | 120 | ptr_info.sentinel = null; |
| 119 | return @Type(std.builtin.TypeInfo{ .Pointer = ptr_info }); | 121 | return @Type(std.builtin.TypeInfo{ .Pointer = ptr_info }); |
| 120 | } | 122 | } |
| 121 | 123 | ||
| 122 | test "FixedBufferStream" { | 124 | test "FixedBufferStream output" { |
| 123 | var buf: [255]u8 = undefined; | 125 | var buf: [255]u8 = undefined; |
| 124 | var fbs = fixedBufferStream(&buf); | 126 | var fbs = fixedBufferStream(&buf); |
| 125 | const stream = fbs.outStream(); | 127 | const stream = fbs.outStream(); |
| ... | @@ -127,3 +129,41 @@ test "FixedBufferStream" { | ... | @@ -127,3 +129,41 @@ test "FixedBufferStream" { |
| 127 | try stream.print("{}{}!", .{ "Hello", "World" }); | 129 | try stream.print("{}{}!", .{ "Hello", "World" }); |
| 128 | testing.expectEqualSlices(u8, "HelloWorld!", fbs.getWritten()); | 130 | testing.expectEqualSlices(u8, "HelloWorld!", fbs.getWritten()); |
| 129 | } | 131 | } |
| 132 | |||
| 133 | test "FixedBufferStream output 2" { | ||
| 134 | var buffer: [10]u8 = undefined; | ||
| 135 | var fbs = fixedBufferStream(&buffer); | ||
| 136 | |||
| 137 | try fbs.outStream().writeAll("Hello"); | ||
| 138 | testing.expect(mem.eql(u8, fbs.getWritten(), "Hello")); | ||
| 139 | |||
| 140 | try fbs.outStream().writeAll("world"); | ||
| 141 | testing.expect(mem.eql(u8, fbs.getWritten(), "Helloworld")); | ||
| 142 | |||
| 143 | testing.expectError(error.OutOfMemory, fbs.outStream().writeAll("!")); | ||
| 144 | testing.expect(mem.eql(u8, fbs.getWritten(), "Helloworld")); | ||
| 145 | |||
| 146 | fbs.reset(); | ||
| 147 | testing.expect(fbs.getWritten().len == 0); | ||
| 148 | |||
| 149 | testing.expectError(error.OutOfMemory, fbs.outStream().writeAll("Hello world!")); | ||
| 150 | testing.expect(mem.eql(u8, fbs.getWritten(), "Hello worl")); | ||
| 151 | } | ||
| 152 | |||
| 153 | test "FixedBufferStream input" { | ||
| 154 | const bytes = [_]u8{ 1, 2, 3, 4, 5, 6, 7 }; | ||
| 155 | var fbs = fixedBufferStream(&bytes); | ||
| 156 | |||
| 157 | var dest: [4]u8 = undefined; | ||
| 158 | |||
| 159 | var read = try fbs.inStream().read(dest[0..4]); | ||
| 160 | testing.expect(read == 4); | ||
| 161 | testing.expect(mem.eql(u8, dest[0..4], bytes[0..4])); | ||
| 162 | |||
| 163 | read = try fbs.inStream().read(dest[0..4]); | ||
| 164 | testing.expect(read == 3); | ||
| 165 | testing.expect(mem.eql(u8, dest[0..3], bytes[4..7])); | ||
| 166 | |||
| 167 | read = try fbs.inStream().read(dest[0..4]); | ||
| 168 | testing.expect(read == 0); | ||
| 169 | } |
lib/std/io/in_stream.zig+1-2| ... | @@ -273,8 +273,7 @@ pub fn InStream( | ... | @@ -273,8 +273,7 @@ pub fn InStream( |
| 273 | 273 | ||
| 274 | test "InStream" { | 274 | test "InStream" { |
| 275 | var buf = "a\x02".*; | 275 | var buf = "a\x02".*; |
| 276 | var slice_stream = std.io.SliceInStream.init(&buf); | 276 | const in_stream = std.io.fixedBufferStream(&buf).inStream(); |
| 277 | const in_stream = &slice_stream.stream; | ||
| 278 | testing.expect((try in_stream.readByte()) == 'a'); | 277 | testing.expect((try in_stream.readByte()) == 'a'); |
| 279 | testing.expect((try in_stream.readEnum(enum(u8) { | 278 | testing.expect((try in_stream.readEnum(enum(u8) { |
| 280 | a = 0, | 279 | a = 0, |
lib/std/io/peek_stream.zig created+112| ... | @@ -0,0 +1,112 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const io = std.io; | ||
| 3 | const mem = std.mem; | ||
| 4 | const testing = std.testing; | ||
| 5 | |||
| 6 | /// Creates a stream which supports 'un-reading' data, so that it can be read again. | ||
| 7 | /// This makes look-ahead style parsing much easier. | ||
| 8 | /// TODO merge this with `std.io.BufferedInStream`: https://github.com/ziglang/zig/issues/4501 | ||
| 9 | pub fn PeekStream( | ||
| 10 | comptime buffer_type: std.fifo.LinearFifoBufferType, | ||
| 11 | comptime InStreamType: type, | ||
| 12 | ) type { | ||
| 13 | return struct { | ||
| 14 | unbuffered_in_stream: InStreamType, | ||
| 15 | fifo: FifoType, | ||
| 16 | |||
| 17 | pub const Error = InStreamType.Error; | ||
| 18 | pub const InStream = io.InStream(*Self, Error, read); | ||
| 19 | |||
| 20 | const Self = @This(); | ||
| 21 | const FifoType = std.fifo.LinearFifo(u8, buffer_type); | ||
| 22 | |||
| 23 | pub usingnamespace switch (buffer_type) { | ||
| 24 | .Static => struct { | ||
| 25 | pub fn init(base: InStreamType) Self { | ||
| 26 | return .{ | ||
| 27 | .base = base, | ||
| 28 | .fifo = FifoType.init(), | ||
| 29 | }; | ||
| 30 | } | ||
| 31 | }, | ||
| 32 | .Slice => struct { | ||
| 33 | pub fn init(base: InStreamType, buf: []u8) Self { | ||
| 34 | return .{ | ||
| 35 | .base = base, | ||
| 36 | .fifo = FifoType.init(buf), | ||
| 37 | }; | ||
| 38 | } | ||
| 39 | }, | ||
| 40 | .Dynamic => struct { | ||
| 41 | pub fn init(base: InStreamType, allocator: *mem.Allocator) Self { | ||
| 42 | return .{ | ||
| 43 | .base = base, | ||
| 44 | .fifo = FifoType.init(allocator), | ||
| 45 | }; | ||
| 46 | } | ||
| 47 | }, | ||
| 48 | }; | ||
| 49 | |||
| 50 | pub fn putBackByte(self: *Self, byte: u8) !void { | ||
| 51 | try self.putBack(&[_]u8{byte}); | ||
| 52 | } | ||
| 53 | |||
| 54 | pub fn putBack(self: *Self, bytes: []const u8) !void { | ||
| 55 | try self.fifo.unget(bytes); | ||
| 56 | } | ||
| 57 | |||
| 58 | pub fn read(self: *Self, dest: []u8) Error!usize { | ||
| 59 | // copy over anything putBack()'d | ||
| 60 | var dest_index = self.fifo.read(dest); | ||
| 61 | if (dest_index == dest.len) return dest_index; | ||
| 62 | |||
| 63 | // ask the backing stream for more | ||
| 64 | dest_index += try self.base.read(dest[dest_index..]); | ||
| 65 | return dest_index; | ||
| 66 | } | ||
| 67 | |||
| 68 | pub fn inStream(self: *Self) InStream { | ||
| 69 | return .{ .context = self }; | ||
| 70 | } | ||
| 71 | }; | ||
| 72 | } | ||
| 73 | |||
| 74 | pub fn peekStream( | ||
| 75 | comptime lookahead: comptime_int, | ||
| 76 | underlying_stream: var, | ||
| 77 | ) PeekStream(.{ .Static = lookahead }, @TypeOf(underlying_stream)) { | ||
| 78 | return PeekStream(.{ .Static = lookahead }, @TypeOf(underlying_stream)).init(underlying_stream); | ||
| 79 | } | ||
| 80 | |||
| 81 | test "PeekStream" { | ||
| 82 | const bytes = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; | ||
| 83 | var fbs = io.fixedBufferStream(&bytes); | ||
| 84 | var ps = peekStream(2, fbs.inStream()); | ||
| 85 | |||
| 86 | var dest: [4]u8 = undefined; | ||
| 87 | |||
| 88 | try ps.putBackByte(9); | ||
| 89 | try ps.putBackByte(10); | ||
| 90 | |||
| 91 | var read = try ps.inStream().read(dest[0..4]); | ||
| 92 | testing.expect(read == 4); | ||
| 93 | testing.expect(dest[0] == 10); | ||
| 94 | testing.expect(dest[1] == 9); | ||
| 95 | testing.expect(mem.eql(u8, dest[2..4], bytes[0..2])); | ||
| 96 | |||
| 97 | read = try ps.inStream().read(dest[0..4]); | ||
| 98 | testing.expect(read == 4); | ||
| 99 | testing.expect(mem.eql(u8, dest[0..4], bytes[2..6])); | ||
| 100 | |||
| 101 | read = try ps.inStream().read(dest[0..4]); | ||
| 102 | testing.expect(read == 2); | ||
| 103 | testing.expect(mem.eql(u8, dest[0..2], bytes[6..8])); | ||
| 104 | |||
| 105 | try ps.putBackByte(11); | ||
| 106 | try ps.putBackByte(12); | ||
| 107 | |||
| 108 | read = try ps.inStream().read(dest[0..4]); | ||
| 109 | testing.expect(read == 2); | ||
| 110 | testing.expect(dest[0] == 12); | ||
| 111 | testing.expect(dest[1] == 11); | ||
| 112 | } | ||
lib/std/io/serialization.zig created+606| ... | @@ -0,0 +1,606 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const builtin = std.builtin; | ||
| 3 | const io = std.io; | ||
| 4 | |||
| 5 | pub const Packing = enum { | ||
| 6 | /// Pack data to byte alignment | ||
| 7 | Byte, | ||
| 8 | |||
| 9 | /// Pack data to bit alignment | ||
| 10 | Bit, | ||
| 11 | }; | ||
| 12 | |||
| 13 | /// Creates a deserializer that deserializes types from any stream. | ||
| 14 | /// If `is_packed` is true, the data stream is treated as bit-packed, | ||
| 15 | /// otherwise data is expected to be packed to the smallest byte. | ||
| 16 | /// Types may implement a custom deserialization routine with a | ||
| 17 | /// function named `deserialize` in the form of: | ||
| 18 | /// pub fn deserialize(self: *Self, deserializer: var) !void | ||
| 19 | /// which will be called when the deserializer is used to deserialize | ||
| 20 | /// that type. It will pass a pointer to the type instance to deserialize | ||
| 21 | /// into and a pointer to the deserializer struct. | ||
| 22 | pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime InStreamType: type) type { | ||
| 23 | return struct { | ||
| 24 | in_stream: if (packing == .Bit) io.BitInStream(endian, InStreamType) else InStreamType, | ||
| 25 | |||
| 26 | const Self = @This(); | ||
| 27 | |||
| 28 | pub fn init(in_stream: InStreamType) Self { | ||
| 29 | return Self{ | ||
| 30 | .in_stream = switch (packing) { | ||
| 31 | .Bit => io.bitInStream(endian, in_stream), | ||
| 32 | .Byte => in_stream, | ||
| 33 | }, | ||
| 34 | }; | ||
| 35 | } | ||
| 36 | |||
| 37 | pub fn alignToByte(self: *Self) void { | ||
| 38 | if (packing == .Byte) return; | ||
| 39 | self.in_stream.alignToByte(); | ||
| 40 | } | ||
| 41 | |||
| 42 | //@BUG: inferred error issue. See: #1386 | ||
| 43 | fn deserializeInt(self: *Self, comptime T: type) (InStreamType.Error || error{EndOfStream})!T { | ||
| 44 | comptime assert(trait.is(.Int)(T) or trait.is(.Float)(T)); | ||
| 45 | |||
| 46 | const u8_bit_count = 8; | ||
| 47 | const t_bit_count = comptime meta.bitCount(T); | ||
| 48 | |||
| 49 | const U = std.meta.IntType(false, t_bit_count); | ||
| 50 | const Log2U = math.Log2Int(U); | ||
| 51 | const int_size = (U.bit_count + 7) / 8; | ||
| 52 | |||
| 53 | if (packing == .Bit) { | ||
| 54 | const result = try self.in_stream.readBitsNoEof(U, t_bit_count); | ||
| 55 | return @bitCast(T, result); | ||
| 56 | } | ||
| 57 | |||
| 58 | var buffer: [int_size]u8 = undefined; | ||
| 59 | const read_size = try self.in_stream.read(buffer[0..]); | ||
| 60 | if (read_size < int_size) return error.EndOfStream; | ||
| 61 | |||
| 62 | if (int_size == 1) { | ||
| 63 | if (t_bit_count == 8) return @bitCast(T, buffer[0]); | ||
| 64 | const PossiblySignedByte = std.meta.IntType(T.is_signed, 8); | ||
| 65 | return @truncate(T, @bitCast(PossiblySignedByte, buffer[0])); | ||
| 66 | } | ||
| 67 | |||
| 68 | var result = @as(U, 0); | ||
| 69 | for (buffer) |byte, i| { | ||
| 70 | switch (endian) { | ||
| 71 | .Big => { | ||
| 72 | result = (result << u8_bit_count) | byte; | ||
| 73 | }, | ||
| 74 | .Little => { | ||
| 75 | result |= @as(U, byte) << @intCast(Log2U, u8_bit_count * i); | ||
| 76 | }, | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 80 | return @bitCast(T, result); | ||
| 81 | } | ||
| 82 | |||
| 83 | /// Deserializes and returns data of the specified type from the stream | ||
| 84 | pub fn deserialize(self: *Self, comptime T: type) !T { | ||
| 85 | var value: T = undefined; | ||
| 86 | try self.deserializeInto(&value); | ||
| 87 | return value; | ||
| 88 | } | ||
| 89 | |||
| 90 | /// Deserializes data into the type pointed to by `ptr` | ||
| 91 | pub fn deserializeInto(self: *Self, ptr: var) !void { | ||
| 92 | const T = @TypeOf(ptr); | ||
| 93 | comptime assert(trait.is(.Pointer)(T)); | ||
| 94 | |||
| 95 | if (comptime trait.isSlice(T) or comptime trait.isPtrTo(.Array)(T)) { | ||
| 96 | for (ptr) |*v| | ||
| 97 | try self.deserializeInto(v); | ||
| 98 | return; | ||
| 99 | } | ||
| 100 | |||
| 101 | comptime assert(trait.isSingleItemPtr(T)); | ||
| 102 | |||
| 103 | const C = comptime meta.Child(T); | ||
| 104 | const child_type_id = @typeInfo(C); | ||
| 105 | |||
| 106 | //custom deserializer: fn(self: *Self, deserializer: var) !void | ||
| 107 | if (comptime trait.hasFn("deserialize")(C)) return C.deserialize(ptr, self); | ||
| 108 | |||
| 109 | if (comptime trait.isPacked(C) and packing != .Bit) { | ||
| 110 | var packed_deserializer = deserializer(endian, .Bit, self.in_stream); | ||
| 111 | return packed_deserializer.deserializeInto(ptr); | ||
| 112 | } | ||
| 113 | |||
| 114 | switch (child_type_id) { | ||
| 115 | .Void => return, | ||
| 116 | .Bool => ptr.* = (try self.deserializeInt(u1)) > 0, | ||
| 117 | .Float, .Int => ptr.* = try self.deserializeInt(C), | ||
| 118 | .Struct => { | ||
| 119 | const info = @typeInfo(C).Struct; | ||
| 120 | |||
| 121 | inline for (info.fields) |*field_info| { | ||
| 122 | const name = field_info.name; | ||
| 123 | const FieldType = field_info.field_type; | ||
| 124 | |||
| 125 | if (FieldType == void or FieldType == u0) continue; | ||
| 126 | |||
| 127 | //it doesn't make any sense to read pointers | ||
| 128 | if (comptime trait.is(.Pointer)(FieldType)) { | ||
| 129 | @compileError("Will not " ++ "read field " ++ name ++ " of struct " ++ | ||
| 130 | @typeName(C) ++ " because it " ++ "is of pointer-type " ++ | ||
| 131 | @typeName(FieldType) ++ "."); | ||
| 132 | } | ||
| 133 | |||
| 134 | try self.deserializeInto(&@field(ptr, name)); | ||
| 135 | } | ||
| 136 | }, | ||
| 137 | .Union => { | ||
| 138 | const info = @typeInfo(C).Union; | ||
| 139 | if (info.tag_type) |TagType| { | ||
| 140 | //we avoid duplicate iteration over the enum tags | ||
| 141 | // by getting the int directly and casting it without | ||
| 142 | // safety. If it is bad, it will be caught anyway. | ||
| 143 | const TagInt = @TagType(TagType); | ||
| 144 | const tag = try self.deserializeInt(TagInt); | ||
| 145 | |||
| 146 | inline for (info.fields) |field_info| { | ||
| 147 | if (field_info.enum_field.?.value == tag) { | ||
| 148 | const name = field_info.name; | ||
| 149 | const FieldType = field_info.field_type; | ||
| 150 | ptr.* = @unionInit(C, name, undefined); | ||
| 151 | try self.deserializeInto(&@field(ptr, name)); | ||
| 152 | return; | ||
| 153 | } | ||
| 154 | } | ||
| 155 | //This is reachable if the enum data is bad | ||
| 156 | return error.InvalidEnumTag; | ||
| 157 | } | ||
| 158 | @compileError("Cannot meaningfully deserialize " ++ @typeName(C) ++ | ||
| 159 | " because it is an untagged union. Use a custom deserialize()."); | ||
| 160 | }, | ||
| 161 | .Optional => { | ||
| 162 | const OC = comptime meta.Child(C); | ||
| 163 | const exists = (try self.deserializeInt(u1)) > 0; | ||
| 164 | if (!exists) { | ||
| 165 | ptr.* = null; | ||
| 166 | return; | ||
| 167 | } | ||
| 168 | |||
| 169 | ptr.* = @as(OC, undefined); //make it non-null so the following .? is guaranteed safe | ||
| 170 | const val_ptr = &ptr.*.?; | ||
| 171 | try self.deserializeInto(val_ptr); | ||
| 172 | }, | ||
| 173 | .Enum => { | ||
| 174 | var value = try self.deserializeInt(@TagType(C)); | ||
| 175 | ptr.* = try meta.intToEnum(C, value); | ||
| 176 | }, | ||
| 177 | else => { | ||
| 178 | @compileError("Cannot deserialize " ++ @tagName(child_type_id) ++ " types (unimplemented)."); | ||
| 179 | }, | ||
| 180 | } | ||
| 181 | } | ||
| 182 | }; | ||
| 183 | } | ||
| 184 | |||
| 185 | pub fn deserializer( | ||
| 186 | comptime endian: builtin.Endian, | ||
| 187 | comptime packing: Packing, | ||
| 188 | in_stream: var, | ||
| 189 | ) Deserializer(endian, packing, @TypeOf(in_stream)) { | ||
| 190 | return Deserializer(endian, packing, @TypeOf(in_stream)).init(in_stream); | ||
| 191 | } | ||
| 192 | |||
| 193 | /// Creates a serializer that serializes types to any stream. | ||
| 194 | /// If `is_packed` is true, the data will be bit-packed into the stream. | ||
| 195 | /// Note that the you must call `serializer.flush()` when you are done | ||
| 196 | /// writing bit-packed data in order ensure any unwritten bits are committed. | ||
| 197 | /// If `is_packed` is false, data is packed to the smallest byte. In the case | ||
| 198 | /// of packed structs, the struct will written bit-packed and with the specified | ||
| 199 | /// endianess, after which data will resume being written at the next byte boundary. | ||
| 200 | /// Types may implement a custom serialization routine with a | ||
| 201 | /// function named `serialize` in the form of: | ||
| 202 | /// pub fn serialize(self: Self, serializer: var) !void | ||
| 203 | /// which will be called when the serializer is used to serialize that type. It will | ||
| 204 | /// pass a const pointer to the type instance to be serialized and a pointer | ||
| 205 | /// to the serializer struct. | ||
| 206 | pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime OutStreamType: type) type { | ||
| 207 | return struct { | ||
| 208 | out_stream: if (packing == .Bit) BitOutStream(endian, OutStreamType) else OutStreamType, | ||
| 209 | |||
| 210 | const Self = @This(); | ||
| 211 | pub const Error = OutStreamType.Error; | ||
| 212 | |||
| 213 | pub fn init(out_stream: OutStreamType) Self { | ||
| 214 | return Self{ | ||
| 215 | .out_stream = switch (packing) { | ||
| 216 | .Bit => io.bitOutStream(endian, out_stream), | ||
| 217 | .Byte => out_stream, | ||
| 218 | }, | ||
| 219 | }; | ||
| 220 | } | ||
| 221 | |||
| 222 | /// Flushes any unwritten bits to the stream | ||
| 223 | pub fn flush(self: *Self) Error!void { | ||
| 224 | if (packing == .Bit) return self.out_stream.flushBits(); | ||
| 225 | } | ||
| 226 | |||
| 227 | fn serializeInt(self: *Self, value: var) Error!void { | ||
| 228 | const T = @TypeOf(value); | ||
| 229 | comptime assert(trait.is(.Int)(T) or trait.is(.Float)(T)); | ||
| 230 | |||
| 231 | const t_bit_count = comptime meta.bitCount(T); | ||
| 232 | const u8_bit_count = comptime meta.bitCount(u8); | ||
| 233 | |||
| 234 | const U = std.meta.IntType(false, t_bit_count); | ||
| 235 | const Log2U = math.Log2Int(U); | ||
| 236 | const int_size = (U.bit_count + 7) / 8; | ||
| 237 | |||
| 238 | const u_value = @bitCast(U, value); | ||
| 239 | |||
| 240 | if (packing == .Bit) return self.out_stream.writeBits(u_value, t_bit_count); | ||
| 241 | |||
| 242 | var buffer: [int_size]u8 = undefined; | ||
| 243 | if (int_size == 1) buffer[0] = u_value; | ||
| 244 | |||
| 245 | for (buffer) |*byte, i| { | ||
| 246 | const idx = switch (endian) { | ||
| 247 | .Big => int_size - i - 1, | ||
| 248 | .Little => i, | ||
| 249 | }; | ||
| 250 | const shift = @intCast(Log2U, idx * u8_bit_count); | ||
| 251 | const v = u_value >> shift; | ||
| 252 | byte.* = if (t_bit_count < u8_bit_count) v else @truncate(u8, v); | ||
| 253 | } | ||
| 254 | |||
| 255 | try self.out_stream.write(&buffer); | ||
| 256 | } | ||
| 257 | |||
| 258 | /// Serializes the passed value into the stream | ||
| 259 | pub fn serialize(self: *Self, value: var) Error!void { | ||
| 260 | const T = comptime @TypeOf(value); | ||
| 261 | |||
| 262 | if (comptime trait.isIndexable(T)) { | ||
| 263 | for (value) |v| | ||
| 264 | try self.serialize(v); | ||
| 265 | return; | ||
| 266 | } | ||
| 267 | |||
| 268 | //custom serializer: fn(self: Self, serializer: var) !void | ||
| 269 | if (comptime trait.hasFn("serialize")(T)) return T.serialize(value, self); | ||
| 270 | |||
| 271 | if (comptime trait.isPacked(T) and packing != .Bit) { | ||
| 272 | var packed_serializer = Serializer(endian, .Bit, Error).init(self.out_stream); | ||
| 273 | try packed_serializer.serialize(value); | ||
| 274 | try packed_serializer.flush(); | ||
| 275 | return; | ||
| 276 | } | ||
| 277 | |||
| 278 | switch (@typeInfo(T)) { | ||
| 279 | .Void => return, | ||
| 280 | .Bool => try self.serializeInt(@as(u1, @boolToInt(value))), | ||
| 281 | .Float, .Int => try self.serializeInt(value), | ||
| 282 | .Struct => { | ||
| 283 | const info = @typeInfo(T); | ||
| 284 | |||
| 285 | inline for (info.Struct.fields) |*field_info| { | ||
| 286 | const name = field_info.name; | ||
| 287 | const FieldType = field_info.field_type; | ||
| 288 | |||
| 289 | if (FieldType == void or FieldType == u0) continue; | ||
| 290 | |||
| 291 | //It doesn't make sense to write pointers | ||
| 292 | if (comptime trait.is(.Pointer)(FieldType)) { | ||
| 293 | @compileError("Will not " ++ "serialize field " ++ name ++ | ||
| 294 | " of struct " ++ @typeName(T) ++ " because it " ++ | ||
| 295 | "is of pointer-type " ++ @typeName(FieldType) ++ "."); | ||
| 296 | } | ||
| 297 | try self.serialize(@field(value, name)); | ||
| 298 | } | ||
| 299 | }, | ||
| 300 | .Union => { | ||
| 301 | const info = @typeInfo(T).Union; | ||
| 302 | if (info.tag_type) |TagType| { | ||
| 303 | const active_tag = meta.activeTag(value); | ||
| 304 | try self.serialize(active_tag); | ||
| 305 | //This inline loop is necessary because active_tag is a runtime | ||
| 306 | // value, but @field requires a comptime value. Our alternative | ||
| 307 | // is to check each field for a match | ||
| 308 | inline for (info.fields) |field_info| { | ||
| 309 | if (field_info.enum_field.?.value == @enumToInt(active_tag)) { | ||
| 310 | const name = field_info.name; | ||
| 311 | const FieldType = field_info.field_type; | ||
| 312 | try self.serialize(@field(value, name)); | ||
| 313 | return; | ||
| 314 | } | ||
| 315 | } | ||
| 316 | unreachable; | ||
| 317 | } | ||
| 318 | @compileError("Cannot meaningfully serialize " ++ @typeName(T) ++ | ||
| 319 | " because it is an untagged union. Use a custom serialize()."); | ||
| 320 | }, | ||
| 321 | .Optional => { | ||
| 322 | if (value == null) { | ||
| 323 | try self.serializeInt(@as(u1, @boolToInt(false))); | ||
| 324 | return; | ||
| 325 | } | ||
| 326 | try self.serializeInt(@as(u1, @boolToInt(true))); | ||
| 327 | |||
| 328 | const OC = comptime meta.Child(T); | ||
| 329 | const val_ptr = &value.?; | ||
| 330 | try self.serialize(val_ptr.*); | ||
| 331 | }, | ||
| 332 | .Enum => { | ||
| 333 | try self.serializeInt(@enumToInt(value)); | ||
| 334 | }, | ||
| 335 | else => @compileError("Cannot serialize " ++ @tagName(@typeInfo(T)) ++ " types (unimplemented)."), | ||
| 336 | } | ||
| 337 | } | ||
| 338 | }; | ||
| 339 | } | ||
| 340 | |||
| 341 | pub fn serializer( | ||
| 342 | comptime endian: builtin.Endian, | ||
| 343 | comptime packing: Packing, | ||
| 344 | out_stream: var, | ||
| 345 | ) Serializer(endian, packing, @TypeOf(out_stream)) { | ||
| 346 | return Serializer(endian, packing, @TypeOf(out_stream)).init(out_stream); | ||
| 347 | } | ||
| 348 | |||
| 349 | fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void { | ||
| 350 | @setEvalBranchQuota(1500); | ||
| 351 | //@NOTE: if this test is taking too long, reduce the maximum tested bitsize | ||
| 352 | const max_test_bitsize = 128; | ||
| 353 | |||
| 354 | const total_bytes = comptime blk: { | ||
| 355 | var bytes = 0; | ||
| 356 | comptime var i = 0; | ||
| 357 | while (i <= max_test_bitsize) : (i += 1) bytes += (i / 8) + @boolToInt(i % 8 > 0); | ||
| 358 | break :blk bytes * 2; | ||
| 359 | }; | ||
| 360 | |||
| 361 | var data_mem: [total_bytes]u8 = undefined; | ||
| 362 | var out = io.fixedBufferStream(&data_mem); | ||
| 363 | var serializer = serializer(endian, packing, out.outStream()); | ||
| 364 | |||
| 365 | var in = io.fixedBufferStream(&data_mem); | ||
| 366 | var deserializer = Deserializer(endian, packing, in.inStream()); | ||
| 367 | |||
| 368 | comptime var i = 0; | ||
| 369 | inline while (i <= max_test_bitsize) : (i += 1) { | ||
| 370 | const U = std.meta.IntType(false, i); | ||
| 371 | const S = std.meta.IntType(true, i); | ||
| 372 | try serializer.serializeInt(@as(U, i)); | ||
| 373 | if (i != 0) try serializer.serializeInt(@as(S, -1)) else try serializer.serialize(@as(S, 0)); | ||
| 374 | } | ||
| 375 | try serializer.flush(); | ||
| 376 | |||
| 377 | i = 0; | ||
| 378 | inline while (i <= max_test_bitsize) : (i += 1) { | ||
| 379 | const U = std.meta.IntType(false, i); | ||
| 380 | const S = std.meta.IntType(true, i); | ||
| 381 | const x = try deserializer.deserializeInt(U); | ||
| 382 | const y = try deserializer.deserializeInt(S); | ||
| 383 | expect(x == @as(U, i)); | ||
| 384 | if (i != 0) expect(y == @as(S, -1)) else expect(y == 0); | ||
| 385 | } | ||
| 386 | |||
| 387 | const u8_bit_count = comptime meta.bitCount(u8); | ||
| 388 | //0 + 1 + 2 + ... n = (n * (n + 1)) / 2 | ||
| 389 | //and we have each for unsigned and signed, so * 2 | ||
| 390 | const total_bits = (max_test_bitsize * (max_test_bitsize + 1)); | ||
| 391 | const extra_packed_byte = @boolToInt(total_bits % u8_bit_count > 0); | ||
| 392 | const total_packed_bytes = (total_bits / u8_bit_count) + extra_packed_byte; | ||
| 393 | |||
| 394 | expect(in.pos == if (packing == .Bit) total_packed_bytes else total_bytes); | ||
| 395 | |||
| 396 | //Verify that empty error set works with serializer. | ||
| 397 | //deserializer is covered by FixedBufferStream | ||
| 398 | var null_serializer = io.serializer(endian, packing, std.io.null_out_stream); | ||
| 399 | try null_serializer.serialize(data_mem[0..]); | ||
| 400 | try null_serializer.flush(); | ||
| 401 | } | ||
| 402 | |||
| 403 | test "Serializer/Deserializer Int" { | ||
| 404 | try testIntSerializerDeserializer(.Big, .Byte); | ||
| 405 | try testIntSerializerDeserializer(.Little, .Byte); | ||
| 406 | // TODO these tests are disabled due to tripping an LLVM assertion | ||
| 407 | // https://github.com/ziglang/zig/issues/2019 | ||
| 408 | //try testIntSerializerDeserializer(builtin.Endian.Big, true); | ||
| 409 | //try testIntSerializerDeserializer(builtin.Endian.Little, true); | ||
| 410 | } | ||
| 411 | |||
| 412 | fn testIntSerializerDeserializerInfNaN( | ||
| 413 | comptime endian: builtin.Endian, | ||
| 414 | comptime packing: io.Packing, | ||
| 415 | ) !void { | ||
| 416 | const mem_size = (16 * 2 + 32 * 2 + 64 * 2 + 128 * 2) / comptime meta.bitCount(u8); | ||
| 417 | var data_mem: [mem_size]u8 = undefined; | ||
| 418 | |||
| 419 | var out = io.fixedBufferStream(&data_mem); | ||
| 420 | var serializer = serializer(endian, packing, out.outStream()); | ||
| 421 | |||
| 422 | var in = io.fixedBufferStream(&data_mem); | ||
| 423 | var deserializer = deserializer(endian, packing, in.inStream()); | ||
| 424 | |||
| 425 | //@TODO: isInf/isNan not currently implemented for f128. | ||
| 426 | try serializer.serialize(std.math.nan(f16)); | ||
| 427 | try serializer.serialize(std.math.inf(f16)); | ||
| 428 | try serializer.serialize(std.math.nan(f32)); | ||
| 429 | try serializer.serialize(std.math.inf(f32)); | ||
| 430 | try serializer.serialize(std.math.nan(f64)); | ||
| 431 | try serializer.serialize(std.math.inf(f64)); | ||
| 432 | //try serializer.serialize(std.math.nan(f128)); | ||
| 433 | //try serializer.serialize(std.math.inf(f128)); | ||
| 434 | const nan_check_f16 = try deserializer.deserialize(f16); | ||
| 435 | const inf_check_f16 = try deserializer.deserialize(f16); | ||
| 436 | const nan_check_f32 = try deserializer.deserialize(f32); | ||
| 437 | deserializer.alignToByte(); | ||
| 438 | const inf_check_f32 = try deserializer.deserialize(f32); | ||
| 439 | const nan_check_f64 = try deserializer.deserialize(f64); | ||
| 440 | const inf_check_f64 = try deserializer.deserialize(f64); | ||
| 441 | //const nan_check_f128 = try deserializer.deserialize(f128); | ||
| 442 | //const inf_check_f128 = try deserializer.deserialize(f128); | ||
| 443 | expect(std.math.isNan(nan_check_f16)); | ||
| 444 | expect(std.math.isInf(inf_check_f16)); | ||
| 445 | expect(std.math.isNan(nan_check_f32)); | ||
| 446 | expect(std.math.isInf(inf_check_f32)); | ||
| 447 | expect(std.math.isNan(nan_check_f64)); | ||
| 448 | expect(std.math.isInf(inf_check_f64)); | ||
| 449 | //expect(std.math.isNan(nan_check_f128)); | ||
| 450 | //expect(std.math.isInf(inf_check_f128)); | ||
| 451 | } | ||
| 452 | |||
| 453 | test "Serializer/Deserializer Int: Inf/NaN" { | ||
| 454 | try testIntSerializerDeserializerInfNaN(.Big, .Byte); | ||
| 455 | try testIntSerializerDeserializerInfNaN(.Little, .Byte); | ||
| 456 | try testIntSerializerDeserializerInfNaN(.Big, .Bit); | ||
| 457 | try testIntSerializerDeserializerInfNaN(.Little, .Bit); | ||
| 458 | } | ||
| 459 | |||
| 460 | fn testAlternateSerializer(self: var, serializer: var) !void { | ||
| 461 | try serializer.serialize(self.f_f16); | ||
| 462 | } | ||
| 463 | |||
| 464 | fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void { | ||
| 465 | const ColorType = enum(u4) { | ||
| 466 | RGB8 = 1, | ||
| 467 | RA16 = 2, | ||
| 468 | R32 = 3, | ||
| 469 | }; | ||
| 470 | |||
| 471 | const TagAlign = union(enum(u32)) { | ||
| 472 | A: u8, | ||
| 473 | B: u8, | ||
| 474 | C: u8, | ||
| 475 | }; | ||
| 476 | |||
| 477 | const Color = union(ColorType) { | ||
| 478 | RGB8: struct { | ||
| 479 | r: u8, | ||
| 480 | g: u8, | ||
| 481 | b: u8, | ||
| 482 | a: u8, | ||
| 483 | }, | ||
| 484 | RA16: struct { | ||
| 485 | r: u16, | ||
| 486 | a: u16, | ||
| 487 | }, | ||
| 488 | R32: u32, | ||
| 489 | }; | ||
| 490 | |||
| 491 | const PackedStruct = packed struct { | ||
| 492 | f_i3: i3, | ||
| 493 | f_u2: u2, | ||
| 494 | }; | ||
| 495 | |||
| 496 | //to test custom serialization | ||
| 497 | const Custom = struct { | ||
| 498 | f_f16: f16, | ||
| 499 | f_unused_u32: u32, | ||
| 500 | |||
| 501 | pub fn deserialize(self: *@This(), deserializer: var) !void { | ||
| 502 | try deserializer.deserializeInto(&self.f_f16); | ||
| 503 | self.f_unused_u32 = 47; | ||
| 504 | } | ||
| 505 | |||
| 506 | pub const serialize = testAlternateSerializer; | ||
| 507 | }; | ||
| 508 | |||
| 509 | const MyStruct = struct { | ||
| 510 | f_i3: i3, | ||
| 511 | f_u8: u8, | ||
| 512 | f_tag_align: TagAlign, | ||
| 513 | f_u24: u24, | ||
| 514 | f_i19: i19, | ||
| 515 | f_void: void, | ||
| 516 | f_f32: f32, | ||
| 517 | f_f128: f128, | ||
| 518 | f_packed_0: PackedStruct, | ||
| 519 | f_i7arr: [10]i7, | ||
| 520 | f_of64n: ?f64, | ||
| 521 | f_of64v: ?f64, | ||
| 522 | f_color_type: ColorType, | ||
| 523 | f_packed_1: PackedStruct, | ||
| 524 | f_custom: Custom, | ||
| 525 | f_color: Color, | ||
| 526 | }; | ||
| 527 | |||
| 528 | const my_inst = MyStruct{ | ||
| 529 | .f_i3 = -1, | ||
| 530 | .f_u8 = 8, | ||
| 531 | .f_tag_align = TagAlign{ .B = 148 }, | ||
| 532 | .f_u24 = 24, | ||
| 533 | .f_i19 = 19, | ||
| 534 | .f_void = {}, | ||
| 535 | .f_f32 = 32.32, | ||
| 536 | .f_f128 = 128.128, | ||
| 537 | .f_packed_0 = PackedStruct{ .f_i3 = -1, .f_u2 = 2 }, | ||
| 538 | .f_i7arr = [10]i7{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, | ||
| 539 | .f_of64n = null, | ||
| 540 | .f_of64v = 64.64, | ||
| 541 | .f_color_type = ColorType.R32, | ||
| 542 | .f_packed_1 = PackedStruct{ .f_i3 = 1, .f_u2 = 1 }, | ||
| 543 | .f_custom = Custom{ .f_f16 = 38.63, .f_unused_u32 = 47 }, | ||
| 544 | .f_color = Color{ .R32 = 123822 }, | ||
| 545 | }; | ||
| 546 | |||
| 547 | var data_mem: [@sizeOf(MyStruct)]u8 = undefined; | ||
| 548 | var out = io.fixedBufferStream(&data_mem); | ||
| 549 | var serializer = serializer(endian, packing, out.outStream()); | ||
| 550 | |||
| 551 | var in = io.fixedBufferStream(&data_mem); | ||
| 552 | var deserializer = deserializer(endian, packing, in.inStream()); | ||
| 553 | |||
| 554 | try serializer.serialize(my_inst); | ||
| 555 | |||
| 556 | const my_copy = try deserializer.deserialize(MyStruct); | ||
| 557 | expect(meta.eql(my_copy, my_inst)); | ||
| 558 | } | ||
| 559 | |||
| 560 | test "Serializer/Deserializer generic" { | ||
| 561 | if (std.Target.current.os.tag == .windows) { | ||
| 562 | // TODO https://github.com/ziglang/zig/issues/508 | ||
| 563 | return error.SkipZigTest; | ||
| 564 | } | ||
| 565 | try testSerializerDeserializer(builtin.Endian.Big, .Byte); | ||
| 566 | try testSerializerDeserializer(builtin.Endian.Little, .Byte); | ||
| 567 | try testSerializerDeserializer(builtin.Endian.Big, .Bit); | ||
| 568 | try testSerializerDeserializer(builtin.Endian.Little, .Bit); | ||
| 569 | } | ||
| 570 | |||
| 571 | fn testBadData(comptime endian: builtin.Endian, comptime packing: io.Packing) !void { | ||
| 572 | const E = enum(u14) { | ||
| 573 | One = 1, | ||
| 574 | Two = 2, | ||
| 575 | }; | ||
| 576 | |||
| 577 | const A = struct { | ||
| 578 | e: E, | ||
| 579 | }; | ||
| 580 | |||
| 581 | const C = union(E) { | ||
| 582 | One: u14, | ||
| 583 | Two: f16, | ||
| 584 | }; | ||
| 585 | |||
| 586 | var data_mem: [4]u8 = undefined; | ||
| 587 | var out = io.fixedBufferStream.init(&data_mem); | ||
| 588 | var serializer = serializer(endian, packing, out.outStream()); | ||
| 589 | |||
| 590 | var in = io.fixedBufferStream(&data_mem); | ||
| 591 | var deserializer = deserializer(endian, packing, in.inStream()); | ||
| 592 | |||
| 593 | try serializer.serialize(@as(u14, 3)); | ||
| 594 | expectError(error.InvalidEnumTag, deserializer.deserialize(A)); | ||
| 595 | out.pos = 0; | ||
| 596 | try serializer.serialize(@as(u14, 3)); | ||
| 597 | try serializer.serialize(@as(u14, 88)); | ||
| 598 | expectError(error.InvalidEnumTag, deserializer.deserialize(C)); | ||
| 599 | } | ||
| 600 | |||
| 601 | test "Deserializer bad data" { | ||
| 602 | try testBadData(.Big, .Byte); | ||
| 603 | try testBadData(.Little, .Byte); | ||
| 604 | try testBadData(.Big, .Bit); | ||
| 605 | try testBadData(.Little, .Bit); | ||
| 606 | } | ||
lib/std/io/test.zig+13-521| ... | @@ -22,11 +22,10 @@ test "write a file, read it, then delete it" { | ... | @@ -22,11 +22,10 @@ test "write a file, read it, then delete it" { |
| 22 | var file = try cwd.createFile(tmp_file_name, .{}); | 22 | var file = try cwd.createFile(tmp_file_name, .{}); |
| 23 | defer file.close(); | 23 | defer file.close(); |
| 24 | 24 | ||
| 25 | var file_out_stream = file.outStream(); | 25 | var buf_stream = io.bufferedOutStream(file.outStream()); |
| 26 | var buf_stream = io.BufferedOutStream(File.WriteError).init(&file_out_stream.stream); | 26 | const st = buf_stream.outStream(); |
| 27 | const st = &buf_stream.stream; | ||
| 28 | try st.print("begin", .{}); | 27 | try st.print("begin", .{}); |
| 29 | try st.write(data[0..]); | 28 | try st.writeAll(data[0..]); |
| 30 | try st.print("end", .{}); | 29 | try st.print("end", .{}); |
| 31 | try buf_stream.flush(); | 30 | try buf_stream.flush(); |
| 32 | } | 31 | } |
| ... | @@ -48,9 +47,8 @@ test "write a file, read it, then delete it" { | ... | @@ -48,9 +47,8 @@ test "write a file, read it, then delete it" { |
| 48 | const expected_file_size: u64 = "begin".len + data.len + "end".len; | 47 | const expected_file_size: u64 = "begin".len + data.len + "end".len; |
| 49 | expectEqual(expected_file_size, file_size); | 48 | expectEqual(expected_file_size, file_size); |
| 50 | 49 | ||
| 51 | var file_in_stream = file.inStream(); | 50 | var buf_stream = io.bufferedInStream(file.inStream()); |
| 52 | var buf_stream = io.BufferedInStream(File.ReadError).init(&file_in_stream.stream); | 51 | const st = buf_stream.inStream(); |
| 53 | const st = &buf_stream.stream; | ||
| 54 | const contents = try st.readAllAlloc(std.testing.allocator, 2 * 1024); | 52 | const contents = try st.readAllAlloc(std.testing.allocator, 2 * 1024); |
| 55 | defer std.testing.allocator.free(contents); | 53 | defer std.testing.allocator.free(contents); |
| 56 | 54 | ||
| ... | @@ -61,224 +59,13 @@ test "write a file, read it, then delete it" { | ... | @@ -61,224 +59,13 @@ test "write a file, read it, then delete it" { |
| 61 | try cwd.deleteFile(tmp_file_name); | 59 | try cwd.deleteFile(tmp_file_name); |
| 62 | } | 60 | } |
| 63 | 61 | ||
| 64 | test "BufferOutStream" { | ||
| 65 | var buffer = try std.Buffer.initSize(std.testing.allocator, 0); | ||
| 66 | defer buffer.deinit(); | ||
| 67 | var buf_stream = &std.io.BufferOutStream.init(&buffer).stream; | ||
| 68 | |||
| 69 | const x: i32 = 42; | ||
| 70 | const y: i32 = 1234; | ||
| 71 | try buf_stream.print("x: {}\ny: {}\n", .{ x, y }); | ||
| 72 | |||
| 73 | expect(mem.eql(u8, buffer.toSlice(), "x: 42\ny: 1234\n")); | ||
| 74 | } | ||
| 75 | |||
| 76 | test "SliceInStream" { | ||
| 77 | const bytes = [_]u8{ 1, 2, 3, 4, 5, 6, 7 }; | ||
| 78 | var ss = io.SliceInStream.init(&bytes); | ||
| 79 | |||
| 80 | var dest: [4]u8 = undefined; | ||
| 81 | |||
| 82 | var read = try ss.stream.read(dest[0..4]); | ||
| 83 | expect(read == 4); | ||
| 84 | expect(mem.eql(u8, dest[0..4], bytes[0..4])); | ||
| 85 | |||
| 86 | read = try ss.stream.read(dest[0..4]); | ||
| 87 | expect(read == 3); | ||
| 88 | expect(mem.eql(u8, dest[0..3], bytes[4..7])); | ||
| 89 | |||
| 90 | read = try ss.stream.read(dest[0..4]); | ||
| 91 | expect(read == 0); | ||
| 92 | } | ||
| 93 | |||
| 94 | test "PeekStream" { | ||
| 95 | const bytes = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; | ||
| 96 | var ss = io.SliceInStream.init(&bytes); | ||
| 97 | var ps = io.PeekStream(.{ .Static = 2 }, io.SliceInStream.Error).init(&ss.stream); | ||
| 98 | |||
| 99 | var dest: [4]u8 = undefined; | ||
| 100 | |||
| 101 | try ps.putBackByte(9); | ||
| 102 | try ps.putBackByte(10); | ||
| 103 | |||
| 104 | var read = try ps.stream.read(dest[0..4]); | ||
| 105 | expect(read == 4); | ||
| 106 | expect(dest[0] == 10); | ||
| 107 | expect(dest[1] == 9); | ||
| 108 | expect(mem.eql(u8, dest[2..4], bytes[0..2])); | ||
| 109 | |||
| 110 | read = try ps.stream.read(dest[0..4]); | ||
| 111 | expect(read == 4); | ||
| 112 | expect(mem.eql(u8, dest[0..4], bytes[2..6])); | ||
| 113 | |||
| 114 | read = try ps.stream.read(dest[0..4]); | ||
| 115 | expect(read == 2); | ||
| 116 | expect(mem.eql(u8, dest[0..2], bytes[6..8])); | ||
| 117 | |||
| 118 | try ps.putBackByte(11); | ||
| 119 | try ps.putBackByte(12); | ||
| 120 | |||
| 121 | read = try ps.stream.read(dest[0..4]); | ||
| 122 | expect(read == 2); | ||
| 123 | expect(dest[0] == 12); | ||
| 124 | expect(dest[1] == 11); | ||
| 125 | } | ||
| 126 | |||
| 127 | test "SliceOutStream" { | ||
| 128 | var buffer: [10]u8 = undefined; | ||
| 129 | var ss = io.SliceOutStream.init(buffer[0..]); | ||
| 130 | |||
| 131 | try ss.stream.write("Hello"); | ||
| 132 | expect(mem.eql(u8, ss.getWritten(), "Hello")); | ||
| 133 | |||
| 134 | try ss.stream.write("world"); | ||
| 135 | expect(mem.eql(u8, ss.getWritten(), "Helloworld")); | ||
| 136 | |||
| 137 | expectError(error.OutOfMemory, ss.stream.write("!")); | ||
| 138 | expect(mem.eql(u8, ss.getWritten(), "Helloworld")); | ||
| 139 | |||
| 140 | ss.reset(); | ||
| 141 | expect(ss.getWritten().len == 0); | ||
| 142 | |||
| 143 | expectError(error.OutOfMemory, ss.stream.write("Hello world!")); | ||
| 144 | expect(mem.eql(u8, ss.getWritten(), "Hello worl")); | ||
| 145 | } | ||
| 146 | |||
| 147 | test "BitInStream" { | ||
| 148 | const mem_be = [_]u8{ 0b11001101, 0b00001011 }; | ||
| 149 | const mem_le = [_]u8{ 0b00011101, 0b10010101 }; | ||
| 150 | |||
| 151 | var mem_in_be = io.SliceInStream.init(mem_be[0..]); | ||
| 152 | const InError = io.SliceInStream.Error; | ||
| 153 | var bit_stream_be = io.BitInStream(builtin.Endian.Big, InError).init(&mem_in_be.stream); | ||
| 154 | |||
| 155 | var out_bits: usize = undefined; | ||
| 156 | |||
| 157 | expect(1 == try bit_stream_be.readBits(u2, 1, &out_bits)); | ||
| 158 | expect(out_bits == 1); | ||
| 159 | expect(2 == try bit_stream_be.readBits(u5, 2, &out_bits)); | ||
| 160 | expect(out_bits == 2); | ||
| 161 | expect(3 == try bit_stream_be.readBits(u128, 3, &out_bits)); | ||
| 162 | expect(out_bits == 3); | ||
| 163 | expect(4 == try bit_stream_be.readBits(u8, 4, &out_bits)); | ||
| 164 | expect(out_bits == 4); | ||
| 165 | expect(5 == try bit_stream_be.readBits(u9, 5, &out_bits)); | ||
| 166 | expect(out_bits == 5); | ||
| 167 | expect(1 == try bit_stream_be.readBits(u1, 1, &out_bits)); | ||
| 168 | expect(out_bits == 1); | ||
| 169 | |||
| 170 | mem_in_be.pos = 0; | ||
| 171 | bit_stream_be.bit_count = 0; | ||
| 172 | expect(0b110011010000101 == try bit_stream_be.readBits(u15, 15, &out_bits)); | ||
| 173 | expect(out_bits == 15); | ||
| 174 | |||
| 175 | mem_in_be.pos = 0; | ||
| 176 | bit_stream_be.bit_count = 0; | ||
| 177 | expect(0b1100110100001011 == try bit_stream_be.readBits(u16, 16, &out_bits)); | ||
| 178 | expect(out_bits == 16); | ||
| 179 | |||
| 180 | _ = try bit_stream_be.readBits(u0, 0, &out_bits); | ||
| 181 | |||
| 182 | expect(0 == try bit_stream_be.readBits(u1, 1, &out_bits)); | ||
| 183 | expect(out_bits == 0); | ||
| 184 | expectError(error.EndOfStream, bit_stream_be.readBitsNoEof(u1, 1)); | ||
| 185 | |||
| 186 | var mem_in_le = io.SliceInStream.init(mem_le[0..]); | ||
| 187 | var bit_stream_le = io.BitInStream(builtin.Endian.Little, InError).init(&mem_in_le.stream); | ||
| 188 | |||
| 189 | expect(1 == try bit_stream_le.readBits(u2, 1, &out_bits)); | ||
| 190 | expect(out_bits == 1); | ||
| 191 | expect(2 == try bit_stream_le.readBits(u5, 2, &out_bits)); | ||
| 192 | expect(out_bits == 2); | ||
| 193 | expect(3 == try bit_stream_le.readBits(u128, 3, &out_bits)); | ||
| 194 | expect(out_bits == 3); | ||
| 195 | expect(4 == try bit_stream_le.readBits(u8, 4, &out_bits)); | ||
| 196 | expect(out_bits == 4); | ||
| 197 | expect(5 == try bit_stream_le.readBits(u9, 5, &out_bits)); | ||
| 198 | expect(out_bits == 5); | ||
| 199 | expect(1 == try bit_stream_le.readBits(u1, 1, &out_bits)); | ||
| 200 | expect(out_bits == 1); | ||
| 201 | |||
| 202 | mem_in_le.pos = 0; | ||
| 203 | bit_stream_le.bit_count = 0; | ||
| 204 | expect(0b001010100011101 == try bit_stream_le.readBits(u15, 15, &out_bits)); | ||
| 205 | expect(out_bits == 15); | ||
| 206 | |||
| 207 | mem_in_le.pos = 0; | ||
| 208 | bit_stream_le.bit_count = 0; | ||
| 209 | expect(0b1001010100011101 == try bit_stream_le.readBits(u16, 16, &out_bits)); | ||
| 210 | expect(out_bits == 16); | ||
| 211 | |||
| 212 | _ = try bit_stream_le.readBits(u0, 0, &out_bits); | ||
| 213 | |||
| 214 | expect(0 == try bit_stream_le.readBits(u1, 1, &out_bits)); | ||
| 215 | expect(out_bits == 0); | ||
| 216 | expectError(error.EndOfStream, bit_stream_le.readBitsNoEof(u1, 1)); | ||
| 217 | } | ||
| 218 | |||
| 219 | test "BitOutStream" { | ||
| 220 | var mem_be = [_]u8{0} ** 2; | ||
| 221 | var mem_le = [_]u8{0} ** 2; | ||
| 222 | |||
| 223 | var mem_out_be = io.SliceOutStream.init(mem_be[0..]); | ||
| 224 | const OutError = io.SliceOutStream.Error; | ||
| 225 | var bit_stream_be = io.BitOutStream(builtin.Endian.Big, OutError).init(&mem_out_be.stream); | ||
| 226 | |||
| 227 | try bit_stream_be.writeBits(@as(u2, 1), 1); | ||
| 228 | try bit_stream_be.writeBits(@as(u5, 2), 2); | ||
| 229 | try bit_stream_be.writeBits(@as(u128, 3), 3); | ||
| 230 | try bit_stream_be.writeBits(@as(u8, 4), 4); | ||
| 231 | try bit_stream_be.writeBits(@as(u9, 5), 5); | ||
| 232 | try bit_stream_be.writeBits(@as(u1, 1), 1); | ||
| 233 | |||
| 234 | expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001011); | ||
| 235 | |||
| 236 | mem_out_be.pos = 0; | ||
| 237 | |||
| 238 | try bit_stream_be.writeBits(@as(u15, 0b110011010000101), 15); | ||
| 239 | try bit_stream_be.flushBits(); | ||
| 240 | expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001010); | ||
| 241 | |||
| 242 | mem_out_be.pos = 0; | ||
| 243 | try bit_stream_be.writeBits(@as(u32, 0b110011010000101), 16); | ||
| 244 | expect(mem_be[0] == 0b01100110 and mem_be[1] == 0b10000101); | ||
| 245 | |||
| 246 | try bit_stream_be.writeBits(@as(u0, 0), 0); | ||
| 247 | |||
| 248 | var mem_out_le = io.SliceOutStream.init(mem_le[0..]); | ||
| 249 | var bit_stream_le = io.BitOutStream(builtin.Endian.Little, OutError).init(&mem_out_le.stream); | ||
| 250 | |||
| 251 | try bit_stream_le.writeBits(@as(u2, 1), 1); | ||
| 252 | try bit_stream_le.writeBits(@as(u5, 2), 2); | ||
| 253 | try bit_stream_le.writeBits(@as(u128, 3), 3); | ||
| 254 | try bit_stream_le.writeBits(@as(u8, 4), 4); | ||
| 255 | try bit_stream_le.writeBits(@as(u9, 5), 5); | ||
| 256 | try bit_stream_le.writeBits(@as(u1, 1), 1); | ||
| 257 | |||
| 258 | expect(mem_le[0] == 0b00011101 and mem_le[1] == 0b10010101); | ||
| 259 | |||
| 260 | mem_out_le.pos = 0; | ||
| 261 | try bit_stream_le.writeBits(@as(u15, 0b110011010000101), 15); | ||
| 262 | try bit_stream_le.flushBits(); | ||
| 263 | expect(mem_le[0] == 0b10000101 and mem_le[1] == 0b01100110); | ||
| 264 | |||
| 265 | mem_out_le.pos = 0; | ||
| 266 | try bit_stream_le.writeBits(@as(u32, 0b1100110100001011), 16); | ||
| 267 | expect(mem_le[0] == 0b00001011 and mem_le[1] == 0b11001101); | ||
| 268 | |||
| 269 | try bit_stream_le.writeBits(@as(u0, 0), 0); | ||
| 270 | } | ||
| 271 | |||
| 272 | test "BitStreams with File Stream" { | 62 | test "BitStreams with File Stream" { |
| 273 | const tmp_file_name = "temp_test_file.txt"; | 63 | const tmp_file_name = "temp_test_file.txt"; |
| 274 | { | 64 | { |
| 275 | var file = try fs.cwd().createFile(tmp_file_name, .{}); | 65 | var file = try fs.cwd().createFile(tmp_file_name, .{}); |
| 276 | defer file.close(); | 66 | defer file.close(); |
| 277 | 67 | ||
| 278 | var file_out = file.outStream(); | 68 | var bit_stream = io.bitOutStream(builtin.endian, file.outStream()); |
| 279 | var file_out_stream = &file_out.stream; | ||
| 280 | const OutError = File.WriteError; | ||
| 281 | var bit_stream = io.BitOutStream(builtin.endian, OutError).init(file_out_stream); | ||
| 282 | 69 | ||
| 283 | try bit_stream.writeBits(@as(u2, 1), 1); | 70 | try bit_stream.writeBits(@as(u2, 1), 1); |
| 284 | try bit_stream.writeBits(@as(u5, 2), 2); | 71 | try bit_stream.writeBits(@as(u5, 2), 2); |
| ... | @@ -292,10 +79,7 @@ test "BitStreams with File Stream" { | ... | @@ -292,10 +79,7 @@ test "BitStreams with File Stream" { |
| 292 | var file = try fs.cwd().openFile(tmp_file_name, .{}); | 79 | var file = try fs.cwd().openFile(tmp_file_name, .{}); |
| 293 | defer file.close(); | 80 | defer file.close(); |
| 294 | 81 | ||
| 295 | var file_in = file.inStream(); | 82 | var bit_stream = io.bitInStream(builtin.endian, file.inStream()); |
| 296 | var file_in_stream = &file_in.stream; | ||
| 297 | const InError = File.ReadError; | ||
| 298 | var bit_stream = io.BitInStream(builtin.endian, InError).init(file_in_stream); | ||
| 299 | 83 | ||
| 300 | var out_bits: usize = undefined; | 84 | var out_bits: usize = undefined; |
| 301 | 85 | ||
| ... | @@ -317,298 +101,6 @@ test "BitStreams with File Stream" { | ... | @@ -317,298 +101,6 @@ test "BitStreams with File Stream" { |
| 317 | try fs.cwd().deleteFile(tmp_file_name); | 101 | try fs.cwd().deleteFile(tmp_file_name); |
| 318 | } | 102 | } |
| 319 | 103 | ||
| 320 | fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void { | ||
| 321 | @setEvalBranchQuota(1500); | ||
| 322 | //@NOTE: if this test is taking too long, reduce the maximum tested bitsize | ||
| 323 | const max_test_bitsize = 128; | ||
| 324 | |||
| 325 | const total_bytes = comptime blk: { | ||
| 326 | var bytes = 0; | ||
| 327 | comptime var i = 0; | ||
| 328 | while (i <= max_test_bitsize) : (i += 1) bytes += (i / 8) + @boolToInt(i % 8 > 0); | ||
| 329 | break :blk bytes * 2; | ||
| 330 | }; | ||
| 331 | |||
| 332 | var data_mem: [total_bytes]u8 = undefined; | ||
| 333 | var out = io.SliceOutStream.init(data_mem[0..]); | ||
| 334 | const OutError = io.SliceOutStream.Error; | ||
| 335 | var out_stream = &out.stream; | ||
| 336 | var serializer = io.Serializer(endian, packing, OutError).init(out_stream); | ||
| 337 | |||
| 338 | var in = io.SliceInStream.init(data_mem[0..]); | ||
| 339 | const InError = io.SliceInStream.Error; | ||
| 340 | var in_stream = &in.stream; | ||
| 341 | var deserializer = io.Deserializer(endian, packing, InError).init(in_stream); | ||
| 342 | |||
| 343 | comptime var i = 0; | ||
| 344 | inline while (i <= max_test_bitsize) : (i += 1) { | ||
| 345 | const U = std.meta.IntType(false, i); | ||
| 346 | const S = std.meta.IntType(true, i); | ||
| 347 | try serializer.serializeInt(@as(U, i)); | ||
| 348 | if (i != 0) try serializer.serializeInt(@as(S, -1)) else try serializer.serialize(@as(S, 0)); | ||
| 349 | } | ||
| 350 | try serializer.flush(); | ||
| 351 | |||
| 352 | i = 0; | ||
| 353 | inline while (i <= max_test_bitsize) : (i += 1) { | ||
| 354 | const U = std.meta.IntType(false, i); | ||
| 355 | const S = std.meta.IntType(true, i); | ||
| 356 | const x = try deserializer.deserializeInt(U); | ||
| 357 | const y = try deserializer.deserializeInt(S); | ||
| 358 | expect(x == @as(U, i)); | ||
| 359 | if (i != 0) expect(y == @as(S, -1)) else expect(y == 0); | ||
| 360 | } | ||
| 361 | |||
| 362 | const u8_bit_count = comptime meta.bitCount(u8); | ||
| 363 | //0 + 1 + 2 + ... n = (n * (n + 1)) / 2 | ||
| 364 | //and we have each for unsigned and signed, so * 2 | ||
| 365 | const total_bits = (max_test_bitsize * (max_test_bitsize + 1)); | ||
| 366 | const extra_packed_byte = @boolToInt(total_bits % u8_bit_count > 0); | ||
| 367 | const total_packed_bytes = (total_bits / u8_bit_count) + extra_packed_byte; | ||
| 368 | |||
| 369 | expect(in.pos == if (packing == .Bit) total_packed_bytes else total_bytes); | ||
| 370 | |||
| 371 | //Verify that empty error set works with serializer. | ||
| 372 | //deserializer is covered by SliceInStream | ||
| 373 | const NullError = io.NullOutStream.Error; | ||
| 374 | var null_out = io.NullOutStream.init(); | ||
| 375 | var null_out_stream = &null_out.stream; | ||
| 376 | var null_serializer = io.Serializer(endian, packing, NullError).init(null_out_stream); | ||
| 377 | try null_serializer.serialize(data_mem[0..]); | ||
| 378 | try null_serializer.flush(); | ||
| 379 | } | ||
| 380 | |||
| 381 | test "Serializer/Deserializer Int" { | ||
| 382 | try testIntSerializerDeserializer(.Big, .Byte); | ||
| 383 | try testIntSerializerDeserializer(.Little, .Byte); | ||
| 384 | // TODO these tests are disabled due to tripping an LLVM assertion | ||
| 385 | // https://github.com/ziglang/zig/issues/2019 | ||
| 386 | //try testIntSerializerDeserializer(builtin.Endian.Big, true); | ||
| 387 | //try testIntSerializerDeserializer(builtin.Endian.Little, true); | ||
| 388 | } | ||
| 389 | |||
| 390 | fn testIntSerializerDeserializerInfNaN( | ||
| 391 | comptime endian: builtin.Endian, | ||
| 392 | comptime packing: io.Packing, | ||
| 393 | ) !void { | ||
| 394 | const mem_size = (16 * 2 + 32 * 2 + 64 * 2 + 128 * 2) / comptime meta.bitCount(u8); | ||
| 395 | var data_mem: [mem_size]u8 = undefined; | ||
| 396 | |||
| 397 | var out = io.SliceOutStream.init(data_mem[0..]); | ||
| 398 | const OutError = io.SliceOutStream.Error; | ||
| 399 | var out_stream = &out.stream; | ||
| 400 | var serializer = io.Serializer(endian, packing, OutError).init(out_stream); | ||
| 401 | |||
| 402 | var in = io.SliceInStream.init(data_mem[0..]); | ||
| 403 | const InError = io.SliceInStream.Error; | ||
| 404 | var in_stream = &in.stream; | ||
| 405 | var deserializer = io.Deserializer(endian, packing, InError).init(in_stream); | ||
| 406 | |||
| 407 | //@TODO: isInf/isNan not currently implemented for f128. | ||
| 408 | try serializer.serialize(std.math.nan(f16)); | ||
| 409 | try serializer.serialize(std.math.inf(f16)); | ||
| 410 | try serializer.serialize(std.math.nan(f32)); | ||
| 411 | try serializer.serialize(std.math.inf(f32)); | ||
| 412 | try serializer.serialize(std.math.nan(f64)); | ||
| 413 | try serializer.serialize(std.math.inf(f64)); | ||
| 414 | //try serializer.serialize(std.math.nan(f128)); | ||
| 415 | //try serializer.serialize(std.math.inf(f128)); | ||
| 416 | const nan_check_f16 = try deserializer.deserialize(f16); | ||
| 417 | const inf_check_f16 = try deserializer.deserialize(f16); | ||
| 418 | const nan_check_f32 = try deserializer.deserialize(f32); | ||
| 419 | deserializer.alignToByte(); | ||
| 420 | const inf_check_f32 = try deserializer.deserialize(f32); | ||
| 421 | const nan_check_f64 = try deserializer.deserialize(f64); | ||
| 422 | const inf_check_f64 = try deserializer.deserialize(f64); | ||
| 423 | //const nan_check_f128 = try deserializer.deserialize(f128); | ||
| 424 | //const inf_check_f128 = try deserializer.deserialize(f128); | ||
| 425 | expect(std.math.isNan(nan_check_f16)); | ||
| 426 | expect(std.math.isInf(inf_check_f16)); | ||
| 427 | expect(std.math.isNan(nan_check_f32)); | ||
| 428 | expect(std.math.isInf(inf_check_f32)); | ||
| 429 | expect(std.math.isNan(nan_check_f64)); | ||
| 430 | expect(std.math.isInf(inf_check_f64)); | ||
| 431 | //expect(std.math.isNan(nan_check_f128)); | ||
| 432 | //expect(std.math.isInf(inf_check_f128)); | ||
| 433 | } | ||
| 434 | |||
| 435 | test "Serializer/Deserializer Int: Inf/NaN" { | ||
| 436 | try testIntSerializerDeserializerInfNaN(.Big, .Byte); | ||
| 437 | try testIntSerializerDeserializerInfNaN(.Little, .Byte); | ||
| 438 | try testIntSerializerDeserializerInfNaN(.Big, .Bit); | ||
| 439 | try testIntSerializerDeserializerInfNaN(.Little, .Bit); | ||
| 440 | } | ||
| 441 | |||
| 442 | fn testAlternateSerializer(self: var, serializer: var) !void { | ||
| 443 | try serializer.serialize(self.f_f16); | ||
| 444 | } | ||
| 445 | |||
| 446 | fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void { | ||
| 447 | const ColorType = enum(u4) { | ||
| 448 | RGB8 = 1, | ||
| 449 | RA16 = 2, | ||
| 450 | R32 = 3, | ||
| 451 | }; | ||
| 452 | |||
| 453 | const TagAlign = union(enum(u32)) { | ||
| 454 | A: u8, | ||
| 455 | B: u8, | ||
| 456 | C: u8, | ||
| 457 | }; | ||
| 458 | |||
| 459 | const Color = union(ColorType) { | ||
| 460 | RGB8: struct { | ||
| 461 | r: u8, | ||
| 462 | g: u8, | ||
| 463 | b: u8, | ||
| 464 | a: u8, | ||
| 465 | }, | ||
| 466 | RA16: struct { | ||
| 467 | r: u16, | ||
| 468 | a: u16, | ||
| 469 | }, | ||
| 470 | R32: u32, | ||
| 471 | }; | ||
| 472 | |||
| 473 | const PackedStruct = packed struct { | ||
| 474 | f_i3: i3, | ||
| 475 | f_u2: u2, | ||
| 476 | }; | ||
| 477 | |||
| 478 | //to test custom serialization | ||
| 479 | const Custom = struct { | ||
| 480 | f_f16: f16, | ||
| 481 | f_unused_u32: u32, | ||
| 482 | |||
| 483 | pub fn deserialize(self: *@This(), deserializer: var) !void { | ||
| 484 | try deserializer.deserializeInto(&self.f_f16); | ||
| 485 | self.f_unused_u32 = 47; | ||
| 486 | } | ||
| 487 | |||
| 488 | pub const serialize = testAlternateSerializer; | ||
| 489 | }; | ||
| 490 | |||
| 491 | const MyStruct = struct { | ||
| 492 | f_i3: i3, | ||
| 493 | f_u8: u8, | ||
| 494 | f_tag_align: TagAlign, | ||
| 495 | f_u24: u24, | ||
| 496 | f_i19: i19, | ||
| 497 | f_void: void, | ||
| 498 | f_f32: f32, | ||
| 499 | f_f128: f128, | ||
| 500 | f_packed_0: PackedStruct, | ||
| 501 | f_i7arr: [10]i7, | ||
| 502 | f_of64n: ?f64, | ||
| 503 | f_of64v: ?f64, | ||
| 504 | f_color_type: ColorType, | ||
| 505 | f_packed_1: PackedStruct, | ||
| 506 | f_custom: Custom, | ||
| 507 | f_color: Color, | ||
| 508 | }; | ||
| 509 | |||
| 510 | const my_inst = MyStruct{ | ||
| 511 | .f_i3 = -1, | ||
| 512 | .f_u8 = 8, | ||
| 513 | .f_tag_align = TagAlign{ .B = 148 }, | ||
| 514 | .f_u24 = 24, | ||
| 515 | .f_i19 = 19, | ||
| 516 | .f_void = {}, | ||
| 517 | .f_f32 = 32.32, | ||
| 518 | .f_f128 = 128.128, | ||
| 519 | .f_packed_0 = PackedStruct{ .f_i3 = -1, .f_u2 = 2 }, | ||
| 520 | .f_i7arr = [10]i7{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, | ||
| 521 | .f_of64n = null, | ||
| 522 | .f_of64v = 64.64, | ||
| 523 | .f_color_type = ColorType.R32, | ||
| 524 | .f_packed_1 = PackedStruct{ .f_i3 = 1, .f_u2 = 1 }, | ||
| 525 | .f_custom = Custom{ .f_f16 = 38.63, .f_unused_u32 = 47 }, | ||
| 526 | .f_color = Color{ .R32 = 123822 }, | ||
| 527 | }; | ||
| 528 | |||
| 529 | var data_mem: [@sizeOf(MyStruct)]u8 = undefined; | ||
| 530 | var out = io.SliceOutStream.init(data_mem[0..]); | ||
| 531 | const OutError = io.SliceOutStream.Error; | ||
| 532 | var out_stream = &out.stream; | ||
| 533 | var serializer = io.Serializer(endian, packing, OutError).init(out_stream); | ||
| 534 | |||
| 535 | var in = io.SliceInStream.init(data_mem[0..]); | ||
| 536 | const InError = io.SliceInStream.Error; | ||
| 537 | var in_stream = &in.stream; | ||
| 538 | var deserializer = io.Deserializer(endian, packing, InError).init(in_stream); | ||
| 539 | |||
| 540 | try serializer.serialize(my_inst); | ||
| 541 | |||
| 542 | const my_copy = try deserializer.deserialize(MyStruct); | ||
| 543 | expect(meta.eql(my_copy, my_inst)); | ||
| 544 | } | ||
| 545 | |||
| 546 | test "Serializer/Deserializer generic" { | ||
| 547 | if (std.Target.current.os.tag == .windows) { | ||
| 548 | // TODO https://github.com/ziglang/zig/issues/508 | ||
| 549 | return error.SkipZigTest; | ||
| 550 | } | ||
| 551 | try testSerializerDeserializer(builtin.Endian.Big, .Byte); | ||
| 552 | try testSerializerDeserializer(builtin.Endian.Little, .Byte); | ||
| 553 | try testSerializerDeserializer(builtin.Endian.Big, .Bit); | ||
| 554 | try testSerializerDeserializer(builtin.Endian.Little, .Bit); | ||
| 555 | } | ||
| 556 | |||
| 557 | fn testBadData(comptime endian: builtin.Endian, comptime packing: io.Packing) !void { | ||
| 558 | const E = enum(u14) { | ||
| 559 | One = 1, | ||
| 560 | Two = 2, | ||
| 561 | }; | ||
| 562 | |||
| 563 | const A = struct { | ||
| 564 | e: E, | ||
| 565 | }; | ||
| 566 | |||
| 567 | const C = union(E) { | ||
| 568 | One: u14, | ||
| 569 | Two: f16, | ||
| 570 | }; | ||
| 571 | |||
| 572 | var data_mem: [4]u8 = undefined; | ||
| 573 | var out = io.SliceOutStream.init(data_mem[0..]); | ||
| 574 | const OutError = io.SliceOutStream.Error; | ||
| 575 | var out_stream = &out.stream; | ||
| 576 | var serializer = io.Serializer(endian, packing, OutError).init(out_stream); | ||
| 577 | |||
| 578 | var in = io.SliceInStream.init(data_mem[0..]); | ||
| 579 | const InError = io.SliceInStream.Error; | ||
| 580 | var in_stream = &in.stream; | ||
| 581 | var deserializer = io.Deserializer(endian, packing, InError).init(in_stream); | ||
| 582 | |||
| 583 | try serializer.serialize(@as(u14, 3)); | ||
| 584 | expectError(error.InvalidEnumTag, deserializer.deserialize(A)); | ||
| 585 | out.pos = 0; | ||
| 586 | try serializer.serialize(@as(u14, 3)); | ||
| 587 | try serializer.serialize(@as(u14, 88)); | ||
| 588 | expectError(error.InvalidEnumTag, deserializer.deserialize(C)); | ||
| 589 | } | ||
| 590 | |||
| 591 | test "Deserializer bad data" { | ||
| 592 | try testBadData(.Big, .Byte); | ||
| 593 | try testBadData(.Little, .Byte); | ||
| 594 | try testBadData(.Big, .Bit); | ||
| 595 | try testBadData(.Little, .Bit); | ||
| 596 | } | ||
| 597 | |||
| 598 | test "c out stream" { | ||
| 599 | if (!builtin.link_libc) return error.SkipZigTest; | ||
| 600 | |||
| 601 | const filename = "tmp_io_test_file.txt"; | ||
| 602 | const out_file = std.c.fopen(filename, "w") orelse return error.UnableToOpenTestFile; | ||
| 603 | defer { | ||
| 604 | _ = std.c.fclose(out_file); | ||
| 605 | fs.cwd().deleteFileC(filename) catch {}; | ||
| 606 | } | ||
| 607 | |||
| 608 | const out_stream = &io.COutStream.init(out_file).stream; | ||
| 609 | try out_stream.print("hi: {}\n", .{@as(i32, 123)}); | ||
| 610 | } | ||
| 611 | |||
| 612 | test "File seek ops" { | 104 | test "File seek ops" { |
| 613 | const tmp_file_name = "temp_test_file.txt"; | 105 | const tmp_file_name = "temp_test_file.txt"; |
| 614 | var file = try fs.cwd().createFile(tmp_file_name, .{}); | 106 | var file = try fs.cwd().createFile(tmp_file_name, .{}); |
| ... | @@ -621,16 +113,16 @@ test "File seek ops" { | ... | @@ -621,16 +113,16 @@ test "File seek ops" { |
| 621 | 113 | ||
| 622 | // Seek to the end | 114 | // Seek to the end |
| 623 | try file.seekFromEnd(0); | 115 | try file.seekFromEnd(0); |
| 624 | std.testing.expect((try file.getPos()) == try file.getEndPos()); | 116 | expect((try file.getPos()) == try file.getEndPos()); |
| 625 | // Negative delta | 117 | // Negative delta |
| 626 | try file.seekBy(-4096); | 118 | try file.seekBy(-4096); |
| 627 | std.testing.expect((try file.getPos()) == 4096); | 119 | expect((try file.getPos()) == 4096); |
| 628 | // Positive delta | 120 | // Positive delta |
| 629 | try file.seekBy(10); | 121 | try file.seekBy(10); |
| 630 | std.testing.expect((try file.getPos()) == 4106); | 122 | expect((try file.getPos()) == 4106); |
| 631 | // Absolute position | 123 | // Absolute position |
| 632 | try file.seekTo(1234); | 124 | try file.seekTo(1234); |
| 633 | std.testing.expect((try file.getPos()) == 1234); | 125 | expect((try file.getPos()) == 1234); |
| 634 | } | 126 | } |
| 635 | 127 | ||
| 636 | test "updateTimes" { | 128 | test "updateTimes" { |
| ... | @@ -647,6 +139,6 @@ test "updateTimes" { | ... | @@ -647,6 +139,6 @@ test "updateTimes" { |
| 647 | stat_old.mtime - 5 * std.time.ns_per_s, | 139 | stat_old.mtime - 5 * std.time.ns_per_s, |
| 648 | ); | 140 | ); |
| 649 | var stat_new = try file.stat(); | 141 | var stat_new = try file.stat(); |
| 650 | std.testing.expect(stat_new.atime < stat_old.atime); | 142 | expect(stat_new.atime < stat_old.atime); |
| 651 | std.testing.expect(stat_new.mtime < stat_old.mtime); | 143 | expect(stat_new.mtime < stat_old.mtime); |
| 652 | } | 144 | } |
lib/std/json.zig+3-2| ... | @@ -10,6 +10,7 @@ const mem = std.mem; | ... | @@ -10,6 +10,7 @@ const mem = std.mem; |
| 10 | const maxInt = std.math.maxInt; | 10 | const maxInt = std.math.maxInt; |
| 11 | 11 | ||
| 12 | pub const WriteStream = @import("json/write_stream.zig").WriteStream; | 12 | pub const WriteStream = @import("json/write_stream.zig").WriteStream; |
| 13 | pub const writeStream = @import("json/write_stream.zig").writeStream; | ||
| 13 | 14 | ||
| 14 | const StringEscapes = union(enum) { | 15 | const StringEscapes = union(enum) { |
| 15 | None, | 16 | None, |
| ... | @@ -2109,7 +2110,7 @@ test "write json then parse it" { | ... | @@ -2109,7 +2110,7 @@ test "write json then parse it" { |
| 2109 | 2110 | ||
| 2110 | var fixed_buffer_stream = std.io.fixedBufferStream(&out_buffer); | 2111 | var fixed_buffer_stream = std.io.fixedBufferStream(&out_buffer); |
| 2111 | const out_stream = fixed_buffer_stream.outStream(); | 2112 | const out_stream = fixed_buffer_stream.outStream(); |
| 2112 | var jw = WriteStream(@TypeOf(out_stream).Child, 4).init(out_stream); | 2113 | var jw = writeStream(out_stream, 4); |
| 2113 | 2114 | ||
| 2114 | try jw.beginObject(); | 2115 | try jw.beginObject(); |
| 2115 | 2116 | ||
| ... | @@ -2140,7 +2141,7 @@ test "write json then parse it" { | ... | @@ -2140,7 +2141,7 @@ test "write json then parse it" { |
| 2140 | 2141 | ||
| 2141 | var parser = Parser.init(testing.allocator, false); | 2142 | var parser = Parser.init(testing.allocator, false); |
| 2142 | defer parser.deinit(); | 2143 | defer parser.deinit(); |
| 2143 | var tree = try parser.parse(slice_out_stream.getWritten()); | 2144 | var tree = try parser.parse(fixed_buffer_stream.getWritten()); |
| 2144 | defer tree.deinit(); | 2145 | defer tree.deinit(); |
| 2145 | 2146 | ||
| 2146 | testing.expect(tree.root.Object.get("f").?.value.Bool == false); | 2147 | testing.expect(tree.root.Object.get("f").?.value.Bool == false); |
lib/std/json/write_stream.zig+10-3| ... | @@ -249,15 +249,22 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type { | ... | @@ -249,15 +249,22 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type { |
| 249 | }; | 249 | }; |
| 250 | } | 250 | } |
| 251 | 251 | ||
| 252 | pub fn writeStream( | ||
| 253 | out_stream: var, | ||
| 254 | comptime max_depth: usize, | ||
| 255 | ) WriteStream(@TypeOf(out_stream), max_depth) { | ||
| 256 | return WriteStream(@TypeOf(out_stream), max_depth).init(out_stream); | ||
| 257 | } | ||
| 258 | |||
| 252 | test "json write stream" { | 259 | test "json write stream" { |
| 253 | var out_buf: [1024]u8 = undefined; | 260 | var out_buf: [1024]u8 = undefined; |
| 254 | var slice_stream = std.io.SliceOutStream.init(&out_buf); | 261 | var slice_stream = std.io.fixedBufferStream(&out_buf); |
| 255 | const out = &slice_stream.stream; | 262 | const out = slice_stream.outStream(); |
| 256 | 263 | ||
| 257 | var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator); | 264 | var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator); |
| 258 | defer arena_allocator.deinit(); | 265 | defer arena_allocator.deinit(); |
| 259 | 266 | ||
| 260 | var w = std.json.WriteStream(@TypeOf(out).Child, 10).init(out); | 267 | var w = std.json.writeStream(out, 10); |
| 261 | try w.emitJson(try getJson(&arena_allocator.allocator)); | 268 | try w.emitJson(try getJson(&arena_allocator.allocator)); |
| 262 | 269 | ||
| 263 | const result = slice_stream.getWritten(); | 270 | const result = slice_stream.getWritten(); |
lib/std/net.zig+2-2| ... | @@ -816,7 +816,7 @@ fn linuxLookupNameFromHosts( | ... | @@ -816,7 +816,7 @@ fn linuxLookupNameFromHosts( |
| 816 | }; | 816 | }; |
| 817 | defer file.close(); | 817 | defer file.close(); |
| 818 | 818 | ||
| 819 | const stream = &std.io.BufferedInStream(fs.File.ReadError).init(&file.inStream().stream).stream; | 819 | const stream = std.io.bufferedInStream(file.inStream()).inStream(); |
| 820 | var line_buf: [512]u8 = undefined; | 820 | var line_buf: [512]u8 = undefined; |
| 821 | while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) { | 821 | while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) { |
| 822 | error.StreamTooLong => blk: { | 822 | error.StreamTooLong => blk: { |
| ... | @@ -1010,7 +1010,7 @@ fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void { | ... | @@ -1010,7 +1010,7 @@ fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void { |
| 1010 | }; | 1010 | }; |
| 1011 | defer file.close(); | 1011 | defer file.close(); |
| 1012 | 1012 | ||
| 1013 | const stream = &std.io.BufferedInStream(fs.File.ReadError).init(&file.inStream().stream).stream; | 1013 | const stream = std.io.bufferedInStream(file.inStream()).inStream(); |
| 1014 | var line_buf: [512]u8 = undefined; | 1014 | var line_buf: [512]u8 = undefined; |
| 1015 | while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) { | 1015 | while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) { |
| 1016 | error.StreamTooLong => blk: { | 1016 | error.StreamTooLong => blk: { |
lib/std/os/test.zig+5-6| ... | @@ -354,8 +354,7 @@ test "mmap" { | ... | @@ -354,8 +354,7 @@ test "mmap" { |
| 354 | const file = try fs.cwd().createFile(test_out_file, .{}); | 354 | const file = try fs.cwd().createFile(test_out_file, .{}); |
| 355 | defer file.close(); | 355 | defer file.close(); |
| 356 | 356 | ||
| 357 | var out_stream = file.outStream(); | 357 | const stream = file.outStream(); |
| 358 | const stream = &out_stream.stream; | ||
| 359 | 358 | ||
| 360 | var i: u32 = 0; | 359 | var i: u32 = 0; |
| 361 | while (i < alloc_size / @sizeOf(u32)) : (i += 1) { | 360 | while (i < alloc_size / @sizeOf(u32)) : (i += 1) { |
| ... | @@ -378,8 +377,8 @@ test "mmap" { | ... | @@ -378,8 +377,8 @@ test "mmap" { |
| 378 | ); | 377 | ); |
| 379 | defer os.munmap(data); | 378 | defer os.munmap(data); |
| 380 | 379 | ||
| 381 | var mem_stream = io.SliceInStream.init(data); | 380 | var mem_stream = io.fixedBufferStream(data); |
| 382 | const stream = &mem_stream.stream; | 381 | const stream = mem_stream.inStream(); |
| 383 | 382 | ||
| 384 | var i: u32 = 0; | 383 | var i: u32 = 0; |
| 385 | while (i < alloc_size / @sizeOf(u32)) : (i += 1) { | 384 | while (i < alloc_size / @sizeOf(u32)) : (i += 1) { |
| ... | @@ -402,8 +401,8 @@ test "mmap" { | ... | @@ -402,8 +401,8 @@ test "mmap" { |
| 402 | ); | 401 | ); |
| 403 | defer os.munmap(data); | 402 | defer os.munmap(data); |
| 404 | 403 | ||
| 405 | var mem_stream = io.SliceInStream.init(data); | 404 | var mem_stream = io.fixedBufferStream(data); |
| 406 | const stream = &mem_stream.stream; | 405 | const stream = mem_stream.inStream(); |
| 407 | 406 | ||
| 408 | var i: u32 = alloc_size / 2 / @sizeOf(u32); | 407 | var i: u32 = alloc_size / 2 / @sizeOf(u32); |
| 409 | while (i < alloc_size / @sizeOf(u32)) : (i += 1) { | 408 | while (i < alloc_size / @sizeOf(u32)) : (i += 1) { |
lib/std/zig/parser_test.zig+5-6| ... | @@ -2809,7 +2809,7 @@ const maxInt = std.math.maxInt; | ... | @@ -2809,7 +2809,7 @@ const maxInt = std.math.maxInt; |
| 2809 | var fixed_buffer_mem: [100 * 1024]u8 = undefined; | 2809 | var fixed_buffer_mem: [100 * 1024]u8 = undefined; |
| 2810 | 2810 | ||
| 2811 | fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *bool) ![]u8 { | 2811 | fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *bool) ![]u8 { |
| 2812 | const stderr = &io.getStdErr().outStream().stream; | 2812 | const stderr = io.getStdErr().outStream(); |
| 2813 | 2813 | ||
| 2814 | const tree = try std.zig.parse(allocator, source); | 2814 | const tree = try std.zig.parse(allocator, source); |
| 2815 | defer tree.deinit(); | 2815 | defer tree.deinit(); |
| ... | @@ -2824,17 +2824,17 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b | ... | @@ -2824,17 +2824,17 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b |
| 2824 | { | 2824 | { |
| 2825 | var i: usize = 0; | 2825 | var i: usize = 0; |
| 2826 | while (i < loc.column) : (i += 1) { | 2826 | while (i < loc.column) : (i += 1) { |
| 2827 | try stderr.write(" "); | 2827 | try stderr.writeAll(" "); |
| 2828 | } | 2828 | } |
| 2829 | } | 2829 | } |
| 2830 | { | 2830 | { |
| 2831 | const caret_count = token.end - token.start; | 2831 | const caret_count = token.end - token.start; |
| 2832 | var i: usize = 0; | 2832 | var i: usize = 0; |
| 2833 | while (i < caret_count) : (i += 1) { | 2833 | while (i < caret_count) : (i += 1) { |
| 2834 | try stderr.write("~"); | 2834 | try stderr.writeAll("~"); |
| 2835 | } | 2835 | } |
| 2836 | } | 2836 | } |
| 2837 | try stderr.write("\n"); | 2837 | try stderr.writeAll("\n"); |
| 2838 | } | 2838 | } |
| 2839 | if (tree.errors.len != 0) { | 2839 | if (tree.errors.len != 0) { |
| 2840 | return error.ParseError; | 2840 | return error.ParseError; |
| ... | @@ -2843,8 +2843,7 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b | ... | @@ -2843,8 +2843,7 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b |
| 2843 | var buffer = try std.Buffer.initSize(allocator, 0); | 2843 | var buffer = try std.Buffer.initSize(allocator, 0); |
| 2844 | errdefer buffer.deinit(); | 2844 | errdefer buffer.deinit(); |
| 2845 | 2845 | ||
| 2846 | var buffer_out_stream = io.BufferOutStream.init(&buffer); | 2846 | anything_changed.* = try std.zig.render(allocator, buffer.outStream(), tree); |
| 2847 | anything_changed.* = try std.zig.render(allocator, &buffer_out_stream.stream, tree); | ||
| 2848 | return buffer.toOwnedSlice(); | 2847 | return buffer.toOwnedSlice(); |
| 2849 | } | 2848 | } |
| 2850 | 2849 |
lib/std/zig/render.zig+1-1| ... | @@ -903,7 +903,7 @@ fn renderExpression( | ... | @@ -903,7 +903,7 @@ fn renderExpression( |
| 903 | var column_widths = widths[widths.len - row_size ..]; | 903 | var column_widths = widths[widths.len - row_size ..]; |
| 904 | 904 | ||
| 905 | // Null stream for counting the printed length of each expression | 905 | // Null stream for counting the printed length of each expression |
| 906 | var counting_stream = std.io.CountingOutStream(@TypeOf(std.io.null_out_stream)).init(std.io.null_out_stream); | 906 | var counting_stream = std.io.countingOutStream(std.io.null_out_stream); |
| 907 | 907 | ||
| 908 | var it = exprs.iterator(0); | 908 | var it = exprs.iterator(0); |
| 909 | var i: usize = 0; | 909 | var i: usize = 0; |