| ... | ... | @@ -1,230 +0,0 @@ |
| 1 | | //! This ring buffer stores read and write indices while being able to utilise |
| 2 | | //! the full backing slice by incrementing the indices modulo twice the slice's |
| 3 | | //! length and reducing indices modulo the slice's length on slice access. This |
| 4 | | //! means that whether the ring buffer is full or empty can be distinguished by |
| 5 | | //! looking at the difference between the read and write indices without adding |
| 6 | | //! an extra boolean flag or having to reserve a slot in the buffer. |
| 7 | | //! |
| 8 | | //! This ring buffer has not been implemented with thread safety in mind, and |
| 9 | | //! therefore should not be assumed to be suitable for use cases involving |
| 10 | | //! separate reader and writer threads. |
| 11 | | |
| 12 | | const Allocator = @import("std").mem.Allocator; |
| 13 | | const assert = @import("std").debug.assert; |
| 14 | | const copyForwards = @import("std").mem.copyForwards; |
| 15 | | |
| 16 | | const RingBuffer = @This(); |
| 17 | | |
| 18 | | data: []u8, |
| 19 | | read_index: usize, |
| 20 | | write_index: usize, |
| 21 | | |
| 22 | | pub const Error = error{ Full, ReadLengthInvalid }; |
| 23 | | |
| 24 | | /// Allocate a new `RingBuffer`; `deinit()` should be called to free the buffer. |
| 25 | | pub fn init(allocator: Allocator, capacity: usize) Allocator.Error!RingBuffer { |
| 26 | | const bytes = try allocator.alloc(u8, capacity); |
| 27 | | return RingBuffer{ |
| 28 | | .data = bytes, |
| 29 | | .write_index = 0, |
| 30 | | .read_index = 0, |
| 31 | | }; |
| 32 | | } |
| 33 | | |
| 34 | | /// Free the data backing a `RingBuffer`; must be passed the same `Allocator` as |
| 35 | | /// `init()`. |
| 36 | | pub fn deinit(self: *RingBuffer, allocator: Allocator) void { |
| 37 | | allocator.free(self.data); |
| 38 | | self.* = undefined; |
| 39 | | } |
| 40 | | |
| 41 | | /// Returns `index` modulo the length of the backing slice. |
| 42 | | pub fn mask(self: RingBuffer, index: usize) usize { |
| 43 | | return index % self.data.len; |
| 44 | | } |
| 45 | | |
| 46 | | /// Returns `index` modulo twice the length of the backing slice. |
| 47 | | pub fn mask2(self: RingBuffer, index: usize) usize { |
| 48 | | return index % (2 * self.data.len); |
| 49 | | } |
| 50 | | |
| 51 | | /// Write `byte` into the ring buffer. Returns `error.Full` if the ring |
| 52 | | /// buffer is full. |
| 53 | | pub fn write(self: *RingBuffer, byte: u8) Error!void { |
| 54 | | if (self.isFull()) return error.Full; |
| 55 | | self.writeAssumeCapacity(byte); |
| 56 | | } |
| 57 | | |
| 58 | | /// Write `byte` into the ring buffer. If the ring buffer is full, the |
| 59 | | /// oldest byte is overwritten. |
| 60 | | pub fn writeAssumeCapacity(self: *RingBuffer, byte: u8) void { |
| 61 | | self.data[self.mask(self.write_index)] = byte; |
| 62 | | self.write_index = self.mask2(self.write_index + 1); |
| 63 | | } |
| 64 | | |
| 65 | | /// Write `bytes` into the ring buffer. Returns `error.Full` if the ring |
| 66 | | /// buffer does not have enough space, without writing any data. |
| 67 | | /// Uses memcpy and so `bytes` must not overlap ring buffer data. |
| 68 | | pub fn writeSlice(self: *RingBuffer, bytes: []const u8) Error!void { |
| 69 | | if (self.len() + bytes.len > self.data.len) return error.Full; |
| 70 | | self.writeSliceAssumeCapacity(bytes); |
| 71 | | } |
| 72 | | |
| 73 | | /// Write `bytes` into the ring buffer. If there is not enough space, older |
| 74 | | /// bytes will be overwritten. |
| 75 | | /// Uses memcpy and so `bytes` must not overlap ring buffer data. |
| 76 | | pub fn writeSliceAssumeCapacity(self: *RingBuffer, bytes: []const u8) void { |
| 77 | | assert(bytes.len <= self.data.len); |
| 78 | | const data_start = self.mask(self.write_index); |
| 79 | | const part1_data_end = @min(data_start + bytes.len, self.data.len); |
| 80 | | const part1_len = part1_data_end - data_start; |
| 81 | | @memcpy(self.data[data_start..part1_data_end], bytes[0..part1_len]); |
| 82 | | |
| 83 | | const remaining = bytes.len - part1_len; |
| 84 | | const to_write = @min(remaining, remaining % self.data.len + self.data.len); |
| 85 | | const part2_bytes_start = bytes.len - to_write; |
| 86 | | const part2_bytes_end = @min(part2_bytes_start + self.data.len, bytes.len); |
| 87 | | const part2_len = part2_bytes_end - part2_bytes_start; |
| 88 | | @memcpy(self.data[0..part2_len], bytes[part2_bytes_start..part2_bytes_end]); |
| 89 | | if (part2_bytes_end != bytes.len) { |
| 90 | | const part3_len = bytes.len - part2_bytes_end; |
| 91 | | @memcpy(self.data[0..part3_len], bytes[part2_bytes_end..bytes.len]); |
| 92 | | } |
| 93 | | self.write_index = self.mask2(self.write_index + bytes.len); |
| 94 | | } |
| 95 | | |
| 96 | | /// Write `bytes` into the ring buffer. Returns `error.Full` if the ring |
| 97 | | /// buffer does not have enough space, without writing any data. |
| 98 | | /// Uses copyForwards and can write slices from this RingBuffer into itself. |
| 99 | | pub fn writeSliceForwards(self: *RingBuffer, bytes: []const u8) Error!void { |
| 100 | | if (self.len() + bytes.len > self.data.len) return error.Full; |
| 101 | | self.writeSliceForwardsAssumeCapacity(bytes); |
| 102 | | } |
| 103 | | |
| 104 | | /// Write `bytes` into the ring buffer. If there is not enough space, older |
| 105 | | /// bytes will be overwritten. |
| 106 | | /// Uses copyForwards and can write slices from this RingBuffer into itself. |
| 107 | | pub fn writeSliceForwardsAssumeCapacity(self: *RingBuffer, bytes: []const u8) void { |
| 108 | | assert(bytes.len <= self.data.len); |
| 109 | | const data_start = self.mask(self.write_index); |
| 110 | | const part1_data_end = @min(data_start + bytes.len, self.data.len); |
| 111 | | const part1_len = part1_data_end - data_start; |
| 112 | | copyForwards(u8, self.data[data_start..], bytes[0..part1_len]); |
| 113 | | |
| 114 | | const remaining = bytes.len - part1_len; |
| 115 | | const to_write = @min(remaining, remaining % self.data.len + self.data.len); |
| 116 | | const part2_bytes_start = bytes.len - to_write; |
| 117 | | const part2_bytes_end = @min(part2_bytes_start + self.data.len, bytes.len); |
| 118 | | copyForwards(u8, self.data[0..], bytes[part2_bytes_start..part2_bytes_end]); |
| 119 | | if (part2_bytes_end != bytes.len) |
| 120 | | copyForwards(u8, self.data[0..], bytes[part2_bytes_end..bytes.len]); |
| 121 | | self.write_index = self.mask2(self.write_index + bytes.len); |
| 122 | | } |
| 123 | | |
| 124 | | /// Consume a byte from the ring buffer and return it. Returns `null` if the |
| 125 | | /// ring buffer is empty. |
| 126 | | pub fn read(self: *RingBuffer) ?u8 { |
| 127 | | if (self.isEmpty()) return null; |
| 128 | | return self.readAssumeLength(); |
| 129 | | } |
| 130 | | |
| 131 | | /// Consume a byte from the ring buffer and return it; asserts that the buffer |
| 132 | | /// is not empty. |
| 133 | | pub fn readAssumeLength(self: *RingBuffer) u8 { |
| 134 | | assert(!self.isEmpty()); |
| 135 | | const byte = self.data[self.mask(self.read_index)]; |
| 136 | | self.read_index = self.mask2(self.read_index + 1); |
| 137 | | return byte; |
| 138 | | } |
| 139 | | |
| 140 | | /// Reads first `length` bytes written to the ring buffer into `dest`; Returns |
| 141 | | /// Error.ReadLengthInvalid if length greater than ring or dest length |
| 142 | | /// Uses memcpy and so `dest` must not overlap ring buffer data. |
| 143 | | pub fn readFirst(self: *RingBuffer, dest: []u8, length: usize) Error!void { |
| 144 | | if (length > self.len() or length > dest.len) return error.ReadLengthInvalid; |
| 145 | | self.readFirstAssumeLength(dest, length); |
| 146 | | } |
| 147 | | |
| 148 | | /// Reads first `length` bytes written to the ring buffer into `dest`; |
| 149 | | /// Asserts that length not greater than ring buffer or dest length |
| 150 | | /// Uses memcpy and so `dest` must not overlap ring buffer data. |
| 151 | | pub fn readFirstAssumeLength(self: *RingBuffer, dest: []u8, length: usize) void { |
| 152 | | assert(length <= self.len() and length <= dest.len); |
| 153 | | const slice = self.sliceAt(self.read_index, length); |
| 154 | | slice.copyTo(dest); |
| 155 | | self.read_index = self.mask2(self.read_index + length); |
| 156 | | } |
| 157 | | |
| 158 | | /// Reads last `length` bytes written to the ring buffer into `dest`; Returns |
| 159 | | /// Error.ReadLengthInvalid if length greater than ring or dest length |
| 160 | | /// Uses memcpy and so `dest` must not overlap ring buffer data. |
| 161 | | /// Reduces write index by `length`. |
| 162 | | pub fn readLast(self: *RingBuffer, dest: []u8, length: usize) Error!void { |
| 163 | | if (length > self.len() or length > dest.len) return error.ReadLengthInvalid; |
| 164 | | self.readLastAssumeLength(dest, length); |
| 165 | | } |
| 166 | | |
| 167 | | /// Reads last `length` bytes written to the ring buffer into `dest`; |
| 168 | | /// Asserts that length not greater than ring buffer or dest length |
| 169 | | /// Uses memcpy and so `dest` must not overlap ring buffer data. |
| 170 | | /// Reduces write index by `length`. |
| 171 | | pub fn readLastAssumeLength(self: *RingBuffer, dest: []u8, length: usize) void { |
| 172 | | assert(length <= self.len() and length <= dest.len); |
| 173 | | const slice = self.sliceLast(length); |
| 174 | | slice.copyTo(dest); |
| 175 | | self.write_index = if (self.write_index >= self.data.len) |
| 176 | | self.write_index - length |
| 177 | | else |
| 178 | | self.mask(self.write_index + self.data.len - length); |
| 179 | | } |
| 180 | | |
| 181 | | /// Returns `true` if the ring buffer is empty and `false` otherwise. |
| 182 | | pub fn isEmpty(self: RingBuffer) bool { |
| 183 | | return self.write_index == self.read_index; |
| 184 | | } |
| 185 | | |
| 186 | | /// Returns `true` if the ring buffer is full and `false` otherwise. |
| 187 | | pub fn isFull(self: RingBuffer) bool { |
| 188 | | return self.mask2(self.write_index + self.data.len) == self.read_index; |
| 189 | | } |
| 190 | | |
| 191 | | /// Returns the length of data available for reading |
| 192 | | pub fn len(self: RingBuffer) usize { |
| 193 | | const wrap_offset = 2 * self.data.len * @intFromBool(self.write_index < self.read_index); |
| 194 | | const adjusted_write_index = self.write_index + wrap_offset; |
| 195 | | return adjusted_write_index - self.read_index; |
| 196 | | } |
| 197 | | |
| 198 | | /// A `Slice` represents a region of a ring buffer. The region is split into two |
| 199 | | /// sections as the ring buffer data will not be contiguous if the desired |
| 200 | | /// region wraps to the start of the backing slice. |
| 201 | | pub const Slice = struct { |
| 202 | | first: []u8, |
| 203 | | second: []u8, |
| 204 | | |
| 205 | | /// Copy data from `self` into `dest` |
| 206 | | pub fn copyTo(self: Slice, dest: []u8) void { |
| 207 | | @memcpy(dest[0..self.first.len], self.first); |
| 208 | | @memcpy(dest[self.first.len..][0..self.second.len], self.second); |
| 209 | | } |
| 210 | | }; |
| 211 | | |
| 212 | | /// Returns a `Slice` for the region of the ring buffer starting at |
| 213 | | /// `self.mask(start_unmasked)` with the specified length. |
| 214 | | pub fn sliceAt(self: RingBuffer, start_unmasked: usize, length: usize) Slice { |
| 215 | | assert(length <= self.data.len); |
| 216 | | const slice1_start = self.mask(start_unmasked); |
| 217 | | const slice1_end = @min(self.data.len, slice1_start + length); |
| 218 | | const slice1 = self.data[slice1_start..slice1_end]; |
| 219 | | const slice2 = self.data[0 .. length - slice1.len]; |
| 220 | | return Slice{ |
| 221 | | .first = slice1, |
| 222 | | .second = slice2, |
| 223 | | }; |
| 224 | | } |
| 225 | | |
| 226 | | /// Returns a `Slice` for the last `length` bytes written to the ring buffer. |
| 227 | | /// Does not check that any bytes have been written into the region. |
| 228 | | pub fn sliceLast(self: RingBuffer, length: usize) Slice { |
| 229 | | return self.sliceAt(self.write_index + self.data.len - length, length); |
| 230 | | } |