| author | |
| committer | |
| log | d96b6c0d9f7a4fc6e7e28dd92d18e548f00885de |
| tree | 1c2774d9d9ffe279fa1ae330eec529dbdc9cb994 |
| parent | ed13cffca4c42718cc7239faf7aab642ac67ef77 |
| signature |
4 files changed, 75 insertions(+), 37 deletions(-)
lib/std/fs.zig+12-21| ... | @@ -96,6 +96,7 @@ pub fn updateFile(source_path: []const u8, dest_path: []const u8) !PrevStatus { | ... | @@ -96,6 +96,7 @@ pub fn updateFile(source_path: []const u8, dest_path: []const u8) !PrevStatus { |
| 96 | /// atime, and mode of the source file so that the next call to `updateFile` will not need a copy. | 96 | /// atime, and mode of the source file so that the next call to `updateFile` will not need a copy. |
| 97 | /// Returns the previous status of the file before updating. | 97 | /// Returns the previous status of the file before updating. |
| 98 | /// If any of the directories do not exist for dest_path, they are created. | 98 | /// If any of the directories do not exist for dest_path, they are created. |
| 99 | /// TODO rework this to integrate with Dir | ||
| 99 | pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus { | 100 | pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus { |
| 100 | const my_cwd = cwd(); | 101 | const my_cwd = cwd(); |
| 101 | 102 | ||
| ... | @@ -141,29 +142,25 @@ pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?Fil | ... | @@ -141,29 +142,25 @@ pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?Fil |
| 141 | /// there is a possibility of power loss or application termination leaving temporary files present | 142 | /// there is a possibility of power loss or application termination leaving temporary files present |
| 142 | /// in the same directory as dest_path. | 143 | /// in the same directory as dest_path. |
| 143 | /// Destination file will have the same mode as the source file. | 144 | /// Destination file will have the same mode as the source file. |
| 145 | /// TODO rework this to integrate with Dir | ||
| 144 | pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void { | 146 | pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void { |
| 145 | var in_file = try cwd().openFile(source_path, .{}); | 147 | var in_file = try cwd().openFile(source_path, .{}); |
| 146 | defer in_file.close(); | 148 | defer in_file.close(); |
| 147 | 149 | ||
| 148 | const mode = try in_file.mode(); | 150 | const stat = try in_file.stat(); |
| 149 | const in_stream = &in_file.inStream().stream; | ||
| 150 | 151 | ||
| 151 | var atomic_file = try AtomicFile.init(dest_path, mode); | 152 | var atomic_file = try AtomicFile.init(dest_path, stat.mode); |
| 152 | defer atomic_file.deinit(); | 153 | defer atomic_file.deinit(); |
| 153 | 154 | ||
| 154 | var buf: [mem.page_size]u8 = undefined; | 155 | try atomic_file.file.writeFileAll(in_file, .{ .in_len = stat.size }); |
| 155 | while (true) { | 156 | return atomic_file.finish(); |
| 156 | const amt = try in_stream.readFull(buf[0..]); | ||
| 157 | try atomic_file.file.write(buf[0..amt]); | ||
| 158 | if (amt != buf.len) { | ||
| 159 | return atomic_file.finish(); | ||
| 160 | } | ||
| 161 | } | ||
| 162 | } | 157 | } |
| 163 | 158 | ||
| 164 | /// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is | 159 | /// Guaranteed to be atomic. |
| 165 | /// merged and readily available, | 160 | /// On Linux, until https://patchwork.kernel.org/patch/9636735/ is merged and readily available, |
| 166 | /// there is a possibility of power loss or application termination leaving temporary files present | 161 | /// there is a possibility of power loss or application termination leaving temporary files present |
| 162 | /// in the same directory as dest_path. | ||
| 163 | /// TODO rework this to integrate with Dir | ||
| 167 | pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void { | 164 | pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void { |
| 168 | var in_file = try cwd().openFile(source_path, .{}); | 165 | var in_file = try cwd().openFile(source_path, .{}); |
| 169 | defer in_file.close(); | 166 | defer in_file.close(); |
| ... | @@ -171,14 +168,8 @@ pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.M | ... | @@ -171,14 +168,8 @@ pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.M |
| 171 | var atomic_file = try AtomicFile.init(dest_path, mode); | 168 | var atomic_file = try AtomicFile.init(dest_path, mode); |
| 172 | defer atomic_file.deinit(); | 169 | defer atomic_file.deinit(); |
| 173 | 170 | ||
| 174 | var buf: [mem.page_size * 6]u8 = undefined; | 171 | try atomic_file.file.writeFileAll(in_file, .{}); |
| 175 | while (true) { | 172 | return atomic_file.finish(); |
| 176 | const amt = try in_file.read(buf[0..]); | ||
| 177 | try atomic_file.file.write(buf[0..amt]); | ||
| 178 | if (amt != buf.len) { | ||
| 179 | return atomic_file.finish(); | ||
| 180 | } | ||
| 181 | } | ||
| 182 | } | 173 | } |
| 183 | 174 | ||
| 184 | /// TODO update this API to avoid a getrandom syscall for every operation. It | 175 | /// TODO update this API to avoid a getrandom syscall for every operation. It |
lib/std/fs/file.zig+33-9| ... | @@ -250,11 +250,16 @@ pub const File = struct { | ... | @@ -250,11 +250,16 @@ pub const File = struct { |
| 250 | } | 250 | } |
| 251 | } | 251 | } |
| 252 | 252 | ||
| 253 | pub fn readAll(self: File, buffer: []u8) ReadError!void { | 253 | /// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it |
| 254 | /// means the file reached the end. Reaching the end of a file is not an error condition. | ||
| 255 | pub fn readAll(self: File, buffer: []u8) ReadError!usize { | ||
| 254 | var index: usize = 0; | 256 | var index: usize = 0; |
| 255 | while (index < buffer.len) { | 257 | while (index != buffer.len) { |
| 256 | index += try self.read(buffer[index..]); | 258 | const amt = try self.read(buffer[index..]); |
| 259 | if (amt == 0) break; | ||
| 260 | index += amt; | ||
| 257 | } | 261 | } |
| 262 | return index; | ||
| 258 | } | 263 | } |
| 259 | 264 | ||
| 260 | pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize { | 265 | pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize { |
| ... | @@ -265,11 +270,16 @@ pub const File = struct { | ... | @@ -265,11 +270,16 @@ pub const File = struct { |
| 265 | } | 270 | } |
| 266 | } | 271 | } |
| 267 | 272 | ||
| 268 | pub fn preadAll(self: File, buffer: []u8, offset: u64) PReadError!void { | 273 | /// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it |
| 274 | /// means the file reached the end. Reaching the end of a file is not an error condition. | ||
| 275 | pub fn preadAll(self: File, buffer: []u8, offset: u64) PReadError!usize { | ||
| 269 | var index: usize = 0; | 276 | var index: usize = 0; |
| 270 | while (index < buffer.len) { | 277 | while (index != buffer.len) { |
| 271 | index += try self.pread(buffer[index..], offset + index); | 278 | const amt = try self.pread(buffer[index..], offset + index); |
| 279 | if (amt == 0) break; | ||
| 280 | index += amt; | ||
| 272 | } | 281 | } |
| 282 | return index; | ||
| 273 | } | 283 | } |
| 274 | 284 | ||
| 275 | pub fn readv(self: File, iovecs: []const os.iovec) ReadError!usize { | 285 | pub fn readv(self: File, iovecs: []const os.iovec) ReadError!usize { |
| ... | @@ -280,19 +290,27 @@ pub const File = struct { | ... | @@ -280,19 +290,27 @@ pub const File = struct { |
| 280 | } | 290 | } |
| 281 | } | 291 | } |
| 282 | 292 | ||
| 293 | /// Returns the number of bytes read. If the number read is smaller than the total bytes | ||
| 294 | /// from all the buffers, it means the file reached the end. Reaching the end of a file | ||
| 295 | /// is not an error condition. | ||
| 283 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in | 296 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in |
| 284 | /// order to handle partial reads from the underlying OS layer. | 297 | /// order to handle partial reads from the underlying OS layer. |
| 285 | pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!void { | 298 | pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!usize { |
| 286 | if (iovecs.len == 0) return; | 299 | if (iovecs.len == 0) return; |
| 287 | 300 | ||
| 288 | var i: usize = 0; | 301 | var i: usize = 0; |
| 302 | var off: usize = 0; | ||
| 289 | while (true) { | 303 | while (true) { |
| 290 | var amt = try self.readv(iovecs[i..]); | 304 | var amt = try self.readv(iovecs[i..]); |
| 305 | var eof = amt == 0; | ||
| 306 | off += amt; | ||
| 291 | while (amt >= iovecs[i].iov_len) { | 307 | while (amt >= iovecs[i].iov_len) { |
| 292 | amt -= iovecs[i].iov_len; | 308 | amt -= iovecs[i].iov_len; |
| 293 | i += 1; | 309 | i += 1; |
| 294 | if (i >= iovecs.len) return; | 310 | if (i >= iovecs.len) return off; |
| 311 | eof = false; | ||
| 295 | } | 312 | } |
| 313 | if (eof) return off; | ||
| 296 | iovecs[i].iov_base += amt; | 314 | iovecs[i].iov_base += amt; |
| 297 | iovecs[i].iov_len -= amt; | 315 | iovecs[i].iov_len -= amt; |
| 298 | } | 316 | } |
| ... | @@ -306,6 +324,9 @@ pub const File = struct { | ... | @@ -306,6 +324,9 @@ pub const File = struct { |
| 306 | } | 324 | } |
| 307 | } | 325 | } |
| 308 | 326 | ||
| 327 | /// Returns the number of bytes read. If the number read is smaller than the total bytes | ||
| 328 | /// from all the buffers, it means the file reached the end. Reaching the end of a file | ||
| 329 | /// is not an error condition. | ||
| 309 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in | 330 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in |
| 310 | /// order to handle partial reads from the underlying OS layer. | 331 | /// order to handle partial reads from the underlying OS layer. |
| 311 | pub fn preadvAll(self: File, iovecs: []const os.iovec, offset: u64) PReadError!void { | 332 | pub fn preadvAll(self: File, iovecs: []const os.iovec, offset: u64) PReadError!void { |
| ... | @@ -315,12 +336,15 @@ pub const File = struct { | ... | @@ -315,12 +336,15 @@ pub const File = struct { |
| 315 | var off: usize = 0; | 336 | var off: usize = 0; |
| 316 | while (true) { | 337 | while (true) { |
| 317 | var amt = try self.preadv(iovecs[i..], offset + off); | 338 | var amt = try self.preadv(iovecs[i..], offset + off); |
| 339 | var eof = amt == 0; | ||
| 318 | off += amt; | 340 | off += amt; |
| 319 | while (amt >= iovecs[i].iov_len) { | 341 | while (amt >= iovecs[i].iov_len) { |
| 320 | amt -= iovecs[i].iov_len; | 342 | amt -= iovecs[i].iov_len; |
| 321 | i += 1; | 343 | i += 1; |
| 322 | if (i >= iovecs.len) return; | 344 | if (i >= iovecs.len) return off; |
| 345 | eof = false; | ||
| 323 | } | 346 | } |
| 347 | if (eof) return off; | ||
| 324 | iovecs[i].iov_base += amt; | 348 | iovecs[i].iov_base += amt; |
| 325 | iovecs[i].iov_len -= amt; | 349 | iovecs[i].iov_len -= amt; |
| 326 | } | 350 | } |
lib/std/io/in_stream.zig+1-4| ... | @@ -28,10 +28,7 @@ pub fn InStream( | ... | @@ -28,10 +28,7 @@ pub fn InStream( |
| 28 | return readFn(self.context, buffer); | 28 | return readFn(self.context, buffer); |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | /// Deprecated: use `readAll`. | 31 | /// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it |
| 32 | pub const readFull = readAll; | ||
| 33 | |||
| 34 | /// Returns the number of bytes read. If the number read is smaller than buf.len, it | ||
| 35 | /// means the stream reached the end. Reaching the end of a stream is not an error | 32 | /// means the stream reached the end. Reaching the end of a stream is not an error |
| 36 | /// condition. | 33 | /// condition. |
| 37 | pub fn readAll(self: Self, buffer: []u8) Error!usize { | 34 | pub fn readAll(self: Self, buffer: []u8) Error!usize { |
lib/std/os/test.zig+29-3| ... | @@ -95,15 +95,41 @@ test "sendfile" { | ... | @@ -95,15 +95,41 @@ test "sendfile" { |
| 95 | }, | 95 | }, |
| 96 | }; | 96 | }; |
| 97 | 97 | ||
| 98 | var written_buf: [header1.len + header2.len + 10 + trailer1.len + trailer2.len]u8 = undefined; | 98 | var written_buf: [100]u8 = undefined; |
| 99 | try dest_file.writeFileAll(src_file, .{ | 99 | try dest_file.writeFileAll(src_file, .{ |
| 100 | .in_offset = 1, | 100 | .in_offset = 1, |
| 101 | .in_len = 10, | 101 | .in_len = 10, |
| 102 | .headers_and_trailers = &hdtr, | 102 | .headers_and_trailers = &hdtr, |
| 103 | .header_count = 2, | 103 | .header_count = 2, |
| 104 | }); | 104 | }); |
| 105 | try dest_file.preadAll(&written_buf, 0); | 105 | const amt = try dest_file.preadAll(&written_buf, 0); |
| 106 | expect(mem.eql(u8, &written_buf, "header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n")); | 106 | expect(mem.eql(u8, written_buf[0..amt], "header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n")); |
| 107 | } | ||
| 108 | |||
| 109 | test "fs.copyFile" { | ||
| 110 | const data = "u6wj+JmdF3qHsFPE BUlH2g4gJCmEz0PP"; | ||
| 111 | const src_file = "tmp_test_copy_file.txt"; | ||
| 112 | const dest_file = "tmp_test_copy_file2.txt"; | ||
| 113 | const dest_file2 = "tmp_test_copy_file3.txt"; | ||
| 114 | |||
| 115 | try fs.cwd().writeFile(src_file, data); | ||
| 116 | defer fs.cwd().deleteFile(src_file) catch {}; | ||
| 117 | |||
| 118 | try fs.copyFile(src_file, dest_file); | ||
| 119 | defer fs.cwd().deleteFile(dest_file) catch {}; | ||
| 120 | |||
| 121 | try fs.copyFileMode(src_file, dest_file2, File.default_mode); | ||
| 122 | defer fs.cwd().deleteFile(dest_file2) catch {}; | ||
| 123 | |||
| 124 | try expectFileContents(dest_file, data); | ||
| 125 | try expectFileContents(dest_file2, data); | ||
| 126 | } | ||
| 127 | |||
| 128 | fn expectFileContents(file_path: []const u8, data: []const u8) !void { | ||
| 129 | const contents = try fs.cwd().readFileAlloc(testing.allocator, file_path, 1000); | ||
| 130 | defer testing.allocator.free(contents); | ||
| 131 | |||
| 132 | testing.expectEqualSlices(u8, data, contents); | ||
| 107 | } | 133 | } |
| 108 | 134 | ||
| 109 | test "std.Thread.getCurrentId" { | 135 | test "std.Thread.getCurrentId" { |