| ... | ... | @@ -1,548 +0,0 @@ |
| 1 | | // FIFO of fixed size items |
| 2 | | // Usually used for e.g. byte buffers |
| 3 | | |
| 4 | | const std = @import("std"); |
| 5 | | const math = std.math; |
| 6 | | const mem = std.mem; |
| 7 | | const Allocator = mem.Allocator; |
| 8 | | const assert = std.debug.assert; |
| 9 | | const testing = std.testing; |
| 10 | | |
| 11 | | pub const LinearFifoBufferType = union(enum) { |
| 12 | | /// The buffer is internal to the fifo; it is of the specified size. |
| 13 | | Static: usize, |
| 14 | | |
| 15 | | /// The buffer is passed as a slice to the initialiser. |
| 16 | | Slice, |
| 17 | | |
| 18 | | /// The buffer is managed dynamically using a `mem.Allocator`. |
| 19 | | Dynamic, |
| 20 | | }; |
| 21 | | |
| 22 | | pub fn LinearFifo( |
| 23 | | comptime T: type, |
| 24 | | comptime buffer_type: LinearFifoBufferType, |
| 25 | | ) type { |
| 26 | | const autoalign = false; |
| 27 | | |
| 28 | | const powers_of_two = switch (buffer_type) { |
| 29 | | .Static => std.math.isPowerOfTwo(buffer_type.Static), |
| 30 | | .Slice => false, // Any size slice could be passed in |
| 31 | | .Dynamic => true, // This could be configurable in future |
| 32 | | }; |
| 33 | | |
| 34 | | return struct { |
| 35 | | allocator: if (buffer_type == .Dynamic) Allocator else void, |
| 36 | | buf: if (buffer_type == .Static) [buffer_type.Static]T else []T, |
| 37 | | head: usize, |
| 38 | | count: usize, |
| 39 | | |
| 40 | | const Self = @This(); |
| 41 | | pub const Reader = std.io.GenericReader(*Self, error{}, readFn); |
| 42 | | pub const Writer = std.io.GenericWriter(*Self, error{OutOfMemory}, appendWrite); |
| 43 | | |
| 44 | | // Type of Self argument for slice operations. |
| 45 | | // If buffer is inline (Static) then we need to ensure we haven't |
| 46 | | // returned a slice into a copy on the stack |
| 47 | | const SliceSelfArg = if (buffer_type == .Static) *Self else Self; |
| 48 | | |
| 49 | | pub const init = switch (buffer_type) { |
| 50 | | .Static => initStatic, |
| 51 | | .Slice => initSlice, |
| 52 | | .Dynamic => initDynamic, |
| 53 | | }; |
| 54 | | |
| 55 | | fn initStatic() Self { |
| 56 | | comptime assert(buffer_type == .Static); |
| 57 | | return .{ |
| 58 | | .allocator = {}, |
| 59 | | .buf = undefined, |
| 60 | | .head = 0, |
| 61 | | .count = 0, |
| 62 | | }; |
| 63 | | } |
| 64 | | |
| 65 | | fn initSlice(buf: []T) Self { |
| 66 | | comptime assert(buffer_type == .Slice); |
| 67 | | return .{ |
| 68 | | .allocator = {}, |
| 69 | | .buf = buf, |
| 70 | | .head = 0, |
| 71 | | .count = 0, |
| 72 | | }; |
| 73 | | } |
| 74 | | |
| 75 | | fn initDynamic(allocator: Allocator) Self { |
| 76 | | comptime assert(buffer_type == .Dynamic); |
| 77 | | return .{ |
| 78 | | .allocator = allocator, |
| 79 | | .buf = &.{}, |
| 80 | | .head = 0, |
| 81 | | .count = 0, |
| 82 | | }; |
| 83 | | } |
| 84 | | |
| 85 | | pub fn deinit(self: Self) void { |
| 86 | | if (buffer_type == .Dynamic) self.allocator.free(self.buf); |
| 87 | | } |
| 88 | | |
| 89 | | pub fn realign(self: *Self) void { |
| 90 | | if (self.buf.len - self.head >= self.count) { |
| 91 | | mem.copyForwards(T, self.buf[0..self.count], self.buf[self.head..][0..self.count]); |
| 92 | | self.head = 0; |
| 93 | | } else { |
| 94 | | var tmp: [4096 / 2 / @sizeOf(T)]T = undefined; |
| 95 | | |
| 96 | | while (self.head != 0) { |
| 97 | | const n = @min(self.head, tmp.len); |
| 98 | | const m = self.buf.len - n; |
| 99 | | @memcpy(tmp[0..n], self.buf[0..n]); |
| 100 | | mem.copyForwards(T, self.buf[0..m], self.buf[n..][0..m]); |
| 101 | | @memcpy(self.buf[m..][0..n], tmp[0..n]); |
| 102 | | self.head -= n; |
| 103 | | } |
| 104 | | } |
| 105 | | { // set unused area to undefined |
| 106 | | const unused = mem.sliceAsBytes(self.buf[self.count..]); |
| 107 | | @memset(unused, undefined); |
| 108 | | } |
| 109 | | } |
| 110 | | |
| 111 | | /// Reduce allocated capacity to `size`. |
| 112 | | pub fn shrink(self: *Self, size: usize) void { |
| 113 | | assert(size >= self.count); |
| 114 | | if (buffer_type == .Dynamic) { |
| 115 | | self.realign(); |
| 116 | | self.buf = self.allocator.realloc(self.buf, size) catch |e| switch (e) { |
| 117 | | error.OutOfMemory => return, // no problem, capacity is still correct then. |
| 118 | | }; |
| 119 | | } |
| 120 | | } |
| 121 | | |
| 122 | | /// Ensure that the buffer can fit at least `size` items |
| 123 | | pub fn ensureTotalCapacity(self: *Self, size: usize) !void { |
| 124 | | if (self.buf.len >= size) return; |
| 125 | | if (buffer_type == .Dynamic) { |
| 126 | | self.realign(); |
| 127 | | const new_size = if (powers_of_two) math.ceilPowerOfTwo(usize, size) catch return error.OutOfMemory else size; |
| 128 | | self.buf = try self.allocator.realloc(self.buf, new_size); |
| 129 | | } else { |
| 130 | | return error.OutOfMemory; |
| 131 | | } |
| 132 | | } |
| 133 | | |
| 134 | | /// Makes sure at least `size` items are unused |
| 135 | | pub fn ensureUnusedCapacity(self: *Self, size: usize) error{OutOfMemory}!void { |
| 136 | | if (self.writableLength() >= size) return; |
| 137 | | |
| 138 | | return try self.ensureTotalCapacity(math.add(usize, self.count, size) catch return error.OutOfMemory); |
| 139 | | } |
| 140 | | |
| 141 | | /// Returns number of items currently in fifo |
| 142 | | pub fn readableLength(self: Self) usize { |
| 143 | | return self.count; |
| 144 | | } |
| 145 | | |
| 146 | | /// Returns a writable slice from the 'read' end of the fifo |
| 147 | | fn readableSliceMut(self: SliceSelfArg, offset: usize) []T { |
| 148 | | if (offset > self.count) return &[_]T{}; |
| 149 | | |
| 150 | | var start = self.head + offset; |
| 151 | | if (start >= self.buf.len) { |
| 152 | | start -= self.buf.len; |
| 153 | | return self.buf[start .. start + (self.count - offset)]; |
| 154 | | } else { |
| 155 | | const end = @min(self.head + self.count, self.buf.len); |
| 156 | | return self.buf[start..end]; |
| 157 | | } |
| 158 | | } |
| 159 | | |
| 160 | | /// Returns a readable slice from `offset` |
| 161 | | pub fn readableSlice(self: SliceSelfArg, offset: usize) []const T { |
| 162 | | return self.readableSliceMut(offset); |
| 163 | | } |
| 164 | | |
| 165 | | pub fn readableSliceOfLen(self: *Self, len: usize) []const T { |
| 166 | | assert(len <= self.count); |
| 167 | | const buf = self.readableSlice(0); |
| 168 | | if (buf.len >= len) { |
| 169 | | return buf[0..len]; |
| 170 | | } else { |
| 171 | | self.realign(); |
| 172 | | return self.readableSlice(0)[0..len]; |
| 173 | | } |
| 174 | | } |
| 175 | | |
| 176 | | /// Discard first `count` items in the fifo |
| 177 | | pub fn discard(self: *Self, count: usize) void { |
| 178 | | assert(count <= self.count); |
| 179 | | { // set old range to undefined. Note: may be wrapped around |
| 180 | | const slice = self.readableSliceMut(0); |
| 181 | | if (slice.len >= count) { |
| 182 | | const unused = mem.sliceAsBytes(slice[0..count]); |
| 183 | | @memset(unused, undefined); |
| 184 | | } else { |
| 185 | | const unused = mem.sliceAsBytes(slice[0..]); |
| 186 | | @memset(unused, undefined); |
| 187 | | const unused2 = mem.sliceAsBytes(self.readableSliceMut(slice.len)[0 .. count - slice.len]); |
| 188 | | @memset(unused2, undefined); |
| 189 | | } |
| 190 | | } |
| 191 | | if (autoalign and self.count == count) { |
| 192 | | self.head = 0; |
| 193 | | self.count = 0; |
| 194 | | } else { |
| 195 | | var head = self.head + count; |
| 196 | | if (powers_of_two) { |
| 197 | | // Note it is safe to do a wrapping subtract as |
| 198 | | // bitwise & with all 1s is a noop |
| 199 | | head &= self.buf.len -% 1; |
| 200 | | } else { |
| 201 | | head %= self.buf.len; |
| 202 | | } |
| 203 | | self.head = head; |
| 204 | | self.count -= count; |
| 205 | | } |
| 206 | | } |
| 207 | | |
| 208 | | /// Read the next item from the fifo |
| 209 | | pub fn readItem(self: *Self) ?T { |
| 210 | | if (self.count == 0) return null; |
| 211 | | |
| 212 | | const c = self.buf[self.head]; |
| 213 | | self.discard(1); |
| 214 | | return c; |
| 215 | | } |
| 216 | | |
| 217 | | /// Read data from the fifo into `dst`, returns number of items copied. |
| 218 | | pub fn read(self: *Self, dst: []T) usize { |
| 219 | | var dst_left = dst; |
| 220 | | |
| 221 | | while (dst_left.len > 0) { |
| 222 | | const slice = self.readableSlice(0); |
| 223 | | if (slice.len == 0) break; |
| 224 | | const n = @min(slice.len, dst_left.len); |
| 225 | | @memcpy(dst_left[0..n], slice[0..n]); |
| 226 | | self.discard(n); |
| 227 | | dst_left = dst_left[n..]; |
| 228 | | } |
| 229 | | |
| 230 | | return dst.len - dst_left.len; |
| 231 | | } |
| 232 | | |
| 233 | | /// Same as `read` except it returns an error union |
| 234 | | /// The purpose of this function existing is to match `std.io.GenericReader` API. |
| 235 | | fn readFn(self: *Self, dest: []u8) error{}!usize { |
| 236 | | return self.read(dest); |
| 237 | | } |
| 238 | | |
| 239 | | pub fn reader(self: *Self) Reader { |
| 240 | | return .{ .context = self }; |
| 241 | | } |
| 242 | | |
| 243 | | /// Returns number of items available in fifo |
| 244 | | pub fn writableLength(self: Self) usize { |
| 245 | | return self.buf.len - self.count; |
| 246 | | } |
| 247 | | |
| 248 | | /// Returns the first section of writable buffer. |
| 249 | | /// Note that this may be of length 0 |
| 250 | | pub fn writableSlice(self: SliceSelfArg, offset: usize) []T { |
| 251 | | if (offset > self.buf.len) return &[_]T{}; |
| 252 | | |
| 253 | | const tail = self.head + offset + self.count; |
| 254 | | if (tail < self.buf.len) { |
| 255 | | return self.buf[tail..]; |
| 256 | | } else { |
| 257 | | return self.buf[tail - self.buf.len ..][0 .. self.writableLength() - offset]; |
| 258 | | } |
| 259 | | } |
| 260 | | |
| 261 | | /// Returns a writable buffer of at least `size` items, allocating memory as needed. |
| 262 | | /// Use `fifo.update` once you've written data to it. |
| 263 | | pub fn writableWithSize(self: *Self, size: usize) ![]T { |
| 264 | | try self.ensureUnusedCapacity(size); |
| 265 | | |
| 266 | | // try to avoid realigning buffer |
| 267 | | var slice = self.writableSlice(0); |
| 268 | | if (slice.len < size) { |
| 269 | | self.realign(); |
| 270 | | slice = self.writableSlice(0); |
| 271 | | } |
| 272 | | return slice; |
| 273 | | } |
| 274 | | |
| 275 | | /// Update the tail location of the buffer (usually follows use of writable/writableWithSize) |
| 276 | | pub fn update(self: *Self, count: usize) void { |
| 277 | | assert(self.count + count <= self.buf.len); |
| 278 | | self.count += count; |
| 279 | | } |
| 280 | | |
| 281 | | /// Appends the data in `src` to the fifo. |
| 282 | | /// You must have ensured there is enough space. |
| 283 | | pub fn writeAssumeCapacity(self: *Self, src: []const T) void { |
| 284 | | assert(self.writableLength() >= src.len); |
| 285 | | |
| 286 | | var src_left = src; |
| 287 | | while (src_left.len > 0) { |
| 288 | | const writable_slice = self.writableSlice(0); |
| 289 | | assert(writable_slice.len != 0); |
| 290 | | const n = @min(writable_slice.len, src_left.len); |
| 291 | | @memcpy(writable_slice[0..n], src_left[0..n]); |
| 292 | | self.update(n); |
| 293 | | src_left = src_left[n..]; |
| 294 | | } |
| 295 | | } |
| 296 | | |
| 297 | | /// Write a single item to the fifo |
| 298 | | pub fn writeItem(self: *Self, item: T) !void { |
| 299 | | try self.ensureUnusedCapacity(1); |
| 300 | | return self.writeItemAssumeCapacity(item); |
| 301 | | } |
| 302 | | |
| 303 | | pub fn writeItemAssumeCapacity(self: *Self, item: T) void { |
| 304 | | var tail = self.head + self.count; |
| 305 | | if (powers_of_two) { |
| 306 | | tail &= self.buf.len - 1; |
| 307 | | } else { |
| 308 | | tail %= self.buf.len; |
| 309 | | } |
| 310 | | self.buf[tail] = item; |
| 311 | | self.update(1); |
| 312 | | } |
| 313 | | |
| 314 | | /// Appends the data in `src` to the fifo. |
| 315 | | /// Allocates more memory as necessary |
| 316 | | pub fn write(self: *Self, src: []const T) !void { |
| 317 | | try self.ensureUnusedCapacity(src.len); |
| 318 | | |
| 319 | | return self.writeAssumeCapacity(src); |
| 320 | | } |
| 321 | | |
| 322 | | /// Same as `write` except it returns the number of bytes written, which is always the same |
| 323 | | /// as `bytes.len`. The purpose of this function existing is to match `std.io.GenericWriter` API. |
| 324 | | fn appendWrite(self: *Self, bytes: []const u8) error{OutOfMemory}!usize { |
| 325 | | try self.write(bytes); |
| 326 | | return bytes.len; |
| 327 | | } |
| 328 | | |
| 329 | | pub fn writer(self: *Self) Writer { |
| 330 | | return .{ .context = self }; |
| 331 | | } |
| 332 | | |
| 333 | | /// Make `count` items available before the current read location |
| 334 | | fn rewind(self: *Self, count: usize) void { |
| 335 | | assert(self.writableLength() >= count); |
| 336 | | |
| 337 | | var head = self.head + (self.buf.len - count); |
| 338 | | if (powers_of_two) { |
| 339 | | head &= self.buf.len - 1; |
| 340 | | } else { |
| 341 | | head %= self.buf.len; |
| 342 | | } |
| 343 | | self.head = head; |
| 344 | | self.count += count; |
| 345 | | } |
| 346 | | |
| 347 | | /// Place data back into the read stream |
| 348 | | pub fn unget(self: *Self, src: []const T) !void { |
| 349 | | try self.ensureUnusedCapacity(src.len); |
| 350 | | |
| 351 | | self.rewind(src.len); |
| 352 | | |
| 353 | | const slice = self.readableSliceMut(0); |
| 354 | | if (src.len < slice.len) { |
| 355 | | @memcpy(slice[0..src.len], src); |
| 356 | | } else { |
| 357 | | @memcpy(slice, src[0..slice.len]); |
| 358 | | const slice2 = self.readableSliceMut(slice.len); |
| 359 | | @memcpy(slice2[0 .. src.len - slice.len], src[slice.len..]); |
| 360 | | } |
| 361 | | } |
| 362 | | |
| 363 | | /// Returns the item at `offset`. |
| 364 | | /// Asserts offset is within bounds. |
| 365 | | pub fn peekItem(self: Self, offset: usize) T { |
| 366 | | assert(offset < self.count); |
| 367 | | |
| 368 | | var index = self.head + offset; |
| 369 | | if (powers_of_two) { |
| 370 | | index &= self.buf.len - 1; |
| 371 | | } else { |
| 372 | | index %= self.buf.len; |
| 373 | | } |
| 374 | | return self.buf[index]; |
| 375 | | } |
| 376 | | |
| 377 | | /// Pump data from a reader into a writer. |
| 378 | | /// Stops when reader returns 0 bytes (EOF). |
| 379 | | /// Buffer size must be set before calling; a buffer length of 0 is invalid. |
| 380 | | pub fn pump(self: *Self, src_reader: anytype, dest_writer: anytype) !void { |
| 381 | | assert(self.buf.len > 0); |
| 382 | | while (true) { |
| 383 | | if (self.writableLength() > 0) { |
| 384 | | const n = try src_reader.read(self.writableSlice(0)); |
| 385 | | if (n == 0) break; // EOF |
| 386 | | self.update(n); |
| 387 | | } |
| 388 | | self.discard(try dest_writer.write(self.readableSlice(0))); |
| 389 | | } |
| 390 | | // flush remaining data |
| 391 | | while (self.readableLength() > 0) { |
| 392 | | self.discard(try dest_writer.write(self.readableSlice(0))); |
| 393 | | } |
| 394 | | } |
| 395 | | |
| 396 | | pub fn toOwnedSlice(self: *Self) Allocator.Error![]T { |
| 397 | | if (self.head != 0) self.realign(); |
| 398 | | assert(self.head == 0); |
| 399 | | assert(self.count <= self.buf.len); |
| 400 | | const allocator = self.allocator; |
| 401 | | if (allocator.resize(self.buf, self.count)) { |
| 402 | | const result = self.buf[0..self.count]; |
| 403 | | self.* = Self.init(allocator); |
| 404 | | return result; |
| 405 | | } |
| 406 | | const new_memory = try allocator.dupe(T, self.buf[0..self.count]); |
| 407 | | allocator.free(self.buf); |
| 408 | | self.* = Self.init(allocator); |
| 409 | | return new_memory; |
| 410 | | } |
| 411 | | }; |
| 412 | | } |
| 413 | | |
| 414 | | test "LinearFifo(u8, .Dynamic) discard(0) from empty buffer should not error on overflow" { |
| 415 | | var fifo = LinearFifo(u8, .Dynamic).init(testing.allocator); |
| 416 | | defer fifo.deinit(); |
| 417 | | |
| 418 | | // If overflow is not explicitly allowed this will crash in debug / safe mode |
| 419 | | fifo.discard(0); |
| 420 | | } |
| 421 | | |
| 422 | | test "LinearFifo(u8, .Dynamic)" { |
| 423 | | var fifo = LinearFifo(u8, .Dynamic).init(testing.allocator); |
| 424 | | defer fifo.deinit(); |
| 425 | | |
| 426 | | try fifo.write("HELLO"); |
| 427 | | try testing.expectEqual(@as(usize, 5), fifo.readableLength()); |
| 428 | | try testing.expectEqualSlices(u8, "HELLO", fifo.readableSlice(0)); |
| 429 | | |
| 430 | | { |
| 431 | | var i: usize = 0; |
| 432 | | while (i < 5) : (i += 1) { |
| 433 | | try fifo.write(&[_]u8{fifo.peekItem(i)}); |
| 434 | | } |
| 435 | | try testing.expectEqual(@as(usize, 10), fifo.readableLength()); |
| 436 | | try testing.expectEqualSlices(u8, "HELLOHELLO", fifo.readableSlice(0)); |
| 437 | | } |
| 438 | | |
| 439 | | { |
| 440 | | try testing.expectEqual(@as(u8, 'H'), fifo.readItem().?); |
| 441 | | try testing.expectEqual(@as(u8, 'E'), fifo.readItem().?); |
| 442 | | try testing.expectEqual(@as(u8, 'L'), fifo.readItem().?); |
| 443 | | try testing.expectEqual(@as(u8, 'L'), fifo.readItem().?); |
| 444 | | try testing.expectEqual(@as(u8, 'O'), fifo.readItem().?); |
| 445 | | } |
| 446 | | try testing.expectEqual(@as(usize, 5), fifo.readableLength()); |
| 447 | | |
| 448 | | { // Writes that wrap around |
| 449 | | try testing.expectEqual(@as(usize, 11), fifo.writableLength()); |
| 450 | | try testing.expectEqual(@as(usize, 6), fifo.writableSlice(0).len); |
| 451 | | fifo.writeAssumeCapacity("6<chars<11"); |
| 452 | | try testing.expectEqualSlices(u8, "HELLO6<char", fifo.readableSlice(0)); |
| 453 | | try testing.expectEqualSlices(u8, "s<11", fifo.readableSlice(11)); |
| 454 | | try testing.expectEqualSlices(u8, "11", fifo.readableSlice(13)); |
| 455 | | try testing.expectEqualSlices(u8, "", fifo.readableSlice(15)); |
| 456 | | fifo.discard(11); |
| 457 | | try testing.expectEqualSlices(u8, "s<11", fifo.readableSlice(0)); |
| 458 | | fifo.discard(4); |
| 459 | | try testing.expectEqual(@as(usize, 0), fifo.readableLength()); |
| 460 | | } |
| 461 | | |
| 462 | | { |
| 463 | | const buf = try fifo.writableWithSize(12); |
| 464 | | try testing.expectEqual(@as(usize, 12), buf.len); |
| 465 | | var i: u8 = 0; |
| 466 | | while (i < 10) : (i += 1) { |
| 467 | | buf[i] = i + 'a'; |
| 468 | | } |
| 469 | | fifo.update(10); |
| 470 | | try testing.expectEqualSlices(u8, "abcdefghij", fifo.readableSlice(0)); |
| 471 | | } |
| 472 | | |
| 473 | | { |
| 474 | | try fifo.unget("prependedstring"); |
| 475 | | var result: [30]u8 = undefined; |
| 476 | | try testing.expectEqualSlices(u8, "prependedstringabcdefghij", result[0..fifo.read(&result)]); |
| 477 | | try fifo.unget("b"); |
| 478 | | try fifo.unget("a"); |
| 479 | | try testing.expectEqualSlices(u8, "ab", result[0..fifo.read(&result)]); |
| 480 | | } |
| 481 | | |
| 482 | | fifo.shrink(0); |
| 483 | | |
| 484 | | { |
| 485 | | try fifo.writer().print("{s}, {s}!", .{ "Hello", "World" }); |
| 486 | | var result: [30]u8 = undefined; |
| 487 | | try testing.expectEqualSlices(u8, "Hello, World!", result[0..fifo.read(&result)]); |
| 488 | | try testing.expectEqual(@as(usize, 0), fifo.readableLength()); |
| 489 | | } |
| 490 | | |
| 491 | | { |
| 492 | | try fifo.writer().writeAll("This is a test"); |
| 493 | | var result: [30]u8 = undefined; |
| 494 | | try testing.expectEqualSlices(u8, "This", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?); |
| 495 | | try testing.expectEqualSlices(u8, "is", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?); |
| 496 | | try testing.expectEqualSlices(u8, "a", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?); |
| 497 | | try testing.expectEqualSlices(u8, "test", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?); |
| 498 | | } |
| 499 | | |
| 500 | | { |
| 501 | | try fifo.ensureTotalCapacity(1); |
| 502 | | var in_fbs = std.io.fixedBufferStream("pump test"); |
| 503 | | var out_buf: [50]u8 = undefined; |
| 504 | | var out_fbs = std.io.fixedBufferStream(&out_buf); |
| 505 | | try fifo.pump(in_fbs.reader(), out_fbs.writer()); |
| 506 | | try testing.expectEqualSlices(u8, in_fbs.buffer, out_fbs.getWritten()); |
| 507 | | } |
| 508 | | } |
| 509 | | |
| 510 | | test LinearFifo { |
| 511 | | inline for ([_]type{ u1, u8, u16, u64 }) |T| { |
| 512 | | inline for ([_]LinearFifoBufferType{ LinearFifoBufferType{ .Static = 32 }, .Slice, .Dynamic }) |bt| { |
| 513 | | const FifoType = LinearFifo(T, bt); |
| 514 | | var buf: if (bt == .Slice) [32]T else void = undefined; |
| 515 | | var fifo = switch (bt) { |
| 516 | | .Static => FifoType.init(), |
| 517 | | .Slice => FifoType.init(buf[0..]), |
| 518 | | .Dynamic => FifoType.init(testing.allocator), |
| 519 | | }; |
| 520 | | defer fifo.deinit(); |
| 521 | | |
| 522 | | try fifo.write(&[_]T{ 0, 1, 1, 0, 1 }); |
| 523 | | try testing.expectEqual(@as(usize, 5), fifo.readableLength()); |
| 524 | | |
| 525 | | { |
| 526 | | try testing.expectEqual(@as(T, 0), fifo.readItem().?); |
| 527 | | try testing.expectEqual(@as(T, 1), fifo.readItem().?); |
| 528 | | try testing.expectEqual(@as(T, 1), fifo.readItem().?); |
| 529 | | try testing.expectEqual(@as(T, 0), fifo.readItem().?); |
| 530 | | try testing.expectEqual(@as(T, 1), fifo.readItem().?); |
| 531 | | try testing.expectEqual(@as(usize, 0), fifo.readableLength()); |
| 532 | | } |
| 533 | | |
| 534 | | { |
| 535 | | try fifo.writeItem(1); |
| 536 | | try fifo.writeItem(1); |
| 537 | | try fifo.writeItem(1); |
| 538 | | try testing.expectEqual(@as(usize, 3), fifo.readableLength()); |
| 539 | | } |
| 540 | | |
| 541 | | { |
| 542 | | var readBuf: [3]T = undefined; |
| 543 | | const n = fifo.read(&readBuf); |
| 544 | | try testing.expectEqual(@as(usize, 3), n); // NOTE: It should be the number of items. |
| 545 | | } |
| 546 | | } |
| 547 | | } |
| 548 | | } |