├── LICENSE ├── README.md └── lzav.h /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023-2025 Aleksey Vaneev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LZAV - Fast Data Compression Algorithm (in C/C++) 2 | 3 | ## Introduction 4 | 5 | LZAV is a fast general-purpose in-memory data compression algorithm based on 6 | now-classic [LZ77](https://wikipedia.org/wiki/LZ77_and_LZ78) lossless data 7 | compression method. LZAV holds a good position on the Pareto landscape of 8 | factors, among many similar in-memory (non-streaming) compression algorithms. 9 | 10 | The LZAV algorithm's code is portable, cross-platform, scalar, header-only, 11 | and inlinable C (compatible with C++). It supports big- and little-endian 12 | platforms, and any memory alignment models. The algorithm is efficient on both 13 | 32- and 64-bit platforms. Incompressible data almost does not expand. 14 | Compliant with WebAssembly (WASI libc), and runs there at just twice lower 15 | performance than the native code. 16 | 17 | LZAV does not sacrifice internal out-of-bounds (OOB) checks for decompression 18 | speed. This means that LZAV can be used in strict conditions where OOB memory 19 | writes (and especially reads) that lead to a trap are unacceptable (e.g., 20 | real-time, system, server software). LZAV can be used safely (causing no 21 | crashing nor UB) even when decompressing malformed or damaged compressed data, 22 | which means that LZAV does not require calculation of a checksum (or hash) of 23 | the compressed data. Only a checksum of the uncompressed data may be required, 24 | depending on an application's guarantees. 25 | 26 | The internal functions available in the `lzav.h` file allow you to easily 27 | implement, and experiment with, your own compression algorithms. LZAV stream 28 | format and decompressor have a potential of high decompression speeds and 29 | compression ratios, which depend on the way data is compressed. 30 | 31 | ## Usage 32 | 33 | To compress data: 34 | 35 | ```c 36 | #include "lzav.h" 37 | 38 | int max_len = lzav_compress_bound( src_len ); 39 | void* comp_buf = malloc( max_len ); 40 | int comp_len = lzav_compress_default( src_buf, comp_buf, src_len, max_len ); 41 | 42 | if( comp_len == 0 && src_len != 0 ) 43 | { 44 | // Error handling. 45 | } 46 | ``` 47 | 48 | To decompress data: 49 | 50 | ```c 51 | #include "lzav.h" 52 | 53 | void* decomp_buf = malloc( src_len ); 54 | int l = lzav_decompress( comp_buf, decomp_buf, comp_len, src_len ); 55 | 56 | if( l < 0 ) 57 | { 58 | // Error handling. 59 | } 60 | ``` 61 | 62 | To compress data with a higher ratio, for non-time-critical uses (e.g., 63 | compression of application's static assets): 64 | 65 | ```c 66 | #include "lzav.h" 67 | 68 | int max_len = lzav_compress_bound_hi( src_len ); // Note another bound function! 69 | void* comp_buf = malloc( max_len ); 70 | int comp_len = lzav_compress_hi( src_buf, comp_buf, src_len, max_len ); 71 | 72 | if( comp_len == 0 && src_len != 0 ) 73 | { 74 | // Error handling. 75 | } 76 | ``` 77 | 78 | LZAV algorithm and the source code (which conforms to 79 | [ISO C99](https://en.wikipedia.org/wiki/C99)) were quality-tested on: 80 | Clang, GCC, MSVC, and Intel C++ compilers; on x86, x86-64 (Intel, AMD), 81 | and AArch64 (Apple Silicon) architectures; Windows 10, AlmaLinux 9.3, and 82 | macOS 15.7. Full C++ compliance is enabled conditionally and automatically 83 | when the source code is compiled with a C++ compiler. 84 | 85 | ## Ports 86 | 87 | * [C++, vcpkg](https://vcpkg.link/ports/lzav) 88 | * [FreeArc, by Shegorat](https://krinkels.org/resources/cls-lzav.579/) 89 | * [Rust, by pkolaczk](https://crates.io/crates/lzav) 90 | 91 | ## Customizing C++ namespace 92 | 93 | In C++ environments where it is undesirable to export LZAV symbols into the 94 | global namespace, the `LZAV_NS_CUSTOM` macro can be defined externally: 95 | 96 | ```c++ 97 | #define LZAV_NS_CUSTOM lzav 98 | #include "lzav.h" 99 | ``` 100 | 101 | Similarly, LZAV symbols can be placed into any other custom namespace (e.g., 102 | a namespace with data compression functions): 103 | 104 | ```c++ 105 | #define LZAV_NS_CUSTOM my_namespace 106 | #include "lzav.h" 107 | ``` 108 | 109 | This way, LZAV symbols and functions can be referenced like 110 | `my_namespace::lzav_compress_default(...)`. Note that since all LZAV functions 111 | have the `static` specifier, there can be no ABI conflicts, even if the LZAV 112 | header is included in unrelated, mixed C/C++, compilation units. 113 | 114 | ## Comparisons 115 | 116 | The tables below present performance ballpark numbers of LZAV algorithm 117 | (based on Silesia dataset). 118 | 119 | While LZ4 seems to compress faster, LZAV comparably provides 15.5% memory 120 | storage cost savings. This is a significant benefit in database and file 121 | system use cases since compression is only about 35% slower while CPUs rarely 122 | run at their maximum capacity anyway (considering cached data writes are 123 | deferred in background threads), and disk I/O times are reduced due to a 124 | better compression. In general, LZAV holds a very strong position in this 125 | class of data compression algorithms, if one considers all factors: 126 | compression and decompression speeds, compression ratio, and just as 127 | important - code maintainability: LZAV is maximally portable and has a rather 128 | small independent codebase. 129 | 130 | Performance of LZAV is not limited to the presented ballpark numbers. 131 | Depending on the data being compressed, LZAV can achieve 800 MB/s compression 132 | and 5000 MB/s decompression speeds. Incompressible data decompresses at 10000 133 | MB/s rate, which is not far from the "memcpy". There are cases like the 134 | [enwik9 dataset](https://mattmahoney.net/dc/textdata.html) where LZAV 135 | provides 22% higher memory storage savings compared to LZ4. 136 | 137 | The geomean performance of the LZAV algorithm on a variety of datasets is 138 | 550 +/- 150 MB/s compression and 3800 +/- 1300 MB/s decompression speeds, 139 | on 4+ GHz 64-bit processors released since 2019. Note that the algorithm 140 | exhibits adaptive qualities, and its actual performance depends on the data 141 | being compressed. LZAV may show an exceptional performance on your specific 142 | data, including, but not limited to: sparse databases, log files, HTML/XML 143 | files. 144 | 145 | It is also worth noting that compression methods like LZAV and LZ4 usually 146 | have an advantage over dictionary- and entropy-based coding in that 147 | hash-table-based compression has a small memory and operational overhead while 148 | the classic LZ77 decompression has no overhead at all - this is especially 149 | relevant for smaller data. 150 | 151 | For a more comprehensive in-memory compression algorithms benchmark you may 152 | visit [lzbench](https://github.com/inikep/lzbench). 153 | 154 | ### Apple clang 15.0.0 arm64, macOS 15.7, Apple M1, 3.5 GHz 155 | 156 | Silesia compression corpus 157 | 158 | | Compressor | Compression | Decompression | Ratio % | 159 | |-----------------|-------------|---------------|---------| 160 | | **LZAV 5.8** | 625 MB/s | 3790 MB/s | 39.94 | 161 | | LZ4 1.9.4 | 700 MB/s | 4570 MB/s | 47.60 | 162 | | Snappy 1.1.10 | 495 MB/s | 3230 MB/s | 48.22 | 163 | | LZF 3.6 | 395 MB/s | 800 MB/s | 48.15 | 164 | | **LZAV 5.8 HI** | 134 MB/s | 3700 MB/s | 35.12 | 165 | | LZ4HC 1.9.4 -9 | 40 MB/s | 4360 MB/s | 36.75 | 166 | 167 | ### LLVM clang 19.1.7 x86-64, AlmaLinux 9.3, Xeon E-2386G (RocketLake), 5.1 GHz 168 | 169 | Silesia compression corpus 170 | 171 | | Compressor | Compression | Decompression | Ratio % | 172 | |-----------------|-------------|---------------|---------| 173 | | **LZAV 5.8** | 620 MB/s | 3490 MB/s | 39.94 | 174 | | LZ4 1.9.4 | 848 MB/s | 4980 MB/s | 47.60 | 175 | | Snappy 1.1.10 | 690 MB/s | 3360 MB/s | 48.22 | 176 | | LZF 3.6 | 455 MB/s | 1000 MB/s | 48.15 | 177 | | **LZAV 5.8 HI** | 115 MB/s | 3330 MB/s | 35.12 | 178 | | LZ4HC 1.9.4 -9 | 43 MB/s | 4920 MB/s | 36.75 | 179 | 180 | ### LLVM clang-cl 18.1.8 x86-64, Windows 10, Ryzen 3700X (Zen2), 4.2 GHz 181 | 182 | Silesia compression corpus 183 | 184 | | Compressor | Compression | Decompression | Ratio % | 185 | |-----------------|-------------|---------------|---------| 186 | | **LZAV 5.8** | 525 MB/s | 3060 MB/s | 39.94 | 187 | | LZ4 1.9.4 | 675 MB/s | 4560 MB/s | 47.60 | 188 | | Snappy 1.1.10 | 415 MB/s | 2440 MB/s | 48.22 | 189 | | LZF 3.6 | 310 MB/s | 700 MB/s | 48.15 | 190 | | **LZAV 5.8 HI** | 116 MB/s | 2970 MB/s | 35.12 | 191 | | LZ4HC 1.9.4 -9 | 36 MB/s | 4430 MB/s | 36.75 | 192 | 193 | P.S. Popular Zstd's benchmark was not included here, because it is not a pure 194 | LZ77, much harder to integrate, and has a much larger code size - a different 195 | league, close to zlib. Here are author's Zstd measurements with 196 | [TurboBench](https://github.com/powturbo/TurboBench/releases), on Ryzen 3700X, 197 | on Silesia dataset: 198 | 199 | | Compressor | Compression | Decompression | Ratio % | 200 | |-----------------|-------------|---------------|---------| 201 | | zstd 1.5.5 -1 | 460 MB/s | 1870 MB/s | 41.0 | 202 | | zstd 1.5.5 1 | 436 MB/s | 1400 MB/s | 34.6 | 203 | 204 | ## Datasets Benchmark 205 | 206 | This section presents compression ratio comparisons for various popular 207 | datasets. Note that each file within these datasets was compressed 208 | individually, which contributed to the overall ratio. 209 | 210 | | Dataset | Size, MiB | LZAV 5.8 | LZ4 1.9.4 | Snappy 1.1.10 | LZF 3.6 | Source | 211 | |-----------------------|-----------|----------|-----------|---------------|---------|--------| 212 | | 4SICS 151020 PCAP | 24.5 | 20.47 | 21.82 | 24.95 | 25.34 | [www.netresec.com](https://www.netresec.com/?page=PCAP4SICS)| 213 | | 4SICS 151022 PCAP | 200.0 | 36.45 | 37.35 | 40.24 | 41.37 | [www.netresec.com](https://www.netresec.com/?page=PCAP4SICS)| 214 | | Calgary Large | 3.1 | 44.29 | 51.97 | 51.76 | 49.07 | [data-compression.info](https://www.data-compression.info/Corpora/CalgaryCorpus/) | 215 | | Canterbury | 2.68 | 38.07 | 43.73 | 45.42 | 42.49 | [corpus.canterbury.ac.nz](https://corpus.canterbury.ac.nz/) | 216 | | Canterbury Large | 10.6 | 38.25 | 51.97 | 48.37 | 54.28 | [corpus.canterbury.ac.nz](https://corpus.canterbury.ac.nz/) | 217 | | Canterbury Artificial | 0.29 | 33.36 | 33.74 | 36.48 | 34.66 | [corpus.canterbury.ac.nz](https://corpus.canterbury.ac.nz/) | 218 | | employees_10KB.json | 0.01 | 22.55 | 24.68 | 23.92 | 23.52 | [sample.json-format.com](https://sample.json-format.com/) | 219 | | employees_100KB.json | 0.10 | 15.96 | 17.71 | 19.02 | 21.88 | [sample.json-format.com](https://sample.json-format.com/) | 220 | | employees_50MB.json | 51.5 | 10.78 | 16.42 | 18.57 | 21.44 | [sample.json-format.com](https://sample.json-format.com/) | 221 | | enwik8 | 95.4 | 44.61 | 57.26 | 56.56 | 53.95 | [www.mattmahoney.net](https://www.mattmahoney.net/dc/textdata.html) | 222 | | enwik9 | 954.7 | 39.39 | 50.92 | 50.79 | 49.30 | [www.mattmahoney.net](https://www.mattmahoney.net/dc/textdata.html) | 223 | | Manzini | 855.3 | 26.98 | 37.30 | 38.57 | 39.04 | [people.unipmn.it/manzini](https://people.unipmn.it/manzini/boosting/index.html) | 224 | | chr22.dna (Manzini) | 33.0 | 38.79 | 52.82 | 44.53 | 55.86 | [people.unipmn.it/manzini](https://people.unipmn.it/manzini/boosting/index.html) | 225 | | w3c2 HTML (Manzini) | 99.4 | 11.43 | 22.20 | 25.35 | 27.20 | [people.unipmn.it/manzini](https://people.unipmn.it/manzini/boosting/index.html) | 226 | | Silesia | 202.1 | 39.94 | 47.60 | 48.17 | 48.15 | [github.com/MiloszKrajewski](https://github.com/MiloszKrajewski/SilesiaCorpus) | 227 | 228 | ## Notes 229 | 230 | 1. LZAV API is not equivalent to LZ4 or Snappy API. For example, the `dstlen` 231 | parameter in the decompressor should specify the original uncompressed length, 232 | which should have been previously stored in some way, independent of LZAV. 233 | 234 | 2. From a technical point of view, peak decompression speeds of LZAV have an 235 | implicit limitation arising from its more complex stream format, compared to 236 | LZ4: LZAV decompression requires more code branching. Another limiting factor 237 | is a rather large dynamic 2-512 MiB LZ77 window which is not CPU 238 | cache-friendly. On the other hand, without these features it would not be 239 | possible to achieve competitive compression ratios while having fast 240 | compression speeds. 241 | 242 | 3. LZAV supports compression of continuous data blocks of up to 2 GB. Larger 243 | data should be compressed in chunks of at least 16 MB. Using smaller chunks 244 | may reduce the achieved compression ratio. 245 | 246 | ## Thanks 247 | 248 | * [Paul Dreik](https://github.com/pauldreik), for finding memcpy UB in the 249 | decompressor. 250 | -------------------------------------------------------------------------------- /lzav.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file lzav.h 3 | * 4 | * @version 5.8 5 | * 6 | * @brief Self-contained header file for the "LZAV" in-memory data compression 7 | * and decompression algorithms. 8 | * 9 | * The source code is written in ISO C99, with full C++ compliance enabled 10 | * conditionally and automatically when compiled with a C++ compiler. 11 | * 12 | * Description is available at https://github.com/avaneev/lzav 13 | * 14 | * Email: aleksey.vaneev@gmail.com or info@voxengo.com 15 | * 16 | * LICENSE: 17 | * 18 | * Copyright (c) 2023-2025 Aleksey Vaneev 19 | * 20 | * Permission is hereby granted, free of charge, to any person obtaining a 21 | * copy of this software and associated documentation files (the "Software"), 22 | * to deal in the Software without restriction, including without limitation 23 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 24 | * and/or sell copies of the Software, and to permit persons to whom the 25 | * Software is furnished to do so, subject to the following conditions: 26 | * 27 | * The above copyright notice and this permission notice shall be included in 28 | * all copies or substantial portions of the Software. 29 | * 30 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 31 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 32 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 33 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 34 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 35 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 36 | * DEALINGS IN THE SOFTWARE. 37 | */ 38 | 39 | #ifndef LZAV_INCLUDED 40 | #define LZAV_INCLUDED 41 | 42 | #define LZAV_API_VER 0x204 ///< API version; unrelated to source code version. 43 | #define LZAV_VER_STR "5.8" ///< LZAV source code version string. 44 | 45 | /** 46 | * @def LZAV_FMT_MIN 47 | * @brief The minimal data format id supported by the decompressor. A value 48 | * of 3 can be defined externally to reduce the decompressor's code size. 49 | */ 50 | 51 | #if !defined( LZAV_FMT_MIN ) 52 | #define LZAV_FMT_MIN 2 53 | #endif // !defined( LZAV_FMT_MIN ) 54 | 55 | /** 56 | * @def LZAV_NS_CUSTOM 57 | * @brief If this macro is defined externally, all symbols will be placed 58 | * into the namespace specified by the macro, and won't be exported to the 59 | * global namespace. WARNING: if the defined value of the macro is empty, the 60 | * symbols will be placed into the global namespace anyway. 61 | */ 62 | 63 | /** 64 | * @def LZAV_NOEX 65 | * @brief Macro that defines the "noexcept" function specifier for the C++ 66 | * environment. 67 | */ 68 | 69 | /** 70 | * @def LZAV_NULL 71 | * @brief Macro that defines the "nullptr" value for C++ guidelines 72 | * compliance. 73 | */ 74 | 75 | /** 76 | * @def LZAV_NS 77 | * @brief Macro that defines an actual implementation namespace in the C++ 78 | * environment, with export of relevant symbols to the global namespace 79 | * (if @ref LZAV_NS_CUSTOM is undefined). 80 | */ 81 | 82 | /** 83 | * @def LZAV_MALLOC 84 | * @brief Macro defining the call to the memory allocation function. 85 | * 86 | * Can be defined externally if the standard `malloc` is unavailable, or if 87 | * the use of the `operator new[]` is undesired in a C++ environment. 88 | * The implementation must return a `T*` pointer aligned to 4 bytes, or 89 | * preferably, 16 bytes. 90 | * 91 | * The called function should have the "noexcept" or "throw()" specifier. 92 | * 93 | * @param s Allocation size, in bytes; a multiple of `sizeof( T )`. 94 | * @param T Allocation element type, for a C++ environment. 95 | */ 96 | 97 | /** 98 | * @def LZAV_FREE 99 | * @brief Macro defining the call to the memory free function. 100 | * 101 | * Can be defined externally if the standard `free` is unavailable, or if 102 | * the `operator delete[]` is undesired in a C++ environment. 103 | * 104 | * @param p Memory block pointer to free. 105 | */ 106 | 107 | /** 108 | * @def LZAV_DEF_MALLOC 109 | * @brief Macro denotes that the default memory allocator is being used. 110 | */ 111 | 112 | #if !defined( LZAV_MALLOC ) 113 | 114 | #if defined( LZAV_FREE ) 115 | #error LZAV: the LZAV_FREE is defined while the LZAV_MALLOC is not. 116 | #endif // defined( LZAV_FREE ) 117 | 118 | #define LZAV_DEF_MALLOC 119 | 120 | #else // !defined( LZAV_MALLOC ) 121 | 122 | #if !defined( LZAV_FREE ) 123 | #error LZAV: the LZAV_MALLOC is defined while the LZAV_FREE is not. 124 | #endif // !defined( LZAV_FREE ) 125 | 126 | #endif // !defined( LZAV_MALLOC ) 127 | 128 | #if defined( __cplusplus ) 129 | 130 | #include 131 | 132 | #if __cplusplus >= 201103L 133 | 134 | #include 135 | 136 | #define LZAV_NOEX noexcept 137 | #define LZAV_NULL nullptr 138 | 139 | #if defined( LZAV_DEF_MALLOC ) 140 | #include 141 | 142 | #define LZAV_MALLOC( s, T ) \ 143 | new( std :: nothrow ) T[ s / sizeof( T )] 144 | 145 | #define LZAV_FREE( p ) delete[] p 146 | #endif // !defined( LZAV_DEF_MALLOC ) 147 | 148 | #else // __cplusplus >= 201103L 149 | 150 | #include 151 | 152 | #define LZAV_NOEX throw() 153 | #define LZAV_NULL NULL 154 | 155 | #if defined( LZAV_DEF_MALLOC ) 156 | #include 157 | 158 | #define LZAV_MALLOC( s, T ) (T*) std :: malloc( s ) 159 | #define LZAV_FREE( p ) std :: free( p ) 160 | #endif // !defined( LZAV_DEF_MALLOC ) 161 | 162 | #endif // __cplusplus >= 201103L 163 | 164 | #if defined( LZAV_NS_CUSTOM ) 165 | #define LZAV_NS LZAV_NS_CUSTOM 166 | #else // defined( LZAV_NS_CUSTOM ) 167 | #define LZAV_NS lzav 168 | #endif // defined( LZAV_NS_CUSTOM ) 169 | 170 | #else // defined( __cplusplus ) 171 | 172 | #include 173 | #include 174 | 175 | #define LZAV_NOEX 176 | #define LZAV_NULL NULL 177 | 178 | #if defined( LZAV_DEF_MALLOC ) 179 | #include 180 | 181 | #define LZAV_MALLOC( s, T ) (T*) malloc( s ) 182 | #define LZAV_FREE( p ) free( p ) 183 | #endif // !defined( LZAV_DEF_MALLOC ) 184 | 185 | #endif // defined( __cplusplus ) 186 | 187 | #if SIZE_MAX < 0xFFFFFFFFU 188 | 189 | #error LZAV: the platform or the compiler has an incompatible size_t type. 190 | 191 | #endif // size_t check 192 | 193 | /** 194 | * @def LZAV_X86 195 | * @brief Macro that is defined if an `x86` or `x86_64` platform is detected. 196 | */ 197 | 198 | #if defined( i386 ) || defined( __i386 ) || defined( __i386__ ) || \ 199 | defined( _X86_ ) || defined( __x86_64 ) || defined( __x86_64__ ) || \ 200 | defined( __amd64 ) || defined( __amd64__ ) || defined( _M_IX86 ) || \ 201 | ( defined( _M_AMD64 ) && !defined( _M_ARM64EC )) 202 | 203 | #define LZAV_X86 204 | 205 | #endif // x86 platform check 206 | 207 | /** 208 | * @def LZAV_LITTLE_ENDIAN 209 | * @brief Endianness definition macro that can be used as a logical constant. 210 | * It always equals 0 if the C++20 `endian` is in use. 211 | */ 212 | 213 | /** 214 | * @def LZAV_COND_EC( vl, vb ) 215 | * @brief Macro that emits either `vl` or `vb`, depending on the platform's 216 | * endianness. 217 | */ 218 | 219 | #if ( defined( __BYTE_ORDER__ ) && \ 220 | __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ ) || \ 221 | ( defined( __BYTE_ORDER ) && __BYTE_ORDER == __LITTLE_ENDIAN ) || \ 222 | defined( __LITTLE_ENDIAN__ ) || defined( _LITTLE_ENDIAN ) || \ 223 | defined( LZAV_X86 ) || defined( _WIN32 ) || defined( _M_ARM ) || \ 224 | defined( _M_ARM64EC ) 225 | 226 | #define LZAV_LITTLE_ENDIAN 1 227 | 228 | #elif ( defined( __BYTE_ORDER__ ) && \ 229 | __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ ) || \ 230 | ( defined( __BYTE_ORDER ) && __BYTE_ORDER == __BIG_ENDIAN ) || \ 231 | defined( __BIG_ENDIAN__ ) || defined( _BIG_ENDIAN ) || \ 232 | defined( __SYSC_ZARCH__ ) || defined( __zarch__ ) || \ 233 | defined( __s390x__ ) || defined( __sparc ) || defined( __sparc__ ) 234 | 235 | #define LZAV_LITTLE_ENDIAN 0 236 | #define LZAV_COND_EC( vl, vb ) ( vb ) 237 | 238 | #elif defined( __cplusplus ) && __cplusplus >= 202002L 239 | 240 | #include 241 | 242 | #define LZAV_LITTLE_ENDIAN 0 243 | #define LZAV_COND_EC( vl, vb ) ( std :: endian :: native == \ 244 | std :: endian :: little ? vl : vb ) 245 | 246 | #else // defined( __cplusplus ) 247 | 248 | #warning LZAV: cannot determine endianness, assuming little-endian. 249 | 250 | #define LZAV_LITTLE_ENDIAN 1 251 | 252 | #endif // defined( __cplusplus ) 253 | 254 | /** 255 | * @def LZAV_PTR32 256 | * @brief Macro that denotes that pointers are likely 32-bit (pointer overflow 257 | * checks are required). 258 | */ 259 | 260 | #if SIZE_MAX <= 0xFFFFFFFFU && \ 261 | ( !defined( UINTPTR_MAX ) || UINTPTR_MAX <= 0xFFFFFFFFU ) 262 | 263 | #define LZAV_PTR32 264 | 265 | #endif // 32-bit pointers check 266 | 267 | /** 268 | * @def LZAV_ARCH64 269 | * @brief Macro that denotes the availability of 64-bit instructions. 270 | */ 271 | 272 | #if defined( __LP64__ ) || defined( _LP64 ) || !defined( LZAV_PTR32 ) || \ 273 | defined( __x86_64__ ) || defined( __aarch64__ ) || \ 274 | defined( _M_AMD64 ) || defined( _M_ARM64 ) 275 | 276 | #define LZAV_ARCH64 277 | 278 | #endif // 64-bit availability check 279 | 280 | /** 281 | * @def LZAV_LONG_COPY 282 | * @brief Macro that permits the use of runs of 16-byte `memcpy` on platforms 283 | * where this should not cause performance issues. 284 | */ 285 | 286 | #if defined( LZAV_ARCH64 ) || defined( __SSE__ ) || defined( __ARM_NEON ) || \ 287 | ( defined( _M_IX86_FP ) && _M_IX86_FP >= 1 ) || \ 288 | defined( __VEC__ ) || defined( __ALTIVEC__ ) || \ 289 | defined( __wasm_simd128__ ) 290 | 291 | #define LZAV_LONG_COPY 292 | 293 | #endif // Long copy check 294 | 295 | /** 296 | * @def LZAV_GCC_BUILTINS 297 | * @brief Macro that denotes the availability of GCC-style built-in functions. 298 | */ 299 | 300 | /** 301 | * @def LZAV_CPP_BIT 302 | * @brief Macro that denotes the availability of C++20 `bit` functions. 303 | */ 304 | 305 | #if defined( __GNUC__ ) || defined( __clang__ ) || \ 306 | defined( __IBMC__ ) || defined( __IBMCPP__ ) || \ 307 | defined( __COMPCERT__ ) || ( defined( __INTEL_COMPILER ) && \ 308 | __INTEL_COMPILER >= 1300 && !defined( _MSC_VER )) 309 | 310 | #define LZAV_GCC_BUILTINS 311 | 312 | #elif defined( __cplusplus ) && __cplusplus >= 202002L 313 | 314 | #include 315 | 316 | #define LZAV_CPP_BIT 317 | 318 | #elif defined( _MSC_VER ) 319 | 320 | #include // For _BitScanForward. 321 | 322 | #endif // defined( _MSC_VER ) 323 | 324 | /** 325 | * @def LZAV_IEC32( x ) 326 | * @brief In-place endianness-correction macro for singular 32-bit variables. 327 | * 328 | * @param x The value to correct in-place. 329 | */ 330 | 331 | #if LZAV_LITTLE_ENDIAN 332 | 333 | #define LZAV_COND_EC( vl, vb ) ( vl ) 334 | #define LZAV_IEC32( x ) (void) 0 335 | 336 | #else // LZAV_LITTLE_ENDIAN 337 | 338 | #if defined( LZAV_GCC_BUILTINS ) 339 | 340 | #define LZAV_IEC32( x ) x = LZAV_COND_EC( x, __builtin_bswap32( x )) 341 | 342 | #elif defined( _MSC_VER ) 343 | 344 | #if defined( __cplusplus ) 345 | #include 346 | #else // defined( __cplusplus ) 347 | #include 348 | #endif // defined( __cplusplus ) 349 | 350 | #define LZAV_IEC32( x ) x = LZAV_COND_EC( x, _byteswap_ulong( x )) 351 | 352 | #elif defined( __cplusplus ) && __cplusplus >= 202302L 353 | 354 | #define LZAV_IEC32( x ) x = LZAV_COND_EC( x, std :: byteswap( x )) 355 | 356 | #else // defined( __cplusplus ) 357 | 358 | #define LZAV_IEC32( x ) x = (uint32_t) LZAV_COND_EC( x, \ 359 | x >> 24 | \ 360 | ( x & 0x00FF0000 ) >> 8 | \ 361 | ( x & 0x0000FF00 ) << 8 | \ 362 | x << 24 ) 363 | 364 | #endif // defined( __cplusplus ) 365 | 366 | #endif // LZAV_LITTLE_ENDIAN 367 | 368 | /** 369 | * @def LZAV_LIKELY( x ) 370 | * @brief Likelihood macro that is used for manually-guided 371 | * micro-optimization. 372 | * 373 | * @param x An expression that is likely to be evaluated to 1. 374 | */ 375 | 376 | /** 377 | * @def LZAV_UNLIKELY( x ) 378 | * @brief Unlikelihood macro that is used for manually-guided 379 | * micro-optimization. 380 | * 381 | * @param x An expression that is unlikely to be evaluated to 1. 382 | */ 383 | 384 | #if defined( LZAV_GCC_BUILTINS ) 385 | 386 | #define LZAV_LIKELY( x ) ( __builtin_expect( x, 1 )) 387 | #define LZAV_UNLIKELY( x ) ( __builtin_expect( x, 0 )) 388 | 389 | #elif defined( __cplusplus ) && __cplusplus >= 202002L 390 | 391 | #define LZAV_LIKELY( x ) ( x ) [[likely]] 392 | #define LZAV_UNLIKELY( x ) ( x ) [[unlikely]] 393 | 394 | #else // Likelihood macros 395 | 396 | #define LZAV_LIKELY( x ) ( x ) 397 | #define LZAV_UNLIKELY( x ) ( x ) 398 | 399 | #endif // Likelihood macros 400 | 401 | /** 402 | * @def LZAV_RESTRICT 403 | * @brief Macro that defines the "restrict" variable specifier. 404 | */ 405 | 406 | #if defined( LZAV_GCC_BUILTINS ) || defined( _MSC_VER ) 407 | 408 | #define LZAV_RESTRICT __restrict 409 | 410 | #elif !defined( __cplusplus ) 411 | 412 | #define LZAV_RESTRICT restrict 413 | 414 | #else // !defined( __cplusplus ) 415 | 416 | #define LZAV_RESTRICT 417 | 418 | #endif // !defined( __cplusplus ) 419 | 420 | /** 421 | * @def LZAV_PREFETCH( a ) 422 | * @brief Memory address prefetch macro to preload data into the CPU cache. 423 | * 424 | * @param a Prefetch address. 425 | */ 426 | 427 | #if defined( LZAV_GCC_BUILTINS ) && !defined( __COMPCERT__ ) 428 | 429 | #define LZAV_PREFETCH( a ) __builtin_prefetch( a, 0, 3 ) 430 | 431 | #elif defined( _MSC_VER ) && !defined( __INTEL_COMPILER ) && \ 432 | defined( LZAV_X86 ) 433 | 434 | #include 435 | 436 | #define LZAV_PREFETCH( a ) _mm_prefetch( (const char*) ( a ), _MM_HINT_T0 ) 437 | 438 | #else // defined( _MSC_VER ) 439 | 440 | #define LZAV_PREFETCH( a ) (void) 0 441 | 442 | #endif // defined( _MSC_VER ) 443 | 444 | /** 445 | * @def LZAV_STATIC 446 | * @brief Macro that defines a function as "static". 447 | */ 448 | 449 | #if ( defined( __cplusplus ) && __cplusplus >= 201703L ) || \ 450 | ( defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 202311L ) 451 | 452 | #define LZAV_STATIC [[maybe_unused]] static 453 | 454 | #elif defined( LZAV_GCC_BUILTINS ) 455 | 456 | #define LZAV_STATIC static __attribute__((unused)) 457 | 458 | #else // defined( LZAV_GCC_BUILTINS ) 459 | 460 | #define LZAV_STATIC static 461 | 462 | #endif // defined( LZAV_GCC_BUILTINS ) 463 | 464 | /** 465 | * @def LZAV_INLINE 466 | * @brief Macro that defines a function as inlinable at the compiler's 467 | * discretion. 468 | */ 469 | 470 | #define LZAV_INLINE LZAV_STATIC inline 471 | 472 | /** 473 | * @def LZAV_INLINE_F 474 | * @brief Macro to force code inlining. 475 | */ 476 | 477 | #if defined( LZAV_GCC_BUILTINS ) 478 | 479 | #define LZAV_INLINE_F LZAV_INLINE __attribute__((always_inline)) 480 | 481 | #elif defined( _MSC_VER ) 482 | 483 | #define LZAV_INLINE_F LZAV_STATIC __forceinline 484 | 485 | #else // defined( _MSC_VER ) 486 | 487 | #define LZAV_INLINE_F LZAV_INLINE 488 | 489 | #endif // defined( _MSC_VER ) 490 | 491 | /** 492 | * @def LZAV_NO_INLINE 493 | * @brief Macro that defines a function as not inlinable. 494 | */ 495 | 496 | #if defined( __cplusplus ) 497 | 498 | #define LZAV_NO_INLINE LZAV_STATIC 499 | 500 | #else // defined( __cplusplus ) 501 | 502 | #define LZAV_NO_INLINE LZAV_INLINE 503 | 504 | #endif // defined( __cplusplus ) 505 | 506 | #if defined( LZAV_NS ) 507 | 508 | namespace LZAV_NS { 509 | 510 | using std :: memcpy; 511 | using std :: memset; 512 | using std :: size_t; 513 | 514 | #if __cplusplus >= 201103L 515 | 516 | using std :: uint16_t; 517 | using std :: uint32_t; 518 | using uint8_t = unsigned char; ///< For C++ type aliasing compliance. 519 | 520 | #if defined( LZAV_ARCH64 ) 521 | using std :: uint64_t; 522 | #endif // defined( LZAV_ARCH64 ) 523 | 524 | #endif // __cplusplus >= 201103L 525 | 526 | namespace enum_wrapper { 527 | 528 | #endif // defined( LZAV_NS ) 529 | 530 | /** 531 | * @brief Decompression error codes. 532 | */ 533 | 534 | enum LZAV_ERROR 535 | { 536 | LZAV_E_PARAMS = -1, ///< Incorrect function parameters. 537 | LZAV_E_SRCOOB = -2, ///< Source buffer out-of-bounds error. 538 | LZAV_E_DSTOOB = -3, ///< Destination buffer out-of-bounds error. 539 | LZAV_E_REFOOB = -4, ///< Back-reference out-of-bounds error. 540 | LZAV_E_DSTLEN = -5, ///< Decompressed length mismatch error. 541 | LZAV_E_UNKFMT = -6, ///< Unknown data format or mref error. 542 | LZAV_E_PTROVR = -7 ///< Pointer overflow error. 543 | }; 544 | 545 | #if defined( LZAV_NS ) 546 | 547 | } // namespace enum_wrapper 548 | 549 | using namespace enum_wrapper; 550 | 551 | #endif // defined( LZAV_NS ) 552 | 553 | /** 554 | * @brief Compression algorithm parameters. 555 | */ 556 | 557 | enum LZAV_PARAM 558 | { 559 | LZAV_WIN_LEN = ( 1 << 21 ), ///< The LZ77 window length, in bytes. 560 | LZAV_LIT_FIN = 9, ///< The number of literals required at finish. 561 | LZAV_OFS_MIN = 8, ///< The minimal reference offset to use. 562 | LZAV_OFS_TH1 = ( 1 << 10 ) - 1, ///< Reference offset threshold 1. 563 | LZAV_OFS_TH2 = ( 1 << 15 ) - 1, ///< Reference offset threshold 2. 564 | LZAV_MR5_THR = ( 1 << 18 ), ///< `srclen` threshold to use `mref=5`. 565 | LZAV_FMT_CUR = 3 ///< The data format identifier used by the compressor. 566 | }; 567 | 568 | /** 569 | * @brief Data match length finding function. 570 | * 571 | * This function finds the number of continuously-matching leading bytes 572 | * between two buffers. This function is well-optimized for a wide variety of 573 | * compilers and platforms. 574 | * 575 | * @param p1 Pointer to buffer 1. 576 | * @param p2 Pointer to buffer 2. 577 | * @param ml The maximal number of bytes to match. 578 | * @param o The initial offset, can be greater than `ml`. 579 | * @return The number of matching leading bytes, not less than `o`. 580 | */ 581 | 582 | LZAV_INLINE_F size_t lzav_match_len( const uint8_t* const p1, 583 | const uint8_t* const p2, const size_t ml, size_t o ) LZAV_NOEX 584 | { 585 | #if defined( LZAV_ARCH64 ) 586 | 587 | size_t o2 = o + 7; 588 | 589 | while LZAV_LIKELY( o2 < ml ) 590 | { 591 | uint64_t v1, v2, vd; 592 | memcpy( &v1, p1 + o, 8 ); 593 | memcpy( &v2, p2 + o, 8 ); 594 | vd = v1 ^ v2; 595 | 596 | if( vd != 0 ) 597 | { 598 | #if defined( LZAV_GCC_BUILTINS ) 599 | 600 | return( o + (size_t) ( LZAV_COND_EC( 601 | __builtin_ctzll( vd ), __builtin_clzll( vd )) >> 3 )); 602 | 603 | #elif defined( LZAV_CPP_BIT ) 604 | 605 | return( o + (size_t) ( LZAV_COND_EC( 606 | std :: countr_zero( vd ), std :: countl_zero( vd )) >> 3 )); 607 | 608 | #elif defined( _MSC_VER ) 609 | 610 | unsigned long i; 611 | _BitScanForward64( &i, (unsigned __int64) vd ); 612 | 613 | return( o + ( i >> 3 )); 614 | 615 | #else // defined( _MSC_VER ) 616 | 617 | #if !LZAV_LITTLE_ENDIAN 618 | const uint64_t sw = vd >> 32 | vd << 32; 619 | const uint64_t sw2 = 620 | ( sw & (uint64_t) 0xFFFF0000FFFF0000 ) >> 16 | 621 | ( sw & (uint64_t) 0x0000FFFF0000FFFF ) << 16; 622 | vd = LZAV_COND_EC( vd, 623 | ( sw2 & (uint64_t) 0xFF00FF00FF00FF00 ) >> 8 | 624 | ( sw2 & (uint64_t) 0x00FF00FF00FF00FF ) << 8 ); 625 | #endif // !LZAV_LITTLE_ENDIAN 626 | 627 | const uint64_t m = (uint64_t) 0x0101010101010101; 628 | 629 | return( o + (((( vd ^ ( vd - 1 )) & ( m - 1 )) * m ) >> 56 )); 630 | 631 | #endif // defined( _MSC_VER ) 632 | } 633 | 634 | o2 += 8; 635 | o += 8; 636 | } 637 | 638 | // At most 7 bytes left. 639 | 640 | if LZAV_LIKELY( o + 3 < ml ) 641 | { 642 | 643 | #else // defined( LZAV_ARCH64 ) 644 | 645 | size_t o2 = o + 3; 646 | 647 | while LZAV_LIKELY( o2 < ml ) 648 | { 649 | 650 | #endif // defined( LZAV_ARCH64 ) 651 | 652 | uint32_t v1, v2, vd; 653 | memcpy( &v1, p1 + o, 4 ); 654 | memcpy( &v2, p2 + o, 4 ); 655 | vd = v1 ^ v2; 656 | 657 | if( vd != 0 ) 658 | { 659 | #if defined( LZAV_GCC_BUILTINS ) 660 | 661 | return( o + (size_t) ( LZAV_COND_EC( 662 | __builtin_ctz( vd ), __builtin_clz( vd )) >> 3 )); 663 | 664 | #elif defined( LZAV_CPP_BIT ) 665 | 666 | return( o + (size_t) ( LZAV_COND_EC( 667 | std :: countr_zero( vd ), std :: countl_zero( vd )) >> 3 )); 668 | 669 | #elif defined( _MSC_VER ) 670 | 671 | unsigned long i; 672 | _BitScanForward( &i, (unsigned long) vd ); 673 | 674 | return( o + ( i >> 3 )); 675 | 676 | #else // defined( _MSC_VER ) 677 | 678 | LZAV_IEC32( vd ); 679 | const uint32_t m = 0x01010101; 680 | 681 | return( o + (((( vd ^ ( vd - 1 )) & ( m - 1 )) * m ) >> 24 )); 682 | 683 | #endif // defined( _MSC_VER ) 684 | } 685 | 686 | o2 += 4; 687 | o += 4; 688 | } 689 | 690 | // At most 3 bytes left. 691 | 692 | if( o < ml ) 693 | { 694 | if( p1[ o ] != p2[ o ]) 695 | { 696 | return( o ); 697 | } 698 | 699 | if( ++o < ml ) 700 | { 701 | if( p1[ o ] != p2[ o ]) 702 | { 703 | return( o ); 704 | } 705 | 706 | if( ++o < ml ) 707 | { 708 | if( p1[ o ] != p2[ o ]) 709 | { 710 | return( o ); 711 | } 712 | } 713 | } 714 | } 715 | 716 | return( ml ); 717 | } 718 | 719 | /** 720 | * @brief Data match length finding function in the reverse direction. 721 | * 722 | * Note that the function assumes `p1[ -1 ] == p2[ -1 ]`. 723 | * 724 | * @param p1 The origin pointer to buffer 1. 725 | * @param p2 The origin pointer to buffer 2. 726 | * @param ml The maximal number of bytes to back-match. Cannot be 0. 727 | * @return The number of matching prior bytes, not including the origin 728 | * position. 729 | */ 730 | 731 | LZAV_INLINE_F size_t lzav_match_len_r1( const uint8_t* p1, const uint8_t* p2, 732 | const size_t ml ) LZAV_NOEX 733 | { 734 | if( ml != 1 ) 735 | { 736 | const uint8_t* const p1s = p1; 737 | const uint8_t* const p1e = p1 - ml + 1; 738 | p1--; 739 | 740 | while( p1 > p1e ) 741 | { 742 | uint16_t v1, v2; 743 | memcpy( &v1, p1 - 2, 2 ); 744 | memcpy( &v2, p2 - 3, 2 ); 745 | 746 | const uint32_t vd = (uint32_t) ( v1 ^ v2 ); 747 | 748 | if( vd != 0 ) 749 | { 750 | return( (size_t) ( p1s - p1 + 751 | ( LZAV_COND_EC( vd & 0xFF00, vd & 0x00FF ) == 0 ))); 752 | } 753 | 754 | p1 -= 2; 755 | p2 -= 2; 756 | } 757 | 758 | if( p1 == p1e && p1[ -1 ] != p2[ -2 ]) 759 | { 760 | return( (size_t) ( p1s - p1 )); 761 | } 762 | } 763 | 764 | return( ml ); 765 | } 766 | 767 | /** 768 | * @brief Internal LZAV block header writing function (data format 3). 769 | * 770 | * This internal function writes a block to the output buffer. It can be used 771 | * in custom compression algorithms. 772 | * 773 | * Data format 3. 774 | * 775 | * A "raw" compressed data consists of any quantity of unnumbered "blocks". 776 | * A block starts with a header byte, followed by several optional bytes. 777 | * Bits 4-5 of the header specify the block's type. 778 | * 779 | * CC00LLLL: literal block (1-6 bytes). `LLLL` is the literal length. 780 | * 781 | * OO01RRRR: 10-bit offset block (2-7 bytes). `RRRR` is the reference length. 782 | * 783 | * OO10RRRR: 15-bit offset block (3-8 bytes). 3 offset carry bits. 784 | * 785 | * OO11RRRR: 21-bit offset block (4-9 bytes). 5 offset carry bits. 786 | * 787 | * If `LLLL` or `RRRR` equals 0, a value of 16 is assumed, and an additional 788 | * length byte follows. If, in a block, this additional byte's highest bit 789 | * is 1, one more length byte follows that defines the higher bits of the 790 | * length (up to 4 bytes). In a reference block, additional length bytes 791 | * follow the offset bytes. `CC` is a reference offset carry value (additional 792 | * 2 lowest bits of the offset of the next reference block). Block types 2 and 793 | * 3 include more carry bits (in the highest bits of the offset byte). 794 | * 795 | * Note that reference offsets can be much larger than the @ref LZAV_WIN_LEN 796 | * constant. This is due to offset carry bits, which create a dynamic LZ77 797 | * window instead of a fixed-length one. In practice, this encoding scheme 798 | * covers 99.5% of the offsets in the compressor's hash-table at any given 799 | * time. 800 | * 801 | * The overall compressed data are prefixed with a byte whose lower 4 bits 802 | * contain the minimal reference length (`mref`), and the highest 4 bits 803 | * contain the data format identifier. The compressed data always finish with 804 | * @ref LZAV_LIT_FIN literals. The lzav_write_fin_3() function should be used 805 | * to finalize compression. 806 | * 807 | * Except for the last block, a literal block is always followed by a 808 | * reference block. 809 | * 810 | * @param op Output buffer pointer. 811 | * @param lc Literal length, in bytes. 812 | * @param rc Reference length, in bytes, not less than `mref`. 813 | * @param d Reference offset, in bytes. Should not be less than 814 | * @ref LZAV_OFS_MIN; it may be less than `rc`, permitting overlapped copying. 815 | * @param ipa Literals anchor pointer. 816 | * @param cbpp Pointer to the pointer to the latest offset carry block header. 817 | * Cannot be 0, but the contained pointer can be 0 (the initial value). 818 | * @param cshp Pointer to the offset carry shift. 819 | * @param mref Minimal reference length, in bytes, used by the compression 820 | * algorithm. 821 | * @return Incremented output buffer pointer. 822 | */ 823 | 824 | LZAV_INLINE_F uint8_t* lzav_write_blk_3( uint8_t* LZAV_RESTRICT op, 825 | const size_t lc, size_t rc, size_t d, 826 | const uint8_t* LZAV_RESTRICT const ipa, uint8_t** const cbpp, 827 | int* const cshp, const size_t mref ) LZAV_NOEX 828 | { 829 | // Perform offset carry to a previous block (`csh` may be zero). 830 | 831 | const int csh = *cshp; 832 | rc = rc + 1 - mref; 833 | const size_t dc = ( d << 8 ) >> csh; 834 | d >>= csh; 835 | **cbpp |= (uint8_t) dc; 836 | 837 | if LZAV_UNLIKELY( lc != 0 ) 838 | { 839 | // Write a literal block. 840 | 841 | const size_t cv = d << 6; // Offset carry value in the literal block. 842 | d >>= 2; 843 | 844 | if LZAV_LIKELY( lc < 9 ) 845 | { 846 | *op = (uint8_t) ( cv | lc ); 847 | 848 | memcpy( op + 1, ipa, 8 ); 849 | op += lc + 1; 850 | } 851 | else 852 | if LZAV_LIKELY( lc < 16 ) 853 | { 854 | *op = (uint8_t) ( cv | lc ); 855 | 856 | memcpy( op + 1, ipa, 16 ); 857 | op += lc + 1; 858 | } 859 | else 860 | if( lc < 33 ) 861 | { 862 | const uint16_t ov = (uint16_t) LZAV_COND_EC( 863 | ( lc - 16 ) << 8 | ( cv & 0xFF ), cv << 8 | ( lc - 16 )); 864 | 865 | memcpy( op, &ov, 2 ); 866 | 867 | memcpy( op + 2, ipa, 16 ); 868 | memcpy( op + 18, ipa + 16, 16 ); 869 | op += lc + 2; 870 | } 871 | else 872 | { 873 | op[ 0 ] = (uint8_t) cv; 874 | 875 | size_t lcw = lc - 16; 876 | 877 | while( lcw > 127 ) 878 | { 879 | op[ 1 ] = (uint8_t) ( 0x80 | lcw ); 880 | lcw >>= 7; 881 | op++; 882 | } 883 | 884 | op[ 1 ] = (uint8_t) lcw; 885 | op += 2; 886 | 887 | memcpy( op, ipa, lc ); 888 | op += lc; 889 | } 890 | } 891 | 892 | // Write a reference block. 893 | 894 | static const int ocsh[ 4 ] = { 0, 0, 3, 5 }; 895 | const size_t bt = (size_t) 1 + ( d > LZAV_OFS_TH1 ) + ( d > LZAV_OFS_TH2 ); 896 | 897 | uint32_t ov = (uint32_t) ( d << 6 | bt << 4 ); 898 | uint8_t* opbt = op + bt; 899 | *cshp = ocsh[ bt ]; 900 | *cbpp = opbt; 901 | 902 | if LZAV_LIKELY( rc < 16 ) 903 | { 904 | ov |= (uint32_t) rc; 905 | 906 | LZAV_IEC32( ov ); 907 | memcpy( op, &ov, 4 ); 908 | 909 | return( opbt + 1 ); 910 | } 911 | 912 | LZAV_IEC32( ov ); 913 | rc -= 16; 914 | memcpy( op, &ov, 4 ); 915 | 916 | if LZAV_LIKELY( rc < 128 ) 917 | { 918 | opbt[ 1 ] = (uint8_t) rc; 919 | return( opbt + 2 ); 920 | } 921 | 922 | do 923 | { 924 | opbt[ 1 ] = (uint8_t) ( 0x80 | rc ); 925 | rc >>= 7; 926 | opbt++; 927 | } while( rc > 127 ); 928 | 929 | opbt[ 1 ] = (uint8_t) rc; 930 | return( opbt + 2 ); 931 | } 932 | 933 | /** 934 | * @brief Internal LZAV finishing function (data format 3). 935 | * 936 | * This internal function writes finishing literal block(s) to the output 937 | * buffer. It can be used in custom compression algorithms. 938 | * 939 | * Data format 3. 940 | * 941 | * @param op Output buffer pointer. 942 | * @param lc Literal length, in bytes. Not less than @ref LZAV_LIT_FIN. 943 | * @param ipa Literals anchor pointer. 944 | * @return Incremented output buffer pointer. 945 | */ 946 | 947 | LZAV_INLINE_F uint8_t* lzav_write_fin_3( uint8_t* LZAV_RESTRICT op, 948 | const size_t lc, const uint8_t* LZAV_RESTRICT const ipa ) LZAV_NOEX 949 | { 950 | size_t lcw = lc; 951 | 952 | if( lc > 15 ) 953 | { 954 | *op = 0; 955 | op++; 956 | 957 | lcw -= 16; 958 | 959 | while( lcw > 127 ) 960 | { 961 | *op = (uint8_t) ( 0x80 | lcw ); 962 | lcw >>= 7; 963 | op++; 964 | } 965 | } 966 | 967 | *op = (uint8_t) lcw; 968 | op++; 969 | 970 | memcpy( op, ipa, lc ); 971 | return( op + lc ); 972 | } 973 | 974 | /** 975 | * @brief Function returns the buffer size required for the minimal reference 976 | * length of 5. 977 | * 978 | * @param srclen The length of the source data to be compressed. 979 | * @return The required allocation size for the destination compression 980 | * buffer. Always a positive value. 981 | */ 982 | 983 | LZAV_INLINE_F int lzav_compress_bound_mref5( const int srclen ) LZAV_NOEX 984 | { 985 | if( srclen <= 0 ) 986 | { 987 | return( 16 ); 988 | } 989 | 990 | const int l2 = srclen / ( 16 + 5 ); 991 | 992 | return(( srclen - l2 * 5 + 15 ) / 16 * 2 - l2 + srclen + 16 ); 993 | } 994 | 995 | /** 996 | * @brief Function returns the buffer size required for the minimal reference 997 | * length of 6. 998 | * 999 | * @param srclen The length of the source data to be compressed. 1000 | * @return The required allocation size for the destination compression 1001 | * buffer. Always a positive value. 1002 | */ 1003 | 1004 | LZAV_INLINE_F int lzav_compress_bound_mref6( const int srclen ) LZAV_NOEX 1005 | { 1006 | if( srclen <= 0 ) 1007 | { 1008 | return( 16 ); 1009 | } 1010 | 1011 | const int k = 16 + 127 + 1; 1012 | const int l2 = srclen / ( k + 6 ); 1013 | 1014 | return(( srclen - l2 * 6 + k - 1 ) / k * 2 - l2 + srclen + 16 ); 1015 | } 1016 | 1017 | /** 1018 | * @brief Function returns the buffer size required for LZAV compression. 1019 | * 1020 | * @param srclen The length of the source data to be compressed. 1021 | * @return The required allocation size for the destination compression 1022 | * buffer. Always a positive value. 1023 | */ 1024 | 1025 | LZAV_INLINE_F int lzav_compress_bound( const int srclen ) LZAV_NOEX 1026 | { 1027 | if( srclen < LZAV_MR5_THR ) 1028 | { 1029 | return( lzav_compress_bound_mref5( srclen )); 1030 | } 1031 | else 1032 | { 1033 | return( lzav_compress_bound_mref6( srclen )); 1034 | } 1035 | } 1036 | 1037 | /** 1038 | * @brief Function returns the buffer size required for the higher-ratio LZAV 1039 | * compression. 1040 | * 1041 | * @param srclen The length of the source data to be compressed. 1042 | * @return The required allocation size for the destination compression 1043 | * buffer. Always a positive value. 1044 | */ 1045 | 1046 | LZAV_INLINE_F int lzav_compress_bound_hi( const int srclen ) LZAV_NOEX 1047 | { 1048 | return( lzav_compress_bound_mref5( srclen )); 1049 | } 1050 | 1051 | /** 1052 | * @brief Hash-table initialization function. 1053 | * 1054 | * This function initializes the hash-table by replicating the contents of the 1055 | * specified tuple value. 1056 | * 1057 | * @param[out] ht Hash-table pointer. 1058 | * @param htsize Hash-table size. The size should be a power of 2 value, not 1059 | * less than 64 bytes. 1060 | * @param[in] initv Pointer to an initialized 8-byte tuple. 1061 | */ 1062 | 1063 | LZAV_INLINE_F void lzav_ht_init( uint8_t* LZAV_RESTRICT const ht, 1064 | const size_t htsize, const uint32_t* LZAV_RESTRICT const initv ) LZAV_NOEX 1065 | { 1066 | memcpy( ht, initv, 8 ); 1067 | memcpy( ht + 8, initv, 8 ); 1068 | memcpy( ht + 16, ht, 16 ); 1069 | memcpy( ht + 32, ht, 16 ); 1070 | memcpy( ht + 48, ht, 16 ); 1071 | 1072 | uint8_t* LZAV_RESTRICT const hte = ht + htsize; 1073 | uint8_t* LZAV_RESTRICT htc = ht + 64; 1074 | 1075 | while LZAV_LIKELY( htc != hte ) 1076 | { 1077 | memcpy( htc, ht, 16 ); 1078 | memcpy( htc + 16, ht + 16, 16 ); 1079 | memcpy( htc + 32, ht + 32, 16 ); 1080 | memcpy( htc + 48, ht + 48, 16 ); 1081 | htc += 64; 1082 | } 1083 | } 1084 | 1085 | /** 1086 | * @brief Calculates a hash value for the specified input words. 1087 | * 1088 | * @param iw1 Input word 1. 1089 | * @param iw2 Input word 2. 1090 | * @param sh Hash value shift, in bits. Should be chosen so that `32-sh` is 1091 | * equal to hash-table's log2 size. 1092 | * @param hmask Hash value mask. 1093 | * @return Masked hash value. 1094 | */ 1095 | 1096 | LZAV_INLINE_F uint32_t lzav_hash( const uint32_t iw1, const uint32_t iw2, 1097 | const int sh, const uint32_t hmask ) LZAV_NOEX 1098 | { 1099 | uint32_t Seed1 = 0x243F6A88; 1100 | uint32_t hval = 0x85A308D3; 1101 | 1102 | Seed1 ^= iw1; 1103 | hval ^= iw2; 1104 | hval *= Seed1; 1105 | hval >>= sh; 1106 | 1107 | return( hval & hmask ); 1108 | } 1109 | 1110 | /** 1111 | * @brief Loads a secondary input word. 1112 | * 1113 | * The function loads a 16- or 8-bit value, depending on the `mref` parameter. 1114 | * This function relies heavily on forced code inlining for performance. 1115 | * Endianness correction is not applied. 1116 | * 1117 | * @param[out] ov Pointer to the variable that receives the loaded value. 1118 | * @param ip Input pointer. 1119 | * @param mref Minimal reference length, in bytes. Only the values 5 and 6 are 1120 | * supported. 1121 | */ 1122 | 1123 | LZAV_INLINE_F void lzav_load_w2( uint16_t* LZAV_RESTRICT const ov, 1124 | const uint8_t* LZAV_RESTRICT const ip, const size_t mref ) LZAV_NOEX 1125 | { 1126 | if( mref == 5 ) 1127 | { 1128 | *ov = *ip; 1129 | } 1130 | else 1131 | { 1132 | memcpy( ov, ip, 2 ); 1133 | } 1134 | } 1135 | 1136 | /** 1137 | * @brief LZAV compression function with an external buffer option. 1138 | * 1139 | * The function performs in-memory data compression using the LZAV compression 1140 | * algorithm and data format. The function produces "raw" compressed data 1141 | * without a header containing the data length, identifier, or checksum. 1142 | * 1143 | * The function relies on forced code inlining meaning its multiple calls 1144 | * throughout the code may increase the code size considerably. It is 1145 | * suggested to wrap the call to this function in a non-inlined function. 1146 | * 1147 | * Note that the compression algorithm and its output on the same source data 1148 | * may differ between LZAV versions, and may differ between big- and 1149 | * little-endian systems. However, decompression of compressed data produced 1150 | * by any prior compressor version will remain possible. 1151 | * 1152 | * @param[in] src Source (uncompressed) data pointer, can be 0 if `srclen` 1153 | * equals 0. Address alignment is unimportant. 1154 | * @param[out] dst Destination (compressed data) buffer pointer. The allocated 1155 | * size should be at least lzav_compress_bound() bytes. Address alignment is 1156 | * unimportant. Should be different from `src`. 1157 | * @param srclen Source data length, in bytes, can be 0: in this case, the 1158 | * compressed length is assumed to be 0 as well. 1159 | * @param dstlen Destination buffer's capacity, in bytes. 1160 | * @param extbuf External buffer to use for the hash-table; set to null for 1161 | * the function to manage memory itself (via the standard `malloc`). Supplying 1162 | * a pre-allocated buffer is useful if compression is performed often during 1163 | * an application's operation: this reduces memory allocation overhead and 1164 | * fragmentation. Note that the access to the supplied buffer is not 1165 | * implicitly thread-safe. Buffer's address must be aligned to 4 bytes. 1166 | * @param extbuflen The capacity of the `extbuf`, in bytes; should be a 1167 | * power-of-2 value. Used as the hash-table size if `extbuf` is null. 1168 | * The capacity should not be less than `4*srclen`, and for the default 1169 | * compression ratio should not be greater than 1 MiB. The same `extbuflen` 1170 | * value can be used for any smaller source data. Using smaller `extbuflen` 1171 | * values reduces the compression ratio and, at the same time, increases the 1172 | * compression speed. This aspect can be utilized on memory-constrained and 1173 | * low-performance processors. 1174 | * @param mref The minimal back-reference length, in bytes. Only 5 and 6 1175 | * values are supported. 1176 | * @return Length of the compressed data, in bytes. Returns 0 if `srclen` is 1177 | * less than or equal to 0, if `dstlen` is too small, if buffer pointers are 1178 | * invalid, or if there is not enough memory. 1179 | */ 1180 | 1181 | LZAV_INLINE_F int lzav_compress( const void* const src, void* const dst, 1182 | const int srclen, const int dstlen, void* const extbuf, 1183 | const int extbuflen, const size_t mref ) LZAV_NOEX 1184 | { 1185 | if(( srclen <= 0 ) | ( src == LZAV_NULL ) | ( dst == LZAV_NULL ) | 1186 | ( src == dst ) | (( mref != 5 ) & ( mref != 6 ))) 1187 | { 1188 | return( 0 ); 1189 | } 1190 | 1191 | if(( mref == 5 && dstlen < lzav_compress_bound_mref5( srclen )) || 1192 | ( mref == 6 && dstlen < lzav_compress_bound_mref6( srclen ))) 1193 | { 1194 | return( 0 ); 1195 | } 1196 | 1197 | uint8_t* op = (uint8_t*) dst; // Destination (compressed data) pointer. 1198 | *op = (uint8_t) ( LZAV_FMT_CUR << 4 | mref ); // Write prefix byte. 1199 | op++; 1200 | 1201 | if( srclen < 16 ) 1202 | { 1203 | // Handle a very short source data. 1204 | 1205 | *op = (uint8_t) srclen; 1206 | op++; 1207 | 1208 | memcpy( op, src, (size_t) srclen ); 1209 | 1210 | if( srclen > LZAV_LIT_FIN - 1 ) 1211 | { 1212 | return( 2 + srclen ); 1213 | } 1214 | 1215 | memset( op + srclen, 0, (size_t) ( LZAV_LIT_FIN - srclen )); 1216 | return( 2 + LZAV_LIT_FIN ); 1217 | } 1218 | 1219 | uint32_t stack_buf[ 2048 ]; // On-stack hash-table. 1220 | uint32_t* alloc_buf = LZAV_NULL; // Hash-table allocated on heap. 1221 | 1222 | uint8_t* LZAV_RESTRICT ht = 1223 | (uint8_t*) stack_buf; // The actual hash-table pointer. 1224 | 1225 | size_t htsize; // Hash-table's size in bytes (power-of-2). 1226 | htsize = ( 1 << 7 ) * sizeof( uint32_t ) * 4; 1227 | 1228 | size_t htsizem; // Maximal hash-table size. 1229 | 1230 | if( extbuf == LZAV_NULL ) 1231 | { 1232 | htsizem = ( extbuflen > 0 ? (size_t) extbuflen : 1 << 20 ); 1233 | } 1234 | else 1235 | { 1236 | htsizem = ( extbuflen > (int) sizeof( stack_buf ) ? 1237 | (size_t) extbuflen : sizeof( stack_buf )); 1238 | } 1239 | 1240 | while( htsize < ( (size_t) srclen << 2 )) 1241 | { 1242 | const size_t htsize2 = htsize << 1; 1243 | 1244 | if( htsize2 > htsizem ) 1245 | { 1246 | break; 1247 | } 1248 | 1249 | htsize = htsize2; 1250 | } 1251 | 1252 | if( htsize > sizeof( stack_buf )) 1253 | { 1254 | if( extbuf == LZAV_NULL ) 1255 | { 1256 | alloc_buf = LZAV_MALLOC( htsize, uint32_t ); 1257 | 1258 | if( alloc_buf == LZAV_NULL ) 1259 | { 1260 | return( 0 ); 1261 | } 1262 | 1263 | ht = (uint8_t*) alloc_buf; 1264 | } 1265 | else 1266 | { 1267 | ht = (uint8_t*) extbuf; 1268 | } 1269 | } 1270 | 1271 | const uint32_t hmask = (uint32_t) (( htsize - 1 ) ^ 15 ); // Hash mask. 1272 | const uint8_t* ip = (const uint8_t*) src; // Source data pointer. 1273 | const uint8_t* const ipe = ip + srclen - LZAV_LIT_FIN; // End pointer. 1274 | const uint8_t* const ipet = ipe - 15 + LZAV_LIT_FIN; // Hashing threshold, 1275 | // avoids I/O OOB. 1276 | const uint8_t* ipa = ip; // Literals anchor pointer. 1277 | 1278 | // Initialize the hash-table. Each hash-table item consists of 2 tuples 1279 | // (4 initial match bytes; 32-bit source data offset). Start at offset 1 1280 | // for non-zero back-match length. 1281 | 1282 | uint32_t initv[ 2 ] = { 0, 1 }; 1283 | ip++; 1284 | memcpy( initv, ip, 4 ); 1285 | 1286 | lzav_ht_init( ht, htsize, initv ); 1287 | 1288 | uint8_t* cbp = op; // Pointer to the latest offset carry block header. 1289 | int csh = 0; // Offset carry shift. 1290 | 1291 | size_t mavg = 100 << 17; // Running average of hash match rate (*2^11). 1292 | // Two-factor average of success (64) multiplied by reference length. 1293 | 1294 | while LZAV_LIKELY( ip < ipet ) 1295 | { 1296 | // Hash source data (endianness is minimally important for compression 1297 | // efficiency). 1298 | 1299 | uint32_t iw1; 1300 | uint16_t iw2, ww2; 1301 | memcpy( &iw1, ip, 4 ); 1302 | lzav_load_w2( &iw2, ip + 4, mref ); 1303 | 1304 | // Hash-table access. 1305 | 1306 | uint32_t* LZAV_RESTRICT hp = (uint32_t*) ( ht + lzav_hash( iw1, iw2, 1307 | 12, hmask )); 1308 | 1309 | uint32_t ipo = (uint32_t) ( ip - (const uint8_t*) src ); 1310 | const uint32_t hw1 = hp[ 0 ]; // Tuple 1's match word. 1311 | 1312 | size_t wpo; // At window offset. 1313 | const uint8_t* wp; // At window pointer. 1314 | const uint8_t* ip0; // `ip` save variable. 1315 | size_t d, ml, rc, lc; 1316 | 1317 | // Find source data in hash-table tuples. 1318 | 1319 | if LZAV_LIKELY( iw1 != hw1 ) 1320 | { 1321 | if LZAV_LIKELY( iw1 != hp[ 2 ]) 1322 | { 1323 | goto _no_match; 1324 | } 1325 | 1326 | wpo = hp[ 3 ]; 1327 | lzav_load_w2( &ww2, (const uint8_t*) src + wpo + 4, mref ); 1328 | 1329 | if LZAV_UNLIKELY( iw2 != ww2 ) 1330 | { 1331 | goto _no_match; 1332 | } 1333 | } 1334 | else 1335 | { 1336 | wpo = hp[ 1 ]; 1337 | lzav_load_w2( &ww2, (const uint8_t*) src + wpo + 4, mref ); 1338 | 1339 | if LZAV_UNLIKELY( iw2 != ww2 ) 1340 | { 1341 | if( iw1 != hp[ 2 ]) 1342 | { 1343 | goto _no_match; 1344 | } 1345 | 1346 | wpo = hp[ 3 ]; 1347 | lzav_load_w2( &ww2, (const uint8_t*) src + wpo + 4, mref ); 1348 | 1349 | if( iw2 != ww2 ) 1350 | { 1351 | goto _no_match; 1352 | } 1353 | } 1354 | } 1355 | 1356 | // Source data and hash-table entry matched. 1357 | 1358 | d = (size_t) ipo - wpo; // Reference offset (distance). 1359 | ml = (size_t) ( ipe - ip ); // Max reference match length. Make sure 1360 | // `LZAV_LIT_FIN` literals remain on finish. 1361 | 1362 | if LZAV_UNLIKELY( d < LZAV_OFS_MIN ) 1363 | { 1364 | // Small offsets may be inefficient. 1365 | 1366 | goto _d_oob; 1367 | } 1368 | 1369 | LZAV_PREFETCH( ip - 2 ); 1370 | 1371 | if LZAV_LIKELY( d > 31 ) 1372 | { 1373 | if LZAV_LIKELY( iw1 == hw1 ) // Replace tuple, or insert. 1374 | { 1375 | hp[ 1 ] = ipo; 1376 | } 1377 | else 1378 | { 1379 | hp[ 2 ] = hw1; 1380 | hp[ 3 ] = hp[ 1 ]; 1381 | hp[ 0 ] = iw1; 1382 | hp[ 1 ] = ipo; 1383 | } 1384 | } 1385 | 1386 | wp = (const uint8_t*) src + wpo; 1387 | rc = lzav_match_len( ip, wp, ml, mref ); 1388 | 1389 | ip0 = ip; 1390 | lc = (size_t) ( ip - ipa ); 1391 | 1392 | if LZAV_UNLIKELY( lc != 0 && ip[ -1 ] == wp[ -1 ]) 1393 | { 1394 | // Try to consume literals by finding a match at a back-position. 1395 | 1396 | ml = lzav_match_len_r1( ip, wp, ( lc < wpo ? lc : wpo )); 1397 | lc -= ml; 1398 | rc += ml; 1399 | ip -= ml; 1400 | } 1401 | 1402 | if LZAV_LIKELY( d < (size_t) LZAV_WIN_LEN << csh << (( lc != 0 ) * 2 )) 1403 | { 1404 | // Update hash-table with 1 skipped position. 1405 | 1406 | memcpy( &iw1, ip0 + 2, 4 ); 1407 | lzav_load_w2( &iw2, ip0 + 6, mref ); 1408 | 1409 | hp = (uint32_t*) ( ht + lzav_hash( iw1, iw2, 12, hmask )); 1410 | ipo += 2; 1411 | mavg -= mavg >> 10; 1412 | 1413 | hp[ 2 ] = hp[ 0 ]; 1414 | hp[ 3 ] = hp[ 1 ]; 1415 | 1416 | ip += rc; 1417 | wp = ipa; 1418 | 1419 | hp[ 0 ] = iw1; 1420 | hp[ 1 ] = ipo; 1421 | 1422 | ipa = ip; 1423 | mavg += rc << 7; 1424 | 1425 | op = lzav_write_blk_3( op, lc, rc, d, wp, &cbp, &csh, mref ); 1426 | continue; 1427 | } 1428 | 1429 | ip = ip0; 1430 | 1431 | _d_oob: 1432 | ip++; 1433 | continue; 1434 | 1435 | _no_match: 1436 | wp = ip; 1437 | hp[ 2 ] = iw1; 1438 | 1439 | mavg -= mavg >> 11; 1440 | ip++; 1441 | 1442 | hp[ 3 ] = ipo; 1443 | 1444 | if( mavg < ( 200 << 10 ) && wp != ipa ) // Speed-up threshold. 1445 | { 1446 | // Advance faster on data which is harder to compress. 1447 | 1448 | ip += 1 + ( ipo & 1 ); // Simple dithering. 1449 | 1450 | if LZAV_UNLIKELY( mavg < ( 130 << 10 )) 1451 | { 1452 | ip++; 1453 | 1454 | if LZAV_UNLIKELY( mavg < ( 100 << 10 )) 1455 | { 1456 | ip += (size_t) 100 - ( mavg >> 10 ); // Gradually faster. 1457 | } 1458 | } 1459 | } 1460 | } 1461 | 1462 | op = lzav_write_fin_3( op, (size_t) ( ipe - ipa + LZAV_LIT_FIN ), ipa ); 1463 | 1464 | LZAV_FREE( alloc_buf ); 1465 | 1466 | return( (int) ( op - (uint8_t*) dst )); 1467 | } 1468 | 1469 | /** 1470 | * @brief Wrapper function for lzav_compress() with `mref` equal to 5. 1471 | * 1472 | * See the lzav_compress() function for a more detailed description. 1473 | * 1474 | * Note that the lzav_compress_bound_mref5() function should be used to obtain 1475 | * the bound size of the `dst` buffer. 1476 | * 1477 | * @param[in] src Source (uncompressed) data pointer. 1478 | * @param[out] dst Destination (compressed data) buffer pointer. The allocated 1479 | * size should be at least lzav_compress_bound() bytes large. 1480 | * @param srclen Source data length, in bytes. 1481 | * @param dstlen Destination buffer's capacity, in bytes. 1482 | * @return The length of the compressed data, in bytes. 1483 | * @param extbuf External buffer to use for the hash-table. 1484 | * @param extbuflen The capacity of the `extbuf`, in bytes. 1485 | * @return Length of the compressed data, in bytes. 1486 | */ 1487 | 1488 | LZAV_NO_INLINE int lzav_compress_mref5( const void* const src, 1489 | void* const dst, const int srclen, const int dstlen, void* const extbuf, 1490 | const int extbuflen ) LZAV_NOEX 1491 | { 1492 | return( lzav_compress( src, dst, srclen, dstlen, extbuf, extbuflen, 5 )); 1493 | } 1494 | 1495 | /** 1496 | * @brief Wrapper function for lzav_compress() with `mref` equal to 6. 1497 | * 1498 | * See the lzav_compress() function for a more detailed description. 1499 | * 1500 | * Note that the lzav_compress_bound_mref6() function should be used to obtain 1501 | * the bound size of the `dst` buffer. 1502 | * 1503 | * @param[in] src Source (uncompressed) data pointer. 1504 | * @param[out] dst Destination (compressed data) buffer pointer. The allocated 1505 | * size should be at least lzav_compress_bound() bytes large. 1506 | * @param srclen Source data length, in bytes. 1507 | * @param dstlen Destination buffer's capacity, in bytes. 1508 | * @return The length of the compressed data, in bytes. 1509 | * @param extbuf External buffer to use for the hash-table. 1510 | * @param extbuflen The capacity of the `extbuf`, in bytes. 1511 | * @return Length of the compressed data, in bytes. 1512 | */ 1513 | 1514 | LZAV_NO_INLINE int lzav_compress_mref6( const void* const src, 1515 | void* const dst, const int srclen, const int dstlen, void* const extbuf, 1516 | const int extbuflen ) LZAV_NOEX 1517 | { 1518 | return( lzav_compress( src, dst, srclen, dstlen, extbuf, extbuflen, 6 )); 1519 | } 1520 | 1521 | /** 1522 | * @brief Default LZAV compression function. 1523 | * 1524 | * The function performs in-memory data compression using the LZAV compression 1525 | * algorithm, with the default settings. 1526 | * 1527 | * See the lzav_compress() function for a more detailed description. 1528 | * 1529 | * @param[in] src Source (uncompressed) data pointer. 1530 | * @param[out] dst Destination (compressed data) buffer pointer. The allocated 1531 | * size should be at least lzav_compress_bound() bytes large. 1532 | * @param srclen Source data length, in bytes. 1533 | * @param dstlen Destination buffer's capacity, in bytes. 1534 | * @return Length of the compressed data, in bytes. Returns 0 if `srclen` is 1535 | * less than or equal to 0, if `dstlen` is too small, or if there is not 1536 | * enough memory. 1537 | */ 1538 | 1539 | LZAV_INLINE_F int lzav_compress_default( const void* const src, 1540 | void* const dst, const int srclen, const int dstlen ) LZAV_NOEX 1541 | { 1542 | if( srclen < LZAV_MR5_THR ) 1543 | { 1544 | return( lzav_compress_mref5( src, dst, srclen, dstlen, 1545 | LZAV_NULL, 0 )); 1546 | } 1547 | else 1548 | { 1549 | return( lzav_compress_mref6( src, dst, srclen, dstlen, 1550 | LZAV_NULL, 0 )); 1551 | } 1552 | } 1553 | 1554 | /** 1555 | * @brief Calculates the estimated LZAV block's size. 1556 | * 1557 | * @param lc Literal count, in bytes. 1558 | * @param d Reference offset. 1559 | * @param csh Carry shift bit count. 1560 | * @return Estimated block size. 1561 | */ 1562 | 1563 | LZAV_INLINE_F size_t lzav_est_blksize( const size_t lc, size_t d, 1564 | const int csh ) LZAV_NOEX 1565 | { 1566 | const int lb = ( lc != 0 ); 1567 | d >>= csh; 1568 | d >>= lb * 2; 1569 | 1570 | return( lc + (size_t) lb + ( lc > 15 ) + 2 + 1571 | ( d > LZAV_OFS_TH1 ) + ( d > LZAV_OFS_TH2 )); 1572 | } 1573 | 1574 | /** 1575 | * @brief Inserts a tuple into a hash-table item. 1576 | * 1577 | * @param hp Pointer to the hash-table item. 1578 | * @param ti0 Offset of tuple 0. 1579 | * @param mti Maximal tuple offset. 1580 | * @param iw1 Initial source bytes. 1581 | * @param ipo Source bytes offset. 1582 | */ 1583 | 1584 | LZAV_INLINE_F void lzav_ht_insert( uint32_t* const hp, size_t ti0, 1585 | const size_t mti, const uint32_t iw1, const uint32_t ipo ) LZAV_NOEX 1586 | { 1587 | ti0 = ( ti0 == 0 ? mti : ti0 - 2 ); 1588 | hp[ ti0 ] = iw1; 1589 | hp[ ti0 + 1 ] = ipo; 1590 | hp[ mti + 3 ] = (uint32_t) ti0; 1591 | } 1592 | 1593 | /** 1594 | * @brief Higher-ratio LZAV compression function (much slower). 1595 | * 1596 | * The function performs in-memory data compression using the higher-ratio 1597 | * LZAV compression algorithm. 1598 | * 1599 | * @param[in] src Source (uncompressed) data pointer. 1600 | * @param[out] dst Destination (compressed data) buffer pointer. The allocated 1601 | * size should be at least lzav_compress_bound_hi() bytes. 1602 | * @param srclen Source data length, in bytes. 1603 | * @param dstlen Destination buffer's capacity, in bytes. 1604 | * @return Length of the compressed data, in bytes. Returns 0 if `srclen` is 1605 | * less than or equal to 0, if `dstlen` is too small, if buffer pointers are 1606 | * invalid, or if there is not enough memory. 1607 | */ 1608 | 1609 | LZAV_NO_INLINE int lzav_compress_hi( const void* const src, void* const dst, 1610 | const int srclen, const int dstlen ) LZAV_NOEX 1611 | { 1612 | if(( srclen <= 0 ) | ( src == LZAV_NULL ) | ( dst == LZAV_NULL ) | 1613 | ( src == dst ) | ( dstlen < lzav_compress_bound_hi( srclen ))) 1614 | { 1615 | return( 0 ); 1616 | } 1617 | 1618 | const size_t mref = 5; // Minimal reference length, in bytes. 1619 | 1620 | uint8_t* op = (uint8_t*) dst; // Destination (compressed data) pointer. 1621 | *op = (uint8_t) ( LZAV_FMT_CUR << 4 | mref ); // Write prefix byte. 1622 | op++; 1623 | 1624 | if( srclen < 16 ) 1625 | { 1626 | // Handle a very short source data. 1627 | 1628 | *op = (uint8_t) srclen; 1629 | op++; 1630 | 1631 | memcpy( op, src, (size_t) srclen ); 1632 | 1633 | if( srclen > LZAV_LIT_FIN - 1 ) 1634 | { 1635 | return( 2 + srclen ); 1636 | } 1637 | 1638 | memset( op + srclen, 0, (size_t) ( LZAV_LIT_FIN - srclen )); 1639 | return( 2 + LZAV_LIT_FIN ); 1640 | } 1641 | 1642 | size_t htsize; // Hash-table's size in bytes (power-of-2). 1643 | htsize = ( 1 << 7 ) * sizeof( uint32_t ) * 2 * 8; 1644 | 1645 | while( htsize != ( 1 << 23 ) && htsize < ( (size_t) srclen << 2 )) 1646 | { 1647 | htsize <<= 1; 1648 | } 1649 | 1650 | uint32_t* const alloc_buf = 1651 | LZAV_MALLOC( htsize, uint32_t ); // Hash-table allocated on heap. 1652 | 1653 | if( alloc_buf == LZAV_NULL ) 1654 | { 1655 | return( 0 ); 1656 | } 1657 | 1658 | uint8_t* LZAV_RESTRICT const ht = (uint8_t*) alloc_buf; // Hash-table ptr. 1659 | 1660 | const size_t mti = 12; // Maximal tuple offset, inclusive. 1661 | const uint32_t hmask = (uint32_t) (( htsize - 1 ) ^ 63 ); // Hash mask. 1662 | const uint8_t* ip = (const uint8_t*) src; // Source data pointer. 1663 | const uint8_t* const ipe = ip + srclen - LZAV_LIT_FIN; // End pointer. 1664 | const uint8_t* const ipet = ipe - 15 + LZAV_LIT_FIN; // Hashing threshold, 1665 | // avoids I/O OOB. 1666 | const uint8_t* ipa = ip; // Literals anchor pointer. 1667 | 1668 | // Initialize the hash-table. Each hash-table item consists of 8 tuples 1669 | // (4 initial match bytes; 32-bit source data offset). The last value of 1670 | // the last tuple is used as head tuple offset (an even value). Start at 1671 | // offset 2 for non-zero back-match length. 1672 | 1673 | uint32_t initv[ 2 ] = { 0, 2 }; 1674 | ip += 2; 1675 | memcpy( initv, ip, 4 ); 1676 | 1677 | lzav_ht_init( ht, htsize, initv ); 1678 | 1679 | uint8_t* cbp = op; // Pointer to the latest offset carry block header. 1680 | int csh = 0; // Offset carry shift. 1681 | 1682 | size_t prc = 0; // Length of a previously found match. 1683 | size_t pd = 0; // Distance of a previously found match. 1684 | const uint8_t* pip = ip; // Source pointer of a previously found match. 1685 | 1686 | while LZAV_LIKELY( ip < ipet ) 1687 | { 1688 | // Hash source data (endianness is minimally important for compression 1689 | // efficiency). 1690 | 1691 | uint32_t iw1; 1692 | memcpy( &iw1, ip, 4 ); 1693 | 1694 | // Hash-table access. 1695 | 1696 | uint32_t* LZAV_RESTRICT hp = (uint32_t*) ( ht + 1697 | lzav_hash( iw1, ip[ 4 ], 8, hmask )); 1698 | 1699 | const uint32_t ipo = (uint32_t) ( ip - (const uint8_t*) src ); 1700 | size_t ti0 = hp[ mti + 3 ]; // Head tuple offset. 1701 | 1702 | // Find source data in hash-table tuples, in up to 7 previous 1703 | // positions. 1704 | 1705 | const size_t mle = (size_t) ( ipe - ip ); // Match length bound. 1706 | size_t rc = 1; // Best found match length-4, 1 - not found. 1707 | size_t d = 0; // Best found reference offset (distance). 1708 | size_t ti = ti0; 1709 | int i; 1710 | 1711 | // Match-finder. 1712 | 1713 | for( i = 0; i < 7; i++ ) 1714 | { 1715 | const uint8_t* const wp0 = (const uint8_t*) src + hp[ ti + 1 ]; 1716 | const uint32_t ww1 = hp[ ti ]; 1717 | const size_t d0 = (size_t) ( ip - wp0 ); 1718 | ti = ( ti == mti ? 0 : ti + 2 ); 1719 | 1720 | if( iw1 == ww1 ) 1721 | { 1722 | // Disallow reference copy overlap by using `d0` as max 1723 | // match length. Make sure `LZAV_LIT_FIN` literals remain 1724 | // on finish. 1725 | 1726 | const size_t ml = lzav_match_len( ip, wp0, 1727 | ( d0 > mle ? mle : d0 ), 4 ); 1728 | 1729 | if( ml > rc ) 1730 | { 1731 | d = d0; 1732 | rc = ml; 1733 | } 1734 | } 1735 | } 1736 | 1737 | if LZAV_LIKELY( d != rc ) 1738 | { 1739 | // Update hash-table entry, if there was no match, or if the match 1740 | // is not an adjacent replication. 1741 | 1742 | lzav_ht_insert( hp, ti0, mti, iw1, ipo ); 1743 | } 1744 | 1745 | if(( rc < mref + ( d > ( 1 << 18 )) + ( d > ( 1 << 22 ))) | 1746 | ( d < LZAV_OFS_MIN )) 1747 | { 1748 | ip++; 1749 | continue; 1750 | } 1751 | 1752 | // Source data and hash-table entry match of suitable length. 1753 | 1754 | const uint8_t* const wp = ip - d; 1755 | const uint8_t* const ip1 = ip + 1; 1756 | size_t lc = (size_t) ( ip - ipa ); 1757 | 1758 | if LZAV_UNLIKELY( lc != 0 && ip[ -1 ] == wp[ -1 ]) 1759 | { 1760 | // Try to consume literals by finding a match at back-position. 1761 | 1762 | size_t ml = (size_t) ( wp - (const uint8_t*) src ); 1763 | 1764 | if LZAV_LIKELY( ml > lc ) 1765 | { 1766 | ml = lc; 1767 | } 1768 | 1769 | ml = lzav_match_len_r1( ip, wp, ml ); 1770 | lc -= ml; 1771 | rc += ml; 1772 | ip -= ml; 1773 | } 1774 | 1775 | if LZAV_UNLIKELY( d >= (size_t) LZAV_WIN_LEN << csh << 1776 | (( lc != 0 ) * 2 )) 1777 | { 1778 | goto _d_oob; 1779 | } 1780 | 1781 | if( prc == 0 ) 1782 | { 1783 | // Save match for a later comparison. 1784 | 1785 | _save_match: 1786 | prc = rc; 1787 | pd = d; 1788 | pip = ip; 1789 | ip = ip1; 1790 | continue; 1791 | 1792 | _d_oob: 1793 | // `d` is out of bounds. 1794 | 1795 | ip = ip1; 1796 | 1797 | if LZAV_LIKELY( d != rc ) 1798 | { 1799 | continue; 1800 | } 1801 | 1802 | lzav_ht_insert( hp, ti0, mti, iw1, ipo ); 1803 | continue; 1804 | } 1805 | 1806 | // Block size overhead estimation, and comparison with a previously 1807 | // found match. 1808 | 1809 | const size_t plc = (size_t) ( pip - ipa ); 1810 | const size_t ov = lzav_est_blksize( lc, d, csh ); 1811 | const size_t pov = lzav_est_blksize( plc, pd, csh ); 1812 | 1813 | if LZAV_LIKELY( prc * ov > rc * pov ) 1814 | { 1815 | op = lzav_write_blk_3( op, plc, prc, pd, ipa, &cbp, &csh, mref ); 1816 | 1817 | ipa = pip + prc; 1818 | 1819 | if LZAV_LIKELY( ipa > ip || d >= (size_t) LZAV_WIN_LEN << csh << 1820 | (( ip - ipa != 0 ) * 2 )) 1821 | { 1822 | prc = 0; 1823 | ip = ( ipa > ip1 ? ipa : ip1 ); 1824 | continue; 1825 | } 1826 | 1827 | // A winning previous match does not overlap a current match. 1828 | 1829 | goto _save_match; 1830 | } 1831 | 1832 | op = lzav_write_blk_3( op, lc, rc, d, ipa, &cbp, &csh, mref ); 1833 | 1834 | // Update hash-table with 1 skipped position. 1835 | 1836 | memcpy( &iw1, ip + 4, 4 ); 1837 | hp = (uint32_t*) ( ht + lzav_hash( iw1, ip[ 8 ], 8, hmask )); 1838 | 1839 | lzav_ht_insert( hp, hp[ mti + 3 ], mti, iw1, 1840 | (uint32_t) ( ip + 4 - (const uint8_t*) src )); 1841 | 1842 | ip += rc; 1843 | prc = 0; 1844 | ipa = ip; 1845 | } 1846 | 1847 | if( prc != 0 ) 1848 | { 1849 | op = lzav_write_blk_3( op, (size_t) ( pip - ipa ), prc, pd, ipa, &cbp, 1850 | &csh, mref ); 1851 | 1852 | ipa = pip + prc; 1853 | } 1854 | 1855 | op = lzav_write_fin_3( op, (size_t) ( ipe - ipa + LZAV_LIT_FIN ), ipa ); 1856 | 1857 | LZAV_FREE( alloc_buf ); 1858 | 1859 | return( (int) ( op - (uint8_t*) dst )); 1860 | } 1861 | 1862 | /** 1863 | * @def LZAV_LOAD32( a ) 1864 | * @brief Defines `bv` and loads a 32-bit unsigned value from memory, with 1865 | * endianness correction. 1866 | * 1867 | * @param a Memory address. 1868 | */ 1869 | 1870 | /** 1871 | * @def LZAV_SET_IPD_CV( x, v, sh ) 1872 | * @brief Defines `ipd` as a pointer to the back-reference, checks bounds, 1873 | * and updates the carry bit variables. 1874 | * 1875 | * @param x Reference offset. 1876 | * @param v Next `cv` value. 1877 | * @param sh Next `csh` value. 1878 | */ 1879 | 1880 | /** 1881 | * @brief Internal LZAV decompression function (data format 3). 1882 | * 1883 | * The function decompresses "raw" data previously compressed into the LZAV 1884 | * data format 3. 1885 | * 1886 | * This function should not be called directly, since it does not check the 1887 | * format identifier. 1888 | * 1889 | * @param[in] src Source (compressed) data pointer. 1890 | * @param[out] dst Destination (decompressed data) buffer pointer. 1891 | * @param srclen Source data length, in bytes. 1892 | * @param dstlen Expected destination data length, in bytes. 1893 | * @param[out] pwl Pointer to a variable that receives the number of bytes 1894 | * written to the destination buffer (until error or end of buffer). 1895 | * @return Length of the decompressed data, in bytes, or any negative value if 1896 | * an error occurred. 1897 | */ 1898 | 1899 | LZAV_NO_INLINE int lzav_decompress_3( const void* const src, void* const dst, 1900 | const int srclen, const int dstlen, int* const pwl ) LZAV_NOEX 1901 | { 1902 | if LZAV_UNLIKELY( srclen < 10 ) 1903 | { 1904 | *pwl = 0; 1905 | return( LZAV_E_SRCOOB ); 1906 | } 1907 | 1908 | const uint8_t* LZAV_RESTRICT ip = 1909 | (const uint8_t*) src; // Compressed data pointer. 1910 | 1911 | const uint8_t* const ipe = ip + srclen; // Compressed data boundary ptr. 1912 | const uint8_t* const ipet = ipe - 9; // Block header read threshold. 1913 | uint8_t* op = (uint8_t*) dst; // Destination (decompressed data) pointer. 1914 | uint8_t* const ope = op + dstlen; // Destination boundary pointer. 1915 | uint8_t* opet = ope - 63; // Threshold for fast copy to destination. 1916 | const size_t mref1 = (size_t) ( *ip & 15 ) - 1; // Minimal ref length - 1. 1917 | 1918 | if LZAV_UNLIKELY( mref1 > 5 ) 1919 | { 1920 | *pwl = 0; 1921 | return( LZAV_E_UNKFMT ); 1922 | } 1923 | 1924 | *pwl = dstlen; 1925 | size_t bh; // Current block header, updated in each branch. 1926 | size_t cv = 0; // Reference offset carry value. 1927 | int csh = 0; // Reference offset carry shift. 1928 | 1929 | #define LZAV_LOAD32( a ) \ 1930 | uint32_t bv; \ 1931 | memcpy( &bv, a, 4 ); \ 1932 | LZAV_IEC32( bv ) 1933 | 1934 | #define LZAV_SET_IPD_CV( x, v, sh ) \ 1935 | const size_t d = ( x ) << csh | cv; \ 1936 | const size_t md = (size_t) ( op - (uint8_t*) dst ); \ 1937 | csh = ( sh ); \ 1938 | cv = ( v ); \ 1939 | ipd = op - d; \ 1940 | if LZAV_UNLIKELY( d > md ) \ 1941 | goto _err_refoob 1942 | 1943 | if LZAV_UNLIKELY(( ope < opet ) | ( ipe < ipet - 16 ) | 1944 | ( ipe < ipet - 64 )) 1945 | { 1946 | opet = LZAV_NULL; 1947 | } 1948 | 1949 | ip++; // Advance beyond prefix byte. 1950 | 1951 | bh = *ip; 1952 | 1953 | while LZAV_LIKELY( ip < ipet ) 1954 | { 1955 | size_t cc; // Byte copy count. 1956 | 1957 | if LZAV_LIKELY(( bh & 0x30 ) != 0 ) // Block type != 0. 1958 | { 1959 | const uint8_t* ipd; // Source data pointer. 1960 | size_t bt; // Block type. 1961 | 1962 | _refblk: 1963 | ip++; 1964 | bt = ( bh >> 4 ) & 3; 1965 | 1966 | LZAV_LOAD32( ip ); 1967 | 1968 | static const size_t om[ 4 ] = { 0, 0x3FF, 0x7FFF, 0x1FFFFF }; 1969 | static const size_t cvm[ 4 ] = { 0, 0, 7, 31 }; 1970 | static const int ocsh[ 4 ] = { 0, 0, 3, 5 }; 1971 | 1972 | const int bt8 = (int) ( bt << 3 ); 1973 | const int ncsh = ocsh[ bt ]; 1974 | 1975 | LZAV_SET_IPD_CV(( bh >> 6 | bv << 2 ) & om[ bt ], 1976 | ( bv >> ( bt8 - ncsh )) & cvm[ bt ], ncsh ); 1977 | 1978 | ip += bt; 1979 | bv >>= bt8; 1980 | 1981 | LZAV_PREFETCH( ipd ); 1982 | 1983 | uint8_t* opcc = op + mref1; 1984 | cc = bh & 15; 1985 | 1986 | if LZAV_LIKELY( cc != 0 ) // True, if no additional length byte. 1987 | { 1988 | opcc += cc; 1989 | bh = bv & 0xFF; 1990 | 1991 | if LZAV_LIKELY( op < opet ) 1992 | { 1993 | if LZAV_LIKELY( d > 15 ) 1994 | { 1995 | memcpy( op, ipd, 16 ); 1996 | memcpy( op + 16, ipd + 16, 4 ); 1997 | op = opcc; 1998 | continue; 1999 | } 2000 | 2001 | if LZAV_LIKELY( d > 7 ) 2002 | { 2003 | memcpy( op, ipd, 8 ); 2004 | memcpy( op + 8, ipd + 8, 8 ); 2005 | memcpy( op + 16, ipd + 16, 4 ); 2006 | op = opcc; 2007 | continue; 2008 | } 2009 | 2010 | goto _err_refoob; 2011 | } 2012 | } 2013 | else 2014 | { 2015 | bh = bv; 2016 | ip++; 2017 | opcc += 16 + ( bh & 0x7F ); 2018 | 2019 | if LZAV_UNLIKELY(( bh & 0x80 ) != 0 ) 2020 | { 2021 | int sh = 7; 2022 | 2023 | do 2024 | { 2025 | bh = *ip; 2026 | ip++; 2027 | opcc += ( bh & 0x7F ) << sh; 2028 | 2029 | if( sh == 28 ) // No more than 4 additional bytes. 2030 | { 2031 | break; 2032 | } 2033 | 2034 | sh += 7; 2035 | 2036 | } while(( bh & 0x80 ) != 0 ); 2037 | } 2038 | 2039 | bh = *ip; 2040 | 2041 | #if defined( LZAV_PTR32 ) 2042 | if LZAV_UNLIKELY( opcc < op ) 2043 | { 2044 | goto _err_ptrovr; 2045 | } 2046 | #endif // defined( LZAV_PTR32 ) 2047 | 2048 | #if defined( LZAV_LONG_COPY ) 2049 | if LZAV_LIKELY(( opcc < opet ) & ( d > 15 )) 2050 | { 2051 | do 2052 | { 2053 | memcpy( op, ipd, 16 ); 2054 | memcpy( op + 16, ipd + 16, 16 ); 2055 | memcpy( op + 32, ipd + 32, 16 ); 2056 | memcpy( op + 48, ipd + 48, 16 ); 2057 | op += 64; 2058 | ipd += 64; 2059 | } while LZAV_LIKELY( op < opcc ); 2060 | 2061 | op = opcc; 2062 | continue; 2063 | } 2064 | #endif // defined( LZAV_LONG_COPY ) 2065 | 2066 | if LZAV_LIKELY(( opcc < opet ) & ( d > 7 )) 2067 | { 2068 | do 2069 | { 2070 | memcpy( op, ipd, 8 ); 2071 | memcpy( op + 8, ipd + 8, 8 ); 2072 | memcpy( op + 16, ipd + 16, 8 ); 2073 | memcpy( op + 24, ipd + 24, 8 ); 2074 | op += 32; 2075 | ipd += 32; 2076 | } while LZAV_LIKELY( op < opcc ); 2077 | 2078 | op = opcc; 2079 | continue; 2080 | } 2081 | } 2082 | 2083 | if LZAV_UNLIKELY( d < 8 ) 2084 | { 2085 | goto _err_refoob; 2086 | } 2087 | 2088 | if LZAV_UNLIKELY( opcc > ope ) 2089 | { 2090 | goto _err_dstoob_ref; 2091 | } 2092 | 2093 | while( op < opcc - 15 ) 2094 | { 2095 | memcpy( op, ipd, 8 ); 2096 | memcpy( op + 8, ipd + 8, 8 ); 2097 | op += 16; 2098 | ipd += 16; 2099 | } 2100 | 2101 | while( op != opcc ) 2102 | { 2103 | *op = *ipd; 2104 | ipd++; 2105 | op++; 2106 | } 2107 | 2108 | continue; 2109 | 2110 | _err_dstoob_ref: 2111 | while( op != ope ) 2112 | { 2113 | *op = *ipd; 2114 | ipd++; 2115 | op++; 2116 | } 2117 | 2118 | return( LZAV_E_DSTOOB ); 2119 | } 2120 | 2121 | const uint8_t* LZAV_RESTRICT ipd; // Source data pointer. 2122 | 2123 | size_t ncv = bh >> 6; // Additional offset carry bits. 2124 | ip++; 2125 | cc = bh & 15; 2126 | 2127 | if LZAV_LIKELY( cc != 0 ) // True, if no additional length byte. 2128 | { 2129 | ipd = ip; 2130 | ncv <<= csh; 2131 | ip += cc; 2132 | csh += 2; 2133 | cv |= ncv; 2134 | 2135 | if LZAV_LIKELY(( op < opet ) & ( ipd < ipet - 16 )) 2136 | { 2137 | bh = *ip; 2138 | memcpy( op, ipd, 16 ); 2139 | op += cc; 2140 | 2141 | goto _refblk; // Reference block follows, if not EOS. 2142 | } 2143 | } 2144 | else 2145 | { 2146 | bh = *ip; 2147 | ncv <<= csh; 2148 | cc = bh & 0x7F; 2149 | csh += 2; 2150 | cv |= ncv; 2151 | ip++; 2152 | 2153 | if LZAV_UNLIKELY(( bh & 0x80 ) != 0 ) 2154 | { 2155 | int sh = 7; 2156 | 2157 | do 2158 | { 2159 | bh = *ip; 2160 | ip++; 2161 | cc |= ( bh & 0x7F ) << sh; 2162 | 2163 | if( sh == 28 ) // No more than 4 additional bytes. 2164 | { 2165 | break; 2166 | } 2167 | 2168 | sh += 7; 2169 | 2170 | } while(( bh & 0x80 ) != 0 ); 2171 | } 2172 | 2173 | cc += 16; 2174 | ipd = ip; 2175 | ip += cc; 2176 | 2177 | uint8_t* const opcc = op + cc; 2178 | 2179 | #if defined( LZAV_PTR32 ) 2180 | if LZAV_UNLIKELY(( ip < ipd ) | ( opcc < op )) 2181 | { 2182 | goto _err_ptrovr; 2183 | } 2184 | #endif // defined( LZAV_PTR32 ) 2185 | 2186 | if LZAV_LIKELY(( opcc < opet ) & ( ip < ipet - 64 )) 2187 | { 2188 | #if defined( LZAV_LONG_COPY ) 2189 | do 2190 | { 2191 | memcpy( op, ipd, 16 ); 2192 | memcpy( op + 16, ipd + 16, 16 ); 2193 | memcpy( op + 32, ipd + 32, 16 ); 2194 | memcpy( op + 48, ipd + 48, 16 ); 2195 | op += 64; 2196 | ipd += 64; 2197 | } while LZAV_LIKELY( op < opcc ); 2198 | #else // defined( LZAV_LONG_COPY ) 2199 | do 2200 | { 2201 | memcpy( op, ipd, 8 ); 2202 | memcpy( op + 8, ipd + 8, 8 ); 2203 | memcpy( op + 16, ipd + 16, 8 ); 2204 | memcpy( op + 24, ipd + 24, 8 ); 2205 | op += 32; 2206 | ipd += 32; 2207 | } while LZAV_LIKELY( op < opcc ); 2208 | #endif // defined( LZAV_LONG_COPY ) 2209 | 2210 | bh = *ip; 2211 | op = opcc; 2212 | 2213 | goto _refblk; // Reference block follows, if not EOS. 2214 | } 2215 | } 2216 | 2217 | uint8_t* const opcc = op + cc; 2218 | 2219 | if LZAV_UNLIKELY( opcc > ope ) 2220 | { 2221 | goto _err_dstoob_lit; 2222 | } 2223 | 2224 | if LZAV_LIKELY( ip < ipe ) 2225 | { 2226 | memcpy( op, ipd, cc ); 2227 | bh = *ip; 2228 | op = opcc; 2229 | continue; 2230 | } 2231 | 2232 | if LZAV_UNLIKELY( ip != ipe ) 2233 | { 2234 | goto _err_srcoob_lit; 2235 | } 2236 | 2237 | memcpy( op, ipd, cc ); 2238 | op = opcc; 2239 | break; 2240 | 2241 | _err_srcoob_lit: 2242 | cc = (size_t) ( ipe - ipd ); 2243 | 2244 | if( cc < (size_t) ( ope - op )) 2245 | { 2246 | memcpy( op, ipd, cc ); 2247 | *pwl = (int) ( op + cc - (uint8_t*) dst ); 2248 | } 2249 | else 2250 | { 2251 | memcpy( op, ipd, (size_t) ( ope - op )); 2252 | } 2253 | 2254 | return( LZAV_E_SRCOOB ); 2255 | 2256 | _err_dstoob_lit: 2257 | if LZAV_UNLIKELY( ip > ipe ) 2258 | { 2259 | goto _err_srcoob_lit; 2260 | } 2261 | 2262 | memcpy( op, ipd, (size_t) ( ope - op )); 2263 | return( LZAV_E_DSTOOB ); 2264 | } 2265 | 2266 | if LZAV_UNLIKELY( op != ope ) 2267 | { 2268 | goto _err_dstlen; 2269 | } 2270 | 2271 | return( (int) ( op - (uint8_t*) dst )); 2272 | 2273 | _err_refoob: 2274 | *pwl = (int) ( op - (uint8_t*) dst ); 2275 | return( LZAV_E_REFOOB ); 2276 | 2277 | _err_dstlen: 2278 | *pwl = (int) ( op - (uint8_t*) dst ); 2279 | return( LZAV_E_DSTLEN ); 2280 | 2281 | #if defined( LZAV_PTR32 ) 2282 | _err_ptrovr: 2283 | *pwl = (int) ( op - (uint8_t*) dst ); 2284 | return( LZAV_E_PTROVR ); 2285 | #endif // defined( LZAV_PTR32 ) 2286 | } 2287 | 2288 | #if LZAV_FMT_MIN < 3 2289 | 2290 | /** 2291 | * @brief Internal LZAV decompression function (data format 2). 2292 | * 2293 | * Function decompresses "raw" data previously compressed into the LZAV data 2294 | * format 2. 2295 | * 2296 | * This function should not be called directly since it does not check the 2297 | * format identifier. 2298 | * 2299 | * @param[in] src Source (compressed) data pointer. 2300 | * @param[out] dst Destination (decompressed data) buffer pointer. 2301 | * @param srclen Source data length, in bytes. 2302 | * @param dstlen Expected destination data length, in bytes. 2303 | * @param[out] pwl Pointer to variable that receives the number of bytes 2304 | * written to the destination buffer (until error or end of buffer). 2305 | * @return The length of decompressed data, in bytes, or any negative value if 2306 | * some error happened. 2307 | */ 2308 | 2309 | LZAV_NO_INLINE int lzav_decompress_2( const void* const src, void* const dst, 2310 | const int srclen, const int dstlen, int* const pwl ) LZAV_NOEX 2311 | { 2312 | if LZAV_UNLIKELY( srclen < 7 ) 2313 | { 2314 | *pwl = 0; 2315 | return( LZAV_E_SRCOOB ); 2316 | } 2317 | 2318 | const uint8_t* ip = (const uint8_t*) src; // Compressed data pointer. 2319 | const uint8_t* const ipe = ip + srclen; // Compressed data boundary ptr. 2320 | const uint8_t* const ipet = ipe - 6; // Block header read threshold. 2321 | uint8_t* op = (uint8_t*) dst; // Destination (decompressed data) pointer. 2322 | uint8_t* const ope = op + dstlen; // Destination boundary pointer. 2323 | uint8_t* opet = ope - 63; // Threshold for fast copy to destination. 2324 | *pwl = dstlen; 2325 | const size_t mref1 = (size_t) ( *ip & 15 ) - 1; // Minimal ref length - 1. 2326 | size_t bh; // Current block header, updated in each branch. 2327 | size_t cv = 0; // Reference offset carry value. 2328 | int csh = 0; // Reference offset carry shift. 2329 | 2330 | if LZAV_UNLIKELY(( ope < opet ) | ( ipe < ipet - 16 ) | 2331 | ( ipe < ipet - 64 )) 2332 | { 2333 | opet = LZAV_NULL; 2334 | } 2335 | 2336 | ip++; // Advance beyond prefix byte. 2337 | 2338 | bh = *ip; 2339 | 2340 | while LZAV_LIKELY( ip < ipet ) 2341 | { 2342 | const uint8_t* ipd; // Source data pointer. 2343 | size_t cc; // Byte copy count. 2344 | size_t bt; // Block type. 2345 | 2346 | if LZAV_LIKELY(( bh & 0x30 ) != 0 ) // Block type != 0. 2347 | { 2348 | _refblk: 2349 | bt = ( bh >> 4 ) & 3; 2350 | ip++; 2351 | const int bt8 = (int) ( bt << 3 ); 2352 | 2353 | LZAV_LOAD32( ip ); 2354 | ip += bt; 2355 | 2356 | #if defined( LZAV_X86 ) 2357 | 2358 | static const uint32_t om[ 4 ] = { 0, 0xFF, 0xFFFF, 0xFFFFFF }; 2359 | static const int ocsh[ 4 ] = { 0, 0, 0, 3 }; 2360 | 2361 | const uint32_t o = bv & om[ bt ]; 2362 | bv >>= bt8; 2363 | 2364 | const int wcsh = ocsh[ bt ]; 2365 | 2366 | LZAV_SET_IPD_CV(( bh >> 6 | o << 2 ) & 0x7FFFFF, o >> 21, wcsh ); 2367 | 2368 | #else // defined( LZAV_X86 ) 2369 | 2370 | // Memory accesses on RISC are less efficient here. 2371 | 2372 | const size_t o = bv & (( (uint32_t) 1 << bt8 ) - 1 ); 2373 | bv >>= bt8; 2374 | 2375 | LZAV_SET_IPD_CV(( bh >> 6 | o << 2 ) & 0x7FFFFF, o >> 21, 2376 | ( bt == 3 ? 3 : 0 )); 2377 | 2378 | #endif // defined( LZAV_X86 ) 2379 | 2380 | LZAV_PREFETCH( ipd ); 2381 | 2382 | cc = bh & 15; 2383 | 2384 | if LZAV_LIKELY( cc != 0 ) // True, if no additional length byte. 2385 | { 2386 | cc += mref1; 2387 | bh = bv & 0xFF; 2388 | 2389 | if LZAV_LIKELY( op < opet ) 2390 | { 2391 | if LZAV_LIKELY( d > 15 ) 2392 | { 2393 | memcpy( op, ipd, 16 ); 2394 | memcpy( op + 16, ipd + 16, 4 ); 2395 | op += cc; 2396 | continue; 2397 | } 2398 | 2399 | if LZAV_LIKELY( d > 7 ) 2400 | { 2401 | memcpy( op, ipd, 8 ); 2402 | memcpy( op + 8, ipd + 8, 8 ); 2403 | op += cc; 2404 | continue; 2405 | } 2406 | 2407 | if( d > 3 ) 2408 | { 2409 | memcpy( op, ipd, 4 ); 2410 | memcpy( op + 4, ipd + 4, 4 ); 2411 | op += cc; 2412 | continue; 2413 | } 2414 | 2415 | goto _err_refoob; 2416 | } 2417 | 2418 | if LZAV_UNLIKELY( cc > d ) 2419 | { 2420 | goto _err_refoob; 2421 | } 2422 | 2423 | uint8_t* const opcc = op + cc; 2424 | 2425 | if LZAV_UNLIKELY( opcc > ope ) 2426 | { 2427 | goto _err_dstoob_ref; 2428 | } 2429 | 2430 | memcpy( op, ipd, cc ); 2431 | op = opcc; 2432 | continue; 2433 | } 2434 | else 2435 | { 2436 | bh = bv & 0xFF; 2437 | ip++; 2438 | cc = 16 + mref1 + bh; 2439 | 2440 | if LZAV_UNLIKELY( bh == 255 ) 2441 | { 2442 | cc += *ip; 2443 | ip++; 2444 | } 2445 | 2446 | uint8_t* const opcc = op + cc; 2447 | bh = *ip; 2448 | 2449 | if LZAV_LIKELY(( opcc < opet ) & ( d > 15 )) 2450 | { 2451 | do 2452 | { 2453 | memcpy( op, ipd, 16 ); 2454 | memcpy( op + 16, ipd + 16, 16 ); 2455 | memcpy( op + 32, ipd + 32, 16 ); 2456 | memcpy( op + 48, ipd + 48, 16 ); 2457 | op += 64; 2458 | ipd += 64; 2459 | } while LZAV_LIKELY( op < opcc ); 2460 | 2461 | op = opcc; 2462 | continue; 2463 | } 2464 | 2465 | if LZAV_UNLIKELY( cc > d ) 2466 | { 2467 | goto _err_refoob; 2468 | } 2469 | 2470 | if LZAV_UNLIKELY( opcc > ope ) 2471 | { 2472 | goto _err_dstoob_ref; 2473 | } 2474 | 2475 | memcpy( op, ipd, cc ); 2476 | op = opcc; 2477 | continue; 2478 | } 2479 | 2480 | _err_dstoob_ref: 2481 | memcpy( op, ipd, (size_t) ( ope - op )); 2482 | return( LZAV_E_DSTOOB ); 2483 | } 2484 | 2485 | size_t ncv = bh >> 6; // Additional offset carry bits. 2486 | ip++; 2487 | cc = bh & 15; 2488 | 2489 | if LZAV_LIKELY( cc != 0 ) // True, if no additional length byte. 2490 | { 2491 | ipd = ip; 2492 | ncv <<= csh; 2493 | ip += cc; 2494 | csh += 2; 2495 | cv |= ncv; 2496 | 2497 | if LZAV_LIKELY(( op < opet ) & ( ipd < ipet - 16 )) 2498 | { 2499 | bh = *ip; 2500 | memcpy( op, ipd, 16 ); 2501 | op += cc; 2502 | 2503 | goto _refblk; // Reference block follows, if not EOS. 2504 | } 2505 | } 2506 | else 2507 | { 2508 | bh = *ip; 2509 | ncv <<= csh; 2510 | cc = bh & 0x7F; 2511 | csh += 2; 2512 | cv |= ncv; 2513 | ip++; 2514 | 2515 | if LZAV_UNLIKELY(( bh & 0x80 ) != 0 ) 2516 | { 2517 | int sh = 7; 2518 | 2519 | do 2520 | { 2521 | bh = *ip; 2522 | ip++; 2523 | cc |= ( bh & 0x7F ) << sh; 2524 | 2525 | if( sh == 28 ) // No more than 4 additional bytes. 2526 | { 2527 | break; 2528 | } 2529 | 2530 | sh += 7; 2531 | 2532 | } while(( bh & 0x80 ) != 0 ); 2533 | 2534 | cc &= 0x7FFFFFFF; // For malformed data. 2535 | } 2536 | 2537 | cc += 16; 2538 | ipd = ip; 2539 | ip += cc; 2540 | 2541 | uint8_t* const opcc = op + cc; 2542 | 2543 | #if defined( LZAV_PTR32 ) 2544 | if LZAV_UNLIKELY(( ip < ipd ) | ( opcc < op )) 2545 | { 2546 | goto _err_ptrovr; 2547 | } 2548 | #endif // defined( LZAV_PTR32 ) 2549 | 2550 | if LZAV_LIKELY(( opcc < opet ) & ( ip < ipet - 64 )) 2551 | { 2552 | do 2553 | { 2554 | memcpy( op, ipd, 16 ); 2555 | memcpy( op + 16, ipd + 16, 16 ); 2556 | memcpy( op + 32, ipd + 32, 16 ); 2557 | memcpy( op + 48, ipd + 48, 16 ); 2558 | op += 64; 2559 | ipd += 64; 2560 | } while LZAV_LIKELY( op < opcc ); 2561 | 2562 | bh = *ip; 2563 | op = opcc; 2564 | 2565 | goto _refblk; // Reference block follows, if not EOS. 2566 | } 2567 | } 2568 | 2569 | uint8_t* const opcc = op + cc; 2570 | 2571 | if LZAV_UNLIKELY( opcc > ope ) 2572 | { 2573 | goto _err_dstoob_lit; 2574 | } 2575 | 2576 | if LZAV_LIKELY( ip < ipe ) 2577 | { 2578 | bh = *ip; 2579 | memcpy( op, ipd, cc ); 2580 | op = opcc; 2581 | continue; 2582 | } 2583 | 2584 | if LZAV_UNLIKELY( ip != ipe ) 2585 | { 2586 | goto _err_srcoob_lit; 2587 | } 2588 | 2589 | memcpy( op, ipd, cc ); 2590 | op = opcc; 2591 | break; 2592 | 2593 | _err_srcoob_lit: 2594 | cc = (size_t) ( ipe - ipd ); 2595 | 2596 | if( cc < (size_t) ( ope - op )) 2597 | { 2598 | memcpy( op, ipd, cc ); 2599 | *pwl = (int) ( op + cc - (uint8_t*) dst ); 2600 | } 2601 | else 2602 | { 2603 | memcpy( op, ipd, (size_t) ( ope - op )); 2604 | } 2605 | 2606 | return( LZAV_E_SRCOOB ); 2607 | 2608 | _err_dstoob_lit: 2609 | if LZAV_UNLIKELY( ip > ipe ) 2610 | { 2611 | goto _err_srcoob_lit; 2612 | } 2613 | 2614 | memcpy( op, ipd, (size_t) ( ope - op )); 2615 | return( LZAV_E_DSTOOB ); 2616 | } 2617 | 2618 | if LZAV_UNLIKELY( op != ope ) 2619 | { 2620 | goto _err_dstlen; 2621 | } 2622 | 2623 | return( (int) ( op - (uint8_t*) dst )); 2624 | 2625 | _err_refoob: 2626 | *pwl = (int) ( op - (uint8_t*) dst ); 2627 | return( LZAV_E_REFOOB ); 2628 | 2629 | _err_dstlen: 2630 | *pwl = (int) ( op - (uint8_t*) dst ); 2631 | return( LZAV_E_DSTLEN ); 2632 | 2633 | #if defined( LZAV_PTR32 ) 2634 | _err_ptrovr: 2635 | *pwl = (int) ( op - (uint8_t*) dst ); 2636 | return( LZAV_E_PTROVR ); 2637 | #endif // defined( LZAV_PTR32 ) 2638 | } 2639 | 2640 | #endif // LZAV_FMT_MIN < 3 2641 | 2642 | #undef LZAV_LOAD32 2643 | #undef LZAV_SET_IPD_CV 2644 | 2645 | /** 2646 | * @brief LZAV decompression function (partial). 2647 | * 2648 | * The function decompresses "raw" data previously compressed into the LZAV 2649 | * data format, for partial or recovery decompression. For example, this 2650 | * function can be used to decompress only an initial segment of a larger data 2651 | * block. 2652 | * 2653 | * @param[in] src Source (compressed) data pointer; can be 0 if `srclen` is 0. 2654 | * Address alignment is unimportant. 2655 | * @param[out] dst Destination (decompressed data) buffer pointer. Address 2656 | * alignment is unimportant. Should be different from `src`. 2657 | * @param srclen Source data length, in bytes; can be 0. 2658 | * @param dstlen Destination buffer length, in bytes; can be 0. 2659 | * @return Length of the decompressed data, in bytes. Always a non-negative 2660 | * value (error codes are not returned). 2661 | */ 2662 | 2663 | LZAV_INLINE_F int lzav_decompress_partial( const void* const src, 2664 | void* const dst, const int srclen, const int dstlen ) LZAV_NOEX 2665 | { 2666 | if( srclen <= 0 || src == LZAV_NULL || dst == LZAV_NULL || src == dst || 2667 | dstlen <= 0 ) 2668 | { 2669 | return( 0 ); 2670 | } 2671 | 2672 | const int fmt = *(const uint8_t*) src >> 4; 2673 | int dl = 0; 2674 | 2675 | if( fmt == 3 ) 2676 | { 2677 | lzav_decompress_3( src, dst, srclen, dstlen, &dl ); 2678 | } 2679 | 2680 | #if LZAV_FMT_MIN < 3 2681 | else 2682 | if( fmt == 2 ) 2683 | { 2684 | lzav_decompress_2( src, dst, srclen, dstlen, &dl ); 2685 | } 2686 | #endif // LZAV_FMT_MIN < 3 2687 | 2688 | return( dl ); 2689 | } 2690 | 2691 | /** 2692 | * @brief LZAV decompression function. 2693 | * 2694 | * The function decompresses "raw" data previously compressed into the LZAV 2695 | * data format. 2696 | * 2697 | * Note that while the function does perform checks to avoid OOB memory 2698 | * accesses, and checks for decompressed data length equality, this is not a 2699 | * strict guarantee of valid decompression. In cases where the compressed data 2700 | * is stored in long-term storage without embedded data integrity mechanisms 2701 | * (e.g., a database without RAID 1 guarantee, a binary container without 2702 | * a digital signature or CRC), then a checksum (hash) of the original 2703 | * uncompressed data should be stored, and then evaluated against that of 2704 | * the decompressed data. Also, a separate checksum (hash) of 2705 | * an application-defined header, which contains uncompressed and compressed 2706 | * data lengths, should be checked before decompression. A high-performance 2707 | * "komihash" hash function can be used to obtain a hash value of the data. 2708 | * 2709 | * @param[in] src Source (compressed) data pointer; can be 0 if `srclen` is 0. 2710 | * Address alignment is unimportant. 2711 | * @param[out] dst Destination (decompressed data) buffer pointer. Address 2712 | * alignment is unimportant. Should be different from `src`. 2713 | * @param srclen Source data length, in bytes; can be 0. 2714 | * @param dstlen Expected destination data length, in bytes; can be 0. Should 2715 | * not be confused with the actual size of the destination buffer (which may 2716 | * be larger). 2717 | * @return Length of the decompressed data, in bytes, or any negative value if 2718 | * an error occurred. Always returns a negative value if the resulting 2719 | * decompressed data length differs from `dstlen`. This means that error 2720 | * result handling requires just a check for a negative return value (see the 2721 | * LZAV_ERROR enum for possible values). 2722 | */ 2723 | 2724 | LZAV_INLINE_F int lzav_decompress( const void* const src, void* const dst, 2725 | const int srclen, const int dstlen ) LZAV_NOEX 2726 | { 2727 | if( srclen < 0 ) 2728 | { 2729 | return( LZAV_E_PARAMS ); 2730 | } 2731 | 2732 | if( srclen == 0 ) 2733 | { 2734 | return( dstlen == 0 ? 0 : LZAV_E_PARAMS ); 2735 | } 2736 | 2737 | if( src == LZAV_NULL || dst == LZAV_NULL || src == dst || dstlen <= 0 ) 2738 | { 2739 | return( LZAV_E_PARAMS ); 2740 | } 2741 | 2742 | const int fmt = *(const uint8_t*) src >> 4; 2743 | 2744 | if( fmt == 3 ) 2745 | { 2746 | int tmp; 2747 | return( lzav_decompress_3( src, dst, srclen, dstlen, &tmp )); 2748 | } 2749 | 2750 | #if LZAV_FMT_MIN < 3 2751 | if( fmt == 2 ) 2752 | { 2753 | int tmp; 2754 | return( lzav_decompress_2( src, dst, srclen, dstlen, &tmp )); 2755 | } 2756 | #endif // LZAV_FMT_MIN < 3 2757 | 2758 | return( LZAV_E_UNKFMT ); 2759 | } 2760 | 2761 | #if defined( LZAV_NS ) 2762 | 2763 | } // namespace LZAV_NS 2764 | 2765 | #if !defined( LZAV_NS_CUSTOM ) 2766 | 2767 | namespace { 2768 | 2769 | using namespace LZAV_NS :: enum_wrapper; 2770 | using LZAV_NS :: lzav_compress_bound_mref5; 2771 | using LZAV_NS :: lzav_compress_bound_mref6; 2772 | using LZAV_NS :: lzav_compress_bound; 2773 | using LZAV_NS :: lzav_compress_bound_hi; 2774 | using LZAV_NS :: lzav_compress; 2775 | using LZAV_NS :: lzav_compress_mref5; 2776 | using LZAV_NS :: lzav_compress_mref6; 2777 | using LZAV_NS :: lzav_compress_default; 2778 | using LZAV_NS :: lzav_compress_hi; 2779 | using LZAV_NS :: lzav_decompress_partial; 2780 | using LZAV_NS :: lzav_decompress; 2781 | 2782 | } // namespace 2783 | 2784 | #endif // !defined( LZAV_NS_CUSTOM ) 2785 | 2786 | #endif // defined( LZAV_NS ) 2787 | 2788 | // Defines for Doxygen. 2789 | 2790 | #if !defined( LZAV_NS_CUSTOM ) 2791 | #define LZAV_NS_CUSTOM 2792 | #endif // !defined( LZAV_NS_CUSTOM ) 2793 | 2794 | #if !defined( LZAV_NS ) 2795 | #define LZAV_NS 2796 | #undef LZAV_NS 2797 | #endif // !defined( LZAV_NS ) 2798 | 2799 | #if defined( LZAV_DEF_MALLOC ) 2800 | #undef LZAV_MALLOC 2801 | #undef LZAV_FREE 2802 | #undef LZAV_DEF_MALLOC 2803 | #endif // defined( LZAV_DEF_MALLOC ) 2804 | 2805 | #undef LZAV_NS_CUSTOM 2806 | #undef LZAV_NOEX 2807 | #undef LZAV_NULL 2808 | #undef LZAV_X86 2809 | #undef LZAV_LITTLE_ENDIAN 2810 | #undef LZAV_COND_EC 2811 | #undef LZAV_LONG_COPY 2812 | #undef LZAV_GCC_BUILTINS 2813 | #undef LZAV_CPP_BIT 2814 | #undef LZAV_IEC32 2815 | #undef LZAV_LIKELY 2816 | #undef LZAV_UNLIKELY 2817 | #undef LZAV_RESTRICT 2818 | #undef LZAV_PREFETCH 2819 | #undef LZAV_STATIC 2820 | #undef LZAV_INLINE 2821 | #undef LZAV_INLINE_F 2822 | #undef LZAV_NO_INLINE 2823 | 2824 | #endif // LZAV_INCLUDED 2825 | --------------------------------------------------------------------------------