| ... | @@ -13,6 +13,7 @@ const native_os = builtin.os.tag; | ... | @@ -13,6 +13,7 @@ const native_os = builtin.os.tag; |
| 13 | const windows = std.os.windows; | 13 | const windows = std.os.windows; |
| 14 | const Allocator = std.mem.Allocator; | 14 | const Allocator = std.mem.Allocator; |
| 15 | const ArrayList = std.ArrayListUnmanaged; | 15 | const ArrayList = std.ArrayListUnmanaged; |
| | 16 | const File = std.fs.File; |
| 16 | | 17 | |
| 17 | // Windows 10 added support for unix sockets in build 17063, redstone 4 is the | 18 | // Windows 10 added support for unix sockets in build 17063, redstone 4 is the |
| 18 | // first release to support them. | 19 | // first release to support them. |
| ... | @@ -853,7 +854,7 @@ pub fn tcpConnectToAddress(address: Address) TcpConnectToAddressError!Stream { | ... | @@ -853,7 +854,7 @@ pub fn tcpConnectToAddress(address: Address) TcpConnectToAddressError!Stream { |
| 853 | | 854 | |
| 854 | // TODO: Instead of having a massive error set, make the error set have categories, and then | 855 | // TODO: Instead of having a massive error set, make the error set have categories, and then |
| 855 | // store the sub-error as a diagnostic value. | 856 | // store the sub-error as a diagnostic value. |
| 856 | const GetAddressListError = Allocator.Error || std.fs.File.OpenError || std.fs.File.ReadError || posix.SocketError || posix.BindError || posix.SetSockOptError || error{ | 857 | const GetAddressListError = Allocator.Error || File.OpenError || File.ReadError || posix.SocketError || posix.BindError || posix.SetSockOptError || error{ |
| 857 | TemporaryNameServerFailure, | 858 | TemporaryNameServerFailure, |
| 858 | NameServerFailure, | 859 | NameServerFailure, |
| 859 | AddressFamilyNotSupported, | 860 | AddressFamilyNotSupported, |
| ... | @@ -1363,9 +1364,8 @@ fn linuxLookupNameFromHosts( | ... | @@ -1363,9 +1364,8 @@ fn linuxLookupNameFromHosts( |
| 1363 | defer file.close(); | 1364 | defer file.close(); |
| 1364 | | 1365 | |
| 1365 | var line_buf: [512]u8 = undefined; | 1366 | var line_buf: [512]u8 = undefined; |
| 1366 | var file_reader = file.reader(); | 1367 | var file_reader = file.reader(&line_buf); |
| 1367 | var br = file_reader.interface().buffered(&line_buf); | 1368 | return parseHosts(gpa, addrs, canon, name, family, port, &file_reader.interface) catch |err| switch (err) { |
| 1368 | return parseHosts(gpa, addrs, canon, name, family, port, &br) catch |err| switch (err) { | | |
| 1369 | error.OutOfMemory => return error.OutOfMemory, | 1369 | error.OutOfMemory => return error.OutOfMemory, |
| 1370 | error.ReadFailed => return file_reader.err.?, | 1370 | error.ReadFailed => return file_reader.err.?, |
| 1371 | }; | 1371 | }; |
| ... | @@ -1378,7 +1378,7 @@ fn parseHosts( | ... | @@ -1378,7 +1378,7 @@ fn parseHosts( |
| 1378 | name: []const u8, | 1378 | name: []const u8, |
| 1379 | family: posix.sa_family_t, | 1379 | family: posix.sa_family_t, |
| 1380 | port: u16, | 1380 | port: u16, |
| 1381 | br: *std.io.Reader, | 1381 | br: *io.Reader, |
| 1382 | ) error{ OutOfMemory, ReadFailed }!void { | 1382 | ) error{ OutOfMemory, ReadFailed }!void { |
| 1383 | while (true) { | 1383 | while (true) { |
| 1384 | const line = br.takeDelimiterExclusive('\n') catch |err| switch (err) { | 1384 | const line = br.takeDelimiterExclusive('\n') catch |err| switch (err) { |
| ... | @@ -1584,15 +1584,14 @@ const ResolvConf = struct { | ... | @@ -1584,15 +1584,14 @@ const ResolvConf = struct { |
| 1584 | defer file.close(); | 1584 | defer file.close(); |
| 1585 | | 1585 | |
| 1586 | var line_buf: [512]u8 = undefined; | 1586 | var line_buf: [512]u8 = undefined; |
| 1587 | var file_reader = file.reader(); | 1587 | var file_reader = file.reader(&line_buf); |
| 1588 | var br = file_reader.interface().buffered(&line_buf); | 1588 | return parse(rc, &file_reader.interface) catch |err| switch (err) { |
| 1589 | return parse(rc, &br) catch |err| switch (err) { | | |
| 1590 | error.ReadFailed => return file_reader.err.?, | 1589 | error.ReadFailed => return file_reader.err.?, |
| 1591 | else => |e| return e, | 1590 | else => |e| return e, |
| 1592 | }; | 1591 | }; |
| 1593 | } | 1592 | } |
| 1594 | | 1593 | |
| 1595 | fn parse(rc: *ResolvConf, br: *std.io.Reader) !void { | 1594 | fn parse(rc: *ResolvConf, br: *io.Reader) !void { |
| 1596 | const gpa = rc.gpa; | 1595 | const gpa = rc.gpa; |
| 1597 | while (br.takeSentinel('\n')) |line_with_comment| { | 1596 | while (br.takeSentinel('\n')) |line_with_comment| { |
| 1598 | const line = line: { | 1597 | const line = line: { |
| ... | @@ -1893,7 +1892,10 @@ pub const Stream = struct { | ... | @@ -1893,7 +1892,10 @@ pub const Stream = struct { |
| 1893 | | 1892 | |
| 1894 | pub const Reader = switch (native_os) { | 1893 | pub const Reader = switch (native_os) { |
| 1895 | .windows => struct { | 1894 | .windows => struct { |
| 1896 | stream: Stream, | 1895 | /// Use `interface` to access portably. |
| | 1896 | interface_state: io.Reader, |
| | 1897 | /// Use `getStream` to access portably. |
| | 1898 | net_stream: Stream, |
| 1897 | err: ?Error = null, | 1899 | err: ?Error = null, |
| 1898 | | 1900 | |
| 1899 | pub const Error = ReadError; | 1901 | pub const Error = ReadError; |
| ... | @@ -1902,29 +1904,27 @@ pub const Stream = struct { | ... | @@ -1902,29 +1904,27 @@ pub const Stream = struct { |
| 1902 | return r.stream; | 1904 | return r.stream; |
| 1903 | } | 1905 | } |
| 1904 | | 1906 | |
| 1905 | pub fn interface(r: *Reader) std.io.Reader { | 1907 | pub fn interface(r: *Reader) *io.Reader { |
| | 1908 | return &r.interface_state; |
| | 1909 | } |
| | 1910 | |
| | 1911 | pub fn init(net_stream: Stream, buffer: []u8) Reader { |
| 1906 | return .{ | 1912 | return .{ |
| 1907 | .context = r.stream.handle, | 1913 | .interface_state = .{ |
| 1908 | .vtable = &.{ | 1914 | .context = undefined, |
| 1909 | .read = read, | 1915 | .vtable = &.{ |
| 1910 | .readVec = readVec, | 1916 | .stream = stream, |
| 1911 | .discard = discard, | 1917 | .discard = discard, |
| | 1918 | }, |
| | 1919 | .buffer = buffer, |
| 1912 | }, | 1920 | }, |
| | 1921 | .net_stream = net_stream, |
| 1913 | }; | 1922 | }; |
| 1914 | } | 1923 | } |
| 1915 | | 1924 | |
| 1916 | fn read( | 1925 | fn stream(io_r: *io.Reader, io_w: *io.Writer, limit: io.Limit) io.Reader.StreamError!usize { |
| 1917 | context: ?*anyopaque, | 1926 | const r: *Reader = @fieldParentPtr("interface", io_r); |
| 1918 | bw: *std.io.Writer, | 1927 | const data = io_w.writableVector(limit); |
| 1919 | limit: std.io.Limit, | | |
| 1920 | ) std.io.Reader.Error!usize { | | |
| 1921 | const buf = limit.slice(try bw.writableSliceGreedy(1)); | | |
| 1922 | const n = try readVec(context, &.{buf}); | | |
| 1923 | bw.advance(n); | | |
| 1924 | return n; | | |
| 1925 | } | | |
| 1926 | | | |
| 1927 | fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { | | |
| 1928 | var iovecs: [max_buffers_len]windows.WSABUF = undefined; | 1928 | var iovecs: [max_buffers_len]windows.WSABUF = undefined; |
| 1929 | var iovecs_i: usize = 0; | 1929 | var iovecs_i: usize = 0; |
| 1930 | for (data) |d| { | 1930 | for (data) |d| { |
| ... | @@ -1939,7 +1939,7 @@ pub const Stream = struct { | ... | @@ -1939,7 +1939,7 @@ pub const Stream = struct { |
| 1939 | if (bufs.len == 0) return .{}; // Prevent false positive end detection on empty `data`. | 1939 | if (bufs.len == 0) return .{}; // Prevent false positive end detection on empty `data`. |
| 1940 | var n: u32 = undefined; | 1940 | var n: u32 = undefined; |
| 1941 | var flags: u32 = 0; | 1941 | var flags: u32 = 0; |
| 1942 | const rc = windows.ws2_32.WSARecvFrom(context, bufs.ptr, bufs.len, &n, &flags, null, null, null, null); | 1942 | const rc = windows.ws2_32.WSARecvFrom(r.net_stream.handle, bufs.ptr, bufs.len, &n, &flags, null, null, null, null); |
| 1943 | if (rc != 0) switch (windows.ws2_32.WSAGetLastError()) { | 1943 | if (rc != 0) switch (windows.ws2_32.WSAGetLastError()) { |
| 1944 | .WSAECONNRESET => return error.ConnectionResetByPeer, | 1944 | .WSAECONNRESET => return error.ConnectionResetByPeer, |
| 1945 | .WSAEFAULT => unreachable, // a pointer is not completely contained in user address space. | 1945 | .WSAEFAULT => unreachable, // a pointer is not completely contained in user address space. |
| ... | @@ -1955,22 +1955,35 @@ pub const Stream = struct { | ... | @@ -1955,22 +1955,35 @@ pub const Stream = struct { |
| 1955 | .WSA_OPERATION_ABORTED => unreachable, // not using overlapped I/O | 1955 | .WSA_OPERATION_ABORTED => unreachable, // not using overlapped I/O |
| 1956 | else => |err| return windows.unexpectedWSAError(err), | 1956 | else => |err| return windows.unexpectedWSAError(err), |
| 1957 | }; | 1957 | }; |
| 1958 | return .{ .len = n, .end = n == 0 }; | 1958 | if (n == 0) return error.EndOfStream; |
| | 1959 | return io_w.advanceVector(n); |
| 1959 | } | 1960 | } |
| 1960 | | 1961 | |
| 1961 | fn discard(context: ?*anyopaque, limit: std.io.Limit) std.io.Reader.Error!usize { | 1962 | fn discard(io_r: *io.Reader, limit: io.Limit) io.Reader.Error!usize { |
| 1962 | _ = context; | 1963 | const r: *Reader = @fieldParentPtr("interface", io_r); |
| | 1964 | _ = r; |
| 1963 | _ = limit; | 1965 | _ = limit; |
| 1964 | @panic("TODO"); | 1966 | @panic("TODO"); |
| 1965 | } | 1967 | } |
| 1966 | }, | 1968 | }, |
| 1967 | else => struct { | 1969 | else => struct { |
| 1968 | file_reader: std.fs.File.Reader, | 1970 | file_reader: File.Reader, |
| 1969 | | 1971 | |
| 1970 | pub const Error = ReadError; | 1972 | pub const Error = ReadError; |
| 1971 | | 1973 | |
| 1972 | pub fn interface(r: *Reader) std.io.Reader { | 1974 | pub fn interface(r: *Reader) *io.Reader { |
| 1973 | return r.file_reader.interface(); | 1975 | return &r.file_reader.interface; |
| | 1976 | } |
| | 1977 | |
| | 1978 | pub fn init(net_stream: Stream, buffer: []u8) Reader { |
| | 1979 | return .{ |
| | 1980 | .file_reader = .{ |
| | 1981 | .interface = File.Reader.initInterface(buffer), |
| | 1982 | .file = .{ .handle = net_stream.handle }, |
| | 1983 | .mode = .streaming, |
| | 1984 | .seek_err = error.Unseekable, |
| | 1985 | }, |
| | 1986 | }; |
| 1974 | } | 1987 | } |
| 1975 | | 1988 | |
| 1976 | pub fn getStream(r: *const Reader) Stream { | 1989 | pub fn getStream(r: *const Reader) Stream { |
| ... | @@ -1981,16 +1994,22 @@ pub const Stream = struct { | ... | @@ -1981,16 +1994,22 @@ pub const Stream = struct { |
| 1981 | | 1994 | |
| 1982 | pub const Writer = switch (native_os) { | 1995 | pub const Writer = switch (native_os) { |
| 1983 | .windows => struct { | 1996 | .windows => struct { |
| | 1997 | /// This field is present on all systems. |
| | 1998 | interface: io.Writer, |
| | 1999 | /// Use `getStream` for cross-platform support. |
| 1984 | stream: Stream, | 2000 | stream: Stream, |
| 1985 | | 2001 | |
| 1986 | pub const Error = WriteError; | 2002 | pub const Error = WriteError; |
| 1987 | | 2003 | |
| 1988 | pub fn interface(w: *Writer) std.io.Writer { | 2004 | pub fn init(stream: Stream, buffer: []u8) Writer { |
| 1989 | return .{ | 2005 | return .{ |
| 1990 | .context = w.stream.handle, | 2006 | .stream = stream, |
| 1991 | .vtable = &.{ | 2007 | .interface = .{ |
| 1992 | .writeSplat = writeSplat, | 2008 | .context = undefined, |
| 1993 | .writeFile = writeFile, | 2009 | .vtable = &.{ |
| | 2010 | .drain = drain, |
| | 2011 | }, |
| | 2012 | .buffer = buffer, |
| 1994 | }, | 2013 | }, |
| 1995 | }; | 2014 | }; |
| 1996 | } | 2015 | } |
| ... | @@ -1999,41 +2018,63 @@ pub const Stream = struct { | ... | @@ -1999,41 +2018,63 @@ pub const Stream = struct { |
| 1999 | return w.stream; | 2018 | return w.stream; |
| 2000 | } | 2019 | } |
| 2001 | | 2020 | |
| 2002 | fn writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize { | 2021 | fn drain(io_w: *io.Writer, data: []const []const u8, splat: usize) io.Writer.Error!usize { |
| | 2022 | const w: *Writer = @fieldParentPtr("interface", io_w); |
| | 2023 | const buffered = io_w.buffered(); |
| 2003 | comptime assert(native_os == .windows); | 2024 | comptime assert(native_os == .windows); |
| 2004 | if (data.len == 1 and splat == 0) return 0; | 2025 | var splat_buffer: [splat_buffer_len]u8 = undefined; |
| 2005 | var splat_buffer: [256]u8 = undefined; | | |
| 2006 | var iovecs: [max_buffers_len]windows.WSABUF = undefined; | 2026 | var iovecs: [max_buffers_len]windows.WSABUF = undefined; |
| 2007 | var len: u32 = @min(iovecs.len, data.len); | 2027 | var len: u32 = 0; |
| 2008 | for (iovecs[0..len], data[0..len]) |*v, d| v.* = .{ | 2028 | if (buffered.len != 0) { |
| 2009 | .buf = if (d.len == 0) "" else d.ptr, // TODO: does Windows allow ptr=undefined len=0 ? | 2029 | iovecs[len] = .{ |
| 2010 | .len = d.len, | 2030 | .base = buffered.ptr, |
| 2011 | }; | 2031 | .len = buffered.len, |
| | 2032 | }; |
| | 2033 | len += 1; |
| | 2034 | } |
| | 2035 | for (data[0..data.len]) |bytes| { |
| | 2036 | if (bytes.len == 0) continue; |
| | 2037 | iovecs[len] = .{ |
| | 2038 | .buf = bytes.ptr, |
| | 2039 | .len = bytes.len, |
| | 2040 | }; |
| | 2041 | len += 1; |
| | 2042 | } |
| | 2043 | const pattern = data[data.len - 1]; |
| 2012 | switch (splat) { | 2044 | switch (splat) { |
| 2013 | 0 => len -= 1, | 2045 | 0 => len -= 1, |
| 2014 | 1 => {}, | 2046 | 1 => {}, |
| 2015 | else => { | 2047 | else => switch (pattern.len) { |
| 2016 | const pattern = data[data.len - 1]; | 2048 | 0 => {}, |
| 2017 | if (pattern.len == 1) { | 2049 | 1 => { |
| | 2050 | // Replace the 1-byte buffer with a bigger one. |
| 2018 | const memset_len = @min(splat_buffer.len, splat); | 2051 | const memset_len = @min(splat_buffer.len, splat); |
| 2019 | const buf = splat_buffer[0..memset_len]; | 2052 | const buf = splat_buffer[0..memset_len]; |
| 2020 | @memset(buf, pattern[0]); | 2053 | @memset(buf, pattern[0]); |
| 2021 | iovecs[len - 1] = .{ .base = buf.ptr, .len = buf.len }; | 2054 | iovecs[len - 1] = .{ .buf = buf.ptr, .len = buf.len }; |
| 2022 | var remaining_splat = splat - buf.len; | 2055 | var remaining_splat = splat - buf.len; |
| 2023 | while (remaining_splat > splat_buffer.len and len < iovecs.len) { | 2056 | while (remaining_splat > splat_buffer.len and len < iovecs.len) { |
| 2024 | iovecs[len] = .{ .base = &splat_buffer, .len = splat_buffer.len }; | 2057 | iovecs[len] = .{ .buf = &splat_buffer, .len = splat_buffer.len }; |
| 2025 | remaining_splat -= splat_buffer.len; | 2058 | remaining_splat -= splat_buffer.len; |
| 2026 | len += 1; | 2059 | len += 1; |
| 2027 | } | 2060 | } |
| 2028 | if (remaining_splat > 0 and len < iovecs.len) { | 2061 | if (remaining_splat > 0 and len < iovecs.len) { |
| 2029 | iovecs[len] = .{ .base = &splat_buffer, .len = remaining_splat }; | 2062 | iovecs[len] = .{ .buf = &splat_buffer, .len = remaining_splat }; |
| 2030 | len += 1; | 2063 | len += 1; |
| 2031 | } | 2064 | } |
| 2032 | } | 2065 | }, |
| | 2066 | else => for (0..splat - 1) |_| { |
| | 2067 | if (iovecs.len - len == 0) break; |
| | 2068 | iovecs[len] = .{ |
| | 2069 | .buf = pattern.ptr, |
| | 2070 | .len = pattern.len, |
| | 2071 | }; |
| | 2072 | len += 1; |
| | 2073 | }, |
| 2033 | }, | 2074 | }, |
| 2034 | } | 2075 | } |
| 2035 | var n: u32 = undefined; | 2076 | var n: u32 = undefined; |
| 2036 | const rc = windows.ws2_32.WSASend(context, &iovecs, len, &n, 0, null, null); | 2077 | const rc = windows.ws2_32.WSASend(w.stream.handle, &iovecs, len, &n, 0, null, null); |
| 2037 | if (rc == windows.ws2_32.SOCKET_ERROR) switch (windows.ws2_32.WSAGetLastError()) { | 2078 | if (rc == windows.ws2_32.SOCKET_ERROR) switch (windows.ws2_32.WSAGetLastError()) { |
| 2038 | .WSAECONNABORTED => return error.ConnectionResetByPeer, | 2079 | .WSAECONNABORTED => return error.ConnectionResetByPeer, |
| 2039 | .WSAECONNRESET => return error.ConnectionResetByPeer, | 2080 | .WSAECONNRESET => return error.ConnectionResetByPeer, |
| ... | @@ -2054,123 +2095,127 @@ pub const Stream = struct { | ... | @@ -2054,123 +2095,127 @@ pub const Stream = struct { |
| 2054 | .WSA_OPERATION_ABORTED => unreachable, // not using overlapped I/O | 2095 | .WSA_OPERATION_ABORTED => unreachable, // not using overlapped I/O |
| 2055 | else => |err| return windows.unexpectedWSAError(err), | 2096 | else => |err| return windows.unexpectedWSAError(err), |
| 2056 | }; | 2097 | }; |
| 2057 | return n; | 2098 | return io_w.consume(n); |
| 2058 | } | | |
| 2059 | | | |
| 2060 | fn writeFile( | | |
| 2061 | context: *anyopaque, | | |
| 2062 | in_file: std.fs.File, | | |
| 2063 | in_offset: u64, | | |
| 2064 | in_len: std.io.Writer.FileLen, | | |
| 2065 | headers_and_trailers: []const []const u8, | | |
| 2066 | headers_len: usize, | | |
| 2067 | ) std.io.Writer.FileError!usize { | | |
| 2068 | const len_int = switch (in_len) { | | |
| 2069 | .zero => return writeSplat(context, headers_and_trailers, 1), | | |
| 2070 | .entire_file => std.math.maxInt(usize), | | |
| 2071 | else => in_len.int(), | | |
| 2072 | }; | | |
| 2073 | if (headers_len > 0) return writeSplat(context, headers_and_trailers[0..headers_len], 1); | | |
| 2074 | var file_contents_buffer: [4096]u8 = undefined; | | |
| 2075 | const read_buffer = file_contents_buffer[0..@min(file_contents_buffer.len, len_int)]; | | |
| 2076 | const n = try windows.ReadFile(in_file.handle, read_buffer, in_offset); | | |
| 2077 | return writeSplat(context, &.{read_buffer[0..n]}, 1); | | |
| 2078 | } | 2099 | } |
| 2079 | }, | 2100 | }, |
| 2080 | else => struct { | 2101 | else => struct { |
| 2081 | file_writer: std.fs.File.Writer, | 2102 | /// This field is present on all systems. |
| | 2103 | interface: io.Writer, |
| | 2104 | |
| 2082 | err: ?Error = null, | 2105 | err: ?Error = null, |
| | 2106 | file_writer: File.Writer, |
| 2083 | | 2107 | |
| 2084 | pub const Error = WriteError; | 2108 | pub const Error = WriteError; |
| 2085 | | 2109 | |
| 2086 | pub fn interface(w: *Writer) std.io.Writer { | 2110 | pub fn init(stream: Stream, buffer: []u8) Writer { |
| 2087 | return .{ | 2111 | return .{ |
| 2088 | .context = &w.file_writer, | 2112 | .interface = .{ |
| 2089 | .vtable = &.{ | 2113 | .context = undefined, |
| 2090 | .writeSplat = writeSplat, | 2114 | .vtable = &.{ |
| 2091 | .writeFile = std.fs.File.Writer.writeFile, | 2115 | .drain = drain, |
| | 2116 | .sendFile = sendFile, |
| | 2117 | }, |
| | 2118 | .buffer = buffer, |
| 2092 | }, | 2119 | }, |
| | 2120 | .file_writer = .initMode(stream.handle, &.{}, .streaming), |
| 2093 | }; | 2121 | }; |
| 2094 | } | 2122 | } |
| 2095 | | 2123 | |
| 2096 | fn writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize { | 2124 | pub fn getStream(w: *const Writer) Stream { |
| 2097 | const fw: *std.fs.File.Writer = @alignCast(@ptrCast(context)); | 2125 | return .{ .handle = w.file_writer.file.handle }; |
| 2098 | const w: *Writer = @fieldParentPtr("file_writer", fw); | 2126 | } |
| 2099 | var splat_buffer: [256]u8 = undefined; | 2127 | |
| | 2128 | fn drain(io_w: *io.Writer, data: []const []const u8, splat: usize) io.Writer.Error!usize { |
| | 2129 | const w: *Writer = @fieldParentPtr("interface", io_w); |
| | 2130 | const buffered = io_w.buffered(); |
| | 2131 | var splat_buffer: [splat_buffer_len]u8 = undefined; |
| 2100 | var iovecs: [max_buffers_len]std.posix.iovec_const = undefined; | 2132 | var iovecs: [max_buffers_len]std.posix.iovec_const = undefined; |
| 2101 | var len: usize = @min(iovecs.len, data.len); | 2133 | var msg: posix.msghdr_const = msg: { |
| 2102 | for (iovecs[0..len], data[0..len]) |*v, d| v.* = .{ | 2134 | var i: usize = 0; |
| 2103 | .base = if (d.len == 0) "" else d.ptr, // OS sadly checks ptr addr before length. | 2135 | if (buffered.len != 0) { |
| 2104 | .len = d.len, | 2136 | iovecs[i] = .{ |
| 2105 | }; | 2137 | .base = buffered.ptr, |
| 2106 | var msg: posix.msghdr_const = .{ | 2138 | .len = buffered.len, |
| 2107 | .name = null, | 2139 | }; |
| 2108 | .namelen = 0, | 2140 | i += 1; |
| 2109 | .iov = &iovecs, | 2141 | } |
| 2110 | .iovlen = len, | 2142 | for (data[0..data.len]) |bytes| { |
| 2111 | .control = null, | 2143 | // OS checks ptr addr before length so zero length vectors must be omitted. |
| 2112 | .controllen = 0, | 2144 | if (bytes.len == 0) continue; |
| 2113 | .flags = 0, | 2145 | iovecs[i] = .{ |
| | 2146 | .base = bytes.ptr, |
| | 2147 | .len = bytes.len, |
| | 2148 | }; |
| | 2149 | i += 1; |
| | 2150 | if (iovecs.len - i == 0) break; |
| | 2151 | } |
| | 2152 | break :msg .{ |
| | 2153 | .name = null, |
| | 2154 | .namelen = 0, |
| | 2155 | .iov = &iovecs, |
| | 2156 | .iovlen = i, |
| | 2157 | .control = null, |
| | 2158 | .controllen = 0, |
| | 2159 | .flags = 0, |
| | 2160 | }; |
| 2114 | }; | 2161 | }; |
| | 2162 | const pattern = data[data.len - 1]; |
| 2115 | switch (splat) { | 2163 | switch (splat) { |
| 2116 | 0 => msg.iovlen = len - 1, | 2164 | 0 => msg.iovlen -= 1, |
| 2117 | 1 => {}, | 2165 | 1 => {}, |
| 2118 | else => { | 2166 | else => switch (pattern.len) { |
| 2119 | const pattern = data[data.len - 1]; | 2167 | 0 => {}, |
| 2120 | if (pattern.len == 1) { | 2168 | 1 => { |
| | 2169 | // Replace the 1-byte buffer with a bigger one. |
| 2121 | const memset_len = @min(splat_buffer.len, splat); | 2170 | const memset_len = @min(splat_buffer.len, splat); |
| 2122 | const buf = splat_buffer[0..memset_len]; | 2171 | const buf = splat_buffer[0..memset_len]; |
| 2123 | @memset(buf, pattern[0]); | 2172 | @memset(buf, pattern[0]); |
| 2124 | iovecs[len - 1] = .{ .base = buf.ptr, .len = buf.len }; | 2173 | iovecs[msg.iovlen - 1] = .{ .base = buf.ptr, .len = buf.len }; |
| 2125 | var remaining_splat = splat - buf.len; | 2174 | var remaining_splat = splat - buf.len; |
| 2126 | while (remaining_splat > splat_buffer.len and len < iovecs.len) { | 2175 | while (remaining_splat > splat_buffer.len and msg.iovlen < iovecs.len) { |
| 2127 | iovecs[len] = .{ .base = &splat_buffer, .len = splat_buffer.len }; | 2176 | iovecs[msg.iovlen] = .{ .base = &splat_buffer, .len = splat_buffer.len }; |
| 2128 | remaining_splat -= splat_buffer.len; | 2177 | remaining_splat -= splat_buffer.len; |
| 2129 | len += 1; | 2178 | msg.iovlen += 1; |
| 2130 | } | 2179 | } |
| 2131 | if (remaining_splat > 0 and len < iovecs.len) { | 2180 | if (remaining_splat > 0 and msg.iovlen < iovecs.len) { |
| 2132 | iovecs[len] = .{ .base = &splat_buffer, .len = remaining_splat }; | 2181 | iovecs[msg.iovlen] = .{ .base = &splat_buffer, .len = remaining_splat }; |
| 2133 | len += 1; | 2182 | msg.iovlen += 1; |
| 2134 | } | 2183 | } |
| 2135 | msg.iovlen = len; | 2184 | }, |
| 2136 | } | 2185 | else => for (0..splat - 1) |_| { |
| | 2186 | if (iovecs.len - msg.iovlen == 0) break; |
| | 2187 | iovecs[msg.iovlen] = .{ |
| | 2188 | .base = pattern.ptr, |
| | 2189 | .len = pattern.len, |
| | 2190 | }; |
| | 2191 | msg.iovlen += 1; |
| | 2192 | }, |
| 2137 | }, | 2193 | }, |
| 2138 | } | 2194 | } |
| 2139 | const flags = posix.MSG.NOSIGNAL; | 2195 | const flags = posix.MSG.NOSIGNAL; |
| 2140 | return std.posix.sendmsg(fw.file.handle, &msg, flags) catch |err| { | 2196 | return io_w.consume(std.posix.sendmsg(w.file_writer.file.handle, &msg, flags) catch |err| { |
| 2141 | w.err = err; | 2197 | w.err = err; |
| 2142 | return error.WriteFailed; | 2198 | return error.WriteFailed; |
| 2143 | }; | 2199 | }); |
| 2144 | } | 2200 | } |
| 2145 | | 2201 | |
| 2146 | pub fn getStream(w: *const Writer) Stream { | 2202 | fn sendFile(io_w: *io.Writer, file_reader: *File.Reader, limit: io.Limit) io.Writer.FileError!usize { |
| 2147 | return .{ .handle = w.file_writer.file.handle }; | 2203 | const w: *Writer = @fieldParentPtr("interface", io_w); |
| | 2204 | return io_w.sendFileTo(&w.file_writer.interface, file_reader, limit); |
| 2148 | } | 2205 | } |
| 2149 | }, | 2206 | }, |
| 2150 | }; | 2207 | }; |
| 2151 | | 2208 | |
| 2152 | pub fn reader(stream: Stream) Reader { | 2209 | pub fn reader(stream: Stream, buffer: []u8) Reader { |
| 2153 | return switch (native_os) { | 2210 | return .init(stream, buffer); |
| 2154 | .windows => .{ .stream = stream }, | | |
| 2155 | else => .{ .file_reader = .{ | | |
| 2156 | .file = .{ .handle = stream.handle }, | | |
| 2157 | .mode = .streaming, | | |
| 2158 | .seek_err = error.Unseekable, | | |
| 2159 | } }, | | |
| 2160 | }; | | |
| 2161 | } | 2211 | } |
| 2162 | | 2212 | |
| 2163 | pub fn writer(stream: Stream) Writer { | 2213 | pub fn writer(stream: Stream, buffer: []u8) Writer { |
| 2164 | return switch (native_os) { | 2214 | return .init(stream, buffer); |
| 2165 | .windows => .{ .stream = stream }, | | |
| 2166 | else => .{ .file_writer = .{ | | |
| 2167 | .file = .{ .handle = stream.handle }, | | |
| 2168 | .mode = .streaming, | | |
| 2169 | } }, | | |
| 2170 | }; | | |
| 2171 | } | 2215 | } |
| 2172 | | 2216 | |
| 2173 | const max_buffers_len = 8; | 2217 | const max_buffers_len = 8; |
| | 2218 | const splat_buffer_len = 256; |
| 2174 | }; | 2219 | }; |
| 2175 | | 2220 | |
| 2176 | pub const Server = struct { | 2221 | pub const Server = struct { |