| ... | @@ -9,30 +9,77 @@ const debug = std.debug; | ... | @@ -9,30 +9,77 @@ const debug = std.debug; |
| 9 | const assert = debug.assert; | 9 | const assert = debug.assert; |
| 10 | const testing = std.testing; | 10 | const testing = std.testing; |
| 11 | | 11 | |
| 12 | pub fn FixedSizeFifo(comptime T: type) type { | 12 | pub const FifoBufferType = union(enum) { |
| | 13 | /// The buffer is internal to the fifo; it is of the specified size. |
| | 14 | Static: usize, |
| | 15 | |
| | 16 | /// The buffer is passed as a slice to the initialiser. |
| | 17 | Slice, |
| | 18 | |
| | 19 | /// The buffer is managed dynamically using a `mem.Allocator`. |
| | 20 | Dynamic, |
| | 21 | }; |
| | 22 | |
| | 23 | pub fn FixedSizeFifo( |
| | 24 | comptime T: type, |
| | 25 | comptime buffer_type: FifoBufferType, |
| | 26 | ) type { |
| 13 | const autoalign = false; | 27 | const autoalign = false; |
| 14 | | 28 | |
| 15 | const powers_of_two = true; | 29 | const powers_of_two = switch (buffer_type) { |
| | 30 | .Static => std.math.isPowerOfTwo(buffer_type.Static), |
| | 31 | .Slice => false, // Any size slice could be passed in |
| | 32 | .Dynamic => true, // This could be configurable in future |
| | 33 | }; |
| 16 | | 34 | |
| 17 | return struct { | 35 | return struct { |
| 18 | allocator: *Allocator, | 36 | allocator: if (buffer_type == .Dynamic) *Allocator else void, |
| 19 | buf: []T, | 37 | buf: if (buffer_type == .Static) [buffer_type.Static]T else []T, |
| 20 | head: usize, | 38 | head: usize, |
| 21 | count: usize, | 39 | count: usize, |
| 22 | | 40 | |
| 23 | const Self = @This(); | 41 | const Self = @This(); |
| 24 | | 42 | |
| 25 | pub fn init(allocator: *Allocator) Self { | 43 | // Type of Self argument for slice operations. |
| 26 | return Self{ | 44 | // If buffer is inline (Static) then we need to ensure we haven't |
| 27 | .allocator = allocator, | 45 | // returned a slice into a copy on the stack |
| 28 | .buf = [_]T{}, | 46 | const SliceSelfArg = if (buffer_type == .Static) *Self else Self; |
| 29 | .head = 0, | 47 | |
| 30 | .count = 0, | 48 | pub usingnamespace switch (buffer_type) { |
| 31 | }; | 49 | .Static => struct { |
| 32 | } | 50 | pub fn init() Self { |
| | 51 | return .{ |
| | 52 | .allocator = {}, |
| | 53 | .buf = undefined, |
| | 54 | .head = 0, |
| | 55 | .count = 0, |
| | 56 | }; |
| | 57 | } |
| | 58 | }, |
| | 59 | .Slice => struct { |
| | 60 | pub fn init(buf: []T) Self { |
| | 61 | return .{ |
| | 62 | .allocator = {}, |
| | 63 | .buf = buf, |
| | 64 | .head = 0, |
| | 65 | .count = 0, |
| | 66 | }; |
| | 67 | } |
| | 68 | }, |
| | 69 | .Dynamic => struct { |
| | 70 | pub fn init(allocator: *Allocator) Self { |
| | 71 | return .{ |
| | 72 | .allocator = allocator, |
| | 73 | .buf = [_]T{}, |
| | 74 | .head = 0, |
| | 75 | .count = 0, |
| | 76 | }; |
| | 77 | } |
| | 78 | }, |
| | 79 | }; |
| 33 | | 80 | |
| 34 | pub fn deinit(self: *Self) void { | 81 | pub fn deinit(self: *Self) void { |
| 35 | self.allocator.free(self.buf); | 82 | if (buffer_type == .Dynamic) self.allocator.free(self.buf); |
| 36 | self.* = undefined; | 83 | self.* = undefined; |
| 37 | } | 84 | } |
| 38 | | 85 | |
| ... | @@ -63,18 +110,24 @@ pub fn FixedSizeFifo(comptime T: type) type { | ... | @@ -63,18 +110,24 @@ pub fn FixedSizeFifo(comptime T: type) type { |
| 63 | /// Reduce allocated capacity to `size`. | 110 | /// Reduce allocated capacity to `size`. |
| 64 | pub fn shrink(self: *Self, size: usize) void { | 111 | pub fn shrink(self: *Self, size: usize) void { |
| 65 | assert(size >= self.count); | 112 | assert(size >= self.count); |
| 66 | self.realign(); | 113 | if (buffer_type == .Dynamic) { |
| 67 | self.buf = self.allocator.realloc(self.buf, size) catch |e| switch (e) { | 114 | self.realign(); |
| 68 | error.OutOfMemory => return, // no problem, capacity is still correct then. | 115 | self.buf = self.allocator.realloc(self.buf, size) catch |e| switch (e) { |
| 69 | }; | 116 | error.OutOfMemory => return, // no problem, capacity is still correct then. |
| | 117 | }; |
| | 118 | } |
| 70 | } | 119 | } |
| 71 | | 120 | |
| 72 | /// Ensure that the buffer can fit at least `size` items | 121 | /// Ensure that the buffer can fit at least `size` items |
| 73 | pub fn ensureCapacity(self: *Self, size: usize) !void { | 122 | pub fn ensureCapacity(self: *Self, size: usize) !void { |
| 74 | if (self.buf.len >= size) return; | 123 | if (self.buf.len >= size) return; |
| 75 | self.realign(); | 124 | if (buffer_type == .Dynamic) { |
| 76 | const new_size = if (powers_of_two) math.ceilPowerOfTwo(usize, size) catch return error.OutOfMemory else size; | 125 | self.realign(); |
| 77 | self.buf = try self.allocator.realloc(self.buf, new_size); | 126 | const new_size = if (powers_of_two) math.ceilPowerOfTwo(usize, size) catch return error.OutOfMemory else size; |
| | 127 | self.buf = try self.allocator.realloc(self.buf, new_size); |
| | 128 | } else { |
| | 129 | return error.OutOfMemory; |
| | 130 | } |
| 78 | } | 131 | } |
| 79 | | 132 | |
| 80 | /// Makes sure at least `size` items are unused | 133 | /// Makes sure at least `size` items are unused |
| ... | @@ -90,7 +143,7 @@ pub fn FixedSizeFifo(comptime T: type) type { | ... | @@ -90,7 +143,7 @@ pub fn FixedSizeFifo(comptime T: type) type { |
| 90 | } | 143 | } |
| 91 | | 144 | |
| 92 | /// Returns a writable slice from the 'read' end of the fifo | 145 | /// Returns a writable slice from the 'read' end of the fifo |
| 93 | fn readableSliceMut(self: Self, offset: usize) []T { | 146 | fn readableSliceMut(self: SliceSelfArg, offset: usize) []T { |
| 94 | if (offset > self.count) return [_]T{}; | 147 | if (offset > self.count) return [_]T{}; |
| 95 | | 148 | |
| 96 | const start = self.head + offset; | 149 | const start = self.head + offset; |
| ... | @@ -107,7 +160,7 @@ pub fn FixedSizeFifo(comptime T: type) type { | ... | @@ -107,7 +160,7 @@ pub fn FixedSizeFifo(comptime T: type) type { |
| 107 | } | 160 | } |
| 108 | | 161 | |
| 109 | /// Returns a readable slice from `offset` | 162 | /// Returns a readable slice from `offset` |
| 110 | pub fn readableSlice(self: Self, offset: usize) []const T { | 163 | pub fn readableSlice(self: SliceSelfArg, offset: usize) []const T { |
| 111 | return self.readableSliceMut(offset); | 164 | return self.readableSliceMut(offset); |
| 112 | } | 165 | } |
| 113 | | 166 | |
| ... | @@ -173,7 +226,7 @@ pub fn FixedSizeFifo(comptime T: type) type { | ... | @@ -173,7 +226,7 @@ pub fn FixedSizeFifo(comptime T: type) type { |
| 173 | | 226 | |
| 174 | /// Returns the first section of writable buffer | 227 | /// Returns the first section of writable buffer |
| 175 | /// Note that this may be of length 0 | 228 | /// Note that this may be of length 0 |
| 176 | pub fn writableSlice(self: Self, offset: usize) []T { | 229 | pub fn writableSlice(self: SliceSelfArg, offset: usize) []T { |
| 177 | if (offset > self.buf.len) return [_]T{}; | 230 | if (offset > self.buf.len) return [_]T{}; |
| 178 | | 231 | |
| 179 | const tail = self.head + offset + self.count; | 232 | const tail = self.head + offset + self.count; |
| ... | @@ -279,10 +332,8 @@ pub fn FixedSizeFifo(comptime T: type) type { | ... | @@ -279,10 +332,8 @@ pub fn FixedSizeFifo(comptime T: type) type { |
| 279 | }; | 332 | }; |
| 280 | } | 333 | } |
| 281 | | 334 | |
| 282 | const ByteFifo = FixedSizeFifo(u8); | 335 | test "FixedSizeFifo(u8, .Dynamic)" { |
| 283 | | 336 | var fifo = FixedSizeFifo(u8, .Dynamic).init(debug.global_allocator); |
| 284 | test "ByteFifo" { | | |
| 285 | var fifo = ByteFifo.init(debug.global_allocator); | | |
| 286 | defer fifo.deinit(); | 337 | defer fifo.deinit(); |
| 287 | | 338 | |
| 288 | try fifo.write("HELLO"); | 339 | try fifo.write("HELLO"); |
| ... | @@ -348,18 +399,26 @@ test "ByteFifo" { | ... | @@ -348,18 +399,26 @@ test "ByteFifo" { |
| 348 | | 399 | |
| 349 | test "FixedSizeFifo" { | 400 | test "FixedSizeFifo" { |
| 350 | inline for ([_]type{ u1, u8, u16, u64 }) |T| { | 401 | inline for ([_]type{ u1, u8, u16, u64 }) |T| { |
| 351 | var fifo = FixedSizeFifo(T).init(debug.global_allocator); | 402 | inline for ([_]FifoBufferType{ FifoBufferType{ .Static = 32 }, .Slice, .Dynamic }) |bt| { |
| 352 | defer fifo.deinit(); | 403 | const FifoType = FixedSizeFifo(T, bt); |
| 353 | | 404 | var buf: if (bt == .Slice) [32]T else void = undefined; |
| 354 | try fifo.write([_]T{ 0, 1, 1, 0, 1 }); | 405 | var fifo = switch (bt) { |
| 355 | testing.expectEqual(@as(usize, 5), fifo.readableLength()); | 406 | .Static => FifoType.init(), |
| 356 | | 407 | .Slice => FifoType.init(buf[0..]), |
| 357 | { | 408 | .Dynamic => FifoType.init(debug.global_allocator), |
| 358 | testing.expectEqual(@as(T, 0), try fifo.readItem()); | 409 | }; |
| 359 | testing.expectEqual(@as(T, 1), try fifo.readItem()); | 410 | defer fifo.deinit(); |
| 360 | testing.expectEqual(@as(T, 1), try fifo.readItem()); | 411 | |
| 361 | testing.expectEqual(@as(T, 0), try fifo.readItem()); | 412 | try fifo.write([_]T{ 0, 1, 1, 0, 1 }); |
| 362 | testing.expectEqual(@as(T, 1), try fifo.readItem()); | 413 | testing.expectEqual(@as(usize, 5), fifo.readableLength()); |
| | 414 | |
| | 415 | { |
| | 416 | testing.expectEqual(@as(T, 0), try fifo.readItem()); |
| | 417 | testing.expectEqual(@as(T, 1), try fifo.readItem()); |
| | 418 | testing.expectEqual(@as(T, 1), try fifo.readItem()); |
| | 419 | testing.expectEqual(@as(T, 0), try fifo.readItem()); |
| | 420 | testing.expectEqual(@as(T, 1), try fifo.readItem()); |
| | 421 | } |
| 363 | } | 422 | } |
| 364 | } | 423 | } |
| 365 | } | 424 | } |