-
Notifications
You must be signed in to change notification settings - Fork 14
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 ZstdFrameCompressor and ZstdFrameDecompressor for non-streaming API #46
Closed
Closed
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
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
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,101 @@ | ||
# Frame Compressor Codec | ||
# ====================== | ||
|
||
struct ZstdFrameCompressor <: TranscodingStreams.Codec | ||
cstream::CStream | ||
level::Int | ||
end | ||
|
||
function Base.show(io::IO, codec::ZstdFrameCompressor) | ||
print(io, summary(codec), "(level=$(codec.level))") | ||
end | ||
|
||
# See compressor.jl for DEFAULT_COMPRESSION_LEVEL | ||
|
||
""" | ||
ZstdFrameCompressor(;level=$(DEFAULT_COMPRESSION_LEVEL)) | ||
|
||
Create a new zstd compression codec using the non-streaming API. | ||
This is uses `ZSTD_compress2`. This compressor expects to have the | ||
entire input buffer to be compressed available and stores the | ||
decompressed length in the frame header. | ||
|
||
Arguments | ||
--------- | ||
- `level`: compression level ($(MIN_CLEVEL)..$(MAX_CLEVEL)) | ||
""" | ||
function ZstdFrameCompressor(;level::Integer=DEFAULT_COMPRESSION_LEVEL) | ||
if !(MIN_CLEVEL ≤ level ≤ MAX_CLEVEL) | ||
throw(ArgumentError("level must be within $(MIN_CLEVEL)..$(MAX_CLEVEL)")) | ||
end | ||
return ZstdFrameCompressor(CStream(), level) | ||
end | ||
|
||
const ZstdFrameCompressorStream{S} = TranscodingStream{ZstdFrameCompressor,S} where S<:IO | ||
|
||
""" | ||
ZstdFrameCompressorStream(stream::IO; kwargs...) | ||
|
||
Create a new zstd compression stream (see `ZstdFrameCompressor` for `kwargs`). | ||
""" | ||
function ZstdFrameCompressorStream(stream::IO; kwargs...) | ||
x, y = splitkwargs(kwargs, (:level,)) | ||
return TranscodingStream(ZstdFrameCompressor(;x...), stream; y...) | ||
end | ||
|
||
|
||
# Methods | ||
# ------- | ||
|
||
function TranscodingStreams.initialize(codec::ZstdFrameCompressor) | ||
code = initialize!(codec.cstream, codec.level) | ||
if iserror(code) | ||
throw(ZstdError(code)) | ||
end | ||
return | ||
end | ||
|
||
function TranscodingStreams.finalize(codec::ZstdFrameCompressor) | ||
if codec.cstream.ptr != C_NULL | ||
code = free!(codec.cstream) | ||
if iserror(code) | ||
throw(ZstdError(code)) | ||
end | ||
codec.cstream.ptr = C_NULL | ||
end | ||
return | ||
end | ||
|
||
function TranscodingStreams.expectedsize(codec::ZstdFrameCompressor, input::Memory) | ||
code = compressed_size_bound(input.size) | ||
if iserror(code) | ||
throw(ZstdError(code)) | ||
end | ||
return Int(code) | ||
end | ||
|
||
function TranscodingStreams.startproc(codec::ZstdFrameCompressor, mode::Symbol, error::Error) | ||
code = reset!(codec.cstream, 0 #=unknown source size=#) | ||
if iserror(code) | ||
error[] = ZstdError(code) | ||
return :error | ||
end | ||
return :ok | ||
end | ||
|
||
function TranscodingStreams.process(codec::ZstdFrameCompressor, input::Memory, output::Memory, error::Error) | ||
cstream = codec.cstream | ||
cstream.ibuffer.src = input.ptr | ||
cstream.ibuffer.size = input.size | ||
cstream.ibuffer.pos = 0 | ||
cstream.obuffer.dst = output.ptr | ||
cstream.obuffer.size = output.size | ||
cstream.obuffer.pos = 0 | ||
code = frameCompress!(cstream) | ||
if iserror(code) | ||
error[] = ZstdError(code) | ||
return 0, 0, :error | ||
else | ||
return Int(input.size), Int(code), :end | ||
end | ||
end |
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,92 @@ | ||
# Decompressor Codec | ||
# ================== | ||
|
||
struct ZstdFrameDecompressor <: TranscodingStreams.Codec | ||
dstream::DStream | ||
end | ||
|
||
function Base.show(io::IO, codec::ZstdFrameDecompressor) | ||
print(io, summary(codec), "()") | ||
end | ||
|
||
""" | ||
ZstdFrameDecompressor() | ||
|
||
Create a new zstd decompression codec. | ||
This decompressor uses the non-streaming API, expecting a known length, via `ZSTD_decompressDCtx` | ||
""" | ||
function ZstdFrameDecompressor() | ||
return ZstdFrameDecompressor(DStream()) | ||
end | ||
|
||
const ZstdFrameDecompressorStream{S} = TranscodingStream{ZstdFrameDecompressor,S} where S<:IO | ||
|
||
""" | ||
ZstdFrameDecompressorStream(stream::IO; kwargs...) | ||
|
||
Create a new zstd decompression stream (`kwargs` are passed to `TranscodingStream`). | ||
""" | ||
function ZstdFrameDecompressorStream(stream::IO; kwargs...) | ||
return TranscodingStream(ZstdFrameDecompressor(), stream; kwargs...) | ||
end | ||
|
||
|
||
# Methods | ||
# ------- | ||
|
||
function TranscodingStreams.initialize(codec::ZstdFrameDecompressor) | ||
code = initialize!(codec.dstream) | ||
if iserror(code) | ||
throw(ZstdError(code)) | ||
end | ||
return | ||
end | ||
|
||
function TranscodingStreams.finalize(codec::ZstdFrameDecompressor) | ||
if codec.dstream.ptr != C_NULL | ||
code = free!(codec.dstream) | ||
if iserror(code) | ||
throw(ZstdError(code)) | ||
end | ||
codec.dstream.ptr = C_NULL | ||
end | ||
return | ||
end | ||
|
||
function TranscodingStreams.startproc(codec::ZstdFrameDecompressor, mode::Symbol, error::Error) | ||
code = reset!(codec.dstream) | ||
if iserror(code) | ||
error[] = ZstdError(code) | ||
return :error | ||
end | ||
return :ok | ||
end | ||
|
||
function TranscodingStreams.process(codec::ZstdFrameDecompressor, input::Memory, output::Memory, error::Error) | ||
dstream = codec.dstream | ||
dstream.ibuffer.src = input.ptr | ||
dstream.ibuffer.size = input.size | ||
dstream.ibuffer.pos = 0 | ||
dstream.obuffer.dst = output.ptr | ||
dstream.obuffer.size = output.size | ||
dstream.obuffer.pos = 0 | ||
code = frameDecompress!(dstream) | ||
if iserror(code) | ||
error[] = ZstdError(code) | ||
return 0, 0, :error | ||
else | ||
return Int(input.size), Int(code), :end | ||
end | ||
end | ||
|
||
function TranscodingStreams.expectedsize(codec::ZstdFrameDecompressor, input::Memory) | ||
ret = find_decompressed_size(input.ptr, input.size) | ||
if ret == ZSTD_CONTENTSIZE_ERROR | ||
throw(ZstdError()) | ||
elseif ret == ZSTD_CONTENTSIZE_UNKNOWN | ||
return Int(decompressed_size_bound(input.ptr, input.size)) | ||
else | ||
# exact size | ||
return Int(ret) | ||
end | ||
end |
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
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.
I don't think
process
can return:end
here. Generally when compressing,:end
is only returned ifinput
is empty and no extra output needs to be written for the current frame.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 are either going to return
:end
or:ok
here as in compression.jl:CodecZstd.jl/src/compression.jl
Line 96 in 4ba1be6
Here, unlike the streaming API, we consumed the entire input buffer and have written the entire output by invoking
ZSTD_compress2
. There is no continuation. There is no more input to process.By the completion of
ZSTD_compress2
there are two possible outcomes. We have either successfully compressed the data into the output buffer. How else would you describe the state afterZSTD_compress2
runs?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 caller of
process
is allowed to break up the input data becauseprocess
is a streaming API. Those small inputs could be appended to an internal buffer in the codec, and then only afterinput.size
is set to zero, signaling there are no more bytes in the frame,ZSTD_compress2
is called.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.
What would be the best way to detect additional bytes added and throw an error?
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 have to assume there will be additional bytes in the frame until
process
is called with input size zero.There should be a new
process2
similar toZSTD_compressStream2
with an additionalendOp
argument, to allow one-shot compression of a frame.