mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
This commit is non-breaking. std.io is deprecated in favor of std.Io, in preparation for that namespace becoming an interface.
198 lines
6.3 KiB
Zig
198 lines
6.3 KiB
Zig
const std = @import("../std.zig");
|
|
const io = std.io;
|
|
const testing = std.testing;
|
|
const mem = std.mem;
|
|
const assert = std.debug.assert;
|
|
|
|
/// This turns a byte buffer into an `io.GenericWriter`, `io.GenericReader`, or `io.SeekableStream`.
|
|
/// If the supplied byte buffer is const, then `io.GenericWriter` is not available.
|
|
pub fn FixedBufferStream(comptime Buffer: type) type {
|
|
return struct {
|
|
/// `Buffer` is either a `[]u8` or `[]const u8`.
|
|
buffer: Buffer,
|
|
pos: usize,
|
|
|
|
pub const ReadError = error{};
|
|
pub const WriteError = error{NoSpaceLeft};
|
|
pub const SeekError = error{};
|
|
pub const GetSeekPosError = error{};
|
|
|
|
pub const Reader = io.GenericReader(*Self, ReadError, read);
|
|
pub const Writer = io.GenericWriter(*Self, WriteError, write);
|
|
|
|
pub const SeekableStream = io.SeekableStream(
|
|
*Self,
|
|
SeekError,
|
|
GetSeekPosError,
|
|
seekTo,
|
|
seekBy,
|
|
getPos,
|
|
getEndPos,
|
|
);
|
|
|
|
const Self = @This();
|
|
|
|
pub fn reader(self: *Self) Reader {
|
|
return .{ .context = self };
|
|
}
|
|
|
|
pub fn writer(self: *Self) Writer {
|
|
return .{ .context = self };
|
|
}
|
|
|
|
pub fn seekableStream(self: *Self) SeekableStream {
|
|
return .{ .context = self };
|
|
}
|
|
|
|
pub fn read(self: *Self, dest: []u8) ReadError!usize {
|
|
const size = @min(dest.len, self.buffer.len - self.pos);
|
|
const end = self.pos + size;
|
|
|
|
@memcpy(dest[0..size], self.buffer[self.pos..end]);
|
|
self.pos = end;
|
|
|
|
return size;
|
|
}
|
|
|
|
/// If the returned number of bytes written is less than requested, the
|
|
/// buffer is full. Returns `error.NoSpaceLeft` when no bytes would be written.
|
|
/// Note: `error.NoSpaceLeft` matches the corresponding error from
|
|
/// `std.fs.File.WriteError`.
|
|
pub fn write(self: *Self, bytes: []const u8) WriteError!usize {
|
|
if (bytes.len == 0) return 0;
|
|
if (self.pos >= self.buffer.len) return error.NoSpaceLeft;
|
|
|
|
const n = @min(self.buffer.len - self.pos, bytes.len);
|
|
@memcpy(self.buffer[self.pos..][0..n], bytes[0..n]);
|
|
self.pos += n;
|
|
|
|
if (n == 0) return error.NoSpaceLeft;
|
|
|
|
return n;
|
|
}
|
|
|
|
pub fn seekTo(self: *Self, pos: u64) SeekError!void {
|
|
self.pos = @min(std.math.lossyCast(usize, pos), self.buffer.len);
|
|
}
|
|
|
|
pub fn seekBy(self: *Self, amt: i64) SeekError!void {
|
|
if (amt < 0) {
|
|
const abs_amt = @abs(amt);
|
|
const abs_amt_usize = std.math.cast(usize, abs_amt) orelse std.math.maxInt(usize);
|
|
if (abs_amt_usize > self.pos) {
|
|
self.pos = 0;
|
|
} else {
|
|
self.pos -= abs_amt_usize;
|
|
}
|
|
} else {
|
|
const amt_usize = std.math.cast(usize, amt) orelse std.math.maxInt(usize);
|
|
const new_pos = std.math.add(usize, self.pos, amt_usize) catch std.math.maxInt(usize);
|
|
self.pos = @min(self.buffer.len, new_pos);
|
|
}
|
|
}
|
|
|
|
pub fn getEndPos(self: *Self) GetSeekPosError!u64 {
|
|
return self.buffer.len;
|
|
}
|
|
|
|
pub fn getPos(self: *Self) GetSeekPosError!u64 {
|
|
return self.pos;
|
|
}
|
|
|
|
pub fn getWritten(self: Self) Buffer {
|
|
return self.buffer[0..self.pos];
|
|
}
|
|
|
|
pub fn reset(self: *Self) void {
|
|
self.pos = 0;
|
|
}
|
|
};
|
|
}
|
|
|
|
pub fn fixedBufferStream(buffer: anytype) FixedBufferStream(Slice(@TypeOf(buffer))) {
|
|
return .{ .buffer = buffer, .pos = 0 };
|
|
}
|
|
|
|
fn Slice(comptime T: type) type {
|
|
switch (@typeInfo(T)) {
|
|
.pointer => |ptr_info| {
|
|
var new_ptr_info = ptr_info;
|
|
switch (ptr_info.size) {
|
|
.slice => {},
|
|
.one => switch (@typeInfo(ptr_info.child)) {
|
|
.array => |info| new_ptr_info.child = info.child,
|
|
else => @compileError("invalid type given to fixedBufferStream"),
|
|
},
|
|
else => @compileError("invalid type given to fixedBufferStream"),
|
|
}
|
|
new_ptr_info.size = .slice;
|
|
return @Type(.{ .pointer = new_ptr_info });
|
|
},
|
|
else => @compileError("invalid type given to fixedBufferStream"),
|
|
}
|
|
}
|
|
|
|
test "output" {
|
|
var buf: [255]u8 = undefined;
|
|
var fbs = fixedBufferStream(&buf);
|
|
const stream = fbs.writer();
|
|
|
|
try stream.print("{s}{s}!", .{ "Hello", "World" });
|
|
try testing.expectEqualSlices(u8, "HelloWorld!", fbs.getWritten());
|
|
}
|
|
|
|
test "output at comptime" {
|
|
comptime {
|
|
var buf: [255]u8 = undefined;
|
|
var fbs = fixedBufferStream(&buf);
|
|
const stream = fbs.writer();
|
|
|
|
try stream.print("{s}{s}!", .{ "Hello", "World" });
|
|
try testing.expectEqualSlices(u8, "HelloWorld!", fbs.getWritten());
|
|
}
|
|
}
|
|
|
|
test "output 2" {
|
|
var buffer: [10]u8 = undefined;
|
|
var fbs = fixedBufferStream(&buffer);
|
|
|
|
try fbs.writer().writeAll("Hello");
|
|
try testing.expect(mem.eql(u8, fbs.getWritten(), "Hello"));
|
|
|
|
try fbs.writer().writeAll("world");
|
|
try testing.expect(mem.eql(u8, fbs.getWritten(), "Helloworld"));
|
|
|
|
try testing.expectError(error.NoSpaceLeft, fbs.writer().writeAll("!"));
|
|
try testing.expect(mem.eql(u8, fbs.getWritten(), "Helloworld"));
|
|
|
|
fbs.reset();
|
|
try testing.expect(fbs.getWritten().len == 0);
|
|
|
|
try testing.expectError(error.NoSpaceLeft, fbs.writer().writeAll("Hello world!"));
|
|
try testing.expect(mem.eql(u8, fbs.getWritten(), "Hello worl"));
|
|
|
|
try fbs.seekTo((try fbs.getEndPos()) + 1);
|
|
try testing.expectError(error.NoSpaceLeft, fbs.writer().writeAll("H"));
|
|
}
|
|
|
|
test "input" {
|
|
const bytes = [_]u8{ 1, 2, 3, 4, 5, 6, 7 };
|
|
var fbs = fixedBufferStream(&bytes);
|
|
|
|
var dest: [4]u8 = undefined;
|
|
|
|
var read = try fbs.reader().read(&dest);
|
|
try testing.expect(read == 4);
|
|
try testing.expect(mem.eql(u8, dest[0..4], bytes[0..4]));
|
|
|
|
read = try fbs.reader().read(&dest);
|
|
try testing.expect(read == 3);
|
|
try testing.expect(mem.eql(u8, dest[0..3], bytes[4..7]));
|
|
|
|
read = try fbs.reader().read(&dest);
|
|
try testing.expect(read == 0);
|
|
|
|
try fbs.seekTo((try fbs.getEndPos()) + 1);
|
|
read = try fbs.reader().read(&dest);
|
|
try testing.expect(read == 0);
|
|
}
|