const std = @import("std"); pub const ReadError = error { EndOfBuffer }; pub fn Cursor(comptime T: type) type { return struct { const Self = @This(); buffer: []const T, index: usize, touched_index: usize, pub fn init(buffer: []const T) Self { return .{ .buffer = buffer, .index = 0, .touched_index = 0, }; } pub fn peek(self: Self, amount: usize) ReadError![]const T { if (self.index + amount > self.buffer.len) { return ReadError.EndOfBuffer; } return self.buffer[self.index..self.index + amount]; } pub fn consume(self: *Self, amount: usize) ReadError![]const T { const slice = try self.peek(amount); self.index += amount; self.touched_index = self.index; return slice; } pub fn snapshot(self: Self) usize { return self.index; } pub fn rollback(self: *Self, index: usize) void { self.index = index; } pub fn touched_slice(self: Self) []const T { return self.buffer[0..self.touched_index]; } }; } test "peek" { const buffer = "1234"; const cursor = Cursor(u8).init(buffer); try std.testing.expectEqualSlices(u8, "1", try cursor.peek(1)); try std.testing.expectEqualSlices(u8, "12", try cursor.peek(2)); try std.testing.expectEqualSlices(u8, "123", try cursor.peek(3)); try std.testing.expectEqualSlices(u8, "1234", try cursor.peek(4)); try std.testing.expectError(ReadError.EndOfBuffer, cursor.peek(5)); } test "consume" { const buffer = "12345678"; var cursor = Cursor(u8).init(buffer); try std.testing.expectEqualSlices(u8, "1", try cursor.consume(1)); try std.testing.expectEqualSlices(u8, "23", try cursor.consume(2)); try std.testing.expectEqualSlices(u8, "456", try cursor.consume(3)); try std.testing.expectError(ReadError.EndOfBuffer, cursor.consume(4)); }