Commit b7bc259093

Andrew Kelley <superjoe30@gmail.com>
2018-02-05 13:38:24
make OutStream and InStream take an error set param
1 parent 893f108
Changed files (3)
src
std
src/ir.cpp
@@ -7174,32 +7174,37 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
                 return ira->codegen->builtin_types.entry_invalid;
             }
             if (err_set_type == nullptr) {
-                err_set_type = cur_type;
+                if (prev_type->id == TypeTableEntryIdErrorUnion) {
+                    err_set_type = prev_type->data.error_union.err_set_type;
+                } else {
+                    err_set_type = cur_type;
+                }
                 errors = allocate<ErrorTableEntry *>(ira->codegen->errors_by_index.length);
                 for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
                     ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
                     errors[error_entry->value] = error_entry;
                 }
-                continue;
-            } else {
-                // check if the cur type error set is a subset
-                bool prev_is_superset = true;
-                for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) {
-                    ErrorTableEntry *contained_error_entry = cur_type->data.error_set.errors[i];
-                    ErrorTableEntry *error_entry = errors[contained_error_entry->value];
-                    if (error_entry == nullptr) {
-                        prev_is_superset = false;
-                        break;
-                    }
-                }
-                if (prev_is_superset) {
+                if (err_set_type == cur_type) {
                     continue;
                 }
-                // not a subset. invent new error set type, union of both of them
-                err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_type);
-                assert(errors != nullptr);
+            }
+            // check if the cur type error set is a subset
+            bool prev_is_superset = true;
+            for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) {
+                ErrorTableEntry *contained_error_entry = cur_type->data.error_set.errors[i];
+                ErrorTableEntry *error_entry = errors[contained_error_entry->value];
+                if (error_entry == nullptr) {
+                    prev_is_superset = false;
+                    break;
+                }
+            }
+            if (prev_is_superset) {
                 continue;
             }
+            // not a subset. invent new error set type, union of both of them
+            err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_type);
+            assert(errors != nullptr);
+            continue;
         }
 
         if (prev_type->id == TypeTableEntryIdErrorUnion && cur_type->id == TypeTableEntryIdErrorUnion) {
std/debug/index.zig
@@ -15,12 +15,12 @@ pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator;
 /// TODO atomic/multithread support
 var stderr_file: io.File = undefined;
 var stderr_file_out_stream: io.FileOutStream = undefined;
-var stderr_stream: ?&io.OutStream = null;
+var stderr_stream: ?&io.OutStream(io.FileOutStream.Error) = null;
 pub fn warn(comptime fmt: []const u8, args: ...) void {
     const stderr = getStderrStream() catch return;
     stderr.print(fmt, args) catch return;
 }
-fn getStderrStream() !&io.OutStream {
+fn getStderrStream() !&io.OutStream(io.FileOutStream.Error) {
     if (stderr_stream) |st| {
         return st;
     } else {
@@ -140,7 +140,7 @@ const WHITE = "\x1b[37;1m";
 const DIM = "\x1b[2m";
 const RESET = "\x1b[0m";
 
-pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator,
+pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: var, allocator: &mem.Allocator,
     debug_info: &ElfStackTrace, tty_color: bool) !void
 {
     var frame_index: usize = undefined;
@@ -162,7 +162,7 @@ pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: &io.O
     }
 }
 
-pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator,
+pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator,
     debug_info: &ElfStackTrace, tty_color: bool, ignore_frame_count: usize) !void
 {
     var ignored_count: usize = 0;
@@ -179,7 +179,7 @@ pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocat
     }
 }
 
-fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, address: usize) !void {
+fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: usize) !void {
     if (builtin.os == builtin.Os.windows) {
         return error.UnsupportedDebugInfo;
     }
@@ -532,7 +532,7 @@ const LineNumberProgram = struct {
     }
 };
 
-fn readStringRaw(allocator: &mem.Allocator, in_stream: &io.InStream) ![]u8 {
+fn readStringRaw(allocator: &mem.Allocator, in_stream: var) ![]u8 {
     var buf = ArrayList(u8).init(allocator);
     while (true) {
         const byte = try in_stream.readByte();
@@ -549,54 +549,62 @@ fn getString(st: &ElfStackTrace, offset: u64) ![]u8 {
     return st.readString();
 }
 
-fn readAllocBytes(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) ![]u8 {
+fn readAllocBytes(allocator: &mem.Allocator, in_stream: var, size: usize) ![]u8 {
     const buf = try global_allocator.alloc(u8, size);
     errdefer global_allocator.free(buf);
     if ((try in_stream.read(buf)) < size) return error.EndOfFile;
     return buf;
 }
 
-fn parseFormValueBlockLen(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) !FormValue {
+fn parseFormValueBlockLen(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue {
     const buf = try readAllocBytes(allocator, in_stream, size);
     return FormValue { .Block = buf };
 }
 
-fn parseFormValueBlock(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) !FormValue {
+fn parseFormValueBlock(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue {
     const block_len = try in_stream.readVarInt(builtin.Endian.Little, usize, size);
     return parseFormValueBlockLen(allocator, in_stream, block_len);
 }
 
-fn parseFormValueConstant(allocator: &mem.Allocator, in_stream: &io.InStream, signed: bool, size: usize) !FormValue {
+fn parseFormValueConstant(allocator: &mem.Allocator, in_stream: var, signed: bool, size: usize) !FormValue {
     return FormValue { .Const = Constant {
         .signed = signed,
         .payload = try readAllocBytes(allocator, in_stream, size),
     }};
 }
 
-fn parseFormValueDwarfOffsetSize(in_stream: &io.InStream, is_64: bool) !u64 {
+fn parseFormValueDwarfOffsetSize(in_stream: var, is_64: bool) !u64 {
     return if (is_64) try in_stream.readIntLe(u64)
     else u64(try in_stream.readIntLe(u32)) ;
 }
 
-fn parseFormValueTargetAddrSize(in_stream: &io.InStream) !u64 {
+fn parseFormValueTargetAddrSize(in_stream: var) !u64 {
     return if (@sizeOf(usize) == 4) u64(try in_stream.readIntLe(u32))
     else if (@sizeOf(usize) == 8) try in_stream.readIntLe(u64)
     else unreachable;
 }
 
-fn parseFormValueRefLen(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) !FormValue {
+fn parseFormValueRefLen(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue {
     const buf = try readAllocBytes(allocator, in_stream, size);
     return FormValue { .Ref = buf };
 }
 
-fn parseFormValueRef(allocator: &mem.Allocator, in_stream: &io.InStream, comptime T: type) !FormValue {
+fn parseFormValueRef(allocator: &mem.Allocator, in_stream: var, comptime T: type) !FormValue {
     const block_len = try in_stream.readIntLe(T);
     return parseFormValueRefLen(allocator, in_stream, block_len);
 }
 
-const ParseFormValueError = error {};
+const ParseFormValueError = error {
+    EndOfStream,
+    Io,
+    BadFd,
+    Unexpected,
+    InvalidDebugInfo,
+    EndOfFile,
+    OutOfMemory,
+};
 
-fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u64, is_64: bool)
+fn parseFormValue(allocator: &mem.Allocator, in_stream: var, form_id: u64, is_64: bool)
     ParseFormValueError!FormValue
 {
     return switch (form_id) {
@@ -739,7 +747,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
         try in_file.seekTo(this_offset);
 
         var is_64: bool = undefined;
-        const unit_length = try readInitialLength(in_stream, &is_64);
+        const unit_length = try readInitialLength(@typeOf(in_stream.readFn).ReturnType.ErrorSet, in_stream, &is_64);
         if (unit_length == 0)
             return error.MissingDebugInfo;
         const next_offset = unit_length + (if (is_64) usize(12) else usize(4));
@@ -914,7 +922,7 @@ fn scanAllCompileUnits(st: &ElfStackTrace) !void {
         try st.self_exe_file.seekTo(this_unit_offset);
 
         var is_64: bool = undefined;
-        const unit_length = try readInitialLength(in_stream, &is_64);
+        const unit_length = try readInitialLength(@typeOf(in_stream.readFn).ReturnType.ErrorSet, in_stream, &is_64);
         if (unit_length == 0)
             return;
         const next_offset = unit_length + (if (is_64) usize(12) else usize(4));
@@ -1014,7 +1022,7 @@ fn findCompileUnit(st: &ElfStackTrace, target_address: u64) !&const CompileUnit
     return error.MissingDebugInfo;
 }
 
-fn readInitialLength(in_stream: &io.InStream, is_64: &bool) !u64 {
+fn readInitialLength(comptime E: type, in_stream: &io.InStream(E), is_64: &bool) !u64 {
     const first_32_bits = try in_stream.readIntLe(u32);
     *is_64 = (first_32_bits == 0xffffffff);
     if (*is_64) {
@@ -1025,7 +1033,7 @@ fn readInitialLength(in_stream: &io.InStream, is_64: &bool) !u64 {
     }
 }
 
-fn readULeb128(in_stream: &io.InStream) !u64 {
+fn readULeb128(in_stream: var) !u64 {
     var result: u64 = 0;
     var shift: usize = 0;
 
std/io.zig
@@ -61,18 +61,21 @@ pub fn getStdIn() GetStdIoErrs!File {
 /// Implementation of InStream trait for File
 pub const FileInStream = struct {
     file: &File,
-    stream: InStream,
+    stream: Stream,
+
+    pub const Error = @typeOf(File.read).ReturnType.ErrorSet;
+    pub const Stream = InStream(Error);
 
     pub fn init(file: &File) FileInStream {
         return FileInStream {
             .file = file,
-            .stream = InStream {
+            .stream = Stream {
                 .readFn = readFn,
             },
         };
     }
 
-    fn readFn(in_stream: &InStream, buffer: []u8) !usize {
+    fn readFn(in_stream: &Stream, buffer: []u8) Error!usize {
         const self = @fieldParentPtr(FileInStream, "stream", in_stream);
         return self.file.read(buffer);
     }
@@ -81,18 +84,21 @@ pub const FileInStream = struct {
 /// Implementation of OutStream trait for File
 pub const FileOutStream = struct {
     file: &File,
-    stream: OutStream,
+    stream: Stream,
+
+    pub const Error = File.WriteError;
+    pub const Stream = OutStream(Error);
 
     pub fn init(file: &File) FileOutStream {
         return FileOutStream {
             .file = file,
-            .stream = OutStream {
+            .stream = Stream {
                 .writeFn = writeFn,
             },
         };
     }
 
-    fn writeFn(out_stream: &OutStream, bytes: []const u8) !void {
+    fn writeFn(out_stream: &Stream, bytes: []const u8) !void {
         const self = @fieldParentPtr(FileOutStream, "stream", out_stream);
         return self.file.write(bytes);
     }
@@ -298,6 +304,8 @@ pub const File = struct {
         }
     }
 
+    pub const ReadError = error {};
+
     pub fn read(self: &File, buffer: []u8) !usize {
         if (is_posix) {
             var index: usize = 0;
@@ -340,7 +348,7 @@ pub const File = struct {
         }
     }
 
-    const WriteError = os.WindowsWriteError || os.PosixWriteError;
+    pub const WriteError = os.WindowsWriteError || os.PosixWriteError;
 
     fn write(self: &File, bytes: []const u8) WriteError!void {
         if (is_posix) {
@@ -353,161 +361,165 @@ pub const File = struct {
     }
 };
 
-pub const InStream = struct {
-    // TODO allow specifying the error set
-    /// Return 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.
-    readFn: fn(self: &InStream, buffer: []u8) error!usize,
-
-    /// Replaces `buffer` contents by reading from the stream until it is finished.
-    /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and
-    /// the contents read from the stream are lost.
-    pub fn readAllBuffer(self: &InStream, buffer: &Buffer, max_size: usize) !void {
-        try buffer.resize(0);
-
-        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);
-            actual_buf_len += bytes_read;
-
-            if (bytes_read != dest_slice.len) {
-                buffer.shrink(actual_buf_len);
-                return;
-            }
-
-            const new_buf_size = math.min(max_size, actual_buf_len + os.page_size);
-            if (new_buf_size == actual_buf_len)
-                return error.StreamTooLong;
-            try buffer.resize(new_buf_size);
-        }
-    }
+pub fn InStream(comptime Error: type) type {
+    return struct {
+        const Self = this;
 
-    /// Allocates enough memory to hold all the contents of the stream. If the allocated
-    /// memory would be greater than `max_size`, returns `error.StreamTooLong`.
-    /// Caller owns returned memory.
-    /// If this function returns an error, the contents from the stream read so far are lost.
-    pub fn readAllAlloc(self: &InStream, allocator: &mem.Allocator, max_size: usize) ![]u8 {
-        var buf = Buffer.initNull(allocator);
-        defer buf.deinit();
+        /// Return 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.
+        readFn: fn(self: &Self, buffer: []u8) Error!usize,
 
-        try self.readAllBuffer(&buf, max_size);
-        return buf.toOwnedSlice();
-    }
+        /// Replaces `buffer` contents by reading from the stream until it is finished.
+        /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and
+        /// the contents read from the stream are lost.
+        pub fn readAllBuffer(self: &Self, buffer: &Buffer, max_size: usize) !void {
+            try buffer.resize(0);
 
-    /// Replaces `buffer` contents by reading from the stream until `delimiter` is found.
-    /// Does not include the delimiter in the result.
-    /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and the contents
-    /// read from the stream so far are lost.
-    pub fn readUntilDelimiterBuffer(self: &InStream, buffer: &Buffer, delimiter: u8, max_size: usize) !void {
-        try buf.resize(0);
+            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);
+                actual_buf_len += bytes_read;
 
-        while (true) {
-            var byte: u8 = try self.readByte();
+                if (bytes_read != dest_slice.len) {
+                    buffer.shrink(actual_buf_len);
+                    return;
+                }
 
-            if (byte == delimiter) {
-                return;
+                const new_buf_size = math.min(max_size, actual_buf_len + os.page_size);
+                if (new_buf_size == actual_buf_len)
+                    return error.StreamTooLong;
+                try buffer.resize(new_buf_size);
             }
+        }
 
-            if (buf.len() == max_size) {
-                return error.StreamTooLong;
-            }
+        /// Allocates enough memory to hold all the contents of the stream. If the allocated
+        /// memory would be greater than `max_size`, returns `error.StreamTooLong`.
+        /// Caller owns returned memory.
+        /// If this function returns an error, the contents from the stream read so far are lost.
+        pub fn readAllAlloc(self: &Self, allocator: &mem.Allocator, max_size: usize) ![]u8 {
+            var buf = Buffer.initNull(allocator);
+            defer buf.deinit();
 
-            try buf.appendByte(byte);
+            try self.readAllBuffer(&buf, max_size);
+            return buf.toOwnedSlice();
         }
-    }
 
-    /// Allocates enough memory to read until `delimiter`. If the allocated
-    /// memory would be greater than `max_size`, returns `error.StreamTooLong`.
-    /// Caller owns returned memory.
-    /// If this function returns an error, the contents from the stream read so far are lost.
-    pub fn readUntilDelimiterAlloc(self: &InStream, allocator: &mem.Allocator,
-        delimiter: u8, max_size: usize) ![]u8
-    {
-        var buf = Buffer.initNull(allocator);
-        defer buf.deinit();
-
-        try self.readUntilDelimiterBuffer(self, &buf, delimiter, max_size);
-        return buf.toOwnedSlice();
-    }
+        /// Replaces `buffer` contents by reading from the stream until `delimiter` is found.
+        /// Does not include the delimiter in the result.
+        /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and the contents
+        /// read from the stream so far are lost.
+        pub fn readUntilDelimiterBuffer(self: &Self, buffer: &Buffer, delimiter: u8, max_size: usize) !void {
+            try buf.resize(0);
 
-    /// 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: &InStream, buffer: []u8) !usize {
-        return self.readFn(self, buffer);
-    }
+            while (true) {
+                var byte: u8 = try self.readByte();
 
-    /// Same as `read` but end of stream returns `error.EndOfStream`.
-    pub fn readNoEof(self: &InStream, buf: []u8) !void {
-        const amt_read = try self.read(buf);
-        if (amt_read < buf.len) return error.EndOfStream;
-    }
+                if (byte == delimiter) {
+                    return;
+                }
 
-    /// Reads 1 byte from the stream or returns `error.EndOfStream`.
-    pub fn readByte(self: &InStream) !u8 {
-        var result: [1]u8 = undefined;
-        try self.readNoEof(result[0..]);
-        return result[0];
-    }
+                if (buf.len() == max_size) {
+                    return error.StreamTooLong;
+                }
 
-    /// Same as `readByte` except the returned byte is signed.
-    pub fn readByteSigned(self: &InStream) !i8 {
-        return @bitCast(i8, try self.readByte());
-    }
+                try buf.appendByte(byte);
+            }
+        }
 
-    pub fn readIntLe(self: &InStream, comptime T: type) !T {
-        return self.readInt(builtin.Endian.Little, T);
-    }
+        /// Allocates enough memory to read until `delimiter`. If the allocated
+        /// memory would be greater than `max_size`, returns `error.StreamTooLong`.
+        /// Caller owns returned memory.
+        /// If this function returns an error, the contents from the stream read so far are lost.
+        pub fn readUntilDelimiterAlloc(self: &Self, allocator: &mem.Allocator,
+            delimiter: u8, max_size: usize) ![]u8
+        {
+            var buf = Buffer.initNull(allocator);
+            defer buf.deinit();
+
+            try self.readUntilDelimiterBuffer(self, &buf, delimiter, max_size);
+            return buf.toOwnedSlice();
+        }
 
-    pub fn readIntBe(self: &InStream, comptime T: type) !T {
-        return self.readInt(builtin.Endian.Big, T);
-    }
+        /// 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 readInt(self: &InStream, endian: builtin.Endian, comptime T: type) !T {
-        var bytes: [@sizeOf(T)]u8 = undefined;
-        try self.readNoEof(bytes[0..]);
-        return mem.readInt(bytes, T, endian);
-    }
+        /// Same as `read` 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;
+        }
 
-    pub fn readVarInt(self: &InStream, endian: builtin.Endian, comptime T: type, size: usize) !T {
-        assert(size <= @sizeOf(T));
-        assert(size <= 8);
-        var input_buf: [8]u8 = undefined;
-        const input_slice = input_buf[0..size];
-        try self.readNoEof(input_slice);
-        return mem.readInt(input_slice, T, endian);
-    }
+        /// Reads 1 byte from the stream or returns `error.EndOfStream`.
+        pub fn readByte(self: &Self) !u8 {
+            var result: [1]u8 = undefined;
+            try self.readNoEof(result[0..]);
+            return result[0];
+        }
 
+        /// Same as `readByte` except the returned byte is signed.
+        pub fn readByteSigned(self: &Self) !i8 {
+            return @bitCast(i8, try self.readByte());
+        }
 
-};
+        pub fn readIntLe(self: &Self, comptime T: type) !T {
+            return self.readInt(builtin.Endian.Little, T);
+        }
 
-pub const OutStream = struct {
-    // TODO allow specifying the error set
-    writeFn: fn(self: &OutStream, bytes: []const u8) error!void,
+        pub fn readIntBe(self: &Self, comptime T: type) !T {
+            return self.readInt(builtin.Endian.Big, T);
+        }
 
-    pub fn print(self: &OutStream, comptime format: []const u8, args: ...) !void {
-        return std.fmt.format(self, error, self.writeFn, format, args);
-    }
+        pub fn readInt(self: &Self, endian: builtin.Endian, comptime T: type) !T {
+            var bytes: [@sizeOf(T)]u8 = undefined;
+            try self.readNoEof(bytes[0..]);
+            return mem.readInt(bytes, T, endian);
+        }
 
-    pub fn write(self: &OutStream, bytes: []const u8) !void {
-        return self.writeFn(self, bytes);
-    }
+        pub fn readVarInt(self: &Self, endian: builtin.Endian, comptime T: type, size: usize) !T {
+            assert(size <= @sizeOf(T));
+            assert(size <= 8);
+            var input_buf: [8]u8 = undefined;
+            const input_slice = input_buf[0..size];
+            try self.readNoEof(input_slice);
+            return mem.readInt(input_slice, T, endian);
+        }
+    };
+}
 
-    pub fn writeByte(self: &OutStream, byte: u8) !void {
-        const slice = (&byte)[0..1];
-        return self.writeFn(self, slice);
-    }
+pub fn OutStream(comptime Error: type) type {
+    return struct {
+        const Self = this;
+
+        writeFn: fn(self: &Self, bytes: []const u8) Error!void,
 
-    pub fn writeByteNTimes(self: &OutStream, byte: u8, n: usize) !void {
-        const slice = (&byte)[0..1];
-        var i: usize = 0;
-        while (i < n) : (i += 1) {
-            try self.writeFn(self, slice);
+        pub fn print(self: &Self, comptime format: []const u8, args: ...) !void {
+            return std.fmt.format(self, error, self.writeFn, format, args);
         }
-    }
-};
+
+        pub fn write(self: &Self, bytes: []const u8) !void {
+            return self.writeFn(self, bytes);
+        }
+
+        pub fn writeByte(self: &Self, byte: u8) !void {
+            const slice = (&byte)[0..1];
+            return self.writeFn(self, slice);
+        }
+
+        pub fn writeByteNTimes(self: &Self, byte: u8, n: usize) !void {
+            const slice = (&byte)[0..1];
+            var i: usize = 0;
+            while (i < n) : (i += 1) {
+                try self.writeFn(self, slice);
+            }
+        }
+    };
+}
 
 /// `path` may need to be copied in memory to add a null terminating byte. In this case
 /// a fixed size buffer of size `std.os.max_noalloc_path_len` is an attempted solution. If the fixed