@@ -1435,7 +1435,7 @@ pub const Reader = struct {
1435
1435
}
1436
1436
return 0 ;
1437
1437
};
1438
- const n = @min (size - pos , std . math . maxInt (i64 ), @intFromEnum (limit ));
1438
+ const n = @min (size - pos , maxInt (i64 ), @intFromEnum (limit ));
1439
1439
file .seekBy (n ) catch | err | {
1440
1440
r .seek_err = err ;
1441
1441
return 0 ;
@@ -1726,18 +1726,123 @@ pub const Writer = struct {
1726
1726
file_reader : * Reader ,
1727
1727
limit : std.io.Limit ,
1728
1728
) std.io.Writer.FileError ! usize {
1729
+ const reader_buffered = file_reader .interface .buffered ();
1730
+ if (reader_buffered .len >= @intFromEnum (limit ))
1731
+ return sendFileBuffered (io_w , file_reader , reader_buffered );
1732
+ const writer_buffered = io_w .buffered ();
1733
+ const file_limit = @intFromEnum (limit ) - reader_buffered .len ;
1729
1734
const w : * Writer = @alignCast (@fieldParentPtr ("interface" , io_w ));
1730
1735
const out_fd = w .file .handle ;
1731
1736
const in_fd = file_reader .file .handle ;
1732
- // TODO try using copy_file_range on FreeBSD
1733
- // TODO try using sendfile on macOS
1734
- // TODO try using sendfile on FreeBSD
1737
+
1738
+ if (native_os == .freebsd and w .mode == .streaming ) sf : {
1739
+ // Try using sendfile on FreeBSD.
1740
+ if (w .sendfile_err != null ) break :sf ;
1741
+ const offset = std .math .cast (std .c .off_t , file_reader .pos ) orelse break :sf ;
1742
+ var hdtr_data : std.c.sf_hdtr = undefined ;
1743
+ var headers : [2 ]posix.iovec_const = undefined ;
1744
+ var headers_i : u8 = 0 ;
1745
+ if (writer_buffered .len != 0 ) {
1746
+ headers [headers_i ] = .{ .base = writer_buffered .ptr , .len = writer_buffered .len };
1747
+ headers_i += 1 ;
1748
+ }
1749
+ if (reader_buffered .len != 0 ) {
1750
+ headers [headers_i ] = .{ .base = reader_buffered .ptr , .len = reader_buffered .len };
1751
+ headers_i += 1 ;
1752
+ }
1753
+ const hdtr : ? * std.c.sf_hdtr = if (headers_i == 0 ) null else b : {
1754
+ hdtr_data = .{
1755
+ .headers = & headers ,
1756
+ .hdr_cnt = headers_i ,
1757
+ .trailers = null ,
1758
+ .trl_cnt = 0 ,
1759
+ };
1760
+ break :b & hdtr_data ;
1761
+ };
1762
+ var sbytes : std.c.off_t = undefined ;
1763
+ const nbytes : usize = @min (file_limit , maxInt (usize ));
1764
+ const flags = 0 ;
1765
+ switch (posix .errno (std .c .sendfile (in_fd , out_fd , offset , nbytes , hdtr , & sbytes , flags ))) {
1766
+ .SUCCESS , .INTR = > {},
1767
+ .INVAL , .OPNOTSUPP , .NOTSOCK , .NOSYS = > w .sendfile_err = error .UnsupportedOperation ,
1768
+ .BADF = > if (builtin .mode == .Debug ) @panic ("race condition" ) else {
1769
+ w .sendfile_err = error .Unexpected ;
1770
+ },
1771
+ .FAULT = > if (builtin .mode == .Debug ) @panic ("segmentation fault" ) else {
1772
+ w .sendfile_err = error .Unexpected ;
1773
+ },
1774
+ .NOTCONN = > w .sendfile_err = error .BrokenPipe ,
1775
+ .AGAIN , .BUSY = > if (sbytes == 0 ) {
1776
+ w .sendfile_err = error .WouldBlock ;
1777
+ },
1778
+ .IO = > w .sendfile_err = error .InputOutput ,
1779
+ .PIPE = > w .sendfile_err = error .BrokenPipe ,
1780
+ .NOBUFS = > w .sendfile_err = error .SystemResources ,
1781
+ else = > | err | w .sendfile_err = posix .unexpectedErrno (err ),
1782
+ }
1783
+ const consumed = io_w .consume (@bitCast (sbytes ));
1784
+ file_reader .seekTo (file_reader .pos + consumed ) catch return error .ReadFailed ;
1785
+ return consumed ;
1786
+ }
1787
+
1788
+ if (native_os .isDarwin () and w .mode == .streaming ) sf : {
1789
+ // Try using sendfile on macOS.
1790
+ if (w .sendfile_err != null ) break :sf ;
1791
+ const offset = std .math .cast (std .c .off_t , file_reader .pos ) orelse break :sf ;
1792
+ var hdtr_data : std.c.sf_hdtr = undefined ;
1793
+ var headers : [2 ]posix.iovec_const = undefined ;
1794
+ var headers_i : u8 = 0 ;
1795
+ if (writer_buffered .len != 0 ) {
1796
+ headers [headers_i ] = .{ .base = writer_buffered .ptr , .len = writer_buffered .len };
1797
+ headers_i += 1 ;
1798
+ }
1799
+ if (reader_buffered .len != 0 ) {
1800
+ headers [headers_i ] = .{ .base = reader_buffered .ptr , .len = reader_buffered .len };
1801
+ headers_i += 1 ;
1802
+ }
1803
+ const hdtr : ? * std.c.sf_hdtr = if (headers_i == 0 ) null else b : {
1804
+ hdtr_data = .{
1805
+ .headers = & headers ,
1806
+ .hdr_cnt = headers_i ,
1807
+ .trailers = null ,
1808
+ .trl_cnt = 0 ,
1809
+ };
1810
+ break :b & hdtr_data ;
1811
+ };
1812
+ const max_count = maxInt (i32 ); // Avoid EINVAL.
1813
+ var sbytes : std.c.off_t = @min (file_limit , max_count );
1814
+ const flags = 0 ;
1815
+ switch (posix .errno (std .c .sendfile (in_fd , out_fd , offset , & sbytes , hdtr , flags ))) {
1816
+ .SUCCESS , .INTR = > {},
1817
+ .OPNOTSUPP , .NOTSOCK , .NOSYS = > w .sendfile_err = error .UnsupportedOperation ,
1818
+ .BADF = > if (builtin .mode == .Debug ) @panic ("race condition" ) else {
1819
+ w .sendfile_err = error .Unexpected ;
1820
+ },
1821
+ .FAULT = > if (builtin .mode == .Debug ) @panic ("segmentation fault" ) else {
1822
+ w .sendfile_err = error .Unexpected ;
1823
+ },
1824
+ .INVAL = > if (builtin .mode == .Debug ) @panic ("invalid API usage" ) else {
1825
+ w .sendfile_err = error .Unexpected ;
1826
+ },
1827
+ .NOTCONN = > w .sendfile_err = error .BrokenPipe ,
1828
+ .AGAIN = > if (sbytes == 0 ) {
1829
+ w .sendfile_err = error .WouldBlock ;
1830
+ },
1831
+ .IO = > w .sendfile_err = error .InputOutput ,
1832
+ .PIPE = > w .sendfile_err = error .BrokenPipe ,
1833
+ else = > | err | w .sendfile_err = posix .unexpectedErrno (err ),
1834
+ }
1835
+ const consumed = io_w .consume (@bitCast (sbytes ));
1836
+ file_reader .seekTo (file_reader .pos + consumed ) catch return error .ReadFailed ;
1837
+ return consumed ;
1838
+ }
1839
+
1735
1840
if (native_os == .linux and w .mode == .streaming ) sf : {
1736
1841
// Try using sendfile on Linux.
1737
1842
if (w .sendfile_err != null ) break :sf ;
1738
1843
// Linux sendfile does not support headers.
1739
- const buffered = limit . slice ( file_reader . interface . buffer );
1740
- if ( io_w . end != 0 or buffered . len != 0 ) return drain (io_w , &.{ buffered }, 1 );
1844
+ if ( writer_buffered . len != 0 or reader_buffered . len != 0 )
1845
+ return sendFileBuffered (io_w , file_reader , reader_buffered );
1741
1846
const max_count = 0x7ffff000 ; // Avoid EINVAL.
1742
1847
var off : std.os.linux.off_t = undefined ;
1743
1848
const off_ptr : ? * std.os.linux.off_t , const count : usize = switch (file_reader .mode ) {
@@ -1784,15 +1889,16 @@ pub const Writer = struct {
1784
1889
w .pos += n ;
1785
1890
return n ;
1786
1891
}
1892
+
1787
1893
const copy_file_range = switch (native_os ) {
1788
1894
.freebsd = > std .os .freebsd .copy_file_range ,
1789
1895
.linux = > if (std .c .versionCheck (.{ .major = 2 , .minor = 27 , .patch = 0 })) std .os .linux .wrapped .copy_file_range else {},
1790
1896
else = > {},
1791
1897
};
1792
1898
if (@TypeOf (copy_file_range ) != void ) cfr : {
1793
1899
if (w .copy_file_range_err != null ) break :cfr ;
1794
- const buffered = limit . slice ( file_reader . interface . buffer );
1795
- if ( io_w . end != 0 or buffered . len != 0 ) return drain (io_w , &.{ buffered }, 1 );
1900
+ if ( writer_buffered . len != 0 or reader_buffered . len != 0 )
1901
+ return sendFileBuffered (io_w , file_reader , reader_buffered );
1796
1902
var off_in : i64 = undefined ;
1797
1903
var off_out : i64 = undefined ;
1798
1904
const off_in_ptr : ? * i64 = switch (file_reader .mode ) {
@@ -1832,6 +1938,8 @@ pub const Writer = struct {
1832
1938
if (w .pos != 0 ) break :fcf ;
1833
1939
if (limit != .unlimited ) break :fcf ;
1834
1940
const size = file_reader .getSize () catch break :fcf ;
1941
+ if (writer_buffered .len != 0 or reader_buffered .len != 0 )
1942
+ return sendFileBuffered (io_w , file_reader , reader_buffered );
1835
1943
const rc = std .c .fcopyfile (in_fd , out_fd , null , .{ .DATA = true });
1836
1944
switch (posix .errno (rc )) {
1837
1945
.SUCCESS = > {},
@@ -1860,6 +1968,16 @@ pub const Writer = struct {
1860
1968
return error .Unimplemented ;
1861
1969
}
1862
1970
1971
+ fn sendFileBuffered (
1972
+ io_w : * std.io.Writer ,
1973
+ file_reader : * Reader ,
1974
+ reader_buffered : []const u8 ,
1975
+ ) std.io.Writer.FileError ! usize {
1976
+ const n = try drain (io_w , &.{reader_buffered }, 1 );
1977
+ file_reader .seekTo (file_reader .pos + n ) catch return error .ReadFailed ;
1978
+ return n ;
1979
+ }
1980
+
1863
1981
pub fn seekTo (w : * Writer , offset : u64 ) SeekError ! void {
1864
1982
switch (w .mode ) {
1865
1983
.positional , .positional_reading = > {
0 commit comments