| author | |
| committer | |
| log | c7c35bf9e61035ead3098d109dc894b285373622 |
| tree | b70c5924b82efb6f44d8ec74e524fc5a86b3755a |
| parent | 1c518bd993b159d80a24925dc09ae7da5035ee05 |
6 files changed, 140 insertions(+), 128 deletions(-)
lib/std/RingBuffer.zig created+136| ... | @@ -0,0 +1,136 @@ | ||
| 1 | //! This ring buffer stores read and write indices while being able to utilise | ||
| 2 | //! the full backing slice by incrementing the indices modulo twice the slice's | ||
| 3 | //! length and reducing indices modulo the slice's length on slice access. This | ||
| 4 | //! means that whether the ring buffer if full or empty can be distinguished by | ||
| 5 | //! looking at the difference between the read and write indices without adding | ||
| 6 | //! an extra boolean flag or having to reserve a slot in the buffer. | ||
| 7 | //! | ||
| 8 | //! This ring buffer has not been implemented with thread safety in mind, and | ||
| 9 | //! therefore should not be assumed to be suitable for use cases involving | ||
| 10 | //! separate reader and writer threads. | ||
| 11 | |||
| 12 | const Allocator = @import("std").mem.Allocator; | ||
| 13 | const assert = @import("std").debug.assert; | ||
| 14 | |||
| 15 | const RingBuffer = @This(); | ||
| 16 | |||
| 17 | data: []u8, | ||
| 18 | read_index: usize, | ||
| 19 | write_index: usize, | ||
| 20 | |||
| 21 | pub const Error = error{Full}; | ||
| 22 | |||
| 23 | /// Allocate a new `RingBuffer`; `deinit()` should be called to free the buffer. | ||
| 24 | pub fn init(allocator: Allocator, capacity: usize) Allocator.Error!RingBuffer { | ||
| 25 | const bytes = try allocator.alloc(u8, capacity); | ||
| 26 | return RingBuffer{ | ||
| 27 | .data = bytes, | ||
| 28 | .write_index = 0, | ||
| 29 | .read_index = 0, | ||
| 30 | }; | ||
| 31 | } | ||
| 32 | |||
| 33 | /// Free the data backing a `RingBuffer`; must be passed the same `Allocator` as | ||
| 34 | /// `init()`. | ||
| 35 | pub fn deinit(self: *RingBuffer, allocator: Allocator) void { | ||
| 36 | allocator.free(self.data); | ||
| 37 | self.* = undefined; | ||
| 38 | } | ||
| 39 | |||
| 40 | /// Returns `index` modulo the length of the backing slice. | ||
| 41 | pub fn mask(self: RingBuffer, index: usize) usize { | ||
| 42 | return index % self.data.len; | ||
| 43 | } | ||
| 44 | |||
| 45 | /// Returns `index` modulo twice the length of the backing slice. | ||
| 46 | pub fn mask2(self: RingBuffer, index: usize) usize { | ||
| 47 | return index % (2 * self.data.len); | ||
| 48 | } | ||
| 49 | |||
| 50 | /// Write `byte` into the ring buffer. Returns `error.Full` if the ring | ||
| 51 | /// buffer is full. | ||
| 52 | pub fn write(self: *RingBuffer, byte: u8) Error!void { | ||
| 53 | if (self.isFull()) return error.Full; | ||
| 54 | self.writeAssumeCapacity(byte); | ||
| 55 | } | ||
| 56 | |||
| 57 | /// Write `byte` into the ring buffer. If the ring buffer is full, the | ||
| 58 | /// oldest byte is overwritten. | ||
| 59 | pub fn writeAssumeCapacity(self: *RingBuffer, byte: u8) void { | ||
| 60 | self.data[self.mask(self.write_index)] = byte; | ||
| 61 | self.write_index = self.mask2(self.write_index + 1); | ||
| 62 | } | ||
| 63 | |||
| 64 | /// Write `bytes` into the ring buffer. Returns `error.Full` if the ring | ||
| 65 | /// buffer does not have enough space, without writing any data. | ||
| 66 | pub fn writeSlice(self: *RingBuffer, bytes: []const u8) Error!void { | ||
| 67 | if (self.len() + bytes.len > self.data.len) return error.Full; | ||
| 68 | self.writeSliceAssumeCapacity(bytes); | ||
| 69 | } | ||
| 70 | |||
| 71 | /// Write `bytes` into the ring buffer. If there is not enough space, older | ||
| 72 | /// bytes will be overwritten. | ||
| 73 | pub fn writeSliceAssumeCapacity(self: *RingBuffer, bytes: []const u8) void { | ||
| 74 | for (bytes) |b| self.writeAssumeCapacity(b); | ||
| 75 | } | ||
| 76 | |||
| 77 | /// Consume a byte from the ring buffer and return it. Returns `null` if the | ||
| 78 | /// ring buffer is empty. | ||
| 79 | pub fn read(self: *RingBuffer) ?u8 { | ||
| 80 | if (self.isEmpty()) return null; | ||
| 81 | return self.readAssumeLength(); | ||
| 82 | } | ||
| 83 | |||
| 84 | /// Consume a byte from the ring buffer and return it; asserts that the buffer | ||
| 85 | /// is not empty. | ||
| 86 | pub fn readAssumeLength(self: *RingBuffer) u8 { | ||
| 87 | assert(!self.isEmpty()); | ||
| 88 | const byte = self.data[self.mask(self.read_index)]; | ||
| 89 | self.read_index = self.mask2(self.read_index + 1); | ||
| 90 | return byte; | ||
| 91 | } | ||
| 92 | |||
| 93 | /// Returns `true` if the ring buffer is empty and `false` otherwise. | ||
| 94 | pub fn isEmpty(self: RingBuffer) bool { | ||
| 95 | return self.write_index == self.read_index; | ||
| 96 | } | ||
| 97 | |||
| 98 | /// Returns `true` if the ring buffer is full and `false` otherwise. | ||
| 99 | pub fn isFull(self: RingBuffer) bool { | ||
| 100 | return self.mask2(self.write_index + self.data.len) == self.read_index; | ||
| 101 | } | ||
| 102 | |||
| 103 | /// Returns the length | ||
| 104 | pub fn len(self: RingBuffer) usize { | ||
| 105 | const wrap_offset = 2 * self.data.len * @boolToInt(self.write_index < self.read_index); | ||
| 106 | const adjusted_write_index = self.write_index + wrap_offset; | ||
| 107 | return adjusted_write_index - self.read_index; | ||
| 108 | } | ||
| 109 | |||
| 110 | /// A `Slice` represents a region of a ring buffer. The region is split into two | ||
| 111 | /// sections as the ring buffer data will not be contiguous if the desired | ||
| 112 | /// region wraps to the start of the backing slice. | ||
| 113 | pub const Slice = struct { | ||
| 114 | first: []u8, | ||
| 115 | second: []u8, | ||
| 116 | }; | ||
| 117 | |||
| 118 | /// Returns a `Slice` for the region of the ring buffer starting at | ||
| 119 | /// `self.mask(start_unmasked)` with the specified length. | ||
| 120 | pub fn sliceAt(self: RingBuffer, start_unmasked: usize, length: usize) Slice { | ||
| 121 | assert(length <= self.data.len); | ||
| 122 | const slice1_start = self.mask(start_unmasked); | ||
| 123 | const slice1_end = @min(self.data.len, slice1_start + length); | ||
| 124 | const slice1 = self.data[slice1_start..slice1_end]; | ||
| 125 | const slice2 = self.data[0 .. length - slice1.len]; | ||
| 126 | return Slice{ | ||
| 127 | .first = slice1, | ||
| 128 | .second = slice2, | ||
| 129 | }; | ||
| 130 | } | ||
| 131 | |||
| 132 | /// Returns a `Slice` for the last `length` bytes written to the ring buffer. | ||
| 133 | /// Does not check that any bytes have been written into the region. | ||
| 134 | pub fn sliceLast(self: RingBuffer, length: usize) Slice { | ||
| 135 | return self.sliceAt(self.write_index + self.data.len - length, length); | ||
| 136 | } | ||
lib/std/compress/zstandard.zig+1-1| ... | @@ -1,11 +1,11 @@ | ... | @@ -1,11 +1,11 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const Allocator = std.mem.Allocator; | 2 | const Allocator = std.mem.Allocator; |
| 3 | const RingBuffer = std.RingBuffer; | ||
| 3 | 4 | ||
| 4 | const types = @import("zstandard/types.zig"); | 5 | const types = @import("zstandard/types.zig"); |
| 5 | pub const frame = types.frame; | 6 | pub const frame = types.frame; |
| 6 | pub const compressed_block = types.compressed_block; | 7 | pub const compressed_block = types.compressed_block; |
| 7 | 8 | ||
| 8 | const RingBuffer = @import("zstandard/RingBuffer.zig"); | ||
| 9 | pub const decompress = @import("zstandard/decompress.zig"); | 9 | pub const decompress = @import("zstandard/decompress.zig"); |
| 10 | 10 | ||
| 11 | pub fn DecompressStream( | 11 | pub fn DecompressStream( |
lib/std/compress/zstandard/RingBuffer.zig deleted-122| ... | @@ -1,122 +0,0 @@ | ||
| 1 | //! This ring buffer stores read and write indices while being able to utilise the full | ||
| 2 | //! backing slice by incrementing the indices modulo twice the slice's length and reducing | ||
| 3 | //! indices modulo the slice's length on slice access. This means that whether the ring buffer | ||
| 4 | //! if full or empty can be distinguised by looking at the different between the read and write | ||
| 5 | //! indices without adding an extra boolean flag or having to reserve a slot in the buffer. | ||
| 6 | |||
| 7 | const Allocator = @import("std").mem.Allocator; | ||
| 8 | const assert = @import("std").debug.assert; | ||
| 9 | |||
| 10 | const RingBuffer = @This(); | ||
| 11 | |||
| 12 | data: []u8, | ||
| 13 | read_index: usize, | ||
| 14 | write_index: usize, | ||
| 15 | |||
| 16 | pub const Error = error{Full}; | ||
| 17 | |||
| 18 | /// Allocate a new `RingBuffer` | ||
| 19 | pub fn init(allocator: Allocator, capacity: usize) Allocator.Error!RingBuffer { | ||
| 20 | const bytes = try allocator.alloc(u8, capacity); | ||
| 21 | return RingBuffer{ | ||
| 22 | .data = bytes, | ||
| 23 | .write_index = 0, | ||
| 24 | .read_index = 0, | ||
| 25 | }; | ||
| 26 | } | ||
| 27 | |||
| 28 | /// Free a `RingBuffer` | ||
| 29 | pub fn deinit(self: *RingBuffer, allocator: Allocator) void { | ||
| 30 | allocator.free(self.data); | ||
| 31 | self.* = undefined; | ||
| 32 | } | ||
| 33 | |||
| 34 | /// Returns `index` modulo the length of the backing slice. | ||
| 35 | pub fn mask(self: RingBuffer, index: usize) usize { | ||
| 36 | return index % self.data.len; | ||
| 37 | } | ||
| 38 | |||
| 39 | /// Returns `index` module twice the length of the backing slice. | ||
| 40 | pub fn mask2(self: RingBuffer, index: usize) usize { | ||
| 41 | return index % (2 * self.data.len); | ||
| 42 | } | ||
| 43 | |||
| 44 | /// Write `byte` into the ring buffer. Returns `error.Full` if the ring | ||
| 45 | /// buffer is full. | ||
| 46 | pub fn write(self: *RingBuffer, byte: u8) Error!void { | ||
| 47 | if (self.isFull()) return error.Full; | ||
| 48 | self.writeAssumeCapacity(byte); | ||
| 49 | } | ||
| 50 | |||
| 51 | /// Write `byte` into the ring buffer. If the ring buffer is full, the | ||
| 52 | /// oldest byte is overwritten. | ||
| 53 | pub fn writeAssumeCapacity(self: *RingBuffer, byte: u8) void { | ||
| 54 | self.data[self.mask(self.write_index)] = byte; | ||
| 55 | self.write_index = self.mask2(self.write_index + 1); | ||
| 56 | } | ||
| 57 | |||
| 58 | /// Write `bytes` into the ring bufffer. Returns `error.Full` if the ring | ||
| 59 | /// buffer does not have enough space, without writing any data. | ||
| 60 | pub fn writeSlice(self: *RingBuffer, bytes: []const u8) Error!void { | ||
| 61 | if (self.len() + bytes.len > self.data.len) return error.Full; | ||
| 62 | self.writeSliceAssumeCapacity(bytes); | ||
| 63 | } | ||
| 64 | |||
| 65 | /// Write `bytes` into the ring buffer. If there is not enough space, older | ||
| 66 | /// bytes will be overwritten. | ||
| 67 | pub fn writeSliceAssumeCapacity(self: *RingBuffer, bytes: []const u8) void { | ||
| 68 | for (bytes) |b| self.writeAssumeCapacity(b); | ||
| 69 | } | ||
| 70 | |||
| 71 | /// Consume a byte from the ring buffer and return it. Returns `null` if the | ||
| 72 | /// ring buffer is empty. | ||
| 73 | pub fn read(self: *RingBuffer) ?u8 { | ||
| 74 | if (self.isEmpty()) return null; | ||
| 75 | const byte = self.data[self.mask(self.read_index)]; | ||
| 76 | self.read_index = self.mask2(self.read_index + 1); | ||
| 77 | return byte; | ||
| 78 | } | ||
| 79 | |||
| 80 | /// Returns `true` if the ring buffer is empty and `false` otherwise. | ||
| 81 | pub fn isEmpty(self: RingBuffer) bool { | ||
| 82 | return self.write_index == self.read_index; | ||
| 83 | } | ||
| 84 | |||
| 85 | /// Returns `true` if the ring buffer is full and `false` otherwise. | ||
| 86 | pub fn isFull(self: RingBuffer) bool { | ||
| 87 | return self.mask2(self.write_index + self.data.len) == self.read_index; | ||
| 88 | } | ||
| 89 | |||
| 90 | /// Returns the length | ||
| 91 | pub fn len(self: RingBuffer) usize { | ||
| 92 | const wrap_offset = 2 * self.data.len * @boolToInt(self.write_index < self.read_index); | ||
| 93 | const adjusted_write_index = self.write_index + wrap_offset; | ||
| 94 | return adjusted_write_index - self.read_index; | ||
| 95 | } | ||
| 96 | |||
| 97 | /// A `Slice` represents a region of a ring buffer. The region is split into two | ||
| 98 | /// sections as the ring buffer data will not be contiguous if the desired region | ||
| 99 | /// wraps to the start of the backing slice. | ||
| 100 | pub const Slice = struct { | ||
| 101 | first: []u8, | ||
| 102 | second: []u8, | ||
| 103 | }; | ||
| 104 | |||
| 105 | /// Returns a `Slice` for the region of the ring buffer staring at `self.mask(start_unmasked)` | ||
| 106 | /// with the specified length. | ||
| 107 | pub fn sliceAt(self: RingBuffer, start_unmasked: usize, length: usize) Slice { | ||
| 108 | assert(length <= self.data.len); | ||
| 109 | const slice1_start = self.mask(start_unmasked); | ||
| 110 | const slice1_end = @min(self.data.len, slice1_start + length); | ||
| 111 | const slice1 = self.data[slice1_start..slice1_end]; | ||
| 112 | const slice2 = self.data[0 .. length - slice1.len]; | ||
| 113 | return Slice{ | ||
| 114 | .first = slice1, | ||
| 115 | .second = slice2, | ||
| 116 | }; | ||
| 117 | } | ||
| 118 | |||
| 119 | /// Returns a `Slice` for the last `length` bytes written to the ring buffer. | ||
| 120 | pub fn sliceLast(self: RingBuffer, length: usize) Slice { | ||
| 121 | return self.sliceAt(self.write_index + self.data.len - length, length); | ||
| 122 | } | ||
lib/std/compress/zstandard/decode/block.zig+1-3| ... | @@ -1,5 +1,6 @@ | ... | @@ -1,5 +1,6 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const assert = std.debug.assert; | 2 | const assert = std.debug.assert; |
| 3 | const RingBuffer = std.RingBuffer; | ||
| 3 | 4 | ||
| 4 | const types = @import("../types.zig"); | 5 | const types = @import("../types.zig"); |
| 5 | const frame = types.frame; | 6 | const frame = types.frame; |
| ... | @@ -8,9 +9,6 @@ const LiteralsSection = types.compressed_block.LiteralsSection; | ... | @@ -8,9 +9,6 @@ const LiteralsSection = types.compressed_block.LiteralsSection; |
| 8 | const SequencesSection = types.compressed_block.SequencesSection; | 9 | const SequencesSection = types.compressed_block.SequencesSection; |
| 9 | 10 | ||
| 10 | const huffman = @import("huffman.zig"); | 11 | const huffman = @import("huffman.zig"); |
| 11 | |||
| 12 | const RingBuffer = @import("../RingBuffer.zig"); | ||
| 13 | |||
| 14 | const readers = @import("../readers.zig"); | 12 | const readers = @import("../readers.zig"); |
| 15 | 13 | ||
| 16 | const decodeFseTable = @import("fse.zig").decodeFseTable; | 14 | const decodeFseTable = @import("fse.zig").decodeFseTable; |
lib/std/compress/zstandard/decompress.zig+1-2| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const assert = std.debug.assert; | 2 | const assert = std.debug.assert; |
| 3 | const Allocator = std.mem.Allocator; | 3 | const Allocator = std.mem.Allocator; |
| 4 | const RingBuffer = std.RingBuffer; | ||
| 4 | 5 | ||
| 5 | const types = @import("types.zig"); | 6 | const types = @import("types.zig"); |
| 6 | const frame = types.frame; | 7 | const frame = types.frame; |
| ... | @@ -12,8 +13,6 @@ const Table = types.compressed_block.Table; | ... | @@ -12,8 +13,6 @@ const Table = types.compressed_block.Table; |
| 12 | 13 | ||
| 13 | pub const block = @import("decode/block.zig"); | 14 | pub const block = @import("decode/block.zig"); |
| 14 | 15 | ||
| 15 | pub const RingBuffer = @import("RingBuffer.zig"); | ||
| 16 | |||
| 17 | const readers = @import("readers.zig"); | 16 | const readers = @import("readers.zig"); |
| 18 | 17 | ||
| 19 | const readInt = std.mem.readIntLittle; | 18 | const readInt = std.mem.readIntLittle; |
lib/std/std.zig+1| ... | @@ -31,6 +31,7 @@ pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceE | ... | @@ -31,6 +31,7 @@ pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceE |
| 31 | pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue; | 31 | pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue; |
| 32 | pub const PriorityDequeue = @import("priority_dequeue.zig").PriorityDequeue; | 32 | pub const PriorityDequeue = @import("priority_dequeue.zig").PriorityDequeue; |
| 33 | pub const Progress = @import("Progress.zig"); | 33 | pub const Progress = @import("Progress.zig"); |
| 34 | pub const RingBuffer = @import("RingBuffer.zig"); | ||
| 34 | pub const SegmentedList = @import("segmented_list.zig").SegmentedList; | 35 | pub const SegmentedList = @import("segmented_list.zig").SegmentedList; |
| 35 | pub const SemanticVersion = @import("SemanticVersion.zig"); | 36 | pub const SemanticVersion = @import("SemanticVersion.zig"); |
| 36 | pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList; | 37 | pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList; |