Created
June 10, 2023 10:55
-
-
Save notcancername/6bf003f1d8b226da7d6a04c5d911944b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const std = @import("std"); | |
const io = std.io; | |
const math = std.math; | |
// TODO: this is a massive hack that relies on implementation details | |
// and **WILL** break when BufferedReader or SeekableStream change, | |
// however, I do not see any other way, so this is fine for now. | |
/// An amalgam of `std.io.BufferedReader` and `std.io.SeekableStream`, such that they work together. | |
/// | |
/// Get the reader with `reader` and the seekable stream with `seekableStream`. | |
pub fn BufferedSeekableReader( | |
comptime buffer_size: usize, | |
comptime Reader: type, | |
comptime SeekableStream: type, | |
) type { | |
const SeekError = SeekableStream.SeekError; | |
const GetSeekPosError = SeekableStream.GetSeekPosError; | |
const BufferedReader = io.BufferedReader(buffer_size, Reader); | |
return struct { | |
unseekable_buffered_reader: BufferedReader, | |
seekable: SeekableStream, | |
const Self = @This(); | |
pub fn seekableStream(self: *Self) io.SeekableStream( | |
*Self, | |
SeekableStream.SeekError, | |
SeekableStream.GetSeekPosError, | |
seekTo, | |
seekBy, | |
getPos, | |
getEndPos, | |
) { | |
return .{.context = self}; | |
} | |
pub fn reader(self: *Self) BufferedReader.Reader { | |
return self.unseekable_buffered_reader.reader(); | |
} | |
fn emptyBuffer(self: *Self) void { | |
self.unseekable_buffered_reader.start = 0; | |
self.unseekable_buffered_reader.end = 0; | |
self.unseekable_buffered_reader.buf = undefined; | |
} | |
fn bytesInBuf(self: *Self) usize { | |
const ubr = self.unseekable_buffered_reader; | |
return ubr.end - ubr.start; | |
} | |
fn seekTo(self: *Self, pos: u64) SeekError!void { | |
const cur_pos = self.getPos() catch return self.seekToFar(pos); | |
const diff = @as(i65, pos) - @as(i65, cur_pos); | |
if(diff > 0 and math.absCast(diff) <= math.maxInt(i64)) { | |
return self.seekBy(@intCast(i64, math.absCast(diff))); | |
} | |
return self.seekToFar(pos); | |
} | |
fn seekBy(self: *Self, amt: i64) SeekError!void { | |
if(amt > 0 and amt < self.bytesInBuf() and amt <= std.math.maxInt(usize)) { | |
self.unseekable_buffered_reader.start += @intCast(usize, amt); | |
return; | |
} | |
return self.seekByFar(amt); | |
} | |
fn getEndPos(self: *Self) GetSeekPosError!u64 { | |
return self.seekable.getEndPos(); | |
} | |
fn getPos(self: *Self) GetSeekPosError!u64 { | |
return (try self.seekable.getPos()) - (self.unseekable_buffered_reader.end - self.unseekable_buffered_reader.start); | |
} | |
fn seekToFar(self: *Self, pos: u64) SeekError!void { | |
self.emptyBuffer(); | |
return self.seekable.seekTo(pos); | |
} | |
fn seekByFar(self: *Self, amt: i64) SeekError!void { | |
self.emptyBuffer(); | |
return self.seekable.seekBy(amt); | |
} | |
}; | |
} | |
pub fn bufferedSeekableReaderSize( | |
comptime buffer_size: usize, | |
reader: anytype, | |
seekable_stream: anytype | |
) BufferedSeekableReader(buffer_size, @TypeOf(reader), @TypeOf(seekable_stream)) { | |
return BufferedSeekableReader(buffer_size, @TypeOf(reader), @TypeOf(seekable_stream)){ | |
.unseekable_buffered_reader = io.bufferedReaderSize(buffer_size, reader), | |
.seekable = seekable_stream, | |
}; | |
} | |
pub fn bufferedSeekableReader( | |
reader: anytype, | |
seekable_stream: anytype | |
) BufferedSeekableReader(4096, @TypeOf(reader), @TypeOf(seekable_stream)) { | |
return bufferedSeekableReaderSize(4096, reader, seekable_stream); | |
} | |
test "intra-buffer seek" { | |
const s1 = "The sun dipped below the horizon, casting a warm golden glow over the tranquil meadow."; | |
const s2 = "The air was filled with the sweet scent of wildflowers, their vibrant colors dotting the landscape like a painter's masterpiece."; | |
const s3 = "Birds chirped their evening melodies, while a gentle breeze rustled the tall grass, creating a soothing symphony of nature."; | |
const s4 = "In this moment of serenity, all worries and troubles seemed to fade away, replaced by a profound sense of peace and harmony."; | |
const s = s1 ++ " " ++ s2 ++ " " ++ s3 ++ " " ++ s4; | |
var fbs = io.fixedBufferStream(s); | |
var bsr = bufferedSeekableReaderSize(512, fbs.reader(), fbs.seekableStream()); | |
const reader = bsr.reader(); | |
const seekable_stream = bsr.seekableStream(); | |
try std.testing.expectEqualSlices(u8, s1, &(try reader.readBytesNoEof(s1.len))); | |
try seekable_stream.seekTo(0); | |
try std.testing.expectEqualSlices(u8, s1, &(try reader.readBytesNoEof(s1.len))); | |
try seekable_stream.seekTo(0); | |
_ = try reader.readByte(); | |
try seekable_stream.seekBy(2); | |
std.debug.print("{!}\n", .{bsr.getPos()}); | |
try std.testing.expectEqualSlices(u8, s1[3..][0..3], &(try reader.readBytesNoEof(3))); | |
try seekable_stream.seekTo(8); | |
try std.testing.expectEqualSlices(u8, s1[8..][0..3], &(try reader.readBytesNoEof(3))); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment