From a9befe704f9550939da2687cb56dca3137c453ca Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Wed, 31 May 2023 12:03:06 +0200 Subject: [PATCH] Squashed 'src/c-blosc2/' changes from 72a17720f..10a16dc18 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit f344bb7c3 Getting ready for release 2.9.2 0a292210a Fix GCC compiler warning b6fbd00a2 Fix a compiler warning 1bbd9278a When re-registering a plugin do not do further actions 21adb4985 Do not return an error when registering a plugin with the same id/name 65a1a3034 Use plugin_module.print_libpath() as the way to get the libpath d24a03e7b tune plugins are now named tuners 56c1f541e Fix windows e8f85191e Add crossplatform dlerror d020bc168 Init tune each time cctx is created 3209b306e Improve detection of plugin locations d5a5b92af call stune blocksize cbe53d92c Fix some more warnings ca60217e4 Check the return value fgets() 07efcf511 Stop supporting ancient versions of Visual Studio eddd96544 Fix for the ZLIB_NG_INCLUDE_DIR variable Fixes #497. 9cc60eb03 BLOSC2_IO_CB_DEFAULTS moved to blosc2.c. Fixes #484. a7019e2af Silence unused codec_info typedef warning c9be81c30 Fix an issue with uncompressible data and lazy_chunks b66e38e97 g_tune moved to blosc2.c. Addresses #484 78fafb979 Fix linking with static -DBUILD_STATIC=0. Fixes #480. 84c11d3a1 Fix GCC warning: ignoring return value of fgets C 8f602184c Fix a fuzzer issue. Now, compress ret code can be negative too. f2873a8e0 Remove SNAPPY license and add ZSTD one. Fixes #495. 6c040285d Initialize blosc2 early c4f28daf2 Use "top-down" header file inclusion ordering ed5e5ff69 Merge branch 'DimitriPapadopoulos-size_t' 9b34ba114 Merge branch 'main' into size_t 849ed7cbe Define and use the ARRAY_SIZE macro in tests 805224d17 Consistent indention of structs 78d39789d free() string after, not before printing it 04035a375 BLOSC_STUNE is not defined in c-blosc f203d2f63 The size passed by the fuzzer is size_t, not int32_t c51d050df Activate PIC for static library too 5178ea860 Post 2.9.1 release actions done 4e78fbb9d Getting ready for release 2.9.1 2a0b89bce Add BTUNE_BALANCE support a1907bb2a Post 2.9.0 release actions done 4a12603f7 Getting ready for release 2.9.0 3c512b299 Get rid of the (undocumented) BLOSC_WARN envvar e1d20fd82 Add minor changes 28665a368 Remove stale lines fb6237c57 Rename btune to tune 9e5626d24 Add properly btune registry f6a701f52 Support diferent btunes c4433cd46 Remove htj2k 944e7ef1f Rename j2k id macro 01a9532c9 Change openhtj2k name 328c88f9c Add load library to plugins-utils.h 4d991a894 Add htj2k as plugin 00df585ff Making load_lib() to look for .dylib files to 1cc73eca5 Use info struct for getting function names 00576bfd0 Rename filter functions to forward & backward fe3550b86 Add bydelta name 3ac1de744 Add blosc2_* to plugins signatures 01808c5bf Fix windows issue II 809011909 Fix windows issue 56c3a65d0 Add support for dynplugins 7e06821a2 Add name & version to filter struct 7b1ce3ae6 Blosc Development team → Team 02a2f6a42 Standardise copyright/licence header 461478bde Blosc Developers → Development team f90524db8 fprintf(stderr) ↔ BLOSC_TRACE_ERROR() 4a2c844db Consistent indentation 70e4490b0 Consistent license and copyright 3a1eeb23d Variant for the © symbol: (C) → (c) bb7eff539 Allow more speed in BloscLZ clevel 1 09b4e461d No need to support Python 2 any more f7b1605b8 Zstd updated to latest 1.5.5 0aa01c655 Fix link 79fb8a974 Undo previous commit on _xgetbv (failing on MSVC) b20fd7400 Fix a compiler warning c2ddfea9f Always use assembly for _xgetbv on GCC/Clang (see https://github.com/Blosc/c-blosc/pull/362) e404e5ff3 Some cleanup in errors and error traces 9ba7d20e4 Removing a test because it used an invalid dataset 582ae3bc0 Undoing mistaken changes 765181d42 Advised changes 577431b86 Remove ZFP test because that dataset has blocks smaller than cells 99e399ebe Disable filters when using ZFP, use macros for plugins tests and making blocks bigger than cells for example_item_prices.b2nd f595beee8 Post 2.8.0 release actions done 8de035e51 Getting ready for release 2.8.0 9f126cc43 Fusion htab buffer b04deb3c8 More micro-optimizations for BloscLZ 218ff0e22 Update to zlib-ng 2.0.7 5d36b14b7 Do a full 14-bit hash even in entropy probe mode 20a5c2cd1 Check return pointer during open operations 3c234b099 Minor compression ratio improvements in BloscLZ codec 6ffb7f629 Fix invalid conversion from void* 1eb6e607f Fix some compiler warnings 718f41548 Fix indents d29259e29 Moved some functions necessary for plugins to blosc2.h and b2nd.h 76c5b3a4d Remove prints in ndlz 237f3e799 Missed removing a couple of printouts in prev commit a19be5481 Some cleanup of lossy filters and codecs e6efc097d Update API for (unused) func bbcc3c4e9 Always use blosc2_init/blosc2_destroy in programs 033c55307 Initiallize blosc2_io_cb in runtime 64f76c0dc Re-enable Windows GCC again (fingers crossed) 50e8d1c3e Bytedelta: enable SSE codepath for 32-bit Intel, use non-SIMD fallback for non-SSE/NEON platforms bf2363392 Updated script for downloading data 85d3d4f8c Remove (commented out) SSE4.2 flag from Win 7f187c9aa Relax SIMD dependency to SSSE3 and remove it from Win 99b749e6d Use names wich are more C99 like f7d622c6f Fixing compile issues on MSVC d63a82f85 Pass sse4.2 flags to compilers 2a1a3bde2 Preliminary version of the new bytedelta filter f95bf2ea5 Some cleanup in plugins bc9627ffd Add codec and filter params to cparams cf6300097 Make doc references using the full URL 022665719 b2nd: Use const where possible. e0910383c Optimise PNG compression f6a9a0f54 Use a reference to the section cbd7e2de6 Make images a bit smaller 42c88c45b Add the video on pineapple-style slicing a3bf05915 Added the video on double partitioning 33cc204df Replaced local images by URL df3c20fd4 Add the reference to the b2nd blog on release notes f7ca0762b Post 2.7.1 release actions done 68841cc38 Getting ready for release 2.7.1 422548f03 Date updated 2dbff4883 Add references to the new blog on NDim 819fdc043 Ninja should be not necessary anymore 097754571 Getting ready for release 2.7.0 317537399 Updated README for new b2nd (more info on b2nd) a9d15a1ab Updated README for new b2nd 829a4da9e Commenting out GCC on Win aeb122fcb Improve some docstrings 312c75eba Test file renaming 878f8d5cc Some syntax changes 94d2f00a5 Added test_b2nd_frame_offset 475f47b88 Added function b2nd_open_offset() 318343e66 Fix docs on b2nd layer 945aef15b Be more explicit on dtype interpretation 9767c987d zstd sources updated to 1.5.4 d9a3e482b export more schunk API 2954de300 Remove unneeded if ca9d7c6f4 Fix issue related with b2nd metalayer d0b73f861 Fix deserialize meta 439d9048b Always copy dtype and free it 43d798459 Try removing mask f7beca08e Fix for the number of fields in b2nd metalayer 6de418918 Fix malloc size and typo 8123f7ea5 Add a dtype format in the b2nd metalayer b9579fbfc Docstrings for orthogonal selections (set/get) e7b610941 Docstring for the new dtype arg 323e0b097 The convention for storing dtype is str() e347c44e8 Fix a leak in examples 1e65a17e9 New mandatory (numpy) dtype for b2nd layer a03fadfb3 Fix typo found by codespell 83d00f231 offset is already an int64_t array, so do not cast 66ae0a481 Avoid duplicate license file 58d05a820 Fix link to python-blosc bfd650f00 Indent file cb21c6959 Document that support for Snappy codec has been removed 60c102393 Canonical landing page for LZ4 6644b8877 Do not treat LZ4 differently than BloscLZ b5a45745f Mute an unused var warning 4489a0836 Reformat b2nd files from 4 spaces indents to 2 f5fad7e26 Merge branch 'merge-caterva' of github.com:Blosc/c-blosc2 into merge-caterva 5a3a6f66e Fix a uninitialized issue 665592e38 Merge branch 'merge-caterva' of https://github.com/Blosc/c-blosc2 into merge-caterva f24f6b9ee Working on Win bug 3 8ff8d00dd Be able to read frames with caterva metalayers 517dd1fd2 Working on Win bug 2 510df9394 Working on Win bug 62696b2aa Trying to fix Win bug 019118e3d fixing silly bug 22b900bf7 Most plugins frames now are generated during testing process instead of storing the data permanently 2e813b0c5 More small touches on b2nd API docs b727d1508 Change to 2 space ident e751187b2 Some naming refactoring (continue) 0a6fb43eb Some naming refactoring cbd6a7c15 Added Blosc2 NDim API to documentation 91891a3f6 Document a method for printing metalayers from command line e0c3f9d41 add BLOSC_EXPORT to make public b2nd functions and make b2nd_context opaque eb5849394 Remove empty space c72ed9ef2 Merge branch 'merge_caterva2' of github.com:Blosc/c-blosc2 into merge_caterva2 a4bb72606 Add the b2nd metalayer spec 4a7ce3384 Add BLOSC_ERROR usage 3304bcd07 Replace some data files with caterva metalayer by b2nd b6e2e566d Replace .caterva extension by .b2nd 24e02706c fixing refactoring mistake f2aab5010 caterva --> b2nd be30e1be6 Some small typos and enhancements e8474c157 Return error if problem with malloc d571f3f30 Fix a warning (unused variable) 8b473c8b3 Fix bug 230ef6c91 Fusion blosc and caterva error macros dc1cae176 Remove context from array structure 86d67cca4 Change caterva_free signature ca17c0477 Remove unused test_data 14f8cabab Merging changes for this new API iteration b3786cca1 End of the changes for this new API iteration bcb1b9267 fixing warnings about types and defined but not used parameters 697d9537a adding caterva array frees in benchmarks and examples 71fb55721 Start a new API change d30aa8087 solving some warnings about types and urfilters parameters e86541c24 Fix a silly bug e67eb5d45 Do not require b2_storage or cparams to be filled 6789e6e6d Make example run in less time 2e4751dee Fix not allowed empty struct on win 4dc214296 WIP. Remove ctx in some functions 0d0690736 Reformat code. Join caterva_storage into caterva_params e8f4fd016 Add caterva examples in the tests suite 5baa586da Fix examples 9fbe10ed0 Remove an unused variable 5b230085b filling correctly caterva tests data field and replacing long expressions for typesize assignments 52fcbe87e fixing caterva tests errors 6c97ddcbf WIP. Add blosc2_storage to caterva_storage 6671512f1 Add forgotten blosc2_context_free 8a69edb8d caterva_config_t --> blosc2_cparams caterva_ctx --> blosc2_context 1ae637122 Fix for qemu-aarch64 in CI b1149394a Use blosc2_remove and blosc2*meta* functions 66b5cb289 Remove free and alloc, uniform some param names 21621cde5 Enable threading in caterva tests 9570bf7c0 Disable threading in caterva tests 35db0f694 Make tests run in less time 3a1cd3c75 Attempt to make disk operations to work on mingw32 fc08d1ffc Make test run faster 8c5033590 Fix some issues with non-compulsory ZSTD 6548d1680 Updated copyright to all caterva sources 1081c5b2c Relocate swap_store and protection for plugins examples 20dfceb78 Merge branch 'caterva' of https://github.com/jdavid/c-blosc2 into merge-caterva f7f92cb55 Fix typos found by codespell ac8be1af1 Update to the latest FastLZ license 6c43e58cc Update to the latest zlib(-ng) license ffbe0dd22 Update to the latest LZ4 library license 260aa67b4 http:// → https:// bf641bedb Fix build with clang 72575da0d Fix compilation on x86_64: revert commit 89f9335 339409412 Set optimisation flags when building shuffle.c a4fdb994a Fixing CI (wip) b4d57c6c5 Merge caterva e204359a2 Link to new Blosc intro video 2a18ffb98 Remove unsupported win32 platform 136930229 Post 2.6.1 release actions done git-subtree-dir: src/c-blosc2 git-subtree-split: 10a16dc18890c3ca6653016be409c5b2d3bb1e7a --- .github/workflows/cmake.yml | 12 +- .gitignore | 2 + ANNOUNCE.md | 18 +- CMakeLists.txt | 10 +- LICENSE.txt | 4 +- LICENSES/FASTLZ.txt | 8 +- LICENSES/LZ4.txt | 48 +- LICENSES/SNAPPY.txt | 28 - LICENSES/STDINT.txt | 29 - LICENSES/ZLIB.txt | 31 +- LICENSES/{BLOSC.txt => ZSTD.txt} | 9 +- README.rst | 101 +- README_ARM.rst | 2 +- README_B2ND_METALAYER.rst | 67 + README_CFRAME_FORMAT.rst | 38 +- RELEASE_NOTES.md | 96 +- ROADMAP.rst | 4 +- bench/CMakeLists.txt | 10 + bench/b2bench.c | 2 +- bench/b2nd/CMakeLists.txt | 16 + bench/b2nd/bench_get_slice.c | 86 + bench/b2nd/bench_zfp_getitem.c | 255 ++ bench/create_frame.c | 6 +- bench/delta_schunk.c | 2 +- bench/plot-speeds.py | 4 +- bench/read-grid-150x150.py | 4 +- bench/sframe_bench.c | 28 +- bench/sum_openmp.c | 11 +- bench/trunc_prec_schunk.c | 2 +- bench/zero_runlen.c | 2 +- blosc/CMakeLists.txt | 48 +- blosc/b2nd.c | 1846 ++++++++ blosc/b2nd_utils.c | 324 ++ blosc/b2nd_utils.h | 33 + blosc/bitshuffle-altivec.c | 6 +- blosc/bitshuffle-altivec.h | 2 +- blosc/bitshuffle-avx2.c | 9 +- blosc/bitshuffle-avx2.h | 2 +- blosc/bitshuffle-generic.c | 2 +- blosc/bitshuffle-generic.h | 2 +- blosc/bitshuffle-neon.c | 4 +- blosc/bitshuffle-neon.h | 2 +- blosc/bitshuffle-sse2.c | 6 +- blosc/bitshuffle-sse2.h | 2 +- blosc/blosc-private.h | 24 +- blosc/blosc2-stdio.c | 8 +- blosc/blosc2.c | 492 ++- blosc/blosclz.c | 93 +- blosc/blosclz.h | 4 +- blosc/context.h | 31 +- blosc/delta.c | 5 +- blosc/delta.h | 2 +- blosc/directories.c | 8 +- blosc/fastcopy.c | 5 +- blosc/fastcopy.h | 2 +- blosc/frame.c | 169 +- blosc/frame.h | 5 +- blosc/schunk.c | 111 +- blosc/sframe.c | 11 +- blosc/sframe.h | 2 +- blosc/shuffle-altivec.c | 4 +- blosc/shuffle-altivec.h | 2 +- blosc/shuffle-avx2.c | 4 +- blosc/shuffle-avx2.c.orig | 747 ---- blosc/shuffle-avx2.h | 2 +- blosc/shuffle-generic.c | 2 +- blosc/shuffle-generic.h | 2 +- blosc/shuffle-neon.c | 4 +- blosc/shuffle-neon.h | 2 +- blosc/shuffle-sse2.c | 4 +- blosc/shuffle-sse2.h | 2 +- blosc/shuffle.c | 18 +- blosc/shuffle.h | 2 +- blosc/stune.c | 9 +- blosc/stune.h | 12 +- blosc/timestamp.c | 2 +- blosc/transpose-altivec.h | 2 +- blosc/trunc-prec.c | 13 +- blosc/trunc-prec.h | 2 +- blosc/win32/pthread.c | 10 +- blosc/win32/pthread.h | 2 +- blosc/win32/stdint-windows.h | 259 -- cmake/FindZLIB_NG.cmake | 16 +- compat/filegen.c | 19 +- .../unshuffle16_neon/unshuffle16_neon_vtbx.c | 2 +- .../unshuffle16_neon_vtbx_bucle.c | 2 +- doc/Doxyfile | 4 +- doc/conf.py | 4 +- doc/reference/b2nd.rst | 103 + doc/reference/index.rst | 3 +- examples/CMakeLists.txt | 6 +- examples/b2nd/CMakeLists.txt | 28 + examples/b2nd/example_empty_shape.c | 79 + examples/b2nd/example_frame_generator.c | 359 ++ examples/b2nd/example_oindex.c | 73 + examples/b2nd/example_plainbuffer.c | 81 + examples/b2nd/example_plugins_codecs.c | 103 + examples/b2nd/example_plugins_filters.c | 100 + examples/b2nd/example_print_meta.c | 53 + examples/b2nd/example_serialize.c | 79 + examples/compress_file.c | 2 +- examples/contexts.c | 6 +- examples/delta_schunk_ex.c | 2 +- examples/find_roots.c | 2 +- examples/frame_backed_schunk.c | 6 +- examples/frame_big.c | 2 +- examples/frame_metalayers.c | 2 +- examples/frame_offset.c | 6 +- examples/frame_roundtrip.c | 6 +- examples/frame_simple.c | 14 +- examples/frame_vlmetalayers.c | 2 +- examples/get_set_slice.c | 6 +- examples/instrument_codec.c | 6 +- examples/many_compressors.c | 2 +- examples/multithread.c | 2 +- examples/noinit.c | 2 +- examples/schunk_postfilter.c | 13 +- examples/schunk_simple.c | 6 +- examples/sframe_simple.c | 2 +- examples/simple.c | 22 +- examples/urcodecs.c | 18 +- examples/urfilters.c | 12 +- examples/win-dynamic-linking.c | 2 +- examples/zstd_dict.c | 2 +- images/Complete-Write-Read-B2ND.png | Bin 0 -> 116429 bytes images/Read-Partial-Slices-B2ND.png | Bin 0 -> 113029 bytes images/b2nd-2level-parts.png | Bin 0 -> 314966 bytes include/b2nd.h | 606 +++ include/blosc2.h | 355 +- include/blosc2/blosc2-common.h | 26 +- include/blosc2/blosc2-export.h | 2 +- include/blosc2/blosc2-stdio.h | 24 +- include/blosc2/codecs-registry.h | 11 +- include/blosc2/filters-registry.h | 9 +- include/blosc2/plugins-utils.h | 100 + include/blosc2/tuners-registry.h | 24 + .../zlib-ng-2.0.6/.gitattributes | 6 - .../.github/workflows/analyze.yml | 44 - .../zlib-ng-2.0.6/.github/workflows/cmake.yml | 452 -- .../.github/workflows/configure.yml | 207 - .../zlib-ng-2.0.6/.github/workflows/fuzz.yml | 23 - .../.github/workflows/libpng.yml | 46 - .../zlib-ng-2.0.6/.github/workflows/nmake.yml | 48 - .../zlib-ng-2.0.6/.github/workflows/pigz.yml | 111 - .../.github/workflows/pkgcheck.yml | 147 - .../.github/workflows/release.yml | 97 - internal-complibs/zlib-ng-2.0.6/.gitignore | 88 - internal-complibs/zlib-ng-2.0.6/chunkset.c | 83 - .../cmake/toolchain-mingw-i686.cmake | 16 - .../cmake/toolchain-mingw-x86_64.cmake | 16 - .../.shellcheckrc | 0 .../CMakeLists.txt | 99 +- .../{zlib-ng-2.0.6 => zlib-ng-2.0.7}/FAQ.zlib | 0 .../{zlib-ng-2.0.6 => zlib-ng-2.0.7}/INDEX.md | 0 .../LICENSE.md | 0 .../Makefile.in | 43 +- .../PORTING.md | 14 +- .../README.md | 5 +- .../adler32.c | 0 .../adler32_p.h | 0 .../arch/.gitignore | 0 .../arch/arm/Makefile.in | 0 .../arch/arm/adler32_neon.c | 0 .../arch/arm/arm.h | 0 .../arch/arm/armfeature.c | 3 + .../arch/arm/chunkset_neon.c | 11 +- .../arch/arm/crc32_acle.c | 34 +- .../arch/arm/ctzl.h | 0 .../arch/arm/insert_string_acle.c | 0 .../arch/arm/slide_neon.c | 0 .../arch/generic/Makefile.in | 0 .../arch/power/Makefile.in | 0 .../arch/power/adler32_power8.c | 0 .../arch/power/power.c | 0 .../arch/power/power.h | 0 .../arch/power/slide_hash_power8.c | 0 .../arch/s390/Makefile.in | 0 .../arch/s390/README.md | 0 .../arch/s390/dfltcc_common.c | 0 .../arch/s390/dfltcc_common.h | 0 .../arch/s390/dfltcc_deflate.c | 10 +- .../arch/s390/dfltcc_deflate.h | 0 .../arch/s390/dfltcc_detail.h | 176 +- .../arch/s390/dfltcc_inflate.c | 8 +- .../arch/s390/dfltcc_inflate.h | 0 .../actions-runner.Dockerfile | 2 + .../actions-runner.service | 0 .../fs/usr/bin/actions-runner | 0 .../self-hosted-builder/fs/usr/bin/entrypoint | 0 .../qemu-user-static.service | 0 .../arch/x86/INDEX.md | 0 .../arch/x86/Makefile.in | 0 .../arch/x86/adler32_avx.c | 0 .../arch/x86/adler32_ssse3.c | 0 .../arch/x86/chunkset_avx.c | 17 +- .../arch/x86/chunkset_sse.c | 17 +- .../arch/x86/compare258_avx.c | 0 .../arch/x86/compare258_sse.c | 0 .../arch/x86/crc_folding.c | 0 .../arch/x86/crc_folding.h | 0 .../arch/x86/insert_string_sse.c | 0 .../arch/x86/slide_avx.c | 0 .../arch/x86/slide_sse.c | 0 .../arch/x86/x86.c | 0 .../arch/x86/x86.h | 0 internal-complibs/zlib-ng-2.0.7/chunkset.c | 47 + .../chunkset_tpl.h | 5 +- .../cmake/detect-arch.c | 2 +- .../cmake/detect-arch.cmake | 0 .../cmake/detect-coverage.cmake | 0 .../cmake/detect-install-dirs.cmake | 0 .../cmake/detect-sanitizer.cmake | 8 +- .../cmake/run-and-compare.cmake | 18 +- .../cmake/run-and-redirect.cmake | 0 .../cmake/test-compress.cmake | 2 +- .../cmake/test-tools.cmake | 0 .../cmake/toolchain-aarch64.cmake | 8 +- .../zlib-ng-2.0.7/cmake/toolchain-arm.cmake | 29 + .../cmake/toolchain-armhf.cmake} | 9 +- .../cmake/toolchain-mingw-i686.cmake | 35 + .../cmake/toolchain-mingw-x86_64.cmake | 34 + .../cmake/toolchain-powerpc.cmake | 12 +- .../cmake/toolchain-powerpc64.cmake | 12 +- .../cmake/toolchain-powerpc64le.cmake | 12 +- .../cmake/toolchain-s390x.cmake | 10 +- .../cmake/toolchain-sparc64.cmake | 10 +- .../compare258.c | 0 .../compress.c | 2 + .../configure | 77 +- .../{zlib-ng-2.0.6 => zlib-ng-2.0.7}/crc32.c | 0 .../crc32_comb.c | 0 .../crc32_comb_tbl._h} | 0 .../zlib-ng-2.0.7/crc32_comb_tbl.h | 300 ++ .../crc32_p.h | 0 .../crc32_tbl._h} | 0 internal-complibs/zlib-ng-2.0.7/crc32_tbl.h | 444 ++ .../deflate.c | 22 +- .../deflate.h | 78 +- .../deflate_fast.c | 0 .../deflate_medium.c | 0 .../deflate_p.h | 0 .../deflate_quick.c | 0 .../deflate_slow.c | 0 .../doc/algorithm.txt | 0 .../doc/rfc1950.txt | 0 .../doc/rfc1951.txt | 0 .../doc/rfc1952.txt | 0 .../doc/txtvsbin.txt | 0 .../fallback_builtins.h | 4 + .../functable.c | 0 .../functable.h | 0 .../{zlib-ng-2.0.6 => zlib-ng-2.0.7}/gzguts.h | 11 +- .../{zlib-ng-2.0.6 => zlib-ng-2.0.7}/gzlib.c | 18 - .../{zlib-ng-2.0.6 => zlib-ng-2.0.7}/gzread.c | 8 +- .../gzwrite.c | 2 +- .../infback.c | 14 +- .../inffast.c | 0 .../inffast.h | 0 .../inffixed_tbl._h} | 0 .../zlib-ng-2.0.7/inffixed_tbl.h | 94 + .../inflate.c | 17 +- .../inflate.h | 0 .../inflate_p.h | 0 .../inftrees.c | 2 +- .../inftrees.h | 0 .../insert_string.c | 0 .../insert_string_tpl.h | 0 .../match_tpl.h | 0 .../test/.gitignore | 0 .../test/CVE-2002-0059/test.gz | Bin .../test/CVE-2003-0107.c | 0 .../test/CVE-2004-0797/test.gz | Bin .../test/CVE-2005-1849/test.gz | Bin .../test/CVE-2005-2096/test.gz | Bin .../test/CVE-2018-25032/default.txt | 1 + .../test/CVE-2018-25032/fixed.txt | 1 + .../test/GH-361/test.txt | 0 .../test/GH-364/test.bin | Bin .../test/GH-382/defneg3.dat | 0 .../test/GH-751/test.txt | 0 .../test/GH-979/pigz-2.6.tar.gz | Bin .../test/Makefile.in | 11 +- .../test/README.md | 3 +- .../test/abi/ignore | 0 .../abi/zlib-v1.2.11-arm-linux-gnueabihf.abi | 0 .../abi/zlib-v1.2.11-x86_64-linux-gnu.abi | 0 .../test/abicheck.md | 0 .../test/abicheck.sh | 24 +- .../test/adler32_test.c | 0 .../test/crc32_test.c | 0 .../test/data/fireworks.jpg | Bin .../test/data/lcet10.txt | 0 .../test/data/paper-100k.pdf | Bin .../test/deflate_quick_bi_valid.c | 0 .../test/deflate_quick_block_open.c | 0 .../test/example.c | 2 +- .../test/fuzz/checksum_fuzzer.c | 0 .../test/fuzz/compress_fuzzer.c | 0 .../test/fuzz/example_dict_fuzzer.c | 0 .../test/fuzz/example_flush_fuzzer.c | 0 .../test/fuzz/example_large_fuzzer.c | 0 .../test/fuzz/example_small_fuzzer.c | 0 .../test/fuzz/minigzip_fuzzer.c | 0 .../test/fuzz/standalone_fuzz_target_runner.c | 0 internal-complibs/zlib-ng-2.0.7/test/gh1235.c | 39 + .../test/hash_head_0.c | 0 .../test/infcover.c | 5 + .../test/inflate_adler32.c | 0 .../test/minideflate.c | 85 +- .../test/minigzip.c | 21 +- .../test/pigz/CMakeLists.txt | 10 +- .../test/pkgcheck.sh | 2 +- .../test/switchlevels.c | 0 .../test/testCVEinputs.sh | 0 .../tools/codecov-upload.sh | 0 .../tools/config.sub | 0 .../tools/makecrct.c | 0 .../tools/makefixed.c | 0 .../tools/maketrees.c | 2 +- .../{zlib-ng-2.0.6 => zlib-ng-2.0.7}/trees.c | 4 +- .../{zlib-ng-2.0.6 => zlib-ng-2.0.7}/trees.h | 0 .../trees_emit.h | 0 .../trees_tbl._h} | 0 internal-complibs/zlib-ng-2.0.7/trees_tbl.h | 132 + .../uncompr.c | 0 .../win32/DLL_FAQ.txt | 0 .../win32/Makefile.a64 | 0 .../win32/Makefile.arm | 0 .../win32/Makefile.msc | 0 .../win32/README-WIN32.txt | 0 .../win32/zlib-ng.def | 0 .../win32/zlib-ng1.rc | 4 - .../win32/zlib.def | 0 .../win32/zlib1.rc | 4 - .../win32/zlibcompat.def | 0 .../{zlib-ng-2.0.6 => zlib-ng-2.0.7}/zbuild.h | 11 + .../zconf-ng.h.in | 1 - internal-complibs/zlib-ng-2.0.7/zconf.h | 201 + .../zconf.h.in | 8 +- .../zlib-ng-2.0.7/zconf.h.included | 201 + .../zendian.h | 0 .../zlib-ng.h | 8 +- .../zlib-ng.map | 1 - .../{zlib-ng-2.0.6 => zlib-ng-2.0.7}/zlib.h | 13 +- .../{zlib-ng-2.0.6 => zlib-ng-2.0.7}/zlib.map | 1 - .../zlib.pc.cmakein | 0 .../zlib.pc.in | 0 .../{zlib-ng-2.0.6 => zlib-ng-2.0.7}/zutil.c | 2 +- .../{zlib-ng-2.0.6 => zlib-ng-2.0.7}/zutil.h | 0 .../zutil_p.h | 4 +- .../zstd-1.5.2/common/threading.c | 122 - .../compress/zstd_compress_literals.c | 159 - .../{zstd-1.5.2 => zstd-1.5.5}/.gitignore | 0 .../{zstd-1.5.2 => zstd-1.5.5}/BUCK | 0 .../{zstd-1.5.2 => zstd-1.5.5}/Makefile | 4 +- .../{zstd-1.5.2 => zstd-1.5.5}/README.md | 9 +- .../zstd-1.5.5/common/allocations.h | 55 + internal-complibs/zstd-1.5.5/common/bits.h | 200 + .../common/bitstream.h | 79 +- .../common/compiler.h | 29 +- .../{zstd-1.5.2 => zstd-1.5.5}/common/cpu.h | 2 +- .../{zstd-1.5.2 => zstd-1.5.5}/common/debug.c | 2 +- .../{zstd-1.5.2 => zstd-1.5.5}/common/debug.h | 2 +- .../common/entropy_common.c | 52 +- .../common/error_private.c | 11 +- .../common/error_private.h | 2 +- .../{zstd-1.5.2 => zstd-1.5.5}/common/fse.h | 88 +- .../common/fse_decompress.c | 106 +- .../{zstd-1.5.2 => zstd-1.5.5}/common/huf.h | 221 +- .../{zstd-1.5.2 => zstd-1.5.5}/common/mem.h | 85 +- .../{zstd-1.5.2 => zstd-1.5.5}/common/pool.c | 36 +- .../{zstd-1.5.2 => zstd-1.5.5}/common/pool.h | 8 +- .../common/portability_macros.h | 25 +- .../zstd-1.5.5/common/threading.c | 176 + .../common/threading.h | 15 +- .../common/xxhash.c | 4 +- .../common/xxhash.h | 16 +- .../common/zstd_common.c | 37 +- .../common/zstd_deps.h | 2 +- .../common/zstd_internal.h | 135 +- .../common/zstd_trace.h | 6 +- .../compress/clevels.h | 2 +- .../compress/fse_compress.c | 131 +- .../compress/hist.c | 2 +- .../compress/hist.h | 2 +- .../compress/huf_compress.c | 403 +- .../compress/zstd_compress.c | 1781 +++++--- .../compress/zstd_compress_internal.h | 376 +- .../compress/zstd_compress_literals.c | 235 + .../compress/zstd_compress_literals.h | 24 +- .../compress/zstd_compress_sequences.c | 6 +- .../compress/zstd_compress_sequences.h | 2 +- .../compress/zstd_compress_superblock.c | 46 +- .../compress/zstd_compress_superblock.h | 2 +- .../compress/zstd_cwksp.h | 190 +- .../compress/zstd_double_fast.c | 128 +- .../compress/zstd_double_fast.h | 5 +- .../compress/zstd_fast.c | 581 ++- .../compress/zstd_fast.h | 5 +- .../compress/zstd_lazy.c | 743 ++-- .../compress/zstd_lazy.h | 6 +- .../compress/zstd_ldm.c | 10 +- .../compress/zstd_ldm.h | 2 +- .../compress/zstd_ldm_geartab.h | 2 +- .../compress/zstd_opt.c | 186 +- .../compress/zstd_opt.h | 2 +- .../compress/zstdmt_compress.c | 26 +- .../compress/zstdmt_compress.h | 2 +- .../decompress/huf_decompress.c | 875 ++-- .../decompress/huf_decompress_amd64.S | 69 +- .../decompress/zstd_ddict.c | 8 +- .../decompress/zstd_ddict.h | 2 +- .../decompress/zstd_decompress.c | 285 +- .../decompress/zstd_decompress_block.c | 282 +- .../decompress/zstd_decompress_block.h | 7 +- .../decompress/zstd_decompress_internal.h | 6 +- .../deprecated/zbuff.h | 2 +- .../deprecated/zbuff_common.c | 2 +- .../deprecated/zbuff_compress.c | 2 +- .../deprecated/zbuff_decompress.c | 4 +- .../dictBuilder/cover.c | 34 +- .../dictBuilder/cover.h | 2 +- .../dictBuilder/divsufsort.c | 0 .../dictBuilder/divsufsort.h | 0 .../dictBuilder/fastcover.c | 4 +- .../dictBuilder/zdict.c | 100 +- .../dll/example/Makefile | 2 +- .../dll/example/README.md | 2 +- .../dll/example/fullbench-dll.sln | 0 .../dll/example/fullbench-dll.vcxproj | 0 .../legacy/zstd_legacy.h | 9 +- .../legacy/zstd_v01.c | 63 +- .../legacy/zstd_v01.h | 2 +- .../legacy/zstd_v02.c | 85 +- .../legacy/zstd_v02.h | 2 +- .../legacy/zstd_v03.c | 89 +- .../legacy/zstd_v03.h | 2 +- .../legacy/zstd_v04.c | 84 +- .../legacy/zstd_v04.h | 2 +- .../legacy/zstd_v05.c | 104 +- .../legacy/zstd_v05.h | 2 +- .../legacy/zstd_v06.c | 113 +- .../legacy/zstd_v06.h | 2 +- .../legacy/zstd_v07.c | 101 +- .../legacy/zstd_v07.h | 2 +- .../{zstd-1.5.2 => zstd-1.5.5}/libzstd.mk | 49 +- .../{zstd-1.5.2 => zstd-1.5.5}/libzstd.pc.in | 6 +- .../module.modulemap | 16 +- .../{zstd-1.5.2 => zstd-1.5.5}/zdict.h | 84 +- .../{zstd-1.5.2 => zstd-1.5.5}/zstd.h | 715 +++- .../{zstd-1.5.2 => zstd-1.5.5}/zstd_errors.h | 35 +- plugins/CMakeLists.txt | 11 +- plugins/README.md | 26 +- plugins/codecs/CMakeLists.txt | 2 +- plugins/codecs/codecs-registry.c | 70 +- plugins/codecs/ndlz/CMakeLists.txt | 19 +- plugins/codecs/ndlz/README.md | 9 +- plugins/codecs/ndlz/ndlz-private.h | 13 +- plugins/codecs/ndlz/ndlz.c | 72 +- plugins/codecs/ndlz/ndlz.h | 6 +- plugins/codecs/ndlz/ndlz4x4.c | 155 +- plugins/codecs/ndlz/ndlz4x4.h | 4 +- plugins/codecs/ndlz/ndlz8x8.c | 133 +- plugins/codecs/ndlz/ndlz8x8.h | 6 +- plugins/codecs/ndlz/test_ndlz.c | 447 +- plugins/codecs/ndlz/xxhash.h | 3811 +++++++++-------- plugins/codecs/zfp/CMakeLists.txt | 13 +- plugins/codecs/zfp/README.md | 2 +- plugins/codecs/zfp/blosc2-zfp.c | 1624 ++++--- plugins/codecs/zfp/blosc2-zfp.h | 15 +- plugins/codecs/zfp/test_zfp_acc_float.c | 452 +- plugins/codecs/zfp/test_zfp_acc_int.c | 148 - plugins/codecs/zfp/test_zfp_prec_float.c | 462 +- plugins/codecs/zfp/test_zfp_rate_float.c | 449 +- plugins/codecs/zfp/test_zfp_rate_getitem.c | 547 +-- plugins/codecs/zfp/zfp-private.h | 12 +- plugins/filters/CMakeLists.txt | 11 +- plugins/filters/bytedelta/CMakeLists.txt | 32 + plugins/filters/bytedelta/bytedelta.c | 162 + plugins/filters/bytedelta/bytedelta.h | 19 + plugins/filters/bytedelta/test_bytedelta.c | 258 ++ plugins/filters/filters-registry.c | 40 +- plugins/filters/ndcell/CMakeLists.txt | 4 +- plugins/filters/ndcell/README.md | 35 +- plugins/filters/ndcell/ndcell.c | 432 +- plugins/filters/ndcell/ndcell.h | 12 +- plugins/filters/ndcell/test_ndcell.c | 317 +- plugins/filters/ndmean/CMakeLists.txt | 8 +- plugins/filters/ndmean/README.md | 4 +- .../ndmean/example_ndmean_1dim_2rows.caterva | Bin 4327 -> 0 bytes .../example_ndmean_1dim_same_cells.caterva | Bin 2895 -> 0 bytes .../example_ndmean_1dim_some_matches.caterva | Bin 5537 -> 0 bytes .../ndmean/example_ndmean_repart_rand.caterva | Bin 8824 -> 0 bytes .../example_ndmean_repart_same_cells.caterva | Bin 19437 -> 0 bytes ...example_ndmean_repart_some_matches.caterva | Bin 18511 -> 0 bytes plugins/filters/ndmean/ndmean.c | 555 +-- plugins/filters/ndmean/ndmean.h | 12 +- plugins/filters/ndmean/test_ndmean_mean.c | 473 +- plugins/filters/ndmean/test_ndmean_repart.c | 319 +- plugins/plugin_utils.c | 61 +- plugins/plugin_utils.h | 6 +- plugins/test_data/README.md | 24 +- plugins/test_data/example_day_month_temp.b2nd | Bin 0 -> 2633 bytes .../test_data/example_day_month_temp.caterva | Bin 1324 -> 0 bytes .../example_double_same_cells.caterva | Bin 1340 -> 0 bytes .../test_data/example_float_cyclic.caterva | Bin 161525 -> 0 bytes plugins/test_data/example_item_prices.b2nd | Bin 0 -> 141455 bytes plugins/test_data/example_item_prices.caterva | Bin 159155 -> 0 bytes plugins/test_data/example_rand.caterva | Bin 49045 -> 0 bytes plugins/test_data/example_same_cells.caterva | Bin 49957 -> 0 bytes .../test_data/example_some_matches.caterva | Bin 29349 -> 0 bytes plugins/tunes/CMakeLists.txt | 1 + plugins/tunes/tunes-registry.c | 22 + tests/CMakeLists.txt | 10 +- tests/b2nd/CMakeLists.txt | 18 + tests/b2nd/cutest.h | 203 + tests/b2nd/test_b2nd_append.c | 158 + tests/b2nd/test_b2nd_copy.c | 181 + tests/b2nd/test_b2nd_delete.c | 180 + tests/b2nd/test_b2nd_full.c | 137 + tests/b2nd/test_b2nd_get_slice.c | 172 + tests/b2nd/test_b2nd_get_slice_buffer.c | 138 + tests/b2nd/test_b2nd_insert.c | 156 + tests/b2nd/test_b2nd_metalayers.c | 157 + tests/b2nd/test_b2nd_open_offset.c | 195 + tests/b2nd/test_b2nd_persistency.c | 119 + tests/b2nd/test_b2nd_resize.c | 206 + tests/b2nd/test_b2nd_roundtrip.c | 78 + tests/b2nd/test_b2nd_save.c | 116 + tests/b2nd/test_b2nd_serialize.c | 96 + tests/b2nd/test_b2nd_set_slice_buffer.c | 150 + tests/b2nd/test_b2nd_squeeze.c | 135 + tests/b2nd/test_b2nd_squeeze_index.c | 147 + tests/b2nd/test_b2nd_uninit.c | 85 + tests/b2nd/test_b2nd_zeros.c | 94 + tests/b2nd/test_common.h | 99 + tests/cutest.h | 21 +- tests/fuzz/fuzz_compress_chunk.c | 11 +- tests/fuzz/fuzz_compress_frame.c | 8 +- tests/fuzz/generate_inputs_corpus.c | 8 +- tests/fuzz/standalone.c | 5 +- tests/gcc-segfault-issue.c | 2 +- tests/print_versions.c | 2 +- tests/test_all.sh | 4 +- tests/test_api.c | 2 +- tests/test_bitshuffle_leftovers.c | 2 +- tests/test_blosc1_compat.c | 2 +- tests/test_change_nthreads_append.c | 2 +- tests/test_common.h | 15 +- tests/test_compress_roundtrip.c | 2 +- tests/test_compressor.c | 2 +- tests/test_contexts.c | 6 +- tests/test_copy.c | 4 +- tests/test_delete_chunk.c | 8 +- tests/test_delta.c | 2 +- tests/test_delta_schunk.c | 2 +- tests/test_dict_schunk.c | 2 +- tests/test_empty_buffer.c | 2 +- tests/test_fill_special.c | 4 +- tests/test_frame.c | 6 +- tests/test_frame_get_offsets.c | 4 +- tests/test_frame_offset.c | 15 +- tests/test_get_slice_buffer.c | 9 +- tests/test_getitem.c | 2 +- tests/test_getitem_delta.c | 2 +- tests/test_insert_chunk.c | 10 +- tests/test_lazychunk.c | 2 +- tests/test_lazychunk_memcpyed.c | 56 +- tests/test_maskout.c | 5 +- tests/test_maxout.c | 10 +- tests/test_noinit.c | 2 +- tests/test_nolock.c | 2 +- tests/test_nthreads.c | 2 +- tests/test_postfilter.c | 6 +- tests/test_prefilter.c | 5 +- tests/test_reorder_offsets.c | 8 +- tests/test_schunk.c | 2 +- tests/test_schunk_frame.c | 2 +- tests/test_schunk_header.c | 2 +- tests/test_set_slice_buffer.c | 9 +- tests/test_sframe.c | 2 +- tests/test_sframe_lazychunk.c | 2 +- tests/test_shuffle_roundtrip_altivec.c | 2 +- tests/test_shuffle_roundtrip_avx2.c | 2 +- tests/test_shuffle_roundtrip_generic.c | 2 +- tests/test_shuffle_roundtrip_neon.c | 2 +- tests/test_shuffle_roundtrip_sse2.c | 2 +- tests/test_small_chunks.c | 8 +- tests/test_udio.c | 4 +- tests/test_update_chunk.c | 10 +- tests/test_urcodecs.c | 18 +- tests/test_urfilters.c | 33 +- tests/test_zero_runlen.c | 4 +- 593 files changed, 23163 insertions(+), 13082 deletions(-) delete mode 100644 LICENSES/SNAPPY.txt delete mode 100644 LICENSES/STDINT.txt rename LICENSES/{BLOSC.txt => ZSTD.txt} (78%) create mode 100644 README_B2ND_METALAYER.rst create mode 100644 bench/b2nd/CMakeLists.txt create mode 100644 bench/b2nd/bench_get_slice.c create mode 100644 bench/b2nd/bench_zfp_getitem.c create mode 100644 blosc/b2nd.c create mode 100644 blosc/b2nd_utils.c create mode 100644 blosc/b2nd_utils.h delete mode 100644 blosc/shuffle-avx2.c.orig delete mode 100644 blosc/win32/stdint-windows.h create mode 100644 doc/reference/b2nd.rst create mode 100644 examples/b2nd/CMakeLists.txt create mode 100644 examples/b2nd/example_empty_shape.c create mode 100644 examples/b2nd/example_frame_generator.c create mode 100644 examples/b2nd/example_oindex.c create mode 100644 examples/b2nd/example_plainbuffer.c create mode 100644 examples/b2nd/example_plugins_codecs.c create mode 100644 examples/b2nd/example_plugins_filters.c create mode 100644 examples/b2nd/example_print_meta.c create mode 100644 examples/b2nd/example_serialize.c create mode 100644 images/Complete-Write-Read-B2ND.png create mode 100644 images/Read-Partial-Slices-B2ND.png create mode 100644 images/b2nd-2level-parts.png create mode 100644 include/b2nd.h create mode 100644 include/blosc2/plugins-utils.h create mode 100644 include/blosc2/tuners-registry.h delete mode 100644 internal-complibs/zlib-ng-2.0.6/.gitattributes delete mode 100644 internal-complibs/zlib-ng-2.0.6/.github/workflows/analyze.yml delete mode 100644 internal-complibs/zlib-ng-2.0.6/.github/workflows/cmake.yml delete mode 100644 internal-complibs/zlib-ng-2.0.6/.github/workflows/configure.yml delete mode 100644 internal-complibs/zlib-ng-2.0.6/.github/workflows/fuzz.yml delete mode 100644 internal-complibs/zlib-ng-2.0.6/.github/workflows/libpng.yml delete mode 100644 internal-complibs/zlib-ng-2.0.6/.github/workflows/nmake.yml delete mode 100644 internal-complibs/zlib-ng-2.0.6/.github/workflows/pigz.yml delete mode 100644 internal-complibs/zlib-ng-2.0.6/.github/workflows/pkgcheck.yml delete mode 100644 internal-complibs/zlib-ng-2.0.6/.github/workflows/release.yml delete mode 100644 internal-complibs/zlib-ng-2.0.6/.gitignore delete mode 100644 internal-complibs/zlib-ng-2.0.6/chunkset.c delete mode 100644 internal-complibs/zlib-ng-2.0.6/cmake/toolchain-mingw-i686.cmake delete mode 100644 internal-complibs/zlib-ng-2.0.6/cmake/toolchain-mingw-x86_64.cmake rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/.shellcheckrc (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/CMakeLists.txt (94%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/FAQ.zlib (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/INDEX.md (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/LICENSE.md (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/Makefile.in (89%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/PORTING.md (82%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/README.md (97%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/adler32.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/adler32_p.h (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/.gitignore (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/arm/Makefile.in (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/arm/adler32_neon.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/arm/arm.h (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/arm/armfeature.c (95%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/arm/chunkset_neon.c (86%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/arm/crc32_acle.c (71%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/arm/ctzl.h (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/arm/insert_string_acle.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/arm/slide_neon.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/generic/Makefile.in (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/power/Makefile.in (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/power/adler32_power8.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/power/power.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/power/power.h (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/power/slide_hash_power8.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/s390/Makefile.in (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/s390/README.md (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/s390/dfltcc_common.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/s390/dfltcc_common.h (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/s390/dfltcc_deflate.c (98%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/s390/dfltcc_deflate.h (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/s390/dfltcc_detail.h (81%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/s390/dfltcc_inflate.c (93%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/s390/dfltcc_inflate.h (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/s390/self-hosted-builder/actions-runner.Dockerfile (96%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/s390/self-hosted-builder/actions-runner.service (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/s390/self-hosted-builder/fs/usr/bin/actions-runner (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/s390/self-hosted-builder/fs/usr/bin/entrypoint (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/s390/self-hosted-builder/qemu-user-static.service (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/x86/INDEX.md (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/x86/Makefile.in (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/x86/adler32_avx.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/x86/adler32_ssse3.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/x86/chunkset_avx.c (78%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/x86/chunkset_sse.c (79%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/x86/compare258_avx.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/x86/compare258_sse.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/x86/crc_folding.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/x86/crc_folding.h (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/x86/insert_string_sse.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/x86/slide_avx.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/x86/slide_sse.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/x86/x86.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/arch/x86/x86.h (100%) create mode 100644 internal-complibs/zlib-ng-2.0.7/chunkset.c rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/chunkset_tpl.h (98%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/cmake/detect-arch.c (97%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/cmake/detect-arch.cmake (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/cmake/detect-coverage.cmake (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/cmake/detect-install-dirs.cmake (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/cmake/detect-sanitizer.cmake (96%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/cmake/run-and-compare.cmake (72%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/cmake/run-and-redirect.cmake (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/cmake/test-compress.cmake (99%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/cmake/test-tools.cmake (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/cmake/toolchain-aarch64.cmake (67%) create mode 100644 internal-complibs/zlib-ng-2.0.7/cmake/toolchain-arm.cmake rename internal-complibs/{zlib-ng-2.0.6/cmake/toolchain-arm.cmake => zlib-ng-2.0.7/cmake/toolchain-armhf.cmake} (60%) create mode 100644 internal-complibs/zlib-ng-2.0.7/cmake/toolchain-mingw-i686.cmake create mode 100644 internal-complibs/zlib-ng-2.0.7/cmake/toolchain-mingw-x86_64.cmake rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/cmake/toolchain-powerpc.cmake (51%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/cmake/toolchain-powerpc64.cmake (50%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/cmake/toolchain-powerpc64le.cmake (50%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/cmake/toolchain-s390x.cmake (61%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/cmake/toolchain-sparc64.cmake (60%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/compare258.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/compress.c (95%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/configure (98%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/crc32.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/crc32_comb.c (100%) rename internal-complibs/{zlib-ng-2.0.6/crc32_comb_tbl.h => zlib-ng-2.0.7/crc32_comb_tbl._h} (100%) create mode 100644 internal-complibs/zlib-ng-2.0.7/crc32_comb_tbl.h rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/crc32_p.h (100%) rename internal-complibs/{zlib-ng-2.0.6/crc32_tbl.h => zlib-ng-2.0.7/crc32_tbl._h} (100%) create mode 100644 internal-complibs/zlib-ng-2.0.7/crc32_tbl.h rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/deflate.c (99%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/deflate.h (89%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/deflate_fast.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/deflate_medium.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/deflate_p.h (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/deflate_quick.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/deflate_slow.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/doc/algorithm.txt (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/doc/rfc1950.txt (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/doc/rfc1951.txt (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/doc/rfc1952.txt (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/doc/txtvsbin.txt (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/fallback_builtins.h (94%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/functable.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/functable.h (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/gzguts.h (93%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/gzlib.c (96%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/gzread.c (99%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/gzwrite.c (99%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/infback.c (98%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/inffast.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/inffast.h (100%) rename internal-complibs/{zlib-ng-2.0.6/inffixed_tbl.h => zlib-ng-2.0.7/inffixed_tbl._h} (100%) create mode 100644 internal-complibs/zlib-ng-2.0.7/inffixed_tbl.h rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/inflate.c (98%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/inflate.h (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/inflate_p.h (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/inftrees.c (99%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/inftrees.h (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/insert_string.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/insert_string_tpl.h (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/match_tpl.h (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/.gitignore (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/CVE-2002-0059/test.gz (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/CVE-2003-0107.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/CVE-2004-0797/test.gz (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/CVE-2005-1849/test.gz (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/CVE-2005-2096/test.gz (100%) create mode 100644 internal-complibs/zlib-ng-2.0.7/test/CVE-2018-25032/default.txt create mode 100644 internal-complibs/zlib-ng-2.0.7/test/CVE-2018-25032/fixed.txt rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/GH-361/test.txt (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/GH-364/test.bin (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/GH-382/defneg3.dat (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/GH-751/test.txt (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/GH-979/pigz-2.6.tar.gz (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/Makefile.in (92%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/README.md (90%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/abi/ignore (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/abi/zlib-v1.2.11-arm-linux-gnueabihf.abi (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/abi/zlib-v1.2.11-x86_64-linux-gnu.abi (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/abicheck.md (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/abicheck.sh (89%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/adler32_test.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/crc32_test.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/data/fireworks.jpg (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/data/lcet10.txt (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/data/paper-100k.pdf (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/deflate_quick_bi_valid.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/deflate_quick_block_open.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/example.c (99%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/fuzz/checksum_fuzzer.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/fuzz/compress_fuzzer.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/fuzz/example_dict_fuzzer.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/fuzz/example_flush_fuzzer.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/fuzz/example_large_fuzzer.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/fuzz/example_small_fuzzer.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/fuzz/minigzip_fuzzer.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/fuzz/standalone_fuzz_target_runner.c (100%) create mode 100644 internal-complibs/zlib-ng-2.0.7/test/gh1235.c rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/hash_head_0.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/infcover.c (99%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/inflate_adler32.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/minideflate.c (80%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/minigzip.c (95%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/pigz/CMakeLists.txt (95%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/pkgcheck.sh (99%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/switchlevels.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/test/testCVEinputs.sh (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/tools/codecov-upload.sh (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/tools/config.sub (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/tools/makecrct.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/tools/makefixed.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/tools/maketrees.c (99%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/trees.c (99%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/trees.h (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/trees_emit.h (100%) rename internal-complibs/{zlib-ng-2.0.6/trees_tbl.h => zlib-ng-2.0.7/trees_tbl._h} (100%) create mode 100644 internal-complibs/zlib-ng-2.0.7/trees_tbl.h rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/uncompr.c (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/win32/DLL_FAQ.txt (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/win32/Makefile.a64 (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/win32/Makefile.arm (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/win32/Makefile.msc (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/win32/README-WIN32.txt (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/win32/zlib-ng.def (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/win32/zlib-ng1.rc (91%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/win32/zlib.def (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/win32/zlib1.rc (91%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/win32/zlibcompat.def (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/zbuild.h (78%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/zconf-ng.h.in (99%) create mode 100644 internal-complibs/zlib-ng-2.0.7/zconf.h rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/zconf.h.in (96%) create mode 100644 internal-complibs/zlib-ng-2.0.7/zconf.h.included rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/zendian.h (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/zlib-ng.h (99%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/zlib-ng.map (99%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/zlib.h (99%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/zlib.map (98%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/zlib.pc.cmakein (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/zlib.pc.in (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/zutil.c (98%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/zutil.h (100%) rename internal-complibs/{zlib-ng-2.0.6 => zlib-ng-2.0.7}/zutil_p.h (85%) delete mode 100644 internal-complibs/zstd-1.5.2/common/threading.c delete mode 100644 internal-complibs/zstd-1.5.2/compress/zstd_compress_literals.c rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/.gitignore (100%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/BUCK (100%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/Makefile (99%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/README.md (94%) create mode 100644 internal-complibs/zstd-1.5.5/common/allocations.h create mode 100644 internal-complibs/zstd-1.5.5/common/bits.h rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/common/bitstream.h (89%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/common/compiler.h (91%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/common/cpu.h (98%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/common/debug.c (93%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/common/debug.h (98%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/common/entropy_common.c (90%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/common/error_private.c (76%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/common/error_private.h (98%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/common/fse.h (85%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/common/fse_decompress.c (78%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/common/huf.h (50%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/common/mem.h (83%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/common/pool.c (91%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/common/pool.h (93%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/common/portability_macros.h (84%) create mode 100644 internal-complibs/zstd-1.5.5/common/threading.c rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/common/threading.h (92%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/common/xxhash.c (85%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/common/xxhash.h (99%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/common/zstd_common.c (59%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/common/zstd_deps.h (97%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/common/zstd_internal.h (72%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/common/zstd_trace.h (96%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/compress/clevels.h (99%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/compress/fse_compress.c (81%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/compress/hist.c (99%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/compress/hist.h (98%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/compress/huf_compress.c (84%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/compress/zstd_compress.c (81%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/compress/zstd_compress_internal.h (81%) create mode 100644 internal-complibs/zstd-1.5.5/compress/zstd_compress_literals.c rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/compress/zstd_compress_literals.h (61%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/compress/zstd_compress_sequences.c (99%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/compress/zstd_compress_sequences.h (97%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/compress/zstd_compress_superblock.c (94%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/compress/zstd_compress_superblock.h (95%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/compress/zstd_cwksp.h (77%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/compress/zstd_double_fast.c (85%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/compress/zstd_double_fast.h (90%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/compress/zstd_fast.c (51%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/compress/zstd_fast.h (90%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/compress/zstd_lazy.c (78%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/compress/zstd_lazy.h (97%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/compress/zstd_ldm.c (98%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/compress/zstd_ldm.h (98%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/compress/zstd_ldm_geartab.h (99%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/compress/zstd_opt.c (91%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/compress/zstd_opt.h (97%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/compress/zstdmt_compress.c (98%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/compress/zstdmt_compress.h (98%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/decompress/huf_decompress.c (75%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/decompress/huf_decompress_amd64.S (88%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/decompress/zstd_ddict.c (96%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/decompress/zstd_ddict.h (95%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/decompress/zstd_decompress.c (89%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/decompress/zstd_decompress_block.c (89%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/decompress/zstd_decompress_block.h (88%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/decompress/zstd_decompress_internal.h (97%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/deprecated/zbuff.h (99%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/deprecated/zbuff_common.c (94%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/deprecated/zbuff_compress.c (99%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/deprecated/zbuff_decompress.c (89%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/dictBuilder/cover.c (97%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/dictBuilder/cover.h (98%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/dictBuilder/divsufsort.c (100%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/dictBuilder/divsufsort.h (100%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/dictBuilder/fastcover.c (99%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/dictBuilder/zdict.c (91%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/dll/example/Makefile (96%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/dll/example/README.md (98%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/dll/example/fullbench-dll.sln (100%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/dll/example/fullbench-dll.vcxproj (100%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/legacy/zstd_legacy.h (96%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/legacy/zstd_v01.c (96%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/legacy/zstd_v01.h (98%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/legacy/zstd_v02.c (97%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/legacy/zstd_v02.h (98%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/legacy/zstd_v03.c (96%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/legacy/zstd_v03.h (98%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/legacy/zstd_v04.c (97%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/legacy/zstd_v04.h (98%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/legacy/zstd_v05.c (97%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/legacy/zstd_v05.h (99%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/legacy/zstd_v06.c (97%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/legacy/zstd_v06.h (99%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/legacy/zstd_v07.c (97%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/legacy/zstd_v07.h (99%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/libzstd.mk (90%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/libzstd.pc.in (63%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/module.modulemap (56%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/zdict.h (89%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/zstd.h (82%) rename internal-complibs/{zstd-1.5.2 => zstd-1.5.5}/zstd_errors.h (74%) delete mode 100644 plugins/codecs/zfp/test_zfp_acc_int.c create mode 100644 plugins/filters/bytedelta/CMakeLists.txt create mode 100644 plugins/filters/bytedelta/bytedelta.c create mode 100644 plugins/filters/bytedelta/bytedelta.h create mode 100644 plugins/filters/bytedelta/test_bytedelta.c delete mode 100644 plugins/filters/ndmean/example_ndmean_1dim_2rows.caterva delete mode 100644 plugins/filters/ndmean/example_ndmean_1dim_same_cells.caterva delete mode 100644 plugins/filters/ndmean/example_ndmean_1dim_some_matches.caterva delete mode 100644 plugins/filters/ndmean/example_ndmean_repart_rand.caterva delete mode 100644 plugins/filters/ndmean/example_ndmean_repart_same_cells.caterva delete mode 100644 plugins/filters/ndmean/example_ndmean_repart_some_matches.caterva create mode 100644 plugins/test_data/example_day_month_temp.b2nd delete mode 100644 plugins/test_data/example_day_month_temp.caterva delete mode 100644 plugins/test_data/example_double_same_cells.caterva delete mode 100644 plugins/test_data/example_float_cyclic.caterva create mode 100644 plugins/test_data/example_item_prices.b2nd delete mode 100644 plugins/test_data/example_item_prices.caterva delete mode 100644 plugins/test_data/example_rand.caterva delete mode 100644 plugins/test_data/example_same_cells.caterva delete mode 100644 plugins/test_data/example_some_matches.caterva create mode 100644 plugins/tunes/CMakeLists.txt create mode 100644 plugins/tunes/tunes-registry.c create mode 100644 tests/b2nd/CMakeLists.txt create mode 100644 tests/b2nd/cutest.h create mode 100644 tests/b2nd/test_b2nd_append.c create mode 100644 tests/b2nd/test_b2nd_copy.c create mode 100644 tests/b2nd/test_b2nd_delete.c create mode 100644 tests/b2nd/test_b2nd_full.c create mode 100644 tests/b2nd/test_b2nd_get_slice.c create mode 100644 tests/b2nd/test_b2nd_get_slice_buffer.c create mode 100644 tests/b2nd/test_b2nd_insert.c create mode 100644 tests/b2nd/test_b2nd_metalayers.c create mode 100644 tests/b2nd/test_b2nd_open_offset.c create mode 100644 tests/b2nd/test_b2nd_persistency.c create mode 100644 tests/b2nd/test_b2nd_resize.c create mode 100644 tests/b2nd/test_b2nd_roundtrip.c create mode 100644 tests/b2nd/test_b2nd_save.c create mode 100644 tests/b2nd/test_b2nd_serialize.c create mode 100644 tests/b2nd/test_b2nd_set_slice_buffer.c create mode 100644 tests/b2nd/test_b2nd_squeeze.c create mode 100644 tests/b2nd/test_b2nd_squeeze_index.c create mode 100644 tests/b2nd/test_b2nd_uninit.c create mode 100644 tests/b2nd/test_b2nd_zeros.c create mode 100644 tests/b2nd/test_common.h diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index ea0d8009..59c538c4 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -81,11 +81,6 @@ jobs: compiler: clang cmake-args: -D DEACTIVATE_ZSTD=ON - - name: Windows MSVC Win32 - os: windows-latest - compiler: cl - cmake-args: -A Win32 - - name: Windows MSVC Win64 os: windows-latest compiler: cl @@ -118,9 +113,10 @@ jobs: sudo apt-get update sudo apt-get install -y ${{ matrix.packages }} - - name: Install packages (Windows) - if: runner.os == 'Windows' - run: choco install ninja ${{ matrix.packages }} + # Ninja should be not necessary anymore (see note on Win / GCC above) + # - name: Install packages (Windows) + # if: runner.os == 'Windows' + # run: choco install ninja ${{ matrix.packages }} - name: Install packages (macOS) if: runner.os == 'macOS' diff --git a/.gitignore b/.gitignore index 3a265615..698c1700 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ bench/bench build* .idea +.*.swp cmake-build-* +/blosc/config.h /doc/doxygen/xml/ /doc/xml diff --git a/ANNOUNCE.md b/ANNOUNCE.md index 847c4fda..f6adb7d1 100644 --- a/ANNOUNCE.md +++ b/ANNOUNCE.md @@ -1,16 +1,12 @@ -# Announcing C-Blosc2 2.6.1 +# Announcing C-Blosc2 2.9.2 A fast, compressed and persistent binary data store library for C. ## What is new? -The Blosc development team is happy to announce a new release of C-Blosc2. -This is a maintenance release with support for MacOS universal2 binaries -(arm64+x86_64 build). - -C-Blosc2 should be backward compatible with C-Blosc, so you can start using -it right away and increasingly start to use its new functionality, like the -new filters, prefilters, super-chunks and frames. -See docs in: https://www.blosc.org/c-blosc2/c-blosc2.html +This a maintenance release with improved support for dynamic plugins and +fixes for some corner cases when handling incompressible data. Also, +many other small fixes and improvements have been included. An upgrade to +this release is recommended. For more info, please see the release notes in: @@ -37,7 +33,7 @@ The github repository is over here: https://github.com/Blosc/c-blosc2 -Blosc is distributed using the BSD license, see LICENSES/BLOSC2.txt +Blosc is distributed using the BSD license, see LICENSE.txt for details. ## Mailing list @@ -45,7 +41,7 @@ for details. There is an official Blosc mailing list at: blosc@googlegroups.com -http://groups.google.es/group/blosc +https://groups.google.com/g/blosc ## Tweeter feed diff --git a/CMakeLists.txt b/CMakeLists.txt index 43910d1d..aa846f1e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,3 +1,11 @@ +# Blosc - Blocked Shuffling and Compression Library +# +# Copyright (c) 2021 The Blosc Development Team +# https://blosc.org +# License: BSD 3-Clause (see LICENSE.txt) +# +# See LICENSE.txt for details about copyright and rights to use. + # CMake build system for Blosc # ============================ # @@ -167,7 +175,7 @@ if(NOT DEACTIVATE_ZLIB) message(STATUS "Using ZLIB-NG internal sources for ZLIB support.") set(HAVE_ZLIB_NG TRUE) add_definitions(-DZLIB_COMPAT) - set(ZLIB_NG_DIR "zlib-ng-2.0.6") # update to the actual included version + set(ZLIB_NG_DIR "zlib-ng-2.0.7") # update to the actual included version set(ZLIB_COMPAT TRUE) set(SKIP_INSTALL_ALL TRUE) set(BUILD_SHARED_LIBS FALSE) diff --git a/LICENSE.txt b/LICENSE.txt index 076549b3..41cfb603 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -2,8 +2,8 @@ BSD License For Blosc - A blocking, shuffling and lossless compression library -Copyright (C) 2009-2018 Francesc Alted -Copyright (C) 2019- The Blosc Development Team +Copyright (c) 2009-2018 Francesc Alted +Copyright (c) 2019-present The Blosc Development Team Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/LICENSES/FASTLZ.txt b/LICENSES/FASTLZ.txt index 4a6abd6a..b22705f9 100644 --- a/LICENSES/FASTLZ.txt +++ b/LICENSES/FASTLZ.txt @@ -1,8 +1,5 @@ -FastLZ - lightning-fast lossless compression library - -Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) -Copyright (C) 2006 Ariya Hidayat (ariya@kde.org) -Copyright (C) 2005 Ariya Hidayat (ariya@kde.org) +FastLZ - Byte-aligned LZ77 compression library +Copyright (C) 2005-2020 Ariya Hidayat Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -21,4 +18,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/LICENSES/LZ4.txt b/LICENSES/LZ4.txt index 2383e103..48849169 100644 --- a/LICENSES/LZ4.txt +++ b/LICENSES/LZ4.txt @@ -1,32 +1,24 @@ -LZ4 - Fast LZ compression algorithm +LZ4 Library +Copyright (c) 2011-2020, Yann Collet +All rights reserved. -Copyright (C) 2011-2014, Yann Collet. -BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -You can contact the author at : -- LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html -- LZ4 source repository : http://code.google.com/p/lz4/ +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/LICENSES/SNAPPY.txt b/LICENSES/SNAPPY.txt deleted file mode 100644 index 8d6bd9fe..00000000 --- a/LICENSES/SNAPPY.txt +++ /dev/null @@ -1,28 +0,0 @@ -Copyright 2011, Google Inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/LICENSES/STDINT.txt b/LICENSES/STDINT.txt deleted file mode 100644 index c28001d1..00000000 --- a/LICENSES/STDINT.txt +++ /dev/null @@ -1,29 +0,0 @@ -ISO C9x compliant stdint.h for Microsoft Visual Studio -Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 - - Copyright (c) 2006-2013 Alexander Chemeris - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - 3. Neither the name of the product nor the names of its contributors may - be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED -WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/LICENSES/ZLIB.txt b/LICENSES/ZLIB.txt index 5d74f5ce..adb48d47 100644 --- a/LICENSES/ZLIB.txt +++ b/LICENSES/ZLIB.txt @@ -1,22 +1,19 @@ -Copyright notice: +(C) 1995-2013 Jean-loup Gailly and Mark Adler - (C) 1995-2013 Jean-loup Gailly and Mark Adler +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. - Jean-loup Gailly Mark Adler - jloup@gzip.org madler@alumni.caltech.edu +3. This notice may not be removed or altered from any source distribution. diff --git a/LICENSES/BLOSC.txt b/LICENSES/ZSTD.txt similarity index 78% rename from LICENSES/BLOSC.txt rename to LICENSES/ZSTD.txt index 22a66d10..a793a802 100644 --- a/LICENSES/BLOSC.txt +++ b/LICENSES/ZSTD.txt @@ -1,9 +1,8 @@ BSD License -For Blosc - A blocking, shuffling and lossless compression library +For Zstandard software -Copyright (C) 2009-2018 Francesc Alted -Copyright (C) 2019-present Blosc Development team +Copyright (c) 2016-present, Facebook, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -15,8 +14,8 @@ are permitted provided that the following conditions are met: this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name Francesc Alted nor the names of its contributors may be used - to endorse or promote products derived from this software without specific + * Neither the name Facebook nor the names of its contributors may be used to + endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND diff --git a/README.rst b/README.rst index 44ab0faa..27720a0e 100644 --- a/README.rst +++ b/README.rst @@ -8,7 +8,7 @@ A fast, compressed and persistent data store library for C :Author: The Blosc Development Team :Contact: blosc@blosc.org -:URL: http://www.blosc.org +:URL: https://www.blosc.org :Gitter: |gitter| :Actions: |actions| :NumFOCUS: |numfocus| @@ -21,9 +21,6 @@ A fast, compressed and persistent data store library for C .. |actions| image:: https://github.com/Blosc/c-blosc2/workflows/CI%20CMake/badge.svg :target: https://github.com/Blosc/c-blosc2/actions?query=workflow%3A%22CI+CMake%22 -.. |appveyor| image:: https://ci.appveyor.com/api/projects/status/qiaxywqrouj6nkug/branch/master?svg=true - :target: https://ci.appveyor.com/project/FrancescAlted/c-blosc2/branch/master - .. |numfocus| image:: https://img.shields.io/badge/powered%20by-NumFOCUS-orange.svg?style=flat&colorA=E1523D&colorB=007D8A :target: https://numfocus.org @@ -34,11 +31,34 @@ A fast, compressed and persistent data store library for C What is it? =========== -`Blosc `_ is a high performance compressor optimized for binary data (i.e. floating point numbers, integers and booleans). It has been designed to transmit data to the processor cache faster than the traditional, non-compressed, direct memory fetch approach via a memcpy() OS call. Blosc main goal is not just to reduce the size of large datasets on-disk or in-memory, but also to accelerate memory-bound computations. +`Blosc `_ is a high performance compressor optimized for binary data (i.e. floating point numbers, integers and booleans, although it can handle string data too). It has been designed to transmit data to the processor cache faster than the traditional, non-compressed, direct memory fetch approach via a memcpy() OS call. Blosc main goal is not just to reduce the size of large datasets on-disk or in-memory, but also to accelerate memory-bound computations. + +C-Blosc2 is the new major version of `C-Blosc `_, and is backward compatible with both the C-Blosc1 API and its in-memory format. However, the reverse thing is generally not true for the format; buffers generated with C-Blosc2 are not format-compatible with C-Blosc1 (i.e. forward compatibility is not supported). In case you want to ensure full API compatibility with C-Blosc1 API, define the `BLOSC1_COMPAT` symbol. + +See a 3 minutes `introductory video to Blosc2 `_. + +Blosc2 NDim: an N-Dimensional store +=================================== + +One of the latest and more exciting additions in C-Blosc2 is the `Blosc2 NDim layer `_ (or b2nd for short), allowing to create *and* read n-dimensional datasets in an extremely efficient way thanks to a n-dim 2-level partitioning, that allows to slice and dice arbitrary large and compressed data in a more fine-grained way: + +.. image:: https://github.com/Blosc/c-blosc2/blob/main/images/b2nd-2level-parts.png?raw=true + :width: 75% + +To wet you appetite, here it is how the `NDArray` object in the `Python wrapper`_ performs on getting slices orthogonal to the different axis of a 4-dim dataset: + +.. image:: https://github.com/Blosc/c-blosc2/blob/main/images/Read-Partial-Slices-B2ND.png?raw=true + :width: 75% + +We have blogged about this: https://www.blosc.org/posts/blosc2-ndim-intro -C-Blosc2 is the new major version of `C-Blosc `_, and tries hard to be backward compatible with both the C-Blosc1 API and its in-memory format. However, the reverse thing is generally not true for the format; buffers generated with C-Blosc2 are not format-compatible with C-Blosc1 (i.e. forward compatibility is not supported). In case you want to ensure full API compatibility with C-Blosc1 API, define the `BLOSC1_COMPAT` symbol. +We also have a ~2 min explanatory video on `why slicing in a pineapple-style (aka double partition) +is useful `_: -See a 3 minutes `introductory video to Blosc2 `_. +.. image:: https://github.com/Blosc/blogsite/blob/master/files/images/slicing-pineapple-style.png?raw=true + :width: 50% + :alt: Slicing a dataset in pineapple-style + :target: https://www.youtube.com/watch?v=LvP9zxMGBng New features in C-Blosc2 @@ -46,8 +66,12 @@ New features in C-Blosc2 * **64-bit containers:** the first-class container in C-Blosc2 is the `super-chunk` or, for brevity, `schunk`, that is made by smaller chunks which are essentially C-Blosc1 32-bit containers. The super-chunk can be backed or not by another container which is called a `frame` (see later). +* **NDim containers (b2nd):** allow to store n-dimensional data that can efficiently read datasets in slices that can be n-dimensional too. To achieve this, a n-dimensional 2-level partitioning has been implemented. This capabilities were formerly part of `Caterva `_, and now it is included in C-Blosc2 for convenience. Caterva is now deprecated. + * **More filters:** besides `shuffle` and `bitshuffle` already present in C-Blosc1, C-Blosc2 already implements: + - `bytedelta`: calculates the difference between bytes in a block that has been shuffle already. We have `blogged about bytedelta `_. + - `delta`: the stored blocks inside a chunk are diff'ed with respect to first block in the chunk. The idea is that, in some situations, the diff will have more zeros than the original data, leading to better compression. - `trunc_prec`: it zeroes the least significant bits of the mantissa of float32 and float64 types. When combined with the `shuffle` or `bitshuffle` filter, this leads to more contiguous zeros, which are compressed better. @@ -72,7 +96,7 @@ New features in C-Blosc2 * **Parallel chunk reads:** when several blocks of a chunk are to be read, this is done in parallel by the decompressing machinery. That means that every thread is responsible to read, post-filter and decompress a block by itself, leading to an efficient overlap of I/O and CPU usage that optimizes reads to a maximum. -* **Meta-layers:** optionally, the user can add meta-data for different uses and in different layers. For example, one may think on providing a meta-layer for `NumPy `_ so that most of the meta-data for it is stored in a meta-layer; then, one can place another meta-layer on top of the latter for adding more high-level info if desired (e.g. geo-spatial, meteorological...). +* **Meta-layers:** optionally, the user can add meta-data for different uses and in different layers. For example, one may think on providing a meta-layer for `NumPy `_ so that most of the meta-data for it is stored in a meta-layer; then, one can place another meta-layer on top of the latter for adding more high-level info if desired (e.g. geo-spatial, meteorological...). * **Variable length meta-layers:** the user may want to add variable-length meta information that can be potentially very large (up to 2 GB). The regular meta-layer described above is very quick to read, but meant to store fixed-length and relatively small meta information. Variable length metalayers are stored in the trailer of a frame, whereas regular meta-layers are in the header. @@ -92,34 +116,13 @@ New features in C-Blosc2 More info about the `improved capabilities of C-Blosc2 can be found in this talk `_. -After a long period of testing, C-Blosc2 entered production stage in 2.0.0. The API and format have been frozen, and that means that there is guarantee that your programs will continue to work with future versions of the library, and that next releases will be able to read from persistent storage generated from previous releases (as of 2.0.0). - - -Meta-compression and other advantages over existing compressors -=============================================================== - -C-Blosc2 is not like other compressors: it should rather be called a meta-compressor. This is so because it can use different codecs (libraries that can reduce the size of inputs) and filters (libraries that generally improve compression ratio). At the same time, it can also be called a compressor because it makes an actual use of the several codecs and filters, so it can actually work like so. - -Currently C-Blosc2 comes with support of BloscLZ, a compressor heavily based on `FastLZ `_, `LZ4 and LZ4HC `_, `Zstd `_, and `Zlib, via zlib-ng: `_, as well as a highly optimized (it can use SSE2, AVX2, NEON or ALTIVEC instructions, if available) shuffle and bitshuffle filters (for info on how shuffling works, see slide 17 of http://www.slideshare.net/PyData/blosc-py-data-2014). - -Blosc is in charge of coordinating the codecs and filters so that they can leverage the blocking technique (described above) as -well as multi-threaded execution (if several cores are available) automatically. That makes that every codec and filter -will work at very high speeds, even if it was not initially designed for doing blocking or multi-threading. For example, -Blosc allows you to use the ``LZ4`` codec, but in a multi-threaded way. - -Last but not least, C-Blosc2 comes with an easy-to-use plugin mechanism for codecs and filters, so anyone can inject their own code in the compression pipeline of Blosc2 and reap its benefits (like multi-threading and integration with other filters) for free (see a `self-contained example `_). In addition, we have implemented a centralized plugin system too (see the `docs in the plugins directory `_). - - -Multidimensional containers -=========================== - -As said, C-Blosc2 adds a powerful mechanism for adding different metalayers on top of its containers. `Caterva `_ is a sibling library that adds such a metalayer specifying not only the dimensionality of a dataset, but also the dimensionality of the chunks inside the dataset. In addition, Caterva adds machinery for retrieving arbitrary multi-dimensional slices (aka hyper-slices) out of the multi-dimensional containers in the most efficient way. Hence, Caterva brings the convenience of multi-dimensional containers to your application very easily. For more info, check out the `Caterva documentation `_. +C-Blosc2 API and format have been frozen, and that means that there is guarantee that your programs will continue to work with future versions of the library, and that next releases will be able to read from persistent storage generated from previous releases (as of 2.0.0). Python wrapper ============== -We are officially supporting (thanks to the Python Software Foundation) a `Python wrapper for Blosc2 `_. Although this is still in early development, it already supports all the features of the venerable `python-blosc ` package. As a bonus, the `python-blosc2` package comes with wheels and binary versions of the C-Blosc2 libraries, so anyone, even non-Python users can install C-Blosc2 binaries easily with: +We are officially supporting (thanks to the Python Software Foundation) a `Python wrapper for Blosc2 `_. It supports all the features of the predecessor `python-blosc `_ package plus most of the bells and whistles of C-Blosc2, like 64-bit and multidimensional containers. As a bonus, the `python-blosc2` package comes with wheels and binary versions of the C-Blosc2 libraries, so anyone, even non-Python users can install C-Blosc2 binaries easily with: .. code-block:: console @@ -129,7 +132,7 @@ We are officially supporting (thanks to the Python Software Foundation) a `Pytho Compiling the C-Blosc2 library with CMake ========================================= -Blosc can be built, tested and installed using `CMake `_. The following procedure describes a typical CMake build. +Blosc can be built, tested and installed using `CMake `_. The following procedure describes a typical CMake build. Create the build directory inside the sources and move into it: @@ -170,59 +173,41 @@ Once you have compiled your Blosc library, you can easily link your apps with it Handling support for codecs (LZ4, LZ4HC, Zstd, Zlib) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -C-Blosc2 comes with full sources for LZ4, LZ4HC, Zstd, and Zlib and in general, you should not worry about not having (or CMake not finding) the libraries in your system because by default the included sources will be automatically compiled and included in the C-Blosc2 library. This means that you can be confident in having a complete support for all the codecs in all the Blosc deployments (unless you are explicitly excluding support for some of them). +C-Blosc2 comes with full sources for LZ4, LZ4HC, Zstd, and Zlib and in general, you should not worry about not having (or CMake not finding) the libraries in your system because by default the included sources will be automatically compiled and included in the C-Blosc2 library. This means that you can be confident in having a complete support for all these codecs in all the official Blosc deployments. Of course, if you consider this is too bloated, you can exclude support for some of them. -If you want to force Blosc to use external libraries instead of the included compression sources: +For example, let's suppose that you want to disable support for Zstd: .. code-block:: console - cmake -DPREFER_EXTERNAL_LZ4=ON .. + cmake -DDEACTIVATE_ZSTD=ON .. -You can also disable support for some compression libraries: +Or, you may want to use a codec in an external library already in the system: .. code-block:: console - cmake -DDEACTIVATE_ZSTD=ON .. + cmake -DPREFER_EXTERNAL_LZ4=ON .. Supported platforms ~~~~~~~~~~~~~~~~~~~ -C-Blosc2 is meant to support all platforms where a C99 compliant C compiler can be found. The ones that are mostly tested are Intel (Linux, Mac OSX and Windows), ARM (Linux, Mac), and PowerPC (Linux) but exotic ones as IBM Blue Gene Q embedded "A2" processor are reported to work too. More on ARM support in `README_ARM.rst`. +C-Blosc2 is meant to support all platforms where a C99 compliant C compiler can be found. The ones that are mostly tested are Intel (Linux, Mac OSX and Windows), ARM (Linux, Mac), and PowerPC (Linux). More on ARM support in `README_ARM.rst`. For Windows, you will need at least VS2015 or higher on x86 and x64 targets (i.e. ARM is not supported on Windows). -For Mac OSX, make sure that you have installed the command line developer tools. You can always install them with: +For Mac OSX, make sure that you have the command line developer tools available. You can always install them with: .. code-block:: console xcode-select --install -For Mac OSX on arm64 architecture, you need to compile like this: +For Mac OSX on arm64 architecture, you may want to compile it like this: .. code-block:: console CC="clang -arch arm64" cmake .. -Support for the LZ4 optimized version in Intel IPP -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -C-Blosc2 comes with support for a highly optimized version of the LZ4 codec present in Intel IPP. Here it is a way to easily install Intel IPP using Conda(https://docs.conda.io): - -.. code-block:: console - - conda install -c intel ipp-static - -With that, you can enable support for LZ4/IPP (it is disabled by default) with: - -.. code-block:: console - - cmake .. -DDEACTIVATE_IPP=OFF - -In some Intel CPUs LZ4/IPP could be faster than regular LZ4, although in many cases you may experience different compression ratios depending on which version you use. See #313 for some quick and dirty benchmarks. - - Display error messages ~~~~~~~~~~~~~~~~~~~~~~ diff --git a/README_ARM.rst b/README_ARM.rst index 098b2404..72dd5d2a 100644 --- a/README_ARM.rst +++ b/README_ARM.rst @@ -151,7 +151,7 @@ Another example for running a bench with the cross compiler: In these cases the NEON flags are: `-mfpu=neon -flax-vector-conversions` -This is explained in detail in: http://linux-sunxi.org/Toolchain +This is explained in detail in: https://linux-sunxi.org/Toolchain This way you can develop and debug applications for ARM on intel machines as if you were in ARM platforms. diff --git a/README_B2ND_METALAYER.rst b/README_B2ND_METALAYER.rst new file mode 100644 index 00000000..d8f7d47a --- /dev/null +++ b/README_B2ND_METALAYER.rst @@ -0,0 +1,67 @@ +b2nd metalayer +++++++++++++++ + +b2nd format is specified as a metalayer on top of a Blosc2 container for storing +multidimensional information. Specifically, this metalayer is named 'b2nd' +and follows this format:: + + |-0-|-1-|-2-|-3-|~~~~~~~~~~~~~~~~|---|~~~~~~~~~~~~~~~~|---|~~~~~~~~~~~~~~~~| + | 9X| v | nd| 9X| shape | 9X| chunkshape | 9X| blockshape | + |---|---|---|---|~~~~~~~~~~~~~~~~|---|~~~~~~~~~~~~~~~~|---|~~~~~~~~~~~~~~~~| + ^ ^ ^ ^ ^ ^ + | | | | | | + | | | | | +--[msgpack] fixarray with X=nd elements + | | | | +--[msgpack] fixarray with X=nd elements + | | | +--[msgpack] fixarray with X=nd elements + | | +--[msgpack] positive fixnum for the number of dimensions (up to 127) + | +--[msgpack] positive fixnum for the metalayer format version (up to 127) + +---[msgpack] fixarray with X=7 elements + +The `shape` section is meant to store the actual shape info:: + + |---|--8 bytes---|---|--8 bytes---|~~~~~|---|--8 bytes---| + | d3| first_dim | d3| second_dim | ... | d3| nth_dim | + |---|------------|---|------------|~~~~~|---|------------| + ^ ^ ^ + | | | + | | +--[msgpack] int64 + | +--[msgpack] int64 + +--[msgpack] int64 + + +Next, the `chunkshape` section is meant to store the actual chunk shape info:: + + |---|--4 bytes---|---|--4 bytes---|~~~~~|---|--4 bytes---| + | d2| first_dim | d2| second_dim | ... | d2| nth_dim | + |---|------------|---|------------|~~~~~|---|------------| + ^ ^ ^ + | | | + | | +--[msgpack] int32 + | +--[msgpack] int32 + +--[msgpack] int32 + + +Next, the `blockshape` section is meant to store the actual block shape info:: + + |---|--4 bytes---|---|--4 bytes---|~~~~~|---|--4 bytes---| + | d2| first_dim | d2| second_dim | ... | d2| nth_dim | + |---|------------|---|------------|~~~~~|---|------------| + ^ ^ ^ + | | | + | | +--[msgpack] int32 + | +--[msgpack] int32 + +--[msgpack] int32 + +Finally, the `dtype` section is meant to store the data type information:: + + |---|---|--4 bytes---|--------------| + | XX| db| dtype_len | dtype_string | + |---|---|------------|--------------| + ^ ^ + | | + | +--[msgpack] str32 + +--[msgpack] positive fixint (7-bit integer). dtype_format; 0 means NumPy format. + +The 0 value for dtype_format means that the dtype_string field follows the NumPy convention +(e.g. an `int32_t` dtype is represented as "`_ command line utilities. After installing the package we +can do e.g.:: + + $ msgpack2json -Bi plugins/test_data/example_day_month_temp.b2nd + ["b2frame\u0000",166,3947,"\u0012\u0000P\u0003",5472,3682,4,684,1368,1,1,false, + "ext:6:base64:AAAAAAABAAAAAAAAAAAAAA==",[17,{"b2nd":107}, + ["lgACktMAAAAAAAABkNMAAAAAAAAAA5LSAAAAbtIAAAADktIAAAA50gAAAAPbAAAABXVpbnQ4"]]] + +Here we see that we have a `b2nd` metalayer that starts at position 107; but as there is a msgpack `bin32` there, we +must add 5 bytes (4 bytes for an int32 and 1 byte for the msgpack `bin32` header), so the actual starting position is +112 (107 + 5). Also, although we don't know the length of the `b2nd` metalayer, it is typically less than 100 bytes, +so let's err on the safe side and dump the first 1000 bytes, just in case:: + + $ dd bs=1 skip=112 count=1000 < plugins/test_data/example_day_month_temp.b2nd | msgpack2json -B + + [0,2,[400,3],[110,3],[57,3],0,"|u1"] + +By having a look at the +`Blosc2 NDim metalayer format `_ +one may note that the number of dimensions is 2, `shape` is [400, 3], `chunkshape` is [110, 3], blockshape is +[57, 3], dtype format is 0 (NumPy) and dtype is "|u1", which is a NumPy shortcut for `np.uint8`. + Chunks ------ @@ -217,7 +245,7 @@ The chunks section is composed of one or more Blosc data chunks followed by an i +========+========+========+========+===========+ Each chunk is stored contiguously one after the other, and each follows the format described in the -`chunk format `_ document. +`chunk format `_ document. The `chunk idx` is a Blosc2 chunk containing the offsets (starting from the beginning of the header) to each chunk in this section. The data in the chunk is a list of offsets (they can be 32-bit, 64-bit @@ -292,7 +320,7 @@ a fingerprint.:: The *vlmetalayers* object which stores the variable-length user meta data can change in size during the lifetime of the frame. This is an important feature and the reason why the *vlmetalayers* are stored in the trailer and not in the header. -However, the *vlmetalayers* follows the same format than the metalayers stored in the header. +However, the *vlmetalayers* follows the same format as the ones stored in the header. :trailer_len: diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index ae1b6cbc..402e92de 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,16 +1,100 @@ -Release notes for C-Blosc2 2.6.1 -================================ +Release notes for C-Blosc2 +========================== -Changes from 2.6.1 to 2.6.2 +Changes from 2.9.1 to 2.9.2 =========================== -#XXX version-specific blurb XXX# +* Now is possible to register the same plugin (as long as they have the same + ID *and* name) without errors. This is useful for registering the same + plugin without worrying on whether it has been registered already. + +* Improved detection of dynamic plugin locations. Now they must implement + `plugin_module.print_libpath()` as the canonical way to find the path for + the dynamic library plugin. + +* The `blosc2_static` has gained the cmake POSITION_INDEPENDENT_CODE property. + This should allow to use the static library in more situations. + +* `BLOSC_STUNE` is defined in `blosc2.h` now. Fixes #481. Thanks to + @DimitriPapadopoulos. + +* Fixed an issue when having incompressible data in combination with lazy_chunks. + +* Fix linking with static -DBUILD_STATIC=0. Fixes #480. + +* Visual Studio 2010 (version 10.0) has been deprecated. Now, users will + need to use Visual Studio 2012 (version 11.0) or later. + +* Many small fixes and code improvements. Thanks to @DimitriPapadopoulos, + @bnavigator. + + +Changes from 2.9.0 to 2.9.1 +=========================== + +* Allow the use of BTUNE by detecting the ``BTUNE_BALANCE`` environment + variable. + + +Changes from 2.8.0 to 2.9.0 +=========================== + +* Dynamic plugins as Python wheels are supported now! + This new feature allows for creating plugins in C, distribute + them as wheels, and load them dynamically in runtime. + Small example at https://github.com/Blosc/blosc2_plugin_example + +* BloscLZ can achieve more speed in clevel 1 now. + +* Internal Zstd sources updated to latest 1.5.5 version. + +* Copyright notice updated. Thanks to @DimitriPapadopoulos. + + +Changes from 2.7.1 to 2.8.0 +=========================== + +* New bytedelta filter added. SIMD support for Intel and ARM platforms is there. + We have blogged about this: https://www.blosc.org/posts/bytedelta-enhance-compression-toolset.rst + Thanks to Aras Pranckevičius for inspiration and initial implementation. + +* Minor improvements in BloscLZ, leading to better compression ratios in general. + BLoscLZ version bumped to 2.5.2. + +* Updated internal zlib-ng to 2.0.7. + +* Used `const` qualifier where possible in b2nd. Thanks to @cf-natali. + + +Changes from 2.6.1 to 2.7.1 +=========================== + +* Caterva has been merged and carefully integrated in C-Blosc2 in the new b2nd interface. + For more info on the new interface, see https://www.blosc.org/c-blosc2/reference/b2nd.html. + Thanks to Marta Iborra, Oscar Guiñón, J. David Ibáñez and Francesc Alted. Also thanks to + Aleix Alcacer for his great work in the Caterva project. + + We have a blog about this: https://www.blosc.org/posts/blosc2-ndim-intro + +* Updated internal zstd sources to 1.5.4. Thanks to Dimitri Papadopoulos. + +* `blosc2_schunk_avoid_cframe_free` and `blosc2_schunk_append_file` are exported as public functions now. + Thanks to @bnavigator. + +* BloscLZ codec is now treated exactly the same as LZ4. Before BloscLZ was considered less capable of reaching + decent compression ratios, but this has changed quite a bit lately, so there is no point in treating both differently. + +* Fixed some leaks, mainly on the test suite. + +* Fixed quite a bit of compiler warnings. + Changes from 2.6.0 to 2.6.1 =========================== * Add support for macos universal2 binaries (arm64+x86_64 build). Thanks to Thomas Vincent. + Changes from 2.5.0 to 2.6.0 =========================== @@ -26,7 +110,7 @@ Changes from 2.5.0 to 2.6.0 Changes from 2.4.3 to 2.5.0 =========================== -* Fixed a nasty bug that prevented retriving data correctly with large super-chunks (> 2^31 elements). +* Fixed a nasty bug that prevented retrieving data correctly with large super-chunks (> 2^31 elements). * Fixed an issue in `blosc2_schunk_get_slice_buffer()` in the interpretation of the `stop` param. Now `stop` is not part of the selected slice (as advertised). @@ -519,7 +603,7 @@ Changes from 2.0.0a2 to 2.0.0a3 * Added support for new Zstd codec (https://github.com/Cyan4973/zstd). This is a new compressor by Yann Collet, the author of LZ4 and LZ4HC. For details on Zstd, see this nice intro: - http://fastcompression.blogspot.com.es/2015/01/zstd-stronger-compression-algorithm.html. + https://fastcompression.blogspot.com/2015/01/zstd-stronger-compression-algorithm.html. * The blosc2_append_chunk() has been removed. This is this because an existing chunk may not fulfill the sequence of filters in super diff --git a/ROADMAP.rst b/ROADMAP.rst index 2c3faa85..21deafea 100644 --- a/ROADMAP.rst +++ b/ROADMAP.rst @@ -40,7 +40,7 @@ Right now, the next features are already implemented (although they may require * **Parallel chunk reads:** when several blocks of a chunk are to be read, this is done in parallel by the decompressing machinery. That means that every thread is responsible to read, post-filter and decompress a block by itself, leading to an efficient overlap of I/O and CPU usage that optimizes reads to a maximum. -* **Meta-layers:** optionally, the user can add meta-data for different uses and in different layers. For example, one may think on providing a meta-layer for `NumPy `_ so that most of the meta-data for it is stored in a meta-layer; then, one can place another meta-layer on top of the latter for adding more high-level info if desired (e.g. geo-spatial, meteorological...). +* **Meta-layers:** optionally, the user can add meta-data for different uses and in different layers. For example, one may think on providing a meta-layer for `NumPy `_ so that most of the meta-data for it is stored in a meta-layer; then, one can place another meta-layer on top of the latter for adding more high-level info if desired (e.g. geo-spatial, meteorological...). * **Variable length meta-layers:** the user may want to add variable-length meta information that can be potentially very large (up to 2 GB). The regular meta-layer described above is very quick to read, but meant to store fixed-length and relatively small meta information. Variable length metalayers are stored in the trailer of a frame, whereas regular meta-layers are in the header. @@ -84,7 +84,7 @@ Actions to be done * **Lock support for super-chunks:** when different processes are accessing concurrently to super-chunks, make them to sync properly by using locks, either on-disk (frame-backed super-chunks), or in-memory. Such a lock support would be configured in build time, so it could be disabled with a cmake flag. -* **Hierarchical structure (aka Groups):** some libraries (like `xarray `_) need an easy way to tie different datasets together (groups). This would also allow to create whole hierarchies so as to endow a structure to these datasets. Besides the structural part (that will be part of the format specification), this will need an accompanying API that allows the user to create groups, add datasets to groups, (recursively) list datasets in groups, access a dataset inside a group, an so on. +* **Hierarchical structure (aka Groups):** some libraries (like `xarray `_) need an easy way to tie different datasets together (groups). This would also allow to create whole hierarchies so as to endow a structure to these datasets. Besides the structural part (that will be part of the format specification), this will need an accompanying API that allows the user to create groups, add datasets to groups, (recursively) list datasets in groups, access a dataset inside a group, an so on. Outreaching diff --git a/bench/CMakeLists.txt b/bench/CMakeLists.txt index c8c6ba6e..6917704e 100644 --- a/bench/CMakeLists.txt +++ b/bench/CMakeLists.txt @@ -1,3 +1,11 @@ +# Blosc - Blocked Shuffling and Compression Library +# +# Copyright (c) 2021 The Blosc Development Team +# https://blosc.org +# License: BSD 3-Clause (see LICENSE.txt) +# +# See LICENSE.txt for details about copyright and rights to use. + # sources for main bench set(SOURCES b2bench.c) # other benchmarks @@ -8,6 +16,8 @@ set(SOURCES_ZERO_RUNLEN zero_runlen.c) set(SOURCES_CFRAME create_frame.c) set(SOURCES_SFRAME sframe_bench.c) +add_subdirectory(b2nd) + # targets add_executable(b2bench ${SOURCES}) add_executable(delta_schunk ${SOURCES_DELTA}) diff --git a/bench/b2bench.c b/bench/b2bench.c index d9de9e06..43dc402e 100644 --- a/bench/b2bench.c +++ b/bench/b2bench.c @@ -1,5 +1,5 @@ /********************************************************************* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/bench/b2nd/CMakeLists.txt b/bench/b2nd/CMakeLists.txt new file mode 100644 index 00000000..65c45e31 --- /dev/null +++ b/bench/b2nd/CMakeLists.txt @@ -0,0 +1,16 @@ +# Blosc - Blocked Shuffling and Compression Library +# +# Copyright (c) 2021 The Blosc Development Team +# https://blosc.org +# License: BSD 3-Clause (see LICENSE.txt) +# +# See LICENSE.txt for details about copyright and rights to use. + +file(GLOB SOURCES bench_*.c) + +foreach (source ${SOURCES}) + get_filename_component(target_name ${source} NAME_WE) + set(target b2nd_${target_name}) + add_executable(${target} ${target_name}.c) + target_link_libraries(${target} blosc_testing ${LIBS}) +endforeach (source) diff --git a/bench/b2nd/bench_get_slice.c b/bench/b2nd/bench_get_slice.c new file mode 100644 index 00000000..1b50bd48 --- /dev/null +++ b/bench/b2nd/bench_get_slice.c @@ -0,0 +1,86 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +#define DATA_TYPE int64_t + +# include + +int main() { + blosc_timestamp_t t0, t1; + + blosc2_init(); + + int nslices = 10; + + int8_t ndim = 3; + uint8_t itemsize = sizeof(DATA_TYPE); + + int64_t shape[] = {1250, 745, 400}; + + int32_t chunkshape[] = {50, 150, 100}; + int32_t blockshape[] = {13, 21, 30}; + + int64_t nbytes = itemsize; + for (int i = 0; i < ndim; ++i) { + nbytes *= shape[i]; + } + + DATA_TYPE *src = malloc(nbytes); + for (int i = 0; i < nbytes / itemsize; ++i) { + src[i] = i; + } + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.nthreads = 4; + cparams.typesize = itemsize; + blosc2_storage b2_storage = {.cparams=&cparams}; + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, ndim, shape, chunkshape, blockshape, NULL, 0, + NULL, 0); + + b2nd_array_t *arr; + blosc_set_timestamp(&t0); + BLOSC_ERROR(b2nd_from_cbuffer(ctx, &arr, src, nbytes)); + blosc_set_timestamp(&t1); + printf("from_buffer: %.4f s\n", blosc_elapsed_secs(t0, t1)); + + blosc_set_timestamp(&t0); + + for (int dim = 0; dim < ndim; ++dim) { + int64_t slice_start[B2ND_MAX_DIM], slice_stop[B2ND_MAX_DIM], slice_shape[B2ND_MAX_DIM]; + int64_t buffersize = itemsize; + for (int j = 0; j < ndim; ++j) { + slice_start[j] = 0; + slice_stop[j] = j == dim ? 1 : shape[j]; + slice_shape[j] = slice_stop[j] - slice_start[j]; + buffersize *= slice_shape[j]; + } + + DATA_TYPE *buffer = malloc(buffersize); + + for (int slice = 0; slice < nslices; ++slice) { + slice_start[dim] = rand() % shape[dim]; + slice_stop[dim] = slice_start[dim] + 1; + BLOSC_ERROR(b2nd_get_slice_cbuffer(arr, slice_start, slice_stop, buffer, slice_shape, buffersize)); + } + free(buffer); + } + + blosc_set_timestamp(&t1); + printf("get_slice: %.4f s\n", blosc_elapsed_secs(t0, t1)); + + free(src); + + BLOSC_ERROR(b2nd_free(arr)); + BLOSC_ERROR(b2nd_free_ctx(ctx)); + + blosc2_destroy(); + + return 0; +} diff --git a/bench/b2nd/bench_zfp_getitem.c b/bench/b2nd/bench_zfp_getitem.c new file mode 100644 index 00000000..6e91cb41 --- /dev/null +++ b/bench/b2nd/bench_zfp_getitem.c @@ -0,0 +1,255 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +/* + * Benchmark to measure the retrieval time of a number of elements in random positions + * in b2nd arrays. To get the necessary arrays (air1.cat, precip1.cat, snow1.cat...) + * you can use the following script, changing the dataset by your preference (see + * https://docs.digitalearthafrica.org/fr/latest/sandbox/notebooks/Datasets/Climate_Data_ERA5_AWS.html): + +8<---snip---- "fetch_data.py" +#!/usr/bin/env python +import numpy as np +import s3fs +import xarray as xr +import blosc2 + +def open_zarr(year, month, datestart, dateend): + fs = s3fs.S3FileSystem(anon=True) + datestring = "era5-pds/zarr/{year}/{month:02d}/data/".format(year=year, month=month) + s3map = s3fs.S3Map(datestring + "precipitation_amount_1hour_Accumulation.zarr/", s3=fs) + precip_zarr = xr.open_dataset(s3map, engine="zarr") + precip_zarr = precip_zarr.sel(time1=slice(np.datetime64(datestart), np.datetime64(dateend))) + return precip_zarr.precipitation_amount_1hour_Accumulation + +print("Fetching data from S3 (era5-pds)...") +precip_m0 = open_zarr(1987, 10, "1987-10-01", "1987-10-30 23:59") +precip0 = blosc2.empty(shape=precip_m0.shape, dtype=precip_m0.dtype, urlpath="precip1.b2nd") +print("Fetching and storing 1st month...") +values = precip_m0.values +precip0[:] = values +8<---snip---- + + * To call this script, you can run the following commands: + * $ pip install blosc2 + * $ python fetch_data.py + * + */ + +#include +#include "../../include/blosc2/codecs-registry.h" +#include "../../plugins/codecs/zfp/blosc2-zfp.h" +#include +#include + +int comp(const char *urlpath) { + blosc2_init(); + + blosc2_schunk *schunk = blosc2_schunk_open(urlpath); + + if (schunk->typesize != 4) { + printf("Error: This test is only for floats.\n"); + return -1; + } + + blosc2_remove_urlpath("schunk_rate.cat"); + blosc2_remove_urlpath("schunk.cat"); + + // Get multidimensional parameters and configure Blosc2 NDim array + int8_t ndim; + int64_t shape[4]; + int64_t shape_aux[4]; + int32_t chunkshape[4]; + int32_t blockshape[4]; + uint8_t *smeta; + int32_t smeta_len; + if (blosc2_meta_get(schunk, "b2nd", &smeta, &smeta_len) < 0) { + printf("This benchmark only supports b2nd arrays"); + return -1; + } + char *dtype; + int8_t dtype_format; + b2nd_deserialize_meta(smeta, smeta_len, &ndim, shape_aux, chunkshape, blockshape, &dtype, &dtype_format); + free(smeta); + free(dtype); + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.nthreads = 6; + + blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; + blosc2_storage b2_storage = {.cparams=&cparams, .dparams=&dparams}; + b2_storage.urlpath = "schunk_rate.cat"; + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, ndim, shape_aux, chunkshape, blockshape, NULL, 0, + NULL, 0); + + b2nd_array_t *arr; + b2nd_from_schunk(schunk, &arr); + int copied; + printf("LZ4 comp ratio: %f \n", (float) arr->sc->nbytes / (float) arr->sc->cbytes); + + /* Use BLOSC_CODEC_ZFP_FIXED_RATE */ + b2nd_array_t *arr_rate; + blosc2_context *ctx_zfp = blosc2_create_cctx(cparams); + ctx_zfp->compcode = BLOSC_CODEC_ZFP_FIXED_RATE; + ctx_zfp->splitmode = BLOSC_NEVER_SPLIT; + ctx_zfp->compcode_meta = (uint8_t) (100.0 * (float) arr->sc->cbytes / (float) arr->sc->nbytes); + ctx_zfp->filters[BLOSC2_MAX_FILTERS - 1] = 0; + ctx_zfp->filters_meta[BLOSC2_MAX_FILTERS - 1] = 0; + copied = b2nd_copy(ctx, arr, &arr_rate); + if (copied != 0) { + printf("Error BLOSC_CODEC_ZFP_FIXED_RATE \n"); + b2nd_free(arr); + return -1; + } + printf("ZFP_FIXED_RATE comp ratio: %f \n", (float) arr_rate->sc->nbytes / (float) arr_rate->sc->cbytes); + + int64_t nelems = arr_rate->nitems; + int dsize_zfp, dsize_blosc; + int64_t index; + float item_zfp, item_blosc; + blosc_timestamp_t t0, t1; + double zfp_time, blosc_time; + zfp_time = blosc_time = 0; + int64_t index_ndim[ZFP_MAX_DIM]; + int64_t index_chunk_ndim[ZFP_MAX_DIM]; + int64_t ind_ndim[ZFP_MAX_DIM]; + int32_t stride_chunk, ind_chunk; + int64_t nchunk; + bool needs_free_blosc, needs_free_zfp; + uint8_t *chunk_blosc, *chunk_zfp; + int32_t chunk_nbytes_zfp, chunk_cbytes_zfp, chunk_nbytes_lossy, chunk_cbytes_lossy; + double ntests = 500.0; + for (int i = 0; i < ntests; ++i) { + srand(i); + index = rand() % nelems; + blosc2_unidim_to_multidim(ndim, shape, index, index_ndim); + for (int j = 0; j < ndim; ++j) { + index_chunk_ndim[j] = index_ndim[j] / chunkshape[j]; + ind_ndim[j] = index_ndim[j] % chunkshape[j]; + } + stride_chunk = (int32_t)(shape[1] - 1) / chunkshape[1] + 1; + nchunk = index_chunk_ndim[0] * stride_chunk + index_chunk_ndim[1]; + ind_chunk = (int32_t) (ind_ndim[0] * chunkshape[1] + ind_ndim[1]); + blosc2_schunk_get_lazychunk(arr->sc, nchunk, &chunk_blosc, &needs_free_blosc); + blosc2_cbuffer_sizes(chunk_blosc, &chunk_nbytes_lossy, &chunk_cbytes_lossy, NULL); + blosc_set_timestamp(&t0); + dsize_blosc = blosc2_getitem_ctx(arr->sc->dctx, chunk_blosc, chunk_cbytes_lossy, + ind_chunk, 1, &item_blosc, sizeof(item_blosc)); + blosc_set_timestamp(&t1); + blosc_time += blosc_elapsed_secs(t0, t1); + blosc2_schunk_get_lazychunk(arr_rate->sc, nchunk, &chunk_zfp, &needs_free_zfp); + blosc2_cbuffer_sizes(chunk_zfp, &chunk_nbytes_zfp, &chunk_cbytes_zfp, NULL); + blosc_set_timestamp(&t0); + dsize_zfp = blosc2_getitem_ctx(arr_rate->sc->dctx, chunk_zfp, chunk_cbytes_zfp, + ind_chunk, 1, &item_zfp, sizeof(item_zfp)); + blosc_set_timestamp(&t1); + zfp_time += blosc_elapsed_secs(t0, t1); + if (dsize_blosc != dsize_zfp) { + printf("Different amount of items gotten"); + return -1; + } + } + printf("ZFP_FIXED_RATE time: %.5f microseconds\n", (zfp_time * 1000000.0 / ntests)); + printf("Blosc2 time: %.5f microseconds\n", (blosc_time * 1000000.0 / ntests)); + + b2nd_free(arr); + b2nd_free(arr_rate); + BLOSC_ERROR(b2nd_free_ctx(ctx)); + blosc2_free_ctx(ctx_zfp); + if (needs_free_blosc) { + free(chunk_blosc); + } + if (needs_free_zfp) { + free(chunk_zfp); + } + blosc2_destroy(); + + return BLOSC2_ERROR_SUCCESS; +} + +int solar1(void) { + const char *urlpath = "../../bench/solar1.cat"; + + int result = comp(urlpath); + return result; +} + +int air1(void) { + const char *urlpath = "../../bench/air1.cat"; + + int result = comp(urlpath); + return result; +} + +int snow1(void) { + const char *urlpath = "../../bench/snow1.cat"; + + int result = comp(urlpath); + return result; +} + +int wind1(void) { + const char *urlpath = "../../bench/wind1.cat"; + + int result = comp(urlpath); + return result; +} + +int precip1(void) { + const char *urlpath = "../../bench/precip1.cat"; + + int result = comp(urlpath); + return result; +} + +int precip2(void) { + const char *urlpath = "../../bench/precip2.cat"; + + int result = comp(urlpath); + return result; +} + +int precip3(void) { + const char *urlpath = "../../bench/precip3.cat"; + + int result = comp(urlpath); + return result; +} + +int precip3m(void) { + const char *urlpath = "../../bench/precip-3m.cat"; + + int result = comp(urlpath); + return result; +} + + +int main() { + + printf("wind1 \n"); + BLOSC_ERROR(wind1()); + printf("air1 \n"); + BLOSC_ERROR(air1()); + printf("solar1 \n"); + BLOSC_ERROR(solar1()); + printf("snow1 \n"); + BLOSC_ERROR(snow1()); + printf("precip1 \n"); + BLOSC_ERROR(precip1()); + printf("precip2 \n"); + BLOSC_ERROR(precip2()); + printf("precip3 \n"); + BLOSC_ERROR(precip3()); +// printf("precip3m \n"); + // BLOSC_ERROR(precip3m()); + return BLOSC2_ERROR_SUCCESS; + +} diff --git a/bench/create_frame.c b/bench/create_frame.c index 163191cd..424295af 100644 --- a/bench/create_frame.c +++ b/bench/create_frame.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -180,6 +180,8 @@ int create_cframe(const char* compname, bool contiguous) { int main(void) { + blosc2_init(); + #ifdef CREATE_ZEROS printf("\n *** Creating zeros ***\n"); #else @@ -197,4 +199,6 @@ int main(void) { create_cframe("blosclz", false); create_cframe("lz4", true); create_cframe("lz4", false); + + blosc2_destroy(); } diff --git a/bench/delta_schunk.c b/bench/delta_schunk.c index acf6589a..bb6d9c65 100644 --- a/bench/delta_schunk.c +++ b/bench/delta_schunk.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/bench/plot-speeds.py b/bench/plot-speeds.py index 341e9d31..5c5faef8 100644 --- a/bench/plot-speeds.py +++ b/bench/plot-speeds.py @@ -1,13 +1,11 @@ """ -Copyright (C) 2021 The Blosc developers +Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Script for plotting the results of the 'suite' benchmark. Invoke without parameters for usage hints. """ -from __future__ import print_function - import matplotlib as mpl from pylab import * diff --git a/bench/read-grid-150x150.py b/bench/read-grid-150x150.py index 42101d72..33ab9fe4 100644 --- a/bench/read-grid-150x150.py +++ b/bench/read-grid-150x150.py @@ -1,5 +1,5 @@ """ -Copyright (C) 2021 The Blosc developers +Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -9,7 +9,7 @@ After downloading it and uncompressing it, just run this script for extracting a small grid. -For more info on these datasets, see http://reanalysis.meteo.uni-bonn.de. +For more info on these datasets, see https://reanalysis.meteo.uni-bonn.de. """ from osgeo import gdal diff --git a/bench/sframe_bench.c b/bench/sframe_bench.c index 2c2d3323..c0f2dfd8 100644 --- a/bench/sframe_bench.c +++ b/bench/sframe_bench.c @@ -1,5 +1,5 @@ /********************************************************************* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -12,10 +12,22 @@ See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ +#include "blosc2.h" + +#if defined(_WIN32) +#include +#endif + +#if defined(_WIN32) && !defined(__MINGW32__) +#include +#else +#include +#endif + #include #include #include -#include +#include #include #include @@ -29,18 +41,6 @@ int nchunks = NCHUNKS; int iterations = 5; -#if defined(_WIN32) && !defined(__MINGW32__) -#include - #include "win32/stdint-windows.h" -#else -#include -#include -#endif - -#if defined(_WIN32) -#include - -#endif /* defined(_WIN32) && !defined(__MINGW32__) */ void test_update(blosc2_schunk* schunk_sframe, blosc2_schunk* schunk_cframe) { diff --git a/bench/sum_openmp.c b/bench/sum_openmp.c index 114dc1fd..2f43c500 100644 --- a/bench/sum_openmp.c +++ b/bench/sum_openmp.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -72,6 +72,8 @@ int main(void) { + blosc2_init(); + static DTYPE udata[N]; DTYPE chunk_buf[CHUNKSIZE]; int32_t isize = CHUNKSIZE * sizeof(DTYPE); @@ -103,7 +105,10 @@ int main(void) { FILE *f = fopen(filegrid, "rb"); size_t blocks_read = fread(cdata, info.st_size, 1, f); - assert(blocks_read == 1); + if (blocks_read != 1) { + printf("Error reading file!\n"); + exit(1); + } fclose(f); int dsize = blosc1_getitem(cdata, 0, CHUNKSIZE, chunk_buf); @@ -239,5 +244,7 @@ int main(void) { /* Free resources */ blosc2_schunk_free(schunk); + blosc2_destroy(); + return 0; } diff --git a/bench/trunc_prec_schunk.c b/bench/trunc_prec_schunk.c index ef189bb1..1c75a0a4 100644 --- a/bench/trunc_prec_schunk.c +++ b/bench/trunc_prec_schunk.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/bench/zero_runlen.c b/bench/zero_runlen.c index 7aea2542..8cd1f1b6 100644 --- a/bench/zero_runlen.c +++ b/bench/zero_runlen.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/blosc/CMakeLists.txt b/blosc/CMakeLists.txt index 441bab63..86f6668f 100644 --- a/blosc/CMakeLists.txt +++ b/blosc/CMakeLists.txt @@ -1,4 +1,12 @@ -# a simple way to detect that we are using CMAKE +# Blosc - Blocked Shuffling and Compression Library +# +# Copyright (c) 2021 The Blosc Development Team +# https://blosc.org +# License: BSD 3-Clause (see LICENSE.txt) +# +# See LICENSE.txt for details about copyright and rights to use. + +# A simple way to detect that we are using CMAKE add_definitions(-DUSING_CMAKE) set(INTERNAL_LIBS ${PROJECT_SOURCE_DIR}/internal-complibs) @@ -32,7 +40,7 @@ if(NOT DEACTIVATE_ZSTD) if(ZSTD_FOUND) set(BLOSC_INCLUDE_DIRS ${BLOSC_INCLUDE_DIRS} ${ZSTD_INCLUDE_DIR}) else() - set(ZSTD_LOCAL_DIR ${INTERNAL_LIBS}/zstd-1.5.2) + set(ZSTD_LOCAL_DIR ${INTERNAL_LIBS}/zstd-1.5.5) set(BLOSC_INCLUDE_DIRS ${BLOSC_INCLUDE_DIRS} ${ZSTD_LOCAL_DIR} ${ZSTD_LOCAL_DIR}/common) endif() @@ -43,7 +51,8 @@ include_directories(${BLOSC_INCLUDE_DIRS}) # library sources set(SOURCES ${SOURCES} blosc2.c blosclz.c fastcopy.c fastcopy.h schunk.c frame.c stune.c stune.h context.h delta.c delta.h shuffle-generic.c bitshuffle-generic.c trunc-prec.c trunc-prec.h - timestamp.c sframe.c directories.c blosc2-stdio.c) + timestamp.c sframe.c directories.c blosc2-stdio.c + b2nd.c b2nd_utils.c) if(NOT CMAKE_SYSTEM_PROCESSOR STREQUAL arm64) if(COMPILER_SUPPORT_SSE2) message(STATUS "Adding run-time support for SSE2") @@ -172,11 +181,21 @@ if(COMPILER_SUPPORT_SSE2) set_source_files_properties( shuffle-sse2.c bitshuffle-sse2.c blosclz.c fastcopy.c PROPERTIES COMPILE_FLAGS "/arch:SSE2") + set_property( + SOURCE shuffle.c + APPEND PROPERTY COMPILE_OPTIONS "/arch:SSE2") endif() else() set_source_files_properties( shuffle-sse2.c bitshuffle-sse2.c blosclz.c fastcopy.c PROPERTIES COMPILE_FLAGS -msse2) + set_property( + SOURCE shuffle.c + APPEND PROPERTY COMPILE_OPTIONS -msse2) + # Add SIMD flags for the bytedelta filter and Intel (it seems that ARM64 does not need these) + set_source_files_properties( + ${PROJECT_SOURCE_DIR}/plugins/filters/bytedelta/bytedelta.c + PROPERTIES COMPILE_OPTIONS "-mssse3") endif() # Define a symbol for the shuffle-dispatch implementation @@ -185,16 +204,23 @@ if(COMPILER_SUPPORT_SSE2) set_property( SOURCE shuffle.c APPEND PROPERTY COMPILE_DEFINITIONS SHUFFLE_SSE2_ENABLED) + endif() if(COMPILER_SUPPORT_AVX2) if(MSVC) set_source_files_properties( shuffle-avx2.c bitshuffle-avx2.c PROPERTIES COMPILE_FLAGS "/arch:AVX2") + set_property( + SOURCE shuffle.c + APPEND PROPERTY COMPILE_OPTIONS "/arch:AVX2") else() set_source_files_properties( shuffle-avx2.c bitshuffle-avx2.c PROPERTIES COMPILE_FLAGS -mavx2) + set_property( + SOURCE shuffle.c + APPEND PROPERTY COMPILE_OPTIONS -mavx2) endif() # Define a symbol for the shuffle-dispatch implementation @@ -208,11 +234,17 @@ if(COMPILER_SUPPORT_NEON) set_source_files_properties( shuffle-neon.c bitshuffle-neon.c PROPERTIES COMPILE_FLAGS "-flax-vector-conversions") + set_property( + SOURCE shuffle.c + APPEND PROPERTY COMPILE_OPTIONS "-flax-vector-conversions") if(CMAKE_SYSTEM_PROCESSOR STREQUAL armv7l) # Only armv7l needs special -mfpu=neon flag; aarch64 doesn't. set_source_files_properties( shuffle-neon.c bitshuffle-neon.c PROPERTIES COMPILE_FLAGS "-mfpu=neon -flax-vector-conversions") + set_property( + SOURCE shuffle.c + APPEND PROPERTY COMPILE_OPTIONS "-mfpu=neon -flax-vector-conversions") endif() # Define a symbol for the shuffle-dispatch implementation # so it knows NEON is supported even though that file is @@ -222,8 +254,12 @@ if(COMPILER_SUPPORT_NEON) APPEND PROPERTY COMPILE_DEFINITIONS SHUFFLE_NEON_ENABLED) endif() if(COMPILER_SUPPORT_ALTIVEC) - set_source_files_properties(shuffle-altivec.c bitshuffle-altivec.c + set_source_files_properties( + shuffle-altivec.c bitshuffle-altivec.c PROPERTIES COMPILE_FLAGS -DNO_WARN_X86_INTRINSICS) + set_property( + SOURCE shuffle.c + APPEND PROPERTY COMPILE_OPTIONS -DNO_WARN_X86_INTRINSICS) # Define a symbol for the shuffle-dispatch implementation # so it knows ALTIVEC is supported even though that file is @@ -263,6 +299,7 @@ endif() if(BUILD_STATIC) add_library(blosc2_static STATIC ${SOURCES}) set_target_properties(blosc2_static PROPERTIES OUTPUT_NAME blosc2) + set_target_properties(blosc2_static PROPERTIES POSITION_INDEPENDENT_CODE ON) if(MSVC OR MINGW) set_target_properties(blosc2_static PROPERTIES PREFIX lib) endif() @@ -273,6 +310,7 @@ endif() # install if(BLOSC_INSTALL) install(FILES ${PROJECT_SOURCE_DIR}/include/blosc2.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT DEV) + install(FILES ${PROJECT_SOURCE_DIR}/include/b2nd.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT DEV) install(FILES ${PROJECT_SOURCE_DIR}/include/blosc2/blosc2-export.h ${PROJECT_SOURCE_DIR}/include/blosc2/blosc2-common.h @@ -282,6 +320,8 @@ if(BLOSC_INSTALL) install(FILES ${PROJECT_SOURCE_DIR}/include/blosc2/filters-registry.h ${PROJECT_SOURCE_DIR}/include/blosc2/codecs-registry.h + ${PROJECT_SOURCE_DIR}/include/blosc2/plugins-utils.h + ${PROJECT_SOURCE_DIR}/include/blosc2/tuners-registry.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/blosc2 COMPONENT DEV) endif() diff --git a/blosc/b2nd.c b/blosc/b2nd.c new file mode 100644 index 00000000..f2cf77ad --- /dev/null +++ b/blosc/b2nd.c @@ -0,0 +1,1846 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +#include "b2nd.h" +#include "context.h" +#include "b2nd_utils.h" +#include "blosc2.h" +#include "blosc2/blosc2-common.h" + +#include + + +int b2nd_serialize_meta(int8_t ndim, const int64_t *shape, const int32_t *chunkshape, + const int32_t *blockshape, const char *dtype, int8_t dtype_format, + uint8_t **smeta) { + if (dtype == NULL) { + dtype = B2ND_DEFAULT_DTYPE; + } + // dtype checks + if (dtype_format < 0) { + BLOSC_TRACE_ERROR("dtype_format cannot be negative"); + BLOSC_ERROR(BLOSC2_ERROR_FAILURE); + } + size_t dtype_len0 = strlen(dtype); + if (dtype_len0 > INT32_MAX) { + BLOSC_TRACE_ERROR("dtype is too large (len > %d)", INT32_MAX); + BLOSC_ERROR(BLOSC2_ERROR_FAILURE); + } + const int32_t dtype_len = (int32_t) dtype_len0; + // Allocate space for b2nd metalayer + int32_t max_smeta_len = (int32_t) (1 + 1 + 1 + (1 + ndim * (1 + sizeof(int64_t))) + + (1 + ndim * (1 + sizeof(int32_t))) + (1 + ndim * (1 + sizeof(int32_t))) + + 1 + 1 + sizeof(int32_t) + dtype_len); + *smeta = malloc((size_t) max_smeta_len); + BLOSC_ERROR_NULL(*smeta, BLOSC2_ERROR_MEMORY_ALLOC); + uint8_t *pmeta = *smeta; + + // Build an array with 7 entries (version, ndim, shape, chunkshape, blockshape, dtype_format, dtype) + *pmeta++ = 0x90 + 7; + + // version entry + *pmeta++ = B2ND_METALAYER_VERSION; // positive fixnum (7-bit positive integer) + + // ndim entry + *pmeta++ = (uint8_t) ndim; // positive fixnum (7-bit positive integer) + + // shape entry + *pmeta++ = (uint8_t) (0x90) + ndim; // fix array with ndim elements + for (uint8_t i = 0; i < ndim; i++) { + *pmeta++ = 0xd3; // int64 + swap_store(pmeta, shape + i, sizeof(int64_t)); + pmeta += sizeof(int64_t); + } + + // chunkshape entry + *pmeta++ = (uint8_t) (0x90) + ndim; // fix array with ndim elements + for (uint8_t i = 0; i < ndim; i++) { + *pmeta++ = 0xd2; // int32 + swap_store(pmeta, chunkshape + i, sizeof(int32_t)); + pmeta += sizeof(int32_t); + } + + // blockshape entry + *pmeta++ = (uint8_t) (0x90) + ndim; // fix array with ndim elements + for (uint8_t i = 0; i < ndim; i++) { + *pmeta++ = 0xd2; // int32 + swap_store(pmeta, blockshape + i, sizeof(int32_t)); + pmeta += sizeof(int32_t); + } + + // dtype entry + *pmeta++ = dtype_format; // positive fixint (7-bit positive integer) + *pmeta++ = (uint8_t) (0xdb); // str with up to 2^31 elements + swap_store(pmeta, &dtype_len, sizeof(int32_t)); + pmeta += sizeof(int32_t); + memcpy(pmeta, dtype, dtype_len); + pmeta += dtype_len; + + int32_t slen = (int32_t) (pmeta - *smeta); + if (max_smeta_len != slen) { + BLOSC_TRACE_ERROR("meta length is inconsistent!"); + return BLOSC2_ERROR_FAILURE; + } + + return (int)slen; +} + + +int update_shape(b2nd_array_t *array, int8_t ndim, const int64_t *shape, + const int32_t *chunkshape, const int32_t *blockshape) { + array->ndim = ndim; + array->nitems = 1; + array->extnitems = 1; + array->extchunknitems = 1; + array->chunknitems = 1; + array->blocknitems = 1; + for (int i = 0; i < B2ND_MAX_DIM; ++i) { + if (i < ndim) { + array->shape[i] = shape[i]; + array->chunkshape[i] = chunkshape[i]; + array->blockshape[i] = blockshape[i]; + if (shape[i] != 0) { + if (shape[i] % array->chunkshape[i] == 0) { + array->extshape[i] = shape[i]; + } else { + array->extshape[i] = shape[i] + chunkshape[i] - shape[i] % chunkshape[i]; + } + if (chunkshape[i] % blockshape[i] == 0) { + array->extchunkshape[i] = chunkshape[i]; + } else { + array->extchunkshape[i] = + chunkshape[i] + blockshape[i] - chunkshape[i] % blockshape[i]; + } + } else { + array->extchunkshape[i] = 0; + array->extshape[i] = 0; + } + } else { + array->blockshape[i] = 1; + array->chunkshape[i] = 1; + array->extshape[i] = 1; + array->extchunkshape[i] = 1; + array->shape[i] = 1; + } + array->nitems *= array->shape[i]; + array->extnitems *= array->extshape[i]; + array->extchunknitems *= array->extchunkshape[i]; + array->chunknitems *= array->chunkshape[i]; + array->blocknitems *= array->blockshape[i]; + } + + // Compute strides + array->item_array_strides[ndim - 1] = 1; + array->item_extchunk_strides[ndim - 1] = 1; + array->item_chunk_strides[ndim - 1] = 1; + array->item_block_strides[ndim - 1] = 1; + array->block_chunk_strides[ndim - 1] = 1; + array->chunk_array_strides[ndim - 1] = 1; + for (int i = ndim - 2; i >= 0; --i) { + if (shape[i + 1] != 0) { + array->item_array_strides[i] = array->item_array_strides[i + 1] * array->shape[i + 1]; + array->item_extchunk_strides[i] = + array->item_extchunk_strides[i + 1] * array->extchunkshape[i + 1]; + array->item_chunk_strides[i] = + array->item_chunk_strides[i + 1] * array->chunkshape[i + 1]; + array->item_block_strides[i] = + array->item_block_strides[i + 1] * array->blockshape[i + 1]; + array->block_chunk_strides[i] = array->block_chunk_strides[i + 1] * + (array->extchunkshape[i + 1] / + array->blockshape[i + 1]); + array->chunk_array_strides[i] = array->chunk_array_strides[i + 1] * + (array->extshape[i + 1] * array->chunkshape[i + 1]); + } else { + array->item_array_strides[i] = 0; + array->item_extchunk_strides[i] = 0; + array->item_chunk_strides[i] = 0; + array->item_block_strides[i] = 0; + array->block_chunk_strides[i] = 0; + array->chunk_array_strides[i] = 0; + } + } + if (array->sc) { + uint8_t *smeta = NULL; + // Serialize the dimension info ... + int32_t smeta_len = + b2nd_serialize_meta(array->ndim, array->shape, array->chunkshape, array->blockshape, + array->dtype, array->dtype_format, &smeta); + if (smeta_len < 0) { + BLOSC_TRACE_ERROR("Error during serializing dims info for Blosc2 NDim"); + BLOSC_ERROR(BLOSC2_ERROR_FAILURE); + } + // ... and update it in its metalayer + if (blosc2_meta_exists(array->sc, "b2nd") < 0) { + if (blosc2_meta_add(array->sc, "b2nd", smeta, smeta_len) < 0) { + BLOSC_ERROR(BLOSC2_ERROR_FAILURE); + } + } else { + if (blosc2_meta_update(array->sc, "b2nd", smeta, smeta_len) < 0) { + BLOSC_ERROR(BLOSC2_ERROR_FAILURE); + } + } + free(smeta); + } + + return BLOSC2_ERROR_SUCCESS; +} + + +int array_without_schunk(b2nd_context_t *ctx, b2nd_array_t **array) { + /* Create a b2nd_array_t buffer */ + (*array) = (b2nd_array_t *) malloc(sizeof(b2nd_array_t)); + BLOSC_ERROR_NULL(*array, BLOSC2_ERROR_MEMORY_ALLOC); + + (*array)->sc = NULL; + + (*array)->ndim = ctx->ndim; + int64_t *shape = ctx->shape; + int32_t *chunkshape = ctx->chunkshape; + int32_t *blockshape = ctx->blockshape; + BLOSC_ERROR(update_shape(*array, ctx->ndim, shape, chunkshape, blockshape)); + + if (ctx->dtype != NULL) { + (*array)->dtype = malloc(strlen(ctx->dtype) + 1); + strcpy((*array)->dtype, ctx->dtype); + } else { + (*array)->dtype = NULL; + } + + (*array)->dtype_format = ctx->dtype_format; + + // The partition cache (empty initially) + (*array)->chunk_cache.data = NULL; + (*array)->chunk_cache.nchunk = -1; // means no valid cache yet + + return BLOSC2_ERROR_SUCCESS; +} + + +int array_new(b2nd_context_t *ctx, int special_value, b2nd_array_t **array) { + BLOSC_ERROR(array_without_schunk(ctx, array)); + + blosc2_schunk *sc = blosc2_schunk_new(ctx->b2_storage); + if (sc == NULL) { + BLOSC_TRACE_ERROR("Pointer is NULL"); + return BLOSC2_ERROR_FAILURE; + } + + // Serialize the dimension info + if (sc->nmetalayers >= BLOSC2_MAX_METALAYERS) { + BLOSC_TRACE_ERROR("the number of metalayers for this schunk has been exceeded"); + return BLOSC2_ERROR_FAILURE; + } + uint8_t *smeta = NULL; + int32_t smeta_len = b2nd_serialize_meta(ctx->ndim, + (*array)->shape, + (*array)->chunkshape, + (*array)->blockshape, + (*array)->dtype, + (*array)->dtype_format, + &smeta); + if (smeta_len < 0) { + BLOSC_TRACE_ERROR("error during serializing dims info for Blosc2 NDim"); + return BLOSC2_ERROR_FAILURE; + } + + // And store it in b2nd metalayer + if (blosc2_meta_add(sc, "b2nd", smeta, smeta_len) < 0) { + return BLOSC2_ERROR_FAILURE; + } + + free(smeta); + + for (int i = 0; i < ctx->nmetalayers; ++i) { + char *name = ctx->metalayers[i].name; + uint8_t *data = ctx->metalayers[i].content; + int32_t size = ctx->metalayers[i].content_len; + if (blosc2_meta_add(sc, name, data, size) < 0) { + BLOSC_ERROR(BLOSC2_ERROR_FAILURE); + } + } + + // Fill schunk with uninit values + if ((*array)->nitems != 0) { + int32_t chunksize = (int32_t) (*array)->extchunknitems * sc->typesize; + int64_t nchunks = (*array)->extnitems / (*array)->chunknitems; + int64_t nitems = nchunks * (*array)->extchunknitems; + // blosc2_schunk_fill_special(sc, nitems, BLOSC2_SPECIAL_ZERO, chunksize); + BLOSC_ERROR(blosc2_schunk_fill_special(sc, nitems, special_value, chunksize)); + } + (*array)->sc = sc; + + return BLOSC2_ERROR_SUCCESS; +} + + +int b2nd_uninit(b2nd_context_t *ctx, b2nd_array_t **array) { + BLOSC_ERROR_NULL(ctx, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + + BLOSC_ERROR(array_new(ctx, BLOSC2_SPECIAL_UNINIT, array)); + + return BLOSC2_ERROR_SUCCESS; +} + + +int b2nd_empty(b2nd_context_t *ctx, b2nd_array_t **array) { + BLOSC_ERROR_NULL(ctx, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + + // BLOSC_ERROR(array_new(ctx, BLOSC2_SPECIAL_UNINIT, array)); + // Avoid variable cratios + BLOSC_ERROR(array_new(ctx, BLOSC2_SPECIAL_ZERO, array)); + + return BLOSC2_ERROR_SUCCESS; +} + + +int b2nd_zeros(b2nd_context_t *ctx, b2nd_array_t **array) { + BLOSC_ERROR_NULL(ctx, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + + BLOSC_ERROR(array_new(ctx, BLOSC2_SPECIAL_ZERO, array)); + + return BLOSC2_ERROR_SUCCESS; +} + + +int b2nd_full(b2nd_context_t *ctx, b2nd_array_t **array, const void *fill_value) { + BLOSC_ERROR_NULL(ctx, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + + BLOSC_ERROR(b2nd_empty(ctx, array)); + + int32_t chunkbytes = (int32_t) (*array)->extchunknitems * (*array)->sc->typesize; + + blosc2_cparams *cparams; + if (blosc2_schunk_get_cparams((*array)->sc, &cparams) != 0) { + BLOSC_ERROR(BLOSC2_ERROR_FAILURE); + } + + int32_t chunksize = BLOSC_EXTENDED_HEADER_LENGTH + (*array)->sc->typesize; + uint8_t *chunk = malloc(chunksize); + BLOSC_ERROR_NULL(chunk, BLOSC2_ERROR_MEMORY_ALLOC); + if (blosc2_chunk_repeatval(*cparams, chunkbytes, chunk, chunksize, fill_value) < 0) { + BLOSC_ERROR(BLOSC2_ERROR_FAILURE); + } + free(cparams); + + for (int i = 0; i < (*array)->sc->nchunks; ++i) { + if (blosc2_schunk_update_chunk((*array)->sc, i, chunk, true) < 0) { + BLOSC_ERROR(BLOSC2_ERROR_FAILURE); + } + } + free(chunk); + + return BLOSC2_ERROR_SUCCESS; +} + + +int b2nd_from_schunk(blosc2_schunk *schunk, b2nd_array_t **array) { + BLOSC_ERROR_NULL(schunk, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + + if (schunk == NULL) { + BLOSC_TRACE_ERROR("Schunk is null"); + return BLOSC2_ERROR_NULL_POINTER; + } + + blosc2_cparams *cparams; + if (blosc2_schunk_get_cparams(schunk, &cparams) < 0) { + BLOSC_TRACE_ERROR("Blosc error"); + return BLOSC2_ERROR_NULL_POINTER; + } + free(cparams); + + b2nd_context_t params = {0}; + params.b2_storage = schunk->storage; + + // Deserialize the b2nd metalayer + uint8_t *smeta; + int32_t smeta_len; + if (blosc2_meta_get(schunk, "b2nd", &smeta, &smeta_len) < 0) { + // Try with a caterva metalayer; we are meant to be backward compatible with it + if (blosc2_meta_get(schunk, "caterva", &smeta, &smeta_len) < 0) { + BLOSC_ERROR(BLOSC2_ERROR_METALAYER_NOT_FOUND); + } + } + BLOSC_ERROR(b2nd_deserialize_meta(smeta, smeta_len, ¶ms.ndim, params.shape, + params.chunkshape, params.blockshape, ¶ms.dtype, + ¶ms.dtype_format)); + free(smeta); + + BLOSC_ERROR(array_without_schunk(¶ms, array)); + free(params.dtype); + + (*array)->sc = schunk; + + if ((*array) == NULL) { + BLOSC_TRACE_ERROR("Error creating a b2nd container from a frame"); + return BLOSC2_ERROR_NULL_POINTER; + } + + return BLOSC2_ERROR_SUCCESS; +} + + +int b2nd_to_cframe(const b2nd_array_t *array, uint8_t **cframe, int64_t *cframe_len, + bool *needs_free) { + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(cframe, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(cframe_len, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(needs_free, BLOSC2_ERROR_NULL_POINTER); + + *cframe_len = blosc2_schunk_to_buffer(array->sc, cframe, needs_free); + if (*cframe_len <= 0) { + BLOSC_TRACE_ERROR("Error serializing the b2nd array"); + return BLOSC2_ERROR_FAILURE; + } + return BLOSC2_ERROR_SUCCESS; +} + + +int b2nd_from_cframe(uint8_t *cframe, int64_t cframe_len, bool copy, b2nd_array_t **array) { + BLOSC_ERROR_NULL(cframe, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + + blosc2_schunk *sc = blosc2_schunk_from_buffer(cframe, cframe_len, copy); + if (sc == NULL) { + BLOSC_TRACE_ERROR("Blosc error"); + return BLOSC2_ERROR_FAILURE; + } + // ...and create a b2nd array out of it + BLOSC_ERROR(b2nd_from_schunk(sc, array)); + + return BLOSC2_ERROR_SUCCESS; +} + + +int b2nd_open(const char *urlpath, b2nd_array_t **array) { + BLOSC_ERROR_NULL(urlpath, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + + blosc2_schunk *sc = blosc2_schunk_open(urlpath); + + // ...and create a b2nd array out of it + BLOSC_ERROR(b2nd_from_schunk(sc, array)); + + return BLOSC2_ERROR_SUCCESS; +} + + +int b2nd_open_offset(const char *urlpath, b2nd_array_t **array, int64_t offset) { + BLOSC_ERROR_NULL(urlpath, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + + blosc2_schunk *sc = blosc2_schunk_open_offset(urlpath, offset); + + // ...and create a b2nd array out of it + BLOSC_ERROR(b2nd_from_schunk(sc, array)); + + return BLOSC2_ERROR_SUCCESS; +} + + +int b2nd_free(b2nd_array_t *array) { + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + + if (array) { + if (array->sc != NULL) { + blosc2_schunk_free(array->sc); + } + free(array->dtype); + free(array); + } + return BLOSC2_ERROR_SUCCESS; +} + + +int b2nd_from_cbuffer(b2nd_context_t *ctx, b2nd_array_t **array, const void *buffer, int64_t buffersize) { + BLOSC_ERROR_NULL(ctx, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(buffer, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + + BLOSC_ERROR(b2nd_empty(ctx, array)); + + if (buffersize < (int64_t) (*array)->nitems * (*array)->sc->typesize) { + BLOSC_TRACE_ERROR("The buffersize (%lld) is smaller than the array size (%lld)", + (long long) buffersize, (long long) (*array)->nitems * (*array)->sc->typesize); + BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); + } + + if ((*array)->nitems == 0) { + return BLOSC2_ERROR_SUCCESS; + } + + int64_t start[B2ND_MAX_DIM] = {0}; + int64_t *stop = (*array)->shape; + int64_t *shape = (*array)->shape; + BLOSC_ERROR(b2nd_set_slice_cbuffer(buffer, shape, buffersize, start, stop, *array)); + + return BLOSC2_ERROR_SUCCESS; +} + + +int b2nd_to_cbuffer(const b2nd_array_t *array, void *buffer, + int64_t buffersize) { + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(buffer, BLOSC2_ERROR_NULL_POINTER); + + if (buffersize < (int64_t) array->nitems * array->sc->typesize) { + BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); + } + + if (array->nitems == 0) { + return BLOSC2_ERROR_SUCCESS; + } + + int64_t start[B2ND_MAX_DIM] = {0}; + const int64_t *stop = array->shape; + BLOSC_ERROR(b2nd_get_slice_cbuffer(array, start, stop, buffer, array->shape, buffersize)); + return BLOSC2_ERROR_SUCCESS; +} + + +// Setting and getting slices +int get_set_slice(void *buffer, int64_t buffersize, const int64_t *start, const int64_t *stop, + const int64_t *shape, b2nd_array_t *array, bool set_slice) { + BLOSC_ERROR_NULL(buffer, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(start, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(stop, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + if (buffersize < 0) { + BLOSC_TRACE_ERROR("buffersize is < 0"); + BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); + } + + uint8_t *buffer_b = (uint8_t *) buffer; + const int64_t *buffer_start = start; + const int64_t *buffer_stop = stop; + const int64_t *buffer_shape = shape; + + int8_t ndim = array->ndim; + + // 0-dim case + if (ndim == 0) { + if (set_slice) { + int32_t chunk_size = array->sc->typesize + BLOSC2_MAX_OVERHEAD; + uint8_t *chunk = malloc(chunk_size); + BLOSC_ERROR_NULL(chunk, BLOSC2_ERROR_MEMORY_ALLOC); + if (blosc2_compress_ctx(array->sc->cctx, buffer_b, array->sc->typesize, chunk, chunk_size) < 0) { + BLOSC_ERROR(BLOSC2_ERROR_FAILURE); + } + if (blosc2_schunk_update_chunk(array->sc, 0, chunk, false) < 0) { + BLOSC_ERROR(BLOSC2_ERROR_FAILURE); + } + + } else { + if (blosc2_schunk_decompress_chunk(array->sc, 0, buffer_b, array->sc->typesize) < 0) { + BLOSC_ERROR(BLOSC2_ERROR_FAILURE); + } + } + return BLOSC2_ERROR_SUCCESS; + } + + int32_t data_nbytes = (int32_t) array->extchunknitems * array->sc->typesize; + uint8_t *data = malloc(data_nbytes); + BLOSC_ERROR_NULL(data, BLOSC2_ERROR_MEMORY_ALLOC); + + int64_t chunks_in_array[B2ND_MAX_DIM] = {0}; + for (int i = 0; i < ndim; ++i) { + chunks_in_array[i] = array->extshape[i] / array->chunkshape[i]; + } + + int64_t chunks_in_array_strides[B2ND_MAX_DIM]; + chunks_in_array_strides[ndim - 1] = 1; + for (int i = ndim - 2; i >= 0; --i) { + chunks_in_array_strides[i] = chunks_in_array_strides[i + 1] * chunks_in_array[i + 1]; + } + + int64_t blocks_in_chunk[B2ND_MAX_DIM] = {0}; + for (int i = 0; i < ndim; ++i) { + blocks_in_chunk[i] = array->extchunkshape[i] / array->blockshape[i]; + } + + // Compute the number of chunks to update + int64_t update_start[B2ND_MAX_DIM]; + int64_t update_shape[B2ND_MAX_DIM]; + + int64_t update_nchunks = 1; + for (int i = 0; i < ndim; ++i) { + int64_t pos = 0; + while (pos <= buffer_start[i]) { + pos += array->chunkshape[i]; + } + update_start[i] = pos / array->chunkshape[i] - 1; + while (pos < buffer_stop[i]) { + pos += array->chunkshape[i]; + } + update_shape[i] = pos / array->chunkshape[i] - update_start[i]; + update_nchunks *= update_shape[i]; + } + + for (int update_nchunk = 0; update_nchunk < update_nchunks; ++update_nchunk) { + int64_t nchunk_ndim[B2ND_MAX_DIM] = {0}; + blosc2_unidim_to_multidim(ndim, update_shape, update_nchunk, nchunk_ndim); + for (int i = 0; i < ndim; ++i) { + nchunk_ndim[i] += update_start[i]; + } + int64_t nchunk; + blosc2_multidim_to_unidim(nchunk_ndim, ndim, chunks_in_array_strides, &nchunk); + + // Check if the chunk needs to be updated + int64_t chunk_start[B2ND_MAX_DIM] = {0}; + int64_t chunk_stop[B2ND_MAX_DIM] = {0}; + for (int i = 0; i < ndim; ++i) { + chunk_start[i] = nchunk_ndim[i] * array->chunkshape[i]; + chunk_stop[i] = chunk_start[i] + array->chunkshape[i]; + if (chunk_stop[i] > array->shape[i]) { + chunk_stop[i] = array->shape[i]; + } + } + bool chunk_empty = false; + for (int i = 0; i < ndim; ++i) { + chunk_empty |= (chunk_stop[i] <= buffer_start[i] || chunk_start[i] >= buffer_stop[i]); + } + if (chunk_empty) { + continue; + } + + int32_t nblocks = (int32_t) array->extchunknitems / array->blocknitems; + + + if (set_slice) { + // Check if all the chunk is going to be updated and avoid the decompression + bool decompress_chunk = false; + for (int i = 0; i < ndim; ++i) { + decompress_chunk |= (chunk_start[i] < buffer_start[i] || chunk_stop[i] > buffer_stop[i]); + } + + if (decompress_chunk) { + int err = blosc2_schunk_decompress_chunk(array->sc, nchunk, data, data_nbytes); + if (err < 0) { + BLOSC_TRACE_ERROR("Error decompressing chunk"); + BLOSC_ERROR(BLOSC2_ERROR_FAILURE); + } + } else { + // Avoid writing non zero padding from previous chunk + memset(data, 0, data_nbytes); + } + } else { + bool *block_maskout = malloc(nblocks); + BLOSC_ERROR_NULL(block_maskout, BLOSC2_ERROR_MEMORY_ALLOC); + for (int nblock = 0; nblock < nblocks; ++nblock) { + int64_t nblock_ndim[B2ND_MAX_DIM] = {0}; + blosc2_unidim_to_multidim(ndim, blocks_in_chunk, nblock, nblock_ndim); + + // Check if the block needs to be updated + int64_t block_start[B2ND_MAX_DIM] = {0}; + int64_t block_stop[B2ND_MAX_DIM] = {0}; + for (int i = 0; i < ndim; ++i) { + block_start[i] = nblock_ndim[i] * array->blockshape[i]; + block_stop[i] = block_start[i] + array->blockshape[i]; + block_start[i] += chunk_start[i]; + block_stop[i] += chunk_start[i]; + + if (block_start[i] > chunk_stop[i]) { + block_start[i] = chunk_stop[i]; + } + if (block_stop[i] > chunk_stop[i]) { + block_stop[i] = chunk_stop[i]; + } + } + + bool block_empty = false; + for (int i = 0; i < ndim; ++i) { + block_empty |= (block_stop[i] <= start[i] || block_start[i] >= stop[i]); + } + block_maskout[nblock] = block_empty ? true : false; + } + + if (blosc2_set_maskout(array->sc->dctx, block_maskout, nblocks) != BLOSC2_ERROR_SUCCESS) { + BLOSC_TRACE_ERROR("Error setting the maskout"); + BLOSC_ERROR(BLOSC2_ERROR_FAILURE); + } + + int err = blosc2_schunk_decompress_chunk(array->sc, nchunk, data, data_nbytes); + if (err < 0) { + BLOSC_TRACE_ERROR("Error decompressing chunk"); + BLOSC_ERROR(BLOSC2_ERROR_FAILURE); + } + + free(block_maskout); + } + + // Iterate over blocks + + for (int nblock = 0; nblock < nblocks; ++nblock) { + int64_t nblock_ndim[B2ND_MAX_DIM] = {0}; + blosc2_unidim_to_multidim(ndim, blocks_in_chunk, nblock, nblock_ndim); + + // Check if the block needs to be updated + int64_t block_start[B2ND_MAX_DIM] = {0}; + int64_t block_stop[B2ND_MAX_DIM] = {0}; + for (int i = 0; i < ndim; ++i) { + block_start[i] = nblock_ndim[i] * array->blockshape[i]; + block_stop[i] = block_start[i] + array->blockshape[i]; + block_start[i] += chunk_start[i]; + block_stop[i] += chunk_start[i]; + + if (block_start[i] > chunk_stop[i]) { + block_start[i] = chunk_stop[i]; + } + if (block_stop[i] > chunk_stop[i]) { + block_stop[i] = chunk_stop[i]; + } + } + int64_t block_shape[B2ND_MAX_DIM] = {0}; + for (int i = 0; i < ndim; ++i) { + block_shape[i] = block_stop[i] - block_start[i]; + } + bool block_empty = false; + for (int i = 0; i < ndim; ++i) { + block_empty |= (block_stop[i] <= start[i] || block_start[i] >= stop[i]); + } + if (block_empty) { + continue; + } + + // compute the start of the slice inside the block + int64_t slice_start[B2ND_MAX_DIM] = {0}; + for (int i = 0; i < ndim; ++i) { + if (block_start[i] < buffer_start[i]) { + slice_start[i] = buffer_start[i] - block_start[i]; + } else { + slice_start[i] = 0; + } + slice_start[i] += block_start[i]; + } + + int64_t slice_stop[B2ND_MAX_DIM] = {0}; + for (int i = 0; i < ndim; ++i) { + if (block_stop[i] > buffer_stop[i]) { + slice_stop[i] = block_shape[i] - (block_stop[i] - buffer_stop[i]); + } else { + slice_stop[i] = block_stop[i] - block_start[i]; + } + slice_stop[i] += block_start[i]; + } + + int64_t slice_shape[B2ND_MAX_DIM] = {0}; + for (int i = 0; i < ndim; ++i) { + slice_shape[i] = slice_stop[i] - slice_start[i]; + } + + uint8_t *src = &buffer_b[0]; + const int64_t *src_pad_shape = buffer_shape; + + int64_t src_start[B2ND_MAX_DIM] = {0}; + int64_t src_stop[B2ND_MAX_DIM] = {0}; + for (int i = 0; i < ndim; ++i) { + src_start[i] = slice_start[i] - buffer_start[i]; + src_stop[i] = slice_stop[i] - buffer_start[i]; + } + + uint8_t *dst = &data[nblock * array->blocknitems * array->sc->typesize]; + int64_t dst_pad_shape[B2ND_MAX_DIM]; + for (int i = 0; i < ndim; ++i) { + dst_pad_shape[i] = array->blockshape[i]; + } + + int64_t dst_start[B2ND_MAX_DIM] = {0}; + int64_t dst_stop[B2ND_MAX_DIM] = {0}; + for (int i = 0; i < ndim; ++i) { + dst_start[i] = slice_start[i] - block_start[i]; + dst_stop[i] = dst_start[i] + slice_shape[i]; + } + + if (set_slice) { + b2nd_copy_buffer(ndim, array->sc->typesize, + src, src_pad_shape, src_start, src_stop, + dst, dst_pad_shape, dst_start); + } else { + b2nd_copy_buffer(ndim, array->sc->typesize, + dst, dst_pad_shape, dst_start, dst_stop, + src, src_pad_shape, src_start); + } + } + + if (set_slice) { + // Recompress the data + int32_t chunk_nbytes = data_nbytes + BLOSC2_MAX_OVERHEAD; + uint8_t *chunk = malloc(chunk_nbytes); + BLOSC_ERROR_NULL(chunk, BLOSC2_ERROR_MEMORY_ALLOC); + int brc; + brc = blosc2_compress_ctx(array->sc->cctx, data, data_nbytes, chunk, chunk_nbytes); + if (brc < 0) { + BLOSC_TRACE_ERROR("Blosc can not compress the data"); + BLOSC_ERROR(BLOSC2_ERROR_FAILURE); + } + int64_t brc_ = blosc2_schunk_update_chunk(array->sc, nchunk, chunk, false); + if (brc_ < 0) { + BLOSC_TRACE_ERROR("Blosc can not update the chunk"); + BLOSC_ERROR(BLOSC2_ERROR_FAILURE); + } + } + } + + free(data); + + return BLOSC2_ERROR_SUCCESS; +} + + +int b2nd_get_slice_cbuffer(const b2nd_array_t *array, const int64_t *start, const int64_t *stop, + void *buffer, const int64_t *buffershape, int64_t buffersize) { + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(start, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(stop, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(buffershape, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(buffer, BLOSC2_ERROR_NULL_POINTER); + + int64_t size = array->sc->typesize; + for (int i = 0; i < array->ndim; ++i) { + if (stop[i] - start[i] > buffershape[i]) { + BLOSC_TRACE_ERROR("The buffer shape can not be smaller than the slice shape"); + return BLOSC2_ERROR_INVALID_PARAM; + } + size *= buffershape[i]; + } + + if (array->nitems == 0) { + return BLOSC2_ERROR_SUCCESS; + } + + if (buffersize < size) { + BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); + } + BLOSC_ERROR(get_set_slice(buffer, buffersize, start, stop, buffershape, (b2nd_array_t *)array, false)); + + return BLOSC2_ERROR_SUCCESS; +} + + +int b2nd_set_slice_cbuffer(const void *buffer, const int64_t *buffershape, int64_t buffersize, + const int64_t *start, const int64_t *stop, + b2nd_array_t *array) { + BLOSC_ERROR_NULL(buffer, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(start, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(stop, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + + int64_t size = array->sc->typesize; + for (int i = 0; i < array->ndim; ++i) { + size *= stop[i] - start[i]; + } + + if (buffersize < size) { + BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); + } + + if (array->nitems == 0) { + return BLOSC2_ERROR_SUCCESS; + } + + BLOSC_ERROR(get_set_slice((void*)buffer, buffersize, start, stop, (int64_t *)buffershape, array, true)); + + return BLOSC2_ERROR_SUCCESS; +} + + +int b2nd_get_slice(b2nd_context_t *ctx, b2nd_array_t **array, const b2nd_array_t *src, const int64_t *start, + const int64_t *stop) { + BLOSC_ERROR_NULL(src, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(start, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(stop, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + + ctx->ndim = src->ndim; + for (int i = 0; i < src->ndim; ++i) { + ctx->shape[i] = stop[i] - start[i]; + } + + // Add data + BLOSC_ERROR(b2nd_empty(ctx, array)); + + if ((*array)->nitems == 0) { + return BLOSC2_ERROR_SUCCESS; + } + + int8_t ndim = (*array)->ndim; + int64_t chunks_in_array[B2ND_MAX_DIM] = {0}; + for (int i = 0; i < ndim; ++i) { + chunks_in_array[i] = (*array)->extshape[i] / (*array)->chunkshape[i]; + } + int64_t nchunks = (*array)->sc->nchunks; + for (int nchunk = 0; nchunk < nchunks; ++nchunk) { + int64_t nchunk_ndim[B2ND_MAX_DIM] = {0}; + blosc2_unidim_to_multidim(ndim, chunks_in_array, nchunk, nchunk_ndim); + + // Check if the chunk needs to be updated + int64_t chunk_start[B2ND_MAX_DIM] = {0}; + int64_t chunk_stop[B2ND_MAX_DIM] = {0}; + int64_t chunk_shape[B2ND_MAX_DIM] = {0}; + for (int i = 0; i < ndim; ++i) { + chunk_start[i] = nchunk_ndim[i] * (*array)->chunkshape[i]; + chunk_stop[i] = chunk_start[i] + (*array)->chunkshape[i]; + if (chunk_stop[i] > (*array)->shape[i]) { + chunk_stop[i] = (*array)->shape[i]; + } + chunk_shape[i] = chunk_stop[i] - chunk_start[i]; + } + + int64_t src_start[B2ND_MAX_DIM] = {0}; + int64_t src_stop[B2ND_MAX_DIM] = {0}; + for (int i = 0; i < ndim; ++i) { + src_start[i] = chunk_start[i] + start[i]; + src_stop[i] = chunk_stop[i] + start[i]; + } + int64_t buffersize = ctx->b2_storage->cparams->typesize; + for (int i = 0; i < ndim; ++i) { + buffersize *= chunk_shape[i]; + } + uint8_t *buffer = malloc(buffersize); + BLOSC_ERROR_NULL(buffer, BLOSC2_ERROR_MEMORY_ALLOC); + BLOSC_ERROR(b2nd_get_slice_cbuffer(src, src_start, src_stop, buffer, chunk_shape, + buffersize)); + BLOSC_ERROR(b2nd_set_slice_cbuffer(buffer, chunk_shape, buffersize, chunk_start, + chunk_stop, *array)); + free(buffer); + } + + return BLOSC2_ERROR_SUCCESS; +} + + +int b2nd_squeeze(b2nd_array_t *array) { + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + + bool index[B2ND_MAX_DIM]; + + for (int i = 0; i < array->ndim; ++i) { + if (array->shape[i] != 1) { + index[i] = false; + } else { + index[i] = true; + } + } + BLOSC_ERROR(b2nd_squeeze_index(array, index)); + + return BLOSC2_ERROR_SUCCESS; +} + + +int b2nd_squeeze_index(b2nd_array_t *array, const bool *index) { + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + + uint8_t nones = 0; + int64_t newshape[B2ND_MAX_DIM]; + int32_t newchunkshape[B2ND_MAX_DIM]; + int32_t newblockshape[B2ND_MAX_DIM]; + + for (int i = 0; i < array->ndim; ++i) { + if (index[i] == true) { + if (array->shape[i] != 1) { + BLOSC_ERROR(BLOSC2_ERROR_INVALID_INDEX); + } + } else { + newshape[nones] = array->shape[i]; + newchunkshape[nones] = array->chunkshape[i]; + newblockshape[nones] = array->blockshape[i]; + nones += 1; + } + } + + for (int i = 0; i < B2ND_MAX_DIM; ++i) { + if (i < nones) { + array->chunkshape[i] = newchunkshape[i]; + array->blockshape[i] = newblockshape[i]; + } else { + array->chunkshape[i] = 1; + array->blockshape[i] = 1; + } + } + + BLOSC_ERROR(update_shape(array, nones, newshape, newchunkshape, newblockshape)); + + return BLOSC2_ERROR_SUCCESS; +} + + +int b2nd_copy(b2nd_context_t *ctx, const b2nd_array_t *src, b2nd_array_t **array) { + BLOSC_ERROR_NULL(src, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + + ctx->ndim = src->ndim; + + for (int i = 0; i < src->ndim; ++i) { + ctx->shape[i] = src->shape[i]; + } + + bool equals = true; + for (int i = 0; i < src->ndim; ++i) { + if (src->chunkshape[i] != ctx->chunkshape[i]) { + equals = false; + break; + } + if (src->blockshape[i] != ctx->blockshape[i]) { + equals = false; + break; + } + } + + if (equals) { + BLOSC_ERROR(array_without_schunk(ctx, array)); + + blosc2_schunk *new_sc = blosc2_schunk_copy(src->sc, ctx->b2_storage); + + if (new_sc == NULL) { + return BLOSC2_ERROR_FAILURE; + } + (*array)->sc = new_sc; + + } else { + int64_t start[B2ND_MAX_DIM] = {0}; + + int64_t stop[B2ND_MAX_DIM]; + for (int i = 0; i < src->ndim; ++i) { + stop[i] = src->shape[i]; + } + // Copy metalayers + b2nd_context_t params_meta; + memcpy(¶ms_meta, ctx, sizeof(params_meta)); + int j = 0; + + for (int i = 0; i < src->sc->nmetalayers; ++i) { + if (strcmp(src->sc->metalayers[i]->name, "b2nd") == 0) { + continue; + } + blosc2_metalayer *meta = ¶ms_meta.metalayers[j]; + meta->name = src->sc->metalayers[i]->name; + meta->content = src->sc->metalayers[i]->content; + meta->content_len = src->sc->metalayers[i]->content_len; + j++; + } + params_meta.nmetalayers = j; + + // Copy data + BLOSC_ERROR(b2nd_get_slice(¶ms_meta, array, src, start, stop)); + + // Copy vlmetayers + for (int i = 0; i < src->sc->nvlmetalayers; ++i) { + uint8_t *content; + int32_t content_len; + if (blosc2_vlmeta_get(src->sc, src->sc->vlmetalayers[i]->name, &content, + &content_len) < 0) { + BLOSC_ERROR(BLOSC2_ERROR_FAILURE); + } + BLOSC_ERROR(blosc2_vlmeta_add((*array)->sc, src->sc->vlmetalayers[i]->name, content, content_len, + (*array)->sc->storage->cparams)); + free(content); + } + } + return BLOSC2_ERROR_SUCCESS; +} + + +int b2nd_save(const b2nd_array_t *array, char *urlpath) { + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(urlpath, BLOSC2_ERROR_NULL_POINTER); + + b2nd_array_t *tmp; + blosc2_storage b2_storage = BLOSC2_STORAGE_DEFAULTS; + b2nd_context_t params = {.b2_storage=&b2_storage}; + b2_storage.urlpath = urlpath; + b2_storage.contiguous = array->sc->storage->contiguous; + + for (int i = 0; i < array->ndim; ++i) { + params.chunkshape[i] = array->chunkshape[i]; + params.blockshape[i] = array->blockshape[i]; + } + + BLOSC_ERROR(b2nd_copy(¶ms, array, &tmp)); + BLOSC_ERROR(b2nd_free(tmp)); + + return BLOSC2_ERROR_SUCCESS; +} + + +int b2nd_print_meta(const b2nd_array_t *array) { + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + int8_t ndim; + int64_t shape[B2ND_MAX_DIM]; + int32_t chunkshape[B2ND_MAX_DIM]; + int32_t blockshape[B2ND_MAX_DIM]; + char *dtype; + int8_t dtype_format; + uint8_t *smeta; + int32_t smeta_len; + if (blosc2_meta_get(array->sc, "b2nd", &smeta, &smeta_len) < 0) { + // Try with a caterva metalayer; we are meant to be backward compatible with it + if (blosc2_meta_get(array->sc, "caterva", &smeta, &smeta_len) < 0) { + BLOSC_ERROR(BLOSC2_ERROR_METALAYER_NOT_FOUND); + } + } + BLOSC_ERROR(b2nd_deserialize_meta(smeta, smeta_len, &ndim, shape, chunkshape, blockshape, + &dtype, &dtype_format)); + free(smeta); + + printf("b2nd metalayer parameters: \n Ndim: %d", ndim); + printf("\n shape: %" PRId64 "", shape[0]); + for (int i = 1; i < ndim; ++i) { + printf(", %" PRId64 "", shape[i]); + } + printf("\n chunkshape: %d", chunkshape[0]); + for (int i = 1; i < ndim; ++i) { + printf(", %d", chunkshape[i]); + } + if (dtype != NULL) { + printf("\n dtype: %s", dtype); + free(dtype); + } + + printf("\n blockshape: %d", blockshape[0]); + for (int i = 1; i < ndim; ++i) { + printf(", %d", blockshape[i]); + } + printf("\n"); + + return BLOSC2_ERROR_SUCCESS; +} + + +int extend_shape(b2nd_array_t *array, const int64_t *new_shape, const int64_t *start) { + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(new_shape, BLOSC2_ERROR_NULL_POINTER); + + int8_t ndim = array->ndim; + int64_t diffs_shape[B2ND_MAX_DIM]; + int64_t diffs_sum = 0; + for (int i = 0; i < ndim; i++) { + diffs_shape[i] = new_shape[i] - array->shape[i]; + diffs_sum += diffs_shape[i]; + if (diffs_shape[i] < 0) { + BLOSC_TRACE_ERROR("The new shape must be greater than the old one"); + BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); + } + if (array->shape[i] == 0) { + BLOSC_TRACE_ERROR("Cannot extend array with shape[%d] = 0", i); + BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); + } + } + if (diffs_sum == 0) { + // Shapes are equal. Do nothing. + return BLOSC2_ERROR_SUCCESS; + } + + int64_t old_nchunks = array->sc->nchunks; + // aux array to keep old shapes + b2nd_array_t *aux = malloc(sizeof(b2nd_array_t)); + BLOSC_ERROR_NULL(aux, BLOSC2_ERROR_MEMORY_ALLOC); + aux->sc = NULL; + BLOSC_ERROR(update_shape(aux, ndim, array->shape, array->chunkshape, array->blockshape)); + + BLOSC_ERROR(update_shape(array, ndim, new_shape, array->chunkshape, array->blockshape)); + + int64_t nchunks = array->extnitems / array->chunknitems; + int64_t nchunks_; + int64_t nchunk_ndim[B2ND_MAX_DIM]; + blosc2_cparams *cparams; + BLOSC_ERROR(blosc2_schunk_get_cparams(array->sc, &cparams)); + void *chunk; + int64_t csize; + if (nchunks != old_nchunks) { + if (start == NULL) { + start = aux->shape; + } + int64_t chunks_in_array[B2ND_MAX_DIM] = {0}; + for (int i = 0; i < ndim; ++i) { + chunks_in_array[i] = array->extshape[i] / array->chunkshape[i]; + } + for (int i = 0; i < nchunks; ++i) { + blosc2_unidim_to_multidim(ndim, chunks_in_array, i, nchunk_ndim); + for (int j = 0; j < ndim; ++j) { + if (start[j] <= (array->chunkshape[j] * nchunk_ndim[j]) + && (array->chunkshape[j] * nchunk_ndim[j]) < (start[j] + new_shape[j] - aux->shape[j])) { + chunk = malloc(BLOSC_EXTENDED_HEADER_LENGTH); + BLOSC_ERROR_NULL(chunk, BLOSC2_ERROR_MEMORY_ALLOC); + csize = blosc2_chunk_zeros(*cparams, array->sc->chunksize, chunk, BLOSC_EXTENDED_HEADER_LENGTH); + if (csize < 0) { + free(aux); + free(cparams); + BLOSC_TRACE_ERROR("Blosc error when creating a chunk"); + return BLOSC2_ERROR_FAILURE; + } + nchunks_ = blosc2_schunk_insert_chunk(array->sc, i, chunk, false); + if (nchunks_ < 0) { + free(aux); + free(cparams); + BLOSC_TRACE_ERROR("Blosc error when inserting a chunk"); + return BLOSC2_ERROR_FAILURE; + } + break; + } + } + } + } + free(aux); + free(cparams); + + return BLOSC2_ERROR_SUCCESS; +} + + +int shrink_shape(b2nd_array_t *array, const int64_t *new_shape, const int64_t *start) { + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(new_shape, BLOSC2_ERROR_NULL_POINTER); + + int8_t ndim = array->ndim; + int64_t diffs_shape[B2ND_MAX_DIM]; + int64_t diffs_sum = 0; + for (int i = 0; i < ndim; i++) { + diffs_shape[i] = new_shape[i] - array->shape[i]; + diffs_sum += diffs_shape[i]; + if (diffs_shape[i] > 0) { + BLOSC_TRACE_ERROR("The new shape must be smaller than the old one"); + BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); + } + if (array->shape[i] == 0) { + BLOSC_TRACE_ERROR("Cannot shrink array with shape[%d] = 0", i); + BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); + } + } + if (diffs_sum == 0) { + // Shapes are equal. Do nothing. + return BLOSC2_ERROR_SUCCESS; + } + + int64_t old_nchunks = array->sc->nchunks; + // aux array to keep old shapes + b2nd_array_t *aux = malloc(sizeof(b2nd_array_t)); + BLOSC_ERROR_NULL(aux, BLOSC2_ERROR_MEMORY_ALLOC); + aux->sc = NULL; + BLOSC_ERROR(update_shape(aux, ndim, array->shape, array->chunkshape, array->blockshape)); + + BLOSC_ERROR(update_shape(array, ndim, new_shape, array->chunkshape, array->blockshape)); + + // Delete chunks if needed + int64_t chunks_in_array_old[B2ND_MAX_DIM] = {0}; + for (int i = 0; i < ndim; ++i) { + chunks_in_array_old[i] = aux->extshape[i] / aux->chunkshape[i]; + } + if (start == NULL) { + start = new_shape; + } + + int64_t nchunk_ndim[B2ND_MAX_DIM] = {0}; + int64_t nchunks_; + for (int i = (int) old_nchunks - 1; i >= 0; --i) { + blosc2_unidim_to_multidim(ndim, chunks_in_array_old, i, nchunk_ndim); + for (int j = 0; j < ndim; ++j) { + if (start[j] <= (array->chunkshape[j] * nchunk_ndim[j]) + && (array->chunkshape[j] * nchunk_ndim[j]) < (start[j] + aux->shape[j] - new_shape[j])) { + nchunks_ = blosc2_schunk_delete_chunk(array->sc, i); + if (nchunks_ < 0) { + free(aux); + BLOSC_TRACE_ERROR("Blosc error when deleting a chunk"); + return BLOSC2_ERROR_FAILURE; + } + break; + } + } + } + free(aux); + + return BLOSC2_ERROR_SUCCESS; +} + + +int b2nd_resize(b2nd_array_t *array, const int64_t *new_shape, + const int64_t *start) { + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(new_shape, BLOSC2_ERROR_NULL_POINTER); + + if (start != NULL) { + for (int i = 0; i < array->ndim; ++i) { + if (start[i] > array->shape[i]) { + BLOSC_TRACE_ERROR("`start` must be lower or equal than old array shape in all dims"); + BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); + } + if ((new_shape[i] > array->shape[i] && start[i] != array->shape[i]) + || (new_shape[i] < array->shape[i] + && (start[i] + array->shape[i] - new_shape[i]) != array->shape[i])) { + // Chunks cannot be cut unless they are in the last position + if (start[i] % array->chunkshape[i] != 0) { + BLOSC_TRACE_ERROR("If array end is not being modified " + "`start` must be a multiple of chunkshape in all dims"); + BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); + } + if ((new_shape[i] - array->shape[i]) % array->chunkshape[i] != 0) { + BLOSC_TRACE_ERROR("If array end is not being modified " + "`(new_shape - shape)` must be multiple of chunkshape in all dims"); + BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); + } + } + } + } + + // Get shrunk shape + int64_t shrunk_shape[B2ND_MAX_DIM] = {0}; + for (int i = 0; i < array->ndim; ++i) { + if (new_shape[i] <= array->shape[i]) { + shrunk_shape[i] = new_shape[i]; + } else { + shrunk_shape[i] = array->shape[i]; + } + } + + BLOSC_ERROR(shrink_shape(array, shrunk_shape, start)); + BLOSC_ERROR(extend_shape(array, new_shape, start)); + + return BLOSC2_ERROR_SUCCESS; +} + + +int b2nd_insert(b2nd_array_t *array, const void *buffer, int64_t buffersize, + int8_t axis, int64_t insert_start) { + + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(buffer, BLOSC2_ERROR_NULL_POINTER); + + if (axis >= array->ndim) { + BLOSC_TRACE_ERROR("`axis` cannot be greater than the number of dimensions"); + BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); + } + + int64_t axis_size = array->sc->typesize; + int64_t buffershape[B2ND_MAX_DIM]; + for (int i = 0; i < array->ndim; ++i) { + if (i != axis) { + axis_size *= array->shape[i]; + buffershape[i] = array->shape[i]; + } + } + if (buffersize % axis_size != 0) { + BLOSC_TRACE_ERROR("`buffersize` must be multiple of the array"); + BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); + } + int64_t newshape[B2ND_MAX_DIM]; + memcpy(newshape, array->shape, array->ndim * sizeof(int64_t)); + newshape[axis] += buffersize / axis_size; + buffershape[axis] = newshape[axis] - array->shape[axis]; + int64_t start[B2ND_MAX_DIM] = {0}; + start[axis] = insert_start; + + if (insert_start == array->shape[axis]) { + BLOSC_ERROR(b2nd_resize(array, newshape, NULL)); + } else { + BLOSC_ERROR(b2nd_resize(array, newshape, start)); + } + + int64_t stop[B2ND_MAX_DIM]; + memcpy(stop, array->shape, sizeof(int64_t) * array->ndim); + stop[axis] = start[axis] + buffershape[axis]; + BLOSC_ERROR(b2nd_set_slice_cbuffer(buffer, buffershape, buffersize, start, stop, array)); + + return BLOSC2_ERROR_SUCCESS; +} + + +int b2nd_append(b2nd_array_t *array, const void *buffer, int64_t buffersize, + int8_t axis) { + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(buffer, BLOSC2_ERROR_NULL_POINTER); + + BLOSC_ERROR(b2nd_insert(array, buffer, buffersize, axis, array->shape[axis])); + + return BLOSC2_ERROR_SUCCESS; +} + + +int b2nd_delete(b2nd_array_t *array, const int8_t axis, + int64_t delete_start, int64_t delete_len) { + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + + if (axis >= array->ndim) { + BLOSC_TRACE_ERROR("axis cannot be greater than the number of dimensions"); + BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); + } + + + int64_t newshape[B2ND_MAX_DIM]; + memcpy(newshape, array->shape, array->ndim * sizeof(int64_t)); + newshape[axis] -= delete_len; + int64_t start[B2ND_MAX_DIM] = {0}; + start[axis] = delete_start; + + if (delete_start == (array->shape[axis] - delete_len)) { + BLOSC_ERROR(b2nd_resize(array, newshape, NULL)); + } else { + BLOSC_ERROR(b2nd_resize(array, newshape, start)); + } + + return BLOSC2_ERROR_SUCCESS; +} + +// Indexing + +typedef struct { + int64_t value; + int64_t index; +} b2nd_selection_t; + + +int compare_selection(const void *a, const void *b) { + int res = (int) (((b2nd_selection_t *) a)->value - ((b2nd_selection_t *) b)->value); + // In case values are equal, sort by index + if (res == 0) { + res = (int) (((b2nd_selection_t *) a)->index - ((b2nd_selection_t *) b)->index); + } + return res; +} + + +int copy_block_buffer_data(b2nd_array_t *array, + int8_t ndim, + int64_t *block_selection_size, + b2nd_selection_t **chunk_selection, + b2nd_selection_t **p_block_selection_0, + b2nd_selection_t **p_block_selection_1, + uint8_t *block, + uint8_t *buffer, + int64_t *buffershape, + int64_t *bufferstrides, + bool get) { + p_block_selection_0[ndim] = chunk_selection[ndim]; + p_block_selection_1[ndim] = chunk_selection[ndim]; + while (p_block_selection_1[ndim] - p_block_selection_0[ndim] < block_selection_size[ndim]) { + if (ndim == array->ndim - 1) { + + int64_t index_in_block_n[B2ND_MAX_DIM]; + for (int i = 0; i < array->ndim; ++i) { + index_in_block_n[i] = p_block_selection_1[i]->value % array->chunkshape[i] % array->blockshape[i]; + } + int64_t index_in_block = 0; + for (int i = 0; i < array->ndim; ++i) { + index_in_block += index_in_block_n[i] * array->item_block_strides[i]; + } + + int64_t index_in_buffer_n[B2ND_MAX_DIM]; + for (int i = 0; i < array->ndim; ++i) { + index_in_buffer_n[i] = p_block_selection_1[i]->index; + } + int64_t index_in_buffer = 0; + for (int i = 0; i < array->ndim; ++i) { + index_in_buffer += index_in_buffer_n[i] * bufferstrides[i]; + } + if (get) { + memcpy(&buffer[index_in_buffer * array->sc->typesize], + &block[index_in_block * array->sc->typesize], + array->sc->typesize); + } else { + memcpy(&block[index_in_block * array->sc->typesize], + &buffer[index_in_buffer * array->sc->typesize], + array->sc->typesize); + } + } else { + BLOSC_ERROR(copy_block_buffer_data(array, (int8_t) (ndim + 1), block_selection_size, + chunk_selection, + p_block_selection_0, p_block_selection_1, block, + buffer, buffershape, bufferstrides, get) + ); + } + p_block_selection_1[ndim]++; + } + return BLOSC2_ERROR_SUCCESS; +} + + +int iter_block_copy(b2nd_array_t *array, int8_t ndim, + int64_t *chunk_selection_size, + b2nd_selection_t **ordered_selection, + b2nd_selection_t **chunk_selection_0, + b2nd_selection_t **chunk_selection_1, + uint8_t *data, + uint8_t *buffer, + int64_t *buffershape, + int64_t *bufferstrides, + bool get) { + chunk_selection_0[ndim] = ordered_selection[ndim]; + chunk_selection_1[ndim] = ordered_selection[ndim]; + while (chunk_selection_1[ndim] - ordered_selection[ndim] < chunk_selection_size[ndim]) { + int64_t block_index_ndim = ((*chunk_selection_1[ndim]).value % array->chunkshape[ndim]) / array->blockshape[ndim]; + while (chunk_selection_1[ndim] - ordered_selection[ndim] < chunk_selection_size[ndim] && + block_index_ndim == ((*chunk_selection_1[ndim]).value % array->chunkshape[ndim]) / array->blockshape[ndim]) { + chunk_selection_1[ndim]++; + } + if (ndim == array->ndim - 1) { + int64_t block_chunk_strides[B2ND_MAX_DIM]; + block_chunk_strides[array->ndim - 1] = 1; + for (int i = array->ndim - 2; i >= 0; --i) { + block_chunk_strides[i] = block_chunk_strides[i + 1] * (array->extchunkshape[i + 1] / array->blockshape[i + 1]); + } + int64_t block_index[B2ND_MAX_DIM]; + for (int i = 0; i < array->ndim; ++i) { + block_index[i] = ((*chunk_selection_0[i]).value % array->chunkshape[i]) / array->blockshape[i]; + } + int64_t nblock = 0; + for (int i = 0; i < array->ndim; ++i) { + nblock += block_index[i] * block_chunk_strides[i]; + } + b2nd_selection_t **p_block_selection_0 = malloc(array->ndim * sizeof(b2nd_selection_t *)); + BLOSC_ERROR_NULL(p_block_selection_0, BLOSC2_ERROR_MEMORY_ALLOC); + b2nd_selection_t **p_block_selection_1 = malloc(array->ndim * sizeof(b2nd_selection_t *)); + BLOSC_ERROR_NULL(p_block_selection_1, BLOSC2_ERROR_MEMORY_ALLOC); + int64_t *block_selection_size = malloc(array->ndim * sizeof(int64_t)); + BLOSC_ERROR_NULL(block_selection_size, BLOSC2_ERROR_MEMORY_ALLOC); + for (int i = 0; i < array->ndim; ++i) { + block_selection_size[i] = chunk_selection_1[i] - chunk_selection_0[i]; + } + + BLOSC_ERROR(copy_block_buffer_data(array, + (int8_t) 0, + block_selection_size, + chunk_selection_0, + p_block_selection_0, + p_block_selection_1, + &data[nblock * array->blocknitems * array->sc->typesize], + buffer, + buffershape, + bufferstrides, + get) + ); + free(p_block_selection_0); + free(p_block_selection_1); + free(block_selection_size); + } else { + BLOSC_ERROR(iter_block_copy(array, (int8_t) (ndim + 1), chunk_selection_size, + ordered_selection, chunk_selection_0, chunk_selection_1, + data, buffer, buffershape, bufferstrides, get) + ); + } + chunk_selection_0[ndim] = chunk_selection_1[ndim]; + + } + + return BLOSC2_ERROR_SUCCESS; +} + + +int iter_block_maskout(b2nd_array_t *array, int8_t ndim, + int64_t *sel_block_size, + b2nd_selection_t **o_selection, + b2nd_selection_t **p_o_sel_block_0, + b2nd_selection_t **p_o_sel_block_1, + bool *maskout) { + p_o_sel_block_0[ndim] = o_selection[ndim]; + p_o_sel_block_1[ndim] = o_selection[ndim]; + while (p_o_sel_block_1[ndim] - o_selection[ndim] < sel_block_size[ndim]) { + int64_t block_index_ndim = ((*p_o_sel_block_1[ndim]).value % array->chunkshape[ndim]) / array->blockshape[ndim]; + while (p_o_sel_block_1[ndim] - o_selection[ndim] < sel_block_size[ndim] && + block_index_ndim == ((*p_o_sel_block_1[ndim]).value % array->chunkshape[ndim]) / array->blockshape[ndim]) { + p_o_sel_block_1[ndim]++; + } + if (ndim == array->ndim - 1) { + int64_t block_chunk_strides[B2ND_MAX_DIM]; + block_chunk_strides[array->ndim - 1] = 1; + for (int i = array->ndim - 2; i >= 0; --i) { + block_chunk_strides[i] = block_chunk_strides[i + 1] * (array->extchunkshape[i + 1] / array->blockshape[i + 1]); + } + int64_t block_index[B2ND_MAX_DIM]; + for (int i = 0; i < array->ndim; ++i) { + block_index[i] = ((*p_o_sel_block_0[i]).value % array->chunkshape[i]) / array->blockshape[i]; + } + int64_t nblock = 0; + for (int i = 0; i < array->ndim; ++i) { + nblock += block_index[i] * block_chunk_strides[i]; + } + maskout[nblock] = false; + } else { + BLOSC_ERROR(iter_block_maskout(array, (int8_t) (ndim + 1), sel_block_size, + o_selection, p_o_sel_block_0, p_o_sel_block_1, + maskout) + ); + } + p_o_sel_block_0[ndim] = p_o_sel_block_1[ndim]; + + } + + return BLOSC2_ERROR_SUCCESS; +} + + +int iter_chunk(b2nd_array_t *array, int8_t ndim, + int64_t *selection_size, + b2nd_selection_t **ordered_selection, + b2nd_selection_t **p_ordered_selection_0, + b2nd_selection_t **p_ordered_selection_1, + uint8_t *buffer, + int64_t *buffershape, + int64_t *bufferstrides, + bool get) { + p_ordered_selection_0[ndim] = ordered_selection[ndim]; + p_ordered_selection_1[ndim] = ordered_selection[ndim]; + while (p_ordered_selection_1[ndim] - ordered_selection[ndim] < selection_size[ndim]) { + int64_t chunk_index_ndim = (*p_ordered_selection_1[ndim]).value / array->chunkshape[ndim]; + while (p_ordered_selection_1[ndim] - ordered_selection[ndim] < selection_size[ndim] && + chunk_index_ndim == (*p_ordered_selection_1[ndim]).value / array->chunkshape[ndim]) { + p_ordered_selection_1[ndim]++; + } + if (ndim == array->ndim - 1) { + int64_t chunk_array_strides[B2ND_MAX_DIM]; + chunk_array_strides[array->ndim - 1] = 1; + for (int i = array->ndim - 2; i >= 0; --i) { + chunk_array_strides[i] = chunk_array_strides[i + 1] * + (array->extshape[i + 1] / array->chunkshape[i + 1]); + } + int64_t chunk_index[B2ND_MAX_DIM]; + for (int i = 0; i < array->ndim; ++i) { + chunk_index[i] = (*p_ordered_selection_0[i]).value / array->chunkshape[i]; + } + int64_t nchunk = 0; + for (int i = 0; i < array->ndim; ++i) { + nchunk += chunk_index[i] * chunk_array_strides[i]; + } + + int64_t nblocks = array->extchunknitems / array->blocknitems; + b2nd_selection_t **p_chunk_selection_0 = malloc(array->ndim * sizeof(b2nd_selection_t *)); + BLOSC_ERROR_NULL(p_chunk_selection_0, BLOSC2_ERROR_MEMORY_ALLOC); + b2nd_selection_t **p_chunk_selection_1 = malloc(array->ndim * sizeof(b2nd_selection_t *)); + BLOSC_ERROR_NULL(p_chunk_selection_1, BLOSC2_ERROR_MEMORY_ALLOC); + int64_t *chunk_selection_size = malloc(array->ndim * sizeof(int64_t)); + BLOSC_ERROR_NULL(chunk_selection_size, BLOSC2_ERROR_MEMORY_ALLOC); + for (int i = 0; i < array->ndim; ++i) { + chunk_selection_size[i] = p_ordered_selection_1[i] - p_ordered_selection_0[i]; + } + + if (get) { + bool *maskout = calloc(nblocks, sizeof(bool)); + for (int i = 0; i < nblocks; ++i) { + maskout[i] = true; + } + + BLOSC_ERROR(iter_block_maskout(array, (int8_t) 0, + chunk_selection_size, + p_ordered_selection_0, + p_chunk_selection_0, + p_chunk_selection_1, + maskout)); + + if (blosc2_set_maskout(array->sc->dctx, maskout, (int) nblocks) != + BLOSC2_ERROR_SUCCESS) { + BLOSC_TRACE_ERROR("Error setting the maskout"); + BLOSC_ERROR(BLOSC2_ERROR_FAILURE); + } + free(maskout); + } + int data_nitems = (int) array->extchunknitems; + int data_nbytes = data_nitems * array->sc->typesize; + uint8_t *data = malloc(data_nitems * array->sc->typesize); + BLOSC_ERROR_NULL(data, BLOSC2_ERROR_MEMORY_ALLOC); + int err = blosc2_schunk_decompress_chunk(array->sc, nchunk, data, data_nbytes); + if (err < 0) { + BLOSC_TRACE_ERROR("Error decompressing chunk"); + BLOSC_ERROR(BLOSC2_ERROR_FAILURE); + } + BLOSC_ERROR(iter_block_copy(array, 0, chunk_selection_size, + p_ordered_selection_0, p_chunk_selection_0, p_chunk_selection_1, + data, buffer, buffershape, bufferstrides, get)); + + if (!get) { + int32_t chunk_size = data_nbytes + BLOSC_EXTENDED_HEADER_LENGTH; + uint8_t *chunk = malloc(chunk_size); + BLOSC_ERROR_NULL(chunk, BLOSC2_ERROR_MEMORY_ALLOC); + err = blosc2_compress_ctx(array->sc->cctx, data, data_nbytes, chunk, chunk_size); + if (err < 0) { + BLOSC_TRACE_ERROR("Error compressing data"); + BLOSC_ERROR(BLOSC2_ERROR_FAILURE); + } + err = (int) blosc2_schunk_update_chunk(array->sc, nchunk, chunk, false); + if (err < 0) { + BLOSC_TRACE_ERROR("Error updating chunk"); + BLOSC_ERROR(BLOSC2_ERROR_FAILURE); + } + } + free(data); + free(chunk_selection_size); + free(p_chunk_selection_0); + free(p_chunk_selection_1); + } else { + BLOSC_ERROR(iter_chunk(array, (int8_t) (ndim + 1), selection_size, + ordered_selection, p_ordered_selection_0, p_ordered_selection_1, + buffer, buffershape, bufferstrides, get)); + } + + p_ordered_selection_0[ndim] = p_ordered_selection_1[ndim]; + } + return BLOSC2_ERROR_SUCCESS; +} + + +int orthogonal_selection(b2nd_array_t *array, int64_t **selection, int64_t *selection_size, void *buffer, + int64_t *buffershape, int64_t buffersize, bool get) { + BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(selection, BLOSC2_ERROR_NULL_POINTER); + BLOSC_ERROR_NULL(selection_size, BLOSC2_ERROR_NULL_POINTER); + + int8_t ndim = array->ndim; + + for (int i = 0; i < ndim; ++i) { + BLOSC_ERROR_NULL(selection[i], BLOSC2_ERROR_NULL_POINTER); + // Check that indexes are not larger than array shape + for (int j = 0; j < selection_size[i]; ++j) { + if (selection[i][j] > array->shape[i]) { + BLOSC_ERROR(BLOSC2_ERROR_INVALID_INDEX); + } + } + } + + // Check buffer size + int64_t sel_size = array->sc->typesize; + for (int i = 0; i < ndim; ++i) { + sel_size *= selection_size[i]; + } + + if (sel_size < buffersize) { + BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); + } + + // Sort selections + b2nd_selection_t **ordered_selection = malloc(ndim * sizeof(b2nd_selection_t *)); + BLOSC_ERROR_NULL(ordered_selection, BLOSC2_ERROR_MEMORY_ALLOC); + for (int i = 0; i < ndim; ++i) { + ordered_selection[i] = malloc(selection_size[i] * sizeof(b2nd_selection_t)); + for (int j = 0; j < selection_size[i]; ++j) { + ordered_selection[i][j].index = j; + ordered_selection[i][j].value = selection[i][j]; + } + qsort(ordered_selection[i], selection_size[i], sizeof(b2nd_selection_t), compare_selection); + } + + // Define pointers to iterate over ordered_selection data + b2nd_selection_t **p_ordered_selection_0 = malloc(ndim * sizeof(b2nd_selection_t *)); + BLOSC_ERROR_NULL(p_ordered_selection_0, BLOSC2_ERROR_MEMORY_ALLOC); + b2nd_selection_t **p_ordered_selection_1 = malloc(ndim * sizeof(b2nd_selection_t *)); + BLOSC_ERROR_NULL(p_ordered_selection_1, BLOSC2_ERROR_MEMORY_ALLOC); + + int64_t bufferstrides[B2ND_MAX_DIM]; + bufferstrides[array->ndim - 1] = 1; + for (int i = array->ndim - 2; i >= 0; --i) { + bufferstrides[i] = bufferstrides[i + 1] * buffershape[i + 1]; + } + + BLOSC_ERROR(iter_chunk(array, 0, + selection_size, ordered_selection, + p_ordered_selection_0, + p_ordered_selection_1, + buffer, buffershape, bufferstrides, get)); + + // Free allocated memory + free(p_ordered_selection_0); + free(p_ordered_selection_1); + for (int i = 0; i < ndim; ++i) { + free(ordered_selection[i]); + } + free(ordered_selection); + + return BLOSC2_ERROR_SUCCESS; +} + + +int b2nd_get_orthogonal_selection(const b2nd_array_t *array, int64_t **selection, int64_t *selection_size, void *buffer, + int64_t *buffershape, int64_t buffersize) { + return orthogonal_selection((b2nd_array_t *)array, selection, selection_size, buffer, buffershape, buffersize, true); +} + + +int b2nd_set_orthogonal_selection(b2nd_array_t *array, int64_t **selection, int64_t *selection_size, const void *buffer, + int64_t *buffershape, int64_t buffersize) { + return orthogonal_selection(array, selection, selection_size, (void*)buffer, buffershape, buffersize, false); +} + + +b2nd_context_t * +b2nd_create_ctx(const blosc2_storage *b2_storage, int8_t ndim, const int64_t *shape, const int32_t *chunkshape, + const int32_t *blockshape, const char *dtype, int8_t dtype_format, const blosc2_metalayer *metalayers, + int32_t nmetalayers) { + b2nd_context_t *ctx = malloc(sizeof(b2nd_context_t)); + BLOSC_ERROR_NULL(ctx, NULL); + blosc2_storage *params_b2_storage = malloc(sizeof(blosc2_storage)); + BLOSC_ERROR_NULL(params_b2_storage, NULL); + if (b2_storage == NULL) { + memcpy(params_b2_storage, &BLOSC2_STORAGE_DEFAULTS, sizeof(blosc2_storage)); + } + else { + memcpy(params_b2_storage, b2_storage, sizeof(blosc2_storage)); + } + blosc2_cparams *cparams = malloc(sizeof(blosc2_cparams)); + BLOSC_ERROR_NULL(cparams, NULL); + // We need a copy of cparams mainly to be able to modify blocksize + if (b2_storage->cparams == NULL) { + memcpy(cparams, &BLOSC2_CPARAMS_DEFAULTS, sizeof(blosc2_cparams)); + } + else { + memcpy(cparams, b2_storage->cparams, sizeof(blosc2_cparams)); + } + + if (dtype == NULL) { + ctx->dtype = strdup(B2ND_DEFAULT_DTYPE); + ctx->dtype_format = 0; // The default is NumPy format + } + else { + ctx->dtype = strdup(dtype); + ctx->dtype_format = dtype_format; + } + + params_b2_storage->cparams = cparams; + ctx->b2_storage = params_b2_storage; + ctx->ndim = ndim; + int32_t blocknitems = 1; + for (int i = 0; i < ndim; i++) { + ctx->shape[i] = shape[i]; + ctx->chunkshape[i] = chunkshape[i]; + ctx->blockshape[i] = blockshape[i]; + blocknitems *= ctx->blockshape[i]; + } + cparams->blocksize = blocknitems * cparams->typesize; + + ctx->nmetalayers = nmetalayers; + for (int i = 0; i < nmetalayers; ++i) { + ctx->metalayers[i] = metalayers[i]; + } + +#if defined(HAVE_PLUGINS) + #include "blosc2/codecs-registry.h" + if ((ctx->b2_storage->cparams->compcode >= BLOSC_CODEC_ZFP_FIXED_ACCURACY) && + (ctx->b2_storage->cparams->compcode <= BLOSC_CODEC_ZFP_FIXED_RATE)) { + for (int i = 0; i < BLOSC2_MAX_FILTERS; ++i) { + if ((ctx->b2_storage->cparams->filters[i] == BLOSC_SHUFFLE) || + (ctx->b2_storage->cparams->filters[i] == BLOSC_BITSHUFFLE)) { + BLOSC_TRACE_ERROR("ZFP cannot be run in presence of SHUFFLE / BITSHUFFLE"); + return NULL; + } + } + } +#endif /* HAVE_PLUGINS */ + + return ctx; +} + + +int b2nd_free_ctx(b2nd_context_t *ctx) { + ctx->b2_storage->cparams->schunk = NULL; + free(ctx->b2_storage->cparams); + free(ctx->b2_storage); + free(ctx->dtype); + free(ctx); + + return BLOSC2_ERROR_SUCCESS; +} diff --git a/blosc/b2nd_utils.c b/blosc/b2nd_utils.c new file mode 100644 index 00000000..4b4ff7d9 --- /dev/null +++ b/blosc/b2nd_utils.c @@ -0,0 +1,324 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +#include "b2nd_utils.h" + +// copyNdim where N = {2-8} - specializations of copy loops to be used by b2nd_copy_buffer +// since we don't have c++ templates, substitute manual specializations for up to known B2ND_MAX_DIM (8) +// it's not pretty, but it substantially reduces overhead vs. the generic method +void copy8dim(const uint8_t itemsize, + const int64_t *copy_shape, + const uint8_t *bsrc, const int64_t *src_strides, + uint8_t *bdst, const int64_t *dst_strides) { + int64_t copy_nbytes = copy_shape[7] * itemsize; + int64_t copy_start[7] = {0}; + do { + do { + do { + do { + do { + do { + do { + int64_t src_copy_start = 0; + int64_t dst_copy_start = 0; + for (int j = 0; j < 7; ++j) { + src_copy_start += copy_start[j] * src_strides[j]; + dst_copy_start += copy_start[j] * dst_strides[j]; + } + memcpy(&bdst[dst_copy_start * itemsize], &bsrc[src_copy_start * itemsize], copy_nbytes); + ++copy_start[6]; + } while (copy_start[6] < copy_shape[6]); + ++copy_start[5]; + copy_start[6] = 0; + } while (copy_start[5] < copy_shape[5]); + ++copy_start[4]; + copy_start[5] = 0; + } while (copy_start[4] < copy_shape[4]); + ++copy_start[3]; + copy_start[4] = 0; + } while (copy_start[3] < copy_shape[3]); + ++copy_start[2]; + copy_start[3] = 0; + } while (copy_start[2] < copy_shape[2]); + ++copy_start[1]; + copy_start[2] = 0; + } while (copy_start[1] < copy_shape[1]); + ++copy_start[0]; + copy_start[1] = 0; + } while (copy_start[0] < copy_shape[0]); +} + +void copy7dim(const uint8_t itemsize, + const int64_t *copy_shape, + const uint8_t *bsrc, const int64_t *src_strides, + uint8_t *bdst, const int64_t *dst_strides) { + int64_t copy_nbytes = copy_shape[6] * itemsize; + int64_t copy_start[6] = {0}; + do { + do { + do { + do { + do { + do { + int64_t src_copy_start = 0; + int64_t dst_copy_start = 0; + for (int j = 0; j < 6; ++j) { + src_copy_start += copy_start[j] * src_strides[j]; + dst_copy_start += copy_start[j] * dst_strides[j]; + } + memcpy(&bdst[dst_copy_start * itemsize], &bsrc[src_copy_start * itemsize], copy_nbytes); + ++copy_start[5]; + } while (copy_start[5] < copy_shape[5]); + ++copy_start[4]; + copy_start[5] = 0; + } while (copy_start[4] < copy_shape[4]); + ++copy_start[3]; + copy_start[4] = 0; + } while (copy_start[3] < copy_shape[3]); + ++copy_start[2]; + copy_start[3] = 0; + } while (copy_start[2] < copy_shape[2]); + ++copy_start[1]; + copy_start[2] = 0; + } while (copy_start[1] < copy_shape[1]); + ++copy_start[0]; + copy_start[1] = 0; + } while (copy_start[0] < copy_shape[0]); +} + +void copy6dim(const uint8_t itemsize, + const int64_t *copy_shape, + const uint8_t *bsrc, const int64_t *src_strides, + uint8_t *bdst, const int64_t *dst_strides) { + int64_t copy_nbytes = copy_shape[5] * itemsize; + int64_t copy_start[5] = {0}; + do { + do { + do { + do { + do { + int64_t src_copy_start = 0; + int64_t dst_copy_start = 0; + for (int j = 0; j < 5; ++j) { + src_copy_start += copy_start[j] * src_strides[j]; + dst_copy_start += copy_start[j] * dst_strides[j]; + } + memcpy(&bdst[dst_copy_start * itemsize], &bsrc[src_copy_start * itemsize], copy_nbytes); + ++copy_start[4]; + } while (copy_start[4] < copy_shape[4]); + ++copy_start[3]; + copy_start[4] = 0; + } while (copy_start[3] < copy_shape[3]); + ++copy_start[2]; + copy_start[3] = 0; + } while (copy_start[2] < copy_shape[2]); + ++copy_start[1]; + copy_start[2] = 0; + } while (copy_start[1] < copy_shape[1]); + ++copy_start[0]; + copy_start[1] = 0; + } while (copy_start[0] < copy_shape[0]); +} + +void copy5dim(const uint8_t itemsize, + const int64_t *copy_shape, + const uint8_t *bsrc, const int64_t *src_strides, + uint8_t *bdst, const int64_t *dst_strides) { + int64_t copy_nbytes = copy_shape[4] * itemsize; + int64_t copy_start[4] = {0}; + do { + do { + do { + do { + int64_t src_copy_start = 0; + int64_t dst_copy_start = 0; + for (int j = 0; j < 4; ++j) { + src_copy_start += copy_start[j] * src_strides[j]; + dst_copy_start += copy_start[j] * dst_strides[j]; + } + memcpy(&bdst[dst_copy_start * itemsize], &bsrc[src_copy_start * itemsize], copy_nbytes); + ++copy_start[3]; + } while (copy_start[3] < copy_shape[3]); + ++copy_start[2]; + copy_start[3] = 0; + } while (copy_start[2] < copy_shape[2]); + ++copy_start[1]; + copy_start[2] = 0; + } while (copy_start[1] < copy_shape[1]); + ++copy_start[0]; + copy_start[1] = 0; + } while (copy_start[0] < copy_shape[0]); +} + +void copy4dim(const uint8_t itemsize, + const int64_t *copy_shape, + const uint8_t *bsrc, const int64_t *src_strides, + uint8_t *bdst, const int64_t *dst_strides) { + int64_t copy_nbytes = copy_shape[3] * itemsize; + int64_t copy_start[3] = {0}; + do { + do { + do { + int64_t src_copy_start = 0; + int64_t dst_copy_start = 0; + for (int j = 0; j < 3; ++j) { + src_copy_start += copy_start[j] * src_strides[j]; + dst_copy_start += copy_start[j] * dst_strides[j]; + } + memcpy(&bdst[dst_copy_start * itemsize], &bsrc[src_copy_start * itemsize], copy_nbytes); + ++copy_start[2]; + } while (copy_start[2] < copy_shape[2]); + ++copy_start[1]; + copy_start[2] = 0; + } while (copy_start[1] < copy_shape[1]); + ++copy_start[0]; + copy_start[1] = 0; + } while (copy_start[0] < copy_shape[0]); +} + +void copy3dim(const uint8_t itemsize, + const int64_t *copy_shape, + const uint8_t *bsrc, const int64_t *src_strides, + uint8_t *bdst, const int64_t *dst_strides) { + int64_t copy_nbytes = copy_shape[2] * itemsize; + int64_t copy_start[2] = {0}; + do { + do { + int64_t src_copy_start = 0; + int64_t dst_copy_start = 0; + for (int j = 0; j < 2; ++j) { + src_copy_start += copy_start[j] * src_strides[j]; + dst_copy_start += copy_start[j] * dst_strides[j]; + } + memcpy(&bdst[dst_copy_start * itemsize], &bsrc[src_copy_start * itemsize], copy_nbytes); + ++copy_start[1]; + } while (copy_start[1] < copy_shape[1]); + ++copy_start[0]; + copy_start[1] = 0; + } while (copy_start[0] < copy_shape[0]); +} + +void copy2dim(const uint8_t itemsize, + const int64_t *copy_shape, + const uint8_t *bsrc, const int64_t *src_strides, + uint8_t *bdst, const int64_t *dst_strides) { + int64_t copy_nbytes = copy_shape[1] * itemsize; + int64_t copy_start = 0; + do { + int64_t src_copy_start = copy_start * src_strides[0]; + int64_t dst_copy_start = copy_start * dst_strides[0]; + memcpy(&bdst[dst_copy_start * itemsize], &bsrc[src_copy_start * itemsize], copy_nbytes); + ++copy_start; + } while (copy_start < copy_shape[0]); +} + + +void copy_ndim_fallback(const int8_t ndim, + const uint8_t itemsize, + int64_t *copy_shape, + const uint8_t *bsrc, int64_t *src_strides, + uint8_t *bdst, int64_t *dst_strides) { + int64_t copy_nbytes = copy_shape[ndim - 1] * itemsize; + int64_t number_of_copies = 1; + for (int i = 0; i < ndim - 1; ++i) { + number_of_copies *= copy_shape[i]; + } + for (int ncopy = 0; ncopy < number_of_copies; ++ncopy) { + // Compute the start of the copy + int64_t copy_start[B2ND_MAX_DIM] = {0}; + blosc2_unidim_to_multidim((int8_t) (ndim - 1), copy_shape, ncopy, copy_start); + + // Translate this index to the src buffer + int64_t src_copy_start; + blosc2_multidim_to_unidim(copy_start, (int8_t) (ndim - 1), src_strides, &src_copy_start); + + // Translate this index to the dst buffer + int64_t dst_copy_start; + blosc2_multidim_to_unidim(copy_start, (int8_t) (ndim - 1), dst_strides, &dst_copy_start); + + // Perform the copy + memcpy(&bdst[dst_copy_start * itemsize], + &bsrc[src_copy_start * itemsize], + copy_nbytes); + } +} + +int b2nd_copy_buffer(int8_t ndim, + uint8_t itemsize, + void *src, const int64_t *src_pad_shape, + int64_t *src_start, const int64_t *src_stop, + void *dst, const int64_t *dst_pad_shape, + int64_t *dst_start) { + // Compute the shape of the copy + int64_t copy_shape[B2ND_MAX_DIM] = {0}; + for (int i = 0; i < ndim; ++i) { + copy_shape[i] = src_stop[i] - src_start[i]; + if (copy_shape[i] == 0) { + return BLOSC2_ERROR_SUCCESS; + } + } + + // Compute the strides + int64_t src_strides[B2ND_MAX_DIM]; + src_strides[ndim - 1] = 1; + for (int i = ndim - 2; i >= 0; --i) { + src_strides[i] = src_strides[i + 1] * src_pad_shape[i + 1]; + } + + int64_t dst_strides[B2ND_MAX_DIM]; + dst_strides[ndim - 1] = 1; + for (int i = ndim - 2; i >= 0; --i) { + dst_strides[i] = dst_strides[i + 1] * dst_pad_shape[i + 1]; + } + + // Align the buffers removing unnecessary data + int64_t src_start_n; + blosc2_multidim_to_unidim(src_start, ndim, src_strides, &src_start_n); + uint8_t *bsrc = (uint8_t *) src; + bsrc = &bsrc[src_start_n * itemsize]; + + int64_t dst_start_n; + blosc2_multidim_to_unidim(dst_start, ndim, dst_strides, &dst_start_n); + uint8_t *bdst = (uint8_t *) dst; + bdst = &bdst[dst_start_n * itemsize]; + + switch (ndim) { + case 1: + memcpy(&bdst[0], &bsrc[0], copy_shape[0] * itemsize); + break; + case 2: + copy2dim(itemsize, copy_shape, bsrc, src_strides, bdst, dst_strides); + break; + case 3: + copy3dim(itemsize, copy_shape, bsrc, src_strides, bdst, dst_strides); + break; + case 4: + copy4dim(itemsize, copy_shape, bsrc, src_strides, bdst, dst_strides); + break; + case 5: + copy5dim(itemsize, copy_shape, bsrc, src_strides, bdst, dst_strides); + break; + case 6: + copy6dim(itemsize, copy_shape, bsrc, src_strides, bdst, dst_strides); + break; + case 7: + copy7dim(itemsize, copy_shape, bsrc, src_strides, bdst, dst_strides); + break; + case 8: + copy8dim(itemsize, copy_shape, bsrc, src_strides, bdst, dst_strides); + break; + default: + // guard against potential future increase to B2ND_MAX_DIM + copy_ndim_fallback(ndim, itemsize, copy_shape, bsrc, src_strides, bdst, dst_strides); + break; + } + + return BLOSC2_ERROR_SUCCESS; +} diff --git a/blosc/b2nd_utils.h b/blosc/b2nd_utils.h new file mode 100644 index 00000000..2ba021af --- /dev/null +++ b/blosc/b2nd_utils.h @@ -0,0 +1,33 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +#ifndef B2ND_B2ND_UTILS_H_ +#define B2ND_B2ND_UTILS_H_ + +#include +#include <../plugins/plugin_utils.h> + +#ifdef __cplusplus +extern "C" { +#endif + + +int b2nd_copy_buffer(int8_t ndim, + uint8_t itemsize, + void *src, const int64_t *src_pad_shape, + int64_t *src_start, const int64_t *src_stop, + void *dst, const int64_t *dst_pad_shape, + int64_t *dst_start); + +#ifdef __cplusplus +} +#endif + +#endif // B2ND_B2ND_UTILS_H_ diff --git a/blosc/bitshuffle-altivec.c b/blosc/bitshuffle-altivec.c index ce314da9..7042e0ed 100644 --- a/blosc/bitshuffle-altivec.c +++ b/blosc/bitshuffle-altivec.c @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -12,7 +12,7 @@ Bitshuffle - Filter for improving compression of typed binary data. Author: Kiyoshi Masui - Website: http://www.github.com/kiyo-masui/bitshuffle + Website: https://github.com/kiyo-masui/bitshuffle Note: Adapted for c-blosc by Francesc Alted Altivec/VSX version by Jerome Kieffer. @@ -22,8 +22,8 @@ **********************************************************************/ -#include "bitshuffle-generic.h" #include "bitshuffle-altivec.h" +#include "bitshuffle-generic.h" /* Make sure ALTIVEC is available for the compilation target and compiler. */ #if defined(__ALTIVEC__) diff --git a/blosc/bitshuffle-altivec.h b/blosc/bitshuffle-altivec.h index 7416c7a8..000fab21 100644 --- a/blosc/bitshuffle-altivec.h +++ b/blosc/bitshuffle-altivec.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/blosc/bitshuffle-avx2.c b/blosc/bitshuffle-avx2.c index 77579dfa..c2014544 100644 --- a/blosc/bitshuffle-avx2.c +++ b/blosc/bitshuffle-avx2.c @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -12,7 +12,7 @@ Bitshuffle - Filter for improving compression of typed binary data. Author: Kiyoshi Masui - Website: http://www.github.com/kiyo-masui/bitshuffle + Website: https://github.com/kiyo-masui/bitshuffle Note: Adapted for c-blosc by Francesc Alted. @@ -21,10 +21,9 @@ **********************************************************************/ -#include "bitshuffle-generic.h" -#include "bitshuffle-sse2.h" #include "bitshuffle-avx2.h" - +#include "bitshuffle-sse2.h" +#include "bitshuffle-generic.h" /* Make sure AVX2 is available for the compilation target and compiler. */ #if defined(__AVX2__) diff --git a/blosc/bitshuffle-avx2.h b/blosc/bitshuffle-avx2.h index 5d9d51bc..e68649e7 100644 --- a/blosc/bitshuffle-avx2.h +++ b/blosc/bitshuffle-avx2.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/blosc/bitshuffle-generic.c b/blosc/bitshuffle-generic.c index 1840045c..ba62c66f 100644 --- a/blosc/bitshuffle-generic.c +++ b/blosc/bitshuffle-generic.c @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/blosc/bitshuffle-generic.h b/blosc/bitshuffle-generic.h index 49b1be3c..88c8388d 100644 --- a/blosc/bitshuffle-generic.h +++ b/blosc/bitshuffle-generic.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/blosc/bitshuffle-neon.c b/blosc/bitshuffle-neon.c index 92477aac..adc7eec6 100644 --- a/blosc/bitshuffle-neon.c +++ b/blosc/bitshuffle-neon.c @@ -1,15 +1,15 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ -#include "bitshuffle-generic.h" #include "bitshuffle-neon.h" +#include "bitshuffle-generic.h" /* Make sure NEON is available for the compilation target and compiler. */ #if defined(__ARM_NEON) diff --git a/blosc/bitshuffle-neon.h b/blosc/bitshuffle-neon.h index 4b08e99f..b8325c1f 100644 --- a/blosc/bitshuffle-neon.h +++ b/blosc/bitshuffle-neon.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/blosc/bitshuffle-sse2.c b/blosc/bitshuffle-sse2.c index 217fa18e..8314ea22 100644 --- a/blosc/bitshuffle-sse2.c +++ b/blosc/bitshuffle-sse2.c @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -12,7 +12,7 @@ Bitshuffle - Filter for improving compression of typed binary data. Author: Kiyoshi Masui - Website: http://www.github.com/kiyo-masui/bitshuffle + Website: https://github.com/kiyo-masui/bitshuffle Note: Adapted for c-blosc by Francesc Alted. @@ -21,8 +21,8 @@ **********************************************************************/ -#include "bitshuffle-generic.h" #include "bitshuffle-sse2.h" +#include "bitshuffle-generic.h" /* Make sure SSE2 is available for the compilation target and compiler. */ #if defined(__SSE2__) diff --git a/blosc/bitshuffle-sse2.h b/blosc/bitshuffle-sse2.h index 7e907989..d3d53049 100644 --- a/blosc/bitshuffle-sse2.h +++ b/blosc/bitshuffle-sse2.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/blosc/blosc-private.h b/blosc/blosc-private.h index 73e21c39..1fba14a3 100644 --- a/blosc/blosc-private.h +++ b/blosc/blosc-private.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -31,14 +31,6 @@ extern "C" { #define to_big(dest, src, itemsize) endian_handler(false, dest, src, itemsize) #define from_big(dest, src, itemsize) endian_handler(false, dest, src, itemsize) -#define BLOSC_ERROR_NULL(pointer, rc) \ - do { \ - if ((pointer) == NULL) { \ - BLOSC_TRACE_ERROR("Pointer is NULL"); \ - return rc; \ - } \ - } while (0) - // Return true if platform is little endian; else false static bool is_little_endian(void) { @@ -175,6 +167,20 @@ int register_filter_private(blosc2_filter *filter); */ int register_codec_private(blosc2_codec *codec); + +/** + * @brief Register a tune in Blosc. + * + * @param tune The tune to register. + * + * @return 0 if succeeds. Else a negative code is returned. + */ +int register_tuner_private(blosc2_tuner *tuner); + +int fill_tuner(blosc2_tuner *tuner); + +extern blosc2_tuner g_tuners[256]; +extern int g_ntuners; #ifdef __cplusplus } #endif diff --git a/blosc/blosc2-stdio.c b/blosc/blosc2-stdio.c index 23f34af1..b5227db3 100644 --- a/blosc/blosc2-stdio.c +++ b/blosc/blosc2-stdio.c @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -32,7 +32,7 @@ int blosc2_stdio_close(void *stream) { int64_t blosc2_stdio_tell(void *stream) { blosc2_stdio_file *my_fp = (blosc2_stdio_file *) stream; int64_t pos; -#if defined(_MSC_VER) && (_MSC_VER >= 1400) +#if defined(_MSC_VER) pos = _ftelli64(my_fp->file); #else pos = (int64_t)ftell(my_fp->file); @@ -43,7 +43,7 @@ int64_t blosc2_stdio_tell(void *stream) { int blosc2_stdio_seek(void *stream, int64_t offset, int whence) { blosc2_stdio_file *my_fp = (blosc2_stdio_file *) stream; int rc; -#if defined(_MSC_VER) && (_MSC_VER >= 1400) +#if defined(_MSC_VER) rc = _fseeki64(my_fp->file, offset, whence); #else rc = fseek(my_fp->file, (long) offset, whence); @@ -67,7 +67,7 @@ int64_t blosc2_stdio_read(void *ptr, int64_t size, int64_t nitems, void *stream) int blosc2_stdio_truncate(void *stream, int64_t size) { blosc2_stdio_file *my_fp = (blosc2_stdio_file *) stream; int rc; -#if defined(_MSC_VER) && (_MSC_VER >= 1400) +#if defined(_MSC_VER) rc = _chsize_s(_fileno(my_fp->file), size); #else rc = ftruncate(fileno(my_fp->file), size); diff --git a/blosc/blosc2.c b/blosc/blosc2.c index 4525dd22..785ee171 100644 --- a/blosc/blosc2.c +++ b/blosc/blosc2.c @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -9,20 +9,12 @@ **********************************************************************/ -#include -#include -#include -#include -#include -#include -#include - #include "blosc2.h" #include "blosc-private.h" #include "../plugins/codecs/zfp/blosc2-zfp.h" +#include "blosc2/plugins-utils.h" #include "frame.h" - #if defined(USING_CMAKE) #include "config.h" #endif /* USING_CMAKE */ @@ -35,6 +27,7 @@ #include "stune.h" #include "blosc2/codecs-registry.h" #include "blosc2/filters-registry.h" +#include "blosc2/tuners-registry.h" #include "lz4.h" #include "lz4hc.h" @@ -58,7 +51,6 @@ #include "zdict.h" #endif /* HAVE_ZSTD */ - #if defined(_WIN32) && !defined(__MINGW32__) #include #include @@ -70,6 +62,15 @@ #include "win32/pthread.c" #endif +#include +#include +#include +#include +#include +#include +#include + + /* Synchronization variables */ /* Global context for non-contextual API */ @@ -91,9 +92,13 @@ uint8_t g_ncodecs = 0; static blosc2_filter g_filters[256] = {0}; static uint64_t g_nfilters = 0; -static blosc2_io_cb g_io[256] = {0}; +static blosc2_io_cb g_ios[256] = {0}; static uint64_t g_nio = 0; +blosc2_tuner g_tuners[256] = {0}; +int g_ntuners = 0; + +static int g_tuner = BLOSC_STUNE; // Forward declarations int init_threadpool(blosc2_context *context); @@ -150,6 +155,7 @@ int release_threadpool(blosc2_context *context); static blosc_threads_callback threads_callback = 0; static void *threads_callback_data = 0; + /* non-threadsafe function should be called before any other Blosc function in order to change how threads are managed */ void blosc2_set_threads_callback(blosc_threads_callback callback, void *callback_data) @@ -335,7 +341,7 @@ static int compcode_to_compformat(int compcode) { default: return BLOSC_UDCODEC_FORMAT; } - return -1; + BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } @@ -343,27 +349,32 @@ static int compcode_to_compformat(int compcode) { static int compcode_to_compversion(int compcode) { /* Write compressor format */ switch (compcode) { - case BLOSC_BLOSCLZ: return BLOSC_BLOSCLZ_VERSION_FORMAT; - case BLOSC_LZ4: return BLOSC_LZ4_VERSION_FORMAT; - case BLOSC_LZ4HC: return BLOSC_LZ4HC_VERSION_FORMAT; + case BLOSC_BLOSCLZ: + return BLOSC_BLOSCLZ_VERSION_FORMAT; + case BLOSC_LZ4: + return BLOSC_LZ4_VERSION_FORMAT; + case BLOSC_LZ4HC: + return BLOSC_LZ4HC_VERSION_FORMAT; #if defined(HAVE_ZLIB) - case BLOSC_ZLIB: return BLOSC_ZLIB_VERSION_FORMAT; + case BLOSC_ZLIB: + return BLOSC_ZLIB_VERSION_FORMAT; break; #endif /* HAVE_ZLIB */ #if defined(HAVE_ZSTD) - case BLOSC_ZSTD: return BLOSC_ZSTD_VERSION_FORMAT; + case BLOSC_ZSTD: + return BLOSC_ZSTD_VERSION_FORMAT; break; #endif /* HAVE_ZSTD */ default: for (int i = 0; i < g_ncodecs; ++i) { if (compcode == g_codecs[i].compcode) { - return g_codecs[i].compver; + return g_codecs[i].version; } } } - return -1; + return BLOSC2_ERROR_FAILURE; } @@ -495,9 +506,7 @@ static int zstd_wrap_compress(struct thread_context* thread_context, (void*)output, maxout, (void*)input, input_length, clevel); } if (ZSTD_isError(code) != ZSTD_error_no_error) { - // Do not print anything because blosc will just memcpy this buffer - // fprintf(stderr, "Error in ZSTD compression: '%s'. Giving up.\n", - // ZDICT_getErrorName(code)); + // Blosc will just memcpy this buffer return 0; } return (int)code; @@ -781,6 +790,76 @@ static int blosc2_initialize_context_from_header(blosc2_context* context, blosc_ } +int fill_filter(blosc2_filter *filter) { + char libpath[PATH_MAX]; + void *lib = load_lib(filter->name, libpath); + if(lib == NULL) { + BLOSC_TRACE_ERROR("Error while loading the library"); + return BLOSC2_ERROR_FAILURE; + } + + filter_info *info = dlsym(lib, "info"); + filter->forward = dlsym(lib, info->forward); + filter->backward = dlsym(lib, info->backward); + + if (filter->forward == NULL || filter->backward == NULL){ + BLOSC_TRACE_ERROR("Wrong library loaded"); + dlclose(lib); + return BLOSC2_ERROR_FAILURE; + } + + return BLOSC2_ERROR_SUCCESS; +} + + +int fill_codec(blosc2_codec *codec) { + char libpath[PATH_MAX]; + void *lib = load_lib(codec->compname, libpath); + if(lib == NULL) { + BLOSC_TRACE_ERROR("Error while loading the library"); + return BLOSC2_ERROR_FAILURE; + } + + codec_info *info = dlsym(lib, "info"); + codec->encoder = dlsym(lib, info->encoder); + codec->decoder = dlsym(lib, info->decoder); + + if (codec->encoder == NULL || codec->decoder == NULL){ + BLOSC_TRACE_ERROR("Wrong library loaded"); + dlclose(lib); + return BLOSC2_ERROR_FAILURE; + } + + return BLOSC2_ERROR_SUCCESS; +} + + +int fill_tuner(blosc2_tuner *tuner) { + char libpath[PATH_MAX] = {0}; + void *lib = load_lib(tuner->name, libpath); + if(lib == NULL) { + BLOSC_TRACE_ERROR("Error while loading the library"); + return BLOSC2_ERROR_FAILURE; + } + + tuner_info *info = dlsym(lib, "info"); + tuner->init = dlsym(lib, info->init); + tuner->update = dlsym(lib, info->update); + tuner->next_blocksize = dlsym(lib, info->next_blocksize); + tuner->free = dlsym(lib, info->free); + tuner->next_cparams = dlsym(lib, info->next_cparams); + + if (tuner->init == NULL || tuner->update == NULL || tuner->next_blocksize == NULL || tuner->free == NULL + || tuner->next_cparams == NULL){ + BLOSC_TRACE_ERROR("Wrong library loaded"); + dlclose(lib); + return BLOSC2_ERROR_FAILURE; + } + + return BLOSC2_ERROR_SUCCESS; +} + + static int blosc2_intialize_header_from_context(blosc2_context* context, blosc_header* header, bool extended_header) { memset(header, 0, sizeof(blosc_header)); @@ -908,6 +987,13 @@ uint8_t* pipeline_forward(struct thread_context* thread_context, const int32_t b // Look for the filters_meta in user filters and run it for (uint64_t j = 0; j < g_nfilters; ++j) { if (g_filters[j].id == filters[i]) { + if (g_filters[j].forward == NULL) { + // Dynamically load library + if (fill_filter(&g_filters[j]) < 0) { + BLOSC_TRACE_ERROR("Could not load filter %d \n", g_filters[j].id); + return NULL; + } + } if (g_filters[j].forward != NULL) { blosc2_cparams cparams; blosc2_ctx_get_cparams(context, &cparams); @@ -1139,6 +1225,13 @@ static int blosc_c(struct thread_context* thread_context, int32_t bsize, else if (context->compcode > BLOSC2_DEFINED_CODECS_STOP) { for (int i = 0; i < g_ncodecs; ++i) { if (g_codecs[i].compcode == context->compcode) { + if (g_codecs[i].encoder == NULL) { + // Dynamically load codec plugin + if (fill_codec(&g_codecs[i]) < 0) { + BLOSC_TRACE_ERROR("Could not load codec %d.", g_codecs[i].compcode); + return BLOSC2_ERROR_CODEC_SUPPORT; + } + } blosc2_cparams cparams; blosc2_ctx_get_cparams(context, &cparams); cbytes = g_codecs[i].encoder(_src + j * neblock, @@ -1213,7 +1306,6 @@ static int blosc_c(struct thread_context* thread_context, int32_t bsize, ctbytes += cbytes; } /* Closes j < nstreams */ - //printf("c%d", ctbytes); return ctbytes; } @@ -1296,6 +1388,13 @@ int pipeline_backward(struct thread_context* thread_context, const int32_t bsize // Look for the filters_meta in user filters and run it for (uint64_t j = 0; j < g_nfilters; ++j) { if (g_filters[j].id == filters[i]) { + if (g_filters[j].backward == NULL) { + // Dynamically load filter + if (fill_filter(&g_filters[j]) < 0) { + BLOSC_TRACE_ERROR("Could not load filter %d.", g_filters[j].id); + return BLOSC2_ERROR_FILTER_PIPELINE; + } + } if (g_filters[j].backward != NULL) { blosc2_dparams dparams; blosc2_ctx_get_dparams(context, &dparams); @@ -1355,9 +1454,9 @@ int pipeline_backward(struct thread_context* thread_context, const int32_t bsize static int32_t set_nans(int32_t typesize, uint8_t* dest, int32_t destsize) { - // destsize can only be a multiple of typesize if (destsize % typesize != 0) { - return -1; + BLOSC_TRACE_ERROR("destsize can only be a multiple of typesize"); + BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } int32_t nitems = destsize / typesize; if (nitems == 0) { @@ -1398,7 +1497,7 @@ static int32_t set_values(int32_t typesize, const uint8_t* src, uint8_t* dest, i int8_t* dest1; if (destsize % typesize != 0) { - return -1; + BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } int32_t nitems = destsize / typesize; if (nitems == 0) { @@ -1460,7 +1559,7 @@ static int blosc_d( int32_t neblock; int32_t nbytes; /* number of decompressed bytes in split */ int32_t cbytes; /* number of compressed bytes in split */ - int32_t ctbytes = 0; /* number of compressed bytes in block */ + // int32_t ctbytes = 0; /* number of compressed bytes in block */ int32_t ntbytes = 0; /* number of uncompressed bytes in block */ uint8_t* _dest; int32_t typesize = context->typesize; @@ -1654,7 +1753,7 @@ static int blosc_d( neblock = bsize / nstreams; if (neblock == 0) { /* Not enough space to output bytes */ - return -1; + BLOSC_ERROR(BLOSC2_ERROR_WRITE_BUFFER); } for (int j = 0; j < nstreams; j++) { if (srcsize < (signed)sizeof(int32_t)) { @@ -1671,7 +1770,7 @@ static int blosc_d( srcsize -= cbytes; } src += sizeof(int32_t); - ctbytes += (signed)sizeof(int32_t); + // ctbytes += (signed)sizeof(int32_t); /* Uncompress */ if (cbytes == 0) { @@ -1691,7 +1790,7 @@ static int blosc_d( token = src[0]; src += 1; - ctbytes += 1; + // ctbytes += 1; if (token & 0x1) { // A run of bytes that are different than 0 @@ -1752,6 +1851,13 @@ static int blosc_d( thread_context->zfp_cell_nitems = 0; for (int i = 0; i < g_ncodecs; ++i) { if (g_codecs[i].compcode == context->compcode) { + if (g_codecs[i].decoder == NULL) { + // Dynamically load codec plugin + if (fill_codec(&g_codecs[i]) < 0) { + BLOSC_TRACE_ERROR("Could not load codec %d.", g_codecs[i].compcode); + return BLOSC2_ERROR_CODEC_SUPPORT; + } + } blosc2_dparams dparams; blosc2_ctx_get_dparams(context, &dparams); nbytes = g_codecs[i].decoder(src, @@ -1786,7 +1892,7 @@ static int blosc_d( } src += cbytes; - ctbytes += cbytes; + // ctbytes += cbytes; _dest += nbytes; ntbytes += nbytes; } /* Closes j < nstreams */ @@ -2033,13 +2139,13 @@ static int do_job(blosc2_context* context) { static int initialize_context_compression( - blosc2_context* context, const void* src, int32_t srcsize, void* dest, - int32_t destsize, int clevel, uint8_t const *filters, - uint8_t const *filters_meta, int32_t typesize, int compressor, - int32_t blocksize, int16_t new_nthreads, int16_t nthreads, - int32_t splitmode, - blosc2_btune *udbtune, void *btune_config, - blosc2_schunk* schunk) { + blosc2_context* context, const void* src, int32_t srcsize, void* dest, + int32_t destsize, int clevel, uint8_t const *filters, + uint8_t const *filters_meta, int32_t typesize, int compressor, + int32_t blocksize, int16_t new_nthreads, int16_t nthreads, + int32_t splitmode, + int tuner_id, void *tuner_params, + blosc2_schunk* schunk) { /* Set parameters */ context->do_compress = 1; @@ -2061,38 +2167,67 @@ static int initialize_context_compression( context->end_threads = 0; context->clevel = clevel; context->schunk = schunk; - context->btune = btune_config; - context->udbtune = udbtune; + context->tuner_params = tuner_params; + context->tuner_id = tuner_id; context->splitmode = splitmode; - /* Tune some compression parameters */ + /* tuner some compression parameters */ context->blocksize = (int32_t)blocksize; - if (context->btune != NULL) { - context->udbtune->btune_next_cparams(context); + if (context->tuner_params != NULL) { + if (context->tuner_id < BLOSC_LAST_TUNER && context->tuner_id == BLOSC_STUNE) { + blosc_stune_next_cparams(context); + } else { + for (int i = 0; i < g_ntuners; ++i) { + if (g_tuners[i].id == context->tuner_id) { + if (g_tuners[i].next_cparams == NULL) { + if (fill_tuner(&g_tuners[i]) < 0) { + BLOSC_TRACE_ERROR("Could not load tuner %d.", g_tuners[i].id); + return BLOSC2_ERROR_FAILURE; + } + } + g_tuners[i].next_cparams(context); + if (g_tuners[i].id == BLOSC_BTUNE && context->blocksize == 0) { + // Call stune for initializing blocksize + blosc_stune_next_blocksize(context); + } + goto urtunersuccess; + } + } + BLOSC_TRACE_ERROR("User-defined tuner %d not found\n", context->tuner_id); + return BLOSC2_ERROR_INVALID_PARAM; + } } else { - context->udbtune->btune_next_blocksize(context); - } - - char* envvar = getenv("BLOSC_WARN"); - int64_t warnlvl = 0; - if (envvar != NULL) { - warnlvl = strtol(envvar, NULL, 10); + if (context->tuner_id < BLOSC_LAST_TUNER && context->tuner_id == BLOSC_STUNE) { + blosc_stune_next_blocksize(context); + } else { + for (int i = 0; i < g_ntuners; ++i) { + if (g_tuners[i].id == context->tuner_id) { + if (g_tuners[i].next_blocksize == NULL) { + if (fill_tuner(&g_tuners[i]) < 0) { + BLOSC_TRACE_ERROR("Could not load tuner %d.", g_tuners[i].id); + return BLOSC2_ERROR_FAILURE; + } + } + g_tuners[i].next_blocksize(context); + goto urtunersuccess; + } + } + BLOSC_TRACE_ERROR("User-defined tuner %d not found\n", context->tuner_id); + return BLOSC2_ERROR_INVALID_PARAM; + } } + urtunersuccess:; /* Check buffer size limits */ if (srcsize > BLOSC2_MAX_BUFFERSIZE) { - if (warnlvl > 0) { - BLOSC_TRACE_ERROR("Input buffer size cannot exceed %d bytes.", - BLOSC2_MAX_BUFFERSIZE); - } - return 0; + BLOSC_TRACE_ERROR("Input buffer size cannot exceed %d bytes.", + BLOSC2_MAX_BUFFERSIZE); + return BLOSC2_ERROR_MAX_BUFSIZE_EXCEEDED; } if (destsize < BLOSC2_MAX_OVERHEAD) { - if (warnlvl > 0) { - BLOSC_TRACE_ERROR("Output buffer size should be larger than %d bytes.", - BLOSC2_MAX_OVERHEAD); - } - return 0; + BLOSC_TRACE_ERROR("Output buffer size should be larger than %d bytes.", + BLOSC2_MAX_OVERHEAD); + return BLOSC2_ERROR_MAX_BUFSIZE_EXCEEDED; } /* Compression level */ @@ -2356,13 +2491,31 @@ static int blosc_compress_context(blosc2_context* context) { _sw32(context->dest + BLOSC2_CHUNK_BLOCKSIZE, blocksize); } - /* Set the number of bytes in dest buffer (might be useful for btune) */ + /* Set the number of bytes in dest buffer (might be useful for tuner) */ context->destsize = ntbytes; - if (context->btune != NULL) { + if (context->tuner_params != NULL) { blosc_set_timestamp(¤t); double ctime = blosc_elapsed_secs(last, current); - context->udbtune->btune_update(context, ctime); + if (context->tuner_id < BLOSC_LAST_TUNER && context->tuner_id == BLOSC_STUNE) { + blosc_stune_update(context, ctime); + } else { + for (int i = 0; i < g_ntuners; ++i) { + if (g_tuners[i].id == context->tuner_id) { + if (g_tuners[i].update == NULL) { + if (fill_tuner(&g_tuners[i]) < 0) { + BLOSC_TRACE_ERROR("Could not load tuner %d.", g_tuners[i].id); + return BLOSC2_ERROR_FAILURE; + } + } + g_tuners[i].update(context, ctime); + goto urtunersuccess; + } + } + BLOSC_TRACE_ERROR("User-defined tuner %d not found\n", context->tuner_id); + return BLOSC2_ERROR_INVALID_PARAM; + urtunersuccess:; + } } return ntbytes; @@ -2380,11 +2533,11 @@ int blosc2_compress_ctx(blosc2_context* context, const void* src, int32_t srcsiz } error = initialize_context_compression( - context, src, srcsize, dest, destsize, - context->clevel, context->filters, context->filters_meta, - context->typesize, context->compcode, context->blocksize, - context->new_nthreads, context->nthreads, context->splitmode, - context->udbtune, context->btune, context->schunk); + context, src, srcsize, dest, destsize, + context->clevel, context->filters, context->filters_meta, + context->typesize, context->compcode, context->blocksize, + context->new_nthreads, context->nthreads, context->splitmode, + context->tuner_id, context->tuner_params, context->schunk); if (error <= 0) { return error; } @@ -2656,9 +2809,9 @@ int blosc2_compress(int clevel, int doshuffle, int32_t typesize, BLOSC_ERROR_NULL(filters_meta, BLOSC2_ERROR_MEMORY_ALLOC); build_filters(doshuffle, g_delta, typesize, filters); error = initialize_context_compression( - g_global_context, src, srcsize, dest, destsize, clevel, filters, - filters_meta, (int32_t)typesize, g_compressor, g_force_blocksize, g_nthreads, g_nthreads, - g_splitmode, &BTUNE_DEFAULTS, NULL, g_schunk); + g_global_context, src, srcsize, dest, destsize, clevel, filters, + filters_meta, (int32_t)typesize, g_compressor, g_force_blocksize, g_nthreads, g_nthreads, + g_splitmode, g_tuner, NULL, g_schunk); free(filters); free(filters_meta); if (error <= 0) { @@ -2883,7 +3036,7 @@ int _blosc_getitem(blosc2_context* context, blosc_header* header, const void* sr break; default: BLOSC_TRACE_ERROR("Unhandled special value case"); - return -1; + BLOSC_ERROR(BLOSC2_ERROR_SCHUNK_SPECIAL); } return ntbytes; } @@ -2943,7 +3096,7 @@ int _blosc_getitem(blosc2_context* context, blosc_header* header, const void* sr // If memcpyed we don't have a bstarts section (because it is not needed) int32_t src_offset = memcpyed ? - context->header_overhead + j * bsize : sw32_(context->bstarts + j); + context->header_overhead + j * header->blocksize : sw32_(context->bstarts + j); int32_t cbytes = blosc_d(context->serial_context, bsize, leftoverblock, memcpyed, src, srcsize, src_offset, j, @@ -3354,7 +3507,7 @@ int blosc1_set_compressor(const char* compname) { int code = blosc2_compname_to_compcode(compname); if (code >= BLOSC_LAST_CODEC) { BLOSC_TRACE_ERROR("User defined codecs cannot be set here. Use Blosc2 mechanism instead."); - return -1; + BLOSC_ERROR(BLOSC2_ERROR_CODEC_SUPPORT); } g_compressor = code; @@ -3580,19 +3733,32 @@ void blosc_set_schunk(blosc2_schunk* schunk) { } blosc2_io *blosc2_io_global = NULL; +blosc2_io_cb BLOSC2_IO_CB_DEFAULTS; void blosc2_init(void) { /* Return if Blosc is already initialized */ if (g_initlib) return; + BLOSC2_IO_CB_DEFAULTS.id = BLOSC2_IO_FILESYSTEM; + BLOSC2_IO_CB_DEFAULTS.name = "filesystem"; + BLOSC2_IO_CB_DEFAULTS.open = (blosc2_open_cb) blosc2_stdio_open; + BLOSC2_IO_CB_DEFAULTS.close = (blosc2_close_cb) blosc2_stdio_close; + BLOSC2_IO_CB_DEFAULTS.tell = (blosc2_tell_cb) blosc2_stdio_tell; + BLOSC2_IO_CB_DEFAULTS.seek = (blosc2_seek_cb) blosc2_stdio_seek; + BLOSC2_IO_CB_DEFAULTS.write = (blosc2_write_cb) blosc2_stdio_write; + BLOSC2_IO_CB_DEFAULTS.read = (blosc2_read_cb) blosc2_stdio_read; + BLOSC2_IO_CB_DEFAULTS.truncate = (blosc2_truncate_cb) blosc2_stdio_truncate; + g_ncodecs = 0; g_nfilters = 0; + g_ntuners = 0; #if defined(HAVE_PLUGINS) #include "blosc2/blosc2-common.h" #include "blosc2/blosc2-stdio.h" register_codecs(); register_filters(); + register_tuners(); #endif pthread_mutex_init(&global_comp_mutex, NULL); /* Create a global context */ @@ -3719,6 +3885,18 @@ blosc2_context* blosc2_create_cctx(blosc2_cparams cparams) { } } +#if defined(HAVE_PLUGINS) +#include "blosc2/codecs-registry.h" + if ((context->compcode >= BLOSC_CODEC_ZFP_FIXED_ACCURACY) && (context->compcode <= BLOSC_CODEC_ZFP_FIXED_RATE)) { + for (int i = 0; i < BLOSC2_MAX_FILTERS; ++i) { + if ((context->filters[i] == BLOSC_SHUFFLE) || (context->filters[i] == BLOSC_BITSHUFFLE)) { + BLOSC_TRACE_ERROR("ZFP cannot be run in presence of SHUFFLE / BITSHUFFLE"); + return NULL; + } + } + } +#endif /* HAVE_PLUGINS */ + /* Check for a BLOSC_SHUFFLE environment variable */ int doshuffle = -1; char* envvar = getenv("BLOSC_SHUFFLE"); @@ -3854,11 +4032,30 @@ blosc2_context* blosc2_create_cctx(blosc2_cparams cparams) { memcpy(context->preparams, cparams.preparams, sizeof(blosc2_prefilter_params)); } - if (cparams.udbtune == NULL) { - context->udbtune = &BTUNE_DEFAULTS; + if (cparams.tuner_id <= 0) { + cparams.tuner_id = g_tuner; } else { - context->udbtune = cparams.udbtune; + for (int i = 0; i < g_ntuners; ++i) { + if (g_tuners[i].id == cparams.tuner_id) { + if (g_tuners[i].init == NULL) { + if (fill_tuner(&g_tuners[i]) < 0) { + BLOSC_TRACE_ERROR("Could not load tuner %d.", g_tuners[i].id); + return NULL; + } + } + g_tuners[i].init(cparams.tuner_params, context, NULL); + goto urtunersuccess; + } + } + BLOSC_TRACE_ERROR("User-defined tuner %d not found\n", cparams.tuner_id); + return NULL; } + urtunersuccess:; + + context->tuner_id = cparams.tuner_id; + + context->codec_params = cparams.codec_params; + memcpy(context->filter_params, cparams.filter_params, BLOSC2_MAX_FILTERS * sizeof(void*)); return context; } @@ -3913,8 +4110,26 @@ void blosc2_free_ctx(blosc2_context* context) { ZSTD_freeDDict(context->dict_ddict); #endif } - if (context->btune != NULL) { - context->udbtune->btune_free(context); + if (context->tuner_params != NULL) { + if (context->tuner_id < BLOSC_LAST_TUNER && context->tuner_id == BLOSC_STUNE) { + blosc_stune_free(context); + } else { + for (int i = 0; i < g_ntuners; ++i) { + if (g_tuners[i].id == context->tuner_id) { + if (g_tuners[i].free == NULL) { + if (fill_tuner(&g_tuners[i]) < 0) { + BLOSC_TRACE_ERROR("Could not load tuner %d.", g_tuners[i].id); + return; + } + } + g_tuners[i].free(context); + goto urtunersuccess; + } + } + BLOSC_TRACE_ERROR("User-defined tuner %d not found\n", context->tuner_id); + return; + urtunersuccess:; + } } if (context->prefilter != NULL) { my_free(context->preparams); @@ -3947,7 +4162,8 @@ int blosc2_ctx_get_cparams(blosc2_context *ctx, blosc2_cparams *cparams) { } cparams->prefilter = ctx->prefilter; cparams->preparams = ctx->preparams; - cparams->udbtune = ctx->udbtune; + cparams->tuner_id = ctx->tuner_id; + cparams->codec_params = ctx->codec_params; return BLOSC2_ERROR_SUCCESS; } @@ -4001,7 +4217,7 @@ int blosc2_chunk_zeros(blosc2_cparams cparams, const int32_t nbytes, void* dest, context->clevel, context->filters, context->filters_meta, context->typesize, context->compcode, context->blocksize, context->new_nthreads, context->nthreads, context->splitmode, - context->udbtune, context->btune, context->schunk); + context->tuner_id, context->tuner_params, context->schunk); if (error <= 0) { blosc2_free_ctx(context); return error; @@ -4043,7 +4259,7 @@ int blosc2_chunk_uninit(blosc2_cparams cparams, const int32_t nbytes, void* dest context->clevel, context->filters, context->filters_meta, context->typesize, context->compcode, context->blocksize, context->new_nthreads, context->nthreads, context->splitmode, - context->udbtune, context->btune, context->schunk); + context->tuner_id, context->tuner_params, context->schunk); if (error <= 0) { blosc2_free_ctx(context); return error; @@ -4086,7 +4302,7 @@ int blosc2_chunk_nans(blosc2_cparams cparams, const int32_t nbytes, void* dest, context->clevel, context->filters, context->filters_meta, context->typesize, context->compcode, context->blocksize, context->new_nthreads, context->nthreads, context->splitmode, - context->udbtune, context->btune, context->schunk); + context->tuner_id, context->tuner_params, context->schunk); if (error <= 0) { blosc2_free_ctx(context); return error; @@ -4111,7 +4327,7 @@ int blosc2_chunk_nans(blosc2_cparams cparams, const int32_t nbytes, void* dest, /* Create a chunk made of repeated values */ int blosc2_chunk_repeatval(blosc2_cparams cparams, const int32_t nbytes, - void* dest, int32_t destsize, void* repeatval) { + void* dest, int32_t destsize, const void* repeatval) { uint8_t typesize = cparams.typesize; if (destsize < BLOSC_EXTENDED_HEADER_LENGTH + typesize) { BLOSC_TRACE_ERROR("dest buffer is not long enough"); @@ -4131,7 +4347,7 @@ int blosc2_chunk_repeatval(blosc2_cparams cparams, const int32_t nbytes, context->clevel, context->filters, context->filters_meta, context->typesize, context->compcode, context->blocksize, context->new_nthreads, context->nthreads, context->splitmode, - context->udbtune, context->btune, context->schunk); + context->tuner_id, context->tuner_params, context->schunk); if (error <= 0) { blosc2_free_ctx(context); return error; @@ -4174,12 +4390,18 @@ int register_filter_private(blosc2_filter *filter) { } */ - // Check if the filter is already registered for (uint64_t i = 0; i < g_nfilters; ++i) { - if (g_filters[i].id == filter->id) { - BLOSC_TRACE_ERROR("The filter is already registered!"); - return BLOSC2_ERROR_FAILURE; + if (g_filters[i].id == filter->id) { + if (strcmp(g_filters[i].name, filter->name) != 0) { + BLOSC_TRACE_ERROR("The filter (ID: %d) plugin is already registered with name: %s." + " Choose another one !", filter->id, g_filters[i].name); + return BLOSC2_ERROR_FAILURE; + } + else { + // Already registered, so no more actions needed + return BLOSC2_ERROR_SUCCESS; } + } } blosc2_filter *filter_new = &g_filters[g_nfilters++]; @@ -4204,26 +4426,32 @@ int blosc2_register_filter(blosc2_filter *filter) { int register_codec_private(blosc2_codec *codec) { BLOSC_ERROR_NULL(codec, BLOSC2_ERROR_INVALID_PARAM); if (g_ncodecs == UINT8_MAX) { - BLOSC_TRACE_ERROR("Can not register more codecs"); - return BLOSC2_ERROR_CODEC_SUPPORT; + BLOSC_TRACE_ERROR("Can not register more codecs"); + return BLOSC2_ERROR_CODEC_SUPPORT; } if (codec->compcode < BLOSC2_GLOBAL_REGISTERED_CODECS_START) { - BLOSC_TRACE_ERROR("The id must be greater or equal than %d", BLOSC2_GLOBAL_REGISTERED_CODECS_START); - return BLOSC2_ERROR_FAILURE; + BLOSC_TRACE_ERROR("The id must be greater or equal than %d", BLOSC2_GLOBAL_REGISTERED_CODECS_START); + return BLOSC2_ERROR_FAILURE; } /* This condition can never be fulfilled if (codec->compcode > BLOSC2_USER_REGISTERED_CODECS_STOP) { - BLOSC_TRACE_ERROR("The id must be lower or equal to %d", BLOSC2_USER_REGISTERED_CODECS_STOP); - return BLOSC2_ERROR_FAILURE; + BLOSC_TRACE_ERROR("The id must be less or equal to %d", BLOSC2_USER_REGISTERED_CODECS_STOP); + return BLOSC2_ERROR_FAILURE; } */ - // Check if the code is already registered for (int i = 0; i < g_ncodecs; ++i) { - if (g_codecs[i].compcode == codec->compcode) { - BLOSC_TRACE_ERROR("The codec is already registered!"); - return BLOSC2_ERROR_CODEC_PARAM; + if (g_codecs[i].compcode == codec->compcode) { + if (strcmp(g_codecs[i].compname, codec->compname) != 0) { + BLOSC_TRACE_ERROR("The codec (ID: %d) plugin is already registered with name: %s." + " Choose another one !", codec->compcode, codec->compname); + return BLOSC2_ERROR_CODEC_PARAM; + } + else { + // Already registered, so no more actions needed + return BLOSC2_ERROR_SUCCESS; } + } } blosc2_codec *codec_new = &g_codecs[g_ncodecs++]; @@ -4243,17 +4471,67 @@ int blosc2_register_codec(blosc2_codec *codec) { } +/* Register tuners */ + +int register_tuner_private(blosc2_tuner *tuner) { + BLOSC_ERROR_NULL(tuner, BLOSC2_ERROR_INVALID_PARAM); + if (g_ntuners == UINT8_MAX) { + BLOSC_TRACE_ERROR("Can not register more tuners"); + return BLOSC2_ERROR_CODEC_SUPPORT; + } + if (tuner->id < BLOSC2_GLOBAL_REGISTERED_TUNER_START) { + BLOSC_TRACE_ERROR("The id must be greater or equal than %d", BLOSC2_GLOBAL_REGISTERED_TUNER_START); + return BLOSC2_ERROR_FAILURE; + } + + for (int i = 0; i < g_ntuners; ++i) { + if (g_tuners[i].id == tuner->id) { + if (strcmp(g_tuners[i].name, tuner->name) != 0) { + BLOSC_TRACE_ERROR("The tuner (ID: %d) plugin is already registered with name: %s." + " Choose another one !", tuner->id, g_tuners[i].name); + return BLOSC2_ERROR_FAILURE; + } + else { + // Already registered, so no more actions needed + return BLOSC2_ERROR_SUCCESS; + } + } + } + + blosc2_tuner *tuner_new = &g_tuners[g_ntuners++]; + memcpy(tuner_new, tuner, sizeof(blosc2_tuner)); + + return BLOSC2_ERROR_SUCCESS; +} + + +int blosc2_register_tuner(blosc2_tuner *tuner) { + if (tuner->id < BLOSC2_USER_REGISTERED_TUNER_START) { + BLOSC_TRACE_ERROR("The id must be greater or equal to %d", BLOSC2_USER_REGISTERED_TUNER_START); + return BLOSC2_ERROR_FAILURE; + } + + return register_tuner_private(tuner); +} + + int _blosc2_register_io_cb(const blosc2_io_cb *io) { - // Check if the io is already registered for (uint64_t i = 0; i < g_nio; ++i) { - if (g_io[i].id == io->id) { - BLOSC_TRACE_ERROR("The codec is already registered!"); - return BLOSC2_ERROR_PLUGIN_IO; + if (g_ios[i].id == io->id) { + if (strcmp(g_ios[i].name, io->name) != 0) { + BLOSC_TRACE_ERROR("The IO (ID: %d) plugin is already registered with name: %s." + " Choose another one !", io->id, g_ios[i].name); + return BLOSC2_ERROR_PLUGIN_IO; + } + else { + // Already registered, so no more actions needed + return BLOSC2_ERROR_SUCCESS; + } } } - blosc2_io_cb *io_new = &g_io[g_nio++]; + blosc2_io_cb *io_new = &g_ios[g_nio++]; memcpy(io_new, io, sizeof(blosc2_io_cb)); return BLOSC2_ERROR_SUCCESS; @@ -4276,8 +4554,8 @@ int blosc2_register_io_cb(const blosc2_io_cb *io) { blosc2_io_cb *blosc2_get_io_cb(uint8_t id) { for (uint64_t i = 0; i < g_nio; ++i) { - if (g_io[i].id == id) { - return &g_io[i]; + if (g_ios[i].id == id) { + return &g_ios[i]; } } if (id == BLOSC2_IO_FILESYSTEM) { diff --git a/blosc/blosclz.c b/blosc/blosclz.c index f5be769e..283f5130 100644 --- a/blosc/blosclz.c +++ b/blosc/blosclz.c @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -14,17 +14,19 @@ **********************************************************************/ -#include -#include #include "blosclz.h" #include "fastcopy.h" #include "blosc2/blosc2-common.h" +#include +#include + /* * Give hints to the compiler for branch prediction optimization. + * This is not necessary anymore with modern CPUs. */ -#if defined(__GNUC__) && (__GNUC__ > 2) +#if 0 && defined(__GNUC__) && (__GNUC__ > 2) #define BLOSCLZ_LIKELY(c) (__builtin_expect((c), 1)) #define BLOSCLZ_UNLIKELY(c) (__builtin_expect((c), 0)) #else @@ -52,7 +54,6 @@ #endif #define HASH_LOG (14U) -#define HASH_LOG2 (12U) // This is used in LZ4 and seems to work pretty well here too #define HASH_FUNCTION(v, s, h) { \ @@ -315,11 +316,10 @@ static uint8_t* get_run_or_match(uint8_t* ip, uint8_t* ip_bound, const uint8_t* // Get a guess for the compressed size of a buffer -static double get_cratio(uint8_t* ibase, int maxlen, int minlen, int ipshift) { +static double get_cratio(uint8_t* ibase, int maxlen, int minlen, int ipshift, uint32_t htab[], int8_t hashlog) { uint8_t* ip = ibase; int32_t oc = 0; - const uint16_t hashlen = (1U << (uint8_t)HASH_LOG2); - uint16_t htab[1U << (uint8_t)HASH_LOG2]; + const uint16_t hashlen = (1U << (uint8_t)hashlog); uint32_t hval; uint32_t seq; uint8_t copy; @@ -329,7 +329,7 @@ static double get_cratio(uint8_t* ibase, int maxlen, int minlen, int ipshift) { uint8_t* ip_limit = ibase + limit - 12; // Initialize the hash table to distances of 0 - memset(htab, 0, hashlen * sizeof(uint16_t)); + memset(htab, 0, hashlen * sizeof(uint32_t)); /* we start with literal copy */ copy = 4; @@ -343,14 +343,14 @@ static double get_cratio(uint8_t* ibase, int maxlen, int minlen, int ipshift) { /* find potential match */ seq = BLOSCLZ_READU32(ip); - HASH_FUNCTION(hval, seq, HASH_LOG2) + HASH_FUNCTION(hval, seq, hashlog) ref = ibase + htab[hval]; /* calculate distance to the match */ distance = (unsigned int)(anchor - ref); /* update hash table */ - htab[hval] = (uint16_t) (anchor - ibase); + htab[hval] = (uint32_t) (anchor - ibase); if (distance == 0 || (distance >= MAX_FARDISTANCE)) { LITERAL2(ip, anchor, copy) @@ -406,8 +406,8 @@ static double get_cratio(uint8_t* ibase, int maxlen, int minlen, int ipshift) { /* update the hash at match boundary */ seq = BLOSCLZ_READU32(ip); - HASH_FUNCTION(hval, seq, HASH_LOG2) - htab[hval] = (uint16_t)(ip++ - ibase); + HASH_FUNCTION(hval, seq, hashlog) + htab[hval] = (uint32_t)(ip++ - ibase); ip++; /* assuming literal copy */ oc++; @@ -420,47 +420,51 @@ static double get_cratio(uint8_t* ibase, int maxlen, int minlen, int ipshift) { int blosclz_compress(const int clevel, const void* input, int length, void* output, int maxout, blosc2_context* ctx) { + BLOSC_UNUSED_PARAM(ctx); uint8_t* ibase = (uint8_t*)input; - - // Experiments say that checking 1/4 of the buffer is enough to figure out approx cratio - int maxlen = length / 4; - // Start probing somewhere inside the buffer - int shift = length - maxlen; - // Actual entropy probing! - double cratio = get_cratio(ibase + shift, maxlen, 3, 3); - // discard probes with small compression ratios (too expensive) - double cratio_[10] = {0, 2, 1.5, 1.2, 1.2, 1.2, 1.2, 1.15, 1.1, 1.0}; - if (cratio < cratio_[clevel]) { - goto out; - } + uint32_t htab[1U << (uint8_t)HASH_LOG]; /* When we go back in a match (shift), we obtain quite different compression properties. * It looks like 4 is more useful in combination with bitshuffle and small typesizes * Fallback to 4 because it provides more consistent results for large cratios. + * UPDATE: new experiments show that using a value of 3 is a bit better, at least for ERA5. * * In this block we also check cratios for the beginning of the buffers and * eventually discard those that are small (take too long to decompress). * This process is called _entropy probing_. */ - unsigned ipshift = 4; - // Compute optimal shift and minimum lengths for encoding - // Use 4 by default, except for low entropy data, where we should do a best effort - unsigned minlen = 4; - // BloscLZ works better with splits mostly, so when data is not split, do a best effort - const int split_block = !((ctx->header_flags & 0x10) >> 4); - // Why using cratio < 4 is based in experiments with low and high entropy - if (!split_block || cratio < 4) { - ipshift = 3; - minlen = 3; - } - else { - minlen = 4; - } + unsigned ipshift = 3; + // Minimum lengths for encoding (normally it is good to match the shift value) + unsigned minlen = 3; - uint8_t hashlog_[10] = {0, HASH_LOG - 2, HASH_LOG - 1, HASH_LOG, HASH_LOG, + uint8_t hashlog_[10] = {0, HASH_LOG - 1, HASH_LOG - 1, HASH_LOG, HASH_LOG, HASH_LOG, HASH_LOG, HASH_LOG, HASH_LOG, HASH_LOG}; uint8_t hashlog = hashlog_[clevel]; + // Experiments say that checking 1/4 of the buffer is enough to figure out approx cratio + // UPDATE: new experiments with ERA5 datasets (float32) say that checking the whole buffer + // is better (specially when combined with bitshuffle). + // The loss in speed for checking the whole buffer is pretty negligible too. + int maxlen = length; + if (clevel < 2) { + maxlen /= 8; + } + else if (clevel < 4) { + maxlen /= 4; + } + else if (clevel < 7) { + maxlen /= 2; + } + // Start probing somewhere inside the buffer + int shift = length - maxlen; + // Actual entropy probing! + double cratio = get_cratio(ibase + shift, maxlen, minlen, ipshift, htab, hashlog); + // discard probes with small compression ratios (too expensive) + double cratio_[10] = {0, 2, 1.5, 1.2, 1.2, 1.2, 1.2, 1.15, 1.1, 1.0}; + if (cratio < cratio_[clevel]) { + goto out; + } + uint8_t* ip = ibase; uint8_t* ip_bound = ibase + length - 1; uint8_t* ip_limit = ibase + length - 12; @@ -476,7 +480,6 @@ int blosclz_compress(const int clevel, const void* input, int length, } // Initialize the hash table - uint32_t htab[1U << (uint8_t)HASH_LOG]; memset(htab, 0, (1U << hashlog) * sizeof(uint32_t)); /* we start with literal copy */ @@ -569,7 +572,7 @@ int blosclz_compress(const int clevel, const void* input, int length, seq = BLOSCLZ_READU32(ip); HASH_FUNCTION(hval, seq, hashlog) htab[hval] = (uint32_t) (ip++ - ibase); - if (ctx->clevel == 9) { + if (clevel == 9) { // In some situations, including a second hash proves to be useful, // but not in others. Activating here in max clevel only. seq >>= 8U; @@ -750,16 +753,16 @@ int blosclz_decompress(const void* input, int length, void* output, int maxout) } else { // general copy with any overlap -#if defined(__AVX2__) +#if 0 && defined(__AVX2__) if (op - ref <= 16) { // This is not faster on a combination of compilers (clang, gcc, icc) or machines, but - // it is not slower either. Let's activate here for experimentation. + // it is not too slower either. op = copy_match_16(op, ref, len); } else { #endif op = copy_match(op, ref, (unsigned) len); -#if defined(__AVX2__) +#if 0 && defined(__AVX2__) } #endif } diff --git a/blosc/blosclz.h b/blosc/blosclz.h index 6adee3cd..f91633eb 100644 --- a/blosc/blosclz.h +++ b/blosc/blosclz.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -23,7 +23,7 @@ extern "C" { #endif -#define BLOSCLZ_VERSION_STRING "2.5.1" +#define BLOSCLZ_VERSION_STRING "2.5.3" /** diff --git a/blosc/context.h b/blosc/context.h index 02b5d67c..fc168511 100644 --- a/blosc/context.h +++ b/blosc/context.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -24,6 +24,7 @@ #endif #include "blosc2.h" +#include "b2nd.h" #if defined(HAVE_ZSTD) #include "zstd.h" @@ -73,8 +74,10 @@ struct blosc2_context_s { blosc2_schunk* schunk; /* Associated super-chunk (if available) */ struct thread_context* serial_context; /* Cache for temporaries for serial operation */ int do_compress; /* 1 if we are compressing, 0 if decompressing */ - void *btune; /* Entry point for BTune persistence between runs */ - blosc2_btune *udbtune; /* User-defined BTune parameters */ + void *tuner_params; /* Entry point for tuner persistence between runs */ + int tuner_id; /* User-defined tuner id */ + void *codec_params; /* User defined parameters for the codec */ + void *filter_params[BLOSC2_MAX_FILTERS]; /* User defined parameters for the filters */ /* Threading */ int16_t nthreads; int16_t new_nthreads; @@ -100,6 +103,28 @@ struct blosc2_context_s { int dref_not_init; /* data ref in delta not initialized */ pthread_mutex_t delta_mutex; pthread_cond_t delta_cv; + // Add new fields here to avoid breaking the ABI. +}; + +struct b2nd_context_s { + int8_t ndim; + //!< The array dimensions. + int64_t shape[B2ND_MAX_DIM]; + //!< The array shape. + int32_t chunkshape[B2ND_MAX_DIM]; + //!< The shape of each chunk of Blosc. + int32_t blockshape[B2ND_MAX_DIM]; + //!< The shape of each block of Blosc. + char *dtype; + //!< Data type. Different formats can be supported (see dtype_format). + int8_t dtype_format; + //!< The format of the data type. Default is 0 (NumPy). + blosc2_storage *b2_storage; + //!< The Blosc storage properties + blosc2_metalayer metalayers[B2ND_MAX_METALAYERS]; + //!< List with the metalayers desired. + int32_t nmetalayers; + //!< The number of metalayers. }; struct thread_context { diff --git a/blosc/delta.c b/blosc/delta.c index 1559075f..2d622ae2 100644 --- a/blosc/delta.c +++ b/blosc/delta.c @@ -1,16 +1,17 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ -#include #include "delta.h" +#include + /* Apply the delta filters to src. This can never fail. */ void delta_encoder(const uint8_t* dref, int32_t offset, int32_t nbytes, int32_t typesize, diff --git a/blosc/delta.h b/blosc/delta.h index 8ca26dee..082b07c5 100644 --- a/blosc/delta.h +++ b/blosc/delta.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/blosc/directories.c b/blosc/directories.c index be852c87..15f2505c 100644 --- a/blosc/directories.c +++ b/blosc/directories.c @@ -1,18 +1,19 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ -#include #include "blosc2.h" + +#include #include -#if defined(_WIN32) +#if defined(_WIN32) || defined(__MINGW32__) #include #include #include @@ -155,4 +156,3 @@ int blosc2_rename_urlpath(char* old_urlpath, char* new_urlpath){ } return BLOSC2_ERROR_SUCCESS; } - diff --git a/blosc/fastcopy.c b/blosc/fastcopy.c index 43f946cf..9ff6d229 100644 --- a/blosc/fastcopy.c +++ b/blosc/fastcopy.c @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -18,9 +18,10 @@ * Support for SSE2/AVX2 copy instructions for these routines **********************************************************************/ -#include #include "blosc2/blosc2-common.h" +#include + /* * Use inlined functions for supported systems. */ diff --git a/blosc/fastcopy.h b/blosc/fastcopy.h index 09934865..0667aa84 100644 --- a/blosc/fastcopy.h +++ b/blosc/fastcopy.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/blosc/frame.c b/blosc/frame.c index c12a1b05..1a437e89 100644 --- a/blosc/frame.c +++ b/blosc/frame.c @@ -1,36 +1,22 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ -#include -#include -#include -#include -#include -#include "blosc2.h" -#include "blosc-private.h" -#include "context.h" #include "frame.h" #include "sframe.h" -#include +#include "context.h" +#include "blosc-private.h" +#include "blosc2.h" #if defined(_WIN32) #include - #include - -/* stdint.h only available in VS2010 (VC++ 16.0) and newer */ - #if defined(_MSC_VER) && _MSC_VER < 1600 - #include "win32/stdint-windows.h" - #else - #include - #endif - +#include #endif /* _WIN32 */ /* If C11 is supported, use it's built-in aligned allocation. */ @@ -38,6 +24,14 @@ #include #endif +#include +#include +#include +#include +#include +#include +#include + /* Create a new (empty) frame */ blosc2_frame_s* frame_new(const char* urlpath) { @@ -380,18 +374,22 @@ int get_header_info(blosc2_frame_s *frame, int32_t *header_len, int64_t *frame_l int64_t rbytes = 0; void* fp = NULL; if (frame->sframe) { - fp = sframe_open_index(frame->urlpath, "rb", - io); + fp = sframe_open_index(frame->urlpath, "rb", io); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); + return BLOSC2_ERROR_FILE_OPEN; + } } else { fp = io_cb->open(frame->urlpath, "rb", io->params); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); + return BLOSC2_ERROR_FILE_OPEN; + } io_cb->seek(fp, frame->file_offset, SEEK_SET); } - if (fp != NULL) { - rbytes = io_cb->read(header, 1, FRAME_HEADER_MINLEN, fp); - io_cb->close(fp); - } - (void) rbytes; + rbytes = io_cb->read(header, 1, FRAME_HEADER_MINLEN, fp); + io_cb->close(fp); if (rbytes != FRAME_HEADER_MINLEN) { return BLOSC2_ERROR_FILE_READ; } @@ -528,6 +526,10 @@ int update_frame_len(blosc2_frame_s* frame, int64_t len) { else { fp = io_cb->open(frame->urlpath, "rb+", frame->schunk->storage->io->params); } + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); + return BLOSC2_ERROR_FILE_OPEN; + } io_cb->seek(fp, frame->file_offset + FRAME_LEN, SEEK_SET); int64_t swap_len; to_big(&swap_len, &len, sizeof(int64_t)); @@ -733,7 +735,7 @@ int frame_update_trailer(blosc2_frame_s* frame, blosc2_schunk* schunk) { fp = io_cb->open(frame->urlpath, "rb+", frame->schunk->storage->io->params); } if (fp == NULL) { - BLOSC_TRACE_ERROR("Cannot open the frame for reading and writing."); + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return BLOSC2_ERROR_FILE_OPEN; } io_cb->seek(fp, frame->file_offset + trailer_offset, SEEK_SET); @@ -818,6 +820,10 @@ blosc2_frame_s* frame_from_file_offset(const char* urlpath, const blosc2_io *io, strcpy(urlpath_cpy, urlpath); fp = io_cb->open(urlpath, "rb", io->params); } + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", urlpath); + return NULL; + } io_cb->seek(fp, offset, SEEK_SET); int64_t rbytes = io_cb->read(header, 1, FRAME_HEADER_MINLEN, fp); if (rbytes != FRAME_HEADER_MINLEN) { @@ -997,6 +1003,10 @@ int64_t frame_from_schunk(blosc2_schunk *schunk, blosc2_frame_s *frame) { else { fp = io_cb->open(frame->urlpath, "wb", frame->schunk->storage->io->params); } + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error creating file in: %s", frame->urlpath); + return BLOSC2_ERROR_FILE_OPEN; + } io_cb->write(h2, h2len, 1, fp); } free(h2); @@ -1121,10 +1131,18 @@ uint8_t* get_coffsets(blosc2_frame_s *frame, int32_t header_len, int64_t cbytes, if (frame->sframe) { fp = sframe_open_index(frame->urlpath, "rb", frame->schunk->storage->io); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); + return NULL; + } io_cb->seek(fp, header_len + 0, SEEK_SET); } else { fp = io_cb->open(frame->urlpath, "rb", frame->schunk->storage->io->params); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); + return NULL; + } io_cb->seek(fp, frame->file_offset + header_len + cbytes, SEEK_SET); } int64_t rbytes = io_cb->read(coffsets, 1, coffsets_cbytes, fp); @@ -1210,9 +1228,17 @@ int frame_update_header(blosc2_frame_s* frame, blosc2_schunk* schunk, bool new) if (frame->sframe) { fp = sframe_open_index(frame->urlpath, "rb+", frame->schunk->storage->io); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); + return BLOSC2_ERROR_FILE_OPEN; + } } else { fp = io_cb->open(frame->urlpath, "rb", frame->schunk->storage->io->params); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); + return BLOSC2_ERROR_FILE_OPEN; + } io_cb->seek(fp, frame->file_offset, SEEK_SET); } if (fp != NULL) { @@ -1255,11 +1281,13 @@ int frame_update_header(blosc2_frame_s* frame, blosc2_schunk* schunk, bool new) else { fp = io_cb->open(frame->urlpath, "rb+", frame->schunk->storage->io->params); } - if (fp != NULL) { - io_cb->seek(fp, frame->file_offset, SEEK_SET); - io_cb->write(h2, h2len, 1, fp); - io_cb->close(fp); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); + return BLOSC2_ERROR_FILE_OPEN; } + io_cb->seek(fp, frame->file_offset, SEEK_SET); + io_cb->write(h2, h2len, 1, fp); + io_cb->close(fp); } else { if (new) { @@ -1417,9 +1445,17 @@ int frame_get_metalayers(blosc2_frame_s* frame, blosc2_schunk* schunk) { if (frame->sframe) { fp = sframe_open_index(frame->urlpath, "rb", frame->schunk->storage->io); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); + return BLOSC2_ERROR_FILE_OPEN; + } } else { fp = io_cb->open(frame->urlpath, "rb", frame->schunk->storage->io->params); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); + return BLOSC2_ERROR_FILE_OPEN; + } io_cb->seek(fp, frame->file_offset, SEEK_SET); } if (fp != NULL) { @@ -1599,10 +1635,18 @@ int frame_get_vlmetalayers(blosc2_frame_s* frame, blosc2_schunk* schunk) { sprintf(eframe_name, "%s/chunks.b2frame", frame->urlpath); fp = io_cb->open(eframe_name, "rb", frame->schunk->storage->io->params); free(eframe_name); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", eframe_name); + return BLOSC2_ERROR_FILE_OPEN; + } io_cb->seek(fp, trailer_offset, SEEK_SET); } else { fp = io_cb->open(frame->urlpath, "rb", frame->schunk->storage->io->params); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); + return BLOSC2_ERROR_FILE_OPEN; + } io_cb->seek(fp, frame->file_offset + trailer_offset, SEEK_SET); } if (fp != NULL) { @@ -1768,6 +1812,7 @@ blosc2_schunk* frame_to_schunk(blosc2_frame_s* frame, bool copy, const blosc2_io // If not the chunks won't be in the frame fp = io_cb->open(frame->urlpath, "rb", udio->params); if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); rc = BLOSC2_ERROR_FILE_OPEN; goto end; } @@ -2061,6 +2106,10 @@ int frame_get_chunk(blosc2_frame_s *frame, int64_t nchunk, uint8_t **chunk, bool if (frame->cframe == NULL) { uint8_t header[BLOSC_EXTENDED_HEADER_LENGTH]; void* fp = io_cb->open(frame->urlpath, "rb", frame->schunk->storage->io->params); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); + return BLOSC2_ERROR_FILE_OPEN; + } io_cb->seek(fp, frame->file_offset + header_len + offset, SEEK_SET); int64_t rbytes = io_cb->read(header, 1, sizeof(header), fp); if (rbytes != sizeof(header)) { @@ -2175,9 +2224,17 @@ int frame_get_lazychunk(blosc2_frame_s *frame, int64_t nchunk, uint8_t **chunk, // The chunk is not in the frame fp = sframe_open_chunk(frame->urlpath, offset, "rb", frame->schunk->storage->io); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); + return BLOSC2_ERROR_FILE_OPEN; + } } else { fp = io_cb->open(frame->urlpath, "rb", frame->schunk->storage->io->params); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); + return BLOSC2_ERROR_FILE_OPEN; + } io_cb->seek(fp, frame->file_offset + header_len + offset, SEEK_SET); } int64_t rbytes = io_cb->read(header, 1, BLOSC_EXTENDED_HEADER_LENGTH, fp); @@ -2445,11 +2502,19 @@ int64_t frame_fill_special(blosc2_frame_s* frame, int64_t nitems, int special_va if (frame->sframe) { // Update the offsets chunk in the chunks frame fp = sframe_open_index(frame->urlpath, "rb+", frame->schunk->storage->io); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); + return BLOSC2_ERROR_FILE_OPEN; + } io_cb->seek(fp, frame->file_offset + header_len, SEEK_SET); } else { // Regular frame fp = io_cb->open(frame->urlpath, "rb+", schunk->storage->io->params); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); + return BLOSC2_ERROR_FILE_OPEN; + } io_cb->seek(fp, frame->file_offset + header_len + cbytes, SEEK_SET); } wbytes = io_cb->write(off_chunk, 1, new_off_cbytes, fp); // the new offsets @@ -2670,11 +2735,19 @@ void* frame_append_chunk(blosc2_frame_s* frame, void* chunk, blosc2_schunk* schu } fp = sframe_open_index(frame->urlpath, "rb+", frame->schunk->storage->io); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); + return NULL; + } io_cb->seek(fp, frame->file_offset + header_len, SEEK_SET); } else { // Regular frame fp = io_cb->open(frame->urlpath, "rb+", frame->schunk->storage->io->params); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); + return NULL; + } io_cb->seek(fp, frame->file_offset + header_len + cbytes, SEEK_SET); wbytes = io_cb->write(chunk, 1, chunk_cbytes, fp); // the new chunk if (wbytes != chunk_cbytes) { @@ -2872,11 +2945,19 @@ void* frame_insert_chunk(blosc2_frame_s* frame, int64_t nchunk, void* chunk, blo // Update the offsets chunk in the chunks frame fp = sframe_open_index(frame->urlpath, "rb+", frame->schunk->storage->io); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); + return NULL; + } io_cb->seek(fp, frame->file_offset + header_len + 0, SEEK_SET); } else { // Regular frame fp = io_cb->open(frame->urlpath, "rb+", frame->schunk->storage->io->params); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); + return NULL; + } io_cb->seek(fp, frame->file_offset + header_len + cbytes, SEEK_SET); wbytes = io_cb->write(chunk, 1, chunk_cbytes, fp); // the new chunk if (wbytes != chunk_cbytes) { @@ -2998,7 +3079,7 @@ void* frame_update_chunk(blosc2_frame_s* frame, int64_t nchunk, void* chunk, blo // Add the new offset int64_t sframe_chunk_id; if (frame->sframe) { - if ((int64_t)offsets[nchunk] < 0) { + if (offsets[nchunk] < 0) { sframe_chunk_id = -1; } else { @@ -3110,11 +3191,19 @@ void* frame_update_chunk(blosc2_frame_s* frame, int64_t nchunk, void* chunk, blo // Update the offsets chunk in the chunks frame fp = sframe_open_index(frame->urlpath, "rb+", frame->schunk->storage->io); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); + return NULL; + } io_cb->seek(fp, frame->file_offset + header_len + 0, SEEK_SET); } else { // Regular frame fp = io_cb->open(frame->urlpath, "rb+", frame->schunk->storage->io->params); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); + return NULL; + } io_cb->seek(fp, frame->file_offset + header_len + cbytes, SEEK_SET); wbytes = io_cb->write(chunk, 1, chunk_cbytes, fp); // the new chunk if (wbytes != chunk_cbytes) { @@ -3268,11 +3357,19 @@ void* frame_delete_chunk(blosc2_frame_s* frame, int64_t nchunk, blosc2_schunk* s } // Update the offsets chunk in the chunks frame fp = sframe_open_index(frame->urlpath, "rb+", frame->schunk->storage->io); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); + return NULL; + } io_cb->seek(fp, frame->file_offset + header_len + 0, SEEK_SET); } else { // Regular frame fp = io_cb->open(frame->urlpath, "rb+", frame->schunk->storage->io); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); + return NULL; + } io_cb->seek(fp, frame->file_offset + header_len + cbytes, SEEK_SET); } wbytes = io_cb->write(off_chunk, 1, new_off_cbytes, fp); // the new offsets @@ -3407,11 +3504,19 @@ int frame_reorder_offsets(blosc2_frame_s* frame, const int64_t* offsets_order, b // Update the offsets chunk in the chunks frame fp = sframe_open_index(frame->urlpath, "rb+", frame->schunk->storage->io); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); + return BLOSC2_ERROR_FILE_OPEN; + } io_cb->seek(fp, frame->file_offset + header_len + 0, SEEK_SET); } else { // Regular frame fp = io_cb->open(frame->urlpath, "rb+", frame->schunk->storage->io->params); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); + return BLOSC2_ERROR_FILE_OPEN; + } io_cb->seek(fp, frame->file_offset + header_len + cbytes, SEEK_SET); } int64_t wbytes = io_cb->write(off_chunk, 1, new_off_cbytes, fp); // the new offsets diff --git a/blosc/frame.h b/blosc/frame.h index 18eb4f3c..b6ca3775 100644 --- a/blosc/frame.h +++ b/blosc/frame.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -11,8 +11,11 @@ #ifndef BLOSC_FRAME_H #define BLOSC_FRAME_H +#include "blosc2.h" + #include #include +#include // Different types of frames #define FRAME_CONTIGUOUS_TYPE 0 diff --git a/blosc/schunk.c b/blosc/schunk.c index 08a17687..7ef3336c 100644 --- a/blosc/schunk.c +++ b/blosc/schunk.c @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -9,32 +9,26 @@ **********************************************************************/ -#include -#include -#include -#include -#include "blosc2.h" +#include "blosc-private.h" +#include "blosc2/tuners-registry.h" #include "frame.h" #include "stune.h" -#include -#include "blosc-private.h" +#include "blosc2.h" #if defined(_WIN32) - #include - #include - #include - - #define mkdir(D, M) _mkdir(D) - -/* stdint.h only available in VS2010 (VC++ 16.0) and newer */ - #if defined(_MSC_VER) && _MSC_VER < 1600 - #include "win32/stdint-windows.h" - #else - #include - #endif +#include +#include +#include +#define mkdir(D, M) _mkdir(D) #endif /* _WIN32 */ +#include +#include +#include +#include +#include +#include /* If C11 is supported, use it's built-in aligned allocation. */ #if __STDC_VERSION__ >= 201112L @@ -95,6 +89,8 @@ void update_schunk_properties(struct blosc2_schunk* schunk) { schunk->typesize = cparams->typesize; schunk->blocksize = cparams->blocksize; schunk->chunksize = -1; + schunk->tuner_params = cparams->tuner_params; + schunk->tuner_id = cparams->tuner_id; /* The compression context */ if (schunk->cctx != NULL) { @@ -128,18 +124,34 @@ blosc2_schunk* blosc2_schunk_new(blosc2_storage *storage) { // Update the (local variable) storage storage = schunk->storage; - schunk->udbtune = malloc(sizeof(blosc2_btune)); - if (schunk->storage->cparams->udbtune == NULL) { - memcpy(schunk->udbtune, &BTUNE_DEFAULTS, sizeof(blosc2_btune)); - } else { - memcpy(schunk->udbtune, schunk->storage->cparams->udbtune, sizeof(blosc2_btune)); + char* btune_balance = getenv("BTUNE_BALANCE"); + if (btune_balance != NULL) { + // If BTUNE_BALANCE passed, automatically use btune + storage->cparams->tuner_id = BLOSC_BTUNE; } - schunk->storage->cparams->udbtune = schunk->udbtune; // ...and update internal properties update_schunk_properties(schunk); - schunk->cctx->udbtune->btune_init(schunk->udbtune->btune_config, schunk->cctx, schunk->dctx); + if (schunk->cctx->tuner_id < BLOSC_LAST_TUNER && schunk->cctx->tuner_id == BLOSC_STUNE) { + blosc_stune_init(schunk->storage->cparams->tuner_params, schunk->cctx, schunk->dctx); + } else { + for (int i = 0; i < g_ntuners; ++i) { + if (g_tuners[i].id == schunk->cctx->tuner_id) { + if (g_tuners[i].init == NULL) { + if (fill_tuner(&g_tuners[i]) < 0) { + BLOSC_TRACE_ERROR("Could not load tuner %d.", g_tuners[i].id); + return NULL; + } + } + g_tuners[i].init(schunk->storage->cparams->tuner_params, schunk->cctx, schunk->dctx); + goto urtunersuccess; + } + } + BLOSC_TRACE_ERROR("User-defined tuner %d not found\n", schunk->cctx->tuner_id); + return NULL; + } + urtunersuccess:; if (!storage->contiguous && storage->urlpath != NULL){ char* urlpath; @@ -540,9 +552,6 @@ int blosc2_schunk_free(blosc2_schunk *schunk) { } } - if (schunk->udbtune != NULL) { - free(schunk->udbtune); - } free(schunk); return 0; @@ -1376,29 +1385,6 @@ int blosc2_schunk_set_slice_buffer(blosc2_schunk *schunk, int64_t start, int64_t } -/* Find whether the schunk has a metalayer or not. - * - * If successful, return the index of the metalayer. Else, return a negative value. - */ -int blosc2_meta_exists(blosc2_schunk *schunk, const char *name) { - if (strlen(name) > BLOSC2_METALAYER_NAME_MAXLEN) { - BLOSC_TRACE_ERROR("Metalayers cannot be larger than %d chars.", BLOSC2_METALAYER_NAME_MAXLEN); - return BLOSC2_ERROR_INVALID_PARAM; - } - - if (schunk == NULL) { - BLOSC_TRACE_ERROR("Schunk must not be NUll."); - return BLOSC2_ERROR_INVALID_PARAM; - } - - for (int nmetalayer = 0; nmetalayer < schunk->nmetalayers; nmetalayer++) { - if (strcmp(name, schunk->metalayers[nmetalayer]->name) == 0) { - return nmetalayer; - } - } - return BLOSC2_ERROR_NOT_FOUND; -} - /* Reorder the chunk offsets of an existing super-chunk. */ int blosc2_schunk_reorder_offsets(blosc2_schunk *schunk, int64_t *offsets_order) { // Check that the offsets order are correct @@ -1553,25 +1539,6 @@ int blosc2_meta_update(blosc2_schunk *schunk, const char *name, uint8_t *content } -/* Get the content out of a metalayer. - * - * The `**content` receives a malloc'ed copy of the content. The user is responsible of freeing it. - * - * If successful, return the index of the new metalayer. Else, return a negative value. - */ -int blosc2_meta_get(blosc2_schunk *schunk, const char *name, uint8_t **content, - int32_t *content_len) { - int nmetalayer = blosc2_meta_exists(schunk, name); - if (nmetalayer < 0) { - BLOSC_TRACE_ERROR("Metalayer \"%s\" not found.", name); - return nmetalayer; - } - *content_len = schunk->metalayers[nmetalayer]->content_len; - *content = malloc((size_t)*content_len); - memcpy(*content, schunk->metalayers[nmetalayer]->content, (size_t)*content_len); - return nmetalayer; -} - /* Find whether the schunk has a variable-length metalayer or not. * * If successful, return the index of the variable-length metalayer. Else, return a negative value. diff --git a/blosc/sframe.c b/blosc/sframe.c index 6ff0f4c6..b7dacaa4 100644 --- a/blosc/sframe.c +++ b/blosc/sframe.c @@ -1,18 +1,19 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ +#include "frame.h" +#include "blosc2.h" + #include #include #include -#include "blosc2.h" -#include "frame.h" /* If C11 is supported, use it's built-in aligned allocation. */ @@ -33,6 +34,8 @@ void* sframe_open_index(const char* urlpath, const char* mode, const blosc2_io * return NULL; } fp = io_cb->open(index_path, mode, io->params); + if (fp == NULL) + BLOSC_TRACE_ERROR("Error creating index path in: %s", index_path); free(index_path); } return fp; @@ -50,6 +53,8 @@ void* sframe_open_chunk(const char* urlpath, int64_t nchunk, const char* mode, c return NULL; } fp = io_cb->open(chunk_path, mode, io->params); + if (fp == NULL) + BLOSC_TRACE_ERROR("Error opening chunk path in: %s", chunk_path); free(chunk_path); } return fp; diff --git a/blosc/sframe.h b/blosc/sframe.h index 75ea2393..0cabc0c4 100644 --- a/blosc/sframe.h +++ b/blosc/sframe.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/blosc/shuffle-altivec.c b/blosc/shuffle-altivec.c index 2de003b3..1f92d5bb 100644 --- a/blosc/shuffle-altivec.c +++ b/blosc/shuffle-altivec.c @@ -1,15 +1,15 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc developers and Jerome Kieffer + Copyright (c) 2021 The Blosc Development Team and Jerome Kieffer https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ -#include "shuffle-generic.h" #include "shuffle-altivec.h" +#include "shuffle-generic.h" /* Make sure ALTIVEC is available for the compilation target and compiler. */ #if defined(__ALTIVEC__) diff --git a/blosc/shuffle-altivec.h b/blosc/shuffle-altivec.h index 3825557e..1d46a09a 100644 --- a/blosc/shuffle-altivec.h +++ b/blosc/shuffle-altivec.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/blosc/shuffle-avx2.c b/blosc/shuffle-avx2.c index ae7dad3d..800bb2d0 100644 --- a/blosc/shuffle-avx2.c +++ b/blosc/shuffle-avx2.c @@ -1,15 +1,15 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ -#include "shuffle-generic.h" #include "shuffle-avx2.h" +#include "shuffle-generic.h" /* Make sure AVX2 is available for the compilation target and compiler. */ #if defined(__AVX2__) diff --git a/blosc/shuffle-avx2.c.orig b/blosc/shuffle-avx2.c.orig deleted file mode 100644 index 82f98640..00000000 --- a/blosc/shuffle-avx2.c.orig +++ /dev/null @@ -1,747 +0,0 @@ -/********************************************************************* - Blosc - Blocked Shuffling and Compression Library - - Copyright (C) 2021 The Blosc Developers - https://blosc.org - License: BSD 3-Clause (see LICENSE.txt) - - See LICENSE.txt for details about copyright and rights to use. -**********************************************************************/ - -#include "shuffle-generic.h" -#include "shuffle-avx2.h" - -/* Make sure AVX2 is available for the compilation target and compiler. */ -#if !defined(__AVX2__) - #error AVX2 is not supported by the target architecture/platform and/or this compiler. -#endif - -#include - - -/* The next is useful for debugging purposes */ -#if 0 -#include -#include - -static void printymm(__m256i ymm0) -{ - uint8_t buf[32]; - - ((__m256i *)buf)[0] = ymm0; - printf("%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n", - buf[0], buf[1], buf[2], buf[3], - buf[4], buf[5], buf[6], buf[7], - buf[8], buf[9], buf[10], buf[11], - buf[12], buf[13], buf[14], buf[15], - buf[16], buf[17], buf[18], buf[19], - buf[20], buf[21], buf[22], buf[23], - buf[24], buf[25], buf[26], buf[27], - buf[28], buf[29], buf[30], buf[31]); -} -#endif - -/* GCC doesn't include the split load/store intrinsics - needed for the tiled shuffle, so define them here. */ -#if defined(__GNUC__) && !defined(__clang__) -static inline __m256i -__attribute__((__always_inline__)) -_mm256_loadu2_m128i(const __m128i* const hiaddr, const __m128i* const loaddr) -{ - return _mm256_inserti128_si256( - _mm256_castsi128_si256(_mm_loadu_si128(loaddr)), _mm_loadu_si128(hiaddr), 1); -} - -static inline void -__attribute__((__always_inline__)) -_mm256_storeu2_m128i(__m128i* const hiaddr, __m128i* const loaddr, const __m256i a) -{ - _mm_storeu_si128(loaddr, _mm256_castsi256_si128(a)); - _mm_storeu_si128(hiaddr, _mm256_extracti128_si256(a, 1)); -} -#endif /* defined(__GNUC__) */ - -/* Routine optimized for shuffling a buffer for a type size of 2 bytes. */ -static void -shuffle2_avx2(uint8_t* const dest, const uint8_t* const src, - const size_t vectorizable_elements, const size_t total_elements) { - static const size_t bytesoftype = 2; - size_t j; - int k; - __m256i ymm0[2], ymm1[2]; - - /* Create the shuffle mask. - NOTE: The XMM/YMM 'set' intrinsics require the arguments to be ordered from - most to least significant (i.e., their order is reversed when compared to - loading the mask from an array). */ - const __m256i shmask = _mm256_set_epi8( - 0x0f, 0x0d, 0x0b, 0x09, 0x07, 0x05, 0x03, 0x01, - 0x0e, 0x0c, 0x0a, 0x08, 0x06, 0x04, 0x02, 0x00, - 0x0f, 0x0d, 0x0b, 0x09, 0x07, 0x05, 0x03, 0x01, - 0x0e, 0x0c, 0x0a, 0x08, 0x06, 0x04, 0x02, 0x00); - - for (j = 0; j < vectorizable_elements; j += sizeof(__m256i)) { - /* Fetch 32 elements (64 bytes) then transpose bytes, words and double words. */ - for (k = 0; k < 2; k++) { - ymm0[k] = _mm256_loadu_si256((__m256i * )(src + (j * bytesoftype) + (k * sizeof(__m256i)))); - ymm1[k] = _mm256_shuffle_epi8(ymm0[k], shmask); - } - - ymm0[0] = _mm256_permute4x64_epi64(ymm1[0], 0xd8); - ymm0[1] = _mm256_permute4x64_epi64(ymm1[1], 0x8d); - - ymm1[0] = _mm256_blend_epi32(ymm0[0], ymm0[1], 0xf0); - ymm0[1] = _mm256_blend_epi32(ymm0[0], ymm0[1], 0x0f); - ymm1[1] = _mm256_permute4x64_epi64(ymm0[1], 0x4e); - - /* Store the result vectors */ - uint8_t* const dest_for_jth_element = dest + j; - for (k = 0; k < 2; k++) { - _mm256_storeu_si256((__m256i * )(dest_for_jth_element + (k * total_elements)), ymm1[k]); - } - } -} - -/* Routine optimized for shuffling a buffer for a type size of 4 bytes. */ -static void -shuffle4_avx2(uint8_t* const dest, const uint8_t* const src, - const size_t vectorizable_elements, const size_t total_elements) { - static const size_t bytesoftype = 4; - size_t i; - int j; - __m256i ymm0[4], ymm1[4]; - - /* Create the shuffle mask. - NOTE: The XMM/YMM 'set' intrinsics require the arguments to be ordered from - most to least significant (i.e., their order is reversed when compared to - loading the mask from an array). */ - const __m256i mask = _mm256_set_epi32( - 0x07, 0x03, 0x06, 0x02, 0x05, 0x01, 0x04, 0x00); - - for (i = 0; i < vectorizable_elements; i += sizeof(__m256i)) { - /* Fetch 32 elements (128 bytes) then transpose bytes and words. */ - for (j = 0; j < 4; j++) { - ymm0[j] = _mm256_loadu_si256((__m256i * )(src + (i * bytesoftype) + (j * sizeof(__m256i)))); - ymm1[j] = _mm256_shuffle_epi32(ymm0[j], 0xd8); - ymm0[j] = _mm256_shuffle_epi32(ymm0[j], 0x8d); - ymm0[j] = _mm256_unpacklo_epi8(ymm1[j], ymm0[j]); - ymm1[j] = _mm256_shuffle_epi32(ymm0[j], 0x04e); - ymm0[j] = _mm256_unpacklo_epi16(ymm0[j], ymm1[j]); - } - /* Transpose double words */ - for (j = 0; j < 2; j++) { - ymm1[j * 2] = _mm256_unpacklo_epi32(ymm0[j * 2], ymm0[j * 2 + 1]); - ymm1[j * 2 + 1] = _mm256_unpackhi_epi32(ymm0[j * 2], ymm0[j * 2 + 1]); - } - /* Transpose quad words */ - for (j = 0; j < 2; j++) { - ymm0[j * 2] = _mm256_unpacklo_epi64(ymm1[j], ymm1[j + 2]); - ymm0[j * 2 + 1] = _mm256_unpackhi_epi64(ymm1[j], ymm1[j + 2]); - } - for (j = 0; j < 4; j++) { - ymm0[j] = _mm256_permutevar8x32_epi32(ymm0[j], mask); - } - /* Store the result vectors */ - uint8_t* const dest_for_ith_element = dest + i; - for (j = 0; j < 4; j++) { - _mm256_storeu_si256((__m256i * )(dest_for_ith_element + (j * total_elements)), ymm0[j]); - } - } -} - -/* Routine optimized for shuffling a buffer for a type size of 8 bytes. */ -static void -shuffle8_avx2(uint8_t* const dest, const uint8_t* const src, - const size_t vectorizable_elements, const size_t total_elements) { - static const size_t bytesoftype = 8; - size_t j; - int k, l; - __m256i ymm0[8], ymm1[8]; - - for (j = 0; j < vectorizable_elements; j += sizeof(__m256i)) { - /* Fetch 32 elements (256 bytes) then transpose bytes. */ - for (k = 0; k < 8; k++) { - ymm0[k] = _mm256_loadu_si256((__m256i * )(src + (j * bytesoftype) + (k * sizeof(__m256i)))); - ymm1[k] = _mm256_shuffle_epi32(ymm0[k], 0x4e); - ymm1[k] = _mm256_unpacklo_epi8(ymm0[k], ymm1[k]); - } - /* Transpose words */ - for (k = 0, l = 0; k < 4; k++, l += 2) { - ymm0[k * 2] = _mm256_unpacklo_epi16(ymm1[l], ymm1[l + 1]); - ymm0[k * 2 + 1] = _mm256_unpackhi_epi16(ymm1[l], ymm1[l + 1]); - } - /* Transpose double words */ - for (k = 0, l = 0; k < 4; k++, l++) { - if (k == 2) l += 2; - ymm1[k * 2] = _mm256_unpacklo_epi32(ymm0[l], ymm0[l + 2]); - ymm1[k * 2 + 1] = _mm256_unpackhi_epi32(ymm0[l], ymm0[l + 2]); - } - /* Transpose quad words */ - for (k = 0; k < 4; k++) { - ymm0[k * 2] = _mm256_unpacklo_epi64(ymm1[k], ymm1[k + 4]); - ymm0[k * 2 + 1] = _mm256_unpackhi_epi64(ymm1[k], ymm1[k + 4]); - } - for (k = 0; k < 8; k++) { - ymm1[k] = _mm256_permute4x64_epi64(ymm0[k], 0x72); - ymm0[k] = _mm256_permute4x64_epi64(ymm0[k], 0xD8); - ymm0[k] = _mm256_unpacklo_epi16(ymm0[k], ymm1[k]); - } - /* Store the result vectors */ - uint8_t* const dest_for_jth_element = dest + j; - for (k = 0; k < 8; k++) { - _mm256_storeu_si256((__m256i * )(dest_for_jth_element + (k * total_elements)), ymm0[k]); - } - } -} - -/* Routine optimized for shuffling a buffer for a type size of 16 bytes. */ -static void -shuffle16_avx2(uint8_t* const dest, const uint8_t* const src, - const size_t vectorizable_elements, const size_t total_elements) { - static const size_t bytesoftype = 16; - size_t j; - int k, l; - __m256i ymm0[16], ymm1[16]; - - /* Create the shuffle mask. - NOTE: The XMM/YMM 'set' intrinsics require the arguments to be ordered from - most to least significant (i.e., their order is reversed when compared to - loading the mask from an array). */ - const __m256i shmask = _mm256_set_epi8( - 0x0f, 0x07, 0x0e, 0x06, 0x0d, 0x05, 0x0c, 0x04, - 0x0b, 0x03, 0x0a, 0x02, 0x09, 0x01, 0x08, 0x00, - 0x0f, 0x07, 0x0e, 0x06, 0x0d, 0x05, 0x0c, 0x04, - 0x0b, 0x03, 0x0a, 0x02, 0x09, 0x01, 0x08, 0x00); - - for (j = 0; j < vectorizable_elements; j += sizeof(__m256i)) { - /* Fetch 32 elements (512 bytes) into 16 YMM registers. */ - for (k = 0; k < 16; k++) { - ymm0[k] = _mm256_loadu_si256((__m256i * )(src + (j * bytesoftype) + (k * sizeof(__m256i)))); - } - /* Transpose bytes */ - for (k = 0, l = 0; k < 8; k++, l += 2) { - ymm1[k * 2] = _mm256_unpacklo_epi8(ymm0[l], ymm0[l + 1]); - ymm1[k * 2 + 1] = _mm256_unpackhi_epi8(ymm0[l], ymm0[l + 1]); - } - /* Transpose words */ - for (k = 0, l = -2; k < 8; k++, l++) { - if ((k % 2) == 0) l += 2; - ymm0[k * 2] = _mm256_unpacklo_epi16(ymm1[l], ymm1[l + 2]); - ymm0[k * 2 + 1] = _mm256_unpackhi_epi16(ymm1[l], ymm1[l + 2]); - } - /* Transpose double words */ - for (k = 0, l = -4; k < 8; k++, l++) { - if ((k % 4) == 0) l += 4; - ymm1[k * 2] = _mm256_unpacklo_epi32(ymm0[l], ymm0[l + 4]); - ymm1[k * 2 + 1] = _mm256_unpackhi_epi32(ymm0[l], ymm0[l + 4]); - } - /* Transpose quad words */ - for (k = 0; k < 8; k++) { - ymm0[k * 2] = _mm256_unpacklo_epi64(ymm1[k], ymm1[k + 8]); - ymm0[k * 2 + 1] = _mm256_unpackhi_epi64(ymm1[k], ymm1[k + 8]); - } - for (k = 0; k < 16; k++) { - ymm0[k] = _mm256_permute4x64_epi64(ymm0[k], 0xd8); - ymm0[k] = _mm256_shuffle_epi8(ymm0[k], shmask); - } - /* Store the result vectors */ - uint8_t* const dest_for_jth_element = dest + j; - for (k = 0; k < 16; k++) { - _mm256_storeu_si256((__m256i * )(dest_for_jth_element + (k * total_elements)), ymm0[k]); - } - } -} - -/* Routine optimized for shuffling a buffer for a type size larger than 16 bytes. */ -static void -shuffle16_tiled_avx2(uint8_t* const dest, const uint8_t* const src, - const size_t vectorizable_elements, const size_t total_elements, const size_t bytesoftype) { - size_t j; - int k, l; - __m256i ymm0[16], ymm1[16]; - - const lldiv_t vecs_per_el = lldiv(bytesoftype, sizeof(__m128i)); - - /* Create the shuffle mask. - NOTE: The XMM/YMM 'set' intrinsics require the arguments to be ordered from - most to least significant (i.e., their order is reversed when compared to - loading the mask from an array). */ - const __m256i shmask = _mm256_set_epi8( - 0x0f, 0x07, 0x0e, 0x06, 0x0d, 0x05, 0x0c, 0x04, - 0x0b, 0x03, 0x0a, 0x02, 0x09, 0x01, 0x08, 0x00, - 0x0f, 0x07, 0x0e, 0x06, 0x0d, 0x05, 0x0c, 0x04, - 0x0b, 0x03, 0x0a, 0x02, 0x09, 0x01, 0x08, 0x00); - - for (j = 0; j < vectorizable_elements; j += sizeof(__m256i)) { - /* Advance the offset into the type by the vector size (in bytes), unless this is - the initial iteration and the type size is not a multiple of the vector size. - In that case, only advance by the number of bytes necessary so that the number - of remaining bytes in the type will be a multiple of the vector size. */ - size_t offset_into_type; - for (offset_into_type = 0; offset_into_type < bytesoftype; - offset_into_type += (offset_into_type == 0 && vecs_per_el.rem > 0 ? vecs_per_el.rem : sizeof(__m128i))) { - - /* Fetch elements in groups of 512 bytes */ - const uint8_t* const src_with_offset = src + offset_into_type; - for (k = 0; k < 16; k++) { - ymm0[k] = _mm256_loadu2_m128i( - (__m128i * )(src_with_offset + (j + (2 * k) + 1) * bytesoftype), - (__m128i * )(src_with_offset + (j + (2 * k)) * bytesoftype)); - } - /* Transpose bytes */ - for (k = 0, l = 0; k < 8; k++, l += 2) { - ymm1[k * 2] = _mm256_unpacklo_epi8(ymm0[l], ymm0[l + 1]); - ymm1[k * 2 + 1] = _mm256_unpackhi_epi8(ymm0[l], ymm0[l + 1]); - } - /* Transpose words */ - for (k = 0, l = -2; k < 8; k++, l++) { - if ((k % 2) == 0) l += 2; - ymm0[k * 2] = _mm256_unpacklo_epi16(ymm1[l], ymm1[l + 2]); - ymm0[k * 2 + 1] = _mm256_unpackhi_epi16(ymm1[l], ymm1[l + 2]); - } - /* Transpose double words */ - for (k = 0, l = -4; k < 8; k++, l++) { - if ((k % 4) == 0) l += 4; - ymm1[k * 2] = _mm256_unpacklo_epi32(ymm0[l], ymm0[l + 4]); - ymm1[k * 2 + 1] = _mm256_unpackhi_epi32(ymm0[l], ymm0[l + 4]); - } - /* Transpose quad words */ - for (k = 0; k < 8; k++) { - ymm0[k * 2] = _mm256_unpacklo_epi64(ymm1[k], ymm1[k + 8]); - ymm0[k * 2 + 1] = _mm256_unpackhi_epi64(ymm1[k], ymm1[k + 8]); - } - for (k = 0; k < 16; k++) { - ymm0[k] = _mm256_permute4x64_epi64(ymm0[k], 0xd8); - ymm0[k] = _mm256_shuffle_epi8(ymm0[k], shmask); - } - /* Store the result vectors */ - uint8_t* const dest_for_jth_element = dest + j; - for (k = 0; k < 16; k++) { - _mm256_storeu_si256((__m256i * )(dest_for_jth_element + (total_elements * (offset_into_type + k))), ymm0[k]); - } - } - } -} - -/* Routine optimized for unshuffling a buffer for a type size of 2 bytes. */ -static void -unshuffle2_avx2(uint8_t* const dest, const uint8_t* const src, - const size_t vectorizable_elements, const size_t total_elements) { - static const size_t bytesoftype = 2; - size_t i; - int j; - __m256i ymm0[2], ymm1[2]; - - for (i = 0; i < vectorizable_elements; i += sizeof(__m256i)) { - /* Load 32 elements (64 bytes) into 2 YMM registers. */ - const uint8_t* const src_for_ith_element = src + i; - for (j = 0; j < 2; j++) { - ymm0[j] = _mm256_loadu_si256((__m256i * )(src_for_ith_element + (j * total_elements))); - } - /* Shuffle bytes */ - for (j = 0; j < 2; j++) { - ymm0[j] = _mm256_permute4x64_epi64(ymm0[j], 0xd8); - } - /* Compute the low 64 bytes */ - ymm1[0] = _mm256_unpacklo_epi8(ymm0[0], ymm0[1]); - /* Compute the hi 64 bytes */ - ymm1[1] = _mm256_unpackhi_epi8(ymm0[0], ymm0[1]); - /* Store the result vectors in proper order */ - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (0 * sizeof(__m256i))), ymm1[0]); - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (1 * sizeof(__m256i))), ymm1[1]); - } -} - -/* Routine optimized for unshuffling a buffer for a type size of 4 bytes. */ -static void -unshuffle4_avx2(uint8_t* const dest, const uint8_t* const src, - const size_t vectorizable_elements, const size_t total_elements) { - static const size_t bytesoftype = 4; - size_t i; - int j; - __m256i ymm0[4], ymm1[4]; - - for (i = 0; i < vectorizable_elements; i += sizeof(__m256i)) { - /* Load 32 elements (128 bytes) into 4 YMM registers. */ - const uint8_t* const src_for_ith_element = src + i; - for (j = 0; j < 4; j++) { - ymm0[j] = _mm256_loadu_si256((__m256i * )(src_for_ith_element + (j * total_elements))); - } - /* Shuffle bytes */ - for (j = 0; j < 2; j++) { - /* Compute the low 64 bytes */ - ymm1[j] = _mm256_unpacklo_epi8(ymm0[j * 2], ymm0[j * 2 + 1]); - /* Compute the hi 64 bytes */ - ymm1[2 + j] = _mm256_unpackhi_epi8(ymm0[j * 2], ymm0[j * 2 + 1]); - } - /* Shuffle 2-byte words */ - for (j = 0; j < 2; j++) { - /* Compute the low 64 bytes */ - ymm0[j] = _mm256_unpacklo_epi16(ymm1[j * 2], ymm1[j * 2 + 1]); - /* Compute the hi 64 bytes */ - ymm0[2 + j] = _mm256_unpackhi_epi16(ymm1[j * 2], ymm1[j * 2 + 1]); - } - ymm1[0] = _mm256_permute2x128_si256(ymm0[0], ymm0[2], 0x20); - ymm1[1] = _mm256_permute2x128_si256(ymm0[1], ymm0[3], 0x20); - ymm1[2] = _mm256_permute2x128_si256(ymm0[0], ymm0[2], 0x31); - ymm1[3] = _mm256_permute2x128_si256(ymm0[1], ymm0[3], 0x31); - - /* Store the result vectors in proper order */ - for (j = 0; j < 4; j++) { - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (j * sizeof(__m256i))), ymm1[j]); - } - } -} - -/* Routine optimized for unshuffling a buffer for a type size of 8 bytes. */ -static void -unshuffle8_avx2(uint8_t* const dest, const uint8_t* const src, - const size_t vectorizable_elements, const size_t total_elements) { - static const size_t bytesoftype = 8; - size_t i; - int j; - __m256i ymm0[8], ymm1[8]; - - for (i = 0; i < vectorizable_elements; i += sizeof(__m256i)) { - /* Fetch 32 elements (256 bytes) into 8 YMM registers. */ - const uint8_t* const src_for_ith_element = src + i; - for (j = 0; j < 8; j++) { - ymm0[j] = _mm256_loadu_si256((__m256i * )(src_for_ith_element + (j * total_elements))); - } - /* Shuffle bytes */ - for (j = 0; j < 4; j++) { - /* Compute the low 32 bytes */ - ymm1[j] = _mm256_unpacklo_epi8(ymm0[j * 2], ymm0[j * 2 + 1]); - /* Compute the hi 32 bytes */ - ymm1[4 + j] = _mm256_unpackhi_epi8(ymm0[j * 2], ymm0[j * 2 + 1]); - } - /* Shuffle words */ - for (j = 0; j < 4; j++) { - /* Compute the low 32 bytes */ - ymm0[j] = _mm256_unpacklo_epi16(ymm1[j * 2], ymm1[j * 2 + 1]); - /* Compute the hi 32 bytes */ - ymm0[4 + j] = _mm256_unpackhi_epi16(ymm1[j * 2], ymm1[j * 2 + 1]); - } - for (j = 0; j < 8; j++) { - ymm0[j] = _mm256_permute4x64_epi64(ymm0[j], 0xd8); - } - - /* Shuffle 4-byte dwords */ - for (j = 0; j < 4; j++) { - /* Compute the low 32 bytes */ - ymm1[j] = _mm256_unpacklo_epi32(ymm0[j * 2], ymm0[j * 2 + 1]); - /* Compute the hi 32 bytes */ - ymm1[4 + j] = _mm256_unpackhi_epi32(ymm0[j * 2], ymm0[j * 2 + 1]); - } - - /* Store the result vectors in proper order */ - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (0 * sizeof(__m256i))), ymm1[0]); - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (1 * sizeof(__m256i))), ymm1[2]); - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (2 * sizeof(__m256i))), ymm1[1]); - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (3 * sizeof(__m256i))), ymm1[3]); - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (4 * sizeof(__m256i))), ymm1[4]); - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (5 * sizeof(__m256i))), ymm1[6]); - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (6 * sizeof(__m256i))), ymm1[5]); - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (7 * sizeof(__m256i))), ymm1[7]); - } -} - -/* Routine optimized for unshuffling a buffer for a type size of 16 bytes. */ -static void -unshuffle16_avx2(uint8_t* const dest, const uint8_t* const src, - const size_t vectorizable_elements, const size_t total_elements) { - static const size_t bytesoftype = 16; - size_t i; - int j; - __m256i ymm0[16], ymm1[16]; - - for (i = 0; i < vectorizable_elements; i += sizeof(__m256i)) { - /* Fetch 32 elements (512 bytes) into 16 YMM registers. */ - const uint8_t* const src_for_ith_element = src + i; - for (j = 0; j < 16; j++) { - ymm0[j] = _mm256_loadu_si256((__m256i * )(src_for_ith_element + (j * total_elements))); - } - - /* Shuffle bytes */ - for (j = 0; j < 8; j++) { - /* Compute the low 32 bytes */ - ymm1[j] = _mm256_unpacklo_epi8(ymm0[j * 2], ymm0[j * 2 + 1]); - /* Compute the hi 32 bytes */ - ymm1[8 + j] = _mm256_unpackhi_epi8(ymm0[j * 2], ymm0[j * 2 + 1]); - } - /* Shuffle 2-byte words */ - for (j = 0; j < 8; j++) { - /* Compute the low 32 bytes */ - ymm0[j] = _mm256_unpacklo_epi16(ymm1[j * 2], ymm1[j * 2 + 1]); - /* Compute the hi 32 bytes */ - ymm0[8 + j] = _mm256_unpackhi_epi16(ymm1[j * 2], ymm1[j * 2 + 1]); - } - /* Shuffle 4-byte dwords */ - for (j = 0; j < 8; j++) { - /* Compute the low 32 bytes */ - ymm1[j] = _mm256_unpacklo_epi32(ymm0[j * 2], ymm0[j * 2 + 1]); - /* Compute the hi 32 bytes */ - ymm1[8 + j] = _mm256_unpackhi_epi32(ymm0[j * 2], ymm0[j * 2 + 1]); - } - - /* Shuffle 8-byte qwords */ - for (j = 0; j < 8; j++) { - /* Compute the low 32 bytes */ - ymm0[j] = _mm256_unpacklo_epi64(ymm1[j * 2], ymm1[j * 2 + 1]); - /* Compute the hi 32 bytes */ - ymm0[8 + j] = _mm256_unpackhi_epi64(ymm1[j * 2], ymm1[j * 2 + 1]); - } - - for (j = 0; j < 8; j++) { - ymm1[j] = _mm256_permute2x128_si256(ymm0[j], ymm0[j + 8], 0x20); - ymm1[j + 8] = _mm256_permute2x128_si256(ymm0[j], ymm0[j + 8], 0x31); - } - - /* Store the result vectors in proper order */ - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (0 * sizeof(__m256i))), ymm1[0]); - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (1 * sizeof(__m256i))), ymm1[4]); - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (2 * sizeof(__m256i))), ymm1[2]); - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (3 * sizeof(__m256i))), ymm1[6]); - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (4 * sizeof(__m256i))), ymm1[1]); - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (5 * sizeof(__m256i))), ymm1[5]); - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (6 * sizeof(__m256i))), ymm1[3]); - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (7 * sizeof(__m256i))), ymm1[7]); - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (8 * sizeof(__m256i))), ymm1[8]); - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (9 * sizeof(__m256i))), ymm1[12]); - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (10 * sizeof(__m256i))), ymm1[10]); - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (11 * sizeof(__m256i))), ymm1[14]); - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (12 * sizeof(__m256i))), ymm1[9]); - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (13 * sizeof(__m256i))), ymm1[13]); - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (14 * sizeof(__m256i))), ymm1[11]); - _mm256_storeu_si256((__m256i * )(dest + (i * bytesoftype) + (15 * sizeof(__m256i))), ymm1[15]); - } -} - -/* Routine optimized for unshuffling a buffer for a type size larger than 16 bytes. */ -static void -unshuffle16_tiled_avx2(uint8_t* const dest, const uint8_t* const src, - const size_t vectorizable_elements, const size_t total_elements, const size_t bytesoftype) { - size_t i; - int j; - __m256i ymm0[16], ymm1[16]; - - const lldiv_t vecs_per_el = lldiv(bytesoftype, sizeof(__m128i)); - - /* The unshuffle loops are inverted (compared to shuffle_tiled16_avx2) - to optimize cache utilization. */ - size_t offset_into_type; - for (offset_into_type = 0; offset_into_type < bytesoftype; - offset_into_type += (offset_into_type == 0 && vecs_per_el.rem > 0 ? vecs_per_el.rem : sizeof(__m128i))) { - for (i = 0; i < vectorizable_elements; i += sizeof(__m256i)) { - /* Load the first 16 bytes of 32 adjacent elements (512 bytes) into 16 YMM registers */ - const uint8_t* const src_for_ith_element = src + i; - for (j = 0; j < 16; j++) { - ymm0[j] = _mm256_loadu_si256((__m256i * )(src_for_ith_element + (total_elements * (offset_into_type + j)))); - } - - /* Shuffle bytes */ - for (j = 0; j < 8; j++) { - /* Compute the low 32 bytes */ - ymm1[j] = _mm256_unpacklo_epi8(ymm0[j * 2], ymm0[j * 2 + 1]); - /* Compute the hi 32 bytes */ - ymm1[8 + j] = _mm256_unpackhi_epi8(ymm0[j * 2], ymm0[j * 2 + 1]); - } - /* Shuffle 2-byte words */ - for (j = 0; j < 8; j++) { - /* Compute the low 32 bytes */ - ymm0[j] = _mm256_unpacklo_epi16(ymm1[j * 2], ymm1[j * 2 + 1]); - /* Compute the hi 32 bytes */ - ymm0[8 + j] = _mm256_unpackhi_epi16(ymm1[j * 2], ymm1[j * 2 + 1]); - } - /* Shuffle 4-byte dwords */ - for (j = 0; j < 8; j++) { - /* Compute the low 32 bytes */ - ymm1[j] = _mm256_unpacklo_epi32(ymm0[j * 2], ymm0[j * 2 + 1]); - /* Compute the hi 32 bytes */ - ymm1[8 + j] = _mm256_unpackhi_epi32(ymm0[j * 2], ymm0[j * 2 + 1]); - } - - /* Shuffle 8-byte qwords */ - for (j = 0; j < 8; j++) { - /* Compute the low 32 bytes */ - ymm0[j] = _mm256_unpacklo_epi64(ymm1[j * 2], ymm1[j * 2 + 1]); - /* Compute the hi 32 bytes */ - ymm0[8 + j] = _mm256_unpackhi_epi64(ymm1[j * 2], ymm1[j * 2 + 1]); - } - - for (j = 0; j < 8; j++) { - ymm1[j] = _mm256_permute2x128_si256(ymm0[j], ymm0[j + 8], 0x20); - ymm1[j + 8] = _mm256_permute2x128_si256(ymm0[j], ymm0[j + 8], 0x31); - } - - /* Store the result vectors in proper order */ - const uint8_t* const dest_with_offset = dest + offset_into_type; - _mm256_storeu2_m128i( - (__m128i * )(dest_with_offset + (i + 0x01) * bytesoftype), - (__m128i * )(dest_with_offset + (i + 0x00) * bytesoftype), ymm1[0]); - _mm256_storeu2_m128i( - (__m128i * )(dest_with_offset + (i + 0x03) * bytesoftype), - (__m128i * )(dest_with_offset + (i + 0x02) * bytesoftype), ymm1[4]); - _mm256_storeu2_m128i( - (__m128i * )(dest_with_offset + (i + 0x05) * bytesoftype), - (__m128i * )(dest_with_offset + (i + 0x04) * bytesoftype), ymm1[2]); - _mm256_storeu2_m128i( - (__m128i * )(dest_with_offset + (i + 0x07) * bytesoftype), - (__m128i * )(dest_with_offset + (i + 0x06) * bytesoftype), ymm1[6]); - _mm256_storeu2_m128i( - (__m128i * )(dest_with_offset + (i + 0x09) * bytesoftype), - (__m128i * )(dest_with_offset + (i + 0x08) * bytesoftype), ymm1[1]); - _mm256_storeu2_m128i( - (__m128i * )(dest_with_offset + (i + 0x0b) * bytesoftype), - (__m128i * )(dest_with_offset + (i + 0x0a) * bytesoftype), ymm1[5]); - _mm256_storeu2_m128i( - (__m128i * )(dest_with_offset + (i + 0x0d) * bytesoftype), - (__m128i * )(dest_with_offset + (i + 0x0c) * bytesoftype), ymm1[3]); - _mm256_storeu2_m128i( - (__m128i * )(dest_with_offset + (i + 0x0f) * bytesoftype), - (__m128i * )(dest_with_offset + (i + 0x0e) * bytesoftype), ymm1[7]); - _mm256_storeu2_m128i( - (__m128i * )(dest_with_offset + (i + 0x11) * bytesoftype), - (__m128i * )(dest_with_offset + (i + 0x10) * bytesoftype), ymm1[8]); - _mm256_storeu2_m128i( - (__m128i * )(dest_with_offset + (i + 0x13) * bytesoftype), - (__m128i * )(dest_with_offset + (i + 0x12) * bytesoftype), ymm1[12]); - _mm256_storeu2_m128i( - (__m128i * )(dest_with_offset + (i + 0x15) * bytesoftype), - (__m128i * )(dest_with_offset + (i + 0x14) * bytesoftype), ymm1[10]); - _mm256_storeu2_m128i( - (__m128i * )(dest_with_offset + (i + 0x17) * bytesoftype), - (__m128i * )(dest_with_offset + (i + 0x16) * bytesoftype), ymm1[14]); - _mm256_storeu2_m128i( - (__m128i * )(dest_with_offset + (i + 0x19) * bytesoftype), - (__m128i * )(dest_with_offset + (i + 0x18) * bytesoftype), ymm1[9]); - _mm256_storeu2_m128i( - (__m128i * )(dest_with_offset + (i + 0x1b) * bytesoftype), - (__m128i * )(dest_with_offset + (i + 0x1a) * bytesoftype), ymm1[13]); - _mm256_storeu2_m128i( - (__m128i * )(dest_with_offset + (i + 0x1d) * bytesoftype), - (__m128i * )(dest_with_offset + (i + 0x1c) * bytesoftype), ymm1[11]); - _mm256_storeu2_m128i( - (__m128i * )(dest_with_offset + (i + 0x1f) * bytesoftype), - (__m128i * )(dest_with_offset + (i + 0x1e) * bytesoftype), ymm1[15]); - } - } -} - -/* Shuffle a block. This can never fail. */ -void -shuffle_avx2(const size_t bytesoftype, const size_t blocksize, - const uint8_t* const _src, uint8_t* const _dest) { - const size_t vectorized_chunk_size = bytesoftype * sizeof(__m256i); - - /* If the block size is too small to be vectorized, - use the generic implementation. */ - if (blocksize < vectorized_chunk_size) { - shuffle_generic(bytesoftype, blocksize, _src, _dest); - return; - } - - /* If the blocksize is not a multiple of both the typesize and - the vector size, round the blocksize down to the next value - which is a multiple of both. The vectorized shuffle can be - used for that portion of the data, and the naive implementation - can be used for the remaining portion. */ - const size_t vectorizable_bytes = blocksize - (blocksize % vectorized_chunk_size); - - const size_t vectorizable_elements = vectorizable_bytes / bytesoftype; - const size_t total_elements = blocksize / bytesoftype; - - /* Optimized shuffle implementations */ - switch (bytesoftype) { - case 2: - shuffle2_avx2(_dest, _src, vectorizable_elements, total_elements); - break; - case 4: - shuffle4_avx2(_dest, _src, vectorizable_elements, total_elements); - break; - case 8: - shuffle8_avx2(_dest, _src, vectorizable_elements, total_elements); - break; - case 16: - shuffle16_avx2(_dest, _src, vectorizable_elements, total_elements); - break; - default: - /* For types larger than 16 bytes, use the AVX2 tiled shuffle. */ - if (bytesoftype > sizeof(__m128i)) { - shuffle16_tiled_avx2(_dest, _src, vectorizable_elements, total_elements, bytesoftype); - } - else { - /* Non-optimized shuffle */ - shuffle_generic(bytesoftype, blocksize, _src, _dest); - /* The non-optimized function covers the whole buffer, - so we're done processing here. */ - return; - } - } - - /* If the buffer had any bytes at the end which couldn't be handled - by the vectorized implementations, use the non-optimized version - to finish them up. */ - if (vectorizable_bytes < blocksize) { - shuffle_generic_inline(bytesoftype, vectorizable_bytes, blocksize, _src, _dest); - } -} - -/* Unshuffle a block. This can never fail. */ -void -unshuffle_avx2(const size_t bytesoftype, const size_t blocksize, - const uint8_t* const _src, uint8_t* const _dest) { - const size_t vectorized_chunk_size = bytesoftype * sizeof(__m256i); - - /* If the block size is too small to be vectorized, - use the generic implementation. */ - if (blocksize < vectorized_chunk_size) { - unshuffle_generic(bytesoftype, blocksize, _src, _dest); - return; - } - - /* If the blocksize is not a multiple of both the typesize and - the vector size, round the blocksize down to the next value - which is a multiple of both. The vectorized unshuffle can be - used for that portion of the data, and the naive implementation - can be used for the remaining portion. */ - const size_t vectorizable_bytes = blocksize - (blocksize % vectorized_chunk_size); - - const size_t vectorizable_elements = vectorizable_bytes / bytesoftype; - const size_t total_elements = blocksize / bytesoftype; - - /* Optimized unshuffle implementations */ - switch (bytesoftype) { - case 2: - unshuffle2_avx2(_dest, _src, vectorizable_elements, total_elements); - break; - case 4: - unshuffle4_avx2(_dest, _src, vectorizable_elements, total_elements); - break; - case 8: - unshuffle8_avx2(_dest, _src, vectorizable_elements, total_elements); - break; - case 16: - unshuffle16_avx2(_dest, _src, vectorizable_elements, total_elements); - break; - default: - /* For types larger than 16 bytes, use the AVX2 tiled unshuffle. */ - if (bytesoftype > sizeof(__m128i)) { - unshuffle16_tiled_avx2(_dest, _src, vectorizable_elements, total_elements, bytesoftype); - } - else { - /* Non-optimized unshuffle */ - unshuffle_generic(bytesoftype, blocksize, _src, _dest); - /* The non-optimized function covers the whole buffer, - so we're done processing here. */ - return; - } - } - - /* If the buffer had any bytes at the end which couldn't be handled - by the vectorized implementations, use the non-optimized version - to finish them up. */ - if (vectorizable_bytes < blocksize) { - unshuffle_generic_inline(bytesoftype, vectorizable_bytes, blocksize, _src, _dest); - } -} diff --git a/blosc/shuffle-avx2.h b/blosc/shuffle-avx2.h index 98c053ff..535493fd 100644 --- a/blosc/shuffle-avx2.h +++ b/blosc/shuffle-avx2.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/blosc/shuffle-generic.c b/blosc/shuffle-generic.c index ebd07206..1c50a6a5 100644 --- a/blosc/shuffle-generic.c +++ b/blosc/shuffle-generic.c @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/blosc/shuffle-generic.h b/blosc/shuffle-generic.h index 6ed95faf..22218d8e 100644 --- a/blosc/shuffle-generic.h +++ b/blosc/shuffle-generic.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/blosc/shuffle-neon.c b/blosc/shuffle-neon.c index c7cd20e1..7dcfeff5 100644 --- a/blosc/shuffle-neon.c +++ b/blosc/shuffle-neon.c @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 Lucian Marc + Copyright (c) 2021 Lucian Marc https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -9,8 +9,8 @@ **********************************************************************/ -#include "shuffle-generic.h" #include "shuffle-neon.h" +#include "shuffle-generic.h" /* Make sure NEON is available for the compilation target and compiler. */ #if defined(__ARM_NEON) diff --git a/blosc/shuffle-neon.h b/blosc/shuffle-neon.h index 3b54655d..50c489e2 100644 --- a/blosc/shuffle-neon.h +++ b/blosc/shuffle-neon.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/blosc/shuffle-sse2.c b/blosc/shuffle-sse2.c index 7b804eec..a34755ff 100644 --- a/blosc/shuffle-sse2.c +++ b/blosc/shuffle-sse2.c @@ -1,15 +1,15 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ -#include "shuffle-generic.h" #include "shuffle-sse2.h" +#include "shuffle-generic.h" /* Make sure SSE2 is available for the compilation target and compiler. */ #if defined(__SSE2__) diff --git a/blosc/shuffle-sse2.h b/blosc/shuffle-sse2.h index 783641df..263d6705 100644 --- a/blosc/shuffle-sse2.h +++ b/blosc/shuffle-sse2.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/blosc/shuffle.c b/blosc/shuffle.c index c6d95b4a..d75052da 100644 --- a/blosc/shuffle.c +++ b/blosc/shuffle.c @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -9,9 +9,11 @@ **********************************************************************/ #include "shuffle.h" +#include "blosc2.h" #include "blosc2/blosc2-common.h" #include "shuffle-generic.h" #include "bitshuffle-generic.h" + #include #include #include @@ -141,7 +143,7 @@ __cpuidex(int32_t cpuInfo[4], int32_t function_id, int32_t subfunction_id) { // GCC folks added _xgetbv in immintrin.h starting in GCC 9 // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71659 -#if defined(__i386__) && !(defined(_IMMINTRIN_H_INCLUDED) && (BLOSC_GCC_VERSION >= 900)) +#if !(defined(_IMMINTRIN_H_INCLUDED) && (BLOSC_GCC_VERSION >= 900)) && !defined(__IMMINTRIN_H) /* Reads the content of an extended control register. https://software.intel.com/en-us/articles/how-to-detect-new-instruction-support-in-the-4th-generation-intel-core-processor-family */ @@ -160,7 +162,7 @@ _xgetbv(uint32_t xcr) { ); return ((uint64_t)edx << 32) | eax; } -#endif // defined(__i386__) && !(defined(_IMMINTRIN_H_INCLUDED) && (BLOSC_GCC_VERSION >= 900)) +#endif // !(defined(_IMMINTRIN_H_INCLUDED) && (BLOSC_GCC_VERSION >= 900)) && !defined(__IMMINTRIN_H) #endif /* defined(_MSC_VER) */ #ifndef _XCR_XFEATURE_ENABLED_MASK @@ -204,7 +206,7 @@ static blosc_cpu_features blosc_get_cpu_features(void) { bool ymm_state_enabled = false; //bool zmm_state_enabled = false; // commented this out for avoiding an 'unused variable' warning -#if defined(__i386__) && defined(_XCR_XFEATURE_ENABLED_MASK) +#if defined(_XCR_XFEATURE_ENABLED_MASK) if (xsave_available && xsave_enabled_by_os && ( sse2_available || sse3_available || ssse3_available || sse41_available || sse42_available @@ -219,7 +221,7 @@ static blosc_cpu_features blosc_get_cpu_features(void) { restored as well as all of zmm16-zmm31 and the opmask registers. */ //zmm_state_enabled = (xcr0_contents & 0x70) == 0x70; } -#endif /* defined(__i386__) && defined(_XCR_XFEATURE_ENABLED_MASK) */ +#endif /* defined(_XCR_XFEATURE_ENABLED_MASK) */ #if defined(BLOSC_DUMP_CPU_INFO) printf("Shuffle CPU Information:\n"); @@ -433,7 +435,7 @@ bitshuffle(const int32_t bytesoftype, const int32_t blocksize, size, bytesoftype, (void *) _tmp); if (ret < 0) { // Some error in bitshuffle (should not happen) - fprintf(stderr, "the impossible happened: the bitshuffle filter failed!"); + BLOSC_TRACE_ERROR("the impossible happened: the bitshuffle filter failed!"); return ret; } @@ -463,7 +465,7 @@ int32_t bitunshuffle(const int32_t bytesoftype, const int32_t blocksize, bytesoftype, (void *) _tmp); if (ret < 0) { // Some error in bitshuffle (should not happen) - fprintf(stderr, "the impossible happened: the bitunshuffle filter failed!"); + BLOSC_TRACE_ERROR("the impossible happened: the bitunshuffle filter failed!"); return ret; } /* Copy the leftovers (we do so starting from c-blosc 1.18 on) */ @@ -480,7 +482,7 @@ int32_t bitunshuffle(const int32_t bytesoftype, const int32_t blocksize, int ret = (int) (host_implementation.bitunshuffle)((void *) _src, (void *) _dest, size, bytesoftype, (void *) _tmp); if (ret < 0) { - fprintf(stderr, "the impossible happened: the bitunshuffle filter failed!"); + BLOSC_TRACE_ERROR("the impossible happened: the bitunshuffle filter failed!"); return ret; } diff --git a/blosc/shuffle.h b/blosc/shuffle.h index 7f98d297..de710dea 100644 --- a/blosc/shuffle.h +++ b/blosc/shuffle.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/blosc/stune.c b/blosc/stune.c index 76d2d5e6..102c1999 100644 --- a/blosc/stune.c +++ b/blosc/stune.c @@ -1,16 +1,17 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ +#include "stune.h" + #include #include -#include "stune.h" /* Whether a codec is meant for High Compression Ratios @@ -21,7 +22,9 @@ static bool is_HCR(blosc2_context * context) { case BLOSC_BLOSCLZ : return false; case BLOSC_LZ4 : - return (context->filter_flags & BLOSC_DOBITSHUFFLE) ? true : false; + // return (context->filter_flags & BLOSC_DOBITSHUFFLE) ? true : false; + // Do not treat LZ4 differently than BloscLZ here + return false; case BLOSC_LZ4HC : case BLOSC_ZLIB : case BLOSC_ZSTD : diff --git a/blosc/stune.h b/blosc/stune.h index cf78cbf5..dd3cf5ab 100644 --- a/blosc/stune.h +++ b/blosc/stune.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -21,6 +21,7 @@ /* The maximum number of compressed data streams in a block for compression */ #define MAX_STREAMS 16 /* Cannot be larger than 128 */ +#define BLOSC_STUNE 0 void blosc_stune_init(void * config, blosc2_context* cctx, blosc2_context* dctx); @@ -32,15 +33,6 @@ void blosc_stune_update(blosc2_context * context, double ctime); void blosc_stune_free(blosc2_context * context); -static blosc2_btune BTUNE_DEFAULTS = { - .btune_init = blosc_stune_init, - .btune_free = blosc_stune_free, - .btune_update = blosc_stune_update, - .btune_next_cparams = blosc_stune_next_cparams, - .btune_next_blocksize = blosc_stune_next_blocksize, - .btune_config = NULL, -}; - /* Conditions for splitting a block before compressing with a codec. */ int split_block(blosc2_context *context, int32_t typesize, int32_t blocksize); diff --git a/blosc/timestamp.c b/blosc/timestamp.c index c1439713..d855910a 100644 --- a/blosc/timestamp.c +++ b/blosc/timestamp.c @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/blosc/transpose-altivec.h b/blosc/transpose-altivec.h index fab0e5d5..31996c4f 100644 --- a/blosc/transpose-altivec.h +++ b/blosc/transpose-altivec.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc developers and Jerome Kieffer + Copyright (c) 2021 The Blosc Development Team and Jerome Kieffer https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/blosc/trunc-prec.c b/blosc/trunc-prec.c index f4891fc9..d2fb5ab6 100644 --- a/blosc/trunc-prec.c +++ b/blosc/trunc-prec.c @@ -1,19 +1,20 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ -#include -#include -#include "assert.h" #include "trunc-prec.h" #include "blosc2.h" +#include +#include +#include + #define BITS_MANTISSA_FLOAT 23 #define BITS_MANTISSA_DOUBLE 52 @@ -77,8 +78,8 @@ int truncate_precision(int8_t prec_bits, int32_t typesize, int32_t nbytes, return truncate_precision64(prec_bits, nbytes / typesize, (int64_t *)src, (int64_t *)dest); default: - fprintf(stderr, "Error in trunc-prec filter: Precision for typesize %d " - "not handled", (int)typesize); + BLOSC_TRACE_ERROR("Error in trunc-prec filter: Precision for typesize %d not handled", + (int)typesize); return -1; } } diff --git a/blosc/trunc-prec.h b/blosc/trunc-prec.h index 50d53de5..8aa38cec 100644 --- a/blosc/trunc-prec.h +++ b/blosc/trunc-prec.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/blosc/win32/pthread.c b/blosc/win32/pthread.c index c6ff3a44..0ef5bd6f 100644 --- a/blosc/win32/pthread.c +++ b/blosc/win32/pthread.c @@ -33,11 +33,11 @@ #include "pthread.h" -#include -#include -#include -#include -#include +#include "stdio.h" +#include "stdlib.h" +#include "process.h" +#include "errno.h" +#include "limits.h" #define PTHREAD_UNUSED_PARAM(x) ((void)(x)) diff --git a/blosc/win32/pthread.h b/blosc/win32/pthread.h index e1a7ffba..6c47c573 100644 --- a/blosc/win32/pthread.h +++ b/blosc/win32/pthread.h @@ -35,7 +35,7 @@ #define WIN32_LEAN_AND_MEAN #endif -#include +#include "windows.h" /* * Defines that adapt Windows API threads to pthreads API diff --git a/blosc/win32/stdint-windows.h b/blosc/win32/stdint-windows.h deleted file mode 100644 index 4fe0ef9a..00000000 --- a/blosc/win32/stdint-windows.h +++ /dev/null @@ -1,259 +0,0 @@ -// ISO C9x compliant stdint.h for Microsoft Visual Studio -// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 -// -// Copyright (c) 2006-2013 Alexander Chemeris -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the product nor the names of its contributors may -// be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef _MSC_VER // [ -#error "Use this header only with Microsoft Visual C++ compilers!" -#endif // _MSC_VER ] - -#ifndef _MSC_STDINT_H_ // [ -#define _MSC_STDINT_H_ - -#if _MSC_VER > 1000 -#pragma once -#endif - -#if _MSC_VER >= 1600 // [ -#include -#else // ] _MSC_VER >= 1600 [ - -#include - -// For Visual Studio 6 in C++ mode and for many Visual Studio versions when -// compiling for ARM we should wrap include with 'extern "C++" {}' -// or compiler give many errors like this: -// error C2733: second C linkage of overloaded function 'wmemchr' not allowed -#ifdef __cplusplus -extern "C" { -#endif -# include -#ifdef __cplusplus -} -#endif - -// Define _W64 macros to mark types changing their size, like intptr_t. -#ifndef _W64 -# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 -# define _W64 __w64 -# else -# define _W64 -# endif -#endif - - -// 7.18.1 Integer types - -// 7.18.1.1 Exact-width integer types - -// Visual Studio 6 and Embedded Visual C++ 4 doesn't -// realize that, e.g. char has the same size as __int8 -// so we give up on __intX for them. -#if (_MSC_VER < 1300) - typedef signed char int8_t; - typedef signed short int16_t; - typedef signed int int32_t; - typedef unsigned char uint8_t; - typedef unsigned short uint16_t; - typedef unsigned int uint32_t; -#else - typedef signed __int8 int8_t; - typedef signed __int16 int16_t; - typedef signed __int32 int32_t; - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; -#endif -typedef signed __int64 int64_t; -typedef unsigned __int64 uint64_t; - - -// 7.18.1.2 Minimum-width integer types -typedef int8_t int_least8_t; -typedef int16_t int_least16_t; -typedef int32_t int_least32_t; -typedef int64_t int_least64_t; -typedef uint8_t uint_least8_t; -typedef uint16_t uint_least16_t; -typedef uint32_t uint_least32_t; -typedef uint64_t uint_least64_t; - -// 7.18.1.3 Fastest minimum-width integer types -typedef int8_t int_fast8_t; -typedef int16_t int_fast16_t; -typedef int32_t int_fast32_t; -typedef int64_t int_fast64_t; -typedef uint8_t uint_fast8_t; -typedef uint16_t uint_fast16_t; -typedef uint32_t uint_fast32_t; -typedef uint64_t uint_fast64_t; - -// 7.18.1.4 Integer types capable of holding object pointers -#ifdef _WIN64 // [ - typedef signed __int64 intptr_t; - typedef unsigned __int64 uintptr_t; -#else // _WIN64 ][ - typedef _W64 signed int intptr_t; - typedef _W64 unsigned int uintptr_t; -#endif // _WIN64 ] - -// 7.18.1.5 Greatest-width integer types -typedef int64_t intmax_t; -typedef uint64_t uintmax_t; - - -// 7.18.2 Limits of specified-width integer types - -#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 - -// 7.18.2.1 Limits of exact-width integer types -#define INT8_MIN ((int8_t)_I8_MIN) -#define INT8_MAX _I8_MAX -#define INT16_MIN ((int16_t)_I16_MIN) -#define INT16_MAX _I16_MAX -#define INT32_MIN ((int32_t)_I32_MIN) -#define INT32_MAX _I32_MAX -#define INT64_MIN ((int64_t)_I64_MIN) -#define INT64_MAX _I64_MAX -#define UINT8_MAX _UI8_MAX -#define UINT16_MAX _UI16_MAX -#define UINT32_MAX _UI32_MAX -#define UINT64_MAX _UI64_MAX - -// 7.18.2.2 Limits of minimum-width integer types -#define INT_LEAST8_MIN INT8_MIN -#define INT_LEAST8_MAX INT8_MAX -#define INT_LEAST16_MIN INT16_MIN -#define INT_LEAST16_MAX INT16_MAX -#define INT_LEAST32_MIN INT32_MIN -#define INT_LEAST32_MAX INT32_MAX -#define INT_LEAST64_MIN INT64_MIN -#define INT_LEAST64_MAX INT64_MAX -#define UINT_LEAST8_MAX UINT8_MAX -#define UINT_LEAST16_MAX UINT16_MAX -#define UINT_LEAST32_MAX UINT32_MAX -#define UINT_LEAST64_MAX UINT64_MAX - -// 7.18.2.3 Limits of fastest minimum-width integer types -#define INT_FAST8_MIN INT8_MIN -#define INT_FAST8_MAX INT8_MAX -#define INT_FAST16_MIN INT16_MIN -#define INT_FAST16_MAX INT16_MAX -#define INT_FAST32_MIN INT32_MIN -#define INT_FAST32_MAX INT32_MAX -#define INT_FAST64_MIN INT64_MIN -#define INT_FAST64_MAX INT64_MAX -#define UINT_FAST8_MAX UINT8_MAX -#define UINT_FAST16_MAX UINT16_MAX -#define UINT_FAST32_MAX UINT32_MAX -#define UINT_FAST64_MAX UINT64_MAX - -// 7.18.2.4 Limits of integer types capable of holding object pointers -#ifdef _WIN64 // [ -# define INTPTR_MIN INT64_MIN -# define INTPTR_MAX INT64_MAX -# define UINTPTR_MAX UINT64_MAX -#else // _WIN64 ][ -# define INTPTR_MIN INT32_MIN -# define INTPTR_MAX INT32_MAX -# define UINTPTR_MAX UINT32_MAX -#endif // _WIN64 ] - -// 7.18.2.5 Limits of greatest-width integer types -#define INTMAX_MIN INT64_MIN -#define INTMAX_MAX INT64_MAX -#define UINTMAX_MAX UINT64_MAX - -// 7.18.3 Limits of other integer types - -#ifdef _WIN64 // [ -# define PTRDIFF_MIN _I64_MIN -# define PTRDIFF_MAX _I64_MAX -#else // _WIN64 ][ -# define PTRDIFF_MIN _I32_MIN -# define PTRDIFF_MAX _I32_MAX -#endif // _WIN64 ] - -#define SIG_ATOMIC_MIN INT_MIN -#define SIG_ATOMIC_MAX INT_MAX - -#ifndef SIZE_MAX // [ -# ifdef _WIN64 // [ -# define SIZE_MAX _UI64_MAX -# else // _WIN64 ][ -# define SIZE_MAX _UI32_MAX -# endif // _WIN64 ] -#endif // SIZE_MAX ] - -// WCHAR_MIN and WCHAR_MAX are also defined in -#ifndef WCHAR_MIN // [ -# define WCHAR_MIN 0 -#endif // WCHAR_MIN ] -#ifndef WCHAR_MAX // [ -# define WCHAR_MAX _UI16_MAX -#endif // WCHAR_MAX ] - -#define WINT_MIN 0 -#define WINT_MAX _UI16_MAX - -#endif // __STDC_LIMIT_MACROS ] - - -// 7.18.4 Limits of other integer types - -#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 - -// 7.18.4.1 Macros for minimum-width integer constants - -#define INT8_C(val) val##i8 -#define INT16_C(val) val##i16 -#define INT32_C(val) val##i32 -#define INT64_C(val) val##i64 - -#define UINT8_C(val) val##ui8 -#define UINT16_C(val) val##ui16 -#define UINT32_C(val) val##ui32 -#define UINT64_C(val) val##ui64 - -// 7.18.4.2 Macros for greatest-width integer constants -// These #ifndef's are needed to prevent collisions with . -// Check out Issue 9 for the details. -#ifndef INTMAX_C // [ -# define INTMAX_C INT64_C -#endif // INTMAX_C ] -#ifndef UINTMAX_C // [ -# define UINTMAX_C UINT64_C -#endif // UINTMAX_C ] - -#endif // __STDC_CONSTANT_MACROS ] - -#endif // _MSC_VER >= 1600 ] - -#endif // _MSC_STDINT_H_ ] diff --git a/cmake/FindZLIB_NG.cmake b/cmake/FindZLIB_NG.cmake index 0b16ce47..679e8d4c 100644 --- a/cmake/FindZLIB_NG.cmake +++ b/cmake/FindZLIB_NG.cmake @@ -1,7 +1,7 @@ -find_path(ZLIB_NG_INCLUDE_DIRS NAMES zlib-ng.h) +find_path(ZLIB_NG_INCLUDE_DIR NAMES zlib-ng.h) if(ZLIB_INCLUDE_DIRS) - set(ZLIB_NG_LIBRARY_DIRS ${ZLIB_NG_INCLUDE_DIRS}) + set(ZLIB_NG_LIBRARY_DIRS ${ZLIB_NG_INCLUDE_DIR}) if("${ZLIB_NG_LIBRARY_DIRS}" MATCHES "/include$") # Strip off the trailing "/include" in the path. @@ -16,19 +16,19 @@ endif() find_library(ZLIB_NG_LIBRARY NAMES z-ng libz-ng zlib-ng libz-ng.a) set(ZLIB_NG_LIBRARIES ${ZLIB_NG_LIBRARY}) -set(ZLIB_NG_INCLUDE_DIRS ${ZLIB_NG_INCLUDE_DIRS}) +set(ZLIB_NG_INCLUDE_DIR ${ZLIB_NG_INCLUDE_DIR}) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(ZLIB_NG DEFAULT_MSG ZLIB_NG_LIBRARY ZLIB_NG_INCLUDE_DIRS) +find_package_handle_standard_args(ZLIB_NG DEFAULT_MSG ZLIB_NG_LIBRARY ZLIB_NG_INCLUDE_DIR) -if(ZLIB_NG_INCLUDE_DIRS AND ZLIB_NG_LIBRARIES) +if(ZLIB_NG_INCLUDE_DIR AND ZLIB_NG_LIBRARIES) set(ZLIB_NG_FOUND TRUE) -else(ZLIB_NG_INCLUDE_DIRS AND ZLIB_NG_LIBRARIES) +else(ZLIB_NG_INCLUDE_DIR AND ZLIB_NG_LIBRARIES) set(ZLIB_NG_FOUND FALSE) -endif(ZLIB_NG_INCLUDE_DIRS AND ZLIB_NG_LIBRARIES) +endif(ZLIB_NG_INCLUDE_DIR AND ZLIB_NG_LIBRARIES) if(ZLIB_NG_FOUND) - message(STATUS "Found zlib-ng: ${ZLIB_NG_LIBRARIES}, ${ZLIB_NG_INCLUDE_DIRS}") + message(STATUS "Found zlib-ng: ${ZLIB_NG_LIBRARIES}, ${ZLIB_NG_INCLUDE_DIR}") endif(ZLIB_NG_FOUND) #[[ diff --git a/compat/filegen.c b/compat/filegen.c index 12853bf6..baea6464 100644 --- a/compat/filegen.c +++ b/compat/filegen.c @@ -8,27 +8,16 @@ See LICENSES/BLOSC.txt for details about copyright and rights to use. **********************************************************************/ +#include "blosc2.h" + #include -#include #include +#include #if defined(_WIN32) && !defined(__MINGW32__) - #include - /* stdint.h only available in VS2010 (VC++ 16.0) and newer */ - #if defined(_MSC_VER) && _MSC_VER < 1600 - #include "win32/stdint-windows.h" - #else - #include - #endif -#else - #include +#include #endif /* _WIN32 */ -#ifdef __HAIKU__ -/* int32_t declared here */ -#include -#endif - #define SIZE (1000 * 1000) diff --git a/contrib/shuffle_neon/unshuffle16_neon/unshuffle16_neon_vtbx.c b/contrib/shuffle_neon/unshuffle16_neon/unshuffle16_neon_vtbx.c index 1614d2b4..34f2f571 100755 --- a/contrib/shuffle_neon/unshuffle16_neon/unshuffle16_neon_vtbx.c +++ b/contrib/shuffle_neon/unshuffle16_neon/unshuffle16_neon_vtbx.c @@ -43,7 +43,7 @@ void unshuffle16_neon(uint8_t* const dest, const uint8_t* const src, "\xff\xff\x0b\x1b\xff\xff\x0f\x1f"; for(i = 0, k = 0; i < vectorizable_elements*bytesoftype; i += 128, k++) { - /* Load 16 gorups of 8 bytes to the structures */ + /* Load 16 groups of 8 bytes to the structures */ for(j = 0; j < 4; j++) { for (l = 0; l < 4; l++) { r0[j].val[l] = vld1_u8(src + j*total_elements + l*4*total_elements + k*8); diff --git a/contrib/shuffle_neon/unshuffle16_neon/unshuffle16_neon_vtbx_bucle.c b/contrib/shuffle_neon/unshuffle16_neon/unshuffle16_neon_vtbx_bucle.c index ed01dad4..764dfdff 100755 --- a/contrib/shuffle_neon/unshuffle16_neon/unshuffle16_neon_vtbx_bucle.c +++ b/contrib/shuffle_neon/unshuffle16_neon/unshuffle16_neon_vtbx_bucle.c @@ -79,7 +79,7 @@ void unshuffle16_neon(uint8_t* const dest, const uint8_t* const src, "\xff\xff\x0b\x1b\xff\xff\x0f\x1f"; for(i = 0, k = 0; i < vectorizable_elements*bytesoftype; i += 128, k++) { - /* Load 16 gorups of 8 bytes to the structures */ + /* Load 16 groups of 8 bytes to the structures */ //printf("\t\tLoad i = %d\n", i); for(j = 0; j < 4; j++) { for (l = 0; l < 4; l++) { diff --git a/doc/Doxyfile b/doc/Doxyfile index 10f49bf2..df232aa6 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -1,13 +1,13 @@ PROJECT_NAME = "blosc2" XML_OUTPUT = xml -INPUT = ../include/blosc2.h +INPUT = ../include/ GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO GENERATE_HTML = NO GENERATE_XML = YES -RECURSIVE = YES +RECURSIVE = NO QUIET = YES JAVADOC_AUTOBRIEF = YES WARN_IF_UNDOCUMENTED = NO diff --git a/doc/conf.py b/doc/conf.py index f322629b..3698a843 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -12,8 +12,8 @@ project = 'C-Blosc2' -copyright = '2019-present, The Blosc Developers' -author = 'The Blosc Developers' +copyright = '2019-present, The Blosc Development Team' +author = 'The Blosc Development Team' extensions = [ 'breathe', 'sphinx.ext.intersphinx', diff --git a/doc/reference/b2nd.rst b/doc/reference/b2nd.rst new file mode 100644 index 00000000..6325be73 --- /dev/null +++ b/doc/reference/b2nd.rst @@ -0,0 +1,103 @@ +Blosc2 NDim +=========== + +It contains both the data and metalayer that stores the dimensional info for the array. +Blosc2 NDim has an internal context managed that stores the different properties of each array. + +Context +------- + +.. doxygentypedef:: b2nd_context_t + +Creation +++++++++ + +.. doxygenfunction:: b2nd_create_ctx + +Destruction ++++++++++++ + +.. doxygenfunction:: b2nd_free_ctx + +Array +----- + +A Blosc2 NDim array is a n-dimensional object that can be managed by the associated functions. +The functions let users to perform different operations with these arrays like copying, getting, setting or +converting data into buffers or files and vice-versa. +Furthermore, Blosc2 NDim only stores the type size (not the data type), and every item of an array has the same size. + +The `b2nd_array_t` type struct is where all data and metadata for an array is stored: + +.. doxygenstruct:: b2nd_array_t + + +Creation +++++++++ + +Constructors +~~~~~~~~~~~~ + +.. doxygenfunction:: b2nd_uninit +.. doxygenfunction:: b2nd_empty +.. doxygenfunction:: b2nd_zeros +.. doxygenfunction:: b2nd_full + +From/To buffer +~~~~~~~~~~~~~~ + +.. doxygenfunction:: b2nd_from_cbuffer +.. doxygenfunction:: b2nd_to_cbuffer + +From/To file +~~~~~~~~~~~~ + +.. doxygenfunction:: b2nd_open +.. doxygenfunction:: b2nd_open_offset +.. doxygenfunction:: b2nd_save + +From Blosc object +~~~~~~~~~~~~~~~~~ + +.. doxygenfunction:: b2nd_from_schunk +.. doxygenfunction:: b2nd_from_cframe +.. doxygenfunction:: b2nd_to_cframe + +Modify data +~~~~~~~~~~~ + +.. doxygenfunction:: b2nd_insert +.. doxygenfunction:: b2nd_append +.. doxygenfunction:: b2nd_delete + +Copying ++++++++ + +.. doxygenfunction:: b2nd_copy + + +Slicing ++++++++ + +.. doxygenfunction:: b2nd_get_slice +.. doxygenfunction:: b2nd_get_slice_cbuffer +.. doxygenfunction:: b2nd_set_slice_cbuffer +.. doxygenfunction:: b2nd_get_orthogonal_selection +.. doxygenfunction:: b2nd_set_orthogonal_selection +.. doxygenfunction:: b2nd_squeeze +.. doxygenfunction:: b2nd_squeeze_index + + +Utils ++++++ + +.. doxygenfunction:: b2nd_print_meta +.. doxygenfunction:: b2nd_serialize_meta +.. doxygenfunction:: b2nd_deserialize_meta +.. doxygenfunction:: b2nd_resize + + +Destruction ++++++++++++ + +.. doxygenfunction:: b2nd_free diff --git a/doc/reference/index.rst b/doc/reference/index.rst index e8bbae4c..c2f1f90d 100644 --- a/doc/reference/index.rst +++ b/doc/reference/index.rst @@ -22,4 +22,5 @@ metainfo to your datasets (metalayers). context plugins schunk - metalayers \ No newline at end of file + metalayers + b2nd diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index c2e150ff..a8d5defb 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -3,6 +3,8 @@ set(EXAMPLES contexts instrument_codec delta_schunk_ex multithread simple frame_ noinit find_roots schunk_simple frame_simple schunk_postfilter urcodecs urfilters frame_vlmetalayers sframe_simple frame_backed_schunk compress_file frame_offset frame_roundtrip get_set_slice) +add_subdirectory(b2nd) + if(NOT DEACTIVATE_ZSTD) set(EXAMPLES ${EXAMPLES} zstd_dict) endif() @@ -12,10 +14,6 @@ if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8") list(APPEND EXAMPLES frame_big) endif() -if(EXISTS btune_schunk.c) - list(APPEND EXAMPLES btune_schunk) -endif() - # targets foreach(example ${EXAMPLES}) add_executable(${example} ${example}.c) diff --git a/examples/b2nd/CMakeLists.txt b/examples/b2nd/CMakeLists.txt new file mode 100644 index 00000000..4c02aa50 --- /dev/null +++ b/examples/b2nd/CMakeLists.txt @@ -0,0 +1,28 @@ +# Blosc - Blocked Shuffling and Compression Library +# +# Copyright (c) 2021 The Blosc Development Team +# https://blosc.org +# License: BSD 3-Clause (see LICENSE.txt) +# +# See LICENSE.txt for details about copyright and rights to use. + +file(GLOB SOURCES example_*.c) +list(REMOVE_ITEM SOURCES example_print_meta.c) +if (NOT HAVE_PLUGINS) + file(GLOB NEED_PLUGINS example_plugins_*.c) + list(REMOVE_ITEM SOURCES ${NEED_PLUGINS}) +endif () + +foreach (source ${SOURCES}) + get_filename_component(target_name ${source} NAME_WE) + set(target b2nd_${target_name}) + add_executable(${target} ${target_name}.c) + target_link_libraries(${target} blosc_testing ${LIBS}) + if (${target_name} STREQUAL example_print_meta) + add_test(NAME ${target} COMMAND ${target} + "${PROJECT_BINARY_DIR}/examples/b2nd/example_big_float_frame.b2nd") + else () + add_test(NAME ${target} COMMAND ${target}) + endif () + set_tests_properties(${target} PROPERTIES LABELS "b2nd") +endforeach (source) diff --git a/examples/b2nd/example_empty_shape.c b/examples/b2nd/example_empty_shape.c new file mode 100644 index 00000000..9e548073 --- /dev/null +++ b/examples/b2nd/example_empty_shape.c @@ -0,0 +1,79 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +# include + +int main() { + + blosc2_init(); + + int8_t ndim = 2; + int64_t shape[] = {10, 10}; + int32_t chunkshape[] = {4, 4}; + int32_t blockshape[] = {2, 2}; + int32_t typesize = 8; + + int64_t slice_start[] = {2, 5}; + int64_t slice_stop[] = {2, 6}; + int32_t slice_chunkshape[] = {0, 1}; + int32_t slice_blockshape[] = {0, 1}; + + int64_t nelem = 1; + for (int i = 0; i < ndim; ++i) { + nelem *= shape[i]; + } + int64_t size = nelem * typesize; + int8_t *data = malloc(size); + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.typesize = typesize; + + blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; + blosc2_storage b2_storage = {.cparams=&cparams, .dparams=&dparams}; + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, ndim, shape, + chunkshape, blockshape, NULL, 0, NULL, 0); + + b2nd_array_t *arr; + BLOSC_ERROR(b2nd_from_cbuffer(ctx, &arr, data, size)); + + BLOSC_ERROR(b2nd_free_ctx(ctx)); + + blosc2_storage slice_b2_storage = {.cparams=&cparams, .dparams=&dparams}; + slice_b2_storage.urlpath = "example_hola.b2frame"; + blosc2_remove_urlpath(slice_b2_storage.urlpath); + + b2nd_context_t *slice_ctx = b2nd_create_ctx(&slice_b2_storage, ndim, shape, + slice_chunkshape, slice_blockshape, NULL, 0, NULL, 0); + + b2nd_array_t *slice; + BLOSC_ERROR(b2nd_get_slice(slice_ctx, &slice, arr, slice_start, slice_stop)); + BLOSC_ERROR(b2nd_free(arr)); + BLOSC_ERROR(b2nd_free_ctx(slice_ctx)); + + uint8_t *buffer; + uint64_t buffer_size = 1; + for (int i = 0; i < slice->ndim; ++i) { + buffer_size *= slice->shape[i]; + } + buffer_size *= slice->sc->typesize; + buffer = malloc(buffer_size); + + BLOSC_ERROR(b2nd_to_cbuffer(slice, buffer, buffer_size)); + BLOSC_ERROR(b2nd_free(slice)); + free(data); + free(buffer); + + // printf("Elapsed seconds: %.5f\n", blosc_elapsed_secs(t0, t1)); + + blosc2_destroy(); + + return 0; +} diff --git a/examples/b2nd/example_frame_generator.c b/examples/b2nd/example_frame_generator.c new file mode 100644 index 00000000..c7330058 --- /dev/null +++ b/examples/b2nd/example_frame_generator.c @@ -0,0 +1,359 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +# include +# include +# include "time.h" + + +int frame_generator(int8_t *data, int8_t ndim, int64_t *shape, int32_t *chunkshape, + int32_t *blockshape, int32_t typesize, int64_t size, char *urlpath) { + blosc2_remove_urlpath(urlpath); + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.typesize = typesize; + blosc2_storage b2_storage = {.cparams=&cparams}; + b2_storage.urlpath = urlpath; + b2_storage.contiguous = true; + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, ndim, shape, chunkshape, blockshape, NULL, 0, + NULL, 0); + + b2nd_array_t *arr; + BLOSC_ERROR(b2nd_from_cbuffer(ctx, &arr, data, size)); + BLOSC_ERROR(b2nd_free_ctx(ctx)); + b2nd_print_meta(arr); + BLOSC_ERROR(b2nd_free(arr)); + + return 0; +} + +int rand_() { + int ndim = 3; + int typesize = 4; + int64_t shape[] = {32, 18, 32}; + int32_t chunkshape[] = {17, 16, 24}; + int32_t blockshape[] = {8, 9, 8}; + int64_t nelem = 1; + for (int i = 0; i < ndim; ++i) { + nelem *= (int) (shape[i]); + } + int64_t size = typesize * nelem; + float *data = malloc(size); + for (int64_t i = 0; i < nelem; i++) { + data[i] = (float) (rand() % 220); + } + char *urlpath = "rand.b2nd"; + BLOSC_ERROR(frame_generator((int8_t *) data, ndim, shape, chunkshape, blockshape, typesize, size, urlpath)); + + return 0; +} + +int all_eq() { + int8_t ndim = 3; + int64_t shape[] = {100, 50, 100}; + int32_t chunkshape[] = {40, 20, 60}; + int32_t blockshape[] = {20, 10, 30}; + int32_t typesize = 8; + int64_t nelem = 1; + for (int i = 0; i < ndim; ++i) { + nelem *= shape[i]; + } + int64_t size = nelem * typesize; + + int8_t *data = malloc(size); + for (int i = 0; i < nelem; i++) { + data[i] = (int8_t) 22; + } + char *urlpath = "all_eq.b2nd"; + BLOSC_ERROR(frame_generator(data, ndim, shape, chunkshape, blockshape, typesize, size, urlpath)); + + return 0; +} + +int cyclic() { + int8_t ndim = 3; + int64_t shape[] = {100, 50, 100}; + int32_t chunkshape[] = {40, 20, 60}; + int32_t blockshape[] = {20, 10, 30}; + int32_t typesize = 8; + int64_t nelem = 1; + for (int i = 0; i < ndim; ++i) { + nelem *= shape[i]; + } + int64_t size = nelem * typesize; + + int8_t *data = malloc(size); + for (int i = 0; i < nelem; i++) { + data[i] = (int8_t) i; + } + char *urlpath = "cyclic.b2nd"; + BLOSC_ERROR(frame_generator(data, ndim, shape, chunkshape, blockshape, typesize, size, urlpath)); + + return 0; +} + +int same_cells() { + int ndim = 2; + int typesize = 8; + int64_t shape[] = {128, 111}; + int32_t chunkshape[] = {32, 11}; + int32_t blockshape[] = {16, 7}; + int64_t nelem = 1; + for (int i = 0; i < ndim; ++i) { + nelem *= (int) (shape[i]); + } + int64_t size = typesize * nelem; + double *data = malloc(size); + for (int64_t i = 0; i < (nelem / 4); i++) { + data[i * 4] = (double) 11111111; + data[i * 4 + 1] = (double) 99999999; + } + char *urlpath = "same_cells.b2nd"; + BLOSC_ERROR(frame_generator((int8_t *) data, ndim, shape, chunkshape, blockshape, typesize, size, urlpath)); + + return 0; +} + +int some_matches() { + int ndim = 2; + int typesize = 8; + int64_t shape[] = {128, 111}; + int32_t chunkshape[] = {48, 32}; + int32_t blockshape[] = {14, 18}; + int64_t nelem = 1; + for (int i = 0; i < ndim; ++i) { + nelem *= (int) (shape[i]); + } + int64_t size = typesize * nelem; + double *data = malloc(size); + for (int64_t i = 0; i < (nelem / 2); i++) { + data[i] = (double) i; + } + for (int64_t i = (nelem / 2); i < nelem; i++) { + data[i] = (double) 1; + } + char *urlpath = "some_matches.b2nd"; + BLOSC_ERROR(frame_generator((int8_t *) data, ndim, shape, chunkshape, blockshape, typesize, size, urlpath)); + + return 0; +} + +int many_matches() { + int8_t ndim = 3; + int64_t shape[] = {80, 120, 111}; + int32_t chunkshape[] = {40, 30, 50}; + int32_t blockshape[] = {11, 14, 24}; + int32_t typesize = 8; + int64_t nelem = 1; + for (int i = 0; i < ndim; ++i) { + nelem *= shape[i]; + } + int64_t size = nelem * typesize; + + int8_t *data = malloc(size); + for (int i = 0; i < nelem; i += 2) { + data[i] = (int8_t) i; + data[i + 1] = (int8_t) 2; + } + char *urlpath = "many_matches.b2nd"; + BLOSC_ERROR(frame_generator(data, ndim, shape, chunkshape, blockshape, typesize, size, urlpath)); + + return 0; +} + +int float_cyclic() { + int8_t ndim = 3; + int64_t shape[] = {40, 60, 20}; + int32_t chunkshape[] = {20, 30, 16}; + int32_t blockshape[] = {11, 14, 7}; + int32_t typesize = sizeof(float); + int64_t nelem = 1; + for (int i = 0; i < ndim; ++i) { + nelem *= shape[i]; + } + int64_t size = nelem * typesize; + + float *data = malloc(size); + for (int i = 0; i < nelem; i += 2) { + float j = (float) i; + data[i] = (j + j / 10 + j / 100); + data[i + 1] = (2 + j / 10 + j / 1000); + } + char *urlpath = "example_float_cyclic.b2nd"; + BLOSC_ERROR(frame_generator((int8_t *) data, ndim, shape, chunkshape, blockshape, typesize, size, urlpath)); + + return 0; +} + +int double_same_cells() { + int8_t ndim = 2; + int64_t shape[] = {40, 60}; + int32_t chunkshape[] = {20, 30}; + int32_t blockshape[] = {16, 16}; + int32_t typesize = sizeof(double); + int64_t nelem = 1; + for (int i = 0; i < ndim; ++i) { + nelem *= shape[i]; + } + int64_t size = nelem * typesize; + + double *data = malloc(size); + for (int i = 0; i < nelem; i += 4) { + data[i] = 1.5; + data[i + 1] = 14.7; + data[i + 2] = 23.6; + data[i + 3] = 3.2; + } + char *urlpath = "example_double_same_cells.b2nd"; + BLOSC_ERROR(frame_generator((int8_t *) data, ndim, shape, chunkshape, blockshape, typesize, size, urlpath)); + + return 0; +} + +int big_float_frame() { + int ndim = 3; + int64_t shape[] = {200, 310, 214}; + int32_t chunkshape[] = {110, 120, 76}; + int32_t blockshape[] = {57, 52, 35}; + int32_t typesize = sizeof(float); + int64_t nelem = 1; + for (int i = 0; i < ndim; ++i) { + nelem *= shape[i]; + } + int64_t size = nelem * typesize; + + float *data = malloc(size); + for (int i = 0; i < nelem; i += 4) { + float j = (float) i; + data[i] = (float) 2.73; + data[i + 1] = (2 + j / 10 + j / 1000); + data[i + 2] = (7 + j / 10 - j / 100); + data[i + 3] = (11 + j / 100 - j / 1000); + } + char *urlpath = "example_big_float_frame.b2nd"; + BLOSC_ERROR(frame_generator((int8_t *) data, ndim, shape, chunkshape, blockshape, typesize, size, urlpath)); + + return 0; +} + +int day_month_temp() { + int ndim = 2; + int64_t shape[] = {400, 3}; + int32_t chunkshape[] = {110, 3}; + int32_t blockshape[] = {57, 3}; + int32_t typesize = sizeof(float); + int64_t nelem = 1; + for (int i = 0; i < ndim; ++i) { + nelem *= shape[i]; + } + int64_t size = nelem * typesize; + + float temp_min = -20; + float temp_max = 40; + srand(time(NULL)); + float *data = malloc(size); + for (int i = 0; i < nelem / 3; i++) { + data[i] = (float) (rand() % 31); + data[i + 1] = (float) (rand() % 12); + data[i + 2] = ((float) (rand() % 10000) / 10000 * (temp_max - temp_min) + temp_min); + i += 3; + } + char *urlpath = "example_day_month_temp.b2nd"; + BLOSC_ERROR(frame_generator((int8_t *) data, ndim, shape, chunkshape, blockshape, typesize, size, urlpath)); + + return 0; +} + +int item_prices() { + int ndim = 3; + int64_t shape[] = {12, 25, 250}; + int32_t chunkshape[] = {8, 10, 50}; + int32_t blockshape[] = {4, 5, 10}; + int32_t typesize = sizeof(float); + int64_t nelem = 1; + for (int i = 0; i < ndim; ++i) { + nelem *= shape[i]; + } + int64_t size = nelem * typesize; + + float price_min = (float) 1; // if I choose 0.99 results are less aproppiate + float price_max = (float) 251; + float *data = malloc(size); + int index = 0; + for (int month = 1; month <= shape[0]; month++) { // month (1 to 12) + for (int store = 1; store <= shape[1]; store++) { // store ID (less to more expensive) + for (int item = 1; item <= shape[2]; item++) { // item ID + srand(item); + data[index] = ((float) store + (float) (3 - (month % 3)) * + ((float) (rand() % 1000) / 1000 * (price_max - price_min) + price_min)); + index++; + } + } + } + char *urlpath = "example_item_prices.b2nd"; + BLOSC_ERROR(frame_generator((int8_t *) data, ndim, shape, chunkshape, blockshape, typesize, size, urlpath)); + + return 0; +} + + +int main() { + blosc2_init(); + + int err; + err = rand_(); + if (err != BLOSC2_ERROR_SUCCESS) { + printf("\n Rand_ error: %d", err); + } + err = all_eq(); + if (err != BLOSC2_ERROR_SUCCESS) { + printf("\n All_eq error: %d", err); + } + err = cyclic(); + if (err != BLOSC2_ERROR_SUCCESS) { + printf("\n Cyclic error: %d", err); + } + err = same_cells(); + if (err != BLOSC2_ERROR_SUCCESS) { + printf("\n Same_cells error: %d", err); + } + err = some_matches(); + if (err != BLOSC2_ERROR_SUCCESS) { + printf("\n Some_matches error: %d", err); + } + err = many_matches(); + if (err != BLOSC2_ERROR_SUCCESS) { + printf("\n Many_matches error: %d", err); + } + err = float_cyclic(); + if (err != BLOSC2_ERROR_SUCCESS) { + printf("\n Float_cyclic error: %d", err); + } + err = double_same_cells(); + if (err != BLOSC2_ERROR_SUCCESS) { + printf("\n Double_same_cells error: %d", err); + } + err = big_float_frame(); + if (err != BLOSC2_ERROR_SUCCESS) { + printf("\n Double_same_cells error: %d", err); + } + err = day_month_temp(); + if (err != BLOSC2_ERROR_SUCCESS) { + printf("\n Day_month_temp error: %d", err); + } + err = item_prices(); + if (err != BLOSC2_ERROR_SUCCESS) { + printf("\n Item_prices error: %d", err); + } + + blosc2_destroy(); + + return err; +} diff --git a/examples/b2nd/example_oindex.c b/examples/b2nd/example_oindex.c new file mode 100644 index 00000000..f9fdae8b --- /dev/null +++ b/examples/b2nd/example_oindex.c @@ -0,0 +1,73 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +#include + +int main() { + + blosc2_init(); + + int8_t ndim = 2; + int64_t shape[] = {10, 10}; + int32_t chunkshape[] = {4, 4}; + int32_t blockshape[] = {2, 2}; + int32_t typesize = 8; + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.typesize = typesize; + blosc2_storage b2_storage = {.cparams=&cparams}; + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, ndim, shape, chunkshape, blockshape, NULL, 0, + NULL, 0); + + int64_t dataitems = 1; + for (int i = 0; i < ndim; ++i) { + dataitems *= shape[i]; + } + int64_t datasize = dataitems * typesize; + double *data = malloc(datasize); + for (int i = 0; i < dataitems; ++i) { + data[i] = (double) i; + } + b2nd_array_t *arr; + BLOSC_ERROR(b2nd_from_cbuffer(ctx, &arr, data, datasize)); + free(data); + + int64_t sel0[] = {3, 1, 2}; + int64_t sel1[] = {2, 5}; + int64_t sel2[] = {3, 3, 3, 9, 3, 1, 0}; + int64_t *selection[] = {sel0, sel1, sel2}; + int64_t selection_size[] = {sizeof(sel0) / sizeof(int64_t), sizeof(sel1) / (sizeof(int64_t)), + sizeof(sel2) / (sizeof(int64_t))}; + int64_t *buffershape = selection_size; + int64_t nitems = 1; + for (int i = 0; i < ndim; ++i) { + nitems *= buffershape[i]; + } + int64_t buffersize = nitems * arr->sc->typesize; + double *buffer = calloc(nitems, arr->sc->typesize); + BLOSC_ERROR(b2nd_set_orthogonal_selection(arr, selection, selection_size, buffer, buffershape, buffersize)); + BLOSC_ERROR(b2nd_get_orthogonal_selection(arr, selection, selection_size, buffer, buffershape, buffersize)); + + printf("Results: \n"); + for (int i = 0; i < nitems; ++i) { + if (i % buffershape[1] == 0) { + printf("\n"); + } + printf(" %f ", buffer[i]); + } + printf("\n"); + free(buffer); + BLOSC_ERROR(b2nd_free(arr)); + BLOSC_ERROR(b2nd_free_ctx(ctx)); + + blosc2_destroy(); + + return 0; +} diff --git a/examples/b2nd/example_plainbuffer.c b/examples/b2nd/example_plainbuffer.c new file mode 100644 index 00000000..4b4be3fe --- /dev/null +++ b/examples/b2nd/example_plainbuffer.c @@ -0,0 +1,81 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +#include + +int main() { + + blosc2_init(); + + int8_t ndim = 2; + int64_t shape[] = {10, 10}; + int32_t chunkshape[] = {4, 4}; + int32_t blockshape[] = {2, 2}; + int32_t typesize = 8; + + int64_t slice_start[] = {2, 5}; + int64_t slice_stop[] = {3, 6}; + int32_t slice_chunkshape[] = {1, 1}; + int32_t slice_blockshape[] = {1, 1}; + + int64_t nelem = 1; + for (int i = 0; i < ndim; ++i) { + nelem *= shape[i]; + } + int64_t size = nelem * typesize; + int8_t *data = malloc(size); + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.typesize = typesize; + blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; + dparams.nthreads = 2; + blosc2_storage b2_storage = {.cparams=&cparams, .dparams=&dparams}; + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, ndim, shape, chunkshape, blockshape, NULL, 0, + NULL, 0); + + b2nd_array_t *arr; + BLOSC_ERROR(b2nd_from_cbuffer(ctx, &arr, data, size)); + + + blosc2_storage slice_b2_storage = {.cparams=&cparams, .dparams=&dparams}; + + // shape will be overwritten by get_slice + b2nd_context_t *slice_ctx = b2nd_create_ctx(&slice_b2_storage, ndim, shape, slice_chunkshape, + slice_blockshape, NULL, 0, + NULL, 0); + + b2nd_array_t *slice; + BLOSC_ERROR(b2nd_get_slice(slice_ctx, &slice, arr, slice_start, slice_stop)); + + BLOSC_ERROR(b2nd_squeeze(slice)); + + uint8_t *buffer; + uint64_t buffer_size = 1; + for (int i = 0; i < slice->ndim; ++i) { + buffer_size *= slice->shape[i]; + } + buffer_size *= slice->sc->typesize; + buffer = malloc(buffer_size); + + BLOSC_ERROR(b2nd_to_cbuffer(slice, buffer, buffer_size)); + + BLOSC_ERROR(b2nd_free(arr)); + BLOSC_ERROR(b2nd_free(slice)); + BLOSC_ERROR(b2nd_free_ctx(ctx)); + BLOSC_ERROR(b2nd_free_ctx(slice_ctx)); + free(buffer); + free(data); + + blosc2_destroy(); + // printf("Elapsed seconds: %.5f\n", blosc_elapsed_secs(t0, t1)); + + return 0; +} diff --git a/examples/b2nd/example_plugins_codecs.c b/examples/b2nd/example_plugins_codecs.c new file mode 100644 index 00000000..6587c8ed --- /dev/null +++ b/examples/b2nd/example_plugins_codecs.c @@ -0,0 +1,103 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +/* Example program demonstrating use of the Blosc plugins from C code. +* +* To compile this program: +* $ gcc example_plugins_codecs.c -o example_plugins_codecs -lblosc2 +* +* To run: +* $ ./example_plugins_codecs +* +* from_buffer: 0.0668 s +* to_buffer: 0.0068 s +* Process finished with exit code 0 +*/ + + + +#include +#include +#include +#include "../../plugins/codecs/codecs-registry.c" +#include + +int main() { + blosc_timestamp_t t0, t1; + + blosc2_init(); + int8_t ndim = 2; + uint8_t typesize = sizeof(int64_t); + + int64_t shape[] = {745, 400}; + int32_t chunkshape[] = {150, 100}; + int32_t blockshape[] = {21, 30}; + + int64_t nbytes = typesize; + for (int i = 0; i < ndim; ++i) { + nbytes *= shape[i]; + } + + int64_t *src = malloc((size_t) nbytes); + for (int i = 0; i < nbytes / typesize; ++i) { + src[i] = (int64_t) i; + } + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.nthreads = 1; + /* + * Use the NDLZ codec through its plugin. + * NDLZ metainformation: - it calls the 4x4 version if meta == 4 + - it calls the 8x8 version if meta == 8 + */ + cparams.compcode = BLOSC_CODEC_NDLZ; + cparams.splitmode = BLOSC_ALWAYS_SPLIT; + cparams.compcode_meta = 4; + cparams.clevel = 5; + cparams.typesize = typesize; + // We could use a filter plugin by setting cparams.filters[]. + + blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; + blosc2_storage b2_storage = {.cparams=&cparams, .dparams=&dparams}; + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, ndim, shape, chunkshape, blockshape, NULL, 0, + NULL, 0); + BLOSC_ERROR_NULL(ctx, -1); + b2nd_array_t *arr; + blosc_set_timestamp(&t0); + BLOSC_ERROR(b2nd_from_cbuffer(ctx, &arr, src, nbytes)); + blosc_set_timestamp(&t1); + printf("from_buffer: %.4f s\n", blosc_elapsed_secs(t0, t1)); + + int64_t *buffer = malloc(nbytes); + int64_t buffer_size = nbytes; + blosc_set_timestamp(&t0); + BLOSC_ERROR(b2nd_to_cbuffer(arr, buffer, buffer_size)); + blosc_set_timestamp(&t1); + printf("to_buffer: %.4f s\n", blosc_elapsed_secs(t0, t1)); + + for (int i = 0; i < buffer_size / typesize; i++) { + if (src[i] != buffer[i]) { + printf("\n Decompressed data differs from original!\n"); + printf("i: %d, data %" PRId64 ", dest %" PRId64 "", i, src[i], buffer[i]); + return -1; + } + } + + free(src); + free(buffer); + + BLOSC_ERROR(b2nd_free(arr)); + BLOSC_ERROR(b2nd_free_ctx(ctx)); + + blosc2_destroy(); + + return 0; +} diff --git a/examples/b2nd/example_plugins_filters.c b/examples/b2nd/example_plugins_filters.c new file mode 100644 index 00000000..33551d03 --- /dev/null +++ b/examples/b2nd/example_plugins_filters.c @@ -0,0 +1,100 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +/* Example program demonstrating use of the Blosc plugins from C code. +* +* To compile this program: +* $ gcc example_plugins_filters.c -o example_plugins_filters -lblosc2 +* +* To run: +* $ ./example_plugins_filters +* +* from_buffer: 0.0668 s +* to_buffer: 0.0068 s +* Process finished with exit code 0 +*/ + + + +#include +#include +#include +#include "../../plugins/filters/filters-registry.c" +#include + +int main() { + blosc_timestamp_t t0, t1; + + blosc2_init(); + int8_t ndim = 3; + int32_t typesize = sizeof(int64_t); + + int64_t shape[] = {345, 200, 50}; + int32_t chunkshape[] = {150, 100, 50}; + int32_t blockshape[] = {21, 30, 27}; + + int64_t nbytes = typesize; + for (int i = 0; i < ndim; ++i) { + nbytes *= shape[i]; + } + + int64_t *src = malloc((size_t) nbytes); + for (int i = 0; i < nbytes / typesize; ++i) { + src[i] = (int64_t) i; + } + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.nthreads = 1; + /* + * Use the NDCELL filter through its plugin. + * NDCELL metainformation: user must specify the parameter meta as the cellshape, so + * if in a 3-dim dataset user specifies meta = 4, then cellshape will be 4x4x4. + */ + cparams.filters[4] = BLOSC_FILTER_NDCELL; + cparams.filters_meta[4] = 4; + cparams.typesize = typesize; + // We could use a codec plugin by setting cparams.compcodec. + + blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; + blosc2_storage b2_storage = {.cparams=&cparams, .dparams=&dparams}; + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, ndim, shape, chunkshape, blockshape, NULL, 0, + NULL, 0); + BLOSC_ERROR_NULL(ctx, -1); + blosc_set_timestamp(&t0); + b2nd_array_t *arr; + BLOSC_ERROR(b2nd_from_cbuffer(ctx, &arr, src, nbytes)); + blosc_set_timestamp(&t1); + printf("from_buffer: %.4f s\n", blosc_elapsed_secs(t0, t1)); + + int64_t *buffer = malloc(nbytes); + int64_t buffer_size = nbytes; + blosc_set_timestamp(&t0); + BLOSC_ERROR(b2nd_to_cbuffer(arr, buffer, buffer_size)); + blosc_set_timestamp(&t1); + printf("to_buffer: %.4f s\n", blosc_elapsed_secs(t0, t1)); + + for (int i = 0; i < buffer_size / typesize; i++) { + if (src[i] != buffer[i]) { + printf("\n Decompressed data differs from original!\n"); + printf("i: %d, data %" PRId64 ", dest %" PRId64 "", i, src[i], buffer[i]); + return -1; + } + } + + free(src); + free(buffer); + + BLOSC_ERROR(b2nd_free(arr)); + BLOSC_ERROR(b2nd_free_ctx(ctx)); + + blosc2_destroy(); + + return 0; +} diff --git a/examples/b2nd/example_print_meta.c b/examples/b2nd/example_print_meta.c new file mode 100644 index 00000000..7e2c76e3 --- /dev/null +++ b/examples/b2nd/example_print_meta.c @@ -0,0 +1,53 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +/* Example program demonstrating how to print metainfo from a b2nd frame. + * You can build frames with example_frame_generator.c + * + * Usage: + * $ ./example_print_meta + * + * Example of output: + * $ ./example_print_meta example_big_float_frame.b2nd + * Blosc2 NDim metalayer parameters: + * Ndim: 3 + * Shape: 200, 310, 214 + * Chunkshape: 110, 120, 76 + * Blockshape: 57, 52, 35 + * +*/ + +# include + +int print_meta(char *urlpath) { + + b2nd_array_t *arr; + BLOSC_ERROR(b2nd_open(urlpath, &arr)); + BLOSC_ERROR(b2nd_print_meta(arr)); + BLOSC_ERROR(b2nd_free(arr)); + + return 0; +} + +int main(int argc, char *argv[]) { + if (argc != 2) { + printf("Usage: %s urlpath", argv[0]); + exit(-1); + } + + blosc2_init(); + + char *urlpath = argv[1]; + print_meta(urlpath); + + blosc2_destroy(); + + return 0; +} diff --git a/examples/b2nd/example_serialize.c b/examples/b2nd/example_serialize.c new file mode 100644 index 00000000..88ad0c1c --- /dev/null +++ b/examples/b2nd/example_serialize.c @@ -0,0 +1,79 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +# include + +int main() { + + blosc2_init(); + + int8_t ndim = 2; + int64_t shape[] = {10, 10}; + int32_t chunkshape[] = {4, 4}; + int32_t blockshape[] = {2, 2}; + int32_t typesize = 8; + + int64_t nelem = 1; + for (int i = 0; i < ndim; ++i) { + nelem *= shape[i]; + } + int64_t size = nelem * typesize; + double *data = malloc(size); + + for (int i = 0; i < nelem; ++i) { + data[i] = i; + } + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.typesize = typesize; + blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; + blosc2_storage b2_storage = {.cparams=&cparams, .dparams=&dparams}; + b2_storage.contiguous = false; + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, ndim, shape, chunkshape, blockshape, NULL, 0, + NULL, 0); + + b2nd_array_t *arr; + BLOSC_ERROR(b2nd_from_cbuffer(ctx, &arr, data, size)); + + uint8_t *cframe; + int64_t cframe_len; + bool needs_free; + BLOSC_ERROR(b2nd_to_cframe(arr, &cframe, &cframe_len, &needs_free)); + + b2nd_array_t *dest; + BLOSC_ERROR(b2nd_from_cframe(cframe, cframe_len, true, &dest)); + + if (needs_free) { + free(cframe); + } + + /* Fill dest array with b2nd_array_t data */ + uint8_t *data_dest = malloc(size); + BLOSC_ERROR(b2nd_to_cbuffer(dest, data_dest, size)); + + for (int i = 0; i < nelem; ++i) { + if (data[i] != data_dest[i] && data[i] != i) { + return -1; + } + } + + /* Free mallocs */ + free(data); + free(data_dest); + + BLOSC_ERROR(b2nd_free(arr)); + BLOSC_ERROR(b2nd_free(dest)); + BLOSC_ERROR(b2nd_free_ctx(ctx)); + + blosc2_destroy(); + + return 0; +} diff --git a/examples/compress_file.c b/examples/compress_file.c index e67d1ad6..12502e34 100644 --- a/examples/compress_file.c +++ b/examples/compress_file.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/examples/contexts.c b/examples/contexts.c index 59da64e9..06694f45 100644 --- a/examples/contexts.c +++ b/examples/contexts.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -28,6 +28,8 @@ int main(void) { + blosc2_init(); + static float data[SIZE]; static float data_out[SIZE]; static float data_dest[SIZE]; @@ -109,5 +111,7 @@ int main(void) { } printf("Successful roundtrip!\n"); + blosc2_destroy(); + return 0; } diff --git a/examples/delta_schunk_ex.c b/examples/delta_schunk_ex.c index d4c258ab..4bbd55ed 100644 --- a/examples/delta_schunk_ex.c +++ b/examples/delta_schunk_ex.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/examples/find_roots.c b/examples/find_roots.c index 6d021d50..14a29621 100644 --- a/examples/find_roots.c +++ b/examples/find_roots.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/examples/frame_backed_schunk.c b/examples/frame_backed_schunk.c index 6f3dd08f..c7cb3645 100644 --- a/examples/frame_backed_schunk.c +++ b/examples/frame_backed_schunk.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -35,6 +35,8 @@ int main(void) { + blosc2_init(); + static int32_t data[CHUNKSIZE]; static int32_t data_dest1[CHUNKSIZE]; static int32_t data_dest2[CHUNKSIZE]; @@ -126,5 +128,7 @@ int main(void) { blosc2_schunk_free(schunk1); blosc2_schunk_free(schunk2); + blosc2_destroy(); + return 0; } diff --git a/examples/frame_big.c b/examples/frame_big.c index 8bf927aa..731d7555 100644 --- a/examples/frame_big.c +++ b/examples/frame_big.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/examples/frame_metalayers.c b/examples/frame_metalayers.c index 0f213ffe..964869bc 100644 --- a/examples/frame_metalayers.c +++ b/examples/frame_metalayers.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/examples/frame_offset.c b/examples/frame_offset.c index ee2db943..3bea0ff0 100644 --- a/examples/frame_offset.c +++ b/examples/frame_offset.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -129,8 +129,8 @@ int main(void) { blosc2_schunk* schunk3o = blosc2_schunk_open_offset("file:///frame_simple.b2frame", offset); blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); - printf("Time for fileframe (%s) + offset %lld -> frame3 : %.3g s, %.1f GB/s\n", - schunk3o->storage->urlpath, offset, ttotal, (double)schunk3o->nbytes / (ttotal * GB)); + printf("Time for fileframe (%s) + offset -> frame3 : %.3g s, %.1f GB/s\n", + schunk3o->storage->urlpath, ttotal, (double)schunk3o->nbytes / (ttotal * GB)); uint8_t* cframe2, *cframe3; bool cframe_needs_free2, cframe_needs_free3; diff --git a/examples/frame_roundtrip.c b/examples/frame_roundtrip.c index a74d2b91..f144e962 100644 --- a/examples/frame_roundtrip.c +++ b/examples/frame_roundtrip.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -23,6 +23,8 @@ int main(void) { + blosc2_init(); + int32_t total_bytes = CHUNKSIZE * sizeof(int32_t); int32_t *buf = calloc(CHUNKSIZE, sizeof(int32_t)); @@ -130,6 +132,8 @@ int main(void) { free(buf); printf("All good!\n"); + blosc2_destroy(); + return 0; failed: diff --git a/examples/frame_simple.c b/examples/frame_simple.c index f13ef67a..f517bb5a 100644 --- a/examples/frame_simple.c +++ b/examples/frame_simple.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -27,7 +27,6 @@ */ #include -#include #include #define KB 1024. @@ -74,7 +73,10 @@ int main(void) { data[i] = i * nchunk; } nchunks = blosc2_schunk_append_buffer(schunk, data, isize); - assert(nchunks == nchunk + 1); + if (nchunks != nchunk + 1) { + printf("blosc2_schunk_append_buffer is not working correctly"); + return BLOSC2_ERROR_FAILURE; + } } // Add some vlmetalayers data @@ -174,8 +176,10 @@ int main(void) { } /* Check integrity of this chunk */ for (i = 0; i < CHUNKSIZE; i++) { - assert (data_dest[i] == i * nchunk); - assert (data_dest2[i] == i * nchunk); + if ((data_dest[i] != i * nchunk) || (data_dest2[i] != i * nchunk)) { + printf("data mismatch"); + return BLOSC2_ERROR_FAILURE; + } } } printf("Successful roundtrip schunk <-> frame <-> fileframe !\n"); diff --git a/examples/frame_vlmetalayers.c b/examples/frame_vlmetalayers.c index 3fbaf0e9..a3698195 100644 --- a/examples/frame_vlmetalayers.c +++ b/examples/frame_vlmetalayers.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/examples/get_set_slice.c b/examples/get_set_slice.c index 764d567d..51c92982 100644 --- a/examples/get_set_slice.c +++ b/examples/get_set_slice.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -33,6 +33,8 @@ #define NTHREADS 4 int main(void) { + blosc2_init(); + static int32_t data[CHUNKSIZE]; int32_t isize = CHUNKSIZE * sizeof(int32_t); int64_t nbytes, cbytes; @@ -119,5 +121,7 @@ int main(void) { free(buffer); free(res); + blosc2_destroy(); + return 0; } diff --git a/examples/instrument_codec.c b/examples/instrument_codec.c index cb9a088a..252aae64 100644 --- a/examples/instrument_codec.c +++ b/examples/instrument_codec.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2022 The Blosc Developers + Copyright (c) 2022 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -30,6 +30,8 @@ int main(void) { + blosc2_init(); + float *data = malloc(SIZE * sizeof(float)); float *data_out = malloc(SIZE * sizeof(float)); float *data_dest = malloc(SIZE * sizeof(float)); @@ -102,5 +104,7 @@ int main(void) { free(data_out); free(data_dest); + blosc2_destroy(); + return 0; } diff --git a/examples/many_compressors.c b/examples/many_compressors.c index 33fe51c2..672fe356 100644 --- a/examples/many_compressors.c +++ b/examples/many_compressors.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/examples/multithread.c b/examples/multithread.c index b0c40e00..fd97c040 100644 --- a/examples/multithread.c +++ b/examples/multithread.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/examples/noinit.c b/examples/noinit.c index 1f9efd09..ad73daea 100644 --- a/examples/noinit.c +++ b/examples/noinit.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/examples/schunk_postfilter.c b/examples/schunk_postfilter.c index 6ff51a9c..01ea3d09 100644 --- a/examples/schunk_postfilter.c +++ b/examples/schunk_postfilter.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -8,7 +8,6 @@ */ #include -#include #include #define KB 1024. @@ -79,7 +78,10 @@ int main(void) { data[i] = i + nchunk * CHUNKSIZE; } nchunks = blosc2_schunk_append_buffer(schunk, data, isize); - assert(nchunks == nchunk + 1); + if (nchunks != nchunk + 1) { + printf("blosc2_schunk_append_buffer is not working correctly"); + return BLOSC2_ERROR_FAILURE; + } } /* Retrieve and decompress the chunks from the super-chunks and compare values */ @@ -91,7 +93,10 @@ int main(void) { } /* Check integrity of this chunk */ for (i = 0; i < CHUNKSIZE; i++) { - assert (data_dest[i] == (i + nchunk * CHUNKSIZE) * user_data.mult + user_data.add); + if (data_dest[i] != (i + nchunk * CHUNKSIZE) * user_data.mult + user_data.add) { + printf("data mismatch!"); + return BLOSC2_ERROR_FAILURE; + } } } printf("Postfilter is working correctly!\n"); diff --git a/examples/schunk_simple.c b/examples/schunk_simple.c index d1678aa3..ea2ea50f 100644 --- a/examples/schunk_simple.c +++ b/examples/schunk_simple.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -32,6 +32,8 @@ #define NTHREADS 4 int main(void) { + blosc2_init(); + static int32_t data[CHUNKSIZE]; static int32_t data_dest[CHUNKSIZE]; int32_t isize = CHUNKSIZE * sizeof(int32_t); @@ -105,5 +107,7 @@ int main(void) { /* Destroy the super-chunk */ blosc2_schunk_free(schunk); + blosc2_destroy(); + return 0; } diff --git a/examples/sframe_simple.c b/examples/sframe_simple.c index b6666eb8..3a9a8380 100644 --- a/examples/sframe_simple.c +++ b/examples/sframe_simple.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/examples/simple.c b/examples/simple.c index fe2ee112..55e91464 100644 --- a/examples/simple.c +++ b/examples/simple.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -38,16 +38,16 @@ int main(void) { int dsize = SIZE * sizeof(float), csize; int i, ret; - for (i = 0; i < SIZE; i++) { - data[i] = (float)i; - } + /* Initialize the Blosc compressor */ + blosc2_init(); + blosc2_set_nthreads(NTHREADS); printf("Blosc version info: %s (%s)\n", BLOSC2_VERSION_STRING, BLOSC2_VERSION_DATE); - /* Initialize the Blosc compressor */ - blosc2_init(); - blosc2_set_nthreads(NTHREADS); + for (i = 0; i < SIZE; i++) { + data[i] = (float)i; + } /* Compress with clevel=5 and shuffle active */ csize = blosc1_compress(5, BLOSC_BITSHUFFLE, sizeof(float), isize, data, @@ -87,16 +87,16 @@ int main(void) { printf("Decompression successful!\n"); - /* After using it, destroy the Blosc environment */ - blosc2_destroy(); - for (i = 0; i < SIZE; i++) { if (data[i] != data_dest[i]) { printf("Decompressed data differs from original!\n"); return -1; } } - printf("Successful roundtrip!\n"); + + /* After using it, destroy the Blosc environment */ + blosc2_destroy(); + return 0; } diff --git a/examples/urcodecs.c b/examples/urcodecs.c index 9cd45f24..608105dd 100644 --- a/examples/urcodecs.c +++ b/examples/urcodecs.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -36,7 +36,7 @@ int codec_encoder(const uint8_t* input, int32_t input_len, return -1; } if (cparams->typesize != 4) { - BLOSC_TRACE_ERROR("Itemsize %d != 4", cparams->typesize); + fprintf(stderr, "Itemsize %d != 4", cparams->typesize); return BLOSC2_ERROR_FAILURE; } @@ -49,7 +49,7 @@ int codec_encoder(const uint8_t* input, int32_t input_len, int32_t step = in_[1] - start; for (int i = 1; i < nelem - 1; ++i) { if (in_[i + 1] - in_[i] != step) { - BLOSC_TRACE_ERROR("Buffer is not an arange"); + fprintf(stderr, "Buffer is not an arange"); return BLOSC2_ERROR_FAILURE; } } @@ -90,6 +90,8 @@ int codec_decoder(const uint8_t* input, int32_t input_len, int main(void) { + blosc2_init(); + static int32_t data[CHUNKSIZE]; static int32_t data_dest[CHUNKSIZE]; int32_t isize = CHUNKSIZE * sizeof(int32_t); @@ -98,13 +100,17 @@ int main(void) { blosc2_codec udcodec; udcodec.compcode = 244; - udcodec.compver = 1; + udcodec.version = 1; udcodec.complib = 1; udcodec.compname = "udcodec"; udcodec.encoder = codec_encoder; udcodec.decoder = codec_decoder; - blosc2_register_codec(&udcodec); + int rc = blosc2_register_codec(&udcodec); + if (rc < 0) { + printf("Cannot register codec!"); + return -1; + } blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.compcode = 244; @@ -178,5 +184,7 @@ int main(void) { /* Destroy the super-chunk */ blosc2_schunk_free(schunk); + blosc2_destroy(); + return 0; } diff --git a/examples/urfilters.c b/examples/urfilters.c index 5fa81e35..dd67ec6f 100644 --- a/examples/urfilters.c +++ b/examples/urfilters.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -46,7 +46,7 @@ int filter_forward(const uint8_t* src, uint8_t* dest, int32_t size, uint8_t meta ((int16_t *) dest)[i] = (int16_t)(((int16_t *) src)[i] + 1); break; default: - BLOSC_TRACE_ERROR("Item size %d not supported", schunk->typesize); + fprintf(stderr, "Item size %d not supported", schunk->typesize); return BLOSC2_ERROR_FAILURE; } } @@ -71,7 +71,7 @@ int filter_backward(const uint8_t* src, uint8_t* dest, int32_t size, uint8_t met ((int16_t *) dest)[i] = (int16_t)(((int16_t *) src)[i] - 1); break; default: - BLOSC_TRACE_ERROR("Item size %d not supported", schunk->typesize); + fprintf(stderr, "Item size %d not supported", schunk->typesize); return BLOSC2_ERROR_FAILURE; } } @@ -79,6 +79,8 @@ int filter_backward(const uint8_t* src, uint8_t* dest, int32_t size, uint8_t met } int main(void) { + blosc2_init(); + static int32_t data[CHUNKSIZE]; static int32_t data_dest[CHUNKSIZE]; int32_t isize = CHUNKSIZE * sizeof(int32_t); @@ -87,6 +89,8 @@ int main(void) { blosc2_filter urfilter; urfilter.id = 250; + urfilter.name = "urfilter_example"; + urfilter.version = 1; urfilter.forward = filter_forward; urfilter.backward = filter_backward; @@ -162,5 +166,7 @@ int main(void) { /* Destroy the super-chunk */ blosc2_schunk_free(schunk); + blosc2_destroy(); + return 0; } diff --git a/examples/win-dynamic-linking.c b/examples/win-dynamic-linking.c index 864d8a76..2ecc410a 100644 --- a/examples/win-dynamic-linking.c +++ b/examples/win-dynamic-linking.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/examples/zstd_dict.c b/examples/zstd_dict.c index c6ee9a1b..1296d988 100644 --- a/examples/zstd_dict.c +++ b/examples/zstd_dict.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/images/Complete-Write-Read-B2ND.png b/images/Complete-Write-Read-B2ND.png new file mode 100644 index 0000000000000000000000000000000000000000..b92fd8df1ad5057460696500180ac5b12de59b7e GIT binary patch literal 116429 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 `1k#oE}Sz(-^xVVt(0Lf6zD=Uc(|>8 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 EAM+we+5i9m literal 0 HcmV?d00001 diff --git a/images/Read-Partial-Slices-B2ND.png b/images/Read-Partial-Slices-B2ND.png new file mode 100644 index 0000000000000000000000000000000000000000..47efce451b4fbcad4747296d2a68435298a84755 GIT binary patch literal 113029 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ℑ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ȧqDmcjdCM~L^oL=z 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

3UhvD`6krKqjX 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 F{|kX^6Yu~4 literal 0 HcmV?d00001 diff --git a/include/b2nd.h b/include/b2nd.h new file mode 100644 index 00000000..801f5937 --- /dev/null +++ b/include/b2nd.h @@ -0,0 +1,606 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +/** @file b2nd.h + * @brief Blosc2 NDim header file. + * + * This file contains Blosc2 NDim public API and the structures needed to use it. + * @author Blosc Development Team + */ + +#ifndef B2ND_B2ND_H_ +#define B2ND_B2ND_H_ + +#ifdef __cplusplus +extern "C" { +#endif +#include "blosc2/blosc2-export.h" +#ifdef __cplusplus +} +#endif + +#include "blosc2.h" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* The version for metalayer format; starts from 0 and it must not exceed 127 */ +#define B2ND_METALAYER_VERSION 0 + +/* The maximum number of dimensions for b2nd arrays */ +#define B2ND_MAX_DIM 8 + +/* The maximum number of metalayers for b2nd arrays */ +#define B2ND_MAX_METALAYERS (BLOSC2_MAX_METALAYERS - 1) + +/* NumPy dtype format + * https://numpy.org/doc/stable/reference/arrays.dtypes.html#arrays-dtypes-constructing + */ +#define DTYPE_NUMPY_FORMAT 0 + +/* The default data type */ +#define B2ND_DEFAULT_DTYPE "|u1" +/* The default data format */ +#define B2ND_DEFAULT_DTYPE_FORMAT DTYPE_NUMPY_FORMAT + +/** + * @brief An *optional* cache for a single block. + * + * When a chunk is needed, it is copied into this cache. In this way, if the same chunk is needed + * again afterwards, it is not necessary to recover it because it is already in the cache. + */ +struct chunk_cache_s { + uint8_t *data; + //!< The chunk data. + int64_t nchunk; + //!< The chunk number in cache. If @p nchunk equals to -1, it means that the cache is empty. +}; + +/** + * @brief General parameters needed for the creation of a b2nd array. + */ +typedef struct b2nd_context_s b2nd_context_t; /* opaque type */ + +/** + * @brief A multidimensional array of data that can be compressed. + */ +typedef struct { + blosc2_schunk *sc; + //!< Pointer to a Blosc super-chunk + int64_t shape[B2ND_MAX_DIM]; + //!< Shape of original data. + int32_t chunkshape[B2ND_MAX_DIM]; + //!< Shape of each chunk. + int64_t extshape[B2ND_MAX_DIM]; + //!< Shape of padded data. + int32_t blockshape[B2ND_MAX_DIM]; + //!< Shape of each block. + int64_t extchunkshape[B2ND_MAX_DIM]; + //!< Shape of padded chunk. + int64_t nitems; + //!< Number of items in original data. + int32_t chunknitems; + //!< Number of items in each chunk. + int64_t extnitems; + //!< Number of items in padded data. + int32_t blocknitems; + //!< Number of items in each block. + int64_t extchunknitems; + //!< Number of items in a padded chunk. + int8_t ndim; + //!< Data dimensions. + struct chunk_cache_s chunk_cache; + //!< A partition cache. + int64_t item_array_strides[B2ND_MAX_DIM]; + //!< Item - shape strides. + int64_t item_chunk_strides[B2ND_MAX_DIM]; + //!< Item - shape strides. + int64_t item_extchunk_strides[B2ND_MAX_DIM]; + //!< Item - shape strides. + int64_t item_block_strides[B2ND_MAX_DIM]; + //!< Item - shape strides. + int64_t block_chunk_strides[B2ND_MAX_DIM]; + //!< Item - shape strides. + int64_t chunk_array_strides[B2ND_MAX_DIM]; + //!< Item - shape strides. + char *dtype; + //!< Data type. Different formats can be supported (see dtype_format). + int8_t dtype_format; + //!< The format of the data type. Default is DTYPE_NUMPY_FORMAT. +} b2nd_array_t; + + +/** + * @brief Create b2nd params. + * + * @param b2_storage The Blosc2 storage params. + * @param ndim The dimensions. + * @param shape The shape. + * @param chunkshape The chunk shape. + * @param blockshape The block shape. + * @param dtype The data type expressed as a string version. + * @param dtype_format The data type format; default is DTYPE_NUMPY_FORMAT. + * @param metalayers The memory pointer to the list of the metalayers desired. + * @param nmetalayers The number of metalayers. + * + * @return A pointer to the new b2nd params. NULL is returned if this fails. + * + * @note The pointer returned must be freed when not used anymore with #b2nd_free_ctx. + * + */ +BLOSC_EXPORT b2nd_context_t * +b2nd_create_ctx(const blosc2_storage *b2_storage, int8_t ndim, const int64_t *shape, const int32_t *chunkshape, + const int32_t *blockshape, const char *dtype, int8_t dtype_format, const blosc2_metalayer *metalayers, + int32_t nmetalayers); + + +/** + * @brief Free the resources associated with b2nd_context_t. + * + * @param ctx The b2nd context to free. + * + * @return An error code. + * + * @note This is safe in the sense that it will not free the schunk pointer in internal cparams. + * + */ +BLOSC_EXPORT int b2nd_free_ctx(b2nd_context_t *ctx); + + +/** + * @brief Create an uninitialized array. + * + * @param ctx The b2nd context for the new array. + * @param array The memory pointer where the array will be created. + * + * @return An error code. + */ +BLOSC_EXPORT int b2nd_uninit(b2nd_context_t *ctx, b2nd_array_t **array); + + +/** + * @brief Create an empty array. + * + * @param ctx The b2nd context for the new array. + * @param array The memory pointer where the array will be created. + * + * @return An error code. + */ +BLOSC_EXPORT int b2nd_empty(b2nd_context_t *ctx, b2nd_array_t **array); + + +/** + * Create an array, with zero being used as the default value for + * uninitialized portions of the array. + * + * @param ctx The b2nd context for the new array. + * @param array The memory pointer where the array will be created. + * + * @return An error code. + */ +BLOSC_EXPORT int b2nd_zeros(b2nd_context_t *ctx, b2nd_array_t **array); + + +/** + * Create an array, with @p fill_value being used as the default value for + * uninitialized portions of the array. + * + * @param ctx The b2nd context for the new array. + * @param fill_value Default value for uninitialized portions of the array. + * @param array The memory pointer where the array will be created. + * + * @return An error code. + */ +BLOSC_EXPORT int b2nd_full(b2nd_context_t *ctx, b2nd_array_t **array, const void *fill_value); + +/** + * @brief Free an array. + * + * @param array The array. + * + * @return An error code. + */ +BLOSC_EXPORT int b2nd_free(b2nd_array_t *array); + +/** + * @brief Create a b2nd array from a super-chunk. It can only be used if the array + * is backed by a blosc super-chunk. + * + * @param schunk The blosc super-chunk where the b2nd array is stored. + * @param array The memory pointer where the array will be created. + * + * @return An error code. + */ +BLOSC_EXPORT int b2nd_from_schunk(blosc2_schunk *schunk, b2nd_array_t **array); + +/** + * Create a serialized super-chunk from a b2nd array. + * + * @param array The b2nd array to be serialized. + * @param cframe The pointer of the buffer where the in-memory array will be copied. + * @param cframe_len The length of the in-memory array buffer. + * @param needs_free Whether the buffer should be freed or not. + * + * @return An error code + */ +BLOSC_EXPORT int b2nd_to_cframe(const b2nd_array_t *array, uint8_t **cframe, + int64_t *cframe_len, bool *needs_free); + +/** + * @brief Create a b2nd array from a serialized super-chunk. + * + * @param cframe The buffer of the in-memory array. + * @param cframe_len The size (in bytes) of the in-memory array. + * @param copy Whether b2nd should make a copy of the cframe data or not. The copy will be made to an internal sparse frame. + * @param array The memory pointer where the array will be created. + * + * @return An error code. + */ +BLOSC_EXPORT int b2nd_from_cframe(uint8_t *cframe, int64_t cframe_len, bool copy, b2nd_array_t **array); + +/** + * @brief Open a b2nd array from a file. + * + * @param urlpath The path of the b2nd array on disk. + * @param array The memory pointer where the array info will be stored. + * + * @return An error code. + */ +BLOSC_EXPORT int b2nd_open(const char *urlpath, b2nd_array_t **array); + +/** + * @brief Open a b2nd array from a file using an offset. + * + * @param urlpath The path of the b2nd array on disk. + * @param array The memory pointer where the array info will be stored. + * @param offset The offset in the file where the b2nd array frame starts. + * + * @return An error code. + */ +BLOSC_EXPORT int b2nd_open_offset(const char *urlpath, b2nd_array_t **array, int64_t offset); + +/** + * @brief Save b2nd array into a specific urlpath. + * + * @param array The array to be saved. + * @param urlpath The urlpath where the array will be stored. + * + * @return An error code. + */ +BLOSC_EXPORT int b2nd_save(const b2nd_array_t *array, char *urlpath); + +/** + * @brief Create a b2nd array from a C buffer. + * + * @param ctx The b2nd context for the new array. + * @param array The memory pointer where the array will be created. + * @param buffer The buffer where source data is stored. + * @param buffersize The size (in bytes) of the buffer. + * + * @return An error code. + */ +BLOSC_EXPORT int b2nd_from_cbuffer(b2nd_context_t *ctx, b2nd_array_t **array, const void *buffer, int64_t buffersize); + +/** + * @brief Extract the data from a b2nd array into a C buffer. + * + * @param array The b2nd array. + * @param buffer The buffer where the data will be stored. + * @param buffersize Size (in bytes) of the buffer. + * + * @return An error code. + */ +BLOSC_EXPORT int b2nd_to_cbuffer(const b2nd_array_t *array, void *buffer, int64_t buffersize); + +/** + * @brief Get a slice from an array and store it into a new array. + * + * @param ctx The b2nd context for the new array. + * @param array The memory pointer where the array will be created. + * @param src The array from which the slice will be extracted + * @param start The coordinates where the slice will begin. + * @param stop The coordinates where the slice will end. + * + * @return An error code. + * + * @note The ndim and shape from ctx will be overwritten by the src and stop-start respectively. + * + */ +BLOSC_EXPORT int b2nd_get_slice(b2nd_context_t *ctx, b2nd_array_t **array, const b2nd_array_t *src, + const int64_t *start, const int64_t *stop); + +/** + * @brief Squeeze a b2nd array + * + * This function remove selected single-dimensional entries from the shape of a + b2nd array. + * + * @param array The b2nd array. + * @param index Indexes of the single-dimensional entries to remove. + * + * @return An error code + */ +BLOSC_EXPORT int b2nd_squeeze_index(b2nd_array_t *array, const bool *index); + +/** + * @brief Squeeze a b2nd array + * + * This function remove single-dimensional entries from the shape of a b2nd array. + * + * @param array The b2nd array. + * + * @return An error code + */ +BLOSC_EXPORT int b2nd_squeeze(b2nd_array_t *array); + +/** + * @brief Get a slice from an array and store it into a C buffer. + * + * @param array The array from which the slice will be extracted. + * @param start The coordinates where the slice will begin. + * @param stop The coordinates where the slice will end. + * @param buffershape The shape of the buffer. + * @param buffer The buffer where the data will be stored. + * @param buffersize The size (in bytes) of the buffer. + * + * @return An error code. + */ +BLOSC_EXPORT int b2nd_get_slice_cbuffer(const b2nd_array_t *array, const int64_t *start, const int64_t *stop, + void *buffer, const int64_t *buffershape, int64_t buffersize); + +/** + * @brief Set a slice in a b2nd array using a C buffer. + * + * @param buffer The buffer where the slice data is. + * @param buffersize The size (in bytes) of the buffer. + * @param start The coordinates where the slice will begin. + * @param stop The coordinates where the slice will end. + * @param buffershape The shape of the buffer. + * @param array The b2nd array where the slice will be set + * + * @return An error code. + */ +BLOSC_EXPORT int b2nd_set_slice_cbuffer(const void *buffer, const int64_t *buffershape, int64_t buffersize, + const int64_t *start, const int64_t *stop, b2nd_array_t *array); + +/** + * @brief Make a copy of the array data. The copy is done into a new b2nd array. + * + * @param ctx The b2nd context for the new array. + * @param src The array from which data is copied. + * @param array The memory pointer where the array will be created. + * + * @return An error code + * + * @note The ndim and shape in ctx will be overwritten by the src ctx. + * + */ +BLOSC_EXPORT int b2nd_copy(b2nd_context_t *ctx, const b2nd_array_t *src, b2nd_array_t **array); + +/** + * @brief Print metalayer parameters. + * + * @param array The array where the metalayer is stored. + * + * @return An error code + */ +BLOSC_EXPORT int b2nd_print_meta(const b2nd_array_t *array); + +/** + * @brief Resize the shape of an array + * + * @param array The array to be resized. + * @param new_shape The new shape from the array. + * @param start The position in which the array will be extended or shrunk. + * + * @return An error code + */ +BLOSC_EXPORT int b2nd_resize(b2nd_array_t *array, const int64_t *new_shape, const int64_t *start); + + +/** + * @brief Insert given buffer in an array extending the given axis. + * + * @param array The array to insert the data in. + * @param buffer The buffer data to be inserted. + * @param buffersize The size (in bytes) of the buffer. + * @param axis The axis that will be extended. + * @param insert_start The position inside the axis to start inserting the data. + * + * @return An error code. + */ +BLOSC_EXPORT int b2nd_insert(b2nd_array_t *array, const void *buffer, int64_t buffersize, + int8_t axis, int64_t insert_start); + +/** + * Append a buffer at the end of a b2nd array. + * + * @param array The array to append the data in. + * @param buffer The buffer data to be appended. + * @param buffersize Size (in bytes) of the buffer. + * @param axis The axis that will be extended to append the data. + * + * @return An error code. + */ +BLOSC_EXPORT int b2nd_append(b2nd_array_t *array, const void *buffer, int64_t buffersize, + int8_t axis); + +/** + * @brief Delete shrinking the given axis delete_len items. + * + * @param array The array to shrink. + * @param axis The axis to shrink. + * @param delete_start The start position from the axis to start deleting chunks. + * @param delete_len The number of items to delete to the array->shape[axis]. + * The newshape[axis] will be the old array->shape[axis] - delete_len + * + * @return An error code. + * + * @note See also b2nd_resize + */ +BLOSC_EXPORT int b2nd_delete(b2nd_array_t *array, int8_t axis, + int64_t delete_start, int64_t delete_len); + + +// Indexing section + +/** + * @brief Get an element selection along each dimension of an array independently. + * + * @param array The array to get the data from. + * @param selection The elements along each dimension. + * @param selection_size The size of the selection along each dimension. + * @param buffer The buffer for getting the data. + * @param buffershape The shape of the buffer. + * @param buffersize The buffer size (in bytes). + * + * @return An error code. + * + * @note See also b2nd_set_orthogonal_selection. + */ +BLOSC_EXPORT int b2nd_get_orthogonal_selection(const b2nd_array_t *array, int64_t **selection, + int64_t *selection_size, void *buffer, + int64_t *buffershape, int64_t buffersize); + +/** + * @brief Set an element selection along each dimension of an array independently. + * + * @param array The array to set the data to. + * @param selection The elements along each dimension. + * @param selection_size The size of the selection along each dimension. + * @param buffer The buffer with the data for setting. + * @param buffershape The shape of the buffer. + * @param buffersize The buffer size (in bytes). + * + * @return An error code. + * + * @note See also b2nd_get_orthogonal_selection. + */ +BLOSC_EXPORT int b2nd_set_orthogonal_selection(b2nd_array_t *array, int64_t **selection, + int64_t *selection_size, const void *buffer, + int64_t *buffershape, int64_t buffersize); + + +/** + * @brief Create the metainfo for the b2nd metalayer. + * + * @param ndim The number of dimensions in the array. + * @param shape The shape of the array. + * @param chunkshape The shape of the chunks in the array. + * @param blockshape The shape of the blocks in the array. + * @param dtype A string representation of the data type of the array. + * @param dtype_format The format of the dtype representation. 0 means NumPy. + * @param smeta The msgpack buffer (output). + * + * @return An error code. + */ +BLOSC_EXPORT int b2nd_serialize_meta(int8_t ndim, const int64_t *shape, const int32_t *chunkshape, + const int32_t *blockshape, const char *dtype, + int8_t dtype_format, uint8_t **smeta); + +/** + * @brief Read the metainfo in the b2nd metalayer. + * + * @param smeta The msgpack buffer (input). + * @param smeta_len The length of the smeta buffer (input). + * @param ndim The number of dimensions in the array (output). + * @param shape The shape of the array (output). + * @param chunkshape The shape of the chunks in the array (output). + * @param blockshape The shape of the blocks in the array (output). + * @param dtype A string representation of the data type of the array (output). + * @param dtype_format The format of the dtype representation (output). 0 means NumPy (the default). + * + * @note This function is inlined and available even when not linking with libblosc2. + * + * @return An error code. + */ +static inline int b2nd_deserialize_meta( + const uint8_t *smeta, int32_t smeta_len, int8_t *ndim, int64_t *shape, + int32_t *chunkshape, int32_t *blockshape, char **dtype, int8_t *dtype_format) { + const uint8_t *pmeta = smeta; + + // Check that we have an array with 7 entries (version, ndim, shape, chunkshape, blockshape, dtype_format, dtype) + pmeta += 1; + + // version entry + // int8_t version = (int8_t)pmeta[0]; // positive fixnum (7-bit positive integer) commented to avoid warning + pmeta += 1; + + // ndim entry + *ndim = (int8_t) pmeta[0]; + int8_t ndim_aux = *ndim; // positive fixnum (7-bit positive integer) + pmeta += 1; + + // shape entry + // Initialize to ones, as required by b2nd + for (int i = 0; i < ndim_aux; i++) shape[i] = 1; + pmeta += 1; + for (int8_t i = 0; i < ndim_aux; i++) { + pmeta += 1; + swap_store(shape + i, pmeta, sizeof(int64_t)); + pmeta += sizeof(int64_t); + } + + // chunkshape entry + // Initialize to ones, as required by b2nd + for (int i = 0; i < ndim_aux; i++) chunkshape[i] = 1; + pmeta += 1; + for (int8_t i = 0; i < ndim_aux; i++) { + pmeta += 1; + swap_store(chunkshape + i, pmeta, sizeof(int32_t)); + pmeta += sizeof(int32_t); + } + + // blockshape entry + // Initialize to ones, as required by b2nd + for (int i = 0; i < ndim_aux; i++) blockshape[i] = 1; + pmeta += 1; + for (int8_t i = 0; i < ndim_aux; i++) { + pmeta += 1; + swap_store(blockshape + i, pmeta, sizeof(int32_t)); + pmeta += sizeof(int32_t); + } + + // dtype entry + if (pmeta - smeta < smeta_len) { + // dtype info is here + *dtype_format = (int8_t) *(pmeta++); + pmeta += 1; + int dtype_len; + swap_store(&dtype_len, pmeta, sizeof(int32_t)); + pmeta += sizeof(int32_t); + *dtype = (char*)malloc(dtype_len + 1); + char* dtype_ = *dtype; + memcpy(dtype_, (char*)pmeta, dtype_len); + dtype_[dtype_len] = '\0'; + pmeta += dtype_len; + } + else { + // dtype is mandatory in b2nd metalayer, but this is mainly meant as + // a fall-back for deprecated caterva headers + *dtype = NULL; + *dtype_format = 0; + } + + int32_t slen = (int32_t) (pmeta - smeta); + return (int)slen; +} + + +#ifdef __cplusplus +} +#endif + +#endif // B2ND_B2ND_H_ diff --git a/include/blosc2.h b/include/blosc2.h index 2da87dee..71f58d5e 100644 --- a/include/blosc2.h +++ b/include/blosc2.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -13,19 +13,13 @@ @brief Blosc2 header file. This file contains Blosc2 public API and the structures needed to use it. - @author The Blosc Developers + @author The Blosc Development Team **********************************************************************/ #ifndef BLOSC2_H #define BLOSC2_H -#include -#include -#include -#include -#include - #ifdef __cplusplus extern "C" { #endif @@ -38,12 +32,17 @@ extern "C" { #if defined(_WIN32) && !defined(__MINGW32__) #include - #include - - #include - #define getpid _getpid +#include +#include +#define getpid _getpid #endif +#include +#include +#include +#include +#include + #ifdef __cplusplus extern "C" { #endif @@ -90,26 +89,43 @@ extern "C" { /* Version numbers */ #define BLOSC2_VERSION_MAJOR 2 /* for major interface/format changes */ -#define BLOSC2_VERSION_MINOR 6 /* for minor interface/format changes */ -#define BLOSC2_VERSION_RELEASE 1 /* for tweaks, bug-fixes, or development */ +#define BLOSC2_VERSION_MINOR 9 /* for minor interface/format changes */ +#define BLOSC2_VERSION_RELEASE 2 /* for tweaks, bug-fixes, or development */ -#define BLOSC2_VERSION_STRING "2.6.1" /* string version. Sync with above! */ -#define BLOSC2_VERSION_DATE "$Date:: 2022-12-15 #$" /* date version */ +#define BLOSC2_VERSION_STRING "2.9.2" /* string version. Sync with above! */ +#define BLOSC2_VERSION_DATE "$Date:: 2023-05-18 #$" /* date version */ -/* The maximum number of dimensions for caterva arrays */ +/* The maximum number of dimensions for Blosc2 NDim arrays */ #define BLOSC2_MAX_DIM 8 /* Tracing macros */ #define BLOSC_TRACE_ERROR(msg, ...) BLOSC_TRACE(error, msg, ##__VA_ARGS__) #define BLOSC_TRACE_WARNING(msg, ...) BLOSC_TRACE(warning, msg, ##__VA_ARGS__) -#define BLOSC_TRACE(cat, msg, ...) \ - do { \ - const char *__e = getenv("BLOSC_TRACE"); \ - if (!__e) { break; } \ - fprintf(stderr, "[%s] - " msg " (%s:%d)\n", #cat, ##__VA_ARGS__, __FILE__, __LINE__); \ - } while(0) +#define BLOSC_TRACE(cat, msg, ...) \ + do { \ + const char *__e = getenv("BLOSC_TRACE"); \ + if (!__e) { break; } \ + fprintf(stderr, "[%s] - " msg " (%s:%d)\n", #cat, ##__VA_ARGS__, __FILE__, __LINE__); \ + } while(0) + +#define BLOSC_ERROR_NULL(pointer, rc) \ + do { \ + if ((pointer) == NULL) { \ + BLOSC_TRACE_ERROR("Pointer is null"); \ + return rc; \ + } \ + } while (0) +#define BLOSC_ERROR(rc) \ + do { \ + int rc_ = rc; \ + if (rc_ < BLOSC2_ERROR_SUCCESS) { \ + char *error_msg = print_error(rc_); \ + BLOSC_TRACE_ERROR("%s", error_msg); \ + return rc_; \ + } \ + } while (0) /* The VERSION_FORMAT symbols below should be just 1-byte long */ @@ -177,6 +193,30 @@ enum { //!< Minimum buffer size to be compressed. }; +enum { + BLOSC2_DEFINED_TUNER_START = 0, + BLOSC2_DEFINED_TUNER_STOP = 31, + //!< Blosc-defined tuners must be between 0 - 31. + BLOSC2_GLOBAL_REGISTERED_TUNER_START = 32, + BLOSC2_GLOBAL_REGISTERED_TUNER_STOP = 159, + //!< Blosc-registered tuners must be between 31 - 159. + BLOSC2_GLOBAL_REGISTERED_TUNERS = 0, + //!< Number of Blosc-registered tuners at the moment. + BLOSC2_USER_REGISTERED_TUNER_START = 160, + BLOSC2_USER_REGISTERED_TUNER_STOP = 255, + //!< User-defined tuners must be between 160 - 255. +}; + +/** + * @brief Codes for the different tuners shipped with Blosc + */ +enum { + BLOSC_STUNE = 0, + BLOSC_LAST_TUNER = 1, + //!< Determine the last tuner defined by Blosc. + BLOSC_LAST_REGISTERED_TUNE = BLOSC2_GLOBAL_REGISTERED_TUNER_START + BLOSC2_GLOBAL_REGISTERED_TUNERS - 1, + //!< Determine the last registered tuner. It is used to check if a tuner is registered or not. +}; enum { BLOSC2_DEFINED_FILTERS_START = 0, @@ -185,7 +225,7 @@ enum { BLOSC2_GLOBAL_REGISTERED_FILTERS_START = 32, BLOSC2_GLOBAL_REGISTERED_FILTERS_STOP = 159, //!< Blosc-registered filters must be between 32 - 159. - BLOSC2_GLOBAL_REGISTERED_FILTERS = 2, + BLOSC2_GLOBAL_REGISTERED_FILTERS = 3, //!< Number of Blosc-registered filters at the moment. BLOSC2_USER_REGISTERED_FILTERS_START = 160, BLOSC2_USER_REGISTERED_FILTERS_STOP = 255, @@ -392,7 +432,7 @@ enum { enum { BLOSC2_ERROR_SUCCESS = 0, // BTune configuration. -}blosc2_btune; + void (*init)(void * config, blosc2_context* cctx, blosc2_context* dctx); + //!< Initialize tuner. Keep in mind dctx may be NULL. This should memcpy the cctx->tuner_params. + void (*next_blocksize)(blosc2_context * context); + //!< Only compute the next blocksize. Only it is executed if tuner is not initialized. + void (*next_cparams)(blosc2_context * context); + //!< Compute the next cparams. Only is executed if tuner is initialized. + void (*update)(blosc2_context * context, double ctime); + //!< Update the tuner parameters. + void (*free)(blosc2_context * context); + //!< Free the tuner. + int id; + //!< The tuner id + char *name; + //!< The tuner name +} blosc2_tuner; + + +/** + * @brief Register locally a user-defined tuner in Blosc. + * + * @param tuner The tuner to register. + * + * @return 0 if succeeds. Else a negative code is returned. + */ +BLOSC_EXPORT int register_tuner_private(blosc2_tuner *tuner); /** @@ -1059,10 +1191,16 @@ typedef struct { //!< The prefilter function. blosc2_prefilter_params *preparams; //!< The prefilter parameters. - blosc2_btune *udbtune; - //!< The user-defined BTune parameters. + void *tuner_params; + //!< Tune configuration. + int tuner_id; + //!< The tuner id. bool instr_codec; //!< Whether the codec is instrumented or not + void *codec_params; + //!< User defined parameters for the codec + void *filter_params[BLOSC2_MAX_FILTERS]; + //!< User defined parameters for the filters } blosc2_cparams; /** @@ -1073,7 +1211,9 @@ static const blosc2_cparams BLOSC2_CPARAMS_DEFAULTS = { BLOSC_FORWARD_COMPAT_SPLIT, NULL, {0, 0, 0, 0, 0, BLOSC_SHUFFLE}, {0, 0, 0, 0, 0, 0}, - NULL, NULL, NULL, 0}; + NULL, NULL, NULL, 0, 0, + NULL, {NULL, NULL, NULL, NULL, NULL, NULL} + }; /** @@ -1210,9 +1350,11 @@ BLOSC_EXPORT int blosc2_set_maskout(blosc2_context *ctx, bool *maskout, int nblo * @return The number of bytes compressed. * If @p src buffer cannot be compressed into @p destsize, the return * value is zero and you should discard the contents of the @p dest - * buffer. A negative return value means that an internal error happened. This - * should never happen. If you see this, please report it back - * together with the buffer data causing this and compression settings. + * buffer. A negative return value means that either a parameter is not correct + * or that an internal error happened. Set the BLOSC_TRACE environment variable + * for getting more info on what is happening. If the error is not related with + * wrong params, please report it back together with the buffer data causing this, + * as well as the compression params used. */ /* * Environment variables @@ -1416,7 +1558,7 @@ BLOSC_EXPORT int blosc2_chunk_nans(blosc2_cparams cparams, int32_t nbytes, * If negative, there has been an error and @p dest is unusable. * */ BLOSC_EXPORT int blosc2_chunk_repeatval(blosc2_cparams cparams, int32_t nbytes, - void* dest, int32_t destsize, void* repeatval); + void* dest, int32_t destsize, const void* repeatval); /** @@ -1501,7 +1643,7 @@ typedef struct blosc2_frame_s blosc2_frame; /* opaque type */ * the contents included in the schunk. */ typedef struct blosc2_metalayer { - char* name; //!< The metalayer identifier for Blosc client (e.g. Caterva). + char* name; //!< The metalayer identifier for Blosc client (e.g. Blosc2 NDim). uint8_t* content; //!< The serialized (msgpack preferably) content of the metalayer. int32_t content_len; //!< The length in bytes of the content. } blosc2_metalayer; @@ -1538,9 +1680,9 @@ typedef struct blosc2_schunk { int64_t current_nchunk; //!< The current chunk that is being accessed int64_t nbytes; - //!< The data size + metadata size + header size (uncompressed). + //!< The data size (uncompressed). int64_t cbytes; - //!< The data size + metadata size + header size (compressed). + //!< The data size + chunks header size (compressed). uint8_t** data; //!< Pointer to chunk data pointers buffer. size_t data_len; @@ -1563,8 +1705,10 @@ typedef struct blosc2_schunk { // BLOSC2_METALAYER_NAME_MAXLEN) { + BLOSC_TRACE_ERROR("Metalayers cannot be larger than %d chars.", BLOSC2_METALAYER_NAME_MAXLEN); + return BLOSC2_ERROR_INVALID_PARAM; + } + + if (schunk == NULL) { + BLOSC_TRACE_ERROR("Schunk must not be NUll."); + return BLOSC2_ERROR_INVALID_PARAM; + } + + for (int nmetalayer = 0; nmetalayer < schunk->nmetalayers; nmetalayer++) { + if (strcmp(name, schunk->metalayers[nmetalayer]->name) == 0) { + return nmetalayer; + } + } + return BLOSC2_ERROR_NOT_FOUND; +} /** * @brief Add content into a new metalayer. @@ -1961,6 +2122,46 @@ BLOSC_EXPORT int blosc2_meta_add(blosc2_schunk *schunk, const char *name, uint8_ BLOSC_EXPORT int blosc2_meta_update(blosc2_schunk *schunk, const char *name, uint8_t *content, int32_t content_len); +static inline void swap_store(void *dest, const void *pa, int size) { + uint8_t *pa_ = (uint8_t *) pa; + uint8_t *pa2_ = (uint8_t*)malloc((size_t) size); + int i = 1; /* for big/little endian detection */ + char *p = (char *) &i; + + if (p[0] == 1) { + /* little endian */ + switch (size) { + case 8: + pa2_[0] = pa_[7]; + pa2_[1] = pa_[6]; + pa2_[2] = pa_[5]; + pa2_[3] = pa_[4]; + pa2_[4] = pa_[3]; + pa2_[5] = pa_[2]; + pa2_[6] = pa_[1]; + pa2_[7] = pa_[0]; + break; + case 4: + pa2_[0] = pa_[3]; + pa2_[1] = pa_[2]; + pa2_[2] = pa_[1]; + pa2_[3] = pa_[0]; + break; + case 2: + pa2_[0] = pa_[1]; + pa2_[1] = pa_[0]; + break; + case 1: + pa2_[0] = pa_[0]; + break; + default: + fprintf(stderr, "Unhandled nitems: %d\n", size); + } + } + memcpy(dest, pa2_, size); + free(pa2_); +} + /** * @brief Get the content out of a metalayer. * @@ -1972,10 +2173,22 @@ BLOSC_EXPORT int blosc2_meta_update(blosc2_schunk *schunk, const char *name, uin * @warning The @p **content receives a malloc'ed copy of the content. * The user is responsible of freeing it. * + * @note This function is inlined and available even when not linking with libblosc2. + * * @return If successful, the index of the new metalayer. Else, return a negative value. */ -BLOSC_EXPORT int blosc2_meta_get(blosc2_schunk *schunk, const char *name, uint8_t **content, - int32_t *content_len); +static inline int blosc2_meta_get(blosc2_schunk *schunk, const char *name, uint8_t **content, + int32_t *content_len) { + int nmetalayer = blosc2_meta_exists(schunk, name); + if (nmetalayer < 0) { + BLOSC_TRACE_WARNING("Metalayer \"%s\" not found.", name); + return nmetalayer; + } + *content_len = schunk->metalayers[nmetalayer]->content_len; + *content = (uint8_t*)malloc((size_t)*content_len); + memcpy(*content, schunk->metalayers[nmetalayer]->content, (size_t)*content_len); + return nmetalayer; +} /********************************************************************* @@ -2055,7 +2268,7 @@ BLOSC_EXPORT int blosc2_vlmeta_delete(blosc2_schunk *schunk, const char *name); * * @param schunk The super-chunk containing the variable-length metalayers. * @param names The pointer to a char** to store the name pointers. This should - * be of size @p *schunk->nvlmetalayers * sizeof(char*). + * be of size *schunk->nvlmetalayers * sizeof(char*). * * @return The number of the variable-length metalayers in the super-chunk. * This cannot fail unless the user does not pass a @p names which is large enough to @@ -2181,7 +2394,7 @@ typedef struct { //!< The codec name. uint8_t complib; //!< The codec library format. - uint8_t compver; + uint8_t version; //!< The codec version. blosc2_codec_encoder_cb encoder; //!< The codec encoder that is used during compression. @@ -2214,6 +2427,10 @@ typedef int (* blosc2_filter_backward_cb) (const uint8_t *, uint8_t *, int32_t, typedef struct { uint8_t id; //!< The filter identifier. + char * name; + //!< The filter name. + uint8_t version; + //!< The filter version. blosc2_filter_forward_cb forward; //!< The filter function that is used during compression. blosc2_filter_backward_cb backward; diff --git a/include/blosc2/blosc2-common.h b/include/blosc2/blosc2-common.h index 9acffdbe..ef4413c9 100644 --- a/include/blosc2/blosc2-common.h +++ b/include/blosc2/blosc2-common.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -12,6 +12,8 @@ #define SHUFFLE_COMMON_H #include "blosc2-export.h" + +#include #include // For shutting up stupid compiler warning about some 'unused' variables in GCC @@ -25,24 +27,10 @@ // For shutting up compiler warning about unused parameters #define BLOSC_UNUSED_PARAM(x) ((void)(x)) -/* Import standard integer type definitions */ -#if defined(_WIN32) && !defined(__MINGW32__) - - /* stdint.h only available in VS2010 (VC++ 16.0) and newer */ - #if defined(_MSC_VER) && _MSC_VER < 1600 - #include "win32/stdint-windows.h" - #else - #include - #endif - - /* Use inlined functions for supported systems */ - #if defined(_MSC_VER) && !defined(__cplusplus) /* Visual Studio */ - #define inline __inline /* Visual C is not C99, but supports some kind of inline */ - #endif - -#else - #include -#endif /* _WIN32 */ +/* Use inlined functions for supported systems */ +#if defined(_MSC_VER) && !defined(__cplusplus) /* Visual Studio */ + #define inline __inline /* Visual C is not C99, but supports some kind of inline */ +#endif /* Define the __SSE2__ symbol if compiling with Visual C++ and diff --git a/include/blosc2/blosc2-export.h b/include/blosc2/blosc2-export.h index 39236510..3b9e4e17 100644 --- a/include/blosc2/blosc2-export.h +++ b/include/blosc2/blosc2-export.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/include/blosc2/blosc2-stdio.h b/include/blosc2/blosc2-stdio.h index 09368cff..5969987e 100644 --- a/include/blosc2/blosc2-stdio.h +++ b/include/blosc2/blosc2-stdio.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -13,30 +13,18 @@ #define BLOSC_BLOSC2_STDIO_H -#include -#include #include "blosc2-export.h" - -#if defined(_WIN32) && !defined(__MINGW32__) - -/* stdint.h only available in VS2010 (VC++ 16.0) and newer */ - #if defined(_MSC_VER) && _MSC_VER < 1600 - #include "win32/stdint-windows.h" - #else - #include - #endif - -#else -#include -#endif - -#if defined(_MSC_VER) && (_MSC_VER >= 1400) +#if defined(_MSC_VER) #include #else #include #endif +#include +#include +#include + typedef struct { FILE *file; } blosc2_stdio_file; diff --git a/include/blosc2/codecs-registry.h b/include/blosc2/codecs-registry.h index 8b5e78db..12f61b76 100644 --- a/include/blosc2/codecs-registry.h +++ b/include/blosc2/codecs-registry.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -16,3 +16,12 @@ enum { }; void register_codecs(void); + +// For dynamically loaded codecs +typedef struct { + char *encoder; + char *decoder; +} codec_info; + +// Silence unused codec_info typedef warning +static codec_info codec_info_defaults BLOSC_ATTRIBUTE_UNUSED = {0}; diff --git a/include/blosc2/filters-registry.h b/include/blosc2/filters-registry.h index 60e27966..fd74e4e4 100644 --- a/include/blosc2/filters-registry.h +++ b/include/blosc2/filters-registry.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -11,6 +11,13 @@ enum { BLOSC_FILTER_NDCELL = 32, BLOSC_FILTER_NDMEAN = 33, + BLOSC_FILTER_BYTEDELTA = 34, }; void register_filters(void); + +// For dynamically loaded filters +typedef struct { + char *forward; + char *backward; +} filter_info; diff --git a/include/blosc2/plugins-utils.h b/include/blosc2/plugins-utils.h new file mode 100644 index 00000000..be23f7e9 --- /dev/null +++ b/include/blosc2/plugins-utils.h @@ -0,0 +1,100 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (C) 2021 The Blosc Developers + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + + +#include + +#if defined(_WIN32) +#include +#define PATH_MAX MAX_PATH +#define RTLD_LAZY 0x000 +#define popen _popen +#define pclose _pclose + +static struct { + long lasterror; + const char *err_rutin; +} +var = { + 0, + NULL +}; + +void *dlopen (const char *filename, int flags) { + HINSTANCE hInst; + hInst = LoadLibrary(filename); + if (hInst==NULL) { + var.lasterror = GetLastError(); + var.err_rutin = "dlopen"; + } + + return hInst; +} + +void *dlsym(void *handle, const char *name) { + FARPROC fp; + fp = GetProcAddress((HINSTANCE)handle, name); + if (!fp) { + var.lasterror = GetLastError (); + var.err_rutin = "dlsym"; + } + return (void *)(intptr_t)fp; +} + +int dlclose(void *handle) { + bool ok = FreeLibrary((HINSTANCE)handle); + if (!ok) { + var.lasterror = GetLastError(); + var.err_rutin = "dlclose"; + return BLOSC2_ERROR_FAILURE; + } + return BLOSC2_ERROR_SUCCESS; +} + +const char *dlerror (void) { + static char errstr [88]; + if (var.lasterror) { + sprintf (errstr, "%s error #%ld", var.err_rutin, var.lasterror); + return errstr; + } else { + return NULL; + } +} +#else +#include +#endif + + +static inline void* load_lib(char *plugin_name, char *libpath) { + char python_cmd[PATH_MAX] = {0}; + sprintf(python_cmd, "python -c \"import blosc2_%s; blosc2_%s.print_libpath()\"", plugin_name, plugin_name); + FILE *fp = popen(python_cmd, "r"); + if (fp == NULL) { + BLOSC_TRACE_ERROR("Could not run python"); + return NULL; + } + if (fgets(libpath, PATH_MAX, fp) == NULL) { + BLOSC_TRACE_ERROR("Could not read python output"); + return NULL; + } + pclose(fp); + if (strlen(libpath) == 0) { + BLOSC_TRACE_ERROR("Could not find plugin libpath"); + return NULL; + } + void* loaded_lib; + BLOSC_TRACE_WARNING("libpath for plugin blosc2_%s: %s\n", plugin_name, libpath); + loaded_lib = dlopen(libpath, RTLD_LAZY); + if (loaded_lib == NULL) { + BLOSC_TRACE_ERROR("Attempt to load plugin in path '%s' failed with error: %s", + libpath, dlerror()); + } + return loaded_lib; +} diff --git a/include/blosc2/tuners-registry.h b/include/blosc2/tuners-registry.h new file mode 100644 index 00000000..a6310115 --- /dev/null +++ b/include/blosc2/tuners-registry.h @@ -0,0 +1,24 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (C) 2021 The Blosc Developers + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +enum { + BLOSC_BTUNE = 32, +}; + +void register_tuners(void); + +// For dynamically loaded tunes +typedef struct { + char *init; + char *next_blocksize; + char *next_cparams; + char *update; + char *free; +} tuner_info; diff --git a/internal-complibs/zlib-ng-2.0.6/.gitattributes b/internal-complibs/zlib-ng-2.0.6/.gitattributes deleted file mode 100644 index 3b35744b..00000000 --- a/internal-complibs/zlib-ng-2.0.6/.gitattributes +++ /dev/null @@ -1,6 +0,0 @@ -* text=auto -*.c text -*.h text -Makefile text -configure text eol=lf -testCVEinputs.sh text eol=lf diff --git a/internal-complibs/zlib-ng-2.0.6/.github/workflows/analyze.yml b/internal-complibs/zlib-ng-2.0.6/.github/workflows/analyze.yml deleted file mode 100644 index d9d2555b..00000000 --- a/internal-complibs/zlib-ng-2.0.6/.github/workflows/analyze.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: CI Static Analysis -on: [push, pull_request] -jobs: - GCC: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Install packages (Ubuntu) - run: | - sudo apt-get install -y gcc-10 - - - name: Generate project files - run: | - cmake . -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DWITH_FUZZERS=OFF -DWITH_CODE_COVERAGE=OFF -DWITH_MAINTAINER_WARNINGS=OFF - env: - CC: gcc-10 - CFLAGS: "-fanalyzer -Werror -Wanalyzer-double-fclose -Wanalyzer-double-free -Wanalyzer-exposure-through-output-file -Wanalyzer-file-leak -Wanalyzer-free-of-non-heap -Wanalyzer-malloc-leak -Wanalyzer-null-argument -Wanalyzer-null-dereference -Wanalyzer-possible-null-argument -Wanalyzer-possible-null-dereference -Wanalyzer-stale-setjmp-buffer -Wanalyzer-tainted-array-index -Wanalyzer-unsafe-call-within-signal-handler -Wanalyzer-use-after-free -Wanalyzer-use-of-pointer-in-stale-stack-frame" - CI: true - - - name: Compile source code - run: | - cmake --build . --config Release > /dev/null - - Clang: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Install packages (Ubuntu) - run: | - sudo apt-get install clang-tools -y - - - name: Generate project files - run: | - scan-build --status-bugs cmake . -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DWITH_FUZZERS=OFF -DWITH_CODE_COVERAGE=OFF -DWITH_MAINTAINER_WARNINGS=OFF - env: - CI: true - - - name: Compile source code - run: | - scan-build --status-bugs cmake --build . --config Release > /dev/null diff --git a/internal-complibs/zlib-ng-2.0.6/.github/workflows/cmake.yml b/internal-complibs/zlib-ng-2.0.6/.github/workflows/cmake.yml deleted file mode 100644 index 6629a4b7..00000000 --- a/internal-complibs/zlib-ng-2.0.6/.github/workflows/cmake.yml +++ /dev/null @@ -1,452 +0,0 @@ -name: CI CMake -on: [push, pull_request] -jobs: - ci-cmake: - name: ${{ matrix.name }} - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - include: - - name: Ubuntu 18.04 GCC - os: ubuntu-18.04 - compiler: gcc - - - name: Ubuntu GCC - os: ubuntu-latest - compiler: gcc - cmake-args: -DWITH_SANITIZER=Address - codecov: ubuntu_gcc - - - name: Ubuntu GCC OSB -O1 No Unaligned64 - os: ubuntu-latest - compiler: gcc - cmake-args: -DWITH_UNALIGNED=ON -DUNALIGNED64_OK=OFF -DWITH_SANITIZER=Undefined - build-dir: ../build - build-src-dir: ../zlib-ng - codecov: ubuntu_gcc_osb - cflags: -O1 -g3 - - - name: Ubuntu GCC -O3 No Unaligned - os: ubuntu-latest - compiler: gcc - cmake-args: -DWITH_UNALIGNED=OFF - codecov: ubuntu_gcc_o3 - cflags: -O3 - - - name: Ubuntu GCC No CTZLL - os: ubuntu-latest - compiler: gcc - cmake-args: -DWITH_OPTIM=OFF -DHAVE_BUILTIN_CTZLL=OFF - codecov: ubuntu_gcc_no_ctzll - - - name: Ubuntu GCC No CTZ - os: ubuntu-latest - compiler: gcc - cmake-args: -DWITH_OPTIM=OFF -DHAVE_BUILTIN_CTZLL=OFF -DHAVE_BUILTIN_CTZ=OFF - codecov: ubuntu_gcc_no_ctz - - - name: Ubuntu GCC Link Zlib - os: ubuntu-latest - compiler: gcc - cmake-args: -DZLIB_DUAL_LINK=ON - - - name: Ubuntu GCC No AVX2 - os: ubuntu-latest - compiler: gcc - cmake-args: -DWITH_AVX2=OFF -DWITH_SANITIZER=Undefined - codecov: ubuntu_gcc_no_avx2 - - - name: Ubuntu GCC No SSE2 - os: ubuntu-latest - compiler: gcc - cmake-args: -DWITH_SSE2=OFF -DWITH_SANITIZER=Undefined - codecov: ubuntu_gcc_no_sse2 - - - name: Ubuntu GCC No SSE4 - os: ubuntu-latest - compiler: gcc - cmake-args: -DWITH_SSE4=OFF -DWITH_SANITIZER=Undefined - codecov: ubuntu_gcc_no_sse4 - - - name: Ubuntu GCC No PCLMULQDQ - os: ubuntu-latest - compiler: gcc - cmake-args: -DWITH_PCLMULQDQ=OFF -DWITH_SANITIZER=Undefined - codecov: ubuntu_gcc_no_pclmulqdq - - - name: Ubuntu GCC Compat No Opt - os: ubuntu-latest - compiler: gcc - cmake-args: -DZLIB_COMPAT=ON -DWITH_NEW_STRATEGIES=OFF -DWITH_OPTIM=OFF -DWITH_SANITIZER=Address - codecov: ubuntu_gcc_compat_no_opt - cflags: -DNOT_TWEAK_COMPILER - - - name: Ubuntu GCC ARM SF - os: ubuntu-latest - compiler: arm-linux-gnueabi-gcc - cmake-args: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-arm.cmake -DCMAKE_C_COMPILER_TARGET=arm-linux-gnueabi -DWITH_SANITIZER=Address - asan-options: detect_leaks=0 - packages: qemu qemu-user gcc-arm-linux-gnueabi libc-dev-armel-cross - qemu-run: qemu-arm - codecov: ubuntu_gcc_armsf - - - name: Ubuntu GCC ARM SF Compat No Opt - os: ubuntu-latest - compiler: arm-linux-gnueabi-gcc - cmake-args: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-arm.cmake -DCMAKE_C_COMPILER_TARGET=arm-linux-gnueabi -DZLIB_COMPAT=ON -DWITH_NEW_STRATEGIES=OFF -DWITH_OPTIM=OFF -DWITH_SANITIZER=Undefined - asan-options: detect_leaks=0 - packages: qemu qemu-user gcc-arm-linux-gnueabi libc-dev-armel-cross - qemu-run: qemu-arm - codecov: ubuntu_gcc_armsf_compat_no_opt - - - name: Ubuntu GCC ARM HF - os: ubuntu-latest - compiler: arm-linux-gnueabihf-gcc - cmake-args: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-arm.cmake -DCMAKE_C_COMPILER_TARGET=arm-linux-gnueabihf -DWITH_SANITIZER=Address - asan-options: detect_leaks=0 - packages: qemu qemu-user gcc-arm-linux-gnueabihf libc-dev-armel-cross - qemu-run: qemu-arm - codecov: ubuntu_gcc_armhf - - - name: Ubuntu GCC ARM HF No ACLE - os: ubuntu-latest - compiler: arm-linux-gnueabihf-gcc - cmake-args: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-arm.cmake -DCMAKE_C_COMPILER_TARGET=arm-linux-gnueabihf -DWITH_ACLE=OFF -DWITH_SANITIZER=Address - asan-options: detect_leaks=0 - packages: qemu qemu-user gcc-arm-linux-gnueabihf libc-dev-armel-cross - qemu-run: qemu-arm - codecov: ubuntu_gcc_armhf_no_acle - - - name: Ubuntu GCC ARM HF No NEON - os: ubuntu-latest - compiler: arm-linux-gnueabihf-gcc - cmake-args: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-arm.cmake -DCMAKE_C_COMPILER_TARGET=arm-linux-gnueabihf -DWITH_NEON=OFF -DWITH_SANITIZER=Address - asan-options: detect_leaks=0 - packages: qemu qemu-user gcc-arm-linux-gnueabihf libc-dev-armel-cross - qemu-run: qemu-arm - codecov: ubuntu_gcc_armhf_no_neon - - - name: Ubuntu GCC ARM HF Compat No Opt - os: ubuntu-latest - compiler: arm-linux-gnueabihf-gcc - cmake-args: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-arm.cmake -DCMAKE_C_COMPILER_TARGET=arm-linux-gnueabihf -DZLIB_COMPAT=ON -DWITH_NEW_STRATEGIES=OFF -DWITH_OPTIM=OFF -DWITH_SANITIZER=Undefined - asan-options: detect_leaks=0 - packages: qemu qemu-user gcc-arm-linux-gnueabihf libc-dev-armel-cross - qemu-run: qemu-arm - codecov: ubuntu_gcc_armhf_compat_no_opt - - - name: Ubuntu GCC AARCH64 - os: ubuntu-latest - compiler: aarch64-linux-gnu-gcc - cmake-args: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-aarch64.cmake -DWITH_SANITIZER=Address - asan-options: detect_leaks=0 - packages: qemu qemu-user gcc-aarch64-linux-gnu libc-dev-arm64-cross - qemu-run: qemu-aarch64 - codecov: ubuntu_gcc_aarch64 - - - name: Ubuntu GCC AARCH64 No ACLE - os: ubuntu-latest - compiler: aarch64-linux-gnu-gcc - cmake-args: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-aarch64.cmake -DWITH_ACLE=OFF -DWITH_SANITIZER=Undefined - asan-options: detect_leaks=0 - packages: qemu qemu-user gcc-aarch64-linux-gnu libc-dev-arm64-cross - qemu-run: qemu-aarch64 - codecov: ubuntu_gcc_aarch64_no_acle - - - name: Ubuntu GCC AARCH64 No NEON - os: ubuntu-latest - compiler: aarch64-linux-gnu-gcc - cmake-args: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-aarch64.cmake -DWITH_NEON=OFF -DWITH_SANITIZER=Undefined - asan-options: detect_leaks=0 - packages: qemu qemu-user gcc-aarch64-linux-gnu libc-dev-arm64-cross - qemu-run: qemu-aarch64 - codecov: ubuntu_gcc_aarch64_no_neon - - - name: Ubuntu GCC AARCH64 Compat No Opt - os: ubuntu-latest - compiler: aarch64-linux-gnu-gcc - cmake-args: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-aarch64.cmake -DZLIB_COMPAT=ON -DWITH_NEW_STRATEGIES=OFF -DWITH_OPTIM=OFF -DWITH_SANITIZER=Undefined - asan-options: detect_leaks=0 - packages: qemu qemu-user gcc-aarch64-linux-gnu libc-dev-arm64-cross - qemu-run: qemu-aarch64 - codecov: ubuntu_gcc_aarch64_compat_no_opt - - - name: Ubuntu GCC PPC - os: ubuntu-latest - compiler: powerpc-linux-gnu-gcc - cmake-args: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-powerpc.cmake - asan-options: detect_leaks=0 - packages: qemu qemu-user gcc-powerpc-linux-gnu libc-dev-powerpc-cross - qemu-run: qemu-ppc - ldflags: -static - codecov: ubuntu_gcc_ppc - - - name: Ubuntu GCC PPC64 - os: ubuntu-latest - compiler: powerpc64-linux-gnu-gcc - cmake-args: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-powerpc64.cmake - asan-options: detect_leaks=0 - packages: qemu qemu-user gcc-powerpc64-linux-gnu libc-dev-ppc64-cross - qemu-run: qemu-ppc64 - ldflags: -static - codecov: ubuntu_gcc_ppc64 - - - name: Ubuntu GCC PPC64LE - os: ubuntu-latest - compiler: powerpc64le-linux-gnu-gcc - cmake-args: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-powerpc64le.cmake - asan-options: detect_leaks=0 - packages: qemu qemu-user gcc-powerpc64le-linux-gnu libc-dev-ppc64el-cross - qemu-run: qemu-ppc64le - codecov: ubuntu_gcc_ppc64le - - - name: Ubuntu GCC SPARC64 - os: ubuntu-latest - compiler: sparc64-linux-gnu-gcc - cmake-args: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-sparc64.cmake - asan-options: detect_leaks=0 - packages: qemu qemu-user gcc-sparc64-linux-gnu libc-dev-sparc64-cross - qemu-run: qemu-sparc64 - ldflags: -static - codecov: ubuntu_gcc_sparc64 - - - name: Ubuntu GCC S390X - os: ubuntu-latest - compiler: s390x-linux-gnu-gcc - cmake-args: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-s390x.cmake -DWITH_SANITIZER=Address - asan-options: detect_leaks=0 - packages: qemu qemu-user gcc-s390x-linux-gnu libc-dev-s390x-cross - qemu-run: qemu-s390x - ldflags: -static - codecov: ubuntu_gcc_s390x - - - name: Ubuntu GCC S390X DFLTCC - os: z15 - compiler: gcc - cmake-args: -DWITH_DFLTCC_DEFLATE=ON -DWITH_DFLTCC_INFLATE=ON -DWITH_SANITIZER=Address - asan-options: detect_leaks=0 - codecov: ubuntu_gcc_s390x - - - name: Ubuntu GCC S390X DFLTCC Compat - os: z15 - compiler: gcc - cmake-args: -DZLIB_COMPAT=ON -DWITH_DFLTCC_DEFLATE=ON -DWITH_DFLTCC_INFLATE=ON -DWITH_SANITIZER=Undefined - asan-options: detect_leaks=0 - codecov: ubuntu_gcc_s390x - - - name: Ubuntu MinGW i686 - os: ubuntu-latest - compiler: i686-w64-mingw32-gcc - cmake-args: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-mingw-i686.cmake - packages: wine32 gcc-mingw-w64 - codecov: ubuntu_gcc_mingw_i686 - # Limit parallel test jobs to prevent wine errors - parallels-jobs: 1 - - - name: Ubuntu MinGW x86_64 - os: ubuntu-latest - compiler: x86_64-w64-mingw32-gcc - cmake-args: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-mingw-x86_64.cmake - packages: wine-stable gcc-mingw-w64 - codecov: ubuntu_gcc_mingw_x86_64 - # Limit parallel test jobs to prevent wine errors - parallels-jobs: 1 - - - name: Ubuntu 18.04 Clang - os: ubuntu-18.04 - compiler: clang-6.0 - packages: clang-6.0 - - - name: Ubuntu Clang - os: ubuntu-latest - compiler: clang-11 - packages: llvm-11-tools - gcov-exec: llvm-cov-11 gcov - codecov: ubuntu_clang - - - name: Ubuntu Clang Inflate Strict - os: ubuntu-latest - compiler: clang-11 - cmake-args: -DWITH_INFLATE_STRICT=ON - packages: llvm-11-tools - gcov-exec: llvm-cov-11 gcov - codecov: ubuntu_clang_inflate_strict - - - name: Ubuntu Clang Inflate Allow Invalid Dist - os: ubuntu-latest - compiler: clang-11 - cmake-args: -DWITH_INFLATE_ALLOW_INVALID_DIST=ON - packages: llvm-11-tools - gcov-exec: llvm-cov-11 gcov - codecov: ubuntu_clang_inflate_allow_invalid_dist - - - name: Ubuntu Clang Memory Map - os: ubuntu-latest - compiler: clang-11 - cflags: -DUSE_MMAP - packages: llvm-11-tools - gcov-exec: llvm-cov-11 gcov - codecov: ubuntu_clang_mmap - - - name: Ubuntu Clang Debug - os: ubuntu-latest - compiler: clang-11 - packages: llvm-11-tools - gcov-exec: llvm-cov-11 gcov - codecov: ubuntu_clang_debug - build-config: Debug - - - name: Ubuntu Clang MSAN - os: ubuntu-latest - compiler: clang-11 - cmake-args: -GNinja -DWITH_SANITIZER=Memory - packages: ninja-build llvm-11-tools - gcov-exec: llvm-cov-11 gcov - cflags: -g3 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize-memory-track-origins - codecov: ubuntu_clang_msan - - - name: Windows MSVC Win32 - os: windows-latest - compiler: cl - cmake-args: -A Win32 - - - name: Windows MSVC Win64 - os: windows-latest - compiler: cl - cmake-args: -A x64 - - - name: Windows MSVC ARM No Test - os: windows-latest - compiler: cl - cmake-args: -A ARM - - - name: Windows MSVC ARM64 No Test - os: windows-latest - compiler: cl - cmake-args: -A ARM64 - - - name: Windows GCC - os: windows-latest - compiler: gcc - cmake-args: -G Ninja - codecov: win64_gcc - - - name: Windows GCC Compat No Opt - os: windows-latest - compiler: gcc - cmake-args: -G Ninja -DZLIB_COMPAT=ON -DWITH_NEW_STRATEGIES=OFF -DWITH_OPTIM=OFF - codecov: win64_gcc_compat_no_opt - - - name: macOS Clang - os: macos-latest - compiler: clang - cmake-args: -DWITH_SANITIZER=Address - codecov: macos_clang - - - name: macOS GCC - os: macos-latest - compiler: gcc-10 - cmake-args: -DWITH_SANITIZER=Undefined - packages: gcc@10 - gcov-exec: gcov-10 - codecov: macos_gcc - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Checkout test corpora - uses: actions/checkout@v2 - # Don't test against all corpora with MinGW due to Wine being unable to run parallel jobs - # without connection timeout. Without parallel jobs test runs using Wine take close to an hour. - if: contains(matrix.name, 'MinGW') == false - with: - repository: zlib-ng/corpora - path: test/data/corpora - - - name: Install packages (Ubuntu) - if: runner.os == 'Linux' && matrix.packages - run: | - sudo dpkg --add-architecture i386 # Required for wine32 - sudo apt-add-repository ppa:ondrej/php -y - sudo apt-get update - sudo apt-get install -y ${{ matrix.packages }} - - - name: Install packages (Windows) - if: runner.os == 'Windows' - run: | - choco install ninja ${{ matrix.packages }} --no-progress - - - name: Install packages (macOS) - if: runner.os == 'macOS' - run: | - brew install ninja ${{ matrix.packages }} - env: - HOMEBREW_NO_INSTALL_CLEANUP: 1 - - - name: Install codecov.io tools - if: matrix.codecov - run: | - python -u -m pip install --user codecov - - - name: Initialize Wine - # Prevent parallel test jobs from initializing Wine at the same time - if: contains(matrix.packages, 'wine') - run: | - wineboot --init - - - name: Generate project files - # Shared libraries turned off for qemu ppc* and sparc & reduce code coverage sources - run: | - mkdir ${{ matrix.build-dir || '.not-used' }} - cd ${{ matrix.build-dir || '.' }} - cmake ${{ matrix.build-src-dir || '.' }} ${{ matrix.cmake-args }} -DCMAKE_BUILD_TYPE=${{ matrix.build-config || 'Release' }} -DBUILD_SHARED_LIBS=OFF -DWITH_FUZZERS=ON -DWITH_CODE_COVERAGE=ON -DWITH_MAINTAINER_WARNINGS=ON - env: - CC: ${{ matrix.compiler }} - CFLAGS: ${{ matrix.cflags }} - LDFLAGS: ${{ matrix.ldflags }} - CI: true - - - name: Compile source code - run: | - cd ${{ matrix.build-dir || '.' }} - cmake --build . --config ${{ matrix.build-config || 'Release' }} - - - name: Run test cases - # Don't run tests on Windows ARM - if: runner.os != 'Windows' || contains(matrix.name, 'ARM') == false - run: | - cd ${{ matrix.build-dir || '.' }} - ctest --verbose -C Release --output-on-failure --max-width 120 -j ${{ matrix.parallels-jobs || '6' }} - env: - ASAN_OPTIONS: ${{ matrix.asan-options || 'verbosity=0' }}:abort_on_error=1 - MSAN_OPTIONS: ${{ matrix.msan-options || 'verbosity=0' }}:abort_on_error=1 - TSAN_OPTIONS: ${{ matrix.tsan-options || 'verbosity=0' }}:abort_on_error=1 - LSAN_OPTIONS: ${{ matrix.lsan-options || 'verbosity=0' }}:abort_on_error=1 - QEMU_RUN: ${{ matrix.qemu-run }} - - - name: Upload coverage report - if: matrix.codecov && ( env.CODECOV_TOKEN_SECRET != '' || github.repository == 'zlib-ng/zlib-ng' ) - shell: bash - run: | - bash tools/codecov-upload.sh - env: - # Codecov does not yet support GitHub Actions - CODECOV_TOKEN_SECRET: "${{secrets.CODECOV_TOKEN}}" - CODECOV_TOKEN: "${{ secrets.CODECOV_TOKEN || 'e4fdf847-f541-4ab1-9d50-3d27e5913906' }}" - CODECOV_FLAGS: "${{ matrix.codecov }}" - CODECOV_NAME: "${{ matrix.name }}" - CODECOV_EXEC: "${{ matrix.gcov-exec || 'gcov' }}" - CODECOV_DIR: "${{ matrix.build-dir || '.' }}" - - - name: Upload build errors - uses: actions/upload-artifact@v2 - if: failure() - with: - name: ${{ matrix.name }} (cmake) - path: | - ${{ matrix.build-dir || '.' }}/CMakeFiles/CMakeOutput.log - ${{ matrix.build-dir || '.' }}/CMakeFiles/CMakeError.log - ${{ matrix.build-dir || '.' }}/Testing/Temporary/* - retention-days: 30 diff --git a/internal-complibs/zlib-ng-2.0.6/.github/workflows/configure.yml b/internal-complibs/zlib-ng-2.0.6/.github/workflows/configure.yml deleted file mode 100644 index d85d966f..00000000 --- a/internal-complibs/zlib-ng-2.0.6/.github/workflows/configure.yml +++ /dev/null @@ -1,207 +0,0 @@ -name: CI Configure -on: [push, pull_request] -jobs: - ci-configure: - name: ${{ matrix.name }} - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - include: - - name: Ubuntu GCC - os: ubuntu-latest - compiler: gcc - configure-args: --warn - - - name: Ubuntu 18.04 GCC - os: ubuntu-18.04 - compiler: gcc - configure-args: --warn - - - name: Ubuntu GCC OSB - os: ubuntu-latest - compiler: gcc - configure-args: --warn - build-dir: ../build - build-src-dir: ../zlib-ng - - - name: Ubuntu GCC Compat No Opt - os: ubuntu-latest - compiler: gcc - configure-args: --warn --zlib-compat --without-optimizations --without-new-strategies - - - name: Ubuntu GCC ARM SF - os: ubuntu-latest - compiler: arm-linux-gnueabi-gcc - configure-args: --warn - chost: arm-linux-gnueabi - packages: qemu qemu-user gcc-arm-linux-gnueabi libc-dev-armel-cross - qemu-run: qemu-arm - - - name: Ubuntu GCC ARM SF Compat No Opt - os: ubuntu-latest - compiler: arm-linux-gnueabi-gcc - configure-args: --warn --zlib-compat --without-optimizations --without-new-strategies - chost: arm-linux-gnueabi - packages: qemu qemu-user gcc-arm-linux-gnueabi libc-dev-armel-cross - qemu-run: qemu-arm - - - name: Ubuntu GCC ARM HF - os: ubuntu-latest - compiler: arm-linux-gnueabihf-gcc - configure-args: --warn - chost: arm-linux-gnueabihf - packages: qemu qemu-user gcc-arm-linux-gnueabihf libc-dev-armel-cross - qemu-run: qemu-arm - - - name: Ubuntu GCC ARM HF No ACLE - os: ubuntu-latest - compiler: arm-linux-gnueabihf-gcc - configure-args: --warn --without-acle - chost: arm-linux-gnueabihf - packages: qemu qemu-user gcc-arm-linux-gnueabihf libc-dev-armel-cross - qemu-run: qemu-arm - - - name: Ubuntu GCC ARM HF No NEON - os: ubuntu-latest - compiler: arm-linux-gnueabihf-gcc - configure-args: --warn --without-neon - chost: arm-linux-gnueabihf - packages: qemu qemu-user gcc-arm-linux-gnueabihf libc-dev-armel-cross - qemu-run: qemu-arm - - - name: Ubuntu GCC ARM HF Compat No Opt - os: ubuntu-latest - compiler: arm-linux-gnueabihf-gcc - configure-args: --warn --zlib-compat --without-optimizations --without-new-strategies - chost: arm-linux-gnueabihf - packages: qemu qemu-user gcc-arm-linux-gnueabihf libc-dev-armel-cross - qemu-run: qemu-arm - - - name: Ubuntu GCC AARCH64 - os: ubuntu-latest - compiler: aarch64-linux-gnu-gcc - configure-args: --warn - chost: aarch64-linux-gnu - packages: qemu qemu-user gcc-aarch64-linux-gnu libc-dev-arm64-cross - qemu-run: qemu-aarch64 - - - name: Ubuntu GCC AARCH64 No ACLE - os: ubuntu-latest - compiler: aarch64-linux-gnu-gcc - configure-args: --warn --without-acle - chost: aarch64-linux-gnu - packages: qemu qemu-user gcc-aarch64-linux-gnu libc-dev-arm64-cross - qemu-run: qemu-aarch64 - - - name: Ubuntu GCC AARCH64 No NEON - os: ubuntu-latest - compiler: aarch64-linux-gnu-gcc - configure-args: --warn --without-neon - chost: aarch64-linux-gnu - packages: qemu qemu-user gcc-aarch64-linux-gnu libc-dev-arm64-cross - qemu-run: qemu-aarch64 - - - name: Ubuntu GCC AARCH64 Compat No Opt - os: ubuntu-latest - compiler: aarch64-linux-gnu-gcc - configure-args: --warn --zlib-compat --without-optimizations --without-new-strategies - chost: aarch64-linux-gnu - packages: qemu qemu-user gcc-aarch64-linux-gnu libc-dev-arm64-cross - qemu-run: qemu-aarch64 - - - name: Ubuntu GCC PPC - os: ubuntu-latest - compiler: powerpc-linux-gnu-gcc - configure-args: --warn --static - chost: powerpc-linux-gnu - packages: qemu qemu-user gcc-powerpc-linux-gnu libc-dev-powerpc-cross - qemu-run: qemu-ppc - cflags: -static - ldflags: -static - - - name: Ubuntu GCC PPC64 - os: ubuntu-latest - compiler: powerpc64-linux-gnu-gcc - configure-args: --warn --static - chost: powerpc-linux-gnu - packages: qemu qemu-user gcc-powerpc64-linux-gnu libc-dev-ppc64-cross - qemu-run: qemu-ppc64 - cflags: -static - ldflags: -static - - - name: Ubuntu GCC PPC64LE - os: ubuntu-latest - compiler: powerpc64le-linux-gnu-gcc - configure-args: --warn - chost: powerpc64le-linux-gnu - packages: qemu qemu-user gcc-powerpc64le-linux-gnu libc-dev-ppc64el-cross - qemu-run: qemu-ppc64le - - - name: Ubuntu GCC S390X - os: ubuntu-latest - compiler: s390x-linux-gnu-gcc - configure-args: --warn --static - chost: s390x-linux-gnu - packages: qemu qemu-user gcc-s390x-linux-gnu libc-dev-s390x-cross - qemu-run: qemu-s390x - cflags: -static - ldflags: -static - - - name: Ubuntu GCC S390X DFLTCC - os: z15 - compiler: gcc - configure-args: --warn --static --with-dfltcc-deflate --with-dfltcc-inflate - - - name: Ubuntu GCC S390X DFLTCC Compat - os: z15 - compiler: gcc - configure-args: --warn --zlib-compat --static --with-dfltcc-deflate --with-dfltcc-inflate - - - name: macOS GCC - os: macOS-latest - compiler: gcc - configure-args: --warn - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Install packages (Ubuntu) - if: runner.os == 'Linux' && matrix.packages - run: | - sudo apt-get update - sudo apt-get install -y ${{ matrix.packages }} - - - name: Generate project files - run: | - mkdir ${{ matrix.build-dir || '.not-used' }} - cd ${{ matrix.build-dir || '.' }} - ${{ matrix.build-src-dir || '.' }}/configure ${{ matrix.configure-args }} - env: - CC: ${{ matrix.compiler }} - CFLAGS: ${{ matrix.cflags }} - LDFLAGS: ${{ matrix.ldflags }} - CHOST: ${{ matrix.chost }} - CI: true - - - name: Compile source code - run: | - cd ${{ matrix.build-dir || '.' }} - make -j2 - - - name: Run test cases - run: | - cd ${{ matrix.build-dir || '.' }} - make test - env: - QEMU_RUN: ${{ matrix.qemu-run }} - - - name: Upload build errors - uses: actions/upload-artifact@v2 - if: failure() - with: - name: ${{ matrix.name }} (configure) - path: | - ${{ matrix.build-dir || '.' }}/configure.log - retention-days: 30 diff --git a/internal-complibs/zlib-ng-2.0.6/.github/workflows/fuzz.yml b/internal-complibs/zlib-ng-2.0.6/.github/workflows/fuzz.yml deleted file mode 100644 index e7ddaea5..00000000 --- a/internal-complibs/zlib-ng-2.0.6/.github/workflows/fuzz.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: CI Fuzz -on: [pull_request] -jobs: - Fuzzing: - runs-on: ubuntu-latest - steps: - - name: Build Fuzzers - uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master - with: - oss-fuzz-project-name: 'zlib-ng' - dry-run: false - - name: Run Fuzzers - uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master - with: - oss-fuzz-project-name: 'zlib-ng' - fuzz-seconds: 600 - dry-run: false - - name: Upload Crash - uses: actions/upload-artifact@v1 - if: failure() - with: - name: artifacts - path: ./out/artifacts diff --git a/internal-complibs/zlib-ng-2.0.6/.github/workflows/libpng.yml b/internal-complibs/zlib-ng-2.0.6/.github/workflows/libpng.yml deleted file mode 100644 index b3d989f7..00000000 --- a/internal-complibs/zlib-ng-2.0.6/.github/workflows/libpng.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: CI Libpng -on: [pull_request] -jobs: - pngtest: - name: Ubuntu Clang - runs-on: ubuntu-latest - - steps: - - name: Checkout repository (zlib-ng) - uses: actions/checkout@v2 - - - name: Generate project files (zlib-ng) - run: | - cmake . -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DZLIB_COMPAT=ON -DZLIB_ENABLE_TESTS=OFF - env: - CC: clang - CFLAGS: -fPIC - CI: true - - - name: Compile source code (zlib-ng) - run: | - cmake --build . --config Release - - - name: Checkout repository (libpng) - uses: actions/checkout@v2 - with: - repository: glennrp/libpng - path: libpng - - - name: Generate project files (libpng) - run: | - cd libpng - cmake . -DCMAKE_BUILD_TYPE=Release -DPNG_TESTS=ON -DPNG_STATIC=OFF -DZLIB_INCLUDE_DIR=.. -DZLIB_LIBRARY=$PWD/../libz.a - env: - CC: clang - CI: true - - - name: Compile source code (libpng) - run: | - cd libpng - cmake --build . --config Release - - - name: Run test cases (libpng) - run: | - cd libpng - ctest -C Release --output-on-failure --max-width 120 diff --git a/internal-complibs/zlib-ng-2.0.6/.github/workflows/nmake.yml b/internal-complibs/zlib-ng-2.0.6/.github/workflows/nmake.yml deleted file mode 100644 index cf198986..00000000 --- a/internal-complibs/zlib-ng-2.0.6/.github/workflows/nmake.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: CI NMake -on: [push, pull_request] -jobs: - ci-cmake: - name: ${{ matrix.name }} - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - include: - - name: Windows NMake x86 - os: windows-latest - makefile: win32/Makefile.msc - vc-vars: x86 - - - name: Windows NMake x64 - os: windows-latest - makefile: win32/Makefile.msc - vc-vars: x86_amd64 - - - name: Windows NMake ARM No Test - os: windows-latest - makefile: win32/Makefile.arm - vc-vars: x86_arm - - - name: Windows NMake ARM64 No Test - os: windows-latest - makefile: win32/Makefile.a64 - vc-vars: x86_arm64 - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Compile source code - shell: cmd - run: | - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.vc-vars }} - nmake -f ${{ matrix.makefile }} - - - name: Run test cases - shell: cmd - # Don't run tests on Windows ARM - if: contains(matrix.vc-vars, 'arm') == false - run: | - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.vc-vars }} - nmake -f ${{ matrix.makefile }} test - nmake -f ${{ matrix.makefile }} testdll diff --git a/internal-complibs/zlib-ng-2.0.6/.github/workflows/pigz.yml b/internal-complibs/zlib-ng-2.0.6/.github/workflows/pigz.yml deleted file mode 100644 index 8260c82f..00000000 --- a/internal-complibs/zlib-ng-2.0.6/.github/workflows/pigz.yml +++ /dev/null @@ -1,111 +0,0 @@ -name: CI Pigz -on: [push, pull_request] -jobs: - ci-cmake: - name: ${{ matrix.name }} - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - include: - - name: Ubuntu GCC - os: ubuntu-latest - compiler: gcc - codecov: ubuntu_gcc_pigz - - - name: Ubuntu Clang - os: ubuntu-latest - compiler: clang - packages: llvm-11-tools - gcov-exec: llvm-cov-11 gcov - codecov: ubuntu_clang_pigz - - - name: Ubuntu Clang No Optim - os: ubuntu-latest - compiler: clang - packages: llvm-11-tools - gcov-exec: llvm-cov-11 gcov - codecov: ubuntu_clang_pigz_no_optim - cmake-args: -DWITH_OPTIM=OFF - - - name: Ubuntu Clang No Threads - os: ubuntu-latest - compiler: clang - packages: llvm-11-tools - gcov-exec: llvm-cov-11 gcov - codecov: ubuntu_clang_pigz_no_threads - cmake-args: -DWITH_THREADS=OFF - - - name: Ubuntu GCC AARCH64 - os: ubuntu-latest - compiler: aarch64-linux-gnu-gcc - cmake-args: -DCMAKE_TOOLCHAIN_FILE=../../cmake/toolchain-aarch64.cmake - packages: qemu qemu-user gcc-aarch64-linux-gnu libc-dev-arm64-cross - qemu-run: qemu-aarch64 - codecov: ubuntu_gcc_pigz_aarch64 - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Checkout test corpora - uses: actions/checkout@v2 - with: - repository: zlib-ng/corpora - path: test/data/corpora - - - name: Install packages (Ubuntu) - if: runner.os == 'Linux' && matrix.packages - run: | - sudo apt-get update - sudo apt-get install -y ${{ matrix.packages }} - - - name: Install codecov.io tools - if: matrix.codecov - run: | - python -u -m pip install codecov - - - name: Generate project files - run: | - cd test/pigz - cmake ${{ matrix.cmake-args }} -DCMAKE_BUILD_TYPE=${{ matrix.build-config || 'Release' }} -DBUILD_SHARED_LIBS=OFF -DZLIB_ROOT=../.. -DWITH_CODE_COVERAGE=ON -DWITH_MAINTAINER_WARNINGS=ON - env: - CC: ${{ matrix.compiler }} - CFLAGS: ${{ matrix.cflags }} - LDFLAGS: ${{ matrix.ldflags }} - CI: true - - - name: Compile source code - run: | - cd test/pigz - cmake --build . --config ${{ matrix.build-config || 'Release' }} - - - name: Run test cases - run: | - cd test/pigz - ctest --verbose -C Release --output-on-failure --max-width 120 -j ${{ matrix.parallels-jobs || '2' }} - - - name: Upload coverage report - if: matrix.codecov && ( env.CODECOV_TOKEN_SECRET != '' || github.repository == 'zlib-ng/zlib-ng' ) - shell: bash - run: | - bash tools/codecov-upload.sh - env: - # Codecov does not yet support GitHub Actions - CODECOV_TOKEN_SECRET: "${{secrets.CODECOV_TOKEN}}" - CODECOV_TOKEN: "${{ secrets.CODECOV_TOKEN || 'e4fdf847-f541-4ab1-9d50-3d27e5913906' }}" - CODECOV_FLAGS: "${{ matrix.codecov }}" - CODECOV_NAME: "${{ matrix.name }}" - CODECOV_EXEC: "${{ matrix.gcov-exec || 'gcov' }}" - CODECOV_DIR: "." - - - name: Upload build errors - uses: actions/upload-artifact@v2 - if: failure() - with: - name: ${{ matrix.name }} (cmake) - path: | - **/CMakeFiles/CMakeOutput.log - **/CMakeFiles/CMakeError.log - **/Testing/Temporary/* - retention-days: 30 diff --git a/internal-complibs/zlib-ng-2.0.6/.github/workflows/pkgcheck.yml b/internal-complibs/zlib-ng-2.0.6/.github/workflows/pkgcheck.yml deleted file mode 100644 index dde91d37..00000000 --- a/internal-complibs/zlib-ng-2.0.6/.github/workflows/pkgcheck.yml +++ /dev/null @@ -1,147 +0,0 @@ -name: CI Pkgcheck -on: [push, pull_request] -jobs: - ci-pkgcheck: - name: ${{ matrix.name }} - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - include: - - name: Ubuntu GCC - os: ubuntu-latest - compiler: gcc - - - name: Ubuntu GCC -m32 - os: ubuntu-latest - compiler: gcc - packages: gcc-multilib - cmake-args: -DCMAKE_C_FLAGS=-m32 - cflags: -m32 - ldflags: -m32 - - - name: Ubuntu GCC ARM HF - os: ubuntu-latest - chost: arm-linux-gnueabihf - compiler: arm-linux-gnueabihf-gcc - cmake-args: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-arm.cmake -DCMAKE_C_COMPILER_TARGET=arm-linux-gnueabihf - packages: qemu gcc-arm-linux-gnueabihf libc6-dev-armhf-cross - - - name: Ubuntu GCC AARCH64 - os: ubuntu-latest - chost: aarch64-linux-gnu - compiler: aarch64-linux-gnu-gcc - cmake-args: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-aarch64.cmake - packages: qemu gcc-aarch64-linux-gnu libc6-dev-arm64-cross - - - name: Ubuntu GCC PPC - os: ubuntu-latest - chost: powerpc-linux-gnu - compiler: powerpc-linux-gnu-gcc - cmake-args: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-powerpc.cmake - packages: qemu gcc-powerpc-linux-gnu libc6-dev-powerpc-cross - - - name: Ubuntu GCC PPC64LE - os: ubuntu-latest - chost: powerpc64le-linux-gnu - compiler: powerpc64le-linux-gnu-gcc - cmake-args: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-powerpc64le.cmake - packages: qemu gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross - - - name: macOS Clang - os: macOS-latest - compiler: clang - - - name: macOS Clang Native - os: macOS-latest - compiler: clang - cmake-args: -DWITH_NATIVE_INSTRUCTIONS=ON - configure-args: --native - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Install packages (Ubuntu) - if: runner.os == 'Linux' - run: | - sudo apt-get update - sudo apt-get install -y --no-install-recommends abigail-tools ninja-build diffoscope ${{ matrix.packages }} - - - name: Install packages (macOS) - if: runner.os == 'macOS' - run: | - brew install ninja diffoscope ${{ matrix.packages }} - env: - HOMEBREW_NO_INSTALL_CLEANUP: 1 - - - name: Select Xcode version (macOS) - # Use a version of Xcode that supports ZERO_AR_DATE until CMake supports - # AppleClang linking with libtool using -D argument - # https://gitlab.kitware.com/cmake/cmake/-/issues/19852 - if: runner.os == 'macOS' - uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: '11.7.0' - - - name: Compare builds - run: | - sh test/pkgcheck.sh - env: - CC: ${{ matrix.compiler }} - CFLAGS: ${{ matrix.cflags }} - CHOST: ${{ matrix.chost }} - CMAKE_ARGS: ${{ matrix.cmake-args }} - CONFIGURE_ARGS: ${{ matrix.configure-args }} - LDFLAGS: ${{ matrix.ldflags }} - - - name: Compare builds (compat) - run: | - sh test/pkgcheck.sh --zlib-compat - env: - CC: ${{ matrix.compiler }} - CFLAGS: ${{ matrix.cflags }} - CHOST: ${{ matrix.chost }} - CMAKE_ARGS: ${{ matrix.cmake-args }} - CONFIGURE_ARGS: ${{ matrix.configure-args }} - LDFLAGS: ${{ matrix.ldflags }} - - - name: Check ABI - # macOS runner does not contain abigail - if: runner.os != 'macOS' - run: | - sh test/abicheck.sh --refresh_if - env: - CC: ${{ matrix.compiler }} - CFLAGS: ${{ matrix.cflags }} - CHOST: ${{ matrix.chost }} - CMAKE_ARGS: ${{ matrix.cmake-args }} - CONFIGURE_ARGS: ${{ matrix.configure-args }} - LDFLAGS: ${{ matrix.ldflags }} - - - name: Check ABI (compat) - # macOS runner does not contain abigail - if: runner.os != 'macOS' - run: | - sh test/abicheck.sh --zlib-compat --refresh_if - env: - CC: ${{ matrix.compiler }} - CFLAGS: ${{ matrix.cflags }} - CHOST: ${{ matrix.chost }} - CMAKE_ARGS: ${{ matrix.cmake-args }} - CONFIGURE_ARGS: ${{ matrix.configure-args }} - LDFLAGS: ${{ matrix.ldflags }} - - - name: Upload build errors - uses: actions/upload-artifact@v2 - if: failure() - with: - name: ${{ matrix.name }} - path: | - btmp1/configure.log - btmp1/CMakeFiles/CMakeOutput.log - btmp1/CMakeFiles/CMakeError.log - btmp2/configure.log - btmp2/CMakeFiles/CMakeOutput.log - btmp2/CMakeFiles/CMakeError.log - retention-days: 30 diff --git a/internal-complibs/zlib-ng-2.0.6/.github/workflows/release.yml b/internal-complibs/zlib-ng-2.0.6/.github/workflows/release.yml deleted file mode 100644 index e52d6a6d..00000000 --- a/internal-complibs/zlib-ng-2.0.6/.github/workflows/release.yml +++ /dev/null @@ -1,97 +0,0 @@ -name: CI Release -on: - push: - tags: - - '*' -jobs: - ci-cmake: - name: ${{ matrix.name }} - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - include: - - name: Windows MSVC Win32 - os: windows-latest - compiler: cl - cmake-args: -A Win32 - deploy-name: win-x86 - - - name: Windows MSVC Win32 Compat - os: windows-latest - compiler: cl - cmake-args: -A Win32 -DZLIB_COMPAT=ON - deploy-name: win-x86-compat - - - name: Windows MSVC Win64 - os: windows-latest - compiler: cl - cmake-args: -A x64 - deploy-name: win-x86-64 - - - name: Windows MSVC Win64 Compat - os: windows-latest - compiler: cl - cmake-args: -A x64 -DZLIB_COMPAT=ON - deploy-name: win-x86-64-compat - - - name: Windows MSVC ARM - os: windows-latest - compiler: cl - cmake-args: -A ARM - deploy-name: win-arm - - - name: Windows MSVC ARM Compat - os: windows-latest - compiler: cl - cmake-args: -A ARM -DZLIB_COMPAT=ON - deploy-name: win-arm-compat - - - name: Windows MSVC ARM64 - os: windows-latest - compiler: cl - cmake-args: -A ARM64 - deploy-name: win-arm64 - - - name: Windows MSVC ARM64 Compat - os: windows-latest - compiler: cl - cmake-args: -A ARM64 -DZLIB_COMPAT=ON - deploy-name: win-arm64-compat - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Set environment variables - shell: bash - run: echo "tag=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV - - - name: Generate project files - run: | - cmake . ${{ matrix.cmake-args }} -DCMAKE_BUILD_TYPE=Release -DZLIB_ENABLE_TESTS=ON -DCMAKE_INSTALL_PREFIX=out -DINSTALL_UTILS=ON - env: - CC: ${{ matrix.compiler }} - CI: true - - - name: Compile source code - run: | - cmake --build . --config Release --target install - - - name: Package release (Windows) - if: runner.os == 'Windows' - run: | - cd out - 7z a -tzip ../zlib-ng-${{ matrix.deploy-name }}.zip bin include lib ../LICENSE.md ../PORTING.md ../README.md - - - name: Upload release (Windows) - uses: svenstaro/upload-release-action@v1-release - if: runner.os == 'Windows' - with: - asset_name: zlib-ng-${{ matrix.deploy-name }}.zip - file: zlib-ng-${{ matrix.deploy-name }}.zip - tag: ${{env.tag}} - repo_token: ${{ secrets.GITHUB_TOKEN }} - overwrite: true - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" diff --git a/internal-complibs/zlib-ng-2.0.6/.gitignore b/internal-complibs/zlib-ng-2.0.6/.gitignore deleted file mode 100644 index bf420e57..00000000 --- a/internal-complibs/zlib-ng-2.0.6/.gitignore +++ /dev/null @@ -1,88 +0,0 @@ -*.diff -*.patch -*.orig -*.rej - -*~ -*.a -*.lo -*.o -*.dylib - -*.gcda -*.gcno -*.gcov - -/adler32_test -/adler32_testsh -/crc32_test -/crc32_testsh -/example -/example64 -/examplesh -/libz.so* -/libz-ng.so* -/makefixed -/minigzip -/minigzip64 -/minigzipsh -/switchlevels -/zlib.pc -/zlib-ng.pc -/CVE-2003-0107 - -.DS_Store -*_fuzzer -*.obj -*.exe -*.pdb -*.exp -*.lib -*.dll -*.res -foo.gz -*.manifest -*.opensdf -*.sln -*.sdf -*.vcxproj -*.vcxproj.filters -.vs - -CMakeCache.txt -CMakeFiles -Testing -/*.cmake -*.stackdump -*._h -zconf.h -zconf.h.cmakein -zconf.h.included -zconf-ng.h -zconf-ng.h.cmakein -ztest* - -configure.log -a.out - -/Makefile -/arch/arm/Makefile -/arch/generic/Makefile -/arch/power/Makefile -/arch/x86/Makefile -.kdev4 -*.kdev4 - -/Debug -/example.dir -/minigzip.dir -/zlib.dir -/zlibstatic.dir -/win32/Debug -/build/ -/build[.-]*/ -/btmp[12]/ -/pkgtmp[12]/ - -/.idea -/cmake-build-debug diff --git a/internal-complibs/zlib-ng-2.0.6/chunkset.c b/internal-complibs/zlib-ng-2.0.6/chunkset.c deleted file mode 100644 index b07e6f48..00000000 --- a/internal-complibs/zlib-ng-2.0.6/chunkset.c +++ /dev/null @@ -1,83 +0,0 @@ -/* chunkset.c -- inline functions to copy small data chunks. - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -#include "zbuild.h" -#include "zutil.h" - -// We need sizeof(chunk_t) to be 8, no matter what. -#if defined(UNALIGNED64_OK) -typedef uint64_t chunk_t; -#elif defined(UNALIGNED_OK) -typedef struct chunk_t { uint32_t u32[2]; } chunk_t; -#else -typedef struct chunk_t { uint8_t u8[8]; } chunk_t; -#endif - -#define CHUNK_SIZE 8 - -#define HAVE_CHUNKMEMSET_1 -#define HAVE_CHUNKMEMSET_4 -#define HAVE_CHUNKMEMSET_8 - -static inline void chunkmemset_1(uint8_t *from, chunk_t *chunk) { -#if defined(UNALIGNED64_OK) - *chunk = 0x0101010101010101 * (uint8_t)*from; -#elif defined(UNALIGNED_OK) - chunk->u32[0] = 0x01010101 * (uint8_t)*from; - chunk->u32[1] = chunk->u32[0]; -#else - memset(chunk, *from, sizeof(chunk_t)); -#endif -} - -static inline void chunkmemset_4(uint8_t *from, chunk_t *chunk) { -#if defined(UNALIGNED64_OK) - uint32_t half_chunk; - half_chunk = *(uint32_t *)from; - *chunk = 0x0000000100000001 * (uint64_t)half_chunk; -#elif defined(UNALIGNED_OK) - chunk->u32[0] = *(uint32_t *)from; - chunk->u32[1] = chunk->u32[0]; -#else - uint8_t *chunkptr = (uint8_t *)chunk; - memcpy(chunkptr, from, 4); - memcpy(chunkptr+4, from, 4); -#endif -} - -static inline void chunkmemset_8(uint8_t *from, chunk_t *chunk) { -#if defined(UNALIGNED64_OK) - *chunk = *(uint64_t *)from; -#elif defined(UNALIGNED_OK) - uint32_t* p = (uint32_t *)from; - chunk->u32[0] = p[0]; - chunk->u32[1] = p[1]; -#else - memcpy(chunk, from, sizeof(chunk_t)); -#endif -} - -static inline void loadchunk(uint8_t const *s, chunk_t *chunk) { - chunkmemset_8((uint8_t *)s, chunk); -} - -static inline void storechunk(uint8_t *out, chunk_t *chunk) { -#if defined(UNALIGNED64_OK) - *(uint64_t *)out = *chunk; -#elif defined(UNALIGNED_OK) - ((uint32_t *)out)[0] = chunk->u32[0]; - ((uint32_t *)out)[1] = chunk->u32[1]; -#else - memcpy(out, chunk, sizeof(chunk_t)); -#endif -} - -#define CHUNKSIZE chunksize_c -#define CHUNKCOPY chunkcopy_c -#define CHUNKCOPY_SAFE chunkcopy_safe_c -#define CHUNKUNROLL chunkunroll_c -#define CHUNKMEMSET chunkmemset_c -#define CHUNKMEMSET_SAFE chunkmemset_safe_c - -#include "chunkset_tpl.h" diff --git a/internal-complibs/zlib-ng-2.0.6/cmake/toolchain-mingw-i686.cmake b/internal-complibs/zlib-ng-2.0.6/cmake/toolchain-mingw-i686.cmake deleted file mode 100644 index 588ec0ef..00000000 --- a/internal-complibs/zlib-ng-2.0.6/cmake/toolchain-mingw-i686.cmake +++ /dev/null @@ -1,16 +0,0 @@ -set(CMAKE_SYSTEM_NAME Windows) - -set(CMAKE_C_COMPILER_TARGET i686) -set(CMAKE_CXX_COMPILER_TARGET i686) - -set(CMAKE_C_COMPILER i686-w64-mingw32-gcc) -set(CMAKE_CXX_COMPILER i686-w64-mingw32-g++) -set(CMAKE_RC_COMPILER i686-w64-mingw32-windres) - -set(CMAKE_CROSSCOMPILING TRUE) -set(CMAKE_CROSSCOMPILING_EMULATOR wine) - -set(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32) -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/internal-complibs/zlib-ng-2.0.6/cmake/toolchain-mingw-x86_64.cmake b/internal-complibs/zlib-ng-2.0.6/cmake/toolchain-mingw-x86_64.cmake deleted file mode 100644 index c778b722..00000000 --- a/internal-complibs/zlib-ng-2.0.6/cmake/toolchain-mingw-x86_64.cmake +++ /dev/null @@ -1,16 +0,0 @@ -set(CMAKE_SYSTEM_NAME Windows) - -set(CMAKE_C_COMPILER_TARGET x86_64) -set(CMAKE_CXX_COMPILER_TARGET x86_64) - -set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) -set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++) -set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres) - -set(CMAKE_CROSSCOMPILING TRUE) -set(CMAKE_CROSSCOMPILING_EMULATOR wine) - -set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32) -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/internal-complibs/zlib-ng-2.0.6/.shellcheckrc b/internal-complibs/zlib-ng-2.0.7/.shellcheckrc similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/.shellcheckrc rename to internal-complibs/zlib-ng-2.0.7/.shellcheckrc diff --git a/internal-complibs/zlib-ng-2.0.6/CMakeLists.txt b/internal-complibs/zlib-ng-2.0.7/CMakeLists.txt similarity index 94% rename from internal-complibs/zlib-ng-2.0.6/CMakeLists.txt rename to internal-complibs/zlib-ng-2.0.7/CMakeLists.txt index 8e5646d7..778d5def 100644 --- a/internal-complibs/zlib-ng-2.0.6/CMakeLists.txt +++ b/internal-complibs/zlib-ng-2.0.7/CMakeLists.txt @@ -102,6 +102,7 @@ elseif(BASEARCH_S360_FOUND) add_option(WITH_DFLTCC_DEFLATE "Build with DFLTCC intrinsics for compression on IBM Z" OFF) add_option(WITH_DFLTCC_INFLATE "Build with DFLTCC intrinsics for decompression on IBM Z" OFF) elseif(BASEARCH_X86_FOUND) + option(FORCE_TZCNT "Always assume CPU is TZCNT capable" OFF) add_option(WITH_AVX2 "Build with AVX2" ON) add_option(WITH_SSE2 "Build with SSE2" ON) add_option(WITH_SSSE3 "Build with SSSE3" ON) @@ -129,8 +130,10 @@ if(ZLIB_COMPAT) add_definitions(-DZLIB_COMPAT) set(WITH_GZFILEOP ON) set(SUFFIX "") + set(ZLIB_FULL_VERSION ${ZLIB_HEADER_VERSION}.zlib-ng) else() set(SUFFIX "-ng") + set(ZLIB_FULL_VERSION ${ZLIBNG_HEADER_VERSION}) endif() if(WITH_GZFILEOP) @@ -188,6 +191,9 @@ elseif(MSVC) if(WITH_NATIVE_INSTRUCTIONS) message(STATUS "Ignoring WITH_NATIVE_INSTRUCTIONS; not supported on this configuration") endif() + if(MINGW) + list(APPEND WARNFLAGS_DISABLE -Wno-pedantic-ms-format) + endif() else() # catch all GNU C compilers as well as Clang and AppleClang if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") @@ -275,6 +281,15 @@ if(NOT MSVC AND NOT CMAKE_C_FLAGS MATCHES "([\\/\\-]O)3") CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") endif() +# Disable LTO +if(NOT WITH_NATIVE_INSTRUCTIONS) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION OFF) + foreach(_cfg_name IN LISTS CMAKE_CONFIGURATION_TYPES) + string(TOUPPER "${_cfg_name}" _cfg_name_uc) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_${_cfg_name_uc} OFF) + endforeach() +endif() + # Set architecture alignment requirements if(WITH_UNALIGNED) if((BASEARCH_ARM_FOUND AND NOT "${ARCH}" MATCHES "armv[2-7]") OR (BASEARCH_PPC_FOUND AND "${ARCH}" MATCHES "powerpc64le") OR BASEARCH_X86_FOUND) @@ -370,12 +385,18 @@ check_function_exists(strerror HAVE_STRERROR) if(NOT HAVE_STRERROR) add_definitions(-DNO_STRERROR) endif() -set(CMAKE_REQUIRED_DEFINITIONS -D _POSIX_C_SOURCE=200112L) +set(CMAKE_REQUIRED_DEFINITIONS -D_POSIX_C_SOURCE=200112L) check_function_exists(posix_memalign HAVE_POSIX_MEMALIGN) if(HAVE_POSIX_MEMALIGN) add_definitions(-DHAVE_POSIX_MEMALIGN) endif() set(CMAKE_REQUIRED_DEFINITIONS) +set(CMAKE_REQUIRED_DEFINITIONS -D_ISOC11_SOURCE=1) +check_function_exists(aligned_alloc HAVE_ALIGNED_ALLOC) +if(HAVE_ALIGNED_ALLOC) + add_definitions(-DHAVE_ALIGNED_ALLOC) +endif() +set(CMAKE_REQUIRED_DEFINITIONS) if(WITH_SANITIZER STREQUAL "Address") add_address_sanitizer() @@ -821,13 +842,21 @@ if(WITH_OPTIM) endif() endif() endif() - if(WITH_SSSE3 AND HAVE_SSSE3_INTRIN) - add_definitions(-DX86_SSSE3 -DX86_SSSE3_ADLER32) - set(SSSE3_SRCS ${ARCHDIR}/adler32_ssse3.c) - add_feature_info(SSSE3_ADLER32 1 "Support SSSE3-accelerated adler32, using \"${SSSE3FLAG}\"") - list(APPEND ZLIB_ARCH_SRCS ${SSSE3_SRCS}) - set_property(SOURCE ${SSSE3_SRCS} PROPERTY COMPILE_FLAGS "${SSSE3FLAG} ${NOLTOFLAG}") + if(WITH_SSSE3) + if(HAVE_SSSE3_INTRIN) + add_definitions(-DX86_SSSE3 -DX86_SSSE3_ADLER32) + set(SSSE3_SRCS ${ARCHDIR}/adler32_ssse3.c) + add_feature_info(SSSE3_ADLER32 1 "Support SSSE3-accelerated adler32, using \"${SSSE3FLAG}\"") + list(APPEND ZLIB_ARCH_SRCS ${SSSE3_SRCS}) + set_property(SOURCE ${SSSE3_SRCS} PROPERTY COMPILE_FLAGS "${SSSE3FLAG} ${NOLTOFLAG}") + else() + set(WITH_SSSE3 OFF) + endif() endif() + if(FORCE_TZCNT) + add_definitions(-DX86_NOCHECK_TZCNT) + endif() + add_feature_info(FORCE_TZCNT FORCE_TZCNT "Assume CPU is TZCNT capable") if(WITH_PCLMULQDQ AND HAVE_PCLMULQDQ_INTRIN AND WITH_SSSE3 AND WITH_SSE4) add_definitions(-DX86_PCLMULQDQ_CRC) set(PCLMULQDQ_SRCS ${ARCHDIR}/crc_folding.c) @@ -956,26 +985,8 @@ set(ZLIB_GZFILE_SRCS gzwrite.c ) -if(NOT MINGW AND NOT MSYS AND NOT CYGWIN) - set(ZLIB_DLL_SRCS - win32/zlib${SUFFIX}1.rc # If present will override custom build rule below. - ) -endif() - -if(MINGW OR MSYS OR CYGWIN) - # This gets us DLL resource information when compiling on MinGW. - if(NOT CMAKE_RC_COMPILER) - set(CMAKE_RC_COMPILER windres.exe) - endif() - - add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj - COMMAND ${CMAKE_RC_COMPILER} - -D GCC_WINDRES - -I ${CMAKE_CURRENT_SOURCE_DIR} - -I ${CMAKE_CURRENT_BINARY_DIR} - -o ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj - -i ${CMAKE_CURRENT_SOURCE_DIR}/win32/zlib${SUFFIX}1.rc) - set(ZLIB_DLL_SRCS ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj) +if(NOT DEFINED BUILD_SHARED_LIBS OR BUILD_SHARED_LIBS) + set(ZLIB_DLL_SRCS win32/zlib${SUFFIX}1.rc) endif() set(ZLIB_ALL_SRCS ${ZLIB_SRCS} ${ZLIB_ARCH_HDRS} ${ZLIB_ARCH_SRCS} ${ZLIB_DLL_SRCS} @@ -1000,6 +1011,9 @@ else() endif() foreach(ZLIB_INSTALL_LIBRARY ${ZLIB_INSTALL_LIBRARIES}) + if(NOT ZLIB_COMPAT) + target_compile_definitions(${ZLIB_INSTALL_LIBRARY} PUBLIC ZLIBNG_NATIVE_API) + endif() target_include_directories(${ZLIB_INSTALL_LIBRARY} PUBLIC "$" "$") @@ -1033,10 +1047,8 @@ if(NOT DEFINED BUILD_SHARED_LIBS OR BUILD_SHARED_LIBS) set_target_properties(zlib PROPERTIES DEFINE_SYMBOL ZLIB_DLL) if(ZLIB_COMPAT) - set(ZLIB_FULL_VERSION ${ZLIB_HEADER_VERSION}.zlib-ng) set_target_properties(zlib PROPERTIES SOVERSION 1) else() - set(ZLIB_FULL_VERSION ${ZLIBNG_HEADER_VERSION}) set_target_properties(zlib PROPERTIES SOVERSION 2) endif() @@ -1186,7 +1198,7 @@ if(ZLIB_ENABLE_TESTS) add_test(NAME makefixed COMMAND ${CMAKE_COMMAND} "-DCOMMAND=${MAKEFIXED_COMMAND}" - -DOUTPUT=${CMAKE_CURRENT_BINARY_DIR}/inffixed_tbl._h + -DOUTPUT=${CMAKE_CURRENT_BINARY_DIR}/Testing/Temporary/inffixed_tbl._h -DCOMPARE=${CMAKE_CURRENT_SOURCE_DIR}/inffixed_tbl.h -DIGNORE_LINE_ENDINGS=ON -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/run-and-compare.cmake) @@ -1200,7 +1212,7 @@ if(ZLIB_ENABLE_TESTS) add_test(NAME maketrees COMMAND ${CMAKE_COMMAND} "-DCOMMAND=${MAKETREES_COMMAND}" - -DOUTPUT=${CMAKE_CURRENT_BINARY_DIR}/trees_tbl._h + -DOUTPUT=${CMAKE_CURRENT_BINARY_DIR}/Testing/Temporary/trees_tbl._h -DCOMPARE=${CMAKE_CURRENT_SOURCE_DIR}/trees_tbl.h -DIGNORE_LINE_ENDINGS=ON -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/run-and-compare.cmake) @@ -1214,7 +1226,7 @@ if(ZLIB_ENABLE_TESTS) add_test(NAME makecrct-crc32 COMMAND ${CMAKE_COMMAND} "-DCOMMAND=${MAKECRCT_COMMAND}" - -DOUTPUT=${CMAKE_CURRENT_BINARY_DIR}/crc32_tbl._h + -DOUTPUT=${CMAKE_CURRENT_BINARY_DIR}/Testing/Temporary/crc32_tbl._h -DCOMPARE=${CMAKE_CURRENT_SOURCE_DIR}/crc32_tbl.h -DIGNORE_LINE_ENDINGS=ON -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/run-and-compare.cmake) @@ -1223,7 +1235,7 @@ if(ZLIB_ENABLE_TESTS) add_test(NAME makecrct-crc32-combine COMMAND ${CMAKE_COMMAND} "-DCOMMAND=${MAKECRCT_COMMAND}" - -DOUTPUT=${CMAKE_CURRENT_BINARY_DIR}/crc32_comb_tbl._h + -DOUTPUT=${CMAKE_CURRENT_BINARY_DIR}/Testing/Temporary/crc32_comb_tbl._h -DCOMPARE=${CMAKE_CURRENT_SOURCE_DIR}/crc32_comb_tbl.h -DIGNORE_LINE_ENDINGS=ON -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/run-and-compare.cmake) @@ -1312,6 +1324,27 @@ if(ZLIB_ENABLE_TESTS) -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/run-and-redirect.cmake) endforeach() + set(TEST_LEVELS 6 1 2) + foreach(TEST_LEVEL ${TEST_LEVELS}) + add_test(NAME CVE-2018-25032-fixed-level-${TEST_LEVEL} + COMMAND ${CMAKE_COMMAND} + "-DTARGET=${MINIDEFLATE_COMMAND}" + "-DCOMPRESS_ARGS=-c;-k;-m;1;-w;-15;-s;4;-F;-${TEST_LEVEL}" + "-DDECOMPRESS_ARGS=-c;-k;-d;-m;1;-w;-15;-${TEST_LEVEL}" + -DGZIP_VERIFY=OFF + -DINPUT=${CMAKE_CURRENT_SOURCE_DIR}/test/CVE-2018-25032/fixed.txt + -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/test-compress.cmake) + + add_test(NAME CVE-2018-25032-default-level-${TEST_LEVEL} + COMMAND ${CMAKE_COMMAND} + "-DTARGET=${MINIDEFLATE_COMMAND}" + "-DCOMPRESS_ARGS=-c;-k;-m;1;-w;-15;-s;4;-${TEST_LEVEL}" + "-DDECOMPRESS_ARGS=-c;-k;-d;-m;1;-w;-15;-${TEST_LEVEL}" + -DGZIP_VERIFY=OFF + -DINPUT=${CMAKE_CURRENT_SOURCE_DIR}/test/CVE-2018-25032/default.txt + -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/test-compress.cmake) + endforeach() + # Run tests targeting tools include(cmake/test-tools.cmake) diff --git a/internal-complibs/zlib-ng-2.0.6/FAQ.zlib b/internal-complibs/zlib-ng-2.0.7/FAQ.zlib similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/FAQ.zlib rename to internal-complibs/zlib-ng-2.0.7/FAQ.zlib diff --git a/internal-complibs/zlib-ng-2.0.6/INDEX.md b/internal-complibs/zlib-ng-2.0.7/INDEX.md similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/INDEX.md rename to internal-complibs/zlib-ng-2.0.7/INDEX.md diff --git a/internal-complibs/zlib-ng-2.0.6/LICENSE.md b/internal-complibs/zlib-ng-2.0.7/LICENSE.md similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/LICENSE.md rename to internal-complibs/zlib-ng-2.0.7/LICENSE.md diff --git a/internal-complibs/zlib-ng-2.0.6/Makefile.in b/internal-complibs/zlib-ng-2.0.7/Makefile.in similarity index 89% rename from internal-complibs/zlib-ng-2.0.6/Makefile.in rename to internal-complibs/zlib-ng-2.0.7/Makefile.in index 8d4e42f0..ed723df5 100644 --- a/internal-complibs/zlib-ng-2.0.6/Makefile.in +++ b/internal-complibs/zlib-ng-2.0.7/Makefile.in @@ -29,7 +29,7 @@ TEST_LIBS=$(LIBNAME1).a LDSHARED=$(CC) LDSHAREDFLAGS=-shared -VER=2.0.6 +VER=2.0.7 VER1=2 STATICLIB=$(LIBNAME1).a @@ -279,8 +279,8 @@ ifneq ($(SHAREDLIB),$(SHAREDTARGET)) endif endif -adler32_test$(EXE): adler32_test.o $(OBJG) $(STATICLIB) - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ adler32_test.o $(OBJG) $(TEST_LIBS) $(LDSHAREDLIBC) +adler32_test$(EXE): adler32_test.o $(STATICLIB) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ adler32_test.o $(TEST_LIBS) $(LDSHAREDLIBC) ifneq ($(STRIP),) $(STRIP) $@ endif @@ -291,56 +291,56 @@ ifneq ($(STRIP),) $(STRIP) $@ endif -example$(EXE): example.o $(OBJG) $(STATICLIB) - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ example.o $(OBJG) $(TEST_LIBS) $(LDSHAREDLIBC) +example$(EXE): example.o $(STATICLIB) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ example.o $(TEST_LIBS) $(LDSHAREDLIBC) ifneq ($(STRIP),) $(STRIP) $@ endif -minigzip$(EXE): minigzip.o $(OBJG) $(STATICLIB) - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ minigzip.o $(OBJG) $(TEST_LIBS) $(LDSHAREDLIBC) +minigzip$(EXE): minigzip.o $(STATICLIB) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ minigzip.o $(TEST_LIBS) $(LDSHAREDLIBC) ifneq ($(STRIP),) $(STRIP) $@ endif -adler32_testsh$(EXE): adler32_test.o $(OBJG) $(SHAREDTARGET) - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ adler32_test.o $(OBJG) $(SHAREDTARGET) $(LDSHAREDLIBC) +adler32_testsh$(EXE): adler32_test.o $(SHAREDTARGET) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ adler32_test.o $(SHAREDLIB) $(LDSHAREDLIBC) ifneq ($(STRIP),) $(STRIP) $@ endif crc32_testsh$(EXE): crc32_test.o $(SHAREDTARGET) - $(CC) $(LDFLAGS) -o $@ crc32_test.o $(SHAREDTARGET) $(LDSHAREDLIBC) + $(CC) $(LDFLAGS) -o $@ crc32_test.o $(SHAREDLIB) $(LDSHAREDLIBC) ifneq ($(STRIP),) $(STRIP) $@ endif -examplesh$(EXE): example.o $(OBJG) $(SHAREDTARGET) - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ example.o $(OBJG) $(SHAREDTARGET) $(LDSHAREDLIBC) +examplesh$(EXE): example.o $(SHAREDTARGET) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ example.o $(SHAREDLIB) $(LDSHAREDLIBC) ifneq ($(STRIP),) $(STRIP) $@ endif -minigzipsh$(EXE): minigzip.o $(OBJG) $(SHAREDTARGET) - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ minigzip.o $(OBJG) $(SHAREDTARGET) $(LDSHAREDLIBC) +minigzipsh$(EXE): minigzip.o $(SHAREDTARGET) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ minigzip.o $(SHAREDLIB) $(LDSHAREDLIBC) ifneq ($(STRIP),) $(STRIP) $@ endif -makefixed$(EXE): makefixed.o $(OBJG) $(STATICLIB) - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ makefixed.o $(OBJG) $(TEST_LIBS) $(LDSHAREDLIBC) +makefixed$(EXE): makefixed.o $(STATICLIB) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ makefixed.o $(TEST_LIBS) $(LDSHAREDLIBC) ifneq ($(STRIP),) $(STRIP) $@ endif -maketrees$(EXE): maketrees.o $(OBJG) $(STATICLIB) - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ maketrees.o $(OBJG) $(TEST_LIBS) $(LDSHAREDLIBC) +maketrees$(EXE): maketrees.o $(STATICLIB) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ maketrees.o $(TEST_LIBS) $(LDSHAREDLIBC) ifneq ($(STRIP),) $(STRIP) $@ endif -makecrct$(EXE): makecrct.o $(OBJG) $(STATICLIB) - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ makecrct.o $(OBJG) $(TEST_LIBS) $(LDSHAREDLIBC) +makecrct$(EXE): makecrct.o $(STATICLIB) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ makecrct.o $(TEST_LIBS) $(LDSHAREDLIBC) ifneq ($(STRIP),) $(STRIP) $@ endif @@ -417,7 +417,6 @@ clean: rm -rf objs rm -f *.gcda *.gcno *.gcov rm -f a.out a.exe - rm -f *.pc rm -f *._h rm -rf btmp1 btmp2 pkgtmp1 pkgtmp2 @@ -425,7 +424,7 @@ maintainer-clean: distclean distclean: clean @if [ -f $(ARCHDIR)/Makefile ]; then $(MAKE) -C $(ARCHDIR) distclean; fi @if [ -f test/Makefile ]; then $(MAKE) -C test distclean; fi - rm -f $(PKGFILE) configure.log zconf.h zconf.h.cmakein + rm -f $(PKGFILE) configure.log zconf.h zconf.h.cmakein *.pc -@rm -f .DS_Store # Reset Makefile if building inside source tree @if [ -f Makefile.in ]; then \ diff --git a/internal-complibs/zlib-ng-2.0.6/PORTING.md b/internal-complibs/zlib-ng-2.0.7/PORTING.md similarity index 82% rename from internal-complibs/zlib-ng-2.0.6/PORTING.md rename to internal-complibs/zlib-ng-2.0.7/PORTING.md index eda2608d..1f2c7d62 100644 --- a/internal-complibs/zlib-ng-2.0.6/PORTING.md +++ b/internal-complibs/zlib-ng-2.0.7/PORTING.md @@ -9,11 +9,19 @@ zlib-compat mode Zlib-ng can be compiled in zlib-compat mode, suitable for zlib-replacement in a single application or system-wide. -Please note that zlib-ng in zlib-compat mode is API-compatible but not -ABI-compatible, meaning that you cannot simply replace the zlib library/dll -files and expect the application to work. The application will need to be +Please note that zlib-ng in zlib-compat mode tries to maintain both API and +ABI compatibility with the original zlib. Any issues regarding compatibility +can be reported as bugs. + +In certain instances you may not be able to simply replace the zlib library/dll +files and expect the application to work. The application may need to be recompiled against the zlib-ng headers and libs to ensure full compatibility. +It is also possible for the deflate output stream to differ from the original +zlib due to algorithmic differences between the two libraries. Any tests or +applications that depend on the exact length of the deflate stream being a +certain value will need to be updated. + **Advantages:** - Easy to port to, since it only requires a recompile of the application and no changes to the application code. diff --git a/internal-complibs/zlib-ng-2.0.6/README.md b/internal-complibs/zlib-ng-2.0.7/README.md similarity index 97% rename from internal-complibs/zlib-ng-2.0.6/README.md rename to internal-complibs/zlib-ng-2.0.7/README.md index 8528f284..a702ba93 100644 --- a/internal-complibs/zlib-ng-2.0.6/README.md +++ b/internal-complibs/zlib-ng-2.0.7/README.md @@ -194,7 +194,8 @@ Advanced Build Options |:--------------------------------|:----------------------|:--------------------------------------------------------------------|------------------------| | ZLIB_DUAL_LINK | | Dual link tests with system zlib | OFF | | UNALIGNED_OK | | Allow unaligned reads | ON (x86, arm) | -| | --force-sse2 | Skip runtime check for SSE2 instructions (Always on for x86_64) | OFF (x86) | +| FORCE_SSE2 | --force-sse2 | Skip runtime check for SSE2 instructions (Always on for x86_64) | OFF (x86) | +| FORCE_TZCNT | --force-tzcnt | Skip runtime check for TZCNT instructions | OFF | | WITH_AVX2 | | Build with AVX2 intrinsics | ON | | WITH_SSE2 | | Build with SSE2 intrinsics | ON | | WITH_SSE4 | | Build with SSE4 intrinsics | ON | @@ -213,7 +214,7 @@ Advanced Build Options Related Projects ---------------- -* Fork of the popular minigzip https://github.com/zlib-ng/minizip-ng +* Fork of the popular minizip https://github.com/zlib-ng/minizip-ng * Python tool to benchmark minigzip/minideflate https://github.com/zlib-ng/deflatebench * Python tool to benchmark pigz https://github.com/zlib-ng/pigzbench * 3rd party patches for zlib-ng compatibility https://github.com/zlib-ng/patches diff --git a/internal-complibs/zlib-ng-2.0.6/adler32.c b/internal-complibs/zlib-ng-2.0.7/adler32.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/adler32.c rename to internal-complibs/zlib-ng-2.0.7/adler32.c diff --git a/internal-complibs/zlib-ng-2.0.6/adler32_p.h b/internal-complibs/zlib-ng-2.0.7/adler32_p.h similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/adler32_p.h rename to internal-complibs/zlib-ng-2.0.7/adler32_p.h diff --git a/internal-complibs/zlib-ng-2.0.6/arch/.gitignore b/internal-complibs/zlib-ng-2.0.7/arch/.gitignore similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/.gitignore rename to internal-complibs/zlib-ng-2.0.7/arch/.gitignore diff --git a/internal-complibs/zlib-ng-2.0.6/arch/arm/Makefile.in b/internal-complibs/zlib-ng-2.0.7/arch/arm/Makefile.in similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/arm/Makefile.in rename to internal-complibs/zlib-ng-2.0.7/arch/arm/Makefile.in diff --git a/internal-complibs/zlib-ng-2.0.6/arch/arm/adler32_neon.c b/internal-complibs/zlib-ng-2.0.7/arch/arm/adler32_neon.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/arm/adler32_neon.c rename to internal-complibs/zlib-ng-2.0.7/arch/arm/adler32_neon.c diff --git a/internal-complibs/zlib-ng-2.0.6/arch/arm/arm.h b/internal-complibs/zlib-ng-2.0.7/arch/arm/arm.h similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/arm/arm.h rename to internal-complibs/zlib-ng-2.0.7/arch/arm/arm.h diff --git a/internal-complibs/zlib-ng-2.0.6/arch/arm/armfeature.c b/internal-complibs/zlib-ng-2.0.7/arch/arm/armfeature.c similarity index 95% rename from internal-complibs/zlib-ng-2.0.6/arch/arm/armfeature.c rename to internal-complibs/zlib-ng-2.0.7/arch/arm/armfeature.c index bef9b290..978c9872 100644 --- a/internal-complibs/zlib-ng-2.0.6/arch/arm/armfeature.c +++ b/internal-complibs/zlib-ng-2.0.7/arch/arm/armfeature.c @@ -11,6 +11,9 @@ # define ID_AA64ISAR0_CRC32_VAL ID_AA64ISAR0_CRC32 # endif #elif defined(__APPLE__) +# if !defined(_DARWIN_C_SOURCE) +# define _DARWIN_C_SOURCE /* enable types aliases (eg u_int) */ +# endif # include #elif defined(_WIN32) # include diff --git a/internal-complibs/zlib-ng-2.0.6/arch/arm/chunkset_neon.c b/internal-complibs/zlib-ng-2.0.7/arch/arm/chunkset_neon.c similarity index 86% rename from internal-complibs/zlib-ng-2.0.6/arch/arm/chunkset_neon.c rename to internal-complibs/zlib-ng-2.0.7/arch/arm/chunkset_neon.c index e0ad3e04..51dcf093 100644 --- a/internal-complibs/zlib-ng-2.0.6/arch/arm/chunkset_neon.c +++ b/internal-complibs/zlib-ng-2.0.7/arch/arm/chunkset_neon.c @@ -15,30 +15,25 @@ typedef uint8x16_t chunk_t; #define CHUNK_SIZE 16 -#define HAVE_CHUNKMEMSET_1 #define HAVE_CHUNKMEMSET_2 #define HAVE_CHUNKMEMSET_4 #define HAVE_CHUNKMEMSET_8 -static inline void chunkmemset_1(uint8_t *from, chunk_t *chunk) { - *chunk = vld1q_dup_u8(from); -} - static inline void chunkmemset_2(uint8_t *from, chunk_t *chunk) { uint16_t tmp; - memcpy(&tmp, from, 2); + memcpy(&tmp, from, sizeof(tmp)); *chunk = vreinterpretq_u8_u16(vdupq_n_u16(tmp)); } static inline void chunkmemset_4(uint8_t *from, chunk_t *chunk) { uint32_t tmp; - memcpy(&tmp, from, 4); + memcpy(&tmp, from, sizeof(tmp)); *chunk = vreinterpretq_u8_u32(vdupq_n_u32(tmp)); } static inline void chunkmemset_8(uint8_t *from, chunk_t *chunk) { uint64_t tmp; - memcpy(&tmp, from, 8); + memcpy(&tmp, from, sizeof(tmp)); *chunk = vreinterpretq_u8_u64(vdupq_n_u64(tmp)); } diff --git a/internal-complibs/zlib-ng-2.0.6/arch/arm/crc32_acle.c b/internal-complibs/zlib-ng-2.0.7/arch/arm/crc32_acle.c similarity index 71% rename from internal-complibs/zlib-ng-2.0.6/arch/arm/crc32_acle.c rename to internal-complibs/zlib-ng-2.0.7/arch/arm/crc32_acle.c index 88ba6c38..37b1b98a 100644 --- a/internal-complibs/zlib-ng-2.0.6/arch/arm/crc32_acle.c +++ b/internal-complibs/zlib-ng-2.0.7/arch/arm/crc32_acle.c @@ -22,7 +22,7 @@ uint32_t crc32_acle(uint32_t crc, const unsigned char *buf, uint64_t len) { len--; } - if ((len > sizeof(uint16_t)) && ((ptrdiff_t)buf & sizeof(uint16_t))) { + if ((len >= sizeof(uint16_t)) && ((ptrdiff_t)buf & sizeof(uint16_t))) { buf2 = (const uint16_t *) buf; c = __crc32h(c, *buf2++); len -= sizeof(uint16_t); @@ -32,22 +32,17 @@ uint32_t crc32_acle(uint32_t crc, const unsigned char *buf, uint64_t len) { } #if defined(__aarch64__) - if ((len > sizeof(uint32_t)) && ((ptrdiff_t)buf & sizeof(uint32_t))) { + if ((len >= sizeof(uint32_t)) && ((ptrdiff_t)buf & sizeof(uint32_t))) { c = __crc32w(c, *buf4++); len -= sizeof(uint32_t); } - const uint64_t *buf8 = (const uint64_t *) buf4; - -#ifdef UNROLL_MORE - while (len >= 4 * sizeof(uint64_t)) { - c = __crc32d(c, *buf8++); - c = __crc32d(c, *buf8++); - c = __crc32d(c, *buf8++); - c = __crc32d(c, *buf8++); - len -= 4 * sizeof(uint64_t); + if (len == 0) { + c = ~c; + return c; } -#endif + + const uint64_t *buf8 = (const uint64_t *) buf4; while (len >= sizeof(uint64_t)) { c = __crc32d(c, *buf8++); @@ -71,19 +66,10 @@ uint32_t crc32_acle(uint32_t crc, const unsigned char *buf, uint64_t len) { buf = (const unsigned char *) buf2; #else /* __aarch64__ */ -# ifdef UNROLL_MORE - while (len >= 8 * sizeof(uint32_t)) { - c = __crc32w(c, *buf4++); - c = __crc32w(c, *buf4++); - c = __crc32w(c, *buf4++); - c = __crc32w(c, *buf4++); - c = __crc32w(c, *buf4++); - c = __crc32w(c, *buf4++); - c = __crc32w(c, *buf4++); - c = __crc32w(c, *buf4++); - len -= 8 * sizeof(uint32_t); + if (len == 0) { + c = ~c; + return c; } -# endif while (len >= sizeof(uint32_t)) { c = __crc32w(c, *buf4++); diff --git a/internal-complibs/zlib-ng-2.0.6/arch/arm/ctzl.h b/internal-complibs/zlib-ng-2.0.7/arch/arm/ctzl.h similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/arm/ctzl.h rename to internal-complibs/zlib-ng-2.0.7/arch/arm/ctzl.h diff --git a/internal-complibs/zlib-ng-2.0.6/arch/arm/insert_string_acle.c b/internal-complibs/zlib-ng-2.0.7/arch/arm/insert_string_acle.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/arm/insert_string_acle.c rename to internal-complibs/zlib-ng-2.0.7/arch/arm/insert_string_acle.c diff --git a/internal-complibs/zlib-ng-2.0.6/arch/arm/slide_neon.c b/internal-complibs/zlib-ng-2.0.7/arch/arm/slide_neon.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/arm/slide_neon.c rename to internal-complibs/zlib-ng-2.0.7/arch/arm/slide_neon.c diff --git a/internal-complibs/zlib-ng-2.0.6/arch/generic/Makefile.in b/internal-complibs/zlib-ng-2.0.7/arch/generic/Makefile.in similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/generic/Makefile.in rename to internal-complibs/zlib-ng-2.0.7/arch/generic/Makefile.in diff --git a/internal-complibs/zlib-ng-2.0.6/arch/power/Makefile.in b/internal-complibs/zlib-ng-2.0.7/arch/power/Makefile.in similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/power/Makefile.in rename to internal-complibs/zlib-ng-2.0.7/arch/power/Makefile.in diff --git a/internal-complibs/zlib-ng-2.0.6/arch/power/adler32_power8.c b/internal-complibs/zlib-ng-2.0.7/arch/power/adler32_power8.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/power/adler32_power8.c rename to internal-complibs/zlib-ng-2.0.7/arch/power/adler32_power8.c diff --git a/internal-complibs/zlib-ng-2.0.6/arch/power/power.c b/internal-complibs/zlib-ng-2.0.7/arch/power/power.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/power/power.c rename to internal-complibs/zlib-ng-2.0.7/arch/power/power.c diff --git a/internal-complibs/zlib-ng-2.0.6/arch/power/power.h b/internal-complibs/zlib-ng-2.0.7/arch/power/power.h similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/power/power.h rename to internal-complibs/zlib-ng-2.0.7/arch/power/power.h diff --git a/internal-complibs/zlib-ng-2.0.6/arch/power/slide_hash_power8.c b/internal-complibs/zlib-ng-2.0.7/arch/power/slide_hash_power8.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/power/slide_hash_power8.c rename to internal-complibs/zlib-ng-2.0.7/arch/power/slide_hash_power8.c diff --git a/internal-complibs/zlib-ng-2.0.6/arch/s390/Makefile.in b/internal-complibs/zlib-ng-2.0.7/arch/s390/Makefile.in similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/s390/Makefile.in rename to internal-complibs/zlib-ng-2.0.7/arch/s390/Makefile.in diff --git a/internal-complibs/zlib-ng-2.0.6/arch/s390/README.md b/internal-complibs/zlib-ng-2.0.7/arch/s390/README.md similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/s390/README.md rename to internal-complibs/zlib-ng-2.0.7/arch/s390/README.md diff --git a/internal-complibs/zlib-ng-2.0.6/arch/s390/dfltcc_common.c b/internal-complibs/zlib-ng-2.0.7/arch/s390/dfltcc_common.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/s390/dfltcc_common.c rename to internal-complibs/zlib-ng-2.0.7/arch/s390/dfltcc_common.c diff --git a/internal-complibs/zlib-ng-2.0.6/arch/s390/dfltcc_common.h b/internal-complibs/zlib-ng-2.0.7/arch/s390/dfltcc_common.h similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/s390/dfltcc_common.h rename to internal-complibs/zlib-ng-2.0.7/arch/s390/dfltcc_common.h diff --git a/internal-complibs/zlib-ng-2.0.6/arch/s390/dfltcc_deflate.c b/internal-complibs/zlib-ng-2.0.7/arch/s390/dfltcc_deflate.c similarity index 98% rename from internal-complibs/zlib-ng-2.0.6/arch/s390/dfltcc_deflate.c rename to internal-complibs/zlib-ng-2.0.7/arch/s390/dfltcc_deflate.c index e3b53ee1..9ecc6ba2 100644 --- a/internal-complibs/zlib-ng-2.0.6/arch/s390/dfltcc_deflate.c +++ b/internal-complibs/zlib-ng-2.0.7/arch/s390/dfltcc_deflate.c @@ -210,7 +210,10 @@ int Z_INTERNAL dfltcc_deflate(PREFIX3(streamp) strm, int flush, block_state *res *strm->next_out = (unsigned char)state->bi_buf; /* Honor history and check value */ param->nt = 0; - param->cv = state->wrap == 2 ? ZSWAP32(strm->adler) : strm->adler; + if (state->wrap == 1) + param->cv = strm->adler; + else if (state->wrap == 2) + param->cv = ZSWAP32(strm->adler); /* When opening a block, choose a Huffman-Table Type */ if (!param->bcf) { @@ -241,7 +244,10 @@ int Z_INTERNAL dfltcc_deflate(PREFIX3(streamp) strm, int flush, block_state *res state->bi_buf = 0; /* Avoid accessing next_out */ else state->bi_buf = *strm->next_out & ((1 << state->bi_valid) - 1); - strm->adler = state->wrap == 2 ? ZSWAP32(param->cv) : param->cv; + if (state->wrap == 1) + strm->adler = param->cv; + else if (state->wrap == 2) + strm->adler = ZSWAP32(param->cv); /* Unmask the input data */ strm->avail_in += masked_avail_in; diff --git a/internal-complibs/zlib-ng-2.0.6/arch/s390/dfltcc_deflate.h b/internal-complibs/zlib-ng-2.0.7/arch/s390/dfltcc_deflate.h similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/s390/dfltcc_deflate.h rename to internal-complibs/zlib-ng-2.0.7/arch/s390/dfltcc_deflate.h diff --git a/internal-complibs/zlib-ng-2.0.6/arch/s390/dfltcc_detail.h b/internal-complibs/zlib-ng-2.0.7/arch/s390/dfltcc_detail.h similarity index 81% rename from internal-complibs/zlib-ng-2.0.6/arch/s390/dfltcc_detail.h rename to internal-complibs/zlib-ng-2.0.7/arch/s390/dfltcc_detail.h index 4ec03f80..411e9f6c 100644 --- a/internal-complibs/zlib-ng-2.0.6/arch/s390/dfltcc_detail.h +++ b/internal-complibs/zlib-ng-2.0.7/arch/s390/dfltcc_detail.h @@ -25,74 +25,6 @@ #define DFLTCC_RIBM 0 #endif -/* - C wrapper for the DEFLATE CONVERSION CALL instruction. - */ -typedef enum { - DFLTCC_CC_OK = 0, - DFLTCC_CC_OP1_TOO_SHORT = 1, - DFLTCC_CC_OP2_TOO_SHORT = 2, - DFLTCC_CC_OP2_CORRUPT = 2, - DFLTCC_CC_AGAIN = 3, -} dfltcc_cc; - -#define DFLTCC_QAF 0 -#define DFLTCC_GDHT 1 -#define DFLTCC_CMPR 2 -#define DFLTCC_XPND 4 -#define HBT_CIRCULAR (1 << 7) -#define HB_BITS 15 -#define HB_SIZE (1 << HB_BITS) -#define DFLTCC_FACILITY 151 - -static inline dfltcc_cc dfltcc(int fn, void *param, - unsigned char **op1, size_t *len1, z_const unsigned char **op2, size_t *len2, void *hist) { - unsigned char *t2 = op1 ? *op1 : NULL; - size_t t3 = len1 ? *len1 : 0; - z_const unsigned char *t4 = op2 ? *op2 : NULL; - size_t t5 = len2 ? *len2 : 0; - Z_REGISTER int r0 __asm__("r0") = fn; - Z_REGISTER void *r1 __asm__("r1") = param; - Z_REGISTER unsigned char *r2 __asm__("r2") = t2; - Z_REGISTER size_t r3 __asm__("r3") = t3; - Z_REGISTER z_const unsigned char *r4 __asm__("r4") = t4; - Z_REGISTER size_t r5 __asm__("r5") = t5; - int cc; - - __asm__ volatile( -#ifdef HAVE_SYS_SDT_H - STAP_PROBE_ASM(zlib, dfltcc_entry, STAP_PROBE_ASM_TEMPLATE(5)) -#endif - ".insn rrf,0xb9390000,%[r2],%[r4],%[hist],0\n" -#ifdef HAVE_SYS_SDT_H - STAP_PROBE_ASM(zlib, dfltcc_exit, STAP_PROBE_ASM_TEMPLATE(5)) -#endif - "ipm %[cc]\n" - : [r2] "+r" (r2) - , [r3] "+r" (r3) - , [r4] "+r" (r4) - , [r5] "+r" (r5) - , [cc] "=r" (cc) - : [r0] "r" (r0) - , [r1] "r" (r1) - , [hist] "r" (hist) -#ifdef HAVE_SYS_SDT_H - , STAP_PROBE_ASM_OPERANDS(5, r2, r3, r4, r5, hist) -#endif - : "cc", "memory"); - t2 = r2; t3 = r3; t4 = r4; t5 = r5; - - if (op1) - *op1 = t2; - if (len1) - *len1 = t3; - if (op2) - *op2 = t4; - if (len2) - *len2 = t5; - return (cc >> 28) & 3; -} - /* Parameter Block for Query Available Functions. */ @@ -105,7 +37,8 @@ struct dfltcc_qaf_param { char reserved2[6]; }; -static_assert(sizeof(struct dfltcc_qaf_param) == 32, sizeof_struct_dfltcc_qaf_param_is_32); +#define DFLTCC_SIZEOF_QAF 32 +static_assert(sizeof(struct dfltcc_qaf_param) == DFLTCC_SIZEOF_QAF, qaf); static inline int is_bit_set(const char *bits, int n) { return bits[n / 8] & (1 << (7 - (n % 8))); @@ -115,6 +48,8 @@ static inline void clear_bit(char *bits, int n) { bits[n / 8] &= ~(1 << (7 - (n % 8))); } +#define DFLTCC_FACILITY 151 + #define DFLTCC_FMT0 0 /* @@ -165,12 +100,16 @@ struct dfltcc_param_v0 { uint16_t cdhtl : 12; /* Compressed-Dynamic-Huffman Table Length */ uint8_t reserved464[6]; - uint8_t cdht[288]; - uint8_t reserved[32]; - uint8_t csb[1152]; + uint8_t cdht[288]; /* Compressed-Dynamic-Huffman Table */ + uint8_t reserved[24]; + uint8_t ribm2[8]; /* Reserved for IBM use */ + uint8_t csb[1152]; /* Continuation-State Buffer */ }; -static_assert(sizeof(struct dfltcc_param_v0) == 1536, sizeof_struct_dfltcc_param_v0_is_1536); +#define DFLTCC_SIZEOF_GDHT_V0 384 +#define DFLTCC_SIZEOF_CMPR_XPND_V0 1536 +static_assert(offsetof(struct dfltcc_param_v0, csb) == DFLTCC_SIZEOF_GDHT_V0, gdht_v0); +static_assert(sizeof(struct dfltcc_param_v0) == DFLTCC_SIZEOF_CMPR_XPND_V0, cmpr_xpnd_v0); static inline z_const char *oesc_msg(char *buf, int oesc) { if (oesc == 0x00) @@ -181,6 +120,97 @@ static inline z_const char *oesc_msg(char *buf, int oesc) { } } +/* + C wrapper for the DEFLATE CONVERSION CALL instruction. + */ +typedef enum { + DFLTCC_CC_OK = 0, + DFLTCC_CC_OP1_TOO_SHORT = 1, + DFLTCC_CC_OP2_TOO_SHORT = 2, + DFLTCC_CC_OP2_CORRUPT = 2, + DFLTCC_CC_AGAIN = 3, +} dfltcc_cc; + +#define DFLTCC_QAF 0 +#define DFLTCC_GDHT 1 +#define DFLTCC_CMPR 2 +#define DFLTCC_XPND 4 +#define HBT_CIRCULAR (1 << 7) +#define DFLTCC_FN_MASK ((1 << 7) - 1) +#define HB_BITS 15 +#define HB_SIZE (1 << HB_BITS) + +static inline dfltcc_cc dfltcc(int fn, void *param, + unsigned char **op1, size_t *len1, + z_const unsigned char **op2, size_t *len2, void *hist) { + unsigned char *t2 = op1 ? *op1 : NULL; +#ifdef Z_MEMORY_SANITIZER + unsigned char *orig_t2 = t2; +#endif + size_t t3 = len1 ? *len1 : 0; + z_const unsigned char *t4 = op2 ? *op2 : NULL; + size_t t5 = len2 ? *len2 : 0; + Z_REGISTER int r0 __asm__("r0") = fn; + Z_REGISTER void *r1 __asm__("r1") = param; + Z_REGISTER unsigned char *r2 __asm__("r2") = t2; + Z_REGISTER size_t r3 __asm__("r3") = t3; + Z_REGISTER z_const unsigned char *r4 __asm__("r4") = t4; + Z_REGISTER size_t r5 __asm__("r5") = t5; + int cc; + + __asm__ volatile( +#ifdef HAVE_SYS_SDT_H + STAP_PROBE_ASM(zlib, dfltcc_entry, STAP_PROBE_ASM_TEMPLATE(5)) +#endif + ".insn rrf,0xb9390000,%[r2],%[r4],%[hist],0\n" +#ifdef HAVE_SYS_SDT_H + STAP_PROBE_ASM(zlib, dfltcc_exit, STAP_PROBE_ASM_TEMPLATE(5)) +#endif + "ipm %[cc]\n" + : [r2] "+r" (r2) + , [r3] "+r" (r3) + , [r4] "+r" (r4) + , [r5] "+r" (r5) + , [cc] "=r" (cc) + : [r0] "r" (r0) + , [r1] "r" (r1) + , [hist] "r" (hist) +#ifdef HAVE_SYS_SDT_H + , STAP_PROBE_ASM_OPERANDS(5, r2, r3, r4, r5, hist) +#endif + : "cc", "memory"); + t2 = r2; t3 = r3; t4 = r4; t5 = r5; + +#ifdef Z_MEMORY_SANITIZER + switch (fn & DFLTCC_FN_MASK) { + case DFLTCC_QAF: + __msan_unpoison(param, DFLTCC_SIZEOF_QAF); + break; + case DFLTCC_GDHT: + __msan_unpoison(param, DFLTCC_SIZEOF_GDHT_V0); + break; + case DFLTCC_CMPR: + __msan_unpoison(param, DFLTCC_SIZEOF_CMPR_XPND_V0); + __msan_unpoison(orig_t2, t2 - orig_t2 + (((struct dfltcc_param_v0 *)param)->sbb == 0 ? 0 : 1)); + break; + case DFLTCC_XPND: + __msan_unpoison(param, DFLTCC_SIZEOF_CMPR_XPND_V0); + __msan_unpoison(orig_t2, t2 - orig_t2); + break; + } +#endif + + if (op1) + *op1 = t2; + if (len1) + *len1 = t3; + if (op2) + *op2 = t4; + if (len2) + *len2 = t5; + return (cc >> 28) & 3; +} + /* Extension of inflate_state and deflate_state. Must be doubleword-aligned. */ diff --git a/internal-complibs/zlib-ng-2.0.6/arch/s390/dfltcc_inflate.c b/internal-complibs/zlib-ng-2.0.7/arch/s390/dfltcc_inflate.c similarity index 93% rename from internal-complibs/zlib-ng-2.0.6/arch/s390/dfltcc_inflate.c rename to internal-complibs/zlib-ng-2.0.7/arch/s390/dfltcc_inflate.c index 25350646..801e547e 100644 --- a/internal-complibs/zlib-ng-2.0.6/arch/s390/dfltcc_inflate.c +++ b/internal-complibs/zlib-ng-2.0.7/arch/s390/dfltcc_inflate.c @@ -81,13 +81,14 @@ dfltcc_inflate_action Z_INTERNAL dfltcc_inflate(PREFIX3(streamp) strm, int flush } /* Translate stream to parameter block */ - param->cvt = state->flags ? CVT_CRC32 : CVT_ADLER32; + param->cvt = ((state->wrap & 4) && state->flags) ? CVT_CRC32 : CVT_ADLER32; param->sbb = state->bits; param->hl = state->whave; /* Software and hardware history formats match */ param->ho = (state->wnext - state->whave) & ((1 << HB_BITS) - 1); if (param->hl) param->nt = 0; /* Honor history for the first block */ - param->cv = state->flags ? ZSWAP32(state->check) : state->check; + if (state->wrap & 4) + param->cv = state->flags ? ZSWAP32(state->check) : state->check; /* Inflate */ do { @@ -100,7 +101,8 @@ dfltcc_inflate_action Z_INTERNAL dfltcc_inflate(PREFIX3(streamp) strm, int flush state->bits = param->sbb; state->whave = param->hl; state->wnext = (param->ho + param->hl) & ((1 << HB_BITS) - 1); - state->check = state->flags ? ZSWAP32(param->cv) : param->cv; + if (state->wrap & 4) + strm->adler = state->check = state->flags ? ZSWAP32(param->cv) : param->cv; if (cc == DFLTCC_CC_OP2_CORRUPT && param->oesc != 0) { /* Report an error if stream is corrupted */ state->mode = BAD; diff --git a/internal-complibs/zlib-ng-2.0.6/arch/s390/dfltcc_inflate.h b/internal-complibs/zlib-ng-2.0.7/arch/s390/dfltcc_inflate.h similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/s390/dfltcc_inflate.h rename to internal-complibs/zlib-ng-2.0.7/arch/s390/dfltcc_inflate.h diff --git a/internal-complibs/zlib-ng-2.0.6/arch/s390/self-hosted-builder/actions-runner.Dockerfile b/internal-complibs/zlib-ng-2.0.7/arch/s390/self-hosted-builder/actions-runner.Dockerfile similarity index 96% rename from internal-complibs/zlib-ng-2.0.6/arch/s390/self-hosted-builder/actions-runner.Dockerfile rename to internal-complibs/zlib-ng-2.0.7/arch/s390/self-hosted-builder/actions-runner.Dockerfile index a4bb774a..a55a74df 100644 --- a/internal-complibs/zlib-ng-2.0.6/arch/s390/self-hosted-builder/actions-runner.Dockerfile +++ b/internal-complibs/zlib-ng-2.0.7/arch/s390/self-hosted-builder/actions-runner.Dockerfile @@ -11,11 +11,13 @@ FROM s390x/ubuntu:20.04 # Packages for zlib-ng testing. ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get -y install \ + clang-11 \ cmake \ curl \ gcc \ git \ jq \ + llvm-11-tools \ ninja-build \ python-is-python3 \ python3 \ diff --git a/internal-complibs/zlib-ng-2.0.6/arch/s390/self-hosted-builder/actions-runner.service b/internal-complibs/zlib-ng-2.0.7/arch/s390/self-hosted-builder/actions-runner.service similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/s390/self-hosted-builder/actions-runner.service rename to internal-complibs/zlib-ng-2.0.7/arch/s390/self-hosted-builder/actions-runner.service diff --git a/internal-complibs/zlib-ng-2.0.6/arch/s390/self-hosted-builder/fs/usr/bin/actions-runner b/internal-complibs/zlib-ng-2.0.7/arch/s390/self-hosted-builder/fs/usr/bin/actions-runner similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/s390/self-hosted-builder/fs/usr/bin/actions-runner rename to internal-complibs/zlib-ng-2.0.7/arch/s390/self-hosted-builder/fs/usr/bin/actions-runner diff --git a/internal-complibs/zlib-ng-2.0.6/arch/s390/self-hosted-builder/fs/usr/bin/entrypoint b/internal-complibs/zlib-ng-2.0.7/arch/s390/self-hosted-builder/fs/usr/bin/entrypoint similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/s390/self-hosted-builder/fs/usr/bin/entrypoint rename to internal-complibs/zlib-ng-2.0.7/arch/s390/self-hosted-builder/fs/usr/bin/entrypoint diff --git a/internal-complibs/zlib-ng-2.0.6/arch/s390/self-hosted-builder/qemu-user-static.service b/internal-complibs/zlib-ng-2.0.7/arch/s390/self-hosted-builder/qemu-user-static.service similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/s390/self-hosted-builder/qemu-user-static.service rename to internal-complibs/zlib-ng-2.0.7/arch/s390/self-hosted-builder/qemu-user-static.service diff --git a/internal-complibs/zlib-ng-2.0.6/arch/x86/INDEX.md b/internal-complibs/zlib-ng-2.0.7/arch/x86/INDEX.md similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/x86/INDEX.md rename to internal-complibs/zlib-ng-2.0.7/arch/x86/INDEX.md diff --git a/internal-complibs/zlib-ng-2.0.6/arch/x86/Makefile.in b/internal-complibs/zlib-ng-2.0.7/arch/x86/Makefile.in similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/x86/Makefile.in rename to internal-complibs/zlib-ng-2.0.7/arch/x86/Makefile.in diff --git a/internal-complibs/zlib-ng-2.0.6/arch/x86/adler32_avx.c b/internal-complibs/zlib-ng-2.0.7/arch/x86/adler32_avx.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/x86/adler32_avx.c rename to internal-complibs/zlib-ng-2.0.7/arch/x86/adler32_avx.c diff --git a/internal-complibs/zlib-ng-2.0.6/arch/x86/adler32_ssse3.c b/internal-complibs/zlib-ng-2.0.7/arch/x86/adler32_ssse3.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/x86/adler32_ssse3.c rename to internal-complibs/zlib-ng-2.0.7/arch/x86/adler32_ssse3.c diff --git a/internal-complibs/zlib-ng-2.0.6/arch/x86/chunkset_avx.c b/internal-complibs/zlib-ng-2.0.7/arch/x86/chunkset_avx.c similarity index 78% rename from internal-complibs/zlib-ng-2.0.6/arch/x86/chunkset_avx.c rename to internal-complibs/zlib-ng-2.0.7/arch/x86/chunkset_avx.c index 7a9a56a0..398d192a 100644 --- a/internal-complibs/zlib-ng-2.0.6/arch/x86/chunkset_avx.c +++ b/internal-complibs/zlib-ng-2.0.7/arch/x86/chunkset_avx.c @@ -11,25 +11,26 @@ typedef __m256i chunk_t; #define CHUNK_SIZE 32 -#define HAVE_CHUNKMEMSET_1 #define HAVE_CHUNKMEMSET_2 #define HAVE_CHUNKMEMSET_4 #define HAVE_CHUNKMEMSET_8 -static inline void chunkmemset_1(uint8_t *from, chunk_t *chunk) { - *chunk = _mm256_set1_epi8(*(int8_t *)from); -} - static inline void chunkmemset_2(uint8_t *from, chunk_t *chunk) { - *chunk = _mm256_set1_epi16(*(int16_t *)from); + int16_t tmp; + memcpy(&tmp, from, sizeof(tmp)); + *chunk = _mm256_set1_epi16(tmp); } static inline void chunkmemset_4(uint8_t *from, chunk_t *chunk) { - *chunk = _mm256_set1_epi32(*(int32_t *)from); + int32_t tmp; + memcpy(&tmp, from, sizeof(tmp)); + *chunk = _mm256_set1_epi32(tmp); } static inline void chunkmemset_8(uint8_t *from, chunk_t *chunk) { - *chunk = _mm256_set1_epi64x(*(int64_t *)from); + int64_t tmp; + memcpy(&tmp, from, sizeof(tmp)); + *chunk = _mm256_set1_epi64x(tmp); } static inline void loadchunk(uint8_t const *s, chunk_t *chunk) { diff --git a/internal-complibs/zlib-ng-2.0.6/arch/x86/chunkset_sse.c b/internal-complibs/zlib-ng-2.0.7/arch/x86/chunkset_sse.c similarity index 79% rename from internal-complibs/zlib-ng-2.0.6/arch/x86/chunkset_sse.c rename to internal-complibs/zlib-ng-2.0.7/arch/x86/chunkset_sse.c index d38e99da..6b43d4a8 100644 --- a/internal-complibs/zlib-ng-2.0.6/arch/x86/chunkset_sse.c +++ b/internal-complibs/zlib-ng-2.0.7/arch/x86/chunkset_sse.c @@ -12,25 +12,26 @@ typedef __m128i chunk_t; #define CHUNK_SIZE 16 -#define HAVE_CHUNKMEMSET_1 #define HAVE_CHUNKMEMSET_2 #define HAVE_CHUNKMEMSET_4 #define HAVE_CHUNKMEMSET_8 -static inline void chunkmemset_1(uint8_t *from, chunk_t *chunk) { - *chunk = _mm_set1_epi8(*(int8_t *)from); -} - static inline void chunkmemset_2(uint8_t *from, chunk_t *chunk) { - *chunk = _mm_set1_epi16(*(int16_t *)from); + int16_t tmp; + memcpy(&tmp, from, sizeof(tmp)); + *chunk = _mm_set1_epi16(tmp); } static inline void chunkmemset_4(uint8_t *from, chunk_t *chunk) { - *chunk = _mm_set1_epi32(*(int32_t *)from); + int32_t tmp; + memcpy(&tmp, from, sizeof(tmp)); + *chunk = _mm_set1_epi32(tmp); } static inline void chunkmemset_8(uint8_t *from, chunk_t *chunk) { - *chunk = _mm_set1_epi64x(*(int64_t *)from); + int64_t tmp; + memcpy(&tmp, from, sizeof(tmp)); + *chunk = _mm_set1_epi64x(tmp); } static inline void loadchunk(uint8_t const *s, chunk_t *chunk) { diff --git a/internal-complibs/zlib-ng-2.0.6/arch/x86/compare258_avx.c b/internal-complibs/zlib-ng-2.0.7/arch/x86/compare258_avx.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/x86/compare258_avx.c rename to internal-complibs/zlib-ng-2.0.7/arch/x86/compare258_avx.c diff --git a/internal-complibs/zlib-ng-2.0.6/arch/x86/compare258_sse.c b/internal-complibs/zlib-ng-2.0.7/arch/x86/compare258_sse.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/x86/compare258_sse.c rename to internal-complibs/zlib-ng-2.0.7/arch/x86/compare258_sse.c diff --git a/internal-complibs/zlib-ng-2.0.6/arch/x86/crc_folding.c b/internal-complibs/zlib-ng-2.0.7/arch/x86/crc_folding.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/x86/crc_folding.c rename to internal-complibs/zlib-ng-2.0.7/arch/x86/crc_folding.c diff --git a/internal-complibs/zlib-ng-2.0.6/arch/x86/crc_folding.h b/internal-complibs/zlib-ng-2.0.7/arch/x86/crc_folding.h similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/x86/crc_folding.h rename to internal-complibs/zlib-ng-2.0.7/arch/x86/crc_folding.h diff --git a/internal-complibs/zlib-ng-2.0.6/arch/x86/insert_string_sse.c b/internal-complibs/zlib-ng-2.0.7/arch/x86/insert_string_sse.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/x86/insert_string_sse.c rename to internal-complibs/zlib-ng-2.0.7/arch/x86/insert_string_sse.c diff --git a/internal-complibs/zlib-ng-2.0.6/arch/x86/slide_avx.c b/internal-complibs/zlib-ng-2.0.7/arch/x86/slide_avx.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/x86/slide_avx.c rename to internal-complibs/zlib-ng-2.0.7/arch/x86/slide_avx.c diff --git a/internal-complibs/zlib-ng-2.0.6/arch/x86/slide_sse.c b/internal-complibs/zlib-ng-2.0.7/arch/x86/slide_sse.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/x86/slide_sse.c rename to internal-complibs/zlib-ng-2.0.7/arch/x86/slide_sse.c diff --git a/internal-complibs/zlib-ng-2.0.6/arch/x86/x86.c b/internal-complibs/zlib-ng-2.0.7/arch/x86/x86.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/x86/x86.c rename to internal-complibs/zlib-ng-2.0.7/arch/x86/x86.c diff --git a/internal-complibs/zlib-ng-2.0.6/arch/x86/x86.h b/internal-complibs/zlib-ng-2.0.7/arch/x86/x86.h similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/arch/x86/x86.h rename to internal-complibs/zlib-ng-2.0.7/arch/x86/x86.h diff --git a/internal-complibs/zlib-ng-2.0.7/chunkset.c b/internal-complibs/zlib-ng-2.0.7/chunkset.c new file mode 100644 index 00000000..59e30fc1 --- /dev/null +++ b/internal-complibs/zlib-ng-2.0.7/chunkset.c @@ -0,0 +1,47 @@ +/* chunkset.c -- inline functions to copy small data chunks. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zbuild.h" +#include "zutil.h" + +// We need sizeof(chunk_t) to be 8, no matter what. +#if defined(UNALIGNED64_OK) +typedef uint64_t chunk_t; +#elif defined(UNALIGNED_OK) +typedef struct chunk_t { uint32_t u32[2]; } chunk_t; +#else +typedef struct chunk_t { uint8_t u8[8]; } chunk_t; +#endif + +#define CHUNK_SIZE 8 + +#define HAVE_CHUNKMEMSET_4 +#define HAVE_CHUNKMEMSET_8 + +static inline void chunkmemset_4(uint8_t *from, chunk_t *chunk) { + uint8_t *dest = (uint8_t *)chunk; + memcpy(dest, from, sizeof(uint32_t)); + memcpy(dest+4, from, sizeof(uint32_t)); +} + +static inline void chunkmemset_8(uint8_t *from, chunk_t *chunk) { + memcpy(chunk, from, sizeof(uint64_t)); +} + +static inline void loadchunk(uint8_t const *s, chunk_t *chunk) { + memcpy(chunk, (uint8_t *)s, sizeof(uint64_t)); +} + +static inline void storechunk(uint8_t *out, chunk_t *chunk) { + memcpy(out, chunk, sizeof(uint64_t)); +} + +#define CHUNKSIZE chunksize_c +#define CHUNKCOPY chunkcopy_c +#define CHUNKCOPY_SAFE chunkcopy_safe_c +#define CHUNKUNROLL chunkunroll_c +#define CHUNKMEMSET chunkmemset_c +#define CHUNKMEMSET_SAFE chunkmemset_safe_c + +#include "chunkset_tpl.h" diff --git a/internal-complibs/zlib-ng-2.0.6/chunkset_tpl.h b/internal-complibs/zlib-ng-2.0.7/chunkset_tpl.h similarity index 98% rename from internal-complibs/zlib-ng-2.0.6/chunkset_tpl.h rename to internal-complibs/zlib-ng-2.0.7/chunkset_tpl.h index be52ee9f..68728d4a 100644 --- a/internal-complibs/zlib-ng-2.0.6/chunkset_tpl.h +++ b/internal-complibs/zlib-ng-2.0.7/chunkset_tpl.h @@ -200,5 +200,8 @@ Z_INTERNAL uint8_t* CHUNKMEMSET_SAFE(uint8_t *out, unsigned dist, unsigned len, } return out; } - return CHUNKMEMSET(out, dist, len); + if (len) + return CHUNKMEMSET(out, dist, len); + + return out; } diff --git a/internal-complibs/zlib-ng-2.0.6/cmake/detect-arch.c b/internal-complibs/zlib-ng-2.0.7/cmake/detect-arch.c similarity index 97% rename from internal-complibs/zlib-ng-2.0.6/cmake/detect-arch.c rename to internal-complibs/zlib-ng-2.0.7/cmake/detect-arch.c index 84e6a841..43ddb126 100644 --- a/internal-complibs/zlib-ng-2.0.6/cmake/detect-arch.c +++ b/internal-complibs/zlib-ng-2.0.7/cmake/detect-arch.c @@ -12,7 +12,7 @@ #error archfound i686 // ARM -#elif defined(__aarch64__) || defined(_M_ARM64) +#elif defined(__aarch64__) || defined(__arm64__) || defined(_M_ARM64) #error archfound aarch64 #elif defined(__arm__) || defined(__arm) || defined(_M_ARM) || defined(__TARGET_ARCH_ARM) #if defined(__ARM64_ARCH_8__) || defined(__ARMv8__) || defined(__ARMv8_A__) diff --git a/internal-complibs/zlib-ng-2.0.6/cmake/detect-arch.cmake b/internal-complibs/zlib-ng-2.0.7/cmake/detect-arch.cmake similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/cmake/detect-arch.cmake rename to internal-complibs/zlib-ng-2.0.7/cmake/detect-arch.cmake diff --git a/internal-complibs/zlib-ng-2.0.6/cmake/detect-coverage.cmake b/internal-complibs/zlib-ng-2.0.7/cmake/detect-coverage.cmake similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/cmake/detect-coverage.cmake rename to internal-complibs/zlib-ng-2.0.7/cmake/detect-coverage.cmake diff --git a/internal-complibs/zlib-ng-2.0.6/cmake/detect-install-dirs.cmake b/internal-complibs/zlib-ng-2.0.7/cmake/detect-install-dirs.cmake similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/cmake/detect-install-dirs.cmake rename to internal-complibs/zlib-ng-2.0.7/cmake/detect-install-dirs.cmake diff --git a/internal-complibs/zlib-ng-2.0.6/cmake/detect-sanitizer.cmake b/internal-complibs/zlib-ng-2.0.7/cmake/detect-sanitizer.cmake similarity index 96% rename from internal-complibs/zlib-ng-2.0.6/cmake/detect-sanitizer.cmake rename to internal-complibs/zlib-ng-2.0.7/cmake/detect-sanitizer.cmake index b0a0236e..8af1f8e0 100644 --- a/internal-complibs/zlib-ng-2.0.6/cmake/detect-sanitizer.cmake +++ b/internal-complibs/zlib-ng-2.0.7/cmake/detect-sanitizer.cmake @@ -16,12 +16,12 @@ macro(check_sanitizer_support known_checks supported_checks) set(CMAKE_REQUIRED_FLAGS "-fsanitize=${compile_checks}") - check_c_source_compiles("int main() { return 0; }" HAS_SANITIZER_${check} + check_c_source_compiles("int main() { return 0; }" HAVE_SANITIZER_${check} FAIL_REGEX "not supported|unrecognized command|unknown option") set(CMAKE_REQUIRED_FLAGS) - if(HAS_SANITIZER_${check}) + if(HAVE_SANITIZER_${check}) set(available_checks ${compile_checks}) endif() endforeach() @@ -108,7 +108,7 @@ macro(add_undefined_sanitizer) ) # Only check for alignment sanitizer flag if unaligned access is not supported - if(NOT UNALIGNED_OK) + if(NOT WITH_UNALIGNED) list(APPEND known_checks alignment) endif() # Object size sanitizer has no effect at -O0 and produces compiler warning if enabled @@ -124,7 +124,7 @@ macro(add_undefined_sanitizer) # Group sanitizer flag -fsanitize=undefined will automatically add alignment, even if # it is not in our sanitize flag list, so we need to explicitly disable alignment sanitizing. - if(UNALIGNED_OK) + if(WITH_UNALIGNED) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-sanitize=alignment") endif() else() diff --git a/internal-complibs/zlib-ng-2.0.6/cmake/run-and-compare.cmake b/internal-complibs/zlib-ng-2.0.7/cmake/run-and-compare.cmake similarity index 72% rename from internal-complibs/zlib-ng-2.0.6/cmake/run-and-compare.cmake rename to internal-complibs/zlib-ng-2.0.7/cmake/run-and-compare.cmake index 209d31e9..eb2218dc 100644 --- a/internal-complibs/zlib-ng-2.0.6/cmake/run-and-compare.cmake +++ b/internal-complibs/zlib-ng-2.0.7/cmake/run-and-compare.cmake @@ -16,6 +16,10 @@ if(NOT DEFINED OUTPUT OR NOT DEFINED COMPARE OR NOT DEFINED COMMAND) message(FATAL_ERROR "Run and compare arguments missing") endif() +# Ensure directory exists for output files +get_filename_component(OUTPUT_DIR "${OUTPUT}" DIRECTORY) +file(MAKE_DIRECTORY "${OUTPUT_DIR}") + if(INPUT) # Run command with stdin input and redirect stdout to output execute_process(COMMAND ${CMAKE_COMMAND} @@ -41,10 +45,16 @@ endif() # Use configure_file to normalize line-endings if(IGNORE_LINE_ENDINGS) - configure_file(${COMPARE} ${COMPARE}.cmp NEWLINE_STYLE LF) - set(COMPARE ${COMPARE}.cmp) - configure_file(${OUTPUT} ${OUTPUT}.cmp NEWLINE_STYLE LF) - set(OUTPUT ${OUTPUT}.cmp) + # Rewrite files with normalized line endings to temporary directory + get_filename_component(COMPARE_NAME ${COMPARE} NAME) + set(COMPARE_TEMP ${CMAKE_CURRENT_BINARY_DIR}/Testing/Temporary/${COMPARE_NAME}.cmp) + configure_file(${COMPARE} ${COMPARE_TEMP} NEWLINE_STYLE LF) + set(COMPARE ${COMPARE_TEMP}) + + get_filename_component(OUTPUT_NAME ${OUTPUT} NAME) + set(OUTPUT_TEMP ${CMAKE_CURRENT_BINARY_DIR}/Testing/Temporary/${OUTPUT_NAME}.cmp) + configure_file(${OUTPUT} ${OUTPUT_TEMP} NEWLINE_STYLE LF) + set(OUTPUT ${OUTPUT_TEMP}) endif() # Compare that output is equal to specified file diff --git a/internal-complibs/zlib-ng-2.0.6/cmake/run-and-redirect.cmake b/internal-complibs/zlib-ng-2.0.7/cmake/run-and-redirect.cmake similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/cmake/run-and-redirect.cmake rename to internal-complibs/zlib-ng-2.0.7/cmake/run-and-redirect.cmake diff --git a/internal-complibs/zlib-ng-2.0.6/cmake/test-compress.cmake b/internal-complibs/zlib-ng-2.0.7/cmake/test-compress.cmake similarity index 99% rename from internal-complibs/zlib-ng-2.0.6/cmake/test-compress.cmake rename to internal-complibs/zlib-ng-2.0.7/cmake/test-compress.cmake index cfc7613a..199796d8 100644 --- a/internal-complibs/zlib-ng-2.0.6/cmake/test-compress.cmake +++ b/internal-complibs/zlib-ng-2.0.7/cmake/test-compress.cmake @@ -173,7 +173,7 @@ if(GZIP_VERIFY AND NOT "${COMPRESS_ARGS}" MATCHES "-T") endif() # Check gzip can decompress our compressed output - set(GZ_DECOMPRESS_COMMAND ${GZIP} --decompress) + set(GZ_DECOMPRESS_COMMAND ${GZIP} -d) message(STATUS "Gzip decompress ${GZ_DECOMPRESS_COMMAND}") message(STATUS " Input: ${OUTPUT_BASE}.gz") diff --git a/internal-complibs/zlib-ng-2.0.6/cmake/test-tools.cmake b/internal-complibs/zlib-ng-2.0.7/cmake/test-tools.cmake similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/cmake/test-tools.cmake rename to internal-complibs/zlib-ng-2.0.7/cmake/test-tools.cmake diff --git a/internal-complibs/zlib-ng-2.0.6/cmake/toolchain-aarch64.cmake b/internal-complibs/zlib-ng-2.0.7/cmake/toolchain-aarch64.cmake similarity index 67% rename from internal-complibs/zlib-ng-2.0.6/cmake/toolchain-aarch64.cmake rename to internal-complibs/zlib-ng-2.0.7/cmake/toolchain-aarch64.cmake index 31894fdc..1e247310 100644 --- a/internal-complibs/zlib-ng-2.0.6/cmake/toolchain-aarch64.cmake +++ b/internal-complibs/zlib-ng-2.0.7/cmake/toolchain-aarch64.cmake @@ -2,8 +2,6 @@ set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR aarch64) set(CMAKE_SYSTEM_VERSION 1) -message(STATUS "Using cross-compile toolchain: ${CROSS_COMPILE_TOOLCHAIN}") - set(CMAKE_C_COMPILER_TARGET "aarch64-linux-gnu") set(CMAKE_CXX_COMPILER_TARGET "aarch64-linux-gnu") @@ -14,13 +12,13 @@ SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -find_program(C_COMPILER_FULL_PATH ${CMAKE_C_COMPILER_TARGET}-gcc) +find_program(C_COMPILER_FULL_PATH NAMES ${CMAKE_C_COMPILER_TARGET}-gcc) if(NOT C_COMPILER_FULL_PATH) - message(FATAL_ERROR "Cross-compiler ${CMAKE_C_COMPILER_TARGET}-gcc not found") + message(FATAL_ERROR "Cross-compiler for ${CMAKE_C_COMPILER_TARGET} not found") endif() set(CMAKE_C_COMPILER ${C_COMPILER_FULL_PATH}) -find_program(CXX_COMPILER_FULL_PATH ${CMAKE_C_COMPILER_TARGET}-g++) +find_program(CXX_COMPILER_FULL_PATH NAMES g++-${CMAKE_CXX_COMPILER_TARGET} ${CMAKE_CXX_COMPILER_TARGET}-g++) if(CXX_COMPILER_FULL_PATH) set(CMAKE_CXX_COMPILER ${CXX_COMPILER_FULL_PATH}) endif() diff --git a/internal-complibs/zlib-ng-2.0.7/cmake/toolchain-arm.cmake b/internal-complibs/zlib-ng-2.0.7/cmake/toolchain-arm.cmake new file mode 100644 index 00000000..1bdd8d2c --- /dev/null +++ b/internal-complibs/zlib-ng-2.0.7/cmake/toolchain-arm.cmake @@ -0,0 +1,29 @@ +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR arm) +set(CMAKE_SYSTEM_VERSION 1) + +if(NOT DEFINED CMAKE_C_COMPILER_TARGET) + set(CMAKE_C_COMPILER_TARGET arm-linux-gnueabi) +endif() +if(NOT DEFINED CMAKE_CXX_COMPILER_TARGET) + set(CMAKE_CXX_COMPILER_TARGET arm-linux-gnueabi) +endif() + +set(CMAKE_CROSSCOMPILING TRUE) +set(CMAKE_CROSSCOMPILING_EMULATOR qemu-arm -L /usr/${CMAKE_C_COMPILER_TARGET}/) + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +find_program(C_COMPILER_FULL_PATH NAMES ${CMAKE_C_COMPILER_TARGET}-gcc) +if(NOT C_COMPILER_FULL_PATH) + message(FATAL_ERROR "Cross-compiler for ${CMAKE_C_COMPILER_TARGET} not found") +endif() +set(CMAKE_C_COMPILER ${C_COMPILER_FULL_PATH}) + +find_program(CXX_COMPILER_FULL_PATH NAMES g++-${CMAKE_CXX_COMPILER_TARGET} ${CMAKE_CXX_COMPILER_TARGET}-g++) +if(CXX_COMPILER_FULL_PATH) + set(CMAKE_CXX_COMPILER ${CXX_COMPILER_FULL_PATH}) +endif() diff --git a/internal-complibs/zlib-ng-2.0.6/cmake/toolchain-arm.cmake b/internal-complibs/zlib-ng-2.0.7/cmake/toolchain-armhf.cmake similarity index 60% rename from internal-complibs/zlib-ng-2.0.6/cmake/toolchain-arm.cmake rename to internal-complibs/zlib-ng-2.0.7/cmake/toolchain-armhf.cmake index 0e3c5c37..007859ca 100644 --- a/internal-complibs/zlib-ng-2.0.6/cmake/toolchain-arm.cmake +++ b/internal-complibs/zlib-ng-2.0.7/cmake/toolchain-armhf.cmake @@ -2,7 +2,8 @@ set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR arm) set(CMAKE_SYSTEM_VERSION 1) -message(STATUS "Using cross-compile toolchain: ${CMAKE_C_COMPILER_TARGET}") +set(CMAKE_C_COMPILER_TARGET arm-linux-gnueabihf) +set(CMAKE_CXX_COMPILER_TARGET arm-linux-gnueabihf) set(CMAKE_CROSSCOMPILING TRUE) set(CMAKE_CROSSCOMPILING_EMULATOR qemu-arm -L /usr/${CMAKE_C_COMPILER_TARGET}/) @@ -12,13 +13,13 @@ set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) -find_program(C_COMPILER_FULL_PATH ${CMAKE_C_COMPILER_TARGET}-gcc) +find_program(C_COMPILER_FULL_PATH NAMES ${CMAKE_C_COMPILER_TARGET}-gcc) if(NOT C_COMPILER_FULL_PATH) - message(FATAL_ERROR "Cross-compiler ${CMAKE_C_COMPILER_TARGET}-gcc not found") + message(FATAL_ERROR "Cross-compiler for ${CMAKE_C_COMPILER_TARGET} not found") endif() set(CMAKE_C_COMPILER ${C_COMPILER_FULL_PATH}) -find_program(CXX_COMPILER_FULL_PATH ${CMAKE_C_COMPILER_TARGET}-g++) +find_program(CXX_COMPILER_FULL_PATH NAMES g++-${CMAKE_CXX_COMPILER_TARGET} ${CMAKE_CXX_COMPILER_TARGET}-g++) if(CXX_COMPILER_FULL_PATH) set(CMAKE_CXX_COMPILER ${CXX_COMPILER_FULL_PATH}) endif() diff --git a/internal-complibs/zlib-ng-2.0.7/cmake/toolchain-mingw-i686.cmake b/internal-complibs/zlib-ng-2.0.7/cmake/toolchain-mingw-i686.cmake new file mode 100644 index 00000000..b95e63f5 --- /dev/null +++ b/internal-complibs/zlib-ng-2.0.7/cmake/toolchain-mingw-i686.cmake @@ -0,0 +1,35 @@ +set(CMAKE_SYSTEM_NAME Windows) + +set(CMAKE_C_COMPILER_TARGET i686-w64-mingw32) +set(CMAKE_CXX_COMPILER_TARGET i686-w64-mingw32) +set(CMAKE_RC_COMPILER_TARGET i686-w64-mingw32) + +set(CMAKE_CROSSCOMPILING TRUE) +set(CMAKE_CROSSCOMPILING_EMULATOR wine) + +set(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32) +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +# Prefer posix gcc variant for gtest pthread support +find_program(C_COMPILER_FULL_PATH NAMES + ${CMAKE_C_COMPILER_TARGET}-gcc-posix + ${CMAKE_C_COMPILER_TARGET}-gcc) +if(NOT C_COMPILER_FULL_PATH) + message(FATAL_ERROR "Cross-compiler for ${CMAKE_C_COMPILER_TARGET} not found") +endif() +set(CMAKE_C_COMPILER ${C_COMPILER_FULL_PATH}) + +find_program(CXX_COMPILER_FULL_PATH NAMES + ${CMAKE_CXX_COMPILER_TARGET}-g++-posix + ${CMAKE_CXX_COMPILER_TARGET}-g++) +if(CXX_COMPILER_FULL_PATH) + set(CMAKE_CXX_COMPILER ${CXX_COMPILER_FULL_PATH}) +endif() + +find_program(RC_COMPILER_FULL_PATH NAMES + ${CMAKE_RC_COMPILER_TARGET}-windres) +if(RC_COMPILER_FULL_PATH) + set(CMAKE_RC_COMPILER ${RC_COMPILER_FULL_PATH}) +endif() diff --git a/internal-complibs/zlib-ng-2.0.7/cmake/toolchain-mingw-x86_64.cmake b/internal-complibs/zlib-ng-2.0.7/cmake/toolchain-mingw-x86_64.cmake new file mode 100644 index 00000000..8c660b0b --- /dev/null +++ b/internal-complibs/zlib-ng-2.0.7/cmake/toolchain-mingw-x86_64.cmake @@ -0,0 +1,34 @@ +set(CMAKE_SYSTEM_NAME Windows) + +set(CMAKE_C_COMPILER_TARGET x86_64-w64-mingw32) +set(CMAKE_CXX_COMPILER_TARGET x86_64-w64-mingw32) +set(CMAKE_RC_COMPILER_TARGET x86_64-w64-mingw32) + +set(CMAKE_CROSSCOMPILING TRUE) +set(CMAKE_CROSSCOMPILING_EMULATOR wine) + +set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32) +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +# Prefer posix gcc variant for gtest pthread support +find_program(C_COMPILER_FULL_PATH NAMES + ${CMAKE_C_COMPILER_TARGET}-gcc-posix + ${CMAKE_C_COMPILER_TARGET}-gcc) +if(NOT C_COMPILER_FULL_PATH) + message(FATAL_ERROR "Cross-compiler for ${CMAKE_C_COMPILER_TARGET} not found") +endif() +set(CMAKE_C_COMPILER ${C_COMPILER_FULL_PATH}) + +find_program(CXX_COMPILER_FULL_PATH NAMES + ${CMAKE_CXX_COMPILER_TARGET}-g++-posix + ${CMAKE_CXX_COMPILER_TARGET}-g++) +if(CXX_COMPILER_FULL_PATH) + set(CMAKE_CXX_COMPILER ${CXX_COMPILER_FULL_PATH}) +endif() + +find_program(RC_COMPILER_FULL_PATH NAMES ${CMAKE_RC_COMPILER_TARGET}-windres) +if(RC_COMPILER_FULL_PATH) + set(CMAKE_RC_COMPILER ${RC_COMPILER_FULL_PATH}) +endif() diff --git a/internal-complibs/zlib-ng-2.0.6/cmake/toolchain-powerpc.cmake b/internal-complibs/zlib-ng-2.0.7/cmake/toolchain-powerpc.cmake similarity index 51% rename from internal-complibs/zlib-ng-2.0.6/cmake/toolchain-powerpc.cmake rename to internal-complibs/zlib-ng-2.0.7/cmake/toolchain-powerpc.cmake index 4f7f8e92..f0971337 100644 --- a/internal-complibs/zlib-ng-2.0.6/cmake/toolchain-powerpc.cmake +++ b/internal-complibs/zlib-ng-2.0.7/cmake/toolchain-powerpc.cmake @@ -2,24 +2,24 @@ set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR powerpc) set(CMAKE_SYSTEM_VERSION 1) -set(CMAKE_C_COMPILER_TARGET "powerpc-linux-gnu") -set(CMAKE_CXX_COMPILER_TARGET "powerpc-linux-gnu") +set(CMAKE_C_COMPILER_TARGET powerpc-linux-gnu) +set(CMAKE_CXX_COMPILER_TARGET powerpc-linux-gnu) set(CMAKE_CROSSCOMPILING TRUE) -set(CMAKE_CROSSCOMPILING_EMULATOR qemu-ppc -L /usr/${CMAKE_C_COMPILER_TARGET}/) +set(CMAKE_CROSSCOMPILING_EMULATOR qemu-ppc -cpu 7400 -L /usr/${CMAKE_C_COMPILER_TARGET}/) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) -find_program(C_COMPILER_FULL_PATH ${CMAKE_C_COMPILER_TARGET}-gcc) +find_program(C_COMPILER_FULL_PATH NAMES ${CMAKE_C_COMPILER_TARGET}-gcc) if(NOT C_COMPILER_FULL_PATH) - message(FATAL_ERROR "Cross-compiler ${CMAKE_C_COMPILER_TARGET}-gcc not found") + message(FATAL_ERROR "Cross-compiler for ${CMAKE_C_COMPILER_TARGET} not found") endif() set(CMAKE_C_COMPILER ${C_COMPILER_FULL_PATH}) -find_program(CXX_COMPILER_FULL_PATH ${CMAKE_C_COMPILER_TARGET}-g++) +find_program(CXX_COMPILER_FULL_PATH NAMES g++-${CMAKE_CXX_COMPILER_TARGET} ${CMAKE_CXX_COMPILER_TARGET}-g++) if(CXX_COMPILER_FULL_PATH) set(CMAKE_CXX_COMPILER ${CXX_COMPILER_FULL_PATH}) endif() diff --git a/internal-complibs/zlib-ng-2.0.6/cmake/toolchain-powerpc64.cmake b/internal-complibs/zlib-ng-2.0.7/cmake/toolchain-powerpc64.cmake similarity index 50% rename from internal-complibs/zlib-ng-2.0.6/cmake/toolchain-powerpc64.cmake rename to internal-complibs/zlib-ng-2.0.7/cmake/toolchain-powerpc64.cmake index 4be3bbd5..80d8b904 100644 --- a/internal-complibs/zlib-ng-2.0.6/cmake/toolchain-powerpc64.cmake +++ b/internal-complibs/zlib-ng-2.0.7/cmake/toolchain-powerpc64.cmake @@ -2,24 +2,24 @@ set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR ppc64) set(CMAKE_SYSTEM_VERSION 1) -set(CMAKE_C_COMPILER_TARGET "powerpc64-linux-gnu") -set(CMAKE_CXX_COMPILER_TARGET "powerpc64-linux-gnu") +set(CMAKE_C_COMPILER_TARGET powerpc64-linux-gnu) +set(CMAKE_CXX_COMPILER_TARGET powerpc64-linux-gnu) set(CMAKE_CROSSCOMPILING TRUE) -set(CMAKE_CROSSCOMPILING_EMULATOR qemu-ppc64 -L /usr/${CMAKE_C_COMPILER_TARGET}/) +set(CMAKE_CROSSCOMPILING_EMULATOR qemu-ppc64 -cpu power8 -L /usr/${CMAKE_C_COMPILER_TARGET}/) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) -find_program(C_COMPILER_FULL_PATH ${CMAKE_C_COMPILER_TARGET}-gcc) +find_program(C_COMPILER_FULL_PATH NAMES ${CMAKE_C_COMPILER_TARGET}-gcc) if(NOT C_COMPILER_FULL_PATH) - message(FATAL_ERROR "Cross-compiler ${CMAKE_C_COMPILER_TARGET}-gcc not found") + message(FATAL_ERROR "Cross-compiler for ${CMAKE_C_COMPILER_TARGET} not found") endif() set(CMAKE_C_COMPILER ${C_COMPILER_FULL_PATH}) -find_program(CXX_COMPILER_FULL_PATH ${CMAKE_C_COMPILER_TARGET}-g++) +find_program(CXX_COMPILER_FULL_PATH NAMES g++-${CMAKE_CXX_COMPILER_TARGET} ${CMAKE_CXX_COMPILER_TARGET}-g++) if(CXX_COMPILER_FULL_PATH) set(CMAKE_CXX_COMPILER ${CXX_COMPILER_FULL_PATH}) endif() diff --git a/internal-complibs/zlib-ng-2.0.6/cmake/toolchain-powerpc64le.cmake b/internal-complibs/zlib-ng-2.0.7/cmake/toolchain-powerpc64le.cmake similarity index 50% rename from internal-complibs/zlib-ng-2.0.6/cmake/toolchain-powerpc64le.cmake rename to internal-complibs/zlib-ng-2.0.7/cmake/toolchain-powerpc64le.cmake index 5535f616..68381de1 100644 --- a/internal-complibs/zlib-ng-2.0.6/cmake/toolchain-powerpc64le.cmake +++ b/internal-complibs/zlib-ng-2.0.7/cmake/toolchain-powerpc64le.cmake @@ -2,24 +2,24 @@ set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR ppc64le) set(CMAKE_SYSTEM_VERSION 1) -set(CMAKE_C_COMPILER_TARGET "powerpc64le-linux-gnu") -set(CMAKE_CXX_COMPILER_TARGET "powerpc64le-linux-gnu") +set(CMAKE_C_COMPILER_TARGET powerpc64le-linux-gnu) +set(CMAKE_CXX_COMPILER_TARGET powerpc64le-linux-gnu) set(CMAKE_CROSSCOMPILING TRUE) -set(CMAKE_CROSSCOMPILING_EMULATOR qemu-ppc64le -L /usr/${CMAKE_C_COMPILER_TARGET}/) +set(CMAKE_CROSSCOMPILING_EMULATOR qemu-ppc64le -cpu power8 -L /usr/${CMAKE_C_COMPILER_TARGET}/) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) -find_program(C_COMPILER_FULL_PATH ${CMAKE_C_COMPILER_TARGET}-gcc) +find_program(C_COMPILER_FULL_PATH NAMES ${CMAKE_C_COMPILER_TARGET}-gcc) if(NOT C_COMPILER_FULL_PATH) - message(FATAL_ERROR "Cross-compiler ${CMAKE_C_COMPILER_TARGET}-gcc not found") + message(FATAL_ERROR "Cross-compiler for ${CMAKE_C_COMPILER_TARGET} not found") endif() set(CMAKE_C_COMPILER ${C_COMPILER_FULL_PATH}) -find_program(CXX_COMPILER_FULL_PATH ${CMAKE_C_COMPILER_TARGET}-g++) +find_program(CXX_COMPILER_FULL_PATH NAMES g++-${CMAKE_CXX_COMPILER_TARGET} ${CMAKE_CXX_COMPILER_TARGET}-g++) if(CXX_COMPILER_FULL_PATH) set(CMAKE_CXX_COMPILER ${CXX_COMPILER_FULL_PATH}) endif() diff --git a/internal-complibs/zlib-ng-2.0.6/cmake/toolchain-s390x.cmake b/internal-complibs/zlib-ng-2.0.7/cmake/toolchain-s390x.cmake similarity index 61% rename from internal-complibs/zlib-ng-2.0.6/cmake/toolchain-s390x.cmake rename to internal-complibs/zlib-ng-2.0.7/cmake/toolchain-s390x.cmake index 41bc0d10..9455a2be 100644 --- a/internal-complibs/zlib-ng-2.0.6/cmake/toolchain-s390x.cmake +++ b/internal-complibs/zlib-ng-2.0.7/cmake/toolchain-s390x.cmake @@ -2,8 +2,8 @@ set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR s390x) set(CMAKE_SYSTEM_VERSION 1) -set(CMAKE_C_COMPILER_TARGET "s390x-linux-gnu") -set(CMAKE_CXX_COMPILER_TARGET "s390x-linux-gnu") +set(CMAKE_C_COMPILER_TARGET s390x-linux-gnu) +set(CMAKE_CXX_COMPILER_TARGET s390x-linux-gnu) set(CMAKE_CROSSCOMPILING TRUE) set(CMAKE_CROSSCOMPILING_EMULATOR qemu-s390x -L /usr/${CMAKE_C_COMPILER_TARGET}/) @@ -13,13 +13,13 @@ set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) -find_program(C_COMPILER_FULL_PATH ${CMAKE_C_COMPILER_TARGET}-gcc) +find_program(C_COMPILER_FULL_PATH NAMES ${CMAKE_C_COMPILER_TARGET}-gcc) if(NOT C_COMPILER_FULL_PATH) - message(FATAL_ERROR "Cross-compiler ${CMAKE_C_COMPILER_TARGET}-gcc not found") + message(FATAL_ERROR "Cross-compiler for ${CMAKE_C_COMPILER_TARGET} not found") endif() set(CMAKE_C_COMPILER ${C_COMPILER_FULL_PATH}) -find_program(CXX_COMPILER_FULL_PATH ${CMAKE_C_COMPILER_TARGET}-g++) +find_program(CXX_COMPILER_FULL_PATH NAMES g++-${CMAKE_CXX_COMPILER_TARGET} ${CMAKE_CXX_COMPILER_TARGET}-g++) if(CXX_COMPILER_FULL_PATH) set(CMAKE_CXX_COMPILER ${CXX_COMPILER_FULL_PATH}) endif() diff --git a/internal-complibs/zlib-ng-2.0.6/cmake/toolchain-sparc64.cmake b/internal-complibs/zlib-ng-2.0.7/cmake/toolchain-sparc64.cmake similarity index 60% rename from internal-complibs/zlib-ng-2.0.6/cmake/toolchain-sparc64.cmake rename to internal-complibs/zlib-ng-2.0.7/cmake/toolchain-sparc64.cmake index f0cd9956..16161a78 100644 --- a/internal-complibs/zlib-ng-2.0.6/cmake/toolchain-sparc64.cmake +++ b/internal-complibs/zlib-ng-2.0.7/cmake/toolchain-sparc64.cmake @@ -2,8 +2,8 @@ set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR sparc64) set(CMAKE_SYSTEM_VERSION 1) -set(CMAKE_C_COMPILER_TARGET "sparc64-linux-gnu") -set(CMAKE_CXX_COMPILER_TARGET "sparc64-linux-gnu") +set(CMAKE_C_COMPILER_TARGET sparc64-linux-gnu) +set(CMAKE_CXX_COMPILER_TARGET sparc64-linux-gnu) set(CMAKE_CROSSCOMPILING TRUE) set(CMAKE_CROSSCOMPILING_EMULATOR qemu-sparc64 -L /usr/${CMAKE_C_COMPILER_TARGET}/) @@ -13,13 +13,13 @@ set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) -find_program(C_COMPILER_FULL_PATH ${CMAKE_C_COMPILER_TARGET}-gcc) +find_program(C_COMPILER_FULL_PATH NAMES ${CMAKE_C_COMPILER_TARGET}-gcc) if(NOT C_COMPILER_FULL_PATH) - message(FATAL_ERROR "Cross-compiler ${CMAKE_C_COMPILER_TARGET}-gcc not found") + message(FATAL_ERROR "Cross-compiler for ${CMAKE_C_COMPILER_TARGET} not found") endif() set(CMAKE_C_COMPILER ${C_COMPILER_FULL_PATH}) -find_program(CXX_COMPILER_FULL_PATH ${CMAKE_C_COMPILER_TARGET}-g++) +find_program(CXX_COMPILER_FULL_PATH NAMES g++-${CMAKE_CXX_COMPILER_TARGET} ${CMAKE_CXX_COMPILER_TARGET}-g++) if(CXX_COMPILER_FULL_PATH) set(CMAKE_CXX_COMPILER ${CXX_COMPILER_FULL_PATH}) endif() diff --git a/internal-complibs/zlib-ng-2.0.6/compare258.c b/internal-complibs/zlib-ng-2.0.7/compare258.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/compare258.c rename to internal-complibs/zlib-ng-2.0.7/compare258.c diff --git a/internal-complibs/zlib-ng-2.0.6/compress.c b/internal-complibs/zlib-ng-2.0.7/compress.c similarity index 95% rename from internal-complibs/zlib-ng-2.0.6/compress.c rename to internal-complibs/zlib-ng-2.0.7/compress.c index fded2a4c..1cf5d5ff 100644 --- a/internal-complibs/zlib-ng-2.0.6/compress.c +++ b/internal-complibs/zlib-ng-2.0.7/compress.c @@ -92,6 +92,8 @@ z_size_t Z_EXPORT PREFIX(compressBound)(z_size_t sourceLen) { #ifndef NO_QUICK_STRATEGY return sourceLen /* The source size itself */ + + (sourceLen == 0 ? 1 : 0) /* Always at least one byte for any input */ + + (sourceLen < 9 ? 1 : 0) /* One extra byte for lengths less than 9 */ + DEFLATE_QUICK_OVERHEAD(sourceLen) /* Source encoding overhead, padded to next full byte */ + DEFLATE_BLOCK_OVERHEAD /* Deflate block overhead bytes */ + ZLIB_WRAPLEN; /* zlib wrapper */ diff --git a/internal-complibs/zlib-ng-2.0.6/configure b/internal-complibs/zlib-ng-2.0.7/configure similarity index 98% rename from internal-complibs/zlib-ng-2.0.6/configure rename to internal-complibs/zlib-ng-2.0.7/configure index afc07f94..6e978ebb 100755 --- a/internal-complibs/zlib-ng-2.0.6/configure +++ b/internal-complibs/zlib-ng-2.0.7/configure @@ -99,6 +99,7 @@ with_fuzzers=0 floatabi= native=0 forcesse2=0 +forcetzcnt=0 avx2flag="-mavx2" sse2flag="-msse2" ssse3flag="-mssse3" @@ -155,6 +156,7 @@ case "$1" in echo ' [--with-dfltcc-deflate] Use DEFLATE CONVERSION CALL instruction for compression on IBM Z' | tee -a configure.log echo ' [--with-dfltcc-inflate] Use DEFLATE CONVERSION CALL instruction for decompression on IBM Z' | tee -a configure.log echo ' [--force-sse2] Assume SSE2 instructions are always available (disabled by default on x86, enabled on x86_64)' | tee -a configure.log + echo ' [--force-tzcnt] Assume TZCNT instructions are always available (disabled by default)' | tee -a configure.log echo ' [--with-sanitizer] Build with sanitizer (memory, address, undefined)' | tee -a configure.log echo ' [--with-fuzzers] Build test/fuzz (disabled by default)' | tee -a configure.log echo ' [--native] Compiles with full instruction set supported on this host' | tee -a configure.log @@ -181,6 +183,7 @@ case "$1" in --with-dfltcc-deflate) builddfltccdeflate=1; shift ;; --with-dfltcc-inflate) builddfltccinflate=1; shift ;; --force-sse2) forcesse2=1; shift ;; + --force-tzcnt) forcetzcnt=1; shift ;; -n | --native) native=1; shift ;; -a*=* | --archs=*) ARCHS=`echo $1 | sed 's/.*=//'`; shift ;; --sysconfdir=*) echo "ignored option: --sysconfdir" | tee -a configure.log; shift ;; @@ -329,7 +332,7 @@ if test "$gcc" -eq 1 && ($cc $CFLAGS -c $test.c) >> configure.log 2>&1; then else ARCH=native fi ;; - aarch64 | aarch64_be) + aarch64 | aarch64_be | arm64) if test "${uname}" = "elf"; then uname=aarch64 fi @@ -399,7 +402,6 @@ if test "$gcc" -eq 1 && ($cc $CFLAGS -c $test.c) >> configure.log 2>&1; then LDSHAREDLIBC="" DEFFILE='win32/${LIBNAME2}.def' RC="${CROSS_PREFIX}windres" - RCFLAGS='--define GCC_WINDRES' RCOBJS='zlibrc.o' STRIP="${CROSS_PREFIX}strip" EXE='.exe' ;; @@ -422,13 +424,12 @@ if test "$gcc" -eq 1 && ($cc $CFLAGS -c $test.c) >> configure.log 2>&1; then LDSHAREDLIBC="" DEFFILE='win32/${LIBNAME2}.def' RC="${CROSS_PREFIX}windres" - RCFLAGS='--define GCC_WINDRES' RCOBJS='zlibrc.o' STRIP="${CROSS_PREFIX}strip" EXE='.exe' ;; MINGW* | mingw*) ARFLAGS="rcs" - CFLAGS="${CFLAGS} -D_POSIX_C_SOURCE=200809L -D_GNU_SOURCE=1" + CFLAGS="${CFLAGS} -D_POSIX_C_SOURCE=200809L -D_GNU_SOURCE=1 -Wno-pedantic-ms-format" SFLAGS="${CFLAGS}" shared_ext='.dll' sharedlibdir='${bindir}' @@ -442,7 +443,6 @@ if test "$gcc" -eq 1 && ($cc $CFLAGS -c $test.c) >> configure.log 2>&1; then LDSHAREDLIBC="" DEFFILE='win32/${LIBNAME2}.def' RC="${CROSS_PREFIX}windres" - RCFLAGS='--define GCC_WINDRES' if [ "$CC" == "mingw32-gcc" ]; then case $ARCH in i386 | i486 | i586 | i686) RCFLAGS="${RCFLAGS} -F pe-i386";; @@ -551,29 +551,17 @@ if ($CC -c $CFLAGS $test.c) 2>/dev/null; then } echo - using any output from compiler to indicate an error >> configure.log else -try() -{ - show $* - ( $* ) >> configure.log 2>&1 - ret=$? - if test $ret -ne 0; then - echo "(exit code "$ret")" >> configure.log - fi - return $ret -} -fi - -tryboth() -{ - show $* - got=`( $* ) 2>&1` - ret=$? - printf %s "$got" >> configure.log - if test $ret -ne 0; then + try() + { + show $* + ( $* ) >> configure.log 2>&1 + ret=$? + if test $ret -ne 0; then + echo "(exit code $ret)" >> configure.log + fi return $ret - fi - test "$got" = "" -} + } +fi cat > $test.c << EOF int foo() { return 0; } @@ -762,6 +750,25 @@ else fi echo >> configure.log +cat > $test.c < +int main(void) { + void *ptr = aligned_alloc(64, 10); + if (ptr) + free(ptr); + return 0; +} +EOF +if try $CC $CFLAGS -o $test $test.c $LDSHAREDLIBC; then + echo "Checking for aligned_alloc... Yes." | tee -a configure.log + CFLAGS="${CFLAGS} -DHAVE_ALIGNED_ALLOC" + SFLAGS="${SFLAGS} -DHAVE_ALIGNED_ALLOC" +else + echo "Checking for aligned_alloc... No." | tee -a configure.log +fi +echo >> configure.log + # check for strerror() for use by gz* functions cat > $test.c < @@ -875,6 +882,7 @@ echo >> configure.log # Check for ANSI C compliant compiler cat > $test.c < #include #include #include "zconf${SUFFIX}.h" @@ -927,7 +935,7 @@ if test "$gcc" -eq 1; then int Z_INTERNAL foo; int main() { return 0; } EOF - if tryboth $CC -c $CFLAGS $test.c; then + if try $CC $CFLAGS $test.c; then CFLAGS="$CFLAGS -DHAVE_VISIBILITY_HIDDEN" SFLAGS="$SFLAGS -DHAVE_VISIBILITY_HIDDEN" echo >> configure.log @@ -946,7 +954,7 @@ if test "$gcc" -eq 1; then int Z_INTERNAL foo; int main() { return 0; } EOF - if tryboth $CC -c $CFLAGS $test.c; then + if try $CC $CFLAGS $test.c; then CFLAGS="$CFLAGS -DHAVE_VISIBILITY_INTERNAL" SFLAGS="$SFLAGS -DHAVE_VISIBILITY_INTERNAL" echo >> configure.log @@ -1258,8 +1266,8 @@ case "${ARCH}" in fi if test ${HAVE_SSE2_INTRIN} -eq 1; then - CFLAGS="${CFLAGS} -DX86_SSE2 -DX86_SSE2_CHUNKSET" - SFLAGS="${SFLAGS} -DX86_SSE2 -DX86_SSE2_CHUNKSET" + CFLAGS="${CFLAGS} -DX86_SSE2 -DX86_SSE2_CHUNKSET -DX86_SSE2_SLIDEHASH" + SFLAGS="${SFLAGS} -DX86_SSE2 -DX86_SSE2_CHUNKSET -DX86_SSE2_SLIDEHASH" ARCH_STATIC_OBJS="${ARCH_STATIC_OBJS} chunkset_sse.o slide_sse.o" ARCH_SHARED_OBJS="${ARCH_SHARED_OBJS} chunkset_sse.lo slide_sse.lo" @@ -1282,6 +1290,11 @@ case "${ARCH}" in ARCH_STATIC_OBJS="${ARCH_STATIC_OBJS} crc_folding.o" ARCH_SHARED_OBJS="${ARCH_SHARED_OBJS} crc_folding.lo" fi + + if test $forcetzcnt -eq 1; then + CFLAGS="${CFLAGS} -DX86_NOCHECK_TZCNT" + SFLAGS="${SFLAGS} -DX86_NOCHECK_TZCNT" + fi fi ;; @@ -1470,7 +1483,7 @@ EOF ;; # 64-bit ARM specific optimizations - aarch64) + aarch64 | arm64) [ ! -z $CROSS_PREFIX ] && QEMU_ARCH=aarch64 ARCHDIR=arch/arm diff --git a/internal-complibs/zlib-ng-2.0.6/crc32.c b/internal-complibs/zlib-ng-2.0.7/crc32.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/crc32.c rename to internal-complibs/zlib-ng-2.0.7/crc32.c diff --git a/internal-complibs/zlib-ng-2.0.6/crc32_comb.c b/internal-complibs/zlib-ng-2.0.7/crc32_comb.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/crc32_comb.c rename to internal-complibs/zlib-ng-2.0.7/crc32_comb.c diff --git a/internal-complibs/zlib-ng-2.0.6/crc32_comb_tbl.h b/internal-complibs/zlib-ng-2.0.7/crc32_comb_tbl._h similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/crc32_comb_tbl.h rename to internal-complibs/zlib-ng-2.0.7/crc32_comb_tbl._h diff --git a/internal-complibs/zlib-ng-2.0.7/crc32_comb_tbl.h b/internal-complibs/zlib-ng-2.0.7/crc32_comb_tbl.h new file mode 100644 index 00000000..43818c3e --- /dev/null +++ b/internal-complibs/zlib-ng-2.0.7/crc32_comb_tbl.h @@ -0,0 +1,300 @@ +#ifndef CRC32_COMB_TBL_H_ +#define CRC32_COMB_TBL_H_ + +/* crc32_comb_tbl.h -- zero operators table for CRC combine + * Generated automatically by makecrct.c + */ + +static const uint32_t crc_comb[32][32] = +{ + { + 0x77073096, 0xee0e612c, 0x076dc419, 0x0edb8832, 0x1db71064, + 0x3b6e20c8, 0x76dc4190, 0xedb88320, 0x00000001, 0x00000002, + 0x00000004, 0x00000008, 0x00000010, 0x00000020, 0x00000040, + 0x00000080, 0x00000100, 0x00000200, 0x00000400, 0x00000800, + 0x00001000, 0x00002000, 0x00004000, 0x00008000, 0x00010000, + 0x00020000, 0x00040000, 0x00080000, 0x00100000, 0x00200000, + 0x00400000, 0x00800000 + }, + { + 0x191b3141, 0x32366282, 0x646cc504, 0xc8d98a08, 0x4ac21251, + 0x958424a2, 0xf0794f05, 0x3b83984b, 0x77073096, 0xee0e612c, + 0x076dc419, 0x0edb8832, 0x1db71064, 0x3b6e20c8, 0x76dc4190, + 0xedb88320, 0x00000001, 0x00000002, 0x00000004, 0x00000008, + 0x00000010, 0x00000020, 0x00000040, 0x00000080, 0x00000100, + 0x00000200, 0x00000400, 0x00000800, 0x00001000, 0x00002000, + 0x00004000, 0x00008000 + }, + { + 0xb8bc6765, 0xaa09c88b, 0x8f629757, 0xc5b428ef, 0x5019579f, + 0xa032af3e, 0x9b14583d, 0xed59b63b, 0x01c26a37, 0x0384d46e, + 0x0709a8dc, 0x0e1351b8, 0x1c26a370, 0x384d46e0, 0x709a8dc0, + 0xe1351b80, 0x191b3141, 0x32366282, 0x646cc504, 0xc8d98a08, + 0x4ac21251, 0x958424a2, 0xf0794f05, 0x3b83984b, 0x77073096, + 0xee0e612c, 0x076dc419, 0x0edb8832, 0x1db71064, 0x3b6e20c8, + 0x76dc4190, 0xedb88320 + }, + { + 0xccaa009e, 0x4225077d, 0x844a0efa, 0xd3e51bb5, 0x7cbb312b, + 0xf9766256, 0x299dc2ed, 0x533b85da, 0xa6770bb4, 0x979f1129, + 0xf44f2413, 0x33ef4e67, 0x67de9cce, 0xcfbd399c, 0x440b7579, + 0x8816eaf2, 0xcb5cd3a5, 0x4dc8a10b, 0x9b914216, 0xec53826d, + 0x03d6029b, 0x07ac0536, 0x0f580a6c, 0x1eb014d8, 0x3d6029b0, + 0x7ac05360, 0xf580a6c0, 0x30704bc1, 0x60e09782, 0xc1c12f04, + 0x58f35849, 0xb1e6b092 + }, + { + 0xae689191, 0x87a02563, 0xd4314c87, 0x73139f4f, 0xe6273e9e, + 0x173f7b7d, 0x2e7ef6fa, 0x5cfdedf4, 0xb9fbdbe8, 0xa886b191, + 0x8a7c6563, 0xcf89cc87, 0x44629f4f, 0x88c53e9e, 0xcafb7b7d, + 0x4e87f0bb, 0x9d0fe176, 0xe16ec4ad, 0x19ac8f1b, 0x33591e36, + 0x66b23c6c, 0xcd6478d8, 0x41b9f7f1, 0x8373efe2, 0xdd96d985, + 0x605cb54b, 0xc0b96a96, 0x5a03d36d, 0xb407a6da, 0xb37e4bf5, + 0xbd8d91ab, 0xa06a2517 + }, + { + 0xf1da05aa, 0x38c50d15, 0x718a1a2a, 0xe3143454, 0x1d596ee9, + 0x3ab2ddd2, 0x7565bba4, 0xeacb7748, 0x0ee7e8d1, 0x1dcfd1a2, + 0x3b9fa344, 0x773f4688, 0xee7e8d10, 0x078c1c61, 0x0f1838c2, + 0x1e307184, 0x3c60e308, 0x78c1c610, 0xf1838c20, 0x38761e01, + 0x70ec3c02, 0xe1d87804, 0x18c1f649, 0x3183ec92, 0x6307d924, + 0xc60fb248, 0x576e62d1, 0xaedcc5a2, 0x86c88d05, 0xd6e01c4b, + 0x76b13ed7, 0xed627dae + }, + { + 0x8f352d95, 0xc51b5d6b, 0x5147bc97, 0xa28f792e, 0x9e6ff41d, + 0xe7aeee7b, 0x142cdab7, 0x2859b56e, 0x50b36adc, 0xa166d5b8, + 0x99bcad31, 0xe8085c23, 0x0b61be07, 0x16c37c0e, 0x2d86f81c, + 0x5b0df038, 0xb61be070, 0xb746c6a1, 0xb5fc8b03, 0xb0881047, + 0xba6126cf, 0xafb34bdf, 0x841791ff, 0xd35e25bf, 0x7dcd4d3f, + 0xfb9a9a7e, 0x2c4432bd, 0x5888657a, 0xb110caf4, 0xb95093a9, + 0xa9d02113, 0x88d14467 + }, + { + 0x33fff533, 0x67ffea66, 0xcfffd4cc, 0x448eafd9, 0x891d5fb2, + 0xc94bb925, 0x49e6740b, 0x93cce816, 0xfce8d66d, 0x22a0aa9b, + 0x45415536, 0x8a82aa6c, 0xce745299, 0x4799a373, 0x8f3346e6, + 0xc5178b8d, 0x515e115b, 0xa2bc22b6, 0x9e09432d, 0xe763801b, + 0x15b60677, 0x2b6c0cee, 0x56d819dc, 0xadb033b8, 0x80116131, + 0xdb53c423, 0x6dd68e07, 0xdbad1c0e, 0x6c2b3e5d, 0xd8567cba, + 0x6bddff35, 0xd7bbfe6a + }, + { + 0xce3371cb, 0x4717e5d7, 0x8e2fcbae, 0xc72e911d, 0x552c247b, + 0xaa5848f6, 0x8fc197ad, 0xc4f2291b, 0x52955477, 0xa52aa8ee, + 0x9124579d, 0xf939a97b, 0x290254b7, 0x5204a96e, 0xa40952dc, + 0x9363a3f9, 0xfdb641b3, 0x201d8527, 0x403b0a4e, 0x8076149c, + 0xdb9d2f79, 0x6c4b58b3, 0xd896b166, 0x6a5c648d, 0xd4b8c91a, + 0x72009475, 0xe40128ea, 0x13735795, 0x26e6af2a, 0x4dcd5e54, + 0x9b9abca8, 0xec447f11 + }, + { + 0x1072db28, 0x20e5b650, 0x41cb6ca0, 0x8396d940, 0xdc5cb4c1, + 0x63c86fc3, 0xc790df86, 0x5450b94d, 0xa8a1729a, 0x8a33e375, + 0xcf16c0ab, 0x455c8717, 0x8ab90e2e, 0xce031a1d, 0x4777327b, + 0x8eee64f6, 0xc6adcfad, 0x562a991b, 0xac553236, 0x83db622d, + 0xdcc7c21b, 0x62fe8277, 0xc5fd04ee, 0x508b0f9d, 0xa1161f3a, + 0x995d3835, 0xe9cb762b, 0x08e7ea17, 0x11cfd42e, 0x239fa85c, + 0x473f50b8, 0x8e7ea170 + }, + { + 0xf891f16f, 0x2a52e49f, 0x54a5c93e, 0xa94b927c, 0x89e622b9, + 0xc8bd4333, 0x4a0b8027, 0x9417004e, 0xf35f06dd, 0x3dcf0bfb, + 0x7b9e17f6, 0xf73c2fec, 0x35095999, 0x6a12b332, 0xd4256664, + 0x733bca89, 0xe6779512, 0x179e2c65, 0x2f3c58ca, 0x5e78b194, + 0xbcf16328, 0xa293c011, 0x9e568663, 0xe7dc0a87, 0x14c9134f, + 0x2992269e, 0x53244d3c, 0xa6489a78, 0x97e032b1, 0xf4b16323, + 0x3213c007, 0x6427800e + }, + { + 0x88b6ba63, 0xca1c7287, 0x4f49e34f, 0x9e93c69e, 0xe6568b7d, + 0x17dc10bb, 0x2fb82176, 0x5f7042ec, 0xbee085d8, 0xa6b00df1, + 0x96111da3, 0xf7533d07, 0x35d77c4f, 0x6baef89e, 0xd75df13c, + 0x75cae439, 0xeb95c872, 0x0c5a96a5, 0x18b52d4a, 0x316a5a94, + 0x62d4b528, 0xc5a96a50, 0x5023d2e1, 0xa047a5c2, 0x9bfe4dc5, + 0xec8d9dcb, 0x026a3dd7, 0x04d47bae, 0x09a8f75c, 0x1351eeb8, + 0x26a3dd70, 0x4d47bae0 + }, + { + 0x5ad8a92c, 0xb5b15258, 0xb013a2f1, 0xbb5643a3, 0xaddd8107, + 0x80ca044f, 0xdae50edf, 0x6ebb1bff, 0xdd7637fe, 0x619d69bd, + 0xc33ad37a, 0x5d04a0b5, 0xba09416a, 0xaf638495, 0x85b60f6b, + 0xd01d1897, 0x7b4b376f, 0xf6966ede, 0x365ddbfd, 0x6cbbb7fa, + 0xd9776ff4, 0x699fd9a9, 0xd33fb352, 0x7d0e60e5, 0xfa1cc1ca, + 0x2f4885d5, 0x5e910baa, 0xbd221754, 0xa13528e9, 0x991b5793, + 0xe947a967, 0x09fe548f + }, + { + 0xb566f6e2, 0xb1bceb85, 0xb808d14b, 0xab60a4d7, 0x8db04fef, + 0xc011999f, 0x5b52357f, 0xb6a46afe, 0xb639d3bd, 0xb702a13b, + 0xb5744437, 0xb1998e2f, 0xb8421a1f, 0xabf5327f, 0x8c9b62bf, + 0xc247c33f, 0x5ffe803f, 0xbffd007e, 0xa48b06bd, 0x92670b3b, + 0xffbf1037, 0x240f262f, 0x481e4c5e, 0x903c98bc, 0xfb083739, + 0x2d616833, 0x5ac2d066, 0xb585a0cc, 0xb07a47d9, 0xbb8589f3, + 0xac7a15a7, 0x83852d0f + }, + { + 0x9d9129bf, 0xe053553f, 0x1bd7ac3f, 0x37af587e, 0x6f5eb0fc, + 0xdebd61f8, 0x660bc5b1, 0xcc178b62, 0x435e1085, 0x86bc210a, + 0xd6094455, 0x77638eeb, 0xeec71dd6, 0x06ff3ded, 0x0dfe7bda, + 0x1bfcf7b4, 0x37f9ef68, 0x6ff3ded0, 0xdfe7bda0, 0x64be7d01, + 0xc97cfa02, 0x4988f245, 0x9311e48a, 0xfd52cf55, 0x21d498eb, + 0x43a931d6, 0x875263ac, 0xd5d5c119, 0x70da8473, 0xe1b508e6, + 0x181b178d, 0x30362f1a + }, + { + 0x2ee43a2c, 0x5dc87458, 0xbb90e8b0, 0xac50d721, 0x83d0a803, + 0xdcd05647, 0x62d1aacf, 0xc5a3559e, 0x5037ad7d, 0xa06f5afa, + 0x9bafb3b5, 0xec2e612b, 0x032dc417, 0x065b882e, 0x0cb7105c, + 0x196e20b8, 0x32dc4170, 0x65b882e0, 0xcb7105c0, 0x4d930dc1, + 0x9b261b82, 0xed3d3145, 0x010b64cb, 0x0216c996, 0x042d932c, + 0x085b2658, 0x10b64cb0, 0x216c9960, 0x42d932c0, 0x85b26580, + 0xd015cd41, 0x7b5a9cc3 + }, + { + 0x1b4511ee, 0x368a23dc, 0x6d1447b8, 0xda288f70, 0x6f2018a1, + 0xde403142, 0x67f164c5, 0xcfe2c98a, 0x44b49555, 0x89692aaa, + 0xc9a35315, 0x4837a06b, 0x906f40d6, 0xfbaf87ed, 0x2c2e099b, + 0x585c1336, 0xb0b8266c, 0xba014a99, 0xaf739373, 0x859620a7, + 0xd05d470f, 0x7bcb885f, 0xf79710be, 0x345f273d, 0x68be4e7a, + 0xd17c9cf4, 0x79883fa9, 0xf3107f52, 0x3d51f8e5, 0x7aa3f1ca, + 0xf547e394, 0x31fec169 + }, + { + 0xbce15202, 0xa2b3a245, 0x9e1642cb, 0xe75d83d7, 0x15ca01ef, + 0x2b9403de, 0x572807bc, 0xae500f78, 0x87d118b1, 0xd4d33723, + 0x72d76807, 0xe5aed00e, 0x102ca65d, 0x20594cba, 0x40b29974, + 0x816532e8, 0xd9bb6391, 0x6807c163, 0xd00f82c6, 0x7b6e03cd, + 0xf6dc079a, 0x36c90975, 0x6d9212ea, 0xdb2425d4, 0x6d394de9, + 0xda729bd2, 0x6f9431e5, 0xdf2863ca, 0x6521c1d5, 0xca4383aa, + 0x4ff60115, 0x9fec022a + }, + { + 0xff08e5ef, 0x2560cd9f, 0x4ac19b3e, 0x9583367c, 0xf0776ab9, + 0x3b9fd333, 0x773fa666, 0xee7f4ccc, 0x078f9fd9, 0x0f1f3fb2, + 0x1e3e7f64, 0x3c7cfec8, 0x78f9fd90, 0xf1f3fb20, 0x3896f001, + 0x712de002, 0xe25bc004, 0x1fc68649, 0x3f8d0c92, 0x7f1a1924, + 0xfe343248, 0x271962d1, 0x4e32c5a2, 0x9c658b44, 0xe3ba10c9, + 0x1c0527d3, 0x380a4fa6, 0x70149f4c, 0xe0293e98, 0x1b237b71, + 0x3646f6e2, 0x6c8dedc4 + }, + { + 0x6f76172e, 0xdeec2e5c, 0x66a95af9, 0xcd52b5f2, 0x41d46da5, + 0x83a8db4a, 0xdc20b0d5, 0x633067eb, 0xc660cfd6, 0x57b099ed, + 0xaf6133da, 0x85b361f5, 0xd017c5ab, 0x7b5e8d17, 0xf6bd1a2e, + 0x360b321d, 0x6c16643a, 0xd82cc874, 0x6b2896a9, 0xd6512d52, + 0x77d35ce5, 0xefa6b9ca, 0x043c75d5, 0x0878ebaa, 0x10f1d754, + 0x21e3aea8, 0x43c75d50, 0x878ebaa0, 0xd46c7301, 0x73a9e043, + 0xe753c086, 0x15d6874d + }, + { + 0x56f5cab9, 0xadeb9572, 0x80a62ca5, 0xda3d5f0b, 0x6f0bb857, + 0xde1770ae, 0x675fe71d, 0xcebfce3a, 0x460e9a35, 0x8c1d346a, + 0xc34b6e95, 0x5de7db6b, 0xbbcfb6d6, 0xacee6bed, 0x82add19b, + 0xde2aa577, 0x67244caf, 0xce48995e, 0x47e034fd, 0x8fc069fa, + 0xc4f1d5b5, 0x5292ad2b, 0xa5255a56, 0x913bb2ed, 0xf906639b, + 0x297dc177, 0x52fb82ee, 0xa5f705dc, 0x909f0df9, 0xfa4f1db3, + 0x2fef3d27, 0x5fde7a4e + }, + { + 0x385993ac, 0x70b32758, 0xe1664eb0, 0x19bd9b21, 0x337b3642, + 0x66f66c84, 0xcdecd908, 0x40a8b451, 0x815168a2, 0xd9d3d705, + 0x68d6a84b, 0xd1ad5096, 0x782ba76d, 0xf0574eda, 0x3bdf9bf5, + 0x77bf37ea, 0xef7e6fd4, 0x058dd9e9, 0x0b1bb3d2, 0x163767a4, + 0x2c6ecf48, 0x58dd9e90, 0xb1bb3d20, 0xb8077c01, 0xab7ffe43, + 0x8d8efac7, 0xc06cf3cf, 0x5ba8e1df, 0xb751c3be, 0xb5d2813d, + 0xb0d4043b, 0xbad90e37 + }, + { + 0xb4247b20, 0xb339f001, 0xbd02e643, 0xa174cac7, 0x999893cf, + 0xe84021df, 0x0bf145ff, 0x17e28bfe, 0x2fc517fc, 0x5f8a2ff8, + 0xbf145ff0, 0xa559b9a1, 0x91c27503, 0xf8f5ec47, 0x2a9adecf, + 0x5535bd9e, 0xaa6b7b3c, 0x8fa7f039, 0xc43ee633, 0x530cca27, + 0xa619944e, 0x97422edd, 0xf5f55bfb, 0x309bb1b7, 0x6137636e, + 0xc26ec6dc, 0x5fac8bf9, 0xbf5917f2, 0xa5c329a5, 0x90f7550b, + 0xfa9fac57, 0x2e4e5eef + }, + { + 0x695186a7, 0xd2a30d4e, 0x7e371cdd, 0xfc6e39ba, 0x23ad7535, + 0x475aea6a, 0x8eb5d4d4, 0xc61aafe9, 0x57445993, 0xae88b326, + 0x8660600d, 0xd7b1c65b, 0x74128af7, 0xe82515ee, 0x0b3b2d9d, + 0x16765b3a, 0x2cecb674, 0x59d96ce8, 0xb3b2d9d0, 0xbc14b5e1, + 0xa3586d83, 0x9dc1dd47, 0xe0f2bccf, 0x1a947fdf, 0x3528ffbe, + 0x6a51ff7c, 0xd4a3fef8, 0x7236fbb1, 0xe46df762, 0x13aae885, + 0x2755d10a, 0x4eaba214 + }, + { + 0x66bc001e, 0xcd78003c, 0x41810639, 0x83020c72, 0xdd751ea5, + 0x619b3b0b, 0xc3367616, 0x5d1dea6d, 0xba3bd4da, 0xaf06aff5, + 0x857c59ab, 0xd189b517, 0x78626c6f, 0xf0c4d8de, 0x3af8b7fd, + 0x75f16ffa, 0xebe2dff4, 0x0cb4b9a9, 0x19697352, 0x32d2e6a4, + 0x65a5cd48, 0xcb4b9a90, 0x4de63361, 0x9bcc66c2, 0xece9cbc5, + 0x02a291cb, 0x05452396, 0x0a8a472c, 0x15148e58, 0x2a291cb0, + 0x54523960, 0xa8a472c0 + }, + { + 0xb58b27b3, 0xb0674927, 0xbbbf940f, 0xac0e2e5f, 0x836d5aff, + 0xddabb3bf, 0x6026613f, 0xc04cc27e, 0x5be882bd, 0xb7d1057a, + 0xb4d30cb5, 0xb2d71f2b, 0xbedf3817, 0xa6cf766f, 0x96efea9f, + 0xf6aed37f, 0x362ca0bf, 0x6c59417e, 0xd8b282fc, 0x6a1403b9, + 0xd4280772, 0x732108a5, 0xe642114a, 0x17f524d5, 0x2fea49aa, + 0x5fd49354, 0xbfa926a8, 0xa4234b11, 0x93379063, 0xfd1e2687, + 0x214d4b4f, 0x429a969e + }, + { + 0xfe273162, 0x273f6485, 0x4e7ec90a, 0x9cfd9214, 0xe28a2269, + 0x1e654293, 0x3cca8526, 0x79950a4c, 0xf32a1498, 0x3d252f71, + 0x7a4a5ee2, 0xf494bdc4, 0x32587dc9, 0x64b0fb92, 0xc961f724, + 0x49b2e809, 0x9365d012, 0xfdbaa665, 0x20044a8b, 0x40089516, + 0x80112a2c, 0xdb535219, 0x6dd7a273, 0xdbaf44e6, 0x6c2f8f8d, + 0xd85f1f1a, 0x6bcf3875, 0xd79e70ea, 0x744de795, 0xe89bcf2a, + 0x0a469815, 0x148d302a + }, + { + 0xd3c98813, 0x7ce21667, 0xf9c42cce, 0x28f95fdd, 0x51f2bfba, + 0xa3e57f74, 0x9cbbf8a9, 0xe206f713, 0x1f7ce867, 0x3ef9d0ce, + 0x7df3a19c, 0xfbe74338, 0x2cbf8031, 0x597f0062, 0xb2fe00c4, + 0xbe8d07c9, 0xa66b09d3, 0x97a715e7, 0xf43f2d8f, 0x330f5d5f, + 0x661ebabe, 0xcc3d757c, 0x430becb9, 0x8617d972, 0xd75eb4a5, + 0x75cc6f0b, 0xeb98de16, 0x0c40ba6d, 0x188174da, 0x3102e9b4, + 0x6205d368, 0xc40ba6d0 + }, + { + 0xf7d6deb4, 0x34dcbb29, 0x69b97652, 0xd372eca4, 0x7d94df09, + 0xfb29be12, 0x2d227a65, 0x5a44f4ca, 0xb489e994, 0xb262d569, + 0xbfb4ac93, 0xa4185f67, 0x9341b88f, 0xfdf2775f, 0x2095e8ff, + 0x412bd1fe, 0x8257a3fc, 0xdfde41b9, 0x64cd8533, 0xc99b0a66, + 0x4847128d, 0x908e251a, 0xfa6d4c75, 0x2fab9eab, 0x5f573d56, + 0xbeae7aac, 0xa62df319, 0x972ae073, 0xf524c6a7, 0x31388b0f, + 0x6271161e, 0xc4e22c3c + }, + { + 0xedb88320, 0x00000001, 0x00000002, 0x00000004, 0x00000008, + 0x00000010, 0x00000020, 0x00000040, 0x00000080, 0x00000100, + 0x00000200, 0x00000400, 0x00000800, 0x00001000, 0x00002000, + 0x00004000, 0x00008000, 0x00010000, 0x00020000, 0x00040000, + 0x00080000, 0x00100000, 0x00200000, 0x00400000, 0x00800000, + 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, + 0x20000000, 0x40000000 + }, + { + 0x76dc4190, 0xedb88320, 0x00000001, 0x00000002, 0x00000004, + 0x00000008, 0x00000010, 0x00000020, 0x00000040, 0x00000080, + 0x00000100, 0x00000200, 0x00000400, 0x00000800, 0x00001000, + 0x00002000, 0x00004000, 0x00008000, 0x00010000, 0x00020000, + 0x00040000, 0x00080000, 0x00100000, 0x00200000, 0x00400000, + 0x00800000, 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000 + }, + { + 0x1db71064, 0x3b6e20c8, 0x76dc4190, 0xedb88320, 0x00000001, + 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000020, + 0x00000040, 0x00000080, 0x00000100, 0x00000200, 0x00000400, + 0x00000800, 0x00001000, 0x00002000, 0x00004000, 0x00008000, + 0x00010000, 0x00020000, 0x00040000, 0x00080000, 0x00100000, + 0x00200000, 0x00400000, 0x00800000, 0x01000000, 0x02000000, + 0x04000000, 0x08000000 + } +}; + +#endif /* CRC32_COMB_TBL_H_ */ diff --git a/internal-complibs/zlib-ng-2.0.6/crc32_p.h b/internal-complibs/zlib-ng-2.0.7/crc32_p.h similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/crc32_p.h rename to internal-complibs/zlib-ng-2.0.7/crc32_p.h diff --git a/internal-complibs/zlib-ng-2.0.6/crc32_tbl.h b/internal-complibs/zlib-ng-2.0.7/crc32_tbl._h similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/crc32_tbl.h rename to internal-complibs/zlib-ng-2.0.7/crc32_tbl._h diff --git a/internal-complibs/zlib-ng-2.0.7/crc32_tbl.h b/internal-complibs/zlib-ng-2.0.7/crc32_tbl.h new file mode 100644 index 00000000..ee2030c6 --- /dev/null +++ b/internal-complibs/zlib-ng-2.0.7/crc32_tbl.h @@ -0,0 +1,444 @@ +#ifndef CRC32_TBL_H_ +#define CRC32_TBL_H_ + +/* crc32_tbl.h -- tables for rapid CRC calculation + * Generated automatically by makecrct.c + */ + +static const uint32_t crc_table[8][256] = +{ + { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, + 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, + 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, + 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, + 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, + 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, + 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, + 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, + 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, + 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, + 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, + 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, + 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, + 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, + 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, + 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, + 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, + 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, + 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, + 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, + 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, + 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, + 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, + 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, + 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, + 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, + 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, + 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, + 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, + 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, + 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, + 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, + 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, + 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, + 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, + 0x2d02ef8d + }, + { + 0x00000000, 0x191b3141, 0x32366282, 0x2b2d53c3, 0x646cc504, + 0x7d77f445, 0x565aa786, 0x4f4196c7, 0xc8d98a08, 0xd1c2bb49, + 0xfaefe88a, 0xe3f4d9cb, 0xacb54f0c, 0xb5ae7e4d, 0x9e832d8e, + 0x87981ccf, 0x4ac21251, 0x53d92310, 0x78f470d3, 0x61ef4192, + 0x2eaed755, 0x37b5e614, 0x1c98b5d7, 0x05838496, 0x821b9859, + 0x9b00a918, 0xb02dfadb, 0xa936cb9a, 0xe6775d5d, 0xff6c6c1c, + 0xd4413fdf, 0xcd5a0e9e, 0x958424a2, 0x8c9f15e3, 0xa7b24620, + 0xbea97761, 0xf1e8e1a6, 0xe8f3d0e7, 0xc3de8324, 0xdac5b265, + 0x5d5daeaa, 0x44469feb, 0x6f6bcc28, 0x7670fd69, 0x39316bae, + 0x202a5aef, 0x0b07092c, 0x121c386d, 0xdf4636f3, 0xc65d07b2, + 0xed705471, 0xf46b6530, 0xbb2af3f7, 0xa231c2b6, 0x891c9175, + 0x9007a034, 0x179fbcfb, 0x0e848dba, 0x25a9de79, 0x3cb2ef38, + 0x73f379ff, 0x6ae848be, 0x41c51b7d, 0x58de2a3c, 0xf0794f05, + 0xe9627e44, 0xc24f2d87, 0xdb541cc6, 0x94158a01, 0x8d0ebb40, + 0xa623e883, 0xbf38d9c2, 0x38a0c50d, 0x21bbf44c, 0x0a96a78f, + 0x138d96ce, 0x5ccc0009, 0x45d73148, 0x6efa628b, 0x77e153ca, + 0xbabb5d54, 0xa3a06c15, 0x888d3fd6, 0x91960e97, 0xded79850, + 0xc7cca911, 0xece1fad2, 0xf5facb93, 0x7262d75c, 0x6b79e61d, + 0x4054b5de, 0x594f849f, 0x160e1258, 0x0f152319, 0x243870da, + 0x3d23419b, 0x65fd6ba7, 0x7ce65ae6, 0x57cb0925, 0x4ed03864, + 0x0191aea3, 0x188a9fe2, 0x33a7cc21, 0x2abcfd60, 0xad24e1af, + 0xb43fd0ee, 0x9f12832d, 0x8609b26c, 0xc94824ab, 0xd05315ea, + 0xfb7e4629, 0xe2657768, 0x2f3f79f6, 0x362448b7, 0x1d091b74, + 0x04122a35, 0x4b53bcf2, 0x52488db3, 0x7965de70, 0x607eef31, + 0xe7e6f3fe, 0xfefdc2bf, 0xd5d0917c, 0xcccba03d, 0x838a36fa, + 0x9a9107bb, 0xb1bc5478, 0xa8a76539, 0x3b83984b, 0x2298a90a, + 0x09b5fac9, 0x10aecb88, 0x5fef5d4f, 0x46f46c0e, 0x6dd93fcd, + 0x74c20e8c, 0xf35a1243, 0xea412302, 0xc16c70c1, 0xd8774180, + 0x9736d747, 0x8e2de606, 0xa500b5c5, 0xbc1b8484, 0x71418a1a, + 0x685abb5b, 0x4377e898, 0x5a6cd9d9, 0x152d4f1e, 0x0c367e5f, + 0x271b2d9c, 0x3e001cdd, 0xb9980012, 0xa0833153, 0x8bae6290, + 0x92b553d1, 0xddf4c516, 0xc4eff457, 0xefc2a794, 0xf6d996d5, + 0xae07bce9, 0xb71c8da8, 0x9c31de6b, 0x852aef2a, 0xca6b79ed, + 0xd37048ac, 0xf85d1b6f, 0xe1462a2e, 0x66de36e1, 0x7fc507a0, + 0x54e85463, 0x4df36522, 0x02b2f3e5, 0x1ba9c2a4, 0x30849167, + 0x299fa026, 0xe4c5aeb8, 0xfdde9ff9, 0xd6f3cc3a, 0xcfe8fd7b, + 0x80a96bbc, 0x99b25afd, 0xb29f093e, 0xab84387f, 0x2c1c24b0, + 0x350715f1, 0x1e2a4632, 0x07317773, 0x4870e1b4, 0x516bd0f5, + 0x7a468336, 0x635db277, 0xcbfad74e, 0xd2e1e60f, 0xf9ccb5cc, + 0xe0d7848d, 0xaf96124a, 0xb68d230b, 0x9da070c8, 0x84bb4189, + 0x03235d46, 0x1a386c07, 0x31153fc4, 0x280e0e85, 0x674f9842, + 0x7e54a903, 0x5579fac0, 0x4c62cb81, 0x8138c51f, 0x9823f45e, + 0xb30ea79d, 0xaa1596dc, 0xe554001b, 0xfc4f315a, 0xd7626299, + 0xce7953d8, 0x49e14f17, 0x50fa7e56, 0x7bd72d95, 0x62cc1cd4, + 0x2d8d8a13, 0x3496bb52, 0x1fbbe891, 0x06a0d9d0, 0x5e7ef3ec, + 0x4765c2ad, 0x6c48916e, 0x7553a02f, 0x3a1236e8, 0x230907a9, + 0x0824546a, 0x113f652b, 0x96a779e4, 0x8fbc48a5, 0xa4911b66, + 0xbd8a2a27, 0xf2cbbce0, 0xebd08da1, 0xc0fdde62, 0xd9e6ef23, + 0x14bce1bd, 0x0da7d0fc, 0x268a833f, 0x3f91b27e, 0x70d024b9, + 0x69cb15f8, 0x42e6463b, 0x5bfd777a, 0xdc656bb5, 0xc57e5af4, + 0xee530937, 0xf7483876, 0xb809aeb1, 0xa1129ff0, 0x8a3fcc33, + 0x9324fd72 + }, + { + 0x00000000, 0x01c26a37, 0x0384d46e, 0x0246be59, 0x0709a8dc, + 0x06cbc2eb, 0x048d7cb2, 0x054f1685, 0x0e1351b8, 0x0fd13b8f, + 0x0d9785d6, 0x0c55efe1, 0x091af964, 0x08d89353, 0x0a9e2d0a, + 0x0b5c473d, 0x1c26a370, 0x1de4c947, 0x1fa2771e, 0x1e601d29, + 0x1b2f0bac, 0x1aed619b, 0x18abdfc2, 0x1969b5f5, 0x1235f2c8, + 0x13f798ff, 0x11b126a6, 0x10734c91, 0x153c5a14, 0x14fe3023, + 0x16b88e7a, 0x177ae44d, 0x384d46e0, 0x398f2cd7, 0x3bc9928e, + 0x3a0bf8b9, 0x3f44ee3c, 0x3e86840b, 0x3cc03a52, 0x3d025065, + 0x365e1758, 0x379c7d6f, 0x35dac336, 0x3418a901, 0x3157bf84, + 0x3095d5b3, 0x32d36bea, 0x331101dd, 0x246be590, 0x25a98fa7, + 0x27ef31fe, 0x262d5bc9, 0x23624d4c, 0x22a0277b, 0x20e69922, + 0x2124f315, 0x2a78b428, 0x2bbade1f, 0x29fc6046, 0x283e0a71, + 0x2d711cf4, 0x2cb376c3, 0x2ef5c89a, 0x2f37a2ad, 0x709a8dc0, + 0x7158e7f7, 0x731e59ae, 0x72dc3399, 0x7793251c, 0x76514f2b, + 0x7417f172, 0x75d59b45, 0x7e89dc78, 0x7f4bb64f, 0x7d0d0816, + 0x7ccf6221, 0x798074a4, 0x78421e93, 0x7a04a0ca, 0x7bc6cafd, + 0x6cbc2eb0, 0x6d7e4487, 0x6f38fade, 0x6efa90e9, 0x6bb5866c, + 0x6a77ec5b, 0x68315202, 0x69f33835, 0x62af7f08, 0x636d153f, + 0x612bab66, 0x60e9c151, 0x65a6d7d4, 0x6464bde3, 0x662203ba, + 0x67e0698d, 0x48d7cb20, 0x4915a117, 0x4b531f4e, 0x4a917579, + 0x4fde63fc, 0x4e1c09cb, 0x4c5ab792, 0x4d98dda5, 0x46c49a98, + 0x4706f0af, 0x45404ef6, 0x448224c1, 0x41cd3244, 0x400f5873, + 0x4249e62a, 0x438b8c1d, 0x54f16850, 0x55330267, 0x5775bc3e, + 0x56b7d609, 0x53f8c08c, 0x523aaabb, 0x507c14e2, 0x51be7ed5, + 0x5ae239e8, 0x5b2053df, 0x5966ed86, 0x58a487b1, 0x5deb9134, + 0x5c29fb03, 0x5e6f455a, 0x5fad2f6d, 0xe1351b80, 0xe0f771b7, + 0xe2b1cfee, 0xe373a5d9, 0xe63cb35c, 0xe7fed96b, 0xe5b86732, + 0xe47a0d05, 0xef264a38, 0xeee4200f, 0xeca29e56, 0xed60f461, + 0xe82fe2e4, 0xe9ed88d3, 0xebab368a, 0xea695cbd, 0xfd13b8f0, + 0xfcd1d2c7, 0xfe976c9e, 0xff5506a9, 0xfa1a102c, 0xfbd87a1b, + 0xf99ec442, 0xf85cae75, 0xf300e948, 0xf2c2837f, 0xf0843d26, + 0xf1465711, 0xf4094194, 0xf5cb2ba3, 0xf78d95fa, 0xf64fffcd, + 0xd9785d60, 0xd8ba3757, 0xdafc890e, 0xdb3ee339, 0xde71f5bc, + 0xdfb39f8b, 0xddf521d2, 0xdc374be5, 0xd76b0cd8, 0xd6a966ef, + 0xd4efd8b6, 0xd52db281, 0xd062a404, 0xd1a0ce33, 0xd3e6706a, + 0xd2241a5d, 0xc55efe10, 0xc49c9427, 0xc6da2a7e, 0xc7184049, + 0xc25756cc, 0xc3953cfb, 0xc1d382a2, 0xc011e895, 0xcb4dafa8, + 0xca8fc59f, 0xc8c97bc6, 0xc90b11f1, 0xcc440774, 0xcd866d43, + 0xcfc0d31a, 0xce02b92d, 0x91af9640, 0x906dfc77, 0x922b422e, + 0x93e92819, 0x96a63e9c, 0x976454ab, 0x9522eaf2, 0x94e080c5, + 0x9fbcc7f8, 0x9e7eadcf, 0x9c381396, 0x9dfa79a1, 0x98b56f24, + 0x99770513, 0x9b31bb4a, 0x9af3d17d, 0x8d893530, 0x8c4b5f07, + 0x8e0de15e, 0x8fcf8b69, 0x8a809dec, 0x8b42f7db, 0x89044982, + 0x88c623b5, 0x839a6488, 0x82580ebf, 0x801eb0e6, 0x81dcdad1, + 0x8493cc54, 0x8551a663, 0x8717183a, 0x86d5720d, 0xa9e2d0a0, + 0xa820ba97, 0xaa6604ce, 0xaba46ef9, 0xaeeb787c, 0xaf29124b, + 0xad6fac12, 0xacadc625, 0xa7f18118, 0xa633eb2f, 0xa4755576, + 0xa5b73f41, 0xa0f829c4, 0xa13a43f3, 0xa37cfdaa, 0xa2be979d, + 0xb5c473d0, 0xb40619e7, 0xb640a7be, 0xb782cd89, 0xb2cddb0c, + 0xb30fb13b, 0xb1490f62, 0xb08b6555, 0xbbd72268, 0xba15485f, + 0xb853f606, 0xb9919c31, 0xbcde8ab4, 0xbd1ce083, 0xbf5a5eda, + 0xbe9834ed + }, + { + 0x00000000, 0xb8bc6765, 0xaa09c88b, 0x12b5afee, 0x8f629757, + 0x37def032, 0x256b5fdc, 0x9dd738b9, 0xc5b428ef, 0x7d084f8a, + 0x6fbde064, 0xd7018701, 0x4ad6bfb8, 0xf26ad8dd, 0xe0df7733, + 0x58631056, 0x5019579f, 0xe8a530fa, 0xfa109f14, 0x42acf871, + 0xdf7bc0c8, 0x67c7a7ad, 0x75720843, 0xcdce6f26, 0x95ad7f70, + 0x2d111815, 0x3fa4b7fb, 0x8718d09e, 0x1acfe827, 0xa2738f42, + 0xb0c620ac, 0x087a47c9, 0xa032af3e, 0x188ec85b, 0x0a3b67b5, + 0xb28700d0, 0x2f503869, 0x97ec5f0c, 0x8559f0e2, 0x3de59787, + 0x658687d1, 0xdd3ae0b4, 0xcf8f4f5a, 0x7733283f, 0xeae41086, + 0x525877e3, 0x40edd80d, 0xf851bf68, 0xf02bf8a1, 0x48979fc4, + 0x5a22302a, 0xe29e574f, 0x7f496ff6, 0xc7f50893, 0xd540a77d, + 0x6dfcc018, 0x359fd04e, 0x8d23b72b, 0x9f9618c5, 0x272a7fa0, + 0xbafd4719, 0x0241207c, 0x10f48f92, 0xa848e8f7, 0x9b14583d, + 0x23a83f58, 0x311d90b6, 0x89a1f7d3, 0x1476cf6a, 0xaccaa80f, + 0xbe7f07e1, 0x06c36084, 0x5ea070d2, 0xe61c17b7, 0xf4a9b859, + 0x4c15df3c, 0xd1c2e785, 0x697e80e0, 0x7bcb2f0e, 0xc377486b, + 0xcb0d0fa2, 0x73b168c7, 0x6104c729, 0xd9b8a04c, 0x446f98f5, + 0xfcd3ff90, 0xee66507e, 0x56da371b, 0x0eb9274d, 0xb6054028, + 0xa4b0efc6, 0x1c0c88a3, 0x81dbb01a, 0x3967d77f, 0x2bd27891, + 0x936e1ff4, 0x3b26f703, 0x839a9066, 0x912f3f88, 0x299358ed, + 0xb4446054, 0x0cf80731, 0x1e4da8df, 0xa6f1cfba, 0xfe92dfec, + 0x462eb889, 0x549b1767, 0xec277002, 0x71f048bb, 0xc94c2fde, + 0xdbf98030, 0x6345e755, 0x6b3fa09c, 0xd383c7f9, 0xc1366817, + 0x798a0f72, 0xe45d37cb, 0x5ce150ae, 0x4e54ff40, 0xf6e89825, + 0xae8b8873, 0x1637ef16, 0x048240f8, 0xbc3e279d, 0x21e91f24, + 0x99557841, 0x8be0d7af, 0x335cb0ca, 0xed59b63b, 0x55e5d15e, + 0x47507eb0, 0xffec19d5, 0x623b216c, 0xda874609, 0xc832e9e7, + 0x708e8e82, 0x28ed9ed4, 0x9051f9b1, 0x82e4565f, 0x3a58313a, + 0xa78f0983, 0x1f336ee6, 0x0d86c108, 0xb53aa66d, 0xbd40e1a4, + 0x05fc86c1, 0x1749292f, 0xaff54e4a, 0x322276f3, 0x8a9e1196, + 0x982bbe78, 0x2097d91d, 0x78f4c94b, 0xc048ae2e, 0xd2fd01c0, + 0x6a4166a5, 0xf7965e1c, 0x4f2a3979, 0x5d9f9697, 0xe523f1f2, + 0x4d6b1905, 0xf5d77e60, 0xe762d18e, 0x5fdeb6eb, 0xc2098e52, + 0x7ab5e937, 0x680046d9, 0xd0bc21bc, 0x88df31ea, 0x3063568f, + 0x22d6f961, 0x9a6a9e04, 0x07bda6bd, 0xbf01c1d8, 0xadb46e36, + 0x15080953, 0x1d724e9a, 0xa5ce29ff, 0xb77b8611, 0x0fc7e174, + 0x9210d9cd, 0x2aacbea8, 0x38191146, 0x80a57623, 0xd8c66675, + 0x607a0110, 0x72cfaefe, 0xca73c99b, 0x57a4f122, 0xef189647, + 0xfdad39a9, 0x45115ecc, 0x764dee06, 0xcef18963, 0xdc44268d, + 0x64f841e8, 0xf92f7951, 0x41931e34, 0x5326b1da, 0xeb9ad6bf, + 0xb3f9c6e9, 0x0b45a18c, 0x19f00e62, 0xa14c6907, 0x3c9b51be, + 0x842736db, 0x96929935, 0x2e2efe50, 0x2654b999, 0x9ee8defc, + 0x8c5d7112, 0x34e11677, 0xa9362ece, 0x118a49ab, 0x033fe645, + 0xbb838120, 0xe3e09176, 0x5b5cf613, 0x49e959fd, 0xf1553e98, + 0x6c820621, 0xd43e6144, 0xc68bceaa, 0x7e37a9cf, 0xd67f4138, + 0x6ec3265d, 0x7c7689b3, 0xc4caeed6, 0x591dd66f, 0xe1a1b10a, + 0xf3141ee4, 0x4ba87981, 0x13cb69d7, 0xab770eb2, 0xb9c2a15c, + 0x017ec639, 0x9ca9fe80, 0x241599e5, 0x36a0360b, 0x8e1c516e, + 0x866616a7, 0x3eda71c2, 0x2c6fde2c, 0x94d3b949, 0x090481f0, + 0xb1b8e695, 0xa30d497b, 0x1bb12e1e, 0x43d23e48, 0xfb6e592d, + 0xe9dbf6c3, 0x516791a6, 0xccb0a91f, 0x740cce7a, 0x66b96194, + 0xde0506f1 + }, + { + 0x00000000, 0x96300777, 0x2c610eee, 0xba510999, 0x19c46d07, + 0x8ff46a70, 0x35a563e9, 0xa395649e, 0x3288db0e, 0xa4b8dc79, + 0x1ee9d5e0, 0x88d9d297, 0x2b4cb609, 0xbd7cb17e, 0x072db8e7, + 0x911dbf90, 0x6410b71d, 0xf220b06a, 0x4871b9f3, 0xde41be84, + 0x7dd4da1a, 0xebe4dd6d, 0x51b5d4f4, 0xc785d383, 0x56986c13, + 0xc0a86b64, 0x7af962fd, 0xecc9658a, 0x4f5c0114, 0xd96c0663, + 0x633d0ffa, 0xf50d088d, 0xc8206e3b, 0x5e10694c, 0xe44160d5, + 0x727167a2, 0xd1e4033c, 0x47d4044b, 0xfd850dd2, 0x6bb50aa5, + 0xfaa8b535, 0x6c98b242, 0xd6c9bbdb, 0x40f9bcac, 0xe36cd832, + 0x755cdf45, 0xcf0dd6dc, 0x593dd1ab, 0xac30d926, 0x3a00de51, + 0x8051d7c8, 0x1661d0bf, 0xb5f4b421, 0x23c4b356, 0x9995bacf, + 0x0fa5bdb8, 0x9eb80228, 0x0888055f, 0xb2d90cc6, 0x24e90bb1, + 0x877c6f2f, 0x114c6858, 0xab1d61c1, 0x3d2d66b6, 0x9041dc76, + 0x0671db01, 0xbc20d298, 0x2a10d5ef, 0x8985b171, 0x1fb5b606, + 0xa5e4bf9f, 0x33d4b8e8, 0xa2c90778, 0x34f9000f, 0x8ea80996, + 0x18980ee1, 0xbb0d6a7f, 0x2d3d6d08, 0x976c6491, 0x015c63e6, + 0xf4516b6b, 0x62616c1c, 0xd8306585, 0x4e0062f2, 0xed95066c, + 0x7ba5011b, 0xc1f40882, 0x57c40ff5, 0xc6d9b065, 0x50e9b712, + 0xeab8be8b, 0x7c88b9fc, 0xdf1ddd62, 0x492dda15, 0xf37cd38c, + 0x654cd4fb, 0x5861b24d, 0xce51b53a, 0x7400bca3, 0xe230bbd4, + 0x41a5df4a, 0xd795d83d, 0x6dc4d1a4, 0xfbf4d6d3, 0x6ae96943, + 0xfcd96e34, 0x468867ad, 0xd0b860da, 0x732d0444, 0xe51d0333, + 0x5f4c0aaa, 0xc97c0ddd, 0x3c710550, 0xaa410227, 0x10100bbe, + 0x86200cc9, 0x25b56857, 0xb3856f20, 0x09d466b9, 0x9fe461ce, + 0x0ef9de5e, 0x98c9d929, 0x2298d0b0, 0xb4a8d7c7, 0x173db359, + 0x810db42e, 0x3b5cbdb7, 0xad6cbac0, 0x2083b8ed, 0xb6b3bf9a, + 0x0ce2b603, 0x9ad2b174, 0x3947d5ea, 0xaf77d29d, 0x1526db04, + 0x8316dc73, 0x120b63e3, 0x843b6494, 0x3e6a6d0d, 0xa85a6a7a, + 0x0bcf0ee4, 0x9dff0993, 0x27ae000a, 0xb19e077d, 0x44930ff0, + 0xd2a30887, 0x68f2011e, 0xfec20669, 0x5d5762f7, 0xcb676580, + 0x71366c19, 0xe7066b6e, 0x761bd4fe, 0xe02bd389, 0x5a7ada10, + 0xcc4add67, 0x6fdfb9f9, 0xf9efbe8e, 0x43beb717, 0xd58eb060, + 0xe8a3d6d6, 0x7e93d1a1, 0xc4c2d838, 0x52f2df4f, 0xf167bbd1, + 0x6757bca6, 0xdd06b53f, 0x4b36b248, 0xda2b0dd8, 0x4c1b0aaf, + 0xf64a0336, 0x607a0441, 0xc3ef60df, 0x55df67a8, 0xef8e6e31, + 0x79be6946, 0x8cb361cb, 0x1a8366bc, 0xa0d26f25, 0x36e26852, + 0x95770ccc, 0x03470bbb, 0xb9160222, 0x2f260555, 0xbe3bbac5, + 0x280bbdb2, 0x925ab42b, 0x046ab35c, 0xa7ffd7c2, 0x31cfd0b5, + 0x8b9ed92c, 0x1daede5b, 0xb0c2649b, 0x26f263ec, 0x9ca36a75, + 0x0a936d02, 0xa906099c, 0x3f360eeb, 0x85670772, 0x13570005, + 0x824abf95, 0x147ab8e2, 0xae2bb17b, 0x381bb60c, 0x9b8ed292, + 0x0dbed5e5, 0xb7efdc7c, 0x21dfdb0b, 0xd4d2d386, 0x42e2d4f1, + 0xf8b3dd68, 0x6e83da1f, 0xcd16be81, 0x5b26b9f6, 0xe177b06f, + 0x7747b718, 0xe65a0888, 0x706a0fff, 0xca3b0666, 0x5c0b0111, + 0xff9e658f, 0x69ae62f8, 0xd3ff6b61, 0x45cf6c16, 0x78e20aa0, + 0xeed20dd7, 0x5483044e, 0xc2b30339, 0x612667a7, 0xf71660d0, + 0x4d476949, 0xdb776e3e, 0x4a6ad1ae, 0xdc5ad6d9, 0x660bdf40, + 0xf03bd837, 0x53aebca9, 0xc59ebbde, 0x7fcfb247, 0xe9ffb530, + 0x1cf2bdbd, 0x8ac2baca, 0x3093b353, 0xa6a3b424, 0x0536d0ba, + 0x9306d7cd, 0x2957de54, 0xbf67d923, 0x2e7a66b3, 0xb84a61c4, + 0x021b685d, 0x942b6f2a, 0x37be0bb4, 0xa18e0cc3, 0x1bdf055a, + 0x8def022d + }, + { + 0x00000000, 0x41311b19, 0x82623632, 0xc3532d2b, 0x04c56c64, + 0x45f4777d, 0x86a75a56, 0xc796414f, 0x088ad9c8, 0x49bbc2d1, + 0x8ae8effa, 0xcbd9f4e3, 0x0c4fb5ac, 0x4d7eaeb5, 0x8e2d839e, + 0xcf1c9887, 0x5112c24a, 0x1023d953, 0xd370f478, 0x9241ef61, + 0x55d7ae2e, 0x14e6b537, 0xd7b5981c, 0x96848305, 0x59981b82, + 0x18a9009b, 0xdbfa2db0, 0x9acb36a9, 0x5d5d77e6, 0x1c6c6cff, + 0xdf3f41d4, 0x9e0e5acd, 0xa2248495, 0xe3159f8c, 0x2046b2a7, + 0x6177a9be, 0xa6e1e8f1, 0xe7d0f3e8, 0x2483dec3, 0x65b2c5da, + 0xaaae5d5d, 0xeb9f4644, 0x28cc6b6f, 0x69fd7076, 0xae6b3139, + 0xef5a2a20, 0x2c09070b, 0x6d381c12, 0xf33646df, 0xb2075dc6, + 0x715470ed, 0x30656bf4, 0xf7f32abb, 0xb6c231a2, 0x75911c89, + 0x34a00790, 0xfbbc9f17, 0xba8d840e, 0x79dea925, 0x38efb23c, + 0xff79f373, 0xbe48e86a, 0x7d1bc541, 0x3c2ade58, 0x054f79f0, + 0x447e62e9, 0x872d4fc2, 0xc61c54db, 0x018a1594, 0x40bb0e8d, + 0x83e823a6, 0xc2d938bf, 0x0dc5a038, 0x4cf4bb21, 0x8fa7960a, + 0xce968d13, 0x0900cc5c, 0x4831d745, 0x8b62fa6e, 0xca53e177, + 0x545dbbba, 0x156ca0a3, 0xd63f8d88, 0x970e9691, 0x5098d7de, + 0x11a9ccc7, 0xd2fae1ec, 0x93cbfaf5, 0x5cd76272, 0x1de6796b, + 0xdeb55440, 0x9f844f59, 0x58120e16, 0x1923150f, 0xda703824, + 0x9b41233d, 0xa76bfd65, 0xe65ae67c, 0x2509cb57, 0x6438d04e, + 0xa3ae9101, 0xe29f8a18, 0x21cca733, 0x60fdbc2a, 0xafe124ad, + 0xeed03fb4, 0x2d83129f, 0x6cb20986, 0xab2448c9, 0xea1553d0, + 0x29467efb, 0x687765e2, 0xf6793f2f, 0xb7482436, 0x741b091d, + 0x352a1204, 0xf2bc534b, 0xb38d4852, 0x70de6579, 0x31ef7e60, + 0xfef3e6e7, 0xbfc2fdfe, 0x7c91d0d5, 0x3da0cbcc, 0xfa368a83, + 0xbb07919a, 0x7854bcb1, 0x3965a7a8, 0x4b98833b, 0x0aa99822, + 0xc9fab509, 0x88cbae10, 0x4f5def5f, 0x0e6cf446, 0xcd3fd96d, + 0x8c0ec274, 0x43125af3, 0x022341ea, 0xc1706cc1, 0x804177d8, + 0x47d73697, 0x06e62d8e, 0xc5b500a5, 0x84841bbc, 0x1a8a4171, + 0x5bbb5a68, 0x98e87743, 0xd9d96c5a, 0x1e4f2d15, 0x5f7e360c, + 0x9c2d1b27, 0xdd1c003e, 0x120098b9, 0x533183a0, 0x9062ae8b, + 0xd153b592, 0x16c5f4dd, 0x57f4efc4, 0x94a7c2ef, 0xd596d9f6, + 0xe9bc07ae, 0xa88d1cb7, 0x6bde319c, 0x2aef2a85, 0xed796bca, + 0xac4870d3, 0x6f1b5df8, 0x2e2a46e1, 0xe136de66, 0xa007c57f, + 0x6354e854, 0x2265f34d, 0xe5f3b202, 0xa4c2a91b, 0x67918430, + 0x26a09f29, 0xb8aec5e4, 0xf99fdefd, 0x3accf3d6, 0x7bfde8cf, + 0xbc6ba980, 0xfd5ab299, 0x3e099fb2, 0x7f3884ab, 0xb0241c2c, + 0xf1150735, 0x32462a1e, 0x73773107, 0xb4e17048, 0xf5d06b51, + 0x3683467a, 0x77b25d63, 0x4ed7facb, 0x0fe6e1d2, 0xccb5ccf9, + 0x8d84d7e0, 0x4a1296af, 0x0b238db6, 0xc870a09d, 0x8941bb84, + 0x465d2303, 0x076c381a, 0xc43f1531, 0x850e0e28, 0x42984f67, + 0x03a9547e, 0xc0fa7955, 0x81cb624c, 0x1fc53881, 0x5ef42398, + 0x9da70eb3, 0xdc9615aa, 0x1b0054e5, 0x5a314ffc, 0x996262d7, + 0xd85379ce, 0x174fe149, 0x567efa50, 0x952dd77b, 0xd41ccc62, + 0x138a8d2d, 0x52bb9634, 0x91e8bb1f, 0xd0d9a006, 0xecf37e5e, + 0xadc26547, 0x6e91486c, 0x2fa05375, 0xe836123a, 0xa9070923, + 0x6a542408, 0x2b653f11, 0xe479a796, 0xa548bc8f, 0x661b91a4, + 0x272a8abd, 0xe0bccbf2, 0xa18dd0eb, 0x62defdc0, 0x23efe6d9, + 0xbde1bc14, 0xfcd0a70d, 0x3f838a26, 0x7eb2913f, 0xb924d070, + 0xf815cb69, 0x3b46e642, 0x7a77fd5b, 0xb56b65dc, 0xf45a7ec5, + 0x370953ee, 0x763848f7, 0xb1ae09b8, 0xf09f12a1, 0x33cc3f8a, + 0x72fd2493 + }, + { + 0x00000000, 0x376ac201, 0x6ed48403, 0x59be4602, 0xdca80907, + 0xebc2cb06, 0xb27c8d04, 0x85164f05, 0xb851130e, 0x8f3bd10f, + 0xd685970d, 0xe1ef550c, 0x64f91a09, 0x5393d808, 0x0a2d9e0a, + 0x3d475c0b, 0x70a3261c, 0x47c9e41d, 0x1e77a21f, 0x291d601e, + 0xac0b2f1b, 0x9b61ed1a, 0xc2dfab18, 0xf5b56919, 0xc8f23512, + 0xff98f713, 0xa626b111, 0x914c7310, 0x145a3c15, 0x2330fe14, + 0x7a8eb816, 0x4de47a17, 0xe0464d38, 0xd72c8f39, 0x8e92c93b, + 0xb9f80b3a, 0x3cee443f, 0x0b84863e, 0x523ac03c, 0x6550023d, + 0x58175e36, 0x6f7d9c37, 0x36c3da35, 0x01a91834, 0x84bf5731, + 0xb3d59530, 0xea6bd332, 0xdd011133, 0x90e56b24, 0xa78fa925, + 0xfe31ef27, 0xc95b2d26, 0x4c4d6223, 0x7b27a022, 0x2299e620, + 0x15f32421, 0x28b4782a, 0x1fdeba2b, 0x4660fc29, 0x710a3e28, + 0xf41c712d, 0xc376b32c, 0x9ac8f52e, 0xada2372f, 0xc08d9a70, + 0xf7e75871, 0xae591e73, 0x9933dc72, 0x1c259377, 0x2b4f5176, + 0x72f11774, 0x459bd575, 0x78dc897e, 0x4fb64b7f, 0x16080d7d, + 0x2162cf7c, 0xa4748079, 0x931e4278, 0xcaa0047a, 0xfdcac67b, + 0xb02ebc6c, 0x87447e6d, 0xdefa386f, 0xe990fa6e, 0x6c86b56b, + 0x5bec776a, 0x02523168, 0x3538f369, 0x087faf62, 0x3f156d63, + 0x66ab2b61, 0x51c1e960, 0xd4d7a665, 0xe3bd6464, 0xba032266, + 0x8d69e067, 0x20cbd748, 0x17a11549, 0x4e1f534b, 0x7975914a, + 0xfc63de4f, 0xcb091c4e, 0x92b75a4c, 0xa5dd984d, 0x989ac446, + 0xaff00647, 0xf64e4045, 0xc1248244, 0x4432cd41, 0x73580f40, + 0x2ae64942, 0x1d8c8b43, 0x5068f154, 0x67023355, 0x3ebc7557, + 0x09d6b756, 0x8cc0f853, 0xbbaa3a52, 0xe2147c50, 0xd57ebe51, + 0xe839e25a, 0xdf53205b, 0x86ed6659, 0xb187a458, 0x3491eb5d, + 0x03fb295c, 0x5a456f5e, 0x6d2fad5f, 0x801b35e1, 0xb771f7e0, + 0xeecfb1e2, 0xd9a573e3, 0x5cb33ce6, 0x6bd9fee7, 0x3267b8e5, + 0x050d7ae4, 0x384a26ef, 0x0f20e4ee, 0x569ea2ec, 0x61f460ed, + 0xe4e22fe8, 0xd388ede9, 0x8a36abeb, 0xbd5c69ea, 0xf0b813fd, + 0xc7d2d1fc, 0x9e6c97fe, 0xa90655ff, 0x2c101afa, 0x1b7ad8fb, + 0x42c49ef9, 0x75ae5cf8, 0x48e900f3, 0x7f83c2f2, 0x263d84f0, + 0x115746f1, 0x944109f4, 0xa32bcbf5, 0xfa958df7, 0xcdff4ff6, + 0x605d78d9, 0x5737bad8, 0x0e89fcda, 0x39e33edb, 0xbcf571de, + 0x8b9fb3df, 0xd221f5dd, 0xe54b37dc, 0xd80c6bd7, 0xef66a9d6, + 0xb6d8efd4, 0x81b22dd5, 0x04a462d0, 0x33cea0d1, 0x6a70e6d3, + 0x5d1a24d2, 0x10fe5ec5, 0x27949cc4, 0x7e2adac6, 0x494018c7, + 0xcc5657c2, 0xfb3c95c3, 0xa282d3c1, 0x95e811c0, 0xa8af4dcb, + 0x9fc58fca, 0xc67bc9c8, 0xf1110bc9, 0x740744cc, 0x436d86cd, + 0x1ad3c0cf, 0x2db902ce, 0x4096af91, 0x77fc6d90, 0x2e422b92, + 0x1928e993, 0x9c3ea696, 0xab546497, 0xf2ea2295, 0xc580e094, + 0xf8c7bc9f, 0xcfad7e9e, 0x9613389c, 0xa179fa9d, 0x246fb598, + 0x13057799, 0x4abb319b, 0x7dd1f39a, 0x3035898d, 0x075f4b8c, + 0x5ee10d8e, 0x698bcf8f, 0xec9d808a, 0xdbf7428b, 0x82490489, + 0xb523c688, 0x88649a83, 0xbf0e5882, 0xe6b01e80, 0xd1dadc81, + 0x54cc9384, 0x63a65185, 0x3a181787, 0x0d72d586, 0xa0d0e2a9, + 0x97ba20a8, 0xce0466aa, 0xf96ea4ab, 0x7c78ebae, 0x4b1229af, + 0x12ac6fad, 0x25c6adac, 0x1881f1a7, 0x2feb33a6, 0x765575a4, + 0x413fb7a5, 0xc429f8a0, 0xf3433aa1, 0xaafd7ca3, 0x9d97bea2, + 0xd073c4b5, 0xe71906b4, 0xbea740b6, 0x89cd82b7, 0x0cdbcdb2, + 0x3bb10fb3, 0x620f49b1, 0x55658bb0, 0x6822d7bb, 0x5f4815ba, + 0x06f653b8, 0x319c91b9, 0xb48adebc, 0x83e01cbd, 0xda5e5abf, + 0xed3498be + }, + { + 0x00000000, 0x6567bcb8, 0x8bc809aa, 0xeeafb512, 0x5797628f, + 0x32f0de37, 0xdc5f6b25, 0xb938d79d, 0xef28b4c5, 0x8a4f087d, + 0x64e0bd6f, 0x018701d7, 0xb8bfd64a, 0xddd86af2, 0x3377dfe0, + 0x56106358, 0x9f571950, 0xfa30a5e8, 0x149f10fa, 0x71f8ac42, + 0xc8c07bdf, 0xada7c767, 0x43087275, 0x266fcecd, 0x707fad95, + 0x1518112d, 0xfbb7a43f, 0x9ed01887, 0x27e8cf1a, 0x428f73a2, + 0xac20c6b0, 0xc9477a08, 0x3eaf32a0, 0x5bc88e18, 0xb5673b0a, + 0xd00087b2, 0x6938502f, 0x0c5fec97, 0xe2f05985, 0x8797e53d, + 0xd1878665, 0xb4e03add, 0x5a4f8fcf, 0x3f283377, 0x8610e4ea, + 0xe3775852, 0x0dd8ed40, 0x68bf51f8, 0xa1f82bf0, 0xc49f9748, + 0x2a30225a, 0x4f579ee2, 0xf66f497f, 0x9308f5c7, 0x7da740d5, + 0x18c0fc6d, 0x4ed09f35, 0x2bb7238d, 0xc518969f, 0xa07f2a27, + 0x1947fdba, 0x7c204102, 0x928ff410, 0xf7e848a8, 0x3d58149b, + 0x583fa823, 0xb6901d31, 0xd3f7a189, 0x6acf7614, 0x0fa8caac, + 0xe1077fbe, 0x8460c306, 0xd270a05e, 0xb7171ce6, 0x59b8a9f4, + 0x3cdf154c, 0x85e7c2d1, 0xe0807e69, 0x0e2fcb7b, 0x6b4877c3, + 0xa20f0dcb, 0xc768b173, 0x29c70461, 0x4ca0b8d9, 0xf5986f44, + 0x90ffd3fc, 0x7e5066ee, 0x1b37da56, 0x4d27b90e, 0x284005b6, + 0xc6efb0a4, 0xa3880c1c, 0x1ab0db81, 0x7fd76739, 0x9178d22b, + 0xf41f6e93, 0x03f7263b, 0x66909a83, 0x883f2f91, 0xed589329, + 0x546044b4, 0x3107f80c, 0xdfa84d1e, 0xbacff1a6, 0xecdf92fe, + 0x89b82e46, 0x67179b54, 0x027027ec, 0xbb48f071, 0xde2f4cc9, + 0x3080f9db, 0x55e74563, 0x9ca03f6b, 0xf9c783d3, 0x176836c1, + 0x720f8a79, 0xcb375de4, 0xae50e15c, 0x40ff544e, 0x2598e8f6, + 0x73888bae, 0x16ef3716, 0xf8408204, 0x9d273ebc, 0x241fe921, + 0x41785599, 0xafd7e08b, 0xcab05c33, 0x3bb659ed, 0x5ed1e555, + 0xb07e5047, 0xd519ecff, 0x6c213b62, 0x094687da, 0xe7e932c8, + 0x828e8e70, 0xd49eed28, 0xb1f95190, 0x5f56e482, 0x3a31583a, + 0x83098fa7, 0xe66e331f, 0x08c1860d, 0x6da63ab5, 0xa4e140bd, + 0xc186fc05, 0x2f294917, 0x4a4ef5af, 0xf3762232, 0x96119e8a, + 0x78be2b98, 0x1dd99720, 0x4bc9f478, 0x2eae48c0, 0xc001fdd2, + 0xa566416a, 0x1c5e96f7, 0x79392a4f, 0x97969f5d, 0xf2f123e5, + 0x05196b4d, 0x607ed7f5, 0x8ed162e7, 0xebb6de5f, 0x528e09c2, + 0x37e9b57a, 0xd9460068, 0xbc21bcd0, 0xea31df88, 0x8f566330, + 0x61f9d622, 0x049e6a9a, 0xbda6bd07, 0xd8c101bf, 0x366eb4ad, + 0x53090815, 0x9a4e721d, 0xff29cea5, 0x11867bb7, 0x74e1c70f, + 0xcdd91092, 0xa8beac2a, 0x46111938, 0x2376a580, 0x7566c6d8, + 0x10017a60, 0xfeaecf72, 0x9bc973ca, 0x22f1a457, 0x479618ef, + 0xa939adfd, 0xcc5e1145, 0x06ee4d76, 0x6389f1ce, 0x8d2644dc, + 0xe841f864, 0x51792ff9, 0x341e9341, 0xdab12653, 0xbfd69aeb, + 0xe9c6f9b3, 0x8ca1450b, 0x620ef019, 0x07694ca1, 0xbe519b3c, + 0xdb362784, 0x35999296, 0x50fe2e2e, 0x99b95426, 0xfcdee89e, + 0x12715d8c, 0x7716e134, 0xce2e36a9, 0xab498a11, 0x45e63f03, + 0x208183bb, 0x7691e0e3, 0x13f65c5b, 0xfd59e949, 0x983e55f1, + 0x2106826c, 0x44613ed4, 0xaace8bc6, 0xcfa9377e, 0x38417fd6, + 0x5d26c36e, 0xb389767c, 0xd6eecac4, 0x6fd61d59, 0x0ab1a1e1, + 0xe41e14f3, 0x8179a84b, 0xd769cb13, 0xb20e77ab, 0x5ca1c2b9, + 0x39c67e01, 0x80fea99c, 0xe5991524, 0x0b36a036, 0x6e511c8e, + 0xa7166686, 0xc271da3e, 0x2cde6f2c, 0x49b9d394, 0xf0810409, + 0x95e6b8b1, 0x7b490da3, 0x1e2eb11b, 0x483ed243, 0x2d596efb, + 0xc3f6dbe9, 0xa6916751, 0x1fa9b0cc, 0x7ace0c74, 0x9461b966, + 0xf10605de + } +}; + +#endif /* CRC32_TBL_H_ */ diff --git a/internal-complibs/zlib-ng-2.0.6/deflate.c b/internal-complibs/zlib-ng-2.0.7/deflate.c similarity index 99% rename from internal-complibs/zlib-ng-2.0.6/deflate.c rename to internal-complibs/zlib-ng-2.0.7/deflate.c index 031a1bb0..d01ca44a 100644 --- a/internal-complibs/zlib-ng-2.0.6/deflate.c +++ b/internal-complibs/zlib-ng-2.0.7/deflate.c @@ -52,7 +52,7 @@ #include "deflate_p.h" #include "functable.h" -const char PREFIX(deflate_copyright)[] = " deflate 1.2.11.f Copyright 1995-2016 Jean-loup Gailly and Mark Adler "; +const char PREFIX(deflate_copyright)[] = " deflate 1.2.11 Copyright 1995-2022 Jean-loup Gailly and Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot @@ -284,6 +284,8 @@ int32_t Z_EXPORT PREFIX(deflateInit2_)(PREFIX3(stream) *strm, int32_t level, int if (windowBits < 0) { /* suppress zlib wrapper */ wrap = 0; + if (windowBits < -15) + return Z_STREAM_ERROR; windowBits = -windowBits; #ifdef GZIP } else if (windowBits > 15) { @@ -299,11 +301,6 @@ int32_t Z_EXPORT PREFIX(deflateInit2_)(PREFIX3(stream) *strm, int32_t level, int if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ -#if !defined(NO_QUICK_STRATEGY) && !defined(S390_DFLTCC_DEFLATE) - if (level == 1) - windowBits = 13; -#endif - s = (deflate_state *) ZALLOC_STATE(strm, 1, sizeof(deflate_state)); if (s == NULL) return Z_MEM_ERROR; @@ -404,11 +401,11 @@ static int deflateStateCheck (PREFIX3(stream) *strm) { if (s == NULL || s->strm != strm || (s->status != INIT_STATE && #ifdef GZIP s->status != GZIP_STATE && -#endif s->status != EXTRA_STATE && s->status != NAME_STATE && s->status != COMMENT_STATE && s->status != HCRC_STATE && +#endif s->status != BUSY_STATE && s->status != FINISH_STATE)) return 1; @@ -718,11 +715,20 @@ unsigned long Z_EXPORT PREFIX(deflateBound)(PREFIX3(stream) *strm, unsigned long /* if not default parameters, return conservative bound */ if (DEFLATE_NEED_CONSERVATIVE_BOUND(strm) || /* hook for IBM Z DFLTCC */ - s->w_bits != 15 || HASH_BITS < 15) + s->w_bits != 15 || HASH_BITS < 15) { + if (s->level == 0) { + /* upper bound for stored blocks with length 127 (memLevel == 1) -- + ~4% overhead plus a small constant */ + complen = sourceLen + (sourceLen >> 5) + (sourceLen >> 7) + (sourceLen >> 11) + 7; + } + return complen + wraplen; + } #ifndef NO_QUICK_STRATEGY return sourceLen /* The source size itself */ + + (sourceLen == 0 ? 1 : 0) /* Always at least one byte for any input */ + + (sourceLen < 9 ? 1 : 0) /* One extra byte for lengths less than 9 */ + DEFLATE_QUICK_OVERHEAD(sourceLen) /* Source encoding overhead, padded to next full byte */ + DEFLATE_BLOCK_OVERHEAD /* Deflate block overhead bytes */ + wraplen; /* none, zlib or gzip wrapper */ diff --git a/internal-complibs/zlib-ng-2.0.6/deflate.h b/internal-complibs/zlib-ng-2.0.7/deflate.h similarity index 89% rename from internal-complibs/zlib-ng-2.0.6/deflate.h rename to internal-complibs/zlib-ng-2.0.7/deflate.h index 1a1f4d16..3ae6c1bd 100644 --- a/internal-complibs/zlib-ng-2.0.6/deflate.h +++ b/internal-complibs/zlib-ng-2.0.7/deflate.h @@ -52,16 +52,16 @@ #define END_BLOCK 256 /* end of block literal code */ -#define INIT_STATE 42 /* zlib header -> BUSY_STATE */ +#define INIT_STATE 42 /* zlib header -> BUSY_STATE */ #ifdef GZIP -# define GZIP_STATE 57 /* gzip header -> BUSY_STATE | EXTRA_STATE */ +# define GZIP_STATE 57 /* gzip header -> BUSY_STATE | EXTRA_STATE */ +# define EXTRA_STATE 69 /* gzip extra block -> NAME_STATE */ +# define NAME_STATE 73 /* gzip file name -> COMMENT_STATE */ +# define COMMENT_STATE 91 /* gzip comment -> HCRC_STATE */ +# define HCRC_STATE 103 /* gzip header CRC -> BUSY_STATE */ #endif -#define EXTRA_STATE 69 /* gzip extra block -> NAME_STATE */ -#define NAME_STATE 73 /* gzip file name -> COMMENT_STATE */ -#define COMMENT_STATE 91 /* gzip comment -> HCRC_STATE */ -#define HCRC_STATE 103 /* gzip header CRC -> BUSY_STATE */ -#define BUSY_STATE 113 /* deflate -> FINISH_STATE */ -#define FINISH_STATE 666 /* stream complete */ +#define BUSY_STATE 113 /* deflate -> FINISH_STATE */ +#define FINISH_STATE 666 /* stream complete */ /* Stream status */ #define HASH_BITS 16u /* log2(HASH_SIZE) */ @@ -290,13 +290,11 @@ typedef enum { * IN assertion: there is enough room in pending_buf. */ static inline void put_short(deflate_state *s, uint16_t w) { -#if defined(UNALIGNED_OK) - *(uint16_t *)(&s->pending_buf[s->pending]) = w; - s->pending += 2; -#else - put_byte(s, (w & 0xff)); - put_byte(s, ((w >> 8) & 0xff)); +#if BYTE_ORDER == BIG_ENDIAN + w = ZSWAP16(w); #endif + memcpy(&s->pending_buf[s->pending], &w, sizeof(w)); + s->pending += 2; } /* =========================================================================== @@ -304,8 +302,11 @@ static inline void put_short(deflate_state *s, uint16_t w) { * IN assertion: there is enough room in pending_buf. */ static inline void put_short_msb(deflate_state *s, uint16_t w) { - put_byte(s, ((w >> 8) & 0xff)); - put_byte(s, (w & 0xff)); +#if BYTE_ORDER == LITTLE_ENDIAN + w = ZSWAP16(w); +#endif + memcpy(&s->pending_buf[s->pending], &w, sizeof(w)); + s->pending += 2; } /* =========================================================================== @@ -313,15 +314,11 @@ static inline void put_short_msb(deflate_state *s, uint16_t w) { * IN assertion: there is enough room in pending_buf. */ static inline void put_uint32(deflate_state *s, uint32_t dw) { -#if defined(UNALIGNED_OK) - *(uint32_t *)(&s->pending_buf[s->pending]) = dw; - s->pending += 4; -#else - put_byte(s, (dw & 0xff)); - put_byte(s, ((dw >> 8) & 0xff)); - put_byte(s, ((dw >> 16) & 0xff)); - put_byte(s, ((dw >> 24) & 0xff)); +#if BYTE_ORDER == BIG_ENDIAN + dw = ZSWAP32(dw); #endif + memcpy(&s->pending_buf[s->pending], &dw, sizeof(dw)); + s->pending += 4; } /* =========================================================================== @@ -329,15 +326,11 @@ static inline void put_uint32(deflate_state *s, uint32_t dw) { * IN assertion: there is enough room in pending_buf. */ static inline void put_uint32_msb(deflate_state *s, uint32_t dw) { -#if defined(UNALIGNED_OK) - *(uint32_t *)(&s->pending_buf[s->pending]) = ZSWAP32(dw); - s->pending += 4; -#else - put_byte(s, ((dw >> 24) & 0xff)); - put_byte(s, ((dw >> 16) & 0xff)); - put_byte(s, ((dw >> 8) & 0xff)); - put_byte(s, (dw & 0xff)); +#if BYTE_ORDER == LITTLE_ENDIAN + dw = ZSWAP32(dw); #endif + memcpy(&s->pending_buf[s->pending], &dw, sizeof(dw)); + s->pending += 4; } /* =========================================================================== @@ -345,24 +338,11 @@ static inline void put_uint32_msb(deflate_state *s, uint32_t dw) { * IN assertion: there is enough room in pending_buf. */ static inline void put_uint64(deflate_state *s, uint64_t lld) { -#if defined(UNALIGNED64_OK) - *(uint64_t *)(&s->pending_buf[s->pending]) = lld; - s->pending += 8; -#elif defined(UNALIGNED_OK) - *(uint32_t *)(&s->pending_buf[s->pending]) = lld & 0xffffffff; - s->pending += 4; - *(uint32_t *)(&s->pending_buf[s->pending]) = (lld >> 32) & 0xffffffff; - s->pending += 4; -#else - put_byte(s, (lld & 0xff)); - put_byte(s, ((lld >> 8) & 0xff)); - put_byte(s, ((lld >> 16) & 0xff)); - put_byte(s, ((lld >> 24) & 0xff)); - put_byte(s, ((lld >> 32) & 0xff)); - put_byte(s, ((lld >> 40) & 0xff)); - put_byte(s, ((lld >> 48) & 0xff)); - put_byte(s, ((lld >> 56) & 0xff)); +#if BYTE_ORDER == BIG_ENDIAN + lld = ZSWAP64(lld); #endif + memcpy(&s->pending_buf[s->pending], &lld, sizeof(lld)); + s->pending += 8; } #define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) diff --git a/internal-complibs/zlib-ng-2.0.6/deflate_fast.c b/internal-complibs/zlib-ng-2.0.7/deflate_fast.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/deflate_fast.c rename to internal-complibs/zlib-ng-2.0.7/deflate_fast.c diff --git a/internal-complibs/zlib-ng-2.0.6/deflate_medium.c b/internal-complibs/zlib-ng-2.0.7/deflate_medium.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/deflate_medium.c rename to internal-complibs/zlib-ng-2.0.7/deflate_medium.c diff --git a/internal-complibs/zlib-ng-2.0.6/deflate_p.h b/internal-complibs/zlib-ng-2.0.7/deflate_p.h similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/deflate_p.h rename to internal-complibs/zlib-ng-2.0.7/deflate_p.h diff --git a/internal-complibs/zlib-ng-2.0.6/deflate_quick.c b/internal-complibs/zlib-ng-2.0.7/deflate_quick.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/deflate_quick.c rename to internal-complibs/zlib-ng-2.0.7/deflate_quick.c diff --git a/internal-complibs/zlib-ng-2.0.6/deflate_slow.c b/internal-complibs/zlib-ng-2.0.7/deflate_slow.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/deflate_slow.c rename to internal-complibs/zlib-ng-2.0.7/deflate_slow.c diff --git a/internal-complibs/zlib-ng-2.0.6/doc/algorithm.txt b/internal-complibs/zlib-ng-2.0.7/doc/algorithm.txt similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/doc/algorithm.txt rename to internal-complibs/zlib-ng-2.0.7/doc/algorithm.txt diff --git a/internal-complibs/zlib-ng-2.0.6/doc/rfc1950.txt b/internal-complibs/zlib-ng-2.0.7/doc/rfc1950.txt similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/doc/rfc1950.txt rename to internal-complibs/zlib-ng-2.0.7/doc/rfc1950.txt diff --git a/internal-complibs/zlib-ng-2.0.6/doc/rfc1951.txt b/internal-complibs/zlib-ng-2.0.7/doc/rfc1951.txt similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/doc/rfc1951.txt rename to internal-complibs/zlib-ng-2.0.7/doc/rfc1951.txt diff --git a/internal-complibs/zlib-ng-2.0.6/doc/rfc1952.txt b/internal-complibs/zlib-ng-2.0.7/doc/rfc1952.txt similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/doc/rfc1952.txt rename to internal-complibs/zlib-ng-2.0.7/doc/rfc1952.txt diff --git a/internal-complibs/zlib-ng-2.0.6/doc/txtvsbin.txt b/internal-complibs/zlib-ng-2.0.7/doc/txtvsbin.txt similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/doc/txtvsbin.txt rename to internal-complibs/zlib-ng-2.0.7/doc/txtvsbin.txt diff --git a/internal-complibs/zlib-ng-2.0.6/fallback_builtins.h b/internal-complibs/zlib-ng-2.0.7/fallback_builtins.h similarity index 94% rename from internal-complibs/zlib-ng-2.0.6/fallback_builtins.h rename to internal-complibs/zlib-ng-2.0.7/fallback_builtins.h index 314ad326..afa5870a 100644 --- a/internal-complibs/zlib-ng-2.0.6/fallback_builtins.h +++ b/internal-complibs/zlib-ng-2.0.7/fallback_builtins.h @@ -14,7 +14,9 @@ */ static __forceinline unsigned long __builtin_ctz(uint32_t value) { #ifdef X86_FEATURES +# ifndef X86_NOCHECK_TZCNT if (x86_cpu_has_tzcnt) +# endif return _tzcnt_u32(value); #endif unsigned long trailing_zero; @@ -29,7 +31,9 @@ static __forceinline unsigned long __builtin_ctz(uint32_t value) { */ static __forceinline unsigned long long __builtin_ctzll(uint64_t value) { #ifdef X86_FEATURES +# ifndef X86_NOCHECK_TZCNT if (x86_cpu_has_tzcnt) +# endif return _tzcnt_u64(value); #endif unsigned long trailing_zero; diff --git a/internal-complibs/zlib-ng-2.0.6/functable.c b/internal-complibs/zlib-ng-2.0.7/functable.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/functable.c rename to internal-complibs/zlib-ng-2.0.7/functable.c diff --git a/internal-complibs/zlib-ng-2.0.6/functable.h b/internal-complibs/zlib-ng-2.0.7/functable.h similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/functable.h rename to internal-complibs/zlib-ng-2.0.7/functable.h diff --git a/internal-complibs/zlib-ng-2.0.6/gzguts.h b/internal-complibs/zlib-ng-2.0.7/gzguts.h similarity index 93% rename from internal-complibs/zlib-ng-2.0.6/gzguts.h rename to internal-complibs/zlib-ng-2.0.7/gzguts.h index 16029607..ad3690ee 100644 --- a/internal-complibs/zlib-ng-2.0.6/gzguts.h +++ b/internal-complibs/zlib-ng-2.0.7/gzguts.h @@ -38,10 +38,6 @@ # include #endif -#if !defined(_MSC_VER) || defined(__MINGW__) -# include /* for lseek(), read(), close(), write(), unlink() */ -#endif - #if defined(_WIN32) # include # define WIDECHAR @@ -144,11 +140,6 @@ void Z_INTERNAL gz_error(gz_state *, int, const char *); /* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t value -- needed when comparing unsigned to z_off64_t, which is signed (possible z_off64_t types off_t, off64_t, and long are all signed) */ -#ifdef INT_MAX -# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX) -#else -unsigned Z_INTERNAL gz_intmax(void); -# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) -#endif +#define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX) #endif /* GZGUTS_H_ */ diff --git a/internal-complibs/zlib-ng-2.0.6/gzlib.c b/internal-complibs/zlib-ng-2.0.7/gzlib.c similarity index 96% rename from internal-complibs/zlib-ng-2.0.6/gzlib.c rename to internal-complibs/zlib-ng-2.0.7/gzlib.c index 49055166..c1928403 100644 --- a/internal-complibs/zlib-ng-2.0.6/gzlib.c +++ b/internal-complibs/zlib-ng-2.0.7/gzlib.c @@ -523,21 +523,3 @@ void Z_INTERNAL gz_error(gz_state *state, int err, const char *msg) { } (void)snprintf(state->msg, strlen(state->path) + strlen(msg) + 3, "%s%s%s", state->path, ": ", msg); } - -#ifndef INT_MAX -/* portably return maximum value for an int (when limits.h presumed not - available) -- we need to do this to cover cases where 2's complement not - used, since C standard permits 1's complement and sign-bit representations, - otherwise we could just use ((unsigned)-1) >> 1 */ -unsigned Z_INTERNAL gz_intmax() { - unsigned p, q; - - p = 1; - do { - q = p; - p <<= 1; - p++; - } while (p > q); - return q >> 1; -} -#endif diff --git a/internal-complibs/zlib-ng-2.0.6/gzread.c b/internal-complibs/zlib-ng-2.0.7/gzread.c similarity index 99% rename from internal-complibs/zlib-ng-2.0.6/gzread.c rename to internal-complibs/zlib-ng-2.0.7/gzread.c index c3b3a035..f29371bd 100644 --- a/internal-complibs/zlib-ng-2.0.6/gzread.c +++ b/internal-complibs/zlib-ng-2.0.7/gzread.c @@ -145,11 +145,9 @@ static int gz_look(gz_state *state) { the output buffer is larger than the input buffer, which also assures space for gzungetc() */ state->x.next = state->out; - if (strm->avail_in) { - memcpy(state->x.next, strm->next_in, strm->avail_in); - state->x.have = strm->avail_in; - strm->avail_in = 0; - } + memcpy(state->x.next, strm->next_in, strm->avail_in); + state->x.have = strm->avail_in; + strm->avail_in = 0; state->how = COPY; state->direct = 1; return 0; diff --git a/internal-complibs/zlib-ng-2.0.6/gzwrite.c b/internal-complibs/zlib-ng-2.0.7/gzwrite.c similarity index 99% rename from internal-complibs/zlib-ng-2.0.6/gzwrite.c rename to internal-complibs/zlib-ng-2.0.7/gzwrite.c index c4e178f9..a98ee306 100644 --- a/internal-complibs/zlib-ng-2.0.6/gzwrite.c +++ b/internal-complibs/zlib-ng-2.0.7/gzwrite.c @@ -460,7 +460,7 @@ int Z_EXPORT PREFIX(gzsetparams)(gzFile file, int level, int strategy) { strm = &(state->strm); /* check that we're writing and that there's no error */ - if (state->mode != GZ_WRITE || state->err != Z_OK) + if (state->mode != GZ_WRITE || state->err != Z_OK || state->direct) return Z_STREAM_ERROR; /* if no change is requested, then do nothing */ diff --git a/internal-complibs/zlib-ng-2.0.6/infback.c b/internal-complibs/zlib-ng-2.0.7/infback.c similarity index 98% rename from internal-complibs/zlib-ng-2.0.6/infback.c rename to internal-complibs/zlib-ng-2.0.7/infback.c index 62369396..fedfa536 100644 --- a/internal-complibs/zlib-ng-2.0.6/infback.c +++ b/internal-complibs/zlib-ng-2.0.7/infback.c @@ -51,6 +51,7 @@ int32_t Z_EXPORT PREFIX(inflateBackInit_)(PREFIX3(stream) *strm, int32_t windowB state->window = window; state->wnext = 0; state->whave = 0; + state->sane = 1; state->chunksize = functable.chunksize(); return Z_OK; } @@ -464,12 +465,8 @@ int32_t Z_EXPORT PREFIX(inflateBack)(PREFIX3(stream) *strm, in_func in, void *in break; case DONE: - /* inflate stream terminated properly -- write leftover output */ + /* inflate stream terminated properly */ ret = Z_STREAM_END; - if (left < state->wsize) { - if (out(out_desc, state->window, state->wsize - left)) - ret = Z_BUF_ERROR; - } goto inf_leave; case BAD: @@ -481,8 +478,13 @@ int32_t Z_EXPORT PREFIX(inflateBack)(PREFIX3(stream) *strm, in_func in, void *in goto inf_leave; } - /* Return unused input */ + /* Write leftover output and return unused input */ inf_leave: + if (left < state->wsize) { + if (out(out_desc, state->window, state->wsize - left) && (ret == Z_STREAM_END)) { + ret = Z_BUF_ERROR; + } + } strm->next_in = next; strm->avail_in = have; return ret; diff --git a/internal-complibs/zlib-ng-2.0.6/inffast.c b/internal-complibs/zlib-ng-2.0.7/inffast.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/inffast.c rename to internal-complibs/zlib-ng-2.0.7/inffast.c diff --git a/internal-complibs/zlib-ng-2.0.6/inffast.h b/internal-complibs/zlib-ng-2.0.7/inffast.h similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/inffast.h rename to internal-complibs/zlib-ng-2.0.7/inffast.h diff --git a/internal-complibs/zlib-ng-2.0.6/inffixed_tbl.h b/internal-complibs/zlib-ng-2.0.7/inffixed_tbl._h similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/inffixed_tbl.h rename to internal-complibs/zlib-ng-2.0.7/inffixed_tbl._h diff --git a/internal-complibs/zlib-ng-2.0.7/inffixed_tbl.h b/internal-complibs/zlib-ng-2.0.7/inffixed_tbl.h new file mode 100644 index 00000000..7292fa06 --- /dev/null +++ b/internal-complibs/zlib-ng-2.0.7/inffixed_tbl.h @@ -0,0 +1,94 @@ +/* inffixed_tbl.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + +/* WARNING: this file should *not* be used by applications. + * It is part of the implementation of this library and is + * subject to change. Applications should only use zlib.h. + */ + +static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} +}; + +static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} +}; diff --git a/internal-complibs/zlib-ng-2.0.6/inflate.c b/internal-complibs/zlib-ng-2.0.7/inflate.c similarity index 98% rename from internal-complibs/zlib-ng-2.0.6/inflate.c rename to internal-complibs/zlib-ng-2.0.7/inflate.c index a59cd44a..75491b7f 100644 --- a/internal-complibs/zlib-ng-2.0.6/inflate.c +++ b/internal-complibs/zlib-ng-2.0.7/inflate.c @@ -105,6 +105,8 @@ int32_t Z_EXPORT PREFIX(inflateReset2)(PREFIX3(stream) *strm, int32_t windowBits /* extract wrap request from windowBits parameter */ if (windowBits < 0) { wrap = 0; + if (windowBits < -15) + return Z_STREAM_ERROR; windowBits = -windowBits; } else { wrap = (windowBits >> 4) + 5; @@ -209,7 +211,12 @@ int Z_INTERNAL inflate_ensure_window(struct inflate_state *state) { state->window = (unsigned char *) ZALLOC_WINDOW(state->strm, wsize + state->chunksize, sizeof(unsigned char)); if (state->window == Z_NULL) return 1; - memset(state->window + wsize, 0, state->chunksize); +#ifdef Z_MEMORY_SANITIZER + /* This is _not_ to subvert the memory sanitizer but to instead unposion some + data we willingly and purposefully load uninitialized into vector registers + in order to safely read the last < chunksize bytes of the window. */ + __msan_unpoison(state->window + wsize, state->chunksize); +#endif } /* if window not in use yet, initialize */ @@ -507,9 +514,11 @@ int32_t Z_EXPORT PREFIX(inflate)(PREFIX3(stream) *strm, int32_t flush) { if (copy) { if (state->head != NULL && state->head->extra != NULL) { len = state->head->extra_len - state->length; - memcpy(state->head->extra + len, next, - len + copy > state->head->extra_max ? - state->head->extra_max - len : copy); + if (len < state->head->extra_max) { + memcpy(state->head->extra + len, next, + len + copy > state->head->extra_max ? + state->head->extra_max - len : copy); + } } if ((state->flags & 0x0200) && (state->wrap & 4)) state->check = PREFIX(crc32)(state->check, next, copy); diff --git a/internal-complibs/zlib-ng-2.0.6/inflate.h b/internal-complibs/zlib-ng-2.0.7/inflate.h similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/inflate.h rename to internal-complibs/zlib-ng-2.0.7/inflate.h diff --git a/internal-complibs/zlib-ng-2.0.6/inflate_p.h b/internal-complibs/zlib-ng-2.0.7/inflate_p.h similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/inflate_p.h rename to internal-complibs/zlib-ng-2.0.7/inflate_p.h diff --git a/internal-complibs/zlib-ng-2.0.6/inftrees.c b/internal-complibs/zlib-ng-2.0.7/inftrees.c similarity index 99% rename from internal-complibs/zlib-ng-2.0.6/inftrees.c rename to internal-complibs/zlib-ng-2.0.7/inftrees.c index faf1d249..94e65a49 100644 --- a/internal-complibs/zlib-ng-2.0.6/inftrees.c +++ b/internal-complibs/zlib-ng-2.0.7/inftrees.c @@ -9,7 +9,7 @@ #define MAXBITS 15 -const char PREFIX(inflate_copyright)[] = " inflate 1.2.11.f Copyright 1995-2016 Mark Adler "; +const char PREFIX(inflate_copyright)[] = " inflate 1.2.11 Copyright 1995-2022 Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot diff --git a/internal-complibs/zlib-ng-2.0.6/inftrees.h b/internal-complibs/zlib-ng-2.0.7/inftrees.h similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/inftrees.h rename to internal-complibs/zlib-ng-2.0.7/inftrees.h diff --git a/internal-complibs/zlib-ng-2.0.6/insert_string.c b/internal-complibs/zlib-ng-2.0.7/insert_string.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/insert_string.c rename to internal-complibs/zlib-ng-2.0.7/insert_string.c diff --git a/internal-complibs/zlib-ng-2.0.6/insert_string_tpl.h b/internal-complibs/zlib-ng-2.0.7/insert_string_tpl.h similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/insert_string_tpl.h rename to internal-complibs/zlib-ng-2.0.7/insert_string_tpl.h diff --git a/internal-complibs/zlib-ng-2.0.6/match_tpl.h b/internal-complibs/zlib-ng-2.0.7/match_tpl.h similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/match_tpl.h rename to internal-complibs/zlib-ng-2.0.7/match_tpl.h diff --git a/internal-complibs/zlib-ng-2.0.6/test/.gitignore b/internal-complibs/zlib-ng-2.0.7/test/.gitignore similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/.gitignore rename to internal-complibs/zlib-ng-2.0.7/test/.gitignore diff --git a/internal-complibs/zlib-ng-2.0.6/test/CVE-2002-0059/test.gz b/internal-complibs/zlib-ng-2.0.7/test/CVE-2002-0059/test.gz similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/CVE-2002-0059/test.gz rename to internal-complibs/zlib-ng-2.0.7/test/CVE-2002-0059/test.gz diff --git a/internal-complibs/zlib-ng-2.0.6/test/CVE-2003-0107.c b/internal-complibs/zlib-ng-2.0.7/test/CVE-2003-0107.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/CVE-2003-0107.c rename to internal-complibs/zlib-ng-2.0.7/test/CVE-2003-0107.c diff --git a/internal-complibs/zlib-ng-2.0.6/test/CVE-2004-0797/test.gz b/internal-complibs/zlib-ng-2.0.7/test/CVE-2004-0797/test.gz similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/CVE-2004-0797/test.gz rename to internal-complibs/zlib-ng-2.0.7/test/CVE-2004-0797/test.gz diff --git a/internal-complibs/zlib-ng-2.0.6/test/CVE-2005-1849/test.gz b/internal-complibs/zlib-ng-2.0.7/test/CVE-2005-1849/test.gz similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/CVE-2005-1849/test.gz rename to internal-complibs/zlib-ng-2.0.7/test/CVE-2005-1849/test.gz diff --git a/internal-complibs/zlib-ng-2.0.6/test/CVE-2005-2096/test.gz b/internal-complibs/zlib-ng-2.0.7/test/CVE-2005-2096/test.gz similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/CVE-2005-2096/test.gz rename to internal-complibs/zlib-ng-2.0.7/test/CVE-2005-2096/test.gz diff --git a/internal-complibs/zlib-ng-2.0.7/test/CVE-2018-25032/default.txt b/internal-complibs/zlib-ng-2.0.7/test/CVE-2018-25032/default.txt new file mode 100644 index 00000000..5edbff64 --- /dev/null +++ b/internal-complibs/zlib-ng-2.0.7/test/CVE-2018-25032/default.txt @@ -0,0 +1 @@ +OBXESYMXQLOTSVVWGIMGKKJOVTYKPPMYROFHSCXQPOFVHKBKAFYYAFTWCVOBPXWDSSFZCKBJOJUWPUQHKBNYZMCYEDEBXLAOREOASIEPAISNAZSXQKMWJSABFVSOXNKTGDCZKRSNBTWWGRNRZWGQEHKKQJGSGGFWJCETJVULWEJAQPAILMMQBVWJWMHWBSIOAXOQNEJYAQSPEOACRIFYOFHTBLZFSHOYGQFDGLLCDEHGPRAUFMANGCKZNZJBMEAEDDPZPJMMFEQGEUOHYFEQUKKHTOELBFUVDMNCEGPTLQYPDFTUGGOEUMUIAKAAZNXAEAKZREKXSZZAKFMHDZJFQDLLSILAMUHDQJTVMYIZWFEOPCKWVSXYDEOSGVWACWZNDDTOOYSFCOYHMCUSELFMSBJIUMPFOGDDJGHELQJSUAEEOWQEJMWRRBBAANVFEUMJZRQGDIWXOYXCZZLMDRHPJAYGUUJENSDDGBKDFRXMINPXRZJNAUPBIOAAKLMFUTZWAEWIBUTYOLPPNUWMFYJQIEMJYKVRNKRXGODFFRMMSKDBBKRJGUWOFMNKQPJLCCNDOTIWVGNWVIQINFPLJLEQRMNSVYFGNWMWYGKHBRSLPCCPHUNXIDKDGIELLDUIOXAJOZGIJVKGBJMGSCOHPRFVUPXJREGBMUXNSCBBRASVMNZHELWNHROPZGWIYMKESWGWNANICQTNICGQBJLAONQEEDEYUIGAOQCDTVBLSSACVCUAURKRLCYIHAYHZZCBVMLALYJKNVJTHYTFDDZVOQBUSDTPERXSTYJKCOGGFLBRQIARKTDBQHSFBBEEWYIEOOHUWKUGACNGZQMAHWPFJJOHMJGULPPXYTCIHIAVYTAHIZMBMYYZXBPLTRWNEDAALDOWVQLNFWZEVSDWQBBYHUYNOJYXVFQYGKSHGCRDPPRSVZWXECEXCWOYSBRWIBPZLHJKHWVTZYXEYGUWAXEUONCVLUGHLTCUTVSUQEVBRYYCIZESPFTGKDJKDQYNIXWZZYSCWIULGKVRJOLQNMWMKJKYOZGXFDUHXEFEFHUMHYKYINHXMINEEJAFYQLHEXBTTEBWGWEAWYODLKRFWATZYJXAFECXRPEDRKPJTJBHFQYFWLCYPMTASYYIHDGURAAUJYTKAMDUNZHXYGPHJVJPODIJAXYXONXEULMKEQRKUYAHLJLTVJUPMYUQSEMEKYCYBPNDDXRNZUQYHDBITAYHMBXSOETYJDWDAKRJGBIUSIUIKBQPSHOVDRCNSBUYDPAPAXBKHLIGEPMOUBYFXAMXDEASVXOUIMPVLCQQJRQNLIDVIRYOEXXIUASCSMSTQECGPPHPGDLJLCRJTWSHVOUNQNFTQEHOIQIUWTOFEIKBCWNLDYIJDUWPBIBBERKPKYYUWNIMOCMAUEVBLHBYLQEALQVTQYNTGASZMJWOPCUVKMJEZFKJZMCOORIKNUZQAPHVYTTSJUBBYFJJHTJRYCPRHVLZFPNXSSXXJDWDJQVSJVUKVWUYGPLBGUTBVLMNOFYUKIKIVBDCIKAKVEQIPBPQRLOSZXNIGIDKNGCLILUSTJXYFJZKRAJONNYAANNTWVDXYTIESYFRGQVIOLUBOHGNAGKAKZTRSYYIBADADFBHLJXDYPQKWAVHQGWZKKWUVAFWGXBEBNVPUDWCOMMSXJIVVLDHAWNUTQAPKVFGTYMOKTBDBQZTHHAWGRANXNAVWIOIPILFNUKUFVLNNLAFFPHBLFYMHYOZKABEYKKEDHYMBDIGAFUHTJOOIOWOLLKINJMOPYNNMBMRXNYZAHQYOTDNKWDIDBPSZJOEBQWPYHZZTHSFVJAQDFBJHBOBLOKJKIOEQTWPFSXZCKWWOXNEIDFXVWFPPEFTXLPHMFVPZYRKZVHHDWXVHCASKVDKHWNIUMJUEAAKSFYAGUUKLYGDVDPWMKIDQEUEDLZWQJLRSDMLSHKIOSDUQIDIAGIDEQZBUTVLPUFEXJCCJRGCFXNVXRRNSTWXCXEORNNMFJFJUMOWOSLUUYCOQKLTTUTVGSUCKXQGPHWFZOHALSARGDPDYOIFBXZCDEMHVNTDXTHZOZDOGCZYRXEWLWLPTMCTPCTYWJXNRGSYJDRCFIRRCLFSZMTVXMHASZQGVHHHFLGZKDGMGAHVNHXROKDARLQWIYXXRYTERPSEDVYETTARZTXOTUGAHTOHOOTMCJBZKNBBQHAECEJFUEQQYXNXWBAUIESPQGIOEABZTMSUIVNUIOFYGLSTUVHKPIVBBHAPSDEXZPPAFSLSJSKGEGKXQZGZYYYBFHEKOQUZEMBMTLXLLAJMEFEWLECDLUWLMQLNZDXGDHRMCOOTGWXKTDFKFGEJSLUEYWDGRAONPHKSKCTXQZCEYMQSUIWTNCQLAMABCIZAAOJCLGRBWRFCQKSTYSDDYOZOSEPYBBVEKIFDJOEVAAZBYYKHPKNNWKNIGMIBUDADWHVKSWCMSWBKQAHBNFMWKFPRSBAJMJCAFAENZBVDSYGEPAYDMDRJOUZCGHQNDAQQZHSBMLPWOFGNNODEZZZSJUOOOPBYSEJFZOSJQGTSUBQCOTVNAXIZFMVJUFWGDDJWRBHDUTNQOSYJEWTEZXOYUQXKOZSBYEQKHOAEUEUYOMJLRHGQKKCICCLNIKCMAXLJZEWOYSTVZWXDXSXWVEWJRTDDJIDEWOYXXKGKBHDEPXZFUPVYWJDHXJENZANIEAUIZBXRIZFHMVKOUHRKRUALDIQSAOLIBFSRNBNFZHAUSGGMYSXJROAPNFXOWSFWNRJCAEFJDNDCRQDEOACSMGQQSQIVVVPSTTEPWFLQGJFOXUKFEMTQTUZRNMUPLLQQPNYAIIOMGQETSURJIJDOVGGWQDIKSGZYSJCSVPETTGRKXOXPMRPEMCXAXDDQTVOWDUGPKRCKRRCBRBDEMASKWEPIRHBKGOFGUNVXQMTSKOLAYJKMAGDLLMOPHDAHXBDQMYMGVVREVFLEZPXEXXFORECDRSTSWXGRNRQPSAXXIZXOQPDLEBDHDAUOMAGPFWVMWCQLMXDXKSFAJZBLNQLLRYDBYPWDWXFCTGFHNJVKMYONKAAGXCALJCRHZWYODYFRXVGVIGYJIQMUOUYPGDHFVEUTZRLFILMZXFRXHNELGMKVNYKNITXYHGZDGYGLXJXBODBAIITGQNUJOKJTAUDJFRKLEVFWBARAXPOPPXXDAFZBWAYFAQICNUIRRVRZURUHGWMXELBHDGYVRNEFNEFSPZUNOTCUNGFOAWYCMKVCDTNPIHWLJLWXVORXFNSKJVRAHBJCFMQHVMLKAKBQPMGQYUEPHLQSZJBORIOJAPWJLHYBCXTMZPZUWJIZVRBOYWQIHRWXGYWQQZLTLNHZBSJLIBKJBQNNWSFLYVBYXUZFSGQZZYRPPKHYJCCYZBIEWATBDYLJLJGNCAUHLELJZVYRPJNFQIFLIGLEPQEVOVKASMSJZRUPZNQTWZJHXLVRBCNXKUXZCNQKHIJMBFPXNLPBPYUOWEIINXMUYGYRWPNQPBQPRFCBCIIKWKJWKQNRNZAFQCXUUZFPHBMQDSLYKQJKAJNAHBETGJPYAHMWHMNUJWMUHKZHPNPDZQEPDZZKGDQOUWIUPWSBPJQSBXAVFXJJUFTOCRKJGHKASOTJXBLQRKVOLJTOUUUYXQQNUSJCOILWWGYRJAZXXWCXAZFQITHUYREGZILSHQWSADJRTIVYTZSAGAXQHDZYZERPDHQOLOFAIWNLYACZRXKFKNOHQOFXFIKYWRRDPUFNTSBWQYGGQFNSRBPHBATAWCSVFANTUQIVFSEHGGELSJAUZFRBDEMBFXMFMOTENHPWKEVSIUOEPXPCKMSGDWWORTXBPTAOZPLNCKJUHEPBLCPRRXLGXUHKEKFIYXNJCYTNXMVEXNNAGESQWJGSFBCCQXLSXQJVVJUIZFNIUNAZCVCNUQWFPBCPRSLKIYLDOSOHOPABPMIVLSYIZKJHBCJXBTQHVEFIQHYEGYDMXWSNVWNGSAXBSDBCBDOFVNUFFVWGHHDBIGMNPFLGPGOGSUFYISQESRSSKCEKUTTUTNYYPLKSEYXIZGBPCQVRYGFKYVDYVKKIONENEULDVNMPKFKTAOBDKBCJBIWBKYLYESVCHCSCVVVWXLIMUDNWJQYJQJIGVGJXZQPUEGYTENAMCPDMAVXXOHZCNPWLKGRSQGCNXTPZNTWGFRDJIOSPAFNJHGUHXCDLWSWEHBXHOPAHBQMKECIGDGVGKRYRGLSIVCYQZAZJWGDJWIFUOBIHRENRDKEXQRJCSNMVTFQBJAIAKTPCBINZDYCRLPFSPCNQLDJYSQQWDNZNIEMYZCOIBALBGXXOLLRIVBDQUNXFLMGRIHDFDNCNCFBTKMOOQSLXSONNDFGNWGMAMGIHCDFZZFPAUCOBYJCOCHYVDKNDSOTVGMHSWHOQFCYXYIMFHJCVFCVJGATFWRYPYLEWTNFVTZDATIWNNRYQFTXDGQPPYQYOJJBYSJLADOODZYJIWPIWYQFTGFYESGCCOJFOSQWCQDULHBXAYFDJEJOLOBHTMGXGNFUUFMDIBXDYFVHLQDURYGCSHIYGYJHMJQUFIUBWAKDIFXQEUGYKIFMMSYKOMVNKCMTYXWIBEYNHQHNMYKSPSZTKPTDGMODMAXEHOABRZOSYYLHYWPIQQFMXODEYOAMXDWIFNLAVHTUHSCPJQGRMQNSIZQXNJEEFFVOYAZMQTPNBGKMXYFLYCXQVMXNITCYDMKNTBSNKPGOFRMAGENQZQEPUMRLHFIPOZSJDOBQYSHDETQCBBLXJAMHIPPHQIIBNCAOCVCOHQAPYCYEJBIVWSJVIOFZYAKFYEIXVDVIVAXJZQZKUOCGGBAHHPVOADXHBWEQRMVRBKOONFLPDCKKBFFIZJIKRYMEWWYATRBVIIKBUACMLRTONKOUXMZCGSSYFCYMNTBVIENZQXDTYNZGOKCRENDDTNZOQRXLDVZXLOTFOVYAZEEHKXRDGECGCGXNVMYOKKNIQPCPRWRAHKVPZSKRBMEAAFDAWXXHJUBOUOYQWPLZGTMWWYFBGBNUAQBSRHKNUGGYYJOZNEOWWYZBMREVSOVTWVLUDCJWQGJPFHPTDHFEQVJJIYARMBGCTSKXZQFGOXOXHMWBOHMEFKRWKJPOKUQRQLCHHPNWEPJFIAPSYAHXUPHYOAPABLLDFZOVSJFNNHVDPNWXPXFEYADEXFVRWKVBVCEOVMFIKPMABJUBGOGCDADYIEWZZZCANXEXMMFKHOWOMJRJTKBJPYRPHNKHPSQRJXJNQPORMUKIXNFRIXUGLECEJYZXSUFTROJJRAAHGUDXXSPKOTBUWJPMVUDQGBAPSQOWYDPVVEOISXLDKOPWANASRPSICRGBNHJQGPSFRPFOZYIRYEOFCQRZWCRYAARQBLEAJDQGQVIFGVCPFSEIBAUYXUXFQMNWFNLVYDFFDCVAISNQYGNCXLKXERSQKRJOFLTSOPRPQQONCVGVBLALFFJLSTSGNTHWHHBTNCFRQLWTGKPWIBWSUEVHWFHKAMBOQMZYGAZRAEJCFBEWEFLDGPMAKCQXCLFMBIVDECFIOOXPCTKCCJDZPECXVACPBOQVWNYAZGRIFJETXUABRDPOQOGZLWPLZETFWVYOZHYGSQVMNUMYIAPFCCJNQOVKZCEMMNFRLVTKDRUTDNAQXGPTWYGRCEOTQMLESJDAKGIZNSTADDAIMCUKZQLWYUPHWSQELFHEZOFGRBVUSOYZQMGJFBWWCGYBEFIHCCJKQOAFXAPJEMFJCVZAYESUKQVKHGHGJMTBRECBCLFMCIIBJPIWFRROVXDCPTTEUFOMAFJUHXLAHELLPYCVZDPHKTVGLRVVUXDKISXVAIYEXVWLSQPGKGPYXLXIYQSPYEQZZVHAVHSNASXOWRFMRSLNPUDTWYYPJRFGPJIGTZRTNXDLNAEKRBSZPMZHWPFPGLZVDTSAXANFKOPCNWSRWZMEBVUOCZEMLSYVDURZQUVRZOPKPJMRDQPBGLZCFADBWRWKABRGOMGKIOLZEAJHXIEIPINCETSTKEGEDYJNZBIWISBSDTZREGNOIXNYFQFPUBQLGWKHTJVSCTUHKYWZPSIIBJFKVQPPQCKIKNDEIRXHIBAPDDXYIBMWNUPOISTKFBXDELZFYBRVAMLLPQQXGMBMNBTJRCNCGGZMHIWKJNAFWYYCHEJVYVXPUWZHBWPKHMBJNGWWLXRRKPZHQLTPKGXLWZICJMFIVPRLSXVUOFLWNHFSZAUJTYFRTSPSDOEHFYFHTNZOLRTYIQJQSEEVREMWRKLEVXOGDQMQZQJWTHOYIGOJLJUFBSZJLHGYRRJSZRCNQRCNVBDRCOYENFVWULRBHOGLLRKWMFXEZKBZDMDKYKFJRIHGUZOHBFOPJLMWXECZVXYZPYAIKYDDVWAXCMPKOTEFMIRXDOFFQCNAUBGHGYVFOCONJWNXDMIANMLMJOIAHRPTVNYWLSQBBTNJLBAAQMTJXLXADGYLLMUZPCYFOGJGCJORRTGSRLDXYODWLVGHYBYLHCGPWWEYJPMSAQWNRNHPYLLHUJEZTRQYJVZEJFUQPVTIKFITRVXUODHQDNWOXVHXFWDVRLTKPJVKKEUMYFDOZJOSCHWQQTFKUFMFNQNDCRTVHSVOFPUOMVDEWGUASIKYZPJUGIDUDPTOVAYGMQWLGSUMCWOEKCGYOMZJNRVSUKJGYQBFZMUINTJFOQOVPHHJRNCSUPDAVMSSRCYMJGCGRRIKLAUTKOMWRNKOZETUTSKNRHXRNDOGXBGUGTIXOLEKKOTBAAYFPJHNWKUNMDFZUTRWTLWIBFJGMXMMZLHTJLWVVPAFGJVPKAPPIMTMTOBKXSXWOEWIWIIHPJGKDXCNSYUKAWUCBHJNYHIIDJZRPQPVPYIZZMUFCHOQGNXGAMHEULGHOTRFKFLRPAOYUQYSXLSVVHYXGBLSDOBBZRMXEQCBTNUOATVMYSTKGKQNEUUEWCWBNGWMWIEYDTLBVZHXDUUDXNHJRXOPSLPTTHSOGBGBDSNAJWCBJHZGIABQFONILLESWKMHLIDLBWIDWODZFFKCUHMPMBMYEJUGAIOECPPIYQGFNJCLAHJUQMNTHFJOFOJOTRJDXMGPJYFVDCHLCPRMYRRLMQHQJYQLWMBDYFFZZYWSVVTBFMHXHEAWXYSXTWBNEZKDOUIHUQADPIUKUJXZYHUUUFIGBQIHNLRXUOVMEUSILBUBWEBQRQFQMHOWCEUVUXNNJLNNKGSSEQQZNBDVJYWXLAGGHSBUUFAKMYKRHCATRTMTMNEQHNQWUOPNAWHOCGIEROSOMDJJCQQLSXZVZRVKUALQTFJUOQWMATSZTQBDINTLRDTMHGPIAUONZVDRSFOTAFDNWLUCSYCKAYVCAAYJOZPHPDNIJOGBOGTDLNXHYEJAEECJFZNDMEIDSMGEWTWWSKWSQYEDZFKSRNPQZTGNYRTQVWDBGKJYVAEJZWSQAHWNOHNDRHZAUYXWSAMCNPJBYLNPIPNIFGXQDGRZTQPXETWLZDZVKIQILLEIZDDFLAPQFYFHTUHZIOYZLIGTJMFDATODQNBIHQIOZUTQQGFDCSMSZAFKJFXXYTDGWDVMNLTBZCIDNWXWYXANDDGVPMWGCRHAQOVWPXHVWBQLSCXPPJDYQKLVDBWZBNATRXCPYBOHIGLREODDUVFWLRDSOXYICZPITCQXTJCYGNQPKZXGRHOEKOZTMAYZLUHUYPJKHHFTVSDKELBLVBFNULMMSIERUKEVLCMBRBBHOFGVFBIFFXSYKAMSVXDYUIOGBHLKNNXJTEWNDQJNRGJMKVPFRIJHKFRNTTDSPRECSOIFJUXSIEREFEIMXBMWSBGDJPVIUJUEPAQOOOQZGNZORAKQOVJKDAWSLXSJHQHUIASLQVJDWWXLNPVSWXOGMGUHLKOQUYMGTPKUUEEEHXZVMIBDQJSNRDDFZEOEMMOIJLEYDCDDXGPAYEMXDRXERZYPNHNWOLOTPCDDLLHQPLBHQDKTVJNMFGAWXDHAONUGAWQLTOQSCOMVAHYTYHXQVBKENQKNVOWAGEZVUVWVQKDQPERCLETOCMXIJXDMJRGCVAQUMWHDOUOQQAXOHBXJDRLYGEWUZTIMSTMUCZGOPEBSWIQGPGMTIEGWNGRQAUQQGSLVWKCZTZMOTUOVOCDYLPGZTMNVGHSAGHRGPVTEYZKBVUQNYTHTVMWIEMREKWBVRXLHEGZMHKIWOMOZFPXDPXXPOYFWRLUSBBFXRZWGXAIYYSQFEWQMWRHMYMLFFMBOGAZSDJRENVAQXGDCKMRQYQGWVIDECLOMNZYCCMRCLXWPUIWGNATOQZUYIRDXGCOGZIJRDBYBBNFMOEPYUUJYZJSGICOMXWBAUMEBJVWGTRDRYQGNONJZXSZDJLRAMLJLFTFZUVZZOEOLKFOGFODQBWYBRDMUKEFLKDUADCGKYTZUCVRUULXCXMKAZCBCUQLPAWCHHIFIPIXFZRJHATGQCRWCCMSDXRHASKDKACAVAPMYIYUEXZFJCRPMOTGZJLCFOHJMRRCADBGLMQTBZAMGGLIULVTBOKKZTXRODXTAZXQSXDHPVFFPFLMJKDJNQYGLGUNVIVUWJBCWDYASMCYPTPFZBVUCMUWAOYOUBZZTJEQNIZMPPEHRJPJJKAKHRNGIHUGRGZGRYCOIWXFDDHJKQHSOVNQLYZLABMFFWVQXBPNWNGYMYBLPTLLLFAPTNNBPLCKOIUBNPNCBHJIVUAVRENTTPRWXENQAOUPPEMSOIOLXMQFIAYVPLSCZLYDIOLZIUCBIBYNAWSOUQXDYZPQQHWAQLZCJSRDUUPEKFYWYEFXOHSILHHIJDERCNZEPHGODFOECPZONRLPCFKGUXNFOPVGSQCPWNMAVTPMGSPJRRFIHYXQAGJQWYBBBGTFAAODFFZKDGCEZTHQNZKXKQWFSJKIKTWBZWWRFYIOVWXXZYYMPLUJZETDXGJUASQTPZVBPYKHJLHSCONRZWBUZESWAOXWUMPORTJCQULFBHWIOUOPZIPLZRDCWOCQXHPKNHOYXHTKOFFVIZLGMJMKXQLYEURGMQZRCQGPPLUDLECVBNVBHAWBITJYAVUJKXPAKZDBJGMOFUVOYVAESRHLQVZRDOZPZDWJDWGNXBEQVIKHYIXRIANMIJARWDSAGDKQCYRGZZOFEGXDCMWCNYHDFPOYKSZIGNWIUBSAXPGPDRKERFWILYDILHHXGKKWEWCLLUQDODMHSAHQTOXPLMTTVXRIVIRTJRQFXQHTVREBIXOLZSFNUBXRFSQHSBFHUMMTNVCNRDCAQPSYKEUYHULCSKSYULEQYMQHHKELFXQIPWPEBNBCLVBYEBTQQAZKHWJGIHDVFZHDSHCZPHCWOKRWGSLGCOZEAYLTFJSEBTMOELSJXEGJGCOQPURSZOGOANVZVBTTGLPJYSNUSJUCACWAUTJIAIMIAHLMTVRYONFWCHRYGLSJITEVEFHCQQKLVXMVMAQNEKMKRFJIEUZAPKXDRTHBKEDMSCMNZHOSLFUTXUIDFPKOWRDRADBCWFDZSAJOYSLZMRFLOPUHMXOEECQDNRDPUXFQPOYAKWHIQWIFVNOOAQQHGUYVZRFCLYJYYTROCMSRKXLBHMCBACUNCYLMEWEHTUPFFRJEJAWKHYJUWBUQRKFMHOYSBSMZVIJNRUSEMSMXLPQAKAYVGCFARNHOHWTKINTCYLVNWRGALUZBIQQFMQGBXKAEUFDMVOTJLZGOGMIDGUUSACUMFMGFVDZIRWAWFXDBNJZJAOAMXFVBNMJBEJNMYVOJWLSBFISAKMCOVAKYHBPHHGLYSLZLHPDFCQRPFKUOXKFXZASEPZLMITPDIDENSDCGPFRQKGEZQNBMVWMDGUJMPHMPZAAMSAFRQGYAAGXGUGKMLVMZSSQSREGSOVWUXGQIQXNVIDJDHGSKNCWYOUOEWGVESAKGSMGBGEOZMIUYOPIAJXTBQNXEGCXMVHMOVNHLBFKQFNLOEKISQFNEKNDLGBIBAOEWNBALNSIKGZPWOMLAMTQZPCGLCTPDHFQBNXWFTAPXFRCKHHGLPXKDIXEWPYWOAIAHYJEVXCGFBVROFIVDABRSAHRXGALKMXWARMJZMNBFAMSNXOXOFYMFIXDLEUOBQEHZCXRZYJNEXRDHJZSWZMGAKWIGKYXGQFSEAXTXUWHOKWKINLVBVPTHQULCWKGWKJTWVWODZFFVMTJIATOYRHKRUDKVHJPJJDFJKICWFLEICXOKTGAYYLBWZONTDEEHFZYNPAAJHFOPZJNRWTEECVMRCQIJZPTQNEQDTNKLRPNUWYPFFDVEIWRGQFXUHIGRTZWLLAAGODSELKYMRIYAURIJSMJCCACPVAWMJVJHYNPHPDBSGSXPRNLFOMRPPPTPIMRQAZFEWPFTEDNIBANBUGMSCPJURJZSZBLFYLFDHVIJMLWOQRUOEIQSZYCBVEALSIZMSRNRSBJROLAYNVRHYBDPZQEUBXAJXIDJYRMGULISLPLHLTOONHKYRHRCYNYZFZXIVGMYXNFMKLVQNOYOIQISIUHUTUJIGAFKENBJWMCLGEZNTSMHNUFZLUCJTITIHAZFKXDAZBRUFQXWJEMPTDVLXBIXDPLBPHLZGEYYFVRLZBFOIPHNVVFOJZBGYMRYLMXHJQVDQSFWDNLRKBHHUXWHGRCCDPNVSCSVLXJLHARQZFNRKFHVNKATOKZNTIZSERSKNJMKFSIEMWLUXHRKDFGMOBALHUXOYJRSVAHZUFMZNDHTMYKKPCMIVWBENKTXEBRHJUGMRXHHRWVVCTRTIRQPILWKAMKGJIGMQRZJITFOVHLYGOFANJGQKSTVBCGBVJWRTDABQOUGNXOGBERVOHCPCXCOTREQHMLGLFRLFTDDADUJZZTZCBKATKUAPTRQUHGWFUILCISGGNLCBCAJGKXIXZQCCCJPKKVMNXEDCRANPMFFOKGSPDFAFQQCTEYVAUVUUENWWVQQGHGCHVDGSWPGBPPISWUPEDNODLWQXNSHYQAWJANXNKACXXTNGBJIHHZSBDIBHSGPZGTWXGQVPWEBCREOZPAFHNFANHLHVCYNZQMIOFOVSCHXXCQQSIZMXTBFCULNPCUGZMLWXSDAHRFENSWJVWXFCATCDXZKYQRHCNQDFGRDAGCYDIGPYWSSPTSJZOSCOXEBOWXNQXUGQBTUTSDKHEXNRKAWPLUVGWVPEBMGBQLMKHHNWRDOPCUZZGBNHBODDNLQOWRWFBMXDWYIXIXGGZWIYUKXWRIPBDCLQGKMHBHRSUJOYNBXOMVYQPLBLHEIHQAGKYXTADCVLXKGZXBAXLOXWBSLRMMMTPTXQRYFVFTHNKXDFYOEKCVMHINLNACPQNFHVPYMDSUHGTSWJVZHSMNXONFFYCEZUEGMLIBIUGTRMWQGVXUZCRUYAXCMTXCCGDHSTIREDATCUSTBHGAURHOPAYKCYHOVTVEYWPYGXBSVTQAFGBRNVEDMXDUTWXXFWUOSMBOKJEGSPSFRYBRXYFFCJVEMEYPKSHEXHUBRCHZTIPPTMIYKRDSUJGICXPGATHUNVMVKLEXEROYTRMYOGCQTMJWLSQMAWUUIQBDRNCCEMHWKKIVIEKCVBDEHKRZARARXLTVASSMHGDTTWDVYIJWITWJFILRABYGEOQPCHHFXXRFMOYKCPAQRPBOJJLADKVPDSMPTAXTMZJZQPMQOQBECGZDDKBRLDMBZZEBJAFXGXJNAZDEFBCAXBEFXPSETVGQXRZBGFMBGECCBDBAWRYIVJOQIRNGNYZIRFOPLESXVYVGHKRAZQWCQWIKPBYLFZBRFGFDLYNBYZRYDVUOUXECTBFFAUESCYLBFGYJEZOBVQQABDOOTQCNUNKZLVOHEUSOXZWKYSKZSPQNVPAHTMXLLBOUURUXLFMYWKQGTPHXUZJRUOTGEABCBCNZADCKOEIWZBWFUDMOLHAPQQNNARUOWZLADHNLDUPUADBBZACUPKDBXEFZOQEVWPJMLMYQWSFFHBYKSBMZXLMAAUAWFRWZGYUSDGCEGDFOWYPMWXIHVXKCLWEYXBVQSPMEQNIBBHQRKVLNKNQWEFAWUJSTMZTZYESSYIWLSHGCGRDSESWWIDWTOEXFFCYDLDNDNIEYLXZHIXFZSOQRBYSWLPVOWEEYMVDAGBZMAAAGMZSCMIQFOHJKCCAWJHAZXNIVBHIKKEKGUQIZPRLEXPCNJSTIQJOWUVRDIZTHFQCTHCWWMWYPMPMEIVFAUQOLINCKHXNBADPHNFVZOZWIQCYVUGVOMZTCJIRIPKVVZZPKQAZWCQNLPTAUVXJGKUOCJWUPPPHPAFQOTGJTFRFQVGOXHGBKQIPPAISJIZKBYNNPAXAGCMTXDYRYNQLMAEKYLORYHPJJNCHFSHSUMEOPJNCFYOLONJQAGOAYHQKZXKEQOHTHUTBRISEINMZZZULVLGSNVJUCLQVGPTHPNCKHNRJPPCVMGWZPTIOACQWKUTTUDUAMIJFOZHNGNZHFHYWUFJEJLIWORKAGEBYCQKWGIFOOSVRDRFRQTQUHSZQSCIJUAFZTBWXTIVUFBLDCAJWFBKEPTVVXPZGOHNNNNWYLLIDZHYJGWHCJUBWDLBSXUICEZKEVBCPQGWVIKLTHGVWVAAQXXMGCFTIUGNSJYAMOWAOYGTYVYZPPBEMGGGZZQBUIRELHZXTZSYMNHVXTOIJRYOUOMZNNHGXJJKFMCLTGGRCTEQSXWPZJPDECPUAMGXARWINTDXDDLSNHYQCGPSEDSNJITLKJLZRPAXGJOUMBVFMBRKEZYOEBETWXSZJYSQWFEQUDBPYNYSBRDJOKBFXXLXBNSSFZBJLEIICFMLFXTCOLAODHEAABUKRQHGYSEAZPWIJHZJUJMJIFVUZYBZKSJOFSOVQLYUEGDHVIUMAKLKQJQERNZLLYGEDQGDQCCFOKJWBUNGQNSWMLPFWTXVGAWJMQNBDLYIMXCXNAHUECCILVTXWEMPMXNZQDVYMUWOVPRDOHDHSYKTYEZPMOFKWIXFCGXWHANETZSTTISMGHOMDCTTZIXOOCFRLFLIAXNJWVIMQWEGEZUARCLNBETGINFONHNOEHERRJPRYNBMCSSIXVHCISSUXUXINVIKIMCZNPXBIYWNWQDEZTXDREVXEMAWUASQJISTRTDJMLSQGLQVAURUKZICWNBXQJXSNBZEYKPTPJWSYDCXFZVPBPPSRYUOQQQDNFRQOWVYPIMZPBQAOHTZMCUGJLNILKFTCXQIHQCXBNEKMBPUNULWCNYNMCSFTZZLWPCBUUGVEZYQYQADNHTDFUQWWLDQSBEUOJGLYBPNUMWCDPAAFMGMWFIVLDMXFQWCUGMTMXSGKZKHJGSKSHPHGSIFVQFDTCEBAEQBGCYXIKWJFNUSFOIPHVIVSDCKNPYEVPKWENBFHKYZYTEYULFTGUXCQERPDVDYRTGCOWLIWJMDJFDFHARDANOPOOOPJKPVALZSEYDVSABYLHLCLSQXBVDHVFSRHDKHKJWBGNLMVYKEEECQPPUWOEMPEXGYCQFNCDKMEGPBVUMEOMBEDMCQGSNDLHGMGDMTPSPYOHBZYNBBMPSOMAKEHVCSCEEENUFMTEDDKKCTOXTQVFQOJNSXOHWMMCNTOVFFXHHIBTOTRTBMJUXTAVBUKGCLCKTOJKAZYRHJCOXGGWGZQMJNGGTGFMEYFSYQPFGFOQHTCQBGIUZAJLJJTYLMVZUOJYCSJFOXIZKOJSSGIWSBYYTQOUJDDELVCWQZXHSCGASBZQZSIMTPJHBVGXVPYLDNGIPCUXFAJILVPXTTBCSIEPABVBETVRCXDSDGPETFEFGKYDPABNJIKLMBINGAQJVPDKSRFOWARGITJKRALJENOTDNOFQDRUSKYOUGSJZQFHJAYKFCYNJGWTMYPREHKDXGMBQEKTEMTEBYVXYGGLSBWGAKJBQFEORFLJFYMTJLPPATMOUVESREKJRLOZBOHQLDKLJRHBPBHXVMBTJJOITGXMXGAQDATETWKYIKXENGMAEAMHYXRPPXBIUCEMSKSGPTIWSQDXDLXTPGEZVLENCQKBOMJMQEZFVDSJOFGSFEFVZVUPXSRKWYPNNHUDQSMFFZUIKCWVHGZSWFLXGLXDFSPITBEXVPFOALBIMXCAVIZUNBHDMKVGGXUUXANWXSEGTGKVBWXXGHVFIVLVAGVSVJJLKGXEYXYRMPIKYEFGNQKRXQEBMBADMLSRNIWBCAISXVDZVQHPIPXQLSKXSWUMMMAJOUXWSOEFHXZUEWELFVKKKIJYPRWVYPIJMYBZBDZZMTIJRKUGEGASHZASEJPUEYQJHIRXFQAHEUGWYDPGCJCAGBCLQLPGAPHDNVVWKJHBCKXNVBOJGOESBJTTQWZXGMUHBRLHEQHXOQFJMNSRYWZURFQSBXFCOHRHJNIQQOIBRQUWQHEGWIGSCWQYSEYDVBFFFJXYCORRBRULIJZUQEXQUMLNOMTRWSHCRSVFQSVKKSKVTRRAHLLYKUKGQXZCJHTZAJQKRCYELVNGDPUACTERKRMHOREGYUMGLLIFEXTFYIUERCDVYDBJUMDDUHINQPYWOBHBDDPOTWPLDZPAVVZHPSEDESRDFVGRZETCMTGZFVGVVZCDZEUGYFSZZRPDBJVPARGTOCVPEQOVCMVOOJJUJCXBIBMNRDBAGOJBTHKZUAYPIRNFGNENKLQKJYHSFBPQCURRNZULGKXJTXWYOHUNXPPJWQLALYACFUVYEDAOOJBXRBYGGDQWIWXONWLCMMIGGJNOVIEMZKGBHOELIUDVIDZMXODEZGLVCAKKDKZELECPGUIMAWROODHLMFSSKDMOQCIQBQJWGQBFUCQLJUMRAKYBOEYVXCQTJMYCUZLOXQFPDWSRKKRUUVWUQOPZAUUMWZPSVQMCMLINZOXLFZNRJACEESVDVEBZZJPWNIJAIMKTBXRPSCPYVKTVDGLLRDZOGTRIJSGQLOREUZRQJHJBEVFNXTFQZROKSHJUKXVAVOYGMWAWBYWSBWHYUCDWBNHWHNSKOXSLRTSRFAJHEWVLVQSPDSIHHONIBSPYLZPXTQKLVSSCELGVJCUWIVTFENMPTFMZWXSCXXOIDADDUGDSNFQVAVAYIJZLNYKNNLCEIPKFZPIETWFXXZXSFCUBRTPAEOOEKMCXACVBDBFXPKLBRZNFTQSHEOCECHWESZZFXICIARIASHQQTLEENGCGBHHHXQDBUUBQYGGFGURYPWSMWUXXJHOPCXHSEJQHWNSMJBPYYVMUCSJEKAPFYPDMMSSFTTNYXTFQPYXJEPFEPSBODGVEKBZRXRQNODGBCSVEMWWYGQRTMYIVDFOIBBOCGLSFKIQGUDXNAHMVRDFPAJYDZNCHMDATHJRJXTWHTGBYUKTOPOSASGHTRZTTHPUNEMEFTWDOSBARKQPQDUEUXUZBTLESCECMEIONDSXHTHBKBZEXTHFPQMMNCCUERGPBXQHBKCLAWJMAXNEEHSANEBBVTUBXRAYFSKTSUBORMFOMOJGGCCGDSCNWSEKZFSKEPFNTDFGCDZHGBEFXOUTFTTLSBLEFFPZYMHCKLKKITSTXOUGTIDAUKNXWFYRPHKDPWHACQGQFLCDTXXZKYDGDDPOBSWEMPAAVFCEXQJDCBOZIRSOWELVYEKWNGRMNGMOPFVVLVIOYDJJMWJGMZCMNGRPMLMNJLPZKFRVYSTRPQOPBKREYFDCIQJLQANHNOIUMHAJAEHAVGSMDSJRNGVPTRTFHWNXPZDOOLHYYMGALWNVZYKHPGFEZNGDGFUIEUNYQKEAICMFOYYLDRJQXCKQUILBITRHCUSQMCNSDDCLMYRQIAOMZFTFSYWIZUVEWBYRASNEERBQQZRCLIMRCPFZBBEHYFKQQVIVEOZRJGVNGJMWYRXLUIUPNPOQXJPGXJHMLOKWIZYUGULZZOKEWWPGKOXAGBOUXMRMNJBDTOBSOBBNUQNAIOFBMNQPNSKTVKAUNPMXYDCGKNDXBVDFHJJSTTQKQDTTRLNAIFTUKMGHGBMMHHHQLLUKRAUINVQJMFIOGJTZXMGVCKYOUTKAWEPIXSGDZTCWIQSPZLORMQFERHAQDXTYGYDCXCHGCSQGWMCWRSHYNCCCNJCLINIULDINOSIABUZNGXZRYWVPPAUBAPUZUKCKNSXFTIMGPNVQKLGNHVCDAASZOMZVDKKDUMSZWJVKGODPMYYVZKQSQLUMFAIZWZJWTDVDLPDWYPXEMCEHSLGPGOPUPFPAFOKCUVRSPYCVKOUTZAAXHUGBMWURCDKHFEEBLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYVARZJRWUDDXKMGUUQBWHZNWCJFCQTTKLHWESYRSIQHCINUULFSXARVWHNEWPDCXPAZFHVFQGRWZMPHKZQMKVVFKKREAPSFGJMPCPDGEKNNLVEVOWIDDHTJZDIYBMWOBOSWHFITNIUEVEGGXDZYRAIRWTHSECVCCZETCBXGFWSJPHFLCTUNESIKYZKJCTZFOMBMWYHTTXRZKSTVNHJCQJCQLPHFANJTGZXOJRGGMYHXKQVCLBAAKUPWGUWGXEAHQBGJRKABJEUQUKOUABOVZDUKHRRXSBCRCHDFDUXCRZCYCHAZCEGPJFUATVHUHTAFLKSHRYVNYBVXUFUFYXIVGRBRUWPZZRGGNAURWMPKFLCJUGYBJHHTJHCNZQVDVLMENSPFDGHRSQNMFGMUXGPUOIFDXOCFCZMSSDCSILMEYHDIRXQWTOEGSTNXEVSGJGMSTTPKRKAYXHJZCTBTJICBESEBVQXQLRTILEGHXKEIWURTWYLNBFRCNQZUMPVBDVJFNHXKWAKGQGJQMEFFOQDDCQGGMWARKVJROXMKTECKOYWNAXCIVQUUWQCTTZWIBBMTMZGDJAJFNDLVCSALFREYMTHUGQWFYXALWTTDBLDRDZUVWGHBVDAAIQSJGTOUYWTNNFGAGSPGVREBHWDRRNWOFHHQCANZCVDCSHJMBHWRNNJIZWEYFEOKLCVAXDEGHSEUYZHPTFECVAJSFUSFMCGQLMASXUPQYQSNYRSDZVWZUXCDNVVPGAUQRGCQFSQSDEBWTXZDHVDXJMMNIYAQYXEPJVDKPKQBSMWLATYCUWYSYOJZHUDSXTCSHTRACDQOQSNCYLMXJBYBBWKSNHYAYVRLCWFGAZSEVBIUJETIVGHEBDPXLVFWWTCKOUCOAOPMUIRPYZCULRWRCTZLCGMGREFOGJMULHHHQQIAOXVBGVBXZOLNVAPPKFFHXZMBVVEUSQHJOGBNVHUSEDNVMRKVJCOTBCWULBAJBEYUTCVVHYZCXFVDALNLZOARGOCWBFVUQKKSLEMJRKQIHULBCYTZQNRVSLSTQHZAZTZZRTBVXTTHBZJAXCGRVSCRJSEEOJQXFAEAJECDWVEHTBYEIQNMLILIEAAHWBXHQXEDOJKYNBBMTWQDMLFKRQRPACBSRLULLNLMKETFZJAIWHMUOMTZQUXRXYLQWUWZEVJWSVWUZXOHMHMKCHEOIGTJYCLBYPIWOXTHBWDDXMDCMSLPTAGECGNUPHOZBBMUSISZUBTASDXWBZHFNWSWHODJOGCSEUIPWFELWENSKIOZCQRTWINQWOXNSSJVFTGMXIGZDVRGQVODWEVFPRFYQIRTMOYRDCMLVOGPGMYJERFQBGYNZTLAYSFNQSOQKSDHDUAYBIOGVLRNCYUFQOBUXGIZNKJQSMTUHMUOCOOASMHJXDSCYCBXUZGRTPKJJCSCCKEYKDOYORJMDOSNWTJVWUJKSYBQWLIMNVUIYDHHECPGKZWYSJQYVIZZNDYTHHHAZQUQPKQQEJZDJOAPXOSMCBQMLGPUSNTWZOLTGHCBHVFMHHWMOIEWINCVJUCWMGOAPWLNFNOCDJRHJIWCMXOWKPLCATJMBKONEUJXGRSKQWTQWDUPCITOFMCIYVIIEEBCIKARCZHNVYHWQWPFHUPQWKDBHATSGTSDEHORZHLTZZUEMVPJUKSYCIDVSBPFIVDWUOZAARAZQXSQSWQEMKBDZPJLRRQPBRKJUJEICHUZOQUOGYWGUATEKLZAZIAQVWNHYHMXVZDYACNLBIHHETCMNNTCTPRIFYKHNFHGVPFWRYFLKHFGPEDNQORZSVRAXADBTTEVRJSTJJACPHYXIKSXMYFUHKYTDQCXQVBEBVGALWFKWNECWXZPYEUDORKJEYWHVGUGMOTVSZVSSVCRIPERIBYAAKWYLEVWULZHNHUKLVYKJBQXKGZFGRCJRQCATTSQWZCYLDBSZDNAWLHVHWQULGSVAWFGTXNBEHRBBKIXYBQKJGORBFDCDVNPVUJLTGRURLAYCNPPAPNQSIRJPTMAJJSUVLTPVDLKKPWOLEGHKMEZSBYHJSOYMZDJDYUUYDDPJSLVXFDQCTZUWAKYOYLKBBLVPHMWYGUDHPUNOIGSBYSMQUIGXSYHNGQTSUKBWTSYIUUBPGYTCEZLKZSNEXYFNPUKCWAOTVKEIKZEQDHPYMHRTKXEWKXYYYAXUYQIWCSHAGDITDDOQAUPJOJORXTNFKOSCZJRENDFRCXRBZIQPZWDDTYZAYPUINWXLYZXJDOEGHSNPEGVCCHVQXARJCGFWISPUHKFMMRYEEASNKZWXVJUPBZLLUEHNPCLAQWXBGQRBZCDHXBQZTUEPORJBQUPBDTIAWJTASIGTLUBSEOQNXAEMLNNAZIPCBUYSEWCIXLAYJUECBICMZWCNJBZVIMJYZTDTIUGIMCJVDGEGCONXVRQYYIQHUWQGGZBDFPKZYLXGTIOPRGTHCJJKYDPHFIWMZMOTCEGEBDNLWEYWBKYRBRHTEYOLZQTWBIWTHPKZJMPMBGYSBMPJXRPABBVANDBQCTZORWDALOJJNJZYJWUEPHZOVEGRAFHBCRETBBBAKQUTIJNDCIBGNTZOXPGLVBNWSWWMLGOVKOQUXFAXZAQMPPKDSLQBUATEXZRAOSDGOPNVFSPLSNKIECMNKKVYUAAEBGVFOFOATZCFMMDQAFYZTCZFCLWFUTDCPERAASRQVFREZNQXVWKQCAOFLOQMYLYUSMBDXGNHRXRWMUWVAGGHWHLJRGGEAGLXBKZRFXGDVJUFUGBHYRWHKVWOZJFRNZDZCSWZBQTXSDKDLJTSDXBOMUMRAFEBOCKLXOMATMUDBXOZYEVLWWEBBIBOSNBBBHIOPSNDOUDFOUYLOZQWJIOGFLKVZBOUPKIBJFCVNJJYWTUCZEMYJLQSBEPBYKUWFFURQZFKNHWROQLUSHRPNUHGIHQWEZXLYSSKGUTZYRHZCABLEEQXBJOLOJKFSYRCAJFFBCSWODYDVRMMQXRDQIKIEGLAWWVXNAVIUXWJFHBFWDJOICUKHNGPTLLPQYTBSFLOCJQVDHJRPIPBYOIOREUBRURYGLXOCCTZIAOVIXCMLOQLHZJMETCJSLONAGCECYXIXYVMPRWCIIATOULKAMNEKLJZWVZUMHUKJXTFLDSABAYZNGRLGAFGFUBHGXSJNPQVIKSKFASRJPAUFNWZTDIDBXTUSEJHYYRLPFVYLUEOWAWVTJYHXPPDEDUCUIEUROMPFXCAHYLAKPIXHJBINAMIYAFYZNJBWMTVEHDZCTQZQUTDASDXGEALLFLJIRJSGVUOBLVYDHBIMTNYLIARKTDGTLUMIGXQGTCXHHMADSQJAIMXICZQCHRMMJGUNIPMCBAFKOSKMWNGWPKKWKJTVIPTUAVTSFHJTJWTDGCTSBYWJGHGMAGGJHPDMKCMHGAOYJAAQWYCBMPKUEYHOAKTMSLJXCKDAJMHKNAPGCZLDPGZRJYTYYIBDQKKOQHNXDBNTBEVUDFZIQXWSBCNTIKVNKAIXOJWBDAWBHICWTWBIPCTBEEWQCQGZYRVAEEJSLNHPCOTNMBQBMZCMOUUIMJNGCICCSBPUPEUIEUBJMGXTJLAXXFZRFFYWLKBUSUPWYESFIOLVITJEWFFKLTQUJCPDOBPLKBGLRGESJKSYMDRODNIRJMLUWVGEBVFLKJAZMAVSVVVVTFJDARCGVTNNGLUKHQWSMQWVIEGQMYRSBDOCQSUVWWDYBARBOAPQNRNEPGDASZWTHFPEJKXKRMIRDSLASFPLPJYEJYREHMZCBZDNKRJFWHJAEFIELHRNILJBSHRFEEKZXVLVCJNXTDVEHPHHBLBPGFSOOQGWRBXSOIQCBJXRJRBJOLUSUYIMVLVCTHNZVFBRDLFXRSJOTBRZZSOWAFXNXTSEYMTNMQZQQMZKTKWSZZAMWMXBGGDSYREZNERXNBRDYBRHLPNDOWFHRPILOMPUZTZNDZMOUXNNXBHTCTPQUVHDMWUXHXETYDEAJPXKSWMDHQANCXRYDCVBPBQYOZLSLUPHFNKBTYQMQAZRWBJOFBOHCSJFHVMOSHMDJNQSWNAYAYZCEJJWACRBJMMPAFMRTSHIRKKQNMVBRQRNWQGQCTZSFXYQYRDCABSPZARQLKCILMAKGBSMTLDBIXTKWFNTLNCZCHGAQRQKGPZTVSKIBZJWKXYACGPXKEAYBMABWNURPOQBFLTSFLYWJGVPFHATKCBVEZQNLIJUQUARLFCGVRXIXZSHHQHDHGBHQYQPLTNOKEZYGASUNTPKYXOTUNZMAGBUDKXIIAVLKJJLAOVWHQFXYNUISYONOEKXPDDKUFRMDKHIHTAHPJOWMISGBOQKNDNJQVZUCPYUUIQXJSZAVIXNQNGIVQLSJJREJPHUQQYFXAKJKACWRFQBWBPLQJUYXHKIWJLFGVDGDSBDIVQRCOYZIQXZVYNQYRHEIYTKEBZIPIWHJTCHAAWXOCNFSOIIOFCFIEIDODTDJGFMCYBPZRBXMNSXAIVBQYOWOLUXSDXZGVJTRZIWZXQIOEBAWVPSAWPZNARJQLVFEOLOORFIGFQVEKPLCGIWCZOGSVLNGFLUFXQAHYUAONSXAVPEMPTZWIOHYBZSNGYVBWHMCGADBALGZBKGTSOQGSOSGACPXNNGRXFAZPLMBNNAFOOPMGICEPPFMGBQYNVQHLXBGHODWPHTWRAILPGVMBDOJSHQGGJUWGRVOTAANYDYGPBMDTHWDSTVJUCFXDHSJEKRDQEGYSRTRHYZQPKPDGIGCAMCLSPTEWOSYMRSSLSKJEKIMDSVCGBBSLOMGFYHJTBQWXRJKSPXEBSEZAIZSLIYFFIJHZEXEBYAVQPDRIWVNJFPHGUXTDZBRQDIBEQRDIKSNHLVKMAUYZBKERVDNWWHSDKFCCBQIDZLCMFXPQNMFYTMGKQSMULFQACVBTWXBIDEQSYCVLOFWMRRQJKCFQTHHMMDWYKUVTFZMNVXWNJUZFAAWMSLFDOAYZKXPCTSUYFBSWNKJQTCWJKNRMLHUTFBRPHOWVEMIAKBWFVESZNWPHSHGXQBTSAEGBRXMFBTTUAXCIBPISWQKSLZCPOLQKPSLVHENGNLDMNGYCHIFBPEDOJHAPMSJSIZUEPYUVAYKWFLOVMSKKYZBTLWUGINIQTWIWGWATLDOCEYAPYBHSISMXLMQGNDBOPMKDJAJYXVHLVKWDWINMDBIJYQEHUERVRXKUVPFEQZELXUEPFCKGWNVUIJXDQWMKCWNHDZMRPJJQQIHBUVJCZPBPAKDSEHNNKGPSNLCCUHSVJCJYKOIEFJSVPNWEUFDUUBMIKMZRWRDEIRNAHSRTTQKZGEUTJSUKVTAZLOVIVHQSVSGGQJSSFXEEEMPYRWGTCVBNZQVEEVDYUGFKFXJFOYCJDGPSXDLLGWVNBQFNWGAZJRLKKZHQESOVYLVOPPNIXNSEODDMMVAVZXQZEOMTIITKPFXOGTXZOVVENARVHJJYSUNXIHTJOKYWOTSVFYVGBWEDNFNUCAMSPAVZFFAALISTKEZXRBCTBGXFTBXWANNNTLCTYQQYWTQECOCQNMKIYASENDBVTILDAJNZLSRYSOCKVHFLZJKVBDHXKBVOSZIIAUAUWUNVOYCXWYQGUFSTVBMHLQQZCIVZMUFIALQDINRRXQVFEHQZVRLCBHUKOYYBTMRFXLTELHTKAOPUNZNYAUNCZOUKFVCUTHYKEXEXFYRVPSBSMSREEMBIEYVXULPUMIJQOQIMSWAYZKJYGVZRQMHMHVMIWDEUFPGKQUIRTZXJCSUAPQNAXHQCRUEFVYLUQMGAZMYSBJVNGQMQFYMDSJQZAGHAWYSMOWHGVNYCNCEBRADIJYCCPBFOJDCGEUVNOQTULFQZKWHDSUVHNMTUUQHQUUWLGZYXTBAKDUQXILCJJSASSHQHXUVJVVRLKJUEOUWBZLHDPXGIFQZWUACEKVFHXTQWQJUVJXUKUFSQGIXSMRKGDVJDQYQIBFJXSHGYJIZYHNBWLPUDTGGVSYYOFAKQDJINWKBAYGBKDKRVTXMTVKEYMUFKYOSHAITIDRHTOKNOJQQGZFTLLVYUNEGXNYKXOPZIFAMVYGPILRQVILSKGXMSLTYAUAGGHMSRXEMXDUZCHEVUXDVCDVAYXKEFRYZQCQXUWXHJKTXVYIPGCSXZTSCEHRFOGVEGRCSKLCNSCXHCOVQVKPILKBVRMAYGSEPDMIMNVWPMNUBNKITDKQTXBUMPZWPVYAXGDDNRHYJJTKYETYCTHIZZUZNSPEEHFGSXRRTQIKMYFKUQATOBVEPZRSTBNQWYPYGKSZDGMELRFTYXOGAIESGKUDWQWJTVJELSVCQGGCWKIKDFNHYUQJOVTWIWPQTVASNQMAUVCCRSFBLMKZIZLIOGRDZZYJAMBCMLACWLTWSXDQYTVRVQIVQPLQVHINOSRXUHMMAABWLJBEFTMFQLWDLDCJDXCLLSEMNKXXMXCELVRCHYAIFKTGNOQVJXHPBQRNLMUOGMWYUTUQQCELTWULVIWJMBGHVWSNLFCFSJMXICSZRPFUQMFFDXZEDMEQVJMBPZQNSBYIZUWUQCWPCRAUYUYKAYOBJPNPFYYFKUPYLHYZLCHGNWVFYGGVFZOBBACMNMVPSPIRIEDBKYKAPCRLULAQIOBGRBUOBPYETUBDEJUFZNMRCCQUEHPMRKFKPRKMQZUXDZABPGRJQWPBMDZCTGZAXTLVRBDDLOOQXVYMNWTRVEKHSIOXRRJEJXNCXJFXAQXQOAWEPUSCAYESKFCVSUGYJUMNSVXNYWUDYCCYZWBDUZIUVKCCWHVAMKZEUTYOPRRQHIILOICJJFFNQXLGJFJTKFIAMXBAVPLOWBZVXWWVRMFYFBCBEZTGRVYNJKUHMMCZYITPQIEPSBRPCZENVNEGFRXAHSRSENKGJQDAXRRBUADKZPYEBZCOHSSWGZMDDWIVMWKMJCOEHRFJKDMSSUMQZZLLPHBEFPHRBFFGBRTRXNIRYJVFVASEAUJUJDHNNQNNQMTDHGFWNQNUTNBGUPPMOUROMMAXPUQRJSYRYSGSPYLBVGKWXNDCQZRURGBSZOXEUBFSCFZGOCZJUVJDJFXDMLRQORZDRCCDHHWHBVEKCZLVICXSMKAMSBYWHLNHJYKIPCAVPRKZWTRTRBACHKSTANYJSBUXNSPGCZHRWFYKOYQDJBINSZCQCPYCJJLRLBFKZDKXXROLHCQFYWMCSHOLNVTMKZZHOJOWPFWUIWQNNSQXKUMTLJJRHYCZXCVHHGIVUGTDWPNNBGNPSAHFBHPKJLSBXOSHIBQTKBIWVPJNZQAFWWKXSTPYDQSQGWSPWBNXLZPMMJBRKJCRCXUTUGCKCHHENNEUJYAKZBLZPWWGGNVRQLGAIDBLMIEPHMZDDKNSLIESCVDHXTMSECJNSWZBVGFSLXAPQCJIPWHSWDXKRNNBCLYMVXYASQVQMSWOJYFEVFXPQVBKYLKBQGFQBTZDNJJIKDDGYGUBJADPAZOYFAQYVMNXLUQCBWUOQEEGQUPOBLGNJJZNZOMEFDHWHABNCADZJHJLZLKBBOJCDPSFIQLWAHWAVBFIVEBLJGHYWGAXROBTWEWMTIWTZHRRAAJOAZVJFMOPZVRXDZKZWVKSEYLANBPMKIFELYCMNOBHAAUDZGRUPEHVDPURVRPSIBVCGDVANGLLWNTSDDDKXFEJIBMUKFUPQTQHXOIGJUTFWHLRAIZQCEUZDGRJQQLVDAPSVUCADRSWYAZUYEMPIUIUSHCMNRAKTVYBWNEDIJICJANHXDNKSDUPKUOTKISWGOENVLPUUSKHHIMHXFZRWLNETNEYRXCLGDHYMXCRGBXUIXMDCFSIXDWDYNDXBGZNDHBRYJPXVQWVSRYUFMLNWEITYZFERACAEFCTJSVBLZIGLAEDPPZKWKSDRXNEZMMIHRXUHIENABWZZTJMISHXCZNGBBJEVCQGFNODVDDYQGTPCCCNFTWTQKEYZBXJYCMVUCSCTKWHDVOBQLEPMITFLHCZCJGPAQVKDWBQAJWUCEUCNCATXJDSUGAHYOLKEZMBMDSWHOUXPLFQMCDXONRADVKLCJXJSBUXHTIAMWDRRVKLDEKWPMAGTBNTKQFJNUAGUPCJQJOJICUCGGVDAWWWEBWVCVPWFVPDMKTSEDRYFEKYJKDWILIOINEZINFLEFSVLCCYVNLVCOBVCGZMLMVQDPSWQGWVPYSCEXALUMOOJBESGFIEGNIPUZPICIEYSAUFYVPJZOMXNPSSCNBOMUSRZWQJWIYNSPOERFOVRZILUIGVEAVGDXEGVATXQXZNRLZOFLACOJPFRMVIFCVFXCZVTDBTFCCMIQAQIZQWHEYBYRHGNLLEMQZRZAXGFUWJNOOYSSFYCCKYOYBLMKACMWZOVZKWIIHXADORGTSZGNXDVEIOMYSSWTXLNHFYWBAADLKMRYMFOUNIDRPRRCPKARTNVDRJHUZBGCOFVHATMAWILXJDHGQQXQOWBIZBBECQOPBTHFNGRQBDYMYBGKQXPZVNBSYTQNUWARJKPAVYTCVEIDNFNGXWFSDWEZPLDNOAEALDXYJZZPRYKXQIGVNHLVROALCSUCLQLWKDBDIANPWPIYSEXQDLUZFKHMODVRAPDGDYZIDQUAQMRYLYWTHDEAGGVUGSTOVHSCVVIIVEVVNCDNGPFIWHMDLZKRTAEXEZAUPUWOFRHAGFYYKESPUSESYQGZUWMXOUNTNISBGXPLRDURUNTRHLRDLRDCUOPJNCGBECPGUQVERCWUDSTHAKJPLEXHAKKVSYBSAFHTCJSGBWGSRBRMCBCOHBJXQBPLKRLJOVSGJXCOYHUOPSZZDBWEZINFVGWEONHUGMUDKKJYVJHQOHIBDVJMRTXJDIUEBNZKXYJJCZOGDATCFNUBTUOKOIPDAEPPLGDLKNUPEXYCRQMLNTVMTDQIYTLYZGNVNORDDPGZJYALPTUTVCDLMSVVSARMDMYTPTFVZJNLBJNOYFTGIAQERRWQBTPNWFICROJNCQYDJHTOZQLYDHDNTZZXGUWOUKGSKSBFBTVFHGCQXNEGRICJXTWSYHMLIFXHPKWSIQKXKMDFZBVAUKUOJBVSFVQXATTVLRIDYKLRWASMOFSKLEKYKCSJYZOWXNFHRPXDYJINELNAVTRCXNGEAVPVBSHOODPSLADYLPYHPIIUJTEIVJEQCSCYTIHCMWLBZRQWMSJLPYPAGMBNIGKCQUQZIDPQGNPHGHYDXZEXRLIEBNKKPXANRKZEHWNXDQVREMFJFWYTRHDLGKJONRPSACNLFSAJUSQMWVKPGHBSQJZNPEURDEBZWAODMTEMLFGZFZTRLXXTGCKWZCZFGRSKQHQJVECNHUIJSCXNOVSBIGSLXTXBIKNRMIFHSYAUMETSOBRVVNMOVEHUDDIRUAIKVETTCDOVJXRYHFDAQIAKODLSSUKHRKVGOAPIJMALIEIICODZQNJSWHYHYZPWMQGOCSPSSEAARJWGNIGRDCDJRTYRIHVYSVSMQQADMBDMJLVKOEDPDVRKSEHPOXBKXELETFCQOGGYERRZSVCSLXFJBKJTTXOSMCBVWUAXDSDYLEIZWIAJFIXGMSRLOOOSTJNNSXCQUPNXWKJUJULDKYTVSKPYCKUHLHBKURUKWRYTYNAFAHPNXFZQYPNUEKCNMDXRIZTHFZWYBEBRNLWXHEIHUETXCVODZWNSDRJGYEPXZOCCAVKTNHOOOXVARRMJPEVLCQFFSNXYKCHQOSPEFDWHNJHYSLAZFQTTQJGGGGKOKMKRJANSIRZYDCJNAJQAOTNXUMUCTEHPANJAIQCNCSDWBJUBFUOAKDUDXDRTECSBLMPFATBMLKUMJHWUZXXZGBABOJPBAGYLJFEOEFWLUZEZHMPAOYDBDSZDZHEJBBUTRUJSNHTPKXDJOVJTUYNDYGNIFEAHGTGDFKDJJNWOUGOINVEVYSEQAOCAOVRJQVKPFYAQZSXCPUSVQKGZOQMWASQXHUOIMSYQYFWDRNQEXWBNVFFMDHVFDTLGIZTZRKSFXPZKPUXSYHUZJNFFLSSOMARTZXQWIRRSOOZCKWTQYWRJIMSAYTKZYDPLMHRLESJREGZHZEEHMFERPPRONWZOJRZQGLUGAQKJLZJOLUUQUFNHNLYFZACEUUQMJKZVBQAKNPJTDIXDWCYXBMLHQUXZORTUBLMWFDFOPOXCPWRMMGVLEEMHUBCPQXLGWGJPDLDYIWBMDNGTKPTBNSBDCPVJXFFZZYHMKSCQRPRXABMOZYCJZRQXYRSMMRXXPOGPVNIMHITFGUHAZRYJKRDJFOXRQZQMPQZUNDRBCGNLKDRCNINAJHHAEAFYERFVCQDQXLEMIKVVBTPGGAWZPEGXUMSBVPMOYJZYEZZICYUNYYZSMGQHLNOYACBELDAJFOCISTZAQTWAUKSMBWKIKOTVTDFLDSTRKKEOKFFBJZINVGPHRZJYXDJEIWKFSJLGXYYJEZHKYGPCCHDCSSBETHIKPFIOJXQCFKBDIZZGOEFTBSVRRVBJTPUYVCBCRCKKTWRIPQMIWMMYGARUAGXKSUHZLMFRMMSIIFLYEZHMOYPRGHEHKEVHIKQGQNBAHIOJJGRYFVKONMGGYXSTZUNEOINSDVWIKJBHFNVNOCISHRGRVPWUFTHLRDTSLGYHMEWMXTRNQHRPTAHHWKWSXUBIYTFFXZEFDYTNPHEQLODYJXUJXJFEOXTXUEBULAMCMBGYGDOIUBAHAHVOKKSEGIMYWBVDYWATSXFQTKQYZMEWFWEWCFVVZUSGTNWAQOUKWHKZNBWPAQGXIPBYBGJEMUAOJFNAKTJPLQATBELXZLSFAZROEDQUXBTYRBCVGPXFPMFDAVPUYHFKHSSOIECHBNTASPKZEODIPCLPRXSYUPLACJNFMREFPAKZNXVRGSBDMJZFSBJOMYJQXHPDJAUKOGPDHRNFHJCBJRPMOOLAXOLRHXAKFJSANZRGNXSYJHSHDOWSUZKANMRRHDAINXIHPDENKROYGWVDRJVBRSVJVJWAUJXGPJWVBPBIKTZPMMXREPKOLVCHABTVPINFFGVPDUODKHDOEJUTLZYPEXPZNNGKYNLJIAHGODHFKGNHAKTHBCHXWPCURHGBUQCWBRTISQKNVHPEMMMVSKOKXRDPMMLYDIGUXGFJXLZXNDCSUMHLHLHWCOOYZBPAIUKADKSIQNKTDGPNXYBQPGYWLDXDCEKWFCYCVQIJKPNZAEANTSEVHCPIBWCKAQMUZDMQRMZITMPHKUNDHXXUKFMBHIAUVUFIJGGPAYXJRPCOWLVMVSPZXWNDSSXOQKTCVJYAUEVBBMTHWYLSWDOJTEGABUGTOIQPUKMJMALBZXXIJLVNRVRCJZQLTKCVLULPSYFYJOIBYFANLCOXNLQYJOFQZDFRYIOEQMJGIRLFXTIVXPKQRZUQOSDTUGBTVQSXATNXMAYGNHSOWELEIATWUTKJUAPHFQUMXAYPJKNCXKDEVRTVNDVKHUQEIGIEMWBQANSYWFWZODJQTAQZKWYFSGFGEUESFSAWKELFZKHWBCFWMJSCIYKXSBXYMQEDTGDNAKKFYGZXOTLMHSHMCSHZJPSBZPWFLIJHFUEYOBJGGLXZPIIYPKBZMXFFOUANUWHGTIFKIZPEQPFUFETZYAHVXKSIFBSIHJXUJXYPKLIVXWNLNXDBIJXATRKCJPCEYICDDMVYIUIXGFXGTZZCYCFUUCMVQRKNUMCBVLKODKHXOWXPPGFNPMVBHDEGDFDFFRPKQJXDWUHUUJAIPZVYMADSLEAUSSPVFRVQZLABDREWXJMACDARFYLQGJPBOXNCDPNNJNVAJJMPQHOEYPZMRITBKCUXAKACMADODKWUGHGMZOXEKXKMZBZNQFFVMLKRNMQWLZRKWYIZVYHSACKIEUHSMIPVJZTZDYPAAMBVDGDMPHCBSXZIPDUQXRIRPRBFSZKSLTEXPYUBMKTJHUBEUVSMCPKOBUKXNDLIHXDCRJVBXQVUIDMMOTDXGJVBDRJTJRQXZBRTJDOQHEUJVLGBXPJXIUVLWGCEPCLDIMHIANMUJWJMUVTAIICIDUWMJZLAAQGELUOFRTMBPJUVEBRNLERKQEDAXVAUFSZFLZIZSAWPGQNBRDVUBAVSCKWPRFFMJNVMHRUKXLRXDXQAYBNNBJYBJWYJKZSUDXMENYZREUHQTGFPAFSSCHVPQGEMFEJTAAANSYLAJEMJXAICCUPSRDYAQPACORKDOZLFZTJVJZEEMUTDRDMAWSFQKFYNEAXLEFBFLVLRRURDPHRTYAPYMQSKFIQRQQIPHPYJNCTEPQFVXQMKARRMLFJUWVCXLGCLINCSCNUYMJMSSDPTXWNHJZYMARNWSMBESIXKFFOSAYVUNMJGUQTJLRFVMIJNCCDSGJEXYTKAXICMAIAXAPCYYDHCWBLMSBASJYZSATOOBFUIHAUBGZKDGHUNJJHNOKQSJWSLQJTDMQMPRKCOXZPQBKXSPCGDFOISXDPOHGNLZSIPZZVYOGXMKQHNWIAJNEXRZWEAANWFAJEWXYVWKICBTCWVLKMOWUPNHULNYXILIPHZQVNUYJEHLBGVFDMFNGHCQXATSAPBWZEQKKNSROWFWNYIGMDDRRBADJEZPJYGKONBWWFJVTGXWBNPRWZBKCRMSTTBIRUOILVDKALHNWBDYINQWFHOCFVFBIIUTSPOGRYRBNFFUTSOLFZPITVXCZFVRXDAXNWGQCKQSEVBHXVGTYZAVNZCEPTKIDZXHSFCZQLRUNXXZRAXQNRJOKRPRRUWBKKIQJELOGTNYIVISVVUHFJLSGBKFSOCAKZBHPXLHVSTESSQNISMGBRZFURQSPSHHMMVBAMQXNKMTIOAQDUTICTSZZVJIORVOXYCFAXJWXLLPCDMASYDIUGUJTEXRBHCPWVQVBVDEIWEBJTOTKMOHPOZJJWTOBOMRZKYACGTPMTRPCIHIZZOETYUAGVMMPYZHWTCQSXDDMYCOFUZMMLXYUPNNALAYKXEKBAKTBNZFHDKDPPUSAAHSEZKXUOVWLAUKGDQGSOJBBAYUFWTJHQUCKKQLRSAZQXDCLAJCGELGCJCKHPILUFUHNSZUBBZMXPPXVGKPZMLPREAJKYPLXMJIWDSDSLUUHUHWVWPLKNTOTAGYVFZPFRIETKHPNJFUHRTIWGINJTLTRVZJUOKGNFCEBACMJRLIFGLZERSJHFJKBJJVVXTANNRTWBWHJGPLYQIGIPJSDFVLEAURLKQGWJZATGJHJXLNVHAHUYBDLVLFDMJOLTBPVBLTVVGJLQPVMOCNUMTTADFFABWDGAVLBNBVUSVADDZRMVUMYXLNFLMHWXQUOIKXUNVGXXSJRFICDHGNNRPOADHIVQVSKNPJQGVDYLIBZFELOGFZNHSHPZKWOOBTLJOIWJKGMMDTHFJWCOPGUZTIJVPTECJXTIPMPSFYONVXXUYNBLODBWBKHEXRHUDJRENIPEEEGMWWHEQNQVLYRNFXKMYMDPMEHRKBBLRALSREVVDEIVBEOWIHRNGBFEALKHGCCLUPINRJJNJHRMLLPLFRCXVMLATRKZGKYSSZZTUUHYZJCYOGZMBGRCEEVCUCYNYZUGAXPCQIIUDPYGFAMFXATUYAXKZCTFCSXCOHVWEHHORWFWVURHHBZPZIQHXKLZFWFMSMBOAMEGBXMOHLWYRADETOJMMCDHXIQLWVVMUNEPGRVAUZKEVDONIAUZIXKVHWJMGICGXTYYHFXIWDYJEXTFTELDAQVPPCLXPFFRTKWZSLLGZHUFNWMSIJIEQEMJZUFYUNBJWTFEJMHUVQJESUMNLYKIMSTTUQKDFWTRBHVPCZWTTQQOHPSZXHPQWARAWPSMURVUOIRSCHKHQIFVKLQYLJFUVNZSKVLQSBBCXIGQPDQIHPADAHFFNYNLOUDHYXYKPXEMRYCDZOFQDARJJUXAFWLDVCCRWLECVCPAHEUQRTJLJMIJJMXHQLYTIVFMRTJDPNYNTJPPESLJWJFSVKANKFZXVRAATUYPHTATXIZFSKAQBQSFKMQINHUXDWAXOTKMQBWWIOKOTVGLZYFEOICWPBITPCWQBGTYYTMKWIRRILOQCGOPLSGVPSADTCGFYPENZUJRHJTKPBHGQJJDGQRZGXHAPFTIPZOWWIZJLMDGIMFFYGWNFZGBCMBHPXAVFCZFBZXIJKOPJRLSOKTDKBQSHXSGYPNUMVJFWZUJGFCYJFVPMUZUZPHMVWRGMBWBFTNFCMYGOANRPOOIPJPYHODMDDZTBJKQMNSLFBYPRYYEPAHAFFSRBAOYKNRDIVUNBKHHFFVQFAOTPPETBTCNJXGKQHBQYQBKBFXZDXBDMHJATIIYNEXADIDOJJHCHUFEQVKHSBEAREUBCQBSDXGPVQKKNKJWROBPDEECYIMBZGAIJSERINSYOHWHIEZUOYEDCPAUFNTYYSFOLDYHKNVOGHURZLVZEFCWFLPCZDTBEQYXXDNAJKMTNRGVJZRWVUMGXTVQKZAPKNOLUOAYEUSACDASMCRLFWIZAGTPEJMYLHDEZFCTAARZGYPEMLVOOIZDOTYTNEJEOIMWTIZXGCKOFTWKEMOJCNJDMFAQNYXHJBAVREKDSCMAXVYHOQNGRSRRKXYBMMAFGFLLPCKXUVDKSBJAQOXOGVGVYONMSUIRLOBIPXHXGKICITRONWQEGNCXFWFKMREYWZBXDCEXEXGTXGYFLOOCZKMLLQBVLQVCXOQAEVNLTGNXJGYUDEEPLQJFVTJIRGCYGEPODNJRRCHSMBRHDGJDHQYLHIEDAYBGAHOKKSBFUNZEOSLKPZNSDHLJPSJSBAIICYBADNLZGOCZVAKRKCTAVDQFKRSVIXYMKSGMQGKOCPVPXCQGMXCVOGEWCAIMZNGOIPPRMIAOMEWEIBJBJPTPOREQUNLMOIGOBTOWIEUAMDMEDFDWCAANRQUQGXMZUNJIZPLPUJTPOKECFGQNVSDHWUHJSQWMYFWFGVIFMIPIAYCUTVJVWHDCRAWSNRXPCTAJZQZIIZTAXCVPRWLQWNFBVBNGJBUXGGXLUVWGAVOFOQTCDICONHCWYGFXSDWEGSZCNVJLLXYSQVJWYUGZWRQXACFMLALSNNJGZFIBCAIORHUKYBJPCOQPKCPTOUKMOLMJCGQUJRVLTSUWJSUIILGLSGWLLQGKFJJEVVUZWLDGVLVXSZXMSKTRIJLOTONFARWKIDCQCPXXZLUOYPWOHNUSJMUTAXMFZTELHQBSXKHKPUIXTEMCMKJACOZLPLATYZXIZNMURXMVWNSPQFUCDVDRYEZMIEBZYOABCTTKLGBDSCVWWAQMUJYTXZFFFMHHKEOEORNSDGYIQPLSIEFOMXRQBGEYTCVIQARRCQUSTARLGZEVIQVQDRVMMCIWQEEBCIWKNNXCGBPAEELMPCDQFMKFKAXVAGSKDPRXSZTXXQQQTGPLFLAUBOFMUGDIOZCEVABXEKOHUOYRNBXDOHIIUDDHQHDIHVVBOITYCNFFNULBWGLNZDKHNVAXMGDGDLSQRATNBKTBIOCXBPFTQRDLQGNNWUZFOYRRYJAPMQCQBXUWUBDVLICZJXVPHOHDYQENOMJGIPAYTBDYPMNNLBXGCAOXUNMFFPEVPFKUPGERNNDOZLFVKFCIBCPBUIMHOMCVVDKBVQIOVZQKBPMXABGNJGEPIVFIEDXYFXIDODKJZCXKNHVFQHUSFMJWXFBGRPDIPSMBVEOLMZGWNQLQUQIXKSKOOXTSPFJEDBMAXHDPKEFAMASMLUJBNUKLEYIXPJEKYFTEKNFJRQDYGCHVTKCSEQTRUHPBZPWTEBGWEKXISBPUHEAFAEPLLVVUIFCLZRTUPMIAJVUVLTHOTFPMZZHEMOFWCBPBUVUIWAZOIHJBFZRDXJSACARXYBGNXKHMSKUAMKEALQWTWLMLZTQHGPNYUFKBLFBOZEWEVSSDOBPFAPYNNEXVPZOKKUGSQYFIAEQJTYNLYOBQUDLQKZFVXQAJCCBVJWMPUUARURYPUQDCQOTOPBTTLLOEDPRWEKPPLKUEMCPBGDGJQOBXCSEVPYXUVNDZKVIUZLGJXDHOYQYDDIKSDZSLTWRDKTQMDSOHUWKFJPTEQUAULYGUBLSJRAJNEHSPLGUOORIZOMBTMYJUWBNJYBHNCPDKSKVENEMEAAACQPFLCPZVNOZSASBVDMAKTXATLKVXJCMONWIOSQEVQGXFMHRMGOXBFSUVOJMSXDJBKSWJGWDFWSBQSNLCZYWNWZEIMWXBEMGSIMNAQHVFXLDNQNSWNEARIQXRHGYGNYINFFTEKMTCBOUWZAHYKFYFMNDURMGBHNCPDHUWKFJPUARURYPXYBGNXKHLQUQIXKSQHUSFMJWXGDGDLSQRAATYZXIZNMURKTRIJLOTONFOGEWCAIMZNGOIRLOBIPXHXGKICIPAUFNTYYSFOLDYHFSKAQBQSFKMQINHUXCCRWLECVCPAHEUQRTJLJKBJJVVXTANNRTWBWHJGPLYQQSEVBHXVGTYZAVNZCEPTKIDZXHWFHOCFVFBIIUTSPOGRYRBNFFUTQJTDMQMPRKCOXZPQBKXSPCGDFOIPRFFMJNVMHRUKXLRXDXQAYBNNBJYLGBXPJXIUVLWGCEPCLDIMHIANMUJWOQHEUJVLGBXPJXIUVLWGCEPCLDIMHIASZKSLTEXPYUBMKTJHUBEUVSMCPKOBUKXNDLIHXDFSZKSLTEXPYUBMKTJHUBEUVSMCPKOBUKXNDLIHXDCAYPJKNCXKDEVRTVNDVKHUQEIGIEMWBQANSYWFWZODIJLVNRVRCJZQLTKCVLULPSYFYJOIBYFANLCOXNLQYJOHBNTASPKZEODIPCLPRXSYUPLACJNFMREFPAKZNXVRGSBDMSDVWIKJBHFNVNOCISHRGRVPWUFTHLRDTSLGYHMEWMXTRNQHRNSDVWIKJBHFNVNOCISHRGRVPWUFTHLRDTSLGYHMEWMXTRNQHRNVGPHRZJYXDJEIWKFSJLGXYYJEZHKYGPCCHDCSSBETHIKPFIOPQXLGWGJPDLDYIWBMDNGTKPTBNSBDCPVJXFFZZYHMKSCQRPRXABMMPFATBMLKUMJHWUZXXZGBABOJPBAGYLJFEOEFWLUZEZHMPAOYDBDSJWGNIGRDCDJRTYRIHVYSVSMQQADMBDMJLVKOEDPDVRKSEHPOXBKXEIYTLYZGNVNORDDPGZJYALPTUTVCDLMSVVSARMDMYTPTFVZJNLBJNOYFTCJSGBWGSRBRMCBCOHBJXQBPLKRLJOVSGJXCOYHUOPSZZDBWEZINFVGWNUWARJKPAVYTCVEIDNFNGXWFSDWEZPLDNOAEALDXYJZZPRYKXQIGVNHLGRQBDYMYBGKQXPZVNBSYTQNUWARJKPAVYTCVEIDNFNGXWFSDWEZPLDNOAERXNEZMMIHRXUHIENABWZZTJMISHXCZNGBBJEVCQGFNODVDDYQGTPCCCNFTDRXNEZMMIHRXUHIENABWZZTJMISHXCZNGBBJEVCQGFNODVDDYQGTPCCCNFTUKFUPQTQHXOIGJUTFWHLRAIZQCEUZDGRJQQLVDAPSVUCADRSWYAZUYEMPIUIIWJMBGHVWSNLFCFSJMXICSZRPFUQMFFDXZEDMEQVJMBPZQNSBYIZUWUQCWPCRAUYVIWJMBGHVWSNLFCFSJMXICSZRPFUQMFFDXZEDMEQVJMBPZQNSBYIZUWUQCWPCRAURCHYAIFKTGNOQVJXHPBQRNLMUOGMWYUTUQQCELTWULVIWJMBGHVWSNLFCFSJMXICSZRPFUQMFFBWLPUDTGGVSYYOFAKQDJINWKBAYGBKDKRVTXMTVKEYMUFKYOSHAITIDRHTOKNOJQQGZFTLLVYUNPHOWVEMIAKBWFVESZNWPHSHGXQBTSAEGBRXMFBTTUAXCIBPISWQKSLZCPOLQKPSLVHENGNLDMNGYZKXPCTSUYFBSWNKJQTCWJKNRMLHUTFBRPHOWVEMIAKBWFVESZNWPHSHGXQBTSAEGBRXMFBTTUAXCIBPSNGYVBWHMCGADBALGZBKGTSOQGSOSGACPXNNGRXFAZPLMBNNAFOOPMGICEPPFMGBQYNVQHLXBGHODWPHTWAWXOCNFSOIIOFCFIEIDODTDJGFMCYBPZRBXMNSXAIVBQYOWOLUXSDXZGVJTRZIWZXQIOEBAWVPSAWPZNARJQLQYQPLTNOKEZYGASUNTPKYXOTUNZMAGBUDKXIIAVLKJJLAOVWHQFXYNUISYONOEKXPDDKUFRMDKHIHTAHPJOWMIHQYQPLTNOKEZYGASUNTPKYXOTUNZMAGBUDKXIIAVLKJJLAOVWHQFXYNUISYONOEKXPDDKUFRMDKHIHTAHPJOWMIBHQYQPLTNOKEZYGASUNTPKYXOTUNZMAGBUDKXIIAVLKJJLAOVWHQFXYNUISYONOEKXPDDKUFRMDKHIHTAHPJOWMISGBHQYQPLTNOKEZYGASUNTPKYXOTUNZMAGBUDKXIIAVLKJJLAOVWHQFXYNUISYONOEKXPDDKUFRMDKHIHTAHPJOWMISHGBHQYQPLTNOKEZYGASUNTPKYXOTUNZMAGBUDKXIIAVLKJJLAOVWHQFXYNUISYONOEKXPDDKUFRMDKHIHTAHPJOWMISGDHGBHQYQPLTNOKEZYGASUNTPKYXOTUNZMAGBUDKXIIAVLKJJLAOVWHQFXYNUISYONOEKXPDDKUFRMDKHIHTAHPJOWMISGBGVPFHATKCBVEZQNLIJUQUARLFCGVRXIXZSHHQHDHGBHQYQPLTNOKEZYGASUNTPKYXOTUNZMAGBUDKXIIAVLKJJLAOVWHQFXBFLTSFLYWJGVPFHATKCBVEZQNLIJUQUARLFCGVRXIXZSHHQHDHGBHQYQPLTNOKEZYGASUNTPKYXOTUNZMAGBUDKXIIAVLKJJLAVTNNGLUKHQWSMQWVIEGQMYRSBDOCQSUVWWDYBARBOAPQNRNEPGDASZWTHFPEJKXKRMIRDSLASFPLPJYEJYREHMZCBZDNKRJFWHJAEFAXXFZRFFYWLKBUSUPWYESFIOLVITJEWFFKLTQUJCPDOBPLKBGLRGESJKSYMDRODNIRJMLUWVGEBVFLKJAZMAVSVVVVTFJDARCGVTNNGUBJMGXTJLAXXFZRFFYWLKBUSUPWYESFIOLVITJEWFFKLTQUJCPDOBPLKBGLRGESJKSYMDRODNIRJMLUWVGEBVFLKJAZMAVSVVVVTFJDARCGVNPQVIKSKFASRJPAUFNWZTDIDBXTUSEJHYYRLPFVYLUEOWAWVTJYHXPPDEDUCUIEUROMPFXCAHYLAKPIXHJBINAMIYAFYZNJBWMTVEHDZCTQZBHGXSJNPQVIKSKFASRJPAUFNWZTDIDBXTUSEJHYYRLPFVYLUEOWAWVTJYHXPPDEDUCUIEUROMPFXCAHYLAKPIXHJBINAMIYAFYZNJBWMTVEHDZCUBHGXSJNPQVIKSKFASRJPAUFNWZTDIDBXTUSEJHYYRLPFVYLUEOWAWVTJYHXPPDEDUCUIEUROMPFXCAHYLAKPIXHJBINAMIYAFYZNJBWMTVEHDZCTMHUKJXTFLDSABAYZNGRLGAFGFUBHGXSJNPQVIKSKFASRJPAUFNWZTDIDBXTUSEJHYYRLPFVYLUEOWAWVTJYHXPPDEDUCUIEUROMPFXCAHYLAKPIXHJBICTZORWDALOJJNJZYJWUEPHZOVEGRAFHBCRETBBBAKQUTIJNDCIBGNTZOXPGLVBNWSWWMLGOVKOQUXFAXZAQMPPKDSLQBUATEXZRAOSDGOPNVFSPLSNKIWUOZAARAZQXSQSWQEMKBDZPJLRRQPBRKJUJEICHUZOQUOGYWGUATEKLZAZIAQVWNHYHMXVZDYACNLBIHHETCMNNTCTPRIFYKHNFHGVPFWRYFLKHFGPEDNZZNDYTHHHAZQUQPKQQEJZDJOAPXOSMCBQMLGPUSNTWZOLTGHCBHVFMHHWMOIEWINCVJUCWMGOAPWLNFNOCDJRHJIWCMXOWKPLCATJMBKONEUJXGRSKQWTQWDUMLVOGPGMYJERFQBGYNZTLAYSFNQSOQKSDHDUAYBIOGVLRNCYUFQOBUXGIZNKJQSMTUHMUOCOOASMHJXDSCYCBXUZGRTPKJJCSCCKEYKDOYORJMDOSNWTJVWUJKCMLVOGPGMYJERFQBGYNZTLAYSFNQSOQKSDHDUAYBIOGVLRNCYUFQOBUXGIZNKJQSMTUHMUOCOOASMHJXDSCYCBXUZGRTPKJJCSCCKEYKDOYORJMDOSNWTJVWUJKSYBDCMLVOGPGMYJERFQBGYNZTLAYSFNQSOQKSDHDUAYBIOGVLRNCYUFQOBUXGIZNKJQSMTUHMUOCOOASMHJXDSCYCBXUZGRTPKJJCSCCKEYKDOYORJMDOSNWTJVWUJKSYRDCMLVOGPGMYJERFQBGYNZTLAYSFNQSOQKSDHDUAYBIOGVLRNCYUFQOBUXGIZNKJQSMTUHMUOCOOASMHJXDSCYCBXUZGRTPKJJCSCCKEYKDOYORJMDOSNWTJVWUJKSYBYRDCMLVOGPGMYJERFQBGYNZTLAYSFNQSOQKSDHDUAYBIOGVLRNCYUFQOBUXGIZNKJQSMTUHMUOCOOASMHJXDSCYCBXUZGRTPKJJCSCCKEYKDOYORJMDOSNWTJVWUJKSYBOYRDCMLVOGPGMYJERFQBGYNZTLAYSFNQSOQKSDHDUAYBIOGVLRNCYUFQOBUXGIZNKJQSMTUHMUOCOOASMHJXDSCYCBXUZGRTPKJJCSCCKEYKDOYORJMDOSNWTJVWUJKSYBQWMOYRDCMLVOGPGMYJERFQBGYNZTLAYSFNQSOQKSDHDUAYBIOGVLRNCYUFQOBUXGIZNKJQSMTUHMUOCOOASMHJXDSCYCBXUZGRTPKJJCSCCKEYKDOYORJMDOSNWTJVWUJKSYBQWLITMOYRDCMLVOGPGMYJERFQBGYNZTLAYSFNQSOQKSDHDUAYBIOGVLRNCYUFQOBUXGIZNKJQSMTUHMUOCOOASMHJXDSCYCBXUZGRTPKJJCSCCKEYKDOYORJMDOSNWTJVWUJKSYBQWLIMNVUIYDHRTMOYRDCMLVOGPGMYJERFQBGYNZTLAYSFNQSOQKSDHDUAYBIOGVLRNCYUFQOBUXGIZNKJQSMTUHMUOCOOASMHJXDSCYCBXUZGRTPKJJCSCCKEYKDOYORJMDOSNWTJVWUJKSYBQWLIMNVUIYDHIRTMOYRDCMLVOGPGMYJERFQBGYNZTLAYSFNQSOQKSDHDUAYBIOGVLRNCYUFQOBUXGIZNKJQSMTUHMUOCOOASMHJXDSCYCBXUZGRTPKJJCSCCKEYKDOYORJMDOSNWTJVWUJKSYBQWLIMNVUIYDHHQIRTMOYRDCMLVOGPGMYJERFQBGYNZTLAYSFNQSOQKSDHDUAYBIOGVLRNCYUFQOBUXGIZNKJQSMTUHMUOCOOASMHJXDSCYCBXUZGRTPKJJCSCCKEYKDOYORJMDOSNWTJVWUJKSYBQWLIMNVUIYDHHEYQIRTMOYRDCMLVOGPGMYJERFQBGYNZTLAYSFNQSOQKSDHDUAYBIOGVLRNCYUFQOBUXGIZNKJQSMTUHMUOCOOASMHJXDSCYCBXUZGRTPKJJCSCCKEYKDOYORJMDOSNWTJVWUJKSYBQWLIMNVUIYDHHEFYQIRTMOYRDCMLVOGPGMYJERFQBGYNZTLAYSFNQSOQKSDHDUAYBIOGVLRNCYUFQOBUXGIZNKJQSMTUHMUOCOOASMHJXDSCYCBXUZGRTPKJJCSCCKEYKDOYORJMDOSNWTJVWUJKSYBQWLIMNVUIYDHHERFYQIRTMOYRDCMLVOGPGMYJERFQBGYNZTLAYSFNQSOQKSDHDUAYBIOGVLRNCYUFQOBUXGIZNKJQSMTUHMUOCOOASMHJXDSCYCBXUZGRTPKJJCSCCKEYKDOYORJMDOSNWTJVWUJKSYBQWLIMNVUIYDHHECPGKZPRFYQIRTMOYRDCMLVOGPGMYJERFQBGYNZTLAYSFNQSOQKSDHDUAYBIOGVLRNCYUFQOBUXGIZNKJQSMTUHMUOCOOASMHJXDSCYCBXUZGRTPKJJCSCCKEYKDOYORJMDOSNWTJVWUJKSYBQWLIMNVUIYDHHECPGKZFPRFYQIRTMOYRDCMLVOGPGMYJERFQBGYNZTLAYSFNQSOQKSDHDUAYBIOGVLRNCYUFQOBUXGIZNKJQSMTUHMUOCOOASMHJXDSCYCBXUZGRTPKJJCSCCKEYKDOYORJMDOSNWTJVWUJKSYBQWLIMNVUIYDHHECPGKVFPRFYQIRTMOYRDCMLVOGPGMYJERFQBGYNZTLAYSFNQSOQKSDHDUAYBIOGVLRNCYUFQOBUXGIZNKJQSMTUHMUOCOOASMHJXDSCYCBXUZGRTPKJJCSCCKEYKDOYORJMDOSNWTJVWUJKSYBQWLIMNVUIYDHHECPGKVODWEVFPRFYQIRTMOYRDCMLVOGPGMYJERFQBGYNZTLAYSFNQSOQKSDHDUAYBIOGVLRNCYUFQOBUXGIZNKJQSMTUHMUOCOOASMHJXDSCYCBXUZGRTPKJJCSCCKEYKDOYORJMDOSNWTJVWUJKSYBQWLIMNVUIYDHHEQVODWEVFPRFYQIRTMOYRDCMLVOGPGMYJERFQBGYNZTLAYSFNQSOQKSDHDUAYBIOGVLRNCYUFQOBUXGIZNKJQSMTUHMUOCOOASMHJXDSCYCBXUZGRTPKJJCSCCKEYKDOYORJMDOSNWTJVWUJKSYBQWLIMNVUIYDHHECPGQVODWEVFPRFYQIRTMOYRDCMLVOGPGMYJERFQBGYNZTLAYSFNQSOQKSDHDUAYBIOGVLRNCYUFQOBUXGIZNKJQSMTUHMUOCOOASMHJXDSCYCBXUZGRTPKJJCSCCKEYKDOYORJMDOSNWTJVWUJKSYBQWLIMNVUIYDHHECPGCVVHYZCXFVDALNLZOARGOCWBFVUQKKSLEMJRKQIHULBCYTZQNRVSLSTQHZAZTZZRTBVXTTHBZJAXCGRVSCRJSEEOJQXFAEAJECDWVEHTBYEIQNMLILIEAAHWBXHQXEDOJKYNBBMTWQDMLFKRQRPACBSRLULLNLMKETFZJEBWTXZDHVDXJMMNIYAQYXEPJVDKPKQBSMWLATYCUWYSYOJZHUDSXTCSHTRACDQOQSNCYLMXJBYBBWKSNHYAYVRLCWFGAZSEVBIUJETIVGHEBDPXLVFWWTCKOUCOAOPMUIRPYZCULRWRCTZLCGMGREFOGJMULHHHQQIAOXVOKLCVAXDEGHSEUYZHPTFECVAJSFUSFMCGQLMASXUPQYQSNYRSDZVWZUXCDNVVPGAUQRGCQFSQSDEBWTXZDHVDXJMMNIYAQYXEPJVDKPKQBSMWLATYCUWYSYOJZHUDSXTCSHTRACDQOQSNCYLMXJBYBBWKSNHYAYVRLCWFGCQJCQLPHFANJTGZXOJRGGMYHXKQVCLBAAKUPWGUWGXEAHQBGJRKABJEUQUKOUABOVZDUKHRRXSBCRCHDFDUXCRZCYCHAZCEGPJFUATVHUHTAFLKSHRYVNYBVXUFUFYXIVGRBRUWPZZRGGNAURWMPKFLCJUGYBJHHTJHCNZQVDVLMJCQJCQLPHFANJTGZXOJRGGMYHXKQVCLBAAKUPWGUWGXEAHQBGJRKABJEUQUKOUABOVZDUKHRRXSBCRCHDFDUXCRZCYCHAZCEGPJFUATVHUHTAFLKSHRYVNYBVXUFUFYXIVGRBRUWPZZRGGNAURWMPKFLCJUGYBJHHTJHCNZQVDVLMENSPFDIYBMWOBOSWHFITNIUEVEGGXDZYRAIRWTHSECVCCZETCBXGFWSJPHFLCTUNESIKYZKJCTZFOMBMWYHTTXRZKSTVNHJCQJCQLPHFANJTGZXOJRGGMYHXKQVCLBAAKUPWGUWGXEAHQBGJRKABJEUQUKOUABOVZDUKHRRXSBCRCHDFDUXCRZCLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYBLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYVEBLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYVEEBLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYVARZJFEEBLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYVARZJRWUDHFEEBLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYVARZJRWUKHFEEBLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYVARZJRWUDDKHFEEBLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYVARZJRWUDDXKMGCDKHFEEBLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYVARZJRWUDDXKMGUUQBRCDKHFEEBLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYVARZJRWUDDXKMGUUQBWURCDKHFEEBLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYVARZJRWUDDXKMGUUQBWHWURCDKHFEEBLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYVARZJRWUDDXKMGUUQBWHZNMWURCDKHFEEBLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYVARZJRWUDDXKMGUUQBWHZNWCBMWURCDKHFEEBLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYVARZJRWUDDXKMGUUQBWHZNWCJFGBMWURCDKHFEEBLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYVARZJRWUDDXKMGUUQBWHZNWCJUGBMWURCDKHFEEBLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYVARZJRWUDDXKMGUUQBWHZNWCJFCQTTHUGBMWURCDKHFEEBLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYVARZJRWUDDXKMGUUQBWHZNWCJFCQTXHUGBMWURCDKHFEEBLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYVARZJRWUDDXKMGUUQBWHZNWCJFCQTAXHUGBMWURCDKHFEEBLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYVARZJRWUDDXKMGUUQBWHZNWCJFCQTAAXHUGBMWURCDKHFEEBLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYVARZJRWUDDXKMGUUQBWHZNWCJFCQZAAXHUGBMWURCDKHFEEBLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYVARZJRWUDDXKMGUUQBWHZNWCJFCQTTTZAAXHUGBMWURCDKHFEEBLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYVARZJRWUDDXKMGUUQBWHZNWCJFCQTUTZAAXHUGBMWURCDKHFEEBLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYVARZJRWUDDXKMGUUQBWHZNWCJFCQTOUTZAAXHUGBMWURCDKHFEEBLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYVARZJRWUDDXKMGUUQBWHZNWCJFCQTTKOUTZAAXHUGBMWURCDKHFEEBLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYVARZJRWUDDXKMGUUQBWHZNWCJFCQTTKLHVKOUTZAAXHUGBMWURCDKHFEEBLRVDUFOARAOFKXFESYNYRKSMLBUQYTCJSLHIHGMPTXAPLTINHRQXRLOSXQDEFLZEVHHSAGQBRNBPJEQVCKVOTUBGGOIRCWEEYQGCIPXJDFRZUPUTVXGMPSCOVBPUFYVPTEYXTRUHXARDSYTZTGDIHALCQZAIWRPDAYGNZDKMTIFEJHQTEMIYVARZJRWUDDXKMGUUQBWHZNWCJFCQTTKLHFYRPHKDPWHACQGQFLCDTXXZKYDGDDPOBSWEMPAAVFCEXQJDCBOZIRSOWELVYEKWNGRMNGMOPFVVLVIOYDJJMWJGMZCMNGRPMLMNJLPZKFRVYSTRPQOPBKREYFDCIQJLQANHNOIUMHAJAEHAVGSMDSJRNGVPTRTFHWNXPZDOOLHYYMGALWNVZYKHPGFEZNGDGFUIEUNYQKEAICMFOYYLDRJQXCKQUILBITRHCUSQMCNSDDCLMYRQWFYRPHKDPWHACQGQFLCDTXXZKYDGDDPOBSWEMPAAVFCEXQJDCBOZIRSOWELVYEKWNGRMNGMOPFVVLVIOYDJJMWJGMZCMNGRPMLMNJLPZKFRVYSTRPQOPBKREYFDCIQJLQANHNOIUMHAJAEHAVGSMDSJRNGVPTRTFHWNXPZDOOLHYYMGALWNVZYKHPGFEZNGDGFUIEUNYQKEAICMFOYYLDRJQXCKQUILBITRHCUSQMCNSDDCLMYRQXWFYRPHKDPWHACQGQFLCDTXXZKYDGDDPOBSWEMPAAVFCEXQJDCBOZIRSOWELVYEKWNGRMNGMOPFVVLVIOYDJJMWJGMZCMNGRPMLMNJLPZKFRVYSTRPQOPBKREYFDCIQJLQANHNOIUMHAJAEHAVGSMDSJRNGVPTRTFHWNXPZDOOLHYYMGALWNVZYKHPGFEZNGDGFUIEUNYQKEAICMFOYYLDRJQXCKQUILBITRHCUSQMCNSDDCLMYRNXWFYRPHKDPWHACQGQFLCDTXXZKYDGDDPOBSWEMPAAVFCEXQJDCBOZIRSOWELVYEKWNGRMNGMOPFVVLVIOYDJJMWJGMZCMNGRPMLMNJLPZKFRVYSTRPQOPBKREYFDCIQJLQANHNOIUMHAJAEHAVGSMDSJRNGVPTRTFHWNXPZDOOLHYYMGALWNVZYKHPGFEZNGDGFUIEUNYQKEAICMFOYYLDRJQXCKQUILBITRHCUSQMCNSDDCLMYRKNXWFYRPHKDPWHACQGQFLCDTXXZKYDGDDPOBSWEMPAAVFCEXQJDCBOZIRSOWELVYEKWNGRMNGMOPFVVLVIOYDJJMWJGMZCMNGRPMLMNJLPZKFRVYSTRPQOPBKREYFDCIQJLQANHNOIUMHAJAEHAVGSMDSJRNGVPTRTFHWNXPZDOOLHYYMGALWNVZYKHPGFEZNGDGFUIEUNYQKEAICMFOYYLDRJQXCKQUILBITRHCUSQMCNSDDCLMYRQUKNXWFYRPHKDPWHACQGQFLCDTXXZKYDGDDPOBSWEMPAAVFCEXQJDCBOZIRSOWELVYEKWNGRMNGMOPFVVLVIOYDJJMWJGMZCMNGRPMLMNJLPZKFRVYSTRPQOPBKREYFDCIQJLQANHNOIUMHAJAEHAVGSMDSJRNGVPTRTFHWNXPZDOOLHYYMGALWNVZYKHPGFEZNGDGFUIEUNYQKEAICMFOYYLDRJQXCKQUILBITRHCUSQMCNSDDCLMYRQIAUKNXWFYRPHKDPWHACQGQFLCDTXXZKYDGDDPOBSWEMPAAVFCEXQJDCBOZIRSOWELVYEKWNGRMNGMOPFVVLVIOYDJJMWJGMZCMNGRPMLMNJLPZKFRVYSTRPQOPBKREYFDCIQJLQANHNOIUMHAJAEHAVGSMDSJRNGVPTRTFHWNXPZDOOLHYYMGALWNVZYKHPGFEZNGDGFUIEUNYQKEAICMFOYYLDRJQXCKQUILBITRHCUSQMCNSDDCLMYRQIADAUKNXWFYRPHKDPWHACQGQFLCDTXXZKYDGDDPOBSWEMPAAVFCEXQJDCBOZIRSOWELVYEKWNGRMNGMOPFVVLVIOYDJJMWJGMZCMNGRPMLMNJLPZKFRVYSTRPQOPBKREYFDCIQJLQANHNOIUMHAJAEHAVGSMDSJRNGVPTRTFHWNXPZDOOLHYYMGALWNVZYKHPGFEZNGDGFUIEUNYQKEAICMFOYYLDRJQXCKQUILBITRHCUSQMCNSDDCLMYRQIIDAUKNXWFYRPHKDPWHACQGQFLCDTXXZKYDGDDPOBSWEMPAAVFCEXQJDCBOZIRSOWELVYEKWNGRMNGMOPFVVLVIOYDJJMWJGMZCMNGRPMLMNJLPZKFRVYSTRPQOPBKREYFDCIQJLQANHNOIUMHAJAEHAVGSMDSJRNGVPTRTFHWNXPZDOOLHYYMGALWNVZYKHPGFEZNGDGFUIEUNYQKEAICMFOYYLDRJQXCKQUILBITRHCUSQMCNSDDCLMYRQIAOM diff --git a/internal-complibs/zlib-ng-2.0.7/test/CVE-2018-25032/fixed.txt b/internal-complibs/zlib-ng-2.0.7/test/CVE-2018-25032/fixed.txt new file mode 100644 index 00000000..5ccca248 --- /dev/null +++ b/internal-complibs/zlib-ng-2.0.7/test/CVE-2018-25032/fixed.txt @@ -0,0 +1 @@ +AAABAACAADAAEAAFAAGAAHAAIAAJAAKAALAAMAANAAOAAPAAQAARAASAATAAUAAVAAWAAXAAYAAZABBABCABDABEABFABGABHABIABJABKABLABMABNABOABPABQABRABSABTABUABVABWABXABYABZACBACCACDACEACFACGACHACIACJACKACLACMACNACOACPACQACRACSACTACUACVACWACXACYACZADBADCADDADEADFADGADHADIADJADKADLADMADNADOADPADQADRADSADTADUADVADWADXADYADZAEBAECAEDAEEAEFAEGAEHAEIAEJAEKAELAEMAENAEOAEPAEQAERAESAETAEUAEVAEWAEXAEYAEZAFBAFCAFDAFEAFFAFGAFHAFIAFJAFKAFLAFMAFNAFOAFPAFQAFRAFSAFTAFUAFVAFWAFXAFYAFZAGBAGCAGDAGEAGFAGGAGHAGIAGJAGKAGLAGMAGNAGOAGPAGQAGRAGSAGTAGUAGVAGWAGXAGYAGZAHBAHCAHDAHEAHFAHGAHHAHIAHJAHKAHLAHMAHNAHOAHPAHQAHRAHSAHTAHUAHVAHWAHXAHYAHZAIBAICAIDAIEAIFAIGAIHAIIAIJAIKAILAIMAINAIOAIPAIQAIRAISAITAIUAIVAIWAIXAIYAIZAJBAJCAJDAJEAJFAJGAJHAJIAJJAJKAJLAJMAJNAJOAJPAJQAJRAJSAJTAJUAJVAJWAJXAJYAJZAKBAKCAKDAKEAKFAKGAKHAKIAKJAKKAKLAKMAKNAKOAKPAKQAKRAKSAKTAKUAKVAKWAKXAKYAKZALBALCALDALEALFALGALHALIALJALKALLALMALNALOALPALQALRALSALTALUALVALWALXALYALZAMBAMCAMDAMEAMFAMGAMHAMIAMJAMKAMLAMMAMNAMOAMPAMQAMRAMSAMTAMUAMVAMWAMXAMYAMZANBANCANDANEANFANGANHANIANJANKANLANMANNANOANPANQANRANSANTANUANVANWANXANYANZAOBAOCAODAOEAOFAOGAOHAOIAOJAOKAOLAOMAONAOOAOPAOQAORAOSAOTAOUAOVAOWAOXAOYAOZAPBAPCAPDAPEAPFAPGAPHAPIAPJAPKAPLAPMAPNAPOAPPAPQAPRAPSAPTAPUAPVAPWAPXAPYAPZAQBAQCAQDAQEAQFAQGAQHAQIAQJAQKAQLAQMAQNAQOAQPAQQAQRAQSAQTAQUAQVAQWAQXAQYAQZARBARCARDAREARFARGARHARIARJARKARLARMARNAROARPARQARRARSARTARUARVARWARXARYARZASBASCASDASEASFASGASHASIASJASKASLASMASNASOASPASQASRASSASTASUASVASWASXASYASZATBATCATDATEATFATGATHATIATJATKATLATMATNATOATPATQATRATSATTATUATVATWATXATYATZAUBAUCAUDAUEAUFAUGAUHAUIAUJAUKAULAUMAUNAUOAUPAUQAURAUSAUTAUUAUVAUWAUXAUYAUZAVBAVCAVDAVEAVFAVGAVHAVIAVJAVKAVLAVMAVNAVOAVPAVQAVRAVSAVTAVUAVVAVWAVXAVYAVZAWBAWCAWDAWEAWFAWGAWHAWIAWJAWKAWLAWMAWNAWOAWPAWQAWRAWSAWTAWUAWVAWWAWXAWYAWZAXBAXCAXDAXEAXFAXGAXHAXIAXJAXKAXLAXMAXNAXOAXPAXQAXRAXSAXTAXUAXVAXWAXXAXYAXZAYBAYCAYDAYEAYFAYGAYHAYIAYJAYKAYLAYMAYNAYOAYPAYQAYRAYSAYTAYUAYVAYWAYXAYYAYZAZBAZCAZDAZEAZFAZGAZHAZIAZJAZKAZLAZMAZNAZOAZPAZQAZRAZSAZTAZUAZVAZWAZXAZYAZZBBBCBBDBBEBBFBBGBBHBBIBBJBBKBBLBBMBBNBBOBBPBBQBBRBBSBBTBBUBBVBBWBBXBBYBBZBCCBCDBCEBCFBCGBCHBCIBCJBCKBCLBCMBCNBCOBCPBCQBCRBCSBCTBCUBCVBCWBCXBCYBCZBDCBDDBDEBDFBDGBDHBDIBDJBDKBDLBDMBDNBDOBDPBDQBDRBDSBDTBDUBDVBDWBDXBDYBDZBECBEDBEEBEFBEGBEHBEIBEJBEKBELBEMBENBEOBEPBEQBERBESBETBEUBEVBEWBEXBEYBEZBFCBFDBFEBFFBFGBFHBFIBFJBFKBFLBFMBFNBFOBFPBFQBFRBFSBFTBFUBFVBFWBFXBFYBFZBGCBGDBGEBGFBGGBGHBGIBGJBGKBGLBGMBGNBGOBGPBGQBGRBGSBGTBGUBGVBGWBGXBGYBGZBHCBHDBHEBHFBHGBHHBHIBHJBHKBHLBHMBHNBHOBHPBHQBHRBHSBHTBHUBHVBHWBHXBHYBHZBICBIDBIEBIFBIGBIHBIIBIJBIKBILBIMBINBIOBIPBIQBIRBISBITBIUBIVBIWBIXBIYBIZBJCBJDBJEBJFBJGBJHBJIBJJBJKBJLBJMBJNBJOBJPBJQBJRBJSBJTBJUBJVBJWBJXBJYBJZBKCBKDBKEBKFBKGBKHBKIBKJBKKBKLBKMBKNBKOBKPBKQBKRBKSBKTBKUBKVBKWBKXBKYBKZBLCBLDBLEBLFBLGBLHBLIBLJBLKBLLBLMBLNBLOBLPBLQBLRBLSBLTBLUBLVBLWBLXBLYBLZBMCBMDBMEBMFBMGBMHBMIBMJBMKBMLBMMBMNBMOBMPBMQBMRBMSBMTBMUBMVBMWBMXBMYBMZBNCBNDBNEBNFBNGBNHBNIBNJBNKBNLBNMBNNBNOBNPBNQBNRBNSBNTBNUBNVBNWBNXBNYBNZBOCBODBOEBOFBOGBOHBOIBOJBOKBOLBOMBONBOOBOPBOQBORBOSBOTBOUBOVBOWBOXBOYBOZBPCBPDBPEBPFBPGBPHBPIBPJBPKBPLBPMBPNBPOBPPBPQBPRBPSBPTBPUBPVBPWBPXBPYBPZBQCBQDBQEBQFBQGBQHBQIBQJBQKBQLBQMBQNBQOBQPBQQBQRBQSBQTBQUBQVBQWBQXBQYBQZBRCBRDBREBRFBRGBRHBRIBRJBRKBRLBRMBRNBROBRPBRQBRRBRSBRTBRUBRVBRWBRXBRYBRZBSCBSDBSEBSFBSGBSHBSIBSJBSKBSLBSMBSNBSOBSPBSQBSRBSSBSTBSUBSVBSWBSXBSYBSZBTCBTDBTEBTFBTGBTHBTIBTJBTKBTLBTMBTNBTOBTPBTQBTRBTSBTTBTUBTVBTWBTXBTYBTZBUCBUDBUEBUFBUGBUHBUIBUJBUKBULBUMBUNBUOBUPBUQBURBUSBUTBUUBUVBUWBUXBUYBUZBVCBVDBVEBVFBVGBVHBVIBVJBVKBVLBVMBVNBVOBVPBVQBVRBVSBVTBVUBVVBVWBVXBVYBVZBWCBWDBWEBWFBWGBWHBWIBWJBWKBWLBWMBWNBWOBWPBWQBWRBWSBWTBWUBWVBWWBWXBWYBWZBXCBXDBXEBXFBXGBXHBXIBXJBXKBXLBXMBXNBXOBXPBXQBXRBXSBXTBXUBXVBXWBXXBXYBXZBYCBYDBYEBYFBYGBYHBYIBYJBYKBYLBYMBYNBYOBYPBYQBYRBYSBYTBYUBYVBYWBYXBYYBYZBZCBZDBZEBZFBZGBZHBZIBZJBZKBZLBZMBZNBZOBZPBZQBZRBZSBZTBZUBZVBZWBZXBZYBZZCCCDCCECCFCCGCCHCCICCJCCKCCLCCMCCNCCOCCPCCQCCRCCSCCTCCUCCVCCWCCXCCYCCZCDDCDECDFCDGCDHCDICDJCDKCDLCDMCDNCDOCDPCDQCDRCDSCDTCDUCDVCDWCDXCDYCDZCEDCEECEFCEGCEHCEICEJCEKCELCEMCENCEOCEPCEQCERCESCETCEUCEVCEWCEXCEYCEZCFDCFECFFCFGCFHCFICFJCFKCFLCFMCFNCFOCFPCFQCFRCFSCFTCFUCFVCFWCFXCFYCFZCGDCGECGFCGGCGHCGICGJCGKCGLCGMCGNCGOCGPCGQCGRCGSCGTCGUCGVCGWCGXCGYCGZCHDCHECHFCHGCHHCHICHJCHKCHLCHMCHNCHOCHPCHQCHRCHSCHTCHUCHVCHWCHXCHYCHZCIDCIECIFCIGCIHCIICIJCIKCILCIMCINCIOCIPCIQCIRCISCITCIUCIVCIWCIXCIYCIZCJDCJECJFCJGCJHCJICJJCJKCJLCJMCJNCJOCJPCJQCJRCJSCJTCJUCJVCJWCJXCJYCJZCKDCKECKFCKGCKHCKICKJCKKCKLCKMCKNCKOCKPCKQCKRCKSCKTCKUCKVCKWCKXCKYCKZCLDCLECLFCLGCLHCLICLJCLKCLLCLMCLNCLOCLPCLQCLRCLSCLTCLUCLVCLWCLXCLYCLZCMDCMECMFCMGCMHCMICMJCMKCMLCMMCMNCMOCMPCMQCMRCMSCMTCMUCMVCMWCMXCMYCMZCNDCNECNFCNGCNHCNICNJCNKCNLCNMCNNCNOCNPCNQCNRCNSCNTCNUCNVCNWCNXCNYCNZCODCOECOFCOGCOHCOICOJCOKCOLCOMCONCOOCOPCOQCORCOSCOTCOUCOVCOWCOXCOYCOZCPDCPECPFCPGCPHCPICPJCPKCPLCPMCPNCPOCPPCPQCPRCPSCPTCPUCPVCPWCPXCPYCPZCQDCQECQFCQGCQHCQICQJCQKCQLCQMCQNCQOCQPCQQCQRCQSCQTCQUCQVCQWCQXCQYCQZCRDCRECRFCRGCRHCRICRJCRKCRLCRMCRNCROCRPCRQCRRCRSCRTCRUCRVCRWCRXCRYCRZCSDCSECSFCSGCSHCSICSJCSKCSLCSMCSNCSOCSPCSQCSRCSSCSTCSUCSVCSWCSXCSYCSZCTDCTECTFCTGCTHCTICTJCTKCTLCTMCTNCTOCTPCTQCTRCTSCTTCTUCTVCTWCTXCTYCTZCUDCUECUFCUGCUHCUICUJCUKCULCUMCUNCUOCUPCUQCURCUSCUTCUUCUVCUWCUXCUYCUZCVDCVECVFCVGCVHCVICVJCVKCVLCVMCVNCVOCVPCVQCVRCVSCVTCVUCVVCVWCVXCVYCVZCWDCWECWFCWGCWHCWICWJCWKCWLCWMCWNCWOCWPCWQCWRCWSCWTCWUCWVCWWCWXCWYCWZCXDCXECXFCXGCXHCXICXJCXKCXLCXMCXNCXOCXPCXQCXRCXSCXTCXUCXVCXWCXXCXYCXZCYDCYECYFCYGCYHCYICYJCYKCYLCYMCYNCYOCYPCYQCYRCYSCYTCYUCYVCYWCYXCYYCYZCZDCZECZFCZGCZHCZICZJCZKCZLCZMCZNCZOCZPCZQCZRCZSCZTCZUCZVCZWCZXCZYCZZDDDEDDFDDGDDHDDIDDJDDKDDLDDMDDNDDODDPDDQDDRDDSDDTDDUDDVDDWDDXDDYDDZDEEDEFDEGDEHDEIDEJDEKDELDEMDENDEODEPDEQDERDESDETDEUDEVDEWDEXDEYDEZDFEDFFDFGDFHDFIDFJDFKDFLDFMDFNDFODFPDFQDFRDFSDFTDFUDFVDFWDFXDFYDFZDGEDGFDGGDGHDGIDGJDGKDGLDGMDGNDGODGPDGQDGRDGSDGTDGUDGVDGWDGXDGYDGZDHEDHFDHGDHHDHIDHJDHKDHLDHMDHNDHODHPDHQDHRDHSDHTDHUDHVDHWDHXDHYDHZDIEDIFDIGDIHDIIDIJDIKDILDIMDINDIODIPDIQDIRDISDITDIUDIVDIWDIXDIYDIZDJEDJFDJGDJHDJIDJJDJKDJLDJMDJNDJODJPDJQDJRDJSDJTDJUDJVDJWDJXDJYDJZDKEDKFDKGDKHDKIDKJDKKDKLDKMDKNDKODKPDKQDKRDKSDKTDKUDKVDKWDKXDKYDKZDLEDLFDLGDLHDLIDLJDLKDLLDLMDLNDLODLPDLQDLRDLSDLTDLUDLVDLWDLXDLYDLZDMEDMFDMGDMHDMIDMJDMKDMLDMMDMNDMODMPDMQDMRDMSDMTDMUDMVDMWDMXDMYDMZDNEDNFDNGDNHDNIDNJDNKDNLDNMDNNDNODNPDNQDNRDNSDNTDNUDNVDNWDNXDNYDNZDOEDOFDOGDOHDOIDOJDOKDOLDOMDONDOODOPDOQDORDOSDOTDOUDOVDOWDOXDOYDOZDPEDPFDPGDPHDPIDPJDPKDPLDPMDPNDPODPPDPQDPRDPSDPTDPUDPVDPWDPXDPYDPZDQEDQFDQGDQHDQIDQJDQKDQLDQMDQNDQODQPDQQDQRDQSDQTDQUDQVDQWDQXDQYDQZDREDRFDRGDRHDRIDRJDRKDRLDRMDRNDRODRPDRQDRRDRSDRTDRUDRVDRWDRXDRYDRZDSEDSFDSGDSHDSIDSJDSKDSLDSMDSNDSODSPDSQDSRDSSDSTDSUDSVDSWDSXDSYDSZDTEDTFDTGDTHDTIDTJDTKDTLDTMDTNDTODTPDTQDTRDTSDTTDTUDTVDTWDTXDTYDTZDUEDUFDUGDUHDUIDUJDUKDULDUMDUNDUODUPDUQDURDUSDUTDUUDUVDUWDUXDUYDUZDVEDVFDVGDVHDVIDVJDVKDVLDVMDVNDVODVPDVQDVRDVSDVTDVUDVVDVWDVXDVYDVZDWEDWFDWGDWHDWIDWJDWKDWLDWMDWNDWODWPDWQDWRDWSDWTDWUDWVDWWDWXDWYDWZDXEDXFDXGDXHDXIDXJDXKDXLDXMDXNDXODXPDXQDXRDXSDXTDXUDXVDXWDXXDXYDXZDYEDYFDYGDYHDYIDYJDYKDYLDYMDYNDYODYPDYQDYRDYSDYTDYUDYVDYWDYXDYYDYZDZEDZFDZGDZHDZIDZJDZKDZLDZMDZNDZODZPDZQDZRDZSDZTDZUDZVDZWDZXDZYDZZEEEFEEGEEHEEIEEJEEKEELEEMEENEEOEEPEEQEEREESEETEEUEEVEEWEEXEEYEEZEFFEFGEFHEFIEFJEFKEFLEFMEFNEFOEFPEFQEFREFSEFTEFUEFVEFWEFXEFYEFZEGFEGGEGHEGIEGJEGKEGLEGMEGNEGOEGPEGQEGREGSEGTEGUEGVEGWEGXEGYEGZEHFEHGEHHEHIEHJEHKEHLEHMEHNEHOEHPEHQEHREHSEHTEHUEHVEHWEHXEHYEHZEIFEIGEIHEIIEIJEIKEILEIMEINEIOEIPEIQEIREISEITEIUEIVEIWEIXEIYEIZEJFEJGEJHEJIEJJEJKEJLEJMEJNEJOEJPEJQEJREJSEJTEJUEJVEJWEJXEJYEJZEKFEKGEKHEKIEKJEKKEKLEKMEKNEKOEKPEKQEKREKSEKTEKUEKVEKWEKXEKYEKZELFELGELHELIELJELKELLELMELNELOELPELQELRELSELTELUELVELWELXELYELZEMFEMGEMHEMIEMJEMKEMLEMMEMNEMOEMPEMQEMREMSEMTEMUEMVEMWEMXEMYEMZENFENGENHENIENJENKENLENMENNENOENPENQENRENSENTENUENVENWENXENYENZEOFEOGEOHEOIEOJEOKEOLEOMEONEOOEOPEOQEOREOSEOTEOUEOVEOWEOXEOYEOZEPFEPGEPHEPIEPJEPKEPLEPMEPNEPOEPPEPQEPREPSEPTEPUEPVEPWEPXEPYEPZEQFEQGEQHEQIEQJEQKEQLEQMEQNEQOEQPEQQEQREQSEQTEQUEQVEQWEQXEQYEQZERFERGERHERIERJERKERLERMERNEROERPERQERRERSERTERUERVERWERXERYERZESFESGESHESIESJESKESLESMESNESOESPESQESRESSESTESUESVESWESXESYESZETFETGETHETIETJETKETLETMETNETOETPETQETRETSETTETUETVETWETXETYETZEUFEUGEUHEUIEUJEUKEULEUMEUNEUOEUPEUQEUREUSEUTEUUEUVEUWEUXEUYEUZEVFEVGEVHEVIEVJEVKEVLEVMEVNEVOEVPEVQEVREVSEVTEVUEVVEVWEVXEVYEVZEWFEWGEWHEWIEWJEWKEWLEWMEWNEWOEWPEWQEWREWSEWTEWUEWVEWWEWXEWYEWZEXFEXGEXHEXIEXJEXKEXLEXMEXNEXOEXPEXQEXREXSEXTEXUEXVEXWEXXEXYEXZEYFEYGEYHEYIEYJEYKEYLEYMEYNEYOEYPEYQEYREYSEYTEYUEYVEYWEYXEYYEYZEZFEZGEZHEZIEZJEZKEZLEZMEZNEZOEZPEZQEZREZSEZTEZUEZVEZWEZXEZYEZZFFFGFFHFFIFFJFFKFFLFFMFFNFFOFFPFFQFFRFFSFFTFFUFFVFFWFFXFFYFFZFGGFGHFGIFGJFGKFGLFGMFGNFGOFGPFGQFGRFGSFGTFGUFGVFGWFGXFGYFGZFHGFHHFHIFHJFHKFHLFHMFHNFHOFHPFHQFHRFHSFHTFHUFHVFHWFHXFHYFHZFIGFIHFIIFIJFIKFILFIMFINFIOFIPFIQFIRFISFITFIUFIVFIWFIXFIYFIZFJGFJHFJIFJJFJKFJLFJMFJNFJOFJPFJQFJRFJSFJTFJUFJVFJWFJXFJYFJZFKGFKHFKIFKJFKKFKLFKMFKNFKOFKPFKQFKRFKSFKTFKUFKVFKWFKXFKYFKZFLGFLHFLIFLJFLKFLLFLMFLNFLOFLPFLQFLRFLSFLTFLUFLVFLWFLXFLYFLZFMGFMHFMIFMJFMKFMLFMMFMNFMOFMPFMQFMRFMSFMTFMUFMVFMWFMXFMYFMZFNGFNHFNIFNJFNKFNLFNMFNNFNOFNPFNQFNRFNSFNTFNUFNVFNWFNXFNYFNZFOGFOHFOIFOJFOKFOLFOMFONFOOFOPFOQFORFOSFOTFOUFOVFOWFOXFOYFOZFPGFPHFPIFPJFPKFPLFPMFPNFPOFPPFPQFPRFPSFPTFPUFPVFPWFPXFPYFPZFQGFQHFQIFQJFQKFQLFQMFQNFQOFQPFQQFQRFQSFQTFQUFQVFQWFQXFQYFQZFRGFRHFRIFRJFRKFRLFRMFRNFROFRPFRQFRRFRSFRTFRUFRVFRWFRXFRYFRZFSGFSHFSIFSJFSKFSLFSMFSNFSOFSPFSQFSRFSSFSTFSUFSVFSWFSXFSYFSZFTGFTHFTIFTJFTKFTLFTMFTNFTOFTPFTQFTRFTSFTTFTUFTVFTWFTXFTYFTZFUGFUHFUIFUJFUKFULFUMFUNFUOFUPFUQFURFUSFUTFUUFUVFUWFUXFUYFUZFVGFVHFVIFVJFVKFVLFVMFVNFVOFVPFVQFVRFVSFVTFVUFVVFVWFVXFVYFVZFWGFWHFWIFWJFWKFWLFWMFWNFWOFWPFWQFWRFWSFWTFWUFWVFWWFWXFWYFWZFXGFXHFXIFXJFXKFXLFXMFXNFXOFXPFXQFXRFXSFXTFXUFXVFXWFXXFXYFXZFYGFYHFYIFYJFYKFYLFYMFYNFYOFYPFYQFYRFYSFYTFYUFYVFYWFYXFYYFYZFZGFZHFZIFZJFZKFZLFZMFZNFZOFZPFZQFZRFZSFZTFZUFZVFZWFZXFZYFZZGGGHGGIGGJGGKGGLGGMGGNGGOGGPGGQGGRGGSGGTGGUGGVGGWGGXGGYGGZGHHGHIGHJGHKGHLGHMGHNGHOGHPGHQGHRGHSGHTGHUGHVGHWGHXGHYGHZGIHGIIGIJGIKGILGIMGINGIOGIPGIQGIRGISGITGIUGIVGIWGIXGIYGIZGJHGJIGJJGJKGJLGJMGJNGJOGJPGJQGJRGJSGJTGJUGJVGJWGJXGJYGJZGKHGKIGKJGKKGKLGKMGKNGKOGKPGKQGKRGKSGKTGKUGKVGKWGKXGKYGKZGLHGLIGLJGLKGLLGLMGLNGLOGLPGLQGLRGLSGLTGLUGLVGLWGLXGLYGLZGMHGMIGMJGMKGMLGMMGMNGMOGMPGMQGMRGMSGMTGMUGMVGMWGMXGMYGMZGNHGNIGNJGNKGNLGNMGNNGNOGNPGNQGNRGNSGNTGNUGNVGNWGNXGNYGNZGOHGOIGOJGOKGOLGOMGONGOOGOPGOQGORGOSGOTGOUGOVGOWGOXGOYGOZGPHGPIGPJGPKGPLGPMGPNGPOGPPGPQGPRGPSGPTGPUGPVGPWGPXGPYGPZGQHGQIGQJGQKGQLGQMGQNGQOGQPGQQGQRGQSGQTGQUGQVGQWGQXGQYGQZGRHGRIGRJGRKGRLGRMGRNGROGRPGRQGRRGRSGRTGRUGRVGRWGRXGRYGRZGSHGSIGSJGSKGSLGSMGSNGSOGSPGSQGSRGSSGSTGSUGSVGSWGSXGSYGSZGTHGTIGTJGTKGTLGTMGTNGTOGTPGTQGTRGTSGTTGTUGTVGTWGTXGTYGTZGUHGUIGUJGUKGULGUMGUNGUOGUPGUQGURGUSGUTGUUGUVGUWGUXGUYGUZGVHGVIGVJGVKGVLGVMGVNGVOGVPGVQGVRGVSGVTGVUGVVGVWGVXGVYGVZGWHGWIGWJGWKGWLGWMGWNGWOGWPGWQGWRGWSGWTGWUGWVGWWGWXGWYGWZGXHGXIGXJGXKGXLGXMGXNGXOGXPGXQGXRGXSGXTGXUGXVGXWGXXGXYGXZGYHGYIGYJGYKGYLGYMGYNGYOGYPGYQGYRGYSGYTGYUGYVGYWGYXGYYGYZGZHGZIGZJGZKGZLGZMGZNGZOGZPGZQGZRGZSGZTGZUGZVGZWGZXGZYGZZHHHIHHJHHKHHLHHMHHNHHOHHPHHQHHRHHSHHTHHUHHVHHWHHXHHYHHZHIIHIJHIKHILHIMHINHIOHIPHIQHIRHISHITHIUHIVHIWHIXHIYHIZHJIHJJHJKHJLHJMHJNHJOHJPHJQHJRHJSHJTHJUHJVHJWHJXHJYHJZHKIHKJHKKHKLHKMHKNHKOHKPHKQHKRHKSHKTHKUHKVHKWHKXHKYHKZHLIHLJHLKHLLHLMHLNHLOHLPHLQHLRHLSHLTHLUHLVHLWHLXHLYHLZHMIHMJHMKHMLHMMHMNHMOHMPHMQHMRHMSHMTHMUHMVHMWHMXHMYHMZHNIHNJHNKHNLHNMHNNHNOHNPHNQHNRHNSHNTHNUHNVHNWHNXHNYHNZHOIHOJHOKHOLHOMHONHOOHOPHOQHORHOSHOTHOUHOVHOWHOXHOYHOZHPIHPJHPKHPLHPMHPNHPOHPPHPQHPRHPSHPTHPUHPVHPWHPXHPYHPZHQIHQJHQKHQLHQMHQNHQOHQPHQQHQRHQSHQTHQUHQVHQWHQXHQYHQZHRIHRJHRKHRLHRMHRNHROHRPHRQHRRHRSHRTHRUHRVHRWHRXHRYHRZHSIHSJHSKHSLHSMHSNHSOHSPHSQHSRHSSHSTHSUHSVHSWHSXHSYHSZHTIHTJHTKHTLHTMHTNHTOHTPHTQHTRHTSHTTHTUHTVHTWHTXHTYHTZHUIHUJHUKHULHUMHUNHUOHUPHUQHURHUSHUTHUUHUVHUWHUXHUYHUZHVIHVJHVKHVLHVMHVNHVOHVPHVQHVRHVSHVTHVUHVVHVWHVXHVYHVZHWIHWJHWKHWLHWMHWNHWOHWPHWQHWRHWSHWTHWUHWVHWWHWXHWYHWZHXIHXJHXKHXLHXMHXNHXOHXPHXQHXRHXSHXTHXUHXVHXWHXXHXYHXZHYIHYJHYKHYLHYMHYNHYOHYPHYQHYRHYSHYTHYUHYVHYWHYXHYYHYZHZIHZJHZKHZLHZMHZNHZOHZPHZQHZRHZSHZTHZUHZVHZWHZXHZYHZZIIIJIIKIILIIMIINIIOIIPIIQIIRIISIITIIUIIVIIWIIXIIYIIZIJJIJKIJLIJMIJNIJOIJPIJQIJRIJSIJTIJUIJVIJWIJXIJYIJZIKJIKKIKLIKMIKNIKOIKPIKQIKRIKSIKTIKUIKVIKWIKXIKYIKZILJILKILLILMILNILOILPILQILRILSILTILUILVILWILXILYILZIMJIMKIMLIMMIMNIMOIMPIMQIMRIMSIMTIMUIMVIMWIMXIMYIMZINJINKINLINMINNINOINPINQINRINSINTINUINVINWINXINYINZIOJIOKIOLIOMIONIOOIOPIOQIORIOSIOTIOUIOVIOWIOXIOYIOZIPJIPKIPLIPMIPNIPOIPPIPQIPRIPSIPTIPUIPVIPWIPXIPYIPZIQJIQKIQLIQMIQNIQOIQPIQQIQRIQSIQTIQUIQVIQWIQXIQYIQZIRJIRKIRLIRMIRNIROIRPIRQIRRIRSIRTIRUIRVIRWIRXIRYIRZISJISKISLISMISNISOISPISQISRISSISTISUISVISWISXISYISZITJITKITLITMITNITOITPITQITRITSITTITUITVITWITXITYITZIUJIUKIULIUMIUNIUOIUPIUQIURIUSIUTIUUIUVIUWIUXIUYIUZIVJIVKIVLIVMIVNIVOIVPIVQIVRIVSIVTIVUIVVIVWIVXIVYIVZIWJIWKIWLIWMIWNIWOIWPIWQIWRIWSIWTIWUIWVIWWIWXIWYIWZIXJIXKIXLIXMIXNIXOIXPIXQIXRIXSIXTIXUIXVIXWIXXIXYIXZIYJIYKIYLIYMIYNIYOIYPIYQIYRIYSIYTIYUIYVIYWIYXIYYIYZIZJIZKIZLIZMIZNIZOIZPIZQIZRIZSIZTIZUIZVIZWIZXIZYIZZJJJKJJLJJMJJNJJOJJPJJQJJRJJSJJTJJUJJVJJWJJXJJYJJZJKKJKLJKMJKNJKOJKPJKQJKRJKSJKTJKUJKVJKWJKXJKYJKZJLKJLLJLMJLNJLOJLPJLQJLRJLSJLTJLUJLVJLWJLXJLYJLZJMKJMLJMMJMNJMOJMPJMQJMRJMSJMTJMUJMVJMWJMXJMYJMZJNKJNLJNMJNNJNOJNPJNQJNRJNSJNTJNUJNVJNWJNXJNYJNZJOKJOLJOMJONJOOJOPJOQJORJOSJOTJOUJOVJOWJOXJOYJOZJPKJPLJPMJPNJPOJPPJPQJPRJPSJPTJPUJPVJPWJPXJPYJPZJQKJQLJQMJQNJQOJQPJQQJQRJQSJQTJQUJQVJQWJQXJQYJQZJRKJRLJRMJRNJROJRPJRQJRRJRSJRTJRUJRVJRWJRXJRYJRZJSKJSLJSMJSNJSOJSPJSQJSRJSSJSTJSUJSVJSWJSXJSYJSZJTKJTLJTMJTNJTOJTPJTQJTRJTSJTTJTUJTVJTWJTXJTYJTZJUKJULJUMJUNJUOJUPJUQJURJUSJUTJUUJUVJUWJUXJUYJUZJVKJVLJVMJVNJVOJVPJVQJVRJVSJVTJVUJVVJVWJVXJVYJVZJWKJWLJWMJWNJWOJWPJWQJWRJWSJWTJWUJWVJWWJWXJWYJWZJXKJXLJXMJXNJXOJXPJXQJXRJXSJXTJXUJXVJXWJXXJXYJXZJYKJYLJYMJYNJYOJYPJYQJYRJYSJYTJYUJYVJYWJYXJYYJYZJZKJZLJZMJZNJZOJZPJZQJZRJZSJZTJZUJZVJZWJZXJZYJZZKKKLKKMKKNKKOKKPKKQKKRKKSKKTKKUKKVKKWKKXKKYKKZKLLKLMKLNKLOKLPKLQKLRKLSKLTKLUKLVKLWKLXKLYKLZKMLKMMKMNKMOKMPKMQKMRKMSKMTKMUKMVKMWKMXKMYKMZKNLKNMKNNKNOKNPKNQKNRKNSKNTKNUKNVKNWKNXKNYKNZKOLKOMKONKOOKOPKOQKORKOSKOTKOUKOVKOWKOXKOYKOZKPLKPMKPNKPOKPPKPQKPRKPSKPTKPUKPVKPWKPXKPYKPZKQLKQMKQNKQOKQPKQQKQRKQSKQTKQUKQVKQWKQXKQYKQZKRLKRMKRNKROKRPKRQKRRKRSKRTKRUKRVKRWKRXKRYKRZKSLKSMKSNKSOKSPKSQKSRKSSKSTKSUKSVKSWKSXKSYKSZKTLKTMKTNKTOKTPKTQKTRKTSKTTKTUKTVKTWKTXKTYKTZKULKUMKUNKUOKUPKUQKURKUSKUTKUUKUVKUWKUXKUYKUZKVLKVMKVNKVOKVPKVQKVRKVSKVTKVUKVVKVWKVXKVYKVZKWLKWMKWNKWOKWPKWQKWRKWSKWTKWUKWVKWWKWXKWYKWZKXLKXMKXNKXOKXPKXQKXRKXSKXTKXUKXVKXWKXXKXYKXZKYLKYMKYNKYOKYPKYQKYRKYSKYTKYUKYVKYWKYXKYYKYZKZLKZMKZNKZOKZPKZQKZRKZSKZTKZUKZVKZWKZXKZYKZZLLLMLLNLLOLLPLLQLLRLLSLLTLLULLVLLWLLXLLYLLZLMMLMNLMOLMPLMQLMRLMSLMTLMULMVLMWLMXLMYLMZLNMLNNLNOLNPLNQLNRLNSLNTLNULNVLNWLNXLNYLNZLOMLONLOOLOPLOQLORLOSLOTLOULOVLOWLOXLOYLOZLPMLPNLPOLPPLPQLPRLPSLPTLPULPVLPWLPXLPYLPZLQMLQNLQOLQPLQQLQRLQSLQTLQULQVLQWLQXLQYLQZLRMLRNLROLRPLRQLRRLRSLRTLRULRVLRWLRXLRYLRZLSMLSNLSOLSPLSQLSRLSSLSTLSULSVLSWLSXLSYLSZLTMLTNLTOLTPLTQLTRLTSLTTLTULTVLTWLTXLTYLTZLUMLUNLUOLUPLUQLURLUSLUTLUULUVLUWLUXLUYLUZLVMLVNLVOLVPLVQLVRLVSLVTLVULVVLVWLVXLVYLVZLWMLWNLWOLWPLWQLWRLWSLWTLWULWVLWWLWXLWYLWZLXMLXNLXOLXPLXQLXRLXSLXTLXULXVLXWLXXLXYLXZLYMLYNLYOLYPLYQLYRLYSLYTLYULYVLYWLYXLYYLYZLZMLZNLZOLZPLZQLZRLZSLZTLZULZVLZWLZXLZYLZZMMMNMMOMMPMMQMMRMMSMMTMMUMMVMMWMMXMMYMMZMNNMNOMNPMNQMNRMNSMNTMNUMNVMNWMNXMNYMNZMONMOOMOPMOQMORMOSMOTMOUMOVMOWMOXMOYMOZMPNMPOMPPMPQMPRMPSMPTMPUMPVMPWMPXMPYMPZMQNMQOMQPMQQMQRMQSMQTMQUMQVMQWMQXMQYMQZMRNMROMRPMRQMRRMRSMRTMRUMRVMRWMRXMRYMRZMSNMSOMSPMSQMSRMSSMSTMSUMSVMSWMSXMSYMSZMTNMTOMTPMTQMTRMTSMTTMTUMTVMTWMTXMTYMTZMUNMUOMUPMUQMURMUSMUTMUUMUVMUWMUXMUYMUZMVNMVOMVPMVQMVRMVSMVTMVUMVVMVWMVXMVYMVZMWNMWOMWPMWQMWRMWSMWTMWUMWVMWWMWXMWYMWZMXNMXOMXPMXQMXRMXSMXTMXUMXVMXWMXXMXYMXZMYNMYOMYPMYQMYRMYSMYTMYUMYVMYWMYXMYYMYZMZNMZOMZPMZQMZRMZSMZTMZUMZVMZWMZXMZYMZZNNNONNPNNQNNRNNSNNTNNUNNVNNWNNXNNYNNZNOONOPNOQNORNOSNOTNOUNOVNOWNOXNOYNOZNPONPPNPQNPRNPSNPTNPUNPVNPWNPXNPYNPZNQONQPNQQNQRNQSNQTNQUNQVNQWNQXNQYNQZNRONRPNRQNRRNRSNRTNRUNRVNRWNRXNRYNRZNSONSPNSQNSRNSSNSTNSUNSVNSWNSXNSYNSZNTONTPNTQNTRNTSNTTNTUNTVNTWNTXNTYNTZNUONUPNUQNURNUSNUTNUUNUVNUWNUXNUYNUZNVONVPNVQNVRNVSNVTNVUNVVNVWNVXNVYNVZNWONWPNWQNWRNWSNWTNWUNWVNWWNWXNWYNWZNXONXPNXQNXRNXSNXTNXUNXVNXWNXXNXYNXZNYONYPNYQNYRNYSNYTNYUNYVNYWNYXNYYNYZNZONZPNZQNZRNZSNZTNZUNZVNZWNZXNZYNZZOOOPOOQOOROOSOOTOOUOOVOOWOOXOOYOOZOPPOPQOPROPSOPTOPUOPVOPWOPXOPYOPZOQPOQQOQROQSOQTOQUOQVOQWOQXOQYOQZORPORQORRORSORTORUORVORWORXORYORZOSPOSQOSROSSOSTOSUOSVOSWOSXOSYOSZOTPOTQOTROTSOTTOTUOTVOTWOTXOTYOTZOUPOUQOUROUSOUTOUUOUVOUWOUXOUYOUZOVPOVQOVROVSOVTOVUOVVOVWOVXOVYOVZOWPOWQOWROWSOWTOWUOWVOWWOWXOWYOWZOXPOXQOXROXSOXTOXUOXVOXWOXXOXYOXZOYPOYQOYROYSOYTOYUOYVOYWOYXOYYOYZOZPOZQOZROZSOZTOZUOZVOZWOZXOZYOZZPPPQPPRPPSPPTPPUPPVPPWPPXPPYPPZPQQPQRPQSPQTPQUPQVPQWPQXPQYPQZPRQPRRPRSPRTPRUPRVPRWPRXPRYPRZPSQPSRPSSPSTPSUPSVPSWPSXPSYPSZPTQPTRPTSPTTPTUPTVTABUABVABWABXABYABZACBACCACDACEACFACGACHACIACJACKACLACMACNACOACPACQACRACSACTACUACVACWACXACYACZADBADCADDADEADFADGADHADIADJADKADLADMADAAABAACAADAAEAAFAAGAAHAAIAAJAAKAALAAMAANAAOAAPAAQAARAASAATAAUAAVAAWAAXAAYAAZABBABCABDABEABFABGABHABIABJABKABLABMABNABOABPABQABRABSABHAFIAFJAFKAFLAFMAFNAFOAFPAFQAFRAFSAFTAFUAFVAFWAFXAFYAFZAGBAGCAGDAGEAGFAGGAGHAGIAGJAGKAGLAGMAGNAGOAGPAGQAGRAGSAGTAGUAGVAGWAGXAGYAGZAHNADOADPADQADRADSADTADUADVADWADXADYADZAEBAECAEDAEEAEFAEGAEHAEIAEJAEKAELAEMAENAEOAEPAEQAERAESAETAEUAEVAEWAEXAEYAEZAFBAFCAFDAFEAFFAFGAFUAIVAIWAIXAIYAIZAJBAJCAJDAJEAJFAJGAJHAJIAJJAJKAJLAJMAJNAJOAJPAJQAJRAJSAJTAJUAJVAJWAJXAJYAJZAKBAKCAKDAKEAKFAKGAKHAKIAKJAKKAKLAKMAKNAKBAHCAHDAHEAHFAHGAHHAHIAHJAHKAHLAHMAHNAHOAHPAHQAHRAHSAHTAHUAHVAHWAHXAHYAHZAIBAICAIDAIEAIFAIGAIHAIIAIJAIKAILAIMAINAIOAIPAIQAIRAISAITAIIAMJAMKAMLAMMAMNAMOAMPAMQAMRAMSAMTAMUAMVAMWAMXAMYAMZANBANCANDANEANFANGANHANIANJANKANLANMANNANOANPANQANRANSANTANUANVANWANXANYANZAOBAOOAKPAKQAKRAKSAKTAKUAKVAKWAKXAKYAKZALBALCALDALEALFALGALHALIALJALKALLALMALNALOALPALQALRALSALTALUALVALWALXALYALZAMBAMCAMDAMEAMFAMGAMHAMVAPWAPXAPYAPZAQBAQCAQDAQEAQFAQGAQHAQIAQJAQKAQLAQMAQNAQOAQPAQQAQRAQSAQTAQUAQVAQWAQXAQYAQZARBARCARDAREARFARGARHARIARJARKARLARMARNAROARCAODAOEAOFAOGAOHAOIAOJAOKAOLAOMAONAOOAOPAOQAORAOSAOTAOUAOVAOWAOXAOYAOZAPBAPCAPDAPEAPFAPGAPHAPIAPJAPKAPLAPMAPNAPOAPPAPQAPRAPSAPTAPUAPJATKATLATMATNATOATPATQATRATSATTATUATVATWATXATYATZAUBAUCAUDAUEAUFAUGAUHAUIAUJAUKAULAUMAUNAUOAUPAUQAURAUSAUTAUUAUVAUWAUXAUYAUZAVBAVCAVPARQARRARSARTARUARVARWARXARYARZASBASCASDASEASFASGASHASIASJASKASLASMASNASOASPASQASRASSASTASUASVASWASXASYASZATBATCATDATEATFATGATHATIATWAWXAWYAWZAXBAXCAXDAXEAXFAXGAXHAXIAXJAXKAXLAXMAXNAXOAXPAXQAXRAXSAXTAXUAXVAXWAXXAXYAXZAYBAYCAYDAYEAYFAYGAYHAYIAYJAYKAYLAYMAYNAYOAYPAYDAVEAVFAVGAVHAVIAVJAVKAVLAVMAVNAVOAVPAVQAVRAVSAVTAVUAVVAVWAVXAVYAVZAWBAWCAWDAWEAWFAWGAWHAWIAWJAWKAWLAWMAWNAWOAWPAWQAWRAWSAWTAWUAWVAWBLBBMBBNBBOBBPBBQBBRBBSBBTBBUBBVBBWBBXBBYBBZBCCBCDBCEBCFBCGBCHBCIBCJBCKBCLBCMBCNBCOBCPBCQBCRBCSBCTBCUBCVBCWBCXBCYBCZBDCBDDBDEBDFBDGBQAYRAYSAYTAYUAYVAYWAYXAYYAYZAZBAZCAZDAZEAZFAZGAZHAZIAZJAZKAZLAZMAZNAZOAZPAZQAZRAZSAZTAZUAZVAZWAZXAZYAZZBBBCBBDBBEBBFBBGBBHBBIBBJBBKBFDBFEBFFBFGBFHBFIBFJBFKBFLBFMBFNBFOBFPBFQBFRBFSBFTBFUBFVBFWBFXBFYBFZBGCBGDBGEBGFBGGBGHBGIBGJBGKBGLBGMBGNBGOBGPBGQBGRBGSBGTBGUBGVBGWBDHBDIBDJBDKBDLBDMBDNBDOBDPBDQBDRBDSBDTBDUBDVBDWBDXBDYBDZBECBEDBEEBEFBEGBEHBEIBEJBEKBELBEMBENBEOBEPBEQBERBESBETBEUBEVBEWBEXBEYBEZBFCBITBIUBIVBIWBIXBIYBIZBJCBJDBJEBJFBJGBJHBJIBJJBJKBJLBJMBJNBJOBJPBJQBJRBJSBJTBJUBJVBJWBJXBJYBJZBKCBKDBKEBKFBKGBKHBKIBKJBKKBKLBKMBKNBKOBGXBGYBGZBHCBHDBHEBHFBHGBHHBHIBHJBHKBHLBHMBHNBHOBHPBHQBHRBHSBHTBHUBHVBHWBHXBHYBHZBICBIDBIEBIFBIGBIHBIIBIJBIKBILBIMBINBIOBIPBIQBIRBISBMLBMMBMNBMOBMPBMQBMRBMSBMTBMUBMVBMWBMXBMYBMZBNCBNDBNEBNFBNGBNHBNIBNJBNKBNLBNMBNNBNOBNPBNQBNRBNSBNTBNUBNVBNWBNXBNYBNZBOCBODBOEBOFBOGBKPBKQBKRBKSBKTBKUBKVBKWBKXBKYBKZBLCBLDBLEBLFBLGBLHBLIBLJBLKBLLBLMBLNBLOBLPBLQBLRBLSBLTBLUBLVBLWBLXBLYBLZBMCBMDBMEBMFBMGBMHBMIBMJBMKBQDBQEBQFBQGBQHBQIBQJBQKBQLBQMBQNBQOBQPBQQBQRBQSBQTBQUBQVBQWBQXBQYBQZBRCBRDBREBRFBRGBRHBRIBRJBRKBRLBRMBRNBROBRPBRQBRRBRSBRTBRUBRVBRWBOHBOIBOJBOKBOLBOMBONBOOBOPBOQBORBOSBOTBOUBOVBOWBOXBOYBOZBPCBPDBPEBPFBPGBPHBPIBPJBPKBPLBPMBPNBPOBPPBPQBPRBPSBPTBPUBPVBPWBPXBPYBPZBQCBTTBTUBTVBTWBTXBTYBTZBUCBUDBUEBUFBUGBUHBUIBUJBUKBULBUMBUNBUOBUPBUQBURBUSBUTBUUBUVBUWBUXBUYBUZBVCBVDBVEBVFBVGBVHBVIBVJBVKBVLBVMBVNBVOBRXBRYBRZBSCBSDBSEBSFBSGBSHBSIBSJBSKBSLBSMBSNBSOBSPBSQBSRBSSBSTBSUBSVBSWBSXBSYBSZBTCBTDBTEBTFBTGBTHBTIBTJBTKBTLBTMBTNBTOBTPBTQBTRBTSBXLBXMBXNBXOBXPBXQBXRBXSBXTBXUBXVBXWBXXBXYBXZBYCBYDBYEBYFBYGBYHBYIBYJBYKBYLBYMBYNBYOBYPBYQBYRBYSBYTBYUBYVBYWBYXBYYBYZBZCBZDBZEBZFBZGBVPBVQBVRBVSBVTBVUBVVBVWBVXBVYBVZBWCBWDBWEBWFBWGBWHBWIBWJBWKBWLBWMBWNBWOBWPBWQBWRBWSBWTBWUBWVBWWBWXBWYBWZBXCBXDBXEBXFBXGBXHBXIBXJBXKBCDFCDGCDHCDICDJCDKCDLCDMCDNCDOCDPCDQCDRCDSCDTCDUCDVCDWCDXCDYCDZCEDCEECEFCEGCEHCEICEJCEKCELCEMCENCEOCEPCEQCERCESCETCEUCEVCEWCEXCEYCEZZHBZIBZJBZKBZLBZMBZNBZOBZPBZQBZRBZSBZTBZUBZVBZWBZXBZYBZZCCCDCCECCFCCGCCHCCICCJCCKCCLCCMCCNCCOCCPCCQCCRCCSCCTCCUCCVCCWCCXCCYCCZCDDCDECGYCGZCHDCHECHFCHGCHHCHICHJCHKCHLCHMCHNCHOCHPCHQCHRCHSCHTCHUCHVCHWCHXCHYCHZCIDCIECIFCIGCIHCIICIJCIKCILCIMCINCIOCIPCIQCIRCISCITCIUCIVCFDCFECFFCFGCFHCFICFJCFKCFLCFMCFNCFOCFPCFQCFRCFSCFTCFUCFVCFWCFXCFYCFZCGDCGECGFCGGCGHCGICGJCGKCGLCGMCGNCGOCGPCGQCGRCGSCGTCGUCGVCGWCGXCKUCKVCKWCKXCKYCKZCLDCLECLFCLGCLHCLICLJCLKCLLCLMCLNCLOCLPCLQCLRCLSCLTCLUCLVCLWCLXCLYCLZCMDCMECMFCMGCMHCMICMJCMKCMLCMMCMNCMOCMPCMQCMRCIWCIXCIYCIZCJDCJECJFCJGCJHCJICJJCJKCJLCJMCJNCJOCJPCJQCJRCJSCJTCJUCJVCJWCJXCJYCJZCKDCKECKFCKGCKHCKICKJCKKCKLCKMCKNCKOCKPCKQCKRCKSCKTCOQCORCOSCOTCOUCOVCOWCOXCOYCOZCPDCPECPFCPGCPHCPICPJCPKCPLCPMCPNCPOCPPCPQCPRCPSCPTCPUCPVCPWCPXCPYCPZCQDCQECQFCQGCQHCQICQJCQKCQLCQMCQNCMSCMTCMUCMVCMWCMXCMYCMZCNDCNECNFCNGCNHCNICNJCNKCNLCNMCNNCNOCNPCNQCNRCNSCNTCNUCNVCNWCNXCNYCNZCODCOECOFCOGCOHCOICOJCOKCOLCOMCONCOOCOPCSMCSNCSOCSPCSQCSRCSSCSTCSUCSVCSWCSXCSYCSZCTDCTECTFCTGCTHCTICTJCTKCTLCTMCTNCTOCTPCTQCTRCTSCTTCTUCTVCTWCTXCTYCTZCUDCUECUFCUGCUHCUICUJCQOCQPCQQCQRCQSCQTCQUCQVCQWCQXCQYCQZCRDCRECRFCRGCRHCRICRJCRKCRLCRMCRNCROCRPCRQCRRCRSCRTCRUCRVCRWCRXCRYCRZCSDCSECSFCSGCSHCSICSJCSKCSLCWICWJCWKCWLCWMCWNCWOCWPCWQCWRCWSCWTCWUCWVCWWCWXCWYCWZCXDCXECXFCXGCXHCXICXJCXKCXLCXMCXNCXOCXPCXQCXRCXSCXTCXUCXVCXWCXXCXYCXZCYDCYECYFCUKCULCUMCUNCUOCUPCUQCURCUSCUTCUUCUVCUWCUXCUYCUZCVDCVECVFCVGCVHCVICVJCVKCVLCVMCVNCVOCVPCVQCVRCVSCVTCVUCVVCVWCVXCVYCVZCWDCWECWFCWGCWHEDDFDDGDDHDDIDDJDDKDDLDDMDDNDDODDPDDQDDRDDSDDTDDUDDVDDWDDXDDYDDZDEEDEFDEGDEHDEIDEJDEKDELDEMDENDEODEPDEQDERDESDETDEUDEVDEWDEXDEYDEZDFCYGCYHCYICYJCYKCYLCYMCYNCYOCYPCYQCYRCYSCYTCYUCYVCYWCYXCYYCYZCZDCZECZFCZGCZHCZICZJCZKCZLCZMCZNCZOCZPCZQCZRCZSCZTCZUCZVCZWCZXCZYCZZDDDEDHFDHGDHHDHIDHJDHKDHLDHMDHNDHODHPDHQDHRDHSDHTDHUDHVDHWDHXDHYDHZDIEDIFDIGDIHDIIDIJDIKDILDIMDINDIODIPDIQDIRDISDITDIUDIVDIWDIXDIYDIZDJEDFFDFGDFHDFIDFJDFKDFLDFMDFNDFODFPDFQDFRDFSDFTDFUDFVDFWDFXDFYDFZDGEDGFDGGDGHDGIDGJDGKDGLDGMDGNDGODGPDGQDGRDGSDGTDGUDGVDGWDGXDGYDGZDHEDLFDLGDLHDLIDLJDLKDLLDLMDLNDLODLPDLQDLRDLSDLTDLUDLVDLWDLXDLYDLZDMEDMFDMGDMHDMIDMJDMKDMLDMMDMNDMODMPDMQDMRDMSDMTDMUDMVDMWDMXDMYDMZDNEDJFDJGDJHDJIDJJDJKDJLDJMDJNDJODJPDJQDJRDJSDJTDJUDJVDJWDJXDJYDJZDKEDKFDKGDKHDKIDKJDKKDKLDKMDKNDKODKPDKQDKRDKSDKTDKUDKVDKWDKXDKYDKZDLEDPFDPGDPHDPIDPJDPKDPLDPMDPNDPODPPDPQDPRDPSDPTDPUDPVDPWDPXDPYDPZDQEDQFDQGDQHDQIDQJDQKDQLDQMDQNDQODQPDQQDQRDQSDQTDQUDQVDQWDQXDQYDQZDREDNFDNGDNHDNIDNJDNKDNLDNMDNNDNODNPDNQDNRDNSDNTDNUDNVDNWDNXDNYDNZDOEDOFDOGDOHDOIDOJDOKDOLDOMDONDOODOPDOQDORDOSDOTDOUDOVDOWDOXDOYDOZDPEDTFDTGDTHDTIDTJDTKDTLDTMDTNDTODTPDTQDTRDTSDTTDTUDTVDTWDTXDTYDTZDUEDUFDUGDUHDUIDUJDUKDULDUMDUNDUODUPDUQDURDUSDUTDUUDUVDUWDUXDUYDUZDVEDRFDRGDRHDRIDRJDRKDRLDRMDRNDRODRPDRQDRRDRSDRTDRUDRVDRWDRXDRYDRZDSEDSFDSGDSHDSIDSJDSKDSLDSMDSNDSODSPDSQDSRDSSDSTDSUDSVDSWDSXDSYDSZDTEDXFDXGDXHDXIDXJDXKDXLDXMDXNDXODXPDXQDXRDXSDXTDXUDXVDXWDXXDXYDXZDYEDYFDYGDYHDYIDYJDYKDYLDYMDYNDYODYPDYQDYRDYSDYTDYUDYVDYWDYXDYYDYZDZEDVFDVGDVHDVIDVJDVKDVLDVMDVNDVODVPDVQDVRDVSDVTDVUDVVDVWDVXDVYDVZDWEDWFDWGDWHDWIDWJDWKDWLDWMDWNDWODWPDWQDWRDWSDWTDWUDWVDWWDWXDWYDWZDXFGEFHEFIEFJEFKEFLEFMEFNEFOEFPEFQEFREFSEFTEFUEFVEFWEFXEFYEFZEGFEGGEGHEGIEGJEGKEGLEGMEGNEGOEGPEGQEGREGSEGTEGUEGVEGWEGXEGYEGZEHFEHGEHHEEDZFDZGDZHDZIDZJDZKDZLDZMDZNDZODZPDZQDZRDZSDZTDZUDZVDZWDZXDZYDZZEEEFEEGEEHEEIEEJEEKEELEEMEENEEOEEPEEQEEREESEETEEUEEVEEWEEXEEYEEZEFFEJKEJLEJMEJNEJOEJPEJQEJREJSEJTEJUEJVEJWEJXEJYEJZEKFEKGEKHEKIEKJEKKEKLEKMEKNEKOEKPEKQEKREKSEKTEKUEKVEKWEKXEKYEKZELFELGELHELIELJELKELLEHIEHJEHKEHLEHMEHNEHOEHPEHQEHREHSEHTEHUEHVEHWEHXEHYEHZEIFEIGEIHEIIEIJEIKEILEIMEINEIOEIPEIQEIREISEITEIUEIVEIWEIXEIYEIZEJFEJGEJHEJIEJJENOENPENQENRENSENTENUENVENWENXENYENZEOFEOGEOHEOIEOJEOKEOLEOMEONEOOEOPEOQEOREOSEOTEOUEOVEOWEOXEOYEOZEPFEPGEPHEPIEPJEPKEPLEPMEPNEPOEPPELMELNELOELPELQELRELSELTELUELVELWELXELYELZEMFEMGEMHEMIEMJEMKEMLEMMEMNEMOEMPEMQEMREMSEMTEMUEMVEMWEMXEMYEMZENFENGENHENIENJENKENLENMENNERSERTERUERVERWERXERYERZESFESGESHESIESJESKESLESMESNESOESPESQESRESSESTESUESVESWESXESYESZETFETGETHETIETJETKETLETMETNETOETPETQETRETSETTEPQEPREPSEPTEPUEPVEPWEPXEPYEPZEQFEQGEQHEQIEQJEQKEQLEQMEQNEQOEQPEQQEQREQSEQTEQUEQVEQWEQXEQYEQZERFERGERHERIERJERKERLERMERNEROERPERQERREVWEVXEVYEVZEWFEWGEWHEWIEWJEWKEWLEWMEWNEWOEWPEWQEWREWSEWTEWUEWVEWWEWXEWYEWZEXFEXGEXHEXIEXJEXKEXLEXMEXNEXOEXPEXQEXREXSEXTEXUEXVEXWEXXETUETVETWETXETYETZEUFEUGEUHEUIEUJEUKEULEUMEUNEUOEUPEUQEUREUSEUTEUUEUVEUWEUXEUYEUZEVFEVGEVHEVIEVJEVKEVLEVMEVNEVOEVPEVQEVREVSEVTEVUEVVEFFGFFHFFIFFJFFKFFLFFMFFNFFOFFPFFQFFRFFSFFTFFUFFVFFWFFXFFYFFZFGGFGHFGIFGJFGKFGLFGMFGNFGOFGPFGQFGRFGSFGTFGUFGVFGWFGXFGYFGZFHGFHHFHIFHJXYEXZEYFEYGEYHEYIEYJEYKEYLEYMEYNEYOEYPEYQEYREYSEYTEYUEYVEYWEYXEYYEYZEZFEZGEZHEZIEZJEZKEZLEZMEZNEZOEZPEZQEZREZSEZTEZUEZVEZWEZXEZYEZZFFJOFJPFJQFJRFJSFJTFJUFJVFJWFJXFJYFJZFKGFKHFKIFKJFKKFKLFKMFKNFKOFKPFKQFKRFKSFKTFKUFKVFKWFKXFKYFKZFLGFLHFLIFLJFLKFLLFLMFLNFLOFLPFLQFLRFHKFHLFHMFHNFHOFHPFHQFHRFHSFHTFHUFHVFHWFHXFHYFHZFIGFIHFIIFIJFIKFILFIMFINFIOFIPFIQFIRFISFITFIUFIVFIWFIXFIYFIZFJGFJHFJIFJJFJKFJLFJMFJNFNWFNXFNYFNZFOGFOHFOIFOJFOKFOLFOMFONFOOFOPFOQFORFOSFOTFOUFOVFOWFOXFOYFOZFPGFPHFPIFPJFPKFPLFPMFPNFPOFPPFPQFPRFPSFPTFPUFPVFPWFPXFPYFPZFLSFLTFLUFLVFLWFLXFLYFLZFMGFMHFMIFMJFMKFMLFMMFMNFMOFMPFMQFMRFMSFMTFMUFMVFMWFMXFMYFMZFNGFNHFNIFNJFNKFNLFNMFNNFNOFNPFNQFNRFNSFNTFNUFNVFSKFSLFSMFSNFSOFSPFSQFSRFSSFSTFSUFSVFSWFSXFSYFSZFTGFTHFTIFTJFTKFTLFTMFTNFTOFTPFTQFTRFTSFTTFTUFTVFTWFTXFTYFTZFUGFUHFUIFUJFUKFULFUMFUNFQGFQHFQIFQJFQKFQLFQMFQNFQOFQPFQQFQRFQSFQTFQUFQVFQWFQXFQYFQZFRGFRHFRIFRJFRKFRLFRMFRNFROFRPFRQFRRFRSFRTFRUFRVFRWFRXFRYFRZFSGFSHFSIFSJFWSFWTFWUFWVFWWFWXFWYFWZFXGFXHFXIFXJFXKFXLFXMFXNFXOFXPFXQFXRFXSFXTFXUFXVFXWFXXFXYFXZFYGFYHFYIFYJFYKFYLFYMFYNFYOFYPFYQFYRFYSFYTFYUFYVFUOFUPFUQFURFUSFUTFUUFUVFUWFUXFUYFUZFVGFVHFVIFVJFVKFVLFVMFVNFVOFVPFVQFVRFVSFVTFVUFVVFVWFVXFVYFVZFWGFWHFWIFWJFWKFWLFWMFWNFWOFWPFWQFWRHGHIGHJGHKGHLGHMGHNGHOGHPGHQGHRGHSGHTGHUGHVGHWGHXGHYGHZGIHGIIGIJGIKGILGIMGINGIOGIPGIQGIRGISGITGIUGIVGIWGIXGIYGIZGJHGJIGJJGJKGJLGJMGJFYWFYXFYYFYZFZGFZHFZIFZJFZKFZLFZMFZNFZOFZPFZQFZRFZSFZTFZUFZVFZWFZXFZYFZZGGGHGGIGGJGGKGGLGGMGGNGGOGGPGGQGGRGGSGGTGGUGGVGGWGGXGGYGGZGHTGLUGLVGLWGLXGLYGLZGMHGMIGMJGMKGMLGMMGMNGMOGMPGMQGMRGMSGMTGMUGMVGMWGMXGMYGMZGNHGNIGNJGNKGNLGNMGNNGNOGNPGNQGNRGNSGNTGNUGNVGNWGNXGNYGNNGJOGJPGJQGJRGJSGJTGJUGJVGJWGJXGJYGJZGKHGKIGKJGKKGKLGKMGKNGKOGKPGKQGKRGKSGKTGKUGKVGKWGKXGKYGKZGLHGLIGLJGLKGLLGLMGLNGLOGLPGLQGLRGLSGLMGQNGQOGQPGQQGQRGQSGQTGQUGQVGQWGQXGQYGQZGRHGRIGRJGRKGRLGRMGRNGROGRPGRQGRRGRSGRTGRUGRVGRWGRXGRYGRZGSHGSIGSJGSKGSLGSMGSNGSOGSPGSQGSRGSZGOHGOIGOJGOKGOLGOMGONGOOGOPGOQGORGOSGOTGOUGOVGOWGOXGOYGOZGPHGPIGPJGPKGPLGPMGPNGPOGPPGPQGPRGPSGPTGPUGPVGPWGPXGPYGPZGQHGQIGQJGQKGQLGQYGUZGVHGVIGVJGVKGVLGVMGVNGVOGVPGVQGVRGVSGVTGVUGVVGVWGVXGVYGVZGWHGWIGWJGWKGWLGWMGWNGWOGWPGWQGWRGWSGWTGWUGWVGWWGWXGWYGWZGXHGXIGXJGXKGXSGSTGSUGSVGSWGSXGSYGSZGTHGTIGTJGTKGTLGTMGTNGTOGTPGTQGTRGTSGTTGTUGTVGTWGTXGTYGTZGUHGUIGUJGUKGULGUMGUNGUOGUPGUQGURGUSGUTGUUGUVGUWGUXGURGZSGZTGZUGZVGZWGZXGZYGZZHHHIHHJHHKHHLHHMHHNHHOHHPHHQHHRHHSHHTHHUHHVHHWHHXHHYHHZHIIHIJHIKHILHIMHINHIOHIPHIQHIRHISHITHIUHIVHIWHIXHIYHLGXMGXNGXOGXPGXQGXRGXSGXTGXUGXVGXWGXXGXYGXZGYHGYIGYJGYKGYLGYMGYNGYOGYPGYQGYRGYSGYTGYUGYVGYWGYXGYYGYZGZHGZIGZJGZKGZLGZMGZNGZOGZPGZQGZLPHLQHLRHLSHLTHLUHLVHLWHLXHLYHLZHMIHMJHMKHMLHMMHMNHMOHMPHMQHMRHMSHMTHMUHMVHMWHMXHMYHMZHNIHNJHNKHNLHNMHNNHNOHNPHNQHNRHNSHNTHNUHNVHNWHIZHJIHJJHJKHJLHJMHJNHJOHJPHJQHJRHJSHJTHJUHJVHJWHJXHJYHJZHKIHKJHKKHKLHKMHKNHKOHKPHKQHKRHKSHKTHKUHKVHKWHKXHKYHKZHLIHLJHLKHLLHLMHLNHLOHQNHQOHQPHQQHQRHQSHQTHQUHQVHQWHQXHQYHQZHRIHRJHRKHRLHRMHRNHROHRPHRQHRRHRSHRTHRUHRVHRWHRXHRYHRZHSIHSJHSKHSLHSMHSNHSOHSPHSQHSRHSSHSTHSUHNXHNYHNZHOIHOJHOKHOLHOMHONHOOHOPHOQHORHOSHOTHOUHOVHOWHOXHOYHOZHPIHPJHPKHPLHPMHPNHPOHPPHPQHPRHPSHPTHPUHPVHPWHPXHPYHPZHQIHQJHQKHQLHQMHVLHVMHVNHVOHVPHVQHVRHVSHVTHVUHVVHVWHVXHVYHVZHWIHWJHWKHWLHWMHWNHWOHWPHWQHWRHWSHWTHWUHWVHWWHWXHWYHWZHXIHXJHXKHXLHXMHXNHXOHXPHXQHXRHXSHSVHSWHSXHSYHSZHTIHTJHTKHTLHTMHTNHTOHTPHTQHTRHTSHTTHTUHTVHTWHTXHTYHTZHUIHUJHUKHULHUMHUNHUOHUPHUQHURHUSHUTHUUHUVHUWHUXHUYHUZHVIHVJHVKHIIKIILIIMIINIIOIIPIIQIIRIISIITIIUIIVIIWIIXIIYIIZIJJIJKIJLIJMIJNIJOIJPIJQIJRIJSIJTIJUIJVIJWIJXIJYIJZIKJIKKIKLIKMIKNIKOIKPIKQIKRIKSIKTXTHXUHXVHXWHXXHXYHXZHYIHYJHYKHYLHYMHYNHYOHYPHYQHYRHYSHYTHYUHYVHYWHYXHYYHYZHZIHZJHZKHZLHZMHZNHZOHZPHZQHZRHZSHZTHZUHZVHZWHZXHZYHZZIIIJINNINOINPINQINRINSINTINUINVINWINXINYINZIOJIOKIOLIOMIONIOOIOPIOQIORIOSIOTIOUIOVIOWIOXIOYIOZIPJIPKIPLIPMIPNIPOIPPIPQIPRIPSIPTIPUIPVIPWIKUIKVIKWIKXIKYIKZILJILKILLILMILNILOILPILQILRILSILTILUILVILWILXILYILZIMJIMKIMLIMMIMNIMOIMPIMQIMRIMSIMTIMUIMVIMWIMXIMYIMZINJINKINLINMISQISRISSISTISUISVISWISXISYISZITJITKITLITMITNITOITPITQITRITSITTITUITVITWITXITYITZIUJIUKIULIUMIUNIUOIUPIUQIURIUSIUTIUUIUVIUWIUXIUYIUZIPXIPYIPZIQJIQKIQLIQMIQNIQOIQPIQQIQRIQSIQTIQUIQVIQWIQXIQYIQZIRJIRKIRLIRMIRNIROIRPIRQIRRIRSIRTIRUIRVIRWIRXIRYIRZISJISKISLISMISNISOISPIXTIXUIXVIXWIXXIXYIXZIYJIYKIYLIYMIYNIYOIYPIYQIYRIYSIYTIYUIYVIYWIYXIYYIYZIZJIZKIZLIZMIZNIZOIZPIZQIZRIZSIZTIZUIZVIZWIZXIZYIZZJJJKJJLJJIVJIVKIVLIVMIVNIVOIVPIVQIVRIVSIVTIVUIVVIVWIVXIVYIVZIWJIWKIWLIWMIWNIWOIWPIWQIWRIWSIWTIWUIWVIWWIWXIWYIWZIXJIXKIXLIXMIXNIXOIXPIXQIXRIXSYJLZJMKJMLJMMJMNJMOJMPJMQJMRJMSJMTJMUJMVJMWJMXJMYJMZJNKJNLJNMJNNJNOJNPJNQJNRJNSJNTJNUJNVJNWJNXJNYJNZJOKJOLJOMJONJOOJOPJOQJORJOSJOTJOMJJNJJOJJPJJQJJRJJSJJTJJUJJVJJWJJXJJYJJZJKKJKLJKMJKNJKOJKPJKQJKRJKSJKTJKUJKVJKWJKXJKYJKZJLKJLLJLMJLNJLOJLPJLQJLRJLSJLTJLUJLVJLWJLXJLQJRRJRSJRTJRUJRVJRWJRXJRYJRZJSKJSLJSMJSNJSOJSPJSQJSRJSSJSTJSUJSVJSWJSXJSYJSZJTKJTLJTMJTNJTOJTPJTQJTRJTSJTTJTUJTVJTWJTXJTYJTZJUKJULJUUJOVJOWJOXJOYJOZJPKJPLJPMJPNJPOJPPJPQJPRJPSJPTJPUJPVJPWJPXJPYJPZJQKJQLJQMJQNJQOJQPJQQJQRJQSJQTJQUJQVJQWJQXJQYJQZJRKJRLJRMJRNJROJRPJRYJWZJXKJXLJXMJXNJXOJXPJXQJXRJXSJXTJXUJXVJXWJXXJXYJXZJYKJYLJYMJYNJYOJYPJYQJYRJYSJYTJYUJYVJYWJYXJYYJYZJZKJZLJZMJZNJZOJZPJZQJZRJZSJZTJZMJUNJUOJUPJUQJURJUSJUTJUUJUVJUWJUXJUYJUZJVKJVLJVMJVNJVOJVPJVQJVRJVSJVTJVUJVVJVWJVXJVYJVZJWKJWLJWMJWNJWOJWPJWQJWRJWSJWTJWUJWVJWWJWXJWMTKMUKMVKMWKMXKMYKMZKNLKNMKNNKNOKNPKNQKNRKNSKNTKNUKNVKNWKNXKNYKNZKOLKOMKONKOOKOPKOQKORKOSKOTKOUKOVKOWKOXKOYKOZKPLKPMKPNKPOKPPKPQKPRKUJZVJZWJZXJZYJZZKKKLKKMKKNKKOKKPKKQKKRKKSKKTKKUKKVKKWKKXKKYKKZKLLKLMKLNKLOKLPKLQKLRKLSKLTKLUKLVKLWKLXKLYKLZKMLKMMKMNKMOKMPKMQKMRKMSKSRKSSKSTKSUKSVKSWKSXKSYKSZKTLKTMKTNKTOKTPKTQKTRKTSKTTKTUKTVKTWKTXKTYKTZKULKUMKUNKUOKUPKUQKURKUSKUTKUUKUVKUWKUXKUYKUZKVLKVMKVNKVOKVPKPSKPTKPUKPVKPWKPXKPYKPZKQLKQMKQNKQOKQPKQQKQRKQSKQTKQUKQVKQWKQXKQYKQZKRLKRMKRNKROKRPKRQKRRKRSKRTKRUKRVKRWKRXKRYKRZKSLKSMKSNKSOKSPKSQKYPKYQKYRKYSKYTKYUKYVKYWKYXKYYKYZKZLKZMKZNKZOKZPKZQKZRKZSKZTKZUKZVKZWKZXKZYKZZLLLMLLNLLOLLPLLQLLRLLSLLTLLULLVLLWLLXLLYLLZLMMLMNLMOLMPVQKVRKVSKVTKVUKVVKVWKVXKVYKVZKWLKWMKWNKWOKWPKWQKWRKWSKWTKWUKWVKWWKWXKWYKWZKXLKXMKXNKXOKXPKXQKXRKXSKXTKXUKXVKXWKXXKXYKXZKYLKYMKYNKYOKLPSLPTLPULPVLPWLPXLPYLPZLQMLQNLQOLQPLQQLQRLQSLQTLQULQVLQWLQXLQYLQZLRMLRNLROLRPLRQLRRLRSLRTLRULRVLRWLRXLRYLRZLSMLSNLSOLSPLSQLSRLSSLSTLMQLMRLMSLMTLMULMVLMWLMXLMYLMZLNMLNNLNOLNPLNQLNRLNSLNTLNULNVLNWLNXLNYLNZLOMLONLOOLOPLOQLORLOSLOTLOULOVLOWLOXLOYLOZLPMLPNLPOLPPLPQLPRLVWLVXLVYLVZLWMLWNLWOLWPLWQLWRLWSLWTLWULWVLWWLWXLWYLWZLXMLXNLXOLXPLXQLXRLXSLXTLXULXVLXWLXXLXYLXZLYMLYNLYOLYPLYQLYRLYSLYTLYULYVLYWLYXLSULSVLSWLSXLSYLSZLTMLTNLTOLTPLTQLTRLTSLTTLTULTVLTWLTXLTYLTZLUMLUNLUOLUPLUQLURLUSLUTLUULUVLUWLUXLUYLUZLVMLVNLVOLVPLVQLVRLVSLVTLVULVVOMOPMOQMORMOSMOTMOUMOVMOWMOXMOYMOZMPNMPOMPPMPQMPRMPSMPTMPUMPVMPWMPXMPYMPZMQNMQOMQPMQQMQRMQSMQTMQUMQVMQWMQXMQYMQZMRNMROMRPMRQMRRMRSMRLYYLYZLZMLZNLZOLZPLZQLZRLZSLZTLZULZVLZWLZXLZYLZZMMMNMMOMMPMMQMMRMMSMMTMMUMMVMMWMMXMMYMMZMNNMNOMNPMNQMNRMNSMNTMNUMNVMNWMNXMNYMNZMONMOYMUZMVNMVOMVPMVQMVRMVSMVTMVUMVVMVWMVXMVYMVZMWNMWOMWPMWQMWRMWSMWTMWUMWVMWWMWXMWYMWZMXNMXOMXPMXQMXRMXSMXTMXUMXVMXWMXXMXYMXZMYNMYOMYPMYTMRUMRVMRWMRXMRYMRZMSNMSOMSPMSQMSRMSSMSTMSUMSVMSWMSXMSYMSZMTNMTOMTPMTQMTRMTSMTTMTUMTVMTWMTXMTYMTZMUNMUOMUPMUQMURMUSMUTMUUMUVMUWMUXMUOXNOYNOZNPONPPNPQNPRNPSNPTNPUNPVNPWNPXNPYNPZNQONQPNQQNQRNQSNQTNQUNQVNQWNQXNQYNQZNRONRPNRQNRRNRSNRTNRUNRVNRWNRXNRYNRZNSONSPNSQNSRNSSNQMYRMYSMYTMYUMYVMYWMYXMYYMYZMZNMZOMZPMZQMZRMZSMZTMZUMZVMZWMZXMZYMZZNNNONNPNNQNNRNNSNNTNNUNNVNNWNNXNNYNNZNOONOPNOQNORNOSNOTNOUNOVNOWNWPNWQNWRNWSNWTNWUNWVNWWNWXNWYNWZNXONXPNXQNXRNXSNXTNXUNXVNXWNXXNXYNXZNYONYPNYQNYRNYSNYTNYUNYVNYWNYXNYYNYZNZONZPNZQNZRNZSNZTNZUNZVNZWNSTNSUNSVNSWNSXNSYNSZNTONTPNTQNTRNTSNTTNTUNTVNTWNTXNTYNTZNUONUPNUQNURNUSNUTNUUNUVNUWNUXNUYNUZNVONVPNVQNVRNVSNVTNVUNVVNVWNVXNVYNVZNWONORXORYORZOSPOSQOSROSSOSTOSUOSVOSWOSXOSYOSZOTPOTQOTROTSOTTOTUOTVOTWOTXOTYOTZOUPOUQOUROUSOUTOUUOUVOUWOUXOUYOUZOVPOVQOVROVSOVTOVUOVVOVWZXNZYNZZOOOPOOQOOROOSOOTOOUOOVOOWOOXOOYOOZOPPOPQOPROPSOPTOPUOPVOPWOPXOPYOPZOQPOQQOQROQSOQTOQUOQVOQWOQXOQYOQZORPORQORRORSORTORUORVORWOZXOZYOZZPPPQPPRPPSPPTPPUPPVPPWPPXPPYPPZPQQPQRPQSPQTPQUPQVPQWPQXPQYPQZPRQPRRPRSPRTPRUPRVPRWPRXPRYPRZPSQPSRPSSPSTPSUPSVPSWPSXPSYPSZPTOVXOVYOVZOWPOWQOWROWSOWTOWUOWVOWWOWXOWYOWZOXPOXQOXROXSOXTOXUOXVOXWOXXOXYOXZOYPOYQOYROYSOYTOYUOYVOYWOYXOYYOYZOZPOZQOZROZSOZTOZUOZVOZWQPTRPTSPTTPTUPTV \ No newline at end of file diff --git a/internal-complibs/zlib-ng-2.0.6/test/GH-361/test.txt b/internal-complibs/zlib-ng-2.0.7/test/GH-361/test.txt similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/GH-361/test.txt rename to internal-complibs/zlib-ng-2.0.7/test/GH-361/test.txt diff --git a/internal-complibs/zlib-ng-2.0.6/test/GH-364/test.bin b/internal-complibs/zlib-ng-2.0.7/test/GH-364/test.bin similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/GH-364/test.bin rename to internal-complibs/zlib-ng-2.0.7/test/GH-364/test.bin diff --git a/internal-complibs/zlib-ng-2.0.6/test/GH-382/defneg3.dat b/internal-complibs/zlib-ng-2.0.7/test/GH-382/defneg3.dat similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/GH-382/defneg3.dat rename to internal-complibs/zlib-ng-2.0.7/test/GH-382/defneg3.dat diff --git a/internal-complibs/zlib-ng-2.0.6/test/GH-751/test.txt b/internal-complibs/zlib-ng-2.0.7/test/GH-751/test.txt similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/GH-751/test.txt rename to internal-complibs/zlib-ng-2.0.7/test/GH-751/test.txt diff --git a/internal-complibs/zlib-ng-2.0.6/test/GH-979/pigz-2.6.tar.gz b/internal-complibs/zlib-ng-2.0.7/test/GH-979/pigz-2.6.tar.gz similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/GH-979/pigz-2.6.tar.gz rename to internal-complibs/zlib-ng-2.0.7/test/GH-979/pigz-2.6.tar.gz diff --git a/internal-complibs/zlib-ng-2.0.6/test/Makefile.in b/internal-complibs/zlib-ng-2.0.7/test/Makefile.in similarity index 92% rename from internal-complibs/zlib-ng-2.0.6/test/Makefile.in rename to internal-complibs/zlib-ng-2.0.7/test/Makefile.in index 6a075d36..98ff6f1b 100644 --- a/internal-complibs/zlib-ng-2.0.6/test/Makefile.in +++ b/internal-complibs/zlib-ng-2.0.7/test/Makefile.in @@ -91,7 +91,7 @@ CVE-2003-0107$(EXE): CVE-2003-0107.o $(CC) $(CFLAGS) -o $@ CVE-2003-0107.o $(TEST_LDFLAGS) .PHONY: ghtests -ghtests: testGH-361 testGH-364 testGH-751 +ghtests: testGH-361 testGH-364 testGH-751 testGH-1235 .PHONY: testGH-361 testGH-361: @@ -108,9 +108,16 @@ testGH-364: switchlevels$(EXE) testGH-751: $(QEMU_RUN) ../minigzip$(EXE) <$(SRCDIR)/GH-751/test.txt | $(QEMU_RUN) ../minigzip$(EXE) -d >/dev/null +gh1235$(EXE): $(SRCDIR)/gh1235.c + $(CC) $(CFLAGS) -I.. -I$(SRCTOP) -o $@ $< $(TEST_LDFLAGS) + +.PHONY: testGH-1235 +testGH-1235: gh1235$(EXE) + $(QEMU_RUN) ./gh1235$(EXE) + clean: rm -f *.o *.gcda *.gcno *.gcov - rm -f CVE-2003-0107$(EXE) switchlevels$(EXE) + rm -f CVE-2003-0107$(EXE) switchlevels$(EXE) gh1235$(EXE) distclean: rm -f Makefile diff --git a/internal-complibs/zlib-ng-2.0.6/test/README.md b/internal-complibs/zlib-ng-2.0.7/test/README.md similarity index 90% rename from internal-complibs/zlib-ng-2.0.6/test/README.md rename to internal-complibs/zlib-ng-2.0.7/test/README.md index b15b01df..107a1caf 100644 --- a/internal-complibs/zlib-ng-2.0.6/test/README.md +++ b/internal-complibs/zlib-ng-2.0.7/test/README.md @@ -7,7 +7,8 @@ Contents |[CVE-2002-0059](https://nvd.nist.gov/vuln/detail/CVE-2002-0059)|inflateEnd to release memory more than once| |[CVE-2004-0797](https://nvd.nist.gov/vuln/detail/CVE-2004-0797)|Error handling in inflate and inflateBack causes crash| |[CVE-2005-1849](https://nvd.nist.gov/vuln/detail/CVE-2005-1849)|inftrees.h bug causes crash| -|[CVE-2005-2096](https://nvd.nist.gov/vuln/detail/CVE-2005-2096)|Buffer overflow when incomplete code description +|[CVE-2005-2096](https://nvd.nist.gov/vuln/detail/CVE-2005-2096)|Buffer overflow when incomplete code description| +|[CVE-2018-25032](https://nvd.nist.gov/vuln/detail/CVE-2018-25032)|Memory corruption when compressing if the input has many distant matches.| |[GH-361](https://github.com/zlib-ng/zlib-ng/issues/361)|Test case for overlapping matches| |[GH-364](https://github.com/zlib-ng/zlib-ng/issues/364)|Test case for switching compression levels| |[GH-382](https://github.com/zlib-ng/zlib-ng/issues/382)|Test case for deflateEnd returning -3 in deflate quick| diff --git a/internal-complibs/zlib-ng-2.0.6/test/abi/ignore b/internal-complibs/zlib-ng-2.0.7/test/abi/ignore similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/abi/ignore rename to internal-complibs/zlib-ng-2.0.7/test/abi/ignore diff --git a/internal-complibs/zlib-ng-2.0.6/test/abi/zlib-v1.2.11-arm-linux-gnueabihf.abi b/internal-complibs/zlib-ng-2.0.7/test/abi/zlib-v1.2.11-arm-linux-gnueabihf.abi similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/abi/zlib-v1.2.11-arm-linux-gnueabihf.abi rename to internal-complibs/zlib-ng-2.0.7/test/abi/zlib-v1.2.11-arm-linux-gnueabihf.abi diff --git a/internal-complibs/zlib-ng-2.0.6/test/abi/zlib-v1.2.11-x86_64-linux-gnu.abi b/internal-complibs/zlib-ng-2.0.7/test/abi/zlib-v1.2.11-x86_64-linux-gnu.abi similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/abi/zlib-v1.2.11-x86_64-linux-gnu.abi rename to internal-complibs/zlib-ng-2.0.7/test/abi/zlib-v1.2.11-x86_64-linux-gnu.abi diff --git a/internal-complibs/zlib-ng-2.0.6/test/abicheck.md b/internal-complibs/zlib-ng-2.0.7/test/abicheck.md similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/abicheck.md rename to internal-complibs/zlib-ng-2.0.7/test/abicheck.md diff --git a/internal-complibs/zlib-ng-2.0.6/test/abicheck.sh b/internal-complibs/zlib-ng-2.0.7/test/abicheck.sh similarity index 89% rename from internal-complibs/zlib-ng-2.0.6/test/abicheck.sh rename to internal-complibs/zlib-ng-2.0.7/test/abicheck.sh index 89199a59..bc158e6c 100755 --- a/internal-complibs/zlib-ng-2.0.6/test/abicheck.sh +++ b/internal-complibs/zlib-ng-2.0.7/test/abicheck.sh @@ -49,7 +49,7 @@ do --refresh) refresh=true ;; - --refresh_if) + --refresh-if) refresh_if=true ;; --help) @@ -71,11 +71,9 @@ then ABI_GIT_REPO=https://github.com/madler/zlib.git ABI_GIT_COMMIT=v1.2.11 else - # Reference should be the tag for zlib-ng 2.0 - # but until that bright, shining day, use some - # random recent SHA. Annoyingly, can't shorten it. + # Reference is zlib-ng 2.0.0 ABI_GIT_REPO=https://github.com/zlib-ng/zlib-ng.git - ABI_GIT_COMMIT=56ce27343bf295ae9457f8e3d38ec96d2f949a1c + ABI_GIT_COMMIT=2.0.0 fi # FIXME: even when using a tag, check the hash. @@ -94,7 +92,11 @@ then fi # Canonicalize CHOST to work around bug in original zlib's configure -export CHOST=$(sh $TESTDIR/../tools/config.sub $CHOST) +# (Don't export it if it wasn't already exported, else may cause +# default compiler detection failure and shared library link error +# when building both zlib and zlib-ng. +# See https://github.com/zlib-ng/zlib-ng/issues/1219) +CHOST=$(sh $TESTDIR/../tools/config.sub $CHOST) if test "$CHOST" = "" then @@ -121,7 +123,7 @@ then git reset --hard FETCH_HEAD cd .. # Build unstripped, uninstalled, very debug shared library - CFLAGS="$CFLAGS -ggdb" sh src.d/configure $CONFIGURE_ARGS + CFLAGS="$CFLAGS -ggdb" src.d/configure $CONFIGURE_ARGS make -j2 cd .. # Find shared library, extract its abi @@ -134,12 +136,10 @@ then # caching abi files in git (but that would slow builds down). fi -if test -f "$ABIFILE" +if ! test -f "$ABIFILE" then - ABIFILE="$ABIFILE" -else - echo "abicheck: SKIP: $ABIFILE not found; rerun with --refresh or --refresh_if" - exit 0 + echo "abicheck: SKIP: $ABIFILE not found; rerun with --refresh or --refresh-if" + exit 1 fi # Build unstripped, uninstalled, very debug shared library diff --git a/internal-complibs/zlib-ng-2.0.6/test/adler32_test.c b/internal-complibs/zlib-ng-2.0.7/test/adler32_test.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/adler32_test.c rename to internal-complibs/zlib-ng-2.0.7/test/adler32_test.c diff --git a/internal-complibs/zlib-ng-2.0.6/test/crc32_test.c b/internal-complibs/zlib-ng-2.0.7/test/crc32_test.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/crc32_test.c rename to internal-complibs/zlib-ng-2.0.7/test/crc32_test.c diff --git a/internal-complibs/zlib-ng-2.0.6/test/data/fireworks.jpg b/internal-complibs/zlib-ng-2.0.7/test/data/fireworks.jpg similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/data/fireworks.jpg rename to internal-complibs/zlib-ng-2.0.7/test/data/fireworks.jpg diff --git a/internal-complibs/zlib-ng-2.0.6/test/data/lcet10.txt b/internal-complibs/zlib-ng-2.0.7/test/data/lcet10.txt similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/data/lcet10.txt rename to internal-complibs/zlib-ng-2.0.7/test/data/lcet10.txt diff --git a/internal-complibs/zlib-ng-2.0.6/test/data/paper-100k.pdf b/internal-complibs/zlib-ng-2.0.7/test/data/paper-100k.pdf similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/data/paper-100k.pdf rename to internal-complibs/zlib-ng-2.0.7/test/data/paper-100k.pdf diff --git a/internal-complibs/zlib-ng-2.0.6/test/deflate_quick_bi_valid.c b/internal-complibs/zlib-ng-2.0.7/test/deflate_quick_bi_valid.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/deflate_quick_bi_valid.c rename to internal-complibs/zlib-ng-2.0.7/test/deflate_quick_bi_valid.c diff --git a/internal-complibs/zlib-ng-2.0.6/test/deflate_quick_block_open.c b/internal-complibs/zlib-ng-2.0.7/test/deflate_quick_block_open.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/deflate_quick_block_open.c rename to internal-complibs/zlib-ng-2.0.7/test/deflate_quick_block_open.c diff --git a/internal-complibs/zlib-ng-2.0.6/test/example.c b/internal-complibs/zlib-ng-2.0.7/test/example.c similarity index 99% rename from internal-complibs/zlib-ng-2.0.6/test/example.c rename to internal-complibs/zlib-ng-2.0.7/test/example.c index c31d1cf5..d870dd10 100644 --- a/internal-complibs/zlib-ng-2.0.6/test/example.c +++ b/internal-complibs/zlib-ng-2.0.7/test/example.c @@ -966,7 +966,7 @@ int main(int argc, char *argv[]) { exit(1); } else if (strcmp(zVersion(), PREFIX2(VERSION)) != 0) { - fprintf(stderr, "warning: different zlib version\n"); + fprintf(stderr, "warning: different zlib version linked: %s\n", zVersion()); } printf("zlib version %s = 0x%04x, compile flags = 0x%lx\n", diff --git a/internal-complibs/zlib-ng-2.0.6/test/fuzz/checksum_fuzzer.c b/internal-complibs/zlib-ng-2.0.7/test/fuzz/checksum_fuzzer.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/fuzz/checksum_fuzzer.c rename to internal-complibs/zlib-ng-2.0.7/test/fuzz/checksum_fuzzer.c diff --git a/internal-complibs/zlib-ng-2.0.6/test/fuzz/compress_fuzzer.c b/internal-complibs/zlib-ng-2.0.7/test/fuzz/compress_fuzzer.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/fuzz/compress_fuzzer.c rename to internal-complibs/zlib-ng-2.0.7/test/fuzz/compress_fuzzer.c diff --git a/internal-complibs/zlib-ng-2.0.6/test/fuzz/example_dict_fuzzer.c b/internal-complibs/zlib-ng-2.0.7/test/fuzz/example_dict_fuzzer.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/fuzz/example_dict_fuzzer.c rename to internal-complibs/zlib-ng-2.0.7/test/fuzz/example_dict_fuzzer.c diff --git a/internal-complibs/zlib-ng-2.0.6/test/fuzz/example_flush_fuzzer.c b/internal-complibs/zlib-ng-2.0.7/test/fuzz/example_flush_fuzzer.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/fuzz/example_flush_fuzzer.c rename to internal-complibs/zlib-ng-2.0.7/test/fuzz/example_flush_fuzzer.c diff --git a/internal-complibs/zlib-ng-2.0.6/test/fuzz/example_large_fuzzer.c b/internal-complibs/zlib-ng-2.0.7/test/fuzz/example_large_fuzzer.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/fuzz/example_large_fuzzer.c rename to internal-complibs/zlib-ng-2.0.7/test/fuzz/example_large_fuzzer.c diff --git a/internal-complibs/zlib-ng-2.0.6/test/fuzz/example_small_fuzzer.c b/internal-complibs/zlib-ng-2.0.7/test/fuzz/example_small_fuzzer.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/fuzz/example_small_fuzzer.c rename to internal-complibs/zlib-ng-2.0.7/test/fuzz/example_small_fuzzer.c diff --git a/internal-complibs/zlib-ng-2.0.6/test/fuzz/minigzip_fuzzer.c b/internal-complibs/zlib-ng-2.0.7/test/fuzz/minigzip_fuzzer.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/fuzz/minigzip_fuzzer.c rename to internal-complibs/zlib-ng-2.0.7/test/fuzz/minigzip_fuzzer.c diff --git a/internal-complibs/zlib-ng-2.0.6/test/fuzz/standalone_fuzz_target_runner.c b/internal-complibs/zlib-ng-2.0.7/test/fuzz/standalone_fuzz_target_runner.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/fuzz/standalone_fuzz_target_runner.c rename to internal-complibs/zlib-ng-2.0.7/test/fuzz/standalone_fuzz_target_runner.c diff --git a/internal-complibs/zlib-ng-2.0.7/test/gh1235.c b/internal-complibs/zlib-ng-2.0.7/test/gh1235.c new file mode 100644 index 00000000..472282d1 --- /dev/null +++ b/internal-complibs/zlib-ng-2.0.7/test/gh1235.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include "zutil.h" + +int main(void) { + unsigned char plain[32]; + unsigned char compressed[130]; + PREFIX3(stream) strm; + int bound; + z_size_t bytes; + + for (int i = 0; i <= 32; i++) { + memset(plain, 6, i); + memset(&strm, 0, sizeof(strm)); + PREFIX(deflateInit2)(&strm, 0, 8, 31, 1, Z_DEFAULT_STRATEGY); + bound = PREFIX(deflateBound)(&strm, i); + strm.next_in = plain; + strm.next_out = compressed; + strm.avail_in = i; + strm.avail_out = sizeof(compressed); + if (PREFIX(deflate)(&strm, Z_FINISH) != Z_STREAM_END) return -1; + if (strm.avail_in != 0) return -1; + printf("bytes = %2i, deflateBound = %2i, total_out = %2zi\n", i, bound, strm.total_out); + if (bound < strm.total_out) return -1; + if (PREFIX(deflateEnd)(&strm) != Z_OK) return -1; + } + for (int i = 0; i <= 32; i++) { + bytes = sizeof(compressed); + for (int j = 0; j < i; j++) { + plain[j] = j; + } + bound = PREFIX(compressBound)(i); + if (PREFIX(compress2)(compressed, &bytes, plain, i, 1) != Z_OK) return -1; + printf("bytes = %2i, compressBound = %2i, total_out = %2zi\n", i, bound, (size_t)bytes); + if (bytes > bound) return -1; + } + return 0; +} diff --git a/internal-complibs/zlib-ng-2.0.6/test/hash_head_0.c b/internal-complibs/zlib-ng-2.0.7/test/hash_head_0.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/hash_head_0.c rename to internal-complibs/zlib-ng-2.0.7/test/hash_head_0.c diff --git a/internal-complibs/zlib-ng-2.0.6/test/infcover.c b/internal-complibs/zlib-ng-2.0.7/test/infcover.c similarity index 99% rename from internal-complibs/zlib-ng-2.0.6/test/infcover.c rename to internal-complibs/zlib-ng-2.0.7/test/infcover.c index 3446289e..cc597f36 100644 --- a/internal-complibs/zlib-ng-2.0.6/test/infcover.c +++ b/internal-complibs/zlib-ng-2.0.7/test/infcover.c @@ -670,6 +670,10 @@ static void cover_fast(void) { Z_STREAM_END); } +static void cover_cve_2022_37434(void) { + inf("1f 8b 08 04 61 62 63 64 61 62 52 51 1f 8b 08 04 61 62 63 64 61 62 52 51 1f 8b 08 04 61 62 63 64 61 62 52 51 1f 8b 08 04 61 62 63 64 61 62 52 51", "wtf", 13, 47, 12, Z_OK); +} + int main(void) { fprintf(stderr, "%s\n", zVersion()); cover_support(); @@ -678,5 +682,6 @@ int main(void) { cover_inflate(); cover_trees(); cover_fast(); + cover_cve_2022_37434(); return 0; } diff --git a/internal-complibs/zlib-ng-2.0.6/test/inflate_adler32.c b/internal-complibs/zlib-ng-2.0.7/test/inflate_adler32.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/inflate_adler32.c rename to internal-complibs/zlib-ng-2.0.7/test/inflate_adler32.c diff --git a/internal-complibs/zlib-ng-2.0.6/test/minideflate.c b/internal-complibs/zlib-ng-2.0.7/test/minideflate.c similarity index 80% rename from internal-complibs/zlib-ng-2.0.6/test/minideflate.c rename to internal-complibs/zlib-ng-2.0.7/test/minideflate.c index ae04a29e..be2110a9 100644 --- a/internal-complibs/zlib-ng-2.0.6/test/minideflate.c +++ b/internal-complibs/zlib-ng-2.0.7/test/minideflate.c @@ -15,26 +15,21 @@ #include #include "zbuild.h" -#ifdef ZLIB_COMPAT -# include "zlib.h" -#else -# include "zlib-ng.h" -#endif +#include "zutil.h" #if defined(_WIN32) || defined(__CYGWIN__) # include # include +# include # define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) +# ifdef _MSC_VER +# define strcasecmp _stricmp +# endif #else +# include # define SET_BINARY_MODE(file) #endif -#if MAX_MEM_LEVEL >= 8 -# define DEF_MEM_LEVEL 8 -#else -# define DEF_MEM_LEVEL MAX_MEM_LEVEL -#endif - #define CHECK_ERR(err, msg) { \ if (err != Z_OK) { \ fprintf(stderr, "%s error: %d\n", msg, err); \ @@ -42,6 +37,9 @@ } \ } +/* Default read/write i/o buffer size based on GZBUFSIZE */ +#define BUFSIZE 131072 + /* =========================================================================== * deflate() using specialized parameters */ @@ -175,7 +173,7 @@ void inflate_params(FILE *fin, FILE *fout, int32_t read_buf_size, int32_t write_ do { err = PREFIX(inflate)(&d_stream, flush); if (err == Z_STREAM_END) break; - CHECK_ERR(err, "deflate"); + CHECK_ERR(err, "inflate"); if (d_stream.next_out == write_buf + write_buf_size) { fwrite(write_buf, 1, write_buf_size, fout); @@ -214,15 +212,19 @@ void inflate_params(FILE *fin, FILE *fout, int32_t read_buf_size, int32_t write_ } void show_help(void) { - printf("Usage: minideflate [-c] [-f|-h|-R|-F] [-m level] [-r/-t size] [-s flush] [-w bits] [-0 to -9] [input file]\n\n" \ + printf("Usage: minideflate [-c][-d][-k] [-f|-h|-R|-F] [-m level] [-r/-t size] [-s flush] [-w bits] [-0 to -9] [input file]\n\n" \ " -c : write to standard output\n" \ " -d : decompress\n" \ + " -k : keep input file\n" \ " -f : compress with Z_FILTERED\n" \ " -h : compress with Z_HUFFMAN_ONLY\n" \ " -R : compress with Z_RLE\n" \ " -F : compress with Z_FIXED\n" \ " -m : memory level (1 to 8)\n" \ - " -w : window bits (8 to 15 for gzip, -8 to -15 for zlib)\n" \ + " -w : window bits..\n" \ + " : -1 to -15 for raw deflate\n" + " : 0 to 15 for deflate (adler32)\n" + " : 16 to 31 for gzip (crc32)\n" " -s : flush type (0 to 5)\n" \ " -r : read buffer size\n" \ " -t : write buffer size\n" \ @@ -232,18 +234,24 @@ void show_help(void) { int main(int argc, char **argv) { int32_t i; int32_t mem_level = DEF_MEM_LEVEL; - int32_t window_bits = MAX_WBITS; + int32_t window_bits = INT32_MAX; int32_t strategy = Z_DEFAULT_STRATEGY; int32_t level = Z_DEFAULT_COMPRESSION; - int32_t read_buf_size = 4096; - int32_t write_buf_size = 4096; + int32_t read_buf_size = BUFSIZE; + int32_t write_buf_size = BUFSIZE; int32_t flush = Z_NO_FLUSH; uint8_t copyout = 0; uint8_t uncompr = 0; - char out_file[320]; + uint8_t keep = 0; FILE *fin = stdin; FILE *fout = stdout; + + if (argc == 1) { + show_help(); + return 64; /* EX_USAGE */ + } + for (i = 1; i < argc; i++) { if ((strcmp(argv[i], "-m") == 0) && (i + 1 < argc)) mem_level = atoi(argv[++i]); @@ -259,8 +267,12 @@ int main(int argc, char **argv) { copyout = 1; else if (strcmp(argv[i], "-d") == 0) uncompr = 1; + else if (strcmp(argv[i], "-k") == 0) + keep = 1; else if (strcmp(argv[i], "-f") == 0) strategy = Z_FILTERED; + else if (strcmp(argv[i], "-F") == 0) + strategy = Z_FIXED; else if (strcmp(argv[i], "-h") == 0) strategy = Z_HUFFMAN_ONLY; else if (strcmp(argv[i], "-R") == 0) @@ -279,6 +291,7 @@ int main(int argc, char **argv) { SET_BINARY_MODE(stdin); SET_BINARY_MODE(stdout); + if (i != argc) { fin = fopen(argv[i], "rb+"); if (fin == NULL) { @@ -286,14 +299,45 @@ int main(int argc, char **argv) { exit(1); } if (!copyout) { - snprintf(out_file, sizeof(out_file), "%s%s", argv[i], (window_bits < 0) ? ".zz" : ".gz"); + char *out_file = (char *)calloc(1, strlen(argv[i]) + 6); + if (out_file == NULL) { + fprintf(stderr, "Not enough memory\n"); + exit(1); + } + strcat(out_file, argv[i]); + if (!uncompr) { + if (window_bits < 0) { + strcat(out_file, ".zraw"); + } else if (window_bits > MAX_WBITS) { + strcat(out_file, ".gz"); + } else { + strcat(out_file, ".z"); + } + } else { + char *out_ext = strrchr(out_file, '.'); + if (out_ext != NULL) { + if (strcasecmp(out_ext, ".zraw") == 0 && window_bits == INT32_MAX) { + fprintf(stderr, "Must specify window bits for raw deflate stream\n"); + exit(1); + } + *out_ext = 0; + } + } fout = fopen(out_file, "wb"); if (fout == NULL) { fprintf(stderr, "Failed to open file: %s\n", out_file); exit(1); } + free(out_file); } } + + if (window_bits == INT32_MAX) { + window_bits = MAX_WBITS; + /* Auto-detect wrapper for inflateInit */ + if (uncompr) + window_bits += 32; + } if (uncompr) { inflate_params(fin, fout, read_buf_size, write_buf_size, window_bits, flush); @@ -303,6 +347,9 @@ int main(int argc, char **argv) { if (fin != stdin) { fclose(fin); + if (!copyout && !keep) { + unlink(argv[i]); + } } if (fout != stdout) { fclose(fout); diff --git a/internal-complibs/zlib-ng-2.0.6/test/minigzip.c b/internal-complibs/zlib-ng-2.0.7/test/minigzip.c similarity index 95% rename from internal-complibs/zlib-ng-2.0.6/test/minigzip.c rename to internal-complibs/zlib-ng-2.0.7/test/minigzip.c index 29729f38..d1b9e689 100644 --- a/internal-complibs/zlib-ng-2.0.6/test/minigzip.c +++ b/internal-complibs/zlib-ng-2.0.7/test/minigzip.c @@ -64,6 +64,7 @@ extern int unlink (const char *); static char *prog; void error (const char *msg); +void gz_fatal (gzFile file); void gz_compress (FILE *in, gzFile out); #ifdef USE_MMAP int gz_compress_mmap (FILE *in, gzFile out); @@ -81,6 +82,17 @@ void error(const char *msg) { exit(1); } +/* =========================================================================== + * Display last error message of gzFile, close it and exit + */ + +void gz_fatal(gzFile file) { + int err; + fprintf(stderr, "%s: %s\n", prog, PREFIX(gzerror)(file, &err)); + PREFIX(gzclose)(file); + exit(1); +} + /* =========================================================================== * Compress input to output then close both files. */ @@ -88,7 +100,6 @@ void error(const char *msg) { void gz_compress(FILE *in, gzFile out) { char *buf; int len; - int err; #ifdef USE_MMAP /* Try first compressing with mmap. If mmap fails (minigzip used in a @@ -111,7 +122,7 @@ void gz_compress(FILE *in, gzFile out) { } if (len == 0) break; - if (PREFIX(gzwrite)(out, buf, (unsigned)len) != len) error(PREFIX(gzerror)(out, &err)); + if (PREFIX(gzwrite)(out, buf, (unsigned)len) != len) gz_fatal(out); } free(buf); fclose(in); @@ -125,7 +136,6 @@ void gz_compress(FILE *in, gzFile out) { */ int gz_compress_mmap(FILE *in, gzFile out) { int len; - int err; int ifd = fileno(in); char *buf; /* mmap'ed buffer for the entire input file */ off_t buf_len; /* length of the input file */ @@ -143,7 +153,7 @@ int gz_compress_mmap(FILE *in, gzFile out) { /* Compress the whole file at once: */ len = PREFIX(gzwrite)(out, buf, (unsigned)buf_len); - if (len != (int)buf_len) error(PREFIX(gzerror)(out, &err)); + if (len != (int)buf_len) gz_fatal(out); munmap(buf, buf_len); fclose(in); @@ -158,7 +168,6 @@ int gz_compress_mmap(FILE *in, gzFile out) { void gz_uncompress(gzFile in, FILE *out) { char *buf = (char *)malloc(BUFLENW); int len; - int err; if (buf == NULL) error("out of memory"); @@ -166,7 +175,7 @@ void gz_uncompress(gzFile in, FILE *out) { len = PREFIX(gzread)(in, buf, BUFLENW); if (len < 0) { free(buf); - error(PREFIX(gzerror)(in, &err)); + gz_fatal(in); } if (len == 0) break; diff --git a/internal-complibs/zlib-ng-2.0.6/test/pigz/CMakeLists.txt b/internal-complibs/zlib-ng-2.0.7/test/pigz/CMakeLists.txt similarity index 95% rename from internal-complibs/zlib-ng-2.0.6/test/pigz/CMakeLists.txt rename to internal-complibs/zlib-ng-2.0.7/test/pigz/CMakeLists.txt index 43082cf2..0d5bc864 100644 --- a/internal-complibs/zlib-ng-2.0.6/test/pigz/CMakeLists.txt +++ b/internal-complibs/zlib-ng-2.0.7/test/pigz/CMakeLists.txt @@ -13,6 +13,7 @@ # WITH_CODE_COVERAGE - Enable code coverage reporting # WITH_THREADS - Enable threading support # PIGZ_ENABLE_TESTS - Enable adding unit tests +# PIGZ_VERSION - Set the version of pigz to build # ZLIB_ROOT - Path to the zlib source directory # PTHREADS4W_ROOT - Path to pthreads4w source directory on Windows. # If not specified then threading will be disabled. @@ -28,6 +29,7 @@ include(../../cmake/detect-coverage.cmake) option(WITH_CODE_COVERAGE "Enable code coverage reporting" OFF) option(WITH_THREADS "Enable threading support" ON) option(PIGZ_ENABLE_TESTS "Build unit tests" ON) +option(PIGZ_VERSION "Set the version of pigz to build" "") project(pigz LANGUAGES C) @@ -54,8 +56,14 @@ elseif(WIN32) endif() # Fetch pigz source code from official repository +if(PIGZ_VERSION STREQUAL "") + set(PIGZ_TAG master) +else() + set(PIGZ_TAG ${PIGZ_VERSION}) +endif() FetchContent_Declare(pigz - GIT_REPOSITORY https://github.com/madler/pigz.git) + GIT_REPOSITORY https://github.com/madler/pigz.git + GIT_TAG ${PIGZ_TAG}) FetchContent_MakeAvailable(pigz) FetchContent_GetProperties(pigz) diff --git a/internal-complibs/zlib-ng-2.0.6/test/pkgcheck.sh b/internal-complibs/zlib-ng-2.0.7/test/pkgcheck.sh similarity index 99% rename from internal-complibs/zlib-ng-2.0.6/test/pkgcheck.sh rename to internal-complibs/zlib-ng-2.0.7/test/pkgcheck.sh index 4c757dff..832df8db 100644 --- a/internal-complibs/zlib-ng-2.0.6/test/pkgcheck.sh +++ b/internal-complibs/zlib-ng-2.0.7/test/pkgcheck.sh @@ -125,7 +125,7 @@ cd btmp1 ;; esac ../configure $CONFIGURE_ARGS - make + make -j2 make install cd .. diff --git a/internal-complibs/zlib-ng-2.0.6/test/switchlevels.c b/internal-complibs/zlib-ng-2.0.7/test/switchlevels.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/switchlevels.c rename to internal-complibs/zlib-ng-2.0.7/test/switchlevels.c diff --git a/internal-complibs/zlib-ng-2.0.6/test/testCVEinputs.sh b/internal-complibs/zlib-ng-2.0.7/test/testCVEinputs.sh similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/test/testCVEinputs.sh rename to internal-complibs/zlib-ng-2.0.7/test/testCVEinputs.sh diff --git a/internal-complibs/zlib-ng-2.0.6/tools/codecov-upload.sh b/internal-complibs/zlib-ng-2.0.7/tools/codecov-upload.sh similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/tools/codecov-upload.sh rename to internal-complibs/zlib-ng-2.0.7/tools/codecov-upload.sh diff --git a/internal-complibs/zlib-ng-2.0.6/tools/config.sub b/internal-complibs/zlib-ng-2.0.7/tools/config.sub similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/tools/config.sub rename to internal-complibs/zlib-ng-2.0.7/tools/config.sub diff --git a/internal-complibs/zlib-ng-2.0.6/tools/makecrct.c b/internal-complibs/zlib-ng-2.0.7/tools/makecrct.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/tools/makecrct.c rename to internal-complibs/zlib-ng-2.0.7/tools/makecrct.c diff --git a/internal-complibs/zlib-ng-2.0.6/tools/makefixed.c b/internal-complibs/zlib-ng-2.0.7/tools/makefixed.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/tools/makefixed.c rename to internal-complibs/zlib-ng-2.0.7/tools/makefixed.c diff --git a/internal-complibs/zlib-ng-2.0.6/tools/maketrees.c b/internal-complibs/zlib-ng-2.0.7/tools/maketrees.c similarity index 99% rename from internal-complibs/zlib-ng-2.0.6/tools/maketrees.c rename to internal-complibs/zlib-ng-2.0.7/tools/maketrees.c index 337f2fc0..01e7b4d8 100644 --- a/internal-complibs/zlib-ng-2.0.6/tools/maketrees.c +++ b/internal-complibs/zlib-ng-2.0.7/tools/maketrees.c @@ -98,7 +98,7 @@ static void tr_static_init(void) { ((i) == (last)? "\n};\n\n" : \ ((i) % (width) == (width)-1 ? ",\n" : ", ")) -static void gen_trees_header() { +static void gen_trees_header(void) { int i; printf("#ifndef TREES_TBL_H_\n"); diff --git a/internal-complibs/zlib-ng-2.0.6/trees.c b/internal-complibs/zlib-ng-2.0.7/trees.c similarity index 99% rename from internal-complibs/zlib-ng-2.0.6/trees.c rename to internal-complibs/zlib-ng-2.0.7/trees.c index c3d48495..99657887 100644 --- a/internal-complibs/zlib-ng-2.0.6/trees.c +++ b/internal-complibs/zlib-ng-2.0.7/trees.c @@ -670,7 +670,7 @@ void Z_INTERNAL zng_tr_flush_block(deflate_state *s, char *buf, uint32_t stored_ opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, s->sym_next / 3)); - if (static_lenb <= opt_lenb) + if (static_lenb <= opt_lenb || s->strategy == Z_FIXED) opt_lenb = static_lenb; } else { @@ -688,7 +688,7 @@ void Z_INTERNAL zng_tr_flush_block(deflate_state *s, char *buf, uint32_t stored_ */ zng_tr_stored_block(s, buf, stored_len, last); - } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { + } else if (static_lenb == opt_lenb) { zng_tr_emit_tree(s, STATIC_TREES, last); compress_block(s, (const ct_data *)static_ltree, (const ct_data *)static_dtree); cmpr_bits_add(s, s->static_len); diff --git a/internal-complibs/zlib-ng-2.0.6/trees.h b/internal-complibs/zlib-ng-2.0.7/trees.h similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/trees.h rename to internal-complibs/zlib-ng-2.0.7/trees.h diff --git a/internal-complibs/zlib-ng-2.0.6/trees_emit.h b/internal-complibs/zlib-ng-2.0.7/trees_emit.h similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/trees_emit.h rename to internal-complibs/zlib-ng-2.0.7/trees_emit.h diff --git a/internal-complibs/zlib-ng-2.0.6/trees_tbl.h b/internal-complibs/zlib-ng-2.0.7/trees_tbl._h similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/trees_tbl.h rename to internal-complibs/zlib-ng-2.0.7/trees_tbl._h diff --git a/internal-complibs/zlib-ng-2.0.7/trees_tbl.h b/internal-complibs/zlib-ng-2.0.7/trees_tbl.h new file mode 100644 index 00000000..a4c68a56 --- /dev/null +++ b/internal-complibs/zlib-ng-2.0.7/trees_tbl.h @@ -0,0 +1,132 @@ +#ifndef TREES_TBL_H_ +#define TREES_TBL_H_ + +/* header created automatically with maketrees.c */ + +Z_INTERNAL const ct_data static_ltree[L_CODES+2] = { +{{ 12},{8}}, {{140},{8}}, {{ 76},{8}}, {{204},{8}}, {{ 44},{8}}, +{{172},{8}}, {{108},{8}}, {{236},{8}}, {{ 28},{8}}, {{156},{8}}, +{{ 92},{8}}, {{220},{8}}, {{ 60},{8}}, {{188},{8}}, {{124},{8}}, +{{252},{8}}, {{ 2},{8}}, {{130},{8}}, {{ 66},{8}}, {{194},{8}}, +{{ 34},{8}}, {{162},{8}}, {{ 98},{8}}, {{226},{8}}, {{ 18},{8}}, +{{146},{8}}, {{ 82},{8}}, {{210},{8}}, {{ 50},{8}}, {{178},{8}}, +{{114},{8}}, {{242},{8}}, {{ 10},{8}}, {{138},{8}}, {{ 74},{8}}, +{{202},{8}}, {{ 42},{8}}, {{170},{8}}, {{106},{8}}, {{234},{8}}, +{{ 26},{8}}, {{154},{8}}, {{ 90},{8}}, {{218},{8}}, {{ 58},{8}}, +{{186},{8}}, {{122},{8}}, {{250},{8}}, {{ 6},{8}}, {{134},{8}}, +{{ 70},{8}}, {{198},{8}}, {{ 38},{8}}, {{166},{8}}, {{102},{8}}, +{{230},{8}}, {{ 22},{8}}, {{150},{8}}, {{ 86},{8}}, {{214},{8}}, +{{ 54},{8}}, {{182},{8}}, {{118},{8}}, {{246},{8}}, {{ 14},{8}}, +{{142},{8}}, {{ 78},{8}}, {{206},{8}}, {{ 46},{8}}, {{174},{8}}, +{{110},{8}}, {{238},{8}}, {{ 30},{8}}, {{158},{8}}, {{ 94},{8}}, +{{222},{8}}, {{ 62},{8}}, {{190},{8}}, {{126},{8}}, {{254},{8}}, +{{ 1},{8}}, {{129},{8}}, {{ 65},{8}}, {{193},{8}}, {{ 33},{8}}, +{{161},{8}}, {{ 97},{8}}, {{225},{8}}, {{ 17},{8}}, {{145},{8}}, +{{ 81},{8}}, {{209},{8}}, {{ 49},{8}}, {{177},{8}}, {{113},{8}}, +{{241},{8}}, {{ 9},{8}}, {{137},{8}}, {{ 73},{8}}, {{201},{8}}, +{{ 41},{8}}, {{169},{8}}, {{105},{8}}, {{233},{8}}, {{ 25},{8}}, +{{153},{8}}, {{ 89},{8}}, {{217},{8}}, {{ 57},{8}}, {{185},{8}}, +{{121},{8}}, {{249},{8}}, {{ 5},{8}}, {{133},{8}}, {{ 69},{8}}, +{{197},{8}}, {{ 37},{8}}, {{165},{8}}, {{101},{8}}, {{229},{8}}, +{{ 21},{8}}, {{149},{8}}, {{ 85},{8}}, {{213},{8}}, {{ 53},{8}}, +{{181},{8}}, {{117},{8}}, {{245},{8}}, {{ 13},{8}}, {{141},{8}}, +{{ 77},{8}}, {{205},{8}}, {{ 45},{8}}, {{173},{8}}, {{109},{8}}, +{{237},{8}}, {{ 29},{8}}, {{157},{8}}, {{ 93},{8}}, {{221},{8}}, +{{ 61},{8}}, {{189},{8}}, {{125},{8}}, {{253},{8}}, {{ 19},{9}}, +{{275},{9}}, {{147},{9}}, {{403},{9}}, {{ 83},{9}}, {{339},{9}}, +{{211},{9}}, {{467},{9}}, {{ 51},{9}}, {{307},{9}}, {{179},{9}}, +{{435},{9}}, {{115},{9}}, {{371},{9}}, {{243},{9}}, {{499},{9}}, +{{ 11},{9}}, {{267},{9}}, {{139},{9}}, {{395},{9}}, {{ 75},{9}}, +{{331},{9}}, {{203},{9}}, {{459},{9}}, {{ 43},{9}}, {{299},{9}}, +{{171},{9}}, {{427},{9}}, {{107},{9}}, {{363},{9}}, {{235},{9}}, +{{491},{9}}, {{ 27},{9}}, {{283},{9}}, {{155},{9}}, {{411},{9}}, +{{ 91},{9}}, {{347},{9}}, {{219},{9}}, {{475},{9}}, {{ 59},{9}}, +{{315},{9}}, {{187},{9}}, {{443},{9}}, {{123},{9}}, {{379},{9}}, +{{251},{9}}, {{507},{9}}, {{ 7},{9}}, {{263},{9}}, {{135},{9}}, +{{391},{9}}, {{ 71},{9}}, {{327},{9}}, {{199},{9}}, {{455},{9}}, +{{ 39},{9}}, {{295},{9}}, {{167},{9}}, {{423},{9}}, {{103},{9}}, +{{359},{9}}, {{231},{9}}, {{487},{9}}, {{ 23},{9}}, {{279},{9}}, +{{151},{9}}, {{407},{9}}, {{ 87},{9}}, {{343},{9}}, {{215},{9}}, +{{471},{9}}, {{ 55},{9}}, {{311},{9}}, {{183},{9}}, {{439},{9}}, +{{119},{9}}, {{375},{9}}, {{247},{9}}, {{503},{9}}, {{ 15},{9}}, +{{271},{9}}, {{143},{9}}, {{399},{9}}, {{ 79},{9}}, {{335},{9}}, +{{207},{9}}, {{463},{9}}, {{ 47},{9}}, {{303},{9}}, {{175},{9}}, +{{431},{9}}, {{111},{9}}, {{367},{9}}, {{239},{9}}, {{495},{9}}, +{{ 31},{9}}, {{287},{9}}, {{159},{9}}, {{415},{9}}, {{ 95},{9}}, +{{351},{9}}, {{223},{9}}, {{479},{9}}, {{ 63},{9}}, {{319},{9}}, +{{191},{9}}, {{447},{9}}, {{127},{9}}, {{383},{9}}, {{255},{9}}, +{{511},{9}}, {{ 0},{7}}, {{ 64},{7}}, {{ 32},{7}}, {{ 96},{7}}, +{{ 16},{7}}, {{ 80},{7}}, {{ 48},{7}}, {{112},{7}}, {{ 8},{7}}, +{{ 72},{7}}, {{ 40},{7}}, {{104},{7}}, {{ 24},{7}}, {{ 88},{7}}, +{{ 56},{7}}, {{120},{7}}, {{ 4},{7}}, {{ 68},{7}}, {{ 36},{7}}, +{{100},{7}}, {{ 20},{7}}, {{ 84},{7}}, {{ 52},{7}}, {{116},{7}}, +{{ 3},{8}}, {{131},{8}}, {{ 67},{8}}, {{195},{8}}, {{ 35},{8}}, +{{163},{8}}, {{ 99},{8}}, {{227},{8}} +}; + +Z_INTERNAL const ct_data static_dtree[D_CODES] = { +{{ 0},{5}}, {{16},{5}}, {{ 8},{5}}, {{24},{5}}, {{ 4},{5}}, +{{20},{5}}, {{12},{5}}, {{28},{5}}, {{ 2},{5}}, {{18},{5}}, +{{10},{5}}, {{26},{5}}, {{ 6},{5}}, {{22},{5}}, {{14},{5}}, +{{30},{5}}, {{ 1},{5}}, {{17},{5}}, {{ 9},{5}}, {{25},{5}}, +{{ 5},{5}}, {{21},{5}}, {{13},{5}}, {{29},{5}}, {{ 3},{5}}, +{{19},{5}}, {{11},{5}}, {{27},{5}}, {{ 7},{5}}, {{23},{5}} +}; + +const unsigned char Z_INTERNAL zng_dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const unsigned char Z_INTERNAL zng_length_code[MAX_MATCH-MIN_MATCH+1] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +Z_INTERNAL const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +Z_INTERNAL const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; + +#endif /* TREES_TBL_H_ */ diff --git a/internal-complibs/zlib-ng-2.0.6/uncompr.c b/internal-complibs/zlib-ng-2.0.7/uncompr.c similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/uncompr.c rename to internal-complibs/zlib-ng-2.0.7/uncompr.c diff --git a/internal-complibs/zlib-ng-2.0.6/win32/DLL_FAQ.txt b/internal-complibs/zlib-ng-2.0.7/win32/DLL_FAQ.txt similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/win32/DLL_FAQ.txt rename to internal-complibs/zlib-ng-2.0.7/win32/DLL_FAQ.txt diff --git a/internal-complibs/zlib-ng-2.0.6/win32/Makefile.a64 b/internal-complibs/zlib-ng-2.0.7/win32/Makefile.a64 similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/win32/Makefile.a64 rename to internal-complibs/zlib-ng-2.0.7/win32/Makefile.a64 diff --git a/internal-complibs/zlib-ng-2.0.6/win32/Makefile.arm b/internal-complibs/zlib-ng-2.0.7/win32/Makefile.arm similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/win32/Makefile.arm rename to internal-complibs/zlib-ng-2.0.7/win32/Makefile.arm diff --git a/internal-complibs/zlib-ng-2.0.6/win32/Makefile.msc b/internal-complibs/zlib-ng-2.0.7/win32/Makefile.msc similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/win32/Makefile.msc rename to internal-complibs/zlib-ng-2.0.7/win32/Makefile.msc diff --git a/internal-complibs/zlib-ng-2.0.6/win32/README-WIN32.txt b/internal-complibs/zlib-ng-2.0.7/win32/README-WIN32.txt similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/win32/README-WIN32.txt rename to internal-complibs/zlib-ng-2.0.7/win32/README-WIN32.txt diff --git a/internal-complibs/zlib-ng-2.0.6/win32/zlib-ng.def b/internal-complibs/zlib-ng-2.0.7/win32/zlib-ng.def similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/win32/zlib-ng.def rename to internal-complibs/zlib-ng-2.0.7/win32/zlib-ng.def diff --git a/internal-complibs/zlib-ng-2.0.6/win32/zlib-ng1.rc b/internal-complibs/zlib-ng-2.0.7/win32/zlib-ng1.rc similarity index 91% rename from internal-complibs/zlib-ng-2.0.6/win32/zlib-ng1.rc rename to internal-complibs/zlib-ng-2.0.7/win32/zlib-ng1.rc index b5390690..128b56d2 100644 --- a/internal-complibs/zlib-ng-2.0.6/win32/zlib-ng1.rc +++ b/internal-complibs/zlib-ng-2.0.7/win32/zlib-ng1.rc @@ -1,11 +1,7 @@ #include #include "../zlib-ng.h" -#ifdef GCC_WINDRES VS_VERSION_INFO VERSIONINFO -#else -VS_VERSION_INFO VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE -#endif FILEVERSION ZLIBNG_VER_MAJOR,ZLIBNG_VER_MINOR,ZLIBNG_VER_REVISION,0 PRODUCTVERSION ZLIBNG_VER_MAJOR,ZLIBNG_VER_MINOR,ZLIBNG_VER_REVISION,0 FILEFLAGSMASK VS_FFI_FILEFLAGSMASK diff --git a/internal-complibs/zlib-ng-2.0.6/win32/zlib.def b/internal-complibs/zlib-ng-2.0.7/win32/zlib.def similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/win32/zlib.def rename to internal-complibs/zlib-ng-2.0.7/win32/zlib.def diff --git a/internal-complibs/zlib-ng-2.0.6/win32/zlib1.rc b/internal-complibs/zlib-ng-2.0.7/win32/zlib1.rc similarity index 91% rename from internal-complibs/zlib-ng-2.0.6/win32/zlib1.rc rename to internal-complibs/zlib-ng-2.0.7/win32/zlib1.rc index 11be5f49..39bdcc08 100644 --- a/internal-complibs/zlib-ng-2.0.6/win32/zlib1.rc +++ b/internal-complibs/zlib-ng-2.0.7/win32/zlib1.rc @@ -1,11 +1,7 @@ #include #include "../zlib.h" -#ifdef GCC_WINDRES VS_VERSION_INFO VERSIONINFO -#else -VS_VERSION_INFO VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE -#endif FILEVERSION ZLIB_VER_MAJOR,ZLIB_VER_MINOR,ZLIB_VER_REVISION,0 PRODUCTVERSION ZLIB_VER_MAJOR,ZLIB_VER_MINOR,ZLIB_VER_REVISION,0 FILEFLAGSMASK VS_FFI_FILEFLAGSMASK diff --git a/internal-complibs/zlib-ng-2.0.6/win32/zlibcompat.def b/internal-complibs/zlib-ng-2.0.7/win32/zlibcompat.def similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/win32/zlibcompat.def rename to internal-complibs/zlib-ng-2.0.7/win32/zlibcompat.def diff --git a/internal-complibs/zlib-ng-2.0.6/zbuild.h b/internal-complibs/zlib-ng-2.0.7/zbuild.h similarity index 78% rename from internal-complibs/zlib-ng-2.0.6/zbuild.h rename to internal-complibs/zlib-ng-2.0.7/zbuild.h index 3c5e5fb4..17f98101 100644 --- a/internal-complibs/zlib-ng-2.0.6/zbuild.h +++ b/internal-complibs/zlib-ng-2.0.7/zbuild.h @@ -1,6 +1,10 @@ #ifndef _ZBUILD_H #define _ZBUILD_H +#ifndef _ISOC11_SOURCE +# define _ISOC11_SOURCE 1 /* aligned_alloc */ +#endif + /* This has to be first include that defines any types */ #if defined(_MSC_VER) # if defined(_WIN64) @@ -33,4 +37,11 @@ /* Ignore unused variable warning */ #define Z_UNUSED(var) (void)(var) +#if defined(__has_feature) +# if __has_feature(memory_sanitizer) +# define Z_MEMORY_SANITIZER 1 +# include +# endif +#endif + #endif diff --git a/internal-complibs/zlib-ng-2.0.6/zconf-ng.h.in b/internal-complibs/zlib-ng-2.0.7/zconf-ng.h.in similarity index 99% rename from internal-complibs/zlib-ng-2.0.6/zconf-ng.h.in rename to internal-complibs/zlib-ng-2.0.7/zconf-ng.h.in index 7d54668d..2282fc22 100644 --- a/internal-complibs/zlib-ng-2.0.6/zconf-ng.h.in +++ b/internal-complibs/zlib-ng-2.0.7/zconf-ng.h.in @@ -119,7 +119,6 @@ typedef PTRDIFF_TYPE ptrdiff_t; #endif #include /* for off_t */ -#include /* for va_list */ #include /* for wchar_t and NULL */ diff --git a/internal-complibs/zlib-ng-2.0.7/zconf.h b/internal-complibs/zlib-ng-2.0.7/zconf.h new file mode 100644 index 00000000..1c03f294 --- /dev/null +++ b/internal-complibs/zlib-ng-2.0.7/zconf.h @@ -0,0 +1,201 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#ifndef ZCONF_H +#define ZCONF_H + +#if !defined(_WIN32) && defined(__WIN32__) +# define _WIN32 +#endif + +#ifdef __STDC_VERSION__ +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif + +/* Clang macro for detecting declspec support + * https://clang.llvm.org/docs/LanguageExtensions.html#has-declspec-attribute + */ +#ifndef __has_declspec_attribute +# define __has_declspec_attribute(x) 0 +#endif + +#if defined(ZLIB_CONST) && !defined(z_const) +# define z_const const +#else +# define z_const +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# define MAX_MEM_LEVEL 9 +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus about 7 kilobytes + for small objects. +*/ + +/* Type declarations */ + + +#ifndef OF /* function prototypes */ +# define OF(args) args +#endif + +#ifdef ZLIB_INTERNAL +# define Z_INTERNAL ZLIB_INTERNAL +#endif + +/* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +#if defined(ZLIB_DLL) && (defined(_WIN32) || (__has_declspec_attribute(dllexport) && __has_declspec_attribute(dllimport))) +# ifdef Z_INTERNAL +# define Z_EXTERN extern __declspec(dllexport) +# else +# define Z_EXTERN extern __declspec(dllimport) +# endif +#endif + +/* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +#if defined(ZLIB_WINAPI) && defined(_WIN32) +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define Z_EXPORT WINAPI +# define Z_EXPORTVA WINAPIV +#endif + +#ifndef Z_EXTERN +# define Z_EXTERN extern +#endif +#ifndef Z_EXPORT +# define Z_EXPORT +#endif +#ifndef Z_EXPORTVA +# define Z_EXPORTVA +#endif + +/* For backwards compatibility */ + +#ifndef ZEXTERN +# define ZEXTERN Z_EXTERN +#endif +#ifndef ZEXPORT +# define ZEXPORT Z_EXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA Z_EXPORTVA +#endif + +/* Legacy zlib typedefs for backwards compatibility. Don't assume stdint.h is defined. */ +typedef unsigned char Byte; +typedef Byte Bytef; + +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +typedef char charf; +typedef int intf; +typedef uInt uIntf; +typedef uLong uLongf; + +typedef void const *voidpc; +typedef void *voidpf; +typedef void *voidp; + +typedef unsigned int z_crc_t; + +#if 1 /* was set to #if 1 by configure/cmake/etc */ +# define Z_HAVE_UNISTD_H +#endif + +#ifdef NEED_PTRDIFF_T /* may be set to #if 1 by configure/cmake/etc */ +typedef PTRDIFF_TYPE ptrdiff_t; +#endif + +#include /* for off_t */ + +#include /* for wchar_t and NULL */ + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ +# ifndef z_off_t +# define z_off_t off_t +# endif +#endif + +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 +# define Z_LFS64 +#endif + +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) +# define Z_LARGE64 +#endif + +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) +# define Z_WANT64 +#endif + +#if !defined(SEEK_SET) +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if !defined(_WIN32) && defined(Z_LARGE64) +# define z_off64_t off64_t +#else +# if defined(__MSYS__) +# define z_off64_t _off64_t +# elif defined(_WIN32) && !defined(__GNUC__) +# define z_off64_t __int64 +# else +# define z_off64_t z_off_t +# endif +#endif + +#endif /* ZCONF_H */ diff --git a/internal-complibs/zlib-ng-2.0.6/zconf.h.in b/internal-complibs/zlib-ng-2.0.7/zconf.h.in similarity index 96% rename from internal-complibs/zlib-ng-2.0.6/zconf.h.in rename to internal-complibs/zlib-ng-2.0.7/zconf.h.in index ae2a3093..c9058862 100644 --- a/internal-complibs/zlib-ng-2.0.6/zconf.h.in +++ b/internal-complibs/zlib-ng-2.0.7/zconf.h.in @@ -85,6 +85,9 @@ * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. */ #if defined(ZLIB_WINAPI) && defined(_WIN32) +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif # include /* No need for _export, use ZLIB.DEF instead. */ /* For complete Windows compatibility, use WINAPI, not __stdcall. */ @@ -114,7 +117,7 @@ # define ZEXPORTVA Z_EXPORTVA #endif -/* Fallback for something that includes us. */ +/* Legacy zlib typedefs for backwards compatibility. Don't assume stdint.h is defined. */ typedef unsigned char Byte; typedef Byte Bytef; @@ -130,6 +133,8 @@ typedef void const *voidpc; typedef void *voidpf; typedef void *voidp; +typedef unsigned int z_crc_t; + #ifdef HAVE_UNISTD_H /* may be set to #if 1 by configure/cmake/etc */ # define Z_HAVE_UNISTD_H #endif @@ -139,7 +144,6 @@ typedef PTRDIFF_TYPE ptrdiff_t; #endif #include /* for off_t */ -#include /* for va_list */ #include /* for wchar_t and NULL */ diff --git a/internal-complibs/zlib-ng-2.0.7/zconf.h.included b/internal-complibs/zlib-ng-2.0.7/zconf.h.included new file mode 100644 index 00000000..1c03f294 --- /dev/null +++ b/internal-complibs/zlib-ng-2.0.7/zconf.h.included @@ -0,0 +1,201 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#ifndef ZCONF_H +#define ZCONF_H + +#if !defined(_WIN32) && defined(__WIN32__) +# define _WIN32 +#endif + +#ifdef __STDC_VERSION__ +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif + +/* Clang macro for detecting declspec support + * https://clang.llvm.org/docs/LanguageExtensions.html#has-declspec-attribute + */ +#ifndef __has_declspec_attribute +# define __has_declspec_attribute(x) 0 +#endif + +#if defined(ZLIB_CONST) && !defined(z_const) +# define z_const const +#else +# define z_const +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# define MAX_MEM_LEVEL 9 +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus about 7 kilobytes + for small objects. +*/ + +/* Type declarations */ + + +#ifndef OF /* function prototypes */ +# define OF(args) args +#endif + +#ifdef ZLIB_INTERNAL +# define Z_INTERNAL ZLIB_INTERNAL +#endif + +/* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +#if defined(ZLIB_DLL) && (defined(_WIN32) || (__has_declspec_attribute(dllexport) && __has_declspec_attribute(dllimport))) +# ifdef Z_INTERNAL +# define Z_EXTERN extern __declspec(dllexport) +# else +# define Z_EXTERN extern __declspec(dllimport) +# endif +#endif + +/* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +#if defined(ZLIB_WINAPI) && defined(_WIN32) +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define Z_EXPORT WINAPI +# define Z_EXPORTVA WINAPIV +#endif + +#ifndef Z_EXTERN +# define Z_EXTERN extern +#endif +#ifndef Z_EXPORT +# define Z_EXPORT +#endif +#ifndef Z_EXPORTVA +# define Z_EXPORTVA +#endif + +/* For backwards compatibility */ + +#ifndef ZEXTERN +# define ZEXTERN Z_EXTERN +#endif +#ifndef ZEXPORT +# define ZEXPORT Z_EXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA Z_EXPORTVA +#endif + +/* Legacy zlib typedefs for backwards compatibility. Don't assume stdint.h is defined. */ +typedef unsigned char Byte; +typedef Byte Bytef; + +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +typedef char charf; +typedef int intf; +typedef uInt uIntf; +typedef uLong uLongf; + +typedef void const *voidpc; +typedef void *voidpf; +typedef void *voidp; + +typedef unsigned int z_crc_t; + +#if 1 /* was set to #if 1 by configure/cmake/etc */ +# define Z_HAVE_UNISTD_H +#endif + +#ifdef NEED_PTRDIFF_T /* may be set to #if 1 by configure/cmake/etc */ +typedef PTRDIFF_TYPE ptrdiff_t; +#endif + +#include /* for off_t */ + +#include /* for wchar_t and NULL */ + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ +# ifndef z_off_t +# define z_off_t off_t +# endif +#endif + +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 +# define Z_LFS64 +#endif + +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) +# define Z_LARGE64 +#endif + +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) +# define Z_WANT64 +#endif + +#if !defined(SEEK_SET) +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if !defined(_WIN32) && defined(Z_LARGE64) +# define z_off64_t off64_t +#else +# if defined(__MSYS__) +# define z_off64_t _off64_t +# elif defined(_WIN32) && !defined(__GNUC__) +# define z_off64_t __int64 +# else +# define z_off64_t z_off_t +# endif +#endif + +#endif /* ZCONF_H */ diff --git a/internal-complibs/zlib-ng-2.0.6/zendian.h b/internal-complibs/zlib-ng-2.0.7/zendian.h similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/zendian.h rename to internal-complibs/zlib-ng-2.0.7/zendian.h diff --git a/internal-complibs/zlib-ng-2.0.6/zlib-ng.h b/internal-complibs/zlib-ng-2.0.7/zlib-ng.h similarity index 99% rename from internal-complibs/zlib-ng-2.0.6/zlib-ng.h rename to internal-complibs/zlib-ng-2.0.7/zlib-ng.h index 0cd234ac..b5a9fcb4 100644 --- a/internal-complibs/zlib-ng-2.0.6/zlib-ng.h +++ b/internal-complibs/zlib-ng-2.0.7/zlib-ng.h @@ -34,6 +34,8 @@ #endif #include +#include + #include "zconf-ng.h" #ifndef ZCONFNG_H @@ -44,11 +46,11 @@ extern "C" { #endif -#define ZLIBNG_VERSION "2.0.6" -#define ZLIBNG_VERNUM 0x2060 +#define ZLIBNG_VERSION "2.0.7" +#define ZLIBNG_VERNUM 0x2070 #define ZLIBNG_VER_MAJOR 2 #define ZLIBNG_VER_MINOR 0 -#define ZLIBNG_VER_REVISION 6 +#define ZLIBNG_VER_REVISION 7 #define ZLIBNG_VER_SUBREVISION 0 /* diff --git a/internal-complibs/zlib-ng-2.0.6/zlib-ng.map b/internal-complibs/zlib-ng-2.0.7/zlib-ng.map similarity index 99% rename from internal-complibs/zlib-ng-2.0.6/zlib-ng.map rename to internal-complibs/zlib-ng-2.0.7/zlib-ng.map index b0046034..461c2566 100644 --- a/internal-complibs/zlib-ng-2.0.6/zlib-ng.map +++ b/internal-complibs/zlib-ng-2.0.7/zlib-ng.map @@ -66,7 +66,6 @@ ZLIB_NG_2.0.0 { zng_zcfree; zng_z_errmsg; zng_gz_error; - zng_gz_intmax; _*; }; diff --git a/internal-complibs/zlib-ng-2.0.6/zlib.h b/internal-complibs/zlib-ng-2.0.7/zlib.h similarity index 99% rename from internal-complibs/zlib-ng-2.0.6/zlib.h rename to internal-complibs/zlib-ng-2.0.7/zlib.h index a593c47f..4303e38c 100644 --- a/internal-complibs/zlib-ng-2.0.6/zlib.h +++ b/internal-complibs/zlib-ng-2.0.7/zlib.h @@ -36,6 +36,7 @@ #include #include + #include "zconf.h" #ifndef ZCONF_H @@ -46,11 +47,11 @@ extern "C" { #endif -#define ZLIBNG_VERSION "2.0.6" -#define ZLIBNG_VERNUM 0x2060 +#define ZLIBNG_VERSION "2.0.7" +#define ZLIBNG_VERNUM 0x2070 #define ZLIBNG_VER_MAJOR 2 #define ZLIBNG_VER_MINOR 0 -#define ZLIBNG_VER_REVISION 6 +#define ZLIBNG_VER_REVISION 7 #define ZLIBNG_VER_SUBREVISION 0 #define ZLIB_VERSION "1.2.11.zlib-ng" @@ -58,7 +59,7 @@ extern "C" { #define ZLIB_VER_MAJOR 1 #define ZLIB_VER_MINOR 2 #define ZLIB_VER_REVISION 11 -#define ZLIB_VER_SUBREVISION 0 +#define ZLIB_VER_SUBREVISION 15 /* 15=fork (0xf) */ /* The 'zlib' compression library provides in-memory compression and @@ -1799,7 +1800,7 @@ Z_EXTERN int Z_EXPORT gzgetc_(gzFile file); /* backward compatibility */ #endif #endif -#if !defined(Z_INTERNAL) && defined(Z_WANT64) +#if !defined(Z_SOLO) && !defined(Z_INTERNAL) && defined(Z_WANT64) # define gzopen gzopen64 # define gzseek gzseek64 # define gztell gztell64 @@ -1817,10 +1818,12 @@ Z_EXTERN int Z_EXPORT gzgetc_(gzFile file); /* backward compatibility */ Z_EXTERN void Z_EXPORT crc32_combine_gen64(uint32_t *op, z_off64_t); # endif #else +# ifndef Z_SOLO Z_EXTERN gzFile Z_EXPORT gzopen(const char *, const char *); Z_EXTERN z_off_t Z_EXPORT gzseek(gzFile, z_off_t, int); Z_EXTERN z_off_t Z_EXPORT gztell(gzFile); Z_EXTERN z_off_t Z_EXPORT gzoffset(gzFile); +# endif Z_EXTERN unsigned long Z_EXPORT adler32_combine(unsigned long, unsigned long, z_off_t); Z_EXTERN unsigned long Z_EXPORT crc32_combine(unsigned long, unsigned long, z_off_t); Z_EXTERN void Z_EXPORT crc32_combine_gen(uint32_t *op, z_off_t); diff --git a/internal-complibs/zlib-ng-2.0.6/zlib.map b/internal-complibs/zlib-ng-2.0.7/zlib.map similarity index 98% rename from internal-complibs/zlib-ng-2.0.6/zlib.map rename to internal-complibs/zlib-ng-2.0.7/zlib.map index f608f2bd..fccc0e12 100644 --- a/internal-complibs/zlib-ng-2.0.6/zlib.map +++ b/internal-complibs/zlib-ng-2.0.7/zlib.map @@ -15,7 +15,6 @@ ZLIB_1.2.0 { zcfree; z_errmsg; gz_error; - gz_intmax; _*; }; diff --git a/internal-complibs/zlib-ng-2.0.6/zlib.pc.cmakein b/internal-complibs/zlib-ng-2.0.7/zlib.pc.cmakein similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/zlib.pc.cmakein rename to internal-complibs/zlib-ng-2.0.7/zlib.pc.cmakein diff --git a/internal-complibs/zlib-ng-2.0.6/zlib.pc.in b/internal-complibs/zlib-ng-2.0.7/zlib.pc.in similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/zlib.pc.in rename to internal-complibs/zlib-ng-2.0.7/zlib.pc.in diff --git a/internal-complibs/zlib-ng-2.0.6/zutil.c b/internal-complibs/zlib-ng-2.0.7/zutil.c similarity index 98% rename from internal-complibs/zlib-ng-2.0.6/zutil.c rename to internal-complibs/zlib-ng-2.0.7/zutil.c index 398e17e7..09c595f7 100644 --- a/internal-complibs/zlib-ng-2.0.6/zutil.c +++ b/internal-complibs/zlib-ng-2.0.7/zutil.c @@ -21,7 +21,7 @@ z_const char * const PREFIX(z_errmsg)[10] = { }; const char zlibng_string[] = - " zlib-ng 2.0.6 forked from zlib"; + " zlib-ng 2.0.7 forked from zlib"; #ifdef ZLIB_COMPAT const char * Z_EXPORT zlibVersion(void) { diff --git a/internal-complibs/zlib-ng-2.0.6/zutil.h b/internal-complibs/zlib-ng-2.0.7/zutil.h similarity index 100% rename from internal-complibs/zlib-ng-2.0.6/zutil.h rename to internal-complibs/zlib-ng-2.0.7/zutil.h diff --git a/internal-complibs/zlib-ng-2.0.6/zutil_p.h b/internal-complibs/zlib-ng-2.0.7/zutil_p.h similarity index 85% rename from internal-complibs/zlib-ng-2.0.6/zutil_p.h rename to internal-complibs/zlib-ng-2.0.7/zutil_p.h index 55f00611..856b44f9 100644 --- a/internal-complibs/zlib-ng-2.0.6/zutil_p.h +++ b/internal-complibs/zlib-ng-2.0.7/zutil_p.h @@ -9,7 +9,7 @@ # define _POSIX_C_SOURCE 200112L /* For posix_memalign(). */ #endif -#if defined(__APPLE__) || defined(HAVE_POSIX_MEMALIGN) +#if defined(__APPLE__) || defined(HAVE_POSIX_MEMALIGN) || defined(HAVE_ALIGNED_ALLOC) # include #elif defined(__FreeBSD__) # include @@ -27,6 +27,8 @@ static inline void *zng_alloc(size_t size) { return (void *)_aligned_malloc(size, 64); #elif defined(__APPLE__) return (void *)malloc(size); /* MacOS always aligns to 16 bytes */ +#elif defined(HAVE_ALIGNED_ALLOC) + return (void *)aligned_alloc(64, size); #else return (void *)memalign(64, size); #endif diff --git a/internal-complibs/zstd-1.5.2/common/threading.c b/internal-complibs/zstd-1.5.2/common/threading.c deleted file mode 100644 index 92cf57c1..00000000 --- a/internal-complibs/zstd-1.5.2/common/threading.c +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Copyright (c) 2016 Tino Reichardt - * All rights reserved. - * - * You can contact the author at: - * - zstdmt source repository: https://github.com/mcmilk/zstdmt - * - * This source code is licensed under both the BSD-style license (found in the - * LICENSE file in the root directory of this source tree) and the GPLv2 (found - * in the COPYING file in the root directory of this source tree). - * You may select, at your option, one of the above-listed licenses. - */ - -/** - * This file will hold wrapper for systems, which do not support pthreads - */ - -#include "threading.h" - -/* create fake symbol to avoid empty translation unit warning */ -int g_ZSTD_threading_useless_symbol; - -#if defined(ZSTD_MULTITHREAD) && defined(_WIN32) - -/** - * Windows minimalist Pthread Wrapper, based on : - * http://www.cse.wustl.edu/~schmidt/win32-cv-1.html - */ - - -/* === Dependencies === */ -#include -#include - - -/* === Implementation === */ - -static unsigned __stdcall worker(void *arg) -{ - ZSTD_pthread_t* const thread = (ZSTD_pthread_t*) arg; - thread->arg = thread->start_routine(thread->arg); - return 0; -} - -int ZSTD_pthread_create(ZSTD_pthread_t* thread, const void* unused, - void* (*start_routine) (void*), void* arg) -{ - (void)unused; - thread->arg = arg; - thread->start_routine = start_routine; - thread->handle = (HANDLE) _beginthreadex(NULL, 0, worker, thread, 0, NULL); - - if (!thread->handle) - return errno; - else - return 0; -} - -int ZSTD_pthread_join(ZSTD_pthread_t thread, void **value_ptr) -{ - DWORD result; - - if (!thread.handle) return 0; - - result = WaitForSingleObject(thread.handle, INFINITE); - switch (result) { - case WAIT_OBJECT_0: - if (value_ptr) *value_ptr = thread.arg; - return 0; - case WAIT_ABANDONED: - return EINVAL; - default: - return GetLastError(); - } -} - -#endif /* ZSTD_MULTITHREAD */ - -#if defined(ZSTD_MULTITHREAD) && DEBUGLEVEL >= 1 && !defined(_WIN32) - -#define ZSTD_DEPS_NEED_MALLOC -#include "zstd_deps.h" - -int ZSTD_pthread_mutex_init(ZSTD_pthread_mutex_t* mutex, pthread_mutexattr_t const* attr) -{ - *mutex = (pthread_mutex_t*)ZSTD_malloc(sizeof(pthread_mutex_t)); - if (!*mutex) - return 1; - return pthread_mutex_init(*mutex, attr); -} - -int ZSTD_pthread_mutex_destroy(ZSTD_pthread_mutex_t* mutex) -{ - if (!*mutex) - return 0; - { - int const ret = pthread_mutex_destroy(*mutex); - ZSTD_free(*mutex); - return ret; - } -} - -int ZSTD_pthread_cond_init(ZSTD_pthread_cond_t* cond, pthread_condattr_t const* attr) -{ - *cond = (pthread_cond_t*)ZSTD_malloc(sizeof(pthread_cond_t)); - if (!*cond) - return 1; - return pthread_cond_init(*cond, attr); -} - -int ZSTD_pthread_cond_destroy(ZSTD_pthread_cond_t* cond) -{ - if (!*cond) - return 0; - { - int const ret = pthread_cond_destroy(*cond); - ZSTD_free(*cond); - return ret; - } -} - -#endif diff --git a/internal-complibs/zstd-1.5.2/compress/zstd_compress_literals.c b/internal-complibs/zstd-1.5.2/compress/zstd_compress_literals.c deleted file mode 100644 index 52b0a805..00000000 --- a/internal-complibs/zstd-1.5.2/compress/zstd_compress_literals.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) Yann Collet, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under both the BSD-style license (found in the - * LICENSE file in the root directory of this source tree) and the GPLv2 (found - * in the COPYING file in the root directory of this source tree). - * You may select, at your option, one of the above-listed licenses. - */ - - /*-************************************* - * Dependencies - ***************************************/ -#include "zstd_compress_literals.h" - -size_t ZSTD_noCompressLiterals (void* dst, size_t dstCapacity, const void* src, size_t srcSize) -{ - BYTE* const ostart = (BYTE*)dst; - U32 const flSize = 1 + (srcSize>31) + (srcSize>4095); - - RETURN_ERROR_IF(srcSize + flSize > dstCapacity, dstSize_tooSmall, ""); - - switch(flSize) - { - case 1: /* 2 - 1 - 5 */ - ostart[0] = (BYTE)((U32)set_basic + (srcSize<<3)); - break; - case 2: /* 2 - 2 - 12 */ - MEM_writeLE16(ostart, (U16)((U32)set_basic + (1<<2) + (srcSize<<4))); - break; - case 3: /* 2 - 2 - 20 */ - MEM_writeLE32(ostart, (U32)((U32)set_basic + (3<<2) + (srcSize<<4))); - break; - default: /* not necessary : flSize is {1,2,3} */ - assert(0); - } - - ZSTD_memcpy(ostart + flSize, src, srcSize); - DEBUGLOG(5, "Raw literals: %u -> %u", (U32)srcSize, (U32)(srcSize + flSize)); - return srcSize + flSize; -} - -size_t ZSTD_compressRleLiteralsBlock (void* dst, size_t dstCapacity, const void* src, size_t srcSize) -{ - BYTE* const ostart = (BYTE*)dst; - U32 const flSize = 1 + (srcSize>31) + (srcSize>4095); - - (void)dstCapacity; /* dstCapacity already guaranteed to be >=4, hence large enough */ - - switch(flSize) - { - case 1: /* 2 - 1 - 5 */ - ostart[0] = (BYTE)((U32)set_rle + (srcSize<<3)); - break; - case 2: /* 2 - 2 - 12 */ - MEM_writeLE16(ostart, (U16)((U32)set_rle + (1<<2) + (srcSize<<4))); - break; - case 3: /* 2 - 2 - 20 */ - MEM_writeLE32(ostart, (U32)((U32)set_rle + (3<<2) + (srcSize<<4))); - break; - default: /* not necessary : flSize is {1,2,3} */ - assert(0); - } - - ostart[flSize] = *(const BYTE*)src; - DEBUGLOG(5, "RLE literals: %u -> %u", (U32)srcSize, (U32)flSize + 1); - return flSize+1; -} - -size_t ZSTD_compressLiterals (ZSTD_hufCTables_t const* prevHuf, - ZSTD_hufCTables_t* nextHuf, - ZSTD_strategy strategy, int disableLiteralCompression, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize, - void* entropyWorkspace, size_t entropyWorkspaceSize, - const int bmi2, - unsigned suspectUncompressible) -{ - size_t const minGain = ZSTD_minGain(srcSize, strategy); - size_t const lhSize = 3 + (srcSize >= 1 KB) + (srcSize >= 16 KB); - BYTE* const ostart = (BYTE*)dst; - U32 singleStream = srcSize < 256; - symbolEncodingType_e hType = set_compressed; - size_t cLitSize; - - DEBUGLOG(5,"ZSTD_compressLiterals (disableLiteralCompression=%i srcSize=%u)", - disableLiteralCompression, (U32)srcSize); - - /* Prepare nextEntropy assuming reusing the existing table */ - ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); - - if (disableLiteralCompression) - return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); - - /* small ? don't even attempt compression (speed opt) */ -# define COMPRESS_LITERALS_SIZE_MIN 63 - { size_t const minLitSize = (prevHuf->repeatMode == HUF_repeat_valid) ? 6 : COMPRESS_LITERALS_SIZE_MIN; - if (srcSize <= minLitSize) return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); - } - - RETURN_ERROR_IF(dstCapacity < lhSize+1, dstSize_tooSmall, "not enough space for compression"); - { HUF_repeat repeat = prevHuf->repeatMode; - int const preferRepeat = strategy < ZSTD_lazy ? srcSize <= 1024 : 0; - if (repeat == HUF_repeat_valid && lhSize == 3) singleStream = 1; - cLitSize = singleStream ? - HUF_compress1X_repeat( - ostart+lhSize, dstCapacity-lhSize, src, srcSize, - HUF_SYMBOLVALUE_MAX, HUF_TABLELOG_DEFAULT, entropyWorkspace, entropyWorkspaceSize, - (HUF_CElt*)nextHuf->CTable, &repeat, preferRepeat, bmi2, suspectUncompressible) : - HUF_compress4X_repeat( - ostart+lhSize, dstCapacity-lhSize, src, srcSize, - HUF_SYMBOLVALUE_MAX, HUF_TABLELOG_DEFAULT, entropyWorkspace, entropyWorkspaceSize, - (HUF_CElt*)nextHuf->CTable, &repeat, preferRepeat, bmi2, suspectUncompressible); - if (repeat != HUF_repeat_none) { - /* reused the existing table */ - DEBUGLOG(5, "Reusing previous huffman table"); - hType = set_repeat; - } - } - - if ((cLitSize==0) || (cLitSize >= srcSize - minGain) || ERR_isError(cLitSize)) { - ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); - return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); - } - if (cLitSize==1) { - ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); - return ZSTD_compressRleLiteralsBlock(dst, dstCapacity, src, srcSize); - } - - if (hType == set_compressed) { - /* using a newly constructed table */ - nextHuf->repeatMode = HUF_repeat_check; - } - - /* Build header */ - switch(lhSize) - { - case 3: /* 2 - 2 - 10 - 10 */ - { U32 const lhc = hType + ((!singleStream) << 2) + ((U32)srcSize<<4) + ((U32)cLitSize<<14); - MEM_writeLE24(ostart, lhc); - break; - } - case 4: /* 2 - 2 - 14 - 14 */ - { U32 const lhc = hType + (2 << 2) + ((U32)srcSize<<4) + ((U32)cLitSize<<18); - MEM_writeLE32(ostart, lhc); - break; - } - case 5: /* 2 - 2 - 18 - 18 */ - { U32 const lhc = hType + (3 << 2) + ((U32)srcSize<<4) + ((U32)cLitSize<<22); - MEM_writeLE32(ostart, lhc); - ostart[4] = (BYTE)(cLitSize >> 10); - break; - } - default: /* not possible : lhSize is {3,4,5} */ - assert(0); - } - DEBUGLOG(5, "Compressed literals: %u -> %u", (U32)srcSize, (U32)(lhSize+cLitSize)); - return lhSize+cLitSize; -} diff --git a/internal-complibs/zstd-1.5.2/.gitignore b/internal-complibs/zstd-1.5.5/.gitignore similarity index 100% rename from internal-complibs/zstd-1.5.2/.gitignore rename to internal-complibs/zstd-1.5.5/.gitignore diff --git a/internal-complibs/zstd-1.5.2/BUCK b/internal-complibs/zstd-1.5.5/BUCK similarity index 100% rename from internal-complibs/zstd-1.5.2/BUCK rename to internal-complibs/zstd-1.5.5/BUCK diff --git a/internal-complibs/zstd-1.5.2/Makefile b/internal-complibs/zstd-1.5.5/Makefile similarity index 99% rename from internal-complibs/zstd-1.5.2/Makefile rename to internal-complibs/zstd-1.5.5/Makefile index ef202183..a4cf61ab 100644 --- a/internal-complibs/zstd-1.5.2/Makefile +++ b/internal-complibs/zstd-1.5.5/Makefile @@ -1,5 +1,5 @@ # ################################################################ -# Copyright (c) Yann Collet, Facebook, Inc. +# Copyright (c) Meta Platforms, Inc. and affiliates. # All rights reserved. # # This source code is licensed under both the BSD-style license (found in the @@ -298,7 +298,7 @@ INSTALL_DATA ?= $(INSTALL) -m 644 libzstd.pc: libzstd.pc.in @echo creating pkgconfig - @sed $(SED_ERE_OPT) \ + @sed \ -e 's|@PREFIX@|$(PREFIX)|' \ -e 's|@EXEC_PREFIX@|$(PCEXEC_PREFIX)|' \ -e 's|@INCLUDEDIR@|$(PCINCPREFIX)$(PCINCDIR)|' \ diff --git a/internal-complibs/zstd-1.5.2/README.md b/internal-complibs/zstd-1.5.5/README.md similarity index 94% rename from internal-complibs/zstd-1.5.2/README.md rename to internal-complibs/zstd-1.5.5/README.md index 4c9d8f05..c3b5d181 100644 --- a/internal-complibs/zstd-1.5.2/README.md +++ b/internal-complibs/zstd-1.5.5/README.md @@ -91,7 +91,7 @@ The file structure is designed to make this selection manually achievable for an `ZSTD_LIB_COMPRESSION, ZSTD_LIB_DECOMPRESSION`, `ZSTD_LIB_DICTBUILDER`, and `ZSTD_LIB_DEPRECATED` as `0` to forgo compilation of the corresponding features. This will also disable compilation of all - dependencies (eg. `ZSTD_LIB_COMPRESSION=0` will also disable + dependencies (e.g. `ZSTD_LIB_COMPRESSION=0` will also disable dictBuilder). - There are a number of options that can help minimize the binary size of @@ -161,6 +161,13 @@ The file structure is designed to make this selection manually achievable for an `ZSTD_DCtx` decompression contexts, but might also result in a small decompression speed cost. +- The C compiler macros `ZSTDLIB_VISIBLE`, `ZSTDERRORLIB_VISIBLE` and `ZDICTLIB_VISIBLE` + can be overridden to control the visibility of zstd's API. Additionally, + `ZSTDLIB_STATIC_API` and `ZDICTLIB_STATIC_API` can be overridden to control the visibility + of zstd's static API. Specifically, it can be set to `ZSTDLIB_HIDDEN` to hide the symbols + from the shared library. These macros default to `ZSTDLIB_VISIBILITY`, + `ZSTDERRORLIB_VSIBILITY`, and `ZDICTLIB_VISIBILITY` if unset, for backwards compatibility + with the old macro names. #### Windows : using MinGW+MSYS to create DLL diff --git a/internal-complibs/zstd-1.5.5/common/allocations.h b/internal-complibs/zstd-1.5.5/common/allocations.h new file mode 100644 index 00000000..a3153c4b --- /dev/null +++ b/internal-complibs/zstd-1.5.5/common/allocations.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under both the BSD-style license (found in the + * LICENSE file in the root directory of this source tree) and the GPLv2 (found + * in the COPYING file in the root directory of this source tree). + * You may select, at your option, one of the above-listed licenses. + */ + +/* This file provides custom allocation primitives + */ + +#define ZSTD_DEPS_NEED_MALLOC +#include "zstd_deps.h" /* ZSTD_malloc, ZSTD_calloc, ZSTD_free, ZSTD_memset */ + +#include "mem.h" /* MEM_STATIC */ +#define ZSTD_STATIC_LINKING_ONLY +#include "../zstd.h" /* ZSTD_customMem */ + +#ifndef ZSTD_ALLOCATIONS_H +#define ZSTD_ALLOCATIONS_H + +/* custom memory allocation functions */ + +MEM_STATIC void* ZSTD_customMalloc(size_t size, ZSTD_customMem customMem) +{ + if (customMem.customAlloc) + return customMem.customAlloc(customMem.opaque, size); + return ZSTD_malloc(size); +} + +MEM_STATIC void* ZSTD_customCalloc(size_t size, ZSTD_customMem customMem) +{ + if (customMem.customAlloc) { + /* calloc implemented as malloc+memset; + * not as efficient as calloc, but next best guess for custom malloc */ + void* const ptr = customMem.customAlloc(customMem.opaque, size); + ZSTD_memset(ptr, 0, size); + return ptr; + } + return ZSTD_calloc(1, size); +} + +MEM_STATIC void ZSTD_customFree(void* ptr, ZSTD_customMem customMem) +{ + if (ptr!=NULL) { + if (customMem.customFree) + customMem.customFree(customMem.opaque, ptr); + else + ZSTD_free(ptr); + } +} + +#endif /* ZSTD_ALLOCATIONS_H */ diff --git a/internal-complibs/zstd-1.5.5/common/bits.h b/internal-complibs/zstd-1.5.5/common/bits.h new file mode 100644 index 00000000..def56c47 --- /dev/null +++ b/internal-complibs/zstd-1.5.5/common/bits.h @@ -0,0 +1,200 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under both the BSD-style license (found in the + * LICENSE file in the root directory of this source tree) and the GPLv2 (found + * in the COPYING file in the root directory of this source tree). + * You may select, at your option, one of the above-listed licenses. + */ + +#ifndef ZSTD_BITS_H +#define ZSTD_BITS_H + +#include "mem.h" + +MEM_STATIC unsigned ZSTD_countTrailingZeros32_fallback(U32 val) +{ + assert(val != 0); + { + static const U32 DeBruijnBytePos[32] = {0, 1, 28, 2, 29, 14, 24, 3, + 30, 22, 20, 15, 25, 17, 4, 8, + 31, 27, 13, 23, 21, 19, 16, 7, + 26, 12, 18, 6, 11, 5, 10, 9}; + return DeBruijnBytePos[((U32) ((val & -(S32) val) * 0x077CB531U)) >> 27]; + } +} + +MEM_STATIC unsigned ZSTD_countTrailingZeros32(U32 val) +{ + assert(val != 0); +# if defined(_MSC_VER) +# if STATIC_BMI2 == 1 + return (unsigned)_tzcnt_u32(val); +# else + if (val != 0) { + unsigned long r; + _BitScanForward(&r, val); + return (unsigned)r; + } else { + /* Should not reach this code path */ + __assume(0); + } +# endif +# elif defined(__GNUC__) && (__GNUC__ >= 4) + return (unsigned)__builtin_ctz(val); +# else + return ZSTD_countTrailingZeros32_fallback(val); +# endif +} + +MEM_STATIC unsigned ZSTD_countLeadingZeros32_fallback(U32 val) { + assert(val != 0); + { + static const U32 DeBruijnClz[32] = {0, 9, 1, 10, 13, 21, 2, 29, + 11, 14, 16, 18, 22, 25, 3, 30, + 8, 12, 20, 28, 15, 17, 24, 7, + 19, 27, 23, 6, 26, 5, 4, 31}; + val |= val >> 1; + val |= val >> 2; + val |= val >> 4; + val |= val >> 8; + val |= val >> 16; + return 31 - DeBruijnClz[(val * 0x07C4ACDDU) >> 27]; + } +} + +MEM_STATIC unsigned ZSTD_countLeadingZeros32(U32 val) +{ + assert(val != 0); +# if defined(_MSC_VER) +# if STATIC_BMI2 == 1 + return (unsigned)_lzcnt_u32(val); +# else + if (val != 0) { + unsigned long r; + _BitScanReverse(&r, val); + return (unsigned)(31 - r); + } else { + /* Should not reach this code path */ + __assume(0); + } +# endif +# elif defined(__GNUC__) && (__GNUC__ >= 4) + return (unsigned)__builtin_clz(val); +# else + return ZSTD_countLeadingZeros32_fallback(val); +# endif +} + +MEM_STATIC unsigned ZSTD_countTrailingZeros64(U64 val) +{ + assert(val != 0); +# if defined(_MSC_VER) && defined(_WIN64) +# if STATIC_BMI2 == 1 + return (unsigned)_tzcnt_u64(val); +# else + if (val != 0) { + unsigned long r; + _BitScanForward64(&r, val); + return (unsigned)r; + } else { + /* Should not reach this code path */ + __assume(0); + } +# endif +# elif defined(__GNUC__) && (__GNUC__ >= 4) && defined(__LP64__) + return (unsigned)__builtin_ctzll(val); +# else + { + U32 mostSignificantWord = (U32)(val >> 32); + U32 leastSignificantWord = (U32)val; + if (leastSignificantWord == 0) { + return 32 + ZSTD_countTrailingZeros32(mostSignificantWord); + } else { + return ZSTD_countTrailingZeros32(leastSignificantWord); + } + } +# endif +} + +MEM_STATIC unsigned ZSTD_countLeadingZeros64(U64 val) +{ + assert(val != 0); +# if defined(_MSC_VER) && defined(_WIN64) +# if STATIC_BMI2 == 1 + return (unsigned)_lzcnt_u64(val); +# else + if (val != 0) { + unsigned long r; + _BitScanReverse64(&r, val); + return (unsigned)(63 - r); + } else { + /* Should not reach this code path */ + __assume(0); + } +# endif +# elif defined(__GNUC__) && (__GNUC__ >= 4) + return (unsigned)(__builtin_clzll(val)); +# else + { + U32 mostSignificantWord = (U32)(val >> 32); + U32 leastSignificantWord = (U32)val; + if (mostSignificantWord == 0) { + return 32 + ZSTD_countLeadingZeros32(leastSignificantWord); + } else { + return ZSTD_countLeadingZeros32(mostSignificantWord); + } + } +# endif +} + +MEM_STATIC unsigned ZSTD_NbCommonBytes(size_t val) +{ + if (MEM_isLittleEndian()) { + if (MEM_64bits()) { + return ZSTD_countTrailingZeros64((U64)val) >> 3; + } else { + return ZSTD_countTrailingZeros32((U32)val) >> 3; + } + } else { /* Big Endian CPU */ + if (MEM_64bits()) { + return ZSTD_countLeadingZeros64((U64)val) >> 3; + } else { + return ZSTD_countLeadingZeros32((U32)val) >> 3; + } + } +} + +MEM_STATIC unsigned ZSTD_highbit32(U32 val) /* compress, dictBuilder, decodeCorpus */ +{ + assert(val != 0); + return 31 - ZSTD_countLeadingZeros32(val); +} + +/* ZSTD_rotateRight_*(): + * Rotates a bitfield to the right by "count" bits. + * https://en.wikipedia.org/w/index.php?title=Circular_shift&oldid=991635599#Implementing_circular_shifts + */ +MEM_STATIC +U64 ZSTD_rotateRight_U64(U64 const value, U32 count) { + assert(count < 64); + count &= 0x3F; /* for fickle pattern recognition */ + return (value >> count) | (U64)(value << ((0U - count) & 0x3F)); +} + +MEM_STATIC +U32 ZSTD_rotateRight_U32(U32 const value, U32 count) { + assert(count < 32); + count &= 0x1F; /* for fickle pattern recognition */ + return (value >> count) | (U32)(value << ((0U - count) & 0x1F)); +} + +MEM_STATIC +U16 ZSTD_rotateRight_U16(U16 const value, U32 count) { + assert(count < 16); + count &= 0x0F; /* for fickle pattern recognition */ + return (value >> count) | (U16)(value << ((0U - count) & 0x0F)); +} + +#endif /* ZSTD_BITS_H */ diff --git a/internal-complibs/zstd-1.5.2/common/bitstream.h b/internal-complibs/zstd-1.5.5/common/bitstream.h similarity index 89% rename from internal-complibs/zstd-1.5.2/common/bitstream.h rename to internal-complibs/zstd-1.5.5/common/bitstream.h index 84b6062f..72b0b3df 100644 --- a/internal-complibs/zstd-1.5.2/common/bitstream.h +++ b/internal-complibs/zstd-1.5.5/common/bitstream.h @@ -1,7 +1,7 @@ /* ****************************************************************** * bitstream * Part of FSE library - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy @@ -30,14 +30,15 @@ extern "C" { #include "compiler.h" /* UNLIKELY() */ #include "debug.h" /* assert(), DEBUGLOG(), RAWLOG() */ #include "error_private.h" /* error codes and messages */ +#include "bits.h" /* ZSTD_highbit32 */ /*========================================= * Target specific =========================================*/ #ifndef ZSTD_NO_INTRINSICS -# if defined(__BMI__) && defined(__GNUC__) -# include /* support for bextr (experimental) */ +# if (defined(__BMI__) || defined(__BMI2__)) && defined(__GNUC__) +# include /* support for bextr (experimental)/bzhi */ # elif defined(__ICCARM__) # include # endif @@ -132,48 +133,6 @@ MEM_STATIC void BIT_flushBitsFast(BIT_CStream_t* bitC); MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, unsigned nbBits); /* faster, but works only if nbBits >= 1 */ - - -/*-************************************************************** -* Internal functions -****************************************************************/ -MEM_STATIC unsigned BIT_highbit32 (U32 val) -{ - assert(val != 0); - { -# if defined(_MSC_VER) /* Visual */ -# if STATIC_BMI2 == 1 - return _lzcnt_u32(val) ^ 31; -# else - if (val != 0) { - unsigned long r; - _BitScanReverse(&r, val); - return (unsigned)r; - } else { - /* Should not reach this code path */ - __assume(0); - } -# endif -# elif defined(__GNUC__) && (__GNUC__ >= 3) /* Use GCC Intrinsic */ - return __builtin_clz (val) ^ 31; -# elif defined(__ICCARM__) /* IAR Intrinsic */ - return 31 - __CLZ(val); -# else /* Software version */ - static const unsigned DeBruijnClz[32] = { 0, 9, 1, 10, 13, 21, 2, 29, - 11, 14, 16, 18, 22, 25, 3, 30, - 8, 12, 20, 28, 15, 17, 24, 7, - 19, 27, 23, 6, 26, 5, 4, 31 }; - U32 v = val; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - return DeBruijnClz[ (U32) (v * 0x07C4ACDDU) >> 27]; -# endif - } -} - /*===== Local Constants =====*/ static const unsigned BIT_mask[] = { 0, 1, 3, 7, 0xF, 0x1F, @@ -203,6 +162,16 @@ MEM_STATIC size_t BIT_initCStream(BIT_CStream_t* bitC, return 0; } +MEM_STATIC FORCE_INLINE_ATTR size_t BIT_getLowerBits(size_t bitContainer, U32 const nbBits) +{ +#if defined(STATIC_BMI2) && STATIC_BMI2 == 1 && !defined(ZSTD_NO_INTRINSICS) + return _bzhi_u64(bitContainer, nbBits); +#else + assert(nbBits < BIT_MASK_SIZE); + return bitContainer & BIT_mask[nbBits]; +#endif +} + /*! BIT_addBits() : * can add up to 31 bits into `bitC`. * Note : does not check for register overflow ! */ @@ -212,7 +181,7 @@ MEM_STATIC void BIT_addBits(BIT_CStream_t* bitC, DEBUG_STATIC_ASSERT(BIT_MASK_SIZE == 32); assert(nbBits < BIT_MASK_SIZE); assert(nbBits + bitC->bitPos < sizeof(bitC->bitContainer) * 8); - bitC->bitContainer |= (value & BIT_mask[nbBits]) << bitC->bitPos; + bitC->bitContainer |= BIT_getLowerBits(value, nbBits) << bitC->bitPos; bitC->bitPos += nbBits; } @@ -291,7 +260,7 @@ MEM_STATIC size_t BIT_initDStream(BIT_DStream_t* bitD, const void* srcBuffer, si bitD->ptr = (const char*)srcBuffer + srcSize - sizeof(bitD->bitContainer); bitD->bitContainer = MEM_readLEST(bitD->ptr); { BYTE const lastByte = ((const BYTE*)srcBuffer)[srcSize-1]; - bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0; /* ensures bitsConsumed is always set */ + bitD->bitsConsumed = lastByte ? 8 - ZSTD_highbit32(lastByte) : 0; /* ensures bitsConsumed is always set */ if (lastByte == 0) return ERROR(GENERIC); /* endMark not present */ } } else { bitD->ptr = bitD->start; @@ -319,7 +288,7 @@ MEM_STATIC size_t BIT_initDStream(BIT_DStream_t* bitD, const void* srcBuffer, si default: break; } { BYTE const lastByte = ((const BYTE*)srcBuffer)[srcSize-1]; - bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0; + bitD->bitsConsumed = lastByte ? 8 - ZSTD_highbit32(lastByte) : 0; if (lastByte == 0) return ERROR(corruption_detected); /* endMark not present */ } bitD->bitsConsumed += (U32)(sizeof(bitD->bitContainer) - srcSize)*8; @@ -350,16 +319,6 @@ MEM_STATIC FORCE_INLINE_ATTR size_t BIT_getMiddleBits(size_t bitContainer, U32 c #endif } -MEM_STATIC FORCE_INLINE_ATTR size_t BIT_getLowerBits(size_t bitContainer, U32 const nbBits) -{ -#if defined(STATIC_BMI2) && STATIC_BMI2 == 1 - return _bzhi_u64(bitContainer, nbBits); -#else - assert(nbBits < BIT_MASK_SIZE); - return bitContainer & BIT_mask[nbBits]; -#endif -} - /*! BIT_lookBits() : * Provides next n bits from local register. * local register is not modified. @@ -406,7 +365,7 @@ MEM_STATIC FORCE_INLINE_ATTR size_t BIT_readBits(BIT_DStream_t* bitD, unsigned n } /*! BIT_readBitsFast() : - * unsafe version; only works only if nbBits >= 1 */ + * unsafe version; only works if nbBits >= 1 */ MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, unsigned nbBits) { size_t const value = BIT_lookBitsFast(bitD, nbBits); @@ -437,7 +396,7 @@ MEM_STATIC BIT_DStream_status BIT_reloadDStreamFast(BIT_DStream_t* bitD) * This function is safe, it guarantees it will not read beyond src buffer. * @return : status of `BIT_DStream_t` internal register. * when status == BIT_DStream_unfinished, internal register is filled with at least 25 or 57 bits */ -MEM_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD) +MEM_STATIC FORCE_INLINE_ATTR BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD) { if (bitD->bitsConsumed > (sizeof(bitD->bitContainer)*8)) /* overflow detected, like end of stream */ return BIT_DStream_overflow; diff --git a/internal-complibs/zstd-1.5.2/common/compiler.h b/internal-complibs/zstd-1.5.5/common/compiler.h similarity index 91% rename from internal-complibs/zstd-1.5.2/common/compiler.h rename to internal-complibs/zstd-1.5.5/common/compiler.h index 516930c0..73f8d019 100644 --- a/internal-complibs/zstd-1.5.2/common/compiler.h +++ b/internal-complibs/zstd-1.5.5/common/compiler.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -165,6 +165,12 @@ #define UNLIKELY(x) (x) #endif +#if __has_builtin(__builtin_unreachable) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))) +# define ZSTD_UNREACHABLE { assert(0), __builtin_unreachable(); } +#else +# define ZSTD_UNREACHABLE { assert(0); } +#endif + /* disable warnings */ #ifdef _MSC_VER /* Visual Studio */ # include /* For Visual 2005 */ @@ -181,6 +187,8 @@ # ifdef __AVX2__ //MSVC does not have a BMI2 specific flag, but every CPU that supports AVX2 also supports BMI2 # define STATIC_BMI2 1 # endif +# elif defined(__BMI2__) && defined(__x86_64__) && defined(__GNUC__) +# define STATIC_BMI2 1 # endif #endif @@ -273,7 +281,18 @@ * Sanitizer *****************************************************************/ -#if ZSTD_MEMORY_SANITIZER +/* Issue #3240 reports an ASAN failure on an llvm-mingw build. Out of an + * abundance of caution, disable our custom poisoning on mingw. */ +#ifdef __MINGW32__ +#ifndef ZSTD_ASAN_DONT_POISON_WORKSPACE +#define ZSTD_ASAN_DONT_POISON_WORKSPACE 1 +#endif +#ifndef ZSTD_MSAN_DONT_POISON_WORKSPACE +#define ZSTD_MSAN_DONT_POISON_WORKSPACE 1 +#endif +#endif + +#if ZSTD_MEMORY_SANITIZER && !defined(ZSTD_MSAN_DONT_POISON_WORKSPACE) /* Not all platforms that support msan provide sanitizers/msan_interface.h. * We therefore declare the functions we need ourselves, rather than trying to * include the header file... */ @@ -292,9 +311,13 @@ void __msan_poison(const volatile void *a, size_t size); /* Returns the offset of the first (at least partially) poisoned byte in the memory range, or -1 if the whole range is good. */ intptr_t __msan_test_shadow(const volatile void *x, size_t size); + +/* Print shadow and origin for the memory range to stderr in a human-readable + format. */ +void __msan_print_shadow(const volatile void *x, size_t size); #endif -#if ZSTD_ADDRESS_SANITIZER +#if ZSTD_ADDRESS_SANITIZER && !defined(ZSTD_ASAN_DONT_POISON_WORKSPACE) /* Not all platforms that support asan provide sanitizers/asan_interface.h. * We therefore declare the functions we need ourselves, rather than trying to * include the header file... */ diff --git a/internal-complibs/zstd-1.5.2/common/cpu.h b/internal-complibs/zstd-1.5.5/common/cpu.h similarity index 98% rename from internal-complibs/zstd-1.5.2/common/cpu.h rename to internal-complibs/zstd-1.5.5/common/cpu.h index 8acd33be..8bc34a36 100644 --- a/internal-complibs/zstd-1.5.2/common/cpu.h +++ b/internal-complibs/zstd-1.5.5/common/cpu.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the diff --git a/internal-complibs/zstd-1.5.2/common/debug.c b/internal-complibs/zstd-1.5.5/common/debug.c similarity index 93% rename from internal-complibs/zstd-1.5.2/common/debug.c rename to internal-complibs/zstd-1.5.5/common/debug.c index bb863c9e..ebf7bfcc 100644 --- a/internal-complibs/zstd-1.5.2/common/debug.c +++ b/internal-complibs/zstd-1.5.5/common/debug.c @@ -1,7 +1,7 @@ /* ****************************************************************** * debug * Part of FSE library - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy diff --git a/internal-complibs/zstd-1.5.2/common/debug.h b/internal-complibs/zstd-1.5.5/common/debug.h similarity index 98% rename from internal-complibs/zstd-1.5.2/common/debug.h rename to internal-complibs/zstd-1.5.5/common/debug.h index 3b2a320a..0e9817ea 100644 --- a/internal-complibs/zstd-1.5.2/common/debug.h +++ b/internal-complibs/zstd-1.5.5/common/debug.h @@ -1,7 +1,7 @@ /* ****************************************************************** * debug * Part of FSE library - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy diff --git a/internal-complibs/zstd-1.5.2/common/entropy_common.c b/internal-complibs/zstd-1.5.5/common/entropy_common.c similarity index 90% rename from internal-complibs/zstd-1.5.2/common/entropy_common.c rename to internal-complibs/zstd-1.5.5/common/entropy_common.c index 4229b40c..e2173afb 100644 --- a/internal-complibs/zstd-1.5.2/common/entropy_common.c +++ b/internal-complibs/zstd-1.5.5/common/entropy_common.c @@ -1,6 +1,6 @@ /* ****************************************************************** * Common functions of New Generation Entropy library - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : * - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy @@ -19,8 +19,8 @@ #include "error_private.h" /* ERR_*, ERROR */ #define FSE_STATIC_LINKING_ONLY /* FSE_MIN_TABLELOG */ #include "fse.h" -#define HUF_STATIC_LINKING_ONLY /* HUF_TABLELOG_ABSOLUTEMAX */ #include "huf.h" +#include "bits.h" /* ZSDT_highbit32, ZSTD_countTrailingZeros32 */ /*=== Version ===*/ @@ -38,34 +38,6 @@ const char* HUF_getErrorName(size_t code) { return ERR_getErrorName(code); } /*-************************************************************** * FSE NCount encoding-decoding ****************************************************************/ -static U32 FSE_ctz(U32 val) -{ - assert(val != 0); - { -# if defined(_MSC_VER) /* Visual */ - if (val != 0) { - unsigned long r; - _BitScanForward(&r, val); - return (unsigned)r; - } else { - /* Should not reach this code path */ - __assume(0); - } -# elif defined(__GNUC__) && (__GNUC__ >= 3) /* GCC Intrinsic */ - return __builtin_ctz(val); -# elif defined(__ICCARM__) /* IAR Intrinsic */ - return __CTZ(val); -# else /* Software version */ - U32 count = 0; - while ((val & 1) == 0) { - val >>= 1; - ++count; - } - return count; -# endif - } -} - FORCE_INLINE_TEMPLATE size_t FSE_readNCount_body(short* normalizedCounter, unsigned* maxSVPtr, unsigned* tableLogPtr, const void* headerBuffer, size_t hbSize) @@ -113,7 +85,7 @@ size_t FSE_readNCount_body(short* normalizedCounter, unsigned* maxSVPtr, unsigne * repeat. * Avoid UB by setting the high bit to 1. */ - int repeats = FSE_ctz(~bitStream | 0x80000000) >> 1; + int repeats = ZSTD_countTrailingZeros32(~bitStream | 0x80000000) >> 1; while (repeats >= 12) { charnum += 3 * 12; if (LIKELY(ip <= iend-7)) { @@ -124,7 +96,7 @@ size_t FSE_readNCount_body(short* normalizedCounter, unsigned* maxSVPtr, unsigne ip = iend - 4; } bitStream = MEM_readLE32(ip) >> bitCount; - repeats = FSE_ctz(~bitStream | 0x80000000) >> 1; + repeats = ZSTD_countTrailingZeros32(~bitStream | 0x80000000) >> 1; } charnum += 3 * repeats; bitStream >>= 2 * repeats; @@ -189,7 +161,7 @@ size_t FSE_readNCount_body(short* normalizedCounter, unsigned* maxSVPtr, unsigne * know that threshold > 1. */ if (remaining <= 1) break; - nbBits = BIT_highbit32(remaining) + 1; + nbBits = ZSTD_highbit32(remaining) + 1; threshold = 1 << (nbBits - 1); } if (charnum >= maxSV1) break; @@ -264,7 +236,7 @@ size_t HUF_readStats(BYTE* huffWeight, size_t hwSize, U32* rankStats, const void* src, size_t srcSize) { U32 wksp[HUF_READ_STATS_WORKSPACE_SIZE_U32]; - return HUF_readStats_wksp(huffWeight, hwSize, rankStats, nbSymbolsPtr, tableLogPtr, src, srcSize, wksp, sizeof(wksp), /* bmi2 */ 0); + return HUF_readStats_wksp(huffWeight, hwSize, rankStats, nbSymbolsPtr, tableLogPtr, src, srcSize, wksp, sizeof(wksp), /* flags */ 0); } FORCE_INLINE_TEMPLATE size_t @@ -312,14 +284,14 @@ HUF_readStats_body(BYTE* huffWeight, size_t hwSize, U32* rankStats, if (weightTotal == 0) return ERROR(corruption_detected); /* get last non-null symbol weight (implied, total must be 2^n) */ - { U32 const tableLog = BIT_highbit32(weightTotal) + 1; + { U32 const tableLog = ZSTD_highbit32(weightTotal) + 1; if (tableLog > HUF_TABLELOG_MAX) return ERROR(corruption_detected); *tableLogPtr = tableLog; /* determine last weight */ { U32 const total = 1 << tableLog; U32 const rest = total - weightTotal; - U32 const verif = 1 << BIT_highbit32(rest); - U32 const lastWeight = BIT_highbit32(rest) + 1; + U32 const verif = 1 << ZSTD_highbit32(rest); + U32 const lastWeight = ZSTD_highbit32(rest) + 1; if (verif != rest) return ERROR(corruption_detected); /* last value must be a clean power of 2 */ huffWeight[oSize] = (BYTE)lastWeight; rankStats[lastWeight]++; @@ -356,13 +328,13 @@ size_t HUF_readStats_wksp(BYTE* huffWeight, size_t hwSize, U32* rankStats, U32* nbSymbolsPtr, U32* tableLogPtr, const void* src, size_t srcSize, void* workSpace, size_t wkspSize, - int bmi2) + int flags) { #if DYNAMIC_BMI2 - if (bmi2) { + if (flags & HUF_flags_bmi2) { return HUF_readStats_body_bmi2(huffWeight, hwSize, rankStats, nbSymbolsPtr, tableLogPtr, src, srcSize, workSpace, wkspSize); } #endif - (void)bmi2; + (void)flags; return HUF_readStats_body_default(huffWeight, hwSize, rankStats, nbSymbolsPtr, tableLogPtr, src, srcSize, workSpace, wkspSize); } diff --git a/internal-complibs/zstd-1.5.2/common/error_private.c b/internal-complibs/zstd-1.5.5/common/error_private.c similarity index 76% rename from internal-complibs/zstd-1.5.2/common/error_private.c rename to internal-complibs/zstd-1.5.5/common/error_private.c index 6d1135f8..075fc5ef 100644 --- a/internal-complibs/zstd-1.5.2/common/error_private.c +++ b/internal-complibs/zstd-1.5.5/common/error_private.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -27,9 +27,11 @@ const char* ERR_getErrorString(ERR_enum code) case PREFIX(version_unsupported): return "Version not supported"; case PREFIX(frameParameter_unsupported): return "Unsupported frame parameter"; case PREFIX(frameParameter_windowTooLarge): return "Frame requires too much memory for decoding"; - case PREFIX(corruption_detected): return "Corrupted block detected"; + case PREFIX(corruption_detected): return "Data corruption detected"; case PREFIX(checksum_wrong): return "Restored data doesn't match checksum"; + case PREFIX(literals_headerWrong): return "Header of Literals' block doesn't respect format specification"; case PREFIX(parameter_unsupported): return "Unsupported parameter"; + case PREFIX(parameter_combination_unsupported): return "Unsupported combination of parameters"; case PREFIX(parameter_outOfBound): return "Parameter is out of bound"; case PREFIX(init_missing): return "Context should be init first"; case PREFIX(memory_allocation): return "Allocation error : not enough memory"; @@ -38,17 +40,22 @@ const char* ERR_getErrorString(ERR_enum code) case PREFIX(tableLog_tooLarge): return "tableLog requires too much memory : unsupported"; case PREFIX(maxSymbolValue_tooLarge): return "Unsupported max Symbol Value : too large"; case PREFIX(maxSymbolValue_tooSmall): return "Specified maxSymbolValue is too small"; + case PREFIX(stabilityCondition_notRespected): return "pledged buffer stability condition is not respected"; case PREFIX(dictionary_corrupted): return "Dictionary is corrupted"; case PREFIX(dictionary_wrong): return "Dictionary mismatch"; case PREFIX(dictionaryCreation_failed): return "Cannot create Dictionary from provided samples"; case PREFIX(dstSize_tooSmall): return "Destination buffer is too small"; case PREFIX(srcSize_wrong): return "Src size is incorrect"; case PREFIX(dstBuffer_null): return "Operation on NULL destination buffer"; + case PREFIX(noForwardProgress_destFull): return "Operation made no progress over multiple calls, due to output buffer being full"; + case PREFIX(noForwardProgress_inputEmpty): return "Operation made no progress over multiple calls, due to input being empty"; /* following error codes are not stable and may be removed or changed in a future version */ case PREFIX(frameIndex_tooLarge): return "Frame index is too large"; case PREFIX(seekableIO): return "An I/O error occurred when reading/seeking"; case PREFIX(dstBuffer_wrong): return "Destination buffer is wrong"; case PREFIX(srcBuffer_wrong): return "Source buffer is wrong"; + case PREFIX(sequenceProducer_failed): return "Block-level external sequence producer returned an error code"; + case PREFIX(externalSequences_invalid): return "External sequences are not valid"; case PREFIX(maxCode): default: return notErrorCode; } diff --git a/internal-complibs/zstd-1.5.2/common/error_private.h b/internal-complibs/zstd-1.5.5/common/error_private.h similarity index 98% rename from internal-complibs/zstd-1.5.2/common/error_private.h rename to internal-complibs/zstd-1.5.5/common/error_private.h index 007d8106..325daad4 100644 --- a/internal-complibs/zstd-1.5.2/common/error_private.h +++ b/internal-complibs/zstd-1.5.5/common/error_private.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the diff --git a/internal-complibs/zstd-1.5.2/common/fse.h b/internal-complibs/zstd-1.5.5/common/fse.h similarity index 85% rename from internal-complibs/zstd-1.5.2/common/fse.h rename to internal-complibs/zstd-1.5.5/common/fse.h index 714bfd3e..02a1f0bc 100644 --- a/internal-complibs/zstd-1.5.2/common/fse.h +++ b/internal-complibs/zstd-1.5.5/common/fse.h @@ -1,7 +1,7 @@ /* ****************************************************************** * FSE : Finite State Entropy codec * Public Prototypes declaration - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy @@ -53,34 +53,6 @@ extern "C" { FSE_PUBLIC_API unsigned FSE_versionNumber(void); /**< library version number; to be used when checking dll version */ -/*-**************************************** -* FSE simple functions -******************************************/ -/*! FSE_compress() : - Compress content of buffer 'src', of size 'srcSize', into destination buffer 'dst'. - 'dst' buffer must be already allocated. Compression runs faster is dstCapacity >= FSE_compressBound(srcSize). - @return : size of compressed data (<= dstCapacity). - Special values : if return == 0, srcData is not compressible => Nothing is stored within dst !!! - if return == 1, srcData is a single byte symbol * srcSize times. Use RLE compression instead. - if FSE_isError(return), compression failed (more details using FSE_getErrorName()) -*/ -FSE_PUBLIC_API size_t FSE_compress(void* dst, size_t dstCapacity, - const void* src, size_t srcSize); - -/*! FSE_decompress(): - Decompress FSE data from buffer 'cSrc', of size 'cSrcSize', - into already allocated destination buffer 'dst', of size 'dstCapacity'. - @return : size of regenerated data (<= maxDstSize), - or an error code, which can be tested using FSE_isError() . - - ** Important ** : FSE_decompress() does not decompress non-compressible nor RLE data !!! - Why ? : making this distinction requires a header. - Header management is intentionally delegated to the user layer, which can better manage special cases. -*/ -FSE_PUBLIC_API size_t FSE_decompress(void* dst, size_t dstCapacity, - const void* cSrc, size_t cSrcSize); - - /*-***************************************** * Tool functions ******************************************/ @@ -91,20 +63,6 @@ FSE_PUBLIC_API unsigned FSE_isError(size_t code); /* tells if a return FSE_PUBLIC_API const char* FSE_getErrorName(size_t code); /* provides error code string (useful for debugging) */ -/*-***************************************** -* FSE advanced functions -******************************************/ -/*! FSE_compress2() : - Same as FSE_compress(), but allows the selection of 'maxSymbolValue' and 'tableLog' - Both parameters can be defined as '0' to mean : use default value - @return : size of compressed data - Special values : if return == 0, srcData is not compressible => Nothing is stored within cSrc !!! - if return == 1, srcData is a single byte symbol * srcSize times. Use RLE compression. - if FSE_isError(return), it's an error code. -*/ -FSE_PUBLIC_API size_t FSE_compress2 (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog); - - /*-***************************************** * FSE detailed API ******************************************/ @@ -164,8 +122,6 @@ FSE_PUBLIC_API size_t FSE_writeNCount (void* buffer, size_t bufferSize, /*! Constructor and Destructor of FSE_CTable. Note that FSE_CTable size depends on 'tableLog' and 'maxSymbolValue' */ typedef unsigned FSE_CTable; /* don't allocate that. It's only meant to be more restrictive than void* */ -FSE_PUBLIC_API FSE_CTable* FSE_createCTable (unsigned maxSymbolValue, unsigned tableLog); -FSE_PUBLIC_API void FSE_freeCTable (FSE_CTable* ct); /*! FSE_buildCTable(): Builds `ct`, which must be already allocated, using FSE_createCTable(). @@ -241,23 +197,7 @@ FSE_PUBLIC_API size_t FSE_readNCount_bmi2(short* normalizedCounter, unsigned* maxSymbolValuePtr, unsigned* tableLogPtr, const void* rBuffer, size_t rBuffSize, int bmi2); -/*! Constructor and Destructor of FSE_DTable. - Note that its size depends on 'tableLog' */ typedef unsigned FSE_DTable; /* don't allocate that. It's just a way to be more restrictive than void* */ -FSE_PUBLIC_API FSE_DTable* FSE_createDTable(unsigned tableLog); -FSE_PUBLIC_API void FSE_freeDTable(FSE_DTable* dt); - -/*! FSE_buildDTable(): - Builds 'dt', which must be already allocated, using FSE_createDTable(). - return : 0, or an errorCode, which can be tested using FSE_isError() */ -FSE_PUBLIC_API size_t FSE_buildDTable (FSE_DTable* dt, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog); - -/*! FSE_decompress_usingDTable(): - Decompress compressed source `cSrc` of size `cSrcSize` using `dt` - into `dst` which must be already allocated. - @return : size of regenerated data (necessarily <= `dstCapacity`), - or an errorCode, which can be tested using FSE_isError() */ -FSE_PUBLIC_API size_t FSE_decompress_usingDTable(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, const FSE_DTable* dt); /*! Tutorial : @@ -320,16 +260,6 @@ If there is an error, the function will return an error code, which can be teste unsigned FSE_optimalTableLog_internal(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue, unsigned minus); /**< same as FSE_optimalTableLog(), which used `minus==2` */ -/* FSE_compress_wksp() : - * Same as FSE_compress2(), but using an externally allocated scratch buffer (`workSpace`). - * FSE_COMPRESS_WKSP_SIZE_U32() provides the minimum size required for `workSpace` as a table of FSE_CTable. - */ -#define FSE_COMPRESS_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) ( FSE_CTABLE_SIZE_U32(maxTableLog, maxSymbolValue) + ((maxTableLog > 12) ? (1 << (maxTableLog - 2)) : 1024) ) -size_t FSE_compress_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize); - -size_t FSE_buildCTable_raw (FSE_CTable* ct, unsigned nbBits); -/**< build a fake FSE_CTable, designed for a flat distribution, where each symbol uses nbBits */ - size_t FSE_buildCTable_rle (FSE_CTable* ct, unsigned char symbolValue); /**< build a fake FSE_CTable, designed to compress always the same symbolValue */ @@ -347,19 +277,11 @@ size_t FSE_buildCTable_wksp(FSE_CTable* ct, const short* normalizedCounter, unsi FSE_PUBLIC_API size_t FSE_buildDTable_wksp(FSE_DTable* dt, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize); /**< Same as FSE_buildDTable(), using an externally allocated `workspace` produced with `FSE_BUILD_DTABLE_WKSP_SIZE_U32(maxSymbolValue)` */ -size_t FSE_buildDTable_raw (FSE_DTable* dt, unsigned nbBits); -/**< build a fake FSE_DTable, designed to read a flat distribution where each symbol uses nbBits */ - -size_t FSE_buildDTable_rle (FSE_DTable* dt, unsigned char symbolValue); -/**< build a fake FSE_DTable, designed to always generate the same symbolValue */ - -#define FSE_DECOMPRESS_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) (FSE_DTABLE_SIZE_U32(maxTableLog) + FSE_BUILD_DTABLE_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) + (FSE_MAX_SYMBOL_VALUE + 1) / 2 + 1) +#define FSE_DECOMPRESS_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) (FSE_DTABLE_SIZE_U32(maxTableLog) + 1 + FSE_BUILD_DTABLE_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) + (FSE_MAX_SYMBOL_VALUE + 1) / 2 + 1) #define FSE_DECOMPRESS_WKSP_SIZE(maxTableLog, maxSymbolValue) (FSE_DECOMPRESS_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) * sizeof(unsigned)) -size_t FSE_decompress_wksp(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, unsigned maxLog, void* workSpace, size_t wkspSize); -/**< same as FSE_decompress(), using an externally allocated `workSpace` produced with `FSE_DECOMPRESS_WKSP_SIZE_U32(maxLog, maxSymbolValue)` */ - size_t FSE_decompress_wksp_bmi2(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, unsigned maxLog, void* workSpace, size_t wkspSize, int bmi2); -/**< Same as FSE_decompress_wksp() but with dynamic BMI2 support. Pass 1 if your CPU supports BMI2 or 0 if it doesn't. */ +/**< same as FSE_decompress(), using an externally allocated `workSpace` produced with `FSE_DECOMPRESS_WKSP_SIZE_U32(maxLog, maxSymbolValue)`. + * Set bmi2 to 1 if your CPU supports BMI2 or 0 if it doesn't */ typedef enum { FSE_repeat_none, /**< Cannot use the previous table */ @@ -555,7 +477,7 @@ MEM_STATIC void FSE_flushCState(BIT_CStream_t* bitC, const FSE_CState_t* statePt /* FSE_getMaxNbBits() : * Approximate maximum cost of a symbol, in bits. - * Fractional get rounded up (i.e : a symbol with a normalized frequency of 3 gives the same result as a frequency of 2) + * Fractional get rounded up (i.e. a symbol with a normalized frequency of 3 gives the same result as a frequency of 2) * note 1 : assume symbolValue is valid (<= maxSymbolValue) * note 2 : if freq[symbolValue]==0, @return a fake cost of tableLog+1 bits */ MEM_STATIC U32 FSE_getMaxNbBits(const void* symbolTTPtr, U32 symbolValue) diff --git a/internal-complibs/zstd-1.5.2/common/fse_decompress.c b/internal-complibs/zstd-1.5.5/common/fse_decompress.c similarity index 78% rename from internal-complibs/zstd-1.5.2/common/fse_decompress.c rename to internal-complibs/zstd-1.5.5/common/fse_decompress.c index a5a35801..1e1c9f92 100644 --- a/internal-complibs/zstd-1.5.2/common/fse_decompress.c +++ b/internal-complibs/zstd-1.5.5/common/fse_decompress.c @@ -1,6 +1,6 @@ /* ****************************************************************** * FSE : Finite State Entropy decoder - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : * - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy @@ -24,6 +24,7 @@ #include "error_private.h" #define ZSTD_DEPS_NEED_MALLOC #include "zstd_deps.h" +#include "bits.h" /* ZSTD_highbit32 */ /* ************************************************************** @@ -55,19 +56,6 @@ #define FSE_FUNCTION_NAME(X,Y) FSE_CAT(X,Y) #define FSE_TYPE_NAME(X,Y) FSE_CAT(X,Y) - -/* Function templates */ -FSE_DTable* FSE_createDTable (unsigned tableLog) -{ - if (tableLog > FSE_TABLELOG_ABSOLUTE_MAX) tableLog = FSE_TABLELOG_ABSOLUTE_MAX; - return (FSE_DTable*)ZSTD_malloc( FSE_DTABLE_SIZE_U32(tableLog) * sizeof (U32) ); -} - -void FSE_freeDTable (FSE_DTable* dt) -{ - ZSTD_free(dt); -} - static size_t FSE_buildDTable_internal(FSE_DTable* dt, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize) { void* const tdPtr = dt+1; /* because *dt is unsigned, 32-bits aligned on 32-bits */ @@ -127,10 +115,10 @@ static size_t FSE_buildDTable_internal(FSE_DTable* dt, const short* normalizedCo } } /* Now we spread those positions across the table. - * The benefit of doing it in two stages is that we avoid the the + * The benefit of doing it in two stages is that we avoid the * variable size inner loop, which caused lots of branch misses. * Now we can run through all the positions without any branch misses. - * We unroll the loop twice, since that is what emperically worked best. + * We unroll the loop twice, since that is what empirically worked best. */ { size_t position = 0; @@ -166,7 +154,7 @@ static size_t FSE_buildDTable_internal(FSE_DTable* dt, const short* normalizedCo for (u=0; utableLog = 0; - DTableH->fastMode = 0; - - cell->newState = 0; - cell->symbol = symbolValue; - cell->nbBits = 0; - - return 0; -} - - -size_t FSE_buildDTable_raw (FSE_DTable* dt, unsigned nbBits) -{ - void* ptr = dt; - FSE_DTableHeader* const DTableH = (FSE_DTableHeader*)ptr; - void* dPtr = dt + 1; - FSE_decode_t* const dinfo = (FSE_decode_t*)dPtr; - const unsigned tableSize = 1 << nbBits; - const unsigned tableMask = tableSize - 1; - const unsigned maxSV1 = tableMask+1; - unsigned s; - - /* Sanity checks */ - if (nbBits < 1) return ERROR(GENERIC); /* min size */ - - /* Build Decoding Table */ - DTableH->tableLog = (U16)nbBits; - DTableH->fastMode = 1; - for (s=0; sfastMode; - - /* select fast mode (static) */ - if (fastMode) return FSE_decompress_usingDTable_generic(dst, originalSize, cSrc, cSrcSize, dt, 1); - return FSE_decompress_usingDTable_generic(dst, originalSize, cSrc, cSrcSize, dt, 0); -} - - -size_t FSE_decompress_wksp(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, unsigned maxLog, void* workSpace, size_t wkspSize) -{ - return FSE_decompress_wksp_bmi2(dst, dstCapacity, cSrc, cSrcSize, maxLog, workSpace, wkspSize, /* bmi2 */ 0); -} - typedef struct { short ncount[FSE_MAX_SYMBOL_VALUE + 1]; FSE_DTable dtable[1]; /* Dynamically sized */ @@ -342,7 +267,8 @@ FORCE_INLINE_TEMPLATE size_t FSE_decompress_wksp_body( } if (FSE_DECOMPRESS_WKSP_SIZE(tableLog, maxSymbolValue) > wkspSize) return ERROR(tableLog_tooLarge); - workSpace = wksp->dtable + FSE_DTABLE_SIZE_U32(tableLog); + assert(sizeof(*wksp) + FSE_DTABLE_SIZE(tableLog) <= wkspSize); + workSpace = (BYTE*)workSpace + sizeof(*wksp) + FSE_DTABLE_SIZE(tableLog); wkspSize -= sizeof(*wksp) + FSE_DTABLE_SIZE(tableLog); CHECK_F( FSE_buildDTable_internal(wksp->dtable, wksp->ncount, maxSymbolValue, tableLog, workSpace, wkspSize) ); @@ -382,22 +308,4 @@ size_t FSE_decompress_wksp_bmi2(void* dst, size_t dstCapacity, const void* cSrc, return FSE_decompress_wksp_body_default(dst, dstCapacity, cSrc, cSrcSize, maxLog, workSpace, wkspSize); } - -typedef FSE_DTable DTable_max_t[FSE_DTABLE_SIZE_U32(FSE_MAX_TABLELOG)]; - -#ifndef ZSTD_NO_UNUSED_FUNCTIONS -size_t FSE_buildDTable(FSE_DTable* dt, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog) { - U32 wksp[FSE_BUILD_DTABLE_WKSP_SIZE_U32(FSE_TABLELOG_ABSOLUTE_MAX, FSE_MAX_SYMBOL_VALUE)]; - return FSE_buildDTable_wksp(dt, normalizedCounter, maxSymbolValue, tableLog, wksp, sizeof(wksp)); -} - -size_t FSE_decompress(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize) -{ - /* Static analyzer seems unable to understand this table will be properly initialized later */ - U32 wksp[FSE_DECOMPRESS_WKSP_SIZE_U32(FSE_MAX_TABLELOG, FSE_MAX_SYMBOL_VALUE)]; - return FSE_decompress_wksp(dst, dstCapacity, cSrc, cSrcSize, FSE_MAX_TABLELOG, wksp, sizeof(wksp)); -} -#endif - - #endif /* FSE_COMMONDEFS_ONLY */ diff --git a/internal-complibs/zstd-1.5.2/common/huf.h b/internal-complibs/zstd-1.5.5/common/huf.h similarity index 50% rename from internal-complibs/zstd-1.5.2/common/huf.h rename to internal-complibs/zstd-1.5.5/common/huf.h index 85518481..73d1ee56 100644 --- a/internal-complibs/zstd-1.5.2/common/huf.h +++ b/internal-complibs/zstd-1.5.5/common/huf.h @@ -1,7 +1,7 @@ /* ****************************************************************** * huff0 huffman codec, * part of Finite State Entropy library - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy @@ -21,99 +21,22 @@ extern "C" { /* *** Dependencies *** */ #include "zstd_deps.h" /* size_t */ - - -/* *** library symbols visibility *** */ -/* Note : when linking with -fvisibility=hidden on gcc, or by default on Visual, - * HUF symbols remain "private" (internal symbols for library only). - * Set macro FSE_DLL_EXPORT to 1 if you want HUF symbols visible on DLL interface */ -#if defined(FSE_DLL_EXPORT) && (FSE_DLL_EXPORT==1) && defined(__GNUC__) && (__GNUC__ >= 4) -# define HUF_PUBLIC_API __attribute__ ((visibility ("default"))) -#elif defined(FSE_DLL_EXPORT) && (FSE_DLL_EXPORT==1) /* Visual expected */ -# define HUF_PUBLIC_API __declspec(dllexport) -#elif defined(FSE_DLL_IMPORT) && (FSE_DLL_IMPORT==1) -# define HUF_PUBLIC_API __declspec(dllimport) /* not required, just to generate faster code (saves a function pointer load from IAT and an indirect jump) */ -#else -# define HUF_PUBLIC_API -#endif - - -/* ========================== */ -/* *** simple functions *** */ -/* ========================== */ - -/** HUF_compress() : - * Compress content from buffer 'src', of size 'srcSize', into buffer 'dst'. - * 'dst' buffer must be already allocated. - * Compression runs faster if `dstCapacity` >= HUF_compressBound(srcSize). - * `srcSize` must be <= `HUF_BLOCKSIZE_MAX` == 128 KB. - * @return : size of compressed data (<= `dstCapacity`). - * Special values : if return == 0, srcData is not compressible => Nothing is stored within dst !!! - * if HUF_isError(return), compression failed (more details using HUF_getErrorName()) - */ -HUF_PUBLIC_API size_t HUF_compress(void* dst, size_t dstCapacity, - const void* src, size_t srcSize); - -/** HUF_decompress() : - * Decompress HUF data from buffer 'cSrc', of size 'cSrcSize', - * into already allocated buffer 'dst', of minimum size 'dstSize'. - * `originalSize` : **must** be the ***exact*** size of original (uncompressed) data. - * Note : in contrast with FSE, HUF_decompress can regenerate - * RLE (cSrcSize==1) and uncompressed (cSrcSize==dstSize) data, - * because it knows size to regenerate (originalSize). - * @return : size of regenerated data (== originalSize), - * or an error code, which can be tested using HUF_isError() - */ -HUF_PUBLIC_API size_t HUF_decompress(void* dst, size_t originalSize, - const void* cSrc, size_t cSrcSize); +#include "mem.h" /* U32 */ +#define FSE_STATIC_LINKING_ONLY +#include "fse.h" /* *** Tool functions *** */ -#define HUF_BLOCKSIZE_MAX (128 * 1024) /**< maximum input size for a single block compressed with HUF_compress */ -HUF_PUBLIC_API size_t HUF_compressBound(size_t size); /**< maximum compressed size (worst case) */ +#define HUF_BLOCKSIZE_MAX (128 * 1024) /**< maximum input size for a single block compressed with HUF_compress */ +size_t HUF_compressBound(size_t size); /**< maximum compressed size (worst case) */ /* Error Management */ -HUF_PUBLIC_API unsigned HUF_isError(size_t code); /**< tells if a return value is an error code */ -HUF_PUBLIC_API const char* HUF_getErrorName(size_t code); /**< provides error code string (useful for debugging) */ +unsigned HUF_isError(size_t code); /**< tells if a return value is an error code */ +const char* HUF_getErrorName(size_t code); /**< provides error code string (useful for debugging) */ -/* *** Advanced function *** */ - -/** HUF_compress2() : - * Same as HUF_compress(), but offers control over `maxSymbolValue` and `tableLog`. - * `maxSymbolValue` must be <= HUF_SYMBOLVALUE_MAX . - * `tableLog` must be `<= HUF_TABLELOG_MAX` . */ -HUF_PUBLIC_API size_t HUF_compress2 (void* dst, size_t dstCapacity, - const void* src, size_t srcSize, - unsigned maxSymbolValue, unsigned tableLog); - -/** HUF_compress4X_wksp() : - * Same as HUF_compress2(), but uses externally allocated `workSpace`. - * `workspace` must be at least as large as HUF_WORKSPACE_SIZE */ #define HUF_WORKSPACE_SIZE ((8 << 10) + 512 /* sorting scratch space */) #define HUF_WORKSPACE_SIZE_U64 (HUF_WORKSPACE_SIZE / sizeof(U64)) -HUF_PUBLIC_API size_t HUF_compress4X_wksp (void* dst, size_t dstCapacity, - const void* src, size_t srcSize, - unsigned maxSymbolValue, unsigned tableLog, - void* workSpace, size_t wkspSize); - -#endif /* HUF_H_298734234 */ - -/* ****************************************************************** - * WARNING !! - * The following section contains advanced and experimental definitions - * which shall never be used in the context of a dynamic library, - * because they are not guaranteed to remain stable in the future. - * Only consider them in association with static linking. - * *****************************************************************/ -#if defined(HUF_STATIC_LINKING_ONLY) && !defined(HUF_H_HUF_STATIC_LINKING_ONLY) -#define HUF_H_HUF_STATIC_LINKING_ONLY - -/* *** Dependencies *** */ -#include "mem.h" /* U32 */ -#define FSE_STATIC_LINKING_ONLY -#include "fse.h" - /* *** Constants *** */ #define HUF_TABLELOG_MAX 12 /* max runtime value of tableLog (due to static allocation); can be modified up to HUF_TABLELOG_ABSOLUTEMAX */ @@ -154,25 +77,49 @@ typedef U32 HUF_DTable; /* **************************************** * Advanced decompression functions ******************************************/ -size_t HUF_decompress4X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< single-symbol decoder */ -#ifndef HUF_FORCE_DECOMPRESS_X1 -size_t HUF_decompress4X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< double-symbols decoder */ -#endif -size_t HUF_decompress4X_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< decodes RLE and uncompressed */ -size_t HUF_decompress4X_hufOnly(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< considers RLE and uncompressed as errors */ -size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< considers RLE and uncompressed as errors */ -size_t HUF_decompress4X1_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< single-symbol decoder */ -size_t HUF_decompress4X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< single-symbol decoder */ -#ifndef HUF_FORCE_DECOMPRESS_X1 -size_t HUF_decompress4X2_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< double-symbols decoder */ -size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< double-symbols decoder */ -#endif +/** + * Huffman flags bitset. + * For all flags, 0 is the default value. + */ +typedef enum { + /** + * If compiled with DYNAMIC_BMI2: Set flag only if the CPU supports BMI2 at runtime. + * Otherwise: Ignored. + */ + HUF_flags_bmi2 = (1 << 0), + /** + * If set: Test possible table depths to find the one that produces the smallest header + encoded size. + * If unset: Use heuristic to find the table depth. + */ + HUF_flags_optimalDepth = (1 << 1), + /** + * If set: If the previous table can encode the input, always reuse the previous table. + * If unset: If the previous table can encode the input, reuse the previous table if it results in a smaller output. + */ + HUF_flags_preferRepeat = (1 << 2), + /** + * If set: Sample the input and check if the sample is uncompressible, if it is then don't attempt to compress. + * If unset: Always histogram the entire input. + */ + HUF_flags_suspectUncompressible = (1 << 3), + /** + * If set: Don't use assembly implementations + * If unset: Allow using assembly implementations + */ + HUF_flags_disableAsm = (1 << 4), + /** + * If set: Don't use the fast decoding loop, always use the fallback decoding loop. + * If unset: Use the fast decoding loop when possible. + */ + HUF_flags_disableFast = (1 << 5) +} HUF_flags_e; /* **************************************** * HUF detailed API * ****************************************/ +#define HUF_OPTIMAL_DEPTH_THRESHOLD ZSTD_btultra /*! HUF_compress() does the following: * 1. count symbol occurrence from source[] into table count[] using FSE_count() (exposed within "fse.h") @@ -185,12 +132,12 @@ size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, * For example, it's possible to compress several blocks using the same 'CTable', * or to save and regenerate 'CTable' using external methods. */ -unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue); -size_t HUF_buildCTable (HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue, unsigned maxNbBits); /* @return : maxNbBits; CTable and count can overlap. In which case, CTable will overwrite count content */ -size_t HUF_writeCTable (void* dst, size_t maxDstSize, const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog); +unsigned HUF_minTableLog(unsigned symbolCardinality); +unsigned HUF_cardinality(const unsigned* count, unsigned maxSymbolValue); +unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue, void* workSpace, + size_t wkspSize, HUF_CElt* table, const unsigned* count, int flags); /* table is used as scratch space for building and testing tables, not a return value */ size_t HUF_writeCTable_wksp(void* dst, size_t maxDstSize, const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog, void* workspace, size_t workspaceSize); -size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable); -size_t HUF_compress4X_usingCTable_bmi2(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable, int bmi2); +size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable, int flags); size_t HUF_estimateCompressedSize(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue); int HUF_validateCTable(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue); @@ -199,6 +146,7 @@ typedef enum { HUF_repeat_check, /**< Can use the previous table but it must be checked. Note : The previous table must have been constructed by HUF_compress{1, 4}X_repeat */ HUF_repeat_valid /**< Can use the previous table and it is assumed to be valid */ } HUF_repeat; + /** HUF_compress4X_repeat() : * Same as HUF_compress4X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none. * If it uses hufTable it does not modify hufTable or repeat. @@ -209,13 +157,13 @@ size_t HUF_compress4X_repeat(void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, /**< `workSpace` must be aligned on 4-bytes boundaries, `wkspSize` must be >= HUF_WORKSPACE_SIZE */ - HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2, unsigned suspectUncompressible); + HUF_CElt* hufTable, HUF_repeat* repeat, int flags); /** HUF_buildCTable_wksp() : * Same as HUF_buildCTable(), but using externally allocated scratch buffer. * `workSpace` must be aligned on 4-bytes boundaries, and its size must be >= HUF_CTABLE_WORKSPACE_SIZE. */ -#define HUF_CTABLE_WORKSPACE_SIZE_U32 (2*HUF_SYMBOLVALUE_MAX +1 +1) +#define HUF_CTABLE_WORKSPACE_SIZE_U32 ((4 * (HUF_SYMBOLVALUE_MAX + 1)) + 192) #define HUF_CTABLE_WORKSPACE_SIZE (HUF_CTABLE_WORKSPACE_SIZE_U32 * sizeof(unsigned)) size_t HUF_buildCTable_wksp (HUF_CElt* tree, const unsigned* count, U32 maxSymbolValue, U32 maxNbBits, @@ -241,7 +189,7 @@ size_t HUF_readStats_wksp(BYTE* huffWeight, size_t hwSize, U32* rankStats, U32* nbSymbolsPtr, U32* tableLogPtr, const void* src, size_t srcSize, void* workspace, size_t wkspSize, - int bmi2); + int flags); /** HUF_readCTable() : * Loading a CTable saved with HUF_writeCTable() */ @@ -279,32 +227,12 @@ U32 HUF_selectDecoder (size_t dstSize, size_t cSrcSize); #define HUF_DECOMPRESS_WORKSPACE_SIZE ((2 << 10) + (1 << 9)) #define HUF_DECOMPRESS_WORKSPACE_SIZE_U32 (HUF_DECOMPRESS_WORKSPACE_SIZE / sizeof(U32)) -#ifndef HUF_FORCE_DECOMPRESS_X2 -size_t HUF_readDTableX1 (HUF_DTable* DTable, const void* src, size_t srcSize); -size_t HUF_readDTableX1_wksp (HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize); -#endif -#ifndef HUF_FORCE_DECOMPRESS_X1 -size_t HUF_readDTableX2 (HUF_DTable* DTable, const void* src, size_t srcSize); -size_t HUF_readDTableX2_wksp (HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize); -#endif - -size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); -#ifndef HUF_FORCE_DECOMPRESS_X2 -size_t HUF_decompress4X1_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); -#endif -#ifndef HUF_FORCE_DECOMPRESS_X1 -size_t HUF_decompress4X2_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); -#endif - /* ====================== */ /* single stream variants */ /* ====================== */ -size_t HUF_compress1X (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog); -size_t HUF_compress1X_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize); /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U64 U64 */ -size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable); -size_t HUF_compress1X_usingCTable_bmi2(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable, int bmi2); +size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable, int flags); /** HUF_compress1X_repeat() : * Same as HUF_compress1X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none. * If it uses hufTable it does not modify hufTable or repeat. @@ -315,49 +243,30 @@ size_t HUF_compress1X_repeat(void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, /**< `workSpace` must be aligned on 4-bytes boundaries, `wkspSize` must be >= HUF_WORKSPACE_SIZE */ - HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2, unsigned suspectUncompressible); - -size_t HUF_decompress1X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* single-symbol decoder */ -#ifndef HUF_FORCE_DECOMPRESS_X1 -size_t HUF_decompress1X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* double-symbol decoder */ -#endif - -size_t HUF_decompress1X_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); -size_t HUF_decompress1X_DCtx_wksp (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); -#ifndef HUF_FORCE_DECOMPRESS_X2 -size_t HUF_decompress1X1_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< single-symbol decoder */ -size_t HUF_decompress1X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< single-symbol decoder */ -#endif -#ifndef HUF_FORCE_DECOMPRESS_X1 -size_t HUF_decompress1X2_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< double-symbols decoder */ -size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< double-symbols decoder */ -#endif + HUF_CElt* hufTable, HUF_repeat* repeat, int flags); -size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); /**< automatic selection of sing or double symbol decoder, based on DTable */ -#ifndef HUF_FORCE_DECOMPRESS_X2 -size_t HUF_decompress1X1_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); -#endif +size_t HUF_decompress1X_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int flags); #ifndef HUF_FORCE_DECOMPRESS_X1 -size_t HUF_decompress1X2_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); +size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int flags); /**< double-symbols decoder */ #endif /* BMI2 variants. * If the CPU has BMI2 support, pass bmi2=1, otherwise pass bmi2=0. */ -size_t HUF_decompress1X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2); +size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int flags); #ifndef HUF_FORCE_DECOMPRESS_X2 -size_t HUF_decompress1X1_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2); +size_t HUF_decompress1X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int flags); #endif -size_t HUF_decompress4X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2); -size_t HUF_decompress4X_hufOnly_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2); +size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int flags); +size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int flags); #ifndef HUF_FORCE_DECOMPRESS_X2 -size_t HUF_readDTableX1_wksp_bmi2(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize, int bmi2); +size_t HUF_readDTableX1_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize, int flags); #endif #ifndef HUF_FORCE_DECOMPRESS_X1 -size_t HUF_readDTableX2_wksp_bmi2(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize, int bmi2); +size_t HUF_readDTableX2_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize, int flags); #endif -#endif /* HUF_STATIC_LINKING_ONLY */ +#endif /* HUF_H_298734234 */ #if defined (__cplusplus) } diff --git a/internal-complibs/zstd-1.5.2/common/mem.h b/internal-complibs/zstd-1.5.5/common/mem.h similarity index 83% rename from internal-complibs/zstd-1.5.2/common/mem.h rename to internal-complibs/zstd-1.5.5/common/mem.h index 85581c38..98dd47a0 100644 --- a/internal-complibs/zstd-1.5.2/common/mem.h +++ b/internal-complibs/zstd-1.5.5/common/mem.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -133,21 +133,15 @@ MEM_STATIC size_t MEM_swapST(size_t in); /*-************************************************************** * Memory I/O Implementation *****************************************************************/ -/* MEM_FORCE_MEMORY_ACCESS : - * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. - * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. - * The below switch allow to select different access method for improved performance. - * Method 0 (default) : use `memcpy()`. Safe and portable. - * Method 1 : `__packed` statement. It depends on compiler extension (i.e., not portable). - * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. +/* MEM_FORCE_MEMORY_ACCESS : For accessing unaligned memory: + * Method 0 : always use `memcpy()`. Safe and portable. + * Method 1 : Use compiler extension to set unaligned access. * Method 2 : direct access. This method is portable but violate C standard. * It can generate buggy code on targets depending on alignment. - * In some circumstances, it's the only known way to get the most performance (i.e. GCC + ARMv6) - * See http://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details. - * Prefer these methods in priority order (0 > 1 > 2) + * Default : method 1 if supported, else method 0 */ #ifndef MEM_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ -# if defined(__INTEL_COMPILER) || defined(__GNUC__) || defined(__ICCARM__) +# ifdef __GNUC__ # define MEM_FORCE_MEMORY_ACCESS 1 # endif #endif @@ -190,30 +184,19 @@ MEM_STATIC void MEM_write64(void* memPtr, U64 value) { *(U64*)memPtr = value; } #elif defined(MEM_FORCE_MEMORY_ACCESS) && (MEM_FORCE_MEMORY_ACCESS==1) -/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ -/* currently only defined for gcc and icc */ -#if defined(_MSC_VER) || (defined(__INTEL_COMPILER) && defined(WIN32)) - __pragma( pack(push, 1) ) - typedef struct { U16 v; } unalign16; - typedef struct { U32 v; } unalign32; - typedef struct { U64 v; } unalign64; - typedef struct { size_t v; } unalignArch; - __pragma( pack(pop) ) -#else - typedef struct { U16 v; } __attribute__((packed)) unalign16; - typedef struct { U32 v; } __attribute__((packed)) unalign32; - typedef struct { U64 v; } __attribute__((packed)) unalign64; - typedef struct { size_t v; } __attribute__((packed)) unalignArch; -#endif +typedef __attribute__((aligned(1))) U16 unalign16; +typedef __attribute__((aligned(1))) U32 unalign32; +typedef __attribute__((aligned(1))) U64 unalign64; +typedef __attribute__((aligned(1))) size_t unalignArch; -MEM_STATIC U16 MEM_read16(const void* ptr) { return ((const unalign16*)ptr)->v; } -MEM_STATIC U32 MEM_read32(const void* ptr) { return ((const unalign32*)ptr)->v; } -MEM_STATIC U64 MEM_read64(const void* ptr) { return ((const unalign64*)ptr)->v; } -MEM_STATIC size_t MEM_readST(const void* ptr) { return ((const unalignArch*)ptr)->v; } +MEM_STATIC U16 MEM_read16(const void* ptr) { return *(const unalign16*)ptr; } +MEM_STATIC U32 MEM_read32(const void* ptr) { return *(const unalign32*)ptr; } +MEM_STATIC U64 MEM_read64(const void* ptr) { return *(const unalign64*)ptr; } +MEM_STATIC size_t MEM_readST(const void* ptr) { return *(const unalignArch*)ptr; } -MEM_STATIC void MEM_write16(void* memPtr, U16 value) { ((unalign16*)memPtr)->v = value; } -MEM_STATIC void MEM_write32(void* memPtr, U32 value) { ((unalign32*)memPtr)->v = value; } -MEM_STATIC void MEM_write64(void* memPtr, U64 value) { ((unalign64*)memPtr)->v = value; } +MEM_STATIC void MEM_write16(void* memPtr, U16 value) { *(unalign16*)memPtr = value; } +MEM_STATIC void MEM_write32(void* memPtr, U32 value) { *(unalign32*)memPtr = value; } +MEM_STATIC void MEM_write64(void* memPtr, U64 value) { *(unalign64*)memPtr = value; } #else @@ -257,6 +240,14 @@ MEM_STATIC void MEM_write64(void* memPtr, U64 value) #endif /* MEM_FORCE_MEMORY_ACCESS */ +MEM_STATIC U32 MEM_swap32_fallback(U32 in) +{ + return ((in << 24) & 0xff000000 ) | + ((in << 8) & 0x00ff0000 ) | + ((in >> 8) & 0x0000ff00 ) | + ((in >> 24) & 0x000000ff ); +} + MEM_STATIC U32 MEM_swap32(U32 in) { #if defined(_MSC_VER) /* Visual Studio */ @@ -265,22 +256,13 @@ MEM_STATIC U32 MEM_swap32(U32 in) || (defined(__clang__) && __has_builtin(__builtin_bswap32)) return __builtin_bswap32(in); #else - return ((in << 24) & 0xff000000 ) | - ((in << 8) & 0x00ff0000 ) | - ((in >> 8) & 0x0000ff00 ) | - ((in >> 24) & 0x000000ff ); + return MEM_swap32_fallback(in); #endif } -MEM_STATIC U64 MEM_swap64(U64 in) +MEM_STATIC U64 MEM_swap64_fallback(U64 in) { -#if defined(_MSC_VER) /* Visual Studio */ - return _byteswap_uint64(in); -#elif (defined (__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 403)) \ - || (defined(__clang__) && __has_builtin(__builtin_bswap64)) - return __builtin_bswap64(in); -#else - return ((in << 56) & 0xff00000000000000ULL) | + return ((in << 56) & 0xff00000000000000ULL) | ((in << 40) & 0x00ff000000000000ULL) | ((in << 24) & 0x0000ff0000000000ULL) | ((in << 8) & 0x000000ff00000000ULL) | @@ -288,6 +270,17 @@ MEM_STATIC U64 MEM_swap64(U64 in) ((in >> 24) & 0x0000000000ff0000ULL) | ((in >> 40) & 0x000000000000ff00ULL) | ((in >> 56) & 0x00000000000000ffULL); +} + +MEM_STATIC U64 MEM_swap64(U64 in) +{ +#if defined(_MSC_VER) /* Visual Studio */ + return _byteswap_uint64(in); +#elif (defined (__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 403)) \ + || (defined(__clang__) && __has_builtin(__builtin_bswap64)) + return __builtin_bswap64(in); +#else + return MEM_swap64_fallback(in); #endif } diff --git a/internal-complibs/zstd-1.5.2/common/pool.c b/internal-complibs/zstd-1.5.5/common/pool.c similarity index 91% rename from internal-complibs/zstd-1.5.2/common/pool.c rename to internal-complibs/zstd-1.5.5/common/pool.c index 2e37cdd7..d5ca5a78 100644 --- a/internal-complibs/zstd-1.5.2/common/pool.c +++ b/internal-complibs/zstd-1.5.5/common/pool.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -10,9 +10,9 @@ /* ====== Dependencies ======= */ +#include "../common/allocations.h" /* ZSTD_customCalloc, ZSTD_customFree */ #include "zstd_deps.h" /* size_t */ #include "debug.h" /* assert */ -#include "zstd_internal.h" /* ZSTD_customMalloc, ZSTD_customFree */ #include "pool.h" /* ====== Compiler specifics ====== */ @@ -96,9 +96,7 @@ static void* POOL_thread(void* opaque) { /* If the intended queue size was 0, signal after finishing job */ ZSTD_pthread_mutex_lock(&ctx->queueMutex); ctx->numThreadsBusy--; - if (ctx->queueSize == 1) { - ZSTD_pthread_cond_signal(&ctx->queuePushCond); - } + ZSTD_pthread_cond_signal(&ctx->queuePushCond); ZSTD_pthread_mutex_unlock(&ctx->queueMutex); } } /* for (;;) */ @@ -128,7 +126,7 @@ POOL_ctx* POOL_create_advanced(size_t numThreads, size_t queueSize, * empty and full queues. */ ctx->queueSize = queueSize + 1; - ctx->queue = (POOL_job*)ZSTD_customMalloc(ctx->queueSize * sizeof(POOL_job), customMem); + ctx->queue = (POOL_job*)ZSTD_customCalloc(ctx->queueSize * sizeof(POOL_job), customMem); ctx->queueHead = 0; ctx->queueTail = 0; ctx->numThreadsBusy = 0; @@ -142,7 +140,7 @@ POOL_ctx* POOL_create_advanced(size_t numThreads, size_t queueSize, } ctx->shutdown = 0; /* Allocate space for the thread handles */ - ctx->threads = (ZSTD_pthread_t*)ZSTD_customMalloc(numThreads * sizeof(ZSTD_pthread_t), customMem); + ctx->threads = (ZSTD_pthread_t*)ZSTD_customCalloc(numThreads * sizeof(ZSTD_pthread_t), customMem); ctx->threadCapacity = 0; ctx->customMem = customMem; /* Check for errors */ @@ -175,7 +173,7 @@ static void POOL_join(POOL_ctx* ctx) { /* Join all of the threads */ { size_t i; for (i = 0; i < ctx->threadCapacity; ++i) { - ZSTD_pthread_join(ctx->threads[i], NULL); /* note : could fail */ + ZSTD_pthread_join(ctx->threads[i]); /* note : could fail */ } } } @@ -190,6 +188,17 @@ void POOL_free(POOL_ctx *ctx) { ZSTD_customFree(ctx, ctx->customMem); } +/*! POOL_joinJobs() : + * Waits for all queued jobs to finish executing. + */ +void POOL_joinJobs(POOL_ctx* ctx) { + ZSTD_pthread_mutex_lock(&ctx->queueMutex); + while(!ctx->queueEmpty || ctx->numThreadsBusy > 0) { + ZSTD_pthread_cond_wait(&ctx->queuePushCond, &ctx->queueMutex); + } + ZSTD_pthread_mutex_unlock(&ctx->queueMutex); +} + void ZSTD_freeThreadPool (ZSTD_threadPool* pool) { POOL_free (pool); } @@ -211,7 +220,7 @@ static int POOL_resize_internal(POOL_ctx* ctx, size_t numThreads) return 0; } /* numThreads > threadCapacity */ - { ZSTD_pthread_t* const threadPool = (ZSTD_pthread_t*)ZSTD_customMalloc(numThreads * sizeof(ZSTD_pthread_t), ctx->customMem); + { ZSTD_pthread_t* const threadPool = (ZSTD_pthread_t*)ZSTD_customCalloc(numThreads * sizeof(ZSTD_pthread_t), ctx->customMem); if (!threadPool) return 1; /* replace existing thread pool */ ZSTD_memcpy(threadPool, ctx->threads, ctx->threadCapacity * sizeof(*threadPool)); @@ -262,7 +271,9 @@ static int isQueueFull(POOL_ctx const* ctx) { static void POOL_add_internal(POOL_ctx* ctx, POOL_function function, void *opaque) { - POOL_job const job = {function, opaque}; + POOL_job job; + job.function = function; + job.opaque = opaque; assert(ctx != NULL); if (ctx->shutdown) return; @@ -330,6 +341,11 @@ void POOL_free(POOL_ctx* ctx) { (void)ctx; } +void POOL_joinJobs(POOL_ctx* ctx){ + assert(!ctx || ctx == &g_poolCtx); + (void)ctx; +} + int POOL_resize(POOL_ctx* ctx, size_t numThreads) { (void)ctx; (void)numThreads; return 0; diff --git a/internal-complibs/zstd-1.5.2/common/pool.h b/internal-complibs/zstd-1.5.5/common/pool.h similarity index 93% rename from internal-complibs/zstd-1.5.2/common/pool.h rename to internal-complibs/zstd-1.5.5/common/pool.h index 0ebde180..eb22ff50 100644 --- a/internal-complibs/zstd-1.5.2/common/pool.h +++ b/internal-complibs/zstd-1.5.5/common/pool.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -38,6 +38,12 @@ POOL_ctx* POOL_create_advanced(size_t numThreads, size_t queueSize, */ void POOL_free(POOL_ctx* ctx); + +/*! POOL_joinJobs() : + * Waits for all queued jobs to finish executing. + */ +void POOL_joinJobs(POOL_ctx* ctx); + /*! POOL_resize() : * Expands or shrinks pool's number of threads. * This is more efficient than releasing + creating a new context, diff --git a/internal-complibs/zstd-1.5.2/common/portability_macros.h b/internal-complibs/zstd-1.5.5/common/portability_macros.h similarity index 84% rename from internal-complibs/zstd-1.5.2/common/portability_macros.h rename to internal-complibs/zstd-1.5.5/common/portability_macros.h index 2143817f..8fd6ea82 100644 --- a/internal-complibs/zstd-1.5.2/common/portability_macros.h +++ b/internal-complibs/zstd-1.5.5/common/portability_macros.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -12,7 +12,7 @@ #define ZSTD_PORTABILITY_MACROS_H /** - * This header file contains macro defintions to support portability. + * This header file contains macro definitions to support portability. * This header is shared between C and ASM code, so it MUST only * contain macro definitions. It MUST not contain any C code. * @@ -88,7 +88,7 @@ #endif /** - * Only enable assembly for GNUC comptabile compilers, + * Only enable assembly for GNUC compatible compilers, * because other platforms may not support GAS assembly syntax. * * Only enable assembly for Linux / MacOS, other platforms may @@ -134,4 +134,23 @@ # define ZSTD_ENABLE_ASM_X86_64_BMI2 0 #endif +/* + * For x86 ELF targets, add .note.gnu.property section for Intel CET in + * assembly sources when CET is enabled. + * + * Additionally, any function that may be called indirectly must begin + * with ZSTD_CET_ENDBRANCH. + */ +#if defined(__ELF__) && (defined(__x86_64__) || defined(__i386__)) \ + && defined(__has_include) +# if __has_include() +# include +# define ZSTD_CET_ENDBRANCH _CET_ENDBR +# endif +#endif + +#ifndef ZSTD_CET_ENDBRANCH +# define ZSTD_CET_ENDBRANCH +#endif + #endif /* ZSTD_PORTABILITY_MACROS_H */ diff --git a/internal-complibs/zstd-1.5.5/common/threading.c b/internal-complibs/zstd-1.5.5/common/threading.c new file mode 100644 index 00000000..ca155b9b --- /dev/null +++ b/internal-complibs/zstd-1.5.5/common/threading.c @@ -0,0 +1,176 @@ +/** + * Copyright (c) 2016 Tino Reichardt + * All rights reserved. + * + * You can contact the author at: + * - zstdmt source repository: https://github.com/mcmilk/zstdmt + * + * This source code is licensed under both the BSD-style license (found in the + * LICENSE file in the root directory of this source tree) and the GPLv2 (found + * in the COPYING file in the root directory of this source tree). + * You may select, at your option, one of the above-listed licenses. + */ + +/** + * This file will hold wrapper for systems, which do not support pthreads + */ + +#include "threading.h" + +/* create fake symbol to avoid empty translation unit warning */ +int g_ZSTD_threading_useless_symbol; + +#if defined(ZSTD_MULTITHREAD) && defined(_WIN32) + +/** + * Windows minimalist Pthread Wrapper + */ + + +/* === Dependencies === */ +#include +#include + + +/* === Implementation === */ + +typedef struct { + void* (*start_routine)(void*); + void* arg; + int initialized; + ZSTD_pthread_cond_t initialized_cond; + ZSTD_pthread_mutex_t initialized_mutex; +} ZSTD_thread_params_t; + +static unsigned __stdcall worker(void *arg) +{ + void* (*start_routine)(void*); + void* thread_arg; + + /* Initialized thread_arg and start_routine and signal main thread that we don't need it + * to wait any longer. + */ + { + ZSTD_thread_params_t* thread_param = (ZSTD_thread_params_t*)arg; + thread_arg = thread_param->arg; + start_routine = thread_param->start_routine; + + /* Signal main thread that we are running and do not depend on its memory anymore */ + ZSTD_pthread_mutex_lock(&thread_param->initialized_mutex); + thread_param->initialized = 1; + ZSTD_pthread_cond_signal(&thread_param->initialized_cond); + ZSTD_pthread_mutex_unlock(&thread_param->initialized_mutex); + } + + start_routine(thread_arg); + + return 0; +} + +int ZSTD_pthread_create(ZSTD_pthread_t* thread, const void* unused, + void* (*start_routine) (void*), void* arg) +{ + ZSTD_thread_params_t thread_param; + (void)unused; + + thread_param.start_routine = start_routine; + thread_param.arg = arg; + thread_param.initialized = 0; + *thread = NULL; + + /* Setup thread initialization synchronization */ + if(ZSTD_pthread_cond_init(&thread_param.initialized_cond, NULL)) { + /* Should never happen on Windows */ + return -1; + } + if(ZSTD_pthread_mutex_init(&thread_param.initialized_mutex, NULL)) { + /* Should never happen on Windows */ + ZSTD_pthread_cond_destroy(&thread_param.initialized_cond); + return -1; + } + + /* Spawn thread */ + *thread = (HANDLE)_beginthreadex(NULL, 0, worker, &thread_param, 0, NULL); + if (!thread) { + ZSTD_pthread_mutex_destroy(&thread_param.initialized_mutex); + ZSTD_pthread_cond_destroy(&thread_param.initialized_cond); + return errno; + } + + /* Wait for thread to be initialized */ + ZSTD_pthread_mutex_lock(&thread_param.initialized_mutex); + while(!thread_param.initialized) { + ZSTD_pthread_cond_wait(&thread_param.initialized_cond, &thread_param.initialized_mutex); + } + ZSTD_pthread_mutex_unlock(&thread_param.initialized_mutex); + ZSTD_pthread_mutex_destroy(&thread_param.initialized_mutex); + ZSTD_pthread_cond_destroy(&thread_param.initialized_cond); + + return 0; +} + +int ZSTD_pthread_join(ZSTD_pthread_t thread) +{ + DWORD result; + + if (!thread) return 0; + + result = WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + + switch (result) { + case WAIT_OBJECT_0: + return 0; + case WAIT_ABANDONED: + return EINVAL; + default: + return GetLastError(); + } +} + +#endif /* ZSTD_MULTITHREAD */ + +#if defined(ZSTD_MULTITHREAD) && DEBUGLEVEL >= 1 && !defined(_WIN32) + +#define ZSTD_DEPS_NEED_MALLOC +#include "zstd_deps.h" + +int ZSTD_pthread_mutex_init(ZSTD_pthread_mutex_t* mutex, pthread_mutexattr_t const* attr) +{ + *mutex = (pthread_mutex_t*)ZSTD_malloc(sizeof(pthread_mutex_t)); + if (!*mutex) + return 1; + return pthread_mutex_init(*mutex, attr); +} + +int ZSTD_pthread_mutex_destroy(ZSTD_pthread_mutex_t* mutex) +{ + if (!*mutex) + return 0; + { + int const ret = pthread_mutex_destroy(*mutex); + ZSTD_free(*mutex); + return ret; + } +} + +int ZSTD_pthread_cond_init(ZSTD_pthread_cond_t* cond, pthread_condattr_t const* attr) +{ + *cond = (pthread_cond_t*)ZSTD_malloc(sizeof(pthread_cond_t)); + if (!*cond) + return 1; + return pthread_cond_init(*cond, attr); +} + +int ZSTD_pthread_cond_destroy(ZSTD_pthread_cond_t* cond) +{ + if (!*cond) + return 0; + { + int const ret = pthread_cond_destroy(*cond); + ZSTD_free(*cond); + return ret; + } +} + +#endif diff --git a/internal-complibs/zstd-1.5.2/common/threading.h b/internal-complibs/zstd-1.5.5/common/threading.h similarity index 92% rename from internal-complibs/zstd-1.5.2/common/threading.h rename to internal-complibs/zstd-1.5.5/common/threading.h index fd0060d5..fb5c1c87 100644 --- a/internal-complibs/zstd-1.5.2/common/threading.h +++ b/internal-complibs/zstd-1.5.5/common/threading.h @@ -23,8 +23,7 @@ extern "C" { #if defined(ZSTD_MULTITHREAD) && defined(_WIN32) /** - * Windows minimalist Pthread Wrapper, based on : - * http://www.cse.wustl.edu/~schmidt/win32-cv-1.html + * Windows minimalist Pthread Wrapper */ #ifdef WINVER # undef WINVER @@ -62,16 +61,12 @@ extern "C" { #define ZSTD_pthread_cond_broadcast(a) WakeAllConditionVariable((a)) /* ZSTD_pthread_create() and ZSTD_pthread_join() */ -typedef struct { - HANDLE handle; - void* (*start_routine)(void*); - void* arg; -} ZSTD_pthread_t; +typedef HANDLE ZSTD_pthread_t; int ZSTD_pthread_create(ZSTD_pthread_t* thread, const void* unused, void* (*start_routine) (void*), void* arg); -int ZSTD_pthread_join(ZSTD_pthread_t thread, void** value_ptr); +int ZSTD_pthread_join(ZSTD_pthread_t thread); /** * add here more wrappers as required @@ -99,7 +94,7 @@ int ZSTD_pthread_join(ZSTD_pthread_t thread, void** value_ptr); #define ZSTD_pthread_t pthread_t #define ZSTD_pthread_create(a, b, c, d) pthread_create((a), (b), (c), (d)) -#define ZSTD_pthread_join(a, b) pthread_join((a),(b)) +#define ZSTD_pthread_join(a) pthread_join((a),NULL) #else /* DEBUGLEVEL >= 1 */ @@ -124,7 +119,7 @@ int ZSTD_pthread_cond_destroy(ZSTD_pthread_cond_t* cond); #define ZSTD_pthread_t pthread_t #define ZSTD_pthread_create(a, b, c, d) pthread_create((a), (b), (c), (d)) -#define ZSTD_pthread_join(a, b) pthread_join((a),(b)) +#define ZSTD_pthread_join(a) pthread_join((a),NULL) #endif diff --git a/internal-complibs/zstd-1.5.2/common/xxhash.c b/internal-complibs/zstd-1.5.5/common/xxhash.c similarity index 85% rename from internal-complibs/zstd-1.5.2/common/xxhash.c rename to internal-complibs/zstd-1.5.5/common/xxhash.c index d49497cf..fd237c90 100644 --- a/internal-complibs/zstd-1.5.2/common/xxhash.c +++ b/internal-complibs/zstd-1.5.5/common/xxhash.c @@ -1,9 +1,9 @@ /* * xxHash - Fast Hash algorithm - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : - * - xxHash homepage: http://www.xxhash.com + * - xxHash homepage: https://cyan4973.github.io/xxHash/ * - xxHash source repository : https://github.com/Cyan4973/xxHash * * This source code is licensed under both the BSD-style license (found in the diff --git a/internal-complibs/zstd-1.5.2/common/xxhash.h b/internal-complibs/zstd-1.5.5/common/xxhash.h similarity index 99% rename from internal-complibs/zstd-1.5.2/common/xxhash.h rename to internal-complibs/zstd-1.5.5/common/xxhash.h index 8ebbfdd6..b8b73290 100644 --- a/internal-complibs/zstd-1.5.2/common/xxhash.h +++ b/internal-complibs/zstd-1.5.5/common/xxhash.h @@ -1,9 +1,9 @@ /* * xxHash - Fast Hash algorithm - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : - * - xxHash homepage: http://www.xxhash.com + * - xxHash homepage: https://cyan4973.github.io/xxHash/ * - xxHash source repository : https://github.com/Cyan4973/xxHash * * This source code is licensed under both the BSD-style license (found in the @@ -1314,7 +1314,7 @@ XXH3_128bits_reset_withSecretandSeed(XXH3_state_t* statePtr, * care, as what works on one compiler/platform/optimization level may cause * another to read garbage data or even crash. * - * See http://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html for details. + * See https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html for details. * * Prefer these methods in priority order (0 > 3 > 1 > 2) */ @@ -1534,7 +1534,7 @@ static void* XXH_memcpy(void* dest, const void* src, size_t size) { return ZSTD_ * @brief Used to prevent unwanted optimizations for @p var. * * It uses an empty GCC inline assembly statement with a register constraint - * which forces @p var into a general purpose register (eg eax, ebx, ecx + * which forces @p var into a general purpose register (e.g. eax, ebx, ecx * on x86) and marks it as modified. * * This is used in a few places to avoid unwanted autovectorization (e.g. @@ -1655,7 +1655,7 @@ static xxh_u32 XXH_read32(const void* ptr) /* * Portable and safe solution. Generally efficient. - * see: http://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html + * see: https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html */ static xxh_u32 XXH_read32(const void* memPtr) { @@ -2296,7 +2296,7 @@ static xxh_u64 XXH_read64(const void* ptr) /* * Portable and safe solution. Generally efficient. - * see: http://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html + * see: https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html */ static xxh_u64 XXH_read64(const void* memPtr) { @@ -2809,7 +2809,7 @@ enum XXH_VECTOR_TYPE /* fake enum */ { * @ingroup tuning * @brief Selects the minimum alignment for XXH3's accumulators. * - * When using SIMD, this should match the alignment reqired for said vector + * When using SIMD, this should match the alignment required for said vector * type, so, for example, 32 for AVX2. * * Default: Auto detected. @@ -3026,7 +3026,7 @@ enum XXH_VECTOR_TYPE /* fake enum */ { * have more than 2 NEON (F0/F1) micro-ops. If you are only using NEON instructions, * you are only using 2/3 of the CPU bandwidth. * - * This is even more noticable on the more advanced cores like the A76 which + * This is even more noticeable on the more advanced cores like the A76 which * can dispatch 8 micro-ops per cycle, but still only 2 NEON micro-ops at once. * * Therefore, @ref XXH3_NEON_LANES lanes will be processed using NEON, and the diff --git a/internal-complibs/zstd-1.5.2/common/zstd_common.c b/internal-complibs/zstd-1.5.5/common/zstd_common.c similarity index 59% rename from internal-complibs/zstd-1.5.2/common/zstd_common.c rename to internal-complibs/zstd-1.5.5/common/zstd_common.c index 3d7e35b3..3f04c22a 100644 --- a/internal-complibs/zstd-1.5.2/common/zstd_common.c +++ b/internal-complibs/zstd-1.5.5/common/zstd_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -14,7 +14,6 @@ * Dependencies ***************************************/ #define ZSTD_DEPS_NEED_MALLOC -#include "zstd_deps.h" /* ZSTD_malloc, ZSTD_calloc, ZSTD_free, ZSTD_memset */ #include "error_private.h" #include "zstd_internal.h" @@ -47,37 +46,3 @@ ZSTD_ErrorCode ZSTD_getErrorCode(size_t code) { return ERR_getErrorCode(code); } /*! ZSTD_getErrorString() : * provides error code string from enum */ const char* ZSTD_getErrorString(ZSTD_ErrorCode code) { return ERR_getErrorString(code); } - - - -/*=************************************************************** -* Custom allocator -****************************************************************/ -void* ZSTD_customMalloc(size_t size, ZSTD_customMem customMem) -{ - if (customMem.customAlloc) - return customMem.customAlloc(customMem.opaque, size); - return ZSTD_malloc(size); -} - -void* ZSTD_customCalloc(size_t size, ZSTD_customMem customMem) -{ - if (customMem.customAlloc) { - /* calloc implemented as malloc+memset; - * not as efficient as calloc, but next best guess for custom malloc */ - void* const ptr = customMem.customAlloc(customMem.opaque, size); - ZSTD_memset(ptr, 0, size); - return ptr; - } - return ZSTD_calloc(1, size); -} - -void ZSTD_customFree(void* ptr, ZSTD_customMem customMem) -{ - if (ptr!=NULL) { - if (customMem.customFree) - customMem.customFree(customMem.opaque, ptr); - else - ZSTD_free(ptr); - } -} diff --git a/internal-complibs/zstd-1.5.2/common/zstd_deps.h b/internal-complibs/zstd-1.5.5/common/zstd_deps.h similarity index 97% rename from internal-complibs/zstd-1.5.2/common/zstd_deps.h rename to internal-complibs/zstd-1.5.5/common/zstd_deps.h index 14211344..4d767ae9 100644 --- a/internal-complibs/zstd-1.5.2/common/zstd_deps.h +++ b/internal-complibs/zstd-1.5.5/common/zstd_deps.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the diff --git a/internal-complibs/zstd-1.5.2/common/zstd_internal.h b/internal-complibs/zstd-1.5.5/common/zstd_internal.h similarity index 72% rename from internal-complibs/zstd-1.5.2/common/zstd_internal.h rename to internal-complibs/zstd-1.5.5/common/zstd_internal.h index e4d36ce0..1f942f27 100644 --- a/internal-complibs/zstd-1.5.2/common/zstd_internal.h +++ b/internal-complibs/zstd-1.5.5/common/zstd_internal.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -28,7 +28,6 @@ #include "../zstd.h" #define FSE_STATIC_LINKING_ONLY #include "fse.h" -#define HUF_STATIC_LINKING_ONLY #include "huf.h" #ifndef XXH_STATIC_LINKING_ONLY # define XXH_STATIC_LINKING_ONLY /* XXH64_state_t */ @@ -93,9 +92,9 @@ typedef enum { bt_raw, bt_rle, bt_compressed, bt_reserved } blockType_e; #define ZSTD_FRAMECHECKSUMSIZE 4 #define MIN_SEQUENCES_SIZE 1 /* nbSeq==0 */ -#define MIN_CBLOCK_SIZE (1 /*litCSize*/ + 1 /* RLE or RAW */ + MIN_SEQUENCES_SIZE /* nbSeq==0 */) /* for a non-null block */ +#define MIN_CBLOCK_SIZE (1 /*litCSize*/ + 1 /* RLE or RAW */) /* for a non-null block */ +#define MIN_LITERALS_FOR_4_STREAMS 6 -#define HufLog 12 typedef enum { set_basic, set_rle, set_compressed, set_repeat } symbolEncodingType_e; #define LONGNBSEQ 0x7F00 @@ -103,6 +102,7 @@ typedef enum { set_basic, set_rle, set_compressed, set_repeat } symbolEncodingTy #define MINMATCH 3 #define Litbits 8 +#define LitHufLog 11 #define MaxLit ((1<= length) return; op += 16; @@ -250,7 +246,6 @@ void ZSTD_wildcopy(void* dst, const void* src, ptrdiff_t length, ZSTD_overlap_e COPY16(op, ip); } while (op < oend); -#endif } } @@ -299,11 +294,11 @@ typedef enum { typedef struct { seqDef* sequencesStart; seqDef* sequences; /* ptr to end of sequences */ - BYTE* litStart; - BYTE* lit; /* ptr to end of literals */ - BYTE* llCode; - BYTE* mlCode; - BYTE* ofCode; + BYTE* litStart; + BYTE* lit; /* ptr to end of literals */ + BYTE* llCode; + BYTE* mlCode; + BYTE* ofCode; size_t maxNbSeq; size_t maxNbLit; @@ -311,8 +306,8 @@ typedef struct { * in the seqStore that has a value larger than U16 (if it exists). To do so, we increment * the existing value of the litLength or matchLength by 0x10000. */ - ZSTD_longLengthType_e longLengthType; - U32 longLengthPos; /* Index of the sequence to apply long length modification to */ + ZSTD_longLengthType_e longLengthType; + U32 longLengthPos; /* Index of the sequence to apply long length modification to */ } seqStore_t; typedef struct { @@ -331,10 +326,10 @@ MEM_STATIC ZSTD_sequenceLength ZSTD_getSequenceLength(seqStore_t const* seqStore seqLen.matchLength = seq->mlBase + MINMATCH; if (seqStore->longLengthPos == (U32)(seq - seqStore->sequencesStart)) { if (seqStore->longLengthType == ZSTD_llt_literalLength) { - seqLen.litLength += 0xFFFF; + seqLen.litLength += 0x10000; } if (seqStore->longLengthType == ZSTD_llt_matchLength) { - seqLen.matchLength += 0xFFFF; + seqLen.matchLength += 0x10000; } } return seqLen; @@ -347,109 +342,13 @@ MEM_STATIC ZSTD_sequenceLength ZSTD_getSequenceLength(seqStore_t const* seqStore * `decompressedBound != ZSTD_CONTENTSIZE_ERROR` */ typedef struct { + size_t nbBlocks; size_t compressedSize; unsigned long long decompressedBound; } ZSTD_frameSizeInfo; /* decompress & legacy */ const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx); /* compress & dictBuilder */ -void ZSTD_seqToCodes(const seqStore_t* seqStorePtr); /* compress, dictBuilder, decodeCorpus (shouldn't get its definition from here) */ - -/* custom memory allocation functions */ -void* ZSTD_customMalloc(size_t size, ZSTD_customMem customMem); -void* ZSTD_customCalloc(size_t size, ZSTD_customMem customMem); -void ZSTD_customFree(void* ptr, ZSTD_customMem customMem); - - -MEM_STATIC U32 ZSTD_highbit32(U32 val) /* compress, dictBuilder, decodeCorpus */ -{ - assert(val != 0); - { -# if defined(_MSC_VER) /* Visual */ -# if STATIC_BMI2 == 1 - return _lzcnt_u32(val)^31; -# else - if (val != 0) { - unsigned long r; - _BitScanReverse(&r, val); - return (unsigned)r; - } else { - /* Should not reach this code path */ - __assume(0); - } -# endif -# elif defined(__GNUC__) && (__GNUC__ >= 3) /* GCC Intrinsic */ - return __builtin_clz (val) ^ 31; -# elif defined(__ICCARM__) /* IAR Intrinsic */ - return 31 - __CLZ(val); -# else /* Software version */ - static const U32 DeBruijnClz[32] = { 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 }; - U32 v = val; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - return DeBruijnClz[(v * 0x07C4ACDDU) >> 27]; -# endif - } -} - -/** - * Counts the number of trailing zeros of a `size_t`. - * Most compilers should support CTZ as a builtin. A backup - * implementation is provided if the builtin isn't supported, but - * it may not be terribly efficient. - */ -MEM_STATIC unsigned ZSTD_countTrailingZeros(size_t val) -{ - if (MEM_64bits()) { -# if defined(_MSC_VER) && defined(_WIN64) -# if STATIC_BMI2 - return _tzcnt_u64(val); -# else - if (val != 0) { - unsigned long r; - _BitScanForward64(&r, (U64)val); - return (unsigned)r; - } else { - /* Should not reach this code path */ - __assume(0); - } -# endif -# elif defined(__GNUC__) && (__GNUC__ >= 4) - return __builtin_ctzll((U64)val); -# else - static const int DeBruijnBytePos[64] = { 0, 1, 2, 7, 3, 13, 8, 19, - 4, 25, 14, 28, 9, 34, 20, 56, - 5, 17, 26, 54, 15, 41, 29, 43, - 10, 31, 38, 35, 21, 45, 49, 57, - 63, 6, 12, 18, 24, 27, 33, 55, - 16, 53, 40, 42, 30, 37, 44, 48, - 62, 11, 23, 32, 52, 39, 36, 47, - 61, 22, 51, 46, 60, 50, 59, 58 }; - return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58]; -# endif - } else { /* 32 bits */ -# if defined(_MSC_VER) - if (val != 0) { - unsigned long r; - _BitScanForward(&r, (U32)val); - return (unsigned)r; - } else { - /* Should not reach this code path */ - __assume(0); - } -# elif defined(__GNUC__) && (__GNUC__ >= 3) - return __builtin_ctz((U32)val); -# else - static const int DeBruijnBytePos[32] = { 0, 1, 28, 2, 29, 14, 24, 3, - 30, 22, 20, 15, 25, 17, 4, 8, - 31, 27, 13, 23, 21, 19, 16, 7, - 26, 12, 18, 6, 11, 5, 10, 9 }; - return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27]; -# endif - } -} +int ZSTD_seqToCodes(const seqStore_t* seqStorePtr); /* compress, dictBuilder, decodeCorpus (shouldn't get its definition from here) */ /* ZSTD_invalidateRepCodes() : diff --git a/internal-complibs/zstd-1.5.2/common/zstd_trace.h b/internal-complibs/zstd-1.5.5/common/zstd_trace.h similarity index 96% rename from internal-complibs/zstd-1.5.2/common/zstd_trace.h rename to internal-complibs/zstd-1.5.5/common/zstd_trace.h index f9121f7d..da20534e 100644 --- a/internal-complibs/zstd-1.5.2/common/zstd_trace.h +++ b/internal-complibs/zstd-1.5.5/common/zstd_trace.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -21,13 +21,13 @@ extern "C" { * For now, enable conservatively: * - Only GNUC * - Only ELF - * - Only x86-64 and i386 + * - Only x86-64, i386 and aarch64 * Also, explicitly disable on platforms known not to work so they aren't * forgotten in the future. */ #if !defined(ZSTD_HAVE_WEAK_SYMBOLS) && \ defined(__GNUC__) && defined(__ELF__) && \ - (defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86)) && \ + (defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86) || defined(__aarch64__)) && \ !defined(__APPLE__) && !defined(_WIN32) && !defined(__MINGW32__) && \ !defined(__CYGWIN__) && !defined(_AIX) # define ZSTD_HAVE_WEAK_SYMBOLS 1 diff --git a/internal-complibs/zstd-1.5.2/compress/clevels.h b/internal-complibs/zstd-1.5.5/compress/clevels.h similarity index 99% rename from internal-complibs/zstd-1.5.2/compress/clevels.h rename to internal-complibs/zstd-1.5.5/compress/clevels.h index 7ed2e004..c18da465 100644 --- a/internal-complibs/zstd-1.5.2/compress/clevels.h +++ b/internal-complibs/zstd-1.5.5/compress/clevels.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the diff --git a/internal-complibs/zstd-1.5.2/compress/fse_compress.c b/internal-complibs/zstd-1.5.5/compress/fse_compress.c similarity index 81% rename from internal-complibs/zstd-1.5.2/compress/fse_compress.c rename to internal-complibs/zstd-1.5.5/compress/fse_compress.c index 5547b4ac..5d377080 100644 --- a/internal-complibs/zstd-1.5.2/compress/fse_compress.c +++ b/internal-complibs/zstd-1.5.5/compress/fse_compress.c @@ -1,6 +1,6 @@ /* ****************************************************************** * FSE : Finite State Entropy encoder - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : * - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy @@ -26,6 +26,7 @@ #define ZSTD_DEPS_NEED_MALLOC #define ZSTD_DEPS_NEED_MATH64 #include "../common/zstd_deps.h" /* ZSTD_malloc, ZSTD_free, ZSTD_memcpy, ZSTD_memset */ +#include "../common/bits.h" /* ZSTD_highbit32 */ /* ************************************************************** @@ -90,7 +91,7 @@ size_t FSE_buildCTable_wksp(FSE_CTable* ct, assert(tableLog < 16); /* required for threshold strategy to work */ /* For explanations on how to distribute symbol values over the table : - * http://fastcompression.blogspot.fr/2014/02/fse-distributing-symbol-values.html */ + * https://fastcompression.blogspot.fr/2014/02/fse-distributing-symbol-values.html */ #ifdef __clang_analyzer__ ZSTD_memset(tableSymbol, 0, sizeof(*tableSymbol) * tableSize); /* useless initialization, just to keep scan-build happy */ @@ -191,7 +192,7 @@ size_t FSE_buildCTable_wksp(FSE_CTable* ct, break; default : assert(normalizedCounter[s] > 1); - { U32 const maxBitsOut = tableLog - BIT_highbit32 ((U32)normalizedCounter[s]-1); + { U32 const maxBitsOut = tableLog - ZSTD_highbit32 ((U32)normalizedCounter[s]-1); U32 const minStatePlus = (U32)normalizedCounter[s] << maxBitsOut; symbolTT[s].deltaNbBits = (maxBitsOut << 16) - minStatePlus; symbolTT[s].deltaFindState = (int)(total - (unsigned)normalizedCounter[s]); @@ -342,21 +343,11 @@ size_t FSE_writeNCount (void* buffer, size_t bufferSize, * FSE Compression Code ****************************************************************/ -FSE_CTable* FSE_createCTable (unsigned maxSymbolValue, unsigned tableLog) -{ - size_t size; - if (tableLog > FSE_TABLELOG_ABSOLUTE_MAX) tableLog = FSE_TABLELOG_ABSOLUTE_MAX; - size = FSE_CTABLE_SIZE_U32 (tableLog, maxSymbolValue) * sizeof(U32); - return (FSE_CTable*)ZSTD_malloc(size); -} - -void FSE_freeCTable (FSE_CTable* ct) { ZSTD_free(ct); } - /* provides the minimum logSize to safely represent a distribution */ static unsigned FSE_minTableLog(size_t srcSize, unsigned maxSymbolValue) { - U32 minBitsSrc = BIT_highbit32((U32)(srcSize)) + 1; - U32 minBitsSymbols = BIT_highbit32(maxSymbolValue) + 2; + U32 minBitsSrc = ZSTD_highbit32((U32)(srcSize)) + 1; + U32 minBitsSymbols = ZSTD_highbit32(maxSymbolValue) + 2; U32 minBits = minBitsSrc < minBitsSymbols ? minBitsSrc : minBitsSymbols; assert(srcSize > 1); /* Not supported, RLE should be used instead */ return minBits; @@ -364,7 +355,7 @@ static unsigned FSE_minTableLog(size_t srcSize, unsigned maxSymbolValue) unsigned FSE_optimalTableLog_internal(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue, unsigned minus) { - U32 maxBitsSrc = BIT_highbit32((U32)(srcSize - 1)) - minus; + U32 maxBitsSrc = ZSTD_highbit32((U32)(srcSize - 1)) - minus; U32 tableLog = maxTableLog; U32 minBits = FSE_minTableLog(srcSize, maxSymbolValue); assert(srcSize > 1); /* Not supported, RLE should be used instead */ @@ -532,40 +523,6 @@ size_t FSE_normalizeCount (short* normalizedCounter, unsigned tableLog, return tableLog; } - -/* fake FSE_CTable, for raw (uncompressed) input */ -size_t FSE_buildCTable_raw (FSE_CTable* ct, unsigned nbBits) -{ - const unsigned tableSize = 1 << nbBits; - const unsigned tableMask = tableSize - 1; - const unsigned maxSymbolValue = tableMask; - void* const ptr = ct; - U16* const tableU16 = ( (U16*) ptr) + 2; - void* const FSCT = ((U32*)ptr) + 1 /* header */ + (tableSize>>1); /* assumption : tableLog >= 1 */ - FSE_symbolCompressionTransform* const symbolTT = (FSE_symbolCompressionTransform*) (FSCT); - unsigned s; - - /* Sanity checks */ - if (nbBits < 1) return ERROR(GENERIC); /* min size */ - - /* header */ - tableU16[-2] = (U16) nbBits; - tableU16[-1] = (U16) maxSymbolValue; - - /* Build table */ - for (s=0; s not compressible */ - if (maxCount < (srcSize >> 7)) return 0; /* Heuristic : not compressible enough */ - } - - tableLog = FSE_optimalTableLog(tableLog, srcSize, maxSymbolValue); - CHECK_F( FSE_normalizeCount(norm, tableLog, count, srcSize, maxSymbolValue, /* useLowProbCount */ srcSize >= 2048) ); - - /* Write table description header */ - { CHECK_V_F(nc_err, FSE_writeNCount(op, oend-op, norm, maxSymbolValue, tableLog) ); - op += nc_err; - } - - /* Compress */ - CHECK_F( FSE_buildCTable_wksp(CTable, norm, maxSymbolValue, tableLog, scratchBuffer, scratchBufferSize) ); - { CHECK_V_F(cSize, FSE_compress_usingCTable(op, oend - op, src, srcSize, CTable) ); - if (cSize == 0) return 0; /* not enough space for compressed data */ - op += cSize; - } - - /* check compressibility */ - if ( (size_t)(op-ostart) >= srcSize-1 ) return 0; - - return op-ostart; -} - -typedef struct { - FSE_CTable CTable_max[FSE_CTABLE_SIZE_U32(FSE_MAX_TABLELOG, FSE_MAX_SYMBOL_VALUE)]; - union { - U32 hist_wksp[HIST_WKSP_SIZE_U32]; - BYTE scratchBuffer[1 << FSE_MAX_TABLELOG]; - } workspace; -} fseWkspMax_t; - -size_t FSE_compress2 (void* dst, size_t dstCapacity, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog) -{ - fseWkspMax_t scratchBuffer; - DEBUG_STATIC_ASSERT(sizeof(scratchBuffer) >= FSE_COMPRESS_WKSP_SIZE_U32(FSE_MAX_TABLELOG, FSE_MAX_SYMBOL_VALUE)); /* compilation failures here means scratchBuffer is not large enough */ - if (tableLog > FSE_MAX_TABLELOG) return ERROR(tableLog_tooLarge); - return FSE_compress_wksp(dst, dstCapacity, src, srcSize, maxSymbolValue, tableLog, &scratchBuffer, sizeof(scratchBuffer)); -} - -size_t FSE_compress (void* dst, size_t dstCapacity, const void* src, size_t srcSize) -{ - return FSE_compress2(dst, dstCapacity, src, srcSize, FSE_MAX_SYMBOL_VALUE, FSE_DEFAULT_TABLELOG); -} -#endif - #endif /* FSE_COMMONDEFS_ONLY */ diff --git a/internal-complibs/zstd-1.5.2/compress/hist.c b/internal-complibs/zstd-1.5.5/compress/hist.c similarity index 99% rename from internal-complibs/zstd-1.5.2/compress/hist.c rename to internal-complibs/zstd-1.5.5/compress/hist.c index 073c57e7..e2fb431f 100644 --- a/internal-complibs/zstd-1.5.2/compress/hist.c +++ b/internal-complibs/zstd-1.5.5/compress/hist.c @@ -1,7 +1,7 @@ /* ****************************************************************** * hist : Histogram functions * part of Finite State Entropy project - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : * - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy diff --git a/internal-complibs/zstd-1.5.2/compress/hist.h b/internal-complibs/zstd-1.5.5/compress/hist.h similarity index 98% rename from internal-complibs/zstd-1.5.2/compress/hist.h rename to internal-complibs/zstd-1.5.5/compress/hist.h index 228ed48a..887896b8 100644 --- a/internal-complibs/zstd-1.5.2/compress/hist.h +++ b/internal-complibs/zstd-1.5.5/compress/hist.h @@ -1,7 +1,7 @@ /* ****************************************************************** * hist : Histogram functions * part of Finite State Entropy project - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : * - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy diff --git a/internal-complibs/zstd-1.5.2/compress/huf_compress.c b/internal-complibs/zstd-1.5.5/compress/huf_compress.c similarity index 84% rename from internal-complibs/zstd-1.5.2/compress/huf_compress.c rename to internal-complibs/zstd-1.5.5/compress/huf_compress.c index 2b3d6adc..29871877 100644 --- a/internal-complibs/zstd-1.5.2/compress/huf_compress.c +++ b/internal-complibs/zstd-1.5.5/compress/huf_compress.c @@ -1,6 +1,6 @@ /* ****************************************************************** * Huffman encoder, part of New Generation Entropy library - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : * - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy @@ -29,9 +29,9 @@ #include "hist.h" #define FSE_STATIC_LINKING_ONLY /* FSE_optimalTableLog_internal */ #include "../common/fse.h" /* header compression */ -#define HUF_STATIC_LINKING_ONLY #include "../common/huf.h" #include "../common/error_private.h" +#include "../common/bits.h" /* ZSTD_highbit32 */ /* ************************************************************** @@ -42,13 +42,67 @@ /* ************************************************************** -* Utils +* Required declarations ****************************************************************/ -unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue) +typedef struct nodeElt_s { + U32 count; + U16 parent; + BYTE byte; + BYTE nbBits; +} nodeElt; + + +/* ************************************************************** +* Debug Traces +****************************************************************/ + +#if DEBUGLEVEL >= 2 + +static size_t showU32(const U32* arr, size_t size) { - return FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 1); + size_t u; + for (u=0; u= sizeof(HUF_WriteCTableWksp)); + /* check conditions */ if (workspaceSize < sizeof(HUF_WriteCTableWksp)) return ERROR(GENERIC); if (maxSymbolValue > HUF_SYMBOLVALUE_MAX) return ERROR(maxSymbolValue_tooLarge); @@ -207,16 +266,6 @@ size_t HUF_writeCTable_wksp(void* dst, size_t maxDstSize, return ((maxSymbolValue+1)/2) + 1; } -/*! HUF_writeCTable() : - `CTable` : Huffman tree to save, using huf representation. - @return : size of saved CTable */ -size_t HUF_writeCTable (void* dst, size_t maxDstSize, - const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog) -{ - HUF_WriteCTableWksp wksp; - return HUF_writeCTable_wksp(dst, maxDstSize, CTable, maxSymbolValue, huffLog, &wksp, sizeof(wksp)); -} - size_t HUF_readCTable (HUF_CElt* CTable, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize, unsigned* hasZeroWeights) { @@ -272,68 +321,64 @@ size_t HUF_readCTable (HUF_CElt* CTable, unsigned* maxSymbolValuePtr, const void U32 HUF_getNbBitsFromCTable(HUF_CElt const* CTable, U32 symbolValue) { - const HUF_CElt* ct = CTable + 1; + const HUF_CElt* const ct = CTable + 1; assert(symbolValue <= HUF_SYMBOLVALUE_MAX); return (U32)HUF_getNbBits(ct[symbolValue]); } -typedef struct nodeElt_s { - U32 count; - U16 parent; - BYTE byte; - BYTE nbBits; -} nodeElt; - /** * HUF_setMaxHeight(): - * Enforces maxNbBits on the Huffman tree described in huffNode. + * Try to enforce @targetNbBits on the Huffman tree described in @huffNode. * - * It sets all nodes with nbBits > maxNbBits to be maxNbBits. Then it adjusts - * the tree to so that it is a valid canonical Huffman tree. + * It attempts to convert all nodes with nbBits > @targetNbBits + * to employ @targetNbBits instead. Then it adjusts the tree + * so that it remains a valid canonical Huffman tree. * * @pre The sum of the ranks of each symbol == 2^largestBits, * where largestBits == huffNode[lastNonNull].nbBits. * @post The sum of the ranks of each symbol == 2^largestBits, - * where largestBits is the return value <= maxNbBits. + * where largestBits is the return value (expected <= targetNbBits). * - * @param huffNode The Huffman tree modified in place to enforce maxNbBits. + * @param huffNode The Huffman tree modified in place to enforce targetNbBits. + * It's presumed sorted, from most frequent to rarest symbol. * @param lastNonNull The symbol with the lowest count in the Huffman tree. - * @param maxNbBits The maximum allowed number of bits, which the Huffman tree + * @param targetNbBits The allowed number of bits, which the Huffman tree * may not respect. After this function the Huffman tree will - * respect maxNbBits. - * @return The maximum number of bits of the Huffman tree after adjustment, - * necessarily no more than maxNbBits. + * respect targetNbBits. + * @return The maximum number of bits of the Huffman tree after adjustment. */ -static U32 HUF_setMaxHeight(nodeElt* huffNode, U32 lastNonNull, U32 maxNbBits) +static U32 HUF_setMaxHeight(nodeElt* huffNode, U32 lastNonNull, U32 targetNbBits) { const U32 largestBits = huffNode[lastNonNull].nbBits; - /* early exit : no elt > maxNbBits, so the tree is already valid. */ - if (largestBits <= maxNbBits) return largestBits; + /* early exit : no elt > targetNbBits, so the tree is already valid. */ + if (largestBits <= targetNbBits) return largestBits; + + DEBUGLOG(5, "HUF_setMaxHeight (targetNbBits = %u)", targetNbBits); /* there are several too large elements (at least >= 2) */ { int totalCost = 0; - const U32 baseCost = 1 << (largestBits - maxNbBits); + const U32 baseCost = 1 << (largestBits - targetNbBits); int n = (int)lastNonNull; - /* Adjust any ranks > maxNbBits to maxNbBits. + /* Adjust any ranks > targetNbBits to targetNbBits. * Compute totalCost, which is how far the sum of the ranks is * we are over 2^largestBits after adjust the offending ranks. */ - while (huffNode[n].nbBits > maxNbBits) { + while (huffNode[n].nbBits > targetNbBits) { totalCost += baseCost - (1 << (largestBits - huffNode[n].nbBits)); - huffNode[n].nbBits = (BYTE)maxNbBits; + huffNode[n].nbBits = (BYTE)targetNbBits; n--; } - /* n stops at huffNode[n].nbBits <= maxNbBits */ - assert(huffNode[n].nbBits <= maxNbBits); - /* n end at index of smallest symbol using < maxNbBits */ - while (huffNode[n].nbBits == maxNbBits) --n; + /* n stops at huffNode[n].nbBits <= targetNbBits */ + assert(huffNode[n].nbBits <= targetNbBits); + /* n end at index of smallest symbol using < targetNbBits */ + while (huffNode[n].nbBits == targetNbBits) --n; - /* renorm totalCost from 2^largestBits to 2^maxNbBits + /* renorm totalCost from 2^largestBits to 2^targetNbBits * note : totalCost is necessarily a multiple of baseCost */ - assert((totalCost & (baseCost - 1)) == 0); - totalCost >>= (largestBits - maxNbBits); + assert(((U32)totalCost & (baseCost - 1)) == 0); + totalCost >>= (largestBits - targetNbBits); assert(totalCost > 0); /* repay normalized cost */ @@ -342,19 +387,19 @@ static U32 HUF_setMaxHeight(nodeElt* huffNode, U32 lastNonNull, U32 maxNbBits) /* Get pos of last (smallest = lowest cum. count) symbol per rank */ ZSTD_memset(rankLast, 0xF0, sizeof(rankLast)); - { U32 currentNbBits = maxNbBits; + { U32 currentNbBits = targetNbBits; int pos; for (pos=n ; pos >= 0; pos--) { if (huffNode[pos].nbBits >= currentNbBits) continue; - currentNbBits = huffNode[pos].nbBits; /* < maxNbBits */ - rankLast[maxNbBits-currentNbBits] = (U32)pos; + currentNbBits = huffNode[pos].nbBits; /* < targetNbBits */ + rankLast[targetNbBits-currentNbBits] = (U32)pos; } } while (totalCost > 0) { /* Try to reduce the next power of 2 above totalCost because we * gain back half the rank. */ - U32 nBitsToDecrease = BIT_highbit32((U32)totalCost) + 1; + U32 nBitsToDecrease = ZSTD_highbit32((U32)totalCost) + 1; for ( ; nBitsToDecrease > 1; nBitsToDecrease--) { U32 const highPos = rankLast[nBitsToDecrease]; U32 const lowPos = rankLast[nBitsToDecrease-1]; @@ -394,7 +439,7 @@ static U32 HUF_setMaxHeight(nodeElt* huffNode, U32 lastNonNull, U32 maxNbBits) rankLast[nBitsToDecrease] = noSymbol; else { rankLast[nBitsToDecrease]--; - if (huffNode[rankLast[nBitsToDecrease]].nbBits != maxNbBits-nBitsToDecrease) + if (huffNode[rankLast[nBitsToDecrease]].nbBits != targetNbBits-nBitsToDecrease) rankLast[nBitsToDecrease] = noSymbol; /* this rank is now empty */ } } /* while (totalCost > 0) */ @@ -406,11 +451,11 @@ static U32 HUF_setMaxHeight(nodeElt* huffNode, U32 lastNonNull, U32 maxNbBits) * TODO. */ while (totalCost < 0) { /* Sometimes, cost correction overshoot */ - /* special case : no rank 1 symbol (using maxNbBits-1); - * let's create one from largest rank 0 (using maxNbBits). + /* special case : no rank 1 symbol (using targetNbBits-1); + * let's create one from largest rank 0 (using targetNbBits). */ if (rankLast[1] == noSymbol) { - while (huffNode[n].nbBits == maxNbBits) n--; + while (huffNode[n].nbBits == targetNbBits) n--; huffNode[n+1].nbBits--; assert(n >= 0); rankLast[1] = (U32)(n+1); @@ -424,7 +469,7 @@ static U32 HUF_setMaxHeight(nodeElt* huffNode, U32 lastNonNull, U32 maxNbBits) } /* repay normalized cost */ } /* there are several too large elements (at least >= 2) */ - return maxNbBits; + return targetNbBits; } typedef struct { @@ -432,7 +477,7 @@ typedef struct { U16 curr; } rankPos; -typedef nodeElt huffNodeTable[HUF_CTABLE_WORKSPACE_SIZE_U32]; +typedef nodeElt huffNodeTable[2 * (HUF_SYMBOLVALUE_MAX + 1)]; /* Number of buckets available for HUF_sort() */ #define RANK_POSITION_TABLE_SIZE 192 @@ -451,8 +496,8 @@ typedef struct { * Let buckets 166 to 192 represent all remaining counts up to RANK_POSITION_MAX_COUNT_LOG using log2 bucketing. */ #define RANK_POSITION_MAX_COUNT_LOG 32 -#define RANK_POSITION_LOG_BUCKETS_BEGIN (RANK_POSITION_TABLE_SIZE - 1) - RANK_POSITION_MAX_COUNT_LOG - 1 /* == 158 */ -#define RANK_POSITION_DISTINCT_COUNT_CUTOFF RANK_POSITION_LOG_BUCKETS_BEGIN + BIT_highbit32(RANK_POSITION_LOG_BUCKETS_BEGIN) /* == 166 */ +#define RANK_POSITION_LOG_BUCKETS_BEGIN ((RANK_POSITION_TABLE_SIZE - 1) - RANK_POSITION_MAX_COUNT_LOG - 1 /* == 158 */) +#define RANK_POSITION_DISTINCT_COUNT_CUTOFF (RANK_POSITION_LOG_BUCKETS_BEGIN + ZSTD_highbit32(RANK_POSITION_LOG_BUCKETS_BEGIN) /* == 166 */) /* Return the appropriate bucket index for a given count. See definition of * RANK_POSITION_DISTINCT_COUNT_CUTOFF for explanation of bucketing strategy. @@ -460,7 +505,7 @@ typedef struct { static U32 HUF_getIndex(U32 const count) { return (count < RANK_POSITION_DISTINCT_COUNT_CUTOFF) ? count - : BIT_highbit32(count) + RANK_POSITION_LOG_BUCKETS_BEGIN; + : ZSTD_highbit32(count) + RANK_POSITION_LOG_BUCKETS_BEGIN; } /* Helper swap function for HUF_quickSortPartition() */ @@ -583,7 +628,7 @@ static void HUF_sort(nodeElt huffNode[], const unsigned count[], U32 const maxSy /* Sort each bucket. */ for (n = RANK_POSITION_DISTINCT_COUNT_CUTOFF; n < RANK_POSITION_TABLE_SIZE - 1; ++n) { - U32 const bucketSize = rankPosition[n].curr-rankPosition[n].base; + int const bucketSize = rankPosition[n].curr - rankPosition[n].base; U32 const bucketStartIdx = rankPosition[n].base; if (bucketSize > 1) { assert(bucketStartIdx < maxSymbolValue1); @@ -594,6 +639,7 @@ static void HUF_sort(nodeElt huffNode[], const unsigned count[], U32 const maxSy assert(HUF_isSorted(huffNode, maxSymbolValue1)); } + /** HUF_buildCTable_wksp() : * Same as HUF_buildCTable(), but using externally allocated scratch buffer. * `workSpace` must be aligned on 4-bytes boundaries, and be at least as large as sizeof(HUF_buildCTable_wksp_tables). @@ -614,6 +660,7 @@ static int HUF_buildTree(nodeElt* huffNode, U32 maxSymbolValue) int lowS, lowN; int nodeNb = STARTNODE; int n, nodeRoot; + DEBUGLOG(5, "HUF_buildTree (alphabet size = %u)", maxSymbolValue + 1); /* init for parents */ nonNullRank = (int)maxSymbolValue; while(huffNode[nonNullRank].count == 0) nonNullRank--; @@ -640,6 +687,8 @@ static int HUF_buildTree(nodeElt* huffNode, U32 maxSymbolValue) for (n=0; n<=nonNullRank; n++) huffNode[n].nbBits = huffNode[ huffNode[n].parent ].nbBits + 1; + DEBUGLOG(6, "Initial distribution of bits completed (%zu sorted symbols)", showHNodeBits(huffNode, maxSymbolValue+1)); + return nonNullRank; } @@ -677,28 +726,36 @@ static void HUF_buildCTableFromTree(HUF_CElt* CTable, nodeElt const* huffNode, i CTable[0] = maxNbBits; } -size_t HUF_buildCTable_wksp (HUF_CElt* CTable, const unsigned* count, U32 maxSymbolValue, U32 maxNbBits, void* workSpace, size_t wkspSize) +size_t +HUF_buildCTable_wksp(HUF_CElt* CTable, const unsigned* count, U32 maxSymbolValue, U32 maxNbBits, + void* workSpace, size_t wkspSize) { - HUF_buildCTable_wksp_tables* const wksp_tables = (HUF_buildCTable_wksp_tables*)HUF_alignUpWorkspace(workSpace, &wkspSize, ZSTD_ALIGNOF(U32)); + HUF_buildCTable_wksp_tables* const wksp_tables = + (HUF_buildCTable_wksp_tables*)HUF_alignUpWorkspace(workSpace, &wkspSize, ZSTD_ALIGNOF(U32)); nodeElt* const huffNode0 = wksp_tables->huffNodeTbl; nodeElt* const huffNode = huffNode0+1; int nonNullRank; + HUF_STATIC_ASSERT(HUF_CTABLE_WORKSPACE_SIZE == sizeof(HUF_buildCTable_wksp_tables)); + + DEBUGLOG(5, "HUF_buildCTable_wksp (alphabet size = %u)", maxSymbolValue+1); + /* safety checks */ if (wkspSize < sizeof(HUF_buildCTable_wksp_tables)) - return ERROR(workSpace_tooSmall); + return ERROR(workSpace_tooSmall); if (maxNbBits == 0) maxNbBits = HUF_TABLELOG_DEFAULT; if (maxSymbolValue > HUF_SYMBOLVALUE_MAX) - return ERROR(maxSymbolValue_tooLarge); + return ERROR(maxSymbolValue_tooLarge); ZSTD_memset(huffNode0, 0, sizeof(huffNodeTable)); /* sort, decreasing order */ HUF_sort(huffNode, count, maxSymbolValue, wksp_tables->rankPosition); + DEBUGLOG(6, "sorted symbols completed (%zu symbols)", showHNodeSymbols(huffNode, maxSymbolValue+1)); /* build tree */ nonNullRank = HUF_buildTree(huffNode, maxSymbolValue); - /* enforce maxTableLog */ + /* determine and enforce maxTableLog */ maxNbBits = HUF_setMaxHeight(huffNode, (U32)nonNullRank, maxNbBits); if (maxNbBits > HUF_TABLELOG_MAX) return ERROR(GENERIC); /* check fit into table */ @@ -807,7 +864,7 @@ FORCE_INLINE_TEMPLATE void HUF_addBits(HUF_CStream_t* bitC, HUF_CElt elt, int id #if DEBUGLEVEL >= 1 { size_t const nbBits = HUF_getNbBits(elt); - size_t const dirtyBits = nbBits == 0 ? 0 : BIT_highbit32((U32)nbBits) + 1; + size_t const dirtyBits = nbBits == 0 ? 0 : ZSTD_highbit32((U32)nbBits) + 1; (void)dirtyBits; /* Middle bits are 0. */ assert(((elt >> dirtyBits) << (dirtyBits + nbBits)) == 0); @@ -887,7 +944,7 @@ static size_t HUF_closeCStream(HUF_CStream_t* bitC) { size_t const nbBits = bitC->bitPos[0] & 0xFF; if (bitC->ptr >= bitC->endPtr) return 0; /* overflow detected */ - return (bitC->ptr - bitC->startPtr) + (nbBits > 0); + return (size_t)(bitC->ptr - bitC->startPtr) + (nbBits > 0); } } @@ -1048,9 +1105,9 @@ HUF_compress1X_usingCTable_internal_default(void* dst, size_t dstSize, static size_t HUF_compress1X_usingCTable_internal(void* dst, size_t dstSize, const void* src, size_t srcSize, - const HUF_CElt* CTable, const int bmi2) + const HUF_CElt* CTable, const int flags) { - if (bmi2) { + if (flags & HUF_flags_bmi2) { return HUF_compress1X_usingCTable_internal_bmi2(dst, dstSize, src, srcSize, CTable); } return HUF_compress1X_usingCTable_internal_default(dst, dstSize, src, srcSize, CTable); @@ -1061,28 +1118,23 @@ HUF_compress1X_usingCTable_internal(void* dst, size_t dstSize, static size_t HUF_compress1X_usingCTable_internal(void* dst, size_t dstSize, const void* src, size_t srcSize, - const HUF_CElt* CTable, const int bmi2) + const HUF_CElt* CTable, const int flags) { - (void)bmi2; + (void)flags; return HUF_compress1X_usingCTable_internal_body(dst, dstSize, src, srcSize, CTable); } #endif -size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable) +size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable, int flags) { - return HUF_compress1X_usingCTable_bmi2(dst, dstSize, src, srcSize, CTable, /* bmi2 */ 0); -} - -size_t HUF_compress1X_usingCTable_bmi2(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable, int bmi2) -{ - return HUF_compress1X_usingCTable_internal(dst, dstSize, src, srcSize, CTable, bmi2); + return HUF_compress1X_usingCTable_internal(dst, dstSize, src, srcSize, CTable, flags); } static size_t HUF_compress4X_usingCTable_internal(void* dst, size_t dstSize, const void* src, size_t srcSize, - const HUF_CElt* CTable, int bmi2) + const HUF_CElt* CTable, int flags) { size_t const segmentSize = (srcSize+3)/4; /* first 3 segments */ const BYTE* ip = (const BYTE*) src; @@ -1096,7 +1148,7 @@ HUF_compress4X_usingCTable_internal(void* dst, size_t dstSize, op += 6; /* jumpTable */ assert(op <= oend); - { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, bmi2) ); + { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, flags) ); if (cSize == 0 || cSize > 65535) return 0; MEM_writeLE16(ostart, (U16)cSize); op += cSize; @@ -1104,7 +1156,7 @@ HUF_compress4X_usingCTable_internal(void* dst, size_t dstSize, ip += segmentSize; assert(op <= oend); - { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, bmi2) ); + { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, flags) ); if (cSize == 0 || cSize > 65535) return 0; MEM_writeLE16(ostart+2, (U16)cSize); op += cSize; @@ -1112,7 +1164,7 @@ HUF_compress4X_usingCTable_internal(void* dst, size_t dstSize, ip += segmentSize; assert(op <= oend); - { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, bmi2) ); + { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, flags) ); if (cSize == 0 || cSize > 65535) return 0; MEM_writeLE16(ostart+4, (U16)cSize); op += cSize; @@ -1121,7 +1173,7 @@ HUF_compress4X_usingCTable_internal(void* dst, size_t dstSize, ip += segmentSize; assert(op <= oend); assert(ip <= iend); - { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, (size_t)(iend-ip), CTable, bmi2) ); + { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, (size_t)(iend-ip), CTable, flags) ); if (cSize == 0 || cSize > 65535) return 0; op += cSize; } @@ -1129,14 +1181,9 @@ HUF_compress4X_usingCTable_internal(void* dst, size_t dstSize, return (size_t)(op-ostart); } -size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable) -{ - return HUF_compress4X_usingCTable_bmi2(dst, dstSize, src, srcSize, CTable, /* bmi2 */ 0); -} - -size_t HUF_compress4X_usingCTable_bmi2(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable, int bmi2) +size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable, int flags) { - return HUF_compress4X_usingCTable_internal(dst, dstSize, src, srcSize, CTable, bmi2); + return HUF_compress4X_usingCTable_internal(dst, dstSize, src, srcSize, CTable, flags); } typedef enum { HUF_singleStream, HUF_fourStreams } HUF_nbStreams_e; @@ -1144,11 +1191,11 @@ typedef enum { HUF_singleStream, HUF_fourStreams } HUF_nbStreams_e; static size_t HUF_compressCTable_internal( BYTE* const ostart, BYTE* op, BYTE* const oend, const void* src, size_t srcSize, - HUF_nbStreams_e nbStreams, const HUF_CElt* CTable, const int bmi2) + HUF_nbStreams_e nbStreams, const HUF_CElt* CTable, const int flags) { size_t const cSize = (nbStreams==HUF_singleStream) ? - HUF_compress1X_usingCTable_internal(op, (size_t)(oend - op), src, srcSize, CTable, bmi2) : - HUF_compress4X_usingCTable_internal(op, (size_t)(oend - op), src, srcSize, CTable, bmi2); + HUF_compress1X_usingCTable_internal(op, (size_t)(oend - op), src, srcSize, CTable, flags) : + HUF_compress4X_usingCTable_internal(op, (size_t)(oend - op), src, srcSize, CTable, flags); if (HUF_isError(cSize)) { return cSize; } if (cSize==0) { return 0; } /* uncompressible */ op += cSize; @@ -1171,6 +1218,79 @@ typedef struct { #define SUSPECT_INCOMPRESSIBLE_SAMPLE_SIZE 4096 #define SUSPECT_INCOMPRESSIBLE_SAMPLE_RATIO 10 /* Must be >= 2 */ +unsigned HUF_cardinality(const unsigned* count, unsigned maxSymbolValue) +{ + unsigned cardinality = 0; + unsigned i; + + for (i = 0; i < maxSymbolValue + 1; i++) { + if (count[i] != 0) cardinality += 1; + } + + return cardinality; +} + +unsigned HUF_minTableLog(unsigned symbolCardinality) +{ + U32 minBitsSymbols = ZSTD_highbit32(symbolCardinality) + 1; + return minBitsSymbols; +} + +unsigned HUF_optimalTableLog( + unsigned maxTableLog, + size_t srcSize, + unsigned maxSymbolValue, + void* workSpace, size_t wkspSize, + HUF_CElt* table, + const unsigned* count, + int flags) +{ + assert(srcSize > 1); /* Not supported, RLE should be used instead */ + assert(wkspSize >= sizeof(HUF_buildCTable_wksp_tables)); + + if (!(flags & HUF_flags_optimalDepth)) { + /* cheap evaluation, based on FSE */ + return FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 1); + } + + { BYTE* dst = (BYTE*)workSpace + sizeof(HUF_WriteCTableWksp); + size_t dstSize = wkspSize - sizeof(HUF_WriteCTableWksp); + size_t maxBits, hSize, newSize; + const unsigned symbolCardinality = HUF_cardinality(count, maxSymbolValue); + const unsigned minTableLog = HUF_minTableLog(symbolCardinality); + size_t optSize = ((size_t) ~0) - 1; + unsigned optLog = maxTableLog, optLogGuess; + + DEBUGLOG(6, "HUF_optimalTableLog: probing huf depth (srcSize=%zu)", srcSize); + + /* Search until size increases */ + for (optLogGuess = minTableLog; optLogGuess <= maxTableLog; optLogGuess++) { + DEBUGLOG(7, "checking for huffLog=%u", optLogGuess); + maxBits = HUF_buildCTable_wksp(table, count, maxSymbolValue, optLogGuess, workSpace, wkspSize); + if (ERR_isError(maxBits)) continue; + + if (maxBits < optLogGuess && optLogGuess > minTableLog) break; + + hSize = HUF_writeCTable_wksp(dst, dstSize, table, maxSymbolValue, (U32)maxBits, workSpace, wkspSize); + + if (ERR_isError(hSize)) continue; + + newSize = HUF_estimateCompressedSize(table, count, maxSymbolValue) + hSize; + + if (newSize > optSize + 1) { + break; + } + + if (newSize < optSize) { + optSize = newSize; + optLog = optLogGuess; + } + } + assert(optLog <= HUF_TABLELOG_MAX); + return optLog; + } +} + /* HUF_compress_internal() : * `workSpace_align4` must be aligned on 4-bytes boundaries, * and occupies the same space as a table of HUF_WORKSPACE_SIZE_U64 unsigned */ @@ -1180,14 +1300,14 @@ HUF_compress_internal (void* dst, size_t dstSize, unsigned maxSymbolValue, unsigned huffLog, HUF_nbStreams_e nbStreams, void* workSpace, size_t wkspSize, - HUF_CElt* oldHufTable, HUF_repeat* repeat, int preferRepeat, - const int bmi2, unsigned suspectUncompressible) + HUF_CElt* oldHufTable, HUF_repeat* repeat, int flags) { HUF_compress_tables_t* const table = (HUF_compress_tables_t*)HUF_alignUpWorkspace(workSpace, &wkspSize, ZSTD_ALIGNOF(size_t)); BYTE* const ostart = (BYTE*)dst; BYTE* const oend = ostart + dstSize; BYTE* op = ostart; + DEBUGLOG(5, "HUF_compress_internal (srcSize=%zu)", srcSize); HUF_STATIC_ASSERT(sizeof(*table) + HUF_WORKSPACE_MAX_ALIGNMENT <= HUF_WORKSPACE_SIZE); /* checks & inits */ @@ -1201,16 +1321,17 @@ HUF_compress_internal (void* dst, size_t dstSize, if (!huffLog) huffLog = HUF_TABLELOG_DEFAULT; /* Heuristic : If old table is valid, use it for small inputs */ - if (preferRepeat && repeat && *repeat == HUF_repeat_valid) { + if ((flags & HUF_flags_preferRepeat) && repeat && *repeat == HUF_repeat_valid) { return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, - nbStreams, oldHufTable, bmi2); + nbStreams, oldHufTable, flags); } /* If uncompressible data is suspected, do a smaller sampling first */ DEBUG_STATIC_ASSERT(SUSPECT_INCOMPRESSIBLE_SAMPLE_RATIO >= 2); - if (suspectUncompressible && srcSize >= (SUSPECT_INCOMPRESSIBLE_SAMPLE_SIZE * SUSPECT_INCOMPRESSIBLE_SAMPLE_RATIO)) { + if ((flags & HUF_flags_suspectUncompressible) && srcSize >= (SUSPECT_INCOMPRESSIBLE_SAMPLE_SIZE * SUSPECT_INCOMPRESSIBLE_SAMPLE_RATIO)) { size_t largestTotal = 0; + DEBUGLOG(5, "input suspected incompressible : sampling to check"); { unsigned maxSymbolValueBegin = maxSymbolValue; CHECK_V_F(largestBegin, HIST_count_simple (table->count, &maxSymbolValueBegin, (const BYTE*)src, SUSPECT_INCOMPRESSIBLE_SAMPLE_SIZE) ); largestTotal += largestBegin; @@ -1227,6 +1348,7 @@ HUF_compress_internal (void* dst, size_t dstSize, if (largest == srcSize) { *ostart = ((const BYTE*)src)[0]; return 1; } /* single symbol, rle */ if (largest <= (srcSize >> 7)+4) return 0; /* heuristic : probably not compressible enough */ } + DEBUGLOG(6, "histogram detail completed (%zu symbols)", showU32(table->count, maxSymbolValue+1)); /* Check validity of previous table */ if ( repeat @@ -1235,19 +1357,20 @@ HUF_compress_internal (void* dst, size_t dstSize, *repeat = HUF_repeat_none; } /* Heuristic : use existing table for small inputs */ - if (preferRepeat && repeat && *repeat != HUF_repeat_none) { + if ((flags & HUF_flags_preferRepeat) && repeat && *repeat != HUF_repeat_none) { return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, - nbStreams, oldHufTable, bmi2); + nbStreams, oldHufTable, flags); } /* Build Huffman Tree */ - huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue); + huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue, &table->wksps, sizeof(table->wksps), table->CTable, table->count, flags); { size_t const maxBits = HUF_buildCTable_wksp(table->CTable, table->count, maxSymbolValue, huffLog, &table->wksps.buildCTable_wksp, sizeof(table->wksps.buildCTable_wksp)); CHECK_F(maxBits); huffLog = (U32)maxBits; + DEBUGLOG(6, "bit distribution completed (%zu symbols)", showCTableBits(table->CTable + 1, maxSymbolValue+1)); } /* Zero unused symbols in CTable, so we can check it for validity */ { @@ -1266,7 +1389,7 @@ HUF_compress_internal (void* dst, size_t dstSize, if (oldSize <= hSize + newSize || hSize + 12 >= srcSize) { return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, - nbStreams, oldHufTable, bmi2); + nbStreams, oldHufTable, flags); } } /* Use the new huffman table */ @@ -1278,46 +1401,20 @@ HUF_compress_internal (void* dst, size_t dstSize, } return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, - nbStreams, table->CTable, bmi2); -} - - -size_t HUF_compress1X_wksp (void* dst, size_t dstSize, - const void* src, size_t srcSize, - unsigned maxSymbolValue, unsigned huffLog, - void* workSpace, size_t wkspSize) -{ - return HUF_compress_internal(dst, dstSize, src, srcSize, - maxSymbolValue, huffLog, HUF_singleStream, - workSpace, wkspSize, - NULL, NULL, 0, 0 /*bmi2*/, 0); + nbStreams, table->CTable, flags); } size_t HUF_compress1X_repeat (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, void* workSpace, size_t wkspSize, - HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, - int bmi2, unsigned suspectUncompressible) + HUF_CElt* hufTable, HUF_repeat* repeat, int flags) { + DEBUGLOG(5, "HUF_compress1X_repeat (srcSize = %zu)", srcSize); return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, HUF_singleStream, workSpace, wkspSize, hufTable, - repeat, preferRepeat, bmi2, suspectUncompressible); -} - -/* HUF_compress4X_repeat(): - * compress input using 4 streams. - * provide workspace to generate compression tables */ -size_t HUF_compress4X_wksp (void* dst, size_t dstSize, - const void* src, size_t srcSize, - unsigned maxSymbolValue, unsigned huffLog, - void* workSpace, size_t wkspSize) -{ - return HUF_compress_internal(dst, dstSize, src, srcSize, - maxSymbolValue, huffLog, HUF_fourStreams, - workSpace, wkspSize, - NULL, NULL, 0, 0 /*bmi2*/, 0); + repeat, flags); } /* HUF_compress4X_repeat(): @@ -1328,43 +1425,11 @@ size_t HUF_compress4X_repeat (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, void* workSpace, size_t wkspSize, - HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2, unsigned suspectUncompressible) + HUF_CElt* hufTable, HUF_repeat* repeat, int flags) { + DEBUGLOG(5, "HUF_compress4X_repeat (srcSize = %zu)", srcSize); return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, HUF_fourStreams, workSpace, wkspSize, - hufTable, repeat, preferRepeat, bmi2, suspectUncompressible); + hufTable, repeat, flags); } - -#ifndef ZSTD_NO_UNUSED_FUNCTIONS -/** HUF_buildCTable() : - * @return : maxNbBits - * Note : count is used before tree is written, so they can safely overlap - */ -size_t HUF_buildCTable (HUF_CElt* tree, const unsigned* count, unsigned maxSymbolValue, unsigned maxNbBits) -{ - HUF_buildCTable_wksp_tables workspace; - return HUF_buildCTable_wksp(tree, count, maxSymbolValue, maxNbBits, &workspace, sizeof(workspace)); -} - -size_t HUF_compress1X (void* dst, size_t dstSize, - const void* src, size_t srcSize, - unsigned maxSymbolValue, unsigned huffLog) -{ - U64 workSpace[HUF_WORKSPACE_SIZE_U64]; - return HUF_compress1X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, workSpace, sizeof(workSpace)); -} - -size_t HUF_compress2 (void* dst, size_t dstSize, - const void* src, size_t srcSize, - unsigned maxSymbolValue, unsigned huffLog) -{ - U64 workSpace[HUF_WORKSPACE_SIZE_U64]; - return HUF_compress4X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, workSpace, sizeof(workSpace)); -} - -size_t HUF_compress (void* dst, size_t maxDstSize, const void* src, size_t srcSize) -{ - return HUF_compress2(dst, maxDstSize, src, srcSize, 255, HUF_TABLELOG_DEFAULT); -} -#endif diff --git a/internal-complibs/zstd-1.5.2/compress/zstd_compress.c b/internal-complibs/zstd-1.5.5/compress/zstd_compress.c similarity index 81% rename from internal-complibs/zstd-1.5.2/compress/zstd_compress.c rename to internal-complibs/zstd-1.5.5/compress/zstd_compress.c index f06456af..d6133e70 100644 --- a/internal-complibs/zstd-1.5.2/compress/zstd_compress.c +++ b/internal-complibs/zstd-1.5.5/compress/zstd_compress.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -11,12 +11,12 @@ /*-************************************* * Dependencies ***************************************/ +#include "../common/allocations.h" /* ZSTD_customMalloc, ZSTD_customCalloc, ZSTD_customFree */ #include "../common/zstd_deps.h" /* INT_MAX, ZSTD_memset, ZSTD_memcpy */ #include "../common/mem.h" #include "hist.h" /* HIST_countFast_wksp */ #define FSE_STATIC_LINKING_ONLY /* FSE_encodeSymbol */ #include "../common/fse.h" -#define HUF_STATIC_LINKING_ONLY #include "../common/huf.h" #include "zstd_compress_internal.h" #include "zstd_compress_sequences.h" @@ -27,6 +27,7 @@ #include "zstd_opt.h" #include "zstd_ldm.h" #include "zstd_compress_superblock.h" +#include "../common/bits.h" /* ZSTD_highbit32, ZSTD_rotateRight_U64 */ /* *************************************************************** * Tuning parameters @@ -58,14 +59,17 @@ * Helper functions ***************************************/ /* ZSTD_compressBound() - * Note that the result from this function is only compatible with the "normal" - * full-block strategy. - * When there are a lot of small blocks due to frequent flush in streaming mode - * the overhead of headers can make the compressed data to be larger than the - * return value of ZSTD_compressBound(). + * Note that the result from this function is only valid for + * the one-pass compression functions. + * When employing the streaming mode, + * if flushes are frequently altering the size of blocks, + * the overhead from block headers can make the compressed data larger + * than the return value of ZSTD_compressBound(). */ size_t ZSTD_compressBound(size_t srcSize) { - return ZSTD_COMPRESSBOUND(srcSize); + size_t const r = ZSTD_COMPRESSBOUND(srcSize); + if (r==0) return ERROR(srcSize_wrong); + return r; } @@ -177,12 +181,9 @@ size_t ZSTD_freeCCtx(ZSTD_CCtx* cctx) if (cctx==NULL) return 0; /* support free on NULL */ RETURN_ERROR_IF(cctx->staticSize, memory_allocation, "not compatible with static CCtx"); - { - int cctxInWorkspace = ZSTD_cwksp_owns_buffer(&cctx->workspace, cctx); + { int cctxInWorkspace = ZSTD_cwksp_owns_buffer(&cctx->workspace, cctx); ZSTD_freeCCtxContent(cctx); - if (!cctxInWorkspace) { - ZSTD_customFree(cctx, cctx->customMem); - } + if (!cctxInWorkspace) ZSTD_customFree(cctx, cctx->customMem); } return 0; } @@ -267,9 +268,9 @@ static int ZSTD_allocateChainTable(const ZSTD_strategy strategy, return forDDSDict || ((strategy != ZSTD_fast) && !ZSTD_rowMatchFinderUsed(strategy, useRowMatchFinder)); } -/* Returns 1 if compression parameters are such that we should +/* Returns ZSTD_ps_enable if compression parameters are such that we should * enable long distance matching (wlog >= 27, strategy >= btopt). - * Returns 0 otherwise. + * Returns ZSTD_ps_disable otherwise. */ static ZSTD_paramSwitch_e ZSTD_resolveEnableLdm(ZSTD_paramSwitch_e mode, const ZSTD_compressionParameters* const cParams) { @@ -277,6 +278,34 @@ static ZSTD_paramSwitch_e ZSTD_resolveEnableLdm(ZSTD_paramSwitch_e mode, return (cParams->strategy >= ZSTD_btopt && cParams->windowLog >= 27) ? ZSTD_ps_enable : ZSTD_ps_disable; } +static int ZSTD_resolveExternalSequenceValidation(int mode) { + return mode; +} + +/* Resolves maxBlockSize to the default if no value is present. */ +static size_t ZSTD_resolveMaxBlockSize(size_t maxBlockSize) { + if (maxBlockSize == 0) { + return ZSTD_BLOCKSIZE_MAX; + } else { + return maxBlockSize; + } +} + +static ZSTD_paramSwitch_e ZSTD_resolveExternalRepcodeSearch(ZSTD_paramSwitch_e value, int cLevel) { + if (value != ZSTD_ps_auto) return value; + if (cLevel < 10) { + return ZSTD_ps_disable; + } else { + return ZSTD_ps_enable; + } +} + +/* Returns 1 if compression parameters are such that CDict hashtable and chaintable indices are tagged. + * If so, the tags need to be removed in ZSTD_resetCCtx_byCopyingCDict. */ +static int ZSTD_CDictIndicesAreTagged(const ZSTD_compressionParameters* const cParams) { + return cParams->strategy == ZSTD_fast || cParams->strategy == ZSTD_dfast; +} + static ZSTD_CCtx_params ZSTD_makeCCtxParamsFromCParams( ZSTD_compressionParameters cParams) { @@ -294,6 +323,10 @@ static ZSTD_CCtx_params ZSTD_makeCCtxParamsFromCParams( } cctxParams.useBlockSplitter = ZSTD_resolveBlockSplitterMode(cctxParams.useBlockSplitter, &cParams); cctxParams.useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(cctxParams.useRowMatchFinder, &cParams); + cctxParams.validateSequences = ZSTD_resolveExternalSequenceValidation(cctxParams.validateSequences); + cctxParams.maxBlockSize = ZSTD_resolveMaxBlockSize(cctxParams.maxBlockSize); + cctxParams.searchForExternalRepcodes = ZSTD_resolveExternalRepcodeSearch(cctxParams.searchForExternalRepcodes, + cctxParams.compressionLevel); assert(!ZSTD_checkCParams(cParams)); return cctxParams; } @@ -339,10 +372,13 @@ size_t ZSTD_CCtxParams_init(ZSTD_CCtx_params* cctxParams, int compressionLevel) #define ZSTD_NO_CLEVEL 0 /** - * Initializes the cctxParams from params and compressionLevel. + * Initializes `cctxParams` from `params` and `compressionLevel`. * @param compressionLevel If params are derived from a compression level then that compression level, otherwise ZSTD_NO_CLEVEL. */ -static void ZSTD_CCtxParams_init_internal(ZSTD_CCtx_params* cctxParams, ZSTD_parameters const* params, int compressionLevel) +static void +ZSTD_CCtxParams_init_internal(ZSTD_CCtx_params* cctxParams, + const ZSTD_parameters* params, + int compressionLevel) { assert(!ZSTD_checkCParams(params->cParams)); ZSTD_memset(cctxParams, 0, sizeof(*cctxParams)); @@ -355,6 +391,9 @@ static void ZSTD_CCtxParams_init_internal(ZSTD_CCtx_params* cctxParams, ZSTD_par cctxParams->useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(cctxParams->useRowMatchFinder, ¶ms->cParams); cctxParams->useBlockSplitter = ZSTD_resolveBlockSplitterMode(cctxParams->useBlockSplitter, ¶ms->cParams); cctxParams->ldmParams.enableLdm = ZSTD_resolveEnableLdm(cctxParams->ldmParams.enableLdm, ¶ms->cParams); + cctxParams->validateSequences = ZSTD_resolveExternalSequenceValidation(cctxParams->validateSequences); + cctxParams->maxBlockSize = ZSTD_resolveMaxBlockSize(cctxParams->maxBlockSize); + cctxParams->searchForExternalRepcodes = ZSTD_resolveExternalRepcodeSearch(cctxParams->searchForExternalRepcodes, compressionLevel); DEBUGLOG(4, "ZSTD_CCtxParams_init_internal: useRowMatchFinder=%d, useBlockSplitter=%d ldm=%d", cctxParams->useRowMatchFinder, cctxParams->useBlockSplitter, cctxParams->ldmParams.enableLdm); } @@ -369,7 +408,7 @@ size_t ZSTD_CCtxParams_init_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_paramete /** * Sets cctxParams' cParams and fParams from params, but otherwise leaves them alone. - * @param param Validated zstd parameters. + * @param params Validated zstd parameters. */ static void ZSTD_CCtxParams_setZstdParams( ZSTD_CCtx_params* cctxParams, const ZSTD_parameters* params) @@ -478,8 +517,8 @@ ZSTD_bounds ZSTD_cParam_getBounds(ZSTD_cParameter param) return bounds; case ZSTD_c_enableLongDistanceMatching: - bounds.lowerBound = 0; - bounds.upperBound = 1; + bounds.lowerBound = (int)ZSTD_ps_auto; + bounds.upperBound = (int)ZSTD_ps_disable; return bounds; case ZSTD_c_ldmHashLog: @@ -572,6 +611,26 @@ ZSTD_bounds ZSTD_cParam_getBounds(ZSTD_cParameter param) bounds.upperBound = 1; return bounds; + case ZSTD_c_prefetchCDictTables: + bounds.lowerBound = (int)ZSTD_ps_auto; + bounds.upperBound = (int)ZSTD_ps_disable; + return bounds; + + case ZSTD_c_enableSeqProducerFallback: + bounds.lowerBound = 0; + bounds.upperBound = 1; + return bounds; + + case ZSTD_c_maxBlockSize: + bounds.lowerBound = ZSTD_BLOCKSIZE_MAX_MIN; + bounds.upperBound = ZSTD_BLOCKSIZE_MAX; + return bounds; + + case ZSTD_c_searchForExternalRepcodes: + bounds.lowerBound = (int)ZSTD_ps_auto; + bounds.upperBound = (int)ZSTD_ps_disable; + return bounds; + default: bounds.error = ERROR(parameter_unsupported); return bounds; @@ -636,6 +695,10 @@ static int ZSTD_isUpdateAuthorized(ZSTD_cParameter param) case ZSTD_c_useBlockSplitter: case ZSTD_c_useRowMatchFinder: case ZSTD_c_deterministicRefPrefix: + case ZSTD_c_prefetchCDictTables: + case ZSTD_c_enableSeqProducerFallback: + case ZSTD_c_maxBlockSize: + case ZSTD_c_searchForExternalRepcodes: default: return 0; } @@ -648,7 +711,7 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, int value) if (ZSTD_isUpdateAuthorized(param)) { cctx->cParamsChanged = 1; } else { - RETURN_ERROR(stage_wrong, "can only set params in ctx init stage"); + RETURN_ERROR(stage_wrong, "can only set params in cctx init stage"); } } switch(param) @@ -691,6 +754,10 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, int value) case ZSTD_c_useBlockSplitter: case ZSTD_c_useRowMatchFinder: case ZSTD_c_deterministicRefPrefix: + case ZSTD_c_prefetchCDictTables: + case ZSTD_c_enableSeqProducerFallback: + case ZSTD_c_maxBlockSize: + case ZSTD_c_searchForExternalRepcodes: break; default: RETURN_ERROR(parameter_unsupported, "unknown parameter"); @@ -746,12 +813,12 @@ size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* CCtxParams, case ZSTD_c_minMatch : if (value!=0) /* 0 => use default */ BOUNDCHECK(ZSTD_c_minMatch, value); - CCtxParams->cParams.minMatch = value; + CCtxParams->cParams.minMatch = (U32)value; return CCtxParams->cParams.minMatch; case ZSTD_c_targetLength : BOUNDCHECK(ZSTD_c_targetLength, value); - CCtxParams->cParams.targetLength = value; + CCtxParams->cParams.targetLength = (U32)value; return CCtxParams->cParams.targetLength; case ZSTD_c_strategy : @@ -764,12 +831,12 @@ size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* CCtxParams, /* Content size written in frame header _when known_ (default:1) */ DEBUGLOG(4, "set content size flag = %u", (value!=0)); CCtxParams->fParams.contentSizeFlag = value != 0; - return CCtxParams->fParams.contentSizeFlag; + return (size_t)CCtxParams->fParams.contentSizeFlag; case ZSTD_c_checksumFlag : /* A 32-bits content checksum will be calculated and written at end of frame (default:0) */ CCtxParams->fParams.checksumFlag = value != 0; - return CCtxParams->fParams.checksumFlag; + return (size_t)CCtxParams->fParams.checksumFlag; case ZSTD_c_dictIDFlag : /* When applicable, dictionary's dictID is provided in frame header (default:1) */ DEBUGLOG(4, "set dictIDFlag = %u", (value!=0)); @@ -778,18 +845,18 @@ size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* CCtxParams, case ZSTD_c_forceMaxWindow : CCtxParams->forceWindow = (value != 0); - return CCtxParams->forceWindow; + return (size_t)CCtxParams->forceWindow; case ZSTD_c_forceAttachDict : { const ZSTD_dictAttachPref_e pref = (ZSTD_dictAttachPref_e)value; - BOUNDCHECK(ZSTD_c_forceAttachDict, pref); + BOUNDCHECK(ZSTD_c_forceAttachDict, (int)pref); CCtxParams->attachDictPref = pref; return CCtxParams->attachDictPref; } case ZSTD_c_literalCompressionMode : { const ZSTD_paramSwitch_e lcm = (ZSTD_paramSwitch_e)value; - BOUNDCHECK(ZSTD_c_literalCompressionMode, lcm); + BOUNDCHECK(ZSTD_c_literalCompressionMode, (int)lcm); CCtxParams->literalCompressionMode = lcm; return CCtxParams->literalCompressionMode; } @@ -840,47 +907,48 @@ size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* CCtxParams, case ZSTD_c_enableDedicatedDictSearch : CCtxParams->enableDedicatedDictSearch = (value!=0); - return CCtxParams->enableDedicatedDictSearch; + return (size_t)CCtxParams->enableDedicatedDictSearch; case ZSTD_c_enableLongDistanceMatching : + BOUNDCHECK(ZSTD_c_enableLongDistanceMatching, value); CCtxParams->ldmParams.enableLdm = (ZSTD_paramSwitch_e)value; return CCtxParams->ldmParams.enableLdm; case ZSTD_c_ldmHashLog : if (value!=0) /* 0 ==> auto */ BOUNDCHECK(ZSTD_c_ldmHashLog, value); - CCtxParams->ldmParams.hashLog = value; + CCtxParams->ldmParams.hashLog = (U32)value; return CCtxParams->ldmParams.hashLog; case ZSTD_c_ldmMinMatch : if (value!=0) /* 0 ==> default */ BOUNDCHECK(ZSTD_c_ldmMinMatch, value); - CCtxParams->ldmParams.minMatchLength = value; + CCtxParams->ldmParams.minMatchLength = (U32)value; return CCtxParams->ldmParams.minMatchLength; case ZSTD_c_ldmBucketSizeLog : if (value!=0) /* 0 ==> default */ BOUNDCHECK(ZSTD_c_ldmBucketSizeLog, value); - CCtxParams->ldmParams.bucketSizeLog = value; + CCtxParams->ldmParams.bucketSizeLog = (U32)value; return CCtxParams->ldmParams.bucketSizeLog; case ZSTD_c_ldmHashRateLog : if (value!=0) /* 0 ==> default */ BOUNDCHECK(ZSTD_c_ldmHashRateLog, value); - CCtxParams->ldmParams.hashRateLog = value; + CCtxParams->ldmParams.hashRateLog = (U32)value; return CCtxParams->ldmParams.hashRateLog; case ZSTD_c_targetCBlockSize : if (value!=0) /* 0 ==> default */ BOUNDCHECK(ZSTD_c_targetCBlockSize, value); - CCtxParams->targetCBlockSize = value; + CCtxParams->targetCBlockSize = (U32)value; return CCtxParams->targetCBlockSize; case ZSTD_c_srcSizeHint : if (value!=0) /* 0 ==> default */ BOUNDCHECK(ZSTD_c_srcSizeHint, value); CCtxParams->srcSizeHint = value; - return CCtxParams->srcSizeHint; + return (size_t)CCtxParams->srcSizeHint; case ZSTD_c_stableInBuffer: BOUNDCHECK(ZSTD_c_stableInBuffer, value); @@ -917,6 +985,27 @@ size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* CCtxParams, CCtxParams->deterministicRefPrefix = !!value; return CCtxParams->deterministicRefPrefix; + case ZSTD_c_prefetchCDictTables: + BOUNDCHECK(ZSTD_c_prefetchCDictTables, value); + CCtxParams->prefetchCDictTables = (ZSTD_paramSwitch_e)value; + return CCtxParams->prefetchCDictTables; + + case ZSTD_c_enableSeqProducerFallback: + BOUNDCHECK(ZSTD_c_enableSeqProducerFallback, value); + CCtxParams->enableMatchFinderFallback = value; + return CCtxParams->enableMatchFinderFallback; + + case ZSTD_c_maxBlockSize: + if (value!=0) /* 0 ==> default */ + BOUNDCHECK(ZSTD_c_maxBlockSize, value); + CCtxParams->maxBlockSize = value; + return CCtxParams->maxBlockSize; + + case ZSTD_c_searchForExternalRepcodes: + BOUNDCHECK(ZSTD_c_searchForExternalRepcodes, value); + CCtxParams->searchForExternalRepcodes = (ZSTD_paramSwitch_e)value; + return CCtxParams->searchForExternalRepcodes; + default: RETURN_ERROR(parameter_unsupported, "unknown parameter"); } } @@ -1049,6 +1138,18 @@ size_t ZSTD_CCtxParams_getParameter( case ZSTD_c_deterministicRefPrefix: *value = (int)CCtxParams->deterministicRefPrefix; break; + case ZSTD_c_prefetchCDictTables: + *value = (int)CCtxParams->prefetchCDictTables; + break; + case ZSTD_c_enableSeqProducerFallback: + *value = CCtxParams->enableMatchFinderFallback; + break; + case ZSTD_c_maxBlockSize: + *value = (int)CCtxParams->maxBlockSize; + break; + case ZSTD_c_searchForExternalRepcodes: + *value = (int)CCtxParams->searchForExternalRepcodes; + break; default: RETURN_ERROR(parameter_unsupported, "unknown parameter"); } return 0; @@ -1075,9 +1176,47 @@ size_t ZSTD_CCtx_setParametersUsingCCtxParams( return 0; } +size_t ZSTD_CCtx_setCParams(ZSTD_CCtx* cctx, ZSTD_compressionParameters cparams) +{ + ZSTD_STATIC_ASSERT(sizeof(cparams) == 7 * 4 /* all params are listed below */); + DEBUGLOG(4, "ZSTD_CCtx_setCParams"); + /* only update if all parameters are valid */ + FORWARD_IF_ERROR(ZSTD_checkCParams(cparams), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_windowLog, cparams.windowLog), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_chainLog, cparams.chainLog), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_hashLog, cparams.hashLog), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_searchLog, cparams.searchLog), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_minMatch, cparams.minMatch), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_targetLength, cparams.targetLength), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_strategy, cparams.strategy), ""); + return 0; +} + +size_t ZSTD_CCtx_setFParams(ZSTD_CCtx* cctx, ZSTD_frameParameters fparams) +{ + ZSTD_STATIC_ASSERT(sizeof(fparams) == 3 * 4 /* all params are listed below */); + DEBUGLOG(4, "ZSTD_CCtx_setFParams"); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_contentSizeFlag, fparams.contentSizeFlag != 0), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, fparams.checksumFlag != 0), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_dictIDFlag, fparams.noDictIDFlag == 0), ""); + return 0; +} + +size_t ZSTD_CCtx_setParams(ZSTD_CCtx* cctx, ZSTD_parameters params) +{ + DEBUGLOG(4, "ZSTD_CCtx_setParams"); + /* First check cParams, because we want to update all or none. */ + FORWARD_IF_ERROR(ZSTD_checkCParams(params.cParams), ""); + /* Next set fParams, because this could fail if the cctx isn't in init stage. */ + FORWARD_IF_ERROR(ZSTD_CCtx_setFParams(cctx, params.fParams), ""); + /* Finally set cParams, which should succeed. */ + FORWARD_IF_ERROR(ZSTD_CCtx_setCParams(cctx, params.cParams), ""); + return 0; +} + size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long long pledgedSrcSize) { - DEBUGLOG(4, "ZSTD_CCtx_setPledgedSrcSize to %u bytes", (U32)pledgedSrcSize); + DEBUGLOG(4, "ZSTD_CCtx_setPledgedSrcSize to %llu bytes", pledgedSrcSize); RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong, "Can't set pledgedSrcSize when not in init stage."); cctx->pledgedSrcSizePlusOne = pledgedSrcSize+1; @@ -1093,9 +1232,9 @@ static void ZSTD_dedicatedDictSearch_revertCParams( ZSTD_compressionParameters* cParams); /** - * Initializes the local dict using the requested parameters. - * NOTE: This does not use the pledged src size, because it may be used for more - * than one compression. + * Initializes the local dictionary using requested parameters. + * NOTE: Initialization does not employ the pledged src size, + * because the dictionary may be used for multiple compressions. */ static size_t ZSTD_initLocalDict(ZSTD_CCtx* cctx) { @@ -1108,8 +1247,8 @@ static size_t ZSTD_initLocalDict(ZSTD_CCtx* cctx) return 0; } if (dl->cdict != NULL) { - assert(cctx->cdict == dl->cdict); /* Local dictionary already initialized. */ + assert(cctx->cdict == dl->cdict); return 0; } assert(dl->dictSize > 0); @@ -1129,26 +1268,30 @@ static size_t ZSTD_initLocalDict(ZSTD_CCtx* cctx) } size_t ZSTD_CCtx_loadDictionary_advanced( - ZSTD_CCtx* cctx, const void* dict, size_t dictSize, - ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType) + ZSTD_CCtx* cctx, + const void* dict, size_t dictSize, + ZSTD_dictLoadMethod_e dictLoadMethod, + ZSTD_dictContentType_e dictContentType) { - RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong, - "Can't load a dictionary when ctx is not in init stage."); DEBUGLOG(4, "ZSTD_CCtx_loadDictionary_advanced (size: %u)", (U32)dictSize); - ZSTD_clearAllDicts(cctx); /* in case one already exists */ - if (dict == NULL || dictSize == 0) /* no dictionary mode */ + RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong, + "Can't load a dictionary when cctx is not in init stage."); + ZSTD_clearAllDicts(cctx); /* erase any previously set dictionary */ + if (dict == NULL || dictSize == 0) /* no dictionary */ return 0; if (dictLoadMethod == ZSTD_dlm_byRef) { cctx->localDict.dict = dict; } else { + /* copy dictionary content inside CCtx to own its lifetime */ void* dictBuffer; RETURN_ERROR_IF(cctx->staticSize, memory_allocation, - "no malloc for static CCtx"); + "static CCtx can't allocate for an internal copy of dictionary"); dictBuffer = ZSTD_customMalloc(dictSize, cctx->customMem); - RETURN_ERROR_IF(!dictBuffer, memory_allocation, "NULL pointer!"); + RETURN_ERROR_IF(dictBuffer==NULL, memory_allocation, + "allocation failed for dictionary content"); ZSTD_memcpy(dictBuffer, dict, dictSize); - cctx->localDict.dictBuffer = dictBuffer; - cctx->localDict.dict = dictBuffer; + cctx->localDict.dictBuffer = dictBuffer; /* owned ptr to free */ + cctx->localDict.dict = dictBuffer; /* read-only reference */ } cctx->localDict.dictSize = dictSize; cctx->localDict.dictContentType = dictContentType; @@ -1218,8 +1361,9 @@ size_t ZSTD_CCtx_reset(ZSTD_CCtx* cctx, ZSTD_ResetDirective reset) if ( (reset == ZSTD_reset_parameters) || (reset == ZSTD_reset_session_and_parameters) ) { RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong, - "Can't reset parameters only when not in init stage."); + "Reset parameters is only possible during init stage."); ZSTD_clearAllDicts(cctx); + ZSTD_memset(&cctx->externalMatchCtx, 0, sizeof(cctx->externalMatchCtx)); return ZSTD_CCtxParams_reset(&cctx->requestedParams); } return 0; @@ -1316,7 +1460,8 @@ static ZSTD_compressionParameters ZSTD_adjustCParams_internal(ZSTD_compressionParameters cPar, unsigned long long srcSize, size_t dictSize, - ZSTD_cParamMode_e mode) + ZSTD_cParamMode_e mode, + ZSTD_paramSwitch_e useRowMatchFinder) { const U64 minSrcSize = 513; /* (1<<9) + 1 */ const U64 maxWindowResize = 1ULL << (ZSTD_WINDOWLOG_MAX-1); @@ -1350,8 +1495,8 @@ ZSTD_adjustCParams_internal(ZSTD_compressionParameters cPar, } /* resize windowLog if input is small enough, to use less memory */ - if ( (srcSize < maxWindowResize) - && (dictSize < maxWindowResize) ) { + if ( (srcSize <= maxWindowResize) + && (dictSize <= maxWindowResize) ) { U32 const tSize = (U32)(srcSize + dictSize); static U32 const hashSizeMin = 1 << ZSTD_HASHLOG_MIN; U32 const srcLog = (tSize < hashSizeMin) ? ZSTD_HASHLOG_MIN : @@ -1369,6 +1514,42 @@ ZSTD_adjustCParams_internal(ZSTD_compressionParameters cPar, if (cPar.windowLog < ZSTD_WINDOWLOG_ABSOLUTEMIN) cPar.windowLog = ZSTD_WINDOWLOG_ABSOLUTEMIN; /* minimum wlog required for valid frame header */ + /* We can't use more than 32 bits of hash in total, so that means that we require: + * (hashLog + 8) <= 32 && (chainLog + 8) <= 32 + */ + if (mode == ZSTD_cpm_createCDict && ZSTD_CDictIndicesAreTagged(&cPar)) { + U32 const maxShortCacheHashLog = 32 - ZSTD_SHORT_CACHE_TAG_BITS; + if (cPar.hashLog > maxShortCacheHashLog) { + cPar.hashLog = maxShortCacheHashLog; + } + if (cPar.chainLog > maxShortCacheHashLog) { + cPar.chainLog = maxShortCacheHashLog; + } + } + + + /* At this point, we aren't 100% sure if we are using the row match finder. + * Unless it is explicitly disabled, conservatively assume that it is enabled. + * In this case it will only be disabled for small sources, so shrinking the + * hash log a little bit shouldn't result in any ratio loss. + */ + if (useRowMatchFinder == ZSTD_ps_auto) + useRowMatchFinder = ZSTD_ps_enable; + + /* We can't hash more than 32-bits in total. So that means that we require: + * (hashLog - rowLog + 8) <= 32 + */ + if (ZSTD_rowMatchFinderUsed(cPar.strategy, useRowMatchFinder)) { + /* Switch to 32-entry rows if searchLog is 5 (or more) */ + U32 const rowLog = BOUNDED(4, cPar.searchLog, 6); + U32 const maxRowHashLog = 32 - ZSTD_ROW_HASH_TAG_BITS; + U32 const maxHashLog = maxRowHashLog + rowLog; + assert(cPar.hashLog >= rowLog); + if (cPar.hashLog > maxHashLog) { + cPar.hashLog = maxHashLog; + } + } + return cPar; } @@ -1379,7 +1560,7 @@ ZSTD_adjustCParams(ZSTD_compressionParameters cPar, { cPar = ZSTD_clampCParams(cPar); /* resulting cPar is necessarily valid (all parameters within range) */ if (srcSize == 0) srcSize = ZSTD_CONTENTSIZE_UNKNOWN; - return ZSTD_adjustCParams_internal(cPar, srcSize, dictSize, ZSTD_cpm_unknown); + return ZSTD_adjustCParams_internal(cPar, srcSize, dictSize, ZSTD_cpm_unknown, ZSTD_ps_auto); } static ZSTD_compressionParameters ZSTD_getCParams_internal(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize, ZSTD_cParamMode_e mode); @@ -1410,7 +1591,7 @@ ZSTD_compressionParameters ZSTD_getCParamsFromCCtxParams( ZSTD_overrideCParams(&cParams, &CCtxParams->cParams); assert(!ZSTD_checkCParams(cParams)); /* srcSizeHint == 0 means 0 */ - return ZSTD_adjustCParams_internal(cParams, srcSizeHint, dictSize, mode); + return ZSTD_adjustCParams_internal(cParams, srcSizeHint, dictSize, mode, CCtxParams->useRowMatchFinder); } static size_t @@ -1439,7 +1620,7 @@ ZSTD_sizeof_matchState(const ZSTD_compressionParameters* const cParams, + ZSTD_cwksp_aligned_alloc_size((ZSTD_OPT_NUM+1) * sizeof(ZSTD_match_t)) + ZSTD_cwksp_aligned_alloc_size((ZSTD_OPT_NUM+1) * sizeof(ZSTD_optimal_t)); size_t const lazyAdditionalSpace = ZSTD_rowMatchFinderUsed(cParams->strategy, useRowMatchFinder) - ? ZSTD_cwksp_aligned_alloc_size(hSize*sizeof(U16)) + ? ZSTD_cwksp_aligned_alloc_size(hSize) : 0; size_t const optSpace = (forCCtx && (cParams->strategy >= ZSTD_btopt)) ? optPotentialSpace @@ -1455,6 +1636,13 @@ ZSTD_sizeof_matchState(const ZSTD_compressionParameters* const cParams, return tableSpace + optSpace + slackSpace + lazyAdditionalSpace; } +/* Helper function for calculating memory requirements. + * Gives a tighter bound than ZSTD_sequenceBound() by taking minMatch into account. */ +static size_t ZSTD_maxNbSeq(size_t blockSize, unsigned minMatch, int useSequenceProducer) { + U32 const divider = (minMatch==3 || useSequenceProducer) ? 3 : 4; + return blockSize / divider; +} + static size_t ZSTD_estimateCCtxSize_usingCCtxParams_internal( const ZSTD_compressionParameters* cParams, const ldmParams_t* ldmParams, @@ -1462,12 +1650,13 @@ static size_t ZSTD_estimateCCtxSize_usingCCtxParams_internal( const ZSTD_paramSwitch_e useRowMatchFinder, const size_t buffInSize, const size_t buffOutSize, - const U64 pledgedSrcSize) + const U64 pledgedSrcSize, + int useSequenceProducer, + size_t maxBlockSize) { size_t const windowSize = (size_t) BOUNDED(1ULL, 1ULL << cParams->windowLog, pledgedSrcSize); - size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, windowSize); - U32 const divider = (cParams->minMatch==3) ? 3 : 4; - size_t const maxNbSeq = blockSize / divider; + size_t const blockSize = MIN(ZSTD_resolveMaxBlockSize(maxBlockSize), windowSize); + size_t const maxNbSeq = ZSTD_maxNbSeq(blockSize, cParams->minMatch, useSequenceProducer); size_t const tokenSpace = ZSTD_cwksp_alloc_size(WILDCOPY_OVERLENGTH + blockSize) + ZSTD_cwksp_aligned_alloc_size(maxNbSeq * sizeof(seqDef)) + 3 * ZSTD_cwksp_alloc_size(maxNbSeq * sizeof(BYTE)); @@ -1486,6 +1675,11 @@ static size_t ZSTD_estimateCCtxSize_usingCCtxParams_internal( size_t const cctxSpace = isStatic ? ZSTD_cwksp_alloc_size(sizeof(ZSTD_CCtx)) : 0; + size_t const maxNbExternalSeq = ZSTD_sequenceBound(blockSize); + size_t const externalSeqSpace = useSequenceProducer + ? ZSTD_cwksp_aligned_alloc_size(maxNbExternalSeq * sizeof(ZSTD_Sequence)) + : 0; + size_t const neededSpace = cctxSpace + entropySpace + @@ -1494,7 +1688,8 @@ static size_t ZSTD_estimateCCtxSize_usingCCtxParams_internal( ldmSeqSpace + matchStateSize + tokenSpace + - bufferSpace; + bufferSpace + + externalSeqSpace; DEBUGLOG(5, "estimate workspace : %u", (U32)neededSpace); return neededSpace; @@ -1512,7 +1707,7 @@ size_t ZSTD_estimateCCtxSize_usingCCtxParams(const ZSTD_CCtx_params* params) * be needed. However, we still allocate two 0-sized buffers, which can * take space under ASAN. */ return ZSTD_estimateCCtxSize_usingCCtxParams_internal( - &cParams, ¶ms->ldmParams, 1, useRowMatchFinder, 0, 0, ZSTD_CONTENTSIZE_UNKNOWN); + &cParams, ¶ms->ldmParams, 1, useRowMatchFinder, 0, 0, ZSTD_CONTENTSIZE_UNKNOWN, params->useSequenceProducer, params->maxBlockSize); } size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams) @@ -1562,7 +1757,7 @@ size_t ZSTD_estimateCStreamSize_usingCCtxParams(const ZSTD_CCtx_params* params) RETURN_ERROR_IF(params->nbWorkers > 0, GENERIC, "Estimate CCtx size is supported for single-threaded compression only."); { ZSTD_compressionParameters const cParams = ZSTD_getCParamsFromCCtxParams(params, ZSTD_CONTENTSIZE_UNKNOWN, 0, ZSTD_cpm_noAttachDict); - size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, (size_t)1 << cParams.windowLog); + size_t const blockSize = MIN(ZSTD_resolveMaxBlockSize(params->maxBlockSize), (size_t)1 << cParams.windowLog); size_t const inBuffSize = (params->inBufferMode == ZSTD_bm_buffered) ? ((size_t)1 << cParams.windowLog) + blockSize : 0; @@ -1573,7 +1768,7 @@ size_t ZSTD_estimateCStreamSize_usingCCtxParams(const ZSTD_CCtx_params* params) return ZSTD_estimateCCtxSize_usingCCtxParams_internal( &cParams, ¶ms->ldmParams, 1, useRowMatchFinder, inBuffSize, outBuffSize, - ZSTD_CONTENTSIZE_UNKNOWN); + ZSTD_CONTENTSIZE_UNKNOWN, params->useSequenceProducer, params->maxBlockSize); } } @@ -1716,6 +1911,19 @@ typedef enum { ZSTD_resetTarget_CCtx } ZSTD_resetTarget_e; +/* Mixes bits in a 64 bits in a value, based on XXH3_rrmxmx */ +static U64 ZSTD_bitmix(U64 val, U64 len) { + val ^= ZSTD_rotateRight_U64(val, 49) ^ ZSTD_rotateRight_U64(val, 24); + val *= 0x9FB21C651E98DF25ULL; + val ^= (val >> 35) + len ; + val *= 0x9FB21C651E98DF25ULL; + return val ^ (val >> 28); +} + +/* Mixes in the hashSalt and hashSaltEntropy to create a new hashSalt */ +static void ZSTD_advanceHashSalt(ZSTD_matchState_t* ms) { + ms->hashSalt = ZSTD_bitmix(ms->hashSalt, 8) ^ ZSTD_bitmix((U64) ms->hashSaltEntropy, 4); +} static size_t ZSTD_reset_matchState(ZSTD_matchState_t* ms, @@ -1743,6 +1951,7 @@ ZSTD_reset_matchState(ZSTD_matchState_t* ms, } ms->hashLog3 = hashLog3; + ms->lazySkipping = 0; ZSTD_invalidateMatchState(ms); @@ -1764,6 +1973,27 @@ ZSTD_reset_matchState(ZSTD_matchState_t* ms, ZSTD_cwksp_clean_tables(ws); } + if (ZSTD_rowMatchFinderUsed(cParams->strategy, useRowMatchFinder)) { + /* Row match finder needs an additional table of hashes ("tags") */ + size_t const tagTableSize = hSize; + /* We want to generate a new salt in case we reset a Cctx, but we always want to use + * 0 when we reset a Cdict */ + if(forWho == ZSTD_resetTarget_CCtx) { + ms->tagTable = (BYTE*) ZSTD_cwksp_reserve_aligned_init_once(ws, tagTableSize); + ZSTD_advanceHashSalt(ms); + } else { + /* When we are not salting we want to always memset the memory */ + ms->tagTable = (BYTE*) ZSTD_cwksp_reserve_aligned(ws, tagTableSize); + ZSTD_memset(ms->tagTable, 0, tagTableSize); + ms->hashSalt = 0; + } + { /* Switch to 32-entry rows if searchLog is 5 (or more) */ + U32 const rowLog = BOUNDED(4, cParams->searchLog, 6); + assert(cParams->hashLog >= rowLog); + ms->rowHashLog = cParams->hashLog - rowLog; + } + } + /* opt parser space */ if ((forWho == ZSTD_resetTarget_CCtx) && (cParams->strategy >= ZSTD_btopt)) { DEBUGLOG(4, "reserving optimal parser space"); @@ -1775,19 +2005,6 @@ ZSTD_reset_matchState(ZSTD_matchState_t* ms, ms->opt.priceTable = (ZSTD_optimal_t*)ZSTD_cwksp_reserve_aligned(ws, (ZSTD_OPT_NUM+1) * sizeof(ZSTD_optimal_t)); } - if (ZSTD_rowMatchFinderUsed(cParams->strategy, useRowMatchFinder)) { - { /* Row match finder needs an additional table of hashes ("tags") */ - size_t const tagTableSize = hSize*sizeof(U16); - ms->tagTable = (U16*)ZSTD_cwksp_reserve_aligned(ws, tagTableSize); - if (ms->tagTable) ZSTD_memset(ms->tagTable, 0, tagTableSize); - } - { /* Switch to 32-entry rows if searchLog is 5 (or more) */ - U32 const rowLog = BOUNDED(4, cParams->searchLog, 6); - assert(cParams->hashLog >= rowLog); - ms->rowHashLog = cParams->hashLog - rowLog; - } - } - ms->cParams = *cParams; RETURN_ERROR_IF(ZSTD_cwksp_reserve_failed(ws), memory_allocation, @@ -1847,6 +2064,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, assert(params->useRowMatchFinder != ZSTD_ps_auto); assert(params->useBlockSplitter != ZSTD_ps_auto); assert(params->ldmParams.enableLdm != ZSTD_ps_auto); + assert(params->maxBlockSize != 0); if (params->ldmParams.enableLdm == ZSTD_ps_enable) { /* Adjust long distance matching parameters */ ZSTD_ldm_adjustParameters(&zc->appliedParams.ldmParams, ¶ms->cParams); @@ -1855,9 +2073,8 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, } { size_t const windowSize = MAX(1, (size_t)MIN(((U64)1 << params->cParams.windowLog), pledgedSrcSize)); - size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, windowSize); - U32 const divider = (params->cParams.minMatch==3) ? 3 : 4; - size_t const maxNbSeq = blockSize / divider; + size_t const blockSize = MIN(params->maxBlockSize, windowSize); + size_t const maxNbSeq = ZSTD_maxNbSeq(blockSize, params->cParams.minMatch, params->useSequenceProducer); size_t const buffOutSize = (zbuff == ZSTDb_buffered && params->outBufferMode == ZSTD_bm_buffered) ? ZSTD_compressBound(blockSize) + 1 : 0; @@ -1874,7 +2091,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, size_t const neededSpace = ZSTD_estimateCCtxSize_usingCCtxParams_internal( ¶ms->cParams, ¶ms->ldmParams, zc->staticSize != 0, params->useRowMatchFinder, - buffInSize, buffOutSize, pledgedSrcSize); + buffInSize, buffOutSize, pledgedSrcSize, params->useSequenceProducer, params->maxBlockSize); int resizeWorkspace; FORWARD_IF_ERROR(neededSpace, "cctx size estimate failed!"); @@ -1917,6 +2134,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, /* init params */ zc->blockState.matchState.cParams = params->cParams; + zc->blockState.matchState.prefetchCDictTables = params->prefetchCDictTables == ZSTD_ps_enable; zc->pledgedSrcSizePlusOne = pledgedSrcSize+1; zc->consumedSrcSize = 0; zc->producedCSize = 0; @@ -1933,13 +2151,46 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, ZSTD_reset_compressedBlockState(zc->blockState.prevCBlock); + FORWARD_IF_ERROR(ZSTD_reset_matchState( + &zc->blockState.matchState, + ws, + ¶ms->cParams, + params->useRowMatchFinder, + crp, + needsIndexReset, + ZSTD_resetTarget_CCtx), ""); + + zc->seqStore.sequencesStart = (seqDef*)ZSTD_cwksp_reserve_aligned(ws, maxNbSeq * sizeof(seqDef)); + + /* ldm hash table */ + if (params->ldmParams.enableLdm == ZSTD_ps_enable) { + /* TODO: avoid memset? */ + size_t const ldmHSize = ((size_t)1) << params->ldmParams.hashLog; + zc->ldmState.hashTable = (ldmEntry_t*)ZSTD_cwksp_reserve_aligned(ws, ldmHSize * sizeof(ldmEntry_t)); + ZSTD_memset(zc->ldmState.hashTable, 0, ldmHSize * sizeof(ldmEntry_t)); + zc->ldmSequences = (rawSeq*)ZSTD_cwksp_reserve_aligned(ws, maxNbLdmSeq * sizeof(rawSeq)); + zc->maxNbLdmSequences = maxNbLdmSeq; + + ZSTD_window_init(&zc->ldmState.window); + zc->ldmState.loadedDictEnd = 0; + } + + /* reserve space for block-level external sequences */ + if (params->useSequenceProducer) { + size_t const maxNbExternalSeq = ZSTD_sequenceBound(blockSize); + zc->externalMatchCtx.seqBufferCapacity = maxNbExternalSeq; + zc->externalMatchCtx.seqBuffer = + (ZSTD_Sequence*)ZSTD_cwksp_reserve_aligned(ws, maxNbExternalSeq * sizeof(ZSTD_Sequence)); + } + + /* buffers */ + /* ZSTD_wildcopy() is used to copy into the literals buffer, * so we have to oversize the buffer by WILDCOPY_OVERLENGTH bytes. */ zc->seqStore.litStart = ZSTD_cwksp_reserve_buffer(ws, blockSize + WILDCOPY_OVERLENGTH); zc->seqStore.maxNbLit = blockSize; - /* buffers */ zc->bufferedPolicy = zbuff; zc->inBuffSize = buffInSize; zc->inBuff = (char*)ZSTD_cwksp_reserve_buffer(ws, buffInSize); @@ -1962,32 +2213,9 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, zc->seqStore.llCode = ZSTD_cwksp_reserve_buffer(ws, maxNbSeq * sizeof(BYTE)); zc->seqStore.mlCode = ZSTD_cwksp_reserve_buffer(ws, maxNbSeq * sizeof(BYTE)); zc->seqStore.ofCode = ZSTD_cwksp_reserve_buffer(ws, maxNbSeq * sizeof(BYTE)); - zc->seqStore.sequencesStart = (seqDef*)ZSTD_cwksp_reserve_aligned(ws, maxNbSeq * sizeof(seqDef)); - - FORWARD_IF_ERROR(ZSTD_reset_matchState( - &zc->blockState.matchState, - ws, - ¶ms->cParams, - params->useRowMatchFinder, - crp, - needsIndexReset, - ZSTD_resetTarget_CCtx), ""); - - /* ldm hash table */ - if (params->ldmParams.enableLdm == ZSTD_ps_enable) { - /* TODO: avoid memset? */ - size_t const ldmHSize = ((size_t)1) << params->ldmParams.hashLog; - zc->ldmState.hashTable = (ldmEntry_t*)ZSTD_cwksp_reserve_aligned(ws, ldmHSize * sizeof(ldmEntry_t)); - ZSTD_memset(zc->ldmState.hashTable, 0, ldmHSize * sizeof(ldmEntry_t)); - zc->ldmSequences = (rawSeq*)ZSTD_cwksp_reserve_aligned(ws, maxNbLdmSeq * sizeof(rawSeq)); - zc->maxNbLdmSequences = maxNbLdmSeq; - - ZSTD_window_init(&zc->ldmState.window); - zc->ldmState.loadedDictEnd = 0; - } DEBUGLOG(3, "wksp: finished allocating, %zd bytes remain available", ZSTD_cwksp_available_space(ws)); - assert(ZSTD_cwksp_estimated_space_within_bounds(ws, neededSpace, resizeWorkspace)); + assert(ZSTD_cwksp_estimated_space_within_bounds(ws, neededSpace)); zc->initialized = 1; @@ -2059,7 +2287,8 @@ ZSTD_resetCCtx_byAttachingCDict(ZSTD_CCtx* cctx, } params.cParams = ZSTD_adjustCParams_internal(adjusted_cdict_cParams, pledgedSrcSize, - cdict->dictContentSize, ZSTD_cpm_attachDict); + cdict->dictContentSize, ZSTD_cpm_attachDict, + params.useRowMatchFinder); params.cParams.windowLog = windowLog; params.useRowMatchFinder = cdict->useRowMatchFinder; /* cdict overrides */ FORWARD_IF_ERROR(ZSTD_resetCCtx_internal(cctx, ¶ms, pledgedSrcSize, @@ -2098,6 +2327,22 @@ ZSTD_resetCCtx_byAttachingCDict(ZSTD_CCtx* cctx, return 0; } +static void ZSTD_copyCDictTableIntoCCtx(U32* dst, U32 const* src, size_t tableSize, + ZSTD_compressionParameters const* cParams) { + if (ZSTD_CDictIndicesAreTagged(cParams)){ + /* Remove tags from the CDict table if they are present. + * See docs on "short cache" in zstd_compress_internal.h for context. */ + size_t i; + for (i = 0; i < tableSize; i++) { + U32 const taggedIndex = src[i]; + U32 const index = taggedIndex >> ZSTD_SHORT_CACHE_TAG_BITS; + dst[i] = index; + } + } else { + ZSTD_memcpy(dst, src, tableSize * sizeof(U32)); + } +} + static size_t ZSTD_resetCCtx_byCopyingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict, ZSTD_CCtx_params params, @@ -2133,21 +2378,23 @@ static size_t ZSTD_resetCCtx_byCopyingCDict(ZSTD_CCtx* cctx, : 0; size_t const hSize = (size_t)1 << cdict_cParams->hashLog; - ZSTD_memcpy(cctx->blockState.matchState.hashTable, - cdict->matchState.hashTable, - hSize * sizeof(U32)); + ZSTD_copyCDictTableIntoCCtx(cctx->blockState.matchState.hashTable, + cdict->matchState.hashTable, + hSize, cdict_cParams); + /* Do not copy cdict's chainTable if cctx has parameters such that it would not use chainTable */ if (ZSTD_allocateChainTable(cctx->appliedParams.cParams.strategy, cctx->appliedParams.useRowMatchFinder, 0 /* forDDSDict */)) { - ZSTD_memcpy(cctx->blockState.matchState.chainTable, - cdict->matchState.chainTable, - chainSize * sizeof(U32)); + ZSTD_copyCDictTableIntoCCtx(cctx->blockState.matchState.chainTable, + cdict->matchState.chainTable, + chainSize, cdict_cParams); } /* copy tag table */ if (ZSTD_rowMatchFinderUsed(cdict_cParams->strategy, cdict->useRowMatchFinder)) { - size_t const tagTableSize = hSize*sizeof(U16); + size_t const tagTableSize = hSize; ZSTD_memcpy(cctx->blockState.matchState.tagTable, - cdict->matchState.tagTable, - tagTableSize); + cdict->matchState.tagTable, + tagTableSize); + cctx->blockState.matchState.hashSalt = cdict->matchState.hashSalt; } } @@ -2226,6 +2473,7 @@ static size_t ZSTD_copyCCtx_internal(ZSTD_CCtx* dstCCtx, params.useBlockSplitter = srcCCtx->appliedParams.useBlockSplitter; params.ldmParams = srcCCtx->appliedParams.ldmParams; params.fParams = fParams; + params.maxBlockSize = srcCCtx->appliedParams.maxBlockSize; ZSTD_resetCCtx_internal(dstCCtx, ¶ms, pledgedSrcSize, /* loadedDictSize */ 0, ZSTDcrp_leaveDirty, zbuff); @@ -2385,7 +2633,7 @@ static void ZSTD_reduceIndex (ZSTD_matchState_t* ms, ZSTD_CCtx_params const* par /* See doc/zstd_compression_format.md for detailed format description */ -void ZSTD_seqToCodes(const seqStore_t* seqStorePtr) +int ZSTD_seqToCodes(const seqStore_t* seqStorePtr) { const seqDef* const sequences = seqStorePtr->sequencesStart; BYTE* const llCodeTable = seqStorePtr->llCode; @@ -2393,18 +2641,24 @@ void ZSTD_seqToCodes(const seqStore_t* seqStorePtr) BYTE* const mlCodeTable = seqStorePtr->mlCode; U32 const nbSeq = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart); U32 u; + int longOffsets = 0; assert(nbSeq <= seqStorePtr->maxNbSeq); for (u=0; u= STREAM_ACCUMULATOR_MIN)); + if (MEM_32bits() && ofCode >= STREAM_ACCUMULATOR_MIN) + longOffsets = 1; } if (seqStorePtr->longLengthType==ZSTD_llt_literalLength) llCodeTable[seqStorePtr->longLengthPos] = MaxLL; if (seqStorePtr->longLengthType==ZSTD_llt_matchLength) mlCodeTable[seqStorePtr->longLengthPos] = MaxML; + return longOffsets; } /* ZSTD_useTargetCBlockSize(): @@ -2438,6 +2692,7 @@ typedef struct { U32 MLtype; size_t size; size_t lastCountSize; /* Accounts for bug in 1.3.4. More detail in ZSTD_entropyCompressSeqStore_internal() */ + int longOffsets; } ZSTD_symbolEncodingTypeStats_t; /* ZSTD_buildSequencesStatistics(): @@ -2448,11 +2703,13 @@ typedef struct { * entropyWkspSize must be of size at least ENTROPY_WORKSPACE_SIZE - (MaxSeq + 1)*sizeof(U32) */ static ZSTD_symbolEncodingTypeStats_t -ZSTD_buildSequencesStatistics(seqStore_t* seqStorePtr, size_t nbSeq, - const ZSTD_fseCTables_t* prevEntropy, ZSTD_fseCTables_t* nextEntropy, - BYTE* dst, const BYTE* const dstEnd, - ZSTD_strategy strategy, unsigned* countWorkspace, - void* entropyWorkspace, size_t entropyWkspSize) { +ZSTD_buildSequencesStatistics( + const seqStore_t* seqStorePtr, size_t nbSeq, + const ZSTD_fseCTables_t* prevEntropy, ZSTD_fseCTables_t* nextEntropy, + BYTE* dst, const BYTE* const dstEnd, + ZSTD_strategy strategy, unsigned* countWorkspace, + void* entropyWorkspace, size_t entropyWkspSize) +{ BYTE* const ostart = dst; const BYTE* const oend = dstEnd; BYTE* op = ostart; @@ -2466,7 +2723,7 @@ ZSTD_buildSequencesStatistics(seqStore_t* seqStorePtr, size_t nbSeq, stats.lastCountSize = 0; /* convert length/distances into codes */ - ZSTD_seqToCodes(seqStorePtr); + stats.longOffsets = ZSTD_seqToCodes(seqStorePtr); assert(op <= oend); assert(nbSeq != 0); /* ZSTD_selectEncodingType() divides by nbSeq */ /* build CTable for Literal Lengths */ @@ -2571,22 +2828,22 @@ ZSTD_buildSequencesStatistics(seqStore_t* seqStorePtr, size_t nbSeq, */ #define SUSPECT_UNCOMPRESSIBLE_LITERAL_RATIO 20 MEM_STATIC size_t -ZSTD_entropyCompressSeqStore_internal(seqStore_t* seqStorePtr, - const ZSTD_entropyCTables_t* prevEntropy, - ZSTD_entropyCTables_t* nextEntropy, - const ZSTD_CCtx_params* cctxParams, - void* dst, size_t dstCapacity, - void* entropyWorkspace, size_t entropyWkspSize, - const int bmi2) +ZSTD_entropyCompressSeqStore_internal( + const seqStore_t* seqStorePtr, + const ZSTD_entropyCTables_t* prevEntropy, + ZSTD_entropyCTables_t* nextEntropy, + const ZSTD_CCtx_params* cctxParams, + void* dst, size_t dstCapacity, + void* entropyWorkspace, size_t entropyWkspSize, + const int bmi2) { - const int longOffsets = cctxParams->cParams.windowLog > STREAM_ACCUMULATOR_MIN; ZSTD_strategy const strategy = cctxParams->cParams.strategy; unsigned* count = (unsigned*)entropyWorkspace; FSE_CTable* CTable_LitLength = nextEntropy->fse.litlengthCTable; FSE_CTable* CTable_OffsetBits = nextEntropy->fse.offcodeCTable; FSE_CTable* CTable_MatchLength = nextEntropy->fse.matchlengthCTable; const seqDef* const sequences = seqStorePtr->sequencesStart; - const size_t nbSeq = seqStorePtr->sequences - seqStorePtr->sequencesStart; + const size_t nbSeq = (size_t)(seqStorePtr->sequences - seqStorePtr->sequencesStart); const BYTE* const ofCodeTable = seqStorePtr->ofCode; const BYTE* const llCodeTable = seqStorePtr->llCode; const BYTE* const mlCodeTable = seqStorePtr->mlCode; @@ -2594,29 +2851,31 @@ ZSTD_entropyCompressSeqStore_internal(seqStore_t* seqStorePtr, BYTE* const oend = ostart + dstCapacity; BYTE* op = ostart; size_t lastCountSize; + int longOffsets = 0; entropyWorkspace = count + (MaxSeq + 1); entropyWkspSize -= (MaxSeq + 1) * sizeof(*count); - DEBUGLOG(4, "ZSTD_entropyCompressSeqStore_internal (nbSeq=%zu)", nbSeq); + DEBUGLOG(5, "ZSTD_entropyCompressSeqStore_internal (nbSeq=%zu, dstCapacity=%zu)", nbSeq, dstCapacity); ZSTD_STATIC_ASSERT(HUF_WORKSPACE_SIZE >= (1<= HUF_WORKSPACE_SIZE); /* Compress literals */ { const BYTE* const literals = seqStorePtr->litStart; - size_t const numSequences = seqStorePtr->sequences - seqStorePtr->sequencesStart; - size_t const numLiterals = seqStorePtr->lit - seqStorePtr->litStart; + size_t const numSequences = (size_t)(seqStorePtr->sequences - seqStorePtr->sequencesStart); + size_t const numLiterals = (size_t)(seqStorePtr->lit - seqStorePtr->litStart); /* Base suspicion of uncompressibility on ratio of literals to sequences */ unsigned const suspectUncompressible = (numSequences == 0) || (numLiterals / numSequences >= SUSPECT_UNCOMPRESSIBLE_LITERAL_RATIO); size_t const litSize = (size_t)(seqStorePtr->lit - literals); + size_t const cSize = ZSTD_compressLiterals( - &prevEntropy->huf, &nextEntropy->huf, - cctxParams->cParams.strategy, - ZSTD_literalsCompressionIsDisabled(cctxParams), op, dstCapacity, literals, litSize, entropyWorkspace, entropyWkspSize, - bmi2, suspectUncompressible); + &prevEntropy->huf, &nextEntropy->huf, + cctxParams->cParams.strategy, + ZSTD_literalsCompressionIsDisabled(cctxParams), + suspectUncompressible, bmi2); FORWARD_IF_ERROR(cSize, "ZSTD_compressLiterals failed"); assert(cSize <= dstCapacity); op += cSize; @@ -2642,11 +2901,10 @@ ZSTD_entropyCompressSeqStore_internal(seqStore_t* seqStorePtr, ZSTD_memcpy(&nextEntropy->fse, &prevEntropy->fse, sizeof(prevEntropy->fse)); return (size_t)(op - ostart); } - { - ZSTD_symbolEncodingTypeStats_t stats; - BYTE* seqHead = op++; + { BYTE* const seqHead = op++; /* build stats for sequences */ - stats = ZSTD_buildSequencesStatistics(seqStorePtr, nbSeq, + const ZSTD_symbolEncodingTypeStats_t stats = + ZSTD_buildSequencesStatistics(seqStorePtr, nbSeq, &prevEntropy->fse, &nextEntropy->fse, op, oend, strategy, count, @@ -2655,6 +2913,7 @@ ZSTD_entropyCompressSeqStore_internal(seqStore_t* seqStorePtr, *seqHead = (BYTE)((stats.LLtype<<6) + (stats.Offtype<<4) + (stats.MLtype<<2)); lastCountSize = stats.lastCountSize; op += stats.size; + longOffsets = stats.longOffsets; } { size_t const bitstreamSize = ZSTD_encodeSequences( @@ -2689,14 +2948,15 @@ ZSTD_entropyCompressSeqStore_internal(seqStore_t* seqStorePtr, } MEM_STATIC size_t -ZSTD_entropyCompressSeqStore(seqStore_t* seqStorePtr, - const ZSTD_entropyCTables_t* prevEntropy, - ZSTD_entropyCTables_t* nextEntropy, - const ZSTD_CCtx_params* cctxParams, - void* dst, size_t dstCapacity, - size_t srcSize, - void* entropyWorkspace, size_t entropyWkspSize, - int bmi2) +ZSTD_entropyCompressSeqStore( + const seqStore_t* seqStorePtr, + const ZSTD_entropyCTables_t* prevEntropy, + ZSTD_entropyCTables_t* nextEntropy, + const ZSTD_CCtx_params* cctxParams, + void* dst, size_t dstCapacity, + size_t srcSize, + void* entropyWorkspace, size_t entropyWkspSize, + int bmi2) { size_t const cSize = ZSTD_entropyCompressSeqStore_internal( seqStorePtr, prevEntropy, nextEntropy, cctxParams, @@ -2706,15 +2966,21 @@ ZSTD_entropyCompressSeqStore(seqStore_t* seqStorePtr, /* When srcSize <= dstCapacity, there is enough space to write a raw uncompressed block. * Since we ran out of space, block must be not compressible, so fall back to raw uncompressed block. */ - if ((cSize == ERROR(dstSize_tooSmall)) & (srcSize <= dstCapacity)) + if ((cSize == ERROR(dstSize_tooSmall)) & (srcSize <= dstCapacity)) { + DEBUGLOG(4, "not enough dstCapacity (%zu) for ZSTD_entropyCompressSeqStore_internal()=> do not compress block", dstCapacity); return 0; /* block not compressed */ + } FORWARD_IF_ERROR(cSize, "ZSTD_entropyCompressSeqStore_internal failed"); /* Check compressibility */ { size_t const maxCSize = srcSize - ZSTD_minGain(srcSize, cctxParams->cParams.strategy); if (cSize >= maxCSize) return 0; /* block not compressed */ } - DEBUGLOG(4, "ZSTD_entropyCompressSeqStore() cSize: %zu", cSize); + DEBUGLOG(5, "ZSTD_entropyCompressSeqStore() cSize: %zu", cSize); + /* libzstd decoder before > v1.5.4 is not compatible with compressed blocks of size ZSTD_BLOCKSIZE_MAX exactly. + * This restriction is indirectly already fulfilled by respecting ZSTD_minGain() condition above. + */ + assert(cSize < ZSTD_BLOCKSIZE_MAX); return cSize; } @@ -2809,6 +3075,72 @@ void ZSTD_resetSeqStore(seqStore_t* ssPtr) ssPtr->longLengthType = ZSTD_llt_none; } +/* ZSTD_postProcessSequenceProducerResult() : + * Validates and post-processes sequences obtained through the external matchfinder API: + * - Checks whether nbExternalSeqs represents an error condition. + * - Appends a block delimiter to outSeqs if one is not already present. + * See zstd.h for context regarding block delimiters. + * Returns the number of sequences after post-processing, or an error code. */ +static size_t ZSTD_postProcessSequenceProducerResult( + ZSTD_Sequence* outSeqs, size_t nbExternalSeqs, size_t outSeqsCapacity, size_t srcSize +) { + RETURN_ERROR_IF( + nbExternalSeqs > outSeqsCapacity, + sequenceProducer_failed, + "External sequence producer returned error code %lu", + (unsigned long)nbExternalSeqs + ); + + RETURN_ERROR_IF( + nbExternalSeqs == 0 && srcSize > 0, + sequenceProducer_failed, + "Got zero sequences from external sequence producer for a non-empty src buffer!" + ); + + if (srcSize == 0) { + ZSTD_memset(&outSeqs[0], 0, sizeof(ZSTD_Sequence)); + return 1; + } + + { + ZSTD_Sequence const lastSeq = outSeqs[nbExternalSeqs - 1]; + + /* We can return early if lastSeq is already a block delimiter. */ + if (lastSeq.offset == 0 && lastSeq.matchLength == 0) { + return nbExternalSeqs; + } + + /* This error condition is only possible if the external matchfinder + * produced an invalid parse, by definition of ZSTD_sequenceBound(). */ + RETURN_ERROR_IF( + nbExternalSeqs == outSeqsCapacity, + sequenceProducer_failed, + "nbExternalSeqs == outSeqsCapacity but lastSeq is not a block delimiter!" + ); + + /* lastSeq is not a block delimiter, so we need to append one. */ + ZSTD_memset(&outSeqs[nbExternalSeqs], 0, sizeof(ZSTD_Sequence)); + return nbExternalSeqs + 1; + } +} + +/* ZSTD_fastSequenceLengthSum() : + * Returns sum(litLen) + sum(matchLen) + lastLits for *seqBuf*. + * Similar to another function in zstd_compress.c (determine_blockSize), + * except it doesn't check for a block delimiter to end summation. + * Removing the early exit allows the compiler to auto-vectorize (https://godbolt.org/z/cY1cajz9P). + * This function can be deleted and replaced by determine_blockSize after we resolve issue #3456. */ +static size_t ZSTD_fastSequenceLengthSum(ZSTD_Sequence const* seqBuf, size_t seqBufSize) { + size_t matchLenSum, litLenSum, i; + matchLenSum = 0; + litLenSum = 0; + for (i = 0; i < seqBufSize; i++) { + litLenSum += seqBuf[i].litLength; + matchLenSum += seqBuf[i].matchLength; + } + return litLenSum + matchLenSum; +} + typedef enum { ZSTDbss_compress, ZSTDbss_noCompress } ZSTD_buildSeqStore_e; static size_t ZSTD_buildSeqStore(ZSTD_CCtx* zc, const void* src, size_t srcSize) @@ -2818,7 +3150,9 @@ static size_t ZSTD_buildSeqStore(ZSTD_CCtx* zc, const void* src, size_t srcSize) assert(srcSize <= ZSTD_BLOCKSIZE_MAX); /* Assert that we have correctly flushed the ctx params into the ms's copy */ ZSTD_assertEqualCParams(zc->appliedParams.cParams, ms->cParams); - if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) { + /* TODO: See 3090. We reduced MIN_CBLOCK_SIZE from 3 to 2 so to compensate we are adding + * additional 1. We need to revisit and change this logic to be more consistent */ + if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1+1) { if (zc->appliedParams.cParams.strategy >= ZSTD_btopt) { ZSTD_ldm_skipRawSeqStoreBytes(&zc->externSeqStore, srcSize); } else { @@ -2854,6 +3188,15 @@ static size_t ZSTD_buildSeqStore(ZSTD_CCtx* zc, const void* src, size_t srcSize) } if (zc->externSeqStore.pos < zc->externSeqStore.size) { assert(zc->appliedParams.ldmParams.enableLdm == ZSTD_ps_disable); + + /* External matchfinder + LDM is technically possible, just not implemented yet. + * We need to revisit soon and implement it. */ + RETURN_ERROR_IF( + zc->appliedParams.useSequenceProducer, + parameter_combination_unsupported, + "Long-distance matching with external sequence producer enabled is not currently supported." + ); + /* Updates ldmSeqStore.pos */ lastLLSize = ZSTD_ldm_blockCompress(&zc->externSeqStore, @@ -2865,6 +3208,14 @@ static size_t ZSTD_buildSeqStore(ZSTD_CCtx* zc, const void* src, size_t srcSize) } else if (zc->appliedParams.ldmParams.enableLdm == ZSTD_ps_enable) { rawSeqStore_t ldmSeqStore = kNullRawSeqStore; + /* External matchfinder + LDM is technically possible, just not implemented yet. + * We need to revisit soon and implement it. */ + RETURN_ERROR_IF( + zc->appliedParams.useSequenceProducer, + parameter_combination_unsupported, + "Long-distance matching with external sequence producer enabled is not currently supported." + ); + ldmSeqStore.seq = zc->ldmSequences; ldmSeqStore.capacity = zc->maxNbLdmSequences; /* Updates ldmSeqStore.size */ @@ -2879,7 +3230,68 @@ static size_t ZSTD_buildSeqStore(ZSTD_CCtx* zc, const void* src, size_t srcSize) zc->appliedParams.useRowMatchFinder, src, srcSize); assert(ldmSeqStore.pos == ldmSeqStore.size); - } else { /* not long range mode */ + } else if (zc->appliedParams.useSequenceProducer) { + assert( + zc->externalMatchCtx.seqBufferCapacity >= ZSTD_sequenceBound(srcSize) + ); + assert(zc->externalMatchCtx.mFinder != NULL); + + { U32 const windowSize = (U32)1 << zc->appliedParams.cParams.windowLog; + + size_t const nbExternalSeqs = (zc->externalMatchCtx.mFinder)( + zc->externalMatchCtx.mState, + zc->externalMatchCtx.seqBuffer, + zc->externalMatchCtx.seqBufferCapacity, + src, srcSize, + NULL, 0, /* dict and dictSize, currently not supported */ + zc->appliedParams.compressionLevel, + windowSize + ); + + size_t const nbPostProcessedSeqs = ZSTD_postProcessSequenceProducerResult( + zc->externalMatchCtx.seqBuffer, + nbExternalSeqs, + zc->externalMatchCtx.seqBufferCapacity, + srcSize + ); + + /* Return early if there is no error, since we don't need to worry about last literals */ + if (!ZSTD_isError(nbPostProcessedSeqs)) { + ZSTD_sequencePosition seqPos = {0,0,0}; + size_t const seqLenSum = ZSTD_fastSequenceLengthSum(zc->externalMatchCtx.seqBuffer, nbPostProcessedSeqs); + RETURN_ERROR_IF(seqLenSum > srcSize, externalSequences_invalid, "External sequences imply too large a block!"); + FORWARD_IF_ERROR( + ZSTD_copySequencesToSeqStoreExplicitBlockDelim( + zc, &seqPos, + zc->externalMatchCtx.seqBuffer, nbPostProcessedSeqs, + src, srcSize, + zc->appliedParams.searchForExternalRepcodes + ), + "Failed to copy external sequences to seqStore!" + ); + ms->ldmSeqStore = NULL; + DEBUGLOG(5, "Copied %lu sequences from external sequence producer to internal seqStore.", (unsigned long)nbExternalSeqs); + return ZSTDbss_compress; + } + + /* Propagate the error if fallback is disabled */ + if (!zc->appliedParams.enableMatchFinderFallback) { + return nbPostProcessedSeqs; + } + + /* Fallback to software matchfinder */ + { ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, + zc->appliedParams.useRowMatchFinder, + dictMode); + ms->ldmSeqStore = NULL; + DEBUGLOG( + 5, + "External sequence producer returned error code %lu. Falling back to internal parser.", + (unsigned long)nbExternalSeqs + ); + lastLLSize = blockCompressor(ms, &zc->seqStore, zc->blockState.nextCBlock->rep, src, srcSize); + } } + } else { /* not long range mode and no external matchfinder */ ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, zc->appliedParams.useRowMatchFinder, dictMode); @@ -2940,7 +3352,7 @@ static void ZSTD_copyBlockSequences(ZSTD_CCtx* zc) /* seqStoreSeqs[i].offset == offCode+1, and ZSTD_updateRep() expects offCode so we provide seqStoreSeqs[i].offset - 1 */ ZSTD_updateRep(updatedRepcodes.rep, - seqStoreSeqs[i].offBase - 1, + seqStoreSeqs[i].offBase, seqStoreSeqs[i].litLength == 0); literalsRead += outSeqs[i].litLength; } @@ -2956,6 +3368,10 @@ static void ZSTD_copyBlockSequences(ZSTD_CCtx* zc) zc->seqCollector.seqIndex += seqStoreSeqSize; } +size_t ZSTD_sequenceBound(size_t srcSize) { + return (srcSize / ZSTD_MINMATCH_MIN) + 1; +} + size_t ZSTD_generateSequences(ZSTD_CCtx* zc, ZSTD_Sequence* outSeqs, size_t outSeqsSize, const void* src, size_t srcSize) { @@ -3001,19 +3417,17 @@ static int ZSTD_isRLE(const BYTE* src, size_t length) { const size_t unrollMask = unrollSize - 1; const size_t prefixLength = length & unrollMask; size_t i; - size_t u; if (length == 1) return 1; /* Check if prefix is RLE first before using unrolled loop */ if (prefixLength && ZSTD_count(ip+1, ip, ip+prefixLength) != prefixLength-1) { return 0; } for (i = prefixLength; i != length; i += unrollSize) { + size_t u; for (u = 0; u < unrollSize; u += sizeof(size_t)) { if (MEM_readST(ip + i + u) != valueST) { return 0; - } - } - } + } } } return 1; } @@ -3029,7 +3443,8 @@ static int ZSTD_maybeRLE(seqStore_t const* seqStore) return nbSeqs < 4 && nbLits < 10; } -static void ZSTD_blockState_confirmRepcodesAndEntropyTables(ZSTD_blockState_t* const bs) +static void +ZSTD_blockState_confirmRepcodesAndEntropyTables(ZSTD_blockState_t* const bs) { ZSTD_compressedBlockState_t* const tmp = bs->prevCBlock; bs->prevCBlock = bs->nextCBlock; @@ -3037,7 +3452,9 @@ static void ZSTD_blockState_confirmRepcodesAndEntropyTables(ZSTD_blockState_t* c } /* Writes the block header */ -static void writeBlockHeader(void* op, size_t cSize, size_t blockSize, U32 lastBlock) { +static void +writeBlockHeader(void* op, size_t cSize, size_t blockSize, U32 lastBlock) +{ U32 const cBlockHeader = cSize == 1 ? lastBlock + (((U32)bt_rle)<<1) + (U32)(blockSize << 3) : lastBlock + (((U32)bt_compressed)<<1) + (U32)(cSize << 3); @@ -3050,13 +3467,16 @@ static void writeBlockHeader(void* op, size_t cSize, size_t blockSize, U32 lastB * Stores literals block type (raw, rle, compressed, repeat) and * huffman description table to hufMetadata. * Requires ENTROPY_WORKSPACE_SIZE workspace - * @return : size of huffman description table or error code */ -static size_t ZSTD_buildBlockEntropyStats_literals(void* const src, size_t srcSize, - const ZSTD_hufCTables_t* prevHuf, - ZSTD_hufCTables_t* nextHuf, - ZSTD_hufCTablesMetadata_t* hufMetadata, - const int literalsCompressionIsDisabled, - void* workspace, size_t wkspSize) + * @return : size of huffman description table, or an error code + */ +static size_t +ZSTD_buildBlockEntropyStats_literals(void* const src, size_t srcSize, + const ZSTD_hufCTables_t* prevHuf, + ZSTD_hufCTables_t* nextHuf, + ZSTD_hufCTablesMetadata_t* hufMetadata, + const int literalsCompressionIsDisabled, + void* workspace, size_t wkspSize, + int hufFlags) { BYTE* const wkspStart = (BYTE*)workspace; BYTE* const wkspEnd = wkspStart + wkspSize; @@ -3064,9 +3484,9 @@ static size_t ZSTD_buildBlockEntropyStats_literals(void* const src, size_t srcSi unsigned* const countWksp = (unsigned*)workspace; const size_t countWkspSize = (HUF_SYMBOLVALUE_MAX + 1) * sizeof(unsigned); BYTE* const nodeWksp = countWkspStart + countWkspSize; - const size_t nodeWkspSize = wkspEnd-nodeWksp; + const size_t nodeWkspSize = (size_t)(wkspEnd - nodeWksp); unsigned maxSymbolValue = HUF_SYMBOLVALUE_MAX; - unsigned huffLog = HUF_TABLELOG_DEFAULT; + unsigned huffLog = LitHufLog; HUF_repeat repeat = prevHuf->repeatMode; DEBUGLOG(5, "ZSTD_buildBlockEntropyStats_literals (srcSize=%zu)", srcSize); @@ -3081,73 +3501,77 @@ static size_t ZSTD_buildBlockEntropyStats_literals(void* const src, size_t srcSi /* small ? don't even attempt compression (speed opt) */ #ifndef COMPRESS_LITERALS_SIZE_MIN -#define COMPRESS_LITERALS_SIZE_MIN 63 +# define COMPRESS_LITERALS_SIZE_MIN 63 /* heuristic */ #endif { size_t const minLitSize = (prevHuf->repeatMode == HUF_repeat_valid) ? 6 : COMPRESS_LITERALS_SIZE_MIN; if (srcSize <= minLitSize) { DEBUGLOG(5, "set_basic - too small"); hufMetadata->hType = set_basic; return 0; - } - } + } } /* Scan input and build symbol stats */ - { size_t const largest = HIST_count_wksp (countWksp, &maxSymbolValue, (const BYTE*)src, srcSize, workspace, wkspSize); + { size_t const largest = + HIST_count_wksp (countWksp, &maxSymbolValue, + (const BYTE*)src, srcSize, + workspace, wkspSize); FORWARD_IF_ERROR(largest, "HIST_count_wksp failed"); if (largest == srcSize) { + /* only one literal symbol */ DEBUGLOG(5, "set_rle"); hufMetadata->hType = set_rle; return 0; } if (largest <= (srcSize >> 7)+4) { + /* heuristic: likely not compressible */ DEBUGLOG(5, "set_basic - no gain"); hufMetadata->hType = set_basic; return 0; - } - } + } } /* Validate the previous Huffman table */ - if (repeat == HUF_repeat_check && !HUF_validateCTable((HUF_CElt const*)prevHuf->CTable, countWksp, maxSymbolValue)) { + if (repeat == HUF_repeat_check + && !HUF_validateCTable((HUF_CElt const*)prevHuf->CTable, countWksp, maxSymbolValue)) { repeat = HUF_repeat_none; } /* Build Huffman Tree */ ZSTD_memset(nextHuf->CTable, 0, sizeof(nextHuf->CTable)); - huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue); + huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue, nodeWksp, nodeWkspSize, nextHuf->CTable, countWksp, hufFlags); + assert(huffLog <= LitHufLog); { size_t const maxBits = HUF_buildCTable_wksp((HUF_CElt*)nextHuf->CTable, countWksp, maxSymbolValue, huffLog, nodeWksp, nodeWkspSize); FORWARD_IF_ERROR(maxBits, "HUF_buildCTable_wksp"); huffLog = (U32)maxBits; - { /* Build and write the CTable */ - size_t const newCSize = HUF_estimateCompressedSize( - (HUF_CElt*)nextHuf->CTable, countWksp, maxSymbolValue); - size_t const hSize = HUF_writeCTable_wksp( - hufMetadata->hufDesBuffer, sizeof(hufMetadata->hufDesBuffer), - (HUF_CElt*)nextHuf->CTable, maxSymbolValue, huffLog, - nodeWksp, nodeWkspSize); - /* Check against repeating the previous CTable */ - if (repeat != HUF_repeat_none) { - size_t const oldCSize = HUF_estimateCompressedSize( - (HUF_CElt const*)prevHuf->CTable, countWksp, maxSymbolValue); - if (oldCSize < srcSize && (oldCSize <= hSize + newCSize || hSize + 12 >= srcSize)) { - DEBUGLOG(5, "set_repeat - smaller"); - ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); - hufMetadata->hType = set_repeat; - return 0; - } - } - if (newCSize + hSize >= srcSize) { - DEBUGLOG(5, "set_basic - no gains"); + } + { /* Build and write the CTable */ + size_t const newCSize = HUF_estimateCompressedSize( + (HUF_CElt*)nextHuf->CTable, countWksp, maxSymbolValue); + size_t const hSize = HUF_writeCTable_wksp( + hufMetadata->hufDesBuffer, sizeof(hufMetadata->hufDesBuffer), + (HUF_CElt*)nextHuf->CTable, maxSymbolValue, huffLog, + nodeWksp, nodeWkspSize); + /* Check against repeating the previous CTable */ + if (repeat != HUF_repeat_none) { + size_t const oldCSize = HUF_estimateCompressedSize( + (HUF_CElt const*)prevHuf->CTable, countWksp, maxSymbolValue); + if (oldCSize < srcSize && (oldCSize <= hSize + newCSize || hSize + 12 >= srcSize)) { + DEBUGLOG(5, "set_repeat - smaller"); ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); - hufMetadata->hType = set_basic; + hufMetadata->hType = set_repeat; return 0; - } - DEBUGLOG(5, "set_compressed (hSize=%u)", (U32)hSize); - hufMetadata->hType = set_compressed; - nextHuf->repeatMode = HUF_repeat_check; - return hSize; + } } + if (newCSize + hSize >= srcSize) { + DEBUGLOG(5, "set_basic - no gains"); + ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); + hufMetadata->hType = set_basic; + return 0; } + DEBUGLOG(5, "set_compressed (hSize=%u)", (U32)hSize); + hufMetadata->hType = set_compressed; + nextHuf->repeatMode = HUF_repeat_check; + return hSize; } } @@ -3157,8 +3581,9 @@ static size_t ZSTD_buildBlockEntropyStats_literals(void* const src, size_t srcSi * and updates nextEntropy to the appropriate repeatMode. */ static ZSTD_symbolEncodingTypeStats_t -ZSTD_buildDummySequencesStatistics(ZSTD_fseCTables_t* nextEntropy) { - ZSTD_symbolEncodingTypeStats_t stats = {set_basic, set_basic, set_basic, 0, 0}; +ZSTD_buildDummySequencesStatistics(ZSTD_fseCTables_t* nextEntropy) +{ + ZSTD_symbolEncodingTypeStats_t stats = {set_basic, set_basic, set_basic, 0, 0, 0}; nextEntropy->litlength_repeatMode = FSE_repeat_none; nextEntropy->offcode_repeatMode = FSE_repeat_none; nextEntropy->matchlength_repeatMode = FSE_repeat_none; @@ -3169,16 +3594,18 @@ ZSTD_buildDummySequencesStatistics(ZSTD_fseCTables_t* nextEntropy) { * Builds entropy for the sequences. * Stores symbol compression modes and fse table to fseMetadata. * Requires ENTROPY_WORKSPACE_SIZE wksp. - * @return : size of fse tables or error code */ -static size_t ZSTD_buildBlockEntropyStats_sequences(seqStore_t* seqStorePtr, - const ZSTD_fseCTables_t* prevEntropy, - ZSTD_fseCTables_t* nextEntropy, - const ZSTD_CCtx_params* cctxParams, - ZSTD_fseCTablesMetadata_t* fseMetadata, - void* workspace, size_t wkspSize) + * @return : size of fse tables or error code */ +static size_t +ZSTD_buildBlockEntropyStats_sequences( + const seqStore_t* seqStorePtr, + const ZSTD_fseCTables_t* prevEntropy, + ZSTD_fseCTables_t* nextEntropy, + const ZSTD_CCtx_params* cctxParams, + ZSTD_fseCTablesMetadata_t* fseMetadata, + void* workspace, size_t wkspSize) { ZSTD_strategy const strategy = cctxParams->cParams.strategy; - size_t const nbSeq = seqStorePtr->sequences - seqStorePtr->sequencesStart; + size_t const nbSeq = (size_t)(seqStorePtr->sequences - seqStorePtr->sequencesStart); BYTE* const ostart = fseMetadata->fseTablesBuffer; BYTE* const oend = ostart + sizeof(fseMetadata->fseTablesBuffer); BYTE* op = ostart; @@ -3205,23 +3632,28 @@ static size_t ZSTD_buildBlockEntropyStats_sequences(seqStore_t* seqStorePtr, /** ZSTD_buildBlockEntropyStats() : * Builds entropy for the block. * Requires workspace size ENTROPY_WORKSPACE_SIZE - * - * @return : 0 on success or error code + * @return : 0 on success, or an error code + * Note : also employed in superblock */ -size_t ZSTD_buildBlockEntropyStats(seqStore_t* seqStorePtr, - const ZSTD_entropyCTables_t* prevEntropy, - ZSTD_entropyCTables_t* nextEntropy, - const ZSTD_CCtx_params* cctxParams, - ZSTD_entropyCTablesMetadata_t* entropyMetadata, - void* workspace, size_t wkspSize) -{ - size_t const litSize = seqStorePtr->lit - seqStorePtr->litStart; +size_t ZSTD_buildBlockEntropyStats( + const seqStore_t* seqStorePtr, + const ZSTD_entropyCTables_t* prevEntropy, + ZSTD_entropyCTables_t* nextEntropy, + const ZSTD_CCtx_params* cctxParams, + ZSTD_entropyCTablesMetadata_t* entropyMetadata, + void* workspace, size_t wkspSize) +{ + size_t const litSize = (size_t)(seqStorePtr->lit - seqStorePtr->litStart); + int const huf_useOptDepth = (cctxParams->cParams.strategy >= HUF_OPTIMAL_DEPTH_THRESHOLD); + int const hufFlags = huf_useOptDepth ? HUF_flags_optimalDepth : 0; + entropyMetadata->hufMetadata.hufDesSize = ZSTD_buildBlockEntropyStats_literals(seqStorePtr->litStart, litSize, &prevEntropy->huf, &nextEntropy->huf, &entropyMetadata->hufMetadata, ZSTD_literalsCompressionIsDisabled(cctxParams), - workspace, wkspSize); + workspace, wkspSize, hufFlags); + FORWARD_IF_ERROR(entropyMetadata->hufMetadata.hufDesSize, "ZSTD_buildBlockEntropyStats_literals failed"); entropyMetadata->fseMetadata.fseTablesSize = ZSTD_buildBlockEntropyStats_sequences(seqStorePtr, @@ -3234,11 +3666,12 @@ size_t ZSTD_buildBlockEntropyStats(seqStore_t* seqStorePtr, } /* Returns the size estimate for the literals section (header + content) of a block */ -static size_t ZSTD_estimateBlockSize_literal(const BYTE* literals, size_t litSize, - const ZSTD_hufCTables_t* huf, - const ZSTD_hufCTablesMetadata_t* hufMetadata, - void* workspace, size_t wkspSize, - int writeEntropy) +static size_t +ZSTD_estimateBlockSize_literal(const BYTE* literals, size_t litSize, + const ZSTD_hufCTables_t* huf, + const ZSTD_hufCTablesMetadata_t* hufMetadata, + void* workspace, size_t wkspSize, + int writeEntropy) { unsigned* const countWksp = (unsigned*)workspace; unsigned maxSymbolValue = HUF_SYMBOLVALUE_MAX; @@ -3260,12 +3693,13 @@ static size_t ZSTD_estimateBlockSize_literal(const BYTE* literals, size_t litSiz } /* Returns the size estimate for the FSE-compressed symbols (of, ml, ll) of a block */ -static size_t ZSTD_estimateBlockSize_symbolType(symbolEncodingType_e type, - const BYTE* codeTable, size_t nbSeq, unsigned maxCode, - const FSE_CTable* fseCTable, - const U8* additionalBits, - short const* defaultNorm, U32 defaultNormLog, U32 defaultMax, - void* workspace, size_t wkspSize) +static size_t +ZSTD_estimateBlockSize_symbolType(symbolEncodingType_e type, + const BYTE* codeTable, size_t nbSeq, unsigned maxCode, + const FSE_CTable* fseCTable, + const U8* additionalBits, + short const* defaultNorm, U32 defaultNormLog, U32 defaultMax, + void* workspace, size_t wkspSize) { unsigned* const countWksp = (unsigned*)workspace; const BYTE* ctp = codeTable; @@ -3297,99 +3731,107 @@ static size_t ZSTD_estimateBlockSize_symbolType(symbolEncodingType_e type, } /* Returns the size estimate for the sequences section (header + content) of a block */ -static size_t ZSTD_estimateBlockSize_sequences(const BYTE* ofCodeTable, - const BYTE* llCodeTable, - const BYTE* mlCodeTable, - size_t nbSeq, - const ZSTD_fseCTables_t* fseTables, - const ZSTD_fseCTablesMetadata_t* fseMetadata, - void* workspace, size_t wkspSize, - int writeEntropy) +static size_t +ZSTD_estimateBlockSize_sequences(const BYTE* ofCodeTable, + const BYTE* llCodeTable, + const BYTE* mlCodeTable, + size_t nbSeq, + const ZSTD_fseCTables_t* fseTables, + const ZSTD_fseCTablesMetadata_t* fseMetadata, + void* workspace, size_t wkspSize, + int writeEntropy) { size_t sequencesSectionHeaderSize = 1 /* seqHead */ + 1 /* min seqSize size */ + (nbSeq >= 128) + (nbSeq >= LONGNBSEQ); size_t cSeqSizeEstimate = 0; cSeqSizeEstimate += ZSTD_estimateBlockSize_symbolType(fseMetadata->ofType, ofCodeTable, nbSeq, MaxOff, - fseTables->offcodeCTable, NULL, - OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff, - workspace, wkspSize); + fseTables->offcodeCTable, NULL, + OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff, + workspace, wkspSize); cSeqSizeEstimate += ZSTD_estimateBlockSize_symbolType(fseMetadata->llType, llCodeTable, nbSeq, MaxLL, - fseTables->litlengthCTable, LL_bits, - LL_defaultNorm, LL_defaultNormLog, MaxLL, - workspace, wkspSize); + fseTables->litlengthCTable, LL_bits, + LL_defaultNorm, LL_defaultNormLog, MaxLL, + workspace, wkspSize); cSeqSizeEstimate += ZSTD_estimateBlockSize_symbolType(fseMetadata->mlType, mlCodeTable, nbSeq, MaxML, - fseTables->matchlengthCTable, ML_bits, - ML_defaultNorm, ML_defaultNormLog, MaxML, - workspace, wkspSize); + fseTables->matchlengthCTable, ML_bits, + ML_defaultNorm, ML_defaultNormLog, MaxML, + workspace, wkspSize); if (writeEntropy) cSeqSizeEstimate += fseMetadata->fseTablesSize; return cSeqSizeEstimate + sequencesSectionHeaderSize; } /* Returns the size estimate for a given stream of literals, of, ll, ml */ -static size_t ZSTD_estimateBlockSize(const BYTE* literals, size_t litSize, - const BYTE* ofCodeTable, - const BYTE* llCodeTable, - const BYTE* mlCodeTable, - size_t nbSeq, - const ZSTD_entropyCTables_t* entropy, - const ZSTD_entropyCTablesMetadata_t* entropyMetadata, - void* workspace, size_t wkspSize, - int writeLitEntropy, int writeSeqEntropy) { +static size_t +ZSTD_estimateBlockSize(const BYTE* literals, size_t litSize, + const BYTE* ofCodeTable, + const BYTE* llCodeTable, + const BYTE* mlCodeTable, + size_t nbSeq, + const ZSTD_entropyCTables_t* entropy, + const ZSTD_entropyCTablesMetadata_t* entropyMetadata, + void* workspace, size_t wkspSize, + int writeLitEntropy, int writeSeqEntropy) +{ size_t const literalsSize = ZSTD_estimateBlockSize_literal(literals, litSize, - &entropy->huf, &entropyMetadata->hufMetadata, - workspace, wkspSize, writeLitEntropy); + &entropy->huf, &entropyMetadata->hufMetadata, + workspace, wkspSize, writeLitEntropy); size_t const seqSize = ZSTD_estimateBlockSize_sequences(ofCodeTable, llCodeTable, mlCodeTable, - nbSeq, &entropy->fse, &entropyMetadata->fseMetadata, - workspace, wkspSize, writeSeqEntropy); + nbSeq, &entropy->fse, &entropyMetadata->fseMetadata, + workspace, wkspSize, writeSeqEntropy); return seqSize + literalsSize + ZSTD_blockHeaderSize; } /* Builds entropy statistics and uses them for blocksize estimation. * - * Returns the estimated compressed size of the seqStore, or a zstd error. + * @return: estimated compressed size of the seqStore, or a zstd error. */ -static size_t ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(seqStore_t* seqStore, ZSTD_CCtx* zc) { - ZSTD_entropyCTablesMetadata_t* entropyMetadata = &zc->blockSplitCtx.entropyMetadata; +static size_t +ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(seqStore_t* seqStore, ZSTD_CCtx* zc) +{ + ZSTD_entropyCTablesMetadata_t* const entropyMetadata = &zc->blockSplitCtx.entropyMetadata; DEBUGLOG(6, "ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize()"); FORWARD_IF_ERROR(ZSTD_buildBlockEntropyStats(seqStore, &zc->blockState.prevCBlock->entropy, &zc->blockState.nextCBlock->entropy, &zc->appliedParams, entropyMetadata, - zc->entropyWorkspace, ENTROPY_WORKSPACE_SIZE /* statically allocated in resetCCtx */), ""); - return ZSTD_estimateBlockSize(seqStore->litStart, (size_t)(seqStore->lit - seqStore->litStart), + zc->entropyWorkspace, ENTROPY_WORKSPACE_SIZE), ""); + return ZSTD_estimateBlockSize( + seqStore->litStart, (size_t)(seqStore->lit - seqStore->litStart), seqStore->ofCode, seqStore->llCode, seqStore->mlCode, (size_t)(seqStore->sequences - seqStore->sequencesStart), - &zc->blockState.nextCBlock->entropy, entropyMetadata, zc->entropyWorkspace, ENTROPY_WORKSPACE_SIZE, + &zc->blockState.nextCBlock->entropy, + entropyMetadata, + zc->entropyWorkspace, ENTROPY_WORKSPACE_SIZE, (int)(entropyMetadata->hufMetadata.hType == set_compressed), 1); } /* Returns literals bytes represented in a seqStore */ -static size_t ZSTD_countSeqStoreLiteralsBytes(const seqStore_t* const seqStore) { +static size_t ZSTD_countSeqStoreLiteralsBytes(const seqStore_t* const seqStore) +{ size_t literalsBytes = 0; - size_t const nbSeqs = seqStore->sequences - seqStore->sequencesStart; + size_t const nbSeqs = (size_t)(seqStore->sequences - seqStore->sequencesStart); size_t i; for (i = 0; i < nbSeqs; ++i) { - seqDef seq = seqStore->sequencesStart[i]; + seqDef const seq = seqStore->sequencesStart[i]; literalsBytes += seq.litLength; if (i == seqStore->longLengthPos && seqStore->longLengthType == ZSTD_llt_literalLength) { literalsBytes += 0x10000; - } - } + } } return literalsBytes; } /* Returns match bytes represented in a seqStore */ -static size_t ZSTD_countSeqStoreMatchBytes(const seqStore_t* const seqStore) { +static size_t ZSTD_countSeqStoreMatchBytes(const seqStore_t* const seqStore) +{ size_t matchBytes = 0; - size_t const nbSeqs = seqStore->sequences - seqStore->sequencesStart; + size_t const nbSeqs = (size_t)(seqStore->sequences - seqStore->sequencesStart); size_t i; for (i = 0; i < nbSeqs; ++i) { seqDef seq = seqStore->sequencesStart[i]; matchBytes += seq.mlBase + MINMATCH; if (i == seqStore->longLengthPos && seqStore->longLengthType == ZSTD_llt_matchLength) { matchBytes += 0x10000; - } - } + } } return matchBytes; } @@ -3398,15 +3840,12 @@ static size_t ZSTD_countSeqStoreMatchBytes(const seqStore_t* const seqStore) { */ static void ZSTD_deriveSeqStoreChunk(seqStore_t* resultSeqStore, const seqStore_t* originalSeqStore, - size_t startIdx, size_t endIdx) { - BYTE* const litEnd = originalSeqStore->lit; - size_t literalsBytes; - size_t literalsBytesPreceding = 0; - + size_t startIdx, size_t endIdx) +{ *resultSeqStore = *originalSeqStore; if (startIdx > 0) { resultSeqStore->sequences = originalSeqStore->sequencesStart + startIdx; - literalsBytesPreceding = ZSTD_countSeqStoreLiteralsBytes(resultSeqStore); + resultSeqStore->litStart += ZSTD_countSeqStoreLiteralsBytes(resultSeqStore); } /* Move longLengthPos into the correct position if necessary */ @@ -3419,13 +3858,12 @@ static void ZSTD_deriveSeqStoreChunk(seqStore_t* resultSeqStore, } resultSeqStore->sequencesStart = originalSeqStore->sequencesStart + startIdx; resultSeqStore->sequences = originalSeqStore->sequencesStart + endIdx; - literalsBytes = ZSTD_countSeqStoreLiteralsBytes(resultSeqStore); - resultSeqStore->litStart += literalsBytesPreceding; if (endIdx == (size_t)(originalSeqStore->sequences - originalSeqStore->sequencesStart)) { /* This accounts for possible last literals if the derived chunk reaches the end of the block */ - resultSeqStore->lit = litEnd; + assert(resultSeqStore->lit == originalSeqStore->lit); } else { - resultSeqStore->lit = resultSeqStore->litStart+literalsBytes; + size_t const literalsBytes = ZSTD_countSeqStoreLiteralsBytes(resultSeqStore); + resultSeqStore->lit = resultSeqStore->litStart + literalsBytes; } resultSeqStore->llCode += startIdx; resultSeqStore->mlCode += startIdx; @@ -3433,20 +3871,26 @@ static void ZSTD_deriveSeqStoreChunk(seqStore_t* resultSeqStore, } /** - * Returns the raw offset represented by the combination of offCode, ll0, and repcode history. - * offCode must represent a repcode in the numeric representation of ZSTD_storeSeq(). + * Returns the raw offset represented by the combination of offBase, ll0, and repcode history. + * offBase must represent a repcode in the numeric representation of ZSTD_storeSeq(). */ static U32 -ZSTD_resolveRepcodeToRawOffset(const U32 rep[ZSTD_REP_NUM], const U32 offCode, const U32 ll0) -{ - U32 const adjustedOffCode = STORED_REPCODE(offCode) - 1 + ll0; /* [ 0 - 3 ] */ - assert(STORED_IS_REPCODE(offCode)); - if (adjustedOffCode == ZSTD_REP_NUM) { - /* litlength == 0 and offCode == 2 implies selection of first repcode - 1 */ - assert(rep[0] > 0); +ZSTD_resolveRepcodeToRawOffset(const U32 rep[ZSTD_REP_NUM], const U32 offBase, const U32 ll0) +{ + U32 const adjustedRepCode = OFFBASE_TO_REPCODE(offBase) - 1 + ll0; /* [ 0 - 3 ] */ + assert(OFFBASE_IS_REPCODE(offBase)); + if (adjustedRepCode == ZSTD_REP_NUM) { + assert(ll0); + /* litlength == 0 and offCode == 2 implies selection of first repcode - 1 + * This is only valid if it results in a valid offset value, aka > 0. + * Note : it may happen that `rep[0]==1` in exceptional circumstances. + * In which case this function will return 0, which is an invalid offset. + * It's not an issue though, since this value will be + * compared and discarded within ZSTD_seqStore_resolveOffCodes(). + */ return rep[0] - 1; } - return rep[adjustedOffCode]; + return rep[adjustedRepCode]; } /** @@ -3462,30 +3906,33 @@ ZSTD_resolveRepcodeToRawOffset(const U32 rep[ZSTD_REP_NUM], const U32 offCode, c * 1-3 : repcode 1-3 * 4+ : real_offset+3 */ -static void ZSTD_seqStore_resolveOffCodes(repcodes_t* const dRepcodes, repcodes_t* const cRepcodes, - seqStore_t* const seqStore, U32 const nbSeq) { +static void +ZSTD_seqStore_resolveOffCodes(repcodes_t* const dRepcodes, repcodes_t* const cRepcodes, + const seqStore_t* const seqStore, U32 const nbSeq) +{ U32 idx = 0; + U32 const longLitLenIdx = seqStore->longLengthType == ZSTD_llt_literalLength ? seqStore->longLengthPos : nbSeq; for (; idx < nbSeq; ++idx) { seqDef* const seq = seqStore->sequencesStart + idx; - U32 const ll0 = (seq->litLength == 0); - U32 const offCode = OFFBASE_TO_STORED(seq->offBase); - assert(seq->offBase > 0); - if (STORED_IS_REPCODE(offCode)) { - U32 const dRawOffset = ZSTD_resolveRepcodeToRawOffset(dRepcodes->rep, offCode, ll0); - U32 const cRawOffset = ZSTD_resolveRepcodeToRawOffset(cRepcodes->rep, offCode, ll0); + U32 const ll0 = (seq->litLength == 0) && (idx != longLitLenIdx); + U32 const offBase = seq->offBase; + assert(offBase > 0); + if (OFFBASE_IS_REPCODE(offBase)) { + U32 const dRawOffset = ZSTD_resolveRepcodeToRawOffset(dRepcodes->rep, offBase, ll0); + U32 const cRawOffset = ZSTD_resolveRepcodeToRawOffset(cRepcodes->rep, offBase, ll0); /* Adjust simulated decompression repcode history if we come across a mismatch. Replace * the repcode with the offset it actually references, determined by the compression * repcode history. */ if (dRawOffset != cRawOffset) { - seq->offBase = cRawOffset + ZSTD_REP_NUM; + seq->offBase = OFFSET_TO_OFFBASE(cRawOffset); } } /* Compression repcode history is always updated with values directly from the unmodified seqStore. * Decompression repcode history may use modified seq->offset value taken from compression repcode history. */ - ZSTD_updateRep(dRepcodes->rep, OFFBASE_TO_STORED(seq->offBase), ll0); - ZSTD_updateRep(cRepcodes->rep, offCode, ll0); + ZSTD_updateRep(dRepcodes->rep, seq->offBase, ll0); + ZSTD_updateRep(cRepcodes->rep, offBase, ll0); } } @@ -3495,10 +3942,11 @@ static void ZSTD_seqStore_resolveOffCodes(repcodes_t* const dRepcodes, repcodes_ * Returns the total size of that block (including header) or a ZSTD error code. */ static size_t -ZSTD_compressSeqStore_singleBlock(ZSTD_CCtx* zc, seqStore_t* const seqStore, +ZSTD_compressSeqStore_singleBlock(ZSTD_CCtx* zc, + const seqStore_t* const seqStore, repcodes_t* const dRep, repcodes_t* const cRep, void* dst, size_t dstCapacity, - const void* src, size_t srcSize, + const void* src, size_t srcSize, U32 lastBlock, U32 isPartition) { const U32 rleMaxLength = 25; @@ -3572,45 +4020,49 @@ typedef struct { /* Helper function to perform the recursive search for block splits. * Estimates the cost of seqStore prior to split, and estimates the cost of splitting the sequences in half. - * If advantageous to split, then we recurse down the two sub-blocks. If not, or if an error occurred in estimation, then - * we do not recurse. + * If advantageous to split, then we recurse down the two sub-blocks. + * If not, or if an error occurred in estimation, then we do not recurse. * - * Note: The recursion depth is capped by a heuristic minimum number of sequences, defined by MIN_SEQUENCES_BLOCK_SPLITTING. + * Note: The recursion depth is capped by a heuristic minimum number of sequences, + * defined by MIN_SEQUENCES_BLOCK_SPLITTING. * In theory, this means the absolute largest recursion depth is 10 == log2(maxNbSeqInBlock/MIN_SEQUENCES_BLOCK_SPLITTING). * In practice, recursion depth usually doesn't go beyond 4. * - * Furthermore, the number of splits is capped by ZSTD_MAX_NB_BLOCK_SPLITS. At ZSTD_MAX_NB_BLOCK_SPLITS == 196 with the current existing blockSize + * Furthermore, the number of splits is capped by ZSTD_MAX_NB_BLOCK_SPLITS. + * At ZSTD_MAX_NB_BLOCK_SPLITS == 196 with the current existing blockSize * maximum of 128 KB, this value is actually impossible to reach. */ static void ZSTD_deriveBlockSplitsHelper(seqStoreSplits* splits, size_t startIdx, size_t endIdx, ZSTD_CCtx* zc, const seqStore_t* origSeqStore) { - seqStore_t* fullSeqStoreChunk = &zc->blockSplitCtx.fullSeqStoreChunk; - seqStore_t* firstHalfSeqStore = &zc->blockSplitCtx.firstHalfSeqStore; - seqStore_t* secondHalfSeqStore = &zc->blockSplitCtx.secondHalfSeqStore; + seqStore_t* const fullSeqStoreChunk = &zc->blockSplitCtx.fullSeqStoreChunk; + seqStore_t* const firstHalfSeqStore = &zc->blockSplitCtx.firstHalfSeqStore; + seqStore_t* const secondHalfSeqStore = &zc->blockSplitCtx.secondHalfSeqStore; size_t estimatedOriginalSize; size_t estimatedFirstHalfSize; size_t estimatedSecondHalfSize; size_t midIdx = (startIdx + endIdx)/2; + DEBUGLOG(5, "ZSTD_deriveBlockSplitsHelper: startIdx=%zu endIdx=%zu", startIdx, endIdx); + assert(endIdx >= startIdx); if (endIdx - startIdx < MIN_SEQUENCES_BLOCK_SPLITTING || splits->idx >= ZSTD_MAX_NB_BLOCK_SPLITS) { - DEBUGLOG(6, "ZSTD_deriveBlockSplitsHelper: Too few sequences"); + DEBUGLOG(6, "ZSTD_deriveBlockSplitsHelper: Too few sequences (%zu)", endIdx - startIdx); return; } - DEBUGLOG(4, "ZSTD_deriveBlockSplitsHelper: startIdx=%zu endIdx=%zu", startIdx, endIdx); ZSTD_deriveSeqStoreChunk(fullSeqStoreChunk, origSeqStore, startIdx, endIdx); ZSTD_deriveSeqStoreChunk(firstHalfSeqStore, origSeqStore, startIdx, midIdx); ZSTD_deriveSeqStoreChunk(secondHalfSeqStore, origSeqStore, midIdx, endIdx); estimatedOriginalSize = ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(fullSeqStoreChunk, zc); estimatedFirstHalfSize = ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(firstHalfSeqStore, zc); estimatedSecondHalfSize = ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(secondHalfSeqStore, zc); - DEBUGLOG(4, "Estimated original block size: %zu -- First half split: %zu -- Second half split: %zu", + DEBUGLOG(5, "Estimated original block size: %zu -- First half split: %zu -- Second half split: %zu", estimatedOriginalSize, estimatedFirstHalfSize, estimatedSecondHalfSize); if (ZSTD_isError(estimatedOriginalSize) || ZSTD_isError(estimatedFirstHalfSize) || ZSTD_isError(estimatedSecondHalfSize)) { return; } if (estimatedFirstHalfSize + estimatedSecondHalfSize < estimatedOriginalSize) { + DEBUGLOG(5, "split decided at seqNb:%zu", midIdx); ZSTD_deriveBlockSplitsHelper(splits, startIdx, midIdx, zc, origSeqStore); splits->splitLocations[splits->idx] = (U32)midIdx; splits->idx++; @@ -3618,14 +4070,18 @@ ZSTD_deriveBlockSplitsHelper(seqStoreSplits* splits, size_t startIdx, size_t end } } -/* Base recursive function. Populates a table with intra-block partition indices that can improve compression ratio. +/* Base recursive function. + * Populates a table with intra-block partition indices that can improve compression ratio. * - * Returns the number of splits made (which equals the size of the partition table - 1). + * @return: number of splits made (which equals the size of the partition table - 1). */ -static size_t ZSTD_deriveBlockSplits(ZSTD_CCtx* zc, U32 partitions[], U32 nbSeq) { - seqStoreSplits splits = {partitions, 0}; +static size_t ZSTD_deriveBlockSplits(ZSTD_CCtx* zc, U32 partitions[], U32 nbSeq) +{ + seqStoreSplits splits; + splits.splitLocations = partitions; + splits.idx = 0; if (nbSeq <= 4) { - DEBUGLOG(4, "ZSTD_deriveBlockSplits: Too few sequences to split"); + DEBUGLOG(5, "ZSTD_deriveBlockSplits: Too few sequences to split (%u <= 4)", nbSeq); /* Refuse to try and split anything with less than 4 sequences */ return 0; } @@ -3641,18 +4097,20 @@ static size_t ZSTD_deriveBlockSplits(ZSTD_CCtx* zc, U32 partitions[], U32 nbSeq) * Returns combined size of all blocks (which includes headers), or a ZSTD error code. */ static size_t -ZSTD_compressBlock_splitBlock_internal(ZSTD_CCtx* zc, void* dst, size_t dstCapacity, - const void* src, size_t blockSize, U32 lastBlock, U32 nbSeq) +ZSTD_compressBlock_splitBlock_internal(ZSTD_CCtx* zc, + void* dst, size_t dstCapacity, + const void* src, size_t blockSize, + U32 lastBlock, U32 nbSeq) { size_t cSize = 0; const BYTE* ip = (const BYTE*)src; BYTE* op = (BYTE*)dst; size_t i = 0; size_t srcBytesTotal = 0; - U32* partitions = zc->blockSplitCtx.partitions; /* size == ZSTD_MAX_NB_BLOCK_SPLITS */ - seqStore_t* nextSeqStore = &zc->blockSplitCtx.nextSeqStore; - seqStore_t* currSeqStore = &zc->blockSplitCtx.currSeqStore; - size_t numSplits = ZSTD_deriveBlockSplits(zc, partitions, nbSeq); + U32* const partitions = zc->blockSplitCtx.partitions; /* size == ZSTD_MAX_NB_BLOCK_SPLITS */ + seqStore_t* const nextSeqStore = &zc->blockSplitCtx.nextSeqStore; + seqStore_t* const currSeqStore = &zc->blockSplitCtx.currSeqStore; + size_t const numSplits = ZSTD_deriveBlockSplits(zc, partitions, nbSeq); /* If a block is split and some partitions are emitted as RLE/uncompressed, then repcode history * may become invalid. In order to reconcile potentially invalid repcodes, we keep track of two @@ -3674,30 +4132,31 @@ ZSTD_compressBlock_splitBlock_internal(ZSTD_CCtx* zc, void* dst, size_t dstCapac ZSTD_memcpy(cRep.rep, zc->blockState.prevCBlock->rep, sizeof(repcodes_t)); ZSTD_memset(nextSeqStore, 0, sizeof(seqStore_t)); - DEBUGLOG(4, "ZSTD_compressBlock_splitBlock_internal (dstCapacity=%u, dictLimit=%u, nextToUpdate=%u)", + DEBUGLOG(5, "ZSTD_compressBlock_splitBlock_internal (dstCapacity=%u, dictLimit=%u, nextToUpdate=%u)", (unsigned)dstCapacity, (unsigned)zc->blockState.matchState.window.dictLimit, (unsigned)zc->blockState.matchState.nextToUpdate); if (numSplits == 0) { - size_t cSizeSingleBlock = ZSTD_compressSeqStore_singleBlock(zc, &zc->seqStore, - &dRep, &cRep, - op, dstCapacity, - ip, blockSize, - lastBlock, 0 /* isPartition */); + size_t cSizeSingleBlock = + ZSTD_compressSeqStore_singleBlock(zc, &zc->seqStore, + &dRep, &cRep, + op, dstCapacity, + ip, blockSize, + lastBlock, 0 /* isPartition */); FORWARD_IF_ERROR(cSizeSingleBlock, "Compressing single block from splitBlock_internal() failed!"); DEBUGLOG(5, "ZSTD_compressBlock_splitBlock_internal: No splits"); - assert(cSizeSingleBlock <= ZSTD_BLOCKSIZE_MAX + ZSTD_blockHeaderSize); + assert(zc->blockSize <= ZSTD_BLOCKSIZE_MAX); + assert(cSizeSingleBlock <= zc->blockSize + ZSTD_blockHeaderSize); return cSizeSingleBlock; } ZSTD_deriveSeqStoreChunk(currSeqStore, &zc->seqStore, 0, partitions[0]); for (i = 0; i <= numSplits; ++i) { - size_t srcBytes; size_t cSizeChunk; U32 const lastPartition = (i == numSplits); U32 lastBlockEntireSrc = 0; - srcBytes = ZSTD_countSeqStoreLiteralsBytes(currSeqStore) + ZSTD_countSeqStoreMatchBytes(currSeqStore); + size_t srcBytes = ZSTD_countSeqStoreLiteralsBytes(currSeqStore) + ZSTD_countSeqStoreMatchBytes(currSeqStore); srcBytesTotal += srcBytes; if (lastPartition) { /* This is the final partition, need to account for possible last literals */ @@ -3712,7 +4171,8 @@ ZSTD_compressBlock_splitBlock_internal(ZSTD_CCtx* zc, void* dst, size_t dstCapac op, dstCapacity, ip, srcBytes, lastBlockEntireSrc, 1 /* isPartition */); - DEBUGLOG(5, "Estimated size: %zu actual size: %zu", ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(currSeqStore, zc), cSizeChunk); + DEBUGLOG(5, "Estimated size: %zu vs %zu : actual size", + ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(currSeqStore, zc), cSizeChunk); FORWARD_IF_ERROR(cSizeChunk, "Compressing chunk failed!"); ip += srcBytes; @@ -3720,10 +4180,10 @@ ZSTD_compressBlock_splitBlock_internal(ZSTD_CCtx* zc, void* dst, size_t dstCapac dstCapacity -= cSizeChunk; cSize += cSizeChunk; *currSeqStore = *nextSeqStore; - assert(cSizeChunk <= ZSTD_BLOCKSIZE_MAX + ZSTD_blockHeaderSize); + assert(cSizeChunk <= zc->blockSize + ZSTD_blockHeaderSize); } - /* cRep and dRep may have diverged during the compression. If so, we use the dRep repcodes - * for the next block. + /* cRep and dRep may have diverged during the compression. + * If so, we use the dRep repcodes for the next block. */ ZSTD_memcpy(zc->blockState.prevCBlock->rep, dRep.rep, sizeof(repcodes_t)); return cSize; @@ -3734,8 +4194,6 @@ ZSTD_compressBlock_splitBlock(ZSTD_CCtx* zc, void* dst, size_t dstCapacity, const void* src, size_t srcSize, U32 lastBlock) { - const BYTE* ip = (const BYTE*)src; - BYTE* op = (BYTE*)dst; U32 nbSeq; size_t cSize; DEBUGLOG(4, "ZSTD_compressBlock_splitBlock"); @@ -3746,7 +4204,7 @@ ZSTD_compressBlock_splitBlock(ZSTD_CCtx* zc, if (bss == ZSTDbss_noCompress) { if (zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat_valid) zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode = FSE_repeat_check; - cSize = ZSTD_noCompressBlock(op, dstCapacity, ip, srcSize, lastBlock); + cSize = ZSTD_noCompressBlock(dst, dstCapacity, src, srcSize, lastBlock); FORWARD_IF_ERROR(cSize, "ZSTD_noCompressBlock failed"); DEBUGLOG(4, "ZSTD_compressBlock_splitBlock: Nocompress block"); return cSize; @@ -3764,9 +4222,9 @@ ZSTD_compressBlock_internal(ZSTD_CCtx* zc, void* dst, size_t dstCapacity, const void* src, size_t srcSize, U32 frame) { - /* This the upper bound for the length of an rle block. - * This isn't the actual upper bound. Finding the real threshold - * needs further investigation. + /* This is an estimated upper bound for the length of an rle block. + * This isn't the actual upper bound. + * Finding the real threshold needs further investigation. */ const U32 rleMaxLength = 25; size_t cSize; @@ -3858,10 +4316,11 @@ static size_t ZSTD_compressBlock_targetCBlockSize_body(ZSTD_CCtx* zc, * * cSize >= blockBound(srcSize): We have expanded the block too much so * emit an uncompressed block. */ - { - size_t const cSize = ZSTD_compressSuperBlock(zc, dst, dstCapacity, src, srcSize, lastBlock); + { size_t const cSize = + ZSTD_compressSuperBlock(zc, dst, dstCapacity, src, srcSize, lastBlock); if (cSize != ERROR(dstSize_tooSmall)) { - size_t const maxCSize = srcSize - ZSTD_minGain(srcSize, zc->appliedParams.cParams.strategy); + size_t const maxCSize = + srcSize - ZSTD_minGain(srcSize, zc->appliedParams.cParams.strategy); FORWARD_IF_ERROR(cSize, "ZSTD_compressSuperBlock failed"); if (cSize != 0 && cSize < maxCSize + ZSTD_blockHeaderSize) { ZSTD_blockState_confirmRepcodesAndEntropyTables(&zc->blockState); @@ -3869,7 +4328,7 @@ static size_t ZSTD_compressBlock_targetCBlockSize_body(ZSTD_CCtx* zc, } } } - } + } /* if (bss == ZSTDbss_compress)*/ DEBUGLOG(6, "Resorting to ZSTD_noCompressBlock()"); /* Superblock compression failed, attempt to emit a single no compress block. @@ -3927,7 +4386,7 @@ static void ZSTD_overflowCorrectIfNeeded(ZSTD_matchState_t* ms, * All blocks will be terminated, all input will be consumed. * Function will issue an error if there is not enough `dstCapacity` to hold the compressed content. * Frame is supposed already started (header already produced) -* @return : compressed size, or an error code +* @return : compressed size, or an error code */ static size_t ZSTD_compress_frameChunk(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, @@ -3951,7 +4410,9 @@ static size_t ZSTD_compress_frameChunk(ZSTD_CCtx* cctx, ZSTD_matchState_t* const ms = &cctx->blockState.matchState; U32 const lastBlock = lastFrameChunk & (blockSize >= remaining); - RETURN_ERROR_IF(dstCapacity < ZSTD_blockHeaderSize + MIN_CBLOCK_SIZE, + /* TODO: See 3090. We reduced MIN_CBLOCK_SIZE from 3 to 2 so to compensate we are adding + * additional 1. We need to revisit and change this logic to be more consistent */ + RETURN_ERROR_IF(dstCapacity < ZSTD_blockHeaderSize + MIN_CBLOCK_SIZE + 1, dstSize_tooSmall, "not enough space to store compressed block"); if (remaining < blockSize) blockSize = remaining; @@ -3990,7 +4451,7 @@ static size_t ZSTD_compress_frameChunk(ZSTD_CCtx* cctx, MEM_writeLE24(op, cBlockHeader); cSize += ZSTD_blockHeaderSize; } - } + } /* if (ZSTD_useTargetCBlockSize(&cctx->appliedParams))*/ ip += blockSize; @@ -4169,31 +4630,51 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx, } } -size_t ZSTD_compressContinue (ZSTD_CCtx* cctx, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize) +size_t ZSTD_compressContinue_public(ZSTD_CCtx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize) { DEBUGLOG(5, "ZSTD_compressContinue (srcSize=%u)", (unsigned)srcSize); return ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 1 /* frame mode */, 0 /* last chunk */); } +/* NOTE: Must just wrap ZSTD_compressContinue_public() */ +size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize) +{ + return ZSTD_compressContinue_public(cctx, dst, dstCapacity, src, srcSize); +} -size_t ZSTD_getBlockSize(const ZSTD_CCtx* cctx) +static size_t ZSTD_getBlockSize_deprecated(const ZSTD_CCtx* cctx) { ZSTD_compressionParameters const cParams = cctx->appliedParams.cParams; assert(!ZSTD_checkCParams(cParams)); - return MIN (ZSTD_BLOCKSIZE_MAX, (U32)1 << cParams.windowLog); + return MIN(cctx->appliedParams.maxBlockSize, (size_t)1 << cParams.windowLog); } -size_t ZSTD_compressBlock(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) +/* NOTE: Must just wrap ZSTD_getBlockSize_deprecated() */ +size_t ZSTD_getBlockSize(const ZSTD_CCtx* cctx) +{ + return ZSTD_getBlockSize_deprecated(cctx); +} + +/* NOTE: Must just wrap ZSTD_compressBlock_deprecated() */ +size_t ZSTD_compressBlock_deprecated(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) { DEBUGLOG(5, "ZSTD_compressBlock: srcSize = %u", (unsigned)srcSize); - { size_t const blockSizeMax = ZSTD_getBlockSize(cctx); + { size_t const blockSizeMax = ZSTD_getBlockSize_deprecated(cctx); RETURN_ERROR_IF(srcSize > blockSizeMax, srcSize_wrong, "input is larger than a block"); } return ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 0 /* frame mode */, 0 /* last chunk */); } +/* NOTE: Must just wrap ZSTD_compressBlock_deprecated() */ +size_t ZSTD_compressBlock(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) +{ + return ZSTD_compressBlock_deprecated(cctx, dst, dstCapacity, src, srcSize); +} + /*! ZSTD_loadDictionaryContent() : * @return : 0, or an error code */ @@ -4202,25 +4683,36 @@ static size_t ZSTD_loadDictionaryContent(ZSTD_matchState_t* ms, ZSTD_cwksp* ws, ZSTD_CCtx_params const* params, const void* src, size_t srcSize, - ZSTD_dictTableLoadMethod_e dtlm) + ZSTD_dictTableLoadMethod_e dtlm, + ZSTD_tableFillPurpose_e tfp) { const BYTE* ip = (const BYTE*) src; const BYTE* const iend = ip + srcSize; int const loadLdmDict = params->ldmParams.enableLdm == ZSTD_ps_enable && ls != NULL; - /* Assert that we the ms params match the params we're being given */ + /* Assert that the ms params match the params we're being given */ ZSTD_assertEqualCParams(params->cParams, ms->cParams); - if (srcSize > ZSTD_CHUNKSIZE_MAX) { + { /* Ensure large dictionaries can't cause index overflow */ + /* Allow the dictionary to set indices up to exactly ZSTD_CURRENT_MAX. * Dictionaries right at the edge will immediately trigger overflow * correction, but I don't want to insert extra constraints here. */ - U32 const maxDictSize = ZSTD_CURRENT_MAX - 1; - /* We must have cleared our windows when our source is this large. */ - assert(ZSTD_window_isEmpty(ms->window)); - if (loadLdmDict) - assert(ZSTD_window_isEmpty(ls->window)); + U32 maxDictSize = ZSTD_CURRENT_MAX - ZSTD_WINDOW_START_INDEX; + + int const CDictTaggedIndices = ZSTD_CDictIndicesAreTagged(¶ms->cParams); + if (CDictTaggedIndices && tfp == ZSTD_tfp_forCDict) { + /* Some dictionary matchfinders in zstd use "short cache", + * which treats the lower ZSTD_SHORT_CACHE_TAG_BITS of each + * CDict hashtable entry as a tag rather than as part of an index. + * When short cache is used, we need to truncate the dictionary + * so that its indices don't overlap with the tag. */ + U32 const shortCacheMaxDictSize = (1u << (32 - ZSTD_SHORT_CACHE_TAG_BITS)) - ZSTD_WINDOW_START_INDEX; + maxDictSize = MIN(maxDictSize, shortCacheMaxDictSize); + assert(!loadLdmDict); + } + /* If the dictionary is too large, only load the suffix of the dictionary. */ if (srcSize > maxDictSize) { ip = iend - maxDictSize; @@ -4229,30 +4721,46 @@ static size_t ZSTD_loadDictionaryContent(ZSTD_matchState_t* ms, } } - DEBUGLOG(4, "ZSTD_loadDictionaryContent(): useRowMatchFinder=%d", (int)params->useRowMatchFinder); + if (srcSize > ZSTD_CHUNKSIZE_MAX) { + /* We must have cleared our windows when our source is this large. */ + assert(ZSTD_window_isEmpty(ms->window)); + if (loadLdmDict) assert(ZSTD_window_isEmpty(ls->window)); + } ZSTD_window_update(&ms->window, src, srcSize, /* forceNonContiguous */ 0); - ms->loadedDictEnd = params->forceWindow ? 0 : (U32)(iend - ms->window.base); - ms->forceNonContiguous = params->deterministicRefPrefix; - if (loadLdmDict) { + DEBUGLOG(4, "ZSTD_loadDictionaryContent(): useRowMatchFinder=%d", (int)params->useRowMatchFinder); + + if (loadLdmDict) { /* Load the entire dict into LDM matchfinders. */ ZSTD_window_update(&ls->window, src, srcSize, /* forceNonContiguous */ 0); ls->loadedDictEnd = params->forceWindow ? 0 : (U32)(iend - ls->window.base); + ZSTD_ldm_fillHashTable(ls, ip, iend, ¶ms->ldmParams); + } + + /* If the dict is larger than we can reasonably index in our tables, only load the suffix. */ + if (params->cParams.strategy < ZSTD_btultra) { + U32 maxDictSize = 8U << MIN(MAX(params->cParams.hashLog, params->cParams.chainLog), 28); + if (srcSize > maxDictSize) { + ip = iend - maxDictSize; + src = ip; + srcSize = maxDictSize; + } } + ms->nextToUpdate = (U32)(ip - ms->window.base); + ms->loadedDictEnd = params->forceWindow ? 0 : (U32)(iend - ms->window.base); + ms->forceNonContiguous = params->deterministicRefPrefix; + if (srcSize <= HASH_READ_SIZE) return 0; ZSTD_overflowCorrectIfNeeded(ms, ws, params, ip, iend); - if (loadLdmDict) - ZSTD_ldm_fillHashTable(ls, ip, iend, ¶ms->ldmParams); - switch(params->cParams.strategy) { case ZSTD_fast: - ZSTD_fillHashTable(ms, iend, dtlm); + ZSTD_fillHashTable(ms, iend, dtlm, tfp); break; case ZSTD_dfast: - ZSTD_fillDoubleHashTable(ms, iend, dtlm); + ZSTD_fillDoubleHashTable(ms, iend, dtlm, tfp); break; case ZSTD_greedy: @@ -4265,7 +4773,7 @@ static size_t ZSTD_loadDictionaryContent(ZSTD_matchState_t* ms, } else { assert(params->useRowMatchFinder != ZSTD_ps_auto); if (params->useRowMatchFinder == ZSTD_ps_enable) { - size_t const tagTableSize = ((size_t)1 << params->cParams.hashLog) * sizeof(U16); + size_t const tagTableSize = ((size_t)1 << params->cParams.hashLog); ZSTD_memset(ms->tagTable, 0, tagTableSize); ZSTD_row_update(ms, iend-HASH_READ_SIZE); DEBUGLOG(4, "Using row-based hash table for lazy dict"); @@ -4418,6 +4926,7 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs, ZSTD_CCtx_params const* params, const void* dict, size_t dictSize, ZSTD_dictTableLoadMethod_e dtlm, + ZSTD_tableFillPurpose_e tfp, void* workspace) { const BYTE* dictPtr = (const BYTE*)dict; @@ -4436,7 +4945,7 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs, { size_t const dictContentSize = (size_t)(dictEnd - dictPtr); FORWARD_IF_ERROR(ZSTD_loadDictionaryContent( - ms, NULL, ws, params, dictPtr, dictContentSize, dtlm), ""); + ms, NULL, ws, params, dictPtr, dictContentSize, dtlm, tfp), ""); } return dictID; } @@ -4452,6 +4961,7 @@ ZSTD_compress_insertDictionary(ZSTD_compressedBlockState_t* bs, const void* dict, size_t dictSize, ZSTD_dictContentType_e dictContentType, ZSTD_dictTableLoadMethod_e dtlm, + ZSTD_tableFillPurpose_e tfp, void* workspace) { DEBUGLOG(4, "ZSTD_compress_insertDictionary (dictSize=%u)", (U32)dictSize); @@ -4464,13 +4974,13 @@ ZSTD_compress_insertDictionary(ZSTD_compressedBlockState_t* bs, /* dict restricted modes */ if (dictContentType == ZSTD_dct_rawContent) - return ZSTD_loadDictionaryContent(ms, ls, ws, params, dict, dictSize, dtlm); + return ZSTD_loadDictionaryContent(ms, ls, ws, params, dict, dictSize, dtlm, tfp); if (MEM_readLE32(dict) != ZSTD_MAGIC_DICTIONARY) { if (dictContentType == ZSTD_dct_auto) { DEBUGLOG(4, "raw content dictionary detected"); return ZSTD_loadDictionaryContent( - ms, ls, ws, params, dict, dictSize, dtlm); + ms, ls, ws, params, dict, dictSize, dtlm, tfp); } RETURN_ERROR_IF(dictContentType == ZSTD_dct_fullDict, dictionary_wrong, ""); assert(0); /* impossible */ @@ -4478,13 +4988,14 @@ ZSTD_compress_insertDictionary(ZSTD_compressedBlockState_t* bs, /* dict as full zstd dictionary */ return ZSTD_loadZstdDictionary( - bs, ms, ws, params, dict, dictSize, dtlm, workspace); + bs, ms, ws, params, dict, dictSize, dtlm, tfp, workspace); } #define ZSTD_USE_CDICT_PARAMS_SRCSIZE_CUTOFF (128 KB) #define ZSTD_USE_CDICT_PARAMS_DICTSIZE_MULTIPLIER (6ULL) /*! ZSTD_compressBegin_internal() : + * Assumption : either @dict OR @cdict (or none) is non-NULL, never both * @return : 0, or an error code */ static size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, @@ -4520,11 +5031,11 @@ static size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx, cctx->blockState.prevCBlock, &cctx->blockState.matchState, &cctx->ldmState, &cctx->workspace, &cctx->appliedParams, cdict->dictContent, cdict->dictContentSize, cdict->dictContentType, dtlm, - cctx->entropyWorkspace) + ZSTD_tfp_forCCtx, cctx->entropyWorkspace) : ZSTD_compress_insertDictionary( cctx->blockState.prevCBlock, &cctx->blockState.matchState, &cctx->ldmState, &cctx->workspace, &cctx->appliedParams, dict, dictSize, - dictContentType, dtlm, cctx->entropyWorkspace); + dictContentType, dtlm, ZSTD_tfp_forCCtx, cctx->entropyWorkspace); FORWARD_IF_ERROR(dictID, "ZSTD_compress_insertDictionary failed"); assert(dictID <= UINT_MAX); cctx->dictID = (U32)dictID; @@ -4565,11 +5076,11 @@ size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx, &cctxParams, pledgedSrcSize); } -size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel) +static size_t +ZSTD_compressBegin_usingDict_deprecated(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel) { ZSTD_CCtx_params cctxParams; - { - ZSTD_parameters const params = ZSTD_getParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize, ZSTD_cpm_noAttachDict); + { ZSTD_parameters const params = ZSTD_getParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize, ZSTD_cpm_noAttachDict); ZSTD_CCtxParams_init_internal(&cctxParams, ¶ms, (compressionLevel == 0) ? ZSTD_CLEVEL_DEFAULT : compressionLevel); } DEBUGLOG(4, "ZSTD_compressBegin_usingDict (dictSize=%u)", (unsigned)dictSize); @@ -4577,9 +5088,15 @@ size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t di &cctxParams, ZSTD_CONTENTSIZE_UNKNOWN, ZSTDb_not_buffered); } +size_t +ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel) +{ + return ZSTD_compressBegin_usingDict_deprecated(cctx, dict, dictSize, compressionLevel); +} + size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel) { - return ZSTD_compressBegin_usingDict(cctx, NULL, 0, compressionLevel); + return ZSTD_compressBegin_usingDict_deprecated(cctx, NULL, 0, compressionLevel); } @@ -4649,9 +5166,9 @@ void ZSTD_CCtx_trace(ZSTD_CCtx* cctx, size_t extraCSize) #endif } -size_t ZSTD_compressEnd (ZSTD_CCtx* cctx, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize) +size_t ZSTD_compressEnd_public(ZSTD_CCtx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize) { size_t endResult; size_t const cSize = ZSTD_compressContinue_internal(cctx, @@ -4675,6 +5192,14 @@ size_t ZSTD_compressEnd (ZSTD_CCtx* cctx, return cSize + endResult; } +/* NOTE: Must just wrap ZSTD_compressEnd_public() */ +size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize) +{ + return ZSTD_compressEnd_public(cctx, dst, dstCapacity, src, srcSize); +} + size_t ZSTD_compress_advanced (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, @@ -4703,7 +5228,7 @@ size_t ZSTD_compress_advanced_internal( FORWARD_IF_ERROR( ZSTD_compressBegin_internal(cctx, dict, dictSize, ZSTD_dct_auto, ZSTD_dtlm_fast, NULL, params, srcSize, ZSTDb_not_buffered) , ""); - return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize); + return ZSTD_compressEnd_public(cctx, dst, dstCapacity, src, srcSize); } size_t ZSTD_compress_usingDict(ZSTD_CCtx* cctx, @@ -4828,7 +5353,7 @@ static size_t ZSTD_initCDict_internal( { size_t const dictID = ZSTD_compress_insertDictionary( &cdict->cBlockState, &cdict->matchState, NULL, &cdict->workspace, ¶ms, cdict->dictContent, cdict->dictContentSize, - dictContentType, ZSTD_dtlm_full, cdict->entropyWorkspace); + dictContentType, ZSTD_dtlm_full, ZSTD_tfp_forCDict, cdict->entropyWorkspace); FORWARD_IF_ERROR(dictID, "ZSTD_compress_insertDictionary failed"); assert(dictID <= (size_t)(U32)-1); cdict->dictID = (U32)dictID; @@ -5025,6 +5550,7 @@ const ZSTD_CDict* ZSTD_initStaticCDict( params.cParams = cParams; params.useRowMatchFinder = useRowMatchFinder; cdict->useRowMatchFinder = useRowMatchFinder; + cdict->compressionLevel = ZSTD_NO_CLEVEL; if (ZSTD_isError( ZSTD_initCDict_internal(cdict, dict, dictSize, @@ -5104,12 +5630,17 @@ size_t ZSTD_compressBegin_usingCDict_advanced( /* ZSTD_compressBegin_usingCDict() : * cdict must be != NULL */ -size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict) +size_t ZSTD_compressBegin_usingCDict_deprecated(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict) { ZSTD_frameParameters const fParams = { 0 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ }; return ZSTD_compressBegin_usingCDict_internal(cctx, cdict, fParams, ZSTD_CONTENTSIZE_UNKNOWN); } +size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict) +{ + return ZSTD_compressBegin_usingCDict_deprecated(cctx, cdict); +} + /*! ZSTD_compress_usingCDict_internal(): * Implementation of various ZSTD_compress_usingCDict* functions. */ @@ -5119,7 +5650,7 @@ static size_t ZSTD_compress_usingCDict_internal(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict, ZSTD_frameParameters fParams) { FORWARD_IF_ERROR(ZSTD_compressBegin_usingCDict_internal(cctx, cdict, fParams, srcSize), ""); /* will check if cdict != NULL */ - return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize); + return ZSTD_compressEnd_public(cctx, dst, dstCapacity, src, srcSize); } /*! ZSTD_compress_usingCDict_advanced(): @@ -5316,30 +5847,41 @@ size_t ZSTD_initCStream(ZSTD_CStream* zcs, int compressionLevel) static size_t ZSTD_nextInputSizeHint(const ZSTD_CCtx* cctx) { - size_t hintInSize = cctx->inBuffTarget - cctx->inBuffPos; - if (hintInSize==0) hintInSize = cctx->blockSize; - return hintInSize; + if (cctx->appliedParams.inBufferMode == ZSTD_bm_stable) { + return cctx->blockSize - cctx->stableIn_notConsumed; + } + assert(cctx->appliedParams.inBufferMode == ZSTD_bm_buffered); + { size_t hintInSize = cctx->inBuffTarget - cctx->inBuffPos; + if (hintInSize==0) hintInSize = cctx->blockSize; + return hintInSize; + } } /** ZSTD_compressStream_generic(): * internal function for all *compressStream*() variants - * non-static, because can be called from zstdmt_compress.c - * @return : hint size for next input */ + * @return : hint size for next input to complete ongoing block */ static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input, ZSTD_EndDirective const flushMode) { - const char* const istart = (const char*)input->src; - const char* const iend = input->size != 0 ? istart + input->size : istart; - const char* ip = input->pos != 0 ? istart + input->pos : istart; - char* const ostart = (char*)output->dst; - char* const oend = output->size != 0 ? ostart + output->size : ostart; - char* op = output->pos != 0 ? ostart + output->pos : ostart; + const char* const istart = (assert(input != NULL), (const char*)input->src); + const char* const iend = (istart != NULL) ? istart + input->size : istart; + const char* ip = (istart != NULL) ? istart + input->pos : istart; + char* const ostart = (assert(output != NULL), (char*)output->dst); + char* const oend = (ostart != NULL) ? ostart + output->size : ostart; + char* op = (ostart != NULL) ? ostart + output->pos : ostart; U32 someMoreWork = 1; /* check expectations */ - DEBUGLOG(5, "ZSTD_compressStream_generic, flush=%u", (unsigned)flushMode); + DEBUGLOG(5, "ZSTD_compressStream_generic, flush=%i, srcSize = %zu", (int)flushMode, input->size - input->pos); + assert(zcs != NULL); + if (zcs->appliedParams.inBufferMode == ZSTD_bm_stable) { + assert(input->pos >= zcs->stableIn_notConsumed); + input->pos -= zcs->stableIn_notConsumed; + ip -= zcs->stableIn_notConsumed; + zcs->stableIn_notConsumed = 0; + } if (zcs->appliedParams.inBufferMode == ZSTD_bm_buffered) { assert(zcs->inBuff != NULL); assert(zcs->inBuffSize > 0); @@ -5348,8 +5890,10 @@ static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs, assert(zcs->outBuff != NULL); assert(zcs->outBuffSize > 0); } - assert(output->pos <= output->size); + if (input->src == NULL) assert(input->size == 0); assert(input->pos <= input->size); + if (output->dst == NULL) assert(output->size == 0); + assert(output->pos <= output->size); assert((U32)flushMode <= (U32)ZSTD_e_end); while (someMoreWork) { @@ -5364,7 +5908,7 @@ static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs, || zcs->appliedParams.outBufferMode == ZSTD_bm_stable) /* OR we are allowed to return dstSizeTooSmall */ && (zcs->inBuffPos == 0) ) { /* shortcut to compression pass directly into output buffer */ - size_t const cSize = ZSTD_compressEnd(zcs, + size_t const cSize = ZSTD_compressEnd_public(zcs, op, oend-op, ip, iend-ip); DEBUGLOG(4, "ZSTD_compressEnd : cSize=%u", (unsigned)cSize); FORWARD_IF_ERROR(cSize, "ZSTD_compressEnd failed"); @@ -5381,8 +5925,7 @@ static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs, zcs->inBuff + zcs->inBuffPos, toLoad, ip, iend-ip); zcs->inBuffPos += loaded; - if (loaded != 0) - ip += loaded; + if (ip) ip += loaded; if ( (flushMode == ZSTD_e_continue) && (zcs->inBuffPos < zcs->inBuffTarget) ) { /* not enough input to fill full block : stop here */ @@ -5393,6 +5936,20 @@ static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs, /* empty */ someMoreWork = 0; break; } + } else { + assert(zcs->appliedParams.inBufferMode == ZSTD_bm_stable); + if ( (flushMode == ZSTD_e_continue) + && ( (size_t)(iend - ip) < zcs->blockSize) ) { + /* can't compress a full block : stop here */ + zcs->stableIn_notConsumed = (size_t)(iend - ip); + ip = iend; /* pretend to have consumed input */ + someMoreWork = 0; break; + } + if ( (flushMode == ZSTD_e_flush) + && (ip == iend) ) { + /* empty */ + someMoreWork = 0; break; + } } /* compress current block (note : this stage cannot be stopped in the middle) */ DEBUGLOG(5, "stream compression stage (flushMode==%u)", flushMode); @@ -5400,9 +5957,8 @@ static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs, void* cDst; size_t cSize; size_t oSize = oend-op; - size_t const iSize = inputBuffered - ? zcs->inBuffPos - zcs->inToCompress - : MIN((size_t)(iend - ip), zcs->blockSize); + size_t const iSize = inputBuffered ? zcs->inBuffPos - zcs->inToCompress + : MIN((size_t)(iend - ip), zcs->blockSize); if (oSize >= ZSTD_compressBound(iSize) || zcs->appliedParams.outBufferMode == ZSTD_bm_stable) cDst = op; /* compress into output buffer, to skip flush stage */ else @@ -5410,9 +5966,9 @@ static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs, if (inputBuffered) { unsigned const lastBlock = (flushMode == ZSTD_e_end) && (ip==iend); cSize = lastBlock ? - ZSTD_compressEnd(zcs, cDst, oSize, + ZSTD_compressEnd_public(zcs, cDst, oSize, zcs->inBuff + zcs->inToCompress, iSize) : - ZSTD_compressContinue(zcs, cDst, oSize, + ZSTD_compressContinue_public(zcs, cDst, oSize, zcs->inBuff + zcs->inToCompress, iSize); FORWARD_IF_ERROR(cSize, "%s", lastBlock ? "ZSTD_compressEnd failed" : "ZSTD_compressContinue failed"); zcs->frameEnded = lastBlock; @@ -5425,19 +5981,16 @@ static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs, if (!lastBlock) assert(zcs->inBuffTarget <= zcs->inBuffSize); zcs->inToCompress = zcs->inBuffPos; - } else { - unsigned const lastBlock = (ip + iSize == iend); - assert(flushMode == ZSTD_e_end /* Already validated */); + } else { /* !inputBuffered, hence ZSTD_bm_stable */ + unsigned const lastBlock = (flushMode == ZSTD_e_end) && (ip + iSize == iend); cSize = lastBlock ? - ZSTD_compressEnd(zcs, cDst, oSize, ip, iSize) : - ZSTD_compressContinue(zcs, cDst, oSize, ip, iSize); + ZSTD_compressEnd_public(zcs, cDst, oSize, ip, iSize) : + ZSTD_compressContinue_public(zcs, cDst, oSize, ip, iSize); /* Consume the input prior to error checking to mirror buffered mode. */ - if (iSize > 0) - ip += iSize; + if (ip) ip += iSize; FORWARD_IF_ERROR(cSize, "%s", lastBlock ? "ZSTD_compressEnd failed" : "ZSTD_compressContinue failed"); zcs->frameEnded = lastBlock; - if (lastBlock) - assert(ip == iend); + if (lastBlock) assert(ip == iend); } if (cDst == op) { /* no need to flush */ op += cSize; @@ -5513,8 +6066,10 @@ size_t ZSTD_compressStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output, ZSTD_inBuf /* After a compression call set the expected input/output buffer. * This is validated at the start of the next compression call. */ -static void ZSTD_setBufferExpectations(ZSTD_CCtx* cctx, ZSTD_outBuffer const* output, ZSTD_inBuffer const* input) +static void +ZSTD_setBufferExpectations(ZSTD_CCtx* cctx, const ZSTD_outBuffer* output, const ZSTD_inBuffer* input) { + DEBUGLOG(5, "ZSTD_setBufferExpectations (for advanced stable in/out modes)"); if (cctx->appliedParams.inBufferMode == ZSTD_bm_stable) { cctx->expectedInBuffer = *input; } @@ -5533,22 +6088,22 @@ static size_t ZSTD_checkBufferStability(ZSTD_CCtx const* cctx, { if (cctx->appliedParams.inBufferMode == ZSTD_bm_stable) { ZSTD_inBuffer const expect = cctx->expectedInBuffer; - if (expect.src != input->src || expect.pos != input->pos || expect.size != input->size) - RETURN_ERROR(srcBuffer_wrong, "ZSTD_c_stableInBuffer enabled but input differs!"); - if (endOp != ZSTD_e_end) - RETURN_ERROR(srcBuffer_wrong, "ZSTD_c_stableInBuffer can only be used with ZSTD_e_end!"); + if (expect.src != input->src || expect.pos != input->pos) + RETURN_ERROR(stabilityCondition_notRespected, "ZSTD_c_stableInBuffer enabled but input differs!"); } + (void)endOp; if (cctx->appliedParams.outBufferMode == ZSTD_bm_stable) { size_t const outBufferSize = output->size - output->pos; if (cctx->expectedOutBufferSize != outBufferSize) - RETURN_ERROR(dstBuffer_wrong, "ZSTD_c_stableOutBuffer enabled but output size differs!"); + RETURN_ERROR(stabilityCondition_notRespected, "ZSTD_c_stableOutBuffer enabled but output size differs!"); } return 0; } static size_t ZSTD_CCtx_init_compressStream2(ZSTD_CCtx* cctx, ZSTD_EndDirective endOp, - size_t inSize) { + size_t inSize) +{ ZSTD_CCtx_params params = cctx->requestedParams; ZSTD_prefixDict const prefixDict = cctx->prefixDict; FORWARD_IF_ERROR( ZSTD_initLocalDict(cctx) , ""); /* Init the local dict if present. */ @@ -5562,9 +6117,9 @@ static size_t ZSTD_CCtx_init_compressStream2(ZSTD_CCtx* cctx, params.compressionLevel = cctx->cdict->compressionLevel; } DEBUGLOG(4, "ZSTD_compressStream2 : transparent init stage"); - if (endOp == ZSTD_e_end) cctx->pledgedSrcSizePlusOne = inSize + 1; /* auto-fix pledgedSrcSize */ - { - size_t const dictSize = prefixDict.dict + if (endOp == ZSTD_e_end) cctx->pledgedSrcSizePlusOne = inSize + 1; /* auto-determine pledgedSrcSize */ + + { size_t const dictSize = prefixDict.dict ? prefixDict.dictSize : (cctx->cdict ? cctx->cdict->dictContentSize : 0); ZSTD_cParamMode_e const mode = ZSTD_getCParamMode(cctx->cdict, ¶ms, cctx->pledgedSrcSizePlusOne - 1); @@ -5576,8 +6131,18 @@ static size_t ZSTD_CCtx_init_compressStream2(ZSTD_CCtx* cctx, params.useBlockSplitter = ZSTD_resolveBlockSplitterMode(params.useBlockSplitter, ¶ms.cParams); params.ldmParams.enableLdm = ZSTD_resolveEnableLdm(params.ldmParams.enableLdm, ¶ms.cParams); params.useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(params.useRowMatchFinder, ¶ms.cParams); + params.validateSequences = ZSTD_resolveExternalSequenceValidation(params.validateSequences); + params.maxBlockSize = ZSTD_resolveMaxBlockSize(params.maxBlockSize); + params.searchForExternalRepcodes = ZSTD_resolveExternalRepcodeSearch(params.searchForExternalRepcodes, params.compressionLevel); #ifdef ZSTD_MULTITHREAD + /* If external matchfinder is enabled, make sure to fail before checking job size (for consistency) */ + RETURN_ERROR_IF( + params.useSequenceProducer == 1 && params.nbWorkers >= 1, + parameter_combination_unsupported, + "External sequence producer isn't supported with nbWorkers >= 1" + ); + if ((cctx->pledgedSrcSizePlusOne-1) <= ZSTDMT_JOBSIZE_MIN) { params.nbWorkers = 0; /* do not invoke multi-threading when src size is too small */ } @@ -5605,7 +6170,7 @@ static size_t ZSTD_CCtx_init_compressStream2(ZSTD_CCtx* cctx, cctx->streamStage = zcss_load; cctx->appliedParams = params; } else -#endif +#endif /* ZSTD_MULTITHREAD */ { U64 const pledgedSrcSize = cctx->pledgedSrcSizePlusOne - 1; assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams))); FORWARD_IF_ERROR( ZSTD_compressBegin_internal(cctx, @@ -5631,6 +6196,8 @@ static size_t ZSTD_CCtx_init_compressStream2(ZSTD_CCtx* cctx, return 0; } +/* @return provides a minimum amount of data remaining to be flushed from internal buffers + */ size_t ZSTD_compressStream2( ZSTD_CCtx* cctx, ZSTD_outBuffer* output, ZSTD_inBuffer* input, @@ -5645,8 +6212,27 @@ size_t ZSTD_compressStream2( ZSTD_CCtx* cctx, /* transparent initialization stage */ if (cctx->streamStage == zcss_init) { - FORWARD_IF_ERROR(ZSTD_CCtx_init_compressStream2(cctx, endOp, input->size), "CompressStream2 initialization failed"); - ZSTD_setBufferExpectations(cctx, output, input); /* Set initial buffer expectations now that we've initialized */ + size_t const inputSize = input->size - input->pos; /* no obligation to start from pos==0 */ + size_t const totalInputSize = inputSize + cctx->stableIn_notConsumed; + if ( (cctx->requestedParams.inBufferMode == ZSTD_bm_stable) /* input is presumed stable, across invocations */ + && (endOp == ZSTD_e_continue) /* no flush requested, more input to come */ + && (totalInputSize < ZSTD_BLOCKSIZE_MAX) ) { /* not even reached one block yet */ + if (cctx->stableIn_notConsumed) { /* not the first time */ + /* check stable source guarantees */ + RETURN_ERROR_IF(input->src != cctx->expectedInBuffer.src, stabilityCondition_notRespected, "stableInBuffer condition not respected: wrong src pointer"); + RETURN_ERROR_IF(input->pos != cctx->expectedInBuffer.size, stabilityCondition_notRespected, "stableInBuffer condition not respected: externally modified pos"); + } + /* pretend input was consumed, to give a sense forward progress */ + input->pos = input->size; + /* save stable inBuffer, for later control, and flush/end */ + cctx->expectedInBuffer = *input; + /* but actually input wasn't consumed, so keep track of position from where compression shall resume */ + cctx->stableIn_notConsumed += inputSize; + /* don't initialize yet, wait for the first block of flush() order, for better parameters adaptation */ + return ZSTD_FRAMEHEADERSIZE_MIN(cctx->requestedParams.format); /* at least some header to produce */ + } + FORWARD_IF_ERROR(ZSTD_CCtx_init_compressStream2(cctx, endOp, totalInputSize), "compressStream2 initialization failed"); + ZSTD_setBufferExpectations(cctx, output, input); /* Set initial buffer expectations now that we've initialized */ } /* end of transparent initialization stage */ @@ -5659,6 +6245,13 @@ size_t ZSTD_compressStream2( ZSTD_CCtx* cctx, ZSTDMT_updateCParams_whileCompressing(cctx->mtctx, &cctx->requestedParams); cctx->cParamsChanged = 0; } + if (cctx->stableIn_notConsumed) { + assert(cctx->appliedParams.inBufferMode == ZSTD_bm_stable); + /* some early data was skipped - make it available for consumption */ + assert(input->pos >= cctx->stableIn_notConsumed); + input->pos -= cctx->stableIn_notConsumed; + cctx->stableIn_notConsumed = 0; + } for (;;) { size_t const ipos = input->pos; size_t const opos = output->pos; @@ -5697,7 +6290,7 @@ size_t ZSTD_compressStream2( ZSTD_CCtx* cctx, ZSTD_setBufferExpectations(cctx, output, input); return flushMin; } -#endif +#endif /* ZSTD_MULTITHREAD */ FORWARD_IF_ERROR( ZSTD_compressStream_generic(cctx, output, input, endOp) , ""); DEBUGLOG(5, "completed ZSTD_compressStream2"); ZSTD_setBufferExpectations(cctx, output, input); @@ -5710,13 +6303,20 @@ size_t ZSTD_compressStream2_simpleArgs ( const void* src, size_t srcSize, size_t* srcPos, ZSTD_EndDirective endOp) { - ZSTD_outBuffer output = { dst, dstCapacity, *dstPos }; - ZSTD_inBuffer input = { src, srcSize, *srcPos }; + ZSTD_outBuffer output; + ZSTD_inBuffer input; + output.dst = dst; + output.size = dstCapacity; + output.pos = *dstPos; + input.src = src; + input.size = srcSize; + input.pos = *srcPos; /* ZSTD_compressStream2() will check validity of dstPos and srcPos */ - size_t const cErr = ZSTD_compressStream2(cctx, &output, &input, endOp); - *dstPos = output.pos; - *srcPos = input.pos; - return cErr; + { size_t const cErr = ZSTD_compressStream2(cctx, &output, &input, endOp); + *dstPos = output.pos; + *srcPos = input.pos; + return cErr; + } } size_t ZSTD_compress2(ZSTD_CCtx* cctx, @@ -5739,6 +6339,7 @@ size_t ZSTD_compress2(ZSTD_CCtx* cctx, /* Reset to the original values. */ cctx->requestedParams.inBufferMode = originalInBufferMode; cctx->requestedParams.outBufferMode = originalOutBufferMode; + FORWARD_IF_ERROR(result, "ZSTD_compressStream2_simpleArgs failed"); if (result != 0) { /* compression not completed, due to lack of output space */ assert(oPos == dstCapacity); @@ -5749,64 +6350,61 @@ size_t ZSTD_compress2(ZSTD_CCtx* cctx, } } -typedef struct { - U32 idx; /* Index in array of ZSTD_Sequence */ - U32 posInSequence; /* Position within sequence at idx */ - size_t posInSrc; /* Number of bytes given by sequences provided so far */ -} ZSTD_sequencePosition; - /* ZSTD_validateSequence() : * @offCode : is presumed to follow format required by ZSTD_storeSeq() * @returns a ZSTD error code if sequence is not valid */ static size_t -ZSTD_validateSequence(U32 offCode, U32 matchLength, - size_t posInSrc, U32 windowLog, size_t dictSize) +ZSTD_validateSequence(U32 offCode, U32 matchLength, U32 minMatch, + size_t posInSrc, U32 windowLog, size_t dictSize, int useSequenceProducer) { - U32 const windowSize = 1 << windowLog; - /* posInSrc represents the amount of data the the decoder would decode up to this point. + U32 const windowSize = 1u << windowLog; + /* posInSrc represents the amount of data the decoder would decode up to this point. * As long as the amount of data decoded is less than or equal to window size, offsets may be * larger than the total length of output decoded in order to reference the dict, even larger than * window size. After output surpasses windowSize, we're limited to windowSize offsets again. */ size_t const offsetBound = posInSrc > windowSize ? (size_t)windowSize : posInSrc + (size_t)dictSize; - RETURN_ERROR_IF(offCode > STORE_OFFSET(offsetBound), corruption_detected, "Offset too large!"); - RETURN_ERROR_IF(matchLength < MINMATCH, corruption_detected, "Matchlength too small"); + size_t const matchLenLowerBound = (minMatch == 3 || useSequenceProducer) ? 3 : 4; + RETURN_ERROR_IF(offCode > OFFSET_TO_OFFBASE(offsetBound), externalSequences_invalid, "Offset too large!"); + /* Validate maxNbSeq is large enough for the given matchLength and minMatch */ + RETURN_ERROR_IF(matchLength < matchLenLowerBound, externalSequences_invalid, "Matchlength too small for the minMatch"); return 0; } /* Returns an offset code, given a sequence's raw offset, the ongoing repcode array, and whether litLength == 0 */ -static U32 ZSTD_finalizeOffCode(U32 rawOffset, const U32 rep[ZSTD_REP_NUM], U32 ll0) +static U32 ZSTD_finalizeOffBase(U32 rawOffset, const U32 rep[ZSTD_REP_NUM], U32 ll0) { - U32 offCode = STORE_OFFSET(rawOffset); + U32 offBase = OFFSET_TO_OFFBASE(rawOffset); if (!ll0 && rawOffset == rep[0]) { - offCode = STORE_REPCODE_1; + offBase = REPCODE1_TO_OFFBASE; } else if (rawOffset == rep[1]) { - offCode = STORE_REPCODE(2 - ll0); + offBase = REPCODE_TO_OFFBASE(2 - ll0); } else if (rawOffset == rep[2]) { - offCode = STORE_REPCODE(3 - ll0); + offBase = REPCODE_TO_OFFBASE(3 - ll0); } else if (ll0 && rawOffset == rep[0] - 1) { - offCode = STORE_REPCODE_3; + offBase = REPCODE3_TO_OFFBASE; } - return offCode; + return offBase; } -/* Returns 0 on success, and a ZSTD_error otherwise. This function scans through an array of - * ZSTD_Sequence, storing the sequences it finds, until it reaches a block delimiter. - */ -static size_t +size_t ZSTD_copySequencesToSeqStoreExplicitBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition* seqPos, const ZSTD_Sequence* const inSeqs, size_t inSeqsSize, - const void* src, size_t blockSize) + const void* src, size_t blockSize, + ZSTD_paramSwitch_e externalRepSearch) { U32 idx = seqPos->idx; + U32 const startIdx = idx; BYTE const* ip = (BYTE const*)(src); const BYTE* const iend = ip + blockSize; repcodes_t updatedRepcodes; U32 dictSize; + DEBUGLOG(5, "ZSTD_copySequencesToSeqStoreExplicitBlockDelim (blockSize = %zu)", blockSize); + if (cctx->cdict) { dictSize = (U32)cctx->cdict->dictContentSize; } else if (cctx->prefixDict.dict) { @@ -5815,25 +6413,55 @@ ZSTD_copySequencesToSeqStoreExplicitBlockDelim(ZSTD_CCtx* cctx, dictSize = 0; } ZSTD_memcpy(updatedRepcodes.rep, cctx->blockState.prevCBlock->rep, sizeof(repcodes_t)); - for (; (inSeqs[idx].matchLength != 0 || inSeqs[idx].offset != 0) && idx < inSeqsSize; ++idx) { + for (; idx < inSeqsSize && (inSeqs[idx].matchLength != 0 || inSeqs[idx].offset != 0); ++idx) { U32 const litLength = inSeqs[idx].litLength; - U32 const ll0 = (litLength == 0); U32 const matchLength = inSeqs[idx].matchLength; - U32 const offCode = ZSTD_finalizeOffCode(inSeqs[idx].offset, updatedRepcodes.rep, ll0); - ZSTD_updateRep(updatedRepcodes.rep, offCode, ll0); + U32 offBase; - DEBUGLOG(6, "Storing sequence: (of: %u, ml: %u, ll: %u)", offCode, matchLength, litLength); + if (externalRepSearch == ZSTD_ps_disable) { + offBase = OFFSET_TO_OFFBASE(inSeqs[idx].offset); + } else { + U32 const ll0 = (litLength == 0); + offBase = ZSTD_finalizeOffBase(inSeqs[idx].offset, updatedRepcodes.rep, ll0); + ZSTD_updateRep(updatedRepcodes.rep, offBase, ll0); + } + + DEBUGLOG(6, "Storing sequence: (of: %u, ml: %u, ll: %u)", offBase, matchLength, litLength); if (cctx->appliedParams.validateSequences) { seqPos->posInSrc += litLength + matchLength; - FORWARD_IF_ERROR(ZSTD_validateSequence(offCode, matchLength, seqPos->posInSrc, - cctx->appliedParams.cParams.windowLog, dictSize), + FORWARD_IF_ERROR(ZSTD_validateSequence(offBase, matchLength, cctx->appliedParams.cParams.minMatch, seqPos->posInSrc, + cctx->appliedParams.cParams.windowLog, dictSize, cctx->appliedParams.useSequenceProducer), "Sequence validation failed"); } - RETURN_ERROR_IF(idx - seqPos->idx > cctx->seqStore.maxNbSeq, memory_allocation, + RETURN_ERROR_IF(idx - seqPos->idx >= cctx->seqStore.maxNbSeq, externalSequences_invalid, "Not enough memory allocated. Try adjusting ZSTD_c_minMatch."); - ZSTD_storeSeq(&cctx->seqStore, litLength, ip, iend, offCode, matchLength); + ZSTD_storeSeq(&cctx->seqStore, litLength, ip, iend, offBase, matchLength); ip += matchLength + litLength; } + + /* If we skipped repcode search while parsing, we need to update repcodes now */ + assert(externalRepSearch != ZSTD_ps_auto); + assert(idx >= startIdx); + if (externalRepSearch == ZSTD_ps_disable && idx != startIdx) { + U32* const rep = updatedRepcodes.rep; + U32 lastSeqIdx = idx - 1; /* index of last non-block-delimiter sequence */ + + if (lastSeqIdx >= startIdx + 2) { + rep[2] = inSeqs[lastSeqIdx - 2].offset; + rep[1] = inSeqs[lastSeqIdx - 1].offset; + rep[0] = inSeqs[lastSeqIdx].offset; + } else if (lastSeqIdx == startIdx + 1) { + rep[2] = rep[0]; + rep[1] = inSeqs[lastSeqIdx - 1].offset; + rep[0] = inSeqs[lastSeqIdx].offset; + } else { + assert(lastSeqIdx == startIdx); + rep[2] = rep[1]; + rep[1] = rep[0]; + rep[0] = inSeqs[lastSeqIdx].offset; + } + } + ZSTD_memcpy(cctx->blockState.nextCBlock->rep, updatedRepcodes.rep, sizeof(repcodes_t)); if (inSeqs[idx].litLength) { @@ -5842,26 +6470,15 @@ ZSTD_copySequencesToSeqStoreExplicitBlockDelim(ZSTD_CCtx* cctx, ip += inSeqs[idx].litLength; seqPos->posInSrc += inSeqs[idx].litLength; } - RETURN_ERROR_IF(ip != iend, corruption_detected, "Blocksize doesn't agree with block delimiter!"); + RETURN_ERROR_IF(ip != iend, externalSequences_invalid, "Blocksize doesn't agree with block delimiter!"); seqPos->idx = idx+1; return 0; } -/* Returns the number of bytes to move the current read position back by. Only non-zero - * if we ended up splitting a sequence. Otherwise, it may return a ZSTD error if something - * went wrong. - * - * This function will attempt to scan through blockSize bytes represented by the sequences - * in inSeqs, storing any (partial) sequences. - * - * Occasionally, we may want to change the actual number of bytes we consumed from inSeqs to - * avoid splitting a match, or to avoid splitting a match such that it would produce a match - * smaller than MINMATCH. In this case, we return the number of bytes that we didn't read from this block. - */ -static size_t +size_t ZSTD_copySequencesToSeqStoreNoBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition* seqPos, const ZSTD_Sequence* const inSeqs, size_t inSeqsSize, - const void* src, size_t blockSize) + const void* src, size_t blockSize, ZSTD_paramSwitch_e externalRepSearch) { U32 idx = seqPos->idx; U32 startPosInSequence = seqPos->posInSequence; @@ -5873,6 +6490,9 @@ ZSTD_copySequencesToSeqStoreNoBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition* U32 bytesAdjustment = 0; U32 finalMatchSplit = 0; + /* TODO(embg) support fast parsing mode in noBlockDelim mode */ + (void)externalRepSearch; + if (cctx->cdict) { dictSize = cctx->cdict->dictContentSize; } else if (cctx->prefixDict.dict) { @@ -5880,7 +6500,7 @@ ZSTD_copySequencesToSeqStoreNoBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition* } else { dictSize = 0; } - DEBUGLOG(5, "ZSTD_copySequencesToSeqStore: idx: %u PIS: %u blockSize: %zu", idx, startPosInSequence, blockSize); + DEBUGLOG(5, "ZSTD_copySequencesToSeqStoreNoBlockDelim: idx: %u PIS: %u blockSize: %zu", idx, startPosInSequence, blockSize); DEBUGLOG(5, "Start seq: idx: %u (of: %u ml: %u ll: %u)", idx, inSeqs[idx].offset, inSeqs[idx].matchLength, inSeqs[idx].litLength); ZSTD_memcpy(updatedRepcodes.rep, cctx->blockState.prevCBlock->rep, sizeof(repcodes_t)); while (endPosInSequence && idx < inSeqsSize && !finalMatchSplit) { @@ -5888,7 +6508,7 @@ ZSTD_copySequencesToSeqStoreNoBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition* U32 litLength = currSeq.litLength; U32 matchLength = currSeq.matchLength; U32 const rawOffset = currSeq.offset; - U32 offCode; + U32 offBase; /* Modify the sequence depending on where endPosInSequence lies */ if (endPosInSequence >= currSeq.litLength + currSeq.matchLength) { @@ -5902,7 +6522,6 @@ ZSTD_copySequencesToSeqStoreNoBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition* /* Move to the next sequence */ endPosInSequence -= currSeq.litLength + currSeq.matchLength; startPosInSequence = 0; - idx++; } else { /* This is the final (partial) sequence we're adding from inSeqs, and endPosInSequence does not reach the end of the match. So, we have to split the sequence */ @@ -5942,21 +6561,23 @@ ZSTD_copySequencesToSeqStoreNoBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition* } /* Check if this offset can be represented with a repcode */ { U32 const ll0 = (litLength == 0); - offCode = ZSTD_finalizeOffCode(rawOffset, updatedRepcodes.rep, ll0); - ZSTD_updateRep(updatedRepcodes.rep, offCode, ll0); + offBase = ZSTD_finalizeOffBase(rawOffset, updatedRepcodes.rep, ll0); + ZSTD_updateRep(updatedRepcodes.rep, offBase, ll0); } if (cctx->appliedParams.validateSequences) { seqPos->posInSrc += litLength + matchLength; - FORWARD_IF_ERROR(ZSTD_validateSequence(offCode, matchLength, seqPos->posInSrc, - cctx->appliedParams.cParams.windowLog, dictSize), + FORWARD_IF_ERROR(ZSTD_validateSequence(offBase, matchLength, cctx->appliedParams.cParams.minMatch, seqPos->posInSrc, + cctx->appliedParams.cParams.windowLog, dictSize, cctx->appliedParams.useSequenceProducer), "Sequence validation failed"); } - DEBUGLOG(6, "Storing sequence: (of: %u, ml: %u, ll: %u)", offCode, matchLength, litLength); - RETURN_ERROR_IF(idx - seqPos->idx > cctx->seqStore.maxNbSeq, memory_allocation, + DEBUGLOG(6, "Storing sequence: (of: %u, ml: %u, ll: %u)", offBase, matchLength, litLength); + RETURN_ERROR_IF(idx - seqPos->idx >= cctx->seqStore.maxNbSeq, externalSequences_invalid, "Not enough memory allocated. Try adjusting ZSTD_c_minMatch."); - ZSTD_storeSeq(&cctx->seqStore, litLength, ip, iend, offCode, matchLength); + ZSTD_storeSeq(&cctx->seqStore, litLength, ip, iend, offBase, matchLength); ip += matchLength + litLength; + if (!finalMatchSplit) + idx++; /* Next Sequence */ } DEBUGLOG(5, "Ending seq: idx: %u (of: %u ml: %u ll: %u)", idx, inSeqs[idx].offset, inSeqs[idx].matchLength, inSeqs[idx].litLength); assert(idx == inSeqsSize || endPosInSequence <= inSeqs[idx].litLength + inSeqs[idx].matchLength); @@ -5979,7 +6600,7 @@ ZSTD_copySequencesToSeqStoreNoBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition* typedef size_t (*ZSTD_sequenceCopier) (ZSTD_CCtx* cctx, ZSTD_sequencePosition* seqPos, const ZSTD_Sequence* const inSeqs, size_t inSeqsSize, - const void* src, size_t blockSize); + const void* src, size_t blockSize, ZSTD_paramSwitch_e externalRepSearch); static ZSTD_sequenceCopier ZSTD_selectSequenceCopier(ZSTD_sequenceFormat_e mode) { ZSTD_sequenceCopier sequenceCopier = NULL; @@ -5993,6 +6614,57 @@ static ZSTD_sequenceCopier ZSTD_selectSequenceCopier(ZSTD_sequenceFormat_e mode) return sequenceCopier; } +/* Discover the size of next block by searching for the delimiter. + * Note that a block delimiter **must** exist in this mode, + * otherwise it's an input error. + * The block size retrieved will be later compared to ensure it remains within bounds */ +static size_t +blockSize_explicitDelimiter(const ZSTD_Sequence* inSeqs, size_t inSeqsSize, ZSTD_sequencePosition seqPos) +{ + int end = 0; + size_t blockSize = 0; + size_t spos = seqPos.idx; + DEBUGLOG(6, "blockSize_explicitDelimiter : seq %zu / %zu", spos, inSeqsSize); + assert(spos <= inSeqsSize); + while (spos < inSeqsSize) { + end = (inSeqs[spos].offset == 0); + blockSize += inSeqs[spos].litLength + inSeqs[spos].matchLength; + if (end) { + if (inSeqs[spos].matchLength != 0) + RETURN_ERROR(externalSequences_invalid, "delimiter format error : both matchlength and offset must be == 0"); + break; + } + spos++; + } + if (!end) + RETURN_ERROR(externalSequences_invalid, "Reached end of sequences without finding a block delimiter"); + return blockSize; +} + +/* More a "target" block size */ +static size_t blockSize_noDelimiter(size_t blockSize, size_t remaining) +{ + int const lastBlock = (remaining <= blockSize); + return lastBlock ? remaining : blockSize; +} + +static size_t determine_blockSize(ZSTD_sequenceFormat_e mode, + size_t blockSize, size_t remaining, + const ZSTD_Sequence* inSeqs, size_t inSeqsSize, ZSTD_sequencePosition seqPos) +{ + DEBUGLOG(6, "determine_blockSize : remainingSize = %zu", remaining); + if (mode == ZSTD_sf_noBlockDelimiters) + return blockSize_noDelimiter(blockSize, remaining); + { size_t const explicitBlockSize = blockSize_explicitDelimiter(inSeqs, inSeqsSize, seqPos); + FORWARD_IF_ERROR(explicitBlockSize, "Error while determining block size with explicit delimiters"); + if (explicitBlockSize > blockSize) + RETURN_ERROR(externalSequences_invalid, "sequences incorrectly define a too large block"); + if (explicitBlockSize > remaining) + RETURN_ERROR(externalSequences_invalid, "sequences define a frame longer than source"); + return explicitBlockSize; + } +} + /* Compress, block-by-block, all of the sequences given. * * Returns the cumulative size of all compressed blocks (including their headers), @@ -6005,9 +6677,6 @@ ZSTD_compressSequences_internal(ZSTD_CCtx* cctx, const void* src, size_t srcSize) { size_t cSize = 0; - U32 lastBlock; - size_t blockSize; - size_t compressedSeqsSize; size_t remaining = srcSize; ZSTD_sequencePosition seqPos = {0, 0, 0}; @@ -6027,22 +6696,29 @@ ZSTD_compressSequences_internal(ZSTD_CCtx* cctx, } while (remaining) { + size_t compressedSeqsSize; size_t cBlockSize; size_t additionalByteAdjustment; - lastBlock = remaining <= cctx->blockSize; - blockSize = lastBlock ? (U32)remaining : (U32)cctx->blockSize; + size_t blockSize = determine_blockSize(cctx->appliedParams.blockDelimiters, + cctx->blockSize, remaining, + inSeqs, inSeqsSize, seqPos); + U32 const lastBlock = (blockSize == remaining); + FORWARD_IF_ERROR(blockSize, "Error while trying to determine block size"); + assert(blockSize <= remaining); ZSTD_resetSeqStore(&cctx->seqStore); - DEBUGLOG(4, "Working on new block. Blocksize: %zu", blockSize); + DEBUGLOG(5, "Working on new block. Blocksize: %zu (total:%zu)", blockSize, (ip - (const BYTE*)src) + blockSize); - additionalByteAdjustment = sequenceCopier(cctx, &seqPos, inSeqs, inSeqsSize, ip, blockSize); + additionalByteAdjustment = sequenceCopier(cctx, &seqPos, inSeqs, inSeqsSize, ip, blockSize, cctx->appliedParams.searchForExternalRepcodes); FORWARD_IF_ERROR(additionalByteAdjustment, "Bad sequence copy"); blockSize -= additionalByteAdjustment; /* If blocks are too small, emit as a nocompress block */ - if (blockSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) { + /* TODO: See 3090. We reduced MIN_CBLOCK_SIZE from 3 to 2 so to compensate we are adding + * additional 1. We need to revisit and change this logic to be more consistent */ + if (blockSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1+1) { cBlockSize = ZSTD_noCompressBlock(op, dstCapacity, ip, blockSize, lastBlock); FORWARD_IF_ERROR(cBlockSize, "Nocompress block failed"); - DEBUGLOG(4, "Block too small, writing out nocompress block: cSize: %zu", cBlockSize); + DEBUGLOG(5, "Block too small, writing out nocompress block: cSize: %zu", cBlockSize); cSize += cBlockSize; ip += blockSize; op += cBlockSize; @@ -6051,6 +6727,7 @@ ZSTD_compressSequences_internal(ZSTD_CCtx* cctx, continue; } + RETURN_ERROR_IF(dstCapacity < ZSTD_blockHeaderSize, dstSize_tooSmall, "not enough dstCapacity to write a new compressed block"); compressedSeqsSize = ZSTD_entropyCompressSeqStore(&cctx->seqStore, &cctx->blockState.prevCBlock->entropy, &cctx->blockState.nextCBlock->entropy, &cctx->appliedParams, @@ -6059,11 +6736,11 @@ ZSTD_compressSequences_internal(ZSTD_CCtx* cctx, cctx->entropyWorkspace, ENTROPY_WORKSPACE_SIZE /* statically allocated in resetCCtx */, cctx->bmi2); FORWARD_IF_ERROR(compressedSeqsSize, "Compressing sequences of block failed"); - DEBUGLOG(4, "Compressed sequences size: %zu", compressedSeqsSize); + DEBUGLOG(5, "Compressed sequences size: %zu", compressedSeqsSize); if (!cctx->isFirstBlock && ZSTD_maybeRLE(&cctx->seqStore) && - ZSTD_isRLE((BYTE const*)src, srcSize)) { + ZSTD_isRLE(ip, blockSize)) { /* We don't want to emit our first block as a RLE even if it qualifies because * doing so will cause the decoder (cli only) to throw a "should consume all input error." * This is only an issue for zstd <= v1.4.3 @@ -6074,12 +6751,12 @@ ZSTD_compressSequences_internal(ZSTD_CCtx* cctx, if (compressedSeqsSize == 0) { /* ZSTD_noCompressBlock writes the block header as well */ cBlockSize = ZSTD_noCompressBlock(op, dstCapacity, ip, blockSize, lastBlock); - FORWARD_IF_ERROR(cBlockSize, "Nocompress block failed"); - DEBUGLOG(4, "Writing out nocompress block, size: %zu", cBlockSize); + FORWARD_IF_ERROR(cBlockSize, "ZSTD_noCompressBlock failed"); + DEBUGLOG(5, "Writing out nocompress block, size: %zu", cBlockSize); } else if (compressedSeqsSize == 1) { cBlockSize = ZSTD_rleCompressBlock(op, dstCapacity, *ip, blockSize, lastBlock); - FORWARD_IF_ERROR(cBlockSize, "RLE compress block failed"); - DEBUGLOG(4, "Writing out RLE block, size: %zu", cBlockSize); + FORWARD_IF_ERROR(cBlockSize, "ZSTD_rleCompressBlock failed"); + DEBUGLOG(5, "Writing out RLE block, size: %zu", cBlockSize); } else { U32 cBlockHeader; /* Error checking and repcodes update */ @@ -6091,11 +6768,10 @@ ZSTD_compressSequences_internal(ZSTD_CCtx* cctx, cBlockHeader = lastBlock + (((U32)bt_compressed)<<1) + (U32)(compressedSeqsSize << 3); MEM_writeLE24(op, cBlockHeader); cBlockSize = ZSTD_blockHeaderSize + compressedSeqsSize; - DEBUGLOG(4, "Writing out compressed block, size: %zu", cBlockSize); + DEBUGLOG(5, "Writing out compressed block, size: %zu", cBlockSize); } cSize += cBlockSize; - DEBUGLOG(4, "cSize running total: %zu", cSize); if (lastBlock) { break; @@ -6106,12 +6782,15 @@ ZSTD_compressSequences_internal(ZSTD_CCtx* cctx, dstCapacity -= cBlockSize; cctx->isFirstBlock = 0; } + DEBUGLOG(5, "cSize running total: %zu (remaining dstCapacity=%zu)", cSize, dstCapacity); } + DEBUGLOG(4, "cSize final total: %zu", cSize); return cSize; } -size_t ZSTD_compressSequences(ZSTD_CCtx* const cctx, void* dst, size_t dstCapacity, +size_t ZSTD_compressSequences(ZSTD_CCtx* cctx, + void* dst, size_t dstCapacity, const ZSTD_Sequence* inSeqs, size_t inSeqsSize, const void* src, size_t srcSize) { @@ -6121,7 +6800,7 @@ size_t ZSTD_compressSequences(ZSTD_CCtx* const cctx, void* dst, size_t dstCapaci size_t frameHeaderSize = 0; /* Transparent initialization stage, same as compressStream2() */ - DEBUGLOG(3, "ZSTD_compressSequences()"); + DEBUGLOG(4, "ZSTD_compressSequences (dstCapacity=%zu)", dstCapacity); assert(cctx != NULL); FORWARD_IF_ERROR(ZSTD_CCtx_init_compressStream2(cctx, ZSTD_e_end, srcSize), "CCtx initialization failed"); /* Begin writing output, starting with frame header */ @@ -6149,26 +6828,34 @@ size_t ZSTD_compressSequences(ZSTD_CCtx* const cctx, void* dst, size_t dstCapaci cSize += 4; } - DEBUGLOG(3, "Final compressed size: %zu", cSize); + DEBUGLOG(4, "Final compressed size: %zu", cSize); return cSize; } /*====== Finalize ======*/ +static ZSTD_inBuffer inBuffer_forEndFlush(const ZSTD_CStream* zcs) +{ + const ZSTD_inBuffer nullInput = { NULL, 0, 0 }; + const int stableInput = (zcs->appliedParams.inBufferMode == ZSTD_bm_stable); + return stableInput ? zcs->expectedInBuffer : nullInput; +} + /*! ZSTD_flushStream() : * @return : amount of data remaining to flush */ size_t ZSTD_flushStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output) { - ZSTD_inBuffer input = { NULL, 0, 0 }; + ZSTD_inBuffer input = inBuffer_forEndFlush(zcs); + input.size = input.pos; /* do not ingest more input during flush */ return ZSTD_compressStream2(zcs, output, &input, ZSTD_e_flush); } size_t ZSTD_endStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output) { - ZSTD_inBuffer input = { NULL, 0, 0 }; + ZSTD_inBuffer input = inBuffer_forEndFlush(zcs); size_t const remainingToFlush = ZSTD_compressStream2(zcs, output, &input, ZSTD_e_end); - FORWARD_IF_ERROR( remainingToFlush , "ZSTD_compressStream2 failed"); + FORWARD_IF_ERROR(remainingToFlush , "ZSTD_compressStream2(,,ZSTD_e_end) failed"); if (zcs->appliedParams.nbWorkers > 0) return remainingToFlush; /* minimal estimation */ /* single thread mode : attempt to calculate remaining to flush more precisely */ { size_t const lastBlockSize = zcs->frameEnded ? 0 : ZSTD_BLOCKHEADERSIZE; @@ -6290,7 +6977,7 @@ static ZSTD_compressionParameters ZSTD_getCParams_internal(int compressionLevel, cp.targetLength = (unsigned)(-clampedCompressionLevel); } /* refine parameters based on srcSize & dictSize */ - return ZSTD_adjustCParams_internal(cp, srcSizeHint, dictSize, mode); + return ZSTD_adjustCParams_internal(cp, srcSizeHint, dictSize, mode, ZSTD_ps_auto); } } @@ -6325,3 +7012,21 @@ ZSTD_parameters ZSTD_getParams(int compressionLevel, unsigned long long srcSizeH if (srcSizeHint == 0) srcSizeHint = ZSTD_CONTENTSIZE_UNKNOWN; return ZSTD_getParams_internal(compressionLevel, srcSizeHint, dictSize, ZSTD_cpm_unknown); } + +void ZSTD_registerSequenceProducer( + ZSTD_CCtx* zc, void* mState, + ZSTD_sequenceProducer_F* mFinder +) { + if (mFinder != NULL) { + ZSTD_externalMatchCtx emctx; + emctx.mState = mState; + emctx.mFinder = mFinder; + emctx.seqBuffer = NULL; + emctx.seqBufferCapacity = 0; + zc->externalMatchCtx = emctx; + zc->requestedParams.useSequenceProducer = 1; + } else { + ZSTD_memset(&zc->externalMatchCtx, 0, sizeof(zc->externalMatchCtx)); + zc->requestedParams.useSequenceProducer = 0; + } +} diff --git a/internal-complibs/zstd-1.5.2/compress/zstd_compress_internal.h b/internal-complibs/zstd-1.5.5/compress/zstd_compress_internal.h similarity index 81% rename from internal-complibs/zstd-1.5.2/compress/zstd_compress_internal.h rename to internal-complibs/zstd-1.5.5/compress/zstd_compress_internal.h index c406e794..10f68d01 100644 --- a/internal-complibs/zstd-1.5.2/compress/zstd_compress_internal.h +++ b/internal-complibs/zstd-1.5.5/compress/zstd_compress_internal.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -23,6 +23,7 @@ #ifdef ZSTD_MULTITHREAD # include "zstdmt_compress.h" #endif +#include "../common/bits.h" /* ZSTD_highbit32, ZSTD_NbCommonBytes */ #if defined (__cplusplus) extern "C" { @@ -117,12 +118,13 @@ typedef struct { /** ZSTD_buildBlockEntropyStats() : * Builds entropy for the block. * @return : 0 on success or error code */ -size_t ZSTD_buildBlockEntropyStats(seqStore_t* seqStorePtr, - const ZSTD_entropyCTables_t* prevEntropy, - ZSTD_entropyCTables_t* nextEntropy, - const ZSTD_CCtx_params* cctxParams, - ZSTD_entropyCTablesMetadata_t* entropyMetadata, - void* workspace, size_t wkspSize); +size_t ZSTD_buildBlockEntropyStats( + const seqStore_t* seqStorePtr, + const ZSTD_entropyCTables_t* prevEntropy, + ZSTD_entropyCTables_t* nextEntropy, + const ZSTD_CCtx_params* cctxParams, + ZSTD_entropyCTablesMetadata_t* entropyMetadata, + void* workspace, size_t wkspSize); /********************************* * Compression internals structs * @@ -148,6 +150,12 @@ typedef struct { size_t capacity; /* The capacity starting from `seq` pointer */ } rawSeqStore_t; +typedef struct { + U32 idx; /* Index in array of ZSTD_Sequence */ + U32 posInSequence; /* Position within sequence at idx */ + size_t posInSrc; /* Number of bytes given by sequences provided so far */ +} ZSTD_sequencePosition; + UNUSED_ATTR static const rawSeqStore_t kNullRawSeqStore = {NULL, 0, 0, 0, 0}; typedef struct { @@ -218,8 +226,10 @@ struct ZSTD_matchState_t { U32 hashLog3; /* dispatch table for matches of len==3 : larger == faster, more memory */ U32 rowHashLog; /* For row-based matchfinder: Hashlog based on nb of rows in the hashTable.*/ - U16* tagTable; /* For row-based matchFinder: A row-based table containing the hashes and head index. */ + BYTE* tagTable; /* For row-based matchFinder: A row-based table containing the hashes and head index. */ U32 hashCache[ZSTD_ROW_HASH_CACHE_SIZE]; /* For row-based matchFinder: a cache of hashes to improve speed */ + U64 hashSalt; /* For row-based matchFinder: salts the hash for re-use of tag table */ + U32 hashSaltEntropy; /* For row-based matchFinder: collects entropy for salt generation */ U32* hashTable; U32* hashTable3; @@ -234,6 +244,18 @@ struct ZSTD_matchState_t { const ZSTD_matchState_t* dictMatchState; ZSTD_compressionParameters cParams; const rawSeqStore_t* ldmSeqStore; + + /* Controls prefetching in some dictMatchState matchfinders. + * This behavior is controlled from the cctx ms. + * This parameter has no effect in the cdict ms. */ + int prefetchCDictTables; + + /* When == 0, lazy match finders insert every position. + * When != 0, lazy match finders only insert positions they search. + * This allows them to skip much faster over incompressible data, + * at a small cost to compression ratio. + */ + int lazySkipping; }; typedef struct { @@ -330,6 +352,24 @@ struct ZSTD_CCtx_params_s { /* Internal use, for createCCtxParams() and freeCCtxParams() only */ ZSTD_customMem customMem; + + /* Controls prefetching in some dictMatchState matchfinders */ + ZSTD_paramSwitch_e prefetchCDictTables; + + /* Controls whether zstd will fall back to an internal matchfinder + * if the external matchfinder returns an error code. */ + int enableMatchFinderFallback; + + /* Indicates whether an external matchfinder has been referenced. + * Users can't set this externally. + * It is set internally in ZSTD_registerSequenceProducer(). */ + int useSequenceProducer; + + /* Adjust the max block size*/ + size_t maxBlockSize; + + /* Controls repcode search in external sequence parsing */ + ZSTD_paramSwitch_e searchForExternalRepcodes; }; /* typedef'd to ZSTD_CCtx_params within "zstd.h" */ #define COMPRESS_SEQUENCES_WORKSPACE_SIZE (sizeof(unsigned) * (MaxSeq + 2)) @@ -361,6 +401,14 @@ typedef struct { ZSTD_entropyCTablesMetadata_t entropyMetadata; } ZSTD_blockSplitCtx; +/* Context for block-level external matchfinder API */ +typedef struct { + void* mState; + ZSTD_sequenceProducer_F* mFinder; + ZSTD_Sequence* seqBuffer; + size_t seqBufferCapacity; +} ZSTD_externalMatchCtx; + struct ZSTD_CCtx_s { ZSTD_compressionStage_e stage; int cParamsChanged; /* == 1 if cParams(except wlog) or compression level are changed in requestedParams. Triggers transmission of new params to ZSTDMT (if available) then reset to 0. */ @@ -410,6 +458,7 @@ struct ZSTD_CCtx_s { /* Stable in/out buffer verification */ ZSTD_inBuffer expectedInBuffer; + size_t stableIn_notConsumed; /* nb bytes within stable input buffer that are said to be consumed but are not */ size_t expectedOutBufferSize; /* Dictionary */ @@ -429,9 +478,13 @@ struct ZSTD_CCtx_s { /* Workspace for block splitter */ ZSTD_blockSplitCtx blockSplitCtx; + + /* Workspace for external matchfinder */ + ZSTD_externalMatchCtx externalMatchCtx; }; typedef enum { ZSTD_dtlm_fast, ZSTD_dtlm_full } ZSTD_dictTableLoadMethod_e; +typedef enum { ZSTD_tfp_forCCtx, ZSTD_tfp_forCDict } ZSTD_tableFillPurpose_e; typedef enum { ZSTD_noDict = 0, @@ -453,7 +506,7 @@ typedef enum { * In this mode we take both the source size and the dictionary size * into account when selecting and adjusting the parameters. */ - ZSTD_cpm_unknown = 3, /* ZSTD_getCParams, ZSTD_getParams, ZSTD_adjustParams. + ZSTD_cpm_unknown = 3 /* ZSTD_getCParams, ZSTD_getParams, ZSTD_adjustParams. * We don't know what these parameters are for. We default to the legacy * behavior of taking both the source size and the dict size into account * when selecting and adjusting parameters. @@ -512,9 +565,11 @@ MEM_STATIC int ZSTD_cParam_withinBounds(ZSTD_cParameter cParam, int value) /* ZSTD_noCompressBlock() : * Writes uncompressed block to dst buffer from given src. * Returns the size of the block */ -MEM_STATIC size_t ZSTD_noCompressBlock (void* dst, size_t dstCapacity, const void* src, size_t srcSize, U32 lastBlock) +MEM_STATIC size_t +ZSTD_noCompressBlock(void* dst, size_t dstCapacity, const void* src, size_t srcSize, U32 lastBlock) { U32 const cBlockHeader24 = lastBlock + (((U32)bt_raw)<<1) + (U32)(srcSize << 3); + DEBUGLOG(5, "ZSTD_noCompressBlock (srcSize=%zu, dstCapacity=%zu)", srcSize, dstCapacity); RETURN_ERROR_IF(srcSize + ZSTD_blockHeaderSize > dstCapacity, dstSize_tooSmall, "dst buf too small for uncompressed block"); MEM_writeLE24(dst, cBlockHeader24); @@ -522,7 +577,8 @@ MEM_STATIC size_t ZSTD_noCompressBlock (void* dst, size_t dstCapacity, const voi return ZSTD_blockHeaderSize + srcSize; } -MEM_STATIC size_t ZSTD_rleCompressBlock (void* dst, size_t dstCapacity, BYTE src, size_t srcSize, U32 lastBlock) +MEM_STATIC size_t +ZSTD_rleCompressBlock(void* dst, size_t dstCapacity, BYTE src, size_t srcSize, U32 lastBlock) { BYTE* const op = (BYTE*)dst; U32 const cBlockHeader = lastBlock + (((U32)bt_rle)<<1) + (U32)(srcSize << 3); @@ -541,7 +597,7 @@ MEM_STATIC size_t ZSTD_minGain(size_t srcSize, ZSTD_strategy strat) { U32 const minlog = (strat>=ZSTD_btultra) ? (U32)(strat) - 1 : 6; ZSTD_STATIC_ASSERT(ZSTD_btultra == 8); - assert(ZSTD_cParam_withinBounds(ZSTD_c_strategy, strat)); + assert(ZSTD_cParam_withinBounds(ZSTD_c_strategy, (int)strat)); return (srcSize >> minlog) + 2; } @@ -577,29 +633,27 @@ ZSTD_safecopyLiterals(BYTE* op, BYTE const* ip, BYTE const* const iend, BYTE con while (ip < iend) *op++ = *ip++; } -#define ZSTD_REP_MOVE (ZSTD_REP_NUM-1) -#define STORE_REPCODE_1 STORE_REPCODE(1) -#define STORE_REPCODE_2 STORE_REPCODE(2) -#define STORE_REPCODE_3 STORE_REPCODE(3) -#define STORE_REPCODE(r) (assert((r)>=1), assert((r)<=3), (r)-1) -#define STORE_OFFSET(o) (assert((o)>0), o + ZSTD_REP_MOVE) -#define STORED_IS_OFFSET(o) ((o) > ZSTD_REP_MOVE) -#define STORED_IS_REPCODE(o) ((o) <= ZSTD_REP_MOVE) -#define STORED_OFFSET(o) (assert(STORED_IS_OFFSET(o)), (o)-ZSTD_REP_MOVE) -#define STORED_REPCODE(o) (assert(STORED_IS_REPCODE(o)), (o)+1) /* returns ID 1,2,3 */ -#define STORED_TO_OFFBASE(o) ((o)+1) -#define OFFBASE_TO_STORED(o) ((o)-1) + +#define REPCODE1_TO_OFFBASE REPCODE_TO_OFFBASE(1) +#define REPCODE2_TO_OFFBASE REPCODE_TO_OFFBASE(2) +#define REPCODE3_TO_OFFBASE REPCODE_TO_OFFBASE(3) +#define REPCODE_TO_OFFBASE(r) (assert((r)>=1), assert((r)<=ZSTD_REP_NUM), (r)) /* accepts IDs 1,2,3 */ +#define OFFSET_TO_OFFBASE(o) (assert((o)>0), o + ZSTD_REP_NUM) +#define OFFBASE_IS_OFFSET(o) ((o) > ZSTD_REP_NUM) +#define OFFBASE_IS_REPCODE(o) ( 1 <= (o) && (o) <= ZSTD_REP_NUM) +#define OFFBASE_TO_OFFSET(o) (assert(OFFBASE_IS_OFFSET(o)), (o) - ZSTD_REP_NUM) +#define OFFBASE_TO_REPCODE(o) (assert(OFFBASE_IS_REPCODE(o)), (o)) /* returns ID 1,2,3 */ /*! ZSTD_storeSeq() : - * Store a sequence (litlen, litPtr, offCode and matchLength) into seqStore_t. - * @offBase_minus1 : Users should use employ macros STORE_REPCODE_X and STORE_OFFSET(). + * Store a sequence (litlen, litPtr, offBase and matchLength) into seqStore_t. + * @offBase : Users should employ macros REPCODE_TO_OFFBASE() and OFFSET_TO_OFFBASE(). * @matchLength : must be >= MINMATCH - * Allowed to overread literals up to litLimit. + * Allowed to over-read literals up to litLimit. */ HINT_INLINE UNUSED_ATTR void ZSTD_storeSeq(seqStore_t* seqStorePtr, size_t litLength, const BYTE* literals, const BYTE* litLimit, - U32 offBase_minus1, + U32 offBase, size_t matchLength) { BYTE const* const litLimit_w = litLimit - WILDCOPY_OVERLENGTH; @@ -608,8 +662,8 @@ ZSTD_storeSeq(seqStore_t* seqStorePtr, static const BYTE* g_start = NULL; if (g_start==NULL) g_start = (const BYTE*)literals; /* note : index only works for compression within a single segment */ { U32 const pos = (U32)((const BYTE*)literals - g_start); - DEBUGLOG(6, "Cpos%7u :%3u literals, match%4u bytes at offCode%7u", - pos, (U32)litLength, (U32)matchLength, (U32)offBase_minus1); + DEBUGLOG(6, "Cpos%7u :%3u literals, match%4u bytes at offBase%7u", + pos, (U32)litLength, (U32)matchLength, (U32)offBase); } #endif assert((size_t)(seqStorePtr->sequences - seqStorePtr->sequencesStart) < seqStorePtr->maxNbSeq); @@ -619,9 +673,9 @@ ZSTD_storeSeq(seqStore_t* seqStorePtr, assert(literals + litLength <= litLimit); if (litEnd <= litLimit_w) { /* Common case we can use wildcopy. - * First copy 16 bytes, because literals are likely short. - */ - assert(WILDCOPY_OVERLENGTH >= 16); + * First copy 16 bytes, because literals are likely short. + */ + ZSTD_STATIC_ASSERT(WILDCOPY_OVERLENGTH >= 16); ZSTD_copy16(seqStorePtr->lit, literals); if (litLength > 16) { ZSTD_wildcopy(seqStorePtr->lit+16, literals+16, (ptrdiff_t)litLength-16, ZSTD_no_overlap); @@ -640,7 +694,7 @@ ZSTD_storeSeq(seqStore_t* seqStorePtr, seqStorePtr->sequences[0].litLength = (U16)litLength; /* match offset */ - seqStorePtr->sequences[0].offBase = STORED_TO_OFFBASE(offBase_minus1); + seqStorePtr->sequences[0].offBase = offBase; /* match Length */ assert(matchLength >= MINMATCH); @@ -658,17 +712,17 @@ ZSTD_storeSeq(seqStore_t* seqStorePtr, /* ZSTD_updateRep() : * updates in-place @rep (array of repeat offsets) - * @offBase_minus1 : sum-type, with same numeric representation as ZSTD_storeSeq() + * @offBase : sum-type, using numeric representation of ZSTD_storeSeq() */ MEM_STATIC void -ZSTD_updateRep(U32 rep[ZSTD_REP_NUM], U32 const offBase_minus1, U32 const ll0) +ZSTD_updateRep(U32 rep[ZSTD_REP_NUM], U32 const offBase, U32 const ll0) { - if (STORED_IS_OFFSET(offBase_minus1)) { /* full offset */ + if (OFFBASE_IS_OFFSET(offBase)) { /* full offset */ rep[2] = rep[1]; rep[1] = rep[0]; - rep[0] = STORED_OFFSET(offBase_minus1); + rep[0] = OFFBASE_TO_OFFSET(offBase); } else { /* repcode */ - U32 const repCode = STORED_REPCODE(offBase_minus1) - 1 + ll0; + U32 const repCode = OFFBASE_TO_REPCODE(offBase) - 1 + ll0; if (repCode > 0) { /* note : if repCode==0, no change */ U32 const currentOffset = (repCode==ZSTD_REP_NUM) ? (rep[0] - 1) : rep[repCode]; rep[2] = (repCode >= 2) ? rep[1] : rep[2]; @@ -685,11 +739,11 @@ typedef struct repcodes_s { } repcodes_t; MEM_STATIC repcodes_t -ZSTD_newRep(U32 const rep[ZSTD_REP_NUM], U32 const offBase_minus1, U32 const ll0) +ZSTD_newRep(U32 const rep[ZSTD_REP_NUM], U32 const offBase, U32 const ll0) { repcodes_t newReps; ZSTD_memcpy(&newReps, rep, sizeof(newReps)); - ZSTD_updateRep(newReps.rep, offBase_minus1, ll0); + ZSTD_updateRep(newReps.rep, offBase, ll0); return newReps; } @@ -697,103 +751,6 @@ ZSTD_newRep(U32 const rep[ZSTD_REP_NUM], U32 const offBase_minus1, U32 const ll0 /*-************************************* * Match length counter ***************************************/ -static unsigned ZSTD_NbCommonBytes (size_t val) -{ - if (MEM_isLittleEndian()) { - if (MEM_64bits()) { -# if defined(_MSC_VER) && defined(_WIN64) -# if STATIC_BMI2 - return _tzcnt_u64(val) >> 3; -# else - if (val != 0) { - unsigned long r; - _BitScanForward64(&r, (U64)val); - return (unsigned)(r >> 3); - } else { - /* Should not reach this code path */ - __assume(0); - } -# endif -# elif defined(__GNUC__) && (__GNUC__ >= 4) - return (__builtin_ctzll((U64)val) >> 3); -# else - static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, - 0, 3, 1, 3, 1, 4, 2, 7, - 0, 2, 3, 6, 1, 5, 3, 5, - 1, 3, 4, 4, 2, 5, 6, 7, - 7, 0, 1, 2, 3, 3, 4, 6, - 2, 6, 5, 5, 3, 4, 5, 6, - 7, 1, 2, 4, 6, 4, 4, 5, - 7, 2, 6, 5, 7, 6, 7, 7 }; - return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58]; -# endif - } else { /* 32 bits */ -# if defined(_MSC_VER) - if (val != 0) { - unsigned long r; - _BitScanForward(&r, (U32)val); - return (unsigned)(r >> 3); - } else { - /* Should not reach this code path */ - __assume(0); - } -# elif defined(__GNUC__) && (__GNUC__ >= 3) - return (__builtin_ctz((U32)val) >> 3); -# else - static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, - 3, 2, 2, 1, 3, 2, 0, 1, - 3, 3, 1, 2, 2, 2, 2, 0, - 3, 1, 2, 0, 1, 0, 1, 1 }; - return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27]; -# endif - } - } else { /* Big Endian CPU */ - if (MEM_64bits()) { -# if defined(_MSC_VER) && defined(_WIN64) -# if STATIC_BMI2 - return _lzcnt_u64(val) >> 3; -# else - if (val != 0) { - unsigned long r; - _BitScanReverse64(&r, (U64)val); - return (unsigned)(r >> 3); - } else { - /* Should not reach this code path */ - __assume(0); - } -# endif -# elif defined(__GNUC__) && (__GNUC__ >= 4) - return (__builtin_clzll(val) >> 3); -# else - unsigned r; - const unsigned n32 = sizeof(size_t)*4; /* calculate this way due to compiler complaining in 32-bits mode */ - if (!(val>>n32)) { r=4; } else { r=0; val>>=n32; } - if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; } - r += (!val); - return r; -# endif - } else { /* 32 bits */ -# if defined(_MSC_VER) - if (val != 0) { - unsigned long r; - _BitScanReverse(&r, (unsigned long)val); - return (unsigned)(r >> 3); - } else { - /* Should not reach this code path */ - __assume(0); - } -# elif defined(__GNUC__) && (__GNUC__ >= 3) - return (__builtin_clz((U32)val) >> 3); -# else - unsigned r; - if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; } - r += (!val); - return r; -# endif - } } -} - - MEM_STATIC size_t ZSTD_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* const pInLimit) { const BYTE* const pStart = pIn; @@ -839,32 +796,43 @@ ZSTD_count_2segments(const BYTE* ip, const BYTE* match, * Hashes ***************************************/ static const U32 prime3bytes = 506832829U; -static U32 ZSTD_hash3(U32 u, U32 h) { return ((u << (32-24)) * prime3bytes) >> (32-h) ; } -MEM_STATIC size_t ZSTD_hash3Ptr(const void* ptr, U32 h) { return ZSTD_hash3(MEM_readLE32(ptr), h); } /* only in zstd_opt.h */ +static U32 ZSTD_hash3(U32 u, U32 h, U32 s) { assert(h <= 32); return (((u << (32-24)) * prime3bytes) ^ s) >> (32-h) ; } +MEM_STATIC size_t ZSTD_hash3Ptr(const void* ptr, U32 h) { return ZSTD_hash3(MEM_readLE32(ptr), h, 0); } /* only in zstd_opt.h */ +MEM_STATIC size_t ZSTD_hash3PtrS(const void* ptr, U32 h, U32 s) { return ZSTD_hash3(MEM_readLE32(ptr), h, s); } static const U32 prime4bytes = 2654435761U; -static U32 ZSTD_hash4(U32 u, U32 h) { return (u * prime4bytes) >> (32-h) ; } -static size_t ZSTD_hash4Ptr(const void* ptr, U32 h) { return ZSTD_hash4(MEM_read32(ptr), h); } +static U32 ZSTD_hash4(U32 u, U32 h, U32 s) { assert(h <= 32); return ((u * prime4bytes) ^ s) >> (32-h) ; } +static size_t ZSTD_hash4Ptr(const void* ptr, U32 h) { return ZSTD_hash4(MEM_readLE32(ptr), h, 0); } +static size_t ZSTD_hash4PtrS(const void* ptr, U32 h, U32 s) { return ZSTD_hash4(MEM_readLE32(ptr), h, s); } static const U64 prime5bytes = 889523592379ULL; -static size_t ZSTD_hash5(U64 u, U32 h) { return (size_t)(((u << (64-40)) * prime5bytes) >> (64-h)) ; } -static size_t ZSTD_hash5Ptr(const void* p, U32 h) { return ZSTD_hash5(MEM_readLE64(p), h); } +static size_t ZSTD_hash5(U64 u, U32 h, U64 s) { assert(h <= 64); return (size_t)((((u << (64-40)) * prime5bytes) ^ s) >> (64-h)) ; } +static size_t ZSTD_hash5Ptr(const void* p, U32 h) { return ZSTD_hash5(MEM_readLE64(p), h, 0); } +static size_t ZSTD_hash5PtrS(const void* p, U32 h, U64 s) { return ZSTD_hash5(MEM_readLE64(p), h, s); } static const U64 prime6bytes = 227718039650203ULL; -static size_t ZSTD_hash6(U64 u, U32 h) { return (size_t)(((u << (64-48)) * prime6bytes) >> (64-h)) ; } -static size_t ZSTD_hash6Ptr(const void* p, U32 h) { return ZSTD_hash6(MEM_readLE64(p), h); } +static size_t ZSTD_hash6(U64 u, U32 h, U64 s) { assert(h <= 64); return (size_t)((((u << (64-48)) * prime6bytes) ^ s) >> (64-h)) ; } +static size_t ZSTD_hash6Ptr(const void* p, U32 h) { return ZSTD_hash6(MEM_readLE64(p), h, 0); } +static size_t ZSTD_hash6PtrS(const void* p, U32 h, U64 s) { return ZSTD_hash6(MEM_readLE64(p), h, s); } static const U64 prime7bytes = 58295818150454627ULL; -static size_t ZSTD_hash7(U64 u, U32 h) { return (size_t)(((u << (64-56)) * prime7bytes) >> (64-h)) ; } -static size_t ZSTD_hash7Ptr(const void* p, U32 h) { return ZSTD_hash7(MEM_readLE64(p), h); } +static size_t ZSTD_hash7(U64 u, U32 h, U64 s) { assert(h <= 64); return (size_t)((((u << (64-56)) * prime7bytes) ^ s) >> (64-h)) ; } +static size_t ZSTD_hash7Ptr(const void* p, U32 h) { return ZSTD_hash7(MEM_readLE64(p), h, 0); } +static size_t ZSTD_hash7PtrS(const void* p, U32 h, U64 s) { return ZSTD_hash7(MEM_readLE64(p), h, s); } static const U64 prime8bytes = 0xCF1BBCDCB7A56463ULL; -static size_t ZSTD_hash8(U64 u, U32 h) { return (size_t)(((u) * prime8bytes) >> (64-h)) ; } -static size_t ZSTD_hash8Ptr(const void* p, U32 h) { return ZSTD_hash8(MEM_readLE64(p), h); } +static size_t ZSTD_hash8(U64 u, U32 h, U64 s) { assert(h <= 64); return (size_t)((((u) * prime8bytes) ^ s) >> (64-h)) ; } +static size_t ZSTD_hash8Ptr(const void* p, U32 h) { return ZSTD_hash8(MEM_readLE64(p), h, 0); } +static size_t ZSTD_hash8PtrS(const void* p, U32 h, U64 s) { return ZSTD_hash8(MEM_readLE64(p), h, s); } + MEM_STATIC FORCE_INLINE_ATTR size_t ZSTD_hashPtr(const void* p, U32 hBits, U32 mls) { + /* Although some of these hashes do support hBits up to 64, some do not. + * To be on the safe side, always avoid hBits > 32. */ + assert(hBits <= 32); + switch(mls) { default: @@ -876,6 +844,24 @@ size_t ZSTD_hashPtr(const void* p, U32 hBits, U32 mls) } } +MEM_STATIC FORCE_INLINE_ATTR +size_t ZSTD_hashPtrSalted(const void* p, U32 hBits, U32 mls, const U64 hashSalt) { + /* Although some of these hashes do support hBits up to 64, some do not. + * To be on the safe side, always avoid hBits > 32. */ + assert(hBits <= 32); + + switch(mls) + { + default: + case 4: return ZSTD_hash4PtrS(p, hBits, (U32)hashSalt); + case 5: return ZSTD_hash5PtrS(p, hBits, hashSalt); + case 6: return ZSTD_hash6PtrS(p, hBits, hashSalt); + case 7: return ZSTD_hash7PtrS(p, hBits, hashSalt); + case 8: return ZSTD_hash8PtrS(p, hBits, hashSalt); + } +} + + /** ZSTD_ipow() : * Return base^exponent. */ @@ -1223,10 +1209,15 @@ ZSTD_checkDictValidity(const ZSTD_window_t* window, (unsigned)blockEndIdx, (unsigned)maxDist, (unsigned)loadedDictEnd); assert(blockEndIdx >= loadedDictEnd); - if (blockEndIdx > loadedDictEnd + maxDist) { + if (blockEndIdx > loadedDictEnd + maxDist || loadedDictEnd != window->dictLimit) { /* On reaching window size, dictionaries are invalidated. * For simplification, if window size is reached anywhere within next block, * the dictionary is invalidated for the full block. + * + * We also have to invalidate the dictionary if ZSTD_window_update() has detected + * non-contiguous segments, which means that loadedDictEnd != window->dictLimit. + * loadedDictEnd may be 0, if forceWindow is true, but in that case we never use + * dictMatchState, so setting it to NULL is not a problem. */ DEBUGLOG(6, "invalidating dictionary for current block (distance > windowSize)"); *loadedDictEndPtr = 0; @@ -1358,6 +1349,42 @@ MEM_STATIC void ZSTD_debugTable(const U32* table, U32 max) #endif +/* Short Cache */ + +/* Normally, zstd matchfinders follow this flow: + * 1. Compute hash at ip + * 2. Load index from hashTable[hash] + * 3. Check if *ip == *(base + index) + * In dictionary compression, loading *(base + index) is often an L2 or even L3 miss. + * + * Short cache is an optimization which allows us to avoid step 3 most of the time + * when the data doesn't actually match. With short cache, the flow becomes: + * 1. Compute (hash, currentTag) at ip. currentTag is an 8-bit independent hash at ip. + * 2. Load (index, matchTag) from hashTable[hash]. See ZSTD_writeTaggedIndex to understand how this works. + * 3. Only if currentTag == matchTag, check *ip == *(base + index). Otherwise, continue. + * + * Currently, short cache is only implemented in CDict hashtables. Thus, its use is limited to + * dictMatchState matchfinders. + */ +#define ZSTD_SHORT_CACHE_TAG_BITS 8 +#define ZSTD_SHORT_CACHE_TAG_MASK ((1u << ZSTD_SHORT_CACHE_TAG_BITS) - 1) + +/* Helper function for ZSTD_fillHashTable and ZSTD_fillDoubleHashTable. + * Unpacks hashAndTag into (hash, tag), then packs (index, tag) into hashTable[hash]. */ +MEM_STATIC void ZSTD_writeTaggedIndex(U32* const hashTable, size_t hashAndTag, U32 index) { + size_t const hash = hashAndTag >> ZSTD_SHORT_CACHE_TAG_BITS; + U32 const tag = (U32)(hashAndTag & ZSTD_SHORT_CACHE_TAG_MASK); + assert(index >> (32 - ZSTD_SHORT_CACHE_TAG_BITS) == 0); + hashTable[hash] = (index << ZSTD_SHORT_CACHE_TAG_BITS) | tag; +} + +/* Helper function for short cache matchfinders. + * Unpacks tag1 and tag2 from lower bits of packedTag1 and packedTag2, then checks if the tags match. */ +MEM_STATIC int ZSTD_comparePackedTags(size_t packedTag1, size_t packedTag2) { + U32 const tag1 = packedTag1 & ZSTD_SHORT_CACHE_TAG_MASK; + U32 const tag2 = packedTag2 & ZSTD_SHORT_CACHE_TAG_MASK; + return tag1 == tag2; +} #if defined (__cplusplus) } @@ -1455,4 +1482,51 @@ U32 ZSTD_cycleLog(U32 hashLog, ZSTD_strategy strat); */ void ZSTD_CCtx_trace(ZSTD_CCtx* cctx, size_t extraCSize); +/* Returns 0 on success, and a ZSTD_error otherwise. This function scans through an array of + * ZSTD_Sequence, storing the sequences it finds, until it reaches a block delimiter. + * Note that the block delimiter must include the last literals of the block. + */ +size_t +ZSTD_copySequencesToSeqStoreExplicitBlockDelim(ZSTD_CCtx* cctx, + ZSTD_sequencePosition* seqPos, + const ZSTD_Sequence* const inSeqs, size_t inSeqsSize, + const void* src, size_t blockSize, ZSTD_paramSwitch_e externalRepSearch); + +/* Returns the number of bytes to move the current read position back by. + * Only non-zero if we ended up splitting a sequence. + * Otherwise, it may return a ZSTD error if something went wrong. + * + * This function will attempt to scan through blockSize bytes + * represented by the sequences in @inSeqs, + * storing any (partial) sequences. + * + * Occasionally, we may want to change the actual number of bytes we consumed from inSeqs to + * avoid splitting a match, or to avoid splitting a match such that it would produce a match + * smaller than MINMATCH. In this case, we return the number of bytes that we didn't read from this block. + */ +size_t +ZSTD_copySequencesToSeqStoreNoBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition* seqPos, + const ZSTD_Sequence* const inSeqs, size_t inSeqsSize, + const void* src, size_t blockSize, ZSTD_paramSwitch_e externalRepSearch); + + +/* =============================================================== + * Deprecated definitions that are still used internally to avoid + * deprecation warnings. These functions are exactly equivalent to + * their public variants, but avoid the deprecation warnings. + * =============================================================== */ + +size_t ZSTD_compressBegin_usingCDict_deprecated(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); + +size_t ZSTD_compressContinue_public(ZSTD_CCtx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize); + +size_t ZSTD_compressEnd_public(ZSTD_CCtx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize); + +size_t ZSTD_compressBlock_deprecated(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); + + #endif /* ZSTD_COMPRESS_H */ diff --git a/internal-complibs/zstd-1.5.5/compress/zstd_compress_literals.c b/internal-complibs/zstd-1.5.5/compress/zstd_compress_literals.c new file mode 100644 index 00000000..bfd4f11a --- /dev/null +++ b/internal-complibs/zstd-1.5.5/compress/zstd_compress_literals.c @@ -0,0 +1,235 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under both the BSD-style license (found in the + * LICENSE file in the root directory of this source tree) and the GPLv2 (found + * in the COPYING file in the root directory of this source tree). + * You may select, at your option, one of the above-listed licenses. + */ + + /*-************************************* + * Dependencies + ***************************************/ +#include "zstd_compress_literals.h" + + +/* ************************************************************** +* Debug Traces +****************************************************************/ +#if DEBUGLEVEL >= 2 + +static size_t showHexa(const void* src, size_t srcSize) +{ + const BYTE* const ip = (const BYTE*)src; + size_t u; + for (u=0; u31) + (srcSize>4095); + + DEBUGLOG(5, "ZSTD_noCompressLiterals: srcSize=%zu, dstCapacity=%zu", srcSize, dstCapacity); + + RETURN_ERROR_IF(srcSize + flSize > dstCapacity, dstSize_tooSmall, ""); + + switch(flSize) + { + case 1: /* 2 - 1 - 5 */ + ostart[0] = (BYTE)((U32)set_basic + (srcSize<<3)); + break; + case 2: /* 2 - 2 - 12 */ + MEM_writeLE16(ostart, (U16)((U32)set_basic + (1<<2) + (srcSize<<4))); + break; + case 3: /* 2 - 2 - 20 */ + MEM_writeLE32(ostart, (U32)((U32)set_basic + (3<<2) + (srcSize<<4))); + break; + default: /* not necessary : flSize is {1,2,3} */ + assert(0); + } + + ZSTD_memcpy(ostart + flSize, src, srcSize); + DEBUGLOG(5, "Raw (uncompressed) literals: %u -> %u", (U32)srcSize, (U32)(srcSize + flSize)); + return srcSize + flSize; +} + +static int allBytesIdentical(const void* src, size_t srcSize) +{ + assert(srcSize >= 1); + assert(src != NULL); + { const BYTE b = ((const BYTE*)src)[0]; + size_t p; + for (p=1; p31) + (srcSize>4095); + + assert(dstCapacity >= 4); (void)dstCapacity; + assert(allBytesIdentical(src, srcSize)); + + switch(flSize) + { + case 1: /* 2 - 1 - 5 */ + ostart[0] = (BYTE)((U32)set_rle + (srcSize<<3)); + break; + case 2: /* 2 - 2 - 12 */ + MEM_writeLE16(ostart, (U16)((U32)set_rle + (1<<2) + (srcSize<<4))); + break; + case 3: /* 2 - 2 - 20 */ + MEM_writeLE32(ostart, (U32)((U32)set_rle + (3<<2) + (srcSize<<4))); + break; + default: /* not necessary : flSize is {1,2,3} */ + assert(0); + } + + ostart[flSize] = *(const BYTE*)src; + DEBUGLOG(5, "RLE : Repeated Literal (%02X: %u times) -> %u bytes encoded", ((const BYTE*)src)[0], (U32)srcSize, (U32)flSize + 1); + return flSize+1; +} + +/* ZSTD_minLiteralsToCompress() : + * returns minimal amount of literals + * for literal compression to even be attempted. + * Minimum is made tighter as compression strategy increases. + */ +static size_t +ZSTD_minLiteralsToCompress(ZSTD_strategy strategy, HUF_repeat huf_repeat) +{ + assert((int)strategy >= 0); + assert((int)strategy <= 9); + /* btultra2 : min 8 bytes; + * then 2x larger for each successive compression strategy + * max threshold 64 bytes */ + { int const shift = MIN(9-(int)strategy, 3); + size_t const mintc = (huf_repeat == HUF_repeat_valid) ? 6 : (size_t)8 << shift; + DEBUGLOG(7, "minLiteralsToCompress = %zu", mintc); + return mintc; + } +} + +size_t ZSTD_compressLiterals ( + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, + void* entropyWorkspace, size_t entropyWorkspaceSize, + const ZSTD_hufCTables_t* prevHuf, + ZSTD_hufCTables_t* nextHuf, + ZSTD_strategy strategy, + int disableLiteralCompression, + int suspectUncompressible, + int bmi2) +{ + size_t const lhSize = 3 + (srcSize >= 1 KB) + (srcSize >= 16 KB); + BYTE* const ostart = (BYTE*)dst; + U32 singleStream = srcSize < 256; + symbolEncodingType_e hType = set_compressed; + size_t cLitSize; + + DEBUGLOG(5,"ZSTD_compressLiterals (disableLiteralCompression=%i, srcSize=%u, dstCapacity=%zu)", + disableLiteralCompression, (U32)srcSize, dstCapacity); + + DEBUGLOG(6, "Completed literals listing (%zu bytes)", showHexa(src, srcSize)); + + /* Prepare nextEntropy assuming reusing the existing table */ + ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); + + if (disableLiteralCompression) + return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); + + /* if too small, don't even attempt compression (speed opt) */ + if (srcSize < ZSTD_minLiteralsToCompress(strategy, prevHuf->repeatMode)) + return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); + + RETURN_ERROR_IF(dstCapacity < lhSize+1, dstSize_tooSmall, "not enough space for compression"); + { HUF_repeat repeat = prevHuf->repeatMode; + int const flags = 0 + | (bmi2 ? HUF_flags_bmi2 : 0) + | (strategy < ZSTD_lazy && srcSize <= 1024 ? HUF_flags_preferRepeat : 0) + | (strategy >= HUF_OPTIMAL_DEPTH_THRESHOLD ? HUF_flags_optimalDepth : 0) + | (suspectUncompressible ? HUF_flags_suspectUncompressible : 0); + + typedef size_t (*huf_compress_f)(void*, size_t, const void*, size_t, unsigned, unsigned, void*, size_t, HUF_CElt*, HUF_repeat*, int); + huf_compress_f huf_compress; + if (repeat == HUF_repeat_valid && lhSize == 3) singleStream = 1; + huf_compress = singleStream ? HUF_compress1X_repeat : HUF_compress4X_repeat; + cLitSize = huf_compress(ostart+lhSize, dstCapacity-lhSize, + src, srcSize, + HUF_SYMBOLVALUE_MAX, LitHufLog, + entropyWorkspace, entropyWorkspaceSize, + (HUF_CElt*)nextHuf->CTable, + &repeat, flags); + DEBUGLOG(5, "%zu literals compressed into %zu bytes (before header)", srcSize, cLitSize); + if (repeat != HUF_repeat_none) { + /* reused the existing table */ + DEBUGLOG(5, "reusing statistics from previous huffman block"); + hType = set_repeat; + } + } + + { size_t const minGain = ZSTD_minGain(srcSize, strategy); + if ((cLitSize==0) || (cLitSize >= srcSize - minGain) || ERR_isError(cLitSize)) { + ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); + return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); + } } + if (cLitSize==1) { + /* A return value of 1 signals that the alphabet consists of a single symbol. + * However, in some rare circumstances, it could be the compressed size (a single byte). + * For that outcome to have a chance to happen, it's necessary that `srcSize < 8`. + * (it's also necessary to not generate statistics). + * Therefore, in such a case, actively check that all bytes are identical. */ + if ((srcSize >= 8) || allBytesIdentical(src, srcSize)) { + ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); + return ZSTD_compressRleLiteralsBlock(dst, dstCapacity, src, srcSize); + } } + + if (hType == set_compressed) { + /* using a newly constructed table */ + nextHuf->repeatMode = HUF_repeat_check; + } + + /* Build header */ + switch(lhSize) + { + case 3: /* 2 - 2 - 10 - 10 */ + if (!singleStream) assert(srcSize >= MIN_LITERALS_FOR_4_STREAMS); + { U32 const lhc = hType + ((U32)(!singleStream) << 2) + ((U32)srcSize<<4) + ((U32)cLitSize<<14); + MEM_writeLE24(ostart, lhc); + break; + } + case 4: /* 2 - 2 - 14 - 14 */ + assert(srcSize >= MIN_LITERALS_FOR_4_STREAMS); + { U32 const lhc = hType + (2 << 2) + ((U32)srcSize<<4) + ((U32)cLitSize<<18); + MEM_writeLE32(ostart, lhc); + break; + } + case 5: /* 2 - 2 - 18 - 18 */ + assert(srcSize >= MIN_LITERALS_FOR_4_STREAMS); + { U32 const lhc = hType + (3 << 2) + ((U32)srcSize<<4) + ((U32)cLitSize<<22); + MEM_writeLE32(ostart, lhc); + ostart[4] = (BYTE)(cLitSize >> 10); + break; + } + default: /* not possible : lhSize is {3,4,5} */ + assert(0); + } + DEBUGLOG(5, "Compressed literals: %u -> %u", (U32)srcSize, (U32)(lhSize+cLitSize)); + return lhSize+cLitSize; +} diff --git a/internal-complibs/zstd-1.5.2/compress/zstd_compress_literals.h b/internal-complibs/zstd-1.5.5/compress/zstd_compress_literals.h similarity index 61% rename from internal-complibs/zstd-1.5.2/compress/zstd_compress_literals.h rename to internal-complibs/zstd-1.5.5/compress/zstd_compress_literals.h index 9775fb97..b060c8ad 100644 --- a/internal-complibs/zstd-1.5.2/compress/zstd_compress_literals.h +++ b/internal-complibs/zstd-1.5.5/compress/zstd_compress_literals.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -16,16 +16,24 @@ size_t ZSTD_noCompressLiterals (void* dst, size_t dstCapacity, const void* src, size_t srcSize); +/* ZSTD_compressRleLiteralsBlock() : + * Conditions : + * - All bytes in @src are identical + * - dstCapacity >= 4 */ size_t ZSTD_compressRleLiteralsBlock (void* dst, size_t dstCapacity, const void* src, size_t srcSize); -/* If suspectUncompressible then some sampling checks will be run to potentially skip huffman coding */ -size_t ZSTD_compressLiterals (ZSTD_hufCTables_t const* prevHuf, - ZSTD_hufCTables_t* nextHuf, - ZSTD_strategy strategy, int disableLiteralCompression, - void* dst, size_t dstCapacity, +/* ZSTD_compressLiterals(): + * @entropyWorkspace: must be aligned on 4-bytes boundaries + * @entropyWorkspaceSize : must be >= HUF_WORKSPACE_SIZE + * @suspectUncompressible: sampling checks, to potentially skip huffman coding + */ +size_t ZSTD_compressLiterals (void* dst, size_t dstCapacity, const void* src, size_t srcSize, void* entropyWorkspace, size_t entropyWorkspaceSize, - const int bmi2, - unsigned suspectUncompressible); + const ZSTD_hufCTables_t* prevHuf, + ZSTD_hufCTables_t* nextHuf, + ZSTD_strategy strategy, int disableLiteralCompression, + int suspectUncompressible, + int bmi2); #endif /* ZSTD_COMPRESS_LITERALS_H */ diff --git a/internal-complibs/zstd-1.5.2/compress/zstd_compress_sequences.c b/internal-complibs/zstd-1.5.5/compress/zstd_compress_sequences.c similarity index 99% rename from internal-complibs/zstd-1.5.2/compress/zstd_compress_sequences.c rename to internal-complibs/zstd-1.5.5/compress/zstd_compress_sequences.c index f1e40af2..8872d4d3 100644 --- a/internal-complibs/zstd-1.5.2/compress/zstd_compress_sequences.c +++ b/internal-complibs/zstd-1.5.5/compress/zstd_compress_sequences.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -58,7 +58,7 @@ static unsigned ZSTD_useLowProbCount(size_t const nbSeq) { /* Heuristic: This should cover most blocks <= 16K and * start to fade out after 16K to about 32K depending on - * comprssibility. + * compressibility. */ return nbSeq >= 2048; } @@ -166,7 +166,7 @@ ZSTD_selectEncodingType( if (mostFrequent == nbSeq) { *repeatMode = FSE_repeat_none; if (isDefaultAllowed && nbSeq <= 2) { - /* Prefer set_basic over set_rle when there are 2 or less symbols, + /* Prefer set_basic over set_rle when there are 2 or fewer symbols, * since RLE uses 1 byte, but set_basic uses 5-6 bits per symbol. * If basic encoding isn't possible, always choose RLE. */ diff --git a/internal-complibs/zstd-1.5.2/compress/zstd_compress_sequences.h b/internal-complibs/zstd-1.5.5/compress/zstd_compress_sequences.h similarity index 97% rename from internal-complibs/zstd-1.5.2/compress/zstd_compress_sequences.h rename to internal-complibs/zstd-1.5.5/compress/zstd_compress_sequences.h index 7991364c..4a3a05da 100644 --- a/internal-complibs/zstd-1.5.2/compress/zstd_compress_sequences.h +++ b/internal-complibs/zstd-1.5.5/compress/zstd_compress_sequences.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the diff --git a/internal-complibs/zstd-1.5.2/compress/zstd_compress_superblock.c b/internal-complibs/zstd-1.5.5/compress/zstd_compress_superblock.c similarity index 94% rename from internal-complibs/zstd-1.5.2/compress/zstd_compress_superblock.c rename to internal-complibs/zstd-1.5.5/compress/zstd_compress_superblock.c index 10e33785..638c4acb 100644 --- a/internal-complibs/zstd-1.5.2/compress/zstd_compress_superblock.c +++ b/internal-complibs/zstd-1.5.5/compress/zstd_compress_superblock.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -36,13 +36,14 @@ * If it is set_compressed, first sub-block's literals section will be Treeless_Literals_Block * and the following sub-blocks' literals sections will be Treeless_Literals_Block. * @return : compressed size of literals section of a sub-block - * Or 0 if it unable to compress. + * Or 0 if unable to compress. * Or error code */ -static size_t ZSTD_compressSubBlock_literal(const HUF_CElt* hufTable, - const ZSTD_hufCTablesMetadata_t* hufMetadata, - const BYTE* literals, size_t litSize, - void* dst, size_t dstSize, - const int bmi2, int writeEntropy, int* entropyWritten) +static size_t +ZSTD_compressSubBlock_literal(const HUF_CElt* hufTable, + const ZSTD_hufCTablesMetadata_t* hufMetadata, + const BYTE* literals, size_t litSize, + void* dst, size_t dstSize, + const int bmi2, int writeEntropy, int* entropyWritten) { size_t const header = writeEntropy ? 200 : 0; size_t const lhSize = 3 + (litSize >= (1 KB - header)) + (litSize >= (16 KB - header)); @@ -53,8 +54,6 @@ static size_t ZSTD_compressSubBlock_literal(const HUF_CElt* hufTable, symbolEncodingType_e hType = writeEntropy ? hufMetadata->hType : set_repeat; size_t cLitSize = 0; - (void)bmi2; /* TODO bmi2... */ - DEBUGLOG(5, "ZSTD_compressSubBlock_literal (litSize=%zu, lhSize=%zu, writeEntropy=%d)", litSize, lhSize, writeEntropy); *entropyWritten = 0; @@ -76,9 +75,9 @@ static size_t ZSTD_compressSubBlock_literal(const HUF_CElt* hufTable, DEBUGLOG(5, "ZSTD_compressSubBlock_literal (hSize=%zu)", hufMetadata->hufDesSize); } - /* TODO bmi2 */ - { const size_t cSize = singleStream ? HUF_compress1X_usingCTable(op, oend-op, literals, litSize, hufTable) - : HUF_compress4X_usingCTable(op, oend-op, literals, litSize, hufTable); + { int const flags = bmi2 ? HUF_flags_bmi2 : 0; + const size_t cSize = singleStream ? HUF_compress1X_usingCTable(op, oend-op, literals, litSize, hufTable, flags) + : HUF_compress4X_usingCTable(op, oend-op, literals, litSize, hufTable, flags); op += cSize; cLitSize += cSize; if (cSize == 0 || ERR_isError(cSize)) { @@ -126,7 +125,11 @@ static size_t ZSTD_compressSubBlock_literal(const HUF_CElt* hufTable, return op-ostart; } -static size_t ZSTD_seqDecompressedSize(seqStore_t const* seqStore, const seqDef* sequences, size_t nbSeq, size_t litSize, int lastSequence) { +static size_t +ZSTD_seqDecompressedSize(seqStore_t const* seqStore, + const seqDef* sequences, size_t nbSeq, + size_t litSize, int lastSequence) +{ const seqDef* const sstart = sequences; const seqDef* const send = sequences + nbSeq; const seqDef* sp = sstart; @@ -156,13 +159,14 @@ static size_t ZSTD_seqDecompressedSize(seqStore_t const* seqStore, const seqDef* * @return : compressed size of sequences section of a sub-block * Or 0 if it is unable to compress * Or error code. */ -static size_t ZSTD_compressSubBlock_sequences(const ZSTD_fseCTables_t* fseTables, - const ZSTD_fseCTablesMetadata_t* fseMetadata, - const seqDef* sequences, size_t nbSeq, - const BYTE* llCode, const BYTE* mlCode, const BYTE* ofCode, - const ZSTD_CCtx_params* cctxParams, - void* dst, size_t dstCapacity, - const int bmi2, int writeEntropy, int* entropyWritten) +static size_t +ZSTD_compressSubBlock_sequences(const ZSTD_fseCTables_t* fseTables, + const ZSTD_fseCTablesMetadata_t* fseMetadata, + const seqDef* sequences, size_t nbSeq, + const BYTE* llCode, const BYTE* mlCode, const BYTE* ofCode, + const ZSTD_CCtx_params* cctxParams, + void* dst, size_t dstCapacity, + const int bmi2, int writeEntropy, int* entropyWritten) { const int longOffsets = cctxParams->cParams.windowLog > STREAM_ACCUMULATOR_MIN; BYTE* const ostart = (BYTE*)dst; @@ -539,7 +543,7 @@ static size_t ZSTD_compressSubBlock_multi(const seqStore_t* seqStorePtr, repcodes_t rep; ZSTD_memcpy(&rep, prevCBlock->rep, sizeof(rep)); for (seq = sstart; seq < sp; ++seq) { - ZSTD_updateRep(rep.rep, seq->offBase - 1, ZSTD_getSequenceLength(seqStorePtr, seq).litLength == 0); + ZSTD_updateRep(rep.rep, seq->offBase, ZSTD_getSequenceLength(seqStorePtr, seq).litLength == 0); } ZSTD_memcpy(nextCBlock->rep, &rep, sizeof(rep)); } diff --git a/internal-complibs/zstd-1.5.2/compress/zstd_compress_superblock.h b/internal-complibs/zstd-1.5.5/compress/zstd_compress_superblock.h similarity index 95% rename from internal-complibs/zstd-1.5.2/compress/zstd_compress_superblock.h rename to internal-complibs/zstd-1.5.5/compress/zstd_compress_superblock.h index 176f9b10..8e494f0d 100644 --- a/internal-complibs/zstd-1.5.2/compress/zstd_compress_superblock.h +++ b/internal-complibs/zstd-1.5.5/compress/zstd_compress_superblock.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the diff --git a/internal-complibs/zstd-1.5.2/compress/zstd_cwksp.h b/internal-complibs/zstd-1.5.5/compress/zstd_cwksp.h similarity index 77% rename from internal-complibs/zstd-1.5.2/compress/zstd_cwksp.h rename to internal-complibs/zstd-1.5.5/compress/zstd_cwksp.h index dc3f40c8..cc7fb1c7 100644 --- a/internal-complibs/zstd-1.5.2/compress/zstd_cwksp.h +++ b/internal-complibs/zstd-1.5.5/compress/zstd_cwksp.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -14,7 +14,9 @@ /*-************************************* * Dependencies ***************************************/ +#include "../common/allocations.h" /* ZSTD_customMalloc, ZSTD_customFree */ #include "../common/zstd_internal.h" +#include "../common/portability_macros.h" #if defined (__cplusplus) extern "C" { @@ -44,8 +46,9 @@ extern "C" { ***************************************/ typedef enum { ZSTD_cwksp_alloc_objects, - ZSTD_cwksp_alloc_buffers, - ZSTD_cwksp_alloc_aligned + ZSTD_cwksp_alloc_aligned_init_once, + ZSTD_cwksp_alloc_aligned, + ZSTD_cwksp_alloc_buffers } ZSTD_cwksp_alloc_phase_e; /** @@ -98,8 +101,8 @@ typedef enum { * * Workspace Layout: * - * [ ... workspace ... ] - * [objects][tables ... ->] free space [<- ... aligned][<- ... buffers] + * [ ... workspace ... ] + * [objects][tables ->] free space [<- buffers][<- aligned][<- init once] * * The various objects that live in the workspace are divided into the * following categories, and are allocated separately: @@ -123,9 +126,18 @@ typedef enum { * uint32_t arrays, all of whose values are between 0 and (nextSrc - base). * Their sizes depend on the cparams. These tables are 64-byte aligned. * - * - Aligned: these buffers are used for various purposes that require 4 byte - * alignment, but don't require any initialization before they're used. These - * buffers are each aligned to 64 bytes. + * - Init once: these buffers require to be initialized at least once before + * use. They should be used when we want to skip memory initialization + * while not triggering memory checkers (like Valgrind) when reading from + * from this memory without writing to it first. + * These buffers should be used carefully as they might contain data + * from previous compressions. + * Buffers are aligned to 64 bytes. + * + * - Aligned: these buffers don't require any initialization before they're + * used. The user of the buffer should make sure they write into a buffer + * location before reading from it. + * Buffers are aligned to 64 bytes. * * - Buffers: these buffers are used for various purposes that don't require * any alignment or initialization before they're used. This means they can @@ -137,8 +149,9 @@ typedef enum { * correctly packed into the workspace buffer. That order is: * * 1. Objects - * 2. Buffers - * 3. Aligned/Tables + * 2. Init once / Tables + * 3. Aligned / Tables + * 4. Buffers / Tables * * Attempts to reserve objects of different types out of order will fail. */ @@ -150,6 +163,7 @@ typedef struct { void* tableEnd; void* tableValidEnd; void* allocStart; + void* initOnceStart; BYTE allocFailed; int workspaceOversizedDuration; @@ -162,6 +176,7 @@ typedef struct { ***************************************/ MEM_STATIC size_t ZSTD_cwksp_available_space(ZSTD_cwksp* ws); +MEM_STATIC void* ZSTD_cwksp_initialAllocStart(ZSTD_cwksp* ws); MEM_STATIC void ZSTD_cwksp_assert_internal_consistency(ZSTD_cwksp* ws) { (void)ws; @@ -171,6 +186,20 @@ MEM_STATIC void ZSTD_cwksp_assert_internal_consistency(ZSTD_cwksp* ws) { assert(ws->tableEnd <= ws->allocStart); assert(ws->tableValidEnd <= ws->allocStart); assert(ws->allocStart <= ws->workspaceEnd); + assert(ws->initOnceStart <= ZSTD_cwksp_initialAllocStart(ws)); + assert(ws->workspace <= ws->initOnceStart); +#if ZSTD_MEMORY_SANITIZER + { + intptr_t const offset = __msan_test_shadow(ws->initOnceStart, + (U8*)ZSTD_cwksp_initialAllocStart(ws) - (U8*)ws->initOnceStart); +#if defined(ZSTD_MSAN_PRINT) + if(offset!=-1) { + __msan_print_shadow((U8*)ws->initOnceStart + offset - 8, 32); + } +#endif + assert(offset==-1); + }; +#endif } /** @@ -217,14 +246,10 @@ MEM_STATIC size_t ZSTD_cwksp_aligned_alloc_size(size_t size) { * for internal purposes (currently only alignment). */ MEM_STATIC size_t ZSTD_cwksp_slack_space_required(void) { - /* For alignment, the wksp will always allocate an additional n_1=[1, 64] bytes - * to align the beginning of tables section, as well as another n_2=[0, 63] bytes - * to align the beginning of the aligned section. - * - * n_1 + n_2 == 64 bytes if the cwksp is freshly allocated, due to tables and - * aligneds being sized in multiples of 64 bytes. + /* For alignment, the wksp will always allocate an additional 2*ZSTD_CWKSP_ALIGNMENT_BYTES + * bytes to align the beginning of tables section and end of buffers; */ - size_t const slackSpace = ZSTD_CWKSP_ALIGNMENT_BYTES; + size_t const slackSpace = ZSTD_CWKSP_ALIGNMENT_BYTES * 2; return slackSpace; } @@ -237,10 +262,18 @@ MEM_STATIC size_t ZSTD_cwksp_bytes_to_align_ptr(void* ptr, const size_t alignByt size_t const alignBytesMask = alignBytes - 1; size_t const bytes = (alignBytes - ((size_t)ptr & (alignBytesMask))) & alignBytesMask; assert((alignBytes & alignBytesMask) == 0); - assert(bytes != ZSTD_CWKSP_ALIGNMENT_BYTES); + assert(bytes < alignBytes); return bytes; } +/** + * Returns the initial value for allocStart which is used to determine the position from + * which we can allocate from the end of the workspace. + */ +MEM_STATIC void* ZSTD_cwksp_initialAllocStart(ZSTD_cwksp* ws) { + return (void*)((size_t)ws->workspaceEnd & ~(ZSTD_CWKSP_ALIGNMENT_BYTES-1)); +} + /** * Internal function. Do not use directly. * Reserves the given number of bytes within the aligned/buffer segment of the wksp, @@ -281,27 +314,16 @@ ZSTD_cwksp_internal_advance_phase(ZSTD_cwksp* ws, ZSTD_cwksp_alloc_phase_e phase { assert(phase >= ws->phase); if (phase > ws->phase) { - /* Going from allocating objects to allocating buffers */ - if (ws->phase < ZSTD_cwksp_alloc_buffers && - phase >= ZSTD_cwksp_alloc_buffers) { + /* Going from allocating objects to allocating initOnce / tables */ + if (ws->phase < ZSTD_cwksp_alloc_aligned_init_once && + phase >= ZSTD_cwksp_alloc_aligned_init_once) { ws->tableValidEnd = ws->objectEnd; - } + ws->initOnceStart = ZSTD_cwksp_initialAllocStart(ws); - /* Going from allocating buffers to allocating aligneds/tables */ - if (ws->phase < ZSTD_cwksp_alloc_aligned && - phase >= ZSTD_cwksp_alloc_aligned) { - { /* Align the start of the "aligned" to 64 bytes. Use [1, 64] bytes. */ - size_t const bytesToAlign = - ZSTD_CWKSP_ALIGNMENT_BYTES - ZSTD_cwksp_bytes_to_align_ptr(ws->allocStart, ZSTD_CWKSP_ALIGNMENT_BYTES); - DEBUGLOG(5, "reserving aligned alignment addtl space: %zu", bytesToAlign); - ZSTD_STATIC_ASSERT((ZSTD_CWKSP_ALIGNMENT_BYTES & (ZSTD_CWKSP_ALIGNMENT_BYTES - 1)) == 0); /* power of 2 */ - RETURN_ERROR_IF(!ZSTD_cwksp_reserve_internal_buffer_space(ws, bytesToAlign), - memory_allocation, "aligned phase - alignment initial allocation failed!"); - } { /* Align the start of the tables to 64 bytes. Use [0, 63] bytes */ - void* const alloc = ws->objectEnd; + void *const alloc = ws->objectEnd; size_t const bytesToAlign = ZSTD_cwksp_bytes_to_align_ptr(alloc, ZSTD_CWKSP_ALIGNMENT_BYTES); - void* const objectEnd = (BYTE*)alloc + bytesToAlign; + void *const objectEnd = (BYTE *) alloc + bytesToAlign; DEBUGLOG(5, "reserving table alignment addtl space: %zu", bytesToAlign); RETURN_ERROR_IF(objectEnd > ws->workspaceEnd, memory_allocation, "table phase - alignment initial allocation failed!"); @@ -309,7 +331,9 @@ ZSTD_cwksp_internal_advance_phase(ZSTD_cwksp* ws, ZSTD_cwksp_alloc_phase_e phase ws->tableEnd = objectEnd; /* table area starts being empty */ if (ws->tableValidEnd < ws->tableEnd) { ws->tableValidEnd = ws->tableEnd; - } } } + } + } + } ws->phase = phase; ZSTD_cwksp_assert_internal_consistency(ws); } @@ -321,7 +345,7 @@ ZSTD_cwksp_internal_advance_phase(ZSTD_cwksp* ws, ZSTD_cwksp_alloc_phase_e phase */ MEM_STATIC int ZSTD_cwksp_owns_buffer(const ZSTD_cwksp* ws, const void* ptr) { - return (ptr != NULL) && (ws->workspace <= ptr) && (ptr <= ws->workspaceEnd); + return (ptr != NULL) && (ws->workspace <= ptr) && (ptr < ws->workspaceEnd); } /** @@ -348,7 +372,9 @@ ZSTD_cwksp_reserve_internal(ZSTD_cwksp* ws, size_t bytes, ZSTD_cwksp_alloc_phase if (alloc) { alloc = (BYTE *)alloc + ZSTD_CWKSP_ASAN_REDZONE_SIZE; if (ws->isStatic == ZSTD_cwksp_dynamic_alloc) { - __asan_unpoison_memory_region(alloc, bytes); + /* We need to keep the redzone poisoned while unpoisoning the bytes that + * are actually allocated. */ + __asan_unpoison_memory_region(alloc, bytes - 2 * ZSTD_CWKSP_ASAN_REDZONE_SIZE); } } #endif @@ -364,6 +390,36 @@ MEM_STATIC BYTE* ZSTD_cwksp_reserve_buffer(ZSTD_cwksp* ws, size_t bytes) return (BYTE*)ZSTD_cwksp_reserve_internal(ws, bytes, ZSTD_cwksp_alloc_buffers); } +/** + * Reserves and returns memory sized on and aligned on ZSTD_CWKSP_ALIGNMENT_BYTES (64 bytes). + * This memory has been initialized at least once in the past. + * This doesn't mean it has been initialized this time, and it might contain data from previous + * operations. + * The main usage is for algorithms that might need read access into uninitialized memory. + * The algorithm must maintain safety under these conditions and must make sure it doesn't + * leak any of the past data (directly or in side channels). + */ +MEM_STATIC void* ZSTD_cwksp_reserve_aligned_init_once(ZSTD_cwksp* ws, size_t bytes) +{ + size_t const alignedBytes = ZSTD_cwksp_align(bytes, ZSTD_CWKSP_ALIGNMENT_BYTES); + void* ptr = ZSTD_cwksp_reserve_internal(ws, alignedBytes, ZSTD_cwksp_alloc_aligned_init_once); + assert(((size_t)ptr & (ZSTD_CWKSP_ALIGNMENT_BYTES-1))== 0); + if(ptr && ptr < ws->initOnceStart) { + /* We assume the memory following the current allocation is either: + * 1. Not usable as initOnce memory (end of workspace) + * 2. Another initOnce buffer that has been allocated before (and so was previously memset) + * 3. An ASAN redzone, in which case we don't want to write on it + * For these reasons it should be fine to not explicitly zero every byte up to ws->initOnceStart. + * Note that we assume here that MSAN and ASAN cannot run in the same time. */ + ZSTD_memset(ptr, 0, MIN((size_t)((U8*)ws->initOnceStart - (U8*)ptr), alignedBytes)); + ws->initOnceStart = ptr; + } +#if ZSTD_MEMORY_SANITIZER + assert(__msan_test_shadow(ptr, bytes) == -1); +#endif + return ptr; +} + /** * Reserves and returns memory sized on and aligned on ZSTD_CWKSP_ALIGNMENT_BYTES (64 bytes). */ @@ -382,13 +438,17 @@ MEM_STATIC void* ZSTD_cwksp_reserve_aligned(ZSTD_cwksp* ws, size_t bytes) */ MEM_STATIC void* ZSTD_cwksp_reserve_table(ZSTD_cwksp* ws, size_t bytes) { - const ZSTD_cwksp_alloc_phase_e phase = ZSTD_cwksp_alloc_aligned; + const ZSTD_cwksp_alloc_phase_e phase = ZSTD_cwksp_alloc_aligned_init_once; void* alloc; void* end; void* top; - if (ZSTD_isError(ZSTD_cwksp_internal_advance_phase(ws, phase))) { - return NULL; + /* We can only start allocating tables after we are done reserving space for objects at the + * start of the workspace */ + if(ws->phase < phase) { + if (ZSTD_isError(ZSTD_cwksp_internal_advance_phase(ws, phase))) { + return NULL; + } } alloc = ws->tableEnd; end = (BYTE *)alloc + bytes; @@ -467,11 +527,19 @@ MEM_STATIC void ZSTD_cwksp_mark_tables_dirty(ZSTD_cwksp* ws) #if ZSTD_MEMORY_SANITIZER && !defined (ZSTD_MSAN_DONT_POISON_WORKSPACE) /* To validate that the table re-use logic is sound, and that we don't * access table space that we haven't cleaned, we re-"poison" the table - * space every time we mark it dirty. */ + * space every time we mark it dirty. + * Since tableValidEnd space and initOnce space may overlap we don't poison + * the initOnce portion as it break its promise. This means that this poisoning + * check isn't always applied fully. */ { size_t size = (BYTE*)ws->tableValidEnd - (BYTE*)ws->objectEnd; assert(__msan_test_shadow(ws->objectEnd, size) == -1); - __msan_poison(ws->objectEnd, size); + if((BYTE*)ws->tableValidEnd < (BYTE*)ws->initOnceStart) { + __msan_poison(ws->objectEnd, size); + } else { + assert(ws->initOnceStart >= ws->objectEnd); + __msan_poison(ws->objectEnd, (BYTE*)ws->initOnceStart - (BYTE*)ws->objectEnd); + } } #endif @@ -499,7 +567,7 @@ MEM_STATIC void ZSTD_cwksp_clean_tables(ZSTD_cwksp* ws) { assert(ws->tableValidEnd >= ws->objectEnd); assert(ws->tableValidEnd <= ws->allocStart); if (ws->tableValidEnd < ws->tableEnd) { - ZSTD_memset(ws->tableValidEnd, 0, (BYTE*)ws->tableEnd - (BYTE*)ws->tableValidEnd); + ZSTD_memset(ws->tableValidEnd, 0, (size_t)((BYTE*)ws->tableEnd - (BYTE*)ws->tableValidEnd)); } ZSTD_cwksp_mark_tables_clean(ws); } @@ -536,11 +604,14 @@ MEM_STATIC void ZSTD_cwksp_clear(ZSTD_cwksp* ws) { #if ZSTD_MEMORY_SANITIZER && !defined (ZSTD_MSAN_DONT_POISON_WORKSPACE) /* To validate that the context re-use logic is sound, and that we don't * access stuff that this compression hasn't initialized, we re-"poison" - * the workspace (or at least the non-static, non-table parts of it) - * every time we start a new compression. */ + * the workspace except for the areas in which we expect memory re-use + * without initialization (objects, valid tables area and init once + * memory). */ { - size_t size = (BYTE*)ws->workspaceEnd - (BYTE*)ws->tableValidEnd; - __msan_poison(ws->tableValidEnd, size); + if((BYTE*)ws->tableValidEnd < (BYTE*)ws->initOnceStart) { + size_t size = (BYTE*)ws->initOnceStart - (BYTE*)ws->tableValidEnd; + __msan_poison(ws->tableValidEnd, size); + } } #endif @@ -556,10 +627,10 @@ MEM_STATIC void ZSTD_cwksp_clear(ZSTD_cwksp* ws) { #endif ws->tableEnd = ws->objectEnd; - ws->allocStart = ws->workspaceEnd; + ws->allocStart = ZSTD_cwksp_initialAllocStart(ws); ws->allocFailed = 0; - if (ws->phase > ZSTD_cwksp_alloc_buffers) { - ws->phase = ZSTD_cwksp_alloc_buffers; + if (ws->phase > ZSTD_cwksp_alloc_aligned_init_once) { + ws->phase = ZSTD_cwksp_alloc_aligned_init_once; } ZSTD_cwksp_assert_internal_consistency(ws); } @@ -576,6 +647,7 @@ MEM_STATIC void ZSTD_cwksp_init(ZSTD_cwksp* ws, void* start, size_t size, ZSTD_c ws->workspaceEnd = (BYTE*)start + size; ws->objectEnd = ws->workspace; ws->tableValidEnd = ws->objectEnd; + ws->initOnceStart = ZSTD_cwksp_initialAllocStart(ws); ws->phase = ZSTD_cwksp_alloc_objects; ws->isStatic = isStatic; ZSTD_cwksp_clear(ws); @@ -628,17 +700,11 @@ MEM_STATIC int ZSTD_cwksp_reserve_failed(const ZSTD_cwksp* ws) { * Returns if the estimated space needed for a wksp is within an acceptable limit of the * actual amount of space used. */ -MEM_STATIC int ZSTD_cwksp_estimated_space_within_bounds(const ZSTD_cwksp* const ws, - size_t const estimatedSpace, int resizedWorkspace) { - if (resizedWorkspace) { - /* Resized/newly allocated wksp should have exact bounds */ - return ZSTD_cwksp_used(ws) == estimatedSpace; - } else { - /* Due to alignment, when reusing a workspace, we can actually consume 63 fewer or more bytes - * than estimatedSpace. See the comments in zstd_cwksp.h for details. - */ - return (ZSTD_cwksp_used(ws) >= estimatedSpace - 63) && (ZSTD_cwksp_used(ws) <= estimatedSpace + 63); - } +MEM_STATIC int ZSTD_cwksp_estimated_space_within_bounds(const ZSTD_cwksp *const ws, size_t const estimatedSpace) { + /* We have an alignment space between objects and tables between tables and buffers, so we can have up to twice + * the alignment bytes difference between estimation and actual usage */ + return (estimatedSpace - ZSTD_cwksp_slack_space_required()) <= ZSTD_cwksp_used(ws) && + ZSTD_cwksp_used(ws) <= estimatedSpace; } diff --git a/internal-complibs/zstd-1.5.2/compress/zstd_double_fast.c b/internal-complibs/zstd-1.5.5/compress/zstd_double_fast.c similarity index 85% rename from internal-complibs/zstd-1.5.2/compress/zstd_double_fast.c rename to internal-complibs/zstd-1.5.5/compress/zstd_double_fast.c index 76933dea..0ad88ffc 100644 --- a/internal-complibs/zstd-1.5.2/compress/zstd_double_fast.c +++ b/internal-complibs/zstd-1.5.5/compress/zstd_double_fast.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -11,8 +11,43 @@ #include "zstd_compress_internal.h" #include "zstd_double_fast.h" +static void ZSTD_fillDoubleHashTableForCDict(ZSTD_matchState_t* ms, + void const* end, ZSTD_dictTableLoadMethod_e dtlm) +{ + const ZSTD_compressionParameters* const cParams = &ms->cParams; + U32* const hashLarge = ms->hashTable; + U32 const hBitsL = cParams->hashLog + ZSTD_SHORT_CACHE_TAG_BITS; + U32 const mls = cParams->minMatch; + U32* const hashSmall = ms->chainTable; + U32 const hBitsS = cParams->chainLog + ZSTD_SHORT_CACHE_TAG_BITS; + const BYTE* const base = ms->window.base; + const BYTE* ip = base + ms->nextToUpdate; + const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE; + const U32 fastHashFillStep = 3; -void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms, + /* Always insert every fastHashFillStep position into the hash tables. + * Insert the other positions into the large hash table if their entry + * is empty. + */ + for (; ip + fastHashFillStep - 1 <= iend; ip += fastHashFillStep) { + U32 const curr = (U32)(ip - base); + U32 i; + for (i = 0; i < fastHashFillStep; ++i) { + size_t const smHashAndTag = ZSTD_hashPtr(ip + i, hBitsS, mls); + size_t const lgHashAndTag = ZSTD_hashPtr(ip + i, hBitsL, 8); + if (i == 0) { + ZSTD_writeTaggedIndex(hashSmall, smHashAndTag, curr + i); + } + if (i == 0 || hashLarge[lgHashAndTag >> ZSTD_SHORT_CACHE_TAG_BITS] == 0) { + ZSTD_writeTaggedIndex(hashLarge, lgHashAndTag, curr + i); + } + /* Only load extra positions for ZSTD_dtlm_full */ + if (dtlm == ZSTD_dtlm_fast) + break; + } } +} + +static void ZSTD_fillDoubleHashTableForCCtx(ZSTD_matchState_t* ms, void const* end, ZSTD_dictTableLoadMethod_e dtlm) { const ZSTD_compressionParameters* const cParams = &ms->cParams; @@ -43,7 +78,19 @@ void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms, /* Only load extra positions for ZSTD_dtlm_full */ if (dtlm == ZSTD_dtlm_fast) break; - } } + } } +} + +void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms, + const void* const end, + ZSTD_dictTableLoadMethod_e dtlm, + ZSTD_tableFillPurpose_e tfp) +{ + if (tfp == ZSTD_tfp_forCDict) { + ZSTD_fillDoubleHashTableForCDict(ms, end, dtlm); + } else { + ZSTD_fillDoubleHashTableForCCtx(ms, end, dtlm); + } } @@ -67,7 +114,7 @@ size_t ZSTD_compressBlock_doubleFast_noDict_generic( const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - HASH_READ_SIZE; U32 offset_1=rep[0], offset_2=rep[1]; - U32 offsetSaved = 0; + U32 offsetSaved1 = 0, offsetSaved2 = 0; size_t mLength; U32 offset; @@ -100,8 +147,8 @@ size_t ZSTD_compressBlock_doubleFast_noDict_generic( U32 const current = (U32)(ip - base); U32 const windowLow = ZSTD_getLowestPrefixIndex(ms, current, cParams->windowLog); U32 const maxRep = current - windowLow; - if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; - if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; + if (offset_2 > maxRep) offsetSaved2 = offset_2, offset_2 = 0; + if (offset_1 > maxRep) offsetSaved1 = offset_1, offset_1 = 0; } /* Outer Loop: one iteration per match found and stored */ @@ -131,7 +178,7 @@ size_t ZSTD_compressBlock_doubleFast_noDict_generic( if ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1))) { mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; ip++; - ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, STORE_REPCODE_1, mLength); + ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, REPCODE1_TO_OFFBASE, mLength); goto _match_stored; } @@ -175,9 +222,13 @@ size_t ZSTD_compressBlock_doubleFast_noDict_generic( } while (ip1 <= ilimit); _cleanup: + /* If offset_1 started invalid (offsetSaved1 != 0) and became valid (offset_1 != 0), + * rotate saved offsets. See comment in ZSTD_compressBlock_fast_noDict for more context. */ + offsetSaved2 = ((offsetSaved1 != 0) && (offset_1 != 0)) ? offsetSaved1 : offsetSaved2; + /* save reps for next block */ - rep[0] = offset_1 ? offset_1 : offsetSaved; - rep[1] = offset_2 ? offset_2 : offsetSaved; + rep[0] = offset_1 ? offset_1 : offsetSaved1; + rep[1] = offset_2 ? offset_2 : offsetSaved2; /* Return the last literals size */ return (size_t)(iend - anchor); @@ -217,7 +268,7 @@ size_t ZSTD_compressBlock_doubleFast_noDict_generic( hashLong[hl1] = (U32)(ip1 - base); } - ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, STORE_OFFSET(offset), mLength); + ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength); _match_stored: /* match found */ @@ -243,7 +294,7 @@ size_t ZSTD_compressBlock_doubleFast_noDict_generic( U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff; /* swap offset_2 <=> offset_1 */ hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = (U32)(ip-base); hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = (U32)(ip-base); - ZSTD_storeSeq(seqStore, 0, anchor, iend, STORE_REPCODE_1, rLength); + ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, rLength); ip += rLength; anchor = ip; continue; /* faster when present ... (?) */ @@ -275,7 +326,6 @@ size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic( const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - HASH_READ_SIZE; U32 offset_1=rep[0], offset_2=rep[1]; - U32 offsetSaved = 0; const ZSTD_matchState_t* const dms = ms->dictMatchState; const ZSTD_compressionParameters* const dictCParams = &dms->cParams; @@ -286,8 +336,8 @@ size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic( const BYTE* const dictStart = dictBase + dictStartIndex; const BYTE* const dictEnd = dms->window.nextSrc; const U32 dictIndexDelta = prefixLowestIndex - (U32)(dictEnd - dictBase); - const U32 dictHBitsL = dictCParams->hashLog; - const U32 dictHBitsS = dictCParams->chainLog; + const U32 dictHBitsL = dictCParams->hashLog + ZSTD_SHORT_CACHE_TAG_BITS; + const U32 dictHBitsS = dictCParams->chainLog + ZSTD_SHORT_CACHE_TAG_BITS; const U32 dictAndPrefixLength = (U32)((ip - prefixLowest) + (dictEnd - dictStart)); DEBUGLOG(5, "ZSTD_compressBlock_doubleFast_dictMatchState_generic"); @@ -295,6 +345,13 @@ size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic( /* if a dictionary is attached, it must be within window range */ assert(ms->window.dictLimit + (1U << cParams->windowLog) >= endIndex); + if (ms->prefetchCDictTables) { + size_t const hashTableBytes = (((size_t)1) << dictCParams->hashLog) * sizeof(U32); + size_t const chainTableBytes = (((size_t)1) << dictCParams->chainLog) * sizeof(U32); + PREFETCH_AREA(dictHashLong, hashTableBytes) + PREFETCH_AREA(dictHashSmall, chainTableBytes) + } + /* init */ ip += (dictAndPrefixLength == 0); @@ -309,8 +366,12 @@ size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic( U32 offset; size_t const h2 = ZSTD_hashPtr(ip, hBitsL, 8); size_t const h = ZSTD_hashPtr(ip, hBitsS, mls); - size_t const dictHL = ZSTD_hashPtr(ip, dictHBitsL, 8); - size_t const dictHS = ZSTD_hashPtr(ip, dictHBitsS, mls); + size_t const dictHashAndTagL = ZSTD_hashPtr(ip, dictHBitsL, 8); + size_t const dictHashAndTagS = ZSTD_hashPtr(ip, dictHBitsS, mls); + U32 const dictMatchIndexAndTagL = dictHashLong[dictHashAndTagL >> ZSTD_SHORT_CACHE_TAG_BITS]; + U32 const dictMatchIndexAndTagS = dictHashSmall[dictHashAndTagS >> ZSTD_SHORT_CACHE_TAG_BITS]; + int const dictTagsMatchL = ZSTD_comparePackedTags(dictMatchIndexAndTagL, dictHashAndTagL); + int const dictTagsMatchS = ZSTD_comparePackedTags(dictMatchIndexAndTagS, dictHashAndTagS); U32 const curr = (U32)(ip-base); U32 const matchIndexL = hashLong[h2]; U32 matchIndexS = hashSmall[h]; @@ -328,7 +389,7 @@ size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic( const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend; mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixLowest) + 4; ip++; - ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, STORE_REPCODE_1, mLength); + ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, REPCODE1_TO_OFFBASE, mLength); goto _match_stored; } @@ -340,9 +401,9 @@ size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic( while (((ip>anchor) & (matchLong>prefixLowest)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */ goto _match_found; } - } else { + } else if (dictTagsMatchL) { /* check dictMatchState long match */ - U32 const dictMatchIndexL = dictHashLong[dictHL]; + U32 const dictMatchIndexL = dictMatchIndexAndTagL >> ZSTD_SHORT_CACHE_TAG_BITS; const BYTE* dictMatchL = dictBase + dictMatchIndexL; assert(dictMatchL < dictEnd); @@ -358,9 +419,9 @@ size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic( if (MEM_read32(match) == MEM_read32(ip)) { goto _search_next_long; } - } else { + } else if (dictTagsMatchS) { /* check dictMatchState short match */ - U32 const dictMatchIndexS = dictHashSmall[dictHS]; + U32 const dictMatchIndexS = dictMatchIndexAndTagS >> ZSTD_SHORT_CACHE_TAG_BITS; match = dictBase + dictMatchIndexS; matchIndexS = dictMatchIndexS + dictIndexDelta; @@ -375,10 +436,11 @@ size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic( continue; _search_next_long: - { size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); - size_t const dictHLNext = ZSTD_hashPtr(ip+1, dictHBitsL, 8); + size_t const dictHashAndTagL3 = ZSTD_hashPtr(ip+1, dictHBitsL, 8); U32 const matchIndexL3 = hashLong[hl3]; + U32 const dictMatchIndexAndTagL3 = dictHashLong[dictHashAndTagL3 >> ZSTD_SHORT_CACHE_TAG_BITS]; + int const dictTagsMatchL3 = ZSTD_comparePackedTags(dictMatchIndexAndTagL3, dictHashAndTagL3); const BYTE* matchL3 = base + matchIndexL3; hashLong[hl3] = curr + 1; @@ -391,9 +453,9 @@ size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic( while (((ip>anchor) & (matchL3>prefixLowest)) && (ip[-1] == matchL3[-1])) { ip--; matchL3--; mLength++; } /* catch up */ goto _match_found; } - } else { + } else if (dictTagsMatchL3) { /* check dict long +1 match */ - U32 const dictMatchIndexL3 = dictHashLong[dictHLNext]; + U32 const dictMatchIndexL3 = dictMatchIndexAndTagL3 >> ZSTD_SHORT_CACHE_TAG_BITS; const BYTE* dictMatchL3 = dictBase + dictMatchIndexL3; assert(dictMatchL3 < dictEnd); if (dictMatchL3 > dictStart && MEM_read64(dictMatchL3) == MEM_read64(ip+1)) { @@ -419,7 +481,7 @@ size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic( offset_2 = offset_1; offset_1 = offset; - ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, STORE_OFFSET(offset), mLength); + ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength); _match_stored: /* match found */ @@ -448,7 +510,7 @@ size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic( const BYTE* const repEnd2 = repIndex2 < prefixLowestIndex ? dictEnd : iend; size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixLowest) + 4; U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ - ZSTD_storeSeq(seqStore, 0, anchor, iend, STORE_REPCODE_1, repLength2); + ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, repLength2); hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = current2; hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = current2; ip += repLength2; @@ -461,8 +523,8 @@ size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic( } /* while (ip < ilimit) */ /* save reps for next block */ - rep[0] = offset_1 ? offset_1 : offsetSaved; - rep[1] = offset_2 ? offset_2 : offsetSaved; + rep[0] = offset_1; + rep[1] = offset_2; /* Return the last literals size */ return (size_t)(iend - anchor); @@ -585,7 +647,7 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic( const BYTE* repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend; mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixStart) + 4; ip++; - ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, STORE_REPCODE_1, mLength); + ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, REPCODE1_TO_OFFBASE, mLength); } else { if ((matchLongIndex > dictStartIndex) && (MEM_read64(matchLong) == MEM_read64(ip))) { const BYTE* const matchEnd = matchLongIndex < prefixStartIndex ? dictEnd : iend; @@ -596,7 +658,7 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic( while (((ip>anchor) & (matchLong>lowMatchPtr)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */ offset_2 = offset_1; offset_1 = offset; - ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, STORE_OFFSET(offset), mLength); + ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength); } else if ((matchIndex > dictStartIndex) && (MEM_read32(match) == MEM_read32(ip))) { size_t const h3 = ZSTD_hashPtr(ip+1, hBitsL, 8); @@ -621,7 +683,7 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic( } offset_2 = offset_1; offset_1 = offset; - ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, STORE_OFFSET(offset), mLength); + ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength); } else { ip += ((ip-anchor) >> kSearchStrength) + 1; @@ -653,7 +715,7 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic( const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend; size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4; U32 const tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ - ZSTD_storeSeq(seqStore, 0, anchor, iend, STORE_REPCODE_1, repLength2); + ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, repLength2); hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = current2; hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = current2; ip += repLength2; diff --git a/internal-complibs/zstd-1.5.2/compress/zstd_double_fast.h b/internal-complibs/zstd-1.5.5/compress/zstd_double_fast.h similarity index 90% rename from internal-complibs/zstd-1.5.2/compress/zstd_double_fast.h rename to internal-complibs/zstd-1.5.5/compress/zstd_double_fast.h index e16b7b03..6f0047c4 100644 --- a/internal-complibs/zstd-1.5.2/compress/zstd_double_fast.h +++ b/internal-complibs/zstd-1.5.5/compress/zstd_double_fast.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -19,7 +19,8 @@ extern "C" { #include "zstd_compress_internal.h" /* ZSTD_CCtx, size_t */ void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms, - void const* end, ZSTD_dictTableLoadMethod_e dtlm); + void const* end, ZSTD_dictTableLoadMethod_e dtlm, + ZSTD_tableFillPurpose_e tfp); size_t ZSTD_compressBlock_doubleFast( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); diff --git a/internal-complibs/zstd-1.5.2/compress/zstd_fast.c b/internal-complibs/zstd-1.5.5/compress/zstd_fast.c similarity index 51% rename from internal-complibs/zstd-1.5.2/compress/zstd_fast.c rename to internal-complibs/zstd-1.5.5/compress/zstd_fast.c index 802fc315..5f2c6a2e 100644 --- a/internal-complibs/zstd-1.5.2/compress/zstd_fast.c +++ b/internal-complibs/zstd-1.5.5/compress/zstd_fast.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -11,8 +11,42 @@ #include "zstd_compress_internal.h" /* ZSTD_hashPtr, ZSTD_count, ZSTD_storeSeq */ #include "zstd_fast.h" +static void ZSTD_fillHashTableForCDict(ZSTD_matchState_t* ms, + const void* const end, + ZSTD_dictTableLoadMethod_e dtlm) +{ + const ZSTD_compressionParameters* const cParams = &ms->cParams; + U32* const hashTable = ms->hashTable; + U32 const hBits = cParams->hashLog + ZSTD_SHORT_CACHE_TAG_BITS; + U32 const mls = cParams->minMatch; + const BYTE* const base = ms->window.base; + const BYTE* ip = base + ms->nextToUpdate; + const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE; + const U32 fastHashFillStep = 3; -void ZSTD_fillHashTable(ZSTD_matchState_t* ms, + /* Currently, we always use ZSTD_dtlm_full for filling CDict tables. + * Feel free to remove this assert if there's a good reason! */ + assert(dtlm == ZSTD_dtlm_full); + + /* Always insert every fastHashFillStep position into the hash table. + * Insert the other positions if their hash entry is empty. + */ + for ( ; ip + fastHashFillStep < iend + 2; ip += fastHashFillStep) { + U32 const curr = (U32)(ip - base); + { size_t const hashAndTag = ZSTD_hashPtr(ip, hBits, mls); + ZSTD_writeTaggedIndex(hashTable, hashAndTag, curr); } + + if (dtlm == ZSTD_dtlm_fast) continue; + /* Only load extra positions for ZSTD_dtlm_full */ + { U32 p; + for (p = 1; p < fastHashFillStep; ++p) { + size_t const hashAndTag = ZSTD_hashPtr(ip + p, hBits, mls); + if (hashTable[hashAndTag >> ZSTD_SHORT_CACHE_TAG_BITS] == 0) { /* not yet filled */ + ZSTD_writeTaggedIndex(hashTable, hashAndTag, curr + p); + } } } } +} + +static void ZSTD_fillHashTableForCCtx(ZSTD_matchState_t* ms, const void* const end, ZSTD_dictTableLoadMethod_e dtlm) { @@ -25,6 +59,10 @@ void ZSTD_fillHashTable(ZSTD_matchState_t* ms, const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE; const U32 fastHashFillStep = 3; + /* Currently, we always use ZSTD_dtlm_fast for filling CCtx tables. + * Feel free to remove this assert if there's a good reason! */ + assert(dtlm == ZSTD_dtlm_fast); + /* Always insert every fastHashFillStep position into the hash table. * Insert the other positions if their hash entry is empty. */ @@ -42,6 +80,18 @@ void ZSTD_fillHashTable(ZSTD_matchState_t* ms, } } } } } +void ZSTD_fillHashTable(ZSTD_matchState_t* ms, + const void* const end, + ZSTD_dictTableLoadMethod_e dtlm, + ZSTD_tableFillPurpose_e tfp) +{ + if (tfp == ZSTD_tfp_forCDict) { + ZSTD_fillHashTableForCDict(ms, end, dtlm); + } else { + ZSTD_fillHashTableForCCtx(ms, end, dtlm); + } +} + /** * If you squint hard enough (and ignore repcodes), the search operation at any @@ -117,7 +167,7 @@ ZSTD_compressBlock_fast_noDict_generic( U32 rep_offset1 = rep[0]; U32 rep_offset2 = rep[1]; - U32 offsetSaved = 0; + U32 offsetSaved1 = 0, offsetSaved2 = 0; size_t hash0; /* hash for ip0 */ size_t hash1; /* hash for ip1 */ @@ -141,8 +191,8 @@ ZSTD_compressBlock_fast_noDict_generic( { U32 const curr = (U32)(ip0 - base); U32 const windowLow = ZSTD_getLowestPrefixIndex(ms, curr, cParams->windowLog); U32 const maxRep = curr - windowLow; - if (rep_offset2 > maxRep) offsetSaved = rep_offset2, rep_offset2 = 0; - if (rep_offset1 > maxRep) offsetSaved = rep_offset1, rep_offset1 = 0; + if (rep_offset2 > maxRep) offsetSaved2 = rep_offset2, rep_offset2 = 0; + if (rep_offset1 > maxRep) offsetSaved1 = rep_offset1, rep_offset1 = 0; } /* start each op */ @@ -180,8 +230,14 @@ ZSTD_compressBlock_fast_noDict_generic( mLength = ip0[-1] == match0[-1]; ip0 -= mLength; match0 -= mLength; - offcode = STORE_REPCODE_1; + offcode = REPCODE1_TO_OFFBASE; mLength += 4; + + /* First write next hash table entry; we've already calculated it. + * This write is known to be safe because the ip1 is before the + * repcode (ip2). */ + hashTable[hash1] = (U32)(ip1 - base); + goto _match; } @@ -195,6 +251,12 @@ ZSTD_compressBlock_fast_noDict_generic( /* check match at ip[0] */ if (MEM_read32(ip0) == mval) { /* found a match! */ + + /* First write next hash table entry; we've already calculated it. + * This write is known to be safe because the ip1 == ip0 + 1, so + * we know we will resume searching after ip1 */ + hashTable[hash1] = (U32)(ip1 - base); + goto _offset; } @@ -224,6 +286,21 @@ ZSTD_compressBlock_fast_noDict_generic( /* check match at ip[0] */ if (MEM_read32(ip0) == mval) { /* found a match! */ + + /* first write next hash table entry; we've already calculated it */ + if (step <= 4) { + /* We need to avoid writing an index into the hash table >= the + * position at which we will pick up our searching after we've + * taken this match. + * + * The minimum possible match has length 4, so the earliest ip0 + * can be after we take this match will be the current ip0 + 4. + * ip1 is ip0 + step - 1. If ip1 is >= ip0 + 4, we can't safely + * write this position. + */ + hashTable[hash1] = (U32)(ip1 - base); + } + goto _offset; } @@ -254,9 +331,24 @@ ZSTD_compressBlock_fast_noDict_generic( * However, it seems to be a meaningful performance hit to try to search * them. So let's not. */ + /* When the repcodes are outside of the prefix, we set them to zero before the loop. + * When the offsets are still zero, we need to restore them after the block to have a correct + * repcode history. If only one offset was invalid, it is easy. The tricky case is when both + * offsets were invalid. We need to figure out which offset to refill with. + * - If both offsets are zero they are in the same order. + * - If both offsets are non-zero, we won't restore the offsets from `offsetSaved[12]`. + * - If only one is zero, we need to decide which offset to restore. + * - If rep_offset1 is non-zero, then rep_offset2 must be offsetSaved1. + * - It is impossible for rep_offset2 to be non-zero. + * + * So if rep_offset1 started invalid (offsetSaved1 != 0) and became valid (rep_offset1 != 0), then + * set rep[0] = rep_offset1 and rep[1] = offsetSaved1. + */ + offsetSaved2 = ((offsetSaved1 != 0) && (rep_offset1 != 0)) ? offsetSaved1 : offsetSaved2; + /* save reps for next block */ - rep[0] = rep_offset1 ? rep_offset1 : offsetSaved; - rep[1] = rep_offset2 ? rep_offset2 : offsetSaved; + rep[0] = rep_offset1 ? rep_offset1 : offsetSaved1; + rep[1] = rep_offset2 ? rep_offset2 : offsetSaved2; /* Return the last literals size */ return (size_t)(iend - anchor); @@ -267,7 +359,7 @@ ZSTD_compressBlock_fast_noDict_generic( match0 = base + idx; rep_offset2 = rep_offset1; rep_offset1 = (U32)(ip0-match0); - offcode = STORE_OFFSET(rep_offset1); + offcode = OFFSET_TO_OFFBASE(rep_offset1); mLength = 4; /* Count the backwards match length. */ @@ -287,11 +379,6 @@ ZSTD_compressBlock_fast_noDict_generic( ip0 += mLength; anchor = ip0; - /* write next hash table entry */ - if (ip1 < ip0) { - hashTable[hash1] = (U32)(ip1 - base); - } - /* Fill table and check for immediate repcode. */ if (ip0 <= ilimit) { /* Fill Table */ @@ -306,7 +393,7 @@ ZSTD_compressBlock_fast_noDict_generic( { U32 const tmpOff = rep_offset2; rep_offset2 = rep_offset1; rep_offset1 = tmpOff; } /* swap rep_offset2 <=> rep_offset1 */ hashTable[ZSTD_hashPtr(ip0, hlog, mls)] = (U32)(ip0-base); ip0 += rLength; - ZSTD_storeSeq(seqStore, 0 /*litLen*/, anchor, iend, STORE_REPCODE_1, rLength); + ZSTD_storeSeq(seqStore, 0 /*litLen*/, anchor, iend, REPCODE1_TO_OFFBASE, rLength); anchor = ip0; continue; /* faster when present (confirmed on gcc-8) ... (?) */ } } } @@ -380,14 +467,14 @@ size_t ZSTD_compressBlock_fast_dictMatchState_generic( U32 const stepSize = cParams->targetLength + !(cParams->targetLength); const BYTE* const base = ms->window.base; const BYTE* const istart = (const BYTE*)src; - const BYTE* ip = istart; + const BYTE* ip0 = istart; + const BYTE* ip1 = ip0 + stepSize; /* we assert below that stepSize >= 1 */ const BYTE* anchor = istart; const U32 prefixStartIndex = ms->window.dictLimit; const BYTE* const prefixStart = base + prefixStartIndex; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - HASH_READ_SIZE; U32 offset_1=rep[0], offset_2=rep[1]; - U32 offsetSaved = 0; const ZSTD_matchState_t* const dms = ms->dictMatchState; const ZSTD_compressionParameters* const dictCParams = &dms->cParams ; @@ -397,13 +484,13 @@ size_t ZSTD_compressBlock_fast_dictMatchState_generic( const BYTE* const dictStart = dictBase + dictStartIndex; const BYTE* const dictEnd = dms->window.nextSrc; const U32 dictIndexDelta = prefixStartIndex - (U32)(dictEnd - dictBase); - const U32 dictAndPrefixLength = (U32)(ip - prefixStart + dictEnd - dictStart); - const U32 dictHLog = dictCParams->hashLog; + const U32 dictAndPrefixLength = (U32)(istart - prefixStart + dictEnd - dictStart); + const U32 dictHBits = dictCParams->hashLog + ZSTD_SHORT_CACHE_TAG_BITS; /* if a dictionary is still attached, it necessarily means that * it is within window size. So we just check it. */ const U32 maxDistance = 1U << cParams->windowLog; - const U32 endIndex = (U32)((size_t)(ip - base) + srcSize); + const U32 endIndex = (U32)((size_t)(istart - base) + srcSize); assert(endIndex - prefixStartIndex <= maxDistance); (void)maxDistance; (void)endIndex; /* these variables are not used when assert() is disabled */ @@ -413,106 +500,155 @@ size_t ZSTD_compressBlock_fast_dictMatchState_generic( * when translating a dict index into a local index */ assert(prefixStartIndex >= (U32)(dictEnd - dictBase)); + if (ms->prefetchCDictTables) { + size_t const hashTableBytes = (((size_t)1) << dictCParams->hashLog) * sizeof(U32); + PREFETCH_AREA(dictHashTable, hashTableBytes) + } + /* init */ DEBUGLOG(5, "ZSTD_compressBlock_fast_dictMatchState_generic"); - ip += (dictAndPrefixLength == 0); + ip0 += (dictAndPrefixLength == 0); /* dictMatchState repCode checks don't currently handle repCode == 0 * disabling. */ assert(offset_1 <= dictAndPrefixLength); assert(offset_2 <= dictAndPrefixLength); - /* Main Search Loop */ - while (ip < ilimit) { /* < instead of <=, because repcode check at (ip+1) */ + /* Outer search loop */ + assert(stepSize >= 1); + while (ip1 <= ilimit) { /* repcode check at (ip0 + 1) is safe because ip0 < ip1 */ size_t mLength; - size_t const h = ZSTD_hashPtr(ip, hlog, mls); - U32 const curr = (U32)(ip-base); - U32 const matchIndex = hashTable[h]; - const BYTE* match = base + matchIndex; - const U32 repIndex = curr + 1 - offset_1; - const BYTE* repMatch = (repIndex < prefixStartIndex) ? - dictBase + (repIndex - dictIndexDelta) : - base + repIndex; - hashTable[h] = curr; /* update hash table */ - - if ( ((U32)((prefixStartIndex-1) - repIndex) >= 3) /* intentional underflow : ensure repIndex isn't overlapping dict + prefix */ - && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { - const BYTE* const repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend; - mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixStart) + 4; - ip++; - ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, STORE_REPCODE_1, mLength); - } else if ( (matchIndex <= prefixStartIndex) ) { - size_t const dictHash = ZSTD_hashPtr(ip, dictHLog, mls); - U32 const dictMatchIndex = dictHashTable[dictHash]; - const BYTE* dictMatch = dictBase + dictMatchIndex; - if (dictMatchIndex <= dictStartIndex || - MEM_read32(dictMatch) != MEM_read32(ip)) { - assert(stepSize >= 1); - ip += ((ip-anchor) >> kSearchStrength) + stepSize; - continue; - } else { - /* found a dict match */ - U32 const offset = (U32)(curr-dictMatchIndex-dictIndexDelta); - mLength = ZSTD_count_2segments(ip+4, dictMatch+4, iend, dictEnd, prefixStart) + 4; - while (((ip>anchor) & (dictMatch>dictStart)) - && (ip[-1] == dictMatch[-1])) { - ip--; dictMatch--; mLength++; + size_t hash0 = ZSTD_hashPtr(ip0, hlog, mls); + + size_t const dictHashAndTag0 = ZSTD_hashPtr(ip0, dictHBits, mls); + U32 dictMatchIndexAndTag = dictHashTable[dictHashAndTag0 >> ZSTD_SHORT_CACHE_TAG_BITS]; + int dictTagsMatch = ZSTD_comparePackedTags(dictMatchIndexAndTag, dictHashAndTag0); + + U32 matchIndex = hashTable[hash0]; + U32 curr = (U32)(ip0 - base); + size_t step = stepSize; + const size_t kStepIncr = 1 << kSearchStrength; + const BYTE* nextStep = ip0 + kStepIncr; + + /* Inner search loop */ + while (1) { + const BYTE* match = base + matchIndex; + const U32 repIndex = curr + 1 - offset_1; + const BYTE* repMatch = (repIndex < prefixStartIndex) ? + dictBase + (repIndex - dictIndexDelta) : + base + repIndex; + const size_t hash1 = ZSTD_hashPtr(ip1, hlog, mls); + size_t const dictHashAndTag1 = ZSTD_hashPtr(ip1, dictHBits, mls); + hashTable[hash0] = curr; /* update hash table */ + + if (((U32) ((prefixStartIndex - 1) - repIndex) >= + 3) /* intentional underflow : ensure repIndex isn't overlapping dict + prefix */ + && (MEM_read32(repMatch) == MEM_read32(ip0 + 1))) { + const BYTE* const repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend; + mLength = ZSTD_count_2segments(ip0 + 1 + 4, repMatch + 4, iend, repMatchEnd, prefixStart) + 4; + ip0++; + ZSTD_storeSeq(seqStore, (size_t) (ip0 - anchor), anchor, iend, REPCODE1_TO_OFFBASE, mLength); + break; + } + + if (dictTagsMatch) { + /* Found a possible dict match */ + const U32 dictMatchIndex = dictMatchIndexAndTag >> ZSTD_SHORT_CACHE_TAG_BITS; + const BYTE* dictMatch = dictBase + dictMatchIndex; + if (dictMatchIndex > dictStartIndex && + MEM_read32(dictMatch) == MEM_read32(ip0)) { + /* To replicate extDict parse behavior, we only use dict matches when the normal matchIndex is invalid */ + if (matchIndex <= prefixStartIndex) { + U32 const offset = (U32) (curr - dictMatchIndex - dictIndexDelta); + mLength = ZSTD_count_2segments(ip0 + 4, dictMatch + 4, iend, dictEnd, prefixStart) + 4; + while (((ip0 > anchor) & (dictMatch > dictStart)) + && (ip0[-1] == dictMatch[-1])) { + ip0--; + dictMatch--; + mLength++; + } /* catch up */ + offset_2 = offset_1; + offset_1 = offset; + ZSTD_storeSeq(seqStore, (size_t) (ip0 - anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength); + break; + } + } + } + + if (matchIndex > prefixStartIndex && MEM_read32(match) == MEM_read32(ip0)) { + /* found a regular match */ + U32 const offset = (U32) (ip0 - match); + mLength = ZSTD_count(ip0 + 4, match + 4, iend) + 4; + while (((ip0 > anchor) & (match > prefixStart)) + && (ip0[-1] == match[-1])) { + ip0--; + match--; + mLength++; } /* catch up */ offset_2 = offset_1; offset_1 = offset; - ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, STORE_OFFSET(offset), mLength); + ZSTD_storeSeq(seqStore, (size_t) (ip0 - anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength); + break; } - } else if (MEM_read32(match) != MEM_read32(ip)) { - /* it's not a match, and we're not going to check the dictionary */ - assert(stepSize >= 1); - ip += ((ip-anchor) >> kSearchStrength) + stepSize; - continue; - } else { - /* found a regular match */ - U32 const offset = (U32)(ip-match); - mLength = ZSTD_count(ip+4, match+4, iend) + 4; - while (((ip>anchor) & (match>prefixStart)) - && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ - offset_2 = offset_1; - offset_1 = offset; - ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, STORE_OFFSET(offset), mLength); - } + + /* Prepare for next iteration */ + dictMatchIndexAndTag = dictHashTable[dictHashAndTag1 >> ZSTD_SHORT_CACHE_TAG_BITS]; + dictTagsMatch = ZSTD_comparePackedTags(dictMatchIndexAndTag, dictHashAndTag1); + matchIndex = hashTable[hash1]; + + if (ip1 >= nextStep) { + step++; + nextStep += kStepIncr; + } + ip0 = ip1; + ip1 = ip1 + step; + if (ip1 > ilimit) goto _cleanup; + + curr = (U32)(ip0 - base); + hash0 = hash1; + } /* end inner search loop */ /* match found */ - ip += mLength; - anchor = ip; + assert(mLength); + ip0 += mLength; + anchor = ip0; - if (ip <= ilimit) { + if (ip0 <= ilimit) { /* Fill Table */ assert(base+curr+2 > istart); /* check base overflow */ hashTable[ZSTD_hashPtr(base+curr+2, hlog, mls)] = curr+2; /* here because curr+2 could be > iend-8 */ - hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base); + hashTable[ZSTD_hashPtr(ip0-2, hlog, mls)] = (U32)(ip0-2-base); /* check immediate repcode */ - while (ip <= ilimit) { - U32 const current2 = (U32)(ip-base); + while (ip0 <= ilimit) { + U32 const current2 = (U32)(ip0-base); U32 const repIndex2 = current2 - offset_2; const BYTE* repMatch2 = repIndex2 < prefixStartIndex ? dictBase - dictIndexDelta + repIndex2 : base + repIndex2; if ( ((U32)((prefixStartIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) - && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { + && (MEM_read32(repMatch2) == MEM_read32(ip0))) { const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend; - size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4; + size_t const repLength2 = ZSTD_count_2segments(ip0+4, repMatch2+4, iend, repEnd2, prefixStart) + 4; U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ - ZSTD_storeSeq(seqStore, 0, anchor, iend, STORE_REPCODE_1, repLength2); - hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2; - ip += repLength2; - anchor = ip; + ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, repLength2); + hashTable[ZSTD_hashPtr(ip0, hlog, mls)] = current2; + ip0 += repLength2; + anchor = ip0; continue; } break; } } + + /* Prepare for next iteration */ + assert(ip0 == anchor); + ip1 = ip0 + stepSize; } +_cleanup: /* save reps for next block */ - rep[0] = offset_1 ? offset_1 : offsetSaved; - rep[1] = offset_2 ? offset_2 : offsetSaved; + rep[0] = offset_1; + rep[1] = offset_2; /* Return the last literals size */ return (size_t)(iend - anchor); @@ -553,11 +689,10 @@ static size_t ZSTD_compressBlock_fast_extDict_generic( U32* const hashTable = ms->hashTable; U32 const hlog = cParams->hashLog; /* support stepSize of 0 */ - U32 const stepSize = cParams->targetLength + !(cParams->targetLength); + size_t const stepSize = cParams->targetLength + !(cParams->targetLength) + 1; const BYTE* const base = ms->window.base; const BYTE* const dictBase = ms->window.dictBase; const BYTE* const istart = (const BYTE*)src; - const BYTE* ip = istart; const BYTE* anchor = istart; const U32 endIndex = (U32)((size_t)(istart - base) + srcSize); const U32 lowLimit = ZSTD_getLowestMatchIndex(ms, endIndex, cParams->windowLog); @@ -570,6 +705,28 @@ static size_t ZSTD_compressBlock_fast_extDict_generic( const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - 8; U32 offset_1=rep[0], offset_2=rep[1]; + U32 offsetSaved1 = 0, offsetSaved2 = 0; + + const BYTE* ip0 = istart; + const BYTE* ip1; + const BYTE* ip2; + const BYTE* ip3; + U32 current0; + + + size_t hash0; /* hash for ip0 */ + size_t hash1; /* hash for ip1 */ + U32 idx; /* match idx for ip0 */ + const BYTE* idxBase; /* base pointer for idx */ + + U32 offcode; + const BYTE* match0; + size_t mLength; + const BYTE* matchEnd = 0; /* initialize to avoid warning, assert != 0 later */ + + size_t step; + const BYTE* nextStep; + const size_t kStepIncr = (1 << (kSearchStrength - 1)); (void)hasStep; /* not currently specialized on whether it's accelerated */ @@ -579,75 +736,202 @@ static size_t ZSTD_compressBlock_fast_extDict_generic( if (prefixStartIndex == dictStartIndex) return ZSTD_compressBlock_fast(ms, seqStore, rep, src, srcSize); - /* Search Loop */ - while (ip < ilimit) { /* < instead of <=, because (ip+1) */ - const size_t h = ZSTD_hashPtr(ip, hlog, mls); - const U32 matchIndex = hashTable[h]; - const BYTE* const matchBase = matchIndex < prefixStartIndex ? dictBase : base; - const BYTE* match = matchBase + matchIndex; - const U32 curr = (U32)(ip-base); - const U32 repIndex = curr + 1 - offset_1; - const BYTE* const repBase = repIndex < prefixStartIndex ? dictBase : base; - const BYTE* const repMatch = repBase + repIndex; - hashTable[h] = curr; /* update hash table */ - DEBUGLOG(7, "offset_1 = %u , curr = %u", offset_1, curr); - - if ( ( ((U32)((prefixStartIndex-1) - repIndex) >= 3) /* intentional underflow */ - & (offset_1 <= curr+1 - dictStartIndex) ) /* note: we are searching at curr+1 */ - && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { - const BYTE* const repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend; - size_t const rLength = ZSTD_count_2segments(ip+1 +4, repMatch +4, iend, repMatchEnd, prefixStart) + 4; - ip++; - ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, STORE_REPCODE_1, rLength); - ip += rLength; - anchor = ip; - } else { - if ( (matchIndex < dictStartIndex) || - (MEM_read32(match) != MEM_read32(ip)) ) { - assert(stepSize >= 1); - ip += ((ip-anchor) >> kSearchStrength) + stepSize; - continue; + { U32 const curr = (U32)(ip0 - base); + U32 const maxRep = curr - dictStartIndex; + if (offset_2 >= maxRep) offsetSaved2 = offset_2, offset_2 = 0; + if (offset_1 >= maxRep) offsetSaved1 = offset_1, offset_1 = 0; + } + + /* start each op */ +_start: /* Requires: ip0 */ + + step = stepSize; + nextStep = ip0 + kStepIncr; + + /* calculate positions, ip0 - anchor == 0, so we skip step calc */ + ip1 = ip0 + 1; + ip2 = ip0 + step; + ip3 = ip2 + 1; + + if (ip3 >= ilimit) { + goto _cleanup; + } + + hash0 = ZSTD_hashPtr(ip0, hlog, mls); + hash1 = ZSTD_hashPtr(ip1, hlog, mls); + + idx = hashTable[hash0]; + idxBase = idx < prefixStartIndex ? dictBase : base; + + do { + { /* load repcode match for ip[2] */ + U32 const current2 = (U32)(ip2 - base); + U32 const repIndex = current2 - offset_1; + const BYTE* const repBase = repIndex < prefixStartIndex ? dictBase : base; + U32 rval; + if ( ((U32)(prefixStartIndex - repIndex) >= 4) /* intentional underflow */ + & (offset_1 > 0) ) { + rval = MEM_read32(repBase + repIndex); + } else { + rval = MEM_read32(ip2) ^ 1; /* guaranteed to not match. */ } - { const BYTE* const matchEnd = matchIndex < prefixStartIndex ? dictEnd : iend; - const BYTE* const lowMatchPtr = matchIndex < prefixStartIndex ? dictStart : prefixStart; - U32 const offset = curr - matchIndex; - size_t mLength = ZSTD_count_2segments(ip+4, match+4, iend, matchEnd, prefixStart) + 4; - while (((ip>anchor) & (match>lowMatchPtr)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ - offset_2 = offset_1; offset_1 = offset; /* update offset history */ - ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, STORE_OFFSET(offset), mLength); - ip += mLength; - anchor = ip; + + /* write back hash table entry */ + current0 = (U32)(ip0 - base); + hashTable[hash0] = current0; + + /* check repcode at ip[2] */ + if (MEM_read32(ip2) == rval) { + ip0 = ip2; + match0 = repBase + repIndex; + matchEnd = repIndex < prefixStartIndex ? dictEnd : iend; + assert((match0 != prefixStart) & (match0 != dictStart)); + mLength = ip0[-1] == match0[-1]; + ip0 -= mLength; + match0 -= mLength; + offcode = REPCODE1_TO_OFFBASE; + mLength += 4; + goto _match; } } - if (ip <= ilimit) { - /* Fill Table */ - hashTable[ZSTD_hashPtr(base+curr+2, hlog, mls)] = curr+2; - hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base); - /* check immediate repcode */ - while (ip <= ilimit) { - U32 const current2 = (U32)(ip-base); - U32 const repIndex2 = current2 - offset_2; - const BYTE* const repMatch2 = repIndex2 < prefixStartIndex ? dictBase + repIndex2 : base + repIndex2; - if ( (((U32)((prefixStartIndex-1) - repIndex2) >= 3) & (offset_2 <= curr - dictStartIndex)) /* intentional overflow */ - && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { - const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend; - size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4; - { U32 const tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; } /* swap offset_2 <=> offset_1 */ - ZSTD_storeSeq(seqStore, 0 /*litlen*/, anchor, iend, STORE_REPCODE_1, repLength2); - hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2; - ip += repLength2; - anchor = ip; - continue; - } - break; - } } } + { /* load match for ip[0] */ + U32 const mval = idx >= dictStartIndex ? + MEM_read32(idxBase + idx) : + MEM_read32(ip0) ^ 1; /* guaranteed not to match */ + + /* check match at ip[0] */ + if (MEM_read32(ip0) == mval) { + /* found a match! */ + goto _offset; + } } + + /* lookup ip[1] */ + idx = hashTable[hash1]; + idxBase = idx < prefixStartIndex ? dictBase : base; + + /* hash ip[2] */ + hash0 = hash1; + hash1 = ZSTD_hashPtr(ip2, hlog, mls); + + /* advance to next positions */ + ip0 = ip1; + ip1 = ip2; + ip2 = ip3; + + /* write back hash table entry */ + current0 = (U32)(ip0 - base); + hashTable[hash0] = current0; + + { /* load match for ip[0] */ + U32 const mval = idx >= dictStartIndex ? + MEM_read32(idxBase + idx) : + MEM_read32(ip0) ^ 1; /* guaranteed not to match */ + + /* check match at ip[0] */ + if (MEM_read32(ip0) == mval) { + /* found a match! */ + goto _offset; + } } + + /* lookup ip[1] */ + idx = hashTable[hash1]; + idxBase = idx < prefixStartIndex ? dictBase : base; + + /* hash ip[2] */ + hash0 = hash1; + hash1 = ZSTD_hashPtr(ip2, hlog, mls); + + /* advance to next positions */ + ip0 = ip1; + ip1 = ip2; + ip2 = ip0 + step; + ip3 = ip1 + step; + + /* calculate step */ + if (ip2 >= nextStep) { + step++; + PREFETCH_L1(ip1 + 64); + PREFETCH_L1(ip1 + 128); + nextStep += kStepIncr; + } + } while (ip3 < ilimit); + +_cleanup: + /* Note that there are probably still a couple positions we could search. + * However, it seems to be a meaningful performance hit to try to search + * them. So let's not. */ + + /* If offset_1 started invalid (offsetSaved1 != 0) and became valid (offset_1 != 0), + * rotate saved offsets. See comment in ZSTD_compressBlock_fast_noDict for more context. */ + offsetSaved2 = ((offsetSaved1 != 0) && (offset_1 != 0)) ? offsetSaved1 : offsetSaved2; /* save reps for next block */ - rep[0] = offset_1; - rep[1] = offset_2; + rep[0] = offset_1 ? offset_1 : offsetSaved1; + rep[1] = offset_2 ? offset_2 : offsetSaved2; /* Return the last literals size */ return (size_t)(iend - anchor); + +_offset: /* Requires: ip0, idx, idxBase */ + + /* Compute the offset code. */ + { U32 const offset = current0 - idx; + const BYTE* const lowMatchPtr = idx < prefixStartIndex ? dictStart : prefixStart; + matchEnd = idx < prefixStartIndex ? dictEnd : iend; + match0 = idxBase + idx; + offset_2 = offset_1; + offset_1 = offset; + offcode = OFFSET_TO_OFFBASE(offset); + mLength = 4; + + /* Count the backwards match length. */ + while (((ip0>anchor) & (match0>lowMatchPtr)) && (ip0[-1] == match0[-1])) { + ip0--; + match0--; + mLength++; + } } + +_match: /* Requires: ip0, match0, offcode, matchEnd */ + + /* Count the forward length. */ + assert(matchEnd != 0); + mLength += ZSTD_count_2segments(ip0 + mLength, match0 + mLength, iend, matchEnd, prefixStart); + + ZSTD_storeSeq(seqStore, (size_t)(ip0 - anchor), anchor, iend, offcode, mLength); + + ip0 += mLength; + anchor = ip0; + + /* write next hash table entry */ + if (ip1 < ip0) { + hashTable[hash1] = (U32)(ip1 - base); + } + + /* Fill table and check for immediate repcode. */ + if (ip0 <= ilimit) { + /* Fill Table */ + assert(base+current0+2 > istart); /* check base overflow */ + hashTable[ZSTD_hashPtr(base+current0+2, hlog, mls)] = current0+2; /* here because current+2 could be > iend-8 */ + hashTable[ZSTD_hashPtr(ip0-2, hlog, mls)] = (U32)(ip0-2-base); + + while (ip0 <= ilimit) { + U32 const repIndex2 = (U32)(ip0-base) - offset_2; + const BYTE* const repMatch2 = repIndex2 < prefixStartIndex ? dictBase + repIndex2 : base + repIndex2; + if ( (((U32)((prefixStartIndex-1) - repIndex2) >= 3) & (offset_2 > 0)) /* intentional underflow */ + && (MEM_read32(repMatch2) == MEM_read32(ip0)) ) { + const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend; + size_t const repLength2 = ZSTD_count_2segments(ip0+4, repMatch2+4, iend, repEnd2, prefixStart) + 4; + { U32 const tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; } /* swap offset_2 <=> offset_1 */ + ZSTD_storeSeq(seqStore, 0 /*litlen*/, anchor, iend, REPCODE1_TO_OFFBASE, repLength2); + hashTable[ZSTD_hashPtr(ip0, hlog, mls)] = (U32)(ip0-base); + ip0 += repLength2; + anchor = ip0; + continue; + } + break; + } } + + goto _start; } ZSTD_GEN_FAST_FN(extDict, 4, 0) @@ -660,6 +944,7 @@ size_t ZSTD_compressBlock_fast_extDict( void const* src, size_t srcSize) { U32 const mls = ms->cParams.minMatch; + assert(ms->dictMatchState == NULL); switch(mls) { default: /* includes case 3 */ diff --git a/internal-complibs/zstd-1.5.2/compress/zstd_fast.h b/internal-complibs/zstd-1.5.5/compress/zstd_fast.h similarity index 90% rename from internal-complibs/zstd-1.5.2/compress/zstd_fast.h rename to internal-complibs/zstd-1.5.5/compress/zstd_fast.h index 0d4a0c10..9e4236b4 100644 --- a/internal-complibs/zstd-1.5.2/compress/zstd_fast.h +++ b/internal-complibs/zstd-1.5.5/compress/zstd_fast.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -19,7 +19,8 @@ extern "C" { #include "zstd_compress_internal.h" void ZSTD_fillHashTable(ZSTD_matchState_t* ms, - void const* end, ZSTD_dictTableLoadMethod_e dtlm); + void const* end, ZSTD_dictTableLoadMethod_e dtlm, + ZSTD_tableFillPurpose_e tfp); size_t ZSTD_compressBlock_fast( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); diff --git a/internal-complibs/zstd-1.5.2/compress/zstd_lazy.c b/internal-complibs/zstd-1.5.5/compress/zstd_lazy.c similarity index 78% rename from internal-complibs/zstd-1.5.2/compress/zstd_lazy.c rename to internal-complibs/zstd-1.5.5/compress/zstd_lazy.c index 2e38dcb4..5ba88e86 100644 --- a/internal-complibs/zstd-1.5.2/compress/zstd_lazy.c +++ b/internal-complibs/zstd-1.5.5/compress/zstd_lazy.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -10,6 +10,9 @@ #include "zstd_compress_internal.h" #include "zstd_lazy.h" +#include "../common/bits.h" /* ZSTD_countTrailingZeros64 */ + +#define kLazySkippingStep 8 /*-************************************* @@ -197,8 +200,8 @@ ZSTD_DUBT_findBetterDictMatch ( U32 matchIndex = dictMatchIndex + dictIndexDelta; if ( (4*(int)(matchLength-bestLength)) > (int)(ZSTD_highbit32(curr-matchIndex+1) - ZSTD_highbit32((U32)offsetPtr[0]+1)) ) { DEBUGLOG(9, "ZSTD_DUBT_findBetterDictMatch(%u) : found better match length %u -> %u and offsetCode %u -> %u (dictMatchIndex %u, matchIndex %u)", - curr, (U32)bestLength, (U32)matchLength, (U32)*offsetPtr, STORE_OFFSET(curr - matchIndex), dictMatchIndex, matchIndex); - bestLength = matchLength, *offsetPtr = STORE_OFFSET(curr - matchIndex); + curr, (U32)bestLength, (U32)matchLength, (U32)*offsetPtr, OFFSET_TO_OFFBASE(curr - matchIndex), dictMatchIndex, matchIndex); + bestLength = matchLength, *offsetPtr = OFFSET_TO_OFFBASE(curr - matchIndex); } if (ip+matchLength == iend) { /* reached end of input : ip[matchLength] is not valid, no way to know if it's larger or smaller than match */ break; /* drop, to guarantee consistency (miss a little bit of compression) */ @@ -218,7 +221,7 @@ ZSTD_DUBT_findBetterDictMatch ( } if (bestLength >= MINMATCH) { - U32 const mIndex = curr - (U32)STORED_OFFSET(*offsetPtr); (void)mIndex; + U32 const mIndex = curr - (U32)OFFBASE_TO_OFFSET(*offsetPtr); (void)mIndex; DEBUGLOG(8, "ZSTD_DUBT_findBetterDictMatch(%u) : found match of length %u and offsetCode %u (pos %u)", curr, (U32)bestLength, (U32)*offsetPtr, mIndex); } @@ -230,7 +233,7 @@ ZSTD_DUBT_findBetterDictMatch ( static size_t ZSTD_DUBT_findBestMatch(ZSTD_matchState_t* ms, const BYTE* const ip, const BYTE* const iend, - size_t* offsetPtr, + size_t* offBasePtr, U32 const mls, const ZSTD_dictMode_e dictMode) { @@ -327,8 +330,8 @@ ZSTD_DUBT_findBestMatch(ZSTD_matchState_t* ms, if (matchLength > bestLength) { if (matchLength > matchEndIdx - matchIndex) matchEndIdx = matchIndex + (U32)matchLength; - if ( (4*(int)(matchLength-bestLength)) > (int)(ZSTD_highbit32(curr-matchIndex+1) - ZSTD_highbit32((U32)offsetPtr[0]+1)) ) - bestLength = matchLength, *offsetPtr = STORE_OFFSET(curr - matchIndex); + if ( (4*(int)(matchLength-bestLength)) > (int)(ZSTD_highbit32(curr - matchIndex + 1) - ZSTD_highbit32((U32)*offBasePtr)) ) + bestLength = matchLength, *offBasePtr = OFFSET_TO_OFFBASE(curr - matchIndex); if (ip+matchLength == iend) { /* equal : no way to know if inf or sup */ if (dictMode == ZSTD_dictMatchState) { nbCompares = 0; /* in addition to avoiding checking any @@ -361,16 +364,16 @@ ZSTD_DUBT_findBestMatch(ZSTD_matchState_t* ms, if (dictMode == ZSTD_dictMatchState && nbCompares) { bestLength = ZSTD_DUBT_findBetterDictMatch( ms, ip, iend, - offsetPtr, bestLength, nbCompares, + offBasePtr, bestLength, nbCompares, mls, dictMode); } assert(matchEndIdx > curr+8); /* ensure nextToUpdate is increased */ ms->nextToUpdate = matchEndIdx - 8; /* skip repetitive patterns */ if (bestLength >= MINMATCH) { - U32 const mIndex = curr - (U32)STORED_OFFSET(*offsetPtr); (void)mIndex; + U32 const mIndex = curr - (U32)OFFBASE_TO_OFFSET(*offBasePtr); (void)mIndex; DEBUGLOG(8, "ZSTD_DUBT_findBestMatch(%u) : found match of length %u and offsetCode %u (pos %u)", - curr, (U32)bestLength, (U32)*offsetPtr, mIndex); + curr, (U32)bestLength, (U32)*offBasePtr, mIndex); } return bestLength; } @@ -381,14 +384,14 @@ ZSTD_DUBT_findBestMatch(ZSTD_matchState_t* ms, FORCE_INLINE_TEMPLATE size_t ZSTD_BtFindBestMatch( ZSTD_matchState_t* ms, const BYTE* const ip, const BYTE* const iLimit, - size_t* offsetPtr, + size_t* offBasePtr, const U32 mls /* template */, const ZSTD_dictMode_e dictMode) { DEBUGLOG(7, "ZSTD_BtFindBestMatch"); if (ip < ms->window.base + ms->nextToUpdate) return 0; /* skipped area */ ZSTD_updateDUBT(ms, ip, iLimit, mls); - return ZSTD_DUBT_findBestMatch(ms, ip, iLimit, offsetPtr, mls, dictMode); + return ZSTD_DUBT_findBestMatch(ms, ip, iLimit, offBasePtr, mls, dictMode); } /*********************************** @@ -561,7 +564,7 @@ size_t ZSTD_dedicatedDictSearch_lazy_search(size_t* offsetPtr, size_t ml, U32 nb /* save best solution */ if (currentMl > ml) { ml = currentMl; - *offsetPtr = STORE_OFFSET(curr - (matchIndex + ddsIndexDelta)); + *offsetPtr = OFFSET_TO_OFFBASE(curr - (matchIndex + ddsIndexDelta)); if (ip+currentMl == iLimit) { /* best possible, avoids read overflow on next attempt */ return ml; @@ -598,7 +601,7 @@ size_t ZSTD_dedicatedDictSearch_lazy_search(size_t* offsetPtr, size_t ml, U32 nb /* save best solution */ if (currentMl > ml) { ml = currentMl; - *offsetPtr = STORE_OFFSET(curr - (matchIndex + ddsIndexDelta)); + *offsetPtr = OFFSET_TO_OFFBASE(curr - (matchIndex + ddsIndexDelta)); if (ip+currentMl == iLimit) break; /* best possible, avoids read overflow on next attempt */ } } @@ -617,7 +620,7 @@ size_t ZSTD_dedicatedDictSearch_lazy_search(size_t* offsetPtr, size_t ml, U32 nb FORCE_INLINE_TEMPLATE U32 ZSTD_insertAndFindFirstIndex_internal( ZSTD_matchState_t* ms, const ZSTD_compressionParameters* const cParams, - const BYTE* ip, U32 const mls) + const BYTE* ip, U32 const mls, U32 const lazySkipping) { U32* const hashTable = ms->hashTable; const U32 hashLog = cParams->hashLog; @@ -632,6 +635,9 @@ FORCE_INLINE_TEMPLATE U32 ZSTD_insertAndFindFirstIndex_internal( NEXT_IN_CHAIN(idx, chainMask) = hashTable[h]; hashTable[h] = idx; idx++; + /* Stop inserting every position when in the lazy skipping mode. */ + if (lazySkipping) + break; } ms->nextToUpdate = target; @@ -640,7 +646,7 @@ FORCE_INLINE_TEMPLATE U32 ZSTD_insertAndFindFirstIndex_internal( U32 ZSTD_insertAndFindFirstIndex(ZSTD_matchState_t* ms, const BYTE* ip) { const ZSTD_compressionParameters* const cParams = &ms->cParams; - return ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, ms->cParams.minMatch); + return ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, ms->cParams.minMatch, /* lazySkipping*/ 0); } /* inlining is important to hardwire a hot branch (template emulation) */ @@ -684,14 +690,15 @@ size_t ZSTD_HcFindBestMatch( } /* HC4 match finder */ - matchIndex = ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, mls); + matchIndex = ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, mls, ms->lazySkipping); for ( ; (matchIndex>=lowLimit) & (nbAttempts>0) ; nbAttempts--) { size_t currentMl=0; if ((dictMode != ZSTD_extDict) || matchIndex >= dictLimit) { const BYTE* const match = base + matchIndex; assert(matchIndex >= dictLimit); /* ensures this is true if dictMode != ZSTD_extDict */ - if (match[ml] == ip[ml]) /* potentially better */ + /* read 4B starting from (match + ml + 1 - sizeof(U32)) */ + if (MEM_read32(match + ml - 3) == MEM_read32(ip + ml - 3)) /* potentially better */ currentMl = ZSTD_count(ip, match, iLimit); } else { const BYTE* const match = dictBase + matchIndex; @@ -703,7 +710,7 @@ size_t ZSTD_HcFindBestMatch( /* save best solution */ if (currentMl > ml) { ml = currentMl; - *offsetPtr = STORE_OFFSET(curr - matchIndex); + *offsetPtr = OFFSET_TO_OFFBASE(curr - matchIndex); if (ip+currentMl == iLimit) break; /* best possible, avoids read overflow on next attempt */ } @@ -739,7 +746,7 @@ size_t ZSTD_HcFindBestMatch( if (currentMl > ml) { ml = currentMl; assert(curr > matchIndex + dmsIndexDelta); - *offsetPtr = STORE_OFFSET(curr - (matchIndex + dmsIndexDelta)); + *offsetPtr = OFFSET_TO_OFFBASE(curr - (matchIndex + dmsIndexDelta)); if (ip+currentMl == iLimit) break; /* best possible, avoids read overflow on next attempt */ } @@ -756,8 +763,6 @@ size_t ZSTD_HcFindBestMatch( * (SIMD) Row-based matchfinder ***********************************/ /* Constants for row-based hash */ -#define ZSTD_ROW_HASH_TAG_OFFSET 16 /* byte offset of hashes in the match state's tagTable from the beginning of a row */ -#define ZSTD_ROW_HASH_TAG_BITS 8 /* nb bits to use for the tag */ #define ZSTD_ROW_HASH_TAG_MASK ((1u << ZSTD_ROW_HASH_TAG_BITS) - 1) #define ZSTD_ROW_HASH_MAX_ENTRIES 64 /* absolute maximum number of entries per row, for all configurations */ @@ -769,73 +774,19 @@ typedef U64 ZSTD_VecMask; /* Clarifies when we are interacting with a U64 repr * Starting from the LSB, returns the idx of the next non-zero bit. * Basically counting the nb of trailing zeroes. */ -static U32 ZSTD_VecMask_next(ZSTD_VecMask val) { - assert(val != 0); -# if defined(_MSC_VER) && defined(_WIN64) - if (val != 0) { - unsigned long r; - _BitScanForward64(&r, val); - return (U32)(r); - } else { - /* Should not reach this code path */ - __assume(0); - } -# elif (defined(__GNUC__) && ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4)))) - if (sizeof(size_t) == 4) { - U32 mostSignificantWord = (U32)(val >> 32); - U32 leastSignificantWord = (U32)val; - if (leastSignificantWord == 0) { - return 32 + (U32)__builtin_ctz(mostSignificantWord); - } else { - return (U32)__builtin_ctz(leastSignificantWord); - } - } else { - return (U32)__builtin_ctzll(val); - } -# else - /* Software ctz version: http://aggregate.org/MAGIC/#Trailing%20Zero%20Count - * and: https://stackoverflow.com/questions/2709430/count-number-of-bits-in-a-64-bit-long-big-integer - */ - val = ~val & (val - 1ULL); /* Lowest set bit mask */ - val = val - ((val >> 1) & 0x5555555555555555); - val = (val & 0x3333333333333333ULL) + ((val >> 2) & 0x3333333333333333ULL); - return (U32)((((val + (val >> 4)) & 0xF0F0F0F0F0F0F0FULL) * 0x101010101010101ULL) >> 56); -# endif -} - -/* ZSTD_rotateRight_*(): - * Rotates a bitfield to the right by "count" bits. - * https://en.wikipedia.org/w/index.php?title=Circular_shift&oldid=991635599#Implementing_circular_shifts - */ -FORCE_INLINE_TEMPLATE -U64 ZSTD_rotateRight_U64(U64 const value, U32 count) { - assert(count < 64); - count &= 0x3F; /* for fickle pattern recognition */ - return (value >> count) | (U64)(value << ((0U - count) & 0x3F)); -} - -FORCE_INLINE_TEMPLATE -U32 ZSTD_rotateRight_U32(U32 const value, U32 count) { - assert(count < 32); - count &= 0x1F; /* for fickle pattern recognition */ - return (value >> count) | (U32)(value << ((0U - count) & 0x1F)); -} - -FORCE_INLINE_TEMPLATE -U16 ZSTD_rotateRight_U16(U16 const value, U32 count) { - assert(count < 16); - count &= 0x0F; /* for fickle pattern recognition */ - return (value >> count) | (U16)(value << ((0U - count) & 0x0F)); +MEM_STATIC U32 ZSTD_VecMask_next(ZSTD_VecMask val) { + return ZSTD_countTrailingZeros64(val); } /* ZSTD_row_nextIndex(): * Returns the next index to insert at within a tagTable row, and updates the "head" - * value to reflect the update. Essentially cycles backwards from [0, {entries per row}) + * value to reflect the update. Essentially cycles backwards from [1, {entries per row}) */ FORCE_INLINE_TEMPLATE U32 ZSTD_row_nextIndex(BYTE* const tagRow, U32 const rowMask) { - U32 const next = (*tagRow - 1) & rowMask; - *tagRow = (BYTE)next; - return next; + U32 next = (*tagRow-1) & rowMask; + next += (next == 0) ? rowMask : 0; /* skip first position */ + *tagRow = (BYTE)next; + return next; } /* ZSTD_isAligned(): @@ -849,7 +800,7 @@ MEM_STATIC int ZSTD_isAligned(void const* ptr, size_t align) { /* ZSTD_row_prefetch(): * Performs prefetching for the hashTable and tagTable at a given row. */ -FORCE_INLINE_TEMPLATE void ZSTD_row_prefetch(U32 const* hashTable, U16 const* tagTable, U32 const relRow, U32 const rowLog) { +FORCE_INLINE_TEMPLATE void ZSTD_row_prefetch(U32 const* hashTable, BYTE const* tagTable, U32 const relRow, U32 const rowLog) { PREFETCH_L1(hashTable + relRow); if (rowLog >= 5) { PREFETCH_L1(hashTable + relRow + 16); @@ -873,13 +824,13 @@ FORCE_INLINE_TEMPLATE void ZSTD_row_fillHashCache(ZSTD_matchState_t* ms, const B U32 idx, const BYTE* const iLimit) { U32 const* const hashTable = ms->hashTable; - U16 const* const tagTable = ms->tagTable; + BYTE const* const tagTable = ms->tagTable; U32 const hashLog = ms->rowHashLog; U32 const maxElemsToPrefetch = (base + idx) > iLimit ? 0 : (U32)(iLimit - (base + idx) + 1); U32 const lim = idx + MIN(ZSTD_ROW_HASH_CACHE_SIZE, maxElemsToPrefetch); for (; idx < lim; ++idx) { - U32 const hash = (U32)ZSTD_hashPtr(base + idx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls); + U32 const hash = (U32)ZSTD_hashPtrSalted(base + idx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls, ms->hashSalt); U32 const row = (hash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog; ZSTD_row_prefetch(hashTable, tagTable, row, rowLog); ms->hashCache[idx & ZSTD_ROW_HASH_CACHE_MASK] = hash; @@ -895,11 +846,12 @@ FORCE_INLINE_TEMPLATE void ZSTD_row_fillHashCache(ZSTD_matchState_t* ms, const B * base + idx + ZSTD_ROW_HASH_CACHE_SIZE. Also prefetches the appropriate rows from hashTable and tagTable. */ FORCE_INLINE_TEMPLATE U32 ZSTD_row_nextCachedHash(U32* cache, U32 const* hashTable, - U16 const* tagTable, BYTE const* base, + BYTE const* tagTable, BYTE const* base, U32 idx, U32 const hashLog, - U32 const rowLog, U32 const mls) + U32 const rowLog, U32 const mls, + U64 const hashSalt) { - U32 const newHash = (U32)ZSTD_hashPtr(base+idx+ZSTD_ROW_HASH_CACHE_SIZE, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls); + U32 const newHash = (U32)ZSTD_hashPtrSalted(base+idx+ZSTD_ROW_HASH_CACHE_SIZE, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls, hashSalt); U32 const row = (newHash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog; ZSTD_row_prefetch(hashTable, tagTable, row, rowLog); { U32 const hash = cache[idx & ZSTD_ROW_HASH_CACHE_MASK]; @@ -917,22 +869,21 @@ FORCE_INLINE_TEMPLATE void ZSTD_row_update_internalImpl(ZSTD_matchState_t* ms, U32 const rowMask, U32 const useCache) { U32* const hashTable = ms->hashTable; - U16* const tagTable = ms->tagTable; + BYTE* const tagTable = ms->tagTable; U32 const hashLog = ms->rowHashLog; const BYTE* const base = ms->window.base; DEBUGLOG(6, "ZSTD_row_update_internalImpl(): updateStartIdx=%u, updateEndIdx=%u", updateStartIdx, updateEndIdx); for (; updateStartIdx < updateEndIdx; ++updateStartIdx) { - U32 const hash = useCache ? ZSTD_row_nextCachedHash(ms->hashCache, hashTable, tagTable, base, updateStartIdx, hashLog, rowLog, mls) - : (U32)ZSTD_hashPtr(base + updateStartIdx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls); + U32 const hash = useCache ? ZSTD_row_nextCachedHash(ms->hashCache, hashTable, tagTable, base, updateStartIdx, hashLog, rowLog, mls, ms->hashSalt) + : (U32)ZSTD_hashPtrSalted(base + updateStartIdx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls, ms->hashSalt); U32 const relRow = (hash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog; U32* const row = hashTable + relRow; - BYTE* tagRow = (BYTE*)(tagTable + relRow); /* Though tagTable is laid out as a table of U16, each tag is only 1 byte. - Explicit cast allows us to get exact desired position within each row */ + BYTE* tagRow = tagTable + relRow; U32 const pos = ZSTD_row_nextIndex(tagRow, rowMask); - assert(hash == ZSTD_hashPtr(base + updateStartIdx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls)); - ((BYTE*)tagRow)[pos + ZSTD_ROW_HASH_TAG_OFFSET] = hash & ZSTD_ROW_HASH_TAG_MASK; + assert(hash == ZSTD_hashPtrSalted(base + updateStartIdx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls, ms->hashSalt)); + tagRow[pos] = hash & ZSTD_ROW_HASH_TAG_MASK; row[pos] = updateStartIdx; } } @@ -980,7 +931,35 @@ void ZSTD_row_update(ZSTD_matchState_t* const ms, const BYTE* ip) { const U32 mls = MIN(ms->cParams.minMatch, 6 /* mls caps out at 6 */); DEBUGLOG(5, "ZSTD_row_update(), rowLog=%u", rowLog); - ZSTD_row_update_internal(ms, ip, mls, rowLog, rowMask, 0 /* dont use cache */); + ZSTD_row_update_internal(ms, ip, mls, rowLog, rowMask, 0 /* don't use cache */); +} + +/* Returns the mask width of bits group of which will be set to 1. Given not all + * architectures have easy movemask instruction, this helps to iterate over + * groups of bits easier and faster. + */ +FORCE_INLINE_TEMPLATE U32 +ZSTD_row_matchMaskGroupWidth(const U32 rowEntries) +{ + assert((rowEntries == 16) || (rowEntries == 32) || rowEntries == 64); + assert(rowEntries <= ZSTD_ROW_HASH_MAX_ENTRIES); + (void)rowEntries; +#if defined(ZSTD_ARCH_ARM_NEON) + /* NEON path only works for little endian */ + if (!MEM_isLittleEndian()) { + return 1; + } + if (rowEntries == 16) { + return 4; + } + if (rowEntries == 32) { + return 2; + } + if (rowEntries == 64) { + return 1; + } +#endif + return 1; } #if defined(ZSTD_ARCH_X86_SSE2) @@ -1003,71 +982,82 @@ ZSTD_row_getSSEMask(int nbChunks, const BYTE* const src, const BYTE tag, const U } #endif -/* Returns a ZSTD_VecMask (U32) that has the nth bit set to 1 if the newly-computed "tag" matches - * the hash at the nth position in a row of the tagTable. - * Each row is a circular buffer beginning at the value of "head". So we must rotate the "matches" bitfield - * to match up with the actual layout of the entries within the hashTable */ +#if defined(ZSTD_ARCH_ARM_NEON) +FORCE_INLINE_TEMPLATE ZSTD_VecMask +ZSTD_row_getNEONMask(const U32 rowEntries, const BYTE* const src, const BYTE tag, const U32 headGrouped) +{ + assert((rowEntries == 16) || (rowEntries == 32) || rowEntries == 64); + if (rowEntries == 16) { + /* vshrn_n_u16 shifts by 4 every u16 and narrows to 8 lower bits. + * After that groups of 4 bits represent the equalMask. We lower + * all bits except the highest in these groups by doing AND with + * 0x88 = 0b10001000. + */ + const uint8x16_t chunk = vld1q_u8(src); + const uint16x8_t equalMask = vreinterpretq_u16_u8(vceqq_u8(chunk, vdupq_n_u8(tag))); + const uint8x8_t res = vshrn_n_u16(equalMask, 4); + const U64 matches = vget_lane_u64(vreinterpret_u64_u8(res), 0); + return ZSTD_rotateRight_U64(matches, headGrouped) & 0x8888888888888888ull; + } else if (rowEntries == 32) { + /* Same idea as with rowEntries == 16 but doing AND with + * 0x55 = 0b01010101. + */ + const uint16x8x2_t chunk = vld2q_u16((const uint16_t*)(const void*)src); + const uint8x16_t chunk0 = vreinterpretq_u8_u16(chunk.val[0]); + const uint8x16_t chunk1 = vreinterpretq_u8_u16(chunk.val[1]); + const uint8x16_t dup = vdupq_n_u8(tag); + const uint8x8_t t0 = vshrn_n_u16(vreinterpretq_u16_u8(vceqq_u8(chunk0, dup)), 6); + const uint8x8_t t1 = vshrn_n_u16(vreinterpretq_u16_u8(vceqq_u8(chunk1, dup)), 6); + const uint8x8_t res = vsli_n_u8(t0, t1, 4); + const U64 matches = vget_lane_u64(vreinterpret_u64_u8(res), 0) ; + return ZSTD_rotateRight_U64(matches, headGrouped) & 0x5555555555555555ull; + } else { /* rowEntries == 64 */ + const uint8x16x4_t chunk = vld4q_u8(src); + const uint8x16_t dup = vdupq_n_u8(tag); + const uint8x16_t cmp0 = vceqq_u8(chunk.val[0], dup); + const uint8x16_t cmp1 = vceqq_u8(chunk.val[1], dup); + const uint8x16_t cmp2 = vceqq_u8(chunk.val[2], dup); + const uint8x16_t cmp3 = vceqq_u8(chunk.val[3], dup); + + const uint8x16_t t0 = vsriq_n_u8(cmp1, cmp0, 1); + const uint8x16_t t1 = vsriq_n_u8(cmp3, cmp2, 1); + const uint8x16_t t2 = vsriq_n_u8(t1, t0, 2); + const uint8x16_t t3 = vsriq_n_u8(t2, t2, 4); + const uint8x8_t t4 = vshrn_n_u16(vreinterpretq_u16_u8(t3), 4); + const U64 matches = vget_lane_u64(vreinterpret_u64_u8(t4), 0); + return ZSTD_rotateRight_U64(matches, headGrouped); + } +} +#endif + +/* Returns a ZSTD_VecMask (U64) that has the nth group (determined by + * ZSTD_row_matchMaskGroupWidth) of bits set to 1 if the newly-computed "tag" + * matches the hash at the nth position in a row of the tagTable. + * Each row is a circular buffer beginning at the value of "headGrouped". So we + * must rotate the "matches" bitfield to match up with the actual layout of the + * entries within the hashTable */ FORCE_INLINE_TEMPLATE ZSTD_VecMask -ZSTD_row_getMatchMask(const BYTE* const tagRow, const BYTE tag, const U32 head, const U32 rowEntries) +ZSTD_row_getMatchMask(const BYTE* const tagRow, const BYTE tag, const U32 headGrouped, const U32 rowEntries) { - const BYTE* const src = tagRow + ZSTD_ROW_HASH_TAG_OFFSET; + const BYTE* const src = tagRow; assert((rowEntries == 16) || (rowEntries == 32) || rowEntries == 64); assert(rowEntries <= ZSTD_ROW_HASH_MAX_ENTRIES); + assert(ZSTD_row_matchMaskGroupWidth(rowEntries) * rowEntries <= sizeof(ZSTD_VecMask) * 8); #if defined(ZSTD_ARCH_X86_SSE2) - return ZSTD_row_getSSEMask(rowEntries / 16, src, tag, head); + return ZSTD_row_getSSEMask(rowEntries / 16, src, tag, headGrouped); #else /* SW or NEON-LE */ # if defined(ZSTD_ARCH_ARM_NEON) /* This NEON path only works for little endian - otherwise use SWAR below */ if (MEM_isLittleEndian()) { - if (rowEntries == 16) { - const uint8x16_t chunk = vld1q_u8(src); - const uint16x8_t equalMask = vreinterpretq_u16_u8(vceqq_u8(chunk, vdupq_n_u8(tag))); - const uint16x8_t t0 = vshlq_n_u16(equalMask, 7); - const uint32x4_t t1 = vreinterpretq_u32_u16(vsriq_n_u16(t0, t0, 14)); - const uint64x2_t t2 = vreinterpretq_u64_u32(vshrq_n_u32(t1, 14)); - const uint8x16_t t3 = vreinterpretq_u8_u64(vsraq_n_u64(t2, t2, 28)); - const U16 hi = (U16)vgetq_lane_u8(t3, 8); - const U16 lo = (U16)vgetq_lane_u8(t3, 0); - return ZSTD_rotateRight_U16((hi << 8) | lo, head); - } else if (rowEntries == 32) { - const uint16x8x2_t chunk = vld2q_u16((const U16*)(const void*)src); - const uint8x16_t chunk0 = vreinterpretq_u8_u16(chunk.val[0]); - const uint8x16_t chunk1 = vreinterpretq_u8_u16(chunk.val[1]); - const uint8x16_t equalMask0 = vceqq_u8(chunk0, vdupq_n_u8(tag)); - const uint8x16_t equalMask1 = vceqq_u8(chunk1, vdupq_n_u8(tag)); - const int8x8_t pack0 = vqmovn_s16(vreinterpretq_s16_u8(equalMask0)); - const int8x8_t pack1 = vqmovn_s16(vreinterpretq_s16_u8(equalMask1)); - const uint8x8_t t0 = vreinterpret_u8_s8(pack0); - const uint8x8_t t1 = vreinterpret_u8_s8(pack1); - const uint8x8_t t2 = vsri_n_u8(t1, t0, 2); - const uint8x8x2_t t3 = vuzp_u8(t2, t0); - const uint8x8_t t4 = vsri_n_u8(t3.val[1], t3.val[0], 4); - const U32 matches = vget_lane_u32(vreinterpret_u32_u8(t4), 0); - return ZSTD_rotateRight_U32(matches, head); - } else { /* rowEntries == 64 */ - const uint8x16x4_t chunk = vld4q_u8(src); - const uint8x16_t dup = vdupq_n_u8(tag); - const uint8x16_t cmp0 = vceqq_u8(chunk.val[0], dup); - const uint8x16_t cmp1 = vceqq_u8(chunk.val[1], dup); - const uint8x16_t cmp2 = vceqq_u8(chunk.val[2], dup); - const uint8x16_t cmp3 = vceqq_u8(chunk.val[3], dup); - - const uint8x16_t t0 = vsriq_n_u8(cmp1, cmp0, 1); - const uint8x16_t t1 = vsriq_n_u8(cmp3, cmp2, 1); - const uint8x16_t t2 = vsriq_n_u8(t1, t0, 2); - const uint8x16_t t3 = vsriq_n_u8(t2, t2, 4); - const uint8x8_t t4 = vshrn_n_u16(vreinterpretq_u16_u8(t3), 4); - const U64 matches = vget_lane_u64(vreinterpret_u64_u8(t4), 0); - return ZSTD_rotateRight_U64(matches, head); - } + return ZSTD_row_getNEONMask(rowEntries, src, tag, headGrouped); } # endif /* ZSTD_ARCH_ARM_NEON */ /* SWAR */ - { const size_t chunkSize = sizeof(size_t); + { const int chunkSize = sizeof(size_t); const size_t shiftAmount = ((chunkSize * 8) - chunkSize); const size_t xFF = ~((size_t)0); const size_t x01 = xFF / 0xFF; @@ -1100,11 +1090,11 @@ ZSTD_row_getMatchMask(const BYTE* const tagRow, const BYTE tag, const U32 head, } matches = ~matches; if (rowEntries == 16) { - return ZSTD_rotateRight_U16((U16)matches, head); + return ZSTD_rotateRight_U16((U16)matches, headGrouped); } else if (rowEntries == 32) { - return ZSTD_rotateRight_U32((U32)matches, head); + return ZSTD_rotateRight_U32((U32)matches, headGrouped); } else { - return ZSTD_rotateRight_U64((U64)matches, head); + return ZSTD_rotateRight_U64((U64)matches, headGrouped); } } #endif @@ -1134,7 +1124,7 @@ size_t ZSTD_RowFindBestMatch( const U32 rowLog) { U32* const hashTable = ms->hashTable; - U16* const tagTable = ms->tagTable; + BYTE* const tagTable = ms->tagTable; U32* const hashCache = ms->hashCache; const U32 hashLog = ms->rowHashLog; const ZSTD_compressionParameters* const cParams = &ms->cParams; @@ -1152,8 +1142,11 @@ size_t ZSTD_RowFindBestMatch( const U32 rowEntries = (1U << rowLog); const U32 rowMask = rowEntries - 1; const U32 cappedSearchLog = MIN(cParams->searchLog, rowLog); /* nb of searches is capped at nb entries per row */ + const U32 groupWidth = ZSTD_row_matchMaskGroupWidth(rowEntries); + const U64 hashSalt = ms->hashSalt; U32 nbAttempts = 1U << cappedSearchLog; size_t ml=4-1; + U32 hash; /* DMS/DDS variables that may be referenced laster */ const ZSTD_matchState_t* const dms = ms->dictMatchState; @@ -1177,7 +1170,7 @@ size_t ZSTD_RowFindBestMatch( if (dictMode == ZSTD_dictMatchState) { /* Prefetch DMS rows */ U32* const dmsHashTable = dms->hashTable; - U16* const dmsTagTable = dms->tagTable; + BYTE* const dmsTagTable = dms->tagTable; U32 const dmsHash = (U32)ZSTD_hashPtr(ip, dms->rowHashLog + ZSTD_ROW_HASH_TAG_BITS, mls); U32 const dmsRelRow = (dmsHash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog; dmsTag = dmsHash & ZSTD_ROW_HASH_TAG_MASK; @@ -1187,23 +1180,34 @@ size_t ZSTD_RowFindBestMatch( } /* Update the hashTable and tagTable up to (but not including) ip */ - ZSTD_row_update_internal(ms, ip, mls, rowLog, rowMask, 1 /* useCache */); + if (!ms->lazySkipping) { + ZSTD_row_update_internal(ms, ip, mls, rowLog, rowMask, 1 /* useCache */); + hash = ZSTD_row_nextCachedHash(hashCache, hashTable, tagTable, base, curr, hashLog, rowLog, mls, hashSalt); + } else { + /* Stop inserting every position when in the lazy skipping mode. + * The hash cache is also not kept up to date in this mode. + */ + hash = (U32)ZSTD_hashPtrSalted(ip, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls, hashSalt); + ms->nextToUpdate = curr; + } + ms->hashSaltEntropy += hash; /* collect salt entropy */ + { /* Get the hash for ip, compute the appropriate row */ - U32 const hash = ZSTD_row_nextCachedHash(hashCache, hashTable, tagTable, base, curr, hashLog, rowLog, mls); U32 const relRow = (hash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog; U32 const tag = hash & ZSTD_ROW_HASH_TAG_MASK; U32* const row = hashTable + relRow; BYTE* tagRow = (BYTE*)(tagTable + relRow); - U32 const head = *tagRow & rowMask; + U32 const headGrouped = (*tagRow & rowMask) * groupWidth; U32 matchBuffer[ZSTD_ROW_HASH_MAX_ENTRIES]; size_t numMatches = 0; size_t currMatch = 0; - ZSTD_VecMask matches = ZSTD_row_getMatchMask(tagRow, (BYTE)tag, head, rowEntries); + ZSTD_VecMask matches = ZSTD_row_getMatchMask(tagRow, (BYTE)tag, headGrouped, rowEntries); /* Cycle through the matches and prefetch */ - for (; (matches > 0) && (nbAttempts > 0); --nbAttempts, matches &= (matches - 1)) { - U32 const matchPos = (head + ZSTD_VecMask_next(matches)) & rowMask; + for (; (matches > 0) && (nbAttempts > 0); matches &= (matches - 1)) { + U32 const matchPos = ((headGrouped + ZSTD_VecMask_next(matches)) / groupWidth) & rowMask; U32 const matchIndex = row[matchPos]; + if(matchPos == 0) continue; assert(numMatches < rowEntries); if (matchIndex < lowLimit) break; @@ -1213,13 +1217,14 @@ size_t ZSTD_RowFindBestMatch( PREFETCH_L1(dictBase + matchIndex); } matchBuffer[numMatches++] = matchIndex; + --nbAttempts; } /* Speed opt: insert current byte into hashtable too. This allows us to avoid one iteration of the loop in ZSTD_row_update_internal() at the next search. */ { U32 const pos = ZSTD_row_nextIndex(tagRow, rowMask); - tagRow[pos + ZSTD_ROW_HASH_TAG_OFFSET] = (BYTE)tag; + tagRow[pos] = (BYTE)tag; row[pos] = ms->nextToUpdate++; } @@ -1233,7 +1238,8 @@ size_t ZSTD_RowFindBestMatch( if ((dictMode != ZSTD_extDict) || matchIndex >= dictLimit) { const BYTE* const match = base + matchIndex; assert(matchIndex >= dictLimit); /* ensures this is true if dictMode != ZSTD_extDict */ - if (match[ml] == ip[ml]) /* potentially better */ + /* read 4B starting from (match + ml + 1 - sizeof(U32)) */ + if (MEM_read32(match + ml - 3) == MEM_read32(ip + ml - 3)) /* potentially better */ currentMl = ZSTD_count(ip, match, iLimit); } else { const BYTE* const match = dictBase + matchIndex; @@ -1245,7 +1251,7 @@ size_t ZSTD_RowFindBestMatch( /* Save best solution */ if (currentMl > ml) { ml = currentMl; - *offsetPtr = STORE_OFFSET(curr - matchIndex); + *offsetPtr = OFFSET_TO_OFFBASE(curr - matchIndex); if (ip+currentMl == iLimit) break; /* best possible, avoids read overflow on next attempt */ } } @@ -1263,19 +1269,21 @@ size_t ZSTD_RowFindBestMatch( const U32 dmsSize = (U32)(dmsEnd - dmsBase); const U32 dmsIndexDelta = dictLimit - dmsSize; - { U32 const head = *dmsTagRow & rowMask; + { U32 const headGrouped = (*dmsTagRow & rowMask) * groupWidth; U32 matchBuffer[ZSTD_ROW_HASH_MAX_ENTRIES]; size_t numMatches = 0; size_t currMatch = 0; - ZSTD_VecMask matches = ZSTD_row_getMatchMask(dmsTagRow, (BYTE)dmsTag, head, rowEntries); + ZSTD_VecMask matches = ZSTD_row_getMatchMask(dmsTagRow, (BYTE)dmsTag, headGrouped, rowEntries); - for (; (matches > 0) && (nbAttempts > 0); --nbAttempts, matches &= (matches - 1)) { - U32 const matchPos = (head + ZSTD_VecMask_next(matches)) & rowMask; + for (; (matches > 0) && (nbAttempts > 0); matches &= (matches - 1)) { + U32 const matchPos = ((headGrouped + ZSTD_VecMask_next(matches)) / groupWidth) & rowMask; U32 const matchIndex = dmsRow[matchPos]; + if(matchPos == 0) continue; if (matchIndex < dmsLowestIndex) break; PREFETCH_L1(dmsBase + matchIndex); matchBuffer[numMatches++] = matchIndex; + --nbAttempts; } /* Return the longest match */ @@ -1294,7 +1302,7 @@ size_t ZSTD_RowFindBestMatch( if (currentMl > ml) { ml = currentMl; assert(curr > matchIndex + dmsIndexDelta); - *offsetPtr = STORE_OFFSET(curr - (matchIndex + dmsIndexDelta)); + *offsetPtr = OFFSET_TO_OFFBASE(curr - (matchIndex + dmsIndexDelta)); if (ip+currentMl == iLimit) break; } } @@ -1304,14 +1312,10 @@ size_t ZSTD_RowFindBestMatch( } -typedef size_t (*searchMax_f)( - ZSTD_matchState_t* ms, - const BYTE* ip, const BYTE* iLimit, size_t* offsetPtr); - /** - * This struct contains the functions necessary for lazy to search. - * Currently, that is only searchMax. However, it is still valuable to have the - * VTable because this makes it easier to add more functions to the VTable later. + * Generate search functions templated on (dictMode, mls, rowLog). + * These functions are outlined for code size & compilation time. + * ZSTD_searchMax() dispatches to the correct implementation function. * * TODO: The start of the search function involves loading and calculating a * bunch of constants from the ZSTD_matchState_t. These computations could be @@ -1329,25 +1333,25 @@ typedef size_t (*searchMax_f)( * the single segment loop. It should go in searchMax instead of its own * function to avoid having multiple virtual function calls per search. */ -typedef struct { - searchMax_f searchMax; -} ZSTD_LazyVTable; -#define GEN_ZSTD_BT_VTABLE(dictMode, mls) \ - static size_t ZSTD_BtFindBestMatch_##dictMode##_##mls( \ - ZSTD_matchState_t* ms, \ - const BYTE* ip, const BYTE* const iLimit, \ - size_t* offsetPtr) \ - { \ - assert(MAX(4, MIN(6, ms->cParams.minMatch)) == mls); \ - return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, mls, ZSTD_##dictMode); \ - } \ - static const ZSTD_LazyVTable ZSTD_BtVTable_##dictMode##_##mls = { \ - ZSTD_BtFindBestMatch_##dictMode##_##mls \ - }; +#define ZSTD_BT_SEARCH_FN(dictMode, mls) ZSTD_BtFindBestMatch_##dictMode##_##mls +#define ZSTD_HC_SEARCH_FN(dictMode, mls) ZSTD_HcFindBestMatch_##dictMode##_##mls +#define ZSTD_ROW_SEARCH_FN(dictMode, mls, rowLog) ZSTD_RowFindBestMatch_##dictMode##_##mls##_##rowLog -#define GEN_ZSTD_HC_VTABLE(dictMode, mls) \ - static size_t ZSTD_HcFindBestMatch_##dictMode##_##mls( \ +#define ZSTD_SEARCH_FN_ATTRS FORCE_NOINLINE + +#define GEN_ZSTD_BT_SEARCH_FN(dictMode, mls) \ + ZSTD_SEARCH_FN_ATTRS size_t ZSTD_BT_SEARCH_FN(dictMode, mls)( \ + ZSTD_matchState_t* ms, \ + const BYTE* ip, const BYTE* const iLimit, \ + size_t* offBasePtr) \ + { \ + assert(MAX(4, MIN(6, ms->cParams.minMatch)) == mls); \ + return ZSTD_BtFindBestMatch(ms, ip, iLimit, offBasePtr, mls, ZSTD_##dictMode); \ + } \ + +#define GEN_ZSTD_HC_SEARCH_FN(dictMode, mls) \ + ZSTD_SEARCH_FN_ATTRS size_t ZSTD_HC_SEARCH_FN(dictMode, mls)( \ ZSTD_matchState_t* ms, \ const BYTE* ip, const BYTE* const iLimit, \ size_t* offsetPtr) \ @@ -1355,12 +1359,9 @@ typedef struct { assert(MAX(4, MIN(6, ms->cParams.minMatch)) == mls); \ return ZSTD_HcFindBestMatch(ms, ip, iLimit, offsetPtr, mls, ZSTD_##dictMode); \ } \ - static const ZSTD_LazyVTable ZSTD_HcVTable_##dictMode##_##mls = { \ - ZSTD_HcFindBestMatch_##dictMode##_##mls \ - }; -#define GEN_ZSTD_ROW_VTABLE(dictMode, mls, rowLog) \ - static size_t ZSTD_RowFindBestMatch_##dictMode##_##mls##_##rowLog( \ +#define GEN_ZSTD_ROW_SEARCH_FN(dictMode, mls, rowLog) \ + ZSTD_SEARCH_FN_ATTRS size_t ZSTD_ROW_SEARCH_FN(dictMode, mls, rowLog)( \ ZSTD_matchState_t* ms, \ const BYTE* ip, const BYTE* const iLimit, \ size_t* offsetPtr) \ @@ -1369,9 +1370,6 @@ typedef struct { assert(MAX(4, MIN(6, ms->cParams.searchLog)) == rowLog); \ return ZSTD_RowFindBestMatch(ms, ip, iLimit, offsetPtr, mls, ZSTD_##dictMode, rowLog); \ } \ - static const ZSTD_LazyVTable ZSTD_RowVTable_##dictMode##_##mls##_##rowLog = { \ - ZSTD_RowFindBestMatch_##dictMode##_##mls##_##rowLog \ - }; #define ZSTD_FOR_EACH_ROWLOG(X, dictMode, mls) \ X(dictMode, mls, 4) \ @@ -1394,84 +1392,103 @@ typedef struct { X(__VA_ARGS__, dictMatchState) \ X(__VA_ARGS__, dedicatedDictSearch) -/* Generate Row VTables for each combination of (dictMode, mls, rowLog) */ -ZSTD_FOR_EACH_DICT_MODE(ZSTD_FOR_EACH_MLS_ROWLOG, GEN_ZSTD_ROW_VTABLE) -/* Generate Binary Tree VTables for each combination of (dictMode, mls) */ -ZSTD_FOR_EACH_DICT_MODE(ZSTD_FOR_EACH_MLS, GEN_ZSTD_BT_VTABLE) -/* Generate Hash Chain VTables for each combination of (dictMode, mls) */ -ZSTD_FOR_EACH_DICT_MODE(ZSTD_FOR_EACH_MLS, GEN_ZSTD_HC_VTABLE) - -#define GEN_ZSTD_BT_VTABLE_ARRAY(dictMode) \ - { \ - &ZSTD_BtVTable_##dictMode##_4, \ - &ZSTD_BtVTable_##dictMode##_5, \ - &ZSTD_BtVTable_##dictMode##_6 \ - } +/* Generate row search fns for each combination of (dictMode, mls, rowLog) */ +ZSTD_FOR_EACH_DICT_MODE(ZSTD_FOR_EACH_MLS_ROWLOG, GEN_ZSTD_ROW_SEARCH_FN) +/* Generate binary Tree search fns for each combination of (dictMode, mls) */ +ZSTD_FOR_EACH_DICT_MODE(ZSTD_FOR_EACH_MLS, GEN_ZSTD_BT_SEARCH_FN) +/* Generate hash chain search fns for each combination of (dictMode, mls) */ +ZSTD_FOR_EACH_DICT_MODE(ZSTD_FOR_EACH_MLS, GEN_ZSTD_HC_SEARCH_FN) -#define GEN_ZSTD_HC_VTABLE_ARRAY(dictMode) \ - { \ - &ZSTD_HcVTable_##dictMode##_4, \ - &ZSTD_HcVTable_##dictMode##_5, \ - &ZSTD_HcVTable_##dictMode##_6 \ - } - -#define GEN_ZSTD_ROW_VTABLE_ARRAY_(dictMode, mls) \ - { \ - &ZSTD_RowVTable_##dictMode##_##mls##_4, \ - &ZSTD_RowVTable_##dictMode##_##mls##_5, \ - &ZSTD_RowVTable_##dictMode##_##mls##_6 \ - } +typedef enum { search_hashChain=0, search_binaryTree=1, search_rowHash=2 } searchMethod_e; -#define GEN_ZSTD_ROW_VTABLE_ARRAY(dictMode) \ - { \ - GEN_ZSTD_ROW_VTABLE_ARRAY_(dictMode, 4), \ - GEN_ZSTD_ROW_VTABLE_ARRAY_(dictMode, 5), \ - GEN_ZSTD_ROW_VTABLE_ARRAY_(dictMode, 6) \ +#define GEN_ZSTD_CALL_BT_SEARCH_FN(dictMode, mls) \ + case mls: \ + return ZSTD_BT_SEARCH_FN(dictMode, mls)(ms, ip, iend, offsetPtr); +#define GEN_ZSTD_CALL_HC_SEARCH_FN(dictMode, mls) \ + case mls: \ + return ZSTD_HC_SEARCH_FN(dictMode, mls)(ms, ip, iend, offsetPtr); +#define GEN_ZSTD_CALL_ROW_SEARCH_FN(dictMode, mls, rowLog) \ + case rowLog: \ + return ZSTD_ROW_SEARCH_FN(dictMode, mls, rowLog)(ms, ip, iend, offsetPtr); + +#define ZSTD_SWITCH_MLS(X, dictMode) \ + switch (mls) { \ + ZSTD_FOR_EACH_MLS(X, dictMode) \ } -#define GEN_ZSTD_VTABLE_ARRAY(X) \ - { \ - X(noDict), \ - X(extDict), \ - X(dictMatchState), \ - X(dedicatedDictSearch) \ - } - -/* ******************************* -* Common parser - lazy strategy -*********************************/ -typedef enum { search_hashChain=0, search_binaryTree=1, search_rowHash=2 } searchMethod_e; +#define ZSTD_SWITCH_ROWLOG(dictMode, mls) \ + case mls: \ + switch (rowLog) { \ + ZSTD_FOR_EACH_ROWLOG(GEN_ZSTD_CALL_ROW_SEARCH_FN, dictMode, mls) \ + } \ + ZSTD_UNREACHABLE; \ + break; + +#define ZSTD_SWITCH_SEARCH_METHOD(dictMode) \ + switch (searchMethod) { \ + case search_hashChain: \ + ZSTD_SWITCH_MLS(GEN_ZSTD_CALL_HC_SEARCH_FN, dictMode) \ + break; \ + case search_binaryTree: \ + ZSTD_SWITCH_MLS(GEN_ZSTD_CALL_BT_SEARCH_FN, dictMode) \ + break; \ + case search_rowHash: \ + ZSTD_SWITCH_MLS(ZSTD_SWITCH_ROWLOG, dictMode) \ + break; \ + } \ + ZSTD_UNREACHABLE; /** - * This table is indexed first by the four ZSTD_dictMode_e values, and then - * by the two searchMethod_e values. NULLs are placed for configurations - * that should never occur (extDict modes go to the other implementation - * below and there is no DDSS for binary tree search yet). + * Searches for the longest match at @p ip. + * Dispatches to the correct implementation function based on the + * (searchMethod, dictMode, mls, rowLog). We use switch statements + * here instead of using an indirect function call through a function + * pointer because after Spectre and Meltdown mitigations, indirect + * function calls can be very costly, especially in the kernel. + * + * NOTE: dictMode and searchMethod should be templated, so those switch + * statements should be optimized out. Only the mls & rowLog switches + * should be left. + * + * @param ms The match state. + * @param ip The position to search at. + * @param iend The end of the input data. + * @param[out] offsetPtr Stores the match offset into this pointer. + * @param mls The minimum search length, in the range [4, 6]. + * @param rowLog The row log (if applicable), in the range [4, 6]. + * @param searchMethod The search method to use (templated). + * @param dictMode The dictMode (templated). + * + * @returns The length of the longest match found, or < mls if no match is found. + * If a match is found its offset is stored in @p offsetPtr. */ - -static ZSTD_LazyVTable const* -ZSTD_selectLazyVTable(ZSTD_matchState_t const* ms, searchMethod_e searchMethod, ZSTD_dictMode_e dictMode) +FORCE_INLINE_TEMPLATE size_t ZSTD_searchMax( + ZSTD_matchState_t* ms, + const BYTE* ip, + const BYTE* iend, + size_t* offsetPtr, + U32 const mls, + U32 const rowLog, + searchMethod_e const searchMethod, + ZSTD_dictMode_e const dictMode) { - /* Fill the Hc/Bt VTable arrays with the right functions for the (dictMode, mls) combination. */ - ZSTD_LazyVTable const* const hcVTables[4][3] = GEN_ZSTD_VTABLE_ARRAY(GEN_ZSTD_HC_VTABLE_ARRAY); - ZSTD_LazyVTable const* const btVTables[4][3] = GEN_ZSTD_VTABLE_ARRAY(GEN_ZSTD_BT_VTABLE_ARRAY); - /* Fill the Row VTable array with the right functions for the (dictMode, mls, rowLog) combination. */ - ZSTD_LazyVTable const* const rowVTables[4][3][3] = GEN_ZSTD_VTABLE_ARRAY(GEN_ZSTD_ROW_VTABLE_ARRAY); - - U32 const mls = MAX(4, MIN(6, ms->cParams.minMatch)); - U32 const rowLog = MAX(4, MIN(6, ms->cParams.searchLog)); - switch (searchMethod) { - case search_hashChain: - return hcVTables[dictMode][mls - 4]; - case search_binaryTree: - return btVTables[dictMode][mls - 4]; - case search_rowHash: - return rowVTables[dictMode][mls - 4][rowLog - 4]; - default: - return NULL; + if (dictMode == ZSTD_noDict) { + ZSTD_SWITCH_SEARCH_METHOD(noDict) + } else if (dictMode == ZSTD_extDict) { + ZSTD_SWITCH_SEARCH_METHOD(extDict) + } else if (dictMode == ZSTD_dictMatchState) { + ZSTD_SWITCH_SEARCH_METHOD(dictMatchState) + } else if (dictMode == ZSTD_dedicatedDictSearch) { + ZSTD_SWITCH_SEARCH_METHOD(dedicatedDictSearch) } + ZSTD_UNREACHABLE; + return 0; } +/* ******************************* +* Common parser - lazy strategy +*********************************/ + FORCE_INLINE_TEMPLATE size_t ZSTD_compressBlock_lazy_generic( ZSTD_matchState_t* ms, seqStore_t* seqStore, @@ -1488,9 +1505,11 @@ ZSTD_compressBlock_lazy_generic( const BYTE* const base = ms->window.base; const U32 prefixLowestIndex = ms->window.dictLimit; const BYTE* const prefixLowest = base + prefixLowestIndex; + const U32 mls = BOUNDED(4, ms->cParams.minMatch, 6); + const U32 rowLog = BOUNDED(4, ms->cParams.searchLog, 6); - searchMax_f const searchMax = ZSTD_selectLazyVTable(ms, searchMethod, dictMode)->searchMax; - U32 offset_1 = rep[0], offset_2 = rep[1], savedOffset=0; + U32 offset_1 = rep[0], offset_2 = rep[1]; + U32 offsetSaved1 = 0, offsetSaved2 = 0; const int isDMS = dictMode == ZSTD_dictMatchState; const int isDDS = dictMode == ZSTD_dedicatedDictSearch; @@ -1505,16 +1524,14 @@ ZSTD_compressBlock_lazy_generic( 0; const U32 dictAndPrefixLength = (U32)((ip - prefixLowest) + (dictEnd - dictLowest)); - assert(searchMax != NULL); - DEBUGLOG(5, "ZSTD_compressBlock_lazy_generic (dictMode=%u) (searchFunc=%u)", (U32)dictMode, (U32)searchMethod); ip += (dictAndPrefixLength == 0); if (dictMode == ZSTD_noDict) { U32 const curr = (U32)(ip - base); U32 const windowLow = ZSTD_getLowestPrefixIndex(ms, curr, ms->cParams.windowLog); U32 const maxRep = curr - windowLow; - if (offset_2 > maxRep) savedOffset = offset_2, offset_2 = 0; - if (offset_1 > maxRep) savedOffset = offset_1, offset_1 = 0; + if (offset_2 > maxRep) offsetSaved2 = offset_2, offset_2 = 0; + if (offset_1 > maxRep) offsetSaved1 = offset_1, offset_1 = 0; } if (isDxS) { /* dictMatchState repCode checks don't currently handle repCode == 0 @@ -1523,11 +1540,11 @@ ZSTD_compressBlock_lazy_generic( assert(offset_2 <= dictAndPrefixLength); } + /* Reset the lazy skipping state */ + ms->lazySkipping = 0; + if (searchMethod == search_rowHash) { - const U32 rowLog = MAX(4, MIN(6, ms->cParams.searchLog)); - ZSTD_row_fillHashCache(ms, base, rowLog, - MIN(ms->cParams.minMatch, 6 /* mls caps out at 6 */), - ms->nextToUpdate, ilimit); + ZSTD_row_fillHashCache(ms, base, rowLog, mls, ms->nextToUpdate, ilimit); } /* Match Loop */ @@ -1539,7 +1556,7 @@ ZSTD_compressBlock_lazy_generic( #endif while (ip < ilimit) { size_t matchLength=0; - size_t offcode=STORE_REPCODE_1; + size_t offBase = REPCODE1_TO_OFFBASE; const BYTE* start=ip+1; DEBUGLOG(7, "search baseline (depth 0)"); @@ -1564,14 +1581,23 @@ ZSTD_compressBlock_lazy_generic( } /* first search (depth 0) */ - { size_t offsetFound = 999999999; - size_t const ml2 = searchMax(ms, ip, iend, &offsetFound); + { size_t offbaseFound = 999999999; + size_t const ml2 = ZSTD_searchMax(ms, ip, iend, &offbaseFound, mls, rowLog, searchMethod, dictMode); if (ml2 > matchLength) - matchLength = ml2, start = ip, offcode=offsetFound; + matchLength = ml2, start = ip, offBase = offbaseFound; } if (matchLength < 4) { - ip += ((ip-anchor) >> kSearchStrength) + 1; /* jump faster over incompressible sections */ + size_t const step = ((size_t)(ip-anchor) >> kSearchStrength) + 1; /* jump faster over incompressible sections */; + ip += step; + /* Enter the lazy skipping mode once we are skipping more than 8 bytes at a time. + * In this mode we stop inserting every position into our tables, and only insert + * positions that we search, which is one in step positions. + * The exact cutoff is flexible, I've just chosen a number that is reasonably high, + * so we minimize the compression ratio loss in "normal" scenarios. This mode gets + * triggered once we've gone 2KB without finding any matches. + */ + ms->lazySkipping = step > kLazySkippingStep; continue; } @@ -1581,12 +1607,12 @@ ZSTD_compressBlock_lazy_generic( DEBUGLOG(7, "search depth 1"); ip ++; if ( (dictMode == ZSTD_noDict) - && (offcode) && ((offset_1>0) & (MEM_read32(ip) == MEM_read32(ip - offset_1)))) { + && (offBase) && ((offset_1>0) & (MEM_read32(ip) == MEM_read32(ip - offset_1)))) { size_t const mlRep = ZSTD_count(ip+4, ip+4-offset_1, iend) + 4; int const gain2 = (int)(mlRep * 3); - int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offcode)) + 1); + int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)offBase) + 1); if ((mlRep >= 4) && (gain2 > gain1)) - matchLength = mlRep, offcode = STORE_REPCODE_1, start = ip; + matchLength = mlRep, offBase = REPCODE1_TO_OFFBASE, start = ip; } if (isDxS) { const U32 repIndex = (U32)(ip - base) - offset_1; @@ -1598,17 +1624,17 @@ ZSTD_compressBlock_lazy_generic( const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend; size_t const mlRep = ZSTD_count_2segments(ip+4, repMatch+4, iend, repMatchEnd, prefixLowest) + 4; int const gain2 = (int)(mlRep * 3); - int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offcode)) + 1); + int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)offBase) + 1); if ((mlRep >= 4) && (gain2 > gain1)) - matchLength = mlRep, offcode = STORE_REPCODE_1, start = ip; + matchLength = mlRep, offBase = REPCODE1_TO_OFFBASE, start = ip; } } - { size_t offset2=999999999; - size_t const ml2 = searchMax(ms, ip, iend, &offset2); - int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offset2))); /* raw approx */ - int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offcode)) + 4); + { size_t ofbCandidate=999999999; + size_t const ml2 = ZSTD_searchMax(ms, ip, iend, &ofbCandidate, mls, rowLog, searchMethod, dictMode); + int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)ofbCandidate)); /* raw approx */ + int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 4); if ((ml2 >= 4) && (gain2 > gain1)) { - matchLength = ml2, offcode = offset2, start = ip; + matchLength = ml2, offBase = ofbCandidate, start = ip; continue; /* search a better one */ } } @@ -1617,12 +1643,12 @@ ZSTD_compressBlock_lazy_generic( DEBUGLOG(7, "search depth 2"); ip ++; if ( (dictMode == ZSTD_noDict) - && (offcode) && ((offset_1>0) & (MEM_read32(ip) == MEM_read32(ip - offset_1)))) { + && (offBase) && ((offset_1>0) & (MEM_read32(ip) == MEM_read32(ip - offset_1)))) { size_t const mlRep = ZSTD_count(ip+4, ip+4-offset_1, iend) + 4; int const gain2 = (int)(mlRep * 4); - int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offcode)) + 1); + int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 1); if ((mlRep >= 4) && (gain2 > gain1)) - matchLength = mlRep, offcode = STORE_REPCODE_1, start = ip; + matchLength = mlRep, offBase = REPCODE1_TO_OFFBASE, start = ip; } if (isDxS) { const U32 repIndex = (U32)(ip - base) - offset_1; @@ -1634,17 +1660,17 @@ ZSTD_compressBlock_lazy_generic( const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend; size_t const mlRep = ZSTD_count_2segments(ip+4, repMatch+4, iend, repMatchEnd, prefixLowest) + 4; int const gain2 = (int)(mlRep * 4); - int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offcode)) + 1); + int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 1); if ((mlRep >= 4) && (gain2 > gain1)) - matchLength = mlRep, offcode = STORE_REPCODE_1, start = ip; + matchLength = mlRep, offBase = REPCODE1_TO_OFFBASE, start = ip; } } - { size_t offset2=999999999; - size_t const ml2 = searchMax(ms, ip, iend, &offset2); - int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offset2))); /* raw approx */ - int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offcode)) + 7); + { size_t ofbCandidate=999999999; + size_t const ml2 = ZSTD_searchMax(ms, ip, iend, &ofbCandidate, mls, rowLog, searchMethod, dictMode); + int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)ofbCandidate)); /* raw approx */ + int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 7); if ((ml2 >= 4) && (gain2 > gain1)) { - matchLength = ml2, offcode = offset2, start = ip; + matchLength = ml2, offBase = ofbCandidate, start = ip; continue; } } } break; /* nothing found : store previous solution */ @@ -1655,26 +1681,33 @@ ZSTD_compressBlock_lazy_generic( * notably if `value` is unsigned, resulting in a large positive `-value`. */ /* catch up */ - if (STORED_IS_OFFSET(offcode)) { + if (OFFBASE_IS_OFFSET(offBase)) { if (dictMode == ZSTD_noDict) { - while ( ((start > anchor) & (start - STORED_OFFSET(offcode) > prefixLowest)) - && (start[-1] == (start-STORED_OFFSET(offcode))[-1]) ) /* only search for offset within prefix */ + while ( ((start > anchor) & (start - OFFBASE_TO_OFFSET(offBase) > prefixLowest)) + && (start[-1] == (start-OFFBASE_TO_OFFSET(offBase))[-1]) ) /* only search for offset within prefix */ { start--; matchLength++; } } if (isDxS) { - U32 const matchIndex = (U32)((size_t)(start-base) - STORED_OFFSET(offcode)); + U32 const matchIndex = (U32)((size_t)(start-base) - OFFBASE_TO_OFFSET(offBase)); const BYTE* match = (matchIndex < prefixLowestIndex) ? dictBase + matchIndex - dictIndexDelta : base + matchIndex; const BYTE* const mStart = (matchIndex < prefixLowestIndex) ? dictLowest : prefixLowest; while ((start>anchor) && (match>mStart) && (start[-1] == match[-1])) { start--; match--; matchLength++; } /* catch up */ } - offset_2 = offset_1; offset_1 = (U32)STORED_OFFSET(offcode); + offset_2 = offset_1; offset_1 = (U32)OFFBASE_TO_OFFSET(offBase); } /* store sequence */ _storeSequence: { size_t const litLength = (size_t)(start - anchor); - ZSTD_storeSeq(seqStore, litLength, anchor, iend, (U32)offcode, matchLength); + ZSTD_storeSeq(seqStore, litLength, anchor, iend, (U32)offBase, matchLength); anchor = ip = start + matchLength; } + if (ms->lazySkipping) { + /* We've found a match, disable lazy skipping mode, and refill the hash cache. */ + if (searchMethod == search_rowHash) { + ZSTD_row_fillHashCache(ms, base, rowLog, mls, ms->nextToUpdate, ilimit); + } + ms->lazySkipping = 0; + } /* check immediate repcode */ if (isDxS) { @@ -1688,8 +1721,8 @@ ZSTD_compressBlock_lazy_generic( && (MEM_read32(repMatch) == MEM_read32(ip)) ) { const BYTE* const repEnd2 = repIndex < prefixLowestIndex ? dictEnd : iend; matchLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd2, prefixLowest) + 4; - offcode = offset_2; offset_2 = offset_1; offset_1 = (U32)offcode; /* swap offset_2 <=> offset_1 */ - ZSTD_storeSeq(seqStore, 0, anchor, iend, STORE_REPCODE_1, matchLength); + offBase = offset_2; offset_2 = offset_1; offset_1 = (U32)offBase; /* swap offset_2 <=> offset_1 */ + ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, matchLength); ip += matchLength; anchor = ip; continue; @@ -1703,16 +1736,20 @@ ZSTD_compressBlock_lazy_generic( && (MEM_read32(ip) == MEM_read32(ip - offset_2)) ) { /* store sequence */ matchLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4; - offcode = offset_2; offset_2 = offset_1; offset_1 = (U32)offcode; /* swap repcodes */ - ZSTD_storeSeq(seqStore, 0, anchor, iend, STORE_REPCODE_1, matchLength); + offBase = offset_2; offset_2 = offset_1; offset_1 = (U32)offBase; /* swap repcodes */ + ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, matchLength); ip += matchLength; anchor = ip; continue; /* faster when present ... (?) */ } } } - /* Save reps for next block */ - rep[0] = offset_1 ? offset_1 : savedOffset; - rep[1] = offset_2 ? offset_2 : savedOffset; + /* If offset_1 started invalid (offsetSaved1 != 0) and became valid (offset_1 != 0), + * rotate saved offsets. See comment in ZSTD_compressBlock_fast_noDict for more context. */ + offsetSaved2 = ((offsetSaved1 != 0) && (offset_1 != 0)) ? offsetSaved1 : offsetSaved2; + + /* save reps for next block */ + rep[0] = offset_1 ? offset_1 : offsetSaved1; + rep[1] = offset_2 ? offset_2 : offsetSaved2; /* Return the last literals size */ return (size_t)(iend - anchor); @@ -1881,19 +1918,20 @@ size_t ZSTD_compressBlock_lazy_extDict_generic( const BYTE* const dictEnd = dictBase + dictLimit; const BYTE* const dictStart = dictBase + ms->window.lowLimit; const U32 windowLog = ms->cParams.windowLog; - const U32 rowLog = ms->cParams.searchLog < 5 ? 4 : 5; + const U32 mls = BOUNDED(4, ms->cParams.minMatch, 6); + const U32 rowLog = BOUNDED(4, ms->cParams.searchLog, 6); - searchMax_f const searchMax = ZSTD_selectLazyVTable(ms, searchMethod, ZSTD_extDict)->searchMax; U32 offset_1 = rep[0], offset_2 = rep[1]; DEBUGLOG(5, "ZSTD_compressBlock_lazy_extDict_generic (searchFunc=%u)", (U32)searchMethod); + /* Reset the lazy skipping state */ + ms->lazySkipping = 0; + /* init */ ip += (ip == prefixStart); if (searchMethod == search_rowHash) { - ZSTD_row_fillHashCache(ms, base, rowLog, - MIN(ms->cParams.minMatch, 6 /* mls caps out at 6 */), - ms->nextToUpdate, ilimit); + ZSTD_row_fillHashCache(ms, base, rowLog, mls, ms->nextToUpdate, ilimit); } /* Match Loop */ @@ -1905,7 +1943,7 @@ size_t ZSTD_compressBlock_lazy_extDict_generic( #endif while (ip < ilimit) { size_t matchLength=0; - size_t offcode=STORE_REPCODE_1; + size_t offBase = REPCODE1_TO_OFFBASE; const BYTE* start=ip+1; U32 curr = (U32)(ip-base); @@ -1924,14 +1962,23 @@ size_t ZSTD_compressBlock_lazy_extDict_generic( } } /* first search (depth 0) */ - { size_t offsetFound = 999999999; - size_t const ml2 = searchMax(ms, ip, iend, &offsetFound); + { size_t ofbCandidate = 999999999; + size_t const ml2 = ZSTD_searchMax(ms, ip, iend, &ofbCandidate, mls, rowLog, searchMethod, ZSTD_extDict); if (ml2 > matchLength) - matchLength = ml2, start = ip, offcode=offsetFound; + matchLength = ml2, start = ip, offBase = ofbCandidate; } if (matchLength < 4) { - ip += ((ip-anchor) >> kSearchStrength) + 1; /* jump faster over incompressible sections */ + size_t const step = ((size_t)(ip-anchor) >> kSearchStrength); + ip += step + 1; /* jump faster over incompressible sections */ + /* Enter the lazy skipping mode once we are skipping more than 8 bytes at a time. + * In this mode we stop inserting every position into our tables, and only insert + * positions that we search, which is one in step positions. + * The exact cutoff is flexible, I've just chosen a number that is reasonably high, + * so we minimize the compression ratio loss in "normal" scenarios. This mode gets + * triggered once we've gone 2KB without finding any matches. + */ + ms->lazySkipping = step > kLazySkippingStep; continue; } @@ -1941,7 +1988,7 @@ size_t ZSTD_compressBlock_lazy_extDict_generic( ip ++; curr++; /* check repCode */ - if (offcode) { + if (offBase) { const U32 windowLow = ZSTD_getLowestMatchIndex(ms, curr, windowLog); const U32 repIndex = (U32)(curr - offset_1); const BYTE* const repBase = repIndex < dictLimit ? dictBase : base; @@ -1953,18 +2000,18 @@ size_t ZSTD_compressBlock_lazy_extDict_generic( const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend; size_t const repLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd, prefixStart) + 4; int const gain2 = (int)(repLength * 3); - int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offcode)) + 1); + int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)offBase) + 1); if ((repLength >= 4) && (gain2 > gain1)) - matchLength = repLength, offcode = STORE_REPCODE_1, start = ip; + matchLength = repLength, offBase = REPCODE1_TO_OFFBASE, start = ip; } } /* search match, depth 1 */ - { size_t offset2=999999999; - size_t const ml2 = searchMax(ms, ip, iend, &offset2); - int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offset2))); /* raw approx */ - int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offcode)) + 4); + { size_t ofbCandidate = 999999999; + size_t const ml2 = ZSTD_searchMax(ms, ip, iend, &ofbCandidate, mls, rowLog, searchMethod, ZSTD_extDict); + int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)ofbCandidate)); /* raw approx */ + int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 4); if ((ml2 >= 4) && (gain2 > gain1)) { - matchLength = ml2, offcode = offset2, start = ip; + matchLength = ml2, offBase = ofbCandidate, start = ip; continue; /* search a better one */ } } @@ -1973,7 +2020,7 @@ size_t ZSTD_compressBlock_lazy_extDict_generic( ip ++; curr++; /* check repCode */ - if (offcode) { + if (offBase) { const U32 windowLow = ZSTD_getLowestMatchIndex(ms, curr, windowLog); const U32 repIndex = (U32)(curr - offset_1); const BYTE* const repBase = repIndex < dictLimit ? dictBase : base; @@ -1985,38 +2032,45 @@ size_t ZSTD_compressBlock_lazy_extDict_generic( const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend; size_t const repLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd, prefixStart) + 4; int const gain2 = (int)(repLength * 4); - int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offcode)) + 1); + int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 1); if ((repLength >= 4) && (gain2 > gain1)) - matchLength = repLength, offcode = STORE_REPCODE_1, start = ip; + matchLength = repLength, offBase = REPCODE1_TO_OFFBASE, start = ip; } } /* search match, depth 2 */ - { size_t offset2=999999999; - size_t const ml2 = searchMax(ms, ip, iend, &offset2); - int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offset2))); /* raw approx */ - int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offcode)) + 7); + { size_t ofbCandidate = 999999999; + size_t const ml2 = ZSTD_searchMax(ms, ip, iend, &ofbCandidate, mls, rowLog, searchMethod, ZSTD_extDict); + int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)ofbCandidate)); /* raw approx */ + int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 7); if ((ml2 >= 4) && (gain2 > gain1)) { - matchLength = ml2, offcode = offset2, start = ip; + matchLength = ml2, offBase = ofbCandidate, start = ip; continue; } } } break; /* nothing found : store previous solution */ } /* catch up */ - if (STORED_IS_OFFSET(offcode)) { - U32 const matchIndex = (U32)((size_t)(start-base) - STORED_OFFSET(offcode)); + if (OFFBASE_IS_OFFSET(offBase)) { + U32 const matchIndex = (U32)((size_t)(start-base) - OFFBASE_TO_OFFSET(offBase)); const BYTE* match = (matchIndex < dictLimit) ? dictBase + matchIndex : base + matchIndex; const BYTE* const mStart = (matchIndex < dictLimit) ? dictStart : prefixStart; while ((start>anchor) && (match>mStart) && (start[-1] == match[-1])) { start--; match--; matchLength++; } /* catch up */ - offset_2 = offset_1; offset_1 = (U32)STORED_OFFSET(offcode); + offset_2 = offset_1; offset_1 = (U32)OFFBASE_TO_OFFSET(offBase); } /* store sequence */ _storeSequence: { size_t const litLength = (size_t)(start - anchor); - ZSTD_storeSeq(seqStore, litLength, anchor, iend, (U32)offcode, matchLength); + ZSTD_storeSeq(seqStore, litLength, anchor, iend, (U32)offBase, matchLength); anchor = ip = start + matchLength; } + if (ms->lazySkipping) { + /* We've found a match, disable lazy skipping mode, and refill the hash cache. */ + if (searchMethod == search_rowHash) { + ZSTD_row_fillHashCache(ms, base, rowLog, mls, ms->nextToUpdate, ilimit); + } + ms->lazySkipping = 0; + } /* check immediate repcode */ while (ip <= ilimit) { @@ -2031,8 +2085,8 @@ size_t ZSTD_compressBlock_lazy_extDict_generic( /* repcode detected we should take it */ const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend; matchLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd, prefixStart) + 4; - offcode = offset_2; offset_2 = offset_1; offset_1 = (U32)offcode; /* swap offset history */ - ZSTD_storeSeq(seqStore, 0, anchor, iend, STORE_REPCODE_1, matchLength); + offBase = offset_2; offset_2 = offset_1; offset_1 = (U32)offBase; /* swap offset history */ + ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, matchLength); ip += matchLength; anchor = ip; continue; /* faster when present ... (?) */ @@ -2098,7 +2152,6 @@ size_t ZSTD_compressBlock_lazy_extDict_row( size_t ZSTD_compressBlock_lazy2_extDict_row( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) - { return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, src, srcSize, search_rowHash, 2); } diff --git a/internal-complibs/zstd-1.5.2/compress/zstd_lazy.h b/internal-complibs/zstd-1.5.5/compress/zstd_lazy.h similarity index 97% rename from internal-complibs/zstd-1.5.2/compress/zstd_lazy.h rename to internal-complibs/zstd-1.5.5/compress/zstd_lazy.h index 150f7b39..3bde6733 100644 --- a/internal-complibs/zstd-1.5.2/compress/zstd_lazy.h +++ b/internal-complibs/zstd-1.5.5/compress/zstd_lazy.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -25,6 +25,8 @@ extern "C" { */ #define ZSTD_LAZY_DDSS_BUCKET_LOG 2 +#define ZSTD_ROW_HASH_TAG_BITS 8 /* nb bits to use for the tag */ + U32 ZSTD_insertAndFindFirstIndex(ZSTD_matchState_t* ms, const BYTE* ip); void ZSTD_row_update(ZSTD_matchState_t* const ms, const BYTE* ip); @@ -116,7 +118,7 @@ size_t ZSTD_compressBlock_lazy2_extDict_row( size_t ZSTD_compressBlock_btlazy2_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); - + #if defined (__cplusplus) } diff --git a/internal-complibs/zstd-1.5.2/compress/zstd_ldm.c b/internal-complibs/zstd-1.5.5/compress/zstd_ldm.c similarity index 98% rename from internal-complibs/zstd-1.5.2/compress/zstd_ldm.c rename to internal-complibs/zstd-1.5.5/compress/zstd_ldm.c index f662b254..3d74ff19 100644 --- a/internal-complibs/zstd-1.5.2/compress/zstd_ldm.c +++ b/internal-complibs/zstd-1.5.5/compress/zstd_ldm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -242,11 +242,11 @@ static size_t ZSTD_ldm_fillFastTables(ZSTD_matchState_t* ms, switch(ms->cParams.strategy) { case ZSTD_fast: - ZSTD_fillHashTable(ms, iend, ZSTD_dtlm_fast); + ZSTD_fillHashTable(ms, iend, ZSTD_dtlm_fast, ZSTD_tfp_forCCtx); break; case ZSTD_dfast: - ZSTD_fillDoubleHashTable(ms, iend, ZSTD_dtlm_fast); + ZSTD_fillDoubleHashTable(ms, iend, ZSTD_dtlm_fast, ZSTD_tfp_forCCtx); break; case ZSTD_greedy: @@ -549,7 +549,7 @@ size_t ZSTD_ldm_generateSequences( * the window through early invalidation. * TODO: * Test the chunk size. * * Try invalidation after the sequence generation and test the - * the offset against maxDist directly. + * offset against maxDist directly. * * NOTE: Because of dictionaries + sequence splitting we MUST make sure * that any offset used is valid at the END of the sequence, since it may @@ -711,7 +711,7 @@ size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore, rep[0] = sequence.offset; /* Store the sequence */ ZSTD_storeSeq(seqStore, newLitLength, ip - newLitLength, iend, - STORE_OFFSET(sequence.offset), + OFFSET_TO_OFFBASE(sequence.offset), sequence.matchLength); ip += sequence.matchLength; } diff --git a/internal-complibs/zstd-1.5.2/compress/zstd_ldm.h b/internal-complibs/zstd-1.5.5/compress/zstd_ldm.h similarity index 98% rename from internal-complibs/zstd-1.5.2/compress/zstd_ldm.h rename to internal-complibs/zstd-1.5.5/compress/zstd_ldm.h index 4e68dbf5..f147021d 100644 --- a/internal-complibs/zstd-1.5.2/compress/zstd_ldm.h +++ b/internal-complibs/zstd-1.5.5/compress/zstd_ldm.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the diff --git a/internal-complibs/zstd-1.5.2/compress/zstd_ldm_geartab.h b/internal-complibs/zstd-1.5.5/compress/zstd_ldm_geartab.h similarity index 99% rename from internal-complibs/zstd-1.5.2/compress/zstd_ldm_geartab.h rename to internal-complibs/zstd-1.5.5/compress/zstd_ldm_geartab.h index 647f865b..ef34bc5c 100644 --- a/internal-complibs/zstd-1.5.2/compress/zstd_ldm_geartab.h +++ b/internal-complibs/zstd-1.5.5/compress/zstd_ldm_geartab.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the diff --git a/internal-complibs/zstd-1.5.2/compress/zstd_opt.c b/internal-complibs/zstd-1.5.5/compress/zstd_opt.c similarity index 91% rename from internal-complibs/zstd-1.5.2/compress/zstd_opt.c rename to internal-complibs/zstd-1.5.5/compress/zstd_opt.c index 1b1ddad4..f02a7609 100644 --- a/internal-complibs/zstd-1.5.2/compress/zstd_opt.c +++ b/internal-complibs/zstd-1.5.5/compress/zstd_opt.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Przemyslaw Skibinski, Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -16,7 +16,7 @@ #define ZSTD_LITFREQ_ADD 2 /* scaling factor for litFreq, so that frequencies adapt faster to new stats */ #define ZSTD_MAX_PRICE (1<<30) -#define ZSTD_PREDEF_THRESHOLD 1024 /* if srcSize < ZSTD_PREDEF_THRESHOLD, symbols' cost is assumed static, directly determined by pre-defined distributions */ +#define ZSTD_PREDEF_THRESHOLD 8 /* if srcSize < ZSTD_PREDEF_THRESHOLD, symbols' cost is assumed static, directly determined by pre-defined distributions */ /*-************************************* @@ -26,27 +26,35 @@ #if 0 /* approximation at bit level (for tests) */ # define BITCOST_ACCURACY 0 # define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY) -# define WEIGHT(stat, opt) ((void)opt, ZSTD_bitWeight(stat)) +# define WEIGHT(stat, opt) ((void)(opt), ZSTD_bitWeight(stat)) #elif 0 /* fractional bit accuracy (for tests) */ # define BITCOST_ACCURACY 8 # define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY) -# define WEIGHT(stat,opt) ((void)opt, ZSTD_fracWeight(stat)) +# define WEIGHT(stat,opt) ((void)(opt), ZSTD_fracWeight(stat)) #else /* opt==approx, ultra==accurate */ # define BITCOST_ACCURACY 8 # define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY) -# define WEIGHT(stat,opt) (opt ? ZSTD_fracWeight(stat) : ZSTD_bitWeight(stat)) +# define WEIGHT(stat,opt) ((opt) ? ZSTD_fracWeight(stat) : ZSTD_bitWeight(stat)) #endif +/* ZSTD_bitWeight() : + * provide estimated "cost" of a stat in full bits only */ MEM_STATIC U32 ZSTD_bitWeight(U32 stat) { return (ZSTD_highbit32(stat+1) * BITCOST_MULTIPLIER); } +/* ZSTD_fracWeight() : + * provide fractional-bit "cost" of a stat, + * using linear interpolation approximation */ MEM_STATIC U32 ZSTD_fracWeight(U32 rawStat) { U32 const stat = rawStat + 1; U32 const hb = ZSTD_highbit32(stat); U32 const BWeight = hb * BITCOST_MULTIPLIER; + /* Fweight was meant for "Fractional weight" + * but it's effectively a value between 1 and 2 + * using fixed point arithmetic */ U32 const FWeight = (stat << BITCOST_ACCURACY) >> hb; U32 const weight = BWeight + FWeight; assert(hb + BITCOST_ACCURACY < 31); @@ -57,7 +65,7 @@ MEM_STATIC U32 ZSTD_fracWeight(U32 rawStat) /* debugging function, * @return price in bytes as fractional value * for debug messages only */ -MEM_STATIC double ZSTD_fCost(U32 price) +MEM_STATIC double ZSTD_fCost(int price) { return (double)price / (BITCOST_MULTIPLIER*8); } @@ -88,20 +96,26 @@ static U32 sum_u32(const unsigned table[], size_t nbElts) return total; } -static U32 ZSTD_downscaleStats(unsigned* table, U32 lastEltIndex, U32 shift) +typedef enum { base_0possible=0, base_1guaranteed=1 } base_directive_e; + +static U32 +ZSTD_downscaleStats(unsigned* table, U32 lastEltIndex, U32 shift, base_directive_e base1) { U32 s, sum=0; - DEBUGLOG(5, "ZSTD_downscaleStats (nbElts=%u, shift=%u)", (unsigned)lastEltIndex+1, (unsigned)shift); + DEBUGLOG(5, "ZSTD_downscaleStats (nbElts=%u, shift=%u)", + (unsigned)lastEltIndex+1, (unsigned)shift ); assert(shift < 30); for (s=0; s> shift); - sum += table[s]; + unsigned const base = base1 ? 1 : (table[s]>0); + unsigned const newStat = base + (table[s] >> shift); + sum += newStat; + table[s] = newStat; } return sum; } /* ZSTD_scaleStats() : - * reduce all elements in table is sum too large + * reduce all elt frequencies in table if sum too large * return the resulting sum of elements */ static U32 ZSTD_scaleStats(unsigned* table, U32 lastEltIndex, U32 logTarget) { @@ -110,7 +124,7 @@ static U32 ZSTD_scaleStats(unsigned* table, U32 lastEltIndex, U32 logTarget) DEBUGLOG(5, "ZSTD_scaleStats (nbElts=%u, target=%u)", (unsigned)lastEltIndex+1, (unsigned)logTarget); assert(logTarget < 30); if (factor <= 1) return prevsum; - return ZSTD_downscaleStats(table, lastEltIndex, ZSTD_highbit32(factor)); + return ZSTD_downscaleStats(table, lastEltIndex, ZSTD_highbit32(factor), base_1guaranteed); } /* ZSTD_rescaleFreqs() : @@ -129,18 +143,22 @@ ZSTD_rescaleFreqs(optState_t* const optPtr, DEBUGLOG(5, "ZSTD_rescaleFreqs (srcSize=%u)", (unsigned)srcSize); optPtr->priceType = zop_dynamic; - if (optPtr->litLengthSum == 0) { /* first block : init */ - if (srcSize <= ZSTD_PREDEF_THRESHOLD) { /* heuristic */ - DEBUGLOG(5, "(srcSize <= ZSTD_PREDEF_THRESHOLD) => zop_predef"); + if (optPtr->litLengthSum == 0) { /* no literals stats collected -> first block assumed -> init */ + + /* heuristic: use pre-defined stats for too small inputs */ + if (srcSize <= ZSTD_PREDEF_THRESHOLD) { + DEBUGLOG(5, "srcSize <= %i : use predefined stats", ZSTD_PREDEF_THRESHOLD); optPtr->priceType = zop_predef; } assert(optPtr->symbolCosts != NULL); if (optPtr->symbolCosts->huf.repeatMode == HUF_repeat_valid) { - /* huffman table presumed generated by dictionary */ + + /* huffman stats covering the full value set : table presumed generated by dictionary */ optPtr->priceType = zop_dynamic; if (compressedLiterals) { + /* generate literals statistics from huffman table */ unsigned lit; assert(optPtr->litFreq != NULL); optPtr->litSum = 0; @@ -188,13 +206,14 @@ ZSTD_rescaleFreqs(optState_t* const optPtr, optPtr->offCodeSum += optPtr->offCodeFreq[of]; } } - } else { /* not a dictionary */ + } else { /* first block, no dictionary */ assert(optPtr->litFreq != NULL); if (compressedLiterals) { + /* base initial cost of literals on direct frequency within src */ unsigned lit = MaxLit; HIST_count_simple(optPtr->litFreq, &lit, src, srcSize); /* use raw first block to init statistics */ - optPtr->litSum = ZSTD_downscaleStats(optPtr->litFreq, MaxLit, 8); + optPtr->litSum = ZSTD_downscaleStats(optPtr->litFreq, MaxLit, 8, base_0possible); } { unsigned const baseLLfreqs[MaxLL+1] = { @@ -224,10 +243,9 @@ ZSTD_rescaleFreqs(optState_t* const optPtr, optPtr->offCodeSum = sum_u32(baseOFCfreqs, MaxOff+1); } - } - } else { /* new block : re-use previous statistics, scaled down */ + } else { /* new block : scale down accumulated statistics */ if (compressedLiterals) optPtr->litSum = ZSTD_scaleStats(optPtr->litFreq, MaxLit, 12); @@ -255,11 +273,14 @@ static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength, return (litLength*6) * BITCOST_MULTIPLIER; /* 6 bit per literal - no statistic used */ /* dynamic statistics */ - { U32 price = litLength * optPtr->litSumBasePrice; + { U32 price = optPtr->litSumBasePrice * litLength; + U32 const litPriceMax = optPtr->litSumBasePrice - BITCOST_MULTIPLIER; U32 u; + assert(optPtr->litSumBasePrice >= BITCOST_MULTIPLIER); for (u=0; u < litLength; u++) { - assert(WEIGHT(optPtr->litFreq[literals[u]], optLevel) <= optPtr->litSumBasePrice); /* literal cost should never be negative */ - price -= WEIGHT(optPtr->litFreq[literals[u]], optLevel); + U32 litPrice = WEIGHT(optPtr->litFreq[literals[u]], optLevel); + if (UNLIKELY(litPrice > litPriceMax)) litPrice = litPriceMax; + price -= litPrice; } return price; } @@ -272,10 +293,11 @@ static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optP assert(litLength <= ZSTD_BLOCKSIZE_MAX); if (optPtr->priceType == zop_predef) return WEIGHT(litLength, optLevel); - /* We can't compute the litLength price for sizes >= ZSTD_BLOCKSIZE_MAX - * because it isn't representable in the zstd format. So instead just - * call it 1 bit more than ZSTD_BLOCKSIZE_MAX - 1. In this case the block - * would be all literals. + + /* ZSTD_LLcode() can't compute litLength price for sizes >= ZSTD_BLOCKSIZE_MAX + * because it isn't representable in the zstd format. + * So instead just pretend it would cost 1 bit more than ZSTD_BLOCKSIZE_MAX - 1. + * In such a case, the block would be all literals. */ if (litLength == ZSTD_BLOCKSIZE_MAX) return BITCOST_MULTIPLIER + ZSTD_litLengthPrice(ZSTD_BLOCKSIZE_MAX - 1, optPtr, optLevel); @@ -289,24 +311,25 @@ static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optP } /* ZSTD_getMatchPrice() : - * Provides the cost of the match part (offset + matchLength) of a sequence + * Provides the cost of the match part (offset + matchLength) of a sequence. * Must be combined with ZSTD_fullLiteralsCost() to get the full cost of a sequence. - * @offcode : expects a scale where 0,1,2 are repcodes 1-3, and 3+ are real_offsets+2 + * @offBase : sumtype, representing an offset or a repcode, and using numeric representation of ZSTD_storeSeq() * @optLevel: when <2, favors small offset for decompression speed (improved cache efficiency) */ FORCE_INLINE_TEMPLATE U32 -ZSTD_getMatchPrice(U32 const offcode, +ZSTD_getMatchPrice(U32 const offBase, U32 const matchLength, const optState_t* const optPtr, int const optLevel) { U32 price; - U32 const offCode = ZSTD_highbit32(STORED_TO_OFFBASE(offcode)); + U32 const offCode = ZSTD_highbit32(offBase); U32 const mlBase = matchLength - MINMATCH; assert(matchLength >= MINMATCH); - if (optPtr->priceType == zop_predef) /* fixed scheme, do not use statistics */ - return WEIGHT(mlBase, optLevel) + ((16 + offCode) * BITCOST_MULTIPLIER); + if (optPtr->priceType == zop_predef) /* fixed scheme, does not use statistics */ + return WEIGHT(mlBase, optLevel) + + ((16 + offCode) * BITCOST_MULTIPLIER); /* emulated offset cost */ /* dynamic statistics */ price = (offCode * BITCOST_MULTIPLIER) + (optPtr->offCodeSumBasePrice - WEIGHT(optPtr->offCodeFreq[offCode], optLevel)); @@ -325,10 +348,10 @@ ZSTD_getMatchPrice(U32 const offcode, } /* ZSTD_updateStats() : - * assumption : literals + litLengtn <= iend */ + * assumption : literals + litLength <= iend */ static void ZSTD_updateStats(optState_t* const optPtr, U32 litLength, const BYTE* literals, - U32 offsetCode, U32 matchLength) + U32 offBase, U32 matchLength) { /* literals */ if (ZSTD_compressedLiterals(optPtr)) { @@ -344,8 +367,8 @@ static void ZSTD_updateStats(optState_t* const optPtr, optPtr->litLengthSum++; } - /* offset code : expected to follow storeSeq() numeric representation */ - { U32 const offCode = ZSTD_highbit32(STORED_TO_OFFBASE(offsetCode)); + /* offset code : follows storeSeq() numeric representation */ + { U32 const offCode = ZSTD_highbit32(offBase); assert(offCode <= MaxOff); optPtr->offCodeFreq[offCode]++; optPtr->offCodeSum++; @@ -552,16 +575,17 @@ void ZSTD_updateTree(ZSTD_matchState_t* ms, const BYTE* ip, const BYTE* iend) { ZSTD_updateTree_internal(ms, ip, iend, ms->cParams.minMatch, ZSTD_noDict); } -FORCE_INLINE_TEMPLATE -U32 ZSTD_insertBtAndGetAllMatches ( - ZSTD_match_t* matches, /* store result (found matches) in this table (presumed large enough) */ - ZSTD_matchState_t* ms, - U32* nextToUpdate3, - const BYTE* const ip, const BYTE* const iLimit, const ZSTD_dictMode_e dictMode, - const U32 rep[ZSTD_REP_NUM], - U32 const ll0, /* tells if associated literal length is 0 or not. This value must be 0 or 1 */ - const U32 lengthToBeat, - U32 const mls /* template */) +FORCE_INLINE_TEMPLATE U32 +ZSTD_insertBtAndGetAllMatches ( + ZSTD_match_t* matches, /* store result (found matches) in this table (presumed large enough) */ + ZSTD_matchState_t* ms, + U32* nextToUpdate3, + const BYTE* const ip, const BYTE* const iLimit, + const ZSTD_dictMode_e dictMode, + const U32 rep[ZSTD_REP_NUM], + const U32 ll0, /* tells if associated literal length is 0 or not. This value must be 0 or 1 */ + const U32 lengthToBeat, + const U32 mls /* template */) { const ZSTD_compressionParameters* const cParams = &ms->cParams; U32 const sufficient_len = MIN(cParams->targetLength, ZSTD_OPT_NUM -1); @@ -644,7 +668,7 @@ U32 ZSTD_insertBtAndGetAllMatches ( DEBUGLOG(8, "found repCode %u (ll0:%u, offset:%u) of length %u", repCode, ll0, repOffset, repLen); bestLength = repLen; - matches[mnum].off = STORE_REPCODE(repCode - ll0 + 1); /* expect value between 1 and 3 */ + matches[mnum].off = REPCODE_TO_OFFBASE(repCode - ll0 + 1); /* expect value between 1 and 3 */ matches[mnum].len = (U32)repLen; mnum++; if ( (repLen > sufficient_len) @@ -673,7 +697,7 @@ U32 ZSTD_insertBtAndGetAllMatches ( bestLength = mlen; assert(curr > matchIndex3); assert(mnum==0); /* no prior solution */ - matches[0].off = STORE_OFFSET(curr - matchIndex3); + matches[0].off = OFFSET_TO_OFFBASE(curr - matchIndex3); matches[0].len = (U32)mlen; mnum = 1; if ( (mlen > sufficient_len) | @@ -706,13 +730,13 @@ U32 ZSTD_insertBtAndGetAllMatches ( } if (matchLength > bestLength) { - DEBUGLOG(8, "found match of length %u at distance %u (offCode=%u)", - (U32)matchLength, curr - matchIndex, STORE_OFFSET(curr - matchIndex)); + DEBUGLOG(8, "found match of length %u at distance %u (offBase=%u)", + (U32)matchLength, curr - matchIndex, OFFSET_TO_OFFBASE(curr - matchIndex)); assert(matchEndIdx > matchIndex); if (matchLength > matchEndIdx - matchIndex) matchEndIdx = matchIndex + (U32)matchLength; bestLength = matchLength; - matches[mnum].off = STORE_OFFSET(curr - matchIndex); + matches[mnum].off = OFFSET_TO_OFFBASE(curr - matchIndex); matches[mnum].len = (U32)matchLength; mnum++; if ( (matchLength > ZSTD_OPT_NUM) @@ -754,12 +778,12 @@ U32 ZSTD_insertBtAndGetAllMatches ( if (matchLength > bestLength) { matchIndex = dictMatchIndex + dmsIndexDelta; - DEBUGLOG(8, "found dms match of length %u at distance %u (offCode=%u)", - (U32)matchLength, curr - matchIndex, STORE_OFFSET(curr - matchIndex)); + DEBUGLOG(8, "found dms match of length %u at distance %u (offBase=%u)", + (U32)matchLength, curr - matchIndex, OFFSET_TO_OFFBASE(curr - matchIndex)); if (matchLength > matchEndIdx - matchIndex) matchEndIdx = matchIndex + (U32)matchLength; bestLength = matchLength; - matches[mnum].off = STORE_OFFSET(curr - matchIndex); + matches[mnum].off = OFFSET_TO_OFFBASE(curr - matchIndex); matches[mnum].len = (U32)matchLength; mnum++; if ( (matchLength > ZSTD_OPT_NUM) @@ -960,7 +984,7 @@ static void ZSTD_optLdm_maybeAddMatch(ZSTD_match_t* matches, U32* nbMatches, const ZSTD_optLdm_t* optLdm, U32 currPosInBlock) { U32 const posDiff = currPosInBlock - optLdm->startPosInBlock; - /* Note: ZSTD_match_t actually contains offCode and matchLength (before subtracting MINMATCH) */ + /* Note: ZSTD_match_t actually contains offBase and matchLength (before subtracting MINMATCH) */ U32 const candidateMatchLength = optLdm->endPosInBlock - optLdm->startPosInBlock - posDiff; /* Ensure that current block position is not outside of the match */ @@ -971,11 +995,11 @@ static void ZSTD_optLdm_maybeAddMatch(ZSTD_match_t* matches, U32* nbMatches, } if (*nbMatches == 0 || ((candidateMatchLength > matches[*nbMatches-1].len) && *nbMatches < ZSTD_OPT_NUM)) { - U32 const candidateOffCode = STORE_OFFSET(optLdm->offset); - DEBUGLOG(6, "ZSTD_optLdm_maybeAddMatch(): Adding ldm candidate match (offCode: %u matchLength %u) at block position=%u", - candidateOffCode, candidateMatchLength, currPosInBlock); + U32 const candidateOffBase = OFFSET_TO_OFFBASE(optLdm->offset); + DEBUGLOG(6, "ZSTD_optLdm_maybeAddMatch(): Adding ldm candidate match (offBase: %u matchLength %u) at block position=%u", + candidateOffBase, candidateMatchLength, currPosInBlock); matches[*nbMatches].len = candidateMatchLength; - matches[*nbMatches].off = candidateOffCode; + matches[*nbMatches].off = candidateOffBase; (*nbMatches)++; } } @@ -1062,6 +1086,8 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, ZSTD_optimal_t lastSequence; ZSTD_optLdm_t optLdm; + ZSTD_memset(&lastSequence, 0, sizeof(ZSTD_optimal_t)); + optLdm.seqStore = ms->ldmSeqStore ? *ms->ldmSeqStore : kNullRawSeqStore; optLdm.endPosInBlock = optLdm.startPosInBlock = optLdm.offset = 0; ZSTD_opt_getNextMatchAndUpdateSeqStore(&optLdm, (U32)(ip-istart), (U32)(iend-ip)); @@ -1098,14 +1124,14 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, /* large match -> immediate encoding */ { U32 const maxML = matches[nbMatches-1].len; - U32 const maxOffcode = matches[nbMatches-1].off; - DEBUGLOG(6, "found %u matches of maxLength=%u and maxOffCode=%u at cPos=%u => start new series", - nbMatches, maxML, maxOffcode, (U32)(ip-prefixStart)); + U32 const maxOffBase = matches[nbMatches-1].off; + DEBUGLOG(6, "found %u matches of maxLength=%u and maxOffBase=%u at cPos=%u => start new series", + nbMatches, maxML, maxOffBase, (U32)(ip-prefixStart)); if (maxML > sufficient_len) { lastSequence.litlen = litlen; lastSequence.mlen = maxML; - lastSequence.off = maxOffcode; + lastSequence.off = maxOffBase; DEBUGLOG(6, "large match (%u>%u), immediate encoding", maxML, sufficient_len); cur = 0; @@ -1122,15 +1148,15 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, opt[pos].price = ZSTD_MAX_PRICE; /* mlen, litlen and price will be fixed during forward scanning */ } for (matchNb = 0; matchNb < nbMatches; matchNb++) { - U32 const offcode = matches[matchNb].off; + U32 const offBase = matches[matchNb].off; U32 const end = matches[matchNb].len; for ( ; pos <= end ; pos++ ) { - U32 const matchPrice = ZSTD_getMatchPrice(offcode, pos, optStatePtr, optLevel); + U32 const matchPrice = ZSTD_getMatchPrice(offBase, pos, optStatePtr, optLevel); U32 const sequencePrice = literalsPrice + matchPrice; DEBUGLOG(7, "rPos:%u => set initial price : %.2f", - pos, ZSTD_fCost(sequencePrice)); + pos, ZSTD_fCost((int)sequencePrice)); opt[pos].mlen = pos; - opt[pos].off = offcode; + opt[pos].off = offBase; opt[pos].litlen = litlen; opt[pos].price = (int)sequencePrice; } } @@ -1230,7 +1256,7 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, U32 const startML = (matchNb>0) ? matches[matchNb-1].len+1 : minMatch; U32 mlen; - DEBUGLOG(7, "testing match %u => offCode=%4u, mlen=%2u, llen=%2u", + DEBUGLOG(7, "testing match %u => offBase=%4u, mlen=%2u, llen=%2u", matchNb, matches[matchNb].off, lastML, litlen); for (mlen = lastML; mlen >= startML; mlen--) { /* scan downward */ @@ -1296,7 +1322,7 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, for (storePos=storeStart; storePos <= storeEnd; storePos++) { U32 const llen = opt[storePos].litlen; U32 const mlen = opt[storePos].mlen; - U32 const offCode = opt[storePos].off; + U32 const offBase = opt[storePos].off; U32 const advance = llen + mlen; DEBUGLOG(6, "considering seq starting at %zi, llen=%u, mlen=%u", anchor - istart, (unsigned)llen, (unsigned)mlen); @@ -1308,8 +1334,8 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, } assert(anchor + llen <= iend); - ZSTD_updateStats(optStatePtr, llen, anchor, offCode, mlen); - ZSTD_storeSeq(seqStore, llen, anchor, iend, offCode, mlen); + ZSTD_updateStats(optStatePtr, llen, anchor, offBase, mlen); + ZSTD_storeSeq(seqStore, llen, anchor, iend, offBase, mlen); anchor += advance; ip = anchor; } } @@ -1349,7 +1375,7 @@ size_t ZSTD_compressBlock_btopt( /* ZSTD_initStats_ultra(): * make a first compression pass, just to seed stats with more accurate starting values. * only works on first block, with no dictionary and no ldm. - * this function cannot error, hence its contract must be respected. + * this function cannot error out, its narrow contract must be respected. */ static void ZSTD_initStats_ultra(ZSTD_matchState_t* ms, @@ -1368,7 +1394,7 @@ ZSTD_initStats_ultra(ZSTD_matchState_t* ms, ZSTD_compressBlock_opt2(ms, seqStore, tmpRep, src, srcSize, ZSTD_noDict); /* generate stats into ms->opt*/ - /* invalidate first scan from history */ + /* invalidate first scan from history, only keep entropy stats */ ZSTD_resetSeqStore(seqStore); ms->window.base -= srcSize; ms->window.dictLimit += (U32)srcSize; @@ -1392,20 +1418,20 @@ size_t ZSTD_compressBlock_btultra2( U32 const curr = (U32)((const BYTE*)src - ms->window.base); DEBUGLOG(5, "ZSTD_compressBlock_btultra2 (srcSize=%zu)", srcSize); - /* 2-pass strategy: + /* 2-passes strategy: * this strategy makes a first pass over first block to collect statistics - * and seed next round's statistics with it. - * After 1st pass, function forgets everything, and starts a new block. + * in order to seed next round's statistics with it. + * After 1st pass, function forgets history, and starts a new block. * Consequently, this can only work if no data has been previously loaded in tables, * aka, no dictionary, no prefix, no ldm preprocessing. * The compression ratio gain is generally small (~0.5% on first block), - * the cost is 2x cpu time on first block. */ + ** the cost is 2x cpu time on first block. */ assert(srcSize <= ZSTD_BLOCKSIZE_MAX); if ( (ms->opt.litLengthSum==0) /* first block */ && (seqStore->sequences == seqStore->sequencesStart) /* no ldm */ && (ms->window.dictLimit == ms->window.lowLimit) /* no dictionary */ - && (curr == ms->window.dictLimit) /* start of frame, nothing already loaded nor skipped */ - && (srcSize > ZSTD_PREDEF_THRESHOLD) + && (curr == ms->window.dictLimit) /* start of frame, nothing already loaded nor skipped */ + && (srcSize > ZSTD_PREDEF_THRESHOLD) /* input large enough to not employ default stats */ ) { ZSTD_initStats_ultra(ms, seqStore, rep, src, srcSize); } diff --git a/internal-complibs/zstd-1.5.2/compress/zstd_opt.h b/internal-complibs/zstd-1.5.5/compress/zstd_opt.h similarity index 97% rename from internal-complibs/zstd-1.5.2/compress/zstd_opt.h rename to internal-complibs/zstd-1.5.5/compress/zstd_opt.h index 627255f5..342e5a31 100644 --- a/internal-complibs/zstd-1.5.2/compress/zstd_opt.h +++ b/internal-complibs/zstd-1.5.5/compress/zstd_opt.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the diff --git a/internal-complibs/zstd-1.5.2/compress/zstdmt_compress.c b/internal-complibs/zstd-1.5.5/compress/zstdmt_compress.c similarity index 98% rename from internal-complibs/zstd-1.5.2/compress/zstdmt_compress.c rename to internal-complibs/zstd-1.5.5/compress/zstdmt_compress.c index 6bc14b03..67860755 100644 --- a/internal-complibs/zstd-1.5.2/compress/zstdmt_compress.c +++ b/internal-complibs/zstd-1.5.5/compress/zstdmt_compress.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -20,6 +20,7 @@ /* ====== Dependencies ====== */ +#include "../common/allocations.h" /* ZSTD_customMalloc, ZSTD_customCalloc, ZSTD_customFree */ #include "../common/zstd_deps.h" /* ZSTD_memcpy, ZSTD_memset, INT_MAX, UINT_MAX */ #include "../common/mem.h" /* MEM_STATIC */ #include "../common/pool.h" /* threadpool */ @@ -266,11 +267,11 @@ static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* bufPool, buffer_t buf) * 1 buffer for input loading * 1 buffer for "next input" when submitting current one * 1 buffer stuck in queue */ -#define BUF_POOL_MAX_NB_BUFFERS(nbWorkers) 2*nbWorkers + 3 +#define BUF_POOL_MAX_NB_BUFFERS(nbWorkers) (2*(nbWorkers) + 3) /* After a worker releases its rawSeqStore, it is immediately ready for reuse. * So we only need one seq buffer per worker. */ -#define SEQ_POOL_MAX_NB_BUFFERS(nbWorkers) nbWorkers +#define SEQ_POOL_MAX_NB_BUFFERS(nbWorkers) (nbWorkers) /* ===== Seq Pool Wrapper ====== */ @@ -719,7 +720,7 @@ static void ZSTDMT_compressionJob(void* jobDescription) ZSTDMT_serialState_update(job->serial, cctx, rawSeqStore, job->src, job->jobID); if (!job->firstJob) { /* flush and overwrite frame header when it's not first job */ - size_t const hSize = ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.capacity, job->src.start, 0); + size_t const hSize = ZSTD_compressContinue_public(cctx, dstBuff.start, dstBuff.capacity, job->src.start, 0); if (ZSTD_isError(hSize)) JOB_ERROR(hSize); DEBUGLOG(5, "ZSTDMT_compressionJob: flush and overwrite %u bytes of frame header (not first job)", (U32)hSize); ZSTD_invalidateRepCodes(cctx); @@ -737,7 +738,7 @@ static void ZSTDMT_compressionJob(void* jobDescription) DEBUGLOG(5, "ZSTDMT_compressionJob: compress %u bytes in %i blocks", (U32)job->src.size, nbChunks); assert(job->cSize == 0); for (chunkNb = 1; chunkNb < nbChunks; chunkNb++) { - size_t const cSize = ZSTD_compressContinue(cctx, op, oend-op, ip, chunkSize); + size_t const cSize = ZSTD_compressContinue_public(cctx, op, oend-op, ip, chunkSize); if (ZSTD_isError(cSize)) JOB_ERROR(cSize); ip += chunkSize; op += cSize; assert(op < oend); @@ -757,8 +758,8 @@ static void ZSTDMT_compressionJob(void* jobDescription) size_t const lastBlockSize1 = job->src.size & (chunkSize-1); size_t const lastBlockSize = ((lastBlockSize1==0) & (job->src.size>=chunkSize)) ? chunkSize : lastBlockSize1; size_t const cSize = (job->lastJob) ? - ZSTD_compressEnd (cctx, op, oend-op, ip, lastBlockSize) : - ZSTD_compressContinue(cctx, op, oend-op, ip, lastBlockSize); + ZSTD_compressEnd_public(cctx, op, oend-op, ip, lastBlockSize) : + ZSTD_compressContinue_public(cctx, op, oend-op, ip, lastBlockSize); if (ZSTD_isError(cSize)) JOB_ERROR(cSize); lastCBlockSize = cSize; } } @@ -1734,7 +1735,7 @@ findSynchronizationPoint(ZSTDMT_CCtx const* mtctx, ZSTD_inBuffer const input) } } else { /* We have enough bytes buffered to initialize the hash, - * and are have processed enough bytes to find a sync point. + * and have processed enough bytes to find a sync point. * Start scanning at the beginning of the input. */ assert(mtctx->inBuff.filled >= RSYNC_MIN_BLOCK_SIZE); @@ -1761,17 +1762,24 @@ findSynchronizationPoint(ZSTDMT_CCtx const* mtctx, ZSTD_inBuffer const input) * then a block will be emitted anyways, but this is okay, since if we * are already synchronized we will remain synchronized. */ + assert(pos < RSYNC_LENGTH || ZSTD_rollingHash_compute(istart + pos - RSYNC_LENGTH, RSYNC_LENGTH) == hash); for (; pos < syncPoint.toLoad; ++pos) { BYTE const toRemove = pos < RSYNC_LENGTH ? prev[pos] : istart[pos - RSYNC_LENGTH]; - assert(pos < RSYNC_LENGTH || ZSTD_rollingHash_compute(istart + pos - RSYNC_LENGTH, RSYNC_LENGTH) == hash); + /* This assert is very expensive, and Debian compiles with asserts enabled. + * So disable it for now. We can get similar coverage by checking it at the + * beginning & end of the loop. + * assert(pos < RSYNC_LENGTH || ZSTD_rollingHash_compute(istart + pos - RSYNC_LENGTH, RSYNC_LENGTH) == hash); + */ hash = ZSTD_rollingHash_rotate(hash, toRemove, istart[pos], primePower); assert(mtctx->inBuff.filled + pos >= RSYNC_MIN_BLOCK_SIZE); if ((hash & hitMask) == hitMask) { syncPoint.toLoad = pos + 1; syncPoint.flush = 1; + ++pos; /* for assert */ break; } } + assert(pos < RSYNC_LENGTH || ZSTD_rollingHash_compute(istart + pos - RSYNC_LENGTH, RSYNC_LENGTH) == hash); return syncPoint; } diff --git a/internal-complibs/zstd-1.5.2/compress/zstdmt_compress.h b/internal-complibs/zstd-1.5.5/compress/zstdmt_compress.h similarity index 98% rename from internal-complibs/zstd-1.5.2/compress/zstdmt_compress.h rename to internal-complibs/zstd-1.5.5/compress/zstdmt_compress.h index 271eb1ac..ed4dc0e9 100644 --- a/internal-complibs/zstd-1.5.2/compress/zstdmt_compress.h +++ b/internal-complibs/zstd-1.5.5/compress/zstdmt_compress.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the diff --git a/internal-complibs/zstd-1.5.2/decompress/huf_decompress.c b/internal-complibs/zstd-1.5.5/decompress/huf_decompress.c similarity index 75% rename from internal-complibs/zstd-1.5.2/decompress/huf_decompress.c rename to internal-complibs/zstd-1.5.5/decompress/huf_decompress.c index 20271882..5b217ac5 100644 --- a/internal-complibs/zstd-1.5.2/decompress/huf_decompress.c +++ b/internal-complibs/zstd-1.5.5/decompress/huf_decompress.c @@ -1,7 +1,7 @@ /* ****************************************************************** * huff0 huffman decoder, * part of Finite State Entropy library - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : * - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy @@ -19,10 +19,10 @@ #include "../common/compiler.h" #include "../common/bitstream.h" /* BIT_* */ #include "../common/fse.h" /* to compress headers */ -#define HUF_STATIC_LINKING_ONLY #include "../common/huf.h" #include "../common/error_private.h" #include "../common/zstd_internal.h" +#include "../common/bits.h" /* ZSTD_highbit32, ZSTD_countTrailingZeros64 */ /* ************************************************************** * Constants @@ -43,10 +43,14 @@ #error "Cannot force the use of the X1 and X2 decoders at the same time!" #endif -#if ZSTD_ENABLE_ASM_X86_64_BMI2 && DYNAMIC_BMI2 -# define HUF_ASM_X86_64_BMI2_ATTRS BMI2_TARGET_ATTRIBUTE +/* When DYNAMIC_BMI2 is enabled, fast decoders are only called when bmi2 is + * supported at runtime, so we can add the BMI2 target attribute. + * When it is disabled, we will still get BMI2 if it is enabled statically. + */ +#if DYNAMIC_BMI2 +# define HUF_FAST_BMI2_ATTRS BMI2_TARGET_ATTRIBUTE #else -# define HUF_ASM_X86_64_BMI2_ATTRS +# define HUF_FAST_BMI2_ATTRS #endif #ifdef __cplusplus @@ -56,18 +60,12 @@ #endif #define HUF_ASM_DECL HUF_EXTERN_C -#if DYNAMIC_BMI2 || (ZSTD_ENABLE_ASM_X86_64_BMI2 && defined(__BMI2__)) +#if DYNAMIC_BMI2 # define HUF_NEED_BMI2_FUNCTION 1 #else # define HUF_NEED_BMI2_FUNCTION 0 #endif -#if !(ZSTD_ENABLE_ASM_X86_64_BMI2 && defined(__BMI2__)) -# define HUF_NEED_DEFAULT_FUNCTION 1 -#else -# define HUF_NEED_DEFAULT_FUNCTION 0 -#endif - /* ************************************************************** * Error Management ****************************************************************/ @@ -84,6 +82,11 @@ /* ************************************************************** * BMI2 Variant Wrappers ****************************************************************/ +typedef size_t (*HUF_DecompressUsingDTableFn)(void *dst, size_t dstSize, + const void *cSrc, + size_t cSrcSize, + const HUF_DTable *DTable); + #if DYNAMIC_BMI2 #define HUF_DGEN(fn) \ @@ -105,9 +108,9 @@ } \ \ static size_t fn(void* dst, size_t dstSize, void const* cSrc, \ - size_t cSrcSize, HUF_DTable const* DTable, int bmi2) \ + size_t cSrcSize, HUF_DTable const* DTable, int flags) \ { \ - if (bmi2) { \ + if (flags & HUF_flags_bmi2) { \ return fn##_bmi2(dst, dstSize, cSrc, cSrcSize, DTable); \ } \ return fn##_default(dst, dstSize, cSrc, cSrcSize, DTable); \ @@ -117,9 +120,9 @@ #define HUF_DGEN(fn) \ static size_t fn(void* dst, size_t dstSize, void const* cSrc, \ - size_t cSrcSize, HUF_DTable const* DTable, int bmi2) \ + size_t cSrcSize, HUF_DTable const* DTable, int flags) \ { \ - (void)bmi2; \ + (void)flags; \ return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \ } @@ -138,15 +141,28 @@ static DTableDesc HUF_getDTableDesc(const HUF_DTable* table) return dtd; } -#if ZSTD_ENABLE_ASM_X86_64_BMI2 - -static size_t HUF_initDStream(BYTE const* ip) { +static size_t HUF_initFastDStream(BYTE const* ip) { BYTE const lastByte = ip[7]; - size_t const bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0; + size_t const bitsConsumed = lastByte ? 8 - ZSTD_highbit32(lastByte) : 0; size_t const value = MEM_readLEST(ip) | 1; assert(bitsConsumed <= 8); + assert(sizeof(size_t) == 8); return value << bitsConsumed; } + + +/** + * The input/output arguments to the Huffman fast decoding loop: + * + * ip [in/out] - The input pointers, must be updated to reflect what is consumed. + * op [in/out] - The output pointers, must be updated to reflect what is written. + * bits [in/out] - The bitstream containers, must be updated to reflect the current state. + * dt [in] - The decoding table. + * ilimit [in] - The input limit, stop when any input pointer is below ilimit. + * oend [in] - The end of the output stream. op[3] must not cross oend. + * iend [in] - The end of each input stream. ip[i] may cross iend[i], + * as long as it is above ilimit, but that indicates corruption. + */ typedef struct { BYTE const* ip[4]; BYTE* op[4]; @@ -155,15 +171,17 @@ typedef struct { BYTE const* ilimit; BYTE* oend; BYTE const* iend[4]; -} HUF_DecompressAsmArgs; +} HUF_DecompressFastArgs; + +typedef void (*HUF_DecompressFastLoopFn)(HUF_DecompressFastArgs*); /** - * Initializes args for the asm decoding loop. - * @returns 0 on success - * 1 if the fallback implementation should be used. + * Initializes args for the fast decoding loop. + * @returns 1 on success + * 0 if the fallback implementation should be used. * Or an error code on failure. */ -static size_t HUF_DecompressAsmArgs_init(HUF_DecompressAsmArgs* args, void* dst, size_t dstSize, void const* src, size_t srcSize, const HUF_DTable* DTable) +static size_t HUF_DecompressFastArgs_init(HUF_DecompressFastArgs* args, void* dst, size_t dstSize, void const* src, size_t srcSize, const HUF_DTable* DTable) { void const* dt = DTable + 1; U32 const dtLog = HUF_getDTableDesc(DTable).tableLog; @@ -172,9 +190,11 @@ static size_t HUF_DecompressAsmArgs_init(HUF_DecompressAsmArgs* args, void* dst, BYTE* const oend = (BYTE*)dst + dstSize; - /* The following condition is false on x32 platform, - * but HUF_asm is not compatible with this ABI */ - if (!(MEM_isLittleEndian() && !MEM_32bits())) return 1; + /* The fast decoding loop assumes 64-bit little-endian. + * This condition is false on x32. + */ + if (!MEM_isLittleEndian() || MEM_32bits()) + return 0; /* strict minimum : jump table + 1 byte per stream */ if (srcSize < 10) @@ -185,7 +205,7 @@ static size_t HUF_DecompressAsmArgs_init(HUF_DecompressAsmArgs* args, void* dst, * On small inputs we don't have enough data to trigger the fast loop, so use the old decoder. */ if (dtLog != HUF_DECODER_FAST_TABLELOG) - return 1; + return 0; /* Read the jump table. */ { @@ -199,13 +219,13 @@ static size_t HUF_DecompressAsmArgs_init(HUF_DecompressAsmArgs* args, void* dst, args->iend[2] = args->iend[1] + length2; args->iend[3] = args->iend[2] + length3; - /* HUF_initDStream() requires this, and this small of an input + /* HUF_initFastDStream() requires this, and this small of an input * won't benefit from the ASM loop anyways. * length1 must be >= 16 so that ip[0] >= ilimit before the loop * starts. */ if (length1 < 16 || length2 < 8 || length3 < 8 || length4 < 8) - return 1; + return 0; if (length4 > srcSize) return ERROR(corruption_detected); /* overflow */ } /* ip[] contains the position that is currently loaded into bits[]. */ @@ -222,7 +242,7 @@ static size_t HUF_DecompressAsmArgs_init(HUF_DecompressAsmArgs* args, void* dst, /* No point to call the ASM loop for tiny outputs. */ if (args->op[3] >= oend) - return 1; + return 0; /* bits[] is the bit container. * It is read from the MSB down to the LSB. @@ -231,10 +251,10 @@ static size_t HUF_DecompressAsmArgs_init(HUF_DecompressAsmArgs* args, void* dst, * set, so that CountTrailingZeros(bits[]) can be used * to count how many bits we've consumed. */ - args->bits[0] = HUF_initDStream(args->ip[0]); - args->bits[1] = HUF_initDStream(args->ip[1]); - args->bits[2] = HUF_initDStream(args->ip[2]); - args->bits[3] = HUF_initDStream(args->ip[3]); + args->bits[0] = HUF_initFastDStream(args->ip[0]); + args->bits[1] = HUF_initFastDStream(args->ip[1]); + args->bits[2] = HUF_initFastDStream(args->ip[2]); + args->bits[3] = HUF_initFastDStream(args->ip[3]); /* If ip[] >= ilimit, it is guaranteed to be safe to * reload bits[]. It may be beyond its section, but is @@ -245,10 +265,10 @@ static size_t HUF_DecompressAsmArgs_init(HUF_DecompressAsmArgs* args, void* dst, args->oend = oend; args->dt = dt; - return 0; + return 1; } -static size_t HUF_initRemainingDStream(BIT_DStream_t* bit, HUF_DecompressAsmArgs const* args, int stream, BYTE* segmentEnd) +static size_t HUF_initRemainingDStream(BIT_DStream_t* bit, HUF_DecompressFastArgs const* args, int stream, BYTE* segmentEnd) { /* Validate that we haven't overwritten. */ if (args->op[stream] > segmentEnd) @@ -262,15 +282,15 @@ static size_t HUF_initRemainingDStream(BIT_DStream_t* bit, HUF_DecompressAsmArgs return ERROR(corruption_detected); /* Construct the BIT_DStream_t. */ - bit->bitContainer = MEM_readLE64(args->ip[stream]); - bit->bitsConsumed = ZSTD_countTrailingZeros((size_t)args->bits[stream]); + assert(sizeof(size_t) == 8); + bit->bitContainer = MEM_readLEST(args->ip[stream]); + bit->bitsConsumed = ZSTD_countTrailingZeros64(args->bits[stream]); bit->start = (const char*)args->iend[0]; bit->limitPtr = bit->start + sizeof(size_t); bit->ptr = (const char*)args->ip[stream]; return 0; } -#endif #ifndef HUF_FORCE_DECOMPRESS_X2 @@ -287,10 +307,11 @@ typedef struct { BYTE nbBits; BYTE byte; } HUF_DEltX1; /* single-symbol decodi static U64 HUF_DEltX1_set4(BYTE symbol, BYTE nbBits) { U64 D4; if (MEM_isLittleEndian()) { - D4 = (symbol << 8) + nbBits; + D4 = (U64)((symbol << 8) + nbBits); } else { - D4 = symbol + (nbBits << 8); + D4 = (U64)(symbol + (nbBits << 8)); } + assert(D4 < (1U << 16)); D4 *= 0x0001000100010001ULL; return D4; } @@ -333,13 +354,7 @@ typedef struct { BYTE huffWeight[HUF_SYMBOLVALUE_MAX + 1]; } HUF_ReadDTableX1_Workspace; - -size_t HUF_readDTableX1_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize) -{ - return HUF_readDTableX1_wksp_bmi2(DTable, src, srcSize, workSpace, wkspSize, /* bmi2 */ 0); -} - -size_t HUF_readDTableX1_wksp_bmi2(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize, int bmi2) +size_t HUF_readDTableX1_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize, int flags) { U32 tableLog = 0; U32 nbSymbols = 0; @@ -354,7 +369,7 @@ size_t HUF_readDTableX1_wksp_bmi2(HUF_DTable* DTable, const void* src, size_t sr DEBUG_STATIC_ASSERT(sizeof(DTableDesc) == sizeof(HUF_DTable)); /* ZSTD_memset(huffWeight, 0, sizeof(huffWeight)); */ /* is not necessary, even though some analyzer complain ... */ - iSize = HUF_readStats_wksp(wksp->huffWeight, HUF_SYMBOLVALUE_MAX + 1, wksp->rankVal, &nbSymbols, &tableLog, src, srcSize, wksp->statsWksp, sizeof(wksp->statsWksp), bmi2); + iSize = HUF_readStats_wksp(wksp->huffWeight, HUF_SYMBOLVALUE_MAX + 1, wksp->rankVal, &nbSymbols, &tableLog, src, srcSize, wksp->statsWksp, sizeof(wksp->statsWksp), flags); if (HUF_isError(iSize)) return iSize; @@ -381,9 +396,8 @@ size_t HUF_readDTableX1_wksp_bmi2(HUF_DTable* DTable, const void* src, size_t sr * rankStart[0] is not filled because there are no entries in the table for * weight 0. */ - { - int n; - int nextRankStart = 0; + { int n; + U32 nextRankStart = 0; int const unroll = 4; int const nLimit = (int)nbSymbols - unroll + 1; for (n=0; n<(int)tableLog+1; n++) { @@ -410,10 +424,9 @@ size_t HUF_readDTableX1_wksp_bmi2(HUF_DTable* DTable, const void* src, size_t sr * We can switch based on the length to a different inner loop which is * optimized for that particular case. */ - { - U32 w; - int symbol=wksp->rankVal[0]; - int rankStart=0; + { U32 w; + int symbol = wksp->rankVal[0]; + int rankStart = 0; for (w=1; wrankVal[w]; int const length = (1 << w) >> 1; @@ -523,7 +536,7 @@ HUF_decodeStreamX1(BYTE* p, BIT_DStream_t* const bitDPtr, BYTE* const pEnd, cons while (p < pEnd) HUF_DECODE_SYMBOLX1_0(p, bitDPtr); - return pEnd-pStart; + return (size_t)(pEnd-pStart); } FORCE_INLINE_TEMPLATE size_t @@ -549,6 +562,10 @@ HUF_decompress1X1_usingDTable_internal_body( return dstSize; } +/* HUF_decompress4X1_usingDTable_internal_body(): + * Conditions : + * @dstSize >= 6 + */ FORCE_INLINE_TEMPLATE size_t HUF_decompress4X1_usingDTable_internal_body( void* dst, size_t dstSize, @@ -592,6 +609,7 @@ HUF_decompress4X1_usingDTable_internal_body( if (length4 > cSrcSize) return ERROR(corruption_detected); /* overflow */ if (opStart4 > oend) return ERROR(corruption_detected); /* overflow */ + if (dstSize < 6) return ERROR(corruption_detected); /* stream 4-split doesn't work */ CHECK_F( BIT_initDStream(&bitD1, istart1, length1) ); CHECK_F( BIT_initDStream(&bitD2, istart2, length2) ); CHECK_F( BIT_initDStream(&bitD3, istart3, length3) ); @@ -654,38 +672,142 @@ size_t HUF_decompress4X1_usingDTable_internal_bmi2(void* dst, size_t dstSize, vo } #endif -#if HUF_NEED_DEFAULT_FUNCTION static size_t HUF_decompress4X1_usingDTable_internal_default(void* dst, size_t dstSize, void const* cSrc, size_t cSrcSize, HUF_DTable const* DTable) { return HUF_decompress4X1_usingDTable_internal_body(dst, dstSize, cSrc, cSrcSize, DTable); } -#endif #if ZSTD_ENABLE_ASM_X86_64_BMI2 -HUF_ASM_DECL void HUF_decompress4X1_usingDTable_internal_bmi2_asm_loop(HUF_DecompressAsmArgs* args) ZSTDLIB_HIDDEN; +HUF_ASM_DECL void HUF_decompress4X1_usingDTable_internal_fast_asm_loop(HUF_DecompressFastArgs* args) ZSTDLIB_HIDDEN; -static HUF_ASM_X86_64_BMI2_ATTRS +#endif + +static HUF_FAST_BMI2_ATTRS +void HUF_decompress4X1_usingDTable_internal_fast_c_loop(HUF_DecompressFastArgs* args) +{ + U64 bits[4]; + BYTE const* ip[4]; + BYTE* op[4]; + U16 const* const dtable = (U16 const*)args->dt; + BYTE* const oend = args->oend; + BYTE const* const ilimit = args->ilimit; + + /* Copy the arguments to local variables */ + ZSTD_memcpy(&bits, &args->bits, sizeof(bits)); + ZSTD_memcpy((void*)(&ip), &args->ip, sizeof(ip)); + ZSTD_memcpy(&op, &args->op, sizeof(op)); + + assert(MEM_isLittleEndian()); + assert(!MEM_32bits()); + + for (;;) { + BYTE* olimit; + int stream; + int symbol; + + /* Assert loop preconditions */ +#ifndef NDEBUG + for (stream = 0; stream < 4; ++stream) { + assert(op[stream] <= (stream == 3 ? oend : op[stream + 1])); + assert(ip[stream] >= ilimit); + } +#endif + /* Compute olimit */ + { + /* Each iteration produces 5 output symbols per stream */ + size_t const oiters = (size_t)(oend - op[3]) / 5; + /* Each iteration consumes up to 11 bits * 5 = 55 bits < 7 bytes + * per stream. + */ + size_t const iiters = (size_t)(ip[0] - ilimit) / 7; + /* We can safely run iters iterations before running bounds checks */ + size_t const iters = MIN(oiters, iiters); + size_t const symbols = iters * 5; + + /* We can simply check that op[3] < olimit, instead of checking all + * of our bounds, since we can't hit the other bounds until we've run + * iters iterations, which only happens when op[3] == olimit. + */ + olimit = op[3] + symbols; + + /* Exit fast decoding loop once we get close to the end. */ + if (op[3] + 20 > olimit) + break; + + /* Exit the decoding loop if any input pointer has crossed the + * previous one. This indicates corruption, and a precondition + * to our loop is that ip[i] >= ip[0]. + */ + for (stream = 1; stream < 4; ++stream) { + if (ip[stream] < ip[stream - 1]) + goto _out; + } + } + +#ifndef NDEBUG + for (stream = 1; stream < 4; ++stream) { + assert(ip[stream] >= ip[stream - 1]); + } +#endif + + do { + /* Decode 5 symbols in each of the 4 streams */ + for (symbol = 0; symbol < 5; ++symbol) { + for (stream = 0; stream < 4; ++stream) { + int const index = (int)(bits[stream] >> 53); + int const entry = (int)dtable[index]; + bits[stream] <<= (entry & 63); + op[stream][symbol] = (BYTE)((entry >> 8) & 0xFF); + } + } + /* Reload the bitstreams */ + for (stream = 0; stream < 4; ++stream) { + int const ctz = ZSTD_countTrailingZeros64(bits[stream]); + int const nbBits = ctz & 7; + int const nbBytes = ctz >> 3; + op[stream] += 5; + ip[stream] -= nbBytes; + bits[stream] = MEM_read64(ip[stream]) | 1; + bits[stream] <<= nbBits; + } + } while (op[3] < olimit); + } + +_out: + + /* Save the final values of each of the state variables back to args. */ + ZSTD_memcpy(&args->bits, &bits, sizeof(bits)); + ZSTD_memcpy((void*)(&args->ip), &ip, sizeof(ip)); + ZSTD_memcpy(&args->op, &op, sizeof(op)); +} + +/** + * @returns @p dstSize on success (>= 6) + * 0 if the fallback implementation should be used + * An error if an error occurred + */ +static HUF_FAST_BMI2_ATTRS size_t -HUF_decompress4X1_usingDTable_internal_bmi2_asm( +HUF_decompress4X1_usingDTable_internal_fast( void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, - const HUF_DTable* DTable) + const HUF_DTable* DTable, + HUF_DecompressFastLoopFn loopFn) { void const* dt = DTable + 1; const BYTE* const iend = (const BYTE*)cSrc + 6; BYTE* const oend = (BYTE*)dst + dstSize; - HUF_DecompressAsmArgs args; - { - size_t const ret = HUF_DecompressAsmArgs_init(&args, dst, dstSize, cSrc, cSrcSize, DTable); - FORWARD_IF_ERROR(ret, "Failed to init asm args"); - if (ret != 0) - return HUF_decompress4X1_usingDTable_internal_bmi2(dst, dstSize, cSrc, cSrcSize, DTable); + HUF_DecompressFastArgs args; + { size_t const ret = HUF_DecompressFastArgs_init(&args, dst, dstSize, cSrc, cSrcSize, DTable); + FORWARD_IF_ERROR(ret, "Failed to init fast loop args"); + if (ret == 0) + return 0; } assert(args.ip[0] >= args.ilimit); - HUF_decompress4X1_usingDTable_internal_bmi2_asm_loop(&args); + loopFn(&args); /* Our loop guarantees that ip[] >= ilimit and that we haven't * overwritten any op[]. @@ -698,8 +820,7 @@ HUF_decompress4X1_usingDTable_internal_bmi2_asm( (void)iend; /* finish bit streams one by one. */ - { - size_t const segmentSize = (dstSize+3) / 4; + { size_t const segmentSize = (dstSize+3) / 4; BYTE* segmentEnd = (BYTE*)dst; int i; for (i = 0; i < 4; ++i) { @@ -716,97 +837,59 @@ HUF_decompress4X1_usingDTable_internal_bmi2_asm( } /* decoded size */ + assert(dstSize != 0); return dstSize; } -#endif /* ZSTD_ENABLE_ASM_X86_64_BMI2 */ - -typedef size_t (*HUF_decompress_usingDTable_t)(void *dst, size_t dstSize, - const void *cSrc, - size_t cSrcSize, - const HUF_DTable *DTable); HUF_DGEN(HUF_decompress1X1_usingDTable_internal) static size_t HUF_decompress4X1_usingDTable_internal(void* dst, size_t dstSize, void const* cSrc, - size_t cSrcSize, HUF_DTable const* DTable, int bmi2) + size_t cSrcSize, HUF_DTable const* DTable, int flags) { + HUF_DecompressUsingDTableFn fallbackFn = HUF_decompress4X1_usingDTable_internal_default; + HUF_DecompressFastLoopFn loopFn = HUF_decompress4X1_usingDTable_internal_fast_c_loop; + #if DYNAMIC_BMI2 - if (bmi2) { + if (flags & HUF_flags_bmi2) { + fallbackFn = HUF_decompress4X1_usingDTable_internal_bmi2; # if ZSTD_ENABLE_ASM_X86_64_BMI2 - return HUF_decompress4X1_usingDTable_internal_bmi2_asm(dst, dstSize, cSrc, cSrcSize, DTable); -# else - return HUF_decompress4X1_usingDTable_internal_bmi2(dst, dstSize, cSrc, cSrcSize, DTable); + if (!(flags & HUF_flags_disableAsm)) { + loopFn = HUF_decompress4X1_usingDTable_internal_fast_asm_loop; + } # endif + } else { + return fallbackFn(dst, dstSize, cSrc, cSrcSize, DTable); } -#else - (void)bmi2; #endif #if ZSTD_ENABLE_ASM_X86_64_BMI2 && defined(__BMI2__) - return HUF_decompress4X1_usingDTable_internal_bmi2_asm(dst, dstSize, cSrc, cSrcSize, DTable); -#else - return HUF_decompress4X1_usingDTable_internal_default(dst, dstSize, cSrc, cSrcSize, DTable); + if (!(flags & HUF_flags_disableAsm)) { + loopFn = HUF_decompress4X1_usingDTable_internal_fast_asm_loop; + } #endif -} - -size_t HUF_decompress1X1_usingDTable( - void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize, - const HUF_DTable* DTable) -{ - DTableDesc dtd = HUF_getDTableDesc(DTable); - if (dtd.tableType != 0) return ERROR(GENERIC); - return HUF_decompress1X1_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); -} - -size_t HUF_decompress1X1_DCtx_wksp(HUF_DTable* DCtx, void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize, - void* workSpace, size_t wkspSize) -{ - const BYTE* ip = (const BYTE*) cSrc; - - size_t const hSize = HUF_readDTableX1_wksp(DCtx, cSrc, cSrcSize, workSpace, wkspSize); - if (HUF_isError(hSize)) return hSize; - if (hSize >= cSrcSize) return ERROR(srcSize_wrong); - ip += hSize; cSrcSize -= hSize; - - return HUF_decompress1X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx, /* bmi2 */ 0); -} - - -size_t HUF_decompress4X1_usingDTable( - void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize, - const HUF_DTable* DTable) -{ - DTableDesc dtd = HUF_getDTableDesc(DTable); - if (dtd.tableType != 0) return ERROR(GENERIC); - return HUF_decompress4X1_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); + if (!(flags & HUF_flags_disableFast)) { + size_t const ret = HUF_decompress4X1_usingDTable_internal_fast(dst, dstSize, cSrc, cSrcSize, DTable, loopFn); + if (ret != 0) + return ret; + } + return fallbackFn(dst, dstSize, cSrc, cSrcSize, DTable); } -static size_t HUF_decompress4X1_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, +static size_t HUF_decompress4X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, - void* workSpace, size_t wkspSize, int bmi2) + void* workSpace, size_t wkspSize, int flags) { const BYTE* ip = (const BYTE*) cSrc; - size_t const hSize = HUF_readDTableX1_wksp_bmi2(dctx, cSrc, cSrcSize, workSpace, wkspSize, bmi2); + size_t const hSize = HUF_readDTableX1_wksp(dctx, cSrc, cSrcSize, workSpace, wkspSize, flags); if (HUF_isError(hSize)) return hSize; if (hSize >= cSrcSize) return ERROR(srcSize_wrong); ip += hSize; cSrcSize -= hSize; - return HUF_decompress4X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2); -} - -size_t HUF_decompress4X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize, - void* workSpace, size_t wkspSize) -{ - return HUF_decompress4X1_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, 0); + return HUF_decompress4X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, flags); } - #endif /* HUF_FORCE_DECOMPRESS_X2 */ @@ -989,7 +1072,7 @@ static void HUF_fillDTableX2Level2(HUF_DEltX2* DTable, U32 targetLog, const U32 static void HUF_fillDTableX2(HUF_DEltX2* DTable, const U32 targetLog, const sortedSymbol_t* sortedList, - const U32* rankStart, rankVal_t rankValOrigin, const U32 maxWeight, + const U32* rankStart, rankValCol_t* rankValOrigin, const U32 maxWeight, const U32 nbBitsBaseline) { U32* const rankVal = rankValOrigin[0]; @@ -1044,14 +1127,7 @@ typedef struct { size_t HUF_readDTableX2_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, - void* workSpace, size_t wkspSize) -{ - return HUF_readDTableX2_wksp_bmi2(DTable, src, srcSize, workSpace, wkspSize, /* bmi2 */ 0); -} - -size_t HUF_readDTableX2_wksp_bmi2(HUF_DTable* DTable, - const void* src, size_t srcSize, - void* workSpace, size_t wkspSize, int bmi2) + void* workSpace, size_t wkspSize, int flags) { U32 tableLog, maxW, nbSymbols; DTableDesc dtd = HUF_getDTableDesc(DTable); @@ -1073,7 +1149,7 @@ size_t HUF_readDTableX2_wksp_bmi2(HUF_DTable* DTable, if (maxTableLog > HUF_TABLELOG_MAX) return ERROR(tableLog_tooLarge); /* ZSTD_memset(weightList, 0, sizeof(weightList)); */ /* is not necessary, even though some analyzer complain ... */ - iSize = HUF_readStats_wksp(wksp->weightList, HUF_SYMBOLVALUE_MAX + 1, wksp->rankStats, &nbSymbols, &tableLog, src, srcSize, wksp->calleeWksp, sizeof(wksp->calleeWksp), bmi2); + iSize = HUF_readStats_wksp(wksp->weightList, HUF_SYMBOLVALUE_MAX + 1, wksp->rankStats, &nbSymbols, &tableLog, src, srcSize, wksp->calleeWksp, sizeof(wksp->calleeWksp), flags); if (HUF_isError(iSize)) return iSize; /* check result */ @@ -1244,6 +1320,11 @@ HUF_decompress1X2_usingDTable_internal_body( /* decoded size */ return dstSize; } + +/* HUF_decompress4X2_usingDTable_internal_body(): + * Conditions: + * @dstSize >= 6 + */ FORCE_INLINE_TEMPLATE size_t HUF_decompress4X2_usingDTable_internal_body( void* dst, size_t dstSize, @@ -1284,8 +1365,9 @@ HUF_decompress4X2_usingDTable_internal_body( DTableDesc const dtd = HUF_getDTableDesc(DTable); U32 const dtLog = dtd.tableLog; - if (length4 > cSrcSize) return ERROR(corruption_detected); /* overflow */ - if (opStart4 > oend) return ERROR(corruption_detected); /* overflow */ + if (length4 > cSrcSize) return ERROR(corruption_detected); /* overflow */ + if (opStart4 > oend) return ERROR(corruption_detected); /* overflow */ + if (dstSize < 6) return ERROR(corruption_detected); /* stream 4-split doesn't work */ CHECK_F( BIT_initDStream(&bitD1, istart1, length1) ); CHECK_F( BIT_initDStream(&bitD2, istart2, length2) ); CHECK_F( BIT_initDStream(&bitD3, istart3, length3) ); @@ -1370,36 +1452,177 @@ size_t HUF_decompress4X2_usingDTable_internal_bmi2(void* dst, size_t dstSize, vo } #endif -#if HUF_NEED_DEFAULT_FUNCTION static size_t HUF_decompress4X2_usingDTable_internal_default(void* dst, size_t dstSize, void const* cSrc, size_t cSrcSize, HUF_DTable const* DTable) { return HUF_decompress4X2_usingDTable_internal_body(dst, dstSize, cSrc, cSrcSize, DTable); } -#endif #if ZSTD_ENABLE_ASM_X86_64_BMI2 -HUF_ASM_DECL void HUF_decompress4X2_usingDTable_internal_bmi2_asm_loop(HUF_DecompressAsmArgs* args) ZSTDLIB_HIDDEN; +HUF_ASM_DECL void HUF_decompress4X2_usingDTable_internal_fast_asm_loop(HUF_DecompressFastArgs* args) ZSTDLIB_HIDDEN; + +#endif + +static HUF_FAST_BMI2_ATTRS +void HUF_decompress4X2_usingDTable_internal_fast_c_loop(HUF_DecompressFastArgs* args) +{ + U64 bits[4]; + BYTE const* ip[4]; + BYTE* op[4]; + BYTE* oend[4]; + HUF_DEltX2 const* const dtable = (HUF_DEltX2 const*)args->dt; + BYTE const* const ilimit = args->ilimit; + + /* Copy the arguments to local registers. */ + ZSTD_memcpy(&bits, &args->bits, sizeof(bits)); + ZSTD_memcpy((void*)(&ip), &args->ip, sizeof(ip)); + ZSTD_memcpy(&op, &args->op, sizeof(op)); + + oend[0] = op[1]; + oend[1] = op[2]; + oend[2] = op[3]; + oend[3] = args->oend; + + assert(MEM_isLittleEndian()); + assert(!MEM_32bits()); + + for (;;) { + BYTE* olimit; + int stream; + int symbol; + + /* Assert loop preconditions */ +#ifndef NDEBUG + for (stream = 0; stream < 4; ++stream) { + assert(op[stream] <= oend[stream]); + assert(ip[stream] >= ilimit); + } +#endif + /* Compute olimit */ + { + /* Each loop does 5 table lookups for each of the 4 streams. + * Each table lookup consumes up to 11 bits of input, and produces + * up to 2 bytes of output. + */ + /* We can consume up to 7 bytes of input per iteration per stream. + * We also know that each input pointer is >= ip[0]. So we can run + * iters loops before running out of input. + */ + size_t iters = (size_t)(ip[0] - ilimit) / 7; + /* Each iteration can produce up to 10 bytes of output per stream. + * Each output stream my advance at different rates. So take the + * minimum number of safe iterations among all the output streams. + */ + for (stream = 0; stream < 4; ++stream) { + size_t const oiters = (size_t)(oend[stream] - op[stream]) / 10; + iters = MIN(iters, oiters); + } + + /* Each iteration produces at least 5 output symbols. So until + * op[3] crosses olimit, we know we haven't executed iters + * iterations yet. This saves us maintaining an iters counter, + * at the expense of computing the remaining # of iterations + * more frequently. + */ + olimit = op[3] + (iters * 5); + + /* Exit the fast decoding loop if we are too close to the end. */ + if (op[3] + 10 > olimit) + break; + + /* Exit the decoding loop if any input pointer has crossed the + * previous one. This indicates corruption, and a precondition + * to our loop is that ip[i] >= ip[0]. + */ + for (stream = 1; stream < 4; ++stream) { + if (ip[stream] < ip[stream - 1]) + goto _out; + } + } + +#ifndef NDEBUG + for (stream = 1; stream < 4; ++stream) { + assert(ip[stream] >= ip[stream - 1]); + } +#endif + + do { + /* Do 5 table lookups for each of the first 3 streams */ + for (symbol = 0; symbol < 5; ++symbol) { + for (stream = 0; stream < 3; ++stream) { + int const index = (int)(bits[stream] >> 53); + HUF_DEltX2 const entry = dtable[index]; + MEM_write16(op[stream], entry.sequence); + bits[stream] <<= (entry.nbBits); + op[stream] += (entry.length); + } + } + /* Do 1 table lookup from the final stream */ + { + int const index = (int)(bits[3] >> 53); + HUF_DEltX2 const entry = dtable[index]; + MEM_write16(op[3], entry.sequence); + bits[3] <<= (entry.nbBits); + op[3] += (entry.length); + } + /* Do 4 table lookups from the final stream & reload bitstreams */ + for (stream = 0; stream < 4; ++stream) { + /* Do a table lookup from the final stream. + * This is interleaved with the reloading to reduce register + * pressure. This shouldn't be necessary, but compilers can + * struggle with codegen with high register pressure. + */ + { + int const index = (int)(bits[3] >> 53); + HUF_DEltX2 const entry = dtable[index]; + MEM_write16(op[3], entry.sequence); + bits[3] <<= (entry.nbBits); + op[3] += (entry.length); + } + /* Reload the bistreams. The final bitstream must be reloaded + * after the 5th symbol was decoded. + */ + { + int const ctz = ZSTD_countTrailingZeros64(bits[stream]); + int const nbBits = ctz & 7; + int const nbBytes = ctz >> 3; + ip[stream] -= nbBytes; + bits[stream] = MEM_read64(ip[stream]) | 1; + bits[stream] <<= nbBits; + } + } + } while (op[3] < olimit); + } -static HUF_ASM_X86_64_BMI2_ATTRS size_t -HUF_decompress4X2_usingDTable_internal_bmi2_asm( +_out: + + /* Save the final values of each of the state variables back to args. */ + ZSTD_memcpy(&args->bits, &bits, sizeof(bits)); + ZSTD_memcpy((void*)(&args->ip), &ip, sizeof(ip)); + ZSTD_memcpy(&args->op, &op, sizeof(op)); +} + + +static HUF_FAST_BMI2_ATTRS size_t +HUF_decompress4X2_usingDTable_internal_fast( void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, - const HUF_DTable* DTable) { + const HUF_DTable* DTable, + HUF_DecompressFastLoopFn loopFn) { void const* dt = DTable + 1; const BYTE* const iend = (const BYTE*)cSrc + 6; BYTE* const oend = (BYTE*)dst + dstSize; - HUF_DecompressAsmArgs args; + HUF_DecompressFastArgs args; { - size_t const ret = HUF_DecompressAsmArgs_init(&args, dst, dstSize, cSrc, cSrcSize, DTable); + size_t const ret = HUF_DecompressFastArgs_init(&args, dst, dstSize, cSrc, cSrcSize, DTable); FORWARD_IF_ERROR(ret, "Failed to init asm args"); - if (ret != 0) - return HUF_decompress4X2_usingDTable_internal_bmi2(dst, dstSize, cSrc, cSrcSize, DTable); + if (ret == 0) + return 0; } assert(args.ip[0] >= args.ilimit); - HUF_decompress4X2_usingDTable_internal_bmi2_asm_loop(&args); + loopFn(&args); /* note : op4 already verified within main loop */ assert(args.ip[0] >= iend); @@ -1430,91 +1653,72 @@ HUF_decompress4X2_usingDTable_internal_bmi2_asm( /* decoded size */ return dstSize; } -#endif /* ZSTD_ENABLE_ASM_X86_64_BMI2 */ static size_t HUF_decompress4X2_usingDTable_internal(void* dst, size_t dstSize, void const* cSrc, - size_t cSrcSize, HUF_DTable const* DTable, int bmi2) + size_t cSrcSize, HUF_DTable const* DTable, int flags) { + HUF_DecompressUsingDTableFn fallbackFn = HUF_decompress4X2_usingDTable_internal_default; + HUF_DecompressFastLoopFn loopFn = HUF_decompress4X2_usingDTable_internal_fast_c_loop; + #if DYNAMIC_BMI2 - if (bmi2) { + if (flags & HUF_flags_bmi2) { + fallbackFn = HUF_decompress4X2_usingDTable_internal_bmi2; # if ZSTD_ENABLE_ASM_X86_64_BMI2 - return HUF_decompress4X2_usingDTable_internal_bmi2_asm(dst, dstSize, cSrc, cSrcSize, DTable); -# else - return HUF_decompress4X2_usingDTable_internal_bmi2(dst, dstSize, cSrc, cSrcSize, DTable); + if (!(flags & HUF_flags_disableAsm)) { + loopFn = HUF_decompress4X2_usingDTable_internal_fast_asm_loop; + } # endif + } else { + return fallbackFn(dst, dstSize, cSrc, cSrcSize, DTable); } -#else - (void)bmi2; #endif #if ZSTD_ENABLE_ASM_X86_64_BMI2 && defined(__BMI2__) - return HUF_decompress4X2_usingDTable_internal_bmi2_asm(dst, dstSize, cSrc, cSrcSize, DTable); -#else - return HUF_decompress4X2_usingDTable_internal_default(dst, dstSize, cSrc, cSrcSize, DTable); + if (!(flags & HUF_flags_disableAsm)) { + loopFn = HUF_decompress4X2_usingDTable_internal_fast_asm_loop; + } #endif + + if (!(flags & HUF_flags_disableFast)) { + size_t const ret = HUF_decompress4X2_usingDTable_internal_fast(dst, dstSize, cSrc, cSrcSize, DTable, loopFn); + if (ret != 0) + return ret; + } + return fallbackFn(dst, dstSize, cSrc, cSrcSize, DTable); } HUF_DGEN(HUF_decompress1X2_usingDTable_internal) -size_t HUF_decompress1X2_usingDTable( - void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize, - const HUF_DTable* DTable) -{ - DTableDesc dtd = HUF_getDTableDesc(DTable); - if (dtd.tableType != 1) return ERROR(GENERIC); - return HUF_decompress1X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); -} - size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable* DCtx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, - void* workSpace, size_t wkspSize) + void* workSpace, size_t wkspSize, int flags) { const BYTE* ip = (const BYTE*) cSrc; size_t const hSize = HUF_readDTableX2_wksp(DCtx, cSrc, cSrcSize, - workSpace, wkspSize); + workSpace, wkspSize, flags); if (HUF_isError(hSize)) return hSize; if (hSize >= cSrcSize) return ERROR(srcSize_wrong); ip += hSize; cSrcSize -= hSize; - return HUF_decompress1X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx, /* bmi2 */ 0); -} - - -size_t HUF_decompress4X2_usingDTable( - void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize, - const HUF_DTable* DTable) -{ - DTableDesc dtd = HUF_getDTableDesc(DTable); - if (dtd.tableType != 1) return ERROR(GENERIC); - return HUF_decompress4X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); + return HUF_decompress1X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx, flags); } -static size_t HUF_decompress4X2_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, +static size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, - void* workSpace, size_t wkspSize, int bmi2) + void* workSpace, size_t wkspSize, int flags) { const BYTE* ip = (const BYTE*) cSrc; size_t hSize = HUF_readDTableX2_wksp(dctx, cSrc, cSrcSize, - workSpace, wkspSize); + workSpace, wkspSize, flags); if (HUF_isError(hSize)) return hSize; if (hSize >= cSrcSize) return ERROR(srcSize_wrong); ip += hSize; cSrcSize -= hSize; - return HUF_decompress4X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2); + return HUF_decompress4X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, flags); } -size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize, - void* workSpace, size_t wkspSize) -{ - return HUF_decompress4X2_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, /* bmi2 */ 0); -} - - #endif /* HUF_FORCE_DECOMPRESS_X1 */ @@ -1522,44 +1726,6 @@ size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, /* Universal decompression selectors */ /* ***********************************/ -size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize, - const void* cSrc, size_t cSrcSize, - const HUF_DTable* DTable) -{ - DTableDesc const dtd = HUF_getDTableDesc(DTable); -#if defined(HUF_FORCE_DECOMPRESS_X1) - (void)dtd; - assert(dtd.tableType == 0); - return HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); -#elif defined(HUF_FORCE_DECOMPRESS_X2) - (void)dtd; - assert(dtd.tableType == 1); - return HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); -#else - return dtd.tableType ? HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0) : - HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); -#endif -} - -size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize, - const void* cSrc, size_t cSrcSize, - const HUF_DTable* DTable) -{ - DTableDesc const dtd = HUF_getDTableDesc(DTable); -#if defined(HUF_FORCE_DECOMPRESS_X1) - (void)dtd; - assert(dtd.tableType == 0); - return HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); -#elif defined(HUF_FORCE_DECOMPRESS_X2) - (void)dtd; - assert(dtd.tableType == 1); - return HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); -#else - return dtd.tableType ? HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0) : - HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); -#endif -} - #if !defined(HUF_FORCE_DECOMPRESS_X1) && !defined(HUF_FORCE_DECOMPRESS_X2) typedef struct { U32 tableTime; U32 decode256Time; } algo_time_t; @@ -1614,36 +1780,9 @@ U32 HUF_selectDecoder (size_t dstSize, size_t cSrcSize) #endif } - -size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable* dctx, void* dst, - size_t dstSize, const void* cSrc, - size_t cSrcSize, void* workSpace, - size_t wkspSize) -{ - /* validation checks */ - if (dstSize == 0) return ERROR(dstSize_tooSmall); - if (cSrcSize == 0) return ERROR(corruption_detected); - - { U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); -#if defined(HUF_FORCE_DECOMPRESS_X1) - (void)algoNb; - assert(algoNb == 0); - return HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize); -#elif defined(HUF_FORCE_DECOMPRESS_X2) - (void)algoNb; - assert(algoNb == 1); - return HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize); -#else - return algoNb ? HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, - cSrcSize, workSpace, wkspSize): - HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize); -#endif - } -} - size_t HUF_decompress1X_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, - void* workSpace, size_t wkspSize) + void* workSpace, size_t wkspSize, int flags) { /* validation checks */ if (dstSize == 0) return ERROR(dstSize_tooSmall); @@ -1656,71 +1795,71 @@ size_t HUF_decompress1X_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, (void)algoNb; assert(algoNb == 0); return HUF_decompress1X1_DCtx_wksp(dctx, dst, dstSize, cSrc, - cSrcSize, workSpace, wkspSize); + cSrcSize, workSpace, wkspSize, flags); #elif defined(HUF_FORCE_DECOMPRESS_X2) (void)algoNb; assert(algoNb == 1); return HUF_decompress1X2_DCtx_wksp(dctx, dst, dstSize, cSrc, - cSrcSize, workSpace, wkspSize); + cSrcSize, workSpace, wkspSize, flags); #else return algoNb ? HUF_decompress1X2_DCtx_wksp(dctx, dst, dstSize, cSrc, - cSrcSize, workSpace, wkspSize): + cSrcSize, workSpace, wkspSize, flags): HUF_decompress1X1_DCtx_wksp(dctx, dst, dstSize, cSrc, - cSrcSize, workSpace, wkspSize); + cSrcSize, workSpace, wkspSize, flags); #endif } } -size_t HUF_decompress1X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2) +size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int flags) { DTableDesc const dtd = HUF_getDTableDesc(DTable); #if defined(HUF_FORCE_DECOMPRESS_X1) (void)dtd; assert(dtd.tableType == 0); - return HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2); + return HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, flags); #elif defined(HUF_FORCE_DECOMPRESS_X2) (void)dtd; assert(dtd.tableType == 1); - return HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2); + return HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, flags); #else - return dtd.tableType ? HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2) : - HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2); + return dtd.tableType ? HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, flags) : + HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, flags); #endif } #ifndef HUF_FORCE_DECOMPRESS_X2 -size_t HUF_decompress1X1_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2) +size_t HUF_decompress1X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int flags) { const BYTE* ip = (const BYTE*) cSrc; - size_t const hSize = HUF_readDTableX1_wksp_bmi2(dctx, cSrc, cSrcSize, workSpace, wkspSize, bmi2); + size_t const hSize = HUF_readDTableX1_wksp(dctx, cSrc, cSrcSize, workSpace, wkspSize, flags); if (HUF_isError(hSize)) return hSize; if (hSize >= cSrcSize) return ERROR(srcSize_wrong); ip += hSize; cSrcSize -= hSize; - return HUF_decompress1X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2); + return HUF_decompress1X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, flags); } #endif -size_t HUF_decompress4X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2) +size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int flags) { DTableDesc const dtd = HUF_getDTableDesc(DTable); #if defined(HUF_FORCE_DECOMPRESS_X1) (void)dtd; assert(dtd.tableType == 0); - return HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2); + return HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, flags); #elif defined(HUF_FORCE_DECOMPRESS_X2) (void)dtd; assert(dtd.tableType == 1); - return HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2); + return HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, flags); #else - return dtd.tableType ? HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2) : - HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2); + return dtd.tableType ? HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, flags) : + HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, flags); #endif } -size_t HUF_decompress4X_hufOnly_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2) +size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int flags) { /* validation checks */ if (dstSize == 0) return ERROR(dstSize_tooSmall); @@ -1730,160 +1869,14 @@ size_t HUF_decompress4X_hufOnly_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t ds #if defined(HUF_FORCE_DECOMPRESS_X1) (void)algoNb; assert(algoNb == 0); - return HUF_decompress4X1_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2); + return HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, flags); #elif defined(HUF_FORCE_DECOMPRESS_X2) (void)algoNb; assert(algoNb == 1); - return HUF_decompress4X2_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2); + return HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, flags); #else - return algoNb ? HUF_decompress4X2_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2) : - HUF_decompress4X1_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2); + return algoNb ? HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, flags) : + HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, flags); #endif } } - -#ifndef ZSTD_NO_UNUSED_FUNCTIONS -#ifndef HUF_FORCE_DECOMPRESS_X2 -size_t HUF_readDTableX1(HUF_DTable* DTable, const void* src, size_t srcSize) -{ - U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; - return HUF_readDTableX1_wksp(DTable, src, srcSize, - workSpace, sizeof(workSpace)); -} - -size_t HUF_decompress1X1_DCtx(HUF_DTable* DCtx, void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize) -{ - U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; - return HUF_decompress1X1_DCtx_wksp(DCtx, dst, dstSize, cSrc, cSrcSize, - workSpace, sizeof(workSpace)); -} - -size_t HUF_decompress1X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) -{ - HUF_CREATE_STATIC_DTABLEX1(DTable, HUF_TABLELOG_MAX); - return HUF_decompress1X1_DCtx (DTable, dst, dstSize, cSrc, cSrcSize); -} -#endif - -#ifndef HUF_FORCE_DECOMPRESS_X1 -size_t HUF_readDTableX2(HUF_DTable* DTable, const void* src, size_t srcSize) -{ - U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; - return HUF_readDTableX2_wksp(DTable, src, srcSize, - workSpace, sizeof(workSpace)); -} - -size_t HUF_decompress1X2_DCtx(HUF_DTable* DCtx, void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize) -{ - U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; - return HUF_decompress1X2_DCtx_wksp(DCtx, dst, dstSize, cSrc, cSrcSize, - workSpace, sizeof(workSpace)); -} - -size_t HUF_decompress1X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) -{ - HUF_CREATE_STATIC_DTABLEX2(DTable, HUF_TABLELOG_MAX); - return HUF_decompress1X2_DCtx(DTable, dst, dstSize, cSrc, cSrcSize); -} -#endif - -#ifndef HUF_FORCE_DECOMPRESS_X2 -size_t HUF_decompress4X1_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) -{ - U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; - return HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, - workSpace, sizeof(workSpace)); -} -size_t HUF_decompress4X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) -{ - HUF_CREATE_STATIC_DTABLEX1(DTable, HUF_TABLELOG_MAX); - return HUF_decompress4X1_DCtx(DTable, dst, dstSize, cSrc, cSrcSize); -} -#endif - -#ifndef HUF_FORCE_DECOMPRESS_X1 -size_t HUF_decompress4X2_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize) -{ - U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; - return HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, - workSpace, sizeof(workSpace)); -} - -size_t HUF_decompress4X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) -{ - HUF_CREATE_STATIC_DTABLEX2(DTable, HUF_TABLELOG_MAX); - return HUF_decompress4X2_DCtx(DTable, dst, dstSize, cSrc, cSrcSize); -} -#endif - -typedef size_t (*decompressionAlgo)(void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); - -size_t HUF_decompress (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) -{ -#if !defined(HUF_FORCE_DECOMPRESS_X1) && !defined(HUF_FORCE_DECOMPRESS_X2) - static const decompressionAlgo decompress[2] = { HUF_decompress4X1, HUF_decompress4X2 }; -#endif - - /* validation checks */ - if (dstSize == 0) return ERROR(dstSize_tooSmall); - if (cSrcSize > dstSize) return ERROR(corruption_detected); /* invalid */ - if (cSrcSize == dstSize) { ZSTD_memcpy(dst, cSrc, dstSize); return dstSize; } /* not compressed */ - if (cSrcSize == 1) { ZSTD_memset(dst, *(const BYTE*)cSrc, dstSize); return dstSize; } /* RLE */ - - { U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); -#if defined(HUF_FORCE_DECOMPRESS_X1) - (void)algoNb; - assert(algoNb == 0); - return HUF_decompress4X1(dst, dstSize, cSrc, cSrcSize); -#elif defined(HUF_FORCE_DECOMPRESS_X2) - (void)algoNb; - assert(algoNb == 1); - return HUF_decompress4X2(dst, dstSize, cSrc, cSrcSize); -#else - return decompress[algoNb](dst, dstSize, cSrc, cSrcSize); -#endif - } -} - -size_t HUF_decompress4X_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) -{ - /* validation checks */ - if (dstSize == 0) return ERROR(dstSize_tooSmall); - if (cSrcSize > dstSize) return ERROR(corruption_detected); /* invalid */ - if (cSrcSize == dstSize) { ZSTD_memcpy(dst, cSrc, dstSize); return dstSize; } /* not compressed */ - if (cSrcSize == 1) { ZSTD_memset(dst, *(const BYTE*)cSrc, dstSize); return dstSize; } /* RLE */ - - { U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); -#if defined(HUF_FORCE_DECOMPRESS_X1) - (void)algoNb; - assert(algoNb == 0); - return HUF_decompress4X1_DCtx(dctx, dst, dstSize, cSrc, cSrcSize); -#elif defined(HUF_FORCE_DECOMPRESS_X2) - (void)algoNb; - assert(algoNb == 1); - return HUF_decompress4X2_DCtx(dctx, dst, dstSize, cSrc, cSrcSize); -#else - return algoNb ? HUF_decompress4X2_DCtx(dctx, dst, dstSize, cSrc, cSrcSize) : - HUF_decompress4X1_DCtx(dctx, dst, dstSize, cSrc, cSrcSize) ; -#endif - } -} - -size_t HUF_decompress4X_hufOnly(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) -{ - U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; - return HUF_decompress4X_hufOnly_wksp(dctx, dst, dstSize, cSrc, cSrcSize, - workSpace, sizeof(workSpace)); -} - -size_t HUF_decompress1X_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize) -{ - U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; - return HUF_decompress1X_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, - workSpace, sizeof(workSpace)); -} -#endif diff --git a/internal-complibs/zstd-1.5.2/decompress/huf_decompress_amd64.S b/internal-complibs/zstd-1.5.5/decompress/huf_decompress_amd64.S similarity index 88% rename from internal-complibs/zstd-1.5.2/decompress/huf_decompress_amd64.S rename to internal-complibs/zstd-1.5.5/decompress/huf_decompress_amd64.S index 49589cb6..671624fe 100644 --- a/internal-complibs/zstd-1.5.2/decompress/huf_decompress_amd64.S +++ b/internal-complibs/zstd-1.5.5/decompress/huf_decompress_amd64.S @@ -1,5 +1,5 @@ /* - * Copyright (c) Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -30,14 +30,14 @@ * TODO: Support Windows calling convention. */ -ZSTD_HIDE_ASM_FUNCTION(HUF_decompress4X1_usingDTable_internal_bmi2_asm_loop) -ZSTD_HIDE_ASM_FUNCTION(HUF_decompress4X2_usingDTable_internal_bmi2_asm_loop) -ZSTD_HIDE_ASM_FUNCTION(_HUF_decompress4X2_usingDTable_internal_bmi2_asm_loop) -ZSTD_HIDE_ASM_FUNCTION(_HUF_decompress4X1_usingDTable_internal_bmi2_asm_loop) -.global HUF_decompress4X1_usingDTable_internal_bmi2_asm_loop -.global HUF_decompress4X2_usingDTable_internal_bmi2_asm_loop -.global _HUF_decompress4X1_usingDTable_internal_bmi2_asm_loop -.global _HUF_decompress4X2_usingDTable_internal_bmi2_asm_loop +ZSTD_HIDE_ASM_FUNCTION(HUF_decompress4X1_usingDTable_internal_fast_asm_loop) +ZSTD_HIDE_ASM_FUNCTION(HUF_decompress4X2_usingDTable_internal_fast_asm_loop) +ZSTD_HIDE_ASM_FUNCTION(_HUF_decompress4X2_usingDTable_internal_fast_asm_loop) +ZSTD_HIDE_ASM_FUNCTION(_HUF_decompress4X1_usingDTable_internal_fast_asm_loop) +.global HUF_decompress4X1_usingDTable_internal_fast_asm_loop +.global HUF_decompress4X2_usingDTable_internal_fast_asm_loop +.global _HUF_decompress4X1_usingDTable_internal_fast_asm_loop +.global _HUF_decompress4X2_usingDTable_internal_fast_asm_loop .text /* Sets up register mappings for clarity. @@ -95,8 +95,9 @@ ZSTD_HIDE_ASM_FUNCTION(_HUF_decompress4X1_usingDTable_internal_bmi2_asm_loop) /* Define both _HUF_* & HUF_* symbols because MacOS * C symbols are prefixed with '_' & Linux symbols aren't. */ -_HUF_decompress4X1_usingDTable_internal_bmi2_asm_loop: -HUF_decompress4X1_usingDTable_internal_bmi2_asm_loop: +_HUF_decompress4X1_usingDTable_internal_fast_asm_loop: +HUF_decompress4X1_usingDTable_internal_fast_asm_loop: + ZSTD_CET_ENDBRANCH /* Save all registers - even if they are callee saved for simplicity. */ push %rax push %rbx @@ -350,8 +351,9 @@ HUF_decompress4X1_usingDTable_internal_bmi2_asm_loop: pop %rax ret -_HUF_decompress4X2_usingDTable_internal_bmi2_asm_loop: -HUF_decompress4X2_usingDTable_internal_bmi2_asm_loop: +_HUF_decompress4X2_usingDTable_internal_fast_asm_loop: +HUF_decompress4X2_usingDTable_internal_fast_asm_loop: + ZSTD_CET_ENDBRANCH /* Save all registers - even if they are callee saved for simplicity. */ push %rax push %rbx @@ -427,41 +429,30 @@ HUF_decompress4X2_usingDTable_internal_bmi2_asm_loop: /* r15 = (ip0 - ilimit) / 7 */ movq %rdx, %r15 - movabsq $-3689348814741910323, %rdx - movq 8(%rsp), %rax /* rax = oend0 */ - subq %op0, %rax /* rax = oend0 - op0 */ - mulq %rdx - shrq $3, %rdx /* rdx = rax / 10 */ - - /* r15 = min(%rdx, %r15) */ - cmpq %rdx, %r15 - cmova %rdx, %r15 + /* r15 = min(r15, min(oend0 - op0, oend1 - op1, oend2 - op2, oend3 - op3) / 10) */ + movq 8(%rsp), %rax /* rax = oend0 */ + subq %op0, %rax /* rax = oend0 - op0 */ + movq 16(%rsp), %rdx /* rdx = oend1 */ + subq %op1, %rdx /* rdx = oend1 - op1 */ - movabsq $-3689348814741910323, %rdx - movq 16(%rsp), %rax /* rax = oend1 */ - subq %op1, %rax /* rax = oend1 - op1 */ - mulq %rdx - shrq $3, %rdx /* rdx = rax / 10 */ - - /* r15 = min(%rdx, %r15) */ - cmpq %rdx, %r15 - cmova %rdx, %r15 + cmpq %rax, %rdx + cmova %rax, %rdx /* rdx = min(%rdx, %rax) */ - movabsq $-3689348814741910323, %rdx movq 24(%rsp), %rax /* rax = oend2 */ subq %op2, %rax /* rax = oend2 - op2 */ - mulq %rdx - shrq $3, %rdx /* rdx = rax / 10 */ - /* r15 = min(%rdx, %r15) */ - cmpq %rdx, %r15 - cmova %rdx, %r15 + cmpq %rax, %rdx + cmova %rax, %rdx /* rdx = min(%rdx, %rax) */ - movabsq $-3689348814741910323, %rdx movq 32(%rsp), %rax /* rax = oend3 */ subq %op3, %rax /* rax = oend3 - op3 */ + + cmpq %rax, %rdx + cmova %rax, %rdx /* rdx = min(%rdx, %rax) */ + + movabsq $-3689348814741910323, %rax mulq %rdx - shrq $3, %rdx /* rdx = rax / 10 */ + shrq $3, %rdx /* rdx = rdx / 10 */ /* r15 = min(%rdx, %r15) */ cmpq %rdx, %r15 diff --git a/internal-complibs/zstd-1.5.2/decompress/zstd_ddict.c b/internal-complibs/zstd-1.5.5/decompress/zstd_ddict.c similarity index 96% rename from internal-complibs/zstd-1.5.2/decompress/zstd_ddict.c rename to internal-complibs/zstd-1.5.5/decompress/zstd_ddict.c index ce335477..309ec0d0 100644 --- a/internal-complibs/zstd-1.5.2/decompress/zstd_ddict.c +++ b/internal-complibs/zstd-1.5.5/decompress/zstd_ddict.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -14,12 +14,12 @@ /*-******************************************************* * Dependencies *********************************************************/ +#include "../common/allocations.h" /* ZSTD_customMalloc, ZSTD_customFree */ #include "../common/zstd_deps.h" /* ZSTD_memcpy, ZSTD_memmove, ZSTD_memset */ #include "../common/cpu.h" /* bmi2 */ #include "../common/mem.h" /* low level memory routines */ #define FSE_STATIC_LINKING_ONLY #include "../common/fse.h" -#define HUF_STATIC_LINKING_ONLY #include "../common/huf.h" #include "zstd_decompress_internal.h" #include "zstd_ddict.h" @@ -134,7 +134,7 @@ static size_t ZSTD_initDDict_internal(ZSTD_DDict* ddict, ZSTD_memcpy(internalBuffer, dict, dictSize); } ddict->dictSize = dictSize; - ddict->entropy.hufTable[0] = (HUF_DTable)((HufLog)*0x1000001); /* cover both little and big endian */ + ddict->entropy.hufTable[0] = (HUF_DTable)((ZSTD_HUFFDTABLE_CAPACITY_LOG)*0x1000001); /* cover both little and big endian */ /* parse dictionary content */ FORWARD_IF_ERROR( ZSTD_loadEntropy_intoDDict(ddict, dictContentType) , ""); @@ -240,5 +240,5 @@ size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict) unsigned ZSTD_getDictID_fromDDict(const ZSTD_DDict* ddict) { if (ddict==NULL) return 0; - return ZSTD_getDictID_fromDict(ddict->dictContent, ddict->dictSize); + return ddict->dictID; } diff --git a/internal-complibs/zstd-1.5.2/decompress/zstd_ddict.h b/internal-complibs/zstd-1.5.5/decompress/zstd_ddict.h similarity index 95% rename from internal-complibs/zstd-1.5.2/decompress/zstd_ddict.h rename to internal-complibs/zstd-1.5.5/decompress/zstd_ddict.h index bd03268b..c4ca8877 100644 --- a/internal-complibs/zstd-1.5.2/decompress/zstd_ddict.h +++ b/internal-complibs/zstd-1.5.5/decompress/zstd_ddict.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the diff --git a/internal-complibs/zstd-1.5.2/decompress/zstd_decompress.c b/internal-complibs/zstd-1.5.5/decompress/zstd_decompress.c similarity index 89% rename from internal-complibs/zstd-1.5.2/decompress/zstd_decompress.c rename to internal-complibs/zstd-1.5.5/decompress/zstd_decompress.c index 0031e98c..7bc27134 100644 --- a/internal-complibs/zstd-1.5.2/decompress/zstd_decompress.c +++ b/internal-complibs/zstd-1.5.5/decompress/zstd_decompress.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -55,17 +55,18 @@ /*-******************************************************* * Dependencies *********************************************************/ +#include "../common/allocations.h" /* ZSTD_customMalloc, ZSTD_customCalloc, ZSTD_customFree */ #include "../common/zstd_deps.h" /* ZSTD_memcpy, ZSTD_memmove, ZSTD_memset */ #include "../common/mem.h" /* low level memory routines */ #define FSE_STATIC_LINKING_ONLY #include "../common/fse.h" -#define HUF_STATIC_LINKING_ONLY #include "../common/huf.h" #include "../common/xxhash.h" /* XXH64_reset, XXH64_update, XXH64_digest, XXH64 */ #include "../common/zstd_internal.h" /* blockProperties_t */ #include "zstd_decompress_internal.h" /* ZSTD_DCtx */ #include "zstd_ddict.h" /* ZSTD_DDictDictContent */ #include "zstd_decompress_block.h" /* ZSTD_decompressBlock_internal */ +#include "../common/bits.h" /* ZSTD_highbit32 */ #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1) # include "../legacy/zstd_legacy.h" @@ -78,11 +79,11 @@ *************************************/ #define DDICT_HASHSET_MAX_LOAD_FACTOR_COUNT_MULT 4 -#define DDICT_HASHSET_MAX_LOAD_FACTOR_SIZE_MULT 3 /* These two constants represent SIZE_MULT/COUNT_MULT load factor without using a float. - * Currently, that means a 0.75 load factor. - * So, if count * COUNT_MULT / size * SIZE_MULT != 0, then we've exceeded - * the load factor of the ddict hash set. - */ +#define DDICT_HASHSET_MAX_LOAD_FACTOR_SIZE_MULT 3 /* These two constants represent SIZE_MULT/COUNT_MULT load factor without using a float. + * Currently, that means a 0.75 load factor. + * So, if count * COUNT_MULT / size * SIZE_MULT != 0, then we've exceeded + * the load factor of the ddict hash set. + */ #define DDICT_HASHSET_TABLE_BASE_SIZE 64 #define DDICT_HASHSET_RESIZE_FACTOR 2 @@ -243,6 +244,7 @@ static void ZSTD_DCtx_resetParameters(ZSTD_DCtx* dctx) dctx->outBufferMode = ZSTD_bm_buffered; dctx->forceIgnoreChecksum = ZSTD_d_validateChecksum; dctx->refMultipleDDicts = ZSTD_rmd_refSingleDDict; + dctx->disableHufAsm = 0; } static void ZSTD_initDCtx_internal(ZSTD_DCtx* dctx) @@ -438,16 +440,40 @@ size_t ZSTD_frameHeaderSize(const void* src, size_t srcSize) * note : only works for formats ZSTD_f_zstd1 and ZSTD_f_zstd1_magicless * @return : 0, `zfhPtr` is correctly filled, * >0, `srcSize` is too small, value is wanted `srcSize` amount, - * or an error code, which can be tested using ZSTD_isError() */ +** or an error code, which can be tested using ZSTD_isError() */ size_t ZSTD_getFrameHeader_advanced(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize, ZSTD_format_e format) { const BYTE* ip = (const BYTE*)src; size_t const minInputSize = ZSTD_startingInputLength(format); - ZSTD_memset(zfhPtr, 0, sizeof(*zfhPtr)); /* not strictly necessary, but static analyzer do not understand that zfhPtr is only going to be read only if return value is zero, since they are 2 different signals */ - if (srcSize < minInputSize) return minInputSize; - RETURN_ERROR_IF(src==NULL, GENERIC, "invalid parameter"); + DEBUGLOG(5, "ZSTD_getFrameHeader_advanced: minInputSize = %zu, srcSize = %zu", minInputSize, srcSize); + + if (srcSize > 0) { + /* note : technically could be considered an assert(), since it's an invalid entry */ + RETURN_ERROR_IF(src==NULL, GENERIC, "invalid parameter : src==NULL, but srcSize>0"); + } + if (srcSize < minInputSize) { + if (srcSize > 0 && format != ZSTD_f_zstd1_magicless) { + /* when receiving less than @minInputSize bytes, + * control these bytes at least correspond to a supported magic number + * in order to error out early if they don't. + **/ + size_t const toCopy = MIN(4, srcSize); + unsigned char hbuf[4]; MEM_writeLE32(hbuf, ZSTD_MAGICNUMBER); + assert(src != NULL); + ZSTD_memcpy(hbuf, src, toCopy); + if ( MEM_readLE32(hbuf) != ZSTD_MAGICNUMBER ) { + /* not a zstd frame : let's check if it's a skippable frame */ + MEM_writeLE32(hbuf, ZSTD_MAGIC_SKIPPABLE_START); + ZSTD_memcpy(hbuf, src, toCopy); + if ((MEM_readLE32(hbuf) & ZSTD_MAGIC_SKIPPABLE_MASK) != ZSTD_MAGIC_SKIPPABLE_START) { + RETURN_ERROR(prefix_unknown, + "first bytes don't correspond to any supported magic number"); + } } } + return minInputSize; + } + ZSTD_memset(zfhPtr, 0, sizeof(*zfhPtr)); /* not strictly necessary, but static analyzers may not understand that zfhPtr will be read only if return value is zero, since they are 2 different signals */ if ( (format != ZSTD_f_zstd1_magicless) && (MEM_readLE32(src) != ZSTD_MAGICNUMBER) ) { if ((MEM_readLE32(src) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) { @@ -563,49 +589,52 @@ static size_t readSkippableFrameSize(void const* src, size_t srcSize) sizeU32 = MEM_readLE32((BYTE const*)src + ZSTD_FRAMEIDSIZE); RETURN_ERROR_IF((U32)(sizeU32 + ZSTD_SKIPPABLEHEADERSIZE) < sizeU32, frameParameter_unsupported, ""); - { - size_t const skippableSize = skippableHeaderSize + sizeU32; + { size_t const skippableSize = skippableHeaderSize + sizeU32; RETURN_ERROR_IF(skippableSize > srcSize, srcSize_wrong, ""); return skippableSize; } } /*! ZSTD_readSkippableFrame() : - * Retrieves a zstd skippable frame containing data given by src, and writes it to dst buffer. + * Retrieves content of a skippable frame, and writes it to dst buffer. * * The parameter magicVariant will receive the magicVariant that was supplied when the frame was written, * i.e. magicNumber - ZSTD_MAGIC_SKIPPABLE_START. This can be NULL if the caller is not interested * in the magicVariant. * - * Returns an error if destination buffer is not large enough, or if the frame is not skippable. + * Returns an error if destination buffer is not large enough, or if this is not a valid skippable frame. * * @return : number of bytes written or a ZSTD error. */ -ZSTDLIB_API size_t ZSTD_readSkippableFrame(void* dst, size_t dstCapacity, unsigned* magicVariant, - const void* src, size_t srcSize) +size_t ZSTD_readSkippableFrame(void* dst, size_t dstCapacity, + unsigned* magicVariant, /* optional, can be NULL */ + const void* src, size_t srcSize) { - U32 const magicNumber = MEM_readLE32(src); - size_t skippableFrameSize = readSkippableFrameSize(src, srcSize); - size_t skippableContentSize = skippableFrameSize - ZSTD_SKIPPABLEHEADERSIZE; - - /* check input validity */ - RETURN_ERROR_IF(!ZSTD_isSkippableFrame(src, srcSize), frameParameter_unsupported, ""); - RETURN_ERROR_IF(skippableFrameSize < ZSTD_SKIPPABLEHEADERSIZE || skippableFrameSize > srcSize, srcSize_wrong, ""); - RETURN_ERROR_IF(skippableContentSize > dstCapacity, dstSize_tooSmall, ""); + RETURN_ERROR_IF(srcSize < ZSTD_SKIPPABLEHEADERSIZE, srcSize_wrong, ""); - /* deliver payload */ - if (skippableContentSize > 0 && dst != NULL) - ZSTD_memcpy(dst, (const BYTE *)src + ZSTD_SKIPPABLEHEADERSIZE, skippableContentSize); - if (magicVariant != NULL) - *magicVariant = magicNumber - ZSTD_MAGIC_SKIPPABLE_START; - return skippableContentSize; + { U32 const magicNumber = MEM_readLE32(src); + size_t skippableFrameSize = readSkippableFrameSize(src, srcSize); + size_t skippableContentSize = skippableFrameSize - ZSTD_SKIPPABLEHEADERSIZE; + + /* check input validity */ + RETURN_ERROR_IF(!ZSTD_isSkippableFrame(src, srcSize), frameParameter_unsupported, ""); + RETURN_ERROR_IF(skippableFrameSize < ZSTD_SKIPPABLEHEADERSIZE || skippableFrameSize > srcSize, srcSize_wrong, ""); + RETURN_ERROR_IF(skippableContentSize > dstCapacity, dstSize_tooSmall, ""); + + /* deliver payload */ + if (skippableContentSize > 0 && dst != NULL) + ZSTD_memcpy(dst, (const BYTE *)src + ZSTD_SKIPPABLEHEADERSIZE, skippableContentSize); + if (magicVariant != NULL) + *magicVariant = magicNumber - ZSTD_MAGIC_SKIPPABLE_START; + return skippableContentSize; + } } /** ZSTD_findDecompressedSize() : - * compatible with legacy mode * `srcSize` must be the exact length of some number of ZSTD compressed and/or * skippable frames - * @return : decompressed size of the frames contained */ + * note: compatible with legacy mode + * @return : decompressed size of the frames contained */ unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize) { unsigned long long totalDstSize = 0; @@ -615,9 +644,7 @@ unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize) if ((magicNumber & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) { size_t const skippableSize = readSkippableFrameSize(src, srcSize); - if (ZSTD_isError(skippableSize)) { - return ZSTD_CONTENTSIZE_ERROR; - } + if (ZSTD_isError(skippableSize)) return ZSTD_CONTENTSIZE_ERROR; assert(skippableSize <= srcSize); src = (const BYTE *)src + skippableSize; @@ -625,17 +652,17 @@ unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize) continue; } - { unsigned long long const ret = ZSTD_getFrameContentSize(src, srcSize); - if (ret >= ZSTD_CONTENTSIZE_ERROR) return ret; + { unsigned long long const fcs = ZSTD_getFrameContentSize(src, srcSize); + if (fcs >= ZSTD_CONTENTSIZE_ERROR) return fcs; - /* check for overflow */ - if (totalDstSize + ret < totalDstSize) return ZSTD_CONTENTSIZE_ERROR; - totalDstSize += ret; + if (totalDstSize + fcs < totalDstSize) + return ZSTD_CONTENTSIZE_ERROR; /* check for overflow */ + totalDstSize += fcs; } + /* skip to next frame */ { size_t const frameSrcSize = ZSTD_findFrameCompressedSize(src, srcSize); - if (ZSTD_isError(frameSrcSize)) { - return ZSTD_CONTENTSIZE_ERROR; - } + if (ZSTD_isError(frameSrcSize)) return ZSTD_CONTENTSIZE_ERROR; + assert(frameSrcSize <= srcSize); src = (const BYTE *)src + frameSrcSize; srcSize -= frameSrcSize; @@ -757,10 +784,11 @@ static ZSTD_frameSizeInfo ZSTD_findFrameSizeInfo(const void* src, size_t srcSize ip += 4; } + frameSizeInfo.nbBlocks = nbBlocks; frameSizeInfo.compressedSize = (size_t)(ip - ipstart); frameSizeInfo.decompressedBound = (zfh.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN) ? zfh.frameContentSize - : nbBlocks * zfh.blockSizeMax; + : (unsigned long long)nbBlocks * zfh.blockSizeMax; return frameSizeInfo; } } @@ -800,6 +828,48 @@ unsigned long long ZSTD_decompressBound(const void* src, size_t srcSize) return bound; } +size_t ZSTD_decompressionMargin(void const* src, size_t srcSize) +{ + size_t margin = 0; + unsigned maxBlockSize = 0; + + /* Iterate over each frame */ + while (srcSize > 0) { + ZSTD_frameSizeInfo const frameSizeInfo = ZSTD_findFrameSizeInfo(src, srcSize); + size_t const compressedSize = frameSizeInfo.compressedSize; + unsigned long long const decompressedBound = frameSizeInfo.decompressedBound; + ZSTD_frameHeader zfh; + + FORWARD_IF_ERROR(ZSTD_getFrameHeader(&zfh, src, srcSize), ""); + if (ZSTD_isError(compressedSize) || decompressedBound == ZSTD_CONTENTSIZE_ERROR) + return ERROR(corruption_detected); + + if (zfh.frameType == ZSTD_frame) { + /* Add the frame header to our margin */ + margin += zfh.headerSize; + /* Add the checksum to our margin */ + margin += zfh.checksumFlag ? 4 : 0; + /* Add 3 bytes per block */ + margin += 3 * frameSizeInfo.nbBlocks; + + /* Compute the max block size */ + maxBlockSize = MAX(maxBlockSize, zfh.blockSizeMax); + } else { + assert(zfh.frameType == ZSTD_skippableFrame); + /* Add the entire skippable frame size to our margin. */ + margin += compressedSize; + } + + assert(srcSize >= compressedSize); + src = (const BYTE*)src + compressedSize; + srcSize -= compressedSize; + } + + /* Add the max block size back to the margin. */ + margin += maxBlockSize; + + return margin; +} /*-************************************************************* * Frame decoding @@ -825,7 +895,7 @@ static size_t ZSTD_copyRawBlock(void* dst, size_t dstCapacity, if (srcSize == 0) return 0; RETURN_ERROR(dstBuffer_null, ""); } - ZSTD_memcpy(dst, src, srcSize); + ZSTD_memmove(dst, src, srcSize); return srcSize; } @@ -903,6 +973,7 @@ static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx, /* Loop on each block */ while (1) { + BYTE* oBlockEnd = oend; size_t decodedSize; blockProperties_t blockProperties; size_t const cBlockSize = ZSTD_getcBlockSize(ip, remainingSrcSize, &blockProperties); @@ -912,16 +983,34 @@ static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx, remainingSrcSize -= ZSTD_blockHeaderSize; RETURN_ERROR_IF(cBlockSize > remainingSrcSize, srcSize_wrong, ""); + if (ip >= op && ip < oBlockEnd) { + /* We are decompressing in-place. Limit the output pointer so that we + * don't overwrite the block that we are currently reading. This will + * fail decompression if the input & output pointers aren't spaced + * far enough apart. + * + * This is important to set, even when the pointers are far enough + * apart, because ZSTD_decompressBlock_internal() can decide to store + * literals in the output buffer, after the block it is decompressing. + * Since we don't want anything to overwrite our input, we have to tell + * ZSTD_decompressBlock_internal to never write past ip. + * + * See ZSTD_allocateLiteralsBuffer() for reference. + */ + oBlockEnd = op + (ip - op); + } + switch(blockProperties.blockType) { case bt_compressed: - decodedSize = ZSTD_decompressBlock_internal(dctx, op, (size_t)(oend-op), ip, cBlockSize, /* frame */ 1, not_streaming); + decodedSize = ZSTD_decompressBlock_internal(dctx, op, (size_t)(oBlockEnd-op), ip, cBlockSize, /* frame */ 1, not_streaming); break; case bt_raw : + /* Use oend instead of oBlockEnd because this function is safe to overlap. It uses memmove. */ decodedSize = ZSTD_copyRawBlock(op, (size_t)(oend-op), ip, cBlockSize); break; case bt_rle : - decodedSize = ZSTD_setRleBlock(op, (size_t)(oend-op), *ip, blockProperties.origSize); + decodedSize = ZSTD_setRleBlock(op, (size_t)(oBlockEnd-op), *ip, blockProperties.origSize); break; case bt_reserved : default: @@ -956,6 +1045,7 @@ static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx, } ZSTD_DCtx_trace_end(dctx, (U64)(op-ostart), (U64)(ip-istart), /* streaming */ 0); /* Allow caller to get size read */ + DEBUGLOG(4, "ZSTD_decompressFrame: decompressed frame of size %zi, consuming %zi bytes of input", op-ostart, ip - (const BYTE*)*srcPtr); *srcPtr = ip; *srcSizePtr = remainingSrcSize; return (size_t)(op-ostart); @@ -1002,17 +1092,18 @@ static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx, } #endif - { U32 const magicNumber = MEM_readLE32(src); - DEBUGLOG(4, "reading magic number %08X (expecting %08X)", - (unsigned)magicNumber, ZSTD_MAGICNUMBER); + if (srcSize >= 4) { + U32 const magicNumber = MEM_readLE32(src); + DEBUGLOG(5, "reading magic number %08X", (unsigned)magicNumber); if ((magicNumber & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) { + /* skippable frame detected : skip it */ size_t const skippableSize = readSkippableFrameSize(src, srcSize); - FORWARD_IF_ERROR(skippableSize, "readSkippableFrameSize failed"); + FORWARD_IF_ERROR(skippableSize, "invalid skippable frame"); assert(skippableSize <= srcSize); src = (const BYTE *)src + skippableSize; srcSize -= skippableSize; - continue; + continue; /* check next frame */ } } if (ddict) { @@ -1108,8 +1199,8 @@ size_t ZSTD_decompress(void* dst, size_t dstCapacity, const void* src, size_t sr size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx) { return dctx->expected; } /** - * Similar to ZSTD_nextSrcSizeToDecompress(), but when when a block input can be streamed, - * we allow taking a partial block as the input. Currently only raw uncompressed blocks can + * Similar to ZSTD_nextSrcSizeToDecompress(), but when a block input can be streamed, we + * allow taking a partial block as the input. Currently only raw uncompressed blocks can * be streamed. * * For blocks that can be streamed, this allows us to reduce the latency until we produce @@ -1309,7 +1400,7 @@ size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, c default: assert(0); /* impossible */ - RETURN_ERROR(GENERIC, "impossible to reach"); /* some compiler require default to do something */ + RETURN_ERROR(GENERIC, "impossible to reach"); /* some compilers require default to do something */ } } @@ -1350,11 +1441,11 @@ ZSTD_loadDEntropy(ZSTD_entropyDTables_t* entropy, /* in minimal huffman, we always use X1 variants */ size_t const hSize = HUF_readDTableX1_wksp(entropy->hufTable, dictPtr, dictEnd - dictPtr, - workspace, workspaceSize); + workspace, workspaceSize, /* flags */ 0); #else size_t const hSize = HUF_readDTableX2_wksp(entropy->hufTable, dictPtr, (size_t)(dictEnd - dictPtr), - workspace, workspaceSize); + workspace, workspaceSize, /* flags */ 0); #endif RETURN_ERROR_IF(HUF_isError(hSize), dictionary_corrupted, ""); dictPtr += hSize; @@ -1453,7 +1544,7 @@ size_t ZSTD_decompressBegin(ZSTD_DCtx* dctx) dctx->prefixStart = NULL; dctx->virtualStart = NULL; dctx->dictEnd = NULL; - dctx->entropy.hufTable[0] = (HUF_DTable)((HufLog)*0x1000001); /* cover both little and big endian */ + dctx->entropy.hufTable[0] = (HUF_DTable)((ZSTD_HUFFDTABLE_CAPACITY_LOG)*0x1000001); /* cover both little and big endian */ dctx->litEntropy = dctx->fseEntropy = 0; dctx->dictID = 0; dctx->bType = bt_reserved; @@ -1515,7 +1606,7 @@ unsigned ZSTD_getDictID_fromDict(const void* dict, size_t dictSize) * This could for one of the following reasons : * - The frame does not require a dictionary (most common case). * - The frame was built with dictID intentionally removed. - * Needed dictionary is a hidden information. + * Needed dictionary is a hidden piece of information. * Note : this use case also happens when using a non-conformant dictionary. * - `srcSize` is too small, and as a result, frame header could not be decoded. * Note : possible if `srcSize < ZSTD_FRAMEHEADERSIZE_MAX`. @@ -1524,7 +1615,7 @@ unsigned ZSTD_getDictID_fromDict(const void* dict, size_t dictSize) * ZSTD_getFrameHeader(), which will provide a more precise error code. */ unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize) { - ZSTD_frameHeader zfp = { 0, 0, 0, ZSTD_frame, 0, 0, 0 }; + ZSTD_frameHeader zfp = { 0, 0, 0, ZSTD_frame, 0, 0, 0, 0, 0 }; size_t const hError = ZSTD_getFrameHeader(&zfp, src, srcSize); if (ZSTD_isError(hError)) return 0; return zfp.dictID; @@ -1631,7 +1722,9 @@ size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t di size_t ZSTD_initDStream(ZSTD_DStream* zds) { DEBUGLOG(4, "ZSTD_initDStream"); - return ZSTD_initDStream_usingDDict(zds, NULL); + FORWARD_IF_ERROR(ZSTD_DCtx_reset(zds, ZSTD_reset_session_only), ""); + FORWARD_IF_ERROR(ZSTD_DCtx_refDDict(zds, NULL), ""); + return ZSTD_startingInputLength(zds->format); } /* ZSTD_initDStream_usingDDict() : @@ -1639,6 +1732,7 @@ size_t ZSTD_initDStream(ZSTD_DStream* zds) * this function cannot fail */ size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* dctx, const ZSTD_DDict* ddict) { + DEBUGLOG(4, "ZSTD_initDStream_usingDDict"); FORWARD_IF_ERROR( ZSTD_DCtx_reset(dctx, ZSTD_reset_session_only) , ""); FORWARD_IF_ERROR( ZSTD_DCtx_refDDict(dctx, ddict) , ""); return ZSTD_startingInputLength(dctx->format); @@ -1649,6 +1743,7 @@ size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* dctx, const ZSTD_DDict* ddict) * this function cannot fail */ size_t ZSTD_resetDStream(ZSTD_DStream* dctx) { + DEBUGLOG(4, "ZSTD_resetDStream"); FORWARD_IF_ERROR(ZSTD_DCtx_reset(dctx, ZSTD_reset_session_only), ""); return ZSTD_startingInputLength(dctx->format); } @@ -1720,6 +1815,11 @@ ZSTD_bounds ZSTD_dParam_getBounds(ZSTD_dParameter dParam) bounds.lowerBound = (int)ZSTD_rmd_refSingleDDict; bounds.upperBound = (int)ZSTD_rmd_refMultipleDDicts; return bounds; + case ZSTD_d_disableHuffmanAssembly: + bounds.lowerBound = 0; + bounds.upperBound = 1; + return bounds; + default:; } bounds.error = ERROR(parameter_unsupported); @@ -1760,6 +1860,9 @@ size_t ZSTD_DCtx_getParameter(ZSTD_DCtx* dctx, ZSTD_dParameter param, int* value case ZSTD_d_refMultipleDDicts: *value = (int)dctx->refMultipleDDicts; return 0; + case ZSTD_d_disableHuffmanAssembly: + *value = (int)dctx->disableHufAsm; + return 0; default:; } RETURN_ERROR(parameter_unsupported, ""); @@ -1793,6 +1896,10 @@ size_t ZSTD_DCtx_setParameter(ZSTD_DCtx* dctx, ZSTD_dParameter dParam, int value } dctx->refMultipleDDicts = (ZSTD_refMultipleDDicts_e)value; return 0; + case ZSTD_d_disableHuffmanAssembly: + CHECK_DBOUNDS(ZSTD_d_disableHuffmanAssembly, value); + dctx->disableHufAsm = value != 0; + return 0; default:; } RETURN_ERROR(parameter_unsupported, ""); @@ -1980,7 +2087,6 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB if (zds->refMultipleDDicts && zds->ddictSet) { ZSTD_DCtx_selectFrameDDict(zds); } - DEBUGLOG(5, "header size : %u", (U32)hSize); if (ZSTD_isError(hSize)) { #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1) U32 const legacyVersion = ZSTD_isLegacy(istart, iend-istart); @@ -2012,6 +2118,11 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB zds->lhSize += remainingInput; } input->pos = input->size; + /* check first few bytes */ + FORWARD_IF_ERROR( + ZSTD_getFrameHeader_advanced(&zds->fParams, zds->headerBuffer, zds->lhSize, zds->format), + "First few bytes detected incorrect" ); + /* return hint input size */ return (MAX((size_t)ZSTD_FRAMEHEADERSIZE_MIN(zds->format), hSize) - zds->lhSize) + ZSTD_blockHeaderSize; /* remaining header bytes + next block header */ } assert(ip != NULL); @@ -2029,8 +2140,9 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB size_t const decompressedSize = ZSTD_decompress_usingDDict(zds, op, (size_t)(oend-op), istart, cSize, ZSTD_getDDict(zds)); if (ZSTD_isError(decompressedSize)) return decompressedSize; DEBUGLOG(4, "shortcut to single-pass ZSTD_decompress_usingDDict()") + assert(istart != NULL); ip = istart + cSize; - op += decompressedSize; + op = op ? op + decompressedSize : op; /* can occur if frameContentSize = 0 (empty frame) */ zds->expected = 0; zds->streamStage = zdss_init; someMoreWork = 0; @@ -2114,6 +2226,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB } if ((size_t)(iend-ip) >= neededInSize) { /* decode directly from src */ FORWARD_IF_ERROR(ZSTD_decompressContinueStream(zds, &op, oend, ip, neededInSize), ""); + assert(ip != NULL); ip += neededInSize; /* Function modifies the stage so we must break */ break; @@ -2128,7 +2241,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB int const isSkipFrame = ZSTD_isSkipFrame(zds); size_t loadedSize; /* At this point we shouldn't be decompressing a block that we can stream. */ - assert(neededInSize == ZSTD_nextSrcSizeToDecompressWithInputSize(zds, iend - ip)); + assert(neededInSize == ZSTD_nextSrcSizeToDecompressWithInputSize(zds, (size_t)(iend - ip))); if (isSkipFrame) { loadedSize = MIN(toLoad, (size_t)(iend-ip)); } else { @@ -2137,8 +2250,11 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB "should never happen"); loadedSize = ZSTD_limitCopy(zds->inBuff + zds->inPos, toLoad, ip, (size_t)(iend-ip)); } - ip += loadedSize; - zds->inPos += loadedSize; + if (loadedSize != 0) { + /* ip may be NULL */ + ip += loadedSize; + zds->inPos += loadedSize; + } if (loadedSize < toLoad) { someMoreWork = 0; break; } /* not enough input, wait for more */ /* decode loaded input */ @@ -2148,14 +2264,17 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB break; } case zdss_flush: - { size_t const toFlushSize = zds->outEnd - zds->outStart; + { + size_t const toFlushSize = zds->outEnd - zds->outStart; size_t const flushedSize = ZSTD_limitCopy(op, (size_t)(oend-op), zds->outBuff + zds->outStart, toFlushSize); - op += flushedSize; + + op = op ? op + flushedSize : op; + zds->outStart += flushedSize; if (flushedSize == toFlushSize) { /* flush completed */ zds->streamStage = zdss_read; if ( (zds->outBuffSize < zds->fParams.frameContentSize) - && (zds->outStart + zds->fParams.blockSizeMax > zds->outBuffSize) ) { + && (zds->outStart + zds->fParams.blockSizeMax > zds->outBuffSize) ) { DEBUGLOG(5, "restart filling outBuff from beginning (left:%i, needed:%u)", (int)(zds->outBuffSize - zds->outStart), (U32)zds->fParams.blockSizeMax); @@ -2169,7 +2288,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB default: assert(0); /* impossible */ - RETURN_ERROR(GENERIC, "impossible to reach"); /* some compiler require default to do something */ + RETURN_ERROR(GENERIC, "impossible to reach"); /* some compilers require default to do something */ } } /* result */ @@ -2182,8 +2301,8 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB if ((ip==istart) && (op==ostart)) { /* no forward progress */ zds->noForwardProgress ++; if (zds->noForwardProgress >= ZSTD_NO_FORWARD_PROGRESS_MAX) { - RETURN_ERROR_IF(op==oend, dstSize_tooSmall, ""); - RETURN_ERROR_IF(ip==iend, srcSize_wrong, ""); + RETURN_ERROR_IF(op==oend, noForwardProgress_destFull, ""); + RETURN_ERROR_IF(ip==iend, noForwardProgress_inputEmpty, ""); assert(0); } } else { @@ -2220,11 +2339,17 @@ size_t ZSTD_decompressStream_simpleArgs ( void* dst, size_t dstCapacity, size_t* dstPos, const void* src, size_t srcSize, size_t* srcPos) { - ZSTD_outBuffer output = { dst, dstCapacity, *dstPos }; - ZSTD_inBuffer input = { src, srcSize, *srcPos }; - /* ZSTD_compress_generic() will check validity of dstPos and srcPos */ - size_t const cErr = ZSTD_decompressStream(dctx, &output, &input); - *dstPos = output.pos; - *srcPos = input.pos; - return cErr; + ZSTD_outBuffer output; + ZSTD_inBuffer input; + output.dst = dst; + output.size = dstCapacity; + output.pos = *dstPos; + input.src = src; + input.size = srcSize; + input.pos = *srcPos; + { size_t const cErr = ZSTD_decompressStream(dctx, &output, &input); + *dstPos = output.pos; + *srcPos = input.pos; + return cErr; + } } diff --git a/internal-complibs/zstd-1.5.2/decompress/zstd_decompress_block.c b/internal-complibs/zstd-1.5.5/decompress/zstd_decompress_block.c similarity index 89% rename from internal-complibs/zstd-1.5.2/decompress/zstd_decompress_block.c rename to internal-complibs/zstd-1.5.5/decompress/zstd_decompress_block.c index 2e44d30d..09896a93 100644 --- a/internal-complibs/zstd-1.5.2/decompress/zstd_decompress_block.c +++ b/internal-complibs/zstd-1.5.5/decompress/zstd_decompress_block.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -20,12 +20,12 @@ #include "../common/mem.h" /* low level memory routines */ #define FSE_STATIC_LINKING_ONLY #include "../common/fse.h" -#define HUF_STATIC_LINKING_ONLY #include "../common/huf.h" #include "../common/zstd_internal.h" #include "zstd_decompress_internal.h" /* ZSTD_DCtx */ #include "zstd_ddict.h" /* ZSTD_DDictDictContent */ #include "zstd_decompress_block.h" +#include "../common/bits.h" /* ZSTD_highbit32 */ /*_******************************************************* * Macros @@ -89,7 +89,7 @@ static void ZSTD_allocateLiteralsBuffer(ZSTD_DCtx* dctx, void* const dst, const dctx->litBufferEnd = dctx->litBuffer + litSize - ZSTD_LITBUFFEREXTRASIZE; } else { - /* initially this will be stored entirely in dst during huffman decoding, it will partially shifted to litExtraBuffer after */ + /* initially this will be stored entirely in dst during huffman decoding, it will partially be shifted to litExtraBuffer after */ dctx->litBuffer = (BYTE*)dst + expectedWriteSize - litSize; dctx->litBufferEnd = (BYTE*)dst + expectedWriteSize; } @@ -134,13 +134,16 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx, ZSTD_FALLTHROUGH; case set_compressed: - RETURN_ERROR_IF(srcSize < 5, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 3; here we need up to 5 for case 3"); + RETURN_ERROR_IF(srcSize < 5, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 2; here we need up to 5 for case 3"); { size_t lhSize, litSize, litCSize; U32 singleStream=0; U32 const lhlCode = (istart[0] >> 2) & 3; U32 const lhc = MEM_readLE32(istart); size_t hufSuccess; size_t expectedWriteSize = MIN(ZSTD_BLOCKSIZE_MAX, dstCapacity); + int const flags = 0 + | (ZSTD_DCtx_get_bmi2(dctx) ? HUF_flags_bmi2 : 0) + | (dctx->disableHufAsm ? HUF_flags_disableAsm : 0); switch(lhlCode) { case 0: case 1: default: /* note : default is impossible, since lhlCode into [0..3] */ @@ -165,6 +168,10 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx, } RETURN_ERROR_IF(litSize > 0 && dst == NULL, dstSize_tooSmall, "NULL not handled"); RETURN_ERROR_IF(litSize > ZSTD_BLOCKSIZE_MAX, corruption_detected, ""); + if (!singleStream) + RETURN_ERROR_IF(litSize < MIN_LITERALS_FOR_4_STREAMS, literals_headerWrong, + "Not enough literals (%zu) for the 4-streams mode (min %u)", + litSize, MIN_LITERALS_FOR_4_STREAMS); RETURN_ERROR_IF(litCSize + lhSize > srcSize, corruption_detected, ""); RETURN_ERROR_IF(expectedWriteSize < litSize , dstSize_tooSmall, ""); ZSTD_allocateLiteralsBuffer(dctx, dst, dstCapacity, litSize, streaming, expectedWriteSize, 0); @@ -176,13 +183,14 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx, if (litEncType==set_repeat) { if (singleStream) { - hufSuccess = HUF_decompress1X_usingDTable_bmi2( + hufSuccess = HUF_decompress1X_usingDTable( dctx->litBuffer, litSize, istart+lhSize, litCSize, - dctx->HUFptr, ZSTD_DCtx_get_bmi2(dctx)); + dctx->HUFptr, flags); } else { - hufSuccess = HUF_decompress4X_usingDTable_bmi2( + assert(litSize >= MIN_LITERALS_FOR_4_STREAMS); + hufSuccess = HUF_decompress4X_usingDTable( dctx->litBuffer, litSize, istart+lhSize, litCSize, - dctx->HUFptr, ZSTD_DCtx_get_bmi2(dctx)); + dctx->HUFptr, flags); } } else { if (singleStream) { @@ -190,18 +198,18 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx, hufSuccess = HUF_decompress1X_DCtx_wksp( dctx->entropy.hufTable, dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->workspace, - sizeof(dctx->workspace)); + sizeof(dctx->workspace), flags); #else - hufSuccess = HUF_decompress1X1_DCtx_wksp_bmi2( + hufSuccess = HUF_decompress1X1_DCtx_wksp( dctx->entropy.hufTable, dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->workspace, - sizeof(dctx->workspace), ZSTD_DCtx_get_bmi2(dctx)); + sizeof(dctx->workspace), flags); #endif } else { - hufSuccess = HUF_decompress4X_hufOnly_wksp_bmi2( + hufSuccess = HUF_decompress4X_hufOnly_wksp( dctx->entropy.hufTable, dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->workspace, - sizeof(dctx->workspace), ZSTD_DCtx_get_bmi2(dctx)); + sizeof(dctx->workspace), flags); } } if (dctx->litBufferLocation == ZSTD_split) @@ -237,6 +245,7 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx, break; case 3: lhSize = 3; + RETURN_ERROR_IF(srcSize<3, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 2; here we need lhSize = 3"); litSize = MEM_readLE24(istart) >> 4; break; } @@ -279,12 +288,13 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx, break; case 1: lhSize = 2; + RETURN_ERROR_IF(srcSize<3, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 2; here we need lhSize+1 = 3"); litSize = MEM_readLE16(istart) >> 4; break; case 3: lhSize = 3; + RETURN_ERROR_IF(srcSize<4, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 2; here we need lhSize+1 = 4"); litSize = MEM_readLE24(istart) >> 4; - RETURN_ERROR_IF(srcSize<4, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 3; here we need lhSize+1 = 4"); break; } RETURN_ERROR_IF(litSize > 0 && dst == NULL, dstSize_tooSmall, "NULL not handled"); @@ -506,14 +516,15 @@ void ZSTD_buildFSETable_body(ZSTD_seqSymbol* dt, for (i = 8; i < n; i += 8) { MEM_write64(spread + pos + i, sv); } - pos += n; + assert(n>=0); + pos += (size_t)n; } } /* Now we spread those positions across the table. - * The benefit of doing it in two stages is that we avoid the the + * The benefit of doing it in two stages is that we avoid the * variable size inner loop, which caused lots of branch misses. * Now we can run through all the positions without any branch misses. - * We unroll the loop twice, since that is what emperically worked best. + * We unroll the loop twice, since that is what empirically worked best. */ { size_t position = 0; @@ -540,7 +551,7 @@ void ZSTD_buildFSETable_body(ZSTD_seqSymbol* dt, for (i=0; i highThreshold) position = (position + step) & tableMask; /* lowprob area */ + while (UNLIKELY(position > highThreshold)) position = (position + step) & tableMask; /* lowprob area */ } } assert(position == 0); /* position must reach all cells once, otherwise normalizedCounter is incorrect */ } @@ -551,7 +562,7 @@ void ZSTD_buildFSETable_body(ZSTD_seqSymbol* dt, for (u=0; ustateLL.table + seqState->stateLL.state, sizeof(ZSTD_seqSymbol)); + ZSTD_memcpy(mlDInfo, seqState->stateML.table + seqState->stateML.state, sizeof(ZSTD_seqSymbol)); + ZSTD_memcpy(ofDInfo, seqState->stateOffb.table + seqState->stateOffb.state, sizeof(ZSTD_seqSymbol)); +#else const ZSTD_seqSymbol* const llDInfo = seqState->stateLL.table + seqState->stateLL.state; const ZSTD_seqSymbol* const mlDInfo = seqState->stateML.table + seqState->stateML.state; const ZSTD_seqSymbol* const ofDInfo = seqState->stateOffb.table + seqState->stateOffb.state; +#endif seq.matchLength = mlDInfo->baseValue; seq.litLength = llDInfo->baseValue; { U32 const ofBase = ofDInfo->baseValue; @@ -1186,28 +1220,31 @@ ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e longOffsets) U32 const llnbBits = llDInfo->nbBits; U32 const mlnbBits = mlDInfo->nbBits; U32 const ofnbBits = ofDInfo->nbBits; + + assert(llBits <= MaxLLBits); + assert(mlBits <= MaxMLBits); + assert(ofBits <= MaxOff); /* * As gcc has better branch and block analyzers, sometimes it is only - * valuable to mark likelyness for clang, it gives around 3-4% of + * valuable to mark likeliness for clang, it gives around 3-4% of * performance. */ /* sequence */ { size_t offset; - #if defined(__clang__) - if (LIKELY(ofBits > 1)) { - #else if (ofBits > 1) { - #endif ZSTD_STATIC_ASSERT(ZSTD_lo_isLongOffset == 1); ZSTD_STATIC_ASSERT(LONG_OFFSETS_MAX_EXTRA_BITS_32 == 5); - assert(ofBits <= MaxOff); + ZSTD_STATIC_ASSERT(STREAM_ACCUMULATOR_MIN_32 > LONG_OFFSETS_MAX_EXTRA_BITS_32); + ZSTD_STATIC_ASSERT(STREAM_ACCUMULATOR_MIN_32 - LONG_OFFSETS_MAX_EXTRA_BITS_32 >= MaxMLBits); if (MEM_32bits() && longOffsets && (ofBits >= STREAM_ACCUMULATOR_MIN_32)) { - U32 const extraBits = ofBits - MIN(ofBits, 32 - seqState->DStream.bitsConsumed); + /* Always read extra bits, this keeps the logic simple, + * avoids branches, and avoids accidentally reading 0 bits. + */ + U32 const extraBits = LONG_OFFSETS_MAX_EXTRA_BITS_32; offset = ofBase + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits); BIT_reloadDStream(&seqState->DStream); - if (extraBits) offset += BIT_readBitsFast(&seqState->DStream, extraBits); - assert(extraBits <= LONG_OFFSETS_MAX_EXTRA_BITS_32); /* to avoid another reload */ + offset += BIT_readBitsFast(&seqState->DStream, extraBits); } else { offset = ofBase + BIT_readBitsFast(&seqState->DStream, ofBits/*>0*/); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); @@ -1232,11 +1269,7 @@ ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e longOffsets) seq.offset = offset; } - #if defined(__clang__) - if (UNLIKELY(mlBits > 0)) - #else if (mlBits > 0) - #endif seq.matchLength += BIT_readBitsFast(&seqState->DStream, mlBits/*>0*/); if (MEM_32bits() && (mlBits+llBits >= STREAM_ACCUMULATOR_MIN_32-LONG_OFFSETS_MAX_EXTRA_BITS_32)) @@ -1246,11 +1279,7 @@ ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e longOffsets) /* Ensure there are enough bits to read the rest of data in 64-bit mode. */ ZSTD_STATIC_ASSERT(16+LLFSELog+MLFSELog+OffFSELog < STREAM_ACCUMULATOR_MIN_64); - #if defined(__clang__) - if (UNLIKELY(llBits > 0)) - #else if (llBits > 0) - #endif seq.litLength += BIT_readBitsFast(&seqState->DStream, llBits/*>0*/); if (MEM_32bits()) @@ -1552,7 +1581,7 @@ ZSTD_decompressSequences_body(ZSTD_DCtx* dctx, const BYTE* const prefixStart = (const BYTE*)(dctx->prefixStart); const BYTE* const vBase = (const BYTE*)(dctx->virtualStart); const BYTE* const dictEnd = (const BYTE*)(dctx->dictEnd); - DEBUGLOG(5, "ZSTD_decompressSequences_body"); + DEBUGLOG(5, "ZSTD_decompressSequences_body: nbSeq = %d", nbSeq); (void)frame; /* Regen sequences */ @@ -1945,34 +1974,79 @@ ZSTD_decompressSequencesLong(ZSTD_DCtx* dctx, #endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT */ +/** + * @returns The total size of the history referenceable by zstd, including + * both the prefix and the extDict. At @p op any offset larger than this + * is invalid. + */ +static size_t ZSTD_totalHistorySize(BYTE* op, BYTE const* virtualStart) +{ + return (size_t)(op - virtualStart); +} -#if !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT) && \ - !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG) -/* ZSTD_getLongOffsetsShare() : +typedef struct { + unsigned longOffsetShare; + unsigned maxNbAdditionalBits; +} ZSTD_OffsetInfo; + +/* ZSTD_getOffsetInfo() : * condition : offTable must be valid * @return : "share" of long offsets (arbitrarily defined as > (1<<23)) - * compared to maximum possible of (1< 22) total += 1; + ZSTD_OffsetInfo info = {0, 0}; + /* If nbSeq == 0, then the offTable is uninitialized, but we have + * no sequences, so both values should be 0. + */ + if (nbSeq != 0) { + const void* ptr = offTable; + U32 const tableLog = ((const ZSTD_seqSymbol_header*)ptr)[0].tableLog; + const ZSTD_seqSymbol* table = offTable + 1; + U32 const max = 1 << tableLog; + U32 u; + DEBUGLOG(5, "ZSTD_getLongOffsetsShare: (tableLog=%u)", tableLog); + + assert(max <= (1 << OffFSELog)); /* max not too large */ + for (u=0; u 22) info.longOffsetShare += 1; + } + + assert(tableLog <= OffFSELog); + info.longOffsetShare <<= (OffFSELog - tableLog); /* scale to OffFSELog */ } - assert(tableLog <= OffFSELog); - total <<= (OffFSELog - tableLog); /* scale to OffFSELog */ + return info; +} - return total; +/** + * @returns The maximum offset we can decode in one read of our bitstream, without + * reloading more bits in the middle of the offset bits read. Any offsets larger + * than this must use the long offset decoder. + */ +static size_t ZSTD_maxShortOffset(void) +{ + if (MEM_64bits()) { + /* We can decode any offset without reloading bits. + * This might change if the max window size grows. + */ + ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX <= 31); + return (size_t)-1; + } else { + /* The maximum offBase is (1 << (STREAM_ACCUMULATOR_MIN + 1)) - 1. + * This offBase would require STREAM_ACCUMULATOR_MIN extra bits. + * Then we have to subtract ZSTD_REP_NUM to get the maximum possible offset. + */ + size_t const maxOffbase = ((size_t)1 << (STREAM_ACCUMULATOR_MIN + 1)) - 1; + size_t const maxOffset = maxOffbase - ZSTD_REP_NUM; + assert(ZSTD_highbit32((U32)maxOffbase) == STREAM_ACCUMULATOR_MIN); + return maxOffset; + } } -#endif size_t ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, @@ -1980,20 +2054,21 @@ ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, const void* src, size_t srcSize, const int frame, const streaming_operation streaming) { /* blockType == blockCompressed */ const BYTE* ip = (const BYTE*)src; - /* isLongOffset must be true if there are long offsets. - * Offsets are long if they are larger than 2^STREAM_ACCUMULATOR_MIN. - * We don't expect that to be the case in 64-bit mode. - * In block mode, window size is not known, so we have to be conservative. - * (note: but it could be evaluated from current-lowLimit) - */ - ZSTD_longOffset_e const isLongOffset = (ZSTD_longOffset_e)(MEM_32bits() && (!frame || (dctx->fParams.windowSize > (1ULL << STREAM_ACCUMULATOR_MIN)))); DEBUGLOG(5, "ZSTD_decompressBlock_internal (size : %u)", (U32)srcSize); - RETURN_ERROR_IF(srcSize >= ZSTD_BLOCKSIZE_MAX, srcSize_wrong, ""); + /* Note : the wording of the specification + * allows compressed block to be sized exactly ZSTD_BLOCKSIZE_MAX. + * This generally does not happen, as it makes little sense, + * since an uncompressed block would feature same size and have no decompression cost. + * Also, note that decoder from reference libzstd before < v1.5.4 + * would consider this edge case as an error. + * As a consequence, avoid generating compressed blocks of size ZSTD_BLOCKSIZE_MAX + * for broader compatibility with the deployed ecosystem of zstd decoders */ + RETURN_ERROR_IF(srcSize > ZSTD_BLOCKSIZE_MAX, srcSize_wrong, ""); /* Decode literals section */ { size_t const litCSize = ZSTD_decodeLiteralsBlock(dctx, src, srcSize, dst, dstCapacity, streaming); - DEBUGLOG(5, "ZSTD_decodeLiteralsBlock : %u", (U32)litCSize); + DEBUGLOG(5, "ZSTD_decodeLiteralsBlock : cSize=%u, nbLiterals=%zu", (U32)litCSize, dctx->litSize); if (ZSTD_isError(litCSize)) return litCSize; ip += litCSize; srcSize -= litCSize; @@ -2001,6 +2076,23 @@ ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, /* Build Decoding Tables */ { + /* Compute the maximum block size, which must also work when !frame and fParams are unset. + * Additionally, take the min with dstCapacity to ensure that the totalHistorySize fits in a size_t. + */ + size_t const blockSizeMax = MIN(dstCapacity, (frame ? dctx->fParams.blockSizeMax : ZSTD_BLOCKSIZE_MAX)); + size_t const totalHistorySize = ZSTD_totalHistorySize((BYTE*)dst + blockSizeMax, (BYTE const*)dctx->virtualStart); + /* isLongOffset must be true if there are long offsets. + * Offsets are long if they are larger than ZSTD_maxShortOffset(). + * We don't expect that to be the case in 64-bit mode. + * + * We check here to see if our history is large enough to allow long offsets. + * If it isn't, then we can't possible have (valid) long offsets. If the offset + * is invalid, then it is okay to read it incorrectly. + * + * If isLongOffsets is true, then we will later check our decoding table to see + * if it is even possible to generate long offsets. + */ + ZSTD_longOffset_e isLongOffset = (ZSTD_longOffset_e)(MEM_32bits() && (totalHistorySize > ZSTD_maxShortOffset())); /* These macros control at build-time which decompressor implementation * we use. If neither is defined, we do some inspection and dispatch at * runtime. @@ -2008,6 +2100,11 @@ ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, #if !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT) && \ !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG) int usePrefetchDecoder = dctx->ddictIsCold; +#else + /* Set to 1 to avoid computing offset info if we don't need to. + * Otherwise this value is ignored. + */ + int usePrefetchDecoder = 1; #endif int nbSeq; size_t const seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, srcSize); @@ -2015,28 +2112,42 @@ ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, ip += seqHSize; srcSize -= seqHSize; - RETURN_ERROR_IF(dst == NULL && nbSeq > 0, dstSize_tooSmall, "NULL not handled"); + RETURN_ERROR_IF((dst == NULL || dstCapacity == 0) && nbSeq > 0, dstSize_tooSmall, "NULL not handled"); + RETURN_ERROR_IF(MEM_64bits() && sizeof(size_t) == sizeof(void*) && (size_t)(-1) - (size_t)dst < (size_t)(1 << 20), dstSize_tooSmall, + "invalid dst"); -#if !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT) && \ - !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG) - if ( !usePrefetchDecoder - && (!frame || (dctx->fParams.windowSize > (1<<24))) - && (nbSeq>ADVANCED_SEQS) ) { /* could probably use a larger nbSeq limit */ - U32 const shareLongOffsets = ZSTD_getLongOffsetsShare(dctx->OFTptr); - U32 const minShare = MEM_64bits() ? 7 : 20; /* heuristic values, correspond to 2.73% and 7.81% */ - usePrefetchDecoder = (shareLongOffsets >= minShare); + /* If we could potentially have long offsets, or we might want to use the prefetch decoder, + * compute information about the share of long offsets, and the maximum nbAdditionalBits. + * NOTE: could probably use a larger nbSeq limit + */ + if (isLongOffset || (!usePrefetchDecoder && (totalHistorySize > (1u << 24)) && (nbSeq > 8))) { + ZSTD_OffsetInfo const info = ZSTD_getOffsetInfo(dctx->OFTptr, nbSeq); + if (isLongOffset && info.maxNbAdditionalBits <= STREAM_ACCUMULATOR_MIN) { + /* If isLongOffset, but the maximum number of additional bits that we see in our table is small + * enough, then we know it is impossible to have too long an offset in this block, so we can + * use the regular offset decoder. + */ + isLongOffset = ZSTD_lo_isRegularOffset; + } + if (!usePrefetchDecoder) { + U32 const minShare = MEM_64bits() ? 7 : 20; /* heuristic values, correspond to 2.73% and 7.81% */ + usePrefetchDecoder = (info.longOffsetShare >= minShare); + } } -#endif dctx->ddictIsCold = 0; #if !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT) && \ !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG) - if (usePrefetchDecoder) + if (usePrefetchDecoder) { +#else + (void)usePrefetchDecoder; + { #endif #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset, frame); #endif + } #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG /* else */ @@ -2060,9 +2171,9 @@ void ZSTD_checkContinuity(ZSTD_DCtx* dctx, const void* dst, size_t dstSize) } -size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize) +size_t ZSTD_decompressBlock_deprecated(ZSTD_DCtx* dctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize) { size_t dSize; ZSTD_checkContinuity(dctx, dst, dstCapacity); @@ -2070,3 +2181,12 @@ size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, dctx->previousDstEnd = (char*)dst + dSize; return dSize; } + + +/* NOTE: Must just wrap ZSTD_decompressBlock_deprecated() */ +size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize) +{ + return ZSTD_decompressBlock_deprecated(dctx, dst, dstCapacity, src, srcSize); +} diff --git a/internal-complibs/zstd-1.5.2/decompress/zstd_decompress_block.h b/internal-complibs/zstd-1.5.5/decompress/zstd_decompress_block.h similarity index 88% rename from internal-complibs/zstd-1.5.2/decompress/zstd_decompress_block.h rename to internal-complibs/zstd-1.5.5/decompress/zstd_decompress_block.h index c61a9d0c..9d131888 100644 --- a/internal-complibs/zstd-1.5.2/decompress/zstd_decompress_block.h +++ b/internal-complibs/zstd-1.5.5/decompress/zstd_decompress_block.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -64,5 +64,10 @@ void ZSTD_buildFSETable(ZSTD_seqSymbol* dt, unsigned tableLog, void* wksp, size_t wkspSize, int bmi2); +/* Internal definition of ZSTD_decompressBlock() to avoid deprecation warnings. */ +size_t ZSTD_decompressBlock_deprecated(ZSTD_DCtx* dctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize); + #endif /* ZSTD_DEC_BLOCK_H */ diff --git a/internal-complibs/zstd-1.5.2/decompress/zstd_decompress_internal.h b/internal-complibs/zstd-1.5.5/decompress/zstd_decompress_internal.h similarity index 97% rename from internal-complibs/zstd-1.5.2/decompress/zstd_decompress_internal.h rename to internal-complibs/zstd-1.5.5/decompress/zstd_decompress_internal.h index 2b5a5385..c2ec5d9f 100644 --- a/internal-complibs/zstd-1.5.2/decompress/zstd_decompress_internal.h +++ b/internal-complibs/zstd-1.5.5/decompress/zstd_decompress_internal.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -75,12 +75,13 @@ static UNUSED_ATTR const U32 ML_base[MaxML+1] = { #define ZSTD_BUILD_FSE_TABLE_WKSP_SIZE (sizeof(S16) * (MaxSeq + 1) + (1u << MaxFSELog) + sizeof(U64)) #define ZSTD_BUILD_FSE_TABLE_WKSP_SIZE_U32 ((ZSTD_BUILD_FSE_TABLE_WKSP_SIZE + sizeof(U32) - 1) / sizeof(U32)) +#define ZSTD_HUFFDTABLE_CAPACITY_LOG 12 typedef struct { ZSTD_seqSymbol LLTable[SEQSYMBOL_TABLE_SIZE(LLFSELog)]; /* Note : Space reserved for FSE Tables */ ZSTD_seqSymbol OFTable[SEQSYMBOL_TABLE_SIZE(OffFSELog)]; /* is also used as temporary workspace while building hufTable during DDict creation */ ZSTD_seqSymbol MLTable[SEQSYMBOL_TABLE_SIZE(MLFSELog)]; /* and therefore must be at least HUF_DECOMPRESS_WORKSPACE_SIZE large */ - HUF_DTable hufTable[HUF_DTABLE_SIZE(HufLog)]; /* can accommodate HUF_decompress4X */ + HUF_DTable hufTable[HUF_DTABLE_SIZE(ZSTD_HUFFDTABLE_CAPACITY_LOG)]; /* can accommodate HUF_decompress4X */ U32 rep[ZSTD_REP_NUM]; U32 workspace[ZSTD_BUILD_FSE_TABLE_WKSP_SIZE_U32]; } ZSTD_entropyDTables_t; @@ -164,6 +165,7 @@ struct ZSTD_DCtx_s ZSTD_dictUses_e dictUses; ZSTD_DDictHashSet* ddictSet; /* Hash set for multiple ddicts */ ZSTD_refMultipleDDicts_e refMultipleDDicts; /* User specified: if == 1, will allow references to multiple DDicts. Default == 0 (disabled) */ + int disableHufAsm; /* streaming */ ZSTD_dStreamStage streamStage; diff --git a/internal-complibs/zstd-1.5.2/deprecated/zbuff.h b/internal-complibs/zstd-1.5.5/deprecated/zbuff.h similarity index 99% rename from internal-complibs/zstd-1.5.2/deprecated/zbuff.h rename to internal-complibs/zstd-1.5.5/deprecated/zbuff.h index b83ea0fe..a968245b 100644 --- a/internal-complibs/zstd-1.5.2/deprecated/zbuff.h +++ b/internal-complibs/zstd-1.5.5/deprecated/zbuff.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the diff --git a/internal-complibs/zstd-1.5.2/deprecated/zbuff_common.c b/internal-complibs/zstd-1.5.5/deprecated/zbuff_common.c similarity index 94% rename from internal-complibs/zstd-1.5.2/deprecated/zbuff_common.c rename to internal-complibs/zstd-1.5.5/deprecated/zbuff_common.c index e7d01a08..5a2f2db3 100644 --- a/internal-complibs/zstd-1.5.2/deprecated/zbuff_common.c +++ b/internal-complibs/zstd-1.5.5/deprecated/zbuff_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the diff --git a/internal-complibs/zstd-1.5.2/deprecated/zbuff_compress.c b/internal-complibs/zstd-1.5.5/deprecated/zbuff_compress.c similarity index 99% rename from internal-complibs/zstd-1.5.2/deprecated/zbuff_compress.c rename to internal-complibs/zstd-1.5.5/deprecated/zbuff_compress.c index 51cf158c..1d868215 100644 --- a/internal-complibs/zstd-1.5.2/deprecated/zbuff_compress.c +++ b/internal-complibs/zstd-1.5.5/deprecated/zbuff_compress.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the diff --git a/internal-complibs/zstd-1.5.2/deprecated/zbuff_decompress.c b/internal-complibs/zstd-1.5.5/deprecated/zbuff_decompress.c similarity index 89% rename from internal-complibs/zstd-1.5.2/deprecated/zbuff_decompress.c rename to internal-complibs/zstd-1.5.5/deprecated/zbuff_decompress.c index d73c0f35..12a66af7 100644 --- a/internal-complibs/zstd-1.5.2/deprecated/zbuff_decompress.c +++ b/internal-complibs/zstd-1.5.5/deprecated/zbuff_decompress.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -13,6 +13,8 @@ /* ************************************* * Dependencies ***************************************/ +#define ZSTD_DISABLE_DEPRECATE_WARNINGS /* suppress warning on ZSTD_initDStream_usingDict */ +#include "../zstd.h" /* ZSTD_CStream, ZSTD_DStream, ZSTDLIB_API */ #define ZBUFF_STATIC_LINKING_ONLY #include "zbuff.h" diff --git a/internal-complibs/zstd-1.5.2/dictBuilder/cover.c b/internal-complibs/zstd-1.5.5/dictBuilder/cover.c similarity index 97% rename from internal-complibs/zstd-1.5.2/dictBuilder/cover.c rename to internal-complibs/zstd-1.5.5/dictBuilder/cover.c index 028802a1..9e5e7d5b 100644 --- a/internal-complibs/zstd-1.5.2/dictBuilder/cover.c +++ b/internal-complibs/zstd-1.5.5/dictBuilder/cover.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -34,6 +34,7 @@ #include "../common/pool.h" #include "../common/threading.h" #include "../common/zstd_internal.h" /* includes zstd.h */ +#include "../common/bits.h" /* ZSTD_highbit32 */ #include "../zdict.h" #include "cover.h" @@ -541,7 +542,7 @@ static void COVER_ctx_destroy(COVER_ctx_t *ctx) { /** * Prepare a context for dictionary building. - * The context is only dependent on the parameter `d` and can used multiple + * The context is only dependent on the parameter `d` and can be used multiple * times. * Returns 0 on success or error code on error. * The context must be destroyed with `COVER_ctx_destroy()`. @@ -646,7 +647,7 @@ static size_t COVER_ctx_init(COVER_ctx_t *ctx, const void *samplesBuffer, void COVER_warnOnSmallCorpus(size_t maxDictSize, size_t nbDmers, int displayLevel) { - const double ratio = (double)nbDmers / maxDictSize; + const double ratio = (double)nbDmers / (double)maxDictSize; if (ratio >= 10) { return; } @@ -950,9 +951,17 @@ void COVER_best_finish(COVER_best_t *best, ZDICT_cover_params_t parameters, } } +static COVER_dictSelection_t setDictSelection(BYTE* buf, size_t s, size_t csz) +{ + COVER_dictSelection_t ds; + ds.dictContent = buf; + ds.dictSize = s; + ds.totalCompressedSize = csz; + return ds; +} + COVER_dictSelection_t COVER_dictSelectionError(size_t error) { - COVER_dictSelection_t selection = { NULL, 0, error }; - return selection; + return setDictSelection(NULL, 0, error); } unsigned COVER_dictSelectionIsError(COVER_dictSelection_t selection) { @@ -1005,9 +1014,8 @@ COVER_dictSelection_t COVER_selectDict(BYTE* customDictContent, size_t dictBuffe } if (params.shrinkDict == 0) { - COVER_dictSelection_t selection = { largestDictbuffer, dictContentSize, totalCompressedSize }; free(candidateDictBuffer); - return selection; + return setDictSelection(largestDictbuffer, dictContentSize, totalCompressedSize); } largestDict = dictContentSize; @@ -1039,20 +1047,16 @@ COVER_dictSelection_t COVER_selectDict(BYTE* customDictContent, size_t dictBuffe return COVER_dictSelectionError(totalCompressedSize); } - if (totalCompressedSize <= largestCompressed * regressionTolerance) { - COVER_dictSelection_t selection = { candidateDictBuffer, dictContentSize, totalCompressedSize }; + if ((double)totalCompressedSize <= (double)largestCompressed * regressionTolerance) { free(largestDictbuffer); - return selection; + return setDictSelection( candidateDictBuffer, dictContentSize, totalCompressedSize ); } dictContentSize *= 2; } dictContentSize = largestDict; totalCompressedSize = largestCompressed; - { - COVER_dictSelection_t selection = { largestDictbuffer, dictContentSize, totalCompressedSize }; - free(candidateDictBuffer); - return selection; - } + free(candidateDictBuffer); + return setDictSelection( largestDictbuffer, dictContentSize, totalCompressedSize ); } /** diff --git a/internal-complibs/zstd-1.5.2/dictBuilder/cover.h b/internal-complibs/zstd-1.5.5/dictBuilder/cover.h similarity index 98% rename from internal-complibs/zstd-1.5.2/dictBuilder/cover.h rename to internal-complibs/zstd-1.5.5/dictBuilder/cover.h index 1aacdddd..252624bd 100644 --- a/internal-complibs/zstd-1.5.2/dictBuilder/cover.h +++ b/internal-complibs/zstd-1.5.5/dictBuilder/cover.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the diff --git a/internal-complibs/zstd-1.5.2/dictBuilder/divsufsort.c b/internal-complibs/zstd-1.5.5/dictBuilder/divsufsort.c similarity index 100% rename from internal-complibs/zstd-1.5.2/dictBuilder/divsufsort.c rename to internal-complibs/zstd-1.5.5/dictBuilder/divsufsort.c diff --git a/internal-complibs/zstd-1.5.2/dictBuilder/divsufsort.h b/internal-complibs/zstd-1.5.5/dictBuilder/divsufsort.h similarity index 100% rename from internal-complibs/zstd-1.5.2/dictBuilder/divsufsort.h rename to internal-complibs/zstd-1.5.5/dictBuilder/divsufsort.h diff --git a/internal-complibs/zstd-1.5.2/dictBuilder/fastcover.c b/internal-complibs/zstd-1.5.5/dictBuilder/fastcover.c similarity index 99% rename from internal-complibs/zstd-1.5.2/dictBuilder/fastcover.c rename to internal-complibs/zstd-1.5.5/dictBuilder/fastcover.c index 3352859a..46bba012 100644 --- a/internal-complibs/zstd-1.5.2/dictBuilder/fastcover.c +++ b/internal-complibs/zstd-1.5.5/dictBuilder/fastcover.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -304,7 +304,7 @@ FASTCOVER_computeFrequency(U32* freqs, const FASTCOVER_ctx_t* ctx) /** * Prepare a context for dictionary building. - * The context is only dependent on the parameter `d` and can used multiple + * The context is only dependent on the parameter `d` and can be used multiple * times. * Returns 0 on success or error code on error. * The context must be destroyed with `FASTCOVER_ctx_destroy()`. diff --git a/internal-complibs/zstd-1.5.2/dictBuilder/zdict.c b/internal-complibs/zstd-1.5.5/dictBuilder/zdict.c similarity index 91% rename from internal-complibs/zstd-1.5.2/dictBuilder/zdict.c rename to internal-complibs/zstd-1.5.5/dictBuilder/zdict.c index 006aba7c..58290f45 100644 --- a/internal-complibs/zstd-1.5.2/dictBuilder/zdict.c +++ b/internal-complibs/zstd-1.5.5/dictBuilder/zdict.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -44,7 +44,6 @@ #ifndef ZDICT_STATIC_LINKING_ONLY # define ZDICT_STATIC_LINKING_ONLY #endif -#define HUF_STATIC_LINKING_ONLY #include "../common/mem.h" /* read */ #include "../common/fse.h" /* FSE_normalizeCount, FSE_writeNCount */ @@ -54,6 +53,7 @@ #include "../compress/zstd_compress_internal.h" /* ZSTD_loadCEntropy() */ #include "../zdict.h" #include "divsufsort.h" +#include "../common/bits.h" /* ZSTD_NbCommonBytes */ /*-************************************* @@ -130,85 +130,6 @@ size_t ZDICT_getDictHeaderSize(const void* dictBuffer, size_t dictSize) /*-******************************************************** * Dictionary training functions **********************************************************/ -static unsigned ZDICT_NbCommonBytes (size_t val) -{ - if (MEM_isLittleEndian()) { - if (MEM_64bits()) { -# if defined(_MSC_VER) && defined(_WIN64) - if (val != 0) { - unsigned long r; - _BitScanForward64(&r, (U64)val); - return (unsigned)(r >> 3); - } else { - /* Should not reach this code path */ - __assume(0); - } -# elif defined(__GNUC__) && (__GNUC__ >= 3) - return (unsigned)(__builtin_ctzll((U64)val) >> 3); -# else - static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 }; - return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58]; -# endif - } else { /* 32 bits */ -# if defined(_MSC_VER) - if (val != 0) { - unsigned long r; - _BitScanForward(&r, (U32)val); - return (unsigned)(r >> 3); - } else { - /* Should not reach this code path */ - __assume(0); - } -# elif defined(__GNUC__) && (__GNUC__ >= 3) - return (unsigned)(__builtin_ctz((U32)val) >> 3); -# else - static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 }; - return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27]; -# endif - } - } else { /* Big Endian CPU */ - if (MEM_64bits()) { -# if defined(_MSC_VER) && defined(_WIN64) - if (val != 0) { - unsigned long r; - _BitScanReverse64(&r, val); - return (unsigned)(r >> 3); - } else { - /* Should not reach this code path */ - __assume(0); - } -# elif defined(__GNUC__) && (__GNUC__ >= 3) - return (unsigned)(__builtin_clzll(val) >> 3); -# else - unsigned r; - const unsigned n32 = sizeof(size_t)*4; /* calculate this way due to compiler complaining in 32-bits mode */ - if (!(val>>n32)) { r=4; } else { r=0; val>>=n32; } - if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; } - r += (!val); - return r; -# endif - } else { /* 32 bits */ -# if defined(_MSC_VER) - if (val != 0) { - unsigned long r; - _BitScanReverse(&r, (unsigned long)val); - return (unsigned)(r >> 3); - } else { - /* Should not reach this code path */ - __assume(0); - } -# elif defined(__GNUC__) && (__GNUC__ >= 3) - return (unsigned)(__builtin_clz((U32)val) >> 3); -# else - unsigned r; - if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; } - r += (!val); - return r; -# endif - } } -} - - /*! ZDICT_count() : Count the nb of common bytes between 2 pointers. Note : this function presumes end of buffer followed by noisy guard band. @@ -223,7 +144,7 @@ static size_t ZDICT_count(const void* pIn, const void* pMatch) pMatch = (const char*)pMatch+sizeof(size_t); continue; } - pIn = (const char*)pIn+ZDICT_NbCommonBytes(diff); + pIn = (const char*)pIn+ZSTD_NbCommonBytes(diff); return (size_t)((const char*)pIn - pStart); } } @@ -451,7 +372,7 @@ static U32 ZDICT_tryMerge(dictItem* table, dictItem elt, U32 eltNbToSkip, const elt = table[u]; /* sort : improve rank */ while ((u>1) && (table[u-1].savings < elt.savings)) - table[u] = table[u-1], u--; + table[u] = table[u-1], u--; table[u] = elt; return u; } } @@ -602,7 +523,7 @@ static size_t ZDICT_trainBuffer_legacy(dictItem* dictList, U32 dictListSize, if (solution.length==0) { cursor++; continue; } ZDICT_insertDictItem(dictList, dictListSize, solution, buffer); cursor += solution.length; - DISPLAYUPDATE(2, "\r%4.2f %% \r", (double)cursor / bufferSize * 100); + DISPLAYUPDATE(2, "\r%4.2f %% \r", (double)cursor / (double)bufferSize * 100.0); } } _cleanup: @@ -645,11 +566,11 @@ static void ZDICT_countEStats(EStats_ress_t esr, const ZSTD_parameters* params, size_t cSize; if (srcSize > blockSizeMax) srcSize = blockSizeMax; /* protection vs large samples */ - { size_t const errorCode = ZSTD_compressBegin_usingCDict(esr.zc, esr.dict); + { size_t const errorCode = ZSTD_compressBegin_usingCDict_deprecated(esr.zc, esr.dict); if (ZSTD_isError(errorCode)) { DISPLAYLEVEL(1, "warning : ZSTD_compressBegin_usingCDict failed \n"); return; } } - cSize = ZSTD_compressBlock(esr.zc, esr.workPlace, ZSTD_BLOCKSIZE_MAX, src, srcSize); + cSize = ZSTD_compressBlock_deprecated(esr.zc, esr.workPlace, ZSTD_BLOCKSIZE_MAX, src, srcSize); if (ZSTD_isError(cSize)) { DISPLAYLEVEL(3, "warning : could not compress sample size %u \n", (unsigned)srcSize); return; } if (cSize) { /* if == 0; block is not compressible */ @@ -754,6 +675,7 @@ static size_t ZDICT_analyzeEntropy(void* dstBuffer, size_t maxDstSize, size_t const totalSrcSize = ZDICT_totalSampleSize(fileSizes, nbFiles); size_t const averageSampleSize = totalSrcSize / (nbFiles + !nbFiles); BYTE* dstPtr = (BYTE*)dstBuffer; + U32 wksp[HUF_CTABLE_WORKSPACE_SIZE_U32]; /* init */ DEBUGLOG(4, "ZDICT_analyzeEntropy"); @@ -794,7 +716,7 @@ static size_t ZDICT_analyzeEntropy(void* dstBuffer, size_t maxDstSize, } } /* analyze, build stats, starting with literals */ - { size_t maxNbBits = HUF_buildCTable (hufTable, countLit, 255, huffLog); + { size_t maxNbBits = HUF_buildCTable_wksp(hufTable, countLit, 255, huffLog, wksp, sizeof(wksp)); if (HUF_isError(maxNbBits)) { eSize = maxNbBits; DISPLAYLEVEL(1, " HUF_buildCTable error \n"); @@ -803,7 +725,7 @@ static size_t ZDICT_analyzeEntropy(void* dstBuffer, size_t maxDstSize, if (maxNbBits==8) { /* not compressible : will fail on HUF_writeCTable() */ DISPLAYLEVEL(2, "warning : pathological dataset : literals are not compressible : samples are noisy or too regular \n"); ZDICT_flatLit(countLit); /* replace distribution by a fake "mostly flat but still compressible" distribution, that HUF_writeCTable() can encode */ - maxNbBits = HUF_buildCTable (hufTable, countLit, 255, huffLog); + maxNbBits = HUF_buildCTable_wksp(hufTable, countLit, 255, huffLog, wksp, sizeof(wksp)); assert(maxNbBits==9); } huffLog = (U32)maxNbBits; @@ -844,7 +766,7 @@ static size_t ZDICT_analyzeEntropy(void* dstBuffer, size_t maxDstSize, llLog = (U32)errorCode; /* write result to buffer */ - { size_t const hhSize = HUF_writeCTable(dstPtr, maxDstSize, hufTable, 255, huffLog); + { size_t const hhSize = HUF_writeCTable_wksp(dstPtr, maxDstSize, hufTable, 255, huffLog, wksp, sizeof(wksp)); if (HUF_isError(hhSize)) { eSize = hhSize; DISPLAYLEVEL(1, "HUF_writeCTable error \n"); diff --git a/internal-complibs/zstd-1.5.2/dll/example/Makefile b/internal-complibs/zstd-1.5.5/dll/example/Makefile similarity index 96% rename from internal-complibs/zstd-1.5.2/dll/example/Makefile rename to internal-complibs/zstd-1.5.5/dll/example/Makefile index 03b034dd..86cf6906 100644 --- a/internal-complibs/zstd-1.5.2/dll/example/Makefile +++ b/internal-complibs/zstd-1.5.5/dll/example/Makefile @@ -1,5 +1,5 @@ # ################################################################ -# Copyright (c) Yann Collet, Facebook, Inc. +# Copyright (c) Meta Platforms, Inc. and affiliates. # All rights reserved. # # This source code is licensed under both the BSD-style license (found in the diff --git a/internal-complibs/zstd-1.5.2/dll/example/README.md b/internal-complibs/zstd-1.5.5/dll/example/README.md similarity index 98% rename from internal-complibs/zstd-1.5.2/dll/example/README.md rename to internal-complibs/zstd-1.5.5/dll/example/README.md index 9e30fd59..46aec798 100644 --- a/internal-complibs/zstd-1.5.2/dll/example/README.md +++ b/internal-complibs/zstd-1.5.5/dll/example/README.md @@ -46,7 +46,7 @@ The compiled executable will require ZSTD DLL which is available at `dll\libzstd Open `example\fullbench-dll.sln` to compile `fullbench-dll` that uses a dynamic ZSTD library from the `dll` directory. The solution works with Visual C++ 2010 or newer. When one will open the solution with Visual C++ newer than 2010 -then the solution will upgraded to the current version. +then the solution will be upgraded to the current version. ## Using ZSTD DLL with Visual C++ diff --git a/internal-complibs/zstd-1.5.2/dll/example/fullbench-dll.sln b/internal-complibs/zstd-1.5.5/dll/example/fullbench-dll.sln similarity index 100% rename from internal-complibs/zstd-1.5.2/dll/example/fullbench-dll.sln rename to internal-complibs/zstd-1.5.5/dll/example/fullbench-dll.sln diff --git a/internal-complibs/zstd-1.5.2/dll/example/fullbench-dll.vcxproj b/internal-complibs/zstd-1.5.5/dll/example/fullbench-dll.vcxproj similarity index 100% rename from internal-complibs/zstd-1.5.2/dll/example/fullbench-dll.vcxproj rename to internal-complibs/zstd-1.5.5/dll/example/fullbench-dll.vcxproj diff --git a/internal-complibs/zstd-1.5.2/legacy/zstd_legacy.h b/internal-complibs/zstd-1.5.5/legacy/zstd_legacy.h similarity index 96% rename from internal-complibs/zstd-1.5.2/legacy/zstd_legacy.h rename to internal-complibs/zstd-1.5.5/legacy/zstd_legacy.h index a6f1174b..dd173251 100644 --- a/internal-complibs/zstd-1.5.2/legacy/zstd_legacy.h +++ b/internal-complibs/zstd-1.5.5/legacy/zstd_legacy.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -242,6 +242,13 @@ MEM_STATIC ZSTD_frameSizeInfo ZSTD_findFrameSizeInfoLegacy(const void *src, size frameSizeInfo.compressedSize = ERROR(srcSize_wrong); frameSizeInfo.decompressedBound = ZSTD_CONTENTSIZE_ERROR; } + /* In all cases, decompressedBound == nbBlocks * ZSTD_BLOCKSIZE_MAX. + * So we can compute nbBlocks without having to change every function. + */ + if (frameSizeInfo.decompressedBound != ZSTD_CONTENTSIZE_ERROR) { + assert((frameSizeInfo.decompressedBound & (ZSTD_BLOCKSIZE_MAX - 1)) == 0); + frameSizeInfo.nbBlocks = (size_t)(frameSizeInfo.decompressedBound / ZSTD_BLOCKSIZE_MAX); + } return frameSizeInfo; } diff --git a/internal-complibs/zstd-1.5.2/legacy/zstd_v01.c b/internal-complibs/zstd-1.5.5/legacy/zstd_v01.c similarity index 96% rename from internal-complibs/zstd-1.5.2/legacy/zstd_v01.c rename to internal-complibs/zstd-1.5.5/legacy/zstd_v01.c index 23caaef5..1a3aad07 100644 --- a/internal-complibs/zstd-1.5.2/legacy/zstd_v01.c +++ b/internal-complibs/zstd-1.5.5/legacy/zstd_v01.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Yann Collet, Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -190,25 +190,6 @@ typedef signed long long S64; /**************************************************************** * Memory I/O *****************************************************************/ -/* FSE_FORCE_MEMORY_ACCESS - * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. - * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. - * The below switch allow to select different access method for improved performance. - * Method 0 (default) : use `memcpy()`. Safe and portable. - * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). - * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. - * Method 2 : direct access. This method is portable but violate C standard. - * It can generate buggy code on targets generating assembly depending on alignment. - * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) - * See http://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details. - * Prefer these methods in priority order (0 > 1 > 2) - */ -#ifndef FSE_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ -# if defined(__INTEL_COMPILER) || defined(__GNUC__) || defined(__ICCARM__) -# define FSE_FORCE_MEMORY_ACCESS 1 -# endif -#endif - static unsigned FSE_32bits(void) { @@ -221,24 +202,6 @@ static unsigned FSE_isLittleEndian(void) return one.c[0]; } -#if defined(FSE_FORCE_MEMORY_ACCESS) && (FSE_FORCE_MEMORY_ACCESS==2) - -static U16 FSE_read16(const void* memPtr) { return *(const U16*) memPtr; } -static U32 FSE_read32(const void* memPtr) { return *(const U32*) memPtr; } -static U64 FSE_read64(const void* memPtr) { return *(const U64*) memPtr; } - -#elif defined(FSE_FORCE_MEMORY_ACCESS) && (FSE_FORCE_MEMORY_ACCESS==1) - -/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ -/* currently only defined for gcc and icc */ -typedef union { U16 u16; U32 u32; U64 u64; } __attribute__((packed)) unalign; - -static U16 FSE_read16(const void* ptr) { return ((const unalign*)ptr)->u16; } -static U32 FSE_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } -static U64 FSE_read64(const void* ptr) { return ((const unalign*)ptr)->u64; } - -#else - static U16 FSE_read16(const void* memPtr) { U16 val; memcpy(&val, memPtr, sizeof(val)); return val; @@ -254,8 +217,6 @@ static U64 FSE_read64(const void* memPtr) U64 val; memcpy(&val, memPtr, sizeof(val)); return val; } -#endif /* FSE_FORCE_MEMORY_ACCESS */ - static U16 FSE_readLE16(const void* memPtr) { if (FSE_isLittleEndian()) @@ -1190,7 +1151,7 @@ static size_t HUF_decompress (void* dst, size_t maxDstSize, const void* cSrc, si zstd - standard compression library Copyright (C) 2014-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -1759,20 +1720,26 @@ static size_t ZSTD_execSequence(BYTE* op, static const int dec32table[] = {0, 1, 2, 1, 4, 4, 4, 4}; /* added */ static const int dec64table[] = {8, 8, 8, 7, 8, 9,10,11}; /* subtracted */ const BYTE* const ostart = op; + BYTE* const oLitEnd = op + sequence.litLength; const size_t litLength = sequence.litLength; BYTE* const endMatch = op + litLength + sequence.matchLength; /* risk : address space overflow (32-bits) */ const BYTE* const litEnd = *litPtr + litLength; - /* check */ + /* checks */ + size_t const seqLength = sequence.litLength + sequence.matchLength; + + if (seqLength > (size_t)(oend - op)) return ERROR(dstSize_tooSmall); + if (sequence.litLength > (size_t)(litLimit - *litPtr)) return ERROR(corruption_detected); + /* Now we know there are no overflow in literal nor match lengths, can use pointer checks */ + if (sequence.offset > (U32)(oLitEnd - base)) return ERROR(corruption_detected); + if (endMatch > oend) return ERROR(dstSize_tooSmall); /* overwrite beyond dst buffer */ - if (litEnd > litLimit) return ERROR(corruption_detected); - if (sequence.matchLength > (size_t)(*litPtr-op)) return ERROR(dstSize_tooSmall); /* overwrite literal segment */ + if (litEnd > litLimit) return ERROR(corruption_detected); /* overRead beyond lit buffer */ + if (sequence.matchLength > (size_t)(*litPtr-op)) return ERROR(dstSize_tooSmall); /* overwrite literal segment */ /* copy Literals */ - if (((size_t)(*litPtr - op) < 8) || ((size_t)(oend-litEnd) < 8) || (op+litLength > oend-8)) - memmove(op, *litPtr, litLength); /* overwrite risk */ - else - ZSTD_wildcopy(op, *litPtr, litLength); + ZSTD_memmove(op, *litPtr, sequence.litLength); /* note : v0.1 seems to allow scenarios where output or input are close to end of buffer */ + op += litLength; *litPtr = litEnd; /* update for next sequence */ diff --git a/internal-complibs/zstd-1.5.2/legacy/zstd_v01.h b/internal-complibs/zstd-1.5.5/legacy/zstd_v01.h similarity index 98% rename from internal-complibs/zstd-1.5.2/legacy/zstd_v01.h rename to internal-complibs/zstd-1.5.5/legacy/zstd_v01.h index f777eb6e..6ac87695 100644 --- a/internal-complibs/zstd-1.5.2/legacy/zstd_v01.h +++ b/internal-complibs/zstd-1.5.5/legacy/zstd_v01.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Yann Collet, Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the diff --git a/internal-complibs/zstd-1.5.2/legacy/zstd_v02.c b/internal-complibs/zstd-1.5.5/legacy/zstd_v02.c similarity index 97% rename from internal-complibs/zstd-1.5.2/legacy/zstd_v02.c rename to internal-complibs/zstd-1.5.5/legacy/zstd_v02.c index 2f473a75..e09bb4a2 100644 --- a/internal-complibs/zstd-1.5.2/legacy/zstd_v02.c +++ b/internal-complibs/zstd-1.5.5/legacy/zstd_v02.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Yann Collet, Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -28,7 +28,7 @@ low-level memory access routines Copyright (C) 2013-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -115,24 +115,6 @@ extern "C" { /**************************************************************** * Memory I/O *****************************************************************/ -/* MEM_FORCE_MEMORY_ACCESS - * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. - * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. - * The below switch allow to select different access method for improved performance. - * Method 0 (default) : use `memcpy()`. Safe and portable. - * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). - * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. - * Method 2 : direct access. This method is portable but violate C standard. - * It can generate buggy code on targets generating assembly depending on alignment. - * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) - * See http://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details. - * Prefer these methods in priority order (0 > 1 > 2) - */ -#ifndef MEM_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ -# if defined(__INTEL_COMPILER) || defined(__GNUC__) || defined(__ICCARM__) -# define MEM_FORCE_MEMORY_ACCESS 1 -# endif -#endif MEM_STATIC unsigned MEM_32bits(void) { return sizeof(void*)==4; } MEM_STATIC unsigned MEM_64bits(void) { return sizeof(void*)==8; } @@ -143,33 +125,6 @@ MEM_STATIC unsigned MEM_isLittleEndian(void) return one.c[0]; } -#if defined(MEM_FORCE_MEMORY_ACCESS) && (MEM_FORCE_MEMORY_ACCESS==2) - -/* violates C standard on structure alignment. -Only use if no other choice to achieve best performance on target platform */ -MEM_STATIC U16 MEM_read16(const void* memPtr) { return *(const U16*) memPtr; } -MEM_STATIC U32 MEM_read32(const void* memPtr) { return *(const U32*) memPtr; } -MEM_STATIC U64 MEM_read64(const void* memPtr) { return *(const U64*) memPtr; } - -MEM_STATIC void MEM_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; } - -#elif defined(MEM_FORCE_MEMORY_ACCESS) && (MEM_FORCE_MEMORY_ACCESS==1) - -/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ -/* currently only defined for gcc and icc */ -typedef union { U16 u16; U32 u32; U64 u64; } __attribute__((packed)) unalign; - -MEM_STATIC U16 MEM_read16(const void* ptr) { return ((const unalign*)ptr)->u16; } -MEM_STATIC U32 MEM_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } -MEM_STATIC U64 MEM_read64(const void* ptr) { return ((const unalign*)ptr)->u64; } - -MEM_STATIC void MEM_write16(void* memPtr, U16 value) { ((unalign*)memPtr)->u16 = value; } - -#else - -/* default method, safe and standard. - can sometimes prove slower */ - MEM_STATIC U16 MEM_read16(const void* memPtr) { U16 val; memcpy(&val, memPtr, sizeof(val)); return val; @@ -190,9 +145,6 @@ MEM_STATIC void MEM_write16(void* memPtr, U16 value) memcpy(memPtr, &value, sizeof(value)); } -#endif /* MEM_FORCE_MEMORY_ACCESS */ - - MEM_STATIC U16 MEM_readLE16(const void* memPtr) { if (MEM_isLittleEndian()) @@ -269,7 +221,7 @@ MEM_STATIC size_t MEM_readLEST(const void* memPtr) header file (to include) Copyright (C) 2013-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -433,7 +385,7 @@ MEM_STATIC size_t BIT_lookBits(BIT_DStream_t* bitD, U32 nbBits) } /*! BIT_lookBitsFast : -* unsafe version; only works only if nbBits >= 1 */ +* unsafe version; only works if nbBits >= 1 */ MEM_STATIC size_t BIT_lookBitsFast(BIT_DStream_t* bitD, U32 nbBits) { const U32 bitMask = sizeof(bitD->bitContainer)*8 - 1; @@ -453,7 +405,7 @@ MEM_STATIC size_t BIT_readBits(BIT_DStream_t* bitD, U32 nbBits) } /*!BIT_readBitsFast : -* unsafe version; only works only if nbBits >= 1 */ +* unsafe version; only works if nbBits >= 1 */ MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, U32 nbBits) { size_t value = BIT_lookBitsFast(bitD, nbBits); @@ -510,7 +462,7 @@ MEM_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t* DStream) Error codes and messages Copyright (C) 2013-2015, Yann Collet - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -609,7 +561,7 @@ typedef unsigned FSE_DTable; /* don't allocate that. It's just a way to be mor header file for static linking (only) Copyright (C) 2013-2015, Yann Collet - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -753,7 +705,7 @@ MEM_STATIC unsigned FSE_endOfDState(const FSE_DState_t* DStatePtr) header file for static linking (only) Copyright (C) 2013-2015, Yann Collet - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -822,7 +774,7 @@ static size_t HUF_decompress4X6 (void* dst, size_t dstSize, const void* cSrc, si Header File Copyright (C) 2014-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -882,7 +834,7 @@ typedef struct ZSTD_CCtx_s ZSTD_CCtx; /* incomplete type */ Header File for static linking only Copyright (C) 2014-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -946,7 +898,7 @@ typedef struct ZSTD_DCtx_s ZSTD_DCtx; FSE : Finite State Entropy coder Copyright (C) 2013-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -1450,7 +1402,7 @@ static size_t FSE_decompress(void* dst, size_t maxDstSize, const void* cSrc, siz Huff0 : Huffman coder, part of New Generation Entropy library Copyright (C) 2013-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -2609,7 +2561,7 @@ static size_t HUF_decompress (void* dst, size_t dstSize, const void* cSrc, size_ zstd - standard compression library Copyright (C) 2014-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -3114,12 +3066,19 @@ static size_t ZSTD_execSequence(BYTE* op, const BYTE* const litEnd = *litPtr + sequence.litLength; /* checks */ - if (oLitEnd > oend_8) return ERROR(dstSize_tooSmall); /* last match must start at a minimum distance of 8 from oend */ + size_t const seqLength = sequence.litLength + sequence.matchLength; + + if (seqLength > (size_t)(oend - op)) return ERROR(dstSize_tooSmall); + if (sequence.litLength > (size_t)(litLimit - *litPtr)) return ERROR(corruption_detected); + /* Now we know there are no overflow in literal nor match lengths, can use the pointer check */ + if (oLitEnd > oend_8) return ERROR(dstSize_tooSmall); + if (sequence.offset > (U32)(oLitEnd - base)) return ERROR(corruption_detected); + if (oMatchEnd > oend) return ERROR(dstSize_tooSmall); /* overwrite beyond dst buffer */ if (litEnd > litLimit) return ERROR(corruption_detected); /* overRead beyond lit buffer */ /* copy Literals */ - ZSTD_wildcopy(op, *litPtr, sequence.litLength); /* note : oLitEnd <= oend-8 : no risk of overwrite beyond oend */ + ZSTD_wildcopy(op, *litPtr, (ptrdiff_t)sequence.litLength); /* note : oLitEnd <= oend-8 : no risk of overwrite beyond oend */ op = oLitEnd; *litPtr = litEnd; /* update for next sequence */ diff --git a/internal-complibs/zstd-1.5.2/legacy/zstd_v02.h b/internal-complibs/zstd-1.5.5/legacy/zstd_v02.h similarity index 98% rename from internal-complibs/zstd-1.5.2/legacy/zstd_v02.h rename to internal-complibs/zstd-1.5.5/legacy/zstd_v02.h index 1b371953..dab0260e 100644 --- a/internal-complibs/zstd-1.5.2/legacy/zstd_v02.h +++ b/internal-complibs/zstd-1.5.5/legacy/zstd_v02.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Yann Collet, Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the diff --git a/internal-complibs/zstd-1.5.2/legacy/zstd_v03.c b/internal-complibs/zstd-1.5.5/legacy/zstd_v03.c similarity index 96% rename from internal-complibs/zstd-1.5.2/legacy/zstd_v03.c rename to internal-complibs/zstd-1.5.5/legacy/zstd_v03.c index 6625f4df..b0d7f521 100644 --- a/internal-complibs/zstd-1.5.2/legacy/zstd_v03.c +++ b/internal-complibs/zstd-1.5.5/legacy/zstd_v03.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Yann Collet, Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -29,7 +29,7 @@ low-level memory access routines Copyright (C) 2013-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -116,24 +116,6 @@ extern "C" { /**************************************************************** * Memory I/O *****************************************************************/ -/* MEM_FORCE_MEMORY_ACCESS - * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. - * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. - * The below switch allow to select different access method for improved performance. - * Method 0 (default) : use `memcpy()`. Safe and portable. - * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). - * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. - * Method 2 : direct access. This method is portable but violate C standard. - * It can generate buggy code on targets generating assembly depending on alignment. - * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) - * See http://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details. - * Prefer these methods in priority order (0 > 1 > 2) - */ -#ifndef MEM_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ -# if defined(__INTEL_COMPILER) || defined(__GNUC__) || defined(__ICCARM__) -# define MEM_FORCE_MEMORY_ACCESS 1 -# endif -#endif MEM_STATIC unsigned MEM_32bits(void) { return sizeof(void*)==4; } MEM_STATIC unsigned MEM_64bits(void) { return sizeof(void*)==8; } @@ -144,33 +126,6 @@ MEM_STATIC unsigned MEM_isLittleEndian(void) return one.c[0]; } -#if defined(MEM_FORCE_MEMORY_ACCESS) && (MEM_FORCE_MEMORY_ACCESS==2) - -/* violates C standard on structure alignment. -Only use if no other choice to achieve best performance on target platform */ -MEM_STATIC U16 MEM_read16(const void* memPtr) { return *(const U16*) memPtr; } -MEM_STATIC U32 MEM_read32(const void* memPtr) { return *(const U32*) memPtr; } -MEM_STATIC U64 MEM_read64(const void* memPtr) { return *(const U64*) memPtr; } - -MEM_STATIC void MEM_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; } - -#elif defined(MEM_FORCE_MEMORY_ACCESS) && (MEM_FORCE_MEMORY_ACCESS==1) - -/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ -/* currently only defined for gcc and icc */ -typedef union { U16 u16; U32 u32; U64 u64; } __attribute__((packed)) unalign; - -MEM_STATIC U16 MEM_read16(const void* ptr) { return ((const unalign*)ptr)->u16; } -MEM_STATIC U32 MEM_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } -MEM_STATIC U64 MEM_read64(const void* ptr) { return ((const unalign*)ptr)->u64; } - -MEM_STATIC void MEM_write16(void* memPtr, U16 value) { ((unalign*)memPtr)->u16 = value; } - -#else - -/* default method, safe and standard. - can sometimes prove slower */ - MEM_STATIC U16 MEM_read16(const void* memPtr) { U16 val; memcpy(&val, memPtr, sizeof(val)); return val; @@ -191,10 +146,6 @@ MEM_STATIC void MEM_write16(void* memPtr, U16 value) memcpy(memPtr, &value, sizeof(value)); } - -#endif /* MEM_FORCE_MEMORY_ACCESS */ - - MEM_STATIC U16 MEM_readLE16(const void* memPtr) { if (MEM_isLittleEndian()) @@ -271,7 +222,7 @@ MEM_STATIC size_t MEM_readLEST(const void* memPtr) header file (to include) Copyright (C) 2013-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -435,7 +386,7 @@ MEM_STATIC size_t BIT_lookBits(BIT_DStream_t* bitD, U32 nbBits) } /*! BIT_lookBitsFast : -* unsafe version; only works only if nbBits >= 1 */ +* unsafe version; only works if nbBits >= 1 */ MEM_STATIC size_t BIT_lookBitsFast(BIT_DStream_t* bitD, U32 nbBits) { const U32 bitMask = sizeof(bitD->bitContainer)*8 - 1; @@ -455,7 +406,7 @@ MEM_STATIC size_t BIT_readBits(BIT_DStream_t* bitD, U32 nbBits) } /*!BIT_readBitsFast : -* unsafe version; only works only if nbBits >= 1 */ +* unsafe version; only works if nbBits >= 1 */ MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, U32 nbBits) { size_t value = BIT_lookBitsFast(bitD, nbBits); @@ -512,7 +463,7 @@ MEM_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t* DStream) Error codes and messages Copyright (C) 2013-2015, Yann Collet - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -611,7 +562,7 @@ typedef unsigned FSE_DTable; /* don't allocate that. It's just a way to be mor header file for static linking (only) Copyright (C) 2013-2015, Yann Collet - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -755,7 +706,7 @@ MEM_STATIC unsigned FSE_endOfDState(const FSE_DState_t* DStatePtr) header file for static linking (only) Copyright (C) 2013-2015, Yann Collet - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -823,7 +774,7 @@ static size_t HUF_decompress4X4 (void* dst, size_t dstSize, const void* cSrc, si Header File Copyright (C) 2014-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -883,7 +834,7 @@ typedef struct ZSTD_CCtx_s ZSTD_CCtx; /* incomplete type */ Header File for static linking only Copyright (C) 2014-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -947,7 +898,7 @@ typedef struct ZSTD_DCtx_s ZSTD_DCtx; FSE : Finite State Entropy coder Copyright (C) 2013-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -1451,7 +1402,7 @@ static size_t FSE_decompress(void* dst, size_t maxDstSize, const void* cSrc, siz Huff0 : Huffman coder, part of New Generation Entropy library Copyright (C) 2013-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -2248,7 +2199,7 @@ static size_t HUF_decompress (void* dst, size_t dstSize, const void* cSrc, size_ zstd - standard compression library Copyright (C) 2014-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -2755,18 +2706,24 @@ static size_t ZSTD_execSequence(BYTE* op, const BYTE* const litEnd = *litPtr + sequence.litLength; /* checks */ - if (oLitEnd > oend_8) return ERROR(dstSize_tooSmall); /* last match must start at a minimum distance of 8 from oend */ + size_t const seqLength = sequence.litLength + sequence.matchLength; + + if (seqLength > (size_t)(oend - op)) return ERROR(dstSize_tooSmall); + if (sequence.litLength > (size_t)(litLimit - *litPtr)) return ERROR(corruption_detected); + /* Now we know there are no overflow in literal nor match lengths, can use pointer checks */ + if (oLitEnd > oend_8) return ERROR(dstSize_tooSmall); + if (sequence.offset > (U32)(oLitEnd - base)) return ERROR(corruption_detected); + if (oMatchEnd > oend) return ERROR(dstSize_tooSmall); /* overwrite beyond dst buffer */ if (litEnd > litLimit) return ERROR(corruption_detected); /* overRead beyond lit buffer */ /* copy Literals */ - ZSTD_wildcopy(op, *litPtr, sequence.litLength); /* note : oLitEnd <= oend-8 : no risk of overwrite beyond oend */ + ZSTD_wildcopy(op, *litPtr, (ptrdiff_t)sequence.litLength); /* note : oLitEnd <= oend-8 : no risk of overwrite beyond oend */ op = oLitEnd; *litPtr = litEnd; /* update for next sequence */ /* copy Match */ - { - const BYTE* match = op - sequence.offset; + { const BYTE* match = op - sequence.offset; /* check */ if (sequence.offset > (size_t)op) return ERROR(corruption_detected); /* address space overflow test (this test seems kept by clang optimizer) */ diff --git a/internal-complibs/zstd-1.5.2/legacy/zstd_v03.h b/internal-complibs/zstd-1.5.5/legacy/zstd_v03.h similarity index 98% rename from internal-complibs/zstd-1.5.2/legacy/zstd_v03.h rename to internal-complibs/zstd-1.5.5/legacy/zstd_v03.h index 7a00d430..9bf3cce6 100644 --- a/internal-complibs/zstd-1.5.2/legacy/zstd_v03.h +++ b/internal-complibs/zstd-1.5.5/legacy/zstd_v03.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Yann Collet, Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the diff --git a/internal-complibs/zstd-1.5.2/legacy/zstd_v04.c b/internal-complibs/zstd-1.5.5/legacy/zstd_v04.c similarity index 97% rename from internal-complibs/zstd-1.5.2/legacy/zstd_v04.c rename to internal-complibs/zstd-1.5.5/legacy/zstd_v04.c index 8d305c7e..57be832b 100644 --- a/internal-complibs/zstd-1.5.2/legacy/zstd_v04.c +++ b/internal-complibs/zstd-1.5.5/legacy/zstd_v04.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Yann Collet, Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -87,24 +87,6 @@ extern "C" { /**************************************************************** * Memory I/O *****************************************************************/ -/* MEM_FORCE_MEMORY_ACCESS - * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. - * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. - * The below switch allow to select different access method for improved performance. - * Method 0 (default) : use `memcpy()`. Safe and portable. - * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). - * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. - * Method 2 : direct access. This method is portable but violate C standard. - * It can generate buggy code on targets generating assembly depending on alignment. - * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) - * See http://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details. - * Prefer these methods in priority order (0 > 1 > 2) - */ -#ifndef MEM_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ -# if defined(__INTEL_COMPILER) || defined(__GNUC__) || defined(__ICCARM__) -# define MEM_FORCE_MEMORY_ACCESS 1 -# endif -#endif MEM_STATIC unsigned MEM_32bits(void) { return sizeof(void*)==4; } MEM_STATIC unsigned MEM_64bits(void) { return sizeof(void*)==8; } @@ -115,33 +97,6 @@ MEM_STATIC unsigned MEM_isLittleEndian(void) return one.c[0]; } -#if defined(MEM_FORCE_MEMORY_ACCESS) && (MEM_FORCE_MEMORY_ACCESS==2) - -/* violates C standard on structure alignment. -Only use if no other choice to achieve best performance on target platform */ -MEM_STATIC U16 MEM_read16(const void* memPtr) { return *(const U16*) memPtr; } -MEM_STATIC U32 MEM_read32(const void* memPtr) { return *(const U32*) memPtr; } -MEM_STATIC U64 MEM_read64(const void* memPtr) { return *(const U64*) memPtr; } - -MEM_STATIC void MEM_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; } - -#elif defined(MEM_FORCE_MEMORY_ACCESS) && (MEM_FORCE_MEMORY_ACCESS==1) - -/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ -/* currently only defined for gcc and icc */ -typedef union { U16 u16; U32 u32; U64 u64; } __attribute__((packed)) unalign; - -MEM_STATIC U16 MEM_read16(const void* ptr) { return ((const unalign*)ptr)->u16; } -MEM_STATIC U32 MEM_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } -MEM_STATIC U64 MEM_read64(const void* ptr) { return ((const unalign*)ptr)->u64; } - -MEM_STATIC void MEM_write16(void* memPtr, U16 value) { ((unalign*)memPtr)->u16 = value; } - -#else - -/* default method, safe and standard. - can sometimes prove slower */ - MEM_STATIC U16 MEM_read16(const void* memPtr) { U16 val; memcpy(&val, memPtr, sizeof(val)); return val; @@ -162,9 +117,6 @@ MEM_STATIC void MEM_write16(void* memPtr, U16 value) memcpy(memPtr, &value, sizeof(value)); } -#endif /* MEM_FORCE_MEMORY_ACCESS */ - - MEM_STATIC U16 MEM_readLE16(const void* memPtr) { if (MEM_isLittleEndian()) @@ -542,7 +494,7 @@ If there is an error, the function will return an error code, which can be teste header file (to include) Copyright (C) 2013-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -700,7 +652,7 @@ MEM_STATIC size_t BIT_lookBits(BIT_DStream_t* bitD, U32 nbBits) } /*! BIT_lookBitsFast : -* unsafe version; only works only if nbBits >= 1 */ +* unsafe version; only works if nbBits >= 1 */ MEM_STATIC size_t BIT_lookBitsFast(BIT_DStream_t* bitD, U32 nbBits) { const U32 bitMask = sizeof(bitD->bitContainer)*8 - 1; @@ -720,7 +672,7 @@ MEM_STATIC size_t BIT_readBits(BIT_DStream_t* bitD, U32 nbBits) } /*!BIT_readBitsFast : -* unsafe version; only works only if nbBits >= 1 */ +* unsafe version; only works if nbBits >= 1 */ MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, U32 nbBits) { size_t value = BIT_lookBitsFast(bitD, nbBits); @@ -781,7 +733,7 @@ MEM_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t* DStream) header file for static linking (only) Copyright (C) 2013-2015, Yann Collet - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -930,7 +882,7 @@ MEM_STATIC unsigned FSE_endOfDState(const FSE_DState_t* DStatePtr) FSE : Finite State Entropy coder Copyright (C) 2013-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -1436,7 +1388,7 @@ static size_t FSE_decompress(void* dst, size_t maxDstSize, const void* cSrc, siz header file Copyright (C) 2013-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -1514,7 +1466,7 @@ static unsigned HUF_isError(size_t code); /* tells if a return value i header file for static linking (only) Copyright (C) 2013-2015, Yann Collet - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -1601,7 +1553,7 @@ static size_t HUF_decompress4X4_usingDTable(void* dst, size_t maxDstSize, const Huff0 : Huffman coder, part of New Generation Entropy library Copyright (C) 2013-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -2401,7 +2353,7 @@ static size_t HUF_decompress (void* dst, size_t dstSize, const void* cSrc, size_ zstd - decompression module fo v0.4 legacy format Copyright (C) 2015-2016, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -2876,13 +2828,19 @@ static size_t ZSTD_execSequence(BYTE* op, const BYTE* const litEnd = *litPtr + sequence.litLength; const BYTE* match = oLitEnd - sequence.offset; - /* check */ - if (oLitEnd > oend_8) return ERROR(dstSize_tooSmall); /* last match must start at a minimum distance of 8 from oend */ + /* checks */ + size_t const seqLength = sequence.litLength + sequence.matchLength; + + if (seqLength > (size_t)(oend - op)) return ERROR(dstSize_tooSmall); + if (sequence.litLength > (size_t)(litLimit - *litPtr)) return ERROR(corruption_detected); + /* Now we know there are no overflow in literal nor match lengths, can use pointer checks */ + if (oLitEnd > oend_8) return ERROR(dstSize_tooSmall); + if (oMatchEnd > oend) return ERROR(dstSize_tooSmall); /* overwrite beyond dst buffer */ - if (litEnd > litLimit) return ERROR(corruption_detected); /* risk read beyond lit buffer */ + if (litEnd > litLimit) return ERROR(corruption_detected); /* overRead beyond lit buffer */ /* copy Literals */ - ZSTD_wildcopy(op, *litPtr, sequence.litLength); /* note : oLitEnd <= oend-8 : no risk of overwrite beyond oend */ + ZSTD_wildcopy(op, *litPtr, (ptrdiff_t)sequence.litLength); /* note : oLitEnd <= oend-8 : no risk of overwrite beyond oend */ op = oLitEnd; *litPtr = litEnd; /* update for next sequence */ @@ -3283,7 +3241,7 @@ static void ZSTD_decompress_insertDictionary(ZSTD_DCtx* ctx, const void* dict, s Buffered version of Zstd compression library Copyright (C) 2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff --git a/internal-complibs/zstd-1.5.2/legacy/zstd_v04.h b/internal-complibs/zstd-1.5.5/legacy/zstd_v04.h similarity index 98% rename from internal-complibs/zstd-1.5.2/legacy/zstd_v04.h rename to internal-complibs/zstd-1.5.5/legacy/zstd_v04.h index 66b97ab8..640240d6 100644 --- a/internal-complibs/zstd-1.5.2/legacy/zstd_v04.h +++ b/internal-complibs/zstd-1.5.5/legacy/zstd_v04.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Yann Collet, Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the diff --git a/internal-complibs/zstd-1.5.2/legacy/zstd_v05.c b/internal-complibs/zstd-1.5.5/legacy/zstd_v05.c similarity index 97% rename from internal-complibs/zstd-1.5.2/legacy/zstd_v05.c rename to internal-complibs/zstd-1.5.5/legacy/zstd_v05.c index 795dfb41..93a1169f 100644 --- a/internal-complibs/zstd-1.5.2/legacy/zstd_v05.c +++ b/internal-complibs/zstd-1.5.5/legacy/zstd_v05.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Yann Collet, Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -19,7 +19,7 @@ low-level memory access routines Copyright (C) 2013-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -106,24 +106,6 @@ extern "C" { /*-************************************************************** * Memory I/O *****************************************************************/ -/* MEM_FORCE_MEMORY_ACCESS : - * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. - * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. - * The below switch allow to select different access method for improved performance. - * Method 0 (default) : use `memcpy()`. Safe and portable. - * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). - * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. - * Method 2 : direct access. This method is portable but violate C standard. - * It can generate buggy code on targets depending on alignment. - * In some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) - * See http://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details. - * Prefer these methods in priority order (0 > 1 > 2) - */ -#ifndef MEM_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ -# if defined(__INTEL_COMPILER) || defined(__GNUC__) || defined(__ICCARM__) -# define MEM_FORCE_MEMORY_ACCESS 1 -# endif -#endif MEM_STATIC unsigned MEM_32bits(void) { return sizeof(void*)==4; } MEM_STATIC unsigned MEM_64bits(void) { return sizeof(void*)==8; } @@ -134,37 +116,6 @@ MEM_STATIC unsigned MEM_isLittleEndian(void) return one.c[0]; } -#if defined(MEM_FORCE_MEMORY_ACCESS) && (MEM_FORCE_MEMORY_ACCESS==2) - -/* violates C standard, by lying on structure alignment. -Only use if no other choice to achieve best performance on target platform */ -MEM_STATIC U16 MEM_read16(const void* memPtr) { return *(const U16*) memPtr; } -MEM_STATIC U32 MEM_read32(const void* memPtr) { return *(const U32*) memPtr; } -MEM_STATIC U64 MEM_read64(const void* memPtr) { return *(const U64*) memPtr; } - -MEM_STATIC void MEM_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; } -MEM_STATIC void MEM_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; } -MEM_STATIC void MEM_write64(void* memPtr, U64 value) { *(U64*)memPtr = value; } - -#elif defined(MEM_FORCE_MEMORY_ACCESS) && (MEM_FORCE_MEMORY_ACCESS==1) - -/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ -/* currently only defined for gcc and icc */ -typedef union { U16 u16; U32 u32; U64 u64; size_t st; } __attribute__((packed)) unalign; - -MEM_STATIC U16 MEM_read16(const void* ptr) { return ((const unalign*)ptr)->u16; } -MEM_STATIC U32 MEM_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } -MEM_STATIC U64 MEM_read64(const void* ptr) { return ((const unalign*)ptr)->u64; } - -MEM_STATIC void MEM_write16(void* memPtr, U16 value) { ((unalign*)memPtr)->u16 = value; } -MEM_STATIC void MEM_write32(void* memPtr, U32 value) { ((unalign*)memPtr)->u32 = value; } -MEM_STATIC void MEM_write64(void* memPtr, U64 value) { ((unalign*)memPtr)->u64 = value; } - -#else - -/* default method, safe and standard. - can sometimes prove slower */ - MEM_STATIC U16 MEM_read16(const void* memPtr) { U16 val; memcpy(&val, memPtr, sizeof(val)); return val; @@ -195,9 +146,6 @@ MEM_STATIC void MEM_write64(void* memPtr, U64 value) memcpy(memPtr, &value, sizeof(value)); } -#endif /* MEM_FORCE_MEMORY_ACCESS */ - - MEM_STATIC U16 MEM_readLE16(const void* memPtr) { if (MEM_isLittleEndian()) @@ -262,7 +210,7 @@ MEM_STATIC size_t MEM_readLEST(const void* memPtr) Header File for static linking only Copyright (C) 2014-2016, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -286,7 +234,7 @@ MEM_STATIC size_t MEM_readLEST(const void* memPtr) OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - - zstd homepage : http://www.zstd.net + - zstd homepage : https://facebook.github.io/zstd */ #ifndef ZSTD_STATIC_H #define ZSTD_STATIC_H @@ -398,7 +346,7 @@ size_t ZSTDv05_decompressBlock(ZSTDv05_DCtx* dctx, void* dst, size_t dstCapacity Header File for include Copyright (C) 2014-2016, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -485,7 +433,7 @@ static const size_t ZSTDv05_frameHeaderSize_min = 5; #define FSEv05_ENCODING_DYNAMIC 3 -#define HufLog 12 +#define ZSTD_HUFFDTABLE_CAPACITY_LOG 12 #define MIN_SEQUENCES_SIZE 1 /* nbSeq==0 */ #define MIN_CBLOCK_SIZE (1 /*litCSize*/ + 1 /* RLE or RAW */ + MIN_SEQUENCES_SIZE /* nbSeq==0 */) /* for a non-null block */ @@ -553,7 +501,7 @@ typedef struct { header file Copyright (C) 2013-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -672,7 +620,7 @@ size_t FSEv05_decompress_usingDTable(void* dst, size_t dstCapacity, const void* header file (to include) Copyright (C) 2013-2016, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -826,7 +774,7 @@ MEM_STATIC size_t BITv05_lookBits(BITv05_DStream_t* bitD, U32 nbBits) } /*! BITv05_lookBitsFast : -* unsafe version; only works only if nbBits >= 1 */ +* unsafe version; only works if nbBits >= 1 */ MEM_STATIC size_t BITv05_lookBitsFast(BITv05_DStream_t* bitD, U32 nbBits) { const U32 bitMask = sizeof(bitD->bitContainer)*8 - 1; @@ -846,7 +794,7 @@ MEM_STATIC size_t BITv05_readBits(BITv05_DStream_t* bitD, unsigned nbBits) } /*!BITv05_readBitsFast : -* unsafe version; only works only if nbBits >= 1 */ +* unsafe version; only works if nbBits >= 1 */ MEM_STATIC size_t BITv05_readBitsFast(BITv05_DStream_t* bitD, unsigned nbBits) { size_t value = BITv05_lookBitsFast(bitD, nbBits); @@ -901,7 +849,7 @@ MEM_STATIC unsigned BITv05_endOfDStream(const BITv05_DStream_t* DStream) header file for static linking (only) Copyright (C) 2013-2015, Yann Collet - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -1051,7 +999,7 @@ MEM_STATIC unsigned FSEv05_endOfDState(const FSEv05_DState_t* DStatePtr) FSEv05 : Finite State Entropy coder Copyright (C) 2013-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -1537,7 +1485,7 @@ size_t FSEv05_decompress(void* dst, size_t maxDstSize, const void* cSrc, size_t header file Copyright (C) 2013-2016, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -1610,7 +1558,7 @@ const char* HUFv05_getErrorName(size_t code); /* provides error code string (u header file, for static linking only Copyright (C) 2013-2016, Yann Collet - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -1702,7 +1650,7 @@ size_t HUFv05_decompress1X4_usingDTable(void* dst, size_t maxDstSize, const void Huff0 : Huffman coder, part of New Generation Entropy library Copyright (C) 2013-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -2547,7 +2495,7 @@ size_t HUFv05_decompress (void* dst, size_t dstSize, const void* cSrc, size_t cS zstd - standard compression library Copyright (C) 2014-2016, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -2645,7 +2593,7 @@ struct ZSTDv05_DCtx_s FSEv05_DTable LLTable[FSEv05_DTABLE_SIZE_U32(LLFSEv05Log)]; FSEv05_DTable OffTable[FSEv05_DTABLE_SIZE_U32(OffFSEv05Log)]; FSEv05_DTable MLTable[FSEv05_DTABLE_SIZE_U32(MLFSEv05Log)]; - unsigned hufTableX4[HUFv05_DTABLE_SIZE(HufLog)]; + unsigned hufTableX4[HUFv05_DTABLE_SIZE(ZSTD_HUFFDTABLE_CAPACITY_LOG)]; const void* previousDstEnd; const void* base; const void* vBase; @@ -2673,7 +2621,7 @@ size_t ZSTDv05_decompressBegin(ZSTDv05_DCtx* dctx) dctx->base = NULL; dctx->vBase = NULL; dctx->dictEnd = NULL; - dctx->hufTableX4[0] = HufLog; + dctx->hufTableX4[0] = ZSTD_HUFFDTABLE_CAPACITY_LOG; dctx->flagStaticTables = 0; return 0; } @@ -3234,13 +3182,19 @@ static size_t ZSTDv05_execSequence(BYTE* op, const BYTE* const litEnd = *litPtr + sequence.litLength; const BYTE* match = oLitEnd - sequence.offset; - /* check */ - if (oLitEnd > oend_8) return ERROR(dstSize_tooSmall); /* last match must start at a minimum distance of 8 from oend */ + /* checks */ + size_t const seqLength = sequence.litLength + sequence.matchLength; + + if (seqLength > (size_t)(oend - op)) return ERROR(dstSize_tooSmall); + if (sequence.litLength > (size_t)(litLimit - *litPtr)) return ERROR(corruption_detected); + /* Now we know there are no overflow in literal nor match lengths, can use pointer checks */ + if (oLitEnd > oend_8) return ERROR(dstSize_tooSmall); + if (oMatchEnd > oend) return ERROR(dstSize_tooSmall); /* overwrite beyond dst buffer */ - if (litEnd > litLimit) return ERROR(corruption_detected); /* risk read beyond lit buffer */ + if (litEnd > litLimit) return ERROR(corruption_detected); /* overRead beyond lit buffer */ /* copy Literals */ - ZSTDv05_wildcopy(op, *litPtr, sequence.litLength); /* note : oLitEnd <= oend-8 : no risk of overwrite beyond oend */ + ZSTDv05_wildcopy(op, *litPtr, (ptrdiff_t)sequence.litLength); /* note : oLitEnd <= oend-8 : no risk of overwrite beyond oend */ op = oLitEnd; *litPtr = litEnd; /* update for next sequence */ @@ -3746,7 +3700,7 @@ size_t ZSTDv05_decompressBegin_usingDict(ZSTDv05_DCtx* dctx, const void* dict, s Buffered version of Zstd compression library Copyright (C) 2015-2016, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff --git a/internal-complibs/zstd-1.5.2/legacy/zstd_v05.h b/internal-complibs/zstd-1.5.5/legacy/zstd_v05.h similarity index 99% rename from internal-complibs/zstd-1.5.2/legacy/zstd_v05.h rename to internal-complibs/zstd-1.5.5/legacy/zstd_v05.h index bd423bfc..2dcffc92 100644 --- a/internal-complibs/zstd-1.5.2/legacy/zstd_v05.h +++ b/internal-complibs/zstd-1.5.5/legacy/zstd_v05.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Yann Collet, Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the diff --git a/internal-complibs/zstd-1.5.2/legacy/zstd_v06.c b/internal-complibs/zstd-1.5.5/legacy/zstd_v06.c similarity index 97% rename from internal-complibs/zstd-1.5.2/legacy/zstd_v06.c rename to internal-complibs/zstd-1.5.5/legacy/zstd_v06.c index ead213c4..175f7cc4 100644 --- a/internal-complibs/zstd-1.5.2/legacy/zstd_v06.c +++ b/internal-complibs/zstd-1.5.5/legacy/zstd_v06.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Yann Collet, Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -23,7 +23,7 @@ low-level memory access routines Copyright (C) 2013-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -108,24 +108,6 @@ extern "C" { /*-************************************************************** * Memory I/O *****************************************************************/ -/* MEM_FORCE_MEMORY_ACCESS : - * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. - * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. - * The below switch allow to select different access method for improved performance. - * Method 0 (default) : use `memcpy()`. Safe and portable. - * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). - * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. - * Method 2 : direct access. This method is portable but violate C standard. - * It can generate buggy code on targets depending on alignment. - * In some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) - * See http://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details. - * Prefer these methods in priority order (0 > 1 > 2) - */ -#ifndef MEM_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ -# if defined(__INTEL_COMPILER) || defined(__GNUC__) || defined(__ICCARM__) -# define MEM_FORCE_MEMORY_ACCESS 1 -# endif -#endif MEM_STATIC unsigned MEM_32bits(void) { return sizeof(size_t)==4; } MEM_STATIC unsigned MEM_64bits(void) { return sizeof(size_t)==8; } @@ -136,33 +118,6 @@ MEM_STATIC unsigned MEM_isLittleEndian(void) return one.c[0]; } -#if defined(MEM_FORCE_MEMORY_ACCESS) && (MEM_FORCE_MEMORY_ACCESS==2) - -/* violates C standard, by lying on structure alignment. -Only use if no other choice to achieve best performance on target platform */ -MEM_STATIC U16 MEM_read16(const void* memPtr) { return *(const U16*) memPtr; } -MEM_STATIC U32 MEM_read32(const void* memPtr) { return *(const U32*) memPtr; } -MEM_STATIC U64 MEM_read64(const void* memPtr) { return *(const U64*) memPtr; } - -MEM_STATIC void MEM_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; } - -#elif defined(MEM_FORCE_MEMORY_ACCESS) && (MEM_FORCE_MEMORY_ACCESS==1) - -/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ -/* currently only defined for gcc and icc */ -typedef union { U16 u16; U32 u32; U64 u64; size_t st; } __attribute__((packed)) unalign; - -MEM_STATIC U16 MEM_read16(const void* ptr) { return ((const unalign*)ptr)->u16; } -MEM_STATIC U32 MEM_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } -MEM_STATIC U64 MEM_read64(const void* ptr) { return ((const unalign*)ptr)->u64; } - -MEM_STATIC void MEM_write16(void* memPtr, U16 value) { ((unalign*)memPtr)->u16 = value; } - -#else - -/* default method, safe and standard. - can sometimes prove slower */ - MEM_STATIC U16 MEM_read16(const void* memPtr) { U16 val; memcpy(&val, memPtr, sizeof(val)); return val; @@ -183,9 +138,6 @@ MEM_STATIC void MEM_write16(void* memPtr, U16 value) memcpy(memPtr, &value, sizeof(value)); } - -#endif /* MEM_FORCE_MEMORY_ACCESS */ - MEM_STATIC U32 MEM_swap32(U32 in) { #if defined(_MSC_VER) /* Visual Studio */ @@ -281,7 +233,7 @@ MEM_STATIC size_t MEM_readLEST(const void* memPtr) Header File for static linking only Copyright (C) 2014-2016, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -305,7 +257,7 @@ MEM_STATIC size_t MEM_readLEST(const void* memPtr) OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - - zstd homepage : http://www.zstd.net + - zstd homepage : https://facebook.github.io/zstd */ #ifndef ZSTDv06_STATIC_H #define ZSTDv06_STATIC_H @@ -412,7 +364,7 @@ ZSTDLIBv06_API size_t ZSTDv06_decompressBlock(ZSTDv06_DCtx* dctx, void* dst, siz Header File for include Copyright (C) 2014-2016, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -479,7 +431,7 @@ typedef enum { bt_compressed, bt_raw, bt_rle, bt_end } blockType_t; #define MIN_SEQUENCES_SIZE 1 /* nbSeq==0 */ #define MIN_CBLOCK_SIZE (1 /*litCSize*/ + 1 /* RLE or RAW */ + MIN_SEQUENCES_SIZE /* nbSeq==0 */) /* for a non-null block */ -#define HufLog 12 +#define ZSTD_HUFFDTABLE_CAPACITY_LOG 12 #define IS_HUF 0 #define IS_PCH 1 @@ -619,7 +571,7 @@ void ZSTDv06_seqToCodes(const seqStore_t* seqStorePtr, size_t const nbSeq); Public Prototypes declaration Copyright (C) 2013-2016, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -767,7 +719,7 @@ If there is an error, the function will return an error code, which can be teste header file (to include) Copyright (C) 2013-2016, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -928,7 +880,7 @@ MEM_STATIC size_t BITv06_initDStream(BITv06_DStream_t* bitD, const void* srcBuff } /*! BITv06_lookBitsFast() : -* unsafe version; only works only if nbBits >= 1 */ +* unsafe version; only works if nbBits >= 1 */ MEM_STATIC size_t BITv06_lookBitsFast(const BITv06_DStream_t* bitD, U32 nbBits) { U32 const bitMask = sizeof(bitD->bitContainer)*8 - 1; @@ -948,7 +900,7 @@ MEM_STATIC size_t BITv06_readBits(BITv06_DStream_t* bitD, U32 nbBits) } /*! BITv06_readBitsFast() : -* unsafe version; only works only if nbBits >= 1 */ +* unsafe version; only works if nbBits >= 1 */ MEM_STATIC size_t BITv06_readBitsFast(BITv06_DStream_t* bitD, U32 nbBits) { size_t const value = BITv06_lookBitsFast(bitD, nbBits); @@ -1002,7 +954,7 @@ MEM_STATIC unsigned BITv06_endOfDStream(const BITv06_DStream_t* DStream) header file for static linking (only) Copyright (C) 2013-2015, Yann Collet - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -1210,7 +1162,7 @@ MEM_STATIC BYTE FSEv06_decodeSymbolFast(FSEv06_DState_t* DStatePtr, BITv06_DStre Common functions of New Generation Entropy library Copyright (C) 2016, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -1355,7 +1307,7 @@ size_t FSEv06_readNCount (short* normalizedCounter, unsigned* maxSVPtr, unsigned FSE : Finite State Entropy decoder Copyright (C) 2013-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -1679,7 +1631,7 @@ size_t FSEv06_decompress(void* dst, size_t maxDstSize, const void* cSrc, size_t header file Copyright (C) 2013-2016, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -1749,7 +1701,7 @@ size_t HUFv06_compressBound(size_t size); /**< maximum compressed size */ header file, for static linking only Copyright (C) 2013-2016, Yann Collet - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -1931,7 +1883,7 @@ MEM_STATIC size_t HUFv06_readStats(BYTE* huffWeight, size_t hwSize, U32* rankSta Huffman decoder, part of New Generation Entropy library Copyright (C) 2013-2016, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -2676,7 +2628,7 @@ size_t HUFv06_decompress (void* dst, size_t dstSize, const void* cSrc, size_t cS Common functions of Zstd compression library Copyright (C) 2015-2016, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -2700,7 +2652,7 @@ size_t HUFv06_decompress (void* dst, size_t dstSize, const void* cSrc, size_t cS OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - - zstd homepage : http://www.zstd.net/ + - zstd homepage : https://facebook.github.io/zstd/ */ @@ -2730,7 +2682,7 @@ const char* ZBUFFv06_getErrorName(size_t errorCode) { return ERR_getErrorName(er zstd - standard compression library Copyright (C) 2014-2016, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -2754,7 +2706,7 @@ const char* ZBUFFv06_getErrorName(size_t errorCode) { return ERR_getErrorName(er OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - - zstd homepage : http://www.zstd.net + - zstd homepage : https://facebook.github.io/zstd */ /* *************************************************************** @@ -2806,7 +2758,7 @@ struct ZSTDv06_DCtx_s FSEv06_DTable LLTable[FSEv06_DTABLE_SIZE_U32(LLFSELog)]; FSEv06_DTable OffTable[FSEv06_DTABLE_SIZE_U32(OffFSELog)]; FSEv06_DTable MLTable[FSEv06_DTABLE_SIZE_U32(MLFSELog)]; - unsigned hufTableX4[HUFv06_DTABLE_SIZE(HufLog)]; + unsigned hufTableX4[HUFv06_DTABLE_SIZE(ZSTD_HUFFDTABLE_CAPACITY_LOG)]; const void* previousDstEnd; const void* base; const void* vBase; @@ -2834,7 +2786,7 @@ size_t ZSTDv06_decompressBegin(ZSTDv06_DCtx* dctx) dctx->base = NULL; dctx->vBase = NULL; dctx->dictEnd = NULL; - dctx->hufTableX4[0] = HufLog; + dctx->hufTableX4[0] = ZSTD_HUFFDTABLE_CAPACITY_LOG; dctx->flagRepeatTable = 0; return 0; } @@ -3370,13 +3322,19 @@ static size_t ZSTDv06_execSequence(BYTE* op, const BYTE* const iLitEnd = *litPtr + sequence.litLength; const BYTE* match = oLitEnd - sequence.offset; - /* check */ - if (oLitEnd > oend_8) return ERROR(dstSize_tooSmall); /* last match must start at a minimum distance of 8 from oend */ + /* checks */ + size_t const seqLength = sequence.litLength + sequence.matchLength; + + if (seqLength > (size_t)(oend - op)) return ERROR(dstSize_tooSmall); + if (sequence.litLength > (size_t)(litLimit - *litPtr)) return ERROR(corruption_detected); + /* Now we know there are no overflow in literal nor match lengths, can use pointer checks */ + if (oLitEnd > oend_8) return ERROR(dstSize_tooSmall); + if (oMatchEnd > oend) return ERROR(dstSize_tooSmall); /* overwrite beyond dst buffer */ - if (iLitEnd > litLimit) return ERROR(corruption_detected); /* over-read beyond lit buffer */ + if (iLitEnd > litLimit) return ERROR(corruption_detected); /* overRead beyond lit buffer */ /* copy Literals */ - ZSTDv06_wildcopy(op, *litPtr, sequence.litLength); /* note : oLitEnd <= oend-8 : no risk of overwrite beyond oend */ + ZSTDv06_wildcopy(op, *litPtr, (ptrdiff_t)sequence.litLength); /* note : oLitEnd <= oend-8 : no risk of overwrite beyond oend */ op = oLitEnd; *litPtr = iLitEnd; /* update for next sequence */ @@ -3889,7 +3847,7 @@ size_t ZSTDv06_decompressBegin_usingDict(ZSTDv06_DCtx* dctx, const void* dict, s Buffered version of Zstd compression library Copyright (C) 2015-2016, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -3913,7 +3871,7 @@ size_t ZSTDv06_decompressBegin_usingDict(ZSTDv06_DCtx* dctx, const void* dict, s OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - - zstd homepage : http://www.zstd.net/ + - zstd homepage : https://facebook.github.io/zstd/ */ @@ -4035,7 +3993,8 @@ size_t ZBUFFv06_decompressContinue(ZBUFFv06_DCtx* zbd, size_t const toLoad = hSize - zbd->lhSize; /* if hSize!=0, hSize > zbd->lhSize */ if (ZSTDv06_isError(hSize)) return hSize; if (toLoad > (size_t)(iend-ip)) { /* not enough input to load full header */ - memcpy(zbd->headerBuffer + zbd->lhSize, ip, iend-ip); + if (ip != NULL) + memcpy(zbd->headerBuffer + zbd->lhSize, ip, iend-ip); zbd->lhSize += iend-ip; *dstCapacityPtr = 0; return (hSize - zbd->lhSize) + ZSTDv06_blockHeaderSize; /* remaining header bytes + next block header */ diff --git a/internal-complibs/zstd-1.5.2/legacy/zstd_v06.h b/internal-complibs/zstd-1.5.5/legacy/zstd_v06.h similarity index 99% rename from internal-complibs/zstd-1.5.2/legacy/zstd_v06.h rename to internal-complibs/zstd-1.5.5/legacy/zstd_v06.h index 9e32b76e..63389101 100644 --- a/internal-complibs/zstd-1.5.2/legacy/zstd_v06.h +++ b/internal-complibs/zstd-1.5.5/legacy/zstd_v06.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Yann Collet, Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the diff --git a/internal-complibs/zstd-1.5.2/legacy/zstd_v07.c b/internal-complibs/zstd-1.5.5/legacy/zstd_v07.c similarity index 97% rename from internal-complibs/zstd-1.5.2/legacy/zstd_v07.c rename to internal-complibs/zstd-1.5.5/legacy/zstd_v07.c index 3a0418e5..15dc3ef7 100644 --- a/internal-complibs/zstd-1.5.2/legacy/zstd_v07.c +++ b/internal-complibs/zstd-1.5.5/legacy/zstd_v07.c @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Yann Collet, Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -184,7 +184,7 @@ ZSTDLIBv07_API size_t ZSTDv07_insertBlock(ZSTDv07_DCtx* dctx, const void* blockS low-level memory access routines Copyright (C) 2013-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -268,24 +268,6 @@ extern "C" { /*-************************************************************** * Memory I/O *****************************************************************/ -/* MEM_FORCE_MEMORY_ACCESS : - * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. - * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. - * The below switch allow to select different access method for improved performance. - * Method 0 (default) : use `memcpy()`. Safe and portable. - * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). - * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. - * Method 2 : direct access. This method is portable but violate C standard. - * It can generate buggy code on targets depending on alignment. - * In some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) - * See http://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details. - * Prefer these methods in priority order (0 > 1 > 2) - */ -#ifndef MEM_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ -# if defined(__INTEL_COMPILER) || defined(__GNUC__) || defined(__ICCARM__) -# define MEM_FORCE_MEMORY_ACCESS 1 -# endif -#endif MEM_STATIC unsigned MEM_32bits(void) { return sizeof(size_t)==4; } MEM_STATIC unsigned MEM_64bits(void) { return sizeof(size_t)==8; } @@ -296,33 +278,6 @@ MEM_STATIC unsigned MEM_isLittleEndian(void) return one.c[0]; } -#if defined(MEM_FORCE_MEMORY_ACCESS) && (MEM_FORCE_MEMORY_ACCESS==2) - -/* violates C standard, by lying on structure alignment. -Only use if no other choice to achieve best performance on target platform */ -MEM_STATIC U16 MEM_read16(const void* memPtr) { return *(const U16*) memPtr; } -MEM_STATIC U32 MEM_read32(const void* memPtr) { return *(const U32*) memPtr; } -MEM_STATIC U64 MEM_read64(const void* memPtr) { return *(const U64*) memPtr; } - -MEM_STATIC void MEM_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; } - -#elif defined(MEM_FORCE_MEMORY_ACCESS) && (MEM_FORCE_MEMORY_ACCESS==1) - -/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ -/* currently only defined for gcc and icc */ -typedef union { U16 u16; U32 u32; U64 u64; size_t st; } __attribute__((packed)) unalign; - -MEM_STATIC U16 MEM_read16(const void* ptr) { return ((const unalign*)ptr)->u16; } -MEM_STATIC U32 MEM_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } -MEM_STATIC U64 MEM_read64(const void* ptr) { return ((const unalign*)ptr)->u64; } - -MEM_STATIC void MEM_write16(void* memPtr, U16 value) { ((unalign*)memPtr)->u16 = value; } - -#else - -/* default method, safe and standard. - can sometimes prove slower */ - MEM_STATIC U16 MEM_read16(const void* memPtr) { U16 val; memcpy(&val, memPtr, sizeof(val)); return val; @@ -343,8 +298,6 @@ MEM_STATIC void MEM_write16(void* memPtr, U16 value) memcpy(memPtr, &value, sizeof(value)); } -#endif /* MEM_FORCE_MEMORY_ACCESS */ - MEM_STATIC U32 MEM_swap32(U32 in) { #if defined(_MSC_VER) /* Visual Studio */ @@ -439,7 +392,7 @@ MEM_STATIC size_t MEM_readLEST(const void* memPtr) header file (to include) Copyright (C) 2013-2016, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -596,7 +549,7 @@ MEM_STATIC size_t BITv07_initDStream(BITv07_DStream_t* bitD, const void* srcBuff } /*! BITv07_lookBitsFast() : -* unsafe version; only works only if nbBits >= 1 */ +* unsafe version; only works if nbBits >= 1 */ MEM_STATIC size_t BITv07_lookBitsFast(const BITv07_DStream_t* bitD, U32 nbBits) { U32 const bitMask = sizeof(bitD->bitContainer)*8 - 1; @@ -616,7 +569,7 @@ MEM_STATIC size_t BITv07_readBits(BITv07_DStream_t* bitD, U32 nbBits) } /*! BITv07_readBitsFast() : -* unsafe version; only works only if nbBits >= 1 */ +* unsafe version; only works if nbBits >= 1 */ MEM_STATIC size_t BITv07_readBitsFast(BITv07_DStream_t* bitD, U32 nbBits) { size_t const value = BITv07_lookBitsFast(bitD, nbBits); @@ -670,7 +623,7 @@ MEM_STATIC unsigned BITv07_endOfDStream(const BITv07_DStream_t* DStream) Public Prototypes declaration Copyright (C) 2013-2016, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -978,7 +931,7 @@ MEM_STATIC BYTE FSEv07_decodeSymbolFast(FSEv07_DState_t* DStatePtr, BITv07_DStre header file Copyright (C) 2013-2016, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -1151,7 +1104,7 @@ size_t HUFv07_decompress1X4_usingDTable(void* dst, size_t maxDstSize, const void Common functions of New Generation Entropy library Copyright (C) 2016, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -1375,7 +1328,7 @@ size_t HUFv07_readStats(BYTE* huffWeight, size_t hwSize, U32* rankStats, FSE : Finite State Entropy decoder Copyright (C) 2013-2015, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -1699,7 +1652,7 @@ size_t FSEv07_decompress(void* dst, size_t maxDstSize, const void* cSrc, size_t Huffman decoder, part of New Generation Entropy library Copyright (C) 2013-2016, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -2577,7 +2530,7 @@ size_t HUFv07_decompress1X_DCtx (HUFv07_DTable* dctx, void* dst, size_t dstSize, Common functions of Zstd compression library Copyright (C) 2015-2016, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -2601,7 +2554,7 @@ size_t HUFv07_decompress1X_DCtx (HUFv07_DTable* dctx, void* dst, size_t dstSize, OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - - zstd homepage : http://www.zstd.net/ + - zstd homepage : https://facebook.github.io/zstd/ */ @@ -2647,7 +2600,7 @@ static void ZSTDv07_defaultFreeFunction(void* opaque, void* address) Header File for include Copyright (C) 2014-2016, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -2717,7 +2670,7 @@ typedef enum { bt_compressed, bt_raw, bt_rle, bt_end } blockType_t; #define MIN_SEQUENCES_SIZE 1 /* nbSeq==0 */ #define MIN_CBLOCK_SIZE (1 /*litCSize*/ + 1 /* RLE or RAW */ + MIN_SEQUENCES_SIZE /* nbSeq==0 */) /* for a non-null block */ -#define HufLog 12 +#define ZSTD_HUFFDTABLE_CAPACITY_LOG 12 typedef enum { lbt_huffman, lbt_repeat, lbt_raw, lbt_rle } litBlockType_t; #define LONGNBSEQ 0x7F00 @@ -2854,7 +2807,7 @@ static const ZSTDv07_customMem defaultCustomMem = { ZSTDv07_defaultAllocFunction zstd - standard compression library Copyright (C) 2014-2016, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -2878,7 +2831,7 @@ static const ZSTDv07_customMem defaultCustomMem = { ZSTDv07_defaultAllocFunction OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - - zstd homepage : http://www.zstd.net + - zstd homepage : https://facebook.github.io/zstd */ /* *************************************************************** @@ -2931,7 +2884,7 @@ struct ZSTDv07_DCtx_s FSEv07_DTable LLTable[FSEv07_DTABLE_SIZE_U32(LLFSELog)]; FSEv07_DTable OffTable[FSEv07_DTABLE_SIZE_U32(OffFSELog)]; FSEv07_DTable MLTable[FSEv07_DTABLE_SIZE_U32(MLFSELog)]; - HUFv07_DTable hufTable[HUFv07_DTABLE_SIZE(HufLog)]; /* can accommodate HUFv07_decompress4X */ + HUFv07_DTable hufTable[HUFv07_DTABLE_SIZE(ZSTD_HUFFDTABLE_CAPACITY_LOG)]; /* can accommodate HUFv07_decompress4X */ const void* previousDstEnd; const void* base; const void* vBase; @@ -2967,7 +2920,7 @@ size_t ZSTDv07_decompressBegin(ZSTDv07_DCtx* dctx) dctx->base = NULL; dctx->vBase = NULL; dctx->dictEnd = NULL; - dctx->hufTable[0] = (HUFv07_DTable)((HufLog)*0x1000001); + dctx->hufTable[0] = (HUFv07_DTable)((ZSTD_HUFFDTABLE_CAPACITY_LOG)*0x1000001); dctx->litEntropy = dctx->fseEntropy = 0; dctx->dictID = 0; { int i; for (i=0; irep[i] = repStartValue[i]; } @@ -3599,11 +3552,14 @@ size_t ZSTDv07_execSequence(BYTE* op, const BYTE* match = oLitEnd - sequence.offset; /* check */ - if ((oLitEnd>oend_w) | (oMatchEnd>oend)) return ERROR(dstSize_tooSmall); /* last match must start at a minimum distance of WILDCOPY_OVERLENGTH from oend */ - if (iLitEnd > litLimit) return ERROR(corruption_detected); /* over-read beyond lit buffer */ + assert(oend >= op); + if (sequence.litLength + WILDCOPY_OVERLENGTH > (size_t)(oend - op)) return ERROR(dstSize_tooSmall); + if (sequenceLength > (size_t)(oend - op)) return ERROR(dstSize_tooSmall); + assert(litLimit >= *litPtr); + if (sequence.litLength > (size_t)(litLimit - *litPtr)) return ERROR(corruption_detected);; /* copy Literals */ - ZSTDv07_wildcopy(op, *litPtr, sequence.litLength); /* note : since oLitEnd <= oend-WILDCOPY_OVERLENGTH, no risk of overwrite beyond oend */ + ZSTDv07_wildcopy(op, *litPtr, (ptrdiff_t)sequence.litLength); /* note : since oLitEnd <= oend-WILDCOPY_OVERLENGTH, no risk of overwrite beyond oend */ op = oLitEnd; *litPtr = iLitEnd; /* update for next sequence */ @@ -3617,7 +3573,7 @@ size_t ZSTDv07_execSequence(BYTE* op, return sequenceLength; } /* span extDict & currentPrefixSegment */ - { size_t const length1 = dictEnd - match; + { size_t const length1 = (size_t)(dictEnd - match); memmove(oLitEnd, match, length1); op = oLitEnd + length1; sequence.matchLength -= length1; @@ -4253,7 +4209,7 @@ ZSTDLIBv07_API size_t ZSTDv07_decompress_usingDDict(ZSTDv07_DCtx* dctx, Buffered version of Zstd compression library Copyright (C) 2015-2016, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -4277,7 +4233,7 @@ ZSTDLIBv07_API size_t ZSTDv07_decompress_usingDDict(ZSTDv07_DCtx* dctx, OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - - zstd homepage : http://www.zstd.net/ + - zstd homepage : https://facebook.github.io/zstd/ */ @@ -4417,7 +4373,8 @@ size_t ZBUFFv07_decompressContinue(ZBUFFv07_DCtx* zbd, if (hSize != 0) { size_t const toLoad = hSize - zbd->lhSize; /* if hSize!=0, hSize > zbd->lhSize */ if (toLoad > (size_t)(iend-ip)) { /* not enough input to load full header */ - memcpy(zbd->headerBuffer + zbd->lhSize, ip, iend-ip); + if (ip != NULL) + memcpy(zbd->headerBuffer + zbd->lhSize, ip, iend-ip); zbd->lhSize += iend-ip; *dstCapacityPtr = 0; return (hSize - zbd->lhSize) + ZSTDv07_blockHeaderSize; /* remaining header bytes + next block header */ diff --git a/internal-complibs/zstd-1.5.2/legacy/zstd_v07.h b/internal-complibs/zstd-1.5.5/legacy/zstd_v07.h similarity index 99% rename from internal-complibs/zstd-1.5.2/legacy/zstd_v07.h rename to internal-complibs/zstd-1.5.5/legacy/zstd_v07.h index bc35cfa6..1ff39041 100644 --- a/internal-complibs/zstd-1.5.2/legacy/zstd_v07.h +++ b/internal-complibs/zstd-1.5.5/legacy/zstd_v07.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Yann Collet, Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the diff --git a/internal-complibs/zstd-1.5.2/libzstd.mk b/internal-complibs/zstd-1.5.5/libzstd.mk similarity index 90% rename from internal-complibs/zstd-1.5.2/libzstd.mk rename to internal-complibs/zstd-1.5.5/libzstd.mk index 6e9a6439..5e11d5d2 100644 --- a/internal-complibs/zstd-1.5.2/libzstd.mk +++ b/internal-complibs/zstd-1.5.5/libzstd.mk @@ -1,5 +1,5 @@ # ################################################################ -# Copyright (c) Yann Collet, Facebook, Inc. +# Copyright (c) Meta Platforms, Inc. and affiliates. # All rights reserved. # # This source code is licensed under both the BSD-style license (found in the @@ -15,17 +15,34 @@ # Zstd lib directory LIBZSTD ?= ./ +# ZSTD_LIB_MINIFY is a helper variable that +# configures a bunch of other variables to space-optimized defaults. +ZSTD_LIB_MINIFY ?= 0 + # Legacy support -ZSTD_LEGACY_SUPPORT ?= 5 +ifneq ($(ZSTD_LIB_MINIFY), 0) + ZSTD_LEGACY_SUPPORT ?= 0 +else + ZSTD_LEGACY_SUPPORT ?= 5 +endif ZSTD_LEGACY_MULTITHREADED_API ?= 0 # Build size optimizations -HUF_FORCE_DECOMPRESS_X1 ?= 0 -HUF_FORCE_DECOMPRESS_X2 ?= 0 -ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT ?= 0 -ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG ?= 0 -ZSTD_NO_INLINE ?= 0 -ZSTD_STRIP_ERROR_STRINGS ?= 0 +ifneq ($(ZSTD_LIB_MINIFY), 0) + HUF_FORCE_DECOMPRESS_X1 ?= 1 + HUF_FORCE_DECOMPRESS_X2 ?= 0 + ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT ?= 1 + ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG ?= 0 + ZSTD_NO_INLINE ?= 1 + ZSTD_STRIP_ERROR_STRINGS ?= 1 +else + HUF_FORCE_DECOMPRESS_X1 ?= 0 + HUF_FORCE_DECOMPRESS_X2 ?= 0 + ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT ?= 0 + ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG ?= 0 + ZSTD_NO_INLINE ?= 0 + ZSTD_STRIP_ERROR_STRINGS ?= 0 +endif # Assembly support ZSTD_NO_ASM ?= 0 @@ -61,17 +78,8 @@ LIBVER := $(shell echo $(LIBVER_SCRIPT)) CCVER := $(shell $(CC) --version) ZSTD_VERSION?= $(LIBVER) -# ZSTD_LIB_MINIFY is a helper variable that -# configures a bunch of other variables to space-optimized defaults. -ZSTD_LIB_MINIFY ?= 0 ifneq ($(ZSTD_LIB_MINIFY), 0) HAVE_CC_OZ ?= $(shell echo "" | $(CC) -Oz -x c -c - -o /dev/null 2> /dev/null && echo 1 || echo 0) - ZSTD_LEGACY_SUPPORT ?= 0 - ZSTD_LIB_DEPRECATED ?= 0 - HUF_FORCE_DECOMPRESS_X1 ?= 1 - ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT ?= 1 - ZSTD_NO_INLINE ?= 1 - ZSTD_STRIP_ERROR_STRINGS ?= 1 ifneq ($(HAVE_CC_OZ), 0) # Some compilers (clang) support an even more space-optimized setting. CFLAGS += -Oz @@ -114,13 +122,16 @@ CFLAGS += -Qunused-arguments -Wa,--noexecstack endif endif +ifeq ($(shell echo "int main(int argc, char* argv[]) { (void)argc; (void)argv; return 0; }" | $(CC) $(FLAGS) -z cet-report=error -x c -Werror - -o $(VOID) 2>$(VOID) && echo 1 || echo 0),1) +LDFLAGS += -z cet-report=error +endif + HAVE_COLORNEVER = $(shell echo a | grep --color=never a > /dev/null 2> /dev/null && echo 1 || echo 0) GREP_OPTIONS ?= -ifeq ($HAVE_COLORNEVER, 1) +ifeq ($(HAVE_COLORNEVER), 1) GREP_OPTIONS += --color=never endif GREP = grep $(GREP_OPTIONS) -SED_ERE_OPT ?= -E ZSTD_COMMON_FILES := $(sort $(wildcard $(LIBZSTD)/common/*.c)) ZSTD_COMPRESS_FILES := $(sort $(wildcard $(LIBZSTD)/compress/*.c)) diff --git a/internal-complibs/zstd-1.5.2/libzstd.pc.in b/internal-complibs/zstd-1.5.5/libzstd.pc.in similarity index 63% rename from internal-complibs/zstd-1.5.2/libzstd.pc.in rename to internal-complibs/zstd-1.5.5/libzstd.pc.in index 43ebaec3..d5cc0270 100644 --- a/internal-complibs/zstd-1.5.2/libzstd.pc.in +++ b/internal-complibs/zstd-1.5.5/libzstd.pc.in @@ -1,6 +1,6 @@ # ZSTD - standard compression algorithm -# Copyright (C) 2014-2016, Yann Collet, Facebook -# BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) +# Copyright (c) Meta Platforms, Inc. and affiliates. +# BSD 2-Clause License (https://opensource.org/licenses/bsd-license.php) prefix=@PREFIX@ exec_prefix=@EXEC_PREFIX@ @@ -9,7 +9,7 @@ libdir=@LIBDIR@ Name: zstd Description: fast lossless compression algorithm library -URL: http://www.zstd.net/ +URL: https://facebook.github.io/zstd/ Version: @VERSION@ Libs: -L${libdir} -lzstd Libs.private: @LIBS_PRIVATE@ diff --git a/internal-complibs/zstd-1.5.2/module.modulemap b/internal-complibs/zstd-1.5.5/module.modulemap similarity index 56% rename from internal-complibs/zstd-1.5.2/module.modulemap rename to internal-complibs/zstd-1.5.5/module.modulemap index bbb93978..eff98dfa 100644 --- a/internal-complibs/zstd-1.5.2/module.modulemap +++ b/internal-complibs/zstd-1.5.5/module.modulemap @@ -1,17 +1,27 @@ module libzstd [extern_c] { header "zstd.h" export * - config_macros [exhaustive] /* zstd.h */ \ + config_macros [exhaustive] \ + /* zstd.h */ \ ZSTD_STATIC_LINKING_ONLY, \ + ZSTDLIB_VISIBILITY, \ ZSTDLIB_VISIBLE, \ + ZSTDLIB_HIDDEN, \ ZSTD_DLL_EXPORT, \ ZSTDLIB_STATIC_API, \ ZSTD_DISABLE_DEPRECATE_WARNINGS, \ ZSTD_CLEVEL_DEFAULT, \ - /* zdict.h */ ZDICT_STATIC_LINKING_ONLY, \ + /* zdict.h */ \ + ZDICT_STATIC_LINKING_ONLY, \ + ZDICTLIB_VISIBLE, \ + ZDICTLIB_HIDDEN, \ ZDICTLIB_VISIBILITY, \ + ZDICTLIB_STATIC_API, \ ZDICT_DISABLE_DEPRECATE_WARNINGS, \ - /* zstd_errors.h */ ZSTDERRORLIB_VISIBILITY + /* zstd_errors.h */ \ + ZSTDERRORLIB_VISIBLE, \ + ZSTDERRORLIB_HIDDEN, \ + ZSTDERRORLIB_VISIBILITY module dictbuilder [extern_c] { header "zdict.h" diff --git a/internal-complibs/zstd-1.5.2/zdict.h b/internal-complibs/zstd-1.5.5/zdict.h similarity index 89% rename from internal-complibs/zstd-1.5.2/zdict.h rename to internal-complibs/zstd-1.5.5/zdict.h index f1e139a4..2268f948 100644 --- a/internal-complibs/zstd-1.5.2/zdict.h +++ b/internal-complibs/zstd-1.5.5/zdict.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -8,32 +8,43 @@ * You may select, at your option, one of the above-listed licenses. */ -#ifndef DICTBUILDER_H_001 -#define DICTBUILDER_H_001 - #if defined (__cplusplus) extern "C" { #endif +#ifndef ZSTD_ZDICT_H +#define ZSTD_ZDICT_H /*====== Dependencies ======*/ #include /* size_t */ /* ===== ZDICTLIB_API : control library symbols visibility ===== */ -#ifndef ZDICTLIB_VISIBILITY -# if defined(__GNUC__) && (__GNUC__ >= 4) -# define ZDICTLIB_VISIBILITY __attribute__ ((visibility ("default"))) +#ifndef ZDICTLIB_VISIBLE + /* Backwards compatibility with old macro name */ +# ifdef ZDICTLIB_VISIBILITY +# define ZDICTLIB_VISIBLE ZDICTLIB_VISIBILITY +# elif defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__) +# define ZDICTLIB_VISIBLE __attribute__ ((visibility ("default"))) +# else +# define ZDICTLIB_VISIBLE +# endif +#endif + +#ifndef ZDICTLIB_HIDDEN +# if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__) +# define ZDICTLIB_HIDDEN __attribute__ ((visibility ("hidden"))) # else -# define ZDICTLIB_VISIBILITY +# define ZDICTLIB_HIDDEN # endif #endif + #if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1) -# define ZDICTLIB_API __declspec(dllexport) ZDICTLIB_VISIBILITY +# define ZDICTLIB_API __declspec(dllexport) ZDICTLIB_VISIBLE #elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1) -# define ZDICTLIB_API __declspec(dllimport) ZDICTLIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ +# define ZDICTLIB_API __declspec(dllimport) ZDICTLIB_VISIBLE /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ #else -# define ZDICTLIB_API ZDICTLIB_VISIBILITY +# define ZDICTLIB_API ZDICTLIB_VISIBLE #endif /******************************************************************************* @@ -110,8 +121,8 @@ extern "C" { * The zstd CLI defaults to a 110KB dictionary. You likely don't need a * dictionary larger than that. But, most use cases can get away with a * smaller dictionary. The advanced dictionary builders can automatically - * shrink the dictionary for you, and select a the smallest size that - * doesn't hurt compression ratio too much. See the `shrinkDict` parameter. + * shrink the dictionary for you, and select the smallest size that doesn't + * hurt compression ratio too much. See the `shrinkDict` parameter. * A smaller dictionary can save memory, and potentially speed up * compression. * @@ -201,9 +212,9 @@ ZDICTLIB_API size_t ZDICT_trainFromBuffer(void* dictBuffer, size_t dictBufferCap const size_t* samplesSizes, unsigned nbSamples); typedef struct { - int compressionLevel; /*< optimize for a specific zstd compression level; 0 means default */ - unsigned notificationLevel; /*< Write log to stderr; 0 = none (default); 1 = errors; 2 = progression; 3 = details; 4 = debug; */ - unsigned dictID; /*< force dictID value; 0 means auto mode (32-bits random value) + int compressionLevel; /**< optimize for a specific zstd compression level; 0 means default */ + unsigned notificationLevel; /**< Write log to stderr; 0 = none (default); 1 = errors; 2 = progression; 3 = details; 4 = debug; */ + unsigned dictID; /**< force dictID value; 0 means auto mode (32-bits random value) * NOTE: The zstd format reserves some dictionary IDs for future use. * You may use them in private settings, but be warned that they * may be used by zstd in a public dictionary registry in the future. @@ -260,9 +271,21 @@ ZDICTLIB_API size_t ZDICT_getDictHeaderSize(const void* dictBuffer, size_t dictS ZDICTLIB_API unsigned ZDICT_isError(size_t errorCode); ZDICTLIB_API const char* ZDICT_getErrorName(size_t errorCode); +#endif /* ZSTD_ZDICT_H */ +#if defined(ZDICT_STATIC_LINKING_ONLY) && !defined(ZSTD_ZDICT_H_STATIC) +#define ZSTD_ZDICT_H_STATIC -#ifdef ZDICT_STATIC_LINKING_ONLY +/* This can be overridden externally to hide static symbols. */ +#ifndef ZDICTLIB_STATIC_API +# if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1) +# define ZDICTLIB_STATIC_API __declspec(dllexport) ZDICTLIB_VISIBLE +# elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1) +# define ZDICTLIB_STATIC_API __declspec(dllimport) ZDICTLIB_VISIBLE +# else +# define ZDICTLIB_STATIC_API ZDICTLIB_VISIBLE +# endif +#endif /* ==================================================================================== * The definitions in this section are considered experimental. @@ -318,7 +341,7 @@ typedef struct { * In general, it's recommended to provide a few thousands samples, though this can vary a lot. * It's recommended that total size of all samples be about ~x100 times the target size of dictionary. */ -ZDICTLIB_API size_t ZDICT_trainFromBuffer_cover( +ZDICTLIB_STATIC_API size_t ZDICT_trainFromBuffer_cover( void *dictBuffer, size_t dictBufferCapacity, const void *samplesBuffer, const size_t *samplesSizes, unsigned nbSamples, ZDICT_cover_params_t parameters); @@ -340,7 +363,7 @@ ZDICTLIB_API size_t ZDICT_trainFromBuffer_cover( * See ZDICT_trainFromBuffer() for details on failure modes. * Note: ZDICT_optimizeTrainFromBuffer_cover() requires about 8 bytes of memory for each input byte and additionally another 5 bytes of memory for each byte of memory for each thread. */ -ZDICTLIB_API size_t ZDICT_optimizeTrainFromBuffer_cover( +ZDICTLIB_STATIC_API size_t ZDICT_optimizeTrainFromBuffer_cover( void* dictBuffer, size_t dictBufferCapacity, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, ZDICT_cover_params_t* parameters); @@ -361,7 +384,7 @@ ZDICTLIB_API size_t ZDICT_optimizeTrainFromBuffer_cover( * In general, it's recommended to provide a few thousands samples, though this can vary a lot. * It's recommended that total size of all samples be about ~x100 times the target size of dictionary. */ -ZDICTLIB_API size_t ZDICT_trainFromBuffer_fastCover(void *dictBuffer, +ZDICTLIB_STATIC_API size_t ZDICT_trainFromBuffer_fastCover(void *dictBuffer, size_t dictBufferCapacity, const void *samplesBuffer, const size_t *samplesSizes, unsigned nbSamples, ZDICT_fastCover_params_t parameters); @@ -384,7 +407,7 @@ ZDICTLIB_API size_t ZDICT_trainFromBuffer_fastCover(void *dictBuffer, * See ZDICT_trainFromBuffer() for details on failure modes. * Note: ZDICT_optimizeTrainFromBuffer_fastCover() requires about 6 * 2^f bytes of memory for each thread. */ -ZDICTLIB_API size_t ZDICT_optimizeTrainFromBuffer_fastCover(void* dictBuffer, +ZDICTLIB_STATIC_API size_t ZDICT_optimizeTrainFromBuffer_fastCover(void* dictBuffer, size_t dictBufferCapacity, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, ZDICT_fastCover_params_t* parameters); @@ -409,7 +432,7 @@ typedef struct { * It's recommended that total size of all samples be about ~x100 times the target size of dictionary. * Note: ZDICT_trainFromBuffer_legacy() will send notifications into stderr if instructed to, using notificationLevel>0. */ -ZDICTLIB_API size_t ZDICT_trainFromBuffer_legacy( +ZDICTLIB_STATIC_API size_t ZDICT_trainFromBuffer_legacy( void* dictBuffer, size_t dictBufferCapacity, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, ZDICT_legacy_params_t parameters); @@ -421,32 +444,31 @@ ZDICTLIB_API size_t ZDICT_trainFromBuffer_legacy( or _CRT_SECURE_NO_WARNINGS in Visual. Otherwise, it's also possible to manually define ZDICT_DISABLE_DEPRECATE_WARNINGS */ #ifdef ZDICT_DISABLE_DEPRECATE_WARNINGS -# define ZDICT_DEPRECATED(message) ZDICTLIB_API /* disable deprecation warnings */ +# define ZDICT_DEPRECATED(message) /* disable deprecation warnings */ #else # define ZDICT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) # if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */ -# define ZDICT_DEPRECATED(message) [[deprecated(message)]] ZDICTLIB_API +# define ZDICT_DEPRECATED(message) [[deprecated(message)]] # elif defined(__clang__) || (ZDICT_GCC_VERSION >= 405) -# define ZDICT_DEPRECATED(message) ZDICTLIB_API __attribute__((deprecated(message))) +# define ZDICT_DEPRECATED(message) __attribute__((deprecated(message))) # elif (ZDICT_GCC_VERSION >= 301) -# define ZDICT_DEPRECATED(message) ZDICTLIB_API __attribute__((deprecated)) +# define ZDICT_DEPRECATED(message) __attribute__((deprecated)) # elif defined(_MSC_VER) -# define ZDICT_DEPRECATED(message) ZDICTLIB_API __declspec(deprecated(message)) +# define ZDICT_DEPRECATED(message) __declspec(deprecated(message)) # else # pragma message("WARNING: You need to implement ZDICT_DEPRECATED for this compiler") -# define ZDICT_DEPRECATED(message) ZDICTLIB_API +# define ZDICT_DEPRECATED(message) # endif #endif /* ZDICT_DISABLE_DEPRECATE_WARNINGS */ ZDICT_DEPRECATED("use ZDICT_finalizeDictionary() instead") +ZDICTLIB_STATIC_API size_t ZDICT_addEntropyTablesFromBuffer(void* dictBuffer, size_t dictContentSize, size_t dictBufferCapacity, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples); -#endif /* ZDICT_STATIC_LINKING_ONLY */ +#endif /* ZSTD_ZDICT_H_STATIC */ #if defined (__cplusplus) } #endif - -#endif /* DICTBUILDER_H_001 */ diff --git a/internal-complibs/zstd-1.5.2/zstd.h b/internal-complibs/zstd-1.5.5/zstd.h similarity index 82% rename from internal-complibs/zstd-1.5.2/zstd.h rename to internal-complibs/zstd-1.5.5/zstd.h index a88ae7bf..e5c3f8b6 100644 --- a/internal-complibs/zstd-1.5.2/zstd.h +++ b/internal-complibs/zstd-1.5.5/zstd.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -14,21 +14,31 @@ extern "C" { #ifndef ZSTD_H_235446 #define ZSTD_H_235446 -/* ====== Dependency ======*/ +/* ====== Dependencies ======*/ #include /* INT_MAX */ #include /* size_t */ /* ===== ZSTDLIB_API : control library symbols visibility ===== */ #ifndef ZSTDLIB_VISIBLE -# if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__) + /* Backwards compatibility with old macro name */ +# ifdef ZSTDLIB_VISIBILITY +# define ZSTDLIB_VISIBLE ZSTDLIB_VISIBILITY +# elif defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__) # define ZSTDLIB_VISIBLE __attribute__ ((visibility ("default"))) -# define ZSTDLIB_HIDDEN __attribute__ ((visibility ("hidden"))) # else # define ZSTDLIB_VISIBLE +# endif +#endif + +#ifndef ZSTDLIB_HIDDEN +# if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__) +# define ZSTDLIB_HIDDEN __attribute__ ((visibility ("hidden"))) +# else # define ZSTDLIB_HIDDEN # endif #endif + #if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1) # define ZSTDLIB_API __declspec(dllexport) ZSTDLIB_VISIBLE #elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1) @@ -37,6 +47,28 @@ extern "C" { # define ZSTDLIB_API ZSTDLIB_VISIBLE #endif +/* Deprecation warnings : + * Should these warnings be a problem, it is generally possible to disable them, + * typically with -Wno-deprecated-declarations for gcc or _CRT_SECURE_NO_WARNINGS in Visual. + * Otherwise, it's also possible to define ZSTD_DISABLE_DEPRECATE_WARNINGS. + */ +#ifdef ZSTD_DISABLE_DEPRECATE_WARNINGS +# define ZSTD_DEPRECATED(message) /* disable deprecation warnings */ +#else +# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */ +# define ZSTD_DEPRECATED(message) [[deprecated(message)]] +# elif (defined(GNUC) && (GNUC > 4 || (GNUC == 4 && GNUC_MINOR >= 5))) || defined(__clang__) +# define ZSTD_DEPRECATED(message) __attribute__((deprecated(message))) +# elif defined(__GNUC__) && (__GNUC__ >= 3) +# define ZSTD_DEPRECATED(message) __attribute__((deprecated)) +# elif defined(_MSC_VER) +# define ZSTD_DEPRECATED(message) __declspec(deprecated(message)) +# else +# pragma message("WARNING: You need to implement ZSTD_DEPRECATED for this compiler") +# define ZSTD_DEPRECATED(message) +# endif +#endif /* ZSTD_DISABLE_DEPRECATE_WARNINGS */ + /******************************************************************************* Introduction @@ -74,7 +106,7 @@ extern "C" { /*------ Version ------*/ #define ZSTD_VERSION_MAJOR 1 #define ZSTD_VERSION_MINOR 5 -#define ZSTD_VERSION_RELEASE 2 +#define ZSTD_VERSION_RELEASE 5 #define ZSTD_VERSION_NUMBER (ZSTD_VERSION_MAJOR *100*100 + ZSTD_VERSION_MINOR *100 + ZSTD_VERSION_RELEASE) /*! ZSTD_versionNumber() : @@ -116,7 +148,8 @@ ZSTDLIB_API const char* ZSTD_versionString(void); ***************************************/ /*! ZSTD_compress() : * Compresses `src` content as a single zstd compressed frame into already allocated `dst`. - * Hint : compression runs faster if `dstCapacity` >= `ZSTD_compressBound(srcSize)`. + * NOTE: Providing `dstCapacity >= ZSTD_compressBound(srcSize)` guarantees that zstd will have + * enough space to successfully compress the data. * @return : compressed size written into `dst` (<= `dstCapacity), * or an error code if it fails (which can be tested using ZSTD_isError()). */ ZSTDLIB_API size_t ZSTD_compress( void* dst, size_t dstCapacity, @@ -165,7 +198,9 @@ ZSTDLIB_API unsigned long long ZSTD_getFrameContentSize(const void *src, size_t * "empty", "unknown" and "error" results to the same return value (0), * while ZSTD_getFrameContentSize() gives them separate return values. * @return : decompressed size of `src` frame content _if known and not empty_, 0 otherwise. */ -ZSTDLIB_API unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize); +ZSTD_DEPRECATED("Replaced by ZSTD_getFrameContentSize") +ZSTDLIB_API +unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize); /*! ZSTD_findFrameCompressedSize() : Requires v1.4.0+ * `src` should point to the start of a ZSTD frame or skippable frame. @@ -177,8 +212,30 @@ ZSTDLIB_API size_t ZSTD_findFrameCompressedSize(const void* src, size_t srcSize) /*====== Helper functions ======*/ -#define ZSTD_COMPRESSBOUND(srcSize) ((srcSize) + ((srcSize)>>8) + (((srcSize) < (128<<10)) ? (((128<<10) - (srcSize)) >> 11) /* margin, from 64 to 0 */ : 0)) /* this formula ensures that bound(A) + bound(B) <= bound(A+B) as long as A and B >= 128 KB */ -ZSTDLIB_API size_t ZSTD_compressBound(size_t srcSize); /*!< maximum compressed size in worst case single-pass scenario */ +/* ZSTD_compressBound() : + * maximum compressed size in worst case single-pass scenario. + * When invoking `ZSTD_compress()` or any other one-pass compression function, + * it's recommended to provide @dstCapacity >= ZSTD_compressBound(srcSize) + * as it eliminates one potential failure scenario, + * aka not enough room in dst buffer to write the compressed frame. + * Note : ZSTD_compressBound() itself can fail, if @srcSize > ZSTD_MAX_INPUT_SIZE . + * In which case, ZSTD_compressBound() will return an error code + * which can be tested using ZSTD_isError(). + * + * ZSTD_COMPRESSBOUND() : + * same as ZSTD_compressBound(), but as a macro. + * It can be used to produce constants, which can be useful for static allocation, + * for example to size a static array on stack. + * Will produce constant value 0 if srcSize too large. + */ +#define ZSTD_MAX_INPUT_SIZE ((sizeof(size_t)==8) ? 0xFF00FF00FF00FF00LLU : 0xFF00FF00U) +#define ZSTD_COMPRESSBOUND(srcSize) (((size_t)(srcSize) >= ZSTD_MAX_INPUT_SIZE) ? 0 : (srcSize) + ((srcSize)>>8) + (((srcSize) < (128<<10)) ? (((128<<10) - (srcSize)) >> 11) /* margin, from 64 to 0 */ : 0)) /* this formula ensures that bound(A) + bound(B) <= bound(A+B) as long as A and B >= 128 KB */ +ZSTDLIB_API size_t ZSTD_compressBound(size_t srcSize); /*!< maximum compressed size in worst case single-pass scenario */ +/* ZSTD_isError() : + * Most ZSTD_* functions returning a size_t value can be tested for error, + * using ZSTD_isError(). + * @return 1 if error, 0 otherwise + */ ZSTDLIB_API unsigned ZSTD_isError(size_t code); /*!< tells if a `size_t` function result is an error code */ ZSTDLIB_API const char* ZSTD_getErrorName(size_t code); /*!< provides readable string from an error code */ ZSTDLIB_API int ZSTD_minCLevel(void); /*!< minimum negative compression level allowed, requires v1.4.0+ */ @@ -421,6 +478,9 @@ typedef enum { * ZSTD_c_validateSequences * ZSTD_c_useBlockSplitter * ZSTD_c_useRowMatchFinder + * ZSTD_c_prefetchCDictTables + * ZSTD_c_enableSeqProducerFallback + * ZSTD_c_maxBlockSize * Because they are not stable, it's necessary to define ZSTD_STATIC_LINKING_ONLY to access them. * note : never ever use experimentalParam? names directly; * also, the enums values themselves are unstable and can still change. @@ -439,7 +499,11 @@ typedef enum { ZSTD_c_experimentalParam12=1009, ZSTD_c_experimentalParam13=1010, ZSTD_c_experimentalParam14=1011, - ZSTD_c_experimentalParam15=1012 + ZSTD_c_experimentalParam15=1012, + ZSTD_c_experimentalParam16=1013, + ZSTD_c_experimentalParam17=1014, + ZSTD_c_experimentalParam18=1015, + ZSTD_c_experimentalParam19=1016 } ZSTD_cParameter; typedef struct { @@ -502,7 +566,7 @@ typedef enum { * They will be used to compress next frame. * Resetting session never fails. * - The parameters : changes all parameters back to "default". - * This removes any reference to any dictionary too. + * This also removes any reference to any dictionary or external sequence producer. * Parameters can only be changed between 2 sessions (i.e. no compression is currently ongoing) * otherwise the reset fails, and function returns an error value (which can be tested using ZSTD_isError()) * - Both : similar to resetting the session, followed by resetting parameters. @@ -515,7 +579,8 @@ ZSTDLIB_API size_t ZSTD_CCtx_reset(ZSTD_CCtx* cctx, ZSTD_ResetDirective reset); * Should cctx hold data from a previously unfinished frame, everything about it is forgotten. * - Compression parameters are pushed into CCtx before starting compression, using ZSTD_CCtx_set*() * - The function is always blocking, returns when compression is completed. - * Hint : compression runs faster if `dstCapacity` >= `ZSTD_compressBound(srcSize)`. + * NOTE: Providing `dstCapacity >= ZSTD_compressBound(srcSize)` guarantees that zstd will have + * enough space to successfully compress the data, though it is possible it fails for other reasons. * @return : compressed size written into `dst` (<= `dstCapacity), * or an error code if it fails (which can be tested using ZSTD_isError()). */ @@ -552,13 +617,15 @@ typedef enum { * ZSTD_d_stableOutBuffer * ZSTD_d_forceIgnoreChecksum * ZSTD_d_refMultipleDDicts + * ZSTD_d_disableHuffmanAssembly * Because they are not stable, it's necessary to define ZSTD_STATIC_LINKING_ONLY to access them. * note : never ever use experimentalParam? names directly */ ZSTD_d_experimentalParam1=1000, ZSTD_d_experimentalParam2=1001, ZSTD_d_experimentalParam3=1002, - ZSTD_d_experimentalParam4=1003 + ZSTD_d_experimentalParam4=1003, + ZSTD_d_experimentalParam5=1004 } ZSTD_dParameter; @@ -737,8 +804,6 @@ ZSTDLIB_API size_t ZSTD_CStreamOutSize(void); /**< recommended size for output * This following is a legacy streaming API, available since v1.0+ . * It can be replaced by ZSTD_CCtx_reset() and ZSTD_compressStream2(). * It is redundant, but remains fully supported. - * Streaming in combination with advanced parameters and dictionary compression - * can only be used through the new API. ******************************************************************************/ /*! @@ -747,6 +812,9 @@ ZSTDLIB_API size_t ZSTD_CStreamOutSize(void); /**< recommended size for output * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only); * ZSTD_CCtx_refCDict(zcs, NULL); // clear the dictionary (if any) * ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel); + * + * Note that ZSTD_initCStream() clears any previously set dictionary. Use the new API + * to compress with a dictionary. */ ZSTDLIB_API size_t ZSTD_initCStream(ZSTD_CStream* zcs, int compressionLevel); /*! @@ -797,13 +865,31 @@ ZSTDLIB_API size_t ZSTD_freeDStream(ZSTD_DStream* zds); /* accept NULL pointer /*===== Streaming decompression functions =====*/ -/* This function is redundant with the advanced API and equivalent to: +/*! ZSTD_initDStream() : + * Initialize/reset DStream state for new decompression operation. + * Call before new decompression operation using same DStream. * + * Note : This function is redundant with the advanced API and equivalent to: * ZSTD_DCtx_reset(zds, ZSTD_reset_session_only); * ZSTD_DCtx_refDDict(zds, NULL); */ ZSTDLIB_API size_t ZSTD_initDStream(ZSTD_DStream* zds); +/*! ZSTD_decompressStream() : + * Streaming decompression function. + * Call repetitively to consume full input updating it as necessary. + * Function will update both input and output `pos` fields exposing current state via these fields: + * - `input.pos < input.size`, some input remaining and caller should provide remaining input + * on the next call. + * - `output.pos < output.size`, decoder finished and flushed all remaining buffers. + * - `output.pos == output.size`, potentially uncflushed data present in the internal buffers, + * call ZSTD_decompressStream() again to flush remaining data to output. + * Note : with no additional input, amount of data flushed <= ZSTD_BLOCKSIZE_MAX. + * + * @return : 0 when a frame is completely decoded and fully flushed, + * or an error code, which can be tested using ZSTD_isError(), + * or any other value > 0, which means there is some decoding or flushing to do to complete current frame. + */ ZSTDLIB_API size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inBuffer* input); ZSTDLIB_API size_t ZSTD_DStreamInSize(void); /*!< recommended size for input buffer */ @@ -922,7 +1008,7 @@ ZSTDLIB_API unsigned ZSTD_getDictID_fromDDict(const ZSTD_DDict* ddict); * If @return == 0, the dictID could not be decoded. * This could for one of the following reasons : * - The frame does not require a dictionary to be decoded (most common case). - * - The frame was built with dictID intentionally removed. Whatever dictionary is necessary is a hidden information. + * - The frame was built with dictID intentionally removed. Whatever dictionary is necessary is a hidden piece of information. * Note : this use case also happens when using a non-conformant dictionary. * - `srcSize` is too small, and as a result, the frame header could not be decoded (only possible if `srcSize < ZSTD_FRAMEHEADERSIZE_MAX`). * - This is not a Zstandard frame. @@ -934,9 +1020,11 @@ ZSTDLIB_API unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize); * Advanced dictionary and prefix API (Requires v1.4.0+) * * This API allows dictionaries to be used with ZSTD_compress2(), - * ZSTD_compressStream2(), and ZSTD_decompressDCtx(). Dictionaries are sticky, and - * only reset with the context is reset with ZSTD_reset_parameters or - * ZSTD_reset_session_and_parameters. Prefixes are single-use. + * ZSTD_compressStream2(), and ZSTD_decompressDCtx(). + * Dictionaries are sticky, they remain valid when same context is re-used, + * they only reset when the context is reset + * with ZSTD_reset_parameters or ZSTD_reset_session_and_parameters. + * In contrast, Prefixes are single-use. ******************************************************************************/ @@ -946,8 +1034,9 @@ ZSTDLIB_API unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize); * @result : 0, or an error code (which can be tested with ZSTD_isError()). * Special: Loading a NULL (or 0-size) dictionary invalidates previous dictionary, * meaning "return to no-dictionary mode". - * Note 1 : Dictionary is sticky, it will be used for all future compressed frames. - * To return to "no-dictionary" situation, load a NULL dictionary (or reset parameters). + * Note 1 : Dictionary is sticky, it will be used for all future compressed frames, + * until parameters are reset, a new dictionary is loaded, or the dictionary + * is explicitly invalidated by loading a NULL dictionary. * Note 2 : Loading a dictionary involves building tables. * It's also a CPU consuming operation, with non-negligible impact on latency. * Tables are dependent on compression parameters, and for this reason, @@ -956,11 +1045,15 @@ ZSTDLIB_API unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize); * Use experimental ZSTD_CCtx_loadDictionary_byReference() to reference content instead. * In such a case, dictionary buffer must outlive its users. * Note 4 : Use ZSTD_CCtx_loadDictionary_advanced() - * to precisely select how dictionary content must be interpreted. */ + * to precisely select how dictionary content must be interpreted. + * Note 5 : This method does not benefit from LDM (long distance mode). + * If you want to employ LDM on some large dictionary content, + * prefer employing ZSTD_CCtx_refPrefix() described below. + */ ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize); /*! ZSTD_CCtx_refCDict() : Requires v1.4.0+ - * Reference a prepared dictionary, to be used for all next compressed frames. + * Reference a prepared dictionary, to be used for all future compressed frames. * Note that compression parameters are enforced from within CDict, * and supersede any compression parameter previously set within CCtx. * The parameters ignored are labelled as "superseded-by-cdict" in the ZSTD_cParameter enum docs. @@ -979,6 +1072,7 @@ ZSTDLIB_API size_t ZSTD_CCtx_refCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); * Decompression will need same prefix to properly regenerate data. * Compressing with a prefix is similar in outcome as performing a diff and compressing it, * but performs much faster, especially during decompression (compression speed is tunable with compression level). + * This method is compatible with LDM (long distance mode). * @result : 0, or an error code (which can be tested with ZSTD_isError()). * Special: Adding any prefix (including NULL) invalidates any previous prefix or dictionary * Note 1 : Prefix buffer is referenced. It **must** outlive compression. @@ -995,9 +1089,9 @@ ZSTDLIB_API size_t ZSTD_CCtx_refPrefix(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize); /*! ZSTD_DCtx_loadDictionary() : Requires v1.4.0+ - * Create an internal DDict from dict buffer, - * to be used to decompress next frames. - * The dictionary remains valid for all future frames, until explicitly invalidated. + * Create an internal DDict from dict buffer, to be used to decompress all future frames. + * The dictionary remains valid for all future frames, until explicitly invalidated, or + * a new dictionary is loaded. * @result : 0, or an error code (which can be tested with ZSTD_isError()). * Special : Adding a NULL (or 0-size) dictionary invalidates any previous dictionary, * meaning "return to no-dictionary mode". @@ -1021,9 +1115,10 @@ ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary(ZSTD_DCtx* dctx, const void* dict, s * The memory for the table is allocated on the first call to refDDict, and can be * freed with ZSTD_freeDCtx(). * + * If called with ZSTD_d_refMultipleDDicts disabled (the default), only one dictionary + * will be managed, and referencing a dictionary effectively "discards" any previous one. + * * @result : 0, or an error code (which can be tested with ZSTD_isError()). - * Note 1 : Currently, only one dictionary can be managed. - * Referencing a new dictionary effectively "discards" any previous one. * Special: referencing a NULL DDict means "return to no-dictionary mode". * Note 2 : DDict is just referenced, its lifetime must outlive its usage from DCtx. */ @@ -1086,28 +1181,6 @@ ZSTDLIB_API size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict); # endif #endif -/* Deprecation warnings : - * Should these warnings be a problem, it is generally possible to disable them, - * typically with -Wno-deprecated-declarations for gcc or _CRT_SECURE_NO_WARNINGS in Visual. - * Otherwise, it's also possible to define ZSTD_DISABLE_DEPRECATE_WARNINGS. - */ -#ifdef ZSTD_DISABLE_DEPRECATE_WARNINGS -# define ZSTD_DEPRECATED(message) ZSTDLIB_STATIC_API /* disable deprecation warnings */ -#else -# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */ -# define ZSTD_DEPRECATED(message) [[deprecated(message)]] ZSTDLIB_STATIC_API -# elif (defined(GNUC) && (GNUC > 4 || (GNUC == 4 && GNUC_MINOR >= 5))) || defined(__clang__) -# define ZSTD_DEPRECATED(message) ZSTDLIB_STATIC_API __attribute__((deprecated(message))) -# elif defined(__GNUC__) && (__GNUC__ >= 3) -# define ZSTD_DEPRECATED(message) ZSTDLIB_STATIC_API __attribute__((deprecated)) -# elif defined(_MSC_VER) -# define ZSTD_DEPRECATED(message) ZSTDLIB_STATIC_API __declspec(deprecated(message)) -# else -# pragma message("WARNING: You need to implement ZSTD_DEPRECATED for this compiler") -# define ZSTD_DEPRECATED(message) ZSTDLIB_STATIC_API -# endif -#endif /* ZSTD_DISABLE_DEPRECATE_WARNINGS */ - /**************************************************************************************** * experimental API (static linking only) **************************************************************************************** @@ -1142,6 +1215,7 @@ ZSTDLIB_API size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict); #define ZSTD_TARGETLENGTH_MIN 0 /* note : comparing this constant to an unsigned results in a tautological test */ #define ZSTD_STRATEGY_MIN ZSTD_fast #define ZSTD_STRATEGY_MAX ZSTD_btultra2 +#define ZSTD_BLOCKSIZE_MAX_MIN (1 << 10) /* The minimum valid max blocksize. Maximum blocksizes smaller than this make compressBound() inaccurate. */ #define ZSTD_OVERLAPLOG_MIN 0 @@ -1322,7 +1396,7 @@ typedef enum { } ZSTD_paramSwitch_e; /*************************************** -* Frame size functions +* Frame header and size functions ***************************************/ /*! ZSTD_findDecompressedSize() : @@ -1369,33 +1443,113 @@ ZSTDLIB_STATIC_API unsigned long long ZSTD_decompressBound(const void* src, size * or an error code (if srcSize is too small) */ ZSTDLIB_STATIC_API size_t ZSTD_frameHeaderSize(const void* src, size_t srcSize); +typedef enum { ZSTD_frame, ZSTD_skippableFrame } ZSTD_frameType_e; +typedef struct { + unsigned long long frameContentSize; /* if == ZSTD_CONTENTSIZE_UNKNOWN, it means this field is not available. 0 means "empty" */ + unsigned long long windowSize; /* can be very large, up to <= frameContentSize */ + unsigned blockSizeMax; + ZSTD_frameType_e frameType; /* if == ZSTD_skippableFrame, frameContentSize is the size of skippable content */ + unsigned headerSize; + unsigned dictID; + unsigned checksumFlag; + unsigned _reserved1; + unsigned _reserved2; +} ZSTD_frameHeader; + +/*! ZSTD_getFrameHeader() : + * decode Frame Header, or requires larger `srcSize`. + * @return : 0, `zfhPtr` is correctly filled, + * >0, `srcSize` is too small, value is wanted `srcSize` amount, + * or an error code, which can be tested using ZSTD_isError() */ +ZSTDLIB_STATIC_API size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize); /**< doesn't consume input */ +/*! ZSTD_getFrameHeader_advanced() : + * same as ZSTD_getFrameHeader(), + * with added capability to select a format (like ZSTD_f_zstd1_magicless) */ +ZSTDLIB_STATIC_API size_t ZSTD_getFrameHeader_advanced(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize, ZSTD_format_e format); + +/*! ZSTD_decompressionMargin() : + * Zstd supports in-place decompression, where the input and output buffers overlap. + * In this case, the output buffer must be at least (Margin + Output_Size) bytes large, + * and the input buffer must be at the end of the output buffer. + * + * _______________________ Output Buffer ________________________ + * | | + * | ____ Input Buffer ____| + * | | | + * v v v + * |---------------------------------------|-----------|----------| + * ^ ^ ^ + * |___________________ Output_Size ___________________|_ Margin _| + * + * NOTE: See also ZSTD_DECOMPRESSION_MARGIN(). + * NOTE: This applies only to single-pass decompression through ZSTD_decompress() or + * ZSTD_decompressDCtx(). + * NOTE: This function supports multi-frame input. + * + * @param src The compressed frame(s) + * @param srcSize The size of the compressed frame(s) + * @returns The decompression margin or an error that can be checked with ZSTD_isError(). + */ +ZSTDLIB_STATIC_API size_t ZSTD_decompressionMargin(const void* src, size_t srcSize); + +/*! ZSTD_DECOMPRESS_MARGIN() : + * Similar to ZSTD_decompressionMargin(), but instead of computing the margin from + * the compressed frame, compute it from the original size and the blockSizeLog. + * See ZSTD_decompressionMargin() for details. + * + * WARNING: This macro does not support multi-frame input, the input must be a single + * zstd frame. If you need that support use the function, or implement it yourself. + * + * @param originalSize The original uncompressed size of the data. + * @param blockSize The block size == MIN(windowSize, ZSTD_BLOCKSIZE_MAX). + * Unless you explicitly set the windowLog smaller than + * ZSTD_BLOCKSIZELOG_MAX you can just use ZSTD_BLOCKSIZE_MAX. + */ +#define ZSTD_DECOMPRESSION_MARGIN(originalSize, blockSize) ((size_t)( \ + ZSTD_FRAMEHEADERSIZE_MAX /* Frame header */ + \ + 4 /* checksum */ + \ + ((originalSize) == 0 ? 0 : 3 * (((originalSize) + (blockSize) - 1) / blockSize)) /* 3 bytes per block */ + \ + (blockSize) /* One block of margin */ \ + )) + typedef enum { ZSTD_sf_noBlockDelimiters = 0, /* Representation of ZSTD_Sequence has no block delimiters, sequences only */ ZSTD_sf_explicitBlockDelimiters = 1 /* Representation of ZSTD_Sequence contains explicit block delimiters */ } ZSTD_sequenceFormat_e; +/*! ZSTD_sequenceBound() : + * `srcSize` : size of the input buffer + * @return : upper-bound for the number of sequences that can be generated + * from a buffer of srcSize bytes + * + * note : returns number of sequences - to get bytes, multiply by sizeof(ZSTD_Sequence). + */ +ZSTDLIB_STATIC_API size_t ZSTD_sequenceBound(size_t srcSize); + /*! ZSTD_generateSequences() : - * Generate sequences using ZSTD_compress2, given a source buffer. + * Generate sequences using ZSTD_compress2(), given a source buffer. * * Each block will end with a dummy sequence * with offset == 0, matchLength == 0, and litLength == length of last literals. * litLength may be == 0, and if so, then the sequence of (of: 0 ml: 0 ll: 0) * simply acts as a block delimiter. * - * zc can be used to insert custom compression params. - * This function invokes ZSTD_compress2 + * @zc can be used to insert custom compression params. + * This function invokes ZSTD_compress2(). * * The output of this function can be fed into ZSTD_compressSequences() with CCtx * setting of ZSTD_c_blockDelimiters as ZSTD_sf_explicitBlockDelimiters * @return : number of sequences generated */ -ZSTDLIB_STATIC_API size_t ZSTD_generateSequences(ZSTD_CCtx* zc, ZSTD_Sequence* outSeqs, - size_t outSeqsSize, const void* src, size_t srcSize); +ZSTDLIB_STATIC_API size_t +ZSTD_generateSequences( ZSTD_CCtx* zc, + ZSTD_Sequence* outSeqs, size_t outSeqsSize, + const void* src, size_t srcSize); /*! ZSTD_mergeBlockDelimiters() : * Given an array of ZSTD_Sequence, remove all sequences that represent block delimiters/last literals - * by merging them into into the literals of the next sequence. + * by merging them into the literals of the next sequence. * * As such, the final generated result has no explicit representation of block boundaries, * and the final last literals segment is not represented in the sequences. @@ -1407,7 +1561,9 @@ ZSTDLIB_STATIC_API size_t ZSTD_generateSequences(ZSTD_CCtx* zc, ZSTD_Sequence* o ZSTDLIB_STATIC_API size_t ZSTD_mergeBlockDelimiters(ZSTD_Sequence* sequences, size_t seqsSize); /*! ZSTD_compressSequences() : - * Compress an array of ZSTD_Sequence, generated from the original source buffer, into dst. + * Compress an array of ZSTD_Sequence, associated with @src buffer, into dst. + * @src contains the entire input (not just the literals). + * If @srcSize > sum(sequence.length), the remaining bytes are considered all literals * If a dictionary is included, then the cctx should reference the dict. (see: ZSTD_CCtx_refCDict(), ZSTD_CCtx_loadDictionary(), etc.) * The entire source is compressed into a single frame. * @@ -1432,17 +1588,18 @@ ZSTDLIB_STATIC_API size_t ZSTD_mergeBlockDelimiters(ZSTD_Sequence* sequences, si * Note: Repcodes are, as of now, always re-calculated within this function, so ZSTD_Sequence::rep is unused. * Note 2: Once we integrate ability to ingest repcodes, the explicit block delims mode must respect those repcodes exactly, * and cannot emit an RLE block that disagrees with the repcode history - * @return : final compressed size or a ZSTD error. + * @return : final compressed size, or a ZSTD error code. */ -ZSTDLIB_STATIC_API size_t ZSTD_compressSequences(ZSTD_CCtx* const cctx, void* dst, size_t dstSize, - const ZSTD_Sequence* inSeqs, size_t inSeqsSize, - const void* src, size_t srcSize); +ZSTDLIB_STATIC_API size_t +ZSTD_compressSequences( ZSTD_CCtx* cctx, void* dst, size_t dstSize, + const ZSTD_Sequence* inSeqs, size_t inSeqsSize, + const void* src, size_t srcSize); /*! ZSTD_writeSkippableFrame() : * Generates a zstd skippable frame containing data given by src, and writes it to dst buffer. * - * Skippable frames begin with a a 4-byte magic number. There are 16 possible choices of magic number, + * Skippable frames begin with a 4-byte magic number. There are 16 possible choices of magic number, * ranging from ZSTD_MAGIC_SKIPPABLE_START to ZSTD_MAGIC_SKIPPABLE_START+15. * As such, the parameter magicVariant controls the exact skippable frame magic number variant used, so * the magic number used will be ZSTD_MAGIC_SKIPPABLE_START + magicVariant. @@ -1500,8 +1657,11 @@ ZSTDLIB_API unsigned ZSTD_isSkippableFrame(const void* buffer, size_t size); * and ZSTD_estimateCCtxSize_usingCCtxParams(), which can be used in tandem with ZSTD_CCtxParams_setParameter(). * Both can be used to estimate memory using custom compression parameters and arbitrary srcSize limits. * - * Note 2 : only single-threaded compression is supported. + * Note : only single-threaded compression is supported. * ZSTD_estimateCCtxSize_usingCCtxParams() will return an error code if ZSTD_c_nbWorkers is >= 1. + * + * Note 2 : ZSTD_estimateCCtxSize* functions are not compatible with the Block-Level Sequence Producer API at this time. + * Size estimates assume that no external sequence producer is registered. */ ZSTDLIB_STATIC_API size_t ZSTD_estimateCCtxSize(int compressionLevel); ZSTDLIB_STATIC_API size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams); @@ -1520,7 +1680,12 @@ ZSTDLIB_STATIC_API size_t ZSTD_estimateDCtxSize(void); * or deducted from a valid frame Header, using ZSTD_estimateDStreamSize_fromFrame(); * Note : if streaming is init with function ZSTD_init?Stream_usingDict(), * an internal ?Dict will be created, which additional size is not estimated here. - * In this case, get total size by adding ZSTD_estimate?DictSize */ + * In this case, get total size by adding ZSTD_estimate?DictSize + * Note 2 : only single-threaded compression is supported. + * ZSTD_estimateCStreamSize_usingCCtxParams() will return an error code if ZSTD_c_nbWorkers is >= 1. + * Note 3 : ZSTD_estimateCStreamSize* functions are not compatible with the Block-Level Sequence Producer API at this time. + * Size estimates assume that no external sequence producer is registered. + */ ZSTDLIB_STATIC_API size_t ZSTD_estimateCStreamSize(int compressionLevel); ZSTDLIB_STATIC_API size_t ZSTD_estimateCStreamSize_usingCParams(ZSTD_compressionParameters cParams); ZSTDLIB_STATIC_API size_t ZSTD_estimateCStreamSize_usingCCtxParams(const ZSTD_CCtx_params* params); @@ -1670,22 +1835,45 @@ ZSTDLIB_STATIC_API size_t ZSTD_checkCParams(ZSTD_compressionParameters params); * This function never fails (wide contract) */ ZSTDLIB_STATIC_API ZSTD_compressionParameters ZSTD_adjustCParams(ZSTD_compressionParameters cPar, unsigned long long srcSize, size_t dictSize); +/*! ZSTD_CCtx_setCParams() : + * Set all parameters provided within @p cparams into the working @p cctx. + * Note : if modifying parameters during compression (MT mode only), + * note that changes to the .windowLog parameter will be ignored. + * @return 0 on success, or an error code (can be checked with ZSTD_isError()). + * On failure, no parameters are updated. + */ +ZSTDLIB_STATIC_API size_t ZSTD_CCtx_setCParams(ZSTD_CCtx* cctx, ZSTD_compressionParameters cparams); + +/*! ZSTD_CCtx_setFParams() : + * Set all parameters provided within @p fparams into the working @p cctx. + * @return 0 on success, or an error code (can be checked with ZSTD_isError()). + */ +ZSTDLIB_STATIC_API size_t ZSTD_CCtx_setFParams(ZSTD_CCtx* cctx, ZSTD_frameParameters fparams); + +/*! ZSTD_CCtx_setParams() : + * Set all parameters provided within @p params into the working @p cctx. + * @return 0 on success, or an error code (can be checked with ZSTD_isError()). + */ +ZSTDLIB_STATIC_API size_t ZSTD_CCtx_setParams(ZSTD_CCtx* cctx, ZSTD_parameters params); + /*! ZSTD_compress_advanced() : * Note : this function is now DEPRECATED. * It can be replaced by ZSTD_compress2(), in combination with ZSTD_CCtx_setParameter() and other parameter setters. * This prototype will generate compilation warnings. */ ZSTD_DEPRECATED("use ZSTD_compress2") +ZSTDLIB_STATIC_API size_t ZSTD_compress_advanced(ZSTD_CCtx* cctx, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize, - const void* dict,size_t dictSize, - ZSTD_parameters params); + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, + const void* dict,size_t dictSize, + ZSTD_parameters params); /*! ZSTD_compress_usingCDict_advanced() : * Note : this function is now DEPRECATED. * It can be replaced by ZSTD_compress2(), in combination with ZSTD_CCtx_loadDictionary() and other parameter setters. * This prototype will generate compilation warnings. */ ZSTD_DEPRECATED("use ZSTD_compress2 with ZSTD_CCtx_loadDictionary") +ZSTDLIB_STATIC_API size_t ZSTD_compress_usingCDict_advanced(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, @@ -1829,13 +2017,16 @@ ZSTDLIB_STATIC_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const vo * Experimental parameter. * Default is 0 == disabled. Set to 1 to enable. * - * Tells the compressor that the ZSTD_inBuffer will ALWAYS be the same - * between calls, except for the modifications that zstd makes to pos (the - * caller must not modify pos). This is checked by the compressor, and - * compression will fail if it ever changes. This means the only flush - * mode that makes sense is ZSTD_e_end, so zstd will error if ZSTD_e_end - * is not used. The data in the ZSTD_inBuffer in the range [src, src + pos) - * MUST not be modified during compression or you will get data corruption. + * Tells the compressor that input data presented with ZSTD_inBuffer + * will ALWAYS be the same between calls. + * Technically, the @src pointer must never be changed, + * and the @pos field can only be updated by zstd. + * However, it's possible to increase the @size field, + * allowing scenarios where more data can be appended after compressions starts. + * These conditions are checked by the compressor, + * and compression will fail if they are not respected. + * Also, data in the ZSTD_inBuffer within the range [src, src + pos) + * MUST not be modified during compression or it will result in data corruption. * * When this flag is enabled zstd won't allocate an input window buffer, * because the user guarantees it can reference the ZSTD_inBuffer until @@ -1843,18 +2034,15 @@ ZSTDLIB_STATIC_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const vo * large enough to fit a block (see ZSTD_c_stableOutBuffer). This will also * avoid the memcpy() from the input buffer to the input window buffer. * - * NOTE: ZSTD_compressStream2() will error if ZSTD_e_end is not used. - * That means this flag cannot be used with ZSTD_compressStream(). - * * NOTE: So long as the ZSTD_inBuffer always points to valid memory, using * this flag is ALWAYS memory safe, and will never access out-of-bounds - * memory. However, compression WILL fail if you violate the preconditions. + * memory. However, compression WILL fail if conditions are not respected. * - * WARNING: The data in the ZSTD_inBuffer in the range [dst, dst + pos) MUST - * not be modified during compression or you will get data corruption. This - * is because zstd needs to reference data in the ZSTD_inBuffer to find + * WARNING: The data in the ZSTD_inBuffer in the range [src, src + pos) MUST + * not be modified during compression or it will result in data corruption. + * This is because zstd needs to reference data in the ZSTD_inBuffer to find * matches. Normally zstd maintains its own window buffer for this purpose, - * but passing this flag tells zstd to use the user provided buffer. + * but passing this flag tells zstd to rely on user provided buffer instead. */ #define ZSTD_c_stableInBuffer ZSTD_c_experimentalParam9 @@ -1899,7 +2087,7 @@ ZSTDLIB_STATIC_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const vo * Without validation, providing a sequence that does not conform to the zstd spec will cause * undefined behavior, and may produce a corrupted block. * - * With validation enabled, a if sequence is invalid (see doc/zstd_compression_format.md for + * With validation enabled, if sequence is invalid (see doc/zstd_compression_format.md for * specifics regarding offset/matchlength requirements) then the function will bail out and * return an error. * @@ -1949,6 +2137,79 @@ ZSTDLIB_STATIC_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const vo */ #define ZSTD_c_deterministicRefPrefix ZSTD_c_experimentalParam15 +/* ZSTD_c_prefetchCDictTables + * Controlled with ZSTD_paramSwitch_e enum. Default is ZSTD_ps_auto. + * + * In some situations, zstd uses CDict tables in-place rather than copying them + * into the working context. (See docs on ZSTD_dictAttachPref_e above for details). + * In such situations, compression speed is seriously impacted when CDict tables are + * "cold" (outside CPU cache). This parameter instructs zstd to prefetch CDict tables + * when they are used in-place. + * + * For sufficiently small inputs, the cost of the prefetch will outweigh the benefit. + * For sufficiently large inputs, zstd will by default memcpy() CDict tables + * into the working context, so there is no need to prefetch. This parameter is + * targeted at a middle range of input sizes, where a prefetch is cheap enough to be + * useful but memcpy() is too expensive. The exact range of input sizes where this + * makes sense is best determined by careful experimentation. + * + * Note: for this parameter, ZSTD_ps_auto is currently equivalent to ZSTD_ps_disable, + * but in the future zstd may conditionally enable this feature via an auto-detection + * heuristic for cold CDicts. + * Use ZSTD_ps_disable to opt out of prefetching under any circumstances. + */ +#define ZSTD_c_prefetchCDictTables ZSTD_c_experimentalParam16 + +/* ZSTD_c_enableSeqProducerFallback + * Allowed values are 0 (disable) and 1 (enable). The default setting is 0. + * + * Controls whether zstd will fall back to an internal sequence producer if an + * external sequence producer is registered and returns an error code. This fallback + * is block-by-block: the internal sequence producer will only be called for blocks + * where the external sequence producer returns an error code. Fallback parsing will + * follow any other cParam settings, such as compression level, the same as in a + * normal (fully-internal) compression operation. + * + * The user is strongly encouraged to read the full Block-Level Sequence Producer API + * documentation (below) before setting this parameter. */ +#define ZSTD_c_enableSeqProducerFallback ZSTD_c_experimentalParam17 + +/* ZSTD_c_maxBlockSize + * Allowed values are between 1KB and ZSTD_BLOCKSIZE_MAX (128KB). + * The default is ZSTD_BLOCKSIZE_MAX, and setting to 0 will set to the default. + * + * This parameter can be used to set an upper bound on the blocksize + * that overrides the default ZSTD_BLOCKSIZE_MAX. It cannot be used to set upper + * bounds greater than ZSTD_BLOCKSIZE_MAX or bounds lower than 1KB (will make + * compressBound() inaccurate). Only currently meant to be used for testing. + * + */ +#define ZSTD_c_maxBlockSize ZSTD_c_experimentalParam18 + +/* ZSTD_c_searchForExternalRepcodes + * This parameter affects how zstd parses external sequences, such as sequences + * provided through the compressSequences() API or from an external block-level + * sequence producer. + * + * If set to ZSTD_ps_enable, the library will check for repeated offsets in + * external sequences, even if those repcodes are not explicitly indicated in + * the "rep" field. Note that this is the only way to exploit repcode matches + * while using compressSequences() or an external sequence producer, since zstd + * currently ignores the "rep" field of external sequences. + * + * If set to ZSTD_ps_disable, the library will not exploit repeated offsets in + * external sequences, regardless of whether the "rep" field has been set. This + * reduces sequence compression overhead by about 25% while sacrificing some + * compression ratio. + * + * The default value is ZSTD_ps_auto, for which the library will enable/disable + * based on compression level. + * + * Note: for now, this param only has an effect if ZSTD_c_blockDelimiters is + * set to ZSTD_sf_explicitBlockDelimiters. That may change in the future. + */ +#define ZSTD_c_searchForExternalRepcodes ZSTD_c_experimentalParam19 + /*! ZSTD_CCtx_getParameter() : * Get the requested compression parameter value, selected by enum ZSTD_cParameter, * and store it into int* value. @@ -2105,7 +2366,7 @@ ZSTDLIB_STATIC_API size_t ZSTD_DCtx_getParameter(ZSTD_DCtx* dctx, ZSTD_dParamete * in the range [dst, dst + pos) MUST not be modified during decompression * or you will get data corruption. * - * When this flags is enabled zstd won't allocate an output buffer, because + * When this flag is enabled zstd won't allocate an output buffer, because * it can write directly to the ZSTD_outBuffer, but it will still allocate * an input buffer large enough to fit any compressed block. This will also * avoid the memcpy() from the internal output buffer to the ZSTD_outBuffer. @@ -2158,6 +2419,17 @@ ZSTDLIB_STATIC_API size_t ZSTD_DCtx_getParameter(ZSTD_DCtx* dctx, ZSTD_dParamete */ #define ZSTD_d_refMultipleDDicts ZSTD_d_experimentalParam4 +/* ZSTD_d_disableHuffmanAssembly + * Set to 1 to disable the Huffman assembly implementation. + * The default value is 0, which allows zstd to use the Huffman assembly + * implementation if available. + * + * This parameter can be used to disable Huffman assembly at runtime. + * If you want to disable it at compile time you can define the macro + * ZSTD_DISABLE_ASM. + */ +#define ZSTD_d_disableHuffmanAssembly ZSTD_d_experimentalParam5 + /*! ZSTD_DCtx_setFormat() : * This function is REDUNDANT. Prefer ZSTD_DCtx_setParameter(). @@ -2166,6 +2438,7 @@ ZSTDLIB_STATIC_API size_t ZSTD_DCtx_getParameter(ZSTD_DCtx* dctx, ZSTD_dParamete * such ZSTD_f_zstd1_magicless for example. * @return : 0, or an error code (which can be tested using ZSTD_isError()). */ ZSTD_DEPRECATED("use ZSTD_DCtx_setParameter() instead") +ZSTDLIB_STATIC_API size_t ZSTD_DCtx_setFormat(ZSTD_DCtx* dctx, ZSTD_format_e format); /*! ZSTD_decompressStream_simpleArgs() : @@ -2202,6 +2475,7 @@ ZSTDLIB_STATIC_API size_t ZSTD_decompressStream_simpleArgs ( * This prototype will generate compilation warnings. */ ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions") +ZSTDLIB_STATIC_API size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize); @@ -2219,17 +2493,15 @@ size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, * This prototype will generate compilation warnings. */ ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions") +ZSTDLIB_STATIC_API size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); /*! ZSTD_initCStream_advanced() : - * This function is DEPRECATED, and is approximately equivalent to: + * This function is DEPRECATED, and is equivalent to: * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only); - * // Pseudocode: Set each zstd parameter and leave the rest as-is. - * for ((param, value) : params) { - * ZSTD_CCtx_setParameter(zcs, param, value); - * } + * ZSTD_CCtx_setParams(zcs, params); * ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize); * ZSTD_CCtx_loadDictionary(zcs, dict, dictSize); * @@ -2239,6 +2511,7 @@ size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, * This prototype will generate compilation warnings. */ ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions") +ZSTDLIB_STATIC_API size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize, ZSTD_parameters params, @@ -2253,15 +2526,13 @@ size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, * This prototype will generate compilation warnings. */ ZSTD_DEPRECATED("use ZSTD_CCtx_reset and ZSTD_CCtx_refCDict, see zstd.h for detailed instructions") +ZSTDLIB_STATIC_API size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict); /*! ZSTD_initCStream_usingCDict_advanced() : - * This function is DEPRECATED, and is approximately equivalent to: + * This function is DEPRECATED, and is equivalent to: * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only); - * // Pseudocode: Set each zstd frame parameter and leave the rest as-is. - * for ((fParam, value) : fParams) { - * ZSTD_CCtx_setParameter(zcs, fParam, value); - * } + * ZSTD_CCtx_setFParams(zcs, fParams); * ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize); * ZSTD_CCtx_refCDict(zcs, cdict); * @@ -2271,6 +2542,7 @@ size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict); * This prototype will generate compilation warnings. */ ZSTD_DEPRECATED("use ZSTD_CCtx_reset and ZSTD_CCtx_refCDict, see zstd.h for detailed instructions") +ZSTDLIB_STATIC_API size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const ZSTD_CDict* cdict, ZSTD_frameParameters fParams, @@ -2295,6 +2567,7 @@ size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, * This prototype will generate compilation warnings. */ ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions") +ZSTDLIB_STATIC_API size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize); @@ -2340,8 +2613,8 @@ ZSTDLIB_STATIC_API size_t ZSTD_toFlushNow(ZSTD_CCtx* cctx); * ZSTD_DCtx_loadDictionary(zds, dict, dictSize); * * note: no dictionary will be used if dict == NULL or dictSize < 8 - * Note : this prototype will be marked as deprecated and generate compilation warnings on reaching v1.5.x */ +ZSTD_DEPRECATED("use ZSTD_DCtx_reset + ZSTD_DCtx_loadDictionary, see zstd.h for detailed instructions") ZSTDLIB_STATIC_API size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize); /*! @@ -2351,8 +2624,8 @@ ZSTDLIB_STATIC_API size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const vo * ZSTD_DCtx_refDDict(zds, ddict); * * note : ddict is referenced, it must outlive decompression session - * Note : this prototype will be marked as deprecated and generate compilation warnings on reaching v1.5.x */ +ZSTD_DEPRECATED("use ZSTD_DCtx_reset + ZSTD_DCtx_refDDict, see zstd.h for detailed instructions") ZSTDLIB_STATIC_API size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const ZSTD_DDict* ddict); /*! @@ -2361,17 +2634,185 @@ ZSTDLIB_STATIC_API size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const Z * ZSTD_DCtx_reset(zds, ZSTD_reset_session_only); * * re-use decompression parameters from previous init; saves dictionary loading - * Note : this prototype will be marked as deprecated and generate compilation warnings on reaching v1.5.x */ +ZSTD_DEPRECATED("use ZSTD_DCtx_reset, see zstd.h for detailed instructions") ZSTDLIB_STATIC_API size_t ZSTD_resetDStream(ZSTD_DStream* zds); +/* ********************* BLOCK-LEVEL SEQUENCE PRODUCER API ********************* + * + * *** OVERVIEW *** + * The Block-Level Sequence Producer API allows users to provide their own custom + * sequence producer which libzstd invokes to process each block. The produced list + * of sequences (literals and matches) is then post-processed by libzstd to produce + * valid compressed blocks. + * + * This block-level offload API is a more granular complement of the existing + * frame-level offload API compressSequences() (introduced in v1.5.1). It offers + * an easier migration story for applications already integrated with libzstd: the + * user application continues to invoke the same compression functions + * ZSTD_compress2() or ZSTD_compressStream2() as usual, and transparently benefits + * from the specific advantages of the external sequence producer. For example, + * the sequence producer could be tuned to take advantage of known characteristics + * of the input, to offer better speed / ratio, or could leverage hardware + * acceleration not available within libzstd itself. + * + * See contrib/externalSequenceProducer for an example program employing the + * Block-Level Sequence Producer API. + * + * *** USAGE *** + * The user is responsible for implementing a function of type + * ZSTD_sequenceProducer_F. For each block, zstd will pass the following + * arguments to the user-provided function: + * + * - sequenceProducerState: a pointer to a user-managed state for the sequence + * producer. + * + * - outSeqs, outSeqsCapacity: an output buffer for the sequence producer. + * outSeqsCapacity is guaranteed >= ZSTD_sequenceBound(srcSize). The memory + * backing outSeqs is managed by the CCtx. + * + * - src, srcSize: an input buffer for the sequence producer to parse. + * srcSize is guaranteed to be <= ZSTD_BLOCKSIZE_MAX. + * + * - dict, dictSize: a history buffer, which may be empty, which the sequence + * producer may reference as it parses the src buffer. Currently, zstd will + * always pass dictSize == 0 into external sequence producers, but this will + * change in the future. + * + * - compressionLevel: a signed integer representing the zstd compression level + * set by the user for the current operation. The sequence producer may choose + * to use this information to change its compression strategy and speed/ratio + * tradeoff. Note: the compression level does not reflect zstd parameters set + * through the advanced API. + * + * - windowSize: a size_t representing the maximum allowed offset for external + * sequences. Note that sequence offsets are sometimes allowed to exceed the + * windowSize if a dictionary is present, see doc/zstd_compression_format.md + * for details. + * + * The user-provided function shall return a size_t representing the number of + * sequences written to outSeqs. This return value will be treated as an error + * code if it is greater than outSeqsCapacity. The return value must be non-zero + * if srcSize is non-zero. The ZSTD_SEQUENCE_PRODUCER_ERROR macro is provided + * for convenience, but any value greater than outSeqsCapacity will be treated as + * an error code. + * + * If the user-provided function does not return an error code, the sequences + * written to outSeqs must be a valid parse of the src buffer. Data corruption may + * occur if the parse is not valid. A parse is defined to be valid if the + * following conditions hold: + * - The sum of matchLengths and literalLengths must equal srcSize. + * - All sequences in the parse, except for the final sequence, must have + * matchLength >= ZSTD_MINMATCH_MIN. The final sequence must have + * matchLength >= ZSTD_MINMATCH_MIN or matchLength == 0. + * - All offsets must respect the windowSize parameter as specified in + * doc/zstd_compression_format.md. + * - If the final sequence has matchLength == 0, it must also have offset == 0. + * + * zstd will only validate these conditions (and fail compression if they do not + * hold) if the ZSTD_c_validateSequences cParam is enabled. Note that sequence + * validation has a performance cost. + * + * If the user-provided function returns an error, zstd will either fall back + * to an internal sequence producer or fail the compression operation. The user can + * choose between the two behaviors by setting the ZSTD_c_enableSeqProducerFallback + * cParam. Fallback compression will follow any other cParam settings, such as + * compression level, the same as in a normal compression operation. + * + * The user shall instruct zstd to use a particular ZSTD_sequenceProducer_F + * function by calling + * ZSTD_registerSequenceProducer(cctx, + * sequenceProducerState, + * sequenceProducer) + * This setting will persist until the next parameter reset of the CCtx. + * + * The sequenceProducerState must be initialized by the user before calling + * ZSTD_registerSequenceProducer(). The user is responsible for destroying the + * sequenceProducerState. + * + * *** LIMITATIONS *** + * This API is compatible with all zstd compression APIs which respect advanced parameters. + * However, there are three limitations: + * + * First, the ZSTD_c_enableLongDistanceMatching cParam is not currently supported. + * COMPRESSION WILL FAIL if it is enabled and the user tries to compress with a block-level + * external sequence producer. + * - Note that ZSTD_c_enableLongDistanceMatching is auto-enabled by default in some + * cases (see its documentation for details). Users must explicitly set + * ZSTD_c_enableLongDistanceMatching to ZSTD_ps_disable in such cases if an external + * sequence producer is registered. + * - As of this writing, ZSTD_c_enableLongDistanceMatching is disabled by default + * whenever ZSTD_c_windowLog < 128MB, but that's subject to change. Users should + * check the docs on ZSTD_c_enableLongDistanceMatching whenever the Block-Level Sequence + * Producer API is used in conjunction with advanced settings (like ZSTD_c_windowLog). + * + * Second, history buffers are not currently supported. Concretely, zstd will always pass + * dictSize == 0 to the external sequence producer (for now). This has two implications: + * - Dictionaries are not currently supported. Compression will *not* fail if the user + * references a dictionary, but the dictionary won't have any effect. + * - Stream history is not currently supported. All advanced compression APIs, including + * streaming APIs, work with external sequence producers, but each block is treated as + * an independent chunk without history from previous blocks. + * + * Third, multi-threading within a single compression is not currently supported. In other words, + * COMPRESSION WILL FAIL if ZSTD_c_nbWorkers > 0 and an external sequence producer is registered. + * Multi-threading across compressions is fine: simply create one CCtx per thread. + * + * Long-term, we plan to overcome all three limitations. There is no technical blocker to + * overcoming them. It is purely a question of engineering effort. + */ + +#define ZSTD_SEQUENCE_PRODUCER_ERROR ((size_t)(-1)) + +typedef size_t ZSTD_sequenceProducer_F ( + void* sequenceProducerState, + ZSTD_Sequence* outSeqs, size_t outSeqsCapacity, + const void* src, size_t srcSize, + const void* dict, size_t dictSize, + int compressionLevel, + size_t windowSize +); + +/*! ZSTD_registerSequenceProducer() : + * Instruct zstd to use a block-level external sequence producer function. + * + * The sequenceProducerState must be initialized by the caller, and the caller is + * responsible for managing its lifetime. This parameter is sticky across + * compressions. It will remain set until the user explicitly resets compression + * parameters. + * + * Sequence producer registration is considered to be an "advanced parameter", + * part of the "advanced API". This means it will only have an effect on compression + * APIs which respect advanced parameters, such as compress2() and compressStream2(). + * Older compression APIs such as compressCCtx(), which predate the introduction of + * "advanced parameters", will ignore any external sequence producer setting. + * + * The sequence producer can be "cleared" by registering a NULL function pointer. This + * removes all limitations described above in the "LIMITATIONS" section of the API docs. + * + * The user is strongly encouraged to read the full API documentation (above) before + * calling this function. */ +ZSTDLIB_STATIC_API void +ZSTD_registerSequenceProducer( + ZSTD_CCtx* cctx, + void* sequenceProducerState, + ZSTD_sequenceProducer_F* sequenceProducer +); + + /********************************************************************* -* Buffer-less and synchronous inner streaming functions +* Buffer-less and synchronous inner streaming functions (DEPRECATED) +* +* This API is deprecated, and will be removed in a future version. +* It allows streaming (de)compression with user allocated buffers. +* However, it is hard to use, and not as well tested as the rest of +* our API. * -* This is an advanced API, giving full control over buffer management, for users which need direct control over memory. -* But it's also a complex one, with several restrictions, documented below. -* Prefer normal streaming API for an easier experience. +* Please use the normal streaming API instead: ZSTD_compressStream2, +* and ZSTD_decompressStream. +* If there is functionality that you need, but it doesn't provide, +* please open an issue on our GitHub. ********************************************************************* */ /** @@ -2383,7 +2824,6 @@ ZSTDLIB_STATIC_API size_t ZSTD_resetDStream(ZSTD_DStream* zds); Start by initializing a context. Use ZSTD_compressBegin(), or ZSTD_compressBegin_usingDict() for dictionary compression. - It's also possible to duplicate a reference context which has already been initialized, using ZSTD_copyCCtx() Then, consume your input using ZSTD_compressContinue(). There are some important considerations to keep in mind when using this advanced function : @@ -2405,18 +2845,28 @@ ZSTDLIB_STATIC_API size_t ZSTD_resetDStream(ZSTD_DStream* zds); */ /*===== Buffer-less streaming compression functions =====*/ +ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.") ZSTDLIB_STATIC_API size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel); +ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.") ZSTDLIB_STATIC_API size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel); +ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.") ZSTDLIB_STATIC_API size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); /**< note: fails if cdict==NULL */ -ZSTDLIB_STATIC_API size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); /**< note: if pledgedSrcSize is not known, use ZSTD_CONTENTSIZE_UNKNOWN */ +ZSTD_DEPRECATED("This function will likely be removed in a future release. It is misleading and has very limited utility.") +ZSTDLIB_STATIC_API +size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); /**< note: if pledgedSrcSize is not known, use ZSTD_CONTENTSIZE_UNKNOWN */ + +ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.") ZSTDLIB_STATIC_API size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); +ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.") ZSTDLIB_STATIC_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); /* The ZSTD_compressBegin_advanced() and ZSTD_compressBegin_usingCDict_advanced() are now DEPRECATED and will generate a compiler warning */ ZSTD_DEPRECATED("use advanced API to access custom parameters") +ZSTDLIB_STATIC_API size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize : If srcSize is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN */ ZSTD_DEPRECATED("use advanced API to access custom parameters") +ZSTDLIB_STATIC_API size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_CDict* const cdict, ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize); /* compression parameters are already set within cdict. pledgedSrcSize must be correct. If srcSize is not known, use macro ZSTD_CONTENTSIZE_UNKNOWN */ /** Buffer-less streaming decompression (synchronous mode) @@ -2429,8 +2879,8 @@ size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_ Frame header is extracted from the beginning of compressed frame, so providing only the frame's beginning is enough. Data fragment must be large enough to ensure successful decoding. `ZSTD_frameHeaderSize_max` bytes is guaranteed to always be large enough. - @result : 0 : successful decoding, the `ZSTD_frameHeader` structure is correctly filled. - >0 : `srcSize` is too small, please provide at least @result bytes on next attempt. + result : 0 : successful decoding, the `ZSTD_frameHeader` structure is correctly filled. + >0 : `srcSize` is too small, please provide at least result bytes on next attempt. errorCode, which can be tested using ZSTD_isError(). It fills a ZSTD_frameHeader structure with important information to correctly decode the frame, @@ -2449,7 +2899,7 @@ size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_ The most memory efficient way is to use a round buffer of sufficient size. Sufficient size is determined by invoking ZSTD_decodingBufferSize_min(), - which can @return an error code if required value is too large for current system (in 32-bits mode). + which can return an error code if required value is too large for current system (in 32-bits mode). In a round buffer methodology, ZSTD_decompressContinue() decompresses each block next to previous one, up to the moment there is not enough room left in the buffer to guarantee decoding another full block, which maximum size is provided in `ZSTD_frameHeader` structure, field `blockSizeMax`. @@ -2469,7 +2919,7 @@ size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_ ZSTD_nextSrcSizeToDecompress() tells how many bytes to provide as 'srcSize' to ZSTD_decompressContinue(). ZSTD_decompressContinue() requires this _exact_ amount of bytes, or it will fail. - @result of ZSTD_decompressContinue() is the number of bytes regenerated within 'dst' (necessarily <= dstCapacity). + result of ZSTD_decompressContinue() is the number of bytes regenerated within 'dst' (necessarily <= dstCapacity). It can be zero : it just means ZSTD_decompressContinue() has decoded some metadata item. It can also be an error code, which can be tested with ZSTD_isError(). @@ -2492,27 +2942,7 @@ size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_ */ /*===== Buffer-less streaming decompression functions =====*/ -typedef enum { ZSTD_frame, ZSTD_skippableFrame } ZSTD_frameType_e; -typedef struct { - unsigned long long frameContentSize; /* if == ZSTD_CONTENTSIZE_UNKNOWN, it means this field is not available. 0 means "empty" */ - unsigned long long windowSize; /* can be very large, up to <= frameContentSize */ - unsigned blockSizeMax; - ZSTD_frameType_e frameType; /* if == ZSTD_skippableFrame, frameContentSize is the size of skippable content */ - unsigned headerSize; - unsigned dictID; - unsigned checksumFlag; -} ZSTD_frameHeader; -/*! ZSTD_getFrameHeader() : - * decode Frame Header, or requires larger `srcSize`. - * @return : 0, `zfhPtr` is correctly filled, - * >0, `srcSize` is too small, value is wanted `srcSize` amount, - * or an error code, which can be tested using ZSTD_isError() */ -ZSTDLIB_STATIC_API size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize); /**< doesn't consume input */ -/*! ZSTD_getFrameHeader_advanced() : - * same as ZSTD_getFrameHeader(), - * with added capability to select a format (like ZSTD_f_zstd1_magicless) */ -ZSTDLIB_STATIC_API size_t ZSTD_getFrameHeader_advanced(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize, ZSTD_format_e format); ZSTDLIB_STATIC_API size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long long frameContentSize); /**< when frame content size is not known, pass in frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN */ ZSTDLIB_STATIC_API size_t ZSTD_decompressBegin(ZSTD_DCtx* dctx); @@ -2523,6 +2953,7 @@ ZSTDLIB_STATIC_API size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx); ZSTDLIB_STATIC_API size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); /* misc */ +ZSTD_DEPRECATED("This function will likely be removed in the next minor release. It is misleading and has very limited utility.") ZSTDLIB_STATIC_API void ZSTD_copyDCtx(ZSTD_DCtx* dctx, const ZSTD_DCtx* preparedDCtx); typedef enum { ZSTDnit_frameHeader, ZSTDnit_blockHeader, ZSTDnit_block, ZSTDnit_lastBlock, ZSTDnit_checksum, ZSTDnit_skippableFrame } ZSTD_nextInputType_e; ZSTDLIB_STATIC_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx); @@ -2530,11 +2961,23 @@ ZSTDLIB_STATIC_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx); -/* ============================ */ -/** Block level API */ -/* ============================ */ +/* ========================================= */ +/** Block level API (DEPRECATED) */ +/* ========================================= */ /*! + + This API is deprecated in favor of the regular compression API. + You can get the frame header down to 2 bytes by setting: + - ZSTD_c_format = ZSTD_f_zstd1_magicless + - ZSTD_c_contentSizeFlag = 0 + - ZSTD_c_checksumFlag = 0 + - ZSTD_c_dictIDFlag = 0 + + This API is not as well tested as our normal API, so we recommend not using it. + We will be removing it in a future version. If the normal API doesn't provide + the functionality you need, please open a GitHub issue. + Block functions produce and decode raw zstd blocks, without frame metadata. Frame metadata cost is typically ~12 bytes, which can be non-negligible for very small blocks (< 100 bytes). But users will have to take in charge needed metadata to regenerate data, such as compressed and content sizes. @@ -2545,7 +2988,6 @@ ZSTDLIB_STATIC_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx); - It is necessary to init context before starting + compression : any ZSTD_compressBegin*() variant, including with dictionary + decompression : any ZSTD_decompressBegin*() variant, including with dictionary - + copyCCtx() and copyDCtx() can be used too - Block size is limited, it must be <= ZSTD_getBlockSize() <= ZSTD_BLOCKSIZE_MAX == 128 KB + If input is larger than a block size, it's necessary to split input data into multiple blocks + For inputs larger than a single block, consider using regular ZSTD_compress() instead. @@ -2562,12 +3004,15 @@ ZSTDLIB_STATIC_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx); */ /*===== Raw zstd block functions =====*/ +ZSTD_DEPRECATED("The block API is deprecated in favor of the normal compression API. See docs.") ZSTDLIB_STATIC_API size_t ZSTD_getBlockSize (const ZSTD_CCtx* cctx); +ZSTD_DEPRECATED("The block API is deprecated in favor of the normal compression API. See docs.") ZSTDLIB_STATIC_API size_t ZSTD_compressBlock (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); +ZSTD_DEPRECATED("The block API is deprecated in favor of the normal compression API. See docs.") ZSTDLIB_STATIC_API size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); +ZSTD_DEPRECATED("The block API is deprecated in favor of the normal compression API. See docs.") ZSTDLIB_STATIC_API size_t ZSTD_insertBlock (ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize); /**< insert uncompressed block into `dctx` history. Useful for multi-blocks decompression. */ - #endif /* ZSTD_H_ZSTD_STATIC_LINKING_ONLY */ #if defined (__cplusplus) diff --git a/internal-complibs/zstd-1.5.2/zstd_errors.h b/internal-complibs/zstd-1.5.5/zstd_errors.h similarity index 74% rename from internal-complibs/zstd-1.5.2/zstd_errors.h rename to internal-complibs/zstd-1.5.5/zstd_errors.h index fa3686b7..dc75eeeb 100644 --- a/internal-complibs/zstd-1.5.2/zstd_errors.h +++ b/internal-complibs/zstd-1.5.5/zstd_errors.h @@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -20,19 +20,31 @@ extern "C" { /* ===== ZSTDERRORLIB_API : control library symbols visibility ===== */ -#ifndef ZSTDERRORLIB_VISIBILITY -# if defined(__GNUC__) && (__GNUC__ >= 4) -# define ZSTDERRORLIB_VISIBILITY __attribute__ ((visibility ("default"))) +#ifndef ZSTDERRORLIB_VISIBLE + /* Backwards compatibility with old macro name */ +# ifdef ZSTDERRORLIB_VISIBILITY +# define ZSTDERRORLIB_VISIBLE ZSTDERRORLIB_VISIBILITY +# elif defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__) +# define ZSTDERRORLIB_VISIBLE __attribute__ ((visibility ("default"))) # else -# define ZSTDERRORLIB_VISIBILITY +# define ZSTDERRORLIB_VISIBLE # endif #endif + +#ifndef ZSTDERRORLIB_HIDDEN +# if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__) +# define ZSTDERRORLIB_HIDDEN __attribute__ ((visibility ("hidden"))) +# else +# define ZSTDERRORLIB_HIDDEN +# endif +#endif + #if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1) -# define ZSTDERRORLIB_API __declspec(dllexport) ZSTDERRORLIB_VISIBILITY +# define ZSTDERRORLIB_API __declspec(dllexport) ZSTDERRORLIB_VISIBLE #elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1) -# define ZSTDERRORLIB_API __declspec(dllimport) ZSTDERRORLIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ +# define ZSTDERRORLIB_API __declspec(dllimport) ZSTDERRORLIB_VISIBLE /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ #else -# define ZSTDERRORLIB_API ZSTDERRORLIB_VISIBILITY +# define ZSTDERRORLIB_API ZSTDERRORLIB_VISIBLE #endif /*-********************************************* @@ -58,14 +70,17 @@ typedef enum { ZSTD_error_frameParameter_windowTooLarge = 16, ZSTD_error_corruption_detected = 20, ZSTD_error_checksum_wrong = 22, + ZSTD_error_literals_headerWrong = 24, ZSTD_error_dictionary_corrupted = 30, ZSTD_error_dictionary_wrong = 32, ZSTD_error_dictionaryCreation_failed = 34, ZSTD_error_parameter_unsupported = 40, + ZSTD_error_parameter_combination_unsupported = 41, ZSTD_error_parameter_outOfBound = 42, ZSTD_error_tableLog_tooLarge = 44, ZSTD_error_maxSymbolValue_tooLarge = 46, ZSTD_error_maxSymbolValue_tooSmall = 48, + ZSTD_error_stabilityCondition_notRespected = 50, ZSTD_error_stage_wrong = 60, ZSTD_error_init_missing = 62, ZSTD_error_memory_allocation = 64, @@ -73,11 +88,15 @@ typedef enum { ZSTD_error_dstSize_tooSmall = 70, ZSTD_error_srcSize_wrong = 72, ZSTD_error_dstBuffer_null = 74, + ZSTD_error_noForwardProgress_destFull = 80, + ZSTD_error_noForwardProgress_inputEmpty = 82, /* following error codes are __NOT STABLE__, they can be removed or changed in future versions */ ZSTD_error_frameIndex_tooLarge = 100, ZSTD_error_seekableIO = 102, ZSTD_error_dstBuffer_wrong = 104, ZSTD_error_srcBuffer_wrong = 105, + ZSTD_error_sequenceProducer_failed = 106, + ZSTD_error_externalSequences_invalid = 107, ZSTD_error_maxCode = 120 /* never EVER use this value directly, it can change in future versions! Use ZSTD_isError() instead */ } ZSTD_ErrorCode; diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 62c1f959..1b46ac22 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -1,4 +1,13 @@ +# Blosc - Blocked Shuffling and Compression Library +# +# Copyright (c) 2021 The Blosc Development Team +# https://blosc.org +# License: BSD 3-Clause (see LICENSE.txt) +# +# See LICENSE.txt for details about copyright and rights to use. + add_subdirectory(codecs) +add_subdirectory(tunes) add_subdirectory(filters) -set(SOURCES ${SOURCES} ../plugins/plugin_utils.c ../plugins/plugin_utils.h PARENT_SCOPE) +set(SOURCES ${SOURCES} ${PROJECT_SOURCE_DIR}/plugins/plugin_utils.c PARENT_SCOPE) diff --git a/plugins/README.md b/plugins/README.md index 380d1fcc..c2751b4b 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -3,14 +3,14 @@ Plugins registry for Blosc users Blosc has a tradition of supporting different filters and codecs for compressing data, and it was up to the user to choose one or another depending on his needs. -However, it is clear that there will always be scenarios where a more richer variety -of them could be useful. So the Blosc Development Team has set new goals: +However, it is clear that there will always be scenarios where a richer variety +of them could be useful. So the Blosc team has set new goals: 1) Implement a way for users to locally register filters and codecs so that they can use them in their setup at will. -2) Setup a central registry so that *other* users can make use of these filters and codecs - without intefering with other ones that have been created by other users. +2) Set up a central registry so that *other* users can make use of these filters and codecs + without interfering with other ones that have been created by other users. As a bonus, those codecs and filters accepted in the central registry and meeting the quality standards defined in these guidelines will be distributed *inside* the C-Blosc2 library, @@ -26,7 +26,7 @@ The plugins that are registered in the repository can be codecs or filters. A **codec** is a program able to compress and decompress a digital data stream with the objective of reduce dataset size to enable a faster transmission of data. -Some of the codecs used by Blosc are e.g. *BLOSCLZ*, *LZ4* and *ZSTANDARD*. +Some codecs used by Blosc are e.g. *BLOSCLZ*, *LZ4* and *ZSTANDARD*. A **filter** is a program that reorders the data without changing its size, so that the initial and final size are equal. @@ -35,7 +35,7 @@ using the codec compressor (or codec encoder) in order to make data easier to co and filter decoder is used after codec decompressor (or codec decoder) to recover the original data arrangement. Some filters actually used by Blosc are e.g. *SHUFFLE*, which rearranges data -based on the typesize, or *TRUNC*, which zeroes mantissa bits so as to reduce +based on the typesize, or *TRUNC*, which zeroes mantissa bits to reduce the precision of (floating point) data, and hence, increase the compression ratio. Here it is an example on how the compression process goes: @@ -62,7 +62,7 @@ Blosc global registered plugins vs user registered plugins and have been recognised by the Blosc Development Team. These plugins are available for everybody in the C-Blosc2 GitHub repository and users can install them anytime. -**User registered plugins** are plugins that users register locally and they can use them +**User registered plugins** are plugins that users register locally, and they can use them in the same way as in the examples `urcodecs.c` and `urfilters.c`. If you only want to use a plugin on your own devices you can just register it as a user registered @@ -84,7 +84,7 @@ that their code must satisfy: Finally, even if these requirements are completely satisfied, it is not guaranteed that the plugin will be useful or contribute something -different than the existing ones, so the Blosc development team has the final +different from the existing ones, so the Blosc development team has the final say and will decide if a plugin is to be accepted or not. @@ -97,8 +97,7 @@ Steps - `blosc2_init()` at the beginning - `blosc2_destroy()` in the end - -2. Then, the user must make a fork of the C-Blosc2 Github repository, +2. Then, the user must make a fork of the C-Blosc2 GitHub repository, adding a new folder within the plugin sources to the path `plugins/codecs` or `plugins/filters` depending on the plugin type. @@ -123,8 +122,7 @@ Steps and into the register function you must follow the same steps that were done for the existing plugins. 5. Finally, the Blosc development team will carry out the evaluation process - (probably via a votation process, with the BDFL having the last say in case of the team is undecided) - so as to decide whether the plugin is useful and hence, candidate to be integrated into the C-Blosc2 + to decide whether the plugin is useful and hence, candidate to be integrated into the C-Blosc2 source code distribution. In case of a negative decision, the original author will be informed, together with a series of advices for starting a new iteration if desired. @@ -135,10 +133,10 @@ Examples In the `plugins/` directory there can be found different examples of codecs and filters available as plugins that can be used in the compression process, and that can be used as an example on how to implement plugins that can make into C-Blosc2. -Some of these examples are `ndlz`, `ndcell` or `ndmean`. +Some of these are `ndlz`, `ndcell`, `ndmean` or `bytedelta`. Thanks ------ -We would like to express our gratitude to the NumFOCUS Foundation so as to provide the funds to implement this functionality. +We would like to express our gratitude to the NumFOCUS Foundation for providing the funds to implement this functionality. diff --git a/plugins/codecs/CMakeLists.txt b/plugins/codecs/CMakeLists.txt index e2fdcfa0..70ebc2d6 100644 --- a/plugins/codecs/CMakeLists.txt +++ b/plugins/codecs/CMakeLists.txt @@ -1,4 +1,4 @@ add_subdirectory(ndlz) add_subdirectory(zfp) -set(SOURCES ${SOURCES} ../plugins/codecs/codecs-registry.c PARENT_SCOPE) \ No newline at end of file +set(SOURCES ${SOURCES} ${PROJECT_SOURCE_DIR}/plugins/codecs/codecs-registry.c PARENT_SCOPE) diff --git a/plugins/codecs/codecs-registry.c b/plugins/codecs/codecs-registry.c index 751cc4f7..db5c9f02 100644 --- a/plugins/codecs/codecs-registry.c +++ b/plugins/codecs/codecs-registry.c @@ -1,49 +1,49 @@ /* - Copyright (C) 2021 The Blosc Developers - http://blosc.org + Copyright (c) 2021 The Blosc Development Team + https://blosc.org License: BSD 3-Clause (see LICENSE.txt) */ -#include +#include "blosc-private.h" #include "blosc2/codecs-registry.h" #include "ndlz/ndlz.h" #include "zfp/blosc2-zfp.h" void register_codecs(void) { - blosc2_codec ndlz; - ndlz.compcode = BLOSC_CODEC_NDLZ; - ndlz.compver = 1; - ndlz.complib = BLOSC_CODEC_NDLZ; - ndlz.encoder = ndlz_compress; - ndlz.decoder = ndlz_decompress; - ndlz.compname = "ndlz"; - register_codec_private(&ndlz); + blosc2_codec ndlz; + ndlz.compcode = BLOSC_CODEC_NDLZ; + ndlz.version = 1; + ndlz.complib = BLOSC_CODEC_NDLZ; + ndlz.encoder = ndlz_compress; + ndlz.decoder = ndlz_decompress; + ndlz.compname = "ndlz"; + register_codec_private(&ndlz); - blosc2_codec zfp_acc; - zfp_acc.compcode = BLOSC_CODEC_ZFP_FIXED_ACCURACY; - zfp_acc.compver = 1; - zfp_acc.complib = BLOSC_CODEC_ZFP_FIXED_ACCURACY; - zfp_acc.encoder = zfp_acc_compress; - zfp_acc.decoder = zfp_acc_decompress; - zfp_acc.compname = "zfp_acc"; - register_codec_private(&zfp_acc); + blosc2_codec zfp_acc; + zfp_acc.compcode = BLOSC_CODEC_ZFP_FIXED_ACCURACY; + zfp_acc.version = 1; + zfp_acc.complib = BLOSC_CODEC_ZFP_FIXED_ACCURACY; + zfp_acc.encoder = zfp_acc_compress; + zfp_acc.decoder = zfp_acc_decompress; + zfp_acc.compname = "zfp_acc"; + register_codec_private(&zfp_acc); - blosc2_codec zfp_prec; - zfp_prec.compcode = BLOSC_CODEC_ZFP_FIXED_PRECISION; - zfp_prec.compver = 1; - zfp_prec.complib = BLOSC_CODEC_ZFP_FIXED_PRECISION; - zfp_prec.encoder = zfp_prec_compress; - zfp_prec.decoder = zfp_prec_decompress; - zfp_prec.compname = "zfp_prec"; - register_codec_private(&zfp_prec); + blosc2_codec zfp_prec; + zfp_prec.compcode = BLOSC_CODEC_ZFP_FIXED_PRECISION; + zfp_prec.version = 1; + zfp_prec.complib = BLOSC_CODEC_ZFP_FIXED_PRECISION; + zfp_prec.encoder = zfp_prec_compress; + zfp_prec.decoder = zfp_prec_decompress; + zfp_prec.compname = "zfp_prec"; + register_codec_private(&zfp_prec); - blosc2_codec zfp_rate; - zfp_rate.compcode = BLOSC_CODEC_ZFP_FIXED_RATE; - zfp_rate.compver = 1; - zfp_rate.complib = BLOSC_CODEC_ZFP_FIXED_RATE; - zfp_rate.encoder = zfp_rate_compress; - zfp_rate.decoder = zfp_rate_decompress; - zfp_rate.compname = "zfp_rate"; - register_codec_private(&zfp_rate); + blosc2_codec zfp_rate; + zfp_rate.compcode = BLOSC_CODEC_ZFP_FIXED_RATE; + zfp_rate.version = 1; + zfp_rate.complib = BLOSC_CODEC_ZFP_FIXED_RATE; + zfp_rate.encoder = zfp_rate_compress; + zfp_rate.decoder = zfp_rate_decompress; + zfp_rate.compname = "zfp_rate"; + register_codec_private(&zfp_rate); } diff --git a/plugins/codecs/ndlz/CMakeLists.txt b/plugins/codecs/ndlz/CMakeLists.txt index da7ce92f..24424fa1 100644 --- a/plugins/codecs/ndlz/CMakeLists.txt +++ b/plugins/codecs/ndlz/CMakeLists.txt @@ -1,7 +1,18 @@ +# Blosc - Blocked Shuffling and Compression Library +# +# Copyright (c) 2021 The Blosc Development Team +# https://blosc.org +# License: BSD 3-Clause (see LICENSE.txt) +# +# See LICENSE.txt for details about copyright and rights to use. + # sources -set(SOURCES ${SOURCES} ../plugins/codecs/ndlz/ndlz.c ../plugins/codecs/ndlz/ndlz.h ../plugins/codecs/ndlz/ndlz-private.h - ../plugins/codecs/ndlz/ndlz4x4.c ../plugins/codecs/ndlz/ndlz4x4.h ../plugins/codecs/ndlz/ndlz8x8.c - ../plugins/codecs/ndlz/ndlz8x8.h ../plugins/codecs/ndlz/xxhash.c ../plugins/codecs/ndlz/xxhash.h PARENT_SCOPE) +set(SOURCES ${SOURCES} + ${PROJECT_SOURCE_DIR}/plugins/codecs/ndlz/ndlz.c + ${PROJECT_SOURCE_DIR}/plugins/codecs/ndlz/ndlz4x4.c + ${PROJECT_SOURCE_DIR}/plugins/codecs/ndlz/ndlz8x8.c + ${PROJECT_SOURCE_DIR}/plugins/codecs/ndlz/xxhash.c + PARENT_SCOPE) # targets if(BUILD_TESTS) @@ -19,7 +30,7 @@ if(BUILD_TESTS) COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $) # Copy test files - file(GLOB TESTS_DATA ../../test_data/example_s*.caterva) + file(GLOB TESTS_DATA ../../test_data/example_s*.b2nd) foreach (data ${TESTS_DATA}) file(COPY ${data} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) diff --git a/plugins/codecs/ndlz/README.md b/plugins/codecs/ndlz/README.md index de2174fd..c607d3e7 100644 --- a/plugins/codecs/ndlz/README.md +++ b/plugins/codecs/ndlz/README.md @@ -6,7 +6,7 @@ Given a 2-dim array or matrix, *NDLZ* is a compressor based on the Lempel-Ziv al Plugin motivation -------------------- -*NDLZ* was created in order to search for patterns repetitions in multidimensional cells using the Caterva blocking machinery. +*NDLZ* was created in order to search for patterns repetitions in multidimensional cells using a multidimensional blocking machinery. Plugin usage ------------------- @@ -58,11 +58,10 @@ Otherwise, it is important to know that there exist different hash tables which Advantages and disadvantages ------------------------------ -The main advantage of *NDLZ* in front of most of the codecs is that this one +The main advantage of *NDLZ* when compared with most of the codecs is that this one considers dataset multidimensionality and takes advantage of it instead of processing all data as serial. The main disadvantage of *NDLZ* is that it is only useful for 2-dim datasets -and at the moment other more developed -codecs that do not consider multidimensionality obtain better results -(times and ratios) for 2-dim datasets. +and at the moment it gets worse results (times and ratios) than other, more developed codecs +that do not consider multidimensionality, at least in our limited testing. diff --git a/plugins/codecs/ndlz/ndlz-private.h b/plugins/codecs/ndlz/ndlz-private.h index 4c70a4b8..c73239a0 100644 --- a/plugins/codecs/ndlz/ndlz-private.h +++ b/plugins/codecs/ndlz/ndlz-private.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -12,6 +12,7 @@ #ifndef NDLZ_PRIVATE_H #define NDLZ_PRIVATE_H + #include "context.h" #if defined (__cplusplus) @@ -20,11 +21,11 @@ extern "C" { #define XXH_INLINE_ALL #define NDLZ_ERROR_NULL(pointer) \ - do { \ - if ((pointer) == NULL) { \ - return 0; \ - } \ - } while (0) + do { \ + if ((pointer) == NULL) { \ + return 0; \ + } \ + } while (0) #if defined (__cplusplus) diff --git a/plugins/codecs/ndlz/ndlz.c b/plugins/codecs/ndlz/ndlz.c index 86c7c08c..128be185 100644 --- a/plugins/codecs/ndlz/ndlz.c +++ b/plugins/codecs/ndlz/ndlz.c @@ -1,10 +1,9 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Author: Francesc Alted - Author: Oscar Griñón - Author: Aleix Alcacer - Creation date: 2020-06-12 + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ @@ -24,42 +23,37 @@ #include "ndlz8x8.h" int ndlz_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int32_t output_len, - uint8_t meta, blosc2_cparams *cparams, const void* chunk) { - NDLZ_ERROR_NULL(input); - NDLZ_ERROR_NULL(output); - NDLZ_ERROR_NULL(cparams); - BLOSC_UNUSED_PARAM(chunk); - - switch (meta) { - case 4: - return ndlz4_compress(input, input_len, output, output_len, meta, cparams); - case 8: - return ndlz8_compress(input, input_len, output, output_len, meta, cparams); - default: - printf("\n NDLZ is not available for this cellsize \n"); - return 0; - } + uint8_t meta, blosc2_cparams *cparams, const void *chunk) { + NDLZ_ERROR_NULL(input); + NDLZ_ERROR_NULL(output); + NDLZ_ERROR_NULL(cparams); + BLOSC_UNUSED_PARAM(chunk); + + switch (meta) { + case 4: + return ndlz4_compress(input, input_len, output, output_len, meta, cparams); + case 8: + return ndlz8_compress(input, input_len, output, output_len, meta, cparams); + default: + BLOSC_TRACE_ERROR("NDLZ is not available for this cellsize: %d", meta); + } + return BLOSC2_ERROR_FAILURE; } int ndlz_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, int32_t output_len, - uint8_t meta, blosc2_dparams *dparams, const void* chunk) { - NDLZ_ERROR_NULL(input); - NDLZ_ERROR_NULL(output); - NDLZ_ERROR_NULL(dparams); - BLOSC_UNUSED_PARAM(chunk); - - switch (meta) { - case 4: - return ndlz4_decompress(input, input_len, output, output_len, meta, dparams); - case 8: - return ndlz8_decompress(input, input_len, output, output_len, meta, dparams); - default: - printf("\n NDLZ is not available for this cellsize \n"); - return 0; - } + uint8_t meta, blosc2_dparams *dparams, const void *chunk) { + NDLZ_ERROR_NULL(input); + NDLZ_ERROR_NULL(output); + NDLZ_ERROR_NULL(dparams); + BLOSC_UNUSED_PARAM(chunk); + + switch (meta) { + case 4: + return ndlz4_decompress(input, input_len, output, output_len, meta, dparams); + case 8: + return ndlz8_decompress(input, input_len, output, output_len, meta, dparams); + default: + BLOSC_TRACE_ERROR("NDLZ is not available for this cellsize: %d", meta); + } + return BLOSC2_ERROR_FAILURE; } - - - - - diff --git a/plugins/codecs/ndlz/ndlz.h b/plugins/codecs/ndlz/ndlz.h index a9934d88..ed3efbdb 100644 --- a/plugins/codecs/ndlz/ndlz.h +++ b/plugins/codecs/ndlz/ndlz.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -19,10 +19,10 @@ extern "C" { #endif int ndlz_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int32_t output_len, - uint8_t meta, blosc2_cparams *cparams, const void* chunk); + uint8_t meta, blosc2_cparams *cparams, const void* chunk); int ndlz_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, int32_t output_len, - uint8_t meta, blosc2_dparams *dparams, const void* chunk); + uint8_t meta, blosc2_dparams *dparams, const void* chunk); #if defined (__cplusplus) } diff --git a/plugins/codecs/ndlz/ndlz4x4.c b/plugins/codecs/ndlz/ndlz4x4.c index e75c61d4..062472e3 100644 --- a/plugins/codecs/ndlz/ndlz4x4.c +++ b/plugins/codecs/ndlz/ndlz4x4.c @@ -1,10 +1,9 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Author: Francesc Alted - Author: Oscar Griñón - Author: Aleix Alcacer - Creation date: 2020-06-12 + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ @@ -47,59 +46,60 @@ #ifdef BLOSC_STRICT_ALIGN - #define NDLZ_READU16(p) ((p)[0] | (p)[1]<<8) - #define NDLZ_READU32(p) ((p)[0] | (p)[1]<<8 | (p)[2]<<16 | (p)[3]<<24) +#define NDLZ_READU16(p) ((p)[0] | (p)[1]<<8) +#define NDLZ_READU32(p) ((p)[0] | (p)[1]<<8 | (p)[2]<<16 | (p)[3]<<24) #else - #define NDLZ_READU16(p) *((const uint16_t*)(p)) - #define NDLZ_READU32(p) *((const uint32_t*)(p)) +#define NDLZ_READU16(p) *((const uint16_t*)(p)) +#define NDLZ_READU32(p) *((const uint32_t*)(p)) #endif #define HASH_LOG (12) int ndlz4_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int32_t output_len, - uint8_t meta, blosc2_cparams *cparams) { + uint8_t meta, blosc2_cparams *cparams) { BLOSC_UNUSED_PARAM(meta); - - int8_t ndim; - int64_t* shape = malloc(8 * sizeof(int64_t)); - int32_t* chunkshape = malloc(8 * sizeof(int32_t)); - int32_t* blockshape = malloc(8 * sizeof(int32_t)); - uint8_t* smeta; + uint8_t *smeta; int32_t smeta_len; - if (blosc2_meta_get(cparams->schunk, "caterva", &smeta, &smeta_len) < 0) { - printf("Blosc error"); - return -1; + + if (blosc2_meta_get(cparams->schunk, "b2nd", &smeta, &smeta_len) < 0) { + BLOSC_TRACE_ERROR("b2nd layer not found!"); + return BLOSC2_ERROR_FAILURE; } - deserialize_meta(smeta, smeta_len, &ndim, shape, chunkshape, blockshape); - free(smeta); + + int8_t ndim; + int64_t *shape = malloc(8 * sizeof(int64_t)); + int32_t *chunkshape = malloc(8 * sizeof(int32_t)); + int32_t *blockshape = malloc(8 * sizeof(int32_t)); + deserialize_meta(smeta, smeta_len, &ndim, shape, chunkshape, blockshape); + free(smeta); if (ndim != 2) { - fprintf(stderr, "This codec only works for ndim = 2"); - return -1; + BLOSC_TRACE_ERROR("This codec only works for ndim = 2"); + return BLOSC2_ERROR_FAILURE; } if (input_len != (blockshape[0] * blockshape[1])) { - printf("Length not equal to blocksize \n"); - return -1; + BLOSC_TRACE_ERROR("Length not equal to blocksize"); + return BLOSC2_ERROR_FAILURE; } if (NDLZ_UNEXPECT_CONDITIONAL(output_len < (int) (1 + ndim * sizeof(int32_t)))) { - printf("Output too small \n"); - return -1; + BLOSC_TRACE_ERROR("Output too small"); + return BLOSC2_ERROR_FAILURE; } - uint8_t* ip = (uint8_t *) input; - uint8_t* op = (uint8_t *) output; - uint8_t* op_limit; + uint8_t *ip = (uint8_t *) input; + uint8_t *op = (uint8_t *) output; + uint8_t *op_limit; uint32_t hval, hash_cell; uint32_t hash_triple[2] = {0}; uint32_t hash_pair[3] = {0}; uint8_t bufarea[16]; - uint8_t* buf_cell = bufarea; + uint8_t *buf_cell = bufarea; uint8_t buf_triple[12]; uint8_t buf_pair[8]; - uint8_t* buf_aux; + uint8_t *buf_aux; uint32_t tab_cell[1U << 12U] = {0}; uint32_t tab_triple[1U << 12U] = {0}; uint32_t tab_pair[1U << 12U] = {0}; @@ -119,11 +119,11 @@ int ndlz4_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int /* input and output buffer cannot be less than 16 and 66 bytes or we can get into trouble */ int overhead = 17 + (blockshape[0] * blockshape[1] / 16 - 1) * 2; if (input_len < 16 || output_len < overhead) { - printf("Incorrect length or maxout"); + BLOSC_TRACE_ERROR("Incorrect length or maxout"); return 0; } - uint8_t* obase = op; + uint8_t *obase = op; /* we start with literal copy */ *op++ = ndim; @@ -157,7 +157,8 @@ int ndlz4_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int } uint32_t orig = ii[0] * 4 * blockshape[1] + ii[1] * 4; - if (((blockshape[0] % 4 != 0) && (ii[0] == i_stop[0] - 1)) || ((blockshape[1] % 4 != 0) && (ii[1] == i_stop[1] - 1))) { + if (((blockshape[0] % 4 != 0) && (ii[0] == i_stop[0] - 1)) || + ((blockshape[1] % 4 != 0) && (ii[1] == i_stop[1] - 1))) { token = 0; // padding -> literal copy *op++ = token; if (ii[0] == i_stop[0] - 1) { @@ -174,8 +175,7 @@ int ndlz4_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int memcpy(op, &ip[orig + i * blockshape[1]], padding[1]); op += padding[1]; } - } - else { + } else { for (uint64_t i = 0; i < 4; i++) { // fill cell buffer uint64_t ind = orig + i * blockshape[1]; memcpy(buf_cell, &ip[ind], 4); @@ -183,9 +183,9 @@ int ndlz4_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int } buf_cell -= 16; - const uint8_t* ref; + const uint8_t *ref; uint32_t distance; - uint8_t* anchor = op; /* comparison starting-point */ + uint8_t *anchor = op; /* comparison starting-point */ /* find potential match */ hash_cell = XXH32(buf_cell, 16, 1); // calculate cell hash @@ -198,7 +198,7 @@ int ndlz4_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int } else { bool same = true; buf_aux = obase + tab_cell[hash_cell]; - for(int i = 0; i < 16; i++){ + for (int i = 0; i < 16; i++) { if (buf_cell[i] != buf_aux[i]) { same = false; break; @@ -301,7 +301,7 @@ int ndlz4_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int } // rows triples - for(int i = 0; i < 2; i++) { + for (int i = 0; i < 2; i++) { memcpy(buf_triple, &buf_cell[i * 4], 4); for (int j = i + 1; j < 3; j++) { memcpy(&buf_triple[4], &buf_cell[j * 4], 4); @@ -358,7 +358,7 @@ int ndlz4_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int } // rows pairs - for(int i = 0; i < 3; i++) { + for (int i = 0; i < 3; i++) { memcpy(buf_pair, &buf_cell[i * 4], 4); for (int j = i + 1; j < 4; j++) { memcpy(&buf_pair[4], &buf_cell[j * 4], 4); @@ -370,8 +370,8 @@ int ndlz4_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int uint16_t offset; if (tab_pair[hval] != 0) { buf_aux = obase + tab_pair[hval]; - for(int k = 0; k < 8; k++){ - if(buf_pair[k] != buf_aux[k]) { + for (int k = 0; k < 8; k++) { + if (buf_pair[k] != buf_aux[k]) { same = false; break; } @@ -392,7 +392,7 @@ int ndlz4_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int if ((distance != 0) && (distance < MAX_DISTANCE)) { /* rows pair match */ literal = false; if (i == 2) { - token = (uint8_t) (1U << 7U); + token = (uint8_t) (1U << 7U); } else { token = (uint8_t) ((1U << 7U) | (i << 5U) | (j << 3U)); } @@ -430,7 +430,7 @@ int ndlz4_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int } } else { // cell match - token = (uint8_t )((1U << 7U) | (1U << 6U)); + token = (uint8_t) ((1U << 7U) | (1U << 6U)); *op++ = token; uint16_t offset = (uint16_t) (anchor - obase - tab_cell[hash_cell]); memcpy(op, &offset, 2); @@ -438,8 +438,8 @@ int ndlz4_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int } } - if((op - obase) > input_len) { - printf("Compressed data is bigger than input! \n"); + if ((op - obase) > input_len) { + BLOSC_TRACE_ERROR("Compressed data is bigger than input!"); return 0; } } @@ -449,7 +449,7 @@ int ndlz4_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int free(chunkshape); free(blockshape); - return (int)(op - obase); + return (int) (op - obase); } @@ -510,17 +510,17 @@ static unsigned char* copy_match_16(unsigned char *op, const unsigned char *matc int ndlz4_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, int32_t output_len, - uint8_t meta, blosc2_dparams *dparams) { + uint8_t meta, blosc2_dparams *dparams) { BLOSC_UNUSED_PARAM(meta); BLOSC_UNUSED_PARAM(dparams); - uint8_t* ip = (uint8_t*)input; - uint8_t* ip_limit = ip + input_len; - uint8_t* op = (uint8_t*)output; + uint8_t *ip = (uint8_t *) input; + uint8_t *ip_limit = ip + input_len; + uint8_t *op = (uint8_t *) output; uint8_t ndim; uint32_t blockshape[2]; uint32_t eshape[2]; - uint8_t* buffercpy; + uint8_t *buffercpy; uint8_t local_buffer[16]; uint8_t token; if (NDLZ_UNEXPECT_CONDITIONAL(input_len < 8)) { @@ -529,10 +529,10 @@ int ndlz4_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, i /* we start with literal copy */ ndim = *ip; - ip ++; + ip++; if (ndim != 2) { - fprintf(stderr, "This codec only works for ndim = 2"); - return -1; + BLOSC_TRACE_ERROR("This codec only works for ndim = 2"); + return BLOSC2_ERROR_FAILURE; } memcpy(&blockshape[0], ip, 4); ip += 4; @@ -541,7 +541,7 @@ int ndlz4_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, i eshape[0] = ((blockshape[0] + 3) / 4) * 4; eshape[1] = ((blockshape[1] + 3) / 4) * 4; - if (NDLZ_UNEXPECT_CONDITIONAL(output_len < (int32_t)(blockshape[0] * blockshape[1]))) { + if (NDLZ_UNEXPECT_CONDITIONAL(output_len < (int32_t) (blockshape[0] * blockshape[1]))) { return 0; } memset(op, 0, blockshape[0] * blockshape[1]); @@ -551,7 +551,6 @@ int ndlz4_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, i i_stop[i] = eshape[i] / 4; } - /* main loop */ uint32_t ii[2]; uint32_t padding[2] = {0}; @@ -560,8 +559,8 @@ int ndlz4_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, i for (ii[0] = 0; ii[0] < i_stop[0]; ++ii[0]) { for (ii[1] = 0; ii[1] < i_stop[1]; ++ii[1]) { // for each cell if (NDLZ_UNEXPECT_CONDITIONAL(ip > ip_limit)) { - printf("Literal copy \n"); - return 0; + BLOSC_TRACE_ERROR("Exceeding input length"); + return BLOSC2_ERROR_FAILURE; } if (ii[0] == i_stop[0] - 1) { padding[0] = (blockshape[0] % 4 == 0) ? 4 : blockshape[0] % 4; @@ -574,20 +573,20 @@ int ndlz4_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, i padding[1] = 4; } token = *ip++; - if (token == 0){ // no match + if (token == 0) { // no match buffercpy = ip; ip += padding[0] * padding[1]; - } else if (token == (uint8_t)((1U << 7U) | (1U << 6U))) { // cell match - uint16_t offset = *((uint16_t*) ip); + } else if (token == (uint8_t) ((1U << 7U) | (1U << 6U))) { // cell match + uint16_t offset = *((uint16_t *) ip); buffercpy = ip - offset - 1; ip += 2; - } else if (token == (uint8_t)(1U << 6U)) { // whole cell of same element + } else if (token == (uint8_t) (1U << 6U)) { // whole cell of same element buffercpy = cell_aux; memset(buffercpy, *ip, 16); ip++; } else if (token >= 224) { // three rows match buffercpy = local_buffer; - uint16_t offset = *((uint16_t*) ip); + uint16_t offset = *((uint16_t *) ip); offset += 3; ip += 2; int i, j, k; @@ -620,9 +619,9 @@ int ndlz4_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, i } } - } else if ((token >= 128) && (token <= 191)){ // rows pair match + } else if ((token >= 128) && (token <= 191)) { // rows pair match buffercpy = local_buffer; - uint16_t offset = *((uint16_t*) ip); + uint16_t offset = *((uint16_t *) ip); offset += 3; ip += 2; int i, j; @@ -643,10 +642,10 @@ int ndlz4_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, i } } else if ((token >= 40) && (token <= 63)) { // 2 rows pair matches buffercpy = local_buffer; - uint16_t offset_1 = *((uint16_t*) ip); + uint16_t offset_1 = *((uint16_t *) ip); offset_1 += 5; ip += 2; - uint16_t offset_2 = *((uint16_t*) ip); + uint16_t offset_2 = *((uint16_t *) ip); offset_2 += 5; ip += 2; int i, j, k, l, m; @@ -668,8 +667,8 @@ int ndlz4_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, i memcpy(&buffercpy[m * 4], ip - offset_2 + 4, 4); } else { - printf("Invalid token: %u at cell [%d, %d]\n", token, ii[0], ii[1]); - return 0; + BLOSC_TRACE_ERROR("Invalid token: %u at cell [%d, %d]\n", token, ii[0], ii[1]); + return BLOSC2_ERROR_FAILURE; } // fill op with buffercpy uint32_t orig = ii[0] * 4 * blockshape[1] + ii[1] * 4; @@ -681,21 +680,21 @@ int ndlz4_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, i buffercpy += padding[1]; } if (ind > (uint32_t) output_len) { - printf("Output size is bigger than max \n"); - return 0; + BLOSC_TRACE_ERROR("Exceeding output size"); + return BLOSC2_ERROR_FAILURE; } } } ind += padding[1]; if (ind != (blockshape[0] * blockshape[1])) { - printf("Output size is not compatible with embedded blockshape \n"); - return 0; + BLOSC_TRACE_ERROR("Output size is not compatible with embedded blockshape"); + return BLOSC2_ERROR_FAILURE; } if (ind > (uint32_t) output_len) { - printf("Output size is bigger than max \n"); - return 0; + BLOSC_TRACE_ERROR("Exceeding output size"); + return BLOSC2_ERROR_FAILURE; } - return (int)ind; + return (int) ind; } diff --git a/plugins/codecs/ndlz/ndlz4x4.h b/plugins/codecs/ndlz/ndlz4x4.h index 7a477d5a..cefd85c8 100644 --- a/plugins/codecs/ndlz/ndlz4x4.h +++ b/plugins/codecs/ndlz/ndlz4x4.h @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -63,7 +63,7 @@ int ndlz4_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int */ int ndlz4_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, int32_t output_len, - uint8_t meta, blosc2_dparams *dparams); + uint8_t meta, blosc2_dparams *dparams); #if defined (__cplusplus) } diff --git a/plugins/codecs/ndlz/ndlz8x8.c b/plugins/codecs/ndlz/ndlz8x8.c index 06d0e864..6949184d 100644 --- a/plugins/codecs/ndlz/ndlz8x8.c +++ b/plugins/codecs/ndlz/ndlz8x8.c @@ -1,10 +1,9 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Author: Francesc Alted - Author: Oscar Griñón - Author: Aleix Alcacer - Creation date: 2020-06-12 + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ @@ -47,11 +46,11 @@ #ifdef BLOSC_STRICT_ALIGN - #define NDLZ_READU16(p) ((p)[0] | (p)[1]<<8) - #define NDLZ_READU32(p) ((p)[0] | (p)[1]<<8 | (p)[2]<<16 | (p)[3]<<24) +#define NDLZ_READU16(p) ((p)[0] | (p)[1]<<8) +#define NDLZ_READU32(p) ((p)[0] | (p)[1]<<8 | (p)[2]<<16 | (p)[3]<<24) #else - #define NDLZ_READU16(p) *((const uint16_t*)(p)) - #define NDLZ_READU32(p) *((const uint32_t*)(p)) +#define NDLZ_READU16(p) *((const uint16_t*)(p)) +#define NDLZ_READU32(p) *((const uint32_t*)(p)) #endif #define HASH_LOG (12) @@ -60,46 +59,47 @@ int ndlz8_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int32_t output_len, uint8_t meta, blosc2_cparams *cparams) { BLOSC_UNUSED_PARAM(meta); + uint8_t *smeta; + int32_t smeta_len; + + if (blosc2_meta_get(cparams->schunk, "b2nd", &smeta, &smeta_len) < 0) { + BLOSC_TRACE_ERROR("b2nd layer not found!"); + return BLOSC2_ERROR_FAILURE; + } const int cell_shape = 8; const int cell_size = 64; int8_t ndim; - int64_t* shape = malloc(8 * sizeof(int64_t)); - int32_t* chunkshape = malloc(8 * sizeof(int32_t)); - int32_t* blockshape = malloc(8 * sizeof(int32_t)); - uint8_t* smeta; - int32_t smeta_len; - if (blosc2_meta_get(cparams->schunk, "caterva", &smeta, &smeta_len) < 0) { - printf("Blosc error"); - return 0; - } + int64_t *shape = malloc(8 * sizeof(int64_t)); + int32_t *chunkshape = malloc(8 * sizeof(int32_t)); + int32_t *blockshape = malloc(8 * sizeof(int32_t)); deserialize_meta(smeta, smeta_len, &ndim, shape, chunkshape, blockshape); free(smeta); if (ndim != 2) { - fprintf(stderr, "This codec only works for ndim = 2"); - return -1; + BLOSC_TRACE_ERROR("This codec only works for ndim = 2"); + return BLOSC2_ERROR_FAILURE; } if (input_len != (blockshape[0] * blockshape[1])) { - printf("Length not equal to blocksize \n"); - return -1; + BLOSC_TRACE_ERROR("Length not equal to blocksize"); + return BLOSC2_ERROR_FAILURE; } if (NDLZ_UNEXPECT_CONDITIONAL(output_len < (int) (1 + ndim * sizeof(int32_t)))) { - printf("Output too small \n"); - return -1; + BLOSC_TRACE_ERROR("Output too small"); + return BLOSC2_ERROR_FAILURE; } - uint8_t* ip = (uint8_t *) input; - uint8_t* op = (uint8_t *) output; - uint8_t* op_limit; + uint8_t *ip = (uint8_t *) input; + uint8_t *op = (uint8_t *) output; + uint8_t *op_limit; uint32_t hval, hash_cell; uint32_t hash_triple[6] = {0}; uint32_t hash_pair[7] = {0}; - uint8_t* bufarea = malloc(cell_size); - uint8_t* buf_cell = bufarea; - uint8_t* buf_aux; + uint8_t *bufarea = malloc(cell_size); + uint8_t *buf_cell = bufarea; + uint8_t *buf_aux; uint32_t tab_cell[1U << 12U] = {0}; uint32_t tab_triple[1U << 12U] = {0}; uint32_t tab_pair[1U << 12U] = {0}; @@ -121,11 +121,11 @@ int ndlz8_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int /* input and output buffer cannot be less than 64 (cells are 8x8) */ int overhead = 17 + (blockshape[0] * blockshape[1] / cell_size - 1) * 2; if (input_len < cell_size || output_len < overhead) { - printf("Incorrect length or maxout"); + BLOSC_TRACE_ERROR("Incorrect length or maxout"); return 0; } - uint8_t* obase = op; + uint8_t *obase = op; /* we start with literal copy */ *op++ = ndim; @@ -179,8 +179,7 @@ int ndlz8_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int memcpy(op, &ip[orig + i * blockshape[1]], padding[1]); op += padding[1]; } - } - else { + } else { for (uint64_t i = 0; i < (uint64_t) cell_shape; i++) { // fill cell buffer uint64_t ind = orig + i * blockshape[1]; memcpy(buf_cell, &ip[ind], cell_shape); @@ -188,9 +187,9 @@ int ndlz8_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int } buf_cell -= cell_size; - const uint8_t* ref; + const uint8_t *ref; uint32_t distance; - uint8_t* anchor = op; /* comparison starting-point */ + uint8_t *anchor = op; /* comparison starting-point */ /* find potential match */ hash_cell = XXH32(buf_cell, cell_size, 1); // calculate cell hash @@ -203,7 +202,7 @@ int ndlz8_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int } else { bool same = true; buf_aux = obase + tab_cell[hash_cell]; - for(int i = 0; i < cell_size; i++){ + for (int i = 0; i < cell_size; i++) { if (buf_cell[i] != buf_aux[i]) { same = false; break; @@ -342,7 +341,7 @@ int ndlz8_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int } } else { // cell match - uint8_t token = (uint8_t)((1U << 7U) | (1U << 6U)); + uint8_t token = (uint8_t) ((1U << 7U) | (1U << 6U)); *op++ = token; uint16_t offset = (uint16_t) (anchor - obase - tab_cell[hash_cell]); memcpy(op, &offset, 2); @@ -351,11 +350,12 @@ int ndlz8_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int } } - if((op - obase) > input_len) { + if ((op - obase) > input_len) { free(shape); free(chunkshape); free(blockshape); free(bufarea); + BLOSC_TRACE_ERROR("Compressed data is bigger than input!"); return 0; } } @@ -366,7 +366,7 @@ int ndlz8_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int free(blockshape); free(bufarea); - return (int)(op - obase); + return (int) (op - obase); } @@ -433,13 +433,13 @@ int ndlz8_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, i const int cell_shape = 8; const int cell_size = 64; - uint8_t* ip = (uint8_t*)input; - uint8_t* ip_limit = ip + input_len; - uint8_t* op = (uint8_t*)output; + uint8_t *ip = (uint8_t *) input; + uint8_t *ip_limit = ip + input_len; + uint8_t *op = (uint8_t *) output; uint8_t ndim; int32_t blockshape[2]; int32_t eshape[2]; - uint8_t* buffercpy; + uint8_t *buffercpy; uint8_t token; if (NDLZ_UNEXPECT_CONDITIONAL(input_len < 8)) { return 0; @@ -447,10 +447,10 @@ int ndlz8_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, i /* we start with literal copy */ ndim = *ip; - ip ++; + ip++; if (ndim != 2) { - fprintf(stderr, "This codec only works for ndim = 2"); - return -1; + BLOSC_TRACE_ERROR("This codec only works for ndim = 2"); + return BLOSC2_ERROR_FAILURE; } memcpy(&blockshape[0], ip, 4); ip += 4; @@ -469,20 +469,19 @@ int ndlz8_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, i i_stop[i] = eshape[i] / cell_shape; } - /* main loop */ int32_t ii[2]; int32_t padding[2] = {0}; int32_t ind = 0; - uint8_t* local_buffer = malloc(cell_size); - uint8_t* cell_aux = malloc(cell_size); + uint8_t *local_buffer = malloc(cell_size); + uint8_t *cell_aux = malloc(cell_size); for (ii[0] = 0; ii[0] < i_stop[0]; ++ii[0]) { for (ii[1] = 0; ii[1] < i_stop[1]; ++ii[1]) { // for each cell if (NDLZ_UNEXPECT_CONDITIONAL(ip > ip_limit)) { - printf("Literal copy \n"); free(local_buffer); free(cell_aux); - return 0; + BLOSC_TRACE_ERROR("Exceeding input length"); + return BLOSC2_ERROR_FAILURE; } if (ii[0] == i_stop[0] - 1) { padding[0] = (blockshape[0] % cell_shape == 0) ? cell_shape : blockshape[0] % cell_shape; @@ -496,21 +495,21 @@ int ndlz8_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, i } token = *ip++; uint8_t match_type = (token >> 3U); - if (token == 0){ // no match + if (token == 0) { // no match buffercpy = ip; ip += padding[0] * padding[1]; - } else if (token == (uint8_t)((1U << 7U) | (1U << 6U))) { // cell match - uint16_t offset = *((uint16_t*) ip); + } else if (token == (uint8_t) ((1U << 7U) | (1U << 6U))) { // cell match + uint16_t offset = *((uint16_t *) ip); buffercpy = ip - offset - 1; ip += 2; - } else if (token == (uint8_t)(1U << 6U)) { // whole cell of same element + } else if (token == (uint8_t) (1U << 6U)) { // whole cell of same element buffercpy = cell_aux; memset(buffercpy, *ip, cell_size); ip++; } else if (match_type == 21) { // triple match buffercpy = local_buffer; int row = (int) (token & 7); - uint16_t offset = *((uint16_t*) ip); + uint16_t offset = *((uint16_t *) ip); ip += 2; for (int l = 0; l < 3; l++) { memcpy(&buffercpy[(row + l) * cell_shape], @@ -525,7 +524,7 @@ int ndlz8_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, i } else if (match_type == 17) { // pair match buffercpy = local_buffer; int row = (int) (token & 7); - uint16_t offset = *((uint16_t*) ip); + uint16_t offset = *((uint16_t *) ip); ip += 2; for (int l = 0; l < 2; l++) { memcpy(&buffercpy[(row + l) * cell_shape], @@ -538,13 +537,13 @@ int ndlz8_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, i } } } else { - printf("Invalid token: %u at cell [%d, %d]\n", token, ii[0], ii[1]); free(local_buffer); free(cell_aux); - return 0; + BLOSC_TRACE_ERROR("Invalid token: %u at cell [%d, %d]\n", token, ii[0], ii[1]); + return BLOSC2_ERROR_FAILURE; } - uint32_t orig = ii[0] * cell_shape * blockshape[1] + ii[1] * cell_shape; + int32_t orig = ii[0] * cell_shape * blockshape[1] + ii[1] * cell_shape; for (int32_t i = 0; i < (int32_t) cell_shape; i++) { if (i < padding[0]) { ind = orig + i * blockshape[1]; @@ -553,10 +552,10 @@ int ndlz8_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, i buffercpy += padding[1]; } if (ind > output_len) { - printf("Output size is bigger than max \n"); free(local_buffer); free(cell_aux); - return 0; + BLOSC_TRACE_ERROR("Exceeding output size"); + return BLOSC2_ERROR_FAILURE; } } } @@ -566,13 +565,13 @@ int ndlz8_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, i free(local_buffer); if (ind != (blockshape[0] * blockshape[1])) { - printf("Output size is not compatible with embedded blockshape \n"); - return 0; + BLOSC_TRACE_ERROR("Output size is not compatible with embedded blockshape"); + return BLOSC2_ERROR_FAILURE; } if (ind > output_len) { - printf("Output size is bigger than max \n"); - return 0; + BLOSC_TRACE_ERROR("Exceeding output size"); + return BLOSC2_ERROR_FAILURE; } - return (int)ind; + return (int) ind; } diff --git a/plugins/codecs/ndlz/ndlz8x8.h b/plugins/codecs/ndlz/ndlz8x8.h index f7a98acc..d3fc107b 100644 --- a/plugins/codecs/ndlz/ndlz8x8.h +++ b/plugins/codecs/ndlz/ndlz8x8.h @@ -1,22 +1,22 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ - - #ifndef NDLZ8_H #define NDLZ8_H + #include "context.h" #if defined (__cplusplus) extern "C" { #endif + #include "ndlz.h" #include "ndlz-private.h" diff --git a/plugins/codecs/ndlz/test_ndlz.c b/plugins/codecs/ndlz/test_ndlz.c index 050b244d..efff0194 100644 --- a/plugins/codecs/ndlz/test_ndlz.c +++ b/plugins/codecs/ndlz/test_ndlz.c @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -34,206 +34,295 @@ #include "blosc2.h" #include "blosc2/codecs-registry.h" #include - -static int test_ndlz_4(blosc2_schunk* schunk) { - - int64_t nchunks = schunk->nchunks; - int32_t chunksize = schunk->chunksize; - uint8_t *data_in = malloc(chunksize); - int decompressed; - int64_t csize; - int64_t dsize; - int64_t csize_f = 0; - uint8_t *data_out = malloc(chunksize + BLOSC2_MAX_OVERHEAD); - uint8_t *data_dest = malloc(chunksize); - - /* Create a context for compression */ - blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; - cparams.splitmode = BLOSC_ALWAYS_SPLIT; - cparams.typesize = schunk->typesize; - cparams.compcode = BLOSC_CODEC_NDLZ; - cparams.compcode_meta = 4; - cparams.filters[BLOSC2_MAX_FILTERS - 1] = BLOSC_SHUFFLE; - cparams.clevel = 5; - cparams.nthreads = 1; - cparams.blocksize = schunk->blocksize; - cparams.schunk = schunk; - blosc2_context *cctx; - cctx = blosc2_create_cctx(cparams); - - blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; - dparams.nthreads = 1; - blosc2_context *dctx; - dctx = blosc2_create_dctx(dparams); - - for (int ci = 0; ci < nchunks; ci++) { - - decompressed = blosc2_schunk_decompress_chunk(schunk, ci, data_in, chunksize); - if (decompressed < 0) { - printf("Error decompressing chunk \n"); - return -1; - } - - /* Compress with clevel=5 and shuffle active */ - csize = blosc2_compress_ctx(cctx, data_in, chunksize, data_out, chunksize + BLOSC2_MAX_OVERHEAD); - if (csize == 0) { - printf("Buffer is incompressible. Giving up.\n"); - return 0; - } else if (csize < 0) { - printf("Compression error. Error code: %" PRId64 "\n", csize); - return (int) csize; - } - csize_f += csize; - - - /* Decompress */ - dsize = blosc2_decompress_ctx(dctx, data_out, chunksize + BLOSC2_MAX_OVERHEAD, data_dest, chunksize); - if (dsize <= 0) { - printf("Decompression error. Error code: %" PRId64 "\n", dsize); - return (int) dsize; - } - - for (int i = 0; i < chunksize; i++) { - if (data_in[i] != data_dest[i]) { - printf("i: %d, data %u, dest %u", i, data_in[i], data_dest[i]); - printf("\n Decompressed data differs from original!\n"); - return -1; - } - } +#include "b2nd.h" + +static int test_ndlz_4(blosc2_schunk *schunk) { + + int64_t nchunks = schunk->nchunks; + int32_t chunksize = schunk->chunksize; + uint8_t *data_in = malloc(chunksize); + int decompressed; + int64_t csize; + int64_t dsize; + int64_t csize_f = 0; + uint8_t *data_out = malloc(chunksize + BLOSC2_MAX_OVERHEAD); + uint8_t *data_dest = malloc(chunksize); + + /* Create a context for compression */ + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.splitmode = BLOSC_ALWAYS_SPLIT; + cparams.typesize = schunk->typesize; + cparams.compcode = BLOSC_CODEC_NDLZ; + cparams.compcode_meta = 4; + cparams.filters[BLOSC2_MAX_FILTERS - 1] = BLOSC_SHUFFLE; + cparams.clevel = 5; + cparams.nthreads = 1; + cparams.blocksize = schunk->blocksize; + cparams.schunk = schunk; + blosc2_context *cctx; + cctx = blosc2_create_cctx(cparams); + + blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; + dparams.nthreads = 1; + blosc2_context *dctx; + dctx = blosc2_create_dctx(dparams); + + for (int ci = 0; ci < nchunks; ci++) { + + decompressed = blosc2_schunk_decompress_chunk(schunk, ci, data_in, chunksize); + if (decompressed < 0) { + printf("Error decompressing chunk \n"); + return -1; } - csize_f = csize_f / nchunks; - free(data_in); - free(data_out); - free(data_dest); - blosc2_free_ctx(cctx); - blosc2_free_ctx(dctx); + /* Compress with clevel=5 and shuffle active */ + csize = blosc2_compress_ctx(cctx, data_in, chunksize, data_out, chunksize + BLOSC2_MAX_OVERHEAD); + if (csize == 0) { + printf("Buffer is incompressible. Giving up.\n"); + return -1; + } else if (csize < 0) { + printf("Compression error. Error code: %" PRId64 "\n", csize); + return (int) csize; + } + csize_f += csize; - printf("Successful roundtrip!\n"); - printf("Compression: %d -> %" PRId64 " (%.1fx)\n", chunksize, csize_f, (1. * chunksize) / (double)csize_f); - return (int) (chunksize - csize_f); -} -static int test_ndlz_8(blosc2_schunk* schunk) { - - int64_t nchunks = schunk->nchunks; - int32_t chunksize = (int32_t) (schunk->chunksize); - // int isize = (int) array->extchunknitems * typesize; - uint8_t *data_in = malloc(chunksize); - int decompressed; - int64_t csize; - int64_t dsize; - int64_t csize_f = 0; - uint8_t *data_out = malloc(chunksize + BLOSC2_MAX_OVERHEAD); - uint8_t *data_dest = malloc(chunksize); - - /* Create a context for compression */ - blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; - cparams.splitmode = BLOSC_ALWAYS_SPLIT; - cparams.typesize = schunk->typesize; - cparams.compcode = BLOSC_CODEC_NDLZ; - cparams.compcode_meta = 8; - cparams.filters[BLOSC2_MAX_FILTERS - 1] = BLOSC_SHUFFLE; - cparams.clevel = 5; - cparams.nthreads = 1; - cparams.blocksize = schunk->blocksize; - cparams.schunk = schunk; - blosc2_context *cctx; - cctx = blosc2_create_cctx(cparams); - - blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; - dparams.nthreads = 1; - blosc2_context *dctx; - dctx = blosc2_create_dctx(dparams); - - for (int ci = 0; ci < nchunks; ci++) { - - decompressed = blosc2_schunk_decompress_chunk(schunk, ci, data_in, chunksize); - if (decompressed < 0) { - printf("Error decompressing chunk \n"); - return -1; - } - - /* Compress with clevel=5 and shuffle active */ - csize = blosc2_compress_ctx(cctx, data_in, chunksize, data_out, chunksize + BLOSC2_MAX_OVERHEAD); - if (csize == 0) { - printf("Buffer is incompressible. Giving up.\n"); - return 0; - } else if (csize < 0) { - printf("Compression error. Error code: %" PRId64 "\n", csize); - return (int) csize; - } - csize_f += csize; - - /* Decompress */ - dsize = blosc2_decompress_ctx(dctx, data_out, chunksize + BLOSC2_MAX_OVERHEAD, data_dest, chunksize); - if (dsize <= 0) { - printf("Decompression error. Error code: %" PRId64 "\n", dsize); - return (int) dsize; - } - - for (int i = 0; i < chunksize; i++) { - if (data_in[i] != data_dest[i]) { - printf("i: %d, data %u, dest %u", i, data_in[i], data_dest[i]); - printf("\n Decompressed data differs from original!\n"); - return -1; - } - } + /* Decompress */ + dsize = blosc2_decompress_ctx(dctx, data_out, chunksize + BLOSC2_MAX_OVERHEAD, data_dest, chunksize); + if (dsize <= 0) { + printf("Decompression error. Error code: %" PRId64 "\n", dsize); + return (int) dsize; } - csize_f = csize_f / nchunks; - free(data_in); - free(data_out); - free(data_dest); - blosc2_free_ctx(cctx); - blosc2_free_ctx(dctx); - - printf("Successful roundtrip!\n"); - printf("Compression: %d -> %" PRId64 " (%.1fx)\n", chunksize, csize_f, (1. * chunksize) / (double)csize_f); - return (int) (chunksize - csize_f); + for (int i = 0; i < chunksize; i++) { + if (data_in[i] != data_dest[i]) { + printf("i: %d, data %u, dest %u", i, data_in[i], data_dest[i]); + printf("\n Decompressed data differs from original!\n"); + return -1; + } + } + } + csize_f = csize_f / nchunks; + + free(data_in); + free(data_out); + free(data_dest); + blosc2_free_ctx(cctx); + blosc2_free_ctx(dctx); + + printf("Successful roundtrip!\n"); + printf("Compression: %d -> %" PRId64 " (%.1fx)\n", chunksize, csize_f, (1. * chunksize) / (double) csize_f); + return (int) (chunksize - csize_f); } +static int test_ndlz_8(blosc2_schunk *schunk) { + + int64_t nchunks = schunk->nchunks; + int32_t chunksize = (int32_t) (schunk->chunksize); + // int isize = (int) array->extchunknitems * typesize; + uint8_t *data_in = malloc(chunksize); + int decompressed; + int64_t csize; + int64_t dsize; + int64_t csize_f = 0; + uint8_t *data_out = malloc(chunksize + BLOSC2_MAX_OVERHEAD); + uint8_t *data_dest = malloc(chunksize); + + /* Create a context for compression */ + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.splitmode = BLOSC_ALWAYS_SPLIT; + cparams.typesize = schunk->typesize; + cparams.compcode = BLOSC_CODEC_NDLZ; + cparams.compcode_meta = 8; + cparams.filters[BLOSC2_MAX_FILTERS - 1] = BLOSC_SHUFFLE; + cparams.clevel = 5; + cparams.nthreads = 1; + cparams.blocksize = schunk->blocksize; + cparams.schunk = schunk; + blosc2_context *cctx; + cctx = blosc2_create_cctx(cparams); + + blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; + dparams.nthreads = 1; + blosc2_context *dctx; + dctx = blosc2_create_dctx(dparams); + + for (int ci = 0; ci < nchunks; ci++) { + + decompressed = blosc2_schunk_decompress_chunk(schunk, ci, data_in, chunksize); + if (decompressed < 0) { + printf("Error decompressing chunk \n"); + return -1; + } -int same_cells() { - blosc2_schunk *schunk = blosc2_schunk_open("example_same_cells.caterva"); + /* Compress with clevel=5 and shuffle active */ + csize = blosc2_compress_ctx(cctx, data_in, chunksize, data_out, chunksize + BLOSC2_MAX_OVERHEAD); + if (csize == 0) { + printf("Buffer is incompressible. Giving up.\n"); + return -1; + } else if (csize < 0) { + printf("Compression error. Error code: %" PRId64 "\n", csize); + return (int) csize; + } + csize_f += csize; - /* Run the test. */ - int result = test_ndlz_4(schunk); - if (result < 0) { - blosc2_schunk_free(schunk); - return result; + /* Decompress */ + dsize = blosc2_decompress_ctx(dctx, data_out, chunksize + BLOSC2_MAX_OVERHEAD, data_dest, chunksize); + if (dsize <= 0) { + printf("Decompression error. Error code: %" PRId64 "\n", dsize); + return (int) dsize; } - result = test_ndlz_8(schunk); - blosc2_schunk_free(schunk); - return result; + for (int i = 0; i < chunksize; i++) { + if (data_in[i] != data_dest[i]) { + printf("i: %d, data %u, dest %u", i, data_in[i], data_dest[i]); + printf("\n Decompressed data differs from original!\n"); + return -1; + } + } + } + csize_f = csize_f / nchunks; + + free(data_in); + free(data_out); + free(data_dest); + blosc2_free_ctx(cctx); + blosc2_free_ctx(dctx); + + printf("Successful roundtrip!\n"); + printf("Compression: %d -> %" PRId64 " (%.1fx)\n", chunksize, csize_f, (1. * chunksize) / (double) csize_f); + return (int) (chunksize - csize_f); } -int some_matches() { - blosc2_schunk *schunk = blosc2_schunk_open("example_some_matches.caterva"); - /* Run the test. */ - int result = test_ndlz_4(schunk); - if (result < 0) { - blosc2_schunk_free(schunk); - return result; - } - result = test_ndlz_8(schunk); - blosc2_schunk_free(schunk); +int rand_() { + int ndim = 2; + int typesize = 4; + int64_t shape[] = {32, 18}; + int32_t chunkshape[] = {17, 16}; + int32_t blockshape[] = {8, 9}; + int64_t nelem = 1; + for (int i = 0; i < ndim; ++i) { + nelem *= (int) (shape[i]); + } + int64_t size = typesize * nelem; + float *data = malloc(size); + for (int64_t i = 0; i < nelem; i++) { + data[i] = (float) (rand() % 220); + } + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.typesize = typesize; + blosc2_storage b2_storage = {.cparams=&cparams}; + b2_storage.contiguous = true; + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, ndim, shape, chunkshape, blockshape, NULL, 0, + NULL, 0); + + b2nd_array_t *arr; + BLOSC_ERROR(b2nd_from_cbuffer(ctx, &arr, data, size)); + blosc2_schunk *schunk = arr->sc; + + /* Run the test. */ + int result = test_ndlz_4(schunk); + BLOSC_ERROR(b2nd_free_ctx(ctx)); + BLOSC_ERROR(b2nd_free(arr)); + return result; +} - return result; +int same_cells() { + int ndim = 2; + int typesize = 8; + int64_t shape[] = {128, 111}; + int32_t chunkshape[] = {32, 11}; + int32_t blockshape[] = {16, 7}; + int64_t nelem = 1; + for (int i = 0; i < ndim; ++i) { + nelem *= (int) (shape[i]); + } + int64_t size = typesize * nelem; + double *data = malloc(size); + for (int64_t i = 0; i < (nelem / 4); i++) { + data[i * 4] = (double) 11111111; + data[i * 4 + 1] = (double) 99999999; + } + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.typesize = typesize; + blosc2_storage b2_storage = {.cparams=&cparams}; + b2_storage.contiguous = true; + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, ndim, shape, chunkshape, blockshape, NULL, 0, + NULL, 0); + + b2nd_array_t *arr; + BLOSC_ERROR(b2nd_from_cbuffer(ctx, &arr, data, size)); + blosc2_schunk *schunk = arr->sc; + + /* Run the test. */ + int result = test_ndlz_4(schunk); + BLOSC_ERROR(b2nd_free_ctx(ctx)); + BLOSC_ERROR(b2nd_free(arr)); + return result; +} + +int some_matches() { + int ndim = 2; + int typesize = 8; + int64_t shape[] = {128, 111}; + int32_t chunkshape[] = {48, 32}; + int32_t blockshape[] = {14, 18}; + int64_t nelem = 1; + for (int i = 0; i < ndim; ++i) { + nelem *= (int) (shape[i]); + } + int64_t size = typesize * nelem; + double *data = malloc(size); + for (int64_t i = 0; i < (nelem / 2); i++) { + data[i] = (double) i; + } + for (int64_t i = (nelem / 2); i < nelem; i++) { + data[i] = (double) 1; + } + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.typesize = typesize; + blosc2_storage b2_storage = {.cparams=&cparams}; + b2_storage.contiguous = true; + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, ndim, shape, chunkshape, blockshape, NULL, 0, + NULL, 0); + + b2nd_array_t *arr; + BLOSC_ERROR(b2nd_from_cbuffer(ctx, &arr, data, size)); + blosc2_schunk *schunk = arr->sc; + + /* Run the test. */ + int result = test_ndlz_8(schunk); + BLOSC_ERROR(b2nd_free_ctx(ctx)); + BLOSC_ERROR(b2nd_free(arr)); + return result; } int main(void) { - int result; + int result; blosc2_init(); // this is mandatory for initiallizing the plugin mechanism - result = same_cells(); - printf("same_cells: %d obtained \n \n", result); - result = some_matches(); - printf("some_matches: %d obtained \n \n", result); + result = rand_(); + printf("rand: %d obtained \n \n", result); + if (result < 0) + return result; + result = same_cells(); + printf("same_cells: %d obtained \n \n", result); + if (result < 0) + return result; + result = some_matches(); + if (result < 0) + return result; + printf("some_matches: %d obtained \n \n", result); blosc2_destroy(); - return BLOSC2_ERROR_SUCCESS; + return BLOSC2_ERROR_SUCCESS; + } diff --git a/plugins/codecs/ndlz/xxhash.h b/plugins/codecs/ndlz/xxhash.h index 08ab7945..e8a5a3c9 100644 --- a/plugins/codecs/ndlz/xxhash.h +++ b/plugins/codecs/ndlz/xxhash.h @@ -100,13 +100,13 @@ extern "C" { * Do not compile and link xxhash.o as a separate object, as it is not useful. */ #if (defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API)) \ - && !defined(XXH_INLINE_ALL_31684351384) - /* this section should be traversed only once */ + && !defined(XXH_INLINE_ALL_31684351384) +/* this section should be traversed only once */ # define XXH_INLINE_ALL_31684351384 - /* give access to the advanced API, required to compile implementations */ +/* give access to the advanced API, required to compile implementations */ # undef XXH_STATIC_LINKING_ONLY /* avoid macro redef */ # define XXH_STATIC_LINKING_ONLY - /* make all functions private */ +/* make all functions private */ # undef XXH_PUBLIC_API # if defined(__GNUC__) # define XXH_PUBLIC_API static __inline __attribute__((unused)) @@ -115,25 +115,25 @@ extern "C" { # elif defined(_MSC_VER) # define XXH_PUBLIC_API static __inline # else - /* note: this version may generate warnings for unused static functions */ + /* note: this version may generate warnings for unused static functions */ # define XXH_PUBLIC_API static # endif - /* - * This part deals with the special case where a unit wants to inline xxHash, - * but "xxhash.h" has previously been included without XXH_INLINE_ALL, - * such as part of some previously included *.h header file. - * Without further action, the new include would just be ignored, - * and functions would effectively _not_ be inlined (silent failure). - * The following macros solve this situation by prefixing all inlined names, - * avoiding naming collision with previous inclusions. - */ - /* Before that, we unconditionally #undef all symbols, - * in case they were already defined with XXH_NAMESPACE. - * They will then be redefined for XXH_INLINE_ALL - */ +/* + * This part deals with the special case where a unit wants to inline xxHash, + * but "xxhash.h" has previously been included without XXH_INLINE_ALL, + * such as part of some previously included *.h header file. + * Without further action, the new include would just be ignored, + * and functions would effectively _not_ be inlined (silent failure). + * The following macros solve this situation by prefixing all inlined names, + * avoiding naming collision with previous inclusions. + */ +/* Before that, we unconditionally #undef all symbols, + * in case they were already defined with XXH_NAMESPACE. + * They will then be redefined for XXH_INLINE_ALL + */ # undef XXH_versionNumber - /* XXH32 */ + /* XXH32 */ # undef XXH32 # undef XXH32_createState # undef XXH32_freeState @@ -143,7 +143,7 @@ extern "C" { # undef XXH32_copyState # undef XXH32_canonicalFromHash # undef XXH32_hashFromCanonical - /* XXH64 */ + /* XXH64 */ # undef XXH64 # undef XXH64_createState # undef XXH64_freeState @@ -153,7 +153,7 @@ extern "C" { # undef XXH64_copyState # undef XXH64_canonicalFromHash # undef XXH64_hashFromCanonical - /* XXH3_64bits */ + /* XXH3_64bits */ # undef XXH3_64bits # undef XXH3_64bits_withSecret # undef XXH3_64bits_withSeed @@ -167,7 +167,7 @@ extern "C" { # undef XXH3_64bits_update # undef XXH3_64bits_digest # undef XXH3_generateSecret - /* XXH3_128bits */ + /* XXH3_128bits */ # undef XXH128 # undef XXH3_128bits # undef XXH3_128bits_withSeed @@ -182,18 +182,18 @@ extern "C" { # undef XXH128_cmp # undef XXH128_canonicalFromHash # undef XXH128_hashFromCanonical - /* Finally, free the namespace itself */ + /* Finally, free the namespace itself */ # undef XXH_NAMESPACE - /* employ the namespace for XXH_INLINE_ALL */ + /* employ the namespace for XXH_INLINE_ALL */ # define XXH_NAMESPACE XXH_INLINE_ - /* - * Some identifiers (enums, type names) are not symbols, - * but they must nonetheless be renamed to avoid redeclaration. - * Alternative solution: do not redeclare them. - * However, this requires some #ifdefs, and has a more dispersed impact. - * Meanwhile, renaming can be achieved in a single place. - */ +/* + * Some identifiers (enums, type names) are not symbols, + * but they must nonetheless be renamed to avoid redeclaration. + * Alternative solution: do not redeclare them. + * However, this requires some #ifdefs, and has a more dispersed impact. + * Meanwhile, renaming can be achieved in a single place. + */ # define XXH_IPREF(Id) XXH_NAMESPACE ## Id # define XXH_OK XXH_IPREF(XXH_OK) # define XXH_ERROR XXH_IPREF(XXH_ERROR) @@ -208,7 +208,7 @@ extern "C" { # define XXH3_state_s XXH_IPREF(XXH3_state_s) # define XXH3_state_t XXH_IPREF(XXH3_state_t) # define XXH128_hash_t XXH_IPREF(XXH128_hash_t) - /* Ensure the header is parsed again, even if it was previously included */ +/* Ensure the header is parsed again, even if it was previously included */ # undef XXHASH_H_5627135585666179 # undef XXHASH_H_STATIC_13879238742 #endif /* XXH_INLINE_ALL || XXH_PRIVATE_API */ @@ -333,14 +333,17 @@ extern "C" { * * @return `XXH_VERSION_NUMBER` of the invoked library. */ -XXH_PUBLIC_API unsigned XXH_versionNumber (void); +XXH_PUBLIC_API unsigned XXH_versionNumber(void); /* **************************** * Common basic types ******************************/ #include /* size_t */ -typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode; + +typedef enum { + XXH_OK = 0, XXH_ERROR +} XXH_errorcode; /*-********************************************************************** @@ -355,18 +358,20 @@ typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode; typedef uint32_t XXH32_hash_t; #elif !defined (__VMS) \ - && (defined (__cplusplus) \ - || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)) + # include - typedef uint32_t XXH32_hash_t; + +typedef uint32_t XXH32_hash_t; #else # include # if UINT_MAX == 0xFFFFFFFFUL - typedef unsigned int XXH32_hash_t; +typedef unsigned int XXH32_hash_t; # else # if ULONG_MAX == 0xFFFFFFFFUL - typedef unsigned long XXH32_hash_t; + typedef unsigned long XXH32_hash_t; # else # error "unsupported platform: need a 32-bit type" # endif @@ -412,7 +417,7 @@ typedef uint32_t XXH32_hash_t; * @see * XXH32_createState(), XXH32_update(), XXH32_digest(): Streaming version. */ -XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, XXH32_hash_t seed); +XXH_PUBLIC_API XXH32_hash_t XXH32(const void *input, size_t length, XXH32_hash_t seed); /*! * Streaming functions generate the xxHash value from an incremental input. @@ -478,7 +483,7 @@ typedef struct XXH32_state_s XXH32_state_t; * Must be freed with XXH32_freeState(). * @return An allocated XXH32_state_t on success, `NULL` on failure. */ -XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void); +XXH_PUBLIC_API XXH32_state_t *XXH32_createState(void); /*! * @brief Frees an @ref XXH32_state_t. * @@ -486,7 +491,7 @@ XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void); * @param statePtr A pointer to an @ref XXH32_state_t allocated with @ref XXH32_createState(). * @return XXH_OK. */ -XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr); +XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t *statePtr); /*! * @brief Copies one @ref XXH32_state_t to another. * @@ -495,7 +500,7 @@ XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr); * @pre * @p dst_state and @p src_state must not be `NULL` and must not overlap. */ -XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state); +XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t *dst_state, const XXH32_state_t *src_state); /*! * @brief Resets an @ref XXH32_state_t to begin a new hash. @@ -510,7 +515,7 @@ XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_ * * @return @ref XXH_OK on success, @ref XXH_ERROR on failure. */ -XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, XXH32_hash_t seed); +XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t *statePtr, XXH32_hash_t seed); /*! * @brief Consumes a block of @p input to an @ref XXH32_state_t. @@ -530,7 +535,7 @@ XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, XXH32_hash_t * * @return @ref XXH_OK on success, @ref XXH_ERROR on failure. */ -XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length); +XXH_PUBLIC_API XXH_errorcode XXH32_update(XXH32_state_t *statePtr, const void *input, size_t length); /*! * @brief Returns the calculated hash value from an @ref XXH32_state_t. @@ -546,7 +551,7 @@ XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* * * @return The calculated xxHash32 value from that state. */ -XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr); +XXH_PUBLIC_API XXH32_hash_t XXH32_digest(const XXH32_state_t *statePtr); /******* Canonical representation *******/ @@ -573,7 +578,7 @@ XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr); * @brief Canonical (big endian) representation of @ref XXH32_hash_t. */ typedef struct { - unsigned char digest[4]; /*!< Hash bytes, big endian */ + unsigned char digest[4]; /*!< Hash bytes, big endian */ } XXH32_canonical_t; /*! @@ -585,7 +590,7 @@ typedef struct { * @pre * @p dst must not be `NULL`. */ -XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash); +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t *dst, XXH32_hash_t hash); /*! * @brief Converts an @ref XXH32_canonical_t to a native @ref XXH32_hash_t. @@ -597,7 +602,7 @@ XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t * * @return The converted hash. */ -XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src); +XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t *src); #ifdef __has_attribute @@ -653,18 +658,20 @@ C23 : https://en.cppreference.com/w/c/language/attributes/fallthrough */ typedef uint64_t XXH64_hash_t; #elif !defined (__VMS) \ - && (defined (__cplusplus) \ - || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)) + # include - typedef uint64_t XXH64_hash_t; + +typedef uint64_t XXH64_hash_t; #else # include # if defined(__LP64__) && ULONG_MAX == 0xFFFFFFFFFFFFFFFFULL - /* LP64 ABI says uint64_t is unsigned long */ - typedef unsigned long XXH64_hash_t; +/* LP64 ABI says uint64_t is unsigned long */ +typedef unsigned long XXH64_hash_t; # else - /* the following type must have a width of 64-bit */ - typedef unsigned long long XXH64_hash_t; +/* the following type must have a width of 64-bit */ +typedef unsigned long long XXH64_hash_t; # endif #endif @@ -706,7 +713,7 @@ typedef uint64_t XXH64_hash_t; * @see * XXH64_createState(), XXH64_update(), XXH64_digest(): Streaming version. */ -XXH_PUBLIC_API XXH64_hash_t XXH64(const void* input, size_t length, XXH64_hash_t seed); +XXH_PUBLIC_API XXH64_hash_t XXH64(const void *input, size_t length, XXH64_hash_t seed); /******* Streaming *******/ /*! @@ -715,18 +722,25 @@ XXH_PUBLIC_API XXH64_hash_t XXH64(const void* input, size_t length, XXH64_hash_t * @see XXH64_state_s for details. */ typedef struct XXH64_state_s XXH64_state_t; /* incomplete type */ -XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void); -XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr); -XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dst_state, const XXH64_state_t* src_state); +XXH_PUBLIC_API XXH64_state_t *XXH64_createState(void); -XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH64_state_t* statePtr, XXH64_hash_t seed); -XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length); -XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* statePtr); +XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t *statePtr); + +XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t *dst_state, const XXH64_state_t *src_state); + +XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t *statePtr, XXH64_hash_t seed); + +XXH_PUBLIC_API XXH_errorcode XXH64_update(XXH64_state_t *statePtr, const void *input, size_t length); + +XXH_PUBLIC_API XXH64_hash_t XXH64_digest(const XXH64_state_t *statePtr); /******* Canonical representation *******/ -typedef struct { unsigned char digest[sizeof(XXH64_hash_t)]; } XXH64_canonical_t; -XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash); -XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src); +typedef struct { + unsigned char digest[sizeof(XXH64_hash_t)]; +} XXH64_canonical_t; +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t *dst, XXH64_hash_t hash); + +XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t *src); /*! * @} @@ -780,7 +794,7 @@ XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src /* XXH3_64bits(): * default 64-bit variant, using default secret and default seed of 0. * It's the fastest variant. */ -XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(const void* data, size_t len); +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(const void *data, size_t len); /* * XXH3_64bits_withSeed(): @@ -789,7 +803,7 @@ XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(const void* data, size_t len); * While this operation is decently fast, note that it's not completely free. * Note: seed==0 produces the same results as XXH3_64bits(). */ -XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSeed(const void* data, size_t len, XXH64_hash_t seed); +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSeed(const void *data, size_t len, XXH64_hash_t seed); /*! * The bare minimum size for a custom secret. @@ -817,7 +831,7 @@ XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSeed(const void* data, size_t len, X * This is not necessarily the case when using the blob of bytes directly * because, when hashing _small_ inputs, only a portion of the secret is employed. */ -XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSecret(const void* data, size_t len, const void* secret, size_t secretSize); +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSecret(const void *data, size_t len, const void *secret, size_t secretSize); /******* Streaming *******/ @@ -834,22 +848,24 @@ XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSecret(const void* data, size_t len, * @see XXH3_state_s for details. */ typedef struct XXH3_state_s XXH3_state_t; -XXH_PUBLIC_API XXH3_state_t* XXH3_createState(void); -XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr); -XXH_PUBLIC_API void XXH3_copyState(XXH3_state_t* dst_state, const XXH3_state_t* src_state); +XXH_PUBLIC_API XXH3_state_t *XXH3_createState(void); + +XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t *statePtr); + +XXH_PUBLIC_API void XXH3_copyState(XXH3_state_t *dst_state, const XXH3_state_t *src_state); /* * XXH3_64bits_reset(): * Initialize with default parameters. * digest will be equivalent to `XXH3_64bits()`. */ -XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset(XXH3_state_t* statePtr); +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset(XXH3_state_t *statePtr); /* * XXH3_64bits_reset_withSeed(): * Generate a custom secret from `seed`, and store it into `statePtr`. * digest will be equivalent to `XXH3_64bits_withSeed()`. */ -XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed); +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSeed(XXH3_state_t *statePtr, XXH64_hash_t seed); /* * XXH3_64bits_reset_withSecret(): * `secret` is referenced, it _must outlive_ the hash streaming session. @@ -859,10 +875,12 @@ XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSeed(XXH3_state_t* statePtr, * When in doubt about the randomness of a candidate `secret`, * consider employing `XXH3_generateSecret()` instead (see below). */ -XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize); +XXH_PUBLIC_API XXH_errorcode +XXH3_64bits_reset_withSecret(XXH3_state_t *statePtr, const void *secret, size_t secretSize); -XXH_PUBLIC_API XXH_errorcode XXH3_64bits_update (XXH3_state_t* statePtr, const void* input, size_t length); -XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (const XXH3_state_t* statePtr); +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_update(XXH3_state_t *statePtr, const void *input, size_t length); + +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest(const XXH3_state_t *statePtr); /* note : canonical representation of XXH3 is the same as XXH64 * since they both produce XXH64_hash_t values */ @@ -879,13 +897,16 @@ XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (const XXH3_state_t* statePtr); * endianness. */ typedef struct { - XXH64_hash_t low64; /*!< `value & 0xFFFFFFFFFFFFFFFF` */ - XXH64_hash_t high64; /*!< `value >> 64` */ + XXH64_hash_t low64; /*!< `value & 0xFFFFFFFFFFFFFFFF` */ + XXH64_hash_t high64; /*!< `value >> 64` */ } XXH128_hash_t; -XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(const void* data, size_t len); -XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSeed(const void* data, size_t len, XXH64_hash_t seed); -XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSecret(const void* data, size_t len, const void* secret, size_t secretSize); +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(const void *data, size_t len); + +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSeed(const void *data, size_t len, XXH64_hash_t seed); + +XXH_PUBLIC_API XXH128_hash_t +XXH3_128bits_withSecret(const void *data, size_t len, const void *secret, size_t secretSize); /******* Streaming *******/ /* @@ -900,12 +921,16 @@ XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSecret(const void* data, size_t le * All reset and streaming functions have same meaning as their 64-bit counterpart. */ -XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset(XXH3_state_t* statePtr); -XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed); -XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize); +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset(XXH3_state_t *statePtr); + +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSeed(XXH3_state_t *statePtr, XXH64_hash_t seed); -XXH_PUBLIC_API XXH_errorcode XXH3_128bits_update (XXH3_state_t* statePtr, const void* input, size_t length); -XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest (const XXH3_state_t* statePtr); +XXH_PUBLIC_API XXH_errorcode +XXH3_128bits_reset_withSecret(XXH3_state_t *statePtr, const void *secret, size_t secretSize); + +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_update(XXH3_state_t *statePtr, const void *input, size_t length); + +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest(const XXH3_state_t *statePtr); /* Following helper functions make it possible to compare XXH128_hast_t values. * Since XXH128_hash_t is a structure, this capability is not offered by the language. @@ -926,13 +951,16 @@ XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2); * =0 if *h128_1 == *h128_2 * <0 if *h128_1 < *h128_2 */ -XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2); +XXH_PUBLIC_API int XXH128_cmp(const void *h128_1, const void *h128_2); /******* Canonical representation *******/ -typedef struct { unsigned char digest[sizeof(XXH128_hash_t)]; } XXH128_canonical_t; -XXH_PUBLIC_API void XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash); -XXH_PUBLIC_API XXH128_hash_t XXH128_hashFromCanonical(const XXH128_canonical_t* src); +typedef struct { + unsigned char digest[sizeof(XXH128_hash_t)]; +} XXH128_canonical_t; +XXH_PUBLIC_API void XXH128_canonicalFromHash(XXH128_canonical_t *dst, XXH128_hash_t hash); + +XXH_PUBLIC_API XXH128_hash_t XXH128_hashFromCanonical(const XXH128_canonical_t *src); #endif /* XXH_NO_LONG_LONG */ @@ -943,7 +971,6 @@ XXH_PUBLIC_API XXH128_hash_t XXH128_hashFromCanonical(const XXH128_canonical_t* #endif /* XXHASH_H_5627135585666179 */ - #if defined(XXH_STATIC_LINKING_ONLY) && !defined(XXHASH_H_STATIC_13879238742) #define XXHASH_H_STATIC_13879238742 /* **************************************************************************** @@ -973,12 +1000,12 @@ XXH_PUBLIC_API XXH128_hash_t XXH128_hashFromCanonical(const XXH128_canonical_t* * @see XXH64_state_s, XXH3_state_s */ struct XXH32_state_s { - XXH32_hash_t total_len_32; /*!< Total length hashed, modulo 2^32 */ - XXH32_hash_t large_len; /*!< Whether the hash is >= 16 (handles @ref total_len_32 overflow) */ - XXH32_hash_t v[4]; /*!< Accumulator lanes */ - XXH32_hash_t mem32[4]; /*!< Internal buffer for partial reads. Treated as unsigned char[16]. */ - XXH32_hash_t memsize; /*!< Amount of data in @ref mem32 */ - XXH32_hash_t reserved; /*!< Reserved field. Do not read or write to it, it may be removed. */ + XXH32_hash_t total_len_32; /*!< Total length hashed, modulo 2^32 */ + XXH32_hash_t large_len; /*!< Whether the hash is >= 16 (handles @ref total_len_32 overflow) */ + XXH32_hash_t v[4]; /*!< Accumulator lanes */ + XXH32_hash_t mem32[4]; /*!< Internal buffer for partial reads. Treated as unsigned char[16]. */ + XXH32_hash_t memsize; /*!< Amount of data in @ref mem32 */ + XXH32_hash_t reserved; /*!< Reserved field. Do not read or write to it, it may be removed. */ }; /* typedef'd to XXH32_state_t */ @@ -997,12 +1024,12 @@ struct XXH32_state_s { * @see XXH32_state_s, XXH3_state_s */ struct XXH64_state_s { - XXH64_hash_t total_len; /*!< Total length hashed. This is always 64-bit. */ - XXH64_hash_t v[4]; /*!< Accumulator lanes */ - XXH64_hash_t mem64[4]; /*!< Internal buffer for partial reads. Treated as unsigned char[32]. */ - XXH32_hash_t memsize; /*!< Amount of data in @ref mem64 */ - XXH32_hash_t reserved32; /*!< Reserved field, needed for padding anyways*/ - XXH64_hash_t reserved64; /*!< Reserved field. Do not read or write to it, it may be removed. */ + XXH64_hash_t total_len; /*!< Total length hashed. This is always 64-bit. */ + XXH64_hash_t v[4]; /*!< Accumulator lanes */ + XXH64_hash_t mem64[4]; /*!< Internal buffer for partial reads. Treated as unsigned char[32]. */ + XXH32_hash_t memsize; /*!< Amount of data in @ref mem64 */ + XXH32_hash_t reserved32; /*!< Reserved field, needed for padding anyways*/ + XXH64_hash_t reserved64; /*!< Reserved field. Do not read or write to it, it may be removed. */ }; /* typedef'd to XXH64_state_t */ #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* >= C11 */ @@ -1021,8 +1048,8 @@ struct XXH64_state_s { /* Old GCC versions only accept the attribute after the type in structures. */ #if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) /* C11+ */ \ - && ! (defined(__cplusplus) && (__cplusplus >= 201103L)) /* >= C++11 */ \ - && defined(__GNUC__) + && !(defined(__cplusplus) && (__cplusplus >= 201103L)) /* >= C++11 */ \ + && defined(__GNUC__) # define XXH_ALIGN_MEMBER(align, type) type XXH_ALIGN(align) #else # define XXH_ALIGN_MEMBER(align, type) XXH_ALIGN(align) type @@ -1069,32 +1096,32 @@ struct XXH64_state_s { * @see XXH32_state_s, XXH64_state_s */ struct XXH3_state_s { - XXH_ALIGN_MEMBER(64, XXH64_hash_t acc[8]); - /*!< The 8 accumulators. Similar to `vN` in @ref XXH32_state_s::v1 and @ref XXH64_state_s */ - XXH_ALIGN_MEMBER(64, unsigned char customSecret[XXH3_SECRET_DEFAULT_SIZE]); - /*!< Used to store a custom secret generated from a seed. */ - XXH_ALIGN_MEMBER(64, unsigned char buffer[XXH3_INTERNALBUFFER_SIZE]); - /*!< The internal buffer. @see XXH32_state_s::mem32 */ - XXH32_hash_t bufferedSize; - /*!< The amount of memory in @ref buffer, @see XXH32_state_s::memsize */ - XXH32_hash_t useSeed; - /*!< Reserved field. Needed for padding on 64-bit. */ - size_t nbStripesSoFar; - /*!< Number or stripes processed. */ - XXH64_hash_t totalLen; - /*!< Total length hashed. 64-bit even on 32-bit targets. */ - size_t nbStripesPerBlock; - /*!< Number of stripes per block. */ - size_t secretLimit; - /*!< Size of @ref customSecret or @ref extSecret */ - XXH64_hash_t seed; - /*!< Seed for _withSeed variants. Must be zero otherwise, @see XXH3_INITSTATE() */ - XXH64_hash_t reserved64; - /*!< Reserved field. */ - const unsigned char* extSecret; - /*!< Reference to an external secret for the _withSecret variants, NULL - * for other variants. */ - /* note: there may be some padding at the end due to alignment on 64 bytes */ + XXH_ALIGN_MEMBER(64, XXH64_hash_t acc[8]); + /*!< The 8 accumulators. Similar to `vN` in @ref XXH32_state_s::v1 and @ref XXH64_state_s */ + XXH_ALIGN_MEMBER(64, unsigned char customSecret[XXH3_SECRET_DEFAULT_SIZE]); + /*!< Used to store a custom secret generated from a seed. */ + XXH_ALIGN_MEMBER(64, unsigned char buffer[XXH3_INTERNALBUFFER_SIZE]); + /*!< The internal buffer. @see XXH32_state_s::mem32 */ + XXH32_hash_t bufferedSize; + /*!< The amount of memory in @ref buffer, @see XXH32_state_s::memsize */ + XXH32_hash_t useSeed; + /*!< Reserved field. Needed for padding on 64-bit. */ + size_t nbStripesSoFar; + /*!< Number or stripes processed. */ + XXH64_hash_t totalLen; + /*!< Total length hashed. 64-bit even on 32-bit targets. */ + size_t nbStripesPerBlock; + /*!< Number of stripes per block. */ + size_t secretLimit; + /*!< Size of @ref customSecret or @ref extSecret */ + XXH64_hash_t seed; + /*!< Seed for _withSeed variants. Must be zero otherwise, @see XXH3_INITSTATE() */ + XXH64_hash_t reserved64; + /*!< Reserved field. */ + const unsigned char *extSecret; + /*!< Reference to an external secret for the _withSecret variants, NULL + * for other variants. */ + /* note: there may be some padding at the end due to alignment on 64 bytes */ }; /* typedef'd to XXH3_state_t */ #undef XXH_ALIGN_MEMBER @@ -1116,7 +1143,7 @@ struct XXH3_state_s { /* XXH128() : * simple alias to pre-selected XXH3_128bits variant */ -XXH_PUBLIC_API XXH128_hash_t XXH128(const void* data, size_t len, XXH64_hash_t seed); +XXH_PUBLIC_API XXH128_hash_t XXH128(const void *data, size_t len, XXH64_hash_t seed); /* === Experimental API === */ @@ -1150,7 +1177,8 @@ XXH_PUBLIC_API XXH128_hash_t XXH128(const void* data, size_t len, XXH64_hash_t s * * When customSeedSize > 0, supplying NULL as customSeed is undefined behavior. */ -XXH_PUBLIC_API XXH_errorcode XXH3_generateSecret(void* secretBuffer, size_t secretSize, const void* customSeed, size_t customSeedSize); +XXH_PUBLIC_API XXH_errorcode +XXH3_generateSecret(void *secretBuffer, size_t secretSize, const void *customSeed, size_t customSeedSize); /* @@ -1166,7 +1194,7 @@ XXH_PUBLIC_API XXH_errorcode XXH3_generateSecret(void* secretBuffer, size_t secr * This generator is notably useful in combination with `_withSecretandSeed()`, * as a way to emulate a faster `_withSeed()` variant. */ -XXH_PUBLIC_API void XXH3_generateSecret_fromSeed(void* secretBuffer, XXH64_hash_t seed); +XXH_PUBLIC_API void XXH3_generateSecret_fromSeed(void *secretBuffer, XXH64_hash_t seed); /* * *_withSecretandSeed() : @@ -1196,23 +1224,23 @@ XXH_PUBLIC_API void XXH3_generateSecret_fromSeed(void* secretBuffer, XXH64_hash_ * because only portions of the secret are employed for small data. */ XXH_PUBLIC_API XXH64_hash_t -XXH3_64bits_withSecretandSeed(const void* data, size_t len, - const void* secret, size_t secretSize, +XXH3_64bits_withSecretandSeed(const void *data, size_t len, + const void *secret, size_t secretSize, XXH64_hash_t seed); XXH_PUBLIC_API XXH128_hash_t -XXH3_128bits_withSecretandSeed(const void* data, size_t len, - const void* secret, size_t secretSize, +XXH3_128bits_withSecretandSeed(const void *data, size_t len, + const void *secret, size_t secretSize, XXH64_hash_t seed64); XXH_PUBLIC_API XXH_errorcode -XXH3_64bits_reset_withSecretandSeed(XXH3_state_t* statePtr, - const void* secret, size_t secretSize, +XXH3_64bits_reset_withSecretandSeed(XXH3_state_t *statePtr, + const void *secret, size_t secretSize, XXH64_hash_t seed64); XXH_PUBLIC_API XXH_errorcode -XXH3_128bits_reset_withSecretandSeed(XXH3_state_t* statePtr, - const void* secret, size_t secretSize, +XXH3_128bits_reset_withSecretandSeed(XXH3_state_t *statePtr, + const void *secret, size_t secretSize, XXH64_hash_t seed64); @@ -1251,8 +1279,8 @@ XXH3_128bits_reset_withSecretandSeed(XXH3_state_t* statePtr, * which can then be linked into the final binary. ************************************************************************/ -#if ( defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) \ - || defined(XXH_IMPLEMENTATION) ) && !defined(XXH_IMPLEM_13a8737387) +#if (defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) \ + || defined(XXH_IMPLEMENTATION)) && !defined(XXH_IMPLEM_13a8737387) # define XXH_IMPLEM_13a8737387 /* ************************************* @@ -1402,28 +1430,28 @@ XXH3_128bits_reset_withSecretandSeed(XXH3_state_t* statePtr, */ #ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ - /* prefer __packed__ structures (method 1) for gcc on armv7+ and mips */ +/* prefer __packed__ structures (method 1) for gcc on armv7+ and mips */ # if !defined(__clang__) && \ -( \ +(\ (defined(__INTEL_COMPILER) && !defined(_WIN32)) || \ - ( \ - defined(__GNUC__) && ( \ + (\ + defined(__GNUC__) && (\ (defined(__ARM_ARCH) && __ARM_ARCH >= 7) || \ - ( \ + (\ defined(__mips__) && \ (__mips <= 5 || __mips_isa_rev < 6) && \ (!defined(__mips16) || defined(__mips_mips16e2)) \ - ) \ - ) \ - ) \ +) \ +) \ +) \ ) # define XXH_FORCE_MEMORY_ACCESS 1 # endif #endif #ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */ -# if defined(__i386) || defined(__x86_64__) || defined(__aarch64__) \ - || defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM64) /* visual */ +# if defined(__i386) || defined(__x86_64__) || defined(__aarch64__) \ + || defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM64) /* visual */ # define XXH_FORCE_ALIGN_CHECK 0 # else # define XXH_FORCE_ALIGN_CHECK 1 @@ -1432,7 +1460,7 @@ XXH3_128bits_reset_withSecretandSeed(XXH3_state_t* statePtr, #ifndef XXH_NO_INLINE_HINTS # if defined(__OPTIMIZE_SIZE__) /* -Os, -Oz */ \ - || defined(__NO_INLINE__) /* -O0, -fno-inline */ + || defined(__NO_INLINE__) /* -O0, -fno-inline */ # define XXH_NO_INLINE_HINTS 1 # else # define XXH_NO_INLINE_HINTS 0 @@ -1463,13 +1491,13 @@ XXH3_128bits_reset_withSecretandSeed(XXH3_state_t* statePtr, * @internal * @brief Modify this function to use a different routine than malloc(). */ -static void* XXH_malloc(size_t s) { return malloc(s); } +static void *XXH_malloc(size_t s) { return malloc(s); } /*! * @internal * @brief Modify this function to use a different routine than free(). */ -static void XXH_free(void* p) { free(p); } +static void XXH_free(void *p) { free(p); } #include @@ -1477,9 +1505,8 @@ static void XXH_free(void* p) { free(p); } * @internal * @brief Modify this function to use a different routine than memcpy(). */ -static void* XXH_memcpy(void* dest, const void* src, size_t size) -{ - return memcpy(dest,src,size); +static void *XXH_memcpy(void *dest, const void *src, size_t size) { + return memcpy(dest, src, size); } #include /* ULLONG_MAX */ @@ -1536,7 +1563,7 @@ static void* XXH_memcpy(void* dest, const void* src, size_t size) # endif #endif -#if (XXH_DEBUGLEVEL>=1) +#if (XXH_DEBUGLEVEL >= 1) # include /* note: can still be disabled with NDEBUG */ # define XXH_ASSERT(c) assert(c) #else @@ -1551,7 +1578,7 @@ static void* XXH_memcpy(void* dest, const void* src, size_t size) # elif defined(__cplusplus) && (__cplusplus >= 201103L) /* C++11 */ # define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { static_assert((c),m); } while(0) # else -# define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { struct xxh_sa { char x[(c) ? 1 : -1]; }; } while(0) +# define XXH_STATIC_ASSERT_WITH_MESSAGE(c, m) do { struct xxh_sa { char x[(c) ? 1 : -1]; }; } while(0) # endif # define XXH_STATIC_ASSERT(c) XXH_STATIC_ASSERT_WITH_MESSAGE((c),#c) #endif @@ -1583,11 +1610,13 @@ static void* XXH_memcpy(void* dest, const void* src, size_t size) ***************************************/ #if !defined (__VMS) \ && (defined (__cplusplus) \ - || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)) + # include - typedef uint8_t xxh_u8; + +typedef uint8_t xxh_u8; #else - typedef unsigned char xxh_u8; +typedef unsigned char xxh_u8; #endif typedef XXH32_hash_t xxh_u32; @@ -1649,12 +1678,12 @@ typedef XXH32_hash_t xxh_u32; * @return The 32-bit little endian integer from the bytes at @p ptr. */ -#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3)) +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS == 3)) /* * Manual byteshift. Best for old compilers which don't inline memcpy. * We actually directly use XXH_readLE32 and XXH_readBE32. */ -#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS == 2)) /* * Force direct memory access. Only works on CPU which support unaligned memory @@ -1662,7 +1691,7 @@ typedef XXH32_hash_t xxh_u32; */ static xxh_u32 XXH_read32(const void* memPtr) { return *(const xxh_u32*) memPtr; } -#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS == 1)) /* * __pack instructions are safer but compiler specific, hence potentially @@ -1685,11 +1714,10 @@ static xxh_u32 XXH_read32(const void* ptr) * Portable and safe solution. Generally efficient. * see: http://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html */ -static xxh_u32 XXH_read32(const void* memPtr) -{ - xxh_u32 val; - XXH_memcpy(&val, memPtr, sizeof(val)); - return val; +static xxh_u32 XXH_read32(const void *memPtr) { + xxh_u32 val; + XXH_memcpy(&val, memPtr, sizeof(val)); + return val; } #endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ @@ -1719,11 +1747,11 @@ static xxh_u32 XXH_read32(const void* memPtr) * in `XXH_isLittleEndian()` */ # if defined(_WIN32) /* Windows is always little endian */ \ - || defined(__LITTLE_ENDIAN__) \ - || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + || defined(__LITTLE_ENDIAN__) \ + || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) # define XXH_CPU_LITTLE_ENDIAN 1 # elif defined(__BIG_ENDIAN__) \ - || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) + || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) # define XXH_CPU_LITTLE_ENDIAN 0 # else /*! @@ -1773,7 +1801,7 @@ static int XXH_isLittleEndian(void) * @return The rotated result. */ #if !defined(NO_CLANG_BUILTIN) && XXH_HAS_BUILTIN(__builtin_rotateleft32) \ - && XXH_HAS_BUILTIN(__builtin_rotateleft64) + && XXH_HAS_BUILTIN(__builtin_rotateleft64) # define XXH_rotl32 __builtin_rotateleft32 # define XXH_rotl64 __builtin_rotateleft64 /* Note: although _rotl exists for minGW (GCC under windows), performance seems poor */ @@ -1781,8 +1809,8 @@ static int XXH_isLittleEndian(void) # define XXH_rotl32(x,r) _rotl(x,r) # define XXH_rotl64(x,r) _rotl64(x,r) #else -# define XXH_rotl32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) -# define XXH_rotl64(x,r) (((x) << (r)) | ((x) >> (64 - (r)))) +# define XXH_rotl32(x, r) (((x) << (r)) | ((x) >> (32 - (r)))) +# define XXH_rotl64(x, r) (((x) << (r)) | ((x) >> (64 - (r)))) #endif /*! @@ -1817,8 +1845,8 @@ static xxh_u32 XXH_swap32 (xxh_u32 x) * @brief Enum to indicate whether a pointer is aligned. */ typedef enum { - XXH_aligned, /*!< Aligned */ - XXH_unaligned /*!< Possibly unaligned */ + XXH_aligned, /*!< Aligned */ + XXH_unaligned /*!< Possibly unaligned */ } XXH_alignment; /* @@ -1826,7 +1854,7 @@ typedef enum { * * This is ideal for older compilers which don't inline memcpy. */ -#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3)) +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS == 3)) XXH_FORCE_INLINE xxh_u32 XXH_readLE32(const void* memPtr) { @@ -1847,25 +1875,24 @@ XXH_FORCE_INLINE xxh_u32 XXH_readBE32(const void* memPtr) } #else -XXH_FORCE_INLINE xxh_u32 XXH_readLE32(const void* ptr) -{ - return XXH_CPU_LITTLE_ENDIAN ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr)); + +XXH_FORCE_INLINE xxh_u32 XXH_readLE32(const void *ptr) { + return XXH_CPU_LITTLE_ENDIAN ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr)); } -static xxh_u32 XXH_readBE32(const void* ptr) -{ - return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr); +static xxh_u32 XXH_readBE32(const void *ptr) { + return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr); } + #endif XXH_FORCE_INLINE xxh_u32 -XXH_readLE32_align(const void* ptr, XXH_alignment align) -{ - if (align==XXH_unaligned) { - return XXH_readLE32(ptr); - } else { - return XXH_CPU_LITTLE_ENDIAN ? *(const xxh_u32*)ptr : XXH_swap32(*(const xxh_u32*)ptr); - } +XXH_readLE32_align(const void *ptr, XXH_alignment align) { + if (align == XXH_unaligned) { + return XXH_readLE32(ptr); + } else { + return XXH_CPU_LITTLE_ENDIAN ? *(const xxh_u32 *) ptr : XXH_swap32(*(const xxh_u32 *) ptr); + } } @@ -1873,7 +1900,7 @@ XXH_readLE32_align(const void* ptr, XXH_alignment align) * Misc ***************************************/ /*! @ingroup public */ -XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; } +XXH_PUBLIC_API unsigned XXH_versionNumber(void) { return XXH_VERSION_NUMBER; } /* ******************************************************************* @@ -1885,7 +1912,7 @@ XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; } * @ingroup impl * @{ */ - /* #define instead of static const, to be used as initializers */ +/* #define instead of static const, to be used as initializers */ #define XXH_PRIME32_1 0x9E3779B1U /*!< 0b10011110001101110111100110110001 */ #define XXH_PRIME32_2 0x85EBCA77U /*!< 0b10000101111010111100101001110111 */ #define XXH_PRIME32_3 0xC2B2AE3DU /*!< 0b11000010101100101010111000111101 */ @@ -1911,48 +1938,47 @@ XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; } * @param input The stripe of input to mix. * @return The mixed accumulator lane. */ -static xxh_u32 XXH32_round(xxh_u32 acc, xxh_u32 input) -{ - acc += input * XXH_PRIME32_2; - acc = XXH_rotl32(acc, 13); - acc *= XXH_PRIME32_1; +static xxh_u32 XXH32_round(xxh_u32 acc, xxh_u32 input) { + acc += input * XXH_PRIME32_2; + acc = XXH_rotl32(acc, 13); + acc *= XXH_PRIME32_1; #if (defined(__SSE4_1__) || defined(__aarch64__)) && !defined(XXH_ENABLE_AUTOVECTORIZE) - /* - * UGLY HACK: - * A compiler fence is the only thing that prevents GCC and Clang from - * autovectorizing the XXH32 loop (pragmas and attributes don't work for some - * reason) without globally disabling SSE4.1. - * - * The reason we want to avoid vectorization is because despite working on - * 4 integers at a time, there are multiple factors slowing XXH32 down on - * SSE4: - * - There's a ridiculous amount of lag from pmulld (10 cycles of latency on - * newer chips!) making it slightly slower to multiply four integers at - * once compared to four integers independently. Even when pmulld was - * fastest, Sandy/Ivy Bridge, it is still not worth it to go into SSE - * just to multiply unless doing a long operation. - * - * - Four instructions are required to rotate, - * movqda tmp, v // not required with VEX encoding - * pslld tmp, 13 // tmp <<= 13 - * psrld v, 19 // x >>= 19 - * por v, tmp // x |= tmp - * compared to one for scalar: - * roll v, 13 // reliably fast across the board - * shldl v, v, 13 // Sandy Bridge and later prefer this for some reason - * - * - Instruction level parallelism is actually more beneficial here because - * the SIMD actually serializes this operation: While v1 is rotating, v2 - * can load data, while v3 can multiply. SSE forces them to operate - * together. - * - * This is also enabled on AArch64, as Clang autovectorizes it incorrectly - * and it is pointless writing a NEON implementation that is basically the - * same speed as scalar for XXH32. - */ - XXH_COMPILER_GUARD(acc); + /* + * UGLY HACK: + * A compiler fence is the only thing that prevents GCC and Clang from + * autovectorizing the XXH32 loop (pragmas and attributes don't work for some + * reason) without globally disabling SSE4.1. + * + * The reason we want to avoid vectorization is because despite working on + * 4 integers at a time, there are multiple factors slowing XXH32 down on + * SSE4: + * - There's a ridiculous amount of lag from pmulld (10 cycles of latency on + * newer chips!) making it slightly slower to multiply four integers at + * once compared to four integers independently. Even when pmulld was + * fastest, Sandy/Ivy Bridge, it is still not worth it to go into SSE + * just to multiply unless doing a long operation. + * + * - Four instructions are required to rotate, + * movqda tmp, v // not required with VEX encoding + * pslld tmp, 13 // tmp <<= 13 + * psrld v, 19 // x >>= 19 + * por v, tmp // x |= tmp + * compared to one for scalar: + * roll v, 13 // reliably fast across the board + * shldl v, v, 13 // Sandy Bridge and later prefer this for some reason + * + * - Instruction level parallelism is actually more beneficial here because + * the SIMD actually serializes this operation: While v1 is rotating, v2 + * can load data, while v3 can multiply. SSE forces them to operate + * together. + * + * This is also enabled on AArch64, as Clang autovectorizes it incorrectly + * and it is pointless writing a NEON implementation that is basically the + * same speed as scalar for XXH32. + */ + XXH_COMPILER_GUARD(acc); #endif - return acc; + return acc; } /*! @@ -1965,14 +1991,13 @@ static xxh_u32 XXH32_round(xxh_u32 acc, xxh_u32 input) * @param h32 The hash to avalanche. * @return The avalanched hash. */ -static xxh_u32 XXH32_avalanche(xxh_u32 h32) -{ - h32 ^= h32 >> 15; - h32 *= XXH_PRIME32_2; - h32 ^= h32 >> 13; - h32 *= XXH_PRIME32_3; - h32 ^= h32 >> 16; - return(h32); +static xxh_u32 XXH32_avalanche(xxh_u32 h32) { + h32 ^= h32 >> 15; + h32 *= XXH_PRIME32_2; + h32 ^= h32 >> 13; + h32 *= XXH_PRIME32_3; + h32 ^= h32 >> 16; + return (h32); } #define XXH_get32bits(p) XXH_readLE32_align(p, align) @@ -1992,8 +2017,7 @@ static xxh_u32 XXH32_avalanche(xxh_u32 h32) * @return The finalized hash. */ static xxh_u32 -XXH32_finalize(xxh_u32 h32, const xxh_u8* ptr, size_t len, XXH_alignment align) -{ +XXH32_finalize(xxh_u32 h32, const xxh_u8 *ptr, size_t len, XXH_alignment align) { #define XXH_PROCESS1 do { \ h32 += (*ptr++) * XXH_PRIME32_5; \ h32 = XXH_rotl32(h32, 11) * XXH_PRIME32_1; \ @@ -2005,63 +2029,79 @@ XXH32_finalize(xxh_u32 h32, const xxh_u8* ptr, size_t len, XXH_alignment align) h32 = XXH_rotl32(h32, 17) * XXH_PRIME32_4; \ } while (0) - if (ptr==NULL) XXH_ASSERT(len == 0); + if (ptr == NULL) XXH_ASSERT(len == 0); - /* Compact rerolled version; generally faster */ - if (!XXH32_ENDJMP) { - len &= 15; - while (len >= 4) { - XXH_PROCESS4; - len -= 4; - } - while (len > 0) { - XXH_PROCESS1; - --len; - } + /* Compact rerolled version; generally faster */ + if (!XXH32_ENDJMP) { + len &= 15; + while (len >= 4) { + XXH_PROCESS4; + len -= 4; + } + while (len > 0) { + XXH_PROCESS1; + --len; + } + return XXH32_avalanche(h32); + } else { + switch (len & 15) /* or switch(bEnd - p) */ { + case 12: + XXH_PROCESS4; + XXH_FALLTHROUGH; + case 8: + XXH_PROCESS4; + XXH_FALLTHROUGH; + case 4: + XXH_PROCESS4; + return XXH32_avalanche(h32); + + case 13: + XXH_PROCESS4; + XXH_FALLTHROUGH; + case 9: + XXH_PROCESS4; + XXH_FALLTHROUGH; + case 5: + XXH_PROCESS4; + XXH_PROCESS1; + return XXH32_avalanche(h32); + + case 14: + XXH_PROCESS4; + XXH_FALLTHROUGH; + case 10: + XXH_PROCESS4; + XXH_FALLTHROUGH; + case 6: + XXH_PROCESS4; + XXH_PROCESS1; + XXH_PROCESS1; + return XXH32_avalanche(h32); + + case 15: + XXH_PROCESS4; + XXH_FALLTHROUGH; + case 11: + XXH_PROCESS4; + XXH_FALLTHROUGH; + case 7: + XXH_PROCESS4; + XXH_FALLTHROUGH; + case 3: + XXH_PROCESS1; + XXH_FALLTHROUGH; + case 2: + XXH_PROCESS1; + XXH_FALLTHROUGH; + case 1: + XXH_PROCESS1; + XXH_FALLTHROUGH; + case 0: return XXH32_avalanche(h32); - } else { - switch(len&15) /* or switch(bEnd - p) */ { - case 12: XXH_PROCESS4; - XXH_FALLTHROUGH; - case 8: XXH_PROCESS4; - XXH_FALLTHROUGH; - case 4: XXH_PROCESS4; - return XXH32_avalanche(h32); - - case 13: XXH_PROCESS4; - XXH_FALLTHROUGH; - case 9: XXH_PROCESS4; - XXH_FALLTHROUGH; - case 5: XXH_PROCESS4; - XXH_PROCESS1; - return XXH32_avalanche(h32); - - case 14: XXH_PROCESS4; - XXH_FALLTHROUGH; - case 10: XXH_PROCESS4; - XXH_FALLTHROUGH; - case 6: XXH_PROCESS4; - XXH_PROCESS1; - XXH_PROCESS1; - return XXH32_avalanche(h32); - - case 15: XXH_PROCESS4; - XXH_FALLTHROUGH; - case 11: XXH_PROCESS4; - XXH_FALLTHROUGH; - case 7: XXH_PROCESS4; - XXH_FALLTHROUGH; - case 3: XXH_PROCESS1; - XXH_FALLTHROUGH; - case 2: XXH_PROCESS1; - XXH_FALLTHROUGH; - case 1: XXH_PROCESS1; - XXH_FALLTHROUGH; - case 0: return XXH32_avalanche(h32); - } - XXH_ASSERT(0); - return h32; /* reaching this point is deemed impossible */ } + XXH_ASSERT(0); + return h32; /* reaching this point is deemed impossible */ + } } #ifdef XXH_OLD_NAMES @@ -2081,54 +2121,57 @@ XXH32_finalize(xxh_u32 h32, const xxh_u8* ptr, size_t len, XXH_alignment align) * @return The calculated hash. */ XXH_FORCE_INLINE xxh_u32 -XXH32_endian_align(const xxh_u8* input, size_t len, xxh_u32 seed, XXH_alignment align) -{ - xxh_u32 h32; +XXH32_endian_align(const xxh_u8 *input, size_t len, xxh_u32 seed, XXH_alignment align) { + xxh_u32 h32; - if (input==NULL) XXH_ASSERT(len == 0); + if (input == NULL) XXH_ASSERT(len == 0); - if (len>=16) { - const xxh_u8* const bEnd = input + len; - const xxh_u8* const limit = bEnd - 15; - xxh_u32 v1 = seed + XXH_PRIME32_1 + XXH_PRIME32_2; - xxh_u32 v2 = seed + XXH_PRIME32_2; - xxh_u32 v3 = seed + 0; - xxh_u32 v4 = seed - XXH_PRIME32_1; + if (len >= 16) { + const xxh_u8 *const bEnd = input + len; + const xxh_u8 *const limit = bEnd - 15; + xxh_u32 v1 = seed + XXH_PRIME32_1 + XXH_PRIME32_2; + xxh_u32 v2 = seed + XXH_PRIME32_2; + xxh_u32 v3 = seed + 0; + xxh_u32 v4 = seed - XXH_PRIME32_1; - do { - v1 = XXH32_round(v1, XXH_get32bits(input)); input += 4; - v2 = XXH32_round(v2, XXH_get32bits(input)); input += 4; - v3 = XXH32_round(v3, XXH_get32bits(input)); input += 4; - v4 = XXH32_round(v4, XXH_get32bits(input)); input += 4; - } while (input < limit); + do { + v1 = XXH32_round(v1, XXH_get32bits(input)); + input += 4; + v2 = XXH32_round(v2, XXH_get32bits(input)); + input += 4; + v3 = XXH32_round(v3, XXH_get32bits(input)); + input += 4; + v4 = XXH32_round(v4, XXH_get32bits(input)); + input += 4; + } while (input < limit); - h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) - + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18); - } else { - h32 = seed + XXH_PRIME32_5; - } + h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18); + } else { + h32 = seed + XXH_PRIME32_5; + } - h32 += (xxh_u32)len; + h32 += (xxh_u32) len; - return XXH32_finalize(h32, input, len&15, align); + return XXH32_finalize(h32, input, len & 15, align); } /*! @ingroup xxh32_family */ -XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t len, XXH32_hash_t seed) -{ +XXH_PUBLIC_API XXH32_hash_t XXH32(const void *input, size_t len, XXH32_hash_t seed) { #if 0 - /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ - XXH32_state_t state; - XXH32_reset(&state, seed); - XXH32_update(&state, (const xxh_u8*)input, len); - return XXH32_digest(&state); + /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ + XXH32_state_t state; + XXH32_reset(&state, seed); + XXH32_update(&state, (const xxh_u8*)input, len); + return XXH32_digest(&state); #else - if (XXH_FORCE_ALIGN_CHECK) { - if ((((size_t)input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */ - return XXH32_endian_align((const xxh_u8*)input, len, seed, XXH_aligned); - } } + if (XXH_FORCE_ALIGN_CHECK) { + if ((((size_t) input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */ + return XXH32_endian_align((const xxh_u8 *) input, len, seed, XXH_aligned); + } + } - return XXH32_endian_align((const xxh_u8*)input, len, seed, XXH_unaligned); + return XXH32_endian_align((const xxh_u8 *) input, len, seed, XXH_unaligned); #endif } @@ -2138,110 +2181,113 @@ XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t len, XXH32_hash_t s /*! * @ingroup xxh32_family */ -XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void) -{ - return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t)); +XXH_PUBLIC_API XXH32_state_t *XXH32_createState(void) { + return (XXH32_state_t *) XXH_malloc(sizeof(XXH32_state_t)); } /*! @ingroup xxh32_family */ -XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr) -{ - XXH_free(statePtr); - return XXH_OK; +XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t *statePtr) { + XXH_free(statePtr); + return XXH_OK; } /*! @ingroup xxh32_family */ -XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState) -{ - XXH_memcpy(dstState, srcState, sizeof(*dstState)); +XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t *dstState, const XXH32_state_t *srcState) { + XXH_memcpy(dstState, srcState, sizeof(*dstState)); } /*! @ingroup xxh32_family */ -XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, XXH32_hash_t seed) -{ - XXH32_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ - memset(&state, 0, sizeof(state)); - state.v[0] = seed + XXH_PRIME32_1 + XXH_PRIME32_2; - state.v[1] = seed + XXH_PRIME32_2; - state.v[2] = seed + 0; - state.v[3] = seed - XXH_PRIME32_1; - /* do not write into reserved, planned to be removed in a future version */ - XXH_memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved)); - return XXH_OK; +XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t *statePtr, XXH32_hash_t seed) { + XXH32_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ + memset(&state, 0, sizeof(state)); + state.v[0] = seed + XXH_PRIME32_1 + XXH_PRIME32_2; + state.v[1] = seed + XXH_PRIME32_2; + state.v[2] = seed + 0; + state.v[3] = seed - XXH_PRIME32_1; + /* do not write into reserved, planned to be removed in a future version */ + XXH_memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved)); + return XXH_OK; } /*! @ingroup xxh32_family */ XXH_PUBLIC_API XXH_errorcode -XXH32_update(XXH32_state_t* state, const void* input, size_t len) -{ - if (input==NULL) { - XXH_ASSERT(len == 0); - return XXH_OK; - } +XXH32_update(XXH32_state_t *state, const void *input, size_t len) { + if (input == NULL) { + XXH_ASSERT(len == 0); + return XXH_OK; + } - { const xxh_u8* p = (const xxh_u8*)input; - const xxh_u8* const bEnd = p + len; + { + const xxh_u8 *p = (const xxh_u8 *) input; + const xxh_u8 *const bEnd = p + len; - state->total_len_32 += (XXH32_hash_t)len; - state->large_len |= (XXH32_hash_t)((len>=16) | (state->total_len_32>=16)); + state->total_len_32 += (XXH32_hash_t) len; + state->large_len |= (XXH32_hash_t) ((len >= 16) | (state->total_len_32 >= 16)); - if (state->memsize + len < 16) { /* fill in tmp buffer */ - XXH_memcpy((xxh_u8*)(state->mem32) + state->memsize, input, len); - state->memsize += (XXH32_hash_t)len; - return XXH_OK; - } + if (state->memsize + len < 16) { /* fill in tmp buffer */ + XXH_memcpy((xxh_u8 *) (state->mem32) + state->memsize, input, len); + state->memsize += (XXH32_hash_t) len; + return XXH_OK; + } - if (state->memsize) { /* some data left from previous update */ - XXH_memcpy((xxh_u8*)(state->mem32) + state->memsize, input, 16-state->memsize); - { const xxh_u32* p32 = state->mem32; - state->v[0] = XXH32_round(state->v[0], XXH_readLE32(p32)); p32++; - state->v[1] = XXH32_round(state->v[1], XXH_readLE32(p32)); p32++; - state->v[2] = XXH32_round(state->v[2], XXH_readLE32(p32)); p32++; - state->v[3] = XXH32_round(state->v[3], XXH_readLE32(p32)); - } - p += 16-state->memsize; - state->memsize = 0; - } + if (state->memsize) { /* some data left from previous update */ + XXH_memcpy((xxh_u8 *) (state->mem32) + state->memsize, input, 16 - state->memsize); + { + const xxh_u32 *p32 = state->mem32; + state->v[0] = XXH32_round(state->v[0], XXH_readLE32(p32)); + p32++; + state->v[1] = XXH32_round(state->v[1], XXH_readLE32(p32)); + p32++; + state->v[2] = XXH32_round(state->v[2], XXH_readLE32(p32)); + p32++; + state->v[3] = XXH32_round(state->v[3], XXH_readLE32(p32)); + } + p += 16 - state->memsize; + state->memsize = 0; + } - if (p <= bEnd-16) { - const xxh_u8* const limit = bEnd - 16; + if (p <= bEnd - 16) { + const xxh_u8 *const limit = bEnd - 16; - do { - state->v[0] = XXH32_round(state->v[0], XXH_readLE32(p)); p+=4; - state->v[1] = XXH32_round(state->v[1], XXH_readLE32(p)); p+=4; - state->v[2] = XXH32_round(state->v[2], XXH_readLE32(p)); p+=4; - state->v[3] = XXH32_round(state->v[3], XXH_readLE32(p)); p+=4; - } while (p<=limit); + do { + state->v[0] = XXH32_round(state->v[0], XXH_readLE32(p)); + p += 4; + state->v[1] = XXH32_round(state->v[1], XXH_readLE32(p)); + p += 4; + state->v[2] = XXH32_round(state->v[2], XXH_readLE32(p)); + p += 4; + state->v[3] = XXH32_round(state->v[3], XXH_readLE32(p)); + p += 4; + } while (p <= limit); - } + } - if (p < bEnd) { - XXH_memcpy(state->mem32, p, (size_t)(bEnd-p)); - state->memsize = (unsigned)(bEnd-p); - } + if (p < bEnd) { + XXH_memcpy(state->mem32, p, (size_t) (bEnd - p)); + state->memsize = (unsigned) (bEnd - p); } + } - return XXH_OK; + return XXH_OK; } /*! @ingroup xxh32_family */ -XXH_PUBLIC_API XXH32_hash_t XXH32_digest(const XXH32_state_t* state) -{ - xxh_u32 h32; +XXH_PUBLIC_API XXH32_hash_t XXH32_digest(const XXH32_state_t *state) { + xxh_u32 h32; - if (state->large_len) { - h32 = XXH_rotl32(state->v[0], 1) - + XXH_rotl32(state->v[1], 7) - + XXH_rotl32(state->v[2], 12) - + XXH_rotl32(state->v[3], 18); - } else { - h32 = state->v[2] /* == seed */ + XXH_PRIME32_5; - } + if (state->large_len) { + h32 = XXH_rotl32(state->v[0], 1) + + XXH_rotl32(state->v[1], 7) + + XXH_rotl32(state->v[2], 12) + + XXH_rotl32(state->v[3], 18); + } else { + h32 = state->v[2] /* == seed */ + XXH_PRIME32_5; + } - h32 += state->total_len_32; + h32 += state->total_len_32; - return XXH32_finalize(h32, (const xxh_u8*)state->mem32, state->memsize, XXH_aligned); + return XXH32_finalize(h32, (const xxh_u8 *) state->mem32, state->memsize, XXH_aligned); } @@ -2261,16 +2307,14 @@ XXH_PUBLIC_API XXH32_hash_t XXH32_digest(const XXH32_state_t* state) * The following functions allow transformation of hash values to and from their * canonical format. */ -XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash) -{ - XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t)); - if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash); - XXH_memcpy(dst, &hash, sizeof(*dst)); +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t *dst, XXH32_hash_t hash) { + XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash); + XXH_memcpy(dst, &hash, sizeof(*dst)); } /*! @ingroup xxh32_family */ -XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src) -{ - return XXH_readBE32(src); +XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t *src) { + return XXH_readBE32(src); } @@ -2292,12 +2336,12 @@ typedef XXH64_hash_t xxh_u64; # define U64 xxh_u64 #endif -#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3)) +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS == 3)) /* * Manual byteshift. Best for old compilers which don't inline memcpy. * We actually directly use XXH_readLE64 and XXH_readBE64. */ -#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS == 2)) /* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ static xxh_u64 XXH_read64(const void* memPtr) @@ -2305,7 +2349,7 @@ static xxh_u64 XXH_read64(const void* memPtr) return *(const xxh_u64*) memPtr; } -#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS == 1)) /* * __pack instructions are safer, but compiler specific, hence potentially @@ -2328,11 +2372,10 @@ static xxh_u64 XXH_read64(const void* ptr) * Portable and safe solution. Generally efficient. * see: http://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html */ -static xxh_u64 XXH_read64(const void* memPtr) -{ - xxh_u64 val; - XXH_memcpy(&val, memPtr, sizeof(val)); - return val; +static xxh_u64 XXH_read64(const void *memPtr) { + xxh_u64 val; + XXH_memcpy(&val, memPtr, sizeof(val)); + return val; } #endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ @@ -2357,7 +2400,7 @@ static xxh_u64 XXH_swap64(xxh_u64 x) /* XXH_FORCE_MEMORY_ACCESS==3 is an endian-independent byteshift load. */ -#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3)) +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS == 3)) XXH_FORCE_INLINE xxh_u64 XXH_readLE64(const void* memPtr) { @@ -2386,24 +2429,23 @@ XXH_FORCE_INLINE xxh_u64 XXH_readBE64(const void* memPtr) } #else -XXH_FORCE_INLINE xxh_u64 XXH_readLE64(const void* ptr) -{ - return XXH_CPU_LITTLE_ENDIAN ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr)); + +XXH_FORCE_INLINE xxh_u64 XXH_readLE64(const void *ptr) { + return XXH_CPU_LITTLE_ENDIAN ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr)); } -static xxh_u64 XXH_readBE64(const void* ptr) -{ - return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr); +static xxh_u64 XXH_readBE64(const void *ptr) { + return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr); } + #endif XXH_FORCE_INLINE xxh_u64 -XXH_readLE64_align(const void* ptr, XXH_alignment align) -{ - if (align==XXH_unaligned) - return XXH_readLE64(ptr); - else - return XXH_CPU_LITTLE_ENDIAN ? *(const xxh_u64*)ptr : XXH_swap64(*(const xxh_u64*)ptr); +XXH_readLE64_align(const void *ptr, XXH_alignment align) { + if (align == XXH_unaligned) + return XXH_readLE64(ptr); + else + return XXH_CPU_LITTLE_ENDIAN ? *(const xxh_u64 *) ptr : XXH_swap64(*(const xxh_u64 *) ptr); } @@ -2429,59 +2471,55 @@ XXH_readLE64_align(const void* ptr, XXH_alignment align) # define PRIME64_5 XXH_PRIME64_5 #endif -static xxh_u64 XXH64_round(xxh_u64 acc, xxh_u64 input) -{ - acc += input * XXH_PRIME64_2; - acc = XXH_rotl64(acc, 31); - acc *= XXH_PRIME64_1; - return acc; +static xxh_u64 XXH64_round(xxh_u64 acc, xxh_u64 input) { + acc += input * XXH_PRIME64_2; + acc = XXH_rotl64(acc, 31); + acc *= XXH_PRIME64_1; + return acc; } -static xxh_u64 XXH64_mergeRound(xxh_u64 acc, xxh_u64 val) -{ - val = XXH64_round(0, val); - acc ^= val; - acc = acc * XXH_PRIME64_1 + XXH_PRIME64_4; - return acc; +static xxh_u64 XXH64_mergeRound(xxh_u64 acc, xxh_u64 val) { + val = XXH64_round(0, val); + acc ^= val; + acc = acc * XXH_PRIME64_1 + XXH_PRIME64_4; + return acc; } -static xxh_u64 XXH64_avalanche(xxh_u64 h64) -{ - h64 ^= h64 >> 33; - h64 *= XXH_PRIME64_2; - h64 ^= h64 >> 29; - h64 *= XXH_PRIME64_3; - h64 ^= h64 >> 32; - return h64; +static xxh_u64 XXH64_avalanche(xxh_u64 h64) { + h64 ^= h64 >> 33; + h64 *= XXH_PRIME64_2; + h64 ^= h64 >> 29; + h64 *= XXH_PRIME64_3; + h64 ^= h64 >> 32; + return h64; } #define XXH_get64bits(p) XXH_readLE64_align(p, align) static xxh_u64 -XXH64_finalize(xxh_u64 h64, const xxh_u8* ptr, size_t len, XXH_alignment align) -{ - if (ptr==NULL) XXH_ASSERT(len == 0); - len &= 31; - while (len >= 8) { - xxh_u64 const k1 = XXH64_round(0, XXH_get64bits(ptr)); - ptr += 8; - h64 ^= k1; - h64 = XXH_rotl64(h64,27) * XXH_PRIME64_1 + XXH_PRIME64_4; - len -= 8; - } - if (len >= 4) { - h64 ^= (xxh_u64)(XXH_get32bits(ptr)) * XXH_PRIME64_1; - ptr += 4; - h64 = XXH_rotl64(h64, 23) * XXH_PRIME64_2 + XXH_PRIME64_3; - len -= 4; - } - while (len > 0) { - h64 ^= (*ptr++) * XXH_PRIME64_5; - h64 = XXH_rotl64(h64, 11) * XXH_PRIME64_1; - --len; - } - return XXH64_avalanche(h64); +XXH64_finalize(xxh_u64 h64, const xxh_u8 *ptr, size_t len, XXH_alignment align) { + if (ptr == NULL) XXH_ASSERT(len == 0); + len &= 31; + while (len >= 8) { + xxh_u64 const k1 = XXH64_round(0, XXH_get64bits(ptr)); + ptr += 8; + h64 ^= k1; + h64 = XXH_rotl64(h64, 27) * XXH_PRIME64_1 + XXH_PRIME64_4; + len -= 8; + } + if (len >= 4) { + h64 ^= (xxh_u64) (XXH_get32bits(ptr)) * XXH_PRIME64_1; + ptr += 4; + h64 = XXH_rotl64(h64, 23) * XXH_PRIME64_2 + XXH_PRIME64_3; + len -= 4; + } + while (len > 0) { + h64 ^= (*ptr++) * XXH_PRIME64_5; + h64 = XXH_rotl64(h64, 11) * XXH_PRIME64_1; + --len; + } + return XXH64_avalanche(h64); } #ifdef XXH_OLD_NAMES @@ -2495,58 +2533,61 @@ XXH64_finalize(xxh_u64 h64, const xxh_u8* ptr, size_t len, XXH_alignment align) #endif XXH_FORCE_INLINE xxh_u64 -XXH64_endian_align(const xxh_u8* input, size_t len, xxh_u64 seed, XXH_alignment align) -{ - xxh_u64 h64; - if (input==NULL) XXH_ASSERT(len == 0); +XXH64_endian_align(const xxh_u8 *input, size_t len, xxh_u64 seed, XXH_alignment align) { + xxh_u64 h64; + if (input == NULL) XXH_ASSERT(len == 0); - if (len>=32) { - const xxh_u8* const bEnd = input + len; - const xxh_u8* const limit = bEnd - 31; - xxh_u64 v1 = seed + XXH_PRIME64_1 + XXH_PRIME64_2; - xxh_u64 v2 = seed + XXH_PRIME64_2; - xxh_u64 v3 = seed + 0; - xxh_u64 v4 = seed - XXH_PRIME64_1; + if (len >= 32) { + const xxh_u8 *const bEnd = input + len; + const xxh_u8 *const limit = bEnd - 31; + xxh_u64 v1 = seed + XXH_PRIME64_1 + XXH_PRIME64_2; + xxh_u64 v2 = seed + XXH_PRIME64_2; + xxh_u64 v3 = seed + 0; + xxh_u64 v4 = seed - XXH_PRIME64_1; - do { - v1 = XXH64_round(v1, XXH_get64bits(input)); input+=8; - v2 = XXH64_round(v2, XXH_get64bits(input)); input+=8; - v3 = XXH64_round(v3, XXH_get64bits(input)); input+=8; - v4 = XXH64_round(v4, XXH_get64bits(input)); input+=8; - } while (inputtotal_len += len; + state->total_len += len; - if (state->memsize + len < 32) { /* fill in tmp buffer */ - XXH_memcpy(((xxh_u8*)state->mem64) + state->memsize, input, len); - state->memsize += (xxh_u32)len; - return XXH_OK; - } + if (state->memsize + len < 32) { /* fill in tmp buffer */ + XXH_memcpy(((xxh_u8 *) state->mem64) + state->memsize, input, len); + state->memsize += (xxh_u32) len; + return XXH_OK; + } - if (state->memsize) { /* tmp buffer is full */ - XXH_memcpy(((xxh_u8*)state->mem64) + state->memsize, input, 32-state->memsize); - state->v[0] = XXH64_round(state->v[0], XXH_readLE64(state->mem64+0)); - state->v[1] = XXH64_round(state->v[1], XXH_readLE64(state->mem64+1)); - state->v[2] = XXH64_round(state->v[2], XXH_readLE64(state->mem64+2)); - state->v[3] = XXH64_round(state->v[3], XXH_readLE64(state->mem64+3)); - p += 32 - state->memsize; - state->memsize = 0; - } + if (state->memsize) { /* tmp buffer is full */ + XXH_memcpy(((xxh_u8 *) state->mem64) + state->memsize, input, 32 - state->memsize); + state->v[0] = XXH64_round(state->v[0], XXH_readLE64(state->mem64 + 0)); + state->v[1] = XXH64_round(state->v[1], XXH_readLE64(state->mem64 + 1)); + state->v[2] = XXH64_round(state->v[2], XXH_readLE64(state->mem64 + 2)); + state->v[3] = XXH64_round(state->v[3], XXH_readLE64(state->mem64 + 3)); + p += 32 - state->memsize; + state->memsize = 0; + } - if (p+32 <= bEnd) { - const xxh_u8* const limit = bEnd - 32; + if (p + 32 <= bEnd) { + const xxh_u8 *const limit = bEnd - 32; - do { - state->v[0] = XXH64_round(state->v[0], XXH_readLE64(p)); p+=8; - state->v[1] = XXH64_round(state->v[1], XXH_readLE64(p)); p+=8; - state->v[2] = XXH64_round(state->v[2], XXH_readLE64(p)); p+=8; - state->v[3] = XXH64_round(state->v[3], XXH_readLE64(p)); p+=8; - } while (p<=limit); + do { + state->v[0] = XXH64_round(state->v[0], XXH_readLE64(p)); + p += 8; + state->v[1] = XXH64_round(state->v[1], XXH_readLE64(p)); + p += 8; + state->v[2] = XXH64_round(state->v[2], XXH_readLE64(p)); + p += 8; + state->v[3] = XXH64_round(state->v[3], XXH_readLE64(p)); + p += 8; + } while (p <= limit); - } + } - if (p < bEnd) { - XXH_memcpy(state->mem64, p, (size_t)(bEnd-p)); - state->memsize = (unsigned)(bEnd-p); - } + if (p < bEnd) { + XXH_memcpy(state->mem64, p, (size_t) (bEnd - p)); + state->memsize = (unsigned) (bEnd - p); } + } - return XXH_OK; + return XXH_OK; } /*! @ingroup xxh64_family */ -XXH_PUBLIC_API XXH64_hash_t XXH64_digest(const XXH64_state_t* state) -{ - xxh_u64 h64; - - if (state->total_len >= 32) { - h64 = XXH_rotl64(state->v[0], 1) + XXH_rotl64(state->v[1], 7) + XXH_rotl64(state->v[2], 12) + XXH_rotl64(state->v[3], 18); - h64 = XXH64_mergeRound(h64, state->v[0]); - h64 = XXH64_mergeRound(h64, state->v[1]); - h64 = XXH64_mergeRound(h64, state->v[2]); - h64 = XXH64_mergeRound(h64, state->v[3]); - } else { - h64 = state->v[2] /*seed*/ + XXH_PRIME64_5; - } +XXH_PUBLIC_API XXH64_hash_t XXH64_digest(const XXH64_state_t *state) { + xxh_u64 h64; - h64 += (xxh_u64) state->total_len; + if (state->total_len >= 32) { + h64 = XXH_rotl64(state->v[0], 1) + XXH_rotl64(state->v[1], 7) + XXH_rotl64(state->v[2], 12) + + XXH_rotl64(state->v[3], 18); + h64 = XXH64_mergeRound(h64, state->v[0]); + h64 = XXH64_mergeRound(h64, state->v[1]); + h64 = XXH64_mergeRound(h64, state->v[2]); + h64 = XXH64_mergeRound(h64, state->v[3]); + } else { + h64 = state->v[2] /*seed*/ + XXH_PRIME64_5; + } - return XXH64_finalize(h64, (const xxh_u8*)state->mem64, (size_t)state->total_len, XXH_aligned); + h64 += (xxh_u64) state->total_len; + + return XXH64_finalize(h64, (const xxh_u8 *) state->mem64, (size_t) state->total_len, XXH_aligned); } /******* Canonical representation *******/ /*! @ingroup xxh64_family */ -XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash) -{ - XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t)); - if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash); - XXH_memcpy(dst, &hash, sizeof(*dst)); +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t *dst, XXH64_hash_t hash) { + XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash); + XXH_memcpy(dst, &hash, sizeof(*dst)); } /*! @ingroup xxh64_family */ -XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src) -{ - return XXH_readBE64(src); +XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t *src) { + return XXH_readBE64(src); } #ifndef XXH_NO_XXH3 @@ -2699,8 +2738,8 @@ XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src #endif #if (defined(__GNUC__) && (__GNUC__ >= 3)) \ - || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) \ - || defined(__clang__) + || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) \ + || defined(__clang__) # define XXH_likely(x) __builtin_expect(x, 1) # define XXH_unlikely(x) __builtin_expect(x, 0) #else @@ -2712,7 +2751,9 @@ XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src # if defined(__AVX2__) # include # elif defined(__SSE2__) + # include + # elif defined(__ARM_NEON__) || defined(__ARM_NEON) # define inline __inline__ /* circumvent a clang bug */ # include @@ -2862,13 +2903,13 @@ enum XXH_VECTOR_TYPE /* fake enum */ { # define XXH_VECTOR XXH_AVX2 # elif defined(__SSE2__) || defined(_M_AMD64) || defined(_M_X64) || (defined(_M_IX86_FP) && (_M_IX86_FP == 2)) # define XXH_VECTOR XXH_SSE2 -# elif ( \ +# elif (\ defined(__ARM_NEON__) || defined(__ARM_NEON) /* gcc */ \ - || defined(_M_ARM64) || defined(_M_ARM_ARMV7VE) /* msvc */ \ - ) && ( \ + || defined(_M_ARM64) || defined(_M_ARM_ARMV7VE) /* msvc */ \ +) && (\ defined(_WIN32) || defined(__LITTLE_ENDIAN__) /* little endian only */ \ - || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) \ - ) + || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) \ +) # define XXH_VECTOR XXH_NEON # elif (defined(__PPC64__) && defined(__POWER8_VECTOR__)) \ || (defined(__s390x__) && defined(__VEC__)) \ @@ -2902,7 +2943,7 @@ enum XXH_VECTOR_TYPE /* fake enum */ { #endif #if defined(XXH_X86DISPATCH) || XXH_VECTOR == XXH_SSE2 \ - || XXH_VECTOR == XXH_AVX2 || XXH_VECTOR == XXH_AVX512 + || XXH_VECTOR == XXH_AVX2 || XXH_VECTOR == XXH_AVX512 # define XXH_SEC_ALIGN XXH_ACC_ALIGN #else # define XXH_SEC_ALIGN 8 @@ -2930,8 +2971,8 @@ enum XXH_VECTOR_TYPE /* fake enum */ { * inline function due to target mismatch" warnings. */ #if XXH_VECTOR == XXH_AVX2 /* AVX2 */ \ - && defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \ - && defined(__OPTIMIZE__) && !defined(__OPTIMIZE_SIZE__) /* respect -O0 and -Os */ + && defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \ + && defined(__OPTIMIZE__) && !defined(__OPTIMIZE_SIZE__) /* respect -O0 and -Os */ # pragma GCC push_options # pragma GCC optimize("-O2") #endif @@ -3151,7 +3192,7 @@ XXH_FORCE_INLINE xxh_u64x2 XXH_vec_mule(xxh_u32x4 a, xxh_u32x4 b) # if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86)) /* _mm_prefetch() not defined outside of x86/x64 */ # include /* https://msdn.microsoft.com/fr-fr/library/84szxsww(v=vs.90).aspx */ # define XXH_PREFETCH(ptr) _mm_prefetch((const char*)(ptr), _MM_HINT_T0) -# elif defined(__GNUC__) && ( (__GNUC__ >= 4) || ( (__GNUC__ == 3) && (__GNUC_MINOR__ >= 1) ) ) +# elif defined(__GNUC__) && ((__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1))) # define XXH_PREFETCH(ptr) __builtin_prefetch((ptr), 0 /* rw==read */, 3 /* locality */) # else # define XXH_PREFETCH(ptr) (void)(ptr) /* disabled */ @@ -3236,126 +3277,125 @@ XXH_mult32to64(xxh_u64 x, xxh_u64 y) * @return The 128-bit result represented in an @ref XXH128_hash_t. */ static XXH128_hash_t -XXH_mult64to128(xxh_u64 lhs, xxh_u64 rhs) -{ - /* - * GCC/Clang __uint128_t method. - * - * On most 64-bit targets, GCC and Clang define a __uint128_t type. - * This is usually the best way as it usually uses a native long 64-bit - * multiply, such as MULQ on x86_64 or MUL + UMULH on aarch64. - * - * Usually. - * - * Despite being a 32-bit platform, Clang (and emscripten) define this type - * despite not having the arithmetic for it. This results in a laggy - * compiler builtin call which calculates a full 128-bit multiply. - * In that case it is best to use the portable one. - * https://github.com/Cyan4973/xxHash/issues/211#issuecomment-515575677 - */ +XXH_mult64to128(xxh_u64 lhs, xxh_u64 rhs) { + /* + * GCC/Clang __uint128_t method. + * + * On most 64-bit targets, GCC and Clang define a __uint128_t type. + * This is usually the best way as it usually uses a native long 64-bit + * multiply, such as MULQ on x86_64 or MUL + UMULH on aarch64. + * + * Usually. + * + * Despite being a 32-bit platform, Clang (and emscripten) define this type + * despite not having the arithmetic for it. This results in a laggy + * compiler builtin call which calculates a full 128-bit multiply. + * In that case it is best to use the portable one. + * https://github.com/Cyan4973/xxHash/issues/211#issuecomment-515575677 + */ #if defined(__GNUC__) && !defined(__wasm__) \ - && defined(__SIZEOF_INT128__) \ - || (defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 128) - - __uint128_t const product = (__uint128_t)lhs * (__uint128_t)rhs; - XXH128_hash_t r128; - r128.low64 = (xxh_u64)(product); - r128.high64 = (xxh_u64)(product >> 64); - return r128; - - /* - * MSVC for x64's _umul128 method. - * - * xxh_u64 _umul128(xxh_u64 Multiplier, xxh_u64 Multiplicand, xxh_u64 *HighProduct); - * - * This compiles to single operand MUL on x64. - */ + && defined(__SIZEOF_INT128__) \ + || (defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 128) + + __uint128_t const product = (__uint128_t) lhs * (__uint128_t) rhs; + XXH128_hash_t r128; + r128.low64 = (xxh_u64) (product); + r128.high64 = (xxh_u64) (product >> 64); + return r128; + + /* + * MSVC for x64's _umul128 method. + * + * xxh_u64 _umul128(xxh_u64 Multiplier, xxh_u64 Multiplicand, xxh_u64 *HighProduct); + * + * This compiles to single operand MUL on x64. + */ #elif defined(_M_X64) || defined(_M_IA64) #ifndef _MSC_VER # pragma intrinsic(_umul128) #endif - xxh_u64 product_high; - xxh_u64 const product_low = _umul128(lhs, rhs, &product_high); - XXH128_hash_t r128; - r128.low64 = product_low; - r128.high64 = product_high; - return r128; - - /* - * MSVC for ARM64's __umulh method. - * - * This compiles to the same MUL + UMULH as GCC/Clang's __uint128_t method. - */ + xxh_u64 product_high; + xxh_u64 const product_low = _umul128(lhs, rhs, &product_high); + XXH128_hash_t r128; + r128.low64 = product_low; + r128.high64 = product_high; + return r128; + + /* + * MSVC for ARM64's __umulh method. + * + * This compiles to the same MUL + UMULH as GCC/Clang's __uint128_t method. + */ #elif defined(_M_ARM64) #ifndef _MSC_VER # pragma intrinsic(__umulh) #endif - XXH128_hash_t r128; - r128.low64 = lhs * rhs; - r128.high64 = __umulh(lhs, rhs); - return r128; + XXH128_hash_t r128; + r128.low64 = lhs * rhs; + r128.high64 = __umulh(lhs, rhs); + return r128; #else - /* - * Portable scalar method. Optimized for 32-bit and 64-bit ALUs. - * - * This is a fast and simple grade school multiply, which is shown below - * with base 10 arithmetic instead of base 0x100000000. - * - * 9 3 // D2 lhs = 93 - * x 7 5 // D2 rhs = 75 - * ---------- - * 1 5 // D2 lo_lo = (93 % 10) * (75 % 10) = 15 - * 4 5 | // D2 hi_lo = (93 / 10) * (75 % 10) = 45 - * 2 1 | // D2 lo_hi = (93 % 10) * (75 / 10) = 21 - * + 6 3 | | // D2 hi_hi = (93 / 10) * (75 / 10) = 63 - * --------- - * 2 7 | // D2 cross = (15 / 10) + (45 % 10) + 21 = 27 - * + 6 7 | | // D2 upper = (27 / 10) + (45 / 10) + 63 = 67 - * --------- - * 6 9 7 5 // D4 res = (27 * 10) + (15 % 10) + (67 * 100) = 6975 - * - * The reasons for adding the products like this are: - * 1. It avoids manual carry tracking. Just like how - * (9 * 9) + 9 + 9 = 99, the same applies with this for UINT64_MAX. - * This avoids a lot of complexity. - * - * 2. It hints for, and on Clang, compiles to, the powerful UMAAL - * instruction available in ARM's Digital Signal Processing extension - * in 32-bit ARMv6 and later, which is shown below: - * - * void UMAAL(xxh_u32 *RdLo, xxh_u32 *RdHi, xxh_u32 Rn, xxh_u32 Rm) - * { - * xxh_u64 product = (xxh_u64)*RdLo * (xxh_u64)*RdHi + Rn + Rm; - * *RdLo = (xxh_u32)(product & 0xFFFFFFFF); - * *RdHi = (xxh_u32)(product >> 32); - * } - * - * This instruction was designed for efficient long multiplication, and - * allows this to be calculated in only 4 instructions at speeds - * comparable to some 64-bit ALUs. - * - * 3. It isn't terrible on other platforms. Usually this will be a couple - * of 32-bit ADD/ADCs. - */ - - /* First calculate all of the cross products. */ - xxh_u64 const lo_lo = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs & 0xFFFFFFFF); - xxh_u64 const hi_lo = XXH_mult32to64(lhs >> 32, rhs & 0xFFFFFFFF); - xxh_u64 const lo_hi = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs >> 32); - xxh_u64 const hi_hi = XXH_mult32to64(lhs >> 32, rhs >> 32); - - /* Now add the products together. These will never overflow. */ - xxh_u64 const cross = (lo_lo >> 32) + (hi_lo & 0xFFFFFFFF) + lo_hi; - xxh_u64 const upper = (hi_lo >> 32) + (cross >> 32) + hi_hi; - xxh_u64 const lower = (cross << 32) | (lo_lo & 0xFFFFFFFF); - - XXH128_hash_t r128; - r128.low64 = lower; - r128.high64 = upper; - return r128; + /* + * Portable scalar method. Optimized for 32-bit and 64-bit ALUs. + * + * This is a fast and simple grade school multiply, which is shown below + * with base 10 arithmetic instead of base 0x100000000. + * + * 9 3 // D2 lhs = 93 + * x 7 5 // D2 rhs = 75 + * ---------- + * 1 5 // D2 lo_lo = (93 % 10) * (75 % 10) = 15 + * 4 5 | // D2 hi_lo = (93 / 10) * (75 % 10) = 45 + * 2 1 | // D2 lo_hi = (93 % 10) * (75 / 10) = 21 + * + 6 3 | | // D2 hi_hi = (93 / 10) * (75 / 10) = 63 + * --------- + * 2 7 | // D2 cross = (15 / 10) + (45 % 10) + 21 = 27 + * + 6 7 | | // D2 upper = (27 / 10) + (45 / 10) + 63 = 67 + * --------- + * 6 9 7 5 // D4 res = (27 * 10) + (15 % 10) + (67 * 100) = 6975 + * + * The reasons for adding the products like this are: + * 1. It avoids manual carry tracking. Just like how + * (9 * 9) + 9 + 9 = 99, the same applies with this for UINT64_MAX. + * This avoids a lot of complexity. + * + * 2. It hints for, and on Clang, compiles to, the powerful UMAAL + * instruction available in ARM's Digital Signal Processing extension + * in 32-bit ARMv6 and later, which is shown below: + * + * void UMAAL(xxh_u32 *RdLo, xxh_u32 *RdHi, xxh_u32 Rn, xxh_u32 Rm) + * { + * xxh_u64 product = (xxh_u64)*RdLo * (xxh_u64)*RdHi + Rn + Rm; + * *RdLo = (xxh_u32)(product & 0xFFFFFFFF); + * *RdHi = (xxh_u32)(product >> 32); + * } + * + * This instruction was designed for efficient long multiplication, and + * allows this to be calculated in only 4 instructions at speeds + * comparable to some 64-bit ALUs. + * + * 3. It isn't terrible on other platforms. Usually this will be a couple + * of 32-bit ADD/ADCs. + */ + + /* First calculate all of the cross products. */ + xxh_u64 const lo_lo = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs & 0xFFFFFFFF); + xxh_u64 const hi_lo = XXH_mult32to64(lhs >> 32, rhs & 0xFFFFFFFF); + xxh_u64 const lo_hi = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs >> 32); + xxh_u64 const hi_hi = XXH_mult32to64(lhs >> 32, rhs >> 32); + + /* Now add the products together. These will never overflow. */ + xxh_u64 const cross = (lo_lo >> 32) + (hi_lo & 0xFFFFFFFF) + lo_hi; + xxh_u64 const upper = (hi_lo >> 32) + (cross >> 32) + hi_hi; + xxh_u64 const lower = (cross << 32) | (lo_lo & 0xFFFFFFFF); + + XXH128_hash_t r128; + r128.low64 = lower; + r128.high64 = upper; + return r128; #endif } @@ -3370,29 +3410,26 @@ XXH_mult64to128(xxh_u64 lhs, xxh_u64 rhs) * @see XXH_mult64to128() */ static xxh_u64 -XXH3_mul128_fold64(xxh_u64 lhs, xxh_u64 rhs) -{ - XXH128_hash_t product = XXH_mult64to128(lhs, rhs); - return product.low64 ^ product.high64; +XXH3_mul128_fold64(xxh_u64 lhs, xxh_u64 rhs) { + XXH128_hash_t product = XXH_mult64to128(lhs, rhs); + return product.low64 ^ product.high64; } /*! Seems to produce slightly better code on GCC for some reason. */ -XXH_FORCE_INLINE xxh_u64 XXH_xorshift64(xxh_u64 v64, int shift) -{ - XXH_ASSERT(0 <= shift && shift < 64); - return v64 ^ (v64 >> shift); +XXH_FORCE_INLINE xxh_u64 XXH_xorshift64(xxh_u64 v64, int shift) { + XXH_ASSERT(0 <= shift && shift < 64); + return v64 ^ (v64 >> shift); } /* * This is a fast avalanche stage, * suitable when input bits are already partially mixed */ -static XXH64_hash_t XXH3_avalanche(xxh_u64 h64) -{ - h64 = XXH_xorshift64(h64, 37); - h64 *= 0x165667919E3779F9ULL; - h64 = XXH_xorshift64(h64, 32); - return h64; +static XXH64_hash_t XXH3_avalanche(xxh_u64 h64) { + h64 = XXH_xorshift64(h64, 37); + h64 *= 0x165667919E3779F9ULL; + h64 = XXH_xorshift64(h64, 32); + return h64; } /* @@ -3400,14 +3437,13 @@ static XXH64_hash_t XXH3_avalanche(xxh_u64 h64) * inspired by Pelle Evensen's rrmxmx * preferable when input has not been previously mixed */ -static XXH64_hash_t XXH3_rrmxmx(xxh_u64 h64, xxh_u64 len) -{ - /* this mix is inspired by Pelle Evensen's rrmxmx */ - h64 ^= XXH_rotl64(h64, 49) ^ XXH_rotl64(h64, 24); - h64 *= 0x9FB21C651E98DF25ULL; - h64 ^= (h64 >> 35) + len ; - h64 *= 0x9FB21C651E98DF25ULL; - return XXH_xorshift64(h64, 28); +static XXH64_hash_t XXH3_rrmxmx(xxh_u64 h64, xxh_u64 len) { + /* this mix is inspired by Pelle Evensen's rrmxmx */ + h64 ^= XXH_rotl64(h64, 49) ^ XXH_rotl64(h64, 24); + h64 *= 0x9FB21C651E98DF25ULL; + h64 ^= (h64 >> 35) + len; + h64 *= 0x9FB21C651E98DF25ULL; + return XXH_xorshift64(h64, 28); } @@ -3445,69 +3481,69 @@ static XXH64_hash_t XXH3_rrmxmx(xxh_u64 h64, xxh_u64 len) * This adds an extra layer of strength for custom secrets. */ XXH_FORCE_INLINE XXH64_hash_t -XXH3_len_1to3_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) -{ - XXH_ASSERT(input != NULL); - XXH_ASSERT(1 <= len && len <= 3); - XXH_ASSERT(secret != NULL); - /* - * len = 1: combined = { input[0], 0x01, input[0], input[0] } - * len = 2: combined = { input[1], 0x02, input[0], input[1] } - * len = 3: combined = { input[2], 0x03, input[0], input[1] } - */ - { xxh_u8 const c1 = input[0]; - xxh_u8 const c2 = input[len >> 1]; - xxh_u8 const c3 = input[len - 1]; - xxh_u32 const combined = ((xxh_u32)c1 << 16) | ((xxh_u32)c2 << 24) - | ((xxh_u32)c3 << 0) | ((xxh_u32)len << 8); - xxh_u64 const bitflip = (XXH_readLE32(secret) ^ XXH_readLE32(secret+4)) + seed; - xxh_u64 const keyed = (xxh_u64)combined ^ bitflip; - return XXH64_avalanche(keyed); - } +XXH3_len_1to3_64b(const xxh_u8 *input, size_t len, const xxh_u8 *secret, XXH64_hash_t seed) { + XXH_ASSERT(input != NULL); + XXH_ASSERT(1 <= len && len <= 3); + XXH_ASSERT(secret != NULL); + /* + * len = 1: combined = { input[0], 0x01, input[0], input[0] } + * len = 2: combined = { input[1], 0x02, input[0], input[1] } + * len = 3: combined = { input[2], 0x03, input[0], input[1] } + */ + { + xxh_u8 const c1 = input[0]; + xxh_u8 const c2 = input[len >> 1]; + xxh_u8 const c3 = input[len - 1]; + xxh_u32 const combined = ((xxh_u32) c1 << 16) | ((xxh_u32) c2 << 24) + | ((xxh_u32) c3 << 0) | ((xxh_u32) len << 8); + xxh_u64 const bitflip = (XXH_readLE32(secret) ^ XXH_readLE32(secret + 4)) + seed; + xxh_u64 const keyed = (xxh_u64) combined ^ bitflip; + return XXH64_avalanche(keyed); + } } XXH_FORCE_INLINE XXH64_hash_t -XXH3_len_4to8_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) -{ - XXH_ASSERT(input != NULL); - XXH_ASSERT(secret != NULL); - XXH_ASSERT(4 <= len && len <= 8); - seed ^= (xxh_u64)XXH_swap32((xxh_u32)seed) << 32; - { xxh_u32 const input1 = XXH_readLE32(input); - xxh_u32 const input2 = XXH_readLE32(input + len - 4); - xxh_u64 const bitflip = (XXH_readLE64(secret+8) ^ XXH_readLE64(secret+16)) - seed; - xxh_u64 const input64 = input2 + (((xxh_u64)input1) << 32); - xxh_u64 const keyed = input64 ^ bitflip; - return XXH3_rrmxmx(keyed, len); - } +XXH3_len_4to8_64b(const xxh_u8 *input, size_t len, const xxh_u8 *secret, XXH64_hash_t seed) { + XXH_ASSERT(input != NULL); + XXH_ASSERT(secret != NULL); + XXH_ASSERT(4 <= len && len <= 8); + seed ^= (xxh_u64) XXH_swap32((xxh_u32) seed) << 32; + { + xxh_u32 const input1 = XXH_readLE32(input); + xxh_u32 const input2 = XXH_readLE32(input + len - 4); + xxh_u64 const bitflip = (XXH_readLE64(secret + 8) ^ XXH_readLE64(secret + 16)) - seed; + xxh_u64 const input64 = input2 + (((xxh_u64) input1) << 32); + xxh_u64 const keyed = input64 ^ bitflip; + return XXH3_rrmxmx(keyed, len); + } } XXH_FORCE_INLINE XXH64_hash_t -XXH3_len_9to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) -{ - XXH_ASSERT(input != NULL); - XXH_ASSERT(secret != NULL); - XXH_ASSERT(9 <= len && len <= 16); - { xxh_u64 const bitflip1 = (XXH_readLE64(secret+24) ^ XXH_readLE64(secret+32)) + seed; - xxh_u64 const bitflip2 = (XXH_readLE64(secret+40) ^ XXH_readLE64(secret+48)) - seed; - xxh_u64 const input_lo = XXH_readLE64(input) ^ bitflip1; - xxh_u64 const input_hi = XXH_readLE64(input + len - 8) ^ bitflip2; - xxh_u64 const acc = len - + XXH_swap64(input_lo) + input_hi - + XXH3_mul128_fold64(input_lo, input_hi); - return XXH3_avalanche(acc); - } +XXH3_len_9to16_64b(const xxh_u8 *input, size_t len, const xxh_u8 *secret, XXH64_hash_t seed) { + XXH_ASSERT(input != NULL); + XXH_ASSERT(secret != NULL); + XXH_ASSERT(9 <= len && len <= 16); + { + xxh_u64 const bitflip1 = (XXH_readLE64(secret + 24) ^ XXH_readLE64(secret + 32)) + seed; + xxh_u64 const bitflip2 = (XXH_readLE64(secret + 40) ^ XXH_readLE64(secret + 48)) - seed; + xxh_u64 const input_lo = XXH_readLE64(input) ^ bitflip1; + xxh_u64 const input_hi = XXH_readLE64(input + len - 8) ^ bitflip2; + xxh_u64 const acc = len + + XXH_swap64(input_lo) + input_hi + + XXH3_mul128_fold64(input_lo, input_hi); + return XXH3_avalanche(acc); + } } XXH_FORCE_INLINE XXH64_hash_t -XXH3_len_0to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) -{ - XXH_ASSERT(len <= 16); - { if (XXH_likely(len > 8)) return XXH3_len_9to16_64b(input, len, secret, seed); - if (XXH_likely(len >= 4)) return XXH3_len_4to8_64b(input, len, secret, seed); - if (len) return XXH3_len_1to3_64b(input, len, secret, seed); - return XXH64_avalanche(seed ^ (XXH_readLE64(secret+56) ^ XXH_readLE64(secret+64))); - } +XXH3_len_0to16_64b(const xxh_u8 *input, size_t len, const xxh_u8 *secret, XXH64_hash_t seed) { + XXH_ASSERT(len <= 16); + { + if (XXH_likely(len > 8)) return XXH3_len_9to16_64b(input, len, secret, seed); + if (XXH_likely(len >= 4)) return XXH3_len_4to8_64b(input, len, secret, seed); + if (len) return XXH3_len_1to3_64b(input, len, secret, seed); + return XXH64_avalanche(seed ^ (XXH_readLE64(secret + 56) ^ XXH_readLE64(secret + 64))); + } } /* @@ -3536,120 +3572,122 @@ XXH3_len_0to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_ * by this, although it is always a good idea to use a proper seed if you care * about strength. */ -XXH_FORCE_INLINE xxh_u64 XXH3_mix16B(const xxh_u8* XXH_RESTRICT input, - const xxh_u8* XXH_RESTRICT secret, xxh_u64 seed64) -{ +XXH_FORCE_INLINE xxh_u64 XXH3_mix16B(const xxh_u8 *XXH_RESTRICT input, + const xxh_u8 *XXH_RESTRICT secret, xxh_u64 seed64) { #if defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \ - && defined(__i386__) && defined(__SSE2__) /* x86 + SSE2 */ \ - && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable like XXH32 hack */ - /* - * UGLY HACK: - * GCC for x86 tends to autovectorize the 128-bit multiply, resulting in - * slower code. - * - * By forcing seed64 into a register, we disrupt the cost model and - * cause it to scalarize. See `XXH32_round()` - * - * FIXME: Clang's output is still _much_ faster -- On an AMD Ryzen 3600, - * XXH3_64bits @ len=240 runs at 4.6 GB/s with Clang 9, but 3.3 GB/s on - * GCC 9.2, despite both emitting scalar code. - * - * GCC generates much better scalar code than Clang for the rest of XXH3, - * which is why finding a more optimal codepath is an interest. - */ - XXH_COMPILER_GUARD(seed64); + && defined(__i386__) && defined(__SSE2__) /* x86 + SSE2 */ \ + && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable like XXH32 hack */ + /* + * UGLY HACK: + * GCC for x86 tends to autovectorize the 128-bit multiply, resulting in + * slower code. + * + * By forcing seed64 into a register, we disrupt the cost model and + * cause it to scalarize. See `XXH32_round()` + * + * FIXME: Clang's output is still _much_ faster -- On an AMD Ryzen 3600, + * XXH3_64bits @ len=240 runs at 4.6 GB/s with Clang 9, but 3.3 GB/s on + * GCC 9.2, despite both emitting scalar code. + * + * GCC generates much better scalar code than Clang for the rest of XXH3, + * which is why finding a more optimal codepath is an interest. + */ + XXH_COMPILER_GUARD(seed64); #endif - { xxh_u64 const input_lo = XXH_readLE64(input); - xxh_u64 const input_hi = XXH_readLE64(input+8); - return XXH3_mul128_fold64( - input_lo ^ (XXH_readLE64(secret) + seed64), - input_hi ^ (XXH_readLE64(secret+8) - seed64) - ); - } + { + xxh_u64 const input_lo = XXH_readLE64(input); + xxh_u64 const input_hi = XXH_readLE64(input + 8); + return XXH3_mul128_fold64( + input_lo ^ (XXH_readLE64(secret) + seed64), + input_hi ^ (XXH_readLE64(secret + 8) - seed64) + ); + } } /* For mid range keys, XXH3 uses a Mum-hash variant. */ XXH_FORCE_INLINE XXH64_hash_t -XXH3_len_17to128_64b(const xxh_u8* XXH_RESTRICT input, size_t len, - const xxh_u8* XXH_RESTRICT secret, size_t secretSize, - XXH64_hash_t seed) -{ - XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize; - XXH_ASSERT(16 < len && len <= 128); - - { xxh_u64 acc = len * XXH_PRIME64_1; - if (len > 32) { - if (len > 64) { - if (len > 96) { - acc += XXH3_mix16B(input+48, secret+96, seed); - acc += XXH3_mix16B(input+len-64, secret+112, seed); - } - acc += XXH3_mix16B(input+32, secret+64, seed); - acc += XXH3_mix16B(input+len-48, secret+80, seed); - } - acc += XXH3_mix16B(input+16, secret+32, seed); - acc += XXH3_mix16B(input+len-32, secret+48, seed); +XXH3_len_17to128_64b(const xxh_u8 *XXH_RESTRICT input, size_t len, + const xxh_u8 *XXH_RESTRICT secret, size_t secretSize, + XXH64_hash_t seed) { + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); + (void) secretSize; + XXH_ASSERT(16 < len && len <= 128); + + { + xxh_u64 acc = len * XXH_PRIME64_1; + if (len > 32) { + if (len > 64) { + if (len > 96) { + acc += XXH3_mix16B(input + 48, secret + 96, seed); + acc += XXH3_mix16B(input + len - 64, secret + 112, seed); } - acc += XXH3_mix16B(input+0, secret+0, seed); - acc += XXH3_mix16B(input+len-16, secret+16, seed); - - return XXH3_avalanche(acc); + acc += XXH3_mix16B(input + 32, secret + 64, seed); + acc += XXH3_mix16B(input + len - 48, secret + 80, seed); + } + acc += XXH3_mix16B(input + 16, secret + 32, seed); + acc += XXH3_mix16B(input + len - 32, secret + 48, seed); } + acc += XXH3_mix16B(input + 0, secret + 0, seed); + acc += XXH3_mix16B(input + len - 16, secret + 16, seed); + + return XXH3_avalanche(acc); + } } #define XXH3_MIDSIZE_MAX 240 XXH_NO_INLINE XXH64_hash_t -XXH3_len_129to240_64b(const xxh_u8* XXH_RESTRICT input, size_t len, - const xxh_u8* XXH_RESTRICT secret, size_t secretSize, - XXH64_hash_t seed) -{ - XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize; - XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX); - - #define XXH3_MIDSIZE_STARTOFFSET 3 - #define XXH3_MIDSIZE_LASTOFFSET 17 - - { xxh_u64 acc = len * XXH_PRIME64_1; - int const nbRounds = (int)len / 16; - int i; - for (i=0; i<8; i++) { - acc += XXH3_mix16B(input+(16*i), secret+(16*i), seed); - } - acc = XXH3_avalanche(acc); - XXH_ASSERT(nbRounds >= 8); +XXH3_len_129to240_64b(const xxh_u8 *XXH_RESTRICT input, size_t len, + const xxh_u8 *XXH_RESTRICT secret, size_t secretSize, + XXH64_hash_t seed) { + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); + (void) secretSize; + XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX); + +#define XXH3_MIDSIZE_STARTOFFSET 3 +#define XXH3_MIDSIZE_LASTOFFSET 17 + + { + xxh_u64 acc = len * XXH_PRIME64_1; + int const nbRounds = (int) len / 16; + int i; + for (i = 0; i < 8; i++) { + acc += XXH3_mix16B(input + (16 * i), secret + (16 * i), seed); + } + acc = XXH3_avalanche(acc); + XXH_ASSERT(nbRounds >= 8); #if defined(__clang__) /* Clang */ \ - && (defined(__ARM_NEON) || defined(__ARM_NEON__)) /* NEON */ \ - && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable */ - /* - * UGLY HACK: - * Clang for ARMv7-A tries to vectorize this loop, similar to GCC x86. - * In everywhere else, it uses scalar code. - * - * For 64->128-bit multiplies, even if the NEON was 100% optimal, it - * would still be slower than UMAAL (see XXH_mult64to128). - * - * Unfortunately, Clang doesn't handle the long multiplies properly and - * converts them to the nonexistent "vmulq_u64" intrinsic, which is then - * scalarized into an ugly mess of VMOV.32 instructions. - * - * This mess is difficult to avoid without turning autovectorization - * off completely, but they are usually relatively minor and/or not - * worth it to fix. - * - * This loop is the easiest to fix, as unlike XXH32, this pragma - * _actually works_ because it is a loop vectorization instead of an - * SLP vectorization. - */ - #pragma clang loop vectorize(disable) + && (defined(__ARM_NEON) || defined(__ARM_NEON__)) /* NEON */ \ + && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable */ + /* + * UGLY HACK: + * Clang for ARMv7-A tries to vectorize this loop, similar to GCC x86. + * In everywhere else, it uses scalar code. + * + * For 64->128-bit multiplies, even if the NEON was 100% optimal, it + * would still be slower than UMAAL (see XXH_mult64to128). + * + * Unfortunately, Clang doesn't handle the long multiplies properly and + * converts them to the nonexistent "vmulq_u64" intrinsic, which is then + * scalarized into an ugly mess of VMOV.32 instructions. + * + * This mess is difficult to avoid without turning autovectorization + * off completely, but they are usually relatively minor and/or not + * worth it to fix. + * + * This loop is the easiest to fix, as unlike XXH32, this pragma + * _actually works_ because it is a loop vectorization instead of an + * SLP vectorization. + */ +#pragma clang loop vectorize(disable) #endif - for (i=8 ; i < nbRounds; i++) { - acc += XXH3_mix16B(input+(16*i), secret+(16*(i-8)) + XXH3_MIDSIZE_STARTOFFSET, seed); - } - /* last bytes */ - acc += XXH3_mix16B(input + len - 16, secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET, seed); - return XXH3_avalanche(acc); + for (i = 8; i < nbRounds; i++) { + acc += XXH3_mix16B(input + (16 * i), secret + (16 * (i - 8)) + XXH3_MIDSIZE_STARTOFFSET, seed); } + /* last bytes */ + acc += XXH3_mix16B(input + len - 16, secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET, seed); + return XXH3_avalanche(acc); + } } @@ -3664,10 +3702,9 @@ XXH3_len_129to240_64b(const xxh_u8* XXH_RESTRICT input, size_t len, # define ACC_NB XXH_ACC_NB #endif -XXH_FORCE_INLINE void XXH_writeLE64(void* dst, xxh_u64 v64) -{ - if (!XXH_CPU_LITTLE_ENDIAN) v64 = XXH_swap64(v64); - XXH_memcpy(dst, &v64, sizeof(v64)); +XXH_FORCE_INLINE void XXH_writeLE64(void *dst, xxh_u64 v64) { + if (!XXH_CPU_LITTLE_ENDIAN) v64 = XXH_swap64(v64); + XXH_memcpy(dst, &v64, sizeof(v64)); } /* Several intrinsic functions below are supposed to accept __int64 as argument, @@ -3676,12 +3713,12 @@ XXH_FORCE_INLINE void XXH_writeLE64(void* dst, xxh_u64 v64) * requiring a workaround. */ #if !defined (__VMS) \ - && (defined (__cplusplus) \ - || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) - typedef int64_t xxh_i64; + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)) +typedef int64_t xxh_i64; #else - /* the following type must have a width of 64-bit */ - typedef long long xxh_i64; +/* the following type must have a width of 64-bit */ +typedef long long xxh_i64; #endif /* @@ -3708,7 +3745,7 @@ XXH_FORCE_INLINE void XXH_writeLE64(void* dst, xxh_u64 v64) */ #if (XXH_VECTOR == XXH_AVX512) \ - || (defined(XXH_DISPATCH_AVX512) && XXH_DISPATCH_AVX512 != 0) + || (defined(XXH_DISPATCH_AVX512) && XXH_DISPATCH_AVX512 != 0) #ifndef XXH_TARGET_AVX512 # define XXH_TARGET_AVX512 /* disable attribute target */ @@ -3817,7 +3854,7 @@ XXH3_initCustomSecret_avx512(void* XXH_RESTRICT customSecret, xxh_u64 seed64) #endif #if (XXH_VECTOR == XXH_AVX2) \ - || (defined(XXH_DISPATCH_AVX2) && XXH_DISPATCH_AVX2 != 0) + || (defined(XXH_DISPATCH_AVX2) && XXH_DISPATCH_AVX2 != 0) #ifndef XXH_TARGET_AVX2 # define XXH_TARGET_AVX2 /* disable attribute target */ @@ -3929,100 +3966,102 @@ XXH_FORCE_INLINE XXH_TARGET_AVX2 void XXH3_initCustomSecret_avx2(void* XXH_RESTR #endif XXH_FORCE_INLINE XXH_TARGET_SSE2 void -XXH3_accumulate_512_sse2( void* XXH_RESTRICT acc, - const void* XXH_RESTRICT input, - const void* XXH_RESTRICT secret) -{ - /* SSE2 is just a half-scale version of the AVX2 version. */ - XXH_ASSERT((((size_t)acc) & 15) == 0); - { __m128i* const xacc = (__m128i *) acc; - /* Unaligned. This is mainly for pointer arithmetic, and because - * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ - const __m128i* const xinput = (const __m128i *) input; - /* Unaligned. This is mainly for pointer arithmetic, and because - * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ - const __m128i* const xsecret = (const __m128i *) secret; +XXH3_accumulate_512_sse2(void *XXH_RESTRICT acc, + const void *XXH_RESTRICT input, + const void *XXH_RESTRICT secret) { + /* SSE2 is just a half-scale version of the AVX2 version. */ + XXH_ASSERT((((size_t) acc) & 15) == 0); + { + __m128i *const xacc = (__m128i *) acc; + /* Unaligned. This is mainly for pointer arithmetic, and because + * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ + const __m128i *const xinput = (const __m128i *) input; + /* Unaligned. This is mainly for pointer arithmetic, and because + * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ + const __m128i *const xsecret = (const __m128i *) secret; - size_t i; - for (i=0; i < XXH_STRIPE_LEN/sizeof(__m128i); i++) { - /* data_vec = xinput[i]; */ - __m128i const data_vec = _mm_loadu_si128 (xinput+i); - /* key_vec = xsecret[i]; */ - __m128i const key_vec = _mm_loadu_si128 (xsecret+i); - /* data_key = data_vec ^ key_vec; */ - __m128i const data_key = _mm_xor_si128 (data_vec, key_vec); - /* data_key_lo = data_key >> 32; */ - __m128i const data_key_lo = _mm_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1)); - /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ - __m128i const product = _mm_mul_epu32 (data_key, data_key_lo); - /* xacc[i] += swap(data_vec); */ - __m128i const data_swap = _mm_shuffle_epi32(data_vec, _MM_SHUFFLE(1,0,3,2)); - __m128i const sum = _mm_add_epi64(xacc[i], data_swap); - /* xacc[i] += product; */ - xacc[i] = _mm_add_epi64(product, sum); - } } + size_t i; + for (i = 0; i < XXH_STRIPE_LEN / sizeof(__m128i); i++) { + /* data_vec = xinput[i]; */ + __m128i const data_vec = _mm_loadu_si128(xinput + i); + /* key_vec = xsecret[i]; */ + __m128i const key_vec = _mm_loadu_si128(xsecret + i); + /* data_key = data_vec ^ key_vec; */ + __m128i const data_key = _mm_xor_si128(data_vec, key_vec); + /* data_key_lo = data_key >> 32; */ + __m128i const data_key_lo = _mm_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1)); + /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ + __m128i const product = _mm_mul_epu32(data_key, data_key_lo); + /* xacc[i] += swap(data_vec); */ + __m128i const data_swap = _mm_shuffle_epi32(data_vec, _MM_SHUFFLE(1, 0, 3, 2)); + __m128i const sum = _mm_add_epi64(xacc[i], data_swap); + /* xacc[i] += product; */ + xacc[i] = _mm_add_epi64(product, sum); + } + } } XXH_FORCE_INLINE XXH_TARGET_SSE2 void -XXH3_scrambleAcc_sse2(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) -{ - XXH_ASSERT((((size_t)acc) & 15) == 0); - { __m128i* const xacc = (__m128i*) acc; - /* Unaligned. This is mainly for pointer arithmetic, and because - * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ - const __m128i* const xsecret = (const __m128i *) secret; - const __m128i prime32 = _mm_set1_epi32((int)XXH_PRIME32_1); - - size_t i; - for (i=0; i < XXH_STRIPE_LEN/sizeof(__m128i); i++) { - /* xacc[i] ^= (xacc[i] >> 47) */ - __m128i const acc_vec = xacc[i]; - __m128i const shifted = _mm_srli_epi64 (acc_vec, 47); - __m128i const data_vec = _mm_xor_si128 (acc_vec, shifted); - /* xacc[i] ^= xsecret[i]; */ - __m128i const key_vec = _mm_loadu_si128 (xsecret+i); - __m128i const data_key = _mm_xor_si128 (data_vec, key_vec); +XXH3_scrambleAcc_sse2(void *XXH_RESTRICT acc, const void *XXH_RESTRICT secret) { + XXH_ASSERT((((size_t) acc) & 15) == 0); + { + __m128i *const xacc = (__m128i *) acc; + /* Unaligned. This is mainly for pointer arithmetic, and because + * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ + const __m128i *const xsecret = (const __m128i *) secret; + const __m128i prime32 = _mm_set1_epi32((int) XXH_PRIME32_1); - /* xacc[i] *= XXH_PRIME32_1; */ - __m128i const data_key_hi = _mm_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1)); - __m128i const prod_lo = _mm_mul_epu32 (data_key, prime32); - __m128i const prod_hi = _mm_mul_epu32 (data_key_hi, prime32); - xacc[i] = _mm_add_epi64(prod_lo, _mm_slli_epi64(prod_hi, 32)); - } + size_t i; + for (i = 0; i < XXH_STRIPE_LEN / sizeof(__m128i); i++) { + /* xacc[i] ^= (xacc[i] >> 47) */ + __m128i const acc_vec = xacc[i]; + __m128i const shifted = _mm_srli_epi64(acc_vec, 47); + __m128i const data_vec = _mm_xor_si128(acc_vec, shifted); + /* xacc[i] ^= xsecret[i]; */ + __m128i const key_vec = _mm_loadu_si128(xsecret + i); + __m128i const data_key = _mm_xor_si128(data_vec, key_vec); + + /* xacc[i] *= XXH_PRIME32_1; */ + __m128i const data_key_hi = _mm_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1)); + __m128i const prod_lo = _mm_mul_epu32(data_key, prime32); + __m128i const prod_hi = _mm_mul_epu32(data_key_hi, prime32); + xacc[i] = _mm_add_epi64(prod_lo, _mm_slli_epi64(prod_hi, 32)); } + } } -XXH_FORCE_INLINE XXH_TARGET_SSE2 void XXH3_initCustomSecret_sse2(void* XXH_RESTRICT customSecret, xxh_u64 seed64) -{ - XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0); - (void)(&XXH_writeLE64); - { int const nbRounds = XXH_SECRET_DEFAULT_SIZE / sizeof(__m128i); +XXH_FORCE_INLINE XXH_TARGET_SSE2 void XXH3_initCustomSecret_sse2(void *XXH_RESTRICT customSecret, xxh_u64 seed64) { + XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0); + (void) (&XXH_writeLE64); + { + int const nbRounds = XXH_SECRET_DEFAULT_SIZE / sizeof(__m128i); # if defined(_MSC_VER) && defined(_M_IX86) && _MSC_VER < 1900 - /* MSVC 32bit mode does not support _mm_set_epi64x before 2015 */ - XXH_ALIGN(16) const xxh_i64 seed64x2[2] = { (xxh_i64)seed64, (xxh_i64)(0U - seed64) }; - __m128i const seed = _mm_load_si128((__m128i const*)seed64x2); + /* MSVC 32bit mode does not support _mm_set_epi64x before 2015 */ + XXH_ALIGN(16) const xxh_i64 seed64x2[2] = { (xxh_i64)seed64, (xxh_i64)(0U - seed64) }; + __m128i const seed = _mm_load_si128((__m128i const*)seed64x2); # else - __m128i const seed = _mm_set_epi64x((xxh_i64)(0U - seed64), (xxh_i64)seed64); + __m128i const seed = _mm_set_epi64x((xxh_i64) (0U - seed64), (xxh_i64) seed64); # endif - int i; + int i; - const void* const src16 = XXH3_kSecret; - __m128i* dst16 = (__m128i*) customSecret; + const void *const src16 = XXH3_kSecret; + __m128i *dst16 = (__m128i *) customSecret; # if defined(__GNUC__) || defined(__clang__) - /* - * On GCC & Clang, marking 'dest' as modified will cause the compiler: - * - do not extract the secret from sse registers in the internal loop - * - use less common registers, and avoid pushing these reg into stack - */ - XXH_COMPILER_GUARD(dst16); + /* + * On GCC & Clang, marking 'dest' as modified will cause the compiler: + * - do not extract the secret from sse registers in the internal loop + * - use less common registers, and avoid pushing these reg into stack + */ + XXH_COMPILER_GUARD(dst16); # endif - XXH_ASSERT(((size_t)src16 & 15) == 0); /* control alignment */ - XXH_ASSERT(((size_t)dst16 & 15) == 0); + XXH_ASSERT(((size_t) src16 & 15) == 0); /* control alignment */ + XXH_ASSERT(((size_t) dst16 & 15) == 0); - for (i=0; i < nbRounds; ++i) { - dst16[i] = _mm_add_epi64(_mm_load_si128((const __m128i *)src16+i), seed); - } } + for (i = 0; i < nbRounds; ++i) { + dst16[i] = _mm_add_epi64(_mm_load_si128((const __m128i *) src16 + i), seed); + } + } } #endif @@ -4194,108 +4233,109 @@ XXH3_scrambleAcc_vsx(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) /* scalar variants - universal */ XXH_FORCE_INLINE void -XXH3_accumulate_512_scalar(void* XXH_RESTRICT acc, - const void* XXH_RESTRICT input, - const void* XXH_RESTRICT secret) -{ - xxh_u64* const xacc = (xxh_u64*) acc; /* presumed aligned */ - const xxh_u8* const xinput = (const xxh_u8*) input; /* no alignment restriction */ - const xxh_u8* const xsecret = (const xxh_u8*) secret; /* no alignment restriction */ - size_t i; - XXH_ASSERT(((size_t)acc & (XXH_ACC_ALIGN-1)) == 0); - for (i=0; i < XXH_ACC_NB; i++) { - xxh_u64 const data_val = XXH_readLE64(xinput + 8*i); - xxh_u64 const data_key = data_val ^ XXH_readLE64(xsecret + i*8); - xacc[i ^ 1] += data_val; /* swap adjacent lanes */ - xacc[i] += XXH_mult32to64(data_key & 0xFFFFFFFF, data_key >> 32); - } +XXH3_accumulate_512_scalar(void *XXH_RESTRICT acc, + const void *XXH_RESTRICT input, + const void *XXH_RESTRICT secret) { + xxh_u64 *const xacc = (xxh_u64 *) acc; /* presumed aligned */ + const xxh_u8 *const xinput = (const xxh_u8 *) input; /* no alignment restriction */ + const xxh_u8 *const xsecret = (const xxh_u8 *) secret; /* no alignment restriction */ + size_t i; + XXH_ASSERT(((size_t) acc & (XXH_ACC_ALIGN - 1)) == 0); + for (i = 0; i < XXH_ACC_NB; i++) { + xxh_u64 const data_val = XXH_readLE64(xinput + 8 * i); + xxh_u64 const data_key = data_val ^ XXH_readLE64(xsecret + i * 8); + xacc[i ^ 1] += data_val; /* swap adjacent lanes */ + xacc[i] += XXH_mult32to64(data_key & 0xFFFFFFFF, data_key >> 32); + } } XXH_FORCE_INLINE void -XXH3_scrambleAcc_scalar(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) -{ - xxh_u64* const xacc = (xxh_u64*) acc; /* presumed aligned */ - const xxh_u8* const xsecret = (const xxh_u8*) secret; /* no alignment restriction */ - size_t i; - XXH_ASSERT((((size_t)acc) & (XXH_ACC_ALIGN-1)) == 0); - for (i=0; i < XXH_ACC_NB; i++) { - xxh_u64 const key64 = XXH_readLE64(xsecret + 8*i); - xxh_u64 acc64 = xacc[i]; - acc64 = XXH_xorshift64(acc64, 47); - acc64 ^= key64; - acc64 *= XXH_PRIME32_1; - xacc[i] = acc64; - } +XXH3_scrambleAcc_scalar(void *XXH_RESTRICT acc, const void *XXH_RESTRICT secret) { + xxh_u64 *const xacc = (xxh_u64 *) acc; /* presumed aligned */ + const xxh_u8 *const xsecret = (const xxh_u8 *) secret; /* no alignment restriction */ + size_t i; + XXH_ASSERT((((size_t) acc) & (XXH_ACC_ALIGN - 1)) == 0); + for (i = 0; i < XXH_ACC_NB; i++) { + xxh_u64 const key64 = XXH_readLE64(xsecret + 8 * i); + xxh_u64 acc64 = xacc[i]; + acc64 = XXH_xorshift64(acc64, 47); + acc64 ^= key64; + acc64 *= XXH_PRIME32_1; + xacc[i] = acc64; + } } XXH_FORCE_INLINE void -XXH3_initCustomSecret_scalar(void* XXH_RESTRICT customSecret, xxh_u64 seed64) -{ - /* - * We need a separate pointer for the hack below, - * which requires a non-const pointer. - * Any decent compiler will optimize this out otherwise. - */ - const xxh_u8* kSecretPtr = XXH3_kSecret; - XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0); +XXH3_initCustomSecret_scalar(void *XXH_RESTRICT customSecret, xxh_u64 seed64) { + /* + * We need a separate pointer for the hack below, + * which requires a non-const pointer. + * Any decent compiler will optimize this out otherwise. + */ + const xxh_u8 *kSecretPtr = XXH3_kSecret; + XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0); #if defined(__clang__) && defined(__aarch64__) - /* - * UGLY HACK: - * Clang generates a bunch of MOV/MOVK pairs for aarch64, and they are - * placed sequentially, in order, at the top of the unrolled loop. - * - * While MOVK is great for generating constants (2 cycles for a 64-bit - * constant compared to 4 cycles for LDR), long MOVK chains stall the - * integer pipelines: - * I L S - * MOVK - * MOVK - * MOVK - * MOVK - * ADD - * SUB STR - * STR - * By forcing loads from memory (as the asm line causes Clang to assume - * that XXH3_kSecretPtr has been changed), the pipelines are used more - * efficiently: - * I L S - * LDR - * ADD LDR - * SUB STR - * STR - * XXH3_64bits_withSeed, len == 256, Snapdragon 835 - * without hack: 2654.4 MB/s - * with hack: 3202.9 MB/s - */ - XXH_COMPILER_GUARD(kSecretPtr); + /* + * UGLY HACK: + * Clang generates a bunch of MOV/MOVK pairs for aarch64, and they are + * placed sequentially, in order, at the top of the unrolled loop. + * + * While MOVK is great for generating constants (2 cycles for a 64-bit + * constant compared to 4 cycles for LDR), long MOVK chains stall the + * integer pipelines: + * I L S + * MOVK + * MOVK + * MOVK + * MOVK + * ADD + * SUB STR + * STR + * By forcing loads from memory (as the asm line causes Clang to assume + * that XXH3_kSecretPtr has been changed), the pipelines are used more + * efficiently: + * I L S + * LDR + * ADD LDR + * SUB STR + * STR + * XXH3_64bits_withSeed, len == 256, Snapdragon 835 + * without hack: 2654.4 MB/s + * with hack: 3202.9 MB/s + */ + XXH_COMPILER_GUARD(kSecretPtr); #endif - /* - * Note: in debug mode, this overrides the asm optimization - * and Clang will emit MOVK chains again. - */ - XXH_ASSERT(kSecretPtr == XXH3_kSecret); - - { int const nbRounds = XXH_SECRET_DEFAULT_SIZE / 16; - int i; - for (i=0; i < nbRounds; i++) { - /* - * The asm hack causes Clang to assume that kSecretPtr aliases with - * customSecret, and on aarch64, this prevented LDP from merging two - * loads together for free. Putting the loads together before the stores - * properly generates LDP. - */ - xxh_u64 lo = XXH_readLE64(kSecretPtr + 16*i) + seed64; - xxh_u64 hi = XXH_readLE64(kSecretPtr + 16*i + 8) - seed64; - XXH_writeLE64((xxh_u8*)customSecret + 16*i, lo); - XXH_writeLE64((xxh_u8*)customSecret + 16*i + 8, hi); - } } + /* + * Note: in debug mode, this overrides the asm optimization + * and Clang will emit MOVK chains again. + */ + XXH_ASSERT(kSecretPtr == XXH3_kSecret); + + { + int const nbRounds = XXH_SECRET_DEFAULT_SIZE / 16; + int i; + for (i = 0; i < nbRounds; i++) { + /* + * The asm hack causes Clang to assume that kSecretPtr aliases with + * customSecret, and on aarch64, this prevented LDP from merging two + * loads together for free. Putting the loads together before the stores + * properly generates LDP. + */ + xxh_u64 lo = XXH_readLE64(kSecretPtr + 16 * i) + seed64; + xxh_u64 hi = XXH_readLE64(kSecretPtr + 16 * i + 8) - seed64; + XXH_writeLE64((xxh_u8 *) customSecret + 16 * i, lo); + XXH_writeLE64((xxh_u8 *) customSecret + 16 * i + 8, hi); + } + } } -typedef void (*XXH3_f_accumulate_512)(void* XXH_RESTRICT, const void*, const void*); -typedef void (*XXH3_f_scrambleAcc)(void* XXH_RESTRICT, const void*); -typedef void (*XXH3_f_initCustomSecret)(void* XXH_RESTRICT, xxh_u64); +typedef void (*XXH3_f_accumulate_512)(void *XXH_RESTRICT, const void *, const void *); + +typedef void (*XXH3_f_scrambleAcc)(void *XXH_RESTRICT, const void *); + +typedef void (*XXH3_f_initCustomSecret)(void *XXH_RESTRICT, xxh_u64); #if (XXH_VECTOR == XXH_AVX512) @@ -4337,7 +4377,6 @@ typedef void (*XXH3_f_initCustomSecret)(void* XXH_RESTRICT, xxh_u64); #endif - #ifndef XXH_PREFETCH_DIST # ifdef __clang__ # define XXH_PREFETCH_DIST 320 @@ -4356,109 +4395,108 @@ typedef void (*XXH3_f_initCustomSecret)(void* XXH_RESTRICT, xxh_u64); * Assumption: nbStripes will not overflow the secret size */ XXH_FORCE_INLINE void -XXH3_accumulate( xxh_u64* XXH_RESTRICT acc, - const xxh_u8* XXH_RESTRICT input, - const xxh_u8* XXH_RESTRICT secret, - size_t nbStripes, - XXH3_f_accumulate_512 f_acc512) -{ - size_t n; - for (n = 0; n < nbStripes; n++ ) { - const xxh_u8* const in = input + n*XXH_STRIPE_LEN; - XXH_PREFETCH(in + XXH_PREFETCH_DIST); - f_acc512(acc, - in, - secret + n*XXH_SECRET_CONSUME_RATE); - } +XXH3_accumulate(xxh_u64 *XXH_RESTRICT acc, + const xxh_u8 *XXH_RESTRICT input, + const xxh_u8 *XXH_RESTRICT secret, + size_t nbStripes, + XXH3_f_accumulate_512 f_acc512) { + size_t n; + for (n = 0; n < nbStripes; n++) { + const xxh_u8 *const in = input + n * XXH_STRIPE_LEN; + XXH_PREFETCH(in + XXH_PREFETCH_DIST); + f_acc512(acc, + in, + secret + n * XXH_SECRET_CONSUME_RATE); + } } XXH_FORCE_INLINE void -XXH3_hashLong_internal_loop(xxh_u64* XXH_RESTRICT acc, - const xxh_u8* XXH_RESTRICT input, size_t len, - const xxh_u8* XXH_RESTRICT secret, size_t secretSize, +XXH3_hashLong_internal_loop(xxh_u64 *XXH_RESTRICT acc, + const xxh_u8 *XXH_RESTRICT input, size_t len, + const xxh_u8 *XXH_RESTRICT secret, size_t secretSize, XXH3_f_accumulate_512 f_acc512, - XXH3_f_scrambleAcc f_scramble) -{ - size_t const nbStripesPerBlock = (secretSize - XXH_STRIPE_LEN) / XXH_SECRET_CONSUME_RATE; - size_t const block_len = XXH_STRIPE_LEN * nbStripesPerBlock; - size_t const nb_blocks = (len - 1) / block_len; + XXH3_f_scrambleAcc f_scramble) { + size_t const nbStripesPerBlock = (secretSize - XXH_STRIPE_LEN) / XXH_SECRET_CONSUME_RATE; + size_t const block_len = XXH_STRIPE_LEN * nbStripesPerBlock; + size_t const nb_blocks = (len - 1) / block_len; - size_t n; + size_t n; - XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); - for (n = 0; n < nb_blocks; n++) { - XXH3_accumulate(acc, input + n*block_len, secret, nbStripesPerBlock, f_acc512); - f_scramble(acc, secret + secretSize - XXH_STRIPE_LEN); - } + for (n = 0; n < nb_blocks; n++) { + XXH3_accumulate(acc, input + n * block_len, secret, nbStripesPerBlock, f_acc512); + f_scramble(acc, secret + secretSize - XXH_STRIPE_LEN); + } - /* last partial block */ - XXH_ASSERT(len > XXH_STRIPE_LEN); - { size_t const nbStripes = ((len - 1) - (block_len * nb_blocks)) / XXH_STRIPE_LEN; - XXH_ASSERT(nbStripes <= (secretSize / XXH_SECRET_CONSUME_RATE)); - XXH3_accumulate(acc, input + nb_blocks*block_len, secret, nbStripes, f_acc512); + /* last partial block */ + XXH_ASSERT(len > XXH_STRIPE_LEN); + { + size_t const nbStripes = ((len - 1) - (block_len * nb_blocks)) / XXH_STRIPE_LEN; + XXH_ASSERT(nbStripes <= (secretSize / XXH_SECRET_CONSUME_RATE)); + XXH3_accumulate(acc, input + nb_blocks * block_len, secret, nbStripes, f_acc512); - /* last stripe */ - { const xxh_u8* const p = input + len - XXH_STRIPE_LEN; + /* last stripe */ + { + const xxh_u8 *const p = input + len - XXH_STRIPE_LEN; #define XXH_SECRET_LASTACC_START 7 /* not aligned on 8, last secret is different from acc & scrambler */ - f_acc512(acc, p, secret + secretSize - XXH_STRIPE_LEN - XXH_SECRET_LASTACC_START); - } } + f_acc512(acc, p, secret + secretSize - XXH_STRIPE_LEN - XXH_SECRET_LASTACC_START); + } + } } XXH_FORCE_INLINE xxh_u64 -XXH3_mix2Accs(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret) -{ - return XXH3_mul128_fold64( - acc[0] ^ XXH_readLE64(secret), - acc[1] ^ XXH_readLE64(secret+8) ); +XXH3_mix2Accs(const xxh_u64 *XXH_RESTRICT acc, const xxh_u8 *XXH_RESTRICT secret) { + return XXH3_mul128_fold64( + acc[0] ^ XXH_readLE64(secret), + acc[1] ^ XXH_readLE64(secret + 8)); } static XXH64_hash_t -XXH3_mergeAccs(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret, xxh_u64 start) -{ - xxh_u64 result64 = start; - size_t i = 0; +XXH3_mergeAccs(const xxh_u64 *XXH_RESTRICT acc, const xxh_u8 *XXH_RESTRICT secret, xxh_u64 start) { + xxh_u64 result64 = start; + size_t i = 0; - for (i = 0; i < 4; i++) { - result64 += XXH3_mix2Accs(acc+2*i, secret + 16*i); + for (i = 0; i < 4; i++) { + result64 += XXH3_mix2Accs(acc + 2 * i, secret + 16 * i); #if defined(__clang__) /* Clang */ \ - && (defined(__arm__) || defined(__thumb__)) /* ARMv7 */ \ - && (defined(__ARM_NEON) || defined(__ARM_NEON__)) /* NEON */ \ - && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable */ - /* - * UGLY HACK: - * Prevent autovectorization on Clang ARMv7-a. Exact same problem as - * the one in XXH3_len_129to240_64b. Speeds up shorter keys > 240b. - * XXH3_64bits, len == 256, Snapdragon 835: - * without hack: 2063.7 MB/s - * with hack: 2560.7 MB/s - */ - XXH_COMPILER_GUARD(result64); + && (defined(__arm__) || defined(__thumb__)) /* ARMv7 */ \ + && (defined(__ARM_NEON) || defined(__ARM_NEON__)) /* NEON */ \ + && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable */ + /* + * UGLY HACK: + * Prevent autovectorization on Clang ARMv7-a. Exact same problem as + * the one in XXH3_len_129to240_64b. Speeds up shorter keys > 240b. + * XXH3_64bits, len == 256, Snapdragon 835: + * without hack: 2063.7 MB/s + * with hack: 2560.7 MB/s + */ + XXH_COMPILER_GUARD(result64); #endif - } + } - return XXH3_avalanche(result64); + return XXH3_avalanche(result64); } #define XXH3_INIT_ACC { XXH_PRIME32_3, XXH_PRIME64_1, XXH_PRIME64_2, XXH_PRIME64_3, \ XXH_PRIME64_4, XXH_PRIME32_2, XXH_PRIME64_5, XXH_PRIME32_1 } XXH_FORCE_INLINE XXH64_hash_t -XXH3_hashLong_64b_internal(const void* XXH_RESTRICT input, size_t len, - const void* XXH_RESTRICT secret, size_t secretSize, +XXH3_hashLong_64b_internal(const void *XXH_RESTRICT input, size_t len, + const void *XXH_RESTRICT secret, size_t secretSize, XXH3_f_accumulate_512 f_acc512, - XXH3_f_scrambleAcc f_scramble) -{ - XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC; + XXH3_f_scrambleAcc f_scramble) { + XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC; - XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len, (const xxh_u8*)secret, secretSize, f_acc512, f_scramble); + XXH3_hashLong_internal_loop(acc, (const xxh_u8 *) input, len, (const xxh_u8 *) secret, secretSize, f_acc512, + f_scramble); - /* converge into final hash */ - XXH_STATIC_ASSERT(sizeof(acc) == 64); - /* do not align on 8, so that the secret is different from the accumulator */ + /* converge into final hash */ + XXH_STATIC_ASSERT(sizeof(acc) == 64); + /* do not align on 8, so that the secret is different from the accumulator */ #define XXH_SECRET_MERGEACCS_START 11 - XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); - return XXH3_mergeAccs(acc, (const xxh_u8*)secret + XXH_SECRET_MERGEACCS_START, (xxh_u64)len * XXH_PRIME64_1); + XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); + return XXH3_mergeAccs(acc, (const xxh_u8 *) secret + XXH_SECRET_MERGEACCS_START, (xxh_u64) len * XXH_PRIME64_1); } /* @@ -4467,11 +4505,10 @@ XXH3_hashLong_64b_internal(const void* XXH_RESTRICT input, size_t len, * This makes a big performance difference for "medium" keys (<1 KB) when using AVX instruction set. */ XXH_FORCE_INLINE XXH64_hash_t -XXH3_hashLong_64b_withSecret(const void* XXH_RESTRICT input, size_t len, - XXH64_hash_t seed64, const xxh_u8* XXH_RESTRICT secret, size_t secretLen) -{ - (void)seed64; - return XXH3_hashLong_64b_internal(input, len, secret, secretLen, XXH3_accumulate_512, XXH3_scrambleAcc); +XXH3_hashLong_64b_withSecret(const void *XXH_RESTRICT input, size_t len, + XXH64_hash_t seed64, const xxh_u8 *XXH_RESTRICT secret, size_t secretLen) { + (void) seed64; + return XXH3_hashLong_64b_internal(input, len, secret, secretLen, XXH3_accumulate_512, XXH3_scrambleAcc); } /* @@ -4481,11 +4518,13 @@ XXH3_hashLong_64b_withSecret(const void* XXH_RESTRICT input, size_t len, * and provide a statically defined secret size to allow optimization of vector loop. */ XXH_NO_INLINE XXH64_hash_t -XXH3_hashLong_64b_default(const void* XXH_RESTRICT input, size_t len, - XXH64_hash_t seed64, const xxh_u8* XXH_RESTRICT secret, size_t secretLen) -{ - (void)seed64; (void)secret; (void)secretLen; - return XXH3_hashLong_64b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_accumulate_512, XXH3_scrambleAcc); +XXH3_hashLong_64b_default(const void *XXH_RESTRICT input, size_t len, + XXH64_hash_t seed64, const xxh_u8 *XXH_RESTRICT secret, size_t secretLen) { + (void) seed64; + (void) secret; + (void) secretLen; + return XXH3_hashLong_64b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_accumulate_512, + XXH3_scrambleAcc); } /* @@ -4500,90 +4539,85 @@ XXH3_hashLong_64b_default(const void* XXH_RESTRICT input, size_t len, * why (uop cache maybe?), but the difference is large and easily measurable. */ XXH_FORCE_INLINE XXH64_hash_t -XXH3_hashLong_64b_withSeed_internal(const void* input, size_t len, +XXH3_hashLong_64b_withSeed_internal(const void *input, size_t len, XXH64_hash_t seed, XXH3_f_accumulate_512 f_acc512, XXH3_f_scrambleAcc f_scramble, - XXH3_f_initCustomSecret f_initSec) -{ - if (seed == 0) - return XXH3_hashLong_64b_internal(input, len, - XXH3_kSecret, sizeof(XXH3_kSecret), - f_acc512, f_scramble); - { XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE]; - f_initSec(secret, seed); - return XXH3_hashLong_64b_internal(input, len, secret, sizeof(secret), - f_acc512, f_scramble); - } + XXH3_f_initCustomSecret f_initSec) { + if (seed == 0) + return XXH3_hashLong_64b_internal(input, len, + XXH3_kSecret, sizeof(XXH3_kSecret), + f_acc512, f_scramble); + { + XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE]; + f_initSec(secret, seed); + return XXH3_hashLong_64b_internal(input, len, secret, sizeof(secret), + f_acc512, f_scramble); + } } /* * It's important for performance that XXH3_hashLong is not inlined. */ XXH_NO_INLINE XXH64_hash_t -XXH3_hashLong_64b_withSeed(const void* input, size_t len, - XXH64_hash_t seed, const xxh_u8* secret, size_t secretLen) -{ - (void)secret; (void)secretLen; - return XXH3_hashLong_64b_withSeed_internal(input, len, seed, - XXH3_accumulate_512, XXH3_scrambleAcc, XXH3_initCustomSecret); +XXH3_hashLong_64b_withSeed(const void *input, size_t len, + XXH64_hash_t seed, const xxh_u8 *secret, size_t secretLen) { + (void) secret; + (void) secretLen; + return XXH3_hashLong_64b_withSeed_internal(input, len, seed, + XXH3_accumulate_512, XXH3_scrambleAcc, XXH3_initCustomSecret); } -typedef XXH64_hash_t (*XXH3_hashLong64_f)(const void* XXH_RESTRICT, size_t, - XXH64_hash_t, const xxh_u8* XXH_RESTRICT, size_t); +typedef XXH64_hash_t (*XXH3_hashLong64_f)(const void *XXH_RESTRICT, size_t, + XXH64_hash_t, const xxh_u8 *XXH_RESTRICT, size_t); XXH_FORCE_INLINE XXH64_hash_t -XXH3_64bits_internal(const void* XXH_RESTRICT input, size_t len, - XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen, - XXH3_hashLong64_f f_hashLong) -{ - XXH_ASSERT(secretLen >= XXH3_SECRET_SIZE_MIN); - /* - * If an action is to be taken if `secretLen` condition is not respected, - * it should be done here. - * For now, it's a contract pre-condition. - * Adding a check and a branch here would cost performance at every hash. - * Also, note that function signature doesn't offer room to return an error. - */ - if (len <= 16) - return XXH3_len_0to16_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, seed64); - if (len <= 128) - return XXH3_len_17to128_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64); - if (len <= XXH3_MIDSIZE_MAX) - return XXH3_len_129to240_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64); - return f_hashLong(input, len, seed64, (const xxh_u8*)secret, secretLen); +XXH3_64bits_internal(const void *XXH_RESTRICT input, size_t len, + XXH64_hash_t seed64, const void *XXH_RESTRICT secret, size_t secretLen, + XXH3_hashLong64_f f_hashLong) { + XXH_ASSERT(secretLen >= XXH3_SECRET_SIZE_MIN); + /* + * If an action is to be taken if `secretLen` condition is not respected, + * it should be done here. + * For now, it's a contract pre-condition. + * Adding a check and a branch here would cost performance at every hash. + * Also, note that function signature doesn't offer room to return an error. + */ + if (len <= 16) + return XXH3_len_0to16_64b((const xxh_u8 *) input, len, (const xxh_u8 *) secret, seed64); + if (len <= 128) + return XXH3_len_17to128_64b((const xxh_u8 *) input, len, (const xxh_u8 *) secret, secretLen, seed64); + if (len <= XXH3_MIDSIZE_MAX) + return XXH3_len_129to240_64b((const xxh_u8 *) input, len, (const xxh_u8 *) secret, secretLen, seed64); + return f_hashLong(input, len, seed64, (const xxh_u8 *) secret, secretLen); } /* === Public entry point === */ /*! @ingroup xxh3_family */ -XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(const void* input, size_t len) -{ - return XXH3_64bits_internal(input, len, 0, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_default); +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(const void *input, size_t len) { + return XXH3_64bits_internal(input, len, 0, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_default); } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH64_hash_t -XXH3_64bits_withSecret(const void* input, size_t len, const void* secret, size_t secretSize) -{ - return XXH3_64bits_internal(input, len, 0, secret, secretSize, XXH3_hashLong_64b_withSecret); +XXH3_64bits_withSecret(const void *input, size_t len, const void *secret, size_t secretSize) { + return XXH3_64bits_internal(input, len, 0, secret, secretSize, XXH3_hashLong_64b_withSecret); } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH64_hash_t -XXH3_64bits_withSeed(const void* input, size_t len, XXH64_hash_t seed) -{ - return XXH3_64bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_withSeed); +XXH3_64bits_withSeed(const void *input, size_t len, XXH64_hash_t seed) { + return XXH3_64bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_withSeed); } XXH_PUBLIC_API XXH64_hash_t -XXH3_64bits_withSecretandSeed(const void* input, size_t len, const void* secret, size_t secretSize, XXH64_hash_t seed) -{ - if (len <= XXH3_MIDSIZE_MAX) - return XXH3_64bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), NULL); - return XXH3_hashLong_64b_withSecret(input, len, seed, (const xxh_u8*)secret, secretSize); +XXH3_64bits_withSecretandSeed(const void *input, size_t len, const void *secret, size_t secretSize, XXH64_hash_t seed) { + if (len <= XXH3_MIDSIZE_MAX) + return XXH3_64bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), NULL); + return XXH3_hashLong_64b_withSecret(input, len, seed, (const xxh_u8 *) secret, secretSize); } @@ -4612,167 +4646,159 @@ XXH3_64bits_withSecretandSeed(const void* input, size_t len, const void* secret, * * Align must be a power of 2 and 8 <= align <= 128. */ -static void* XXH_alignedMalloc(size_t s, size_t align) -{ - XXH_ASSERT(align <= 128 && align >= 8); /* range check */ - XXH_ASSERT((align & (align-1)) == 0); /* power of 2 */ - XXH_ASSERT(s != 0 && s < (s + align)); /* empty/overflow */ - { /* Overallocate to make room for manual realignment and an offset byte */ - xxh_u8* base = (xxh_u8*)XXH_malloc(s + align); - if (base != NULL) { - /* - * Get the offset needed to align this pointer. - * - * Even if the returned pointer is aligned, there will always be - * at least one byte to store the offset to the original pointer. - */ - size_t offset = align - ((size_t)base & (align - 1)); /* base % align */ - /* Add the offset for the now-aligned pointer */ - xxh_u8* ptr = base + offset; - - XXH_ASSERT((size_t)ptr % align == 0); - - /* Store the offset immediately before the returned pointer. */ - ptr[-1] = (xxh_u8)offset; - return ptr; - } - return NULL; +static void *XXH_alignedMalloc(size_t s, size_t align) { + XXH_ASSERT(align <= 128 && align >= 8); /* range check */ + XXH_ASSERT((align & (align - 1)) == 0); /* power of 2 */ + XXH_ASSERT(s != 0 && s < (s + align)); /* empty/overflow */ + { /* Overallocate to make room for manual realignment and an offset byte */ + xxh_u8 *base = (xxh_u8 *) XXH_malloc(s + align); + if (base != NULL) { + /* + * Get the offset needed to align this pointer. + * + * Even if the returned pointer is aligned, there will always be + * at least one byte to store the offset to the original pointer. + */ + size_t offset = align - ((size_t) base & (align - 1)); /* base % align */ + /* Add the offset for the now-aligned pointer */ + xxh_u8 *ptr = base + offset; + + XXH_ASSERT((size_t) ptr % align == 0); + + /* Store the offset immediately before the returned pointer. */ + ptr[-1] = (xxh_u8) offset; + return ptr; } + return NULL; + } } + /* * Frees an aligned pointer allocated by XXH_alignedMalloc(). Don't pass * normal malloc'd pointers, XXH_alignedMalloc has a specific data layout. */ -static void XXH_alignedFree(void* p) -{ - if (p != NULL) { - xxh_u8* ptr = (xxh_u8*)p; - /* Get the offset byte we added in XXH_malloc. */ - xxh_u8 offset = ptr[-1]; - /* Free the original malloc'd pointer */ - xxh_u8* base = ptr - offset; - XXH_free(base); - } +static void XXH_alignedFree(void *p) { + if (p != NULL) { + xxh_u8 *ptr = (xxh_u8 *) p; + /* Get the offset byte we added in XXH_malloc. */ + xxh_u8 offset = ptr[-1]; + /* Free the original malloc'd pointer */ + xxh_u8 *base = ptr - offset; + XXH_free(base); + } } /*! @ingroup xxh3_family */ -XXH_PUBLIC_API XXH3_state_t* XXH3_createState(void) -{ - XXH3_state_t* const state = (XXH3_state_t*)XXH_alignedMalloc(sizeof(XXH3_state_t), 64); - if (state==NULL) return NULL; - XXH3_INITSTATE(state); - return state; +XXH_PUBLIC_API XXH3_state_t *XXH3_createState(void) { + XXH3_state_t *const state = (XXH3_state_t *) XXH_alignedMalloc(sizeof(XXH3_state_t), 64); + if (state == NULL) return NULL; + XXH3_INITSTATE(state); + return state; } /*! @ingroup xxh3_family */ -XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr) -{ - XXH_alignedFree(statePtr); - return XXH_OK; +XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t *statePtr) { + XXH_alignedFree(statePtr); + return XXH_OK; } /*! @ingroup xxh3_family */ XXH_PUBLIC_API void -XXH3_copyState(XXH3_state_t* dst_state, const XXH3_state_t* src_state) -{ - XXH_memcpy(dst_state, src_state, sizeof(*dst_state)); +XXH3_copyState(XXH3_state_t *dst_state, const XXH3_state_t *src_state) { + XXH_memcpy(dst_state, src_state, sizeof(*dst_state)); } static void -XXH3_reset_internal(XXH3_state_t* statePtr, +XXH3_reset_internal(XXH3_state_t *statePtr, XXH64_hash_t seed, - const void* secret, size_t secretSize) -{ - size_t const initStart = offsetof(XXH3_state_t, bufferedSize); - size_t const initLength = offsetof(XXH3_state_t, nbStripesPerBlock) - initStart; - XXH_ASSERT(offsetof(XXH3_state_t, nbStripesPerBlock) > initStart); - XXH_ASSERT(statePtr != NULL); - /* set members from bufferedSize to nbStripesPerBlock (excluded) to 0 */ - memset((char*)statePtr + initStart, 0, initLength); - statePtr->acc[0] = XXH_PRIME32_3; - statePtr->acc[1] = XXH_PRIME64_1; - statePtr->acc[2] = XXH_PRIME64_2; - statePtr->acc[3] = XXH_PRIME64_3; - statePtr->acc[4] = XXH_PRIME64_4; - statePtr->acc[5] = XXH_PRIME32_2; - statePtr->acc[6] = XXH_PRIME64_5; - statePtr->acc[7] = XXH_PRIME32_1; - statePtr->seed = seed; - statePtr->useSeed = (seed != 0); - statePtr->extSecret = (const unsigned char*)secret; - XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); - statePtr->secretLimit = secretSize - XXH_STRIPE_LEN; - statePtr->nbStripesPerBlock = statePtr->secretLimit / XXH_SECRET_CONSUME_RATE; + const void *secret, size_t secretSize) { + size_t const initStart = offsetof(XXH3_state_t, bufferedSize); + size_t const initLength = offsetof(XXH3_state_t, nbStripesPerBlock) - initStart; + XXH_ASSERT(offsetof(XXH3_state_t, nbStripesPerBlock) > initStart); + XXH_ASSERT(statePtr != NULL); + /* set members from bufferedSize to nbStripesPerBlock (excluded) to 0 */ + memset((char *) statePtr + initStart, 0, initLength); + statePtr->acc[0] = XXH_PRIME32_3; + statePtr->acc[1] = XXH_PRIME64_1; + statePtr->acc[2] = XXH_PRIME64_2; + statePtr->acc[3] = XXH_PRIME64_3; + statePtr->acc[4] = XXH_PRIME64_4; + statePtr->acc[5] = XXH_PRIME32_2; + statePtr->acc[6] = XXH_PRIME64_5; + statePtr->acc[7] = XXH_PRIME32_1; + statePtr->seed = seed; + statePtr->useSeed = (seed != 0); + statePtr->extSecret = (const unsigned char *) secret; + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); + statePtr->secretLimit = secretSize - XXH_STRIPE_LEN; + statePtr->nbStripesPerBlock = statePtr->secretLimit / XXH_SECRET_CONSUME_RATE; } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_64bits_reset(XXH3_state_t* statePtr) -{ - if (statePtr == NULL) return XXH_ERROR; - XXH3_reset_internal(statePtr, 0, XXH3_kSecret, XXH_SECRET_DEFAULT_SIZE); - return XXH_OK; +XXH3_64bits_reset(XXH3_state_t *statePtr) { + if (statePtr == NULL) return XXH_ERROR; + XXH3_reset_internal(statePtr, 0, XXH3_kSecret, XXH_SECRET_DEFAULT_SIZE); + return XXH_OK; } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_64bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize) -{ - if (statePtr == NULL) return XXH_ERROR; - XXH3_reset_internal(statePtr, 0, secret, secretSize); - if (secret == NULL) return XXH_ERROR; - if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; - return XXH_OK; +XXH3_64bits_reset_withSecret(XXH3_state_t *statePtr, const void *secret, size_t secretSize) { + if (statePtr == NULL) return XXH_ERROR; + XXH3_reset_internal(statePtr, 0, secret, secretSize); + if (secret == NULL) return XXH_ERROR; + if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; + return XXH_OK; } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_64bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed) -{ - if (statePtr == NULL) return XXH_ERROR; - if (seed==0) return XXH3_64bits_reset(statePtr); - if ((seed != statePtr->seed) || (statePtr->extSecret != NULL)) - XXH3_initCustomSecret(statePtr->customSecret, seed); - XXH3_reset_internal(statePtr, seed, NULL, XXH_SECRET_DEFAULT_SIZE); - return XXH_OK; +XXH3_64bits_reset_withSeed(XXH3_state_t *statePtr, XXH64_hash_t seed) { + if (statePtr == NULL) return XXH_ERROR; + if (seed == 0) return XXH3_64bits_reset(statePtr); + if ((seed != statePtr->seed) || (statePtr->extSecret != NULL)) + XXH3_initCustomSecret(statePtr->customSecret, seed); + XXH3_reset_internal(statePtr, seed, NULL, XXH_SECRET_DEFAULT_SIZE); + return XXH_OK; } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_64bits_reset_withSecretandSeed(XXH3_state_t* statePtr, const void* secret, size_t secretSize, XXH64_hash_t seed64) -{ - if (statePtr == NULL) return XXH_ERROR; - if (secret == NULL) return XXH_ERROR; - if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; - XXH3_reset_internal(statePtr, seed64, secret, secretSize); - statePtr->useSeed = 1; /* always, even if seed64==0 */ - return XXH_OK; +XXH3_64bits_reset_withSecretandSeed(XXH3_state_t *statePtr, const void *secret, size_t secretSize, + XXH64_hash_t seed64) { + if (statePtr == NULL) return XXH_ERROR; + if (secret == NULL) return XXH_ERROR; + if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; + XXH3_reset_internal(statePtr, seed64, secret, secretSize); + statePtr->useSeed = 1; /* always, even if seed64==0 */ + return XXH_OK; } /* Note : when XXH3_consumeStripes() is invoked, * there must be a guarantee that at least one more byte must be consumed from input * so that the function can blindly consume all stripes using the "normal" secret segment */ XXH_FORCE_INLINE void -XXH3_consumeStripes(xxh_u64* XXH_RESTRICT acc, - size_t* XXH_RESTRICT nbStripesSoFarPtr, size_t nbStripesPerBlock, - const xxh_u8* XXH_RESTRICT input, size_t nbStripes, - const xxh_u8* XXH_RESTRICT secret, size_t secretLimit, +XXH3_consumeStripes(xxh_u64 *XXH_RESTRICT acc, + size_t *XXH_RESTRICT nbStripesSoFarPtr, size_t nbStripesPerBlock, + const xxh_u8 *XXH_RESTRICT input, size_t nbStripes, + const xxh_u8 *XXH_RESTRICT secret, size_t secretLimit, XXH3_f_accumulate_512 f_acc512, - XXH3_f_scrambleAcc f_scramble) -{ - XXH_ASSERT(nbStripes <= nbStripesPerBlock); /* can handle max 1 scramble per invocation */ - XXH_ASSERT(*nbStripesSoFarPtr < nbStripesPerBlock); - if (nbStripesPerBlock - *nbStripesSoFarPtr <= nbStripes) { - /* need a scrambling operation */ - size_t const nbStripesToEndofBlock = nbStripesPerBlock - *nbStripesSoFarPtr; - size_t const nbStripesAfterBlock = nbStripes - nbStripesToEndofBlock; - XXH3_accumulate(acc, input, secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE, nbStripesToEndofBlock, f_acc512); - f_scramble(acc, secret + secretLimit); - XXH3_accumulate(acc, input + nbStripesToEndofBlock * XXH_STRIPE_LEN, secret, nbStripesAfterBlock, f_acc512); - *nbStripesSoFarPtr = nbStripesAfterBlock; - } else { - XXH3_accumulate(acc, input, secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE, nbStripes, f_acc512); - *nbStripesSoFarPtr += nbStripes; - } + XXH3_f_scrambleAcc f_scramble) { + XXH_ASSERT(nbStripes <= nbStripesPerBlock); /* can handle max 1 scramble per invocation */ + XXH_ASSERT(*nbStripesSoFarPtr < nbStripesPerBlock); + if (nbStripesPerBlock - *nbStripesSoFarPtr <= nbStripes) { + /* need a scrambling operation */ + size_t const nbStripesToEndofBlock = nbStripesPerBlock - *nbStripesSoFarPtr; + size_t const nbStripesAfterBlock = nbStripes - nbStripesToEndofBlock; + XXH3_accumulate(acc, input, secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE, nbStripesToEndofBlock, + f_acc512); + f_scramble(acc, secret + secretLimit); + XXH3_accumulate(acc, input + nbStripesToEndofBlock * XXH_STRIPE_LEN, secret, nbStripesAfterBlock, f_acc512); + *nbStripesSoFarPtr = nbStripesAfterBlock; + } else { + XXH3_accumulate(acc, input, secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE, nbStripes, f_acc512); + *nbStripesSoFarPtr += nbStripes; + } } #ifndef XXH3_STREAM_USE_STACK @@ -4784,179 +4810,178 @@ XXH3_consumeStripes(xxh_u64* XXH_RESTRICT acc, * Both XXH3_64bits_update and XXH3_128bits_update use this routine. */ XXH_FORCE_INLINE XXH_errorcode -XXH3_update(XXH3_state_t* XXH_RESTRICT const state, - const xxh_u8* XXH_RESTRICT input, size_t len, +XXH3_update(XXH3_state_t *XXH_RESTRICT const state, + const xxh_u8 *XXH_RESTRICT input, size_t len, XXH3_f_accumulate_512 f_acc512, - XXH3_f_scrambleAcc f_scramble) -{ - if (input==NULL) { - XXH_ASSERT(len == 0); - return XXH_OK; - } + XXH3_f_scrambleAcc f_scramble) { + if (input == NULL) { + XXH_ASSERT(len == 0); + return XXH_OK; + } - XXH_ASSERT(state != NULL); - { const xxh_u8* const bEnd = input + len; - const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret; + XXH_ASSERT(state != NULL); + { + const xxh_u8 *const bEnd = input + len; + const unsigned char *const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret; #if defined(XXH3_STREAM_USE_STACK) && XXH3_STREAM_USE_STACK >= 1 - /* For some reason, gcc and MSVC seem to suffer greatly - * when operating accumulators directly into state. - * Operating into stack space seems to enable proper optimization. - * clang, on the other hand, doesn't seem to need this trick */ - XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[8]; memcpy(acc, state->acc, sizeof(acc)); + /* For some reason, gcc and MSVC seem to suffer greatly + * when operating accumulators directly into state. + * Operating into stack space seems to enable proper optimization. + * clang, on the other hand, doesn't seem to need this trick */ + XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[8]; + memcpy(acc, state->acc, sizeof(acc)); #else - xxh_u64* XXH_RESTRICT const acc = state->acc; + xxh_u64* XXH_RESTRICT const acc = state->acc; #endif - state->totalLen += len; - XXH_ASSERT(state->bufferedSize <= XXH3_INTERNALBUFFER_SIZE); - - /* small input : just fill in tmp buffer */ - if (state->bufferedSize + len <= XXH3_INTERNALBUFFER_SIZE) { - XXH_memcpy(state->buffer + state->bufferedSize, input, len); - state->bufferedSize += (XXH32_hash_t)len; - return XXH_OK; - } + state->totalLen += len; + XXH_ASSERT(state->bufferedSize <= XXH3_INTERNALBUFFER_SIZE); + + /* small input : just fill in tmp buffer */ + if (state->bufferedSize + len <= XXH3_INTERNALBUFFER_SIZE) { + XXH_memcpy(state->buffer + state->bufferedSize, input, len); + state->bufferedSize += (XXH32_hash_t) len; + return XXH_OK; + } - /* total input is now > XXH3_INTERNALBUFFER_SIZE */ - #define XXH3_INTERNALBUFFER_STRIPES (XXH3_INTERNALBUFFER_SIZE / XXH_STRIPE_LEN) - XXH_STATIC_ASSERT(XXH3_INTERNALBUFFER_SIZE % XXH_STRIPE_LEN == 0); /* clean multiple */ + /* total input is now > XXH3_INTERNALBUFFER_SIZE */ +#define XXH3_INTERNALBUFFER_STRIPES (XXH3_INTERNALBUFFER_SIZE / XXH_STRIPE_LEN) + XXH_STATIC_ASSERT(XXH3_INTERNALBUFFER_SIZE % XXH_STRIPE_LEN == 0); /* clean multiple */ - /* - * Internal buffer is partially filled (always, except at beginning) - * Complete it, then consume it. - */ - if (state->bufferedSize) { - size_t const loadSize = XXH3_INTERNALBUFFER_SIZE - state->bufferedSize; - XXH_memcpy(state->buffer + state->bufferedSize, input, loadSize); - input += loadSize; - XXH3_consumeStripes(acc, - &state->nbStripesSoFar, state->nbStripesPerBlock, - state->buffer, XXH3_INTERNALBUFFER_STRIPES, - secret, state->secretLimit, - f_acc512, f_scramble); - state->bufferedSize = 0; - } - XXH_ASSERT(input < bEnd); - - /* large input to consume : ingest per full block */ - if ((size_t)(bEnd - input) > state->nbStripesPerBlock * XXH_STRIPE_LEN) { - size_t nbStripes = (size_t)(bEnd - 1 - input) / XXH_STRIPE_LEN; - XXH_ASSERT(state->nbStripesPerBlock >= state->nbStripesSoFar); - /* join to current block's end */ - { size_t const nbStripesToEnd = state->nbStripesPerBlock - state->nbStripesSoFar; - XXH_ASSERT(nbStripes <= nbStripes); - XXH3_accumulate(acc, input, secret + state->nbStripesSoFar * XXH_SECRET_CONSUME_RATE, nbStripesToEnd, f_acc512); - f_scramble(acc, secret + state->secretLimit); - state->nbStripesSoFar = 0; - input += nbStripesToEnd * XXH_STRIPE_LEN; - nbStripes -= nbStripesToEnd; - } - /* consume per entire blocks */ - while(nbStripes >= state->nbStripesPerBlock) { - XXH3_accumulate(acc, input, secret, state->nbStripesPerBlock, f_acc512); - f_scramble(acc, secret + state->secretLimit); - input += state->nbStripesPerBlock * XXH_STRIPE_LEN; - nbStripes -= state->nbStripesPerBlock; - } - /* consume last partial block */ - XXH3_accumulate(acc, input, secret, nbStripes, f_acc512); - input += nbStripes * XXH_STRIPE_LEN; - XXH_ASSERT(input < bEnd); /* at least some bytes left */ - state->nbStripesSoFar = nbStripes; - /* buffer predecessor of last partial stripe */ - XXH_memcpy(state->buffer + sizeof(state->buffer) - XXH_STRIPE_LEN, input - XXH_STRIPE_LEN, XXH_STRIPE_LEN); - XXH_ASSERT(bEnd - input <= XXH_STRIPE_LEN); - } else { - /* content to consume <= block size */ - /* Consume input by a multiple of internal buffer size */ - if (bEnd - input > XXH3_INTERNALBUFFER_SIZE) { - const xxh_u8* const limit = bEnd - XXH3_INTERNALBUFFER_SIZE; - do { - XXH3_consumeStripes(acc, - &state->nbStripesSoFar, state->nbStripesPerBlock, - input, XXH3_INTERNALBUFFER_STRIPES, - secret, state->secretLimit, - f_acc512, f_scramble); - input += XXH3_INTERNALBUFFER_SIZE; - } while (inputbuffer + sizeof(state->buffer) - XXH_STRIPE_LEN, input - XXH_STRIPE_LEN, XXH_STRIPE_LEN); - } - } + /* + * Internal buffer is partially filled (always, except at beginning) + * Complete it, then consume it. + */ + if (state->bufferedSize) { + size_t const loadSize = XXH3_INTERNALBUFFER_SIZE - state->bufferedSize; + XXH_memcpy(state->buffer + state->bufferedSize, input, loadSize); + input += loadSize; + XXH3_consumeStripes(acc, + &state->nbStripesSoFar, state->nbStripesPerBlock, + state->buffer, XXH3_INTERNALBUFFER_STRIPES, + secret, state->secretLimit, + f_acc512, f_scramble); + state->bufferedSize = 0; + } + XXH_ASSERT(input < bEnd); + + /* large input to consume : ingest per full block */ + if ((size_t) (bEnd - input) > state->nbStripesPerBlock * XXH_STRIPE_LEN) { + size_t nbStripes = (size_t) (bEnd - 1 - input) / XXH_STRIPE_LEN; + XXH_ASSERT(state->nbStripesPerBlock >= state->nbStripesSoFar); + /* join to current block's end */ + { + size_t const nbStripesToEnd = state->nbStripesPerBlock - state->nbStripesSoFar; + XXH_ASSERT(nbStripes <= nbStripes); + XXH3_accumulate(acc, input, secret + state->nbStripesSoFar * XXH_SECRET_CONSUME_RATE, nbStripesToEnd, f_acc512); + f_scramble(acc, secret + state->secretLimit); + state->nbStripesSoFar = 0; + input += nbStripesToEnd * XXH_STRIPE_LEN; + nbStripes -= nbStripesToEnd; + } + /* consume per entire blocks */ + while (nbStripes >= state->nbStripesPerBlock) { + XXH3_accumulate(acc, input, secret, state->nbStripesPerBlock, f_acc512); + f_scramble(acc, secret + state->secretLimit); + input += state->nbStripesPerBlock * XXH_STRIPE_LEN; + nbStripes -= state->nbStripesPerBlock; + } + /* consume last partial block */ + XXH3_accumulate(acc, input, secret, nbStripes, f_acc512); + input += nbStripes * XXH_STRIPE_LEN; + XXH_ASSERT(input < bEnd); /* at least some bytes left */ + state->nbStripesSoFar = nbStripes; + /* buffer predecessor of last partial stripe */ + XXH_memcpy(state->buffer + sizeof(state->buffer) - XXH_STRIPE_LEN, input - XXH_STRIPE_LEN, XXH_STRIPE_LEN); + XXH_ASSERT(bEnd - input <= XXH_STRIPE_LEN); + } else { + /* content to consume <= block size */ + /* Consume input by a multiple of internal buffer size */ + if (bEnd - input > XXH3_INTERNALBUFFER_SIZE) { + const xxh_u8 *const limit = bEnd - XXH3_INTERNALBUFFER_SIZE; + do { + XXH3_consumeStripes(acc, + &state->nbStripesSoFar, state->nbStripesPerBlock, + input, XXH3_INTERNALBUFFER_STRIPES, + secret, state->secretLimit, + f_acc512, f_scramble); + input += XXH3_INTERNALBUFFER_SIZE; + } while (input < limit); + /* buffer predecessor of last partial stripe */ + XXH_memcpy(state->buffer + sizeof(state->buffer) - XXH_STRIPE_LEN, input - XXH_STRIPE_LEN, XXH_STRIPE_LEN); + } + } - /* Some remaining input (always) : buffer it */ - XXH_ASSERT(input < bEnd); - XXH_ASSERT(bEnd - input <= XXH3_INTERNALBUFFER_SIZE); - XXH_ASSERT(state->bufferedSize == 0); - XXH_memcpy(state->buffer, input, (size_t)(bEnd-input)); - state->bufferedSize = (XXH32_hash_t)(bEnd-input); + /* Some remaining input (always) : buffer it */ + XXH_ASSERT(input < bEnd); + XXH_ASSERT(bEnd - input <= XXH3_INTERNALBUFFER_SIZE); + XXH_ASSERT(state->bufferedSize == 0); + XXH_memcpy(state->buffer, input, (size_t) (bEnd - input)); + state->bufferedSize = (XXH32_hash_t) (bEnd - input); #if defined(XXH3_STREAM_USE_STACK) && XXH3_STREAM_USE_STACK >= 1 - /* save stack accumulators into state */ - memcpy(state->acc, acc, sizeof(acc)); + /* save stack accumulators into state */ + memcpy(state->acc, acc, sizeof(acc)); #endif - } + } - return XXH_OK; + return XXH_OK; } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_64bits_update(XXH3_state_t* state, const void* input, size_t len) -{ - return XXH3_update(state, (const xxh_u8*)input, len, - XXH3_accumulate_512, XXH3_scrambleAcc); +XXH3_64bits_update(XXH3_state_t *state, const void *input, size_t len) { + return XXH3_update(state, (const xxh_u8 *) input, len, + XXH3_accumulate_512, XXH3_scrambleAcc); } XXH_FORCE_INLINE void -XXH3_digest_long (XXH64_hash_t* acc, - const XXH3_state_t* state, - const unsigned char* secret) -{ - /* - * Digest on a local copy. This way, the state remains unaltered, and it can - * continue ingesting more input afterwards. - */ - XXH_memcpy(acc, state->acc, sizeof(state->acc)); - if (state->bufferedSize >= XXH_STRIPE_LEN) { - size_t const nbStripes = (state->bufferedSize - 1) / XXH_STRIPE_LEN; - size_t nbStripesSoFar = state->nbStripesSoFar; - XXH3_consumeStripes(acc, - &nbStripesSoFar, state->nbStripesPerBlock, - state->buffer, nbStripes, - secret, state->secretLimit, - XXH3_accumulate_512, XXH3_scrambleAcc); - /* last stripe */ - XXH3_accumulate_512(acc, - state->buffer + state->bufferedSize - XXH_STRIPE_LEN, - secret + state->secretLimit - XXH_SECRET_LASTACC_START); - } else { /* bufferedSize < XXH_STRIPE_LEN */ - xxh_u8 lastStripe[XXH_STRIPE_LEN]; - size_t const catchupSize = XXH_STRIPE_LEN - state->bufferedSize; - XXH_ASSERT(state->bufferedSize > 0); /* there is always some input buffered */ - XXH_memcpy(lastStripe, state->buffer + sizeof(state->buffer) - catchupSize, catchupSize); - XXH_memcpy(lastStripe + catchupSize, state->buffer, state->bufferedSize); - XXH3_accumulate_512(acc, - lastStripe, - secret + state->secretLimit - XXH_SECRET_LASTACC_START); - } +XXH3_digest_long(XXH64_hash_t *acc, + const XXH3_state_t *state, + const unsigned char *secret) { + /* + * Digest on a local copy. This way, the state remains unaltered, and it can + * continue ingesting more input afterwards. + */ + XXH_memcpy(acc, state->acc, sizeof(state->acc)); + if (state->bufferedSize >= XXH_STRIPE_LEN) { + size_t const nbStripes = (state->bufferedSize - 1) / XXH_STRIPE_LEN; + size_t nbStripesSoFar = state->nbStripesSoFar; + XXH3_consumeStripes(acc, + &nbStripesSoFar, state->nbStripesPerBlock, + state->buffer, nbStripes, + secret, state->secretLimit, + XXH3_accumulate_512, XXH3_scrambleAcc); + /* last stripe */ + XXH3_accumulate_512(acc, + state->buffer + state->bufferedSize - XXH_STRIPE_LEN, + secret + state->secretLimit - XXH_SECRET_LASTACC_START); + } else { /* bufferedSize < XXH_STRIPE_LEN */ + xxh_u8 lastStripe[XXH_STRIPE_LEN]; + size_t const catchupSize = XXH_STRIPE_LEN - state->bufferedSize; + XXH_ASSERT(state->bufferedSize > 0); /* there is always some input buffered */ + XXH_memcpy(lastStripe, state->buffer + sizeof(state->buffer) - catchupSize, catchupSize); + XXH_memcpy(lastStripe + catchupSize, state->buffer, state->bufferedSize); + XXH3_accumulate_512(acc, + lastStripe, + secret + state->secretLimit - XXH_SECRET_LASTACC_START); + } } /*! @ingroup xxh3_family */ -XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (const XXH3_state_t* state) -{ - const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret; - if (state->totalLen > XXH3_MIDSIZE_MAX) { - XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB]; - XXH3_digest_long(acc, state, secret); - return XXH3_mergeAccs(acc, - secret + XXH_SECRET_MERGEACCS_START, - (xxh_u64)state->totalLen * XXH_PRIME64_1); - } - /* totalLen <= XXH3_MIDSIZE_MAX: digesting a short input */ - if (state->useSeed) - return XXH3_64bits_withSeed(state->buffer, (size_t)state->totalLen, state->seed); - return XXH3_64bits_withSecret(state->buffer, (size_t)(state->totalLen), - secret, state->secretLimit + XXH_STRIPE_LEN); +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest(const XXH3_state_t *state) { + const unsigned char *const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret; + if (state->totalLen > XXH3_MIDSIZE_MAX) { + XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB]; + XXH3_digest_long(acc, state, secret); + return XXH3_mergeAccs(acc, + secret + XXH_SECRET_MERGEACCS_START, + (xxh_u64) state->totalLen * XXH_PRIME64_1); + } + /* totalLen <= XXH3_MIDSIZE_MAX: digesting a short input */ + if (state->useSeed) + return XXH3_64bits_withSeed(state->buffer, (size_t) state->totalLen, state->seed); + return XXH3_64bits_withSecret(state->buffer, (size_t) (state->totalLen), + secret, state->secretLimit + XXH_STRIPE_LEN); } @@ -4979,285 +5004,292 @@ XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (const XXH3_state_t* state) */ XXH_FORCE_INLINE XXH128_hash_t -XXH3_len_1to3_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) -{ - /* A doubled version of 1to3_64b with different constants. */ - XXH_ASSERT(input != NULL); - XXH_ASSERT(1 <= len && len <= 3); - XXH_ASSERT(secret != NULL); - /* - * len = 1: combinedl = { input[0], 0x01, input[0], input[0] } - * len = 2: combinedl = { input[1], 0x02, input[0], input[1] } - * len = 3: combinedl = { input[2], 0x03, input[0], input[1] } - */ - { xxh_u8 const c1 = input[0]; - xxh_u8 const c2 = input[len >> 1]; - xxh_u8 const c3 = input[len - 1]; - xxh_u32 const combinedl = ((xxh_u32)c1 <<16) | ((xxh_u32)c2 << 24) - | ((xxh_u32)c3 << 0) | ((xxh_u32)len << 8); - xxh_u32 const combinedh = XXH_rotl32(XXH_swap32(combinedl), 13); - xxh_u64 const bitflipl = (XXH_readLE32(secret) ^ XXH_readLE32(secret+4)) + seed; - xxh_u64 const bitfliph = (XXH_readLE32(secret+8) ^ XXH_readLE32(secret+12)) - seed; - xxh_u64 const keyed_lo = (xxh_u64)combinedl ^ bitflipl; - xxh_u64 const keyed_hi = (xxh_u64)combinedh ^ bitfliph; - XXH128_hash_t h128; - h128.low64 = XXH64_avalanche(keyed_lo); - h128.high64 = XXH64_avalanche(keyed_hi); - return h128; - } +XXH3_len_1to3_128b(const xxh_u8 *input, size_t len, const xxh_u8 *secret, XXH64_hash_t seed) { + /* A doubled version of 1to3_64b with different constants. */ + XXH_ASSERT(input != NULL); + XXH_ASSERT(1 <= len && len <= 3); + XXH_ASSERT(secret != NULL); + /* + * len = 1: combinedl = { input[0], 0x01, input[0], input[0] } + * len = 2: combinedl = { input[1], 0x02, input[0], input[1] } + * len = 3: combinedl = { input[2], 0x03, input[0], input[1] } + */ + { + xxh_u8 const c1 = input[0]; + xxh_u8 const c2 = input[len >> 1]; + xxh_u8 const c3 = input[len - 1]; + xxh_u32 const combinedl = ((xxh_u32) c1 << 16) | ((xxh_u32) c2 << 24) + | ((xxh_u32) c3 << 0) | ((xxh_u32) len << 8); + xxh_u32 const combinedh = XXH_rotl32(XXH_swap32(combinedl), 13); + xxh_u64 const bitflipl = (XXH_readLE32(secret) ^ XXH_readLE32(secret + 4)) + seed; + xxh_u64 const bitfliph = (XXH_readLE32(secret + 8) ^ XXH_readLE32(secret + 12)) - seed; + xxh_u64 const keyed_lo = (xxh_u64) combinedl ^ bitflipl; + xxh_u64 const keyed_hi = (xxh_u64) combinedh ^ bitfliph; + XXH128_hash_t h128; + h128.low64 = XXH64_avalanche(keyed_lo); + h128.high64 = XXH64_avalanche(keyed_hi); + return h128; + } } XXH_FORCE_INLINE XXH128_hash_t -XXH3_len_4to8_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) -{ - XXH_ASSERT(input != NULL); - XXH_ASSERT(secret != NULL); - XXH_ASSERT(4 <= len && len <= 8); - seed ^= (xxh_u64)XXH_swap32((xxh_u32)seed) << 32; - { xxh_u32 const input_lo = XXH_readLE32(input); - xxh_u32 const input_hi = XXH_readLE32(input + len - 4); - xxh_u64 const input_64 = input_lo + ((xxh_u64)input_hi << 32); - xxh_u64 const bitflip = (XXH_readLE64(secret+16) ^ XXH_readLE64(secret+24)) + seed; - xxh_u64 const keyed = input_64 ^ bitflip; - - /* Shift len to the left to ensure it is even, this avoids even multiplies. */ - XXH128_hash_t m128 = XXH_mult64to128(keyed, XXH_PRIME64_1 + (len << 2)); - - m128.high64 += (m128.low64 << 1); - m128.low64 ^= (m128.high64 >> 3); - - m128.low64 = XXH_xorshift64(m128.low64, 35); - m128.low64 *= 0x9FB21C651E98DF25ULL; - m128.low64 = XXH_xorshift64(m128.low64, 28); - m128.high64 = XXH3_avalanche(m128.high64); - return m128; - } +XXH3_len_4to8_128b(const xxh_u8 *input, size_t len, const xxh_u8 *secret, XXH64_hash_t seed) { + XXH_ASSERT(input != NULL); + XXH_ASSERT(secret != NULL); + XXH_ASSERT(4 <= len && len <= 8); + seed ^= (xxh_u64) XXH_swap32((xxh_u32) seed) << 32; + { + xxh_u32 const input_lo = XXH_readLE32(input); + xxh_u32 const input_hi = XXH_readLE32(input + len - 4); + xxh_u64 const input_64 = input_lo + ((xxh_u64) input_hi << 32); + xxh_u64 const bitflip = (XXH_readLE64(secret + 16) ^ XXH_readLE64(secret + 24)) + seed; + xxh_u64 const keyed = input_64 ^ bitflip; + + /* Shift len to the left to ensure it is even, this avoids even multiplies. */ + XXH128_hash_t m128 = XXH_mult64to128(keyed, XXH_PRIME64_1 + (len << 2)); + + m128.high64 += (m128.low64 << 1); + m128.low64 ^= (m128.high64 >> 3); + + m128.low64 = XXH_xorshift64(m128.low64, 35); + m128.low64 *= 0x9FB21C651E98DF25ULL; + m128.low64 = XXH_xorshift64(m128.low64, 28); + m128.high64 = XXH3_avalanche(m128.high64); + return m128; + } } XXH_FORCE_INLINE XXH128_hash_t -XXH3_len_9to16_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) -{ - XXH_ASSERT(input != NULL); - XXH_ASSERT(secret != NULL); - XXH_ASSERT(9 <= len && len <= 16); - { xxh_u64 const bitflipl = (XXH_readLE64(secret+32) ^ XXH_readLE64(secret+40)) - seed; - xxh_u64 const bitfliph = (XXH_readLE64(secret+48) ^ XXH_readLE64(secret+56)) + seed; - xxh_u64 const input_lo = XXH_readLE64(input); - xxh_u64 input_hi = XXH_readLE64(input + len - 8); - XXH128_hash_t m128 = XXH_mult64to128(input_lo ^ input_hi ^ bitflipl, XXH_PRIME64_1); - /* - * Put len in the middle of m128 to ensure that the length gets mixed to - * both the low and high bits in the 128x64 multiply below. - */ - m128.low64 += (xxh_u64)(len - 1) << 54; - input_hi ^= bitfliph; - /* - * Add the high 32 bits of input_hi to the high 32 bits of m128, then - * add the long product of the low 32 bits of input_hi and XXH_PRIME32_2 to - * the high 64 bits of m128. - * - * The best approach to this operation is different on 32-bit and 64-bit. - */ - if (sizeof(void *) < sizeof(xxh_u64)) { /* 32-bit */ - /* - * 32-bit optimized version, which is more readable. - * - * On 32-bit, it removes an ADC and delays a dependency between the two - * halves of m128.high64, but it generates an extra mask on 64-bit. - */ - m128.high64 += (input_hi & 0xFFFFFFFF00000000ULL) + XXH_mult32to64((xxh_u32)input_hi, XXH_PRIME32_2); - } else { - /* - * 64-bit optimized (albeit more confusing) version. - * - * Uses some properties of addition and multiplication to remove the mask: - * - * Let: - * a = input_hi.lo = (input_hi & 0x00000000FFFFFFFF) - * b = input_hi.hi = (input_hi & 0xFFFFFFFF00000000) - * c = XXH_PRIME32_2 - * - * a + (b * c) - * Inverse Property: x + y - x == y - * a + (b * (1 + c - 1)) - * Distributive Property: x * (y + z) == (x * y) + (x * z) - * a + (b * 1) + (b * (c - 1)) - * Identity Property: x * 1 == x - * a + b + (b * (c - 1)) - * - * Substitute a, b, and c: - * input_hi.hi + input_hi.lo + ((xxh_u64)input_hi.lo * (XXH_PRIME32_2 - 1)) - * - * Since input_hi.hi + input_hi.lo == input_hi, we get this: - * input_hi + ((xxh_u64)input_hi.lo * (XXH_PRIME32_2 - 1)) - */ - m128.high64 += input_hi + XXH_mult32to64((xxh_u32)input_hi, XXH_PRIME32_2 - 1); - } - /* m128 ^= XXH_swap64(m128 >> 64); */ - m128.low64 ^= XXH_swap64(m128.high64); +XXH3_len_9to16_128b(const xxh_u8 *input, size_t len, const xxh_u8 *secret, XXH64_hash_t seed) { + XXH_ASSERT(input != NULL); + XXH_ASSERT(secret != NULL); + XXH_ASSERT(9 <= len && len <= 16); + { + xxh_u64 const bitflipl = (XXH_readLE64(secret + 32) ^ XXH_readLE64(secret + 40)) - seed; + xxh_u64 const bitfliph = (XXH_readLE64(secret + 48) ^ XXH_readLE64(secret + 56)) + seed; + xxh_u64 const input_lo = XXH_readLE64(input); + xxh_u64 input_hi = XXH_readLE64(input + len - 8); + XXH128_hash_t m128 = XXH_mult64to128(input_lo ^ input_hi ^ bitflipl, XXH_PRIME64_1); + /* + * Put len in the middle of m128 to ensure that the length gets mixed to + * both the low and high bits in the 128x64 multiply below. + */ + m128.low64 += (xxh_u64) (len - 1) << 54; + input_hi ^= bitfliph; + /* + * Add the high 32 bits of input_hi to the high 32 bits of m128, then + * add the long product of the low 32 bits of input_hi and XXH_PRIME32_2 to + * the high 64 bits of m128. + * + * The best approach to this operation is different on 32-bit and 64-bit. + */ + if (sizeof(void *) < sizeof(xxh_u64)) { /* 32-bit */ + /* + * 32-bit optimized version, which is more readable. + * + * On 32-bit, it removes an ADC and delays a dependency between the two + * halves of m128.high64, but it generates an extra mask on 64-bit. + */ + m128.high64 += (input_hi & 0xFFFFFFFF00000000ULL) + XXH_mult32to64((xxh_u32) input_hi, XXH_PRIME32_2); + } else { + /* + * 64-bit optimized (albeit more confusing) version. + * + * Uses some properties of addition and multiplication to remove the mask: + * + * Let: + * a = input_hi.lo = (input_hi & 0x00000000FFFFFFFF) + * b = input_hi.hi = (input_hi & 0xFFFFFFFF00000000) + * c = XXH_PRIME32_2 + * + * a + (b * c) + * Inverse Property: x + y - x == y + * a + (b * (1 + c - 1)) + * Distributive Property: x * (y + z) == (x * y) + (x * z) + * a + (b * 1) + (b * (c - 1)) + * Identity Property: x * 1 == x + * a + b + (b * (c - 1)) + * + * Substitute a, b, and c: + * input_hi.hi + input_hi.lo + ((xxh_u64)input_hi.lo * (XXH_PRIME32_2 - 1)) + * + * Since input_hi.hi + input_hi.lo == input_hi, we get this: + * input_hi + ((xxh_u64)input_hi.lo * (XXH_PRIME32_2 - 1)) + */ + m128.high64 += input_hi + XXH_mult32to64((xxh_u32) input_hi, XXH_PRIME32_2 - 1); + } + /* m128 ^= XXH_swap64(m128 >> 64); */ + m128.low64 ^= XXH_swap64(m128.high64); - { /* 128x64 multiply: h128 = m128 * XXH_PRIME64_2; */ - XXH128_hash_t h128 = XXH_mult64to128(m128.low64, XXH_PRIME64_2); - h128.high64 += m128.high64 * XXH_PRIME64_2; + { /* 128x64 multiply: h128 = m128 * XXH_PRIME64_2; */ + XXH128_hash_t h128 = XXH_mult64to128(m128.low64, XXH_PRIME64_2); + h128.high64 += m128.high64 * XXH_PRIME64_2; - h128.low64 = XXH3_avalanche(h128.low64); - h128.high64 = XXH3_avalanche(h128.high64); - return h128; - } } + h128.low64 = XXH3_avalanche(h128.low64); + h128.high64 = XXH3_avalanche(h128.high64); + return h128; + } + } } /* * Assumption: `secret` size is >= XXH3_SECRET_SIZE_MIN */ XXH_FORCE_INLINE XXH128_hash_t -XXH3_len_0to16_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) -{ - XXH_ASSERT(len <= 16); - { if (len > 8) return XXH3_len_9to16_128b(input, len, secret, seed); - if (len >= 4) return XXH3_len_4to8_128b(input, len, secret, seed); - if (len) return XXH3_len_1to3_128b(input, len, secret, seed); - { XXH128_hash_t h128; - xxh_u64 const bitflipl = XXH_readLE64(secret+64) ^ XXH_readLE64(secret+72); - xxh_u64 const bitfliph = XXH_readLE64(secret+80) ^ XXH_readLE64(secret+88); - h128.low64 = XXH64_avalanche(seed ^ bitflipl); - h128.high64 = XXH64_avalanche( seed ^ bitfliph); - return h128; - } } +XXH3_len_0to16_128b(const xxh_u8 *input, size_t len, const xxh_u8 *secret, XXH64_hash_t seed) { + XXH_ASSERT(len <= 16); + { + if (len > 8) return XXH3_len_9to16_128b(input, len, secret, seed); + if (len >= 4) return XXH3_len_4to8_128b(input, len, secret, seed); + if (len) return XXH3_len_1to3_128b(input, len, secret, seed); + { + XXH128_hash_t h128; + xxh_u64 const bitflipl = XXH_readLE64(secret + 64) ^ XXH_readLE64(secret + 72); + xxh_u64 const bitfliph = XXH_readLE64(secret + 80) ^ XXH_readLE64(secret + 88); + h128.low64 = XXH64_avalanche(seed ^ bitflipl); + h128.high64 = XXH64_avalanche(seed ^ bitfliph); + return h128; + } + } } /* * A bit slower than XXH3_mix16B, but handles multiply by zero better. */ XXH_FORCE_INLINE XXH128_hash_t -XXH128_mix32B(XXH128_hash_t acc, const xxh_u8* input_1, const xxh_u8* input_2, - const xxh_u8* secret, XXH64_hash_t seed) -{ - acc.low64 += XXH3_mix16B (input_1, secret+0, seed); - acc.low64 ^= XXH_readLE64(input_2) + XXH_readLE64(input_2 + 8); - acc.high64 += XXH3_mix16B (input_2, secret+16, seed); - acc.high64 ^= XXH_readLE64(input_1) + XXH_readLE64(input_1 + 8); - return acc; +XXH128_mix32B(XXH128_hash_t acc, const xxh_u8 *input_1, const xxh_u8 *input_2, + const xxh_u8 *secret, XXH64_hash_t seed) { + acc.low64 += XXH3_mix16B(input_1, secret + 0, seed); + acc.low64 ^= XXH_readLE64(input_2) + XXH_readLE64(input_2 + 8); + acc.high64 += XXH3_mix16B(input_2, secret + 16, seed); + acc.high64 ^= XXH_readLE64(input_1) + XXH_readLE64(input_1 + 8); + return acc; } XXH_FORCE_INLINE XXH128_hash_t -XXH3_len_17to128_128b(const xxh_u8* XXH_RESTRICT input, size_t len, - const xxh_u8* XXH_RESTRICT secret, size_t secretSize, - XXH64_hash_t seed) -{ - XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize; - XXH_ASSERT(16 < len && len <= 128); - - { XXH128_hash_t acc; - acc.low64 = len * XXH_PRIME64_1; - acc.high64 = 0; - if (len > 32) { - if (len > 64) { - if (len > 96) { - acc = XXH128_mix32B(acc, input+48, input+len-64, secret+96, seed); - } - acc = XXH128_mix32B(acc, input+32, input+len-48, secret+64, seed); - } - acc = XXH128_mix32B(acc, input+16, input+len-32, secret+32, seed); - } - acc = XXH128_mix32B(acc, input, input+len-16, secret, seed); - { XXH128_hash_t h128; - h128.low64 = acc.low64 + acc.high64; - h128.high64 = (acc.low64 * XXH_PRIME64_1) - + (acc.high64 * XXH_PRIME64_4) - + ((len - seed) * XXH_PRIME64_2); - h128.low64 = XXH3_avalanche(h128.low64); - h128.high64 = (XXH64_hash_t)0 - XXH3_avalanche(h128.high64); - return h128; +XXH3_len_17to128_128b(const xxh_u8 *XXH_RESTRICT input, size_t len, + const xxh_u8 *XXH_RESTRICT secret, size_t secretSize, + XXH64_hash_t seed) { + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); + (void) secretSize; + XXH_ASSERT(16 < len && len <= 128); + + { + XXH128_hash_t acc; + acc.low64 = len * XXH_PRIME64_1; + acc.high64 = 0; + if (len > 32) { + if (len > 64) { + if (len > 96) { + acc = XXH128_mix32B(acc, input + 48, input + len - 64, secret + 96, seed); } + acc = XXH128_mix32B(acc, input + 32, input + len - 48, secret + 64, seed); + } + acc = XXH128_mix32B(acc, input + 16, input + len - 32, secret + 32, seed); } + acc = XXH128_mix32B(acc, input, input + len - 16, secret, seed); + { + XXH128_hash_t h128; + h128.low64 = acc.low64 + acc.high64; + h128.high64 = (acc.low64 * XXH_PRIME64_1) + + (acc.high64 * XXH_PRIME64_4) + + ((len - seed) * XXH_PRIME64_2); + h128.low64 = XXH3_avalanche(h128.low64); + h128.high64 = (XXH64_hash_t) 0 - XXH3_avalanche(h128.high64); + return h128; + } + } } XXH_NO_INLINE XXH128_hash_t -XXH3_len_129to240_128b(const xxh_u8* XXH_RESTRICT input, size_t len, - const xxh_u8* XXH_RESTRICT secret, size_t secretSize, - XXH64_hash_t seed) -{ - XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize; - XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX); +XXH3_len_129to240_128b(const xxh_u8 *XXH_RESTRICT input, size_t len, + const xxh_u8 *XXH_RESTRICT secret, size_t secretSize, + XXH64_hash_t seed) { + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); + (void) secretSize; + XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX); + + { + XXH128_hash_t acc; + int const nbRounds = (int) len / 32; + int i; + acc.low64 = len * XXH_PRIME64_1; + acc.high64 = 0; + for (i = 0; i < 4; i++) { + acc = XXH128_mix32B(acc, + input + (32 * i), + input + (32 * i) + 16, + secret + (32 * i), + seed); + } + acc.low64 = XXH3_avalanche(acc.low64); + acc.high64 = XXH3_avalanche(acc.high64); + XXH_ASSERT(nbRounds >= 4); + for (i = 4; i < nbRounds; i++) { + acc = XXH128_mix32B(acc, + input + (32 * i), + input + (32 * i) + 16, + secret + XXH3_MIDSIZE_STARTOFFSET + (32 * (i - 4)), + seed); + } + /* last bytes */ + acc = XXH128_mix32B(acc, + input + len - 16, + input + len - 32, + secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET - 16, + 0ULL - seed); - { XXH128_hash_t acc; - int const nbRounds = (int)len / 32; - int i; - acc.low64 = len * XXH_PRIME64_1; - acc.high64 = 0; - for (i=0; i<4; i++) { - acc = XXH128_mix32B(acc, - input + (32 * i), - input + (32 * i) + 16, - secret + (32 * i), - seed); - } - acc.low64 = XXH3_avalanche(acc.low64); - acc.high64 = XXH3_avalanche(acc.high64); - XXH_ASSERT(nbRounds >= 4); - for (i=4 ; i < nbRounds; i++) { - acc = XXH128_mix32B(acc, - input + (32 * i), - input + (32 * i) + 16, - secret + XXH3_MIDSIZE_STARTOFFSET + (32 * (i - 4)), - seed); - } - /* last bytes */ - acc = XXH128_mix32B(acc, - input + len - 16, - input + len - 32, - secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET - 16, - 0ULL - seed); - - { XXH128_hash_t h128; - h128.low64 = acc.low64 + acc.high64; - h128.high64 = (acc.low64 * XXH_PRIME64_1) - + (acc.high64 * XXH_PRIME64_4) - + ((len - seed) * XXH_PRIME64_2); - h128.low64 = XXH3_avalanche(h128.low64); - h128.high64 = (XXH64_hash_t)0 - XXH3_avalanche(h128.high64); - return h128; - } + { + XXH128_hash_t h128; + h128.low64 = acc.low64 + acc.high64; + h128.high64 = (acc.low64 * XXH_PRIME64_1) + + (acc.high64 * XXH_PRIME64_4) + + ((len - seed) * XXH_PRIME64_2); + h128.low64 = XXH3_avalanche(h128.low64); + h128.high64 = (XXH64_hash_t) 0 - XXH3_avalanche(h128.high64); + return h128; } + } } XXH_FORCE_INLINE XXH128_hash_t -XXH3_hashLong_128b_internal(const void* XXH_RESTRICT input, size_t len, - const xxh_u8* XXH_RESTRICT secret, size_t secretSize, +XXH3_hashLong_128b_internal(const void *XXH_RESTRICT input, size_t len, + const xxh_u8 *XXH_RESTRICT secret, size_t secretSize, XXH3_f_accumulate_512 f_acc512, - XXH3_f_scrambleAcc f_scramble) -{ - XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC; - - XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len, secret, secretSize, f_acc512, f_scramble); - - /* converge into final hash */ - XXH_STATIC_ASSERT(sizeof(acc) == 64); - XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); - { XXH128_hash_t h128; - h128.low64 = XXH3_mergeAccs(acc, - secret + XXH_SECRET_MERGEACCS_START, - (xxh_u64)len * XXH_PRIME64_1); - h128.high64 = XXH3_mergeAccs(acc, - secret + secretSize - - sizeof(acc) - XXH_SECRET_MERGEACCS_START, - ~((xxh_u64)len * XXH_PRIME64_2)); - return h128; - } + XXH3_f_scrambleAcc f_scramble) { + XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC; + + XXH3_hashLong_internal_loop(acc, (const xxh_u8 *) input, len, secret, secretSize, f_acc512, f_scramble); + + /* converge into final hash */ + XXH_STATIC_ASSERT(sizeof(acc) == 64); + XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); + { + XXH128_hash_t h128; + h128.low64 = XXH3_mergeAccs(acc, + secret + XXH_SECRET_MERGEACCS_START, + (xxh_u64) len * XXH_PRIME64_1); + h128.high64 = XXH3_mergeAccs(acc, + secret + secretSize + - sizeof(acc) - XXH_SECRET_MERGEACCS_START, + ~((xxh_u64) len * XXH_PRIME64_2)); + return h128; + } } /* * It's important for performance that XXH3_hashLong is not inlined. */ XXH_NO_INLINE XXH128_hash_t -XXH3_hashLong_128b_default(const void* XXH_RESTRICT input, size_t len, +XXH3_hashLong_128b_default(const void *XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, - const void* XXH_RESTRICT secret, size_t secretLen) -{ - (void)seed64; (void)secret; (void)secretLen; - return XXH3_hashLong_128b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), - XXH3_accumulate_512, XXH3_scrambleAcc); + const void *XXH_RESTRICT secret, size_t secretLen) { + (void) seed64; + (void) secret; + (void) secretLen; + return XXH3_hashLong_128b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), + XXH3_accumulate_512, XXH3_scrambleAcc); } /* @@ -5265,112 +5297,106 @@ XXH3_hashLong_128b_default(const void* XXH_RESTRICT input, size_t len, * to the compiler, so that it can properly optimize the vectorized loop. */ XXH_FORCE_INLINE XXH128_hash_t -XXH3_hashLong_128b_withSecret(const void* XXH_RESTRICT input, size_t len, +XXH3_hashLong_128b_withSecret(const void *XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, - const void* XXH_RESTRICT secret, size_t secretLen) -{ - (void)seed64; - return XXH3_hashLong_128b_internal(input, len, (const xxh_u8*)secret, secretLen, - XXH3_accumulate_512, XXH3_scrambleAcc); + const void *XXH_RESTRICT secret, size_t secretLen) { + (void) seed64; + return XXH3_hashLong_128b_internal(input, len, (const xxh_u8 *) secret, secretLen, + XXH3_accumulate_512, XXH3_scrambleAcc); } XXH_FORCE_INLINE XXH128_hash_t -XXH3_hashLong_128b_withSeed_internal(const void* XXH_RESTRICT input, size_t len, - XXH64_hash_t seed64, - XXH3_f_accumulate_512 f_acc512, - XXH3_f_scrambleAcc f_scramble, - XXH3_f_initCustomSecret f_initSec) -{ - if (seed64 == 0) - return XXH3_hashLong_128b_internal(input, len, - XXH3_kSecret, sizeof(XXH3_kSecret), - f_acc512, f_scramble); - { XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE]; - f_initSec(secret, seed64); - return XXH3_hashLong_128b_internal(input, len, (const xxh_u8*)secret, sizeof(secret), - f_acc512, f_scramble); - } +XXH3_hashLong_128b_withSeed_internal(const void *XXH_RESTRICT input, size_t len, + XXH64_hash_t seed64, + XXH3_f_accumulate_512 f_acc512, + XXH3_f_scrambleAcc f_scramble, + XXH3_f_initCustomSecret f_initSec) { + if (seed64 == 0) + return XXH3_hashLong_128b_internal(input, len, + XXH3_kSecret, sizeof(XXH3_kSecret), + f_acc512, f_scramble); + { + XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE]; + f_initSec(secret, seed64); + return XXH3_hashLong_128b_internal(input, len, (const xxh_u8 *) secret, sizeof(secret), + f_acc512, f_scramble); + } } /* * It's important for performance that XXH3_hashLong is not inlined. */ XXH_NO_INLINE XXH128_hash_t -XXH3_hashLong_128b_withSeed(const void* input, size_t len, - XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen) -{ - (void)secret; (void)secretLen; - return XXH3_hashLong_128b_withSeed_internal(input, len, seed64, - XXH3_accumulate_512, XXH3_scrambleAcc, XXH3_initCustomSecret); +XXH3_hashLong_128b_withSeed(const void *input, size_t len, + XXH64_hash_t seed64, const void *XXH_RESTRICT secret, size_t secretLen) { + (void) secret; + (void) secretLen; + return XXH3_hashLong_128b_withSeed_internal(input, len, seed64, + XXH3_accumulate_512, XXH3_scrambleAcc, XXH3_initCustomSecret); } -typedef XXH128_hash_t (*XXH3_hashLong128_f)(const void* XXH_RESTRICT, size_t, - XXH64_hash_t, const void* XXH_RESTRICT, size_t); +typedef XXH128_hash_t (*XXH3_hashLong128_f)(const void *XXH_RESTRICT, size_t, + XXH64_hash_t, const void *XXH_RESTRICT, size_t); XXH_FORCE_INLINE XXH128_hash_t -XXH3_128bits_internal(const void* input, size_t len, - XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen, - XXH3_hashLong128_f f_hl128) -{ - XXH_ASSERT(secretLen >= XXH3_SECRET_SIZE_MIN); - /* - * If an action is to be taken if `secret` conditions are not respected, - * it should be done here. - * For now, it's a contract pre-condition. - * Adding a check and a branch here would cost performance at every hash. - */ - if (len <= 16) - return XXH3_len_0to16_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, seed64); - if (len <= 128) - return XXH3_len_17to128_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64); - if (len <= XXH3_MIDSIZE_MAX) - return XXH3_len_129to240_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64); - return f_hl128(input, len, seed64, secret, secretLen); +XXH3_128bits_internal(const void *input, size_t len, + XXH64_hash_t seed64, const void *XXH_RESTRICT secret, size_t secretLen, + XXH3_hashLong128_f f_hl128) { + XXH_ASSERT(secretLen >= XXH3_SECRET_SIZE_MIN); + /* + * If an action is to be taken if `secret` conditions are not respected, + * it should be done here. + * For now, it's a contract pre-condition. + * Adding a check and a branch here would cost performance at every hash. + */ + if (len <= 16) + return XXH3_len_0to16_128b((const xxh_u8 *) input, len, (const xxh_u8 *) secret, seed64); + if (len <= 128) + return XXH3_len_17to128_128b((const xxh_u8 *) input, len, (const xxh_u8 *) secret, secretLen, seed64); + if (len <= XXH3_MIDSIZE_MAX) + return XXH3_len_129to240_128b((const xxh_u8 *) input, len, (const xxh_u8 *) secret, secretLen, seed64); + return f_hl128(input, len, seed64, secret, secretLen); } /* === Public XXH128 API === */ /*! @ingroup xxh3_family */ -XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(const void* input, size_t len) -{ - return XXH3_128bits_internal(input, len, 0, - XXH3_kSecret, sizeof(XXH3_kSecret), - XXH3_hashLong_128b_default); +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(const void *input, size_t len) { + return XXH3_128bits_internal(input, len, 0, + XXH3_kSecret, sizeof(XXH3_kSecret), + XXH3_hashLong_128b_default); } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH128_hash_t -XXH3_128bits_withSecret(const void* input, size_t len, const void* secret, size_t secretSize) -{ - return XXH3_128bits_internal(input, len, 0, - (const xxh_u8*)secret, secretSize, - XXH3_hashLong_128b_withSecret); +XXH3_128bits_withSecret(const void *input, size_t len, const void *secret, size_t secretSize) { + return XXH3_128bits_internal(input, len, 0, + (const xxh_u8 *) secret, secretSize, + XXH3_hashLong_128b_withSecret); } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH128_hash_t -XXH3_128bits_withSeed(const void* input, size_t len, XXH64_hash_t seed) -{ - return XXH3_128bits_internal(input, len, seed, - XXH3_kSecret, sizeof(XXH3_kSecret), - XXH3_hashLong_128b_withSeed); +XXH3_128bits_withSeed(const void *input, size_t len, XXH64_hash_t seed) { + return XXH3_128bits_internal(input, len, seed, + XXH3_kSecret, sizeof(XXH3_kSecret), + XXH3_hashLong_128b_withSeed); } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH128_hash_t -XXH3_128bits_withSecretandSeed(const void* input, size_t len, const void* secret, size_t secretSize, XXH64_hash_t seed) -{ - if (len <= XXH3_MIDSIZE_MAX) - return XXH3_128bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), NULL); - return XXH3_hashLong_128b_withSecret(input, len, seed, secret, secretSize); +XXH3_128bits_withSecretandSeed(const void *input, size_t len, const void *secret, size_t secretSize, + XXH64_hash_t seed) { + if (len <= XXH3_MIDSIZE_MAX) + return XXH3_128bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), NULL); + return XXH3_hashLong_128b_withSecret(input, len, seed, secret, secretSize); } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH128_hash_t -XXH128(const void* input, size_t len, XXH64_hash_t seed) -{ - return XXH3_128bits_withSeed(input, len, seed); +XXH128(const void *input, size_t len, XXH64_hash_t seed) { + return XXH3_128bits_withSeed(input, len, seed); } @@ -5383,64 +5409,59 @@ XXH128(const void* input, size_t len, XXH64_hash_t seed) /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_128bits_reset(XXH3_state_t* statePtr) -{ - return XXH3_64bits_reset(statePtr); +XXH3_128bits_reset(XXH3_state_t *statePtr) { + return XXH3_64bits_reset(statePtr); } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_128bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize) -{ - return XXH3_64bits_reset_withSecret(statePtr, secret, secretSize); +XXH3_128bits_reset_withSecret(XXH3_state_t *statePtr, const void *secret, size_t secretSize) { + return XXH3_64bits_reset_withSecret(statePtr, secret, secretSize); } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_128bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed) -{ - return XXH3_64bits_reset_withSeed(statePtr, seed); +XXH3_128bits_reset_withSeed(XXH3_state_t *statePtr, XXH64_hash_t seed) { + return XXH3_64bits_reset_withSeed(statePtr, seed); } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_128bits_reset_withSecretandSeed(XXH3_state_t* statePtr, const void* secret, size_t secretSize, XXH64_hash_t seed) -{ - return XXH3_64bits_reset_withSecretandSeed(statePtr, secret, secretSize, seed); +XXH3_128bits_reset_withSecretandSeed(XXH3_state_t *statePtr, const void *secret, size_t secretSize, XXH64_hash_t seed) { + return XXH3_64bits_reset_withSecretandSeed(statePtr, secret, secretSize, seed); } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_128bits_update(XXH3_state_t* state, const void* input, size_t len) -{ - return XXH3_update(state, (const xxh_u8*)input, len, - XXH3_accumulate_512, XXH3_scrambleAcc); +XXH3_128bits_update(XXH3_state_t *state, const void *input, size_t len) { + return XXH3_update(state, (const xxh_u8 *) input, len, + XXH3_accumulate_512, XXH3_scrambleAcc); } /*! @ingroup xxh3_family */ -XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest (const XXH3_state_t* state) -{ - const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret; - if (state->totalLen > XXH3_MIDSIZE_MAX) { - XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB]; - XXH3_digest_long(acc, state, secret); - XXH_ASSERT(state->secretLimit + XXH_STRIPE_LEN >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); - { XXH128_hash_t h128; - h128.low64 = XXH3_mergeAccs(acc, - secret + XXH_SECRET_MERGEACCS_START, - (xxh_u64)state->totalLen * XXH_PRIME64_1); - h128.high64 = XXH3_mergeAccs(acc, - secret + state->secretLimit + XXH_STRIPE_LEN - - sizeof(acc) - XXH_SECRET_MERGEACCS_START, - ~((xxh_u64)state->totalLen * XXH_PRIME64_2)); - return h128; - } +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest(const XXH3_state_t *state) { + const unsigned char *const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret; + if (state->totalLen > XXH3_MIDSIZE_MAX) { + XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB]; + XXH3_digest_long(acc, state, secret); + XXH_ASSERT(state->secretLimit + XXH_STRIPE_LEN >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); + { + XXH128_hash_t h128; + h128.low64 = XXH3_mergeAccs(acc, + secret + XXH_SECRET_MERGEACCS_START, + (xxh_u64) state->totalLen * XXH_PRIME64_1); + h128.high64 = XXH3_mergeAccs(acc, + secret + state->secretLimit + XXH_STRIPE_LEN + - sizeof(acc) - XXH_SECRET_MERGEACCS_START, + ~((xxh_u64) state->totalLen * XXH_PRIME64_2)); + return h128; } - /* len <= XXH3_MIDSIZE_MAX : short code */ - if (state->seed) - return XXH3_128bits_withSeed(state->buffer, (size_t)state->totalLen, state->seed); - return XXH3_128bits_withSecret(state->buffer, (size_t)(state->totalLen), - secret, state->secretLimit + XXH_STRIPE_LEN); + } + /* len <= XXH3_MIDSIZE_MAX : short code */ + if (state->seed) + return XXH3_128bits_withSeed(state->buffer, (size_t) state->totalLen, state->seed); + return XXH3_128bits_withSecret(state->buffer, (size_t) (state->totalLen), + secret, state->secretLimit + XXH_STRIPE_LEN); } /* 128-bit utility functions */ @@ -5449,10 +5470,9 @@ XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest (const XXH3_state_t* state) /* return : 1 is equal, 0 if different */ /*! @ingroup xxh3_family */ -XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2) -{ - /* note : XXH128_hash_t is compact, it has no padding byte */ - return !(memcmp(&h1, &h2, sizeof(h1))); +XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2) { + /* note : XXH128_hash_t is compact, it has no padding byte */ + return !(memcmp(&h1, &h2, sizeof(h1))); } /* This prototype is compatible with stdlib's qsort(). @@ -5460,39 +5480,36 @@ XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2) * <0 if *h128_1 < *h128_2 * =0 if *h128_1 == *h128_2 */ /*! @ingroup xxh3_family */ -XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2) -{ - XXH128_hash_t const h1 = *(const XXH128_hash_t*)h128_1; - XXH128_hash_t const h2 = *(const XXH128_hash_t*)h128_2; - int const hcmp = (h1.high64 > h2.high64) - (h2.high64 > h1.high64); - /* note : bets that, in most cases, hash values are different */ - if (hcmp) return hcmp; - return (h1.low64 > h2.low64) - (h2.low64 > h1.low64); +XXH_PUBLIC_API int XXH128_cmp(const void *h128_1, const void *h128_2) { + XXH128_hash_t const h1 = *(const XXH128_hash_t *) h128_1; + XXH128_hash_t const h2 = *(const XXH128_hash_t *) h128_2; + int const hcmp = (h1.high64 > h2.high64) - (h2.high64 > h1.high64); + /* note : bets that, in most cases, hash values are different */ + if (hcmp) return hcmp; + return (h1.low64 > h2.low64) - (h2.low64 > h1.low64); } /*====== Canonical representation ======*/ /*! @ingroup xxh3_family */ XXH_PUBLIC_API void -XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash) -{ - XXH_STATIC_ASSERT(sizeof(XXH128_canonical_t) == sizeof(XXH128_hash_t)); - if (XXH_CPU_LITTLE_ENDIAN) { - hash.high64 = XXH_swap64(hash.high64); - hash.low64 = XXH_swap64(hash.low64); - } - XXH_memcpy(dst, &hash.high64, sizeof(hash.high64)); - XXH_memcpy((char*)dst + sizeof(hash.high64), &hash.low64, sizeof(hash.low64)); +XXH128_canonicalFromHash(XXH128_canonical_t *dst, XXH128_hash_t hash) { + XXH_STATIC_ASSERT(sizeof(XXH128_canonical_t) == sizeof(XXH128_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) { + hash.high64 = XXH_swap64(hash.high64); + hash.low64 = XXH_swap64(hash.low64); + } + XXH_memcpy(dst, &hash.high64, sizeof(hash.high64)); + XXH_memcpy((char *) dst + sizeof(hash.high64), &hash.low64, sizeof(hash.low64)); } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH128_hash_t -XXH128_hashFromCanonical(const XXH128_canonical_t* src) -{ - XXH128_hash_t h; - h.high64 = XXH_readBE64(src); - h.low64 = XXH_readBE64(src->digest + 8); - return h; +XXH128_hashFromCanonical(const XXH128_canonical_t *src) { + XXH128_hash_t h; + h.high64 = XXH_readBE64(src); + h.low64 = XXH_readBE64(src->digest + 8); + return h; } @@ -5503,65 +5520,65 @@ XXH128_hashFromCanonical(const XXH128_canonical_t* src) */ #define XXH_MIN(x, y) (((x) > (y)) ? (y) : (x)) -static void XXH3_combine16(void* dst, XXH128_hash_t h128) -{ - XXH_writeLE64( dst, XXH_readLE64(dst) ^ h128.low64 ); - XXH_writeLE64( (char*)dst+8, XXH_readLE64((char*)dst+8) ^ h128.high64 ); +static void XXH3_combine16(void *dst, XXH128_hash_t h128) { + XXH_writeLE64(dst, XXH_readLE64(dst) ^ h128.low64); + XXH_writeLE64((char *) dst + 8, XXH_readLE64((char *) dst + 8) ^ h128.high64); } /*! @ingroup xxh3_family */ XXH_PUBLIC_API XXH_errorcode -XXH3_generateSecret(void* secretBuffer, size_t secretSize, const void* customSeed, size_t customSeedSize) -{ - XXH_ASSERT(secretBuffer != NULL); - if (secretBuffer == NULL) return XXH_ERROR; - XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); - if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; - if (customSeedSize == 0) { - customSeed = XXH3_kSecret; - customSeedSize = XXH_SECRET_DEFAULT_SIZE; +XXH3_generateSecret(void *secretBuffer, size_t secretSize, const void *customSeed, size_t customSeedSize) { + XXH_ASSERT(secretBuffer != NULL); + if (secretBuffer == NULL) return XXH_ERROR; + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); + if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; + if (customSeedSize == 0) { + customSeed = XXH3_kSecret; + customSeedSize = XXH_SECRET_DEFAULT_SIZE; + } + XXH_ASSERT(customSeed != NULL); + if (customSeed == NULL) return XXH_ERROR; + + /* Fill secretBuffer with a copy of customSeed - repeat as needed */ + { + size_t pos = 0; + while (pos < secretSize) { + size_t const toCopy = XXH_MIN((secretSize - pos), customSeedSize); + memcpy((char *) secretBuffer + pos, customSeed, toCopy); + pos += toCopy; } - XXH_ASSERT(customSeed != NULL); - if (customSeed == NULL) return XXH_ERROR; - - /* Fill secretBuffer with a copy of customSeed - repeat as needed */ - { size_t pos = 0; - while (pos < secretSize) { - size_t const toCopy = XXH_MIN((secretSize - pos), customSeedSize); - memcpy((char*)secretBuffer + pos, customSeed, toCopy); - pos += toCopy; - } } + } - { size_t const nbSeg16 = secretSize / 16; - size_t n; - XXH128_canonical_t scrambler; - XXH128_canonicalFromHash(&scrambler, XXH128(customSeed, customSeedSize, 0)); - for (n=0; n

(<&BS4);g5$i# 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 literal 0 HcmV?d00001 diff --git a/plugins/test_data/example_item_prices.caterva b/plugins/test_data/example_item_prices.caterva deleted file mode 100644 index a75de6f65a8b3e68df4cf847e8f9c7fbfeebdf46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 159155 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 YIeizAZ$^-(kTnXwGQH{3?|%OKU(mJx;s5{u diff --git a/plugins/test_data/example_rand.caterva b/plugins/test_data/example_rand.caterva deleted file mode 100644 index d0f0dcf42d60bcf2d044044757f1fc15d846127c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49045 zcmZ^~1z1$w_cnZHsG+;NyJ6^(?hXY3=@bx=8l<~RN*W{tDJ2w#p^+Lwq(nMJX#^y` z@%jCKum9(JulHQ6efGJo!O(Y0GR&UA^C5IDf7?G#D5y1KMmDC*N{J5>^}_z9Tbg@`sqJo z07m6d=YNj>^UHt>>N){+iNFB>I3WPsk^+Du6#y*L0RV^v09ZHyK#UInB7^|ohZq2; zN&x_+Gyv?Q1TF&rzOn#7hkC9$2mrX=3EtQ$f_`8FuhPUsgiLYh5um6~yz!j(!-xaN z^0@;l<(1Sd4V0BjTlYOYOe;sf#--}wdU%TVr^Ipo;=^D9*Z^)|*;v$qKJc-AuGbLb z_xNMg04Mz%h3p2z^vc2J>!XTGw(_nns?gV&jD=Nu4gHuq)TcT9Fqtd1t9)7a2g_3@ z%@;4w3ripYZ)DGx&o#KaXz*?H2xrC zspeT4OXxE)Aq~>Hlzm07sVL-Kg%H!jGP*ZfGxTdEF~|*>M|iJVI889$LhE(*7(xr2 z*Nn!*K7^*MlMnBexNe&EeYz|CTia`RWcM{beyl5)2K$8_Z#U-F;9_Ccz^4IUYw8x- zaU1#~7UZDmguP?#7d4It@n*%wvEMb)x}I_q&{b&?3X@+mn1Q7KLK%$>h!6^k2^nV) z%fth+0n;ng{{W3?xR5{^A3J*>{*Y zl?j^ZKo$Ziv6@P$rcTWD_}$SU+Jx3}^YN*;4~AW=0FEGYC{)BIk#p?V6;xJ}czNMu zpsE5_Mb7#5fqtn%$w9yWTKvr;{TE&N&U=?azt0`4F2~+s50$aqC2e6>6#I)UGca1}DD_xcLGTsL z)$CY5Jfi(t=8yoXB=u)4w7X!~!^RH&QpitSY4Db_xRv>Q)>Qj1diXfNGef8-lOGPf z#%iQ89Tr|$%p4?^%~lxHkp))H1#sBH8q3h1KHIApE&q1#h8x_A`$}s2)4d92fD7OO z^h>wL{nrCeKL@^n)9Du+^tA>dIqWEOsnd2pB>SSxFoe`O!|oJCi>-0iT+mTf~kFHvK7ROvS% zM48-2rTS48Z>?tQgHW^y9ThhQAY5kk{`?auM`-EC zZtq);zH$zi)SqiRCl7otdmm20_!K0EW)%W@ASb@66N+vV-ZeL-744RMIvtl%k`Z@` zm`H@ew3m~;lo3p1NslSitC<^ld0ey}%t;^&SZWFWXs9Bi4;Q@F_c_AmCT-7FKKt2| z0X41&LVJd+=X@8UUv;maU7?d$P!G*L7e)xLe?Obkc)diU>o0dO+4kj$=t=I4WzZ4# zH+N&ceP%NZoxezu;{dJ1l43;uB6K7TSrGFC#Uyq!O=9#H6dFp=>`#~sfNFq0dJ~hZxZKhd+U6mAxP84v>WkO|x8(HAMQ<~A9e$edN1sjhj-jm4vn0qVc? zVD7%EE5ts5 z7W%Wra5?oM#|cHAr_b}b)MfiXNstueNDr>TdZyXYa%_}-@bxJ7oQkzW2G7WwP&^bI z3L$17zyEu6+syg5dur(%J|yw<2yO;a{)^`rHn65IBF^NSK>&{j>5~~3KqSwJz5)fRgipoMEk{%BrS zo49R{OIHld6`4s25(}}0?_5DRg+&s}8CCHD$WpH%(QVvg|3gzb^}2@G$f$nvb<-b$ z&yFA7WOHd$9iIk1mJOEA4#9@wAxoUUqp4OK=JK*=R*FR@qfKb2#03#?BCv{o`H&R3 zKKJP$E^%UJ$|z-Q=@D zxCRj}I~WxGgb$&mWnfVsr&dq2@v-7i6+?gchb)&C2E_)wz{h~9t)mJSfF0li`UAdn zE}C)a+@KvEoZ(+2$ojy+%c;)2{FjeqLyMsLIfTjtGE%+ElgcVCUS!y2n5z}nv#k*Zi`APZJG;(iu888(R9azfRp?`FZ( zGj#gGR!ZHo0Gb=)3mONZ$B;Mvzk&4ADA!K`G^RTcYd4nHDuxI{Toa1*vS-I8cVMk* z;R^0D%~39{w=|7LOd<|gimKIiYmBRL6tXKFGf%FHMDhF5f)`uDw z52lUJfZE?72dwBZ1g6iMLos^i071@$)o%H`!ymL_UZWpe9eSUlM=`xk<}o(W`U@xq z4nUFqp9LscmEDzf2Hjf(&&(2p1~A1%(Br%c+07r`@auCHkk_ZzUq+?GeK9f z&X5!5TC|75>AuszXQTcwjI_N|3Hv-6$aCG_YA`~u7z1snbVI-17V;ICRmF9P@@zJ- zq0jf+<1#|l*h)F{ji8#F3{OW%n;l6yoU~y!o(D5jP~896`P8HTy8jkR+{0AM!=;FM zA6~<0cu%wXlvJ^rSm?bA*OF24BfLfdk}_$caydbVtjCvctk2$#!dgaiPE8Lr8B2=1 zDOnTMxCF+xhfkfUX#4f|N$j|_W8{zhZHaaqAy1W`QTS}XPYcby;TAM!ziapKZm;|B z_~#sn1%~KfG9qmwl9usN5V!xA z*PVL`QMf94UxG~}vnxA`_^_pTutE@}Xq5NHbt@hZkZ1U_yaJe;vt1ULgsTV3DD~X2 z@Ez-PWYU=~W)Cqg15&6&OmtL~xGP_g*+e#Ad)87=y-yOm1yvI90PUtAE-L*Z!PxO+ z!d=(Gi<8f9GC-;R2kuu1aMlASIJN$y@9&sv<(uEUhy6t|%M)EWghtiEC4xyjKZ-#J zJ+%QS6Xyh)uRtB)y9c+%p;DeN;|F^b4i)K0+#T9Ba98Uod#zpB#xlDK2sQGEzmgQ; zzc}n~`5kyNmjKr*!l_oyUY^_37rqmh1t?g7vMd2V#c~)1S zf;T#M#;Sp>Y6Mp7FVnD8V(f&s-(V|i9G#tB+J|T^pX;iYXlMU~${M2GK!%-eO|*n; zG6cJqkv1|o=21g?8#OIP@ez^93AvmjajGNlIMgJU;Y>$dWN5)sKdio3DbFd#av}QJ zqPsDpByf>QF|xr$RaLvGTeyc=mm@_DC>y(Od0ZFR+Av_gqJIX`tro5pF#Y&q3FYay z$T6`aUW587W%Uk>uO&C{_Je;&Kkmhzw;#B7$mVqOMQb3jfcf{oqj?+*~sHSq#|K zy$_rv($a|b!O(r_SfZ9a^Ijnop1U7$C!YygE(pzaX35cmrHwbEY={D=)4 z>x+sp`H9e_p<~rLP)K1Er$KblV|X$Qd`W~i5kLTXNq4rG+gE>{&GRLdps*a4l{+>! zYJLnIIUhTRj|DV$52P`qc(?{k?wlS+qet?92;b%%4CW}8k&FAOY(>Zg7HRUc$t-Ne z(8z`f=);T4IH^sX7eb*jHahe*Ny7G6%Z~fe)5|uw=py0er0wtSSNSlkBKqax#%)IJ zYNcwb`H0+BDB6TUL{fkO`y7ZViC^r-e9*Esb?t{~rmD?t%Iy9`w{JZ*yVt2bhTrx2 zQ|}G*x5X4@R2mQY&@|s1`8(XGUMLZ7-R}uIdSFGEZrAFcKc?$x+f!~X)aW&5WDdgl z3nB&tFiPSQR5YEUX-a|Iv6nkK)K`ax+p9@ECM@5fu@Bdd4%YmlO&#}E16{O8C z2v3LSjTqx``l^nkrIKxhOsW&dHf1gt##y5rOZn``T8X<6&MU_DGJ=_&Td%Dvbl={< zSZIfT;m&zO^XeT$=_RX$>!{{Qrr**d3>*dsgmJoIcXtYCeUZk73x+D9ux!aT?9Wwh zK1;b5_tpdhcHARU)M>u=d@Xj|(ML9-Jy<8V)ug!IFSx|6#y$-VxEkcj_gcA{i4d;B z!TXB_IRqdX=N44-ouXmGdAgG?6lXPmqi8PM+`(7@=z8iLL!vZ%Sb=)sFt7MZ*u+3-Se zy&xdFQwN%DE^g{s(6G8V#{f@66Ri#}A!`~MYHq@8)QgIt7cK%WZ|_1e@jqE1@k6= zZgs5=hv+XJ;t(KXj9btEr5!>FQ?-m#vkgTZ!O3inT*WPJHEqwz<$9}~nQy@_nuGhW7?FbU;(Zbt?rL-V6B-PQx6Jxq5O=hQf<7+pf3g((!q z^A?!a(1M+j3oVBdbU82o5RL(Xn!mh8n>qVDD1`%j>ju?kH@RTE6nm-m`|*jR49PHDgEwT zkI_j;UIiwhpgqI$?-|pK&nsYH9wWX2%bjqu0833cojt-HVb+EWLbOd*L8OUk$O>3W zuK?R1$!gq0(C9`Sx9FHn+WJ_U&6`}h?ZWr{*~ID}nFW&a$vO2P{A`cx z1n_K1555a-W<|k+@yd8_8YFA^7cx;x8WSJ@EV|+z-WWmiksFC~WFGj|uadjnPs8Dn zGdno%j_7y`2C$K51Bvv6iPatns{y)_cm1 zVa~ydk}=HanEj(i=~oVQ$jMHd%a3)L z3LCAd^dQ1kc6B*j_};!ZgP@Z#(Q-&@QqD3f$I@0G@`+Ab9~q9hG4Wrt z+T!^4EkBmCipTb}m-nz5vZLR-SgZu@x(w0PT*)(94zEiJ*ved@%ZHhPNvL7;e3q_> z^E}qodHi}M>D^WWT#`~YJujLkt#;Vt7p>)!bT@-tI%=(FD2_!!FmNoBhcq1Q^1T%= zAW04*l9H%EkPBEBN6Ux{I$>0y$Y4nG=o<x6%EWHd>8{S#|cZnI>6xV2mvS6aNNaX`+un@?gR%lKoeqjN8_X#~U7Q*xlp@X|S=RV#fNZPMWw`Y9roWqQ)R3Moq zcFRl?r&Np{^F6IF?c}vNqM?tBW%kl$IOpBVW8rn*~#WDl}v5pp}C&@@&O`qz0==?bGkOJ8A~0 z&NuwF#lr{2-{^0ZvA;I4=jCv|uNj`GLKHY&3qVfo^@ffJd37t^);)YWmug2H9hMiv zp3Fn=7Y=sRLX^ZUC}fOFkHF+}$Z7xl$$c`{>QvDTx`SuI9d5`%7NkG0gwHL5ZHzx- zY54;)>@%iuC7vojg$qrQFfln6r3xNfxO4COVm|%!bFbe#Fwr83;(M}H+n*xUvLa7R zI&5#9ilzcsP5fkv=G$y57a#(d=me~f5EGXrXZx2dbF#X!Y(Gz{K{m(%bODTXfM1Yyy6IS{0V?>OhdU)aM;Az)D=7 zz@|H&R**b&(btWm=CL~YgS(W%@R`DdK8OwAKv5&*En+=)^qObnQf=)_XaE5MP}_ zh~Ns45w82lj-3U9zx5|dMeF4O+C+X2M%N3i*xnFVk>n(dKx%s5fj8H2j%)?6nBT1m zxAV!SO-FJ*0T;4>i@=9aYY^Thbp#~`*NR4j)Db8j@$8S6hp*{=ZGKKF;=3xF16m?a zUsi8yYjsrY<2@2qa~h|>{3_8{sLhzma=Sib@r{7Ui7YocWa9Nx+xQsa3m@>m*Y0D~ z+Wp7djUSBk6eCK~7hMXH45-jRD6f=PG~T8kO>Sy;bb_8gZ^}V4Sqb`B>}4#`^pUGV z=$THUOZf$lHMd}j?uvE@Hj~XzFYySLPfHZIZm&I+BsSlRwd^gA?@L z_*Cn$xCj68uKipXJQBH%vNxUCTEp-5X_<5%En*&}l>EB86UE{kDP8>}^<=`sMVp zqA>$1TFe4`ISN6U#nGIm%o^810pri?;sEFahp)$4BOAhvakQq)*DixZ{5Ek9qXHS^ z{EKv#qrG}b&q2#6&V9umltnQxZ8!n;D0L&#m3xPaMR~rQA^-AB=2>kQDRK@A{VBPe za}boPJwz1FS936EzJYsWa%Bh8gfrPhd-X48hD}VEWo8xX$=!-Y<;0wrURr0_Z<0}A|2IQ{a;lw>X(G^xqyp*}Tj7(Q8$hlXol*^rFIhFCb1=MK3X50!Q;%vtnIfgQc z^155q4X?xru?#W1d|z=@BVAE#TyEcR$!jS;-#zfVBk1d9lZ%2OwuE5u{jHw%=QLRX zR35yOB~(i|FwT-wQrzDj{?{$rza<5d`h0Fc|BePjiN(eK#EmSdTA5<3^z7ZZ)XdP+ zAT&J6M2?C)fZjJ*nfr=*K zSOD(?+CfFTEYS`)6Sj~Tu+Id_v^>SI<2 z(#ITQALd2-u0im0b0?_5Bj{|qQZ1Xl|Bh12d$`*w<~Jz%w?Q3P_U|mt#sA{)Fq;FB4EHO6(=cIX4n<5#Q#hr5 z{&ZL7LEl(^uOU&S%g#3T37=0TCRRr0?+k0F>iZ?K?r<8LgsKUJmj_q_GQXh3=`RiY zGgvO1#7Z_;g3i4gHxuRB8hWOqY?opMcP)=bix#HW(KM@tbGdUZr?g!0OH$$t&WKch zf|#7cZo{Qq{V~ucyK*l5$dDY2XL+b`T{-`HIK{%!C`U zU{k{!D(UQ-@JMQii>m{M52p&X)Rk#cpn6oqUr&n6hlFF~HS{dkGBB|{ZismXGy9u;=f<=+$ULSH z=iCg|9;3_Hn5&q;hMdFhCB958rL5Xj+Z)3h1+Tu9O}uSNhvg^}tb*?oj~!f2lkc(v z`f5JSz8*=Lz^2k@|4zPk?=1Y3op9~vJNf&f?JxL_xC4%I14Ycpwf+K#q9a<<#Kr$) z6A(F$mf)N529YOb;_E&e`?($;MGy{|F&T{HlQ*+;`LH>Z-%-zOeCA&_OBN_0B8W1Q zyr`(=SSegCRe#l&F0bH#d)4<;BWvoa8B?AZ4%NIO2r-hy2?><1CZL3nelhFSG)*T= z#Vk?0WIoPVJxadJy4Y{);!6E#`O2`-n6&qoI`F$+UP+!6E@YE5XsS1&!C+iWfyHdD z4OdE?_iFdzFRAgGoOR&%2%ydt7babJ=aMM4wV{ zP3{hvaz$s;pHTcWG_MN#ae0e*5*nmL%6{H$eY*ONEc2$m4KmDEGNgpjC(&wBvJsD1{2gHAFG^ zJ9wU`?s21wYPvW!5e~l>Z}x;e!wIuvfCTuszt3=nm=D$qwl@?P1=@K}ap^81@3j3P)R>Zd;q)vDxE2 zUb@gh@nbr*?_PwUHD<)c1qrF18oM%9XYaCPZ*pz1=XoP?E+HX^9;4?wnJ;wCPeA<$8@>H&ZH7-Ls=1u5Sik9~ar+7(Du z#U|5ZuY}fZYF*OgvNfWj&^0;R8s}D*dP&mN&6mBGcAr%;I0x0(ac-Llxi<<{%TC&Y zdMt>Msb0ztTcW!P1b1)`D=tN3m63T>qvoe|fkmh&u&_;X)$sD};^Aok4os{1Q7&Iv zLC=j0LdPMF{!r^D$nq||4R%2@C|bxd`zEe;$yf3Rk~t5b=<>(g`kD_i2Xjcg4VsgY zy|}{9+ePWcFyj+4CMX!^xoHRuv#%W2X6P@DJzbsXOi zLb~QRPafog@&JnpP7P(0&%@iy-oG%1-5+Gntr zqA?Na+Rp2+l#$P8D;b%MCwQe*%&+eda7^uYbw?%V7NjiSm9L}y%TaASuGjt^p}fgm zRBN1PZC5~;3r*14q&Z0PFPMHPT_i;15u|4jFe9Wt)4wdC!&PzjYn~_}4-2P>2(bN( zNy=Pl-z_0@ByH5{947Pdz7h9$DL1 zeWRk$yYub}GZVCffbl-$eb(UB?H^u3uR_jR)*+CB4r0N6XP@TT2sx$?VS71ZgAc~> zDdJw<=e@EI*U_DXxwFkTLcF(V4`%}I8xox*-P~*h^uUV``il3EC-fIVMGVMu=i|;x zZ8^&#spUxLPc3I;fweI8YT^{csxBtlPRK$=MPl=rsoK;4n8qa&|j!n5Fu%tLJ- zJbM)<^)cM3`!fk{wQxuWB zYK^fOzW(KlN$_glxKedqp`#o!sR3d{5oUq_CUVARTw=9G?om<0!4B^gX$U!-bUC`K z_)XX6<#UiAZ4$m*o7bltuDHzUp}|O~l6ZKZh6aC(7uv(y2S#vEQs6k8YB|!2Ke=kc zf5q#(&e2Q}ojibIB};kp{s~`RT7ZiQ30ZY^L5jkCedEtcfshWxv>I@|?dte)-R$hy z4IzcFeY#H&IH0FUVZpfXlt0;H;n({2Lp&l?6sH7W zv^_&U0q*h3W!Z27Tu?~%?@iz<1Bw4=Vf+@A(y+$U#@dhd0Y0UVJRy#3TJ3fq3PpCG zsko5G%a+_Z1f&RuS7l(|xg8nk&Q#c7@a~H*XHSjWCaiW}|&1BBH=E-&7mMlHWyPNah2n>2t1Y&^jVN%(E$wn%> zVT=?ySRb+vl!!bObs~F`hY&fdsi$oroX_3wf5p`cOIDVn0f~=2&vbn{&)rweJylGg zp&AFNW-YZXCk}1)s@W*#w8=bh+fqGtE(eEYh{#~5RSP3t=yWhFf*Lc>S!U4wXku(g zD7bDB8>UV-AiY&L7WeM{QGgaz-D7)~A~{!JL!b{JpEL-tn_O;O=Ie4c-JrRrb%Ms?0qX85?Tg9pDJ zRf9e}^W3&kPNpogH2MjK)EXWpT80a)lqJ0YV@gM-e?r*VgS-tcN#xUH(zq&*1F}>K zhSW|pVbo-tQ@;X}tvQPQI*lOb$MhyPhyig22@%$q_X)ANn3nfn6;dlx&v|&aW;X zYDq9guC~v8GLVBp%#XAh=L|g+xQgS$Ccs7yU#HrQ%&RaC%^n%eTfooat!q*pWJ2f) zT-~LlP_CV3Us;yZ0s$gRV*|4^qweS3-5>~1dPyVMmo2~lq1VZ+sdhck8DCC+^z608 zs@KK?s$Im?dS4iyD#89EC;GbC7vXZ83Fd2!r0V#mLv564ojCY^A)$u=J-R#sLa30P zEI5cIbPh$HIw&1acyUOT{>E=V z7)iqmF9%*}6lwA{hl|UgQmp{AVh~|Q7@n{AkFGFiiet&Z*KQO2TH=Ka8H3d)Qa7zb z0VJlx;C3sH=w^B!|GF2&nzw4x+?~_F-FS_qC`P(E-X*2tp^!cKzeC~2cs&2`kBdzm zd8{CxL|f#2&7K~z2b=6u)=oSHY?1^<&sUtkym5G+2oeM0cZcO3mkrvBh5&dB<$;JX##mx)NlIEnOK5j6nmqslm z=p;z4+OUyb$%(9DKnN4i&uB&KHnaUhBH?R(3yCadmo1L${%$AXrF#g))Y+w+Qc_eH zDN^NmN%O0bK9;PU_MWK|Q#dZYk;{2Ws$Y%)lh=II#g9>ahFvkXzbY~V0bUpgizqsh zKR^?_sWdeoCsrNQC1^Lu%)vllCa6ibizL}cQi!J=R(#!0_cyqCrWeVTA|F@9h>Bx! z0oaydlt}F}@lFEyjCus-iS#@XwAgEvG;k8eN;?iJ9#C)k)Jdfk<7Rk2Z^CH-9Y#Pq z;oLY6Beon@Ka7Tfc)2B%1#CXOc1V{_ZK-!=4Ix@~p$$PR*vk{K#&aBukY>Z-8Qt(B zu9*$PpB#NqZ#+8q>cl7PCB)W4e6iR4?4GB>a-aGFz~#YL#*1=K-M|T1{*PXRO487c zWYMl)6I3#uHAsPU=Zc#tum|AAbFI7e;4l3W!Jz z1ww@Qzb@^>_FG#^w>`>SoiJ?ahXK4YHTav+zD=dpuf{fUnTOb-JR}NSXgx*wZTfgm zwLa5_CBJy6-@OB3<7=^|a(d@C!Sx29adL z`35g@Vovp_dVSZeSlM#^sSNjPFv(wFR3ShFA>TjCk+QFI-Ur{J-{F(#d|9gF>sM4A z=Gh;!(E6xivg|c%g7Q8Krr(By?Vh>fpfe&UOTAgLQOm~ayv|}$(zw>VPHV7X% zNX=%DrPNA^&$DkV54?Q+ba)HPEa8!5j_FFT3$7TYe9o`0I z1T=m15H`EQl#k(e)(*i~?wxV5oY!FN^FP9Qm=i(B6>1Z-p z7#*(pDCzBCi(ymOUniXA5XY6im>Y)@KEyYC^Y8bKlD z6qC@apN{mB`hU|7_5IaWeftC5ebVM3{pHm^Tr|1b##z_ zrz5*ZM19I8x(!2%qs>n&W9o%(sYZ}CWX&2qdzSklT$Z7X8w%*)(4pKw4uBUKdnp9X zN`6}dW3cIav9b=sYe1?WdBT$#)`lIF4;=|y%t|SD92fL#dZD(wL{rT^)>Wq1Z*46* zqO-%zcYaH5b|kM0eKd)T-<&{-Wd}>CL!MS$CyM_tQ=!F%ZVd+ujdjm=nS)a5g2;{^a5HVJ zmH+=1Z5CW^3B}iJ^bI2&Wzjf3RFC+nOTUX)kk>yd5t)n%kLJ+Nz706?YKzl8qK&n@ z{YvLM8ZbA1b1#k`r}gd%Sk#wpLUDko+kh7p#>bCIozc2C>-v9;|CRh7Hy!vG43CR@$>)`_=GuzS#t|UAeWN&$lr)Kd0D! z+bYv1|94`UQ3~%rrdK&K+!>eSO=TtilSpM^)!eKHk?C zxG|hm*=Acj&CxRHA%jC*K5SopmG)*OTor5ElKh2no{Qxr4KyctL!5>fJLLRI7-*`r zss!-3D8}!NovP&0p(SXlMgU66PXRlE$^!nkGk-E}%|>UBv^=Z$op~?s$*17Iy-~D5 zByNAW;A<`XvDq7tEgz1*CF%*Kb#uSuAdskL?#m-HZ*HMQWTDqfcPCW;Lp^GWwg`3o zG&*hTbdsOP7&r_doz49=p-*3vb?~+&=S?T8td~V}_z#HS!O~I1aTRqyl6O^JEc~H4 z=-+reN#qo@G)YTy92GNBPqko`sAJ~7UvyU z7h@$u=B1~07eBsp405r3JI<-dZrB-xqezs0O9Ja^I4um=hv)0> zr~fvCwKNQ2$yF=W@hnX>qmrv1NA(dXWt4UPhsGb{WT}##Sdzy6g|*Pu8d#@8<6ql; zt#O_?@JsI@oo}_&)%)^Qv^*zky$^&**|Fn`i2C0rP8Och67}Yxp_<&HSW)D6#`WMH zRFUNW6gT!`02w$;JO=t11BY}Bgf7VEtrr3tLEqjxjkZ(IQVD(uysDp2A`zYzM*Lhc z^~C3n?P(%T4I>LrMZYhbKoZMK#N6&}osZShhy7IUWf|@YSx*_sNu3pud0%wtWSDK* zk=2{%#MOlWHo>5Y;6M(-4Wo9RH}Sl_3!N{%udrK2|8-RU#_KRm$SqyO(4EnxsGBTPKr zx;V+8n1ZF00}TaxU&Hm0DL>kUnHlw@`-8$xBTK8sRYkpsutXko?mjBi}z0*1cXmnhMCXY@O)jMzR?&#@M#(%ytMrA6TWHQml zz+B$%^pO-P*ssJ7cuZ~)hxkTVh}?|6JIDL`N&xWh&t!zl%;l|LDSUlkIeXvv<1{jV zzV7Z?zUA9ILNCQ(+e5sZI=F_Tcl~i@KG~q~5t9IB#A^gvxFZ*TVNDsc<7T!FZ2>q} zYb!+396fsIgzY7t6^L=VGg0DL=uIT|#*|KxX`Z$%cvYv2Od!%-)K4};;*jqZGP*-L zb7VR|aJE;pgU)&YL|0AZ!l^WH?o1$LXyjoVx$w{1Jq;d~v(VDyQW)7x>12DXF#M$f;&J^Ld?qN0_<)g5EVjUp^Q>&HnS zKp1)(1PPHhNnnLP|{bzXzT7chDi|K*S&=a~l6!%sP(yWf#B6i)R#`_G*JM0ijy3zi2L~ll2oGgsX$8{}nNHD#kv~Nx6v$XzpSHPP3QviRQ{+Fl ziX`%K-kAFFUSZzhe-E_e??B(7(AA)34>NVeZ=opJB6Eh~trM%fU!|%`F=xt2%ShZQ z3eb^BXs^Ro1p8(%TbHCBv0K0NTEUj9frfecc{E|e zDBPxD(N%k*uqkL`ynMI$e03ETB^Q&f(y1bME^7Jmw{kf&Z%{gqJ71bNX*NC{KF;kS zMO#Z363^LgOuqVI{NGaXe}?_PYq=ZA|NABw|Ly!!%l#5{S%;ENlrW%bx&=@oj}l8% zZMQp0f>DA*)p#$XL_SCh5PJI*-Rx9~r~5KRR+m(ab~#e;eC(pkO21TGhYwZA z$G_-u`tV{APGFPZJe;|Wd(;;ofBi{T3Qo_c{)bW^N;G*;?~D-0XktK>)zL~^BHh%b zW}c=Ut$m_t36gAkCn9+CJ#<%DmY}kZzwYTB!)at}OOciWs||Gs`t`n7BsZs#VSUJa zZFo?|-i$y_^RIIrS>TA~TG7=*4xI#OP-IKsG5Cw1II0HB04h#ooDPt_$Pi5#7YOgiA(8!8LSl)0bPn|El=`QH}XLf>vzvY;N$iWidf9 zV=+BvssfxrURa*p?Zok_SzZ)gi%b@r;OxU{zZdRZl9g2PoWb6Pe`R`&_=^@*NdVdK zPtgsZ-+M62j48C$;8P?Q`uySMC8`1i))LD2baQ&+u_KJz*?Jk~t-PFIw)_Fe4=uu2qp8uV) zF*shA|L3}sGKnxUzZ1V-JNW5b_{zz7X^z}rxhyV}F{S&*W4|=YU~OL-XY96e9HqaQ zq6$i$j0^okWKY%Ss$XSag<+gPct}(E#QSx0$Ls@oxX_NHEC-5Jzixb6J5Rkmr>(6$ zjw$;zfmOAn!py=>o#N!;!HwU?B_tT}l^HM_Dpp2%QI%Eq*5MoI}wm%8t(O@LQ z{nE`OoW?7JNmYMgbb9m>&cKE~?L4}Ttg0ymHKfC78RscKn6a8LxZi$sdl=wy9v8&- zum(9vxpJbi;_h?x*5`tu9fm0f4^@EgS17{rVdS@duk}j@e;S^Byzsb0l{J-UMm(4@ z8lg$ch%*>NV?t$997K^jj&}CFEvbK7O&Qvu^2|qWAX#5?jeKb zAk)7fKEwgOWJt*}jd0Y1G@pc0wr?DOnZ|0!!Qo745w!$_Op@fy8etWaUzrno-<9KY z56YBdUWZ{5@>xe#i=MEpsuzM;P#Yy)fMUgOfdKPS&Rlb1wE|zpd@Tq(Fd!Yx}VE-0eFz} zVH~fmP&0>*vywHDdk6Z-FRP;C6A0;4(EHe*UA7UlUUc9iCQ6r7<2qlaSXsJ1Ex==BDNEn89e;E&E={tV#n9abB>1QNj zcDtMP>sa7}{2c8t_kPwhuuPe3EsHnD>oe>XEu~L5^=6QoyF&$yIN4`m|G-c#t5WCveonb5``YVD;VYl(mxw%qAHLF)__-p0hnF#eJ+785IX>~CiQJhmN~D5ECwDfblAEr zQgfZtm2v(MU*-yZ#Q6DCuDb8b3JtYK&F@f-HC=cpC+2qE`o0vId1}$Gz8$tubcH06 zgKg)%ps@Uv&{foQx}`b&2k9b?IfvXDE=ezN9qigugsaxkflWTyH3*iZxsir1J-tf!cuT=vkOdUlx)se z)Nw)80~KetcoG7a?;aqt{^^zFZRCmUCcmcOO~HyGSSB0KCKr?Vn(qe+o~G<8)OWhmpMQs!$FoqTNyp4Dc0tL@ z3faD8=Uv9qYZ(V6Gr7o31obArdZ|B8>wrjc)&)d<(5om8*P=we^ZAcByKcfiarPX) zdm?#@5pC668ca?P7#j$+G{jyEz0Qr<_m#lHU-gYywV9lBVyYBfwM z?0&Vu{X_+@^IYw^;L6c^hW3x4<7rn5ax7}Zbf!u}=LfO@q!z{1Ct8f`T%^d~_z;%O z@3w2-2Y|^Zo=(r$KrHd|^~t0#sb|h+7oLf!a6m$MY0@%zKSUj*yU5dyurv+?Q0Dix z95B0xvV(f0`1{9i(6BA!(4&hHeg3$cAw=q8p8$HKx}|BpCdEIG+Fh_uoLU zM>+ts6M!!H&Z)I@gOR*qut!zUEj{K+2$b;qbT$?sj!ulrjWq1@@k)7SV~V`^$(7MLaNAGSBgsJd^4DDk_q`A}%jkLW z>&RjK({d+KSGT2CTaco%@aY(q!te;qsfCu6C{b`(kz4NPF2mvK!-p3oOduJ^2FfOe!_0#Zp_JYP4e8X0Bo{;Nq=cd629)|D zxcwChf2QhtfZj=4;CHru8Xs3E8@V4J z?Y&!!)n7_mGn8cC=T)1P0zIpBACUeq#?0znYD!lpW5k;$A_*5go`dHZtU8`BkvXDQ zaJ0u}o$!7gIoj~W2yAd?-%vjizHq86#fs}uc;9#V;vR)LBoB{+^6^DU-ly}`<=B`h z92HFOL}8dv>Cu;ILKON<^}}+_Fn)A=hTX$f*~_RofjV${o-(!XBUlUq^_Th7d!rTS z!vWuBDFEP^s~Y;EKYgYir65U#&kmJN`Aov=)0ySu)PB%ZGxH~LYNm7NNQ?K9b?5Hf z;yV}bu#L`?6VTlFeN<;czDm>zSHcw}=dZiZY@*^6NsKS@e%0mKeW_s`5BbUQP_Q}c z<7r!B=rMQ*&HjSpZ}d&klmNglBS7^jGMO#aw~(`55!F@(BiIzn`aPL8%j*F;4{O$b z-p#k(t{POwS-Q`-XVb58{M|9;E-D(gvCu#k#UzcJBKkm*J->?myuxn6l4B~*eK*Kzo<2cjZKw6pf3`N=aAPUh|*7E`}2@Ax04aR=OY%afJ( zWv;z&9>|PfoqE?LtpyaVlRdQ6S9M+yQr)#A{>IDyIj8Y0Aslhb>P73c-n`6x86Ds3 zGrQKt;P2H_+f|Ve{^*V0oTCFJYVRyI%GUy{$b+oTE*7f+wB7f&4I0zTB$0hlsoO(Q zi7S~3#D8;cu>dpruJ6dmGgOdRuV{FSnI6sN92E=SPa>j_d%y>Y)2Pldz+%}P%SKG2 z*6_{#d`K>ra9&CS95UEJqHB)F29oMiIZr<|Y=8Ma;`auDI`v|||LIEHVdn+5_RHXF%Hyz6RtVX)uI1%#?PsMd)88(VCM>s&$E_L;_| z)AG#Qh0K*PONz@#BAHgC4#PJ=Z1sfdxLe;fIAk!7@b0JShwBkMMrLjdt_?@fx|I~y ztn0*lTaD+izGD3;x}K;35c3ppzf94!v%x9QXVR1416a4-PG8se9M4Gv{=)8{(wW^j zvu0ft8kg8P)4ddI3t9DeBDrxGZFjv96#YFV-~U@z*sNql#oLtQNfxnWC?CNWZ6<-f z&W?@CKY=hwVgZH-u8su}rv)(O_k5^-7(n=GG&J4(HE-DCG9cBW!2pV@S@&6d%qJDa zZMRMyh(_7>0YFut=S-YmN;PM?Zu?&-HGea3Z2>aIsqkbR15_bktD+GaYwhGLxS|2c zUIgFI_!la|s(LszT+^}X!z3*ki`-|=&pJQpJBi+}gZSsbJ;sJt76A>`3?9ppoA0ok z1DZoL`md~5?Kc#HIc~X~@AYu}-6_^u|Aft{B6kZkDcQ)OAzUF$dh4@8R3t2^jv4h{ zd(oPsr-t@;xKEvktJN7|S&?P#Wy}^OS+NFBtznwPu7sSRSItB&su)>F0fm4kM=#15 zblkAwts3CeeUDRu3q08`ahhM`*&SgEk*$*xU%PPSM>`a+rTe8*8IHCi=O&FiA_Wpr zj;M7D2hg}~k3NoAloCmLZX&a21^@s>>Rfjq5x$J<9o2phybO=#Jk#v(bivA_ zcHPf3#Q~e`AD@PEKY+GGC)O$FLPpiFYEmcc{bgHM>bB~W^)=#^h=R?(nHnAoyzI9> zS_I%Uar^6fPi%S|F76qs;+5jsnzG~7l4Ib$)EuH82yQ&8qsM`~{rF4?m!1P!XPABfq3zr4K3e4)kME`Yy1v5N}67}Ov5-lD+PTHWH|H(EG!7O=wpnI+M zIPz3{&r0XvCwC&%wiFt&vo^hNY)-^Y?~JjWS2QqkFWvO5CoM*+S3ouZ1;e5pvcdW> zDZiXUkME24lU08@yMmtO(+II)L;~~NDy2t=q={Q!sETBXe_;Vfj31Jg?PCP;vQr+9 z1u`+&yQiUpp&-_clsx7sN}|NB9ZJ7F5Ug^0ly>BNT^*>}`LGP^Y)-*qK;}&Y^0qkL zPNG{Uyb(thJf5jXbgpA66JyDt*LEOKsaj92zt zB~@}o5hOnop3Zp`NpiZiXcs)3WA3g#;;5CUuQ}TvkIiEbPWDhRN)l((LP^Xc(w?h26mVKt2V%E!V|LF|3;} zxoGl{O$ZxTkqx0S3$BeSEFnJFM|JudR#l`!vQ2#LTlG8wPO83%T{ZEoBhvnmPday? zj*X^&3>K_>z%^x+&=JP$MP0_{z}_hBkAkNT;iuAJ&b

QeCVDa~;zNs*^M&w7i%6 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 ZaLNb>e;x$-1Yq`ow>YQ&y#H^1{|D8mtMdQ= diff --git a/plugins/test_data/example_same_cells.caterva b/plugins/test_data/example_same_cells.caterva deleted file mode 100644 index f6b0f45c9e45d793974095d6d5f405c5f071173f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49957 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 +# https://blosc.org +# License: BSD 3-Clause (see LICENSE.txt) +# +# See LICENSE.txt for details about copyright and rights to use. + # sources -file(GLOB SOURCES test_*.c fuzz/generate_inputs_corpus.c) +file(GLOB SOURCES test_*.c b2nd/test_*.c fuzz/generate_inputs_corpus.c) # flags link_directories(${PROJECT_BINARY_DIR}/blosc) diff --git a/tests/b2nd/CMakeLists.txt b/tests/b2nd/CMakeLists.txt new file mode 100644 index 00000000..5fc91c0a --- /dev/null +++ b/tests/b2nd/CMakeLists.txt @@ -0,0 +1,18 @@ +# Blosc - Blocked Shuffling and Compression Library +# +# Copyright (c) 2021 The Blosc Development Team +# https://blosc.org +# License: BSD 3-Clause (see LICENSE.txt) +# +# See LICENSE.txt for details about copyright and rights to use. + +file(GLOB SOURCES test_*.c) + +foreach (source ${SOURCES}) + get_filename_component(target_name ${source} NAME_WE) + set(target b2nd_${target_name}) + add_executable(${target} ${target_name}.c) + target_link_libraries(${target} blosc2_static ${LIBS}) + add_test(NAME ${target} COMMAND ${target}) + set_tests_properties(${target} PROPERTIES LABELS "b2nd") +endforeach (source) diff --git a/tests/b2nd/cutest.h b/tests/b2nd/cutest.h new file mode 100644 index 00000000..379dec64 --- /dev/null +++ b/tests/b2nd/cutest.h @@ -0,0 +1,203 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +#ifndef CUTEST_CUTEST_H +#define CUTEST_CUTEST_H + +#include +#include +#include + + +#define RED "\033[31m" /* Red */ +#define GREEN "\033[32m" /* Green */ +#define RESET "\033[0m" + + +#define CUNIT_OK 0 +#define CUNIT_FAIL 1 + + +#define CUTEST_DATA(...) __VA_ARGS__ + + +#define CUTEST_PARAMETRIZE(name, type, ...) \ + do { \ + type cutest_##name[] = {__VA_ARGS__}; \ + _cutest_parametrize(#name, cutest_##name, \ + sizeof(cutest_##name) / sizeof(type), sizeof(type)); \ + } while(0) + +#define CUTEST_PARAMETRIZE2(name, type, params_len, params) \ + do { \ + (type) *cutest_##name = params; \ + _cutest_parametrize(#name, cutest_##name, params_len, sizeof(type)); \ + } while(0) + +#define CUTEST_GET_PARAMETER(name, type) \ + type name = * (type *) _cutest_get_parameter(#name) + +#define CUTEST_TEST_SETUP(sname) \ + void sname##_setup() + +#define CUTEST_TEST_TEARDOWN(sname) \ + void sname##_teardown() + +#define CUTEST_TEST_TEST(sname) \ + CUTEST_TEST_SETUP(sname); \ + CUTEST_TEST_TEARDOWN(sname); \ + int sname##_test(); \ + int sname##_test() \ + + +#define CUTEST_TEST_RUN(sname) \ + _cutest_setup(); \ + sname##_setup(); \ + int rc = _cutest_run((int (*)(void)) sname##_test, \ + #sname); \ + sname##_teardown(); \ + _cutest_teardown(); \ + return rc; + + +#define CUTEST_ASSERT(msg, cond) \ + do { \ + if (!(cond)) { \ + sprintf(_cutest_error_msg, "Error: %s %s:%d", msg, __FILE__, __LINE__); \ + return CUNIT_FAIL; \ + } \ + } while(0) + + +#define CUTEST_PARAMS_MAX 16 +#define MAXLEN_TESTNAME 1024 + + +typedef struct { + char *name; + uint8_t *params; + int32_t params_len; + int32_t param_size; +} cutest_param_t; + +static cutest_param_t cutest_params[CUTEST_PARAMS_MAX] = {0}; +static int8_t cutest_params_ind[CUTEST_PARAMS_MAX] = {0}; + + +void _cutest_parametrize(char *name, void *params, int32_t params_len, int32_t param_size) { + int i = 0; + while (cutest_params[i].name != NULL) { + i++; + } + uint8_t *new_params = malloc(param_size * params_len); + char *new_name = strdup(name); + memcpy(new_params, params, param_size * params_len); + cutest_params[i].name = new_name; + cutest_params[i].params = new_params; + cutest_params[i].param_size = param_size; + cutest_params[i].params_len = params_len; +} + +uint8_t *_cutest_get_parameter(char *name) { + int i = 0; + while (strcmp(cutest_params[i].name, name) != 0) { + i++; + } + return cutest_params[i].params + cutest_params_ind[i] * cutest_params[i].param_size; +} + + +void _cutest_setup() { + for (int i = 0; i < CUTEST_PARAMS_MAX; ++i) { + cutest_params[i].name = NULL; + } +} + + +void _cutest_teardown() { + int i = 0; + while (cutest_params[i].name != NULL) { + free(cutest_params[i].params); + free(cutest_params[i].name); + i++; + } +} + + +char _cutest_error_msg[1024]; + + +int _cutest_run(int (*test)(void), char *name) { + int cutest_ok = 0; + int cutest_failed = 0; + int cutest_total = 0; + + int nparams = 0; + while (cutest_params[nparams].name != NULL) { + nparams++; + } + + int niters = 1; + for (int i = 0; i < nparams; ++i) { + niters *= cutest_params[i].params_len; + } + + int32_t params_strides[CUTEST_PARAMS_MAX] = {0}; + params_strides[0] = 1; + for (int i = 1; i < nparams; ++i) { + params_strides[i] = params_strides[i - 1] * cutest_params[i - 1].params_len; + } + + char test_name[MAXLEN_TESTNAME + 1]; + char aux[MAXLEN_TESTNAME + 1]; + uint8_t count = 0; + int num = niters; + do { + count++; + num /= 10; + } while (num != 0); + for (int niter = 0; niter < niters; ++niter) { + sprintf(test_name, "[%0*d/%d] %s(", count, niter + 1, niters, name); + for (int i = 0; i < nparams; ++i) { + cutest_params_ind[i] = (int8_t) (niter / params_strides[i] % + cutest_params[i].params_len); + strcpy(aux, test_name); + snprintf(test_name, MAXLEN_TESTNAME, "%s%s[%d], ", aux, cutest_params[i].name, + cutest_params_ind[i]); + } + if (nparams > 0) + test_name[strlen(test_name) - 2] = ')'; + test_name[strlen(test_name) - 1] = '\0'; + printf("%s ", test_name); + + cutest_total++; + + int rc = test(); + if (rc == CUNIT_OK) { + cutest_ok++; + fprintf(stdout, GREEN "[ OK ]\n" RESET); + } else { + cutest_failed++; + fprintf(stdout, RED "[FAILED]\n" RESET); + } + if (_cutest_error_msg[0] != 0) { + fprintf(stdout, RED " %s\n" RESET, _cutest_error_msg); + _cutest_error_msg[0] = 0; + } + } + + printf("\nTEST RESULTS: %d tests (%d ok, %d failed)\n", + cutest_total, cutest_ok, cutest_failed); + + return cutest_failed; +} + + +#endif //CUTEST_CUTEST_H diff --git a/tests/b2nd/test_b2nd_append.c b/tests/b2nd/test_b2nd_append.c new file mode 100644 index 00000000..2a9bfd9d --- /dev/null +++ b/tests/b2nd/test_b2nd_append.c @@ -0,0 +1,158 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + + +#include "test_common.h" + +typedef struct { + int8_t ndim; + int64_t shape[B2ND_MAX_DIM]; + int32_t chunkshape[B2ND_MAX_DIM]; + int32_t blockshape[B2ND_MAX_DIM]; + int64_t buffershape[B2ND_MAX_DIM]; + int8_t axis; +} test_shapes_t; + + +CUTEST_TEST_SETUP(append) { + blosc2_init(); + + // Add parametrizations + CUTEST_PARAMETRIZE(typesize, uint8_t, CUTEST_DATA( + 1, + 2, + 4, + 8, + )); + + CUTEST_PARAMETRIZE(backend, _test_backend, CUTEST_DATA( + {false, false}, + {true, false}, + {true, true}, + {false, true}, + )); + + + CUTEST_PARAMETRIZE(shapes, test_shapes_t, CUTEST_DATA( + {1, {5}, {3}, {2}, {10}, 0}, + {2, {18, 6}, {6, 6}, {3, 3}, {18, 12}, 1}, + {3, {12, 10, 14}, {3, 5, 9}, {3, 4, 4}, {12, 10, 18}, 2}, + {4, {10, 10, 5, 5}, {5, 7, 3, 3}, {2, 2, 1, 1}, {10, 10, 5, 30}, 3}, + + )); +} + +CUTEST_TEST_TEST(append) { + CUTEST_GET_PARAMETER(backend, _test_backend); + CUTEST_GET_PARAMETER(shapes, test_shapes_t); + CUTEST_GET_PARAMETER(typesize, uint8_t); + + char *urlpath = "test_append_shape.b2frame"; + blosc2_remove_urlpath(urlpath); + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.nthreads = 2; + cparams.typesize = typesize; + blosc2_storage b2_storage = BLOSC2_STORAGE_DEFAULTS; + b2_storage.cparams = &cparams; + b2_storage.contiguous = backend.contiguous; + if (backend.persistent) { + b2_storage.urlpath = urlpath; + } + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, shapes.ndim, shapes.shape, + shapes.chunkshape, shapes.blockshape, NULL, 0, + NULL, 0); + + int64_t buffersize = typesize; + for (int i = 0; i < ctx->ndim; ++i) { + buffersize *= shapes.buffershape[i]; + } + + /* Create b2nd_array_t with original data */ + b2nd_array_t *src; + uint8_t *value = malloc(typesize); + int8_t fill_value = 1; + switch (typesize) { + case 8: + ((int64_t *) value)[0] = (int64_t) fill_value; + break; + case 4: + ((int32_t *) value)[0] = (int32_t) fill_value; + break; + case 2: + ((int16_t *) value)[0] = (int16_t) fill_value; + break; + case 1: + ((int8_t *) value)[0] = fill_value; + break; + default: + break; + } + BLOSC_ERROR(b2nd_full(ctx, &src, value)); + + uint8_t *buffer = malloc(buffersize); + fill_buf(buffer, typesize, buffersize / typesize); + BLOSC_ERROR(b2nd_append(src, buffer, buffersize, shapes.axis)); + + int64_t start[B2ND_MAX_DIM] = {0}; + start[shapes.axis] = shapes.shape[shapes.axis]; + int64_t stop[B2ND_MAX_DIM]; + for (int i = 0; i < shapes.ndim; ++i) { + stop[i] = shapes.shape[i]; + } + stop[shapes.axis] = shapes.shape[shapes.axis] + shapes.buffershape[shapes.axis]; + + /* Fill buffer with a slice from the new chunks */ + uint8_t *res_buffer = malloc(buffersize); + BLOSC_ERROR(b2nd_get_slice_cbuffer(src, start, stop, res_buffer, + shapes.buffershape, buffersize)); + + for (uint64_t i = 0; i < (uint64_t) buffersize / typesize; ++i) { + switch (typesize) { + case 8: + CUTEST_ASSERT("Elements are not equal!", + ((uint64_t *) buffer)[i] == ((uint64_t *) res_buffer)[i]); + break; + case 4: + CUTEST_ASSERT("Elements are not equal!", + ((uint32_t *) buffer)[i] == ((uint32_t *) res_buffer)[i]); + break; + case 2: + CUTEST_ASSERT("Elements are not equal!", + ((uint16_t *) buffer)[i] == ((uint16_t *) res_buffer)[i]); + break; + case 1: + CUTEST_ASSERT("Elements are not equal!", + ((uint8_t *) buffer)[i] == ((uint8_t *) res_buffer)[i]); + break; + default: + B2ND_TEST_ASSERT(BLOSC2_ERROR_INVALID_PARAM); + } + } + /* Free mallocs */ + free(value); + free(buffer); + free(res_buffer); + + B2ND_TEST_ASSERT(b2nd_free(src)); + B2ND_TEST_ASSERT(b2nd_free_ctx(ctx)); + blosc2_remove_urlpath(urlpath); + + return 0; +} + +CUTEST_TEST_TEARDOWN(append) { + blosc2_destroy(); +} + +int main() { + CUTEST_TEST_RUN(append); +} diff --git a/tests/b2nd/test_b2nd_copy.c b/tests/b2nd/test_b2nd_copy.c new file mode 100644 index 00000000..b216690b --- /dev/null +++ b/tests/b2nd/test_b2nd_copy.c @@ -0,0 +1,181 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +#include "test_common.h" + +typedef struct { + int8_t ndim; + int64_t shape[B2ND_MAX_DIM]; + int32_t chunkshape[B2ND_MAX_DIM]; + int32_t blockshape[B2ND_MAX_DIM]; + int32_t chunkshape2[B2ND_MAX_DIM]; + int32_t blockshape2[B2ND_MAX_DIM]; +} test_shapes_t; + + +CUTEST_TEST_SETUP(copy) { + blosc2_init(); + + // Add parametrizations + CUTEST_PARAMETRIZE(typesize, uint8_t, CUTEST_DATA( + 2, + 4, + )); + CUTEST_PARAMETRIZE(shapes, test_shapes_t, CUTEST_DATA( + {2, {30, 30}, {20, 20}, {10, 10}, + {20, 20}, {10, 10}}, + {3, {40, 15, 23}, {31, 5, 22}, {4, 4, 4}, + {30, 5, 20}, {10, 4, 4}}, + {3, {40, 0, 12}, {31, 0, 12}, {10, 0, 12}, + {20, 0, 12}, {25, 0, 6}}, +// The following cases are skipped to reduce the execution time of the test suite +// {4, {25, 60, 31, 12}, {12, 20, 20, 10}, {5, 5, 5, 10}, +// {25, 20, 20, 10}, {5, 5, 5, 10}}, +// {5, {1, 1, 1024, 1, 1}, {1, 1, 500, 1, 1}, {1, 1, 200, 1, 1}, +// {1, 1, 300, 1, 1}, {1, 1, 50, 1, 1}}, +// {6, {5, 1, 100, 3, 1, 2}, {4, 1, 50, 2, 1, 2}, {2, 1, 20, 2, 1, 2}, +// {4, 1, 50, 2, 1, 1}, {2, 1, 20, 2, 1, 1}}, + )); + CUTEST_PARAMETRIZE(backend, _test_backend, CUTEST_DATA( + {false, false}, + {true, false}, + {false, true}, + {true, true}, + )); + + CUTEST_PARAMETRIZE(backend2, _test_backend, CUTEST_DATA( + {false, false}, + {false, false}, + {true, false}, + {false, true}, + {true, true}, + )); +} + +CUTEST_TEST_TEST(copy) { + CUTEST_GET_PARAMETER(backend, _test_backend); + CUTEST_GET_PARAMETER(shapes, test_shapes_t); + CUTEST_GET_PARAMETER(backend2, _test_backend); + CUTEST_GET_PARAMETER(typesize, uint8_t); + + char *urlpath = "test_copy.b2frame"; + char *urlpath2 = "test_copy2.b2frame"; + blosc2_remove_urlpath(urlpath); + blosc2_remove_urlpath(urlpath2); + + double datatoserialize = 8.34; + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.nthreads = 2; + cparams.typesize = typesize; + blosc2_storage b2_storage = {.cparams=&cparams}; + if (backend.persistent) { + b2_storage.urlpath = urlpath; + } else { + b2_storage.urlpath = NULL; + } + b2_storage.contiguous = backend.contiguous; + + int8_t nmetalayers = 1; + blosc2_metalayer metalayers[B2ND_MAX_METALAYERS]; + metalayers[0].name = "random"; + metalayers[0].content = (uint8_t *) &datatoserialize; + metalayers[0].content_len = 8; + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, shapes.ndim, shapes.shape, + shapes.chunkshape, shapes.blockshape, NULL, 0, metalayers, nmetalayers); + + /* Create original data */ + size_t buffersize = typesize; + for (int i = 0; i < ctx->ndim; ++i) { + buffersize *= (size_t) ctx->shape[i]; + } + uint8_t *buffer = malloc(buffersize); + CUTEST_ASSERT("Buffer filled incorrectly", fill_buf(buffer, typesize, (buffersize / typesize))); + + /* Create b2nd_array_t with original data */ + b2nd_array_t *src; + B2ND_TEST_ASSERT(b2nd_from_cbuffer(ctx, + &src, buffer, buffersize)); + + /* Assert the metalayers creation */ + int rc = blosc2_meta_exists(src->sc, "random"); + if (rc < 0) { + B2ND_TEST_ASSERT(BLOSC2_ERROR_FAILURE); + } + uint8_t *content; + int32_t content_len; + B2ND_TEST_ASSERT(blosc2_meta_get(src->sc, "random", &content, &content_len)); + double serializeddata = *((double *) content); + if (serializeddata != datatoserialize) { + B2ND_TEST_ASSERT(BLOSC2_ERROR_FAILURE); + } + + B2ND_TEST_ASSERT(blosc2_vlmeta_add(src->sc, "random", content, content_len, + src->sc->storage->cparams)); + free(content); + + + /* Create storage for dest container */ + if (backend2.persistent) { + b2_storage.urlpath = urlpath2; + } else { + b2_storage.urlpath = NULL; + } + b2_storage.contiguous = backend2.contiguous; + b2nd_context_t *ctx2 = b2nd_create_ctx(&b2_storage, shapes.ndim, shapes.shape, + shapes.chunkshape2, shapes.blockshape2, NULL, 0, NULL, 0); + + b2nd_array_t *dest; + B2ND_TEST_ASSERT(b2nd_copy(ctx2, src, &dest)); + + /* Assert the metalayers creation */ + B2ND_TEST_ASSERT(blosc2_meta_get(dest->sc, "random", &content, &content_len)); + serializeddata = *((double *) content); + if (serializeddata != datatoserialize) { + B2ND_TEST_ASSERT(BLOSC2_ERROR_FAILURE); + } + free(content); + + B2ND_TEST_ASSERT(blosc2_vlmeta_get(dest->sc, "random", &content, &content_len)); + serializeddata = *((double *) content); + if (serializeddata != datatoserialize) { + B2ND_TEST_ASSERT(BLOSC2_ERROR_FAILURE); + } + free(content); + + + uint8_t *buffer_dest = malloc(buffersize); + B2ND_TEST_ASSERT(b2nd_to_cbuffer(dest, buffer_dest, buffersize)); + + /* Testing */ + B2ND_TEST_ASSERT_BUFFER(buffer, buffer_dest, (int) buffersize); + + /* Free mallocs */ + free(buffer); + free(buffer_dest); + B2ND_TEST_ASSERT(b2nd_free(src)); + B2ND_TEST_ASSERT(b2nd_free(dest)); + B2ND_TEST_ASSERT(b2nd_free_ctx(ctx)); + B2ND_TEST_ASSERT(b2nd_free_ctx(ctx2)); + + blosc2_remove_urlpath(urlpath); + blosc2_remove_urlpath(urlpath2); + + return 0; +} + +CUTEST_TEST_TEARDOWN(copy) { + blosc2_destroy(); +} + +int main() { + CUTEST_TEST_RUN(copy); +} diff --git a/tests/b2nd/test_b2nd_delete.c b/tests/b2nd/test_b2nd_delete.c new file mode 100644 index 00000000..de951859 --- /dev/null +++ b/tests/b2nd/test_b2nd_delete.c @@ -0,0 +1,180 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + + +#include "test_common.h" + +typedef struct { + int8_t ndim; + int64_t shape[B2ND_MAX_DIM]; + int32_t chunkshape[B2ND_MAX_DIM]; + int32_t blockshape[B2ND_MAX_DIM]; + int8_t axis; + int64_t start; + int64_t delete_len; +} test_shapes_t; + + +CUTEST_TEST_SETUP(delete) { + blosc2_init(); + + // Add parametrizations + CUTEST_PARAMETRIZE(typesize, uint8_t, CUTEST_DATA( + 1, + 2, + 4, + 8, + )); + + CUTEST_PARAMETRIZE(backend, _test_backend, CUTEST_DATA( + {false, false}, + {true, false}, + {true, true}, + {false, true}, + )); + + + CUTEST_PARAMETRIZE(shapes, test_shapes_t, CUTEST_DATA( + {1, {10}, {3}, {2}, 0, 5, 5}, // delete the end + {2, {18, 12}, {6, 6}, {3, 3}, 1, 0, 6}, // delete at the beginning + {3, {12, 10, 27}, {3, 5, 9}, {3, 4, 4}, 2, 9, 9}, // delete in the middle + {4, {10, 10, 5, 30}, {5, 7, 3, 3}, {2, 2, 1, 1}, 3, 12, 9}, // delete in the middle + + )); +} + +CUTEST_TEST_TEST(delete) { + CUTEST_GET_PARAMETER(backend, _test_backend); + CUTEST_GET_PARAMETER(shapes, test_shapes_t); + CUTEST_GET_PARAMETER(typesize, uint8_t); + + char *urlpath = "test_delete.b2frame"; + blosc2_remove_urlpath(urlpath); + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.nthreads = 2; + cparams.compcode = BLOSC_LZ4; + cparams.typesize = typesize; + blosc2_storage b2_storage = {.cparams=&cparams}; + if (backend.persistent) { + b2_storage.urlpath = urlpath; + } + b2_storage.contiguous = backend.contiguous; + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, shapes.ndim, shapes.shape, + shapes.chunkshape, shapes.blockshape, NULL, 0, NULL, 0); + + /* Create b2nd_array_t with original data */ + b2nd_array_t *src; + uint8_t *value = malloc(typesize); + int8_t fill_value = 1; + switch (typesize) { + case 8: + ((int64_t *) value)[0] = (int64_t) fill_value; + break; + case 4: + ((int32_t *) value)[0] = (int32_t) fill_value; + break; + case 2: + ((int16_t *) value)[0] = (int16_t) fill_value; + break; + case 1: + ((int8_t *) value)[0] = fill_value; + break; + default: + break; + } + BLOSC_ERROR(b2nd_full(ctx, &src, value)); + + int64_t bufferlen = 1; + int64_t stop[B2ND_MAX_DIM]; + int64_t buffer_shape[B2ND_MAX_DIM]; + for (int i = 0; i < ctx->ndim; ++i) { + if (i != shapes.axis) { + bufferlen *= shapes.shape[i]; + stop[i] = shapes.shape[i]; + buffer_shape[i] = shapes.shape[i]; + } else { + bufferlen *= shapes.delete_len; + stop[i] = shapes.start + shapes.delete_len; + buffer_shape[i] = shapes.delete_len; + } + } + // Set future deleted values to 0 + int64_t start[B2ND_MAX_DIM] = {0}; + start[shapes.axis] = shapes.start; + uint8_t *buffer = calloc((size_t) bufferlen, (size_t) typesize); + BLOSC_ERROR(b2nd_set_slice_cbuffer(buffer, buffer_shape, bufferlen * typesize, start, stop, src)); + + BLOSC_ERROR(b2nd_delete(src, shapes.axis, shapes.start, shapes.delete_len)); + + int64_t newshape[B2ND_MAX_DIM] = {0}; + for (int i = 0; i < shapes.ndim; ++i) { + newshape[i] = shapes.shape[i]; + } + newshape[shapes.axis] -= shapes.delete_len; + + // Create aux array to compare values + b2nd_array_t *aux; + b2_storage.urlpath = NULL; + b2_storage.contiguous = backend.contiguous; + b2nd_context_t *aux_ctx = b2nd_create_ctx(&b2_storage, shapes.ndim, newshape, + shapes.chunkshape, shapes.blockshape, NULL, 0, NULL, 0); + + BLOSC_ERROR(b2nd_full(aux_ctx, &aux, value)); + + /* Fill buffer with whole array data */ + uint8_t *src_buffer = malloc((size_t) (src->nitems * typesize)); + B2ND_TEST_ASSERT(b2nd_to_cbuffer(src, src_buffer, src->nitems * typesize)); + + for (uint64_t i = 0; i < (uint64_t) src->nitems; ++i) { + switch (typesize) { + case 8: + CUTEST_ASSERT("Elements are not equal!", + ((uint64_t *) src_buffer)[i] == (uint64_t) fill_value); + break; + case 4: + CUTEST_ASSERT("Elements are not equal!", + ((uint32_t *) src_buffer)[i] == (uint32_t) fill_value); + break; + case 2: + CUTEST_ASSERT("Elements are not equal!", + ((uint16_t *) src_buffer)[i] == (uint16_t) fill_value); + break; + case 1: + CUTEST_ASSERT("Elements are not equal!", + ((uint8_t *) src_buffer)[i] == (uint8_t) fill_value); + break; + default: + B2ND_TEST_ASSERT(BLOSC2_ERROR_INVALID_PARAM); + } + } + /* Free mallocs */ + free(value); + free(buffer); + free(src_buffer); + + B2ND_TEST_ASSERT(b2nd_free(src)); + B2ND_TEST_ASSERT(b2nd_free(aux)); + B2ND_TEST_ASSERT(b2nd_free_ctx(ctx)); + B2ND_TEST_ASSERT(b2nd_free_ctx(aux_ctx)); + + blosc2_remove_urlpath(urlpath); + + return 0; +} + +CUTEST_TEST_TEARDOWN(delete) { + blosc2_destroy(); +} + +int main() { + CUTEST_TEST_RUN(delete); +} diff --git a/tests/b2nd/test_b2nd_full.c b/tests/b2nd/test_b2nd_full.c new file mode 100644 index 00000000..9789fd66 --- /dev/null +++ b/tests/b2nd/test_b2nd_full.c @@ -0,0 +1,137 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + + +#include "test_common.h" + + +CUTEST_TEST_SETUP(full) { + blosc2_init(); + + // Add parametrizations + CUTEST_PARAMETRIZE(typesize, uint8_t, CUTEST_DATA( + 1, 2, 4, 8 + )); + CUTEST_PARAMETRIZE(shapes, _test_shapes, CUTEST_DATA( + {0, {0}, {0}, {0}}, // 0-dim + {1, {5}, {3}, {2}}, // 1-idim + {2, {20, 0}, {7, 0}, {3, 0}}, // 0-shape + {2, {20, 10}, {7, 5}, {3, 5}}, // 0-shape + {2, {14, 10}, {8, 5}, {2, 2}}, // general, + {3, {12, 10, 14}, {3, 5, 9}, {3, 4, 4}}, // general + {3, {10, 21, 20, 5}, {8, 7, 15, 3}, {5, 5, 10, 1}}, // general, + )); + CUTEST_PARAMETRIZE(backend, _test_backend, CUTEST_DATA( + {false, false}, + {true, false}, + {true, true}, + {false, true}, + )); + CUTEST_PARAMETRIZE(fill_value, int8_t, CUTEST_DATA( + 3, 113, 33, -5 + )); +} + + +CUTEST_TEST_TEST(full) { + CUTEST_GET_PARAMETER(backend, _test_backend); + CUTEST_GET_PARAMETER(shapes, _test_shapes); + CUTEST_GET_PARAMETER(typesize, uint8_t); + CUTEST_GET_PARAMETER(fill_value, int8_t); + + char *urlpath = "test_full.b2frame"; + blosc2_remove_urlpath(urlpath); + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.nthreads = 2; + cparams.typesize = typesize; + blosc2_storage b2_storage = {.cparams=&cparams}; + + if (backend.persistent) { + b2_storage.urlpath = urlpath; + } + b2_storage.contiguous = backend.contiguous; + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, shapes.ndim, shapes.shape, + shapes.chunkshape, shapes.blockshape, NULL, 0, NULL, 0); + + /* Create original data */ + int64_t buffersize = typesize; + for (int i = 0; i < shapes.ndim; ++i) { + buffersize *= shapes.shape[i]; + } + + /* Create b2nd_array_t with original data */ + b2nd_array_t *src; + uint8_t *value = malloc(typesize); + switch (typesize) { + case 8: + ((int64_t *) value)[0] = (int64_t) fill_value; + break; + case 4: + ((int32_t *) value)[0] = (int32_t) fill_value; + break; + case 2: + ((int16_t *) value)[0] = (int16_t) fill_value; + break; + case 1: + ((int8_t *) value)[0] = fill_value; + break; + default: + break; + } + + B2ND_TEST_ASSERT(b2nd_full(ctx, &src, value)); + + /* Fill dest array with b2nd_array_t data */ + uint8_t *buffer_dest = malloc(buffersize); + B2ND_TEST_ASSERT(b2nd_to_cbuffer(src, buffer_dest, buffersize)); + + /* Testing */ + for (int i = 0; i < buffersize / typesize; ++i) { + bool is_true = false; + switch (typesize) { + case 8: + is_true = ((int64_t *) buffer_dest)[i] == fill_value; + break; + case 4: + is_true = ((int32_t *) buffer_dest)[i] == fill_value; + break; + case 2: + is_true = ((int16_t *) buffer_dest)[i] == fill_value; + break; + case 1: + is_true = ((int8_t *) buffer_dest)[i] == fill_value; + break; + default: + break; + } + CUTEST_ASSERT("Elements are not equals", is_true); + } + + /* Free mallocs */ + free(buffer_dest); + free(value); + B2ND_TEST_ASSERT(b2nd_free(src)); + B2ND_TEST_ASSERT(b2nd_free_ctx(ctx)); + + blosc2_remove_urlpath(urlpath); + + return BLOSC2_ERROR_SUCCESS; +} + + +CUTEST_TEST_TEARDOWN(full) { + blosc2_destroy(); +} + +int main() { + CUTEST_TEST_RUN(full); +} diff --git a/tests/b2nd/test_b2nd_get_slice.c b/tests/b2nd/test_b2nd_get_slice.c new file mode 100644 index 00000000..7991579c --- /dev/null +++ b/tests/b2nd/test_b2nd_get_slice.c @@ -0,0 +1,172 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +#include "test_common.h" + +uint64_t result0[1024] = {0}; +uint64_t result1[1024] = {2, 3, 4, 5, 6, 7, 8}; +uint64_t result2[1024] = {53, 54, 55, 56, 57, 58, 59, 63, 64, 65, 66, 67, 68, 69, 73, 74, 75, 76, + 77, 78, 79, 83, 84, 85, 86, 87, 88, 89}; +uint64_t result3[1024] = {303, 304, 305, 306, 307, 308, 309, 313, 314, 315, 316, 317, 318, 319, + 323, 324, 325, 326, 327, 328, 329, 333, 334, 335, 336, 337, 338, 339, + 343, 344, 345, 346, 347, 348, 349, 353, 354, 355, 356, 357, 358, 359, + 363, 364, 365, 366, 367, 368, 369, 403, 404, 405, 406, 407, 408, 409, + 413, 414, 415, 416, 417, 418, 419, 423, 424, 425, 426, 427, 428, 429, + 433, 434, 435, 436, 437, 438, 439, 443, 444, 445, 446, 447, 448, 449, + 453, 454, 455, 456, 457, 458, 459, 463, 464, 465, 466, 467, 468, 469, + 503, 504, 505, 506, 507, 508, 509, 513, 514, 515, 516, 517, 518, 519, + 523, 524, 525, 526, 527, 528, 529, 533, 534, 535, 536, 537, 538, 539, + 543, 544, 545, 546, 547, 548, 549, 553, 554, 555, 556, 557, 558, 559, + 563, 564, 565, 566, 567, 568, 569}; +uint64_t result4[1024] = {0}; +uint64_t result5[1024] = {0}; + +typedef struct { + int8_t ndim; + int64_t shape[B2ND_MAX_DIM]; + int32_t chunkshape[B2ND_MAX_DIM]; + int32_t blockshape[B2ND_MAX_DIM]; + int32_t chunkshape2[B2ND_MAX_DIM]; + int32_t blockshape2[B2ND_MAX_DIM]; + int64_t start[B2ND_MAX_DIM]; + int64_t stop[B2ND_MAX_DIM]; + uint64_t *result; +} test_shapes_t; + + +CUTEST_TEST_SETUP(get_slice) { + blosc2_init(); + + // Add parametrizations + CUTEST_PARAMETRIZE(typesize, uint8_t, CUTEST_DATA(8)); + CUTEST_PARAMETRIZE(backend, _test_backend, CUTEST_DATA( + {false, false}, + {true, false}, + {true, true}, + {false, true}, + )); + CUTEST_PARAMETRIZE(backend2, _test_backend, CUTEST_DATA( + {false, false}, + {true, false}, + {true, true}, + {false, true}, + )); + + CUTEST_PARAMETRIZE(shapes, test_shapes_t, CUTEST_DATA( + {0, {0}, {0}, {0}, {0}, {0}, {0}, {0}, result0}, // 0-dim + {1, {10}, {7}, {2}, {6}, {2}, {2}, {9}, result1}, // 1-idim + {2, {14, 10}, {8, 5}, {2, 2}, {4, 4}, {2, 3}, {5, 3}, {9, 10}, result2}, // general, + {3, {10, 10, 10}, {3, 5, 9}, {3, 4, 4}, {3, 7, 7}, {2, 5, 5}, {3, 0, 3}, {6, 7, 10}, result3}, // general + {2, {20, 0}, {7, 0}, {3, 0}, {5, 0}, {2, 0}, {2, 0}, {8, 0}, result4}, // 0-shape + {2, {20, 10}, {7, 5}, {3, 5}, {5, 5}, {2, 2}, {2, 0}, {18, 0}, result5}, // 0-shape + )); +} + +CUTEST_TEST_TEST(get_slice) { + CUTEST_GET_PARAMETER(backend, _test_backend); + CUTEST_GET_PARAMETER(shapes, test_shapes_t); + CUTEST_GET_PARAMETER(backend2, _test_backend); + CUTEST_GET_PARAMETER(typesize, uint8_t); + + char *urlpath = "test_get_slice.b2frame"; + char *urlpath2 = "test_get_slice2.b2frame"; + + blosc2_remove_urlpath(urlpath); + blosc2_remove_urlpath(urlpath2); + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.nthreads = 2; + cparams.typesize = typesize; + blosc2_storage b2_storage = {.cparams=&cparams}; + if (backend.persistent) { + b2_storage.urlpath = urlpath; + } + b2_storage.contiguous = backend.contiguous; + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, shapes.ndim, shapes.shape, + shapes.chunkshape, shapes.blockshape, NULL, 0, NULL, 0); + + /* Create original data */ + size_t buffersize = typesize; + for (int i = 0; i < ctx->ndim; ++i) { + buffersize *= (size_t) shapes.shape[i]; + } + uint8_t *buffer = malloc(buffersize); + CUTEST_ASSERT("Buffer filled incorrectly", fill_buf(buffer, typesize, buffersize / typesize)); + + /* Create b2nd_array_t with original data */ + b2nd_array_t *src; + B2ND_TEST_ASSERT(b2nd_from_cbuffer(ctx, &src, buffer, buffersize)); + + /* Add vlmeta */ + + blosc2_metalayer vlmeta; + vlmeta.name = "test_get_slice"; + double sdata = 2.3; + vlmeta.content = (uint8_t *) &sdata; + vlmeta.content_len = sizeof(double); + + B2ND_TEST_ASSERT(blosc2_vlmeta_add(src->sc, vlmeta.name, vlmeta.content, vlmeta.content_len, + src->sc->storage->cparams)); + + /* Create storage for dest container */ + blosc2_storage b2_storage2 = {.cparams=&cparams}; + if (backend2.persistent) { + b2_storage2.urlpath = urlpath2; + } + b2_storage.contiguous = backend2.contiguous; + + // shape param will then be ignored + b2nd_context_t *ctx2 = b2nd_create_ctx(&b2_storage2, shapes.ndim, shapes.shape, + shapes.chunkshape2, shapes.blockshape2, NULL, 0, NULL, 0); + + b2nd_array_t *dest; + B2ND_TEST_ASSERT(b2nd_get_slice(ctx2, &dest, src, shapes.start, shapes.stop)); + + /* Check metalayers */ + int rc = blosc2_meta_exists(dest->sc, "b2nd"); + CUTEST_ASSERT("metalayer not exists", rc == 0); + rc = blosc2_vlmeta_exists(dest->sc, vlmeta.name); + CUTEST_ASSERT("vlmetalayer should not exist", rc < 0); + + int64_t destbuffersize = typesize; + for (int i = 0; i < src->ndim; ++i) { + destbuffersize *= (shapes.stop[i] - shapes.start[i]); + } + + uint64_t *buffer_dest = malloc((size_t) destbuffersize); + B2ND_TEST_ASSERT(b2nd_to_cbuffer(dest, buffer_dest, destbuffersize)); + + for (int i = 0; i < destbuffersize / typesize; ++i) { + uint64_t a = shapes.result[i] + 1; + uint64_t b = buffer_dest[i]; + CUTEST_ASSERT("Elements are not equals!", a == b); + } + + /* Free mallocs */ + free(buffer); + free(buffer_dest); + B2ND_TEST_ASSERT(b2nd_free(src)); + B2ND_TEST_ASSERT(b2nd_free(dest)); + B2ND_TEST_ASSERT(b2nd_free_ctx(ctx)); + B2ND_TEST_ASSERT(b2nd_free_ctx(ctx2)); + blosc2_remove_urlpath(urlpath); + blosc2_remove_urlpath(urlpath2); + + return 0; +} + +CUTEST_TEST_TEARDOWN(get_slice) { + blosc2_destroy(); +} + +int main() { + CUTEST_TEST_RUN(get_slice); +} diff --git a/tests/b2nd/test_b2nd_get_slice_buffer.c b/tests/b2nd/test_b2nd_get_slice_buffer.c new file mode 100644 index 00000000..8e4a4c7c --- /dev/null +++ b/tests/b2nd/test_b2nd_get_slice_buffer.c @@ -0,0 +1,138 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + + +#include "test_common.h" + + +uint64_t result0[1024] = {0}; +uint64_t result1[1024] = {2, 3, 4, 5, 6, 7, 8}; +uint64_t result2[1024] = {53, 54, 55, 56, 57, 58, 59, 63, 64, 65, 66, 67, 68, 69, 73, 74, 75, 76, + 77, 78, 79, 83, 84, 85, 86, 87, 88, 89}; +uint64_t result3[1024] = {303, 304, 305, 306, 307, 308, 309, 313, 314, 315, 316, 317, 318, 319, + 323, 324, 325, 326, 327, 328, 329, 333, 334, 335, 336, 337, 338, 339, + 343, 344, 345, 346, 347, 348, 349, 353, 354, 355, 356, 357, 358, 359, + 363, 364, 365, 366, 367, 368, 369, 403, 404, 405, 406, 407, 408, 409, + 413, 414, 415, 416, 417, 418, 419, 423, 424, 425, 426, 427, 428, 429, + 433, 434, 435, 436, 437, 438, 439, 443, 444, 445, 446, 447, 448, 449, + 453, 454, 455, 456, 457, 458, 459, 463, 464, 465, 466, 467, 468, 469, + 503, 504, 505, 506, 507, 508, 509, 513, 514, 515, 516, 517, 518, 519, + 523, 524, 525, 526, 527, 528, 529, 533, 534, 535, 536, 537, 538, 539, + 543, 544, 545, 546, 547, 548, 549, 553, 554, 555, 556, 557, 558, 559, + 563, 564, 565, 566, 567, 568, 569}; +uint64_t result4[1024] = {0}; +uint64_t result5[1024] = {0}; + +typedef struct { + int8_t ndim; + int64_t shape[B2ND_MAX_DIM]; + int32_t chunkshape[B2ND_MAX_DIM]; + int32_t blockshape[B2ND_MAX_DIM]; + int32_t chunkshape2[B2ND_MAX_DIM]; + int32_t blockshape2[B2ND_MAX_DIM]; + int64_t start[B2ND_MAX_DIM]; + int64_t stop[B2ND_MAX_DIM]; + uint64_t *result; +} test_shapes_t; + + +CUTEST_TEST_SETUP(get_slice_buffer) { + blosc2_init(); + + // Add parametrizations + CUTEST_PARAMETRIZE(typesize, uint8_t, CUTEST_DATA(8)); + CUTEST_PARAMETRIZE(backend, _test_backend, CUTEST_DATA( + {false, false}, + {true, false}, + {true, true}, + {false, true}, + )); + + CUTEST_PARAMETRIZE(shapes, test_shapes_t, CUTEST_DATA( + {0, {0}, {0}, {0}, {0}, {0}, {0}, {0}, result0}, // 0-dim + {1, {10}, {7}, {2}, {6}, {2}, {2}, {9}, result1}, // 1-idim + {2, {14, 10}, {8, 5}, {2, 2}, {4, 4}, {2, 3}, {5, 3}, {9, 10}, result2}, // general, + {3, {10, 10, 10}, {3, 5, 9}, {3, 4, 4}, {3, 7, 7}, {2, 5, 5}, {3, 0, 3}, {6, 7, 10}, result3}, // general + {2, {20, 0}, {7, 0}, {3, 0}, {5, 0}, {2, 0}, {2, 0}, {8, 0}, result4}, // 0-shape + {2, {20, 10}, {7, 5}, {3, 5}, {5, 5}, {2, 2}, {2, 0}, {18, 0}, result5}, // 0-shape + )); +} + +CUTEST_TEST_TEST(get_slice_buffer) { + CUTEST_GET_PARAMETER(backend, _test_backend); + CUTEST_GET_PARAMETER(shapes, test_shapes_t); + CUTEST_GET_PARAMETER(typesize, uint8_t); + + char *urlpath = "test_get_slice_buffer.b2frame"; + blosc2_remove_urlpath(urlpath); + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.nthreads = 2; + cparams.typesize = typesize; + blosc2_storage b2_storage = {.cparams=&cparams}; + if (backend.persistent) { + b2_storage.urlpath = urlpath; + } + b2_storage.contiguous = backend.contiguous; + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, shapes.ndim, shapes.shape, + shapes.chunkshape, shapes.blockshape, NULL, 0, NULL, 0); + + /* Create original data */ + size_t buffersize = typesize; + for (int i = 0; i < ctx->ndim; ++i) { + buffersize *= (size_t) shapes.shape[i]; + } + uint8_t *buffer = malloc(buffersize); + + CUTEST_ASSERT("Buffer filled incorrectly", fill_buf(buffer, typesize, buffersize / typesize)); + + /* Create b2nd_array_t with original data */ + b2nd_array_t *src; + B2ND_TEST_ASSERT(b2nd_from_cbuffer(ctx, &src, buffer, buffersize)); + + /* Create dest buffer */ + int64_t destshape[B2ND_MAX_DIM] = {0}; + int64_t destbuffersize = typesize; + for (int i = 0; i < ctx->ndim; ++i) { + destshape[i] = shapes.stop[i] - shapes.start[i]; + destbuffersize *= destshape[i]; + } + + uint64_t *destbuffer = malloc((size_t) destbuffersize); + + /* Fill dest buffer with a slice*/ + B2ND_TEST_ASSERT(b2nd_get_slice_cbuffer(src, shapes.start, shapes.stop, destbuffer, + destshape, destbuffersize)); + + for (int i = 0; i < destbuffersize / typesize; ++i) { + uint64_t a = destbuffer[i]; + uint64_t b = shapes.result[i] + 1; + CUTEST_ASSERT("Elements are not equals!", a == b); + } + + /* Free mallocs */ + free(buffer); + free(destbuffer); + B2ND_TEST_ASSERT(b2nd_free(src)); + B2ND_TEST_ASSERT(b2nd_free_ctx(ctx)); + + blosc2_remove_urlpath(urlpath); + + return 0; +} + +CUTEST_TEST_TEARDOWN(get_slice_buffer) { + blosc2_destroy(); +} + +int main() { + CUTEST_TEST_RUN(get_slice_buffer); +} diff --git a/tests/b2nd/test_b2nd_insert.c b/tests/b2nd/test_b2nd_insert.c new file mode 100644 index 00000000..89b5299f --- /dev/null +++ b/tests/b2nd/test_b2nd_insert.c @@ -0,0 +1,156 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +#include "test_common.h" + +typedef struct { + int8_t ndim; + int64_t shape[B2ND_MAX_DIM]; + int32_t chunkshape[B2ND_MAX_DIM]; + int32_t blockshape[B2ND_MAX_DIM]; + int64_t buffershape[B2ND_MAX_DIM]; + int8_t axis; + int64_t start; +} test_shapes_t; + + +CUTEST_TEST_SETUP(insert) { + blosc2_init(); + + // Add parametrizations + CUTEST_PARAMETRIZE(typesize, uint8_t, CUTEST_DATA( + 1, + 2, + 4, + 8, + )); + + CUTEST_PARAMETRIZE(backend, _test_backend, CUTEST_DATA( + {false, false}, + {true, false}, + {true, true}, + {false, true}, + )); + + + CUTEST_PARAMETRIZE(shapes, test_shapes_t, CUTEST_DATA( + {1, {5}, {3}, {2}, {10}, 0, 5}, // insert at the end (old padding modified) + {2, {18, 6}, {6, 6}, {3, 3}, {18, 12}, 1, 0}, // insert at the beginning + {3, {12, 10, 14}, {3, 5, 9}, {3, 4, 4}, {12, 10, 18}, 2, 9}, // insert in the middle + {4, {10, 10, 5, 5}, {5, 7, 3, 3}, {2, 2, 1, 1}, {10, 10, 5, 30}, 3, 3}, // insert in the middle + + )); +} + +CUTEST_TEST_TEST(insert) { + CUTEST_GET_PARAMETER(backend, _test_backend); + CUTEST_GET_PARAMETER(shapes, test_shapes_t); + CUTEST_GET_PARAMETER(typesize, uint8_t); + + char *urlpath = "test_insert_shape.b2frame"; + blosc2_remove_urlpath(urlpath); + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.typesize = typesize; + cparams.nthreads = 2; + blosc2_storage b2_storage = {.cparams=&cparams}; + if (backend.persistent) { + b2_storage.urlpath = urlpath; + } + b2_storage.contiguous = backend.contiguous; + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, shapes.ndim, shapes.shape, + shapes.chunkshape, shapes.blockshape, NULL, 0, NULL, 0); + + int64_t buffersize = typesize; + for (int i = 0; i < ctx->ndim; ++i) { + buffersize *= shapes.buffershape[i]; + } + + /* Create b2nd_array_t filled with 1 */ + b2nd_array_t *src; + uint8_t *value = malloc(typesize); + int8_t fill_value = 1; + switch (typesize) { + case 8: + ((int64_t *) value)[0] = (int64_t) fill_value; + break; + case 4: + ((int32_t *) value)[0] = (int32_t) fill_value; + break; + case 2: + ((int16_t *) value)[0] = (int16_t) fill_value; + break; + case 1: + ((int8_t *) value)[0] = fill_value; + break; + default: + break; + } + BLOSC_ERROR(b2nd_full(ctx, &src, value)); + + uint8_t *buffer = malloc(buffersize); + fill_buf(buffer, typesize, buffersize / typesize); + BLOSC_ERROR(b2nd_insert(src, buffer, buffersize, shapes.axis, shapes.start)); + + int64_t start[B2ND_MAX_DIM] = {0}; + start[shapes.axis] = shapes.start; + int64_t stop[B2ND_MAX_DIM]; + for (int i = 0; i < shapes.ndim; ++i) { + stop[i] = shapes.shape[i]; + } + stop[shapes.axis] = shapes.start + shapes.buffershape[shapes.axis]; + + /* Fill buffer with a slice from the new chunks */ + uint8_t *res_buffer = malloc(buffersize); + BLOSC_ERROR(b2nd_get_slice_cbuffer(src, start, stop, res_buffer, + shapes.buffershape, buffersize)); + + for (uint64_t i = 0; i < (uint64_t) buffersize / typesize; ++i) { + switch (typesize) { + case 8: + CUTEST_ASSERT("Elements are not equal!", + ((uint64_t *) buffer)[i] == ((uint64_t *) res_buffer)[i]); + break; + case 4: + CUTEST_ASSERT("Elements are not equal!", + ((uint32_t *) buffer)[i] == ((uint32_t *) res_buffer)[i]); + break; + case 2: + CUTEST_ASSERT("Elements are not equal!", + ((uint16_t *) buffer)[i] == ((uint16_t *) res_buffer)[i]); + break; + case 1: + CUTEST_ASSERT("Elements are not equal!", + ((uint8_t *) buffer)[i] == ((uint8_t *) res_buffer)[i]); + break; + default: + B2ND_TEST_ASSERT(BLOSC2_ERROR_INVALID_PARAM); + } + } + /* Free mallocs */ + free(value); + free(buffer); + free(res_buffer); + + B2ND_TEST_ASSERT(b2nd_free(src)); + B2ND_TEST_ASSERT(b2nd_free_ctx(ctx)); + blosc2_remove_urlpath(urlpath); + + return 0; +} + +CUTEST_TEST_TEARDOWN(insert) { + blosc2_destroy(); +} + +int main() { + CUTEST_TEST_RUN(insert); +} diff --git a/tests/b2nd/test_b2nd_metalayers.c b/tests/b2nd/test_b2nd_metalayers.c new file mode 100644 index 00000000..3bed63a8 --- /dev/null +++ b/tests/b2nd/test_b2nd_metalayers.c @@ -0,0 +1,157 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +#include "test_common.h" + + +CUTEST_TEST_SETUP(metalayers) { + blosc2_init(); + + // Add parametrizations + CUTEST_PARAMETRIZE(typesize, uint8_t, CUTEST_DATA(1, 2, 4, 8)); + CUTEST_PARAMETRIZE(shapes, _test_shapes, CUTEST_DATA( + {0, {0}, {0}, {0}}, // 0-dim + {1, {10}, {7}, {2}}, // 1-idim + {2, {100, 100}, {20, 20}, {10, 10}}, + )); + CUTEST_PARAMETRIZE(backend, _test_backend, CUTEST_DATA( + {true, true}, + {false, true}, + )); +} + + +CUTEST_TEST_TEST(metalayers) { + CUTEST_GET_PARAMETER(shapes, _test_shapes); + CUTEST_GET_PARAMETER(typesize, uint8_t); + CUTEST_GET_PARAMETER(backend, _test_backend); + + char *urlpath = "test_metalayers.b2nd"; + blosc2_remove_urlpath(urlpath); + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.nthreads = 2; + cparams.typesize = typesize; + blosc2_storage b2_storage = {.cparams=&cparams}; + if (backend.persistent) { + b2_storage.urlpath = urlpath; + } + b2_storage.contiguous = backend.contiguous; + + int8_t nmetalayers = 1; + blosc2_metalayer metalayers[B2ND_MAX_METALAYERS]; + blosc2_metalayer *meta0 = &metalayers[0]; + meta0->name = "test_meta"; + meta0->content_len = 3; + double sdata0 = 5.789; + meta0->content = (uint8_t *) &sdata0; + meta0->content_len = sizeof(sdata0); + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, shapes.ndim, shapes.shape, + shapes.chunkshape, shapes.blockshape, NULL, 0, metalayers, nmetalayers); + + /* Create original data */ + size_t buffersize = typesize; + for (int i = 0; i < ctx->ndim; ++i) { + buffersize *= (size_t) ctx->shape[i]; + } + + uint8_t *buffer = malloc(buffersize); + CUTEST_ASSERT("Buffer filled incorrectly", fill_buf(buffer, typesize, buffersize / typesize)); + + /* Create b2nd_array_t with original data */ + b2nd_array_t *src; + B2ND_TEST_ASSERT(b2nd_from_cbuffer(ctx, &src, buffer, buffersize)); + + blosc2_metalayer vlmeta1; + + uint64_t sdata1 = 56; + vlmeta1.name = "vlmeta1"; + vlmeta1.content = (uint8_t *) &sdata1; + vlmeta1.content_len = sizeof(sdata1); + + B2ND_TEST_ASSERT(blosc2_vlmeta_add(src->sc, vlmeta1.name, vlmeta1.content, vlmeta1.content_len, + src->sc->storage->cparams)); + + int rc = blosc2_vlmeta_exists(src->sc, "vlmeta2"); + CUTEST_ASSERT("", rc < 0); + rc = blosc2_vlmeta_exists(src->sc, vlmeta1.name); + CUTEST_ASSERT("", rc == 0); + + uint8_t *content; + int32_t content_len; + B2ND_TEST_ASSERT(blosc2_vlmeta_get(src->sc, vlmeta1.name, &content, &content_len)); + CUTEST_ASSERT("Contents are not equal", + *((uint64_t *) vlmeta1.content) == *((uint64_t *) content)); + CUTEST_ASSERT("Sizes are not equal", vlmeta1.content_len == content_len); + free(content); + + float sdata11 = 4.5f; + vlmeta1.content = (uint8_t *) &sdata11; + vlmeta1.content_len = sizeof(sdata11); + + B2ND_TEST_ASSERT(blosc2_vlmeta_update(src->sc, vlmeta1.name, vlmeta1.content, vlmeta1.content_len, + src->sc->storage->cparams)); + + B2ND_TEST_ASSERT(blosc2_vlmeta_get(src->sc, vlmeta1.name, &content, &content_len)); + CUTEST_ASSERT("Contents are not equal", *((float *) vlmeta1.content) == *((float *) content)); + CUTEST_ASSERT("Sizes are not equal", vlmeta1.content_len == content_len); + free(content); + + blosc2_metalayer vlmeta2; + vlmeta2.name = "vlmeta2"; + vlmeta2.content = (uint8_t *) &sdata1; + vlmeta2.content_len = sizeof(sdata1); + B2ND_TEST_ASSERT(blosc2_vlmeta_add(src->sc, vlmeta2.name, vlmeta2.content, vlmeta2.content_len, + src->sc->storage->cparams)); + B2ND_TEST_ASSERT(b2nd_free(src)); + + b2nd_array_t *src2; + b2nd_open(urlpath, &src2); + + B2ND_TEST_ASSERT(blosc2_vlmeta_get(src2->sc, vlmeta2.name, &content, &content_len)); + CUTEST_ASSERT("Contents are not equal", *((uint64_t *) vlmeta2.content) == *((uint64_t *) content)); + CUTEST_ASSERT("Sizes are not equal", vlmeta2.content_len == content_len); + free(content); + + sdata0 = 1e-10; + blosc2_metalayer meta1; + meta1.name = meta0->name; + meta1.content = (uint8_t *) &sdata0; + meta1.content_len = sizeof(sdata0); + + rc = blosc2_meta_exists(src2->sc, meta0->name); + CUTEST_ASSERT("", rc == 1); + B2ND_TEST_ASSERT(blosc2_meta_update(src2->sc, meta1.name, meta1.content, meta1.content_len)); + + blosc2_metalayer meta2; + B2ND_TEST_ASSERT(blosc2_meta_get(src2->sc, meta1.name, &meta2.content, &meta2.content_len)); + + CUTEST_ASSERT("Contents are not equal", *((double *) meta2.content) == *((double *) meta1.content)); + CUTEST_ASSERT("Sizes are not equal", meta2.content_len == meta1.content_len); + free(meta2.content); + + /* Free mallocs */ + free(buffer); + B2ND_TEST_ASSERT(b2nd_free(src2)); + B2ND_TEST_ASSERT(b2nd_free_ctx(ctx)); + + blosc2_remove_urlpath(urlpath); + return 0; +} + + +CUTEST_TEST_TEARDOWN(metalayers) { + blosc2_destroy(); +} + +int main() { + CUTEST_TEST_RUN(metalayers); +} diff --git a/tests/b2nd/test_b2nd_open_offset.c b/tests/b2nd/test_b2nd_open_offset.c new file mode 100644 index 00000000..d29c105a --- /dev/null +++ b/tests/b2nd/test_b2nd_open_offset.c @@ -0,0 +1,195 @@ +/* + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) +*/ + +#include +#include +#include +#include "test_common.h" + +#include "blosc2.h" +#include "b2nd.h" +#include "cutest.h" + +#define KB 1024. +#define MB (1024*KB) +#define GB (1024*MB) + +#define CHUNKSIZE (200 * 1000) +#define NCHUNKS 100 +#define NTHREADS 4 + + +CUTEST_TEST_SETUP(open_offset) { + blosc2_init(); +} + + +CUTEST_TEST_TEST(open_offset) { + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; + int32_t *data1 = malloc(CHUNKSIZE * sizeof(int32_t)); + int32_t *data2 = malloc(CHUNKSIZE * sizeof(int32_t)); + + blosc2_storage storage = BLOSC2_STORAGE_DEFAULTS; + blosc2_schunk* schunk_write_start; + blosc2_schunk* schunk_write_append; + cparams.typesize = sizeof(int32_t); + cparams.compcode = BLOSC_LZ4; + cparams.clevel = 9; + cparams.nthreads = NTHREADS; + dparams.nthreads = NTHREADS; + storage.cparams = &cparams; + storage.dparams = &dparams; + int64_t shape[1] = {NCHUNKS * CHUNKSIZE}; + int32_t chunkshape[1] = {CHUNKSIZE}; + int32_t blockshape[1] = {CHUNKSIZE}; + b2nd_context_t *b2nd_params = b2nd_create_ctx(&storage, 1, shape, chunkshape, blockshape, + NULL, 0, NULL, 0); + b2nd_array_t* arr_write_start; + BLOSC_ERROR(b2nd_empty(b2nd_params, &arr_write_start)); + b2nd_array_t* arr_write_offset; + BLOSC_ERROR(b2nd_empty(b2nd_params, &arr_write_offset)); + schunk_write_start = arr_write_start->sc; + schunk_write_append = arr_write_offset->sc; + schunk_write_start->nchunks = 0; + schunk_write_append->nchunks = 0; + int32_t isize = CHUNKSIZE * sizeof(int32_t); + int i, nchunk; + int64_t nchunks; + + // Add some data + for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { + for (i = 0; i < CHUNKSIZE; i++) { + data1[i] = i * nchunk; + data2[i] = 2 * i * nchunk; + } + nchunks = blosc2_schunk_append_buffer(schunk_write_start, data1, isize); + assert(nchunks == nchunk + 1); + blosc2_schunk_append_buffer(schunk_write_append, data2, isize); + } + + blosc_timestamp_t last, current; + double ttotal; + printf("Blosc version info: %s (%s)\n", + BLOSC2_VERSION_STRING, BLOSC2_VERSION_DATE); + + // Start different conversions between schunks, frames and fileframes + + // super-chunk -> cframe_write_start (contiguous frame, or buffer) + uint8_t* cframe_write_start, *cframe_write_append; + bool cframe_needs_free, cframe_needs_free1; + int64_t frame_len = blosc2_schunk_to_buffer(schunk_write_start, &cframe_write_start, &cframe_needs_free); + if (frame_len < 0) { + return (int)frame_len; + } + int64_t frame_len1 = blosc2_schunk_to_buffer(schunk_write_append, &cframe_write_append, &cframe_needs_free1); + if (frame_len1 < 0) { + return (int)frame_len1; + } + + // super-chunk -> fileframe (contiguous frame, on-disk) + remove("frame_simple.b2frame"); + blosc_set_timestamp(&last); + frame_len = blosc2_schunk_to_file(schunk_write_start, "frame_simple.b2frame"); + if (frame_len < 0) { + return (int)frame_len; + } + printf("Frame length on disk: %ld bytes\n", (long)frame_len); + blosc_set_timestamp(¤t); + ttotal = blosc_elapsed_secs(last, current); + printf("Time for frame_start -> fileframe (frame_simple.b2frame): %.3g s, %.1f GB/s\n", + ttotal, (double)schunk_write_start->nbytes / (ttotal * GB)); + + // super-chunk -> fileframe (contiguous frame, on-disk) + offset + blosc_set_timestamp(&last); + int64_t offset = blosc2_schunk_append_file(schunk_write_append, "frame_simple.b2frame"); + if (offset < 0) { + return (int)offset; + } + blosc_set_timestamp(¤t); + ttotal = blosc_elapsed_secs(last, current); + printf("Time for frame_append -> fileframe (frame_simple.b2frame) + offset: %.3g s, %.1f GB/s\n", + ttotal, (double)schunk_write_append->nbytes / (ttotal * GB)); + + // fileframe (file) -> schunk (on-disk contiguous, super-chunk) + blosc_set_timestamp(&last); + b2nd_array_t* arr_read_start; + BLOSC_ERROR(b2nd_open("file:///frame_simple.b2frame", &arr_read_start)); + blosc_set_timestamp(¤t); + ttotal = blosc_elapsed_secs(last, current); + printf("Time for fileframe (%s) -> frame_start : %.3g s, %.1f GB/s\n", + arr_read_start->sc->storage->urlpath, ttotal, (double)arr_read_start->sc->nbytes / (ttotal * GB)); + + // fileframe (file) + offset -> schunk (on-disk contiguous, super-chunk) + blosc_set_timestamp(&last); + b2nd_array_t* arr_read_offset; + BLOSC_ERROR(b2nd_open_offset("file:///frame_simple.b2frame", &arr_read_offset, offset)); + blosc_set_timestamp(¤t); + ttotal = blosc_elapsed_secs(last, current); + printf("Time for fileframe (%s) + offset %ld -> open_offset : %.3g s, %.1f GB/s\n", + arr_read_offset->sc->storage->urlpath, offset, ttotal, (double)arr_read_offset->sc->nbytes / (ttotal * GB)); + + uint8_t* cframe_read_start, *cframe_read_offset; + bool cframe_needs_free2, cframe_needs_free3; + int64_t frame_len2 = b2nd_to_cframe(arr_read_start, &cframe_read_start, &frame_len, + &cframe_needs_free2); + if (frame_len2 != frame_len) { + return (int)frame_len2; + } + for (int j = 0; j < frame_len; ++j) { + if (cframe_write_start[j] != cframe_read_start[j]) { + printf("schunk != schunk2 in index %d: %u, %u", j, cframe_write_start[j], cframe_read_start[j]); + return -1; + } + } + int64_t frame_len3 = b2nd_to_cframe(arr_read_offset, &cframe_read_offset, &frame_len1, + &cframe_needs_free3); + if (frame_len3 != frame_len1) { + return (int)frame_len3; + } + for (int j = 0; j < frame_len1; ++j) { + if (cframe_write_append[j] != cframe_read_offset[j]) { + printf("schunk1 != schunk3 in index %d: %u, %u", j, cframe_write_append[j], cframe_read_offset[j]); + return -1; + } + } + + printf("Successful roundtrip schunk <-> frame <-> fileframe\n" + " schunk1 <-> frame1 <-> fileframe + offset"); + + +/* Free resources */ + + blosc2_schunk_free(schunk_write_start); + blosc2_schunk_free(schunk_write_append); + b2nd_free(arr_read_start); + b2nd_free(arr_read_offset); + if (cframe_needs_free) { + free(cframe_write_start); + } + if (cframe_needs_free1) { + free(cframe_write_append); + } + if (cframe_needs_free2) { + free(cframe_read_start); + } + if (cframe_needs_free3) { + free(cframe_read_offset); + } + free(data1); + free(data2); + + return 0; +} + +CUTEST_TEST_TEARDOWN(open_offset) { + blosc2_destroy(); +} + + +int main() { + CUTEST_TEST_RUN(open_offset) +} diff --git a/tests/b2nd/test_b2nd_persistency.c b/tests/b2nd/test_b2nd_persistency.c new file mode 100644 index 00000000..3c268b93 --- /dev/null +++ b/tests/b2nd/test_b2nd_persistency.c @@ -0,0 +1,119 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +#include "test_common.h" + +#ifdef __GNUC__ + +#include + +#define FILE_EXISTS(urlpath) access(urlpath, F_OK) +#else +#include +#define FILE_EXISTS(urlpath) _access(urlpath, 0) +#endif + + +typedef struct { + int8_t ndim; + int64_t shape[B2ND_MAX_DIM]; + int32_t chunkshape[B2ND_MAX_DIM]; + int32_t blockshape[B2ND_MAX_DIM]; +} test_shapes_t; + + +CUTEST_TEST_SETUP(persistency) { + blosc2_init(); + + // Add parametrizations + CUTEST_PARAMETRIZE(typesize, uint8_t, CUTEST_DATA(1, 2, 4, 8)); + CUTEST_PARAMETRIZE(shapes, test_shapes_t, CUTEST_DATA( + {0, {0}, {0}, {0}}, // 0-dim + {1, {10}, {7}, {2}}, // 1-idim + {2, {100, 100}, {20, 20}, {10, 10}}, + {3, {100, 55, 23}, {31, 5, 22}, {4, 4, 4}}, + {3, {100, 0, 12}, {31, 0, 12}, {10, 0, 12}}, + {4, {50, 30, 31, 12}, {25, 20, 20, 10}, {5, 5, 5, 10}}, + {5, {1, 1, 1024, 1, 1}, {1, 1, 500, 1, 1}, {1, 1, 200, 1, 1}}, + {6, {5, 1, 100, 3, 1, 2}, {5, 1, 50, 2, 1, 2}, {2, 1, 20, 2, 1, 2}} + )); + CUTEST_PARAMETRIZE(backend, _test_backend, CUTEST_DATA( + {true, true}, + {false, true}, + )); +} + +CUTEST_TEST_TEST(persistency) { + CUTEST_GET_PARAMETER(backend, _test_backend); + CUTEST_GET_PARAMETER(shapes, test_shapes_t); + CUTEST_GET_PARAMETER(typesize, uint8_t); + + char *urlpath = "test_persistency.b2frame"; + blosc2_remove_urlpath(urlpath); + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.nthreads = 2; + cparams.typesize = typesize; + blosc2_storage b2_storage = {.cparams=&cparams}; + if (backend.persistent) { + b2_storage.urlpath = urlpath; + } + b2_storage.contiguous = backend.contiguous; + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, shapes.ndim, shapes.shape, + shapes.chunkshape, shapes.blockshape, NULL, 0, NULL, 0); + + /* Create original data */ + int64_t buffersize = typesize; + for (int i = 0; i < ctx->ndim; ++i) { + buffersize *= shapes.shape[i]; + } + uint8_t *buffer = malloc(buffersize); + CUTEST_ASSERT("Buffer filled incorrectly", fill_buf(buffer, typesize, buffersize / typesize)); + + /* Create b2nd_array_t with original data */ + b2nd_array_t *src; + B2ND_TEST_ASSERT(b2nd_from_cbuffer(ctx, &src, buffer, buffersize)); + + b2nd_array_t *dest; + B2ND_TEST_ASSERT(b2nd_open(urlpath, &dest)); + + /* Fill dest array with b2nd_array_t data */ + uint8_t *buffer_dest = malloc(buffersize); + B2ND_TEST_ASSERT(b2nd_to_cbuffer(dest, buffer_dest, buffersize)); + + /* Testing */ + if (dest->nitems != 0) { + for (int i = 0; i < buffersize / typesize; ++i) { + // printf("%d - %d\n", buffer[i], buffer_dest[i]); + CUTEST_ASSERT("Elements are not equals!", buffer[i] == buffer_dest[i]); + } + } + + /* Free mallocs */ + free(buffer); + free(buffer_dest); + B2ND_TEST_ASSERT(b2nd_free(src)); + B2ND_TEST_ASSERT(b2nd_free(dest)); + B2ND_TEST_ASSERT(b2nd_free_ctx(ctx)); + + blosc2_remove_urlpath(urlpath); + + return 0; +} + + +CUTEST_TEST_TEARDOWN(persistency) { + blosc2_destroy(); +} + +int main() { + CUTEST_TEST_RUN(persistency); +} diff --git a/tests/b2nd/test_b2nd_resize.c b/tests/b2nd/test_b2nd_resize.c new file mode 100644 index 00000000..71a9c196 --- /dev/null +++ b/tests/b2nd/test_b2nd_resize.c @@ -0,0 +1,206 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +#include "test_common.h" + +typedef struct { + int8_t ndim; + int64_t shape[B2ND_MAX_DIM]; + int32_t chunkshape[B2ND_MAX_DIM]; + int32_t blockshape[B2ND_MAX_DIM]; + int64_t newshape[B2ND_MAX_DIM]; + bool given_pos; + int64_t start_resize[B2ND_MAX_DIM]; +} test_shapes_t; + + +CUTEST_TEST_SETUP(resize_shape) { + blosc2_init(); + + // Add parametrizations + CUTEST_PARAMETRIZE(typesize, uint8_t, CUTEST_DATA( + 1, + 2, + 4, + 8, + )); + + CUTEST_PARAMETRIZE(backend, _test_backend, CUTEST_DATA( + {false, false}, + {true, false}, + {true, true}, + {false, true}, + )); + + + CUTEST_PARAMETRIZE(shapes, test_shapes_t, CUTEST_DATA( + {1, {5}, {3}, {2}, {10}, false, {5}}, // extend only + {2, {20, 5}, {7, 5}, {3, 3}, {27, 10}, true, {14, 5}}, // extend only - start + {2, {20, 10}, {7, 5}, {3, 5}, {10, 10}, false, {10, 10}}, // shrink only + {2, {30, 20}, {8, 5}, {2, 2}, {22, 10}, true, {8, 5}}, // shrink only - start + {3, {12, 10, 14}, {3, 5, 9}, {3, 4, 4}, {10, 15, 14}, false, {10, 10, 14}}, // shrink and extend + {3, {10, 21, 30}, {8, 7, 15}, {5, 5, 10}, {10, 13, 10}, false, {10, 13, 10}}, // shrink only + {3, {10, 23, 30}, {8, 7, 15}, {5, 5, 10}, {10, 16, 45}, true, {0, 0, 0}}, // shrink and extend - start + {2, {75, 50}, {25, 13}, {8, 8}, {50, 76}, true, {50, 13}}, // shrink and extend - start + {2, {50, 50}, {25, 13}, {8, 8}, {49, 51}, false, {49, 50}}, // shrink and extend + {2, {143, 41}, {18, 13}, {7, 7}, {50, 50}, false, {50, 41}}, // shrink and extend + {4, {10, 10, 5, 5}, {5, 7, 3, 3}, {2, 2, 1, 1}, {11, 20, 2, 2}, false, {10, 10, 2, 2}}, // shrink and extend + + )); +} + +CUTEST_TEST_TEST(resize_shape) { + CUTEST_GET_PARAMETER(backend, _test_backend); + CUTEST_GET_PARAMETER(shapes, test_shapes_t); + CUTEST_GET_PARAMETER(typesize, uint8_t); + + char *urlpath = "test_resize_shape.b2frame"; + blosc2_remove_urlpath(urlpath); + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.nthreads = 2; + cparams.typesize = typesize; + blosc2_storage b2_storage = {.cparams=&cparams}; + if (backend.persistent) { + b2_storage.urlpath = urlpath; + } + b2_storage.contiguous = backend.contiguous; + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, shapes.ndim, shapes.shape, + shapes.chunkshape, shapes.blockshape, NULL, 0, NULL, 0); + + int64_t buffersize = typesize; + bool only_shrink = true; + for (int i = 0; i < ctx->ndim; ++i) { + if (shapes.newshape[i] > shapes.shape[i]) { + only_shrink = false; + } + buffersize *= shapes.newshape[i]; + } + + /* Create b2nd_array_t with original data */ + b2nd_array_t *src; + uint8_t *value = malloc(typesize); + int8_t fill_value = 1; + switch (typesize) { + case 8: + ((int64_t *) value)[0] = (int64_t) fill_value; + break; + case 4: + ((int32_t *) value)[0] = (int32_t) fill_value; + break; + case 2: + ((int16_t *) value)[0] = (int16_t) fill_value; + break; + case 1: + ((int8_t *) value)[0] = fill_value; + break; + default: + break; + } + BLOSC_ERROR(b2nd_full(ctx, &src, value)); + + if (shapes.given_pos) { + BLOSC_ERROR(b2nd_resize(src, shapes.newshape, shapes.start_resize)); + } else { + BLOSC_ERROR(b2nd_resize(src, shapes.newshape, NULL)); + } + + // Create aux array to compare values + b2nd_array_t *aux; + blosc2_storage aux_b2_storage = {.cparams=&cparams}; + aux_b2_storage.contiguous = backend.contiguous; + b2nd_context_t *aux_ctx = b2nd_create_ctx(&aux_b2_storage, shapes.ndim, shapes.newshape, + shapes.chunkshape, shapes.blockshape, NULL, 0, NULL, 0); + + BLOSC_ERROR(b2nd_full(aux_ctx, &aux, value)); + if (!only_shrink) { + for (int i = 0; i < shapes.ndim; ++i) { + if (shapes.newshape[i] <= shapes.shape[i]) { + continue; + } + int64_t slice_start[B2ND_MAX_DIM] = {0}; + int64_t slice_stop[B2ND_MAX_DIM]; + int64_t slice_shape[B2ND_MAX_DIM] = {0}; + int64_t buffer_len = 1; + for (int j = 0; j < shapes.ndim; ++j) { + if (j != i) { + slice_shape[j] = shapes.newshape[j]; + buffer_len *= slice_shape[j]; + slice_stop[j] = shapes.newshape[j]; + } + } + slice_start[i] = shapes.start_resize[i]; + slice_shape[i] = shapes.newshape[i] - shapes.shape[i]; + if (slice_start[i] % shapes.chunkshape[i] != 0) { + // Old padding was filled with ones + slice_shape[i] -= shapes.chunkshape[i] - slice_start[i] % shapes.chunkshape[i]; + slice_start[i] += shapes.chunkshape[i] - slice_start[i] % shapes.chunkshape[i]; + } + if (slice_start[i] > shapes.newshape[i]) { + continue; + } + slice_stop[i] = slice_start[i] + slice_shape[i]; + buffer_len *= slice_shape[i]; + uint8_t *buffer = calloc((size_t) buffer_len, (size_t) typesize); + BLOSC_ERROR(b2nd_set_slice_cbuffer(buffer, slice_shape, buffer_len * typesize, + slice_start, slice_stop, aux)); + free(buffer); + } + } + + /* Fill buffers with whole arrays */ + uint8_t *src_buffer = malloc((size_t) buffersize); + uint8_t *aux_buffer = malloc((size_t) buffersize); + B2ND_TEST_ASSERT(b2nd_to_cbuffer(src, src_buffer, buffersize)); + B2ND_TEST_ASSERT(b2nd_to_cbuffer(aux, aux_buffer, buffersize)); + for (uint64_t i = 0; i < (uint64_t) buffersize / typesize; ++i) { + switch (typesize) { + case 8: + CUTEST_ASSERT("Elements are not equal!", + ((uint64_t *) src_buffer)[i] == ((uint64_t *) aux_buffer)[i]); + break; + case 4: + CUTEST_ASSERT("Elements are not equal!", + ((uint32_t *) src_buffer)[i] == ((uint32_t *) aux_buffer)[i]); + break; + case 2: + CUTEST_ASSERT("Elements are not equal!", + ((uint16_t *) src_buffer)[i] == ((uint16_t *) aux_buffer)[i]); + break; + case 1: + CUTEST_ASSERT("Elements are not equal!", + ((uint8_t *) src_buffer)[i] == ((uint8_t *) aux_buffer)[i]); + break; + default: + B2ND_TEST_ASSERT(BLOSC2_ERROR_INVALID_PARAM); + } + } + /* Free mallocs */ + free(value); + free(src_buffer); + free(aux_buffer); + + B2ND_TEST_ASSERT(b2nd_free(src)); + B2ND_TEST_ASSERT(b2nd_free(aux)); + B2ND_TEST_ASSERT(b2nd_free_ctx(ctx)); + B2ND_TEST_ASSERT(b2nd_free_ctx(aux_ctx)); + blosc2_remove_urlpath(urlpath); + + return 0; +} + +CUTEST_TEST_TEARDOWN(resize_shape) { + blosc2_destroy(); +} + +int main() { + CUTEST_TEST_RUN(resize_shape); +} diff --git a/tests/b2nd/test_b2nd_roundtrip.c b/tests/b2nd/test_b2nd_roundtrip.c new file mode 100644 index 00000000..bea57218 --- /dev/null +++ b/tests/b2nd/test_b2nd_roundtrip.c @@ -0,0 +1,78 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +#include "test_common.h" + + +CUTEST_TEST_SETUP(roundtrip) { + blosc2_init(); + + // Add parametrizations + b2nd_default_parameters(); +} + + +CUTEST_TEST_TEST(roundtrip) { + CUTEST_GET_PARAMETER(backend, _test_backend); + CUTEST_GET_PARAMETER(shapes, _test_shapes); + CUTEST_GET_PARAMETER(typesize, uint8_t); + + char *urlpath = "test_roundtrip.b2frame"; + blosc2_remove_urlpath(urlpath); + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.nthreads = 2; + cparams.typesize = typesize; + blosc2_storage b2_storage = {.cparams=&cparams}; + if (backend.persistent) { + b2_storage.urlpath = urlpath; + } + b2_storage.contiguous = backend.contiguous; + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, shapes.ndim, shapes.shape, + shapes.chunkshape, shapes.blockshape, NULL, 0, NULL, 0); + + /* Create original data */ + size_t buffersize = (size_t) typesize; + for (int i = 0; i < shapes.ndim; ++i) { + buffersize *= (size_t) shapes.shape[i]; + } + uint8_t *buffer = malloc(buffersize); + CUTEST_ASSERT("Buffer filled incorrectly", fill_buf(buffer, typesize, buffersize / typesize)); + + /* Create b2nd_array_t with original data */ + b2nd_array_t *src; + B2ND_TEST_ASSERT(b2nd_from_cbuffer(ctx, &src, buffer, buffersize)); + + /* Fill dest array with b2nd_array_t data */ + uint8_t *buffer_dest = malloc(buffersize); + B2ND_TEST_ASSERT(b2nd_to_cbuffer(src, buffer_dest, buffersize)); + + /* Testing */ + B2ND_TEST_ASSERT_BUFFER(buffer, buffer_dest, (int) buffersize); + + /* Free mallocs */ + free(buffer); + free(buffer_dest); + B2ND_TEST_ASSERT(b2nd_free(src)); + B2ND_TEST_ASSERT(b2nd_free_ctx(ctx)); + blosc2_remove_urlpath(urlpath); + + return BLOSC2_ERROR_SUCCESS; +} + + +CUTEST_TEST_TEARDOWN(roundtrip) { + blosc2_destroy(); +} + +int main() { + CUTEST_TEST_RUN(roundtrip); +} diff --git a/tests/b2nd/test_b2nd_save.c b/tests/b2nd/test_b2nd_save.c new file mode 100644 index 00000000..63048808 --- /dev/null +++ b/tests/b2nd/test_b2nd_save.c @@ -0,0 +1,116 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +#include "test_common.h" + +#ifdef __GNUC__ + +#include + +#define FILE_EXISTS(urlpath) access(urlpath, F_OK) +#else +#include +#define FILE_EXISTS(urlpath) _access(urlpath, 0) +#endif + + +typedef struct { + int8_t ndim; + int64_t shape[B2ND_MAX_DIM]; + int32_t chunkshape[B2ND_MAX_DIM]; + int32_t blockshape[B2ND_MAX_DIM]; +} test_shapes_t; + + +CUTEST_TEST_SETUP(save) { + blosc2_init(); + + // Add parametrizations + CUTEST_PARAMETRIZE(typesize, uint8_t, CUTEST_DATA(1, 2, 4, 8)); + CUTEST_PARAMETRIZE(shapes, test_shapes_t, CUTEST_DATA( + {0, {0}, {0}, {0}}, // 0-dim + {1, {10}, {7}, {2}}, // 1-idim + {2, {100, 100}, {20, 20}, {10, 10}}, + {3, {40, 55, 23}, {31, 5, 22}, {4, 4, 4}}, + {3, {100, 0, 12}, {31, 0, 12}, {10, 0, 12}}, + )); + CUTEST_PARAMETRIZE(backend, _test_backend, CUTEST_DATA( + {true, false}, + {false, false}, + )); +} + +CUTEST_TEST_TEST(save) { + CUTEST_GET_PARAMETER(backend, _test_backend); + CUTEST_GET_PARAMETER(shapes, test_shapes_t); + CUTEST_GET_PARAMETER(typesize, uint8_t); + + char *urlpath = "test_save.b2frame"; + blosc2_remove_urlpath(urlpath); + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.nthreads = 2; + cparams.typesize = typesize; + blosc2_storage b2_storage = {.cparams=&cparams}; + b2_storage.urlpath = NULL; + b2_storage.contiguous = backend.contiguous; + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, shapes.ndim, shapes.shape, + shapes.chunkshape, shapes.blockshape, NULL, 0, NULL, 0); + + /* Create original data */ + int64_t buffersize = typesize; + for (int i = 0; i < ctx->ndim; ++i) { + buffersize *= shapes.shape[i]; + } + uint8_t *buffer = malloc(buffersize); + CUTEST_ASSERT("Buffer filled incorrectly", fill_buf(buffer, typesize, buffersize / typesize)); + + /* Create b2nd_array_t with original data */ + b2nd_array_t *src; + B2ND_TEST_ASSERT(b2nd_from_cbuffer(ctx, &src, buffer, buffersize)); + + B2ND_TEST_ASSERT(b2nd_save(src, urlpath)); + b2nd_array_t *dest; + B2ND_TEST_ASSERT(b2nd_open(urlpath, &dest)); + + /* Fill dest array with b2nd_array_t data */ + uint8_t *buffer_dest = malloc(buffersize); + B2ND_TEST_ASSERT(b2nd_to_cbuffer(dest, buffer_dest, buffersize)); + + /* Testing */ + if (dest->nitems != 0) { + for (int i = 0; i < buffersize / typesize; ++i) { + // printf("%d - %d\n", buffer[i], buffer_dest[i]); + CUTEST_ASSERT("Elements are not equals!", buffer[i] == buffer_dest[i]); + } + } + + /* Free mallocs */ + free(buffer); + free(buffer_dest); + B2ND_TEST_ASSERT(b2nd_free(src)); + B2ND_TEST_ASSERT(b2nd_free(dest)); + B2ND_TEST_ASSERT(b2nd_free_ctx(ctx)); + + blosc2_remove_urlpath(urlpath); + + return 0; +} + + +CUTEST_TEST_TEARDOWN(save) { + blosc2_destroy(); +} + +int main() { + CUTEST_TEST_RUN(save); + +} diff --git a/tests/b2nd/test_b2nd_serialize.c b/tests/b2nd/test_b2nd_serialize.c new file mode 100644 index 00000000..d3dc5f8f --- /dev/null +++ b/tests/b2nd/test_b2nd_serialize.c @@ -0,0 +1,96 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +#include "test_common.h" + + +CUTEST_TEST_SETUP(serialize) { + blosc2_init(); + + // Add parametrizations + CUTEST_PARAMETRIZE(typesize, uint8_t, CUTEST_DATA(1, 2, 4, 8)); + CUTEST_PARAMETRIZE(shapes, _test_shapes, CUTEST_DATA( + {0, {0}, {0}, {0}}, // 0-dim + {1, {10}, {7}, {2}}, // 1-idim + {2, {40, 40}, {20, 20}, {10, 10}}, + {3, {100, 55, 23}, {31, 5, 22}, {4, 4, 4}}, + {3, {100, 0, 12}, {31, 0, 12}, {10, 0, 12}}, + {4, {30, 26, 31, 12}, {25, 20, 20, 10}, {5, 5, 5, 10}}, + {5, {1, 1, 1024, 1, 1}, {1, 1, 500, 1, 1}, {1, 1, 200, 1, 1}}, + {6, {5, 1, 60, 3, 1, 2}, {5, 1, 50, 2, 1, 2}, {2, 1, 20, 2, 1, 2}} + )); + CUTEST_PARAMETRIZE(contiguous, bool, CUTEST_DATA(true, false)); +} + + +CUTEST_TEST_TEST(serialize) { + CUTEST_GET_PARAMETER(shapes, _test_shapes); + CUTEST_GET_PARAMETER(typesize, uint8_t); + CUTEST_GET_PARAMETER(contiguous, bool); + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.nthreads = 2; + cparams.typesize = typesize; + blosc2_storage b2_storage = {.cparams=&cparams}; + b2_storage.contiguous = contiguous; + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, shapes.ndim, shapes.shape, + shapes.chunkshape, shapes.blockshape, NULL, 0, NULL, 0); + + /* Create original data */ + size_t buffersize = typesize; + for (int i = 0; i < ctx->ndim; ++i) { + buffersize *= (size_t) ctx->shape[i]; + } + + uint8_t *buffer = malloc(buffersize); + CUTEST_ASSERT("Buffer filled incorrectly", fill_buf(buffer, typesize, buffersize / typesize)); + + /* Create b2nd_array_t with original data */ + b2nd_array_t *src; + B2ND_TEST_ASSERT(b2nd_from_cbuffer(ctx, &src, buffer, buffersize)); + + uint8_t *cframe; + int64_t cframe_len; + bool needs_free; + B2ND_TEST_ASSERT(b2nd_to_cframe(src, &cframe, &cframe_len, &needs_free)); + + b2nd_array_t *dest; + B2ND_TEST_ASSERT(b2nd_from_cframe(cframe, cframe_len, true, &dest)); + + /* Fill dest array with b2nd_array_t data */ + uint8_t *buffer_dest = malloc(buffersize); + B2ND_TEST_ASSERT(b2nd_to_cbuffer(dest, buffer_dest, buffersize)); + + /* Testing */ + B2ND_TEST_ASSERT_BUFFER(buffer, buffer_dest, (int) buffersize); + + /* Free mallocs */ + if (needs_free) { + free(cframe); + } + + free(buffer); + free(buffer_dest); + B2ND_TEST_ASSERT(b2nd_free(src)); + B2ND_TEST_ASSERT(b2nd_free(dest)); + B2ND_TEST_ASSERT(b2nd_free_ctx(ctx)); + + return 0; +} + + +CUTEST_TEST_TEARDOWN(serialize) { + blosc2_destroy(); +} + +int main() { + CUTEST_TEST_RUN(serialize); +} diff --git a/tests/b2nd/test_b2nd_set_slice_buffer.c b/tests/b2nd/test_b2nd_set_slice_buffer.c new file mode 100644 index 00000000..5496dc06 --- /dev/null +++ b/tests/b2nd/test_b2nd_set_slice_buffer.c @@ -0,0 +1,150 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +#include "test_common.h" + +typedef struct { + int8_t ndim; + int64_t shape[B2ND_MAX_DIM]; + int32_t chunkshape[B2ND_MAX_DIM]; + int32_t blockshape[B2ND_MAX_DIM]; + int64_t start[B2ND_MAX_DIM]; + int64_t stop[B2ND_MAX_DIM]; +} test_shapes_t; + + +CUTEST_TEST_SETUP(set_slice_buffer) { + blosc2_init(); + + // Add parametrizations + CUTEST_PARAMETRIZE(typesize, uint8_t, CUTEST_DATA( + 1, + 2, + 4, + 8, + )); + + CUTEST_PARAMETRIZE(backend, _test_backend, CUTEST_DATA( + {false, false}, + {true, false}, + {true, true}, + {false, true}, + )); + + + CUTEST_PARAMETRIZE(shapes, test_shapes_t, CUTEST_DATA( + {0, {0}, {0}, {0}, {0}, {0}}, // 0-dim + {1, {5}, {3}, {2}, {2}, {5}}, // 1-idim + {2, {20, 0}, {7, 0}, {3, 0}, {2, 0}, {8, 0}}, // 0-shape + {2, {20, 10}, {7, 5}, {3, 5}, {2, 0}, {18, 0}}, // 0-shape + {2, {14, 10}, {8, 5}, {2, 2}, {5, 3}, {9, 10}}, + {3, {12, 10, 14}, {3, 5, 9}, {3, 4, 4}, {3, 0, 3}, {6, 7, 10}}, + {4, {10, 21, 30, 5}, {8, 7, 15, 3}, {5, 5, 10, 1}, {5, 4, 3, 3}, {10, 8, 8, 4}}, + {2, {50, 50}, {25, 13}, {8, 8}, {0, 0}, {10, 10}}, + // The case below makes qemu-aarch64 (AARCH64 emulation) in CI (Ubuntu 22.04) to crash with a segfault. + // Interestingly, this works perfectly well on both intel64 (native) and in aarch64 (emulated via docker). + // Moreover, valgrind does not issue any warning at all when run in the later platforms. + // In conclusion, this *may* be revealing a bug in the qemu-aarch64 binaries in Ubuntu 22.04. + // {2, {143, 41}, {18, 13}, {7, 7}, {4, 2}, {6, 5}}, + // Replacing the above line by this one makes qemu-aarch64 happy. + {2, {150, 45}, {15, 15}, {7, 7}, {4, 2}, {6, 5}}, + {2, {10, 10}, {5, 7}, {2, 2}, {0, 0}, {5, 5}}, + + )); +} + +CUTEST_TEST_TEST(set_slice_buffer) { + CUTEST_GET_PARAMETER(backend, _test_backend); + CUTEST_GET_PARAMETER(shapes, test_shapes_t); + CUTEST_GET_PARAMETER(typesize, uint8_t); + + char *urlpath = "test_set_slice_buffer.b2frame"; + blosc2_remove_urlpath(urlpath); + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.nthreads = 2; + cparams.typesize = typesize; + blosc2_storage b2_storage = {.cparams=&cparams}; + if (backend.persistent) { + b2_storage.urlpath = urlpath; + } + b2_storage.contiguous = backend.contiguous; + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, shapes.ndim, shapes.shape, + shapes.chunkshape, shapes.blockshape, NULL, 0, NULL, 0); + + /* Create dest buffer */ + int64_t shape[B2ND_MAX_DIM] = {0}; + int64_t buffersize = typesize; + for (int i = 0; i < ctx->ndim; ++i) { + shape[i] = shapes.stop[i] - shapes.start[i]; + buffersize *= shape[i]; + } + + uint8_t *buffer = malloc(buffersize); + CUTEST_ASSERT("Buffer filled incorrectly", fill_buf(buffer, typesize, buffersize / typesize)); + + /* Create b2nd_array_t with original data */ + b2nd_array_t *src; + BLOSC_ERROR(b2nd_zeros(ctx, &src)); + + + BLOSC_ERROR(b2nd_set_slice_cbuffer(buffer, shape, buffersize, + shapes.start, shapes.stop, src)); + + + uint8_t *destbuffer = malloc((size_t) buffersize); + + /* Fill dest buffer with a slice*/ + B2ND_TEST_ASSERT(b2nd_get_slice_cbuffer(src, shapes.start, shapes.stop, + destbuffer, + shape, buffersize)); + + for (uint64_t i = 0; i < (uint64_t) buffersize / typesize; ++i) { + uint64_t k = i + 1; + switch (typesize) { + case 8: + CUTEST_ASSERT("Elements are not equals!", + (uint64_t) k == ((uint64_t *) destbuffer)[i]); + break; + case 4: + CUTEST_ASSERT("Elements are not equals!", + (uint32_t) k == ((uint32_t *) destbuffer)[i]); + break; + case 2: + CUTEST_ASSERT("Elements are not equals!", + (uint16_t) k == ((uint16_t *) destbuffer)[i]); + break; + case 1: + CUTEST_ASSERT("Elements are not equals!", + (uint8_t) k == ((uint8_t *) destbuffer)[i]); + break; + default: + B2ND_TEST_ASSERT(BLOSC2_ERROR_INVALID_PARAM); + } + } + + /* Free mallocs */ + free(buffer); + free(destbuffer); + B2ND_TEST_ASSERT(b2nd_free(src)); + B2ND_TEST_ASSERT(b2nd_free_ctx(ctx)); + blosc2_remove_urlpath(urlpath); + + return 0; +} + +CUTEST_TEST_TEARDOWN(set_slice_buffer) { + blosc2_destroy(); +} + +int main() { + CUTEST_TEST_RUN(set_slice_buffer); +} diff --git a/tests/b2nd/test_b2nd_squeeze.c b/tests/b2nd/test_b2nd_squeeze.c new file mode 100644 index 00000000..3de13838 --- /dev/null +++ b/tests/b2nd/test_b2nd_squeeze.c @@ -0,0 +1,135 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +#include "test_common.h" + +typedef struct { + int8_t ndim; + int64_t shape[B2ND_MAX_DIM]; + int32_t chunkshape[B2ND_MAX_DIM]; + int32_t blockshape[B2ND_MAX_DIM]; + int32_t chunkshape2[B2ND_MAX_DIM]; + int32_t blockshape2[B2ND_MAX_DIM]; + int64_t start[B2ND_MAX_DIM]; + int64_t stop[B2ND_MAX_DIM]; +} test_shapes_t; + + +CUTEST_TEST_SETUP(squeeze) { + blosc2_init(); + + // Add parametrizations + CUTEST_PARAMETRIZE(typesize, uint8_t, CUTEST_DATA( + 1, + 2, + 4, + 8 + )); + CUTEST_PARAMETRIZE(backend, _test_backend, CUTEST_DATA( + {false, false}, + {true, false}, + {true, true}, + {false, true}, + )); + CUTEST_PARAMETRIZE(backend2, _test_backend, CUTEST_DATA( + {false, false}, + {true, false}, + {true, true}, + {false, true}, + )); + + + CUTEST_PARAMETRIZE(shapes, test_shapes_t, CUTEST_DATA( + {0, {0}, {0}, {0}, {0}, {0}, {0}, {0}}, // 0-dim + {1, {10}, {7}, {2}, {1}, {1}, {2}, {3}}, // 1-idim + {2, {14, 10}, {8, 5}, {2, 2}, {4, 1}, {2, 1}, {5, 3}, {9, 4}}, // general, + {3, {10, 10, 10}, {3, 5, 9}, {3, 4, 4}, {1, 7, 1}, {1, 5, 1}, {3, 0, 9}, {4, 7, 10}}, + {2, {20, 0}, {7, 0}, {3, 0}, {1, 0}, {1, 0}, {1, 0}, {2, 0}}, // 0-shape + {2, {20, 10}, {7, 5}, {3, 5}, {1, 0}, {1, 0}, {17, 0}, {18, 0}}, // 0-shape + )); +} + + +CUTEST_TEST_TEST(squeeze) { + CUTEST_GET_PARAMETER(backend, _test_backend); + CUTEST_GET_PARAMETER(shapes, test_shapes_t); + CUTEST_GET_PARAMETER(backend2, _test_backend); + CUTEST_GET_PARAMETER(typesize, uint8_t); + + char *urlpath = "test_squeeze.b2frame"; + char *urlpath2 = "test_squeeze2.b2frame"; + + blosc2_remove_urlpath(urlpath); + blosc2_remove_urlpath(urlpath2); + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.nthreads = 2; + cparams.typesize = typesize; + blosc2_storage b2_storage = {.cparams=&cparams}; + if (backend.persistent) { + b2_storage.urlpath = urlpath; + } + b2_storage.contiguous = backend.contiguous; + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, shapes.ndim, shapes.shape, + shapes.chunkshape, shapes.blockshape, NULL, 0, NULL, 0); + + /* Create original data */ + size_t buffersize = typesize; + for (int i = 0; i < ctx->ndim; ++i) { + buffersize *= (size_t) ctx->shape[i]; + } + uint8_t *buffer = malloc(buffersize); + CUTEST_ASSERT("Buffer filled incorrectly", fill_buf(buffer, typesize, buffersize / typesize)); + + /* Create b2nd_array_t with original data */ + b2nd_array_t *src; + B2ND_TEST_ASSERT(b2nd_from_cbuffer(ctx, &src, buffer, buffersize)); + + + /* Create storage for dest container */ + + blosc2_storage b2_storage2 = {.cparams=&cparams}; + if (backend2.persistent) { + b2_storage.urlpath = urlpath2; + } + b2_storage.contiguous = backend2.contiguous; + + // shape will then be overwritten + b2nd_context_t *ctx2 = b2nd_create_ctx(&b2_storage2, shapes.ndim, shapes.shape, + shapes.chunkshape2, shapes.blockshape2, NULL, 0, NULL, 0); + + b2nd_array_t *dest; + B2ND_TEST_ASSERT(b2nd_get_slice(ctx2, &dest, src, shapes.start, shapes.stop)); + + B2ND_TEST_ASSERT(b2nd_squeeze(dest)); + + if (ctx->ndim != 0) { + CUTEST_ASSERT("dims are equal", src->ndim != dest->ndim); + } + + free(buffer); + B2ND_TEST_ASSERT(b2nd_free(src)); + B2ND_TEST_ASSERT(b2nd_free(dest)); + B2ND_TEST_ASSERT(b2nd_free_ctx(ctx)); + B2ND_TEST_ASSERT(b2nd_free_ctx(ctx2)); + blosc2_remove_urlpath(urlpath); + blosc2_remove_urlpath(urlpath2); + + return BLOSC2_ERROR_SUCCESS; +} + +CUTEST_TEST_TEARDOWN(squeeze) { + blosc2_destroy(); +} + +int main() { + CUTEST_TEST_RUN(squeeze); +} diff --git a/tests/b2nd/test_b2nd_squeeze_index.c b/tests/b2nd/test_b2nd_squeeze_index.c new file mode 100644 index 00000000..e7f95747 --- /dev/null +++ b/tests/b2nd/test_b2nd_squeeze_index.c @@ -0,0 +1,147 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +#include "test_common.h" + +typedef struct { + int8_t ndim; + int64_t shape[B2ND_MAX_DIM]; + int32_t chunkshape[B2ND_MAX_DIM]; + int32_t blockshape[B2ND_MAX_DIM]; + int32_t chunkshape2[B2ND_MAX_DIM]; + int32_t blockshape2[B2ND_MAX_DIM]; + int64_t start[B2ND_MAX_DIM]; + int64_t stop[B2ND_MAX_DIM]; + bool squeeze_indexes[B2ND_MAX_DIM]; +} test_squeeze_index_shapes_t; + + +CUTEST_TEST_SETUP(squeeze_index) { + blosc2_init(); + + // Add parametrizations + CUTEST_PARAMETRIZE(typesize, uint8_t, CUTEST_DATA( + 1, + 2, + 4, + 8 + )); + CUTEST_PARAMETRIZE(backend, _test_backend, CUTEST_DATA( + {false, false}, + {true, false}, + {true, true}, + {false, true}, + )); + CUTEST_PARAMETRIZE(backend2, _test_backend, CUTEST_DATA( + {false, false}, + {true, false}, + {true, true}, + {false, true}, + )); + + + CUTEST_PARAMETRIZE(shapes, test_squeeze_index_shapes_t, CUTEST_DATA( + {0, {0}, {0}, {0}, {0}, {0}, + {0}, {0}, {0}}, // 0-dim + {1, {10}, {7}, {2}, {1}, {1}, + {2}, {3}, {0}}, // 1-idim + {2, {14, 10}, {8, 5}, {2, 2}, {4, 1}, {2, 1}, + {5, 3}, {9, 4}, {0, 1}}, // general, + {3, {10, 10, 10}, {3, 5, 9}, {3, 4, 4}, {1, 7, 1}, {1, 5, 1}, + {3, 0, 9}, {4, 7, 10}, {1, 0, 0}}, + {2, {20, 0}, {7, 0}, {3, 0}, {1, 0}, {1, 0}, + {1, 0}, {2, 0}, {1, 0}}, // 0-shape + {2, {20, 10}, {7, 5}, {3, 5}, {1, 0}, {1, 0}, + {17, 0}, {18, 0}, {1, 0}}, // 0-shape + {4, {10, 7, 6, 4}, {7, 5, 1, 4}, {2, 2, 1, 2}, {1, 1, 5, 1}, {1, 1, 2, 1}, + {4, 4, 0, 4}, {5, 5, 10, 5}, {1, 0, 0, 1}} // general + )); +} + + +CUTEST_TEST_TEST(squeeze_index) { + CUTEST_GET_PARAMETER(backend, _test_backend); + CUTEST_GET_PARAMETER(shapes, test_squeeze_index_shapes_t); + CUTEST_GET_PARAMETER(backend2, _test_backend); + CUTEST_GET_PARAMETER(typesize, uint8_t); + + char *urlpath = "test_squeeze_index.b2frame"; + char *urlpath2 = "test_squezze_index2.b2frame"; + + blosc2_remove_urlpath(urlpath); + blosc2_remove_urlpath(urlpath2); + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.nthreads = 2; + cparams.typesize = typesize; + blosc2_storage b2_storage = {.cparams=&cparams}; + if (backend.persistent) { + b2_storage.urlpath = urlpath; + } + b2_storage.contiguous = backend.persistent; + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, shapes.ndim, shapes.shape, + shapes.chunkshape, shapes.blockshape, NULL, 0, NULL, 0); + + /* Create original data */ + size_t buffersize = typesize; + for (int i = 0; i < ctx->ndim; ++i) { + buffersize *= (size_t) ctx->shape[i]; + } + uint8_t *buffer = malloc(buffersize); + CUTEST_ASSERT("Buffer filled incorrectly", fill_buf(buffer, typesize, buffersize / typesize)); + + /* Create b2nd_array_t with original data */ + b2nd_array_t *src; + B2ND_TEST_ASSERT(b2nd_from_cbuffer(ctx, &src, buffer, buffersize)); + + /* Create storage for dest container */ + blosc2_storage b2_storage2 = {.cparams=&cparams}; + if (backend2.persistent) { + b2_storage2.urlpath = urlpath2; + } + b2_storage2.contiguous = backend2.contiguous; + + b2nd_context_t *ctx2 = b2nd_create_ctx(&b2_storage2, shapes.ndim, shapes.shape, + shapes.chunkshape2, shapes.blockshape2, NULL, 0, NULL, 0); + + b2nd_array_t *dest; + B2ND_TEST_ASSERT(b2nd_get_slice(ctx2, &dest, src, shapes.start, shapes.stop)); + + B2ND_TEST_ASSERT(b2nd_squeeze_index(dest, shapes.squeeze_indexes)); + + int8_t nsq = 0; + for (int i = 0; i < ctx->ndim; ++i) { + if (shapes.squeeze_indexes[i] == true) { + nsq++; + } + } + CUTEST_ASSERT("dims are not correct", src->ndim == dest->ndim + nsq); + + free(buffer); + B2ND_TEST_ASSERT(b2nd_free(src)); + B2ND_TEST_ASSERT(b2nd_free(dest)); + B2ND_TEST_ASSERT(b2nd_free_ctx(ctx)); + B2ND_TEST_ASSERT(b2nd_free_ctx(ctx2)); + + blosc2_remove_urlpath(urlpath); + blosc2_remove_urlpath(urlpath2); + + return 0; +} + + +CUTEST_TEST_TEARDOWN(squeeze_index) { + blosc2_destroy(); +} + +int main() { + CUTEST_TEST_RUN(squeeze_index); +} diff --git a/tests/b2nd/test_b2nd_uninit.c b/tests/b2nd/test_b2nd_uninit.c new file mode 100644 index 00000000..d70c4615 --- /dev/null +++ b/tests/b2nd/test_b2nd_uninit.c @@ -0,0 +1,85 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +#include "test_common.h" + + +CUTEST_TEST_SETUP(uninit) { + blosc2_init(); + + // Add parametrizations + CUTEST_PARAMETRIZE(typesize, uint8_t, CUTEST_DATA( + 1, 2, 4, 7 + )); + CUTEST_PARAMETRIZE(shapes, _test_shapes, CUTEST_DATA( + {0, {0}, {0}, {0}}, // 0-dim + {1, {5}, {3}, {2}}, // 1-idim + {2, {20, 0}, {7, 0}, {3, 0}}, // 0-shape + {2, {20, 10}, {7, 5}, {3, 5}}, // 0-shape + {2, {14, 10}, {8, 5}, {2, 2}}, // general, + {3, {12, 10, 14}, {3, 5, 9}, {3, 4, 4}}, // general + {3, {10, 21, 30, 55}, {8, 7, 15, 3}, {5, 5, 10, 1}}, // general, + )); + CUTEST_PARAMETRIZE(backend, _test_backend, CUTEST_DATA( + {false, false}, + {true, false}, + {true, true}, + {false, true}, + )); +} + + +CUTEST_TEST_TEST(uninit) { + CUTEST_GET_PARAMETER(backend, _test_backend); + CUTEST_GET_PARAMETER(shapes, _test_shapes); + CUTEST_GET_PARAMETER(typesize, uint8_t); + + char *urlpath = "test_uninit.b2frame"; + blosc2_remove_urlpath(urlpath); + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.nthreads = 2; + cparams.typesize = typesize; + blosc2_storage b2_storage = {.cparams=&cparams}; + if (backend.persistent) { + b2_storage.urlpath = urlpath; + } + b2_storage.contiguous = backend.contiguous; + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, shapes.ndim, shapes.shape, + shapes.chunkshape, shapes.blockshape, NULL, 0, NULL, 0); + + /* Create b2nd_array_t with uninitialized values */ + b2nd_array_t *src; + B2ND_TEST_ASSERT(b2nd_uninit(ctx, &src)); + + CUTEST_ASSERT("dims are not equal", src->ndim == shapes.ndim); + for (int i = 0; i < shapes.ndim; i++) { + CUTEST_ASSERT("shapes are not equal", src->shape[i] == shapes.shape[i]); + CUTEST_ASSERT("chunkshapes are not equal", src->chunkshape[i] == shapes.chunkshape[i]); + CUTEST_ASSERT("blockshapes are not equal", src->blockshape[i] == shapes.blockshape[i]); + } + + /* Free resources */ + B2ND_TEST_ASSERT(b2nd_free(src)); + B2ND_TEST_ASSERT(b2nd_free_ctx(ctx)); + blosc2_remove_urlpath(urlpath); + + return BLOSC2_ERROR_SUCCESS; +} + + +CUTEST_TEST_TEARDOWN(uninit) { + blosc2_destroy(); +} + +int main() { + CUTEST_TEST_RUN(uninit); +} diff --git a/tests/b2nd/test_b2nd_zeros.c b/tests/b2nd/test_b2nd_zeros.c new file mode 100644 index 00000000..c2ff31ca --- /dev/null +++ b/tests/b2nd/test_b2nd_zeros.c @@ -0,0 +1,94 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +#include "test_common.h" + + +CUTEST_TEST_SETUP(zeros) { + blosc2_init(); + + // Add parametrizations + CUTEST_PARAMETRIZE(typesize, uint8_t, CUTEST_DATA( + 1, 2, 4, 7 + )); + CUTEST_PARAMETRIZE(shapes, _test_shapes, CUTEST_DATA( + {0, {0}, {0}, {0}}, // 0-dim + {1, {5}, {3}, {2}}, // 1-idim + {2, {20, 0}, {7, 0}, {3, 0}}, // 0-shape + {2, {20, 10}, {7, 5}, {3, 5}}, // 0-shape + {2, {14, 10}, {8, 5}, {2, 2}}, // general, + {3, {12, 10, 14}, {3, 5, 9}, {3, 4, 4}}, // general + {3, {10, 21, 30, 55}, {8, 7, 15, 3}, {5, 5, 10, 1}}, // general, + )); + CUTEST_PARAMETRIZE(backend, _test_backend, CUTEST_DATA( + {false, false}, + {true, false}, + {true, true}, + {false, true}, + )); +} + + +CUTEST_TEST_TEST(zeros) { + CUTEST_GET_PARAMETER(backend, _test_backend); + CUTEST_GET_PARAMETER(shapes, _test_shapes); + CUTEST_GET_PARAMETER(typesize, uint8_t); + + char *urlpath = "test_zeros.b2frame"; + blosc2_remove_urlpath(urlpath); + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.nthreads = 2; + cparams.typesize = typesize; + blosc2_storage b2_storage = {.cparams=&cparams}; + if (backend.persistent) { + b2_storage.urlpath = urlpath; + } + b2_storage.contiguous = backend.contiguous; + + b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, shapes.ndim, shapes.shape, + shapes.chunkshape, shapes.blockshape, NULL, 0, NULL, 0); + + /* Create original data */ + int64_t buffersize = typesize; + for (int i = 0; i < shapes.ndim; ++i) { + buffersize *= shapes.shape[i]; + } + + /* Create b2nd_array_t with original data */ + b2nd_array_t *src; + B2ND_TEST_ASSERT(b2nd_zeros(ctx, &src)); + + /* Fill dest array with b2nd_array_t data */ + uint8_t *buffer_dest = malloc(buffersize); + B2ND_TEST_ASSERT(b2nd_to_cbuffer(src, buffer_dest, buffersize)); + + /* Testing */ + for (int i = 0; i < buffersize; ++i) { + CUTEST_ASSERT("Elements are not equals", buffer_dest[i] == 0); + } + + /* Free mallocs */ + free(buffer_dest); + B2ND_TEST_ASSERT(b2nd_free(src)); + B2ND_TEST_ASSERT(b2nd_free_ctx(ctx)); + blosc2_remove_urlpath(urlpath); + + return BLOSC2_ERROR_SUCCESS; +} + + +CUTEST_TEST_TEARDOWN(zeros) { + blosc2_destroy(); +} + +int main() { + CUTEST_TEST_RUN(zeros); +} diff --git a/tests/b2nd/test_common.h b/tests/b2nd/test_common.h new file mode 100644 index 00000000..54a29961 --- /dev/null +++ b/tests/b2nd/test_common.h @@ -0,0 +1,99 @@ +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) + + See LICENSE.txt for details about copyright and rights to use. +**********************************************************************/ + +#ifndef B2ND_TEST_COMMON_H +#define B2ND_TEST_COMMON_H + +#include +#include "context.h" +#include "cutest.h" + + +#define B2ND_TEST_ASSERT(rc) CUTEST_ASSERT(print_error(rc), (rc) >= 0); + +#ifdef __GNUC__ +#define B2ND_TEST_UNUSED __attribute__((unused)) +#else +#define B2ND_TEST_UNUSED +#endif + +static bool fill_buf(void *buf, uint8_t typesize, size_t buf_size) B2ND_TEST_UNUSED; + +static bool fill_buf(void *buf, uint8_t typesize, size_t buf_size) { + switch (typesize) { + case 8: + for (size_t i = 0; i < buf_size; ++i) { + ((uint64_t *) buf)[i] = (uint64_t) i + 1; + } + break; + case 4: + for (size_t i = 0; i < buf_size; ++i) { + ((uint32_t *) buf)[i] = (uint32_t) i + 1; + } + break; + case 2: + for (size_t i = 0; i < buf_size; ++i) { + ((uint16_t *) buf)[i] = (uint16_t) i + 1; + } + break; + case 1: + for (size_t i = 0; i < buf_size; ++i) { + ((uint8_t *) buf)[i] = (uint8_t) i + 1; + } + break; + default: + return false; + } + return true; +} + + +/* Tests data */ + +typedef struct { + int8_t ndim; + int64_t shape[B2ND_MAX_DIM]; + int32_t chunkshape[B2ND_MAX_DIM]; + int32_t blockshape[B2ND_MAX_DIM]; +} _test_shapes; + + +typedef struct { + bool contiguous; + bool persistent; +} _test_backend; + + +void b2nd_default_parameters() { + CUTEST_PARAMETRIZE(typesize, uint8_t, CUTEST_DATA(1, 2, 4, 8)); + CUTEST_PARAMETRIZE(shapes, _test_shapes, CUTEST_DATA( + {2, {40, 40}, {20, 20}, {10, 10}}, + {3, {40, 55, 23}, {31, 5, 22}, {4, 4, 4}}, + {3, {40, 0, 12}, {31, 0, 12}, {10, 0, 12}}, + {4, {50, 60, 31, 12}, {25, 20, 20, 10}, {5, 5, 5, 10}}, + {5, {1, 1, 1024, 1, 1}, {1, 1, 500, 1, 1}, {1, 1, 200, 1, 1}}, + {6, {5, 1, 50, 3, 1, 2}, {5, 1, 50, 2, 1, 2}, {2, 1, 20, 2, 1, 2}}, + )); + CUTEST_PARAMETRIZE(backend, _test_backend, CUTEST_DATA( + {false, false}, + {true, false}, + {false, true}, + {true, true}, + )); +} + + +#define B2ND_TEST_ASSERT_BUFFER(buffer1, buffer2, buffersize) \ + for (int i = 0; i < (buffersize); ++i) { \ + CUTEST_ASSERT("elements are not equals!", (buffer1)[i] == (buffer2)[i]); \ + } + + +#endif //B2ND_TEST_COMMON_H diff --git a/tests/cutest.h b/tests/cutest.h index d610a3e4..1aaaff46 100644 --- a/tests/cutest.h +++ b/tests/cutest.h @@ -1,11 +1,12 @@ -/* - Copyright (C) 2021 Aleix Alcacer - Copyright (C) 2021 The Blosc Developers - http://blosc.org - License: BSD (see LICENSE.txt) +/********************************************************************* + Blosc - Blocked Shuffling and Compression Library + + Copyright (c) 2021 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. -*/ +**********************************************************************/ #ifndef CUTEST_CUTEST_H #define CUTEST_CUTEST_H @@ -174,11 +175,9 @@ int _cutest_run(int (*test)(void *), void *test_data, char *name) { snprintf(test_name, MAXLEN_TESTNAME, "%s%s[%" PRId8 "], ", aux, cutest_params[i].name, cutest_params_ind[i]); } - test_name[strlen(test_name) - 1] = 0; - strncpy(&test_name[strlen(test_name) - 1], ")", 1); - if (nparams == 0) { - test_name[strlen(test_name) - 1] = 0; - } + if (nparams > 0) + test_name[strlen(test_name) - 2] = ')'; + test_name[strlen(test_name) - 1] = '\0'; printf("%s ", test_name); cutest_total++; diff --git a/tests/fuzz/fuzz_compress_chunk.c b/tests/fuzz/fuzz_compress_chunk.c index c973ec9a..79877732 100644 --- a/tests/fuzz/fuzz_compress_chunk.c +++ b/tests/fuzz/fuzz_compress_chunk.c @@ -3,13 +3,15 @@ #include +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + #ifdef __cplusplus extern "C" { #endif -int LLVMFuzzerTestOneInput(const uint8_t *data, int32_t size) { - const char *compressors[] = { "blosclz", "lz4", "lz4hc", "zlib", "zstd" }; - int num_comp = sizeof(compressors) / sizeof(char*); +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + const char *const compressors[] = { "blosclz", "lz4", "lz4hc", "zlib", "zstd" }; + const int num_comp = ARRAY_SIZE(compressors); int level = 9, filter = BLOSC_BITSHUFFLE, cindex = 0, i = 0; size_t nbytes, cbytes, blocksize; void *output, *input; @@ -39,7 +41,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, int32_t size) { if (output == NULL) return 0; - if (blosc2_compress(level, filter, 1, data, size, output, size) == 0) { + int csize = blosc2_compress(level, filter, 1, data, size, output, size); + if (csize <= 0) { /* Cannot compress src buffer into dest */ free(output); return 0; diff --git a/tests/fuzz/fuzz_compress_frame.c b/tests/fuzz/fuzz_compress_frame.c index c74914bd..cab59b6a 100644 --- a/tests/fuzz/fuzz_compress_frame.c +++ b/tests/fuzz/fuzz_compress_frame.c @@ -5,12 +5,14 @@ #include +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + #ifdef __cplusplus extern "C" { #endif int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - const char *compressors[] = { "blosclz", "lz4", "lz4hc", "zlib", "zstd" }; + const char *const compressors[] = { "blosclz", "lz4", "lz4hc", "zlib", "zstd" }; int32_t i = 0, dsize = 0, filter = BLOSC_BITSHUFFLE; int32_t nchunk = 0, max_chunksize = 512; int64_t nchunks = 0; @@ -29,6 +31,10 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { blosc2_destroy(); return 0; } + if (size > INT32_MAX) { + printf("Conversion error: size_t does not fit in int32_t\n"); + return 0; + } if (size > 0) { /* Variable size compression level and max chunksize */ cparams.clevel = data[0] % (9 + 1); diff --git a/tests/fuzz/generate_inputs_corpus.c b/tests/fuzz/generate_inputs_corpus.c index f7ffb0cd..c9c8f135 100644 --- a/tests/fuzz/generate_inputs_corpus.c +++ b/tests/fuzz/generate_inputs_corpus.c @@ -1,6 +1,6 @@ /* - Copyright (C) 2021 The Blosc Developers - http://blosc.org + Copyright (c) 2021 The Blosc Development Team + https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Generate inputs for fuzzer corpus @@ -59,7 +59,6 @@ int create_cframe(const char* compname) { size_t isize = CHUNKSIZE * sizeof(int32_t); int64_t nbytes, cbytes; int i, nchunk; - int64_t nchunks; blosc_timestamp_t last, current; double ttotal; int compcode = blosc2_compname_to_compcode(compname); @@ -86,8 +85,7 @@ int create_cframe(const char* compname) { for (i = 0; i < CHUNKSIZE; i++) { data[i] = i * nchunk; } - nchunks = blosc2_schunk_append_buffer(schunk, data, (int32_t)isize); - assert(nchunks == nchunk + 1); + BLOSC_ERROR(blosc2_schunk_append_buffer(schunk, data, (int32_t)isize)); } blosc_set_timestamp(¤t); diff --git a/tests/fuzz/standalone.c b/tests/fuzz/standalone.c index c43738c6..b272113e 100644 --- a/tests/fuzz/standalone.c +++ b/tests/fuzz/standalone.c @@ -1,8 +1,9 @@ #include +#include #include #include -extern int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size); +extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); int main(int argc, char **argv) { int i; @@ -10,7 +11,7 @@ int main(int argc, char **argv) { for (i = 1; i < argc; i++) { size_t len, err, n_read = 0; - unsigned char *buf; + uint8_t *buf; FILE *f = NULL; f = fopen(argv[i], "rb+"); diff --git a/tests/gcc-segfault-issue.c b/tests/gcc-segfault-issue.c index fb46e61a..01e92031 100644 --- a/tests/gcc-segfault-issue.c +++ b/tests/gcc-segfault-issue.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/tests/print_versions.c b/tests/print_versions.c index 7d5d15ca..44841b4d 100644 --- a/tests/print_versions.c +++ b/tests/print_versions.c @@ -1,7 +1,7 @@ /********************************************************************* Print versions for Blosc and all its internal compressors. - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/tests/test_all.sh b/tests/test_all.sh index 28b66cd8..35be54e6 100644 --- a/tests/test_all.sh +++ b/tests/test_all.sh @@ -3,7 +3,9 @@ # # Unit tests for basic features in Blosc. # -# Author: The Blosc Developers +# Copyright (c) 2010 The Blosc Development Team +# https://blosc.org +# License: BSD 3-Clause (see LICENSE.txt) # # See LICENSE.txt for details about copyright and rights to use. #********************************************************************** diff --git a/tests/test_api.c b/tests/test_api.c index e5928843..20b2ffc4 100644 --- a/tests/test_api.c +++ b/tests/test_api.c @@ -3,7 +3,7 @@ Unit tests for Blosc API. - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/tests/test_bitshuffle_leftovers.c b/tests/test_bitshuffle_leftovers.c index e8b81735..fea0b60b 100644 --- a/tests/test_bitshuffle_leftovers.c +++ b/tests/test_bitshuffle_leftovers.c @@ -5,7 +5,7 @@ See https://github.com/Blosc/python-blosc/issues/220 Probably related: https://github.com/Blosc/c-blosc/issues/240 - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/tests/test_blosc1_compat.c b/tests/test_blosc1_compat.c index 03fe0338..234c755e 100644 --- a/tests/test_blosc1_compat.c +++ b/tests/test_blosc1_compat.c @@ -3,7 +3,7 @@ Unit tests for BLOSC_BLOSC1_COMPAT environment variable in Blosc. - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/tests/test_change_nthreads_append.c b/tests/test_change_nthreads_append.c index 33ec252a..f01923de 100644 --- a/tests/test_change_nthreads_append.c +++ b/tests/test_change_nthreads_append.c @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/tests/test_common.h b/tests/test_common.h index 062f5017..0b277025 100644 --- a/tests/test_common.h +++ b/tests/test_common.h @@ -3,8 +3,9 @@ Unit tests for basic features in Blosc. - Creation date: 2010-06-07 - Author: The Blosc Developers + Copyright (c) 2010 The Blosc Development Team + https://blosc.org + License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ @@ -12,20 +13,20 @@ #ifndef BLOSC_TEST_COMMON_H #define BLOSC_TEST_COMMON_H +#include "blosc2.h" + #include #include #include #include +#include #include #include #include #include -#include "blosc2.h" #if defined(_WIN32) && !defined(__MINGW32__) #include - #include "win32/stdint-windows.h" #else - #include #include #endif @@ -35,7 +36,7 @@ #endif -/* This is MinUnit in action (http://www.jera.com/techinfo/jtns/jtn002.html) */ +/* This is MinUnit in action (https://jera.com/techinfo/jtns/jtn002) */ #define mu_assert(message, test) do { if (!(test)) return message; } while (0) #define mu_run_test(test) do \ { char *message = test(); tests_run++; \ @@ -48,6 +49,8 @@ extern int tests_run; #define MB (1024*KB) #define GB (1024*MB) +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + /* Memory functions. */ diff --git a/tests/test_compress_roundtrip.c b/tests/test_compress_roundtrip.c index 371d1d38..2ee73c09 100644 --- a/tests/test_compress_roundtrip.c +++ b/tests/test_compress_roundtrip.c @@ -3,7 +3,7 @@ Roundtrip compression/decompression tests. - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/tests/test_compressor.c b/tests/test_compressor.c index d17d44f4..af279a8c 100644 --- a/tests/test_compressor.c +++ b/tests/test_compressor.c @@ -3,7 +3,7 @@ Unit tests for BLOSC_COMPRESSOR environment variable in Blosc. - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/tests/test_contexts.c b/tests/test_contexts.c index 257272da..23d572d3 100644 --- a/tests/test_contexts.c +++ b/tests/test_contexts.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) */ @@ -12,6 +12,8 @@ int main(void) { + blosc2_init(); + int32_t *data = malloc(SIZE * sizeof(int32_t)); int32_t *data_out = malloc(SIZE * sizeof(int32_t)); int32_t *data_dest = malloc(SIZE * sizeof(int32_t)); @@ -133,5 +135,7 @@ int main(void) { free(data_out); free(data_dest); + blosc2_destroy(); + return EXIT_SUCCESS; } diff --git a/tests/test_copy.c b/tests/test_copy.c index 6daaed41..1aeef31c 100644 --- a/tests/test_copy.c +++ b/tests/test_copy.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -33,7 +33,7 @@ enum { typedef struct { bool contiguous; char *urlpath; -}test_copy_backend; +} test_copy_backend; CUTEST_TEST_DATA(copy) { diff --git a/tests/test_delete_chunk.c b/tests/test_delete_chunk.c index 67b6f0fd..437e3815 100644 --- a/tests/test_delete_chunk.c +++ b/tests/test_delete_chunk.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) */ @@ -41,7 +41,7 @@ test_ndata tndata[] = { typedef struct { bool contiguous; char *urlpath; -}test_storage; +} test_storage; test_storage tstorage[] = { {false, NULL}, // memory - schunk @@ -144,8 +144,8 @@ static char* test_delete_chunk(void) { static char *all_tests(void) { - for (int i = 0; i < (int) (sizeof(tstorage) / sizeof(test_storage)); ++i) { - for (int j = 0; j < (int) (sizeof(tndata) / sizeof(test_ndata)); ++j) { + for (int i = 0; i < (int) ARRAY_SIZE(tstorage); ++i) { + for (int j = 0; j < (int) ARRAY_SIZE(tndata); ++j) { tdata.contiguous = tstorage[i].contiguous; tdata.urlpath = tstorage[i].urlpath; diff --git a/tests/test_delta.c b/tests/test_delta.c index 6eca069e..aa910dbc 100644 --- a/tests/test_delta.c +++ b/tests/test_delta.c @@ -3,7 +3,7 @@ Unit tests for BLOSC_COMPRESSOR environment variable in Blosc. - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/tests/test_delta_schunk.c b/tests/test_delta_schunk.c index a33320c2..b5fabd1e 100644 --- a/tests/test_delta_schunk.c +++ b/tests/test_delta_schunk.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) */ diff --git a/tests/test_dict_schunk.c b/tests/test_dict_schunk.c index 8b0d418c..60c29f27 100644 --- a/tests/test_dict_schunk.c +++ b/tests/test_dict_schunk.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) */ diff --git a/tests/test_empty_buffer.c b/tests/test_empty_buffer.c index e8477a24..fc114e64 100644 --- a/tests/test_empty_buffer.c +++ b/tests/test_empty_buffer.c @@ -3,7 +3,7 @@ Unit tests for the blosc1_decompress() function. - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/tests/test_fill_special.c b/tests/test_fill_special.c index 0164cbfb..a8bd8a15 100644 --- a/tests/test_fill_special.c +++ b/tests/test_fill_special.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -27,7 +27,7 @@ enum { typedef struct { bool contiguous; char *urlpath; -}test_fill_special_backend; +} test_fill_special_backend; CUTEST_TEST_DATA(fill_special) { blosc2_cparams cparams; diff --git a/tests/test_frame.c b/tests/test_frame.c index 8bae4774..75ecec0c 100644 --- a/tests/test_frame.c +++ b/tests/test_frame.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) */ @@ -274,7 +274,7 @@ static char* test_frame(void) { static char *all_tests(void) { // Iterate over all different parameters - for (int i = 0; i < (int)sizeof(nchunks_) / (int)sizeof(int); i++) { + for (int i = 0; i < (int) ARRAY_SIZE(nchunks_); i++) { nchunks = nchunks_[i]; for (int isplits = 0; isplits < 2; isplits++) { for (int imultithread = 0; imultithread < 2; imultithread++) { @@ -283,7 +283,7 @@ static char *all_tests(void) { for (int ifilter_pipeline = 0; ifilter_pipeline < 2; ifilter_pipeline++) { for (int imetalayers = 0; imetalayers < 2; imetalayers++) { for (int ivlmetalayers = 0; ivlmetalayers < 2; ivlmetalayers++) { - for (int iblocksize = 0; iblocksize < (int) (sizeof(blocksize_) / sizeof(int32_t)); ++iblocksize) { + for (int iblocksize = 0; iblocksize < (int) ARRAY_SIZE(blocksize_); ++iblocksize) { blocksize = blocksize_[iblocksize]; splits = (bool) isplits; multithread = (bool) imultithread; diff --git a/tests/test_frame_get_offsets.c b/tests/test_frame_get_offsets.c index 67c21359..8f1b76b6 100644 --- a/tests/test_frame_get_offsets.c +++ b/tests/test_frame_get_offsets.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) */ @@ -22,7 +22,7 @@ enum { typedef struct { bool contiguous; char *urlpath; -}test_fill_special_backend; +} test_fill_special_backend; CUTEST_TEST_DATA(fill_special) { blosc2_cparams cparams; diff --git a/tests/test_frame_offset.c b/tests/test_frame_offset.c index 474a5c8f..a6ae394d 100644 --- a/tests/test_frame_offset.c +++ b/tests/test_frame_offset.c @@ -1,16 +1,15 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) */ #include -#include -#include #include "blosc2.h" #include "cutest.h" + #define KB 1024. #define MB (1024*KB) #define GB (1024*MB) @@ -26,7 +25,7 @@ enum { typedef struct { bool contiguous; char *urlpath; -}test_fill_special_backend; +} test_fill_special_backend; CUTEST_TEST_DATA(fill_special) { blosc2_cparams cparams; @@ -56,7 +55,6 @@ CUTEST_TEST_SETUP(fill_special) { data->schunk_write_append = blosc2_schunk_new(&storage); int32_t isize = CHUNKSIZE * sizeof(int32_t); int i, nchunk; - int64_t nchunks; // Add some data for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { @@ -64,8 +62,7 @@ CUTEST_TEST_SETUP(fill_special) { data->data1[i] = i * nchunk; data->data2[i] = 2 * i * nchunk; } - nchunks = blosc2_schunk_append_buffer(data->schunk_write_start, data->data1, isize); - assert(nchunks == nchunk + 1); + blosc2_schunk_append_buffer(data->schunk_write_start, data->data1, isize); blosc2_schunk_append_buffer(data->schunk_write_append, data->data2, isize); } } @@ -131,8 +128,8 @@ CUTEST_TEST_TEST(fill_special) { blosc2_schunk* schunk_read_offset = blosc2_schunk_open_offset("file:///frame_simple.b2frame", offset); blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); - printf("Time for fileframe (%s) + offset %lld -> frame_offset : %.3g s, %.1f GB/s\n", - schunk_read_offset->storage->urlpath, offset, ttotal, (double)schunk_read_offset->nbytes / (ttotal * GB)); + printf("Time for fileframe (%s) + offset %ld -> frame_offset : %.3g s, %.1f GB/s\n", + schunk_read_offset->storage->urlpath, (long)offset, ttotal, (double)schunk_read_offset->nbytes / (ttotal * GB)); uint8_t* cframe_read_start, *cframe_read_offset; bool cframe_needs_free2, cframe_needs_free3; diff --git a/tests/test_get_slice_buffer.c b/tests/test_get_slice_buffer.c index 48b69e99..6ca79c5d 100644 --- a/tests/test_get_slice_buffer.c +++ b/tests/test_get_slice_buffer.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -46,7 +46,7 @@ test_ndata tndata[] = { typedef struct { bool contiguous; char *urlpath; -}test_storage; +} test_storage; test_storage tstorage[] = { {false, NULL}, // memory - schunk @@ -100,6 +100,7 @@ static char* test_get_slice_buffer(void) { } int64_t nchunks_ = blosc2_schunk_append_buffer(schunk, data_ + tdata.nchunks * CHUNKSIZE, (tdata.stop % CHUNKSIZE) * sizeof(int32_t)); + mu_assert("ERROR: bad append in frame", nchunks_ > 0); } // Get slice @@ -134,8 +135,8 @@ static char* test_get_slice_buffer(void) { } static char *all_tests(void) { - for (int i = 0; i < (int) (sizeof(tstorage) / sizeof(test_storage)); ++i) { - for (int j = 0; j < (int) (sizeof(tndata) / sizeof(test_ndata)); ++j) { + for (int i = 0; i < (int) ARRAY_SIZE(tstorage); ++i) { + for (int j = 0; j < (int) ARRAY_SIZE(tndata); ++j) { tdata.contiguous = tstorage[i].contiguous; tdata.urlpath = tstorage[i].urlpath; tdata.nchunks = tndata[j].nchunks; diff --git a/tests/test_getitem.c b/tests/test_getitem.c index 89649ed8..5c263cff 100644 --- a/tests/test_getitem.c +++ b/tests/test_getitem.c @@ -3,7 +3,7 @@ Unit tests for the blosc1_getitem() function. - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/tests/test_getitem_delta.c b/tests/test_getitem_delta.c index 8546697b..40878c8e 100644 --- a/tests/test_getitem_delta.c +++ b/tests/test_getitem_delta.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/tests/test_insert_chunk.c b/tests/test_insert_chunk.c index 65093d73..030bf15f 100644 --- a/tests/test_insert_chunk.c +++ b/tests/test_insert_chunk.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -45,7 +45,7 @@ test_ndata tndata[] = { typedef struct { bool contiguous; char *urlpath; -}test_storage; +} test_storage; test_storage tstorage[] = { {false, NULL}, // memory - schunk @@ -170,9 +170,9 @@ static char* test_insert_chunk(void) { static char *all_tests(void) { - for (int i = 0; i < (int) (sizeof(tstorage) / sizeof(test_storage)); ++i) { - for (int j = 0; j < (int) (sizeof(tndata) / sizeof(test_ndata)); ++j) { - for (int k = 0; k < (int) (sizeof(tcopy) / sizeof(bool)); ++k) { + for (int i = 0; i < (int) ARRAY_SIZE(tstorage); ++i) { + for (int j = 0; j < (int) ARRAY_SIZE(tndata); ++j) { + for (int k = 0; k < (int) ARRAY_SIZE(tcopy); ++k) { tdata.contiguous = tstorage[i].contiguous; tdata.urlpath = tstorage[i].urlpath; diff --git a/tests/test_lazychunk.c b/tests/test_lazychunk.c index 6f16bd9f..af7d6f73 100644 --- a/tests/test_lazychunk.c +++ b/tests/test_lazychunk.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/tests/test_lazychunk_memcpyed.c b/tests/test_lazychunk_memcpyed.c index 3c3c25bd..d409bb53 100644 --- a/tests/test_lazychunk_memcpyed.c +++ b/tests/test_lazychunk_memcpyed.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -52,8 +52,62 @@ static char* test_lazy_chunk_memcpyed(void) { return 0; } +static char* test_lazy_chunk_memcpyed_nofilter(void) { + int chunk_nitems = 2000; + int blocksize = 2000 - sizeof(int32_t) * 4; // make it not a multiple of chunk_nitems + int cbytes, nbytes; + + blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; + cparams.filters[BLOSC_LAST_FILTER] = BLOSC_NOFILTER; + cparams.blocksize = blocksize; + cparams.typesize = sizeof(int32_t); + + blosc2_storage storage = {.urlpath="memcpyed_nofilter.b2frame", .contiguous=false, .cparams=&cparams}; + blosc2_remove_dir(storage.urlpath); + + blosc2_schunk *sc = blosc2_schunk_new(&storage); + + int32_t chunk_size = chunk_nitems * sc->typesize; + int32_t *buffer_b = malloc(chunk_size); + for (int i = 0; i < chunk_nitems; i++) { + buffer_b[i] = i; + } + + int dest_size = chunk_size + BLOSC2_MAX_OVERHEAD; + uint8_t *chunk = malloc(dest_size); + cbytes = blosc2_compress_ctx(sc->cctx, buffer_b, chunk_size, chunk, dest_size); + mu_assert("ERROR: cbytes are incorrect", cbytes == dest_size); // incompressible data + + nbytes = blosc2_decompress_ctx(sc->dctx, chunk, dest_size, buffer_b, chunk_size); + mu_assert("ERROR: nbytes are incorrect", nbytes == chunk_size); + + blosc2_schunk_append_chunk(sc, chunk, false); + + uint8_t *chunk2; + bool needs_free; + int32_t chunk2_size = blosc2_schunk_get_chunk(sc, 0, &chunk2, &needs_free); + nbytes = blosc2_decompress_ctx(sc->dctx, chunk2, chunk2_size, buffer_b, chunk_size); + mu_assert("ERROR: nbytes are incorrect", nbytes == chunk_size); + if (needs_free) { + free(chunk2); + } + + nbytes = blosc2_schunk_decompress_chunk(sc, 0, buffer_b, chunk_size); + mu_assert("ERROR: nbytes are incorrect", nbytes == chunk_size); + + // Retrieve the last item using the lazy_chunk mechanism + int32_t last_item; + blosc2_schunk_get_slice_buffer(sc, chunk_nitems - 1, chunk_nitems, &last_item); + mu_assert("ERROR: last value is incorrect", last_item == chunk_nitems - 1); + + blosc2_remove_dir(storage.urlpath); + + return 0; +} + static char *all_tests(void) { mu_run_test(test_lazy_chunk_memcpyed); + mu_run_test(test_lazy_chunk_memcpyed_nofilter); return EXIT_SUCCESS; } diff --git a/tests/test_maskout.c b/tests/test_maskout.c index b2460f88..c30f31fd 100644 --- a/tests/test_maskout.c +++ b/tests/test_maskout.c @@ -3,7 +3,7 @@ Unit tests for BLOSC_COMPRESSOR environment variable in Blosc. - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -162,6 +162,8 @@ static char *all_tests(void) { #define BUFFER_ALIGN_SIZE 32 int main(void) { + blosc2_init(); + char *result; nblocks = bytesize / blocksize; @@ -230,5 +232,6 @@ int main(void) { blosc_test_free(maskout); blosc_test_free(maskout2); + blosc2_destroy(); return result != 0; } diff --git a/tests/test_maxout.c b/tests/test_maxout.c index c7f7e9b8..7a736b08 100644 --- a/tests/test_maxout.c +++ b/tests/test_maxout.c @@ -3,7 +3,7 @@ Unit tests for basic features in Blosc. - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -29,7 +29,7 @@ static char* test_input_too_large(void) { /* Get a compressed buffer */ cbytes = blosc1_compress(clevel, doshuffle, typesize, BLOSC2_MAX_BUFFERSIZE + 1, src, dest, size + BLOSC2_MAX_OVERHEAD - 1); - mu_assert("ERROR: cbytes is not == 0", cbytes == 0); + mu_assert("ERROR: cbytes is not == 0", cbytes < 0); return 0; } @@ -124,18 +124,18 @@ static char* test_max_overhead(void) { blosc2_init(); cbytes = blosc1_compress(0, doshuffle, typesize, size, src, dest, BLOSC2_MAX_OVERHEAD - 1); - mu_assert("ERROR: cbytes is not correct", cbytes == 0); + mu_assert("ERROR: cbytes is not correct", cbytes < 0); blosc2_destroy(); blosc2_init(); cbytes = blosc1_compress(0, doshuffle, typesize, size, src, dest, BLOSC2_MAX_OVERHEAD - 2); - mu_assert("ERROR: cbytes is not correct", cbytes == 0); + mu_assert("ERROR: cbytes is not correct", cbytes < 0); blosc2_destroy(); blosc2_init(); cbytes = blosc1_compress(0, doshuffle, typesize, size, src, dest, 0); - mu_assert("ERROR: cbytes is not correct", cbytes == 0); + mu_assert("ERROR: cbytes is not correct", cbytes < 0); blosc2_destroy(); return 0; diff --git a/tests/test_noinit.c b/tests/test_noinit.c index 04ccdd8f..ff6d2833 100644 --- a/tests/test_noinit.c +++ b/tests/test_noinit.c @@ -1,7 +1,7 @@ /********************************************************************* Blosc - Blocked Shuffling and Compression Library - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/tests/test_nolock.c b/tests/test_nolock.c index 8ac6bf66..f329f0d0 100644 --- a/tests/test_nolock.c +++ b/tests/test_nolock.c @@ -3,7 +3,7 @@ Unit tests for BLOSC_NOLOCK environment variable in Blosc. - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/tests/test_nthreads.c b/tests/test_nthreads.c index 362dd028..03c06e3f 100644 --- a/tests/test_nthreads.c +++ b/tests/test_nthreads.c @@ -3,7 +3,7 @@ Unit tests for BLOSC_NTHREADS environment variable in Blosc. - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/tests/test_postfilter.c b/tests/test_postfilter.c index 48001b73..7d0090fb 100644 --- a/tests/test_postfilter.c +++ b/tests/test_postfilter.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) */ @@ -290,6 +290,8 @@ static char *all_tests(void) { int main(void) { + blosc2_init(); + install_blosc_callback_test(); /* optionally install callback test */ /* Create a context for compression */ @@ -308,5 +310,7 @@ int main(void) { } printf("\tTests run: %d\n", tests_run); + blosc2_destroy(); + return result != 0; } diff --git a/tests/test_prefilter.c b/tests/test_prefilter.c index a60bc905..9cfc36f0 100644 --- a/tests/test_prefilter.c +++ b/tests/test_prefilter.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) */ @@ -213,6 +213,8 @@ static char *all_tests(void) { int main(void) { + blosc2_init(); + /* Initialize inputs */ for (int i = 0; i < SIZE; i++) { data[i] = i; @@ -238,5 +240,6 @@ int main(void) { } printf("\tTests run: %d\n", tests_run); + blosc2_destroy(); return result != 0; } diff --git a/tests/test_reorder_offsets.c b/tests/test_reorder_offsets.c index 435aa49c..1ca1a9e8 100644 --- a/tests/test_reorder_offsets.c +++ b/tests/test_reorder_offsets.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -28,7 +28,7 @@ test_data tdata; typedef struct { bool contiguous; char *urlpath; -}test_storage; +} test_storage; test_storage tstorage[] = { {false, NULL}, // memory - schunk @@ -102,8 +102,8 @@ static char* test_reorder_offsets(void) { static char *all_tests(void) { - for (int i = 0; i < (int) (sizeof(tstorage) / sizeof(test_storage)); ++i) { - for (int j = 0; j < (int) (sizeof(tnchunks) / sizeof(int32_t)); ++j) { + for (int i = 0; i < (int) ARRAY_SIZE(tstorage); ++i) { + for (int j = 0; j < (int) ARRAY_SIZE(tnchunks); ++j) { tdata.contiguous = tstorage[i].contiguous; tdata.urlpath = tstorage[i].urlpath; diff --git a/tests/test_schunk.c b/tests/test_schunk.c index 6612572f..b5ed8612 100644 --- a/tests/test_schunk.c +++ b/tests/test_schunk.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/tests/test_schunk_frame.c b/tests/test_schunk_frame.c index 4c8dfc2a..caf917ad 100644 --- a/tests/test_schunk_frame.c +++ b/tests/test_schunk_frame.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/tests/test_schunk_header.c b/tests/test_schunk_header.c index 3d6785e0..72c2a685 100644 --- a/tests/test_schunk_header.c +++ b/tests/test_schunk_header.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/tests/test_set_slice_buffer.c b/tests/test_set_slice_buffer.c index 174fa061..161b42f3 100644 --- a/tests/test_set_slice_buffer.c +++ b/tests/test_set_slice_buffer.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -45,7 +45,7 @@ test_ndata tndata[] = { typedef struct { bool contiguous; char *urlpath; -}test_storage; +} test_storage; test_storage tstorage[] = { {false, NULL}, // memory - schunk @@ -97,6 +97,7 @@ static char* test_set_slice_buffer(void) { } int64_t nchunks_ = blosc2_schunk_append_buffer(schunk, data_ + tdata.nchunks * CHUNKSIZE, (tdata.stop % CHUNKSIZE) * sizeof(int32_t)); + mu_assert("ERROR: bad append in frame", nchunks_ > 0); free(data_); } @@ -128,8 +129,8 @@ static char* test_set_slice_buffer(void) { } static char *all_tests(void) { - for (int i = 0; i < (int) (sizeof(tstorage) / sizeof(test_storage)); ++i) { - for (int j = 0; j < (int) (sizeof(tndata) / sizeof(test_ndata)); ++j) { + for (int i = 0; i < (int) ARRAY_SIZE(tstorage); ++i) { + for (int j = 0; j < (int) ARRAY_SIZE(tndata); ++j) { tdata.contiguous = tstorage[i].contiguous; tdata.urlpath = tstorage[i].urlpath; tdata.nchunks = tndata[j].nchunks; diff --git a/tests/test_sframe.c b/tests/test_sframe.c index 9bddb549..472fdd75 100644 --- a/tests/test_sframe.c +++ b/tests/test_sframe.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/tests/test_sframe_lazychunk.c b/tests/test_sframe_lazychunk.c index 17490e70..ac2bedcd 100644 --- a/tests/test_sframe_lazychunk.c +++ b/tests/test_sframe_lazychunk.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/tests/test_shuffle_roundtrip_altivec.c b/tests/test_shuffle_roundtrip_altivec.c index 4742c7a4..6219e2c1 100644 --- a/tests/test_shuffle_roundtrip_altivec.c +++ b/tests/test_shuffle_roundtrip_altivec.c @@ -3,7 +3,7 @@ Roundtrip tests for the ALTIVEC-accelerated shuffle/unshuffle. - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/tests/test_shuffle_roundtrip_avx2.c b/tests/test_shuffle_roundtrip_avx2.c index ddaf5667..6e20ba20 100644 --- a/tests/test_shuffle_roundtrip_avx2.c +++ b/tests/test_shuffle_roundtrip_avx2.c @@ -3,7 +3,7 @@ Roundtrip tests for the AVX2-accelerated shuffle/unshuffle. - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/tests/test_shuffle_roundtrip_generic.c b/tests/test_shuffle_roundtrip_generic.c index 3a29a865..db0f79e2 100644 --- a/tests/test_shuffle_roundtrip_generic.c +++ b/tests/test_shuffle_roundtrip_generic.c @@ -3,7 +3,7 @@ Roundtrip tests - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/tests/test_shuffle_roundtrip_neon.c b/tests/test_shuffle_roundtrip_neon.c index f57e1c97..fea5c970 100644 --- a/tests/test_shuffle_roundtrip_neon.c +++ b/tests/test_shuffle_roundtrip_neon.c @@ -3,7 +3,7 @@ Roundtrip tests for the NEON-accelerated shuffle/unshuffle. - Copyright (C) 2021 Lucian Marc + Copyright (c) 2021 Lucian Marc https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/tests/test_shuffle_roundtrip_sse2.c b/tests/test_shuffle_roundtrip_sse2.c index 52591b08..010bc41b 100644 --- a/tests/test_shuffle_roundtrip_sse2.c +++ b/tests/test_shuffle_roundtrip_sse2.c @@ -3,7 +3,7 @@ Roundtrip tests for the SSE2-accelerated shuffle/unshuffle. - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) diff --git a/tests/test_small_chunks.c b/tests/test_small_chunks.c index b065278b..9e0a361d 100644 --- a/tests/test_small_chunks.c +++ b/tests/test_small_chunks.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) */ @@ -31,8 +31,8 @@ CUTEST_TEST_TEST(small_chunks) { cparams.blocksize = blocksize; blosc2_storage storage = BLOSC2_STORAGE_DEFAULTS; storage.cparams = &cparams; - storage.urlpath = "ex_update.caterva"; - blosc2_remove_dir("ex_update.caterva"); + storage.urlpath = "ex_update.b2nd"; + blosc2_remove_dir("ex_update.b2nd"); storage.contiguous = false; blosc2_schunk *sc = blosc2_schunk_new(&storage); @@ -61,7 +61,7 @@ CUTEST_TEST_TEST(small_chunks) { blosc2_schunk_free(sc); - blosc2_remove_dir("ex_update.caterva"); + blosc2_remove_dir("ex_update.b2nd"); blosc2_destroy(); diff --git a/tests/test_udio.c b/tests/test_udio.c index d854f26d..c74bacbf 100644 --- a/tests/test_udio.c +++ b/tests/test_udio.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) */ @@ -79,7 +79,7 @@ int test_truncate(void *stream, int64_t size) { typedef struct { bool contiguous; char *urlpath; -}test_udio_backend; +} test_udio_backend; CUTEST_TEST_DATA(udio) { blosc2_cparams cparams; diff --git a/tests/test_update_chunk.c b/tests/test_update_chunk.c index 40bfb1b3..0355deb9 100644 --- a/tests/test_update_chunk.c +++ b/tests/test_update_chunk.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -41,7 +41,7 @@ test_ndata tndata[] = { typedef struct { bool contiguous; char *urlpath; -}test_storage; +} test_storage; test_storage tstorage[] = { {false, NULL}, // memory - schunk @@ -150,8 +150,8 @@ static char* test_update_chunk(void) { } static char *all_tests(void) { - for (int i = 0; i < (int) (sizeof(tstorage) / sizeof(test_storage)); ++i) { - for (int j = 0; j < (int) (sizeof(tndata) / sizeof(test_ndata)); ++j) { + for (int i = 0; i < (int) ARRAY_SIZE(tstorage); ++i) { + for (int j = 0; j < (int) ARRAY_SIZE(tndata); ++j) { tdata.contiguous = tstorage[i].contiguous; tdata.urlpath = tstorage[i].urlpath; tdata.nchunks = tndata[j].nchunks; @@ -169,8 +169,8 @@ static char *all_tests(void) { int main(void) { char *result; - install_blosc_callback_test(); /* optionally install callback test */ blosc2_init(); + install_blosc_callback_test(); /* optionally install callback test */ data = blosc_test_malloc(BUFFER_ALIGN_SIZE, CHUNKSIZE * sizeof(int64_t)); data_dest = blosc_test_malloc(BUFFER_ALIGN_SIZE, CHUNKSIZE * sizeof(int64_t)); diff --git a/tests/test_urcodecs.c b/tests/test_urcodecs.c index 33761783..aad2a620 100644 --- a/tests/test_urcodecs.c +++ b/tests/test_urcodecs.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) */ @@ -30,7 +30,7 @@ int codec_encoder(const uint8_t* input, int32_t input_len, return -1; } if (cparams->typesize != 4) { - BLOSC_TRACE_ERROR("Itemsize %d != 4", cparams->typesize); + fprintf(stderr, "Itemsize %d != 4", cparams->typesize); return BLOSC2_ERROR_FAILURE; } uint8_t *content; @@ -54,7 +54,7 @@ int codec_encoder(const uint8_t* input, int32_t input_len, int32_t step = in_[1] - start; for (int i = 1; i < nelem - 1; ++i) { if (in_[i + 1] - in_[i] != step) { - BLOSC_TRACE_ERROR("Buffer is not an arange"); + fprintf(stderr, "Buffer is not an arange"); return BLOSC2_ERROR_FAILURE; } } @@ -162,7 +162,7 @@ CUTEST_TEST_TEST(urcodecs) { blosc2_codec udcodec; udcodec.compname = "arange"; - udcodec.compver = 1; + udcodec.version = 1; udcodec.encoder = codec_encoder; if (correct_backward) { udcodec.compcode = 250; @@ -175,7 +175,7 @@ CUTEST_TEST_TEST(urcodecs) { } int rc = blosc2_register_codec(&udcodec); if (rc != 0) { - BLOSC_TRACE_ERROR("Error registering the code."); + fprintf(stderr, "Error registering the code."); return BLOSC2_ERROR_FAILURE; } @@ -211,7 +211,7 @@ CUTEST_TEST_TEST(urcodecs) { } int64_t nchunks_ = blosc2_schunk_append_buffer(schunk, bdata, isize); if (nchunks_ != nchunk + 1) { - BLOSC_TRACE_ERROR("Unexpected nchunks!"); + fprintf(stderr, "Unexpected nchunks!"); return -1; } } @@ -223,7 +223,7 @@ CUTEST_TEST_TEST(urcodecs) { for (nchunk = NCHUNKS-1; nchunk >= 0; nchunk--) { dsize = blosc2_schunk_decompress_chunk(schunk, nchunk, bdata_dest, isize); if (dsize < 0) { - BLOSC_TRACE_ERROR("Decompression error. Error code: %d\n", dsize); + fprintf(stderr, "Decompression error. Error code: %d\n", dsize); return dsize; } } @@ -237,11 +237,11 @@ CUTEST_TEST_TEST(urcodecs) { } if (!equals && correct_backward) { - BLOSC_TRACE_ERROR("Decompressed bdata differs from original!\n"); + fprintf(stderr, "Decompressed bdata differs from original!\n"); return -1; } if (equals && !correct_backward) { - BLOSC_TRACE_ERROR("Decompressed bdata is equal than original!\n"); + fprintf(stderr, "Decompressed bdata is equal than original!\n"); return -1; } } diff --git a/tests/test_urfilters.c b/tests/test_urfilters.c index 06b91640..01bad57a 100644 --- a/tests/test_urfilters.c +++ b/tests/test_urfilters.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) */ @@ -42,7 +42,7 @@ int filter_forward(const uint8_t* src, uint8_t* dest, int32_t size, uint8_t meta ((int16_t *) dest)[i] = (int16_t) (((int16_t *) src)[i] + 1); break; default: - BLOSC_TRACE_ERROR("Item size %d not supported", schunk->typesize); + fprintf(stderr, "Item size %d not supported", schunk->typesize); return BLOSC2_ERROR_FAILURE; } } @@ -71,7 +71,7 @@ int filter_backward(const uint8_t* src, uint8_t* dest, int32_t size, uint8_t met ((int16_t *) dest)[i] = (int16_t)(((int16_t *) src)[i] - 1); break; default: - BLOSC_TRACE_ERROR("Item size %d not supported", schunk->typesize); + fprintf(stderr, "Item size %d not supported", schunk->typesize); return BLOSC2_ERROR_FAILURE; } } @@ -101,7 +101,7 @@ int filter_backward_error(const uint8_t* src, uint8_t* dest, int32_t size, uint8 ((int16_t *) dest)[i] = (int16_t)(((int16_t *) src)[i] - 13); break; default: - BLOSC_TRACE_ERROR("Item size %d not supported", schunk->typesize); + fprintf(stderr, "Item size %d not supported", schunk->typesize); return BLOSC2_ERROR_FAILURE; } } @@ -152,13 +152,16 @@ CUTEST_TEST_TEST(urfilters) { int dsize; blosc2_filter urfilter; - urfilter.forward = filter_forward; + urfilter.version = 1; + urfilter.forward = filter_forward; if (correct_backward) { - urfilter.backward = filter_backward; - urfilter.id = 244; + urfilter.backward = filter_backward; + urfilter.id = 244; + urfilter.name = "test_urfilter244"; } else { - urfilter.backward = filter_backward_error; - urfilter.id = 245; + urfilter.backward = filter_backward_error; + urfilter.id = 245; + urfilter.name = "test_urfilter245"; } blosc2_register_filter(&urfilter); @@ -191,13 +194,13 @@ CUTEST_TEST_TEST(urfilters) { ((int16_t *) bdata)[i] = (int16_t)(i * nchunk); break; default: - BLOSC_TRACE_ERROR("Itemsize %d not supported\n", itemsize); + fprintf(stderr, "Itemsize %d not supported\n", itemsize); return -1; } } int64_t nchunks_ = blosc2_schunk_append_buffer(schunk, bdata, isize); if (nchunks_ != nchunk + 1) { - BLOSC_TRACE_ERROR("Unexpected nchunks!"); + fprintf(stderr, "Unexpected nchunks!"); return -1; } } @@ -206,7 +209,7 @@ CUTEST_TEST_TEST(urfilters) { for (nchunk = NCHUNKS-1; nchunk >= 0; nchunk--) { dsize = blosc2_schunk_decompress_chunk(schunk, nchunk, bdata_dest, isize); if (dsize < 0) { - BLOSC_TRACE_ERROR("Decompression error. Error code: %d\n", dsize); + fprintf(stderr, "Decompression error. Error code: %d\n", dsize); return dsize; } } @@ -232,15 +235,15 @@ CUTEST_TEST_TEST(urfilters) { } break; default: - BLOSC_TRACE_ERROR("Itemsize %d not supported\n", itemsize); + fprintf(stderr, "Itemsize %d not supported\n", itemsize); return -1; } if (!equals && correct_backward) { - BLOSC_TRACE_ERROR("Decompressed bdata differs from original!\n"); + fprintf(stderr, "Decompressed bdata differs from original!\n"); return -1; } if (equals && !correct_backward) { - BLOSC_TRACE_ERROR("Decompressed bdata is equal than original!\n"); + fprintf(stderr, "Decompressed bdata is equal than original!\n"); return -1; } } diff --git a/tests/test_zero_runlen.c b/tests/test_zero_runlen.c index 39d5c784..511dcfaf 100644 --- a/tests/test_zero_runlen.c +++ b/tests/test_zero_runlen.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 The Blosc Developers + Copyright (c) 2021 The Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) @@ -35,7 +35,7 @@ enum { typedef struct { bool contiguous; char *urlpath; -}test_zero_runlen_backend; +} test_zero_runlen_backend; CUTEST_TEST_DATA(zero_runlen) { blosc2_cparams cparams;

G*mf!!BmIK#p^=dZ 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