aboutsummaryrefslogtreecommitdiff
path: root/lib/std/io/in_stream.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2019-09-26 01:54:45 -0400
committerGitHub <noreply@github.com>2019-09-26 01:54:45 -0400
commit68bb3945708c43109c48bda3664176307d45b62c (patch)
treeafb9731e10cef9d192560b52cd9ae2cf179775c4 /lib/std/io/in_stream.zig
parent6128bc728d1e1024a178c16c2149f5b1a167a013 (diff)
parent4637e8f9699af9c3c6cf4df50ef5bb67c7a318a4 (diff)
downloadzig-68bb3945708c43109c48bda3664176307d45b62c.tar.gz
zig-68bb3945708c43109c48bda3664176307d45b62c.zip
Merge pull request #3315 from ziglang/mv-std-lib
Move std/ to lib/std/
Diffstat (limited to 'lib/std/io/in_stream.zig')
-rw-r--r--lib/std/io/in_stream.zig200
1 files changed, 200 insertions, 0 deletions
diff --git a/lib/std/io/in_stream.zig b/lib/std/io/in_stream.zig
new file mode 100644
index 0000000000..44c74fcca4
--- /dev/null
+++ b/lib/std/io/in_stream.zig
@@ -0,0 +1,200 @@
+const std = @import("../std.zig");
+const builtin = @import("builtin");
+const root = @import("root");
+const math = std.math;
+const assert = std.debug.assert;
+const mem = std.mem;
+const Buffer = std.Buffer;
+
+pub const default_stack_size = 1 * 1024 * 1024;
+pub const stack_size: usize = if (@hasDecl(root, "stack_size_std_io_InStream"))
+ root.stack_size_std_io_InStream
+else
+ default_stack_size;
+pub const stack_align = 16;
+
+pub fn InStream(comptime ReadError: type) type {
+ return struct {
+ const Self = @This();
+ pub const Error = ReadError;
+ pub const ReadFn = if (std.io.is_async)
+ async fn (self: *Self, buffer: []u8) Error!usize
+ else
+ fn (self: *Self, buffer: []u8) Error!usize;
+
+ /// Returns the number of bytes read. It may be less than buffer.len.
+ /// If the number of bytes read is 0, it means end of stream.
+ /// End of stream is not an error condition.
+ readFn: ReadFn,
+
+ /// Returns the number of bytes read. It may be less than buffer.len.
+ /// If the number of bytes read is 0, it means end of stream.
+ /// End of stream is not an error condition.
+ pub fn read(self: *Self, buffer: []u8) Error!usize {
+ if (std.io.is_async) {
+ // Let's not be writing 0xaa in safe modes for upwards of 4 MiB for every stream read.
+ @setRuntimeSafety(false);
+ var stack_frame: [stack_size]u8 align(stack_align) = undefined;
+ return await @asyncCall(&stack_frame, {}, self.readFn, self, buffer);
+ } else {
+ return self.readFn(self, buffer);
+ }
+ }
+
+ /// Returns the number of bytes read. If the number read is smaller than buf.len, it
+ /// means the stream reached the end. Reaching the end of a stream is not an error
+ /// condition.
+ pub fn readFull(self: *Self, buffer: []u8) Error!usize {
+ var index: usize = 0;
+ while (index != buffer.len) {
+ const amt = try self.read(buffer[index..]);
+ if (amt == 0) return index;
+ index += amt;
+ }
+ return index;
+ }
+
+ /// Returns the number of bytes read. If the number read would be smaller than buf.len,
+ /// error.EndOfStream is returned instead.
+ pub fn readNoEof(self: *Self, buf: []u8) !void {
+ const amt_read = try self.readFull(buf);
+ if (amt_read < buf.len) return error.EndOfStream;
+ }
+
+ /// Replaces `buffer` contents by reading from the stream until it is finished.
+ /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and
+ /// the contents read from the stream are lost.
+ pub fn readAllBuffer(self: *Self, buffer: *Buffer, max_size: usize) !void {
+ try buffer.resize(0);
+
+ var actual_buf_len: usize = 0;
+ while (true) {
+ const dest_slice = buffer.toSlice()[actual_buf_len..];
+ const bytes_read = try self.readFull(dest_slice);
+ actual_buf_len += bytes_read;
+
+ if (bytes_read != dest_slice.len) {
+ buffer.shrink(actual_buf_len);
+ return;
+ }
+
+ const new_buf_size = math.min(max_size, actual_buf_len + mem.page_size);
+ if (new_buf_size == actual_buf_len) return error.StreamTooLong;
+ try buffer.resize(new_buf_size);
+ }
+ }
+
+ /// Allocates enough memory to hold all the contents of the stream. If the allocated
+ /// memory would be greater than `max_size`, returns `error.StreamTooLong`.
+ /// Caller owns returned memory.
+ /// If this function returns an error, the contents from the stream read so far are lost.
+ pub fn readAllAlloc(self: *Self, allocator: *mem.Allocator, max_size: usize) ![]u8 {
+ var buf = Buffer.initNull(allocator);
+ defer buf.deinit();
+
+ try self.readAllBuffer(&buf, max_size);
+ return buf.toOwnedSlice();
+ }
+
+ /// Replaces `buffer` contents by reading from the stream until `delimiter` is found.
+ /// Does not include the delimiter in the result.
+ /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and the contents
+ /// read from the stream so far are lost.
+ pub fn readUntilDelimiterBuffer(self: *Self, buffer: *Buffer, delimiter: u8, max_size: usize) !void {
+ try buffer.resize(0);
+
+ while (true) {
+ var byte: u8 = try self.readByte();
+
+ if (byte == delimiter) {
+ return;
+ }
+
+ if (buffer.len() == max_size) {
+ return error.StreamTooLong;
+ }
+
+ try buffer.appendByte(byte);
+ }
+ }
+
+ /// Allocates enough memory to read until `delimiter`. If the allocated
+ /// memory would be greater than `max_size`, returns `error.StreamTooLong`.
+ /// Caller owns returned memory.
+ /// If this function returns an error, the contents from the stream read so far are lost.
+ pub fn readUntilDelimiterAlloc(self: *Self, allocator: *mem.Allocator, delimiter: u8, max_size: usize) ![]u8 {
+ var buf = Buffer.initNull(allocator);
+ defer buf.deinit();
+
+ try self.readUntilDelimiterBuffer(&buf, delimiter, max_size);
+ return buf.toOwnedSlice();
+ }
+
+ /// Reads 1 byte from the stream or returns `error.EndOfStream`.
+ pub fn readByte(self: *Self) !u8 {
+ var result: [1]u8 = undefined;
+ try self.readNoEof(result[0..]);
+ return result[0];
+ }
+
+ /// Same as `readByte` except the returned byte is signed.
+ pub fn readByteSigned(self: *Self) !i8 {
+ return @bitCast(i8, try self.readByte());
+ }
+
+ /// Reads a native-endian integer
+ pub fn readIntNative(self: *Self, comptime T: type) !T {
+ var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
+ try self.readNoEof(bytes[0..]);
+ return mem.readIntNative(T, &bytes);
+ }
+
+ /// Reads a foreign-endian integer
+ pub fn readIntForeign(self: *Self, comptime T: type) !T {
+ var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
+ try self.readNoEof(bytes[0..]);
+ return mem.readIntForeign(T, &bytes);
+ }
+
+ pub fn readIntLittle(self: *Self, comptime T: type) !T {
+ var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
+ try self.readNoEof(bytes[0..]);
+ return mem.readIntLittle(T, &bytes);
+ }
+
+ pub fn readIntBig(self: *Self, comptime T: type) !T {
+ var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
+ try self.readNoEof(bytes[0..]);
+ return mem.readIntBig(T, &bytes);
+ }
+
+ pub fn readInt(self: *Self, comptime T: type, endian: builtin.Endian) !T {
+ var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
+ try self.readNoEof(bytes[0..]);
+ return mem.readInt(T, &bytes, endian);
+ }
+
+ pub fn readVarInt(self: *Self, comptime ReturnType: type, endian: builtin.Endian, size: usize) !ReturnType {
+ assert(size <= @sizeOf(ReturnType));
+ var bytes_buf: [@sizeOf(ReturnType)]u8 = undefined;
+ const bytes = bytes_buf[0..size];
+ try self.readNoEof(bytes);
+ return mem.readVarInt(ReturnType, bytes, endian);
+ }
+
+ pub fn skipBytes(self: *Self, num_bytes: u64) !void {
+ var i: u64 = 0;
+ while (i < num_bytes) : (i += 1) {
+ _ = try self.readByte();
+ }
+ }
+
+ pub fn readStruct(self: *Self, comptime T: type) !T {
+ // Only extern and packed structs have defined in-memory layout.
+ comptime assert(@typeInfo(T).Struct.layout != builtin.TypeInfo.ContainerLayout.Auto);
+ var res: [1]T = undefined;
+ try self.readNoEof(@sliceToBytes(res[0..]));
+ return res[0];
+ }
+ };
+}