authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-03-10 18:44:30-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-03-10 18:44:30-04:00
log18f1fef1426cb0405c733890b2e1d8d48627e4fe
treef019a3291b74a42206691a75e2832802bb50abc7
parentb6fbd524f122449e6e2bb4d73ce3f59b01286f50
signaturelock-open Commit is signed but in an unrecognized format.

update standard library to new I/O streams API


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 {
104104 }
105105
106106 pub fn dump(self: *Self) void {
107 var stderr_file = std.io.getStdErr() 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;
107 self.dumpToStream(std.io.getStdErr().outStream()) catch return;
112108 }
113109
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 {
115111 const S = struct {
116112 fn dumpRecursive(
117 s: *std.io.OutStream(Error),
113 s: var,
118114 optional_node: ?*Node,
119115 indent: usize,
120116 comptime depth: comptime_int,
121 ) Error!void {
117 ) !void {
122118 try s.writeByteNTimes(' ', indent);
123119 if (optional_node) |node| {
124120 try s.print("0x{x}={}\n", .{ @ptrToInt(node), node.data });
......@@ -326,17 +322,16 @@ test "std.atomic.Queue single-threaded" {
326322
327323test "std.atomic.Queue dump" {
328324 const mem = std.mem;
329 const SliceOutStream = std.io.SliceOutStream;
330325 var buffer: [1024]u8 = undefined;
331326 var expected_buffer: [1024]u8 = undefined;
332 var sos = SliceOutStream.init(buffer[0..]);
327 var fbs = std.io.fixedBufferStream(&buffer);
333328
334329 var queue = Queue(i32).init();
335330
336331 // Test empty stream
337 sos.reset();
338 try queue.dumpToStream(SliceOutStream.Error, &sos.stream);
339 expect(mem.eql(u8, buffer[0..sos.pos],
332 fbs.reset();
333 try queue.dumpToStream(fbs.outStream());
334 expect(mem.eql(u8, buffer[0..fbs.pos],
340335 \\head: (null)
341336 \\tail: (null)
342337 \\
......@@ -350,8 +345,8 @@ test "std.atomic.Queue dump" {
350345 };
351346 queue.put(&node_0);
352347
353 sos.reset();
354 try queue.dumpToStream(SliceOutStream.Error, &sos.stream);
348 fbs.reset();
349 try queue.dumpToStream(fbs.outStream());
355350
356351 var expected = try std.fmt.bufPrint(expected_buffer[0..],
357352 \\head: 0x{x}=1
......@@ -360,7 +355,7 @@ test "std.atomic.Queue dump" {
360355 \\ (null)
361356 \\
362357 , .{ @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));
364359
365360 // Test a stream with two elements
366361 var node_1 = Queue(i32).Node{
......@@ -370,8 +365,8 @@ test "std.atomic.Queue dump" {
370365 };
371366 queue.put(&node_1);
372367
373 sos.reset();
374 try queue.dumpToStream(SliceOutStream.Error, &sos.stream);
368 fbs.reset();
369 try queue.dumpToStream(fbs.outStream());
375370
376371 expected = try std.fmt.bufPrint(expected_buffer[0..],
377372 \\head: 0x{x}=1
......@@ -381,5 +376,5 @@ test "std.atomic.Queue dump" {
381376 \\ (null)
382377 \\
383378 , .{ @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));
385380}
lib/std/buffer.zig+12
......@@ -219,3 +219,15 @@ test "Buffer.print" {
219219 try buf.print("Hello {} the {}", .{ 2, "world" });
220220 testing.expect(buf.eql("Hello 2 the world"));
221221}
222
223test "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 {
121121}
122122
123123fn test_read_stream_ileb128(comptime T: type, encoded: []const u8) !T {
124 var in_stream = std.io.SliceInStream.init(encoded);
125 return try readILEB128(T, &in_stream.stream);
124 var in_stream = std.io.fixedBufferStream(encoded);
125 return try readILEB128(T, in_stream.inStream());
126126}
127127
128128fn test_read_stream_uleb128(comptime T: type, encoded: []const u8) !T {
129 var in_stream = std.io.SliceInStream.init(encoded);
130 return try readULEB128(T, &in_stream.stream);
129 var in_stream = std.io.fixedBufferStream(encoded);
130 return try readULEB128(T, in_stream.inStream());
131131}
132132
133133fn test_read_ileb128(comptime T: type, encoded: []const u8) !T {
134 var in_stream = std.io.SliceInStream.init(encoded);
135 const v1 = readILEB128(T, &in_stream.stream);
134 var in_stream = std.io.fixedBufferStream(encoded);
135 const v1 = readILEB128(T, in_stream.inStream());
136136 var in_ptr = encoded.ptr;
137137 const v2 = readILEB128Mem(T, &in_ptr);
138138 testing.expectEqual(v1, v2);
......@@ -140,8 +140,8 @@ fn test_read_ileb128(comptime T: type, encoded: []const u8) !T {
140140}
141141
142142fn test_read_uleb128(comptime T: type, encoded: []const u8) !T {
143 var in_stream = std.io.SliceInStream.init(encoded);
144 const v1 = readULEB128(T, &in_stream.stream);
143 var in_stream = std.io.fixedBufferStream(encoded);
144 const v1 = readULEB128(T, in_stream.inStream());
145145 var in_ptr = encoded.ptr;
146146 const v2 = readULEB128Mem(T, &in_ptr);
147147 testing.expectEqual(v1, v2);
......@@ -149,22 +149,22 @@ fn test_read_uleb128(comptime T: type, encoded: []const u8) !T {
149149}
150150
151151fn 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);
153153 var in_ptr = encoded.ptr;
154154 var i: usize = 0;
155155 while (i < N) : (i += 1) {
156 const v1 = readILEB128(T, &in_stream.stream);
156 const v1 = readILEB128(T, in_stream.inStream());
157157 const v2 = readILEB128Mem(T, &in_ptr);
158158 testing.expectEqual(v1, v2);
159159 }
160160}
161161
162162fn 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);
164164 var in_ptr = encoded.ptr;
165165 var i: usize = 0;
166166 while (i < N) : (i += 1) {
167 const v1 = readULEB128(T, &in_stream.stream);
167 const v1 = readULEB128(T, in_stream.inStream());
168168 const v2 = readULEB128Mem(T, &in_ptr);
169169 testing.expectEqual(v1, v2);
170170 }
lib/std/heap.zig+1
......@@ -10,6 +10,7 @@ const c = std.c;
1010const maxInt = std.math.maxInt;
1111
1212pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator;
13pub const loggingAllocator = @import("heap/logging_allocator.zig").loggingAllocator;
1314
1415const Allocator = mem.Allocator;
1516
lib/std/heap/logging_allocator.zig+51-45
......@@ -1,63 +1,69 @@
11const std = @import("../std.zig");
22const Allocator = std.mem.Allocator;
33
4const AnyErrorOutStream = std.io.OutStream(anyerror);
5
64/// This allocator is used in front of another allocator and logs to the provided stream
75/// on every call to the allocator. Stream errors are ignored.
86/// If https://github.com/ziglang/zig/issues/2586 is implemented, this API can be improved.
9pub const LoggingAllocator = struct {
10 allocator: Allocator,
11 parent_allocator: *Allocator,
12 out_stream: *AnyErrorOutStream,
7pub fn LoggingAllocator(comptime OutStreamType: type) type {
8 return struct {
9 allocator: Allocator,
10 parent_allocator: *Allocator,
11 out_stream: OutStreamType,
1312
14 const Self = @This();
13 const Self = @This();
1514
16 pub fn init(parent_allocator: *Allocator, out_stream: *AnyErrorOutStream) Self {
17 return Self{
18 .allocator = Allocator{
19 .reallocFn = realloc,
20 .shrinkFn = shrink,
21 },
22 .parent_allocator = parent_allocator,
23 .out_stream = out_stream,
24 };
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 {};
15 pub fn init(parent_allocator: *Allocator, out_stream: OutStreamType) Self {
16 return Self{
17 .allocator = Allocator{
18 .reallocFn = realloc,
19 .shrinkFn = shrink,
20 },
21 .parent_allocator = parent_allocator,
22 .out_stream = out_stream,
23 };
3324 }
34 const result = self.parent_allocator.reallocFn(self.parent_allocator, old_mem, old_align, new_size, new_align);
35 if (result) |buff| {
36 self.out_stream.print("success!\n", .{}) catch {};
37 } else |err| {
38 self.out_stream.print("failure!\n", .{}) catch {};
25
26 fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
27 const self = @fieldParentPtr(Self, "allocator", allocator);
28 if (old_mem.len == 0) {
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;
3940 }
40 return result;
41 }
4241
43 fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
44 const self = @fieldParentPtr(Self, "allocator", allocator);
45 const result = self.parent_allocator.shrinkFn(self.parent_allocator, old_mem, old_align, new_size, new_align);
46 if (new_size == 0) {
47 self.out_stream.print("free of {} bytes success!\n", .{old_mem.len}) catch {};
48 } else {
49 self.out_stream.print("shrink from {} bytes to {} bytes success!\n", .{ old_mem.len, new_size }) catch {};
42 fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
43 const self = @fieldParentPtr(Self, "allocator", allocator);
44 const result = self.parent_allocator.shrinkFn(self.parent_allocator, old_mem, old_align, new_size, new_align);
45 if (new_size == 0) {
46 self.out_stream.print("free of {} bytes success!\n", .{old_mem.len}) catch {};
47 } else {
48 self.out_stream.print("shrink from {} bytes to {} bytes success!\n", .{ old_mem.len, new_size }) catch {};
49 }
50 return result;
5051 }
51 return result;
52 }
53};
52 };
53}
54
55pub 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}
5461
5562test "LoggingAllocator" {
5663 var buf: [255]u8 = undefined;
57 var slice_stream = std.io.SliceOutStream.init(buf[0..]);
58 const stream = &slice_stream.stream;
64 var fbs = std.io.fixedBufferStream(&buf);
5965
60 const allocator = &LoggingAllocator.init(std.testing.allocator, @ptrCast(*AnyErrorOutStream, stream)).allocator;
66 const allocator = &loggingAllocator(std.testing.allocator, fbs.outStream()).allocator;
6167
6268 const ptr = try allocator.alloc(u8, 10);
6369 allocator.free(ptr);
......@@ -66,5 +72,5 @@ test "LoggingAllocator" {
6672 \\allocation of 10 success!
6773 \\free of 10 bytes success!
6874 \\
69 , slice_stream.getWritten());
75 , fbs.getWritten());
7076}
lib/std/io.zig+25-739
......@@ -4,17 +4,13 @@ const root = @import("root");
44const c = std.c;
55
66const math = std.math;
7const debug = std.debug;
8const assert = debug.assert;
7const assert = std.debug.assert;
98const os = std.os;
109const fs = std.fs;
1110const mem = std.mem;
1211const meta = std.meta;
1312const trait = meta.trait;
14const Buffer = std.Buffer;
15const fmt = std.fmt;
1613const File = std.fs.File;
17const testing = std.testing;
1814
1915pub const Mode = enum {
2016 /// I/O operates normally, waiting for the operating system syscalls to complete.
......@@ -92,10 +88,9 @@ pub fn getStdIn() File {
9288 };
9389}
9490
95pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream;
9691pub const InStream = @import("io/in_stream.zig").InStream;
9792pub const OutStream = @import("io/out_stream.zig").OutStream;
98pub const BufferedAtomicFile = @import("io/buffered_atomic_file.zig").BufferedAtomicFile;
93pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream;
9994
10095pub const BufferedOutStream = @import("io/buffered_out_stream.zig").BufferedOutStream;
10196pub const bufferedOutStream = @import("io/buffered_out_stream.zig").bufferedOutStream;
......@@ -103,36 +98,33 @@ pub const bufferedOutStream = @import("io/buffered_out_stream.zig").bufferedOutS
10398pub const BufferedInStream = @import("io/buffered_in_stream.zig").BufferedInStream;
10499pub const bufferedInStream = @import("io/buffered_in_stream.zig").bufferedInStream;
105100
101pub const PeekStream = @import("io/peek_stream.zig").PeekStream;
102pub const peekStream = @import("io/peek_stream.zig").peekStream;
103
106104pub const FixedBufferStream = @import("io/fixed_buffer_stream.zig").FixedBufferStream;
107105pub const fixedBufferStream = @import("io/fixed_buffer_stream.zig").fixedBufferStream;
108106
107pub const COutStream = @import("io/c_out_stream.zig").COutStream;
108pub const cOutStream = @import("io/c_out_stream.zig").cOutStream;
109
109110pub const CountingOutStream = @import("io/counting_out_stream.zig").CountingOutStream;
111pub const countingOutStream = @import("io/counting_out_stream.zig").countingOutStream;
110112
111pub fn cOutStream(c_file: *std.c.FILE) COutStream {
112 return .{ .context = c_file };
113}
113pub const BitInStream = @import("io/bit_in_stream.zig").BitInStream;
114pub const bitInStream = @import("io/bit_in_stream.zig").bitInStream;
114115
115pub const COutStream = OutStream(*std.c.FILE, std.fs.File.WriteError, cOutStreamWrite);
116
117pub fn cOutStreamWrite(c_file: *std.c.FILE, bytes: []const u8) std.fs.File.WriteError!usize {
118 const amt_written = std.c.fwrite(bytes.ptr, 1, bytes.len, c_file);
119 if (amt_written >= 0) return amt_written;
120 switch (std.c._errno().*) {
121 0 => unreachable,
122 os.EINVAL => unreachable,
123 os.EFAULT => unreachable,
124 os.EAGAIN => unreachable, // this is a blocking API
125 os.EBADF => unreachable, // always a race condition
126 os.EDESTADDRREQ => unreachable, // connect was never called
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}
116pub const BitOutStream = @import("io/bit_out_stream.zig").BitOutStream;
117pub const bitOutStream = @import("io/bit_out_stream.zig").bitOutStream;
118
119pub const Packing = @import("io/serialization.zig").Packing;
120
121pub const Serializer = @import("io/serialization.zig").Serializer;
122pub const serializer = @import("io/serialization.zig").serializer;
123
124pub const Deserializer = @import("io/serialization.zig").Deserializer;
125pub const deserializer = @import("io/serialization.zig").deserializer;
126
127pub const BufferedAtomicFile = @import("io/buffered_atomic_file.zig").BufferedAtomicFile;
136128
137129/// Deprecated; use `std.fs.Dir.writeFile`.
138130pub fn writeFile(path: []const u8, data: []const u8) !void {
......@@ -144,249 +136,6 @@ pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 {
144136 return fs.cwd().readFileAlloc(allocator, path, math.maxInt(usize));
145137}
146138
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.
149pub 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
213pub 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
244pub 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
390139/// An OutStream that doesn't write to anything.
391140pub const null_out_stream = @as(NullOutStream, .{ .context = {} });
392141
......@@ -396,472 +145,9 @@ fn dummyWrite(context: void, data: []const u8) error{}!usize {
396145}
397146
398147test "null_out_stream" {
399 null_out_stream.writeAll("yay" ** 1000) catch |err| switch (err) {};
400}
401
402/// Creates a stream which allows for writing bit fields to another stream
403pub 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
531pub 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.
548pub 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.
726pub 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 };
148 null_out_stream.writeAll("yay" ** 10) catch |err| switch (err) {};
860149}
861150
862151test "" {
863 comptime {
864 _ = @import("io/test.zig");
865 }
866 std.meta.refAllDecls(@This());
152 _ = @import("io/test.zig");
867153}
lib/std/io/bit_in_stream.zig created+237
......@@ -0,0 +1,237 @@
1const std = @import("../std.zig");
2const builtin = std.builtin;
3const io = std.io;
4const assert = std.debug.assert;
5const testing = std.testing;
6const trait = std.meta.trait;
7const meta = std.meta;
8const math = std.math;
9
10/// Creates a stream which allows for reading bit fields from another stream
11pub 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
158pub 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
165test "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 @@
1const std = @import("../std.zig");
2const builtin = std.builtin;
3const io = std.io;
4const testing = std.testing;
5const assert = std.debug.assert;
6const trait = std.meta.trait;
7const meta = std.meta;
8const math = std.math;
9
10/// Creates a stream which allows for writing bit fields to another stream
11pub 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
140pub 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
147test "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 @@
11const std = @import("../std.zig");
22const io = std.io;
3const assert = std.debug.assert;
4const testing = std.testing;
35
4pub fn BufferedInStream(comptime buffer_size: usize, comptime InStreamType) type {
6pub fn BufferedInStream(comptime buffer_size: usize, comptime InStreamType: type) type {
57 return struct {
68 unbuffered_in_stream: InStreamType,
79 fifo: FifoType = FifoType.init(),
lib/std/io/c_out_stream.zig created+44
......@@ -0,0 +1,44 @@
1const std = @import("../std.zig");
2const builtin = std.builtin;
3const io = std.io;
4const testing = std.testing;
5
6pub const COutStream = io.OutStream(*std.c.FILE, std.fs.File.WriteError, cOutStreamWrite);
7
8pub fn cOutStream(c_file: *std.c.FILE) COutStream {
9 return .{ .context = c_file };
10}
11
12fn 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
32test "" {
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 @@
11const std = @import("../std.zig");
22const io = std.io;
3const testing = std.testing;
34
45/// An OutStream that counts how many bytes has been written to it.
56pub fn CountingOutStream(comptime OutStreamType: type) type {
......@@ -12,13 +13,6 @@ pub fn CountingOutStream(comptime OutStreamType: type) type {
1213
1314 const Self = @This();
1415
15 pub fn init(child_stream: OutStreamType) Self {
16 return Self{
17 .bytes_written = 0,
18 .child_stream = child_stream,
19 };
20 }
21
2216 pub fn write(self: *Self, bytes: []const u8) Error!usize {
2317 const amt = try self.child_stream.write(bytes);
2418 self.bytes_written += amt;
......@@ -31,12 +25,15 @@ pub fn CountingOutStream(comptime OutStreamType: type) type {
3125 };
3226}
3327
28pub fn countingOutStream(child_stream: var) CountingOutStream(@TypeOf(child_stream)) {
29 return .{ .bytes_written = 0, .child_stream = child_stream };
30}
31
3432test "io.CountingOutStream" {
35 var counting_stream = CountingOutStream(NullOutStream.Error).init(std.io.null_out_stream);
36 const stream = &counting_stream.stream;
33 var counting_stream = countingOutStream(std.io.null_out_stream);
34 const stream = counting_stream.outStream();
3735
38 const bytes = "yay" ** 10000;
39 stream.write(bytes) catch unreachable;
36 const bytes = "yay" ** 100;
37 stream.writeAll(bytes) catch unreachable;
4038 testing.expect(counting_stream.bytes_written == bytes.len);
4139}
42
lib/std/io/fixed_buffer_stream.zig+48-8
......@@ -1,9 +1,11 @@
11const std = @import("../std.zig");
22const io = std.io;
33const testing = std.testing;
4const mem = std.mem;
5const assert = std.debug.assert;
46
5/// This turns a slice into an `io.OutStream`, `io.InStream`, or `io.SeekableStream`.
6/// If the supplied slice is const, then `io.OutStream` is not available.
7/// This turns a byte buffer into an `io.OutStream`, `io.InStream`, or `io.SeekableStream`.
8/// If the supplied byte buffer is const, then `io.OutStream` is not available.
79pub fn FixedBufferStream(comptime Buffer: type) type {
810 return struct {
911 /// `Buffer` is either a `[]u8` or `[]const u8`.
......@@ -46,7 +48,7 @@ pub fn FixedBufferStream(comptime Buffer: type) type {
4648 const size = std.math.min(dest.len, self.buffer.len - self.pos);
4749 const end = self.pos + size;
4850
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]);
5052 self.pos = end;
5153
5254 if (size == 0) return error.EndOfStream;
......@@ -65,7 +67,7 @@ pub fn FixedBufferStream(comptime Buffer: type) type {
6567 else
6668 self.buffer.len - self.pos;
6769
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]);
6971 self.pos += n;
7072
7173 if (n == 0) return error.OutOfMemory;
......@@ -100,7 +102,7 @@ pub fn FixedBufferStream(comptime Buffer: type) type {
100102 }
101103
102104 pub fn getWritten(self: Self) []const u8 {
103 return self.slice[0..self.pos];
105 return self.buffer[0..self.pos];
104106 }
105107
106108 pub fn reset(self: *Self) void {
......@@ -110,16 +112,16 @@ pub fn FixedBufferStream(comptime Buffer: type) type {
110112}
111113
112114pub 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 };
114116}
115117
116118fn NonSentinelSpan(comptime T: type) type {
117 var ptr_info = @typeInfo(std.mem.Span(T)).Pointer;
119 var ptr_info = @typeInfo(mem.Span(T)).Pointer;
118120 ptr_info.sentinel = null;
119121 return @Type(std.builtin.TypeInfo{ .Pointer = ptr_info });
120122}
121123
122test "FixedBufferStream" {
124test "FixedBufferStream output" {
123125 var buf: [255]u8 = undefined;
124126 var fbs = fixedBufferStream(&buf);
125127 const stream = fbs.outStream();
......@@ -127,3 +129,41 @@ test "FixedBufferStream" {
127129 try stream.print("{}{}!", .{ "Hello", "World" });
128130 testing.expectEqualSlices(u8, "HelloWorld!", fbs.getWritten());
129131}
132
133test "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
153test "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(
273273
274274test "InStream" {
275275 var buf = "a\x02".*;
276 var slice_stream = std.io.SliceInStream.init(&buf);
277 const in_stream = &slice_stream.stream;
276 const in_stream = std.io.fixedBufferStream(&buf).inStream();
278277 testing.expect((try in_stream.readByte()) == 'a');
279278 testing.expect((try in_stream.readEnum(enum(u8) {
280279 a = 0,
lib/std/io/peek_stream.zig created+112
......@@ -0,0 +1,112 @@
1const std = @import("../std.zig");
2const io = std.io;
3const mem = std.mem;
4const 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
9pub 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
74pub 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
81test "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 @@
1const std = @import("../std.zig");
2const builtin = std.builtin;
3const io = std.io;
4
5pub 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.
22pub 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
185pub 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.
206pub 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
341pub 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
349fn 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
403test "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
412fn 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
453test "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
460fn testAlternateSerializer(self: var, serializer: var) !void {
461 try serializer.serialize(self.f_f16);
462}
463
464fn 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
560test "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
571fn 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
601test "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" {
2222 var file = try cwd.createFile(tmp_file_name, .{});
2323 defer file.close();
2424
25 var file_out_stream = file.outStream();
26 var buf_stream = io.BufferedOutStream(File.WriteError).init(&file_out_stream.stream);
27 const st = &buf_stream.stream;
25 var buf_stream = io.bufferedOutStream(file.outStream());
26 const st = buf_stream.outStream();
2827 try st.print("begin", .{});
29 try st.write(data[0..]);
28 try st.writeAll(data[0..]);
3029 try st.print("end", .{});
3130 try buf_stream.flush();
3231 }
......@@ -48,9 +47,8 @@ test "write a file, read it, then delete it" {
4847 const expected_file_size: u64 = "begin".len + data.len + "end".len;
4948 expectEqual(expected_file_size, file_size);
5049
51 var file_in_stream = file.inStream();
52 var buf_stream = io.BufferedInStream(File.ReadError).init(&file_in_stream.stream);
53 const st = &buf_stream.stream;
50 var buf_stream = io.bufferedInStream(file.inStream());
51 const st = buf_stream.inStream();
5452 const contents = try st.readAllAlloc(std.testing.allocator, 2 * 1024);
5553 defer std.testing.allocator.free(contents);
5654
......@@ -61,224 +59,13 @@ test "write a file, read it, then delete it" {
6159 try cwd.deleteFile(tmp_file_name);
6260}
6361
64test "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
76test "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
94test "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
127test "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
147test "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
219test "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
27262test "BitStreams with File Stream" {
27363 const tmp_file_name = "temp_test_file.txt";
27464 {
27565 var file = try fs.cwd().createFile(tmp_file_name, .{});
27666 defer file.close();
27767
278 var file_out = 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);
68 var bit_stream = io.bitOutStream(builtin.endian, file.outStream());
28269
28370 try bit_stream.writeBits(@as(u2, 1), 1);
28471 try bit_stream.writeBits(@as(u5, 2), 2);
......@@ -292,10 +79,7 @@ test "BitStreams with File Stream" {
29279 var file = try fs.cwd().openFile(tmp_file_name, .{});
29380 defer file.close();
29481
295 var file_in = 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);
82 var bit_stream = io.bitInStream(builtin.endian, file.inStream());
29983
30084 var out_bits: usize = undefined;
30185
......@@ -317,298 +101,6 @@ test "BitStreams with File Stream" {
317101 try fs.cwd().deleteFile(tmp_file_name);
318102}
319103
320fn 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
381test "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
390fn 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
435test "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
442fn testAlternateSerializer(self: var, serializer: var) !void {
443 try serializer.serialize(self.f_f16);
444}
445
446fn 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
546test "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
557fn 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
591test "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
598test "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
612104test "File seek ops" {
613105 const tmp_file_name = "temp_test_file.txt";
614106 var file = try fs.cwd().createFile(tmp_file_name, .{});
......@@ -621,16 +113,16 @@ test "File seek ops" {
621113
622114 // Seek to the end
623115 try file.seekFromEnd(0);
624 std.testing.expect((try file.getPos()) == try file.getEndPos());
116 expect((try file.getPos()) == try file.getEndPos());
625117 // Negative delta
626118 try file.seekBy(-4096);
627 std.testing.expect((try file.getPos()) == 4096);
119 expect((try file.getPos()) == 4096);
628120 // Positive delta
629121 try file.seekBy(10);
630 std.testing.expect((try file.getPos()) == 4106);
122 expect((try file.getPos()) == 4106);
631123 // Absolute position
632124 try file.seekTo(1234);
633 std.testing.expect((try file.getPos()) == 1234);
125 expect((try file.getPos()) == 1234);
634126}
635127
636128test "updateTimes" {
......@@ -647,6 +139,6 @@ test "updateTimes" {
647139 stat_old.mtime - 5 * std.time.ns_per_s,
648140 );
649141 var stat_new = try file.stat();
650 std.testing.expect(stat_new.atime < stat_old.atime);
651 std.testing.expect(stat_new.mtime < stat_old.mtime);
142 expect(stat_new.atime < stat_old.atime);
143 expect(stat_new.mtime < stat_old.mtime);
652144}
lib/std/json.zig+3-2
......@@ -10,6 +10,7 @@ const mem = std.mem;
1010const maxInt = std.math.maxInt;
1111
1212pub const WriteStream = @import("json/write_stream.zig").WriteStream;
13pub const writeStream = @import("json/write_stream.zig").writeStream;
1314
1415const StringEscapes = union(enum) {
1516 None,
......@@ -2109,7 +2110,7 @@ test "write json then parse it" {
21092110
21102111 var fixed_buffer_stream = std.io.fixedBufferStream(&out_buffer);
21112112 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);
21132114
21142115 try jw.beginObject();
21152116
......@@ -2140,7 +2141,7 @@ test "write json then parse it" {
21402141
21412142 var parser = Parser.init(testing.allocator, false);
21422143 defer parser.deinit();
2143 var tree = try parser.parse(slice_out_stream.getWritten());
2144 var tree = try parser.parse(fixed_buffer_stream.getWritten());
21442145 defer tree.deinit();
21452146
21462147 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 {
249249 };
250250}
251251
252pub 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
252259test "json write stream" {
253260 var out_buf: [1024]u8 = undefined;
254 var slice_stream = std.io.SliceOutStream.init(&out_buf);
255 const out = &slice_stream.stream;
261 var slice_stream = std.io.fixedBufferStream(&out_buf);
262 const out = slice_stream.outStream();
256263
257264 var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
258265 defer arena_allocator.deinit();
259266
260 var w = std.json.WriteStream(@TypeOf(out).Child, 10).init(out);
267 var w = std.json.writeStream(out, 10);
261268 try w.emitJson(try getJson(&arena_allocator.allocator));
262269
263270 const result = slice_stream.getWritten();
lib/std/net.zig+2-2
......@@ -816,7 +816,7 @@ fn linuxLookupNameFromHosts(
816816 };
817817 defer file.close();
818818
819 const stream = &std.io.BufferedInStream(fs.File.ReadError).init(&file.inStream().stream).stream;
819 const stream = std.io.bufferedInStream(file.inStream()).inStream();
820820 var line_buf: [512]u8 = undefined;
821821 while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) {
822822 error.StreamTooLong => blk: {
......@@ -1010,7 +1010,7 @@ fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void {
10101010 };
10111011 defer file.close();
10121012
1013 const stream = &std.io.BufferedInStream(fs.File.ReadError).init(&file.inStream().stream).stream;
1013 const stream = std.io.bufferedInStream(file.inStream()).inStream();
10141014 var line_buf: [512]u8 = undefined;
10151015 while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) {
10161016 error.StreamTooLong => blk: {
lib/std/os/test.zig+5-6
......@@ -354,8 +354,7 @@ test "mmap" {
354354 const file = try fs.cwd().createFile(test_out_file, .{});
355355 defer file.close();
356356
357 var out_stream = file.outStream();
358 const stream = &out_stream.stream;
357 const stream = file.outStream();
359358
360359 var i: u32 = 0;
361360 while (i < alloc_size / @sizeOf(u32)) : (i += 1) {
......@@ -378,8 +377,8 @@ test "mmap" {
378377 );
379378 defer os.munmap(data);
380379
381 var mem_stream = io.SliceInStream.init(data);
382 const stream = &mem_stream.stream;
380 var mem_stream = io.fixedBufferStream(data);
381 const stream = mem_stream.inStream();
383382
384383 var i: u32 = 0;
385384 while (i < alloc_size / @sizeOf(u32)) : (i += 1) {
......@@ -402,8 +401,8 @@ test "mmap" {
402401 );
403402 defer os.munmap(data);
404403
405 var mem_stream = io.SliceInStream.init(data);
406 const stream = &mem_stream.stream;
404 var mem_stream = io.fixedBufferStream(data);
405 const stream = mem_stream.inStream();
407406
408407 var i: u32 = alloc_size / 2 / @sizeOf(u32);
409408 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;
28092809var fixed_buffer_mem: [100 * 1024]u8 = undefined;
28102810
28112811fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *bool) ![]u8 {
2812 const stderr = &io.getStdErr().outStream().stream;
2812 const stderr = io.getStdErr().outStream();
28132813
28142814 const tree = try std.zig.parse(allocator, source);
28152815 defer tree.deinit();
......@@ -2824,17 +2824,17 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b
28242824 {
28252825 var i: usize = 0;
28262826 while (i < loc.column) : (i += 1) {
2827 try stderr.write(" ");
2827 try stderr.writeAll(" ");
28282828 }
28292829 }
28302830 {
28312831 const caret_count = token.end - token.start;
28322832 var i: usize = 0;
28332833 while (i < caret_count) : (i += 1) {
2834 try stderr.write("~");
2834 try stderr.writeAll("~");
28352835 }
28362836 }
2837 try stderr.write("\n");
2837 try stderr.writeAll("\n");
28382838 }
28392839 if (tree.errors.len != 0) {
28402840 return error.ParseError;
......@@ -2843,8 +2843,7 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b
28432843 var buffer = try std.Buffer.initSize(allocator, 0);
28442844 errdefer buffer.deinit();
28452845
2846 var buffer_out_stream = io.BufferOutStream.init(&buffer);
2847 anything_changed.* = try std.zig.render(allocator, &buffer_out_stream.stream, tree);
2846 anything_changed.* = try std.zig.render(allocator, buffer.outStream(), tree);
28482847 return buffer.toOwnedSlice();
28492848}
28502849
lib/std/zig/render.zig+1-1
......@@ -903,7 +903,7 @@ fn renderExpression(
903903 var column_widths = widths[widths.len - row_size ..];
904904
905905 // 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);
907907
908908 var it = exprs.iterator(0);
909909 var i: usize = 0;