Commit af229c1fdc
std/event/io.zig
@@ -21,6 +21,24 @@ pub fn InStream(comptime ReadError: type) type {
return await (async self.readFn(self, buffer) catch unreachable);
}
+ /// Return the number of bytes read. If it is less than buffer.len
+ /// it means end of stream.
+ pub async fn readFull(self: *Self, buffer: []u8) !usize {
+ var index: usize = 0;
+ while (index != buf.len) {
+ const amt_read = try await (async self.read(buf[index..]) catch unreachable);
+ if (amt_read == 0) return index;
+ index += amt_read;
+ }
+ return index;
+ }
+
+ /// Same as `readFull` but end of stream returns `error.EndOfStream`.
+ pub async fn readNoEof(self: *Self, buf: []u8) !void {
+ const amt_read = try await (async self.readFull(buf[index..]) catch unreachable);
+ if (amt_read < buf.len) return error.EndOfStream;
+ }
+
pub async fn readIntLe(self: *Self, comptime T: type) !T {
return await (async self.readInt(builtin.Endian.Little, T) catch unreachable);
}
@@ -31,24 +49,14 @@ pub fn InStream(comptime ReadError: type) type {
pub async fn readInt(self: *Self, endian: builtin.Endian, comptime T: type) !T {
var bytes: [@sizeOf(T)]u8 = undefined;
- try await (async self.readFull(bytes[0..]) catch unreachable);
+ try await (async self.readNoEof(bytes[0..]) catch unreachable);
return mem.readInt(bytes, T, endian);
}
- /// Same as `read` but end of stream returns `error.EndOfStream`.
- pub async fn readFull(self: *Self, buf: []u8) !void {
- var index: usize = 0;
- while (index != buf.len) {
- const amt_read = try await (async self.read(buf[index..]) catch unreachable);
- if (amt_read == 0) return error.EndOfStream;
- index += amt_read;
- }
- }
-
pub async fn readStruct(self: *Self, comptime T: type, ptr: *T) !void {
// Only extern and packed structs have defined in-memory layout.
comptime assert(@typeInfo(T).Struct.layout != builtin.TypeInfo.ContainerLayout.Auto);
- return await (async self.readFull(@sliceToBytes((*[1]T)(ptr)[0..])) catch unreachable);
+ return await (async self.readNoEof(@sliceToBytes((*[1]T)(ptr)[0..])) catch unreachable);
}
};
}
std/os/child_process.zig
@@ -792,13 +792,11 @@ fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn {
const ErrInt = @IntType(false, @sizeOf(error) * 8);
fn writeIntFd(fd: i32, value: ErrInt) !void {
- var bytes: [@sizeOf(ErrInt)]u8 = undefined;
- mem.writeInt(bytes[0..], value, builtin.endian);
- os.posixWrite(fd, bytes[0..]) catch return error.SystemResources;
+ const stream = &os.File.openHandle(fd).outStream().stream;
+ stream.writeIntNe(ErrInt, value) catch return error.SystemResources;
}
fn readIntFd(fd: i32) !ErrInt {
- var bytes: [@sizeOf(ErrInt)]u8 = undefined;
- os.posixRead(fd, bytes[0..]) catch return error.SystemResources;
- return mem.readInt(bytes[0..], ErrInt, builtin.endian);
+ const stream = &os.File.openHandle(fd).inStream().stream;
+ return stream.readIntNe(ErrInt) catch return error.SystemResources;
}
std/os/file.zig
@@ -102,12 +102,24 @@ pub const File = struct {
/// If a file already exists in the destination this returns OpenError.PathAlreadyExists
/// Call close to clean up.
pub fn openWriteNoClobber(path: []const u8, file_mode: Mode) OpenError!File {
+ if (is_posix) {
+ const path_c = try os.toPosixPath(path);
+ return openWriteNoClobberC(path_c, file_mode);
+ } else if (is_windows) {
+ const path_w = try windows_util.sliceToPrefixedFileW(path);
+ return openWriteNoClobberW(&path_w, file_mode);
+ } else {
+ @compileError("TODO implement openWriteMode for this OS");
+ }
+ }
+
+ pub fn openWriteNoClobberC(path: [*]const u8, file_mode: Mode) OpenError!File {
if (is_posix) {
const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_EXCL;
- const fd = try os.posixOpen(path, flags, file_mode);
+ const fd = try os.posixOpenC(path, flags, file_mode);
return openHandle(fd);
} else if (is_windows) {
- const path_w = try windows_util.sliceToPrefixedFileW(path);
+ const path_w = try windows_util.cStrToPrefixedFileW(path);
return openWriteNoClobberW(&path_w, file_mode);
} else {
@compileError("TODO implement openWriteMode for this OS");
@@ -369,28 +381,7 @@ pub const File = struct {
pub fn read(self: File, buffer: []u8) ReadError!usize {
if (is_posix) {
- var index: usize = 0;
- while (index < buffer.len) {
- const amt_read = posix.read(self.handle, buffer.ptr + index, buffer.len - index);
- const read_err = posix.getErrno(amt_read);
- if (read_err > 0) {
- switch (read_err) {
- posix.EINTR => continue,
- posix.EINVAL => unreachable,
- posix.EFAULT => unreachable,
- posix.EAGAIN => unreachable,
- posix.EBADF => unreachable, // always a race condition
- posix.EIO => return error.InputOutput,
- posix.EISDIR => return error.IsDir,
- posix.ENOBUFS => return error.SystemResources,
- posix.ENOMEM => return error.SystemResources,
- else => return os.unexpectedErrorPosix(read_err),
- }
- }
- if (amt_read == 0) return index;
- index += amt_read;
- }
- return index;
+ return os.posixRead(self.handle, buffer);
} else if (is_windows) {
var index: usize = 0;
while (index < buffer.len) {
@@ -409,7 +400,7 @@ pub const File = struct {
}
return index;
} else {
- unreachable;
+ @compileError("Unsupported OS");
}
}
std/os/index.zig
@@ -104,30 +104,17 @@ pub fn getRandomBytes(buf: []u8) !void {
Os.linux => while (true) {
// TODO check libc version and potentially call c.getrandom.
// See #397
- const err = posix.getErrno(posix.getrandom(buf.ptr, buf.len, 0));
- if (err > 0) {
- switch (err) {
- posix.EINVAL => unreachable,
- posix.EFAULT => unreachable,
- posix.EINTR => continue,
- posix.ENOSYS => {
- const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0);
- defer close(fd);
-
- try posixRead(fd, buf);
- return;
- },
- else => return unexpectedErrorPosix(err),
- }
+ const errno = posix.getErrno(posix.getrandom(buf.ptr, buf.len, 0));
+ switch (errno) {
+ 0 => return,
+ posix.EINVAL => unreachable,
+ posix.EFAULT => unreachable,
+ posix.EINTR => continue,
+ posix.ENOSYS => return getRandomBytesDevURandom(buf),
+ else => return unexpectedErrorPosix(errno),
}
- return;
- },
- Os.macosx, Os.ios => {
- const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0);
- defer close(fd);
-
- try posixRead(fd, buf);
},
+ Os.macosx, Os.ios => return getRandomBytesDevURandom(buf),
Os.windows => {
// Call RtlGenRandom() instead of CryptGetRandom() on Windows
// https://github.com/rust-lang-nursery/rand/issues/111
@@ -151,6 +138,22 @@ pub fn getRandomBytes(buf: []u8) !void {
}
}
+fn getRandomBytesDevURandom(buf: []u8) !void {
+ const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0);
+ defer close(fd);
+
+ const stream = &File.openHandle(fd).inStream().stream;
+ stream.readNoEof(buf) catch |err| switch (err) {
+ error.EndOfStream => unreachable,
+ error.OperationAborted => unreachable,
+ error.BrokenPipe => unreachable,
+ error.Unexpected => return error.Unexpected,
+ error.InputOutput => return error.Unexpected,
+ error.SystemResources => return error.Unexpected,
+ error.IsDir => unreachable,
+ };
+}
+
test "os.getRandomBytes" {
var buf_a: [50]u8 = undefined;
var buf_b: [50]u8 = undefined;
@@ -235,8 +238,9 @@ pub const PosixReadError = error{
Unexpected,
};
-/// Calls POSIX read, and keeps trying if it gets interrupted.
-pub fn posixRead(fd: i32, buf: []u8) !void {
+/// Returns the number of bytes that were read, which can be less than
+/// buf.len. If 0 bytes were read, that means EOF.
+pub fn posixRead(fd: i32, buf: []u8) PosixReadError!usize {
// Linux can return EINVAL when read amount is > 0x7ffff000
// See https://github.com/ziglang/zig/pull/743#issuecomment-363158274
const max_buf_len = 0x7ffff000;
@@ -249,7 +253,9 @@ pub fn posixRead(fd: i32, buf: []u8) !void {
switch (err) {
0 => {
index += rc;
- continue;
+ if (rc == want_to_read) continue;
+ // Read returned less than buf.len.
+ return index;
},
posix.EINTR => continue,
posix.EINVAL => unreachable,
@@ -263,6 +269,7 @@ pub fn posixRead(fd: i32, buf: []u8) !void {
else => return unexpectedErrorPosix(err),
}
}
+ return index;
}
/// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
@@ -962,16 +969,16 @@ pub const DeleteFileError = error{
pub fn deleteFile(file_path: []const u8) DeleteFileError!void {
if (builtin.os == Os.windows) {
- return deleteFileWindows(file_path);
+ const file_path_w = try windows_util.sliceToPrefixedFileW(file_path);
+ return deleteFileW(&file_path_w);
} else {
- return deleteFilePosix(file_path);
+ const file_path_c = try toPosixPath(file_path);
+ return deleteFileC(&file_path_c);
}
}
-pub fn deleteFileWindows(file_path: []const u8) !void {
- const file_path_w = try windows_util.sliceToPrefixedFileW(file_path);
-
- if (windows.DeleteFileW(&file_path_w) == 0) {
+pub fn deleteFileW(file_path: [*]const u16) DeleteFileError!void {
+ if (windows.DeleteFileW(file_path) == 0) {
const err = windows.GetLastError();
switch (err) {
windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
@@ -983,50 +990,49 @@ pub fn deleteFileWindows(file_path: []const u8) !void {
}
}
-pub fn deleteFilePosixC(file_path: [*]const u8) !void {
- const err = posix.getErrno(posix.unlink(file_path));
- switch (err) {
- 0 => return,
- posix.EACCES => return error.AccessDenied,
- posix.EPERM => return error.AccessDenied,
- posix.EBUSY => return error.FileBusy,
- posix.EFAULT => unreachable,
- posix.EINVAL => unreachable,
- posix.EIO => return error.FileSystem,
- posix.EISDIR => return error.IsDir,
- posix.ELOOP => return error.SymLinkLoop,
- posix.ENAMETOOLONG => return error.NameTooLong,
- posix.ENOENT => return error.FileNotFound,
- posix.ENOTDIR => return error.NotDir,
- posix.ENOMEM => return error.SystemResources,
- posix.EROFS => return error.ReadOnlyFileSystem,
- else => return unexpectedErrorPosix(err),
+pub fn deleteFileC(file_path: [*]const u8) DeleteFileError!void {
+ if (is_windows) {
+ const file_path_w = try windows_util.cStrToPrefixedFileW(file_path);
+ return deleteFileW(&file_path_w);
+ } else {
+ const err = posix.getErrno(posix.unlink(file_path));
+ switch (err) {
+ 0 => return,
+ posix.EACCES => return error.AccessDenied,
+ posix.EPERM => return error.AccessDenied,
+ posix.EBUSY => return error.FileBusy,
+ posix.EFAULT => unreachable,
+ posix.EINVAL => unreachable,
+ posix.EIO => return error.FileSystem,
+ posix.EISDIR => return error.IsDir,
+ posix.ELOOP => return error.SymLinkLoop,
+ posix.ENAMETOOLONG => return error.NameTooLong,
+ posix.ENOENT => return error.FileNotFound,
+ posix.ENOTDIR => return error.NotDir,
+ posix.ENOMEM => return error.SystemResources,
+ posix.EROFS => return error.ReadOnlyFileSystem,
+ else => return unexpectedErrorPosix(err),
+ }
}
}
-pub fn deleteFilePosix(file_path: []const u8) !void {
- const file_path_c = try toPosixPath(file_path);
- return deleteFilePosixC(&file_path_c);
-}
-
/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
/// merged and readily available,
/// there is a possibility of power loss or application termination leaving temporary files present
/// in the same directory as dest_path.
/// Destination file will have the same mode as the source file.
-/// TODO investigate if this can work with no allocator
-pub fn copyFile(allocator: *Allocator, source_path: []const u8, dest_path: []const u8) !void {
+pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void {
var in_file = try os.File.openRead(source_path);
defer in_file.close();
const mode = try in_file.mode();
- var atomic_file = try AtomicFile.init(allocator, dest_path, mode);
+ var atomic_file = try AtomicFile.init(dest_path, mode);
defer atomic_file.deinit();
var buf: [page_size]u8 = undefined;
while (true) {
- const amt = try in_file.read(buf[0..]);
+ const amt = try in_file.readFull(buf[0..]);
try atomic_file.file.write(buf[0..amt]);
if (amt != buf.len) {
return atomic_file.finish();
@@ -1037,12 +1043,11 @@ pub fn copyFile(allocator: *Allocator, source_path: []const u8, dest_path: []con
/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
/// merged and readily available,
/// there is a possibility of power loss or application termination leaving temporary files present
-/// TODO investigate if this can work with no allocator
-pub fn copyFileMode(allocator: *Allocator, source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void {
+pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void {
var in_file = try os.File.openRead(source_path);
defer in_file.close();
- var atomic_file = try AtomicFile.init(allocator, dest_path, mode);
+ var atomic_file = try AtomicFile.init(dest_path, mode);
defer atomic_file.deinit();
var buf: [page_size]u8 = undefined;
@@ -1056,35 +1061,38 @@ pub fn copyFileMode(allocator: *Allocator, source_path: []const u8, dest_path: [
}
pub const AtomicFile = struct {
- /// TODO investigate if we can make this work with no allocator
- allocator: *Allocator,
file: os.File,
- tmp_path: []u8,
+ tmp_path_buf: [MAX_PATH_BYTES]u8,
dest_path: []const u8,
finished: bool,
+ const InitError = os.File.OpenError;
+
/// dest_path must remain valid for the lifetime of AtomicFile
/// call finish to atomically replace dest_path with contents
- pub fn init(allocator: *Allocator, dest_path: []const u8, mode: File.Mode) !AtomicFile {
+ /// TODO once we have null terminated pointers, use the
+ /// openWriteNoClobberN function
+ pub fn init(dest_path: []const u8, mode: File.Mode) InitError!AtomicFile {
const dirname = os.path.dirname(dest_path);
-
var rand_buf: [12]u8 = undefined;
-
const dirname_component_len = if (dirname) |d| d.len + 1 else 0;
- const tmp_path = try allocator.alloc(u8, dirname_component_len +
- base64.Base64Encoder.calcSize(rand_buf.len));
- errdefer allocator.free(tmp_path);
+ const encoded_rand_len = comptime base64.Base64Encoder.calcSize(rand_buf.len);
+ const tmp_path_len = dirname_component_len + encoded_rand_len;
+ var tmp_path_buf: [MAX_PATH_BYTES]u8 = undefined;
+ if (tmp_path_len >= tmp_path_buf.len) return error.NameTooLong;
if (dirname) |dir| {
- mem.copy(u8, tmp_path[0..], dir);
- tmp_path[dir.len] = os.path.sep;
+ mem.copy(u8, tmp_path_buf[0..], dir);
+ tmp_path_buf[dir.len] = os.path.sep;
}
+ tmp_path_buf[tmp_path_len] = 0;
+
while (true) {
try getRandomBytes(rand_buf[0..]);
- b64_fs_encoder.encode(tmp_path[dirname_component_len..], rand_buf);
+ b64_fs_encoder.encode(tmp_path_buf[dirname_component_len..tmp_path_len], rand_buf);
- const file = os.File.openWriteNoClobber(tmp_path, mode) catch |err| switch (err) {
+ const file = os.File.openWriteNoClobberC(&tmp_path_buf, mode) catch |err| switch (err) {
error.PathAlreadyExists => continue,
// TODO zig should figure out that this error set does not include PathAlreadyExists since
// it is handled in the above switch
@@ -1092,9 +1100,8 @@ pub const AtomicFile = struct {
};
return AtomicFile{
- .allocator = allocator,
.file = file,
- .tmp_path = tmp_path,
+ .tmp_path_buf = tmp_path_buf,
.dest_path = dest_path,
.finished = false,
};
@@ -1105,8 +1112,7 @@ pub const AtomicFile = struct {
pub fn deinit(self: *AtomicFile) void {
if (!self.finished) {
self.file.close();
- deleteFile(self.tmp_path) catch {};
- self.allocator.free(self.tmp_path);
+ deleteFileC(&self.tmp_path_buf) catch {};
self.finished = true;
}
}
@@ -1114,15 +1120,25 @@ pub const AtomicFile = struct {
pub fn finish(self: *AtomicFile) !void {
assert(!self.finished);
self.file.close();
- try rename(self.tmp_path, self.dest_path);
- self.allocator.free(self.tmp_path);
self.finished = true;
+ if (is_posix) {
+ const dest_path_c = try toPosixPath(self.dest_path);
+ return renameC(&self.tmp_path_buf, &dest_path_c);
+ } else if (is_windows) {
+ const dest_path_w = try windows_util.sliceToPrefixedFileW(self.dest_path);
+ const tmp_path_w = try windows_util.cStrToPrefixedFileW(&self.tmp_path_buf);
+ return renameW(&tmp_path_w, &dest_path_w);
+ } else {
+ @compileError("Unsupported OS");
+ }
}
};
pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) !void {
if (is_windows) {
- @compileError("TODO implement for windows");
+ const old_path_w = try windows_util.cStrToPrefixedFileW(old_path);
+ const new_path_w = try windows_util.cStrToPrefixedFileW(new_path);
+ return renameW(&old_path_w, &new_path_w);
} else {
const err = posix.getErrno(posix.rename(old_path, new_path));
switch (err) {
@@ -1150,17 +1166,21 @@ pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) !void {
}
}
+pub fn renameW(old_path: [*]const u16, new_path: [*]const u16) !void {
+ const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH;
+ if (windows.MoveFileExW(old_path, new_path, flags) == 0) {
+ const err = windows.GetLastError();
+ switch (err) {
+ else => return unexpectedErrorWindows(err),
+ }
+ }
+}
+
pub fn rename(old_path: []const u8, new_path: []const u8) !void {
if (is_windows) {
- const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH;
const old_path_w = try windows_util.sliceToPrefixedFileW(old_path);
const new_path_w = try windows_util.sliceToPrefixedFileW(new_path);
- if (windows.MoveFileExW(&old_path_w, &new_path_w, flags) == 0) {
- const err = windows.GetLastError();
- switch (err) {
- else => return unexpectedErrorWindows(err),
- }
- }
+ return renameW(&old_path_w, &new_path_w);
} else {
const old_path_c = try toPosixPath(old_path);
const new_path_c = try toPosixPath(new_path);
std/os/test.zig
@@ -2,6 +2,7 @@ const std = @import("../index.zig");
const os = std.os;
const assert = std.debug.assert;
const io = std.io;
+const mem = std.mem;
const a = std.debug.global_allocator;
@@ -80,3 +81,23 @@ test "cpu count" {
const cpu_count = try std.os.cpuCount(a);
assert(cpu_count >= 1);
}
+
+test "AtomicFile" {
+ var buffer: [1024]u8 = undefined;
+ const allocator = &std.heap.FixedBufferAllocator.init(buffer[0..]).allocator;
+ const test_out_file = "tmp_atomic_file_test_dest.txt";
+ const test_content =
+ \\ hello!
+ \\ this is a test file
+ ;
+ {
+ var af = try os.AtomicFile.init(test_out_file, os.File.default_mode);
+ defer af.deinit();
+ try af.file.write(test_content);
+ try af.finish();
+ }
+ const content = try io.readFileAlloc(allocator, test_out_file);
+ assert(mem.eql(u8, content, test_content));
+
+ try os.deleteFile(test_out_file);
+}
std/build.zig
@@ -634,7 +634,7 @@ pub const Builder = struct {
warn("Unable to create path {}: {}\n", dirname, @errorName(err));
return err;
};
- os.copyFileMode(self.allocator, abs_source_path, dest_path, mode) catch |err| {
+ os.copyFileMode(abs_source_path, dest_path, mode) catch |err| {
warn("Unable to copy {} to {}: {}\n", abs_source_path, dest_path, @errorName(err));
return err;
};
std/io.zig
@@ -51,7 +51,7 @@ pub fn InStream(comptime ReadError: type) type {
var actual_buf_len: usize = 0;
while (true) {
const dest_slice = buffer.toSlice()[actual_buf_len..];
- const bytes_read = try self.readFn(self, dest_slice);
+ const bytes_read = try self.readFull(dest_slice);
actual_buf_len += bytes_read;
if (bytes_read != dest_slice.len) {
@@ -111,14 +111,27 @@ pub fn InStream(comptime ReadError: type) type {
return buf.toOwnedSlice();
}
+ /// Returns the number of bytes read. It may be less than buffer.len.
+ /// If the number of bytes read is 0, it means end of stream.
+ /// End of stream is not an error condition.
+ pub fn read(self: *Self, buffer: []u8) !usize {
+ return self.readFn(self, buffer);
+ }
+
/// Returns the number of bytes read. If the number read is smaller than buf.len, it
/// means the stream reached the end. Reaching the end of a stream is not an error
/// condition.
- pub fn read(self: *Self, buffer: []u8) !usize {
- return self.readFn(self, buffer);
+ pub fn readFull(self: *Self, buffer: []u8) !usize {
+ var index: usize = 0;
+ while (index != buffer.len) {
+ const amt = try self.read(buffer[index..]);
+ if (amt == 0) return index;
+ index += amt;
+ }
+ return index;
}
- /// Same as `read` but end of stream returns `error.EndOfStream`.
+ /// Same as `readFull` but end of stream returns `error.EndOfStream`.
pub fn readNoEof(self: *Self, buf: []u8) !void {
const amt_read = try self.read(buf);
if (amt_read < buf.len) return error.EndOfStream;
@@ -136,6 +149,11 @@ pub fn InStream(comptime ReadError: type) type {
return @bitCast(i8, try self.readByte());
}
+ /// Reads a native-endian integer
+ pub fn readIntNe(self: *Self, comptime T: type) !T {
+ return self.readInt(builtin.endian, T);
+ }
+
pub fn readIntLe(self: *Self, comptime T: type) !T {
return self.readInt(builtin.Endian.Little, T);
}
@@ -202,6 +220,11 @@ pub fn OutStream(comptime WriteError: type) type {
}
}
+ /// Write a native-endian integer.
+ pub fn writeIntNe(self: *Self, comptime T: type, value: T) !void {
+ return self.writeInt(builtin.endian, T, value);
+ }
+
pub fn writeIntLe(self: *Self, comptime T: type, value: T) !void {
return self.writeInt(builtin.Endian.Little, T, value);
}
@@ -537,6 +560,7 @@ pub const BufferedAtomicFile = struct {
atomic_file: os.AtomicFile,
file_stream: os.File.OutStream,
buffered_stream: BufferedOutStream(os.File.WriteError),
+ allocator: *mem.Allocator,
pub fn create(allocator: *mem.Allocator, dest_path: []const u8) !*BufferedAtomicFile {
// TODO with well defined copy elision we don't need this allocation
@@ -544,10 +568,11 @@ pub const BufferedAtomicFile = struct {
.atomic_file = undefined,
.file_stream = undefined,
.buffered_stream = undefined,
+ .allocator = allocator,
});
errdefer allocator.destroy(self);
- self.atomic_file = try os.AtomicFile.init(allocator, dest_path, os.File.default_mode);
+ self.atomic_file = try os.AtomicFile.init(dest_path, os.File.default_mode);
errdefer self.atomic_file.deinit();
self.file_stream = self.atomic_file.file.outStream();
@@ -557,9 +582,8 @@ pub const BufferedAtomicFile = struct {
/// always call destroy, even after successful finish()
pub fn destroy(self: *BufferedAtomicFile) void {
- const allocator = self.atomic_file.allocator;
self.atomic_file.deinit();
- allocator.destroy(self);
+ self.allocator.destroy(self);
}
pub fn finish(self: *BufferedAtomicFile) !void {