authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-11-24 17:33:53-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2019-11-24 17:33:53-05:00
log8309b6188d849c5d4c27c086b7759566d9f86716
treec44f3023b78ac9b7ed126a36d894a4de7e487413
parenteea8b10463b944dae5434e748c9a5b67c2287bea
parent1a84bcefb658b45103155725067e0820dd4243a0
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #3644 from daurnimator/bytefifo

Improvements to std.fifo

1 files changed, 183 insertions(+), 60 deletions(-)

lib/std/fifo.zig+183-60
......@@ -9,27 +9,77 @@ const debug = std.debug;
99const assert = debug.assert;
1010const testing = std.testing;
1111
12pub fn FixedSizeFifo(comptime T: type) type {
12pub const LinearFifoBufferType = 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 LinearFifo(
24 comptime T: type,
25 comptime buffer_type: LinearFifoBufferType,
26) type {
27 const autoalign = false;
28
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 };
34
1335 return struct {
14 allocator: *Allocator,
15 buf: []T,
36 allocator: if (buffer_type == .Dynamic) *Allocator else void,
37 buf: if (buffer_type == .Static) [buffer_type.Static]T else []T,
1638 head: usize,
1739 count: usize,
1840
1941 const Self = @This();
2042
21 pub fn init(allocator: *Allocator) Self {
22 return Self{
23 .allocator = allocator,
24 .buf = [_]T{},
25 .head = 0,
26 .count = 0,
27 };
28 }
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 };
2980
30 pub fn deinit(self: *Self) void {
31 self.allocator.free(self.buf);
32 self.* = undefined;
81 pub fn deinit(self: Self) void {
82 if (buffer_type == .Dynamic) self.allocator.free(self.buf);
3383 }
3484
3585 pub fn realign(self: *Self) void {
......@@ -59,18 +109,24 @@ pub fn FixedSizeFifo(comptime T: type) type {
59109 /// Reduce allocated capacity to `size`.
60110 pub fn shrink(self: *Self, size: usize) void {
61111 assert(size >= self.count);
62 self.realign();
63 self.buf = self.allocator.realloc(self.buf, size) catch |e| switch (e) {
64 error.OutOfMemory => return, // no problem, capacity is still correct then.
65 };
112 if (buffer_type == .Dynamic) {
113 self.realign();
114 self.buf = self.allocator.realloc(self.buf, size) catch |e| switch (e) {
115 error.OutOfMemory => return, // no problem, capacity is still correct then.
116 };
117 }
66118 }
67119
68120 /// Ensure that the buffer can fit at least `size` items
69 pub fn ensureCapacity(self: *Self, size: usize) error{OutOfMemory}!void {
121 pub fn ensureCapacity(self: *Self, size: usize) !void {
70122 if (self.buf.len >= size) return;
71 self.realign();
72 const new_size = math.ceilPowerOfTwo(usize, size) catch return error.OutOfMemory;
73 self.buf = try self.allocator.realloc(self.buf, new_size);
123 if (buffer_type == .Dynamic) {
124 self.realign();
125 const new_size = if (powers_of_two) math.ceilPowerOfTwo(usize, size) catch return error.OutOfMemory else size;
126 self.buf = try self.allocator.realloc(self.buf, new_size);
127 } else {
128 return error.OutOfMemory;
129 }
74130 }
75131
76132 /// Makes sure at least `size` items are unused
......@@ -86,29 +142,24 @@ pub fn FixedSizeFifo(comptime T: type) type {
86142 }
87143
88144 /// Returns a writable slice from the 'read' end of the fifo
89 fn readableSliceMut(self: Self, offset: usize) []T {
145 fn readableSliceMut(self: SliceSelfArg, offset: usize) []T {
90146 if (offset > self.count) return [_]T{};
91147
92 const start = self.head + offset;
148 var start = self.head + offset;
93149 if (start >= self.buf.len) {
94 return self.buf[start - self.buf.len ..][0 .. self.count - offset];
150 start -= self.buf.len;
151 return self.buf[start..self.count - offset];
95152 } else {
96 const end: usize = self.head + self.count;
97 if (end >= self.buf.len) {
98 return self.buf[start..self.buf.len];
99 } else {
100 return self.buf[start..end];
101 }
153 const end = math.min(self.head + self.count, self.buf.len);
154 return self.buf[start..end];
102155 }
103156 }
104157
105158 /// Returns a readable slice from `offset`
106 pub fn readableSlice(self: Self, offset: usize) []const T {
159 pub fn readableSlice(self: SliceSelfArg, offset: usize) []const T {
107160 return self.readableSliceMut(offset);
108161 }
109162
110 const autoalign = false;
111
112163 /// Discard first `count` bytes of readable data
113164 pub fn discard(self: *Self, count: usize) void {
114165 assert(count <= self.count);
......@@ -124,10 +175,19 @@ pub fn FixedSizeFifo(comptime T: type) type {
124175 @memset(unused2.ptr, undefined, unused2.len);
125176 }
126177 }
127 self.head = (self.head + count) % self.buf.len;
128 self.count -= count;
129 if (autoalign and self.count == 0)
178 if (autoalign and self.count == count) {
130179 self.head = 0;
180 self.count = 0;
181 } else {
182 var head = self.head + count;
183 if (powers_of_two) {
184 head &= self.buf.len - 1;
185 } else {
186 head %= self.buf.len;
187 }
188 self.head = head;
189 self.count -= count;
190 }
131191 }
132192
133193 /// Read the next item from the fifo
......@@ -139,8 +199,8 @@ pub fn FixedSizeFifo(comptime T: type) type {
139199 return c;
140200 }
141201
142 /// Read data from the fifo into `dst`, returns slice of bytes copied (subslice of `dst`)
143 pub fn read(self: *Self, dst: []T) []T {
202 /// Read data from the fifo into `dst`, returns number of bytes copied.
203 pub fn read(self: *Self, dst: []T) usize {
144204 var dst_left = dst;
145205
146206 while (dst_left.len > 0) {
......@@ -152,7 +212,7 @@ pub fn FixedSizeFifo(comptime T: type) type {
152212 dst_left = dst_left[n..];
153213 }
154214
155 return dst[0 .. dst.len - dst_left.len];
215 return dst.len - dst_left.len;
156216 }
157217
158218 /// Returns number of bytes available in fifo
......@@ -162,7 +222,7 @@ pub fn FixedSizeFifo(comptime T: type) type {
162222
163223 /// Returns the first section of writable buffer
164224 /// Note that this may be of length 0
165 pub fn writableSlice(self: Self, offset: usize) []T {
225 pub fn writableSlice(self: SliceSelfArg, offset: usize) []T {
166226 if (offset > self.buf.len) return [_]T{};
167227
168228 const tail = self.head + offset + self.count;
......@@ -193,7 +253,8 @@ pub fn FixedSizeFifo(comptime T: type) type {
193253 self.count += count;
194254 }
195255
196 /// Appends the data in `src` to the fifo. You must
256 /// Appends the data in `src` to the fifo.
257 /// You must have ensured there is enough space.
197258 pub fn writeAssumeCapacity(self: *Self, src: []const T) void {
198259 assert(self.writableLength() >= src.len);
199260
......@@ -208,6 +269,20 @@ pub fn FixedSizeFifo(comptime T: type) type {
208269 }
209270 }
210271
272 /// Write a single item to the fifo
273 pub fn writeItem(self: *Self, item: T) !void {
274 try self.ensureUnusedCapacity(1);
275
276 var tail = self.head + self.count;
277 if (powers_of_two) {
278 tail &= self.buf.len - 1;
279 } else {
280 tail %= self.buf.len;
281 }
282 self.buf[tail] = byte;
283 self.update(1);
284 }
285
211286 /// Appends the data in `src` to the fifo.
212287 /// Allocates more memory as necessary
213288 pub fn write(self: *Self, src: []const T) !void {
......@@ -216,16 +291,27 @@ pub fn FixedSizeFifo(comptime T: type) type {
216291 return self.writeAssumeCapacity(src);
217292 }
218293
219 pub fn print(self: *Self, comptime format: []const u8, args: ...) !void {
220 return std.fmt.format(self, error{OutOfMemory}, Self.write, format, args);
221 }
294 pub usingnamespace if (T == u8)
295 struct {
296 pub fn print(self: *Self, comptime format: []const u8, args: ...) !void {
297 return std.fmt.format(self, error{OutOfMemory}, Self.write, format, args);
298 }
299 }
300 else
301 struct {};
222302
223 /// Make `count` bytes available before the current read location
224 fn rewind(self: *Self, size: usize) void {
225 assert(self.writableLength() >= size);
303 /// Make `count` items available before the current read location
304 fn rewind(self: *Self, count: usize) void {
305 assert(self.writableLength() >= count);
226306
227 self.head = (self.head + (self.buf.len - size)) % self.buf.len;
228 self.count += size;
307 var head = self.head + (self.buf.len - count);
308 if (powers_of_two) {
309 head &= self.buf.len - 1;
310 } else {
311 head %= self.buf.len;
312 }
313 self.head = head;
314 self.count += count;
229315 }
230316
231317 /// Place data back into the read stream
......@@ -235,9 +321,13 @@ pub fn FixedSizeFifo(comptime T: type) type {
235321 self.rewind(src.len);
236322
237323 const slice = self.readableSliceMut(0);
238 mem.copy(T, slice, src[0..slice.len]);
239 const slice2 = self.readableSliceMut(slice.len);
240 mem.copy(T, slice2, src[slice.len..]);
324 if (src.len < slice.len) {
325 mem.copy(T, slice, src);
326 } else {
327 mem.copy(T, slice, src[0..slice.len]);
328 const slice2 = self.readableSliceMut(slice.len);
329 mem.copy(T, slice2, src[slice.len..]);
330 }
241331 }
242332
243333 /// Peek at the item at `offset`
......@@ -245,15 +335,19 @@ pub fn FixedSizeFifo(comptime T: type) type {
245335 if (offset >= self.count)
246336 return error.EndOfStream;
247337
248 return self.buf[(self.head + offset) % self.buf.len];
338 var index = self.head + offset;
339 if (powers_of_two) {
340 index &= self.buf.len - 1;
341 } else {
342 index %= self.buf.len;
343 }
344 return self.buf[index];
249345 }
250346 };
251347}
252348
253const ByteFifo = FixedSizeFifo(u8);
254
255test "ByteFifo" {
256 var fifo = ByteFifo.init(debug.global_allocator);
349test "LinearFifo(u8, .Dynamic)" {
350 var fifo = LinearFifo(u8, .Dynamic).init(debug.global_allocator);
257351 defer fifo.deinit();
258352
259353 try fifo.write("HELLO");
......@@ -304,7 +398,10 @@ test "ByteFifo" {
304398 {
305399 try fifo.unget("prependedstring");
306400 var result: [30]u8 = undefined;
307 testing.expectEqualSlices(u8, "prependedstringabcdefghij", fifo.read(&result));
401 testing.expectEqualSlices(u8, "prependedstringabcdefghij", result[0..fifo.read(&result)]);
402 try fifo.unget("b");
403 try fifo.unget("a");
404 testing.expectEqualSlices(u8, "ab", result[0..fifo.read(&result)]);
308405 }
309406
310407 fifo.shrink(0);
......@@ -312,7 +409,33 @@ test "ByteFifo" {
312409 {
313410 try fifo.print("{}, {}!", "Hello", "World");
314411 var result: [30]u8 = undefined;
315 testing.expectEqualSlices(u8, "Hello, World!", fifo.read(&result));
412 testing.expectEqualSlices(u8, "Hello, World!", result[0..fifo.read(&result)]);
316413 testing.expectEqual(@as(usize, 0), fifo.readableLength());
317414 }
318415}
416
417test "LinearFifo" {
418 inline for ([_]type{ u1, u8, u16, u64 }) |T| {
419 inline for ([_]LinearFifoBufferType{ LinearFifoBufferType{ .Static = 32 }, .Slice, .Dynamic }) |bt| {
420 const FifoType = LinearFifo(T, bt);
421 var buf: if (bt == .Slice) [32]T else void = undefined;
422 var fifo = switch (bt) {
423 .Static => FifoType.init(),
424 .Slice => FifoType.init(buf[0..]),
425 .Dynamic => FifoType.init(debug.global_allocator),
426 };
427 defer fifo.deinit();
428
429 try fifo.write([_]T{ 0, 1, 1, 0, 1 });
430 testing.expectEqual(@as(usize, 5), fifo.readableLength());
431
432 {
433 testing.expectEqual(@as(T, 0), try fifo.readItem());
434 testing.expectEqual(@as(T, 1), try fifo.readItem());
435 testing.expectEqual(@as(T, 1), try fifo.readItem());
436 testing.expectEqual(@as(T, 0), try fifo.readItem());
437 testing.expectEqual(@as(T, 1), try fifo.readItem());
438 }
439 }
440 }
441}