diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2020-09-03 23:52:19 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2020-09-03 23:52:19 -0700 |
| commit | 338f155a02b72117ff710f72c8578e7d2f8eb296 (patch) | |
| tree | a902526d5dc901de7458ef318f52f5ac0dad77e7 /lib/std/io | |
| parent | c354f074fa91d3d1672469ba4bbc49a1730e1d01 (diff) | |
| parent | 88724b2a89157ecc3a8eea03aa0f8a6b66829915 (diff) | |
| download | zig-338f155a02b72117ff710f72c8578e7d2f8eb296.tar.gz zig-338f155a02b72117ff710f72c8578e7d2f8eb296.zip | |
Merge remote-tracking branch 'origin/master' into llvm11
Diffstat (limited to 'lib/std/io')
| -rw-r--r-- | lib/std/io/auto_indenting_stream.zig | 148 | ||||
| -rw-r--r-- | lib/std/io/change_detection_stream.zig | 55 | ||||
| -rw-r--r-- | lib/std/io/find_byte_out_stream.zig | 40 |
3 files changed, 243 insertions, 0 deletions
diff --git a/lib/std/io/auto_indenting_stream.zig b/lib/std/io/auto_indenting_stream.zig new file mode 100644 index 0000000000..d08878e851 --- /dev/null +++ b/lib/std/io/auto_indenting_stream.zig @@ -0,0 +1,148 @@ +const std = @import("../std.zig"); +const io = std.io; +const mem = std.mem; +const assert = std.debug.assert; + +/// Automatically inserts indentation of written data by keeping +/// track of the current indentation level +pub fn AutoIndentingStream(comptime UnderlyingWriter: type) type { + return struct { + const Self = @This(); + pub const Error = UnderlyingWriter.Error; + pub const Writer = io.Writer(*Self, Error, write); + + underlying_writer: UnderlyingWriter, + + indent_count: usize = 0, + indent_delta: usize, + current_line_empty: bool = true, + indent_one_shot_count: usize = 0, // automatically popped when applied + applied_indent: usize = 0, // the most recently applied indent + indent_next_line: usize = 0, // not used until the next line + + pub fn writer(self: *Self) Writer { + return .{ .context = self }; + } + + pub fn write(self: *Self, bytes: []const u8) Error!usize { + if (bytes.len == 0) + return @as(usize, 0); + + try self.applyIndent(); + return self.writeNoIndent(bytes); + } + + // Change the indent delta without changing the final indentation level + pub fn setIndentDelta(self: *Self, indent_delta: usize) void { + if (self.indent_delta == indent_delta) { + return; + } else if (self.indent_delta > indent_delta) { + assert(self.indent_delta % indent_delta == 0); + self.indent_count = self.indent_count * (self.indent_delta / indent_delta); + } else { + // assert that the current indentation (in spaces) in a multiple of the new delta + assert((self.indent_count * self.indent_delta) % indent_delta == 0); + self.indent_count = self.indent_count / (indent_delta / self.indent_delta); + } + self.indent_delta = indent_delta; + } + + fn writeNoIndent(self: *Self, bytes: []const u8) Error!usize { + if (bytes.len == 0) + return @as(usize, 0); + + try self.underlying_writer.writeAll(bytes); + if (bytes[bytes.len - 1] == '\n') + self.resetLine(); + return bytes.len; + } + + pub fn insertNewline(self: *Self) Error!void { + _ = try self.writeNoIndent("\n"); + } + + fn resetLine(self: *Self) void { + self.current_line_empty = true; + self.indent_next_line = 0; + } + + /// Insert a newline unless the current line is blank + pub fn maybeInsertNewline(self: *Self) Error!void { + if (!self.current_line_empty) + try self.insertNewline(); + } + + /// Push default indentation + pub fn pushIndent(self: *Self) void { + // Doesn't actually write any indentation. + // Just primes the stream to be able to write the correct indentation if it needs to. + self.indent_count += 1; + } + + /// Push an indent that is automatically popped after being applied + pub fn pushIndentOneShot(self: *Self) void { + self.indent_one_shot_count += 1; + self.pushIndent(); + } + + /// Turns all one-shot indents into regular indents + /// Returns number of indents that must now be manually popped + pub fn lockOneShotIndent(self: *Self) usize { + var locked_count = self.indent_one_shot_count; + self.indent_one_shot_count = 0; + return locked_count; + } + + /// Push an indent that should not take effect until the next line + pub fn pushIndentNextLine(self: *Self) void { + self.indent_next_line += 1; + self.pushIndent(); + } + + pub fn popIndent(self: *Self) void { + assert(self.indent_count != 0); + self.indent_count -= 1; + + if (self.indent_next_line > 0) + self.indent_next_line -= 1; + } + + /// Writes ' ' bytes if the current line is empty + fn applyIndent(self: *Self) Error!void { + const current_indent = self.currentIndent(); + if (self.current_line_empty and current_indent > 0) { + try self.underlying_writer.writeByteNTimes(' ', current_indent); + self.applied_indent = current_indent; + } + + self.indent_count -= self.indent_one_shot_count; + self.indent_one_shot_count = 0; + self.current_line_empty = false; + } + + /// Checks to see if the most recent indentation exceeds the currently pushed indents + pub fn isLineOverIndented(self: *Self) bool { + if (self.current_line_empty) return false; + return self.applied_indent > self.currentIndent(); + } + + fn currentIndent(self: *Self) usize { + var indent_current: usize = 0; + if (self.indent_count > 0) { + const indent_count = self.indent_count - self.indent_next_line; + indent_current = indent_count * self.indent_delta; + } + return indent_current; + } + }; +} + +pub fn autoIndentingStream( + indent_delta: usize, + underlying_writer: anytype, +) AutoIndentingStream(@TypeOf(underlying_writer)) { + return AutoIndentingStream(@TypeOf(underlying_writer)){ + .underlying_writer = underlying_writer, + .indent_delta = indent_delta, + }; +} diff --git a/lib/std/io/change_detection_stream.zig b/lib/std/io/change_detection_stream.zig new file mode 100644 index 0000000000..5ba2bb3c10 --- /dev/null +++ b/lib/std/io/change_detection_stream.zig @@ -0,0 +1,55 @@ +const std = @import("../std.zig"); +const io = std.io; +const mem = std.mem; +const assert = std.debug.assert; + +/// Used to detect if the data written to a stream differs from a source buffer +pub fn ChangeDetectionStream(comptime WriterType: type) type { + return struct { + const Self = @This(); + pub const Error = WriterType.Error; + pub const Writer = io.Writer(*Self, Error, write); + + anything_changed: bool, + underlying_writer: WriterType, + source_index: usize, + source: []const u8, + + pub fn writer(self: *Self) Writer { + return .{ .context = self }; + } + + fn write(self: *Self, bytes: []const u8) Error!usize { + if (!self.anything_changed) { + const end = self.source_index + bytes.len; + if (end > self.source.len) { + self.anything_changed = true; + } else { + const src_slice = self.source[self.source_index..end]; + self.source_index += bytes.len; + if (!mem.eql(u8, bytes, src_slice)) { + self.anything_changed = true; + } + } + } + + return self.underlying_writer.write(bytes); + } + + pub fn changeDetected(self: *Self) bool { + return self.anything_changed or (self.source_index != self.source.len); + } + }; +} + +pub fn changeDetectionStream( + source: []const u8, + underlying_writer: anytype, +) ChangeDetectionStream(@TypeOf(underlying_writer)) { + return ChangeDetectionStream(@TypeOf(underlying_writer)){ + .anything_changed = false, + .underlying_writer = underlying_writer, + .source_index = 0, + .source = source, + }; +} diff --git a/lib/std/io/find_byte_out_stream.zig b/lib/std/io/find_byte_out_stream.zig new file mode 100644 index 0000000000..b8689b7992 --- /dev/null +++ b/lib/std/io/find_byte_out_stream.zig @@ -0,0 +1,40 @@ +const std = @import("../std.zig"); +const io = std.io; +const assert = std.debug.assert; + +/// An OutStream that returns whether the given character has been written to it. +/// The contents are not written to anything. +pub fn FindByteOutStream(comptime UnderlyingWriter: type) type { + return struct { + const Self = @This(); + pub const Error = UnderlyingWriter.Error; + pub const Writer = io.Writer(*Self, Error, write); + + underlying_writer: UnderlyingWriter, + byte_found: bool, + byte: u8, + + pub fn writer(self: *Self) Writer { + return .{ .context = self }; + } + + fn write(self: *Self, bytes: []const u8) Error!usize { + if (!self.byte_found) { + self.byte_found = blk: { + for (bytes) |b| + if (b == self.byte) break :blk true; + break :blk false; + }; + } + return self.underlying_writer.write(bytes); + } + }; +} + +pub fn findByteOutStream(byte: u8, underlying_writer: anytype) FindByteOutStream(@TypeOf(underlying_writer)) { + return FindByteOutStream(@TypeOf(underlying_writer)){ + .underlying_writer = underlying_writer, + .byte = byte, + .byte_found = false, + }; +} |
