Skip to content

Commit d8c0679

Browse files
authored
Merge pull request #17400 from joadnacer/ringbuffer-optim
std.RingBuffer: Implement mem.copy read/write
2 parents ccc9ebf + a8eb70c commit d8c0679

File tree

3 files changed

+95
-10
lines changed

3 files changed

+95
-10
lines changed

lib/std/RingBuffer.zig

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@
1111

1212
const Allocator = @import("std").mem.Allocator;
1313
const assert = @import("std").debug.assert;
14+
const copyForwards = @import("std").mem.copyForwards;
1415

1516
const RingBuffer = @This();
1617

1718
data: []u8,
1819
read_index: usize,
1920
write_index: usize,
2021

21-
pub const Error = error{Full};
22+
pub const Error = error{ Full, ReadLengthInvalid };
2223

2324
/// Allocate a new `RingBuffer`; `deinit()` should be called to free the buffer.
2425
pub fn init(allocator: Allocator, capacity: usize) Allocator.Error!RingBuffer {
@@ -63,15 +64,59 @@ pub fn writeAssumeCapacity(self: *RingBuffer, byte: u8) void {
6364

6465
/// Write `bytes` into the ring buffer. Returns `error.Full` if the ring
6566
/// buffer does not have enough space, without writing any data.
67+
/// Uses memcpy and so `bytes` must not overlap ring buffer data.
6668
pub fn writeSlice(self: *RingBuffer, bytes: []const u8) Error!void {
6769
if (self.len() + bytes.len > self.data.len) return error.Full;
6870
self.writeSliceAssumeCapacity(bytes);
6971
}
7072

7173
/// Write `bytes` into the ring buffer. If there is not enough space, older
7274
/// bytes will be overwritten.
75+
/// Uses memcpy and so `bytes` must not overlap ring buffer data.
7376
pub fn writeSliceAssumeCapacity(self: *RingBuffer, bytes: []const u8) void {
74-
for (bytes) |b| self.writeAssumeCapacity(b);
77+
const data_start = self.mask(self.write_index);
78+
const part1_data_end = @min(data_start + bytes.len, self.data.len);
79+
const part1_len = part1_data_end - data_start;
80+
@memcpy(self.data[data_start..part1_data_end], bytes[0..part1_len]);
81+
82+
const remaining = bytes.len - part1_len;
83+
const to_write = @min(remaining, remaining % self.data.len + self.data.len);
84+
const part2_bytes_start = bytes.len - to_write;
85+
const part2_bytes_end = @min(part2_bytes_start + self.data.len, bytes.len);
86+
const part2_len = part2_bytes_end - part2_bytes_start;
87+
@memcpy(self.data[0..part2_len], bytes[part2_bytes_start..part2_bytes_end]);
88+
if (part2_bytes_end != bytes.len) {
89+
const part3_len = bytes.len - part2_bytes_end;
90+
@memcpy(self.data[0..part3_len], bytes[part2_bytes_end..bytes.len]);
91+
}
92+
self.write_index = self.mask2(self.write_index + bytes.len);
93+
}
94+
95+
/// Write `bytes` into the ring buffer. Returns `error.Full` if the ring
96+
/// buffer does not have enough space, without writing any data.
97+
/// Uses copyForwards and can write slices from this RingBuffer into itself.
98+
pub fn writeSliceForwards(self: *RingBuffer, bytes: []const u8) Error!void {
99+
if (self.len() + bytes.len > self.data.len) return error.Full;
100+
self.writeSliceForwardsAssumeCapacity(bytes);
101+
}
102+
103+
/// Write `bytes` into the ring buffer. If there is not enough space, older
104+
/// bytes will be overwritten.
105+
/// Uses copyForwards and can write slices from this RingBuffer into itself.
106+
pub fn writeSliceForwardsAssumeCapacity(self: *RingBuffer, bytes: []const u8) void {
107+
const data_start = self.mask(self.write_index);
108+
const part1_data_end = @min(data_start + bytes.len, self.data.len);
109+
const part1_len = part1_data_end - data_start;
110+
copyForwards(u8, self.data[data_start..], bytes[0..part1_len]);
111+
112+
const remaining = bytes.len - part1_len;
113+
const to_write = @min(remaining, remaining % self.data.len + self.data.len);
114+
const part2_bytes_start = bytes.len - to_write;
115+
const part2_bytes_end = @min(part2_bytes_start + self.data.len, bytes.len);
116+
copyForwards(u8, self.data[0..], bytes[part2_bytes_start..part2_bytes_end]);
117+
if (part2_bytes_end != bytes.len)
118+
copyForwards(u8, self.data[0..], bytes[part2_bytes_end..bytes.len]);
119+
self.write_index = self.mask2(self.write_index + bytes.len);
75120
}
76121

77122
/// Consume a byte from the ring buffer and return it. Returns `null` if the
@@ -90,6 +135,50 @@ pub fn readAssumeLength(self: *RingBuffer) u8 {
90135
return byte;
91136
}
92137

138+
/// Reads first `length` bytes written to the ring buffer into `dest`; Returns
139+
/// Error.ReadLengthInvalid if length greater than ring or dest length
140+
/// Uses memcpy and so `dest` must not overlap ring buffer data.
141+
pub fn readFirst(self: *RingBuffer, dest: []u8, length: usize) Error!void {
142+
if (length > self.len() or length > dest.len) return error.ReadLengthInvalid;
143+
self.readFirstAssumeLength(dest, length);
144+
}
145+
146+
/// Reads first `length` bytes written to the ring buffer into `dest`;
147+
/// Asserts that length not greater than ring buffer or dest length
148+
/// Uses memcpy and so `dest` must not overlap ring buffer data.
149+
pub fn readFirstAssumeLength(self: *RingBuffer, dest: []u8, length: usize) void {
150+
assert(length <= self.len() and length <= dest.len);
151+
const data_start = self.mask(self.read_index);
152+
const part1_data_end = @min(self.data.len, data_start + length);
153+
const part1_len = part1_data_end - data_start;
154+
const part2_len = length - part1_len;
155+
@memcpy(dest[0..part1_len], self.data[data_start..part1_data_end]);
156+
@memcpy(dest[part1_len..length], self.data[0..part2_len]);
157+
self.read_index = self.mask2(self.read_index + length);
158+
}
159+
160+
/// Reads last `length` bytes written to the ring buffer into `dest`; Returns
161+
/// Error.ReadLengthInvalid if length greater than ring or dest length
162+
/// Uses memcpy and so `dest` must not overlap ring buffer data.
163+
pub fn readLast(self: *RingBuffer, dest: []u8, length: usize) Error!void {
164+
if (length > self.len() or length > dest.len) return error.ReadLengthInvalid;
165+
self.readLastAssumeLength(dest, length);
166+
}
167+
168+
/// Reads last `length` bytes written to the ring buffer into `dest`;
169+
/// Asserts that length not greater than ring buffer or dest length
170+
/// Uses memcpy and so `dest` must not overlap ring buffer data.
171+
pub fn readLastAssumeLength(self: *RingBuffer, dest: []u8, length: usize) void {
172+
assert(length <= self.len() and length <= dest.len);
173+
const data_start = self.mask(self.write_index + self.data.len - length);
174+
const part1_data_end = @min(self.data.len, data_start + length);
175+
const part1_len = part1_data_end - data_start;
176+
const part2_len = length - part1_len;
177+
@memcpy(dest[0..part1_len], self.data[data_start..part1_data_end]);
178+
@memcpy(dest[part1_len..length], self.data[0..part2_len]);
179+
self.write_index = if (self.write_index >= self.data_len) self.write_index - length else data_start;
180+
}
181+
93182
/// Returns `true` if the ring buffer is empty and `false` otherwise.
94183
pub fn isEmpty(self: RingBuffer) bool {
95184
return self.write_index == self.read_index;

lib/std/compress/zstandard.zig

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,7 @@ pub fn DecompressStream(
219219
}
220220

221221
const size = @min(self.buffer.len(), buffer.len);
222-
for (0..size) |i| {
223-
buffer[i] = self.buffer.read().?;
224-
}
222+
self.buffer.readFirstAssumeLength(buffer, size);
225223
if (self.state == .LastBlock and self.buffer.len() == 0) {
226224
self.state = .NewFrame;
227225
self.allocator.free(self.literal_fse_buffer);

lib/std/compress/zstandard/decode/block.zig

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -311,8 +311,8 @@ pub const DecodeState = struct {
311311
try self.decodeLiteralsRingBuffer(dest, sequence.literal_length);
312312
const copy_start = dest.write_index + dest.data.len - sequence.offset;
313313
const copy_slice = dest.sliceAt(copy_start, sequence.match_length);
314-
for (copy_slice.first) |b| dest.writeAssumeCapacity(b);
315-
for (copy_slice.second) |b| dest.writeAssumeCapacity(b);
314+
dest.writeSliceForwardsAssumeCapacity(copy_slice.first);
315+
dest.writeSliceForwardsAssumeCapacity(copy_slice.second);
316316
self.written_count += sequence.match_length;
317317
}
318318

@@ -723,9 +723,7 @@ pub fn decodeBlockRingBuffer(
723723
},
724724
.rle => {
725725
if (src.len < 1) return error.MalformedRleBlock;
726-
for (0..block_size) |_| {
727-
dest.writeAssumeCapacity(src[0]);
728-
}
726+
dest.writeSliceAssumeCapacity(src[0..block_size]);
729727
consumed_count.* += 1;
730728
decode_state.written_count += block_size;
731729
return block_size;

0 commit comments

Comments
 (0)