Skip to content

Commit b52be97

Browse files
authored
Merge pull request #14394 from dweiller/zstandard
Zstandard decompressor
2 parents 98dd041 + 765a6d3 commit b52be97

16 files changed

+6383
-0
lines changed

build.zig

+3
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,11 @@ pub fn build(b: *std.Build) !void {
113113
".gz",
114114
".z.0",
115115
".z.9",
116+
".zstd.3",
117+
".zstd.19",
116118
"rfc1951.txt",
117119
"rfc1952.txt",
120+
"rfc8478.txt",
118121
// exclude files from lib/std/compress/deflate/testdata
119122
".expect",
120123
".expect-noinput",

lib/std/RingBuffer.zig

+136
Original file line numberDiff line numberDiff line change
@@ -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.zig

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub const lzma = @import("compress/lzma.zig");
66
pub const lzma2 = @import("compress/lzma2.zig");
77
pub const xz = @import("compress/xz.zig");
88
pub const zlib = @import("compress/zlib.zig");
9+
pub const zstd = @import("compress/zstandard.zig");
910

1011
pub fn HashedReader(
1112
comptime ReaderType: anytype,
@@ -44,4 +45,5 @@ test {
4445
_ = lzma2;
4546
_ = xz;
4647
_ = zlib;
48+
_ = zstd;
4749
}

0 commit comments

Comments
 (0)