aboutsummaryrefslogtreecommitdiff
path: root/lib/std/io
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2020-09-03 23:52:19 -0700
committerAndrew Kelley <andrew@ziglang.org>2020-09-03 23:52:19 -0700
commit338f155a02b72117ff710f72c8578e7d2f8eb296 (patch)
treea902526d5dc901de7458ef318f52f5ac0dad77e7 /lib/std/io
parentc354f074fa91d3d1672469ba4bbc49a1730e1d01 (diff)
parent88724b2a89157ecc3a8eea03aa0f8a6b66829915 (diff)
downloadzig-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.zig148
-rw-r--r--lib/std/io/change_detection_stream.zig55
-rw-r--r--lib/std/io/find_byte_out_stream.zig40
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,
+ };
+}