-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Zstandard decompressor #14394
Merged
Merged
Zstandard decompressor #14394
Changes from all commits
Commits
Show all changes
62 commits
Select commit
Hold shift + click to select a range
19984d8
std.hash: add XxHash64 and XxHash32
61cb514
std.compress: add zstandard decompressor
1809172
std.compress.zstandard: cleanup decodeBlock
05e63f2
std.compress.zstandard: add functions decoding into ring buffer
c819e58
std.compress.zstandard: add decodeZStandardFrameAlloc
cbfaa87
std.compress.zstandard: cleanup ReverseBitReader
fc64c27
std.compress.zstandard: clean up api
082acd7
std.compress.zstandard: clean up integer casts
6b85373
std.compress.zstandard: validate sequence lengths
95953e1
std.compress.zstandard: fix dictionary field size
31d1cae
std.compress.zstandard: validate fse table value count
774e2f5
std.compress.zstandard: add input length safety checks
d40b135
std.compress.zstandard: properly track consumed count in decodeFrameB…
ab18adf
std.compress.zstandard: remove debug logging
7558bf6
std.compress.zstandard: minor cleanup and add doc comments
e2306ef
std.compress.zstandard: add integer casts u64 -> usize
1e5b8be
std.compress.zstandard: add window size limit param
3c06e2e
std.compress.zstandard: add doc comments for RingBuffer
3bfba36
std.compress.zstandard: clean up error sets and line lengths
e92575d
std.compress.zstandard: verify checksum in decodeFrameAlloc()
2d35c16
std.compress.zstandard: add init/deinit for ring buffer, fix len()
947ad3e
std.compress.zstandard: add FrameContext and add literals into Decode…
5723291
std.compress.zstandard: add `decodeBlockReader`
a180fcc
std.compress.zstandard: add `ZstandardStream`
6e3e728
std.compress.zstandard: fix crashes
7e27556
std.compress.zstandard: split decompressor into multiple files
89f9c5c
std.compress.zstandard: improve doc comments
ddeabc9
std.compress.zstandard: add `decodeFrameAlloc()`
3f1c430
std.compress.zstandard: fix capitalisation of Zstandard
a651704
std.compress.zstandard: free allocated result on error
596a97f
std.compress.zstandard: fix crashes
1c509f4
std.compress.zstandard: fix crashes
a625df4
std.compress.zstandard: fix fse decoding crash
06ab5a2
std.compress.zstandard: add multi-frame decoding functions
a9c8376
std.compress.zstandard: make ZstandardStream decode multiple frames
ece52e0
std.compress.zstandard: verify content size and fix crash
98bbd95
std.compress.zstandard: improve block size validation
2134769
std.compress.zstandard: validate skippable frame size
d9a90e1
std.compress.zstandard: fix decodeAlloc() and remove decodeFrameAlloc()
77ca1f7
std.compress.zstandard: remove UnusedBitSet error
3975a9d
std.compress.zstandard: error when FSE bitstream is no fully consumed
6d48b05
std.compress.zstandard: add decodeFrameHeader
55e6e94
std.compress.zstandard: fix content size check
31cc460
std.compress.zstandard: fix errors and crashes in ZstandardStream
ee5af3c
std.compress.zstandard: cleanup high-level api docs and error sets
1530e73
std.compress.zstandard: bytes read assert to error in decodeBlockReader
373d8ef
std.compress.zstandard: check FSE bitstreams are fully consumed
476d2fe
std.compress.zstandard: fix zstandardStream finishing early
8fd4131
std.compress.zstandard: remove unneeded branch
5a31fc2
std.compress.zstandard: fix erroneous literal stream empty checks
a53cf29
std.compress.zstandard: add error condition to ring buffer decoding
12aa478
std.compress.zstandard: also check block size when sequence count is 0
1a86217
std.compress.zstandard: fix zstandardStream content size validation
2766b70
std.compress.zstandard: add DictionaryIdFlagUnsupported ZstandardStre…
a74f800
std.compress.zstandard: update for multi-for-loop change
a34c2de
std.hash: use std.math.rotl in Xxhash64 and Xxhash32
12d9f73
std.compress.zstandard: remove use of usingnamespace
1c518bd
std.compress.zstandard: rename ZStandardStream -> DecompressStream
c7c35bf
std.RingBuffer: add (non-concurrent) RingBuffer implementation
c6ef83e
std.compress.zstandard: clean up streaming API
32cf1d7
std.compress.zstandard: fix error sets for streaming API
765a6d3
std.compress.zstd: renamed from std.compress.zstandard
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
//! This ring buffer stores read and write indices while being able to utilise | ||
//! the full backing slice by incrementing the indices modulo twice the slice's | ||
//! length and reducing indices modulo the slice's length on slice access. This | ||
//! means that whether the ring buffer if full or empty can be distinguished by | ||
//! looking at the difference between the read and write indices without adding | ||
//! an extra boolean flag or having to reserve a slot in the buffer. | ||
//! | ||
//! This ring buffer has not been implemented with thread safety in mind, and | ||
//! therefore should not be assumed to be suitable for use cases involving | ||
//! separate reader and writer threads. | ||
|
||
const Allocator = @import("std").mem.Allocator; | ||
const assert = @import("std").debug.assert; | ||
|
||
const RingBuffer = @This(); | ||
|
||
data: []u8, | ||
read_index: usize, | ||
write_index: usize, | ||
|
||
pub const Error = error{Full}; | ||
|
||
/// Allocate a new `RingBuffer`; `deinit()` should be called to free the buffer. | ||
pub fn init(allocator: Allocator, capacity: usize) Allocator.Error!RingBuffer { | ||
const bytes = try allocator.alloc(u8, capacity); | ||
return RingBuffer{ | ||
.data = bytes, | ||
.write_index = 0, | ||
.read_index = 0, | ||
}; | ||
} | ||
|
||
/// Free the data backing a `RingBuffer`; must be passed the same `Allocator` as | ||
/// `init()`. | ||
pub fn deinit(self: *RingBuffer, allocator: Allocator) void { | ||
allocator.free(self.data); | ||
self.* = undefined; | ||
} | ||
|
||
/// Returns `index` modulo the length of the backing slice. | ||
pub fn mask(self: RingBuffer, index: usize) usize { | ||
return index % self.data.len; | ||
} | ||
|
||
/// Returns `index` modulo twice the length of the backing slice. | ||
pub fn mask2(self: RingBuffer, index: usize) usize { | ||
return index % (2 * self.data.len); | ||
} | ||
|
||
/// Write `byte` into the ring buffer. Returns `error.Full` if the ring | ||
/// buffer is full. | ||
pub fn write(self: *RingBuffer, byte: u8) Error!void { | ||
if (self.isFull()) return error.Full; | ||
self.writeAssumeCapacity(byte); | ||
} | ||
|
||
/// Write `byte` into the ring buffer. If the ring buffer is full, the | ||
/// oldest byte is overwritten. | ||
pub fn writeAssumeCapacity(self: *RingBuffer, byte: u8) void { | ||
self.data[self.mask(self.write_index)] = byte; | ||
self.write_index = self.mask2(self.write_index + 1); | ||
} | ||
|
||
/// Write `bytes` into the ring buffer. Returns `error.Full` if the ring | ||
/// buffer does not have enough space, without writing any data. | ||
pub fn writeSlice(self: *RingBuffer, bytes: []const u8) Error!void { | ||
if (self.len() + bytes.len > self.data.len) return error.Full; | ||
self.writeSliceAssumeCapacity(bytes); | ||
} | ||
|
||
/// Write `bytes` into the ring buffer. If there is not enough space, older | ||
/// bytes will be overwritten. | ||
pub fn writeSliceAssumeCapacity(self: *RingBuffer, bytes: []const u8) void { | ||
for (bytes) |b| self.writeAssumeCapacity(b); | ||
} | ||
|
||
/// Consume a byte from the ring buffer and return it. Returns `null` if the | ||
/// ring buffer is empty. | ||
pub fn read(self: *RingBuffer) ?u8 { | ||
if (self.isEmpty()) return null; | ||
return self.readAssumeLength(); | ||
} | ||
|
||
/// Consume a byte from the ring buffer and return it; asserts that the buffer | ||
/// is not empty. | ||
pub fn readAssumeLength(self: *RingBuffer) u8 { | ||
assert(!self.isEmpty()); | ||
const byte = self.data[self.mask(self.read_index)]; | ||
self.read_index = self.mask2(self.read_index + 1); | ||
return byte; | ||
} | ||
|
||
/// Returns `true` if the ring buffer is empty and `false` otherwise. | ||
pub fn isEmpty(self: RingBuffer) bool { | ||
return self.write_index == self.read_index; | ||
} | ||
|
||
/// Returns `true` if the ring buffer is full and `false` otherwise. | ||
pub fn isFull(self: RingBuffer) bool { | ||
return self.mask2(self.write_index + self.data.len) == self.read_index; | ||
} | ||
|
||
/// Returns the length | ||
pub fn len(self: RingBuffer) usize { | ||
const wrap_offset = 2 * self.data.len * @boolToInt(self.write_index < self.read_index); | ||
const adjusted_write_index = self.write_index + wrap_offset; | ||
return adjusted_write_index - self.read_index; | ||
} | ||
|
||
/// A `Slice` represents a region of a ring buffer. The region is split into two | ||
/// sections as the ring buffer data will not be contiguous if the desired | ||
/// region wraps to the start of the backing slice. | ||
pub const Slice = struct { | ||
first: []u8, | ||
second: []u8, | ||
}; | ||
|
||
/// Returns a `Slice` for the region of the ring buffer starting at | ||
/// `self.mask(start_unmasked)` with the specified length. | ||
pub fn sliceAt(self: RingBuffer, start_unmasked: usize, length: usize) Slice { | ||
assert(length <= self.data.len); | ||
const slice1_start = self.mask(start_unmasked); | ||
const slice1_end = @min(self.data.len, slice1_start + length); | ||
const slice1 = self.data[slice1_start..slice1_end]; | ||
const slice2 = self.data[0 .. length - slice1.len]; | ||
return Slice{ | ||
.first = slice1, | ||
.second = slice2, | ||
}; | ||
} | ||
|
||
/// Returns a `Slice` for the last `length` bytes written to the ring buffer. | ||
/// Does not check that any bytes have been written into the region. | ||
pub fn sliceLast(self: RingBuffer, length: usize) Slice { | ||
return self.sliceAt(self.write_index + self.data.len - length, length); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This API has quite a bit of overlap with
std.fifo.LinearFifo()
, I'm not sure we should have both in the standard library.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is quite a bit of overlap (I wasn't aware of
LinearFifo
), however if I'm reading correctlyLinearFifo
as it is currently isn't designed to be used the way the ring buffer is used, so I'm not sure it would be a good option to adapt/extendLinearFifo
and make use of it. If it would be confusing to have both, I can take it back out of thestd
namespace, or we can leave it pending the pre 1.0 stdlib review. Thoughts @andrewrk?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To add to this discussion, it looks like the lzma implementation has its own
LzCircularBuffer
type as well which is also a somewhat specialized ring buffer.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then maybe
LzCircularBuffer
should be replaced by thisRingBuffer
, too.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Opened #19231 to track this