Merge branch 'dev' into doc

This commit is contained in:
Sean Purcell
2017-02-23 15:23:24 -08:00
29 changed files with 1722 additions and 106 deletions
+13 -5
View File
@@ -26,8 +26,7 @@ endif
default: lib zstd-release
.PHONY: all
all: allmost
CPPFLAGS=-I../lib LDFLAGS=-L../lib $(MAKE) -C examples/ $@
all: | allmost examples manual
.PHONY: allmost
allmost:
@@ -68,6 +67,14 @@ zlibwrapper:
test:
$(MAKE) -C $(TESTDIR) $@
.PHONY: examples
examples:
CPPFLAGS=-I../lib LDFLAGS=-L../lib $(MAKE) -C examples/ all
.PHONY: manual
manual:
$(MAKE) -C contrib/gen_html $@
.PHONY: clean
clean:
@$(MAKE) -C $(ZSTDDIR) $@ > $(VOID)
@@ -75,6 +82,7 @@ clean:
@$(MAKE) -C $(TESTDIR) $@ > $(VOID)
@$(MAKE) -C $(ZWRAPDIR) $@ > $(VOID)
@$(MAKE) -C examples/ $@ > $(VOID)
@$(MAKE) -C contrib/gen_html $@ > $(VOID)
@$(RM) zstd$(EXT) zstdmt$(EXT) tmp*
@echo Cleaning completed
@@ -197,16 +205,16 @@ cmaketest:
cd $(BUILDIR)/cmake/build ; cmake -DPREFIX:STRING=~/install_test_dir $(CMAKE_PARAMS) .. ; $(MAKE) install ; $(MAKE) uninstall
c90test: clean
CFLAGS="-std=c90" $(MAKE) all # will fail, due to // and long long
CFLAGS="-std=c90" $(MAKE) allmost # will fail, due to missing support for `long long`
gnu90test: clean
CFLAGS="-std=gnu90" $(MAKE) all
CFLAGS="-std=gnu90" $(MAKE) allmost
c99test: clean
CFLAGS="-std=c99" $(MAKE) allmost
gnu99test: clean
CFLAGS="-std=gnu99" $(MAKE) all
CFLAGS="-std=gnu99" $(MAKE) allmost
c11test: clean
CFLAGS="-std=c11" $(MAKE) allmost
+1 -1
View File
@@ -2,7 +2,7 @@ v1.1.4
cli : new : can compress in *.gz format, using --format=gzip command, by Przemyslaw Skibinski
cli : new : advanced benchmark command --priority=rt
cli : fix : write on sparse-enabled file systems in 32-bits mode, by @ds77
API : new : ZSTD_getFrameCompressedSize(), ZSTD_getFrameContentSize(), ZSTD_findDecompressedSize(), by Sean Purcell
API : new : ZSTD_findFrameCompressedSize(), ZSTD_getFrameContentSize(), ZSTD_findDecompressedSize(), by Sean Purcell
API : change : ZSTD_compress*() with srcSize==0 create an empty-frame of known size
build:new : meson build system in contrib/meson, by Dima Krasner
doc : new : educational decoder, by Sean Purcell
+1 -1
View File
@@ -12,7 +12,7 @@ dependencies:
if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C tests test-invalidDictionaries && make clean; fi
- |
if [[ "$CIRCLE_NODE_INDEX" == "0" ]]; then g++ -v; make gpptest && make clean; fi
if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C tests test-legacy && make clean; fi
if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C tests test-legacy test-decodecorpus && make clean; fi
- |
if [[ "$CIRCLE_NODE_INDEX" == "0" ]]; then gcc -v; make gnu90test && make clean; fi
if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C tests test-symbols && make clean; fi
+20 -5
View File
@@ -7,12 +7,18 @@
# of patent rights can be found in the PATENTS file in the same directory.
# ##########################################################################
CFLAGS ?= -O3
CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow -Wstrict-aliasing=1 -Wswitch-enum -Wno-comment
CFLAGS += $(MOREFLAGS)
FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS)
FLAGS = $(CPPFLAGS) $(CFLAGS) $(CXXFLAGS) $(LDFLAGS)
ZSTDAPI = ../../lib/zstd.h
ZSTDMANUAL = ../../doc/zstd_manual.html
LIBVER_MAJOR_SCRIPT:=`sed -n '/define ZSTD_VERSION_MAJOR/s/.*[[:blank:]]\([0-9][0-9]*\).*/\1/p' < $(ZSTDAPI)`
LIBVER_MINOR_SCRIPT:=`sed -n '/define ZSTD_VERSION_MINOR/s/.*[[:blank:]]\([0-9][0-9]*\).*/\1/p' < $(ZSTDAPI)`
LIBVER_PATCH_SCRIPT:=`sed -n '/define ZSTD_VERSION_RELEASE/s/.*[[:blank:]]\([0-9][0-9]*\).*/\1/p' < $(ZSTDAPI)`
LIBVER_SCRIPT:= $(LIBVER_MAJOR_SCRIPT).$(LIBVER_MINOR_SCRIPT).$(LIBVER_PATCH_SCRIPT)
LIBVER := $(shell echo $(LIBVER_SCRIPT))
# Define *.exe as extension for Windows systems
@@ -23,14 +29,23 @@ EXT =
endif
.PHONY: default gen_html
.PHONY: default
default: gen_html
.PHONY: all
all: manual
gen_html: gen_html.cpp
$(CXX) $(FLAGS) $^ -o $@$(EXT)
$(CXX) $(FLAGS) $^ -o $@$(EXT)
$(ZSTDMANUAL): gen_html $(ZSTDAPI)
echo "Update zstd manual in /doc"
./gen_html $(LIBVER) $(ZSTDAPI) $(ZSTDMANUAL)
.PHONY: manual
manual: gen_html $(ZSTDMANUAL)
.PHONY: clean
clean:
@$(RM) gen_html$(EXT)
@echo Cleaning completed
+10
View File
@@ -17,3 +17,13 @@ It also contains implementations of Huffman and FSE table decoding.
harness <input-file> <output-file> [dictionary]
As an additional resource to be used with this decoder,
see the `decodecorpus` tool in the [tests] directory.
It generates valid Zstandard frames that can be used to verify
a Zstandard decoder implementation.
Note that to use the tool to verify this decoder implementation,
the --content-size flag should be set,
as this decoder does not handle streaming decoding,
and so it must know the decompressed size in advance.
[tests]: https://github.com/facebook/zstd/blob/dev/tests/
+139 -53
View File
@@ -1,10 +1,10 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>zstd 1.1.2 Manual</title>
<title>zstd 1.1.4 Manual</title>
</head>
<body>
<h1>zstd 1.1.2 Manual</h1>
<h1>zstd 1.1.4 Manual</h1>
<hr>
<a name="Contents"></a><h2>Contents</h2>
<ol>
@@ -19,13 +19,15 @@
<li><a href="#Chapter9">Streaming decompression - HowTo</a></li>
<li><a href="#Chapter10">START OF ADVANCED AND EXPERIMENTAL FUNCTIONS</a></li>
<li><a href="#Chapter11">Advanced types</a></li>
<li><a href="#Chapter12">Advanced compression functions</a></li>
<li><a href="#Chapter13">Advanced decompression functions</a></li>
<li><a href="#Chapter14">Advanced streaming functions</a></li>
<li><a href="#Chapter15">Buffer-less and synchronous inner streaming functions</a></li>
<li><a href="#Chapter16">Buffer-less streaming compression (synchronous mode)</a></li>
<li><a href="#Chapter17">Buffer-less streaming decompression (synchronous mode)</a></li>
<li><a href="#Chapter18">Block functions</a></li>
<li><a href="#Chapter12">Compressed size functions</a></li>
<li><a href="#Chapter13">Decompressed size functions</a></li>
<li><a href="#Chapter14">Advanced compression functions</a></li>
<li><a href="#Chapter15">Advanced decompression functions</a></li>
<li><a href="#Chapter16">Advanced streaming functions</a></li>
<li><a href="#Chapter17">Buffer-less and synchronous inner streaming functions</a></li>
<li><a href="#Chapter18">Buffer-less streaming compression (synchronous mode)</a></li>
<li><a href="#Chapter19">Buffer-less streaming decompression (synchronous mode)</a></li>
<li><a href="#Chapter20">Block functions</a></li>
</ol>
<hr>
<a name="Chapter1"></a><h2>Introduction</h2><pre>
@@ -63,7 +65,7 @@
<pre><b>size_t ZSTD_decompress( void* dst, size_t dstCapacity,
const void* src, size_t compressedSize);
</b><p> `compressedSize` : must be the _exact_ size of a single compressed frame.
</b><p> `compressedSize` : must be the _exact_ size of some number of compressed and/or skippable frames.
`dstCapacity` is an upper bound of originalSize.
If user cannot imply a maximum upper bound, it's better to use streaming mode to decompress data.
@return : the number of bytes decompressed into `dst` (<= `dstCapacity`),
@@ -71,7 +73,16 @@
</p></pre><BR>
<pre><b>unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize);
</b><p> 'src' is the start of a zstd compressed frame.
</b><p> NOTE: This function is planned to be obsolete, in favour of ZSTD_getFrameContentSize.
ZSTD_getFrameContentSize functions the same way, returning the decompressed size of a single
frame, but distinguishes empty frames from frames with an unknown size, or errors.
Additionally, ZSTD_findDecompressedSize can be used instead. It can handle multiple
concatenated frames in one buffer, and so is more general.
As a result however, it requires more computation and entire frames to be passed to it,
as opposed to ZSTD_getFrameContentSize which requires only a single frame's header.
'src' is the start of a zstd compressed frame.
@return : content size to be decompressed, as a 64-bits value _if known_, 0 otherwise.
note 1 : decompressed size is an optional field, that may not be present, especially in streaming mode.
When `return==0`, data to decompress could be any size.
@@ -88,21 +99,29 @@
note 5 : when `return==0`, if precise failure cause is needed, use ZSTD_getFrameParams() to know more.
</p></pre><BR>
<h3>Helper functions</h3><pre><b>int ZSTD_maxCLevel(void); </b>/*!< maximum compression level available */<b>
<h3>Helper functions</h3><pre></pre><b><pre>int ZSTD_maxCLevel(void); </b>/*!< maximum compression level available */<b>
size_t ZSTD_compressBound(size_t srcSize); </b>/*!< maximum compressed size in worst case scenario */<b>
unsigned ZSTD_isError(size_t code); </b>/*!< tells if a `size_t` function result is an error code */<b>
const char* ZSTD_getErrorName(size_t code); </b>/*!< provides readable string from an error code */<b>
</b></pre><BR>
</pre></b><BR>
<a name="Chapter4"></a><h2>Explicit memory management</h2><pre></pre>
<h3>Compression context</h3><pre> When compressing many times,
it is recommended to allocate a context just once, and re-use it for each successive compression operation.
This will make workload friendlier for system's memory.
Use one context per thread for parallel execution in multi-threaded environments.
</pre><b><pre>typedef struct ZSTD_CCtx_s ZSTD_CCtx;
ZSTD_CCtx* ZSTD_createCCtx(void);
size_t ZSTD_freeCCtx(ZSTD_CCtx* cctx);
</pre></b><BR>
<pre><b>size_t ZSTD_compressCCtx(ZSTD_CCtx* ctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel);
</b><p> Same as ZSTD_compress(), requires an allocated ZSTD_CCtx (see ZSTD_createCCtx()).
</p></pre><BR>
<h3>Decompression context</h3><pre><b>typedef struct ZSTD_DCtx_s ZSTD_DCtx;
<h3>Decompression context</h3><pre></pre><b><pre>typedef struct ZSTD_DCtx_s ZSTD_DCtx;
ZSTD_DCtx* ZSTD_createDCtx(void);
size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx);
</b></pre><BR>
</pre></b><BR>
<pre><b>size_t ZSTD_decompressDCtx(ZSTD_DCtx* ctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
</b><p> Same as ZSTD_decompress(), requires an allocated ZSTD_DCtx (see ZSTD_createDCtx()).
</p></pre><BR>
@@ -131,11 +150,11 @@ size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx);
<a name="Chapter6"></a><h2>Fast dictionary API</h2><pre></pre>
<pre><b>ZSTD_CDict* ZSTD_createCDict(const void* dict, size_t dictSize, int compressionLevel);
<pre><b>ZSTD_CDict* ZSTD_createCDict(const void* dictBuffer, size_t dictSize, int compressionLevel);
</b><p> When compressing multiple messages / blocks with the same dictionary, it's recommended to load it just once.
ZSTD_createCDict() will create a digested dictionary, ready to start future compression operations without startup delay.
ZSTD_CDict can be created once and used by multiple threads concurrently, as its usage is read-only.
`dict` can be released after ZSTD_CDict creation.
`dictBuffer` can be released after ZSTD_CDict creation, as its content is copied within CDict
</p></pre><BR>
<pre><b>size_t ZSTD_freeCDict(ZSTD_CDict* CDict);
@@ -151,9 +170,9 @@ size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx);
Note that compression level is decided during dictionary creation.
</p></pre><BR>
<pre><b>ZSTD_DDict* ZSTD_createDDict(const void* dict, size_t dictSize);
<pre><b>ZSTD_DDict* ZSTD_createDDict(const void* dictBuffer, size_t dictSize);
</b><p> Create a digested dictionary, ready to start decompression operation without startup delay.
`dict` can be released after creation.
dictBuffer can be released after DDict creation, as its content is copied inside DDict
</p></pre><BR>
<pre><b>size_t ZSTD_freeDDict(ZSTD_DDict* ddict);
@@ -271,9 +290,9 @@ size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx);
} ZSTD_compressionParameters;
</b></pre><BR>
<pre><b>typedef struct {
unsigned contentSizeFlag; </b>/**< 1: content size will be in frame header (if known). */<b>
unsigned checksumFlag; </b>/**< 1: will generate a 22-bits checksum at end of frame, to be used for error detection by decompressor */<b>
unsigned noDictIDFlag; </b>/**< 1: no dict ID will be saved into frame header (if dictionary compression) */<b>
unsigned contentSizeFlag; </b>/**< 1: content size will be in frame header (when known) */<b>
unsigned checksumFlag; </b>/**< 1: generate a 32-bits checksum at end of frame, for error detection */<b>
unsigned noDictIDFlag; </b>/**< 1: no dictID will be saved into frame header (if dictionary compression) */<b>
} ZSTD_frameParameters;
</b></pre><BR>
<pre><b>typedef struct {
@@ -281,11 +300,56 @@ size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx);
ZSTD_frameParameters fParams;
} ZSTD_parameters;
</b></pre><BR>
<h3>Custom memory allocation functions</h3><pre><b>typedef void* (*ZSTD_allocFunction) (void* opaque, size_t size);
<h3>Custom memory allocation functions</h3><pre></pre><b><pre>typedef void* (*ZSTD_allocFunction) (void* opaque, size_t size);
typedef void (*ZSTD_freeFunction) (void* opaque, void* address);
typedef struct { ZSTD_allocFunction customAlloc; ZSTD_freeFunction customFree; void* opaque; } ZSTD_customMem;
</b></pre><BR>
<a name="Chapter12"></a><h2>Advanced compression functions</h2><pre></pre>
</pre></b><BR>
<a name="Chapter12"></a><h2>Compressed size functions</h2><pre></pre>
<pre><b>size_t ZSTD_getFrameCompressedSize(const void* src, size_t srcSize);
</b><p> `src` should point to the start of a ZSTD encoded frame
`srcSize` must be at least as large as the frame
@return : the compressed size of the frame pointed to by `src`, suitable to pass to
`ZSTD_decompress` or similar, or an error code if given invalid input.
</p></pre><BR>
<a name="Chapter13"></a><h2>Decompressed size functions</h2><pre></pre>
<pre><b>unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize);
</b><p> `src` should point to the start of a ZSTD encoded frame
`srcSize` must be at least as large as the frame header. A value greater than or equal
to `ZSTD_frameHeaderSize_max` is guaranteed to be large enough in all cases.
@return : decompressed size of the frame pointed to be `src` if known, otherwise
- ZSTD_CONTENTSIZE_UNKNOWN if the size cannot be determined
- ZSTD_CONTENTSIZE_ERROR if an error occured (e.g. invalid magic number, srcSize too small)
</p></pre><BR>
<pre><b>unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize);
</b><p> `src` should point the start of a series of ZSTD encoded and/or skippable frames
`srcSize` must be the _exact_ size of this series
(i.e. there should be a frame boundary exactly `srcSize` bytes after `src`)
@return : the decompressed size of all data in the contained frames, as a 64-bit value _if known_
- if the decompressed size cannot be determined: ZSTD_CONTENTSIZE_UNKNOWN
- if an error occurred: ZSTD_CONTENTSIZE_ERROR
note 1 : decompressed size is an optional field, that may not be present, especially in streaming mode.
When `return==ZSTD_CONTENTSIZE_UNKNOWN`, data to decompress could be any size.
In which case, it's necessary to use streaming mode to decompress data.
Optionally, application can still use ZSTD_decompress() while relying on implied limits.
(For example, data may be necessarily cut into blocks <= 16 KB).
note 2 : decompressed size is always present when compression is done with ZSTD_compress()
note 3 : decompressed size can be very large (64-bits value),
potentially larger than what local system can handle as a single memory segment.
In which case, it's necessary to use streaming mode to decompress data.
note 4 : If source is untrusted, decompressed size could be wrong or intentionally modified.
Always ensure result fits within application's authorized limits.
Each application can set its own limits.
note 5 : ZSTD_findDecompressedSize handles multiple frames, and so it must traverse the input to
read each contained frame header. This is efficient as most of the data is skipped,
however it does mean that all frame data must be present and valid.
</p></pre><BR>
<a name="Chapter14"></a><h2>Advanced compression functions</h2><pre></pre>
<pre><b>size_t ZSTD_estimateCCtxSize(ZSTD_compressionParameters cParams);
</b><p> Gives the amount of memory allocated for a ZSTD_CCtx given a set of compression parameters.
@@ -300,7 +364,22 @@ typedef struct { ZSTD_allocFunction customAlloc; ZSTD_freeFunction customFree; v
</b><p> Gives the amount of memory used by a given ZSTD_CCtx
</p></pre><BR>
<pre><b>ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictSize,
<pre><b>typedef enum {
ZSTD_p_forceWindow </b>/* Force back-references to remain < windowSize, even when referencing Dictionary content (default:0)*/<b>
} ZSTD_CCtxParameter;
</b></pre><BR>
<pre><b>size_t ZSTD_setCCtxParameter(ZSTD_CCtx* cctx, ZSTD_CCtxParameter param, unsigned value);
</b><p> Set advanced parameters, selected through enum ZSTD_CCtxParameter
@result : 0, or an error code (which can be tested with ZSTD_isError())
</p></pre><BR>
<pre><b>ZSTD_CDict* ZSTD_createCDict_byReference(const void* dictBuffer, size_t dictSize, int compressionLevel);
</b><p> Create a digested dictionary for compression
Dictionary content is simply referenced, and therefore stays in dictBuffer.
It is important that dictBuffer outlives CDict, it must remain read accessible throughout the lifetime of CDict
</p></pre><BR>
<pre><b>ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictSize, unsigned byReference,
ZSTD_parameters params, ZSTD_customMem customMem);
</b><p> Create a ZSTD_CDict using external alloc and free, and customized compression parameters
</p></pre><BR>
@@ -336,7 +415,7 @@ typedef struct { ZSTD_allocFunction customAlloc; ZSTD_freeFunction customFree; v
</b><p> Same as ZSTD_compress_usingDict(), with fine-tune control of each compression parameter
</p></pre><BR>
<a name="Chapter13"></a><h2>Advanced decompression functions</h2><pre></pre>
<a name="Chapter15"></a><h2>Advanced decompression functions</h2><pre></pre>
<pre><b>unsigned ZSTD_isFrame(const void* buffer, size_t size);
</b><p> Tells if the content of `buffer` starts with a valid Frame Identifier.
@@ -357,6 +436,12 @@ typedef struct { ZSTD_allocFunction customAlloc; ZSTD_freeFunction customFree; v
</b><p> Gives the amount of memory used by a given ZSTD_DCtx
</p></pre><BR>
<pre><b>ZSTD_DDict* ZSTD_createDDict_byReference(const void* dictBuffer, size_t dictSize);
</b><p> Create a digested dictionary, ready to start decompression operation without startup delay.
Dictionary content is simply referenced, and therefore stays in dictBuffer.
It is important that dictBuffer outlives DDict, it must remain read accessible throughout the lifetime of DDict
</p></pre><BR>
<pre><b>size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict);
</b><p> Gives the amount of memory used by a given ZSTD_DDict
</p></pre><BR>
@@ -385,33 +470,33 @@ typedef struct { ZSTD_allocFunction customAlloc; ZSTD_freeFunction customFree; v
When identifying the exact failure cause, it's possible to used ZSTD_getFrameParams(), which will provide a more precise error code.
</p></pre><BR>
<a name="Chapter14"></a><h2>Advanced streaming functions</h2><pre></pre>
<a name="Chapter16"></a><h2>Advanced streaming functions</h2><pre></pre>
<h3>Advanced Streaming compression functions</h3><pre><b>ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem);
size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize); </b>/**< pledgedSrcSize must be correct */<b>
size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel);
<h3>Advanced Streaming compression functions</h3><pre></pre><b><pre>ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem);
size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize); </b>/**< pledgedSrcSize must be correct, a size of 0 means unknown. for a frame size of 0 use initCStream_advanced */<b>
size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); </b>/**< note: a dict will not be used if dict == NULL or dictSize < 8 */<b>
size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize,
ZSTD_parameters params, unsigned long long pledgedSrcSize); </b>/**< pledgedSrcSize is optional and can be zero == unknown */<b>
ZSTD_parameters params, unsigned long long pledgedSrcSize); </b>/**< pledgedSrcSize is optional and can be 0 (meaning unknown). note: if the contentSizeFlag is set, pledgedSrcSize == 0 means the source size is actually 0 */<b>
size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict); </b>/**< note : cdict will just be referenced, and must outlive compression session */<b>
size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize); </b>/**< re-use compression parameters from previous init; skip dictionary loading stage; zcs must be init at least once before */<b>
size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize); </b>/**< re-use compression parameters from previous init; skip dictionary loading stage; zcs must be init at least once before. note: pledgedSrcSize must be correct, a size of 0 means unknown. for a frame size of 0 use initCStream_advanced */<b>
size_t ZSTD_sizeof_CStream(const ZSTD_CStream* zcs);
</b></pre><BR>
<h3>Advanced Streaming decompression functions</h3><pre><b>typedef enum { ZSTDdsp_maxWindowSize } ZSTD_DStreamParameter_e;
</pre></b><BR>
<h3>Advanced Streaming decompression functions</h3><pre></pre><b><pre>typedef enum { DStream_p_maxWindowSize } ZSTD_DStreamParameter_e;
ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem);
size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize);
size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize); </b>/**< note: a dict will not be used if dict == NULL or dictSize < 8 */<b>
size_t ZSTD_setDStreamParameter(ZSTD_DStream* zds, ZSTD_DStreamParameter_e paramType, unsigned paramValue);
size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const ZSTD_DDict* ddict); </b>/**< note : ddict will just be referenced, and must outlive decompression session */<b>
size_t ZSTD_resetDStream(ZSTD_DStream* zds); </b>/**< re-use decompression parameters from previous init; saves dictionary loading */<b>
size_t ZSTD_sizeof_DStream(const ZSTD_DStream* zds);
</b></pre><BR>
<a name="Chapter15"></a><h2>Buffer-less and synchronous inner streaming functions</h2><pre>
</pre></b><BR>
<a name="Chapter17"></a><h2>Buffer-less and synchronous inner streaming functions</h2><pre>
This is an advanced API, giving full control over buffer management, for users which need direct control over memory.
But it's also a complex one, with many restrictions (documented below).
Prefer using normal streaming API for an easier experience
<BR></pre>
<a name="Chapter16"></a><h2>Buffer-less streaming compression (synchronous mode)</h2><pre>
<a name="Chapter18"></a><h2>Buffer-less streaming compression (synchronous mode)</h2><pre>
A ZSTD_CCtx object is required to track streaming operations.
Use ZSTD_createCCtx() / ZSTD_freeCCtx() to manage resource.
ZSTD_CCtx object can be re-used multiple times within successive compression operations.
@@ -434,20 +519,21 @@ size_t ZSTD_sizeof_DStream(const ZSTD_DStream* zds);
In which case, it will "discard" the relevant memory section from its history.
Finish a frame with ZSTD_compressEnd(), which will write the last block(s) and optional checksum.
It's possible to use a NULL,0 src content, in which case, it will write a final empty block to end the frame,
Without last block mark, frames will be considered unfinished (broken) by decoders.
It's possible to use srcSize==0, in which case, it will write a final empty block to end the frame.
Without last block mark, frames will be considered unfinished (corrupted) by decoders.
You can then reuse `ZSTD_CCtx` (ZSTD_compressBegin()) to compress some new frame.
`ZSTD_CCtx` object can be re-used (ZSTD_compressBegin()) to compress some new frame.
<BR></pre>
<h3>Buffer-less streaming compression functions</h3><pre><b>size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel);
<h3>Buffer-less streaming compression functions</h3><pre></pre><b><pre>size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel);
size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel);
size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize);
size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize);
size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize); </b>/**< pledgedSrcSize is optional and can be 0 (meaning unknown). note: if the contentSizeFlag is set, pledgedSrcSize == 0 means the source size is actually 0 */<b>
size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); </b>/**< note: if pledgedSrcSize can be 0, indicating unknown size. if it is non-zero, it must be accurate. for 0 size frames, use compressBegin_advanced */<b>
size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict, unsigned long long pledgedSrcSize); </b>/**< note: if pledgedSrcSize can be 0, indicating unknown size. if it is non-zero, it must be accurate. for 0 size frames, use compressBegin_advanced */<b>
size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
</b></pre><BR>
<a name="Chapter17"></a><h2>Buffer-less streaming decompression (synchronous mode)</h2><pre>
</pre></b><BR>
<a name="Chapter19"></a><h2>Buffer-less streaming decompression (synchronous mode)</h2><pre>
A ZSTD_DCtx object is required to track streaming operations.
Use ZSTD_createDCtx() / ZSTD_freeDCtx() to manage it.
A ZSTD_DCtx object can be re-used multiple times.
@@ -490,7 +576,7 @@ size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const vo
Note : it's possible to know if next input to present is a header or a block, using ZSTD_nextInputType().
This information is not required to properly decode a frame.
== Special case : skippable frames ==
== Special case : skippable frames
Skippable frames allow integration of user-defined data into a flow of concatenated frames.
Skippable frames will be ignored (skipped) by a decompressor. The format of skippable frames is as follows :
@@ -509,7 +595,7 @@ size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const vo
unsigned checksumFlag;
} ZSTD_frameParams;
</b></pre><BR>
<h3>Buffer-less streaming decompression functions</h3><pre><b>size_t ZSTD_getFrameParams(ZSTD_frameParams* fparamsPtr, const void* src, size_t srcSize); </b>/**< doesn't consume input, see details below */<b>
<h3>Buffer-less streaming decompression functions</h3><pre></pre><b><pre>size_t ZSTD_getFrameParams(ZSTD_frameParams* fparamsPtr, const void* src, size_t srcSize); </b>/**< doesn't consume input, see details below */<b>
size_t ZSTD_decompressBegin(ZSTD_DCtx* dctx);
size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);
void ZSTD_copyDCtx(ZSTD_DCtx* dctx, const ZSTD_DCtx* preparedDCtx);
@@ -517,8 +603,8 @@ size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx);
size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
typedef enum { ZSTDnit_frameHeader, ZSTDnit_blockHeader, ZSTDnit_block, ZSTDnit_lastBlock, ZSTDnit_checksum, ZSTDnit_skippableFrame } ZSTD_nextInputType_e;
ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx);
</b></pre><BR>
<a name="Chapter18"></a><h2>Block functions</h2><pre>
</pre></b><BR>
<a name="Chapter20"></a><h2>Block functions</h2><pre>
Block functions produce and decode raw zstd blocks, without frame metadata.
Frame metadata cost is typically ~18 bytes, which can be non-negligible for very small blocks (< 100 bytes).
User will have to take in charge required information to regenerate data, such as compressed and content sizes.
@@ -542,10 +628,10 @@ ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx);
Use ZSTD_insertBlock() in such a case.
<BR></pre>
<h3>Raw zstd block functions</h3><pre><b>size_t ZSTD_getBlockSizeMax(ZSTD_CCtx* cctx);
<h3>Raw zstd block functions</h3><pre></pre><b><pre>size_t ZSTD_getBlockSizeMax(ZSTD_CCtx* cctx);
size_t ZSTD_compressBlock (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
size_t ZSTD_insertBlock(ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize); </b>/**< insert block into `dctx` history. Useful for uncompressed blocks */<b>
</b></pre><BR>
</pre></b><BR>
</html>
</body>
+10 -7
View File
@@ -369,7 +369,7 @@ unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize)
totalDstSize += ret;
}
{
size_t const frameSrcSize = ZSTD_getFrameCompressedSize(src, srcSize);
size_t const frameSrcSize = ZSTD_findFrameCompressedSize(src, srcSize);
if (ZSTD_isError(frameSrcSize)) {
return ZSTD_CONTENTSIZE_ERROR;
}
@@ -1437,17 +1437,20 @@ size_t ZSTD_generateNxBytes(void* dst, size_t dstCapacity, BYTE byte, size_t len
return length;
}
/** ZSTD_getFrameCompressedSize() :
/** ZSTD_findFrameCompressedSize() :
* compatible with legacy mode
* `src` must point to the start of a ZSTD or ZSTD legacy frame
* `src` must point to the start of a ZSTD frame, ZSTD legacy frame, or skippable frame
* `srcSize` must be at least as large as the frame contained
* @return : the compressed size of the frame starting at `src` */
size_t ZSTD_getFrameCompressedSize(const void *src, size_t srcSize)
size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize)
{
#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT==1)
if (ZSTD_isLegacy(src, srcSize)) return ZSTD_getFrameCompressedSizeLegacy(src, srcSize);
if (ZSTD_isLegacy(src, srcSize)) return ZSTD_findFrameCompressedSizeLegacy(src, srcSize);
#endif
{
if (srcSize >= ZSTD_skippableHeaderSize &&
(MEM_readLE32(src) & 0xFFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) {
return ZSTD_skippableHeaderSize + MEM_readLE32((const BYTE*)src + 4);
} else {
const BYTE* ip = (const BYTE*)src;
const BYTE* const ipstart = ip;
size_t remainingSize = srcSize;
@@ -1576,7 +1579,7 @@ static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx,
#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
if (ZSTD_isLegacy(src, srcSize)) {
size_t const frameSize = ZSTD_getFrameCompressedSizeLegacy(src, srcSize);
size_t const frameSize = ZSTD_findFrameCompressedSizeLegacy(src, srcSize);
size_t decodedSize;
if (ZSTD_isError(frameSize)) return frameSize;
+8 -8
View File
@@ -123,26 +123,26 @@ MEM_STATIC size_t ZSTD_decompressLegacy(
}
}
MEM_STATIC size_t ZSTD_getFrameCompressedSizeLegacy(const void *src,
MEM_STATIC size_t ZSTD_findFrameCompressedSizeLegacy(const void *src,
size_t compressedSize)
{
U32 const version = ZSTD_isLegacy(src, compressedSize);
switch(version)
{
case 1 :
return ZSTDv01_getFrameCompressedSize(src, compressedSize);
return ZSTDv01_findFrameCompressedSize(src, compressedSize);
case 2 :
return ZSTDv02_getFrameCompressedSize(src, compressedSize);
return ZSTDv02_findFrameCompressedSize(src, compressedSize);
case 3 :
return ZSTDv03_getFrameCompressedSize(src, compressedSize);
return ZSTDv03_findFrameCompressedSize(src, compressedSize);
case 4 :
return ZSTDv04_getFrameCompressedSize(src, compressedSize);
return ZSTDv04_findFrameCompressedSize(src, compressedSize);
case 5 :
return ZSTDv05_getFrameCompressedSize(src, compressedSize);
return ZSTDv05_findFrameCompressedSize(src, compressedSize);
case 6 :
return ZSTDv06_getFrameCompressedSize(src, compressedSize);
return ZSTDv06_findFrameCompressedSize(src, compressedSize);
case 7 :
return ZSTDv07_getFrameCompressedSize(src, compressedSize);
return ZSTDv07_findFrameCompressedSize(src, compressedSize);
default :
return ERROR(prefix_unknown);
}
+1 -1
View File
@@ -1992,7 +1992,7 @@ size_t ZSTDv01_decompress(void* dst, size_t maxDstSize, const void* src, size_t
return ZSTDv01_decompressDCtx(&ctx, dst, maxDstSize, src, srcSize);
}
size_t ZSTDv01_getFrameCompressedSize(const void* src, size_t srcSize)
size_t ZSTDv01_findFrameCompressedSize(const void* src, size_t srcSize)
{
const BYTE* ip = (const BYTE*)src;
size_t remainingSize = srcSize;
+1 -1
View File
@@ -40,7 +40,7 @@ ZSTDv01_getFrameSrcSize() : get the source length of a ZSTD frame compliant with
return : the number of bytes that would be read to decompress this frame
or an errorCode if it fails (which can be tested using ZSTDv01_isError())
*/
size_t ZSTDv01_getFrameCompressedSize(const void* src, size_t compressedSize);
size_t ZSTDv01_findFrameCompressedSize(const void* src, size_t compressedSize);
/**
ZSTDv01_isError() : tells if the result of ZSTDv01_decompress() is an error
+3 -3
View File
@@ -3378,7 +3378,7 @@ static size_t ZSTD_decompress(void* dst, size_t maxDstSize, const void* src, siz
return ZSTD_decompressDCtx(&ctx, dst, maxDstSize, src, srcSize);
}
static size_t ZSTD_getFrameCompressedSize(const void *src, size_t srcSize)
static size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize)
{
const BYTE* ip = (const BYTE*)src;
@@ -3524,9 +3524,9 @@ size_t ZSTDv02_decompress( void* dst, size_t maxOriginalSize,
return ZSTD_decompress(dst, maxOriginalSize, src, compressedSize);
}
size_t ZSTDv02_getFrameCompressedSize(const void *src, size_t compressedSize)
size_t ZSTDv02_findFrameCompressedSize(const void *src, size_t compressedSize)
{
return ZSTD_getFrameCompressedSize(src, compressedSize);
return ZSTD_findFrameCompressedSize(src, compressedSize);
}
ZSTDv02_Dctx* ZSTDv02_createDCtx(void)
+1 -1
View File
@@ -40,7 +40,7 @@ ZSTDv02_getFrameSrcSize() : get the source length of a ZSTD frame compliant with
return : the number of bytes that would be read to decompress this frame
or an errorCode if it fails (which can be tested using ZSTDv02_isError())
*/
size_t ZSTDv02_getFrameCompressedSize(const void* src, size_t compressedSize);
size_t ZSTDv02_findFrameCompressedSize(const void* src, size_t compressedSize);
/**
ZSTDv02_isError() : tells if the result of ZSTDv02_decompress() is an error
+3 -3
View File
@@ -3019,7 +3019,7 @@ static size_t ZSTD_decompress(void* dst, size_t maxDstSize, const void* src, siz
return ZSTD_decompressDCtx(&ctx, dst, maxDstSize, src, srcSize);
}
static size_t ZSTD_getFrameCompressedSize(const void* src, size_t srcSize)
static size_t ZSTD_findFrameCompressedSize(const void* src, size_t srcSize)
{
const BYTE* ip = (const BYTE*)src;
size_t remainingSize = srcSize;
@@ -3165,9 +3165,9 @@ size_t ZSTDv03_decompress( void* dst, size_t maxOriginalSize,
return ZSTD_decompress(dst, maxOriginalSize, src, compressedSize);
}
size_t ZSTDv03_getFrameCompressedSize(const void* src, size_t srcSize)
size_t ZSTDv03_findFrameCompressedSize(const void* src, size_t srcSize)
{
return ZSTD_getFrameCompressedSize(src, srcSize);
return ZSTD_findFrameCompressedSize(src, srcSize);
}
ZSTDv03_Dctx* ZSTDv03_createDCtx(void)
+1 -1
View File
@@ -40,7 +40,7 @@ ZSTDv03_getFrameSrcSize() : get the source length of a ZSTD frame compliant with
return : the number of bytes that would be read to decompress this frame
or an errorCode if it fails (which can be tested using ZSTDv03_isError())
*/
size_t ZSTDv03_getFrameCompressedSize(const void* src, size_t compressedSize);
size_t ZSTDv03_findFrameCompressedSize(const void* src, size_t compressedSize);
/**
ZSTDv03_isError() : tells if the result of ZSTDv03_decompress() is an error
+3 -3
View File
@@ -3326,7 +3326,7 @@ static size_t ZSTD_decompress_usingDict(ZSTD_DCtx* ctx,
return op-ostart;
}
static size_t ZSTD_getFrameCompressedSize(const void* src, size_t srcSize)
static size_t ZSTD_findFrameCompressedSize(const void* src, size_t srcSize)
{
const BYTE* ip = (const BYTE*)src;
size_t remainingSize = srcSize;
@@ -3782,9 +3782,9 @@ size_t ZSTDv04_decompress(void* dst, size_t maxDstSize, const void* src, size_t
#endif
}
size_t ZSTDv04_getFrameCompressedSize(const void* src, size_t srcSize)
size_t ZSTDv04_findFrameCompressedSize(const void* src, size_t srcSize)
{
return ZSTD_getFrameCompressedSize(src, srcSize);
return ZSTD_findFrameCompressedSize(src, srcSize);
}
size_t ZSTDv04_resetDCtx(ZSTDv04_Dctx* dctx) { return ZSTD_resetDCtx(dctx); }
+1 -1
View File
@@ -40,7 +40,7 @@ ZSTDv04_getFrameSrcSize() : get the source length of a ZSTD frame compliant with
return : the number of bytes that would be read to decompress this frame
or an errorCode if it fails (which can be tested using ZSTDv04_isError())
*/
size_t ZSTDv04_getFrameCompressedSize(const void* src, size_t compressedSize);
size_t ZSTDv04_findFrameCompressedSize(const void* src, size_t compressedSize);
/**
ZSTDv04_isError() : tells if the result of ZSTDv04_decompress() is an error
+1 -1
View File
@@ -3583,7 +3583,7 @@ size_t ZSTDv05_decompress(void* dst, size_t maxDstSize, const void* src, size_t
#endif
}
size_t ZSTDv05_getFrameCompressedSize(const void *src, size_t srcSize)
size_t ZSTDv05_findFrameCompressedSize(const void *src, size_t srcSize)
{
const BYTE* ip = (const BYTE*)src;
size_t remainingSize = srcSize;
+1 -1
View File
@@ -38,7 +38,7 @@ ZSTDv05_getFrameSrcSize() : get the source length of a ZSTD frame
return : the number of bytes that would be read to decompress this frame
or an errorCode if it fails (which can be tested using ZSTDv05_isError())
*/
size_t ZSTDv05_getFrameCompressedSize(const void* src, size_t compressedSize);
size_t ZSTDv05_findFrameCompressedSize(const void* src, size_t compressedSize);
/* *************************************
* Helper functions
+1 -1
View File
@@ -3729,7 +3729,7 @@ size_t ZSTDv06_decompress(void* dst, size_t dstCapacity, const void* src, size_t
#endif
}
size_t ZSTDv06_getFrameCompressedSize(const void* src, size_t srcSize)
size_t ZSTDv06_findFrameCompressedSize(const void* src, size_t srcSize)
{
const BYTE* ip = (const BYTE*)src;
size_t remainingSize = srcSize;
+1 -1
View File
@@ -47,7 +47,7 @@ ZSTDv06_getFrameSrcSize() : get the source length of a ZSTD frame
return : the number of bytes that would be read to decompress this frame
or an errorCode if it fails (which can be tested using ZSTDv06_isError())
*/
size_t ZSTDv06_getFrameCompressedSize(const void* src, size_t compressedSize);
size_t ZSTDv06_findFrameCompressedSize(const void* src, size_t compressedSize);
/* *************************************
* Helper functions
+1 -1
View File
@@ -3968,7 +3968,7 @@ size_t ZSTDv07_decompress(void* dst, size_t dstCapacity, const void* src, size_t
#endif
}
size_t ZSTDv07_getFrameCompressedSize(const void* src, size_t srcSize)
size_t ZSTDv07_findFrameCompressedSize(const void* src, size_t srcSize)
{
const BYTE* ip = (const BYTE*)src;
size_t remainingSize = srcSize;
+1 -1
View File
@@ -54,7 +54,7 @@ ZSTDv07_getFrameSrcSize() : get the source length of a ZSTD frame
return : the number of bytes that would be read to decompress this frame
or an errorCode if it fails (which can be tested using ZSTDv07_isError())
*/
size_t ZSTDv07_getFrameCompressedSize(const void* src, size_t compressedSize);
size_t ZSTDv07_findFrameCompressedSize(const void* src, size_t compressedSize);
/*====== Helper functions ======*/
ZSTDLIBv07_API unsigned ZSTDv07_isError(size_t code); /*!< tells if a `size_t` function result is an error code */
+6 -3
View File
@@ -400,12 +400,12 @@ typedef struct { ZSTD_allocFunction customAlloc; ZSTD_freeFunction customFree; v
* Compressed size functions
***************************************/
/*! ZSTD_getFrameCompressedSize() :
* `src` should point to the start of a ZSTD encoded frame
/*! ZSTD_findFrameCompressedSize() :
* `src` should point to the start of a ZSTD encoded frame or skippable frame
* `srcSize` must be at least as large as the frame
* @return : the compressed size of the frame pointed to by `src`, suitable to pass to
* `ZSTD_decompress` or similar, or an error code if given invalid input. */
ZSTDLIB_API size_t ZSTD_getFrameCompressedSize(const void* src, size_t srcSize);
ZSTDLIB_API size_t ZSTD_findFrameCompressedSize(const void* src, size_t srcSize);
/***************************************
* Decompressed size functions
@@ -700,6 +700,9 @@ ZSTDLIB_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapaci
c) Frame Content - any content (User Data) of length equal to Frame Size
For skippable frames ZSTD_decompressContinue() always returns 0.
For skippable frames ZSTD_getFrameParams() returns fparamsPtr->windowLog==0 what means that a frame is skippable.
Note : If fparamsPtr->frameContentSize==0, it is ambiguous: the frame might actually be a Zstd encoded frame with no content.
For purposes of decompression, it is valid in both cases to skip the frame using
ZSTD_findFrameCompressedSize to find its size in bytes.
It also returns Frame Size as fparamsPtr->frameContentSize.
*/
+1
View File
@@ -17,6 +17,7 @@ roundTripCrash
longmatch
symbols
legacy
decodecorpus
pool
invalidDictionaries
+10 -2
View File
@@ -56,6 +56,7 @@ VOID = /dev/null
ZSTREAM_TESTTIME = -T2mn
FUZZERTEST ?= -T5mn
ZSTDRTTEST = --test-large-data
DECODECORPUS_TESTTIME = -T30
.PHONY: default all all32 dll clean test test32 test-all namespaceTest versionsTest
@@ -154,6 +155,9 @@ legacy : CPPFLAGS+= -I$(ZSTDDIR)/legacy
legacy : $(ZSTD_FILES) $(wildcard $(ZSTDDIR)/legacy/*.c) legacy.c
$(CC) $(FLAGS) $^ -o $@$(EXT)
decodecorpus : $(filter-out $(ZSTDDIR)/compress/zstd_compress.c, $(wildcard $(ZSTD_FILES))) decodecorpus.c
$(CC) $(FLAGS) $^ -o $@$(EXT) -lm
symbols : symbols.c
$(MAKE) -C $(ZSTDDIR) libzstd
ifneq (,$(filter Windows%,$(OS)))
@@ -184,7 +188,8 @@ clean:
fuzzer-dll$(EXT) zstreamtest-dll$(EXT) zbufftest-dll$(EXT)\
zstreamtest$(EXT) zstreamtest32$(EXT) \
datagen$(EXT) paramgrill$(EXT) roundTripCrash$(EXT) longmatch$(EXT) \
symbols$(EXT) invalidDictionaries$(EXT) legacy$(EXT) pool$(EXT)
symbols$(EXT) invalidDictionaries$(EXT) legacy$(EXT) pool$(EXT) \
decodecorpus$(EXT)
@echo Cleaning completed
@@ -230,7 +235,7 @@ zstd-playTests: datagen
file $(ZSTD)
ZSTD="$(QEMU_SYS) $(ZSTD)" ./playTests.sh $(ZSTDRTTEST)
test: test-zstd test-fullbench test-fuzzer test-zstream test-invalidDictionaries test-legacy
test: test-zstd test-fullbench test-fuzzer test-zstream test-invalidDictionaries test-legacy test-decodecorpus
ifeq ($(QEMU_SYS),)
test: test-pool
endif
@@ -302,6 +307,9 @@ test-symbols: symbols
test-legacy: legacy
$(QEMU_SYS) ./legacy
test-decodecorpus: decodecorpus
$(QEMU_SYS) ./decodecorpus -t $(DECODECORPUS_TESTTIME)
test-pool: pool
$(QEMU_SYS) ./pool
+25 -1
View File
@@ -10,12 +10,14 @@ This directory contains the following programs and scripts:
- `test-zstd-versions.py` : compatibility test between zstd versions stored on Github (v0.1+)
- `zbufftest` : Test tool to check ZBUFF (a buffered streaming API) integrity
- `zstreamtest` : Fuzzer test tool for zstd streaming API
- `legacy` : Test tool to test decoding of legacy zstd frames
- `decodecorpus` : Tool to generate valid Zstandard frames, for verifying decoder implementations
#### `test-zstd-versions.py` - script for testing zstd interoperability between versions
This script creates `versionsTest` directory to which zstd repository is cloned.
Then all taged (released) versions of zstd are compiled.
Then all tagged (released) versions of zstd are compiled.
In the following step interoperability between zstd versions is checked.
@@ -64,3 +66,25 @@ optional arguments:
--sleepTime SLEEPTIME
frequency of repository checking in seconds
```
#### `decodecorpus` - tool to generate Zstandard frames for decoder testing
Command line tool to generate test .zst files.
This tool will generate .zst files with checksums,
as well as optionally output the corresponding correct uncompressed data for
extra verfication.
Example:
```
./decodecorpus -ptestfiles -otestfiles -n10000 -s5
```
will generate 10,000 sample .zst files using a seed of 5 in the `testfiles` directory,
with the zstd checksum field set,
as well as the 10,000 original files for more detailed comparison of decompression results.
```
./decodecorpus -t -T1mn
```
will choose a random seed, and for 1 minute,
generate random test frames and ensure that the
zstd library correctly decompresses them in both simple and streaming modes.
+1450
View File
@@ -0,0 +1,1450 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include <limits.h>
#include <math.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "zstd.h"
#include "zstd_internal.h"
#include "mem.h"
// Direct access to internal compression functions is required
#include "zstd_compress.c"
#define XXH_STATIC_LINKING_ONLY
#include "xxhash.h" /* XXH64 */
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
#ifndef MAX_PATH
#ifdef PATH_MAX
#define MAX_PATH PATH_MAX
#else
#define MAX_PATH 256
#endif
#endif
/*-************************************
* DISPLAY Macros
**************************************/
#define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
#define DISPLAYLEVEL(l, ...) if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); }
static U32 g_displayLevel = 0;
#define DISPLAYUPDATE(...) \
do { \
if ((clockSpan(g_displayClock) > g_refreshRate) || \
(g_displayLevel >= 4)) { \
g_displayClock = clock(); \
DISPLAY(__VA_ARGS__); \
if (g_displayLevel >= 4) fflush(stderr); \
} \
} while (0)
static const clock_t g_refreshRate = CLOCKS_PER_SEC / 6;
static clock_t g_displayClock = 0;
static clock_t clockSpan(clock_t cStart)
{
return clock() - cStart; /* works even when overflow; max span ~ 30mn */
}
#define CHECKERR(code) \
do { \
if (ZSTD_isError(code)) { \
DISPLAY("Error occurred while generating data: %s\n", \
ZSTD_getErrorName(code)); \
exit(1); \
} \
} while (0)
/*-*******************************************************
* Random function
*********************************************************/
#define CLAMP(x, a, b) ((x) < (a) ? (a) : ((x) > (b) ? (b) : (x)))
static unsigned RAND(unsigned* src)
{
#define RAND_rotl32(x,r) ((x << r) | (x >> (32 - r)))
static const U32 prime1 = 2654435761U;
static const U32 prime2 = 2246822519U;
U32 rand32 = *src;
rand32 *= prime1;
rand32 += prime2;
rand32 = RAND_rotl32(rand32, 13);
*src = rand32;
return RAND_rotl32(rand32, 27);
#undef RAND_rotl32
}
#define DISTSIZE (8192)
/* Write `size` bytes into `ptr`, all of which are less than or equal to `maxSymb` */
static void RAND_bufferMaxSymb(U32* seed, void* ptr, size_t size, int maxSymb)
{
size_t i;
BYTE* op = ptr;
for (i = 0; i < size; i++) {
op[i] = RAND(seed) % (maxSymb + 1);
}
}
/* Write `size` random bytes into `ptr` */
static void RAND_buffer(U32* seed, void* ptr, size_t size)
{
size_t i;
BYTE* op = ptr;
for (i = 0; i + 4 <= size; i += 4) {
MEM_writeLE32(op + i, RAND(seed));
}
for (; i < size; i++) {
op[i] = RAND(seed) & 0xff;
}
}
/* Write `size` bytes into `ptr` following the distribution `dist` */
static void RAND_bufferDist(U32* seed, BYTE* dist, void* ptr, size_t size)
{
size_t i;
BYTE* op = ptr;
for (i = 0; i < size; i++) {
op[i] = dist[RAND(seed) % DISTSIZE];
}
}
/* Generate a random distribution where the frequency of each symbol follows a
* geometric distribution defined by `weight`
* `dist` should have size at least `DISTSIZE` */
static void RAND_genDist(U32* seed, BYTE* dist, double weight)
{
size_t i = 0;
size_t statesLeft = DISTSIZE;
BYTE symb = RAND(seed) % 256;
BYTE step = (RAND(seed) % 256) | 1; /* force it to be odd so it's relatively prime to 256 */
while (i < DISTSIZE) {
size_t states = ((size_t)(weight * statesLeft)) + 1;
size_t j;
for (j = 0; j < states && i < DISTSIZE; j++, i++) {
dist[i] = symb;
}
symb += step;
statesLeft -= states;
}
}
/* Generates a random number in the range [min, max) */
static inline U32 RAND_range(U32* seed, U32 min, U32 max)
{
return (RAND(seed) % (max-min)) + min;
}
#define ROUND(x) ((U32)(x + 0.5))
/* Generates a random number in an exponential distribution with mean `mean` */
static double RAND_exp(U32* seed, double mean)
{
double const u = RAND(seed) / (double) UINT_MAX;
return log(1-u) * (-mean);
}
/*-*******************************************************
* Constants and Structs
*********************************************************/
const char *BLOCK_TYPES[] = {"raw", "rle", "compressed"};
#define MAX_DECOMPRESSED_SIZE_LOG 20
#define MAX_DECOMPRESSED_SIZE (1ULL << MAX_DECOMPRESSED_SIZE_LOG)
#define MAX_WINDOW_LOG 22 /* Recommended support is 8MB, so limit to 4MB + mantissa */
#define MAX_BLOCK_SIZE (128ULL * 1024)
#define MIN_SEQ_LEN (3)
#define MAX_NB_SEQ ((MAX_BLOCK_SIZE + MIN_SEQ_LEN - 1) / MIN_SEQ_LEN)
BYTE CONTENT_BUFFER[MAX_DECOMPRESSED_SIZE];
BYTE FRAME_BUFFER[MAX_DECOMPRESSED_SIZE * 2];
BYTE LITERAL_BUFFER[MAX_BLOCK_SIZE];
seqDef SEQUENCE_BUFFER[MAX_NB_SEQ];
BYTE SEQUENCE_LITERAL_BUFFER[MAX_BLOCK_SIZE]; /* storeSeq expects a place to copy literals to */
BYTE SEQUENCE_LLCODE[MAX_BLOCK_SIZE];
BYTE SEQUENCE_MLCODE[MAX_BLOCK_SIZE];
BYTE SEQUENCE_OFCODE[MAX_BLOCK_SIZE];
unsigned WKSP[1024];
typedef struct {
size_t contentSize; /* 0 means unknown (unless contentSize == windowSize == 0) */
unsigned windowSize; /* contentSize >= windowSize means single segment */
} frameHeader_t;
/* For repeat modes */
typedef struct {
U32 rep[ZSTD_REP_NUM];
int hufInit;
/* the distribution used in the previous block for repeat mode */
BYTE hufDist[DISTSIZE];
U32 hufTable [256]; /* HUF_CElt is an incomplete type */
int fseInit;
FSE_CTable offcodeCTable [FSE_CTABLE_SIZE_U32(OffFSELog, MaxOff)];
FSE_CTable matchlengthCTable[FSE_CTABLE_SIZE_U32(MLFSELog, MaxML)];
FSE_CTable litlengthCTable [FSE_CTABLE_SIZE_U32(LLFSELog, MaxLL)];
/* Symbols that were present in the previous distribution, for use with
* set_repeat */
BYTE litlengthSymbolSet[36];
BYTE offsetSymbolSet[29];
BYTE matchlengthSymbolSet[53];
} cblockStats_t;
typedef struct {
void* data;
void* dataStart;
void* dataEnd;
void* src;
void* srcStart;
void* srcEnd;
frameHeader_t header;
cblockStats_t stats;
cblockStats_t oldStats; /* so they can be rolled back if uncompressible */
} frame_t;
/*-*******************************************************
* Generator Functions
*********************************************************/
struct {
int contentSize; /* force the content size to be present */
} opts; /* advanced options on generation */
/* Generate and write a random frame header */
static void writeFrameHeader(U32* seed, frame_t* frame)
{
BYTE* const op = frame->data;
size_t pos = 0;
frameHeader_t fh;
BYTE windowByte = 0;
int singleSegment = 0;
int contentSizeFlag = 0;
int fcsCode = 0;
memset(&fh, 0, sizeof(fh));
/* generate window size */
{
/* Follow window algorithm from specification */
int const exponent = RAND(seed) % (MAX_WINDOW_LOG - 10);
int const mantissa = RAND(seed) % 8;
windowByte = (exponent << 3) | mantissa;
fh.windowSize = (1U << (exponent + 10));
fh.windowSize += fh.windowSize / 8 * mantissa;
}
{
/* Generate random content size */
size_t highBit;
if (RAND(seed) & 7) {
/* do content of at least 128 bytes */
highBit = 1ULL << RAND_range(seed, 7, MAX_DECOMPRESSED_SIZE_LOG);
} else if (RAND(seed) & 3) {
/* do small content */
highBit = 1ULL << RAND_range(seed, 0, 7);
} else {
/* 0 size frame */
highBit = 0;
}
fh.contentSize = highBit ? highBit + (RAND(seed) % highBit) : 0;
/* provide size sometimes */
contentSizeFlag = opts.contentSize | (RAND(seed) & 1);
if (contentSizeFlag && (fh.contentSize == 0 || !(RAND(seed) & 7))) {
/* do single segment sometimes */
fh.windowSize = fh.contentSize;
singleSegment = 1;
}
}
if (contentSizeFlag) {
/* Determine how large fcs field has to be */
int minFcsCode = (fh.contentSize >= 256) +
(fh.contentSize >= 65536 + 256) +
(fh.contentSize > 0xFFFFFFFFU);
if (!singleSegment && !minFcsCode) {
minFcsCode = 1;
}
fcsCode = minFcsCode + (RAND(seed) % (4 - minFcsCode));
if (fcsCode == 1 && fh.contentSize < 256) fcsCode++;
}
/* write out the header */
MEM_writeLE32(op + pos, ZSTD_MAGICNUMBER);
pos += 4;
{
BYTE const frameHeaderDescriptor =
(fcsCode << 6) | (singleSegment << 5) | (1 << 2);
op[pos++] = frameHeaderDescriptor;
}
if (!singleSegment) {
op[pos++] = windowByte;
}
if (contentSizeFlag) {
switch (fcsCode) {
default: /* Impossible */
case 0: op[pos++] = fh.contentSize; break;
case 1: MEM_writeLE16(op + pos, fh.contentSize - 256); pos += 2; break;
case 2: MEM_writeLE32(op + pos, fh.contentSize); pos += 4; break;
case 3: MEM_writeLE64(op + pos, fh.contentSize); pos += 8; break;
}
}
DISPLAYLEVEL(2, " frame content size:\t%zu\n", fh.contentSize);
DISPLAYLEVEL(2, " frame window size:\t%u\n", fh.windowSize);
DISPLAYLEVEL(2, " content size flag:\t%d\n", contentSizeFlag);
DISPLAYLEVEL(2, " single segment flag:\t%d\n", singleSegment);
frame->data = op + pos;
frame->header = fh;
}
/* Write a literal block in either raw or RLE form, return the literals size */
static size_t writeLiteralsBlockSimple(U32* seed, frame_t* frame, size_t contentSize)
{
BYTE* op = (BYTE*)frame->data;
int const type = RAND(seed) % 2;
int const sizeFormatDesc = RAND(seed) % 8;
size_t litSize;
size_t maxLitSize = MIN(contentSize, MAX_BLOCK_SIZE);
if (sizeFormatDesc == 0) {
/* Size_FormatDesc = ?0 */
maxLitSize = MIN(maxLitSize, 31);
} else if (sizeFormatDesc <= 4) {
/* Size_FormatDesc = 01 */
maxLitSize = MIN(maxLitSize, 4095);
} else {
/* Size_Format = 11 */
maxLitSize = MIN(maxLitSize, 1048575);
}
litSize = RAND(seed) % (maxLitSize + 1);
if (frame->src == frame->srcStart && litSize == 0) {
litSize = 1; /* no empty literals if there's nothing preceding this block */
}
if (litSize + 3 > contentSize) {
litSize = contentSize; /* no matches shorter than 3 are allowed */
}
/* use smallest size format that fits */
if (litSize < 32) {
op[0] = (type | (0 << 2) | (litSize << 3)) & 0xff;
op += 1;
} else if (litSize < 4096) {
op[0] = (type | (1 << 2) | (litSize << 4)) & 0xff;
op[1] = (litSize >> 4) & 0xff;
op += 2;
} else {
op[0] = (type | (3 << 2) | (litSize << 4)) & 0xff;
op[1] = (litSize >> 4) & 0xff;
op[2] = (litSize >> 12) & 0xff;
op += 3;
}
if (type == 0) {
/* Raw literals */
DISPLAYLEVEL(4, " raw literals\n");
RAND_buffer(seed, LITERAL_BUFFER, litSize);
memcpy(op, LITERAL_BUFFER, litSize);
op += litSize;
} else {
/* RLE literals */
BYTE const symb = RAND(seed) % 256;
DISPLAYLEVEL(4, " rle literals: 0x%02x\n", (U32)symb);
memset(LITERAL_BUFFER, symb, litSize);
op[0] = symb;
op++;
}
frame->data = op;
return litSize;
}
/* Generate a Huffman header for the given source */
static size_t writeHufHeader(U32* seed, HUF_CElt* hufTable, void* dst, size_t dstSize,
const void* src, size_t srcSize)
{
BYTE* const ostart = (BYTE*)dst;
BYTE* op = ostart;
unsigned huffLog = 11;
U32 maxSymbolValue = 255;
U32 count[HUF_SYMBOLVALUE_MAX+1];
/* Scan input and build symbol stats */
{ size_t const largest = FSE_count_wksp (count, &maxSymbolValue, (const BYTE*)src, srcSize, WKSP);
if (largest == srcSize) { *ostart = ((const BYTE*)src)[0]; return 0; } /* single symbol, rle */
if (largest <= (srcSize >> 7)+1) return 0; /* Fast heuristic : not compressible enough */
}
/* Build Huffman Tree */
/* Max Huffman log is 11, min is highbit(maxSymbolValue)+1 */
huffLog = RAND_range(seed, ZSTD_highbit32(maxSymbolValue)+1, huffLog+1);
DISPLAYLEVEL(6, " huffman log: %u\n", huffLog);
{ size_t const maxBits = HUF_buildCTable_wksp (hufTable, count, maxSymbolValue, huffLog, WKSP, sizeof(WKSP));
CHECKERR(maxBits);
huffLog = (U32)maxBits;
}
/* Write table description header */
{ size_t const hSize = HUF_writeCTable (op, dstSize, hufTable, maxSymbolValue, huffLog);
if (hSize + 12 >= srcSize) return 0; /* not useful to try compression */
op += hSize;
}
return op - ostart;
}
/* Write a Huffman coded literals block and return the litearls size */
static size_t writeLiteralsBlockCompressed(U32* seed, frame_t* frame, size_t contentSize)
{
BYTE* origop = (BYTE*)frame->data;
BYTE* opend = (BYTE*)frame->dataEnd;
BYTE* op;
BYTE* const ostart = origop;
int const sizeFormat = RAND(seed) % 4;
size_t litSize;
size_t hufHeaderSize = 0;
size_t compressedSize = 0;
size_t maxLitSize = MIN(contentSize-3, MAX_BLOCK_SIZE);
symbolEncodingType_e hType;
if (contentSize < 64) {
/* make sure we get reasonably-sized literals for compression */
return ERROR(GENERIC);
}
DISPLAYLEVEL(4, " compressed literals\n");
switch (sizeFormat) {
case 0: /* fall through, size is the same as case 1 */
case 1:
maxLitSize = MIN(maxLitSize, 1023);
origop += 3;
break;
case 2:
maxLitSize = MIN(maxLitSize, 16383);
origop += 4;
break;
case 3:
maxLitSize = MIN(maxLitSize, 262143);
origop += 5;
break;
default:; /* impossible */
}
do {
op = origop;
do {
litSize = RAND(seed) % (maxLitSize + 1);
} while (litSize < 32); /* avoid small literal sizes */
if (litSize + 3 > contentSize) {
litSize = contentSize; /* no matches shorter than 3 are allowed */
}
/* most of the time generate a new distribution */
if ((RAND(seed) & 3) || !frame->stats.hufInit) {
do {
if (RAND(seed) & 3) {
/* add 10 to ensure some compressability */
double const weight = ((RAND(seed) % 90) + 10) / 100.0;
DISPLAYLEVEL(5, " distribution weight: %d%%\n",
(int)(weight * 100));
RAND_genDist(seed, frame->stats.hufDist, weight);
} else {
/* sometimes do restricted range literals to force
* non-huffman headers */
DISPLAYLEVEL(5, " small range literals\n");
RAND_bufferMaxSymb(seed, frame->stats.hufDist, DISTSIZE,
15);
}
RAND_bufferDist(seed, frame->stats.hufDist, LITERAL_BUFFER,
litSize);
/* generate the header from the distribution instead of the
* actual data to avoid bugs with symbols that were in the
* distribution but never showed up in the output */
hufHeaderSize = writeHufHeader(
seed, (HUF_CElt*)frame->stats.hufTable, op, opend - op,
frame->stats.hufDist, DISTSIZE);
CHECKERR(hufHeaderSize);
/* repeat until a valid header is written */
} while (hufHeaderSize == 0);
op += hufHeaderSize;
hType = set_compressed;
frame->stats.hufInit = 1;
} else {
/* repeat the distribution/table from last time */
DISPLAYLEVEL(5, " huffman repeat stats\n");
RAND_bufferDist(seed, frame->stats.hufDist, LITERAL_BUFFER,
litSize);
hufHeaderSize = 0;
hType = set_repeat;
}
do {
compressedSize =
sizeFormat == 0
? HUF_compress1X_usingCTable(
op, opend - op, LITERAL_BUFFER, litSize,
(HUF_CElt*)frame->stats.hufTable)
: HUF_compress4X_usingCTable(
op, opend - op, LITERAL_BUFFER, litSize,
(HUF_CElt*)frame->stats.hufTable);
CHECKERR(compressedSize);
/* this only occurs when it could not compress or similar */
} while (compressedSize <= 0);
op += compressedSize;
compressedSize += hufHeaderSize;
DISPLAYLEVEL(5, " regenerated size: %zu\n", litSize);
DISPLAYLEVEL(5, " compressed size: %zu\n", compressedSize);
if (compressedSize >= litSize) {
DISPLAYLEVEL(5, " trying again\n");
/* if we have to try again, reset the stats so we don't accidentally
* try to repeat a distribution we just made */
frame->stats = frame->oldStats;
} else {
break;
}
} while (1);
/* write header */
switch (sizeFormat) {
case 0: /* fall through, size is the same as case 1 */
case 1: {
U32 const header = hType | (sizeFormat << 2) | ((U32)litSize << 4) |
((U32)compressedSize << 14);
MEM_writeLE24(ostart, header);
break;
}
case 2: {
U32 const header = hType | (sizeFormat << 2) | ((U32)litSize << 4) |
((U32)compressedSize << 18);
MEM_writeLE32(ostart, header);
break;
}
case 3: {
U32 const header = hType | (sizeFormat << 2) | ((U32)litSize << 4) |
((U32)compressedSize << 22);
MEM_writeLE32(ostart, header);
ostart[4] = (BYTE)(compressedSize >> 10);
break;
}
default:; /* impossible */
}
frame->data = op;
return litSize;
}
static size_t writeLiteralsBlock(U32* seed, frame_t* frame, size_t contentSize)
{
/* only do compressed for larger segments to avoid compressibility issues */
if (RAND(seed) & 7 && contentSize >= 64) {
return writeLiteralsBlockCompressed(seed, frame, contentSize);
} else {
return writeLiteralsBlockSimple(seed, frame, contentSize);
}
}
static inline void initSeqStore(seqStore_t *seqStore) {
seqStore->sequencesStart = SEQUENCE_BUFFER;
seqStore->litStart = SEQUENCE_LITERAL_BUFFER;
seqStore->llCode = SEQUENCE_LLCODE;
seqStore->mlCode = SEQUENCE_MLCODE;
seqStore->ofCode = SEQUENCE_OFCODE;
ZSTD_resetSeqStore(seqStore);
}
/* Randomly generate sequence commands */
static U32 generateSequences(U32* seed, frame_t* frame, seqStore_t* seqStore,
size_t contentSize, size_t literalsSize)
{
/* The total length of all the matches */
size_t const remainingMatch = contentSize - literalsSize;
size_t excessMatch;
U32 i;
U32 numSequences;
const BYTE* literals = LITERAL_BUFFER;
BYTE* srcPtr = frame->src;
if (literalsSize == contentSize) {
numSequences = 0;
} else {
/* each match must be at least MIN_SEQ_LEN, so this is the maximum
* number of sequences we can have */
U32 const maxSequences = (U32)remainingMatch / MIN_SEQ_LEN;
numSequences = (RAND(seed) % maxSequences) + 1;
/* the extra match lengths we have to allocate to each sequence */
excessMatch = remainingMatch - numSequences * MIN_SEQ_LEN;
}
DISPLAYLEVEL(5, " total match lengths: %zu\n", remainingMatch);
for (i = 0; i < numSequences; i++) {
/* Generate match and literal lengths by exponential distribution to
* ensure nice numbers */
U32 matchLen =
MIN_SEQ_LEN +
ROUND(RAND_exp(seed, excessMatch / (double)(numSequences - i)));
U32 literalLen =
(RAND(seed) & 7)
? ROUND(RAND_exp(seed,
literalsSize /
(double)(numSequences - i)))
: 0;
/* actual offset, code to send, and point to copy up to when shifting
* codes in the repeat offsets history */
U32 offset, offsetCode, repIndex;
/* bounds checks */
matchLen = MIN(matchLen, excessMatch + MIN_SEQ_LEN);
literalLen = MIN(literalLen, literalsSize);
if (i == 0 && srcPtr == frame->srcStart && literalLen == 0) literalLen = 1;
if (i + 1 == numSequences) matchLen = MIN_SEQ_LEN + excessMatch;
memcpy(srcPtr, literals, literalLen);
srcPtr += literalLen;
do {
if (RAND(seed) & 7) {
/* do a normal offset */
offset = (RAND(seed) %
MIN(frame->header.windowSize,
(BYTE*)srcPtr - (BYTE*)frame->srcStart)) +
1;
offsetCode = offset + ZSTD_REP_MOVE;
repIndex = 2;
} else {
/* do a repeat offset */
offsetCode = RAND(seed) % 3;
if (literalLen > 0) {
offset = frame->stats.rep[offsetCode];
repIndex = offsetCode;
} else {
/* special case */
offset = offsetCode == 2 ? frame->stats.rep[0] - 1
: frame->stats.rep[offsetCode + 1];
repIndex = MIN(2, offsetCode + 1);
}
}
} while (offset > (BYTE*)srcPtr - (BYTE*)frame->srcStart || offset == 0);
{ size_t j;
for (j = 0; j < matchLen; j++) {
*srcPtr = *(srcPtr-offset);
srcPtr++;
}
}
{ int r;
for (r = repIndex; r > 0; r--) {
frame->stats.rep[r] = frame->stats.rep[r - 1];
}
frame->stats.rep[0] = offset;
}
DISPLAYLEVEL(6, " LL: %5u OF: %5u ML: %5u", literalLen, offset, matchLen);
DISPLAYLEVEL(7, " srcPos: %8zu seqNb: %3u",
(BYTE*)srcPtr - (BYTE*)frame->srcStart, i);
DISPLAYLEVEL(6, "\n");
if (offsetCode < 3) {
DISPLAYLEVEL(7, " repeat offset: %d\n", repIndex);
}
/* use libzstd sequence handling */
ZSTD_storeSeq(seqStore, literalLen, literals, offsetCode,
matchLen - MINMATCH);
literalsSize -= literalLen;
excessMatch -= (matchLen - MIN_SEQ_LEN);
literals += literalLen;
}
memcpy(srcPtr, literals, literalsSize);
srcPtr += literalsSize;
DISPLAYLEVEL(6, " excess literals: %5zu", literalsSize);
DISPLAYLEVEL(7, " srcPos: %8zu", (BYTE*)srcPtr - (BYTE*)frame->srcStart);
DISPLAYLEVEL(6, "\n");
return numSequences;
}
static void initSymbolSet(const BYTE* symbols, size_t len, BYTE* set, BYTE maxSymbolValue)
{
size_t i;
memset(set, 0, (size_t)maxSymbolValue+1);
for (i = 0; i < len; i++) {
set[symbols[i]] = 1;
}
}
static int isSymbolSubset(const BYTE* symbols, size_t len, const BYTE* set, BYTE maxSymbolValue)
{
size_t i;
for (i = 0; i < len; i++) {
if (symbols[i] > maxSymbolValue || !set[symbols[i]]) {
return 0;
}
}
return 1;
}
static size_t writeSequences(U32* seed, frame_t* frame, seqStore_t* seqStorePtr,
size_t nbSeq)
{
/* This code is mostly copied from ZSTD_compressSequences in zstd_compress.c */
U32 count[MaxSeq+1];
S16 norm[MaxSeq+1];
FSE_CTable* CTable_LitLength = frame->stats.litlengthCTable;
FSE_CTable* CTable_OffsetBits = frame->stats.offcodeCTable;
FSE_CTable* CTable_MatchLength = frame->stats.matchlengthCTable;
U32 LLtype, Offtype, MLtype; /* compressed, raw or rle */
const seqDef* const sequences = seqStorePtr->sequencesStart;
const BYTE* const ofCodeTable = seqStorePtr->ofCode;
const BYTE* const llCodeTable = seqStorePtr->llCode;
const BYTE* const mlCodeTable = seqStorePtr->mlCode;
BYTE* const oend = (BYTE*)frame->dataEnd;
BYTE* op = (BYTE*)frame->data;
BYTE* seqHead;
BYTE scratchBuffer[1<<MAX(MLFSELog,LLFSELog)];
/* literals compressing block removed so that can be done separately */
/* Sequences Header */
if ((oend-op) < 3 /*max nbSeq Size*/ + 1 /*seqHead */) return ERROR(dstSize_tooSmall);
if (nbSeq < 0x7F) *op++ = (BYTE)nbSeq;
else if (nbSeq < LONGNBSEQ) op[0] = (BYTE)((nbSeq>>8) + 0x80), op[1] = (BYTE)nbSeq, op+=2;
else op[0]=0xFF, MEM_writeLE16(op+1, (U16)(nbSeq - LONGNBSEQ)), op+=3;
/* seqHead : flags for FSE encoding type */
seqHead = op++;
if (nbSeq==0) {
frame->data = op;
return 0;
}
/* convert length/distances into codes */
ZSTD_seqToCodes(seqStorePtr);
/* CTable for Literal Lengths */
{ U32 max = MaxLL;
size_t const mostFrequent = FSE_countFast_wksp(count, &max, llCodeTable, nbSeq, WKSP);
if (mostFrequent == nbSeq) {
/* do RLE if we have the chance */
*op++ = llCodeTable[0];
FSE_buildCTable_rle(CTable_LitLength, (BYTE)max);
LLtype = set_rle;
} else if (frame->stats.fseInit && !(RAND(seed) & 3) &&
isSymbolSubset(llCodeTable, nbSeq,
frame->stats.litlengthSymbolSet, 35)) {
/* maybe do repeat mode if we're allowed to */
LLtype = set_repeat;
} else if (!(RAND(seed) & 3)) {
/* maybe use the default distribution */
FSE_buildCTable_wksp(CTable_LitLength, LL_defaultNorm, MaxLL, LL_defaultNormLog, scratchBuffer, sizeof(scratchBuffer));
LLtype = set_basic;
} else {
/* fall back on a full table */
size_t nbSeq_1 = nbSeq;
const U32 tableLog = FSE_optimalTableLog(LLFSELog, nbSeq, max);
if (count[llCodeTable[nbSeq-1]]>1) { count[llCodeTable[nbSeq-1]]--; nbSeq_1--; }
FSE_normalizeCount(norm, tableLog, count, nbSeq_1, max);
{ size_t const NCountSize = FSE_writeNCount(op, oend-op, norm, max, tableLog); /* overflow protected */
if (FSE_isError(NCountSize)) return ERROR(GENERIC);
op += NCountSize; }
FSE_buildCTable_wksp(CTable_LitLength, norm, max, tableLog, scratchBuffer, sizeof(scratchBuffer));
LLtype = set_compressed;
} }
/* CTable for Offsets */
/* see Literal Lengths for descriptions of mode choices */
{ U32 max = MaxOff;
size_t const mostFrequent = FSE_countFast_wksp(count, &max, ofCodeTable, nbSeq, WKSP);
if (mostFrequent == nbSeq) {
*op++ = ofCodeTable[0];
FSE_buildCTable_rle(CTable_OffsetBits, (BYTE)max);
Offtype = set_rle;
} else if (frame->stats.fseInit && !(RAND(seed) & 3) &&
isSymbolSubset(ofCodeTable, nbSeq,
frame->stats.offsetSymbolSet, 28)) {
Offtype = set_repeat;
} else if (!(RAND(seed) & 3)) {
FSE_buildCTable_wksp(CTable_OffsetBits, OF_defaultNorm, MaxOff, OF_defaultNormLog, scratchBuffer, sizeof(scratchBuffer));
Offtype = set_basic;
} else {
size_t nbSeq_1 = nbSeq;
const U32 tableLog = FSE_optimalTableLog(OffFSELog, nbSeq, max);
if (count[ofCodeTable[nbSeq-1]]>1) { count[ofCodeTable[nbSeq-1]]--; nbSeq_1--; }
FSE_normalizeCount(norm, tableLog, count, nbSeq_1, max);
{ size_t const NCountSize = FSE_writeNCount(op, oend-op, norm, max, tableLog); /* overflow protected */
if (FSE_isError(NCountSize)) return ERROR(GENERIC);
op += NCountSize; }
FSE_buildCTable_wksp(CTable_OffsetBits, norm, max, tableLog, scratchBuffer, sizeof(scratchBuffer));
Offtype = set_compressed;
} }
/* CTable for MatchLengths */
/* see Literal Lengths for descriptions of mode choices */
{ U32 max = MaxML;
size_t const mostFrequent = FSE_countFast_wksp(count, &max, mlCodeTable, nbSeq, WKSP);
if (mostFrequent == nbSeq) {
*op++ = *mlCodeTable;
FSE_buildCTable_rle(CTable_MatchLength, (BYTE)max);
MLtype = set_rle;
} else if (frame->stats.fseInit && !(RAND(seed) & 3) &&
isSymbolSubset(mlCodeTable, nbSeq,
frame->stats.matchlengthSymbolSet, 52)) {
MLtype = set_repeat;
} else if (!(RAND(seed) & 3)) {
/* sometimes do default distribution */
FSE_buildCTable_wksp(CTable_MatchLength, ML_defaultNorm, MaxML, ML_defaultNormLog, scratchBuffer, sizeof(scratchBuffer));
MLtype = set_basic;
} else {
/* fall back on table */
size_t nbSeq_1 = nbSeq;
const U32 tableLog = FSE_optimalTableLog(MLFSELog, nbSeq, max);
if (count[mlCodeTable[nbSeq-1]]>1) { count[mlCodeTable[nbSeq-1]]--; nbSeq_1--; }
FSE_normalizeCount(norm, tableLog, count, nbSeq_1, max);
{ size_t const NCountSize = FSE_writeNCount(op, oend-op, norm, max, tableLog); /* overflow protected */
if (FSE_isError(NCountSize)) return ERROR(GENERIC);
op += NCountSize; }
FSE_buildCTable_wksp(CTable_MatchLength, norm, max, tableLog, scratchBuffer, sizeof(scratchBuffer));
MLtype = set_compressed;
} }
frame->stats.fseInit = 1;
initSymbolSet(llCodeTable, nbSeq, frame->stats.litlengthSymbolSet, 35);
initSymbolSet(ofCodeTable, nbSeq, frame->stats.offsetSymbolSet, 28);
initSymbolSet(mlCodeTable, nbSeq, frame->stats.matchlengthSymbolSet, 52);
DISPLAYLEVEL(5, " LL type: %d OF type: %d ML type: %d\n", LLtype, Offtype, MLtype);
*seqHead = (BYTE)((LLtype<<6) + (Offtype<<4) + (MLtype<<2));
/* Encoding Sequences */
{ BIT_CStream_t blockStream;
FSE_CState_t stateMatchLength;
FSE_CState_t stateOffsetBits;
FSE_CState_t stateLitLength;
CHECK_E(BIT_initCStream(&blockStream, op, oend-op), dstSize_tooSmall); /* not enough space remaining */
/* first symbols */
FSE_initCState2(&stateMatchLength, CTable_MatchLength, mlCodeTable[nbSeq-1]);
FSE_initCState2(&stateOffsetBits, CTable_OffsetBits, ofCodeTable[nbSeq-1]);
FSE_initCState2(&stateLitLength, CTable_LitLength, llCodeTable[nbSeq-1]);
BIT_addBits(&blockStream, sequences[nbSeq-1].litLength, LL_bits[llCodeTable[nbSeq-1]]);
if (MEM_32bits()) BIT_flushBits(&blockStream);
BIT_addBits(&blockStream, sequences[nbSeq-1].matchLength, ML_bits[mlCodeTable[nbSeq-1]]);
if (MEM_32bits()) BIT_flushBits(&blockStream);
BIT_addBits(&blockStream, sequences[nbSeq-1].offset, ofCodeTable[nbSeq-1]);
BIT_flushBits(&blockStream);
{ size_t n;
for (n=nbSeq-2 ; n<nbSeq ; n--) { /* intentional underflow */
BYTE const llCode = llCodeTable[n];
BYTE const ofCode = ofCodeTable[n];
BYTE const mlCode = mlCodeTable[n];
U32 const llBits = LL_bits[llCode];
U32 const ofBits = ofCode; /* 32b*/ /* 64b*/
U32 const mlBits = ML_bits[mlCode];
/* (7)*/ /* (7)*/
FSE_encodeSymbol(&blockStream, &stateOffsetBits, ofCode); /* 15 */ /* 15 */
FSE_encodeSymbol(&blockStream, &stateMatchLength, mlCode); /* 24 */ /* 24 */
if (MEM_32bits()) BIT_flushBits(&blockStream); /* (7)*/
FSE_encodeSymbol(&blockStream, &stateLitLength, llCode); /* 16 */ /* 33 */
if (MEM_32bits() || (ofBits+mlBits+llBits >= 64-7-(LLFSELog+MLFSELog+OffFSELog)))
BIT_flushBits(&blockStream); /* (7)*/
BIT_addBits(&blockStream, sequences[n].litLength, llBits);
if (MEM_32bits() && ((llBits+mlBits)>24)) BIT_flushBits(&blockStream);
BIT_addBits(&blockStream, sequences[n].matchLength, mlBits);
if (MEM_32bits()) BIT_flushBits(&blockStream); /* (7)*/
BIT_addBits(&blockStream, sequences[n].offset, ofBits); /* 31 */
BIT_flushBits(&blockStream); /* (7)*/
} }
FSE_flushCState(&blockStream, &stateMatchLength);
FSE_flushCState(&blockStream, &stateOffsetBits);
FSE_flushCState(&blockStream, &stateLitLength);
{ size_t const streamSize = BIT_closeCStream(&blockStream);
if (streamSize==0) return ERROR(dstSize_tooSmall); /* not enough space */
op += streamSize;
} }
frame->data = op;
return 0;
}
static size_t writeSequencesBlock(U32* seed, frame_t* frame, size_t contentSize,
size_t literalsSize)
{
seqStore_t seqStore;
size_t numSequences;
initSeqStore(&seqStore);
/* randomly generate sequences */
numSequences = generateSequences(seed, frame, &seqStore, contentSize, literalsSize);
/* write them out to the frame data */
CHECKERR(writeSequences(seed, frame, &seqStore, numSequences));
return numSequences;
}
static size_t writeCompressedBlock(U32* seed, frame_t* frame, size_t contentSize)
{
BYTE* const blockStart = (BYTE*)frame->data;
size_t literalsSize;
size_t nbSeq;
DISPLAYLEVEL(4, " compressed block:\n");
literalsSize = writeLiteralsBlock(seed, frame, contentSize);
DISPLAYLEVEL(4, " literals size: %zu\n", literalsSize);
nbSeq = writeSequencesBlock(seed, frame, contentSize, literalsSize);
DISPLAYLEVEL(4, " number of sequences: %zu\n", nbSeq);
return (BYTE*)frame->data - blockStart;
}
static void writeBlock(U32* seed, frame_t* frame, size_t contentSize,
int lastBlock)
{
int const blockTypeDesc = RAND(seed) % 8;
size_t blockSize;
int blockType;
BYTE *const header = (BYTE*)frame->data;
BYTE *op = header + 3;
DISPLAYLEVEL(3, " block:\n");
DISPLAYLEVEL(3, " block content size: %zu\n", contentSize);
DISPLAYLEVEL(3, " last block: %s\n", lastBlock ? "yes" : "no");
if (blockTypeDesc == 0) {
/* Raw data frame */
RAND_buffer(seed, frame->src, contentSize);
memcpy(op, frame->src, contentSize);
op += contentSize;
blockType = 0;
blockSize = contentSize;
} else if (blockTypeDesc == 1) {
/* RLE */
BYTE const symbol = RAND(seed) & 0xff;
op[0] = symbol;
memset(frame->src, symbol, contentSize);
op++;
blockType = 1;
blockSize = contentSize;
} else {
/* compressed, most common */
size_t compressedSize;
blockType = 2;
frame->oldStats = frame->stats;
frame->data = op;
compressedSize = writeCompressedBlock(seed, frame, contentSize);
if (compressedSize > contentSize) {
blockType = 0;
memcpy(op, frame->src, contentSize);
op += contentSize;
blockSize = contentSize; /* fall back on raw block if data doesn't
compress */
frame->stats = frame->oldStats; /* don't update the stats */
} else {
op += compressedSize;
blockSize = compressedSize;
}
}
frame->src = (BYTE*)frame->src + contentSize;
DISPLAYLEVEL(3, " block type: %s\n", BLOCK_TYPES[blockType]);
DISPLAYLEVEL(3, " block size field: %zu\n", blockSize);
header[0] = (lastBlock | (blockType << 1) | (blockSize << 3)) & 0xff;
MEM_writeLE16(header + 1, blockSize >> 5);
frame->data = op;
}
static void writeBlocks(U32* seed, frame_t* frame)
{
size_t contentLeft = frame->header.contentSize;
size_t const maxBlockSize = MIN(MAX_BLOCK_SIZE, frame->header.windowSize);
while (1) {
/* 1 in 4 chance of ending frame */
int const lastBlock = contentLeft > maxBlockSize ? 0 : !(RAND(seed) & 3);
size_t blockContentSize;
if (lastBlock) {
blockContentSize = contentLeft;
} else {
if (contentLeft > 0 && (RAND(seed) & 7)) {
/* some variable size blocks */
blockContentSize = RAND(seed) % (MIN(maxBlockSize, contentLeft)+1);
} else if (contentLeft > maxBlockSize && (RAND(seed) & 1)) {
/* some full size blocks */
blockContentSize = maxBlockSize;
} else {
/* some empty blocks */
blockContentSize = 0;
}
}
writeBlock(seed, frame, blockContentSize, lastBlock);
contentLeft -= blockContentSize;
if (lastBlock) break;
}
}
static void writeChecksum(frame_t* frame)
{
/* write checksum so implementations can verify their output */
U64 digest = XXH64(frame->srcStart, (BYTE*)frame->src-(BYTE*)frame->srcStart, 0);
DISPLAYLEVEL(2, " checksum: %08x\n", (U32)digest);
MEM_writeLE32(frame->data, (U32)digest);
frame->data = (BYTE*)frame->data + 4;
}
static void outputBuffer(const void* buf, size_t size, const char* const path)
{
/* write data out to file */
const BYTE* ip = (const BYTE*)buf;
FILE* out;
if (path) {
out = fopen(path, "wb");
} else {
out = stdout;
}
if (!out) {
fprintf(stderr, "Failed to open file at %s: ", path);
perror(NULL);
exit(1);
}
{
size_t fsize = size;
size_t written = 0;
while (written < fsize) {
written += fwrite(ip + written, 1, fsize - written, out);
if (ferror(out)) {
fprintf(stderr, "Failed to write to file at %s: ", path);
perror(NULL);
exit(1);
}
}
}
if (path) {
fclose(out);
}
}
static void initFrame(frame_t* fr)
{
memset(fr, 0, sizeof(*fr));
fr->data = fr->dataStart = FRAME_BUFFER;
fr->dataEnd = FRAME_BUFFER + sizeof(FRAME_BUFFER);
fr->src = fr->srcStart = CONTENT_BUFFER;
fr->srcEnd = CONTENT_BUFFER + sizeof(CONTENT_BUFFER);
/* init repeat codes */
fr->stats.rep[0] = 1;
fr->stats.rep[1] = 4;
fr->stats.rep[2] = 8;
}
/* Return the final seed */
static U32 generateFrame(U32 seed, frame_t* fr)
{
/* generate a complete frame */
DISPLAYLEVEL(1, "frame seed: %u\n", seed);
initFrame(fr);
writeFrameHeader(&seed, fr);
writeBlocks(&seed, fr);
writeChecksum(fr);
return seed;
}
/*-*******************************************************
* Test Mode
*********************************************************/
BYTE DECOMPRESSED_BUFFER[MAX_DECOMPRESSED_SIZE];
static size_t testDecodeSimple(frame_t* fr)
{
/* test decoding the generated data with the simple API */
size_t const ret = ZSTD_decompress(DECOMPRESSED_BUFFER, MAX_DECOMPRESSED_SIZE,
fr->dataStart, (BYTE*)fr->data - (BYTE*)fr->dataStart);
if (ZSTD_isError(ret)) return ret;
if (memcmp(DECOMPRESSED_BUFFER, fr->srcStart,
(BYTE*)fr->src - (BYTE*)fr->srcStart) != 0) {
return ERROR(corruption_detected);
}
return ret;
}
static size_t testDecodeStreaming(frame_t* fr)
{
/* test decoding the generated data with the streaming API */
ZSTD_DStream* zd = ZSTD_createDStream();
ZSTD_inBuffer in;
ZSTD_outBuffer out;
size_t ret;
if (!zd) return ERROR(memory_allocation);
in.src = fr->dataStart;
in.pos = 0;
in.size = (BYTE*)fr->data - (BYTE*)fr->dataStart;
out.dst = DECOMPRESSED_BUFFER;
out.pos = 0;
out.size = ZSTD_DStreamOutSize();
ZSTD_initDStream(zd);
while (1) {
ret = ZSTD_decompressStream(zd, &out, &in);
if (ZSTD_isError(ret)) goto cleanup; /* error */
if (ret == 0) break; /* frame is done */
/* force decoding to be done in chunks */
out.size += MIN(ZSTD_DStreamOutSize(), MAX_DECOMPRESSED_SIZE - out.size);
}
ret = out.pos;
if (memcmp(out.dst, fr->srcStart, out.pos) != 0) {
return ERROR(corruption_detected);
}
cleanup:
ZSTD_freeDStream(zd);
return ret;
}
static int runTestMode(U32 seed, unsigned numFiles, unsigned const testDurationS)
{
unsigned fnum;
clock_t const startClock = clock();
clock_t const maxClockSpan = testDurationS * CLOCKS_PER_SEC;
if (numFiles == 0 && !testDurationS) numFiles = 1;
DISPLAY("seed: %u\n", seed);
for (fnum = 0; fnum < numFiles || clockSpan(startClock) < maxClockSpan; fnum++) {
frame_t fr;
if (fnum < numFiles)
DISPLAYUPDATE("\r%u/%u ", fnum, numFiles);
else
DISPLAYUPDATE("\r%u ", fnum);
seed = generateFrame(seed, &fr);
{ size_t const r = testDecodeSimple(&fr);
if (ZSTD_isError(r)) {
DISPLAY("Error in simple mode on test seed %u: %s\n", seed + fnum,
ZSTD_getErrorName(r));
return 1;
}
}
{ size_t const r = testDecodeStreaming(&fr);
if (ZSTD_isError(r)) {
DISPLAY("Error in streaming mode on test seed %u: %s\n", seed + fnum,
ZSTD_getErrorName(r));
return 1;
}
}
}
DISPLAY("\r%u tests completed: ", fnum);
DISPLAY("OK\n");
return 0;
}
/*-*******************************************************
* File I/O
*********************************************************/
static int generateFile(U32 seed, const char* const path,
const char* const origPath)
{
frame_t fr;
DISPLAY("seed: %u\n", seed);
generateFrame(seed, &fr);
outputBuffer(fr.dataStart, (BYTE*)fr.data - (BYTE*)fr.dataStart, path);
if (origPath) {
outputBuffer(fr.srcStart, (BYTE*)fr.src - (BYTE*)fr.srcStart, origPath);
}
return 0;
}
static int generateCorpus(U32 seed, unsigned numFiles, const char* const path,
const char* const origPath)
{
char outPath[MAX_PATH];
unsigned fnum;
DISPLAY("seed: %u\n", seed);
for (fnum = 0; fnum < numFiles; fnum++) {
frame_t fr;
DISPLAYUPDATE("\r%u/%u ", fnum, numFiles);
seed = generateFrame(seed, &fr);
if (snprintf(outPath, MAX_PATH, "%s/z%06u.zst", path, fnum) + 1 > MAX_PATH) {
DISPLAY("Error: path too long\n");
return 1;
}
outputBuffer(fr.dataStart, (BYTE*)fr.data - (BYTE*)fr.dataStart, outPath);
if (origPath) {
if (snprintf(outPath, MAX_PATH, "%s/z%06u", origPath, fnum) + 1 > MAX_PATH) {
DISPLAY("Error: path too long\n");
return 1;
}
outputBuffer(fr.srcStart, (BYTE*)fr.src - (BYTE*)fr.srcStart, outPath);
}
}
DISPLAY("\r%u/%u \n", fnum, numFiles);
return 0;
}
/*_*******************************************************
* Command line
*********************************************************/
static U32 makeSeed(void)
{
U32 t = time(NULL);
return XXH32(&t, sizeof(t), 0) % 65536;
}
static unsigned readInt(const char** argument)
{
unsigned val = 0;
while ((**argument>='0') && (**argument<='9')) {
val *= 10;
val += **argument - '0';
(*argument)++;
}
return val;
}
static void usage(const char* programName)
{
DISPLAY( "Usage :\n");
DISPLAY( " %s [args]\n", programName);
DISPLAY( "\n");
DISPLAY( "Arguments :\n");
DISPLAY( " -p<path> : select output path (default:stdout)\n");
DISPLAY( " in multiple files mode this should be a directory\n");
DISPLAY( " -o<path> : select path to output original file (default:no output)\n");
DISPLAY( " in multiple files mode this should be a directory\n");
DISPLAY( " -s# : select seed (default:random based on time)\n");
DISPLAY( " -n# : number of files to generate (default:1)\n");
DISPLAY( " -t : activate test mode (test files against libzstd instead of outputting them)\n");
DISPLAY( " -T# : length of time to run tests for\n");
DISPLAY( " -v : increase verbosity level (default:0, max:7)\n");
DISPLAY( " -h/H : display help/long help and exit\n");
}
static void advancedUsage(const char* programName)
{
usage(programName);
DISPLAY( "\n");
DISPLAY( "Advanced arguments :\n");
DISPLAY( " --content-size : always include the content size in the frame header\n");
}
int main(int argc, char** argv)
{
U32 seed = 0;
int seedset = 0;
unsigned numFiles = 0;
unsigned testDuration = 0;
int testMode = 0;
const char* path = NULL;
const char* origPath = NULL;
int argNb;
/* Check command line */
for (argNb=1; argNb<argc; argNb++) {
const char* argument = argv[argNb];
if(!argument) continue; /* Protection if argument empty */
/* Handle commands. Aggregated commands are allowed */
if (argument[0]=='-') {
argument++;
while (*argument!=0) {
switch(*argument)
{
case 'h':
usage(argv[0]);
return 0;
case 'H':
advancedUsage(argv[0]);
return 0;
case 'v':
argument++;
g_displayLevel++;
break;
case 's':
argument++;
seedset=1;
seed = readInt(&argument);
break;
case 'n':
argument++;
numFiles = readInt(&argument);
break;
case 'T':
argument++;
testDuration = readInt(&argument);
if (*argument == 'm') {
testDuration *= 60;
argument++;
if (*argument == 'n') argument++;
}
break;
case 'o':
argument++;
origPath = argument;
argument += strlen(argument);
break;
case 'p':
argument++;
path = argument;
argument += strlen(argument);
break;
case 't':
argument++;
testMode = 1;
break;
case '-':
argument++;
if (strcmp(argument, "content-size") == 0) {
opts.contentSize = 1;
} else {
advancedUsage(argv[0]);
return 1;
}
argument += strlen(argument);
break;
default:
usage(argv[0]);
return 1;
} } } } /* for (argNb=1; argNb<argc; argNb++) */
if (!seedset) {
seed = makeSeed();
}
if (testMode) {
return runTestMode(seed, numFiles, testDuration);
} else {
if (testDuration) {
DISPLAY("Error: -T requires test mode (-t)\n\n");
usage(argv[0]);
return 1;
}
}
if (!path) {
DISPLAY("Error: path is required in file generation mode\n");
usage(argv[0]);
return 1;
}
if (numFiles == 0) {
return generateFile(seed, path, origPath);
} else {
return generateCorpus(seed, numFiles, path, origPath);
}
}
+7
View File
@@ -545,6 +545,13 @@ static int basicUnitTests(U32 seed, double compressibility)
if (r != _3BYTESTESTLENGTH) goto _output_error; }
DISPLAYLEVEL(4, "OK \n");
/* findFrameCompressedSize on skippable frames */
DISPLAYLEVEL(4, "test%3i : frame compressed size of skippable frame : ", testNb++);
{ const char* frame = "\x50\x2a\x4d\x18\x05\x0\x0\0abcde";
size_t const frameSrcSize = 13;
if (ZSTD_findFrameCompressedSize(frame, frameSrcSize) != frameSrcSize) goto _output_error; }
DISPLAYLEVEL(4, "OK \n");
/* error string tests */
DISPLAYLEVEL(4, "test%3i : testing ZSTD error code strings : ", testNb++);
if (strcmp("No error detected", ZSTD_getErrorName((ZSTD_ErrorCode)(0-ZSTD_error_no_error))) != 0) goto _output_error;
+1
View File
@@ -16,6 +16,7 @@ static const void *symbols[] = {
&ZSTD_decompress,
&ZSTD_getDecompressedSize,
&ZSTD_findDecompressedSize,
&ZSTD_findFrameCompressedSize,
&ZSTD_getFrameContentSize,
&ZSTD_maxCLevel,
&ZSTD_compressBound,