-
Notifications
You must be signed in to change notification settings - Fork 112
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
Add support for skippable frames #185
base: main
Are you sure you want to change the base?
Conversation
511e7a7
to
b0e5811
Compare
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.
Hi, and thanks for the PR!
Let's start with zstd-safe. I think it would be best to make these free functions, since they have nothing to do with contexts (contexts are not the only way to encode/decode zstd data).
Otherwise, with a few minor details, it looks nice!
zstd-safe/src/lib.rs
Outdated
buffer, | ||
capacity, | ||
magic_variant, | ||
input.as_ptr() as *mut _, |
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.
We shouldn't turn non-mut &[u8]
into *mut
.
input.as_ptr() as *mut _, | |
ptr_void(input), |
zstd-safe/src/lib.rs
Outdated
#[cfg(feature = "experimental")] | ||
pub fn is_skippable_frame(input: &[u8]) -> SafeResult { | ||
unsafe { | ||
parse_code(zstd_sys::ZSTD_isSkippableFrame(input.as_ptr() as *mut _, input.len()) as usize) |
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.
The documentation for ZSTD_isSkippableFrame
does not indicate that it can return an error, and indeed in the source code we can see it just returns either 0 or 1.
So let's just return a bool
from the function.
parse_code(zstd_sys::ZSTD_isSkippableFrame(input.as_ptr() as *mut _, input.len()) as usize) | |
zstd_sys::ZSTD_isSkippableFrame(ptr_void(input), input.len() > 0 |
zstd-safe/src/lib.rs
Outdated
#[cfg(feature = "experimental")] | ||
// TODO: use InBuffer? | ||
pub fn write_skippable_frame<C: WriteBuf + ?Sized>(output: &mut OutBuffer<'_, C>, input: &[u8], magic_variant: u32) -> SafeResult { | ||
let input_len = input.len(); | ||
unsafe { | ||
output.dst.write_from(|buffer, capacity| { | ||
parse_code(zstd_sys::ZSTD_writeSkippableFrame( | ||
buffer, | ||
capacity, | ||
input.as_ptr() as *mut _, | ||
input_len, | ||
magic_variant, | ||
)) | ||
}) | ||
} | ||
} |
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 no need for an InBuffer
here, or even really an OutBuffer
, since the zstd lib either writes everything in one go (in which case no need to maintain an input cursor) or returns an error.
#[cfg(feature = "experimental")] | |
// TODO: use InBuffer? | |
pub fn write_skippable_frame<C: WriteBuf + ?Sized>(output: &mut OutBuffer<'_, C>, input: &[u8], magic_variant: u32) -> SafeResult { | |
let input_len = input.len(); | |
unsafe { | |
output.dst.write_from(|buffer, capacity| { | |
parse_code(zstd_sys::ZSTD_writeSkippableFrame( | |
buffer, | |
capacity, | |
input.as_ptr() as *mut _, | |
input_len, | |
magic_variant, | |
)) | |
}) | |
} | |
} | |
pub fn write_skippable_frame<C: WriteBuf + ?Sized>(dst: &mut C, input: &[u8], magic_variant: u32) -> SafeResult { | |
let input_len = input.len(); | |
unsafe { | |
dst.write_from(|buffer, capacity| { | |
parse_code(zstd_sys::ZSTD_writeSkippableFrame( | |
buffer, | |
capacity, | |
ptr_void(input), | |
input_len, | |
magic_variant, | |
)) | |
}) | |
} | |
} |
zstd-safe/src/lib.rs
Outdated
pub fn read_skippable_frame<C: WriteBuf + ?Sized>(output: &mut OutBuffer<'_, C>, magic_variant: &mut u32, input: &[u8]) -> SafeResult { | ||
let input_len = input.len(); | ||
unsafe { | ||
output.dst.write_from(|buffer, capacity| { |
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.
Here too no need for an OutBuffer
:
pub fn read_skippable_frame<C: WriteBuf + ?Sized>(output: &mut OutBuffer<'_, C>, magic_variant: &mut u32, input: &[u8]) -> SafeResult { | |
let input_len = input.len(); | |
unsafe { | |
output.dst.write_from(|buffer, capacity| { | |
pub fn read_skippable_frame<C: WriteBuf + ?Sized>(dst: &mut C, magic_variant: &mut u32, input: &[u8]) -> SafeResult { | |
let input_len = input.len(); | |
unsafe { | |
dst.write_from(|buffer, capacity| { |
On the higher-level
The second point in particular is where most changes are required to re-implement quite a bit of logic from the zstd library. I feel like there's quite a lot of added code and complexity, maybe without being worth the added convenience. In particular, regarding the re-implementation of
I would be more comfortable with a solution that:
|
If you do not seek back, how can you detect that there is a skippable frame? And an attempt to call |
And if something happens in |
But how would you combine this function with reading frames in a streaming fashion without seeking back? If you read the first 8 bytes (which could be too much bytes IIRC, e.g. I think you can have a frame of 5 bytes) to check if it's a skippable frame, how would you then read the frame (whether it is skippable or not)? |
We could rely on the Maybe we could go a different way and register a |
Ok, so I just pushed with all the changes you asked. |
Closes #13 if merged |
Ping. |
Any update on this? |
Hi - sorry, have had very little time to look more into this. |
Hi. |
I'm also interested in this feature. |
#[cfg(feature = "experimental")] | ||
pub fn read_skippable_frame<C: WriteBuf + ?Sized>(dst: &mut C, magic_variant: &mut u32, input: &[u8]) -> SafeResult { | ||
let input_len = input.len(); | ||
unsafe { |
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.
I suggest adding SAFETY comments for the unsafe blocks and wrapping the minimum amount of code in the unsafe
block. See https://rust-lang.github.io/rust-clippy/master/#/undocumented_unsafe_blocks
@@ -113,12 +153,17 @@ where | |||
loop { | |||
match self.state { | |||
State::Reading => { | |||
let is_peeking = self.peeking; |
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.
I think you should call the peeking()
function here.
let (bytes_read, bytes_written) = { | ||
// Start with a fresh pool of un-processed data. | ||
// This is the only line that can return an interruption error. | ||
let input = if first { | ||
// eprintln!("First run, no input coming."); | ||
b"" | ||
} else if self.peeking { |
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.
Either check the is_peeking
variable or call the peeking()
function.
3dd6aad
to
d26b298
Compare
Hi.
This add supports for writing and reading skippable frames as well as skipping frames.
There is no support for doing that in a streaming way in the ZSTD C library, so that's why so part of this PR parses some stuff manually.
There are a few TODOs in the code that I'd like you to tell what you think we should do with that.
Thanks for the review.