@@ -67,6 +67,18 @@ pub const VTable = struct {
67
67
///
68
68
/// This function is only called when `buffer` is empty.
69
69
discard : * const fn (r : * Reader , limit : Limit ) Error ! usize = defaultDiscard ,
70
+
71
+ /// Ensures `capacity` more data can be buffered without rebasing.
72
+ ///
73
+ /// Asserts `capacity` is within buffer capacity, or that the stream ends
74
+ /// within `capacity` bytes.
75
+ ///
76
+ /// Only called when `capacity` cannot fit into the unused capacity of
77
+ /// `buffer`.
78
+ ///
79
+ /// The default implementation moves buffered data to the start of
80
+ /// `buffer`, setting `seek` to zero, and cannot fail.
81
+ rebase : * const fn (r : * Reader , capacity : usize ) RebaseError ! void = defaultRebase ,
70
82
};
71
83
72
84
pub const StreamError = error {
@@ -97,6 +109,10 @@ pub const ShortError = error{
97
109
ReadFailed ,
98
110
};
99
111
112
+ pub const RebaseError = error {
113
+ EndOfStream ,
114
+ };
115
+
100
116
pub const failing : Reader = .{
101
117
.vtable = &.{
102
118
.stream = failingStream ,
@@ -122,6 +138,7 @@ pub fn fixed(buffer: []const u8) Reader {
122
138
.vtable = &.{
123
139
.stream = endingStream ,
124
140
.discard = endingDiscard ,
141
+ .rebase = endingRebase ,
125
142
},
126
143
// This cast is safe because all potential writes to it will instead
127
144
// return `error.EndOfStream`.
@@ -780,11 +797,8 @@ pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {
780
797
@branchHint (.likely );
781
798
return buffer [seek .. end + 1 ];
782
799
}
783
- if (r .vtable .stream == & endingStream ) {
784
- // Protect the `@constCast` of `fixed`.
785
- return error .EndOfStream ;
786
- }
787
- r .rebase ();
800
+ // TODO take a parameter for max search length rather than relying on buffer capacity
801
+ try rebase (r , r .buffer .len );
788
802
while (r .buffer .len - r .end != 0 ) {
789
803
const end_cap = r .buffer [r .end .. ];
790
804
var writer : Writer = .fixed (end_cap );
@@ -1050,11 +1064,7 @@ fn fillUnbuffered(r: *Reader, n: usize) Error!void {
1050
1064
};
1051
1065
if (r .seek + n <= r .end ) return ;
1052
1066
};
1053
- if (r .vtable .stream == & endingStream ) {
1054
- // Protect the `@constCast` of `fixed`.
1055
- return error .EndOfStream ;
1056
- }
1057
- rebaseCapacity (r , n );
1067
+ try rebase (r , n );
1058
1068
var writer : Writer = .{
1059
1069
.buffer = r .buffer ,
1060
1070
.vtable = &.{ .drain = Writer .fixedDrain },
@@ -1074,7 +1084,7 @@ fn fillUnbuffered(r: *Reader, n: usize) Error!void {
1074
1084
///
1075
1085
/// Asserts buffer capacity is at least 1.
1076
1086
pub fn fillMore (r : * Reader ) Error ! void {
1077
- rebaseCapacity (r , 1 );
1087
+ try rebase (r , 1 );
1078
1088
var writer : Writer = .{
1079
1089
.buffer = r .buffer ,
1080
1090
.end = r .end ,
@@ -1251,7 +1261,7 @@ pub fn takeLeb128(r: *Reader, comptime Result: type) TakeLeb128Error!Result {
1251
1261
1252
1262
pub fn expandTotalCapacity (r : * Reader , allocator : Allocator , n : usize ) Allocator.Error ! void {
1253
1263
if (n <= r .buffer .len ) return ;
1254
- if (r .seek > 0 ) rebase (r );
1264
+ if (r .seek > 0 ) rebase (r , r . buffer . len );
1255
1265
var list : ArrayList (u8 ) = .{
1256
1266
.items = r .buffer [0.. r .end ],
1257
1267
.capacity = r .buffer .len ,
@@ -1297,37 +1307,20 @@ fn takeMultipleOf7Leb128(r: *Reader, comptime Result: type) TakeLeb128Error!Resu
1297
1307
}
1298
1308
}
1299
1309
1300
- /// Left-aligns data such that `r.seek` becomes zero.
1301
- ///
1302
- /// If `r.seek` is not already zero then `buffer` is mutated, making it illegal
1303
- /// to call this function with a const-casted `buffer`, such as in the case of
1304
- /// `fixed`. This issue can be avoided:
1305
- /// * in implementations, by attempting a read before a rebase, in which
1306
- /// case the read will return `error.EndOfStream`, preventing the rebase.
1307
- /// * in usage, by copying into a mutable buffer before initializing `fixed`.
1308
- pub fn rebase (r : * Reader ) void {
1309
- if (r .seek == 0 ) return ;
1310
+ /// Ensures `capacity` more data can be buffered without rebasing.
1311
+ pub fn rebase (r : * Reader , capacity : usize ) RebaseError ! void {
1312
+ if (r .end + capacity <= r .buffer .len ) return ;
1313
+ return r .vtable .rebase (r , capacity );
1314
+ }
1315
+
1316
+ pub fn defaultRebase (r : * Reader , capacity : usize ) RebaseError ! void {
1317
+ if (r .end <= r .buffer .len - capacity ) return ;
1310
1318
const data = r .buffer [r .seek .. r .end ];
1311
1319
@memmove (r .buffer [0.. data .len ], data );
1312
1320
r .seek = 0 ;
1313
1321
r .end = data .len ;
1314
1322
}
1315
1323
1316
- /// Ensures `capacity` more data can be buffered without rebasing, by rebasing
1317
- /// if necessary.
1318
- ///
1319
- /// Asserts `capacity` is within the buffer capacity.
1320
- ///
1321
- /// If the rebase occurs then `buffer` is mutated, making it illegal to call
1322
- /// this function with a const-casted `buffer`, such as in the case of `fixed`.
1323
- /// This issue can be avoided:
1324
- /// * in implementations, by attempting a read before a rebase, in which
1325
- /// case the read will return `error.EndOfStream`, preventing the rebase.
1326
- /// * in usage, by copying into a mutable buffer before initializing `fixed`.
1327
- pub fn rebaseCapacity (r : * Reader , capacity : usize ) void {
1328
- if (r .end > r .buffer .len - capacity ) rebase (r );
1329
- }
1330
-
1331
1324
/// Advances the stream and decreases the size of the storage buffer by `n`,
1332
1325
/// returning the range of bytes no longer accessible by `r`.
1333
1326
///
@@ -1683,6 +1676,12 @@ fn endingDiscard(r: *Reader, limit: Limit) Error!usize {
1683
1676
return error .EndOfStream ;
1684
1677
}
1685
1678
1679
+ fn endingRebase (r : * Reader , capacity : usize ) RebaseError ! void {
1680
+ _ = r ;
1681
+ _ = capacity ;
1682
+ return error .EndOfStream ;
1683
+ }
1684
+
1686
1685
fn failingStream (r : * Reader , w : * Writer , limit : Limit ) StreamError ! usize {
1687
1686
_ = r ;
1688
1687
_ = w ;
0 commit comments