Show More
The requested changes are too big and content was truncated. Show full diff
@@ -1,2 +1,5 b'' | |||
|
1 | graft c-ext | |
|
1 | 2 | graft zstd |
|
2 | 3 | include make_cffi.py |
|
4 | include setup_zstd.py | |
|
5 | include zstd.c |
@@ -1,63 +1,90 b'' | |||
|
1 | 1 | Version History |
|
2 | 2 | =============== |
|
3 | 3 | |
|
4 | 0.6.0 (released 2017-01-14) | |
|
5 | --------------------------- | |
|
6 | ||
|
7 | * Support for legacy zstd protocols (build time opt in feature). | |
|
8 | * Automation improvements to test against Python 3.6, latest versions | |
|
9 | of Tox, more deterministic AppVeyor behavior. | |
|
10 | * CFFI "parser" improved to use a compiler preprocessor instead of rewriting | |
|
11 | source code manually. | |
|
12 | * Vendored version of zstd updated to 1.1.2. | |
|
13 | * Documentation improvements. | |
|
14 | * Introduce a bench.py script for performing (crude) benchmarks. | |
|
15 | * ZSTD_CCtx instances are now reused across multiple compress() operations. | |
|
16 | * ZstdCompressor.write_to() now has a flush() method. | |
|
17 | * ZstdCompressor.compressobj()'s flush() method now accepts an argument to | |
|
18 | flush a block (as opposed to ending the stream). | |
|
19 | * Disallow compress(b'') when writing content sizes by default (issue #11). | |
|
20 | ||
|
21 | 0.5.2 (released 2016-11-12) | |
|
22 | --------------------------- | |
|
23 | ||
|
24 | * more packaging fixes for source distribution | |
|
25 | ||
|
26 | 0.5.1 (released 2016-11-12) | |
|
27 | --------------------------- | |
|
28 | ||
|
29 | * setup_zstd.py is included in the source distribution | |
|
30 | ||
|
4 | 31 | 0.5.0 (released 2016-11-10) |
|
5 | 32 | --------------------------- |
|
6 | 33 | |
|
7 | 34 | * Vendored version of zstd updated to 1.1.1. |
|
8 | 35 | * Continuous integration for Python 3.6 and 3.7 |
|
9 | 36 | * Continuous integration for Conda |
|
10 | 37 | * Added compression and decompression APIs providing similar interfaces |
|
11 | 38 | to the standard library ``zlib`` and ``bz2`` modules. This allows |
|
12 | 39 | coding to a common interface. |
|
13 | 40 | * ``zstd.__version__` is now defined. |
|
14 | 41 | * ``read_from()`` on various APIs now accepts objects implementing the buffer |
|
15 | 42 | protocol. |
|
16 | 43 | * ``read_from()`` has gained a ``skip_bytes`` argument. This allows callers |
|
17 | 44 | to pass in an existing buffer with a header without having to create a |
|
18 | 45 | slice or a new object. |
|
19 | 46 | * Implemented ``ZstdCompressionDict.as_bytes()``. |
|
20 | 47 | * Python's memory allocator is now used instead of ``malloc()``. |
|
21 | 48 | * Low-level zstd data structures are reused in more instances, cutting down |
|
22 | 49 | on overhead for certain operations. |
|
23 | 50 | * ``distutils`` boilerplate for obtaining an ``Extension`` instance |
|
24 | 51 | has now been refactored into a standalone ``setup_zstd.py`` file. This |
|
25 | 52 | allows other projects with ``setup.py`` files to reuse the |
|
26 | 53 | ``distutils`` code for this project without copying code. |
|
27 | 54 | * The monolithic ``zstd.c`` file has been split into a header file defining |
|
28 | 55 | types and separate ``.c`` source files for the implementation. |
|
29 | 56 | |
|
30 | 57 | History of the Project |
|
31 | 58 | ====================== |
|
32 | 59 | |
|
33 | 60 | 2016-08-31 - Zstandard 1.0.0 is released and Gregory starts hacking on a |
|
34 | 61 | Python extension for use by the Mercurial project. A very hacky prototype |
|
35 | 62 | is sent to the mercurial-devel list for RFC. |
|
36 | 63 | |
|
37 | 64 | 2016-09-03 - Most functionality from Zstandard C API implemented. Source |
|
38 | 65 | code published on https://github.com/indygreg/python-zstandard. Travis-CI |
|
39 | 66 | automation configured. 0.0.1 release on PyPI. |
|
40 | 67 | |
|
41 | 68 | 2016-09-05 - After the API was rounded out a bit and support for Python |
|
42 | 69 | 2.6 and 2.7 was added, version 0.1 was released to PyPI. |
|
43 | 70 | |
|
44 | 71 | 2016-09-05 - After the compressor and decompressor APIs were changed, 0.2 |
|
45 | 72 | was released to PyPI. |
|
46 | 73 | |
|
47 | 74 | 2016-09-10 - 0.3 is released with a bunch of new features. ZstdCompressor |
|
48 | 75 | now accepts arguments controlling frame parameters. The source size can now |
|
49 | 76 | be declared when performing streaming compression. ZstdDecompressor.decompress() |
|
50 | 77 | is implemented. Compression dictionaries are now cached when using the simple |
|
51 | 78 | compression and decompression APIs. Memory size APIs added. |
|
52 | 79 | ZstdCompressor.read_from() and ZstdDecompressor.read_from() have been |
|
53 | 80 | implemented. This rounds out the major compression/decompression APIs planned |
|
54 | 81 | by the author. |
|
55 | 82 | |
|
56 | 83 | 2016-10-02 - 0.3.3 is released with a bug fix for read_from not fully |
|
57 | 84 | decoding a zstd frame (issue #2). |
|
58 | 85 | |
|
59 | 86 | 2016-10-02 - 0.4.0 is released with zstd 1.1.0, support for custom read and |
|
60 | 87 | write buffer sizes, and a few bug fixes involving failure to read/write |
|
61 | 88 | all data when buffer sizes were too small to hold remaining data. |
|
62 | 89 | |
|
63 | 90 | 2016-11-10 - 0.5.0 is released with zstd 1.1.1 and other enhancements. |
@@ -1,776 +1,829 b'' | |||
|
1 | 1 | ================ |
|
2 | 2 | python-zstandard |
|
3 | 3 | ================ |
|
4 | 4 | |
|
5 |
This project provides |
|
|
6 | `Zstandard <http://www.zstd.net>`_ compression library. | |
|
5 | This project provides Python bindings for interfacing with the | |
|
6 | `Zstandard <http://www.zstd.net>`_ compression library. A C extension | |
|
7 | and CFFI interface is provided. | |
|
7 | 8 | |
|
8 | 9 | The primary goal of the extension is to provide a Pythonic interface to |
|
9 | 10 | the underlying C API. This means exposing most of the features and flexibility |
|
10 | 11 | of the C API while not sacrificing usability or safety that Python provides. |
|
11 | 12 | |
|
13 | The canonical home for this project is | |
|
14 | https://github.com/indygreg/python-zstandard. | |
|
15 | ||
|
12 | 16 | | |ci-status| |win-ci-status| |
|
13 | 17 | |
|
14 | 18 | State of Project |
|
15 | 19 | ================ |
|
16 | 20 | |
|
17 | 21 | The project is officially in beta state. The author is reasonably satisfied |
|
18 | 22 | with the current API and that functionality works as advertised. There |
|
19 | 23 | may be some backwards incompatible changes before 1.0. Though the author |
|
20 | 24 | does not intend to make any major changes to the Python API. |
|
21 | 25 | |
|
22 | 26 | There is continuous integration for Python versions 2.6, 2.7, and 3.3+ |
|
23 | 27 | on Linux x86_x64 and Windows x86 and x86_64. The author is reasonably |
|
24 | 28 | confident the extension is stable and works as advertised on these |
|
25 | 29 | platforms. |
|
26 | 30 | |
|
27 | 31 | Expected Changes |
|
28 | 32 | ---------------- |
|
29 | 33 | |
|
30 | 34 | The author is reasonably confident in the current state of what's |
|
31 | 35 | implemented on the ``ZstdCompressor`` and ``ZstdDecompressor`` types. |
|
32 | 36 | Those APIs likely won't change significantly. Some low-level behavior |
|
33 | 37 | (such as naming and types expected by arguments) may change. |
|
34 | 38 | |
|
35 | 39 | There will likely be arguments added to control the input and output |
|
36 | 40 | buffer sizes (currently, certain operations read and write in chunk |
|
37 | 41 | sizes using zstd's preferred defaults). |
|
38 | 42 | |
|
39 | 43 | There should be an API that accepts an object that conforms to the buffer |
|
40 | 44 | interface and returns an iterator over compressed or decompressed output. |
|
41 | 45 | |
|
42 | 46 | The author is on the fence as to whether to support the extremely |
|
43 | 47 | low level compression and decompression APIs. It could be useful to |
|
44 | 48 | support compression without the framing headers. But the author doesn't |
|
45 | 49 | believe it a high priority at this time. |
|
46 | 50 | |
|
47 | 51 | The CFFI bindings are half-baked and need to be finished. |
|
48 | 52 | |
|
49 | 53 | Requirements |
|
50 | 54 | ============ |
|
51 | 55 | |
|
52 | 56 | This extension is designed to run with Python 2.6, 2.7, 3.3, 3.4, and 3.5 |
|
53 | 57 | on common platforms (Linux, Windows, and OS X). Only x86_64 is currently |
|
54 | 58 | well-tested as an architecture. |
|
55 | 59 | |
|
56 | 60 | Installing |
|
57 | 61 | ========== |
|
58 | 62 | |
|
59 | 63 | This package is uploaded to PyPI at https://pypi.python.org/pypi/zstandard. |
|
60 | 64 | So, to install this package:: |
|
61 | 65 | |
|
62 | 66 | $ pip install zstandard |
|
63 | 67 | |
|
64 | 68 | Binary wheels are made available for some platforms. If you need to |
|
65 | 69 | install from a source distribution, all you should need is a working C |
|
66 | 70 | compiler and the Python development headers/libraries. On many Linux |
|
67 | 71 | distributions, you can install a ``python-dev`` or ``python-devel`` |
|
68 | 72 | package to provide these dependencies. |
|
69 | 73 | |
|
70 | 74 | Packages are also uploaded to Anaconda Cloud at |
|
71 | 75 | https://anaconda.org/indygreg/zstandard. See that URL for how to install |
|
72 | 76 | this package with ``conda``. |
|
73 | 77 | |
|
74 | 78 | Performance |
|
75 | 79 | =========== |
|
76 | 80 | |
|
77 | 81 | Very crude and non-scientific benchmarking (most benchmarks fall in this |
|
78 | 82 | category because proper benchmarking is hard) show that the Python bindings |
|
79 | 83 | perform within 10% of the native C implementation. |
|
80 | 84 | |
|
81 | 85 | The following table compares the performance of compressing and decompressing |
|
82 | 86 | a 1.1 GB tar file comprised of the files in a Firefox source checkout. Values |
|
83 | 87 | obtained with the ``zstd`` program are on the left. The remaining columns detail |
|
84 | 88 | performance of various compression APIs in the Python bindings. |
|
85 | 89 | |
|
86 | 90 | +-------+-----------------+-----------------+-----------------+---------------+ |
|
87 | 91 | | Level | Native | Simple | Stream In | Stream Out | |
|
88 | 92 | | | Comp / Decomp | Comp / Decomp | Comp / Decomp | Comp | |
|
89 | 93 | +=======+=================+=================+=================+===============+ |
|
90 | 94 | | 1 | 490 / 1338 MB/s | 458 / 1266 MB/s | 407 / 1156 MB/s | 405 MB/s | |
|
91 | 95 | +-------+-----------------+-----------------+-----------------+---------------+ |
|
92 | 96 | | 2 | 412 / 1288 MB/s | 381 / 1203 MB/s | 345 / 1128 MB/s | 349 MB/s | |
|
93 | 97 | +-------+-----------------+-----------------+-----------------+---------------+ |
|
94 | 98 | | 3 | 342 / 1312 MB/s | 319 / 1182 MB/s | 285 / 1165 MB/s | 287 MB/s | |
|
95 | 99 | +-------+-----------------+-----------------+-----------------+---------------+ |
|
96 | 100 | | 11 | 64 / 1506 MB/s | 66 / 1436 MB/s | 56 / 1342 MB/s | 57 MB/s | |
|
97 | 101 | +-------+-----------------+-----------------+-----------------+---------------+ |
|
98 | 102 | |
|
99 | 103 | Again, these are very unscientific. But it shows that Python is capable of |
|
100 | 104 | compressing at several hundred MB/s and decompressing at over 1 GB/s. |
|
101 | 105 | |
|
102 | 106 | Comparison to Other Python Bindings |
|
103 | 107 | =================================== |
|
104 | 108 | |
|
105 | 109 | https://pypi.python.org/pypi/zstd is an alternative Python binding to |
|
106 | 110 | Zstandard. At the time this was written, the latest release of that |
|
107 | 111 | package (1.0.0.2) had the following significant differences from this package: |
|
108 | 112 | |
|
109 | 113 | * It only exposes the simple API for compression and decompression operations. |
|
110 | 114 | This extension exposes the streaming API, dictionary training, and more. |
|
111 | 115 | * It adds a custom framing header to compressed data and there is no way to |
|
112 | 116 | disable it. This means that data produced with that module cannot be used by |
|
113 | 117 | other Zstandard implementations. |
|
114 | 118 | |
|
115 | 119 | Bundling of Zstandard Source Code |
|
116 | 120 | ================================= |
|
117 | 121 | |
|
118 | 122 | The source repository for this project contains a vendored copy of the |
|
119 | 123 | Zstandard source code. This is done for a few reasons. |
|
120 | 124 | |
|
121 | 125 | First, Zstandard is relatively new and not yet widely available as a system |
|
122 | 126 | package. Providing a copy of the source code enables the Python C extension |
|
123 | 127 | to be compiled without requiring the user to obtain the Zstandard source code |
|
124 | 128 | separately. |
|
125 | 129 | |
|
126 | 130 | Second, Zstandard has both a stable *public* API and an *experimental* API. |
|
127 | 131 | The *experimental* API is actually quite useful (contains functionality for |
|
128 | 132 | training dictionaries for example), so it is something we wish to expose to |
|
129 | 133 | Python. However, the *experimental* API is only available via static linking. |
|
130 | 134 | Furthermore, the *experimental* API can change at any time. So, control over |
|
131 | 135 | the exact version of the Zstandard library linked against is important to |
|
132 | 136 | ensure known behavior. |
|
133 | 137 | |
|
134 | 138 | Instructions for Building and Testing |
|
135 | 139 | ===================================== |
|
136 | 140 | |
|
137 | 141 | Once you have the source code, the extension can be built via setup.py:: |
|
138 | 142 | |
|
139 | 143 | $ python setup.py build_ext |
|
140 | 144 | |
|
141 | 145 | We recommend testing with ``nose``:: |
|
142 | 146 | |
|
143 | 147 | $ nosetests |
|
144 | 148 | |
|
145 | 149 | A Tox configuration is present to test against multiple Python versions:: |
|
146 | 150 | |
|
147 | 151 | $ tox |
|
148 | 152 | |
|
149 | 153 | Tests use the ``hypothesis`` Python package to perform fuzzing. If you |
|
150 | 154 | don't have it, those tests won't run. |
|
151 | 155 | |
|
152 | 156 | There is also an experimental CFFI module. You need the ``cffi`` Python |
|
153 | 157 | package installed to build and test that. |
|
154 | 158 | |
|
155 | 159 | To create a virtualenv with all development dependencies, do something |
|
156 | 160 | like the following:: |
|
157 | 161 | |
|
158 | 162 | # Python 2 |
|
159 | 163 | $ virtualenv venv |
|
160 | 164 | |
|
161 | 165 | # Python 3 |
|
162 | 166 | $ python3 -m venv venv |
|
163 | 167 | |
|
164 | 168 | $ source venv/bin/activate |
|
165 | 169 | $ pip install cffi hypothesis nose tox |
|
166 | 170 | |
|
167 | 171 | API |
|
168 | 172 | === |
|
169 | 173 | |
|
170 | 174 | The compiled C extension provides a ``zstd`` Python module. This module |
|
171 | 175 | exposes the following interfaces. |
|
172 | 176 | |
|
173 | 177 | ZstdCompressor |
|
174 | 178 | -------------- |
|
175 | 179 | |
|
176 | 180 | The ``ZstdCompressor`` class provides an interface for performing |
|
177 | 181 | compression operations. |
|
178 | 182 | |
|
179 | 183 | Each instance is associated with parameters that control compression |
|
180 | 184 | behavior. These come from the following named arguments (all optional): |
|
181 | 185 | |
|
182 | 186 | level |
|
183 | 187 | Integer compression level. Valid values are between 1 and 22. |
|
184 | 188 | dict_data |
|
185 | 189 | Compression dictionary to use. |
|
186 | 190 | |
|
187 | 191 | Note: When using dictionary data and ``compress()`` is called multiple |
|
188 | 192 | times, the ``CompressionParameters`` derived from an integer compression |
|
189 | 193 | ``level`` and the first compressed data's size will be reused for all |
|
190 | 194 | subsequent operations. This may not be desirable if source data size |
|
191 | 195 | varies significantly. |
|
192 | 196 | compression_params |
|
193 | 197 | A ``CompressionParameters`` instance (overrides the ``level`` value). |
|
194 | 198 | write_checksum |
|
195 | 199 | Whether a 4 byte checksum should be written with the compressed data. |
|
196 | 200 | Defaults to False. If True, the decompressor can verify that decompressed |
|
197 | 201 | data matches the original input data. |
|
198 | 202 | write_content_size |
|
199 | 203 | Whether the size of the uncompressed data will be written into the |
|
200 | 204 | header of compressed data. Defaults to False. The data will only be |
|
201 | 205 | written if the compressor knows the size of the input data. This is |
|
202 | 206 | likely not true for streaming compression. |
|
203 | 207 | write_dict_id |
|
204 | 208 | Whether to write the dictionary ID into the compressed data. |
|
205 | 209 | Defaults to True. The dictionary ID is only written if a dictionary |
|
206 | 210 | is being used. |
|
207 | 211 | |
|
212 | Unless specified otherwise, assume that no two methods of ``ZstdCompressor`` | |
|
213 | instances can be called from multiple Python threads simultaneously. In other | |
|
214 | words, assume instances are not thread safe unless stated otherwise. | |
|
215 | ||
|
208 | 216 | Simple API |
|
209 | 217 | ^^^^^^^^^^ |
|
210 | 218 | |
|
211 | 219 | ``compress(data)`` compresses and returns data as a one-shot operation.:: |
|
212 | 220 | |
|
213 | cctx = zstd.ZsdCompressor() | |
|
221 | cctx = zstd.ZstdCompressor() | |
|
214 | 222 | compressed = cctx.compress(b'data to compress') |
|
215 | 223 | |
|
224 | Unless ``compression_params`` or ``dict_data`` are passed to the | |
|
225 | ``ZstdCompressor``, each invocation of ``compress()`` will calculate the | |
|
226 | optimal compression parameters for the configured compression ``level`` and | |
|
227 | input data size (some parameters are fine-tuned for small input sizes). | |
|
228 | ||
|
229 | If a compression dictionary is being used, the compression parameters | |
|
230 | determined from the first input's size will be reused for subsequent | |
|
231 | operations. | |
|
232 | ||
|
233 | There is currently a deficiency in zstd's C APIs that makes it difficult | |
|
234 | to round trip empty inputs when ``write_content_size=True``. Attempting | |
|
235 | this will raise a ``ValueError`` unless ``allow_empty=True`` is passed | |
|
236 | to ``compress()``. | |
|
237 | ||
|
216 | 238 | Streaming Input API |
|
217 | 239 | ^^^^^^^^^^^^^^^^^^^ |
|
218 | 240 | |
|
219 | 241 | ``write_to(fh)`` (which behaves as a context manager) allows you to *stream* |
|
220 | 242 | data into a compressor.:: |
|
221 | 243 | |
|
222 | 244 | cctx = zstd.ZstdCompressor(level=10) |
|
223 | 245 | with cctx.write_to(fh) as compressor: |
|
224 | 246 | compressor.write(b'chunk 0') |
|
225 | 247 | compressor.write(b'chunk 1') |
|
226 | 248 | ... |
|
227 | 249 | |
|
228 | 250 | The argument to ``write_to()`` must have a ``write(data)`` method. As |
|
229 |
compressed data is available, ``write()`` will be called with the com |
|
|
251 | compressed data is available, ``write()`` will be called with the compressed | |
|
230 | 252 | data as its argument. Many common Python types implement ``write()``, including |
|
231 | 253 | open file handles and ``io.BytesIO``. |
|
232 | 254 | |
|
233 | 255 | ``write_to()`` returns an object representing a streaming compressor instance. |
|
234 | 256 | It **must** be used as a context manager. That object's ``write(data)`` method |
|
235 | 257 | is used to feed data into the compressor. |
|
236 | 258 | |
|
259 | A ``flush()`` method can be called to evict whatever data remains within the | |
|
260 | compressor's internal state into the output object. This may result in 0 or | |
|
261 | more ``write()`` calls to the output object. | |
|
262 | ||
|
237 | 263 | If the size of the data being fed to this streaming compressor is known, |
|
238 | 264 | you can declare it before compression begins:: |
|
239 | 265 | |
|
240 | 266 | cctx = zstd.ZstdCompressor() |
|
241 | 267 | with cctx.write_to(fh, size=data_len) as compressor: |
|
242 | 268 | compressor.write(chunk0) |
|
243 | 269 | compressor.write(chunk1) |
|
244 | 270 | ... |
|
245 | 271 | |
|
246 | 272 | Declaring the size of the source data allows compression parameters to |
|
247 | 273 | be tuned. And if ``write_content_size`` is used, it also results in the |
|
248 | 274 | content size being written into the frame header of the output data. |
|
249 | 275 | |
|
250 | 276 | The size of chunks being ``write()`` to the destination can be specified:: |
|
251 | 277 | |
|
252 | 278 | cctx = zstd.ZstdCompressor() |
|
253 | 279 | with cctx.write_to(fh, write_size=32768) as compressor: |
|
254 | 280 | ... |
|
255 | 281 | |
|
256 | 282 | To see how much memory is being used by the streaming compressor:: |
|
257 | 283 | |
|
258 | 284 | cctx = zstd.ZstdCompressor() |
|
259 | 285 | with cctx.write_to(fh) as compressor: |
|
260 | 286 | ... |
|
261 | 287 | byte_size = compressor.memory_size() |
|
262 | 288 | |
|
263 | 289 | Streaming Output API |
|
264 | 290 | ^^^^^^^^^^^^^^^^^^^^ |
|
265 | 291 | |
|
266 | 292 | ``read_from(reader)`` provides a mechanism to stream data out of a compressor |
|
267 | 293 | as an iterator of data chunks.:: |
|
268 | 294 | |
|
269 | 295 | cctx = zstd.ZstdCompressor() |
|
270 | 296 | for chunk in cctx.read_from(fh): |
|
271 | 297 | # Do something with emitted data. |
|
272 | 298 | |
|
273 | 299 | ``read_from()`` accepts an object that has a ``read(size)`` method or conforms |
|
274 | 300 | to the buffer protocol. (``bytes`` and ``memoryview`` are 2 common types that |
|
275 | 301 | provide the buffer protocol.) |
|
276 | 302 | |
|
277 | 303 | Uncompressed data is fetched from the source either by calling ``read(size)`` |
|
278 | 304 | or by fetching a slice of data from the object directly (in the case where |
|
279 | 305 | the buffer protocol is being used). The returned iterator consists of chunks |
|
280 | 306 | of compressed data. |
|
281 | 307 | |
|
308 | If reading from the source via ``read()``, ``read()`` will be called until | |
|
309 | it raises or returns an empty bytes (``b''``). It is perfectly valid for | |
|
310 | the source to deliver fewer bytes than were what requested by ``read(size)``. | |
|
311 | ||
|
282 | 312 | Like ``write_to()``, ``read_from()`` also accepts a ``size`` argument |
|
283 | 313 | declaring the size of the input stream:: |
|
284 | 314 | |
|
285 | 315 | cctx = zstd.ZstdCompressor() |
|
286 | 316 | for chunk in cctx.read_from(fh, size=some_int): |
|
287 | 317 | pass |
|
288 | 318 | |
|
289 | 319 | You can also control the size that data is ``read()`` from the source and |
|
290 | 320 | the ideal size of output chunks:: |
|
291 | 321 | |
|
292 | 322 | cctx = zstd.ZstdCompressor() |
|
293 | 323 | for chunk in cctx.read_from(fh, read_size=16384, write_size=8192): |
|
294 | 324 | pass |
|
295 | 325 | |
|
326 | Unlike ``write_to()``, ``read_from()`` does not give direct control over the | |
|
327 | sizes of chunks fed into the compressor. Instead, chunk sizes will be whatever | |
|
328 | the object being read from delivers. These will often be of a uniform size. | |
|
329 | ||
|
296 | 330 | Stream Copying API |
|
297 | 331 | ^^^^^^^^^^^^^^^^^^ |
|
298 | 332 | |
|
299 | 333 | ``copy_stream(ifh, ofh)`` can be used to copy data between 2 streams while |
|
300 | 334 | compressing it.:: |
|
301 | 335 | |
|
302 | 336 | cctx = zstd.ZstdCompressor() |
|
303 | 337 | cctx.copy_stream(ifh, ofh) |
|
304 | 338 | |
|
305 | 339 | For example, say you wish to compress a file:: |
|
306 | 340 | |
|
307 | 341 | cctx = zstd.ZstdCompressor() |
|
308 | 342 | with open(input_path, 'rb') as ifh, open(output_path, 'wb') as ofh: |
|
309 | 343 | cctx.copy_stream(ifh, ofh) |
|
310 | 344 | |
|
311 | 345 | It is also possible to declare the size of the source stream:: |
|
312 | 346 | |
|
313 | 347 | cctx = zstd.ZstdCompressor() |
|
314 | 348 | cctx.copy_stream(ifh, ofh, size=len_of_input) |
|
315 | 349 | |
|
316 | 350 | You can also specify how large the chunks that are ``read()`` and ``write()`` |
|
317 | 351 | from and to the streams:: |
|
318 | 352 | |
|
319 | 353 | cctx = zstd.ZstdCompressor() |
|
320 | 354 | cctx.copy_stream(ifh, ofh, read_size=32768, write_size=16384) |
|
321 | 355 | |
|
322 | 356 | The stream copier returns a 2-tuple of bytes read and written:: |
|
323 | 357 | |
|
324 | 358 | cctx = zstd.ZstdCompressor() |
|
325 | 359 | read_count, write_count = cctx.copy_stream(ifh, ofh) |
|
326 | 360 | |
|
327 | 361 | Compressor API |
|
328 | 362 | ^^^^^^^^^^^^^^ |
|
329 | 363 | |
|
330 | 364 | ``compressobj()`` returns an object that exposes ``compress(data)`` and |
|
331 | 365 | ``flush()`` methods. Each returns compressed data or an empty bytes. |
|
332 | 366 | |
|
333 | 367 | The purpose of ``compressobj()`` is to provide an API-compatible interface |
|
334 | 368 | with ``zlib.compressobj`` and ``bz2.BZ2Compressor``. This allows callers to |
|
335 | 369 | swap in different compressor objects while using the same API. |
|
336 | 370 | |
|
337 | Once ``flush()`` is called, the compressor will no longer accept new data | |
|
338 | to ``compress()``. ``flush()`` **must** be called to end the compression | |
|
339 | context. If not called, the returned data may be incomplete. | |
|
371 | ``flush()`` accepts an optional argument indicating how to end the stream. | |
|
372 | ``zstd.COMPRESSOBJ_FLUSH_FINISH`` (the default) ends the compression stream. | |
|
373 | Once this type of flush is performed, ``compress()`` and ``flush()`` can | |
|
374 | no longer be called. This type of flush **must** be called to end the | |
|
375 | compression context. If not called, returned data may be incomplete. | |
|
376 | ||
|
377 | A ``zstd.COMPRESSOBJ_FLUSH_BLOCK`` argument to ``flush()`` will flush a | |
|
378 | zstd block. Flushes of this type can be performed multiple times. The next | |
|
379 | call to ``compress()`` will begin a new zstd block. | |
|
340 | 380 | |
|
341 | 381 | Here is how this API should be used:: |
|
342 | 382 | |
|
343 | 383 | cctx = zstd.ZstdCompressor() |
|
344 | 384 | cobj = cctx.compressobj() |
|
345 | 385 | data = cobj.compress(b'raw input 0') |
|
346 | 386 | data = cobj.compress(b'raw input 1') |
|
347 | 387 | data = cobj.flush() |
|
348 | 388 | |
|
389 | Or to flush blocks:: | |
|
390 | ||
|
391 | cctx.zstd.ZstdCompressor() | |
|
392 | cobj = cctx.compressobj() | |
|
393 | data = cobj.compress(b'chunk in first block') | |
|
394 | data = cobj.flush(zstd.COMPRESSOBJ_FLUSH_BLOCK) | |
|
395 | data = cobj.compress(b'chunk in second block') | |
|
396 | data = cobj.flush() | |
|
397 | ||
|
349 | 398 | For best performance results, keep input chunks under 256KB. This avoids |
|
350 | 399 | extra allocations for a large output object. |
|
351 | 400 | |
|
352 | 401 | It is possible to declare the input size of the data that will be fed into |
|
353 | 402 | the compressor:: |
|
354 | 403 | |
|
355 | 404 | cctx = zstd.ZstdCompressor() |
|
356 | 405 | cobj = cctx.compressobj(size=6) |
|
357 | 406 | data = cobj.compress(b'foobar') |
|
358 | 407 | data = cobj.flush() |
|
359 | 408 | |
|
360 | 409 | ZstdDecompressor |
|
361 | 410 | ---------------- |
|
362 | 411 | |
|
363 | 412 | The ``ZstdDecompressor`` class provides an interface for performing |
|
364 | 413 | decompression. |
|
365 | 414 | |
|
366 | 415 | Each instance is associated with parameters that control decompression. These |
|
367 | 416 | come from the following named arguments (all optional): |
|
368 | 417 | |
|
369 | 418 | dict_data |
|
370 | 419 | Compression dictionary to use. |
|
371 | 420 | |
|
372 | 421 | The interface of this class is very similar to ``ZstdCompressor`` (by design). |
|
373 | 422 | |
|
423 | Unless specified otherwise, assume that no two methods of ``ZstdDecompressor`` | |
|
424 | instances can be called from multiple Python threads simultaneously. In other | |
|
425 | words, assume instances are not thread safe unless stated otherwise. | |
|
426 | ||
|
374 | 427 | Simple API |
|
375 | 428 | ^^^^^^^^^^ |
|
376 | 429 | |
|
377 | 430 | ``decompress(data)`` can be used to decompress an entire compressed zstd |
|
378 | 431 | frame in a single operation.:: |
|
379 | 432 | |
|
380 | 433 | dctx = zstd.ZstdDecompressor() |
|
381 | 434 | decompressed = dctx.decompress(data) |
|
382 | 435 | |
|
383 | 436 | By default, ``decompress(data)`` will only work on data written with the content |
|
384 | 437 | size encoded in its header. This can be achieved by creating a |
|
385 | 438 | ``ZstdCompressor`` with ``write_content_size=True``. If compressed data without |
|
386 | 439 | an embedded content size is seen, ``zstd.ZstdError`` will be raised. |
|
387 | 440 | |
|
388 | 441 | If the compressed data doesn't have its content size embedded within it, |
|
389 | 442 | decompression can be attempted by specifying the ``max_output_size`` |
|
390 | 443 | argument.:: |
|
391 | 444 | |
|
392 | 445 | dctx = zstd.ZstdDecompressor() |
|
393 | 446 | uncompressed = dctx.decompress(data, max_output_size=1048576) |
|
394 | 447 | |
|
395 | 448 | Ideally, ``max_output_size`` will be identical to the decompressed output |
|
396 | 449 | size. |
|
397 | 450 | |
|
398 | 451 | If ``max_output_size`` is too small to hold the decompressed data, |
|
399 | 452 | ``zstd.ZstdError`` will be raised. |
|
400 | 453 | |
|
401 | 454 | If ``max_output_size`` is larger than the decompressed data, the allocated |
|
402 | 455 | output buffer will be resized to only use the space required. |
|
403 | 456 | |
|
404 | 457 | Please note that an allocation of the requested ``max_output_size`` will be |
|
405 | 458 | performed every time the method is called. Setting to a very large value could |
|
406 | 459 | result in a lot of work for the memory allocator and may result in |
|
407 | 460 | ``MemoryError`` being raised if the allocation fails. |
|
408 | 461 | |
|
409 | 462 | If the exact size of decompressed data is unknown, it is **strongly** |
|
410 | 463 | recommended to use a streaming API. |
|
411 | 464 | |
|
412 | 465 | Streaming Input API |
|
413 | 466 | ^^^^^^^^^^^^^^^^^^^ |
|
414 | 467 | |
|
415 | 468 | ``write_to(fh)`` can be used to incrementally send compressed data to a |
|
416 | 469 | decompressor.:: |
|
417 | 470 | |
|
418 | 471 | dctx = zstd.ZstdDecompressor() |
|
419 | 472 | with dctx.write_to(fh) as decompressor: |
|
420 | 473 | decompressor.write(compressed_data) |
|
421 | 474 | |
|
422 | 475 | This behaves similarly to ``zstd.ZstdCompressor``: compressed data is written to |
|
423 | 476 | the decompressor by calling ``write(data)`` and decompressed output is written |
|
424 | 477 | to the output object by calling its ``write(data)`` method. |
|
425 | 478 | |
|
426 | 479 | The size of chunks being ``write()`` to the destination can be specified:: |
|
427 | 480 | |
|
428 | 481 | dctx = zstd.ZstdDecompressor() |
|
429 | 482 | with dctx.write_to(fh, write_size=16384) as decompressor: |
|
430 | 483 | pass |
|
431 | 484 | |
|
432 | 485 | You can see how much memory is being used by the decompressor:: |
|
433 | 486 | |
|
434 | 487 | dctx = zstd.ZstdDecompressor() |
|
435 | 488 | with dctx.write_to(fh) as decompressor: |
|
436 | 489 | byte_size = decompressor.memory_size() |
|
437 | 490 | |
|
438 | 491 | Streaming Output API |
|
439 | 492 | ^^^^^^^^^^^^^^^^^^^^ |
|
440 | 493 | |
|
441 | 494 | ``read_from(fh)`` provides a mechanism to stream decompressed data out of a |
|
442 | 495 | compressed source as an iterator of data chunks.:: |
|
443 | 496 | |
|
444 | 497 | dctx = zstd.ZstdDecompressor() |
|
445 | 498 | for chunk in dctx.read_from(fh): |
|
446 | 499 | # Do something with original data. |
|
447 | 500 | |
|
448 | 501 | ``read_from()`` accepts a) an object with a ``read(size)`` method that will |
|
449 | 502 | return compressed bytes b) an object conforming to the buffer protocol that |
|
450 | 503 | can expose its data as a contiguous range of bytes. The ``bytes`` and |
|
451 | 504 | ``memoryview`` types expose this buffer protocol. |
|
452 | 505 | |
|
453 | 506 | ``read_from()`` returns an iterator whose elements are chunks of the |
|
454 | 507 | decompressed data. |
|
455 | 508 | |
|
456 | 509 | The size of requested ``read()`` from the source can be specified:: |
|
457 | 510 | |
|
458 | 511 | dctx = zstd.ZstdDecompressor() |
|
459 | 512 | for chunk in dctx.read_from(fh, read_size=16384): |
|
460 | 513 | pass |
|
461 | 514 | |
|
462 | 515 | It is also possible to skip leading bytes in the input data:: |
|
463 | 516 | |
|
464 | 517 | dctx = zstd.ZstdDecompressor() |
|
465 | 518 | for chunk in dctx.read_from(fh, skip_bytes=1): |
|
466 | 519 | pass |
|
467 | 520 | |
|
468 | 521 | Skipping leading bytes is useful if the source data contains extra |
|
469 | 522 | *header* data but you want to avoid the overhead of making a buffer copy |
|
470 | 523 | or allocating a new ``memoryview`` object in order to decompress the data. |
|
471 | 524 | |
|
472 | 525 | Similarly to ``ZstdCompressor.read_from()``, the consumer of the iterator |
|
473 | 526 | controls when data is decompressed. If the iterator isn't consumed, |
|
474 | 527 | decompression is put on hold. |
|
475 | 528 | |
|
476 | 529 | When ``read_from()`` is passed an object conforming to the buffer protocol, |
|
477 | 530 | the behavior may seem similar to what occurs when the simple decompression |
|
478 | 531 | API is used. However, this API works when the decompressed size is unknown. |
|
479 | 532 | Furthermore, if feeding large inputs, the decompressor will work in chunks |
|
480 | 533 | instead of performing a single operation. |
|
481 | 534 | |
|
482 | 535 | Stream Copying API |
|
483 | 536 | ^^^^^^^^^^^^^^^^^^ |
|
484 | 537 | |
|
485 | 538 | ``copy_stream(ifh, ofh)`` can be used to copy data across 2 streams while |
|
486 | 539 | performing decompression.:: |
|
487 | 540 | |
|
488 | 541 | dctx = zstd.ZstdDecompressor() |
|
489 | 542 | dctx.copy_stream(ifh, ofh) |
|
490 | 543 | |
|
491 | 544 | e.g. to decompress a file to another file:: |
|
492 | 545 | |
|
493 | 546 | dctx = zstd.ZstdDecompressor() |
|
494 | 547 | with open(input_path, 'rb') as ifh, open(output_path, 'wb') as ofh: |
|
495 | 548 | dctx.copy_stream(ifh, ofh) |
|
496 | 549 | |
|
497 | 550 | The size of chunks being ``read()`` and ``write()`` from and to the streams |
|
498 | 551 | can be specified:: |
|
499 | 552 | |
|
500 | 553 | dctx = zstd.ZstdDecompressor() |
|
501 | 554 | dctx.copy_stream(ifh, ofh, read_size=8192, write_size=16384) |
|
502 | 555 | |
|
503 | 556 | Decompressor API |
|
504 | 557 | ^^^^^^^^^^^^^^^^ |
|
505 | 558 | |
|
506 | 559 | ``decompressobj()`` returns an object that exposes a ``decompress(data)`` |
|
507 | 560 | methods. Compressed data chunks are fed into ``decompress(data)`` and |
|
508 | 561 | uncompressed output (or an empty bytes) is returned. Output from subsequent |
|
509 | 562 | calls needs to be concatenated to reassemble the full decompressed byte |
|
510 | 563 | sequence. |
|
511 | 564 | |
|
512 | 565 | The purpose of ``decompressobj()`` is to provide an API-compatible interface |
|
513 | 566 | with ``zlib.decompressobj`` and ``bz2.BZ2Decompressor``. This allows callers |
|
514 | 567 | to swap in different decompressor objects while using the same API. |
|
515 | 568 | |
|
516 | 569 | Each object is single use: once an input frame is decoded, ``decompress()`` |
|
517 | 570 | can no longer be called. |
|
518 | 571 | |
|
519 | 572 | Here is how this API should be used:: |
|
520 | 573 | |
|
521 | 574 | dctx = zstd.ZstdDeompressor() |
|
522 | 575 | dobj = cctx.decompressobj() |
|
523 | 576 | data = dobj.decompress(compressed_chunk_0) |
|
524 | 577 | data = dobj.decompress(compressed_chunk_1) |
|
525 | 578 | |
|
526 | 579 | Choosing an API |
|
527 | 580 | --------------- |
|
528 | 581 | |
|
529 | 582 | Various forms of compression and decompression APIs are provided because each |
|
530 | 583 | are suitable for different use cases. |
|
531 | 584 | |
|
532 | 585 | The simple/one-shot APIs are useful for small data, when the decompressed |
|
533 | 586 | data size is known (either recorded in the zstd frame header via |
|
534 | 587 | ``write_content_size`` or known via an out-of-band mechanism, such as a file |
|
535 | 588 | size). |
|
536 | 589 | |
|
537 | 590 | A limitation of the simple APIs is that input or output data must fit in memory. |
|
538 | 591 | And unless using advanced tricks with Python *buffer objects*, both input and |
|
539 | 592 | output must fit in memory simultaneously. |
|
540 | 593 | |
|
541 | 594 | Another limitation is that compression or decompression is performed as a single |
|
542 | 595 | operation. So if you feed large input, it could take a long time for the |
|
543 | 596 | function to return. |
|
544 | 597 | |
|
545 | 598 | The streaming APIs do not have the limitations of the simple API. The cost to |
|
546 | 599 | this is they are more complex to use than a single function call. |
|
547 | 600 | |
|
548 | 601 | The streaming APIs put the caller in control of compression and decompression |
|
549 | 602 | behavior by allowing them to directly control either the input or output side |
|
550 | 603 | of the operation. |
|
551 | 604 | |
|
552 | 605 | With the streaming input APIs, the caller feeds data into the compressor or |
|
553 | 606 | decompressor as they see fit. Output data will only be written after the caller |
|
554 | 607 | has explicitly written data. |
|
555 | 608 | |
|
556 | 609 | With the streaming output APIs, the caller consumes output from the compressor |
|
557 | 610 | or decompressor as they see fit. The compressor or decompressor will only |
|
558 | 611 | consume data from the source when the caller is ready to receive it. |
|
559 | 612 | |
|
560 | 613 | One end of the streaming APIs involves a file-like object that must |
|
561 | 614 | ``write()`` output data or ``read()`` input data. Depending on what the |
|
562 | 615 | backing storage for these objects is, those operations may not complete quickly. |
|
563 | 616 | For example, when streaming compressed data to a file, the ``write()`` into |
|
564 | 617 | a streaming compressor could result in a ``write()`` to the filesystem, which |
|
565 | 618 | may take a long time to finish due to slow I/O on the filesystem. So, there |
|
566 | 619 | may be overhead in streaming APIs beyond the compression and decompression |
|
567 | 620 | operations. |
|
568 | 621 | |
|
569 | 622 | Dictionary Creation and Management |
|
570 | 623 | ---------------------------------- |
|
571 | 624 | |
|
572 | 625 | Zstandard allows *dictionaries* to be used when compressing and |
|
573 | 626 | decompressing data. The idea is that if you are compressing a lot of similar |
|
574 | 627 | data, you can precompute common properties of that data (such as recurring |
|
575 | 628 | byte sequences) to achieve better compression ratios. |
|
576 | 629 | |
|
577 | 630 | In Python, compression dictionaries are represented as the |
|
578 | 631 | ``ZstdCompressionDict`` type. |
|
579 | 632 | |
|
580 | 633 | Instances can be constructed from bytes:: |
|
581 | 634 | |
|
582 | 635 | dict_data = zstd.ZstdCompressionDict(data) |
|
583 | 636 | |
|
584 | 637 | More interestingly, instances can be created by *training* on sample data:: |
|
585 | 638 | |
|
586 | 639 | dict_data = zstd.train_dictionary(size, samples) |
|
587 | 640 | |
|
588 | 641 | This takes a list of bytes instances and creates and returns a |
|
589 | 642 | ``ZstdCompressionDict``. |
|
590 | 643 | |
|
591 | 644 | You can see how many bytes are in the dictionary by calling ``len()``:: |
|
592 | 645 | |
|
593 | 646 | dict_data = zstd.train_dictionary(size, samples) |
|
594 | 647 | dict_size = len(dict_data) # will not be larger than ``size`` |
|
595 | 648 | |
|
596 | 649 | Once you have a dictionary, you can pass it to the objects performing |
|
597 | 650 | compression and decompression:: |
|
598 | 651 | |
|
599 | 652 | dict_data = zstd.train_dictionary(16384, samples) |
|
600 | 653 | |
|
601 | 654 | cctx = zstd.ZstdCompressor(dict_data=dict_data) |
|
602 | 655 | for source_data in input_data: |
|
603 | 656 | compressed = cctx.compress(source_data) |
|
604 | 657 | # Do something with compressed data. |
|
605 | 658 | |
|
606 | 659 | dctx = zstd.ZstdDecompressor(dict_data=dict_data) |
|
607 | 660 | for compressed_data in input_data: |
|
608 | 661 | buffer = io.BytesIO() |
|
609 | 662 | with dctx.write_to(buffer) as decompressor: |
|
610 | 663 | decompressor.write(compressed_data) |
|
611 | 664 | # Do something with raw data in ``buffer``. |
|
612 | 665 | |
|
613 | 666 | Dictionaries have unique integer IDs. You can retrieve this ID via:: |
|
614 | 667 | |
|
615 | 668 | dict_id = zstd.dictionary_id(dict_data) |
|
616 | 669 | |
|
617 | 670 | You can obtain the raw data in the dict (useful for persisting and constructing |
|
618 | 671 | a ``ZstdCompressionDict`` later) via ``as_bytes()``:: |
|
619 | 672 | |
|
620 | 673 | dict_data = zstd.train_dictionary(size, samples) |
|
621 | 674 | raw_data = dict_data.as_bytes() |
|
622 | 675 | |
|
623 | 676 | Explicit Compression Parameters |
|
624 | 677 | ------------------------------- |
|
625 | 678 | |
|
626 | 679 | Zstandard's integer compression levels along with the input size and dictionary |
|
627 | 680 | size are converted into a data structure defining multiple parameters to tune |
|
628 | 681 | behavior of the compression algorithm. It is possible to use define this |
|
629 | 682 | data structure explicitly to have lower-level control over compression behavior. |
|
630 | 683 | |
|
631 | 684 | The ``zstd.CompressionParameters`` type represents this data structure. |
|
632 | 685 | You can see how Zstandard converts compression levels to this data structure |
|
633 | 686 | by calling ``zstd.get_compression_parameters()``. e.g.:: |
|
634 | 687 | |
|
635 | 688 | params = zstd.get_compression_parameters(5) |
|
636 | 689 | |
|
637 | 690 | This function also accepts the uncompressed data size and dictionary size |
|
638 | 691 | to adjust parameters:: |
|
639 | 692 | |
|
640 | 693 | params = zstd.get_compression_parameters(3, source_size=len(data), dict_size=len(dict_data)) |
|
641 | 694 | |
|
642 | 695 | You can also construct compression parameters from their low-level components:: |
|
643 | 696 | |
|
644 | 697 | params = zstd.CompressionParameters(20, 6, 12, 5, 4, 10, zstd.STRATEGY_FAST) |
|
645 | 698 | |
|
646 | 699 | You can then configure a compressor to use the custom parameters:: |
|
647 | 700 | |
|
648 | 701 | cctx = zstd.ZstdCompressor(compression_params=params) |
|
649 | 702 | |
|
650 | 703 | The members of the ``CompressionParameters`` tuple are as follows:: |
|
651 | 704 | |
|
652 | 705 | * 0 - Window log |
|
653 | 706 | * 1 - Chain log |
|
654 | 707 | * 2 - Hash log |
|
655 | 708 | * 3 - Search log |
|
656 | 709 | * 4 - Search length |
|
657 | 710 | * 5 - Target length |
|
658 | 711 | * 6 - Strategy (one of the ``zstd.STRATEGY_`` constants) |
|
659 | 712 | |
|
660 | 713 | You'll need to read the Zstandard documentation for what these parameters |
|
661 | 714 | do. |
|
662 | 715 | |
|
663 | 716 | Misc Functionality |
|
664 | 717 | ------------------ |
|
665 | 718 | |
|
666 | 719 | estimate_compression_context_size(CompressionParameters) |
|
667 | 720 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
|
668 | 721 | |
|
669 | 722 | Given a ``CompressionParameters`` struct, estimate the memory size required |
|
670 | 723 | to perform compression. |
|
671 | 724 | |
|
672 | 725 | estimate_decompression_context_size() |
|
673 | 726 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
|
674 | 727 | |
|
675 | 728 | Estimate the memory size requirements for a decompressor instance. |
|
676 | 729 | |
|
677 | 730 | Constants |
|
678 | 731 | --------- |
|
679 | 732 | |
|
680 | 733 | The following module constants/attributes are exposed: |
|
681 | 734 | |
|
682 | 735 | ZSTD_VERSION |
|
683 | 736 | This module attribute exposes a 3-tuple of the Zstandard version. e.g. |
|
684 | 737 | ``(1, 0, 0)`` |
|
685 | 738 | MAX_COMPRESSION_LEVEL |
|
686 | 739 | Integer max compression level accepted by compression functions |
|
687 | 740 | COMPRESSION_RECOMMENDED_INPUT_SIZE |
|
688 | 741 | Recommended chunk size to feed to compressor functions |
|
689 | 742 | COMPRESSION_RECOMMENDED_OUTPUT_SIZE |
|
690 | 743 | Recommended chunk size for compression output |
|
691 | 744 | DECOMPRESSION_RECOMMENDED_INPUT_SIZE |
|
692 | 745 | Recommended chunk size to feed into decompresor functions |
|
693 | 746 | DECOMPRESSION_RECOMMENDED_OUTPUT_SIZE |
|
694 | 747 | Recommended chunk size for decompression output |
|
695 | 748 | |
|
696 | 749 | FRAME_HEADER |
|
697 | 750 | bytes containing header of the Zstandard frame |
|
698 | 751 | MAGIC_NUMBER |
|
699 | 752 | Frame header as an integer |
|
700 | 753 | |
|
701 | 754 | WINDOWLOG_MIN |
|
702 | 755 | Minimum value for compression parameter |
|
703 | 756 | WINDOWLOG_MAX |
|
704 | 757 | Maximum value for compression parameter |
|
705 | 758 | CHAINLOG_MIN |
|
706 | 759 | Minimum value for compression parameter |
|
707 | 760 | CHAINLOG_MAX |
|
708 | 761 | Maximum value for compression parameter |
|
709 | 762 | HASHLOG_MIN |
|
710 | 763 | Minimum value for compression parameter |
|
711 | 764 | HASHLOG_MAX |
|
712 | 765 | Maximum value for compression parameter |
|
713 | 766 | SEARCHLOG_MIN |
|
714 | 767 | Minimum value for compression parameter |
|
715 | 768 | SEARCHLOG_MAX |
|
716 | 769 | Maximum value for compression parameter |
|
717 | 770 | SEARCHLENGTH_MIN |
|
718 | 771 | Minimum value for compression parameter |
|
719 | 772 | SEARCHLENGTH_MAX |
|
720 | 773 | Maximum value for compression parameter |
|
721 | 774 | TARGETLENGTH_MIN |
|
722 | 775 | Minimum value for compression parameter |
|
723 | 776 | TARGETLENGTH_MAX |
|
724 | 777 | Maximum value for compression parameter |
|
725 | 778 | STRATEGY_FAST |
|
726 | 779 | Compression strategory |
|
727 | 780 | STRATEGY_DFAST |
|
728 | 781 | Compression strategory |
|
729 | 782 | STRATEGY_GREEDY |
|
730 | 783 | Compression strategory |
|
731 | 784 | STRATEGY_LAZY |
|
732 | 785 | Compression strategory |
|
733 | 786 | STRATEGY_LAZY2 |
|
734 | 787 | Compression strategory |
|
735 | 788 | STRATEGY_BTLAZY2 |
|
736 | 789 | Compression strategory |
|
737 | 790 | STRATEGY_BTOPT |
|
738 | 791 | Compression strategory |
|
739 | 792 | |
|
740 | 793 | Note on Zstandard's *Experimental* API |
|
741 | 794 | ====================================== |
|
742 | 795 | |
|
743 | 796 | Many of the Zstandard APIs used by this module are marked as *experimental* |
|
744 | 797 | within the Zstandard project. This includes a large number of useful |
|
745 | 798 | features, such as compression and frame parameters and parts of dictionary |
|
746 | 799 | compression. |
|
747 | 800 | |
|
748 | 801 | It is unclear how Zstandard's C API will evolve over time, especially with |
|
749 | 802 | regards to this *experimental* functionality. We will try to maintain |
|
750 | 803 | backwards compatibility at the Python API level. However, we cannot |
|
751 | 804 | guarantee this for things not under our control. |
|
752 | 805 | |
|
753 | 806 | Since a copy of the Zstandard source code is distributed with this |
|
754 | 807 | module and since we compile against it, the behavior of a specific |
|
755 | 808 | version of this module should be constant for all of time. So if you |
|
756 | 809 | pin the version of this module used in your projects (which is a Python |
|
757 | 810 | best practice), you should be buffered from unwanted future changes. |
|
758 | 811 | |
|
759 | 812 | Donate |
|
760 | 813 | ====== |
|
761 | 814 | |
|
762 | 815 | A lot of time has been invested into this project by the author. |
|
763 | 816 | |
|
764 | 817 | If you find this project useful and would like to thank the author for |
|
765 | 818 | their work, consider donating some money. Any amount is appreciated. |
|
766 | 819 | |
|
767 | 820 | .. image:: https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif |
|
768 | 821 | :target: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=gregory%2eszorc%40gmail%2ecom&lc=US&item_name=python%2dzstandard¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHosted |
|
769 | 822 | :alt: Donate via PayPal |
|
770 | 823 | |
|
771 | 824 | .. |ci-status| image:: https://travis-ci.org/indygreg/python-zstandard.svg?branch=master |
|
772 | 825 | :target: https://travis-ci.org/indygreg/python-zstandard |
|
773 | 826 | |
|
774 | 827 | .. |win-ci-status| image:: https://ci.appveyor.com/api/projects/status/github/indygreg/python-zstandard?svg=true |
|
775 | 828 | :target: https://ci.appveyor.com/project/indygreg/python-zstandard |
|
776 | 829 | :alt: Windows build status |
@@ -1,247 +1,247 b'' | |||
|
1 | 1 | /** |
|
2 | 2 | * Copyright (c) 2016-present, Gregory Szorc |
|
3 | 3 | * All rights reserved. |
|
4 | 4 | * |
|
5 | 5 | * This software may be modified and distributed under the terms |
|
6 | 6 | * of the BSD license. See the LICENSE file for details. |
|
7 | 7 | */ |
|
8 | 8 | |
|
9 | 9 | #include "python-zstandard.h" |
|
10 | 10 | |
|
11 | 11 | extern PyObject* ZstdError; |
|
12 | 12 | |
|
13 | 13 | ZstdCompressionDict* train_dictionary(PyObject* self, PyObject* args, PyObject* kwargs) { |
|
14 | 14 | static char *kwlist[] = { "dict_size", "samples", "parameters", NULL }; |
|
15 | 15 | size_t capacity; |
|
16 | 16 | PyObject* samples; |
|
17 | 17 | Py_ssize_t samplesLen; |
|
18 | 18 | PyObject* parameters = NULL; |
|
19 | 19 | ZDICT_params_t zparams; |
|
20 | 20 | Py_ssize_t sampleIndex; |
|
21 | 21 | Py_ssize_t sampleSize; |
|
22 | 22 | PyObject* sampleItem; |
|
23 | 23 | size_t zresult; |
|
24 | 24 | void* sampleBuffer; |
|
25 | 25 | void* sampleOffset; |
|
26 | 26 | size_t samplesSize = 0; |
|
27 | 27 | size_t* sampleSizes; |
|
28 | 28 | void* dict; |
|
29 | 29 | ZstdCompressionDict* result; |
|
30 | 30 | |
|
31 | 31 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "nO!|O!", kwlist, |
|
32 | 32 | &capacity, |
|
33 | 33 | &PyList_Type, &samples, |
|
34 | 34 | (PyObject*)&DictParametersType, ¶meters)) { |
|
35 | 35 | return NULL; |
|
36 | 36 | } |
|
37 | 37 | |
|
38 | 38 | /* Validate parameters first since it is easiest. */ |
|
39 | 39 | zparams.selectivityLevel = 0; |
|
40 | 40 | zparams.compressionLevel = 0; |
|
41 | 41 | zparams.notificationLevel = 0; |
|
42 | 42 | zparams.dictID = 0; |
|
43 | 43 | zparams.reserved[0] = 0; |
|
44 | 44 | zparams.reserved[1] = 0; |
|
45 | 45 | |
|
46 | 46 | if (parameters) { |
|
47 | 47 | /* TODO validate data ranges */ |
|
48 | 48 | zparams.selectivityLevel = PyLong_AsUnsignedLong(PyTuple_GetItem(parameters, 0)); |
|
49 | 49 | zparams.compressionLevel = PyLong_AsLong(PyTuple_GetItem(parameters, 1)); |
|
50 | 50 | zparams.notificationLevel = PyLong_AsUnsignedLong(PyTuple_GetItem(parameters, 2)); |
|
51 | 51 | zparams.dictID = PyLong_AsUnsignedLong(PyTuple_GetItem(parameters, 3)); |
|
52 | 52 | } |
|
53 | 53 | |
|
54 | 54 | /* Figure out the size of the raw samples */ |
|
55 | 55 | samplesLen = PyList_Size(samples); |
|
56 | 56 | for (sampleIndex = 0; sampleIndex < samplesLen; sampleIndex++) { |
|
57 | 57 | sampleItem = PyList_GetItem(samples, sampleIndex); |
|
58 | 58 | if (!PyBytes_Check(sampleItem)) { |
|
59 | 59 | PyErr_SetString(PyExc_ValueError, "samples must be bytes"); |
|
60 | 60 | /* TODO probably need to perform DECREF here */ |
|
61 | 61 | return NULL; |
|
62 | 62 | } |
|
63 | 63 | samplesSize += PyBytes_GET_SIZE(sampleItem); |
|
64 | 64 | } |
|
65 | 65 | |
|
66 | 66 | /* Now that we know the total size of the raw simples, we can allocate |
|
67 | 67 | a buffer for the raw data */ |
|
68 | sampleBuffer = malloc(samplesSize); | |
|
68 | sampleBuffer = PyMem_Malloc(samplesSize); | |
|
69 | 69 | if (!sampleBuffer) { |
|
70 | 70 | PyErr_NoMemory(); |
|
71 | 71 | return NULL; |
|
72 | 72 | } |
|
73 | sampleSizes = malloc(samplesLen * sizeof(size_t)); | |
|
73 | sampleSizes = PyMem_Malloc(samplesLen * sizeof(size_t)); | |
|
74 | 74 | if (!sampleSizes) { |
|
75 |
|
|
|
75 | PyMem_Free(sampleBuffer); | |
|
76 | 76 | PyErr_NoMemory(); |
|
77 | 77 | return NULL; |
|
78 | 78 | } |
|
79 | 79 | |
|
80 | 80 | sampleOffset = sampleBuffer; |
|
81 | 81 | /* Now iterate again and assemble the samples in the buffer */ |
|
82 | 82 | for (sampleIndex = 0; sampleIndex < samplesLen; sampleIndex++) { |
|
83 | 83 | sampleItem = PyList_GetItem(samples, sampleIndex); |
|
84 | 84 | sampleSize = PyBytes_GET_SIZE(sampleItem); |
|
85 | 85 | sampleSizes[sampleIndex] = sampleSize; |
|
86 | 86 | memcpy(sampleOffset, PyBytes_AS_STRING(sampleItem), sampleSize); |
|
87 | 87 | sampleOffset = (char*)sampleOffset + sampleSize; |
|
88 | 88 | } |
|
89 | 89 | |
|
90 | dict = malloc(capacity); | |
|
90 | dict = PyMem_Malloc(capacity); | |
|
91 | 91 | if (!dict) { |
|
92 |
|
|
|
93 |
|
|
|
92 | PyMem_Free(sampleSizes); | |
|
93 | PyMem_Free(sampleBuffer); | |
|
94 | 94 | PyErr_NoMemory(); |
|
95 | 95 | return NULL; |
|
96 | 96 | } |
|
97 | 97 | |
|
98 | 98 | zresult = ZDICT_trainFromBuffer_advanced(dict, capacity, |
|
99 | 99 | sampleBuffer, sampleSizes, (unsigned int)samplesLen, |
|
100 | 100 | zparams); |
|
101 | 101 | if (ZDICT_isError(zresult)) { |
|
102 | 102 | PyErr_Format(ZstdError, "Cannot train dict: %s", ZDICT_getErrorName(zresult)); |
|
103 |
|
|
|
104 |
|
|
|
105 |
|
|
|
103 | PyMem_Free(dict); | |
|
104 | PyMem_Free(sampleSizes); | |
|
105 | PyMem_Free(sampleBuffer); | |
|
106 | 106 | return NULL; |
|
107 | 107 | } |
|
108 | 108 | |
|
109 | 109 | result = PyObject_New(ZstdCompressionDict, &ZstdCompressionDictType); |
|
110 | 110 | if (!result) { |
|
111 | 111 | return NULL; |
|
112 | 112 | } |
|
113 | 113 | |
|
114 | 114 | result->dictData = dict; |
|
115 | 115 | result->dictSize = zresult; |
|
116 | 116 | return result; |
|
117 | 117 | } |
|
118 | 118 | |
|
119 | 119 | |
|
120 | 120 | PyDoc_STRVAR(ZstdCompressionDict__doc__, |
|
121 | 121 | "ZstdCompressionDict(data) - Represents a computed compression dictionary\n" |
|
122 | 122 | "\n" |
|
123 | 123 | "This type holds the results of a computed Zstandard compression dictionary.\n" |
|
124 | 124 | "Instances are obtained by calling ``train_dictionary()`` or by passing bytes\n" |
|
125 | 125 | "obtained from another source into the constructor.\n" |
|
126 | 126 | ); |
|
127 | 127 | |
|
128 | 128 | static int ZstdCompressionDict_init(ZstdCompressionDict* self, PyObject* args) { |
|
129 | 129 | const char* source; |
|
130 | 130 | Py_ssize_t sourceSize; |
|
131 | 131 | |
|
132 | 132 | self->dictData = NULL; |
|
133 | 133 | self->dictSize = 0; |
|
134 | 134 | |
|
135 | 135 | #if PY_MAJOR_VERSION >= 3 |
|
136 | 136 | if (!PyArg_ParseTuple(args, "y#", &source, &sourceSize)) { |
|
137 | 137 | #else |
|
138 | 138 | if (!PyArg_ParseTuple(args, "s#", &source, &sourceSize)) { |
|
139 | 139 | #endif |
|
140 | 140 | return -1; |
|
141 | 141 | } |
|
142 | 142 | |
|
143 | self->dictData = malloc(sourceSize); | |
|
143 | self->dictData = PyMem_Malloc(sourceSize); | |
|
144 | 144 | if (!self->dictData) { |
|
145 | 145 | PyErr_NoMemory(); |
|
146 | 146 | return -1; |
|
147 | 147 | } |
|
148 | 148 | |
|
149 | 149 | memcpy(self->dictData, source, sourceSize); |
|
150 | 150 | self->dictSize = sourceSize; |
|
151 | 151 | |
|
152 | 152 | return 0; |
|
153 | 153 | } |
|
154 | 154 | |
|
155 | 155 | static void ZstdCompressionDict_dealloc(ZstdCompressionDict* self) { |
|
156 | 156 | if (self->dictData) { |
|
157 |
|
|
|
157 | PyMem_Free(self->dictData); | |
|
158 | 158 | self->dictData = NULL; |
|
159 | 159 | } |
|
160 | 160 | |
|
161 | 161 | PyObject_Del(self); |
|
162 | 162 | } |
|
163 | 163 | |
|
164 | 164 | static PyObject* ZstdCompressionDict_dict_id(ZstdCompressionDict* self) { |
|
165 | 165 | unsigned dictID = ZDICT_getDictID(self->dictData, self->dictSize); |
|
166 | 166 | |
|
167 | 167 | return PyLong_FromLong(dictID); |
|
168 | 168 | } |
|
169 | 169 | |
|
170 | 170 | static PyObject* ZstdCompressionDict_as_bytes(ZstdCompressionDict* self) { |
|
171 | 171 | return PyBytes_FromStringAndSize(self->dictData, self->dictSize); |
|
172 | 172 | } |
|
173 | 173 | |
|
174 | 174 | static PyMethodDef ZstdCompressionDict_methods[] = { |
|
175 | 175 | { "dict_id", (PyCFunction)ZstdCompressionDict_dict_id, METH_NOARGS, |
|
176 | 176 | PyDoc_STR("dict_id() -- obtain the numeric dictionary ID") }, |
|
177 | 177 | { "as_bytes", (PyCFunction)ZstdCompressionDict_as_bytes, METH_NOARGS, |
|
178 | 178 | PyDoc_STR("as_bytes() -- obtain the raw bytes constituting the dictionary data") }, |
|
179 | 179 | { NULL, NULL } |
|
180 | 180 | }; |
|
181 | 181 | |
|
182 | 182 | static Py_ssize_t ZstdCompressionDict_length(ZstdCompressionDict* self) { |
|
183 | 183 | return self->dictSize; |
|
184 | 184 | } |
|
185 | 185 | |
|
186 | 186 | static PySequenceMethods ZstdCompressionDict_sq = { |
|
187 | 187 | (lenfunc)ZstdCompressionDict_length, /* sq_length */ |
|
188 | 188 | 0, /* sq_concat */ |
|
189 | 189 | 0, /* sq_repeat */ |
|
190 | 190 | 0, /* sq_item */ |
|
191 | 191 | 0, /* sq_ass_item */ |
|
192 | 192 | 0, /* sq_contains */ |
|
193 | 193 | 0, /* sq_inplace_concat */ |
|
194 | 194 | 0 /* sq_inplace_repeat */ |
|
195 | 195 | }; |
|
196 | 196 | |
|
197 | 197 | PyTypeObject ZstdCompressionDictType = { |
|
198 | 198 | PyVarObject_HEAD_INIT(NULL, 0) |
|
199 | 199 | "zstd.ZstdCompressionDict", /* tp_name */ |
|
200 | 200 | sizeof(ZstdCompressionDict), /* tp_basicsize */ |
|
201 | 201 | 0, /* tp_itemsize */ |
|
202 | 202 | (destructor)ZstdCompressionDict_dealloc, /* tp_dealloc */ |
|
203 | 203 | 0, /* tp_print */ |
|
204 | 204 | 0, /* tp_getattr */ |
|
205 | 205 | 0, /* tp_setattr */ |
|
206 | 206 | 0, /* tp_compare */ |
|
207 | 207 | 0, /* tp_repr */ |
|
208 | 208 | 0, /* tp_as_number */ |
|
209 | 209 | &ZstdCompressionDict_sq, /* tp_as_sequence */ |
|
210 | 210 | 0, /* tp_as_mapping */ |
|
211 | 211 | 0, /* tp_hash */ |
|
212 | 212 | 0, /* tp_call */ |
|
213 | 213 | 0, /* tp_str */ |
|
214 | 214 | 0, /* tp_getattro */ |
|
215 | 215 | 0, /* tp_setattro */ |
|
216 | 216 | 0, /* tp_as_buffer */ |
|
217 | 217 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ |
|
218 | 218 | ZstdCompressionDict__doc__, /* tp_doc */ |
|
219 | 219 | 0, /* tp_traverse */ |
|
220 | 220 | 0, /* tp_clear */ |
|
221 | 221 | 0, /* tp_richcompare */ |
|
222 | 222 | 0, /* tp_weaklistoffset */ |
|
223 | 223 | 0, /* tp_iter */ |
|
224 | 224 | 0, /* tp_iternext */ |
|
225 | 225 | ZstdCompressionDict_methods, /* tp_methods */ |
|
226 | 226 | 0, /* tp_members */ |
|
227 | 227 | 0, /* tp_getset */ |
|
228 | 228 | 0, /* tp_base */ |
|
229 | 229 | 0, /* tp_dict */ |
|
230 | 230 | 0, /* tp_descr_get */ |
|
231 | 231 | 0, /* tp_descr_set */ |
|
232 | 232 | 0, /* tp_dictoffset */ |
|
233 | 233 | (initproc)ZstdCompressionDict_init, /* tp_init */ |
|
234 | 234 | 0, /* tp_alloc */ |
|
235 | 235 | PyType_GenericNew, /* tp_new */ |
|
236 | 236 | }; |
|
237 | 237 | |
|
238 | 238 | void compressiondict_module_init(PyObject* mod) { |
|
239 | 239 | Py_TYPE(&ZstdCompressionDictType) = &PyType_Type; |
|
240 | 240 | if (PyType_Ready(&ZstdCompressionDictType) < 0) { |
|
241 | 241 | return; |
|
242 | 242 | } |
|
243 | 243 | |
|
244 | 244 | Py_INCREF((PyObject*)&ZstdCompressionDictType); |
|
245 | 245 | PyModule_AddObject(mod, "ZstdCompressionDict", |
|
246 | 246 | (PyObject*)&ZstdCompressionDictType); |
|
247 | 247 | } |
@@ -1,235 +1,288 b'' | |||
|
1 | 1 | /** |
|
2 | 2 | * Copyright (c) 2016-present, Gregory Szorc |
|
3 | 3 | * All rights reserved. |
|
4 | 4 | * |
|
5 | 5 | * This software may be modified and distributed under the terms |
|
6 | 6 | * of the BSD license. See the LICENSE file for details. |
|
7 | 7 | */ |
|
8 | 8 | |
|
9 | 9 | #include "python-zstandard.h" |
|
10 | 10 | |
|
11 | 11 | extern PyObject* ZstdError; |
|
12 | 12 | |
|
13 | 13 | PyDoc_STRVAR(ZstdCompresssionWriter__doc__, |
|
14 | 14 | """A context manager used for writing compressed output to a writer.\n" |
|
15 | 15 | ); |
|
16 | 16 | |
|
17 | 17 | static void ZstdCompressionWriter_dealloc(ZstdCompressionWriter* self) { |
|
18 | 18 | Py_XDECREF(self->compressor); |
|
19 | 19 | Py_XDECREF(self->writer); |
|
20 | 20 | |
|
21 | 21 | if (self->cstream) { |
|
22 | 22 | ZSTD_freeCStream(self->cstream); |
|
23 | 23 | self->cstream = NULL; |
|
24 | 24 | } |
|
25 | 25 | |
|
26 | 26 | PyObject_Del(self); |
|
27 | 27 | } |
|
28 | 28 | |
|
29 | 29 | static PyObject* ZstdCompressionWriter_enter(ZstdCompressionWriter* self) { |
|
30 | 30 | if (self->entered) { |
|
31 | 31 | PyErr_SetString(ZstdError, "cannot __enter__ multiple times"); |
|
32 | 32 | return NULL; |
|
33 | 33 | } |
|
34 | 34 | |
|
35 | 35 | self->cstream = CStream_from_ZstdCompressor(self->compressor, self->sourceSize); |
|
36 | 36 | if (!self->cstream) { |
|
37 | 37 | return NULL; |
|
38 | 38 | } |
|
39 | 39 | |
|
40 | 40 | self->entered = 1; |
|
41 | 41 | |
|
42 | 42 | Py_INCREF(self); |
|
43 | 43 | return (PyObject*)self; |
|
44 | 44 | } |
|
45 | 45 | |
|
46 | 46 | static PyObject* ZstdCompressionWriter_exit(ZstdCompressionWriter* self, PyObject* args) { |
|
47 | 47 | PyObject* exc_type; |
|
48 | 48 | PyObject* exc_value; |
|
49 | 49 | PyObject* exc_tb; |
|
50 | 50 | size_t zresult; |
|
51 | 51 | |
|
52 | 52 | ZSTD_outBuffer output; |
|
53 | 53 | PyObject* res; |
|
54 | 54 | |
|
55 | 55 | if (!PyArg_ParseTuple(args, "OOO", &exc_type, &exc_value, &exc_tb)) { |
|
56 | 56 | return NULL; |
|
57 | 57 | } |
|
58 | 58 | |
|
59 | 59 | self->entered = 0; |
|
60 | 60 | |
|
61 | 61 | if (self->cstream && exc_type == Py_None && exc_value == Py_None && |
|
62 | 62 | exc_tb == Py_None) { |
|
63 | 63 | |
|
64 | output.dst = malloc(self->outSize); | |
|
64 | output.dst = PyMem_Malloc(self->outSize); | |
|
65 | 65 | if (!output.dst) { |
|
66 | 66 | return PyErr_NoMemory(); |
|
67 | 67 | } |
|
68 | 68 | output.size = self->outSize; |
|
69 | 69 | output.pos = 0; |
|
70 | 70 | |
|
71 | 71 | while (1) { |
|
72 | 72 | zresult = ZSTD_endStream(self->cstream, &output); |
|
73 | 73 | if (ZSTD_isError(zresult)) { |
|
74 | 74 | PyErr_Format(ZstdError, "error ending compression stream: %s", |
|
75 | 75 | ZSTD_getErrorName(zresult)); |
|
76 |
|
|
|
76 | PyMem_Free(output.dst); | |
|
77 | 77 | return NULL; |
|
78 | 78 | } |
|
79 | 79 | |
|
80 | 80 | if (output.pos) { |
|
81 | 81 | #if PY_MAJOR_VERSION >= 3 |
|
82 | 82 | res = PyObject_CallMethod(self->writer, "write", "y#", |
|
83 | 83 | #else |
|
84 | 84 | res = PyObject_CallMethod(self->writer, "write", "s#", |
|
85 | 85 | #endif |
|
86 | 86 | output.dst, output.pos); |
|
87 | 87 | Py_XDECREF(res); |
|
88 | 88 | } |
|
89 | 89 | |
|
90 | 90 | if (!zresult) { |
|
91 | 91 | break; |
|
92 | 92 | } |
|
93 | 93 | |
|
94 | 94 | output.pos = 0; |
|
95 | 95 | } |
|
96 | 96 | |
|
97 |
|
|
|
97 | PyMem_Free(output.dst); | |
|
98 | 98 | ZSTD_freeCStream(self->cstream); |
|
99 | 99 | self->cstream = NULL; |
|
100 | 100 | } |
|
101 | 101 | |
|
102 | 102 | Py_RETURN_FALSE; |
|
103 | 103 | } |
|
104 | 104 | |
|
105 | 105 | static PyObject* ZstdCompressionWriter_memory_size(ZstdCompressionWriter* self) { |
|
106 | 106 | if (!self->cstream) { |
|
107 | 107 | PyErr_SetString(ZstdError, "cannot determine size of an inactive compressor; " |
|
108 | 108 | "call when a context manager is active"); |
|
109 | 109 | return NULL; |
|
110 | 110 | } |
|
111 | 111 | |
|
112 | 112 | return PyLong_FromSize_t(ZSTD_sizeof_CStream(self->cstream)); |
|
113 | 113 | } |
|
114 | 114 | |
|
115 | 115 | static PyObject* ZstdCompressionWriter_write(ZstdCompressionWriter* self, PyObject* args) { |
|
116 | 116 | const char* source; |
|
117 | 117 | Py_ssize_t sourceSize; |
|
118 | 118 | size_t zresult; |
|
119 | 119 | ZSTD_inBuffer input; |
|
120 | 120 | ZSTD_outBuffer output; |
|
121 | 121 | PyObject* res; |
|
122 | 122 | |
|
123 | 123 | #if PY_MAJOR_VERSION >= 3 |
|
124 | 124 | if (!PyArg_ParseTuple(args, "y#", &source, &sourceSize)) { |
|
125 | 125 | #else |
|
126 | 126 | if (!PyArg_ParseTuple(args, "s#", &source, &sourceSize)) { |
|
127 | 127 | #endif |
|
128 | 128 | return NULL; |
|
129 | 129 | } |
|
130 | 130 | |
|
131 | 131 | if (!self->entered) { |
|
132 | 132 | PyErr_SetString(ZstdError, "compress must be called from an active context manager"); |
|
133 | 133 | return NULL; |
|
134 | 134 | } |
|
135 | 135 | |
|
136 | output.dst = malloc(self->outSize); | |
|
136 | output.dst = PyMem_Malloc(self->outSize); | |
|
137 | 137 | if (!output.dst) { |
|
138 | 138 | return PyErr_NoMemory(); |
|
139 | 139 | } |
|
140 | 140 | output.size = self->outSize; |
|
141 | 141 | output.pos = 0; |
|
142 | 142 | |
|
143 | 143 | input.src = source; |
|
144 | 144 | input.size = sourceSize; |
|
145 | 145 | input.pos = 0; |
|
146 | 146 | |
|
147 | 147 | while ((ssize_t)input.pos < sourceSize) { |
|
148 | 148 | Py_BEGIN_ALLOW_THREADS |
|
149 | 149 | zresult = ZSTD_compressStream(self->cstream, &output, &input); |
|
150 | 150 | Py_END_ALLOW_THREADS |
|
151 | 151 | |
|
152 | 152 | if (ZSTD_isError(zresult)) { |
|
153 |
|
|
|
153 | PyMem_Free(output.dst); | |
|
154 | 154 | PyErr_Format(ZstdError, "zstd compress error: %s", ZSTD_getErrorName(zresult)); |
|
155 | 155 | return NULL; |
|
156 | 156 | } |
|
157 | 157 | |
|
158 | 158 | /* Copy data from output buffer to writer. */ |
|
159 | 159 | if (output.pos) { |
|
160 | 160 | #if PY_MAJOR_VERSION >= 3 |
|
161 | 161 | res = PyObject_CallMethod(self->writer, "write", "y#", |
|
162 | 162 | #else |
|
163 | 163 | res = PyObject_CallMethod(self->writer, "write", "s#", |
|
164 | 164 | #endif |
|
165 | 165 | output.dst, output.pos); |
|
166 | 166 | Py_XDECREF(res); |
|
167 | 167 | } |
|
168 | 168 | output.pos = 0; |
|
169 | 169 | } |
|
170 | 170 | |
|
171 |
|
|
|
171 | PyMem_Free(output.dst); | |
|
172 | 172 | |
|
173 | 173 | /* TODO return bytes written */ |
|
174 | 174 | Py_RETURN_NONE; |
|
175 | } | |
|
176 | ||
|
177 | static PyObject* ZstdCompressionWriter_flush(ZstdCompressionWriter* self, PyObject* args) { | |
|
178 | size_t zresult; | |
|
179 | ZSTD_outBuffer output; | |
|
180 | PyObject* res; | |
|
181 | ||
|
182 | if (!self->entered) { | |
|
183 | PyErr_SetString(ZstdError, "flush must be called from an active context manager"); | |
|
184 | return NULL; | |
|
175 | 185 | } |
|
176 | 186 | |
|
187 | output.dst = PyMem_Malloc(self->outSize); | |
|
188 | if (!output.dst) { | |
|
189 | return PyErr_NoMemory(); | |
|
190 | } | |
|
191 | output.size = self->outSize; | |
|
192 | output.pos = 0; | |
|
193 | ||
|
194 | while (1) { | |
|
195 | Py_BEGIN_ALLOW_THREADS | |
|
196 | zresult = ZSTD_flushStream(self->cstream, &output); | |
|
197 | Py_END_ALLOW_THREADS | |
|
198 | ||
|
199 | if (ZSTD_isError(zresult)) { | |
|
200 | PyMem_Free(output.dst); | |
|
201 | PyErr_Format(ZstdError, "zstd compress error: %s", ZSTD_getErrorName(zresult)); | |
|
202 | return NULL; | |
|
203 | } | |
|
204 | ||
|
205 | if (!output.pos) { | |
|
206 | break; | |
|
207 | } | |
|
208 | ||
|
209 | /* Copy data from output buffer to writer. */ | |
|
210 | if (output.pos) { | |
|
211 | #if PY_MAJOR_VERSION >= 3 | |
|
212 | res = PyObject_CallMethod(self->writer, "write", "y#", | |
|
213 | #else | |
|
214 | res = PyObject_CallMethod(self->writer, "write", "s#", | |
|
215 | #endif | |
|
216 | output.dst, output.pos); | |
|
217 | Py_XDECREF(res); | |
|
218 | } | |
|
219 | output.pos = 0; | |
|
220 | } | |
|
221 | ||
|
222 | PyMem_Free(output.dst); | |
|
223 | ||
|
224 | /* TODO return bytes written */ | |
|
225 | Py_RETURN_NONE; | |
|
226 | } | |
|
227 | ||
|
177 | 228 | static PyMethodDef ZstdCompressionWriter_methods[] = { |
|
178 | 229 | { "__enter__", (PyCFunction)ZstdCompressionWriter_enter, METH_NOARGS, |
|
179 | 230 | PyDoc_STR("Enter a compression context.") }, |
|
180 | 231 | { "__exit__", (PyCFunction)ZstdCompressionWriter_exit, METH_VARARGS, |
|
181 | 232 | PyDoc_STR("Exit a compression context.") }, |
|
182 | 233 | { "memory_size", (PyCFunction)ZstdCompressionWriter_memory_size, METH_NOARGS, |
|
183 | 234 | PyDoc_STR("Obtain the memory size of the underlying compressor") }, |
|
184 | 235 | { "write", (PyCFunction)ZstdCompressionWriter_write, METH_VARARGS, |
|
185 | 236 | PyDoc_STR("Compress data") }, |
|
237 | { "flush", (PyCFunction)ZstdCompressionWriter_flush, METH_NOARGS, | |
|
238 | PyDoc_STR("Flush data and finish a zstd frame") }, | |
|
186 | 239 | { NULL, NULL } |
|
187 | 240 | }; |
|
188 | 241 | |
|
189 | 242 | PyTypeObject ZstdCompressionWriterType = { |
|
190 | 243 | PyVarObject_HEAD_INIT(NULL, 0) |
|
191 | 244 | "zstd.ZstdCompressionWriter", /* tp_name */ |
|
192 | 245 | sizeof(ZstdCompressionWriter), /* tp_basicsize */ |
|
193 | 246 | 0, /* tp_itemsize */ |
|
194 | 247 | (destructor)ZstdCompressionWriter_dealloc, /* tp_dealloc */ |
|
195 | 248 | 0, /* tp_print */ |
|
196 | 249 | 0, /* tp_getattr */ |
|
197 | 250 | 0, /* tp_setattr */ |
|
198 | 251 | 0, /* tp_compare */ |
|
199 | 252 | 0, /* tp_repr */ |
|
200 | 253 | 0, /* tp_as_number */ |
|
201 | 254 | 0, /* tp_as_sequence */ |
|
202 | 255 | 0, /* tp_as_mapping */ |
|
203 | 256 | 0, /* tp_hash */ |
|
204 | 257 | 0, /* tp_call */ |
|
205 | 258 | 0, /* tp_str */ |
|
206 | 259 | 0, /* tp_getattro */ |
|
207 | 260 | 0, /* tp_setattro */ |
|
208 | 261 | 0, /* tp_as_buffer */ |
|
209 | 262 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ |
|
210 | 263 | ZstdCompresssionWriter__doc__, /* tp_doc */ |
|
211 | 264 | 0, /* tp_traverse */ |
|
212 | 265 | 0, /* tp_clear */ |
|
213 | 266 | 0, /* tp_richcompare */ |
|
214 | 267 | 0, /* tp_weaklistoffset */ |
|
215 | 268 | 0, /* tp_iter */ |
|
216 | 269 | 0, /* tp_iternext */ |
|
217 | 270 | ZstdCompressionWriter_methods, /* tp_methods */ |
|
218 | 271 | 0, /* tp_members */ |
|
219 | 272 | 0, /* tp_getset */ |
|
220 | 273 | 0, /* tp_base */ |
|
221 | 274 | 0, /* tp_dict */ |
|
222 | 275 | 0, /* tp_descr_get */ |
|
223 | 276 | 0, /* tp_descr_set */ |
|
224 | 277 | 0, /* tp_dictoffset */ |
|
225 | 278 | 0, /* tp_init */ |
|
226 | 279 | 0, /* tp_alloc */ |
|
227 | 280 | PyType_GenericNew, /* tp_new */ |
|
228 | 281 | }; |
|
229 | 282 | |
|
230 | 283 | void compressionwriter_module_init(PyObject* mod) { |
|
231 | 284 | Py_TYPE(&ZstdCompressionWriterType) = &PyType_Type; |
|
232 | 285 | if (PyType_Ready(&ZstdCompressionWriterType) < 0) { |
|
233 | 286 | return; |
|
234 | 287 | } |
|
235 | 288 | } |
@@ -1,205 +1,250 b'' | |||
|
1 | 1 | /** |
|
2 | 2 | * Copyright (c) 2016-present, Gregory Szorc |
|
3 | 3 | * All rights reserved. |
|
4 | 4 | * |
|
5 | 5 | * This software may be modified and distributed under the terms |
|
6 | 6 | * of the BSD license. See the LICENSE file for details. |
|
7 | 7 | */ |
|
8 | 8 | |
|
9 | 9 | #include "python-zstandard.h" |
|
10 | 10 | |
|
11 | 11 | extern PyObject* ZstdError; |
|
12 | 12 | |
|
13 | 13 | PyDoc_STRVAR(ZstdCompressionObj__doc__, |
|
14 | 14 | "Perform compression using a standard library compatible API.\n" |
|
15 | 15 | ); |
|
16 | 16 | |
|
17 | 17 | static void ZstdCompressionObj_dealloc(ZstdCompressionObj* self) { |
|
18 | 18 | PyMem_Free(self->output.dst); |
|
19 | 19 | self->output.dst = NULL; |
|
20 | 20 | |
|
21 | 21 | if (self->cstream) { |
|
22 | 22 | ZSTD_freeCStream(self->cstream); |
|
23 | 23 | self->cstream = NULL; |
|
24 | 24 | } |
|
25 | 25 | |
|
26 | 26 | Py_XDECREF(self->compressor); |
|
27 | 27 | |
|
28 | 28 | PyObject_Del(self); |
|
29 | 29 | } |
|
30 | 30 | |
|
31 | 31 | static PyObject* ZstdCompressionObj_compress(ZstdCompressionObj* self, PyObject* args) { |
|
32 | 32 | const char* source; |
|
33 | 33 | Py_ssize_t sourceSize; |
|
34 | 34 | ZSTD_inBuffer input; |
|
35 | 35 | size_t zresult; |
|
36 | 36 | PyObject* result = NULL; |
|
37 | 37 | Py_ssize_t resultSize = 0; |
|
38 | 38 | |
|
39 |
if (self->f |
|
|
40 |
PyErr_SetString(ZstdError, "cannot call compress() after |
|
|
39 | if (self->finished) { | |
|
40 | PyErr_SetString(ZstdError, "cannot call compress() after compressor finished"); | |
|
41 | 41 | return NULL; |
|
42 | 42 | } |
|
43 | 43 | |
|
44 | 44 | #if PY_MAJOR_VERSION >= 3 |
|
45 | 45 | if (!PyArg_ParseTuple(args, "y#", &source, &sourceSize)) { |
|
46 | 46 | #else |
|
47 | 47 | if (!PyArg_ParseTuple(args, "s#", &source, &sourceSize)) { |
|
48 | 48 | #endif |
|
49 | 49 | return NULL; |
|
50 | 50 | } |
|
51 | 51 | |
|
52 | 52 | input.src = source; |
|
53 | 53 | input.size = sourceSize; |
|
54 | 54 | input.pos = 0; |
|
55 | 55 | |
|
56 | 56 | while ((ssize_t)input.pos < sourceSize) { |
|
57 | 57 | Py_BEGIN_ALLOW_THREADS |
|
58 | 58 | zresult = ZSTD_compressStream(self->cstream, &self->output, &input); |
|
59 | 59 | Py_END_ALLOW_THREADS |
|
60 | 60 | |
|
61 | 61 | if (ZSTD_isError(zresult)) { |
|
62 | 62 | PyErr_Format(ZstdError, "zstd compress error: %s", ZSTD_getErrorName(zresult)); |
|
63 | 63 | return NULL; |
|
64 | 64 | } |
|
65 | 65 | |
|
66 | 66 | if (self->output.pos) { |
|
67 | 67 | if (result) { |
|
68 | 68 | resultSize = PyBytes_GET_SIZE(result); |
|
69 | 69 | if (-1 == _PyBytes_Resize(&result, resultSize + self->output.pos)) { |
|
70 | 70 | return NULL; |
|
71 | 71 | } |
|
72 | 72 | |
|
73 | 73 | memcpy(PyBytes_AS_STRING(result) + resultSize, |
|
74 | 74 | self->output.dst, self->output.pos); |
|
75 | 75 | } |
|
76 | 76 | else { |
|
77 | 77 | result = PyBytes_FromStringAndSize(self->output.dst, self->output.pos); |
|
78 | 78 | if (!result) { |
|
79 | 79 | return NULL; |
|
80 | 80 | } |
|
81 | 81 | } |
|
82 | 82 | |
|
83 | 83 | self->output.pos = 0; |
|
84 | 84 | } |
|
85 | 85 | } |
|
86 | 86 | |
|
87 | 87 | if (result) { |
|
88 | 88 | return result; |
|
89 | 89 | } |
|
90 | 90 | else { |
|
91 | 91 | return PyBytes_FromString(""); |
|
92 | 92 | } |
|
93 | 93 | } |
|
94 | 94 | |
|
95 | static PyObject* ZstdCompressionObj_flush(ZstdCompressionObj* self) { | |
|
95 | static PyObject* ZstdCompressionObj_flush(ZstdCompressionObj* self, PyObject* args) { | |
|
96 | int flushMode = compressorobj_flush_finish; | |
|
96 | 97 | size_t zresult; |
|
97 | 98 | PyObject* result = NULL; |
|
98 | 99 | Py_ssize_t resultSize = 0; |
|
99 | 100 | |
|
100 | if (self->flushed) { | |
|
101 | PyErr_SetString(ZstdError, "flush() already called"); | |
|
101 | if (!PyArg_ParseTuple(args, "|i", &flushMode)) { | |
|
102 | return NULL; | |
|
103 | } | |
|
104 | ||
|
105 | if (flushMode != compressorobj_flush_finish && flushMode != compressorobj_flush_block) { | |
|
106 | PyErr_SetString(PyExc_ValueError, "flush mode not recognized"); | |
|
107 | return NULL; | |
|
108 | } | |
|
109 | ||
|
110 | if (self->finished) { | |
|
111 | PyErr_SetString(ZstdError, "compressor object already finished"); | |
|
102 | 112 | return NULL; |
|
103 | 113 | } |
|
104 | 114 | |
|
105 | self->flushed = 1; | |
|
115 | assert(self->output.pos == 0); | |
|
116 | ||
|
117 | if (flushMode == compressorobj_flush_block) { | |
|
118 | /* The output buffer is of size ZSTD_CStreamOutSize(), which is | |
|
119 | guaranteed to hold a full block. */ | |
|
120 | Py_BEGIN_ALLOW_THREADS | |
|
121 | zresult = ZSTD_flushStream(self->cstream, &self->output); | |
|
122 | Py_END_ALLOW_THREADS | |
|
123 | ||
|
124 | if (ZSTD_isError(zresult)) { | |
|
125 | PyErr_Format(ZstdError, "zstd compress error: %s", ZSTD_getErrorName(zresult)); | |
|
126 | return NULL; | |
|
127 | } | |
|
128 | ||
|
129 | /* Output buffer is guaranteed to hold full block. */ | |
|
130 | assert(zresult == 0); | |
|
131 | ||
|
132 | if (self->output.pos) { | |
|
133 | result = PyBytes_FromStringAndSize(self->output.dst, self->output.pos); | |
|
134 | if (!result) { | |
|
135 | return NULL; | |
|
136 | } | |
|
137 | } | |
|
138 | ||
|
139 | self->output.pos = 0; | |
|
140 | ||
|
141 | if (result) { | |
|
142 | return result; | |
|
143 | } | |
|
144 | else { | |
|
145 | return PyBytes_FromString(""); | |
|
146 | } | |
|
147 | } | |
|
148 | ||
|
149 | assert(flushMode == compressorobj_flush_finish); | |
|
150 | self->finished = 1; | |
|
106 | 151 | |
|
107 | 152 | while (1) { |
|
108 | 153 | zresult = ZSTD_endStream(self->cstream, &self->output); |
|
109 | 154 | if (ZSTD_isError(zresult)) { |
|
110 | 155 | PyErr_Format(ZstdError, "error ending compression stream: %s", |
|
111 | 156 | ZSTD_getErrorName(zresult)); |
|
112 | 157 | return NULL; |
|
113 | 158 | } |
|
114 | 159 | |
|
115 | 160 | if (self->output.pos) { |
|
116 | 161 | if (result) { |
|
117 | 162 | resultSize = PyBytes_GET_SIZE(result); |
|
118 | 163 | if (-1 == _PyBytes_Resize(&result, resultSize + self->output.pos)) { |
|
119 | 164 | return NULL; |
|
120 | 165 | } |
|
121 | 166 | |
|
122 | 167 | memcpy(PyBytes_AS_STRING(result) + resultSize, |
|
123 | 168 | self->output.dst, self->output.pos); |
|
124 | 169 | } |
|
125 | 170 | else { |
|
126 | 171 | result = PyBytes_FromStringAndSize(self->output.dst, self->output.pos); |
|
127 | 172 | if (!result) { |
|
128 | 173 | return NULL; |
|
129 | 174 | } |
|
130 | 175 | } |
|
131 | 176 | |
|
132 | 177 | self->output.pos = 0; |
|
133 | 178 | } |
|
134 | 179 | |
|
135 | 180 | if (!zresult) { |
|
136 | 181 | break; |
|
137 | 182 | } |
|
138 | 183 | } |
|
139 | 184 | |
|
140 | 185 | ZSTD_freeCStream(self->cstream); |
|
141 | 186 | self->cstream = NULL; |
|
142 | 187 | |
|
143 | 188 | if (result) { |
|
144 | 189 | return result; |
|
145 | 190 | } |
|
146 | 191 | else { |
|
147 | 192 | return PyBytes_FromString(""); |
|
148 | 193 | } |
|
149 | 194 | } |
|
150 | 195 | |
|
151 | 196 | static PyMethodDef ZstdCompressionObj_methods[] = { |
|
152 | 197 | { "compress", (PyCFunction)ZstdCompressionObj_compress, METH_VARARGS, |
|
153 | 198 | PyDoc_STR("compress data") }, |
|
154 |
{ "flush", (PyCFunction)ZstdCompressionObj_flush, METH_ |
|
|
199 | { "flush", (PyCFunction)ZstdCompressionObj_flush, METH_VARARGS, | |
|
155 | 200 | PyDoc_STR("finish compression operation") }, |
|
156 | 201 | { NULL, NULL } |
|
157 | 202 | }; |
|
158 | 203 | |
|
159 | 204 | PyTypeObject ZstdCompressionObjType = { |
|
160 | 205 | PyVarObject_HEAD_INIT(NULL, 0) |
|
161 | 206 | "zstd.ZstdCompressionObj", /* tp_name */ |
|
162 | 207 | sizeof(ZstdCompressionObj), /* tp_basicsize */ |
|
163 | 208 | 0, /* tp_itemsize */ |
|
164 | 209 | (destructor)ZstdCompressionObj_dealloc, /* tp_dealloc */ |
|
165 | 210 | 0, /* tp_print */ |
|
166 | 211 | 0, /* tp_getattr */ |
|
167 | 212 | 0, /* tp_setattr */ |
|
168 | 213 | 0, /* tp_compare */ |
|
169 | 214 | 0, /* tp_repr */ |
|
170 | 215 | 0, /* tp_as_number */ |
|
171 | 216 | 0, /* tp_as_sequence */ |
|
172 | 217 | 0, /* tp_as_mapping */ |
|
173 | 218 | 0, /* tp_hash */ |
|
174 | 219 | 0, /* tp_call */ |
|
175 | 220 | 0, /* tp_str */ |
|
176 | 221 | 0, /* tp_getattro */ |
|
177 | 222 | 0, /* tp_setattro */ |
|
178 | 223 | 0, /* tp_as_buffer */ |
|
179 | 224 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ |
|
180 | 225 | ZstdCompressionObj__doc__, /* tp_doc */ |
|
181 | 226 | 0, /* tp_traverse */ |
|
182 | 227 | 0, /* tp_clear */ |
|
183 | 228 | 0, /* tp_richcompare */ |
|
184 | 229 | 0, /* tp_weaklistoffset */ |
|
185 | 230 | 0, /* tp_iter */ |
|
186 | 231 | 0, /* tp_iternext */ |
|
187 | 232 | ZstdCompressionObj_methods, /* tp_methods */ |
|
188 | 233 | 0, /* tp_members */ |
|
189 | 234 | 0, /* tp_getset */ |
|
190 | 235 | 0, /* tp_base */ |
|
191 | 236 | 0, /* tp_dict */ |
|
192 | 237 | 0, /* tp_descr_get */ |
|
193 | 238 | 0, /* tp_descr_set */ |
|
194 | 239 | 0, /* tp_dictoffset */ |
|
195 | 240 | 0, /* tp_init */ |
|
196 | 241 | 0, /* tp_alloc */ |
|
197 | 242 | PyType_GenericNew, /* tp_new */ |
|
198 | 243 | }; |
|
199 | 244 | |
|
200 | 245 | void compressobj_module_init(PyObject* module) { |
|
201 | 246 | Py_TYPE(&ZstdCompressionObjType) = &PyType_Type; |
|
202 | 247 | if (PyType_Ready(&ZstdCompressionObjType) < 0) { |
|
203 | 248 | return; |
|
204 | 249 | } |
|
205 | 250 | } |
@@ -1,757 +1,788 b'' | |||
|
1 | 1 | /** |
|
2 | 2 | * Copyright (c) 2016-present, Gregory Szorc |
|
3 | 3 | * All rights reserved. |
|
4 | 4 | * |
|
5 | 5 | * This software may be modified and distributed under the terms |
|
6 | 6 | * of the BSD license. See the LICENSE file for details. |
|
7 | 7 | */ |
|
8 | 8 | |
|
9 | 9 | #include "python-zstandard.h" |
|
10 | 10 | |
|
11 | 11 | extern PyObject* ZstdError; |
|
12 | 12 | |
|
13 | int populate_cdict(ZstdCompressor* compressor, void* dictData, size_t dictSize, ZSTD_parameters* zparams) { | |
|
14 | ZSTD_customMem zmem; | |
|
15 | assert(!compressor->cdict); | |
|
16 | Py_BEGIN_ALLOW_THREADS | |
|
17 | memset(&zmem, 0, sizeof(zmem)); | |
|
18 | compressor->cdict = ZSTD_createCDict_advanced(compressor->dict->dictData, | |
|
19 | compressor->dict->dictSize, *zparams, zmem); | |
|
20 | Py_END_ALLOW_THREADS | |
|
21 | ||
|
22 | if (!compressor->cdict) { | |
|
23 | PyErr_SetString(ZstdError, "could not create compression dictionary"); | |
|
24 | return 1; | |
|
25 | } | |
|
26 | ||
|
27 | return 0; | |
|
28 | } | |
|
29 | ||
|
13 | 30 | /** |
|
14 | 31 | * Initialize a zstd CStream from a ZstdCompressor instance. |
|
15 | 32 | * |
|
16 | 33 | * Returns a ZSTD_CStream on success or NULL on failure. If NULL, a Python |
|
17 | 34 | * exception will be set. |
|
18 | 35 | */ |
|
19 | 36 | ZSTD_CStream* CStream_from_ZstdCompressor(ZstdCompressor* compressor, Py_ssize_t sourceSize) { |
|
20 | 37 | ZSTD_CStream* cstream; |
|
21 | 38 | ZSTD_parameters zparams; |
|
22 | 39 | void* dictData = NULL; |
|
23 | 40 | size_t dictSize = 0; |
|
24 | 41 | size_t zresult; |
|
25 | 42 | |
|
26 | 43 | cstream = ZSTD_createCStream(); |
|
27 | 44 | if (!cstream) { |
|
28 | 45 | PyErr_SetString(ZstdError, "cannot create CStream"); |
|
29 | 46 | return NULL; |
|
30 | 47 | } |
|
31 | 48 | |
|
32 | 49 | if (compressor->dict) { |
|
33 | 50 | dictData = compressor->dict->dictData; |
|
34 | 51 | dictSize = compressor->dict->dictSize; |
|
35 | 52 | } |
|
36 | 53 | |
|
37 | 54 | memset(&zparams, 0, sizeof(zparams)); |
|
38 | 55 | if (compressor->cparams) { |
|
39 | 56 | ztopy_compression_parameters(compressor->cparams, &zparams.cParams); |
|
40 | 57 | /* Do NOT call ZSTD_adjustCParams() here because the compression params |
|
41 | 58 | come from the user. */ |
|
42 | 59 | } |
|
43 | 60 | else { |
|
44 | 61 | zparams.cParams = ZSTD_getCParams(compressor->compressionLevel, sourceSize, dictSize); |
|
45 | 62 | } |
|
46 | 63 | |
|
47 | 64 | zparams.fParams = compressor->fparams; |
|
48 | 65 | |
|
49 | 66 | zresult = ZSTD_initCStream_advanced(cstream, dictData, dictSize, zparams, sourceSize); |
|
50 | 67 | |
|
51 | 68 | if (ZSTD_isError(zresult)) { |
|
52 | 69 | ZSTD_freeCStream(cstream); |
|
53 | 70 | PyErr_Format(ZstdError, "cannot init CStream: %s", ZSTD_getErrorName(zresult)); |
|
54 | 71 | return NULL; |
|
55 | 72 | } |
|
56 | 73 | |
|
57 | 74 | return cstream; |
|
58 | 75 | } |
|
59 | 76 | |
|
60 | ||
|
61 | 77 | PyDoc_STRVAR(ZstdCompressor__doc__, |
|
62 | 78 | "ZstdCompressor(level=None, dict_data=None, compression_params=None)\n" |
|
63 | 79 | "\n" |
|
64 | 80 | "Create an object used to perform Zstandard compression.\n" |
|
65 | 81 | "\n" |
|
66 | 82 | "An instance can compress data various ways. Instances can be used multiple\n" |
|
67 | 83 | "times. Each compression operation will use the compression parameters\n" |
|
68 | 84 | "defined at construction time.\n" |
|
69 | 85 | "\n" |
|
70 | 86 | "Compression can be configured via the following names arguments:\n" |
|
71 | 87 | "\n" |
|
72 | 88 | "level\n" |
|
73 | 89 | " Integer compression level.\n" |
|
74 | 90 | "dict_data\n" |
|
75 | 91 | " A ``ZstdCompressionDict`` to be used to compress with dictionary data.\n" |
|
76 | 92 | "compression_params\n" |
|
77 | 93 | " A ``CompressionParameters`` instance defining low-level compression" |
|
78 | 94 | " parameters. If defined, this will overwrite the ``level`` argument.\n" |
|
79 | 95 | "write_checksum\n" |
|
80 | 96 | " If True, a 4 byte content checksum will be written with the compressed\n" |
|
81 | 97 | " data, allowing the decompressor to perform content verification.\n" |
|
82 | 98 | "write_content_size\n" |
|
83 | 99 | " If True, the decompressed content size will be included in the header of\n" |
|
84 | 100 | " the compressed data. This data will only be written if the compressor\n" |
|
85 | 101 | " knows the size of the input data.\n" |
|
86 | 102 | "write_dict_id\n" |
|
87 | 103 | " Determines whether the dictionary ID will be written into the compressed\n" |
|
88 | 104 | " data. Defaults to True. Only adds content to the compressed data if\n" |
|
89 | 105 | " a dictionary is being used.\n" |
|
90 | 106 | ); |
|
91 | 107 | |
|
92 | 108 | static int ZstdCompressor_init(ZstdCompressor* self, PyObject* args, PyObject* kwargs) { |
|
93 | 109 | static char* kwlist[] = { |
|
94 | 110 | "level", |
|
95 | 111 | "dict_data", |
|
96 | 112 | "compression_params", |
|
97 | 113 | "write_checksum", |
|
98 | 114 | "write_content_size", |
|
99 | 115 | "write_dict_id", |
|
100 | 116 | NULL |
|
101 | 117 | }; |
|
102 | 118 | |
|
103 | 119 | int level = 3; |
|
104 | 120 | ZstdCompressionDict* dict = NULL; |
|
105 | 121 | CompressionParametersObject* params = NULL; |
|
106 | 122 | PyObject* writeChecksum = NULL; |
|
107 | 123 | PyObject* writeContentSize = NULL; |
|
108 | 124 | PyObject* writeDictID = NULL; |
|
109 | 125 | |
|
126 | self->cctx = NULL; | |
|
110 | 127 | self->dict = NULL; |
|
111 | 128 | self->cparams = NULL; |
|
112 | 129 | self->cdict = NULL; |
|
113 | 130 | |
|
114 | 131 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|iO!O!OOO", kwlist, |
|
115 | 132 | &level, &ZstdCompressionDictType, &dict, |
|
116 | 133 | &CompressionParametersType, ¶ms, |
|
117 | 134 | &writeChecksum, &writeContentSize, &writeDictID)) { |
|
118 | 135 | return -1; |
|
119 | 136 | } |
|
120 | 137 | |
|
121 | 138 | if (level < 1) { |
|
122 | 139 | PyErr_SetString(PyExc_ValueError, "level must be greater than 0"); |
|
123 | 140 | return -1; |
|
124 | 141 | } |
|
125 | 142 | |
|
126 | 143 | if (level > ZSTD_maxCLevel()) { |
|
127 | 144 | PyErr_Format(PyExc_ValueError, "level must be less than %d", |
|
128 | 145 | ZSTD_maxCLevel() + 1); |
|
129 | 146 | return -1; |
|
130 | 147 | } |
|
131 | 148 | |
|
149 | /* We create a ZSTD_CCtx for reuse among multiple operations to reduce the | |
|
150 | overhead of each compression operation. */ | |
|
151 | self->cctx = ZSTD_createCCtx(); | |
|
152 | if (!self->cctx) { | |
|
153 | PyErr_NoMemory(); | |
|
154 | return -1; | |
|
155 | } | |
|
156 | ||
|
132 | 157 | self->compressionLevel = level; |
|
133 | 158 | |
|
134 | 159 | if (dict) { |
|
135 | 160 | self->dict = dict; |
|
136 | 161 | Py_INCREF(dict); |
|
137 | 162 | } |
|
138 | 163 | |
|
139 | 164 | if (params) { |
|
140 | 165 | self->cparams = params; |
|
141 | 166 | Py_INCREF(params); |
|
142 | 167 | } |
|
143 | 168 | |
|
144 | 169 | memset(&self->fparams, 0, sizeof(self->fparams)); |
|
145 | 170 | |
|
146 | 171 | if (writeChecksum && PyObject_IsTrue(writeChecksum)) { |
|
147 | 172 | self->fparams.checksumFlag = 1; |
|
148 | 173 | } |
|
149 | 174 | if (writeContentSize && PyObject_IsTrue(writeContentSize)) { |
|
150 | 175 | self->fparams.contentSizeFlag = 1; |
|
151 | 176 | } |
|
152 | 177 | if (writeDictID && PyObject_Not(writeDictID)) { |
|
153 | 178 | self->fparams.noDictIDFlag = 1; |
|
154 | 179 | } |
|
155 | 180 | |
|
156 | 181 | return 0; |
|
157 | 182 | } |
|
158 | 183 | |
|
159 | 184 | static void ZstdCompressor_dealloc(ZstdCompressor* self) { |
|
160 | 185 | Py_XDECREF(self->cparams); |
|
161 | 186 | Py_XDECREF(self->dict); |
|
162 | 187 | |
|
163 | 188 | if (self->cdict) { |
|
164 | 189 | ZSTD_freeCDict(self->cdict); |
|
165 | 190 | self->cdict = NULL; |
|
166 | 191 | } |
|
167 | 192 | |
|
193 | if (self->cctx) { | |
|
194 | ZSTD_freeCCtx(self->cctx); | |
|
195 | self->cctx = NULL; | |
|
196 | } | |
|
197 | ||
|
168 | 198 | PyObject_Del(self); |
|
169 | 199 | } |
|
170 | 200 | |
|
171 | 201 | PyDoc_STRVAR(ZstdCompressor_copy_stream__doc__, |
|
172 | 202 | "copy_stream(ifh, ofh[, size=0, read_size=default, write_size=default])\n" |
|
173 | 203 | "compress data between streams\n" |
|
174 | 204 | "\n" |
|
175 | 205 | "Data will be read from ``ifh``, compressed, and written to ``ofh``.\n" |
|
176 | 206 | "``ifh`` must have a ``read(size)`` method. ``ofh`` must have a ``write(data)``\n" |
|
177 | 207 | "method.\n" |
|
178 | 208 | "\n" |
|
179 | 209 | "An optional ``size`` argument specifies the size of the source stream.\n" |
|
180 | 210 | "If defined, compression parameters will be tuned based on the size.\n" |
|
181 | 211 | "\n" |
|
182 | 212 | "Optional arguments ``read_size`` and ``write_size`` define the chunk sizes\n" |
|
183 | 213 | "of ``read()`` and ``write()`` operations, respectively. By default, they use\n" |
|
184 | 214 | "the default compression stream input and output sizes, respectively.\n" |
|
185 | 215 | ); |
|
186 | 216 | |
|
187 | 217 | static PyObject* ZstdCompressor_copy_stream(ZstdCompressor* self, PyObject* args, PyObject* kwargs) { |
|
188 | 218 | static char* kwlist[] = { |
|
189 | 219 | "ifh", |
|
190 | 220 | "ofh", |
|
191 | 221 | "size", |
|
192 | 222 | "read_size", |
|
193 | 223 | "write_size", |
|
194 | 224 | NULL |
|
195 | 225 | }; |
|
196 | 226 | |
|
197 | 227 | PyObject* source; |
|
198 | 228 | PyObject* dest; |
|
199 | 229 | Py_ssize_t sourceSize = 0; |
|
200 | 230 | size_t inSize = ZSTD_CStreamInSize(); |
|
201 | 231 | size_t outSize = ZSTD_CStreamOutSize(); |
|
202 | 232 | ZSTD_CStream* cstream; |
|
203 | 233 | ZSTD_inBuffer input; |
|
204 | 234 | ZSTD_outBuffer output; |
|
205 | 235 | Py_ssize_t totalRead = 0; |
|
206 | 236 | Py_ssize_t totalWrite = 0; |
|
207 | 237 | char* readBuffer; |
|
208 | 238 | Py_ssize_t readSize; |
|
209 | 239 | PyObject* readResult; |
|
210 | 240 | PyObject* res = NULL; |
|
211 | 241 | size_t zresult; |
|
212 | 242 | PyObject* writeResult; |
|
213 | 243 | PyObject* totalReadPy; |
|
214 | 244 | PyObject* totalWritePy; |
|
215 | 245 | |
|
216 | 246 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|nkk", kwlist, &source, &dest, &sourceSize, |
|
217 | 247 | &inSize, &outSize)) { |
|
218 | 248 | return NULL; |
|
219 | 249 | } |
|
220 | 250 | |
|
221 | 251 | if (!PyObject_HasAttrString(source, "read")) { |
|
222 | 252 | PyErr_SetString(PyExc_ValueError, "first argument must have a read() method"); |
|
223 | 253 | return NULL; |
|
224 | 254 | } |
|
225 | 255 | |
|
226 | 256 | if (!PyObject_HasAttrString(dest, "write")) { |
|
227 | 257 | PyErr_SetString(PyExc_ValueError, "second argument must have a write() method"); |
|
228 | 258 | return NULL; |
|
229 | 259 | } |
|
230 | 260 | |
|
231 | 261 | cstream = CStream_from_ZstdCompressor(self, sourceSize); |
|
232 | 262 | if (!cstream) { |
|
233 | 263 | res = NULL; |
|
234 | 264 | goto finally; |
|
235 | 265 | } |
|
236 | 266 | |
|
237 | 267 | output.dst = PyMem_Malloc(outSize); |
|
238 | 268 | if (!output.dst) { |
|
239 | 269 | PyErr_NoMemory(); |
|
240 | 270 | res = NULL; |
|
241 | 271 | goto finally; |
|
242 | 272 | } |
|
243 | 273 | output.size = outSize; |
|
244 | 274 | output.pos = 0; |
|
245 | 275 | |
|
246 | 276 | while (1) { |
|
247 | 277 | /* Try to read from source stream. */ |
|
248 | 278 | readResult = PyObject_CallMethod(source, "read", "n", inSize); |
|
249 | 279 | if (!readResult) { |
|
250 | 280 | PyErr_SetString(ZstdError, "could not read() from source"); |
|
251 | 281 | goto finally; |
|
252 | 282 | } |
|
253 | 283 | |
|
254 | 284 | PyBytes_AsStringAndSize(readResult, &readBuffer, &readSize); |
|
255 | 285 | |
|
256 | 286 | /* If no data was read, we're at EOF. */ |
|
257 | 287 | if (0 == readSize) { |
|
258 | 288 | break; |
|
259 | 289 | } |
|
260 | 290 | |
|
261 | 291 | totalRead += readSize; |
|
262 | 292 | |
|
263 | 293 | /* Send data to compressor */ |
|
264 | 294 | input.src = readBuffer; |
|
265 | 295 | input.size = readSize; |
|
266 | 296 | input.pos = 0; |
|
267 | 297 | |
|
268 | 298 | while (input.pos < input.size) { |
|
269 | 299 | Py_BEGIN_ALLOW_THREADS |
|
270 | 300 | zresult = ZSTD_compressStream(cstream, &output, &input); |
|
271 | 301 | Py_END_ALLOW_THREADS |
|
272 | 302 | |
|
273 | 303 | if (ZSTD_isError(zresult)) { |
|
274 | 304 | res = NULL; |
|
275 | 305 | PyErr_Format(ZstdError, "zstd compress error: %s", ZSTD_getErrorName(zresult)); |
|
276 | 306 | goto finally; |
|
277 | 307 | } |
|
278 | 308 | |
|
279 | 309 | if (output.pos) { |
|
280 | 310 | #if PY_MAJOR_VERSION >= 3 |
|
281 | 311 | writeResult = PyObject_CallMethod(dest, "write", "y#", |
|
282 | 312 | #else |
|
283 | 313 | writeResult = PyObject_CallMethod(dest, "write", "s#", |
|
284 | 314 | #endif |
|
285 | 315 | output.dst, output.pos); |
|
286 | 316 | Py_XDECREF(writeResult); |
|
287 | 317 | totalWrite += output.pos; |
|
288 | 318 | output.pos = 0; |
|
289 | 319 | } |
|
290 | 320 | } |
|
291 | 321 | } |
|
292 | 322 | |
|
293 | 323 | /* We've finished reading. Now flush the compressor stream. */ |
|
294 | 324 | while (1) { |
|
295 | 325 | zresult = ZSTD_endStream(cstream, &output); |
|
296 | 326 | if (ZSTD_isError(zresult)) { |
|
297 | 327 | PyErr_Format(ZstdError, "error ending compression stream: %s", |
|
298 | 328 | ZSTD_getErrorName(zresult)); |
|
299 | 329 | res = NULL; |
|
300 | 330 | goto finally; |
|
301 | 331 | } |
|
302 | 332 | |
|
303 | 333 | if (output.pos) { |
|
304 | 334 | #if PY_MAJOR_VERSION >= 3 |
|
305 | 335 | writeResult = PyObject_CallMethod(dest, "write", "y#", |
|
306 | 336 | #else |
|
307 | 337 | writeResult = PyObject_CallMethod(dest, "write", "s#", |
|
308 | 338 | #endif |
|
309 | 339 | output.dst, output.pos); |
|
310 | 340 | totalWrite += output.pos; |
|
311 | 341 | Py_XDECREF(writeResult); |
|
312 | 342 | output.pos = 0; |
|
313 | 343 | } |
|
314 | 344 | |
|
315 | 345 | if (!zresult) { |
|
316 | 346 | break; |
|
317 | 347 | } |
|
318 | 348 | } |
|
319 | 349 | |
|
320 | 350 | ZSTD_freeCStream(cstream); |
|
321 | 351 | cstream = NULL; |
|
322 | 352 | |
|
323 | 353 | totalReadPy = PyLong_FromSsize_t(totalRead); |
|
324 | 354 | totalWritePy = PyLong_FromSsize_t(totalWrite); |
|
325 | 355 | res = PyTuple_Pack(2, totalReadPy, totalWritePy); |
|
326 | 356 | Py_DecRef(totalReadPy); |
|
327 | 357 | Py_DecRef(totalWritePy); |
|
328 | 358 | |
|
329 | 359 | finally: |
|
330 | 360 | if (output.dst) { |
|
331 | 361 | PyMem_Free(output.dst); |
|
332 | 362 | } |
|
333 | 363 | |
|
334 | 364 | if (cstream) { |
|
335 | 365 | ZSTD_freeCStream(cstream); |
|
336 | 366 | } |
|
337 | 367 | |
|
338 | 368 | return res; |
|
339 | 369 | } |
|
340 | 370 | |
|
341 | 371 | PyDoc_STRVAR(ZstdCompressor_compress__doc__, |
|
342 | "compress(data)\n" | |
|
372 | "compress(data, allow_empty=False)\n" | |
|
343 | 373 | "\n" |
|
344 | 374 | "Compress data in a single operation.\n" |
|
345 | 375 | "\n" |
|
346 | 376 | "This is the simplest mechanism to perform compression: simply pass in a\n" |
|
347 | 377 | "value and get a compressed value back. It is almost the most prone to abuse.\n" |
|
348 | 378 | "The input and output values must fit in memory, so passing in very large\n" |
|
349 | 379 | "values can result in excessive memory usage. For this reason, one of the\n" |
|
350 | 380 | "streaming based APIs is preferred for larger values.\n" |
|
351 | 381 | ); |
|
352 | 382 | |
|
353 | static PyObject* ZstdCompressor_compress(ZstdCompressor* self, PyObject* args) { | |
|
383 | static PyObject* ZstdCompressor_compress(ZstdCompressor* self, PyObject* args, PyObject* kwargs) { | |
|
384 | static char* kwlist[] = { | |
|
385 | "data", | |
|
386 | "allow_empty", | |
|
387 | NULL | |
|
388 | }; | |
|
389 | ||
|
354 | 390 | const char* source; |
|
355 | 391 | Py_ssize_t sourceSize; |
|
392 | PyObject* allowEmpty = NULL; | |
|
356 | 393 | size_t destSize; |
|
357 | ZSTD_CCtx* cctx; | |
|
358 | 394 | PyObject* output; |
|
359 | 395 | char* dest; |
|
360 | 396 | void* dictData = NULL; |
|
361 | 397 | size_t dictSize = 0; |
|
362 | 398 | size_t zresult; |
|
363 | 399 | ZSTD_parameters zparams; |
|
364 | ZSTD_customMem zmem; | |
|
365 | 400 | |
|
366 | 401 | #if PY_MAJOR_VERSION >= 3 |
|
367 | if (!PyArg_ParseTuple(args, "y#", &source, &sourceSize)) { | |
|
402 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y#|O", | |
|
368 | 403 | #else |
|
369 | if (!PyArg_ParseTuple(args, "s#", &source, &sourceSize)) { | |
|
404 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|O", | |
|
370 | 405 | #endif |
|
406 | kwlist, &source, &sourceSize, &allowEmpty)) { | |
|
407 | return NULL; | |
|
408 | } | |
|
409 | ||
|
410 | /* Limitation in zstd C API doesn't let decompression side distinguish | |
|
411 | between content size of 0 and unknown content size. This can make round | |
|
412 | tripping via Python difficult. Until this is fixed, require a flag | |
|
413 | to fire the footgun. | |
|
414 | https://github.com/indygreg/python-zstandard/issues/11 */ | |
|
415 | if (0 == sourceSize && self->fparams.contentSizeFlag | |
|
416 | && (!allowEmpty || PyObject_Not(allowEmpty))) { | |
|
417 | PyErr_SetString(PyExc_ValueError, "cannot write empty inputs when writing content sizes"); | |
|
371 | 418 | return NULL; |
|
372 | 419 | } |
|
373 | 420 | |
|
374 | 421 | destSize = ZSTD_compressBound(sourceSize); |
|
375 | 422 | output = PyBytes_FromStringAndSize(NULL, destSize); |
|
376 | 423 | if (!output) { |
|
377 | 424 | return NULL; |
|
378 | 425 | } |
|
379 | 426 | |
|
380 | 427 | dest = PyBytes_AsString(output); |
|
381 | 428 | |
|
382 | cctx = ZSTD_createCCtx(); | |
|
383 | if (!cctx) { | |
|
384 | Py_DECREF(output); | |
|
385 | PyErr_SetString(ZstdError, "could not create CCtx"); | |
|
386 | return NULL; | |
|
387 | } | |
|
388 | ||
|
389 | 429 | if (self->dict) { |
|
390 | 430 | dictData = self->dict->dictData; |
|
391 | 431 | dictSize = self->dict->dictSize; |
|
392 | 432 | } |
|
393 | 433 | |
|
394 | 434 | memset(&zparams, 0, sizeof(zparams)); |
|
395 | 435 | if (!self->cparams) { |
|
396 | 436 | zparams.cParams = ZSTD_getCParams(self->compressionLevel, sourceSize, dictSize); |
|
397 | 437 | } |
|
398 | 438 | else { |
|
399 | 439 | ztopy_compression_parameters(self->cparams, &zparams.cParams); |
|
400 | 440 | /* Do NOT call ZSTD_adjustCParams() here because the compression params |
|
401 | 441 | come from the user. */ |
|
402 | 442 | } |
|
403 | 443 | |
|
404 | 444 | zparams.fParams = self->fparams; |
|
405 | 445 | |
|
406 | 446 | /* The raw dict data has to be processed before it can be used. Since this |
|
407 | 447 | adds overhead - especially if multiple dictionary compression operations |
|
408 | 448 | are performed on the same ZstdCompressor instance - we create a |
|
409 |
ZSTD_CDict once and reuse it for all operations. |
|
|
449 | ZSTD_CDict once and reuse it for all operations. | |
|
410 | 450 |
|
|
411 | /* TODO the zparams (which can be derived from the source data size) used | |
|
412 | on first invocation are effectively reused for subsequent operations. This | |
|
413 | may not be appropriate if input sizes vary significantly and could affect | |
|
414 | chosen compression parameters. | |
|
415 | https://github.com/facebook/zstd/issues/358 tracks this issue. */ | |
|
451 | Note: the compression parameters used for the first invocation (possibly | |
|
452 | derived from the source size) will be reused on all subsequent invocations. | |
|
453 | https://github.com/facebook/zstd/issues/358 contains more info. We could | |
|
454 | potentially add an argument somewhere to control this behavior. | |
|
455 | */ | |
|
416 | 456 | if (dictData && !self->cdict) { |
|
417 | Py_BEGIN_ALLOW_THREADS | |
|
418 | memset(&zmem, 0, sizeof(zmem)); | |
|
419 | self->cdict = ZSTD_createCDict_advanced(dictData, dictSize, zparams, zmem); | |
|
420 | Py_END_ALLOW_THREADS | |
|
421 | ||
|
422 | if (!self->cdict) { | |
|
457 | if (populate_cdict(self, dictData, dictSize, &zparams)) { | |
|
423 | 458 | Py_DECREF(output); |
|
424 | ZSTD_freeCCtx(cctx); | |
|
425 | PyErr_SetString(ZstdError, "could not create compression dictionary"); | |
|
426 | 459 | return NULL; |
|
427 | 460 | } |
|
428 | 461 | } |
|
429 | 462 | |
|
430 | 463 | Py_BEGIN_ALLOW_THREADS |
|
431 | 464 | /* By avoiding ZSTD_compress(), we don't necessarily write out content |
|
432 | 465 | size. This means the argument to ZstdCompressor to control frame |
|
433 | 466 | parameters is honored. */ |
|
434 | 467 | if (self->cdict) { |
|
435 | zresult = ZSTD_compress_usingCDict(cctx, dest, destSize, | |
|
468 | zresult = ZSTD_compress_usingCDict(self->cctx, dest, destSize, | |
|
436 | 469 | source, sourceSize, self->cdict); |
|
437 | 470 | } |
|
438 | 471 | else { |
|
439 | zresult = ZSTD_compress_advanced(cctx, dest, destSize, | |
|
472 | zresult = ZSTD_compress_advanced(self->cctx, dest, destSize, | |
|
440 | 473 | source, sourceSize, dictData, dictSize, zparams); |
|
441 | 474 | } |
|
442 | 475 | Py_END_ALLOW_THREADS |
|
443 | 476 | |
|
444 | ZSTD_freeCCtx(cctx); | |
|
445 | ||
|
446 | 477 | if (ZSTD_isError(zresult)) { |
|
447 | 478 | PyErr_Format(ZstdError, "cannot compress: %s", ZSTD_getErrorName(zresult)); |
|
448 | 479 | Py_CLEAR(output); |
|
449 | 480 | return NULL; |
|
450 | 481 | } |
|
451 | 482 | else { |
|
452 | 483 | Py_SIZE(output) = zresult; |
|
453 | 484 | } |
|
454 | 485 | |
|
455 | 486 | return output; |
|
456 | 487 | } |
|
457 | 488 | |
|
458 | 489 | PyDoc_STRVAR(ZstdCompressionObj__doc__, |
|
459 | 490 | "compressobj()\n" |
|
460 | 491 | "\n" |
|
461 | 492 | "Return an object exposing ``compress(data)`` and ``flush()`` methods.\n" |
|
462 | 493 | "\n" |
|
463 | 494 | "The returned object exposes an API similar to ``zlib.compressobj`` and\n" |
|
464 | 495 | "``bz2.BZ2Compressor`` so that callers can swap in the zstd compressor\n" |
|
465 | 496 | "without changing how compression is performed.\n" |
|
466 | 497 | ); |
|
467 | 498 | |
|
468 | 499 | static ZstdCompressionObj* ZstdCompressor_compressobj(ZstdCompressor* self, PyObject* args, PyObject* kwargs) { |
|
469 | 500 | static char* kwlist[] = { |
|
470 | 501 | "size", |
|
471 | 502 | NULL |
|
472 | 503 | }; |
|
473 | 504 | |
|
474 | 505 | Py_ssize_t inSize = 0; |
|
475 | 506 | size_t outSize = ZSTD_CStreamOutSize(); |
|
476 | 507 | ZstdCompressionObj* result = PyObject_New(ZstdCompressionObj, &ZstdCompressionObjType); |
|
477 | 508 | if (!result) { |
|
478 | 509 | return NULL; |
|
479 | 510 | } |
|
480 | 511 | |
|
481 | 512 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|n", kwlist, &inSize)) { |
|
482 | 513 | return NULL; |
|
483 | 514 | } |
|
484 | 515 | |
|
485 | 516 | result->cstream = CStream_from_ZstdCompressor(self, inSize); |
|
486 | 517 | if (!result->cstream) { |
|
487 | 518 | Py_DECREF(result); |
|
488 | 519 | return NULL; |
|
489 | 520 | } |
|
490 | 521 | |
|
491 | 522 | result->output.dst = PyMem_Malloc(outSize); |
|
492 | 523 | if (!result->output.dst) { |
|
493 | 524 | PyErr_NoMemory(); |
|
494 | 525 | Py_DECREF(result); |
|
495 | 526 | return NULL; |
|
496 | 527 | } |
|
497 | 528 | result->output.size = outSize; |
|
498 | 529 | result->output.pos = 0; |
|
499 | 530 | |
|
500 | 531 | result->compressor = self; |
|
501 | 532 | Py_INCREF(result->compressor); |
|
502 | 533 | |
|
503 |
result->f |
|
|
534 | result->finished = 0; | |
|
504 | 535 | |
|
505 | 536 | return result; |
|
506 | 537 | } |
|
507 | 538 | |
|
508 | 539 | PyDoc_STRVAR(ZstdCompressor_read_from__doc__, |
|
509 | 540 | "read_from(reader, [size=0, read_size=default, write_size=default])\n" |
|
510 | 541 | "Read uncompress data from a reader and return an iterator\n" |
|
511 | 542 | "\n" |
|
512 | 543 | "Returns an iterator of compressed data produced from reading from ``reader``.\n" |
|
513 | 544 | "\n" |
|
514 | 545 | "Uncompressed data will be obtained from ``reader`` by calling the\n" |
|
515 | 546 | "``read(size)`` method of it. The source data will be streamed into a\n" |
|
516 | 547 | "compressor. As compressed data is available, it will be exposed to the\n" |
|
517 | 548 | "iterator.\n" |
|
518 | 549 | "\n" |
|
519 | 550 | "Data is read from the source in chunks of ``read_size``. Compressed chunks\n" |
|
520 | 551 | "are at most ``write_size`` bytes. Both values default to the zstd input and\n" |
|
521 | 552 | "and output defaults, respectively.\n" |
|
522 | 553 | "\n" |
|
523 | 554 | "The caller is partially in control of how fast data is fed into the\n" |
|
524 | 555 | "compressor by how it consumes the returned iterator. The compressor will\n" |
|
525 | 556 | "not consume from the reader unless the caller consumes from the iterator.\n" |
|
526 | 557 | ); |
|
527 | 558 | |
|
528 | 559 | static ZstdCompressorIterator* ZstdCompressor_read_from(ZstdCompressor* self, PyObject* args, PyObject* kwargs) { |
|
529 | 560 | static char* kwlist[] = { |
|
530 | 561 | "reader", |
|
531 | 562 | "size", |
|
532 | 563 | "read_size", |
|
533 | 564 | "write_size", |
|
534 | 565 | NULL |
|
535 | 566 | }; |
|
536 | 567 | |
|
537 | 568 | PyObject* reader; |
|
538 | 569 | Py_ssize_t sourceSize = 0; |
|
539 | 570 | size_t inSize = ZSTD_CStreamInSize(); |
|
540 | 571 | size_t outSize = ZSTD_CStreamOutSize(); |
|
541 | 572 | ZstdCompressorIterator* result; |
|
542 | 573 | |
|
543 | 574 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|nkk", kwlist, &reader, &sourceSize, |
|
544 | 575 | &inSize, &outSize)) { |
|
545 | 576 | return NULL; |
|
546 | 577 | } |
|
547 | 578 | |
|
548 | 579 | result = PyObject_New(ZstdCompressorIterator, &ZstdCompressorIteratorType); |
|
549 | 580 | if (!result) { |
|
550 | 581 | return NULL; |
|
551 | 582 | } |
|
552 | 583 | |
|
553 | 584 | result->compressor = NULL; |
|
554 | 585 | result->reader = NULL; |
|
555 | 586 | result->buffer = NULL; |
|
556 | 587 | result->cstream = NULL; |
|
557 | 588 | result->input.src = NULL; |
|
558 | 589 | result->output.dst = NULL; |
|
559 | 590 | result->readResult = NULL; |
|
560 | 591 | |
|
561 | 592 | if (PyObject_HasAttrString(reader, "read")) { |
|
562 | 593 | result->reader = reader; |
|
563 | 594 | Py_INCREF(result->reader); |
|
564 | 595 | } |
|
565 | 596 | else if (1 == PyObject_CheckBuffer(reader)) { |
|
566 | 597 | result->buffer = PyMem_Malloc(sizeof(Py_buffer)); |
|
567 | 598 | if (!result->buffer) { |
|
568 | 599 | goto except; |
|
569 | 600 | } |
|
570 | 601 | |
|
571 | 602 | memset(result->buffer, 0, sizeof(Py_buffer)); |
|
572 | 603 | |
|
573 | 604 | if (0 != PyObject_GetBuffer(reader, result->buffer, PyBUF_CONTIG_RO)) { |
|
574 | 605 | goto except; |
|
575 | 606 | } |
|
576 | 607 | |
|
577 | 608 | result->bufferOffset = 0; |
|
578 | 609 | sourceSize = result->buffer->len; |
|
579 | 610 | } |
|
580 | 611 | else { |
|
581 | 612 | PyErr_SetString(PyExc_ValueError, |
|
582 | 613 | "must pass an object with a read() method or conforms to buffer protocol"); |
|
583 | 614 | goto except; |
|
584 | 615 | } |
|
585 | 616 | |
|
586 | 617 | result->compressor = self; |
|
587 | 618 | Py_INCREF(result->compressor); |
|
588 | 619 | |
|
589 | 620 | result->sourceSize = sourceSize; |
|
590 | 621 | result->cstream = CStream_from_ZstdCompressor(self, sourceSize); |
|
591 | 622 | if (!result->cstream) { |
|
592 | 623 | goto except; |
|
593 | 624 | } |
|
594 | 625 | |
|
595 | 626 | result->inSize = inSize; |
|
596 | 627 | result->outSize = outSize; |
|
597 | 628 | |
|
598 | 629 | result->output.dst = PyMem_Malloc(outSize); |
|
599 | 630 | if (!result->output.dst) { |
|
600 | 631 | PyErr_NoMemory(); |
|
601 | 632 | goto except; |
|
602 | 633 | } |
|
603 | 634 | result->output.size = outSize; |
|
604 | 635 | result->output.pos = 0; |
|
605 | 636 | |
|
606 | 637 | result->input.src = NULL; |
|
607 | 638 | result->input.size = 0; |
|
608 | 639 | result->input.pos = 0; |
|
609 | 640 | |
|
610 | 641 | result->finishedInput = 0; |
|
611 | 642 | result->finishedOutput = 0; |
|
612 | 643 | |
|
613 | 644 | goto finally; |
|
614 | 645 | |
|
615 | 646 | except: |
|
616 | 647 | if (result->cstream) { |
|
617 | 648 | ZSTD_freeCStream(result->cstream); |
|
618 | 649 | result->cstream = NULL; |
|
619 | 650 | } |
|
620 | 651 | |
|
621 | 652 | Py_DecRef((PyObject*)result->compressor); |
|
622 | 653 | Py_DecRef(result->reader); |
|
623 | 654 | |
|
624 | 655 | Py_DECREF(result); |
|
625 | 656 | result = NULL; |
|
626 | 657 | |
|
627 | 658 | finally: |
|
628 | 659 | return result; |
|
629 | 660 | } |
|
630 | 661 | |
|
631 | 662 | PyDoc_STRVAR(ZstdCompressor_write_to___doc__, |
|
632 | 663 | "Create a context manager to write compressed data to an object.\n" |
|
633 | 664 | "\n" |
|
634 | 665 | "The passed object must have a ``write()`` method.\n" |
|
635 | 666 | "\n" |
|
636 | 667 | "The caller feeds input data to the object by calling ``compress(data)``.\n" |
|
637 | 668 | "Compressed data is written to the argument given to this function.\n" |
|
638 | 669 | "\n" |
|
639 | 670 | "The function takes an optional ``size`` argument indicating the total size\n" |
|
640 | 671 | "of the eventual input. If specified, the size will influence compression\n" |
|
641 | 672 | "parameter tuning and could result in the size being written into the\n" |
|
642 | 673 | "header of the compressed data.\n" |
|
643 | 674 | "\n" |
|
644 | 675 | "An optional ``write_size`` argument is also accepted. It defines the maximum\n" |
|
645 | 676 | "byte size of chunks fed to ``write()``. By default, it uses the zstd default\n" |
|
646 | 677 | "for a compressor output stream.\n" |
|
647 | 678 | ); |
|
648 | 679 | |
|
649 | 680 | static ZstdCompressionWriter* ZstdCompressor_write_to(ZstdCompressor* self, PyObject* args, PyObject* kwargs) { |
|
650 | 681 | static char* kwlist[] = { |
|
651 | 682 | "writer", |
|
652 | 683 | "size", |
|
653 | 684 | "write_size", |
|
654 | 685 | NULL |
|
655 | 686 | }; |
|
656 | 687 | |
|
657 | 688 | PyObject* writer; |
|
658 | 689 | ZstdCompressionWriter* result; |
|
659 | 690 | Py_ssize_t sourceSize = 0; |
|
660 | 691 | size_t outSize = ZSTD_CStreamOutSize(); |
|
661 | 692 | |
|
662 | 693 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|nk", kwlist, &writer, &sourceSize, |
|
663 | 694 | &outSize)) { |
|
664 | 695 | return NULL; |
|
665 | 696 | } |
|
666 | 697 | |
|
667 | 698 | if (!PyObject_HasAttrString(writer, "write")) { |
|
668 | 699 | PyErr_SetString(PyExc_ValueError, "must pass an object with a write() method"); |
|
669 | 700 | return NULL; |
|
670 | 701 | } |
|
671 | 702 | |
|
672 | 703 | result = PyObject_New(ZstdCompressionWriter, &ZstdCompressionWriterType); |
|
673 | 704 | if (!result) { |
|
674 | 705 | return NULL; |
|
675 | 706 | } |
|
676 | 707 | |
|
677 | 708 | result->compressor = self; |
|
678 | 709 | Py_INCREF(result->compressor); |
|
679 | 710 | |
|
680 | 711 | result->writer = writer; |
|
681 | 712 | Py_INCREF(result->writer); |
|
682 | 713 | |
|
683 | 714 | result->sourceSize = sourceSize; |
|
684 | 715 | |
|
685 | 716 | result->outSize = outSize; |
|
686 | 717 | |
|
687 | 718 | result->entered = 0; |
|
688 | 719 | result->cstream = NULL; |
|
689 | 720 | |
|
690 | 721 | return result; |
|
691 | 722 | } |
|
692 | 723 | |
|
693 | 724 | static PyMethodDef ZstdCompressor_methods[] = { |
|
694 |
{ "compress", (PyCFunction)ZstdCompressor_compress, |
|
|
695 | ZstdCompressor_compress__doc__ }, | |
|
725 | { "compress", (PyCFunction)ZstdCompressor_compress, | |
|
726 | METH_VARARGS | METH_KEYWORDS, ZstdCompressor_compress__doc__ }, | |
|
696 | 727 | { "compressobj", (PyCFunction)ZstdCompressor_compressobj, |
|
697 | 728 | METH_VARARGS | METH_KEYWORDS, ZstdCompressionObj__doc__ }, |
|
698 | 729 | { "copy_stream", (PyCFunction)ZstdCompressor_copy_stream, |
|
699 | 730 | METH_VARARGS | METH_KEYWORDS, ZstdCompressor_copy_stream__doc__ }, |
|
700 | 731 | { "read_from", (PyCFunction)ZstdCompressor_read_from, |
|
701 | 732 | METH_VARARGS | METH_KEYWORDS, ZstdCompressor_read_from__doc__ }, |
|
702 | 733 | { "write_to", (PyCFunction)ZstdCompressor_write_to, |
|
703 | 734 | METH_VARARGS | METH_KEYWORDS, ZstdCompressor_write_to___doc__ }, |
|
704 | 735 | { NULL, NULL } |
|
705 | 736 | }; |
|
706 | 737 | |
|
707 | 738 | PyTypeObject ZstdCompressorType = { |
|
708 | 739 | PyVarObject_HEAD_INIT(NULL, 0) |
|
709 | 740 | "zstd.ZstdCompressor", /* tp_name */ |
|
710 | 741 | sizeof(ZstdCompressor), /* tp_basicsize */ |
|
711 | 742 | 0, /* tp_itemsize */ |
|
712 | 743 | (destructor)ZstdCompressor_dealloc, /* tp_dealloc */ |
|
713 | 744 | 0, /* tp_print */ |
|
714 | 745 | 0, /* tp_getattr */ |
|
715 | 746 | 0, /* tp_setattr */ |
|
716 | 747 | 0, /* tp_compare */ |
|
717 | 748 | 0, /* tp_repr */ |
|
718 | 749 | 0, /* tp_as_number */ |
|
719 | 750 | 0, /* tp_as_sequence */ |
|
720 | 751 | 0, /* tp_as_mapping */ |
|
721 | 752 | 0, /* tp_hash */ |
|
722 | 753 | 0, /* tp_call */ |
|
723 | 754 | 0, /* tp_str */ |
|
724 | 755 | 0, /* tp_getattro */ |
|
725 | 756 | 0, /* tp_setattro */ |
|
726 | 757 | 0, /* tp_as_buffer */ |
|
727 | 758 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ |
|
728 | 759 | ZstdCompressor__doc__, /* tp_doc */ |
|
729 | 760 | 0, /* tp_traverse */ |
|
730 | 761 | 0, /* tp_clear */ |
|
731 | 762 | 0, /* tp_richcompare */ |
|
732 | 763 | 0, /* tp_weaklistoffset */ |
|
733 | 764 | 0, /* tp_iter */ |
|
734 | 765 | 0, /* tp_iternext */ |
|
735 | 766 | ZstdCompressor_methods, /* tp_methods */ |
|
736 | 767 | 0, /* tp_members */ |
|
737 | 768 | 0, /* tp_getset */ |
|
738 | 769 | 0, /* tp_base */ |
|
739 | 770 | 0, /* tp_dict */ |
|
740 | 771 | 0, /* tp_descr_get */ |
|
741 | 772 | 0, /* tp_descr_set */ |
|
742 | 773 | 0, /* tp_dictoffset */ |
|
743 | 774 | (initproc)ZstdCompressor_init, /* tp_init */ |
|
744 | 775 | 0, /* tp_alloc */ |
|
745 | 776 | PyType_GenericNew, /* tp_new */ |
|
746 | 777 | }; |
|
747 | 778 | |
|
748 | 779 | void compressor_module_init(PyObject* mod) { |
|
749 | 780 | Py_TYPE(&ZstdCompressorType) = &PyType_Type; |
|
750 | 781 | if (PyType_Ready(&ZstdCompressorType) < 0) { |
|
751 | 782 | return; |
|
752 | 783 | } |
|
753 | 784 | |
|
754 | 785 | Py_INCREF((PyObject*)&ZstdCompressorType); |
|
755 | 786 | PyModule_AddObject(mod, "ZstdCompressor", |
|
756 | 787 | (PyObject*)&ZstdCompressorType); |
|
757 | 788 | } |
@@ -1,84 +1,87 b'' | |||
|
1 | 1 | /** |
|
2 | 2 | * Copyright (c) 2016-present, Gregory Szorc |
|
3 | 3 | * All rights reserved. |
|
4 | 4 | * |
|
5 | 5 | * This software may be modified and distributed under the terms |
|
6 | 6 | * of the BSD license. See the LICENSE file for details. |
|
7 | 7 | */ |
|
8 | 8 | |
|
9 | 9 | #include "python-zstandard.h" |
|
10 | 10 | |
|
11 | 11 | extern PyObject* ZstdError; |
|
12 | 12 | |
|
13 | 13 | static char frame_header[] = { |
|
14 | 14 | '\x28', |
|
15 | 15 | '\xb5', |
|
16 | 16 | '\x2f', |
|
17 | 17 | '\xfd', |
|
18 | 18 | }; |
|
19 | 19 | |
|
20 | 20 | void constants_module_init(PyObject* mod) { |
|
21 | 21 | PyObject* version; |
|
22 | 22 | PyObject* zstdVersion; |
|
23 | 23 | PyObject* frameHeader; |
|
24 | 24 | |
|
25 | 25 | #if PY_MAJOR_VERSION >= 3 |
|
26 | 26 | version = PyUnicode_FromString(PYTHON_ZSTANDARD_VERSION); |
|
27 | 27 | #else |
|
28 | 28 | version = PyString_FromString(PYTHON_ZSTANDARD_VERSION); |
|
29 | 29 | #endif |
|
30 | 30 | Py_INCREF(version); |
|
31 | 31 | PyModule_AddObject(mod, "__version__", version); |
|
32 | 32 | |
|
33 | 33 | ZstdError = PyErr_NewException("zstd.ZstdError", NULL, NULL); |
|
34 | 34 | PyModule_AddObject(mod, "ZstdError", ZstdError); |
|
35 | 35 | |
|
36 | PyModule_AddIntConstant(mod, "COMPRESSOBJ_FLUSH_FINISH", compressorobj_flush_finish); | |
|
37 | PyModule_AddIntConstant(mod, "COMPRESSOBJ_FLUSH_BLOCK", compressorobj_flush_block); | |
|
38 | ||
|
36 | 39 | /* For now, the version is a simple tuple instead of a dedicated type. */ |
|
37 | 40 | zstdVersion = PyTuple_New(3); |
|
38 | 41 | PyTuple_SetItem(zstdVersion, 0, PyLong_FromLong(ZSTD_VERSION_MAJOR)); |
|
39 | 42 | PyTuple_SetItem(zstdVersion, 1, PyLong_FromLong(ZSTD_VERSION_MINOR)); |
|
40 | 43 | PyTuple_SetItem(zstdVersion, 2, PyLong_FromLong(ZSTD_VERSION_RELEASE)); |
|
41 | 44 | Py_IncRef(zstdVersion); |
|
42 | 45 | PyModule_AddObject(mod, "ZSTD_VERSION", zstdVersion); |
|
43 | 46 | |
|
44 | 47 | frameHeader = PyBytes_FromStringAndSize(frame_header, sizeof(frame_header)); |
|
45 | 48 | if (frameHeader) { |
|
46 | 49 | PyModule_AddObject(mod, "FRAME_HEADER", frameHeader); |
|
47 | 50 | } |
|
48 | 51 | else { |
|
49 | 52 | PyErr_Format(PyExc_ValueError, "could not create frame header object"); |
|
50 | 53 | } |
|
51 | 54 | |
|
52 | 55 | PyModule_AddIntConstant(mod, "MAX_COMPRESSION_LEVEL", ZSTD_maxCLevel()); |
|
53 | 56 | PyModule_AddIntConstant(mod, "COMPRESSION_RECOMMENDED_INPUT_SIZE", |
|
54 | 57 | (long)ZSTD_CStreamInSize()); |
|
55 | 58 | PyModule_AddIntConstant(mod, "COMPRESSION_RECOMMENDED_OUTPUT_SIZE", |
|
56 | 59 | (long)ZSTD_CStreamOutSize()); |
|
57 | 60 | PyModule_AddIntConstant(mod, "DECOMPRESSION_RECOMMENDED_INPUT_SIZE", |
|
58 | 61 | (long)ZSTD_DStreamInSize()); |
|
59 | 62 | PyModule_AddIntConstant(mod, "DECOMPRESSION_RECOMMENDED_OUTPUT_SIZE", |
|
60 | 63 | (long)ZSTD_DStreamOutSize()); |
|
61 | 64 | |
|
62 | 65 | PyModule_AddIntConstant(mod, "MAGIC_NUMBER", ZSTD_MAGICNUMBER); |
|
63 | 66 | PyModule_AddIntConstant(mod, "WINDOWLOG_MIN", ZSTD_WINDOWLOG_MIN); |
|
64 | 67 | PyModule_AddIntConstant(mod, "WINDOWLOG_MAX", ZSTD_WINDOWLOG_MAX); |
|
65 | 68 | PyModule_AddIntConstant(mod, "CHAINLOG_MIN", ZSTD_CHAINLOG_MIN); |
|
66 | 69 | PyModule_AddIntConstant(mod, "CHAINLOG_MAX", ZSTD_CHAINLOG_MAX); |
|
67 | 70 | PyModule_AddIntConstant(mod, "HASHLOG_MIN", ZSTD_HASHLOG_MIN); |
|
68 | 71 | PyModule_AddIntConstant(mod, "HASHLOG_MAX", ZSTD_HASHLOG_MAX); |
|
69 | 72 | PyModule_AddIntConstant(mod, "HASHLOG3_MAX", ZSTD_HASHLOG3_MAX); |
|
70 | 73 | PyModule_AddIntConstant(mod, "SEARCHLOG_MIN", ZSTD_SEARCHLOG_MIN); |
|
71 | 74 | PyModule_AddIntConstant(mod, "SEARCHLOG_MAX", ZSTD_SEARCHLOG_MAX); |
|
72 | 75 | PyModule_AddIntConstant(mod, "SEARCHLENGTH_MIN", ZSTD_SEARCHLENGTH_MIN); |
|
73 | 76 | PyModule_AddIntConstant(mod, "SEARCHLENGTH_MAX", ZSTD_SEARCHLENGTH_MAX); |
|
74 | 77 | PyModule_AddIntConstant(mod, "TARGETLENGTH_MIN", ZSTD_TARGETLENGTH_MIN); |
|
75 | 78 | PyModule_AddIntConstant(mod, "TARGETLENGTH_MAX", ZSTD_TARGETLENGTH_MAX); |
|
76 | 79 | |
|
77 | 80 | PyModule_AddIntConstant(mod, "STRATEGY_FAST", ZSTD_fast); |
|
78 | 81 | PyModule_AddIntConstant(mod, "STRATEGY_DFAST", ZSTD_dfast); |
|
79 | 82 | PyModule_AddIntConstant(mod, "STRATEGY_GREEDY", ZSTD_greedy); |
|
80 | 83 | PyModule_AddIntConstant(mod, "STRATEGY_LAZY", ZSTD_lazy); |
|
81 | 84 | PyModule_AddIntConstant(mod, "STRATEGY_LAZY2", ZSTD_lazy2); |
|
82 | 85 | PyModule_AddIntConstant(mod, "STRATEGY_BTLAZY2", ZSTD_btlazy2); |
|
83 | 86 | PyModule_AddIntConstant(mod, "STRATEGY_BTOPT", ZSTD_btopt); |
|
84 | 87 | } |
@@ -1,187 +1,187 b'' | |||
|
1 | 1 | /** |
|
2 | 2 | * Copyright (c) 2016-present, Gregory Szorc |
|
3 | 3 | * All rights reserved. |
|
4 | 4 | * |
|
5 | 5 | * This software may be modified and distributed under the terms |
|
6 | 6 | * of the BSD license. See the LICENSE file for details. |
|
7 | 7 | */ |
|
8 | 8 | |
|
9 | 9 | #include "python-zstandard.h" |
|
10 | 10 | |
|
11 | 11 | extern PyObject* ZstdError; |
|
12 | 12 | |
|
13 | 13 | PyDoc_STRVAR(ZstdDecompressionWriter__doc, |
|
14 | 14 | """A context manager used for writing decompressed output.\n" |
|
15 | 15 | ); |
|
16 | 16 | |
|
17 | 17 | static void ZstdDecompressionWriter_dealloc(ZstdDecompressionWriter* self) { |
|
18 | 18 | Py_XDECREF(self->decompressor); |
|
19 | 19 | Py_XDECREF(self->writer); |
|
20 | 20 | |
|
21 | 21 | if (self->dstream) { |
|
22 | 22 | ZSTD_freeDStream(self->dstream); |
|
23 | 23 | self->dstream = NULL; |
|
24 | 24 | } |
|
25 | 25 | |
|
26 | 26 | PyObject_Del(self); |
|
27 | 27 | } |
|
28 | 28 | |
|
29 | 29 | static PyObject* ZstdDecompressionWriter_enter(ZstdDecompressionWriter* self) { |
|
30 | 30 | if (self->entered) { |
|
31 | 31 | PyErr_SetString(ZstdError, "cannot __enter__ multiple times"); |
|
32 | 32 | return NULL; |
|
33 | 33 | } |
|
34 | 34 | |
|
35 | 35 | self->dstream = DStream_from_ZstdDecompressor(self->decompressor); |
|
36 | 36 | if (!self->dstream) { |
|
37 | 37 | return NULL; |
|
38 | 38 | } |
|
39 | 39 | |
|
40 | 40 | self->entered = 1; |
|
41 | 41 | |
|
42 | 42 | Py_INCREF(self); |
|
43 | 43 | return (PyObject*)self; |
|
44 | 44 | } |
|
45 | 45 | |
|
46 | 46 | static PyObject* ZstdDecompressionWriter_exit(ZstdDecompressionWriter* self, PyObject* args) { |
|
47 | 47 | self->entered = 0; |
|
48 | 48 | |
|
49 | 49 | if (self->dstream) { |
|
50 | 50 | ZSTD_freeDStream(self->dstream); |
|
51 | 51 | self->dstream = NULL; |
|
52 | 52 | } |
|
53 | 53 | |
|
54 | 54 | Py_RETURN_FALSE; |
|
55 | 55 | } |
|
56 | 56 | |
|
57 | 57 | static PyObject* ZstdDecompressionWriter_memory_size(ZstdDecompressionWriter* self) { |
|
58 | 58 | if (!self->dstream) { |
|
59 | 59 | PyErr_SetString(ZstdError, "cannot determine size of inactive decompressor; " |
|
60 | 60 | "call when context manager is active"); |
|
61 | 61 | return NULL; |
|
62 | 62 | } |
|
63 | 63 | |
|
64 | 64 | return PyLong_FromSize_t(ZSTD_sizeof_DStream(self->dstream)); |
|
65 | 65 | } |
|
66 | 66 | |
|
67 | 67 | static PyObject* ZstdDecompressionWriter_write(ZstdDecompressionWriter* self, PyObject* args) { |
|
68 | 68 | const char* source; |
|
69 | 69 | Py_ssize_t sourceSize; |
|
70 | 70 | size_t zresult = 0; |
|
71 | 71 | ZSTD_inBuffer input; |
|
72 | 72 | ZSTD_outBuffer output; |
|
73 | 73 | PyObject* res; |
|
74 | 74 | |
|
75 | 75 | #if PY_MAJOR_VERSION >= 3 |
|
76 | 76 | if (!PyArg_ParseTuple(args, "y#", &source, &sourceSize)) { |
|
77 | 77 | #else |
|
78 | 78 | if (!PyArg_ParseTuple(args, "s#", &source, &sourceSize)) { |
|
79 | 79 | #endif |
|
80 | 80 | return NULL; |
|
81 | 81 | } |
|
82 | 82 | |
|
83 | 83 | if (!self->entered) { |
|
84 | 84 | PyErr_SetString(ZstdError, "write must be called from an active context manager"); |
|
85 | 85 | return NULL; |
|
86 | 86 | } |
|
87 | 87 | |
|
88 | output.dst = malloc(self->outSize); | |
|
88 | output.dst = PyMem_Malloc(self->outSize); | |
|
89 | 89 | if (!output.dst) { |
|
90 | 90 | return PyErr_NoMemory(); |
|
91 | 91 | } |
|
92 | 92 | output.size = self->outSize; |
|
93 | 93 | output.pos = 0; |
|
94 | 94 | |
|
95 | 95 | input.src = source; |
|
96 | 96 | input.size = sourceSize; |
|
97 | 97 | input.pos = 0; |
|
98 | 98 | |
|
99 | 99 | while ((ssize_t)input.pos < sourceSize) { |
|
100 | 100 | Py_BEGIN_ALLOW_THREADS |
|
101 | 101 | zresult = ZSTD_decompressStream(self->dstream, &output, &input); |
|
102 | 102 | Py_END_ALLOW_THREADS |
|
103 | 103 | |
|
104 | 104 | if (ZSTD_isError(zresult)) { |
|
105 |
|
|
|
105 | PyMem_Free(output.dst); | |
|
106 | 106 | PyErr_Format(ZstdError, "zstd decompress error: %s", |
|
107 | 107 | ZSTD_getErrorName(zresult)); |
|
108 | 108 | return NULL; |
|
109 | 109 | } |
|
110 | 110 | |
|
111 | 111 | if (output.pos) { |
|
112 | 112 | #if PY_MAJOR_VERSION >= 3 |
|
113 | 113 | res = PyObject_CallMethod(self->writer, "write", "y#", |
|
114 | 114 | #else |
|
115 | 115 | res = PyObject_CallMethod(self->writer, "write", "s#", |
|
116 | 116 | #endif |
|
117 | 117 | output.dst, output.pos); |
|
118 | 118 | Py_XDECREF(res); |
|
119 | 119 | output.pos = 0; |
|
120 | 120 | } |
|
121 | 121 | } |
|
122 | 122 | |
|
123 |
|
|
|
123 | PyMem_Free(output.dst); | |
|
124 | 124 | |
|
125 | 125 | /* TODO return bytes written */ |
|
126 | 126 | Py_RETURN_NONE; |
|
127 | 127 | } |
|
128 | 128 | |
|
129 | 129 | static PyMethodDef ZstdDecompressionWriter_methods[] = { |
|
130 | 130 | { "__enter__", (PyCFunction)ZstdDecompressionWriter_enter, METH_NOARGS, |
|
131 | 131 | PyDoc_STR("Enter a decompression context.") }, |
|
132 | 132 | { "__exit__", (PyCFunction)ZstdDecompressionWriter_exit, METH_VARARGS, |
|
133 | 133 | PyDoc_STR("Exit a decompression context.") }, |
|
134 | 134 | { "memory_size", (PyCFunction)ZstdDecompressionWriter_memory_size, METH_NOARGS, |
|
135 | 135 | PyDoc_STR("Obtain the memory size in bytes of the underlying decompressor.") }, |
|
136 | 136 | { "write", (PyCFunction)ZstdDecompressionWriter_write, METH_VARARGS, |
|
137 | 137 | PyDoc_STR("Compress data") }, |
|
138 | 138 | { NULL, NULL } |
|
139 | 139 | }; |
|
140 | 140 | |
|
141 | 141 | PyTypeObject ZstdDecompressionWriterType = { |
|
142 | 142 | PyVarObject_HEAD_INIT(NULL, 0) |
|
143 | 143 | "zstd.ZstdDecompressionWriter", /* tp_name */ |
|
144 | 144 | sizeof(ZstdDecompressionWriter),/* tp_basicsize */ |
|
145 | 145 | 0, /* tp_itemsize */ |
|
146 | 146 | (destructor)ZstdDecompressionWriter_dealloc, /* tp_dealloc */ |
|
147 | 147 | 0, /* tp_print */ |
|
148 | 148 | 0, /* tp_getattr */ |
|
149 | 149 | 0, /* tp_setattr */ |
|
150 | 150 | 0, /* tp_compare */ |
|
151 | 151 | 0, /* tp_repr */ |
|
152 | 152 | 0, /* tp_as_number */ |
|
153 | 153 | 0, /* tp_as_sequence */ |
|
154 | 154 | 0, /* tp_as_mapping */ |
|
155 | 155 | 0, /* tp_hash */ |
|
156 | 156 | 0, /* tp_call */ |
|
157 | 157 | 0, /* tp_str */ |
|
158 | 158 | 0, /* tp_getattro */ |
|
159 | 159 | 0, /* tp_setattro */ |
|
160 | 160 | 0, /* tp_as_buffer */ |
|
161 | 161 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ |
|
162 | 162 | ZstdDecompressionWriter__doc, /* tp_doc */ |
|
163 | 163 | 0, /* tp_traverse */ |
|
164 | 164 | 0, /* tp_clear */ |
|
165 | 165 | 0, /* tp_richcompare */ |
|
166 | 166 | 0, /* tp_weaklistoffset */ |
|
167 | 167 | 0, /* tp_iter */ |
|
168 | 168 | 0, /* tp_iternext */ |
|
169 | 169 | ZstdDecompressionWriter_methods,/* tp_methods */ |
|
170 | 170 | 0, /* tp_members */ |
|
171 | 171 | 0, /* tp_getset */ |
|
172 | 172 | 0, /* tp_base */ |
|
173 | 173 | 0, /* tp_dict */ |
|
174 | 174 | 0, /* tp_descr_get */ |
|
175 | 175 | 0, /* tp_descr_set */ |
|
176 | 176 | 0, /* tp_dictoffset */ |
|
177 | 177 | 0, /* tp_init */ |
|
178 | 178 | 0, /* tp_alloc */ |
|
179 | 179 | PyType_GenericNew, /* tp_new */ |
|
180 | 180 | }; |
|
181 | 181 | |
|
182 | 182 | void decompressionwriter_module_init(PyObject* mod) { |
|
183 | 183 | Py_TYPE(&ZstdDecompressionWriterType) = &PyType_Type; |
|
184 | 184 | if (PyType_Ready(&ZstdDecompressionWriterType) < 0) { |
|
185 | 185 | return; |
|
186 | 186 | } |
|
187 | 187 | } |
@@ -1,172 +1,178 b'' | |||
|
1 | 1 | /** |
|
2 | 2 | * Copyright (c) 2016-present, Gregory Szorc |
|
3 | 3 | * All rights reserved. |
|
4 | 4 | * |
|
5 | 5 | * This software may be modified and distributed under the terms |
|
6 | 6 | * of the BSD license. See the LICENSE file for details. |
|
7 | 7 | */ |
|
8 | 8 | |
|
9 | 9 | #define PY_SSIZE_T_CLEAN |
|
10 | 10 | #include <Python.h> |
|
11 | 11 | |
|
12 | 12 | #define ZSTD_STATIC_LINKING_ONLY |
|
13 | 13 | #define ZDICT_STATIC_LINKING_ONLY |
|
14 | 14 | #include "mem.h" |
|
15 | 15 | #include "zstd.h" |
|
16 | 16 | #include "zdict.h" |
|
17 | 17 | |
|
18 |
#define PYTHON_ZSTANDARD_VERSION "0. |
|
|
18 | #define PYTHON_ZSTANDARD_VERSION "0.6.0" | |
|
19 | ||
|
20 | typedef enum { | |
|
21 | compressorobj_flush_finish, | |
|
22 | compressorobj_flush_block, | |
|
23 | } CompressorObj_Flush; | |
|
19 | 24 | |
|
20 | 25 | typedef struct { |
|
21 | 26 | PyObject_HEAD |
|
22 | 27 | unsigned windowLog; |
|
23 | 28 | unsigned chainLog; |
|
24 | 29 | unsigned hashLog; |
|
25 | 30 | unsigned searchLog; |
|
26 | 31 | unsigned searchLength; |
|
27 | 32 | unsigned targetLength; |
|
28 | 33 | ZSTD_strategy strategy; |
|
29 | 34 | } CompressionParametersObject; |
|
30 | 35 | |
|
31 | 36 | extern PyTypeObject CompressionParametersType; |
|
32 | 37 | |
|
33 | 38 | typedef struct { |
|
34 | 39 | PyObject_HEAD |
|
35 | 40 | unsigned selectivityLevel; |
|
36 | 41 | int compressionLevel; |
|
37 | 42 | unsigned notificationLevel; |
|
38 | 43 | unsigned dictID; |
|
39 | 44 | } DictParametersObject; |
|
40 | 45 | |
|
41 | 46 | extern PyTypeObject DictParametersType; |
|
42 | 47 | |
|
43 | 48 | typedef struct { |
|
44 | 49 | PyObject_HEAD |
|
45 | 50 | |
|
46 | 51 | void* dictData; |
|
47 | 52 | size_t dictSize; |
|
48 | 53 | } ZstdCompressionDict; |
|
49 | 54 | |
|
50 | 55 | extern PyTypeObject ZstdCompressionDictType; |
|
51 | 56 | |
|
52 | 57 | typedef struct { |
|
53 | 58 | PyObject_HEAD |
|
54 | 59 | |
|
55 | 60 | int compressionLevel; |
|
56 | 61 | ZstdCompressionDict* dict; |
|
62 | ZSTD_CCtx* cctx; | |
|
57 | 63 | ZSTD_CDict* cdict; |
|
58 | 64 | CompressionParametersObject* cparams; |
|
59 | 65 | ZSTD_frameParameters fparams; |
|
60 | 66 | } ZstdCompressor; |
|
61 | 67 | |
|
62 | 68 | extern PyTypeObject ZstdCompressorType; |
|
63 | 69 | |
|
64 | 70 | typedef struct { |
|
65 | 71 | PyObject_HEAD |
|
66 | 72 | |
|
67 | 73 | ZstdCompressor* compressor; |
|
68 | 74 | ZSTD_CStream* cstream; |
|
69 | 75 | ZSTD_outBuffer output; |
|
70 |
int f |
|
|
76 | int finished; | |
|
71 | 77 | } ZstdCompressionObj; |
|
72 | 78 | |
|
73 | 79 | extern PyTypeObject ZstdCompressionObjType; |
|
74 | 80 | |
|
75 | 81 | typedef struct { |
|
76 | 82 | PyObject_HEAD |
|
77 | 83 | |
|
78 | 84 | ZstdCompressor* compressor; |
|
79 | 85 | PyObject* writer; |
|
80 | 86 | Py_ssize_t sourceSize; |
|
81 | 87 | size_t outSize; |
|
82 | 88 | ZSTD_CStream* cstream; |
|
83 | 89 | int entered; |
|
84 | 90 | } ZstdCompressionWriter; |
|
85 | 91 | |
|
86 | 92 | extern PyTypeObject ZstdCompressionWriterType; |
|
87 | 93 | |
|
88 | 94 | typedef struct { |
|
89 | 95 | PyObject_HEAD |
|
90 | 96 | |
|
91 | 97 | ZstdCompressor* compressor; |
|
92 | 98 | PyObject* reader; |
|
93 | 99 | Py_buffer* buffer; |
|
94 | 100 | Py_ssize_t bufferOffset; |
|
95 | 101 | Py_ssize_t sourceSize; |
|
96 | 102 | size_t inSize; |
|
97 | 103 | size_t outSize; |
|
98 | 104 | |
|
99 | 105 | ZSTD_CStream* cstream; |
|
100 | 106 | ZSTD_inBuffer input; |
|
101 | 107 | ZSTD_outBuffer output; |
|
102 | 108 | int finishedOutput; |
|
103 | 109 | int finishedInput; |
|
104 | 110 | PyObject* readResult; |
|
105 | 111 | } ZstdCompressorIterator; |
|
106 | 112 | |
|
107 | 113 | extern PyTypeObject ZstdCompressorIteratorType; |
|
108 | 114 | |
|
109 | 115 | typedef struct { |
|
110 | 116 | PyObject_HEAD |
|
111 | 117 | |
|
112 | 118 | ZSTD_DCtx* refdctx; |
|
113 | 119 | |
|
114 | 120 | ZstdCompressionDict* dict; |
|
115 | 121 | ZSTD_DDict* ddict; |
|
116 | 122 | } ZstdDecompressor; |
|
117 | 123 | |
|
118 | 124 | extern PyTypeObject ZstdDecompressorType; |
|
119 | 125 | |
|
120 | 126 | typedef struct { |
|
121 | 127 | PyObject_HEAD |
|
122 | 128 | |
|
123 | 129 | ZstdDecompressor* decompressor; |
|
124 | 130 | ZSTD_DStream* dstream; |
|
125 | 131 | int finished; |
|
126 | 132 | } ZstdDecompressionObj; |
|
127 | 133 | |
|
128 | 134 | extern PyTypeObject ZstdDecompressionObjType; |
|
129 | 135 | |
|
130 | 136 | typedef struct { |
|
131 | 137 | PyObject_HEAD |
|
132 | 138 | |
|
133 | 139 | ZstdDecompressor* decompressor; |
|
134 | 140 | PyObject* writer; |
|
135 | 141 | size_t outSize; |
|
136 | 142 | ZSTD_DStream* dstream; |
|
137 | 143 | int entered; |
|
138 | 144 | } ZstdDecompressionWriter; |
|
139 | 145 | |
|
140 | 146 | extern PyTypeObject ZstdDecompressionWriterType; |
|
141 | 147 | |
|
142 | 148 | typedef struct { |
|
143 | 149 | PyObject_HEAD |
|
144 | 150 | |
|
145 | 151 | ZstdDecompressor* decompressor; |
|
146 | 152 | PyObject* reader; |
|
147 | 153 | Py_buffer* buffer; |
|
148 | 154 | Py_ssize_t bufferOffset; |
|
149 | 155 | size_t inSize; |
|
150 | 156 | size_t outSize; |
|
151 | 157 | size_t skipBytes; |
|
152 | 158 | ZSTD_DStream* dstream; |
|
153 | 159 | ZSTD_inBuffer input; |
|
154 | 160 | ZSTD_outBuffer output; |
|
155 | 161 | Py_ssize_t readCount; |
|
156 | 162 | int finishedInput; |
|
157 | 163 | int finishedOutput; |
|
158 | 164 | } ZstdDecompressorIterator; |
|
159 | 165 | |
|
160 | 166 | extern PyTypeObject ZstdDecompressorIteratorType; |
|
161 | 167 | |
|
162 | 168 | typedef struct { |
|
163 | 169 | int errored; |
|
164 | 170 | PyObject* chunk; |
|
165 | 171 | } DecompressorIteratorResult; |
|
166 | 172 | |
|
167 | 173 | void ztopy_compression_parameters(CompressionParametersObject* params, ZSTD_compressionParameters* zparams); |
|
168 | 174 | CompressionParametersObject* get_compression_parameters(PyObject* self, PyObject* args); |
|
169 | 175 | PyObject* estimate_compression_context_size(PyObject* self, PyObject* args); |
|
170 | 176 | ZSTD_CStream* CStream_from_ZstdCompressor(ZstdCompressor* compressor, Py_ssize_t sourceSize); |
|
171 | 177 | ZSTD_DStream* DStream_from_ZstdDecompressor(ZstdDecompressor* decompressor); |
|
172 | 178 | ZstdCompressionDict* train_dictionary(PyObject* self, PyObject* args, PyObject* kwargs); |
@@ -1,110 +1,108 b'' | |||
|
1 | 1 | # Copyright (c) 2016-present, Gregory Szorc |
|
2 | 2 | # All rights reserved. |
|
3 | 3 | # |
|
4 | 4 | # This software may be modified and distributed under the terms |
|
5 | 5 | # of the BSD license. See the LICENSE file for details. |
|
6 | 6 | |
|
7 | 7 | from __future__ import absolute_import |
|
8 | 8 | |
|
9 | 9 | import cffi |
|
10 | import distutils.ccompiler | |
|
10 | 11 | import os |
|
12 | import subprocess | |
|
13 | import tempfile | |
|
11 | 14 | |
|
12 | 15 | |
|
13 | 16 | HERE = os.path.abspath(os.path.dirname(__file__)) |
|
14 | 17 | |
|
15 | 18 | SOURCES = ['zstd/%s' % p for p in ( |
|
16 | 19 | 'common/entropy_common.c', |
|
17 | 20 | 'common/error_private.c', |
|
18 | 21 | 'common/fse_decompress.c', |
|
19 | 22 | 'common/xxhash.c', |
|
20 | 23 | 'common/zstd_common.c', |
|
21 | 24 | 'compress/fse_compress.c', |
|
22 | 25 | 'compress/huf_compress.c', |
|
23 | 'compress/zbuff_compress.c', | |
|
24 | 26 | 'compress/zstd_compress.c', |
|
25 | 27 | 'decompress/huf_decompress.c', |
|
26 | 'decompress/zbuff_decompress.c', | |
|
27 | 28 | 'decompress/zstd_decompress.c', |
|
28 | 29 | 'dictBuilder/divsufsort.c', |
|
29 | 30 | 'dictBuilder/zdict.c', |
|
30 | 31 | )] |
|
31 | 32 | |
|
32 | 33 | INCLUDE_DIRS = [os.path.join(HERE, d) for d in ( |
|
33 | 34 | 'zstd', |
|
34 | 35 | 'zstd/common', |
|
35 | 36 | 'zstd/compress', |
|
36 | 37 | 'zstd/decompress', |
|
37 | 38 | 'zstd/dictBuilder', |
|
38 | 39 | )] |
|
39 | 40 | |
|
41 | # cffi can't parse some of the primitives in zstd.h. So we invoke the | |
|
42 | # preprocessor and feed its output into cffi. | |
|
43 | compiler = distutils.ccompiler.new_compiler() | |
|
44 | ||
|
45 | # Needed for MSVC. | |
|
46 | if hasattr(compiler, 'initialize'): | |
|
47 | compiler.initialize() | |
|
48 | ||
|
49 | # Distutils doesn't set compiler.preprocessor, so invoke the preprocessor | |
|
50 | # manually. | |
|
51 | if compiler.compiler_type == 'unix': | |
|
52 | args = list(compiler.executables['compiler']) | |
|
53 | args.extend([ | |
|
54 | '-E', | |
|
55 | '-DZSTD_STATIC_LINKING_ONLY', | |
|
56 | ]) | |
|
57 | elif compiler.compiler_type == 'msvc': | |
|
58 | args = [compiler.cc] | |
|
59 | args.extend([ | |
|
60 | '/EP', | |
|
61 | '/DZSTD_STATIC_LINKING_ONLY', | |
|
62 | ]) | |
|
63 | else: | |
|
64 | raise Exception('unsupported compiler type: %s' % compiler.compiler_type) | |
|
65 | ||
|
66 | # zstd.h includes <stddef.h>, which is also included by cffi's boilerplate. | |
|
67 | # This can lead to duplicate declarations. So we strip this include from the | |
|
68 | # preprocessor invocation. | |
|
69 | ||
|
40 | 70 | with open(os.path.join(HERE, 'zstd', 'zstd.h'), 'rb') as fh: |
|
41 | zstd_h = fh.read() | |
|
71 | lines = [l for l in fh if not l.startswith(b'#include <stddef.h>')] | |
|
72 | ||
|
73 | fd, input_file = tempfile.mkstemp(suffix='.h') | |
|
74 | os.write(fd, b''.join(lines)) | |
|
75 | os.close(fd) | |
|
76 | ||
|
77 | args.append(input_file) | |
|
78 | ||
|
79 | try: | |
|
80 | process = subprocess.Popen(args, stdout=subprocess.PIPE) | |
|
81 | output = process.communicate()[0] | |
|
82 | ret = process.poll() | |
|
83 | if ret: | |
|
84 | raise Exception('preprocessor exited with error') | |
|
85 | finally: | |
|
86 | os.unlink(input_file) | |
|
87 | ||
|
88 | def normalize_output(): | |
|
89 | lines = [] | |
|
90 | for line in output.splitlines(): | |
|
91 | # CFFI's parser doesn't like __attribute__ on UNIX compilers. | |
|
92 | if line.startswith(b'__attribute__ ((visibility ("default"))) '): | |
|
93 | line = line[len(b'__attribute__ ((visibility ("default"))) '):] | |
|
94 | ||
|
95 | lines.append(line) | |
|
96 | ||
|
97 | return b'\n'.join(lines) | |
|
42 | 98 | |
|
43 | 99 | ffi = cffi.FFI() |
|
44 | 100 | ffi.set_source('_zstd_cffi', ''' |
|
45 | /* needed for typedefs like U32 references in zstd.h */ | |
|
46 | #include "mem.h" | |
|
47 | 101 | #define ZSTD_STATIC_LINKING_ONLY |
|
48 | 102 | #include "zstd.h" |
|
49 | ''', | |
|
50 | sources=SOURCES, include_dirs=INCLUDE_DIRS) | |
|
51 | ||
|
52 | # Rather than define the API definitions from zstd.h inline, munge the | |
|
53 | # source in a way that cdef() will accept. | |
|
54 | lines = zstd_h.splitlines() | |
|
55 | lines = [l.rstrip() for l in lines if l.strip()] | |
|
56 | ||
|
57 | # Strip preprocessor directives - they aren't important for our needs. | |
|
58 | lines = [l for l in lines | |
|
59 | if not l.startswith((b'#if', b'#else', b'#endif', b'#include'))] | |
|
60 | ||
|
61 | # Remove extern C block | |
|
62 | lines = [l for l in lines if l not in (b'extern "C" {', b'}')] | |
|
63 | ||
|
64 | # The version #defines don't parse and aren't necessary. Strip them. | |
|
65 | lines = [l for l in lines if not l.startswith(( | |
|
66 | b'#define ZSTD_H_235446', | |
|
67 | b'#define ZSTD_LIB_VERSION', | |
|
68 | b'#define ZSTD_QUOTE', | |
|
69 | b'#define ZSTD_EXPAND_AND_QUOTE', | |
|
70 | b'#define ZSTD_VERSION_STRING', | |
|
71 | b'#define ZSTD_VERSION_NUMBER'))] | |
|
103 | ''', sources=SOURCES, include_dirs=INCLUDE_DIRS) | |
|
72 | 104 | |
|
73 | # The C parser also doesn't like some constant defines referencing | |
|
74 | # other constants. | |
|
75 | # TODO we pick the 64-bit constants here. We should assert somewhere | |
|
76 | # we're compiling for 64-bit. | |
|
77 | def fix_constants(l): | |
|
78 | if l.startswith(b'#define ZSTD_WINDOWLOG_MAX '): | |
|
79 | return b'#define ZSTD_WINDOWLOG_MAX 27' | |
|
80 | elif l.startswith(b'#define ZSTD_CHAINLOG_MAX '): | |
|
81 | return b'#define ZSTD_CHAINLOG_MAX 28' | |
|
82 | elif l.startswith(b'#define ZSTD_HASHLOG_MAX '): | |
|
83 | return b'#define ZSTD_HASHLOG_MAX 27' | |
|
84 | elif l.startswith(b'#define ZSTD_CHAINLOG_MAX '): | |
|
85 | return b'#define ZSTD_CHAINLOG_MAX 28' | |
|
86 | elif l.startswith(b'#define ZSTD_CHAINLOG_MIN '): | |
|
87 | return b'#define ZSTD_CHAINLOG_MIN 6' | |
|
88 | elif l.startswith(b'#define ZSTD_SEARCHLOG_MAX '): | |
|
89 | return b'#define ZSTD_SEARCHLOG_MAX 26' | |
|
90 | elif l.startswith(b'#define ZSTD_BLOCKSIZE_ABSOLUTEMAX '): | |
|
91 | return b'#define ZSTD_BLOCKSIZE_ABSOLUTEMAX 131072' | |
|
92 | else: | |
|
93 | return l | |
|
94 | lines = map(fix_constants, lines) | |
|
95 | ||
|
96 | # ZSTDLIB_API isn't handled correctly. Strip it. | |
|
97 | lines = [l for l in lines if not l.startswith(b'# define ZSTDLIB_API')] | |
|
98 | def strip_api(l): | |
|
99 | if l.startswith(b'ZSTDLIB_API '): | |
|
100 | return l[len(b'ZSTDLIB_API '):] | |
|
101 | else: | |
|
102 | return l | |
|
103 | lines = map(strip_api, lines) | |
|
104 | ||
|
105 | source = b'\n'.join(lines) | |
|
106 | ffi.cdef(source.decode('latin1')) | |
|
107 | ||
|
105 | ffi.cdef(normalize_output().decode('latin1')) | |
|
108 | 106 | |
|
109 | 107 | if __name__ == '__main__': |
|
110 | 108 | ffi.compile() |
@@ -1,62 +1,69 b'' | |||
|
1 | 1 | #!/usr/bin/env python |
|
2 | 2 | # Copyright (c) 2016-present, Gregory Szorc |
|
3 | 3 | # All rights reserved. |
|
4 | 4 | # |
|
5 | 5 | # This software may be modified and distributed under the terms |
|
6 | 6 | # of the BSD license. See the LICENSE file for details. |
|
7 | 7 | |
|
8 | import sys | |
|
8 | 9 | from setuptools import setup |
|
9 | 10 | |
|
10 | 11 | try: |
|
11 | 12 | import cffi |
|
12 | 13 | except ImportError: |
|
13 | 14 | cffi = None |
|
14 | 15 | |
|
15 | 16 | import setup_zstd |
|
16 | 17 | |
|
18 | SUPPORT_LEGACY = False | |
|
19 | ||
|
20 | if "--legacy" in sys.argv: | |
|
21 | SUPPORT_LEGACY = True | |
|
22 | sys.argv.remove("--legacy") | |
|
23 | ||
|
17 | 24 | # Code for obtaining the Extension instance is in its own module to |
|
18 | 25 | # facilitate reuse in other projects. |
|
19 | extensions = [setup_zstd.get_c_extension()] | |
|
26 | extensions = [setup_zstd.get_c_extension(SUPPORT_LEGACY, 'zstd')] | |
|
20 | 27 | |
|
21 | 28 | if cffi: |
|
22 | 29 | import make_cffi |
|
23 | 30 | extensions.append(make_cffi.ffi.distutils_extension()) |
|
24 | 31 | |
|
25 | 32 | version = None |
|
26 | 33 | |
|
27 | 34 | with open('c-ext/python-zstandard.h', 'r') as fh: |
|
28 | 35 | for line in fh: |
|
29 | 36 | if not line.startswith('#define PYTHON_ZSTANDARD_VERSION'): |
|
30 | 37 | continue |
|
31 | 38 | |
|
32 | 39 | version = line.split()[2][1:-1] |
|
33 | 40 | break |
|
34 | 41 | |
|
35 | 42 | if not version: |
|
36 | 43 | raise Exception('could not resolve package version; ' |
|
37 | 44 | 'this should never happen') |
|
38 | 45 | |
|
39 | 46 | setup( |
|
40 | 47 | name='zstandard', |
|
41 | 48 | version=version, |
|
42 | 49 | description='Zstandard bindings for Python', |
|
43 | 50 | long_description=open('README.rst', 'r').read(), |
|
44 | 51 | url='https://github.com/indygreg/python-zstandard', |
|
45 | 52 | author='Gregory Szorc', |
|
46 | 53 | author_email='gregory.szorc@gmail.com', |
|
47 | 54 | license='BSD', |
|
48 | 55 | classifiers=[ |
|
49 | 56 | 'Development Status :: 4 - Beta', |
|
50 | 57 | 'Intended Audience :: Developers', |
|
51 | 58 | 'License :: OSI Approved :: BSD License', |
|
52 | 59 | 'Programming Language :: C', |
|
53 | 60 | 'Programming Language :: Python :: 2.6', |
|
54 | 61 | 'Programming Language :: Python :: 2.7', |
|
55 | 62 | 'Programming Language :: Python :: 3.3', |
|
56 | 63 | 'Programming Language :: Python :: 3.4', |
|
57 | 64 | 'Programming Language :: Python :: 3.5', |
|
58 | 65 | ], |
|
59 | 66 | keywords='zstandard zstd compression', |
|
60 | 67 | ext_modules=extensions, |
|
61 | 68 | test_suite='tests', |
|
62 | 69 | ) |
@@ -1,64 +1,91 b'' | |||
|
1 | 1 | # Copyright (c) 2016-present, Gregory Szorc |
|
2 | 2 | # All rights reserved. |
|
3 | 3 | # |
|
4 | 4 | # This software may be modified and distributed under the terms |
|
5 | 5 | # of the BSD license. See the LICENSE file for details. |
|
6 | 6 | |
|
7 | 7 | import os |
|
8 | 8 | from distutils.extension import Extension |
|
9 | 9 | |
|
10 | 10 | |
|
11 | 11 | zstd_sources = ['zstd/%s' % p for p in ( |
|
12 | 12 | 'common/entropy_common.c', |
|
13 | 13 | 'common/error_private.c', |
|
14 | 14 | 'common/fse_decompress.c', |
|
15 | 15 | 'common/xxhash.c', |
|
16 | 16 | 'common/zstd_common.c', |
|
17 | 17 | 'compress/fse_compress.c', |
|
18 | 18 | 'compress/huf_compress.c', |
|
19 | 'compress/zbuff_compress.c', | |
|
20 | 19 | 'compress/zstd_compress.c', |
|
21 | 20 | 'decompress/huf_decompress.c', |
|
22 | 'decompress/zbuff_decompress.c', | |
|
23 | 21 | 'decompress/zstd_decompress.c', |
|
24 | 22 | 'dictBuilder/divsufsort.c', |
|
25 | 23 | 'dictBuilder/zdict.c', |
|
26 | 24 | )] |
|
27 | 25 | |
|
26 | zstd_sources_legacy = ['zstd/%s' % p for p in ( | |
|
27 | 'deprecated/zbuff_compress.c', | |
|
28 | 'deprecated/zbuff_decompress.c', | |
|
29 | 'legacy/zstd_v01.c', | |
|
30 | 'legacy/zstd_v02.c', | |
|
31 | 'legacy/zstd_v03.c', | |
|
32 | 'legacy/zstd_v04.c', | |
|
33 | 'legacy/zstd_v05.c', | |
|
34 | 'legacy/zstd_v06.c', | |
|
35 | 'legacy/zstd_v07.c' | |
|
36 | )] | |
|
28 | 37 | |
|
29 | 38 | zstd_includes = [ |
|
30 | 39 | 'c-ext', |
|
31 | 40 | 'zstd', |
|
32 | 41 | 'zstd/common', |
|
33 | 42 | 'zstd/compress', |
|
34 | 43 | 'zstd/decompress', |
|
35 | 44 | 'zstd/dictBuilder', |
|
36 | 45 | ] |
|
37 | 46 | |
|
47 | zstd_includes_legacy = [ | |
|
48 | 'zstd/deprecated', | |
|
49 | 'zstd/legacy', | |
|
50 | ] | |
|
51 | ||
|
38 | 52 | ext_sources = [ |
|
39 | 53 | 'zstd.c', |
|
40 | 54 | 'c-ext/compressiondict.c', |
|
41 | 55 | 'c-ext/compressobj.c', |
|
42 | 56 | 'c-ext/compressor.c', |
|
43 | 57 | 'c-ext/compressoriterator.c', |
|
44 | 58 | 'c-ext/compressionparams.c', |
|
45 | 59 | 'c-ext/compressionwriter.c', |
|
46 | 60 | 'c-ext/constants.c', |
|
47 | 61 | 'c-ext/decompressobj.c', |
|
48 | 62 | 'c-ext/decompressor.c', |
|
49 | 63 | 'c-ext/decompressoriterator.c', |
|
50 | 64 | 'c-ext/decompressionwriter.c', |
|
51 | 65 | 'c-ext/dictparams.c', |
|
52 | 66 | ] |
|
53 | 67 | |
|
68 | zstd_depends = [ | |
|
69 | 'c-ext/python-zstandard.h', | |
|
70 | ] | |
|
54 | 71 | |
|
55 | def get_c_extension(name='zstd'): | |
|
72 | ||
|
73 | def get_c_extension(support_legacy=False, name='zstd'): | |
|
56 | 74 | """Obtain a distutils.extension.Extension for the C extension.""" |
|
57 | 75 | root = os.path.abspath(os.path.dirname(__file__)) |
|
58 | 76 | |
|
59 | 77 | sources = [os.path.join(root, p) for p in zstd_sources + ext_sources] |
|
78 | if support_legacy: | |
|
79 | sources.extend([os.path.join(root, p) for p in zstd_sources_legacy]) | |
|
80 | ||
|
60 | 81 | include_dirs = [os.path.join(root, d) for d in zstd_includes] |
|
82 | if support_legacy: | |
|
83 | include_dirs.extend([os.path.join(root, d) for d in zstd_includes_legacy]) | |
|
84 | ||
|
85 | depends = [os.path.join(root, p) for p in zstd_depends] | |
|
61 | 86 | |
|
62 | 87 | # TODO compile with optimizations. |
|
63 | 88 | return Extension(name, sources, |
|
64 |
include_dirs=include_dirs |
|
|
89 | include_dirs=include_dirs, | |
|
90 | depends=depends, | |
|
91 | extra_compile_args=["-DZSTD_LEGACY_SUPPORT=1"] if support_legacy else []) |
@@ -1,465 +1,536 b'' | |||
|
1 | 1 | import hashlib |
|
2 | 2 | import io |
|
3 | 3 | import struct |
|
4 | 4 | import sys |
|
5 | 5 | |
|
6 | 6 | try: |
|
7 | 7 | import unittest2 as unittest |
|
8 | 8 | except ImportError: |
|
9 | 9 | import unittest |
|
10 | 10 | |
|
11 | 11 | import zstd |
|
12 | 12 | |
|
13 | 13 | from .common import OpCountingBytesIO |
|
14 | 14 | |
|
15 | 15 | |
|
16 | 16 | if sys.version_info[0] >= 3: |
|
17 | 17 | next = lambda it: it.__next__() |
|
18 | 18 | else: |
|
19 | 19 | next = lambda it: it.next() |
|
20 | 20 | |
|
21 | 21 | |
|
22 | 22 | class TestCompressor(unittest.TestCase): |
|
23 | 23 | def test_level_bounds(self): |
|
24 | 24 | with self.assertRaises(ValueError): |
|
25 | 25 | zstd.ZstdCompressor(level=0) |
|
26 | 26 | |
|
27 | 27 | with self.assertRaises(ValueError): |
|
28 | 28 | zstd.ZstdCompressor(level=23) |
|
29 | 29 | |
|
30 | 30 | |
|
31 | 31 | class TestCompressor_compress(unittest.TestCase): |
|
32 | 32 | def test_compress_empty(self): |
|
33 | 33 | cctx = zstd.ZstdCompressor(level=1) |
|
34 | 34 | cctx.compress(b'') |
|
35 | 35 | |
|
36 | 36 | cctx = zstd.ZstdCompressor(level=22) |
|
37 | 37 | cctx.compress(b'') |
|
38 | 38 | |
|
39 | 39 | def test_compress_empty(self): |
|
40 | 40 | cctx = zstd.ZstdCompressor(level=1) |
|
41 | 41 | self.assertEqual(cctx.compress(b''), |
|
42 | 42 | b'\x28\xb5\x2f\xfd\x00\x48\x01\x00\x00') |
|
43 | 43 | |
|
44 | # TODO should be temporary until https://github.com/facebook/zstd/issues/506 | |
|
45 | # is fixed. | |
|
46 | cctx = zstd.ZstdCompressor(write_content_size=True) | |
|
47 | with self.assertRaises(ValueError): | |
|
48 | cctx.compress(b'') | |
|
49 | ||
|
50 | cctx.compress(b'', allow_empty=True) | |
|
51 | ||
|
44 | 52 | def test_compress_large(self): |
|
45 | 53 | chunks = [] |
|
46 | 54 | for i in range(255): |
|
47 | 55 | chunks.append(struct.Struct('>B').pack(i) * 16384) |
|
48 | 56 | |
|
49 | 57 | cctx = zstd.ZstdCompressor(level=3) |
|
50 | 58 | result = cctx.compress(b''.join(chunks)) |
|
51 | 59 | self.assertEqual(len(result), 999) |
|
52 | 60 | self.assertEqual(result[0:4], b'\x28\xb5\x2f\xfd') |
|
53 | 61 | |
|
54 | 62 | def test_write_checksum(self): |
|
55 | 63 | cctx = zstd.ZstdCompressor(level=1) |
|
56 | 64 | no_checksum = cctx.compress(b'foobar') |
|
57 | 65 | cctx = zstd.ZstdCompressor(level=1, write_checksum=True) |
|
58 | 66 | with_checksum = cctx.compress(b'foobar') |
|
59 | 67 | |
|
60 | 68 | self.assertEqual(len(with_checksum), len(no_checksum) + 4) |
|
61 | 69 | |
|
62 | 70 | def test_write_content_size(self): |
|
63 | 71 | cctx = zstd.ZstdCompressor(level=1) |
|
64 | 72 | no_size = cctx.compress(b'foobar' * 256) |
|
65 | 73 | cctx = zstd.ZstdCompressor(level=1, write_content_size=True) |
|
66 | 74 | with_size = cctx.compress(b'foobar' * 256) |
|
67 | 75 | |
|
68 | 76 | self.assertEqual(len(with_size), len(no_size) + 1) |
|
69 | 77 | |
|
70 | 78 | def test_no_dict_id(self): |
|
71 | 79 | samples = [] |
|
72 | 80 | for i in range(128): |
|
73 | 81 | samples.append(b'foo' * 64) |
|
74 | 82 | samples.append(b'bar' * 64) |
|
75 | 83 | samples.append(b'foobar' * 64) |
|
76 | 84 | |
|
77 | 85 | d = zstd.train_dictionary(1024, samples) |
|
78 | 86 | |
|
79 | 87 | cctx = zstd.ZstdCompressor(level=1, dict_data=d) |
|
80 | 88 | with_dict_id = cctx.compress(b'foobarfoobar') |
|
81 | 89 | |
|
82 | 90 | cctx = zstd.ZstdCompressor(level=1, dict_data=d, write_dict_id=False) |
|
83 | 91 | no_dict_id = cctx.compress(b'foobarfoobar') |
|
84 | 92 | |
|
85 | 93 | self.assertEqual(len(with_dict_id), len(no_dict_id) + 4) |
|
86 | 94 | |
|
87 | 95 | def test_compress_dict_multiple(self): |
|
88 | 96 | samples = [] |
|
89 | 97 | for i in range(128): |
|
90 | 98 | samples.append(b'foo' * 64) |
|
91 | 99 | samples.append(b'bar' * 64) |
|
92 | 100 | samples.append(b'foobar' * 64) |
|
93 | 101 | |
|
94 | 102 | d = zstd.train_dictionary(8192, samples) |
|
95 | 103 | |
|
96 | 104 | cctx = zstd.ZstdCompressor(level=1, dict_data=d) |
|
97 | 105 | |
|
98 | 106 | for i in range(32): |
|
99 | 107 | cctx.compress(b'foo bar foobar foo bar foobar') |
|
100 | 108 | |
|
101 | 109 | |
|
102 | 110 | class TestCompressor_compressobj(unittest.TestCase): |
|
103 | 111 | def test_compressobj_empty(self): |
|
104 | 112 | cctx = zstd.ZstdCompressor(level=1) |
|
105 | 113 | cobj = cctx.compressobj() |
|
106 | 114 | self.assertEqual(cobj.compress(b''), b'') |
|
107 | 115 | self.assertEqual(cobj.flush(), |
|
108 | 116 | b'\x28\xb5\x2f\xfd\x00\x48\x01\x00\x00') |
|
109 | 117 | |
|
110 | 118 | def test_compressobj_large(self): |
|
111 | 119 | chunks = [] |
|
112 | 120 | for i in range(255): |
|
113 | 121 | chunks.append(struct.Struct('>B').pack(i) * 16384) |
|
114 | 122 | |
|
115 | 123 | cctx = zstd.ZstdCompressor(level=3) |
|
116 | 124 | cobj = cctx.compressobj() |
|
117 | 125 | |
|
118 | 126 | result = cobj.compress(b''.join(chunks)) + cobj.flush() |
|
119 | 127 | self.assertEqual(len(result), 999) |
|
120 | 128 | self.assertEqual(result[0:4], b'\x28\xb5\x2f\xfd') |
|
121 | 129 | |
|
122 | 130 | def test_write_checksum(self): |
|
123 | 131 | cctx = zstd.ZstdCompressor(level=1) |
|
124 | 132 | cobj = cctx.compressobj() |
|
125 | 133 | no_checksum = cobj.compress(b'foobar') + cobj.flush() |
|
126 | 134 | cctx = zstd.ZstdCompressor(level=1, write_checksum=True) |
|
127 | 135 | cobj = cctx.compressobj() |
|
128 | 136 | with_checksum = cobj.compress(b'foobar') + cobj.flush() |
|
129 | 137 | |
|
130 | 138 | self.assertEqual(len(with_checksum), len(no_checksum) + 4) |
|
131 | 139 | |
|
132 | 140 | def test_write_content_size(self): |
|
133 | 141 | cctx = zstd.ZstdCompressor(level=1) |
|
134 | 142 | cobj = cctx.compressobj(size=len(b'foobar' * 256)) |
|
135 | 143 | no_size = cobj.compress(b'foobar' * 256) + cobj.flush() |
|
136 | 144 | cctx = zstd.ZstdCompressor(level=1, write_content_size=True) |
|
137 | 145 | cobj = cctx.compressobj(size=len(b'foobar' * 256)) |
|
138 | 146 | with_size = cobj.compress(b'foobar' * 256) + cobj.flush() |
|
139 | 147 | |
|
140 | 148 | self.assertEqual(len(with_size), len(no_size) + 1) |
|
141 | 149 | |
|
142 |
def test_compress_after_f |
|
|
150 | def test_compress_after_finished(self): | |
|
143 | 151 | cctx = zstd.ZstdCompressor() |
|
144 | 152 | cobj = cctx.compressobj() |
|
145 | 153 | |
|
146 | 154 | cobj.compress(b'foo') |
|
147 | 155 | cobj.flush() |
|
148 | 156 | |
|
149 |
with self.assertRaisesRegexp(zstd.ZstdError, 'cannot call compress\(\) after |
|
|
157 | with self.assertRaisesRegexp(zstd.ZstdError, 'cannot call compress\(\) after compressor'): | |
|
150 | 158 | cobj.compress(b'foo') |
|
151 | 159 | |
|
152 |
with self.assertRaisesRegexp(zstd.ZstdError, ' |
|
|
160 | with self.assertRaisesRegexp(zstd.ZstdError, 'compressor object already finished'): | |
|
153 | 161 | cobj.flush() |
|
154 | 162 | |
|
163 | def test_flush_block_repeated(self): | |
|
164 | cctx = zstd.ZstdCompressor(level=1) | |
|
165 | cobj = cctx.compressobj() | |
|
166 | ||
|
167 | self.assertEqual(cobj.compress(b'foo'), b'') | |
|
168 | self.assertEqual(cobj.flush(zstd.COMPRESSOBJ_FLUSH_BLOCK), | |
|
169 | b'\x28\xb5\x2f\xfd\x00\x48\x18\x00\x00foo') | |
|
170 | self.assertEqual(cobj.compress(b'bar'), b'') | |
|
171 | # 3 byte header plus content. | |
|
172 | self.assertEqual(cobj.flush(), b'\x19\x00\x00bar') | |
|
173 | ||
|
174 | def test_flush_empty_block(self): | |
|
175 | cctx = zstd.ZstdCompressor(write_checksum=True) | |
|
176 | cobj = cctx.compressobj() | |
|
177 | ||
|
178 | cobj.compress(b'foobar') | |
|
179 | cobj.flush(zstd.COMPRESSOBJ_FLUSH_BLOCK) | |
|
180 | # No-op if no block is active (this is internal to zstd). | |
|
181 | self.assertEqual(cobj.flush(zstd.COMPRESSOBJ_FLUSH_BLOCK), b'') | |
|
182 | ||
|
183 | trailing = cobj.flush() | |
|
184 | # 3 bytes block header + 4 bytes frame checksum | |
|
185 | self.assertEqual(len(trailing), 7) | |
|
186 | header = trailing[0:3] | |
|
187 | self.assertEqual(header, b'\x01\x00\x00') | |
|
188 | ||
|
155 | 189 | |
|
156 | 190 | class TestCompressor_copy_stream(unittest.TestCase): |
|
157 | 191 | def test_no_read(self): |
|
158 | 192 | source = object() |
|
159 | 193 | dest = io.BytesIO() |
|
160 | 194 | |
|
161 | 195 | cctx = zstd.ZstdCompressor() |
|
162 | 196 | with self.assertRaises(ValueError): |
|
163 | 197 | cctx.copy_stream(source, dest) |
|
164 | 198 | |
|
165 | 199 | def test_no_write(self): |
|
166 | 200 | source = io.BytesIO() |
|
167 | 201 | dest = object() |
|
168 | 202 | |
|
169 | 203 | cctx = zstd.ZstdCompressor() |
|
170 | 204 | with self.assertRaises(ValueError): |
|
171 | 205 | cctx.copy_stream(source, dest) |
|
172 | 206 | |
|
173 | 207 | def test_empty(self): |
|
174 | 208 | source = io.BytesIO() |
|
175 | 209 | dest = io.BytesIO() |
|
176 | 210 | |
|
177 | 211 | cctx = zstd.ZstdCompressor(level=1) |
|
178 | 212 | r, w = cctx.copy_stream(source, dest) |
|
179 | 213 | self.assertEqual(int(r), 0) |
|
180 | 214 | self.assertEqual(w, 9) |
|
181 | 215 | |
|
182 | 216 | self.assertEqual(dest.getvalue(), |
|
183 | 217 | b'\x28\xb5\x2f\xfd\x00\x48\x01\x00\x00') |
|
184 | 218 | |
|
185 | 219 | def test_large_data(self): |
|
186 | 220 | source = io.BytesIO() |
|
187 | 221 | for i in range(255): |
|
188 | 222 | source.write(struct.Struct('>B').pack(i) * 16384) |
|
189 | 223 | source.seek(0) |
|
190 | 224 | |
|
191 | 225 | dest = io.BytesIO() |
|
192 | 226 | cctx = zstd.ZstdCompressor() |
|
193 | 227 | r, w = cctx.copy_stream(source, dest) |
|
194 | 228 | |
|
195 | 229 | self.assertEqual(r, 255 * 16384) |
|
196 | 230 | self.assertEqual(w, 999) |
|
197 | 231 | |
|
198 | 232 | def test_write_checksum(self): |
|
199 | 233 | source = io.BytesIO(b'foobar') |
|
200 | 234 | no_checksum = io.BytesIO() |
|
201 | 235 | |
|
202 | 236 | cctx = zstd.ZstdCompressor(level=1) |
|
203 | 237 | cctx.copy_stream(source, no_checksum) |
|
204 | 238 | |
|
205 | 239 | source.seek(0) |
|
206 | 240 | with_checksum = io.BytesIO() |
|
207 | 241 | cctx = zstd.ZstdCompressor(level=1, write_checksum=True) |
|
208 | 242 | cctx.copy_stream(source, with_checksum) |
|
209 | 243 | |
|
210 | 244 | self.assertEqual(len(with_checksum.getvalue()), |
|
211 | 245 | len(no_checksum.getvalue()) + 4) |
|
212 | 246 | |
|
213 | 247 | def test_write_content_size(self): |
|
214 | 248 | source = io.BytesIO(b'foobar' * 256) |
|
215 | 249 | no_size = io.BytesIO() |
|
216 | 250 | |
|
217 | 251 | cctx = zstd.ZstdCompressor(level=1) |
|
218 | 252 | cctx.copy_stream(source, no_size) |
|
219 | 253 | |
|
220 | 254 | source.seek(0) |
|
221 | 255 | with_size = io.BytesIO() |
|
222 | 256 | cctx = zstd.ZstdCompressor(level=1, write_content_size=True) |
|
223 | 257 | cctx.copy_stream(source, with_size) |
|
224 | 258 | |
|
225 | 259 | # Source content size is unknown, so no content size written. |
|
226 | 260 | self.assertEqual(len(with_size.getvalue()), |
|
227 | 261 | len(no_size.getvalue())) |
|
228 | 262 | |
|
229 | 263 | source.seek(0) |
|
230 | 264 | with_size = io.BytesIO() |
|
231 | 265 | cctx.copy_stream(source, with_size, size=len(source.getvalue())) |
|
232 | 266 | |
|
233 | 267 | # We specified source size, so content size header is present. |
|
234 | 268 | self.assertEqual(len(with_size.getvalue()), |
|
235 | 269 | len(no_size.getvalue()) + 1) |
|
236 | 270 | |
|
237 | 271 | def test_read_write_size(self): |
|
238 | 272 | source = OpCountingBytesIO(b'foobarfoobar') |
|
239 | 273 | dest = OpCountingBytesIO() |
|
240 | 274 | cctx = zstd.ZstdCompressor() |
|
241 | 275 | r, w = cctx.copy_stream(source, dest, read_size=1, write_size=1) |
|
242 | 276 | |
|
243 | 277 | self.assertEqual(r, len(source.getvalue())) |
|
244 | 278 | self.assertEqual(w, 21) |
|
245 | 279 | self.assertEqual(source._read_count, len(source.getvalue()) + 1) |
|
246 | 280 | self.assertEqual(dest._write_count, len(dest.getvalue())) |
|
247 | 281 | |
|
248 | 282 | |
|
249 | 283 | def compress(data, level): |
|
250 | 284 | buffer = io.BytesIO() |
|
251 | 285 | cctx = zstd.ZstdCompressor(level=level) |
|
252 | 286 | with cctx.write_to(buffer) as compressor: |
|
253 | 287 | compressor.write(data) |
|
254 | 288 | return buffer.getvalue() |
|
255 | 289 | |
|
256 | 290 | |
|
257 | 291 | class TestCompressor_write_to(unittest.TestCase): |
|
258 | 292 | def test_empty(self): |
|
259 | 293 | self.assertEqual(compress(b'', 1), |
|
260 | 294 | b'\x28\xb5\x2f\xfd\x00\x48\x01\x00\x00') |
|
261 | 295 | |
|
262 | 296 | def test_multiple_compress(self): |
|
263 | 297 | buffer = io.BytesIO() |
|
264 | 298 | cctx = zstd.ZstdCompressor(level=5) |
|
265 | 299 | with cctx.write_to(buffer) as compressor: |
|
266 | 300 | compressor.write(b'foo') |
|
267 | 301 | compressor.write(b'bar') |
|
268 | 302 | compressor.write(b'x' * 8192) |
|
269 | 303 | |
|
270 | 304 | result = buffer.getvalue() |
|
271 | 305 | self.assertEqual(result, |
|
272 | 306 | b'\x28\xb5\x2f\xfd\x00\x50\x75\x00\x00\x38\x66\x6f' |
|
273 | 307 | b'\x6f\x62\x61\x72\x78\x01\x00\xfc\xdf\x03\x23') |
|
274 | 308 | |
|
275 | 309 | def test_dictionary(self): |
|
276 | 310 | samples = [] |
|
277 | 311 | for i in range(128): |
|
278 | 312 | samples.append(b'foo' * 64) |
|
279 | 313 | samples.append(b'bar' * 64) |
|
280 | 314 | samples.append(b'foobar' * 64) |
|
281 | 315 | |
|
282 | 316 | d = zstd.train_dictionary(8192, samples) |
|
283 | 317 | |
|
284 | 318 | buffer = io.BytesIO() |
|
285 | 319 | cctx = zstd.ZstdCompressor(level=9, dict_data=d) |
|
286 | 320 | with cctx.write_to(buffer) as compressor: |
|
287 | 321 | compressor.write(b'foo') |
|
288 | 322 | compressor.write(b'bar') |
|
289 | 323 | compressor.write(b'foo' * 16384) |
|
290 | 324 | |
|
291 | 325 | compressed = buffer.getvalue() |
|
292 | 326 | h = hashlib.sha1(compressed).hexdigest() |
|
293 | 327 | self.assertEqual(h, '1c5bcd25181bcd8c1a73ea8773323e0056129f92') |
|
294 | 328 | |
|
295 | 329 | def test_compression_params(self): |
|
296 | 330 | params = zstd.CompressionParameters(20, 6, 12, 5, 4, 10, zstd.STRATEGY_FAST) |
|
297 | 331 | |
|
298 | 332 | buffer = io.BytesIO() |
|
299 | 333 | cctx = zstd.ZstdCompressor(compression_params=params) |
|
300 | 334 | with cctx.write_to(buffer) as compressor: |
|
301 | 335 | compressor.write(b'foo') |
|
302 | 336 | compressor.write(b'bar') |
|
303 | 337 | compressor.write(b'foobar' * 16384) |
|
304 | 338 | |
|
305 | 339 | compressed = buffer.getvalue() |
|
306 | 340 | h = hashlib.sha1(compressed).hexdigest() |
|
307 | 341 | self.assertEqual(h, '1ae31f270ed7de14235221a604b31ecd517ebd99') |
|
308 | 342 | |
|
309 | 343 | def test_write_checksum(self): |
|
310 | 344 | no_checksum = io.BytesIO() |
|
311 | 345 | cctx = zstd.ZstdCompressor(level=1) |
|
312 | 346 | with cctx.write_to(no_checksum) as compressor: |
|
313 | 347 | compressor.write(b'foobar') |
|
314 | 348 | |
|
315 | 349 | with_checksum = io.BytesIO() |
|
316 | 350 | cctx = zstd.ZstdCompressor(level=1, write_checksum=True) |
|
317 | 351 | with cctx.write_to(with_checksum) as compressor: |
|
318 | 352 | compressor.write(b'foobar') |
|
319 | 353 | |
|
320 | 354 | self.assertEqual(len(with_checksum.getvalue()), |
|
321 | 355 | len(no_checksum.getvalue()) + 4) |
|
322 | 356 | |
|
323 | 357 | def test_write_content_size(self): |
|
324 | 358 | no_size = io.BytesIO() |
|
325 | 359 | cctx = zstd.ZstdCompressor(level=1) |
|
326 | 360 | with cctx.write_to(no_size) as compressor: |
|
327 | 361 | compressor.write(b'foobar' * 256) |
|
328 | 362 | |
|
329 | 363 | with_size = io.BytesIO() |
|
330 | 364 | cctx = zstd.ZstdCompressor(level=1, write_content_size=True) |
|
331 | 365 | with cctx.write_to(with_size) as compressor: |
|
332 | 366 | compressor.write(b'foobar' * 256) |
|
333 | 367 | |
|
334 | 368 | # Source size is not known in streaming mode, so header not |
|
335 | 369 | # written. |
|
336 | 370 | self.assertEqual(len(with_size.getvalue()), |
|
337 | 371 | len(no_size.getvalue())) |
|
338 | 372 | |
|
339 | 373 | # Declaring size will write the header. |
|
340 | 374 | with_size = io.BytesIO() |
|
341 | 375 | with cctx.write_to(with_size, size=len(b'foobar' * 256)) as compressor: |
|
342 | 376 | compressor.write(b'foobar' * 256) |
|
343 | 377 | |
|
344 | 378 | self.assertEqual(len(with_size.getvalue()), |
|
345 | 379 | len(no_size.getvalue()) + 1) |
|
346 | 380 | |
|
347 | 381 | def test_no_dict_id(self): |
|
348 | 382 | samples = [] |
|
349 | 383 | for i in range(128): |
|
350 | 384 | samples.append(b'foo' * 64) |
|
351 | 385 | samples.append(b'bar' * 64) |
|
352 | 386 | samples.append(b'foobar' * 64) |
|
353 | 387 | |
|
354 | 388 | d = zstd.train_dictionary(1024, samples) |
|
355 | 389 | |
|
356 | 390 | with_dict_id = io.BytesIO() |
|
357 | 391 | cctx = zstd.ZstdCompressor(level=1, dict_data=d) |
|
358 | 392 | with cctx.write_to(with_dict_id) as compressor: |
|
359 | 393 | compressor.write(b'foobarfoobar') |
|
360 | 394 | |
|
361 | 395 | cctx = zstd.ZstdCompressor(level=1, dict_data=d, write_dict_id=False) |
|
362 | 396 | no_dict_id = io.BytesIO() |
|
363 | 397 | with cctx.write_to(no_dict_id) as compressor: |
|
364 | 398 | compressor.write(b'foobarfoobar') |
|
365 | 399 | |
|
366 | 400 | self.assertEqual(len(with_dict_id.getvalue()), |
|
367 | 401 | len(no_dict_id.getvalue()) + 4) |
|
368 | 402 | |
|
369 | 403 | def test_memory_size(self): |
|
370 | 404 | cctx = zstd.ZstdCompressor(level=3) |
|
371 | 405 | buffer = io.BytesIO() |
|
372 | 406 | with cctx.write_to(buffer) as compressor: |
|
373 | 407 | size = compressor.memory_size() |
|
374 | 408 | |
|
375 | 409 | self.assertGreater(size, 100000) |
|
376 | 410 | |
|
377 | 411 | def test_write_size(self): |
|
378 | 412 | cctx = zstd.ZstdCompressor(level=3) |
|
379 | 413 | dest = OpCountingBytesIO() |
|
380 | 414 | with cctx.write_to(dest, write_size=1) as compressor: |
|
381 | 415 | compressor.write(b'foo') |
|
382 | 416 | compressor.write(b'bar') |
|
383 | 417 | compressor.write(b'foobar') |
|
384 | 418 | |
|
385 | 419 | self.assertEqual(len(dest.getvalue()), dest._write_count) |
|
386 | 420 | |
|
421 | def test_flush_repeated(self): | |
|
422 | cctx = zstd.ZstdCompressor(level=3) | |
|
423 | dest = OpCountingBytesIO() | |
|
424 | with cctx.write_to(dest) as compressor: | |
|
425 | compressor.write(b'foo') | |
|
426 | self.assertEqual(dest._write_count, 0) | |
|
427 | compressor.flush() | |
|
428 | self.assertEqual(dest._write_count, 1) | |
|
429 | compressor.write(b'bar') | |
|
430 | self.assertEqual(dest._write_count, 1) | |
|
431 | compressor.flush() | |
|
432 | self.assertEqual(dest._write_count, 2) | |
|
433 | compressor.write(b'baz') | |
|
434 | ||
|
435 | self.assertEqual(dest._write_count, 3) | |
|
436 | ||
|
437 | def test_flush_empty_block(self): | |
|
438 | cctx = zstd.ZstdCompressor(level=3, write_checksum=True) | |
|
439 | dest = OpCountingBytesIO() | |
|
440 | with cctx.write_to(dest) as compressor: | |
|
441 | compressor.write(b'foobar' * 8192) | |
|
442 | count = dest._write_count | |
|
443 | offset = dest.tell() | |
|
444 | compressor.flush() | |
|
445 | self.assertGreater(dest._write_count, count) | |
|
446 | self.assertGreater(dest.tell(), offset) | |
|
447 | offset = dest.tell() | |
|
448 | # Ending the write here should cause an empty block to be written | |
|
449 | # to denote end of frame. | |
|
450 | ||
|
451 | trailing = dest.getvalue()[offset:] | |
|
452 | # 3 bytes block header + 4 bytes frame checksum | |
|
453 | self.assertEqual(len(trailing), 7) | |
|
454 | ||
|
455 | header = trailing[0:3] | |
|
456 | self.assertEqual(header, b'\x01\x00\x00') | |
|
457 | ||
|
387 | 458 | |
|
388 | 459 | class TestCompressor_read_from(unittest.TestCase): |
|
389 | 460 | def test_type_validation(self): |
|
390 | 461 | cctx = zstd.ZstdCompressor() |
|
391 | 462 | |
|
392 | 463 | # Object with read() works. |
|
393 | 464 | cctx.read_from(io.BytesIO()) |
|
394 | 465 | |
|
395 | 466 | # Buffer protocol works. |
|
396 | 467 | cctx.read_from(b'foobar') |
|
397 | 468 | |
|
398 | 469 | with self.assertRaisesRegexp(ValueError, 'must pass an object with a read'): |
|
399 | 470 | cctx.read_from(True) |
|
400 | 471 | |
|
401 | 472 | def test_read_empty(self): |
|
402 | 473 | cctx = zstd.ZstdCompressor(level=1) |
|
403 | 474 | |
|
404 | 475 | source = io.BytesIO() |
|
405 | 476 | it = cctx.read_from(source) |
|
406 | 477 | chunks = list(it) |
|
407 | 478 | self.assertEqual(len(chunks), 1) |
|
408 | 479 | compressed = b''.join(chunks) |
|
409 | 480 | self.assertEqual(compressed, b'\x28\xb5\x2f\xfd\x00\x48\x01\x00\x00') |
|
410 | 481 | |
|
411 | 482 | # And again with the buffer protocol. |
|
412 | 483 | it = cctx.read_from(b'') |
|
413 | 484 | chunks = list(it) |
|
414 | 485 | self.assertEqual(len(chunks), 1) |
|
415 | 486 | compressed2 = b''.join(chunks) |
|
416 | 487 | self.assertEqual(compressed2, compressed) |
|
417 | 488 | |
|
418 | 489 | def test_read_large(self): |
|
419 | 490 | cctx = zstd.ZstdCompressor(level=1) |
|
420 | 491 | |
|
421 | 492 | source = io.BytesIO() |
|
422 | 493 | source.write(b'f' * zstd.COMPRESSION_RECOMMENDED_INPUT_SIZE) |
|
423 | 494 | source.write(b'o') |
|
424 | 495 | source.seek(0) |
|
425 | 496 | |
|
426 | 497 | # Creating an iterator should not perform any compression until |
|
427 | 498 | # first read. |
|
428 | 499 | it = cctx.read_from(source, size=len(source.getvalue())) |
|
429 | 500 | self.assertEqual(source.tell(), 0) |
|
430 | 501 | |
|
431 | 502 | # We should have exactly 2 output chunks. |
|
432 | 503 | chunks = [] |
|
433 | 504 | chunk = next(it) |
|
434 | 505 | self.assertIsNotNone(chunk) |
|
435 | 506 | self.assertEqual(source.tell(), zstd.COMPRESSION_RECOMMENDED_INPUT_SIZE) |
|
436 | 507 | chunks.append(chunk) |
|
437 | 508 | chunk = next(it) |
|
438 | 509 | self.assertIsNotNone(chunk) |
|
439 | 510 | chunks.append(chunk) |
|
440 | 511 | |
|
441 | 512 | self.assertEqual(source.tell(), len(source.getvalue())) |
|
442 | 513 | |
|
443 | 514 | with self.assertRaises(StopIteration): |
|
444 | 515 | next(it) |
|
445 | 516 | |
|
446 | 517 | # And again for good measure. |
|
447 | 518 | with self.assertRaises(StopIteration): |
|
448 | 519 | next(it) |
|
449 | 520 | |
|
450 | 521 | # We should get the same output as the one-shot compression mechanism. |
|
451 | 522 | self.assertEqual(b''.join(chunks), cctx.compress(source.getvalue())) |
|
452 | 523 | |
|
453 | 524 | # Now check the buffer protocol. |
|
454 | 525 | it = cctx.read_from(source.getvalue()) |
|
455 | 526 | chunks = list(it) |
|
456 | 527 | self.assertEqual(len(chunks), 2) |
|
457 | 528 | self.assertEqual(b''.join(chunks), cctx.compress(source.getvalue())) |
|
458 | 529 | |
|
459 | 530 | def test_read_write_size(self): |
|
460 | 531 | source = OpCountingBytesIO(b'foobarfoobar') |
|
461 | 532 | cctx = zstd.ZstdCompressor(level=3) |
|
462 | 533 | for chunk in cctx.read_from(source, read_size=1, write_size=1): |
|
463 | 534 | self.assertEqual(len(chunk), 1) |
|
464 | 535 | |
|
465 | 536 | self.assertEqual(source._read_count, len(source.getvalue()) + 1) |
@@ -1,48 +1,48 b'' | |||
|
1 | 1 | from __future__ import unicode_literals |
|
2 | 2 | |
|
3 | 3 | try: |
|
4 | 4 | import unittest2 as unittest |
|
5 | 5 | except ImportError: |
|
6 | 6 | import unittest |
|
7 | 7 | |
|
8 | 8 | import zstd |
|
9 | 9 | |
|
10 | 10 | class TestModuleAttributes(unittest.TestCase): |
|
11 | 11 | def test_version(self): |
|
12 |
self.assertEqual(zstd.ZSTD_VERSION, (1, 1, |
|
|
12 | self.assertEqual(zstd.ZSTD_VERSION, (1, 1, 2)) | |
|
13 | 13 | |
|
14 | 14 | def test_constants(self): |
|
15 | 15 | self.assertEqual(zstd.MAX_COMPRESSION_LEVEL, 22) |
|
16 | 16 | self.assertEqual(zstd.FRAME_HEADER, b'\x28\xb5\x2f\xfd') |
|
17 | 17 | |
|
18 | 18 | def test_hasattr(self): |
|
19 | 19 | attrs = ( |
|
20 | 20 | 'COMPRESSION_RECOMMENDED_INPUT_SIZE', |
|
21 | 21 | 'COMPRESSION_RECOMMENDED_OUTPUT_SIZE', |
|
22 | 22 | 'DECOMPRESSION_RECOMMENDED_INPUT_SIZE', |
|
23 | 23 | 'DECOMPRESSION_RECOMMENDED_OUTPUT_SIZE', |
|
24 | 24 | 'MAGIC_NUMBER', |
|
25 | 25 | 'WINDOWLOG_MIN', |
|
26 | 26 | 'WINDOWLOG_MAX', |
|
27 | 27 | 'CHAINLOG_MIN', |
|
28 | 28 | 'CHAINLOG_MAX', |
|
29 | 29 | 'HASHLOG_MIN', |
|
30 | 30 | 'HASHLOG_MAX', |
|
31 | 31 | 'HASHLOG3_MAX', |
|
32 | 32 | 'SEARCHLOG_MIN', |
|
33 | 33 | 'SEARCHLOG_MAX', |
|
34 | 34 | 'SEARCHLENGTH_MIN', |
|
35 | 35 | 'SEARCHLENGTH_MAX', |
|
36 | 36 | 'TARGETLENGTH_MIN', |
|
37 | 37 | 'TARGETLENGTH_MAX', |
|
38 | 38 | 'STRATEGY_FAST', |
|
39 | 39 | 'STRATEGY_DFAST', |
|
40 | 40 | 'STRATEGY_GREEDY', |
|
41 | 41 | 'STRATEGY_LAZY', |
|
42 | 42 | 'STRATEGY_LAZY2', |
|
43 | 43 | 'STRATEGY_BTLAZY2', |
|
44 | 44 | 'STRATEGY_BTOPT', |
|
45 | 45 | ) |
|
46 | 46 | |
|
47 | 47 | for a in attrs: |
|
48 | 48 | self.assertTrue(hasattr(zstd, a)) |
@@ -1,112 +1,136 b'' | |||
|
1 | 1 | /** |
|
2 | 2 | * Copyright (c) 2016-present, Gregory Szorc |
|
3 | 3 | * All rights reserved. |
|
4 | 4 | * |
|
5 | 5 | * This software may be modified and distributed under the terms |
|
6 | 6 | * of the BSD license. See the LICENSE file for details. |
|
7 | 7 | */ |
|
8 | 8 | |
|
9 | 9 | /* A Python C extension for Zstandard. */ |
|
10 | 10 | |
|
11 | 11 | #include "python-zstandard.h" |
|
12 | 12 | |
|
13 | 13 | PyObject *ZstdError; |
|
14 | 14 | |
|
15 | 15 | PyDoc_STRVAR(estimate_compression_context_size__doc__, |
|
16 | 16 | "estimate_compression_context_size(compression_parameters)\n" |
|
17 | 17 | "\n" |
|
18 | 18 | "Give the amount of memory allocated for a compression context given a\n" |
|
19 | 19 | "CompressionParameters instance"); |
|
20 | 20 | |
|
21 | 21 | PyDoc_STRVAR(estimate_decompression_context_size__doc__, |
|
22 | 22 | "estimate_decompression_context_size()\n" |
|
23 | 23 | "\n" |
|
24 | 24 | "Estimate the amount of memory allocated to a decompression context.\n" |
|
25 | 25 | ); |
|
26 | 26 | |
|
27 | 27 | static PyObject* estimate_decompression_context_size(PyObject* self) { |
|
28 | 28 | return PyLong_FromSize_t(ZSTD_estimateDCtxSize()); |
|
29 | 29 | } |
|
30 | 30 | |
|
31 | 31 | PyDoc_STRVAR(get_compression_parameters__doc__, |
|
32 | 32 | "get_compression_parameters(compression_level[, source_size[, dict_size]])\n" |
|
33 | 33 | "\n" |
|
34 | 34 | "Obtains a ``CompressionParameters`` instance from a compression level and\n" |
|
35 | 35 | "optional input size and dictionary size"); |
|
36 | 36 | |
|
37 | 37 | PyDoc_STRVAR(train_dictionary__doc__, |
|
38 | 38 | "train_dictionary(dict_size, samples)\n" |
|
39 | 39 | "\n" |
|
40 | 40 | "Train a dictionary from sample data.\n" |
|
41 | 41 | "\n" |
|
42 | 42 | "A compression dictionary of size ``dict_size`` will be created from the\n" |
|
43 | 43 | "iterable of samples provided by ``samples``.\n" |
|
44 | 44 | "\n" |
|
45 | 45 | "The raw dictionary content will be returned\n"); |
|
46 | 46 | |
|
47 | 47 | static char zstd_doc[] = "Interface to zstandard"; |
|
48 | 48 | |
|
49 | 49 | static PyMethodDef zstd_methods[] = { |
|
50 | 50 | { "estimate_compression_context_size", (PyCFunction)estimate_compression_context_size, |
|
51 | 51 | METH_VARARGS, estimate_compression_context_size__doc__ }, |
|
52 | 52 | { "estimate_decompression_context_size", (PyCFunction)estimate_decompression_context_size, |
|
53 | 53 | METH_NOARGS, estimate_decompression_context_size__doc__ }, |
|
54 | 54 | { "get_compression_parameters", (PyCFunction)get_compression_parameters, |
|
55 | 55 | METH_VARARGS, get_compression_parameters__doc__ }, |
|
56 | 56 | { "train_dictionary", (PyCFunction)train_dictionary, |
|
57 | 57 | METH_VARARGS | METH_KEYWORDS, train_dictionary__doc__ }, |
|
58 | 58 | { NULL, NULL } |
|
59 | 59 | }; |
|
60 | 60 | |
|
61 | 61 | void compressobj_module_init(PyObject* mod); |
|
62 | 62 | void compressor_module_init(PyObject* mod); |
|
63 | 63 | void compressionparams_module_init(PyObject* mod); |
|
64 | 64 | void constants_module_init(PyObject* mod); |
|
65 | 65 | void dictparams_module_init(PyObject* mod); |
|
66 | 66 | void compressiondict_module_init(PyObject* mod); |
|
67 | 67 | void compressionwriter_module_init(PyObject* mod); |
|
68 | 68 | void compressoriterator_module_init(PyObject* mod); |
|
69 | 69 | void decompressor_module_init(PyObject* mod); |
|
70 | 70 | void decompressobj_module_init(PyObject* mod); |
|
71 | 71 | void decompressionwriter_module_init(PyObject* mod); |
|
72 | 72 | void decompressoriterator_module_init(PyObject* mod); |
|
73 | 73 | |
|
74 | 74 | void zstd_module_init(PyObject* m) { |
|
75 | /* python-zstandard relies on unstable zstd C API features. This means | |
|
76 | that changes in zstd may break expectations in python-zstandard. | |
|
77 | ||
|
78 | python-zstandard is distributed with a copy of the zstd sources. | |
|
79 | python-zstandard is only guaranteed to work with the bundled version | |
|
80 | of zstd. | |
|
81 | ||
|
82 | However, downstream redistributors or packagers may unbundle zstd | |
|
83 | from python-zstandard. This can result in a mismatch between zstd | |
|
84 | versions and API semantics. This essentially "voids the warranty" | |
|
85 | of python-zstandard and may cause undefined behavior. | |
|
86 | ||
|
87 | We detect this mismatch here and refuse to load the module if this | |
|
88 | scenario is detected. | |
|
89 | */ | |
|
90 | if (ZSTD_VERSION_NUMBER != 10102 || ZSTD_versionNumber() != 10102) { | |
|
91 | PyErr_SetString(PyExc_ImportError, "zstd C API mismatch; Python bindings not compiled against expected zstd version"); | |
|
92 | return; | |
|
93 | } | |
|
94 | ||
|
75 | 95 | compressionparams_module_init(m); |
|
76 | 96 | dictparams_module_init(m); |
|
77 | 97 | compressiondict_module_init(m); |
|
78 | 98 | compressobj_module_init(m); |
|
79 | 99 | compressor_module_init(m); |
|
80 | 100 | compressionwriter_module_init(m); |
|
81 | 101 | compressoriterator_module_init(m); |
|
82 | 102 | constants_module_init(m); |
|
83 | 103 | decompressor_module_init(m); |
|
84 | 104 | decompressobj_module_init(m); |
|
85 | 105 | decompressionwriter_module_init(m); |
|
86 | 106 | decompressoriterator_module_init(m); |
|
87 | 107 | } |
|
88 | 108 | |
|
89 | 109 | #if PY_MAJOR_VERSION >= 3 |
|
90 | 110 | static struct PyModuleDef zstd_module = { |
|
91 | 111 | PyModuleDef_HEAD_INIT, |
|
92 | 112 | "zstd", |
|
93 | 113 | zstd_doc, |
|
94 | 114 | -1, |
|
95 | 115 | zstd_methods |
|
96 | 116 | }; |
|
97 | 117 | |
|
98 | 118 | PyMODINIT_FUNC PyInit_zstd(void) { |
|
99 | 119 | PyObject *m = PyModule_Create(&zstd_module); |
|
100 | 120 | if (m) { |
|
101 | 121 | zstd_module_init(m); |
|
122 | if (PyErr_Occurred()) { | |
|
123 | Py_DECREF(m); | |
|
124 | m = NULL; | |
|
125 | } | |
|
102 | 126 | } |
|
103 | 127 | return m; |
|
104 | 128 | } |
|
105 | 129 | #else |
|
106 | 130 | PyMODINIT_FUNC initzstd(void) { |
|
107 | 131 | PyObject *m = Py_InitModule3("zstd", zstd_methods, zstd_doc); |
|
108 | 132 | if (m) { |
|
109 | 133 | zstd_module_init(m); |
|
110 | 134 | } |
|
111 | 135 | } |
|
112 | 136 | #endif |
@@ -1,414 +1,414 b'' | |||
|
1 | 1 | /* ****************************************************************** |
|
2 | 2 | bitstream |
|
3 | 3 | Part of FSE library |
|
4 | 4 | header file (to include) |
|
5 | 5 | Copyright (C) 2013-2016, Yann Collet. |
|
6 | 6 | |
|
7 | 7 | BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) |
|
8 | 8 | |
|
9 | 9 | Redistribution and use in source and binary forms, with or without |
|
10 | 10 | modification, are permitted provided that the following conditions are |
|
11 | 11 | met: |
|
12 | 12 | |
|
13 | 13 | * Redistributions of source code must retain the above copyright |
|
14 | 14 | notice, this list of conditions and the following disclaimer. |
|
15 | 15 | * Redistributions in binary form must reproduce the above |
|
16 | 16 | copyright notice, this list of conditions and the following disclaimer |
|
17 | 17 | in the documentation and/or other materials provided with the |
|
18 | 18 | distribution. |
|
19 | 19 | |
|
20 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
21 | 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
22 | 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
23 | 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
24 | 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
25 | 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
26 | 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
27 | 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
28 | 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
29 | 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
30 | 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
31 | 31 | |
|
32 | 32 | You can contact the author at : |
|
33 | 33 | - Source repository : https://github.com/Cyan4973/FiniteStateEntropy |
|
34 | 34 | ****************************************************************** */ |
|
35 | 35 | #ifndef BITSTREAM_H_MODULE |
|
36 | 36 | #define BITSTREAM_H_MODULE |
|
37 | 37 | |
|
38 | 38 | #if defined (__cplusplus) |
|
39 | 39 | extern "C" { |
|
40 | 40 | #endif |
|
41 | 41 | |
|
42 | 42 | |
|
43 | 43 | /* |
|
44 | 44 | * This API consists of small unitary functions, which must be inlined for best performance. |
|
45 | 45 | * Since link-time-optimization is not available for all compilers, |
|
46 | 46 | * these functions are defined into a .h to be included. |
|
47 | 47 | */ |
|
48 | 48 | |
|
49 | 49 | /*-**************************************** |
|
50 | 50 | * Dependencies |
|
51 | 51 | ******************************************/ |
|
52 | 52 | #include "mem.h" /* unaligned access routines */ |
|
53 | 53 | #include "error_private.h" /* error codes and messages */ |
|
54 | 54 | |
|
55 | 55 | |
|
56 | 56 | /*========================================= |
|
57 | 57 | * Target specific |
|
58 | 58 | =========================================*/ |
|
59 | 59 | #if defined(__BMI__) && defined(__GNUC__) |
|
60 | 60 | # include <immintrin.h> /* support for bextr (experimental) */ |
|
61 | 61 | #endif |
|
62 | 62 | |
|
63 | 63 | |
|
64 | 64 | /*-****************************************** |
|
65 | 65 | * bitStream encoding API (write forward) |
|
66 | 66 | ********************************************/ |
|
67 | 67 | /* bitStream can mix input from multiple sources. |
|
68 | 68 | * A critical property of these streams is that they encode and decode in **reverse** direction. |
|
69 | 69 | * So the first bit sequence you add will be the last to be read, like a LIFO stack. |
|
70 | 70 | */ |
|
71 | 71 | typedef struct |
|
72 | 72 | { |
|
73 | 73 | size_t bitContainer; |
|
74 | 74 | int bitPos; |
|
75 | 75 | char* startPtr; |
|
76 | 76 | char* ptr; |
|
77 | 77 | char* endPtr; |
|
78 | 78 | } BIT_CStream_t; |
|
79 | 79 | |
|
80 | 80 | MEM_STATIC size_t BIT_initCStream(BIT_CStream_t* bitC, void* dstBuffer, size_t dstCapacity); |
|
81 | 81 | MEM_STATIC void BIT_addBits(BIT_CStream_t* bitC, size_t value, unsigned nbBits); |
|
82 | 82 | MEM_STATIC void BIT_flushBits(BIT_CStream_t* bitC); |
|
83 | 83 | MEM_STATIC size_t BIT_closeCStream(BIT_CStream_t* bitC); |
|
84 | 84 | |
|
85 | 85 | /* Start with initCStream, providing the size of buffer to write into. |
|
86 | 86 | * bitStream will never write outside of this buffer. |
|
87 | 87 | * `dstCapacity` must be >= sizeof(bitD->bitContainer), otherwise @return will be an error code. |
|
88 | 88 | * |
|
89 | 89 | * bits are first added to a local register. |
|
90 | 90 | * Local register is size_t, hence 64-bits on 64-bits systems, or 32-bits on 32-bits systems. |
|
91 | 91 | * Writing data into memory is an explicit operation, performed by the flushBits function. |
|
92 | 92 | * Hence keep track how many bits are potentially stored into local register to avoid register overflow. |
|
93 | 93 | * After a flushBits, a maximum of 7 bits might still be stored into local register. |
|
94 | 94 | * |
|
95 | 95 | * Avoid storing elements of more than 24 bits if you want compatibility with 32-bits bitstream readers. |
|
96 | 96 | * |
|
97 | 97 | * Last operation is to close the bitStream. |
|
98 | 98 | * The function returns the final size of CStream in bytes. |
|
99 | 99 | * If data couldn't fit into `dstBuffer`, it will return a 0 ( == not storable) |
|
100 | 100 | */ |
|
101 | 101 | |
|
102 | 102 | |
|
103 | 103 | /*-******************************************** |
|
104 | 104 | * bitStream decoding API (read backward) |
|
105 | 105 | **********************************************/ |
|
106 | 106 | typedef struct |
|
107 | 107 | { |
|
108 | 108 | size_t bitContainer; |
|
109 | 109 | unsigned bitsConsumed; |
|
110 | 110 | const char* ptr; |
|
111 | 111 | const char* start; |
|
112 | 112 | } BIT_DStream_t; |
|
113 | 113 | |
|
114 | 114 | typedef enum { BIT_DStream_unfinished = 0, |
|
115 | 115 | BIT_DStream_endOfBuffer = 1, |
|
116 | 116 | BIT_DStream_completed = 2, |
|
117 | 117 | BIT_DStream_overflow = 3 } BIT_DStream_status; /* result of BIT_reloadDStream() */ |
|
118 | 118 | /* 1,2,4,8 would be better for bitmap combinations, but slows down performance a bit ... :( */ |
|
119 | 119 | |
|
120 | 120 | MEM_STATIC size_t BIT_initDStream(BIT_DStream_t* bitD, const void* srcBuffer, size_t srcSize); |
|
121 | 121 | MEM_STATIC size_t BIT_readBits(BIT_DStream_t* bitD, unsigned nbBits); |
|
122 | 122 | MEM_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD); |
|
123 | 123 | MEM_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t* bitD); |
|
124 | 124 | |
|
125 | 125 | |
|
126 | 126 | /* Start by invoking BIT_initDStream(). |
|
127 | 127 | * A chunk of the bitStream is then stored into a local register. |
|
128 | 128 | * Local register size is 64-bits on 64-bits systems, 32-bits on 32-bits systems (size_t). |
|
129 | 129 | * You can then retrieve bitFields stored into the local register, **in reverse order**. |
|
130 | 130 | * Local register is explicitly reloaded from memory by the BIT_reloadDStream() method. |
|
131 | 131 | * A reload guarantee a minimum of ((8*sizeof(bitD->bitContainer))-7) bits when its result is BIT_DStream_unfinished. |
|
132 | 132 | * Otherwise, it can be less than that, so proceed accordingly. |
|
133 | 133 | * Checking if DStream has reached its end can be performed with BIT_endOfDStream(). |
|
134 | 134 | */ |
|
135 | 135 | |
|
136 | 136 | |
|
137 | 137 | /*-**************************************** |
|
138 | 138 | * unsafe API |
|
139 | 139 | ******************************************/ |
|
140 | 140 | MEM_STATIC void BIT_addBitsFast(BIT_CStream_t* bitC, size_t value, unsigned nbBits); |
|
141 | 141 | /* faster, but works only if value is "clean", meaning all high bits above nbBits are 0 */ |
|
142 | 142 | |
|
143 | 143 | MEM_STATIC void BIT_flushBitsFast(BIT_CStream_t* bitC); |
|
144 | 144 | /* unsafe version; does not check buffer overflow */ |
|
145 | 145 | |
|
146 | 146 | MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, unsigned nbBits); |
|
147 | 147 | /* faster, but works only if nbBits >= 1 */ |
|
148 | 148 | |
|
149 | 149 | |
|
150 | 150 | |
|
151 | 151 | /*-************************************************************** |
|
152 | 152 | * Internal functions |
|
153 | 153 | ****************************************************************/ |
|
154 | 154 | MEM_STATIC unsigned BIT_highbit32 (register U32 val) |
|
155 | 155 | { |
|
156 | 156 | # if defined(_MSC_VER) /* Visual */ |
|
157 | 157 | unsigned long r=0; |
|
158 | 158 | _BitScanReverse ( &r, val ); |
|
159 | 159 | return (unsigned) r; |
|
160 | 160 | # elif defined(__GNUC__) && (__GNUC__ >= 3) /* Use GCC Intrinsic */ |
|
161 | 161 | return 31 - __builtin_clz (val); |
|
162 | 162 | # else /* Software version */ |
|
163 | 163 | static const unsigned DeBruijnClz[32] = { 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 }; |
|
164 | 164 | U32 v = val; |
|
165 | 165 | v |= v >> 1; |
|
166 | 166 | v |= v >> 2; |
|
167 | 167 | v |= v >> 4; |
|
168 | 168 | v |= v >> 8; |
|
169 | 169 | v |= v >> 16; |
|
170 | 170 | return DeBruijnClz[ (U32) (v * 0x07C4ACDDU) >> 27]; |
|
171 | 171 | # endif |
|
172 | 172 | } |
|
173 | 173 | |
|
174 | 174 | /*===== Local Constants =====*/ |
|
175 | 175 | static const unsigned BIT_mask[] = { 0, 1, 3, 7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF, 0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF, 0x1FFFFFF, 0x3FFFFFF }; /* up to 26 bits */ |
|
176 | 176 | |
|
177 | 177 | |
|
178 | 178 | /*-************************************************************** |
|
179 | 179 | * bitStream encoding |
|
180 | 180 | ****************************************************************/ |
|
181 | 181 | /*! BIT_initCStream() : |
|
182 | 182 | * `dstCapacity` must be > sizeof(void*) |
|
183 | 183 | * @return : 0 if success, |
|
184 | 184 | otherwise an error code (can be tested using ERR_isError() ) */ |
|
185 | 185 | MEM_STATIC size_t BIT_initCStream(BIT_CStream_t* bitC, void* startPtr, size_t dstCapacity) |
|
186 | 186 | { |
|
187 | 187 | bitC->bitContainer = 0; |
|
188 | 188 | bitC->bitPos = 0; |
|
189 | 189 | bitC->startPtr = (char*)startPtr; |
|
190 | 190 | bitC->ptr = bitC->startPtr; |
|
191 | 191 | bitC->endPtr = bitC->startPtr + dstCapacity - sizeof(bitC->ptr); |
|
192 | 192 | if (dstCapacity <= sizeof(bitC->ptr)) return ERROR(dstSize_tooSmall); |
|
193 | 193 | return 0; |
|
194 | 194 | } |
|
195 | 195 | |
|
196 | 196 | /*! BIT_addBits() : |
|
197 | 197 | can add up to 26 bits into `bitC`. |
|
198 | 198 | Does not check for register overflow ! */ |
|
199 | 199 | MEM_STATIC void BIT_addBits(BIT_CStream_t* bitC, size_t value, unsigned nbBits) |
|
200 | 200 | { |
|
201 | 201 | bitC->bitContainer |= (value & BIT_mask[nbBits]) << bitC->bitPos; |
|
202 | 202 | bitC->bitPos += nbBits; |
|
203 | 203 | } |
|
204 | 204 | |
|
205 | 205 | /*! BIT_addBitsFast() : |
|
206 | 206 | * works only if `value` is _clean_, meaning all high bits above nbBits are 0 */ |
|
207 | 207 | MEM_STATIC void BIT_addBitsFast(BIT_CStream_t* bitC, size_t value, unsigned nbBits) |
|
208 | 208 | { |
|
209 | 209 | bitC->bitContainer |= value << bitC->bitPos; |
|
210 | 210 | bitC->bitPos += nbBits; |
|
211 | 211 | } |
|
212 | 212 | |
|
213 | 213 | /*! BIT_flushBitsFast() : |
|
214 | 214 | * unsafe version; does not check buffer overflow */ |
|
215 | 215 | MEM_STATIC void BIT_flushBitsFast(BIT_CStream_t* bitC) |
|
216 | 216 | { |
|
217 | 217 | size_t const nbBytes = bitC->bitPos >> 3; |
|
218 | 218 | MEM_writeLEST(bitC->ptr, bitC->bitContainer); |
|
219 | 219 | bitC->ptr += nbBytes; |
|
220 | 220 | bitC->bitPos &= 7; |
|
221 | 221 | bitC->bitContainer >>= nbBytes*8; /* if bitPos >= sizeof(bitContainer)*8 --> undefined behavior */ |
|
222 | 222 | } |
|
223 | 223 | |
|
224 | 224 | /*! BIT_flushBits() : |
|
225 | 225 | * safe version; check for buffer overflow, and prevents it. |
|
226 | 226 | * note : does not signal buffer overflow. This will be revealed later on using BIT_closeCStream() */ |
|
227 | 227 | MEM_STATIC void BIT_flushBits(BIT_CStream_t* bitC) |
|
228 | 228 | { |
|
229 | 229 | size_t const nbBytes = bitC->bitPos >> 3; |
|
230 | 230 | MEM_writeLEST(bitC->ptr, bitC->bitContainer); |
|
231 | 231 | bitC->ptr += nbBytes; |
|
232 | 232 | if (bitC->ptr > bitC->endPtr) bitC->ptr = bitC->endPtr; |
|
233 | 233 | bitC->bitPos &= 7; |
|
234 | 234 | bitC->bitContainer >>= nbBytes*8; /* if bitPos >= sizeof(bitContainer)*8 --> undefined behavior */ |
|
235 | 235 | } |
|
236 | 236 | |
|
237 | 237 | /*! BIT_closeCStream() : |
|
238 | 238 | * @return : size of CStream, in bytes, |
|
239 | 239 | or 0 if it could not fit into dstBuffer */ |
|
240 | 240 | MEM_STATIC size_t BIT_closeCStream(BIT_CStream_t* bitC) |
|
241 | 241 | { |
|
242 | 242 | BIT_addBitsFast(bitC, 1, 1); /* endMark */ |
|
243 | 243 | BIT_flushBits(bitC); |
|
244 | 244 | |
|
245 | 245 | if (bitC->ptr >= bitC->endPtr) return 0; /* doesn't fit within authorized budget : cancel */ |
|
246 | 246 | |
|
247 | 247 | return (bitC->ptr - bitC->startPtr) + (bitC->bitPos > 0); |
|
248 | 248 | } |
|
249 | 249 | |
|
250 | 250 | |
|
251 | 251 | /*-******************************************************** |
|
252 | 252 | * bitStream decoding |
|
253 | 253 | **********************************************************/ |
|
254 | 254 | /*! BIT_initDStream() : |
|
255 | 255 | * Initialize a BIT_DStream_t. |
|
256 | 256 | * `bitD` : a pointer to an already allocated BIT_DStream_t structure. |
|
257 | 257 | * `srcSize` must be the *exact* size of the bitStream, in bytes. |
|
258 | 258 | * @return : size of stream (== srcSize) or an errorCode if a problem is detected |
|
259 | 259 | */ |
|
260 | 260 | MEM_STATIC size_t BIT_initDStream(BIT_DStream_t* bitD, const void* srcBuffer, size_t srcSize) |
|
261 | 261 | { |
|
262 | 262 | if (srcSize < 1) { memset(bitD, 0, sizeof(*bitD)); return ERROR(srcSize_wrong); } |
|
263 | 263 | |
|
264 | 264 | if (srcSize >= sizeof(bitD->bitContainer)) { /* normal case */ |
|
265 | 265 | bitD->start = (const char*)srcBuffer; |
|
266 | 266 | bitD->ptr = (const char*)srcBuffer + srcSize - sizeof(bitD->bitContainer); |
|
267 | 267 | bitD->bitContainer = MEM_readLEST(bitD->ptr); |
|
268 | 268 | { BYTE const lastByte = ((const BYTE*)srcBuffer)[srcSize-1]; |
|
269 | bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0; | |
|
269 | bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0; /* ensures bitsConsumed is always set */ | |
|
270 | 270 | if (lastByte == 0) return ERROR(GENERIC); /* endMark not present */ } |
|
271 | 271 | } else { |
|
272 | 272 | bitD->start = (const char*)srcBuffer; |
|
273 | 273 | bitD->ptr = bitD->start; |
|
274 | 274 | bitD->bitContainer = *(const BYTE*)(bitD->start); |
|
275 | 275 | switch(srcSize) |
|
276 | 276 | { |
|
277 | 277 | case 7: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[6]) << (sizeof(bitD->bitContainer)*8 - 16); |
|
278 | 278 | case 6: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[5]) << (sizeof(bitD->bitContainer)*8 - 24); |
|
279 | 279 | case 5: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[4]) << (sizeof(bitD->bitContainer)*8 - 32); |
|
280 | 280 | case 4: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[3]) << 24; |
|
281 | 281 | case 3: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[2]) << 16; |
|
282 | 282 | case 2: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[1]) << 8; |
|
283 | 283 | default:; |
|
284 | 284 | } |
|
285 | 285 | { BYTE const lastByte = ((const BYTE*)srcBuffer)[srcSize-1]; |
|
286 | 286 | bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0; |
|
287 | 287 | if (lastByte == 0) return ERROR(GENERIC); /* endMark not present */ } |
|
288 | 288 | bitD->bitsConsumed += (U32)(sizeof(bitD->bitContainer) - srcSize)*8; |
|
289 | 289 | } |
|
290 | 290 | |
|
291 | 291 | return srcSize; |
|
292 | 292 | } |
|
293 | 293 | |
|
294 | 294 | MEM_STATIC size_t BIT_getUpperBits(size_t bitContainer, U32 const start) |
|
295 | 295 | { |
|
296 | 296 | return bitContainer >> start; |
|
297 | 297 | } |
|
298 | 298 | |
|
299 | 299 | MEM_STATIC size_t BIT_getMiddleBits(size_t bitContainer, U32 const start, U32 const nbBits) |
|
300 | 300 | { |
|
301 | #if defined(__BMI__) && defined(__GNUC__) /* experimental */ | |
|
301 | #if defined(__BMI__) && defined(__GNUC__) && __GNUC__*1000+__GNUC_MINOR__ >= 4008 /* experimental */ | |
|
302 | 302 | # if defined(__x86_64__) |
|
303 | 303 | if (sizeof(bitContainer)==8) |
|
304 | 304 | return _bextr_u64(bitContainer, start, nbBits); |
|
305 | 305 | else |
|
306 | 306 | # endif |
|
307 | 307 | return _bextr_u32(bitContainer, start, nbBits); |
|
308 | 308 | #else |
|
309 | 309 | return (bitContainer >> start) & BIT_mask[nbBits]; |
|
310 | 310 | #endif |
|
311 | 311 | } |
|
312 | 312 | |
|
313 | 313 | MEM_STATIC size_t BIT_getLowerBits(size_t bitContainer, U32 const nbBits) |
|
314 | 314 | { |
|
315 | 315 | return bitContainer & BIT_mask[nbBits]; |
|
316 | 316 | } |
|
317 | 317 | |
|
318 | 318 | /*! BIT_lookBits() : |
|
319 | 319 | * Provides next n bits from local register. |
|
320 | 320 | * local register is not modified. |
|
321 | 321 | * On 32-bits, maxNbBits==24. |
|
322 | 322 | * On 64-bits, maxNbBits==56. |
|
323 | 323 | * @return : value extracted |
|
324 | 324 | */ |
|
325 | 325 | MEM_STATIC size_t BIT_lookBits(const BIT_DStream_t* bitD, U32 nbBits) |
|
326 | 326 | { |
|
327 | 327 | #if defined(__BMI__) && defined(__GNUC__) /* experimental; fails if bitD->bitsConsumed + nbBits > sizeof(bitD->bitContainer)*8 */ |
|
328 | 328 | return BIT_getMiddleBits(bitD->bitContainer, (sizeof(bitD->bitContainer)*8) - bitD->bitsConsumed - nbBits, nbBits); |
|
329 | 329 | #else |
|
330 | 330 | U32 const bitMask = sizeof(bitD->bitContainer)*8 - 1; |
|
331 | 331 | return ((bitD->bitContainer << (bitD->bitsConsumed & bitMask)) >> 1) >> ((bitMask-nbBits) & bitMask); |
|
332 | 332 | #endif |
|
333 | 333 | } |
|
334 | 334 | |
|
335 | 335 | /*! BIT_lookBitsFast() : |
|
336 | 336 | * unsafe version; only works only if nbBits >= 1 */ |
|
337 | 337 | MEM_STATIC size_t BIT_lookBitsFast(const BIT_DStream_t* bitD, U32 nbBits) |
|
338 | 338 | { |
|
339 | 339 | U32 const bitMask = sizeof(bitD->bitContainer)*8 - 1; |
|
340 | 340 | return (bitD->bitContainer << (bitD->bitsConsumed & bitMask)) >> (((bitMask+1)-nbBits) & bitMask); |
|
341 | 341 | } |
|
342 | 342 | |
|
343 | 343 | MEM_STATIC void BIT_skipBits(BIT_DStream_t* bitD, U32 nbBits) |
|
344 | 344 | { |
|
345 | 345 | bitD->bitsConsumed += nbBits; |
|
346 | 346 | } |
|
347 | 347 | |
|
348 | 348 | /*! BIT_readBits() : |
|
349 | 349 | * Read (consume) next n bits from local register and update. |
|
350 | 350 | * Pay attention to not read more than nbBits contained into local register. |
|
351 | 351 | * @return : extracted value. |
|
352 | 352 | */ |
|
353 | 353 | MEM_STATIC size_t BIT_readBits(BIT_DStream_t* bitD, U32 nbBits) |
|
354 | 354 | { |
|
355 | 355 | size_t const value = BIT_lookBits(bitD, nbBits); |
|
356 | 356 | BIT_skipBits(bitD, nbBits); |
|
357 | 357 | return value; |
|
358 | 358 | } |
|
359 | 359 | |
|
360 | 360 | /*! BIT_readBitsFast() : |
|
361 | 361 | * unsafe version; only works only if nbBits >= 1 */ |
|
362 | 362 | MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, U32 nbBits) |
|
363 | 363 | { |
|
364 | 364 | size_t const value = BIT_lookBitsFast(bitD, nbBits); |
|
365 | 365 | BIT_skipBits(bitD, nbBits); |
|
366 | 366 | return value; |
|
367 | 367 | } |
|
368 | 368 | |
|
369 | 369 | /*! BIT_reloadDStream() : |
|
370 |
* Refill ` |
|
|
370 | * Refill `bitD` from buffer previously set in BIT_initDStream() . | |
|
371 | 371 | * This function is safe, it guarantees it will not read beyond src buffer. |
|
372 | 372 | * @return : status of `BIT_DStream_t` internal register. |
|
373 | if status == unfinished, internal register is filled with >= (sizeof(bitD->bitContainer)*8 - 7) bits */ | |
|
373 | if status == BIT_DStream_unfinished, internal register is filled with >= (sizeof(bitD->bitContainer)*8 - 7) bits */ | |
|
374 | 374 | MEM_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD) |
|
375 | 375 | { |
|
376 | 376 | if (bitD->bitsConsumed > (sizeof(bitD->bitContainer)*8)) /* should not happen => corruption detected */ |
|
377 | 377 | return BIT_DStream_overflow; |
|
378 | 378 | |
|
379 | 379 | if (bitD->ptr >= bitD->start + sizeof(bitD->bitContainer)) { |
|
380 | 380 | bitD->ptr -= bitD->bitsConsumed >> 3; |
|
381 | 381 | bitD->bitsConsumed &= 7; |
|
382 | 382 | bitD->bitContainer = MEM_readLEST(bitD->ptr); |
|
383 | 383 | return BIT_DStream_unfinished; |
|
384 | 384 | } |
|
385 | 385 | if (bitD->ptr == bitD->start) { |
|
386 | 386 | if (bitD->bitsConsumed < sizeof(bitD->bitContainer)*8) return BIT_DStream_endOfBuffer; |
|
387 | 387 | return BIT_DStream_completed; |
|
388 | 388 | } |
|
389 | 389 | { U32 nbBytes = bitD->bitsConsumed >> 3; |
|
390 | 390 | BIT_DStream_status result = BIT_DStream_unfinished; |
|
391 | 391 | if (bitD->ptr - nbBytes < bitD->start) { |
|
392 | 392 | nbBytes = (U32)(bitD->ptr - bitD->start); /* ptr > start */ |
|
393 | 393 | result = BIT_DStream_endOfBuffer; |
|
394 | 394 | } |
|
395 | 395 | bitD->ptr -= nbBytes; |
|
396 | 396 | bitD->bitsConsumed -= nbBytes*8; |
|
397 | 397 | bitD->bitContainer = MEM_readLEST(bitD->ptr); /* reminder : srcSize > sizeof(bitD) */ |
|
398 | 398 | return result; |
|
399 | 399 | } |
|
400 | 400 | } |
|
401 | 401 | |
|
402 | 402 | /*! BIT_endOfDStream() : |
|
403 | 403 | * @return Tells if DStream has exactly reached its end (all bits consumed). |
|
404 | 404 | */ |
|
405 | 405 | MEM_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t* DStream) |
|
406 | 406 | { |
|
407 | 407 | return ((DStream->ptr == DStream->start) && (DStream->bitsConsumed == sizeof(DStream->bitContainer)*8)); |
|
408 | 408 | } |
|
409 | 409 | |
|
410 | 410 | #if defined (__cplusplus) |
|
411 | 411 | } |
|
412 | 412 | #endif |
|
413 | 413 | |
|
414 | 414 | #endif /* BITSTREAM_H_MODULE */ |
@@ -1,225 +1,227 b'' | |||
|
1 | 1 | /* |
|
2 | 2 | Common functions of New Generation Entropy library |
|
3 | 3 | Copyright (C) 2016, Yann Collet. |
|
4 | 4 | |
|
5 | 5 | BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) |
|
6 | 6 | |
|
7 | 7 | Redistribution and use in source and binary forms, with or without |
|
8 | 8 | modification, are permitted provided that the following conditions are |
|
9 | 9 | met: |
|
10 | 10 | |
|
11 | 11 | * Redistributions of source code must retain the above copyright |
|
12 | 12 | notice, this list of conditions and the following disclaimer. |
|
13 | 13 | * Redistributions in binary form must reproduce the above |
|
14 | 14 | copyright notice, this list of conditions and the following disclaimer |
|
15 | 15 | in the documentation and/or other materials provided with the |
|
16 | 16 | distribution. |
|
17 | 17 | |
|
18 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
19 | 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
20 | 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
21 | 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
22 | 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
23 | 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
24 | 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
25 | 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
26 | 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
27 | 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
28 | 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
29 | 29 | |
|
30 | 30 | You can contact the author at : |
|
31 | 31 | - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy |
|
32 | 32 | - Public forum : https://groups.google.com/forum/#!forum/lz4c |
|
33 | 33 | *************************************************************************** */ |
|
34 | 34 | |
|
35 | 35 | /* ************************************* |
|
36 | 36 | * Dependencies |
|
37 | 37 | ***************************************/ |
|
38 | 38 | #include "mem.h" |
|
39 | 39 | #include "error_private.h" /* ERR_*, ERROR */ |
|
40 | 40 | #define FSE_STATIC_LINKING_ONLY /* FSE_MIN_TABLELOG */ |
|
41 | 41 | #include "fse.h" |
|
42 | 42 | #define HUF_STATIC_LINKING_ONLY /* HUF_TABLELOG_ABSOLUTEMAX */ |
|
43 | 43 | #include "huf.h" |
|
44 | 44 | |
|
45 | 45 | |
|
46 | 46 | /*-**************************************** |
|
47 | 47 | * FSE Error Management |
|
48 | 48 | ******************************************/ |
|
49 | 49 | unsigned FSE_isError(size_t code) { return ERR_isError(code); } |
|
50 | 50 | |
|
51 | 51 | const char* FSE_getErrorName(size_t code) { return ERR_getErrorName(code); } |
|
52 | 52 | |
|
53 | 53 | |
|
54 | 54 | /* ************************************************************** |
|
55 | 55 | * HUF Error Management |
|
56 | 56 | ****************************************************************/ |
|
57 | 57 | unsigned HUF_isError(size_t code) { return ERR_isError(code); } |
|
58 | 58 | |
|
59 | 59 | const char* HUF_getErrorName(size_t code) { return ERR_getErrorName(code); } |
|
60 | 60 | |
|
61 | 61 | |
|
62 | 62 | /*-************************************************************** |
|
63 | 63 | * FSE NCount encoding-decoding |
|
64 | 64 | ****************************************************************/ |
|
65 | 65 | static short FSE_abs(short a) { return (short)(a<0 ? -a : a); } |
|
66 | 66 | |
|
67 | 67 | size_t FSE_readNCount (short* normalizedCounter, unsigned* maxSVPtr, unsigned* tableLogPtr, |
|
68 | 68 | const void* headerBuffer, size_t hbSize) |
|
69 | 69 | { |
|
70 | 70 | const BYTE* const istart = (const BYTE*) headerBuffer; |
|
71 | 71 | const BYTE* const iend = istart + hbSize; |
|
72 | 72 | const BYTE* ip = istart; |
|
73 | 73 | int nbBits; |
|
74 | 74 | int remaining; |
|
75 | 75 | int threshold; |
|
76 | 76 | U32 bitStream; |
|
77 | 77 | int bitCount; |
|
78 | 78 | unsigned charnum = 0; |
|
79 | 79 | int previous0 = 0; |
|
80 | 80 | |
|
81 | 81 | if (hbSize < 4) return ERROR(srcSize_wrong); |
|
82 | 82 | bitStream = MEM_readLE32(ip); |
|
83 | 83 | nbBits = (bitStream & 0xF) + FSE_MIN_TABLELOG; /* extract tableLog */ |
|
84 | 84 | if (nbBits > FSE_TABLELOG_ABSOLUTE_MAX) return ERROR(tableLog_tooLarge); |
|
85 | 85 | bitStream >>= 4; |
|
86 | 86 | bitCount = 4; |
|
87 | 87 | *tableLogPtr = nbBits; |
|
88 | 88 | remaining = (1<<nbBits)+1; |
|
89 | 89 | threshold = 1<<nbBits; |
|
90 | 90 | nbBits++; |
|
91 | 91 | |
|
92 | 92 | while ((remaining>1) & (charnum<=*maxSVPtr)) { |
|
93 | 93 | if (previous0) { |
|
94 | 94 | unsigned n0 = charnum; |
|
95 | 95 | while ((bitStream & 0xFFFF) == 0xFFFF) { |
|
96 | 96 | n0 += 24; |
|
97 | 97 | if (ip < iend-5) { |
|
98 | 98 | ip += 2; |
|
99 | 99 | bitStream = MEM_readLE32(ip) >> bitCount; |
|
100 | 100 | } else { |
|
101 | 101 | bitStream >>= 16; |
|
102 | 102 | bitCount += 16; |
|
103 | 103 | } } |
|
104 | 104 | while ((bitStream & 3) == 3) { |
|
105 | 105 | n0 += 3; |
|
106 | 106 | bitStream >>= 2; |
|
107 | 107 | bitCount += 2; |
|
108 | 108 | } |
|
109 | 109 | n0 += bitStream & 3; |
|
110 | 110 | bitCount += 2; |
|
111 | 111 | if (n0 > *maxSVPtr) return ERROR(maxSymbolValue_tooSmall); |
|
112 | 112 | while (charnum < n0) normalizedCounter[charnum++] = 0; |
|
113 | 113 | if ((ip <= iend-7) || (ip + (bitCount>>3) <= iend-4)) { |
|
114 | 114 | ip += bitCount>>3; |
|
115 | 115 | bitCount &= 7; |
|
116 | 116 | bitStream = MEM_readLE32(ip) >> bitCount; |
|
117 | 117 | } else { |
|
118 | 118 | bitStream >>= 2; |
|
119 | 119 | } } |
|
120 | 120 | { short const max = (short)((2*threshold-1)-remaining); |
|
121 | 121 | short count; |
|
122 | 122 | |
|
123 | 123 | if ((bitStream & (threshold-1)) < (U32)max) { |
|
124 | 124 | count = (short)(bitStream & (threshold-1)); |
|
125 | 125 | bitCount += nbBits-1; |
|
126 | 126 | } else { |
|
127 | 127 | count = (short)(bitStream & (2*threshold-1)); |
|
128 | 128 | if (count >= threshold) count -= max; |
|
129 | 129 | bitCount += nbBits; |
|
130 | 130 | } |
|
131 | 131 | |
|
132 | 132 | count--; /* extra accuracy */ |
|
133 | 133 | remaining -= FSE_abs(count); |
|
134 | 134 | normalizedCounter[charnum++] = count; |
|
135 | 135 | previous0 = !count; |
|
136 | 136 | while (remaining < threshold) { |
|
137 | 137 | nbBits--; |
|
138 | 138 | threshold >>= 1; |
|
139 | 139 | } |
|
140 | 140 | |
|
141 | 141 | if ((ip <= iend-7) || (ip + (bitCount>>3) <= iend-4)) { |
|
142 | 142 | ip += bitCount>>3; |
|
143 | 143 | bitCount &= 7; |
|
144 | 144 | } else { |
|
145 | 145 | bitCount -= (int)(8 * (iend - 4 - ip)); |
|
146 | 146 | ip = iend - 4; |
|
147 | 147 | } |
|
148 | 148 | bitStream = MEM_readLE32(ip) >> (bitCount & 31); |
|
149 | 149 | } } /* while ((remaining>1) & (charnum<=*maxSVPtr)) */ |
|
150 | 150 | if (remaining != 1) return ERROR(corruption_detected); |
|
151 | 151 | if (bitCount > 32) return ERROR(corruption_detected); |
|
152 | 152 | *maxSVPtr = charnum-1; |
|
153 | 153 | |
|
154 | 154 | ip += (bitCount+7)>>3; |
|
155 | 155 | return ip-istart; |
|
156 | 156 | } |
|
157 | 157 | |
|
158 | 158 | |
|
159 | 159 | /*! HUF_readStats() : |
|
160 | 160 | Read compact Huffman tree, saved by HUF_writeCTable(). |
|
161 | 161 | `huffWeight` is destination buffer. |
|
162 | `rankStats` is assumed to be a table of at least HUF_TABLELOG_MAX U32. | |
|
162 | 163 | @return : size read from `src` , or an error Code . |
|
163 | 164 | Note : Needed by HUF_readCTable() and HUF_readDTableX?() . |
|
164 | 165 | */ |
|
165 | 166 | size_t HUF_readStats(BYTE* huffWeight, size_t hwSize, U32* rankStats, |
|
166 | 167 | U32* nbSymbolsPtr, U32* tableLogPtr, |
|
167 | 168 | const void* src, size_t srcSize) |
|
168 | 169 | { |
|
169 | 170 | U32 weightTotal; |
|
170 | 171 | const BYTE* ip = (const BYTE*) src; |
|
171 | 172 | size_t iSize; |
|
172 | 173 | size_t oSize; |
|
173 | 174 | |
|
174 | 175 | if (!srcSize) return ERROR(srcSize_wrong); |
|
175 | 176 | iSize = ip[0]; |
|
176 | 177 | /* memset(huffWeight, 0, hwSize); *//* is not necessary, even though some analyzer complain ... */ |
|
177 | 178 | |
|
178 | 179 | if (iSize >= 128) { /* special header */ |
|
179 | 180 | oSize = iSize - 127; |
|
180 | 181 | iSize = ((oSize+1)/2); |
|
181 | 182 | if (iSize+1 > srcSize) return ERROR(srcSize_wrong); |
|
182 | 183 | if (oSize >= hwSize) return ERROR(corruption_detected); |
|
183 | 184 | ip += 1; |
|
184 | 185 | { U32 n; |
|
185 | 186 | for (n=0; n<oSize; n+=2) { |
|
186 | 187 | huffWeight[n] = ip[n/2] >> 4; |
|
187 | 188 | huffWeight[n+1] = ip[n/2] & 15; |
|
188 | 189 | } } } |
|
189 | 190 | else { /* header compressed with FSE (normal case) */ |
|
191 | FSE_DTable fseWorkspace[FSE_DTABLE_SIZE_U32(6)]; /* 6 is max possible tableLog for HUF header (maybe even 5, to be tested) */ | |
|
190 | 192 | if (iSize+1 > srcSize) return ERROR(srcSize_wrong); |
|
191 | oSize = FSE_decompress(huffWeight, hwSize-1, ip+1, iSize); /* max (hwSize-1) values decoded, as last one is implied */ | |
|
193 | oSize = FSE_decompress_wksp(huffWeight, hwSize-1, ip+1, iSize, fseWorkspace, 6); /* max (hwSize-1) values decoded, as last one is implied */ | |
|
192 | 194 | if (FSE_isError(oSize)) return oSize; |
|
193 | 195 | } |
|
194 | 196 | |
|
195 | 197 | /* collect weight stats */ |
|
196 |
memset(rankStats, 0, (HUF_TABLELOG_ |
|
|
198 | memset(rankStats, 0, (HUF_TABLELOG_MAX + 1) * sizeof(U32)); | |
|
197 | 199 | weightTotal = 0; |
|
198 | 200 | { U32 n; for (n=0; n<oSize; n++) { |
|
199 |
if (huffWeight[n] >= HUF_TABLELOG_ |
|
|
201 | if (huffWeight[n] >= HUF_TABLELOG_MAX) return ERROR(corruption_detected); | |
|
200 | 202 | rankStats[huffWeight[n]]++; |
|
201 | 203 | weightTotal += (1 << huffWeight[n]) >> 1; |
|
202 | 204 | } } |
|
203 | 205 | if (weightTotal == 0) return ERROR(corruption_detected); |
|
204 | 206 | |
|
205 | 207 | /* get last non-null symbol weight (implied, total must be 2^n) */ |
|
206 | 208 | { U32 const tableLog = BIT_highbit32(weightTotal) + 1; |
|
207 |
if (tableLog > HUF_TABLELOG_ |
|
|
209 | if (tableLog > HUF_TABLELOG_MAX) return ERROR(corruption_detected); | |
|
208 | 210 | *tableLogPtr = tableLog; |
|
209 | 211 | /* determine last weight */ |
|
210 | 212 | { U32 const total = 1 << tableLog; |
|
211 | 213 | U32 const rest = total - weightTotal; |
|
212 | 214 | U32 const verif = 1 << BIT_highbit32(rest); |
|
213 | 215 | U32 const lastWeight = BIT_highbit32(rest) + 1; |
|
214 | 216 | if (verif != rest) return ERROR(corruption_detected); /* last value must be a clean power of 2 */ |
|
215 | 217 | huffWeight[oSize] = (BYTE)lastWeight; |
|
216 | 218 | rankStats[lastWeight]++; |
|
217 | 219 | } } |
|
218 | 220 | |
|
219 | 221 | /* check tree construction validity */ |
|
220 | 222 | if ((rankStats[1] < 2) || (rankStats[1] & 1)) return ERROR(corruption_detected); /* by construction : at least 2 elts of rank 1, must be even */ |
|
221 | 223 | |
|
222 | 224 | /* results */ |
|
223 | 225 | *nbSymbolsPtr = (U32)(oSize+1); |
|
224 | 226 | return iSize+1; |
|
225 | 227 | } |
@@ -1,634 +1,668 b'' | |||
|
1 | 1 | /* ****************************************************************** |
|
2 | 2 | FSE : Finite State Entropy codec |
|
3 | 3 | Public Prototypes declaration |
|
4 | 4 | Copyright (C) 2013-2016, Yann Collet. |
|
5 | 5 | |
|
6 | 6 | BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) |
|
7 | 7 | |
|
8 | 8 | Redistribution and use in source and binary forms, with or without |
|
9 | 9 | modification, are permitted provided that the following conditions are |
|
10 | 10 | met: |
|
11 | 11 | |
|
12 | 12 | * Redistributions of source code must retain the above copyright |
|
13 | 13 | notice, this list of conditions and the following disclaimer. |
|
14 | 14 | * Redistributions in binary form must reproduce the above |
|
15 | 15 | copyright notice, this list of conditions and the following disclaimer |
|
16 | 16 | in the documentation and/or other materials provided with the |
|
17 | 17 | distribution. |
|
18 | 18 | |
|
19 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
20 | 20 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
21 | 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
22 | 22 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
23 | 23 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
24 | 24 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
25 | 25 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
26 | 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
27 | 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
28 | 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
29 | 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
30 | 30 | |
|
31 | 31 | You can contact the author at : |
|
32 | 32 | - Source repository : https://github.com/Cyan4973/FiniteStateEntropy |
|
33 | 33 | ****************************************************************** */ |
|
34 | 34 | #ifndef FSE_H |
|
35 | 35 | #define FSE_H |
|
36 | 36 | |
|
37 | 37 | #if defined (__cplusplus) |
|
38 | 38 | extern "C" { |
|
39 | 39 | #endif |
|
40 | 40 | |
|
41 | 41 | |
|
42 | 42 | /*-***************************************** |
|
43 | 43 | * Dependencies |
|
44 | 44 | ******************************************/ |
|
45 | 45 | #include <stddef.h> /* size_t, ptrdiff_t */ |
|
46 | 46 | |
|
47 | 47 | |
|
48 | 48 | /*-**************************************** |
|
49 | 49 | * FSE simple functions |
|
50 | 50 | ******************************************/ |
|
51 | 51 | /*! FSE_compress() : |
|
52 | 52 | Compress content of buffer 'src', of size 'srcSize', into destination buffer 'dst'. |
|
53 | 53 | 'dst' buffer must be already allocated. Compression runs faster is dstCapacity >= FSE_compressBound(srcSize). |
|
54 | 54 | @return : size of compressed data (<= dstCapacity). |
|
55 | 55 | Special values : if return == 0, srcData is not compressible => Nothing is stored within dst !!! |
|
56 | 56 | if return == 1, srcData is a single byte symbol * srcSize times. Use RLE compression instead. |
|
57 | 57 | if FSE_isError(return), compression failed (more details using FSE_getErrorName()) |
|
58 | 58 | */ |
|
59 | 59 | size_t FSE_compress(void* dst, size_t dstCapacity, |
|
60 | 60 | const void* src, size_t srcSize); |
|
61 | 61 | |
|
62 | 62 | /*! FSE_decompress(): |
|
63 | 63 | Decompress FSE data from buffer 'cSrc', of size 'cSrcSize', |
|
64 | 64 | into already allocated destination buffer 'dst', of size 'dstCapacity'. |
|
65 | 65 | @return : size of regenerated data (<= maxDstSize), |
|
66 | 66 | or an error code, which can be tested using FSE_isError() . |
|
67 | 67 | |
|
68 | 68 | ** Important ** : FSE_decompress() does not decompress non-compressible nor RLE data !!! |
|
69 | 69 | Why ? : making this distinction requires a header. |
|
70 | 70 | Header management is intentionally delegated to the user layer, which can better manage special cases. |
|
71 | 71 | */ |
|
72 | 72 | size_t FSE_decompress(void* dst, size_t dstCapacity, |
|
73 | 73 | const void* cSrc, size_t cSrcSize); |
|
74 | 74 | |
|
75 | 75 | |
|
76 | 76 | /*-***************************************** |
|
77 | 77 | * Tool functions |
|
78 | 78 | ******************************************/ |
|
79 | 79 | size_t FSE_compressBound(size_t size); /* maximum compressed size */ |
|
80 | 80 | |
|
81 | 81 | /* Error Management */ |
|
82 | 82 | unsigned FSE_isError(size_t code); /* tells if a return value is an error code */ |
|
83 | 83 | const char* FSE_getErrorName(size_t code); /* provides error code string (useful for debugging) */ |
|
84 | 84 | |
|
85 | 85 | |
|
86 | 86 | /*-***************************************** |
|
87 | 87 | * FSE advanced functions |
|
88 | 88 | ******************************************/ |
|
89 | 89 | /*! FSE_compress2() : |
|
90 | 90 | Same as FSE_compress(), but allows the selection of 'maxSymbolValue' and 'tableLog' |
|
91 | 91 | Both parameters can be defined as '0' to mean : use default value |
|
92 | 92 | @return : size of compressed data |
|
93 | 93 | Special values : if return == 0, srcData is not compressible => Nothing is stored within cSrc !!! |
|
94 | 94 | if return == 1, srcData is a single byte symbol * srcSize times. Use RLE compression. |
|
95 | 95 | if FSE_isError(return), it's an error code. |
|
96 | 96 | */ |
|
97 | 97 | size_t FSE_compress2 (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog); |
|
98 | 98 | |
|
99 | 99 | |
|
100 | 100 | /*-***************************************** |
|
101 | 101 | * FSE detailed API |
|
102 | 102 | ******************************************/ |
|
103 | 103 | /*! |
|
104 | 104 | FSE_compress() does the following: |
|
105 | 105 | 1. count symbol occurrence from source[] into table count[] |
|
106 | 106 | 2. normalize counters so that sum(count[]) == Power_of_2 (2^tableLog) |
|
107 | 107 | 3. save normalized counters to memory buffer using writeNCount() |
|
108 | 108 | 4. build encoding table 'CTable' from normalized counters |
|
109 | 109 | 5. encode the data stream using encoding table 'CTable' |
|
110 | 110 | |
|
111 | 111 | FSE_decompress() does the following: |
|
112 | 112 | 1. read normalized counters with readNCount() |
|
113 | 113 | 2. build decoding table 'DTable' from normalized counters |
|
114 | 114 | 3. decode the data stream using decoding table 'DTable' |
|
115 | 115 | |
|
116 | 116 | The following API allows targeting specific sub-functions for advanced tasks. |
|
117 | 117 | For example, it's possible to compress several blocks using the same 'CTable', |
|
118 | 118 | or to save and provide normalized distribution using external method. |
|
119 | 119 | */ |
|
120 | 120 | |
|
121 | 121 | /* *** COMPRESSION *** */ |
|
122 | 122 | |
|
123 | 123 | /*! FSE_count(): |
|
124 | 124 | Provides the precise count of each byte within a table 'count'. |
|
125 | 125 | 'count' is a table of unsigned int, of minimum size (*maxSymbolValuePtr+1). |
|
126 | 126 | *maxSymbolValuePtr will be updated if detected smaller than initial value. |
|
127 | 127 | @return : the count of the most frequent symbol (which is not identified). |
|
128 | 128 | if return == srcSize, there is only one symbol. |
|
129 | 129 | Can also return an error code, which can be tested with FSE_isError(). */ |
|
130 | 130 | size_t FSE_count(unsigned* count, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize); |
|
131 | 131 | |
|
132 | 132 | /*! FSE_optimalTableLog(): |
|
133 | 133 | dynamically downsize 'tableLog' when conditions are met. |
|
134 | 134 | It saves CPU time, by using smaller tables, while preserving or even improving compression ratio. |
|
135 | 135 | @return : recommended tableLog (necessarily <= 'maxTableLog') */ |
|
136 | 136 | unsigned FSE_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue); |
|
137 | 137 | |
|
138 | 138 | /*! FSE_normalizeCount(): |
|
139 | 139 | normalize counts so that sum(count[]) == Power_of_2 (2^tableLog) |
|
140 | 140 | 'normalizedCounter' is a table of short, of minimum size (maxSymbolValue+1). |
|
141 | 141 | @return : tableLog, |
|
142 | 142 | or an errorCode, which can be tested using FSE_isError() */ |
|
143 | 143 | size_t FSE_normalizeCount(short* normalizedCounter, unsigned tableLog, const unsigned* count, size_t srcSize, unsigned maxSymbolValue); |
|
144 | 144 | |
|
145 | 145 | /*! FSE_NCountWriteBound(): |
|
146 | 146 | Provides the maximum possible size of an FSE normalized table, given 'maxSymbolValue' and 'tableLog'. |
|
147 | 147 | Typically useful for allocation purpose. */ |
|
148 | 148 | size_t FSE_NCountWriteBound(unsigned maxSymbolValue, unsigned tableLog); |
|
149 | 149 | |
|
150 | 150 | /*! FSE_writeNCount(): |
|
151 | 151 | Compactly save 'normalizedCounter' into 'buffer'. |
|
152 | 152 | @return : size of the compressed table, |
|
153 | 153 | or an errorCode, which can be tested using FSE_isError(). */ |
|
154 | 154 | size_t FSE_writeNCount (void* buffer, size_t bufferSize, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog); |
|
155 | 155 | |
|
156 | 156 | |
|
157 | 157 | /*! Constructor and Destructor of FSE_CTable. |
|
158 | 158 | Note that FSE_CTable size depends on 'tableLog' and 'maxSymbolValue' */ |
|
159 | 159 | typedef unsigned FSE_CTable; /* don't allocate that. It's only meant to be more restrictive than void* */ |
|
160 | 160 | FSE_CTable* FSE_createCTable (unsigned tableLog, unsigned maxSymbolValue); |
|
161 | 161 | void FSE_freeCTable (FSE_CTable* ct); |
|
162 | 162 | |
|
163 | 163 | /*! FSE_buildCTable(): |
|
164 | 164 | Builds `ct`, which must be already allocated, using FSE_createCTable(). |
|
165 | 165 | @return : 0, or an errorCode, which can be tested using FSE_isError() */ |
|
166 | 166 | size_t FSE_buildCTable(FSE_CTable* ct, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog); |
|
167 | 167 | |
|
168 | 168 | /*! FSE_compress_usingCTable(): |
|
169 | 169 | Compress `src` using `ct` into `dst` which must be already allocated. |
|
170 | 170 | @return : size of compressed data (<= `dstCapacity`), |
|
171 | 171 | or 0 if compressed data could not fit into `dst`, |
|
172 | 172 | or an errorCode, which can be tested using FSE_isError() */ |
|
173 | 173 | size_t FSE_compress_usingCTable (void* dst, size_t dstCapacity, const void* src, size_t srcSize, const FSE_CTable* ct); |
|
174 | 174 | |
|
175 | 175 | /*! |
|
176 | 176 | Tutorial : |
|
177 | 177 | ---------- |
|
178 | 178 | The first step is to count all symbols. FSE_count() does this job very fast. |
|
179 | 179 | Result will be saved into 'count', a table of unsigned int, which must be already allocated, and have 'maxSymbolValuePtr[0]+1' cells. |
|
180 | 180 | 'src' is a table of bytes of size 'srcSize'. All values within 'src' MUST be <= maxSymbolValuePtr[0] |
|
181 | 181 | maxSymbolValuePtr[0] will be updated, with its real value (necessarily <= original value) |
|
182 | 182 | FSE_count() will return the number of occurrence of the most frequent symbol. |
|
183 | 183 | This can be used to know if there is a single symbol within 'src', and to quickly evaluate its compressibility. |
|
184 | 184 | If there is an error, the function will return an ErrorCode (which can be tested using FSE_isError()). |
|
185 | 185 | |
|
186 | 186 | The next step is to normalize the frequencies. |
|
187 | 187 | FSE_normalizeCount() will ensure that sum of frequencies is == 2 ^'tableLog'. |
|
188 | 188 | It also guarantees a minimum of 1 to any Symbol with frequency >= 1. |
|
189 | 189 | You can use 'tableLog'==0 to mean "use default tableLog value". |
|
190 | 190 | If you are unsure of which tableLog value to use, you can ask FSE_optimalTableLog(), |
|
191 | 191 | which will provide the optimal valid tableLog given sourceSize, maxSymbolValue, and a user-defined maximum (0 means "default"). |
|
192 | 192 | |
|
193 | 193 | The result of FSE_normalizeCount() will be saved into a table, |
|
194 | 194 | called 'normalizedCounter', which is a table of signed short. |
|
195 | 195 | 'normalizedCounter' must be already allocated, and have at least 'maxSymbolValue+1' cells. |
|
196 | 196 | The return value is tableLog if everything proceeded as expected. |
|
197 | 197 | It is 0 if there is a single symbol within distribution. |
|
198 | 198 | If there is an error (ex: invalid tableLog value), the function will return an ErrorCode (which can be tested using FSE_isError()). |
|
199 | 199 | |
|
200 | 200 | 'normalizedCounter' can be saved in a compact manner to a memory area using FSE_writeNCount(). |
|
201 | 201 | 'buffer' must be already allocated. |
|
202 | 202 | For guaranteed success, buffer size must be at least FSE_headerBound(). |
|
203 | 203 | The result of the function is the number of bytes written into 'buffer'. |
|
204 | 204 | If there is an error, the function will return an ErrorCode (which can be tested using FSE_isError(); ex : buffer size too small). |
|
205 | 205 | |
|
206 | 206 | 'normalizedCounter' can then be used to create the compression table 'CTable'. |
|
207 | 207 | The space required by 'CTable' must be already allocated, using FSE_createCTable(). |
|
208 | 208 | You can then use FSE_buildCTable() to fill 'CTable'. |
|
209 | 209 | If there is an error, both functions will return an ErrorCode (which can be tested using FSE_isError()). |
|
210 | 210 | |
|
211 | 211 | 'CTable' can then be used to compress 'src', with FSE_compress_usingCTable(). |
|
212 | 212 | Similar to FSE_count(), the convention is that 'src' is assumed to be a table of char of size 'srcSize' |
|
213 | 213 | The function returns the size of compressed data (without header), necessarily <= `dstCapacity`. |
|
214 | 214 | If it returns '0', compressed data could not fit into 'dst'. |
|
215 | 215 | If there is an error, the function will return an ErrorCode (which can be tested using FSE_isError()). |
|
216 | 216 | */ |
|
217 | 217 | |
|
218 | 218 | |
|
219 | 219 | /* *** DECOMPRESSION *** */ |
|
220 | 220 | |
|
221 | 221 | /*! FSE_readNCount(): |
|
222 | 222 | Read compactly saved 'normalizedCounter' from 'rBuffer'. |
|
223 | 223 | @return : size read from 'rBuffer', |
|
224 | 224 | or an errorCode, which can be tested using FSE_isError(). |
|
225 | 225 | maxSymbolValuePtr[0] and tableLogPtr[0] will also be updated with their respective values */ |
|
226 | 226 | size_t FSE_readNCount (short* normalizedCounter, unsigned* maxSymbolValuePtr, unsigned* tableLogPtr, const void* rBuffer, size_t rBuffSize); |
|
227 | 227 | |
|
228 | 228 | /*! Constructor and Destructor of FSE_DTable. |
|
229 | 229 | Note that its size depends on 'tableLog' */ |
|
230 | 230 | typedef unsigned FSE_DTable; /* don't allocate that. It's just a way to be more restrictive than void* */ |
|
231 | 231 | FSE_DTable* FSE_createDTable(unsigned tableLog); |
|
232 | 232 | void FSE_freeDTable(FSE_DTable* dt); |
|
233 | 233 | |
|
234 | 234 | /*! FSE_buildDTable(): |
|
235 | 235 | Builds 'dt', which must be already allocated, using FSE_createDTable(). |
|
236 | 236 | return : 0, or an errorCode, which can be tested using FSE_isError() */ |
|
237 | 237 | size_t FSE_buildDTable (FSE_DTable* dt, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog); |
|
238 | 238 | |
|
239 | 239 | /*! FSE_decompress_usingDTable(): |
|
240 | 240 | Decompress compressed source `cSrc` of size `cSrcSize` using `dt` |
|
241 | 241 | into `dst` which must be already allocated. |
|
242 | 242 | @return : size of regenerated data (necessarily <= `dstCapacity`), |
|
243 | 243 | or an errorCode, which can be tested using FSE_isError() */ |
|
244 | 244 | size_t FSE_decompress_usingDTable(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, const FSE_DTable* dt); |
|
245 | 245 | |
|
246 | 246 | /*! |
|
247 | 247 | Tutorial : |
|
248 | 248 | ---------- |
|
249 | 249 | (Note : these functions only decompress FSE-compressed blocks. |
|
250 | 250 | If block is uncompressed, use memcpy() instead |
|
251 | 251 | If block is a single repeated byte, use memset() instead ) |
|
252 | 252 | |
|
253 | 253 | The first step is to obtain the normalized frequencies of symbols. |
|
254 | 254 | This can be performed by FSE_readNCount() if it was saved using FSE_writeNCount(). |
|
255 | 255 | 'normalizedCounter' must be already allocated, and have at least 'maxSymbolValuePtr[0]+1' cells of signed short. |
|
256 | 256 | In practice, that means it's necessary to know 'maxSymbolValue' beforehand, |
|
257 | 257 | or size the table to handle worst case situations (typically 256). |
|
258 | 258 | FSE_readNCount() will provide 'tableLog' and 'maxSymbolValue'. |
|
259 | 259 | The result of FSE_readNCount() is the number of bytes read from 'rBuffer'. |
|
260 | 260 | Note that 'rBufferSize' must be at least 4 bytes, even if useful information is less than that. |
|
261 | 261 | If there is an error, the function will return an error code, which can be tested using FSE_isError(). |
|
262 | 262 | |
|
263 | 263 | The next step is to build the decompression tables 'FSE_DTable' from 'normalizedCounter'. |
|
264 | 264 | This is performed by the function FSE_buildDTable(). |
|
265 | 265 | The space required by 'FSE_DTable' must be already allocated using FSE_createDTable(). |
|
266 | 266 | If there is an error, the function will return an error code, which can be tested using FSE_isError(). |
|
267 | 267 | |
|
268 | 268 | `FSE_DTable` can then be used to decompress `cSrc`, with FSE_decompress_usingDTable(). |
|
269 | 269 | `cSrcSize` must be strictly correct, otherwise decompression will fail. |
|
270 | 270 | FSE_decompress_usingDTable() result will tell how many bytes were regenerated (<=`dstCapacity`). |
|
271 | 271 | If there is an error, the function will return an error code, which can be tested using FSE_isError(). (ex: dst buffer too small) |
|
272 | 272 | */ |
|
273 | 273 | |
|
274 | 274 | |
|
275 | 275 | #ifdef FSE_STATIC_LINKING_ONLY |
|
276 | 276 | |
|
277 | 277 | /* *** Dependency *** */ |
|
278 | 278 | #include "bitstream.h" |
|
279 | 279 | |
|
280 | 280 | |
|
281 | 281 | /* ***************************************** |
|
282 | 282 | * Static allocation |
|
283 | 283 | *******************************************/ |
|
284 | 284 | /* FSE buffer bounds */ |
|
285 | 285 | #define FSE_NCOUNTBOUND 512 |
|
286 | 286 | #define FSE_BLOCKBOUND(size) (size + (size>>7)) |
|
287 | 287 | #define FSE_COMPRESSBOUND(size) (FSE_NCOUNTBOUND + FSE_BLOCKBOUND(size)) /* Macro version, useful for static allocation */ |
|
288 | 288 | |
|
289 |
/* It is possible to statically allocate FSE CTable/DTable as a table of |
|
|
289 | /* It is possible to statically allocate FSE CTable/DTable as a table of FSE_CTable/FSE_DTable using below macros */ | |
|
290 | 290 | #define FSE_CTABLE_SIZE_U32(maxTableLog, maxSymbolValue) (1 + (1<<(maxTableLog-1)) + ((maxSymbolValue+1)*2)) |
|
291 | 291 | #define FSE_DTABLE_SIZE_U32(maxTableLog) (1 + (1<<maxTableLog)) |
|
292 | 292 | |
|
293 | 293 | |
|
294 | 294 | /* ***************************************** |
|
295 | 295 | * FSE advanced API |
|
296 | 296 | *******************************************/ |
|
297 | /* FSE_count_wksp() : | |
|
298 | * Same as FSE_count(), but using an externally provided scratch buffer. | |
|
299 | * `workSpace` size must be table of >= `1024` unsigned | |
|
300 | */ | |
|
301 | size_t FSE_count_wksp(unsigned* count, unsigned* maxSymbolValuePtr, | |
|
302 | const void* source, size_t sourceSize, unsigned* workSpace); | |
|
303 | ||
|
304 | /** FSE_countFast() : | |
|
305 | * same as FSE_count(), but blindly trusts that all byte values within src are <= *maxSymbolValuePtr | |
|
306 | */ | |
|
297 | 307 | size_t FSE_countFast(unsigned* count, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize); |
|
298 | /**< same as FSE_count(), but blindly trusts that all byte values within src are <= *maxSymbolValuePtr */ | |
|
308 | ||
|
309 | /* FSE_countFast_wksp() : | |
|
310 | * Same as FSE_countFast(), but using an externally provided scratch buffer. | |
|
311 | * `workSpace` must be a table of minimum `1024` unsigned | |
|
312 | */ | |
|
313 | size_t FSE_countFast_wksp(unsigned* count, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize, unsigned* workSpace); | |
|
314 | ||
|
315 | /*! FSE_count_simple | |
|
316 | * Same as FSE_countFast(), but does not use any additional memory (not even on stack). | |
|
317 | * This function is unsafe, and will segfault if any value within `src` is `> *maxSymbolValuePtr` (presuming it's also the size of `count`). | |
|
318 | */ | |
|
319 | size_t FSE_count_simple(unsigned* count, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize); | |
|
320 | ||
|
321 | ||
|
299 | 322 | |
|
300 | 323 | unsigned FSE_optimalTableLog_internal(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue, unsigned minus); |
|
301 | 324 | /**< same as FSE_optimalTableLog(), which used `minus==2` */ |
|
302 | 325 | |
|
326 | /* FSE_compress_wksp() : | |
|
327 | * Same as FSE_compress2(), but using an externally allocated scratch buffer (`workSpace`). | |
|
328 | * FSE_WKSP_SIZE_U32() provides the minimum size required for `workSpace` as a table of FSE_CTable. | |
|
329 | */ | |
|
330 | #define FSE_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) ( FSE_CTABLE_SIZE_U32(maxTableLog, maxSymbolValue) + (1<<((maxTableLog>2)?(maxTableLog-2):0)) ) | |
|
331 | size_t FSE_compress_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize); | |
|
332 | ||
|
303 | 333 | size_t FSE_buildCTable_raw (FSE_CTable* ct, unsigned nbBits); |
|
304 |
/**< build a fake FSE_CTable, designed |
|
|
334 | /**< build a fake FSE_CTable, designed for a flat distribution, where each symbol uses nbBits */ | |
|
305 | 335 | |
|
306 | 336 | size_t FSE_buildCTable_rle (FSE_CTable* ct, unsigned char symbolValue); |
|
307 | 337 | /**< build a fake FSE_CTable, designed to compress always the same symbolValue */ |
|
308 | 338 | |
|
339 | /* FSE_buildCTable_wksp() : | |
|
340 | * Same as FSE_buildCTable(), but using an externally allocated scratch buffer (`workSpace`). | |
|
341 | * `wkspSize` must be >= `(1<<tableLog)`. | |
|
342 | */ | |
|
343 | size_t FSE_buildCTable_wksp(FSE_CTable* ct, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize); | |
|
344 | ||
|
309 | 345 | size_t FSE_buildDTable_raw (FSE_DTable* dt, unsigned nbBits); |
|
310 |
/**< build a fake FSE_DTable, designed to read a |
|
|
346 | /**< build a fake FSE_DTable, designed to read a flat distribution where each symbol uses nbBits */ | |
|
311 | 347 | |
|
312 | 348 | size_t FSE_buildDTable_rle (FSE_DTable* dt, unsigned char symbolValue); |
|
313 | 349 | /**< build a fake FSE_DTable, designed to always generate the same symbolValue */ |
|
314 | 350 | |
|
351 | size_t FSE_decompress_wksp(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, FSE_DTable* workSpace, unsigned maxLog); | |
|
352 | /**< same as FSE_decompress(), using an externally allocated `workSpace` produced with `FSE_DTABLE_SIZE_U32(maxLog)` */ | |
|
353 | ||
|
315 | 354 | |
|
316 | 355 | /* ***************************************** |
|
317 | 356 | * FSE symbol compression API |
|
318 | 357 | *******************************************/ |
|
319 | 358 | /*! |
|
320 | 359 | This API consists of small unitary functions, which highly benefit from being inlined. |
|
321 | You will want to enable link-time-optimization to ensure these functions are properly inlined in your binary. | |
|
322 | Visual seems to do it automatically. | |
|
323 | For gcc or clang, you'll need to add -flto flag at compilation and linking stages. | |
|
324 | If none of these solutions is applicable, include "fse.c" directly. | |
|
360 | Hence their body are included in next section. | |
|
325 | 361 | */ |
|
326 | typedef struct | |
|
327 | { | |
|
362 | typedef struct { | |
|
328 | 363 | ptrdiff_t value; |
|
329 | 364 | const void* stateTable; |
|
330 | 365 | const void* symbolTT; |
|
331 | 366 | unsigned stateLog; |
|
332 | 367 | } FSE_CState_t; |
|
333 | 368 | |
|
334 | 369 | static void FSE_initCState(FSE_CState_t* CStatePtr, const FSE_CTable* ct); |
|
335 | 370 | |
|
336 | 371 | static void FSE_encodeSymbol(BIT_CStream_t* bitC, FSE_CState_t* CStatePtr, unsigned symbol); |
|
337 | 372 | |
|
338 | 373 | static void FSE_flushCState(BIT_CStream_t* bitC, const FSE_CState_t* CStatePtr); |
|
339 | 374 | |
|
340 | 375 | /**< |
|
341 | 376 | These functions are inner components of FSE_compress_usingCTable(). |
|
342 | 377 | They allow the creation of custom streams, mixing multiple tables and bit sources. |
|
343 | 378 | |
|
344 | 379 | A key property to keep in mind is that encoding and decoding are done **in reverse direction**. |
|
345 | 380 | So the first symbol you will encode is the last you will decode, like a LIFO stack. |
|
346 | 381 | |
|
347 | 382 | You will need a few variables to track your CStream. They are : |
|
348 | 383 | |
|
349 | 384 | FSE_CTable ct; // Provided by FSE_buildCTable() |
|
350 | 385 | BIT_CStream_t bitStream; // bitStream tracking structure |
|
351 | 386 | FSE_CState_t state; // State tracking structure (can have several) |
|
352 | 387 | |
|
353 | 388 | |
|
354 | 389 | The first thing to do is to init bitStream and state. |
|
355 | 390 | size_t errorCode = BIT_initCStream(&bitStream, dstBuffer, maxDstSize); |
|
356 | 391 | FSE_initCState(&state, ct); |
|
357 | 392 | |
|
358 | 393 | Note that BIT_initCStream() can produce an error code, so its result should be tested, using FSE_isError(); |
|
359 | 394 | You can then encode your input data, byte after byte. |
|
360 | 395 | FSE_encodeSymbol() outputs a maximum of 'tableLog' bits at a time. |
|
361 | 396 | Remember decoding will be done in reverse direction. |
|
362 | 397 | FSE_encodeByte(&bitStream, &state, symbol); |
|
363 | 398 | |
|
364 | 399 | At any time, you can also add any bit sequence. |
|
365 | 400 | Note : maximum allowed nbBits is 25, for compatibility with 32-bits decoders |
|
366 | 401 | BIT_addBits(&bitStream, bitField, nbBits); |
|
367 | 402 | |
|
368 | 403 | The above methods don't commit data to memory, they just store it into local register, for speed. |
|
369 | 404 | Local register size is 64-bits on 64-bits systems, 32-bits on 32-bits systems (size_t). |
|
370 | 405 | Writing data to memory is a manual operation, performed by the flushBits function. |
|
371 | 406 | BIT_flushBits(&bitStream); |
|
372 | 407 | |
|
373 | 408 | Your last FSE encoding operation shall be to flush your last state value(s). |
|
374 | 409 | FSE_flushState(&bitStream, &state); |
|
375 | 410 | |
|
376 | 411 | Finally, you must close the bitStream. |
|
377 | 412 | The function returns the size of CStream in bytes. |
|
378 | 413 | If data couldn't fit into dstBuffer, it will return a 0 ( == not compressible) |
|
379 | 414 | If there is an error, it returns an errorCode (which can be tested using FSE_isError()). |
|
380 | 415 | size_t size = BIT_closeCStream(&bitStream); |
|
381 | 416 | */ |
|
382 | 417 | |
|
383 | 418 | |
|
384 | 419 | /* ***************************************** |
|
385 | 420 | * FSE symbol decompression API |
|
386 | 421 | *******************************************/ |
|
387 | typedef struct | |
|
388 | { | |
|
422 | typedef struct { | |
|
389 | 423 | size_t state; |
|
390 | 424 | const void* table; /* precise table may vary, depending on U16 */ |
|
391 | 425 | } FSE_DState_t; |
|
392 | 426 | |
|
393 | 427 | |
|
394 | 428 | static void FSE_initDState(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD, const FSE_DTable* dt); |
|
395 | 429 | |
|
396 | 430 | static unsigned char FSE_decodeSymbol(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD); |
|
397 | 431 | |
|
398 | 432 | static unsigned FSE_endOfDState(const FSE_DState_t* DStatePtr); |
|
399 | 433 | |
|
400 | 434 | /**< |
|
401 | 435 | Let's now decompose FSE_decompress_usingDTable() into its unitary components. |
|
402 | 436 | You will decode FSE-encoded symbols from the bitStream, |
|
403 | 437 | and also any other bitFields you put in, **in reverse order**. |
|
404 | 438 | |
|
405 | 439 | You will need a few variables to track your bitStream. They are : |
|
406 | 440 | |
|
407 | 441 | BIT_DStream_t DStream; // Stream context |
|
408 | 442 | FSE_DState_t DState; // State context. Multiple ones are possible |
|
409 | 443 | FSE_DTable* DTablePtr; // Decoding table, provided by FSE_buildDTable() |
|
410 | 444 | |
|
411 | 445 | The first thing to do is to init the bitStream. |
|
412 | 446 | errorCode = BIT_initDStream(&DStream, srcBuffer, srcSize); |
|
413 | 447 | |
|
414 | 448 | You should then retrieve your initial state(s) |
|
415 | 449 | (in reverse flushing order if you have several ones) : |
|
416 | 450 | errorCode = FSE_initDState(&DState, &DStream, DTablePtr); |
|
417 | 451 | |
|
418 | 452 | You can then decode your data, symbol after symbol. |
|
419 | 453 | For information the maximum number of bits read by FSE_decodeSymbol() is 'tableLog'. |
|
420 | 454 | Keep in mind that symbols are decoded in reverse order, like a LIFO stack (last in, first out). |
|
421 | 455 | unsigned char symbol = FSE_decodeSymbol(&DState, &DStream); |
|
422 | 456 | |
|
423 | 457 | You can retrieve any bitfield you eventually stored into the bitStream (in reverse order) |
|
424 | 458 | Note : maximum allowed nbBits is 25, for 32-bits compatibility |
|
425 | 459 | size_t bitField = BIT_readBits(&DStream, nbBits); |
|
426 | 460 | |
|
427 | 461 | All above operations only read from local register (which size depends on size_t). |
|
428 | 462 | Refueling the register from memory is manually performed by the reload method. |
|
429 | 463 | endSignal = FSE_reloadDStream(&DStream); |
|
430 | 464 | |
|
431 | 465 | BIT_reloadDStream() result tells if there is still some more data to read from DStream. |
|
432 | 466 | BIT_DStream_unfinished : there is still some data left into the DStream. |
|
433 | 467 | BIT_DStream_endOfBuffer : Dstream reached end of buffer. Its container may no longer be completely filled. |
|
434 | 468 | BIT_DStream_completed : Dstream reached its exact end, corresponding in general to decompression completed. |
|
435 | 469 | BIT_DStream_tooFar : Dstream went too far. Decompression result is corrupted. |
|
436 | 470 | |
|
437 | 471 | When reaching end of buffer (BIT_DStream_endOfBuffer), progress slowly, notably if you decode multiple symbols per loop, |
|
438 | 472 | to properly detect the exact end of stream. |
|
439 | 473 | After each decoded symbol, check if DStream is fully consumed using this simple test : |
|
440 | 474 | BIT_reloadDStream(&DStream) >= BIT_DStream_completed |
|
441 | 475 | |
|
442 | 476 | When it's done, verify decompression is fully completed, by checking both DStream and the relevant states. |
|
443 | 477 | Checking if DStream has reached its end is performed by : |
|
444 | 478 | BIT_endOfDStream(&DStream); |
|
445 | 479 | Check also the states. There might be some symbols left there, if some high probability ones (>50%) are possible. |
|
446 | 480 | FSE_endOfDState(&DState); |
|
447 | 481 | */ |
|
448 | 482 | |
|
449 | 483 | |
|
450 | 484 | /* ***************************************** |
|
451 | 485 | * FSE unsafe API |
|
452 | 486 | *******************************************/ |
|
453 | 487 | static unsigned char FSE_decodeSymbolFast(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD); |
|
454 | 488 | /* faster, but works only if nbBits is always >= 1 (otherwise, result will be corrupted) */ |
|
455 | 489 | |
|
456 | 490 | |
|
457 | 491 | /* ***************************************** |
|
458 | 492 | * Implementation of inlined functions |
|
459 | 493 | *******************************************/ |
|
460 | 494 | typedef struct { |
|
461 | 495 | int deltaFindState; |
|
462 | 496 | U32 deltaNbBits; |
|
463 | 497 | } FSE_symbolCompressionTransform; /* total 8 bytes */ |
|
464 | 498 | |
|
465 | 499 | MEM_STATIC void FSE_initCState(FSE_CState_t* statePtr, const FSE_CTable* ct) |
|
466 | 500 | { |
|
467 | 501 | const void* ptr = ct; |
|
468 | 502 | const U16* u16ptr = (const U16*) ptr; |
|
469 | 503 | const U32 tableLog = MEM_read16(ptr); |
|
470 | 504 | statePtr->value = (ptrdiff_t)1<<tableLog; |
|
471 | 505 | statePtr->stateTable = u16ptr+2; |
|
472 | 506 | statePtr->symbolTT = ((const U32*)ct + 1 + (tableLog ? (1<<(tableLog-1)) : 1)); |
|
473 | 507 | statePtr->stateLog = tableLog; |
|
474 | 508 | } |
|
475 | 509 | |
|
476 | 510 | |
|
477 | 511 | /*! FSE_initCState2() : |
|
478 | 512 | * Same as FSE_initCState(), but the first symbol to include (which will be the last to be read) |
|
479 | 513 | * uses the smallest state value possible, saving the cost of this symbol */ |
|
480 | 514 | MEM_STATIC void FSE_initCState2(FSE_CState_t* statePtr, const FSE_CTable* ct, U32 symbol) |
|
481 | 515 | { |
|
482 | 516 | FSE_initCState(statePtr, ct); |
|
483 | 517 | { const FSE_symbolCompressionTransform symbolTT = ((const FSE_symbolCompressionTransform*)(statePtr->symbolTT))[symbol]; |
|
484 | 518 | const U16* stateTable = (const U16*)(statePtr->stateTable); |
|
485 | 519 | U32 nbBitsOut = (U32)((symbolTT.deltaNbBits + (1<<15)) >> 16); |
|
486 | 520 | statePtr->value = (nbBitsOut << 16) - symbolTT.deltaNbBits; |
|
487 | 521 | statePtr->value = stateTable[(statePtr->value >> nbBitsOut) + symbolTT.deltaFindState]; |
|
488 | 522 | } |
|
489 | 523 | } |
|
490 | 524 | |
|
491 | 525 | MEM_STATIC void FSE_encodeSymbol(BIT_CStream_t* bitC, FSE_CState_t* statePtr, U32 symbol) |
|
492 | 526 | { |
|
493 | 527 | const FSE_symbolCompressionTransform symbolTT = ((const FSE_symbolCompressionTransform*)(statePtr->symbolTT))[symbol]; |
|
494 | 528 | const U16* const stateTable = (const U16*)(statePtr->stateTable); |
|
495 | 529 | U32 nbBitsOut = (U32)((statePtr->value + symbolTT.deltaNbBits) >> 16); |
|
496 | 530 | BIT_addBits(bitC, statePtr->value, nbBitsOut); |
|
497 | 531 | statePtr->value = stateTable[ (statePtr->value >> nbBitsOut) + symbolTT.deltaFindState]; |
|
498 | 532 | } |
|
499 | 533 | |
|
500 | 534 | MEM_STATIC void FSE_flushCState(BIT_CStream_t* bitC, const FSE_CState_t* statePtr) |
|
501 | 535 | { |
|
502 | 536 | BIT_addBits(bitC, statePtr->value, statePtr->stateLog); |
|
503 | 537 | BIT_flushBits(bitC); |
|
504 | 538 | } |
|
505 | 539 | |
|
506 | 540 | |
|
507 | 541 | /* ====== Decompression ====== */ |
|
508 | 542 | |
|
509 | 543 | typedef struct { |
|
510 | 544 | U16 tableLog; |
|
511 | 545 | U16 fastMode; |
|
512 | 546 | } FSE_DTableHeader; /* sizeof U32 */ |
|
513 | 547 | |
|
514 | 548 | typedef struct |
|
515 | 549 | { |
|
516 | 550 | unsigned short newState; |
|
517 | 551 | unsigned char symbol; |
|
518 | 552 | unsigned char nbBits; |
|
519 | 553 | } FSE_decode_t; /* size == U32 */ |
|
520 | 554 | |
|
521 | 555 | MEM_STATIC void FSE_initDState(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD, const FSE_DTable* dt) |
|
522 | 556 | { |
|
523 | 557 | const void* ptr = dt; |
|
524 | 558 | const FSE_DTableHeader* const DTableH = (const FSE_DTableHeader*)ptr; |
|
525 | 559 | DStatePtr->state = BIT_readBits(bitD, DTableH->tableLog); |
|
526 | 560 | BIT_reloadDStream(bitD); |
|
527 | 561 | DStatePtr->table = dt + 1; |
|
528 | 562 | } |
|
529 | 563 | |
|
530 | 564 | MEM_STATIC BYTE FSE_peekSymbol(const FSE_DState_t* DStatePtr) |
|
531 | 565 | { |
|
532 | 566 | FSE_decode_t const DInfo = ((const FSE_decode_t*)(DStatePtr->table))[DStatePtr->state]; |
|
533 | 567 | return DInfo.symbol; |
|
534 | 568 | } |
|
535 | 569 | |
|
536 | 570 | MEM_STATIC void FSE_updateState(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD) |
|
537 | 571 | { |
|
538 | 572 | FSE_decode_t const DInfo = ((const FSE_decode_t*)(DStatePtr->table))[DStatePtr->state]; |
|
539 | 573 | U32 const nbBits = DInfo.nbBits; |
|
540 | 574 | size_t const lowBits = BIT_readBits(bitD, nbBits); |
|
541 | 575 | DStatePtr->state = DInfo.newState + lowBits; |
|
542 | 576 | } |
|
543 | 577 | |
|
544 | 578 | MEM_STATIC BYTE FSE_decodeSymbol(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD) |
|
545 | 579 | { |
|
546 | 580 | FSE_decode_t const DInfo = ((const FSE_decode_t*)(DStatePtr->table))[DStatePtr->state]; |
|
547 | 581 | U32 const nbBits = DInfo.nbBits; |
|
548 | 582 | BYTE const symbol = DInfo.symbol; |
|
549 | 583 | size_t const lowBits = BIT_readBits(bitD, nbBits); |
|
550 | 584 | |
|
551 | 585 | DStatePtr->state = DInfo.newState + lowBits; |
|
552 | 586 | return symbol; |
|
553 | 587 | } |
|
554 | 588 | |
|
555 | 589 | /*! FSE_decodeSymbolFast() : |
|
556 | 590 | unsafe, only works if no symbol has a probability > 50% */ |
|
557 | 591 | MEM_STATIC BYTE FSE_decodeSymbolFast(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD) |
|
558 | 592 | { |
|
559 | 593 | FSE_decode_t const DInfo = ((const FSE_decode_t*)(DStatePtr->table))[DStatePtr->state]; |
|
560 | 594 | U32 const nbBits = DInfo.nbBits; |
|
561 | 595 | BYTE const symbol = DInfo.symbol; |
|
562 | 596 | size_t const lowBits = BIT_readBitsFast(bitD, nbBits); |
|
563 | 597 | |
|
564 | 598 | DStatePtr->state = DInfo.newState + lowBits; |
|
565 | 599 | return symbol; |
|
566 | 600 | } |
|
567 | 601 | |
|
568 | 602 | MEM_STATIC unsigned FSE_endOfDState(const FSE_DState_t* DStatePtr) |
|
569 | 603 | { |
|
570 | 604 | return DStatePtr->state == 0; |
|
571 | 605 | } |
|
572 | 606 | |
|
573 | 607 | |
|
574 | 608 | |
|
575 | 609 | #ifndef FSE_COMMONDEFS_ONLY |
|
576 | 610 | |
|
577 | 611 | /* ************************************************************** |
|
578 | 612 | * Tuning parameters |
|
579 | 613 | ****************************************************************/ |
|
580 | 614 | /*!MEMORY_USAGE : |
|
581 | 615 | * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) |
|
582 | 616 | * Increasing memory usage improves compression ratio |
|
583 | 617 | * Reduced memory usage can improve speed, due to cache effect |
|
584 | 618 | * Recommended max value is 14, for 16KB, which nicely fits into Intel x86 L1 cache */ |
|
585 | 619 | #ifndef FSE_MAX_MEMORY_USAGE |
|
586 | 620 | # define FSE_MAX_MEMORY_USAGE 14 |
|
587 | 621 | #endif |
|
588 | 622 | #ifndef FSE_DEFAULT_MEMORY_USAGE |
|
589 | 623 | # define FSE_DEFAULT_MEMORY_USAGE 13 |
|
590 | 624 | #endif |
|
591 | 625 | |
|
592 | 626 | /*!FSE_MAX_SYMBOL_VALUE : |
|
593 | 627 | * Maximum symbol value authorized. |
|
594 | 628 | * Required for proper stack allocation */ |
|
595 | 629 | #ifndef FSE_MAX_SYMBOL_VALUE |
|
596 | 630 | # define FSE_MAX_SYMBOL_VALUE 255 |
|
597 | 631 | #endif |
|
598 | 632 | |
|
599 | 633 | /* ************************************************************** |
|
600 | 634 | * template functions type & suffix |
|
601 | 635 | ****************************************************************/ |
|
602 | 636 | #define FSE_FUNCTION_TYPE BYTE |
|
603 | 637 | #define FSE_FUNCTION_EXTENSION |
|
604 | 638 | #define FSE_DECODE_TYPE FSE_decode_t |
|
605 | 639 | |
|
606 | 640 | |
|
607 | 641 | #endif /* !FSE_COMMONDEFS_ONLY */ |
|
608 | 642 | |
|
609 | 643 | |
|
610 | 644 | /* *************************************************************** |
|
611 | 645 | * Constants |
|
612 | 646 | *****************************************************************/ |
|
613 | 647 | #define FSE_MAX_TABLELOG (FSE_MAX_MEMORY_USAGE-2) |
|
614 | 648 | #define FSE_MAX_TABLESIZE (1U<<FSE_MAX_TABLELOG) |
|
615 | 649 | #define FSE_MAXTABLESIZE_MASK (FSE_MAX_TABLESIZE-1) |
|
616 | 650 | #define FSE_DEFAULT_TABLELOG (FSE_DEFAULT_MEMORY_USAGE-2) |
|
617 | 651 | #define FSE_MIN_TABLELOG 5 |
|
618 | 652 | |
|
619 | 653 | #define FSE_TABLELOG_ABSOLUTE_MAX 15 |
|
620 | 654 | #if FSE_MAX_TABLELOG > FSE_TABLELOG_ABSOLUTE_MAX |
|
621 | 655 | # error "FSE_MAX_TABLELOG > FSE_TABLELOG_ABSOLUTE_MAX is not supported" |
|
622 | 656 | #endif |
|
623 | 657 | |
|
624 | 658 | #define FSE_TABLESTEP(tableSize) ((tableSize>>1) + (tableSize>>3) + 3) |
|
625 | 659 | |
|
626 | 660 | |
|
627 | 661 | #endif /* FSE_STATIC_LINKING_ONLY */ |
|
628 | 662 | |
|
629 | 663 | |
|
630 | 664 | #if defined (__cplusplus) |
|
631 | 665 | } |
|
632 | 666 | #endif |
|
633 | 667 | |
|
634 | 668 | #endif /* FSE_H */ |
@@ -1,329 +1,329 b'' | |||
|
1 | 1 | /* ****************************************************************** |
|
2 | 2 | FSE : Finite State Entropy decoder |
|
3 | 3 | Copyright (C) 2013-2015, Yann Collet. |
|
4 | 4 | |
|
5 | 5 | BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) |
|
6 | 6 | |
|
7 | 7 | Redistribution and use in source and binary forms, with or without |
|
8 | 8 | modification, are permitted provided that the following conditions are |
|
9 | 9 | met: |
|
10 | 10 | |
|
11 | 11 | * Redistributions of source code must retain the above copyright |
|
12 | 12 | notice, this list of conditions and the following disclaimer. |
|
13 | 13 | * Redistributions in binary form must reproduce the above |
|
14 | 14 | copyright notice, this list of conditions and the following disclaimer |
|
15 | 15 | in the documentation and/or other materials provided with the |
|
16 | 16 | distribution. |
|
17 | 17 | |
|
18 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
19 | 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
20 | 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
21 | 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
22 | 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
23 | 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
24 | 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
25 | 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
26 | 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
27 | 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
28 | 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
29 | 29 | |
|
30 | 30 | You can contact the author at : |
|
31 | 31 | - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy |
|
32 | 32 | - Public forum : https://groups.google.com/forum/#!forum/lz4c |
|
33 | 33 | ****************************************************************** */ |
|
34 | 34 | |
|
35 | 35 | |
|
36 | 36 | /* ************************************************************** |
|
37 | 37 | * Compiler specifics |
|
38 | 38 | ****************************************************************/ |
|
39 | 39 | #ifdef _MSC_VER /* Visual Studio */ |
|
40 | 40 | # define FORCE_INLINE static __forceinline |
|
41 | 41 | # include <intrin.h> /* For Visual 2005 */ |
|
42 | 42 | # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ |
|
43 | 43 | # pragma warning(disable : 4214) /* disable: C4214: non-int bitfields */ |
|
44 | 44 | #else |
|
45 | 45 | # if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ |
|
46 | 46 | # ifdef __GNUC__ |
|
47 | 47 | # define FORCE_INLINE static inline __attribute__((always_inline)) |
|
48 | 48 | # else |
|
49 | 49 | # define FORCE_INLINE static inline |
|
50 | 50 | # endif |
|
51 | 51 | # else |
|
52 | 52 | # define FORCE_INLINE static |
|
53 | 53 | # endif /* __STDC_VERSION__ */ |
|
54 | 54 | #endif |
|
55 | 55 | |
|
56 | 56 | |
|
57 | 57 | /* ************************************************************** |
|
58 | 58 | * Includes |
|
59 | 59 | ****************************************************************/ |
|
60 | 60 | #include <stdlib.h> /* malloc, free, qsort */ |
|
61 | 61 | #include <string.h> /* memcpy, memset */ |
|
62 | 62 | #include <stdio.h> /* printf (debug) */ |
|
63 | 63 | #include "bitstream.h" |
|
64 | 64 | #define FSE_STATIC_LINKING_ONLY |
|
65 | 65 | #include "fse.h" |
|
66 | 66 | |
|
67 | 67 | |
|
68 | 68 | /* ************************************************************** |
|
69 | 69 | * Error Management |
|
70 | 70 | ****************************************************************/ |
|
71 | 71 | #define FSE_isError ERR_isError |
|
72 | 72 | #define FSE_STATIC_ASSERT(c) { enum { FSE_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */ |
|
73 | 73 | |
|
74 | 74 | /* check and forward error code */ |
|
75 | 75 | #define CHECK_F(f) { size_t const e = f; if (FSE_isError(e)) return e; } |
|
76 | 76 | |
|
77 | 77 | |
|
78 | 78 | /* ************************************************************** |
|
79 | * Complex types | |
|
80 | ****************************************************************/ | |
|
81 | typedef U32 DTable_max_t[FSE_DTABLE_SIZE_U32(FSE_MAX_TABLELOG)]; | |
|
82 | ||
|
83 | ||
|
84 | /* ************************************************************** | |
|
85 | 79 | * Templates |
|
86 | 80 | ****************************************************************/ |
|
87 | 81 | /* |
|
88 | 82 | designed to be included |
|
89 | 83 | for type-specific functions (template emulation in C) |
|
90 | 84 | Objective is to write these functions only once, for improved maintenance |
|
91 | 85 | */ |
|
92 | 86 | |
|
93 | 87 | /* safety checks */ |
|
94 | 88 | #ifndef FSE_FUNCTION_EXTENSION |
|
95 | 89 | # error "FSE_FUNCTION_EXTENSION must be defined" |
|
96 | 90 | #endif |
|
97 | 91 | #ifndef FSE_FUNCTION_TYPE |
|
98 | 92 | # error "FSE_FUNCTION_TYPE must be defined" |
|
99 | 93 | #endif |
|
100 | 94 | |
|
101 | 95 | /* Function names */ |
|
102 | 96 | #define FSE_CAT(X,Y) X##Y |
|
103 | 97 | #define FSE_FUNCTION_NAME(X,Y) FSE_CAT(X,Y) |
|
104 | 98 | #define FSE_TYPE_NAME(X,Y) FSE_CAT(X,Y) |
|
105 | 99 | |
|
106 | 100 | |
|
107 | 101 | /* Function templates */ |
|
108 | 102 | FSE_DTable* FSE_createDTable (unsigned tableLog) |
|
109 | 103 | { |
|
110 | 104 | if (tableLog > FSE_TABLELOG_ABSOLUTE_MAX) tableLog = FSE_TABLELOG_ABSOLUTE_MAX; |
|
111 | 105 | return (FSE_DTable*)malloc( FSE_DTABLE_SIZE_U32(tableLog) * sizeof (U32) ); |
|
112 | 106 | } |
|
113 | 107 | |
|
114 | 108 | void FSE_freeDTable (FSE_DTable* dt) |
|
115 | 109 | { |
|
116 | 110 | free(dt); |
|
117 | 111 | } |
|
118 | 112 | |
|
119 | 113 | size_t FSE_buildDTable(FSE_DTable* dt, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog) |
|
120 | 114 | { |
|
121 | 115 | void* const tdPtr = dt+1; /* because *dt is unsigned, 32-bits aligned on 32-bits */ |
|
122 | 116 | FSE_DECODE_TYPE* const tableDecode = (FSE_DECODE_TYPE*) (tdPtr); |
|
123 | 117 | U16 symbolNext[FSE_MAX_SYMBOL_VALUE+1]; |
|
124 | 118 | |
|
125 | 119 | U32 const maxSV1 = maxSymbolValue + 1; |
|
126 | 120 | U32 const tableSize = 1 << tableLog; |
|
127 | 121 | U32 highThreshold = tableSize-1; |
|
128 | 122 | |
|
129 | 123 | /* Sanity Checks */ |
|
130 | 124 | if (maxSymbolValue > FSE_MAX_SYMBOL_VALUE) return ERROR(maxSymbolValue_tooLarge); |
|
131 | 125 | if (tableLog > FSE_MAX_TABLELOG) return ERROR(tableLog_tooLarge); |
|
132 | 126 | |
|
133 | 127 | /* Init, lay down lowprob symbols */ |
|
134 | 128 | { FSE_DTableHeader DTableH; |
|
135 | 129 | DTableH.tableLog = (U16)tableLog; |
|
136 | 130 | DTableH.fastMode = 1; |
|
137 | 131 | { S16 const largeLimit= (S16)(1 << (tableLog-1)); |
|
138 | 132 | U32 s; |
|
139 | 133 | for (s=0; s<maxSV1; s++) { |
|
140 | 134 | if (normalizedCounter[s]==-1) { |
|
141 | 135 | tableDecode[highThreshold--].symbol = (FSE_FUNCTION_TYPE)s; |
|
142 | 136 | symbolNext[s] = 1; |
|
143 | 137 | } else { |
|
144 | 138 | if (normalizedCounter[s] >= largeLimit) DTableH.fastMode=0; |
|
145 | 139 | symbolNext[s] = normalizedCounter[s]; |
|
146 | 140 | } } } |
|
147 | 141 | memcpy(dt, &DTableH, sizeof(DTableH)); |
|
148 | 142 | } |
|
149 | 143 | |
|
150 | 144 | /* Spread symbols */ |
|
151 | 145 | { U32 const tableMask = tableSize-1; |
|
152 | 146 | U32 const step = FSE_TABLESTEP(tableSize); |
|
153 | 147 | U32 s, position = 0; |
|
154 | 148 | for (s=0; s<maxSV1; s++) { |
|
155 | 149 | int i; |
|
156 | 150 | for (i=0; i<normalizedCounter[s]; i++) { |
|
157 | 151 | tableDecode[position].symbol = (FSE_FUNCTION_TYPE)s; |
|
158 | 152 | position = (position + step) & tableMask; |
|
159 | 153 | while (position > highThreshold) position = (position + step) & tableMask; /* lowprob area */ |
|
160 | 154 | } } |
|
161 | 155 | if (position!=0) return ERROR(GENERIC); /* position must reach all cells once, otherwise normalizedCounter is incorrect */ |
|
162 | 156 | } |
|
163 | 157 | |
|
164 | 158 | /* Build Decoding table */ |
|
165 | 159 | { U32 u; |
|
166 | 160 | for (u=0; u<tableSize; u++) { |
|
167 | 161 | FSE_FUNCTION_TYPE const symbol = (FSE_FUNCTION_TYPE)(tableDecode[u].symbol); |
|
168 | 162 | U16 nextState = symbolNext[symbol]++; |
|
169 | 163 | tableDecode[u].nbBits = (BYTE) (tableLog - BIT_highbit32 ((U32)nextState) ); |
|
170 | 164 | tableDecode[u].newState = (U16) ( (nextState << tableDecode[u].nbBits) - tableSize); |
|
171 | 165 | } } |
|
172 | 166 | |
|
173 | 167 | return 0; |
|
174 | 168 | } |
|
175 | 169 | |
|
176 | 170 | |
|
177 | 171 | #ifndef FSE_COMMONDEFS_ONLY |
|
178 | 172 | |
|
179 | 173 | /*-******************************************************* |
|
180 | 174 | * Decompression (Byte symbols) |
|
181 | 175 | *********************************************************/ |
|
182 | 176 | size_t FSE_buildDTable_rle (FSE_DTable* dt, BYTE symbolValue) |
|
183 | 177 | { |
|
184 | 178 | void* ptr = dt; |
|
185 | 179 | FSE_DTableHeader* const DTableH = (FSE_DTableHeader*)ptr; |
|
186 | 180 | void* dPtr = dt + 1; |
|
187 | 181 | FSE_decode_t* const cell = (FSE_decode_t*)dPtr; |
|
188 | 182 | |
|
189 | 183 | DTableH->tableLog = 0; |
|
190 | 184 | DTableH->fastMode = 0; |
|
191 | 185 | |
|
192 | 186 | cell->newState = 0; |
|
193 | 187 | cell->symbol = symbolValue; |
|
194 | 188 | cell->nbBits = 0; |
|
195 | 189 | |
|
196 | 190 | return 0; |
|
197 | 191 | } |
|
198 | 192 | |
|
199 | 193 | |
|
200 | 194 | size_t FSE_buildDTable_raw (FSE_DTable* dt, unsigned nbBits) |
|
201 | 195 | { |
|
202 | 196 | void* ptr = dt; |
|
203 | 197 | FSE_DTableHeader* const DTableH = (FSE_DTableHeader*)ptr; |
|
204 | 198 | void* dPtr = dt + 1; |
|
205 | 199 | FSE_decode_t* const dinfo = (FSE_decode_t*)dPtr; |
|
206 | 200 | const unsigned tableSize = 1 << nbBits; |
|
207 | 201 | const unsigned tableMask = tableSize - 1; |
|
208 | 202 | const unsigned maxSV1 = tableMask+1; |
|
209 | 203 | unsigned s; |
|
210 | 204 | |
|
211 | 205 | /* Sanity checks */ |
|
212 | 206 | if (nbBits < 1) return ERROR(GENERIC); /* min size */ |
|
213 | 207 | |
|
214 | 208 | /* Build Decoding Table */ |
|
215 | 209 | DTableH->tableLog = (U16)nbBits; |
|
216 | 210 | DTableH->fastMode = 1; |
|
217 | 211 | for (s=0; s<maxSV1; s++) { |
|
218 | 212 | dinfo[s].newState = 0; |
|
219 | 213 | dinfo[s].symbol = (BYTE)s; |
|
220 | 214 | dinfo[s].nbBits = (BYTE)nbBits; |
|
221 | 215 | } |
|
222 | 216 | |
|
223 | 217 | return 0; |
|
224 | 218 | } |
|
225 | 219 | |
|
226 | 220 | FORCE_INLINE size_t FSE_decompress_usingDTable_generic( |
|
227 | 221 | void* dst, size_t maxDstSize, |
|
228 | 222 | const void* cSrc, size_t cSrcSize, |
|
229 | 223 | const FSE_DTable* dt, const unsigned fast) |
|
230 | 224 | { |
|
231 | 225 | BYTE* const ostart = (BYTE*) dst; |
|
232 | 226 | BYTE* op = ostart; |
|
233 | 227 | BYTE* const omax = op + maxDstSize; |
|
234 | 228 | BYTE* const olimit = omax-3; |
|
235 | 229 | |
|
236 | 230 | BIT_DStream_t bitD; |
|
237 | 231 | FSE_DState_t state1; |
|
238 | 232 | FSE_DState_t state2; |
|
239 | 233 | |
|
240 | 234 | /* Init */ |
|
241 | 235 | CHECK_F(BIT_initDStream(&bitD, cSrc, cSrcSize)); |
|
242 | 236 | |
|
243 | 237 | FSE_initDState(&state1, &bitD, dt); |
|
244 | 238 | FSE_initDState(&state2, &bitD, dt); |
|
245 | 239 | |
|
246 | 240 | #define FSE_GETSYMBOL(statePtr) fast ? FSE_decodeSymbolFast(statePtr, &bitD) : FSE_decodeSymbol(statePtr, &bitD) |
|
247 | 241 | |
|
248 | 242 | /* 4 symbols per loop */ |
|
249 | 243 | for ( ; (BIT_reloadDStream(&bitD)==BIT_DStream_unfinished) & (op<olimit) ; op+=4) { |
|
250 | 244 | op[0] = FSE_GETSYMBOL(&state1); |
|
251 | 245 | |
|
252 | 246 | if (FSE_MAX_TABLELOG*2+7 > sizeof(bitD.bitContainer)*8) /* This test must be static */ |
|
253 | 247 | BIT_reloadDStream(&bitD); |
|
254 | 248 | |
|
255 | 249 | op[1] = FSE_GETSYMBOL(&state2); |
|
256 | 250 | |
|
257 | 251 | if (FSE_MAX_TABLELOG*4+7 > sizeof(bitD.bitContainer)*8) /* This test must be static */ |
|
258 | 252 | { if (BIT_reloadDStream(&bitD) > BIT_DStream_unfinished) { op+=2; break; } } |
|
259 | 253 | |
|
260 | 254 | op[2] = FSE_GETSYMBOL(&state1); |
|
261 | 255 | |
|
262 | 256 | if (FSE_MAX_TABLELOG*2+7 > sizeof(bitD.bitContainer)*8) /* This test must be static */ |
|
263 | 257 | BIT_reloadDStream(&bitD); |
|
264 | 258 | |
|
265 | 259 | op[3] = FSE_GETSYMBOL(&state2); |
|
266 | 260 | } |
|
267 | 261 | |
|
268 | 262 | /* tail */ |
|
269 | 263 | /* note : BIT_reloadDStream(&bitD) >= FSE_DStream_partiallyFilled; Ends at exactly BIT_DStream_completed */ |
|
270 | 264 | while (1) { |
|
271 | 265 | if (op>(omax-2)) return ERROR(dstSize_tooSmall); |
|
272 | 266 | *op++ = FSE_GETSYMBOL(&state1); |
|
273 | 267 | if (BIT_reloadDStream(&bitD)==BIT_DStream_overflow) { |
|
274 | 268 | *op++ = FSE_GETSYMBOL(&state2); |
|
275 | 269 | break; |
|
276 | 270 | } |
|
277 | 271 | |
|
278 | 272 | if (op>(omax-2)) return ERROR(dstSize_tooSmall); |
|
279 | 273 | *op++ = FSE_GETSYMBOL(&state2); |
|
280 | 274 | if (BIT_reloadDStream(&bitD)==BIT_DStream_overflow) { |
|
281 | 275 | *op++ = FSE_GETSYMBOL(&state1); |
|
282 | 276 | break; |
|
283 | 277 | } } |
|
284 | 278 | |
|
285 | 279 | return op-ostart; |
|
286 | 280 | } |
|
287 | 281 | |
|
288 | 282 | |
|
289 | 283 | size_t FSE_decompress_usingDTable(void* dst, size_t originalSize, |
|
290 | 284 | const void* cSrc, size_t cSrcSize, |
|
291 | 285 | const FSE_DTable* dt) |
|
292 | 286 | { |
|
293 | 287 | const void* ptr = dt; |
|
294 | 288 | const FSE_DTableHeader* DTableH = (const FSE_DTableHeader*)ptr; |
|
295 | 289 | const U32 fastMode = DTableH->fastMode; |
|
296 | 290 | |
|
297 | 291 | /* select fast mode (static) */ |
|
298 | 292 | if (fastMode) return FSE_decompress_usingDTable_generic(dst, originalSize, cSrc, cSrcSize, dt, 1); |
|
299 | 293 | return FSE_decompress_usingDTable_generic(dst, originalSize, cSrc, cSrcSize, dt, 0); |
|
300 | 294 | } |
|
301 | 295 | |
|
302 | 296 | |
|
303 |
size_t FSE_decompress(void* dst, size_t |
|
|
297 | size_t FSE_decompress_wksp(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, FSE_DTable* workSpace, unsigned maxLog) | |
|
304 | 298 | { |
|
305 | 299 | const BYTE* const istart = (const BYTE*)cSrc; |
|
306 | 300 | const BYTE* ip = istart; |
|
307 | 301 | short counting[FSE_MAX_SYMBOL_VALUE+1]; |
|
308 | DTable_max_t dt; /* Static analyzer seems unable to understand this table will be properly initialized later */ | |
|
309 | 302 | unsigned tableLog; |
|
310 | 303 | unsigned maxSymbolValue = FSE_MAX_SYMBOL_VALUE; |
|
311 | 304 | |
|
312 | if (cSrcSize<2) return ERROR(srcSize_wrong); /* too small input size */ | |
|
305 | /* normal FSE decoding mode */ | |
|
306 | size_t const NCountLength = FSE_readNCount (counting, &maxSymbolValue, &tableLog, istart, cSrcSize); | |
|
307 | if (FSE_isError(NCountLength)) return NCountLength; | |
|
308 | //if (NCountLength >= cSrcSize) return ERROR(srcSize_wrong); /* too small input size; supposed to be already checked in NCountLength, only remaining case : NCountLength==cSrcSize */ | |
|
309 | if (tableLog > maxLog) return ERROR(tableLog_tooLarge); | |
|
310 | ip += NCountLength; | |
|
311 | cSrcSize -= NCountLength; | |
|
312 | ||
|
313 | CHECK_F( FSE_buildDTable (workSpace, counting, maxSymbolValue, tableLog) ); | |
|
313 | 314 | |
|
314 | /* normal FSE decoding mode */ | |
|
315 | { size_t const NCountLength = FSE_readNCount (counting, &maxSymbolValue, &tableLog, istart, cSrcSize); | |
|
316 | if (FSE_isError(NCountLength)) return NCountLength; | |
|
317 | if (NCountLength >= cSrcSize) return ERROR(srcSize_wrong); /* too small input size */ | |
|
318 | ip += NCountLength; | |
|
319 | cSrcSize -= NCountLength; | |
|
320 | } | |
|
315 | return FSE_decompress_usingDTable (dst, dstCapacity, ip, cSrcSize, workSpace); /* always return, even if it is an error code */ | |
|
316 | } | |
|
317 | ||
|
321 | 318 | |
|
322 | CHECK_F( FSE_buildDTable (dt, counting, maxSymbolValue, tableLog) ); | |
|
319 | typedef FSE_DTable DTable_max_t[FSE_DTABLE_SIZE_U32(FSE_MAX_TABLELOG)]; | |
|
323 | 320 | |
|
324 | return FSE_decompress_usingDTable (dst, maxDstSize, ip, cSrcSize, dt); /* always return, even if it is an error code */ | |
|
321 | size_t FSE_decompress(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize) | |
|
322 | { | |
|
323 | DTable_max_t dt; /* Static analyzer seems unable to understand this table will be properly initialized later */ | |
|
324 | return FSE_decompress_wksp(dst, dstCapacity, cSrc, cSrcSize, dt, FSE_MAX_TABLELOG); | |
|
325 | 325 | } |
|
326 | 326 | |
|
327 | 327 | |
|
328 | 328 | |
|
329 | 329 | #endif /* FSE_COMMONDEFS_ONLY */ |
@@ -1,228 +1,238 b'' | |||
|
1 | 1 | /* ****************************************************************** |
|
2 | 2 | Huffman coder, part of New Generation Entropy library |
|
3 | 3 | header file |
|
4 | 4 | Copyright (C) 2013-2016, Yann Collet. |
|
5 | 5 | |
|
6 | 6 | BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) |
|
7 | 7 | |
|
8 | 8 | Redistribution and use in source and binary forms, with or without |
|
9 | 9 | modification, are permitted provided that the following conditions are |
|
10 | 10 | met: |
|
11 | 11 | |
|
12 | 12 | * Redistributions of source code must retain the above copyright |
|
13 | 13 | notice, this list of conditions and the following disclaimer. |
|
14 | 14 | * Redistributions in binary form must reproduce the above |
|
15 | 15 | copyright notice, this list of conditions and the following disclaimer |
|
16 | 16 | in the documentation and/or other materials provided with the |
|
17 | 17 | distribution. |
|
18 | 18 | |
|
19 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
20 | 20 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
21 | 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
22 | 22 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
23 | 23 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
24 | 24 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
25 | 25 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
26 | 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
27 | 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
28 | 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
29 | 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
30 | 30 | |
|
31 | 31 | You can contact the author at : |
|
32 | 32 | - Source repository : https://github.com/Cyan4973/FiniteStateEntropy |
|
33 | 33 | ****************************************************************** */ |
|
34 | 34 | #ifndef HUF_H_298734234 |
|
35 | 35 | #define HUF_H_298734234 |
|
36 | 36 | |
|
37 | 37 | #if defined (__cplusplus) |
|
38 | 38 | extern "C" { |
|
39 | 39 | #endif |
|
40 | 40 | |
|
41 | 41 | |
|
42 | 42 | /* *** Dependencies *** */ |
|
43 | 43 | #include <stddef.h> /* size_t */ |
|
44 | 44 | |
|
45 | 45 | |
|
46 | 46 | /* *** simple functions *** */ |
|
47 | 47 | /** |
|
48 | 48 | HUF_compress() : |
|
49 | 49 | Compress content from buffer 'src', of size 'srcSize', into buffer 'dst'. |
|
50 | 50 | 'dst' buffer must be already allocated. |
|
51 | 51 | Compression runs faster if `dstCapacity` >= HUF_compressBound(srcSize). |
|
52 | 52 | `srcSize` must be <= `HUF_BLOCKSIZE_MAX` == 128 KB. |
|
53 | 53 | @return : size of compressed data (<= `dstCapacity`). |
|
54 | 54 | Special values : if return == 0, srcData is not compressible => Nothing is stored within dst !!! |
|
55 | 55 | if return == 1, srcData is a single repeated byte symbol (RLE compression). |
|
56 | 56 | if HUF_isError(return), compression failed (more details using HUF_getErrorName()) |
|
57 | 57 | */ |
|
58 | 58 | size_t HUF_compress(void* dst, size_t dstCapacity, |
|
59 | 59 | const void* src, size_t srcSize); |
|
60 | 60 | |
|
61 | 61 | /** |
|
62 | 62 | HUF_decompress() : |
|
63 | 63 | Decompress HUF data from buffer 'cSrc', of size 'cSrcSize', |
|
64 | 64 | into already allocated buffer 'dst', of minimum size 'dstSize'. |
|
65 |
` |
|
|
65 | `originalSize` : **must** be the ***exact*** size of original (uncompressed) data. | |
|
66 | 66 | Note : in contrast with FSE, HUF_decompress can regenerate |
|
67 | 67 | RLE (cSrcSize==1) and uncompressed (cSrcSize==dstSize) data, |
|
68 | 68 | because it knows size to regenerate. |
|
69 |
@return : size of regenerated data (== |
|
|
69 | @return : size of regenerated data (== originalSize), | |
|
70 | 70 | or an error code, which can be tested using HUF_isError() |
|
71 | 71 | */ |
|
72 |
size_t HUF_decompress(void* dst, size_t |
|
|
72 | size_t HUF_decompress(void* dst, size_t originalSize, | |
|
73 | 73 | const void* cSrc, size_t cSrcSize); |
|
74 | 74 | |
|
75 | 75 | |
|
76 | /* **************************************** | |
|
77 | * Tool functions | |
|
78 | ******************************************/ | |
|
79 | #define HUF_BLOCKSIZE_MAX (128 * 1024) | |
|
76 | /* *** Tool functions *** */ | |
|
77 | #define HUF_BLOCKSIZE_MAX (128 * 1024) /**< maximum input size for a single block compressed with HUF_compress */ | |
|
80 | 78 | size_t HUF_compressBound(size_t size); /**< maximum compressed size (worst case) */ |
|
81 | 79 | |
|
82 | 80 | /* Error Management */ |
|
83 | 81 | unsigned HUF_isError(size_t code); /**< tells if a return value is an error code */ |
|
84 | 82 | const char* HUF_getErrorName(size_t code); /**< provides error code string (useful for debugging) */ |
|
85 | 83 | |
|
86 | 84 | |
|
87 | /* *** Advanced function *** */ | |
|
85 | /* *** Advanced function *** */ | |
|
88 | 86 | |
|
89 | 87 | /** HUF_compress2() : |
|
90 |
* Same as HUF_compress(), but offers direct control over `maxSymbolValue` and `tableLog` |
|
|
88 | * Same as HUF_compress(), but offers direct control over `maxSymbolValue` and `tableLog` . | |
|
89 | * `tableLog` must be `<= HUF_TABLELOG_MAX` . */ | |
|
91 | 90 | size_t HUF_compress2 (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog); |
|
92 | 91 | |
|
92 | /** HUF_compress4X_wksp() : | |
|
93 | * Same as HUF_compress2(), but uses externally allocated `workSpace`, which must be a table of >= 1024 unsigned */ | |
|
94 | size_t HUF_compress4X_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize); /**< `workSpace` must be a table of at least 1024 unsigned */ | |
|
95 | ||
|
96 | ||
|
93 | 97 | |
|
94 | 98 | #ifdef HUF_STATIC_LINKING_ONLY |
|
95 | 99 | |
|
96 | 100 | /* *** Dependencies *** */ |
|
97 | 101 | #include "mem.h" /* U32 */ |
|
98 | 102 | |
|
99 | 103 | |
|
100 | 104 | /* *** Constants *** */ |
|
101 |
#define HUF_TABLELOG_ABSOLUTEMAX 1 |
|
|
105 | #define HUF_TABLELOG_ABSOLUTEMAX 15 /* absolute limit of HUF_MAX_TABLELOG. Beyond that value, code does not work */ | |
|
102 | 106 | #define HUF_TABLELOG_MAX 12 /* max configured tableLog (for static allocation); can be modified up to HUF_ABSOLUTEMAX_TABLELOG */ |
|
103 | 107 | #define HUF_TABLELOG_DEFAULT 11 /* tableLog by default, when not specified */ |
|
104 | 108 | #define HUF_SYMBOLVALUE_MAX 255 |
|
105 | 109 | #if (HUF_TABLELOG_MAX > HUF_TABLELOG_ABSOLUTEMAX) |
|
106 | 110 | # error "HUF_TABLELOG_MAX is too large !" |
|
107 | 111 | #endif |
|
108 | 112 | |
|
109 | 113 | |
|
110 | 114 | /* **************************************** |
|
111 | 115 | * Static allocation |
|
112 | 116 | ******************************************/ |
|
113 | 117 | /* HUF buffer bounds */ |
|
114 | 118 | #define HUF_CTABLEBOUND 129 |
|
115 | 119 | #define HUF_BLOCKBOUND(size) (size + (size>>8) + 8) /* only true if incompressible pre-filtered with fast heuristic */ |
|
116 | 120 | #define HUF_COMPRESSBOUND(size) (HUF_CTABLEBOUND + HUF_BLOCKBOUND(size)) /* Macro version, useful for static allocation */ |
|
117 | 121 | |
|
118 | 122 | /* static allocation of HUF's Compression Table */ |
|
119 | 123 | #define HUF_CREATE_STATIC_CTABLE(name, maxSymbolValue) \ |
|
120 | 124 | U32 name##hb[maxSymbolValue+1]; \ |
|
121 | 125 | void* name##hv = &(name##hb); \ |
|
122 | 126 | HUF_CElt* name = (HUF_CElt*)(name##hv) /* no final ; */ |
|
123 | 127 | |
|
124 | 128 | /* static allocation of HUF's DTable */ |
|
125 | 129 | typedef U32 HUF_DTable; |
|
126 | 130 | #define HUF_DTABLE_SIZE(maxTableLog) (1 + (1<<(maxTableLog))) |
|
127 | 131 | #define HUF_CREATE_STATIC_DTABLEX2(DTable, maxTableLog) \ |
|
128 | HUF_DTable DTable[HUF_DTABLE_SIZE((maxTableLog)-1)] = { ((U32)((maxTableLog)-1)*0x1000001) } | |
|
132 | HUF_DTable DTable[HUF_DTABLE_SIZE((maxTableLog)-1)] = { ((U32)((maxTableLog)-1) * 0x01000001) } | |
|
129 | 133 | #define HUF_CREATE_STATIC_DTABLEX4(DTable, maxTableLog) \ |
|
130 | HUF_DTable DTable[HUF_DTABLE_SIZE(maxTableLog)] = { ((U32)(maxTableLog)*0x1000001) } | |
|
134 | HUF_DTable DTable[HUF_DTABLE_SIZE(maxTableLog)] = { ((U32)(maxTableLog) * 0x01000001) } | |
|
131 | 135 | |
|
132 | 136 | |
|
133 | 137 | /* **************************************** |
|
134 | 138 | * Advanced decompression functions |
|
135 | 139 | ******************************************/ |
|
136 | 140 | size_t HUF_decompress4X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< single-symbol decoder */ |
|
137 | 141 | size_t HUF_decompress4X4 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< double-symbols decoder */ |
|
138 | 142 | |
|
139 | 143 | size_t HUF_decompress4X_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< decodes RLE and uncompressed */ |
|
140 | 144 | size_t HUF_decompress4X_hufOnly(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< considers RLE and uncompressed as errors */ |
|
141 | 145 | size_t HUF_decompress4X2_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< single-symbol decoder */ |
|
142 | 146 | size_t HUF_decompress4X4_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< double-symbols decoder */ |
|
143 | 147 | |
|
144 | size_t HUF_decompress1X_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); | |
|
145 | size_t HUF_decompress1X2_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< single-symbol decoder */ | |
|
146 | size_t HUF_decompress1X4_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< double-symbols decoder */ | |
|
147 | ||
|
148 | 148 | |
|
149 | 149 | /* **************************************** |
|
150 | 150 | * HUF detailed API |
|
151 | 151 | ******************************************/ |
|
152 | 152 | /*! |
|
153 | 153 | HUF_compress() does the following: |
|
154 | 154 | 1. count symbol occurrence from source[] into table count[] using FSE_count() |
|
155 | 155 | 2. (optional) refine tableLog using HUF_optimalTableLog() |
|
156 | 156 | 3. build Huffman table from count using HUF_buildCTable() |
|
157 | 157 | 4. save Huffman table to memory buffer using HUF_writeCTable() |
|
158 | 158 | 5. encode the data stream using HUF_compress4X_usingCTable() |
|
159 | 159 | |
|
160 | 160 | The following API allows targeting specific sub-functions for advanced tasks. |
|
161 | 161 | For example, it's possible to compress several blocks using the same 'CTable', |
|
162 | 162 | or to save and regenerate 'CTable' using external methods. |
|
163 | 163 | */ |
|
164 | 164 | /* FSE_count() : find it within "fse.h" */ |
|
165 | 165 | unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue); |
|
166 | 166 | typedef struct HUF_CElt_s HUF_CElt; /* incomplete type */ |
|
167 | 167 | size_t HUF_buildCTable (HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue, unsigned maxNbBits); |
|
168 | 168 | size_t HUF_writeCTable (void* dst, size_t maxDstSize, const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog); |
|
169 | 169 | size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable); |
|
170 | 170 | |
|
171 | 171 | |
|
172 | /** HUF_buildCTable_wksp() : | |
|
173 | * Same as HUF_buildCTable(), but using externally allocated scratch buffer. | |
|
174 | * `workSpace` must be aligned on 4-bytes boundaries, and be at least as large as a table of 1024 unsigned. | |
|
175 | */ | |
|
176 | size_t HUF_buildCTable_wksp (HUF_CElt* tree, const U32* count, U32 maxSymbolValue, U32 maxNbBits, void* workSpace, size_t wkspSize); | |
|
177 | ||
|
172 | 178 | /*! HUF_readStats() : |
|
173 | 179 | Read compact Huffman tree, saved by HUF_writeCTable(). |
|
174 | 180 | `huffWeight` is destination buffer. |
|
175 | 181 | @return : size read from `src` , or an error Code . |
|
176 | 182 | Note : Needed by HUF_readCTable() and HUF_readDTableXn() . */ |
|
177 | 183 | size_t HUF_readStats(BYTE* huffWeight, size_t hwSize, U32* rankStats, |
|
178 | 184 | U32* nbSymbolsPtr, U32* tableLogPtr, |
|
179 | 185 | const void* src, size_t srcSize); |
|
180 | 186 | |
|
181 | 187 | /** HUF_readCTable() : |
|
182 | 188 | * Loading a CTable saved with HUF_writeCTable() */ |
|
183 | 189 | size_t HUF_readCTable (HUF_CElt* CTable, unsigned maxSymbolValue, const void* src, size_t srcSize); |
|
184 | 190 | |
|
185 | 191 | |
|
186 | 192 | /* |
|
187 | 193 | HUF_decompress() does the following: |
|
188 | 194 | 1. select the decompression algorithm (X2, X4) based on pre-computed heuristics |
|
189 | 195 | 2. build Huffman table from save, using HUF_readDTableXn() |
|
190 | 196 | 3. decode 1 or 4 segments in parallel using HUF_decompressSXn_usingDTable |
|
191 | 197 | */ |
|
192 | 198 | |
|
193 | 199 | /** HUF_selectDecoder() : |
|
194 | 200 | * Tells which decoder is likely to decode faster, |
|
195 | 201 | * based on a set of pre-determined metrics. |
|
196 | 202 | * @return : 0==HUF_decompress4X2, 1==HUF_decompress4X4 . |
|
197 | 203 | * Assumption : 0 < cSrcSize < dstSize <= 128 KB */ |
|
198 | 204 | U32 HUF_selectDecoder (size_t dstSize, size_t cSrcSize); |
|
199 | 205 | |
|
200 | 206 | size_t HUF_readDTableX2 (HUF_DTable* DTable, const void* src, size_t srcSize); |
|
201 | 207 | size_t HUF_readDTableX4 (HUF_DTable* DTable, const void* src, size_t srcSize); |
|
202 | 208 | |
|
203 | 209 | size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); |
|
204 | 210 | size_t HUF_decompress4X2_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); |
|
205 | 211 | size_t HUF_decompress4X4_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); |
|
206 | 212 | |
|
207 | 213 | |
|
208 | 214 | /* single stream variants */ |
|
209 | 215 | |
|
210 | 216 | size_t HUF_compress1X (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog); |
|
217 | size_t HUF_compress1X_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize); /**< `workSpace` must be a table of at least 1024 unsigned */ | |
|
211 | 218 | size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable); |
|
212 | 219 | |
|
213 | 220 | size_t HUF_decompress1X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* single-symbol decoder */ |
|
214 | 221 | size_t HUF_decompress1X4 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* double-symbol decoder */ |
|
215 | 222 | |
|
216 |
size_t HUF_decompress1X_ |
|
|
223 | size_t HUF_decompress1X_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); | |
|
224 | size_t HUF_decompress1X2_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< single-symbol decoder */ | |
|
225 | size_t HUF_decompress1X4_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< double-symbols decoder */ | |
|
226 | ||
|
227 | size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); /**< automatic selection of sing or double symbol decoder, based on DTable */ | |
|
217 | 228 | size_t HUF_decompress1X2_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); |
|
218 | 229 | size_t HUF_decompress1X4_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); |
|
219 | 230 | |
|
220 | ||
|
221 | 231 | #endif /* HUF_STATIC_LINKING_ONLY */ |
|
222 | 232 | |
|
223 | 233 | |
|
224 | 234 | #if defined (__cplusplus) |
|
225 | 235 | } |
|
226 | 236 | #endif |
|
227 | 237 | |
|
228 | 238 | #endif /* HUF_H_298734234 */ |
@@ -1,370 +1,372 b'' | |||
|
1 | 1 | /** |
|
2 | 2 | * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. |
|
3 | 3 | * All rights reserved. |
|
4 | 4 | * |
|
5 | 5 | * This source code is licensed under the BSD-style license found in the |
|
6 | 6 | * LICENSE file in the root directory of this source tree. An additional grant |
|
7 | 7 | * of patent rights can be found in the PATENTS file in the same directory. |
|
8 | 8 | */ |
|
9 | 9 | |
|
10 | 10 | #ifndef MEM_H_MODULE |
|
11 | 11 | #define MEM_H_MODULE |
|
12 | 12 | |
|
13 | 13 | #if defined (__cplusplus) |
|
14 | 14 | extern "C" { |
|
15 | 15 | #endif |
|
16 | 16 | |
|
17 | 17 | /*-**************************************** |
|
18 | 18 | * Dependencies |
|
19 | 19 | ******************************************/ |
|
20 | 20 | #include <stddef.h> /* size_t, ptrdiff_t */ |
|
21 | 21 | #include <string.h> /* memcpy */ |
|
22 | 22 | |
|
23 | 23 | |
|
24 | 24 | /*-**************************************** |
|
25 | 25 | * Compiler specifics |
|
26 | 26 | ******************************************/ |
|
27 | 27 | #if defined(_MSC_VER) /* Visual Studio */ |
|
28 | 28 | # include <stdlib.h> /* _byteswap_ulong */ |
|
29 | 29 | # include <intrin.h> /* _byteswap_* */ |
|
30 | 30 | #endif |
|
31 | 31 | #if defined(__GNUC__) |
|
32 | 32 | # define MEM_STATIC static __inline __attribute__((unused)) |
|
33 | 33 | #elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) |
|
34 | 34 | # define MEM_STATIC static inline |
|
35 | 35 | #elif defined(_MSC_VER) |
|
36 | 36 | # define MEM_STATIC static __inline |
|
37 | 37 | #else |
|
38 | 38 | # define MEM_STATIC static /* this version may generate warnings for unused static functions; disable the relevant warning */ |
|
39 | 39 | #endif |
|
40 | 40 | |
|
41 | 41 | /* code only tested on 32 and 64 bits systems */ |
|
42 | 42 | #define MEM_STATIC_ASSERT(c) { enum { XXH_static_assert = 1/(int)(!!(c)) }; } |
|
43 | 43 | MEM_STATIC void MEM_check(void) { MEM_STATIC_ASSERT((sizeof(size_t)==4) || (sizeof(size_t)==8)); } |
|
44 | 44 | |
|
45 | 45 | |
|
46 | 46 | /*-************************************************************** |
|
47 | 47 | * Basic Types |
|
48 | 48 | *****************************************************************/ |
|
49 | 49 | #if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) |
|
50 | 50 | # include <stdint.h> |
|
51 | 51 | typedef uint8_t BYTE; |
|
52 | 52 | typedef uint16_t U16; |
|
53 | 53 | typedef int16_t S16; |
|
54 | 54 | typedef uint32_t U32; |
|
55 | 55 | typedef int32_t S32; |
|
56 | 56 | typedef uint64_t U64; |
|
57 | 57 | typedef int64_t S64; |
|
58 | typedef intptr_t iPtrDiff; | |
|
58 | 59 | #else |
|
59 |
typedef unsigned char |
|
|
60 | typedef unsigned char BYTE; | |
|
60 | 61 | typedef unsigned short U16; |
|
61 | 62 | typedef signed short S16; |
|
62 | 63 | typedef unsigned int U32; |
|
63 | 64 | typedef signed int S32; |
|
64 | 65 | typedef unsigned long long U64; |
|
65 | 66 | typedef signed long long S64; |
|
67 | typedef ptrdiff_t iPtrDiff; | |
|
66 | 68 | #endif |
|
67 | 69 | |
|
68 | 70 | |
|
69 | 71 | /*-************************************************************** |
|
70 | 72 | * Memory I/O |
|
71 | 73 | *****************************************************************/ |
|
72 | 74 | /* MEM_FORCE_MEMORY_ACCESS : |
|
73 | 75 | * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. |
|
74 | 76 | * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. |
|
75 | 77 | * The below switch allow to select different access method for improved performance. |
|
76 | 78 | * Method 0 (default) : use `memcpy()`. Safe and portable. |
|
77 | 79 | * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). |
|
78 | 80 | * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. |
|
79 | 81 | * Method 2 : direct access. This method is portable but violate C standard. |
|
80 | 82 | * It can generate buggy code on targets depending on alignment. |
|
81 | 83 | * In some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) |
|
82 | 84 | * See http://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details. |
|
83 | 85 | * Prefer these methods in priority order (0 > 1 > 2) |
|
84 | 86 | */ |
|
85 | 87 | #ifndef MEM_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ |
|
86 | 88 | # if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) |
|
87 | 89 | # define MEM_FORCE_MEMORY_ACCESS 2 |
|
88 | 90 | # elif defined(__INTEL_COMPILER) /*|| defined(_MSC_VER)*/ || \ |
|
89 | 91 | (defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) )) |
|
90 | 92 | # define MEM_FORCE_MEMORY_ACCESS 1 |
|
91 | 93 | # endif |
|
92 | 94 | #endif |
|
93 | 95 | |
|
94 | 96 | MEM_STATIC unsigned MEM_32bits(void) { return sizeof(size_t)==4; } |
|
95 | 97 | MEM_STATIC unsigned MEM_64bits(void) { return sizeof(size_t)==8; } |
|
96 | 98 | |
|
97 | 99 | MEM_STATIC unsigned MEM_isLittleEndian(void) |
|
98 | 100 | { |
|
99 | 101 | const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ |
|
100 | 102 | return one.c[0]; |
|
101 | 103 | } |
|
102 | 104 | |
|
103 | 105 | #if defined(MEM_FORCE_MEMORY_ACCESS) && (MEM_FORCE_MEMORY_ACCESS==2) |
|
104 | 106 | |
|
105 | 107 | /* violates C standard, by lying on structure alignment. |
|
106 | 108 | Only use if no other choice to achieve best performance on target platform */ |
|
107 | 109 | MEM_STATIC U16 MEM_read16(const void* memPtr) { return *(const U16*) memPtr; } |
|
108 | 110 | MEM_STATIC U32 MEM_read32(const void* memPtr) { return *(const U32*) memPtr; } |
|
109 | 111 | MEM_STATIC U64 MEM_read64(const void* memPtr) { return *(const U64*) memPtr; } |
|
110 | 112 | MEM_STATIC U64 MEM_readST(const void* memPtr) { return *(const size_t*) memPtr; } |
|
111 | 113 | |
|
112 | 114 | MEM_STATIC void MEM_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; } |
|
113 | 115 | MEM_STATIC void MEM_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; } |
|
114 | 116 | MEM_STATIC void MEM_write64(void* memPtr, U64 value) { *(U64*)memPtr = value; } |
|
115 | 117 | |
|
116 | 118 | #elif defined(MEM_FORCE_MEMORY_ACCESS) && (MEM_FORCE_MEMORY_ACCESS==1) |
|
117 | 119 | |
|
118 | 120 | /* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ |
|
119 | 121 | /* currently only defined for gcc and icc */ |
|
120 | 122 | #if defined(_MSC_VER) || (defined(__INTEL_COMPILER) && defined(WIN32)) |
|
121 | 123 | __pragma( pack(push, 1) ) |
|
122 | 124 | typedef union { U16 u16; U32 u32; U64 u64; size_t st; } unalign; |
|
123 | 125 | __pragma( pack(pop) ) |
|
124 | 126 | #else |
|
125 | 127 | typedef union { U16 u16; U32 u32; U64 u64; size_t st; } __attribute__((packed)) unalign; |
|
126 | 128 | #endif |
|
127 | 129 | |
|
128 | 130 | MEM_STATIC U16 MEM_read16(const void* ptr) { return ((const unalign*)ptr)->u16; } |
|
129 | 131 | MEM_STATIC U32 MEM_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } |
|
130 | 132 | MEM_STATIC U64 MEM_read64(const void* ptr) { return ((const unalign*)ptr)->u64; } |
|
131 | 133 | MEM_STATIC U64 MEM_readST(const void* ptr) { return ((const unalign*)ptr)->st; } |
|
132 | 134 | |
|
133 | 135 | MEM_STATIC void MEM_write16(void* memPtr, U16 value) { ((unalign*)memPtr)->u16 = value; } |
|
134 | 136 | MEM_STATIC void MEM_write32(void* memPtr, U32 value) { ((unalign*)memPtr)->u32 = value; } |
|
135 | 137 | MEM_STATIC void MEM_write64(void* memPtr, U64 value) { ((unalign*)memPtr)->u64 = value; } |
|
136 | 138 | |
|
137 | 139 | #else |
|
138 | 140 | |
|
139 | 141 | /* default method, safe and standard. |
|
140 | 142 | can sometimes prove slower */ |
|
141 | 143 | |
|
142 | 144 | MEM_STATIC U16 MEM_read16(const void* memPtr) |
|
143 | 145 | { |
|
144 | 146 | U16 val; memcpy(&val, memPtr, sizeof(val)); return val; |
|
145 | 147 | } |
|
146 | 148 | |
|
147 | 149 | MEM_STATIC U32 MEM_read32(const void* memPtr) |
|
148 | 150 | { |
|
149 | 151 | U32 val; memcpy(&val, memPtr, sizeof(val)); return val; |
|
150 | 152 | } |
|
151 | 153 | |
|
152 | 154 | MEM_STATIC U64 MEM_read64(const void* memPtr) |
|
153 | 155 | { |
|
154 | 156 | U64 val; memcpy(&val, memPtr, sizeof(val)); return val; |
|
155 | 157 | } |
|
156 | 158 | |
|
157 | 159 | MEM_STATIC size_t MEM_readST(const void* memPtr) |
|
158 | 160 | { |
|
159 | 161 | size_t val; memcpy(&val, memPtr, sizeof(val)); return val; |
|
160 | 162 | } |
|
161 | 163 | |
|
162 | 164 | MEM_STATIC void MEM_write16(void* memPtr, U16 value) |
|
163 | 165 | { |
|
164 | 166 | memcpy(memPtr, &value, sizeof(value)); |
|
165 | 167 | } |
|
166 | 168 | |
|
167 | 169 | MEM_STATIC void MEM_write32(void* memPtr, U32 value) |
|
168 | 170 | { |
|
169 | 171 | memcpy(memPtr, &value, sizeof(value)); |
|
170 | 172 | } |
|
171 | 173 | |
|
172 | 174 | MEM_STATIC void MEM_write64(void* memPtr, U64 value) |
|
173 | 175 | { |
|
174 | 176 | memcpy(memPtr, &value, sizeof(value)); |
|
175 | 177 | } |
|
176 | 178 | |
|
177 | 179 | #endif /* MEM_FORCE_MEMORY_ACCESS */ |
|
178 | 180 | |
|
179 | 181 | MEM_STATIC U32 MEM_swap32(U32 in) |
|
180 | 182 | { |
|
181 | 183 | #if defined(_MSC_VER) /* Visual Studio */ |
|
182 | 184 | return _byteswap_ulong(in); |
|
183 | 185 | #elif defined (__GNUC__) |
|
184 | 186 | return __builtin_bswap32(in); |
|
185 | 187 | #else |
|
186 | 188 | return ((in << 24) & 0xff000000 ) | |
|
187 | 189 | ((in << 8) & 0x00ff0000 ) | |
|
188 | 190 | ((in >> 8) & 0x0000ff00 ) | |
|
189 | 191 | ((in >> 24) & 0x000000ff ); |
|
190 | 192 | #endif |
|
191 | 193 | } |
|
192 | 194 | |
|
193 | 195 | MEM_STATIC U64 MEM_swap64(U64 in) |
|
194 | 196 | { |
|
195 | 197 | #if defined(_MSC_VER) /* Visual Studio */ |
|
196 | 198 | return _byteswap_uint64(in); |
|
197 | 199 | #elif defined (__GNUC__) |
|
198 | 200 | return __builtin_bswap64(in); |
|
199 | 201 | #else |
|
200 | 202 | return ((in << 56) & 0xff00000000000000ULL) | |
|
201 | 203 | ((in << 40) & 0x00ff000000000000ULL) | |
|
202 | 204 | ((in << 24) & 0x0000ff0000000000ULL) | |
|
203 | 205 | ((in << 8) & 0x000000ff00000000ULL) | |
|
204 | 206 | ((in >> 8) & 0x00000000ff000000ULL) | |
|
205 | 207 | ((in >> 24) & 0x0000000000ff0000ULL) | |
|
206 | 208 | ((in >> 40) & 0x000000000000ff00ULL) | |
|
207 | 209 | ((in >> 56) & 0x00000000000000ffULL); |
|
208 | 210 | #endif |
|
209 | 211 | } |
|
210 | 212 | |
|
211 | 213 | MEM_STATIC size_t MEM_swapST(size_t in) |
|
212 | 214 | { |
|
213 | 215 | if (MEM_32bits()) |
|
214 | 216 | return (size_t)MEM_swap32((U32)in); |
|
215 | 217 | else |
|
216 | 218 | return (size_t)MEM_swap64((U64)in); |
|
217 | 219 | } |
|
218 | 220 | |
|
219 | 221 | /*=== Little endian r/w ===*/ |
|
220 | 222 | |
|
221 | 223 | MEM_STATIC U16 MEM_readLE16(const void* memPtr) |
|
222 | 224 | { |
|
223 | 225 | if (MEM_isLittleEndian()) |
|
224 | 226 | return MEM_read16(memPtr); |
|
225 | 227 | else { |
|
226 | 228 | const BYTE* p = (const BYTE*)memPtr; |
|
227 | 229 | return (U16)(p[0] + (p[1]<<8)); |
|
228 | 230 | } |
|
229 | 231 | } |
|
230 | 232 | |
|
231 | 233 | MEM_STATIC void MEM_writeLE16(void* memPtr, U16 val) |
|
232 | 234 | { |
|
233 | 235 | if (MEM_isLittleEndian()) { |
|
234 | 236 | MEM_write16(memPtr, val); |
|
235 | 237 | } else { |
|
236 | 238 | BYTE* p = (BYTE*)memPtr; |
|
237 | 239 | p[0] = (BYTE)val; |
|
238 | 240 | p[1] = (BYTE)(val>>8); |
|
239 | 241 | } |
|
240 | 242 | } |
|
241 | 243 | |
|
242 | 244 | MEM_STATIC U32 MEM_readLE24(const void* memPtr) |
|
243 | 245 | { |
|
244 | 246 | return MEM_readLE16(memPtr) + (((const BYTE*)memPtr)[2] << 16); |
|
245 | 247 | } |
|
246 | 248 | |
|
247 | 249 | MEM_STATIC void MEM_writeLE24(void* memPtr, U32 val) |
|
248 | 250 | { |
|
249 | 251 | MEM_writeLE16(memPtr, (U16)val); |
|
250 | 252 | ((BYTE*)memPtr)[2] = (BYTE)(val>>16); |
|
251 | 253 | } |
|
252 | 254 | |
|
253 | 255 | MEM_STATIC U32 MEM_readLE32(const void* memPtr) |
|
254 | 256 | { |
|
255 | 257 | if (MEM_isLittleEndian()) |
|
256 | 258 | return MEM_read32(memPtr); |
|
257 | 259 | else |
|
258 | 260 | return MEM_swap32(MEM_read32(memPtr)); |
|
259 | 261 | } |
|
260 | 262 | |
|
261 | 263 | MEM_STATIC void MEM_writeLE32(void* memPtr, U32 val32) |
|
262 | 264 | { |
|
263 | 265 | if (MEM_isLittleEndian()) |
|
264 | 266 | MEM_write32(memPtr, val32); |
|
265 | 267 | else |
|
266 | 268 | MEM_write32(memPtr, MEM_swap32(val32)); |
|
267 | 269 | } |
|
268 | 270 | |
|
269 | 271 | MEM_STATIC U64 MEM_readLE64(const void* memPtr) |
|
270 | 272 | { |
|
271 | 273 | if (MEM_isLittleEndian()) |
|
272 | 274 | return MEM_read64(memPtr); |
|
273 | 275 | else |
|
274 | 276 | return MEM_swap64(MEM_read64(memPtr)); |
|
275 | 277 | } |
|
276 | 278 | |
|
277 | 279 | MEM_STATIC void MEM_writeLE64(void* memPtr, U64 val64) |
|
278 | 280 | { |
|
279 | 281 | if (MEM_isLittleEndian()) |
|
280 | 282 | MEM_write64(memPtr, val64); |
|
281 | 283 | else |
|
282 | 284 | MEM_write64(memPtr, MEM_swap64(val64)); |
|
283 | 285 | } |
|
284 | 286 | |
|
285 | 287 | MEM_STATIC size_t MEM_readLEST(const void* memPtr) |
|
286 | 288 | { |
|
287 | 289 | if (MEM_32bits()) |
|
288 | 290 | return (size_t)MEM_readLE32(memPtr); |
|
289 | 291 | else |
|
290 | 292 | return (size_t)MEM_readLE64(memPtr); |
|
291 | 293 | } |
|
292 | 294 | |
|
293 | 295 | MEM_STATIC void MEM_writeLEST(void* memPtr, size_t val) |
|
294 | 296 | { |
|
295 | 297 | if (MEM_32bits()) |
|
296 | 298 | MEM_writeLE32(memPtr, (U32)val); |
|
297 | 299 | else |
|
298 | 300 | MEM_writeLE64(memPtr, (U64)val); |
|
299 | 301 | } |
|
300 | 302 | |
|
301 | 303 | /*=== Big endian r/w ===*/ |
|
302 | 304 | |
|
303 | 305 | MEM_STATIC U32 MEM_readBE32(const void* memPtr) |
|
304 | 306 | { |
|
305 | 307 | if (MEM_isLittleEndian()) |
|
306 | 308 | return MEM_swap32(MEM_read32(memPtr)); |
|
307 | 309 | else |
|
308 | 310 | return MEM_read32(memPtr); |
|
309 | 311 | } |
|
310 | 312 | |
|
311 | 313 | MEM_STATIC void MEM_writeBE32(void* memPtr, U32 val32) |
|
312 | 314 | { |
|
313 | 315 | if (MEM_isLittleEndian()) |
|
314 | 316 | MEM_write32(memPtr, MEM_swap32(val32)); |
|
315 | 317 | else |
|
316 | 318 | MEM_write32(memPtr, val32); |
|
317 | 319 | } |
|
318 | 320 | |
|
319 | 321 | MEM_STATIC U64 MEM_readBE64(const void* memPtr) |
|
320 | 322 | { |
|
321 | 323 | if (MEM_isLittleEndian()) |
|
322 | 324 | return MEM_swap64(MEM_read64(memPtr)); |
|
323 | 325 | else |
|
324 | 326 | return MEM_read64(memPtr); |
|
325 | 327 | } |
|
326 | 328 | |
|
327 | 329 | MEM_STATIC void MEM_writeBE64(void* memPtr, U64 val64) |
|
328 | 330 | { |
|
329 | 331 | if (MEM_isLittleEndian()) |
|
330 | 332 | MEM_write64(memPtr, MEM_swap64(val64)); |
|
331 | 333 | else |
|
332 | 334 | MEM_write64(memPtr, val64); |
|
333 | 335 | } |
|
334 | 336 | |
|
335 | 337 | MEM_STATIC size_t MEM_readBEST(const void* memPtr) |
|
336 | 338 | { |
|
337 | 339 | if (MEM_32bits()) |
|
338 | 340 | return (size_t)MEM_readBE32(memPtr); |
|
339 | 341 | else |
|
340 | 342 | return (size_t)MEM_readBE64(memPtr); |
|
341 | 343 | } |
|
342 | 344 | |
|
343 | 345 | MEM_STATIC void MEM_writeBEST(void* memPtr, size_t val) |
|
344 | 346 | { |
|
345 | 347 | if (MEM_32bits()) |
|
346 | 348 | MEM_writeBE32(memPtr, (U32)val); |
|
347 | 349 | else |
|
348 | 350 | MEM_writeBE64(memPtr, (U64)val); |
|
349 | 351 | } |
|
350 | 352 | |
|
351 | 353 | |
|
352 | 354 | /* function safe only for comparisons */ |
|
353 | 355 | MEM_STATIC U32 MEM_readMINMATCH(const void* memPtr, U32 length) |
|
354 | 356 | { |
|
355 | 357 | switch (length) |
|
356 | 358 | { |
|
357 | 359 | default : |
|
358 | 360 | case 4 : return MEM_read32(memPtr); |
|
359 | 361 | case 3 : if (MEM_isLittleEndian()) |
|
360 | 362 | return MEM_read32(memPtr)<<8; |
|
361 | 363 | else |
|
362 | 364 | return MEM_read32(memPtr)>>8; |
|
363 | 365 | } |
|
364 | 366 | } |
|
365 | 367 | |
|
366 | 368 | #if defined (__cplusplus) |
|
367 | 369 | } |
|
368 | 370 | #endif |
|
369 | 371 | |
|
370 | 372 | #endif /* MEM_H_MODULE */ |
@@ -1,83 +1,77 b'' | |||
|
1 | 1 | /** |
|
2 | 2 | * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. |
|
3 | 3 | * All rights reserved. |
|
4 | 4 | * |
|
5 | 5 | * This source code is licensed under the BSD-style license found in the |
|
6 | 6 | * LICENSE file in the root directory of this source tree. An additional grant |
|
7 | 7 | * of patent rights can be found in the PATENTS file in the same directory. |
|
8 | 8 | */ |
|
9 | 9 | |
|
10 | 10 | |
|
11 | 11 | |
|
12 | 12 | /*-************************************* |
|
13 | 13 | * Dependencies |
|
14 | 14 | ***************************************/ |
|
15 | 15 | #include <stdlib.h> /* malloc */ |
|
16 | 16 | #include "error_private.h" |
|
17 | 17 | #define ZSTD_STATIC_LINKING_ONLY |
|
18 | 18 | #include "zstd.h" /* declaration of ZSTD_isError, ZSTD_getErrorName, ZSTD_getErrorCode, ZSTD_getErrorString, ZSTD_versionNumber */ |
|
19 | #include "zbuff.h" /* declaration of ZBUFF_isError, ZBUFF_getErrorName */ | |
|
20 | 19 | |
|
21 | 20 | |
|
22 | 21 | /*-**************************************** |
|
23 | 22 | * Version |
|
24 | 23 | ******************************************/ |
|
25 | 24 | unsigned ZSTD_versionNumber (void) { return ZSTD_VERSION_NUMBER; } |
|
26 | 25 | |
|
27 | 26 | |
|
28 | 27 | /*-**************************************** |
|
29 | 28 | * ZSTD Error Management |
|
30 | 29 | ******************************************/ |
|
31 | 30 | /*! ZSTD_isError() : |
|
32 | 31 | * tells if a return value is an error code */ |
|
33 | 32 | unsigned ZSTD_isError(size_t code) { return ERR_isError(code); } |
|
34 | 33 | |
|
35 | 34 | /*! ZSTD_getErrorName() : |
|
36 | 35 | * provides error code string from function result (useful for debugging) */ |
|
37 | 36 | const char* ZSTD_getErrorName(size_t code) { return ERR_getErrorName(code); } |
|
38 | 37 | |
|
39 | 38 | /*! ZSTD_getError() : |
|
40 | 39 | * convert a `size_t` function result into a proper ZSTD_errorCode enum */ |
|
41 | 40 | ZSTD_ErrorCode ZSTD_getErrorCode(size_t code) { return ERR_getErrorCode(code); } |
|
42 | 41 | |
|
43 | 42 | /*! ZSTD_getErrorString() : |
|
44 | 43 | * provides error code string from enum */ |
|
45 | 44 | const char* ZSTD_getErrorString(ZSTD_ErrorCode code) { return ERR_getErrorName(code); } |
|
46 | 45 | |
|
47 | ||
|
48 | /* ************************************************************** | |
|
49 | * ZBUFF Error Management | |
|
50 | ****************************************************************/ | |
|
46 | /* --- ZBUFF Error Management (deprecated) --- */ | |
|
51 | 47 | unsigned ZBUFF_isError(size_t errorCode) { return ERR_isError(errorCode); } |
|
52 | ||
|
53 | 48 | const char* ZBUFF_getErrorName(size_t errorCode) { return ERR_getErrorName(errorCode); } |
|
54 | 49 | |
|
55 | 50 | |
|
56 | ||
|
57 | 51 | /*=************************************************************** |
|
58 | 52 | * Custom allocator |
|
59 | 53 | ****************************************************************/ |
|
60 | 54 | /* default uses stdlib */ |
|
61 | 55 | void* ZSTD_defaultAllocFunction(void* opaque, size_t size) |
|
62 | 56 | { |
|
63 | 57 | void* address = malloc(size); |
|
64 | 58 | (void)opaque; |
|
65 | 59 | return address; |
|
66 | 60 | } |
|
67 | 61 | |
|
68 | 62 | void ZSTD_defaultFreeFunction(void* opaque, void* address) |
|
69 | 63 | { |
|
70 | 64 | (void)opaque; |
|
71 | 65 | free(address); |
|
72 | 66 | } |
|
73 | 67 | |
|
74 | 68 | void* ZSTD_malloc(size_t size, ZSTD_customMem customMem) |
|
75 | 69 | { |
|
76 | 70 | return customMem.customAlloc(customMem.opaque, size); |
|
77 | 71 | } |
|
78 | 72 | |
|
79 | 73 | void ZSTD_free(void* ptr, ZSTD_customMem customMem) |
|
80 | 74 | { |
|
81 | 75 | if (ptr!=NULL) |
|
82 | 76 | customMem.customFree(customMem.opaque, ptr); |
|
83 | 77 | } |
@@ -1,267 +1,270 b'' | |||
|
1 | 1 | /** |
|
2 | 2 | * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. |
|
3 | 3 | * All rights reserved. |
|
4 | 4 | * |
|
5 | 5 | * This source code is licensed under the BSD-style license found in the |
|
6 | 6 | * LICENSE file in the root directory of this source tree. An additional grant |
|
7 | 7 | * of patent rights can be found in the PATENTS file in the same directory. |
|
8 | 8 | */ |
|
9 | 9 | |
|
10 | 10 | #ifndef ZSTD_CCOMMON_H_MODULE |
|
11 | 11 | #define ZSTD_CCOMMON_H_MODULE |
|
12 | 12 | |
|
13 | 13 | /*-******************************************************* |
|
14 | 14 | * Compiler specifics |
|
15 | 15 | *********************************************************/ |
|
16 | 16 | #ifdef _MSC_VER /* Visual Studio */ |
|
17 | 17 | # define FORCE_INLINE static __forceinline |
|
18 | 18 | # include <intrin.h> /* For Visual 2005 */ |
|
19 | 19 | # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ |
|
20 | 20 | # pragma warning(disable : 4324) /* disable: C4324: padded structure */ |
|
21 | 21 | # pragma warning(disable : 4100) /* disable: C4100: unreferenced formal parameter */ |
|
22 | 22 | #else |
|
23 | 23 | # if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ |
|
24 | 24 | # ifdef __GNUC__ |
|
25 | 25 | # define FORCE_INLINE static inline __attribute__((always_inline)) |
|
26 | 26 | # else |
|
27 | 27 | # define FORCE_INLINE static inline |
|
28 | 28 | # endif |
|
29 | 29 | # else |
|
30 | 30 | # define FORCE_INLINE static |
|
31 | 31 | # endif /* __STDC_VERSION__ */ |
|
32 | 32 | #endif |
|
33 | 33 | |
|
34 | 34 | #ifdef _MSC_VER |
|
35 | 35 | # define FORCE_NOINLINE static __declspec(noinline) |
|
36 | 36 | #else |
|
37 | 37 | # ifdef __GNUC__ |
|
38 | 38 | # define FORCE_NOINLINE static __attribute__((__noinline__)) |
|
39 | 39 | # else |
|
40 | 40 | # define FORCE_NOINLINE static |
|
41 | 41 | # endif |
|
42 | 42 | #endif |
|
43 | 43 | |
|
44 | 44 | |
|
45 | 45 | /*-************************************* |
|
46 | 46 | * Dependencies |
|
47 | 47 | ***************************************/ |
|
48 | 48 | #include "mem.h" |
|
49 | 49 | #include "error_private.h" |
|
50 | 50 | #define ZSTD_STATIC_LINKING_ONLY |
|
51 | 51 | #include "zstd.h" |
|
52 | 52 | |
|
53 | 53 | |
|
54 | 54 | /*-************************************* |
|
55 | 55 | * shared macros |
|
56 | 56 | ***************************************/ |
|
57 | 57 | #define MIN(a,b) ((a)<(b) ? (a) : (b)) |
|
58 | 58 | #define MAX(a,b) ((a)>(b) ? (a) : (b)) |
|
59 | 59 | #define CHECK_F(f) { size_t const errcod = f; if (ERR_isError(errcod)) return errcod; } /* check and Forward error code */ |
|
60 | 60 | #define CHECK_E(f, e) { size_t const errcod = f; if (ERR_isError(errcod)) return ERROR(e); } /* check and send Error code */ |
|
61 | 61 | |
|
62 | 62 | |
|
63 | 63 | /*-************************************* |
|
64 | 64 | * Common constants |
|
65 | 65 | ***************************************/ |
|
66 | 66 | #define ZSTD_OPT_NUM (1<<12) |
|
67 | 67 | #define ZSTD_DICT_MAGIC 0xEC30A437 /* v0.7+ */ |
|
68 | 68 | |
|
69 | 69 | #define ZSTD_REP_NUM 3 /* number of repcodes */ |
|
70 | 70 | #define ZSTD_REP_CHECK (ZSTD_REP_NUM) /* number of repcodes to check by the optimal parser */ |
|
71 | 71 | #define ZSTD_REP_MOVE (ZSTD_REP_NUM-1) |
|
72 | 72 | #define ZSTD_REP_MOVE_OPT (ZSTD_REP_NUM) |
|
73 | 73 | static const U32 repStartValue[ZSTD_REP_NUM] = { 1, 4, 8 }; |
|
74 | 74 | |
|
75 | 75 | #define KB *(1 <<10) |
|
76 | 76 | #define MB *(1 <<20) |
|
77 | 77 | #define GB *(1U<<30) |
|
78 | 78 | |
|
79 | 79 | #define BIT7 128 |
|
80 | 80 | #define BIT6 64 |
|
81 | 81 | #define BIT5 32 |
|
82 | 82 | #define BIT4 16 |
|
83 | 83 | #define BIT1 2 |
|
84 | 84 | #define BIT0 1 |
|
85 | 85 | |
|
86 | 86 | #define ZSTD_WINDOWLOG_ABSOLUTEMIN 10 |
|
87 | 87 | static const size_t ZSTD_fcs_fieldSize[4] = { 0, 2, 4, 8 }; |
|
88 | 88 | static const size_t ZSTD_did_fieldSize[4] = { 0, 1, 2, 4 }; |
|
89 | 89 | |
|
90 | 90 | #define ZSTD_BLOCKHEADERSIZE 3 /* C standard doesn't allow `static const` variable to be init using another `static const` variable */ |
|
91 | 91 | static const size_t ZSTD_blockHeaderSize = ZSTD_BLOCKHEADERSIZE; |
|
92 | 92 | typedef enum { bt_raw, bt_rle, bt_compressed, bt_reserved } blockType_e; |
|
93 | 93 | |
|
94 | 94 | #define MIN_SEQUENCES_SIZE 1 /* nbSeq==0 */ |
|
95 | 95 | #define MIN_CBLOCK_SIZE (1 /*litCSize*/ + 1 /* RLE or RAW */ + MIN_SEQUENCES_SIZE /* nbSeq==0 */) /* for a non-null block */ |
|
96 | 96 | |
|
97 | 97 | #define HufLog 12 |
|
98 | 98 | typedef enum { set_basic, set_rle, set_compressed, set_repeat } symbolEncodingType_e; |
|
99 | 99 | |
|
100 | 100 | #define LONGNBSEQ 0x7F00 |
|
101 | 101 | |
|
102 | 102 | #define MINMATCH 3 |
|
103 | 103 | #define EQUAL_READ32 4 |
|
104 | 104 | |
|
105 | 105 | #define Litbits 8 |
|
106 | 106 | #define MaxLit ((1<<Litbits) - 1) |
|
107 | 107 | #define MaxML 52 |
|
108 | 108 | #define MaxLL 35 |
|
109 | 109 | #define MaxOff 28 |
|
110 | 110 | #define MaxSeq MAX(MaxLL, MaxML) /* Assumption : MaxOff < MaxLL,MaxML */ |
|
111 | 111 | #define MLFSELog 9 |
|
112 | 112 | #define LLFSELog 9 |
|
113 | 113 | #define OffFSELog 8 |
|
114 | 114 | |
|
115 | 115 | static const U32 LL_bits[MaxLL+1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
116 | 116 | 1, 1, 1, 1, 2, 2, 3, 3, 4, 6, 7, 8, 9,10,11,12, |
|
117 | 117 | 13,14,15,16 }; |
|
118 | 118 | static const S16 LL_defaultNorm[MaxLL+1] = { 4, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, |
|
119 | 119 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 1, 1, 1, 1, 1, |
|
120 | 120 | -1,-1,-1,-1 }; |
|
121 | 121 | #define LL_DEFAULTNORMLOG 6 /* for static allocation */ |
|
122 | 122 | static const U32 LL_defaultNormLog = LL_DEFAULTNORMLOG; |
|
123 | 123 | |
|
124 | 124 | static const U32 ML_bits[MaxML+1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
125 | 125 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
126 | 126 | 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 7, 8, 9,10,11, |
|
127 | 127 | 12,13,14,15,16 }; |
|
128 | 128 | static const S16 ML_defaultNorm[MaxML+1] = { 1, 4, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, |
|
129 | 129 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|
130 | 130 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,-1,-1, |
|
131 | 131 | -1,-1,-1,-1,-1 }; |
|
132 | 132 | #define ML_DEFAULTNORMLOG 6 /* for static allocation */ |
|
133 | 133 | static const U32 ML_defaultNormLog = ML_DEFAULTNORMLOG; |
|
134 | 134 | |
|
135 | 135 | static const S16 OF_defaultNorm[MaxOff+1] = { 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, |
|
136 | 136 | 1, 1, 1, 1, 1, 1, 1, 1,-1,-1,-1,-1,-1 }; |
|
137 | 137 | #define OF_DEFAULTNORMLOG 5 /* for static allocation */ |
|
138 | 138 | static const U32 OF_defaultNormLog = OF_DEFAULTNORMLOG; |
|
139 | 139 | |
|
140 | 140 | |
|
141 | 141 | /*-******************************************* |
|
142 | 142 | * Shared functions to include for inlining |
|
143 | 143 | *********************************************/ |
|
144 | 144 | static void ZSTD_copy8(void* dst, const void* src) { memcpy(dst, src, 8); } |
|
145 | 145 | #define COPY8(d,s) { ZSTD_copy8(d,s); d+=8; s+=8; } |
|
146 | 146 | |
|
147 | 147 | /*! ZSTD_wildcopy() : |
|
148 | 148 | * custom version of memcpy(), can copy up to 7 bytes too many (8 bytes if length==0) */ |
|
149 | 149 | #define WILDCOPY_OVERLENGTH 8 |
|
150 |
MEM_STATIC void ZSTD_wildcopy(void* dst, const void* src, |
|
|
150 | MEM_STATIC void ZSTD_wildcopy(void* dst, const void* src, ptrdiff_t length) | |
|
151 | 151 | { |
|
152 | 152 | const BYTE* ip = (const BYTE*)src; |
|
153 | 153 | BYTE* op = (BYTE*)dst; |
|
154 | 154 | BYTE* const oend = op + length; |
|
155 | 155 | do |
|
156 | 156 | COPY8(op, ip) |
|
157 | 157 | while (op < oend); |
|
158 | 158 | } |
|
159 | 159 | |
|
160 | 160 | MEM_STATIC void ZSTD_wildcopy_e(void* dst, const void* src, void* dstEnd) /* should be faster for decoding, but strangely, not verified on all platform */ |
|
161 | 161 | { |
|
162 | 162 | const BYTE* ip = (const BYTE*)src; |
|
163 | 163 | BYTE* op = (BYTE*)dst; |
|
164 | 164 | BYTE* const oend = (BYTE*)dstEnd; |
|
165 | 165 | do |
|
166 | 166 | COPY8(op, ip) |
|
167 | 167 | while (op < oend); |
|
168 | 168 | } |
|
169 | 169 | |
|
170 | 170 | |
|
171 | 171 | /*-******************************************* |
|
172 | 172 | * Private interfaces |
|
173 | 173 | *********************************************/ |
|
174 | 174 | typedef struct ZSTD_stats_s ZSTD_stats_t; |
|
175 | 175 | |
|
176 | 176 | typedef struct { |
|
177 | 177 | U32 off; |
|
178 | 178 | U32 len; |
|
179 | 179 | } ZSTD_match_t; |
|
180 | 180 | |
|
181 | 181 | typedef struct { |
|
182 | 182 | U32 price; |
|
183 | 183 | U32 off; |
|
184 | 184 | U32 mlen; |
|
185 | 185 | U32 litlen; |
|
186 | 186 | U32 rep[ZSTD_REP_NUM]; |
|
187 | 187 | } ZSTD_optimal_t; |
|
188 | 188 | |
|
189 | 189 | |
|
190 | 190 | typedef struct seqDef_s { |
|
191 | 191 | U32 offset; |
|
192 | 192 | U16 litLength; |
|
193 | 193 | U16 matchLength; |
|
194 | 194 | } seqDef; |
|
195 | 195 | |
|
196 | 196 | |
|
197 | 197 | typedef struct { |
|
198 | 198 | seqDef* sequencesStart; |
|
199 | 199 | seqDef* sequences; |
|
200 | 200 | BYTE* litStart; |
|
201 | 201 | BYTE* lit; |
|
202 | 202 | BYTE* llCode; |
|
203 | 203 | BYTE* mlCode; |
|
204 | 204 | BYTE* ofCode; |
|
205 | 205 | U32 longLengthID; /* 0 == no longLength; 1 == Lit.longLength; 2 == Match.longLength; */ |
|
206 | 206 | U32 longLengthPos; |
|
207 | 207 | /* opt */ |
|
208 | 208 | ZSTD_optimal_t* priceTable; |
|
209 | 209 | ZSTD_match_t* matchTable; |
|
210 | 210 | U32* matchLengthFreq; |
|
211 | 211 | U32* litLengthFreq; |
|
212 | 212 | U32* litFreq; |
|
213 | 213 | U32* offCodeFreq; |
|
214 | 214 | U32 matchLengthSum; |
|
215 | 215 | U32 matchSum; |
|
216 | 216 | U32 litLengthSum; |
|
217 | 217 | U32 litSum; |
|
218 | 218 | U32 offCodeSum; |
|
219 | 219 | U32 log2matchLengthSum; |
|
220 | 220 | U32 log2matchSum; |
|
221 | 221 | U32 log2litLengthSum; |
|
222 | 222 | U32 log2litSum; |
|
223 | 223 | U32 log2offCodeSum; |
|
224 | 224 | U32 factor; |
|
225 | U32 staticPrices; | |
|
225 | 226 | U32 cachedPrice; |
|
226 | 227 | U32 cachedLitLength; |
|
227 | 228 | const BYTE* cachedLiterals; |
|
228 | 229 | } seqStore_t; |
|
229 | 230 | |
|
230 | 231 | const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx); |
|
231 | 232 | void ZSTD_seqToCodes(const seqStore_t* seqStorePtr); |
|
232 | 233 | int ZSTD_isSkipFrame(ZSTD_DCtx* dctx); |
|
233 | 234 | |
|
234 | 235 | /* custom memory allocation functions */ |
|
235 | 236 | void* ZSTD_defaultAllocFunction(void* opaque, size_t size); |
|
236 | 237 | void ZSTD_defaultFreeFunction(void* opaque, void* address); |
|
238 | #ifndef ZSTD_DLL_IMPORT | |
|
237 | 239 | static const ZSTD_customMem defaultCustomMem = { ZSTD_defaultAllocFunction, ZSTD_defaultFreeFunction, NULL }; |
|
240 | #endif | |
|
238 | 241 | void* ZSTD_malloc(size_t size, ZSTD_customMem customMem); |
|
239 | 242 | void ZSTD_free(void* ptr, ZSTD_customMem customMem); |
|
240 | 243 | |
|
241 | 244 | |
|
242 | 245 | /*====== common function ======*/ |
|
243 | 246 | |
|
244 | 247 | MEM_STATIC U32 ZSTD_highbit32(U32 val) |
|
245 | 248 | { |
|
246 | 249 | # if defined(_MSC_VER) /* Visual */ |
|
247 | 250 | unsigned long r=0; |
|
248 | 251 | _BitScanReverse(&r, val); |
|
249 | 252 | return (unsigned)r; |
|
250 | 253 | # elif defined(__GNUC__) && (__GNUC__ >= 3) /* GCC Intrinsic */ |
|
251 | 254 | return 31 - __builtin_clz(val); |
|
252 | 255 | # else /* Software version */ |
|
253 | 256 | static const int DeBruijnClz[32] = { 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 }; |
|
254 | 257 | U32 v = val; |
|
255 | 258 | int r; |
|
256 | 259 | v |= v >> 1; |
|
257 | 260 | v |= v >> 2; |
|
258 | 261 | v |= v >> 4; |
|
259 | 262 | v |= v >> 8; |
|
260 | 263 | v |= v >> 16; |
|
261 | 264 | r = DeBruijnClz[(U32)(v * 0x07C4ACDDU) >> 27]; |
|
262 | 265 | return r; |
|
263 | 266 | # endif |
|
264 | 267 | } |
|
265 | 268 | |
|
266 | 269 | |
|
267 | 270 | #endif /* ZSTD_CCOMMON_H_MODULE */ |
@@ -1,810 +1,850 b'' | |||
|
1 | 1 | /* ****************************************************************** |
|
2 | 2 | FSE : Finite State Entropy encoder |
|
3 | 3 | Copyright (C) 2013-2015, Yann Collet. |
|
4 | 4 | |
|
5 | 5 | BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) |
|
6 | 6 | |
|
7 | 7 | Redistribution and use in source and binary forms, with or without |
|
8 | 8 | modification, are permitted provided that the following conditions are |
|
9 | 9 | met: |
|
10 | 10 | |
|
11 | 11 | * Redistributions of source code must retain the above copyright |
|
12 | 12 | notice, this list of conditions and the following disclaimer. |
|
13 | 13 | * Redistributions in binary form must reproduce the above |
|
14 | 14 | copyright notice, this list of conditions and the following disclaimer |
|
15 | 15 | in the documentation and/or other materials provided with the |
|
16 | 16 | distribution. |
|
17 | 17 | |
|
18 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
19 | 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
20 | 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
21 | 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
22 | 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
23 | 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
24 | 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
25 | 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
26 | 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
27 | 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
28 | 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
29 | 29 | |
|
30 | 30 | You can contact the author at : |
|
31 | 31 | - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy |
|
32 | 32 | - Public forum : https://groups.google.com/forum/#!forum/lz4c |
|
33 | 33 | ****************************************************************** */ |
|
34 | 34 | |
|
35 | 35 | /* ************************************************************** |
|
36 | 36 | * Compiler specifics |
|
37 | 37 | ****************************************************************/ |
|
38 | 38 | #ifdef _MSC_VER /* Visual Studio */ |
|
39 | 39 | # define FORCE_INLINE static __forceinline |
|
40 | 40 | # include <intrin.h> /* For Visual 2005 */ |
|
41 | 41 | # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ |
|
42 | 42 | # pragma warning(disable : 4214) /* disable: C4214: non-int bitfields */ |
|
43 | 43 | #else |
|
44 | 44 | # if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ |
|
45 | 45 | # ifdef __GNUC__ |
|
46 | 46 | # define FORCE_INLINE static inline __attribute__((always_inline)) |
|
47 | 47 | # else |
|
48 | 48 | # define FORCE_INLINE static inline |
|
49 | 49 | # endif |
|
50 | 50 | # else |
|
51 | 51 | # define FORCE_INLINE static |
|
52 | 52 | # endif /* __STDC_VERSION__ */ |
|
53 | 53 | #endif |
|
54 | 54 | |
|
55 | 55 | |
|
56 | 56 | /* ************************************************************** |
|
57 | 57 | * Includes |
|
58 | 58 | ****************************************************************/ |
|
59 | 59 | #include <stdlib.h> /* malloc, free, qsort */ |
|
60 | 60 | #include <string.h> /* memcpy, memset */ |
|
61 | 61 | #include <stdio.h> /* printf (debug) */ |
|
62 | 62 | #include "bitstream.h" |
|
63 | 63 | #define FSE_STATIC_LINKING_ONLY |
|
64 | 64 | #include "fse.h" |
|
65 | 65 | |
|
66 | 66 | |
|
67 | 67 | /* ************************************************************** |
|
68 | 68 | * Error Management |
|
69 | 69 | ****************************************************************/ |
|
70 | 70 | #define FSE_STATIC_ASSERT(c) { enum { FSE_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */ |
|
71 | 71 | |
|
72 | 72 | |
|
73 | 73 | /* ************************************************************** |
|
74 | * Complex types | |
|
75 | ****************************************************************/ | |
|
76 | typedef U32 CTable_max_t[FSE_CTABLE_SIZE_U32(FSE_MAX_TABLELOG, FSE_MAX_SYMBOL_VALUE)]; | |
|
77 | ||
|
78 | ||
|
79 | /* ************************************************************** | |
|
80 | 74 | * Templates |
|
81 | 75 | ****************************************************************/ |
|
82 | 76 | /* |
|
83 | 77 | designed to be included |
|
84 | 78 | for type-specific functions (template emulation in C) |
|
85 | 79 | Objective is to write these functions only once, for improved maintenance |
|
86 | 80 | */ |
|
87 | 81 | |
|
88 | 82 | /* safety checks */ |
|
89 | 83 | #ifndef FSE_FUNCTION_EXTENSION |
|
90 | 84 | # error "FSE_FUNCTION_EXTENSION must be defined" |
|
91 | 85 | #endif |
|
92 | 86 | #ifndef FSE_FUNCTION_TYPE |
|
93 | 87 | # error "FSE_FUNCTION_TYPE must be defined" |
|
94 | 88 | #endif |
|
95 | 89 | |
|
96 | 90 | /* Function names */ |
|
97 | 91 | #define FSE_CAT(X,Y) X##Y |
|
98 | 92 | #define FSE_FUNCTION_NAME(X,Y) FSE_CAT(X,Y) |
|
99 | 93 | #define FSE_TYPE_NAME(X,Y) FSE_CAT(X,Y) |
|
100 | 94 | |
|
101 | 95 | |
|
102 | 96 | /* Function templates */ |
|
103 | size_t FSE_buildCTable(FSE_CTable* ct, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog) | |
|
97 | ||
|
98 | /* FSE_buildCTable_wksp() : | |
|
99 | * Same as FSE_buildCTable(), but using an externally allocated scratch buffer (`workSpace`). | |
|
100 | * wkspSize should be sized to handle worst case situation, which is `1<<max_tableLog * sizeof(FSE_FUNCTION_TYPE)` | |
|
101 | * workSpace must also be properly aligned with FSE_FUNCTION_TYPE requirements | |
|
102 | */ | |
|
103 | size_t FSE_buildCTable_wksp(FSE_CTable* ct, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize) | |
|
104 | 104 | { |
|
105 | 105 | U32 const tableSize = 1 << tableLog; |
|
106 | 106 | U32 const tableMask = tableSize - 1; |
|
107 | 107 | void* const ptr = ct; |
|
108 | 108 | U16* const tableU16 = ( (U16*) ptr) + 2; |
|
109 | 109 | void* const FSCT = ((U32*)ptr) + 1 /* header */ + (tableLog ? tableSize>>1 : 1) ; |
|
110 | 110 | FSE_symbolCompressionTransform* const symbolTT = (FSE_symbolCompressionTransform*) (FSCT); |
|
111 | 111 | U32 const step = FSE_TABLESTEP(tableSize); |
|
112 | 112 | U32 cumul[FSE_MAX_SYMBOL_VALUE+2]; |
|
113 | 113 | |
|
114 | FSE_FUNCTION_TYPE tableSymbol[FSE_MAX_TABLESIZE]; /* memset() is not necessary, even if static analyzer complain about it */ | |
|
114 | FSE_FUNCTION_TYPE* const tableSymbol = (FSE_FUNCTION_TYPE*)workSpace; | |
|
115 | 115 | U32 highThreshold = tableSize-1; |
|
116 | 116 | |
|
117 | 117 | /* CTable header */ |
|
118 | if (((size_t)1 << tableLog) * sizeof(FSE_FUNCTION_TYPE) > wkspSize) return ERROR(tableLog_tooLarge); | |
|
118 | 119 | tableU16[-2] = (U16) tableLog; |
|
119 | 120 | tableU16[-1] = (U16) maxSymbolValue; |
|
120 | 121 | |
|
121 | 122 | /* For explanations on how to distribute symbol values over the table : |
|
122 | 123 | * http://fastcompression.blogspot.fr/2014/02/fse-distributing-symbol-values.html */ |
|
123 | 124 | |
|
124 | 125 | /* symbol start positions */ |
|
125 | 126 | { U32 u; |
|
126 | 127 | cumul[0] = 0; |
|
127 | 128 | for (u=1; u<=maxSymbolValue+1; u++) { |
|
128 | 129 | if (normalizedCounter[u-1]==-1) { /* Low proba symbol */ |
|
129 | 130 | cumul[u] = cumul[u-1] + 1; |
|
130 | 131 | tableSymbol[highThreshold--] = (FSE_FUNCTION_TYPE)(u-1); |
|
131 | 132 | } else { |
|
132 | 133 | cumul[u] = cumul[u-1] + normalizedCounter[u-1]; |
|
133 | 134 | } } |
|
134 | 135 | cumul[maxSymbolValue+1] = tableSize+1; |
|
135 | 136 | } |
|
136 | 137 | |
|
137 | 138 | /* Spread symbols */ |
|
138 | 139 | { U32 position = 0; |
|
139 | 140 | U32 symbol; |
|
140 | 141 | for (symbol=0; symbol<=maxSymbolValue; symbol++) { |
|
141 | 142 | int nbOccurences; |
|
142 | 143 | for (nbOccurences=0; nbOccurences<normalizedCounter[symbol]; nbOccurences++) { |
|
143 | 144 | tableSymbol[position] = (FSE_FUNCTION_TYPE)symbol; |
|
144 | 145 | position = (position + step) & tableMask; |
|
145 | 146 | while (position > highThreshold) position = (position + step) & tableMask; /* Low proba area */ |
|
146 | 147 | } } |
|
147 | 148 | |
|
148 | 149 | if (position!=0) return ERROR(GENERIC); /* Must have gone through all positions */ |
|
149 | 150 | } |
|
150 | 151 | |
|
151 | 152 | /* Build table */ |
|
152 | 153 | { U32 u; for (u=0; u<tableSize; u++) { |
|
153 | 154 | FSE_FUNCTION_TYPE s = tableSymbol[u]; /* note : static analyzer may not understand tableSymbol is properly initialized */ |
|
154 | 155 | tableU16[cumul[s]++] = (U16) (tableSize+u); /* TableU16 : sorted by symbol order; gives next state value */ |
|
155 | 156 | } } |
|
156 | 157 | |
|
157 | 158 | /* Build Symbol Transformation Table */ |
|
158 | 159 | { unsigned total = 0; |
|
159 | 160 | unsigned s; |
|
160 | 161 | for (s=0; s<=maxSymbolValue; s++) { |
|
161 | 162 | switch (normalizedCounter[s]) |
|
162 | 163 | { |
|
163 | 164 | case 0: break; |
|
164 | 165 | |
|
165 | 166 | case -1: |
|
166 | 167 | case 1: |
|
167 | 168 | symbolTT[s].deltaNbBits = (tableLog << 16) - (1<<tableLog); |
|
168 | 169 | symbolTT[s].deltaFindState = total - 1; |
|
169 | 170 | total ++; |
|
170 | 171 | break; |
|
171 | 172 | default : |
|
172 | 173 | { |
|
173 | 174 | U32 const maxBitsOut = tableLog - BIT_highbit32 (normalizedCounter[s]-1); |
|
174 | 175 | U32 const minStatePlus = normalizedCounter[s] << maxBitsOut; |
|
175 | 176 | symbolTT[s].deltaNbBits = (maxBitsOut << 16) - minStatePlus; |
|
176 | 177 | symbolTT[s].deltaFindState = total - normalizedCounter[s]; |
|
177 | 178 | total += normalizedCounter[s]; |
|
178 | 179 | } } } } |
|
179 | 180 | |
|
180 | 181 | return 0; |
|
181 | 182 | } |
|
182 | 183 | |
|
183 | 184 | |
|
185 | size_t FSE_buildCTable(FSE_CTable* ct, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog) | |
|
186 | { | |
|
187 | FSE_FUNCTION_TYPE tableSymbol[FSE_MAX_TABLESIZE]; /* memset() is not necessary, even if static analyzer complain about it */ | |
|
188 | return FSE_buildCTable_wksp(ct, normalizedCounter, maxSymbolValue, tableLog, tableSymbol, sizeof(tableSymbol)); | |
|
189 | } | |
|
190 | ||
|
191 | ||
|
184 | 192 | |
|
185 | 193 | #ifndef FSE_COMMONDEFS_ONLY |
|
186 | 194 | |
|
187 | 195 | /*-************************************************************** |
|
188 | 196 | * FSE NCount encoding-decoding |
|
189 | 197 | ****************************************************************/ |
|
190 | 198 | size_t FSE_NCountWriteBound(unsigned maxSymbolValue, unsigned tableLog) |
|
191 | 199 | { |
|
192 | size_t maxHeaderSize = (((maxSymbolValue+1) * tableLog) >> 3) + 3; | |
|
200 | size_t const maxHeaderSize = (((maxSymbolValue+1) * tableLog) >> 3) + 3; | |
|
193 | 201 | return maxSymbolValue ? maxHeaderSize : FSE_NCOUNTBOUND; /* maxSymbolValue==0 ? use default */ |
|
194 | 202 | } |
|
195 | 203 | |
|
196 | 204 | static short FSE_abs(short a) { return (short)(a<0 ? -a : a); } |
|
197 | 205 | |
|
198 | 206 | static size_t FSE_writeNCount_generic (void* header, size_t headerBufferSize, |
|
199 | 207 | const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, |
|
200 | 208 | unsigned writeIsSafe) |
|
201 | 209 | { |
|
202 | 210 | BYTE* const ostart = (BYTE*) header; |
|
203 | 211 | BYTE* out = ostart; |
|
204 | 212 | BYTE* const oend = ostart + headerBufferSize; |
|
205 | 213 | int nbBits; |
|
206 | 214 | const int tableSize = 1 << tableLog; |
|
207 | 215 | int remaining; |
|
208 | 216 | int threshold; |
|
209 | 217 | U32 bitStream; |
|
210 | 218 | int bitCount; |
|
211 | 219 | unsigned charnum = 0; |
|
212 | 220 | int previous0 = 0; |
|
213 | 221 | |
|
214 | 222 | bitStream = 0; |
|
215 | 223 | bitCount = 0; |
|
216 | 224 | /* Table Size */ |
|
217 | 225 | bitStream += (tableLog-FSE_MIN_TABLELOG) << bitCount; |
|
218 | 226 | bitCount += 4; |
|
219 | 227 | |
|
220 | 228 | /* Init */ |
|
221 | 229 | remaining = tableSize+1; /* +1 for extra accuracy */ |
|
222 | 230 | threshold = tableSize; |
|
223 | 231 | nbBits = tableLog+1; |
|
224 | 232 | |
|
225 | 233 | while (remaining>1) { /* stops at 1 */ |
|
226 | 234 | if (previous0) { |
|
227 | 235 | unsigned start = charnum; |
|
228 | 236 | while (!normalizedCounter[charnum]) charnum++; |
|
229 | 237 | while (charnum >= start+24) { |
|
230 | 238 | start+=24; |
|
231 | 239 | bitStream += 0xFFFFU << bitCount; |
|
232 | 240 | if ((!writeIsSafe) && (out > oend-2)) return ERROR(dstSize_tooSmall); /* Buffer overflow */ |
|
233 | 241 | out[0] = (BYTE) bitStream; |
|
234 | 242 | out[1] = (BYTE)(bitStream>>8); |
|
235 | 243 | out+=2; |
|
236 | 244 | bitStream>>=16; |
|
237 | 245 | } |
|
238 | 246 | while (charnum >= start+3) { |
|
239 | 247 | start+=3; |
|
240 | 248 | bitStream += 3 << bitCount; |
|
241 | 249 | bitCount += 2; |
|
242 | 250 | } |
|
243 | 251 | bitStream += (charnum-start) << bitCount; |
|
244 | 252 | bitCount += 2; |
|
245 | 253 | if (bitCount>16) { |
|
246 | 254 | if ((!writeIsSafe) && (out > oend - 2)) return ERROR(dstSize_tooSmall); /* Buffer overflow */ |
|
247 | 255 | out[0] = (BYTE)bitStream; |
|
248 | 256 | out[1] = (BYTE)(bitStream>>8); |
|
249 | 257 | out += 2; |
|
250 | 258 | bitStream >>= 16; |
|
251 | 259 | bitCount -= 16; |
|
252 | 260 | } } |
|
253 | 261 | { short count = normalizedCounter[charnum++]; |
|
254 | 262 | const short max = (short)((2*threshold-1)-remaining); |
|
255 | 263 | remaining -= FSE_abs(count); |
|
256 | 264 | if (remaining<1) return ERROR(GENERIC); |
|
257 | 265 | count++; /* +1 for extra accuracy */ |
|
258 | 266 | if (count>=threshold) count += max; /* [0..max[ [max..threshold[ (...) [threshold+max 2*threshold[ */ |
|
259 | 267 | bitStream += count << bitCount; |
|
260 | 268 | bitCount += nbBits; |
|
261 | 269 | bitCount -= (count<max); |
|
262 | 270 | previous0 = (count==1); |
|
263 | 271 | while (remaining<threshold) nbBits--, threshold>>=1; |
|
264 | 272 | } |
|
265 | 273 | if (bitCount>16) { |
|
266 | 274 | if ((!writeIsSafe) && (out > oend - 2)) return ERROR(dstSize_tooSmall); /* Buffer overflow */ |
|
267 | 275 | out[0] = (BYTE)bitStream; |
|
268 | 276 | out[1] = (BYTE)(bitStream>>8); |
|
269 | 277 | out += 2; |
|
270 | 278 | bitStream >>= 16; |
|
271 | 279 | bitCount -= 16; |
|
272 | 280 | } } |
|
273 | 281 | |
|
274 | 282 | /* flush remaining bitStream */ |
|
275 | 283 | if ((!writeIsSafe) && (out > oend - 2)) return ERROR(dstSize_tooSmall); /* Buffer overflow */ |
|
276 | 284 | out[0] = (BYTE)bitStream; |
|
277 | 285 | out[1] = (BYTE)(bitStream>>8); |
|
278 | 286 | out+= (bitCount+7) /8; |
|
279 | 287 | |
|
280 | 288 | if (charnum > maxSymbolValue + 1) return ERROR(GENERIC); |
|
281 | 289 | |
|
282 | 290 | return (out-ostart); |
|
283 | 291 | } |
|
284 | 292 | |
|
285 | 293 | |
|
286 | 294 | size_t FSE_writeNCount (void* buffer, size_t bufferSize, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog) |
|
287 | 295 | { |
|
288 | 296 | if (tableLog > FSE_MAX_TABLELOG) return ERROR(GENERIC); /* Unsupported */ |
|
289 | 297 | if (tableLog < FSE_MIN_TABLELOG) return ERROR(GENERIC); /* Unsupported */ |
|
290 | 298 | |
|
291 | 299 | if (bufferSize < FSE_NCountWriteBound(maxSymbolValue, tableLog)) |
|
292 | 300 | return FSE_writeNCount_generic(buffer, bufferSize, normalizedCounter, maxSymbolValue, tableLog, 0); |
|
293 | 301 | |
|
294 | 302 | return FSE_writeNCount_generic(buffer, bufferSize, normalizedCounter, maxSymbolValue, tableLog, 1); |
|
295 | 303 | } |
|
296 | 304 | |
|
297 | 305 | |
|
298 | 306 | |
|
299 | 307 | /*-************************************************************** |
|
300 | 308 | * Counting histogram |
|
301 | 309 | ****************************************************************/ |
|
302 | 310 | /*! FSE_count_simple |
|
303 |
This function |
|
|
304 | and store the histogram into table `count`. | |
|
305 |
|
|
|
311 | This function counts byte values within `src`, and store the histogram into table `count`. | |
|
312 | It doesn't use any additional memory. | |
|
313 | But this function is unsafe : it doesn't check that all values within `src` can fit into `count`. | |
|
306 | 314 | For this reason, prefer using a table `count` with 256 elements. |
|
307 | 315 | @return : count of most numerous element |
|
308 | 316 | */ |
|
309 |
|
|
|
310 |
|
|
|
317 | size_t FSE_count_simple(unsigned* count, unsigned* maxSymbolValuePtr, | |
|
318 | const void* src, size_t srcSize) | |
|
311 | 319 | { |
|
312 | 320 | const BYTE* ip = (const BYTE*)src; |
|
313 | 321 | const BYTE* const end = ip + srcSize; |
|
314 | 322 | unsigned maxSymbolValue = *maxSymbolValuePtr; |
|
315 | 323 | unsigned max=0; |
|
316 | 324 | |
|
317 | ||
|
318 | 325 | memset(count, 0, (maxSymbolValue+1)*sizeof(*count)); |
|
319 | 326 | if (srcSize==0) { *maxSymbolValuePtr = 0; return 0; } |
|
320 | 327 | |
|
321 | 328 | while (ip<end) count[*ip++]++; |
|
322 | 329 | |
|
323 | 330 | while (!count[maxSymbolValue]) maxSymbolValue--; |
|
324 | 331 | *maxSymbolValuePtr = maxSymbolValue; |
|
325 | 332 | |
|
326 | 333 | { U32 s; for (s=0; s<=maxSymbolValue; s++) if (count[s] > max) max = count[s]; } |
|
327 | 334 | |
|
328 | 335 | return (size_t)max; |
|
329 | 336 | } |
|
330 | 337 | |
|
331 | 338 | |
|
332 | static size_t FSE_count_parallel(unsigned* count, unsigned* maxSymbolValuePtr, | |
|
339 | /* FSE_count_parallel_wksp() : | |
|
340 | * Same as FSE_count_parallel(), but using an externally provided scratch buffer. | |
|
341 | * `workSpace` size must be a minimum of `1024 * sizeof(unsigned)`` */ | |
|
342 | static size_t FSE_count_parallel_wksp( | |
|
343 | unsigned* count, unsigned* maxSymbolValuePtr, | |
|
333 | 344 | const void* source, size_t sourceSize, |
|
334 | unsigned checkMax) | |
|
345 | unsigned checkMax, unsigned* const workSpace) | |
|
335 | 346 | { |
|
336 | 347 | const BYTE* ip = (const BYTE*)source; |
|
337 | 348 | const BYTE* const iend = ip+sourceSize; |
|
338 | 349 | unsigned maxSymbolValue = *maxSymbolValuePtr; |
|
339 | 350 | unsigned max=0; |
|
340 | ||
|
351 | U32* const Counting1 = workSpace; | |
|
352 | U32* const Counting2 = Counting1 + 256; | |
|
353 | U32* const Counting3 = Counting2 + 256; | |
|
354 | U32* const Counting4 = Counting3 + 256; | |
|
341 | 355 | |
|
342 | U32 Counting1[256] = { 0 }; | |
|
343 | U32 Counting2[256] = { 0 }; | |
|
344 | U32 Counting3[256] = { 0 }; | |
|
345 | U32 Counting4[256] = { 0 }; | |
|
356 | memset(Counting1, 0, 4*256*sizeof(unsigned)); | |
|
346 | 357 | |
|
347 | 358 | /* safety checks */ |
|
348 | 359 | if (!sourceSize) { |
|
349 | 360 | memset(count, 0, maxSymbolValue + 1); |
|
350 | 361 | *maxSymbolValuePtr = 0; |
|
351 | 362 | return 0; |
|
352 | 363 | } |
|
353 | 364 | if (!maxSymbolValue) maxSymbolValue = 255; /* 0 == default */ |
|
354 | 365 | |
|
355 | 366 | /* by stripes of 16 bytes */ |
|
356 | 367 | { U32 cached = MEM_read32(ip); ip += 4; |
|
357 | 368 | while (ip < iend-15) { |
|
358 | 369 | U32 c = cached; cached = MEM_read32(ip); ip += 4; |
|
359 | 370 | Counting1[(BYTE) c ]++; |
|
360 | 371 | Counting2[(BYTE)(c>>8) ]++; |
|
361 | 372 | Counting3[(BYTE)(c>>16)]++; |
|
362 | 373 | Counting4[ c>>24 ]++; |
|
363 | 374 | c = cached; cached = MEM_read32(ip); ip += 4; |
|
364 | 375 | Counting1[(BYTE) c ]++; |
|
365 | 376 | Counting2[(BYTE)(c>>8) ]++; |
|
366 | 377 | Counting3[(BYTE)(c>>16)]++; |
|
367 | 378 | Counting4[ c>>24 ]++; |
|
368 | 379 | c = cached; cached = MEM_read32(ip); ip += 4; |
|
369 | 380 | Counting1[(BYTE) c ]++; |
|
370 | 381 | Counting2[(BYTE)(c>>8) ]++; |
|
371 | 382 | Counting3[(BYTE)(c>>16)]++; |
|
372 | 383 | Counting4[ c>>24 ]++; |
|
373 | 384 | c = cached; cached = MEM_read32(ip); ip += 4; |
|
374 | 385 | Counting1[(BYTE) c ]++; |
|
375 | 386 | Counting2[(BYTE)(c>>8) ]++; |
|
376 | 387 | Counting3[(BYTE)(c>>16)]++; |
|
377 | 388 | Counting4[ c>>24 ]++; |
|
378 | 389 | } |
|
379 | 390 | ip-=4; |
|
380 | 391 | } |
|
381 | 392 | |
|
382 | 393 | /* finish last symbols */ |
|
383 | 394 | while (ip<iend) Counting1[*ip++]++; |
|
384 | 395 | |
|
385 | 396 | if (checkMax) { /* verify stats will fit into destination table */ |
|
386 | 397 | U32 s; for (s=255; s>maxSymbolValue; s--) { |
|
387 | 398 | Counting1[s] += Counting2[s] + Counting3[s] + Counting4[s]; |
|
388 | 399 | if (Counting1[s]) return ERROR(maxSymbolValue_tooSmall); |
|
389 | 400 | } } |
|
390 | 401 | |
|
391 | { U32 s; for (s=0; s<=maxSymbolValue; s++) { | |
|
392 | count[s] = Counting1[s] + Counting2[s] + Counting3[s] + Counting4[s]; | |
|
393 | if (count[s] > max) max = count[s]; | |
|
394 | }} | |
|
402 | { U32 s; for (s=0; s<=maxSymbolValue; s++) { | |
|
403 | count[s] = Counting1[s] + Counting2[s] + Counting3[s] + Counting4[s]; | |
|
404 | if (count[s] > max) max = count[s]; | |
|
405 | } } | |
|
395 | 406 | |
|
396 | 407 | while (!count[maxSymbolValue]) maxSymbolValue--; |
|
397 | 408 | *maxSymbolValuePtr = maxSymbolValue; |
|
398 | 409 | return (size_t)max; |
|
399 | 410 | } |
|
400 | 411 | |
|
412 | /* FSE_countFast_wksp() : | |
|
413 | * Same as FSE_countFast(), but using an externally provided scratch buffer. | |
|
414 | * `workSpace` size must be table of >= `1024` unsigned */ | |
|
415 | size_t FSE_countFast_wksp(unsigned* count, unsigned* maxSymbolValuePtr, | |
|
416 | const void* source, size_t sourceSize, unsigned* workSpace) | |
|
417 | { | |
|
418 | if (sourceSize < 1500) return FSE_count_simple(count, maxSymbolValuePtr, source, sourceSize); | |
|
419 | return FSE_count_parallel_wksp(count, maxSymbolValuePtr, source, sourceSize, 0, workSpace); | |
|
420 | } | |
|
421 | ||
|
401 | 422 | /* fast variant (unsafe : won't check if src contains values beyond count[] limit) */ |
|
402 | 423 | size_t FSE_countFast(unsigned* count, unsigned* maxSymbolValuePtr, |
|
403 | 424 | const void* source, size_t sourceSize) |
|
404 | 425 | { |
|
405 | if (sourceSize < 1500) return FSE_count_simple(count, maxSymbolValuePtr, source, sourceSize); | |
|
406 |
return FSE_count_p |
|
|
426 | unsigned tmpCounters[1024]; | |
|
427 | return FSE_countFast_wksp(count, maxSymbolValuePtr, source, sourceSize, tmpCounters); | |
|
428 | } | |
|
429 | ||
|
430 | /* FSE_count_wksp() : | |
|
431 | * Same as FSE_count(), but using an externally provided scratch buffer. | |
|
432 | * `workSpace` size must be table of >= `1024` unsigned */ | |
|
433 | size_t FSE_count_wksp(unsigned* count, unsigned* maxSymbolValuePtr, | |
|
434 | const void* source, size_t sourceSize, unsigned* workSpace) | |
|
435 | { | |
|
436 | if (*maxSymbolValuePtr < 255) | |
|
437 | return FSE_count_parallel_wksp(count, maxSymbolValuePtr, source, sourceSize, 1, workSpace); | |
|
438 | *maxSymbolValuePtr = 255; | |
|
439 | return FSE_countFast_wksp(count, maxSymbolValuePtr, source, sourceSize, workSpace); | |
|
407 | 440 | } |
|
408 | 441 | |
|
409 | 442 | size_t FSE_count(unsigned* count, unsigned* maxSymbolValuePtr, |
|
410 |
const void* s |
|
|
443 | const void* src, size_t srcSize) | |
|
411 | 444 | { |
|
412 | if (*maxSymbolValuePtr <255) | |
|
413 |
|
|
|
414 | *maxSymbolValuePtr = 255; | |
|
415 | return FSE_countFast(count, maxSymbolValuePtr, source, sourceSize); | |
|
445 | unsigned tmpCounters[1024]; | |
|
446 | return FSE_count_wksp(count, maxSymbolValuePtr, src, srcSize, tmpCounters); | |
|
416 | 447 | } |
|
417 | 448 | |
|
418 | 449 | |
|
419 | 450 | |
|
420 | 451 | /*-************************************************************** |
|
421 | 452 | * FSE Compression Code |
|
422 | 453 | ****************************************************************/ |
|
423 | 454 | /*! FSE_sizeof_CTable() : |
|
424 | 455 | FSE_CTable is a variable size structure which contains : |
|
425 | 456 | `U16 tableLog;` |
|
426 | 457 | `U16 maxSymbolValue;` |
|
427 | 458 | `U16 nextStateNumber[1 << tableLog];` // This size is variable |
|
428 | 459 | `FSE_symbolCompressionTransform symbolTT[maxSymbolValue+1];` // This size is variable |
|
429 | 460 | Allocation is manual (C standard does not support variable-size structures). |
|
430 | 461 | */ |
|
431 | ||
|
432 | 462 | size_t FSE_sizeof_CTable (unsigned maxSymbolValue, unsigned tableLog) |
|
433 | 463 | { |
|
434 | size_t size; | |
|
435 | FSE_STATIC_ASSERT((size_t)FSE_CTABLE_SIZE_U32(FSE_MAX_TABLELOG, FSE_MAX_SYMBOL_VALUE)*4 >= sizeof(CTable_max_t)); /* A compilation error here means FSE_CTABLE_SIZE_U32 is not large enough */ | |
|
436 | if (tableLog > FSE_MAX_TABLELOG) return ERROR(GENERIC); | |
|
437 | size = FSE_CTABLE_SIZE_U32 (tableLog, maxSymbolValue) * sizeof(U32); | |
|
438 | return size; | |
|
464 | if (tableLog > FSE_MAX_TABLELOG) return ERROR(tableLog_tooLarge); | |
|
465 | return FSE_CTABLE_SIZE_U32 (tableLog, maxSymbolValue) * sizeof(U32); | |
|
439 | 466 | } |
|
440 | 467 | |
|
441 | 468 | FSE_CTable* FSE_createCTable (unsigned maxSymbolValue, unsigned tableLog) |
|
442 | 469 | { |
|
443 | 470 | size_t size; |
|
444 | 471 | if (tableLog > FSE_TABLELOG_ABSOLUTE_MAX) tableLog = FSE_TABLELOG_ABSOLUTE_MAX; |
|
445 | 472 | size = FSE_CTABLE_SIZE_U32 (tableLog, maxSymbolValue) * sizeof(U32); |
|
446 | 473 | return (FSE_CTable*)malloc(size); |
|
447 | 474 | } |
|
448 | 475 | |
|
449 | 476 | void FSE_freeCTable (FSE_CTable* ct) { free(ct); } |
|
450 | 477 | |
|
451 | 478 | /* provides the minimum logSize to safely represent a distribution */ |
|
452 | 479 | static unsigned FSE_minTableLog(size_t srcSize, unsigned maxSymbolValue) |
|
453 | 480 | { |
|
454 | 481 | U32 minBitsSrc = BIT_highbit32((U32)(srcSize - 1)) + 1; |
|
455 | 482 | U32 minBitsSymbols = BIT_highbit32(maxSymbolValue) + 2; |
|
456 | 483 | U32 minBits = minBitsSrc < minBitsSymbols ? minBitsSrc : minBitsSymbols; |
|
457 | 484 | return minBits; |
|
458 | 485 | } |
|
459 | 486 | |
|
460 | 487 | unsigned FSE_optimalTableLog_internal(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue, unsigned minus) |
|
461 | 488 | { |
|
462 | 489 | U32 maxBitsSrc = BIT_highbit32((U32)(srcSize - 1)) - minus; |
|
463 | 490 | U32 tableLog = maxTableLog; |
|
464 | 491 | U32 minBits = FSE_minTableLog(srcSize, maxSymbolValue); |
|
465 | 492 | if (tableLog==0) tableLog = FSE_DEFAULT_TABLELOG; |
|
466 | 493 | if (maxBitsSrc < tableLog) tableLog = maxBitsSrc; /* Accuracy can be reduced */ |
|
467 | 494 | if (minBits > tableLog) tableLog = minBits; /* Need a minimum to safely represent all symbol values */ |
|
468 | 495 | if (tableLog < FSE_MIN_TABLELOG) tableLog = FSE_MIN_TABLELOG; |
|
469 | 496 | if (tableLog > FSE_MAX_TABLELOG) tableLog = FSE_MAX_TABLELOG; |
|
470 | 497 | return tableLog; |
|
471 | 498 | } |
|
472 | 499 | |
|
473 | 500 | unsigned FSE_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue) |
|
474 | 501 | { |
|
475 | 502 | return FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 2); |
|
476 | 503 | } |
|
477 | 504 | |
|
478 | 505 | |
|
479 | 506 | /* Secondary normalization method. |
|
480 | 507 | To be used when primary method fails. */ |
|
481 | 508 | |
|
482 | 509 | static size_t FSE_normalizeM2(short* norm, U32 tableLog, const unsigned* count, size_t total, U32 maxSymbolValue) |
|
483 | 510 | { |
|
484 | 511 | U32 s; |
|
485 | 512 | U32 distributed = 0; |
|
486 | 513 | U32 ToDistribute; |
|
487 | 514 | |
|
488 | 515 | /* Init */ |
|
489 | U32 lowThreshold = (U32)(total >> tableLog); | |
|
516 | U32 const lowThreshold = (U32)(total >> tableLog); | |
|
490 | 517 | U32 lowOne = (U32)((total * 3) >> (tableLog + 1)); |
|
491 | 518 | |
|
492 | 519 | for (s=0; s<=maxSymbolValue; s++) { |
|
493 | 520 | if (count[s] == 0) { |
|
494 | 521 | norm[s]=0; |
|
495 | 522 | continue; |
|
496 | 523 | } |
|
497 | 524 | if (count[s] <= lowThreshold) { |
|
498 | 525 | norm[s] = -1; |
|
499 | 526 | distributed++; |
|
500 | 527 | total -= count[s]; |
|
501 | 528 | continue; |
|
502 | 529 | } |
|
503 | 530 | if (count[s] <= lowOne) { |
|
504 | 531 | norm[s] = 1; |
|
505 | 532 | distributed++; |
|
506 | 533 | total -= count[s]; |
|
507 | 534 | continue; |
|
508 | 535 | } |
|
509 | 536 | norm[s]=-2; |
|
510 | 537 | } |
|
511 | 538 | ToDistribute = (1 << tableLog) - distributed; |
|
512 | 539 | |
|
513 | 540 | if ((total / ToDistribute) > lowOne) { |
|
514 | 541 | /* risk of rounding to zero */ |
|
515 | 542 | lowOne = (U32)((total * 3) / (ToDistribute * 2)); |
|
516 | 543 | for (s=0; s<=maxSymbolValue; s++) { |
|
517 | 544 | if ((norm[s] == -2) && (count[s] <= lowOne)) { |
|
518 | 545 | norm[s] = 1; |
|
519 | 546 | distributed++; |
|
520 | 547 | total -= count[s]; |
|
521 | 548 | continue; |
|
522 | 549 | } } |
|
523 | 550 | ToDistribute = (1 << tableLog) - distributed; |
|
524 | 551 | } |
|
525 | 552 | |
|
526 | 553 | if (distributed == maxSymbolValue+1) { |
|
527 | 554 | /* all values are pretty poor; |
|
528 | 555 | probably incompressible data (should have already been detected); |
|
529 | 556 | find max, then give all remaining points to max */ |
|
530 | 557 | U32 maxV = 0, maxC = 0; |
|
531 | 558 | for (s=0; s<=maxSymbolValue; s++) |
|
532 | 559 | if (count[s] > maxC) maxV=s, maxC=count[s]; |
|
533 | 560 | norm[maxV] += (short)ToDistribute; |
|
534 | 561 | return 0; |
|
535 | 562 | } |
|
536 | 563 | |
|
537 | { | |
|
538 | U64 const vStepLog = 62 - tableLog; | |
|
564 | { U64 const vStepLog = 62 - tableLog; | |
|
539 | 565 | U64 const mid = (1ULL << (vStepLog-1)) - 1; |
|
540 | 566 | U64 const rStep = ((((U64)1<<vStepLog) * ToDistribute) + mid) / total; /* scale on remaining */ |
|
541 | 567 | U64 tmpTotal = mid; |
|
542 | 568 | for (s=0; s<=maxSymbolValue; s++) { |
|
543 | 569 | if (norm[s]==-2) { |
|
544 | U64 end = tmpTotal + (count[s] * rStep); | |
|
545 | U32 sStart = (U32)(tmpTotal >> vStepLog); | |
|
546 | U32 sEnd = (U32)(end >> vStepLog); | |
|
547 | U32 weight = sEnd - sStart; | |
|
570 | U64 const end = tmpTotal + (count[s] * rStep); | |
|
571 | U32 const sStart = (U32)(tmpTotal >> vStepLog); | |
|
572 | U32 const sEnd = (U32)(end >> vStepLog); | |
|
573 | U32 const weight = sEnd - sStart; | |
|
548 | 574 | if (weight < 1) |
|
549 | 575 | return ERROR(GENERIC); |
|
550 | 576 | norm[s] = (short)weight; |
|
551 | 577 | tmpTotal = end; |
|
552 | 578 | } } } |
|
553 | 579 | |
|
554 | 580 | return 0; |
|
555 | 581 | } |
|
556 | 582 | |
|
557 | 583 | |
|
558 | 584 | size_t FSE_normalizeCount (short* normalizedCounter, unsigned tableLog, |
|
559 | 585 | const unsigned* count, size_t total, |
|
560 | 586 | unsigned maxSymbolValue) |
|
561 | 587 | { |
|
562 | 588 | /* Sanity checks */ |
|
563 | 589 | if (tableLog==0) tableLog = FSE_DEFAULT_TABLELOG; |
|
564 | 590 | if (tableLog < FSE_MIN_TABLELOG) return ERROR(GENERIC); /* Unsupported size */ |
|
565 | 591 | if (tableLog > FSE_MAX_TABLELOG) return ERROR(tableLog_tooLarge); /* Unsupported size */ |
|
566 | 592 | if (tableLog < FSE_minTableLog(total, maxSymbolValue)) return ERROR(GENERIC); /* Too small tableLog, compression potentially impossible */ |
|
567 | 593 | |
|
568 | 594 | { U32 const rtbTable[] = { 0, 473195, 504333, 520860, 550000, 700000, 750000, 830000 }; |
|
569 | ||
|
570 | 595 | U64 const scale = 62 - tableLog; |
|
571 | 596 | U64 const step = ((U64)1<<62) / total; /* <== here, one division ! */ |
|
572 | 597 | U64 const vStep = 1ULL<<(scale-20); |
|
573 | 598 | int stillToDistribute = 1<<tableLog; |
|
574 | 599 | unsigned s; |
|
575 | 600 | unsigned largest=0; |
|
576 | 601 | short largestP=0; |
|
577 | 602 | U32 lowThreshold = (U32)(total >> tableLog); |
|
578 | 603 | |
|
579 | 604 | for (s=0; s<=maxSymbolValue; s++) { |
|
580 | 605 | if (count[s] == total) return 0; /* rle special case */ |
|
581 | 606 | if (count[s] == 0) { normalizedCounter[s]=0; continue; } |
|
582 | 607 | if (count[s] <= lowThreshold) { |
|
583 | 608 | normalizedCounter[s] = -1; |
|
584 | 609 | stillToDistribute--; |
|
585 | 610 | } else { |
|
586 | 611 | short proba = (short)((count[s]*step) >> scale); |
|
587 | 612 | if (proba<8) { |
|
588 | 613 | U64 restToBeat = vStep * rtbTable[proba]; |
|
589 | 614 | proba += (count[s]*step) - ((U64)proba<<scale) > restToBeat; |
|
590 | 615 | } |
|
591 | 616 | if (proba > largestP) largestP=proba, largest=s; |
|
592 | 617 | normalizedCounter[s] = proba; |
|
593 | 618 | stillToDistribute -= proba; |
|
594 | 619 | } } |
|
595 | 620 | if (-stillToDistribute >= (normalizedCounter[largest] >> 1)) { |
|
596 | 621 | /* corner case, need another normalization method */ |
|
597 | size_t errorCode = FSE_normalizeM2(normalizedCounter, tableLog, count, total, maxSymbolValue); | |
|
622 | size_t const errorCode = FSE_normalizeM2(normalizedCounter, tableLog, count, total, maxSymbolValue); | |
|
598 | 623 | if (FSE_isError(errorCode)) return errorCode; |
|
599 | 624 | } |
|
600 | 625 | else normalizedCounter[largest] += (short)stillToDistribute; |
|
601 | 626 | } |
|
602 | 627 | |
|
603 | 628 | #if 0 |
|
604 | 629 | { /* Print Table (debug) */ |
|
605 | 630 | U32 s; |
|
606 | 631 | U32 nTotal = 0; |
|
607 | 632 | for (s=0; s<=maxSymbolValue; s++) |
|
608 | 633 | printf("%3i: %4i \n", s, normalizedCounter[s]); |
|
609 | 634 | for (s=0; s<=maxSymbolValue; s++) |
|
610 | 635 | nTotal += abs(normalizedCounter[s]); |
|
611 | 636 | if (nTotal != (1U<<tableLog)) |
|
612 | 637 | printf("Warning !!! Total == %u != %u !!!", nTotal, 1U<<tableLog); |
|
613 | 638 | getchar(); |
|
614 | 639 | } |
|
615 | 640 | #endif |
|
616 | 641 | |
|
617 | 642 | return tableLog; |
|
618 | 643 | } |
|
619 | 644 | |
|
620 | 645 | |
|
621 | 646 | /* fake FSE_CTable, for raw (uncompressed) input */ |
|
622 | 647 | size_t FSE_buildCTable_raw (FSE_CTable* ct, unsigned nbBits) |
|
623 | 648 | { |
|
624 | 649 | const unsigned tableSize = 1 << nbBits; |
|
625 | 650 | const unsigned tableMask = tableSize - 1; |
|
626 | 651 | const unsigned maxSymbolValue = tableMask; |
|
627 | 652 | void* const ptr = ct; |
|
628 | 653 | U16* const tableU16 = ( (U16*) ptr) + 2; |
|
629 | 654 | void* const FSCT = ((U32*)ptr) + 1 /* header */ + (tableSize>>1); /* assumption : tableLog >= 1 */ |
|
630 | 655 | FSE_symbolCompressionTransform* const symbolTT = (FSE_symbolCompressionTransform*) (FSCT); |
|
631 | 656 | unsigned s; |
|
632 | 657 | |
|
633 | 658 | /* Sanity checks */ |
|
634 | 659 | if (nbBits < 1) return ERROR(GENERIC); /* min size */ |
|
635 | 660 | |
|
636 | 661 | /* header */ |
|
637 | 662 | tableU16[-2] = (U16) nbBits; |
|
638 | 663 | tableU16[-1] = (U16) maxSymbolValue; |
|
639 | 664 | |
|
640 | 665 | /* Build table */ |
|
641 | 666 | for (s=0; s<tableSize; s++) |
|
642 | 667 | tableU16[s] = (U16)(tableSize + s); |
|
643 | 668 | |
|
644 | 669 | /* Build Symbol Transformation Table */ |
|
645 | 670 | { const U32 deltaNbBits = (nbBits << 16) - (1 << nbBits); |
|
646 | ||
|
647 | 671 | for (s=0; s<=maxSymbolValue; s++) { |
|
648 | 672 | symbolTT[s].deltaNbBits = deltaNbBits; |
|
649 | 673 | symbolTT[s].deltaFindState = s-1; |
|
650 | 674 | } } |
|
651 | 675 | |
|
652 | ||
|
653 | 676 | return 0; |
|
654 | 677 | } |
|
655 | 678 | |
|
656 |
/* fake FSE_CTable, for rle ( |
|
|
679 | /* fake FSE_CTable, for rle input (always same symbol) */ | |
|
657 | 680 | size_t FSE_buildCTable_rle (FSE_CTable* ct, BYTE symbolValue) |
|
658 | 681 | { |
|
659 | 682 | void* ptr = ct; |
|
660 | 683 | U16* tableU16 = ( (U16*) ptr) + 2; |
|
661 | 684 | void* FSCTptr = (U32*)ptr + 2; |
|
662 | 685 | FSE_symbolCompressionTransform* symbolTT = (FSE_symbolCompressionTransform*) FSCTptr; |
|
663 | 686 | |
|
664 | 687 | /* header */ |
|
665 | 688 | tableU16[-2] = (U16) 0; |
|
666 | 689 | tableU16[-1] = (U16) symbolValue; |
|
667 | 690 | |
|
668 | 691 | /* Build table */ |
|
669 | 692 | tableU16[0] = 0; |
|
670 | 693 | tableU16[1] = 0; /* just in case */ |
|
671 | 694 | |
|
672 | 695 | /* Build Symbol Transformation Table */ |
|
673 | 696 | symbolTT[symbolValue].deltaNbBits = 0; |
|
674 | 697 | symbolTT[symbolValue].deltaFindState = 0; |
|
675 | 698 | |
|
676 | 699 | return 0; |
|
677 | 700 | } |
|
678 | 701 | |
|
679 | 702 | |
|
680 | 703 | static size_t FSE_compress_usingCTable_generic (void* dst, size_t dstSize, |
|
681 | 704 | const void* src, size_t srcSize, |
|
682 | 705 | const FSE_CTable* ct, const unsigned fast) |
|
683 | 706 | { |
|
684 | 707 | const BYTE* const istart = (const BYTE*) src; |
|
685 | 708 | const BYTE* const iend = istart + srcSize; |
|
686 | 709 | const BYTE* ip=iend; |
|
687 | 710 | |
|
688 | ||
|
689 | 711 | BIT_CStream_t bitC; |
|
690 | 712 | FSE_CState_t CState1, CState2; |
|
691 | 713 | |
|
692 | 714 | /* init */ |
|
693 | 715 | if (srcSize <= 2) return 0; |
|
694 |
{ size_t const |
|
|
695 | if (FSE_isError(errorCode)) return 0; } | |
|
716 | { size_t const initError = BIT_initCStream(&bitC, dst, dstSize); | |
|
717 | if (FSE_isError(initError)) return 0; /* not enough space available to write a bitstream */ } | |
|
696 | 718 | |
|
697 | 719 | #define FSE_FLUSHBITS(s) (fast ? BIT_flushBitsFast(s) : BIT_flushBits(s)) |
|
698 | 720 | |
|
699 | 721 | if (srcSize & 1) { |
|
700 | 722 | FSE_initCState2(&CState1, ct, *--ip); |
|
701 | 723 | FSE_initCState2(&CState2, ct, *--ip); |
|
702 | 724 | FSE_encodeSymbol(&bitC, &CState1, *--ip); |
|
703 | 725 | FSE_FLUSHBITS(&bitC); |
|
704 | 726 | } else { |
|
705 | 727 | FSE_initCState2(&CState2, ct, *--ip); |
|
706 | 728 | FSE_initCState2(&CState1, ct, *--ip); |
|
707 | 729 | } |
|
708 | 730 | |
|
709 | 731 | /* join to mod 4 */ |
|
710 | 732 | srcSize -= 2; |
|
711 | 733 | if ((sizeof(bitC.bitContainer)*8 > FSE_MAX_TABLELOG*4+7 ) && (srcSize & 2)) { /* test bit 2 */ |
|
712 | 734 | FSE_encodeSymbol(&bitC, &CState2, *--ip); |
|
713 | 735 | FSE_encodeSymbol(&bitC, &CState1, *--ip); |
|
714 | 736 | FSE_FLUSHBITS(&bitC); |
|
715 | 737 | } |
|
716 | 738 | |
|
717 | 739 | /* 2 or 4 encoding per loop */ |
|
718 |
|
|
|
740 | while ( ip>istart ) { | |
|
719 | 741 | |
|
720 | 742 | FSE_encodeSymbol(&bitC, &CState2, *--ip); |
|
721 | 743 | |
|
722 | 744 | if (sizeof(bitC.bitContainer)*8 < FSE_MAX_TABLELOG*2+7 ) /* this test must be static */ |
|
723 | 745 | FSE_FLUSHBITS(&bitC); |
|
724 | 746 | |
|
725 | 747 | FSE_encodeSymbol(&bitC, &CState1, *--ip); |
|
726 | 748 | |
|
727 | 749 | if (sizeof(bitC.bitContainer)*8 > FSE_MAX_TABLELOG*4+7 ) { /* this test must be static */ |
|
728 | 750 | FSE_encodeSymbol(&bitC, &CState2, *--ip); |
|
729 | 751 | FSE_encodeSymbol(&bitC, &CState1, *--ip); |
|
730 | 752 | } |
|
731 | 753 | |
|
732 | 754 | FSE_FLUSHBITS(&bitC); |
|
733 | 755 | } |
|
734 | 756 | |
|
735 | 757 | FSE_flushCState(&bitC, &CState2); |
|
736 | 758 | FSE_flushCState(&bitC, &CState1); |
|
737 | 759 | return BIT_closeCStream(&bitC); |
|
738 | 760 | } |
|
739 | 761 | |
|
740 | 762 | size_t FSE_compress_usingCTable (void* dst, size_t dstSize, |
|
741 | 763 | const void* src, size_t srcSize, |
|
742 | 764 | const FSE_CTable* ct) |
|
743 | 765 | { |
|
744 |
const |
|
|
766 | unsigned const fast = (dstSize >= FSE_BLOCKBOUND(srcSize)); | |
|
745 | 767 | |
|
746 | 768 | if (fast) |
|
747 | 769 | return FSE_compress_usingCTable_generic(dst, dstSize, src, srcSize, ct, 1); |
|
748 | 770 | else |
|
749 | 771 | return FSE_compress_usingCTable_generic(dst, dstSize, src, srcSize, ct, 0); |
|
750 | 772 | } |
|
751 | 773 | |
|
752 | 774 | |
|
753 | 775 | size_t FSE_compressBound(size_t size) { return FSE_COMPRESSBOUND(size); } |
|
754 | 776 | |
|
755 | size_t FSE_compress2 (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog) | |
|
777 | #define CHECK_V_F(e, f) size_t const e = f; if (ERR_isError(e)) return f | |
|
778 | #define CHECK_F(f) { CHECK_V_F(_var_err__, f); } | |
|
779 | ||
|
780 | /* FSE_compress_wksp() : | |
|
781 | * Same as FSE_compress2(), but using an externally allocated scratch buffer (`workSpace`). | |
|
782 | * `wkspSize` size must be `(1<<tableLog)`. | |
|
783 | */ | |
|
784 | size_t FSE_compress_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize) | |
|
756 | 785 | { |
|
757 | const BYTE* const istart = (const BYTE*) src; | |
|
758 | const BYTE* ip = istart; | |
|
759 | ||
|
760 | 786 | BYTE* const ostart = (BYTE*) dst; |
|
761 | 787 | BYTE* op = ostart; |
|
762 | 788 | BYTE* const oend = ostart + dstSize; |
|
763 | 789 | |
|
764 | 790 | U32 count[FSE_MAX_SYMBOL_VALUE+1]; |
|
765 | 791 | S16 norm[FSE_MAX_SYMBOL_VALUE+1]; |
|
766 | CTable_max_t ct; | |
|
767 | size_t errorCode; | |
|
792 | FSE_CTable* CTable = (FSE_CTable*)workSpace; | |
|
793 | size_t const CTableSize = FSE_CTABLE_SIZE_U32(tableLog, maxSymbolValue); | |
|
794 | void* scratchBuffer = (void*)(CTable + CTableSize); | |
|
795 | size_t const scratchBufferSize = wkspSize - (CTableSize * sizeof(FSE_CTable)); | |
|
768 | 796 | |
|
769 | 797 | /* init conditions */ |
|
770 | if (srcSize <= 1) return 0; /* Uncompressible */ | |
|
798 | if (wkspSize < FSE_WKSP_SIZE_U32(tableLog, maxSymbolValue)) return ERROR(tableLog_tooLarge); | |
|
799 | if (srcSize <= 1) return 0; /* Not compressible */ | |
|
771 | 800 | if (!maxSymbolValue) maxSymbolValue = FSE_MAX_SYMBOL_VALUE; |
|
772 | 801 | if (!tableLog) tableLog = FSE_DEFAULT_TABLELOG; |
|
773 | 802 | |
|
774 | 803 | /* Scan input and build symbol stats */ |
|
775 |
|
|
|
776 | if (FSE_isError(errorCode)) return errorCode; | |
|
777 | if (errorCode == srcSize) return 1; | |
|
778 | if (errorCode == 1) return 0; /* each symbol only present once */ | |
|
779 | if (errorCode < (srcSize >> 7)) return 0; /* Heuristic : not compressible enough */ | |
|
804 | { CHECK_V_F(maxCount, FSE_count(count, &maxSymbolValue, src, srcSize) ); | |
|
805 | if (maxCount == srcSize) return 1; /* only a single symbol in src : rle */ | |
|
806 | if (maxCount == 1) return 0; /* each symbol present maximum once => not compressible */ | |
|
807 | if (maxCount < (srcSize >> 7)) return 0; /* Heuristic : not compressible enough */ | |
|
808 | } | |
|
780 | 809 | |
|
781 | 810 | tableLog = FSE_optimalTableLog(tableLog, srcSize, maxSymbolValue); |
|
782 |
|
|
|
783 | if (FSE_isError(errorCode)) return errorCode; | |
|
811 | CHECK_F( FSE_normalizeCount(norm, tableLog, count, srcSize, maxSymbolValue) ); | |
|
784 | 812 | |
|
785 | 813 | /* Write table description header */ |
|
786 |
|
|
|
787 | if (FSE_isError(errorCode)) return errorCode; | |
|
788 | op += errorCode; | |
|
814 | { CHECK_V_F(nc_err, FSE_writeNCount(op, oend-op, norm, maxSymbolValue, tableLog) ); | |
|
815 | op += nc_err; | |
|
816 | } | |
|
789 | 817 | |
|
790 | 818 | /* Compress */ |
|
791 |
|
|
|
792 | if (FSE_isError(errorCode)) return errorCode; | |
|
793 | errorCode = FSE_compress_usingCTable(op, oend - op, ip, srcSize, ct); | |
|
794 | if (errorCode == 0) return 0; /* not enough space for compressed data */ | |
|
795 | op += errorCode; | |
|
819 | CHECK_F( FSE_buildCTable_wksp(CTable, norm, maxSymbolValue, tableLog, scratchBuffer, scratchBufferSize) ); | |
|
820 | { CHECK_V_F(cSize, FSE_compress_usingCTable(op, oend - op, src, srcSize, CTable) ); | |
|
821 | if (cSize == 0) return 0; /* not enough space for compressed data */ | |
|
822 | op += cSize; | |
|
823 | } | |
|
796 | 824 | |
|
797 | 825 | /* check compressibility */ |
|
798 | if ( (size_t)(op-ostart) >= srcSize-1 ) | |
|
799 | return 0; | |
|
826 | if ( (size_t)(op-ostart) >= srcSize-1 ) return 0; | |
|
800 | 827 | |
|
801 | 828 | return op-ostart; |
|
802 | 829 | } |
|
803 | 830 | |
|
804 | size_t FSE_compress (void* dst, size_t dstSize, const void* src, size_t srcSize) | |
|
831 | typedef struct { | |
|
832 | FSE_CTable CTable_max[FSE_CTABLE_SIZE_U32(FSE_MAX_TABLELOG, FSE_MAX_SYMBOL_VALUE)]; | |
|
833 | BYTE scratchBuffer[1 << FSE_MAX_TABLELOG]; | |
|
834 | } fseWkspMax_t; | |
|
835 | ||
|
836 | size_t FSE_compress2 (void* dst, size_t dstCapacity, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog) | |
|
805 | 837 | { |
|
806 | return FSE_compress2(dst, dstSize, src, (U32)srcSize, FSE_MAX_SYMBOL_VALUE, FSE_DEFAULT_TABLELOG); | |
|
838 | fseWkspMax_t scratchBuffer; | |
|
839 | FSE_STATIC_ASSERT(sizeof(scratchBuffer) >= FSE_WKSP_SIZE_U32(FSE_MAX_TABLELOG, FSE_MAX_SYMBOL_VALUE)); /* compilation failures here means scratchBuffer is not large enough */ | |
|
840 | if (tableLog > FSE_MAX_TABLELOG) return ERROR(tableLog_tooLarge); | |
|
841 | return FSE_compress_wksp(dst, dstCapacity, src, srcSize, maxSymbolValue, tableLog, &scratchBuffer, sizeof(scratchBuffer)); | |
|
842 | } | |
|
843 | ||
|
844 | size_t FSE_compress (void* dst, size_t dstCapacity, const void* src, size_t srcSize) | |
|
845 | { | |
|
846 | return FSE_compress2(dst, dstCapacity, src, srcSize, FSE_MAX_SYMBOL_VALUE, FSE_DEFAULT_TABLELOG); | |
|
807 | 847 | } |
|
808 | 848 | |
|
809 | 849 | |
|
810 | 850 | #endif /* FSE_COMMONDEFS_ONLY */ |
@@ -1,533 +1,609 b'' | |||
|
1 | 1 | /* ****************************************************************** |
|
2 | 2 | Huffman encoder, part of New Generation Entropy library |
|
3 | 3 | Copyright (C) 2013-2016, Yann Collet. |
|
4 | 4 | |
|
5 | 5 | BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) |
|
6 | 6 | |
|
7 | 7 | Redistribution and use in source and binary forms, with or without |
|
8 | 8 | modification, are permitted provided that the following conditions are |
|
9 | 9 | met: |
|
10 | 10 | |
|
11 | 11 | * Redistributions of source code must retain the above copyright |
|
12 | 12 | notice, this list of conditions and the following disclaimer. |
|
13 | 13 | * Redistributions in binary form must reproduce the above |
|
14 | 14 | copyright notice, this list of conditions and the following disclaimer |
|
15 | 15 | in the documentation and/or other materials provided with the |
|
16 | 16 | distribution. |
|
17 | 17 | |
|
18 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
19 | 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
20 | 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
21 | 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
22 | 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
23 | 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
24 | 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
25 | 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
26 | 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
27 | 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
28 | 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
29 | 29 | |
|
30 | 30 | You can contact the author at : |
|
31 | 31 | - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy |
|
32 | 32 | - Public forum : https://groups.google.com/forum/#!forum/lz4c |
|
33 | 33 | ****************************************************************** */ |
|
34 | 34 | |
|
35 | 35 | /* ************************************************************** |
|
36 | 36 | * Compiler specifics |
|
37 | 37 | ****************************************************************/ |
|
38 | 38 | #ifdef _MSC_VER /* Visual Studio */ |
|
39 | 39 | # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ |
|
40 | 40 | #endif |
|
41 | 41 | |
|
42 | 42 | |
|
43 | 43 | /* ************************************************************** |
|
44 | 44 | * Includes |
|
45 | 45 | ****************************************************************/ |
|
46 | 46 | #include <string.h> /* memcpy, memset */ |
|
47 | 47 | #include <stdio.h> /* printf (debug) */ |
|
48 | 48 | #include "bitstream.h" |
|
49 | 49 | #define FSE_STATIC_LINKING_ONLY /* FSE_optimalTableLog_internal */ |
|
50 | 50 | #include "fse.h" /* header compression */ |
|
51 | 51 | #define HUF_STATIC_LINKING_ONLY |
|
52 | 52 | #include "huf.h" |
|
53 | 53 | |
|
54 | 54 | |
|
55 | 55 | /* ************************************************************** |
|
56 | 56 | * Error Management |
|
57 | 57 | ****************************************************************/ |
|
58 | 58 | #define HUF_STATIC_ASSERT(c) { enum { HUF_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */ |
|
59 | #define CHECK_V_F(e, f) size_t const e = f; if (ERR_isError(e)) return f | |
|
60 | #define CHECK_F(f) { CHECK_V_F(_var_err__, f); } | |
|
59 | 61 | |
|
60 | 62 | |
|
61 | 63 | /* ************************************************************** |
|
62 | 64 | * Utils |
|
63 | 65 | ****************************************************************/ |
|
64 | 66 | unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue) |
|
65 | 67 | { |
|
66 | 68 | return FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 1); |
|
67 | 69 | } |
|
68 | 70 | |
|
69 | 71 | |
|
70 | 72 | /* ******************************************************* |
|
71 | 73 | * HUF : Huffman block compression |
|
72 | 74 | *********************************************************/ |
|
75 | /* HUF_compressWeights() : | |
|
76 | * Same as FSE_compress(), but dedicated to huff0's weights compression. | |
|
77 | * The use case needs much less stack memory. | |
|
78 | * Note : all elements within weightTable are supposed to be <= HUF_TABLELOG_MAX. | |
|
79 | */ | |
|
80 | #define MAX_FSE_TABLELOG_FOR_HUFF_HEADER 6 | |
|
81 | size_t HUF_compressWeights (void* dst, size_t dstSize, const void* weightTable, size_t wtSize) | |
|
82 | { | |
|
83 | BYTE* const ostart = (BYTE*) dst; | |
|
84 | BYTE* op = ostart; | |
|
85 | BYTE* const oend = ostart + dstSize; | |
|
86 | ||
|
87 | U32 maxSymbolValue = HUF_TABLELOG_MAX; | |
|
88 | U32 tableLog = MAX_FSE_TABLELOG_FOR_HUFF_HEADER; | |
|
89 | ||
|
90 | FSE_CTable CTable[FSE_CTABLE_SIZE_U32(MAX_FSE_TABLELOG_FOR_HUFF_HEADER, HUF_TABLELOG_MAX)]; | |
|
91 | BYTE scratchBuffer[1<<MAX_FSE_TABLELOG_FOR_HUFF_HEADER]; | |
|
92 | ||
|
93 | U32 count[HUF_TABLELOG_MAX+1]; | |
|
94 | S16 norm[HUF_TABLELOG_MAX+1]; | |
|
95 | ||
|
96 | /* init conditions */ | |
|
97 | if (wtSize <= 1) return 0; /* Not compressible */ | |
|
98 | ||
|
99 | /* Scan input and build symbol stats */ | |
|
100 | { CHECK_V_F(maxCount, FSE_count_simple(count, &maxSymbolValue, weightTable, wtSize) ); | |
|
101 | if (maxCount == wtSize) return 1; /* only a single symbol in src : rle */ | |
|
102 | if (maxCount == 1) return 0; /* each symbol present maximum once => not compressible */ | |
|
103 | } | |
|
104 | ||
|
105 | tableLog = FSE_optimalTableLog(tableLog, wtSize, maxSymbolValue); | |
|
106 | CHECK_F( FSE_normalizeCount(norm, tableLog, count, wtSize, maxSymbolValue) ); | |
|
107 | ||
|
108 | /* Write table description header */ | |
|
109 | { CHECK_V_F(hSize, FSE_writeNCount(op, oend-op, norm, maxSymbolValue, tableLog) ); | |
|
110 | op += hSize; | |
|
111 | } | |
|
112 | ||
|
113 | /* Compress */ | |
|
114 | CHECK_F( FSE_buildCTable_wksp(CTable, norm, maxSymbolValue, tableLog, scratchBuffer, sizeof(scratchBuffer)) ); | |
|
115 | { CHECK_V_F(cSize, FSE_compress_usingCTable(op, oend - op, weightTable, wtSize, CTable) ); | |
|
116 | if (cSize == 0) return 0; /* not enough space for compressed data */ | |
|
117 | op += cSize; | |
|
118 | } | |
|
119 | ||
|
120 | return op-ostart; | |
|
121 | } | |
|
122 | ||
|
123 | ||
|
73 | 124 | struct HUF_CElt_s { |
|
74 | 125 | U16 val; |
|
75 | 126 | BYTE nbBits; |
|
76 | 127 | }; /* typedef'd to HUF_CElt within "huf.h" */ |
|
77 | 128 | |
|
78 | typedef struct nodeElt_s { | |
|
79 | U32 count; | |
|
80 | U16 parent; | |
|
81 | BYTE byte; | |
|
82 | BYTE nbBits; | |
|
83 | } nodeElt; | |
|
84 | ||
|
85 | 129 | /*! HUF_writeCTable() : |
|
86 | 130 | `CTable` : huffman tree to save, using huf representation. |
|
87 | 131 | @return : size of saved CTable */ |
|
88 | 132 | size_t HUF_writeCTable (void* dst, size_t maxDstSize, |
|
89 | 133 | const HUF_CElt* CTable, U32 maxSymbolValue, U32 huffLog) |
|
90 | 134 | { |
|
91 | BYTE bitsToWeight[HUF_TABLELOG_MAX + 1]; | |
|
135 | BYTE bitsToWeight[HUF_TABLELOG_MAX + 1]; /* precomputed conversion table */ | |
|
92 | 136 | BYTE huffWeight[HUF_SYMBOLVALUE_MAX]; |
|
93 | 137 | BYTE* op = (BYTE*)dst; |
|
94 | 138 | U32 n; |
|
95 | 139 | |
|
96 | 140 | /* check conditions */ |
|
97 |
if (maxSymbolValue > HUF_SYMBOLVALUE_MAX) return ERROR( |
|
|
141 | if (maxSymbolValue > HUF_SYMBOLVALUE_MAX) return ERROR(maxSymbolValue_tooLarge); | |
|
98 | 142 | |
|
99 | 143 | /* convert to weight */ |
|
100 | 144 | bitsToWeight[0] = 0; |
|
101 | 145 | for (n=1; n<huffLog+1; n++) |
|
102 | 146 | bitsToWeight[n] = (BYTE)(huffLog + 1 - n); |
|
103 | 147 | for (n=0; n<maxSymbolValue; n++) |
|
104 | 148 | huffWeight[n] = bitsToWeight[CTable[n].nbBits]; |
|
105 | 149 | |
|
106 | { size_t const size = FSE_compress(op+1, maxDstSize-1, huffWeight, maxSymbolValue); | |
|
107 | if (FSE_isError(size)) return size; | |
|
108 |
if (( |
|
|
109 |
op[0] = (BYTE) |
|
|
110 |
return |
|
|
111 | } | |
|
112 | } | |
|
150 | /* attempt weights compression by FSE */ | |
|
151 | { CHECK_V_F(hSize, HUF_compressWeights(op+1, maxDstSize-1, huffWeight, maxSymbolValue) ); | |
|
152 | if ((hSize>1) & (hSize < maxSymbolValue/2)) { /* FSE compressed */ | |
|
153 | op[0] = (BYTE)hSize; | |
|
154 | return hSize+1; | |
|
155 | } } | |
|
113 | 156 | |
|
114 | /* raw values */ | |
|
115 | if (maxSymbolValue > (256-128)) return ERROR(GENERIC); /* should not happen */ | |
|
157 | /* write raw values as 4-bits (max : 15) */ | |
|
158 | if (maxSymbolValue > (256-128)) return ERROR(GENERIC); /* should not happen : likely means source cannot be compressed */ | |
|
116 | 159 | if (((maxSymbolValue+1)/2) + 1 > maxDstSize) return ERROR(dstSize_tooSmall); /* not enough space within dst buffer */ |
|
117 | 160 | op[0] = (BYTE)(128 /*special case*/ + (maxSymbolValue-1)); |
|
118 | huffWeight[maxSymbolValue] = 0; /* to be sure it doesn't cause issue in final combination */ | |
|
161 | huffWeight[maxSymbolValue] = 0; /* to be sure it doesn't cause msan issue in final combination */ | |
|
119 | 162 | for (n=0; n<maxSymbolValue; n+=2) |
|
120 | 163 | op[(n/2)+1] = (BYTE)((huffWeight[n] << 4) + huffWeight[n+1]); |
|
121 | 164 | return ((maxSymbolValue+1)/2) + 1; |
|
122 | ||
|
123 | 165 | } |
|
124 | 166 | |
|
125 | 167 | |
|
126 | 168 | size_t HUF_readCTable (HUF_CElt* CTable, U32 maxSymbolValue, const void* src, size_t srcSize) |
|
127 | 169 | { |
|
128 | BYTE huffWeight[HUF_SYMBOLVALUE_MAX + 1]; | |
|
170 | BYTE huffWeight[HUF_SYMBOLVALUE_MAX + 1]; /* init not required, even though some static analyzer may complain */ | |
|
129 | 171 | U32 rankVal[HUF_TABLELOG_ABSOLUTEMAX + 1]; /* large enough for values from 0 to 16 */ |
|
130 | 172 | U32 tableLog = 0; |
|
131 | size_t readSize; | |
|
132 | 173 | U32 nbSymbols = 0; |
|
133 | /*memset(huffWeight, 0, sizeof(huffWeight));*/ /* is not necessary, even though some analyzer complain ... */ | |
|
134 | 174 | |
|
135 | 175 | /* get symbol weights */ |
|
136 |
readSize |
|
|
137 | if (HUF_isError(readSize)) return readSize; | |
|
176 | CHECK_V_F(readSize, HUF_readStats(huffWeight, HUF_SYMBOLVALUE_MAX+1, rankVal, &nbSymbols, &tableLog, src, srcSize)); | |
|
138 | 177 | |
|
139 | 178 | /* check result */ |
|
140 | 179 | if (tableLog > HUF_TABLELOG_MAX) return ERROR(tableLog_tooLarge); |
|
141 | 180 | if (nbSymbols > maxSymbolValue+1) return ERROR(maxSymbolValue_tooSmall); |
|
142 | 181 | |
|
143 | 182 | /* Prepare base value per rank */ |
|
144 | 183 | { U32 n, nextRankStart = 0; |
|
145 | 184 | for (n=1; n<=tableLog; n++) { |
|
146 | 185 | U32 current = nextRankStart; |
|
147 | 186 | nextRankStart += (rankVal[n] << (n-1)); |
|
148 | 187 | rankVal[n] = current; |
|
149 | 188 | } } |
|
150 | 189 | |
|
151 | 190 | /* fill nbBits */ |
|
152 | 191 | { U32 n; for (n=0; n<nbSymbols; n++) { |
|
153 | 192 | const U32 w = huffWeight[n]; |
|
154 | 193 | CTable[n].nbBits = (BYTE)(tableLog + 1 - w); |
|
155 | 194 | } } |
|
156 | 195 | |
|
157 | 196 | /* fill val */ |
|
158 | 197 | { U16 nbPerRank[HUF_TABLELOG_MAX+2] = {0}; /* support w=0=>n=tableLog+1 */ |
|
159 | 198 | U16 valPerRank[HUF_TABLELOG_MAX+2] = {0}; |
|
160 | 199 | { U32 n; for (n=0; n<nbSymbols; n++) nbPerRank[CTable[n].nbBits]++; } |
|
161 | 200 | /* determine stating value per rank */ |
|
162 | 201 | valPerRank[tableLog+1] = 0; /* for w==0 */ |
|
163 | 202 | { U16 min = 0; |
|
164 | 203 | U32 n; for (n=tableLog; n>0; n--) { /* start at n=tablelog <-> w=1 */ |
|
165 | 204 | valPerRank[n] = min; /* get starting value within each rank */ |
|
166 | 205 | min += nbPerRank[n]; |
|
167 | 206 | min >>= 1; |
|
168 | 207 | } } |
|
169 | 208 | /* assign value within rank, symbol order */ |
|
170 | 209 | { U32 n; for (n=0; n<=maxSymbolValue; n++) CTable[n].val = valPerRank[CTable[n].nbBits]++; } |
|
171 | 210 | } |
|
172 | 211 | |
|
173 | 212 | return readSize; |
|
174 | 213 | } |
|
175 | 214 | |
|
176 | 215 | |
|
216 | typedef struct nodeElt_s { | |
|
217 | U32 count; | |
|
218 | U16 parent; | |
|
219 | BYTE byte; | |
|
220 | BYTE nbBits; | |
|
221 | } nodeElt; | |
|
222 | ||
|
177 | 223 | static U32 HUF_setMaxHeight(nodeElt* huffNode, U32 lastNonNull, U32 maxNbBits) |
|
178 | 224 | { |
|
179 | 225 | const U32 largestBits = huffNode[lastNonNull].nbBits; |
|
180 | 226 | if (largestBits <= maxNbBits) return largestBits; /* early exit : no elt > maxNbBits */ |
|
181 | 227 | |
|
182 | 228 | /* there are several too large elements (at least >= 2) */ |
|
183 | 229 | { int totalCost = 0; |
|
184 | 230 | const U32 baseCost = 1 << (largestBits - maxNbBits); |
|
185 | 231 | U32 n = lastNonNull; |
|
186 | 232 | |
|
187 | 233 | while (huffNode[n].nbBits > maxNbBits) { |
|
188 | 234 | totalCost += baseCost - (1 << (largestBits - huffNode[n].nbBits)); |
|
189 | 235 | huffNode[n].nbBits = (BYTE)maxNbBits; |
|
190 | 236 | n --; |
|
191 | 237 | } /* n stops at huffNode[n].nbBits <= maxNbBits */ |
|
192 | 238 | while (huffNode[n].nbBits == maxNbBits) n--; /* n end at index of smallest symbol using < maxNbBits */ |
|
193 | 239 | |
|
194 | 240 | /* renorm totalCost */ |
|
195 | 241 | totalCost >>= (largestBits - maxNbBits); /* note : totalCost is necessarily a multiple of baseCost */ |
|
196 | 242 | |
|
197 | 243 | /* repay normalized cost */ |
|
198 | 244 | { U32 const noSymbol = 0xF0F0F0F0; |
|
199 | 245 | U32 rankLast[HUF_TABLELOG_MAX+2]; |
|
200 | 246 | int pos; |
|
201 | 247 | |
|
202 | 248 | /* Get pos of last (smallest) symbol per rank */ |
|
203 | 249 | memset(rankLast, 0xF0, sizeof(rankLast)); |
|
204 | 250 | { U32 currentNbBits = maxNbBits; |
|
205 | 251 | for (pos=n ; pos >= 0; pos--) { |
|
206 | 252 | if (huffNode[pos].nbBits >= currentNbBits) continue; |
|
207 | 253 | currentNbBits = huffNode[pos].nbBits; /* < maxNbBits */ |
|
208 | 254 | rankLast[maxNbBits-currentNbBits] = pos; |
|
209 | 255 | } } |
|
210 | 256 | |
|
211 | 257 | while (totalCost > 0) { |
|
212 | 258 | U32 nBitsToDecrease = BIT_highbit32(totalCost) + 1; |
|
213 | 259 | for ( ; nBitsToDecrease > 1; nBitsToDecrease--) { |
|
214 | 260 | U32 highPos = rankLast[nBitsToDecrease]; |
|
215 | 261 | U32 lowPos = rankLast[nBitsToDecrease-1]; |
|
216 | 262 | if (highPos == noSymbol) continue; |
|
217 | 263 | if (lowPos == noSymbol) break; |
|
218 | 264 | { U32 const highTotal = huffNode[highPos].count; |
|
219 | 265 | U32 const lowTotal = 2 * huffNode[lowPos].count; |
|
220 | 266 | if (highTotal <= lowTotal) break; |
|
221 | 267 | } } |
|
222 | 268 | /* only triggered when no more rank 1 symbol left => find closest one (note : there is necessarily at least one !) */ |
|
223 | 269 | while ((nBitsToDecrease<=HUF_TABLELOG_MAX) && (rankLast[nBitsToDecrease] == noSymbol)) /* HUF_MAX_TABLELOG test just to please gcc 5+; but it should not be necessary */ |
|
224 | 270 | nBitsToDecrease ++; |
|
225 | 271 | totalCost -= 1 << (nBitsToDecrease-1); |
|
226 | 272 | if (rankLast[nBitsToDecrease-1] == noSymbol) |
|
227 | 273 | rankLast[nBitsToDecrease-1] = rankLast[nBitsToDecrease]; /* this rank is no longer empty */ |
|
228 | 274 | huffNode[rankLast[nBitsToDecrease]].nbBits ++; |
|
229 | 275 | if (rankLast[nBitsToDecrease] == 0) /* special case, reached largest symbol */ |
|
230 | 276 | rankLast[nBitsToDecrease] = noSymbol; |
|
231 | 277 | else { |
|
232 | 278 | rankLast[nBitsToDecrease]--; |
|
233 | 279 | if (huffNode[rankLast[nBitsToDecrease]].nbBits != maxNbBits-nBitsToDecrease) |
|
234 | 280 | rankLast[nBitsToDecrease] = noSymbol; /* this rank is now empty */ |
|
235 | 281 | } } /* while (totalCost > 0) */ |
|
236 | 282 | |
|
237 | 283 | while (totalCost < 0) { /* Sometimes, cost correction overshoot */ |
|
238 | 284 | if (rankLast[1] == noSymbol) { /* special case : no rank 1 symbol (using maxNbBits-1); let's create one from largest rank 0 (using maxNbBits) */ |
|
239 | 285 | while (huffNode[n].nbBits == maxNbBits) n--; |
|
240 | 286 | huffNode[n+1].nbBits--; |
|
241 | 287 | rankLast[1] = n+1; |
|
242 | 288 | totalCost++; |
|
243 | 289 | continue; |
|
244 | 290 | } |
|
245 | 291 | huffNode[ rankLast[1] + 1 ].nbBits--; |
|
246 | 292 | rankLast[1]++; |
|
247 | 293 | totalCost ++; |
|
248 | 294 | } } } /* there are several too large elements (at least >= 2) */ |
|
249 | 295 | |
|
250 | 296 | return maxNbBits; |
|
251 | 297 | } |
|
252 | 298 | |
|
253 | 299 | |
|
254 | 300 | typedef struct { |
|
255 | 301 | U32 base; |
|
256 | 302 | U32 current; |
|
257 | 303 | } rankPos; |
|
258 | 304 | |
|
259 | 305 | static void HUF_sort(nodeElt* huffNode, const U32* count, U32 maxSymbolValue) |
|
260 | 306 | { |
|
261 | 307 | rankPos rank[32]; |
|
262 | 308 | U32 n; |
|
263 | 309 | |
|
264 | 310 | memset(rank, 0, sizeof(rank)); |
|
265 | 311 | for (n=0; n<=maxSymbolValue; n++) { |
|
266 | 312 | U32 r = BIT_highbit32(count[n] + 1); |
|
267 | 313 | rank[r].base ++; |
|
268 | 314 | } |
|
269 | 315 | for (n=30; n>0; n--) rank[n-1].base += rank[n].base; |
|
270 | 316 | for (n=0; n<32; n++) rank[n].current = rank[n].base; |
|
271 | 317 | for (n=0; n<=maxSymbolValue; n++) { |
|
272 | 318 | U32 const c = count[n]; |
|
273 | 319 | U32 const r = BIT_highbit32(c+1) + 1; |
|
274 | 320 | U32 pos = rank[r].current++; |
|
275 | 321 | while ((pos > rank[r].base) && (c > huffNode[pos-1].count)) huffNode[pos]=huffNode[pos-1], pos--; |
|
276 | 322 | huffNode[pos].count = c; |
|
277 | 323 | huffNode[pos].byte = (BYTE)n; |
|
278 | 324 | } |
|
279 | 325 | } |
|
280 | 326 | |
|
281 | 327 | |
|
328 | /** HUF_buildCTable_wksp() : | |
|
329 | * Same as HUF_buildCTable(), but using externally allocated scratch buffer. | |
|
330 | * `workSpace` must be aligned on 4-bytes boundaries, and be at least as large as a table of 1024 unsigned. | |
|
331 | */ | |
|
282 | 332 | #define STARTNODE (HUF_SYMBOLVALUE_MAX+1) |
|
283 | size_t HUF_buildCTable (HUF_CElt* tree, const U32* count, U32 maxSymbolValue, U32 maxNbBits) | |
|
333 | typedef nodeElt huffNodeTable[2*HUF_SYMBOLVALUE_MAX+1 +1]; | |
|
334 | size_t HUF_buildCTable_wksp (HUF_CElt* tree, const U32* count, U32 maxSymbolValue, U32 maxNbBits, void* workSpace, size_t wkspSize) | |
|
284 | 335 | { |
|
285 | nodeElt huffNode0[2*HUF_SYMBOLVALUE_MAX+1 +1]; | |
|
286 |
nodeElt* huffNode = huffNode0 |
|
|
336 | nodeElt* const huffNode0 = (nodeElt*)workSpace; | |
|
337 | nodeElt* const huffNode = huffNode0+1; | |
|
287 | 338 | U32 n, nonNullRank; |
|
288 | 339 | int lowS, lowN; |
|
289 | 340 | U16 nodeNb = STARTNODE; |
|
290 | 341 | U32 nodeRoot; |
|
291 | 342 | |
|
292 | 343 | /* safety checks */ |
|
344 | if (wkspSize < sizeof(huffNodeTable)) return ERROR(GENERIC); /* workSpace is not large enough */ | |
|
293 | 345 | if (maxNbBits == 0) maxNbBits = HUF_TABLELOG_DEFAULT; |
|
294 | 346 | if (maxSymbolValue > HUF_SYMBOLVALUE_MAX) return ERROR(GENERIC); |
|
295 |
memset(huffNode0, 0, sizeof(huffNode |
|
|
347 | memset(huffNode0, 0, sizeof(huffNodeTable)); | |
|
296 | 348 | |
|
297 | 349 | /* sort, decreasing order */ |
|
298 | 350 | HUF_sort(huffNode, count, maxSymbolValue); |
|
299 | 351 | |
|
300 | 352 | /* init for parents */ |
|
301 | 353 | nonNullRank = maxSymbolValue; |
|
302 | 354 | while(huffNode[nonNullRank].count == 0) nonNullRank--; |
|
303 | 355 | lowS = nonNullRank; nodeRoot = nodeNb + lowS - 1; lowN = nodeNb; |
|
304 | 356 | huffNode[nodeNb].count = huffNode[lowS].count + huffNode[lowS-1].count; |
|
305 | 357 | huffNode[lowS].parent = huffNode[lowS-1].parent = nodeNb; |
|
306 | 358 | nodeNb++; lowS-=2; |
|
307 | 359 | for (n=nodeNb; n<=nodeRoot; n++) huffNode[n].count = (U32)(1U<<30); |
|
308 | huffNode0[0].count = (U32)(1U<<31); | |
|
360 | huffNode0[0].count = (U32)(1U<<31); /* fake entry, strong barrier */ | |
|
309 | 361 | |
|
310 | 362 | /* create parents */ |
|
311 | 363 | while (nodeNb <= nodeRoot) { |
|
312 | 364 | U32 n1 = (huffNode[lowS].count < huffNode[lowN].count) ? lowS-- : lowN++; |
|
313 | 365 | U32 n2 = (huffNode[lowS].count < huffNode[lowN].count) ? lowS-- : lowN++; |
|
314 | 366 | huffNode[nodeNb].count = huffNode[n1].count + huffNode[n2].count; |
|
315 | 367 | huffNode[n1].parent = huffNode[n2].parent = nodeNb; |
|
316 | 368 | nodeNb++; |
|
317 | 369 | } |
|
318 | 370 | |
|
319 | 371 | /* distribute weights (unlimited tree height) */ |
|
320 | 372 | huffNode[nodeRoot].nbBits = 0; |
|
321 | 373 | for (n=nodeRoot-1; n>=STARTNODE; n--) |
|
322 | 374 | huffNode[n].nbBits = huffNode[ huffNode[n].parent ].nbBits + 1; |
|
323 | 375 | for (n=0; n<=nonNullRank; n++) |
|
324 | 376 | huffNode[n].nbBits = huffNode[ huffNode[n].parent ].nbBits + 1; |
|
325 | 377 | |
|
326 | 378 | /* enforce maxTableLog */ |
|
327 | 379 | maxNbBits = HUF_setMaxHeight(huffNode, nonNullRank, maxNbBits); |
|
328 | 380 | |
|
329 | 381 | /* fill result into tree (val, nbBits) */ |
|
330 | 382 | { U16 nbPerRank[HUF_TABLELOG_MAX+1] = {0}; |
|
331 | 383 | U16 valPerRank[HUF_TABLELOG_MAX+1] = {0}; |
|
332 | 384 | if (maxNbBits > HUF_TABLELOG_MAX) return ERROR(GENERIC); /* check fit into table */ |
|
333 | 385 | for (n=0; n<=nonNullRank; n++) |
|
334 | 386 | nbPerRank[huffNode[n].nbBits]++; |
|
335 | 387 | /* determine stating value per rank */ |
|
336 | 388 | { U16 min = 0; |
|
337 | 389 | for (n=maxNbBits; n>0; n--) { |
|
338 | 390 | valPerRank[n] = min; /* get starting value within each rank */ |
|
339 | 391 | min += nbPerRank[n]; |
|
340 | 392 | min >>= 1; |
|
341 | 393 | } } |
|
342 | 394 | for (n=0; n<=maxSymbolValue; n++) |
|
343 | 395 | tree[huffNode[n].byte].nbBits = huffNode[n].nbBits; /* push nbBits per symbol, symbol order */ |
|
344 | 396 | for (n=0; n<=maxSymbolValue; n++) |
|
345 | 397 | tree[n].val = valPerRank[tree[n].nbBits]++; /* assign value within rank, symbol order */ |
|
346 | 398 | } |
|
347 | 399 | |
|
348 | 400 | return maxNbBits; |
|
349 | 401 | } |
|
350 | 402 | |
|
403 | /** HUF_buildCTable() : | |
|
404 | * Note : count is used before tree is written, so they can safely overlap | |
|
405 | */ | |
|
406 | size_t HUF_buildCTable (HUF_CElt* tree, const U32* count, U32 maxSymbolValue, U32 maxNbBits) | |
|
407 | { | |
|
408 | huffNodeTable nodeTable; | |
|
409 | return HUF_buildCTable_wksp(tree, count, maxSymbolValue, maxNbBits, nodeTable, sizeof(nodeTable)); | |
|
410 | } | |
|
411 | ||
|
351 | 412 | static void HUF_encodeSymbol(BIT_CStream_t* bitCPtr, U32 symbol, const HUF_CElt* CTable) |
|
352 | 413 | { |
|
353 | 414 | BIT_addBitsFast(bitCPtr, CTable[symbol].val, CTable[symbol].nbBits); |
|
354 | 415 | } |
|
355 | 416 | |
|
356 | 417 | size_t HUF_compressBound(size_t size) { return HUF_COMPRESSBOUND(size); } |
|
357 | 418 | |
|
358 | 419 | #define HUF_FLUSHBITS(s) (fast ? BIT_flushBitsFast(s) : BIT_flushBits(s)) |
|
359 | 420 | |
|
360 | 421 | #define HUF_FLUSHBITS_1(stream) \ |
|
361 | 422 | if (sizeof((stream)->bitContainer)*8 < HUF_TABLELOG_MAX*2+7) HUF_FLUSHBITS(stream) |
|
362 | 423 | |
|
363 | 424 | #define HUF_FLUSHBITS_2(stream) \ |
|
364 | 425 | if (sizeof((stream)->bitContainer)*8 < HUF_TABLELOG_MAX*4+7) HUF_FLUSHBITS(stream) |
|
365 | 426 | |
|
366 | 427 | size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable) |
|
367 | 428 | { |
|
368 | 429 | const BYTE* ip = (const BYTE*) src; |
|
369 | 430 | BYTE* const ostart = (BYTE*)dst; |
|
370 | 431 | BYTE* const oend = ostart + dstSize; |
|
371 | 432 | BYTE* op = ostart; |
|
372 | 433 | size_t n; |
|
373 | 434 | const unsigned fast = (dstSize >= HUF_BLOCKBOUND(srcSize)); |
|
374 | 435 | BIT_CStream_t bitC; |
|
375 | 436 | |
|
376 | 437 | /* init */ |
|
377 | 438 | if (dstSize < 8) return 0; /* not enough space to compress */ |
|
378 |
{ size_t const |
|
|
379 |
if (HUF_isError( |
|
|
439 | { size_t const initErr = BIT_initCStream(&bitC, op, oend-op); | |
|
440 | if (HUF_isError(initErr)) return 0; } | |
|
380 | 441 | |
|
381 | 442 | n = srcSize & ~3; /* join to mod 4 */ |
|
382 | 443 | switch (srcSize & 3) |
|
383 | 444 | { |
|
384 | 445 | case 3 : HUF_encodeSymbol(&bitC, ip[n+ 2], CTable); |
|
385 | 446 | HUF_FLUSHBITS_2(&bitC); |
|
386 | 447 | case 2 : HUF_encodeSymbol(&bitC, ip[n+ 1], CTable); |
|
387 | 448 | HUF_FLUSHBITS_1(&bitC); |
|
388 | 449 | case 1 : HUF_encodeSymbol(&bitC, ip[n+ 0], CTable); |
|
389 | 450 | HUF_FLUSHBITS(&bitC); |
|
390 | 451 | case 0 : |
|
391 | 452 | default: ; |
|
392 | 453 | } |
|
393 | 454 | |
|
394 | 455 | for (; n>0; n-=4) { /* note : n&3==0 at this stage */ |
|
395 | 456 | HUF_encodeSymbol(&bitC, ip[n- 1], CTable); |
|
396 | 457 | HUF_FLUSHBITS_1(&bitC); |
|
397 | 458 | HUF_encodeSymbol(&bitC, ip[n- 2], CTable); |
|
398 | 459 | HUF_FLUSHBITS_2(&bitC); |
|
399 | 460 | HUF_encodeSymbol(&bitC, ip[n- 3], CTable); |
|
400 | 461 | HUF_FLUSHBITS_1(&bitC); |
|
401 | 462 | HUF_encodeSymbol(&bitC, ip[n- 4], CTable); |
|
402 | 463 | HUF_FLUSHBITS(&bitC); |
|
403 | 464 | } |
|
404 | 465 | |
|
405 | 466 | return BIT_closeCStream(&bitC); |
|
406 | 467 | } |
|
407 | 468 | |
|
408 | 469 | |
|
409 | 470 | size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable) |
|
410 | 471 | { |
|
411 | 472 | size_t const segmentSize = (srcSize+3)/4; /* first 3 segments */ |
|
412 | 473 | const BYTE* ip = (const BYTE*) src; |
|
413 | 474 | const BYTE* const iend = ip + srcSize; |
|
414 | 475 | BYTE* const ostart = (BYTE*) dst; |
|
415 | 476 | BYTE* const oend = ostart + dstSize; |
|
416 | 477 | BYTE* op = ostart; |
|
417 | 478 | |
|
418 | 479 | if (dstSize < 6 + 1 + 1 + 1 + 8) return 0; /* minimum space to compress successfully */ |
|
419 | 480 | if (srcSize < 12) return 0; /* no saving possible : too small input */ |
|
420 | 481 | op += 6; /* jumpTable */ |
|
421 | 482 | |
|
422 |
{ |
|
|
423 | if (HUF_isError(cSize)) return cSize; | |
|
483 | { CHECK_V_F(cSize, HUF_compress1X_usingCTable(op, oend-op, ip, segmentSize, CTable) ); | |
|
424 | 484 | if (cSize==0) return 0; |
|
425 | 485 | MEM_writeLE16(ostart, (U16)cSize); |
|
426 | 486 | op += cSize; |
|
427 | 487 | } |
|
428 | 488 | |
|
429 | 489 | ip += segmentSize; |
|
430 |
{ |
|
|
431 | if (HUF_isError(cSize)) return cSize; | |
|
490 | { CHECK_V_F(cSize, HUF_compress1X_usingCTable(op, oend-op, ip, segmentSize, CTable) ); | |
|
432 | 491 | if (cSize==0) return 0; |
|
433 | 492 | MEM_writeLE16(ostart+2, (U16)cSize); |
|
434 | 493 | op += cSize; |
|
435 | 494 | } |
|
436 | 495 | |
|
437 | 496 | ip += segmentSize; |
|
438 |
{ |
|
|
439 | if (HUF_isError(cSize)) return cSize; | |
|
497 | { CHECK_V_F(cSize, HUF_compress1X_usingCTable(op, oend-op, ip, segmentSize, CTable) ); | |
|
440 | 498 | if (cSize==0) return 0; |
|
441 | 499 | MEM_writeLE16(ostart+4, (U16)cSize); |
|
442 | 500 | op += cSize; |
|
443 | 501 | } |
|
444 | 502 | |
|
445 | 503 | ip += segmentSize; |
|
446 |
{ |
|
|
447 | if (HUF_isError(cSize)) return cSize; | |
|
504 | { CHECK_V_F(cSize, HUF_compress1X_usingCTable(op, oend-op, ip, iend-ip, CTable) ); | |
|
448 | 505 | if (cSize==0) return 0; |
|
449 | 506 | op += cSize; |
|
450 | 507 | } |
|
451 | 508 | |
|
452 | 509 | return op-ostart; |
|
453 | 510 | } |
|
454 | 511 | |
|
455 | 512 | |
|
513 | /* `workSpace` must a table of at least 1024 unsigned */ | |
|
456 | 514 | static size_t HUF_compress_internal ( |
|
457 | 515 | void* dst, size_t dstSize, |
|
458 | 516 | const void* src, size_t srcSize, |
|
459 | 517 | unsigned maxSymbolValue, unsigned huffLog, |
|
460 |
unsigned singleStream |
|
|
518 | unsigned singleStream, | |
|
519 | void* workSpace, size_t wkspSize) | |
|
461 | 520 | { |
|
462 | 521 | BYTE* const ostart = (BYTE*)dst; |
|
463 | 522 | BYTE* const oend = ostart + dstSize; |
|
464 | 523 | BYTE* op = ostart; |
|
465 | 524 | |
|
466 | U32 count[HUF_SYMBOLVALUE_MAX+1]; | |
|
467 |
|
|
|
525 | union { | |
|
526 | U32 count[HUF_SYMBOLVALUE_MAX+1]; | |
|
527 | HUF_CElt CTable[HUF_SYMBOLVALUE_MAX+1]; | |
|
528 | } table; /* `count` can overlap with `CTable`; saves 1 KB */ | |
|
468 | 529 | |
|
469 | 530 | /* checks & inits */ |
|
531 | if (wkspSize < sizeof(huffNodeTable)) return ERROR(GENERIC); | |
|
470 | 532 | if (!srcSize) return 0; /* Uncompressed (note : 1 means rle, so first byte must be correct) */ |
|
471 | 533 | if (!dstSize) return 0; /* cannot fit within dst budget */ |
|
472 | 534 | if (srcSize > HUF_BLOCKSIZE_MAX) return ERROR(srcSize_wrong); /* current block size limit */ |
|
473 | 535 | if (huffLog > HUF_TABLELOG_MAX) return ERROR(tableLog_tooLarge); |
|
474 | 536 | if (!maxSymbolValue) maxSymbolValue = HUF_SYMBOLVALUE_MAX; |
|
475 | 537 | if (!huffLog) huffLog = HUF_TABLELOG_DEFAULT; |
|
476 | 538 | |
|
477 | 539 | /* Scan input and build symbol stats */ |
|
478 |
{ |
|
|
479 | if (HUF_isError(largest)) return largest; | |
|
540 | { CHECK_V_F(largest, FSE_count_wksp (table.count, &maxSymbolValue, (const BYTE*)src, srcSize, (U32*)workSpace) ); | |
|
480 | 541 | if (largest == srcSize) { *ostart = ((const BYTE*)src)[0]; return 1; } /* single symbol, rle */ |
|
481 | 542 | if (largest <= (srcSize >> 7)+1) return 0; /* Fast heuristic : not compressible enough */ |
|
482 | 543 | } |
|
483 | 544 | |
|
484 | 545 | /* Build Huffman Tree */ |
|
485 | 546 | huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue); |
|
486 |
{ |
|
|
487 | if (HUF_isError(maxBits)) return maxBits; | |
|
547 | { CHECK_V_F(maxBits, HUF_buildCTable_wksp (table.CTable, table.count, maxSymbolValue, huffLog, workSpace, wkspSize) ); | |
|
488 | 548 | huffLog = (U32)maxBits; |
|
489 | 549 | } |
|
490 | 550 | |
|
491 | 551 | /* Write table description header */ |
|
492 |
{ |
|
|
493 | if (HUF_isError(hSize)) return hSize; | |
|
552 | { CHECK_V_F(hSize, HUF_writeCTable (op, dstSize, table.CTable, maxSymbolValue, huffLog) ); | |
|
494 | 553 | if (hSize + 12 >= srcSize) return 0; /* not useful to try compression */ |
|
495 | 554 | op += hSize; |
|
496 | 555 | } |
|
497 | 556 | |
|
498 | 557 | /* Compress */ |
|
499 | 558 | { size_t const cSize = (singleStream) ? |
|
500 | HUF_compress1X_usingCTable(op, oend - op, src, srcSize, CTable) : /* single segment */ | |
|
501 | HUF_compress4X_usingCTable(op, oend - op, src, srcSize, CTable); | |
|
559 | HUF_compress1X_usingCTable(op, oend - op, src, srcSize, table.CTable) : /* single segment */ | |
|
560 | HUF_compress4X_usingCTable(op, oend - op, src, srcSize, table.CTable); | |
|
502 | 561 | if (HUF_isError(cSize)) return cSize; |
|
503 | 562 | if (cSize==0) return 0; /* uncompressible */ |
|
504 | 563 | op += cSize; |
|
505 | 564 | } |
|
506 | 565 | |
|
507 | 566 | /* check compressibility */ |
|
508 | 567 | if ((size_t)(op-ostart) >= srcSize-1) |
|
509 | 568 | return 0; |
|
510 | 569 | |
|
511 | 570 | return op-ostart; |
|
512 | 571 | } |
|
513 | 572 | |
|
514 | 573 | |
|
574 | size_t HUF_compress1X_wksp (void* dst, size_t dstSize, | |
|
575 | const void* src, size_t srcSize, | |
|
576 | unsigned maxSymbolValue, unsigned huffLog, | |
|
577 | void* workSpace, size_t wkspSize) | |
|
578 | { | |
|
579 | return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 1 /* single stream */, workSpace, wkspSize); | |
|
580 | } | |
|
581 | ||
|
515 | 582 | size_t HUF_compress1X (void* dst, size_t dstSize, |
|
516 | 583 | const void* src, size_t srcSize, |
|
517 | 584 | unsigned maxSymbolValue, unsigned huffLog) |
|
518 | 585 | { |
|
519 | return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 1); | |
|
586 | unsigned workSpace[1024]; | |
|
587 | return HUF_compress1X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, workSpace, sizeof(workSpace)); | |
|
588 | } | |
|
589 | ||
|
590 | size_t HUF_compress4X_wksp (void* dst, size_t dstSize, | |
|
591 | const void* src, size_t srcSize, | |
|
592 | unsigned maxSymbolValue, unsigned huffLog, | |
|
593 | void* workSpace, size_t wkspSize) | |
|
594 | { | |
|
595 | return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 0 /* 4 streams */, workSpace, wkspSize); | |
|
520 | 596 | } |
|
521 | 597 | |
|
522 | 598 | size_t HUF_compress2 (void* dst, size_t dstSize, |
|
523 | 599 | const void* src, size_t srcSize, |
|
524 | 600 | unsigned maxSymbolValue, unsigned huffLog) |
|
525 | 601 | { |
|
526 | return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 0); | |
|
602 | unsigned workSpace[1024]; | |
|
603 | return HUF_compress4X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, workSpace, sizeof(workSpace)); | |
|
527 | 604 | } |
|
528 | 605 | |
|
529 | ||
|
530 | 606 | size_t HUF_compress (void* dst, size_t maxDstSize, const void* src, size_t srcSize) |
|
531 | 607 | { |
|
532 | 608 | return HUF_compress2(dst, maxDstSize, src, (U32)srcSize, 255, HUF_TABLELOG_DEFAULT); |
|
533 | 609 | } |
@@ -1,3264 +1,3291 b'' | |||
|
1 | 1 | /** |
|
2 | 2 | * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. |
|
3 | 3 | * All rights reserved. |
|
4 | 4 | * |
|
5 | 5 | * This source code is licensed under the BSD-style license found in the |
|
6 | 6 | * LICENSE file in the root directory of this source tree. An additional grant |
|
7 | 7 | * of patent rights can be found in the PATENTS file in the same directory. |
|
8 | 8 | */ |
|
9 | 9 | |
|
10 | 10 | |
|
11 | 11 | /*-************************************* |
|
12 | 12 | * Dependencies |
|
13 | 13 | ***************************************/ |
|
14 | 14 | #include <string.h> /* memset */ |
|
15 | 15 | #include "mem.h" |
|
16 | 16 | #define XXH_STATIC_LINKING_ONLY /* XXH64_state_t */ |
|
17 | 17 | #include "xxhash.h" /* XXH_reset, update, digest */ |
|
18 | 18 | #define FSE_STATIC_LINKING_ONLY /* FSE_encodeSymbol */ |
|
19 | 19 | #include "fse.h" |
|
20 | 20 | #define HUF_STATIC_LINKING_ONLY |
|
21 | 21 | #include "huf.h" |
|
22 | 22 | #include "zstd_internal.h" /* includes zstd.h */ |
|
23 | 23 | |
|
24 | 24 | |
|
25 | 25 | /*-************************************* |
|
26 | 26 | * Constants |
|
27 | 27 | ***************************************/ |
|
28 | 28 | static const U32 g_searchStrength = 8; /* control skip over incompressible data */ |
|
29 | 29 | #define HASH_READ_SIZE 8 |
|
30 | 30 | typedef enum { ZSTDcs_created=0, ZSTDcs_init, ZSTDcs_ongoing, ZSTDcs_ending } ZSTD_compressionStage_e; |
|
31 | 31 | |
|
32 | 32 | |
|
33 | 33 | /*-************************************* |
|
34 | 34 | * Helper functions |
|
35 | 35 | ***************************************/ |
|
36 | #define ZSTD_STATIC_ASSERT(c) { enum { ZSTD_static_assert = 1/(int)(!!(c)) }; } | |
|
36 | 37 | size_t ZSTD_compressBound(size_t srcSize) { return FSE_compressBound(srcSize) + 12; } |
|
37 | 38 | |
|
38 | 39 | |
|
39 | 40 | /*-************************************* |
|
40 | 41 | * Sequence storage |
|
41 | 42 | ***************************************/ |
|
42 | 43 | static void ZSTD_resetSeqStore(seqStore_t* ssPtr) |
|
43 | 44 | { |
|
44 | 45 | ssPtr->lit = ssPtr->litStart; |
|
45 | 46 | ssPtr->sequences = ssPtr->sequencesStart; |
|
46 | 47 | ssPtr->longLengthID = 0; |
|
47 | 48 | } |
|
48 | 49 | |
|
49 | 50 | |
|
50 | 51 | /*-************************************* |
|
51 | 52 | * Context memory management |
|
52 | 53 | ***************************************/ |
|
53 | 54 | struct ZSTD_CCtx_s |
|
54 | 55 | { |
|
55 | 56 | const BYTE* nextSrc; /* next block here to continue on current prefix */ |
|
56 | 57 | const BYTE* base; /* All regular indexes relative to this position */ |
|
57 | 58 | const BYTE* dictBase; /* extDict indexes relative to this position */ |
|
58 | 59 | U32 dictLimit; /* below that point, need extDict */ |
|
59 | 60 | U32 lowLimit; /* below that point, no more data */ |
|
60 | 61 | U32 nextToUpdate; /* index from which to continue dictionary update */ |
|
61 | 62 | U32 nextToUpdate3; /* index from which to continue dictionary update */ |
|
62 | 63 | U32 hashLog3; /* dispatch table : larger == faster, more memory */ |
|
63 | 64 | U32 loadedDictEnd; |
|
64 | 65 | ZSTD_compressionStage_e stage; |
|
65 | 66 | U32 rep[ZSTD_REP_NUM]; |
|
66 | 67 | U32 savedRep[ZSTD_REP_NUM]; |
|
67 | 68 | U32 dictID; |
|
68 | 69 | ZSTD_parameters params; |
|
69 | 70 | void* workSpace; |
|
70 | 71 | size_t workSpaceSize; |
|
71 | 72 | size_t blockSize; |
|
72 | 73 | U64 frameContentSize; |
|
73 | 74 | XXH64_state_t xxhState; |
|
74 | 75 | ZSTD_customMem customMem; |
|
75 | 76 | |
|
76 | 77 | seqStore_t seqStore; /* sequences storage ptrs */ |
|
77 | 78 | U32* hashTable; |
|
78 | 79 | U32* hashTable3; |
|
79 | 80 | U32* chainTable; |
|
80 | 81 | HUF_CElt* hufTable; |
|
81 | 82 | U32 flagStaticTables; |
|
82 | 83 | FSE_CTable offcodeCTable [FSE_CTABLE_SIZE_U32(OffFSELog, MaxOff)]; |
|
83 | 84 | FSE_CTable matchlengthCTable[FSE_CTABLE_SIZE_U32(MLFSELog, MaxML)]; |
|
84 | 85 | FSE_CTable litlengthCTable [FSE_CTABLE_SIZE_U32(LLFSELog, MaxLL)]; |
|
86 | unsigned tmpCounters[1024]; | |
|
85 | 87 | }; |
|
86 | 88 | |
|
87 | 89 | ZSTD_CCtx* ZSTD_createCCtx(void) |
|
88 | 90 | { |
|
89 | 91 | return ZSTD_createCCtx_advanced(defaultCustomMem); |
|
90 | 92 | } |
|
91 | 93 | |
|
92 | 94 | ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem) |
|
93 | 95 | { |
|
94 | 96 | ZSTD_CCtx* cctx; |
|
95 | 97 | |
|
96 | 98 | if (!customMem.customAlloc && !customMem.customFree) customMem = defaultCustomMem; |
|
97 | 99 | if (!customMem.customAlloc || !customMem.customFree) return NULL; |
|
98 | 100 | |
|
99 | 101 | cctx = (ZSTD_CCtx*) ZSTD_malloc(sizeof(ZSTD_CCtx), customMem); |
|
100 | 102 | if (!cctx) return NULL; |
|
101 | 103 | memset(cctx, 0, sizeof(ZSTD_CCtx)); |
|
102 | 104 | memcpy(&(cctx->customMem), &customMem, sizeof(customMem)); |
|
103 | 105 | return cctx; |
|
104 | 106 | } |
|
105 | 107 | |
|
106 | 108 | size_t ZSTD_freeCCtx(ZSTD_CCtx* cctx) |
|
107 | 109 | { |
|
108 | 110 | if (cctx==NULL) return 0; /* support free on NULL */ |
|
109 | 111 | ZSTD_free(cctx->workSpace, cctx->customMem); |
|
110 | 112 | ZSTD_free(cctx, cctx->customMem); |
|
111 | 113 | return 0; /* reserved as a potential error code in the future */ |
|
112 | 114 | } |
|
113 | 115 | |
|
114 | 116 | size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx) |
|
115 | 117 | { |
|
116 | 118 | if (cctx==NULL) return 0; /* support sizeof on NULL */ |
|
117 | 119 | return sizeof(*cctx) + cctx->workSpaceSize; |
|
118 | 120 | } |
|
119 | 121 | |
|
120 | 122 | const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx) /* hidden interface */ |
|
121 | 123 | { |
|
122 | 124 | return &(ctx->seqStore); |
|
123 | 125 | } |
|
124 | 126 | |
|
125 | 127 | static ZSTD_parameters ZSTD_getParamsFromCCtx(const ZSTD_CCtx* cctx) |
|
126 | 128 | { |
|
127 | 129 | return cctx->params; |
|
128 | 130 | } |
|
129 | 131 | |
|
130 | 132 | |
|
131 | 133 | /** ZSTD_checkParams() : |
|
132 | 134 | ensure param values remain within authorized range. |
|
133 | 135 | @return : 0, or an error code if one value is beyond authorized range */ |
|
134 | 136 | size_t ZSTD_checkCParams(ZSTD_compressionParameters cParams) |
|
135 | 137 | { |
|
136 | 138 | # define CLAMPCHECK(val,min,max) { if ((val<min) | (val>max)) return ERROR(compressionParameter_unsupported); } |
|
137 | 139 | CLAMPCHECK(cParams.windowLog, ZSTD_WINDOWLOG_MIN, ZSTD_WINDOWLOG_MAX); |
|
138 | 140 | CLAMPCHECK(cParams.chainLog, ZSTD_CHAINLOG_MIN, ZSTD_CHAINLOG_MAX); |
|
139 | 141 | CLAMPCHECK(cParams.hashLog, ZSTD_HASHLOG_MIN, ZSTD_HASHLOG_MAX); |
|
140 | 142 | CLAMPCHECK(cParams.searchLog, ZSTD_SEARCHLOG_MIN, ZSTD_SEARCHLOG_MAX); |
|
141 | 143 | { U32 const searchLengthMin = ((cParams.strategy == ZSTD_fast) | (cParams.strategy == ZSTD_greedy)) ? ZSTD_SEARCHLENGTH_MIN+1 : ZSTD_SEARCHLENGTH_MIN; |
|
142 | 144 | U32 const searchLengthMax = (cParams.strategy == ZSTD_fast) ? ZSTD_SEARCHLENGTH_MAX : ZSTD_SEARCHLENGTH_MAX-1; |
|
143 | 145 | CLAMPCHECK(cParams.searchLength, searchLengthMin, searchLengthMax); } |
|
144 | 146 | CLAMPCHECK(cParams.targetLength, ZSTD_TARGETLENGTH_MIN, ZSTD_TARGETLENGTH_MAX); |
|
145 | 147 | if ((U32)(cParams.strategy) > (U32)ZSTD_btopt2) return ERROR(compressionParameter_unsupported); |
|
146 | 148 | return 0; |
|
147 | 149 | } |
|
148 | 150 | |
|
149 | 151 | |
|
152 | /** ZSTD_cycleLog() : | |
|
153 | * condition for correct operation : hashLog > 1 */ | |
|
154 | static U32 ZSTD_cycleLog(U32 hashLog, ZSTD_strategy strat) | |
|
155 | { | |
|
156 | U32 const btScale = ((U32)strat >= (U32)ZSTD_btlazy2); | |
|
157 | return hashLog - btScale; | |
|
158 | } | |
|
159 | ||
|
150 | 160 | /** ZSTD_adjustCParams() : |
|
151 | 161 | optimize `cPar` for a given input (`srcSize` and `dictSize`). |
|
152 | 162 | mostly downsizing to reduce memory consumption and initialization. |
|
153 | 163 | Both `srcSize` and `dictSize` are optional (use 0 if unknown), |
|
154 | 164 | but if both are 0, no optimization can be done. |
|
155 | 165 | Note : cPar is considered validated at this stage. Use ZSTD_checkParams() to ensure that. */ |
|
156 | 166 | ZSTD_compressionParameters ZSTD_adjustCParams(ZSTD_compressionParameters cPar, unsigned long long srcSize, size_t dictSize) |
|
157 | 167 | { |
|
158 | 168 | if (srcSize+dictSize == 0) return cPar; /* no size information available : no adjustment */ |
|
159 | 169 | |
|
160 | 170 | /* resize params, to use less memory when necessary */ |
|
161 | 171 | { U32 const minSrcSize = (srcSize==0) ? 500 : 0; |
|
162 | 172 | U64 const rSize = srcSize + dictSize + minSrcSize; |
|
163 | 173 | if (rSize < ((U64)1<<ZSTD_WINDOWLOG_MAX)) { |
|
164 | 174 | U32 const srcLog = MAX(ZSTD_HASHLOG_MIN, ZSTD_highbit32((U32)(rSize)-1) + 1); |
|
165 | 175 | if (cPar.windowLog > srcLog) cPar.windowLog = srcLog; |
|
166 | 176 | } } |
|
167 | 177 | if (cPar.hashLog > cPar.windowLog) cPar.hashLog = cPar.windowLog; |
|
168 | { U32 const btPlus = (cPar.strategy == ZSTD_btlazy2) | (cPar.strategy == ZSTD_btopt) | (cPar.strategy == ZSTD_btopt2); | |
|
169 | U32 const maxChainLog = cPar.windowLog+btPlus; | |
|
170 | if (cPar.chainLog > maxChainLog) cPar.chainLog = maxChainLog; } /* <= ZSTD_CHAINLOG_MAX */ | |
|
178 | { U32 const cycleLog = ZSTD_cycleLog(cPar.chainLog, cPar.strategy); | |
|
179 | if (cycleLog > cPar.windowLog) cPar.chainLog -= (cycleLog - cPar.windowLog); | |
|
180 | } | |
|
171 | 181 | |
|
172 | 182 | if (cPar.windowLog < ZSTD_WINDOWLOG_ABSOLUTEMIN) cPar.windowLog = ZSTD_WINDOWLOG_ABSOLUTEMIN; /* required for frame header */ |
|
173 | 183 | |
|
174 | 184 | return cPar; |
|
175 | 185 | } |
|
176 | 186 | |
|
177 | 187 | |
|
178 | 188 | size_t ZSTD_estimateCCtxSize(ZSTD_compressionParameters cParams) |
|
179 | 189 | { |
|
180 | 190 | size_t const blockSize = MIN(ZSTD_BLOCKSIZE_ABSOLUTEMAX, (size_t)1 << cParams.windowLog); |
|
181 | 191 | U32 const divider = (cParams.searchLength==3) ? 3 : 4; |
|
182 | 192 | size_t const maxNbSeq = blockSize / divider; |
|
183 | 193 | size_t const tokenSpace = blockSize + 11*maxNbSeq; |
|
184 | 194 | |
|
185 | 195 | size_t const chainSize = (cParams.strategy == ZSTD_fast) ? 0 : (1 << cParams.chainLog); |
|
186 | 196 | size_t const hSize = ((size_t)1) << cParams.hashLog; |
|
187 | 197 | U32 const hashLog3 = (cParams.searchLength>3) ? 0 : MIN(ZSTD_HASHLOG3_MAX, cParams.windowLog); |
|
188 | 198 | size_t const h3Size = ((size_t)1) << hashLog3; |
|
189 | 199 | size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32); |
|
190 | 200 | |
|
191 | 201 | size_t const optSpace = ((MaxML+1) + (MaxLL+1) + (MaxOff+1) + (1<<Litbits))*sizeof(U32) |
|
192 | 202 | + (ZSTD_OPT_NUM+1)*(sizeof(ZSTD_match_t) + sizeof(ZSTD_optimal_t)); |
|
193 | 203 | size_t const neededSpace = tableSpace + (256*sizeof(U32)) /* huffTable */ + tokenSpace |
|
194 | 204 | + (((cParams.strategy == ZSTD_btopt) || (cParams.strategy == ZSTD_btopt2)) ? optSpace : 0); |
|
195 | 205 | |
|
196 | 206 | return sizeof(ZSTD_CCtx) + neededSpace; |
|
197 | 207 | } |
|
198 | 208 | |
|
199 | 209 | |
|
200 | 210 | static U32 ZSTD_equivalentParams(ZSTD_parameters param1, ZSTD_parameters param2) |
|
201 | 211 | { |
|
202 | 212 | return (param1.cParams.hashLog == param2.cParams.hashLog) |
|
203 | 213 | & (param1.cParams.chainLog == param2.cParams.chainLog) |
|
204 | 214 | & (param1.cParams.strategy == param2.cParams.strategy) |
|
205 | 215 | & ((param1.cParams.searchLength==3) == (param2.cParams.searchLength==3)); |
|
206 | 216 | } |
|
207 | 217 | |
|
208 | 218 | /*! ZSTD_continueCCtx() : |
|
209 | 219 | reuse CCtx without reset (note : requires no dictionary) */ |
|
210 | 220 | static size_t ZSTD_continueCCtx(ZSTD_CCtx* cctx, ZSTD_parameters params, U64 frameContentSize) |
|
211 | 221 | { |
|
212 | 222 | U32 const end = (U32)(cctx->nextSrc - cctx->base); |
|
213 | 223 | cctx->params = params; |
|
214 | 224 | cctx->frameContentSize = frameContentSize; |
|
215 | 225 | cctx->lowLimit = end; |
|
216 | 226 | cctx->dictLimit = end; |
|
217 | 227 | cctx->nextToUpdate = end+1; |
|
218 | 228 | cctx->stage = ZSTDcs_init; |
|
219 | 229 | cctx->dictID = 0; |
|
220 | 230 | cctx->loadedDictEnd = 0; |
|
221 | 231 | { int i; for (i=0; i<ZSTD_REP_NUM; i++) cctx->rep[i] = repStartValue[i]; } |
|
222 | 232 | cctx->seqStore.litLengthSum = 0; /* force reset of btopt stats */ |
|
223 | 233 | XXH64_reset(&cctx->xxhState, 0); |
|
224 | 234 | return 0; |
|
225 | 235 | } |
|
226 | 236 | |
|
227 | 237 | typedef enum { ZSTDcrp_continue, ZSTDcrp_noMemset, ZSTDcrp_fullReset } ZSTD_compResetPolicy_e; |
|
228 | 238 | |
|
229 | 239 | /*! ZSTD_resetCCtx_advanced() : |
|
230 | 240 | note : 'params' must be validated */ |
|
231 | 241 | static size_t ZSTD_resetCCtx_advanced (ZSTD_CCtx* zc, |
|
232 | 242 | ZSTD_parameters params, U64 frameContentSize, |
|
233 | 243 | ZSTD_compResetPolicy_e const crp) |
|
234 | 244 | { |
|
235 | 245 | if (crp == ZSTDcrp_continue) |
|
236 | 246 | if (ZSTD_equivalentParams(params, zc->params)) |
|
237 | 247 | return ZSTD_continueCCtx(zc, params, frameContentSize); |
|
238 | 248 | |
|
239 | 249 | { size_t const blockSize = MIN(ZSTD_BLOCKSIZE_ABSOLUTEMAX, (size_t)1 << params.cParams.windowLog); |
|
240 | 250 | U32 const divider = (params.cParams.searchLength==3) ? 3 : 4; |
|
241 | 251 | size_t const maxNbSeq = blockSize / divider; |
|
242 | 252 | size_t const tokenSpace = blockSize + 11*maxNbSeq; |
|
243 | 253 | size_t const chainSize = (params.cParams.strategy == ZSTD_fast) ? 0 : (1 << params.cParams.chainLog); |
|
244 | 254 | size_t const hSize = ((size_t)1) << params.cParams.hashLog; |
|
245 | 255 | U32 const hashLog3 = (params.cParams.searchLength>3) ? 0 : MIN(ZSTD_HASHLOG3_MAX, params.cParams.windowLog); |
|
246 | 256 | size_t const h3Size = ((size_t)1) << hashLog3; |
|
247 | 257 | size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32); |
|
248 | 258 | void* ptr; |
|
249 | 259 | |
|
250 | 260 | /* Check if workSpace is large enough, alloc a new one if needed */ |
|
251 | 261 | { size_t const optSpace = ((MaxML+1) + (MaxLL+1) + (MaxOff+1) + (1<<Litbits))*sizeof(U32) |
|
252 | 262 | + (ZSTD_OPT_NUM+1)*(sizeof(ZSTD_match_t) + sizeof(ZSTD_optimal_t)); |
|
253 | 263 | size_t const neededSpace = tableSpace + (256*sizeof(U32)) /* huffTable */ + tokenSpace |
|
254 | 264 | + (((params.cParams.strategy == ZSTD_btopt) || (params.cParams.strategy == ZSTD_btopt2)) ? optSpace : 0); |
|
255 | 265 | if (zc->workSpaceSize < neededSpace) { |
|
256 | 266 | ZSTD_free(zc->workSpace, zc->customMem); |
|
257 | 267 | zc->workSpace = ZSTD_malloc(neededSpace, zc->customMem); |
|
258 | 268 | if (zc->workSpace == NULL) return ERROR(memory_allocation); |
|
259 | 269 | zc->workSpaceSize = neededSpace; |
|
260 | 270 | } } |
|
261 | 271 | |
|
262 | 272 | if (crp!=ZSTDcrp_noMemset) memset(zc->workSpace, 0, tableSpace); /* reset tables only */ |
|
263 | 273 | XXH64_reset(&zc->xxhState, 0); |
|
264 | 274 | zc->hashLog3 = hashLog3; |
|
265 | 275 | zc->hashTable = (U32*)(zc->workSpace); |
|
266 | 276 | zc->chainTable = zc->hashTable + hSize; |
|
267 | 277 | zc->hashTable3 = zc->chainTable + chainSize; |
|
268 | 278 | ptr = zc->hashTable3 + h3Size; |
|
269 | 279 | zc->hufTable = (HUF_CElt*)ptr; |
|
270 | 280 | zc->flagStaticTables = 0; |
|
271 | 281 | ptr = ((U32*)ptr) + 256; /* note : HUF_CElt* is incomplete type, size is simulated using U32 */ |
|
272 | 282 | |
|
273 | 283 | zc->nextToUpdate = 1; |
|
274 | 284 | zc->nextSrc = NULL; |
|
275 | 285 | zc->base = NULL; |
|
276 | 286 | zc->dictBase = NULL; |
|
277 | 287 | zc->dictLimit = 0; |
|
278 | 288 | zc->lowLimit = 0; |
|
279 | 289 | zc->params = params; |
|
280 | 290 | zc->blockSize = blockSize; |
|
281 | 291 | zc->frameContentSize = frameContentSize; |
|
282 | 292 | { int i; for (i=0; i<ZSTD_REP_NUM; i++) zc->rep[i] = repStartValue[i]; } |
|
283 | 293 | |
|
284 | 294 | if ((params.cParams.strategy == ZSTD_btopt) || (params.cParams.strategy == ZSTD_btopt2)) { |
|
285 | 295 | zc->seqStore.litFreq = (U32*)ptr; |
|
286 | 296 | zc->seqStore.litLengthFreq = zc->seqStore.litFreq + (1<<Litbits); |
|
287 | 297 | zc->seqStore.matchLengthFreq = zc->seqStore.litLengthFreq + (MaxLL+1); |
|
288 | 298 | zc->seqStore.offCodeFreq = zc->seqStore.matchLengthFreq + (MaxML+1); |
|
289 | 299 | ptr = zc->seqStore.offCodeFreq + (MaxOff+1); |
|
290 | 300 | zc->seqStore.matchTable = (ZSTD_match_t*)ptr; |
|
291 | 301 | ptr = zc->seqStore.matchTable + ZSTD_OPT_NUM+1; |
|
292 | 302 | zc->seqStore.priceTable = (ZSTD_optimal_t*)ptr; |
|
293 | 303 | ptr = zc->seqStore.priceTable + ZSTD_OPT_NUM+1; |
|
294 | 304 | zc->seqStore.litLengthSum = 0; |
|
295 | 305 | } |
|
296 | 306 | zc->seqStore.sequencesStart = (seqDef*)ptr; |
|
297 | 307 | ptr = zc->seqStore.sequencesStart + maxNbSeq; |
|
298 | 308 | zc->seqStore.llCode = (BYTE*) ptr; |
|
299 | 309 | zc->seqStore.mlCode = zc->seqStore.llCode + maxNbSeq; |
|
300 | 310 | zc->seqStore.ofCode = zc->seqStore.mlCode + maxNbSeq; |
|
301 | 311 | zc->seqStore.litStart = zc->seqStore.ofCode + maxNbSeq; |
|
302 | 312 | |
|
303 | 313 | zc->stage = ZSTDcs_init; |
|
304 | 314 | zc->dictID = 0; |
|
305 | 315 | zc->loadedDictEnd = 0; |
|
306 | 316 | |
|
307 | 317 | return 0; |
|
308 | 318 | } |
|
309 | 319 | } |
|
310 | 320 | |
|
311 | 321 | |
|
312 | 322 | /*! ZSTD_copyCCtx() : |
|
313 | 323 | * Duplicate an existing context `srcCCtx` into another one `dstCCtx`. |
|
314 | 324 | * Only works during stage ZSTDcs_init (i.e. after creation, but before first call to ZSTD_compressContinue()). |
|
315 | 325 | * @return : 0, or an error code */ |
|
316 | 326 | size_t ZSTD_copyCCtx(ZSTD_CCtx* dstCCtx, const ZSTD_CCtx* srcCCtx, unsigned long long pledgedSrcSize) |
|
317 | 327 | { |
|
318 | 328 | if (srcCCtx->stage!=ZSTDcs_init) return ERROR(stage_wrong); |
|
319 | 329 | |
|
320 | 330 | memcpy(&dstCCtx->customMem, &srcCCtx->customMem, sizeof(ZSTD_customMem)); |
|
321 | 331 | ZSTD_resetCCtx_advanced(dstCCtx, srcCCtx->params, pledgedSrcSize, ZSTDcrp_noMemset); |
|
322 | 332 | |
|
323 | 333 | /* copy tables */ |
|
324 | 334 | { size_t const chainSize = (srcCCtx->params.cParams.strategy == ZSTD_fast) ? 0 : (1 << srcCCtx->params.cParams.chainLog); |
|
325 | 335 | size_t const hSize = ((size_t)1) << srcCCtx->params.cParams.hashLog; |
|
326 | 336 | size_t const h3Size = (size_t)1 << srcCCtx->hashLog3; |
|
327 | 337 | size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32); |
|
328 | 338 | memcpy(dstCCtx->workSpace, srcCCtx->workSpace, tableSpace); |
|
329 | 339 | } |
|
330 | 340 | |
|
331 | 341 | /* copy dictionary offsets */ |
|
332 | 342 | dstCCtx->nextToUpdate = srcCCtx->nextToUpdate; |
|
333 | 343 | dstCCtx->nextToUpdate3= srcCCtx->nextToUpdate3; |
|
334 | 344 | dstCCtx->nextSrc = srcCCtx->nextSrc; |
|
335 | 345 | dstCCtx->base = srcCCtx->base; |
|
336 | 346 | dstCCtx->dictBase = srcCCtx->dictBase; |
|
337 | 347 | dstCCtx->dictLimit = srcCCtx->dictLimit; |
|
338 | 348 | dstCCtx->lowLimit = srcCCtx->lowLimit; |
|
339 | 349 | dstCCtx->loadedDictEnd= srcCCtx->loadedDictEnd; |
|
340 | 350 | dstCCtx->dictID = srcCCtx->dictID; |
|
341 | 351 | |
|
342 | 352 | /* copy entropy tables */ |
|
343 | 353 | dstCCtx->flagStaticTables = srcCCtx->flagStaticTables; |
|
344 | 354 | if (srcCCtx->flagStaticTables) { |
|
345 | 355 | memcpy(dstCCtx->hufTable, srcCCtx->hufTable, 256*4); |
|
346 | 356 | memcpy(dstCCtx->litlengthCTable, srcCCtx->litlengthCTable, sizeof(dstCCtx->litlengthCTable)); |
|
347 | 357 | memcpy(dstCCtx->matchlengthCTable, srcCCtx->matchlengthCTable, sizeof(dstCCtx->matchlengthCTable)); |
|
348 | 358 | memcpy(dstCCtx->offcodeCTable, srcCCtx->offcodeCTable, sizeof(dstCCtx->offcodeCTable)); |
|
349 | 359 | } |
|
350 | 360 | |
|
351 | 361 | return 0; |
|
352 | 362 | } |
|
353 | 363 | |
|
354 | 364 | |
|
355 | 365 | /*! ZSTD_reduceTable() : |
|
356 | 366 | * reduce table indexes by `reducerValue` */ |
|
357 | 367 | static void ZSTD_reduceTable (U32* const table, U32 const size, U32 const reducerValue) |
|
358 | 368 | { |
|
359 | 369 | U32 u; |
|
360 | 370 | for (u=0 ; u < size ; u++) { |
|
361 | 371 | if (table[u] < reducerValue) table[u] = 0; |
|
362 | 372 | else table[u] -= reducerValue; |
|
363 | 373 | } |
|
364 | 374 | } |
|
365 | 375 | |
|
366 | 376 | /*! ZSTD_reduceIndex() : |
|
367 | 377 | * rescale all indexes to avoid future overflow (indexes are U32) */ |
|
368 | 378 | static void ZSTD_reduceIndex (ZSTD_CCtx* zc, const U32 reducerValue) |
|
369 | 379 | { |
|
370 | 380 | { U32 const hSize = 1 << zc->params.cParams.hashLog; |
|
371 | 381 | ZSTD_reduceTable(zc->hashTable, hSize, reducerValue); } |
|
372 | 382 | |
|
373 | 383 | { U32 const chainSize = (zc->params.cParams.strategy == ZSTD_fast) ? 0 : (1 << zc->params.cParams.chainLog); |
|
374 | 384 | ZSTD_reduceTable(zc->chainTable, chainSize, reducerValue); } |
|
375 | 385 | |
|
376 | 386 | { U32 const h3Size = (zc->hashLog3) ? 1 << zc->hashLog3 : 0; |
|
377 | 387 | ZSTD_reduceTable(zc->hashTable3, h3Size, reducerValue); } |
|
378 | 388 | } |
|
379 | 389 | |
|
380 | 390 | |
|
381 | 391 | /*-******************************************************* |
|
382 | 392 | * Block entropic compression |
|
383 | 393 | *********************************************************/ |
|
384 | 394 | |
|
385 | 395 | /* See doc/zstd_compression_format.md for detailed format description */ |
|
386 | 396 | |
|
387 | 397 | size_t ZSTD_noCompressBlock (void* dst, size_t dstCapacity, const void* src, size_t srcSize) |
|
388 | 398 | { |
|
389 | 399 | if (srcSize + ZSTD_blockHeaderSize > dstCapacity) return ERROR(dstSize_tooSmall); |
|
390 | 400 | memcpy((BYTE*)dst + ZSTD_blockHeaderSize, src, srcSize); |
|
391 | 401 | MEM_writeLE24(dst, (U32)(srcSize << 2) + (U32)bt_raw); |
|
392 | 402 | return ZSTD_blockHeaderSize+srcSize; |
|
393 | 403 | } |
|
394 | 404 | |
|
395 | 405 | |
|
396 | 406 | static size_t ZSTD_noCompressLiterals (void* dst, size_t dstCapacity, const void* src, size_t srcSize) |
|
397 | 407 | { |
|
398 | 408 | BYTE* const ostart = (BYTE* const)dst; |
|
399 | 409 | U32 const flSize = 1 + (srcSize>31) + (srcSize>4095); |
|
400 | 410 | |
|
401 | 411 | if (srcSize + flSize > dstCapacity) return ERROR(dstSize_tooSmall); |
|
402 | 412 | |
|
403 | 413 | switch(flSize) |
|
404 | 414 | { |
|
405 | 415 | case 1: /* 2 - 1 - 5 */ |
|
406 | 416 | ostart[0] = (BYTE)((U32)set_basic + (srcSize<<3)); |
|
407 | 417 | break; |
|
408 | 418 | case 2: /* 2 - 2 - 12 */ |
|
409 | 419 | MEM_writeLE16(ostart, (U16)((U32)set_basic + (1<<2) + (srcSize<<4))); |
|
410 | 420 | break; |
|
411 | 421 | default: /*note : should not be necessary : flSize is within {1,2,3} */ |
|
412 | 422 | case 3: /* 2 - 2 - 20 */ |
|
413 | 423 | MEM_writeLE32(ostart, (U32)((U32)set_basic + (3<<2) + (srcSize<<4))); |
|
414 | 424 | break; |
|
415 | 425 | } |
|
416 | 426 | |
|
417 | 427 | memcpy(ostart + flSize, src, srcSize); |
|
418 | 428 | return srcSize + flSize; |
|
419 | 429 | } |
|
420 | 430 | |
|
421 | 431 | static size_t ZSTD_compressRleLiteralsBlock (void* dst, size_t dstCapacity, const void* src, size_t srcSize) |
|
422 | 432 | { |
|
423 | 433 | BYTE* const ostart = (BYTE* const)dst; |
|
424 | 434 | U32 const flSize = 1 + (srcSize>31) + (srcSize>4095); |
|
425 | 435 | |
|
426 | 436 | (void)dstCapacity; /* dstCapacity already guaranteed to be >=4, hence large enough */ |
|
427 | 437 | |
|
428 | 438 | switch(flSize) |
|
429 | 439 | { |
|
430 | 440 | case 1: /* 2 - 1 - 5 */ |
|
431 | 441 | ostart[0] = (BYTE)((U32)set_rle + (srcSize<<3)); |
|
432 | 442 | break; |
|
433 | 443 | case 2: /* 2 - 2 - 12 */ |
|
434 | 444 | MEM_writeLE16(ostart, (U16)((U32)set_rle + (1<<2) + (srcSize<<4))); |
|
435 | 445 | break; |
|
436 | 446 | default: /*note : should not be necessary : flSize is necessarily within {1,2,3} */ |
|
437 | 447 | case 3: /* 2 - 2 - 20 */ |
|
438 | 448 | MEM_writeLE32(ostart, (U32)((U32)set_rle + (3<<2) + (srcSize<<4))); |
|
439 | 449 | break; |
|
440 | 450 | } |
|
441 | 451 | |
|
442 | 452 | ostart[flSize] = *(const BYTE*)src; |
|
443 | 453 | return flSize+1; |
|
444 | 454 | } |
|
445 | 455 | |
|
446 | 456 | |
|
447 | 457 | static size_t ZSTD_minGain(size_t srcSize) { return (srcSize >> 6) + 2; } |
|
448 | 458 | |
|
449 | 459 | static size_t ZSTD_compressLiterals (ZSTD_CCtx* zc, |
|
450 | 460 | void* dst, size_t dstCapacity, |
|
451 | 461 | const void* src, size_t srcSize) |
|
452 | 462 | { |
|
453 | 463 | size_t const minGain = ZSTD_minGain(srcSize); |
|
454 | 464 | size_t const lhSize = 3 + (srcSize >= 1 KB) + (srcSize >= 16 KB); |
|
455 | 465 | BYTE* const ostart = (BYTE*)dst; |
|
456 | 466 | U32 singleStream = srcSize < 256; |
|
457 | 467 | symbolEncodingType_e hType = set_compressed; |
|
458 | 468 | size_t cLitSize; |
|
459 | 469 | |
|
460 | 470 | |
|
461 | 471 | /* small ? don't even attempt compression (speed opt) */ |
|
462 | 472 | # define LITERAL_NOENTROPY 63 |
|
463 | 473 | { size_t const minLitSize = zc->flagStaticTables ? 6 : LITERAL_NOENTROPY; |
|
464 | 474 | if (srcSize <= minLitSize) return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); |
|
465 | 475 | } |
|
466 | 476 | |
|
467 | 477 | if (dstCapacity < lhSize+1) return ERROR(dstSize_tooSmall); /* not enough space for compression */ |
|
468 | 478 | if (zc->flagStaticTables && (lhSize==3)) { |
|
469 | 479 | hType = set_repeat; |
|
470 | 480 | singleStream = 1; |
|
471 | 481 | cLitSize = HUF_compress1X_usingCTable(ostart+lhSize, dstCapacity-lhSize, src, srcSize, zc->hufTable); |
|
472 | 482 | } else { |
|
473 | cLitSize = singleStream ? HUF_compress1X(ostart+lhSize, dstCapacity-lhSize, src, srcSize, 255, 11) | |
|
474 |
: HUF_compress |
|
|
483 | cLitSize = singleStream ? HUF_compress1X_wksp(ostart+lhSize, dstCapacity-lhSize, src, srcSize, 255, 11, zc->tmpCounters, sizeof(zc->tmpCounters)) | |
|
484 | : HUF_compress4X_wksp(ostart+lhSize, dstCapacity-lhSize, src, srcSize, 255, 11, zc->tmpCounters, sizeof(zc->tmpCounters)); | |
|
475 | 485 | } |
|
476 | 486 | |
|
477 | 487 | if ((cLitSize==0) | (cLitSize >= srcSize - minGain)) |
|
478 | 488 | return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); |
|
479 | 489 | if (cLitSize==1) |
|
480 | 490 | return ZSTD_compressRleLiteralsBlock(dst, dstCapacity, src, srcSize); |
|
481 | 491 | |
|
482 | 492 | /* Build header */ |
|
483 | 493 | switch(lhSize) |
|
484 | 494 | { |
|
485 | 495 | case 3: /* 2 - 2 - 10 - 10 */ |
|
486 | 496 | { U32 const lhc = hType + ((!singleStream) << 2) + ((U32)srcSize<<4) + ((U32)cLitSize<<14); |
|
487 | 497 | MEM_writeLE24(ostart, lhc); |
|
488 | 498 | break; |
|
489 | 499 | } |
|
490 | 500 | case 4: /* 2 - 2 - 14 - 14 */ |
|
491 | 501 | { U32 const lhc = hType + (2 << 2) + ((U32)srcSize<<4) + ((U32)cLitSize<<18); |
|
492 | 502 | MEM_writeLE32(ostart, lhc); |
|
493 | 503 | break; |
|
494 | 504 | } |
|
495 | 505 | default: /* should not be necessary, lhSize is only {3,4,5} */ |
|
496 | 506 | case 5: /* 2 - 2 - 18 - 18 */ |
|
497 | 507 | { U32 const lhc = hType + (3 << 2) + ((U32)srcSize<<4) + ((U32)cLitSize<<22); |
|
498 | 508 | MEM_writeLE32(ostart, lhc); |
|
499 | 509 | ostart[4] = (BYTE)(cLitSize >> 10); |
|
500 | 510 | break; |
|
501 | 511 | } |
|
502 | 512 | } |
|
503 | 513 | return lhSize+cLitSize; |
|
504 | 514 | } |
|
505 | 515 | |
|
506 | 516 | static const BYTE LL_Code[64] = { 0, 1, 2, 3, 4, 5, 6, 7, |
|
507 | 517 | 8, 9, 10, 11, 12, 13, 14, 15, |
|
508 | 518 | 16, 16, 17, 17, 18, 18, 19, 19, |
|
509 | 519 | 20, 20, 20, 20, 21, 21, 21, 21, |
|
510 | 520 | 22, 22, 22, 22, 22, 22, 22, 22, |
|
511 | 521 | 23, 23, 23, 23, 23, 23, 23, 23, |
|
512 | 522 | 24, 24, 24, 24, 24, 24, 24, 24, |
|
513 | 523 | 24, 24, 24, 24, 24, 24, 24, 24 }; |
|
514 | 524 | |
|
515 | 525 | static const BYTE ML_Code[128] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, |
|
516 | 526 | 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, |
|
517 | 527 | 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, |
|
518 | 528 | 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, |
|
519 | 529 | 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, |
|
520 | 530 | 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, |
|
521 | 531 | 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, |
|
522 | 532 | 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42 }; |
|
523 | 533 | |
|
524 | 534 | |
|
525 | 535 | void ZSTD_seqToCodes(const seqStore_t* seqStorePtr) |
|
526 | 536 | { |
|
527 | 537 | BYTE const LL_deltaCode = 19; |
|
528 | 538 | BYTE const ML_deltaCode = 36; |
|
529 | 539 | const seqDef* const sequences = seqStorePtr->sequencesStart; |
|
530 | 540 | BYTE* const llCodeTable = seqStorePtr->llCode; |
|
531 | 541 | BYTE* const ofCodeTable = seqStorePtr->ofCode; |
|
532 | 542 | BYTE* const mlCodeTable = seqStorePtr->mlCode; |
|
533 | 543 | U32 const nbSeq = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart); |
|
534 | 544 | U32 u; |
|
535 | 545 | for (u=0; u<nbSeq; u++) { |
|
536 | 546 | U32 const llv = sequences[u].litLength; |
|
537 | 547 | U32 const mlv = sequences[u].matchLength; |
|
538 | 548 | llCodeTable[u] = (llv> 63) ? (BYTE)ZSTD_highbit32(llv) + LL_deltaCode : LL_Code[llv]; |
|
539 | 549 | ofCodeTable[u] = (BYTE)ZSTD_highbit32(sequences[u].offset); |
|
540 | 550 | mlCodeTable[u] = (mlv>127) ? (BYTE)ZSTD_highbit32(mlv) + ML_deltaCode : ML_Code[mlv]; |
|
541 | 551 | } |
|
542 | 552 | if (seqStorePtr->longLengthID==1) |
|
543 | 553 | llCodeTable[seqStorePtr->longLengthPos] = MaxLL; |
|
544 | 554 | if (seqStorePtr->longLengthID==2) |
|
545 | 555 | mlCodeTable[seqStorePtr->longLengthPos] = MaxML; |
|
546 | 556 | } |
|
547 | 557 | |
|
548 | 558 | |
|
549 | 559 | size_t ZSTD_compressSequences(ZSTD_CCtx* zc, |
|
550 | 560 | void* dst, size_t dstCapacity, |
|
551 | 561 | size_t srcSize) |
|
552 | 562 | { |
|
553 | 563 | const seqStore_t* seqStorePtr = &(zc->seqStore); |
|
554 | 564 | U32 count[MaxSeq+1]; |
|
555 | 565 | S16 norm[MaxSeq+1]; |
|
556 | 566 | FSE_CTable* CTable_LitLength = zc->litlengthCTable; |
|
557 | 567 | FSE_CTable* CTable_OffsetBits = zc->offcodeCTable; |
|
558 | 568 | FSE_CTable* CTable_MatchLength = zc->matchlengthCTable; |
|
559 | 569 | U32 LLtype, Offtype, MLtype; /* compressed, raw or rle */ |
|
560 | 570 | const seqDef* const sequences = seqStorePtr->sequencesStart; |
|
561 | 571 | const BYTE* const ofCodeTable = seqStorePtr->ofCode; |
|
562 | 572 | const BYTE* const llCodeTable = seqStorePtr->llCode; |
|
563 | 573 | const BYTE* const mlCodeTable = seqStorePtr->mlCode; |
|
564 | 574 | BYTE* const ostart = (BYTE*)dst; |
|
565 | 575 | BYTE* const oend = ostart + dstCapacity; |
|
566 | 576 | BYTE* op = ostart; |
|
567 | 577 | size_t const nbSeq = seqStorePtr->sequences - seqStorePtr->sequencesStart; |
|
568 | 578 | BYTE* seqHead; |
|
579 | BYTE scratchBuffer[1<<MAX(MLFSELog,LLFSELog)]; | |
|
569 | 580 | |
|
570 | 581 | /* Compress literals */ |
|
571 | 582 | { const BYTE* const literals = seqStorePtr->litStart; |
|
572 | 583 | size_t const litSize = seqStorePtr->lit - literals; |
|
573 | 584 | size_t const cSize = ZSTD_compressLiterals(zc, op, dstCapacity, literals, litSize); |
|
574 | 585 | if (ZSTD_isError(cSize)) return cSize; |
|
575 | 586 | op += cSize; |
|
576 | 587 | } |
|
577 | 588 | |
|
578 | 589 | /* Sequences Header */ |
|
579 | 590 | if ((oend-op) < 3 /*max nbSeq Size*/ + 1 /*seqHead */) return ERROR(dstSize_tooSmall); |
|
580 | 591 | if (nbSeq < 0x7F) *op++ = (BYTE)nbSeq; |
|
581 | 592 | else if (nbSeq < LONGNBSEQ) op[0] = (BYTE)((nbSeq>>8) + 0x80), op[1] = (BYTE)nbSeq, op+=2; |
|
582 | 593 | else op[0]=0xFF, MEM_writeLE16(op+1, (U16)(nbSeq - LONGNBSEQ)), op+=3; |
|
583 | 594 | if (nbSeq==0) goto _check_compressibility; |
|
584 | 595 | |
|
585 | 596 | /* seqHead : flags for FSE encoding type */ |
|
586 | 597 | seqHead = op++; |
|
587 | 598 | |
|
588 | 599 | #define MIN_SEQ_FOR_DYNAMIC_FSE 64 |
|
589 | 600 | #define MAX_SEQ_FOR_STATIC_FSE 1000 |
|
590 | 601 | |
|
591 | 602 | /* convert length/distances into codes */ |
|
592 | 603 | ZSTD_seqToCodes(seqStorePtr); |
|
593 | 604 | |
|
594 | 605 | /* CTable for Literal Lengths */ |
|
595 | 606 | { U32 max = MaxLL; |
|
596 | size_t const mostFrequent = FSE_countFast(count, &max, llCodeTable, nbSeq); | |
|
607 | size_t const mostFrequent = FSE_countFast_wksp(count, &max, llCodeTable, nbSeq, zc->tmpCounters); | |
|
597 | 608 | if ((mostFrequent == nbSeq) && (nbSeq > 2)) { |
|
598 | 609 | *op++ = llCodeTable[0]; |
|
599 | 610 | FSE_buildCTable_rle(CTable_LitLength, (BYTE)max); |
|
600 | 611 | LLtype = set_rle; |
|
601 | 612 | } else if ((zc->flagStaticTables) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { |
|
602 | 613 | LLtype = set_repeat; |
|
603 | 614 | } else if ((nbSeq < MIN_SEQ_FOR_DYNAMIC_FSE) || (mostFrequent < (nbSeq >> (LL_defaultNormLog-1)))) { |
|
604 | FSE_buildCTable(CTable_LitLength, LL_defaultNorm, MaxLL, LL_defaultNormLog); | |
|
615 | FSE_buildCTable_wksp(CTable_LitLength, LL_defaultNorm, MaxLL, LL_defaultNormLog, scratchBuffer, sizeof(scratchBuffer)); | |
|
605 | 616 | LLtype = set_basic; |
|
606 | 617 | } else { |
|
607 | 618 | size_t nbSeq_1 = nbSeq; |
|
608 | 619 | const U32 tableLog = FSE_optimalTableLog(LLFSELog, nbSeq, max); |
|
609 | 620 | if (count[llCodeTable[nbSeq-1]]>1) { count[llCodeTable[nbSeq-1]]--; nbSeq_1--; } |
|
610 | 621 | FSE_normalizeCount(norm, tableLog, count, nbSeq_1, max); |
|
611 | 622 | { size_t const NCountSize = FSE_writeNCount(op, oend-op, norm, max, tableLog); /* overflow protected */ |
|
612 | 623 | if (FSE_isError(NCountSize)) return ERROR(GENERIC); |
|
613 | 624 | op += NCountSize; } |
|
614 | FSE_buildCTable(CTable_LitLength, norm, max, tableLog); | |
|
625 | FSE_buildCTable_wksp(CTable_LitLength, norm, max, tableLog, scratchBuffer, sizeof(scratchBuffer)); | |
|
615 | 626 | LLtype = set_compressed; |
|
616 | 627 | } } |
|
617 | 628 | |
|
618 | 629 | /* CTable for Offsets */ |
|
619 | 630 | { U32 max = MaxOff; |
|
620 | size_t const mostFrequent = FSE_countFast(count, &max, ofCodeTable, nbSeq); | |
|
631 | size_t const mostFrequent = FSE_countFast_wksp(count, &max, ofCodeTable, nbSeq, zc->tmpCounters); | |
|
621 | 632 | if ((mostFrequent == nbSeq) && (nbSeq > 2)) { |
|
622 | 633 | *op++ = ofCodeTable[0]; |
|
623 | 634 | FSE_buildCTable_rle(CTable_OffsetBits, (BYTE)max); |
|
624 | 635 | Offtype = set_rle; |
|
625 | 636 | } else if ((zc->flagStaticTables) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { |
|
626 | 637 | Offtype = set_repeat; |
|
627 | 638 | } else if ((nbSeq < MIN_SEQ_FOR_DYNAMIC_FSE) || (mostFrequent < (nbSeq >> (OF_defaultNormLog-1)))) { |
|
628 | FSE_buildCTable(CTable_OffsetBits, OF_defaultNorm, MaxOff, OF_defaultNormLog); | |
|
639 | FSE_buildCTable_wksp(CTable_OffsetBits, OF_defaultNorm, MaxOff, OF_defaultNormLog, scratchBuffer, sizeof(scratchBuffer)); | |
|
629 | 640 | Offtype = set_basic; |
|
630 | 641 | } else { |
|
631 | 642 | size_t nbSeq_1 = nbSeq; |
|
632 | 643 | const U32 tableLog = FSE_optimalTableLog(OffFSELog, nbSeq, max); |
|
633 | 644 | if (count[ofCodeTable[nbSeq-1]]>1) { count[ofCodeTable[nbSeq-1]]--; nbSeq_1--; } |
|
634 | 645 | FSE_normalizeCount(norm, tableLog, count, nbSeq_1, max); |
|
635 | 646 | { size_t const NCountSize = FSE_writeNCount(op, oend-op, norm, max, tableLog); /* overflow protected */ |
|
636 | 647 | if (FSE_isError(NCountSize)) return ERROR(GENERIC); |
|
637 | 648 | op += NCountSize; } |
|
638 | FSE_buildCTable(CTable_OffsetBits, norm, max, tableLog); | |
|
649 | FSE_buildCTable_wksp(CTable_OffsetBits, norm, max, tableLog, scratchBuffer, sizeof(scratchBuffer)); | |
|
639 | 650 | Offtype = set_compressed; |
|
640 | 651 | } } |
|
641 | 652 | |
|
642 | 653 | /* CTable for MatchLengths */ |
|
643 | 654 | { U32 max = MaxML; |
|
644 | size_t const mostFrequent = FSE_countFast(count, &max, mlCodeTable, nbSeq); | |
|
655 | size_t const mostFrequent = FSE_countFast_wksp(count, &max, mlCodeTable, nbSeq, zc->tmpCounters); | |
|
645 | 656 | if ((mostFrequent == nbSeq) && (nbSeq > 2)) { |
|
646 | 657 | *op++ = *mlCodeTable; |
|
647 | 658 | FSE_buildCTable_rle(CTable_MatchLength, (BYTE)max); |
|
648 | 659 | MLtype = set_rle; |
|
649 | 660 | } else if ((zc->flagStaticTables) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { |
|
650 | 661 | MLtype = set_repeat; |
|
651 | 662 | } else if ((nbSeq < MIN_SEQ_FOR_DYNAMIC_FSE) || (mostFrequent < (nbSeq >> (ML_defaultNormLog-1)))) { |
|
652 | FSE_buildCTable(CTable_MatchLength, ML_defaultNorm, MaxML, ML_defaultNormLog); | |
|
663 | FSE_buildCTable_wksp(CTable_MatchLength, ML_defaultNorm, MaxML, ML_defaultNormLog, scratchBuffer, sizeof(scratchBuffer)); | |
|
653 | 664 | MLtype = set_basic; |
|
654 | 665 | } else { |
|
655 | 666 | size_t nbSeq_1 = nbSeq; |
|
656 | 667 | const U32 tableLog = FSE_optimalTableLog(MLFSELog, nbSeq, max); |
|
657 | 668 | if (count[mlCodeTable[nbSeq-1]]>1) { count[mlCodeTable[nbSeq-1]]--; nbSeq_1--; } |
|
658 | 669 | FSE_normalizeCount(norm, tableLog, count, nbSeq_1, max); |
|
659 | 670 | { size_t const NCountSize = FSE_writeNCount(op, oend-op, norm, max, tableLog); /* overflow protected */ |
|
660 | 671 | if (FSE_isError(NCountSize)) return ERROR(GENERIC); |
|
661 | 672 | op += NCountSize; } |
|
662 | FSE_buildCTable(CTable_MatchLength, norm, max, tableLog); | |
|
673 | FSE_buildCTable_wksp(CTable_MatchLength, norm, max, tableLog, scratchBuffer, sizeof(scratchBuffer)); | |
|
663 | 674 | MLtype = set_compressed; |
|
664 | 675 | } } |
|
665 | 676 | |
|
666 | 677 | *seqHead = (BYTE)((LLtype<<6) + (Offtype<<4) + (MLtype<<2)); |
|
667 | 678 | zc->flagStaticTables = 0; |
|
668 | 679 | |
|
669 | 680 | /* Encoding Sequences */ |
|
670 | 681 | { BIT_CStream_t blockStream; |
|
671 | 682 | FSE_CState_t stateMatchLength; |
|
672 | 683 | FSE_CState_t stateOffsetBits; |
|
673 | 684 | FSE_CState_t stateLitLength; |
|
674 | 685 | |
|
675 | 686 | CHECK_E(BIT_initCStream(&blockStream, op, oend-op), dstSize_tooSmall); /* not enough space remaining */ |
|
676 | 687 | |
|
677 | 688 | /* first symbols */ |
|
678 | 689 | FSE_initCState2(&stateMatchLength, CTable_MatchLength, mlCodeTable[nbSeq-1]); |
|
679 | 690 | FSE_initCState2(&stateOffsetBits, CTable_OffsetBits, ofCodeTable[nbSeq-1]); |
|
680 | 691 | FSE_initCState2(&stateLitLength, CTable_LitLength, llCodeTable[nbSeq-1]); |
|
681 | 692 | BIT_addBits(&blockStream, sequences[nbSeq-1].litLength, LL_bits[llCodeTable[nbSeq-1]]); |
|
682 | 693 | if (MEM_32bits()) BIT_flushBits(&blockStream); |
|
683 | 694 | BIT_addBits(&blockStream, sequences[nbSeq-1].matchLength, ML_bits[mlCodeTable[nbSeq-1]]); |
|
684 | 695 | if (MEM_32bits()) BIT_flushBits(&blockStream); |
|
685 | 696 | BIT_addBits(&blockStream, sequences[nbSeq-1].offset, ofCodeTable[nbSeq-1]); |
|
686 | 697 | BIT_flushBits(&blockStream); |
|
687 | 698 | |
|
688 | 699 | { size_t n; |
|
689 | 700 | for (n=nbSeq-2 ; n<nbSeq ; n--) { /* intentional underflow */ |
|
690 | 701 | BYTE const llCode = llCodeTable[n]; |
|
691 | 702 | BYTE const ofCode = ofCodeTable[n]; |
|
692 | 703 | BYTE const mlCode = mlCodeTable[n]; |
|
693 | 704 | U32 const llBits = LL_bits[llCode]; |
|
694 | 705 | U32 const ofBits = ofCode; /* 32b*/ /* 64b*/ |
|
695 | 706 | U32 const mlBits = ML_bits[mlCode]; |
|
696 | 707 | /* (7)*/ /* (7)*/ |
|
697 | 708 | FSE_encodeSymbol(&blockStream, &stateOffsetBits, ofCode); /* 15 */ /* 15 */ |
|
698 | 709 | FSE_encodeSymbol(&blockStream, &stateMatchLength, mlCode); /* 24 */ /* 24 */ |
|
699 | 710 | if (MEM_32bits()) BIT_flushBits(&blockStream); /* (7)*/ |
|
700 | 711 | FSE_encodeSymbol(&blockStream, &stateLitLength, llCode); /* 16 */ /* 33 */ |
|
701 | 712 | if (MEM_32bits() || (ofBits+mlBits+llBits >= 64-7-(LLFSELog+MLFSELog+OffFSELog))) |
|
702 | 713 | BIT_flushBits(&blockStream); /* (7)*/ |
|
703 | 714 | BIT_addBits(&blockStream, sequences[n].litLength, llBits); |
|
704 | 715 | if (MEM_32bits() && ((llBits+mlBits)>24)) BIT_flushBits(&blockStream); |
|
705 | 716 | BIT_addBits(&blockStream, sequences[n].matchLength, mlBits); |
|
706 | 717 | if (MEM_32bits()) BIT_flushBits(&blockStream); /* (7)*/ |
|
707 | 718 | BIT_addBits(&blockStream, sequences[n].offset, ofBits); /* 31 */ |
|
708 | 719 | BIT_flushBits(&blockStream); /* (7)*/ |
|
709 | 720 | } } |
|
710 | 721 | |
|
711 | 722 | FSE_flushCState(&blockStream, &stateMatchLength); |
|
712 | 723 | FSE_flushCState(&blockStream, &stateOffsetBits); |
|
713 | 724 | FSE_flushCState(&blockStream, &stateLitLength); |
|
714 | 725 | |
|
715 | 726 | { size_t const streamSize = BIT_closeCStream(&blockStream); |
|
716 | 727 | if (streamSize==0) return ERROR(dstSize_tooSmall); /* not enough space */ |
|
717 | 728 | op += streamSize; |
|
718 | 729 | } } |
|
719 | 730 | |
|
720 | 731 | /* check compressibility */ |
|
721 | 732 | _check_compressibility: |
|
722 | 733 | { size_t const minGain = ZSTD_minGain(srcSize); |
|
723 | 734 | size_t const maxCSize = srcSize - minGain; |
|
724 | 735 | if ((size_t)(op-ostart) >= maxCSize) return 0; } |
|
725 | 736 | |
|
726 | 737 | /* confirm repcodes */ |
|
727 | 738 | { int i; for (i=0; i<ZSTD_REP_NUM; i++) zc->rep[i] = zc->savedRep[i]; } |
|
728 | 739 | |
|
729 | 740 | return op - ostart; |
|
730 | 741 | } |
|
731 | 742 | |
|
732 | 743 | |
|
733 | 744 | /*! ZSTD_storeSeq() : |
|
734 | 745 | Store a sequence (literal length, literals, offset code and match length code) into seqStore_t. |
|
735 | 746 | `offsetCode` : distance to match, or 0 == repCode. |
|
736 | 747 | `matchCode` : matchLength - MINMATCH |
|
737 | 748 | */ |
|
738 | 749 | MEM_STATIC void ZSTD_storeSeq(seqStore_t* seqStorePtr, size_t litLength, const void* literals, U32 offsetCode, size_t matchCode) |
|
739 | 750 | { |
|
740 | 751 | #if 0 /* for debug */ |
|
741 | 752 | static const BYTE* g_start = NULL; |
|
742 | const U32 pos = (U32)(literals - g_start); | |
|
743 | if (g_start==NULL) g_start = literals; | |
|
753 | const U32 pos = (U32)((const BYTE*)literals - g_start); | |
|
754 | if (g_start==NULL) g_start = (const BYTE*)literals; | |
|
744 | 755 | //if ((pos > 1) && (pos < 50000)) |
|
745 | 756 | printf("Cpos %6u :%5u literals & match %3u bytes at distance %6u \n", |
|
746 | 757 | pos, (U32)litLength, (U32)matchCode+MINMATCH, (U32)offsetCode); |
|
747 | 758 | #endif |
|
748 | 759 | /* copy Literals */ |
|
749 | 760 | ZSTD_wildcopy(seqStorePtr->lit, literals, litLength); |
|
750 | 761 | seqStorePtr->lit += litLength; |
|
751 | 762 | |
|
752 | 763 | /* literal Length */ |
|
753 | 764 | if (litLength>0xFFFF) { seqStorePtr->longLengthID = 1; seqStorePtr->longLengthPos = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart); } |
|
754 | 765 | seqStorePtr->sequences[0].litLength = (U16)litLength; |
|
755 | 766 | |
|
756 | 767 | /* match offset */ |
|
757 | 768 | seqStorePtr->sequences[0].offset = offsetCode + 1; |
|
758 | 769 | |
|
759 | 770 | /* match Length */ |
|
760 | 771 | if (matchCode>0xFFFF) { seqStorePtr->longLengthID = 2; seqStorePtr->longLengthPos = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart); } |
|
761 | 772 | seqStorePtr->sequences[0].matchLength = (U16)matchCode; |
|
762 | 773 | |
|
763 | 774 | seqStorePtr->sequences++; |
|
764 | 775 | } |
|
765 | 776 | |
|
766 | 777 | |
|
767 | 778 | /*-************************************* |
|
768 | 779 | * Match length counter |
|
769 | 780 | ***************************************/ |
|
770 | 781 | static unsigned ZSTD_NbCommonBytes (register size_t val) |
|
771 | 782 | { |
|
772 | 783 | if (MEM_isLittleEndian()) { |
|
773 | 784 | if (MEM_64bits()) { |
|
774 | 785 | # if defined(_MSC_VER) && defined(_WIN64) |
|
775 | 786 | unsigned long r = 0; |
|
776 | 787 | _BitScanForward64( &r, (U64)val ); |
|
777 | 788 | return (unsigned)(r>>3); |
|
778 | 789 | # elif defined(__GNUC__) && (__GNUC__ >= 3) |
|
779 | 790 | return (__builtin_ctzll((U64)val) >> 3); |
|
780 | 791 | # else |
|
781 | 792 | static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 }; |
|
782 | 793 | return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58]; |
|
783 | 794 | # endif |
|
784 | 795 | } else { /* 32 bits */ |
|
785 | 796 | # if defined(_MSC_VER) |
|
786 | 797 | unsigned long r=0; |
|
787 | 798 | _BitScanForward( &r, (U32)val ); |
|
788 | 799 | return (unsigned)(r>>3); |
|
789 | 800 | # elif defined(__GNUC__) && (__GNUC__ >= 3) |
|
790 | 801 | return (__builtin_ctz((U32)val) >> 3); |
|
791 | 802 | # else |
|
792 | 803 | static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 }; |
|
793 | 804 | return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27]; |
|
794 | 805 | # endif |
|
795 | 806 | } |
|
796 | 807 | } else { /* Big Endian CPU */ |
|
797 | 808 | if (MEM_64bits()) { |
|
798 | 809 | # if defined(_MSC_VER) && defined(_WIN64) |
|
799 | 810 | unsigned long r = 0; |
|
800 | 811 | _BitScanReverse64( &r, val ); |
|
801 | 812 | return (unsigned)(r>>3); |
|
802 | 813 | # elif defined(__GNUC__) && (__GNUC__ >= 3) |
|
803 | 814 | return (__builtin_clzll(val) >> 3); |
|
804 | 815 | # else |
|
805 | 816 | unsigned r; |
|
806 | 817 | const unsigned n32 = sizeof(size_t)*4; /* calculate this way due to compiler complaining in 32-bits mode */ |
|
807 | 818 | if (!(val>>n32)) { r=4; } else { r=0; val>>=n32; } |
|
808 | 819 | if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; } |
|
809 | 820 | r += (!val); |
|
810 | 821 | return r; |
|
811 | 822 | # endif |
|
812 | 823 | } else { /* 32 bits */ |
|
813 | 824 | # if defined(_MSC_VER) |
|
814 | 825 | unsigned long r = 0; |
|
815 | 826 | _BitScanReverse( &r, (unsigned long)val ); |
|
816 | 827 | return (unsigned)(r>>3); |
|
817 | 828 | # elif defined(__GNUC__) && (__GNUC__ >= 3) |
|
818 | 829 | return (__builtin_clz((U32)val) >> 3); |
|
819 | 830 | # else |
|
820 | 831 | unsigned r; |
|
821 | 832 | if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; } |
|
822 | 833 | r += (!val); |
|
823 | 834 | return r; |
|
824 | 835 | # endif |
|
825 | 836 | } } |
|
826 | 837 | } |
|
827 | 838 | |
|
828 | 839 | |
|
829 | 840 | static size_t ZSTD_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* const pInLimit) |
|
830 | 841 | { |
|
831 | 842 | const BYTE* const pStart = pIn; |
|
832 | 843 | const BYTE* const pInLoopLimit = pInLimit - (sizeof(size_t)-1); |
|
833 | 844 | |
|
834 | 845 | while (pIn < pInLoopLimit) { |
|
835 | 846 | size_t const diff = MEM_readST(pMatch) ^ MEM_readST(pIn); |
|
836 | 847 | if (!diff) { pIn+=sizeof(size_t); pMatch+=sizeof(size_t); continue; } |
|
837 | 848 | pIn += ZSTD_NbCommonBytes(diff); |
|
838 | 849 | return (size_t)(pIn - pStart); |
|
839 | 850 | } |
|
840 | 851 | if (MEM_64bits()) if ((pIn<(pInLimit-3)) && (MEM_read32(pMatch) == MEM_read32(pIn))) { pIn+=4; pMatch+=4; } |
|
841 | 852 | if ((pIn<(pInLimit-1)) && (MEM_read16(pMatch) == MEM_read16(pIn))) { pIn+=2; pMatch+=2; } |
|
842 | 853 | if ((pIn<pInLimit) && (*pMatch == *pIn)) pIn++; |
|
843 | 854 | return (size_t)(pIn - pStart); |
|
844 | 855 | } |
|
845 | 856 | |
|
846 | 857 | /** ZSTD_count_2segments() : |
|
847 | 858 | * can count match length with `ip` & `match` in 2 different segments. |
|
848 | 859 | * convention : on reaching mEnd, match count continue starting from iStart |
|
849 | 860 | */ |
|
850 | 861 | static size_t ZSTD_count_2segments(const BYTE* ip, const BYTE* match, const BYTE* iEnd, const BYTE* mEnd, const BYTE* iStart) |
|
851 | 862 | { |
|
852 | 863 | const BYTE* const vEnd = MIN( ip + (mEnd - match), iEnd); |
|
853 | 864 | size_t const matchLength = ZSTD_count(ip, match, vEnd); |
|
854 | 865 | if (match + matchLength != mEnd) return matchLength; |
|
855 | 866 | return matchLength + ZSTD_count(ip+matchLength, iStart, iEnd); |
|
856 | 867 | } |
|
857 | 868 | |
|
858 | 869 | |
|
859 | 870 | /*-************************************* |
|
860 | 871 | * Hashes |
|
861 | 872 | ***************************************/ |
|
862 | 873 | static const U32 prime3bytes = 506832829U; |
|
863 | 874 | static U32 ZSTD_hash3(U32 u, U32 h) { return ((u << (32-24)) * prime3bytes) >> (32-h) ; } |
|
864 | 875 | MEM_STATIC size_t ZSTD_hash3Ptr(const void* ptr, U32 h) { return ZSTD_hash3(MEM_readLE32(ptr), h); } /* only in zstd_opt.h */ |
|
865 | 876 | |
|
866 | 877 | static const U32 prime4bytes = 2654435761U; |
|
867 | 878 | static U32 ZSTD_hash4(U32 u, U32 h) { return (u * prime4bytes) >> (32-h) ; } |
|
868 | 879 | static size_t ZSTD_hash4Ptr(const void* ptr, U32 h) { return ZSTD_hash4(MEM_read32(ptr), h); } |
|
869 | 880 | |
|
870 | 881 | static const U64 prime5bytes = 889523592379ULL; |
|
871 | 882 | static size_t ZSTD_hash5(U64 u, U32 h) { return (size_t)(((u << (64-40)) * prime5bytes) >> (64-h)) ; } |
|
872 | 883 | static size_t ZSTD_hash5Ptr(const void* p, U32 h) { return ZSTD_hash5(MEM_readLE64(p), h); } |
|
873 | 884 | |
|
874 | 885 | static const U64 prime6bytes = 227718039650203ULL; |
|
875 | 886 | static size_t ZSTD_hash6(U64 u, U32 h) { return (size_t)(((u << (64-48)) * prime6bytes) >> (64-h)) ; } |
|
876 | 887 | static size_t ZSTD_hash6Ptr(const void* p, U32 h) { return ZSTD_hash6(MEM_readLE64(p), h); } |
|
877 | 888 | |
|
878 | 889 | static const U64 prime7bytes = 58295818150454627ULL; |
|
879 | 890 | static size_t ZSTD_hash7(U64 u, U32 h) { return (size_t)(((u << (64-56)) * prime7bytes) >> (64-h)) ; } |
|
880 | 891 | static size_t ZSTD_hash7Ptr(const void* p, U32 h) { return ZSTD_hash7(MEM_readLE64(p), h); } |
|
881 | 892 | |
|
882 | 893 | static const U64 prime8bytes = 0xCF1BBCDCB7A56463ULL; |
|
883 | 894 | static size_t ZSTD_hash8(U64 u, U32 h) { return (size_t)(((u) * prime8bytes) >> (64-h)) ; } |
|
884 | 895 | static size_t ZSTD_hash8Ptr(const void* p, U32 h) { return ZSTD_hash8(MEM_readLE64(p), h); } |
|
885 | 896 | |
|
886 | 897 | static size_t ZSTD_hashPtr(const void* p, U32 hBits, U32 mls) |
|
887 | 898 | { |
|
888 | 899 | switch(mls) |
|
889 | 900 | { |
|
890 | 901 | default: |
|
891 | 902 | case 4: return ZSTD_hash4Ptr(p, hBits); |
|
892 | 903 | case 5: return ZSTD_hash5Ptr(p, hBits); |
|
893 | 904 | case 6: return ZSTD_hash6Ptr(p, hBits); |
|
894 | 905 | case 7: return ZSTD_hash7Ptr(p, hBits); |
|
895 | 906 | case 8: return ZSTD_hash8Ptr(p, hBits); |
|
896 | 907 | } |
|
897 | 908 | } |
|
898 | 909 | |
|
899 | 910 | |
|
900 | 911 | /*-************************************* |
|
901 | 912 | * Fast Scan |
|
902 | 913 | ***************************************/ |
|
903 | 914 | static void ZSTD_fillHashTable (ZSTD_CCtx* zc, const void* end, const U32 mls) |
|
904 | 915 | { |
|
905 | 916 | U32* const hashTable = zc->hashTable; |
|
906 | 917 | U32 const hBits = zc->params.cParams.hashLog; |
|
907 | 918 | const BYTE* const base = zc->base; |
|
908 | 919 | const BYTE* ip = base + zc->nextToUpdate; |
|
909 | 920 | const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE; |
|
910 | 921 | const size_t fastHashFillStep = 3; |
|
911 | 922 | |
|
912 | 923 | while(ip <= iend) { |
|
913 | 924 | hashTable[ZSTD_hashPtr(ip, hBits, mls)] = (U32)(ip - base); |
|
914 | 925 | ip += fastHashFillStep; |
|
915 | 926 | } |
|
916 | 927 | } |
|
917 | 928 | |
|
918 | 929 | |
|
919 | 930 | FORCE_INLINE |
|
920 | 931 | void ZSTD_compressBlock_fast_generic(ZSTD_CCtx* cctx, |
|
921 | 932 | const void* src, size_t srcSize, |
|
922 | 933 | const U32 mls) |
|
923 | 934 | { |
|
924 | 935 | U32* const hashTable = cctx->hashTable; |
|
925 | 936 | U32 const hBits = cctx->params.cParams.hashLog; |
|
926 | 937 | seqStore_t* seqStorePtr = &(cctx->seqStore); |
|
927 | 938 | const BYTE* const base = cctx->base; |
|
928 | 939 | const BYTE* const istart = (const BYTE*)src; |
|
929 | 940 | const BYTE* ip = istart; |
|
930 | 941 | const BYTE* anchor = istart; |
|
931 | 942 | const U32 lowestIndex = cctx->dictLimit; |
|
932 | 943 | const BYTE* const lowest = base + lowestIndex; |
|
933 | 944 | const BYTE* const iend = istart + srcSize; |
|
934 | 945 | const BYTE* const ilimit = iend - HASH_READ_SIZE; |
|
935 | 946 | U32 offset_1=cctx->rep[0], offset_2=cctx->rep[1]; |
|
936 | 947 | U32 offsetSaved = 0; |
|
937 | 948 | |
|
938 | 949 | /* init */ |
|
939 | 950 | ip += (ip==lowest); |
|
940 | 951 | { U32 const maxRep = (U32)(ip-lowest); |
|
941 | 952 | if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; |
|
942 | 953 | if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; |
|
943 | 954 | } |
|
944 | 955 | |
|
945 | 956 | /* Main Search Loop */ |
|
946 | 957 | while (ip < ilimit) { /* < instead of <=, because repcode check at (ip+1) */ |
|
947 | 958 | size_t mLength; |
|
948 | 959 | size_t const h = ZSTD_hashPtr(ip, hBits, mls); |
|
949 | 960 | U32 const current = (U32)(ip-base); |
|
950 | 961 | U32 const matchIndex = hashTable[h]; |
|
951 | 962 | const BYTE* match = base + matchIndex; |
|
952 | 963 | hashTable[h] = current; /* update hash table */ |
|
953 | 964 | |
|
954 | 965 | if ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1))) { |
|
955 | 966 | mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; |
|
956 | 967 | ip++; |
|
957 | 968 | ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, 0, mLength-MINMATCH); |
|
958 | 969 | } else { |
|
959 | 970 | U32 offset; |
|
960 | 971 | if ( (matchIndex <= lowestIndex) || (MEM_read32(match) != MEM_read32(ip)) ) { |
|
961 | 972 | ip += ((ip-anchor) >> g_searchStrength) + 1; |
|
962 | 973 | continue; |
|
963 | 974 | } |
|
964 | 975 | mLength = ZSTD_count(ip+4, match+4, iend) + 4; |
|
965 | 976 | offset = (U32)(ip-match); |
|
966 | 977 | while (((ip>anchor) & (match>lowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ |
|
967 | 978 | offset_2 = offset_1; |
|
968 | 979 | offset_1 = offset; |
|
969 | 980 | |
|
970 | 981 | ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); |
|
971 | 982 | } |
|
972 | 983 | |
|
973 | 984 | /* match found */ |
|
974 | 985 | ip += mLength; |
|
975 | 986 | anchor = ip; |
|
976 | 987 | |
|
977 | 988 | if (ip <= ilimit) { |
|
978 | 989 | /* Fill Table */ |
|
979 | 990 | hashTable[ZSTD_hashPtr(base+current+2, hBits, mls)] = current+2; /* here because current+2 could be > iend-8 */ |
|
980 | 991 | hashTable[ZSTD_hashPtr(ip-2, hBits, mls)] = (U32)(ip-2-base); |
|
981 | 992 | /* check immediate repcode */ |
|
982 | 993 | while ( (ip <= ilimit) |
|
983 | 994 | && ( (offset_2>0) |
|
984 | 995 | & (MEM_read32(ip) == MEM_read32(ip - offset_2)) )) { |
|
985 | 996 | /* store sequence */ |
|
986 | 997 | size_t const rLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4; |
|
987 | 998 | { U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff; } /* swap offset_2 <=> offset_1 */ |
|
988 | 999 | hashTable[ZSTD_hashPtr(ip, hBits, mls)] = (U32)(ip-base); |
|
989 | 1000 | ZSTD_storeSeq(seqStorePtr, 0, anchor, 0, rLength-MINMATCH); |
|
990 | 1001 | ip += rLength; |
|
991 | 1002 | anchor = ip; |
|
992 | 1003 | continue; /* faster when present ... (?) */ |
|
993 | 1004 | } } } |
|
994 | 1005 | |
|
995 | 1006 | /* save reps for next block */ |
|
996 | 1007 | cctx->savedRep[0] = offset_1 ? offset_1 : offsetSaved; |
|
997 | 1008 | cctx->savedRep[1] = offset_2 ? offset_2 : offsetSaved; |
|
998 | 1009 | |
|
999 | 1010 | /* Last Literals */ |
|
1000 | 1011 | { size_t const lastLLSize = iend - anchor; |
|
1001 | 1012 | memcpy(seqStorePtr->lit, anchor, lastLLSize); |
|
1002 | 1013 | seqStorePtr->lit += lastLLSize; |
|
1003 | 1014 | } |
|
1004 | 1015 | } |
|
1005 | 1016 | |
|
1006 | 1017 | |
|
1007 | 1018 | static void ZSTD_compressBlock_fast(ZSTD_CCtx* ctx, |
|
1008 | 1019 | const void* src, size_t srcSize) |
|
1009 | 1020 | { |
|
1010 | 1021 | const U32 mls = ctx->params.cParams.searchLength; |
|
1011 | 1022 | switch(mls) |
|
1012 | 1023 | { |
|
1013 | 1024 | default: |
|
1014 | 1025 | case 4 : |
|
1015 | 1026 | ZSTD_compressBlock_fast_generic(ctx, src, srcSize, 4); return; |
|
1016 | 1027 | case 5 : |
|
1017 | 1028 | ZSTD_compressBlock_fast_generic(ctx, src, srcSize, 5); return; |
|
1018 | 1029 | case 6 : |
|
1019 | 1030 | ZSTD_compressBlock_fast_generic(ctx, src, srcSize, 6); return; |
|
1020 | 1031 | case 7 : |
|
1021 | 1032 | ZSTD_compressBlock_fast_generic(ctx, src, srcSize, 7); return; |
|
1022 | 1033 | } |
|
1023 | 1034 | } |
|
1024 | 1035 | |
|
1025 | 1036 | |
|
1026 | 1037 | static void ZSTD_compressBlock_fast_extDict_generic(ZSTD_CCtx* ctx, |
|
1027 | 1038 | const void* src, size_t srcSize, |
|
1028 | 1039 | const U32 mls) |
|
1029 | 1040 | { |
|
1030 | 1041 | U32* hashTable = ctx->hashTable; |
|
1031 | 1042 | const U32 hBits = ctx->params.cParams.hashLog; |
|
1032 | 1043 | seqStore_t* seqStorePtr = &(ctx->seqStore); |
|
1033 | 1044 | const BYTE* const base = ctx->base; |
|
1034 | 1045 | const BYTE* const dictBase = ctx->dictBase; |
|
1035 | 1046 | const BYTE* const istart = (const BYTE*)src; |
|
1036 | 1047 | const BYTE* ip = istart; |
|
1037 | 1048 | const BYTE* anchor = istart; |
|
1038 | 1049 | const U32 lowestIndex = ctx->lowLimit; |
|
1039 | 1050 | const BYTE* const dictStart = dictBase + lowestIndex; |
|
1040 | 1051 | const U32 dictLimit = ctx->dictLimit; |
|
1041 | 1052 | const BYTE* const lowPrefixPtr = base + dictLimit; |
|
1042 | 1053 | const BYTE* const dictEnd = dictBase + dictLimit; |
|
1043 | 1054 | const BYTE* const iend = istart + srcSize; |
|
1044 | 1055 | const BYTE* const ilimit = iend - 8; |
|
1045 | 1056 | U32 offset_1=ctx->rep[0], offset_2=ctx->rep[1]; |
|
1046 | 1057 | |
|
1047 | 1058 | /* Search Loop */ |
|
1048 | 1059 | while (ip < ilimit) { /* < instead of <=, because (ip+1) */ |
|
1049 | 1060 | const size_t h = ZSTD_hashPtr(ip, hBits, mls); |
|
1050 | 1061 | const U32 matchIndex = hashTable[h]; |
|
1051 | 1062 | const BYTE* matchBase = matchIndex < dictLimit ? dictBase : base; |
|
1052 | 1063 | const BYTE* match = matchBase + matchIndex; |
|
1053 | 1064 | const U32 current = (U32)(ip-base); |
|
1054 | 1065 | const U32 repIndex = current + 1 - offset_1; /* offset_1 expected <= current +1 */ |
|
1055 | 1066 | const BYTE* repBase = repIndex < dictLimit ? dictBase : base; |
|
1056 | 1067 | const BYTE* repMatch = repBase + repIndex; |
|
1057 | 1068 | size_t mLength; |
|
1058 | 1069 | hashTable[h] = current; /* update hash table */ |
|
1059 | 1070 | |
|
1060 | 1071 | if ( (((U32)((dictLimit-1) - repIndex) >= 3) /* intentional underflow */ & (repIndex > lowestIndex)) |
|
1061 | 1072 | && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { |
|
1062 | 1073 | const BYTE* repMatchEnd = repIndex < dictLimit ? dictEnd : iend; |
|
1063 | 1074 | mLength = ZSTD_count_2segments(ip+1+EQUAL_READ32, repMatch+EQUAL_READ32, iend, repMatchEnd, lowPrefixPtr) + EQUAL_READ32; |
|
1064 | 1075 | ip++; |
|
1065 | 1076 | ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, 0, mLength-MINMATCH); |
|
1066 | 1077 | } else { |
|
1067 | 1078 | if ( (matchIndex < lowestIndex) || |
|
1068 | 1079 | (MEM_read32(match) != MEM_read32(ip)) ) { |
|
1069 | 1080 | ip += ((ip-anchor) >> g_searchStrength) + 1; |
|
1070 | 1081 | continue; |
|
1071 | 1082 | } |
|
1072 | 1083 | { const BYTE* matchEnd = matchIndex < dictLimit ? dictEnd : iend; |
|
1073 | 1084 | const BYTE* lowMatchPtr = matchIndex < dictLimit ? dictStart : lowPrefixPtr; |
|
1074 | 1085 | U32 offset; |
|
1075 | 1086 | mLength = ZSTD_count_2segments(ip+EQUAL_READ32, match+EQUAL_READ32, iend, matchEnd, lowPrefixPtr) + EQUAL_READ32; |
|
1076 | 1087 | while (((ip>anchor) & (match>lowMatchPtr)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ |
|
1077 | 1088 | offset = current - matchIndex; |
|
1078 | 1089 | offset_2 = offset_1; |
|
1079 | 1090 | offset_1 = offset; |
|
1080 | 1091 | ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); |
|
1081 | 1092 | } } |
|
1082 | 1093 | |
|
1083 | 1094 | /* found a match : store it */ |
|
1084 | 1095 | ip += mLength; |
|
1085 | 1096 | anchor = ip; |
|
1086 | 1097 | |
|
1087 | 1098 | if (ip <= ilimit) { |
|
1088 | 1099 | /* Fill Table */ |
|
1089 | 1100 | hashTable[ZSTD_hashPtr(base+current+2, hBits, mls)] = current+2; |
|
1090 | 1101 | hashTable[ZSTD_hashPtr(ip-2, hBits, mls)] = (U32)(ip-2-base); |
|
1091 | 1102 | /* check immediate repcode */ |
|
1092 | 1103 | while (ip <= ilimit) { |
|
1093 | 1104 | U32 const current2 = (U32)(ip-base); |
|
1094 | 1105 | U32 const repIndex2 = current2 - offset_2; |
|
1095 | 1106 | const BYTE* repMatch2 = repIndex2 < dictLimit ? dictBase + repIndex2 : base + repIndex2; |
|
1096 | 1107 | if ( (((U32)((dictLimit-1) - repIndex2) >= 3) & (repIndex2 > lowestIndex)) /* intentional overflow */ |
|
1097 | 1108 | && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { |
|
1098 | 1109 | const BYTE* const repEnd2 = repIndex2 < dictLimit ? dictEnd : iend; |
|
1099 | 1110 | size_t repLength2 = ZSTD_count_2segments(ip+EQUAL_READ32, repMatch2+EQUAL_READ32, iend, repEnd2, lowPrefixPtr) + EQUAL_READ32; |
|
1100 | 1111 | U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ |
|
1101 | 1112 | ZSTD_storeSeq(seqStorePtr, 0, anchor, 0, repLength2-MINMATCH); |
|
1102 | 1113 | hashTable[ZSTD_hashPtr(ip, hBits, mls)] = current2; |
|
1103 | 1114 | ip += repLength2; |
|
1104 | 1115 | anchor = ip; |
|
1105 | 1116 | continue; |
|
1106 | 1117 | } |
|
1107 | 1118 | break; |
|
1108 | 1119 | } } } |
|
1109 | 1120 | |
|
1110 | 1121 | /* save reps for next block */ |
|
1111 | 1122 | ctx->savedRep[0] = offset_1; ctx->savedRep[1] = offset_2; |
|
1112 | 1123 | |
|
1113 | 1124 | /* Last Literals */ |
|
1114 | 1125 | { size_t const lastLLSize = iend - anchor; |
|
1115 | 1126 | memcpy(seqStorePtr->lit, anchor, lastLLSize); |
|
1116 | 1127 | seqStorePtr->lit += lastLLSize; |
|
1117 | 1128 | } |
|
1118 | 1129 | } |
|
1119 | 1130 | |
|
1120 | 1131 | |
|
1121 | 1132 | static void ZSTD_compressBlock_fast_extDict(ZSTD_CCtx* ctx, |
|
1122 | 1133 | const void* src, size_t srcSize) |
|
1123 | 1134 | { |
|
1124 | 1135 | U32 const mls = ctx->params.cParams.searchLength; |
|
1125 | 1136 | switch(mls) |
|
1126 | 1137 | { |
|
1127 | 1138 | default: |
|
1128 | 1139 | case 4 : |
|
1129 | 1140 | ZSTD_compressBlock_fast_extDict_generic(ctx, src, srcSize, 4); return; |
|
1130 | 1141 | case 5 : |
|
1131 | 1142 | ZSTD_compressBlock_fast_extDict_generic(ctx, src, srcSize, 5); return; |
|
1132 | 1143 | case 6 : |
|
1133 | 1144 | ZSTD_compressBlock_fast_extDict_generic(ctx, src, srcSize, 6); return; |
|
1134 | 1145 | case 7 : |
|
1135 | 1146 | ZSTD_compressBlock_fast_extDict_generic(ctx, src, srcSize, 7); return; |
|
1136 | 1147 | } |
|
1137 | 1148 | } |
|
1138 | 1149 | |
|
1139 | 1150 | |
|
1140 | 1151 | /*-************************************* |
|
1141 | 1152 | * Double Fast |
|
1142 | 1153 | ***************************************/ |
|
1143 | 1154 | static void ZSTD_fillDoubleHashTable (ZSTD_CCtx* cctx, const void* end, const U32 mls) |
|
1144 | 1155 | { |
|
1145 | 1156 | U32* const hashLarge = cctx->hashTable; |
|
1146 | 1157 | U32 const hBitsL = cctx->params.cParams.hashLog; |
|
1147 | 1158 | U32* const hashSmall = cctx->chainTable; |
|
1148 | 1159 | U32 const hBitsS = cctx->params.cParams.chainLog; |
|
1149 | 1160 | const BYTE* const base = cctx->base; |
|
1150 | 1161 | const BYTE* ip = base + cctx->nextToUpdate; |
|
1151 | 1162 | const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE; |
|
1152 | 1163 | const size_t fastHashFillStep = 3; |
|
1153 | 1164 | |
|
1154 | 1165 | while(ip <= iend) { |
|
1155 | 1166 | hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = (U32)(ip - base); |
|
1156 | 1167 | hashLarge[ZSTD_hashPtr(ip, hBitsL, 8)] = (U32)(ip - base); |
|
1157 | 1168 | ip += fastHashFillStep; |
|
1158 | 1169 | } |
|
1159 | 1170 | } |
|
1160 | 1171 | |
|
1161 | 1172 | |
|
1162 | 1173 | FORCE_INLINE |
|
1163 | 1174 | void ZSTD_compressBlock_doubleFast_generic(ZSTD_CCtx* cctx, |
|
1164 | 1175 | const void* src, size_t srcSize, |
|
1165 | 1176 | const U32 mls) |
|
1166 | 1177 | { |
|
1167 | 1178 | U32* const hashLong = cctx->hashTable; |
|
1168 | 1179 | const U32 hBitsL = cctx->params.cParams.hashLog; |
|
1169 | 1180 | U32* const hashSmall = cctx->chainTable; |
|
1170 | 1181 | const U32 hBitsS = cctx->params.cParams.chainLog; |
|
1171 | 1182 | seqStore_t* seqStorePtr = &(cctx->seqStore); |
|
1172 | 1183 | const BYTE* const base = cctx->base; |
|
1173 | 1184 | const BYTE* const istart = (const BYTE*)src; |
|
1174 | 1185 | const BYTE* ip = istart; |
|
1175 | 1186 | const BYTE* anchor = istart; |
|
1176 | 1187 | const U32 lowestIndex = cctx->dictLimit; |
|
1177 | 1188 | const BYTE* const lowest = base + lowestIndex; |
|
1178 | 1189 | const BYTE* const iend = istart + srcSize; |
|
1179 | 1190 | const BYTE* const ilimit = iend - HASH_READ_SIZE; |
|
1180 | 1191 | U32 offset_1=cctx->rep[0], offset_2=cctx->rep[1]; |
|
1181 | 1192 | U32 offsetSaved = 0; |
|
1182 | 1193 | |
|
1183 | 1194 | /* init */ |
|
1184 | 1195 | ip += (ip==lowest); |
|
1185 | 1196 | { U32 const maxRep = (U32)(ip-lowest); |
|
1186 | 1197 | if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; |
|
1187 | 1198 | if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; |
|
1188 | 1199 | } |
|
1189 | 1200 | |
|
1190 | 1201 | /* Main Search Loop */ |
|
1191 | 1202 | while (ip < ilimit) { /* < instead of <=, because repcode check at (ip+1) */ |
|
1192 | 1203 | size_t mLength; |
|
1193 | 1204 | size_t const h2 = ZSTD_hashPtr(ip, hBitsL, 8); |
|
1194 | 1205 | size_t const h = ZSTD_hashPtr(ip, hBitsS, mls); |
|
1195 | 1206 | U32 const current = (U32)(ip-base); |
|
1196 | 1207 | U32 const matchIndexL = hashLong[h2]; |
|
1197 | 1208 | U32 const matchIndexS = hashSmall[h]; |
|
1198 | 1209 | const BYTE* matchLong = base + matchIndexL; |
|
1199 | 1210 | const BYTE* match = base + matchIndexS; |
|
1200 | 1211 | hashLong[h2] = hashSmall[h] = current; /* update hash tables */ |
|
1201 | 1212 | |
|
1202 | 1213 | if ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1))) { /* note : by construction, offset_1 <= current */ |
|
1203 | 1214 | mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; |
|
1204 | 1215 | ip++; |
|
1205 | 1216 | ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, 0, mLength-MINMATCH); |
|
1206 | 1217 | } else { |
|
1207 | 1218 | U32 offset; |
|
1208 | 1219 | if ( (matchIndexL > lowestIndex) && (MEM_read64(matchLong) == MEM_read64(ip)) ) { |
|
1209 | 1220 | mLength = ZSTD_count(ip+8, matchLong+8, iend) + 8; |
|
1210 | 1221 | offset = (U32)(ip-matchLong); |
|
1211 | 1222 | while (((ip>anchor) & (matchLong>lowest)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */ |
|
1212 | 1223 | } else if ( (matchIndexS > lowestIndex) && (MEM_read32(match) == MEM_read32(ip)) ) { |
|
1213 | 1224 | size_t const h3 = ZSTD_hashPtr(ip+1, hBitsL, 8); |
|
1214 | 1225 | U32 const matchIndex3 = hashLong[h3]; |
|
1215 | 1226 | const BYTE* match3 = base + matchIndex3; |
|
1216 | 1227 | hashLong[h3] = current + 1; |
|
1217 | 1228 | if ( (matchIndex3 > lowestIndex) && (MEM_read64(match3) == MEM_read64(ip+1)) ) { |
|
1218 | 1229 | mLength = ZSTD_count(ip+9, match3+8, iend) + 8; |
|
1219 | 1230 | ip++; |
|
1220 | 1231 | offset = (U32)(ip-match3); |
|
1221 | 1232 | while (((ip>anchor) & (match3>lowest)) && (ip[-1] == match3[-1])) { ip--; match3--; mLength++; } /* catch up */ |
|
1222 | 1233 | } else { |
|
1223 | 1234 | mLength = ZSTD_count(ip+4, match+4, iend) + 4; |
|
1224 | 1235 | offset = (U32)(ip-match); |
|
1225 | 1236 | while (((ip>anchor) & (match>lowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ |
|
1226 | 1237 | } |
|
1227 | 1238 | } else { |
|
1228 | 1239 | ip += ((ip-anchor) >> g_searchStrength) + 1; |
|
1229 | 1240 | continue; |
|
1230 | 1241 | } |
|
1231 | 1242 | |
|
1232 | 1243 | offset_2 = offset_1; |
|
1233 | 1244 | offset_1 = offset; |
|
1234 | 1245 | |
|
1235 | 1246 | ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); |
|
1236 | 1247 | } |
|
1237 | 1248 | |
|
1238 | 1249 | /* match found */ |
|
1239 | 1250 | ip += mLength; |
|
1240 | 1251 | anchor = ip; |
|
1241 | 1252 | |
|
1242 | 1253 | if (ip <= ilimit) { |
|
1243 | 1254 | /* Fill Table */ |
|
1244 | 1255 | hashLong[ZSTD_hashPtr(base+current+2, hBitsL, 8)] = |
|
1245 | 1256 | hashSmall[ZSTD_hashPtr(base+current+2, hBitsS, mls)] = current+2; /* here because current+2 could be > iend-8 */ |
|
1246 | 1257 | hashLong[ZSTD_hashPtr(ip-2, hBitsL, 8)] = |
|
1247 | 1258 | hashSmall[ZSTD_hashPtr(ip-2, hBitsS, mls)] = (U32)(ip-2-base); |
|
1248 | 1259 | |
|
1249 | 1260 | /* check immediate repcode */ |
|
1250 | 1261 | while ( (ip <= ilimit) |
|
1251 | 1262 | && ( (offset_2>0) |
|
1252 | 1263 | & (MEM_read32(ip) == MEM_read32(ip - offset_2)) )) { |
|
1253 | 1264 | /* store sequence */ |
|
1254 | 1265 | size_t const rLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4; |
|
1255 | 1266 | { U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff; } /* swap offset_2 <=> offset_1 */ |
|
1256 | 1267 | hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = (U32)(ip-base); |
|
1257 | 1268 | hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = (U32)(ip-base); |
|
1258 | 1269 | ZSTD_storeSeq(seqStorePtr, 0, anchor, 0, rLength-MINMATCH); |
|
1259 | 1270 | ip += rLength; |
|
1260 | 1271 | anchor = ip; |
|
1261 | 1272 | continue; /* faster when present ... (?) */ |
|
1262 | 1273 | } } } |
|
1263 | 1274 | |
|
1264 | 1275 | /* save reps for next block */ |
|
1265 | 1276 | cctx->savedRep[0] = offset_1 ? offset_1 : offsetSaved; |
|
1266 | 1277 | cctx->savedRep[1] = offset_2 ? offset_2 : offsetSaved; |
|
1267 | 1278 | |
|
1268 | 1279 | /* Last Literals */ |
|
1269 | 1280 | { size_t const lastLLSize = iend - anchor; |
|
1270 | 1281 | memcpy(seqStorePtr->lit, anchor, lastLLSize); |
|
1271 | 1282 | seqStorePtr->lit += lastLLSize; |
|
1272 | 1283 | } |
|
1273 | 1284 | } |
|
1274 | 1285 | |
|
1275 | 1286 | |
|
1276 | 1287 | static void ZSTD_compressBlock_doubleFast(ZSTD_CCtx* ctx, const void* src, size_t srcSize) |
|
1277 | 1288 | { |
|
1278 | 1289 | const U32 mls = ctx->params.cParams.searchLength; |
|
1279 | 1290 | switch(mls) |
|
1280 | 1291 | { |
|
1281 | 1292 | default: |
|
1282 | 1293 | case 4 : |
|
1283 | 1294 | ZSTD_compressBlock_doubleFast_generic(ctx, src, srcSize, 4); return; |
|
1284 | 1295 | case 5 : |
|
1285 | 1296 | ZSTD_compressBlock_doubleFast_generic(ctx, src, srcSize, 5); return; |
|
1286 | 1297 | case 6 : |
|
1287 | 1298 | ZSTD_compressBlock_doubleFast_generic(ctx, src, srcSize, 6); return; |
|
1288 | 1299 | case 7 : |
|
1289 | 1300 | ZSTD_compressBlock_doubleFast_generic(ctx, src, srcSize, 7); return; |
|
1290 | 1301 | } |
|
1291 | 1302 | } |
|
1292 | 1303 | |
|
1293 | 1304 | |
|
1294 | 1305 | static void ZSTD_compressBlock_doubleFast_extDict_generic(ZSTD_CCtx* ctx, |
|
1295 | 1306 | const void* src, size_t srcSize, |
|
1296 | 1307 | const U32 mls) |
|
1297 | 1308 | { |
|
1298 | 1309 | U32* const hashLong = ctx->hashTable; |
|
1299 | 1310 | U32 const hBitsL = ctx->params.cParams.hashLog; |
|
1300 | 1311 | U32* const hashSmall = ctx->chainTable; |
|
1301 | 1312 | U32 const hBitsS = ctx->params.cParams.chainLog; |
|
1302 | 1313 | seqStore_t* seqStorePtr = &(ctx->seqStore); |
|
1303 | 1314 | const BYTE* const base = ctx->base; |
|
1304 | 1315 | const BYTE* const dictBase = ctx->dictBase; |
|
1305 | 1316 | const BYTE* const istart = (const BYTE*)src; |
|
1306 | 1317 | const BYTE* ip = istart; |
|
1307 | 1318 | const BYTE* anchor = istart; |
|
1308 | 1319 | const U32 lowestIndex = ctx->lowLimit; |
|
1309 | 1320 | const BYTE* const dictStart = dictBase + lowestIndex; |
|
1310 | 1321 | const U32 dictLimit = ctx->dictLimit; |
|
1311 | 1322 | const BYTE* const lowPrefixPtr = base + dictLimit; |
|
1312 | 1323 | const BYTE* const dictEnd = dictBase + dictLimit; |
|
1313 | 1324 | const BYTE* const iend = istart + srcSize; |
|
1314 | 1325 | const BYTE* const ilimit = iend - 8; |
|
1315 | 1326 | U32 offset_1=ctx->rep[0], offset_2=ctx->rep[1]; |
|
1316 | 1327 | |
|
1317 | 1328 | /* Search Loop */ |
|
1318 | 1329 | while (ip < ilimit) { /* < instead of <=, because (ip+1) */ |
|
1319 | 1330 | const size_t hSmall = ZSTD_hashPtr(ip, hBitsS, mls); |
|
1320 | 1331 | const U32 matchIndex = hashSmall[hSmall]; |
|
1321 | 1332 | const BYTE* matchBase = matchIndex < dictLimit ? dictBase : base; |
|
1322 | 1333 | const BYTE* match = matchBase + matchIndex; |
|
1323 | 1334 | |
|
1324 | 1335 | const size_t hLong = ZSTD_hashPtr(ip, hBitsL, 8); |
|
1325 | 1336 | const U32 matchLongIndex = hashLong[hLong]; |
|
1326 | 1337 | const BYTE* matchLongBase = matchLongIndex < dictLimit ? dictBase : base; |
|
1327 | 1338 | const BYTE* matchLong = matchLongBase + matchLongIndex; |
|
1328 | 1339 | |
|
1329 | 1340 | const U32 current = (U32)(ip-base); |
|
1330 | 1341 | const U32 repIndex = current + 1 - offset_1; /* offset_1 expected <= current +1 */ |
|
1331 | 1342 | const BYTE* repBase = repIndex < dictLimit ? dictBase : base; |
|
1332 | 1343 | const BYTE* repMatch = repBase + repIndex; |
|
1333 | 1344 | size_t mLength; |
|
1334 | 1345 | hashSmall[hSmall] = hashLong[hLong] = current; /* update hash table */ |
|
1335 | 1346 | |
|
1336 | 1347 | if ( (((U32)((dictLimit-1) - repIndex) >= 3) /* intentional underflow */ & (repIndex > lowestIndex)) |
|
1337 | 1348 | && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { |
|
1338 | 1349 | const BYTE* repMatchEnd = repIndex < dictLimit ? dictEnd : iend; |
|
1339 | 1350 | mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, lowPrefixPtr) + 4; |
|
1340 | 1351 | ip++; |
|
1341 | 1352 | ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, 0, mLength-MINMATCH); |
|
1342 | 1353 | } else { |
|
1343 | 1354 | if ((matchLongIndex > lowestIndex) && (MEM_read64(matchLong) == MEM_read64(ip))) { |
|
1344 | 1355 | const BYTE* matchEnd = matchLongIndex < dictLimit ? dictEnd : iend; |
|
1345 | 1356 | const BYTE* lowMatchPtr = matchLongIndex < dictLimit ? dictStart : lowPrefixPtr; |
|
1346 | 1357 | U32 offset; |
|
1347 | 1358 | mLength = ZSTD_count_2segments(ip+8, matchLong+8, iend, matchEnd, lowPrefixPtr) + 8; |
|
1348 | 1359 | offset = current - matchLongIndex; |
|
1349 | 1360 | while (((ip>anchor) & (matchLong>lowMatchPtr)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */ |
|
1350 | 1361 | offset_2 = offset_1; |
|
1351 | 1362 | offset_1 = offset; |
|
1352 | 1363 | ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); |
|
1353 | 1364 | |
|
1354 | 1365 | } else if ((matchIndex > lowestIndex) && (MEM_read32(match) == MEM_read32(ip))) { |
|
1355 | 1366 | size_t const h3 = ZSTD_hashPtr(ip+1, hBitsL, 8); |
|
1356 | 1367 | U32 const matchIndex3 = hashLong[h3]; |
|
1357 | 1368 | const BYTE* const match3Base = matchIndex3 < dictLimit ? dictBase : base; |
|
1358 | 1369 | const BYTE* match3 = match3Base + matchIndex3; |
|
1359 | 1370 | U32 offset; |
|
1360 | 1371 | hashLong[h3] = current + 1; |
|
1361 | 1372 | if ( (matchIndex3 > lowestIndex) && (MEM_read64(match3) == MEM_read64(ip+1)) ) { |
|
1362 | 1373 | const BYTE* matchEnd = matchIndex3 < dictLimit ? dictEnd : iend; |
|
1363 | 1374 | const BYTE* lowMatchPtr = matchIndex3 < dictLimit ? dictStart : lowPrefixPtr; |
|
1364 | 1375 | mLength = ZSTD_count_2segments(ip+9, match3+8, iend, matchEnd, lowPrefixPtr) + 8; |
|
1365 | 1376 | ip++; |
|
1366 | 1377 | offset = current+1 - matchIndex3; |
|
1367 | 1378 | while (((ip>anchor) & (match3>lowMatchPtr)) && (ip[-1] == match3[-1])) { ip--; match3--; mLength++; } /* catch up */ |
|
1368 | 1379 | } else { |
|
1369 | 1380 | const BYTE* matchEnd = matchIndex < dictLimit ? dictEnd : iend; |
|
1370 | 1381 | const BYTE* lowMatchPtr = matchIndex < dictLimit ? dictStart : lowPrefixPtr; |
|
1371 | 1382 | mLength = ZSTD_count_2segments(ip+4, match+4, iend, matchEnd, lowPrefixPtr) + 4; |
|
1372 | 1383 | offset = current - matchIndex; |
|
1373 | 1384 | while (((ip>anchor) & (match>lowMatchPtr)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ |
|
1374 | 1385 | } |
|
1375 | 1386 | offset_2 = offset_1; |
|
1376 | 1387 | offset_1 = offset; |
|
1377 | 1388 | ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); |
|
1378 | 1389 | |
|
1379 | 1390 | } else { |
|
1380 | 1391 | ip += ((ip-anchor) >> g_searchStrength) + 1; |
|
1381 | 1392 | continue; |
|
1382 | 1393 | } } |
|
1383 | 1394 | |
|
1384 | 1395 | /* found a match : store it */ |
|
1385 | 1396 | ip += mLength; |
|
1386 | 1397 | anchor = ip; |
|
1387 | 1398 | |
|
1388 | 1399 | if (ip <= ilimit) { |
|
1389 | 1400 | /* Fill Table */ |
|
1390 | 1401 | hashSmall[ZSTD_hashPtr(base+current+2, hBitsS, mls)] = current+2; |
|
1391 | 1402 | hashLong[ZSTD_hashPtr(base+current+2, hBitsL, 8)] = current+2; |
|
1392 | 1403 | hashSmall[ZSTD_hashPtr(ip-2, hBitsS, mls)] = (U32)(ip-2-base); |
|
1393 | 1404 | hashLong[ZSTD_hashPtr(ip-2, hBitsL, 8)] = (U32)(ip-2-base); |
|
1394 | 1405 | /* check immediate repcode */ |
|
1395 | 1406 | while (ip <= ilimit) { |
|
1396 | 1407 | U32 const current2 = (U32)(ip-base); |
|
1397 | 1408 | U32 const repIndex2 = current2 - offset_2; |
|
1398 | 1409 | const BYTE* repMatch2 = repIndex2 < dictLimit ? dictBase + repIndex2 : base + repIndex2; |
|
1399 | 1410 | if ( (((U32)((dictLimit-1) - repIndex2) >= 3) & (repIndex2 > lowestIndex)) /* intentional overflow */ |
|
1400 | 1411 | && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { |
|
1401 | 1412 | const BYTE* const repEnd2 = repIndex2 < dictLimit ? dictEnd : iend; |
|
1402 | 1413 | size_t const repLength2 = ZSTD_count_2segments(ip+EQUAL_READ32, repMatch2+EQUAL_READ32, iend, repEnd2, lowPrefixPtr) + EQUAL_READ32; |
|
1403 | 1414 | U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ |
|
1404 | 1415 | ZSTD_storeSeq(seqStorePtr, 0, anchor, 0, repLength2-MINMATCH); |
|
1405 | 1416 | hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = current2; |
|
1406 | 1417 | hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = current2; |
|
1407 | 1418 | ip += repLength2; |
|
1408 | 1419 | anchor = ip; |
|
1409 | 1420 | continue; |
|
1410 | 1421 | } |
|
1411 | 1422 | break; |
|
1412 | 1423 | } } } |
|
1413 | 1424 | |
|
1414 | 1425 | /* save reps for next block */ |
|
1415 | 1426 | ctx->savedRep[0] = offset_1; ctx->savedRep[1] = offset_2; |
|
1416 | 1427 | |
|
1417 | 1428 | /* Last Literals */ |
|
1418 | 1429 | { size_t const lastLLSize = iend - anchor; |
|
1419 | 1430 | memcpy(seqStorePtr->lit, anchor, lastLLSize); |
|
1420 | 1431 | seqStorePtr->lit += lastLLSize; |
|
1421 | 1432 | } |
|
1422 | 1433 | } |
|
1423 | 1434 | |
|
1424 | 1435 | |
|
1425 | 1436 | static void ZSTD_compressBlock_doubleFast_extDict(ZSTD_CCtx* ctx, |
|
1426 | 1437 | const void* src, size_t srcSize) |
|
1427 | 1438 | { |
|
1428 | 1439 | U32 const mls = ctx->params.cParams.searchLength; |
|
1429 | 1440 | switch(mls) |
|
1430 | 1441 | { |
|
1431 | 1442 | default: |
|
1432 | 1443 | case 4 : |
|
1433 | 1444 | ZSTD_compressBlock_doubleFast_extDict_generic(ctx, src, srcSize, 4); return; |
|
1434 | 1445 | case 5 : |
|
1435 | 1446 | ZSTD_compressBlock_doubleFast_extDict_generic(ctx, src, srcSize, 5); return; |
|
1436 | 1447 | case 6 : |
|
1437 | 1448 | ZSTD_compressBlock_doubleFast_extDict_generic(ctx, src, srcSize, 6); return; |
|
1438 | 1449 | case 7 : |
|
1439 | 1450 | ZSTD_compressBlock_doubleFast_extDict_generic(ctx, src, srcSize, 7); return; |
|
1440 | 1451 | } |
|
1441 | 1452 | } |
|
1442 | 1453 | |
|
1443 | 1454 | |
|
1444 | 1455 | /*-************************************* |
|
1445 | 1456 | * Binary Tree search |
|
1446 | 1457 | ***************************************/ |
|
1447 | 1458 | /** ZSTD_insertBt1() : add one or multiple positions to tree. |
|
1448 | 1459 | * ip : assumed <= iend-8 . |
|
1449 | 1460 | * @return : nb of positions added */ |
|
1450 | 1461 | static U32 ZSTD_insertBt1(ZSTD_CCtx* zc, const BYTE* const ip, const U32 mls, const BYTE* const iend, U32 nbCompares, |
|
1451 | 1462 | U32 extDict) |
|
1452 | 1463 | { |
|
1453 | 1464 | U32* const hashTable = zc->hashTable; |
|
1454 | 1465 | U32 const hashLog = zc->params.cParams.hashLog; |
|
1455 | 1466 | size_t const h = ZSTD_hashPtr(ip, hashLog, mls); |
|
1456 | 1467 | U32* const bt = zc->chainTable; |
|
1457 | 1468 | U32 const btLog = zc->params.cParams.chainLog - 1; |
|
1458 | 1469 | U32 const btMask = (1 << btLog) - 1; |
|
1459 | 1470 | U32 matchIndex = hashTable[h]; |
|
1460 | 1471 | size_t commonLengthSmaller=0, commonLengthLarger=0; |
|
1461 | 1472 | const BYTE* const base = zc->base; |
|
1462 | 1473 | const BYTE* const dictBase = zc->dictBase; |
|
1463 | 1474 | const U32 dictLimit = zc->dictLimit; |
|
1464 | 1475 | const BYTE* const dictEnd = dictBase + dictLimit; |
|
1465 | 1476 | const BYTE* const prefixStart = base + dictLimit; |
|
1466 | 1477 | const BYTE* match; |
|
1467 | 1478 | const U32 current = (U32)(ip-base); |
|
1468 | 1479 | const U32 btLow = btMask >= current ? 0 : current - btMask; |
|
1469 | 1480 | U32* smallerPtr = bt + 2*(current&btMask); |
|
1470 | 1481 | U32* largerPtr = smallerPtr + 1; |
|
1471 | 1482 | U32 dummy32; /* to be nullified at the end */ |
|
1472 | 1483 | U32 const windowLow = zc->lowLimit; |
|
1473 | 1484 | U32 matchEndIdx = current+8; |
|
1474 | 1485 | size_t bestLength = 8; |
|
1475 | 1486 | #ifdef ZSTD_C_PREDICT |
|
1476 | 1487 | U32 predictedSmall = *(bt + 2*((current-1)&btMask) + 0); |
|
1477 | 1488 | U32 predictedLarge = *(bt + 2*((current-1)&btMask) + 1); |
|
1478 | 1489 | predictedSmall += (predictedSmall>0); |
|
1479 | 1490 | predictedLarge += (predictedLarge>0); |
|
1480 | 1491 | #endif /* ZSTD_C_PREDICT */ |
|
1481 | 1492 | |
|
1482 | 1493 | hashTable[h] = current; /* Update Hash Table */ |
|
1483 | 1494 | |
|
1484 | 1495 | while (nbCompares-- && (matchIndex > windowLow)) { |
|
1485 | U32* nextPtr = bt + 2*(matchIndex & btMask); | |
|
1496 | U32* const nextPtr = bt + 2*(matchIndex & btMask); | |
|
1486 | 1497 | size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ |
|
1498 | ||
|
1487 | 1499 | #ifdef ZSTD_C_PREDICT /* note : can create issues when hlog small <= 11 */ |
|
1488 | 1500 | const U32* predictPtr = bt + 2*((matchIndex-1) & btMask); /* written this way, as bt is a roll buffer */ |
|
1489 | 1501 | if (matchIndex == predictedSmall) { |
|
1490 | 1502 | /* no need to check length, result known */ |
|
1491 | 1503 | *smallerPtr = matchIndex; |
|
1492 | 1504 | if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop the search */ |
|
1493 | 1505 | smallerPtr = nextPtr+1; /* new "smaller" => larger of match */ |
|
1494 | 1506 | matchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to current) */ |
|
1495 | 1507 | predictedSmall = predictPtr[1] + (predictPtr[1]>0); |
|
1496 | 1508 | continue; |
|
1497 | 1509 | } |
|
1498 | 1510 | if (matchIndex == predictedLarge) { |
|
1499 | 1511 | *largerPtr = matchIndex; |
|
1500 | 1512 | if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop the search */ |
|
1501 | 1513 | largerPtr = nextPtr; |
|
1502 | 1514 | matchIndex = nextPtr[0]; |
|
1503 | 1515 | predictedLarge = predictPtr[0] + (predictPtr[0]>0); |
|
1504 | 1516 | continue; |
|
1505 | 1517 | } |
|
1506 | 1518 | #endif |
|
1507 | 1519 | if ((!extDict) || (matchIndex+matchLength >= dictLimit)) { |
|
1508 | 1520 | match = base + matchIndex; |
|
1509 | 1521 | if (match[matchLength] == ip[matchLength]) |
|
1510 | 1522 | matchLength += ZSTD_count(ip+matchLength+1, match+matchLength+1, iend) +1; |
|
1511 | 1523 | } else { |
|
1512 | 1524 | match = dictBase + matchIndex; |
|
1513 | 1525 | matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iend, dictEnd, prefixStart); |
|
1514 | 1526 | if (matchIndex+matchLength >= dictLimit) |
|
1515 | 1527 | match = base + matchIndex; /* to prepare for next usage of match[matchLength] */ |
|
1516 | 1528 | } |
|
1517 | 1529 | |
|
1518 | 1530 | if (matchLength > bestLength) { |
|
1519 | 1531 | bestLength = matchLength; |
|
1520 | 1532 | if (matchLength > matchEndIdx - matchIndex) |
|
1521 | 1533 | matchEndIdx = matchIndex + (U32)matchLength; |
|
1522 | 1534 | } |
|
1523 | 1535 | |
|
1524 | 1536 | if (ip+matchLength == iend) /* equal : no way to know if inf or sup */ |
|
1525 | 1537 | break; /* drop , to guarantee consistency ; miss a bit of compression, but other solutions can corrupt the tree */ |
|
1526 | 1538 | |
|
1527 | 1539 | if (match[matchLength] < ip[matchLength]) { /* necessarily within correct buffer */ |
|
1528 | 1540 | /* match is smaller than current */ |
|
1529 | 1541 | *smallerPtr = matchIndex; /* update smaller idx */ |
|
1530 | 1542 | commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */ |
|
1531 | 1543 | if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop the search */ |
|
1532 | 1544 | smallerPtr = nextPtr+1; /* new "smaller" => larger of match */ |
|
1533 | 1545 | matchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to current) */ |
|
1534 | 1546 | } else { |
|
1535 | 1547 | /* match is larger than current */ |
|
1536 | 1548 | *largerPtr = matchIndex; |
|
1537 | 1549 | commonLengthLarger = matchLength; |
|
1538 | 1550 | if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop the search */ |
|
1539 | 1551 | largerPtr = nextPtr; |
|
1540 | 1552 | matchIndex = nextPtr[0]; |
|
1541 | 1553 | } } |
|
1542 | 1554 | |
|
1543 | 1555 | *smallerPtr = *largerPtr = 0; |
|
1544 | 1556 | if (bestLength > 384) return MIN(192, (U32)(bestLength - 384)); /* speed optimization */ |
|
1545 | 1557 | if (matchEndIdx > current + 8) return matchEndIdx - current - 8; |
|
1546 | 1558 | return 1; |
|
1547 | 1559 | } |
|
1548 | 1560 | |
|
1549 | 1561 | |
|
1550 | 1562 | static size_t ZSTD_insertBtAndFindBestMatch ( |
|
1551 | 1563 | ZSTD_CCtx* zc, |
|
1552 | 1564 | const BYTE* const ip, const BYTE* const iend, |
|
1553 | 1565 | size_t* offsetPtr, |
|
1554 | 1566 | U32 nbCompares, const U32 mls, |
|
1555 | 1567 | U32 extDict) |
|
1556 | 1568 | { |
|
1557 | 1569 | U32* const hashTable = zc->hashTable; |
|
1558 | 1570 | U32 const hashLog = zc->params.cParams.hashLog; |
|
1559 | 1571 | size_t const h = ZSTD_hashPtr(ip, hashLog, mls); |
|
1560 | 1572 | U32* const bt = zc->chainTable; |
|
1561 | 1573 | U32 const btLog = zc->params.cParams.chainLog - 1; |
|
1562 | 1574 | U32 const btMask = (1 << btLog) - 1; |
|
1563 | 1575 | U32 matchIndex = hashTable[h]; |
|
1564 | 1576 | size_t commonLengthSmaller=0, commonLengthLarger=0; |
|
1565 | 1577 | const BYTE* const base = zc->base; |
|
1566 | 1578 | const BYTE* const dictBase = zc->dictBase; |
|
1567 | 1579 | const U32 dictLimit = zc->dictLimit; |
|
1568 | 1580 | const BYTE* const dictEnd = dictBase + dictLimit; |
|
1569 | 1581 | const BYTE* const prefixStart = base + dictLimit; |
|
1570 | 1582 | const U32 current = (U32)(ip-base); |
|
1571 | 1583 | const U32 btLow = btMask >= current ? 0 : current - btMask; |
|
1572 | 1584 | const U32 windowLow = zc->lowLimit; |
|
1573 | 1585 | U32* smallerPtr = bt + 2*(current&btMask); |
|
1574 | 1586 | U32* largerPtr = bt + 2*(current&btMask) + 1; |
|
1575 | 1587 | U32 matchEndIdx = current+8; |
|
1576 | 1588 | U32 dummy32; /* to be nullified at the end */ |
|
1577 | 1589 | size_t bestLength = 0; |
|
1578 | 1590 | |
|
1579 | 1591 | hashTable[h] = current; /* Update Hash Table */ |
|
1580 | 1592 | |
|
1581 | 1593 | while (nbCompares-- && (matchIndex > windowLow)) { |
|
1582 | U32* nextPtr = bt + 2*(matchIndex & btMask); | |
|
1594 | U32* const nextPtr = bt + 2*(matchIndex & btMask); | |
|
1583 | 1595 | size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ |
|
1584 | 1596 | const BYTE* match; |
|
1585 | 1597 | |
|
1586 | 1598 | if ((!extDict) || (matchIndex+matchLength >= dictLimit)) { |
|
1587 | 1599 | match = base + matchIndex; |
|
1588 | 1600 | if (match[matchLength] == ip[matchLength]) |
|
1589 | 1601 | matchLength += ZSTD_count(ip+matchLength+1, match+matchLength+1, iend) +1; |
|
1590 | 1602 | } else { |
|
1591 | 1603 | match = dictBase + matchIndex; |
|
1592 | 1604 | matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iend, dictEnd, prefixStart); |
|
1593 | 1605 | if (matchIndex+matchLength >= dictLimit) |
|
1594 | 1606 | match = base + matchIndex; /* to prepare for next usage of match[matchLength] */ |
|
1595 | 1607 | } |
|
1596 | 1608 | |
|
1597 | 1609 | if (matchLength > bestLength) { |
|
1598 | 1610 | if (matchLength > matchEndIdx - matchIndex) |
|
1599 | 1611 | matchEndIdx = matchIndex + (U32)matchLength; |
|
1600 | 1612 | if ( (4*(int)(matchLength-bestLength)) > (int)(ZSTD_highbit32(current-matchIndex+1) - ZSTD_highbit32((U32)offsetPtr[0]+1)) ) |
|
1601 | 1613 | bestLength = matchLength, *offsetPtr = ZSTD_REP_MOVE + current - matchIndex; |
|
1602 | 1614 | if (ip+matchLength == iend) /* equal : no way to know if inf or sup */ |
|
1603 | 1615 | break; /* drop, to guarantee consistency (miss a little bit of compression) */ |
|
1604 | 1616 | } |
|
1605 | 1617 | |
|
1606 | 1618 | if (match[matchLength] < ip[matchLength]) { |
|
1607 | 1619 | /* match is smaller than current */ |
|
1608 | 1620 | *smallerPtr = matchIndex; /* update smaller idx */ |
|
1609 | 1621 | commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */ |
|
1610 | 1622 | if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop the search */ |
|
1611 | 1623 | smallerPtr = nextPtr+1; /* new "smaller" => larger of match */ |
|
1612 | 1624 | matchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to current) */ |
|
1613 | 1625 | } else { |
|
1614 | 1626 | /* match is larger than current */ |
|
1615 | 1627 | *largerPtr = matchIndex; |
|
1616 | 1628 | commonLengthLarger = matchLength; |
|
1617 | 1629 | if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop the search */ |
|
1618 | 1630 | largerPtr = nextPtr; |
|
1619 | 1631 | matchIndex = nextPtr[0]; |
|
1620 | 1632 | } } |
|
1621 | 1633 | |
|
1622 | 1634 | *smallerPtr = *largerPtr = 0; |
|
1623 | 1635 | |
|
1624 | 1636 | zc->nextToUpdate = (matchEndIdx > current + 8) ? matchEndIdx - 8 : current+1; |
|
1625 | 1637 | return bestLength; |
|
1626 | 1638 | } |
|
1627 | 1639 | |
|
1628 | 1640 | |
|
1629 | 1641 | static void ZSTD_updateTree(ZSTD_CCtx* zc, const BYTE* const ip, const BYTE* const iend, const U32 nbCompares, const U32 mls) |
|
1630 | 1642 | { |
|
1631 | 1643 | const BYTE* const base = zc->base; |
|
1632 | 1644 | const U32 target = (U32)(ip - base); |
|
1633 | 1645 | U32 idx = zc->nextToUpdate; |
|
1634 | 1646 | |
|
1635 | 1647 | while(idx < target) |
|
1636 | 1648 | idx += ZSTD_insertBt1(zc, base+idx, mls, iend, nbCompares, 0); |
|
1637 | 1649 | } |
|
1638 | 1650 | |
|
1639 | 1651 | /** ZSTD_BtFindBestMatch() : Tree updater, providing best match */ |
|
1640 | 1652 | static size_t ZSTD_BtFindBestMatch ( |
|
1641 | 1653 | ZSTD_CCtx* zc, |
|
1642 | 1654 | const BYTE* const ip, const BYTE* const iLimit, |
|
1643 | 1655 | size_t* offsetPtr, |
|
1644 | 1656 | const U32 maxNbAttempts, const U32 mls) |
|
1645 | 1657 | { |
|
1646 | 1658 | if (ip < zc->base + zc->nextToUpdate) return 0; /* skipped area */ |
|
1647 | 1659 | ZSTD_updateTree(zc, ip, iLimit, maxNbAttempts, mls); |
|
1648 | 1660 | return ZSTD_insertBtAndFindBestMatch(zc, ip, iLimit, offsetPtr, maxNbAttempts, mls, 0); |
|
1649 | 1661 | } |
|
1650 | 1662 | |
|
1651 | 1663 | |
|
1652 | 1664 | static size_t ZSTD_BtFindBestMatch_selectMLS ( |
|
1653 | 1665 | ZSTD_CCtx* zc, /* Index table will be updated */ |
|
1654 | 1666 | const BYTE* ip, const BYTE* const iLimit, |
|
1655 | 1667 | size_t* offsetPtr, |
|
1656 | 1668 | const U32 maxNbAttempts, const U32 matchLengthSearch) |
|
1657 | 1669 | { |
|
1658 | 1670 | switch(matchLengthSearch) |
|
1659 | 1671 | { |
|
1660 | 1672 | default : |
|
1661 | 1673 | case 4 : return ZSTD_BtFindBestMatch(zc, ip, iLimit, offsetPtr, maxNbAttempts, 4); |
|
1662 | 1674 | case 5 : return ZSTD_BtFindBestMatch(zc, ip, iLimit, offsetPtr, maxNbAttempts, 5); |
|
1663 | 1675 | case 6 : return ZSTD_BtFindBestMatch(zc, ip, iLimit, offsetPtr, maxNbAttempts, 6); |
|
1664 | 1676 | } |
|
1665 | 1677 | } |
|
1666 | 1678 | |
|
1667 | 1679 | |
|
1668 | 1680 | static void ZSTD_updateTree_extDict(ZSTD_CCtx* zc, const BYTE* const ip, const BYTE* const iend, const U32 nbCompares, const U32 mls) |
|
1669 | 1681 | { |
|
1670 | 1682 | const BYTE* const base = zc->base; |
|
1671 | 1683 | const U32 target = (U32)(ip - base); |
|
1672 | 1684 | U32 idx = zc->nextToUpdate; |
|
1673 | 1685 | |
|
1674 | 1686 | while (idx < target) idx += ZSTD_insertBt1(zc, base+idx, mls, iend, nbCompares, 1); |
|
1675 | 1687 | } |
|
1676 | 1688 | |
|
1677 | 1689 | |
|
1678 | 1690 | /** Tree updater, providing best match */ |
|
1679 | 1691 | static size_t ZSTD_BtFindBestMatch_extDict ( |
|
1680 | 1692 | ZSTD_CCtx* zc, |
|
1681 | 1693 | const BYTE* const ip, const BYTE* const iLimit, |
|
1682 | 1694 | size_t* offsetPtr, |
|
1683 | 1695 | const U32 maxNbAttempts, const U32 mls) |
|
1684 | 1696 | { |
|
1685 | 1697 | if (ip < zc->base + zc->nextToUpdate) return 0; /* skipped area */ |
|
1686 | 1698 | ZSTD_updateTree_extDict(zc, ip, iLimit, maxNbAttempts, mls); |
|
1687 | 1699 | return ZSTD_insertBtAndFindBestMatch(zc, ip, iLimit, offsetPtr, maxNbAttempts, mls, 1); |
|
1688 | 1700 | } |
|
1689 | 1701 | |
|
1690 | 1702 | |
|
1691 | 1703 | static size_t ZSTD_BtFindBestMatch_selectMLS_extDict ( |
|
1692 | 1704 | ZSTD_CCtx* zc, /* Index table will be updated */ |
|
1693 | 1705 | const BYTE* ip, const BYTE* const iLimit, |
|
1694 | 1706 | size_t* offsetPtr, |
|
1695 | 1707 | const U32 maxNbAttempts, const U32 matchLengthSearch) |
|
1696 | 1708 | { |
|
1697 | 1709 | switch(matchLengthSearch) |
|
1698 | 1710 | { |
|
1699 | 1711 | default : |
|
1700 | 1712 | case 4 : return ZSTD_BtFindBestMatch_extDict(zc, ip, iLimit, offsetPtr, maxNbAttempts, 4); |
|
1701 | 1713 | case 5 : return ZSTD_BtFindBestMatch_extDict(zc, ip, iLimit, offsetPtr, maxNbAttempts, 5); |
|
1702 | 1714 | case 6 : return ZSTD_BtFindBestMatch_extDict(zc, ip, iLimit, offsetPtr, maxNbAttempts, 6); |
|
1703 | 1715 | } |
|
1704 | 1716 | } |
|
1705 | 1717 | |
|
1706 | 1718 | |
|
1707 | 1719 | |
|
1708 | 1720 | /* ********************************* |
|
1709 | 1721 | * Hash Chain |
|
1710 | 1722 | ***********************************/ |
|
1711 | 1723 | #define NEXT_IN_CHAIN(d, mask) chainTable[(d) & mask] |
|
1712 | 1724 | |
|
1713 | 1725 | /* Update chains up to ip (excluded) |
|
1714 | 1726 | Assumption : always within prefix (ie. not within extDict) */ |
|
1715 | 1727 | FORCE_INLINE |
|
1716 | 1728 | U32 ZSTD_insertAndFindFirstIndex (ZSTD_CCtx* zc, const BYTE* ip, U32 mls) |
|
1717 | 1729 | { |
|
1718 | 1730 | U32* const hashTable = zc->hashTable; |
|
1719 | 1731 | const U32 hashLog = zc->params.cParams.hashLog; |
|
1720 | 1732 | U32* const chainTable = zc->chainTable; |
|
1721 | 1733 | const U32 chainMask = (1 << zc->params.cParams.chainLog) - 1; |
|
1722 | 1734 | const BYTE* const base = zc->base; |
|
1723 | 1735 | const U32 target = (U32)(ip - base); |
|
1724 | 1736 | U32 idx = zc->nextToUpdate; |
|
1725 | 1737 | |
|
1726 | 1738 | while(idx < target) { /* catch up */ |
|
1727 | 1739 | size_t const h = ZSTD_hashPtr(base+idx, hashLog, mls); |
|
1728 | 1740 | NEXT_IN_CHAIN(idx, chainMask) = hashTable[h]; |
|
1729 | 1741 | hashTable[h] = idx; |
|
1730 | 1742 | idx++; |
|
1731 | 1743 | } |
|
1732 | 1744 | |
|
1733 | 1745 | zc->nextToUpdate = target; |
|
1734 | 1746 | return hashTable[ZSTD_hashPtr(ip, hashLog, mls)]; |
|
1735 | 1747 | } |
|
1736 | 1748 | |
|
1737 | 1749 | |
|
1738 | 1750 | |
|
1739 | 1751 | FORCE_INLINE /* inlining is important to hardwire a hot branch (template emulation) */ |
|
1740 | 1752 | size_t ZSTD_HcFindBestMatch_generic ( |
|
1741 | 1753 | ZSTD_CCtx* zc, /* Index table will be updated */ |
|
1742 | 1754 | const BYTE* const ip, const BYTE* const iLimit, |
|
1743 | 1755 | size_t* offsetPtr, |
|
1744 | 1756 | const U32 maxNbAttempts, const U32 mls, const U32 extDict) |
|
1745 | 1757 | { |
|
1746 | 1758 | U32* const chainTable = zc->chainTable; |
|
1747 | 1759 | const U32 chainSize = (1 << zc->params.cParams.chainLog); |
|
1748 | 1760 | const U32 chainMask = chainSize-1; |
|
1749 | 1761 | const BYTE* const base = zc->base; |
|
1750 | 1762 | const BYTE* const dictBase = zc->dictBase; |
|
1751 | 1763 | const U32 dictLimit = zc->dictLimit; |
|
1752 | 1764 | const BYTE* const prefixStart = base + dictLimit; |
|
1753 | 1765 | const BYTE* const dictEnd = dictBase + dictLimit; |
|
1754 | 1766 | const U32 lowLimit = zc->lowLimit; |
|
1755 | 1767 | const U32 current = (U32)(ip-base); |
|
1756 | 1768 | const U32 minChain = current > chainSize ? current - chainSize : 0; |
|
1757 | 1769 | int nbAttempts=maxNbAttempts; |
|
1758 | 1770 | size_t ml=EQUAL_READ32-1; |
|
1759 | 1771 | |
|
1760 | 1772 | /* HC4 match finder */ |
|
1761 | 1773 | U32 matchIndex = ZSTD_insertAndFindFirstIndex (zc, ip, mls); |
|
1762 | 1774 | |
|
1763 | 1775 | for ( ; (matchIndex>lowLimit) & (nbAttempts>0) ; nbAttempts--) { |
|
1764 | 1776 | const BYTE* match; |
|
1765 | 1777 | size_t currentMl=0; |
|
1766 | 1778 | if ((!extDict) || matchIndex >= dictLimit) { |
|
1767 | 1779 | match = base + matchIndex; |
|
1768 | 1780 | if (match[ml] == ip[ml]) /* potentially better */ |
|
1769 | 1781 | currentMl = ZSTD_count(ip, match, iLimit); |
|
1770 | 1782 | } else { |
|
1771 | 1783 | match = dictBase + matchIndex; |
|
1772 | 1784 | if (MEM_read32(match) == MEM_read32(ip)) /* assumption : matchIndex <= dictLimit-4 (by table construction) */ |
|
1773 | 1785 | currentMl = ZSTD_count_2segments(ip+EQUAL_READ32, match+EQUAL_READ32, iLimit, dictEnd, prefixStart) + EQUAL_READ32; |
|
1774 | 1786 | } |
|
1775 | 1787 | |
|
1776 | 1788 | /* save best solution */ |
|
1777 | 1789 | if (currentMl > ml) { ml = currentMl; *offsetPtr = current - matchIndex + ZSTD_REP_MOVE; if (ip+currentMl == iLimit) break; /* best possible, and avoid read overflow*/ } |
|
1778 | 1790 | |
|
1779 | 1791 | if (matchIndex <= minChain) break; |
|
1780 | 1792 | matchIndex = NEXT_IN_CHAIN(matchIndex, chainMask); |
|
1781 | 1793 | } |
|
1782 | 1794 | |
|
1783 | 1795 | return ml; |
|
1784 | 1796 | } |
|
1785 | 1797 | |
|
1786 | 1798 | |
|
1787 | 1799 | FORCE_INLINE size_t ZSTD_HcFindBestMatch_selectMLS ( |
|
1788 | 1800 | ZSTD_CCtx* zc, |
|
1789 | 1801 | const BYTE* ip, const BYTE* const iLimit, |
|
1790 | 1802 | size_t* offsetPtr, |
|
1791 | 1803 | const U32 maxNbAttempts, const U32 matchLengthSearch) |
|
1792 | 1804 | { |
|
1793 | 1805 | switch(matchLengthSearch) |
|
1794 | 1806 | { |
|
1795 | 1807 | default : |
|
1796 | 1808 | case 4 : return ZSTD_HcFindBestMatch_generic(zc, ip, iLimit, offsetPtr, maxNbAttempts, 4, 0); |
|
1797 | 1809 | case 5 : return ZSTD_HcFindBestMatch_generic(zc, ip, iLimit, offsetPtr, maxNbAttempts, 5, 0); |
|
1798 | 1810 | case 6 : return ZSTD_HcFindBestMatch_generic(zc, ip, iLimit, offsetPtr, maxNbAttempts, 6, 0); |
|
1799 | 1811 | } |
|
1800 | 1812 | } |
|
1801 | 1813 | |
|
1802 | 1814 | |
|
1803 | 1815 | FORCE_INLINE size_t ZSTD_HcFindBestMatch_extDict_selectMLS ( |
|
1804 | 1816 | ZSTD_CCtx* zc, |
|
1805 | 1817 | const BYTE* ip, const BYTE* const iLimit, |
|
1806 | 1818 | size_t* offsetPtr, |
|
1807 | 1819 | const U32 maxNbAttempts, const U32 matchLengthSearch) |
|
1808 | 1820 | { |
|
1809 | 1821 | switch(matchLengthSearch) |
|
1810 | 1822 | { |
|
1811 | 1823 | default : |
|
1812 | 1824 | case 4 : return ZSTD_HcFindBestMatch_generic(zc, ip, iLimit, offsetPtr, maxNbAttempts, 4, 1); |
|
1813 | 1825 | case 5 : return ZSTD_HcFindBestMatch_generic(zc, ip, iLimit, offsetPtr, maxNbAttempts, 5, 1); |
|
1814 | 1826 | case 6 : return ZSTD_HcFindBestMatch_generic(zc, ip, iLimit, offsetPtr, maxNbAttempts, 6, 1); |
|
1815 | 1827 | } |
|
1816 | 1828 | } |
|
1817 | 1829 | |
|
1818 | 1830 | |
|
1819 | 1831 | /* ******************************* |
|
1820 | 1832 | * Common parser - lazy strategy |
|
1821 | 1833 | *********************************/ |
|
1822 | 1834 | FORCE_INLINE |
|
1823 | 1835 | void ZSTD_compressBlock_lazy_generic(ZSTD_CCtx* ctx, |
|
1824 | 1836 | const void* src, size_t srcSize, |
|
1825 | 1837 | const U32 searchMethod, const U32 depth) |
|
1826 | 1838 | { |
|
1827 | 1839 | seqStore_t* seqStorePtr = &(ctx->seqStore); |
|
1828 | 1840 | const BYTE* const istart = (const BYTE*)src; |
|
1829 | 1841 | const BYTE* ip = istart; |
|
1830 | 1842 | const BYTE* anchor = istart; |
|
1831 | 1843 | const BYTE* const iend = istart + srcSize; |
|
1832 | 1844 | const BYTE* const ilimit = iend - 8; |
|
1833 | 1845 | const BYTE* const base = ctx->base + ctx->dictLimit; |
|
1834 | 1846 | |
|
1835 | 1847 | U32 const maxSearches = 1 << ctx->params.cParams.searchLog; |
|
1836 | 1848 | U32 const mls = ctx->params.cParams.searchLength; |
|
1837 | 1849 | |
|
1838 | 1850 | typedef size_t (*searchMax_f)(ZSTD_CCtx* zc, const BYTE* ip, const BYTE* iLimit, |
|
1839 | 1851 | size_t* offsetPtr, |
|
1840 | 1852 | U32 maxNbAttempts, U32 matchLengthSearch); |
|
1841 | 1853 | searchMax_f const searchMax = searchMethod ? ZSTD_BtFindBestMatch_selectMLS : ZSTD_HcFindBestMatch_selectMLS; |
|
1842 | 1854 | U32 offset_1 = ctx->rep[0], offset_2 = ctx->rep[1], savedOffset=0; |
|
1843 | 1855 | |
|
1844 | 1856 | /* init */ |
|
1845 | 1857 | ip += (ip==base); |
|
1846 | 1858 | ctx->nextToUpdate3 = ctx->nextToUpdate; |
|
1847 | 1859 | { U32 const maxRep = (U32)(ip-base); |
|
1848 | 1860 | if (offset_2 > maxRep) savedOffset = offset_2, offset_2 = 0; |
|
1849 | 1861 | if (offset_1 > maxRep) savedOffset = offset_1, offset_1 = 0; |
|
1850 | 1862 | } |
|
1851 | 1863 | |
|
1852 | 1864 | /* Match Loop */ |
|
1853 | 1865 | while (ip < ilimit) { |
|
1854 | 1866 | size_t matchLength=0; |
|
1855 | 1867 | size_t offset=0; |
|
1856 | 1868 | const BYTE* start=ip+1; |
|
1857 | 1869 | |
|
1858 | 1870 | /* check repCode */ |
|
1859 | 1871 | if ((offset_1>0) & (MEM_read32(ip+1) == MEM_read32(ip+1 - offset_1))) { |
|
1860 | 1872 | /* repcode : we take it */ |
|
1861 | 1873 | matchLength = ZSTD_count(ip+1+EQUAL_READ32, ip+1+EQUAL_READ32-offset_1, iend) + EQUAL_READ32; |
|
1862 | 1874 | if (depth==0) goto _storeSequence; |
|
1863 | 1875 | } |
|
1864 | 1876 | |
|
1865 | 1877 | /* first search (depth 0) */ |
|
1866 | 1878 | { size_t offsetFound = 99999999; |
|
1867 | 1879 | size_t const ml2 = searchMax(ctx, ip, iend, &offsetFound, maxSearches, mls); |
|
1868 | 1880 | if (ml2 > matchLength) |
|
1869 | 1881 | matchLength = ml2, start = ip, offset=offsetFound; |
|
1870 | 1882 | } |
|
1871 | 1883 | |
|
1872 | 1884 | if (matchLength < EQUAL_READ32) { |
|
1873 | 1885 | ip += ((ip-anchor) >> g_searchStrength) + 1; /* jump faster over incompressible sections */ |
|
1874 | 1886 | continue; |
|
1875 | 1887 | } |
|
1876 | 1888 | |
|
1877 | 1889 | /* let's try to find a better solution */ |
|
1878 | 1890 | if (depth>=1) |
|
1879 | 1891 | while (ip<ilimit) { |
|
1880 | 1892 | ip ++; |
|
1881 | 1893 | if ((offset) && ((offset_1>0) & (MEM_read32(ip) == MEM_read32(ip - offset_1)))) { |
|
1882 | 1894 | size_t const mlRep = ZSTD_count(ip+EQUAL_READ32, ip+EQUAL_READ32-offset_1, iend) + EQUAL_READ32; |
|
1883 | 1895 | int const gain2 = (int)(mlRep * 3); |
|
1884 | 1896 | int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)offset+1) + 1); |
|
1885 | 1897 | if ((mlRep >= EQUAL_READ32) && (gain2 > gain1)) |
|
1886 | 1898 | matchLength = mlRep, offset = 0, start = ip; |
|
1887 | 1899 | } |
|
1888 | 1900 | { size_t offset2=99999999; |
|
1889 | 1901 | size_t const ml2 = searchMax(ctx, ip, iend, &offset2, maxSearches, mls); |
|
1890 | 1902 | int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1)); /* raw approx */ |
|
1891 | 1903 | int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 4); |
|
1892 | 1904 | if ((ml2 >= EQUAL_READ32) && (gain2 > gain1)) { |
|
1893 | 1905 | matchLength = ml2, offset = offset2, start = ip; |
|
1894 | 1906 | continue; /* search a better one */ |
|
1895 | 1907 | } } |
|
1896 | 1908 | |
|
1897 | 1909 | /* let's find an even better one */ |
|
1898 | 1910 | if ((depth==2) && (ip<ilimit)) { |
|
1899 | 1911 | ip ++; |
|
1900 | 1912 | if ((offset) && ((offset_1>0) & (MEM_read32(ip) == MEM_read32(ip - offset_1)))) { |
|
1901 | 1913 | size_t const ml2 = ZSTD_count(ip+EQUAL_READ32, ip+EQUAL_READ32-offset_1, iend) + EQUAL_READ32; |
|
1902 | 1914 | int const gain2 = (int)(ml2 * 4); |
|
1903 | 1915 | int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 1); |
|
1904 | 1916 | if ((ml2 >= EQUAL_READ32) && (gain2 > gain1)) |
|
1905 | 1917 | matchLength = ml2, offset = 0, start = ip; |
|
1906 | 1918 | } |
|
1907 | 1919 | { size_t offset2=99999999; |
|
1908 | 1920 | size_t const ml2 = searchMax(ctx, ip, iend, &offset2, maxSearches, mls); |
|
1909 | 1921 | int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1)); /* raw approx */ |
|
1910 | 1922 | int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 7); |
|
1911 | 1923 | if ((ml2 >= EQUAL_READ32) && (gain2 > gain1)) { |
|
1912 | 1924 | matchLength = ml2, offset = offset2, start = ip; |
|
1913 | 1925 | continue; |
|
1914 | 1926 | } } } |
|
1915 | 1927 | break; /* nothing found : store previous solution */ |
|
1916 | 1928 | } |
|
1917 | 1929 | |
|
1918 | 1930 | /* catch up */ |
|
1919 | 1931 | if (offset) { |
|
1920 | 1932 | while ((start>anchor) && (start>base+offset-ZSTD_REP_MOVE) && (start[-1] == start[-1-offset+ZSTD_REP_MOVE])) /* only search for offset within prefix */ |
|
1921 | 1933 | { start--; matchLength++; } |
|
1922 | 1934 | offset_2 = offset_1; offset_1 = (U32)(offset - ZSTD_REP_MOVE); |
|
1923 | 1935 | } |
|
1924 | 1936 | |
|
1925 | 1937 | /* store sequence */ |
|
1926 | 1938 | _storeSequence: |
|
1927 | 1939 | { size_t const litLength = start - anchor; |
|
1928 | 1940 | ZSTD_storeSeq(seqStorePtr, litLength, anchor, (U32)offset, matchLength-MINMATCH); |
|
1929 | 1941 | anchor = ip = start + matchLength; |
|
1930 | 1942 | } |
|
1931 | 1943 | |
|
1932 | 1944 | /* check immediate repcode */ |
|
1933 | 1945 | while ( (ip <= ilimit) |
|
1934 | 1946 | && ((offset_2>0) |
|
1935 | 1947 | & (MEM_read32(ip) == MEM_read32(ip - offset_2)) )) { |
|
1936 | 1948 | /* store sequence */ |
|
1937 | 1949 | matchLength = ZSTD_count(ip+EQUAL_READ32, ip+EQUAL_READ32-offset_2, iend) + EQUAL_READ32; |
|
1938 | 1950 | offset = offset_2; offset_2 = offset_1; offset_1 = (U32)offset; /* swap repcodes */ |
|
1939 | 1951 | ZSTD_storeSeq(seqStorePtr, 0, anchor, 0, matchLength-MINMATCH); |
|
1940 | 1952 | ip += matchLength; |
|
1941 | 1953 | anchor = ip; |
|
1942 | 1954 | continue; /* faster when present ... (?) */ |
|
1943 | 1955 | } } |
|
1944 | 1956 | |
|
1945 | 1957 | /* Save reps for next block */ |
|
1946 | 1958 | ctx->savedRep[0] = offset_1 ? offset_1 : savedOffset; |
|
1947 | 1959 | ctx->savedRep[1] = offset_2 ? offset_2 : savedOffset; |
|
1948 | 1960 | |
|
1949 | 1961 | /* Last Literals */ |
|
1950 | 1962 | { size_t const lastLLSize = iend - anchor; |
|
1951 | 1963 | memcpy(seqStorePtr->lit, anchor, lastLLSize); |
|
1952 | 1964 | seqStorePtr->lit += lastLLSize; |
|
1953 | 1965 | } |
|
1954 | 1966 | } |
|
1955 | 1967 | |
|
1956 | 1968 | |
|
1957 | 1969 | static void ZSTD_compressBlock_btlazy2(ZSTD_CCtx* ctx, const void* src, size_t srcSize) |
|
1958 | 1970 | { |
|
1959 | 1971 | ZSTD_compressBlock_lazy_generic(ctx, src, srcSize, 1, 2); |
|
1960 | 1972 | } |
|
1961 | 1973 | |
|
1962 | 1974 | static void ZSTD_compressBlock_lazy2(ZSTD_CCtx* ctx, const void* src, size_t srcSize) |
|
1963 | 1975 | { |
|
1964 | 1976 | ZSTD_compressBlock_lazy_generic(ctx, src, srcSize, 0, 2); |
|
1965 | 1977 | } |
|
1966 | 1978 | |
|
1967 | 1979 | static void ZSTD_compressBlock_lazy(ZSTD_CCtx* ctx, const void* src, size_t srcSize) |
|
1968 | 1980 | { |
|
1969 | 1981 | ZSTD_compressBlock_lazy_generic(ctx, src, srcSize, 0, 1); |
|
1970 | 1982 | } |
|
1971 | 1983 | |
|
1972 | 1984 | static void ZSTD_compressBlock_greedy(ZSTD_CCtx* ctx, const void* src, size_t srcSize) |
|
1973 | 1985 | { |
|
1974 | 1986 | ZSTD_compressBlock_lazy_generic(ctx, src, srcSize, 0, 0); |
|
1975 | 1987 | } |
|
1976 | 1988 | |
|
1977 | 1989 | |
|
1978 | 1990 | FORCE_INLINE |
|
1979 | 1991 | void ZSTD_compressBlock_lazy_extDict_generic(ZSTD_CCtx* ctx, |
|
1980 | 1992 | const void* src, size_t srcSize, |
|
1981 | 1993 | const U32 searchMethod, const U32 depth) |
|
1982 | 1994 | { |
|
1983 | 1995 | seqStore_t* seqStorePtr = &(ctx->seqStore); |
|
1984 | 1996 | const BYTE* const istart = (const BYTE*)src; |
|
1985 | 1997 | const BYTE* ip = istart; |
|
1986 | 1998 | const BYTE* anchor = istart; |
|
1987 | 1999 | const BYTE* const iend = istart + srcSize; |
|
1988 | 2000 | const BYTE* const ilimit = iend - 8; |
|
1989 | 2001 | const BYTE* const base = ctx->base; |
|
1990 | 2002 | const U32 dictLimit = ctx->dictLimit; |
|
1991 | 2003 | const U32 lowestIndex = ctx->lowLimit; |
|
1992 | 2004 | const BYTE* const prefixStart = base + dictLimit; |
|
1993 | 2005 | const BYTE* const dictBase = ctx->dictBase; |
|
1994 | 2006 | const BYTE* const dictEnd = dictBase + dictLimit; |
|
1995 | 2007 | const BYTE* const dictStart = dictBase + ctx->lowLimit; |
|
1996 | 2008 | |
|
1997 | 2009 | const U32 maxSearches = 1 << ctx->params.cParams.searchLog; |
|
1998 | 2010 | const U32 mls = ctx->params.cParams.searchLength; |
|
1999 | 2011 | |
|
2000 | 2012 | typedef size_t (*searchMax_f)(ZSTD_CCtx* zc, const BYTE* ip, const BYTE* iLimit, |
|
2001 | 2013 | size_t* offsetPtr, |
|
2002 | 2014 | U32 maxNbAttempts, U32 matchLengthSearch); |
|
2003 | 2015 | searchMax_f searchMax = searchMethod ? ZSTD_BtFindBestMatch_selectMLS_extDict : ZSTD_HcFindBestMatch_extDict_selectMLS; |
|
2004 | 2016 | |
|
2005 | 2017 | U32 offset_1 = ctx->rep[0], offset_2 = ctx->rep[1]; |
|
2006 | 2018 | |
|
2007 | 2019 | /* init */ |
|
2008 | 2020 | ctx->nextToUpdate3 = ctx->nextToUpdate; |
|
2009 | 2021 | ip += (ip == prefixStart); |
|
2010 | 2022 | |
|
2011 | 2023 | /* Match Loop */ |
|
2012 | 2024 | while (ip < ilimit) { |
|
2013 | 2025 | size_t matchLength=0; |
|
2014 | 2026 | size_t offset=0; |
|
2015 | 2027 | const BYTE* start=ip+1; |
|
2016 | 2028 | U32 current = (U32)(ip-base); |
|
2017 | 2029 | |
|
2018 | 2030 | /* check repCode */ |
|
2019 | 2031 | { const U32 repIndex = (U32)(current+1 - offset_1); |
|
2020 | 2032 | const BYTE* const repBase = repIndex < dictLimit ? dictBase : base; |
|
2021 | 2033 | const BYTE* const repMatch = repBase + repIndex; |
|
2022 | 2034 | if (((U32)((dictLimit-1) - repIndex) >= 3) & (repIndex > lowestIndex)) /* intentional overflow */ |
|
2023 | 2035 | if (MEM_read32(ip+1) == MEM_read32(repMatch)) { |
|
2024 | 2036 | /* repcode detected we should take it */ |
|
2025 | 2037 | const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend; |
|
2026 | 2038 | matchLength = ZSTD_count_2segments(ip+1+EQUAL_READ32, repMatch+EQUAL_READ32, iend, repEnd, prefixStart) + EQUAL_READ32; |
|
2027 | 2039 | if (depth==0) goto _storeSequence; |
|
2028 | 2040 | } } |
|
2029 | 2041 | |
|
2030 | 2042 | /* first search (depth 0) */ |
|
2031 | 2043 | { size_t offsetFound = 99999999; |
|
2032 | 2044 | size_t const ml2 = searchMax(ctx, ip, iend, &offsetFound, maxSearches, mls); |
|
2033 | 2045 | if (ml2 > matchLength) |
|
2034 | 2046 | matchLength = ml2, start = ip, offset=offsetFound; |
|
2035 | 2047 | } |
|
2036 | 2048 | |
|
2037 | 2049 | if (matchLength < EQUAL_READ32) { |
|
2038 | 2050 | ip += ((ip-anchor) >> g_searchStrength) + 1; /* jump faster over incompressible sections */ |
|
2039 | 2051 | continue; |
|
2040 | 2052 | } |
|
2041 | 2053 | |
|
2042 | 2054 | /* let's try to find a better solution */ |
|
2043 | 2055 | if (depth>=1) |
|
2044 | 2056 | while (ip<ilimit) { |
|
2045 | 2057 | ip ++; |
|
2046 | 2058 | current++; |
|
2047 | 2059 | /* check repCode */ |
|
2048 | 2060 | if (offset) { |
|
2049 | 2061 | const U32 repIndex = (U32)(current - offset_1); |
|
2050 | 2062 | const BYTE* const repBase = repIndex < dictLimit ? dictBase : base; |
|
2051 | 2063 | const BYTE* const repMatch = repBase + repIndex; |
|
2052 | 2064 | if (((U32)((dictLimit-1) - repIndex) >= 3) & (repIndex > lowestIndex)) /* intentional overflow */ |
|
2053 | 2065 | if (MEM_read32(ip) == MEM_read32(repMatch)) { |
|
2054 | 2066 | /* repcode detected */ |
|
2055 | 2067 | const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend; |
|
2056 | 2068 | size_t const repLength = ZSTD_count_2segments(ip+EQUAL_READ32, repMatch+EQUAL_READ32, iend, repEnd, prefixStart) + EQUAL_READ32; |
|
2057 | 2069 | int const gain2 = (int)(repLength * 3); |
|
2058 | 2070 | int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)offset+1) + 1); |
|
2059 | 2071 | if ((repLength >= EQUAL_READ32) && (gain2 > gain1)) |
|
2060 | 2072 | matchLength = repLength, offset = 0, start = ip; |
|
2061 | 2073 | } } |
|
2062 | 2074 | |
|
2063 | 2075 | /* search match, depth 1 */ |
|
2064 | 2076 | { size_t offset2=99999999; |
|
2065 | 2077 | size_t const ml2 = searchMax(ctx, ip, iend, &offset2, maxSearches, mls); |
|
2066 | 2078 | int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1)); /* raw approx */ |
|
2067 | 2079 | int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 4); |
|
2068 | 2080 | if ((ml2 >= EQUAL_READ32) && (gain2 > gain1)) { |
|
2069 | 2081 | matchLength = ml2, offset = offset2, start = ip; |
|
2070 | 2082 | continue; /* search a better one */ |
|
2071 | 2083 | } } |
|
2072 | 2084 | |
|
2073 | 2085 | /* let's find an even better one */ |
|
2074 | 2086 | if ((depth==2) && (ip<ilimit)) { |
|
2075 | 2087 | ip ++; |
|
2076 | 2088 | current++; |
|
2077 | 2089 | /* check repCode */ |
|
2078 | 2090 | if (offset) { |
|
2079 | 2091 | const U32 repIndex = (U32)(current - offset_1); |
|
2080 | 2092 | const BYTE* const repBase = repIndex < dictLimit ? dictBase : base; |
|
2081 | 2093 | const BYTE* const repMatch = repBase + repIndex; |
|
2082 | 2094 | if (((U32)((dictLimit-1) - repIndex) >= 3) & (repIndex > lowestIndex)) /* intentional overflow */ |
|
2083 | 2095 | if (MEM_read32(ip) == MEM_read32(repMatch)) { |
|
2084 | 2096 | /* repcode detected */ |
|
2085 | 2097 | const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend; |
|
2086 | 2098 | size_t repLength = ZSTD_count_2segments(ip+EQUAL_READ32, repMatch+EQUAL_READ32, iend, repEnd, prefixStart) + EQUAL_READ32; |
|
2087 | 2099 | int gain2 = (int)(repLength * 4); |
|
2088 | 2100 | int gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 1); |
|
2089 | 2101 | if ((repLength >= EQUAL_READ32) && (gain2 > gain1)) |
|
2090 | 2102 | matchLength = repLength, offset = 0, start = ip; |
|
2091 | 2103 | } } |
|
2092 | 2104 | |
|
2093 | 2105 | /* search match, depth 2 */ |
|
2094 | 2106 | { size_t offset2=99999999; |
|
2095 | 2107 | size_t const ml2 = searchMax(ctx, ip, iend, &offset2, maxSearches, mls); |
|
2096 | 2108 | int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1)); /* raw approx */ |
|
2097 | 2109 | int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 7); |
|
2098 | 2110 | if ((ml2 >= EQUAL_READ32) && (gain2 > gain1)) { |
|
2099 | 2111 | matchLength = ml2, offset = offset2, start = ip; |
|
2100 | 2112 | continue; |
|
2101 | 2113 | } } } |
|
2102 | 2114 | break; /* nothing found : store previous solution */ |
|
2103 | 2115 | } |
|
2104 | 2116 | |
|
2105 | 2117 | /* catch up */ |
|
2106 | 2118 | if (offset) { |
|
2107 | 2119 | U32 const matchIndex = (U32)((start-base) - (offset - ZSTD_REP_MOVE)); |
|
2108 | 2120 | const BYTE* match = (matchIndex < dictLimit) ? dictBase + matchIndex : base + matchIndex; |
|
2109 | 2121 | const BYTE* const mStart = (matchIndex < dictLimit) ? dictStart : prefixStart; |
|
2110 | 2122 | while ((start>anchor) && (match>mStart) && (start[-1] == match[-1])) { start--; match--; matchLength++; } /* catch up */ |
|
2111 | 2123 | offset_2 = offset_1; offset_1 = (U32)(offset - ZSTD_REP_MOVE); |
|
2112 | 2124 | } |
|
2113 | 2125 | |
|
2114 | 2126 | /* store sequence */ |
|
2115 | 2127 | _storeSequence: |
|
2116 | 2128 | { size_t const litLength = start - anchor; |
|
2117 | 2129 | ZSTD_storeSeq(seqStorePtr, litLength, anchor, (U32)offset, matchLength-MINMATCH); |
|
2118 | 2130 | anchor = ip = start + matchLength; |
|
2119 | 2131 | } |
|
2120 | 2132 | |
|
2121 | 2133 | /* check immediate repcode */ |
|
2122 | 2134 | while (ip <= ilimit) { |
|
2123 | 2135 | const U32 repIndex = (U32)((ip-base) - offset_2); |
|
2124 | 2136 | const BYTE* const repBase = repIndex < dictLimit ? dictBase : base; |
|
2125 | 2137 | const BYTE* const repMatch = repBase + repIndex; |
|
2126 | 2138 | if (((U32)((dictLimit-1) - repIndex) >= 3) & (repIndex > lowestIndex)) /* intentional overflow */ |
|
2127 | 2139 | if (MEM_read32(ip) == MEM_read32(repMatch)) { |
|
2128 | 2140 | /* repcode detected we should take it */ |
|
2129 | 2141 | const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend; |
|
2130 | 2142 | matchLength = ZSTD_count_2segments(ip+EQUAL_READ32, repMatch+EQUAL_READ32, iend, repEnd, prefixStart) + EQUAL_READ32; |
|
2131 | 2143 | offset = offset_2; offset_2 = offset_1; offset_1 = (U32)offset; /* swap offset history */ |
|
2132 | 2144 | ZSTD_storeSeq(seqStorePtr, 0, anchor, 0, matchLength-MINMATCH); |
|
2133 | 2145 | ip += matchLength; |
|
2134 | 2146 | anchor = ip; |
|
2135 | 2147 | continue; /* faster when present ... (?) */ |
|
2136 | 2148 | } |
|
2137 | 2149 | break; |
|
2138 | 2150 | } } |
|
2139 | 2151 | |
|
2140 | 2152 | /* Save reps for next block */ |
|
2141 | 2153 | ctx->savedRep[0] = offset_1; ctx->savedRep[1] = offset_2; |
|
2142 | 2154 | |
|
2143 | 2155 | /* Last Literals */ |
|
2144 | 2156 | { size_t const lastLLSize = iend - anchor; |
|
2145 | 2157 | memcpy(seqStorePtr->lit, anchor, lastLLSize); |
|
2146 | 2158 | seqStorePtr->lit += lastLLSize; |
|
2147 | 2159 | } |
|
2148 | 2160 | } |
|
2149 | 2161 | |
|
2150 | 2162 | |
|
2151 | 2163 | void ZSTD_compressBlock_greedy_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize) |
|
2152 | 2164 | { |
|
2153 | 2165 | ZSTD_compressBlock_lazy_extDict_generic(ctx, src, srcSize, 0, 0); |
|
2154 | 2166 | } |
|
2155 | 2167 | |
|
2156 | 2168 | static void ZSTD_compressBlock_lazy_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize) |
|
2157 | 2169 | { |
|
2158 | 2170 | ZSTD_compressBlock_lazy_extDict_generic(ctx, src, srcSize, 0, 1); |
|
2159 | 2171 | } |
|
2160 | 2172 | |
|
2161 | 2173 | static void ZSTD_compressBlock_lazy2_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize) |
|
2162 | 2174 | { |
|
2163 | 2175 | ZSTD_compressBlock_lazy_extDict_generic(ctx, src, srcSize, 0, 2); |
|
2164 | 2176 | } |
|
2165 | 2177 | |
|
2166 | 2178 | static void ZSTD_compressBlock_btlazy2_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize) |
|
2167 | 2179 | { |
|
2168 | 2180 | ZSTD_compressBlock_lazy_extDict_generic(ctx, src, srcSize, 1, 2); |
|
2169 | 2181 | } |
|
2170 | 2182 | |
|
2171 | 2183 | |
|
2172 | 2184 | /* The optimal parser */ |
|
2173 | 2185 | #include "zstd_opt.h" |
|
2174 | 2186 | |
|
2175 | 2187 | static void ZSTD_compressBlock_btopt(ZSTD_CCtx* ctx, const void* src, size_t srcSize) |
|
2176 | 2188 | { |
|
2177 | 2189 | #ifdef ZSTD_OPT_H_91842398743 |
|
2178 | 2190 | ZSTD_compressBlock_opt_generic(ctx, src, srcSize, 0); |
|
2179 | 2191 | #else |
|
2180 | 2192 | (void)ctx; (void)src; (void)srcSize; |
|
2181 | 2193 | return; |
|
2182 | 2194 | #endif |
|
2183 | 2195 | } |
|
2184 | 2196 | |
|
2185 | 2197 | static void ZSTD_compressBlock_btopt2(ZSTD_CCtx* ctx, const void* src, size_t srcSize) |
|
2186 | 2198 | { |
|
2187 | 2199 | #ifdef ZSTD_OPT_H_91842398743 |
|
2188 | 2200 | ZSTD_compressBlock_opt_generic(ctx, src, srcSize, 1); |
|
2189 | 2201 | #else |
|
2190 | 2202 | (void)ctx; (void)src; (void)srcSize; |
|
2191 | 2203 | return; |
|
2192 | 2204 | #endif |
|
2193 | 2205 | } |
|
2194 | 2206 | |
|
2195 | 2207 | static void ZSTD_compressBlock_btopt_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize) |
|
2196 | 2208 | { |
|
2197 | 2209 | #ifdef ZSTD_OPT_H_91842398743 |
|
2198 | 2210 | ZSTD_compressBlock_opt_extDict_generic(ctx, src, srcSize, 0); |
|
2199 | 2211 | #else |
|
2200 | 2212 | (void)ctx; (void)src; (void)srcSize; |
|
2201 | 2213 | return; |
|
2202 | 2214 | #endif |
|
2203 | 2215 | } |
|
2204 | 2216 | |
|
2205 | 2217 | static void ZSTD_compressBlock_btopt2_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize) |
|
2206 | 2218 | { |
|
2207 | 2219 | #ifdef ZSTD_OPT_H_91842398743 |
|
2208 | 2220 | ZSTD_compressBlock_opt_extDict_generic(ctx, src, srcSize, 1); |
|
2209 | 2221 | #else |
|
2210 | 2222 | (void)ctx; (void)src; (void)srcSize; |
|
2211 | 2223 | return; |
|
2212 | 2224 | #endif |
|
2213 | 2225 | } |
|
2214 | 2226 | |
|
2215 | 2227 | |
|
2216 | 2228 | typedef void (*ZSTD_blockCompressor) (ZSTD_CCtx* ctx, const void* src, size_t srcSize); |
|
2217 | 2229 | |
|
2218 | 2230 | static ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, int extDict) |
|
2219 | 2231 | { |
|
2220 | 2232 | static const ZSTD_blockCompressor blockCompressor[2][8] = { |
|
2221 | 2233 | { ZSTD_compressBlock_fast, ZSTD_compressBlock_doubleFast, ZSTD_compressBlock_greedy, ZSTD_compressBlock_lazy, ZSTD_compressBlock_lazy2, ZSTD_compressBlock_btlazy2, ZSTD_compressBlock_btopt, ZSTD_compressBlock_btopt2 }, |
|
2222 | 2234 | { ZSTD_compressBlock_fast_extDict, ZSTD_compressBlock_doubleFast_extDict, ZSTD_compressBlock_greedy_extDict, ZSTD_compressBlock_lazy_extDict,ZSTD_compressBlock_lazy2_extDict, ZSTD_compressBlock_btlazy2_extDict, ZSTD_compressBlock_btopt_extDict, ZSTD_compressBlock_btopt2_extDict } |
|
2223 | 2235 | }; |
|
2224 | 2236 | |
|
2225 | 2237 | return blockCompressor[extDict][(U32)strat]; |
|
2226 | 2238 | } |
|
2227 | 2239 | |
|
2228 | 2240 | |
|
2229 | 2241 | static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, void* dst, size_t dstCapacity, const void* src, size_t srcSize) |
|
2230 | 2242 | { |
|
2231 | 2243 | ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->params.cParams.strategy, zc->lowLimit < zc->dictLimit); |
|
2232 | 2244 | const BYTE* const base = zc->base; |
|
2233 | 2245 | const BYTE* const istart = (const BYTE*)src; |
|
2234 | 2246 | const U32 current = (U32)(istart-base); |
|
2235 | 2247 | if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) return 0; /* don't even attempt compression below a certain srcSize */ |
|
2236 | 2248 | ZSTD_resetSeqStore(&(zc->seqStore)); |
|
2237 | 2249 | if (current > zc->nextToUpdate + 384) |
|
2238 | 2250 | zc->nextToUpdate = current - MIN(192, (U32)(current - zc->nextToUpdate - 384)); /* update tree not updated after finding very long rep matches */ |
|
2239 | 2251 | blockCompressor(zc, src, srcSize); |
|
2240 | 2252 | return ZSTD_compressSequences(zc, dst, dstCapacity, srcSize); |
|
2241 | 2253 | } |
|
2242 | 2254 | |
|
2243 | 2255 | |
|
2244 | 2256 | /*! ZSTD_compress_generic() : |
|
2245 | 2257 | * Compress a chunk of data into one or multiple blocks. |
|
2246 | 2258 | * All blocks will be terminated, all input will be consumed. |
|
2247 | 2259 | * Function will issue an error if there is not enough `dstCapacity` to hold the compressed content. |
|
2248 | 2260 | * Frame is supposed already started (header already produced) |
|
2249 | 2261 | * @return : compressed size, or an error code |
|
2250 | 2262 | */ |
|
2251 | 2263 | static size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, |
|
2252 | 2264 | void* dst, size_t dstCapacity, |
|
2253 | 2265 | const void* src, size_t srcSize, |
|
2254 | 2266 | U32 lastFrameChunk) |
|
2255 | 2267 | { |
|
2256 | 2268 | size_t blockSize = cctx->blockSize; |
|
2257 | 2269 | size_t remaining = srcSize; |
|
2258 | 2270 | const BYTE* ip = (const BYTE*)src; |
|
2259 | 2271 | BYTE* const ostart = (BYTE*)dst; |
|
2260 | 2272 | BYTE* op = ostart; |
|
2261 | 2273 | U32 const maxDist = 1 << cctx->params.cParams.windowLog; |
|
2262 | 2274 | |
|
2263 | 2275 | if (cctx->params.fParams.checksumFlag && srcSize) |
|
2264 | 2276 | XXH64_update(&cctx->xxhState, src, srcSize); |
|
2265 | 2277 | |
|
2266 | 2278 | while (remaining) { |
|
2267 | 2279 | U32 const lastBlock = lastFrameChunk & (blockSize >= remaining); |
|
2268 | 2280 | size_t cSize; |
|
2269 | 2281 | |
|
2270 | 2282 | if (dstCapacity < ZSTD_blockHeaderSize + MIN_CBLOCK_SIZE) return ERROR(dstSize_tooSmall); /* not enough space to store compressed block */ |
|
2271 | 2283 | if (remaining < blockSize) blockSize = remaining; |
|
2272 | 2284 | |
|
2273 | 2285 | /* preemptive overflow correction */ |
|
2274 |
if (cctx->lowLimit > ( |
|
|
2275 | U32 const btplus = (cctx->params.cParams.strategy == ZSTD_btlazy2) | (cctx->params.cParams.strategy == ZSTD_btopt) | (cctx->params.cParams.strategy == ZSTD_btopt2); | |
|
2276 | U32 const chainMask = (1 << (cctx->params.cParams.chainLog - btplus)) - 1; | |
|
2277 |
U32 const |
|
|
2278 | U32 const newLowLimit = (cctx->lowLimit & chainMask) + (1 << supLog); /* preserve position % chainSize, ensure current-repcode doesn't underflow */ | |
|
2279 | U32 const correction = cctx->lowLimit - newLowLimit; | |
|
2286 | if (cctx->lowLimit > (2U<<30)) { | |
|
2287 | U32 const cycleMask = (1 << ZSTD_cycleLog(cctx->params.cParams.hashLog, cctx->params.cParams.strategy)) - 1; | |
|
2288 | U32 const current = (U32)(ip - cctx->base); | |
|
2289 | U32 const newCurrent = (current & cycleMask) + (1 << cctx->params.cParams.windowLog); | |
|
2290 | U32 const correction = current - newCurrent; | |
|
2291 | ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX_64 <= 30); | |
|
2280 | 2292 | ZSTD_reduceIndex(cctx, correction); |
|
2281 | 2293 | cctx->base += correction; |
|
2282 | 2294 | cctx->dictBase += correction; |
|
2283 |
cctx->lowLimit = |
|
|
2295 | cctx->lowLimit -= correction; | |
|
2284 | 2296 | cctx->dictLimit -= correction; |
|
2285 | 2297 | if (cctx->nextToUpdate < correction) cctx->nextToUpdate = 0; |
|
2286 | 2298 | else cctx->nextToUpdate -= correction; |
|
2287 | 2299 | } |
|
2288 | 2300 | |
|
2289 | 2301 | if ((U32)(ip+blockSize - cctx->base) > cctx->loadedDictEnd + maxDist) { |
|
2290 | 2302 | /* enforce maxDist */ |
|
2291 | 2303 | U32 const newLowLimit = (U32)(ip+blockSize - cctx->base) - maxDist; |
|
2292 | 2304 | if (cctx->lowLimit < newLowLimit) cctx->lowLimit = newLowLimit; |
|
2293 | 2305 | if (cctx->dictLimit < cctx->lowLimit) cctx->dictLimit = cctx->lowLimit; |
|
2294 | 2306 | } |
|
2295 | 2307 | |
|
2296 | 2308 | cSize = ZSTD_compressBlock_internal(cctx, op+ZSTD_blockHeaderSize, dstCapacity-ZSTD_blockHeaderSize, ip, blockSize); |
|
2297 | 2309 | if (ZSTD_isError(cSize)) return cSize; |
|
2298 | 2310 | |
|
2299 | 2311 | if (cSize == 0) { /* block is not compressible */ |
|
2300 | 2312 | U32 const cBlockHeader24 = lastBlock + (((U32)bt_raw)<<1) + (U32)(blockSize << 3); |
|
2301 | 2313 | if (blockSize + ZSTD_blockHeaderSize > dstCapacity) return ERROR(dstSize_tooSmall); |
|
2302 | 2314 | MEM_writeLE32(op, cBlockHeader24); /* no pb, 4th byte will be overwritten */ |
|
2303 | 2315 | memcpy(op + ZSTD_blockHeaderSize, ip, blockSize); |
|
2304 | 2316 | cSize = ZSTD_blockHeaderSize+blockSize; |
|
2305 | 2317 | } else { |
|
2306 | 2318 | U32 const cBlockHeader24 = lastBlock + (((U32)bt_compressed)<<1) + (U32)(cSize << 3); |
|
2307 | 2319 | MEM_writeLE24(op, cBlockHeader24); |
|
2308 | 2320 | cSize += ZSTD_blockHeaderSize; |
|
2309 | 2321 | } |
|
2310 | 2322 | |
|
2311 | 2323 | remaining -= blockSize; |
|
2312 | 2324 | dstCapacity -= cSize; |
|
2313 | 2325 | ip += blockSize; |
|
2314 | 2326 | op += cSize; |
|
2315 | 2327 | } |
|
2316 | 2328 | |
|
2317 | 2329 | if (lastFrameChunk && (op>ostart)) cctx->stage = ZSTDcs_ending; |
|
2318 | 2330 | return op-ostart; |
|
2319 | 2331 | } |
|
2320 | 2332 | |
|
2321 | 2333 | |
|
2322 | 2334 | static size_t ZSTD_writeFrameHeader(void* dst, size_t dstCapacity, |
|
2323 | 2335 | ZSTD_parameters params, U64 pledgedSrcSize, U32 dictID) |
|
2324 | 2336 | { BYTE* const op = (BYTE*)dst; |
|
2325 | 2337 | U32 const dictIDSizeCode = (dictID>0) + (dictID>=256) + (dictID>=65536); /* 0-3 */ |
|
2326 | 2338 | U32 const checksumFlag = params.fParams.checksumFlag>0; |
|
2327 | 2339 | U32 const windowSize = 1U << params.cParams.windowLog; |
|
2328 | 2340 | U32 const singleSegment = params.fParams.contentSizeFlag && (windowSize > (pledgedSrcSize-1)); |
|
2329 | 2341 | BYTE const windowLogByte = (BYTE)((params.cParams.windowLog - ZSTD_WINDOWLOG_ABSOLUTEMIN) << 3); |
|
2330 | 2342 | U32 const fcsCode = params.fParams.contentSizeFlag ? |
|
2331 | 2343 | (pledgedSrcSize>=256) + (pledgedSrcSize>=65536+256) + (pledgedSrcSize>=0xFFFFFFFFU) : /* 0-3 */ |
|
2332 | 2344 | 0; |
|
2333 | 2345 | BYTE const frameHeaderDecriptionByte = (BYTE)(dictIDSizeCode + (checksumFlag<<2) + (singleSegment<<5) + (fcsCode<<6) ); |
|
2334 | 2346 | size_t pos; |
|
2335 | 2347 | |
|
2336 | 2348 | if (dstCapacity < ZSTD_frameHeaderSize_max) return ERROR(dstSize_tooSmall); |
|
2337 | 2349 | |
|
2338 | 2350 | MEM_writeLE32(dst, ZSTD_MAGICNUMBER); |
|
2339 | 2351 | op[4] = frameHeaderDecriptionByte; pos=5; |
|
2340 | 2352 | if (!singleSegment) op[pos++] = windowLogByte; |
|
2341 | 2353 | switch(dictIDSizeCode) |
|
2342 | 2354 | { |
|
2343 | 2355 | default: /* impossible */ |
|
2344 | 2356 | case 0 : break; |
|
2345 | 2357 | case 1 : op[pos] = (BYTE)(dictID); pos++; break; |
|
2346 | 2358 | case 2 : MEM_writeLE16(op+pos, (U16)dictID); pos+=2; break; |
|
2347 | 2359 | case 3 : MEM_writeLE32(op+pos, dictID); pos+=4; break; |
|
2348 | 2360 | } |
|
2349 | 2361 | switch(fcsCode) |
|
2350 | 2362 | { |
|
2351 | 2363 | default: /* impossible */ |
|
2352 | 2364 | case 0 : if (singleSegment) op[pos++] = (BYTE)(pledgedSrcSize); break; |
|
2353 | 2365 | case 1 : MEM_writeLE16(op+pos, (U16)(pledgedSrcSize-256)); pos+=2; break; |
|
2354 | 2366 | case 2 : MEM_writeLE32(op+pos, (U32)(pledgedSrcSize)); pos+=4; break; |
|
2355 | 2367 | case 3 : MEM_writeLE64(op+pos, (U64)(pledgedSrcSize)); pos+=8; break; |
|
2356 | 2368 | } |
|
2357 | 2369 | return pos; |
|
2358 | 2370 | } |
|
2359 | 2371 | |
|
2360 | 2372 | |
|
2361 | 2373 | static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx, |
|
2362 | 2374 | void* dst, size_t dstCapacity, |
|
2363 | 2375 | const void* src, size_t srcSize, |
|
2364 | 2376 | U32 frame, U32 lastFrameChunk) |
|
2365 | 2377 | { |
|
2366 | 2378 | const BYTE* const ip = (const BYTE*) src; |
|
2367 | 2379 | size_t fhSize = 0; |
|
2368 | 2380 | |
|
2369 | 2381 | if (cctx->stage==ZSTDcs_created) return ERROR(stage_wrong); /* missing init (ZSTD_compressBegin) */ |
|
2370 | 2382 | |
|
2371 | 2383 | if (frame && (cctx->stage==ZSTDcs_init)) { |
|
2372 | 2384 | fhSize = ZSTD_writeFrameHeader(dst, dstCapacity, cctx->params, cctx->frameContentSize, cctx->dictID); |
|
2373 | 2385 | if (ZSTD_isError(fhSize)) return fhSize; |
|
2374 | 2386 | dstCapacity -= fhSize; |
|
2375 | 2387 | dst = (char*)dst + fhSize; |
|
2376 | 2388 | cctx->stage = ZSTDcs_ongoing; |
|
2377 | 2389 | } |
|
2378 | 2390 | |
|
2379 | 2391 | /* Check if blocks follow each other */ |
|
2380 | 2392 | if (src != cctx->nextSrc) { |
|
2381 | 2393 | /* not contiguous */ |
|
2382 | 2394 | ptrdiff_t const delta = cctx->nextSrc - ip; |
|
2383 | 2395 | cctx->lowLimit = cctx->dictLimit; |
|
2384 | 2396 | cctx->dictLimit = (U32)(cctx->nextSrc - cctx->base); |
|
2385 | 2397 | cctx->dictBase = cctx->base; |
|
2386 | 2398 | cctx->base -= delta; |
|
2387 | 2399 | cctx->nextToUpdate = cctx->dictLimit; |
|
2388 | 2400 | if (cctx->dictLimit - cctx->lowLimit < HASH_READ_SIZE) cctx->lowLimit = cctx->dictLimit; /* too small extDict */ |
|
2389 | 2401 | } |
|
2390 | 2402 | |
|
2391 | 2403 | /* if input and dictionary overlap : reduce dictionary (area presumed modified by input) */ |
|
2392 | 2404 | if ((ip+srcSize > cctx->dictBase + cctx->lowLimit) & (ip < cctx->dictBase + cctx->dictLimit)) { |
|
2393 | 2405 | ptrdiff_t const highInputIdx = (ip + srcSize) - cctx->dictBase; |
|
2394 | 2406 | U32 const lowLimitMax = (highInputIdx > (ptrdiff_t)cctx->dictLimit) ? cctx->dictLimit : (U32)highInputIdx; |
|
2395 | 2407 | cctx->lowLimit = lowLimitMax; |
|
2396 | 2408 | } |
|
2397 | 2409 | |
|
2398 | 2410 | cctx->nextSrc = ip + srcSize; |
|
2399 | 2411 | |
|
2400 | 2412 | { size_t const cSize = frame ? |
|
2401 | 2413 | ZSTD_compress_generic (cctx, dst, dstCapacity, src, srcSize, lastFrameChunk) : |
|
2402 | 2414 | ZSTD_compressBlock_internal (cctx, dst, dstCapacity, src, srcSize); |
|
2403 | 2415 | if (ZSTD_isError(cSize)) return cSize; |
|
2404 | 2416 | return cSize + fhSize; |
|
2405 | 2417 | } |
|
2406 | 2418 | } |
|
2407 | 2419 | |
|
2408 | 2420 | |
|
2409 | 2421 | size_t ZSTD_compressContinue (ZSTD_CCtx* cctx, |
|
2410 | 2422 | void* dst, size_t dstCapacity, |
|
2411 | 2423 | const void* src, size_t srcSize) |
|
2412 | 2424 | { |
|
2413 | 2425 | return ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 1, 0); |
|
2414 | 2426 | } |
|
2415 | 2427 | |
|
2416 | 2428 | |
|
2417 | 2429 | size_t ZSTD_getBlockSizeMax(ZSTD_CCtx* cctx) |
|
2418 | 2430 | { |
|
2419 | 2431 | return MIN (ZSTD_BLOCKSIZE_ABSOLUTEMAX, 1 << cctx->params.cParams.windowLog); |
|
2420 | 2432 | } |
|
2421 | 2433 | |
|
2422 | 2434 | size_t ZSTD_compressBlock(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) |
|
2423 | 2435 | { |
|
2424 | 2436 | size_t const blockSizeMax = ZSTD_getBlockSizeMax(cctx); |
|
2425 | 2437 | if (srcSize > blockSizeMax) return ERROR(srcSize_wrong); |
|
2426 | 2438 | return ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 0, 0); |
|
2427 | 2439 | } |
|
2428 | 2440 | |
|
2429 | 2441 | |
|
2430 | 2442 | static size_t ZSTD_loadDictionaryContent(ZSTD_CCtx* zc, const void* src, size_t srcSize) |
|
2431 | 2443 | { |
|
2432 | 2444 | const BYTE* const ip = (const BYTE*) src; |
|
2433 | 2445 | const BYTE* const iend = ip + srcSize; |
|
2434 | 2446 | |
|
2435 | 2447 | /* input becomes current prefix */ |
|
2436 | 2448 | zc->lowLimit = zc->dictLimit; |
|
2437 | 2449 | zc->dictLimit = (U32)(zc->nextSrc - zc->base); |
|
2438 | 2450 | zc->dictBase = zc->base; |
|
2439 | 2451 | zc->base += ip - zc->nextSrc; |
|
2440 | 2452 | zc->nextToUpdate = zc->dictLimit; |
|
2441 | 2453 | zc->loadedDictEnd = (U32)(iend - zc->base); |
|
2442 | 2454 | |
|
2443 | 2455 | zc->nextSrc = iend; |
|
2444 | 2456 | if (srcSize <= HASH_READ_SIZE) return 0; |
|
2445 | 2457 | |
|
2446 | 2458 | switch(zc->params.cParams.strategy) |
|
2447 | 2459 | { |
|
2448 | 2460 | case ZSTD_fast: |
|
2449 | 2461 | ZSTD_fillHashTable (zc, iend, zc->params.cParams.searchLength); |
|
2450 | 2462 | break; |
|
2451 | 2463 | |
|
2452 | 2464 | case ZSTD_dfast: |
|
2453 | 2465 | ZSTD_fillDoubleHashTable (zc, iend, zc->params.cParams.searchLength); |
|
2454 | 2466 | break; |
|
2455 | 2467 | |
|
2456 | 2468 | case ZSTD_greedy: |
|
2457 | 2469 | case ZSTD_lazy: |
|
2458 | 2470 | case ZSTD_lazy2: |
|
2459 | 2471 | ZSTD_insertAndFindFirstIndex (zc, iend-HASH_READ_SIZE, zc->params.cParams.searchLength); |
|
2460 | 2472 | break; |
|
2461 | 2473 | |
|
2462 | 2474 | case ZSTD_btlazy2: |
|
2463 | 2475 | case ZSTD_btopt: |
|
2464 | 2476 | case ZSTD_btopt2: |
|
2465 | 2477 | ZSTD_updateTree(zc, iend-HASH_READ_SIZE, iend, 1 << zc->params.cParams.searchLog, zc->params.cParams.searchLength); |
|
2466 | 2478 | break; |
|
2467 | 2479 | |
|
2468 | 2480 | default: |
|
2469 | 2481 | return ERROR(GENERIC); /* strategy doesn't exist; impossible */ |
|
2470 | 2482 | } |
|
2471 | 2483 | |
|
2472 | 2484 | zc->nextToUpdate = zc->loadedDictEnd; |
|
2473 | 2485 | return 0; |
|
2474 | 2486 | } |
|
2475 | 2487 | |
|
2476 | 2488 | |
|
2477 | 2489 | /* Dictionaries that assign zero probability to symbols that show up causes problems |
|
2478 | 2490 | when FSE encoding. Refuse dictionaries that assign zero probability to symbols |
|
2479 | 2491 | that we may encounter during compression. |
|
2480 | 2492 | NOTE: This behavior is not standard and could be improved in the future. */ |
|
2481 | 2493 | static size_t ZSTD_checkDictNCount(short* normalizedCounter, unsigned dictMaxSymbolValue, unsigned maxSymbolValue) { |
|
2482 | 2494 | U32 s; |
|
2483 | 2495 | if (dictMaxSymbolValue < maxSymbolValue) return ERROR(dictionary_corrupted); |
|
2484 | 2496 | for (s = 0; s <= maxSymbolValue; ++s) { |
|
2485 | 2497 | if (normalizedCounter[s] == 0) return ERROR(dictionary_corrupted); |
|
2486 | 2498 | } |
|
2487 | 2499 | return 0; |
|
2488 | 2500 | } |
|
2489 | 2501 | |
|
2490 | 2502 | |
|
2491 | 2503 | /* Dictionary format : |
|
2492 | 2504 | Magic == ZSTD_DICT_MAGIC (4 bytes) |
|
2493 | 2505 | HUF_writeCTable(256) |
|
2494 | 2506 | FSE_writeNCount(off) |
|
2495 | 2507 | FSE_writeNCount(ml) |
|
2496 | 2508 | FSE_writeNCount(ll) |
|
2497 | 2509 | RepOffsets |
|
2498 | 2510 | Dictionary content |
|
2499 | 2511 | */ |
|
2500 | 2512 | /*! ZSTD_loadDictEntropyStats() : |
|
2501 | 2513 | @return : size read from dictionary |
|
2502 | 2514 | note : magic number supposed already checked */ |
|
2503 | 2515 | static size_t ZSTD_loadDictEntropyStats(ZSTD_CCtx* cctx, const void* dict, size_t dictSize) |
|
2504 | 2516 | { |
|
2505 | 2517 | const BYTE* dictPtr = (const BYTE*)dict; |
|
2506 | 2518 | const BYTE* const dictEnd = dictPtr + dictSize; |
|
2507 | 2519 | short offcodeNCount[MaxOff+1]; |
|
2508 | 2520 | unsigned offcodeMaxValue = MaxOff; |
|
2521 | BYTE scratchBuffer[1<<MAX(MLFSELog,LLFSELog)]; | |
|
2509 | 2522 | |
|
2510 | 2523 | { size_t const hufHeaderSize = HUF_readCTable(cctx->hufTable, 255, dict, dictSize); |
|
2511 | 2524 | if (HUF_isError(hufHeaderSize)) return ERROR(dictionary_corrupted); |
|
2512 | 2525 | dictPtr += hufHeaderSize; |
|
2513 | 2526 | } |
|
2514 | 2527 | |
|
2515 | 2528 | { unsigned offcodeLog; |
|
2516 | 2529 | size_t const offcodeHeaderSize = FSE_readNCount(offcodeNCount, &offcodeMaxValue, &offcodeLog, dictPtr, dictEnd-dictPtr); |
|
2517 | 2530 | if (FSE_isError(offcodeHeaderSize)) return ERROR(dictionary_corrupted); |
|
2518 | 2531 | if (offcodeLog > OffFSELog) return ERROR(dictionary_corrupted); |
|
2519 | 2532 | /* Defer checking offcodeMaxValue because we need to know the size of the dictionary content */ |
|
2520 | CHECK_E (FSE_buildCTable(cctx->offcodeCTable, offcodeNCount, offcodeMaxValue, offcodeLog), dictionary_corrupted); | |
|
2533 | CHECK_E (FSE_buildCTable_wksp(cctx->offcodeCTable, offcodeNCount, offcodeMaxValue, offcodeLog, scratchBuffer, sizeof(scratchBuffer)), dictionary_corrupted); | |
|
2521 | 2534 | dictPtr += offcodeHeaderSize; |
|
2522 | 2535 | } |
|
2523 | 2536 | |
|
2524 | 2537 | { short matchlengthNCount[MaxML+1]; |
|
2525 | 2538 | unsigned matchlengthMaxValue = MaxML, matchlengthLog; |
|
2526 | 2539 | size_t const matchlengthHeaderSize = FSE_readNCount(matchlengthNCount, &matchlengthMaxValue, &matchlengthLog, dictPtr, dictEnd-dictPtr); |
|
2527 | 2540 | if (FSE_isError(matchlengthHeaderSize)) return ERROR(dictionary_corrupted); |
|
2528 | 2541 | if (matchlengthLog > MLFSELog) return ERROR(dictionary_corrupted); |
|
2529 | 2542 | /* Every match length code must have non-zero probability */ |
|
2530 | 2543 | CHECK_F (ZSTD_checkDictNCount(matchlengthNCount, matchlengthMaxValue, MaxML)); |
|
2531 | CHECK_E (FSE_buildCTable(cctx->matchlengthCTable, matchlengthNCount, matchlengthMaxValue, matchlengthLog), dictionary_corrupted); | |
|
2544 | CHECK_E (FSE_buildCTable_wksp(cctx->matchlengthCTable, matchlengthNCount, matchlengthMaxValue, matchlengthLog, scratchBuffer, sizeof(scratchBuffer)), dictionary_corrupted); | |
|
2532 | 2545 | dictPtr += matchlengthHeaderSize; |
|
2533 | 2546 | } |
|
2534 | 2547 | |
|
2535 | 2548 | { short litlengthNCount[MaxLL+1]; |
|
2536 | 2549 | unsigned litlengthMaxValue = MaxLL, litlengthLog; |
|
2537 | 2550 | size_t const litlengthHeaderSize = FSE_readNCount(litlengthNCount, &litlengthMaxValue, &litlengthLog, dictPtr, dictEnd-dictPtr); |
|
2538 | 2551 | if (FSE_isError(litlengthHeaderSize)) return ERROR(dictionary_corrupted); |
|
2539 | 2552 | if (litlengthLog > LLFSELog) return ERROR(dictionary_corrupted); |
|
2540 | 2553 | /* Every literal length code must have non-zero probability */ |
|
2541 | 2554 | CHECK_F (ZSTD_checkDictNCount(litlengthNCount, litlengthMaxValue, MaxLL)); |
|
2542 | CHECK_E(FSE_buildCTable(cctx->litlengthCTable, litlengthNCount, litlengthMaxValue, litlengthLog), dictionary_corrupted); | |
|
2555 | CHECK_E(FSE_buildCTable_wksp(cctx->litlengthCTable, litlengthNCount, litlengthMaxValue, litlengthLog, scratchBuffer, sizeof(scratchBuffer)), dictionary_corrupted); | |
|
2543 | 2556 | dictPtr += litlengthHeaderSize; |
|
2544 | 2557 | } |
|
2545 | 2558 | |
|
2546 | 2559 | if (dictPtr+12 > dictEnd) return ERROR(dictionary_corrupted); |
|
2547 | 2560 | cctx->rep[0] = MEM_readLE32(dictPtr+0); if (cctx->rep[0] >= dictSize) return ERROR(dictionary_corrupted); |
|
2548 | 2561 | cctx->rep[1] = MEM_readLE32(dictPtr+4); if (cctx->rep[1] >= dictSize) return ERROR(dictionary_corrupted); |
|
2549 | 2562 | cctx->rep[2] = MEM_readLE32(dictPtr+8); if (cctx->rep[2] >= dictSize) return ERROR(dictionary_corrupted); |
|
2550 | 2563 | dictPtr += 12; |
|
2551 | 2564 | |
|
2552 | 2565 | { U32 offcodeMax = MaxOff; |
|
2553 | 2566 | if ((size_t)(dictEnd - dictPtr) <= ((U32)-1) - 128 KB) { |
|
2554 | 2567 | U32 const maxOffset = (U32)(dictEnd - dictPtr) + 128 KB; /* The maximum offset that must be supported */ |
|
2555 | 2568 | /* Calculate minimum offset code required to represent maxOffset */ |
|
2556 | 2569 | offcodeMax = ZSTD_highbit32(maxOffset); |
|
2557 | 2570 | } |
|
2558 | 2571 | /* Every possible supported offset <= dictContentSize + 128 KB must be representable */ |
|
2559 | 2572 | CHECK_F (ZSTD_checkDictNCount(offcodeNCount, offcodeMaxValue, MIN(offcodeMax, MaxOff))); |
|
2560 | 2573 | } |
|
2561 | 2574 | |
|
2562 | 2575 | cctx->flagStaticTables = 1; |
|
2563 | 2576 | return dictPtr - (const BYTE*)dict; |
|
2564 | 2577 | } |
|
2565 | 2578 | |
|
2566 | 2579 | /** ZSTD_compress_insertDictionary() : |
|
2567 | 2580 | * @return : 0, or an error code */ |
|
2568 | 2581 | static size_t ZSTD_compress_insertDictionary(ZSTD_CCtx* zc, const void* dict, size_t dictSize) |
|
2569 | 2582 | { |
|
2570 | 2583 | if ((dict==NULL) || (dictSize<=8)) return 0; |
|
2571 | 2584 | |
|
2572 | 2585 | /* default : dict is pure content */ |
|
2573 | 2586 | if (MEM_readLE32(dict) != ZSTD_DICT_MAGIC) return ZSTD_loadDictionaryContent(zc, dict, dictSize); |
|
2574 | 2587 | zc->dictID = zc->params.fParams.noDictIDFlag ? 0 : MEM_readLE32((const char*)dict+4); |
|
2575 | 2588 | |
|
2576 | 2589 | /* known magic number : dict is parsed for entropy stats and content */ |
|
2577 | 2590 | { size_t const loadError = ZSTD_loadDictEntropyStats(zc, (const char*)dict+8 /* skip dictHeader */, dictSize-8); |
|
2578 | 2591 | size_t const eSize = loadError + 8; |
|
2579 | 2592 | if (ZSTD_isError(loadError)) return loadError; |
|
2580 | 2593 | return ZSTD_loadDictionaryContent(zc, (const char*)dict+eSize, dictSize-eSize); |
|
2581 | 2594 | } |
|
2582 | 2595 | } |
|
2583 | 2596 | |
|
2584 | 2597 | |
|
2585 | 2598 | /*! ZSTD_compressBegin_internal() : |
|
2586 | 2599 | * @return : 0, or an error code */ |
|
2587 | 2600 | static size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx, |
|
2588 | 2601 | const void* dict, size_t dictSize, |
|
2589 | 2602 | ZSTD_parameters params, U64 pledgedSrcSize) |
|
2590 | 2603 | { |
|
2591 | 2604 | ZSTD_compResetPolicy_e const crp = dictSize ? ZSTDcrp_fullReset : ZSTDcrp_continue; |
|
2592 | 2605 | CHECK_F(ZSTD_resetCCtx_advanced(cctx, params, pledgedSrcSize, crp)); |
|
2593 | 2606 | return ZSTD_compress_insertDictionary(cctx, dict, dictSize); |
|
2594 | 2607 | } |
|
2595 | 2608 | |
|
2596 | 2609 | |
|
2597 | 2610 | /*! ZSTD_compressBegin_advanced() : |
|
2598 | 2611 | * @return : 0, or an error code */ |
|
2599 | 2612 | size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx, |
|
2600 | 2613 | const void* dict, size_t dictSize, |
|
2601 | 2614 | ZSTD_parameters params, unsigned long long pledgedSrcSize) |
|
2602 | 2615 | { |
|
2603 | 2616 | /* compression parameters verification and optimization */ |
|
2604 | 2617 | CHECK_F(ZSTD_checkCParams(params.cParams)); |
|
2605 | 2618 | return ZSTD_compressBegin_internal(cctx, dict, dictSize, params, pledgedSrcSize); |
|
2606 | 2619 | } |
|
2607 | 2620 | |
|
2608 | 2621 | |
|
2609 | 2622 | size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel) |
|
2610 | 2623 | { |
|
2611 | 2624 | ZSTD_parameters const params = ZSTD_getParams(compressionLevel, 0, dictSize); |
|
2612 | 2625 | return ZSTD_compressBegin_internal(cctx, dict, dictSize, params, 0); |
|
2613 | 2626 | } |
|
2614 | 2627 | |
|
2615 | 2628 | |
|
2616 | 2629 | size_t ZSTD_compressBegin(ZSTD_CCtx* zc, int compressionLevel) |
|
2617 | 2630 | { |
|
2618 | 2631 | return ZSTD_compressBegin_usingDict(zc, NULL, 0, compressionLevel); |
|
2619 | 2632 | } |
|
2620 | 2633 | |
|
2621 | 2634 | |
|
2622 | 2635 | /*! ZSTD_writeEpilogue() : |
|
2623 | 2636 | * Ends a frame. |
|
2624 | 2637 | * @return : nb of bytes written into dst (or an error code) */ |
|
2625 | 2638 | static size_t ZSTD_writeEpilogue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity) |
|
2626 | 2639 | { |
|
2627 | 2640 | BYTE* const ostart = (BYTE*)dst; |
|
2628 | 2641 | BYTE* op = ostart; |
|
2629 | 2642 | size_t fhSize = 0; |
|
2630 | 2643 | |
|
2631 | 2644 | if (cctx->stage == ZSTDcs_created) return ERROR(stage_wrong); /* init missing */ |
|
2632 | 2645 | |
|
2633 | 2646 | /* special case : empty frame */ |
|
2634 | 2647 | if (cctx->stage == ZSTDcs_init) { |
|
2635 | 2648 | fhSize = ZSTD_writeFrameHeader(dst, dstCapacity, cctx->params, 0, 0); |
|
2636 | 2649 | if (ZSTD_isError(fhSize)) return fhSize; |
|
2637 | 2650 | dstCapacity -= fhSize; |
|
2638 | 2651 | op += fhSize; |
|
2639 | 2652 | cctx->stage = ZSTDcs_ongoing; |
|
2640 | 2653 | } |
|
2641 | 2654 | |
|
2642 | 2655 | if (cctx->stage != ZSTDcs_ending) { |
|
2643 | 2656 | /* write one last empty block, make it the "last" block */ |
|
2644 | 2657 | U32 const cBlockHeader24 = 1 /* last block */ + (((U32)bt_raw)<<1) + 0; |
|
2645 | 2658 | if (dstCapacity<4) return ERROR(dstSize_tooSmall); |
|
2646 | 2659 | MEM_writeLE32(op, cBlockHeader24); |
|
2647 | 2660 | op += ZSTD_blockHeaderSize; |
|
2648 | 2661 | dstCapacity -= ZSTD_blockHeaderSize; |
|
2649 | 2662 | } |
|
2650 | 2663 | |
|
2651 | 2664 | if (cctx->params.fParams.checksumFlag) { |
|
2652 | 2665 | U32 const checksum = (U32) XXH64_digest(&cctx->xxhState); |
|
2653 | 2666 | if (dstCapacity<4) return ERROR(dstSize_tooSmall); |
|
2654 | 2667 | MEM_writeLE32(op, checksum); |
|
2655 | 2668 | op += 4; |
|
2656 | 2669 | } |
|
2657 | 2670 | |
|
2658 | 2671 | cctx->stage = ZSTDcs_created; /* return to "created but no init" status */ |
|
2659 | 2672 | return op-ostart; |
|
2660 | 2673 | } |
|
2661 | 2674 | |
|
2662 | 2675 | |
|
2663 | 2676 | size_t ZSTD_compressEnd (ZSTD_CCtx* cctx, |
|
2664 | 2677 | void* dst, size_t dstCapacity, |
|
2665 | 2678 | const void* src, size_t srcSize) |
|
2666 | 2679 | { |
|
2667 | 2680 | size_t endResult; |
|
2668 | 2681 | size_t const cSize = ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 1, 1); |
|
2669 | 2682 | if (ZSTD_isError(cSize)) return cSize; |
|
2670 | 2683 | endResult = ZSTD_writeEpilogue(cctx, (char*)dst + cSize, dstCapacity-cSize); |
|
2671 | 2684 | if (ZSTD_isError(endResult)) return endResult; |
|
2672 | 2685 | return cSize + endResult; |
|
2673 | 2686 | } |
|
2674 | 2687 | |
|
2675 | 2688 | |
|
2676 | 2689 | static size_t ZSTD_compress_internal (ZSTD_CCtx* cctx, |
|
2677 | 2690 | void* dst, size_t dstCapacity, |
|
2678 | 2691 | const void* src, size_t srcSize, |
|
2679 | 2692 | const void* dict,size_t dictSize, |
|
2680 | 2693 | ZSTD_parameters params) |
|
2681 | 2694 | { |
|
2682 | 2695 | CHECK_F(ZSTD_compressBegin_internal(cctx, dict, dictSize, params, srcSize)); |
|
2683 | 2696 | return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize); |
|
2684 | 2697 | } |
|
2685 | 2698 | |
|
2686 | 2699 | size_t ZSTD_compress_advanced (ZSTD_CCtx* ctx, |
|
2687 | 2700 | void* dst, size_t dstCapacity, |
|
2688 | 2701 | const void* src, size_t srcSize, |
|
2689 | 2702 | const void* dict,size_t dictSize, |
|
2690 | 2703 | ZSTD_parameters params) |
|
2691 | 2704 | { |
|
2692 | 2705 | CHECK_F(ZSTD_checkCParams(params.cParams)); |
|
2693 | 2706 | return ZSTD_compress_internal(ctx, dst, dstCapacity, src, srcSize, dict, dictSize, params); |
|
2694 | 2707 | } |
|
2695 | 2708 | |
|
2696 | 2709 | size_t ZSTD_compress_usingDict(ZSTD_CCtx* ctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, const void* dict, size_t dictSize, int compressionLevel) |
|
2697 | 2710 | { |
|
2698 | ZSTD_parameters params = ZSTD_getParams(compressionLevel, srcSize, dictSize); | |
|
2711 | ZSTD_parameters params = ZSTD_getParams(compressionLevel, srcSize, dict ? dictSize : 0); | |
|
2699 | 2712 | params.fParams.contentSizeFlag = 1; |
|
2700 | 2713 | return ZSTD_compress_internal(ctx, dst, dstCapacity, src, srcSize, dict, dictSize, params); |
|
2701 | 2714 | } |
|
2702 | 2715 | |
|
2703 | 2716 | size_t ZSTD_compressCCtx (ZSTD_CCtx* ctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel) |
|
2704 | 2717 | { |
|
2705 | 2718 | return ZSTD_compress_usingDict(ctx, dst, dstCapacity, src, srcSize, NULL, 0, compressionLevel); |
|
2706 | 2719 | } |
|
2707 | 2720 | |
|
2708 | 2721 | size_t ZSTD_compress(void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel) |
|
2709 | 2722 | { |
|
2710 | 2723 | size_t result; |
|
2711 | 2724 | ZSTD_CCtx ctxBody; |
|
2712 | 2725 | memset(&ctxBody, 0, sizeof(ctxBody)); |
|
2713 | 2726 | memcpy(&ctxBody.customMem, &defaultCustomMem, sizeof(ZSTD_customMem)); |
|
2714 | 2727 | result = ZSTD_compressCCtx(&ctxBody, dst, dstCapacity, src, srcSize, compressionLevel); |
|
2715 | 2728 | ZSTD_free(ctxBody.workSpace, defaultCustomMem); /* can't free ctxBody itself, as it's on stack; free only heap content */ |
|
2716 | 2729 | return result; |
|
2717 | 2730 | } |
|
2718 | 2731 | |
|
2719 | 2732 | |
|
2720 | 2733 | /* ===== Dictionary API ===== */ |
|
2721 | 2734 | |
|
2722 | 2735 | struct ZSTD_CDict_s { |
|
2723 | 2736 | void* dictContent; |
|
2724 | 2737 | size_t dictContentSize; |
|
2725 | 2738 | ZSTD_CCtx* refContext; |
|
2726 | 2739 | }; /* typedef'd tp ZSTD_CDict within "zstd.h" */ |
|
2727 | 2740 | |
|
2728 | 2741 | size_t ZSTD_sizeof_CDict(const ZSTD_CDict* cdict) |
|
2729 | 2742 | { |
|
2730 | 2743 | if (cdict==NULL) return 0; /* support sizeof on NULL */ |
|
2731 | 2744 | return ZSTD_sizeof_CCtx(cdict->refContext) + cdict->dictContentSize; |
|
2732 | 2745 | } |
|
2733 | 2746 | |
|
2734 | 2747 | ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictSize, ZSTD_parameters params, ZSTD_customMem customMem) |
|
2735 | 2748 | { |
|
2736 | 2749 | if (!customMem.customAlloc && !customMem.customFree) customMem = defaultCustomMem; |
|
2737 | 2750 | if (!customMem.customAlloc || !customMem.customFree) return NULL; |
|
2738 | 2751 | |
|
2739 | 2752 | { ZSTD_CDict* const cdict = (ZSTD_CDict*) ZSTD_malloc(sizeof(ZSTD_CDict), customMem); |
|
2740 | 2753 | void* const dictContent = ZSTD_malloc(dictSize, customMem); |
|
2741 | 2754 | ZSTD_CCtx* const cctx = ZSTD_createCCtx_advanced(customMem); |
|
2742 | 2755 | |
|
2743 | 2756 | if (!dictContent || !cdict || !cctx) { |
|
2744 | 2757 | ZSTD_free(dictContent, customMem); |
|
2745 | 2758 | ZSTD_free(cdict, customMem); |
|
2746 | 2759 | ZSTD_free(cctx, customMem); |
|
2747 | 2760 | return NULL; |
|
2748 | 2761 | } |
|
2749 | 2762 | |
|
2750 | 2763 | if (dictSize) { |
|
2751 | 2764 | memcpy(dictContent, dict, dictSize); |
|
2752 | 2765 | } |
|
2753 | 2766 | { size_t const errorCode = ZSTD_compressBegin_advanced(cctx, dictContent, dictSize, params, 0); |
|
2754 | 2767 | if (ZSTD_isError(errorCode)) { |
|
2755 | 2768 | ZSTD_free(dictContent, customMem); |
|
2756 | 2769 | ZSTD_free(cdict, customMem); |
|
2757 | 2770 | ZSTD_free(cctx, customMem); |
|
2758 | 2771 | return NULL; |
|
2759 | 2772 | } } |
|
2760 | 2773 | |
|
2761 | 2774 | cdict->dictContent = dictContent; |
|
2762 | 2775 | cdict->dictContentSize = dictSize; |
|
2763 | 2776 | cdict->refContext = cctx; |
|
2764 | 2777 | return cdict; |
|
2765 | 2778 | } |
|
2766 | 2779 | } |
|
2767 | 2780 | |
|
2768 | 2781 | ZSTD_CDict* ZSTD_createCDict(const void* dict, size_t dictSize, int compressionLevel) |
|
2769 | 2782 | { |
|
2770 | 2783 | ZSTD_customMem const allocator = { NULL, NULL, NULL }; |
|
2771 | 2784 | ZSTD_parameters params = ZSTD_getParams(compressionLevel, 0, dictSize); |
|
2772 | 2785 | params.fParams.contentSizeFlag = 1; |
|
2773 | 2786 | return ZSTD_createCDict_advanced(dict, dictSize, params, allocator); |
|
2774 | 2787 | } |
|
2775 | 2788 | |
|
2776 | 2789 | size_t ZSTD_freeCDict(ZSTD_CDict* cdict) |
|
2777 | 2790 | { |
|
2778 | 2791 | if (cdict==NULL) return 0; /* support free on NULL */ |
|
2779 | 2792 | { ZSTD_customMem const cMem = cdict->refContext->customMem; |
|
2780 | 2793 | ZSTD_freeCCtx(cdict->refContext); |
|
2781 | 2794 | ZSTD_free(cdict->dictContent, cMem); |
|
2782 | 2795 | ZSTD_free(cdict, cMem); |
|
2783 | 2796 | return 0; |
|
2784 | 2797 | } |
|
2785 | 2798 | } |
|
2786 | 2799 | |
|
2787 | 2800 | static ZSTD_parameters ZSTD_getParamsFromCDict(const ZSTD_CDict* cdict) { |
|
2788 | 2801 | return ZSTD_getParamsFromCCtx(cdict->refContext); |
|
2789 | 2802 | } |
|
2790 | 2803 | |
|
2791 | 2804 | size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict, U64 pledgedSrcSize) |
|
2792 | 2805 | { |
|
2793 | 2806 | if (cdict->dictContentSize) CHECK_F(ZSTD_copyCCtx(cctx, cdict->refContext, pledgedSrcSize)) |
|
2794 | 2807 | else CHECK_F(ZSTD_compressBegin_advanced(cctx, NULL, 0, cdict->refContext->params, pledgedSrcSize)); |
|
2795 | 2808 | return 0; |
|
2796 | 2809 | } |
|
2797 | 2810 | |
|
2798 | 2811 | /*! ZSTD_compress_usingCDict() : |
|
2799 | 2812 | * Compression using a digested Dictionary. |
|
2800 | 2813 | * Faster startup than ZSTD_compress_usingDict(), recommended when same dictionary is used multiple times. |
|
2801 | 2814 | * Note that compression level is decided during dictionary creation */ |
|
2802 | 2815 | size_t ZSTD_compress_usingCDict(ZSTD_CCtx* cctx, |
|
2803 | 2816 | void* dst, size_t dstCapacity, |
|
2804 | 2817 | const void* src, size_t srcSize, |
|
2805 | 2818 | const ZSTD_CDict* cdict) |
|
2806 | 2819 | { |
|
2807 | 2820 | CHECK_F(ZSTD_compressBegin_usingCDict(cctx, cdict, srcSize)); |
|
2808 | 2821 | |
|
2809 | 2822 | if (cdict->refContext->params.fParams.contentSizeFlag==1) { |
|
2810 | 2823 | cctx->params.fParams.contentSizeFlag = 1; |
|
2811 | 2824 | cctx->frameContentSize = srcSize; |
|
2812 | 2825 | } |
|
2813 | 2826 | |
|
2814 | 2827 | return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize); |
|
2815 | 2828 | } |
|
2816 | 2829 | |
|
2817 | 2830 | |
|
2818 | 2831 | |
|
2819 | 2832 | /* ****************************************************************** |
|
2820 | 2833 | * Streaming |
|
2821 | 2834 | ********************************************************************/ |
|
2822 | 2835 | |
|
2823 | 2836 | typedef enum { zcss_init, zcss_load, zcss_flush, zcss_final } ZSTD_cStreamStage; |
|
2824 | 2837 | |
|
2825 | 2838 | struct ZSTD_CStream_s { |
|
2826 | 2839 | ZSTD_CCtx* cctx; |
|
2827 | 2840 | ZSTD_CDict* cdictLocal; |
|
2828 | 2841 | const ZSTD_CDict* cdict; |
|
2829 | 2842 | char* inBuff; |
|
2830 | 2843 | size_t inBuffSize; |
|
2831 | 2844 | size_t inToCompress; |
|
2832 | 2845 | size_t inBuffPos; |
|
2833 | 2846 | size_t inBuffTarget; |
|
2834 | 2847 | size_t blockSize; |
|
2835 | 2848 | char* outBuff; |
|
2836 | 2849 | size_t outBuffSize; |
|
2837 | 2850 | size_t outBuffContentSize; |
|
2838 | 2851 | size_t outBuffFlushedSize; |
|
2839 | 2852 | ZSTD_cStreamStage stage; |
|
2840 | 2853 | U32 checksum; |
|
2841 | 2854 | U32 frameEnded; |
|
2855 | U64 pledgedSrcSize; | |
|
2856 | U64 inputProcessed; | |
|
2842 | 2857 | ZSTD_parameters params; |
|
2843 | 2858 | ZSTD_customMem customMem; |
|
2844 | 2859 | }; /* typedef'd to ZSTD_CStream within "zstd.h" */ |
|
2845 | 2860 | |
|
2846 | 2861 | ZSTD_CStream* ZSTD_createCStream(void) |
|
2847 | 2862 | { |
|
2848 | 2863 | return ZSTD_createCStream_advanced(defaultCustomMem); |
|
2849 | 2864 | } |
|
2850 | 2865 | |
|
2851 | 2866 | ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem) |
|
2852 | 2867 | { |
|
2853 | 2868 | ZSTD_CStream* zcs; |
|
2854 | 2869 | |
|
2855 | 2870 | if (!customMem.customAlloc && !customMem.customFree) customMem = defaultCustomMem; |
|
2856 | 2871 | if (!customMem.customAlloc || !customMem.customFree) return NULL; |
|
2857 | 2872 | |
|
2858 | 2873 | zcs = (ZSTD_CStream*)ZSTD_malloc(sizeof(ZSTD_CStream), customMem); |
|
2859 | 2874 | if (zcs==NULL) return NULL; |
|
2860 | 2875 | memset(zcs, 0, sizeof(ZSTD_CStream)); |
|
2861 | 2876 | memcpy(&zcs->customMem, &customMem, sizeof(ZSTD_customMem)); |
|
2862 | 2877 | zcs->cctx = ZSTD_createCCtx_advanced(customMem); |
|
2863 | 2878 | if (zcs->cctx == NULL) { ZSTD_freeCStream(zcs); return NULL; } |
|
2864 | 2879 | return zcs; |
|
2865 | 2880 | } |
|
2866 | 2881 | |
|
2867 | 2882 | size_t ZSTD_freeCStream(ZSTD_CStream* zcs) |
|
2868 | 2883 | { |
|
2869 | 2884 | if (zcs==NULL) return 0; /* support free on NULL */ |
|
2870 | 2885 | { ZSTD_customMem const cMem = zcs->customMem; |
|
2871 | 2886 | ZSTD_freeCCtx(zcs->cctx); |
|
2872 | 2887 | ZSTD_freeCDict(zcs->cdictLocal); |
|
2873 | 2888 | ZSTD_free(zcs->inBuff, cMem); |
|
2874 | 2889 | ZSTD_free(zcs->outBuff, cMem); |
|
2875 | 2890 | ZSTD_free(zcs, cMem); |
|
2876 | 2891 | return 0; |
|
2877 | 2892 | } |
|
2878 | 2893 | } |
|
2879 | 2894 | |
|
2880 | 2895 | |
|
2881 | 2896 | /*====== Initialization ======*/ |
|
2882 | 2897 | |
|
2883 | 2898 | size_t ZSTD_CStreamInSize(void) { return ZSTD_BLOCKSIZE_ABSOLUTEMAX; } |
|
2884 | 2899 | size_t ZSTD_CStreamOutSize(void) { return ZSTD_compressBound(ZSTD_BLOCKSIZE_ABSOLUTEMAX) + ZSTD_blockHeaderSize + 4 /* 32-bits hash */ ; } |
|
2885 | 2900 | |
|
2886 | 2901 | size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize) |
|
2887 | 2902 | { |
|
2888 | 2903 | if (zcs->inBuffSize==0) return ERROR(stage_wrong); /* zcs has not been init at least once */ |
|
2889 | 2904 | |
|
2890 | 2905 | if (zcs->cdict) CHECK_F(ZSTD_compressBegin_usingCDict(zcs->cctx, zcs->cdict, pledgedSrcSize)) |
|
2891 | 2906 | else CHECK_F(ZSTD_compressBegin_advanced(zcs->cctx, NULL, 0, zcs->params, pledgedSrcSize)); |
|
2892 | 2907 | |
|
2893 | 2908 | zcs->inToCompress = 0; |
|
2894 | 2909 | zcs->inBuffPos = 0; |
|
2895 | 2910 | zcs->inBuffTarget = zcs->blockSize; |
|
2896 | 2911 | zcs->outBuffContentSize = zcs->outBuffFlushedSize = 0; |
|
2897 | 2912 | zcs->stage = zcss_load; |
|
2898 | 2913 | zcs->frameEnded = 0; |
|
2914 | zcs->pledgedSrcSize = pledgedSrcSize; | |
|
2915 | zcs->inputProcessed = 0; | |
|
2899 | 2916 | return 0; /* ready to go */ |
|
2900 | 2917 | } |
|
2901 | 2918 | |
|
2902 | 2919 | size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, |
|
2903 | 2920 | const void* dict, size_t dictSize, |
|
2904 | 2921 | ZSTD_parameters params, unsigned long long pledgedSrcSize) |
|
2905 | 2922 | { |
|
2906 | 2923 | /* allocate buffers */ |
|
2907 | 2924 | { size_t const neededInBuffSize = (size_t)1 << params.cParams.windowLog; |
|
2908 | 2925 | if (zcs->inBuffSize < neededInBuffSize) { |
|
2909 | 2926 | zcs->inBuffSize = neededInBuffSize; |
|
2910 | 2927 | ZSTD_free(zcs->inBuff, zcs->customMem); |
|
2911 | 2928 | zcs->inBuff = (char*) ZSTD_malloc(neededInBuffSize, zcs->customMem); |
|
2912 | 2929 | if (zcs->inBuff == NULL) return ERROR(memory_allocation); |
|
2913 | 2930 | } |
|
2914 | 2931 | zcs->blockSize = MIN(ZSTD_BLOCKSIZE_ABSOLUTEMAX, neededInBuffSize); |
|
2915 | 2932 | } |
|
2916 | 2933 | if (zcs->outBuffSize < ZSTD_compressBound(zcs->blockSize)+1) { |
|
2917 | 2934 | zcs->outBuffSize = ZSTD_compressBound(zcs->blockSize)+1; |
|
2918 | 2935 | ZSTD_free(zcs->outBuff, zcs->customMem); |
|
2919 | 2936 | zcs->outBuff = (char*) ZSTD_malloc(zcs->outBuffSize, zcs->customMem); |
|
2920 | 2937 | if (zcs->outBuff == NULL) return ERROR(memory_allocation); |
|
2921 | 2938 | } |
|
2922 | 2939 | |
|
2923 | 2940 | if (dict) { |
|
2924 | 2941 | ZSTD_freeCDict(zcs->cdictLocal); |
|
2925 | 2942 | zcs->cdictLocal = ZSTD_createCDict_advanced(dict, dictSize, params, zcs->customMem); |
|
2926 | 2943 | if (zcs->cdictLocal == NULL) return ERROR(memory_allocation); |
|
2927 | 2944 | zcs->cdict = zcs->cdictLocal; |
|
2928 | 2945 | } else zcs->cdict = NULL; |
|
2929 | 2946 | |
|
2930 | 2947 | zcs->checksum = params.fParams.checksumFlag > 0; |
|
2931 | 2948 | zcs->params = params; |
|
2932 | 2949 | |
|
2933 | 2950 | return ZSTD_resetCStream(zcs, pledgedSrcSize); |
|
2934 | 2951 | } |
|
2935 | 2952 | |
|
2936 | 2953 | /* note : cdict must outlive compression session */ |
|
2937 | 2954 | size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict) |
|
2938 | 2955 | { |
|
2939 | 2956 | ZSTD_parameters const params = ZSTD_getParamsFromCDict(cdict); |
|
2940 | 2957 | size_t const initError = ZSTD_initCStream_advanced(zcs, NULL, 0, params, 0); |
|
2941 | 2958 | zcs->cdict = cdict; |
|
2942 | 2959 | return initError; |
|
2943 | 2960 | } |
|
2944 | 2961 | |
|
2945 | 2962 | size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel) |
|
2946 | 2963 | { |
|
2947 | 2964 | ZSTD_parameters const params = ZSTD_getParams(compressionLevel, 0, dictSize); |
|
2948 | 2965 | return ZSTD_initCStream_advanced(zcs, dict, dictSize, params, 0); |
|
2949 | 2966 | } |
|
2950 | 2967 | |
|
2968 | size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize) | |
|
2969 | { | |
|
2970 | ZSTD_parameters const params = ZSTD_getParams(compressionLevel, pledgedSrcSize, 0); | |
|
2971 | return ZSTD_initCStream_advanced(zcs, NULL, 0, params, pledgedSrcSize); | |
|
2972 | } | |
|
2973 | ||
|
2951 | 2974 | size_t ZSTD_initCStream(ZSTD_CStream* zcs, int compressionLevel) |
|
2952 | 2975 | { |
|
2953 | 2976 | return ZSTD_initCStream_usingDict(zcs, NULL, 0, compressionLevel); |
|
2954 | 2977 | } |
|
2955 | 2978 | |
|
2956 | 2979 | size_t ZSTD_sizeof_CStream(const ZSTD_CStream* zcs) |
|
2957 | 2980 | { |
|
2958 | 2981 | if (zcs==NULL) return 0; /* support sizeof on NULL */ |
|
2959 | 2982 | return sizeof(zcs) + ZSTD_sizeof_CCtx(zcs->cctx) + ZSTD_sizeof_CDict(zcs->cdictLocal) + zcs->outBuffSize + zcs->inBuffSize; |
|
2960 | 2983 | } |
|
2961 | 2984 | |
|
2962 | 2985 | /*====== Compression ======*/ |
|
2963 | 2986 | |
|
2964 | 2987 | typedef enum { zsf_gather, zsf_flush, zsf_end } ZSTD_flush_e; |
|
2965 | 2988 | |
|
2966 | 2989 | MEM_STATIC size_t ZSTD_limitCopy(void* dst, size_t dstCapacity, const void* src, size_t srcSize) |
|
2967 | 2990 | { |
|
2968 | 2991 | size_t const length = MIN(dstCapacity, srcSize); |
|
2969 | 2992 | memcpy(dst, src, length); |
|
2970 | 2993 | return length; |
|
2971 | 2994 | } |
|
2972 | 2995 | |
|
2973 | 2996 | static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs, |
|
2974 | 2997 | void* dst, size_t* dstCapacityPtr, |
|
2975 | 2998 | const void* src, size_t* srcSizePtr, |
|
2976 | 2999 | ZSTD_flush_e const flush) |
|
2977 | 3000 | { |
|
2978 | 3001 | U32 someMoreWork = 1; |
|
2979 | 3002 | const char* const istart = (const char*)src; |
|
2980 | 3003 | const char* const iend = istart + *srcSizePtr; |
|
2981 | 3004 | const char* ip = istart; |
|
2982 | 3005 | char* const ostart = (char*)dst; |
|
2983 | 3006 | char* const oend = ostart + *dstCapacityPtr; |
|
2984 | 3007 | char* op = ostart; |
|
2985 | 3008 | |
|
2986 | 3009 | while (someMoreWork) { |
|
2987 | 3010 | switch(zcs->stage) |
|
2988 | 3011 | { |
|
2989 | 3012 | case zcss_init: return ERROR(init_missing); /* call ZBUFF_compressInit() first ! */ |
|
2990 | 3013 | |
|
2991 | 3014 | case zcss_load: |
|
2992 | 3015 | /* complete inBuffer */ |
|
2993 | 3016 | { size_t const toLoad = zcs->inBuffTarget - zcs->inBuffPos; |
|
2994 | 3017 | size_t const loaded = ZSTD_limitCopy(zcs->inBuff + zcs->inBuffPos, toLoad, ip, iend-ip); |
|
2995 | 3018 | zcs->inBuffPos += loaded; |
|
2996 | 3019 | ip += loaded; |
|
2997 | 3020 | if ( (zcs->inBuffPos==zcs->inToCompress) || (!flush && (toLoad != loaded)) ) { |
|
2998 | 3021 | someMoreWork = 0; break; /* not enough input to get a full block : stop there, wait for more */ |
|
2999 | 3022 | } } |
|
3000 | 3023 | /* compress current block (note : this stage cannot be stopped in the middle) */ |
|
3001 | 3024 | { void* cDst; |
|
3002 | 3025 | size_t cSize; |
|
3003 | 3026 | size_t const iSize = zcs->inBuffPos - zcs->inToCompress; |
|
3004 | 3027 | size_t oSize = oend-op; |
|
3005 | 3028 | if (oSize >= ZSTD_compressBound(iSize)) |
|
3006 | 3029 | cDst = op; /* compress directly into output buffer (avoid flush stage) */ |
|
3007 | 3030 | else |
|
3008 | 3031 | cDst = zcs->outBuff, oSize = zcs->outBuffSize; |
|
3009 | 3032 | cSize = (flush == zsf_end) ? |
|
3010 | 3033 | ZSTD_compressEnd(zcs->cctx, cDst, oSize, zcs->inBuff + zcs->inToCompress, iSize) : |
|
3011 | 3034 | ZSTD_compressContinue(zcs->cctx, cDst, oSize, zcs->inBuff + zcs->inToCompress, iSize); |
|
3012 | 3035 | if (ZSTD_isError(cSize)) return cSize; |
|
3013 | 3036 | if (flush == zsf_end) zcs->frameEnded = 1; |
|
3014 | 3037 | /* prepare next block */ |
|
3015 | 3038 | zcs->inBuffTarget = zcs->inBuffPos + zcs->blockSize; |
|
3016 | 3039 | if (zcs->inBuffTarget > zcs->inBuffSize) |
|
3017 | 3040 | zcs->inBuffPos = 0, zcs->inBuffTarget = zcs->blockSize; /* note : inBuffSize >= blockSize */ |
|
3018 | 3041 | zcs->inToCompress = zcs->inBuffPos; |
|
3019 | 3042 | if (cDst == op) { op += cSize; break; } /* no need to flush */ |
|
3020 | 3043 | zcs->outBuffContentSize = cSize; |
|
3021 | 3044 | zcs->outBuffFlushedSize = 0; |
|
3022 | 3045 | zcs->stage = zcss_flush; /* pass-through to flush stage */ |
|
3023 | 3046 | } |
|
3024 | 3047 | |
|
3025 | 3048 | case zcss_flush: |
|
3026 | 3049 | { size_t const toFlush = zcs->outBuffContentSize - zcs->outBuffFlushedSize; |
|
3027 | 3050 | size_t const flushed = ZSTD_limitCopy(op, oend-op, zcs->outBuff + zcs->outBuffFlushedSize, toFlush); |
|
3028 | 3051 | op += flushed; |
|
3029 | 3052 | zcs->outBuffFlushedSize += flushed; |
|
3030 | 3053 | if (toFlush!=flushed) { someMoreWork = 0; break; } /* dst too small to store flushed data : stop there */ |
|
3031 | 3054 | zcs->outBuffContentSize = zcs->outBuffFlushedSize = 0; |
|
3032 | 3055 | zcs->stage = zcss_load; |
|
3033 | 3056 | break; |
|
3034 | 3057 | } |
|
3035 | 3058 | |
|
3036 | 3059 | case zcss_final: |
|
3037 | 3060 | someMoreWork = 0; /* do nothing */ |
|
3038 | 3061 | break; |
|
3039 | 3062 | |
|
3040 | 3063 | default: |
|
3041 | 3064 | return ERROR(GENERIC); /* impossible */ |
|
3042 | 3065 | } |
|
3043 | 3066 | } |
|
3044 | 3067 | |
|
3045 | 3068 | *srcSizePtr = ip - istart; |
|
3046 | 3069 | *dstCapacityPtr = op - ostart; |
|
3070 | zcs->inputProcessed += *srcSizePtr; | |
|
3047 | 3071 | if (zcs->frameEnded) return 0; |
|
3048 | 3072 | { size_t hintInSize = zcs->inBuffTarget - zcs->inBuffPos; |
|
3049 | 3073 | if (hintInSize==0) hintInSize = zcs->blockSize; |
|
3050 | 3074 | return hintInSize; |
|
3051 | 3075 | } |
|
3052 | 3076 | } |
|
3053 | 3077 | |
|
3054 | 3078 | size_t ZSTD_compressStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input) |
|
3055 | 3079 | { |
|
3056 | 3080 | size_t sizeRead = input->size - input->pos; |
|
3057 | 3081 | size_t sizeWritten = output->size - output->pos; |
|
3058 | 3082 | size_t const result = ZSTD_compressStream_generic(zcs, |
|
3059 | 3083 | (char*)(output->dst) + output->pos, &sizeWritten, |
|
3060 | 3084 | (const char*)(input->src) + input->pos, &sizeRead, zsf_gather); |
|
3061 | 3085 | input->pos += sizeRead; |
|
3062 | 3086 | output->pos += sizeWritten; |
|
3063 | 3087 | return result; |
|
3064 | 3088 | } |
|
3065 | 3089 | |
|
3066 | 3090 | |
|
3067 | 3091 | /*====== Finalize ======*/ |
|
3068 | 3092 | |
|
3069 | 3093 | /*! ZSTD_flushStream() : |
|
3070 | 3094 | * @return : amount of data remaining to flush */ |
|
3071 | 3095 | size_t ZSTD_flushStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output) |
|
3072 | 3096 | { |
|
3073 | 3097 | size_t srcSize = 0; |
|
3074 | 3098 | size_t sizeWritten = output->size - output->pos; |
|
3075 | 3099 | size_t const result = ZSTD_compressStream_generic(zcs, |
|
3076 | 3100 | (char*)(output->dst) + output->pos, &sizeWritten, |
|
3077 | 3101 | &srcSize, &srcSize, /* use a valid src address instead of NULL */ |
|
3078 | 3102 | zsf_flush); |
|
3079 | 3103 | output->pos += sizeWritten; |
|
3080 | 3104 | if (ZSTD_isError(result)) return result; |
|
3081 | 3105 | return zcs->outBuffContentSize - zcs->outBuffFlushedSize; /* remaining to flush */ |
|
3082 | 3106 | } |
|
3083 | 3107 | |
|
3084 | 3108 | |
|
3085 | 3109 | size_t ZSTD_endStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output) |
|
3086 | 3110 | { |
|
3087 | 3111 | BYTE* const ostart = (BYTE*)(output->dst) + output->pos; |
|
3088 | 3112 | BYTE* const oend = (BYTE*)(output->dst) + output->size; |
|
3089 | 3113 | BYTE* op = ostart; |
|
3090 | 3114 | |
|
3115 | if ((zcs->pledgedSrcSize) && (zcs->inputProcessed != zcs->pledgedSrcSize)) | |
|
3116 | return ERROR(srcSize_wrong); /* pledgedSrcSize not respected */ | |
|
3117 | ||
|
3091 | 3118 | if (zcs->stage != zcss_final) { |
|
3092 | 3119 | /* flush whatever remains */ |
|
3093 | 3120 | size_t srcSize = 0; |
|
3094 | 3121 | size_t sizeWritten = output->size - output->pos; |
|
3095 | 3122 | size_t const notEnded = ZSTD_compressStream_generic(zcs, ostart, &sizeWritten, &srcSize, &srcSize, zsf_end); /* use a valid src address instead of NULL */ |
|
3096 | 3123 | size_t const remainingToFlush = zcs->outBuffContentSize - zcs->outBuffFlushedSize; |
|
3097 | 3124 | op += sizeWritten; |
|
3098 | 3125 | if (remainingToFlush) { |
|
3099 | 3126 | output->pos += sizeWritten; |
|
3100 | 3127 | return remainingToFlush + ZSTD_BLOCKHEADERSIZE /* final empty block */ + (zcs->checksum * 4); |
|
3101 | 3128 | } |
|
3102 | 3129 | /* create epilogue */ |
|
3103 | 3130 | zcs->stage = zcss_final; |
|
3104 | 3131 | zcs->outBuffContentSize = !notEnded ? 0 : |
|
3105 | 3132 | ZSTD_compressEnd(zcs->cctx, zcs->outBuff, zcs->outBuffSize, NULL, 0); /* write epilogue, including final empty block, into outBuff */ |
|
3106 | 3133 | } |
|
3107 | 3134 | |
|
3108 | 3135 | /* flush epilogue */ |
|
3109 | 3136 | { size_t const toFlush = zcs->outBuffContentSize - zcs->outBuffFlushedSize; |
|
3110 | 3137 | size_t const flushed = ZSTD_limitCopy(op, oend-op, zcs->outBuff + zcs->outBuffFlushedSize, toFlush); |
|
3111 | 3138 | op += flushed; |
|
3112 | 3139 | zcs->outBuffFlushedSize += flushed; |
|
3113 | 3140 | output->pos += op-ostart; |
|
3114 | 3141 | if (toFlush==flushed) zcs->stage = zcss_init; /* end reached */ |
|
3115 | 3142 | return toFlush - flushed; |
|
3116 | 3143 | } |
|
3117 | 3144 | } |
|
3118 | 3145 | |
|
3119 | 3146 | |
|
3120 | 3147 | |
|
3121 | 3148 | /*-===== Pre-defined compression levels =====-*/ |
|
3122 | 3149 | |
|
3123 | 3150 | #define ZSTD_DEFAULT_CLEVEL 1 |
|
3124 | 3151 | #define ZSTD_MAX_CLEVEL 22 |
|
3125 | 3152 | int ZSTD_maxCLevel(void) { return ZSTD_MAX_CLEVEL; } |
|
3126 | 3153 | |
|
3127 | 3154 | static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEVEL+1] = { |
|
3128 | 3155 | { /* "default" */ |
|
3129 | 3156 | /* W, C, H, S, L, TL, strat */ |
|
3130 | 3157 | { 18, 12, 12, 1, 7, 16, ZSTD_fast }, /* level 0 - never used */ |
|
3131 | 3158 | { 19, 13, 14, 1, 7, 16, ZSTD_fast }, /* level 1 */ |
|
3132 | 3159 | { 19, 15, 16, 1, 6, 16, ZSTD_fast }, /* level 2 */ |
|
3133 | 3160 | { 20, 16, 17, 1, 5, 16, ZSTD_dfast }, /* level 3.*/ |
|
3134 | 3161 | { 20, 18, 18, 1, 5, 16, ZSTD_dfast }, /* level 4.*/ |
|
3135 | 3162 | { 20, 15, 18, 3, 5, 16, ZSTD_greedy }, /* level 5 */ |
|
3136 | 3163 | { 21, 16, 19, 2, 5, 16, ZSTD_lazy }, /* level 6 */ |
|
3137 | 3164 | { 21, 17, 20, 3, 5, 16, ZSTD_lazy }, /* level 7 */ |
|
3138 | 3165 | { 21, 18, 20, 3, 5, 16, ZSTD_lazy2 }, /* level 8 */ |
|
3139 | 3166 | { 21, 20, 20, 3, 5, 16, ZSTD_lazy2 }, /* level 9 */ |
|
3140 | 3167 | { 21, 19, 21, 4, 5, 16, ZSTD_lazy2 }, /* level 10 */ |
|
3141 | 3168 | { 22, 20, 22, 4, 5, 16, ZSTD_lazy2 }, /* level 11 */ |
|
3142 | 3169 | { 22, 20, 22, 5, 5, 16, ZSTD_lazy2 }, /* level 12 */ |
|
3143 | 3170 | { 22, 21, 22, 5, 5, 16, ZSTD_lazy2 }, /* level 13 */ |
|
3144 | 3171 | { 22, 21, 22, 6, 5, 16, ZSTD_lazy2 }, /* level 14 */ |
|
3145 | 3172 | { 22, 21, 21, 5, 5, 16, ZSTD_btlazy2 }, /* level 15 */ |
|
3146 | 3173 | { 23, 22, 22, 5, 5, 16, ZSTD_btlazy2 }, /* level 16 */ |
|
3147 | 3174 | { 23, 21, 22, 4, 5, 24, ZSTD_btopt }, /* level 17 */ |
|
3148 | 3175 | { 23, 23, 22, 6, 5, 32, ZSTD_btopt }, /* level 18 */ |
|
3149 | 3176 | { 23, 23, 22, 6, 3, 48, ZSTD_btopt }, /* level 19 */ |
|
3150 | 3177 | { 25, 25, 23, 7, 3, 64, ZSTD_btopt2 }, /* level 20 */ |
|
3151 | 3178 | { 26, 26, 23, 7, 3,256, ZSTD_btopt2 }, /* level 21 */ |
|
3152 | 3179 | { 27, 27, 25, 9, 3,512, ZSTD_btopt2 }, /* level 22 */ |
|
3153 | 3180 | }, |
|
3154 | 3181 | { /* for srcSize <= 256 KB */ |
|
3155 | 3182 | /* W, C, H, S, L, T, strat */ |
|
3156 | 3183 | { 0, 0, 0, 0, 0, 0, ZSTD_fast }, /* level 0 - not used */ |
|
3157 | 3184 | { 18, 13, 14, 1, 6, 8, ZSTD_fast }, /* level 1 */ |
|
3158 | 3185 | { 18, 14, 13, 1, 5, 8, ZSTD_dfast }, /* level 2 */ |
|
3159 | 3186 | { 18, 16, 15, 1, 5, 8, ZSTD_dfast }, /* level 3 */ |
|
3160 | 3187 | { 18, 15, 17, 1, 5, 8, ZSTD_greedy }, /* level 4.*/ |
|
3161 | 3188 | { 18, 16, 17, 4, 5, 8, ZSTD_greedy }, /* level 5.*/ |
|
3162 | 3189 | { 18, 16, 17, 3, 5, 8, ZSTD_lazy }, /* level 6.*/ |
|
3163 | 3190 | { 18, 17, 17, 4, 4, 8, ZSTD_lazy }, /* level 7 */ |
|
3164 | 3191 | { 18, 17, 17, 4, 4, 8, ZSTD_lazy2 }, /* level 8 */ |
|
3165 | 3192 | { 18, 17, 17, 5, 4, 8, ZSTD_lazy2 }, /* level 9 */ |
|
3166 | 3193 | { 18, 17, 17, 6, 4, 8, ZSTD_lazy2 }, /* level 10 */ |
|
3167 | 3194 | { 18, 18, 17, 6, 4, 8, ZSTD_lazy2 }, /* level 11.*/ |
|
3168 | 3195 | { 18, 18, 17, 7, 4, 8, ZSTD_lazy2 }, /* level 12.*/ |
|
3169 | 3196 | { 18, 19, 17, 6, 4, 8, ZSTD_btlazy2 }, /* level 13 */ |
|
3170 | 3197 | { 18, 18, 18, 4, 4, 16, ZSTD_btopt }, /* level 14.*/ |
|
3171 | 3198 | { 18, 18, 18, 4, 3, 16, ZSTD_btopt }, /* level 15.*/ |
|
3172 | 3199 | { 18, 19, 18, 6, 3, 32, ZSTD_btopt }, /* level 16.*/ |
|
3173 | 3200 | { 18, 19, 18, 8, 3, 64, ZSTD_btopt }, /* level 17.*/ |
|
3174 | 3201 | { 18, 19, 18, 9, 3,128, ZSTD_btopt }, /* level 18.*/ |
|
3175 | 3202 | { 18, 19, 18, 10, 3,256, ZSTD_btopt }, /* level 19.*/ |
|
3176 | 3203 | { 18, 19, 18, 11, 3,512, ZSTD_btopt2 }, /* level 20.*/ |
|
3177 | 3204 | { 18, 19, 18, 12, 3,512, ZSTD_btopt2 }, /* level 21.*/ |
|
3178 | 3205 | { 18, 19, 18, 13, 3,512, ZSTD_btopt2 }, /* level 22.*/ |
|
3179 | 3206 | }, |
|
3180 | 3207 | { /* for srcSize <= 128 KB */ |
|
3181 | 3208 | /* W, C, H, S, L, T, strat */ |
|
3182 | 3209 | { 17, 12, 12, 1, 7, 8, ZSTD_fast }, /* level 0 - not used */ |
|
3183 | 3210 | { 17, 12, 13, 1, 6, 8, ZSTD_fast }, /* level 1 */ |
|
3184 | 3211 | { 17, 13, 16, 1, 5, 8, ZSTD_fast }, /* level 2 */ |
|
3185 | 3212 | { 17, 16, 16, 2, 5, 8, ZSTD_dfast }, /* level 3 */ |
|
3186 | 3213 | { 17, 13, 15, 3, 4, 8, ZSTD_greedy }, /* level 4 */ |
|
3187 | 3214 | { 17, 15, 17, 4, 4, 8, ZSTD_greedy }, /* level 5 */ |
|
3188 | 3215 | { 17, 16, 17, 3, 4, 8, ZSTD_lazy }, /* level 6 */ |
|
3189 | 3216 | { 17, 15, 17, 4, 4, 8, ZSTD_lazy2 }, /* level 7 */ |
|
3190 | 3217 | { 17, 17, 17, 4, 4, 8, ZSTD_lazy2 }, /* level 8 */ |
|
3191 | 3218 | { 17, 17, 17, 5, 4, 8, ZSTD_lazy2 }, /* level 9 */ |
|
3192 | 3219 | { 17, 17, 17, 6, 4, 8, ZSTD_lazy2 }, /* level 10 */ |
|
3193 | 3220 | { 17, 17, 17, 7, 4, 8, ZSTD_lazy2 }, /* level 11 */ |
|
3194 | 3221 | { 17, 17, 17, 8, 4, 8, ZSTD_lazy2 }, /* level 12 */ |
|
3195 | 3222 | { 17, 18, 17, 6, 4, 8, ZSTD_btlazy2 }, /* level 13.*/ |
|
3196 | 3223 | { 17, 17, 17, 7, 3, 8, ZSTD_btopt }, /* level 14.*/ |
|
3197 | 3224 | { 17, 17, 17, 7, 3, 16, ZSTD_btopt }, /* level 15.*/ |
|
3198 | 3225 | { 17, 18, 17, 7, 3, 32, ZSTD_btopt }, /* level 16.*/ |
|
3199 | 3226 | { 17, 18, 17, 7, 3, 64, ZSTD_btopt }, /* level 17.*/ |
|
3200 | 3227 | { 17, 18, 17, 7, 3,256, ZSTD_btopt }, /* level 18.*/ |
|
3201 | 3228 | { 17, 18, 17, 8, 3,256, ZSTD_btopt }, /* level 19.*/ |
|
3202 | 3229 | { 17, 18, 17, 9, 3,256, ZSTD_btopt2 }, /* level 20.*/ |
|
3203 | 3230 | { 17, 18, 17, 10, 3,256, ZSTD_btopt2 }, /* level 21.*/ |
|
3204 | 3231 | { 17, 18, 17, 11, 3,512, ZSTD_btopt2 }, /* level 22.*/ |
|
3205 | 3232 | }, |
|
3206 | 3233 | { /* for srcSize <= 16 KB */ |
|
3207 | 3234 | /* W, C, H, S, L, T, strat */ |
|
3208 | 3235 | { 14, 12, 12, 1, 7, 6, ZSTD_fast }, /* level 0 - not used */ |
|
3209 | 3236 | { 14, 14, 14, 1, 6, 6, ZSTD_fast }, /* level 1 */ |
|
3210 | 3237 | { 14, 14, 14, 1, 4, 6, ZSTD_fast }, /* level 2 */ |
|
3211 | 3238 | { 14, 14, 14, 1, 4, 6, ZSTD_dfast }, /* level 3.*/ |
|
3212 | 3239 | { 14, 14, 14, 4, 4, 6, ZSTD_greedy }, /* level 4.*/ |
|
3213 | 3240 | { 14, 14, 14, 3, 4, 6, ZSTD_lazy }, /* level 5.*/ |
|
3214 | 3241 | { 14, 14, 14, 4, 4, 6, ZSTD_lazy2 }, /* level 6 */ |
|
3215 | 3242 | { 14, 14, 14, 5, 4, 6, ZSTD_lazy2 }, /* level 7 */ |
|
3216 | 3243 | { 14, 14, 14, 6, 4, 6, ZSTD_lazy2 }, /* level 8.*/ |
|
3217 | 3244 | { 14, 15, 14, 6, 4, 6, ZSTD_btlazy2 }, /* level 9.*/ |
|
3218 | 3245 | { 14, 15, 14, 3, 3, 6, ZSTD_btopt }, /* level 10.*/ |
|
3219 | 3246 | { 14, 15, 14, 6, 3, 8, ZSTD_btopt }, /* level 11.*/ |
|
3220 | 3247 | { 14, 15, 14, 6, 3, 16, ZSTD_btopt }, /* level 12.*/ |
|
3221 | 3248 | { 14, 15, 14, 6, 3, 24, ZSTD_btopt }, /* level 13.*/ |
|
3222 | 3249 | { 14, 15, 15, 6, 3, 48, ZSTD_btopt }, /* level 14.*/ |
|
3223 | 3250 | { 14, 15, 15, 6, 3, 64, ZSTD_btopt }, /* level 15.*/ |
|
3224 | 3251 | { 14, 15, 15, 6, 3, 96, ZSTD_btopt }, /* level 16.*/ |
|
3225 | 3252 | { 14, 15, 15, 6, 3,128, ZSTD_btopt }, /* level 17.*/ |
|
3226 | 3253 | { 14, 15, 15, 6, 3,256, ZSTD_btopt }, /* level 18.*/ |
|
3227 | 3254 | { 14, 15, 15, 7, 3,256, ZSTD_btopt }, /* level 19.*/ |
|
3228 | 3255 | { 14, 15, 15, 8, 3,256, ZSTD_btopt2 }, /* level 20.*/ |
|
3229 | 3256 | { 14, 15, 15, 9, 3,256, ZSTD_btopt2 }, /* level 21.*/ |
|
3230 | 3257 | { 14, 15, 15, 10, 3,256, ZSTD_btopt2 }, /* level 22.*/ |
|
3231 | 3258 | }, |
|
3232 | 3259 | }; |
|
3233 | 3260 | |
|
3234 | 3261 | /*! ZSTD_getCParams() : |
|
3235 | 3262 | * @return ZSTD_compressionParameters structure for a selected compression level, `srcSize` and `dictSize`. |
|
3236 | 3263 | * Size values are optional, provide 0 if not known or unused */ |
|
3237 | 3264 | ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long long srcSize, size_t dictSize) |
|
3238 | 3265 | { |
|
3239 | 3266 | ZSTD_compressionParameters cp; |
|
3240 | 3267 | size_t const addedSize = srcSize ? 0 : 500; |
|
3241 | 3268 | U64 const rSize = srcSize+dictSize ? srcSize+dictSize+addedSize : (U64)-1; |
|
3242 | 3269 | U32 const tableID = (rSize <= 256 KB) + (rSize <= 128 KB) + (rSize <= 16 KB); /* intentional underflow for srcSizeHint == 0 */ |
|
3243 | 3270 | if (compressionLevel <= 0) compressionLevel = ZSTD_DEFAULT_CLEVEL; /* 0 == default; no negative compressionLevel yet */ |
|
3244 | 3271 | if (compressionLevel > ZSTD_MAX_CLEVEL) compressionLevel = ZSTD_MAX_CLEVEL; |
|
3245 | 3272 | cp = ZSTD_defaultCParameters[tableID][compressionLevel]; |
|
3246 | 3273 | if (MEM_32bits()) { /* auto-correction, for 32-bits mode */ |
|
3247 | 3274 | if (cp.windowLog > ZSTD_WINDOWLOG_MAX) cp.windowLog = ZSTD_WINDOWLOG_MAX; |
|
3248 | 3275 | if (cp.chainLog > ZSTD_CHAINLOG_MAX) cp.chainLog = ZSTD_CHAINLOG_MAX; |
|
3249 | 3276 | if (cp.hashLog > ZSTD_HASHLOG_MAX) cp.hashLog = ZSTD_HASHLOG_MAX; |
|
3250 | 3277 | } |
|
3251 | 3278 | cp = ZSTD_adjustCParams(cp, srcSize, dictSize); |
|
3252 | 3279 | return cp; |
|
3253 | 3280 | } |
|
3254 | 3281 | |
|
3255 | 3282 | /*! ZSTD_getParams() : |
|
3256 | 3283 | * same as ZSTD_getCParams(), but @return a `ZSTD_parameters` object (instead of `ZSTD_compressionParameters`). |
|
3257 | 3284 | * All fields of `ZSTD_frameParameters` are set to default (0) */ |
|
3258 | 3285 | ZSTD_parameters ZSTD_getParams(int compressionLevel, unsigned long long srcSize, size_t dictSize) { |
|
3259 | 3286 | ZSTD_parameters params; |
|
3260 | 3287 | ZSTD_compressionParameters const cParams = ZSTD_getCParams(compressionLevel, srcSize, dictSize); |
|
3261 | 3288 | memset(¶ms, 0, sizeof(params)); |
|
3262 | 3289 | params.cParams = cParams; |
|
3263 | 3290 | return params; |
|
3264 | 3291 | } |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
General Comments 0
You need to be logged in to leave comments.
Login now