c3d816a98e
* delete the std/event/net directory * `std.event.Loop.waitUntilFdReadable` and related functions no longer have possibility of failure. On Linux, they fall back to poll() and then fall back to sleep(). * add some missing `noasync` decorations in `std.event.Loop` * redo the `std.net.Server` API. it's quite nice now, but shutdown does not work cleanly. There is a race condition with close() that I am actively working on. * move `std.io.OutStream` to its own file to match `std.io.InStream`. I started working on making `write` integrated with evented I/O, but it got tricky so I backed off and filed #3557. However I did integrate `std.os.writev` and `std.os.pwritev` with evented I/O. * add `std.Target.stack_align` * move networking tests to `lib/std/net/test.zig` * add `std.net.tcpConnectToHost` and `std.net.tcpConnectToAddress`. * rename `error.UnknownName` to `error.UnknownHostName` within the context of DNS resolution. * add `std.os.readv`, which is integrated with evented I/O. * `std.os.preadv`, is now integrated with evented I/O. * `std.os.accept4` now asserts that ENOTSOCK and EOPNOTSUPP never occur (misuse of API), instead of returning errors. * `std.os.connect` is now integrated with evented I/O. `std.os.connect_async` is gone. Just use `std.os.connect`. * fix false positive dependency loop regarding async function frames * add more compile notes to help when dependency loops occur in determining whether a function is async. * ir: change an assert to ir_assert to make it easier to find workarounds for when such an assert is triggered. In this case it was trying to parse an IPv4 address at comptime.
241 lines
9.7 KiB
Zig
241 lines
9.7 KiB
Zig
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 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(std.Target.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 from the stream until specified byte is found. If the buffer is not
|
|
/// large enough to hold the entire contents, `error.StreamTooLong` is returned.
|
|
/// If end-of-stream is found, returns the rest of the stream. If this
|
|
/// function is called again after that, returns null.
|
|
/// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The
|
|
/// delimiter byte is not included in the returned slice.
|
|
pub fn readUntilDelimiterOrEof(self: *Self, buf: []u8, delimiter: u8) !?[]u8 {
|
|
var index: usize = 0;
|
|
while (true) {
|
|
const byte = self.readByte() catch |err| switch (err) {
|
|
error.EndOfStream => {
|
|
if (index == 0) {
|
|
return null;
|
|
} else {
|
|
return buf[0..index];
|
|
}
|
|
},
|
|
else => |e| return e,
|
|
};
|
|
|
|
if (byte == delimiter) return buf[0..index];
|
|
if (index >= buf.len) return error.StreamTooLong;
|
|
|
|
buf[index] = byte;
|
|
index += 1;
|
|
}
|
|
}
|
|
|
|
/// Reads from the stream until specified byte is found, discarding all data,
|
|
/// including the delimiter.
|
|
/// If end-of-stream is found, this function succeeds.
|
|
pub fn skipUntilDelimiterOrEof(self: *Self, delimiter: u8) !void {
|
|
while (true) {
|
|
const byte = self.readByte() catch |err| switch (err) {
|
|
error.EndOfStream => return,
|
|
else => |e| return e,
|
|
};
|
|
if (byte == delimiter) return;
|
|
}
|
|
}
|
|
|
|
/// 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];
|
|
}
|
|
};
|
|
}
|