const stack = @import("./stack.zig"); const std = @import("std"); /// /// Opaque interface to a "writable" resource, such as a block device, memory buffer, or network /// socket. /// pub const Writer = struct { context: *anyopaque, operation: fn (*anyopaque, []const u8) usize, /// /// Wraps and returns a reference to `write_context` of type `WriteContext` and its associated /// `writeContext` writing operation in a [Writer]. /// pub fn wrap( comptime WriteContext: type, write_context: *WriteContext, comptime writeContext: fn (*WriteContext, []const u8) usize ) Writer { return .{ .context = write_context, .operation = struct { fn write(context: *anyopaque, buffer: []const u8) usize { return writeContext(@ptrCast(*WriteContext, @alignCast(@alignOf(WriteContext), context)), buffer); } }.write, }; } /// /// Attempts to write `buffer` to `writer`, returning the number of bytes from `buffer` that /// were successfully written. /// pub fn write(writer: Writer, buffer: []const u8) usize { return writer.operation(writer.context, buffer); } /// /// Writes the singular `byte` to `writer`, returning `true` if it was successfully written, /// otherwise `false`. /// pub fn writeByte(writer: Writer, byte: u8) bool { return (writer.operation(writer.context, @ptrCast([*]const u8, &byte)[0 .. 1]) != 0); } /// /// Writes `value` as a ASCII / UTF-8 encoded integer to `writer`, returning `true` if the full /// sequence was successfully written, otherwise `false`. /// /// The `base` argument identifies which base system to encode `value` as, with `10` being /// decimal, `16` being hexadecimal, `8` being octal`, so on and so forth. /// pub fn writeInt(writer: Writer, value: anytype, base: u4) bool { const Int = @TypeOf(value); const type_info = @typeInfo(Int); if (type_info != .Int) @compileError("value must be of type int"); if (value == 0) return writer.writeByte('0'); var buffer = std.mem.zeroes([28]u8); var buffer_count = @as(usize, 0); var n1 = value; if ((type_info.Int.signedness == .signed) and (value < 0)) { // Negative value. n1 = -value; buffer[0] = '-'; buffer_count += 1; } while (n1 != 0) { buffer[buffer_count] = @intCast(u8, (n1 % base) + '0'); n1 = (n1 / base); buffer_count += 1; } for (buffer[0 .. (buffer_count / 2)]) |_, i| std.mem.swap(u8, &buffer[i], &buffer[buffer_count - i - 1]); return (writer.write(buffer[0 .. buffer_count]) == buffer_count); } }; var null_context = @as(usize, 0); /// /// Writer that silently throws consumed data away and never fails. /// /// This is commonly used for testing or redirected otherwise unwanted output data that can't not be /// sent somewhere for whatever reason. /// pub const null_writer = Writer{ .context = (&null_context), .operation = struct { fn write(context: *anyopaque, buffer: []const u8) usize { // Validate context canary value. std.debug.assert(@ptrCast(*usize, @alignCast(@alignOf(usize), context)).* == 0); return buffer.len; } }.write, };