authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-05-28 19:07:53-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-01 16:35:29-07:00
log2ed47f1ed813d786e4d7d4adf18bd95d81e8e931
tree3bb6746f15df8406b70cd717e10a8b45fa7f4fda
parentba684a18ca264463a29eb5dcd0666c07a6188538

std: update AtomicFile to new API


7 files changed, 36 insertions(+), 160 deletions(-)

lib/std/compress/flate/Decompress.zig+1-18
...@@ -345,19 +345,6 @@ fn readInner(...@@ -345,19 +345,6 @@ fn readInner(
345 }345 }
346}346}
347347
348fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
349 _ = context;
350 _ = data;
351 @panic("TODO remove readVec primitive");
352}
353
354fn discard(context: ?*anyopaque, limit: std.io.Limit) std.io.Reader.Error!usize {
355 _ = context;
356 _ = limit;
357 // Problem here is we still need access to the output ring buffer.
358 @panic("TODO allow discard to be null");
359}
360
361/// Write match (back-reference to the same data slice) starting at `distance`348/// Write match (back-reference to the same data slice) starting at `distance`
362/// back from current write position, and `length` of bytes.349/// back from current write position, and `length` of bytes.
363fn writeMatch(bw: *std.io.BufferedWriter, length: u16, distance: u16) !void {350fn writeMatch(bw: *std.io.BufferedWriter, length: u16, distance: u16) !void {
...@@ -370,11 +357,7 @@ fn writeMatch(bw: *std.io.BufferedWriter, length: u16, distance: u16) !void {...@@ -370,11 +357,7 @@ fn writeMatch(bw: *std.io.BufferedWriter, length: u16, distance: u16) !void {
370pub fn reader(self: *Decompress) std.io.Reader {357pub fn reader(self: *Decompress) std.io.Reader {
371 return .{358 return .{
372 .context = self,359 .context = self,
373 .vtable = &.{360 .vtable = &.{ .read = read },
374 .read = read,
375 .readVec = readVec,
376 .discard = discard,
377 },
378 };361 };
379}362}
380363
lib/std/fs/AtomicFile.zig+13-13
...@@ -1,4 +1,12 @@...@@ -1,4 +1,12 @@
1file: File,1const AtomicFile = @This();
2const std = @import("../std.zig");
3const File = std.fs.File;
4const Dir = std.fs.Dir;
5const fs = std.fs;
6const assert = std.debug.assert;
7const posix = std.posix;
8
9file_writer: File.Writer,
2// TODO either replace this with rand_buf or use []u16 on Windows10// TODO either replace this with rand_buf or use []u16 on Windows
3tmp_path_buf: [tmp_path_len:0]u8,11tmp_path_buf: [tmp_path_len:0]u8,
4dest_basename: []const u8,12dest_basename: []const u8,
...@@ -35,8 +43,8 @@ pub fn init(...@@ -35,8 +43,8 @@ pub fn init(
35 else => |e| return e,43 else => |e| return e,
36 };44 };
3745
38 return AtomicFile{46 return .{
39 .file = file,47 .file_writer = file.writer(),
40 .tmp_path_buf = tmp_path_buf,48 .tmp_path_buf = tmp_path_buf,
41 .dest_basename = dest_basename,49 .dest_basename = dest_basename,
42 .file_open = true,50 .file_open = true,
...@@ -50,7 +58,7 @@ pub fn init(...@@ -50,7 +58,7 @@ pub fn init(
50/// Always call deinit, even after a successful finish().58/// Always call deinit, even after a successful finish().
51pub fn deinit(self: *AtomicFile) void {59pub fn deinit(self: *AtomicFile) void {
52 if (self.file_open) {60 if (self.file_open) {
53 self.file.close();61 self.file_writer.file.close();
54 self.file_open = false;62 self.file_open = false;
55 }63 }
56 if (self.file_exists) {64 if (self.file_exists) {
...@@ -72,17 +80,9 @@ pub const FinishError = posix.RenameError;...@@ -72,17 +80,9 @@ pub const FinishError = posix.RenameError;
72pub fn finish(self: *AtomicFile) FinishError!void {80pub fn finish(self: *AtomicFile) FinishError!void {
73 assert(self.file_exists);81 assert(self.file_exists);
74 if (self.file_open) {82 if (self.file_open) {
75 self.file.close();83 self.file_writer.file.close();
76 self.file_open = false;84 self.file_open = false;
77 }85 }
78 try posix.renameat(self.dir.fd, self.tmp_path_buf[0..], self.dir.fd, self.dest_basename);86 try posix.renameat(self.dir.fd, self.tmp_path_buf[0..], self.dir.fd, self.dest_basename);
79 self.file_exists = false;87 self.file_exists = false;
80}88}
81
82const AtomicFile = @This();
83const std = @import("../std.zig");
84const File = std.fs.File;
85const Dir = std.fs.Dir;
86const fs = std.fs;
87const assert = std.debug.assert;
88const posix = std.posix;
lib/std/fs/Dir.zig+12-5
...@@ -2612,11 +2612,18 @@ pub fn updateFile(...@@ -2612,11 +2612,18 @@ pub fn updateFile(
2612 var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = actual_mode });2612 var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = actual_mode });
2613 defer atomic_file.deinit();2613 defer atomic_file.deinit();
26142614
2615 try atomic_file.file.writeFileAll(src_file, .{2615 var src_reader: File.Reader = .{
2616 .offset = .zero,2616 .file = src_file,
2617 .limit = .limited(src_stat.size),2617 .size = src_stat.size,
2618 });2618 };
2619 try atomic_file.file.updateTimes(src_stat.atime, src_stat.mtime);2619 var buffer: [2000]u8 = undefined;
2620 var dest_writer = atomic_file.file_writer.writable(&buffer);
2621
2622 dest_writer.writeFileAll(&src_reader, .{}) catch |err| switch (err) {
2623 error.ReadFailed => return src_reader.err.?,
2624 error.WriteFailed => return atomic_file.file_writer.err.?,
2625 };
2626 try atomic_file.file_writer.file.updateTimes(src_stat.atime, src_stat.mtime);
2620 try atomic_file.finish();2627 try atomic_file.finish();
2621 return .stale;2628 return .stale;
2622}2629}
lib/std/fs/File.zig-12
...@@ -887,18 +887,6 @@ pub fn pwritev(self: File, iovecs: []posix.iovec_const, offset: u64) PWriteError...@@ -887,18 +887,6 @@ pub fn pwritev(self: File, iovecs: []posix.iovec_const, offset: u64) PWriteError
887 return posix.pwritev(self.handle, iovecs, offset);887 return posix.pwritev(self.handle, iovecs, offset);
888}888}
889889
890pub const WriteFileError = PReadError || WriteError;
891
892pub fn writeFileAll(self: File, in_file: File, options: BufferedWriter.WriteFileOptions) WriteFileError!void {
893 var file_writer = self.writer();
894 var buffer: [2000]u8 = undefined;
895 var bw = file_writer.interface().buffered(&buffer);
896 bw.writeFileAll(in_file, options) catch |err| switch (err) {
897 error.WriteFailed => return file_writer.err.?,
898 else => |e| return e,
899 };
900}
901
902/// Memoizes key information about a file handle such as:890/// Memoizes key information about a file handle such as:
903/// * The size from calling stat, or the error that occurred therein.891/// * The size from calling stat, or the error that occurred therein.
904/// * The current seek position.892/// * The current seek position.
lib/std/http.zig+4-109
...@@ -439,9 +439,8 @@ pub const Reader = struct {...@@ -439,9 +439,8 @@ pub const Reader = struct {
439 return .{439 return .{
440 .context = reader,440 .context = reader,
441 .vtable = &.{441 .vtable = &.{
442 .read = &chunkedRead,442 .read = chunkedRead,
443 .readVec = &chunkedReadVec,443 .discard = chunkedDiscard,
444 .discard = &chunkedDiscard,
445 },444 },
446 };445 };
447 },446 },
...@@ -451,9 +450,8 @@ pub const Reader = struct {...@@ -451,9 +450,8 @@ pub const Reader = struct {
451 return .{450 return .{
452 .context = reader,451 .context = reader,
453 .vtable = &.{452 .vtable = &.{
454 .read = &contentLengthRead,453 .read = contentLengthRead,
455 .readVec = &contentLengthReadVec,454 .discard = contentLengthDiscard,
456 .discard = &contentLengthDiscard,
457 },455 },
458 };456 };
459 } else {457 } else {
...@@ -521,19 +519,6 @@ pub const Reader = struct {...@@ -521,19 +519,6 @@ pub const Reader = struct {
521 return n;519 return n;
522 }520 }
523521
524 fn contentLengthReadVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
525 const reader: *Reader = @alignCast(@ptrCast(context));
526 const remaining_content_length = &reader.state.body_remaining_content_length;
527 const remaining = remaining_content_length.*;
528 if (remaining == 0) {
529 reader.state = .ready;
530 return error.EndOfStream;
531 }
532 const n = try reader.in.readVecLimit(data, .limited(remaining));
533 remaining_content_length.* = remaining - n;
534 return n;
535 }
536
537 fn contentLengthDiscard(ctx: ?*anyopaque, limit: std.io.Limit) std.io.Reader.Error!usize {522 fn contentLengthDiscard(ctx: ?*anyopaque, limit: std.io.Limit) std.io.Reader.Error!usize {
538 const reader: *Reader = @alignCast(@ptrCast(ctx));523 const reader: *Reader = @alignCast(@ptrCast(ctx));
539 const remaining_content_length = &reader.state.body_remaining_content_length;524 const remaining_content_length = &reader.state.body_remaining_content_length;
...@@ -621,96 +606,6 @@ pub const Reader = struct {...@@ -621,96 +606,6 @@ pub const Reader = struct {
621 }606 }
622 }607 }
623608
624 fn chunkedReadVec(ctx: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
625 const reader: *Reader = @alignCast(@ptrCast(ctx));
626 const chunk_len_ptr = switch (reader.state) {
627 .ready => return error.EndOfStream,
628 .body_remaining_chunk_len => |*x| x,
629 else => unreachable,
630 };
631 return chunkedReadVecEndless(reader, data, chunk_len_ptr) catch |err| switch (err) {
632 error.ReadFailed => return error.ReadFailed,
633 error.EndOfStream => {
634 reader.body_err = error.HttpChunkTruncated;
635 return error.ReadFailed;
636 },
637 else => |e| {
638 reader.body_err = e;
639 return error.ReadFailed;
640 },
641 };
642 }
643
644 fn chunkedReadVecEndless(
645 reader: *Reader,
646 data: []const []u8,
647 chunk_len_ptr: *RemainingChunkLen,
648 ) (BodyError || std.io.Reader.Error)!usize {
649 const in = reader.in;
650 var already_requested_more = false;
651 var amt_read: usize = 0;
652 data: for (data) |d| {
653 var d_i: usize = 0;
654 len: switch (chunk_len_ptr.*) {
655 .head => {
656 var cp: ChunkParser = .init;
657 while (true) {
658 const i = cp.feed(in.bufferContents());
659 switch (cp.state) {
660 .invalid => return error.HttpChunkInvalid,
661 .data => {
662 in.toss(i);
663 break;
664 },
665 else => {
666 in.toss(i);
667 already_requested_more = true;
668 try in.fillMore();
669 continue;
670 },
671 }
672 }
673 if (cp.chunk_len == 0) return parseTrailers(reader, amt_read);
674 continue :len .init(cp.chunk_len + 2);
675 },
676 .n => {
677 if (in.bufferContents().len < 1) already_requested_more = true;
678 if ((try in.takeByte()) != '\n') return error.HttpChunkInvalid;
679 continue :len .head;
680 },
681 .rn => {
682 if (in.bufferContents().len < 2) already_requested_more = true;
683 const rn = try in.takeArray(2);
684 if (rn[0] != '\r' or rn[1] != '\n') return error.HttpChunkInvalid;
685 continue :len .head;
686 },
687 else => |remaining_chunk_len| {
688 const available_buffer = in.bufferContents();
689 const copy_len = @min(available_buffer.len, d.len - d_i, remaining_chunk_len.int() - 2);
690 @memcpy(d[d_i..][0..copy_len], available_buffer[0..copy_len]);
691 d_i += copy_len;
692 amt_read += copy_len;
693 in.toss(copy_len);
694 const next_chunk_len: RemainingChunkLen = .init(remaining_chunk_len.int() - copy_len);
695 if (d.len - d_i == 0) {
696 chunk_len_ptr.* = next_chunk_len;
697 continue :data;
698 }
699 if (available_buffer.len - copy_len == 0) {
700 if (already_requested_more) {
701 chunk_len_ptr.* = next_chunk_len;
702 return amt_read;
703 }
704 already_requested_more = true;
705 try in.fillMore();
706 }
707 continue :len next_chunk_len;
708 },
709 }
710 }
711 return amt_read;
712 }
713
714 fn chunkedDiscard(ctx: ?*anyopaque, limit: std.io.Limit) std.io.Reader.Error!usize {609 fn chunkedDiscard(ctx: ?*anyopaque, limit: std.io.Limit) std.io.Reader.Error!usize {
715 const reader: *Reader = @alignCast(@ptrCast(ctx));610 const reader: *Reader = @alignCast(@ptrCast(ctx));
716 const chunk_len_ptr = switch (reader.state) {611 const chunk_len_ptr = switch (reader.state) {
lib/std/io/BufferedWriter.zig+5-2
...@@ -561,7 +561,10 @@ pub fn writeFileReading(...@@ -561,7 +561,10 @@ pub fn writeFileReading(
561 limit: Limit,561 limit: Limit,
562) Writer.ReadingFileError!usize {562) Writer.ReadingFileError!usize {
563 const dest = limit.slice(try bw.writableSliceGreedy(1));563 const dest = limit.slice(try bw.writableSliceGreedy(1));
564 const n = try file_reader.read(dest);564 const n = file_reader.read(dest) catch |err| switch (err) {
565 error.EndOfStream => 0,
566 error.ReadFailed => return error.ReadFailed,
567 };
565 bw.advance(n);568 bw.advance(n);
566 return n;569 return n;
567}570}
...@@ -663,7 +666,7 @@ pub fn writeFileAll(...@@ -663,7 +666,7 @@ pub fn writeFileAll(
663 bw: *BufferedWriter,666 bw: *BufferedWriter,
664 file_reader: *std.fs.File.Reader,667 file_reader: *std.fs.File.Reader,
665 options: WriteFileOptions,668 options: WriteFileOptions,
666) Writer.FileError!void {669) Writer.ReadingFileError!void {
667 const headers_and_trailers = options.headers_and_trailers;670 const headers_and_trailers = options.headers_and_trailers;
668 const headers = headers_and_trailers[0..options.headers_len];671 const headers = headers_and_trailers[0..options.headers_len];
669 var remaining = options.limit;672 var remaining = options.limit;
lib/std/io/Writer.zig+1-1
...@@ -153,7 +153,7 @@ pub fn discardingWriteFile(...@@ -153,7 +153,7 @@ pub fn discardingWriteFile(
153 const seek_amt = limit.minInt(remaining);153 const seek_amt = limit.minInt(remaining);
154 // Error is observable on `file_reader` instance, and is safe to ignore154 // Error is observable on `file_reader` instance, and is safe to ignore
155 // depending on the caller's needs. Caller can make that decision.155 // depending on the caller's needs. Caller can make that decision.
156 file_reader.seekForward(seek_amt) catch {};156 file_reader.seekBy(@intCast(seek_amt)) catch {};
157 var n: usize = seek_amt;157 var n: usize = seek_amt;
158 for (headers_and_trailers[0..headers_len]) |bytes| n += bytes.len;158 for (headers_and_trailers[0..headers_len]) |bytes| n += bytes.len;
159 if (seek_amt == remaining) {159 if (seek_amt == remaining) {