| ... | @@ -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 | } |