authorgravatar for quae@daurnimator.comdaurnimator <quae@daurnimator.com> 2019-11-11 01:23:21+11:00
committergravatar for quae@daurnimator.comdaurnimator <quae@daurnimator.com> 2019-11-25 09:26:32+11:00
logcd749e04161a1a3899ed0a723a5cde444159900e
tree7744902ac53c5a09d9c5931142f6c85c40986b5d
parentc0e47cb645a96e8b80ba6f93e87ad276853570e0
signaturelock-open Commit is signed but in an unrecognized format.

std: fifo now has 3 modes: Static, Slice and Dynamic


1 files changed, 98 insertions(+), 39 deletions(-)

lib/std/fifo.zig+98-39
......@@ -9,30 +9,77 @@ const debug = std.debug;
99const assert = debug.assert;
1010const testing = std.testing;
1111
12pub fn FixedSizeFifo(comptime T: type) type {
12pub 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
23pub fn FixedSizeFifo(
24 comptime T: type,
25 comptime buffer_type: FifoBufferType,
26) type {
1327 const autoalign = false;
1428
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 };
1634
1735 return struct {
18 allocator: *Allocator,
19 buf: []T,
36 allocator: if (buffer_type == .Dynamic) *Allocator else void,
37 buf: if (buffer_type == .Static) [buffer_type.Static]T else []T,
2038 head: usize,
2139 count: usize,
2240
2341 const Self = @This();
2442
25 pub fn init(allocator: *Allocator) Self {
26 return Self{
27 .allocator = allocator,
28 .buf = [_]T{},
29 .head = 0,
30 .count = 0,
31 };
32 }
43 // Type of Self argument for slice operations.
44 // If buffer is inline (Static) then we need to ensure we haven't
45 // returned a slice into a copy on the stack
46 const SliceSelfArg = if (buffer_type == .Static) *Self else Self;
47
48 pub usingnamespace switch (buffer_type) {
49 .Static => struct {
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 };
3380
3481 pub fn deinit(self: *Self) void {
35 self.allocator.free(self.buf);
82 if (buffer_type == .Dynamic) self.allocator.free(self.buf);
3683 self.* = undefined;
3784 }
3885
......@@ -63,18 +110,24 @@ pub fn FixedSizeFifo(comptime T: type) type {
63110 /// Reduce allocated capacity to `size`.
64111 pub fn shrink(self: *Self, size: usize) void {
65112 assert(size >= self.count);
66 self.realign();
67 self.buf = self.allocator.realloc(self.buf, size) catch |e| switch (e) {
68 error.OutOfMemory => return, // no problem, capacity is still correct then.
69 };
113 if (buffer_type == .Dynamic) {
114 self.realign();
115 self.buf = self.allocator.realloc(self.buf, size) catch |e| switch (e) {
116 error.OutOfMemory => return, // no problem, capacity is still correct then.
117 };
118 }
70119 }
71120
72121 /// Ensure that the buffer can fit at least `size` items
73122 pub fn ensureCapacity(self: *Self, size: usize) !void {
74123 if (self.buf.len >= size) return;
75 self.realign();
76 const new_size = if (powers_of_two) math.ceilPowerOfTwo(usize, size) catch return error.OutOfMemory else size;
77 self.buf = try self.allocator.realloc(self.buf, new_size);
124 if (buffer_type == .Dynamic) {
125 self.realign();
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 }
78131 }
79132
80133 /// Makes sure at least `size` items are unused
......@@ -90,7 +143,7 @@ pub fn FixedSizeFifo(comptime T: type) type {
90143 }
91144
92145 /// 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 {
94147 if (offset > self.count) return [_]T{};
95148
96149 const start = self.head + offset;
......@@ -107,7 +160,7 @@ pub fn FixedSizeFifo(comptime T: type) type {
107160 }
108161
109162 /// 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 {
111164 return self.readableSliceMut(offset);
112165 }
113166
......@@ -173,7 +226,7 @@ pub fn FixedSizeFifo(comptime T: type) type {
173226
174227 /// Returns the first section of writable buffer
175228 /// 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 {
177230 if (offset > self.buf.len) return [_]T{};
178231
179232 const tail = self.head + offset + self.count;
......@@ -279,10 +332,8 @@ pub fn FixedSizeFifo(comptime T: type) type {
279332 };
280333}
281334
282const ByteFifo = FixedSizeFifo(u8);
283
284test "ByteFifo" {
285 var fifo = ByteFifo.init(debug.global_allocator);
335test "FixedSizeFifo(u8, .Dynamic)" {
336 var fifo = FixedSizeFifo(u8, .Dynamic).init(debug.global_allocator);
286337 defer fifo.deinit();
287338
288339 try fifo.write("HELLO");
......@@ -348,18 +399,26 @@ test "ByteFifo" {
348399
349400test "FixedSizeFifo" {
350401 inline for ([_]type{ u1, u8, u16, u64 }) |T| {
351 var fifo = FixedSizeFifo(T).init(debug.global_allocator);
352 defer fifo.deinit();
353
354 try fifo.write([_]T{ 0, 1, 1, 0, 1 });
355 testing.expectEqual(@as(usize, 5), fifo.readableLength());
356
357 {
358 testing.expectEqual(@as(T, 0), try fifo.readItem());
359 testing.expectEqual(@as(T, 1), try fifo.readItem());
360 testing.expectEqual(@as(T, 1), try fifo.readItem());
361 testing.expectEqual(@as(T, 0), try fifo.readItem());
362 testing.expectEqual(@as(T, 1), try fifo.readItem());
402 inline for ([_]FifoBufferType{ FifoBufferType{ .Static = 32 }, .Slice, .Dynamic }) |bt| {
403 const FifoType = FixedSizeFifo(T, bt);
404 var buf: if (bt == .Slice) [32]T else void = undefined;
405 var fifo = switch (bt) {
406 .Static => FifoType.init(),
407 .Slice => FifoType.init(buf[0..]),
408 .Dynamic => FifoType.init(debug.global_allocator),
409 };
410 defer fifo.deinit();
411
412 try fifo.write([_]T{ 0, 1, 1, 0, 1 });
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 }
363422 }
364423 }
365424}