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;...@@ -9,30 +9,77 @@ const debug = std.debug;
9const assert = debug.assert;9const assert = debug.assert;
10const testing = std.testing;10const 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 {
13 const autoalign = false;27 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
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,
2240
23 const Self = @This();41 const Self = @This();
2442
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 };
3380
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 }
3885
...@@ -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 }
71120
72 /// Ensure that the buffer can fit at least `size` items121 /// 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 }
79132
80 /// Makes sure at least `size` items are unused133 /// 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 }
91144
92 /// Returns a writable slice from the 'read' end of the fifo145 /// 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{};
95148
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 }
108161
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 }
113166
...@@ -173,7 +226,7 @@ pub fn FixedSizeFifo(comptime T: type) type {...@@ -173,7 +226,7 @@ pub fn FixedSizeFifo(comptime T: type) type {
173226
174 /// Returns the first section of writable buffer227 /// Returns the first section of writable buffer
175 /// Note that this may be of length 0228 /// 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{};
178231
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}
281334
282const ByteFifo = FixedSizeFifo(u8);335test "FixedSizeFifo(u8, .Dynamic)" {
283336 var fifo = FixedSizeFifo(u8, .Dynamic).init(debug.global_allocator);
284test "ByteFifo" {
285 var fifo = ByteFifo.init(debug.global_allocator);
286 defer fifo.deinit();337 defer fifo.deinit();
287338
288 try fifo.write("HELLO");339 try fifo.write("HELLO");
...@@ -348,18 +399,26 @@ test "ByteFifo" {...@@ -348,18 +399,26 @@ test "ByteFifo" {
348399
349test "FixedSizeFifo" {400test "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);
353404 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(),
356407 .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}