| 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 | 96 | /// atime, and mode of the source file so that the next call to `updateFile` will not need a copy. |
| 97 | 97 | /// Returns the previous status of the file before updating. |
| 98 | 98 | /// If any of the directories do not exist for dest_path, they are created. |
| 99 | /// TODO rework this to integrate with Dir | |
| 99 | 100 | pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus { |
| 100 | 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 | 142 | /// there is a possibility of power loss or application termination leaving temporary files present |
| 142 | 143 | /// in the same directory as dest_path. |
| 143 | 144 | /// Destination file will have the same mode as the source file. |
| 145 | /// TODO rework this to integrate with Dir | |
| 144 | 146 | pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void { |
| 145 | 147 | var in_file = try cwd().openFile(source_path, .{}); |
| 146 | 148 | defer in_file.close(); |
| 147 | 149 | |
| 148 | const mode = try in_file.mode(); | |
| 149 | const in_stream = &in_file.inStream().stream; | |
| 150 | const stat = try in_file.stat(); | |
| 150 | 151 | |
| 151 | var atomic_file = try AtomicFile.init(dest_path, mode); | |
| 152 | var atomic_file = try AtomicFile.init(dest_path, stat.mode); | |
| 152 | 153 | defer atomic_file.deinit(); |
| 153 | 154 | |
| 154 | var buf: [mem.page_size]u8 = undefined; | |
| 155 | while (true) { | |
| 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 | } | |
| 155 | try atomic_file.file.writeFileAll(in_file, .{ .in_len = stat.size }); | |
| 156 | return atomic_file.finish(); | |
| 162 | 157 | } |
| 163 | 158 | |
| 164 | /// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is | |
| 165 | /// merged and readily available, | |
| 159 | /// Guaranteed to be atomic. | |
| 160 | /// On Linux, until https://patchwork.kernel.org/patch/9636735/ is merged and readily available, | |
| 166 | 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 | 164 | pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void { |
| 168 | 165 | var in_file = try cwd().openFile(source_path, .{}); |
| 169 | 166 | defer in_file.close(); |
| ... | ... | @@ -171,14 +168,8 @@ pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.M |
| 171 | 168 | var atomic_file = try AtomicFile.init(dest_path, mode); |
| 172 | 169 | defer atomic_file.deinit(); |
| 173 | 170 | |
| 174 | var buf: [mem.page_size * 6]u8 = undefined; | |
| 175 | while (true) { | |
| 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 | } | |
| 171 | try atomic_file.file.writeFileAll(in_file, .{}); | |
| 172 | return atomic_file.finish(); | |
| 182 | 173 | } |
| 183 | 174 | |
| 184 | 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 | 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 | 256 | var index: usize = 0; |
| 255 | while (index < buffer.len) { | |
| 256 | index += try self.read(buffer[index..]); | |
| 257 | while (index != buffer.len) { | |
| 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 | 265 | pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize { |
| ... | ... | @@ -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 | 276 | var index: usize = 0; |
| 270 | while (index < buffer.len) { | |
| 271 | index += try self.pread(buffer[index..], offset + index); | |
| 277 | while (index != buffer.len) { | |
| 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 | 285 | pub fn readv(self: File, iovecs: []const os.iovec) ReadError!usize { |
| ... | ... | @@ -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 | 296 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in |
| 284 | 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 | 299 | if (iovecs.len == 0) return; |
| 287 | 300 | |
| 288 | 301 | var i: usize = 0; |
| 302 | var off: usize = 0; | |
| 289 | 303 | while (true) { |
| 290 | 304 | var amt = try self.readv(iovecs[i..]); |
| 305 | var eof = amt == 0; | |
| 306 | off += amt; | |
| 291 | 307 | while (amt >= iovecs[i].iov_len) { |
| 292 | 308 | amt -= iovecs[i].iov_len; |
| 293 | 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 | 314 | iovecs[i].iov_base += amt; |
| 297 | 315 | iovecs[i].iov_len -= amt; |
| 298 | 316 | } |
| ... | ... | @@ -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 | 330 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in |
| 310 | 331 | /// order to handle partial reads from the underlying OS layer. |
| 311 | 332 | pub fn preadvAll(self: File, iovecs: []const os.iovec, offset: u64) PReadError!void { |
| ... | ... | @@ -315,12 +336,15 @@ pub const File = struct { |
| 315 | 336 | var off: usize = 0; |
| 316 | 337 | while (true) { |
| 317 | 338 | var amt = try self.preadv(iovecs[i..], offset + off); |
| 339 | var eof = amt == 0; | |
| 318 | 340 | off += amt; |
| 319 | 341 | while (amt >= iovecs[i].iov_len) { |
| 320 | 342 | amt -= iovecs[i].iov_len; |
| 321 | 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 | 348 | iovecs[i].iov_base += amt; |
| 325 | 349 | iovecs[i].iov_len -= amt; |
| 326 | 350 | } |
lib/std/io/in_stream.zig+1-4| ... | ... | @@ -28,10 +28,7 @@ pub fn InStream( |
| 28 | 28 | return readFn(self.context, buffer); |
| 29 | 29 | } |
| 30 | 30 | |
| 31 | /// Deprecated: use `readAll`. | |
| 32 | pub const readFull = readAll; | |
| 33 | ||
| 34 | /// Returns the number of bytes read. If the number read is smaller than buf.len, it | |
| 31 | /// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it | |
| 35 | 32 | /// means the stream reached the end. Reaching the end of a stream is not an error |
| 36 | 33 | /// condition. |
| 37 | 34 | pub fn readAll(self: Self, buffer: []u8) Error!usize { |
lib/std/os/test.zig+29-3| ... | ... | @@ -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 | 99 | try dest_file.writeFileAll(src_file, .{ |
| 100 | 100 | .in_offset = 1, |
| 101 | 101 | .in_len = 10, |
| 102 | 102 | .headers_and_trailers = &hdtr, |
| 103 | 103 | .header_count = 2, |
| 104 | 104 | }); |
| 105 | try dest_file.preadAll(&written_buf, 0); | |
| 106 | expect(mem.eql(u8, &written_buf, "header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n")); | |
| 105 | const amt = try dest_file.preadAll(&written_buf, 0); | |
| 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 | 135 | test "std.Thread.getCurrentId" { |