zstd 0.1.1.2 → 0.1.2.0
raw patch · 67 files changed
+9709/−6225 lines, 67 files
Files
- CONTRIBUTING.md +2/−17
- ChangeLog +3/−0
- README.md +11/−6
- zstd.cabal +4/−1
- zstd/CONTRIBUTING.md +351/−1
- zstd/README.md +33/−10
- zstd/TESTING.md +2/−2
- zstd/lib/README.md +45/−14
- zstd/lib/common/bitstream.h +38/−39
- zstd/lib/common/compiler.h +40/−5
- zstd/lib/common/cpu.h +1/−1
- zstd/lib/common/debug.c +11/−31
- zstd/lib/common/debug.h +11/−31
- zstd/lib/common/entropy_common.c +13/−33
- zstd/lib/common/error_private.c +2/−1
- zstd/lib/common/error_private.h +6/−2
- zstd/lib/common/fse.h +12/−32
- zstd/lib/common/fse_decompress.c +12/−35
- zstd/lib/common/huf.h +15/−33
- zstd/lib/common/mem.h +75/−2
- zstd/lib/common/pool.c +8/−4
- zstd/lib/common/pool.h +2/−2
- zstd/lib/common/threading.c +50/−4
- zstd/lib/common/threading.h +36/−4
- zstd/lib/common/xxhash.c +23/−35
- zstd/lib/common/xxhash.h +11/−31
- zstd/lib/common/zstd_common.c +1/−1
- zstd/lib/common/zstd_errors.h +2/−1
- zstd/lib/common/zstd_internal.h +154/−26
- zstd/lib/compress/fse_compress.c +17/−40
- zstd/lib/compress/hist.c +15/−35
- zstd/lib/compress/hist.h +12/−32
- zstd/lib/compress/huf_compress.c +92/−92
- zstd/lib/compress/zstd_compress.c +4276/−4415
- zstd/lib/compress/zstd_compress_internal.h +317/−55
- zstd/lib/compress/zstd_compress_literals.c +158/−0
- zstd/lib/compress/zstd_compress_literals.h +29/−0
- zstd/lib/compress/zstd_compress_sequences.c +419/−0
- zstd/lib/compress/zstd_compress_sequences.h +54/−0
- zstd/lib/compress/zstd_compress_superblock.c +845/−0
- zstd/lib/compress/zstd_compress_superblock.h +32/−0
- zstd/lib/compress/zstd_cwksp.h +525/−0
- zstd/lib/compress/zstd_double_fast.c +65/−43
- zstd/lib/compress/zstd_double_fast.h +2/−2
- zstd/lib/compress/zstd_fast.c +92/−66
- zstd/lib/compress/zstd_fast.h +2/−2
- zstd/lib/compress/zstd_lazy.c +74/−42
- zstd/lib/compress/zstd_lazy.h +1/−1
- zstd/lib/compress/zstd_ldm.c +32/−10
- zstd/lib/compress/zstd_ldm.h +7/−2
- zstd/lib/compress/zstd_opt.c +81/−114
- zstd/lib/compress/zstd_opt.h +1/−1
- zstd/lib/compress/zstdmt_compress.c +95/−51
- zstd/lib/compress/zstdmt_compress.h +3/−2
- zstd/lib/decompress/huf_decompress.c +76/−60
- zstd/lib/decompress/zstd_ddict.c +12/−8
- zstd/lib/decompress/zstd_ddict.h +2/−2
- zstd/lib/decompress/zstd_decompress.c +282/−165
- zstd/lib/decompress/zstd_decompress_block.c +456/−335
- zstd/lib/decompress/zstd_decompress_block.h +3/−3
- zstd/lib/decompress/zstd_decompress_internal.h +18/−4
- zstd/lib/dictBuilder/cover.c +164/−54
- zstd/lib/dictBuilder/cover.h +52/−7
- zstd/lib/dictBuilder/fastcover.c +60/−43
- zstd/lib/dictBuilder/zdict.c +43/−19
- zstd/lib/dictBuilder/zdict.h +56/−28
- zstd/lib/zstd.h +265/−88
CONTRIBUTING.md view
@@ -11,7 +11,7 @@ If you run into problems with either the code, documentation, or examples, please-[file a GitHub issue](https://github.com/facebookexperimental/hs-zstd/issues).+[file a GitHub issue](https://github.com/luispedro/hs-zstd/issues). (Note: if you'd like to file an issue requesting new functionality, rather than bugfixes, please plan to implement that functionality@@ -29,32 +29,17 @@ 3. If you're changing APIs, please update the appropriate documentation and examples, and the [changelog](changelog.md). (Breaking API changes have a high bar for approval.)-4. Ensure that the test suite passes (`cabal test`).+4. Ensure that the test suite passes (`stack build --test`). 5. Make sure your code conforms to the existing style you see in the code base. 6. Tidy up your commit history before submitting a pull request. This means one commit per logical change, please.-7. If you haven't yet done so, please complete our Contributor License- Agreement ("CLA"). -## Contributor License Agreement ("CLA")--In order to accept your pull request, we need you to submit a CLA. You-only need to do this once, after which you can work on any of-Facebook's open source projects.--Complete your CLA here: <https://code.facebook.com/cla>- ## Issues We use GitHub issues to track public bugs. Please ensure that your description is clear and has sufficient instructions for us be able to reproduce the issue.--Facebook has a [bounty program](https://www.facebook.com/whitehat/)-for the safe disclosure of security bugs. In those cases, please go-through the process outlined on that page and do not file a public-issue. ## License
ChangeLog view
@@ -1,3 +1,6 @@+Version 0.1.2.0 2020-05-21 by luispedro+ * Update to zstd 1.4.5 (PR #2 by Vanessa McHale)+ Version 0.1.1.2 2019-05-20 by luispedro * Re-release v0.1.1.1 with updated info
README.md view
@@ -1,10 +1,14 @@ # Zstandard bindings for Haskell -[](https://travis-ci.org/luispedro/hs-zstd)+[](https://travis-ci.com/luispedro/hs-zstd) This library provides Haskell bindings to the [Zstandard compression library](http://facebook.github.io/zstd/). +Note that is now the official repositoryc for the+[zstd](https://hackage.haskell.org/package/zstd) Haskell package. The original+authors (Facebook) are no longer interested in maintaining it.+ The library is structured to provide several layers of abstraction. * For the simplest use cases, the top-level@@ -14,12 +18,13 @@ * If you need to stream a large amount of data with a constant memory footprint, use the [`Zstd.Streaming`](http://hackage.haskell.org/package/zstd/docs/Codec-Compression-Zstd-Streaming.html)- module. This can also be used as a building block for adapting to- streaming libraries such as `pipes` and `conduit`. (If you need to- use lazy bytestrings instead, see the+ module. See also the+ [`conduit-zstd`](https://hackage.haskell.org/package/conduit-zstd) package+ which provides a very thin wrapper to integrate with the `conduit` library.+ If you need to use lazy bytestrings instead, see the [`Zstd.Lazy`](http://hackage.haskell.org/package/zstd/docs/Codec-Compression-Zstd-Lazy.html)- module. This is built using the abstractions from the- `Zstd.Streaming` module.)+ module. This is built using the abstractions from the `Zstd.Streaming`+ module. * When your usage is dominated by lots of small messages (presumably using pre-computed compression dictionaries), use the
zstd.cabal view
@@ -1,5 +1,5 @@ name: zstd-version: 0.1.1.2+version: 0.1.2.0 synopsis: Haskell bindings to the Zstandard compression algorithm description: A fast lossless compression algorithm, targeting real-time@@ -63,6 +63,9 @@ zstd/lib/compress/fse_compress.c zstd/lib/compress/hist.c zstd/lib/compress/huf_compress.c+ zstd/lib/compress/zstd_compress_literals.c+ zstd/lib/compress/zstd_compress_sequences.c+ zstd/lib/compress/zstd_compress_superblock.c zstd/lib/compress/zstd_compress.c zstd/lib/compress/zstd_double_fast.c zstd/lib/compress/zstd_fast.c
zstd/CONTRIBUTING.md view
@@ -26,6 +26,356 @@ Complete your CLA here: <https://code.facebook.com/cla> +## Workflow+Zstd uses a branch-based workflow for making changes to the codebase. Typically, zstd+will use a new branch per sizable topic. For smaller changes, it is okay to lump multiple+related changes into a branch.++Our contribution process works in three main stages:+1. Local development+ * Update:+ * Checkout your fork of zstd if you have not already+ ```+ git checkout https://github.com/<username>/zstd+ cd zstd+ ```+ * Update your local dev branch+ ```+ git pull https://github.com/facebook/zstd dev+ git push origin dev+ ```+ * Topic and development:+ * Make a new branch on your fork about the topic you're developing for+ ```+ # branch names should be consise but sufficiently informative+ git checkout -b <branch-name>+ git push origin <branch-name>+ ```+ * Make commits and push+ ```+ # make some changes =+ git add -u && git commit -m <message>+ git push origin <branch-name>+ ```+ * Note: run local tests to ensure that your changes didn't break existing functionality+ * Quick check+ ```+ make shortest+ ```+ * Longer check+ ```+ make test+ ```+2. Code Review and CI tests+ * Ensure CI tests pass:+ * Before sharing anything to the community, make sure that all CI tests pass on your local fork.+ See our section on setting up your CI environment for more information on how to do this.+ * Ensure that static analysis passes on your development machine. See the Static Analysis section+ below to see how to do this.+ * Create a pull request:+ * When you are ready to share you changes to the community, create a pull request from your branch+ to facebook:dev. You can do this very easily by clicking 'Create Pull Request' on your fork's home+ page.+ * From there, select the branch where you made changes as your source branch and facebook:dev+ as the destination.+ * Examine the diff presented between the two branches to make sure there is nothing unexpected.+ * Write a good pull request description:+ * While there is no strict template that our contributors follow, we would like them to+ sufficiently summarize and motivate the changes they are proposing. We recommend all pull requests,+ at least indirectly, address the following points.+ * Is this pull request important and why?+ * Is it addressing an issue? If so, what issue? (provide links for convenience please)+ * Is this a new feature? If so, why is it useful and/or necessary?+ * Are there background references and documents that reviewers should be aware of to properly assess this change?+ * Note: make sure to point out any design and architectural decisions that you made and the rationale behind them.+ * Note: if you have been working with a specific user and would like them to review your work, make sure you mention them using (@<username>)+ * Submit the pull request and iterate with feedback.+3. Merge and Release+ * Getting approval:+ * You will have to iterate on your changes with feedback from other collaborators to reach a point+ where your pull request can be safely merged.+ * To avoid too many comments on style and convention, make sure that you have a+ look at our style section below before creating a pull request.+ * Eventually, someone from the zstd team will approve your pull request and not long after merge it into+ the dev branch.+ * Housekeeping:+ * Most PRs are linked with one or more Github issues. If this is the case for your PR, make sure+ the corresponding issue is mentioned. If your change 'fixes' or completely addresses the+ issue at hand, then please indicate this by requesting that an issue be closed by commenting.+ * Just because your changes have been merged does not mean the topic or larger issue is complete. Remember+ that the change must make it to an official zstd release for it to be meaningful. We recommend+ that contributers track the activity on their pull request and corresponding issue(s) page(s) until+ their change makes it to the next release of zstd. Users will often discover bugs in your code or+ suggest ways to refine and improve your initial changes even after the pull request is merged.++## Static Analysis+Static analysis is a process for examining the correctness or validity of a program without actually+executing it. It usually helps us find many simple bugs. Zstd uses clang's `scan-build` tool for+static analysis. You can install it by following the instructions for your OS on https://clang-analyzer.llvm.org/scan-build.++Once installed, you can ensure that our static analysis tests pass on your local development machine+by running:+```+make staticAnalyze+```++In general, you can use `scan-build` to static analyze any build script. For example, to static analyze+just `contrib/largeNbDicts` and nothing else, you can run:++```+scan-build make -C contrib/largeNbDicts largeNbDicts+```++## Performance+Performance is extremely important for zstd and we only merge pull requests whose performance+landscape and corresponding trade-offs have been adequately analyzed, reproduced, and presented.+This high bar for performance means that every PR which has the potential to+impact performance takes a very long time for us to properly review. That being said, we+always welcome contributions to improve performance (or worsen performance for the trade-off of+something else). Please keep the following in mind before submitting a performance related PR:++1. Zstd isn't as old as gzip but it has been around for time now and its evolution is+very well documented via past Github issues and pull requests. It may be the case that your+particular performance optimization has already been considered in the past. Please take some+time to search through old issues and pull requests using keywords specific to your+would-be PR. Of course, just because a topic has already been discussed (and perhaps rejected+on some grounds) in the past, doesn't mean it isn't worth bringing up again. But even in that case,+it will be helpful for you to have context from that topic's history before contributing.+2. The distinction between noise and actual performance gains can unfortunately be very subtle+especially when microbenchmarking extremely small wins or losses. The only remedy to getting+something subtle merged is extensive benchmarking. You will be doing us a great favor if you+take the time to run extensive, long-duration, and potentially cross-(os, platform, process, etc)+benchmarks on your end before submitting a PR. Of course, you will not be able to benchmark+your changes on every single processor and os out there (and neither will we) but do that best+you can:) We've adding some things to think about when benchmarking below in the Benchmarking+Performance section which might be helpful for you.+3. Optimizing performance for a certain OS, processor vendor, compiler, or network system is a perfectly+legitimate thing to do as long as it does not harm the overall performance health of Zstd.+This is a hard balance to strike but please keep in mind other aspects of Zstd when+submitting changes that are clang-specific, windows-specific, etc.++## Benchmarking Performance+Performance microbenchmarking is a tricky subject but also essential for Zstd. We value empirical+testing over theoretical speculation. This guide it not perfect but for most scenarios, it+is a good place to start.++### Stability+Unfortunately, the most important aspect in being able to benchmark reliably is to have a stable+benchmarking machine. A virtual machine, a machine with shared resources, or your laptop+will typically not be stable enough to obtain reliable benchmark results. If you can get your+hands on a desktop, this is usually a better scenario.++Of course, benchmarking can be done on non-hyper-stable machines as well. You will just have to+do a little more work to ensure that you are in fact measuring the changes you've made not and+noise. Here are some things you can do to make your benchmarks more stable:++1. The most simple thing you can do to drastically improve the stability of your benchmark is+to run it multiple times and then aggregate the results of those runs. As a general rule of+thumb, the smaller the change you are trying to measure, the more samples of benchmark runs+you will have to aggregate over to get reliable results. Here are some additional things to keep in+mind when running multiple trials:+ * How you aggregate your samples are important. You might be tempted to use the mean of your+ results. While this is certainly going to be a more stable number than a raw single sample+ benchmark number, you might have more luck by taking the median. The mean is not robust to+ outliers whereas the median is. Better still, you could simply take the fastest speed your+ benchmark achieved on each run since that is likely the fastest your process will be+ capable of running your code. In our experience, this (aggregating by just taking the sample+ with the fastest running time) has been the most stable approach.+ * The more samples you have, the more stable your benchmarks should be. You can verify+ your improved stability by looking at the size of your confidence intervals as you+ increase your sample count. These should get smaller and smaller. Eventually hopefully+ smaller than the performance win you are expecting.+ * Most processors will take some time to get `hot` when running anything. The observations+ you collect during that time period will very different from the true performance number. Having+ a very large number of sample will help alleviate this problem slightly but you can also+ address is directly by simply not including the first `n` iterations of your benchmark in+ your aggregations. You can determine `n` by simply looking at the results from each iteration+ and then hand picking a good threshold after which the variance in results seems to stabilize.+2. You cannot really get reliable benchmarks if your host machine is simultaneously running+another cpu/memory-intensive application in the background. If you are running benchmarks on your+personal laptop for instance, you should close all applications (including your code editor and+browser) before running your benchmarks. You might also have invisible background applications+running. You can see what these are by looking at either Activity Monitor on Mac or Task Manager+on Windows. You will get more stable benchmark results of you end those processes as well.+ * If you have multiple cores, you can even run your benchmark on a reserved core to prevent+ pollution from other OS and user processes. There are a number of ways to do this depending+ on your OS:+ * On linux boxes, you have use https://github.com/lpechacek/cpuset.+ * On Windows, you can "Set Processor Affinity" using https://www.thewindowsclub.com/processor-affinity-windows+ * On Mac, you can try to use their dedicated affinity API https://developer.apple.com/library/archive/releasenotes/Performance/RN-AffinityAPI/#//apple_ref/doc/uid/TP40006635-CH1-DontLinkElementID_2+3. To benchmark, you will likely end up writing a separate c/c++ program that will link libzstd.+Dynamically linking your library will introduce some added variation (not a large amount but+definitely some). Statically linking libzstd will be more stable. Static libraries should+be enabled by default when building zstd.+4. Use a profiler with a good high resolution timer. See the section below on profiling for+details on this.+5. Disable frequency scaling, turbo boost and address space randomization (this will vary by OS)+6. Try to avoid storage. On some systems you can use tmpfs. Putting the program, inputs and outputs on+tmpfs avoids touching a real storage system, which can have a pretty big variability.++Also check our LLVM's guide on benchmarking here: https://llvm.org/docs/Benchmarking.html++### Zstd benchmark+The fastest signal you can get regarding your performance changes is via the in-build zstd cli+bench option. You can run Zstd as you typically would for your scenario using some set of options+and then additionally also specify the `-b#` option. Doing this will run our benchmarking pipeline+for that options you have just provided. If you want to look at the internals of how this+benchmarking script works, you can check out programs/benchzstd.c++For example: say you have made a change that you believe improves the speed of zstd level 1. The+very first thing you should use to asses whether you actually achieved any sort of improvement+is `zstd -b`. You might try to do something like this. Note: you can use the `-i` option to+specify a running time for your benchmark in seconds (default is 3 seconds).+Usually, the longer the running time, the more stable your results will be.++```+$ git checkout <commit-before-your-change>+$ make && cp zstd zstd-old+$ git checkout <commit-after-your-change>+$ make && cp zstd zstd-new+$ zstd-old -i5 -b1 <your-test-data>+ 1<your-test-data> : 8990 -> 3992 (2.252), 302.6 MB/s , 626.4 MB/s+$ zstd-new -i5 -b1 <your-test-data>+ 1<your-test-data> : 8990 -> 3992 (2.252), 302.8 MB/s , 628.4 MB/s+```++Unless your performance win is large enough to be visible despite the intrinsic noise+on your computer, benchzstd alone will likely not be enough to validate the impact of your+changes. For example, the results of the example above indicate that effectively nothing+changed but there could be a small <3% improvement that the noise on the host machine+obscured. So unless you see a large performance win (10-15% consistently) using just+this method of evaluation will not be sufficient.++### Profiling+There are a number of great profilers out there. We're going to briefly mention how you can+profile your code using `instruments` on mac, `perf` on linux and `visual studio profiler`+on windows.++Say you have an idea for a change that you think will provide some good performance gains+for level 1 compression on Zstd. Typically this means, you have identified a section of+code that you think can be made to run faster.++The first thing you will want to do is make sure that the piece of code is actually taking up+a notable amount of time to run. It is usually not worth optimzing something which accounts for less than+0.0001% of the total running time. Luckily, there are tools to help with this.+Profilers will let you see how much time your code spends inside a particular function.+If your target code snippit is only part of a function, it might be worth trying to+isolate that snippit by moving it to its own function (this is usually not necessary but+might be).++Most profilers (including the profilers dicusssed below) will generate a call graph of+functions for you. Your goal will be to find your function of interest in this call grapch+and then inspect the time spent inside of it. You might also want to to look at the+annotated assembly which most profilers will provide you with.++#### Instruments+We will once again consider the scenario where you think you've identified a piece of code+whose performance can be improved upon. Follow these steps to profile your code using+Instruments.++1. Open Instruments+2. Select `Time Profiler` from the list of standard templates+3. Close all other applications except for your instruments window and your terminal+4. Run your benchmarking script from your terminal window+ * You will want a benchmark that runs for at least a few seconds (5 seconds will+ usually be long enough). This way the profiler will have something to work with+ and you will have ample time to attach your profiler to this process:)+ * I will just use benchzstd as my bencharmking script for this example:+```+$ zstd -b1 -i5 <my-data> # this will run for 5 seconds+```+5. Once you run your benchmarking script, switch back over to instruments and attach your+process to the time profiler. You can do this by:+ * Clicking on the `All Processes` drop down in the top left of the toolbar.+ * Selecting your process from the dropdown. In my case, it is just going to be labled+ `zstd`+ * Hitting the bright red record circle button on the top left of the toolbar+6. You profiler will now start collecting metrics from your bencharking script. Once+you think you have collected enough samples (usually this is the case after 3 seconds of+recording), stop your profiler.+7. Make sure that in toolbar of the bottom window, `profile` is selected.+8. You should be able to see your call graph.+ * If you don't see the call graph or an incomplete call graph, make sure you have compiled+ zstd and your benchmarking scripg using debug flags. On mac and linux, this just means+ you will have to supply the `-g` flag alone with your build script. You might also+ have to provide the `-fno-omit-frame-pointer` flag+9. Dig down the graph to find your function call and then inspect it by double clicking+the list item. You will be able to see the annotated source code and the assembly side by+side.++#### Perf++This wiki has a pretty detailed tutorial on getting started working with perf so we'll+leave you to check that out of you're getting started:++https://perf.wiki.kernel.org/index.php/Tutorial++Some general notes on perf:+* Use `perf stat -r # <bench-program>` to quickly get some relevant timing and+counter statistics. Perf uses a high resolution timer and this is likely one+of the first things your team will run when assessing your PR.+* Perf has a long list of hardware counters that can be viewed with `perf --list`.+When measuring optimizations, something worth trying is to make sure the handware+counters you expect to be impacted by your change are in fact being so. For example,+if you expect the L1 cache misses to decrease with your change, you can look at the+counter `L1-dcache-load-misses`+* Perf hardware counters will not work on a virtual machine.++#### Visual Studio++TODO+++## Setting up continuous integration (CI) on your fork+Zstd uses a number of different continuous integration (CI) tools to ensure that new changes+are well tested before they make it to an official release. Specifically, we use the platforms+travis-ci, circle-ci, and appveyor.++Changes cannot be merged into the main dev branch unless they pass all of our CI tests.+The easiest way to run these CI tests on your own before submitting a PR to our dev branch+is to configure your personal fork of zstd with each of the CI platforms. Below, you'll find+instructions for doing this.++### travis-ci+Follow these steps to link travis-ci with your github fork of zstd++1. Make sure you are logged into your github account+2. Go to https://travis-ci.org/+3. Click 'Sign in with Github' on the top right+4. Click 'Authorize travis-ci'+5. Click 'Activate all repositories using Github Apps'+6. Select 'Only select repositories' and select your fork of zstd from the drop down+7. Click 'Approve and Install'+8. Click 'Sign in with Github' again. This time, it will be for travis-pro (which will let you view your tests on the web dashboard)+9. Click 'Authorize travis-pro'+10. You should have travis set up on your fork now.++### circle-ci+TODO++### appveyor+Follow these steps to link circle-ci with your girhub fork of zstd++1. Make sure you are logged into your github account+2. Go to https://www.appveyor.com/+3. Click 'Sign in' on the top right+4. Select 'Github' on the left panel+5. Click 'Authorize appveyor'+6. You might be asked to select which repositories you want to give appveyor permission to. Select your fork of zstd if you're prompted+7. You should have appveyor set up on your fork now.++### General notes on CI+CI tests run every time a pull request (PR) is created or updated. The exact tests+that get run will depend on the destination branch you specify. Some tests take+longer to run than others. Currently, our CI is set up to run a short+series of tests when creating a PR to the dev branch and a longer series of tests+when creating a PR to the master branch. You can look in the configuration files+of the respective CI platform for more information on what gets run when.++Most people will just want to create a PR with the destination set to their local dev+branch of zstd. You can then find the status of the tests on the PR's page. You can also+re-run tests and cancel running tests from the PR page or from the respective CI's dashboard.+ ## Issues We use GitHub issues to track public bugs. Please ensure your description is clear and has sufficient instructions to be able to reproduce the issue.@@ -34,7 +384,7 @@ disclosure of security bugs. In those cases, please go through the process outlined on that page and do not file a public issue. -## Coding Style +## Coding Style * 4 spaces for indentation rather than tabs ## License
zstd/README.md view
@@ -15,6 +15,7 @@ [![Build status][AppveyorDevBadge]][AppveyorLink] [![Build status][CircleDevBadge]][CircleLink] [![Build status][CirrusDevBadge]][CirrusLink]+[![Fuzzing Status][OSSFuzzBadge]][OSSFuzzLink] [travisDevBadge]: https://travis-ci.org/facebook/zstd.svg?branch=dev "Continuous Integration test suite" [travisLink]: https://travis-ci.org/facebook/zstd@@ -24,14 +25,16 @@ [CircleLink]: https://circleci.com/gh/facebook/zstd [CirrusDevBadge]: https://api.cirrus-ci.com/github/facebook/zstd.svg?branch=dev [CirrusLink]: https://cirrus-ci.com/github/facebook/zstd+[OSSFuzzBadge]: https://oss-fuzz-build-logs.storage.googleapis.com/badges/zstd.svg+[OSSFuzzLink]: https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:zstd ## Benchmarks For reference, several fast compression algorithms were tested and compared-on a server running Arch Linux (`Linux version 5.0.5-arch1-1`),+on a server running Arch Linux (`Linux version 5.5.11-arch1-1`), with a Core i9-9900K CPU @ 5.0GHz, using [lzbench], an open-source in-memory benchmark by @inikep-compiled with [gcc] 8.2.1,+compiled with [gcc] 9.3.0, on the [Silesia compression corpus]. [lzbench]: https://github.com/inikep/lzbench@@ -40,18 +43,26 @@ | Compressor name | Ratio | Compression| Decompress.| | --------------- | ------| -----------| ---------- |-| **zstd 1.4.0 -1** | 2.884 | 530 MB/s | 1360 MB/s |-| zlib 1.2.11 -1 | 2.743 | 110 MB/s | 440 MB/s |-| brotli 1.0.7 -0 | 2.701 | 430 MB/s | 470 MB/s |-| quicklz 1.5.0 -1 | 2.238 | 600 MB/s | 800 MB/s |-| lzo1x 2.09 -1 | 2.106 | 680 MB/s | 950 MB/s |-| lz4 1.8.3 | 2.101 | 800 MB/s | 4220 MB/s |-| snappy 1.1.4 | 2.073 | 580 MB/s | 2020 MB/s |-| lzf 3.6 -1 | 2.077 | 440 MB/s | 930 MB/s |+| **zstd 1.4.5 -1** | 2.884 | 500 MB/s | 1660 MB/s |+| zlib 1.2.11 -1 | 2.743 | 90 MB/s | 400 MB/s |+| brotli 1.0.7 -0 | 2.703 | 400 MB/s | 450 MB/s |+| **zstd 1.4.5 --fast=1** | 2.434 | 570 MB/s | 2200 MB/s |+| **zstd 1.4.5 --fast=3** | 2.312 | 640 MB/s | 2300 MB/s |+| quicklz 1.5.0 -1 | 2.238 | 560 MB/s | 710 MB/s |+| **zstd 1.4.5 --fast=5** | 2.178 | 700 MB/s | 2420 MB/s |+| lzo1x 2.10 -1 | 2.106 | 690 MB/s | 820 MB/s |+| lz4 1.9.2 | 2.101 | 740 MB/s | 4530 MB/s |+| **zstd 1.4.5 --fast=7** | 2.096 | 750 MB/s | 2480 MB/s |+| lzf 3.6 -1 | 2.077 | 410 MB/s | 860 MB/s |+| snappy 1.1.8 | 2.073 | 560 MB/s | 1790 MB/s | [zlib]: http://www.zlib.net/ [LZ4]: http://www.lz4.org/ +The negative compression levels, specified with `--fast=#`,+offer faster compression and decompression speed in exchange for some loss in+compression ratio compared to level 1, as seen in the table above.+ Zstd can also offer stronger compression ratios at the cost of compression speed. Speed vs Compression trade-off is configurable by small increments. Decompression speed is preserved and remains roughly the same at all settings,@@ -139,6 +150,18 @@ example about how Meson is used to build this project. Note that default build type is **release**.++### VCPKG+You can build and install zstd [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager:++ git clone https://github.com/Microsoft/vcpkg.git+ cd vcpkg+ ./bootstrap-vcpkg.sh+ ./vcpkg integrate install+ ./vcpkg install zstd++The zstd port in vcpkg is kept up to date by Microsoft team members and community contributors.+If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. ### Visual Studio (Windows)
zstd/TESTING.md view
@@ -11,7 +11,7 @@ - Compilation on all supported targets (x86, x86_64, ARM, AArch64, PowerPC, and PowerPC64) - Compilation on various versions of gcc, clang, and g++ - `tests/playTests.sh` on x86_64, without the tests on long data (CLI tests)-- Small tests (`tests/legacy.c`, `tests/longmatch.c`, `tests/symbols.c`) on x64_64+- Small tests (`tests/legacy.c`, `tests/longmatch.c`) on x64_64 Medium Tests ------------@@ -19,7 +19,7 @@ They consist of the following tests: - The following tests run with UBsan and Asan on x86_64 and x86, as well as with Msan on x86_64- - `tests/playTests.sh --test-long-data`+ - `tests/playTests.sh --test-large-data` - Fuzzer tests: `tests/fuzzer.c`, `tests/zstreamtest.c`, and `tests/decodecorpus.c` - `tests/zstreamtest.c` under Tsan (streaming mode, including multithreaded mode) - Valgrind Test (`make -C tests valgrindTest`) (testing CLI and fuzzer under valgrind)
zstd/lib/README.md view
@@ -27,10 +27,10 @@ Both conditions are automatically applied when invoking `make lib-mt` target. When linking a POSIX program with a multithreaded version of `libzstd`,-note that it's necessary to request the `-pthread` flag during link stage.+note that it's necessary to invoke the `-pthread` flag during link stage. Multithreading capabilities are exposed-via the [advanced API defined in `lib/zstd.h`](https://github.com/facebook/zstd/blob/v1.3.8/lib/zstd.h#L592).+via the [advanced API defined in `lib/zstd.h`](https://github.com/facebook/zstd/blob/v1.4.3/lib/zstd.h#L351). #### API@@ -85,32 +85,63 @@ - While invoking `make libzstd`, it's possible to define build macros `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 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+ dictBuilder). -- There are some additional build macros that can be used to minify the decoder.+- There are a number of options that can help minimize the binary size of+ `libzstd`. - Zstandard often has more than one implementation of a piece of functionality,- where each implementation optimizes for different scenarios. For example, the- Huffman decoder has complementary implementations that decode the stream one- symbol at a time or two symbols at a time. Zstd normally includes both (and- dispatches between them at runtime), but by defining `HUF_FORCE_DECOMPRESS_X1`- or `HUF_FORCE_DECOMPRESS_X2`, you can force the use of one or the other, avoiding+ The first step is to select the components needed (using the above-described+ `ZSTD_LIB_COMPRESSION` etc.).++ The next step is to set `ZSTD_LIB_MINIFY` to `1` when invoking `make`. This+ disables various optional components and changes the compilation flags to+ prioritize space-saving.++ Detailed options: Zstandard's code and build environment is set up by default+ to optimize above all else for performance. In pursuit of this goal, Zstandard+ makes significant trade-offs in code size. For example, Zstandard often has+ more than one implementation of a particular component, with each+ implementation optimized for different scenarios. For example, the Huffman+ decoder has complementary implementations that decode the stream one symbol at+ a time or two symbols at a time. Zstd normally includes both (and dispatches+ between them at runtime), but by defining `HUF_FORCE_DECOMPRESS_X1` or+ `HUF_FORCE_DECOMPRESS_X2`, you can force the use of one or the other, avoiding compilation of the other. Similarly, `ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT` and `ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG` force the compilation and use of only one or the other of two decompression implementations. The smallest binary is achieved by using `HUF_FORCE_DECOMPRESS_X1` and- `ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT`.+ `ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT` (implied by `ZSTD_LIB_MINIFY`). For squeezing the last ounce of size out, you can also define `ZSTD_NO_INLINE`, which disables inlining, and `ZSTD_STRIP_ERROR_STRINGS`, which removes the error messages that are otherwise returned by- `ZSTD_getErrorName`.+ `ZSTD_getErrorName` (implied by `ZSTD_LIB_MINIFY`). + Finally, when integrating into your application, make sure you're doing link-+ time optimation and unused symbol garbage collection (via some combination of,+ e.g., `-flto`, `-ffat-lto-objects`, `-fuse-linker-plugin`,+ `-ffunction-sections`, `-fdata-sections`, `-fmerge-all-constants`,+ `-Wl,--gc-sections`, `-Wl,-z,norelro`, and an archiver that understands+ the compiler's intermediate representation, e.g., `AR=gcc-ar`). Consult your+ compiler's documentation.+ - While invoking `make libzstd`, the build macro `ZSTD_LEGACY_MULTITHREADED_API=1` will expose the deprecated `ZSTDMT` API exposed by `zstdmt_compress.h` in the shared library, which is now hidden by default.++- The build macro `DYNAMIC_BMI2` can be set to 1 or 0 in order to generate binaries+ which can detect at runtime the presence of BMI2 instructions, and use them only if present.+ These instructions contribute to better performance, notably on the decoder side.+ By default, this feature is automatically enabled on detecting+ the right instruction set (x64) and compiler (clang or gcc >= 5).+ It's obviously disabled for different cpus,+ or when BMI2 instruction set is _required_ by the compiler command line+ (in this case, only the BMI2 code path is generated).+ Setting this macro will either force to generate the BMI2 dispatcher (1)+ or prevent it (0). It overrides automatic detection. #### Windows : using MinGW+MSYS to create DLL
zstd/lib/common/bitstream.h view
@@ -1,35 +1,15 @@ /* ******************************************************************- bitstream- Part of FSE library- Copyright (C) 2013-present, 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:-- * 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 :- - Source repository : https://github.com/Cyan4973/FiniteStateEntropy+ * bitstream+ * Part of FSE library+ * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc.+ *+ * You can contact the author at :+ * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy+ *+ * 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 BITSTREAM_H_MODULE #define BITSTREAM_H_MODULE@@ -48,6 +28,7 @@ * Dependencies ******************************************/ #include "mem.h" /* unaligned access routines */+#include "compiler.h" /* UNLIKELY() */ #include "debug.h" /* assert(), DEBUGLOG(), RAWLOG() */ #include "error_private.h" /* error codes and messages */ @@ -57,6 +38,8 @@ =========================================*/ #if defined(__BMI__) && defined(__GNUC__) # include <immintrin.h> /* support for bextr (experimental) */+#elif defined(__ICCARM__)+# include <intrinsics.h> #endif #define STREAM_ACCUMULATOR_MIN_32 25@@ -159,10 +142,11 @@ { # if defined(_MSC_VER) /* Visual */ unsigned long r=0;- _BitScanReverse ( &r, val );- return (unsigned) r;+ return _BitScanReverse ( &r, val ) ? (unsigned)r : 0; # elif defined(__GNUC__) && (__GNUC__ >= 3) /* Use GCC Intrinsic */- return 31 - __builtin_clz (val);+ 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,@@ -240,9 +224,9 @@ { size_t const nbBytes = bitC->bitPos >> 3; assert(bitC->bitPos < sizeof(bitC->bitContainer) * 8);+ assert(bitC->ptr <= bitC->endPtr); MEM_writeLEST(bitC->ptr, bitC->bitContainer); bitC->ptr += nbBytes;- assert(bitC->ptr <= bitC->endPtr); bitC->bitPos &= 7; bitC->bitContainer >>= nbBytes*8; }@@ -256,6 +240,7 @@ { size_t const nbBytes = bitC->bitPos >> 3; assert(bitC->bitPos < sizeof(bitC->bitContainer) * 8);+ assert(bitC->ptr <= bitC->endPtr); MEM_writeLEST(bitC->ptr, bitC->bitContainer); bitC->ptr += nbBytes; if (bitC->ptr > bitC->endPtr) bitC->ptr = bitC->endPtr;@@ -406,6 +391,23 @@ return value; } +/*! BIT_reloadDStreamFast() :+ * Similar to BIT_reloadDStream(), but with two differences:+ * 1. bitsConsumed <= sizeof(bitD->bitContainer)*8 must hold!+ * 2. Returns BIT_DStream_overflow when bitD->ptr < bitD->limitPtr, at this+ * point you must use BIT_reloadDStream() to reload.+ */+MEM_STATIC BIT_DStream_status BIT_reloadDStreamFast(BIT_DStream_t* bitD)+{+ if (UNLIKELY(bitD->ptr < bitD->limitPtr))+ return BIT_DStream_overflow;+ assert(bitD->bitsConsumed <= sizeof(bitD->bitContainer)*8);+ bitD->ptr -= bitD->bitsConsumed >> 3;+ bitD->bitsConsumed &= 7;+ bitD->bitContainer = MEM_readLEST(bitD->ptr);+ return BIT_DStream_unfinished;+}+ /*! BIT_reloadDStream() : * Refill `bitD` from buffer previously set in BIT_initDStream() . * This function is safe, it guarantees it will not read beyond src buffer.@@ -417,10 +419,7 @@ return BIT_DStream_overflow; if (bitD->ptr >= bitD->limitPtr) {- bitD->ptr -= bitD->bitsConsumed >> 3;- bitD->bitsConsumed &= 7;- bitD->bitContainer = MEM_readLEST(bitD->ptr);- return BIT_DStream_unfinished;+ return BIT_reloadDStreamFast(bitD); } if (bitD->ptr == bitD->start) { if (bitD->bitsConsumed < sizeof(bitD->bitContainer)*8) return BIT_DStream_endOfBuffer;
zstd/lib/common/compiler.h view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -17,13 +17,13 @@ /* force inlining */ #if !defined(ZSTD_NO_INLINE)-#if defined (__GNUC__) || defined(__cplusplus) || defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */+#if (defined(__GNUC__) && !defined(__STRICT_ANSI__)) || defined(__cplusplus) || defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ # define INLINE_KEYWORD inline #else # define INLINE_KEYWORD #endif -#if defined(__GNUC__)+#if defined(__GNUC__) || defined(__ICCARM__) # define FORCE_INLINE_ATTR __attribute__((always_inline)) #elif defined(_MSC_VER) # define FORCE_INLINE_ATTR __forceinline@@ -61,11 +61,18 @@ # define HINT_INLINE static INLINE_KEYWORD FORCE_INLINE_ATTR #endif +/* UNUSED_ATTR tells the compiler it is okay if the function is unused. */+#if defined(__GNUC__)+# define UNUSED_ATTR __attribute__((unused))+#else+# define UNUSED_ATTR+#endif+ /* force no inlining */ #ifdef _MSC_VER # define FORCE_NOINLINE static __declspec(noinline) #else-# ifdef __GNUC__+# if defined(__GNUC__) || defined(__ICCARM__) # define FORCE_NOINLINE static __attribute__((__noinline__)) # else # define FORCE_NOINLINE static@@ -76,7 +83,7 @@ #ifndef __has_attribute #define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ #endif-#if defined(__GNUC__)+#if defined(__GNUC__) || defined(__ICCARM__) # define TARGET_ATTRIBUTE(target) __attribute__((__target__(target))) #else # define TARGET_ATTRIBUTE(target)@@ -107,6 +114,9 @@ # include <mmintrin.h> /* https://msdn.microsoft.com/fr-fr/library/84szxsww(v=vs.90).aspx */ # define PREFETCH_L1(ptr) _mm_prefetch((const char*)(ptr), _MM_HINT_T0) # define PREFETCH_L2(ptr) _mm_prefetch((const char*)(ptr), _MM_HINT_T1)+# elif defined(__aarch64__)+# define PREFETCH_L1(ptr) __asm__ __volatile__("prfm pldl1keep, %0" ::"Q"(*(ptr)))+# define PREFETCH_L2(ptr) __asm__ __volatile__("prfm pldl2keep, %0" ::"Q"(*(ptr))) # elif defined(__GNUC__) && ( (__GNUC__ >= 4) || ( (__GNUC__ == 3) && (__GNUC_MINOR__ >= 1) ) ) # define PREFETCH_L1(ptr) __builtin_prefetch((ptr), 0 /* rw==read */, 3 /* locality */) # define PREFETCH_L2(ptr) __builtin_prefetch((ptr), 0 /* rw==read */, 2 /* locality */)@@ -126,6 +136,31 @@ PREFETCH_L2(_ptr + _pos); \ } \ }++/* vectorization+ * older GCC (pre gcc-4.3 picked as the cutoff) uses a different syntax */+#if !defined(__INTEL_COMPILER) && !defined(__clang__) && defined(__GNUC__)+# if (__GNUC__ == 4 && __GNUC_MINOR__ > 3) || (__GNUC__ >= 5)+# define DONT_VECTORIZE __attribute__((optimize("no-tree-vectorize")))+# else+# define DONT_VECTORIZE _Pragma("GCC optimize(\"no-tree-vectorize\")")+# endif+#else+# define DONT_VECTORIZE+#endif++/* Tell the compiler that a branch is likely or unlikely.+ * Only use these macros if it causes the compiler to generate better code.+ * If you can remove a LIKELY/UNLIKELY annotation without speed changes in gcc+ * and clang, please do.+ */+#if defined(__GNUC__)+#define LIKELY(x) (__builtin_expect((x), 1))+#define UNLIKELY(x) (__builtin_expect((x), 0))+#else+#define LIKELY(x) (x)+#define UNLIKELY(x) (x)+#endif /* disable warnings */ #ifdef _MSC_VER /* Visual Studio */
zstd/lib/common/cpu.h view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2018-present, Facebook, Inc.+ * Copyright (c) 2018-2020, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the
zstd/lib/common/debug.c view
@@ -1,35 +1,15 @@ /* ******************************************************************- debug- Part of FSE library- Copyright (C) 2013-present, 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:-- * 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 :- - Source repository : https://github.com/Cyan4973/FiniteStateEntropy+ * debug+ * Part of FSE library+ * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc.+ *+ * You can contact the author at :+ * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy+ *+ * 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. ****************************************************************** */
zstd/lib/common/debug.h view
@@ -1,35 +1,15 @@ /* ******************************************************************- debug- Part of FSE library- Copyright (C) 2013-present, 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:-- * 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 :- - Source repository : https://github.com/Cyan4973/FiniteStateEntropy+ * debug+ * Part of FSE library+ * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc.+ *+ * You can contact the author at :+ * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy+ *+ * 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. ****************************************************************** */
zstd/lib/common/entropy_common.c view
@@ -1,36 +1,16 @@-/*- Common functions of New Generation Entropy library- Copyright (C) 2016, 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:-- * 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 :- - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy- - Public forum : https://groups.google.com/forum/#!forum/lz4c-*************************************************************************** */+/* ******************************************************************+ * Common functions of New Generation Entropy library+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.+ *+ * You can contact the author at :+ * - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy+ * - Public forum : https://groups.google.com/forum/#!forum/lz4c+ *+ * 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
zstd/lib/common/error_private.c view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -47,6 +47,7 @@ /* 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(maxCode): default: return notErrorCode; }
zstd/lib/common/error_private.h view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -49,13 +49,17 @@ /*-**************************************** * Error codes handling ******************************************/-#undef ERROR /* reported already defined on VS 2015 (Rich Geldreich) */+#undef ERROR /* already defined on Visual Studio */ #define ERROR(name) ZSTD_ERROR(name) #define ZSTD_ERROR(name) ((size_t)-PREFIX(name)) ERR_STATIC unsigned ERR_isError(size_t code) { return (code > ERROR(maxCode)); } ERR_STATIC ERR_enum ERR_getErrorCode(size_t code) { if (!ERR_isError(code)) return (ERR_enum)0; return (ERR_enum) (0-code); }++/* check and forward error code */+#define CHECK_V_F(e, f) size_t const e = f; if (ERR_isError(e)) return e+#define CHECK_F(f) { CHECK_V_F(_var_err__, f); } /*-****************************************
zstd/lib/common/fse.h view
@@ -1,35 +1,15 @@ /* ******************************************************************- FSE : Finite State Entropy codec- Public Prototypes declaration- Copyright (C) 2013-2016, 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:-- * 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 :- - Source repository : https://github.com/Cyan4973/FiniteStateEntropy+ * FSE : Finite State Entropy codec+ * Public Prototypes declaration+ * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc.+ *+ * You can contact the author at :+ * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy+ *+ * 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. ****************************************************************** */ #if defined (__cplusplus)@@ -308,7 +288,7 @@ *******************************************/ /* FSE buffer bounds */ #define FSE_NCOUNTBOUND 512-#define FSE_BLOCKBOUND(size) (size + (size>>7))+#define FSE_BLOCKBOUND(size) (size + (size>>7) + 4 /* fse states */ + sizeof(size_t) /* bitContainer */) #define FSE_COMPRESSBOUND(size) (FSE_NCOUNTBOUND + FSE_BLOCKBOUND(size)) /* Macro version, useful for static allocation */ /* It is possible to statically allocate FSE CTable/DTable as a table of FSE_CTable/FSE_DTable using below macros */
zstd/lib/common/fse_decompress.c view
@@ -1,35 +1,15 @@ /* ******************************************************************- FSE : Finite State Entropy decoder- Copyright (C) 2013-2015, 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:-- * 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 :- - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy- - Public forum : https://groups.google.com/forum/#!forum/lz4c+ * FSE : Finite State Entropy decoder+ * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc.+ *+ * You can contact the author at :+ * - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy+ * - Public forum : https://groups.google.com/forum/#!forum/lz4c+ *+ * 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. ****************************************************************** */ @@ -51,10 +31,7 @@ #define FSE_isError ERR_isError #define FSE_STATIC_ASSERT(c) DEBUG_STATIC_ASSERT(c) /* use only *after* variable declarations */ -/* check and forward error code */-#define CHECK_F(f) { size_t const e = f; if (FSE_isError(e)) return e; } - /* ************************************************************** * Templates ****************************************************************/@@ -285,7 +262,7 @@ /* normal FSE decoding mode */ size_t const NCountLength = FSE_readNCount (counting, &maxSymbolValue, &tableLog, istart, cSrcSize); if (FSE_isError(NCountLength)) return NCountLength;- //if (NCountLength >= cSrcSize) return ERROR(srcSize_wrong); /* too small input size; supposed to be already checked in NCountLength, only remaining case : NCountLength==cSrcSize */+ /* if (NCountLength >= cSrcSize) return ERROR(srcSize_wrong); */ /* too small input size; supposed to be already checked in NCountLength, only remaining case : NCountLength==cSrcSize */ if (tableLog > maxLog) return ERROR(tableLog_tooLarge); ip += NCountLength; cSrcSize -= NCountLength;
zstd/lib/common/huf.h view
@@ -1,35 +1,15 @@ /* ******************************************************************- huff0 huffman codec,- part of Finite State Entropy library- Copyright (C) 2013-present, 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:-- * 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 :- - Source repository : https://github.com/Cyan4973/FiniteStateEntropy+ * huff0 huffman codec,+ * part of Finite State Entropy library+ * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc.+ *+ * You can contact the author at :+ * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy+ *+ * 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. ****************************************************************** */ #if defined (__cplusplus)@@ -110,7 +90,7 @@ /** HUF_compress4X_wksp() : * Same as HUF_compress2(), but uses externally allocated `workSpace`. * `workspace` must have minimum alignment of 4, and be at least as large as HUF_WORKSPACE_SIZE */-#define HUF_WORKSPACE_SIZE (6 << 10)+#define HUF_WORKSPACE_SIZE ((6 << 10) + 256) #define HUF_WORKSPACE_SIZE_U32 (HUF_WORKSPACE_SIZE / sizeof(U32)) HUF_PUBLIC_API size_t HUF_compress4X_wksp (void* dst, size_t dstCapacity, const void* src, size_t srcSize,@@ -208,6 +188,8 @@ 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); size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable);+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); typedef enum { HUF_repeat_none, /**< Cannot use the previous table */@@ -246,7 +228,7 @@ /** HUF_readCTable() : * Loading a CTable saved with HUF_writeCTable() */-size_t HUF_readCTable (HUF_CElt* CTable, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize);+size_t HUF_readCTable (HUF_CElt* CTable, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize, unsigned *hasZeroWeights); /** HUF_getNbBits() : * Read nbBits from CTable symbolTable, for symbol `symbolValue` presumed <= HUF_SYMBOLVALUE_MAX
zstd/lib/common/mem.h view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -47,7 +47,80 @@ #define MEM_STATIC_ASSERT(c) { enum { MEM_static_assert = 1/(int)(!!(c)) }; } MEM_STATIC void MEM_check(void) { MEM_STATIC_ASSERT((sizeof(size_t)==4) || (sizeof(size_t)==8)); } +/* detects whether we are being compiled under msan */+#if defined (__has_feature)+# if __has_feature(memory_sanitizer)+# define MEMORY_SANITIZER 1+# endif+#endif +#if defined (MEMORY_SANITIZER)+/* 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... */++#include <stdint.h> /* intptr_t */++/* Make memory region fully initialized (without changing its contents). */+void __msan_unpoison(const volatile void *a, size_t size);++/* Make memory region fully uninitialized (without changing its contents).+ This is a legacy interface that does not update origin information. Use+ __msan_allocated_memory() instead. */+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);+#endif++/* detects whether we are being compiled under asan */+#if defined (__has_feature)+# if __has_feature(address_sanitizer)+# define ADDRESS_SANITIZER 1+# endif+#elif defined(__SANITIZE_ADDRESS__)+# define ADDRESS_SANITIZER 1+#endif++#if defined (ADDRESS_SANITIZER)+/* 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... */++/**+ * Marks a memory region (<c>[addr, addr+size)</c>) as unaddressable.+ *+ * This memory must be previously allocated by your program. Instrumented+ * code is forbidden from accessing addresses in this region until it is+ * unpoisoned. This function is not guaranteed to poison the entire region -+ * it could poison only a subregion of <c>[addr, addr+size)</c> due to ASan+ * alignment restrictions.+ *+ * \note This function is not thread-safe because no two threads can poison or+ * unpoison memory in the same memory region simultaneously.+ *+ * \param addr Start of memory region.+ * \param size Size of memory region. */+void __asan_poison_memory_region(void const volatile *addr, size_t size);++/**+ * Marks a memory region (<c>[addr, addr+size)</c>) as addressable.+ *+ * This memory must be previously allocated by your program. Accessing+ * addresses in this region is allowed until this region is poisoned again.+ * This function could unpoison a super-region of <c>[addr, addr+size)</c> due+ * to ASan alignment restrictions.+ *+ * \note This function is not thread-safe because no two threads can+ * poison or unpoison memory in the same memory region simultaneously.+ *+ * \param addr Start of memory region.+ * \param size Size of memory region. */+void __asan_unpoison_memory_region(void const volatile *addr, size_t size);+#endif++ /*-************************************************************** * Basic Types *****************************************************************/@@ -102,7 +175,7 @@ #ifndef MEM_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ # if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) # define MEM_FORCE_MEMORY_ACCESS 2-# elif defined(__INTEL_COMPILER) || defined(__GNUC__)+# elif defined(__INTEL_COMPILER) || defined(__GNUC__) || defined(__ICCARM__) # define MEM_FORCE_MEMORY_ACCESS 1 # endif #endif
zstd/lib/common/pool.c view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -127,9 +127,13 @@ ctx->queueTail = 0; ctx->numThreadsBusy = 0; ctx->queueEmpty = 1;- (void)ZSTD_pthread_mutex_init(&ctx->queueMutex, NULL);- (void)ZSTD_pthread_cond_init(&ctx->queuePushCond, NULL);- (void)ZSTD_pthread_cond_init(&ctx->queuePopCond, NULL);+ {+ int error = 0;+ error |= ZSTD_pthread_mutex_init(&ctx->queueMutex, NULL);+ error |= ZSTD_pthread_cond_init(&ctx->queuePushCond, NULL);+ error |= ZSTD_pthread_cond_init(&ctx->queuePopCond, NULL);+ if (error) { POOL_free(ctx); return NULL; }+ } ctx->shutdown = 0; /* Allocate space for the thread handles */ ctx->threads = (ZSTD_pthread_t*)ZSTD_malloc(numThreads * sizeof(ZSTD_pthread_t), customMem);
zstd/lib/common/pool.h view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -18,7 +18,7 @@ #include <stddef.h> /* size_t */ #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_customMem */-#include "zstd.h"+#include "../zstd.h" typedef struct POOL_ctx_s POOL_ctx;
zstd/lib/common/threading.c view
@@ -2,18 +2,21 @@ * 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 can contact the author at:- * - zstdmt source repository: https://github.com/mcmilk/zstdmt+ * 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; @@ -28,7 +31,6 @@ /* === Dependencies === */ #include <process.h> #include <errno.h>-#include "threading.h" /* === Implementation === */@@ -73,3 +75,47 @@ } #endif /* ZSTD_MULTITHREAD */++#if defined(ZSTD_MULTITHREAD) && DEBUGLEVEL >= 1 && !defined(_WIN32)++#include <stdlib.h>++int ZSTD_pthread_mutex_init(ZSTD_pthread_mutex_t* mutex, pthread_mutexattr_t const* attr)+{+ *mutex = (pthread_mutex_t*)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);+ free(*mutex);+ return ret;+ }+}++int ZSTD_pthread_cond_init(ZSTD_pthread_cond_t* cond, pthread_condattr_t const* attr)+{+ *cond = (pthread_cond_t*)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);+ free(*cond);+ return ret;+ }+}++#endif
zstd/lib/common/threading.h view
@@ -2,17 +2,20 @@ * 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 can contact the author at:- * - zstdmt source repository: https://github.com/mcmilk/zstdmt+ * You may select, at your option, one of the above-listed licenses. */ #ifndef THREADING_H_938743 #define THREADING_H_938743 +#include "debug.h"+ #if defined (__cplusplus) extern "C" { #endif@@ -75,10 +78,12 @@ */ -#elif defined(ZSTD_MULTITHREAD) /* posix assumed ; need a better detection method */+#elif defined(ZSTD_MULTITHREAD) /* posix assumed ; need a better detection method */ /* === POSIX Systems === */ # include <pthread.h> +#if DEBUGLEVEL < 1+ #define ZSTD_pthread_mutex_t pthread_mutex_t #define ZSTD_pthread_mutex_init(a, b) pthread_mutex_init((a), (b)) #define ZSTD_pthread_mutex_destroy(a) pthread_mutex_destroy((a))@@ -95,6 +100,33 @@ #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))++#else /* DEBUGLEVEL >= 1 */++/* Debug implementation of threading.+ * In this implementation we use pointers for mutexes and condition variables.+ * This way, if we forget to init/destroy them the program will crash or ASAN+ * will report leaks.+ */++#define ZSTD_pthread_mutex_t pthread_mutex_t*+int ZSTD_pthread_mutex_init(ZSTD_pthread_mutex_t* mutex, pthread_mutexattr_t const* attr);+int ZSTD_pthread_mutex_destroy(ZSTD_pthread_mutex_t* mutex);+#define ZSTD_pthread_mutex_lock(a) pthread_mutex_lock(*(a))+#define ZSTD_pthread_mutex_unlock(a) pthread_mutex_unlock(*(a))++#define ZSTD_pthread_cond_t pthread_cond_t*+int ZSTD_pthread_cond_init(ZSTD_pthread_cond_t* cond, pthread_condattr_t const* attr);+int ZSTD_pthread_cond_destroy(ZSTD_pthread_cond_t* cond);+#define ZSTD_pthread_cond_wait(a, b) pthread_cond_wait(*(a), *(b))+#define ZSTD_pthread_cond_signal(a) pthread_cond_signal(*(a))+#define ZSTD_pthread_cond_broadcast(a) pthread_cond_broadcast(*(a))++#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))++#endif #else /* ZSTD_MULTITHREAD not defined */ /* No multithreading support */
zstd/lib/common/xxhash.c view
@@ -1,35 +1,15 @@ /*-* xxHash - Fast Hash algorithm-* Copyright (C) 2012-2016, 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:-*-* * 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 :-* - xxHash homepage: http://www.xxhash.com-* - xxHash source repository : https://github.com/Cyan4973/xxHash+ * xxHash - Fast Hash algorithm+ * Copyright (c) 2012-2020, Yann Collet, Facebook, Inc.+ *+ * You can contact the author at :+ * - xxHash homepage: http://www.xxhash.com+ * - xxHash source repository : https://github.com/Cyan4973/xxHash+ * + * 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. */ @@ -53,7 +33,8 @@ # if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) # define XXH_FORCE_MEMORY_ACCESS 2 # elif (defined(__INTEL_COMPILER) && !defined(WIN32)) || \- (defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) ))+ (defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) )) || \+ defined(__ICCARM__) # define XXH_FORCE_MEMORY_ACCESS 1 # endif #endif@@ -114,13 +95,13 @@ /* ************************************* * Compiler Specific Options ***************************************/-#if defined (__GNUC__) || defined(__cplusplus) || defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */+#if (defined(__GNUC__) && !defined(__STRICT_ANSI__)) || defined(__cplusplus) || defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ # define INLINE_KEYWORD inline #else # define INLINE_KEYWORD #endif -#if defined(__GNUC__)+#if defined(__GNUC__) || defined(__ICCARM__) # define FORCE_INLINE_ATTR __attribute__((always_inline)) #elif defined(_MSC_VER) # define FORCE_INLINE_ATTR __forceinline@@ -206,7 +187,12 @@ # define XXH_rotl32(x,r) _rotl(x,r) # define XXH_rotl64(x,r) _rotl64(x,r) #else+#if defined(__ICCARM__)+# include <intrinsics.h>+# define XXH_rotl32(x,r) __ROR(x,(32 - r))+#else # define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r)))+#endif # define XXH_rotl64(x,r) ((x << r) | (x >> (64 - r))) #endif @@ -723,7 +709,9 @@ state->total_len += len; if (state->memsize + len < 32) { /* fill in tmp buffer */- XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len);+ if (input != NULL) {+ XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len);+ } state->memsize += (U32)len; return XXH_OK; }
zstd/lib/common/xxhash.h view
@@ -1,35 +1,15 @@ /*- xxHash - Extremely Fast Hash algorithm- Header File- Copyright (C) 2012-2016, 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:-- * 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 :- - xxHash source repository : https://github.com/Cyan4973/xxHash+ * xxHash - Extremely Fast Hash algorithm+ * Header File+ * Copyright (c) 2012-2020, Yann Collet, Facebook, Inc.+ *+ * You can contact the author at :+ * - xxHash source repository : https://github.com/Cyan4973/xxHash+ * + * 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. */ /* Notice extracted from xxHash homepage :
zstd/lib/common/zstd_common.c view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the
zstd/lib/common/zstd_errors.h view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -76,6 +76,7 @@ /* 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_maxCode = 120 /* never EVER use this value directly, it can change in future versions! Use ZSTD_isError() instead */ } ZSTD_ErrorCode;
zstd/lib/common/zstd_internal.h view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -19,12 +19,15 @@ /*-************************************* * Dependencies ***************************************/+#ifdef __aarch64__+#include <arm_neon.h>+#endif #include "compiler.h" #include "mem.h" #include "debug.h" /* assert, DEBUGLOG, RAWLOG, g_debuglevel */ #include "error_private.h" #define ZSTD_STATIC_LINKING_ONLY-#include "zstd.h"+#include "../zstd.h" #define FSE_STATIC_LINKING_ONLY #include "fse.h" #define HUF_STATIC_LINKING_ONLY@@ -34,7 +37,6 @@ #endif #include "xxhash.h" /* XXH_reset, update, digest */ - #if defined (__cplusplus) extern "C" { #endif@@ -55,15 +57,42 @@ #define MAX(a,b) ((a)>(b) ? (a) : (b)) /**+ * Ignore: this is an internal helper.+ *+ * This is a helper function to help force C99-correctness during compilation.+ * Under strict compilation modes, variadic macro arguments can't be empty.+ * However, variadic function arguments can be. Using a function therefore lets+ * us statically check that at least one (string) argument was passed,+ * independent of the compilation flags.+ */+static INLINE_KEYWORD UNUSED_ATTR+void _force_has_format_string(const char *format, ...) {+ (void)format;+}++/**+ * Ignore: this is an internal helper.+ *+ * We want to force this function invocation to be syntactically correct, but+ * we don't want to force runtime evaluation of its arguments.+ */+#define _FORCE_HAS_FORMAT_STRING(...) \+ if (0) { \+ _force_has_format_string(__VA_ARGS__); \+ }++/** * Return the specified error if the condition evaluates to true. *- * In debug modes, prints additional information. In order to do that- * (particularly, printing the conditional that failed), this can't just wrap- * RETURN_ERROR().+ * In debug modes, prints additional information.+ * In order to do that (particularly, printing the conditional that failed),+ * this can't just wrap RETURN_ERROR(). */ #define RETURN_ERROR_IF(cond, err, ...) \ if (cond) { \- RAWLOG(3, "%s:%d: ERROR!: check %s failed, returning %s", __FILE__, __LINE__, ZSTD_QUOTE(cond), ZSTD_QUOTE(ERROR(err))); \+ RAWLOG(3, "%s:%d: ERROR!: check %s failed, returning %s", \+ __FILE__, __LINE__, ZSTD_QUOTE(cond), ZSTD_QUOTE(ERROR(err))); \+ _FORCE_HAS_FORMAT_STRING(__VA_ARGS__); \ RAWLOG(3, ": " __VA_ARGS__); \ RAWLOG(3, "\n"); \ return ERROR(err); \@@ -76,7 +105,9 @@ */ #define RETURN_ERROR(err, ...) \ do { \- RAWLOG(3, "%s:%d: ERROR!: unconditional check failed, returning %s", __FILE__, __LINE__, ZSTD_QUOTE(ERROR(err))); \+ RAWLOG(3, "%s:%d: ERROR!: unconditional check failed, returning %s", \+ __FILE__, __LINE__, ZSTD_QUOTE(ERROR(err))); \+ _FORCE_HAS_FORMAT_STRING(__VA_ARGS__); \ RAWLOG(3, ": " __VA_ARGS__); \ RAWLOG(3, "\n"); \ return ERROR(err); \@@ -91,7 +122,9 @@ do { \ size_t const err_code = (err); \ if (ERR_isError(err_code)) { \- RAWLOG(3, "%s:%d: ERROR!: forwarding error in %s: %s", __FILE__, __LINE__, ZSTD_QUOTE(err), ERR_getErrorName(err_code)); \+ RAWLOG(3, "%s:%d: ERROR!: forwarding error in %s: %s", \+ __FILE__, __LINE__, ZSTD_QUOTE(err), ERR_getErrorName(err_code)); \+ _FORCE_HAS_FORMAT_STRING(__VA_ARGS__); \ RAWLOG(3, ": " __VA_ARGS__); \ RAWLOG(3, "\n"); \ return err_code; \@@ -129,6 +162,8 @@ static const size_t ZSTD_blockHeaderSize = ZSTD_BLOCKHEADERSIZE; 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 */ @@ -192,33 +227,100 @@ /*-******************************************* * Shared functions to include for inlining *********************************************/-static void ZSTD_copy8(void* dst, const void* src) { memcpy(dst, src, 8); }+static void ZSTD_copy8(void* dst, const void* src) {+#ifdef __aarch64__+ vst1_u8((uint8_t*)dst, vld1_u8((const uint8_t*)src));+#else+ memcpy(dst, src, 8);+#endif+}+ #define COPY8(d,s) { ZSTD_copy8(d,s); d+=8; s+=8; }+static void ZSTD_copy16(void* dst, const void* src) {+#ifdef __aarch64__+ vst1q_u8((uint8_t*)dst, vld1q_u8((const uint8_t*)src));+#else+ memcpy(dst, src, 16);+#endif+}+#define COPY16(d,s) { ZSTD_copy16(d,s); d+=16; s+=16; } +#define WILDCOPY_OVERLENGTH 32+#define WILDCOPY_VECLEN 16++typedef enum {+ ZSTD_no_overlap,+ ZSTD_overlap_src_before_dst+ /* ZSTD_overlap_dst_before_src, */+} ZSTD_overlap_e;+ /*! ZSTD_wildcopy() :- * custom version of memcpy(), can overwrite up to WILDCOPY_OVERLENGTH bytes (if length==0) */-#define WILDCOPY_OVERLENGTH 8-MEM_STATIC void ZSTD_wildcopy(void* dst, const void* src, ptrdiff_t length)+ * Custom version of memcpy(), can over read/write up to WILDCOPY_OVERLENGTH bytes (if length==0)+ * @param ovtype controls the overlap detection+ * - ZSTD_no_overlap: The source and destination are guaranteed to be at least WILDCOPY_VECLEN bytes apart.+ * - ZSTD_overlap_src_before_dst: The src and dst may overlap, but they MUST be at least 8 bytes apart.+ * The src buffer must be before the dst buffer.+ */+MEM_STATIC FORCE_INLINE_ATTR +void ZSTD_wildcopy(void* dst, const void* src, ptrdiff_t length, ZSTD_overlap_e const ovtype) {+ ptrdiff_t diff = (BYTE*)dst - (const BYTE*)src; const BYTE* ip = (const BYTE*)src; BYTE* op = (BYTE*)dst; BYTE* const oend = op + length;- do- COPY8(op, ip)- while (op < oend);++ assert(diff >= 8 || (ovtype == ZSTD_no_overlap && diff <= -WILDCOPY_VECLEN));++ if (ovtype == ZSTD_overlap_src_before_dst && diff < WILDCOPY_VECLEN) {+ /* Handle short offset copies. */+ do {+ COPY8(op, ip)+ } while (op < oend);+ } else {+ assert(diff >= WILDCOPY_VECLEN || diff <= -WILDCOPY_VECLEN);+ /* Separate out the first COPY16() call because the copy length is+ * almost certain to be short, so the branches have different+ * probabilities. Since it is almost certain to be short, only do+ * one COPY16() in the first call. Then, do two calls per loop since+ * at that point it is more likely to have a high trip count.+ */+#ifndef __aarch64__+ do {+ COPY16(op, ip);+ }+ while (op < oend);+#else+ COPY16(op, ip);+ if (op >= oend) return;+ do {+ COPY16(op, ip);+ COPY16(op, ip);+ }+ while (op < oend);+#endif+ } } -MEM_STATIC void ZSTD_wildcopy_e(void* dst, const void* src, void* dstEnd) /* should be faster for decoding, but strangely, not verified on all platform */+MEM_STATIC size_t ZSTD_limitCopy(void* dst, size_t dstCapacity, const void* src, size_t srcSize) {- const BYTE* ip = (const BYTE*)src;- BYTE* op = (BYTE*)dst;- BYTE* const oend = (BYTE*)dstEnd;- do- COPY8(op, ip)- while (op < oend);+ size_t const length = MIN(dstCapacity, srcSize);+ if (length > 0) {+ memcpy(dst, src, length);+ }+ return length; } +/* define "workspace is too large" as this number of times larger than needed */+#define ZSTD_WORKSPACETOOLARGE_FACTOR 3 +/* when workspace is continuously too large+ * during at least this number of times,+ * context's memory usage is considered wasteful,+ * because it's sized to handle a worst case scenario which rarely happens.+ * In which case, resize it down to free some memory */+#define ZSTD_WORKSPACETOOLARGE_MAXDURATION 128++ /*-******************************************* * Private declarations *********************************************/@@ -242,7 +344,32 @@ U32 longLengthPos; } seqStore_t; +typedef struct {+ U32 litLength;+ U32 matchLength;+} ZSTD_sequenceLength;+ /**+ * Returns the ZSTD_sequenceLength for the given sequences. It handles the decoding of long sequences+ * indicated by longLengthPos and longLengthID, and adds MINMATCH back to matchLength.+ */+MEM_STATIC ZSTD_sequenceLength ZSTD_getSequenceLength(seqStore_t const* seqStore, seqDef const* seq)+{+ ZSTD_sequenceLength seqLen;+ seqLen.litLength = seq->litLength;+ seqLen.matchLength = seq->matchLength + MINMATCH;+ if (seqStore->longLengthPos == (U32)(seq - seqStore->sequencesStart)) {+ if (seqStore->longLengthID == 1) {+ seqLen.litLength += 0xFFFF;+ }+ if (seqStore->longLengthID == 2) {+ seqLen.matchLength += 0xFFFF;+ }+ }+ return seqLen;+}++/** * Contains the compressed frame size and an upper-bound for the decompressed frame size. * Note: before using `compressedSize`, check for errors using ZSTD_isError(). * similarly, before using `decompressedBound`, check for errors using:@@ -268,10 +395,11 @@ { # if defined(_MSC_VER) /* Visual */ unsigned long r=0;- _BitScanReverse(&r, val);- return (unsigned)r;+ return _BitScanReverse(&r, val) ? (unsigned)r : 0; # elif defined(__GNUC__) && (__GNUC__ >= 3) /* GCC Intrinsic */- return 31 - __builtin_clz(val);+ 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;
zstd/lib/compress/fse_compress.c view
@@ -1,35 +1,15 @@ /* ******************************************************************- FSE : Finite State Entropy encoder- Copyright (C) 2013-present, 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:-- * 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 :- - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy- - Public forum : https://groups.google.com/forum/#!forum/lz4c+ * FSE : Finite State Entropy encoder+ * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc.+ *+ * You can contact the author at :+ * - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy+ * - Public forum : https://groups.google.com/forum/#!forum/lz4c+ *+ * 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. ****************************************************************** */ /* **************************************************************@@ -37,14 +17,14 @@ ****************************************************************/ #include <stdlib.h> /* malloc, free, qsort */ #include <string.h> /* memcpy, memset */-#include "compiler.h"-#include "mem.h" /* U32, U16, etc. */-#include "debug.h" /* assert, DEBUGLOG */+#include "../common/compiler.h"+#include "../common/mem.h" /* U32, U16, etc. */+#include "../common/debug.h" /* assert, DEBUGLOG */ #include "hist.h" /* HIST_count_wksp */-#include "bitstream.h"+#include "../common/bitstream.h" #define FSE_STATIC_LINKING_ONLY-#include "fse.h"-#include "error_private.h"+#include "../common/fse.h"+#include "../common/error_private.h" /* **************************************************************@@ -644,9 +624,6 @@ size_t FSE_compressBound(size_t size) { return FSE_COMPRESSBOUND(size); }--#define CHECK_V_F(e, f) size_t const e = f; if (ERR_isError(e)) return e-#define CHECK_F(f) { CHECK_V_F(_var_err__, f); } /* FSE_compress_wksp() : * Same as FSE_compress2(), but using an externally allocated scratch buffer (`workSpace`).
zstd/lib/compress/hist.c view
@@ -1,42 +1,22 @@ /* ******************************************************************- hist : Histogram functions- part of Finite State Entropy project- Copyright (C) 2013-present, 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:-- * 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 :- - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy- - Public forum : https://groups.google.com/forum/#!forum/lz4c+ * hist : Histogram functions+ * part of Finite State Entropy project+ * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc.+ *+ * You can contact the author at :+ * - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy+ * - Public forum : https://groups.google.com/forum/#!forum/lz4c+ *+ * 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 "mem.h" /* U32, BYTE, etc. */-#include "debug.h" /* assert, DEBUGLOG */-#include "error_private.h" /* ERROR */+#include "../common/mem.h" /* U32, BYTE, etc. */+#include "../common/debug.h" /* assert, DEBUGLOG */+#include "../common/error_private.h" /* ERROR */ #include "hist.h"
zstd/lib/compress/hist.h view
@@ -1,36 +1,16 @@ /* ******************************************************************- hist : Histogram functions- part of Finite State Entropy project- Copyright (C) 2013-present, 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:-- * 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 :- - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy- - Public forum : https://groups.google.com/forum/#!forum/lz4c+ * hist : Histogram functions+ * part of Finite State Entropy project+ * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc.+ *+ * You can contact the author at :+ * - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy+ * - Public forum : https://groups.google.com/forum/#!forum/lz4c+ *+ * 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 --- */
zstd/lib/compress/huf_compress.c view
@@ -1,35 +1,15 @@ /* ******************************************************************- Huffman encoder, part of New Generation Entropy library- Copyright (C) 2013-2016, 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:-- * 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 :- - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy- - Public forum : https://groups.google.com/forum/#!forum/lz4c+ * Huffman encoder, part of New Generation Entropy library+ * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc.+ *+ * You can contact the author at :+ * - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy+ * - Public forum : https://groups.google.com/forum/#!forum/lz4c+ *+ * 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. ****************************************************************** */ /* **************************************************************@@ -45,14 +25,14 @@ ****************************************************************/ #include <string.h> /* memcpy, memset */ #include <stdio.h> /* printf (debug) */-#include "compiler.h"-#include "bitstream.h"+#include "../common/compiler.h"+#include "../common/bitstream.h" #include "hist.h" #define FSE_STATIC_LINKING_ONLY /* FSE_optimalTableLog_internal */-#include "fse.h" /* header compression */+#include "../common/fse.h" /* header compression */ #define HUF_STATIC_LINKING_ONLY-#include "huf.h"-#include "error_private.h"+#include "../common/huf.h"+#include "../common/error_private.h" /* **************************************************************@@ -60,8 +40,6 @@ ****************************************************************/ #define HUF_isError ERR_isError #define HUF_STATIC_ASSERT(c) DEBUG_STATIC_ASSERT(c) /* use only *after* variable declarations */-#define CHECK_V_F(e, f) size_t const e = f; if (ERR_isError(e)) return e-#define CHECK_F(f) { CHECK_V_F(_var_err__, f); } /* **************************************************************@@ -110,18 +88,18 @@ CHECK_F( FSE_normalizeCount(norm, tableLog, count, wtSize, maxSymbolValue) ); /* Write table description header */- { CHECK_V_F(hSize, FSE_writeNCount(op, oend-op, norm, maxSymbolValue, tableLog) );+ { CHECK_V_F(hSize, FSE_writeNCount(op, (size_t)(oend-op), norm, maxSymbolValue, tableLog) ); op += hSize; } /* Compress */ CHECK_F( FSE_buildCTable_wksp(CTable, norm, maxSymbolValue, tableLog, scratchBuffer, sizeof(scratchBuffer)) );- { CHECK_V_F(cSize, FSE_compress_usingCTable(op, oend - op, weightTable, wtSize, CTable) );+ { CHECK_V_F(cSize, FSE_compress_usingCTable(op, (size_t)(oend - op), weightTable, wtSize, CTable) ); if (cSize == 0) return 0; /* not enough space for compressed data */ op += cSize; } - return op-ostart;+ return (size_t)(op-ostart); } @@ -169,7 +147,7 @@ } -size_t HUF_readCTable (HUF_CElt* CTable, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize)+size_t HUF_readCTable (HUF_CElt* CTable, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize, unsigned* hasZeroWeights) { BYTE huffWeight[HUF_SYMBOLVALUE_MAX + 1]; /* init not required, even though some static analyzer may complain */ U32 rankVal[HUF_TABLELOG_ABSOLUTEMAX + 1]; /* large enough for values from 0 to 16 */@@ -192,9 +170,11 @@ } } /* fill nbBits */+ *hasZeroWeights = 0; { U32 n; for (n=0; n<nbSymbols; n++) { const U32 w = huffWeight[n];- CTable[n].nbBits = (BYTE)(tableLog + 1 - w);+ *hasZeroWeights |= (w == 0);+ CTable[n].nbBits = (BYTE)(tableLog + 1 - w) & -(w != 0); } } /* fill val */@@ -240,7 +220,7 @@ /* there are several too large elements (at least >= 2) */ { int totalCost = 0; const U32 baseCost = 1 << (largestBits - maxNbBits);- U32 n = lastNonNull;+ int n = (int)lastNonNull; while (huffNode[n].nbBits > maxNbBits) { totalCost += baseCost - (1 << (largestBits - huffNode[n].nbBits));@@ -255,22 +235,22 @@ /* repay normalized cost */ { U32 const noSymbol = 0xF0F0F0F0; U32 rankLast[HUF_TABLELOG_MAX+2];- int pos; /* Get pos of last (smallest) symbol per rank */ memset(rankLast, 0xF0, sizeof(rankLast)); { U32 currentNbBits = maxNbBits;+ int pos; for (pos=n ; pos >= 0; pos--) { if (huffNode[pos].nbBits >= currentNbBits) continue; currentNbBits = huffNode[pos].nbBits; /* < maxNbBits */- rankLast[maxNbBits-currentNbBits] = pos;+ rankLast[maxNbBits-currentNbBits] = (U32)pos; } } while (totalCost > 0) {- U32 nBitsToDecrease = BIT_highbit32(totalCost) + 1;+ U32 nBitsToDecrease = BIT_highbit32((U32)totalCost) + 1; for ( ; nBitsToDecrease > 1; nBitsToDecrease--) {- U32 highPos = rankLast[nBitsToDecrease];- U32 lowPos = rankLast[nBitsToDecrease-1];+ U32 const highPos = rankLast[nBitsToDecrease];+ U32 const lowPos = rankLast[nBitsToDecrease-1]; if (highPos == noSymbol) continue; if (lowPos == noSymbol) break; { U32 const highTotal = huffNode[highPos].count;@@ -297,7 +277,8 @@ if (rankLast[1] == noSymbol) { /* special case : no rank 1 symbol (using maxNbBits-1); let's create one from largest rank 0 (using maxNbBits) */ while (huffNode[n].nbBits == maxNbBits) n--; huffNode[n+1].nbBits--;- rankLast[1] = n+1;+ assert(n >= 0);+ rankLast[1] = (U32)(n+1); totalCost++; continue; }@@ -309,29 +290,36 @@ return maxNbBits; } - typedef struct { U32 base; U32 current; } rankPos; -static void HUF_sort(nodeElt* huffNode, const unsigned* count, U32 maxSymbolValue)+typedef nodeElt huffNodeTable[HUF_CTABLE_WORKSPACE_SIZE_U32];++#define RANK_POSITION_TABLE_SIZE 32++typedef struct {+ huffNodeTable huffNodeTbl;+ rankPos rankPosition[RANK_POSITION_TABLE_SIZE];+} HUF_buildCTable_wksp_tables;++static void HUF_sort(nodeElt* huffNode, const unsigned* count, U32 maxSymbolValue, rankPos* rankPosition) {- rankPos rank[32]; U32 n; - memset(rank, 0, sizeof(rank));+ memset(rankPosition, 0, sizeof(*rankPosition) * RANK_POSITION_TABLE_SIZE); for (n=0; n<=maxSymbolValue; n++) { U32 r = BIT_highbit32(count[n] + 1);- rank[r].base ++;+ rankPosition[r].base ++; }- for (n=30; n>0; n--) rank[n-1].base += rank[n].base;- for (n=0; n<32; n++) rank[n].current = rank[n].base;+ for (n=30; n>0; n--) rankPosition[n-1].base += rankPosition[n].base;+ for (n=0; n<32; n++) rankPosition[n].current = rankPosition[n].base; for (n=0; n<=maxSymbolValue; n++) { U32 const c = count[n]; U32 const r = BIT_highbit32(c+1) + 1;- U32 pos = rank[r].current++;- while ((pos > rank[r].base) && (c > huffNode[pos-1].count)) {+ U32 pos = rankPosition[r].current++;+ while ((pos > rankPosition[r].base) && (c > huffNode[pos-1].count)) { huffNode[pos] = huffNode[pos-1]; pos--; }@@ -343,45 +331,48 @@ /** 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 a table of HUF_CTABLE_WORKSPACE_SIZE_U32 unsigned.+ * `workSpace` must be aligned on 4-bytes boundaries, and be at least as large as sizeof(HUF_buildCTable_wksp_tables). */ #define STARTNODE (HUF_SYMBOLVALUE_MAX+1)-typedef nodeElt huffNodeTable[HUF_CTABLE_WORKSPACE_SIZE_U32];+ size_t HUF_buildCTable_wksp (HUF_CElt* tree, const unsigned* count, U32 maxSymbolValue, U32 maxNbBits, void* workSpace, size_t wkspSize) {- nodeElt* const huffNode0 = (nodeElt*)workSpace;+ HUF_buildCTable_wksp_tables* const wksp_tables = (HUF_buildCTable_wksp_tables*)workSpace;+ nodeElt* const huffNode0 = wksp_tables->huffNodeTbl; nodeElt* const huffNode = huffNode0+1;- U32 n, nonNullRank;+ int nonNullRank; int lowS, lowN;- U16 nodeNb = STARTNODE;- U32 nodeRoot;+ int nodeNb = STARTNODE;+ int n, nodeRoot; /* safety checks */ if (((size_t)workSpace & 3) != 0) return ERROR(GENERIC); /* must be aligned on 4-bytes boundaries */- if (wkspSize < sizeof(huffNodeTable)) return ERROR(workSpace_tooSmall);+ if (wkspSize < sizeof(HUF_buildCTable_wksp_tables))+ return ERROR(workSpace_tooSmall); if (maxNbBits == 0) maxNbBits = HUF_TABLELOG_DEFAULT;- if (maxSymbolValue > HUF_SYMBOLVALUE_MAX) return ERROR(maxSymbolValue_tooLarge);+ if (maxSymbolValue > HUF_SYMBOLVALUE_MAX)+ return ERROR(maxSymbolValue_tooLarge); memset(huffNode0, 0, sizeof(huffNodeTable)); /* sort, decreasing order */- HUF_sort(huffNode, count, maxSymbolValue);+ HUF_sort(huffNode, count, maxSymbolValue, wksp_tables->rankPosition); /* init for parents */- nonNullRank = maxSymbolValue;+ nonNullRank = (int)maxSymbolValue; while(huffNode[nonNullRank].count == 0) nonNullRank--; lowS = nonNullRank; nodeRoot = nodeNb + lowS - 1; lowN = nodeNb; huffNode[nodeNb].count = huffNode[lowS].count + huffNode[lowS-1].count;- huffNode[lowS].parent = huffNode[lowS-1].parent = nodeNb;+ huffNode[lowS].parent = huffNode[lowS-1].parent = (U16)nodeNb; nodeNb++; lowS-=2; for (n=nodeNb; n<=nodeRoot; n++) huffNode[n].count = (U32)(1U<<30); huffNode0[0].count = (U32)(1U<<31); /* fake entry, strong barrier */ /* create parents */ while (nodeNb <= nodeRoot) {- U32 n1 = (huffNode[lowS].count < huffNode[lowN].count) ? lowS-- : lowN++;- U32 n2 = (huffNode[lowS].count < huffNode[lowN].count) ? lowS-- : lowN++;+ int const n1 = (huffNode[lowS].count < huffNode[lowN].count) ? lowS-- : lowN++;+ int const n2 = (huffNode[lowS].count < huffNode[lowN].count) ? lowS-- : lowN++; huffNode[nodeNb].count = huffNode[n1].count + huffNode[n2].count;- huffNode[n1].parent = huffNode[n2].parent = nodeNb;+ huffNode[n1].parent = huffNode[n2].parent = (U16)nodeNb; nodeNb++; } @@ -393,24 +384,25 @@ huffNode[n].nbBits = huffNode[ huffNode[n].parent ].nbBits + 1; /* enforce maxTableLog */- maxNbBits = HUF_setMaxHeight(huffNode, nonNullRank, maxNbBits);+ maxNbBits = HUF_setMaxHeight(huffNode, (U32)nonNullRank, maxNbBits); /* fill result into tree (val, nbBits) */ { U16 nbPerRank[HUF_TABLELOG_MAX+1] = {0}; U16 valPerRank[HUF_TABLELOG_MAX+1] = {0};+ int const alphabetSize = (int)(maxSymbolValue + 1); if (maxNbBits > HUF_TABLELOG_MAX) return ERROR(GENERIC); /* check fit into table */ for (n=0; n<=nonNullRank; n++) nbPerRank[huffNode[n].nbBits]++; /* determine stating value per rank */ { U16 min = 0;- for (n=maxNbBits; n>0; n--) {+ for (n=(int)maxNbBits; n>0; n--) { valPerRank[n] = min; /* get starting value within each rank */ min += nbPerRank[n]; min >>= 1; } }- for (n=0; n<=maxSymbolValue; n++)+ for (n=0; n<alphabetSize; n++) tree[huffNode[n].byte].nbBits = huffNode[n].nbBits; /* push nbBits per symbol, symbol order */- for (n=0; n<=maxSymbolValue; n++)+ for (n=0; n<alphabetSize; n++) tree[n].val = valPerRank[tree[n].nbBits]++; /* assign value within rank, symbol order */ } @@ -423,11 +415,11 @@ */ size_t HUF_buildCTable (HUF_CElt* tree, const unsigned* count, unsigned maxSymbolValue, unsigned maxNbBits) {- huffNodeTable nodeTable;- return HUF_buildCTable_wksp(tree, count, maxSymbolValue, maxNbBits, nodeTable, sizeof(nodeTable));+ HUF_buildCTable_wksp_tables workspace;+ return HUF_buildCTable_wksp(tree, count, maxSymbolValue, maxNbBits, &workspace, sizeof(workspace)); } -static size_t HUF_estimateCompressedSize(HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue)+size_t HUF_estimateCompressedSize(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue) { size_t nbBits = 0; int s;@@ -437,7 +429,7 @@ return nbBits >> 3; } -static int HUF_validateCTable(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue) {+int HUF_validateCTable(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue) { int bad = 0; int s; for (s = 0; s <= (int)maxSymbolValue; ++s) {@@ -476,7 +468,7 @@ /* init */ if (dstSize < 8) return 0; /* not enough space to compress */- { size_t const initErr = BIT_initCStream(&bitC, op, oend-op);+ { size_t const initErr = BIT_initCStream(&bitC, op, (size_t)(oend-op)); if (HUF_isError(initErr)) return 0; } n = srcSize & ~3; /* join to mod 4 */@@ -573,7 +565,8 @@ if (srcSize < 12) return 0; /* no saving possible : too small input */ op += 6; /* jumpTable */ - { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, oend-op, ip, segmentSize, CTable, bmi2) );+ assert(op <= oend);+ { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, bmi2) ); if (cSize==0) return 0; assert(cSize <= 65535); MEM_writeLE16(ostart, (U16)cSize);@@ -581,7 +574,8 @@ } ip += segmentSize;- { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, oend-op, ip, segmentSize, CTable, bmi2) );+ assert(op <= oend);+ { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, bmi2) ); if (cSize==0) return 0; assert(cSize <= 65535); MEM_writeLE16(ostart+2, (U16)cSize);@@ -589,7 +583,8 @@ } ip += segmentSize;- { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, oend-op, ip, segmentSize, CTable, bmi2) );+ assert(op <= oend);+ { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, bmi2) ); if (cSize==0) return 0; assert(cSize <= 65535); MEM_writeLE16(ostart+4, (U16)cSize);@@ -597,12 +592,14 @@ } ip += segmentSize;- { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, oend-op, ip, iend-ip, CTable, bmi2) );+ 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) ); if (cSize==0) return 0; op += cSize; } - return op-ostart;+ 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)@@ -618,20 +615,21 @@ HUF_nbStreams_e nbStreams, const HUF_CElt* CTable, const int bmi2) { size_t const cSize = (nbStreams==HUF_singleStream) ?- HUF_compress1X_usingCTable_internal(op, oend - op, src, srcSize, CTable, bmi2) :- HUF_compress4X_usingCTable_internal(op, oend - op, src, srcSize, CTable, bmi2);+ 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); if (HUF_isError(cSize)) { return cSize; } if (cSize==0) { return 0; } /* uncompressible */ op += cSize; /* check compressibility */+ assert(op >= ostart); if ((size_t)(op-ostart) >= srcSize-1) { return 0; }- return op-ostart;+ return (size_t)(op-ostart); } typedef struct { unsigned count[HUF_SYMBOLVALUE_MAX + 1]; HUF_CElt CTable[HUF_SYMBOLVALUE_MAX + 1];- huffNodeTable nodeTable;+ HUF_buildCTable_wksp_tables buildCTable_wksp; } HUF_compress_tables_t; /* HUF_compress_internal() :@@ -650,6 +648,8 @@ BYTE* const oend = ostart + dstSize; BYTE* op = ostart; + HUF_STATIC_ASSERT(sizeof(*table) <= HUF_WORKSPACE_SIZE);+ /* checks & inits */ if (((size_t)workSpace & 3) != 0) return ERROR(GENERIC); /* must be aligned on 4-bytes boundaries */ if (wkspSize < HUF_WORKSPACE_SIZE) return ERROR(workSpace_tooSmall);@@ -691,7 +691,7 @@ huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue); { size_t const maxBits = HUF_buildCTable_wksp(table->CTable, table->count, maxSymbolValue, huffLog,- table->nodeTable, sizeof(table->nodeTable));+ &table->buildCTable_wksp, sizeof(table->buildCTable_wksp)); CHECK_F(maxBits); huffLog = (U32)maxBits; /* Zero unused symbols in CTable, so we can check it for validity */
zstd/lib/compress/zstd_compress.c view
@@ -1,4417 +1,4278 @@ /*- * Copyright (c) 2016-present, 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 <limits.h> /* INT_MAX */-#include <string.h> /* memset */-#include "cpu.h"-#include "mem.h"-#include "hist.h" /* HIST_countFast_wksp */-#define FSE_STATIC_LINKING_ONLY /* FSE_encodeSymbol */-#include "fse.h"-#define HUF_STATIC_LINKING_ONLY-#include "huf.h"-#include "zstd_compress_internal.h"-#include "zstd_fast.h"-#include "zstd_double_fast.h"-#include "zstd_lazy.h"-#include "zstd_opt.h"-#include "zstd_ldm.h"---/*-*************************************-* Helper functions-***************************************/-size_t ZSTD_compressBound(size_t srcSize) {- return ZSTD_COMPRESSBOUND(srcSize);-}---/*-*************************************-* Context memory management-***************************************/-struct ZSTD_CDict_s {- void* dictBuffer;- const void* dictContent;- size_t dictContentSize;- void* workspace;- size_t workspaceSize;- ZSTD_matchState_t matchState;- ZSTD_compressedBlockState_t cBlockState;- ZSTD_customMem customMem;- U32 dictID;-}; /* typedef'd to ZSTD_CDict within "zstd.h" */--ZSTD_CCtx* ZSTD_createCCtx(void)-{- return ZSTD_createCCtx_advanced(ZSTD_defaultCMem);-}--static void ZSTD_initCCtx(ZSTD_CCtx* cctx, ZSTD_customMem memManager)-{- assert(cctx != NULL);- memset(cctx, 0, sizeof(*cctx));- cctx->customMem = memManager;- cctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid());- { size_t const err = ZSTD_CCtx_reset(cctx, ZSTD_reset_parameters);- assert(!ZSTD_isError(err));- (void)err;- }-}--ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem)-{- ZSTD_STATIC_ASSERT(zcss_init==0);- ZSTD_STATIC_ASSERT(ZSTD_CONTENTSIZE_UNKNOWN==(0ULL - 1));- if (!customMem.customAlloc ^ !customMem.customFree) return NULL;- { ZSTD_CCtx* const cctx = (ZSTD_CCtx*)ZSTD_malloc(sizeof(ZSTD_CCtx), customMem);- if (!cctx) return NULL;- ZSTD_initCCtx(cctx, customMem);- return cctx;- }-}--ZSTD_CCtx* ZSTD_initStaticCCtx(void *workspace, size_t workspaceSize)-{- ZSTD_CCtx* const cctx = (ZSTD_CCtx*) workspace;- if (workspaceSize <= sizeof(ZSTD_CCtx)) return NULL; /* minimum size */- if ((size_t)workspace & 7) return NULL; /* must be 8-aligned */- memset(workspace, 0, workspaceSize); /* may be a bit generous, could memset be smaller ? */- cctx->staticSize = workspaceSize;- cctx->workSpace = (void*)(cctx+1);- cctx->workSpaceSize = workspaceSize - sizeof(ZSTD_CCtx);-- /* statically sized space. entropyWorkspace never moves (but prev/next block swap places) */- if (cctx->workSpaceSize < HUF_WORKSPACE_SIZE + 2 * sizeof(ZSTD_compressedBlockState_t)) return NULL;- assert(((size_t)cctx->workSpace & (sizeof(void*)-1)) == 0); /* ensure correct alignment */- cctx->blockState.prevCBlock = (ZSTD_compressedBlockState_t*)cctx->workSpace;- cctx->blockState.nextCBlock = cctx->blockState.prevCBlock + 1;- {- void* const ptr = cctx->blockState.nextCBlock + 1;- cctx->entropyWorkspace = (U32*)ptr;- }- cctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid());- return cctx;-}--/**- * Clears and frees all of the dictionaries in the CCtx.- */-static void ZSTD_clearAllDicts(ZSTD_CCtx* cctx)-{- ZSTD_free(cctx->localDict.dictBuffer, cctx->customMem);- ZSTD_freeCDict(cctx->localDict.cdict);- memset(&cctx->localDict, 0, sizeof(cctx->localDict));- memset(&cctx->prefixDict, 0, sizeof(cctx->prefixDict));- cctx->cdict = NULL;-}--static size_t ZSTD_sizeof_localDict(ZSTD_localDict dict)-{- size_t const bufferSize = dict.dictBuffer != NULL ? dict.dictSize : 0;- size_t const cdictSize = ZSTD_sizeof_CDict(dict.cdict);- return bufferSize + cdictSize;-}--static void ZSTD_freeCCtxContent(ZSTD_CCtx* cctx)-{- assert(cctx != NULL);- assert(cctx->staticSize == 0);- ZSTD_free(cctx->workSpace, cctx->customMem); cctx->workSpace = NULL;- ZSTD_clearAllDicts(cctx);-#ifdef ZSTD_MULTITHREAD- ZSTDMT_freeCCtx(cctx->mtctx); cctx->mtctx = NULL;-#endif-}--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");- ZSTD_freeCCtxContent(cctx);- ZSTD_free(cctx, cctx->customMem);- return 0;-}---static size_t ZSTD_sizeof_mtctx(const ZSTD_CCtx* cctx)-{-#ifdef ZSTD_MULTITHREAD- return ZSTDMT_sizeof_CCtx(cctx->mtctx);-#else- (void)cctx;- return 0;-#endif-}---size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx)-{- if (cctx==NULL) return 0; /* support sizeof on NULL */- return sizeof(*cctx) + cctx->workSpaceSize- + ZSTD_sizeof_localDict(cctx->localDict)- + ZSTD_sizeof_mtctx(cctx);-}--size_t ZSTD_sizeof_CStream(const ZSTD_CStream* zcs)-{- return ZSTD_sizeof_CCtx(zcs); /* same object */-}--/* private API call, for dictBuilder only */-const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx) { return &(ctx->seqStore); }--static ZSTD_CCtx_params ZSTD_makeCCtxParamsFromCParams(- ZSTD_compressionParameters cParams)-{- ZSTD_CCtx_params cctxParams;- memset(&cctxParams, 0, sizeof(cctxParams));- cctxParams.cParams = cParams;- cctxParams.compressionLevel = ZSTD_CLEVEL_DEFAULT; /* should not matter, as all cParams are presumed properly defined */- assert(!ZSTD_checkCParams(cParams));- cctxParams.fParams.contentSizeFlag = 1;- return cctxParams;-}--static ZSTD_CCtx_params* ZSTD_createCCtxParams_advanced(- ZSTD_customMem customMem)-{- ZSTD_CCtx_params* params;- if (!customMem.customAlloc ^ !customMem.customFree) return NULL;- params = (ZSTD_CCtx_params*)ZSTD_calloc(- sizeof(ZSTD_CCtx_params), customMem);- if (!params) { return NULL; }- params->customMem = customMem;- params->compressionLevel = ZSTD_CLEVEL_DEFAULT;- params->fParams.contentSizeFlag = 1;- return params;-}--ZSTD_CCtx_params* ZSTD_createCCtxParams(void)-{- return ZSTD_createCCtxParams_advanced(ZSTD_defaultCMem);-}--size_t ZSTD_freeCCtxParams(ZSTD_CCtx_params* params)-{- if (params == NULL) { return 0; }- ZSTD_free(params, params->customMem);- return 0;-}--size_t ZSTD_CCtxParams_reset(ZSTD_CCtx_params* params)-{- return ZSTD_CCtxParams_init(params, ZSTD_CLEVEL_DEFAULT);-}--size_t ZSTD_CCtxParams_init(ZSTD_CCtx_params* cctxParams, int compressionLevel) {- RETURN_ERROR_IF(!cctxParams, GENERIC);- memset(cctxParams, 0, sizeof(*cctxParams));- cctxParams->compressionLevel = compressionLevel;- cctxParams->fParams.contentSizeFlag = 1;- return 0;-}--size_t ZSTD_CCtxParams_init_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params)-{- RETURN_ERROR_IF(!cctxParams, GENERIC);- FORWARD_IF_ERROR( ZSTD_checkCParams(params.cParams) );- memset(cctxParams, 0, sizeof(*cctxParams));- cctxParams->cParams = params.cParams;- cctxParams->fParams = params.fParams;- cctxParams->compressionLevel = ZSTD_CLEVEL_DEFAULT; /* should not matter, as all cParams are presumed properly defined */- assert(!ZSTD_checkCParams(params.cParams));- return 0;-}--/* ZSTD_assignParamsToCCtxParams() :- * params is presumed valid at this stage */-static ZSTD_CCtx_params ZSTD_assignParamsToCCtxParams(- ZSTD_CCtx_params cctxParams, ZSTD_parameters params)-{- ZSTD_CCtx_params ret = cctxParams;- ret.cParams = params.cParams;- ret.fParams = params.fParams;- ret.compressionLevel = ZSTD_CLEVEL_DEFAULT; /* should not matter, as all cParams are presumed properly defined */- assert(!ZSTD_checkCParams(params.cParams));- return ret;-}--ZSTD_bounds ZSTD_cParam_getBounds(ZSTD_cParameter param)-{- ZSTD_bounds bounds = { 0, 0, 0 };-- switch(param)- {- case ZSTD_c_compressionLevel:- bounds.lowerBound = ZSTD_minCLevel();- bounds.upperBound = ZSTD_maxCLevel();- return bounds;-- case ZSTD_c_windowLog:- bounds.lowerBound = ZSTD_WINDOWLOG_MIN;- bounds.upperBound = ZSTD_WINDOWLOG_MAX;- return bounds;-- case ZSTD_c_hashLog:- bounds.lowerBound = ZSTD_HASHLOG_MIN;- bounds.upperBound = ZSTD_HASHLOG_MAX;- return bounds;-- case ZSTD_c_chainLog:- bounds.lowerBound = ZSTD_CHAINLOG_MIN;- bounds.upperBound = ZSTD_CHAINLOG_MAX;- return bounds;-- case ZSTD_c_searchLog:- bounds.lowerBound = ZSTD_SEARCHLOG_MIN;- bounds.upperBound = ZSTD_SEARCHLOG_MAX;- return bounds;-- case ZSTD_c_minMatch:- bounds.lowerBound = ZSTD_MINMATCH_MIN;- bounds.upperBound = ZSTD_MINMATCH_MAX;- return bounds;-- case ZSTD_c_targetLength:- bounds.lowerBound = ZSTD_TARGETLENGTH_MIN;- bounds.upperBound = ZSTD_TARGETLENGTH_MAX;- return bounds;-- case ZSTD_c_strategy:- bounds.lowerBound = ZSTD_STRATEGY_MIN;- bounds.upperBound = ZSTD_STRATEGY_MAX;- return bounds;-- case ZSTD_c_contentSizeFlag:- bounds.lowerBound = 0;- bounds.upperBound = 1;- return bounds;-- case ZSTD_c_checksumFlag:- bounds.lowerBound = 0;- bounds.upperBound = 1;- return bounds;-- case ZSTD_c_dictIDFlag:- bounds.lowerBound = 0;- bounds.upperBound = 1;- return bounds;-- case ZSTD_c_nbWorkers:- bounds.lowerBound = 0;-#ifdef ZSTD_MULTITHREAD- bounds.upperBound = ZSTDMT_NBWORKERS_MAX;-#else- bounds.upperBound = 0;-#endif- return bounds;-- case ZSTD_c_jobSize:- bounds.lowerBound = 0;-#ifdef ZSTD_MULTITHREAD- bounds.upperBound = ZSTDMT_JOBSIZE_MAX;-#else- bounds.upperBound = 0;-#endif- return bounds;-- case ZSTD_c_overlapLog:- bounds.lowerBound = ZSTD_OVERLAPLOG_MIN;- bounds.upperBound = ZSTD_OVERLAPLOG_MAX;- return bounds;-- case ZSTD_c_enableLongDistanceMatching:- bounds.lowerBound = 0;- bounds.upperBound = 1;- return bounds;-- case ZSTD_c_ldmHashLog:- bounds.lowerBound = ZSTD_LDM_HASHLOG_MIN;- bounds.upperBound = ZSTD_LDM_HASHLOG_MAX;- return bounds;-- case ZSTD_c_ldmMinMatch:- bounds.lowerBound = ZSTD_LDM_MINMATCH_MIN;- bounds.upperBound = ZSTD_LDM_MINMATCH_MAX;- return bounds;-- case ZSTD_c_ldmBucketSizeLog:- bounds.lowerBound = ZSTD_LDM_BUCKETSIZELOG_MIN;- bounds.upperBound = ZSTD_LDM_BUCKETSIZELOG_MAX;- return bounds;-- case ZSTD_c_ldmHashRateLog:- bounds.lowerBound = ZSTD_LDM_HASHRATELOG_MIN;- bounds.upperBound = ZSTD_LDM_HASHRATELOG_MAX;- return bounds;-- /* experimental parameters */- case ZSTD_c_rsyncable:- bounds.lowerBound = 0;- bounds.upperBound = 1;- return bounds;-- case ZSTD_c_forceMaxWindow :- bounds.lowerBound = 0;- bounds.upperBound = 1;- return bounds;-- case ZSTD_c_format:- ZSTD_STATIC_ASSERT(ZSTD_f_zstd1 < ZSTD_f_zstd1_magicless);- bounds.lowerBound = ZSTD_f_zstd1;- bounds.upperBound = ZSTD_f_zstd1_magicless; /* note : how to ensure at compile time that this is the highest value enum ? */- return bounds;-- case ZSTD_c_forceAttachDict:- ZSTD_STATIC_ASSERT(ZSTD_dictDefaultAttach < ZSTD_dictForceCopy);- bounds.lowerBound = ZSTD_dictDefaultAttach;- bounds.upperBound = ZSTD_dictForceCopy; /* note : how to ensure at compile time that this is the highest value enum ? */- return bounds;-- case ZSTD_c_literalCompressionMode:- ZSTD_STATIC_ASSERT(ZSTD_lcm_auto < ZSTD_lcm_huffman && ZSTD_lcm_huffman < ZSTD_lcm_uncompressed);- bounds.lowerBound = ZSTD_lcm_auto;- bounds.upperBound = ZSTD_lcm_uncompressed;- return bounds;-- default:- { ZSTD_bounds const boundError = { ERROR(parameter_unsupported), 0, 0 };- return boundError;- }- }-}--/* ZSTD_cParam_withinBounds:- * @return 1 if value is within cParam bounds,- * 0 otherwise */-static int ZSTD_cParam_withinBounds(ZSTD_cParameter cParam, int value)-{- ZSTD_bounds const bounds = ZSTD_cParam_getBounds(cParam);- if (ZSTD_isError(bounds.error)) return 0;- if (value < bounds.lowerBound) return 0;- if (value > bounds.upperBound) return 0;- return 1;-}--/* ZSTD_cParam_clampBounds:- * Clamps the value into the bounded range.- */-static size_t ZSTD_cParam_clampBounds(ZSTD_cParameter cParam, int* value)-{- ZSTD_bounds const bounds = ZSTD_cParam_getBounds(cParam);- if (ZSTD_isError(bounds.error)) return bounds.error;- if (*value < bounds.lowerBound) *value = bounds.lowerBound;- if (*value > bounds.upperBound) *value = bounds.upperBound;- return 0;-}--#define BOUNDCHECK(cParam, val) { \- RETURN_ERROR_IF(!ZSTD_cParam_withinBounds(cParam,val), \- parameter_outOfBound); \-}---static int ZSTD_isUpdateAuthorized(ZSTD_cParameter param)-{- switch(param)- {- case ZSTD_c_compressionLevel:- case ZSTD_c_hashLog:- case ZSTD_c_chainLog:- case ZSTD_c_searchLog:- case ZSTD_c_minMatch:- case ZSTD_c_targetLength:- case ZSTD_c_strategy:- return 1;-- case ZSTD_c_format:- case ZSTD_c_windowLog:- case ZSTD_c_contentSizeFlag:- case ZSTD_c_checksumFlag:- case ZSTD_c_dictIDFlag:- case ZSTD_c_forceMaxWindow :- case ZSTD_c_nbWorkers:- case ZSTD_c_jobSize:- case ZSTD_c_overlapLog:- case ZSTD_c_rsyncable:- case ZSTD_c_enableLongDistanceMatching:- case ZSTD_c_ldmHashLog:- case ZSTD_c_ldmMinMatch:- case ZSTD_c_ldmBucketSizeLog:- case ZSTD_c_ldmHashRateLog:- case ZSTD_c_forceAttachDict:- case ZSTD_c_literalCompressionMode:- default:- return 0;- }-}--size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, int value)-{- DEBUGLOG(4, "ZSTD_CCtx_setParameter (%i, %i)", (int)param, value);- if (cctx->streamStage != zcss_init) {- if (ZSTD_isUpdateAuthorized(param)) {- cctx->cParamsChanged = 1;- } else {- RETURN_ERROR(stage_wrong);- } }-- switch(param)- {- case ZSTD_c_nbWorkers:- RETURN_ERROR_IF((value!=0) && cctx->staticSize, parameter_unsupported,- "MT not compatible with static alloc");- break;-- case ZSTD_c_compressionLevel:- case ZSTD_c_windowLog:- case ZSTD_c_hashLog:- case ZSTD_c_chainLog:- case ZSTD_c_searchLog:- case ZSTD_c_minMatch:- case ZSTD_c_targetLength:- case ZSTD_c_strategy:- case ZSTD_c_ldmHashRateLog:- case ZSTD_c_format:- case ZSTD_c_contentSizeFlag:- case ZSTD_c_checksumFlag:- case ZSTD_c_dictIDFlag:- case ZSTD_c_forceMaxWindow:- case ZSTD_c_forceAttachDict:- case ZSTD_c_literalCompressionMode:- case ZSTD_c_jobSize:- case ZSTD_c_overlapLog:- case ZSTD_c_rsyncable:- case ZSTD_c_enableLongDistanceMatching:- case ZSTD_c_ldmHashLog:- case ZSTD_c_ldmMinMatch:- case ZSTD_c_ldmBucketSizeLog:- break;-- default: RETURN_ERROR(parameter_unsupported);- }- return ZSTD_CCtxParams_setParameter(&cctx->requestedParams, param, value);-}--size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* CCtxParams,- ZSTD_cParameter param, int value)-{- DEBUGLOG(4, "ZSTD_CCtxParams_setParameter (%i, %i)", (int)param, value);- switch(param)- {- case ZSTD_c_format :- BOUNDCHECK(ZSTD_c_format, value);- CCtxParams->format = (ZSTD_format_e)value;- return (size_t)CCtxParams->format;-- case ZSTD_c_compressionLevel : {- FORWARD_IF_ERROR(ZSTD_cParam_clampBounds(param, &value));- if (value) { /* 0 : does not change current level */- CCtxParams->compressionLevel = value;- }- if (CCtxParams->compressionLevel >= 0) return CCtxParams->compressionLevel;- return 0; /* return type (size_t) cannot represent negative values */- }-- case ZSTD_c_windowLog :- if (value!=0) /* 0 => use default */- BOUNDCHECK(ZSTD_c_windowLog, value);- CCtxParams->cParams.windowLog = value;- return CCtxParams->cParams.windowLog;-- case ZSTD_c_hashLog :- if (value!=0) /* 0 => use default */- BOUNDCHECK(ZSTD_c_hashLog, value);- CCtxParams->cParams.hashLog = value;- return CCtxParams->cParams.hashLog;-- case ZSTD_c_chainLog :- if (value!=0) /* 0 => use default */- BOUNDCHECK(ZSTD_c_chainLog, value);- CCtxParams->cParams.chainLog = value;- return CCtxParams->cParams.chainLog;-- case ZSTD_c_searchLog :- if (value!=0) /* 0 => use default */- BOUNDCHECK(ZSTD_c_searchLog, value);- CCtxParams->cParams.searchLog = value;- return value;-- case ZSTD_c_minMatch :- if (value!=0) /* 0 => use default */- BOUNDCHECK(ZSTD_c_minMatch, value);- CCtxParams->cParams.minMatch = value;- return CCtxParams->cParams.minMatch;-- case ZSTD_c_targetLength :- BOUNDCHECK(ZSTD_c_targetLength, value);- CCtxParams->cParams.targetLength = value;- return CCtxParams->cParams.targetLength;-- case ZSTD_c_strategy :- if (value!=0) /* 0 => use default */- BOUNDCHECK(ZSTD_c_strategy, value);- CCtxParams->cParams.strategy = (ZSTD_strategy)value;- return (size_t)CCtxParams->cParams.strategy;-- case ZSTD_c_contentSizeFlag :- /* 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;-- 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;-- case ZSTD_c_dictIDFlag : /* When applicable, dictionary's dictID is provided in frame header (default:1) */- DEBUGLOG(4, "set dictIDFlag = %u", (value!=0));- CCtxParams->fParams.noDictIDFlag = !value;- return !CCtxParams->fParams.noDictIDFlag;-- case ZSTD_c_forceMaxWindow :- CCtxParams->forceWindow = (value != 0);- return CCtxParams->forceWindow;-- case ZSTD_c_forceAttachDict : {- const ZSTD_dictAttachPref_e pref = (ZSTD_dictAttachPref_e)value;- BOUNDCHECK(ZSTD_c_forceAttachDict, pref);- CCtxParams->attachDictPref = pref;- return CCtxParams->attachDictPref;- }-- case ZSTD_c_literalCompressionMode : {- const ZSTD_literalCompressionMode_e lcm = (ZSTD_literalCompressionMode_e)value;- BOUNDCHECK(ZSTD_c_literalCompressionMode, lcm);- CCtxParams->literalCompressionMode = lcm;- return CCtxParams->literalCompressionMode;- }-- case ZSTD_c_nbWorkers :-#ifndef ZSTD_MULTITHREAD- RETURN_ERROR_IF(value!=0, parameter_unsupported, "not compiled with multithreading");- return 0;-#else- FORWARD_IF_ERROR(ZSTD_cParam_clampBounds(param, &value));- CCtxParams->nbWorkers = value;- return CCtxParams->nbWorkers;-#endif-- case ZSTD_c_jobSize :-#ifndef ZSTD_MULTITHREAD- RETURN_ERROR_IF(value!=0, parameter_unsupported, "not compiled with multithreading");- return 0;-#else- /* Adjust to the minimum non-default value. */- if (value != 0 && value < ZSTDMT_JOBSIZE_MIN)- value = ZSTDMT_JOBSIZE_MIN;- FORWARD_IF_ERROR(ZSTD_cParam_clampBounds(param, &value));- assert(value >= 0);- CCtxParams->jobSize = value;- return CCtxParams->jobSize;-#endif-- case ZSTD_c_overlapLog :-#ifndef ZSTD_MULTITHREAD- RETURN_ERROR_IF(value!=0, parameter_unsupported, "not compiled with multithreading");- return 0;-#else- FORWARD_IF_ERROR(ZSTD_cParam_clampBounds(ZSTD_c_overlapLog, &value));- CCtxParams->overlapLog = value;- return CCtxParams->overlapLog;-#endif-- case ZSTD_c_rsyncable :-#ifndef ZSTD_MULTITHREAD- RETURN_ERROR_IF(value!=0, parameter_unsupported, "not compiled with multithreading");- return 0;-#else- FORWARD_IF_ERROR(ZSTD_cParam_clampBounds(ZSTD_c_overlapLog, &value));- CCtxParams->rsyncable = value;- return CCtxParams->rsyncable;-#endif-- case ZSTD_c_enableLongDistanceMatching :- CCtxParams->ldmParams.enableLdm = (value!=0);- return CCtxParams->ldmParams.enableLdm;-- case ZSTD_c_ldmHashLog :- if (value!=0) /* 0 ==> auto */- BOUNDCHECK(ZSTD_c_ldmHashLog, value);- CCtxParams->ldmParams.hashLog = value;- return CCtxParams->ldmParams.hashLog;-- case ZSTD_c_ldmMinMatch :- if (value!=0) /* 0 ==> default */- BOUNDCHECK(ZSTD_c_ldmMinMatch, value);- CCtxParams->ldmParams.minMatchLength = value;- return CCtxParams->ldmParams.minMatchLength;-- case ZSTD_c_ldmBucketSizeLog :- if (value!=0) /* 0 ==> default */- BOUNDCHECK(ZSTD_c_ldmBucketSizeLog, value);- CCtxParams->ldmParams.bucketSizeLog = value;- return CCtxParams->ldmParams.bucketSizeLog;-- case ZSTD_c_ldmHashRateLog :- RETURN_ERROR_IF(value > ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN,- parameter_outOfBound);- CCtxParams->ldmParams.hashRateLog = value;- return CCtxParams->ldmParams.hashRateLog;-- default: RETURN_ERROR(parameter_unsupported, "unknown parameter");- }-}--size_t ZSTD_CCtx_getParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, int* value)-{- return ZSTD_CCtxParams_getParameter(&cctx->requestedParams, param, value);-}--size_t ZSTD_CCtxParams_getParameter(- ZSTD_CCtx_params* CCtxParams, ZSTD_cParameter param, int* value)-{- switch(param)- {- case ZSTD_c_format :- *value = CCtxParams->format;- break;- case ZSTD_c_compressionLevel :- *value = CCtxParams->compressionLevel;- break;- case ZSTD_c_windowLog :- *value = CCtxParams->cParams.windowLog;- break;- case ZSTD_c_hashLog :- *value = CCtxParams->cParams.hashLog;- break;- case ZSTD_c_chainLog :- *value = CCtxParams->cParams.chainLog;- break;- case ZSTD_c_searchLog :- *value = CCtxParams->cParams.searchLog;- break;- case ZSTD_c_minMatch :- *value = CCtxParams->cParams.minMatch;- break;- case ZSTD_c_targetLength :- *value = CCtxParams->cParams.targetLength;- break;- case ZSTD_c_strategy :- *value = (unsigned)CCtxParams->cParams.strategy;- break;- case ZSTD_c_contentSizeFlag :- *value = CCtxParams->fParams.contentSizeFlag;- break;- case ZSTD_c_checksumFlag :- *value = CCtxParams->fParams.checksumFlag;- break;- case ZSTD_c_dictIDFlag :- *value = !CCtxParams->fParams.noDictIDFlag;- break;- case ZSTD_c_forceMaxWindow :- *value = CCtxParams->forceWindow;- break;- case ZSTD_c_forceAttachDict :- *value = CCtxParams->attachDictPref;- break;- case ZSTD_c_literalCompressionMode :- *value = CCtxParams->literalCompressionMode;- break;- case ZSTD_c_nbWorkers :-#ifndef ZSTD_MULTITHREAD- assert(CCtxParams->nbWorkers == 0);-#endif- *value = CCtxParams->nbWorkers;- break;- case ZSTD_c_jobSize :-#ifndef ZSTD_MULTITHREAD- RETURN_ERROR(parameter_unsupported, "not compiled with multithreading");-#else- assert(CCtxParams->jobSize <= INT_MAX);- *value = (int)CCtxParams->jobSize;- break;-#endif- case ZSTD_c_overlapLog :-#ifndef ZSTD_MULTITHREAD- RETURN_ERROR(parameter_unsupported, "not compiled with multithreading");-#else- *value = CCtxParams->overlapLog;- break;-#endif- case ZSTD_c_rsyncable :-#ifndef ZSTD_MULTITHREAD- RETURN_ERROR(parameter_unsupported, "not compiled with multithreading");-#else- *value = CCtxParams->rsyncable;- break;-#endif- case ZSTD_c_enableLongDistanceMatching :- *value = CCtxParams->ldmParams.enableLdm;- break;- case ZSTD_c_ldmHashLog :- *value = CCtxParams->ldmParams.hashLog;- break;- case ZSTD_c_ldmMinMatch :- *value = CCtxParams->ldmParams.minMatchLength;- break;- case ZSTD_c_ldmBucketSizeLog :- *value = CCtxParams->ldmParams.bucketSizeLog;- break;- case ZSTD_c_ldmHashRateLog :- *value = CCtxParams->ldmParams.hashRateLog;- break;- default: RETURN_ERROR(parameter_unsupported, "unknown parameter");- }- return 0;-}--/** ZSTD_CCtx_setParametersUsingCCtxParams() :- * just applies `params` into `cctx`- * no action is performed, parameters are merely stored.- * If ZSTDMT is enabled, parameters are pushed to cctx->mtctx.- * This is possible even if a compression is ongoing.- * In which case, new parameters will be applied on the fly, starting with next compression job.- */-size_t ZSTD_CCtx_setParametersUsingCCtxParams(- ZSTD_CCtx* cctx, const ZSTD_CCtx_params* params)-{- DEBUGLOG(4, "ZSTD_CCtx_setParametersUsingCCtxParams");- RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong);- RETURN_ERROR_IF(cctx->cdict, stage_wrong);-- cctx->requestedParams = *params;- return 0;-}--ZSTDLIB_API size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long long pledgedSrcSize)-{- DEBUGLOG(4, "ZSTD_CCtx_setPledgedSrcSize to %u bytes", (U32)pledgedSrcSize);- RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong);- cctx->pledgedSrcSizePlusOne = pledgedSrcSize+1;- return 0;-}--/**- * 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.- */-static size_t ZSTD_initLocalDict(ZSTD_CCtx* cctx)-{- ZSTD_localDict* const dl = &cctx->localDict;- ZSTD_compressionParameters const cParams = ZSTD_getCParamsFromCCtxParams(- &cctx->requestedParams, 0, dl->dictSize);- if (dl->dict == NULL) {- /* No local dictionary. */- assert(dl->dictBuffer == NULL);- assert(dl->cdict == NULL);- assert(dl->dictSize == 0);- return 0;- }- if (dl->cdict != NULL) {- assert(cctx->cdict == dl->cdict);- /* Local dictionary already initialized. */- return 0;- }- assert(dl->dictSize > 0);- assert(cctx->cdict == NULL);- assert(cctx->prefixDict.dict == NULL);-- dl->cdict = ZSTD_createCDict_advanced(- dl->dict,- dl->dictSize,- ZSTD_dlm_byRef,- dl->dictContentType,- cParams,- cctx->customMem);- RETURN_ERROR_IF(!dl->cdict, memory_allocation);- cctx->cdict = dl->cdict;- return 0;-}--size_t ZSTD_CCtx_loadDictionary_advanced(- 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);- RETURN_ERROR_IF(cctx->staticSize, memory_allocation,- "no malloc for static CCtx");- 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 0;- if (dictLoadMethod == ZSTD_dlm_byRef) {- cctx->localDict.dict = dict;- } else {- void* dictBuffer = ZSTD_malloc(dictSize, cctx->customMem);- RETURN_ERROR_IF(!dictBuffer, memory_allocation);- memcpy(dictBuffer, dict, dictSize);- cctx->localDict.dictBuffer = dictBuffer;- cctx->localDict.dict = dictBuffer;- }- cctx->localDict.dictSize = dictSize;- cctx->localDict.dictContentType = dictContentType;- return 0;-}--ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary_byReference(- ZSTD_CCtx* cctx, const void* dict, size_t dictSize)-{- return ZSTD_CCtx_loadDictionary_advanced(- cctx, dict, dictSize, ZSTD_dlm_byRef, ZSTD_dct_auto);-}--ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize)-{- return ZSTD_CCtx_loadDictionary_advanced(- cctx, dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dct_auto);-}---size_t ZSTD_CCtx_refCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict)-{- RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong);- /* Free the existing local cdict (if any) to save memory. */- ZSTD_clearAllDicts(cctx);- cctx->cdict = cdict;- return 0;-}--size_t ZSTD_CCtx_refPrefix(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize)-{- return ZSTD_CCtx_refPrefix_advanced(cctx, prefix, prefixSize, ZSTD_dct_rawContent);-}--size_t ZSTD_CCtx_refPrefix_advanced(- ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType)-{- RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong);- ZSTD_clearAllDicts(cctx);- cctx->prefixDict.dict = prefix;- cctx->prefixDict.dictSize = prefixSize;- cctx->prefixDict.dictContentType = dictContentType;- return 0;-}--/*! ZSTD_CCtx_reset() :- * Also dumps dictionary */-size_t ZSTD_CCtx_reset(ZSTD_CCtx* cctx, ZSTD_ResetDirective reset)-{- if ( (reset == ZSTD_reset_session_only)- || (reset == ZSTD_reset_session_and_parameters) ) {- cctx->streamStage = zcss_init;- cctx->pledgedSrcSizePlusOne = 0;- }- if ( (reset == ZSTD_reset_parameters)- || (reset == ZSTD_reset_session_and_parameters) ) {- RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong);- ZSTD_clearAllDicts(cctx);- return ZSTD_CCtxParams_reset(&cctx->requestedParams);- }- return 0;-}---/** ZSTD_checkCParams() :- control CParam values remain within authorized range.- @return : 0, or an error code if one value is beyond authorized range */-size_t ZSTD_checkCParams(ZSTD_compressionParameters cParams)-{- BOUNDCHECK(ZSTD_c_windowLog, cParams.windowLog);- BOUNDCHECK(ZSTD_c_chainLog, cParams.chainLog);- BOUNDCHECK(ZSTD_c_hashLog, cParams.hashLog);- BOUNDCHECK(ZSTD_c_searchLog, cParams.searchLog);- BOUNDCHECK(ZSTD_c_minMatch, cParams.minMatch);- BOUNDCHECK(ZSTD_c_targetLength,cParams.targetLength);- BOUNDCHECK(ZSTD_c_strategy, cParams.strategy);- return 0;-}--/** ZSTD_clampCParams() :- * make CParam values within valid range.- * @return : valid CParams */-static ZSTD_compressionParameters-ZSTD_clampCParams(ZSTD_compressionParameters cParams)-{-# define CLAMP_TYPE(cParam, val, type) { \- ZSTD_bounds const bounds = ZSTD_cParam_getBounds(cParam); \- if ((int)val<bounds.lowerBound) val=(type)bounds.lowerBound; \- else if ((int)val>bounds.upperBound) val=(type)bounds.upperBound; \- }-# define CLAMP(cParam, val) CLAMP_TYPE(cParam, val, int)- CLAMP(ZSTD_c_windowLog, cParams.windowLog);- CLAMP(ZSTD_c_chainLog, cParams.chainLog);- CLAMP(ZSTD_c_hashLog, cParams.hashLog);- CLAMP(ZSTD_c_searchLog, cParams.searchLog);- CLAMP(ZSTD_c_minMatch, cParams.minMatch);- CLAMP(ZSTD_c_targetLength,cParams.targetLength);- CLAMP_TYPE(ZSTD_c_strategy,cParams.strategy, ZSTD_strategy);- return cParams;-}--/** ZSTD_cycleLog() :- * condition for correct operation : hashLog > 1 */-static U32 ZSTD_cycleLog(U32 hashLog, ZSTD_strategy strat)-{- U32 const btScale = ((U32)strat >= (U32)ZSTD_btlazy2);- return hashLog - btScale;-}--/** ZSTD_adjustCParams_internal() :- * optimize `cPar` for a specified input (`srcSize` and `dictSize`).- * mostly downsize to reduce memory consumption and initialization latency.- * `srcSize` can be ZSTD_CONTENTSIZE_UNKNOWN when not known.- * note : for the time being, `srcSize==0` means "unknown" too, for compatibility with older convention.- * condition : cPar is presumed validated (can be checked using ZSTD_checkCParams()). */-static ZSTD_compressionParameters-ZSTD_adjustCParams_internal(ZSTD_compressionParameters cPar,- unsigned long long srcSize,- size_t dictSize)-{- static const U64 minSrcSize = 513; /* (1<<9) + 1 */- static const U64 maxWindowResize = 1ULL << (ZSTD_WINDOWLOG_MAX-1);- assert(ZSTD_checkCParams(cPar)==0);-- if (dictSize && (srcSize+1<2) /* ZSTD_CONTENTSIZE_UNKNOWN and 0 mean "unknown" */ )- srcSize = minSrcSize; /* presumed small when there is a dictionary */- else if (srcSize == 0)- srcSize = ZSTD_CONTENTSIZE_UNKNOWN; /* 0 == unknown : presumed large */-- /* resize windowLog if input is small enough, to use less memory */- 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 :- ZSTD_highbit32(tSize-1) + 1;- if (cPar.windowLog > srcLog) cPar.windowLog = srcLog;- }- if (cPar.hashLog > cPar.windowLog+1) cPar.hashLog = cPar.windowLog+1;- { U32 const cycleLog = ZSTD_cycleLog(cPar.chainLog, cPar.strategy);- if (cycleLog > cPar.windowLog)- cPar.chainLog -= (cycleLog - cPar.windowLog);- }-- if (cPar.windowLog < ZSTD_WINDOWLOG_ABSOLUTEMIN)- cPar.windowLog = ZSTD_WINDOWLOG_ABSOLUTEMIN; /* minimum wlog required for valid frame header */-- return cPar;-}--ZSTD_compressionParameters-ZSTD_adjustCParams(ZSTD_compressionParameters cPar,- unsigned long long srcSize,- size_t dictSize)-{- cPar = ZSTD_clampCParams(cPar); /* resulting cPar is necessarily valid (all parameters within range) */- return ZSTD_adjustCParams_internal(cPar, srcSize, dictSize);-}--ZSTD_compressionParameters ZSTD_getCParamsFromCCtxParams(- const ZSTD_CCtx_params* CCtxParams, U64 srcSizeHint, size_t dictSize)-{- ZSTD_compressionParameters cParams = ZSTD_getCParams(CCtxParams->compressionLevel, srcSizeHint, dictSize);- if (CCtxParams->ldmParams.enableLdm) cParams.windowLog = ZSTD_LDM_DEFAULT_WINDOW_LOG;- if (CCtxParams->cParams.windowLog) cParams.windowLog = CCtxParams->cParams.windowLog;- if (CCtxParams->cParams.hashLog) cParams.hashLog = CCtxParams->cParams.hashLog;- if (CCtxParams->cParams.chainLog) cParams.chainLog = CCtxParams->cParams.chainLog;- if (CCtxParams->cParams.searchLog) cParams.searchLog = CCtxParams->cParams.searchLog;- if (CCtxParams->cParams.minMatch) cParams.minMatch = CCtxParams->cParams.minMatch;- if (CCtxParams->cParams.targetLength) cParams.targetLength = CCtxParams->cParams.targetLength;- if (CCtxParams->cParams.strategy) cParams.strategy = CCtxParams->cParams.strategy;- assert(!ZSTD_checkCParams(cParams));- return ZSTD_adjustCParams_internal(cParams, srcSizeHint, dictSize);-}--static size_t-ZSTD_sizeof_matchState(const ZSTD_compressionParameters* const cParams,- const U32 forCCtx)-{- size_t const chainSize = (cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cParams->chainLog);- size_t const hSize = ((size_t)1) << cParams->hashLog;- U32 const hashLog3 = (forCCtx && cParams->minMatch==3) ? MIN(ZSTD_HASHLOG3_MAX, cParams->windowLog) : 0;- size_t const h3Size = ((size_t)1) << hashLog3;- size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32);- size_t const optPotentialSpace = ((MaxML+1) + (MaxLL+1) + (MaxOff+1) + (1<<Litbits)) * sizeof(U32)- + (ZSTD_OPT_NUM+1) * (sizeof(ZSTD_match_t)+sizeof(ZSTD_optimal_t));- size_t const optSpace = (forCCtx && (cParams->strategy >= ZSTD_btopt))- ? optPotentialSpace- : 0;- DEBUGLOG(4, "chainSize: %u - hSize: %u - h3Size: %u",- (U32)chainSize, (U32)hSize, (U32)h3Size);- return tableSpace + optSpace;-}--size_t ZSTD_estimateCCtxSize_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, 0, 0);- size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, (size_t)1 << cParams.windowLog);- U32 const divider = (cParams.minMatch==3) ? 3 : 4;- size_t const maxNbSeq = blockSize / divider;- size_t const tokenSpace = WILDCOPY_OVERLENGTH + blockSize + 11*maxNbSeq;- size_t const entropySpace = HUF_WORKSPACE_SIZE;- size_t const blockStateSpace = 2 * sizeof(ZSTD_compressedBlockState_t);- size_t const matchStateSize = ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 1);-- size_t const ldmSpace = ZSTD_ldm_getTableSize(params->ldmParams);- size_t const ldmSeqSpace = ZSTD_ldm_getMaxNbSeq(params->ldmParams, blockSize) * sizeof(rawSeq);-- size_t const neededSpace = entropySpace + blockStateSpace + tokenSpace +- matchStateSize + ldmSpace + ldmSeqSpace;-- DEBUGLOG(5, "sizeof(ZSTD_CCtx) : %u", (U32)sizeof(ZSTD_CCtx));- DEBUGLOG(5, "estimate workSpace : %u", (U32)neededSpace);- return sizeof(ZSTD_CCtx) + neededSpace;- }-}--size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams)-{- ZSTD_CCtx_params const params = ZSTD_makeCCtxParamsFromCParams(cParams);- return ZSTD_estimateCCtxSize_usingCCtxParams(¶ms);-}--static size_t ZSTD_estimateCCtxSize_internal(int compressionLevel)-{- ZSTD_compressionParameters const cParams = ZSTD_getCParams(compressionLevel, 0, 0);- return ZSTD_estimateCCtxSize_usingCParams(cParams);-}--size_t ZSTD_estimateCCtxSize(int compressionLevel)-{- int level;- size_t memBudget = 0;- for (level=MIN(compressionLevel, 1); level<=compressionLevel; level++) {- size_t const newMB = ZSTD_estimateCCtxSize_internal(level);- if (newMB > memBudget) memBudget = newMB;- }- return memBudget;-}--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, 0, 0);- size_t const CCtxSize = ZSTD_estimateCCtxSize_usingCCtxParams(params);- size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, (size_t)1 << cParams.windowLog);- size_t const inBuffSize = ((size_t)1 << cParams.windowLog) + blockSize;- size_t const outBuffSize = ZSTD_compressBound(blockSize) + 1;- size_t const streamingSize = inBuffSize + outBuffSize;-- return CCtxSize + streamingSize;- }-}--size_t ZSTD_estimateCStreamSize_usingCParams(ZSTD_compressionParameters cParams)-{- ZSTD_CCtx_params const params = ZSTD_makeCCtxParamsFromCParams(cParams);- return ZSTD_estimateCStreamSize_usingCCtxParams(¶ms);-}--static size_t ZSTD_estimateCStreamSize_internal(int compressionLevel)-{- ZSTD_compressionParameters const cParams = ZSTD_getCParams(compressionLevel, 0, 0);- return ZSTD_estimateCStreamSize_usingCParams(cParams);-}--size_t ZSTD_estimateCStreamSize(int compressionLevel)-{- int level;- size_t memBudget = 0;- for (level=MIN(compressionLevel, 1); level<=compressionLevel; level++) {- size_t const newMB = ZSTD_estimateCStreamSize_internal(level);- if (newMB > memBudget) memBudget = newMB;- }- return memBudget;-}--/* ZSTD_getFrameProgression():- * tells how much data has been consumed (input) and produced (output) for current frame.- * able to count progression inside worker threads (non-blocking mode).- */-ZSTD_frameProgression ZSTD_getFrameProgression(const ZSTD_CCtx* cctx)-{-#ifdef ZSTD_MULTITHREAD- if (cctx->appliedParams.nbWorkers > 0) {- return ZSTDMT_getFrameProgression(cctx->mtctx);- }-#endif- { ZSTD_frameProgression fp;- size_t const buffered = (cctx->inBuff == NULL) ? 0 :- cctx->inBuffPos - cctx->inToCompress;- if (buffered) assert(cctx->inBuffPos >= cctx->inToCompress);- assert(buffered <= ZSTD_BLOCKSIZE_MAX);- fp.ingested = cctx->consumedSrcSize + buffered;- fp.consumed = cctx->consumedSrcSize;- fp.produced = cctx->producedCSize;- fp.flushed = cctx->producedCSize; /* simplified; some data might still be left within streaming output buffer */- fp.currentJobID = 0;- fp.nbActiveWorkers = 0;- return fp;-} }--/*! ZSTD_toFlushNow()- * Only useful for multithreading scenarios currently (nbWorkers >= 1).- */-size_t ZSTD_toFlushNow(ZSTD_CCtx* cctx)-{-#ifdef ZSTD_MULTITHREAD- if (cctx->appliedParams.nbWorkers > 0) {- return ZSTDMT_toFlushNow(cctx->mtctx);- }-#endif- (void)cctx;- return 0; /* over-simplification; could also check if context is currently running in streaming mode, and in which case, report how many bytes are left to be flushed within output buffer */-}----static U32 ZSTD_equivalentCParams(ZSTD_compressionParameters cParams1,- ZSTD_compressionParameters cParams2)-{- return (cParams1.hashLog == cParams2.hashLog)- & (cParams1.chainLog == cParams2.chainLog)- & (cParams1.strategy == cParams2.strategy) /* opt parser space */- & ((cParams1.minMatch==3) == (cParams2.minMatch==3)); /* hashlog3 space */-}--static void ZSTD_assertEqualCParams(ZSTD_compressionParameters cParams1,- ZSTD_compressionParameters cParams2)-{- (void)cParams1;- (void)cParams2;- assert(cParams1.windowLog == cParams2.windowLog);- assert(cParams1.chainLog == cParams2.chainLog);- assert(cParams1.hashLog == cParams2.hashLog);- assert(cParams1.searchLog == cParams2.searchLog);- assert(cParams1.minMatch == cParams2.minMatch);- assert(cParams1.targetLength == cParams2.targetLength);- assert(cParams1.strategy == cParams2.strategy);-}--/** The parameters are equivalent if ldm is not enabled in both sets or- * all the parameters are equivalent. */-static U32 ZSTD_equivalentLdmParams(ldmParams_t ldmParams1,- ldmParams_t ldmParams2)-{- return (!ldmParams1.enableLdm && !ldmParams2.enableLdm) ||- (ldmParams1.enableLdm == ldmParams2.enableLdm &&- ldmParams1.hashLog == ldmParams2.hashLog &&- ldmParams1.bucketSizeLog == ldmParams2.bucketSizeLog &&- ldmParams1.minMatchLength == ldmParams2.minMatchLength &&- ldmParams1.hashRateLog == ldmParams2.hashRateLog);-}--typedef enum { ZSTDb_not_buffered, ZSTDb_buffered } ZSTD_buffered_policy_e;--/* ZSTD_sufficientBuff() :- * check internal buffers exist for streaming if buffPol == ZSTDb_buffered .- * Note : they are assumed to be correctly sized if ZSTD_equivalentCParams()==1 */-static U32 ZSTD_sufficientBuff(size_t bufferSize1, size_t maxNbSeq1,- size_t maxNbLit1,- ZSTD_buffered_policy_e buffPol2,- ZSTD_compressionParameters cParams2,- U64 pledgedSrcSize)-{- size_t const windowSize2 = MAX(1, (size_t)MIN(((U64)1 << cParams2.windowLog), pledgedSrcSize));- size_t const blockSize2 = MIN(ZSTD_BLOCKSIZE_MAX, windowSize2);- size_t const maxNbSeq2 = blockSize2 / ((cParams2.minMatch == 3) ? 3 : 4);- size_t const maxNbLit2 = blockSize2;- size_t const neededBufferSize2 = (buffPol2==ZSTDb_buffered) ? windowSize2 + blockSize2 : 0;- DEBUGLOG(4, "ZSTD_sufficientBuff: is neededBufferSize2=%u <= bufferSize1=%u",- (U32)neededBufferSize2, (U32)bufferSize1);- DEBUGLOG(4, "ZSTD_sufficientBuff: is maxNbSeq2=%u <= maxNbSeq1=%u",- (U32)maxNbSeq2, (U32)maxNbSeq1);- DEBUGLOG(4, "ZSTD_sufficientBuff: is maxNbLit2=%u <= maxNbLit1=%u",- (U32)maxNbLit2, (U32)maxNbLit1);- return (maxNbLit2 <= maxNbLit1)- & (maxNbSeq2 <= maxNbSeq1)- & (neededBufferSize2 <= bufferSize1);-}--/** Equivalence for resetCCtx purposes */-static U32 ZSTD_equivalentParams(ZSTD_CCtx_params params1,- ZSTD_CCtx_params params2,- size_t buffSize1,- size_t maxNbSeq1, size_t maxNbLit1,- ZSTD_buffered_policy_e buffPol2,- U64 pledgedSrcSize)-{- DEBUGLOG(4, "ZSTD_equivalentParams: pledgedSrcSize=%u", (U32)pledgedSrcSize);- if (!ZSTD_equivalentCParams(params1.cParams, params2.cParams)) {- DEBUGLOG(4, "ZSTD_equivalentCParams() == 0");- return 0;- }- if (!ZSTD_equivalentLdmParams(params1.ldmParams, params2.ldmParams)) {- DEBUGLOG(4, "ZSTD_equivalentLdmParams() == 0");- return 0;- }- if (!ZSTD_sufficientBuff(buffSize1, maxNbSeq1, maxNbLit1, buffPol2,- params2.cParams, pledgedSrcSize)) {- DEBUGLOG(4, "ZSTD_sufficientBuff() == 0");- return 0;- }- return 1;-}--static void ZSTD_reset_compressedBlockState(ZSTD_compressedBlockState_t* bs)-{- int i;- for (i = 0; i < ZSTD_REP_NUM; ++i)- bs->rep[i] = repStartValue[i];- bs->entropy.huf.repeatMode = HUF_repeat_none;- bs->entropy.fse.offcode_repeatMode = FSE_repeat_none;- bs->entropy.fse.matchlength_repeatMode = FSE_repeat_none;- bs->entropy.fse.litlength_repeatMode = FSE_repeat_none;-}--/*! ZSTD_invalidateMatchState()- * Invalidate all the matches in the match finder tables.- * Requires nextSrc and base to be set (can be NULL).- */-static void ZSTD_invalidateMatchState(ZSTD_matchState_t* ms)-{- ZSTD_window_clear(&ms->window);-- ms->nextToUpdate = ms->window.dictLimit;- ms->nextToUpdate3 = ms->window.dictLimit;- ms->loadedDictEnd = 0;- ms->opt.litLengthSum = 0; /* force reset of btopt stats */- ms->dictMatchState = NULL;-}--/*! ZSTD_continueCCtx() :- * reuse CCtx without reset (note : requires no dictionary) */-static size_t ZSTD_continueCCtx(ZSTD_CCtx* cctx, ZSTD_CCtx_params params, U64 pledgedSrcSize)-{- size_t const windowSize = MAX(1, (size_t)MIN(((U64)1 << params.cParams.windowLog), pledgedSrcSize));- size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, windowSize);- DEBUGLOG(4, "ZSTD_continueCCtx: re-use context in place");-- cctx->blockSize = blockSize; /* previous block size could be different even for same windowLog, due to pledgedSrcSize */- cctx->appliedParams = params;- cctx->blockState.matchState.cParams = params.cParams;- cctx->pledgedSrcSizePlusOne = pledgedSrcSize+1;- cctx->consumedSrcSize = 0;- cctx->producedCSize = 0;- if (pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN)- cctx->appliedParams.fParams.contentSizeFlag = 0;- DEBUGLOG(4, "pledged content size : %u ; flag : %u",- (U32)pledgedSrcSize, cctx->appliedParams.fParams.contentSizeFlag);- cctx->stage = ZSTDcs_init;- cctx->dictID = 0;- if (params.ldmParams.enableLdm)- ZSTD_window_clear(&cctx->ldmState.window);- ZSTD_referenceExternalSequences(cctx, NULL, 0);- ZSTD_invalidateMatchState(&cctx->blockState.matchState);- ZSTD_reset_compressedBlockState(cctx->blockState.prevCBlock);- XXH64_reset(&cctx->xxhState, 0);- return 0;-}--typedef enum { ZSTDcrp_continue, ZSTDcrp_noMemset } ZSTD_compResetPolicy_e;--static void*-ZSTD_reset_matchState(ZSTD_matchState_t* ms,- void* ptr,- const ZSTD_compressionParameters* cParams,- ZSTD_compResetPolicy_e const crp, U32 const forCCtx)-{- size_t const chainSize = (cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cParams->chainLog);- size_t const hSize = ((size_t)1) << cParams->hashLog;- U32 const hashLog3 = (forCCtx && cParams->minMatch==3) ? MIN(ZSTD_HASHLOG3_MAX, cParams->windowLog) : 0;- size_t const h3Size = ((size_t)1) << hashLog3;- size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32);-- assert(((size_t)ptr & 3) == 0);-- ms->hashLog3 = hashLog3;- memset(&ms->window, 0, sizeof(ms->window));- ms->window.dictLimit = 1; /* start from 1, so that 1st position is valid */- ms->window.lowLimit = 1; /* it ensures first and later CCtx usages compress the same */- ms->window.nextSrc = ms->window.base + 1; /* see issue #1241 */- ZSTD_invalidateMatchState(ms);-- /* opt parser space */- if (forCCtx && (cParams->strategy >= ZSTD_btopt)) {- DEBUGLOG(4, "reserving optimal parser space");- ms->opt.litFreq = (unsigned*)ptr;- ms->opt.litLengthFreq = ms->opt.litFreq + (1<<Litbits);- ms->opt.matchLengthFreq = ms->opt.litLengthFreq + (MaxLL+1);- ms->opt.offCodeFreq = ms->opt.matchLengthFreq + (MaxML+1);- ptr = ms->opt.offCodeFreq + (MaxOff+1);- ms->opt.matchTable = (ZSTD_match_t*)ptr;- ptr = ms->opt.matchTable + ZSTD_OPT_NUM+1;- ms->opt.priceTable = (ZSTD_optimal_t*)ptr;- ptr = ms->opt.priceTable + ZSTD_OPT_NUM+1;- }-- /* table Space */- DEBUGLOG(4, "reset table : %u", crp!=ZSTDcrp_noMemset);- assert(((size_t)ptr & 3) == 0); /* ensure ptr is properly aligned */- if (crp!=ZSTDcrp_noMemset) memset(ptr, 0, tableSpace); /* reset tables only */- ms->hashTable = (U32*)(ptr);- ms->chainTable = ms->hashTable + hSize;- ms->hashTable3 = ms->chainTable + chainSize;- ptr = ms->hashTable3 + h3Size;-- ms->cParams = *cParams;-- assert(((size_t)ptr & 3) == 0);- return ptr;-}--#define ZSTD_WORKSPACETOOLARGE_FACTOR 3 /* define "workspace is too large" as this number of times larger than needed */-#define ZSTD_WORKSPACETOOLARGE_MAXDURATION 128 /* when workspace is continuously too large- * during at least this number of times,- * context's memory usage is considered wasteful,- * because it's sized to handle a worst case scenario which rarely happens.- * In which case, resize it down to free some memory */--/*! ZSTD_resetCCtx_internal() :- note : `params` are assumed fully validated at this stage */-static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,- ZSTD_CCtx_params params,- U64 pledgedSrcSize,- ZSTD_compResetPolicy_e const crp,- ZSTD_buffered_policy_e const zbuff)-{- DEBUGLOG(4, "ZSTD_resetCCtx_internal: pledgedSrcSize=%u, wlog=%u",- (U32)pledgedSrcSize, params.cParams.windowLog);- assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams)));-- if (crp == ZSTDcrp_continue) {- if (ZSTD_equivalentParams(zc->appliedParams, params,- zc->inBuffSize,- zc->seqStore.maxNbSeq, zc->seqStore.maxNbLit,- zbuff, pledgedSrcSize)) {- DEBUGLOG(4, "ZSTD_equivalentParams()==1 -> continue mode (wLog1=%u, blockSize1=%zu)",- zc->appliedParams.cParams.windowLog, zc->blockSize);- zc->workSpaceOversizedDuration += (zc->workSpaceOversizedDuration > 0); /* if it was too large, it still is */- if (zc->workSpaceOversizedDuration <= ZSTD_WORKSPACETOOLARGE_MAXDURATION)- return ZSTD_continueCCtx(zc, params, pledgedSrcSize);- } }- DEBUGLOG(4, "ZSTD_equivalentParams()==0 -> reset CCtx");-- if (params.ldmParams.enableLdm) {- /* Adjust long distance matching parameters */- ZSTD_ldm_adjustParameters(¶ms.ldmParams, ¶ms.cParams);- assert(params.ldmParams.hashLog >= params.ldmParams.bucketSizeLog);- assert(params.ldmParams.hashRateLog < 32);- zc->ldmState.hashPower = ZSTD_rollingHash_primePower(params.ldmParams.minMatchLength);- }-- { 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 tokenSpace = WILDCOPY_OVERLENGTH + blockSize + 11*maxNbSeq;- size_t const buffOutSize = (zbuff==ZSTDb_buffered) ? ZSTD_compressBound(blockSize)+1 : 0;- size_t const buffInSize = (zbuff==ZSTDb_buffered) ? windowSize + blockSize : 0;- size_t const matchStateSize = ZSTD_sizeof_matchState(¶ms.cParams, /* forCCtx */ 1);- size_t const maxNbLdmSeq = ZSTD_ldm_getMaxNbSeq(params.ldmParams, blockSize);- void* ptr; /* used to partition workSpace */-- /* Check if workSpace is large enough, alloc a new one if needed */- { size_t const entropySpace = HUF_WORKSPACE_SIZE;- size_t const blockStateSpace = 2 * sizeof(ZSTD_compressedBlockState_t);- size_t const bufferSpace = buffInSize + buffOutSize;- size_t const ldmSpace = ZSTD_ldm_getTableSize(params.ldmParams);- size_t const ldmSeqSpace = maxNbLdmSeq * sizeof(rawSeq);-- size_t const neededSpace = entropySpace + blockStateSpace + ldmSpace +- ldmSeqSpace + matchStateSize + tokenSpace +- bufferSpace;-- int const workSpaceTooSmall = zc->workSpaceSize < neededSpace;- int const workSpaceTooLarge = zc->workSpaceSize > ZSTD_WORKSPACETOOLARGE_FACTOR * neededSpace;- int const workSpaceWasteful = workSpaceTooLarge && (zc->workSpaceOversizedDuration > ZSTD_WORKSPACETOOLARGE_MAXDURATION);- zc->workSpaceOversizedDuration = workSpaceTooLarge ? zc->workSpaceOversizedDuration+1 : 0;-- DEBUGLOG(4, "Need %zuKB workspace, including %zuKB for match state, and %zuKB for buffers",- neededSpace>>10, matchStateSize>>10, bufferSpace>>10);- DEBUGLOG(4, "windowSize: %zu - blockSize: %zu", windowSize, blockSize);-- if (workSpaceTooSmall || workSpaceWasteful) {- DEBUGLOG(4, "Need to resize workSpaceSize from %zuKB to %zuKB",- zc->workSpaceSize >> 10,- neededSpace >> 10);-- RETURN_ERROR_IF(zc->staticSize, memory_allocation, "static cctx : no resize");-- zc->workSpaceSize = 0;- ZSTD_free(zc->workSpace, zc->customMem);- zc->workSpace = ZSTD_malloc(neededSpace, zc->customMem);- RETURN_ERROR_IF(zc->workSpace == NULL, memory_allocation);- zc->workSpaceSize = neededSpace;- zc->workSpaceOversizedDuration = 0;-- /* Statically sized space.- * entropyWorkspace never moves,- * though prev/next block swap places */- assert(((size_t)zc->workSpace & 3) == 0); /* ensure correct alignment */- assert(zc->workSpaceSize >= 2 * sizeof(ZSTD_compressedBlockState_t));- zc->blockState.prevCBlock = (ZSTD_compressedBlockState_t*)zc->workSpace;- zc->blockState.nextCBlock = zc->blockState.prevCBlock + 1;- ptr = zc->blockState.nextCBlock + 1;- zc->entropyWorkspace = (U32*)ptr;- } }-- /* init params */- zc->appliedParams = params;- zc->blockState.matchState.cParams = params.cParams;- zc->pledgedSrcSizePlusOne = pledgedSrcSize+1;- zc->consumedSrcSize = 0;- zc->producedCSize = 0;- if (pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN)- zc->appliedParams.fParams.contentSizeFlag = 0;- DEBUGLOG(4, "pledged content size : %u ; flag : %u",- (unsigned)pledgedSrcSize, zc->appliedParams.fParams.contentSizeFlag);- zc->blockSize = blockSize;-- XXH64_reset(&zc->xxhState, 0);- zc->stage = ZSTDcs_init;- zc->dictID = 0;-- ZSTD_reset_compressedBlockState(zc->blockState.prevCBlock);-- ptr = zc->entropyWorkspace + HUF_WORKSPACE_SIZE_U32;-- /* ldm hash table */- /* initialize bucketOffsets table later for pointer alignment */- if (params.ldmParams.enableLdm) {- size_t const ldmHSize = ((size_t)1) << params.ldmParams.hashLog;- memset(ptr, 0, ldmHSize * sizeof(ldmEntry_t));- assert(((size_t)ptr & 3) == 0); /* ensure ptr is properly aligned */- zc->ldmState.hashTable = (ldmEntry_t*)ptr;- ptr = zc->ldmState.hashTable + ldmHSize;- zc->ldmSequences = (rawSeq*)ptr;- ptr = zc->ldmSequences + maxNbLdmSeq;- zc->maxNbLdmSequences = maxNbLdmSeq;-- memset(&zc->ldmState.window, 0, sizeof(zc->ldmState.window));- }- assert(((size_t)ptr & 3) == 0); /* ensure ptr is properly aligned */-- ptr = ZSTD_reset_matchState(&zc->blockState.matchState, ptr, ¶ms.cParams, crp, /* forCCtx */ 1);-- /* sequences storage */- zc->seqStore.maxNbSeq = maxNbSeq;- zc->seqStore.sequencesStart = (seqDef*)ptr;- ptr = zc->seqStore.sequencesStart + maxNbSeq;- zc->seqStore.llCode = (BYTE*) ptr;- zc->seqStore.mlCode = zc->seqStore.llCode + maxNbSeq;- zc->seqStore.ofCode = zc->seqStore.mlCode + maxNbSeq;- zc->seqStore.litStart = zc->seqStore.ofCode + maxNbSeq;- /* ZSTD_wildcopy() is used to copy into the literals buffer,- * so we have to oversize the buffer by WILDCOPY_OVERLENGTH bytes.- */- zc->seqStore.maxNbLit = blockSize;- ptr = zc->seqStore.litStart + blockSize + WILDCOPY_OVERLENGTH;-- /* ldm bucketOffsets table */- if (params.ldmParams.enableLdm) {- size_t const ldmBucketSize =- ((size_t)1) << (params.ldmParams.hashLog -- params.ldmParams.bucketSizeLog);- memset(ptr, 0, ldmBucketSize);- zc->ldmState.bucketOffsets = (BYTE*)ptr;- ptr = zc->ldmState.bucketOffsets + ldmBucketSize;- ZSTD_window_clear(&zc->ldmState.window);- }- ZSTD_referenceExternalSequences(zc, NULL, 0);-- /* buffers */- zc->inBuffSize = buffInSize;- zc->inBuff = (char*)ptr;- zc->outBuffSize = buffOutSize;- zc->outBuff = zc->inBuff + buffInSize;-- return 0;- }-}--/* ZSTD_invalidateRepCodes() :- * ensures next compression will not use repcodes from previous block.- * Note : only works with regular variant;- * do not use with extDict variant ! */-void ZSTD_invalidateRepCodes(ZSTD_CCtx* cctx) {- int i;- for (i=0; i<ZSTD_REP_NUM; i++) cctx->blockState.prevCBlock->rep[i] = 0;- assert(!ZSTD_window_hasExtDict(cctx->blockState.matchState.window));-}--/* These are the approximate sizes for each strategy past which copying the- * dictionary tables into the working context is faster than using them- * in-place.- */-static const size_t attachDictSizeCutoffs[ZSTD_STRATEGY_MAX+1] = {- 8 KB, /* unused */- 8 KB, /* ZSTD_fast */- 16 KB, /* ZSTD_dfast */- 32 KB, /* ZSTD_greedy */- 32 KB, /* ZSTD_lazy */- 32 KB, /* ZSTD_lazy2 */- 32 KB, /* ZSTD_btlazy2 */- 32 KB, /* ZSTD_btopt */- 8 KB, /* ZSTD_btultra */- 8 KB /* ZSTD_btultra2 */-};--static int ZSTD_shouldAttachDict(const ZSTD_CDict* cdict,- ZSTD_CCtx_params params,- U64 pledgedSrcSize)-{- size_t cutoff = attachDictSizeCutoffs[cdict->matchState.cParams.strategy];- return ( pledgedSrcSize <= cutoff- || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN- || params.attachDictPref == ZSTD_dictForceAttach )- && params.attachDictPref != ZSTD_dictForceCopy- && !params.forceWindow; /* dictMatchState isn't correctly- * handled in _enforceMaxDist */-}--static size_t ZSTD_resetCCtx_byAttachingCDict(- ZSTD_CCtx* cctx,- const ZSTD_CDict* cdict,- ZSTD_CCtx_params params,- U64 pledgedSrcSize,- ZSTD_buffered_policy_e zbuff)-{- {- const ZSTD_compressionParameters *cdict_cParams = &cdict->matchState.cParams;- unsigned const windowLog = params.cParams.windowLog;- assert(windowLog != 0);- /* Resize working context table params for input only, since the dict- * has its own tables. */- params.cParams = ZSTD_adjustCParams_internal(*cdict_cParams, pledgedSrcSize, 0);- params.cParams.windowLog = windowLog;- ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize,- ZSTDcrp_continue, zbuff);- assert(cctx->appliedParams.cParams.strategy == cdict_cParams->strategy);- }-- {- const U32 cdictEnd = (U32)( cdict->matchState.window.nextSrc- - cdict->matchState.window.base);- const U32 cdictLen = cdictEnd - cdict->matchState.window.dictLimit;- if (cdictLen == 0) {- /* don't even attach dictionaries with no contents */- DEBUGLOG(4, "skipping attaching empty dictionary");- } else {- DEBUGLOG(4, "attaching dictionary into context");- cctx->blockState.matchState.dictMatchState = &cdict->matchState;-- /* prep working match state so dict matches never have negative indices- * when they are translated to the working context's index space. */- if (cctx->blockState.matchState.window.dictLimit < cdictEnd) {- cctx->blockState.matchState.window.nextSrc =- cctx->blockState.matchState.window.base + cdictEnd;- ZSTD_window_clear(&cctx->blockState.matchState.window);- }- cctx->blockState.matchState.loadedDictEnd = cctx->blockState.matchState.window.dictLimit;- }- }-- cctx->dictID = cdict->dictID;-- /* copy block state */- memcpy(cctx->blockState.prevCBlock, &cdict->cBlockState, sizeof(cdict->cBlockState));-- return 0;-}--static size_t ZSTD_resetCCtx_byCopyingCDict(ZSTD_CCtx* cctx,- const ZSTD_CDict* cdict,- ZSTD_CCtx_params params,- U64 pledgedSrcSize,- ZSTD_buffered_policy_e zbuff)-{- const ZSTD_compressionParameters *cdict_cParams = &cdict->matchState.cParams;-- DEBUGLOG(4, "copying dictionary into context");-- { unsigned const windowLog = params.cParams.windowLog;- assert(windowLog != 0);- /* Copy only compression parameters related to tables. */- params.cParams = *cdict_cParams;- params.cParams.windowLog = windowLog;- ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize,- ZSTDcrp_noMemset, zbuff);- assert(cctx->appliedParams.cParams.strategy == cdict_cParams->strategy);- assert(cctx->appliedParams.cParams.hashLog == cdict_cParams->hashLog);- assert(cctx->appliedParams.cParams.chainLog == cdict_cParams->chainLog);- }-- /* copy tables */- { size_t const chainSize = (cdict_cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cdict_cParams->chainLog);- size_t const hSize = (size_t)1 << cdict_cParams->hashLog;- size_t const tableSpace = (chainSize + hSize) * sizeof(U32);- assert((U32*)cctx->blockState.matchState.chainTable == (U32*)cctx->blockState.matchState.hashTable + hSize); /* chainTable must follow hashTable */- assert((U32*)cctx->blockState.matchState.hashTable3 == (U32*)cctx->blockState.matchState.chainTable + chainSize);- assert((U32*)cdict->matchState.chainTable == (U32*)cdict->matchState.hashTable + hSize); /* chainTable must follow hashTable */- assert((U32*)cdict->matchState.hashTable3 == (U32*)cdict->matchState.chainTable + chainSize);- memcpy(cctx->blockState.matchState.hashTable, cdict->matchState.hashTable, tableSpace); /* presumes all tables follow each other */- }-- /* Zero the hashTable3, since the cdict never fills it */- { size_t const h3Size = (size_t)1 << cctx->blockState.matchState.hashLog3;- assert(cdict->matchState.hashLog3 == 0);- memset(cctx->blockState.matchState.hashTable3, 0, h3Size * sizeof(U32));- }-- /* copy dictionary offsets */- { ZSTD_matchState_t const* srcMatchState = &cdict->matchState;- ZSTD_matchState_t* dstMatchState = &cctx->blockState.matchState;- dstMatchState->window = srcMatchState->window;- dstMatchState->nextToUpdate = srcMatchState->nextToUpdate;- dstMatchState->nextToUpdate3= srcMatchState->nextToUpdate3;- dstMatchState->loadedDictEnd= srcMatchState->loadedDictEnd;- }-- cctx->dictID = cdict->dictID;-- /* copy block state */- memcpy(cctx->blockState.prevCBlock, &cdict->cBlockState, sizeof(cdict->cBlockState));-- return 0;-}--/* We have a choice between copying the dictionary context into the working- * context, or referencing the dictionary context from the working context- * in-place. We decide here which strategy to use. */-static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx,- const ZSTD_CDict* cdict,- ZSTD_CCtx_params params,- U64 pledgedSrcSize,- ZSTD_buffered_policy_e zbuff)-{-- DEBUGLOG(4, "ZSTD_resetCCtx_usingCDict (pledgedSrcSize=%u)",- (unsigned)pledgedSrcSize);-- if (ZSTD_shouldAttachDict(cdict, params, pledgedSrcSize)) {- return ZSTD_resetCCtx_byAttachingCDict(- cctx, cdict, params, pledgedSrcSize, zbuff);- } else {- return ZSTD_resetCCtx_byCopyingCDict(- cctx, cdict, params, pledgedSrcSize, zbuff);- }-}--/*! ZSTD_copyCCtx_internal() :- * Duplicate an existing context `srcCCtx` into another one `dstCCtx`.- * Only works during stage ZSTDcs_init (i.e. after creation, but before first call to ZSTD_compressContinue()).- * The "context", in this case, refers to the hash and chain tables,- * entropy tables, and dictionary references.- * `windowLog` value is enforced if != 0, otherwise value is copied from srcCCtx.- * @return : 0, or an error code */-static size_t ZSTD_copyCCtx_internal(ZSTD_CCtx* dstCCtx,- const ZSTD_CCtx* srcCCtx,- ZSTD_frameParameters fParams,- U64 pledgedSrcSize,- ZSTD_buffered_policy_e zbuff)-{- DEBUGLOG(5, "ZSTD_copyCCtx_internal");- RETURN_ERROR_IF(srcCCtx->stage!=ZSTDcs_init, stage_wrong);-- memcpy(&dstCCtx->customMem, &srcCCtx->customMem, sizeof(ZSTD_customMem));- { ZSTD_CCtx_params params = dstCCtx->requestedParams;- /* Copy only compression parameters related to tables. */- params.cParams = srcCCtx->appliedParams.cParams;- params.fParams = fParams;- ZSTD_resetCCtx_internal(dstCCtx, params, pledgedSrcSize,- ZSTDcrp_noMemset, zbuff);- assert(dstCCtx->appliedParams.cParams.windowLog == srcCCtx->appliedParams.cParams.windowLog);- assert(dstCCtx->appliedParams.cParams.strategy == srcCCtx->appliedParams.cParams.strategy);- assert(dstCCtx->appliedParams.cParams.hashLog == srcCCtx->appliedParams.cParams.hashLog);- assert(dstCCtx->appliedParams.cParams.chainLog == srcCCtx->appliedParams.cParams.chainLog);- assert(dstCCtx->blockState.matchState.hashLog3 == srcCCtx->blockState.matchState.hashLog3);- }-- /* copy tables */- { size_t const chainSize = (srcCCtx->appliedParams.cParams.strategy == ZSTD_fast) ? 0 : ((size_t)1 << srcCCtx->appliedParams.cParams.chainLog);- size_t const hSize = (size_t)1 << srcCCtx->appliedParams.cParams.hashLog;- size_t const h3Size = (size_t)1 << srcCCtx->blockState.matchState.hashLog3;- size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32);- assert((U32*)dstCCtx->blockState.matchState.chainTable == (U32*)dstCCtx->blockState.matchState.hashTable + hSize); /* chainTable must follow hashTable */- assert((U32*)dstCCtx->blockState.matchState.hashTable3 == (U32*)dstCCtx->blockState.matchState.chainTable + chainSize);- memcpy(dstCCtx->blockState.matchState.hashTable, srcCCtx->blockState.matchState.hashTable, tableSpace); /* presumes all tables follow each other */- }-- /* copy dictionary offsets */- {- const ZSTD_matchState_t* srcMatchState = &srcCCtx->blockState.matchState;- ZSTD_matchState_t* dstMatchState = &dstCCtx->blockState.matchState;- dstMatchState->window = srcMatchState->window;- dstMatchState->nextToUpdate = srcMatchState->nextToUpdate;- dstMatchState->nextToUpdate3= srcMatchState->nextToUpdate3;- dstMatchState->loadedDictEnd= srcMatchState->loadedDictEnd;- }- dstCCtx->dictID = srcCCtx->dictID;-- /* copy block state */- memcpy(dstCCtx->blockState.prevCBlock, srcCCtx->blockState.prevCBlock, sizeof(*srcCCtx->blockState.prevCBlock));-- return 0;-}--/*! ZSTD_copyCCtx() :- * Duplicate an existing context `srcCCtx` into another one `dstCCtx`.- * Only works during stage ZSTDcs_init (i.e. after creation, but before first call to ZSTD_compressContinue()).- * pledgedSrcSize==0 means "unknown".-* @return : 0, or an error code */-size_t ZSTD_copyCCtx(ZSTD_CCtx* dstCCtx, const ZSTD_CCtx* srcCCtx, unsigned long long pledgedSrcSize)-{- ZSTD_frameParameters fParams = { 1 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ };- ZSTD_buffered_policy_e const zbuff = (ZSTD_buffered_policy_e)(srcCCtx->inBuffSize>0);- ZSTD_STATIC_ASSERT((U32)ZSTDb_buffered==1);- if (pledgedSrcSize==0) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN;- fParams.contentSizeFlag = (pledgedSrcSize != ZSTD_CONTENTSIZE_UNKNOWN);-- return ZSTD_copyCCtx_internal(dstCCtx, srcCCtx,- fParams, pledgedSrcSize,- zbuff);-}---#define ZSTD_ROWSIZE 16-/*! ZSTD_reduceTable() :- * reduce table indexes by `reducerValue`, or squash to zero.- * PreserveMark preserves "unsorted mark" for btlazy2 strategy.- * It must be set to a clear 0/1 value, to remove branch during inlining.- * Presume table size is a multiple of ZSTD_ROWSIZE- * to help auto-vectorization */-FORCE_INLINE_TEMPLATE void-ZSTD_reduceTable_internal (U32* const table, U32 const size, U32 const reducerValue, int const preserveMark)-{- int const nbRows = (int)size / ZSTD_ROWSIZE;- int cellNb = 0;- int rowNb;- assert((size & (ZSTD_ROWSIZE-1)) == 0); /* multiple of ZSTD_ROWSIZE */- assert(size < (1U<<31)); /* can be casted to int */- for (rowNb=0 ; rowNb < nbRows ; rowNb++) {- int column;- for (column=0; column<ZSTD_ROWSIZE; column++) {- if (preserveMark) {- U32 const adder = (table[cellNb] == ZSTD_DUBT_UNSORTED_MARK) ? reducerValue : 0;- table[cellNb] += adder;- }- if (table[cellNb] < reducerValue) table[cellNb] = 0;- else table[cellNb] -= reducerValue;- cellNb++;- } }-}--static void ZSTD_reduceTable(U32* const table, U32 const size, U32 const reducerValue)-{- ZSTD_reduceTable_internal(table, size, reducerValue, 0);-}--static void ZSTD_reduceTable_btlazy2(U32* const table, U32 const size, U32 const reducerValue)-{- ZSTD_reduceTable_internal(table, size, reducerValue, 1);-}--/*! ZSTD_reduceIndex() :-* rescale all indexes to avoid future overflow (indexes are U32) */-static void ZSTD_reduceIndex (ZSTD_CCtx* zc, const U32 reducerValue)-{- ZSTD_matchState_t* const ms = &zc->blockState.matchState;- { U32 const hSize = (U32)1 << zc->appliedParams.cParams.hashLog;- ZSTD_reduceTable(ms->hashTable, hSize, reducerValue);- }-- if (zc->appliedParams.cParams.strategy != ZSTD_fast) {- U32 const chainSize = (U32)1 << zc->appliedParams.cParams.chainLog;- if (zc->appliedParams.cParams.strategy == ZSTD_btlazy2)- ZSTD_reduceTable_btlazy2(ms->chainTable, chainSize, reducerValue);- else- ZSTD_reduceTable(ms->chainTable, chainSize, reducerValue);- }-- if (ms->hashLog3) {- U32 const h3Size = (U32)1 << ms->hashLog3;- ZSTD_reduceTable(ms->hashTable3, h3Size, reducerValue);- }-}---/*-*******************************************************-* Block entropic compression-*********************************************************/--/* See doc/zstd_compression_format.md for detailed format description */--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);- RETURN_ERROR_IF(srcSize + ZSTD_blockHeaderSize > dstCapacity,- dstSize_tooSmall);- MEM_writeLE24(dst, cBlockHeader24);- memcpy((BYTE*)dst + ZSTD_blockHeaderSize, src, srcSize);- return ZSTD_blockHeaderSize + srcSize;-}--static size_t ZSTD_noCompressLiterals (void* dst, size_t dstCapacity, const void* src, size_t srcSize)-{- BYTE* const ostart = (BYTE* const)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);- }-- memcpy(ostart + flSize, src, srcSize);- return srcSize + flSize;-}--static size_t ZSTD_compressRleLiteralsBlock (void* dst, size_t dstCapacity, const void* src, size_t srcSize)-{- BYTE* const ostart = (BYTE* const)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;- return flSize+1;-}---/* ZSTD_minGain() :- * minimum compression required- * to generate a compress block or a compressed literals section.- * note : use same formula for both situations */-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));- return (srcSize >> minlog) + 2;-}--static 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* workspace, size_t wkspSize,- const int bmi2)-{- 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)",- disableLiteralCompression);-- /* Prepare nextEntropy assuming reusing the existing table */- 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, 255, 11,- workspace, wkspSize, (HUF_CElt*)nextHuf->CTable, &repeat, preferRepeat, bmi2)- : HUF_compress4X_repeat(ostart+lhSize, dstCapacity-lhSize, src, srcSize, 255, 11,- workspace, wkspSize, (HUF_CElt*)nextHuf->CTable, &repeat, preferRepeat, bmi2);- if (repeat != HUF_repeat_none) {- /* reused the existing table */- hType = set_repeat;- }- }-- if ((cLitSize==0) | (cLitSize >= srcSize - minGain) | ERR_isError(cLitSize)) {- memcpy(nextHuf, prevHuf, sizeof(*prevHuf));- return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize);- }- if (cLitSize==1) {- 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);- }- return lhSize+cLitSize;-}---void ZSTD_seqToCodes(const seqStore_t* seqStorePtr)-{- const seqDef* const sequences = seqStorePtr->sequencesStart;- BYTE* const llCodeTable = seqStorePtr->llCode;- BYTE* const ofCodeTable = seqStorePtr->ofCode;- BYTE* const mlCodeTable = seqStorePtr->mlCode;- U32 const nbSeq = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart);- U32 u;- assert(nbSeq <= seqStorePtr->maxNbSeq);- for (u=0; u<nbSeq; u++) {- U32 const llv = sequences[u].litLength;- U32 const mlv = sequences[u].matchLength;- llCodeTable[u] = (BYTE)ZSTD_LLcode(llv);- ofCodeTable[u] = (BYTE)ZSTD_highbit32(sequences[u].offset);- mlCodeTable[u] = (BYTE)ZSTD_MLcode(mlv);- }- if (seqStorePtr->longLengthID==1)- llCodeTable[seqStorePtr->longLengthPos] = MaxLL;- if (seqStorePtr->longLengthID==2)- mlCodeTable[seqStorePtr->longLengthPos] = MaxML;-}---/**- * -log2(x / 256) lookup table for x in [0, 256).- * If x == 0: Return 0- * Else: Return floor(-log2(x / 256) * 256)- */-static unsigned const kInverseProbabilityLog256[256] = {- 0, 2048, 1792, 1642, 1536, 1453, 1386, 1329, 1280, 1236, 1197, 1162,- 1130, 1100, 1073, 1047, 1024, 1001, 980, 960, 941, 923, 906, 889,- 874, 859, 844, 830, 817, 804, 791, 779, 768, 756, 745, 734,- 724, 714, 704, 694, 685, 676, 667, 658, 650, 642, 633, 626,- 618, 610, 603, 595, 588, 581, 574, 567, 561, 554, 548, 542,- 535, 529, 523, 517, 512, 506, 500, 495, 489, 484, 478, 473,- 468, 463, 458, 453, 448, 443, 438, 434, 429, 424, 420, 415,- 411, 407, 402, 398, 394, 390, 386, 382, 377, 373, 370, 366,- 362, 358, 354, 350, 347, 343, 339, 336, 332, 329, 325, 322,- 318, 315, 311, 308, 305, 302, 298, 295, 292, 289, 286, 282,- 279, 276, 273, 270, 267, 264, 261, 258, 256, 253, 250, 247,- 244, 241, 239, 236, 233, 230, 228, 225, 222, 220, 217, 215,- 212, 209, 207, 204, 202, 199, 197, 194, 192, 190, 187, 185,- 182, 180, 178, 175, 173, 171, 168, 166, 164, 162, 159, 157,- 155, 153, 151, 149, 146, 144, 142, 140, 138, 136, 134, 132,- 130, 128, 126, 123, 121, 119, 117, 115, 114, 112, 110, 108,- 106, 104, 102, 100, 98, 96, 94, 93, 91, 89, 87, 85,- 83, 82, 80, 78, 76, 74, 73, 71, 69, 67, 66, 64,- 62, 61, 59, 57, 55, 54, 52, 50, 49, 47, 46, 44,- 42, 41, 39, 37, 36, 34, 33, 31, 30, 28, 26, 25,- 23, 22, 20, 19, 17, 16, 14, 13, 11, 10, 8, 7,- 5, 4, 2, 1,-};---/**- * Returns the cost in bits of encoding the distribution described by count- * using the entropy bound.- */-static size_t ZSTD_entropyCost(unsigned const* count, unsigned const max, size_t const total)-{- unsigned cost = 0;- unsigned s;- for (s = 0; s <= max; ++s) {- unsigned norm = (unsigned)((256 * count[s]) / total);- if (count[s] != 0 && norm == 0)- norm = 1;- assert(count[s] < total);- cost += count[s] * kInverseProbabilityLog256[norm];- }- return cost >> 8;-}---/**- * Returns the cost in bits of encoding the distribution in count using the- * table described by norm. The max symbol support by norm is assumed >= max.- * norm must be valid for every symbol with non-zero probability in count.- */-static size_t ZSTD_crossEntropyCost(short const* norm, unsigned accuracyLog,- unsigned const* count, unsigned const max)-{- unsigned const shift = 8 - accuracyLog;- size_t cost = 0;- unsigned s;- assert(accuracyLog <= 8);- for (s = 0; s <= max; ++s) {- unsigned const normAcc = norm[s] != -1 ? norm[s] : 1;- unsigned const norm256 = normAcc << shift;- assert(norm256 > 0);- assert(norm256 < 256);- cost += count[s] * kInverseProbabilityLog256[norm256];- }- return cost >> 8;-}---static unsigned ZSTD_getFSEMaxSymbolValue(FSE_CTable const* ctable) {- void const* ptr = ctable;- U16 const* u16ptr = (U16 const*)ptr;- U32 const maxSymbolValue = MEM_read16(u16ptr + 1);- return maxSymbolValue;-}---/**- * Returns the cost in bits of encoding the distribution in count using ctable.- * Returns an error if ctable cannot represent all the symbols in count.- */-static size_t ZSTD_fseBitCost(- FSE_CTable const* ctable,- unsigned const* count,- unsigned const max)-{- unsigned const kAccuracyLog = 8;- size_t cost = 0;- unsigned s;- FSE_CState_t cstate;- FSE_initCState(&cstate, ctable);- RETURN_ERROR_IF(ZSTD_getFSEMaxSymbolValue(ctable) < max, GENERIC,- "Repeat FSE_CTable has maxSymbolValue %u < %u",- ZSTD_getFSEMaxSymbolValue(ctable), max);- for (s = 0; s <= max; ++s) {- unsigned const tableLog = cstate.stateLog;- unsigned const badCost = (tableLog + 1) << kAccuracyLog;- unsigned const bitCost = FSE_bitCost(cstate.symbolTT, tableLog, s, kAccuracyLog);- if (count[s] == 0)- continue;- RETURN_ERROR_IF(bitCost >= badCost, GENERIC,- "Repeat FSE_CTable has Prob[%u] == 0", s);- cost += count[s] * bitCost;- }- return cost >> kAccuracyLog;-}--/**- * Returns the cost in bytes of encoding the normalized count header.- * Returns an error if any of the helper functions return an error.- */-static size_t ZSTD_NCountCost(unsigned const* count, unsigned const max,- size_t const nbSeq, unsigned const FSELog)-{- BYTE wksp[FSE_NCOUNTBOUND];- S16 norm[MaxSeq + 1];- const U32 tableLog = FSE_optimalTableLog(FSELog, nbSeq, max);- FORWARD_IF_ERROR(FSE_normalizeCount(norm, tableLog, count, nbSeq, max));- return FSE_writeNCount(wksp, sizeof(wksp), norm, max, tableLog);-}---typedef enum {- ZSTD_defaultDisallowed = 0,- ZSTD_defaultAllowed = 1-} ZSTD_defaultPolicy_e;--MEM_STATIC symbolEncodingType_e-ZSTD_selectEncodingType(- FSE_repeat* repeatMode, unsigned const* count, unsigned const max,- size_t const mostFrequent, size_t nbSeq, unsigned const FSELog,- FSE_CTable const* prevCTable,- short const* defaultNorm, U32 defaultNormLog,- ZSTD_defaultPolicy_e const isDefaultAllowed,- ZSTD_strategy const strategy)-{- ZSTD_STATIC_ASSERT(ZSTD_defaultDisallowed == 0 && ZSTD_defaultAllowed != 0);- if (mostFrequent == nbSeq) {- *repeatMode = FSE_repeat_none;- if (isDefaultAllowed && nbSeq <= 2) {- /* Prefer set_basic over set_rle when there are 2 or less symbols,- * since RLE uses 1 byte, but set_basic uses 5-6 bits per symbol.- * If basic encoding isn't possible, always choose RLE.- */- DEBUGLOG(5, "Selected set_basic");- return set_basic;- }- DEBUGLOG(5, "Selected set_rle");- return set_rle;- }- if (strategy < ZSTD_lazy) {- if (isDefaultAllowed) {- size_t const staticFse_nbSeq_max = 1000;- size_t const mult = 10 - strategy;- size_t const baseLog = 3;- size_t const dynamicFse_nbSeq_min = (((size_t)1 << defaultNormLog) * mult) >> baseLog; /* 28-36 for offset, 56-72 for lengths */- assert(defaultNormLog >= 5 && defaultNormLog <= 6); /* xx_DEFAULTNORMLOG */- assert(mult <= 9 && mult >= 7);- if ( (*repeatMode == FSE_repeat_valid)- && (nbSeq < staticFse_nbSeq_max) ) {- DEBUGLOG(5, "Selected set_repeat");- return set_repeat;- }- if ( (nbSeq < dynamicFse_nbSeq_min)- || (mostFrequent < (nbSeq >> (defaultNormLog-1))) ) {- DEBUGLOG(5, "Selected set_basic");- /* The format allows default tables to be repeated, but it isn't useful.- * When using simple heuristics to select encoding type, we don't want- * to confuse these tables with dictionaries. When running more careful- * analysis, we don't need to waste time checking both repeating tables- * and default tables.- */- *repeatMode = FSE_repeat_none;- return set_basic;- }- }- } else {- size_t const basicCost = isDefaultAllowed ? ZSTD_crossEntropyCost(defaultNorm, defaultNormLog, count, max) : ERROR(GENERIC);- size_t const repeatCost = *repeatMode != FSE_repeat_none ? ZSTD_fseBitCost(prevCTable, count, max) : ERROR(GENERIC);- size_t const NCountCost = ZSTD_NCountCost(count, max, nbSeq, FSELog);- size_t const compressedCost = (NCountCost << 3) + ZSTD_entropyCost(count, max, nbSeq);-- if (isDefaultAllowed) {- assert(!ZSTD_isError(basicCost));- assert(!(*repeatMode == FSE_repeat_valid && ZSTD_isError(repeatCost)));- }- assert(!ZSTD_isError(NCountCost));- assert(compressedCost < ERROR(maxCode));- DEBUGLOG(5, "Estimated bit costs: basic=%u\trepeat=%u\tcompressed=%u",- (unsigned)basicCost, (unsigned)repeatCost, (unsigned)compressedCost);- if (basicCost <= repeatCost && basicCost <= compressedCost) {- DEBUGLOG(5, "Selected set_basic");- assert(isDefaultAllowed);- *repeatMode = FSE_repeat_none;- return set_basic;- }- if (repeatCost <= compressedCost) {- DEBUGLOG(5, "Selected set_repeat");- assert(!ZSTD_isError(repeatCost));- return set_repeat;- }- assert(compressedCost < basicCost && compressedCost < repeatCost);- }- DEBUGLOG(5, "Selected set_compressed");- *repeatMode = FSE_repeat_check;- return set_compressed;-}--MEM_STATIC size_t-ZSTD_buildCTable(void* dst, size_t dstCapacity,- FSE_CTable* nextCTable, U32 FSELog, symbolEncodingType_e type,- unsigned* count, U32 max,- const BYTE* codeTable, size_t nbSeq,- const S16* defaultNorm, U32 defaultNormLog, U32 defaultMax,- const FSE_CTable* prevCTable, size_t prevCTableSize,- void* workspace, size_t workspaceSize)-{- BYTE* op = (BYTE*)dst;- const BYTE* const oend = op + dstCapacity;- DEBUGLOG(6, "ZSTD_buildCTable (dstCapacity=%u)", (unsigned)dstCapacity);-- switch (type) {- case set_rle:- FORWARD_IF_ERROR(FSE_buildCTable_rle(nextCTable, (BYTE)max));- RETURN_ERROR_IF(dstCapacity==0, dstSize_tooSmall);- *op = codeTable[0];- return 1;- case set_repeat:- memcpy(nextCTable, prevCTable, prevCTableSize);- return 0;- case set_basic:- FORWARD_IF_ERROR(FSE_buildCTable_wksp(nextCTable, defaultNorm, defaultMax, defaultNormLog, workspace, workspaceSize)); /* note : could be pre-calculated */- return 0;- case set_compressed: {- S16 norm[MaxSeq + 1];- size_t nbSeq_1 = nbSeq;- const U32 tableLog = FSE_optimalTableLog(FSELog, nbSeq, max);- if (count[codeTable[nbSeq-1]] > 1) {- count[codeTable[nbSeq-1]]--;- nbSeq_1--;- }- assert(nbSeq_1 > 1);- FORWARD_IF_ERROR(FSE_normalizeCount(norm, tableLog, count, nbSeq_1, max));- { size_t const NCountSize = FSE_writeNCount(op, oend - op, norm, max, tableLog); /* overflow protected */- FORWARD_IF_ERROR(NCountSize);- FORWARD_IF_ERROR(FSE_buildCTable_wksp(nextCTable, norm, max, tableLog, workspace, workspaceSize));- return NCountSize;- }- }- default: assert(0); RETURN_ERROR(GENERIC);- }-}--FORCE_INLINE_TEMPLATE size_t-ZSTD_encodeSequences_body(- void* dst, size_t dstCapacity,- FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable,- FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable,- FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable,- seqDef const* sequences, size_t nbSeq, int longOffsets)-{- BIT_CStream_t blockStream;- FSE_CState_t stateMatchLength;- FSE_CState_t stateOffsetBits;- FSE_CState_t stateLitLength;-- RETURN_ERROR_IF(- ERR_isError(BIT_initCStream(&blockStream, dst, dstCapacity)),- dstSize_tooSmall, "not enough space remaining");- DEBUGLOG(6, "available space for bitstream : %i (dstCapacity=%u)",- (int)(blockStream.endPtr - blockStream.startPtr),- (unsigned)dstCapacity);-- /* first symbols */- FSE_initCState2(&stateMatchLength, CTable_MatchLength, mlCodeTable[nbSeq-1]);- FSE_initCState2(&stateOffsetBits, CTable_OffsetBits, ofCodeTable[nbSeq-1]);- FSE_initCState2(&stateLitLength, CTable_LitLength, llCodeTable[nbSeq-1]);- BIT_addBits(&blockStream, sequences[nbSeq-1].litLength, LL_bits[llCodeTable[nbSeq-1]]);- if (MEM_32bits()) BIT_flushBits(&blockStream);- BIT_addBits(&blockStream, sequences[nbSeq-1].matchLength, ML_bits[mlCodeTable[nbSeq-1]]);- if (MEM_32bits()) BIT_flushBits(&blockStream);- if (longOffsets) {- U32 const ofBits = ofCodeTable[nbSeq-1];- int const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN-1);- if (extraBits) {- BIT_addBits(&blockStream, sequences[nbSeq-1].offset, extraBits);- BIT_flushBits(&blockStream);- }- BIT_addBits(&blockStream, sequences[nbSeq-1].offset >> extraBits,- ofBits - extraBits);- } else {- BIT_addBits(&blockStream, sequences[nbSeq-1].offset, ofCodeTable[nbSeq-1]);- }- BIT_flushBits(&blockStream);-- { size_t n;- for (n=nbSeq-2 ; n<nbSeq ; n--) { /* intentional underflow */- BYTE const llCode = llCodeTable[n];- BYTE const ofCode = ofCodeTable[n];- BYTE const mlCode = mlCodeTable[n];- U32 const llBits = LL_bits[llCode];- U32 const ofBits = ofCode;- U32 const mlBits = ML_bits[mlCode];- DEBUGLOG(6, "encoding: litlen:%2u - matchlen:%2u - offCode:%7u",- (unsigned)sequences[n].litLength,- (unsigned)sequences[n].matchLength + MINMATCH,- (unsigned)sequences[n].offset);- /* 32b*/ /* 64b*/- /* (7)*/ /* (7)*/- FSE_encodeSymbol(&blockStream, &stateOffsetBits, ofCode); /* 15 */ /* 15 */- FSE_encodeSymbol(&blockStream, &stateMatchLength, mlCode); /* 24 */ /* 24 */- if (MEM_32bits()) BIT_flushBits(&blockStream); /* (7)*/- FSE_encodeSymbol(&blockStream, &stateLitLength, llCode); /* 16 */ /* 33 */- if (MEM_32bits() || (ofBits+mlBits+llBits >= 64-7-(LLFSELog+MLFSELog+OffFSELog)))- BIT_flushBits(&blockStream); /* (7)*/- BIT_addBits(&blockStream, sequences[n].litLength, llBits);- if (MEM_32bits() && ((llBits+mlBits)>24)) BIT_flushBits(&blockStream);- BIT_addBits(&blockStream, sequences[n].matchLength, mlBits);- if (MEM_32bits() || (ofBits+mlBits+llBits > 56)) BIT_flushBits(&blockStream);- if (longOffsets) {- int const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN-1);- if (extraBits) {- BIT_addBits(&blockStream, sequences[n].offset, extraBits);- BIT_flushBits(&blockStream); /* (7)*/- }- BIT_addBits(&blockStream, sequences[n].offset >> extraBits,- ofBits - extraBits); /* 31 */- } else {- BIT_addBits(&blockStream, sequences[n].offset, ofBits); /* 31 */- }- BIT_flushBits(&blockStream); /* (7)*/- DEBUGLOG(7, "remaining space : %i", (int)(blockStream.endPtr - blockStream.ptr));- } }-- DEBUGLOG(6, "ZSTD_encodeSequences: flushing ML state with %u bits", stateMatchLength.stateLog);- FSE_flushCState(&blockStream, &stateMatchLength);- DEBUGLOG(6, "ZSTD_encodeSequences: flushing Off state with %u bits", stateOffsetBits.stateLog);- FSE_flushCState(&blockStream, &stateOffsetBits);- DEBUGLOG(6, "ZSTD_encodeSequences: flushing LL state with %u bits", stateLitLength.stateLog);- FSE_flushCState(&blockStream, &stateLitLength);-- { size_t const streamSize = BIT_closeCStream(&blockStream);- RETURN_ERROR_IF(streamSize==0, dstSize_tooSmall, "not enough space");- return streamSize;- }-}--static size_t-ZSTD_encodeSequences_default(- void* dst, size_t dstCapacity,- FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable,- FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable,- FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable,- seqDef const* sequences, size_t nbSeq, int longOffsets)-{- return ZSTD_encodeSequences_body(dst, dstCapacity,- CTable_MatchLength, mlCodeTable,- CTable_OffsetBits, ofCodeTable,- CTable_LitLength, llCodeTable,- sequences, nbSeq, longOffsets);-}---#if DYNAMIC_BMI2--static TARGET_ATTRIBUTE("bmi2") size_t-ZSTD_encodeSequences_bmi2(- void* dst, size_t dstCapacity,- FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable,- FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable,- FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable,- seqDef const* sequences, size_t nbSeq, int longOffsets)-{- return ZSTD_encodeSequences_body(dst, dstCapacity,- CTable_MatchLength, mlCodeTable,- CTable_OffsetBits, ofCodeTable,- CTable_LitLength, llCodeTable,- sequences, nbSeq, longOffsets);-}--#endif--static size_t ZSTD_encodeSequences(- void* dst, size_t dstCapacity,- FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable,- FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable,- FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable,- seqDef const* sequences, size_t nbSeq, int longOffsets, int bmi2)-{- DEBUGLOG(5, "ZSTD_encodeSequences: dstCapacity = %u", (unsigned)dstCapacity);-#if DYNAMIC_BMI2- if (bmi2) {- return ZSTD_encodeSequences_bmi2(dst, dstCapacity,- CTable_MatchLength, mlCodeTable,- CTable_OffsetBits, ofCodeTable,- CTable_LitLength, llCodeTable,- sequences, nbSeq, longOffsets);- }-#endif- (void)bmi2;- return ZSTD_encodeSequences_default(dst, dstCapacity,- CTable_MatchLength, mlCodeTable,- CTable_OffsetBits, ofCodeTable,- CTable_LitLength, llCodeTable,- sequences, nbSeq, longOffsets);-}--static int ZSTD_disableLiteralsCompression(const ZSTD_CCtx_params* cctxParams)-{- switch (cctxParams->literalCompressionMode) {- case ZSTD_lcm_huffman:- return 0;- case ZSTD_lcm_uncompressed:- return 1;- default:- assert(0 /* impossible: pre-validated */);- /* fall-through */- case ZSTD_lcm_auto:- return (cctxParams->cParams.strategy == ZSTD_fast) && (cctxParams->cParams.targetLength > 0);- }-}--/* ZSTD_compressSequences_internal():- * actually compresses both literals and sequences */-MEM_STATIC size_t-ZSTD_compressSequences_internal(seqStore_t* seqStorePtr,- const ZSTD_entropyCTables_t* prevEntropy,- ZSTD_entropyCTables_t* nextEntropy,- const ZSTD_CCtx_params* cctxParams,- void* dst, size_t dstCapacity,- void* workspace, size_t wkspSize,- const int bmi2)-{- const int longOffsets = cctxParams->cParams.windowLog > STREAM_ACCUMULATOR_MIN;- ZSTD_strategy const strategy = cctxParams->cParams.strategy;- unsigned count[MaxSeq+1];- FSE_CTable* CTable_LitLength = nextEntropy->fse.litlengthCTable;- FSE_CTable* CTable_OffsetBits = nextEntropy->fse.offcodeCTable;- FSE_CTable* CTable_MatchLength = nextEntropy->fse.matchlengthCTable;- U32 LLtype, Offtype, MLtype; /* compressed, raw or rle */- const seqDef* const sequences = seqStorePtr->sequencesStart;- const BYTE* const ofCodeTable = seqStorePtr->ofCode;- const BYTE* const llCodeTable = seqStorePtr->llCode;- const BYTE* const mlCodeTable = seqStorePtr->mlCode;- BYTE* const ostart = (BYTE*)dst;- BYTE* const oend = ostart + dstCapacity;- BYTE* op = ostart;- size_t const nbSeq = seqStorePtr->sequences - seqStorePtr->sequencesStart;- BYTE* seqHead;- BYTE* lastNCount = NULL;-- ZSTD_STATIC_ASSERT(HUF_WORKSPACE_SIZE >= (1<<MAX(MLFSELog,LLFSELog)));- DEBUGLOG(5, "ZSTD_compressSequences_internal");-- /* Compress literals */- { const BYTE* const literals = seqStorePtr->litStart;- size_t const litSize = seqStorePtr->lit - literals;- size_t const cSize = ZSTD_compressLiterals(- &prevEntropy->huf, &nextEntropy->huf,- cctxParams->cParams.strategy,- ZSTD_disableLiteralsCompression(cctxParams),- op, dstCapacity,- literals, litSize,- workspace, wkspSize,- bmi2);- FORWARD_IF_ERROR(cSize);- assert(cSize <= dstCapacity);- op += cSize;- }-- /* Sequences Header */- RETURN_ERROR_IF((oend-op) < 3 /*max nbSeq Size*/ + 1 /*seqHead*/,- dstSize_tooSmall);- if (nbSeq < 0x7F)- *op++ = (BYTE)nbSeq;- else if (nbSeq < LONGNBSEQ)- op[0] = (BYTE)((nbSeq>>8) + 0x80), op[1] = (BYTE)nbSeq, op+=2;- else- op[0]=0xFF, MEM_writeLE16(op+1, (U16)(nbSeq - LONGNBSEQ)), op+=3;- if (nbSeq==0) {- /* Copy the old tables over as if we repeated them */- memcpy(&nextEntropy->fse, &prevEntropy->fse, sizeof(prevEntropy->fse));- return op - ostart;- }-- /* seqHead : flags for FSE encoding type */- seqHead = op++;-- /* convert length/distances into codes */- ZSTD_seqToCodes(seqStorePtr);- /* build CTable for Literal Lengths */- { unsigned max = MaxLL;- size_t const mostFrequent = HIST_countFast_wksp(count, &max, llCodeTable, nbSeq, workspace, wkspSize); /* can't fail */- DEBUGLOG(5, "Building LL table");- nextEntropy->fse.litlength_repeatMode = prevEntropy->fse.litlength_repeatMode;- LLtype = ZSTD_selectEncodingType(&nextEntropy->fse.litlength_repeatMode,- count, max, mostFrequent, nbSeq,- LLFSELog, prevEntropy->fse.litlengthCTable,- LL_defaultNorm, LL_defaultNormLog,- ZSTD_defaultAllowed, strategy);- assert(set_basic < set_compressed && set_rle < set_compressed);- assert(!(LLtype < set_compressed && nextEntropy->fse.litlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */- { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_LitLength, LLFSELog, (symbolEncodingType_e)LLtype,- count, max, llCodeTable, nbSeq, LL_defaultNorm, LL_defaultNormLog, MaxLL,- prevEntropy->fse.litlengthCTable, sizeof(prevEntropy->fse.litlengthCTable),- workspace, wkspSize);- FORWARD_IF_ERROR(countSize);- if (LLtype == set_compressed)- lastNCount = op;- op += countSize;- } }- /* build CTable for Offsets */- { unsigned max = MaxOff;- size_t const mostFrequent = HIST_countFast_wksp(count, &max, ofCodeTable, nbSeq, workspace, wkspSize); /* can't fail */- /* We can only use the basic table if max <= DefaultMaxOff, otherwise the offsets are too large */- ZSTD_defaultPolicy_e const defaultPolicy = (max <= DefaultMaxOff) ? ZSTD_defaultAllowed : ZSTD_defaultDisallowed;- DEBUGLOG(5, "Building OF table");- nextEntropy->fse.offcode_repeatMode = prevEntropy->fse.offcode_repeatMode;- Offtype = ZSTD_selectEncodingType(&nextEntropy->fse.offcode_repeatMode,- count, max, mostFrequent, nbSeq,- OffFSELog, prevEntropy->fse.offcodeCTable,- OF_defaultNorm, OF_defaultNormLog,- defaultPolicy, strategy);- assert(!(Offtype < set_compressed && nextEntropy->fse.offcode_repeatMode != FSE_repeat_none)); /* We don't copy tables */- { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_OffsetBits, OffFSELog, (symbolEncodingType_e)Offtype,- count, max, ofCodeTable, nbSeq, OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff,- prevEntropy->fse.offcodeCTable, sizeof(prevEntropy->fse.offcodeCTable),- workspace, wkspSize);- FORWARD_IF_ERROR(countSize);- if (Offtype == set_compressed)- lastNCount = op;- op += countSize;- } }- /* build CTable for MatchLengths */- { unsigned max = MaxML;- size_t const mostFrequent = HIST_countFast_wksp(count, &max, mlCodeTable, nbSeq, workspace, wkspSize); /* can't fail */- DEBUGLOG(5, "Building ML table (remaining space : %i)", (int)(oend-op));- nextEntropy->fse.matchlength_repeatMode = prevEntropy->fse.matchlength_repeatMode;- MLtype = ZSTD_selectEncodingType(&nextEntropy->fse.matchlength_repeatMode,- count, max, mostFrequent, nbSeq,- MLFSELog, prevEntropy->fse.matchlengthCTable,- ML_defaultNorm, ML_defaultNormLog,- ZSTD_defaultAllowed, strategy);- assert(!(MLtype < set_compressed && nextEntropy->fse.matchlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */- { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_MatchLength, MLFSELog, (symbolEncodingType_e)MLtype,- count, max, mlCodeTable, nbSeq, ML_defaultNorm, ML_defaultNormLog, MaxML,- prevEntropy->fse.matchlengthCTable, sizeof(prevEntropy->fse.matchlengthCTable),- workspace, wkspSize);- FORWARD_IF_ERROR(countSize);- if (MLtype == set_compressed)- lastNCount = op;- op += countSize;- } }-- *seqHead = (BYTE)((LLtype<<6) + (Offtype<<4) + (MLtype<<2));-- { size_t const bitstreamSize = ZSTD_encodeSequences(- op, oend - op,- CTable_MatchLength, mlCodeTable,- CTable_OffsetBits, ofCodeTable,- CTable_LitLength, llCodeTable,- sequences, nbSeq,- longOffsets, bmi2);- FORWARD_IF_ERROR(bitstreamSize);- op += bitstreamSize;- /* zstd versions <= 1.3.4 mistakenly report corruption when- * FSE_readNCount() receives a buffer < 4 bytes.- * Fixed by https://github.com/facebook/zstd/pull/1146.- * This can happen when the last set_compressed table present is 2- * bytes and the bitstream is only one byte.- * In this exceedingly rare case, we will simply emit an uncompressed- * block, since it isn't worth optimizing.- */- if (lastNCount && (op - lastNCount) < 4) {- /* NCountSize >= 2 && bitstreamSize > 0 ==> lastCountSize == 3 */- assert(op - lastNCount == 3);- DEBUGLOG(5, "Avoiding bug in zstd decoder in versions <= 1.3.4 by "- "emitting an uncompressed block.");- return 0;- }- }-- DEBUGLOG(5, "compressed block size : %u", (unsigned)(op - ostart));- return op - ostart;-}--MEM_STATIC size_t-ZSTD_compressSequences(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* workspace, size_t wkspSize,- int bmi2)-{- size_t const cSize = ZSTD_compressSequences_internal(- seqStorePtr, prevEntropy, nextEntropy, cctxParams,- dst, dstCapacity,- workspace, wkspSize, bmi2);- if (cSize == 0) return 0;- /* 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))- return 0; /* block not compressed */- FORWARD_IF_ERROR(cSize);-- /* Check compressibility */- { size_t const maxCSize = srcSize - ZSTD_minGain(srcSize, cctxParams->cParams.strategy);- if (cSize >= maxCSize) return 0; /* block not compressed */- }-- return cSize;-}--/* ZSTD_selectBlockCompressor() :- * Not static, but internal use only (used by long distance matcher)- * assumption : strat is a valid strategy */-ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_dictMode_e dictMode)-{- static const ZSTD_blockCompressor blockCompressor[3][ZSTD_STRATEGY_MAX+1] = {- { ZSTD_compressBlock_fast /* default for 0 */,- ZSTD_compressBlock_fast,- ZSTD_compressBlock_doubleFast,- ZSTD_compressBlock_greedy,- ZSTD_compressBlock_lazy,- ZSTD_compressBlock_lazy2,- ZSTD_compressBlock_btlazy2,- ZSTD_compressBlock_btopt,- ZSTD_compressBlock_btultra,- ZSTD_compressBlock_btultra2 },- { ZSTD_compressBlock_fast_extDict /* default for 0 */,- ZSTD_compressBlock_fast_extDict,- ZSTD_compressBlock_doubleFast_extDict,- ZSTD_compressBlock_greedy_extDict,- ZSTD_compressBlock_lazy_extDict,- ZSTD_compressBlock_lazy2_extDict,- ZSTD_compressBlock_btlazy2_extDict,- ZSTD_compressBlock_btopt_extDict,- ZSTD_compressBlock_btultra_extDict,- ZSTD_compressBlock_btultra_extDict },- { ZSTD_compressBlock_fast_dictMatchState /* default for 0 */,- ZSTD_compressBlock_fast_dictMatchState,- ZSTD_compressBlock_doubleFast_dictMatchState,- ZSTD_compressBlock_greedy_dictMatchState,- ZSTD_compressBlock_lazy_dictMatchState,- ZSTD_compressBlock_lazy2_dictMatchState,- ZSTD_compressBlock_btlazy2_dictMatchState,- ZSTD_compressBlock_btopt_dictMatchState,- ZSTD_compressBlock_btultra_dictMatchState,- ZSTD_compressBlock_btultra_dictMatchState }- };- ZSTD_blockCompressor selectedCompressor;- ZSTD_STATIC_ASSERT((unsigned)ZSTD_fast == 1);-- assert(ZSTD_cParam_withinBounds(ZSTD_c_strategy, strat));- selectedCompressor = blockCompressor[(int)dictMode][(int)strat];- assert(selectedCompressor != NULL);- return selectedCompressor;-}--static void ZSTD_storeLastLiterals(seqStore_t* seqStorePtr,- const BYTE* anchor, size_t lastLLSize)-{- memcpy(seqStorePtr->lit, anchor, lastLLSize);- seqStorePtr->lit += lastLLSize;-}--void ZSTD_resetSeqStore(seqStore_t* ssPtr)-{- ssPtr->lit = ssPtr->litStart;- ssPtr->sequences = ssPtr->sequencesStart;- ssPtr->longLengthID = 0;-}--static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc,- void* dst, size_t dstCapacity,- const void* src, size_t srcSize)-{- ZSTD_matchState_t* const ms = &zc->blockState.matchState;- size_t cSize;- DEBUGLOG(5, "ZSTD_compressBlock_internal (dstCapacity=%u, dictLimit=%u, nextToUpdate=%u)",- (unsigned)dstCapacity, (unsigned)ms->window.dictLimit, (unsigned)ms->nextToUpdate);- 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) {- ZSTD_ldm_skipSequences(&zc->externSeqStore, srcSize, zc->appliedParams.cParams.minMatch);- cSize = 0;- goto out; /* don't even attempt compression below a certain srcSize */- }- ZSTD_resetSeqStore(&(zc->seqStore));- /* required for optimal parser to read stats from dictionary */- ms->opt.symbolCosts = &zc->blockState.prevCBlock->entropy;- /* tell the optimal parser how we expect to compress literals */- ms->opt.literalCompressionMode = zc->appliedParams.literalCompressionMode;-- /* a gap between an attached dict and the current window is not safe,- * they must remain adjacent,- * and when that stops being the case, the dict must be unset */- assert(ms->dictMatchState == NULL || ms->loadedDictEnd == ms->window.dictLimit);-- /* limited update after a very long match */- { const BYTE* const base = ms->window.base;- const BYTE* const istart = (const BYTE*)src;- const U32 current = (U32)(istart-base);- if (sizeof(ptrdiff_t)==8) assert(istart - base < (ptrdiff_t)(U32)(-1)); /* ensure no overflow */- if (current > ms->nextToUpdate + 384)- ms->nextToUpdate = current - MIN(192, (U32)(current - ms->nextToUpdate - 384));- }-- /* select and store sequences */- { ZSTD_dictMode_e const dictMode = ZSTD_matchState_dictMode(ms);- size_t lastLLSize;- { int i;- for (i = 0; i < ZSTD_REP_NUM; ++i)- zc->blockState.nextCBlock->rep[i] = zc->blockState.prevCBlock->rep[i];- }- if (zc->externSeqStore.pos < zc->externSeqStore.size) {- assert(!zc->appliedParams.ldmParams.enableLdm);- /* Updates ldmSeqStore.pos */- lastLLSize =- ZSTD_ldm_blockCompress(&zc->externSeqStore,- ms, &zc->seqStore,- zc->blockState.nextCBlock->rep,- src, srcSize);- assert(zc->externSeqStore.pos <= zc->externSeqStore.size);- } else if (zc->appliedParams.ldmParams.enableLdm) {- rawSeqStore_t ldmSeqStore = {NULL, 0, 0, 0};-- ldmSeqStore.seq = zc->ldmSequences;- ldmSeqStore.capacity = zc->maxNbLdmSequences;- /* Updates ldmSeqStore.size */- FORWARD_IF_ERROR(ZSTD_ldm_generateSequences(&zc->ldmState, &ldmSeqStore,- &zc->appliedParams.ldmParams,- src, srcSize));- /* Updates ldmSeqStore.pos */- lastLLSize =- ZSTD_ldm_blockCompress(&ldmSeqStore,- ms, &zc->seqStore,- zc->blockState.nextCBlock->rep,- src, srcSize);- assert(ldmSeqStore.pos == ldmSeqStore.size);- } else { /* not long range mode */- ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, dictMode);- lastLLSize = blockCompressor(ms, &zc->seqStore, zc->blockState.nextCBlock->rep, src, srcSize);- }- { const BYTE* const lastLiterals = (const BYTE*)src + srcSize - lastLLSize;- ZSTD_storeLastLiterals(&zc->seqStore, lastLiterals, lastLLSize);- } }-- /* encode sequences and literals */- cSize = ZSTD_compressSequences(&zc->seqStore,- &zc->blockState.prevCBlock->entropy, &zc->blockState.nextCBlock->entropy,- &zc->appliedParams,- dst, dstCapacity,- srcSize,- zc->entropyWorkspace, HUF_WORKSPACE_SIZE /* statically allocated in resetCCtx */,- zc->bmi2);--out:- if (!ZSTD_isError(cSize) && cSize != 0) {- /* confirm repcodes and entropy tables when emitting a compressed block */- ZSTD_compressedBlockState_t* const tmp = zc->blockState.prevCBlock;- zc->blockState.prevCBlock = zc->blockState.nextCBlock;- zc->blockState.nextCBlock = tmp;- }- /* We check that dictionaries have offset codes available for the first- * block. After the first block, the offcode table might not have large- * enough codes to represent the offsets in the data.- */- if (zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat_valid)- zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode = FSE_repeat_check;-- return cSize;-}---/*! ZSTD_compress_frameChunk() :-* Compress a chunk of data into one or multiple blocks.-* 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-*/-static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx,- void* dst, size_t dstCapacity,- const void* src, size_t srcSize,- U32 lastFrameChunk)-{- size_t blockSize = cctx->blockSize;- size_t remaining = srcSize;- const BYTE* ip = (const BYTE*)src;- BYTE* const ostart = (BYTE*)dst;- BYTE* op = ostart;- U32 const maxDist = (U32)1 << cctx->appliedParams.cParams.windowLog;- assert(cctx->appliedParams.cParams.windowLog <= 31);-- DEBUGLOG(5, "ZSTD_compress_frameChunk (blockSize=%u)", (unsigned)blockSize);- if (cctx->appliedParams.fParams.checksumFlag && srcSize)- XXH64_update(&cctx->xxhState, src, srcSize);-- while (remaining) {- ZSTD_matchState_t* const ms = &cctx->blockState.matchState;- U32 const lastBlock = lastFrameChunk & (blockSize >= remaining);-- RETURN_ERROR_IF(dstCapacity < ZSTD_blockHeaderSize + MIN_CBLOCK_SIZE,- dstSize_tooSmall,- "not enough space to store compressed block");- if (remaining < blockSize) blockSize = remaining;-- if (ZSTD_window_needOverflowCorrection(ms->window, ip + blockSize)) {- U32 const cycleLog = ZSTD_cycleLog(cctx->appliedParams.cParams.chainLog, cctx->appliedParams.cParams.strategy);- U32 const correction = ZSTD_window_correctOverflow(&ms->window, cycleLog, maxDist, ip);- ZSTD_STATIC_ASSERT(ZSTD_CHAINLOG_MAX <= 30);- ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX_32 <= 30);- ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX <= 31);- ZSTD_reduceIndex(cctx, correction);- if (ms->nextToUpdate < correction) ms->nextToUpdate = 0;- else ms->nextToUpdate -= correction;- ms->loadedDictEnd = 0;- ms->dictMatchState = NULL;- }- ZSTD_window_enforceMaxDist(&ms->window, ip + blockSize, maxDist, &ms->loadedDictEnd, &ms->dictMatchState);- if (ms->nextToUpdate < ms->window.lowLimit) ms->nextToUpdate = ms->window.lowLimit;-- { size_t cSize = ZSTD_compressBlock_internal(cctx,- op+ZSTD_blockHeaderSize, dstCapacity-ZSTD_blockHeaderSize,- ip, blockSize);- FORWARD_IF_ERROR(cSize);-- if (cSize == 0) { /* block is not compressible */- cSize = ZSTD_noCompressBlock(op, dstCapacity, ip, blockSize, lastBlock);- FORWARD_IF_ERROR(cSize);- } else {- U32 const cBlockHeader24 = lastBlock + (((U32)bt_compressed)<<1) + (U32)(cSize << 3);- MEM_writeLE24(op, cBlockHeader24);- cSize += ZSTD_blockHeaderSize;- }-- ip += blockSize;- assert(remaining >= blockSize);- remaining -= blockSize;- op += cSize;- assert(dstCapacity >= cSize);- dstCapacity -= cSize;- DEBUGLOG(5, "ZSTD_compress_frameChunk: adding a block of size %u",- (unsigned)cSize);- } }-- if (lastFrameChunk && (op>ostart)) cctx->stage = ZSTDcs_ending;- return op-ostart;-}---static size_t ZSTD_writeFrameHeader(void* dst, size_t dstCapacity,- ZSTD_CCtx_params params, U64 pledgedSrcSize, U32 dictID)-{ BYTE* const op = (BYTE*)dst;- U32 const dictIDSizeCodeLength = (dictID>0) + (dictID>=256) + (dictID>=65536); /* 0-3 */- U32 const dictIDSizeCode = params.fParams.noDictIDFlag ? 0 : dictIDSizeCodeLength; /* 0-3 */- U32 const checksumFlag = params.fParams.checksumFlag>0;- U32 const windowSize = (U32)1 << params.cParams.windowLog;- U32 const singleSegment = params.fParams.contentSizeFlag && (windowSize >= pledgedSrcSize);- BYTE const windowLogByte = (BYTE)((params.cParams.windowLog - ZSTD_WINDOWLOG_ABSOLUTEMIN) << 3);- U32 const fcsCode = params.fParams.contentSizeFlag ?- (pledgedSrcSize>=256) + (pledgedSrcSize>=65536+256) + (pledgedSrcSize>=0xFFFFFFFFU) : 0; /* 0-3 */- BYTE const frameHeaderDescriptionByte = (BYTE)(dictIDSizeCode + (checksumFlag<<2) + (singleSegment<<5) + (fcsCode<<6) );- size_t pos=0;-- assert(!(params.fParams.contentSizeFlag && pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN));- RETURN_ERROR_IF(dstCapacity < ZSTD_FRAMEHEADERSIZE_MAX, dstSize_tooSmall);- DEBUGLOG(4, "ZSTD_writeFrameHeader : dictIDFlag : %u ; dictID : %u ; dictIDSizeCode : %u",- !params.fParams.noDictIDFlag, (unsigned)dictID, (unsigned)dictIDSizeCode);-- if (params.format == ZSTD_f_zstd1) {- MEM_writeLE32(dst, ZSTD_MAGICNUMBER);- pos = 4;- }- op[pos++] = frameHeaderDescriptionByte;- if (!singleSegment) op[pos++] = windowLogByte;- switch(dictIDSizeCode)- {- default: assert(0); /* impossible */- case 0 : break;- case 1 : op[pos] = (BYTE)(dictID); pos++; break;- case 2 : MEM_writeLE16(op+pos, (U16)dictID); pos+=2; break;- case 3 : MEM_writeLE32(op+pos, dictID); pos+=4; break;- }- switch(fcsCode)- {- default: assert(0); /* impossible */- case 0 : if (singleSegment) op[pos++] = (BYTE)(pledgedSrcSize); break;- case 1 : MEM_writeLE16(op+pos, (U16)(pledgedSrcSize-256)); pos+=2; break;- case 2 : MEM_writeLE32(op+pos, (U32)(pledgedSrcSize)); pos+=4; break;- case 3 : MEM_writeLE64(op+pos, (U64)(pledgedSrcSize)); pos+=8; break;- }- return pos;-}--/* ZSTD_writeLastEmptyBlock() :- * output an empty Block with end-of-frame mark to complete a frame- * @return : size of data written into `dst` (== ZSTD_blockHeaderSize (defined in zstd_internal.h))- * or an error code if `dstCapacity` is too small (<ZSTD_blockHeaderSize)- */-size_t ZSTD_writeLastEmptyBlock(void* dst, size_t dstCapacity)-{- RETURN_ERROR_IF(dstCapacity < ZSTD_blockHeaderSize, dstSize_tooSmall);- { U32 const cBlockHeader24 = 1 /*lastBlock*/ + (((U32)bt_raw)<<1); /* 0 size */- MEM_writeLE24(dst, cBlockHeader24);- return ZSTD_blockHeaderSize;- }-}--size_t ZSTD_referenceExternalSequences(ZSTD_CCtx* cctx, rawSeq* seq, size_t nbSeq)-{- RETURN_ERROR_IF(cctx->stage != ZSTDcs_init, stage_wrong);- RETURN_ERROR_IF(cctx->appliedParams.ldmParams.enableLdm,- parameter_unsupported);- cctx->externSeqStore.seq = seq;- cctx->externSeqStore.size = nbSeq;- cctx->externSeqStore.capacity = nbSeq;- cctx->externSeqStore.pos = 0;- return 0;-}---static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx,- void* dst, size_t dstCapacity,- const void* src, size_t srcSize,- U32 frame, U32 lastFrameChunk)-{- ZSTD_matchState_t* const ms = &cctx->blockState.matchState;- size_t fhSize = 0;-- DEBUGLOG(5, "ZSTD_compressContinue_internal, stage: %u, srcSize: %u",- cctx->stage, (unsigned)srcSize);- RETURN_ERROR_IF(cctx->stage==ZSTDcs_created, stage_wrong,- "missing init (ZSTD_compressBegin)");-- if (frame && (cctx->stage==ZSTDcs_init)) {- fhSize = ZSTD_writeFrameHeader(dst, dstCapacity, cctx->appliedParams,- cctx->pledgedSrcSizePlusOne-1, cctx->dictID);- FORWARD_IF_ERROR(fhSize);- dstCapacity -= fhSize;- dst = (char*)dst + fhSize;- cctx->stage = ZSTDcs_ongoing;- }-- if (!srcSize) return fhSize; /* do not generate an empty block if no input */-- if (!ZSTD_window_update(&ms->window, src, srcSize)) {- ms->nextToUpdate = ms->window.dictLimit;- }- if (cctx->appliedParams.ldmParams.enableLdm) {- ZSTD_window_update(&cctx->ldmState.window, src, srcSize);- }-- if (!frame) {- /* overflow check and correction for block mode */- if (ZSTD_window_needOverflowCorrection(ms->window, (const char*)src + srcSize)) {- U32 const cycleLog = ZSTD_cycleLog(cctx->appliedParams.cParams.chainLog, cctx->appliedParams.cParams.strategy);- U32 const correction = ZSTD_window_correctOverflow(&ms->window, cycleLog, 1 << cctx->appliedParams.cParams.windowLog, src);- ZSTD_STATIC_ASSERT(ZSTD_CHAINLOG_MAX <= 30);- ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX_32 <= 30);- ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX <= 31);- ZSTD_reduceIndex(cctx, correction);- if (ms->nextToUpdate < correction) ms->nextToUpdate = 0;- else ms->nextToUpdate -= correction;- ms->loadedDictEnd = 0;- ms->dictMatchState = NULL;- }- }-- DEBUGLOG(5, "ZSTD_compressContinue_internal (blockSize=%u)", (unsigned)cctx->blockSize);- { size_t const cSize = frame ?- ZSTD_compress_frameChunk (cctx, dst, dstCapacity, src, srcSize, lastFrameChunk) :- ZSTD_compressBlock_internal (cctx, dst, dstCapacity, src, srcSize);- FORWARD_IF_ERROR(cSize);- cctx->consumedSrcSize += srcSize;- cctx->producedCSize += (cSize + fhSize);- assert(!(cctx->appliedParams.fParams.contentSizeFlag && cctx->pledgedSrcSizePlusOne == 0));- if (cctx->pledgedSrcSizePlusOne != 0) { /* control src size */- ZSTD_STATIC_ASSERT(ZSTD_CONTENTSIZE_UNKNOWN == (unsigned long long)-1);- RETURN_ERROR_IF(- cctx->consumedSrcSize+1 > cctx->pledgedSrcSizePlusOne,- srcSize_wrong,- "error : pledgedSrcSize = %u, while realSrcSize >= %u",- (unsigned)cctx->pledgedSrcSizePlusOne-1,- (unsigned)cctx->consumedSrcSize);- }- return cSize + fhSize;- }-}--size_t ZSTD_compressContinue (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 */);-}---size_t ZSTD_getBlockSize(const ZSTD_CCtx* cctx)-{- ZSTD_compressionParameters const cParams = cctx->appliedParams.cParams;- assert(!ZSTD_checkCParams(cParams));- return MIN (ZSTD_BLOCKSIZE_MAX, (U32)1 << cParams.windowLog);-}--size_t ZSTD_compressBlock(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize)-{- size_t const blockSizeMax = ZSTD_getBlockSize(cctx);- RETURN_ERROR_IF(srcSize > blockSizeMax, srcSize_wrong);-- return ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 0 /* frame mode */, 0 /* last chunk */);-}--/*! ZSTD_loadDictionaryContent() :- * @return : 0, or an error code- */-static size_t ZSTD_loadDictionaryContent(ZSTD_matchState_t* ms,- ZSTD_CCtx_params const* params,- const void* src, size_t srcSize,- ZSTD_dictTableLoadMethod_e dtlm)-{- const BYTE* const ip = (const BYTE*) src;- const BYTE* const iend = ip + srcSize;-- ZSTD_window_update(&ms->window, src, srcSize);- ms->loadedDictEnd = params->forceWindow ? 0 : (U32)(iend - ms->window.base);-- /* Assert that we the ms params match the params we're being given */- ZSTD_assertEqualCParams(params->cParams, ms->cParams);-- if (srcSize <= HASH_READ_SIZE) return 0;-- switch(params->cParams.strategy)- {- case ZSTD_fast:- ZSTD_fillHashTable(ms, iend, dtlm);- break;- case ZSTD_dfast:- ZSTD_fillDoubleHashTable(ms, iend, dtlm);- break;-- case ZSTD_greedy:- case ZSTD_lazy:- case ZSTD_lazy2:- if (srcSize >= HASH_READ_SIZE)- ZSTD_insertAndFindFirstIndex(ms, iend-HASH_READ_SIZE);- break;-- case ZSTD_btlazy2: /* we want the dictionary table fully sorted */- case ZSTD_btopt:- case ZSTD_btultra:- case ZSTD_btultra2:- if (srcSize >= HASH_READ_SIZE)- ZSTD_updateTree(ms, iend-HASH_READ_SIZE, iend);- break;-- default:- assert(0); /* not possible : not a valid strategy id */- }-- ms->nextToUpdate = (U32)(iend - ms->window.base);- return 0;-}---/* Dictionaries that assign zero probability to symbols that show up causes problems- when FSE encoding. Refuse dictionaries that assign zero probability to symbols- that we may encounter during compression.- NOTE: This behavior is not standard and could be improved in the future. */-static size_t ZSTD_checkDictNCount(short* normalizedCounter, unsigned dictMaxSymbolValue, unsigned maxSymbolValue) {- U32 s;- RETURN_ERROR_IF(dictMaxSymbolValue < maxSymbolValue, dictionary_corrupted);- for (s = 0; s <= maxSymbolValue; ++s) {- RETURN_ERROR_IF(normalizedCounter[s] == 0, dictionary_corrupted);- }- return 0;-}---/* Dictionary format :- * See :- * https://github.com/facebook/zstd/blob/master/doc/zstd_compression_format.md#dictionary-format- */-/*! ZSTD_loadZstdDictionary() :- * @return : dictID, or an error code- * assumptions : magic number supposed already checked- * dictSize supposed > 8- */-static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs,- ZSTD_matchState_t* ms,- ZSTD_CCtx_params const* params,- const void* dict, size_t dictSize,- ZSTD_dictTableLoadMethod_e dtlm,- void* workspace)-{- const BYTE* dictPtr = (const BYTE*)dict;- const BYTE* const dictEnd = dictPtr + dictSize;- short offcodeNCount[MaxOff+1];- unsigned offcodeMaxValue = MaxOff;- size_t dictID;-- ZSTD_STATIC_ASSERT(HUF_WORKSPACE_SIZE >= (1<<MAX(MLFSELog,LLFSELog)));- assert(dictSize > 8);- assert(MEM_readLE32(dictPtr) == ZSTD_MAGIC_DICTIONARY);-- dictPtr += 4; /* skip magic number */- dictID = params->fParams.noDictIDFlag ? 0 : MEM_readLE32(dictPtr);- dictPtr += 4;-- { unsigned maxSymbolValue = 255;- size_t const hufHeaderSize = HUF_readCTable((HUF_CElt*)bs->entropy.huf.CTable, &maxSymbolValue, dictPtr, dictEnd-dictPtr);- RETURN_ERROR_IF(HUF_isError(hufHeaderSize), dictionary_corrupted);- RETURN_ERROR_IF(maxSymbolValue < 255, dictionary_corrupted);- dictPtr += hufHeaderSize;- }-- { unsigned offcodeLog;- size_t const offcodeHeaderSize = FSE_readNCount(offcodeNCount, &offcodeMaxValue, &offcodeLog, dictPtr, dictEnd-dictPtr);- RETURN_ERROR_IF(FSE_isError(offcodeHeaderSize), dictionary_corrupted);- RETURN_ERROR_IF(offcodeLog > OffFSELog, dictionary_corrupted);- /* Defer checking offcodeMaxValue because we need to know the size of the dictionary content */- /* fill all offset symbols to avoid garbage at end of table */- RETURN_ERROR_IF(FSE_isError(FSE_buildCTable_wksp(- bs->entropy.fse.offcodeCTable,- offcodeNCount, MaxOff, offcodeLog,- workspace, HUF_WORKSPACE_SIZE)),- dictionary_corrupted);- dictPtr += offcodeHeaderSize;- }-- { short matchlengthNCount[MaxML+1];- unsigned matchlengthMaxValue = MaxML, matchlengthLog;- size_t const matchlengthHeaderSize = FSE_readNCount(matchlengthNCount, &matchlengthMaxValue, &matchlengthLog, dictPtr, dictEnd-dictPtr);- RETURN_ERROR_IF(FSE_isError(matchlengthHeaderSize), dictionary_corrupted);- RETURN_ERROR_IF(matchlengthLog > MLFSELog, dictionary_corrupted);- /* Every match length code must have non-zero probability */- FORWARD_IF_ERROR( ZSTD_checkDictNCount(matchlengthNCount, matchlengthMaxValue, MaxML));- RETURN_ERROR_IF(FSE_isError(FSE_buildCTable_wksp(- bs->entropy.fse.matchlengthCTable,- matchlengthNCount, matchlengthMaxValue, matchlengthLog,- workspace, HUF_WORKSPACE_SIZE)),- dictionary_corrupted);- dictPtr += matchlengthHeaderSize;- }-- { short litlengthNCount[MaxLL+1];- unsigned litlengthMaxValue = MaxLL, litlengthLog;- size_t const litlengthHeaderSize = FSE_readNCount(litlengthNCount, &litlengthMaxValue, &litlengthLog, dictPtr, dictEnd-dictPtr);- RETURN_ERROR_IF(FSE_isError(litlengthHeaderSize), dictionary_corrupted);- RETURN_ERROR_IF(litlengthLog > LLFSELog, dictionary_corrupted);- /* Every literal length code must have non-zero probability */- FORWARD_IF_ERROR( ZSTD_checkDictNCount(litlengthNCount, litlengthMaxValue, MaxLL));- RETURN_ERROR_IF(FSE_isError(FSE_buildCTable_wksp(- bs->entropy.fse.litlengthCTable,- litlengthNCount, litlengthMaxValue, litlengthLog,- workspace, HUF_WORKSPACE_SIZE)),- dictionary_corrupted);- dictPtr += litlengthHeaderSize;- }-- RETURN_ERROR_IF(dictPtr+12 > dictEnd, dictionary_corrupted);- bs->rep[0] = MEM_readLE32(dictPtr+0);- bs->rep[1] = MEM_readLE32(dictPtr+4);- bs->rep[2] = MEM_readLE32(dictPtr+8);- dictPtr += 12;-- { size_t const dictContentSize = (size_t)(dictEnd - dictPtr);- U32 offcodeMax = MaxOff;- if (dictContentSize <= ((U32)-1) - 128 KB) {- U32 const maxOffset = (U32)dictContentSize + 128 KB; /* The maximum offset that must be supported */- offcodeMax = ZSTD_highbit32(maxOffset); /* Calculate minimum offset code required to represent maxOffset */- }- /* All offset values <= dictContentSize + 128 KB must be representable */- FORWARD_IF_ERROR(ZSTD_checkDictNCount(offcodeNCount, offcodeMaxValue, MIN(offcodeMax, MaxOff)));- /* All repCodes must be <= dictContentSize and != 0*/- { U32 u;- for (u=0; u<3; u++) {- RETURN_ERROR_IF(bs->rep[u] == 0, dictionary_corrupted);- RETURN_ERROR_IF(bs->rep[u] > dictContentSize, dictionary_corrupted);- } }-- bs->entropy.huf.repeatMode = HUF_repeat_valid;- bs->entropy.fse.offcode_repeatMode = FSE_repeat_valid;- bs->entropy.fse.matchlength_repeatMode = FSE_repeat_valid;- bs->entropy.fse.litlength_repeatMode = FSE_repeat_valid;- FORWARD_IF_ERROR(ZSTD_loadDictionaryContent(ms, params, dictPtr, dictContentSize, dtlm));- return dictID;- }-}--/** ZSTD_compress_insertDictionary() :-* @return : dictID, or an error code */-static size_t-ZSTD_compress_insertDictionary(ZSTD_compressedBlockState_t* bs,- ZSTD_matchState_t* ms,- const ZSTD_CCtx_params* params,- const void* dict, size_t dictSize,- ZSTD_dictContentType_e dictContentType,- ZSTD_dictTableLoadMethod_e dtlm,- void* workspace)-{- DEBUGLOG(4, "ZSTD_compress_insertDictionary (dictSize=%u)", (U32)dictSize);- if ((dict==NULL) || (dictSize<=8)) return 0;-- ZSTD_reset_compressedBlockState(bs);-- /* dict restricted modes */- if (dictContentType == ZSTD_dct_rawContent)- return ZSTD_loadDictionaryContent(ms, params, dict, dictSize, dtlm);-- if (MEM_readLE32(dict) != ZSTD_MAGIC_DICTIONARY) {- if (dictContentType == ZSTD_dct_auto) {- DEBUGLOG(4, "raw content dictionary detected");- return ZSTD_loadDictionaryContent(ms, params, dict, dictSize, dtlm);- }- RETURN_ERROR_IF(dictContentType == ZSTD_dct_fullDict, dictionary_wrong);- assert(0); /* impossible */- }-- /* dict as full zstd dictionary */- return ZSTD_loadZstdDictionary(bs, ms, params, dict, dictSize, dtlm, workspace);-}--/*! ZSTD_compressBegin_internal() :- * @return : 0, or an error code */-static size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx,- const void* dict, size_t dictSize,- ZSTD_dictContentType_e dictContentType,- ZSTD_dictTableLoadMethod_e dtlm,- const ZSTD_CDict* cdict,- ZSTD_CCtx_params params, U64 pledgedSrcSize,- ZSTD_buffered_policy_e zbuff)-{- DEBUGLOG(4, "ZSTD_compressBegin_internal: wlog=%u", params.cParams.windowLog);- /* params are supposed to be fully validated at this point */- assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams)));- assert(!((dict) && (cdict))); /* either dict or cdict, not both */-- if (cdict && cdict->dictContentSize>0) {- return ZSTD_resetCCtx_usingCDict(cctx, cdict, params, pledgedSrcSize, zbuff);- }-- FORWARD_IF_ERROR( ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize,- ZSTDcrp_continue, zbuff) );- {- size_t const dictID = ZSTD_compress_insertDictionary(- cctx->blockState.prevCBlock, &cctx->blockState.matchState,- ¶ms, dict, dictSize, dictContentType, dtlm, cctx->entropyWorkspace);- FORWARD_IF_ERROR(dictID);- assert(dictID <= (size_t)(U32)-1);- cctx->dictID = (U32)dictID;- }- return 0;-}--size_t ZSTD_compressBegin_advanced_internal(ZSTD_CCtx* cctx,- const void* dict, size_t dictSize,- ZSTD_dictContentType_e dictContentType,- ZSTD_dictTableLoadMethod_e dtlm,- const ZSTD_CDict* cdict,- ZSTD_CCtx_params params,- unsigned long long pledgedSrcSize)-{- DEBUGLOG(4, "ZSTD_compressBegin_advanced_internal: wlog=%u", params.cParams.windowLog);- /* compression parameters verification and optimization */- FORWARD_IF_ERROR( ZSTD_checkCParams(params.cParams) );- return ZSTD_compressBegin_internal(cctx,- dict, dictSize, dictContentType, dtlm,- cdict,- params, pledgedSrcSize,- ZSTDb_not_buffered);-}--/*! ZSTD_compressBegin_advanced() :-* @return : 0, or an error code */-size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx,- const void* dict, size_t dictSize,- ZSTD_parameters params, unsigned long long pledgedSrcSize)-{- ZSTD_CCtx_params const cctxParams =- ZSTD_assignParamsToCCtxParams(cctx->requestedParams, params);- return ZSTD_compressBegin_advanced_internal(cctx,- dict, dictSize, ZSTD_dct_auto, ZSTD_dtlm_fast,- NULL /*cdict*/,- cctxParams, pledgedSrcSize);-}--size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel)-{- ZSTD_parameters const params = ZSTD_getParams(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize);- ZSTD_CCtx_params const cctxParams =- ZSTD_assignParamsToCCtxParams(cctx->requestedParams, params);- DEBUGLOG(4, "ZSTD_compressBegin_usingDict (dictSize=%u)", (unsigned)dictSize);- return ZSTD_compressBegin_internal(cctx, dict, dictSize, ZSTD_dct_auto, ZSTD_dtlm_fast, NULL,- cctxParams, ZSTD_CONTENTSIZE_UNKNOWN, ZSTDb_not_buffered);-}--size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel)-{- return ZSTD_compressBegin_usingDict(cctx, NULL, 0, compressionLevel);-}---/*! ZSTD_writeEpilogue() :-* Ends a frame.-* @return : nb of bytes written into dst (or an error code) */-static size_t ZSTD_writeEpilogue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity)-{- BYTE* const ostart = (BYTE*)dst;- BYTE* op = ostart;- size_t fhSize = 0;-- DEBUGLOG(4, "ZSTD_writeEpilogue");- RETURN_ERROR_IF(cctx->stage == ZSTDcs_created, stage_wrong, "init missing");-- /* special case : empty frame */- if (cctx->stage == ZSTDcs_init) {- fhSize = ZSTD_writeFrameHeader(dst, dstCapacity, cctx->appliedParams, 0, 0);- FORWARD_IF_ERROR(fhSize);- dstCapacity -= fhSize;- op += fhSize;- cctx->stage = ZSTDcs_ongoing;- }-- if (cctx->stage != ZSTDcs_ending) {- /* write one last empty block, make it the "last" block */- U32 const cBlockHeader24 = 1 /* last block */ + (((U32)bt_raw)<<1) + 0;- RETURN_ERROR_IF(dstCapacity<4, dstSize_tooSmall);- MEM_writeLE32(op, cBlockHeader24);- op += ZSTD_blockHeaderSize;- dstCapacity -= ZSTD_blockHeaderSize;- }-- if (cctx->appliedParams.fParams.checksumFlag) {- U32 const checksum = (U32) XXH64_digest(&cctx->xxhState);- RETURN_ERROR_IF(dstCapacity<4, dstSize_tooSmall);- DEBUGLOG(4, "ZSTD_writeEpilogue: write checksum : %08X", (unsigned)checksum);- MEM_writeLE32(op, checksum);- op += 4;- }-- cctx->stage = ZSTDcs_created; /* return to "created but no init" status */- return op-ostart;-}--size_t ZSTD_compressEnd (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,- dst, dstCapacity, src, srcSize,- 1 /* frame mode */, 1 /* last chunk */);- FORWARD_IF_ERROR(cSize);- endResult = ZSTD_writeEpilogue(cctx, (char*)dst + cSize, dstCapacity-cSize);- FORWARD_IF_ERROR(endResult);- assert(!(cctx->appliedParams.fParams.contentSizeFlag && cctx->pledgedSrcSizePlusOne == 0));- if (cctx->pledgedSrcSizePlusOne != 0) { /* control src size */- ZSTD_STATIC_ASSERT(ZSTD_CONTENTSIZE_UNKNOWN == (unsigned long long)-1);- DEBUGLOG(4, "end of frame : controlling src size");- RETURN_ERROR_IF(- cctx->pledgedSrcSizePlusOne != cctx->consumedSrcSize+1,- srcSize_wrong,- "error : pledgedSrcSize = %u, while realSrcSize = %u",- (unsigned)cctx->pledgedSrcSizePlusOne-1,- (unsigned)cctx->consumedSrcSize);- }- return cSize + endResult;-}---static size_t ZSTD_compress_internal (ZSTD_CCtx* cctx,- void* dst, size_t dstCapacity,- const void* src, size_t srcSize,- const void* dict,size_t dictSize,- ZSTD_parameters params)-{- ZSTD_CCtx_params const cctxParams =- ZSTD_assignParamsToCCtxParams(cctx->requestedParams, params);- DEBUGLOG(4, "ZSTD_compress_internal");- return ZSTD_compress_advanced_internal(cctx,- dst, dstCapacity,- src, srcSize,- dict, dictSize,- cctxParams);-}--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)-{- DEBUGLOG(4, "ZSTD_compress_advanced");- FORWARD_IF_ERROR(ZSTD_checkCParams(params.cParams));- return ZSTD_compress_internal(cctx,- dst, dstCapacity,- src, srcSize,- dict, dictSize,- params);-}--/* Internal */-size_t ZSTD_compress_advanced_internal(- ZSTD_CCtx* cctx,- void* dst, size_t dstCapacity,- const void* src, size_t srcSize,- const void* dict,size_t dictSize,- ZSTD_CCtx_params params)-{- DEBUGLOG(4, "ZSTD_compress_advanced_internal (srcSize:%u)", (unsigned)srcSize);- 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);-}--size_t ZSTD_compress_usingDict(ZSTD_CCtx* cctx,- void* dst, size_t dstCapacity,- const void* src, size_t srcSize,- const void* dict, size_t dictSize,- int compressionLevel)-{- ZSTD_parameters const params = ZSTD_getParams(compressionLevel, srcSize + (!srcSize), dict ? dictSize : 0);- ZSTD_CCtx_params cctxParams = ZSTD_assignParamsToCCtxParams(cctx->requestedParams, params);- assert(params.fParams.contentSizeFlag == 1);- return ZSTD_compress_advanced_internal(cctx, dst, dstCapacity, src, srcSize, dict, dictSize, cctxParams);-}--size_t ZSTD_compressCCtx(ZSTD_CCtx* cctx,- void* dst, size_t dstCapacity,- const void* src, size_t srcSize,- int compressionLevel)-{- DEBUGLOG(4, "ZSTD_compressCCtx (srcSize=%u)", (unsigned)srcSize);- assert(cctx != NULL);- return ZSTD_compress_usingDict(cctx, dst, dstCapacity, src, srcSize, NULL, 0, compressionLevel);-}--size_t ZSTD_compress(void* dst, size_t dstCapacity,- const void* src, size_t srcSize,- int compressionLevel)-{- size_t result;- ZSTD_CCtx ctxBody;- ZSTD_initCCtx(&ctxBody, ZSTD_defaultCMem);- result = ZSTD_compressCCtx(&ctxBody, dst, dstCapacity, src, srcSize, compressionLevel);- ZSTD_freeCCtxContent(&ctxBody); /* can't free ctxBody itself, as it's on stack; free only heap content */- return result;-}---/* ===== Dictionary API ===== */--/*! ZSTD_estimateCDictSize_advanced() :- * Estimate amount of memory that will be needed to create a dictionary with following arguments */-size_t ZSTD_estimateCDictSize_advanced(- size_t dictSize, ZSTD_compressionParameters cParams,- ZSTD_dictLoadMethod_e dictLoadMethod)-{- DEBUGLOG(5, "sizeof(ZSTD_CDict) : %u", (unsigned)sizeof(ZSTD_CDict));- return sizeof(ZSTD_CDict) + HUF_WORKSPACE_SIZE + ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 0)- + (dictLoadMethod == ZSTD_dlm_byRef ? 0 : dictSize);-}--size_t ZSTD_estimateCDictSize(size_t dictSize, int compressionLevel)-{- ZSTD_compressionParameters const cParams = ZSTD_getCParams(compressionLevel, 0, dictSize);- return ZSTD_estimateCDictSize_advanced(dictSize, cParams, ZSTD_dlm_byCopy);-}--size_t ZSTD_sizeof_CDict(const ZSTD_CDict* cdict)-{- if (cdict==NULL) return 0; /* support sizeof on NULL */- DEBUGLOG(5, "sizeof(*cdict) : %u", (unsigned)sizeof(*cdict));- return cdict->workspaceSize + (cdict->dictBuffer ? cdict->dictContentSize : 0) + sizeof(*cdict);-}--static size_t ZSTD_initCDict_internal(- ZSTD_CDict* cdict,- const void* dictBuffer, size_t dictSize,- ZSTD_dictLoadMethod_e dictLoadMethod,- ZSTD_dictContentType_e dictContentType,- ZSTD_compressionParameters cParams)-{- DEBUGLOG(3, "ZSTD_initCDict_internal (dictContentType:%u)", (unsigned)dictContentType);- assert(!ZSTD_checkCParams(cParams));- cdict->matchState.cParams = cParams;- if ((dictLoadMethod == ZSTD_dlm_byRef) || (!dictBuffer) || (!dictSize)) {- cdict->dictBuffer = NULL;- cdict->dictContent = dictBuffer;- } else {- void* const internalBuffer = ZSTD_malloc(dictSize, cdict->customMem);- cdict->dictBuffer = internalBuffer;- cdict->dictContent = internalBuffer;- RETURN_ERROR_IF(!internalBuffer, memory_allocation);- memcpy(internalBuffer, dictBuffer, dictSize);- }- cdict->dictContentSize = dictSize;-- /* Reset the state to no dictionary */- ZSTD_reset_compressedBlockState(&cdict->cBlockState);- { void* const end = ZSTD_reset_matchState(- &cdict->matchState,- (U32*)cdict->workspace + HUF_WORKSPACE_SIZE_U32,- &cParams, ZSTDcrp_continue, /* forCCtx */ 0);- assert(end == (char*)cdict->workspace + cdict->workspaceSize);- (void)end;- }- /* (Maybe) load the dictionary- * Skips loading the dictionary if it is <= 8 bytes.- */- { ZSTD_CCtx_params params;- memset(¶ms, 0, sizeof(params));- params.compressionLevel = ZSTD_CLEVEL_DEFAULT;- params.fParams.contentSizeFlag = 1;- params.cParams = cParams;- { size_t const dictID = ZSTD_compress_insertDictionary(- &cdict->cBlockState, &cdict->matchState, ¶ms,- cdict->dictContent, cdict->dictContentSize,- dictContentType, ZSTD_dtlm_full, cdict->workspace);- FORWARD_IF_ERROR(dictID);- assert(dictID <= (size_t)(U32)-1);- cdict->dictID = (U32)dictID;- }- }-- return 0;-}--ZSTD_CDict* ZSTD_createCDict_advanced(const void* dictBuffer, size_t dictSize,- ZSTD_dictLoadMethod_e dictLoadMethod,- ZSTD_dictContentType_e dictContentType,- ZSTD_compressionParameters cParams, ZSTD_customMem customMem)-{- DEBUGLOG(3, "ZSTD_createCDict_advanced, mode %u", (unsigned)dictContentType);- if (!customMem.customAlloc ^ !customMem.customFree) return NULL;-- { ZSTD_CDict* const cdict = (ZSTD_CDict*)ZSTD_malloc(sizeof(ZSTD_CDict), customMem);- size_t const workspaceSize = HUF_WORKSPACE_SIZE + ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 0);- void* const workspace = ZSTD_malloc(workspaceSize, customMem);-- if (!cdict || !workspace) {- ZSTD_free(cdict, customMem);- ZSTD_free(workspace, customMem);- return NULL;- }- cdict->customMem = customMem;- cdict->workspace = workspace;- cdict->workspaceSize = workspaceSize;- if (ZSTD_isError( ZSTD_initCDict_internal(cdict,- dictBuffer, dictSize,- dictLoadMethod, dictContentType,- cParams) )) {- ZSTD_freeCDict(cdict);- return NULL;- }-- return cdict;- }-}--ZSTD_CDict* ZSTD_createCDict(const void* dict, size_t dictSize, int compressionLevel)-{- ZSTD_compressionParameters cParams = ZSTD_getCParams(compressionLevel, 0, dictSize);- return ZSTD_createCDict_advanced(dict, dictSize,- ZSTD_dlm_byCopy, ZSTD_dct_auto,- cParams, ZSTD_defaultCMem);-}--ZSTD_CDict* ZSTD_createCDict_byReference(const void* dict, size_t dictSize, int compressionLevel)-{- ZSTD_compressionParameters cParams = ZSTD_getCParams(compressionLevel, 0, dictSize);- return ZSTD_createCDict_advanced(dict, dictSize,- ZSTD_dlm_byRef, ZSTD_dct_auto,- cParams, ZSTD_defaultCMem);-}--size_t ZSTD_freeCDict(ZSTD_CDict* cdict)-{- if (cdict==NULL) return 0; /* support free on NULL */- { ZSTD_customMem const cMem = cdict->customMem;- ZSTD_free(cdict->workspace, cMem);- ZSTD_free(cdict->dictBuffer, cMem);- ZSTD_free(cdict, cMem);- return 0;- }-}--/*! ZSTD_initStaticCDict_advanced() :- * Generate a digested dictionary in provided memory area.- * workspace: The memory area to emplace the dictionary into.- * Provided pointer must 8-bytes aligned.- * It must outlive dictionary usage.- * workspaceSize: Use ZSTD_estimateCDictSize()- * to determine how large workspace must be.- * cParams : use ZSTD_getCParams() to transform a compression level- * into its relevants cParams.- * @return : pointer to ZSTD_CDict*, or NULL if error (size too small)- * Note : there is no corresponding "free" function.- * Since workspace was allocated externally, it must be freed externally.- */-const ZSTD_CDict* ZSTD_initStaticCDict(- void* workspace, size_t workspaceSize,- const void* dict, size_t dictSize,- ZSTD_dictLoadMethod_e dictLoadMethod,- ZSTD_dictContentType_e dictContentType,- ZSTD_compressionParameters cParams)-{- size_t const matchStateSize = ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 0);- size_t const neededSize = sizeof(ZSTD_CDict) + (dictLoadMethod == ZSTD_dlm_byRef ? 0 : dictSize)- + HUF_WORKSPACE_SIZE + matchStateSize;- ZSTD_CDict* const cdict = (ZSTD_CDict*) workspace;- void* ptr;- if ((size_t)workspace & 7) return NULL; /* 8-aligned */- DEBUGLOG(4, "(workspaceSize < neededSize) : (%u < %u) => %u",- (unsigned)workspaceSize, (unsigned)neededSize, (unsigned)(workspaceSize < neededSize));- if (workspaceSize < neededSize) return NULL;-- if (dictLoadMethod == ZSTD_dlm_byCopy) {- memcpy(cdict+1, dict, dictSize);- dict = cdict+1;- ptr = (char*)workspace + sizeof(ZSTD_CDict) + dictSize;- } else {- ptr = cdict+1;- }- cdict->workspace = ptr;- cdict->workspaceSize = HUF_WORKSPACE_SIZE + matchStateSize;-- if (ZSTD_isError( ZSTD_initCDict_internal(cdict,- dict, dictSize,- ZSTD_dlm_byRef, dictContentType,- cParams) ))- return NULL;-- return cdict;-}--ZSTD_compressionParameters ZSTD_getCParamsFromCDict(const ZSTD_CDict* cdict)-{- assert(cdict != NULL);- return cdict->matchState.cParams;-}--/* ZSTD_compressBegin_usingCDict_advanced() :- * cdict must be != NULL */-size_t ZSTD_compressBegin_usingCDict_advanced(- ZSTD_CCtx* const cctx, const ZSTD_CDict* const cdict,- ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize)-{- DEBUGLOG(4, "ZSTD_compressBegin_usingCDict_advanced");- RETURN_ERROR_IF(cdict==NULL, dictionary_wrong);- { ZSTD_CCtx_params params = cctx->requestedParams;- params.cParams = ZSTD_getCParamsFromCDict(cdict);- /* Increase window log to fit the entire dictionary and source if the- * source size is known. Limit the increase to 19, which is the- * window log for compression level 1 with the largest source size.- */- if (pledgedSrcSize != ZSTD_CONTENTSIZE_UNKNOWN) {- U32 const limitedSrcSize = (U32)MIN(pledgedSrcSize, 1U << 19);- U32 const limitedSrcLog = limitedSrcSize > 1 ? ZSTD_highbit32(limitedSrcSize - 1) + 1 : 1;- params.cParams.windowLog = MAX(params.cParams.windowLog, limitedSrcLog);- }- params.fParams = fParams;- return ZSTD_compressBegin_internal(cctx,- NULL, 0, ZSTD_dct_auto, ZSTD_dtlm_fast,- cdict,- params, pledgedSrcSize,- ZSTDb_not_buffered);- }-}--/* ZSTD_compressBegin_usingCDict() :- * pledgedSrcSize=0 means "unknown"- * if pledgedSrcSize>0, it will enable contentSizeFlag */-size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict)-{- ZSTD_frameParameters const fParams = { 0 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ };- DEBUGLOG(4, "ZSTD_compressBegin_usingCDict : dictIDFlag == %u", !fParams.noDictIDFlag);- return ZSTD_compressBegin_usingCDict_advanced(cctx, cdict, fParams, ZSTD_CONTENTSIZE_UNKNOWN);-}--size_t ZSTD_compress_usingCDict_advanced(ZSTD_CCtx* cctx,- void* dst, size_t dstCapacity,- const void* src, size_t srcSize,- const ZSTD_CDict* cdict, ZSTD_frameParameters fParams)-{- FORWARD_IF_ERROR(ZSTD_compressBegin_usingCDict_advanced(cctx, cdict, fParams, srcSize)); /* will check if cdict != NULL */- return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize);-}--/*! ZSTD_compress_usingCDict() :- * Compression using a digested Dictionary.- * Faster startup than ZSTD_compress_usingDict(), recommended when same dictionary is used multiple times.- * Note that compression parameters are decided at CDict creation time- * while frame parameters are hardcoded */-size_t ZSTD_compress_usingCDict(ZSTD_CCtx* cctx,- void* dst, size_t dstCapacity,- const void* src, size_t srcSize,- const ZSTD_CDict* cdict)-{- ZSTD_frameParameters const fParams = { 1 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ };- return ZSTD_compress_usingCDict_advanced(cctx, dst, dstCapacity, src, srcSize, cdict, fParams);-}----/* ******************************************************************-* Streaming-********************************************************************/--ZSTD_CStream* ZSTD_createCStream(void)-{- DEBUGLOG(3, "ZSTD_createCStream");- return ZSTD_createCStream_advanced(ZSTD_defaultCMem);-}--ZSTD_CStream* ZSTD_initStaticCStream(void *workspace, size_t workspaceSize)-{- return ZSTD_initStaticCCtx(workspace, workspaceSize);-}--ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem)-{ /* CStream and CCtx are now same object */- return ZSTD_createCCtx_advanced(customMem);-}--size_t ZSTD_freeCStream(ZSTD_CStream* zcs)-{- return ZSTD_freeCCtx(zcs); /* same object */-}----/*====== Initialization ======*/--size_t ZSTD_CStreamInSize(void) { return ZSTD_BLOCKSIZE_MAX; }--size_t ZSTD_CStreamOutSize(void)-{- return ZSTD_compressBound(ZSTD_BLOCKSIZE_MAX) + ZSTD_blockHeaderSize + 4 /* 32-bits hash */ ;-}--static size_t ZSTD_resetCStream_internal(ZSTD_CStream* cctx,- const void* const dict, size_t const dictSize, ZSTD_dictContentType_e const dictContentType,- const ZSTD_CDict* const cdict,- ZSTD_CCtx_params params, unsigned long long const pledgedSrcSize)-{- DEBUGLOG(4, "ZSTD_resetCStream_internal");- /* Finalize the compression parameters */- params.cParams = ZSTD_getCParamsFromCCtxParams(¶ms, pledgedSrcSize, dictSize);- /* params are supposed to be fully validated at this point */- assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams)));- assert(!((dict) && (cdict))); /* either dict or cdict, not both */-- FORWARD_IF_ERROR( ZSTD_compressBegin_internal(cctx,- dict, dictSize, dictContentType, ZSTD_dtlm_fast,- cdict,- params, pledgedSrcSize,- ZSTDb_buffered) );-- cctx->inToCompress = 0;- cctx->inBuffPos = 0;- cctx->inBuffTarget = cctx->blockSize- + (cctx->blockSize == pledgedSrcSize); /* for small input: avoid automatic flush on reaching end of block, since it would require to add a 3-bytes null block to end frame */- cctx->outBuffContentSize = cctx->outBuffFlushedSize = 0;- cctx->streamStage = zcss_load;- cctx->frameEnded = 0;- return 0; /* ready to go */-}--/* ZSTD_resetCStream():- * pledgedSrcSize == 0 means "unknown" */-size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pss)-{- /* temporary : 0 interpreted as "unknown" during transition period.- * Users willing to specify "unknown" **must** use ZSTD_CONTENTSIZE_UNKNOWN.- * 0 will be interpreted as "empty" in the future.- */- U64 const pledgedSrcSize = (pss==0) ? ZSTD_CONTENTSIZE_UNKNOWN : pss;- DEBUGLOG(4, "ZSTD_resetCStream: pledgedSrcSize = %u", (unsigned)pledgedSrcSize);- FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) );- FORWARD_IF_ERROR( ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize) );- return 0;-}--/*! ZSTD_initCStream_internal() :- * Note : for lib/compress only. Used by zstdmt_compress.c.- * Assumption 1 : params are valid- * Assumption 2 : either dict, or cdict, is defined, not both */-size_t ZSTD_initCStream_internal(ZSTD_CStream* zcs,- const void* dict, size_t dictSize, const ZSTD_CDict* cdict,- ZSTD_CCtx_params params, unsigned long long pledgedSrcSize)-{- DEBUGLOG(4, "ZSTD_initCStream_internal");- FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) );- FORWARD_IF_ERROR( ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize) );- assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams)));- zcs->requestedParams = params;- assert(!((dict) && (cdict))); /* either dict or cdict, not both */- if (dict) {- FORWARD_IF_ERROR( ZSTD_CCtx_loadDictionary(zcs, dict, dictSize) );- } else {- /* Dictionary is cleared if !cdict */- FORWARD_IF_ERROR( ZSTD_CCtx_refCDict(zcs, cdict) );- }- return 0;-}--/* ZSTD_initCStream_usingCDict_advanced() :- * same as ZSTD_initCStream_usingCDict(), with control over frame parameters */-size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs,- const ZSTD_CDict* cdict,- ZSTD_frameParameters fParams,- unsigned long long pledgedSrcSize)-{- DEBUGLOG(4, "ZSTD_initCStream_usingCDict_advanced");- FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) );- FORWARD_IF_ERROR( ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize) );- zcs->requestedParams.fParams = fParams;- FORWARD_IF_ERROR( ZSTD_CCtx_refCDict(zcs, cdict) );- return 0;-}--/* note : cdict must outlive compression session */-size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict)-{- DEBUGLOG(4, "ZSTD_initCStream_usingCDict");- FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) );- FORWARD_IF_ERROR( ZSTD_CCtx_refCDict(zcs, cdict) );- return 0;-}---/* ZSTD_initCStream_advanced() :- * pledgedSrcSize must be exact.- * if srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN.- * dict is loaded with default parameters ZSTD_dm_auto and ZSTD_dlm_byCopy. */-size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs,- const void* dict, size_t dictSize,- ZSTD_parameters params, unsigned long long pss)-{- /* for compatibility with older programs relying on this behavior.- * Users should now specify ZSTD_CONTENTSIZE_UNKNOWN.- * This line will be removed in the future.- */- U64 const pledgedSrcSize = (pss==0 && params.fParams.contentSizeFlag==0) ? ZSTD_CONTENTSIZE_UNKNOWN : pss;- DEBUGLOG(4, "ZSTD_initCStream_advanced");- FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) );- FORWARD_IF_ERROR( ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize) );- FORWARD_IF_ERROR( ZSTD_checkCParams(params.cParams) );- zcs->requestedParams = ZSTD_assignParamsToCCtxParams(zcs->requestedParams, params);- FORWARD_IF_ERROR( ZSTD_CCtx_loadDictionary(zcs, dict, dictSize) );- return 0;-}--size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel)-{- DEBUGLOG(4, "ZSTD_initCStream_usingDict");- FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) );- FORWARD_IF_ERROR( ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel) );- FORWARD_IF_ERROR( ZSTD_CCtx_loadDictionary(zcs, dict, dictSize) );- return 0;-}--size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pss)-{- /* temporary : 0 interpreted as "unknown" during transition period.- * Users willing to specify "unknown" **must** use ZSTD_CONTENTSIZE_UNKNOWN.- * 0 will be interpreted as "empty" in the future.- */- U64 const pledgedSrcSize = (pss==0) ? ZSTD_CONTENTSIZE_UNKNOWN : pss;- DEBUGLOG(4, "ZSTD_initCStream_srcSize");- FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) );- FORWARD_IF_ERROR( ZSTD_CCtx_refCDict(zcs, NULL) );- FORWARD_IF_ERROR( ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel) );- FORWARD_IF_ERROR( ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize) );- return 0;-}--size_t ZSTD_initCStream(ZSTD_CStream* zcs, int compressionLevel)-{- DEBUGLOG(4, "ZSTD_initCStream");- FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) );- FORWARD_IF_ERROR( ZSTD_CCtx_refCDict(zcs, NULL) );- FORWARD_IF_ERROR( ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel) );- return 0;-}--/*====== Compression ======*/--static size_t ZSTD_nextInputSizeHint(const ZSTD_CCtx* cctx)-{- size_t hintInSize = cctx->inBuffTarget - cctx->inBuffPos;- if (hintInSize==0) hintInSize = cctx->blockSize;- return hintInSize;-}--static size_t ZSTD_limitCopy(void* dst, size_t dstCapacity,- const void* src, size_t srcSize)-{- size_t const length = MIN(dstCapacity, srcSize);- if (length) memcpy(dst, src, length);- return length;-}--/** 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 */-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 = istart + input->size;- const char* ip = istart + input->pos;- char* const ostart = (char*)output->dst;- char* const oend = ostart + output->size;- char* op = ostart + output->pos;- U32 someMoreWork = 1;-- /* check expectations */- DEBUGLOG(5, "ZSTD_compressStream_generic, flush=%u", (unsigned)flushMode);- assert(zcs->inBuff != NULL);- assert(zcs->inBuffSize > 0);- assert(zcs->outBuff != NULL);- assert(zcs->outBuffSize > 0);- assert(output->pos <= output->size);- assert(input->pos <= input->size);-- while (someMoreWork) {- switch(zcs->streamStage)- {- case zcss_init:- RETURN_ERROR(init_missing, "call ZSTD_initCStream() first!");-- case zcss_load:- if ( (flushMode == ZSTD_e_end)- && ((size_t)(oend-op) >= ZSTD_compressBound(iend-ip)) /* enough dstCapacity */- && (zcs->inBuffPos == 0) ) {- /* shortcut to compression pass directly into output buffer */- size_t const cSize = ZSTD_compressEnd(zcs,- op, oend-op, ip, iend-ip);- DEBUGLOG(4, "ZSTD_compressEnd : cSize=%u", (unsigned)cSize);- FORWARD_IF_ERROR(cSize);- ip = iend;- op += cSize;- zcs->frameEnded = 1;- ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);- someMoreWork = 0; break;- }- /* complete loading into inBuffer */- { size_t const toLoad = zcs->inBuffTarget - zcs->inBuffPos;- size_t const loaded = ZSTD_limitCopy(- zcs->inBuff + zcs->inBuffPos, toLoad,- ip, iend-ip);- zcs->inBuffPos += loaded;- ip += loaded;- if ( (flushMode == ZSTD_e_continue)- && (zcs->inBuffPos < zcs->inBuffTarget) ) {- /* not enough input to fill full block : stop here */- someMoreWork = 0; break;- }- if ( (flushMode == ZSTD_e_flush)- && (zcs->inBuffPos == zcs->inToCompress) ) {- /* empty */- someMoreWork = 0; break;- }- }- /* compress current block (note : this stage cannot be stopped in the middle) */- DEBUGLOG(5, "stream compression stage (flushMode==%u)", flushMode);- { void* cDst;- size_t cSize;- size_t const iSize = zcs->inBuffPos - zcs->inToCompress;- size_t oSize = oend-op;- unsigned const lastBlock = (flushMode == ZSTD_e_end) && (ip==iend);- if (oSize >= ZSTD_compressBound(iSize))- cDst = op; /* compress into output buffer, to skip flush stage */- else- cDst = zcs->outBuff, oSize = zcs->outBuffSize;- cSize = lastBlock ?- ZSTD_compressEnd(zcs, cDst, oSize,- zcs->inBuff + zcs->inToCompress, iSize) :- ZSTD_compressContinue(zcs, cDst, oSize,- zcs->inBuff + zcs->inToCompress, iSize);- FORWARD_IF_ERROR(cSize);- zcs->frameEnded = lastBlock;- /* prepare next block */- zcs->inBuffTarget = zcs->inBuffPos + zcs->blockSize;- if (zcs->inBuffTarget > zcs->inBuffSize)- zcs->inBuffPos = 0, zcs->inBuffTarget = zcs->blockSize;- DEBUGLOG(5, "inBuffTarget:%u / inBuffSize:%u",- (unsigned)zcs->inBuffTarget, (unsigned)zcs->inBuffSize);- if (!lastBlock)- assert(zcs->inBuffTarget <= zcs->inBuffSize);- zcs->inToCompress = zcs->inBuffPos;- if (cDst == op) { /* no need to flush */- op += cSize;- if (zcs->frameEnded) {- DEBUGLOG(5, "Frame completed directly in outBuffer");- someMoreWork = 0;- ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);- }- break;- }- zcs->outBuffContentSize = cSize;- zcs->outBuffFlushedSize = 0;- zcs->streamStage = zcss_flush; /* pass-through to flush stage */- }- /* fall-through */- case zcss_flush:- DEBUGLOG(5, "flush stage");- { size_t const toFlush = zcs->outBuffContentSize - zcs->outBuffFlushedSize;- size_t const flushed = ZSTD_limitCopy(op, oend-op,- zcs->outBuff + zcs->outBuffFlushedSize, toFlush);- DEBUGLOG(5, "toFlush: %u into %u ==> flushed: %u",- (unsigned)toFlush, (unsigned)(oend-op), (unsigned)flushed);- op += flushed;- zcs->outBuffFlushedSize += flushed;- if (toFlush!=flushed) {- /* flush not fully completed, presumably because dst is too small */- assert(op==oend);- someMoreWork = 0;- break;- }- zcs->outBuffContentSize = zcs->outBuffFlushedSize = 0;- if (zcs->frameEnded) {- DEBUGLOG(5, "Frame completed on flush");- someMoreWork = 0;- ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);- break;- }- zcs->streamStage = zcss_load;- break;- }-- default: /* impossible */- assert(0);- }- }-- input->pos = ip - istart;- output->pos = op - ostart;- if (zcs->frameEnded) return 0;- return ZSTD_nextInputSizeHint(zcs);-}--static size_t ZSTD_nextInputSizeHint_MTorST(const ZSTD_CCtx* cctx)-{-#ifdef ZSTD_MULTITHREAD- if (cctx->appliedParams.nbWorkers >= 1) {- assert(cctx->mtctx != NULL);- return ZSTDMT_nextInputSizeHint(cctx->mtctx);- }-#endif- return ZSTD_nextInputSizeHint(cctx);--}--size_t ZSTD_compressStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input)-{- FORWARD_IF_ERROR( ZSTD_compressStream2(zcs, output, input, ZSTD_e_continue) );- return ZSTD_nextInputSizeHint_MTorST(zcs);-}---size_t ZSTD_compressStream2( ZSTD_CCtx* cctx,- ZSTD_outBuffer* output,- ZSTD_inBuffer* input,- ZSTD_EndDirective endOp)-{- DEBUGLOG(5, "ZSTD_compressStream2, endOp=%u ", (unsigned)endOp);- /* check conditions */- RETURN_ERROR_IF(output->pos > output->size, GENERIC);- RETURN_ERROR_IF(input->pos > input->size, GENERIC);- assert(cctx!=NULL);-- /* transparent initialization stage */- if (cctx->streamStage == zcss_init) {- ZSTD_CCtx_params params = cctx->requestedParams;- ZSTD_prefixDict const prefixDict = cctx->prefixDict;- FORWARD_IF_ERROR( ZSTD_initLocalDict(cctx) ); /* Init the local dict if present. */- memset(&cctx->prefixDict, 0, sizeof(cctx->prefixDict)); /* single usage */- assert(prefixDict.dict==NULL || cctx->cdict==NULL); /* only one can be set */- DEBUGLOG(4, "ZSTD_compressStream2 : transparent init stage");- if (endOp == ZSTD_e_end) cctx->pledgedSrcSizePlusOne = input->size + 1; /* auto-fix pledgedSrcSize */- params.cParams = ZSTD_getCParamsFromCCtxParams(- &cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1, 0 /*dictSize*/);---#ifdef ZSTD_MULTITHREAD- if ((cctx->pledgedSrcSizePlusOne-1) <= ZSTDMT_JOBSIZE_MIN) {- params.nbWorkers = 0; /* do not invoke multi-threading when src size is too small */- }- if (params.nbWorkers > 0) {- /* mt context creation */- if (cctx->mtctx == NULL) {- DEBUGLOG(4, "ZSTD_compressStream2: creating new mtctx for nbWorkers=%u",- params.nbWorkers);- cctx->mtctx = ZSTDMT_createCCtx_advanced(params.nbWorkers, cctx->customMem);- RETURN_ERROR_IF(cctx->mtctx == NULL, memory_allocation);- }- /* mt compression */- DEBUGLOG(4, "call ZSTDMT_initCStream_internal as nbWorkers=%u", params.nbWorkers);- FORWARD_IF_ERROR( ZSTDMT_initCStream_internal(- cctx->mtctx,- prefixDict.dict, prefixDict.dictSize, ZSTD_dct_rawContent,- cctx->cdict, params, cctx->pledgedSrcSizePlusOne-1) );- cctx->streamStage = zcss_load;- cctx->appliedParams.nbWorkers = params.nbWorkers;- } else-#endif- { FORWARD_IF_ERROR( ZSTD_resetCStream_internal(cctx,- prefixDict.dict, prefixDict.dictSize, prefixDict.dictContentType,- cctx->cdict,- params, cctx->pledgedSrcSizePlusOne-1) );- assert(cctx->streamStage == zcss_load);- assert(cctx->appliedParams.nbWorkers == 0);- } }- /* end of transparent initialization stage */-- /* compression stage */-#ifdef ZSTD_MULTITHREAD- if (cctx->appliedParams.nbWorkers > 0) {- int const forceMaxProgress = (endOp == ZSTD_e_flush || endOp == ZSTD_e_end);- size_t flushMin;- assert(forceMaxProgress || endOp == ZSTD_e_continue /* Protection for a new flush type */);- if (cctx->cParamsChanged) {- ZSTDMT_updateCParams_whileCompressing(cctx->mtctx, &cctx->requestedParams);- cctx->cParamsChanged = 0;- }- do {- flushMin = ZSTDMT_compressStream_generic(cctx->mtctx, output, input, endOp);- if ( ZSTD_isError(flushMin)- || (endOp == ZSTD_e_end && flushMin == 0) ) { /* compression completed */- ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only);- }- FORWARD_IF_ERROR(flushMin);- } while (forceMaxProgress && flushMin != 0 && output->pos < output->size);- DEBUGLOG(5, "completed ZSTD_compressStream2 delegating to ZSTDMT_compressStream_generic");- /* Either we don't require maximum forward progress, we've finished the- * flush, or we are out of output space.- */- assert(!forceMaxProgress || flushMin == 0 || output->pos == output->size);- return flushMin;- }-#endif- FORWARD_IF_ERROR( ZSTD_compressStream_generic(cctx, output, input, endOp) );- DEBUGLOG(5, "completed ZSTD_compressStream2");- return cctx->outBuffContentSize - cctx->outBuffFlushedSize; /* remaining to flush */-}--size_t ZSTD_compressStream2_simpleArgs (- ZSTD_CCtx* cctx,- void* dst, size_t dstCapacity, size_t* dstPos,- 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_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 ZSTD_compress2(ZSTD_CCtx* cctx,- void* dst, size_t dstCapacity,- const void* src, size_t srcSize)-{- ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only);- { size_t oPos = 0;- size_t iPos = 0;- size_t const result = ZSTD_compressStream2_simpleArgs(cctx,- dst, dstCapacity, &oPos,- src, srcSize, &iPos,- ZSTD_e_end);- FORWARD_IF_ERROR(result);- if (result != 0) { /* compression not completed, due to lack of output space */- assert(oPos == dstCapacity);- RETURN_ERROR(dstSize_tooSmall);- }- assert(iPos == srcSize); /* all input is expected consumed */- return oPos;- }-}--/*====== Finalize ======*/--/*! 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 };- 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 };- size_t const remainingToFlush = ZSTD_compressStream2(zcs, output, &input, ZSTD_e_end);- FORWARD_IF_ERROR( remainingToFlush );- 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;- size_t const checksumSize = zcs->frameEnded ? 0 : zcs->appliedParams.fParams.checksumFlag * 4;- size_t const toFlush = remainingToFlush + lastBlockSize + checksumSize;- DEBUGLOG(4, "ZSTD_endStream : remaining to flush : %u", (unsigned)toFlush);- return toFlush;- }-}---/*-===== Pre-defined compression levels =====-*/--#define ZSTD_MAX_CLEVEL 22-int ZSTD_maxCLevel(void) { return ZSTD_MAX_CLEVEL; }-int ZSTD_minCLevel(void) { return (int)-ZSTD_TARGETLENGTH_MAX; }--static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEVEL+1] = {-{ /* "default" - for any srcSize > 256 KB */- /* W, C, H, S, L, TL, strat */- { 19, 12, 13, 1, 6, 1, ZSTD_fast }, /* base for negative levels */- { 19, 13, 14, 1, 7, 0, ZSTD_fast }, /* level 1 */- { 20, 15, 16, 1, 6, 0, ZSTD_fast }, /* level 2 */- { 21, 16, 17, 1, 5, 1, ZSTD_dfast }, /* level 3 */- { 21, 18, 18, 1, 5, 1, ZSTD_dfast }, /* level 4 */- { 21, 18, 19, 2, 5, 2, ZSTD_greedy }, /* level 5 */- { 21, 19, 19, 3, 5, 4, ZSTD_greedy }, /* level 6 */- { 21, 19, 19, 3, 5, 8, ZSTD_lazy }, /* level 7 */- { 21, 19, 19, 3, 5, 16, ZSTD_lazy2 }, /* level 8 */- { 21, 19, 20, 4, 5, 16, ZSTD_lazy2 }, /* level 9 */- { 22, 20, 21, 4, 5, 16, ZSTD_lazy2 }, /* level 10 */- { 22, 21, 22, 4, 5, 16, ZSTD_lazy2 }, /* level 11 */- { 22, 21, 22, 5, 5, 16, ZSTD_lazy2 }, /* level 12 */- { 22, 21, 22, 5, 5, 32, ZSTD_btlazy2 }, /* level 13 */- { 22, 22, 23, 5, 5, 32, ZSTD_btlazy2 }, /* level 14 */- { 22, 23, 23, 6, 5, 32, ZSTD_btlazy2 }, /* level 15 */- { 22, 22, 22, 5, 5, 48, ZSTD_btopt }, /* level 16 */- { 23, 23, 22, 5, 4, 64, ZSTD_btopt }, /* level 17 */- { 23, 23, 22, 6, 3, 64, ZSTD_btultra }, /* level 18 */- { 23, 24, 22, 7, 3,256, ZSTD_btultra2}, /* level 19 */- { 25, 25, 23, 7, 3,256, ZSTD_btultra2}, /* level 20 */- { 26, 26, 24, 7, 3,512, ZSTD_btultra2}, /* level 21 */- { 27, 27, 25, 9, 3,999, ZSTD_btultra2}, /* level 22 */-},-{ /* for srcSize <= 256 KB */- /* W, C, H, S, L, T, strat */- { 18, 12, 13, 1, 5, 1, ZSTD_fast }, /* base for negative levels */- { 18, 13, 14, 1, 6, 0, ZSTD_fast }, /* level 1 */- { 18, 14, 14, 1, 5, 1, ZSTD_dfast }, /* level 2 */- { 18, 16, 16, 1, 4, 1, ZSTD_dfast }, /* level 3 */- { 18, 16, 17, 2, 5, 2, ZSTD_greedy }, /* level 4.*/- { 18, 18, 18, 3, 5, 2, ZSTD_greedy }, /* level 5.*/- { 18, 18, 19, 3, 5, 4, ZSTD_lazy }, /* level 6.*/- { 18, 18, 19, 4, 4, 4, ZSTD_lazy }, /* level 7 */- { 18, 18, 19, 4, 4, 8, ZSTD_lazy2 }, /* level 8 */- { 18, 18, 19, 5, 4, 8, ZSTD_lazy2 }, /* level 9 */- { 18, 18, 19, 6, 4, 8, ZSTD_lazy2 }, /* level 10 */- { 18, 18, 19, 5, 4, 12, ZSTD_btlazy2 }, /* level 11.*/- { 18, 19, 19, 7, 4, 12, ZSTD_btlazy2 }, /* level 12.*/- { 18, 18, 19, 4, 4, 16, ZSTD_btopt }, /* level 13 */- { 18, 18, 19, 4, 3, 32, ZSTD_btopt }, /* level 14.*/- { 18, 18, 19, 6, 3,128, ZSTD_btopt }, /* level 15.*/- { 18, 19, 19, 6, 3,128, ZSTD_btultra }, /* level 16.*/- { 18, 19, 19, 8, 3,256, ZSTD_btultra }, /* level 17.*/- { 18, 19, 19, 6, 3,128, ZSTD_btultra2}, /* level 18.*/- { 18, 19, 19, 8, 3,256, ZSTD_btultra2}, /* level 19.*/- { 18, 19, 19, 10, 3,512, ZSTD_btultra2}, /* level 20.*/- { 18, 19, 19, 12, 3,512, ZSTD_btultra2}, /* level 21.*/- { 18, 19, 19, 13, 3,999, ZSTD_btultra2}, /* level 22.*/-},-{ /* for srcSize <= 128 KB */- /* W, C, H, S, L, T, strat */- { 17, 12, 12, 1, 5, 1, ZSTD_fast }, /* base for negative levels */- { 17, 12, 13, 1, 6, 0, ZSTD_fast }, /* level 1 */- { 17, 13, 15, 1, 5, 0, ZSTD_fast }, /* level 2 */- { 17, 15, 16, 2, 5, 1, ZSTD_dfast }, /* level 3 */- { 17, 17, 17, 2, 4, 1, ZSTD_dfast }, /* level 4 */- { 17, 16, 17, 3, 4, 2, ZSTD_greedy }, /* level 5 */- { 17, 17, 17, 3, 4, 4, ZSTD_lazy }, /* level 6 */- { 17, 17, 17, 3, 4, 8, ZSTD_lazy2 }, /* level 7 */- { 17, 17, 17, 4, 4, 8, ZSTD_lazy2 }, /* level 8 */- { 17, 17, 17, 5, 4, 8, ZSTD_lazy2 }, /* level 9 */- { 17, 17, 17, 6, 4, 8, ZSTD_lazy2 }, /* level 10 */- { 17, 17, 17, 5, 4, 8, ZSTD_btlazy2 }, /* level 11 */- { 17, 18, 17, 7, 4, 12, ZSTD_btlazy2 }, /* level 12 */- { 17, 18, 17, 3, 4, 12, ZSTD_btopt }, /* level 13.*/- { 17, 18, 17, 4, 3, 32, ZSTD_btopt }, /* level 14.*/- { 17, 18, 17, 6, 3,256, ZSTD_btopt }, /* level 15.*/- { 17, 18, 17, 6, 3,128, ZSTD_btultra }, /* level 16.*/- { 17, 18, 17, 8, 3,256, ZSTD_btultra }, /* level 17.*/- { 17, 18, 17, 10, 3,512, ZSTD_btultra }, /* level 18.*/- { 17, 18, 17, 5, 3,256, ZSTD_btultra2}, /* level 19.*/- { 17, 18, 17, 7, 3,512, ZSTD_btultra2}, /* level 20.*/- { 17, 18, 17, 9, 3,512, ZSTD_btultra2}, /* level 21.*/- { 17, 18, 17, 11, 3,999, ZSTD_btultra2}, /* level 22.*/-},-{ /* for srcSize <= 16 KB */- /* W, C, H, S, L, T, strat */- { 14, 12, 13, 1, 5, 1, ZSTD_fast }, /* base for negative levels */- { 14, 14, 15, 1, 5, 0, ZSTD_fast }, /* level 1 */- { 14, 14, 15, 1, 4, 0, ZSTD_fast }, /* level 2 */- { 14, 14, 15, 2, 4, 1, ZSTD_dfast }, /* level 3 */- { 14, 14, 14, 4, 4, 2, ZSTD_greedy }, /* level 4 */- { 14, 14, 14, 3, 4, 4, ZSTD_lazy }, /* level 5.*/- { 14, 14, 14, 4, 4, 8, ZSTD_lazy2 }, /* level 6 */- { 14, 14, 14, 6, 4, 8, ZSTD_lazy2 }, /* level 7 */- { 14, 14, 14, 8, 4, 8, ZSTD_lazy2 }, /* level 8.*/- { 14, 15, 14, 5, 4, 8, ZSTD_btlazy2 }, /* level 9.*/- { 14, 15, 14, 9, 4, 8, ZSTD_btlazy2 }, /* level 10.*/- { 14, 15, 14, 3, 4, 12, ZSTD_btopt }, /* level 11.*/- { 14, 15, 14, 4, 3, 24, ZSTD_btopt }, /* level 12.*/- { 14, 15, 14, 5, 3, 32, ZSTD_btultra }, /* level 13.*/- { 14, 15, 15, 6, 3, 64, ZSTD_btultra }, /* level 14.*/- { 14, 15, 15, 7, 3,256, ZSTD_btultra }, /* level 15.*/- { 14, 15, 15, 5, 3, 48, ZSTD_btultra2}, /* level 16.*/- { 14, 15, 15, 6, 3,128, ZSTD_btultra2}, /* level 17.*/- { 14, 15, 15, 7, 3,256, ZSTD_btultra2}, /* level 18.*/- { 14, 15, 15, 8, 3,256, ZSTD_btultra2}, /* level 19.*/- { 14, 15, 15, 8, 3,512, ZSTD_btultra2}, /* level 20.*/- { 14, 15, 15, 9, 3,512, ZSTD_btultra2}, /* level 21.*/- { 14, 15, 15, 10, 3,999, ZSTD_btultra2}, /* level 22.*/-},-};--/*! ZSTD_getCParams() :- * @return ZSTD_compressionParameters structure for a selected compression level, srcSize and dictSize.- * Size values are optional, provide 0 if not known or unused */-ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize)-{- size_t const addedSize = srcSizeHint ? 0 : 500;- U64 const rSize = srcSizeHint+dictSize ? srcSizeHint+dictSize+addedSize : ZSTD_CONTENTSIZE_UNKNOWN; /* intentional overflow for srcSizeHint == ZSTD_CONTENTSIZE_UNKNOWN */- U32 const tableID = (rSize <= 256 KB) + (rSize <= 128 KB) + (rSize <= 16 KB);- int row = compressionLevel;- DEBUGLOG(5, "ZSTD_getCParams (cLevel=%i)", compressionLevel);- if (compressionLevel == 0) row = ZSTD_CLEVEL_DEFAULT; /* 0 == default */- if (compressionLevel < 0) row = 0; /* entry 0 is baseline for fast mode */- if (compressionLevel > ZSTD_MAX_CLEVEL) row = ZSTD_MAX_CLEVEL;- { ZSTD_compressionParameters cp = ZSTD_defaultCParameters[tableID][row];- if (compressionLevel < 0) cp.targetLength = (unsigned)(-compressionLevel); /* acceleration factor */- return ZSTD_adjustCParams_internal(cp, srcSizeHint, dictSize); /* refine parameters based on srcSize & dictSize */- }-}--/*! ZSTD_getParams() :- * same idea as ZSTD_getCParams()- * @return a `ZSTD_parameters` structure (instead of `ZSTD_compressionParameters`).- * Fields of `ZSTD_frameParameters` are set to default values */-ZSTD_parameters ZSTD_getParams(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize) {- ZSTD_parameters params;- ZSTD_compressionParameters const cParams = ZSTD_getCParams(compressionLevel, srcSizeHint, dictSize);- DEBUGLOG(5, "ZSTD_getParams (cLevel=%i)", compressionLevel);- memset(¶ms, 0, sizeof(params));- params.cParams = cParams;- params.fParams.contentSizeFlag = 1;- return params;+ * Copyright (c) 2016-2020, 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 <limits.h> /* INT_MAX */+#include <string.h> /* memset */+#include "../common/cpu.h"+#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"+#include "zstd_compress_literals.h"+#include "zstd_fast.h"+#include "zstd_double_fast.h"+#include "zstd_lazy.h"+#include "zstd_opt.h"+#include "zstd_ldm.h"+#include "zstd_compress_superblock.h"+++/*-*************************************+* 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().+ */+size_t ZSTD_compressBound(size_t srcSize) {+ return ZSTD_COMPRESSBOUND(srcSize);+}+++/*-*************************************+* Context memory management+***************************************/+struct ZSTD_CDict_s {+ const void* dictContent;+ size_t dictContentSize;+ U32* entropyWorkspace; /* entropy workspace of HUF_WORKSPACE_SIZE bytes */+ ZSTD_cwksp workspace;+ ZSTD_matchState_t matchState;+ ZSTD_compressedBlockState_t cBlockState;+ ZSTD_customMem customMem;+ U32 dictID;+ int compressionLevel; /* 0 indicates that advanced API was used to select CDict params */+}; /* typedef'd to ZSTD_CDict within "zstd.h" */++ZSTD_CCtx* ZSTD_createCCtx(void)+{+ return ZSTD_createCCtx_advanced(ZSTD_defaultCMem);+}++static void ZSTD_initCCtx(ZSTD_CCtx* cctx, ZSTD_customMem memManager)+{+ assert(cctx != NULL);+ memset(cctx, 0, sizeof(*cctx));+ cctx->customMem = memManager;+ cctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid());+ { size_t const err = ZSTD_CCtx_reset(cctx, ZSTD_reset_parameters);+ assert(!ZSTD_isError(err));+ (void)err;+ }+}++ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem)+{+ ZSTD_STATIC_ASSERT(zcss_init==0);+ ZSTD_STATIC_ASSERT(ZSTD_CONTENTSIZE_UNKNOWN==(0ULL - 1));+ if (!customMem.customAlloc ^ !customMem.customFree) return NULL;+ { ZSTD_CCtx* const cctx = (ZSTD_CCtx*)ZSTD_malloc(sizeof(ZSTD_CCtx), customMem);+ if (!cctx) return NULL;+ ZSTD_initCCtx(cctx, customMem);+ return cctx;+ }+}++ZSTD_CCtx* ZSTD_initStaticCCtx(void* workspace, size_t workspaceSize)+{+ ZSTD_cwksp ws;+ ZSTD_CCtx* cctx;+ if (workspaceSize <= sizeof(ZSTD_CCtx)) return NULL; /* minimum size */+ if ((size_t)workspace & 7) return NULL; /* must be 8-aligned */+ ZSTD_cwksp_init(&ws, workspace, workspaceSize);++ cctx = (ZSTD_CCtx*)ZSTD_cwksp_reserve_object(&ws, sizeof(ZSTD_CCtx));+ if (cctx == NULL) return NULL;++ memset(cctx, 0, sizeof(ZSTD_CCtx));+ ZSTD_cwksp_move(&cctx->workspace, &ws);+ cctx->staticSize = workspaceSize;++ /* statically sized space. entropyWorkspace never moves (but prev/next block swap places) */+ if (!ZSTD_cwksp_check_available(&cctx->workspace, HUF_WORKSPACE_SIZE + 2 * sizeof(ZSTD_compressedBlockState_t))) return NULL;+ cctx->blockState.prevCBlock = (ZSTD_compressedBlockState_t*)ZSTD_cwksp_reserve_object(&cctx->workspace, sizeof(ZSTD_compressedBlockState_t));+ cctx->blockState.nextCBlock = (ZSTD_compressedBlockState_t*)ZSTD_cwksp_reserve_object(&cctx->workspace, sizeof(ZSTD_compressedBlockState_t));+ cctx->entropyWorkspace = (U32*)ZSTD_cwksp_reserve_object(&cctx->workspace, HUF_WORKSPACE_SIZE);+ cctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid());+ return cctx;+}++/**+ * Clears and frees all of the dictionaries in the CCtx.+ */+static void ZSTD_clearAllDicts(ZSTD_CCtx* cctx)+{+ ZSTD_free(cctx->localDict.dictBuffer, cctx->customMem);+ ZSTD_freeCDict(cctx->localDict.cdict);+ memset(&cctx->localDict, 0, sizeof(cctx->localDict));+ memset(&cctx->prefixDict, 0, sizeof(cctx->prefixDict));+ cctx->cdict = NULL;+}++static size_t ZSTD_sizeof_localDict(ZSTD_localDict dict)+{+ size_t const bufferSize = dict.dictBuffer != NULL ? dict.dictSize : 0;+ size_t const cdictSize = ZSTD_sizeof_CDict(dict.cdict);+ return bufferSize + cdictSize;+}++static void ZSTD_freeCCtxContent(ZSTD_CCtx* cctx)+{+ assert(cctx != NULL);+ assert(cctx->staticSize == 0);+ ZSTD_clearAllDicts(cctx);+#ifdef ZSTD_MULTITHREAD+ ZSTDMT_freeCCtx(cctx->mtctx); cctx->mtctx = NULL;+#endif+ ZSTD_cwksp_free(&cctx->workspace, cctx->customMem);+}++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);+ ZSTD_freeCCtxContent(cctx);+ if (!cctxInWorkspace) {+ ZSTD_free(cctx, cctx->customMem);+ }+ }+ return 0;+}+++static size_t ZSTD_sizeof_mtctx(const ZSTD_CCtx* cctx)+{+#ifdef ZSTD_MULTITHREAD+ return ZSTDMT_sizeof_CCtx(cctx->mtctx);+#else+ (void)cctx;+ return 0;+#endif+}+++size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx)+{+ if (cctx==NULL) return 0; /* support sizeof on NULL */+ /* cctx may be in the workspace */+ return (cctx->workspace.workspace == cctx ? 0 : sizeof(*cctx))+ + ZSTD_cwksp_sizeof(&cctx->workspace)+ + ZSTD_sizeof_localDict(cctx->localDict)+ + ZSTD_sizeof_mtctx(cctx);+}++size_t ZSTD_sizeof_CStream(const ZSTD_CStream* zcs)+{+ return ZSTD_sizeof_CCtx(zcs); /* same object */+}++/* private API call, for dictBuilder only */+const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx) { return &(ctx->seqStore); }++static ZSTD_CCtx_params ZSTD_makeCCtxParamsFromCParams(+ ZSTD_compressionParameters cParams)+{+ ZSTD_CCtx_params cctxParams;+ memset(&cctxParams, 0, sizeof(cctxParams));+ cctxParams.cParams = cParams;+ cctxParams.compressionLevel = ZSTD_CLEVEL_DEFAULT; /* should not matter, as all cParams are presumed properly defined */+ assert(!ZSTD_checkCParams(cParams));+ cctxParams.fParams.contentSizeFlag = 1;+ return cctxParams;+}++static ZSTD_CCtx_params* ZSTD_createCCtxParams_advanced(+ ZSTD_customMem customMem)+{+ ZSTD_CCtx_params* params;+ if (!customMem.customAlloc ^ !customMem.customFree) return NULL;+ params = (ZSTD_CCtx_params*)ZSTD_calloc(+ sizeof(ZSTD_CCtx_params), customMem);+ if (!params) { return NULL; }+ params->customMem = customMem;+ params->compressionLevel = ZSTD_CLEVEL_DEFAULT;+ params->fParams.contentSizeFlag = 1;+ return params;+}++ZSTD_CCtx_params* ZSTD_createCCtxParams(void)+{+ return ZSTD_createCCtxParams_advanced(ZSTD_defaultCMem);+}++size_t ZSTD_freeCCtxParams(ZSTD_CCtx_params* params)+{+ if (params == NULL) { return 0; }+ ZSTD_free(params, params->customMem);+ return 0;+}++size_t ZSTD_CCtxParams_reset(ZSTD_CCtx_params* params)+{+ return ZSTD_CCtxParams_init(params, ZSTD_CLEVEL_DEFAULT);+}++size_t ZSTD_CCtxParams_init(ZSTD_CCtx_params* cctxParams, int compressionLevel) {+ RETURN_ERROR_IF(!cctxParams, GENERIC, "NULL pointer!");+ memset(cctxParams, 0, sizeof(*cctxParams));+ cctxParams->compressionLevel = compressionLevel;+ cctxParams->fParams.contentSizeFlag = 1;+ return 0;+}++size_t ZSTD_CCtxParams_init_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params)+{+ RETURN_ERROR_IF(!cctxParams, GENERIC, "NULL pointer!");+ FORWARD_IF_ERROR( ZSTD_checkCParams(params.cParams) , "");+ memset(cctxParams, 0, sizeof(*cctxParams));+ assert(!ZSTD_checkCParams(params.cParams));+ cctxParams->cParams = params.cParams;+ cctxParams->fParams = params.fParams;+ cctxParams->compressionLevel = ZSTD_CLEVEL_DEFAULT; /* should not matter, as all cParams are presumed properly defined */+ return 0;+}++/* ZSTD_assignParamsToCCtxParams() :+ * params is presumed valid at this stage */+static ZSTD_CCtx_params ZSTD_assignParamsToCCtxParams(+ const ZSTD_CCtx_params* cctxParams, const ZSTD_parameters* params)+{+ ZSTD_CCtx_params ret = *cctxParams;+ assert(!ZSTD_checkCParams(params->cParams));+ ret.cParams = params->cParams;+ ret.fParams = params->fParams;+ ret.compressionLevel = ZSTD_CLEVEL_DEFAULT; /* should not matter, as all cParams are presumed properly defined */+ return ret;+}++ZSTD_bounds ZSTD_cParam_getBounds(ZSTD_cParameter param)+{+ ZSTD_bounds bounds = { 0, 0, 0 };++ switch(param)+ {+ case ZSTD_c_compressionLevel:+ bounds.lowerBound = ZSTD_minCLevel();+ bounds.upperBound = ZSTD_maxCLevel();+ return bounds;++ case ZSTD_c_windowLog:+ bounds.lowerBound = ZSTD_WINDOWLOG_MIN;+ bounds.upperBound = ZSTD_WINDOWLOG_MAX;+ return bounds;++ case ZSTD_c_hashLog:+ bounds.lowerBound = ZSTD_HASHLOG_MIN;+ bounds.upperBound = ZSTD_HASHLOG_MAX;+ return bounds;++ case ZSTD_c_chainLog:+ bounds.lowerBound = ZSTD_CHAINLOG_MIN;+ bounds.upperBound = ZSTD_CHAINLOG_MAX;+ return bounds;++ case ZSTD_c_searchLog:+ bounds.lowerBound = ZSTD_SEARCHLOG_MIN;+ bounds.upperBound = ZSTD_SEARCHLOG_MAX;+ return bounds;++ case ZSTD_c_minMatch:+ bounds.lowerBound = ZSTD_MINMATCH_MIN;+ bounds.upperBound = ZSTD_MINMATCH_MAX;+ return bounds;++ case ZSTD_c_targetLength:+ bounds.lowerBound = ZSTD_TARGETLENGTH_MIN;+ bounds.upperBound = ZSTD_TARGETLENGTH_MAX;+ return bounds;++ case ZSTD_c_strategy:+ bounds.lowerBound = ZSTD_STRATEGY_MIN;+ bounds.upperBound = ZSTD_STRATEGY_MAX;+ return bounds;++ case ZSTD_c_contentSizeFlag:+ bounds.lowerBound = 0;+ bounds.upperBound = 1;+ return bounds;++ case ZSTD_c_checksumFlag:+ bounds.lowerBound = 0;+ bounds.upperBound = 1;+ return bounds;++ case ZSTD_c_dictIDFlag:+ bounds.lowerBound = 0;+ bounds.upperBound = 1;+ return bounds;++ case ZSTD_c_nbWorkers:+ bounds.lowerBound = 0;+#ifdef ZSTD_MULTITHREAD+ bounds.upperBound = ZSTDMT_NBWORKERS_MAX;+#else+ bounds.upperBound = 0;+#endif+ return bounds;++ case ZSTD_c_jobSize:+ bounds.lowerBound = 0;+#ifdef ZSTD_MULTITHREAD+ bounds.upperBound = ZSTDMT_JOBSIZE_MAX;+#else+ bounds.upperBound = 0;+#endif+ return bounds;++ case ZSTD_c_overlapLog:+#ifdef ZSTD_MULTITHREAD+ bounds.lowerBound = ZSTD_OVERLAPLOG_MIN;+ bounds.upperBound = ZSTD_OVERLAPLOG_MAX;+#else+ bounds.lowerBound = 0;+ bounds.upperBound = 0;+#endif+ return bounds;++ case ZSTD_c_enableLongDistanceMatching:+ bounds.lowerBound = 0;+ bounds.upperBound = 1;+ return bounds;++ case ZSTD_c_ldmHashLog:+ bounds.lowerBound = ZSTD_LDM_HASHLOG_MIN;+ bounds.upperBound = ZSTD_LDM_HASHLOG_MAX;+ return bounds;++ case ZSTD_c_ldmMinMatch:+ bounds.lowerBound = ZSTD_LDM_MINMATCH_MIN;+ bounds.upperBound = ZSTD_LDM_MINMATCH_MAX;+ return bounds;++ case ZSTD_c_ldmBucketSizeLog:+ bounds.lowerBound = ZSTD_LDM_BUCKETSIZELOG_MIN;+ bounds.upperBound = ZSTD_LDM_BUCKETSIZELOG_MAX;+ return bounds;++ case ZSTD_c_ldmHashRateLog:+ bounds.lowerBound = ZSTD_LDM_HASHRATELOG_MIN;+ bounds.upperBound = ZSTD_LDM_HASHRATELOG_MAX;+ return bounds;++ /* experimental parameters */+ case ZSTD_c_rsyncable:+ bounds.lowerBound = 0;+ bounds.upperBound = 1;+ return bounds;++ case ZSTD_c_forceMaxWindow :+ bounds.lowerBound = 0;+ bounds.upperBound = 1;+ return bounds;++ case ZSTD_c_format:+ ZSTD_STATIC_ASSERT(ZSTD_f_zstd1 < ZSTD_f_zstd1_magicless);+ bounds.lowerBound = ZSTD_f_zstd1;+ bounds.upperBound = ZSTD_f_zstd1_magicless; /* note : how to ensure at compile time that this is the highest value enum ? */+ return bounds;++ case ZSTD_c_forceAttachDict:+ ZSTD_STATIC_ASSERT(ZSTD_dictDefaultAttach < ZSTD_dictForceCopy);+ bounds.lowerBound = ZSTD_dictDefaultAttach;+ bounds.upperBound = ZSTD_dictForceLoad; /* note : how to ensure at compile time that this is the highest value enum ? */+ return bounds;++ case ZSTD_c_literalCompressionMode:+ ZSTD_STATIC_ASSERT(ZSTD_lcm_auto < ZSTD_lcm_huffman && ZSTD_lcm_huffman < ZSTD_lcm_uncompressed);+ bounds.lowerBound = ZSTD_lcm_auto;+ bounds.upperBound = ZSTD_lcm_uncompressed;+ return bounds;++ case ZSTD_c_targetCBlockSize:+ bounds.lowerBound = ZSTD_TARGETCBLOCKSIZE_MIN;+ bounds.upperBound = ZSTD_TARGETCBLOCKSIZE_MAX;+ return bounds;++ case ZSTD_c_srcSizeHint:+ bounds.lowerBound = ZSTD_SRCSIZEHINT_MIN;+ bounds.upperBound = ZSTD_SRCSIZEHINT_MAX;+ return bounds;++ default:+ bounds.error = ERROR(parameter_unsupported);+ return bounds;+ }+}++/* ZSTD_cParam_clampBounds:+ * Clamps the value into the bounded range.+ */+static size_t ZSTD_cParam_clampBounds(ZSTD_cParameter cParam, int* value)+{+ ZSTD_bounds const bounds = ZSTD_cParam_getBounds(cParam);+ if (ZSTD_isError(bounds.error)) return bounds.error;+ if (*value < bounds.lowerBound) *value = bounds.lowerBound;+ if (*value > bounds.upperBound) *value = bounds.upperBound;+ return 0;+}++#define BOUNDCHECK(cParam, val) { \+ RETURN_ERROR_IF(!ZSTD_cParam_withinBounds(cParam,val), \+ parameter_outOfBound, "Param out of bounds"); \+}+++static int ZSTD_isUpdateAuthorized(ZSTD_cParameter param)+{+ switch(param)+ {+ case ZSTD_c_compressionLevel:+ case ZSTD_c_hashLog:+ case ZSTD_c_chainLog:+ case ZSTD_c_searchLog:+ case ZSTD_c_minMatch:+ case ZSTD_c_targetLength:+ case ZSTD_c_strategy:+ return 1;++ case ZSTD_c_format:+ case ZSTD_c_windowLog:+ case ZSTD_c_contentSizeFlag:+ case ZSTD_c_checksumFlag:+ case ZSTD_c_dictIDFlag:+ case ZSTD_c_forceMaxWindow :+ case ZSTD_c_nbWorkers:+ case ZSTD_c_jobSize:+ case ZSTD_c_overlapLog:+ case ZSTD_c_rsyncable:+ case ZSTD_c_enableLongDistanceMatching:+ case ZSTD_c_ldmHashLog:+ case ZSTD_c_ldmMinMatch:+ case ZSTD_c_ldmBucketSizeLog:+ case ZSTD_c_ldmHashRateLog:+ case ZSTD_c_forceAttachDict:+ case ZSTD_c_literalCompressionMode:+ case ZSTD_c_targetCBlockSize:+ case ZSTD_c_srcSizeHint:+ default:+ return 0;+ }+}++size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, int value)+{+ DEBUGLOG(4, "ZSTD_CCtx_setParameter (%i, %i)", (int)param, value);+ if (cctx->streamStage != zcss_init) {+ if (ZSTD_isUpdateAuthorized(param)) {+ cctx->cParamsChanged = 1;+ } else {+ RETURN_ERROR(stage_wrong, "can only set params in ctx init stage");+ } }++ switch(param)+ {+ case ZSTD_c_nbWorkers:+ RETURN_ERROR_IF((value!=0) && cctx->staticSize, parameter_unsupported,+ "MT not compatible with static alloc");+ break;++ case ZSTD_c_compressionLevel:+ case ZSTD_c_windowLog:+ case ZSTD_c_hashLog:+ case ZSTD_c_chainLog:+ case ZSTD_c_searchLog:+ case ZSTD_c_minMatch:+ case ZSTD_c_targetLength:+ case ZSTD_c_strategy:+ case ZSTD_c_ldmHashRateLog:+ case ZSTD_c_format:+ case ZSTD_c_contentSizeFlag:+ case ZSTD_c_checksumFlag:+ case ZSTD_c_dictIDFlag:+ case ZSTD_c_forceMaxWindow:+ case ZSTD_c_forceAttachDict:+ case ZSTD_c_literalCompressionMode:+ case ZSTD_c_jobSize:+ case ZSTD_c_overlapLog:+ case ZSTD_c_rsyncable:+ case ZSTD_c_enableLongDistanceMatching:+ case ZSTD_c_ldmHashLog:+ case ZSTD_c_ldmMinMatch:+ case ZSTD_c_ldmBucketSizeLog:+ case ZSTD_c_targetCBlockSize:+ case ZSTD_c_srcSizeHint:+ break;++ default: RETURN_ERROR(parameter_unsupported, "unknown parameter");+ }+ return ZSTD_CCtxParams_setParameter(&cctx->requestedParams, param, value);+}++size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* CCtxParams,+ ZSTD_cParameter param, int value)+{+ DEBUGLOG(4, "ZSTD_CCtxParams_setParameter (%i, %i)", (int)param, value);+ switch(param)+ {+ case ZSTD_c_format :+ BOUNDCHECK(ZSTD_c_format, value);+ CCtxParams->format = (ZSTD_format_e)value;+ return (size_t)CCtxParams->format;++ case ZSTD_c_compressionLevel : {+ FORWARD_IF_ERROR(ZSTD_cParam_clampBounds(param, &value), "");+ if (value) { /* 0 : does not change current level */+ CCtxParams->compressionLevel = value;+ }+ if (CCtxParams->compressionLevel >= 0) return (size_t)CCtxParams->compressionLevel;+ return 0; /* return type (size_t) cannot represent negative values */+ }++ case ZSTD_c_windowLog :+ if (value!=0) /* 0 => use default */+ BOUNDCHECK(ZSTD_c_windowLog, value);+ CCtxParams->cParams.windowLog = (U32)value;+ return CCtxParams->cParams.windowLog;++ case ZSTD_c_hashLog :+ if (value!=0) /* 0 => use default */+ BOUNDCHECK(ZSTD_c_hashLog, value);+ CCtxParams->cParams.hashLog = (U32)value;+ return CCtxParams->cParams.hashLog;++ case ZSTD_c_chainLog :+ if (value!=0) /* 0 => use default */+ BOUNDCHECK(ZSTD_c_chainLog, value);+ CCtxParams->cParams.chainLog = (U32)value;+ return CCtxParams->cParams.chainLog;++ case ZSTD_c_searchLog :+ if (value!=0) /* 0 => use default */+ BOUNDCHECK(ZSTD_c_searchLog, value);+ CCtxParams->cParams.searchLog = (U32)value;+ return (size_t)value;++ case ZSTD_c_minMatch :+ if (value!=0) /* 0 => use default */+ BOUNDCHECK(ZSTD_c_minMatch, value);+ CCtxParams->cParams.minMatch = value;+ return CCtxParams->cParams.minMatch;++ case ZSTD_c_targetLength :+ BOUNDCHECK(ZSTD_c_targetLength, value);+ CCtxParams->cParams.targetLength = value;+ return CCtxParams->cParams.targetLength;++ case ZSTD_c_strategy :+ if (value!=0) /* 0 => use default */+ BOUNDCHECK(ZSTD_c_strategy, value);+ CCtxParams->cParams.strategy = (ZSTD_strategy)value;+ return (size_t)CCtxParams->cParams.strategy;++ case ZSTD_c_contentSizeFlag :+ /* 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;++ 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;++ case ZSTD_c_dictIDFlag : /* When applicable, dictionary's dictID is provided in frame header (default:1) */+ DEBUGLOG(4, "set dictIDFlag = %u", (value!=0));+ CCtxParams->fParams.noDictIDFlag = !value;+ return !CCtxParams->fParams.noDictIDFlag;++ case ZSTD_c_forceMaxWindow :+ CCtxParams->forceWindow = (value != 0);+ return CCtxParams->forceWindow;++ case ZSTD_c_forceAttachDict : {+ const ZSTD_dictAttachPref_e pref = (ZSTD_dictAttachPref_e)value;+ BOUNDCHECK(ZSTD_c_forceAttachDict, pref);+ CCtxParams->attachDictPref = pref;+ return CCtxParams->attachDictPref;+ }++ case ZSTD_c_literalCompressionMode : {+ const ZSTD_literalCompressionMode_e lcm = (ZSTD_literalCompressionMode_e)value;+ BOUNDCHECK(ZSTD_c_literalCompressionMode, lcm);+ CCtxParams->literalCompressionMode = lcm;+ return CCtxParams->literalCompressionMode;+ }++ case ZSTD_c_nbWorkers :+#ifndef ZSTD_MULTITHREAD+ RETURN_ERROR_IF(value!=0, parameter_unsupported, "not compiled with multithreading");+ return 0;+#else+ FORWARD_IF_ERROR(ZSTD_cParam_clampBounds(param, &value), "");+ CCtxParams->nbWorkers = value;+ return CCtxParams->nbWorkers;+#endif++ case ZSTD_c_jobSize :+#ifndef ZSTD_MULTITHREAD+ RETURN_ERROR_IF(value!=0, parameter_unsupported, "not compiled with multithreading");+ return 0;+#else+ /* Adjust to the minimum non-default value. */+ if (value != 0 && value < ZSTDMT_JOBSIZE_MIN)+ value = ZSTDMT_JOBSIZE_MIN;+ FORWARD_IF_ERROR(ZSTD_cParam_clampBounds(param, &value), "");+ assert(value >= 0);+ CCtxParams->jobSize = value;+ return CCtxParams->jobSize;+#endif++ case ZSTD_c_overlapLog :+#ifndef ZSTD_MULTITHREAD+ RETURN_ERROR_IF(value!=0, parameter_unsupported, "not compiled with multithreading");+ return 0;+#else+ FORWARD_IF_ERROR(ZSTD_cParam_clampBounds(ZSTD_c_overlapLog, &value), "");+ CCtxParams->overlapLog = value;+ return CCtxParams->overlapLog;+#endif++ case ZSTD_c_rsyncable :+#ifndef ZSTD_MULTITHREAD+ RETURN_ERROR_IF(value!=0, parameter_unsupported, "not compiled with multithreading");+ return 0;+#else+ FORWARD_IF_ERROR(ZSTD_cParam_clampBounds(ZSTD_c_overlapLog, &value), "");+ CCtxParams->rsyncable = value;+ return CCtxParams->rsyncable;+#endif++ case ZSTD_c_enableLongDistanceMatching :+ CCtxParams->ldmParams.enableLdm = (value!=0);+ return CCtxParams->ldmParams.enableLdm;++ case ZSTD_c_ldmHashLog :+ if (value!=0) /* 0 ==> auto */+ BOUNDCHECK(ZSTD_c_ldmHashLog, value);+ CCtxParams->ldmParams.hashLog = value;+ return CCtxParams->ldmParams.hashLog;++ case ZSTD_c_ldmMinMatch :+ if (value!=0) /* 0 ==> default */+ BOUNDCHECK(ZSTD_c_ldmMinMatch, value);+ CCtxParams->ldmParams.minMatchLength = value;+ return CCtxParams->ldmParams.minMatchLength;++ case ZSTD_c_ldmBucketSizeLog :+ if (value!=0) /* 0 ==> default */+ BOUNDCHECK(ZSTD_c_ldmBucketSizeLog, value);+ CCtxParams->ldmParams.bucketSizeLog = value;+ return CCtxParams->ldmParams.bucketSizeLog;++ case ZSTD_c_ldmHashRateLog :+ RETURN_ERROR_IF(value > ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN,+ parameter_outOfBound, "Param out of bounds!");+ CCtxParams->ldmParams.hashRateLog = value;+ return CCtxParams->ldmParams.hashRateLog;++ case ZSTD_c_targetCBlockSize :+ if (value!=0) /* 0 ==> default */+ BOUNDCHECK(ZSTD_c_targetCBlockSize, value);+ CCtxParams->targetCBlockSize = value;+ return CCtxParams->targetCBlockSize;++ case ZSTD_c_srcSizeHint :+ if (value!=0) /* 0 ==> default */+ BOUNDCHECK(ZSTD_c_srcSizeHint, value);+ CCtxParams->srcSizeHint = value;+ return CCtxParams->srcSizeHint;++ default: RETURN_ERROR(parameter_unsupported, "unknown parameter");+ }+}++size_t ZSTD_CCtx_getParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, int* value)+{+ return ZSTD_CCtxParams_getParameter(&cctx->requestedParams, param, value);+}++size_t ZSTD_CCtxParams_getParameter(+ ZSTD_CCtx_params* CCtxParams, ZSTD_cParameter param, int* value)+{+ switch(param)+ {+ case ZSTD_c_format :+ *value = CCtxParams->format;+ break;+ case ZSTD_c_compressionLevel :+ *value = CCtxParams->compressionLevel;+ break;+ case ZSTD_c_windowLog :+ *value = (int)CCtxParams->cParams.windowLog;+ break;+ case ZSTD_c_hashLog :+ *value = (int)CCtxParams->cParams.hashLog;+ break;+ case ZSTD_c_chainLog :+ *value = (int)CCtxParams->cParams.chainLog;+ break;+ case ZSTD_c_searchLog :+ *value = CCtxParams->cParams.searchLog;+ break;+ case ZSTD_c_minMatch :+ *value = CCtxParams->cParams.minMatch;+ break;+ case ZSTD_c_targetLength :+ *value = CCtxParams->cParams.targetLength;+ break;+ case ZSTD_c_strategy :+ *value = (unsigned)CCtxParams->cParams.strategy;+ break;+ case ZSTD_c_contentSizeFlag :+ *value = CCtxParams->fParams.contentSizeFlag;+ break;+ case ZSTD_c_checksumFlag :+ *value = CCtxParams->fParams.checksumFlag;+ break;+ case ZSTD_c_dictIDFlag :+ *value = !CCtxParams->fParams.noDictIDFlag;+ break;+ case ZSTD_c_forceMaxWindow :+ *value = CCtxParams->forceWindow;+ break;+ case ZSTD_c_forceAttachDict :+ *value = CCtxParams->attachDictPref;+ break;+ case ZSTD_c_literalCompressionMode :+ *value = CCtxParams->literalCompressionMode;+ break;+ case ZSTD_c_nbWorkers :+#ifndef ZSTD_MULTITHREAD+ assert(CCtxParams->nbWorkers == 0);+#endif+ *value = CCtxParams->nbWorkers;+ break;+ case ZSTD_c_jobSize :+#ifndef ZSTD_MULTITHREAD+ RETURN_ERROR(parameter_unsupported, "not compiled with multithreading");+#else+ assert(CCtxParams->jobSize <= INT_MAX);+ *value = (int)CCtxParams->jobSize;+ break;+#endif+ case ZSTD_c_overlapLog :+#ifndef ZSTD_MULTITHREAD+ RETURN_ERROR(parameter_unsupported, "not compiled with multithreading");+#else+ *value = CCtxParams->overlapLog;+ break;+#endif+ case ZSTD_c_rsyncable :+#ifndef ZSTD_MULTITHREAD+ RETURN_ERROR(parameter_unsupported, "not compiled with multithreading");+#else+ *value = CCtxParams->rsyncable;+ break;+#endif+ case ZSTD_c_enableLongDistanceMatching :+ *value = CCtxParams->ldmParams.enableLdm;+ break;+ case ZSTD_c_ldmHashLog :+ *value = CCtxParams->ldmParams.hashLog;+ break;+ case ZSTD_c_ldmMinMatch :+ *value = CCtxParams->ldmParams.minMatchLength;+ break;+ case ZSTD_c_ldmBucketSizeLog :+ *value = CCtxParams->ldmParams.bucketSizeLog;+ break;+ case ZSTD_c_ldmHashRateLog :+ *value = CCtxParams->ldmParams.hashRateLog;+ break;+ case ZSTD_c_targetCBlockSize :+ *value = (int)CCtxParams->targetCBlockSize;+ break;+ case ZSTD_c_srcSizeHint :+ *value = (int)CCtxParams->srcSizeHint;+ break;+ default: RETURN_ERROR(parameter_unsupported, "unknown parameter");+ }+ return 0;+}++/** ZSTD_CCtx_setParametersUsingCCtxParams() :+ * just applies `params` into `cctx`+ * no action is performed, parameters are merely stored.+ * If ZSTDMT is enabled, parameters are pushed to cctx->mtctx.+ * This is possible even if a compression is ongoing.+ * In which case, new parameters will be applied on the fly, starting with next compression job.+ */+size_t ZSTD_CCtx_setParametersUsingCCtxParams(+ ZSTD_CCtx* cctx, const ZSTD_CCtx_params* params)+{+ DEBUGLOG(4, "ZSTD_CCtx_setParametersUsingCCtxParams");+ RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong,+ "The context is in the wrong stage!");+ RETURN_ERROR_IF(cctx->cdict, stage_wrong,+ "Can't override parameters with cdict attached (some must "+ "be inherited from the cdict).");++ cctx->requestedParams = *params;+ return 0;+}++ZSTDLIB_API size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long long pledgedSrcSize)+{+ DEBUGLOG(4, "ZSTD_CCtx_setPledgedSrcSize to %u bytes", (U32)pledgedSrcSize);+ RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong,+ "Can't set pledgedSrcSize when not in init stage.");+ cctx->pledgedSrcSizePlusOne = pledgedSrcSize+1;+ return 0;+}++/**+ * 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.+ */+static size_t ZSTD_initLocalDict(ZSTD_CCtx* cctx)+{+ ZSTD_localDict* const dl = &cctx->localDict;+ ZSTD_compressionParameters const cParams = ZSTD_getCParamsFromCCtxParams(+ &cctx->requestedParams, ZSTD_CONTENTSIZE_UNKNOWN, dl->dictSize);+ if (dl->dict == NULL) {+ /* No local dictionary. */+ assert(dl->dictBuffer == NULL);+ assert(dl->cdict == NULL);+ assert(dl->dictSize == 0);+ return 0;+ }+ if (dl->cdict != NULL) {+ assert(cctx->cdict == dl->cdict);+ /* Local dictionary already initialized. */+ return 0;+ }+ assert(dl->dictSize > 0);+ assert(cctx->cdict == NULL);+ assert(cctx->prefixDict.dict == NULL);++ dl->cdict = ZSTD_createCDict_advanced(+ dl->dict,+ dl->dictSize,+ ZSTD_dlm_byRef,+ dl->dictContentType,+ cParams,+ cctx->customMem);+ RETURN_ERROR_IF(!dl->cdict, memory_allocation, "ZSTD_createCDict_advanced failed");+ cctx->cdict = dl->cdict;+ return 0;+}++size_t ZSTD_CCtx_loadDictionary_advanced(+ 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.");+ RETURN_ERROR_IF(cctx->staticSize, memory_allocation,+ "no malloc for static CCtx");+ 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 0;+ if (dictLoadMethod == ZSTD_dlm_byRef) {+ cctx->localDict.dict = dict;+ } else {+ void* dictBuffer = ZSTD_malloc(dictSize, cctx->customMem);+ RETURN_ERROR_IF(!dictBuffer, memory_allocation, "NULL pointer!");+ memcpy(dictBuffer, dict, dictSize);+ cctx->localDict.dictBuffer = dictBuffer;+ cctx->localDict.dict = dictBuffer;+ }+ cctx->localDict.dictSize = dictSize;+ cctx->localDict.dictContentType = dictContentType;+ return 0;+}++ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary_byReference(+ ZSTD_CCtx* cctx, const void* dict, size_t dictSize)+{+ return ZSTD_CCtx_loadDictionary_advanced(+ cctx, dict, dictSize, ZSTD_dlm_byRef, ZSTD_dct_auto);+}++ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize)+{+ return ZSTD_CCtx_loadDictionary_advanced(+ cctx, dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dct_auto);+}+++size_t ZSTD_CCtx_refCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict)+{+ RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong,+ "Can't ref a dict when ctx not in init stage.");+ /* Free the existing local cdict (if any) to save memory. */+ ZSTD_clearAllDicts(cctx);+ cctx->cdict = cdict;+ return 0;+}++size_t ZSTD_CCtx_refPrefix(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize)+{+ return ZSTD_CCtx_refPrefix_advanced(cctx, prefix, prefixSize, ZSTD_dct_rawContent);+}++size_t ZSTD_CCtx_refPrefix_advanced(+ ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType)+{+ RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong,+ "Can't ref a prefix when ctx not in init stage.");+ ZSTD_clearAllDicts(cctx);+ if (prefix != NULL && prefixSize > 0) {+ cctx->prefixDict.dict = prefix;+ cctx->prefixDict.dictSize = prefixSize;+ cctx->prefixDict.dictContentType = dictContentType;+ }+ return 0;+}++/*! ZSTD_CCtx_reset() :+ * Also dumps dictionary */+size_t ZSTD_CCtx_reset(ZSTD_CCtx* cctx, ZSTD_ResetDirective reset)+{+ if ( (reset == ZSTD_reset_session_only)+ || (reset == ZSTD_reset_session_and_parameters) ) {+ cctx->streamStage = zcss_init;+ cctx->pledgedSrcSizePlusOne = 0;+ }+ 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.");+ ZSTD_clearAllDicts(cctx);+ return ZSTD_CCtxParams_reset(&cctx->requestedParams);+ }+ return 0;+}+++/** ZSTD_checkCParams() :+ control CParam values remain within authorized range.+ @return : 0, or an error code if one value is beyond authorized range */+size_t ZSTD_checkCParams(ZSTD_compressionParameters cParams)+{+ BOUNDCHECK(ZSTD_c_windowLog, (int)cParams.windowLog);+ BOUNDCHECK(ZSTD_c_chainLog, (int)cParams.chainLog);+ BOUNDCHECK(ZSTD_c_hashLog, (int)cParams.hashLog);+ BOUNDCHECK(ZSTD_c_searchLog, (int)cParams.searchLog);+ BOUNDCHECK(ZSTD_c_minMatch, (int)cParams.minMatch);+ BOUNDCHECK(ZSTD_c_targetLength,(int)cParams.targetLength);+ BOUNDCHECK(ZSTD_c_strategy, cParams.strategy);+ return 0;+}++/** ZSTD_clampCParams() :+ * make CParam values within valid range.+ * @return : valid CParams */+static ZSTD_compressionParameters+ZSTD_clampCParams(ZSTD_compressionParameters cParams)+{+# define CLAMP_TYPE(cParam, val, type) { \+ ZSTD_bounds const bounds = ZSTD_cParam_getBounds(cParam); \+ if ((int)val<bounds.lowerBound) val=(type)bounds.lowerBound; \+ else if ((int)val>bounds.upperBound) val=(type)bounds.upperBound; \+ }+# define CLAMP(cParam, val) CLAMP_TYPE(cParam, val, unsigned)+ CLAMP(ZSTD_c_windowLog, cParams.windowLog);+ CLAMP(ZSTD_c_chainLog, cParams.chainLog);+ CLAMP(ZSTD_c_hashLog, cParams.hashLog);+ CLAMP(ZSTD_c_searchLog, cParams.searchLog);+ CLAMP(ZSTD_c_minMatch, cParams.minMatch);+ CLAMP(ZSTD_c_targetLength,cParams.targetLength);+ CLAMP_TYPE(ZSTD_c_strategy,cParams.strategy, ZSTD_strategy);+ return cParams;+}++/** ZSTD_cycleLog() :+ * condition for correct operation : hashLog > 1 */+U32 ZSTD_cycleLog(U32 hashLog, ZSTD_strategy strat)+{+ U32 const btScale = ((U32)strat >= (U32)ZSTD_btlazy2);+ return hashLog - btScale;+}++/** ZSTD_adjustCParams_internal() :+ * optimize `cPar` for a specified input (`srcSize` and `dictSize`).+ * mostly downsize to reduce memory consumption and initialization latency.+ * `srcSize` can be ZSTD_CONTENTSIZE_UNKNOWN when not known.+ * note : `srcSize==0` means 0!+ * condition : cPar is presumed validated (can be checked using ZSTD_checkCParams()). */+static ZSTD_compressionParameters+ZSTD_adjustCParams_internal(ZSTD_compressionParameters cPar,+ unsigned long long srcSize,+ size_t dictSize)+{+ static const U64 minSrcSize = 513; /* (1<<9) + 1 */+ static const U64 maxWindowResize = 1ULL << (ZSTD_WINDOWLOG_MAX-1);+ assert(ZSTD_checkCParams(cPar)==0);++ if (dictSize && srcSize == ZSTD_CONTENTSIZE_UNKNOWN)+ srcSize = minSrcSize;++ /* resize windowLog if input is small enough, to use less memory */+ 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 :+ ZSTD_highbit32(tSize-1) + 1;+ if (cPar.windowLog > srcLog) cPar.windowLog = srcLog;+ }+ if (cPar.hashLog > cPar.windowLog+1) cPar.hashLog = cPar.windowLog+1;+ { U32 const cycleLog = ZSTD_cycleLog(cPar.chainLog, cPar.strategy);+ if (cycleLog > cPar.windowLog)+ cPar.chainLog -= (cycleLog - cPar.windowLog);+ }++ if (cPar.windowLog < ZSTD_WINDOWLOG_ABSOLUTEMIN)+ cPar.windowLog = ZSTD_WINDOWLOG_ABSOLUTEMIN; /* minimum wlog required for valid frame header */++ return cPar;+}++ZSTD_compressionParameters+ZSTD_adjustCParams(ZSTD_compressionParameters cPar,+ unsigned long long srcSize,+ size_t dictSize)+{+ 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);+}++static ZSTD_compressionParameters ZSTD_getCParams_internal(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize);+static ZSTD_parameters ZSTD_getParams_internal(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize);++ZSTD_compressionParameters ZSTD_getCParamsFromCCtxParams(+ const ZSTD_CCtx_params* CCtxParams, U64 srcSizeHint, size_t dictSize)+{+ ZSTD_compressionParameters cParams;+ if (srcSizeHint == ZSTD_CONTENTSIZE_UNKNOWN && CCtxParams->srcSizeHint > 0) {+ srcSizeHint = CCtxParams->srcSizeHint;+ }+ cParams = ZSTD_getCParams_internal(CCtxParams->compressionLevel, srcSizeHint, dictSize);+ if (CCtxParams->ldmParams.enableLdm) cParams.windowLog = ZSTD_LDM_DEFAULT_WINDOW_LOG;+ if (CCtxParams->cParams.windowLog) cParams.windowLog = CCtxParams->cParams.windowLog;+ if (CCtxParams->cParams.hashLog) cParams.hashLog = CCtxParams->cParams.hashLog;+ if (CCtxParams->cParams.chainLog) cParams.chainLog = CCtxParams->cParams.chainLog;+ if (CCtxParams->cParams.searchLog) cParams.searchLog = CCtxParams->cParams.searchLog;+ if (CCtxParams->cParams.minMatch) cParams.minMatch = CCtxParams->cParams.minMatch;+ if (CCtxParams->cParams.targetLength) cParams.targetLength = CCtxParams->cParams.targetLength;+ if (CCtxParams->cParams.strategy) cParams.strategy = CCtxParams->cParams.strategy;+ assert(!ZSTD_checkCParams(cParams));+ /* srcSizeHint == 0 means 0 */+ return ZSTD_adjustCParams_internal(cParams, srcSizeHint, dictSize);+}++static size_t+ZSTD_sizeof_matchState(const ZSTD_compressionParameters* const cParams,+ const U32 forCCtx)+{+ size_t const chainSize = (cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cParams->chainLog);+ size_t const hSize = ((size_t)1) << cParams->hashLog;+ U32 const hashLog3 = (forCCtx && cParams->minMatch==3) ? MIN(ZSTD_HASHLOG3_MAX, cParams->windowLog) : 0;+ size_t const h3Size = hashLog3 ? ((size_t)1) << hashLog3 : 0;+ /* We don't use ZSTD_cwksp_alloc_size() here because the tables aren't+ * surrounded by redzones in ASAN. */+ size_t const tableSpace = chainSize * sizeof(U32)+ + hSize * sizeof(U32)+ + h3Size * sizeof(U32);+ size_t const optPotentialSpace =+ ZSTD_cwksp_alloc_size((MaxML+1) * sizeof(U32))+ + ZSTD_cwksp_alloc_size((MaxLL+1) * sizeof(U32))+ + ZSTD_cwksp_alloc_size((MaxOff+1) * sizeof(U32))+ + ZSTD_cwksp_alloc_size((1<<Litbits) * sizeof(U32))+ + ZSTD_cwksp_alloc_size((ZSTD_OPT_NUM+1) * sizeof(ZSTD_match_t))+ + ZSTD_cwksp_alloc_size((ZSTD_OPT_NUM+1) * sizeof(ZSTD_optimal_t));+ size_t const optSpace = (forCCtx && (cParams->strategy >= ZSTD_btopt))+ ? optPotentialSpace+ : 0;+ DEBUGLOG(4, "chainSize: %u - hSize: %u - h3Size: %u",+ (U32)chainSize, (U32)hSize, (U32)h3Size);+ return tableSpace + optSpace;+}++size_t ZSTD_estimateCCtxSize_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);+ size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, (size_t)1 << cParams.windowLog);+ U32 const divider = (cParams.minMatch==3) ? 3 : 4;+ size_t const maxNbSeq = blockSize / divider;+ size_t const tokenSpace = ZSTD_cwksp_alloc_size(WILDCOPY_OVERLENGTH + blockSize)+ + ZSTD_cwksp_alloc_size(maxNbSeq * sizeof(seqDef))+ + 3 * ZSTD_cwksp_alloc_size(maxNbSeq * sizeof(BYTE));+ size_t const entropySpace = ZSTD_cwksp_alloc_size(HUF_WORKSPACE_SIZE);+ size_t const blockStateSpace = 2 * ZSTD_cwksp_alloc_size(sizeof(ZSTD_compressedBlockState_t));+ size_t const matchStateSize = ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 1);++ size_t const ldmSpace = ZSTD_ldm_getTableSize(params->ldmParams);+ size_t const ldmSeqSpace = ZSTD_cwksp_alloc_size(ZSTD_ldm_getMaxNbSeq(params->ldmParams, blockSize) * sizeof(rawSeq));++ /* estimateCCtxSize is for one-shot compression. So no buffers should+ * be needed. However, we still allocate two 0-sized buffers, which can+ * take space under ASAN. */+ size_t const bufferSpace = ZSTD_cwksp_alloc_size(0)+ + ZSTD_cwksp_alloc_size(0);++ size_t const cctxSpace = ZSTD_cwksp_alloc_size(sizeof(ZSTD_CCtx));++ size_t const neededSpace =+ cctxSpace ++ entropySpace ++ blockStateSpace ++ ldmSpace ++ ldmSeqSpace ++ matchStateSize ++ tokenSpace ++ bufferSpace;++ DEBUGLOG(5, "estimate workspace : %u", (U32)neededSpace);+ return neededSpace;+ }+}++size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams)+{+ ZSTD_CCtx_params const params = ZSTD_makeCCtxParamsFromCParams(cParams);+ return ZSTD_estimateCCtxSize_usingCCtxParams(¶ms);+}++static size_t ZSTD_estimateCCtxSize_internal(int compressionLevel)+{+ ZSTD_compressionParameters const cParams = ZSTD_getCParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, 0);+ return ZSTD_estimateCCtxSize_usingCParams(cParams);+}++size_t ZSTD_estimateCCtxSize(int compressionLevel)+{+ int level;+ size_t memBudget = 0;+ for (level=MIN(compressionLevel, 1); level<=compressionLevel; level++) {+ size_t const newMB = ZSTD_estimateCCtxSize_internal(level);+ if (newMB > memBudget) memBudget = newMB;+ }+ return memBudget;+}++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);+ size_t const CCtxSize = ZSTD_estimateCCtxSize_usingCCtxParams(params);+ size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, (size_t)1 << cParams.windowLog);+ size_t const inBuffSize = ((size_t)1 << cParams.windowLog) + blockSize;+ size_t const outBuffSize = ZSTD_compressBound(blockSize) + 1;+ size_t const streamingSize = ZSTD_cwksp_alloc_size(inBuffSize)+ + ZSTD_cwksp_alloc_size(outBuffSize);++ return CCtxSize + streamingSize;+ }+}++size_t ZSTD_estimateCStreamSize_usingCParams(ZSTD_compressionParameters cParams)+{+ ZSTD_CCtx_params const params = ZSTD_makeCCtxParamsFromCParams(cParams);+ return ZSTD_estimateCStreamSize_usingCCtxParams(¶ms);+}++static size_t ZSTD_estimateCStreamSize_internal(int compressionLevel)+{+ ZSTD_compressionParameters const cParams = ZSTD_getCParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, 0);+ return ZSTD_estimateCStreamSize_usingCParams(cParams);+}++size_t ZSTD_estimateCStreamSize(int compressionLevel)+{+ int level;+ size_t memBudget = 0;+ for (level=MIN(compressionLevel, 1); level<=compressionLevel; level++) {+ size_t const newMB = ZSTD_estimateCStreamSize_internal(level);+ if (newMB > memBudget) memBudget = newMB;+ }+ return memBudget;+}++/* ZSTD_getFrameProgression():+ * tells how much data has been consumed (input) and produced (output) for current frame.+ * able to count progression inside worker threads (non-blocking mode).+ */+ZSTD_frameProgression ZSTD_getFrameProgression(const ZSTD_CCtx* cctx)+{+#ifdef ZSTD_MULTITHREAD+ if (cctx->appliedParams.nbWorkers > 0) {+ return ZSTDMT_getFrameProgression(cctx->mtctx);+ }+#endif+ { ZSTD_frameProgression fp;+ size_t const buffered = (cctx->inBuff == NULL) ? 0 :+ cctx->inBuffPos - cctx->inToCompress;+ if (buffered) assert(cctx->inBuffPos >= cctx->inToCompress);+ assert(buffered <= ZSTD_BLOCKSIZE_MAX);+ fp.ingested = cctx->consumedSrcSize + buffered;+ fp.consumed = cctx->consumedSrcSize;+ fp.produced = cctx->producedCSize;+ fp.flushed = cctx->producedCSize; /* simplified; some data might still be left within streaming output buffer */+ fp.currentJobID = 0;+ fp.nbActiveWorkers = 0;+ return fp;+} }++/*! ZSTD_toFlushNow()+ * Only useful for multithreading scenarios currently (nbWorkers >= 1).+ */+size_t ZSTD_toFlushNow(ZSTD_CCtx* cctx)+{+#ifdef ZSTD_MULTITHREAD+ if (cctx->appliedParams.nbWorkers > 0) {+ return ZSTDMT_toFlushNow(cctx->mtctx);+ }+#endif+ (void)cctx;+ return 0; /* over-simplification; could also check if context is currently running in streaming mode, and in which case, report how many bytes are left to be flushed within output buffer */+}++static void ZSTD_assertEqualCParams(ZSTD_compressionParameters cParams1,+ ZSTD_compressionParameters cParams2)+{+ (void)cParams1;+ (void)cParams2;+ assert(cParams1.windowLog == cParams2.windowLog);+ assert(cParams1.chainLog == cParams2.chainLog);+ assert(cParams1.hashLog == cParams2.hashLog);+ assert(cParams1.searchLog == cParams2.searchLog);+ assert(cParams1.minMatch == cParams2.minMatch);+ assert(cParams1.targetLength == cParams2.targetLength);+ assert(cParams1.strategy == cParams2.strategy);+}++void ZSTD_reset_compressedBlockState(ZSTD_compressedBlockState_t* bs)+{+ int i;+ for (i = 0; i < ZSTD_REP_NUM; ++i)+ bs->rep[i] = repStartValue[i];+ bs->entropy.huf.repeatMode = HUF_repeat_none;+ bs->entropy.fse.offcode_repeatMode = FSE_repeat_none;+ bs->entropy.fse.matchlength_repeatMode = FSE_repeat_none;+ bs->entropy.fse.litlength_repeatMode = FSE_repeat_none;+}++/*! ZSTD_invalidateMatchState()+ * Invalidate all the matches in the match finder tables.+ * Requires nextSrc and base to be set (can be NULL).+ */+static void ZSTD_invalidateMatchState(ZSTD_matchState_t* ms)+{+ ZSTD_window_clear(&ms->window);++ ms->nextToUpdate = ms->window.dictLimit;+ ms->loadedDictEnd = 0;+ ms->opt.litLengthSum = 0; /* force reset of btopt stats */+ ms->dictMatchState = NULL;+}++/**+ * Indicates whether this compression proceeds directly from user-provided+ * source buffer to user-provided destination buffer (ZSTDb_not_buffered), or+ * whether the context needs to buffer the input/output (ZSTDb_buffered).+ */+typedef enum {+ ZSTDb_not_buffered,+ ZSTDb_buffered+} ZSTD_buffered_policy_e;++/**+ * Controls, for this matchState reset, whether the tables need to be cleared /+ * prepared for the coming compression (ZSTDcrp_makeClean), or whether the+ * tables can be left unclean (ZSTDcrp_leaveDirty), because we know that a+ * subsequent operation will overwrite the table space anyways (e.g., copying+ * the matchState contents in from a CDict).+ */+typedef enum {+ ZSTDcrp_makeClean,+ ZSTDcrp_leaveDirty+} ZSTD_compResetPolicy_e;++/**+ * Controls, for this matchState reset, whether indexing can continue where it+ * left off (ZSTDirp_continue), or whether it needs to be restarted from zero+ * (ZSTDirp_reset).+ */+typedef enum {+ ZSTDirp_continue,+ ZSTDirp_reset+} ZSTD_indexResetPolicy_e;++typedef enum {+ ZSTD_resetTarget_CDict,+ ZSTD_resetTarget_CCtx+} ZSTD_resetTarget_e;++static size_t+ZSTD_reset_matchState(ZSTD_matchState_t* ms,+ ZSTD_cwksp* ws,+ const ZSTD_compressionParameters* cParams,+ const ZSTD_compResetPolicy_e crp,+ const ZSTD_indexResetPolicy_e forceResetIndex,+ const ZSTD_resetTarget_e forWho)+{+ size_t const chainSize = (cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cParams->chainLog);+ size_t const hSize = ((size_t)1) << cParams->hashLog;+ U32 const hashLog3 = ((forWho == ZSTD_resetTarget_CCtx) && cParams->minMatch==3) ? MIN(ZSTD_HASHLOG3_MAX, cParams->windowLog) : 0;+ size_t const h3Size = hashLog3 ? ((size_t)1) << hashLog3 : 0;++ DEBUGLOG(4, "reset indices : %u", forceResetIndex == ZSTDirp_reset);+ if (forceResetIndex == ZSTDirp_reset) {+ ZSTD_window_init(&ms->window);+ ZSTD_cwksp_mark_tables_dirty(ws);+ }++ ms->hashLog3 = hashLog3;++ ZSTD_invalidateMatchState(ms);++ assert(!ZSTD_cwksp_reserve_failed(ws)); /* check that allocation hasn't already failed */++ ZSTD_cwksp_clear_tables(ws);++ DEBUGLOG(5, "reserving table space");+ /* table Space */+ ms->hashTable = (U32*)ZSTD_cwksp_reserve_table(ws, hSize * sizeof(U32));+ ms->chainTable = (U32*)ZSTD_cwksp_reserve_table(ws, chainSize * sizeof(U32));+ ms->hashTable3 = (U32*)ZSTD_cwksp_reserve_table(ws, h3Size * sizeof(U32));+ RETURN_ERROR_IF(ZSTD_cwksp_reserve_failed(ws), memory_allocation,+ "failed a workspace allocation in ZSTD_reset_matchState");++ DEBUGLOG(4, "reset table : %u", crp!=ZSTDcrp_leaveDirty);+ if (crp!=ZSTDcrp_leaveDirty) {+ /* reset tables only */+ ZSTD_cwksp_clean_tables(ws);+ }++ /* opt parser space */+ if ((forWho == ZSTD_resetTarget_CCtx) && (cParams->strategy >= ZSTD_btopt)) {+ DEBUGLOG(4, "reserving optimal parser space");+ ms->opt.litFreq = (unsigned*)ZSTD_cwksp_reserve_aligned(ws, (1<<Litbits) * sizeof(unsigned));+ ms->opt.litLengthFreq = (unsigned*)ZSTD_cwksp_reserve_aligned(ws, (MaxLL+1) * sizeof(unsigned));+ ms->opt.matchLengthFreq = (unsigned*)ZSTD_cwksp_reserve_aligned(ws, (MaxML+1) * sizeof(unsigned));+ ms->opt.offCodeFreq = (unsigned*)ZSTD_cwksp_reserve_aligned(ws, (MaxOff+1) * sizeof(unsigned));+ ms->opt.matchTable = (ZSTD_match_t*)ZSTD_cwksp_reserve_aligned(ws, (ZSTD_OPT_NUM+1) * sizeof(ZSTD_match_t));+ ms->opt.priceTable = (ZSTD_optimal_t*)ZSTD_cwksp_reserve_aligned(ws, (ZSTD_OPT_NUM+1) * sizeof(ZSTD_optimal_t));+ }++ ms->cParams = *cParams;++ RETURN_ERROR_IF(ZSTD_cwksp_reserve_failed(ws), memory_allocation,+ "failed a workspace allocation in ZSTD_reset_matchState");++ return 0;+}++/* ZSTD_indexTooCloseToMax() :+ * minor optimization : prefer memset() rather than reduceIndex()+ * which is measurably slow in some circumstances (reported for Visual Studio).+ * Works when re-using a context for a lot of smallish inputs :+ * if all inputs are smaller than ZSTD_INDEXOVERFLOW_MARGIN,+ * memset() will be triggered before reduceIndex().+ */+#define ZSTD_INDEXOVERFLOW_MARGIN (16 MB)+static int ZSTD_indexTooCloseToMax(ZSTD_window_t w)+{+ return (size_t)(w.nextSrc - w.base) > (ZSTD_CURRENT_MAX - ZSTD_INDEXOVERFLOW_MARGIN);+}++/*! ZSTD_resetCCtx_internal() :+ note : `params` are assumed fully validated at this stage */+static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,+ ZSTD_CCtx_params params,+ U64 const pledgedSrcSize,+ ZSTD_compResetPolicy_e const crp,+ ZSTD_buffered_policy_e const zbuff)+{+ ZSTD_cwksp* const ws = &zc->workspace;+ DEBUGLOG(4, "ZSTD_resetCCtx_internal: pledgedSrcSize=%u, wlog=%u",+ (U32)pledgedSrcSize, params.cParams.windowLog);+ assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams)));++ zc->isFirstBlock = 1;++ if (params.ldmParams.enableLdm) {+ /* Adjust long distance matching parameters */+ ZSTD_ldm_adjustParameters(¶ms.ldmParams, ¶ms.cParams);+ assert(params.ldmParams.hashLog >= params.ldmParams.bucketSizeLog);+ assert(params.ldmParams.hashRateLog < 32);+ zc->ldmState.hashPower = ZSTD_rollingHash_primePower(params.ldmParams.minMatchLength);+ }++ { 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 tokenSpace = ZSTD_cwksp_alloc_size(WILDCOPY_OVERLENGTH + blockSize)+ + ZSTD_cwksp_alloc_size(maxNbSeq * sizeof(seqDef))+ + 3 * ZSTD_cwksp_alloc_size(maxNbSeq * sizeof(BYTE));+ size_t const buffOutSize = (zbuff==ZSTDb_buffered) ? ZSTD_compressBound(blockSize)+1 : 0;+ size_t const buffInSize = (zbuff==ZSTDb_buffered) ? windowSize + blockSize : 0;+ size_t const matchStateSize = ZSTD_sizeof_matchState(¶ms.cParams, /* forCCtx */ 1);+ size_t const maxNbLdmSeq = ZSTD_ldm_getMaxNbSeq(params.ldmParams, blockSize);++ ZSTD_indexResetPolicy_e needsIndexReset = zc->initialized ? ZSTDirp_continue : ZSTDirp_reset;++ if (ZSTD_indexTooCloseToMax(zc->blockState.matchState.window)) {+ needsIndexReset = ZSTDirp_reset;+ }++ if (!zc->staticSize) ZSTD_cwksp_bump_oversized_duration(ws, 0);++ /* Check if workspace is large enough, alloc a new one if needed */+ { size_t const cctxSpace = zc->staticSize ? ZSTD_cwksp_alloc_size(sizeof(ZSTD_CCtx)) : 0;+ size_t const entropySpace = ZSTD_cwksp_alloc_size(HUF_WORKSPACE_SIZE);+ size_t const blockStateSpace = 2 * ZSTD_cwksp_alloc_size(sizeof(ZSTD_compressedBlockState_t));+ size_t const bufferSpace = ZSTD_cwksp_alloc_size(buffInSize) + ZSTD_cwksp_alloc_size(buffOutSize);+ size_t const ldmSpace = ZSTD_ldm_getTableSize(params.ldmParams);+ size_t const ldmSeqSpace = ZSTD_cwksp_alloc_size(maxNbLdmSeq * sizeof(rawSeq));++ size_t const neededSpace =+ cctxSpace ++ entropySpace ++ blockStateSpace ++ ldmSpace ++ ldmSeqSpace ++ matchStateSize ++ tokenSpace ++ bufferSpace;++ int const workspaceTooSmall = ZSTD_cwksp_sizeof(ws) < neededSpace;+ int const workspaceWasteful = ZSTD_cwksp_check_wasteful(ws, neededSpace);++ DEBUGLOG(4, "Need %zuKB workspace, including %zuKB for match state, and %zuKB for buffers",+ neededSpace>>10, matchStateSize>>10, bufferSpace>>10);+ DEBUGLOG(4, "windowSize: %zu - blockSize: %zu", windowSize, blockSize);++ if (workspaceTooSmall || workspaceWasteful) {+ DEBUGLOG(4, "Resize workspaceSize from %zuKB to %zuKB",+ ZSTD_cwksp_sizeof(ws) >> 10,+ neededSpace >> 10);++ RETURN_ERROR_IF(zc->staticSize, memory_allocation, "static cctx : no resize");++ needsIndexReset = ZSTDirp_reset;++ ZSTD_cwksp_free(ws, zc->customMem);+ FORWARD_IF_ERROR(ZSTD_cwksp_create(ws, neededSpace, zc->customMem), "");++ DEBUGLOG(5, "reserving object space");+ /* Statically sized space.+ * entropyWorkspace never moves,+ * though prev/next block swap places */+ assert(ZSTD_cwksp_check_available(ws, 2 * sizeof(ZSTD_compressedBlockState_t)));+ zc->blockState.prevCBlock = (ZSTD_compressedBlockState_t*) ZSTD_cwksp_reserve_object(ws, sizeof(ZSTD_compressedBlockState_t));+ RETURN_ERROR_IF(zc->blockState.prevCBlock == NULL, memory_allocation, "couldn't allocate prevCBlock");+ zc->blockState.nextCBlock = (ZSTD_compressedBlockState_t*) ZSTD_cwksp_reserve_object(ws, sizeof(ZSTD_compressedBlockState_t));+ RETURN_ERROR_IF(zc->blockState.nextCBlock == NULL, memory_allocation, "couldn't allocate nextCBlock");+ zc->entropyWorkspace = (U32*) ZSTD_cwksp_reserve_object(ws, HUF_WORKSPACE_SIZE);+ RETURN_ERROR_IF(zc->blockState.nextCBlock == NULL, memory_allocation, "couldn't allocate entropyWorkspace");+ } }++ ZSTD_cwksp_clear(ws);++ /* init params */+ zc->appliedParams = params;+ zc->blockState.matchState.cParams = params.cParams;+ zc->pledgedSrcSizePlusOne = pledgedSrcSize+1;+ zc->consumedSrcSize = 0;+ zc->producedCSize = 0;+ if (pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN)+ zc->appliedParams.fParams.contentSizeFlag = 0;+ DEBUGLOG(4, "pledged content size : %u ; flag : %u",+ (unsigned)pledgedSrcSize, zc->appliedParams.fParams.contentSizeFlag);+ zc->blockSize = blockSize;++ XXH64_reset(&zc->xxhState, 0);+ zc->stage = ZSTDcs_init;+ zc->dictID = 0;++ ZSTD_reset_compressedBlockState(zc->blockState.prevCBlock);++ /* 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->inBuffSize = buffInSize;+ zc->inBuff = (char*)ZSTD_cwksp_reserve_buffer(ws, buffInSize);+ zc->outBuffSize = buffOutSize;+ zc->outBuff = (char*)ZSTD_cwksp_reserve_buffer(ws, buffOutSize);++ /* ldm bucketOffsets table */+ if (params.ldmParams.enableLdm) {+ /* TODO: avoid memset? */+ size_t const ldmBucketSize =+ ((size_t)1) << (params.ldmParams.hashLog -+ params.ldmParams.bucketSizeLog);+ zc->ldmState.bucketOffsets = ZSTD_cwksp_reserve_buffer(ws, ldmBucketSize);+ memset(zc->ldmState.bucketOffsets, 0, ldmBucketSize);+ }++ /* sequences storage */+ ZSTD_referenceExternalSequences(zc, NULL, 0);+ zc->seqStore.maxNbSeq = maxNbSeq;+ 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,+ crp,+ needsIndexReset,+ ZSTD_resetTarget_CCtx), "");++ /* ldm hash table */+ if (params.ldmParams.enableLdm) {+ /* 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));+ 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);+ ZSTD_window_clear(&zc->ldmState.window);+ zc->ldmState.loadedDictEnd = 0;+ }++ DEBUGLOG(3, "wksp: finished allocating, %zd bytes remain available", ZSTD_cwksp_available_space(ws));+ zc->initialized = 1;++ return 0;+ }+}++/* ZSTD_invalidateRepCodes() :+ * ensures next compression will not use repcodes from previous block.+ * Note : only works with regular variant;+ * do not use with extDict variant ! */+void ZSTD_invalidateRepCodes(ZSTD_CCtx* cctx) {+ int i;+ for (i=0; i<ZSTD_REP_NUM; i++) cctx->blockState.prevCBlock->rep[i] = 0;+ assert(!ZSTD_window_hasExtDict(cctx->blockState.matchState.window));+}++/* These are the approximate sizes for each strategy past which copying the+ * dictionary tables into the working context is faster than using them+ * in-place.+ */+static const size_t attachDictSizeCutoffs[ZSTD_STRATEGY_MAX+1] = {+ 8 KB, /* unused */+ 8 KB, /* ZSTD_fast */+ 16 KB, /* ZSTD_dfast */+ 32 KB, /* ZSTD_greedy */+ 32 KB, /* ZSTD_lazy */+ 32 KB, /* ZSTD_lazy2 */+ 32 KB, /* ZSTD_btlazy2 */+ 32 KB, /* ZSTD_btopt */+ 8 KB, /* ZSTD_btultra */+ 8 KB /* ZSTD_btultra2 */+};++static int ZSTD_shouldAttachDict(const ZSTD_CDict* cdict,+ const ZSTD_CCtx_params* params,+ U64 pledgedSrcSize)+{+ size_t cutoff = attachDictSizeCutoffs[cdict->matchState.cParams.strategy];+ return ( pledgedSrcSize <= cutoff+ || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN+ || params->attachDictPref == ZSTD_dictForceAttach )+ && params->attachDictPref != ZSTD_dictForceCopy+ && !params->forceWindow; /* dictMatchState isn't correctly+ * handled in _enforceMaxDist */+}++static size_t+ZSTD_resetCCtx_byAttachingCDict(ZSTD_CCtx* cctx,+ const ZSTD_CDict* cdict,+ ZSTD_CCtx_params params,+ U64 pledgedSrcSize,+ ZSTD_buffered_policy_e zbuff)+{+ { const ZSTD_compressionParameters* const cdict_cParams = &cdict->matchState.cParams;+ unsigned const windowLog = params.cParams.windowLog;+ assert(windowLog != 0);+ /* Resize working context table params for input only, since the dict+ * has its own tables. */+ /* pledgeSrcSize == 0 means 0! */+ params.cParams = ZSTD_adjustCParams_internal(*cdict_cParams, pledgedSrcSize, 0);+ params.cParams.windowLog = windowLog;+ FORWARD_IF_ERROR(ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize,+ ZSTDcrp_makeClean, zbuff), "");+ assert(cctx->appliedParams.cParams.strategy == cdict_cParams->strategy);+ }++ { const U32 cdictEnd = (U32)( cdict->matchState.window.nextSrc+ - cdict->matchState.window.base);+ const U32 cdictLen = cdictEnd - cdict->matchState.window.dictLimit;+ if (cdictLen == 0) {+ /* don't even attach dictionaries with no contents */+ DEBUGLOG(4, "skipping attaching empty dictionary");+ } else {+ DEBUGLOG(4, "attaching dictionary into context");+ cctx->blockState.matchState.dictMatchState = &cdict->matchState;++ /* prep working match state so dict matches never have negative indices+ * when they are translated to the working context's index space. */+ if (cctx->blockState.matchState.window.dictLimit < cdictEnd) {+ cctx->blockState.matchState.window.nextSrc =+ cctx->blockState.matchState.window.base + cdictEnd;+ ZSTD_window_clear(&cctx->blockState.matchState.window);+ }+ /* loadedDictEnd is expressed within the referential of the active context */+ cctx->blockState.matchState.loadedDictEnd = cctx->blockState.matchState.window.dictLimit;+ } }++ cctx->dictID = cdict->dictID;++ /* copy block state */+ memcpy(cctx->blockState.prevCBlock, &cdict->cBlockState, sizeof(cdict->cBlockState));++ return 0;+}++static size_t ZSTD_resetCCtx_byCopyingCDict(ZSTD_CCtx* cctx,+ const ZSTD_CDict* cdict,+ ZSTD_CCtx_params params,+ U64 pledgedSrcSize,+ ZSTD_buffered_policy_e zbuff)+{+ const ZSTD_compressionParameters *cdict_cParams = &cdict->matchState.cParams;++ DEBUGLOG(4, "copying dictionary into context");++ { unsigned const windowLog = params.cParams.windowLog;+ assert(windowLog != 0);+ /* Copy only compression parameters related to tables. */+ params.cParams = *cdict_cParams;+ params.cParams.windowLog = windowLog;+ FORWARD_IF_ERROR(ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize,+ ZSTDcrp_leaveDirty, zbuff), "");+ assert(cctx->appliedParams.cParams.strategy == cdict_cParams->strategy);+ assert(cctx->appliedParams.cParams.hashLog == cdict_cParams->hashLog);+ assert(cctx->appliedParams.cParams.chainLog == cdict_cParams->chainLog);+ }++ ZSTD_cwksp_mark_tables_dirty(&cctx->workspace);++ /* copy tables */+ { size_t const chainSize = (cdict_cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cdict_cParams->chainLog);+ size_t const hSize = (size_t)1 << cdict_cParams->hashLog;++ memcpy(cctx->blockState.matchState.hashTable,+ cdict->matchState.hashTable,+ hSize * sizeof(U32));+ memcpy(cctx->blockState.matchState.chainTable,+ cdict->matchState.chainTable,+ chainSize * sizeof(U32));+ }++ /* Zero the hashTable3, since the cdict never fills it */+ { int const h3log = cctx->blockState.matchState.hashLog3;+ size_t const h3Size = h3log ? ((size_t)1 << h3log) : 0;+ assert(cdict->matchState.hashLog3 == 0);+ memset(cctx->blockState.matchState.hashTable3, 0, h3Size * sizeof(U32));+ }++ ZSTD_cwksp_mark_tables_clean(&cctx->workspace);++ /* copy dictionary offsets */+ { ZSTD_matchState_t const* srcMatchState = &cdict->matchState;+ ZSTD_matchState_t* dstMatchState = &cctx->blockState.matchState;+ dstMatchState->window = srcMatchState->window;+ dstMatchState->nextToUpdate = srcMatchState->nextToUpdate;+ dstMatchState->loadedDictEnd= srcMatchState->loadedDictEnd;+ }++ cctx->dictID = cdict->dictID;++ /* copy block state */+ memcpy(cctx->blockState.prevCBlock, &cdict->cBlockState, sizeof(cdict->cBlockState));++ return 0;+}++/* We have a choice between copying the dictionary context into the working+ * context, or referencing the dictionary context from the working context+ * in-place. We decide here which strategy to use. */+static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx,+ const ZSTD_CDict* cdict,+ const ZSTD_CCtx_params* params,+ U64 pledgedSrcSize,+ ZSTD_buffered_policy_e zbuff)+{++ DEBUGLOG(4, "ZSTD_resetCCtx_usingCDict (pledgedSrcSize=%u)",+ (unsigned)pledgedSrcSize);++ if (ZSTD_shouldAttachDict(cdict, params, pledgedSrcSize)) {+ return ZSTD_resetCCtx_byAttachingCDict(+ cctx, cdict, *params, pledgedSrcSize, zbuff);+ } else {+ return ZSTD_resetCCtx_byCopyingCDict(+ cctx, cdict, *params, pledgedSrcSize, zbuff);+ }+}++/*! ZSTD_copyCCtx_internal() :+ * Duplicate an existing context `srcCCtx` into another one `dstCCtx`.+ * Only works during stage ZSTDcs_init (i.e. after creation, but before first call to ZSTD_compressContinue()).+ * The "context", in this case, refers to the hash and chain tables,+ * entropy tables, and dictionary references.+ * `windowLog` value is enforced if != 0, otherwise value is copied from srcCCtx.+ * @return : 0, or an error code */+static size_t ZSTD_copyCCtx_internal(ZSTD_CCtx* dstCCtx,+ const ZSTD_CCtx* srcCCtx,+ ZSTD_frameParameters fParams,+ U64 pledgedSrcSize,+ ZSTD_buffered_policy_e zbuff)+{+ DEBUGLOG(5, "ZSTD_copyCCtx_internal");+ RETURN_ERROR_IF(srcCCtx->stage!=ZSTDcs_init, stage_wrong,+ "Can't copy a ctx that's not in init stage.");++ memcpy(&dstCCtx->customMem, &srcCCtx->customMem, sizeof(ZSTD_customMem));+ { ZSTD_CCtx_params params = dstCCtx->requestedParams;+ /* Copy only compression parameters related to tables. */+ params.cParams = srcCCtx->appliedParams.cParams;+ params.fParams = fParams;+ ZSTD_resetCCtx_internal(dstCCtx, params, pledgedSrcSize,+ ZSTDcrp_leaveDirty, zbuff);+ assert(dstCCtx->appliedParams.cParams.windowLog == srcCCtx->appliedParams.cParams.windowLog);+ assert(dstCCtx->appliedParams.cParams.strategy == srcCCtx->appliedParams.cParams.strategy);+ assert(dstCCtx->appliedParams.cParams.hashLog == srcCCtx->appliedParams.cParams.hashLog);+ assert(dstCCtx->appliedParams.cParams.chainLog == srcCCtx->appliedParams.cParams.chainLog);+ assert(dstCCtx->blockState.matchState.hashLog3 == srcCCtx->blockState.matchState.hashLog3);+ }++ ZSTD_cwksp_mark_tables_dirty(&dstCCtx->workspace);++ /* copy tables */+ { size_t const chainSize = (srcCCtx->appliedParams.cParams.strategy == ZSTD_fast) ? 0 : ((size_t)1 << srcCCtx->appliedParams.cParams.chainLog);+ size_t const hSize = (size_t)1 << srcCCtx->appliedParams.cParams.hashLog;+ int const h3log = srcCCtx->blockState.matchState.hashLog3;+ size_t const h3Size = h3log ? ((size_t)1 << h3log) : 0;++ memcpy(dstCCtx->blockState.matchState.hashTable,+ srcCCtx->blockState.matchState.hashTable,+ hSize * sizeof(U32));+ memcpy(dstCCtx->blockState.matchState.chainTable,+ srcCCtx->blockState.matchState.chainTable,+ chainSize * sizeof(U32));+ memcpy(dstCCtx->blockState.matchState.hashTable3,+ srcCCtx->blockState.matchState.hashTable3,+ h3Size * sizeof(U32));+ }++ ZSTD_cwksp_mark_tables_clean(&dstCCtx->workspace);++ /* copy dictionary offsets */+ {+ const ZSTD_matchState_t* srcMatchState = &srcCCtx->blockState.matchState;+ ZSTD_matchState_t* dstMatchState = &dstCCtx->blockState.matchState;+ dstMatchState->window = srcMatchState->window;+ dstMatchState->nextToUpdate = srcMatchState->nextToUpdate;+ dstMatchState->loadedDictEnd= srcMatchState->loadedDictEnd;+ }+ dstCCtx->dictID = srcCCtx->dictID;++ /* copy block state */+ memcpy(dstCCtx->blockState.prevCBlock, srcCCtx->blockState.prevCBlock, sizeof(*srcCCtx->blockState.prevCBlock));++ return 0;+}++/*! ZSTD_copyCCtx() :+ * Duplicate an existing context `srcCCtx` into another one `dstCCtx`.+ * Only works during stage ZSTDcs_init (i.e. after creation, but before first call to ZSTD_compressContinue()).+ * pledgedSrcSize==0 means "unknown".+* @return : 0, or an error code */+size_t ZSTD_copyCCtx(ZSTD_CCtx* dstCCtx, const ZSTD_CCtx* srcCCtx, unsigned long long pledgedSrcSize)+{+ ZSTD_frameParameters fParams = { 1 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ };+ ZSTD_buffered_policy_e const zbuff = (ZSTD_buffered_policy_e)(srcCCtx->inBuffSize>0);+ ZSTD_STATIC_ASSERT((U32)ZSTDb_buffered==1);+ if (pledgedSrcSize==0) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN;+ fParams.contentSizeFlag = (pledgedSrcSize != ZSTD_CONTENTSIZE_UNKNOWN);++ return ZSTD_copyCCtx_internal(dstCCtx, srcCCtx,+ fParams, pledgedSrcSize,+ zbuff);+}+++#define ZSTD_ROWSIZE 16+/*! ZSTD_reduceTable() :+ * reduce table indexes by `reducerValue`, or squash to zero.+ * PreserveMark preserves "unsorted mark" for btlazy2 strategy.+ * It must be set to a clear 0/1 value, to remove branch during inlining.+ * Presume table size is a multiple of ZSTD_ROWSIZE+ * to help auto-vectorization */+FORCE_INLINE_TEMPLATE void+ZSTD_reduceTable_internal (U32* const table, U32 const size, U32 const reducerValue, int const preserveMark)+{+ int const nbRows = (int)size / ZSTD_ROWSIZE;+ int cellNb = 0;+ int rowNb;+ assert((size & (ZSTD_ROWSIZE-1)) == 0); /* multiple of ZSTD_ROWSIZE */+ assert(size < (1U<<31)); /* can be casted to int */++#if defined (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.+ *+ * This function however is intended to operate on those dirty tables and+ * re-clean them. So when this function is used correctly, we can unpoison+ * the memory it operated on. This introduces a blind spot though, since+ * if we now try to operate on __actually__ poisoned memory, we will not+ * detect that. */+ __msan_unpoison(table, size * sizeof(U32));+#endif++ for (rowNb=0 ; rowNb < nbRows ; rowNb++) {+ int column;+ for (column=0; column<ZSTD_ROWSIZE; column++) {+ if (preserveMark) {+ U32 const adder = (table[cellNb] == ZSTD_DUBT_UNSORTED_MARK) ? reducerValue : 0;+ table[cellNb] += adder;+ }+ if (table[cellNb] < reducerValue) table[cellNb] = 0;+ else table[cellNb] -= reducerValue;+ cellNb++;+ } }+}++static void ZSTD_reduceTable(U32* const table, U32 const size, U32 const reducerValue)+{+ ZSTD_reduceTable_internal(table, size, reducerValue, 0);+}++static void ZSTD_reduceTable_btlazy2(U32* const table, U32 const size, U32 const reducerValue)+{+ ZSTD_reduceTable_internal(table, size, reducerValue, 1);+}++/*! ZSTD_reduceIndex() :+* rescale all indexes to avoid future overflow (indexes are U32) */+static void ZSTD_reduceIndex (ZSTD_matchState_t* ms, ZSTD_CCtx_params const* params, const U32 reducerValue)+{+ { U32 const hSize = (U32)1 << params->cParams.hashLog;+ ZSTD_reduceTable(ms->hashTable, hSize, reducerValue);+ }++ if (params->cParams.strategy != ZSTD_fast) {+ U32 const chainSize = (U32)1 << params->cParams.chainLog;+ if (params->cParams.strategy == ZSTD_btlazy2)+ ZSTD_reduceTable_btlazy2(ms->chainTable, chainSize, reducerValue);+ else+ ZSTD_reduceTable(ms->chainTable, chainSize, reducerValue);+ }++ if (ms->hashLog3) {+ U32 const h3Size = (U32)1 << ms->hashLog3;+ ZSTD_reduceTable(ms->hashTable3, h3Size, reducerValue);+ }+}+++/*-*******************************************************+* Block entropic compression+*********************************************************/++/* See doc/zstd_compression_format.md for detailed format description */++void ZSTD_seqToCodes(const seqStore_t* seqStorePtr)+{+ const seqDef* const sequences = seqStorePtr->sequencesStart;+ BYTE* const llCodeTable = seqStorePtr->llCode;+ BYTE* const ofCodeTable = seqStorePtr->ofCode;+ BYTE* const mlCodeTable = seqStorePtr->mlCode;+ U32 const nbSeq = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart);+ U32 u;+ assert(nbSeq <= seqStorePtr->maxNbSeq);+ for (u=0; u<nbSeq; u++) {+ U32 const llv = sequences[u].litLength;+ U32 const mlv = sequences[u].matchLength;+ llCodeTable[u] = (BYTE)ZSTD_LLcode(llv);+ ofCodeTable[u] = (BYTE)ZSTD_highbit32(sequences[u].offset);+ mlCodeTable[u] = (BYTE)ZSTD_MLcode(mlv);+ }+ if (seqStorePtr->longLengthID==1)+ llCodeTable[seqStorePtr->longLengthPos] = MaxLL;+ if (seqStorePtr->longLengthID==2)+ mlCodeTable[seqStorePtr->longLengthPos] = MaxML;+}++/* ZSTD_useTargetCBlockSize():+ * Returns if target compressed block size param is being used.+ * If used, compression will do best effort to make a compressed block size to be around targetCBlockSize.+ * Returns 1 if true, 0 otherwise. */+static int ZSTD_useTargetCBlockSize(const ZSTD_CCtx_params* cctxParams)+{+ DEBUGLOG(5, "ZSTD_useTargetCBlockSize (targetCBlockSize=%zu)", cctxParams->targetCBlockSize);+ return (cctxParams->targetCBlockSize != 0);+}++/* ZSTD_compressSequences_internal():+ * actually compresses both literals and sequences */+MEM_STATIC size_t+ZSTD_compressSequences_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)+{+ const int longOffsets = cctxParams->cParams.windowLog > STREAM_ACCUMULATOR_MIN;+ ZSTD_strategy const strategy = cctxParams->cParams.strategy;+ unsigned count[MaxSeq+1];+ FSE_CTable* CTable_LitLength = nextEntropy->fse.litlengthCTable;+ FSE_CTable* CTable_OffsetBits = nextEntropy->fse.offcodeCTable;+ FSE_CTable* CTable_MatchLength = nextEntropy->fse.matchlengthCTable;+ U32 LLtype, Offtype, MLtype; /* compressed, raw or rle */+ const seqDef* const sequences = seqStorePtr->sequencesStart;+ const BYTE* const ofCodeTable = seqStorePtr->ofCode;+ const BYTE* const llCodeTable = seqStorePtr->llCode;+ const BYTE* const mlCodeTable = seqStorePtr->mlCode;+ BYTE* const ostart = (BYTE*)dst;+ BYTE* const oend = ostart + dstCapacity;+ BYTE* op = ostart;+ size_t const nbSeq = (size_t)(seqStorePtr->sequences - seqStorePtr->sequencesStart);+ BYTE* seqHead;+ BYTE* lastNCount = NULL;++ DEBUGLOG(5, "ZSTD_compressSequences_internal (nbSeq=%zu)", nbSeq);+ ZSTD_STATIC_ASSERT(HUF_WORKSPACE_SIZE >= (1<<MAX(MLFSELog,LLFSELog)));++ /* Compress literals */+ { const BYTE* const literals = seqStorePtr->litStart;+ size_t const litSize = (size_t)(seqStorePtr->lit - literals);+ size_t const cSize = ZSTD_compressLiterals(+ &prevEntropy->huf, &nextEntropy->huf,+ cctxParams->cParams.strategy,+ ZSTD_disableLiteralsCompression(cctxParams),+ op, dstCapacity,+ literals, litSize,+ entropyWorkspace, entropyWkspSize,+ bmi2);+ FORWARD_IF_ERROR(cSize, "ZSTD_compressLiterals failed");+ assert(cSize <= dstCapacity);+ op += cSize;+ }++ /* Sequences Header */+ RETURN_ERROR_IF((oend-op) < 3 /*max nbSeq Size*/ + 1 /*seqHead*/,+ dstSize_tooSmall, "Can't fit seq hdr in output buf!");+ if (nbSeq < 128) {+ *op++ = (BYTE)nbSeq;+ } else if (nbSeq < LONGNBSEQ) {+ op[0] = (BYTE)((nbSeq>>8) + 0x80);+ op[1] = (BYTE)nbSeq;+ op+=2;+ } else {+ op[0]=0xFF;+ MEM_writeLE16(op+1, (U16)(nbSeq - LONGNBSEQ));+ op+=3;+ }+ assert(op <= oend);+ if (nbSeq==0) {+ /* Copy the old tables over as if we repeated them */+ memcpy(&nextEntropy->fse, &prevEntropy->fse, sizeof(prevEntropy->fse));+ return (size_t)(op - ostart);+ }++ /* seqHead : flags for FSE encoding type */+ seqHead = op++;+ assert(op <= oend);++ /* convert length/distances into codes */+ ZSTD_seqToCodes(seqStorePtr);+ /* build CTable for Literal Lengths */+ { unsigned max = MaxLL;+ size_t const mostFrequent = HIST_countFast_wksp(count, &max, llCodeTable, nbSeq, entropyWorkspace, entropyWkspSize); /* can't fail */+ DEBUGLOG(5, "Building LL table");+ nextEntropy->fse.litlength_repeatMode = prevEntropy->fse.litlength_repeatMode;+ LLtype = ZSTD_selectEncodingType(&nextEntropy->fse.litlength_repeatMode,+ count, max, mostFrequent, nbSeq,+ LLFSELog, prevEntropy->fse.litlengthCTable,+ LL_defaultNorm, LL_defaultNormLog,+ ZSTD_defaultAllowed, strategy);+ assert(set_basic < set_compressed && set_rle < set_compressed);+ assert(!(LLtype < set_compressed && nextEntropy->fse.litlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */+ { size_t const countSize = ZSTD_buildCTable(+ op, (size_t)(oend - op),+ CTable_LitLength, LLFSELog, (symbolEncodingType_e)LLtype,+ count, max, llCodeTable, nbSeq,+ LL_defaultNorm, LL_defaultNormLog, MaxLL,+ prevEntropy->fse.litlengthCTable,+ sizeof(prevEntropy->fse.litlengthCTable),+ entropyWorkspace, entropyWkspSize);+ FORWARD_IF_ERROR(countSize, "ZSTD_buildCTable for LitLens failed");+ if (LLtype == set_compressed)+ lastNCount = op;+ op += countSize;+ assert(op <= oend);+ } }+ /* build CTable for Offsets */+ { unsigned max = MaxOff;+ size_t const mostFrequent = HIST_countFast_wksp(+ count, &max, ofCodeTable, nbSeq, entropyWorkspace, entropyWkspSize); /* can't fail */+ /* We can only use the basic table if max <= DefaultMaxOff, otherwise the offsets are too large */+ ZSTD_defaultPolicy_e const defaultPolicy = (max <= DefaultMaxOff) ? ZSTD_defaultAllowed : ZSTD_defaultDisallowed;+ DEBUGLOG(5, "Building OF table");+ nextEntropy->fse.offcode_repeatMode = prevEntropy->fse.offcode_repeatMode;+ Offtype = ZSTD_selectEncodingType(&nextEntropy->fse.offcode_repeatMode,+ count, max, mostFrequent, nbSeq,+ OffFSELog, prevEntropy->fse.offcodeCTable,+ OF_defaultNorm, OF_defaultNormLog,+ defaultPolicy, strategy);+ assert(!(Offtype < set_compressed && nextEntropy->fse.offcode_repeatMode != FSE_repeat_none)); /* We don't copy tables */+ { size_t const countSize = ZSTD_buildCTable(+ op, (size_t)(oend - op),+ CTable_OffsetBits, OffFSELog, (symbolEncodingType_e)Offtype,+ count, max, ofCodeTable, nbSeq,+ OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff,+ prevEntropy->fse.offcodeCTable,+ sizeof(prevEntropy->fse.offcodeCTable),+ entropyWorkspace, entropyWkspSize);+ FORWARD_IF_ERROR(countSize, "ZSTD_buildCTable for Offsets failed");+ if (Offtype == set_compressed)+ lastNCount = op;+ op += countSize;+ assert(op <= oend);+ } }+ /* build CTable for MatchLengths */+ { unsigned max = MaxML;+ size_t const mostFrequent = HIST_countFast_wksp(+ count, &max, mlCodeTable, nbSeq, entropyWorkspace, entropyWkspSize); /* can't fail */+ DEBUGLOG(5, "Building ML table (remaining space : %i)", (int)(oend-op));+ nextEntropy->fse.matchlength_repeatMode = prevEntropy->fse.matchlength_repeatMode;+ MLtype = ZSTD_selectEncodingType(&nextEntropy->fse.matchlength_repeatMode,+ count, max, mostFrequent, nbSeq,+ MLFSELog, prevEntropy->fse.matchlengthCTable,+ ML_defaultNorm, ML_defaultNormLog,+ ZSTD_defaultAllowed, strategy);+ assert(!(MLtype < set_compressed && nextEntropy->fse.matchlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */+ { size_t const countSize = ZSTD_buildCTable(+ op, (size_t)(oend - op),+ CTable_MatchLength, MLFSELog, (symbolEncodingType_e)MLtype,+ count, max, mlCodeTable, nbSeq,+ ML_defaultNorm, ML_defaultNormLog, MaxML,+ prevEntropy->fse.matchlengthCTable,+ sizeof(prevEntropy->fse.matchlengthCTable),+ entropyWorkspace, entropyWkspSize);+ FORWARD_IF_ERROR(countSize, "ZSTD_buildCTable for MatchLengths failed");+ if (MLtype == set_compressed)+ lastNCount = op;+ op += countSize;+ assert(op <= oend);+ } }++ *seqHead = (BYTE)((LLtype<<6) + (Offtype<<4) + (MLtype<<2));++ { size_t const bitstreamSize = ZSTD_encodeSequences(+ op, (size_t)(oend - op),+ CTable_MatchLength, mlCodeTable,+ CTable_OffsetBits, ofCodeTable,+ CTable_LitLength, llCodeTable,+ sequences, nbSeq,+ longOffsets, bmi2);+ FORWARD_IF_ERROR(bitstreamSize, "ZSTD_encodeSequences failed");+ op += bitstreamSize;+ assert(op <= oend);+ /* zstd versions <= 1.3.4 mistakenly report corruption when+ * FSE_readNCount() receives a buffer < 4 bytes.+ * Fixed by https://github.com/facebook/zstd/pull/1146.+ * This can happen when the last set_compressed table present is 2+ * bytes and the bitstream is only one byte.+ * In this exceedingly rare case, we will simply emit an uncompressed+ * block, since it isn't worth optimizing.+ */+ if (lastNCount && (op - lastNCount) < 4) {+ /* NCountSize >= 2 && bitstreamSize > 0 ==> lastCountSize == 3 */+ assert(op - lastNCount == 3);+ DEBUGLOG(5, "Avoiding bug in zstd decoder in versions <= 1.3.4 by "+ "emitting an uncompressed block.");+ return 0;+ }+ }++ DEBUGLOG(5, "compressed block size : %u", (unsigned)(op - ostart));+ return (size_t)(op - ostart);+}++MEM_STATIC size_t+ZSTD_compressSequences(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_compressSequences_internal(+ seqStorePtr, prevEntropy, nextEntropy, cctxParams,+ dst, dstCapacity,+ entropyWorkspace, entropyWkspSize, bmi2);+ if (cSize == 0) return 0;+ /* 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))+ return 0; /* block not compressed */+ FORWARD_IF_ERROR(cSize, "ZSTD_compressSequences_internal failed");++ /* Check compressibility */+ { size_t const maxCSize = srcSize - ZSTD_minGain(srcSize, cctxParams->cParams.strategy);+ if (cSize >= maxCSize) return 0; /* block not compressed */+ }++ return cSize;+}++/* ZSTD_selectBlockCompressor() :+ * Not static, but internal use only (used by long distance matcher)+ * assumption : strat is a valid strategy */+ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_dictMode_e dictMode)+{+ static const ZSTD_blockCompressor blockCompressor[3][ZSTD_STRATEGY_MAX+1] = {+ { ZSTD_compressBlock_fast /* default for 0 */,+ ZSTD_compressBlock_fast,+ ZSTD_compressBlock_doubleFast,+ ZSTD_compressBlock_greedy,+ ZSTD_compressBlock_lazy,+ ZSTD_compressBlock_lazy2,+ ZSTD_compressBlock_btlazy2,+ ZSTD_compressBlock_btopt,+ ZSTD_compressBlock_btultra,+ ZSTD_compressBlock_btultra2 },+ { ZSTD_compressBlock_fast_extDict /* default for 0 */,+ ZSTD_compressBlock_fast_extDict,+ ZSTD_compressBlock_doubleFast_extDict,+ ZSTD_compressBlock_greedy_extDict,+ ZSTD_compressBlock_lazy_extDict,+ ZSTD_compressBlock_lazy2_extDict,+ ZSTD_compressBlock_btlazy2_extDict,+ ZSTD_compressBlock_btopt_extDict,+ ZSTD_compressBlock_btultra_extDict,+ ZSTD_compressBlock_btultra_extDict },+ { ZSTD_compressBlock_fast_dictMatchState /* default for 0 */,+ ZSTD_compressBlock_fast_dictMatchState,+ ZSTD_compressBlock_doubleFast_dictMatchState,+ ZSTD_compressBlock_greedy_dictMatchState,+ ZSTD_compressBlock_lazy_dictMatchState,+ ZSTD_compressBlock_lazy2_dictMatchState,+ ZSTD_compressBlock_btlazy2_dictMatchState,+ ZSTD_compressBlock_btopt_dictMatchState,+ ZSTD_compressBlock_btultra_dictMatchState,+ ZSTD_compressBlock_btultra_dictMatchState }+ };+ ZSTD_blockCompressor selectedCompressor;+ ZSTD_STATIC_ASSERT((unsigned)ZSTD_fast == 1);++ assert(ZSTD_cParam_withinBounds(ZSTD_c_strategy, strat));+ selectedCompressor = blockCompressor[(int)dictMode][(int)strat];+ assert(selectedCompressor != NULL);+ return selectedCompressor;+}++static void ZSTD_storeLastLiterals(seqStore_t* seqStorePtr,+ const BYTE* anchor, size_t lastLLSize)+{+ memcpy(seqStorePtr->lit, anchor, lastLLSize);+ seqStorePtr->lit += lastLLSize;+}++void ZSTD_resetSeqStore(seqStore_t* ssPtr)+{+ ssPtr->lit = ssPtr->litStart;+ ssPtr->sequences = ssPtr->sequencesStart;+ ssPtr->longLengthID = 0;+}++typedef enum { ZSTDbss_compress, ZSTDbss_noCompress } ZSTD_buildSeqStore_e;++static size_t ZSTD_buildSeqStore(ZSTD_CCtx* zc, const void* src, size_t srcSize)+{+ ZSTD_matchState_t* const ms = &zc->blockState.matchState;+ DEBUGLOG(5, "ZSTD_buildSeqStore (srcSize=%zu)", 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) {+ ZSTD_ldm_skipSequences(&zc->externSeqStore, srcSize, zc->appliedParams.cParams.minMatch);+ return ZSTDbss_noCompress; /* don't even attempt compression below a certain srcSize */+ }+ ZSTD_resetSeqStore(&(zc->seqStore));+ /* required for optimal parser to read stats from dictionary */+ ms->opt.symbolCosts = &zc->blockState.prevCBlock->entropy;+ /* tell the optimal parser how we expect to compress literals */+ ms->opt.literalCompressionMode = zc->appliedParams.literalCompressionMode;+ /* a gap between an attached dict and the current window is not safe,+ * they must remain adjacent,+ * and when that stops being the case, the dict must be unset */+ assert(ms->dictMatchState == NULL || ms->loadedDictEnd == ms->window.dictLimit);++ /* limited update after a very long match */+ { const BYTE* const base = ms->window.base;+ const BYTE* const istart = (const BYTE*)src;+ const U32 current = (U32)(istart-base);+ if (sizeof(ptrdiff_t)==8) assert(istart - base < (ptrdiff_t)(U32)(-1)); /* ensure no overflow */+ if (current > ms->nextToUpdate + 384)+ ms->nextToUpdate = current - MIN(192, (U32)(current - ms->nextToUpdate - 384));+ }++ /* select and store sequences */+ { ZSTD_dictMode_e const dictMode = ZSTD_matchState_dictMode(ms);+ size_t lastLLSize;+ { int i;+ for (i = 0; i < ZSTD_REP_NUM; ++i)+ zc->blockState.nextCBlock->rep[i] = zc->blockState.prevCBlock->rep[i];+ }+ if (zc->externSeqStore.pos < zc->externSeqStore.size) {+ assert(!zc->appliedParams.ldmParams.enableLdm);+ /* Updates ldmSeqStore.pos */+ lastLLSize =+ ZSTD_ldm_blockCompress(&zc->externSeqStore,+ ms, &zc->seqStore,+ zc->blockState.nextCBlock->rep,+ src, srcSize);+ assert(zc->externSeqStore.pos <= zc->externSeqStore.size);+ } else if (zc->appliedParams.ldmParams.enableLdm) {+ rawSeqStore_t ldmSeqStore = {NULL, 0, 0, 0};++ ldmSeqStore.seq = zc->ldmSequences;+ ldmSeqStore.capacity = zc->maxNbLdmSequences;+ /* Updates ldmSeqStore.size */+ FORWARD_IF_ERROR(ZSTD_ldm_generateSequences(&zc->ldmState, &ldmSeqStore,+ &zc->appliedParams.ldmParams,+ src, srcSize), "");+ /* Updates ldmSeqStore.pos */+ lastLLSize =+ ZSTD_ldm_blockCompress(&ldmSeqStore,+ ms, &zc->seqStore,+ zc->blockState.nextCBlock->rep,+ src, srcSize);+ assert(ldmSeqStore.pos == ldmSeqStore.size);+ } else { /* not long range mode */+ ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, dictMode);+ lastLLSize = blockCompressor(ms, &zc->seqStore, zc->blockState.nextCBlock->rep, src, srcSize);+ }+ { const BYTE* const lastLiterals = (const BYTE*)src + srcSize - lastLLSize;+ ZSTD_storeLastLiterals(&zc->seqStore, lastLiterals, lastLLSize);+ } }+ return ZSTDbss_compress;+}++static void ZSTD_copyBlockSequences(ZSTD_CCtx* zc)+{+ const seqStore_t* seqStore = ZSTD_getSeqStore(zc);+ const seqDef* seqs = seqStore->sequencesStart;+ size_t seqsSize = seqStore->sequences - seqs;++ ZSTD_Sequence* outSeqs = &zc->seqCollector.seqStart[zc->seqCollector.seqIndex];+ size_t i; size_t position; int repIdx;++ assert(zc->seqCollector.seqIndex + 1 < zc->seqCollector.maxSequences);+ for (i = 0, position = 0; i < seqsSize; ++i) {+ outSeqs[i].offset = seqs[i].offset;+ outSeqs[i].litLength = seqs[i].litLength;+ outSeqs[i].matchLength = seqs[i].matchLength + MINMATCH;++ if (i == seqStore->longLengthPos) {+ if (seqStore->longLengthID == 1) {+ outSeqs[i].litLength += 0x10000;+ } else if (seqStore->longLengthID == 2) {+ outSeqs[i].matchLength += 0x10000;+ }+ }++ if (outSeqs[i].offset <= ZSTD_REP_NUM) {+ outSeqs[i].rep = outSeqs[i].offset;+ repIdx = (unsigned int)i - outSeqs[i].offset;++ if (outSeqs[i].litLength == 0) {+ if (outSeqs[i].offset < 3) {+ --repIdx;+ } else {+ repIdx = (unsigned int)i - 1;+ }+ ++outSeqs[i].rep;+ }+ assert(repIdx >= -3);+ outSeqs[i].offset = repIdx >= 0 ? outSeqs[repIdx].offset : repStartValue[-repIdx - 1];+ if (outSeqs[i].rep == 4) {+ --outSeqs[i].offset;+ }+ } else {+ outSeqs[i].offset -= ZSTD_REP_NUM;+ }++ position += outSeqs[i].litLength;+ outSeqs[i].matchPos = (unsigned int)position;+ position += outSeqs[i].matchLength;+ }+ zc->seqCollector.seqIndex += seqsSize;+}++size_t ZSTD_getSequences(ZSTD_CCtx* zc, ZSTD_Sequence* outSeqs,+ size_t outSeqsSize, const void* src, size_t srcSize)+{+ const size_t dstCapacity = ZSTD_compressBound(srcSize);+ void* dst = ZSTD_malloc(dstCapacity, ZSTD_defaultCMem);+ SeqCollector seqCollector;++ RETURN_ERROR_IF(dst == NULL, memory_allocation, "NULL pointer!");++ seqCollector.collectSequences = 1;+ seqCollector.seqStart = outSeqs;+ seqCollector.seqIndex = 0;+ seqCollector.maxSequences = outSeqsSize;+ zc->seqCollector = seqCollector;++ ZSTD_compress2(zc, dst, dstCapacity, src, srcSize);+ ZSTD_free(dst, ZSTD_defaultCMem);+ return zc->seqCollector.seqIndex;+}++/* Returns true if the given block is a RLE block */+static int ZSTD_isRLE(const BYTE *ip, size_t length) {+ size_t i;+ if (length < 2) return 1;+ for (i = 1; i < length; ++i) {+ if (ip[0] != ip[i]) return 0;+ }+ return 1;+}++/* Returns true if the given block may be RLE.+ * This is just a heuristic based on the compressibility.+ * It may return both false positives and false negatives.+ */+static int ZSTD_maybeRLE(seqStore_t const* seqStore)+{+ size_t const nbSeqs = (size_t)(seqStore->sequences - seqStore->sequencesStart);+ size_t const nbLits = (size_t)(seqStore->lit - seqStore->litStart);++ return nbSeqs < 4 && nbLits < 10;+}++static void ZSTD_confirmRepcodesAndEntropyTables(ZSTD_CCtx* zc)+{+ ZSTD_compressedBlockState_t* const tmp = zc->blockState.prevCBlock;+ zc->blockState.prevCBlock = zc->blockState.nextCBlock;+ zc->blockState.nextCBlock = tmp;+}++static size_t 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.+ */+ const U32 rleMaxLength = 25;+ size_t cSize;+ const BYTE* ip = (const BYTE*)src;+ BYTE* op = (BYTE*)dst;+ DEBUGLOG(5, "ZSTD_compressBlock_internal (dstCapacity=%u, dictLimit=%u, nextToUpdate=%u)",+ (unsigned)dstCapacity, (unsigned)zc->blockState.matchState.window.dictLimit,+ (unsigned)zc->blockState.matchState.nextToUpdate);++ { const size_t bss = ZSTD_buildSeqStore(zc, src, srcSize);+ FORWARD_IF_ERROR(bss, "ZSTD_buildSeqStore failed");+ if (bss == ZSTDbss_noCompress) { cSize = 0; goto out; }+ }++ if (zc->seqCollector.collectSequences) {+ ZSTD_copyBlockSequences(zc);+ return 0;+ }++ /* encode sequences and literals */+ cSize = ZSTD_compressSequences(&zc->seqStore,+ &zc->blockState.prevCBlock->entropy, &zc->blockState.nextCBlock->entropy,+ &zc->appliedParams,+ dst, dstCapacity,+ srcSize,+ zc->entropyWorkspace, HUF_WORKSPACE_SIZE /* statically allocated in resetCCtx */,+ zc->bmi2);++ if (frame &&+ /* 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+ */+ !zc->isFirstBlock &&+ cSize < rleMaxLength &&+ ZSTD_isRLE(ip, srcSize))+ {+ cSize = 1;+ op[0] = ip[0];+ }++out:+ if (!ZSTD_isError(cSize) && cSize > 1) {+ ZSTD_confirmRepcodesAndEntropyTables(zc);+ }+ /* We check that dictionaries have offset codes available for the first+ * block. After the first block, the offcode table might not have large+ * enough codes to represent the offsets in the data.+ */+ if (zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat_valid)+ zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode = FSE_repeat_check;++ return cSize;+}++static size_t ZSTD_compressBlock_targetCBlockSize_body(ZSTD_CCtx* zc,+ void* dst, size_t dstCapacity,+ const void* src, size_t srcSize,+ const size_t bss, U32 lastBlock)+{+ DEBUGLOG(6, "Attempting ZSTD_compressSuperBlock()");+ if (bss == ZSTDbss_compress) {+ if (/* 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+ */+ !zc->isFirstBlock &&+ ZSTD_maybeRLE(&zc->seqStore) &&+ ZSTD_isRLE((BYTE const*)src, srcSize))+ {+ return ZSTD_rleCompressBlock(dst, dstCapacity, *(BYTE const*)src, srcSize, lastBlock);+ }+ /* Attempt superblock compression.+ *+ * Note that compressed size of ZSTD_compressSuperBlock() is not bound by the+ * standard ZSTD_compressBound(). This is a problem, because even if we have+ * space now, taking an extra byte now could cause us to run out of space later+ * and violate ZSTD_compressBound().+ *+ * Define blockBound(blockSize) = blockSize + ZSTD_blockHeaderSize.+ *+ * In order to respect ZSTD_compressBound() we must attempt to emit a raw+ * uncompressed block in these cases:+ * * cSize == 0: Return code for an uncompressed block.+ * * cSize == dstSize_tooSmall: We may have expanded beyond blockBound(srcSize).+ * ZSTD_noCompressBlock() will return dstSize_tooSmall if we are really out of+ * output space.+ * * 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);+ if (cSize != ERROR(dstSize_tooSmall)) {+ 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_confirmRepcodesAndEntropyTables(zc);+ return cSize;+ }+ }+ }+ }++ DEBUGLOG(6, "Resorting to ZSTD_noCompressBlock()");+ /* Superblock compression failed, attempt to emit a single no compress block.+ * The decoder will be able to stream this block since it is uncompressed.+ */+ return ZSTD_noCompressBlock(dst, dstCapacity, src, srcSize, lastBlock);+}++static size_t ZSTD_compressBlock_targetCBlockSize(ZSTD_CCtx* zc,+ void* dst, size_t dstCapacity,+ const void* src, size_t srcSize,+ U32 lastBlock)+{+ size_t cSize = 0;+ const size_t bss = ZSTD_buildSeqStore(zc, src, srcSize);+ DEBUGLOG(5, "ZSTD_compressBlock_targetCBlockSize (dstCapacity=%u, dictLimit=%u, nextToUpdate=%u, srcSize=%zu)",+ (unsigned)dstCapacity, (unsigned)zc->blockState.matchState.window.dictLimit, (unsigned)zc->blockState.matchState.nextToUpdate, srcSize);+ FORWARD_IF_ERROR(bss, "ZSTD_buildSeqStore failed");++ cSize = ZSTD_compressBlock_targetCBlockSize_body(zc, dst, dstCapacity, src, srcSize, bss, lastBlock);+ FORWARD_IF_ERROR(cSize, "ZSTD_compressBlock_targetCBlockSize_body failed");++ if (zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat_valid)+ zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode = FSE_repeat_check;++ return cSize;+}++static void ZSTD_overflowCorrectIfNeeded(ZSTD_matchState_t* ms,+ ZSTD_cwksp* ws,+ ZSTD_CCtx_params const* params,+ void const* ip,+ void const* iend)+{+ if (ZSTD_window_needOverflowCorrection(ms->window, iend)) {+ U32 const maxDist = (U32)1 << params->cParams.windowLog;+ U32 const cycleLog = ZSTD_cycleLog(params->cParams.chainLog, params->cParams.strategy);+ U32 const correction = ZSTD_window_correctOverflow(&ms->window, cycleLog, maxDist, ip);+ ZSTD_STATIC_ASSERT(ZSTD_CHAINLOG_MAX <= 30);+ ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX_32 <= 30);+ ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX <= 31);+ ZSTD_cwksp_mark_tables_dirty(ws);+ ZSTD_reduceIndex(ms, params, correction);+ ZSTD_cwksp_mark_tables_clean(ws);+ if (ms->nextToUpdate < correction) ms->nextToUpdate = 0;+ else ms->nextToUpdate -= correction;+ /* invalidate dictionaries on overflow correction */+ ms->loadedDictEnd = 0;+ ms->dictMatchState = NULL;+ }+}++/*! ZSTD_compress_frameChunk() :+* Compress a chunk of data into one or multiple blocks.+* 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+*/+static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx,+ void* dst, size_t dstCapacity,+ const void* src, size_t srcSize,+ U32 lastFrameChunk)+{+ size_t blockSize = cctx->blockSize;+ size_t remaining = srcSize;+ const BYTE* ip = (const BYTE*)src;+ BYTE* const ostart = (BYTE*)dst;+ BYTE* op = ostart;+ U32 const maxDist = (U32)1 << cctx->appliedParams.cParams.windowLog;++ assert(cctx->appliedParams.cParams.windowLog <= ZSTD_WINDOWLOG_MAX);++ DEBUGLOG(5, "ZSTD_compress_frameChunk (blockSize=%u)", (unsigned)blockSize);+ if (cctx->appliedParams.fParams.checksumFlag && srcSize)+ XXH64_update(&cctx->xxhState, src, srcSize);++ while (remaining) {+ ZSTD_matchState_t* const ms = &cctx->blockState.matchState;+ U32 const lastBlock = lastFrameChunk & (blockSize >= remaining);++ RETURN_ERROR_IF(dstCapacity < ZSTD_blockHeaderSize + MIN_CBLOCK_SIZE,+ dstSize_tooSmall,+ "not enough space to store compressed block");+ if (remaining < blockSize) blockSize = remaining;++ ZSTD_overflowCorrectIfNeeded(+ ms, &cctx->workspace, &cctx->appliedParams, ip, ip + blockSize);+ ZSTD_checkDictValidity(&ms->window, ip + blockSize, maxDist, &ms->loadedDictEnd, &ms->dictMatchState);++ /* Ensure hash/chain table insertion resumes no sooner than lowlimit */+ if (ms->nextToUpdate < ms->window.lowLimit) ms->nextToUpdate = ms->window.lowLimit;++ { size_t cSize;+ if (ZSTD_useTargetCBlockSize(&cctx->appliedParams)) {+ cSize = ZSTD_compressBlock_targetCBlockSize(cctx, op, dstCapacity, ip, blockSize, lastBlock);+ FORWARD_IF_ERROR(cSize, "ZSTD_compressBlock_targetCBlockSize failed");+ assert(cSize > 0);+ assert(cSize <= blockSize + ZSTD_blockHeaderSize);+ } else {+ cSize = ZSTD_compressBlock_internal(cctx,+ op+ZSTD_blockHeaderSize, dstCapacity-ZSTD_blockHeaderSize,+ ip, blockSize, 1 /* frame */);+ FORWARD_IF_ERROR(cSize, "ZSTD_compressBlock_internal failed");++ if (cSize == 0) { /* block is not compressible */+ cSize = ZSTD_noCompressBlock(op, dstCapacity, ip, blockSize, lastBlock);+ FORWARD_IF_ERROR(cSize, "ZSTD_noCompressBlock failed");+ } else {+ U32 const cBlockHeader = cSize == 1 ?+ lastBlock + (((U32)bt_rle)<<1) + (U32)(blockSize << 3) :+ lastBlock + (((U32)bt_compressed)<<1) + (U32)(cSize << 3);+ MEM_writeLE24(op, cBlockHeader);+ cSize += ZSTD_blockHeaderSize;+ }+ }+++ ip += blockSize;+ assert(remaining >= blockSize);+ remaining -= blockSize;+ op += cSize;+ assert(dstCapacity >= cSize);+ dstCapacity -= cSize;+ cctx->isFirstBlock = 0;+ DEBUGLOG(5, "ZSTD_compress_frameChunk: adding a block of size %u",+ (unsigned)cSize);+ } }++ if (lastFrameChunk && (op>ostart)) cctx->stage = ZSTDcs_ending;+ return (size_t)(op-ostart);+}+++static size_t ZSTD_writeFrameHeader(void* dst, size_t dstCapacity,+ const ZSTD_CCtx_params* params, U64 pledgedSrcSize, U32 dictID)+{ BYTE* const op = (BYTE*)dst;+ U32 const dictIDSizeCodeLength = (dictID>0) + (dictID>=256) + (dictID>=65536); /* 0-3 */+ U32 const dictIDSizeCode = params->fParams.noDictIDFlag ? 0 : dictIDSizeCodeLength; /* 0-3 */+ U32 const checksumFlag = params->fParams.checksumFlag>0;+ U32 const windowSize = (U32)1 << params->cParams.windowLog;+ U32 const singleSegment = params->fParams.contentSizeFlag && (windowSize >= pledgedSrcSize);+ BYTE const windowLogByte = (BYTE)((params->cParams.windowLog - ZSTD_WINDOWLOG_ABSOLUTEMIN) << 3);+ U32 const fcsCode = params->fParams.contentSizeFlag ?+ (pledgedSrcSize>=256) + (pledgedSrcSize>=65536+256) + (pledgedSrcSize>=0xFFFFFFFFU) : 0; /* 0-3 */+ BYTE const frameHeaderDescriptionByte = (BYTE)(dictIDSizeCode + (checksumFlag<<2) + (singleSegment<<5) + (fcsCode<<6) );+ size_t pos=0;++ assert(!(params->fParams.contentSizeFlag && pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN));+ RETURN_ERROR_IF(dstCapacity < ZSTD_FRAMEHEADERSIZE_MAX, dstSize_tooSmall,+ "dst buf is too small to fit worst-case frame header size.");+ DEBUGLOG(4, "ZSTD_writeFrameHeader : dictIDFlag : %u ; dictID : %u ; dictIDSizeCode : %u",+ !params->fParams.noDictIDFlag, (unsigned)dictID, (unsigned)dictIDSizeCode);++ if (params->format == ZSTD_f_zstd1) {+ MEM_writeLE32(dst, ZSTD_MAGICNUMBER);+ pos = 4;+ }+ op[pos++] = frameHeaderDescriptionByte;+ if (!singleSegment) op[pos++] = windowLogByte;+ switch(dictIDSizeCode)+ {+ default: assert(0); /* impossible */+ case 0 : break;+ case 1 : op[pos] = (BYTE)(dictID); pos++; break;+ case 2 : MEM_writeLE16(op+pos, (U16)dictID); pos+=2; break;+ case 3 : MEM_writeLE32(op+pos, dictID); pos+=4; break;+ }+ switch(fcsCode)+ {+ default: assert(0); /* impossible */+ case 0 : if (singleSegment) op[pos++] = (BYTE)(pledgedSrcSize); break;+ case 1 : MEM_writeLE16(op+pos, (U16)(pledgedSrcSize-256)); pos+=2; break;+ case 2 : MEM_writeLE32(op+pos, (U32)(pledgedSrcSize)); pos+=4; break;+ case 3 : MEM_writeLE64(op+pos, (U64)(pledgedSrcSize)); pos+=8; break;+ }+ return pos;+}++/* ZSTD_writeLastEmptyBlock() :+ * output an empty Block with end-of-frame mark to complete a frame+ * @return : size of data written into `dst` (== ZSTD_blockHeaderSize (defined in zstd_internal.h))+ * or an error code if `dstCapacity` is too small (<ZSTD_blockHeaderSize)+ */+size_t ZSTD_writeLastEmptyBlock(void* dst, size_t dstCapacity)+{+ RETURN_ERROR_IF(dstCapacity < ZSTD_blockHeaderSize, dstSize_tooSmall,+ "dst buf is too small to write frame trailer empty block.");+ { U32 const cBlockHeader24 = 1 /*lastBlock*/ + (((U32)bt_raw)<<1); /* 0 size */+ MEM_writeLE24(dst, cBlockHeader24);+ return ZSTD_blockHeaderSize;+ }+}++size_t ZSTD_referenceExternalSequences(ZSTD_CCtx* cctx, rawSeq* seq, size_t nbSeq)+{+ RETURN_ERROR_IF(cctx->stage != ZSTDcs_init, stage_wrong,+ "wrong cctx stage");+ RETURN_ERROR_IF(cctx->appliedParams.ldmParams.enableLdm,+ parameter_unsupported,+ "incompatible with ldm");+ cctx->externSeqStore.seq = seq;+ cctx->externSeqStore.size = nbSeq;+ cctx->externSeqStore.capacity = nbSeq;+ cctx->externSeqStore.pos = 0;+ return 0;+}+++static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx,+ void* dst, size_t dstCapacity,+ const void* src, size_t srcSize,+ U32 frame, U32 lastFrameChunk)+{+ ZSTD_matchState_t* const ms = &cctx->blockState.matchState;+ size_t fhSize = 0;++ DEBUGLOG(5, "ZSTD_compressContinue_internal, stage: %u, srcSize: %u",+ cctx->stage, (unsigned)srcSize);+ RETURN_ERROR_IF(cctx->stage==ZSTDcs_created, stage_wrong,+ "missing init (ZSTD_compressBegin)");++ if (frame && (cctx->stage==ZSTDcs_init)) {+ fhSize = ZSTD_writeFrameHeader(dst, dstCapacity, &cctx->appliedParams,+ cctx->pledgedSrcSizePlusOne-1, cctx->dictID);+ FORWARD_IF_ERROR(fhSize, "ZSTD_writeFrameHeader failed");+ assert(fhSize <= dstCapacity);+ dstCapacity -= fhSize;+ dst = (char*)dst + fhSize;+ cctx->stage = ZSTDcs_ongoing;+ }++ if (!srcSize) return fhSize; /* do not generate an empty block if no input */++ if (!ZSTD_window_update(&ms->window, src, srcSize)) {+ ms->nextToUpdate = ms->window.dictLimit;+ }+ if (cctx->appliedParams.ldmParams.enableLdm) {+ ZSTD_window_update(&cctx->ldmState.window, src, srcSize);+ }++ if (!frame) {+ /* overflow check and correction for block mode */+ ZSTD_overflowCorrectIfNeeded(+ ms, &cctx->workspace, &cctx->appliedParams,+ src, (BYTE const*)src + srcSize);+ }++ DEBUGLOG(5, "ZSTD_compressContinue_internal (blockSize=%u)", (unsigned)cctx->blockSize);+ { size_t const cSize = frame ?+ ZSTD_compress_frameChunk (cctx, dst, dstCapacity, src, srcSize, lastFrameChunk) :+ ZSTD_compressBlock_internal (cctx, dst, dstCapacity, src, srcSize, 0 /* frame */);+ FORWARD_IF_ERROR(cSize, "%s", frame ? "ZSTD_compress_frameChunk failed" : "ZSTD_compressBlock_internal failed");+ cctx->consumedSrcSize += srcSize;+ cctx->producedCSize += (cSize + fhSize);+ assert(!(cctx->appliedParams.fParams.contentSizeFlag && cctx->pledgedSrcSizePlusOne == 0));+ if (cctx->pledgedSrcSizePlusOne != 0) { /* control src size */+ ZSTD_STATIC_ASSERT(ZSTD_CONTENTSIZE_UNKNOWN == (unsigned long long)-1);+ RETURN_ERROR_IF(+ cctx->consumedSrcSize+1 > cctx->pledgedSrcSizePlusOne,+ srcSize_wrong,+ "error : pledgedSrcSize = %u, while realSrcSize >= %u",+ (unsigned)cctx->pledgedSrcSizePlusOne-1,+ (unsigned)cctx->consumedSrcSize);+ }+ return cSize + fhSize;+ }+}++size_t ZSTD_compressContinue (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 */);+}+++size_t ZSTD_getBlockSize(const ZSTD_CCtx* cctx)+{+ ZSTD_compressionParameters const cParams = cctx->appliedParams.cParams;+ assert(!ZSTD_checkCParams(cParams));+ return MIN (ZSTD_BLOCKSIZE_MAX, (U32)1 << cParams.windowLog);+}++size_t ZSTD_compressBlock(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);+ 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 */);+}++/*! ZSTD_loadDictionaryContent() :+ * @return : 0, or an error code+ */+static size_t ZSTD_loadDictionaryContent(ZSTD_matchState_t* ms,+ ldmState_t* ls,+ ZSTD_cwksp* ws,+ ZSTD_CCtx_params const* params,+ const void* src, size_t srcSize,+ ZSTD_dictTableLoadMethod_e dtlm)+{+ const BYTE* ip = (const BYTE*) src;+ const BYTE* const iend = ip + srcSize;++ ZSTD_window_update(&ms->window, src, srcSize);+ ms->loadedDictEnd = params->forceWindow ? 0 : (U32)(iend - ms->window.base);++ if (params->ldmParams.enableLdm && ls != NULL) {+ ZSTD_window_update(&ls->window, src, srcSize);+ ls->loadedDictEnd = params->forceWindow ? 0 : (U32)(iend - ls->window.base);+ }++ /* Assert that we the ms params match the params we're being given */+ ZSTD_assertEqualCParams(params->cParams, ms->cParams);++ if (srcSize <= HASH_READ_SIZE) return 0;++ while (iend - ip > HASH_READ_SIZE) {+ size_t const remaining = (size_t)(iend - ip);+ size_t const chunk = MIN(remaining, ZSTD_CHUNKSIZE_MAX);+ const BYTE* const ichunk = ip + chunk;++ ZSTD_overflowCorrectIfNeeded(ms, ws, params, ip, ichunk);++ if (params->ldmParams.enableLdm && ls != NULL)+ ZSTD_ldm_fillHashTable(ls, (const BYTE*)src, (const BYTE*)src + srcSize, ¶ms->ldmParams);++ switch(params->cParams.strategy)+ {+ case ZSTD_fast:+ ZSTD_fillHashTable(ms, ichunk, dtlm);+ break;+ case ZSTD_dfast:+ ZSTD_fillDoubleHashTable(ms, ichunk, dtlm);+ break;++ case ZSTD_greedy:+ case ZSTD_lazy:+ case ZSTD_lazy2:+ if (chunk >= HASH_READ_SIZE)+ ZSTD_insertAndFindFirstIndex(ms, ichunk-HASH_READ_SIZE);+ break;++ case ZSTD_btlazy2: /* we want the dictionary table fully sorted */+ case ZSTD_btopt:+ case ZSTD_btultra:+ case ZSTD_btultra2:+ if (chunk >= HASH_READ_SIZE)+ ZSTD_updateTree(ms, ichunk-HASH_READ_SIZE, ichunk);+ break;++ default:+ assert(0); /* not possible : not a valid strategy id */+ }++ ip = ichunk;+ }++ ms->nextToUpdate = (U32)(iend - ms->window.base);+ return 0;+}+++/* Dictionaries that assign zero probability to symbols that show up causes problems+ when FSE encoding. Refuse dictionaries that assign zero probability to symbols+ that we may encounter during compression.+ NOTE: This behavior is not standard and could be improved in the future. */+static size_t ZSTD_checkDictNCount(short* normalizedCounter, unsigned dictMaxSymbolValue, unsigned maxSymbolValue) {+ U32 s;+ RETURN_ERROR_IF(dictMaxSymbolValue < maxSymbolValue, dictionary_corrupted, "dict fse tables don't have all symbols");+ for (s = 0; s <= maxSymbolValue; ++s) {+ RETURN_ERROR_IF(normalizedCounter[s] == 0, dictionary_corrupted, "dict fse tables don't have all symbols");+ }+ return 0;+}++size_t ZSTD_loadCEntropy(ZSTD_compressedBlockState_t* bs, void* workspace,+ short* offcodeNCount, unsigned* offcodeMaxValue,+ const void* const dict, size_t dictSize)+{+ const BYTE* dictPtr = (const BYTE*)dict; /* skip magic num and dict ID */+ const BYTE* const dictEnd = dictPtr + dictSize;+ dictPtr += 8;+ bs->entropy.huf.repeatMode = HUF_repeat_check;++ { unsigned maxSymbolValue = 255;+ unsigned hasZeroWeights = 1;+ size_t const hufHeaderSize = HUF_readCTable((HUF_CElt*)bs->entropy.huf.CTable, &maxSymbolValue, dictPtr,+ dictEnd-dictPtr, &hasZeroWeights);++ /* We only set the loaded table as valid if it contains all non-zero+ * weights. Otherwise, we set it to check */+ if (!hasZeroWeights)+ bs->entropy.huf.repeatMode = HUF_repeat_valid;++ RETURN_ERROR_IF(HUF_isError(hufHeaderSize), dictionary_corrupted, "");+ RETURN_ERROR_IF(maxSymbolValue < 255, dictionary_corrupted, "");+ dictPtr += hufHeaderSize;+ }++ { unsigned offcodeLog;+ size_t const offcodeHeaderSize = FSE_readNCount(offcodeNCount, offcodeMaxValue, &offcodeLog, dictPtr, dictEnd-dictPtr);+ RETURN_ERROR_IF(FSE_isError(offcodeHeaderSize), dictionary_corrupted, "");+ RETURN_ERROR_IF(offcodeLog > OffFSELog, dictionary_corrupted, "");+ /* Defer checking offcodeMaxValue because we need to know the size of the dictionary content */+ /* fill all offset symbols to avoid garbage at end of table */+ RETURN_ERROR_IF(FSE_isError(FSE_buildCTable_wksp(+ bs->entropy.fse.offcodeCTable,+ offcodeNCount, MaxOff, offcodeLog,+ workspace, HUF_WORKSPACE_SIZE)),+ dictionary_corrupted, "");+ dictPtr += offcodeHeaderSize;+ }++ { short matchlengthNCount[MaxML+1];+ unsigned matchlengthMaxValue = MaxML, matchlengthLog;+ size_t const matchlengthHeaderSize = FSE_readNCount(matchlengthNCount, &matchlengthMaxValue, &matchlengthLog, dictPtr, dictEnd-dictPtr);+ RETURN_ERROR_IF(FSE_isError(matchlengthHeaderSize), dictionary_corrupted, "");+ RETURN_ERROR_IF(matchlengthLog > MLFSELog, dictionary_corrupted, "");+ /* Every match length code must have non-zero probability */+ FORWARD_IF_ERROR( ZSTD_checkDictNCount(matchlengthNCount, matchlengthMaxValue, MaxML), "");+ RETURN_ERROR_IF(FSE_isError(FSE_buildCTable_wksp(+ bs->entropy.fse.matchlengthCTable,+ matchlengthNCount, matchlengthMaxValue, matchlengthLog,+ workspace, HUF_WORKSPACE_SIZE)),+ dictionary_corrupted, "");+ dictPtr += matchlengthHeaderSize;+ }++ { short litlengthNCount[MaxLL+1];+ unsigned litlengthMaxValue = MaxLL, litlengthLog;+ size_t const litlengthHeaderSize = FSE_readNCount(litlengthNCount, &litlengthMaxValue, &litlengthLog, dictPtr, dictEnd-dictPtr);+ RETURN_ERROR_IF(FSE_isError(litlengthHeaderSize), dictionary_corrupted, "");+ RETURN_ERROR_IF(litlengthLog > LLFSELog, dictionary_corrupted, "");+ /* Every literal length code must have non-zero probability */+ FORWARD_IF_ERROR( ZSTD_checkDictNCount(litlengthNCount, litlengthMaxValue, MaxLL), "");+ RETURN_ERROR_IF(FSE_isError(FSE_buildCTable_wksp(+ bs->entropy.fse.litlengthCTable,+ litlengthNCount, litlengthMaxValue, litlengthLog,+ workspace, HUF_WORKSPACE_SIZE)),+ dictionary_corrupted, "");+ dictPtr += litlengthHeaderSize;+ }++ RETURN_ERROR_IF(dictPtr+12 > dictEnd, dictionary_corrupted, "");+ bs->rep[0] = MEM_readLE32(dictPtr+0);+ bs->rep[1] = MEM_readLE32(dictPtr+4);+ bs->rep[2] = MEM_readLE32(dictPtr+8);+ dictPtr += 12;++ return dictPtr - (const BYTE*)dict;+}++/* Dictionary format :+ * See :+ * https://github.com/facebook/zstd/blob/master/doc/zstd_compression_format.md#dictionary-format+ */+/*! ZSTD_loadZstdDictionary() :+ * @return : dictID, or an error code+ * assumptions : magic number supposed already checked+ * dictSize supposed >= 8+ */+static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs,+ ZSTD_matchState_t* ms,+ ZSTD_cwksp* ws,+ ZSTD_CCtx_params const* params,+ const void* dict, size_t dictSize,+ ZSTD_dictTableLoadMethod_e dtlm,+ void* workspace)+{+ const BYTE* dictPtr = (const BYTE*)dict;+ const BYTE* const dictEnd = dictPtr + dictSize;+ short offcodeNCount[MaxOff+1];+ unsigned offcodeMaxValue = MaxOff;+ size_t dictID;+ size_t eSize;++ ZSTD_STATIC_ASSERT(HUF_WORKSPACE_SIZE >= (1<<MAX(MLFSELog,LLFSELog)));+ assert(dictSize >= 8);+ assert(MEM_readLE32(dictPtr) == ZSTD_MAGIC_DICTIONARY);++ dictID = params->fParams.noDictIDFlag ? 0 : MEM_readLE32(dictPtr + 4 /* skip magic number */ );+ eSize = ZSTD_loadCEntropy(bs, workspace, offcodeNCount, &offcodeMaxValue, dict, dictSize);+ FORWARD_IF_ERROR(eSize, "ZSTD_loadCEntropy failed");+ dictPtr += eSize;++ { size_t const dictContentSize = (size_t)(dictEnd - dictPtr);+ U32 offcodeMax = MaxOff;+ if (dictContentSize <= ((U32)-1) - 128 KB) {+ U32 const maxOffset = (U32)dictContentSize + 128 KB; /* The maximum offset that must be supported */+ offcodeMax = ZSTD_highbit32(maxOffset); /* Calculate minimum offset code required to represent maxOffset */+ }+ /* All offset values <= dictContentSize + 128 KB must be representable */+ FORWARD_IF_ERROR(ZSTD_checkDictNCount(offcodeNCount, offcodeMaxValue, MIN(offcodeMax, MaxOff)), "");+ /* All repCodes must be <= dictContentSize and != 0*/+ { U32 u;+ for (u=0; u<3; u++) {+ RETURN_ERROR_IF(bs->rep[u] == 0, dictionary_corrupted, "");+ RETURN_ERROR_IF(bs->rep[u] > dictContentSize, dictionary_corrupted, "");+ } }++ bs->entropy.fse.offcode_repeatMode = FSE_repeat_valid;+ bs->entropy.fse.matchlength_repeatMode = FSE_repeat_valid;+ bs->entropy.fse.litlength_repeatMode = FSE_repeat_valid;+ FORWARD_IF_ERROR(ZSTD_loadDictionaryContent(+ ms, NULL, ws, params, dictPtr, dictContentSize, dtlm), "");+ return dictID;+ }+}++/** ZSTD_compress_insertDictionary() :+* @return : dictID, or an error code */+static size_t+ZSTD_compress_insertDictionary(ZSTD_compressedBlockState_t* bs,+ ZSTD_matchState_t* ms,+ ldmState_t* ls,+ ZSTD_cwksp* ws,+ const ZSTD_CCtx_params* params,+ const void* dict, size_t dictSize,+ ZSTD_dictContentType_e dictContentType,+ ZSTD_dictTableLoadMethod_e dtlm,+ void* workspace)+{+ DEBUGLOG(4, "ZSTD_compress_insertDictionary (dictSize=%u)", (U32)dictSize);+ if ((dict==NULL) || (dictSize<8)) {+ RETURN_ERROR_IF(dictContentType == ZSTD_dct_fullDict, dictionary_wrong, "");+ return 0;+ }++ ZSTD_reset_compressedBlockState(bs);++ /* dict restricted modes */+ if (dictContentType == ZSTD_dct_rawContent)+ return ZSTD_loadDictionaryContent(ms, ls, ws, params, dict, dictSize, dtlm);++ 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);+ }+ RETURN_ERROR_IF(dictContentType == ZSTD_dct_fullDict, dictionary_wrong, "");+ assert(0); /* impossible */+ }++ /* dict as full zstd dictionary */+ return ZSTD_loadZstdDictionary(+ bs, ms, ws, params, dict, dictSize, dtlm, workspace);+}++#define ZSTD_USE_CDICT_PARAMS_SRCSIZE_CUTOFF (128 KB)+#define ZSTD_USE_CDICT_PARAMS_DICTSIZE_MULTIPLIER (6)++/*! ZSTD_compressBegin_internal() :+ * @return : 0, or an error code */+static size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx,+ const void* dict, size_t dictSize,+ ZSTD_dictContentType_e dictContentType,+ ZSTD_dictTableLoadMethod_e dtlm,+ const ZSTD_CDict* cdict,+ const ZSTD_CCtx_params* params, U64 pledgedSrcSize,+ ZSTD_buffered_policy_e zbuff)+{+ DEBUGLOG(4, "ZSTD_compressBegin_internal: wlog=%u", params->cParams.windowLog);+ /* params are supposed to be fully validated at this point */+ assert(!ZSTD_isError(ZSTD_checkCParams(params->cParams)));+ assert(!((dict) && (cdict))); /* either dict or cdict, not both */+ if ( (cdict)+ && (cdict->dictContentSize > 0)+ && ( pledgedSrcSize < ZSTD_USE_CDICT_PARAMS_SRCSIZE_CUTOFF+ || pledgedSrcSize < cdict->dictContentSize * ZSTD_USE_CDICT_PARAMS_DICTSIZE_MULTIPLIER+ || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN+ || cdict->compressionLevel == 0)+ && (params->attachDictPref != ZSTD_dictForceLoad) ) {+ return ZSTD_resetCCtx_usingCDict(cctx, cdict, params, pledgedSrcSize, zbuff);+ }++ FORWARD_IF_ERROR( ZSTD_resetCCtx_internal(cctx, *params, pledgedSrcSize,+ ZSTDcrp_makeClean, zbuff) , "");+ { size_t const dictID = cdict ?+ ZSTD_compress_insertDictionary(+ cctx->blockState.prevCBlock, &cctx->blockState.matchState,+ &cctx->ldmState, &cctx->workspace, &cctx->appliedParams, cdict->dictContent,+ cdict->dictContentSize, dictContentType, dtlm,+ cctx->entropyWorkspace)+ : ZSTD_compress_insertDictionary(+ cctx->blockState.prevCBlock, &cctx->blockState.matchState,+ &cctx->ldmState, &cctx->workspace, &cctx->appliedParams, dict, dictSize,+ dictContentType, dtlm, cctx->entropyWorkspace);+ FORWARD_IF_ERROR(dictID, "ZSTD_compress_insertDictionary failed");+ assert(dictID <= UINT_MAX);+ cctx->dictID = (U32)dictID;+ }+ return 0;+}++size_t ZSTD_compressBegin_advanced_internal(ZSTD_CCtx* cctx,+ const void* dict, size_t dictSize,+ ZSTD_dictContentType_e dictContentType,+ ZSTD_dictTableLoadMethod_e dtlm,+ const ZSTD_CDict* cdict,+ const ZSTD_CCtx_params* params,+ unsigned long long pledgedSrcSize)+{+ DEBUGLOG(4, "ZSTD_compressBegin_advanced_internal: wlog=%u", params->cParams.windowLog);+ /* compression parameters verification and optimization */+ FORWARD_IF_ERROR( ZSTD_checkCParams(params->cParams) , "");+ return ZSTD_compressBegin_internal(cctx,+ dict, dictSize, dictContentType, dtlm,+ cdict,+ params, pledgedSrcSize,+ ZSTDb_not_buffered);+}++/*! ZSTD_compressBegin_advanced() :+* @return : 0, or an error code */+size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx,+ const void* dict, size_t dictSize,+ ZSTD_parameters params, unsigned long long pledgedSrcSize)+{+ ZSTD_CCtx_params const cctxParams =+ ZSTD_assignParamsToCCtxParams(&cctx->requestedParams, ¶ms);+ return ZSTD_compressBegin_advanced_internal(cctx,+ dict, dictSize, ZSTD_dct_auto, ZSTD_dtlm_fast,+ NULL /*cdict*/,+ &cctxParams, pledgedSrcSize);+}++size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel)+{+ ZSTD_parameters const params = ZSTD_getParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize);+ ZSTD_CCtx_params const cctxParams =+ ZSTD_assignParamsToCCtxParams(&cctx->requestedParams, ¶ms);+ DEBUGLOG(4, "ZSTD_compressBegin_usingDict (dictSize=%u)", (unsigned)dictSize);+ return ZSTD_compressBegin_internal(cctx, dict, dictSize, ZSTD_dct_auto, ZSTD_dtlm_fast, NULL,+ &cctxParams, ZSTD_CONTENTSIZE_UNKNOWN, ZSTDb_not_buffered);+}++size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel)+{+ return ZSTD_compressBegin_usingDict(cctx, NULL, 0, compressionLevel);+}+++/*! ZSTD_writeEpilogue() :+* Ends a frame.+* @return : nb of bytes written into dst (or an error code) */+static size_t ZSTD_writeEpilogue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity)+{+ BYTE* const ostart = (BYTE*)dst;+ BYTE* op = ostart;+ size_t fhSize = 0;++ DEBUGLOG(4, "ZSTD_writeEpilogue");+ RETURN_ERROR_IF(cctx->stage == ZSTDcs_created, stage_wrong, "init missing");++ /* special case : empty frame */+ if (cctx->stage == ZSTDcs_init) {+ fhSize = ZSTD_writeFrameHeader(dst, dstCapacity, &cctx->appliedParams, 0, 0);+ FORWARD_IF_ERROR(fhSize, "ZSTD_writeFrameHeader failed");+ dstCapacity -= fhSize;+ op += fhSize;+ cctx->stage = ZSTDcs_ongoing;+ }++ if (cctx->stage != ZSTDcs_ending) {+ /* write one last empty block, make it the "last" block */+ U32 const cBlockHeader24 = 1 /* last block */ + (((U32)bt_raw)<<1) + 0;+ RETURN_ERROR_IF(dstCapacity<4, dstSize_tooSmall, "no room for epilogue");+ MEM_writeLE32(op, cBlockHeader24);+ op += ZSTD_blockHeaderSize;+ dstCapacity -= ZSTD_blockHeaderSize;+ }++ if (cctx->appliedParams.fParams.checksumFlag) {+ U32 const checksum = (U32) XXH64_digest(&cctx->xxhState);+ RETURN_ERROR_IF(dstCapacity<4, dstSize_tooSmall, "no room for checksum");+ DEBUGLOG(4, "ZSTD_writeEpilogue: write checksum : %08X", (unsigned)checksum);+ MEM_writeLE32(op, checksum);+ op += 4;+ }++ cctx->stage = ZSTDcs_created; /* return to "created but no init" status */+ return op-ostart;+}++size_t ZSTD_compressEnd (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,+ dst, dstCapacity, src, srcSize,+ 1 /* frame mode */, 1 /* last chunk */);+ FORWARD_IF_ERROR(cSize, "ZSTD_compressContinue_internal failed");+ endResult = ZSTD_writeEpilogue(cctx, (char*)dst + cSize, dstCapacity-cSize);+ FORWARD_IF_ERROR(endResult, "ZSTD_writeEpilogue failed");+ assert(!(cctx->appliedParams.fParams.contentSizeFlag && cctx->pledgedSrcSizePlusOne == 0));+ if (cctx->pledgedSrcSizePlusOne != 0) { /* control src size */+ ZSTD_STATIC_ASSERT(ZSTD_CONTENTSIZE_UNKNOWN == (unsigned long long)-1);+ DEBUGLOG(4, "end of frame : controlling src size");+ RETURN_ERROR_IF(+ cctx->pledgedSrcSizePlusOne != cctx->consumedSrcSize+1,+ srcSize_wrong,+ "error : pledgedSrcSize = %u, while realSrcSize = %u",+ (unsigned)cctx->pledgedSrcSizePlusOne-1,+ (unsigned)cctx->consumedSrcSize);+ }+ return cSize + endResult;+}+++static size_t ZSTD_compress_internal (ZSTD_CCtx* cctx,+ void* dst, size_t dstCapacity,+ const void* src, size_t srcSize,+ const void* dict,size_t dictSize,+ const ZSTD_parameters* params)+{+ ZSTD_CCtx_params const cctxParams =+ ZSTD_assignParamsToCCtxParams(&cctx->requestedParams, params);+ DEBUGLOG(4, "ZSTD_compress_internal");+ return ZSTD_compress_advanced_internal(cctx,+ dst, dstCapacity,+ src, srcSize,+ dict, dictSize,+ &cctxParams);+}++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)+{+ DEBUGLOG(4, "ZSTD_compress_advanced");+ FORWARD_IF_ERROR(ZSTD_checkCParams(params.cParams), "");+ return ZSTD_compress_internal(cctx,+ dst, dstCapacity,+ src, srcSize,+ dict, dictSize,+ ¶ms);+}++/* Internal */+size_t ZSTD_compress_advanced_internal(+ ZSTD_CCtx* cctx,+ void* dst, size_t dstCapacity,+ const void* src, size_t srcSize,+ const void* dict,size_t dictSize,+ const ZSTD_CCtx_params* params)+{+ DEBUGLOG(4, "ZSTD_compress_advanced_internal (srcSize:%u)", (unsigned)srcSize);+ 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);+}++size_t ZSTD_compress_usingDict(ZSTD_CCtx* cctx,+ void* dst, size_t dstCapacity,+ const void* src, size_t srcSize,+ const void* dict, size_t dictSize,+ int compressionLevel)+{+ ZSTD_parameters const params = ZSTD_getParams_internal(compressionLevel, srcSize, dict ? dictSize : 0);+ ZSTD_CCtx_params cctxParams = ZSTD_assignParamsToCCtxParams(&cctx->requestedParams, ¶ms);+ DEBUGLOG(4, "ZSTD_compress_usingDict (srcSize=%u)", (unsigned)srcSize);+ assert(params.fParams.contentSizeFlag == 1);+ return ZSTD_compress_advanced_internal(cctx, dst, dstCapacity, src, srcSize, dict, dictSize, &cctxParams);+}++size_t ZSTD_compressCCtx(ZSTD_CCtx* cctx,+ void* dst, size_t dstCapacity,+ const void* src, size_t srcSize,+ int compressionLevel)+{+ DEBUGLOG(4, "ZSTD_compressCCtx (srcSize=%u)", (unsigned)srcSize);+ assert(cctx != NULL);+ return ZSTD_compress_usingDict(cctx, dst, dstCapacity, src, srcSize, NULL, 0, compressionLevel);+}++size_t ZSTD_compress(void* dst, size_t dstCapacity,+ const void* src, size_t srcSize,+ int compressionLevel)+{+ size_t result;+ ZSTD_CCtx ctxBody;+ ZSTD_initCCtx(&ctxBody, ZSTD_defaultCMem);+ result = ZSTD_compressCCtx(&ctxBody, dst, dstCapacity, src, srcSize, compressionLevel);+ ZSTD_freeCCtxContent(&ctxBody); /* can't free ctxBody itself, as it's on stack; free only heap content */+ return result;+}+++/* ===== Dictionary API ===== */++/*! ZSTD_estimateCDictSize_advanced() :+ * Estimate amount of memory that will be needed to create a dictionary with following arguments */+size_t ZSTD_estimateCDictSize_advanced(+ size_t dictSize, ZSTD_compressionParameters cParams,+ ZSTD_dictLoadMethod_e dictLoadMethod)+{+ DEBUGLOG(5, "sizeof(ZSTD_CDict) : %u", (unsigned)sizeof(ZSTD_CDict));+ return ZSTD_cwksp_alloc_size(sizeof(ZSTD_CDict))+ + ZSTD_cwksp_alloc_size(HUF_WORKSPACE_SIZE)+ + ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 0)+ + (dictLoadMethod == ZSTD_dlm_byRef ? 0+ : ZSTD_cwksp_alloc_size(ZSTD_cwksp_align(dictSize, sizeof(void *))));+}++size_t ZSTD_estimateCDictSize(size_t dictSize, int compressionLevel)+{+ ZSTD_compressionParameters const cParams = ZSTD_getCParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize);+ return ZSTD_estimateCDictSize_advanced(dictSize, cParams, ZSTD_dlm_byCopy);+}++size_t ZSTD_sizeof_CDict(const ZSTD_CDict* cdict)+{+ if (cdict==NULL) return 0; /* support sizeof on NULL */+ DEBUGLOG(5, "sizeof(*cdict) : %u", (unsigned)sizeof(*cdict));+ /* cdict may be in the workspace */+ return (cdict->workspace.workspace == cdict ? 0 : sizeof(*cdict))+ + ZSTD_cwksp_sizeof(&cdict->workspace);+}++static size_t ZSTD_initCDict_internal(+ ZSTD_CDict* cdict,+ const void* dictBuffer, size_t dictSize,+ ZSTD_dictLoadMethod_e dictLoadMethod,+ ZSTD_dictContentType_e dictContentType,+ ZSTD_compressionParameters cParams)+{+ DEBUGLOG(3, "ZSTD_initCDict_internal (dictContentType:%u)", (unsigned)dictContentType);+ assert(!ZSTD_checkCParams(cParams));+ cdict->matchState.cParams = cParams;+ if ((dictLoadMethod == ZSTD_dlm_byRef) || (!dictBuffer) || (!dictSize)) {+ cdict->dictContent = dictBuffer;+ } else {+ void *internalBuffer = ZSTD_cwksp_reserve_object(&cdict->workspace, ZSTD_cwksp_align(dictSize, sizeof(void*)));+ RETURN_ERROR_IF(!internalBuffer, memory_allocation, "NULL pointer!");+ cdict->dictContent = internalBuffer;+ memcpy(internalBuffer, dictBuffer, dictSize);+ }+ cdict->dictContentSize = dictSize;++ cdict->entropyWorkspace = (U32*)ZSTD_cwksp_reserve_object(&cdict->workspace, HUF_WORKSPACE_SIZE);+++ /* Reset the state to no dictionary */+ ZSTD_reset_compressedBlockState(&cdict->cBlockState);+ FORWARD_IF_ERROR(ZSTD_reset_matchState(+ &cdict->matchState,+ &cdict->workspace,+ &cParams,+ ZSTDcrp_makeClean,+ ZSTDirp_reset,+ ZSTD_resetTarget_CDict), "");+ /* (Maybe) load the dictionary+ * Skips loading the dictionary if it is < 8 bytes.+ */+ { ZSTD_CCtx_params params;+ memset(¶ms, 0, sizeof(params));+ params.compressionLevel = ZSTD_CLEVEL_DEFAULT;+ params.fParams.contentSizeFlag = 1;+ params.cParams = cParams;+ { 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);+ FORWARD_IF_ERROR(dictID, "ZSTD_compress_insertDictionary failed");+ assert(dictID <= (size_t)(U32)-1);+ cdict->dictID = (U32)dictID;+ }+ }++ return 0;+}++ZSTD_CDict* ZSTD_createCDict_advanced(const void* dictBuffer, size_t dictSize,+ ZSTD_dictLoadMethod_e dictLoadMethod,+ ZSTD_dictContentType_e dictContentType,+ ZSTD_compressionParameters cParams, ZSTD_customMem customMem)+{+ DEBUGLOG(3, "ZSTD_createCDict_advanced, mode %u", (unsigned)dictContentType);+ if (!customMem.customAlloc ^ !customMem.customFree) return NULL;++ { size_t const workspaceSize =+ ZSTD_cwksp_alloc_size(sizeof(ZSTD_CDict)) ++ ZSTD_cwksp_alloc_size(HUF_WORKSPACE_SIZE) ++ ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 0) ++ (dictLoadMethod == ZSTD_dlm_byRef ? 0+ : ZSTD_cwksp_alloc_size(ZSTD_cwksp_align(dictSize, sizeof(void*))));+ void* const workspace = ZSTD_malloc(workspaceSize, customMem);+ ZSTD_cwksp ws;+ ZSTD_CDict* cdict;++ if (!workspace) {+ ZSTD_free(workspace, customMem);+ return NULL;+ }++ ZSTD_cwksp_init(&ws, workspace, workspaceSize);++ cdict = (ZSTD_CDict*)ZSTD_cwksp_reserve_object(&ws, sizeof(ZSTD_CDict));+ assert(cdict != NULL);+ ZSTD_cwksp_move(&cdict->workspace, &ws);+ cdict->customMem = customMem;+ cdict->compressionLevel = 0; /* signals advanced API usage */++ if (ZSTD_isError( ZSTD_initCDict_internal(cdict,+ dictBuffer, dictSize,+ dictLoadMethod, dictContentType,+ cParams) )) {+ ZSTD_freeCDict(cdict);+ return NULL;+ }++ return cdict;+ }+}++ZSTD_CDict* ZSTD_createCDict(const void* dict, size_t dictSize, int compressionLevel)+{+ ZSTD_compressionParameters cParams = ZSTD_getCParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize);+ ZSTD_CDict* cdict = ZSTD_createCDict_advanced(dict, dictSize,+ ZSTD_dlm_byCopy, ZSTD_dct_auto,+ cParams, ZSTD_defaultCMem);+ if (cdict)+ cdict->compressionLevel = compressionLevel == 0 ? ZSTD_CLEVEL_DEFAULT : compressionLevel;+ return cdict;+}++ZSTD_CDict* ZSTD_createCDict_byReference(const void* dict, size_t dictSize, int compressionLevel)+{+ ZSTD_compressionParameters cParams = ZSTD_getCParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize);+ return ZSTD_createCDict_advanced(dict, dictSize,+ ZSTD_dlm_byRef, ZSTD_dct_auto,+ cParams, ZSTD_defaultCMem);+}++size_t ZSTD_freeCDict(ZSTD_CDict* cdict)+{+ if (cdict==NULL) return 0; /* support free on NULL */+ { ZSTD_customMem const cMem = cdict->customMem;+ int cdictInWorkspace = ZSTD_cwksp_owns_buffer(&cdict->workspace, cdict);+ ZSTD_cwksp_free(&cdict->workspace, cMem);+ if (!cdictInWorkspace) {+ ZSTD_free(cdict, cMem);+ }+ return 0;+ }+}++/*! ZSTD_initStaticCDict_advanced() :+ * Generate a digested dictionary in provided memory area.+ * workspace: The memory area to emplace the dictionary into.+ * Provided pointer must 8-bytes aligned.+ * It must outlive dictionary usage.+ * workspaceSize: Use ZSTD_estimateCDictSize()+ * to determine how large workspace must be.+ * cParams : use ZSTD_getCParams() to transform a compression level+ * into its relevants cParams.+ * @return : pointer to ZSTD_CDict*, or NULL if error (size too small)+ * Note : there is no corresponding "free" function.+ * Since workspace was allocated externally, it must be freed externally.+ */+const ZSTD_CDict* ZSTD_initStaticCDict(+ void* workspace, size_t workspaceSize,+ const void* dict, size_t dictSize,+ ZSTD_dictLoadMethod_e dictLoadMethod,+ ZSTD_dictContentType_e dictContentType,+ ZSTD_compressionParameters cParams)+{+ size_t const matchStateSize = ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 0);+ size_t const neededSize = ZSTD_cwksp_alloc_size(sizeof(ZSTD_CDict))+ + (dictLoadMethod == ZSTD_dlm_byRef ? 0+ : ZSTD_cwksp_alloc_size(ZSTD_cwksp_align(dictSize, sizeof(void*))))+ + ZSTD_cwksp_alloc_size(HUF_WORKSPACE_SIZE)+ + matchStateSize;+ ZSTD_CDict* cdict;++ if ((size_t)workspace & 7) return NULL; /* 8-aligned */++ {+ ZSTD_cwksp ws;+ ZSTD_cwksp_init(&ws, workspace, workspaceSize);+ cdict = (ZSTD_CDict*)ZSTD_cwksp_reserve_object(&ws, sizeof(ZSTD_CDict));+ if (cdict == NULL) return NULL;+ ZSTD_cwksp_move(&cdict->workspace, &ws);+ }++ DEBUGLOG(4, "(workspaceSize < neededSize) : (%u < %u) => %u",+ (unsigned)workspaceSize, (unsigned)neededSize, (unsigned)(workspaceSize < neededSize));+ if (workspaceSize < neededSize) return NULL;++ if (ZSTD_isError( ZSTD_initCDict_internal(cdict,+ dict, dictSize,+ dictLoadMethod, dictContentType,+ cParams) ))+ return NULL;++ return cdict;+}++ZSTD_compressionParameters ZSTD_getCParamsFromCDict(const ZSTD_CDict* cdict)+{+ assert(cdict != NULL);+ return cdict->matchState.cParams;+}++/* ZSTD_compressBegin_usingCDict_advanced() :+ * cdict must be != NULL */+size_t ZSTD_compressBegin_usingCDict_advanced(+ ZSTD_CCtx* const cctx, const ZSTD_CDict* const cdict,+ ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize)+{+ DEBUGLOG(4, "ZSTD_compressBegin_usingCDict_advanced");+ RETURN_ERROR_IF(cdict==NULL, dictionary_wrong, "NULL pointer!");+ { ZSTD_CCtx_params params = cctx->requestedParams;+ params.cParams = ( pledgedSrcSize < ZSTD_USE_CDICT_PARAMS_SRCSIZE_CUTOFF+ || pledgedSrcSize < cdict->dictContentSize * ZSTD_USE_CDICT_PARAMS_DICTSIZE_MULTIPLIER+ || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN+ || cdict->compressionLevel == 0 )+ && (params.attachDictPref != ZSTD_dictForceLoad) ?+ ZSTD_getCParamsFromCDict(cdict)+ : ZSTD_getCParams(cdict->compressionLevel,+ pledgedSrcSize,+ cdict->dictContentSize);+ /* Increase window log to fit the entire dictionary and source if the+ * source size is known. Limit the increase to 19, which is the+ * window log for compression level 1 with the largest source size.+ */+ if (pledgedSrcSize != ZSTD_CONTENTSIZE_UNKNOWN) {+ U32 const limitedSrcSize = (U32)MIN(pledgedSrcSize, 1U << 19);+ U32 const limitedSrcLog = limitedSrcSize > 1 ? ZSTD_highbit32(limitedSrcSize - 1) + 1 : 1;+ params.cParams.windowLog = MAX(params.cParams.windowLog, limitedSrcLog);+ }+ params.fParams = fParams;+ return ZSTD_compressBegin_internal(cctx,+ NULL, 0, ZSTD_dct_auto, ZSTD_dtlm_fast,+ cdict,+ ¶ms, pledgedSrcSize,+ ZSTDb_not_buffered);+ }+}++/* ZSTD_compressBegin_usingCDict() :+ * pledgedSrcSize=0 means "unknown"+ * if pledgedSrcSize>0, it will enable contentSizeFlag */+size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict)+{+ ZSTD_frameParameters const fParams = { 0 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ };+ DEBUGLOG(4, "ZSTD_compressBegin_usingCDict : dictIDFlag == %u", !fParams.noDictIDFlag);+ return ZSTD_compressBegin_usingCDict_advanced(cctx, cdict, fParams, ZSTD_CONTENTSIZE_UNKNOWN);+}++size_t ZSTD_compress_usingCDict_advanced(ZSTD_CCtx* cctx,+ void* dst, size_t dstCapacity,+ const void* src, size_t srcSize,+ const ZSTD_CDict* cdict, ZSTD_frameParameters fParams)+{+ FORWARD_IF_ERROR(ZSTD_compressBegin_usingCDict_advanced(cctx, cdict, fParams, srcSize), ""); /* will check if cdict != NULL */+ return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize);+}++/*! ZSTD_compress_usingCDict() :+ * Compression using a digested Dictionary.+ * Faster startup than ZSTD_compress_usingDict(), recommended when same dictionary is used multiple times.+ * Note that compression parameters are decided at CDict creation time+ * while frame parameters are hardcoded */+size_t ZSTD_compress_usingCDict(ZSTD_CCtx* cctx,+ void* dst, size_t dstCapacity,+ const void* src, size_t srcSize,+ const ZSTD_CDict* cdict)+{+ ZSTD_frameParameters const fParams = { 1 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ };+ return ZSTD_compress_usingCDict_advanced(cctx, dst, dstCapacity, src, srcSize, cdict, fParams);+}++++/* ******************************************************************+* Streaming+********************************************************************/++ZSTD_CStream* ZSTD_createCStream(void)+{+ DEBUGLOG(3, "ZSTD_createCStream");+ return ZSTD_createCStream_advanced(ZSTD_defaultCMem);+}++ZSTD_CStream* ZSTD_initStaticCStream(void *workspace, size_t workspaceSize)+{+ return ZSTD_initStaticCCtx(workspace, workspaceSize);+}++ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem)+{ /* CStream and CCtx are now same object */+ return ZSTD_createCCtx_advanced(customMem);+}++size_t ZSTD_freeCStream(ZSTD_CStream* zcs)+{+ return ZSTD_freeCCtx(zcs); /* same object */+}++++/*====== Initialization ======*/++size_t ZSTD_CStreamInSize(void) { return ZSTD_BLOCKSIZE_MAX; }++size_t ZSTD_CStreamOutSize(void)+{+ return ZSTD_compressBound(ZSTD_BLOCKSIZE_MAX) + ZSTD_blockHeaderSize + 4 /* 32-bits hash */ ;+}++static size_t ZSTD_resetCStream_internal(ZSTD_CStream* cctx,+ const void* const dict, size_t const dictSize, ZSTD_dictContentType_e const dictContentType,+ const ZSTD_CDict* const cdict,+ ZSTD_CCtx_params params, unsigned long long const pledgedSrcSize)+{+ DEBUGLOG(4, "ZSTD_resetCStream_internal");+ /* Finalize the compression parameters */+ params.cParams = ZSTD_getCParamsFromCCtxParams(¶ms, pledgedSrcSize, dictSize);+ /* params are supposed to be fully validated at this point */+ assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams)));+ assert(!((dict) && (cdict))); /* either dict or cdict, not both */++ FORWARD_IF_ERROR( ZSTD_compressBegin_internal(cctx,+ dict, dictSize, dictContentType, ZSTD_dtlm_fast,+ cdict,+ ¶ms, pledgedSrcSize,+ ZSTDb_buffered) , "");++ cctx->inToCompress = 0;+ cctx->inBuffPos = 0;+ cctx->inBuffTarget = cctx->blockSize+ + (cctx->blockSize == pledgedSrcSize); /* for small input: avoid automatic flush on reaching end of block, since it would require to add a 3-bytes null block to end frame */+ cctx->outBuffContentSize = cctx->outBuffFlushedSize = 0;+ cctx->streamStage = zcss_load;+ cctx->frameEnded = 0;+ return 0; /* ready to go */+}++/* ZSTD_resetCStream():+ * pledgedSrcSize == 0 means "unknown" */+size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pss)+{+ /* temporary : 0 interpreted as "unknown" during transition period.+ * Users willing to specify "unknown" **must** use ZSTD_CONTENTSIZE_UNKNOWN.+ * 0 will be interpreted as "empty" in the future.+ */+ U64 const pledgedSrcSize = (pss==0) ? ZSTD_CONTENTSIZE_UNKNOWN : pss;+ DEBUGLOG(4, "ZSTD_resetCStream: pledgedSrcSize = %u", (unsigned)pledgedSrcSize);+ FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) , "");+ FORWARD_IF_ERROR( ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize) , "");+ return 0;+}++/*! ZSTD_initCStream_internal() :+ * Note : for lib/compress only. Used by zstdmt_compress.c.+ * Assumption 1 : params are valid+ * Assumption 2 : either dict, or cdict, is defined, not both */+size_t ZSTD_initCStream_internal(ZSTD_CStream* zcs,+ const void* dict, size_t dictSize, const ZSTD_CDict* cdict,+ const ZSTD_CCtx_params* params,+ unsigned long long pledgedSrcSize)+{+ DEBUGLOG(4, "ZSTD_initCStream_internal");+ FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) , "");+ FORWARD_IF_ERROR( ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize) , "");+ assert(!ZSTD_isError(ZSTD_checkCParams(params->cParams)));+ zcs->requestedParams = *params;+ assert(!((dict) && (cdict))); /* either dict or cdict, not both */+ if (dict) {+ FORWARD_IF_ERROR( ZSTD_CCtx_loadDictionary(zcs, dict, dictSize) , "");+ } else {+ /* Dictionary is cleared if !cdict */+ FORWARD_IF_ERROR( ZSTD_CCtx_refCDict(zcs, cdict) , "");+ }+ return 0;+}++/* ZSTD_initCStream_usingCDict_advanced() :+ * same as ZSTD_initCStream_usingCDict(), with control over frame parameters */+size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs,+ const ZSTD_CDict* cdict,+ ZSTD_frameParameters fParams,+ unsigned long long pledgedSrcSize)+{+ DEBUGLOG(4, "ZSTD_initCStream_usingCDict_advanced");+ FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) , "");+ FORWARD_IF_ERROR( ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize) , "");+ zcs->requestedParams.fParams = fParams;+ FORWARD_IF_ERROR( ZSTD_CCtx_refCDict(zcs, cdict) , "");+ return 0;+}++/* note : cdict must outlive compression session */+size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict)+{+ DEBUGLOG(4, "ZSTD_initCStream_usingCDict");+ FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) , "");+ FORWARD_IF_ERROR( ZSTD_CCtx_refCDict(zcs, cdict) , "");+ return 0;+}+++/* ZSTD_initCStream_advanced() :+ * pledgedSrcSize must be exact.+ * if srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN.+ * dict is loaded with default parameters ZSTD_dct_auto and ZSTD_dlm_byCopy. */+size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs,+ const void* dict, size_t dictSize,+ ZSTD_parameters params, unsigned long long pss)+{+ /* for compatibility with older programs relying on this behavior.+ * Users should now specify ZSTD_CONTENTSIZE_UNKNOWN.+ * This line will be removed in the future.+ */+ U64 const pledgedSrcSize = (pss==0 && params.fParams.contentSizeFlag==0) ? ZSTD_CONTENTSIZE_UNKNOWN : pss;+ DEBUGLOG(4, "ZSTD_initCStream_advanced");+ FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) , "");+ FORWARD_IF_ERROR( ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize) , "");+ FORWARD_IF_ERROR( ZSTD_checkCParams(params.cParams) , "");+ zcs->requestedParams = ZSTD_assignParamsToCCtxParams(&zcs->requestedParams, ¶ms);+ FORWARD_IF_ERROR( ZSTD_CCtx_loadDictionary(zcs, dict, dictSize) , "");+ return 0;+}++size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel)+{+ DEBUGLOG(4, "ZSTD_initCStream_usingDict");+ FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) , "");+ FORWARD_IF_ERROR( ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel) , "");+ FORWARD_IF_ERROR( ZSTD_CCtx_loadDictionary(zcs, dict, dictSize) , "");+ return 0;+}++size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pss)+{+ /* temporary : 0 interpreted as "unknown" during transition period.+ * Users willing to specify "unknown" **must** use ZSTD_CONTENTSIZE_UNKNOWN.+ * 0 will be interpreted as "empty" in the future.+ */+ U64 const pledgedSrcSize = (pss==0) ? ZSTD_CONTENTSIZE_UNKNOWN : pss;+ DEBUGLOG(4, "ZSTD_initCStream_srcSize");+ FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) , "");+ FORWARD_IF_ERROR( ZSTD_CCtx_refCDict(zcs, NULL) , "");+ FORWARD_IF_ERROR( ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel) , "");+ FORWARD_IF_ERROR( ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize) , "");+ return 0;+}++size_t ZSTD_initCStream(ZSTD_CStream* zcs, int compressionLevel)+{+ DEBUGLOG(4, "ZSTD_initCStream");+ FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) , "");+ FORWARD_IF_ERROR( ZSTD_CCtx_refCDict(zcs, NULL) , "");+ FORWARD_IF_ERROR( ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel) , "");+ return 0;+}++/*====== Compression ======*/++static size_t ZSTD_nextInputSizeHint(const ZSTD_CCtx* cctx)+{+ 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 */+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;+ U32 someMoreWork = 1;++ /* check expectations */+ DEBUGLOG(5, "ZSTD_compressStream_generic, flush=%u", (unsigned)flushMode);+ assert(zcs->inBuff != NULL);+ assert(zcs->inBuffSize > 0);+ assert(zcs->outBuff != NULL);+ assert(zcs->outBuffSize > 0);+ assert(output->pos <= output->size);+ assert(input->pos <= input->size);++ while (someMoreWork) {+ switch(zcs->streamStage)+ {+ case zcss_init:+ RETURN_ERROR(init_missing, "call ZSTD_initCStream() first!");++ case zcss_load:+ if ( (flushMode == ZSTD_e_end)+ && ((size_t)(oend-op) >= ZSTD_compressBound(iend-ip)) /* enough dstCapacity */+ && (zcs->inBuffPos == 0) ) {+ /* shortcut to compression pass directly into output buffer */+ size_t const cSize = ZSTD_compressEnd(zcs,+ op, oend-op, ip, iend-ip);+ DEBUGLOG(4, "ZSTD_compressEnd : cSize=%u", (unsigned)cSize);+ FORWARD_IF_ERROR(cSize, "ZSTD_compressEnd failed");+ ip = iend;+ op += cSize;+ zcs->frameEnded = 1;+ ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);+ someMoreWork = 0; break;+ }+ /* complete loading into inBuffer */+ { size_t const toLoad = zcs->inBuffTarget - zcs->inBuffPos;+ size_t const loaded = ZSTD_limitCopy(+ zcs->inBuff + zcs->inBuffPos, toLoad,+ ip, iend-ip);+ zcs->inBuffPos += loaded;+ if (loaded != 0)+ ip += loaded;+ if ( (flushMode == ZSTD_e_continue)+ && (zcs->inBuffPos < zcs->inBuffTarget) ) {+ /* not enough input to fill full block : stop here */+ someMoreWork = 0; break;+ }+ if ( (flushMode == ZSTD_e_flush)+ && (zcs->inBuffPos == zcs->inToCompress) ) {+ /* empty */+ someMoreWork = 0; break;+ }+ }+ /* compress current block (note : this stage cannot be stopped in the middle) */+ DEBUGLOG(5, "stream compression stage (flushMode==%u)", flushMode);+ { void* cDst;+ size_t cSize;+ size_t const iSize = zcs->inBuffPos - zcs->inToCompress;+ size_t oSize = oend-op;+ unsigned const lastBlock = (flushMode == ZSTD_e_end) && (ip==iend);+ if (oSize >= ZSTD_compressBound(iSize))+ cDst = op; /* compress into output buffer, to skip flush stage */+ else+ cDst = zcs->outBuff, oSize = zcs->outBuffSize;+ cSize = lastBlock ?+ ZSTD_compressEnd(zcs, cDst, oSize,+ zcs->inBuff + zcs->inToCompress, iSize) :+ ZSTD_compressContinue(zcs, cDst, oSize,+ zcs->inBuff + zcs->inToCompress, iSize);+ FORWARD_IF_ERROR(cSize, "%s", lastBlock ? "ZSTD_compressEnd failed" : "ZSTD_compressContinue failed");+ zcs->frameEnded = lastBlock;+ /* prepare next block */+ zcs->inBuffTarget = zcs->inBuffPos + zcs->blockSize;+ if (zcs->inBuffTarget > zcs->inBuffSize)+ zcs->inBuffPos = 0, zcs->inBuffTarget = zcs->blockSize;+ DEBUGLOG(5, "inBuffTarget:%u / inBuffSize:%u",+ (unsigned)zcs->inBuffTarget, (unsigned)zcs->inBuffSize);+ if (!lastBlock)+ assert(zcs->inBuffTarget <= zcs->inBuffSize);+ zcs->inToCompress = zcs->inBuffPos;+ if (cDst == op) { /* no need to flush */+ op += cSize;+ if (zcs->frameEnded) {+ DEBUGLOG(5, "Frame completed directly in outBuffer");+ someMoreWork = 0;+ ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);+ }+ break;+ }+ zcs->outBuffContentSize = cSize;+ zcs->outBuffFlushedSize = 0;+ zcs->streamStage = zcss_flush; /* pass-through to flush stage */+ }+ /* fall-through */+ case zcss_flush:+ DEBUGLOG(5, "flush stage");+ { size_t const toFlush = zcs->outBuffContentSize - zcs->outBuffFlushedSize;+ size_t const flushed = ZSTD_limitCopy(op, (size_t)(oend-op),+ zcs->outBuff + zcs->outBuffFlushedSize, toFlush);+ DEBUGLOG(5, "toFlush: %u into %u ==> flushed: %u",+ (unsigned)toFlush, (unsigned)(oend-op), (unsigned)flushed);+ if (flushed)+ op += flushed;+ zcs->outBuffFlushedSize += flushed;+ if (toFlush!=flushed) {+ /* flush not fully completed, presumably because dst is too small */+ assert(op==oend);+ someMoreWork = 0;+ break;+ }+ zcs->outBuffContentSize = zcs->outBuffFlushedSize = 0;+ if (zcs->frameEnded) {+ DEBUGLOG(5, "Frame completed on flush");+ someMoreWork = 0;+ ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);+ break;+ }+ zcs->streamStage = zcss_load;+ break;+ }++ default: /* impossible */+ assert(0);+ }+ }++ input->pos = ip - istart;+ output->pos = op - ostart;+ if (zcs->frameEnded) return 0;+ return ZSTD_nextInputSizeHint(zcs);+}++static size_t ZSTD_nextInputSizeHint_MTorST(const ZSTD_CCtx* cctx)+{+#ifdef ZSTD_MULTITHREAD+ if (cctx->appliedParams.nbWorkers >= 1) {+ assert(cctx->mtctx != NULL);+ return ZSTDMT_nextInputSizeHint(cctx->mtctx);+ }+#endif+ return ZSTD_nextInputSizeHint(cctx);++}++size_t ZSTD_compressStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input)+{+ FORWARD_IF_ERROR( ZSTD_compressStream2(zcs, output, input, ZSTD_e_continue) , "");+ return ZSTD_nextInputSizeHint_MTorST(zcs);+}+++size_t ZSTD_compressStream2( ZSTD_CCtx* cctx,+ ZSTD_outBuffer* output,+ ZSTD_inBuffer* input,+ ZSTD_EndDirective endOp)+{+ DEBUGLOG(5, "ZSTD_compressStream2, endOp=%u ", (unsigned)endOp);+ /* check conditions */+ RETURN_ERROR_IF(output->pos > output->size, GENERIC, "invalid buffer");+ RETURN_ERROR_IF(input->pos > input->size, GENERIC, "invalid buffer");+ assert(cctx!=NULL);++ /* transparent initialization stage */+ if (cctx->streamStage == zcss_init) {+ ZSTD_CCtx_params params = cctx->requestedParams;+ ZSTD_prefixDict const prefixDict = cctx->prefixDict;+ FORWARD_IF_ERROR( ZSTD_initLocalDict(cctx) , ""); /* Init the local dict if present. */+ memset(&cctx->prefixDict, 0, sizeof(cctx->prefixDict)); /* single usage */+ assert(prefixDict.dict==NULL || cctx->cdict==NULL); /* only one can be set */+ DEBUGLOG(4, "ZSTD_compressStream2 : transparent init stage");+ if (endOp == ZSTD_e_end) cctx->pledgedSrcSizePlusOne = input->size + 1; /* auto-fix pledgedSrcSize */+ params.cParams = ZSTD_getCParamsFromCCtxParams(+ &cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1, 0 /*dictSize*/);+++#ifdef ZSTD_MULTITHREAD+ if ((cctx->pledgedSrcSizePlusOne-1) <= ZSTDMT_JOBSIZE_MIN) {+ params.nbWorkers = 0; /* do not invoke multi-threading when src size is too small */+ }+ if (params.nbWorkers > 0) {+ /* mt context creation */+ if (cctx->mtctx == NULL) {+ DEBUGLOG(4, "ZSTD_compressStream2: creating new mtctx for nbWorkers=%u",+ params.nbWorkers);+ cctx->mtctx = ZSTDMT_createCCtx_advanced((U32)params.nbWorkers, cctx->customMem);+ RETURN_ERROR_IF(cctx->mtctx == NULL, memory_allocation, "NULL pointer!");+ }+ /* mt compression */+ DEBUGLOG(4, "call ZSTDMT_initCStream_internal as nbWorkers=%u", params.nbWorkers);+ FORWARD_IF_ERROR( ZSTDMT_initCStream_internal(+ cctx->mtctx,+ prefixDict.dict, prefixDict.dictSize, prefixDict.dictContentType,+ cctx->cdict, params, cctx->pledgedSrcSizePlusOne-1) , "");+ cctx->streamStage = zcss_load;+ cctx->appliedParams.nbWorkers = params.nbWorkers;+ } else+#endif+ { FORWARD_IF_ERROR( ZSTD_resetCStream_internal(cctx,+ prefixDict.dict, prefixDict.dictSize, prefixDict.dictContentType,+ cctx->cdict,+ params, cctx->pledgedSrcSizePlusOne-1) , "");+ assert(cctx->streamStage == zcss_load);+ assert(cctx->appliedParams.nbWorkers == 0);+ } }+ /* end of transparent initialization stage */++ /* compression stage */+#ifdef ZSTD_MULTITHREAD+ if (cctx->appliedParams.nbWorkers > 0) {+ int const forceMaxProgress = (endOp == ZSTD_e_flush || endOp == ZSTD_e_end);+ size_t flushMin;+ assert(forceMaxProgress || endOp == ZSTD_e_continue /* Protection for a new flush type */);+ if (cctx->cParamsChanged) {+ ZSTDMT_updateCParams_whileCompressing(cctx->mtctx, &cctx->requestedParams);+ cctx->cParamsChanged = 0;+ }+ do {+ flushMin = ZSTDMT_compressStream_generic(cctx->mtctx, output, input, endOp);+ if ( ZSTD_isError(flushMin)+ || (endOp == ZSTD_e_end && flushMin == 0) ) { /* compression completed */+ ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only);+ }+ FORWARD_IF_ERROR(flushMin, "ZSTDMT_compressStream_generic failed");+ } while (forceMaxProgress && flushMin != 0 && output->pos < output->size);+ DEBUGLOG(5, "completed ZSTD_compressStream2 delegating to ZSTDMT_compressStream_generic");+ /* Either we don't require maximum forward progress, we've finished the+ * flush, or we are out of output space.+ */+ assert(!forceMaxProgress || flushMin == 0 || output->pos == output->size);+ return flushMin;+ }+#endif+ FORWARD_IF_ERROR( ZSTD_compressStream_generic(cctx, output, input, endOp) , "");+ DEBUGLOG(5, "completed ZSTD_compressStream2");+ return cctx->outBuffContentSize - cctx->outBuffFlushedSize; /* remaining to flush */+}++size_t ZSTD_compressStream2_simpleArgs (+ ZSTD_CCtx* cctx,+ void* dst, size_t dstCapacity, size_t* dstPos,+ 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_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 ZSTD_compress2(ZSTD_CCtx* cctx,+ void* dst, size_t dstCapacity,+ const void* src, size_t srcSize)+{+ DEBUGLOG(4, "ZSTD_compress2 (srcSize=%u)", (unsigned)srcSize);+ ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only);+ { size_t oPos = 0;+ size_t iPos = 0;+ size_t const result = ZSTD_compressStream2_simpleArgs(cctx,+ dst, dstCapacity, &oPos,+ src, srcSize, &iPos,+ ZSTD_e_end);+ FORWARD_IF_ERROR(result, "ZSTD_compressStream2_simpleArgs failed");+ if (result != 0) { /* compression not completed, due to lack of output space */+ assert(oPos == dstCapacity);+ RETURN_ERROR(dstSize_tooSmall, "");+ }+ assert(iPos == srcSize); /* all input is expected consumed */+ return oPos;+ }+}++/*====== Finalize ======*/++/*! 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 };+ 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 };+ size_t const remainingToFlush = ZSTD_compressStream2(zcs, output, &input, ZSTD_e_end);+ FORWARD_IF_ERROR( remainingToFlush , "ZSTD_compressStream2 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;+ size_t const checksumSize = (size_t)(zcs->frameEnded ? 0 : zcs->appliedParams.fParams.checksumFlag * 4);+ size_t const toFlush = remainingToFlush + lastBlockSize + checksumSize;+ DEBUGLOG(4, "ZSTD_endStream : remaining to flush : %u", (unsigned)toFlush);+ return toFlush;+ }+}+++/*-===== Pre-defined compression levels =====-*/++#define ZSTD_MAX_CLEVEL 22+int ZSTD_maxCLevel(void) { return ZSTD_MAX_CLEVEL; }+int ZSTD_minCLevel(void) { return (int)-ZSTD_TARGETLENGTH_MAX; }++static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEVEL+1] = {+{ /* "default" - for any srcSize > 256 KB */+ /* W, C, H, S, L, TL, strat */+ { 19, 12, 13, 1, 6, 1, ZSTD_fast }, /* base for negative levels */+ { 19, 13, 14, 1, 7, 0, ZSTD_fast }, /* level 1 */+ { 20, 15, 16, 1, 6, 0, ZSTD_fast }, /* level 2 */+ { 21, 16, 17, 1, 5, 0, ZSTD_dfast }, /* level 3 */+ { 21, 18, 18, 1, 5, 0, ZSTD_dfast }, /* level 4 */+ { 21, 18, 19, 2, 5, 2, ZSTD_greedy }, /* level 5 */+ { 21, 19, 19, 3, 5, 4, ZSTD_greedy }, /* level 6 */+ { 21, 19, 19, 3, 5, 8, ZSTD_lazy }, /* level 7 */+ { 21, 19, 19, 3, 5, 16, ZSTD_lazy2 }, /* level 8 */+ { 21, 19, 20, 4, 5, 16, ZSTD_lazy2 }, /* level 9 */+ { 22, 20, 21, 4, 5, 16, ZSTD_lazy2 }, /* level 10 */+ { 22, 21, 22, 4, 5, 16, ZSTD_lazy2 }, /* level 11 */+ { 22, 21, 22, 5, 5, 16, ZSTD_lazy2 }, /* level 12 */+ { 22, 21, 22, 5, 5, 32, ZSTD_btlazy2 }, /* level 13 */+ { 22, 22, 23, 5, 5, 32, ZSTD_btlazy2 }, /* level 14 */+ { 22, 23, 23, 6, 5, 32, ZSTD_btlazy2 }, /* level 15 */+ { 22, 22, 22, 5, 5, 48, ZSTD_btopt }, /* level 16 */+ { 23, 23, 22, 5, 4, 64, ZSTD_btopt }, /* level 17 */+ { 23, 23, 22, 6, 3, 64, ZSTD_btultra }, /* level 18 */+ { 23, 24, 22, 7, 3,256, ZSTD_btultra2}, /* level 19 */+ { 25, 25, 23, 7, 3,256, ZSTD_btultra2}, /* level 20 */+ { 26, 26, 24, 7, 3,512, ZSTD_btultra2}, /* level 21 */+ { 27, 27, 25, 9, 3,999, ZSTD_btultra2}, /* level 22 */+},+{ /* for srcSize <= 256 KB */+ /* W, C, H, S, L, T, strat */+ { 18, 12, 13, 1, 5, 1, ZSTD_fast }, /* base for negative levels */+ { 18, 13, 14, 1, 6, 0, ZSTD_fast }, /* level 1 */+ { 18, 14, 14, 1, 5, 0, ZSTD_dfast }, /* level 2 */+ { 18, 16, 16, 1, 4, 0, ZSTD_dfast }, /* level 3 */+ { 18, 16, 17, 2, 5, 2, ZSTD_greedy }, /* level 4.*/+ { 18, 18, 18, 3, 5, 2, ZSTD_greedy }, /* level 5.*/+ { 18, 18, 19, 3, 5, 4, ZSTD_lazy }, /* level 6.*/+ { 18, 18, 19, 4, 4, 4, ZSTD_lazy }, /* level 7 */+ { 18, 18, 19, 4, 4, 8, ZSTD_lazy2 }, /* level 8 */+ { 18, 18, 19, 5, 4, 8, ZSTD_lazy2 }, /* level 9 */+ { 18, 18, 19, 6, 4, 8, ZSTD_lazy2 }, /* level 10 */+ { 18, 18, 19, 5, 4, 12, ZSTD_btlazy2 }, /* level 11.*/+ { 18, 19, 19, 7, 4, 12, ZSTD_btlazy2 }, /* level 12.*/+ { 18, 18, 19, 4, 4, 16, ZSTD_btopt }, /* level 13 */+ { 18, 18, 19, 4, 3, 32, ZSTD_btopt }, /* level 14.*/+ { 18, 18, 19, 6, 3,128, ZSTD_btopt }, /* level 15.*/+ { 18, 19, 19, 6, 3,128, ZSTD_btultra }, /* level 16.*/+ { 18, 19, 19, 8, 3,256, ZSTD_btultra }, /* level 17.*/+ { 18, 19, 19, 6, 3,128, ZSTD_btultra2}, /* level 18.*/+ { 18, 19, 19, 8, 3,256, ZSTD_btultra2}, /* level 19.*/+ { 18, 19, 19, 10, 3,512, ZSTD_btultra2}, /* level 20.*/+ { 18, 19, 19, 12, 3,512, ZSTD_btultra2}, /* level 21.*/+ { 18, 19, 19, 13, 3,999, ZSTD_btultra2}, /* level 22.*/+},+{ /* for srcSize <= 128 KB */+ /* W, C, H, S, L, T, strat */+ { 17, 12, 12, 1, 5, 1, ZSTD_fast }, /* base for negative levels */+ { 17, 12, 13, 1, 6, 0, ZSTD_fast }, /* level 1 */+ { 17, 13, 15, 1, 5, 0, ZSTD_fast }, /* level 2 */+ { 17, 15, 16, 2, 5, 0, ZSTD_dfast }, /* level 3 */+ { 17, 17, 17, 2, 4, 0, ZSTD_dfast }, /* level 4 */+ { 17, 16, 17, 3, 4, 2, ZSTD_greedy }, /* level 5 */+ { 17, 17, 17, 3, 4, 4, ZSTD_lazy }, /* level 6 */+ { 17, 17, 17, 3, 4, 8, ZSTD_lazy2 }, /* level 7 */+ { 17, 17, 17, 4, 4, 8, ZSTD_lazy2 }, /* level 8 */+ { 17, 17, 17, 5, 4, 8, ZSTD_lazy2 }, /* level 9 */+ { 17, 17, 17, 6, 4, 8, ZSTD_lazy2 }, /* level 10 */+ { 17, 17, 17, 5, 4, 8, ZSTD_btlazy2 }, /* level 11 */+ { 17, 18, 17, 7, 4, 12, ZSTD_btlazy2 }, /* level 12 */+ { 17, 18, 17, 3, 4, 12, ZSTD_btopt }, /* level 13.*/+ { 17, 18, 17, 4, 3, 32, ZSTD_btopt }, /* level 14.*/+ { 17, 18, 17, 6, 3,256, ZSTD_btopt }, /* level 15.*/+ { 17, 18, 17, 6, 3,128, ZSTD_btultra }, /* level 16.*/+ { 17, 18, 17, 8, 3,256, ZSTD_btultra }, /* level 17.*/+ { 17, 18, 17, 10, 3,512, ZSTD_btultra }, /* level 18.*/+ { 17, 18, 17, 5, 3,256, ZSTD_btultra2}, /* level 19.*/+ { 17, 18, 17, 7, 3,512, ZSTD_btultra2}, /* level 20.*/+ { 17, 18, 17, 9, 3,512, ZSTD_btultra2}, /* level 21.*/+ { 17, 18, 17, 11, 3,999, ZSTD_btultra2}, /* level 22.*/+},+{ /* for srcSize <= 16 KB */+ /* W, C, H, S, L, T, strat */+ { 14, 12, 13, 1, 5, 1, ZSTD_fast }, /* base for negative levels */+ { 14, 14, 15, 1, 5, 0, ZSTD_fast }, /* level 1 */+ { 14, 14, 15, 1, 4, 0, ZSTD_fast }, /* level 2 */+ { 14, 14, 15, 2, 4, 0, ZSTD_dfast }, /* level 3 */+ { 14, 14, 14, 4, 4, 2, ZSTD_greedy }, /* level 4 */+ { 14, 14, 14, 3, 4, 4, ZSTD_lazy }, /* level 5.*/+ { 14, 14, 14, 4, 4, 8, ZSTD_lazy2 }, /* level 6 */+ { 14, 14, 14, 6, 4, 8, ZSTD_lazy2 }, /* level 7 */+ { 14, 14, 14, 8, 4, 8, ZSTD_lazy2 }, /* level 8.*/+ { 14, 15, 14, 5, 4, 8, ZSTD_btlazy2 }, /* level 9.*/+ { 14, 15, 14, 9, 4, 8, ZSTD_btlazy2 }, /* level 10.*/+ { 14, 15, 14, 3, 4, 12, ZSTD_btopt }, /* level 11.*/+ { 14, 15, 14, 4, 3, 24, ZSTD_btopt }, /* level 12.*/+ { 14, 15, 14, 5, 3, 32, ZSTD_btultra }, /* level 13.*/+ { 14, 15, 15, 6, 3, 64, ZSTD_btultra }, /* level 14.*/+ { 14, 15, 15, 7, 3,256, ZSTD_btultra }, /* level 15.*/+ { 14, 15, 15, 5, 3, 48, ZSTD_btultra2}, /* level 16.*/+ { 14, 15, 15, 6, 3,128, ZSTD_btultra2}, /* level 17.*/+ { 14, 15, 15, 7, 3,256, ZSTD_btultra2}, /* level 18.*/+ { 14, 15, 15, 8, 3,256, ZSTD_btultra2}, /* level 19.*/+ { 14, 15, 15, 8, 3,512, ZSTD_btultra2}, /* level 20.*/+ { 14, 15, 15, 9, 3,512, ZSTD_btultra2}, /* level 21.*/+ { 14, 15, 15, 10, 3,999, ZSTD_btultra2}, /* level 22.*/+},+};++/*! ZSTD_getCParams_internal() :+ * @return ZSTD_compressionParameters structure for a selected compression level, srcSize and dictSize.+ * Note: srcSizeHint 0 means 0, use ZSTD_CONTENTSIZE_UNKNOWN for unknown.+ * Use dictSize == 0 for unknown or unused. */+static ZSTD_compressionParameters ZSTD_getCParams_internal(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize)+{+ int const unknown = srcSizeHint == ZSTD_CONTENTSIZE_UNKNOWN;+ size_t const addedSize = unknown && dictSize > 0 ? 500 : 0;+ U64 const rSize = unknown && dictSize == 0 ? ZSTD_CONTENTSIZE_UNKNOWN : srcSizeHint+dictSize+addedSize;+ U32 const tableID = (rSize <= 256 KB) + (rSize <= 128 KB) + (rSize <= 16 KB);+ int row = compressionLevel;+ DEBUGLOG(5, "ZSTD_getCParams_internal (cLevel=%i)", compressionLevel);+ if (compressionLevel == 0) row = ZSTD_CLEVEL_DEFAULT; /* 0 == default */+ if (compressionLevel < 0) row = 0; /* entry 0 is baseline for fast mode */+ if (compressionLevel > ZSTD_MAX_CLEVEL) row = ZSTD_MAX_CLEVEL;+ { ZSTD_compressionParameters cp = ZSTD_defaultCParameters[tableID][row];+ if (compressionLevel < 0) cp.targetLength = (unsigned)(-compressionLevel); /* acceleration factor */+ /* refine parameters based on srcSize & dictSize */+ return ZSTD_adjustCParams_internal(cp, srcSizeHint, dictSize);+ }+}++/*! ZSTD_getCParams() :+ * @return ZSTD_compressionParameters structure for a selected compression level, srcSize and dictSize.+ * Size values are optional, provide 0 if not known or unused */+ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize)+{+ if (srcSizeHint == 0) srcSizeHint = ZSTD_CONTENTSIZE_UNKNOWN;+ return ZSTD_getCParams_internal(compressionLevel, srcSizeHint, dictSize);+}++/*! ZSTD_getParams() :+ * same idea as ZSTD_getCParams()+ * @return a `ZSTD_parameters` structure (instead of `ZSTD_compressionParameters`).+ * Fields of `ZSTD_frameParameters` are set to default values */+static ZSTD_parameters ZSTD_getParams_internal(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize) {+ ZSTD_parameters params;+ ZSTD_compressionParameters const cParams = ZSTD_getCParams_internal(compressionLevel, srcSizeHint, dictSize);+ DEBUGLOG(5, "ZSTD_getParams (cLevel=%i)", compressionLevel);+ memset(¶ms, 0, sizeof(params));+ params.cParams = cParams;+ params.fParams.contentSizeFlag = 1;+ return params;+}++/*! ZSTD_getParams() :+ * same idea as ZSTD_getCParams()+ * @return a `ZSTD_parameters` structure (instead of `ZSTD_compressionParameters`).+ * Fields of `ZSTD_frameParameters` are set to default values */+ZSTD_parameters ZSTD_getParams(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize) {+ if (srcSizeHint == 0) srcSizeHint = ZSTD_CONTENTSIZE_UNKNOWN;+ return ZSTD_getParams_internal(compressionLevel, srcSizeHint, dictSize); }
zstd/lib/compress/zstd_compress_internal.h view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -18,7 +18,8 @@ /*-************************************* * Dependencies ***************************************/-#include "zstd_internal.h"+#include "../common/zstd_internal.h"+#include "zstd_cwksp.h" #ifdef ZSTD_MULTITHREAD # include "zstdmt_compress.h" #endif@@ -33,13 +34,13 @@ ***************************************/ #define kSearchStrength 8 #define HASH_READ_SIZE 8-#define ZSTD_DUBT_UNSORTED_MARK 1 /* For btlazy2 strategy, index 1 now means "unsorted".+#define ZSTD_DUBT_UNSORTED_MARK 1 /* For btlazy2 strategy, index ZSTD_DUBT_UNSORTED_MARK==1 means "unsorted". It could be confused for a real successor at index "1", if sorted as larger than its predecessor. It's not a big deal though : candidate will just be sorted again. Additionally, candidate position 1 will be lost. But candidate 1 cannot hide a large tree of candidates, so it's a minimal loss.- The benefit is that ZSTD_DUBT_UNSORTED_MARK cannot be mishandled after table re-use with a different strategy- Constant required by ZSTD_compressBlock_btlazy2() and ZSTD_reduceTable_internal() */+ The benefit is that ZSTD_DUBT_UNSORTED_MARK cannot be mishandled after table re-use with a different strategy.+ This constant is required by ZSTD_compressBlock_btlazy2() and ZSTD_reduceTable_internal() */ /*-*************************************@@ -128,21 +129,26 @@ BYTE const* base; /* All regular indexes relative to this position */ BYTE const* dictBase; /* extDict indexes relative to this position */ U32 dictLimit; /* below that point, need extDict */- U32 lowLimit; /* below that point, no more data */+ U32 lowLimit; /* below that point, no more valid data */ } ZSTD_window_t; typedef struct ZSTD_matchState_t ZSTD_matchState_t; struct ZSTD_matchState_t { ZSTD_window_t window; /* State for window round buffer management */- U32 loadedDictEnd; /* index of end of dictionary */+ U32 loadedDictEnd; /* index of end of dictionary, within context's referential.+ * When loadedDictEnd != 0, a dictionary is in use, and still valid.+ * This relies on a mechanism to set loadedDictEnd=0 when dictionary is no longer within distance.+ * Such mechanism is provided within ZSTD_window_enforceMaxDist() and ZSTD_checkDictValidity().+ * When dict referential is copied into active context (i.e. not attached),+ * loadedDictEnd == dictSize, since referential starts from zero.+ */ U32 nextToUpdate; /* index from which to continue table update */- U32 nextToUpdate3; /* index from which to continue table update */- U32 hashLog3; /* dispatch table : larger == faster, more memory */+ U32 hashLog3; /* dispatch table for matches of len==3 : larger == faster, more memory */ U32* hashTable; U32* hashTable3; U32* chainTable; optState_t opt; /* optimal parser state */- const ZSTD_matchState_t * dictMatchState;+ const ZSTD_matchState_t* dictMatchState; ZSTD_compressionParameters cParams; }; @@ -160,6 +166,7 @@ typedef struct { ZSTD_window_t window; /* State for the window round buffer management */ ldmEntry_t* hashTable;+ U32 loadedDictEnd; BYTE* bucketOffsets; /* Next position in bucket to insert entry */ U64 hashPower; /* Used to compute the rolling hash. * Depends on ldmParams.minMatchLength */@@ -187,6 +194,13 @@ size_t capacity; /* The capacity starting from `seq` pointer */ } rawSeqStore_t; +typedef struct {+ int collectSequences;+ ZSTD_Sequence* seqStart;+ size_t seqIndex;+ size_t maxSequences;+} SeqCollector;+ struct ZSTD_CCtx_params_s { ZSTD_format_e format; ZSTD_compressionParameters cParams;@@ -195,6 +209,12 @@ int compressionLevel; int forceWindow; /* force back-references to respect limit of * 1<<wLog, even for dictionary */+ size_t targetCBlockSize; /* Tries to fit compressed block size to be around targetCBlockSize.+ * No target when targetCBlockSize == 0.+ * There is no guarantee on compressed block size */+ int srcSizeHint; /* User's best guess of source size.+ * Hint is not valid when srcSizeHint == 0.+ * There is no guarantee that hint is close to actual source size */ ZSTD_dictAttachPref_e attachDictPref; ZSTD_literalCompressionMode_e literalCompressionMode;@@ -220,9 +240,7 @@ ZSTD_CCtx_params appliedParams; U32 dictID; - int workSpaceOversizedDuration;- void* workSpace;- size_t workSpaceSize;+ ZSTD_cwksp workspace; /* manages buffer for dynamic allocations */ size_t blockSize; unsigned long long pledgedSrcSizePlusOne; /* this way, 0 (default) == unknown */ unsigned long long consumedSrcSize;@@ -230,6 +248,9 @@ XXH64_state_t xxhState; ZSTD_customMem customMem; size_t staticSize;+ SeqCollector seqCollector;+ int isFirstBlock;+ int initialized; seqStore_t seqStore; /* sequences storage ptrs */ ldmState_t ldmState; /* long distance matching state */@@ -305,26 +326,145 @@ return (mlBase > 127) ? ZSTD_highbit32(mlBase) + ML_deltaCode : ML_Code[mlBase]; } +typedef struct repcodes_s {+ U32 rep[3];+} repcodes_t;++MEM_STATIC repcodes_t ZSTD_updateRep(U32 const rep[3], U32 const offset, U32 const ll0)+{+ repcodes_t newReps;+ if (offset >= ZSTD_REP_NUM) { /* full offset */+ newReps.rep[2] = rep[1];+ newReps.rep[1] = rep[0];+ newReps.rep[0] = offset - ZSTD_REP_MOVE;+ } else { /* repcode */+ U32 const repCode = offset + ll0;+ if (repCode > 0) { /* note : if repCode==0, no change */+ U32 const currentOffset = (repCode==ZSTD_REP_NUM) ? (rep[0] - 1) : rep[repCode];+ newReps.rep[2] = (repCode >= 2) ? rep[1] : rep[2];+ newReps.rep[1] = rep[0];+ newReps.rep[0] = currentOffset;+ } else { /* repCode == 0 */+ memcpy(&newReps, rep, sizeof(newReps));+ }+ }+ return newReps;+}++/* ZSTD_cParam_withinBounds:+ * @return 1 if value is within cParam bounds,+ * 0 otherwise */+MEM_STATIC int ZSTD_cParam_withinBounds(ZSTD_cParameter cParam, int value)+{+ ZSTD_bounds const bounds = ZSTD_cParam_getBounds(cParam);+ if (ZSTD_isError(bounds.error)) return 0;+ if (value < bounds.lowerBound) return 0;+ if (value > bounds.upperBound) return 0;+ return 1;+}++/* 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)+{+ U32 const cBlockHeader24 = lastBlock + (((U32)bt_raw)<<1) + (U32)(srcSize << 3);+ RETURN_ERROR_IF(srcSize + ZSTD_blockHeaderSize > dstCapacity,+ dstSize_tooSmall, "dst buf too small for uncompressed block");+ MEM_writeLE24(dst, cBlockHeader24);+ memcpy((BYTE*)dst + ZSTD_blockHeaderSize, src, srcSize);+ return ZSTD_blockHeaderSize + srcSize;+}++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);+ RETURN_ERROR_IF(dstCapacity < 4, dstSize_tooSmall, "");+ MEM_writeLE24(op, cBlockHeader);+ op[3] = src;+ return 4;+}+++/* ZSTD_minGain() :+ * minimum compression required+ * to generate a compress block or a compressed literals section.+ * note : use same formula for both situations */+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));+ return (srcSize >> minlog) + 2;+}++MEM_STATIC int ZSTD_disableLiteralsCompression(const ZSTD_CCtx_params* cctxParams)+{+ switch (cctxParams->literalCompressionMode) {+ case ZSTD_lcm_huffman:+ return 0;+ case ZSTD_lcm_uncompressed:+ return 1;+ default:+ assert(0 /* impossible: pre-validated */);+ /* fall-through */+ case ZSTD_lcm_auto:+ return (cctxParams->cParams.strategy == ZSTD_fast) && (cctxParams->cParams.targetLength > 0);+ }+}++/*! ZSTD_safecopyLiterals() :+ * memcpy() function that won't read beyond more than WILDCOPY_OVERLENGTH bytes past ilimit_w.+ * Only called when the sequence ends past ilimit_w, so it only needs to be optimized for single+ * large copies.+ */+static void ZSTD_safecopyLiterals(BYTE* op, BYTE const* ip, BYTE const* const iend, BYTE const* ilimit_w) {+ assert(iend > ilimit_w);+ if (ip <= ilimit_w) {+ ZSTD_wildcopy(op, ip, ilimit_w - ip, ZSTD_no_overlap);+ op += ilimit_w - ip;+ ip = ilimit_w;+ }+ while (ip < iend) *op++ = *ip++;+}+ /*! ZSTD_storeSeq() :- * Store a sequence (literal length, literals, offset code and match length code) into seqStore_t.- * `offsetCode` : distance to match + 3 (values 1-3 are repCodes).+ * Store a sequence (litlen, litPtr, offCode and mlBase) into seqStore_t.+ * `offCode` : distance to match + ZSTD_REP_MOVE (values <= ZSTD_REP_MOVE are repCodes). * `mlBase` : matchLength - MINMATCH+ * Allowed to overread literals up to litLimit. */-MEM_STATIC void ZSTD_storeSeq(seqStore_t* seqStorePtr, size_t litLength, const void* literals, U32 offsetCode, size_t mlBase)+HINT_INLINE UNUSED_ATTR+void ZSTD_storeSeq(seqStore_t* seqStorePtr, size_t litLength, const BYTE* literals, const BYTE* litLimit, U32 offCode, size_t mlBase) {+ BYTE const* const litLimit_w = litLimit - WILDCOPY_OVERLENGTH;+ BYTE const* const litEnd = literals + litLength; #if defined(DEBUGLEVEL) && (DEBUGLEVEL >= 6) 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)mlBase+MINMATCH, (U32)offsetCode);+ pos, (U32)litLength, (U32)mlBase+MINMATCH, (U32)offCode); } #endif assert((size_t)(seqStorePtr->sequences - seqStorePtr->sequencesStart) < seqStorePtr->maxNbSeq); /* copy Literals */ assert(seqStorePtr->maxNbLit <= 128 KB); assert(seqStorePtr->lit + litLength <= seqStorePtr->litStart + seqStorePtr->maxNbLit);- ZSTD_wildcopy(seqStorePtr->lit, literals, litLength);+ 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);+ ZSTD_copy16(seqStorePtr->lit, literals);+ if (litLength > 16) {+ ZSTD_wildcopy(seqStorePtr->lit+16, literals+16, (ptrdiff_t)litLength-16, ZSTD_no_overlap);+ }+ } else {+ ZSTD_safecopyLiterals(seqStorePtr->lit, literals, litEnd, litLimit_w);+ } seqStorePtr->lit += litLength; /* literal Length */@@ -336,7 +476,7 @@ seqStorePtr->sequences[0].litLength = (U16)litLength; /* match offset */- seqStorePtr->sequences[0].offset = offsetCode + 1;+ seqStorePtr->sequences[0].offset = offCode + 1; /* match Length */ if (mlBase>0xFFFF) {@@ -359,8 +499,7 @@ if (MEM_64bits()) { # if defined(_MSC_VER) && defined(_WIN64) unsigned long r = 0;- _BitScanForward64( &r, (U64)val );- return (unsigned)(r>>3);+ return _BitScanForward64( &r, (U64)val ) ? (unsigned)(r >> 3) : 0; # elif defined(__GNUC__) && (__GNUC__ >= 4) return (__builtin_ctzll((U64)val) >> 3); # else@@ -377,8 +516,7 @@ } else { /* 32 bits */ # if defined(_MSC_VER) unsigned long r=0;- _BitScanForward( &r, (U32)val );- return (unsigned)(r>>3);+ return _BitScanForward( &r, (U32)val ) ? (unsigned)(r >> 3) : 0; # elif defined(__GNUC__) && (__GNUC__ >= 3) return (__builtin_ctz((U32)val) >> 3); # else@@ -393,8 +531,7 @@ if (MEM_64bits()) { # if defined(_MSC_VER) && defined(_WIN64) unsigned long r = 0;- _BitScanReverse64( &r, val );- return (unsigned)(r>>3);+ return _BitScanReverse64( &r, val ) ? (unsigned)(r >> 3) : 0; # elif defined(__GNUC__) && (__GNUC__ >= 4) return (__builtin_clzll(val) >> 3); # else@@ -408,8 +545,7 @@ } else { /* 32 bits */ # if defined(_MSC_VER) unsigned long r = 0;- _BitScanReverse( &r, (unsigned long)val );- return (unsigned)(r>>3);+ return _BitScanReverse( &r, (unsigned long)val ) ? (unsigned)(r >> 3) : 0; # elif defined(__GNUC__) && (__GNUC__ >= 3) return (__builtin_clz((U32)val) >> 3); # else@@ -564,6 +700,9 @@ /*-************************************* * Round buffer management ***************************************/+#if (ZSTD_WINDOWLOG_MAX_64 > 31)+# error "ZSTD_WINDOWLOG_MAX is too large : would overflow ZSTD_CURRENT_MAX"+#endif /* Max current allowed */ #define ZSTD_CURRENT_MAX ((3U << 29) + (1U << ZSTD_WINDOWLOG_MAX)) /* Maximum chunk size before overflow correction needs to be called again */@@ -653,7 +792,10 @@ */ U32 const cycleMask = (1U << cycleLog) - 1; U32 const current = (U32)((BYTE const*)src - window->base);- U32 const newCurrent = (current & cycleMask) + maxDist;+ U32 const currentCycle0 = current & cycleMask;+ /* Exclude zero so that newCurrent - maxDist >= 1. */+ U32 const currentCycle1 = currentCycle0 == 0 ? (1U << cycleLog) : currentCycle0;+ U32 const newCurrent = currentCycle1 + maxDist; U32 const correction = current - newCurrent; assert((maxDist & cycleMask) == 0); assert(current > newCurrent);@@ -662,9 +804,18 @@ window->base += correction; window->dictBase += correction;- window->lowLimit -= correction;- window->dictLimit -= correction;+ if (window->lowLimit <= correction) window->lowLimit = 1;+ else window->lowLimit -= correction;+ if (window->dictLimit <= correction) window->dictLimit = 1;+ else window->dictLimit -= correction; + /* Ensure we can still reference the full window. */+ assert(newCurrent >= maxDist);+ assert(newCurrent - maxDist >= 1);+ /* Ensure that lowLimit and dictLimit didn't underflow. */+ assert(window->lowLimit <= newCurrent);+ assert(window->dictLimit <= newCurrent);+ DEBUGLOG(4, "Correction of 0x%x bytes to lowLimit=0x%x", correction, window->lowLimit); return correction;@@ -675,31 +826,49 @@ * Updates lowLimit so that: * (srcEnd - base) - lowLimit == maxDist + loadedDictEnd *- * This allows a simple check that index >= lowLimit to see if index is valid.- * This must be called before a block compression call, with srcEnd as the block- * source end.+ * It ensures index is valid as long as index >= lowLimit.+ * This must be called before a block compression call. *- * If loadedDictEndPtr is not NULL, we set it to zero once we update lowLimit.- * This is because dictionaries are allowed to be referenced as long as the last- * byte of the dictionary is in the window, but once they are out of range,- * they cannot be referenced. If loadedDictEndPtr is NULL, we use- * loadedDictEnd == 0.+ * loadedDictEnd is only defined if a dictionary is in use for current compression.+ * As the name implies, loadedDictEnd represents the index at end of dictionary.+ * The value lies within context's referential, it can be directly compared to blockEndIdx. *- * In normal dict mode, the dict is between lowLimit and dictLimit. In- * dictMatchState mode, lowLimit and dictLimit are the same, and the dictionary- * is below them. forceWindow and dictMatchState are therefore incompatible.+ * If loadedDictEndPtr is NULL, no dictionary is in use, and we use loadedDictEnd == 0.+ * If loadedDictEndPtr is not NULL, we set it to zero after updating lowLimit.+ * This is because dictionaries are allowed to be referenced fully+ * as long as the last byte of the dictionary is in the window.+ * Once input has progressed beyond window size, dictionary cannot be referenced anymore.+ *+ * In normal dict mode, the dictionary lies between lowLimit and dictLimit.+ * In dictMatchState mode, lowLimit and dictLimit are the same,+ * and the dictionary is below them.+ * forceWindow and dictMatchState are therefore incompatible. */ MEM_STATIC void ZSTD_window_enforceMaxDist(ZSTD_window_t* window,- void const* srcEnd,- U32 maxDist,- U32* loadedDictEndPtr,+ const void* blockEnd,+ U32 maxDist,+ U32* loadedDictEndPtr, const ZSTD_matchState_t** dictMatchStatePtr) {- U32 const blockEndIdx = (U32)((BYTE const*)srcEnd - window->base);- U32 loadedDictEnd = (loadedDictEndPtr != NULL) ? *loadedDictEndPtr : 0;- DEBUGLOG(5, "ZSTD_window_enforceMaxDist: blockEndIdx=%u, maxDist=%u",- (unsigned)blockEndIdx, (unsigned)maxDist);+ U32 const blockEndIdx = (U32)((BYTE const*)blockEnd - window->base);+ U32 const loadedDictEnd = (loadedDictEndPtr != NULL) ? *loadedDictEndPtr : 0;+ DEBUGLOG(5, "ZSTD_window_enforceMaxDist: blockEndIdx=%u, maxDist=%u, loadedDictEnd=%u",+ (unsigned)blockEndIdx, (unsigned)maxDist, (unsigned)loadedDictEnd);++ /* - When there is no dictionary : loadedDictEnd == 0.+ In which case, the test (blockEndIdx > maxDist) is merely to avoid+ overflowing next operation `newLowLimit = blockEndIdx - maxDist`.+ - When there is a standard dictionary :+ Index referential is copied from the dictionary,+ which means it starts from 0.+ In which case, loadedDictEnd == dictSize,+ and it makes sense to compare `blockEndIdx > maxDist + dictSize`+ since `blockEndIdx` also starts from zero.+ - When there is an attached dictionary :+ loadedDictEnd is expressed within the referential of the context,+ so it can be directly compared against blockEndIdx.+ */ if (blockEndIdx > maxDist + loadedDictEnd) { U32 const newLowLimit = blockEndIdx - maxDist; if (window->lowLimit < newLowLimit) window->lowLimit = newLowLimit;@@ -708,13 +877,56 @@ (unsigned)window->dictLimit, (unsigned)window->lowLimit); window->dictLimit = window->lowLimit; }- if (loadedDictEndPtr)+ /* On reaching window size, dictionaries are invalidated */+ if (loadedDictEndPtr) *loadedDictEndPtr = 0;+ if (dictMatchStatePtr) *dictMatchStatePtr = NULL;+ }+}++/* Similar to ZSTD_window_enforceMaxDist(),+ * but only invalidates dictionary+ * when input progresses beyond window size.+ * assumption : loadedDictEndPtr and dictMatchStatePtr are valid (non NULL)+ * loadedDictEnd uses same referential as window->base+ * maxDist is the window size */+MEM_STATIC void+ZSTD_checkDictValidity(const ZSTD_window_t* window,+ const void* blockEnd,+ U32 maxDist,+ U32* loadedDictEndPtr,+ const ZSTD_matchState_t** dictMatchStatePtr)+{+ assert(loadedDictEndPtr != NULL);+ assert(dictMatchStatePtr != NULL);+ { U32 const blockEndIdx = (U32)((BYTE const*)blockEnd - window->base);+ U32 const loadedDictEnd = *loadedDictEndPtr;+ DEBUGLOG(5, "ZSTD_checkDictValidity: blockEndIdx=%u, maxDist=%u, loadedDictEnd=%u",+ (unsigned)blockEndIdx, (unsigned)maxDist, (unsigned)loadedDictEnd);+ assert(blockEndIdx >= loadedDictEnd);++ if (blockEndIdx > loadedDictEnd + maxDist) {+ /* 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.+ */+ DEBUGLOG(6, "invalidating dictionary for current block (distance > windowSize)"); *loadedDictEndPtr = 0;- if (dictMatchStatePtr) *dictMatchStatePtr = NULL;- }+ } else {+ if (*loadedDictEndPtr != 0) {+ DEBUGLOG(6, "dictionary considered valid for current block");+ } } } } +MEM_STATIC void ZSTD_window_init(ZSTD_window_t* window) {+ memset(window, 0, sizeof(*window));+ window->base = (BYTE const*)"";+ window->dictBase = (BYTE const*)"";+ window->dictLimit = 1; /* start from 1, so that 1st position is valid */+ window->lowLimit = 1; /* it ensures first and later CCtx usages compress the same */+ window->nextSrc = window->base + 1; /* see issue #1241 */+}+ /** * ZSTD_window_update(): * Updates the window by appending [src, src + srcSize) to the window.@@ -728,6 +940,10 @@ BYTE const* const ip = (BYTE const*)src; U32 contiguous = 1; DEBUGLOG(5, "ZSTD_window_update");+ if (srcSize == 0)+ return contiguous;+ assert(window->base != NULL);+ assert(window->dictBase != NULL); /* Check if blocks follow each other */ if (src != window->nextSrc) { /* not contiguous */@@ -738,7 +954,7 @@ window->dictLimit = (U32)distanceFromBase; window->dictBase = window->base; window->base = ip - distanceFromBase;- // ms->nextToUpdate = window->dictLimit;+ /* ms->nextToUpdate = window->dictLimit; */ if (window->dictLimit - window->lowLimit < HASH_READ_SIZE) window->lowLimit = window->dictLimit; /* too small extDict */ contiguous = 0; }@@ -754,7 +970,34 @@ return contiguous; } +/**+ * Returns the lowest allowed match index. It may either be in the ext-dict or the prefix.+ */+MEM_STATIC U32 ZSTD_getLowestMatchIndex(const ZSTD_matchState_t* ms, U32 current, unsigned windowLog)+{+ U32 const maxDistance = 1U << windowLog;+ U32 const lowestValid = ms->window.lowLimit;+ U32 const withinWindow = (current - lowestValid > maxDistance) ? current - maxDistance : lowestValid;+ U32 const isDictionary = (ms->loadedDictEnd != 0);+ U32 const matchLowest = isDictionary ? lowestValid : withinWindow;+ return matchLowest;+} +/**+ * Returns the lowest allowed match index in the prefix.+ */+MEM_STATIC U32 ZSTD_getLowestPrefixIndex(const ZSTD_matchState_t* ms, U32 current, unsigned windowLog)+{+ U32 const maxDistance = 1U << windowLog;+ U32 const lowestValid = ms->window.dictLimit;+ U32 const withinWindow = (current - lowestValid > maxDistance) ? current - maxDistance : lowestValid;+ U32 const isDictionary = (ms->loadedDictEnd != 0);+ U32 const matchLowest = isDictionary ? lowestValid : withinWindow;+ return matchLowest;+}+++ /* debug functions */ #if (DEBUGLEVEL>=2) @@ -791,7 +1034,22 @@ } #endif +/* ===============================================================+ * Shared internal declarations+ * These prototypes may be called from sources not in lib/compress+ * =============================================================== */ +/* ZSTD_loadCEntropy() :+ * dict : must point at beginning of a valid zstd dictionary.+ * return : size of dictionary header (size of magic number + dict ID + entropy tables)+ * assumptions : magic number supposed already checked+ * and dictSize >= 8 */+size_t ZSTD_loadCEntropy(ZSTD_compressedBlockState_t* bs, void* workspace,+ short* offcodeNCount, unsigned* offcodeMaxValue,+ const void* const dict, size_t dictSize);++void ZSTD_reset_compressedBlockState(ZSTD_compressedBlockState_t* bs);+ /* ============================================================== * Private declarations * These prototypes shall only be called from within lib/compress@@ -800,6 +1058,7 @@ /* ZSTD_getCParamsFromCCtxParams() : * cParams are built depending on compressionLevel, src size hints, * LDM and manually set compression parameters.+ * Note: srcSizeHint == 0 means 0! */ ZSTD_compressionParameters ZSTD_getCParamsFromCCtxParams( const ZSTD_CCtx_params* CCtxParams, U64 srcSizeHint, size_t dictSize);@@ -812,7 +1071,7 @@ size_t ZSTD_initCStream_internal(ZSTD_CStream* zcs, const void* dict, size_t dictSize, const ZSTD_CDict* cdict,- ZSTD_CCtx_params params, unsigned long long pledgedSrcSize);+ const ZSTD_CCtx_params* params, unsigned long long pledgedSrcSize); void ZSTD_resetSeqStore(seqStore_t* ssPtr); @@ -827,7 +1086,7 @@ ZSTD_dictContentType_e dictContentType, ZSTD_dictTableLoadMethod_e dtlm, const ZSTD_CDict* cdict,- ZSTD_CCtx_params params,+ const ZSTD_CCtx_params* params, unsigned long long pledgedSrcSize); /* ZSTD_compress_advanced_internal() :@@ -836,7 +1095,7 @@ void* dst, size_t dstCapacity, const void* src, size_t srcSize, const void* dict,size_t dictSize,- ZSTD_CCtx_params params);+ const ZSTD_CCtx_params* params); /* ZSTD_writeLastEmptyBlock() :@@ -859,5 +1118,8 @@ */ size_t ZSTD_referenceExternalSequences(ZSTD_CCtx* cctx, rawSeq* seq, size_t nbSeq); +/** ZSTD_cycleLog() :+ * condition for correct operation : hashLog > 1 */+U32 ZSTD_cycleLog(U32 hashLog, ZSTD_strategy strat); #endif /* ZSTD_COMPRESS_H */
+ zstd/lib/compress/zstd_compress_literals.c view
@@ -0,0 +1,158 @@+/*+ * Copyright (c) 2016-2020, 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* const)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);+ }++ 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* const)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)+{+ 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 */+ 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) :+ HUF_compress4X_repeat(+ ostart+lhSize, dstCapacity-lhSize, src, srcSize,+ HUF_SYMBOLVALUE_MAX, HUF_TABLELOG_DEFAULT, entropyWorkspace, entropyWorkspaceSize,+ (HUF_CElt*)nextHuf->CTable, &repeat, preferRepeat, bmi2);+ 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)) {+ memcpy(nextHuf, prevHuf, sizeof(*prevHuf));+ return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize);+ }+ if (cLitSize==1) {+ 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;+}
+ zstd/lib/compress/zstd_compress_literals.h view
@@ -0,0 +1,29 @@+/*+ * Copyright (c) 2016-2020, 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.+ */++#ifndef ZSTD_COMPRESS_LITERALS_H+#define ZSTD_COMPRESS_LITERALS_H++#include "zstd_compress_internal.h" /* ZSTD_hufCTables_t, ZSTD_minGain() */+++size_t ZSTD_noCompressLiterals (void* dst, size_t dstCapacity, const void* src, size_t srcSize);++size_t ZSTD_compressRleLiteralsBlock (void* dst, size_t dstCapacity, const void* src, size_t srcSize);++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);++#endif /* ZSTD_COMPRESS_LITERALS_H */
+ zstd/lib/compress/zstd_compress_sequences.c view
@@ -0,0 +1,419 @@+/*+ * Copyright (c) 2016-2020, 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_sequences.h"++/**+ * -log2(x / 256) lookup table for x in [0, 256).+ * If x == 0: Return 0+ * Else: Return floor(-log2(x / 256) * 256)+ */+static unsigned const kInverseProbabilityLog256[256] = {+ 0, 2048, 1792, 1642, 1536, 1453, 1386, 1329, 1280, 1236, 1197, 1162,+ 1130, 1100, 1073, 1047, 1024, 1001, 980, 960, 941, 923, 906, 889,+ 874, 859, 844, 830, 817, 804, 791, 779, 768, 756, 745, 734,+ 724, 714, 704, 694, 685, 676, 667, 658, 650, 642, 633, 626,+ 618, 610, 603, 595, 588, 581, 574, 567, 561, 554, 548, 542,+ 535, 529, 523, 517, 512, 506, 500, 495, 489, 484, 478, 473,+ 468, 463, 458, 453, 448, 443, 438, 434, 429, 424, 420, 415,+ 411, 407, 402, 398, 394, 390, 386, 382, 377, 373, 370, 366,+ 362, 358, 354, 350, 347, 343, 339, 336, 332, 329, 325, 322,+ 318, 315, 311, 308, 305, 302, 298, 295, 292, 289, 286, 282,+ 279, 276, 273, 270, 267, 264, 261, 258, 256, 253, 250, 247,+ 244, 241, 239, 236, 233, 230, 228, 225, 222, 220, 217, 215,+ 212, 209, 207, 204, 202, 199, 197, 194, 192, 190, 187, 185,+ 182, 180, 178, 175, 173, 171, 168, 166, 164, 162, 159, 157,+ 155, 153, 151, 149, 146, 144, 142, 140, 138, 136, 134, 132,+ 130, 128, 126, 123, 121, 119, 117, 115, 114, 112, 110, 108,+ 106, 104, 102, 100, 98, 96, 94, 93, 91, 89, 87, 85,+ 83, 82, 80, 78, 76, 74, 73, 71, 69, 67, 66, 64,+ 62, 61, 59, 57, 55, 54, 52, 50, 49, 47, 46, 44,+ 42, 41, 39, 37, 36, 34, 33, 31, 30, 28, 26, 25,+ 23, 22, 20, 19, 17, 16, 14, 13, 11, 10, 8, 7,+ 5, 4, 2, 1,+};++static unsigned ZSTD_getFSEMaxSymbolValue(FSE_CTable const* ctable) {+ void const* ptr = ctable;+ U16 const* u16ptr = (U16 const*)ptr;+ U32 const maxSymbolValue = MEM_read16(u16ptr + 1);+ return maxSymbolValue;+}++/**+ * Returns the cost in bytes of encoding the normalized count header.+ * Returns an error if any of the helper functions return an error.+ */+static size_t ZSTD_NCountCost(unsigned const* count, unsigned const max,+ size_t const nbSeq, unsigned const FSELog)+{+ BYTE wksp[FSE_NCOUNTBOUND];+ S16 norm[MaxSeq + 1];+ const U32 tableLog = FSE_optimalTableLog(FSELog, nbSeq, max);+ FORWARD_IF_ERROR(FSE_normalizeCount(norm, tableLog, count, nbSeq, max), "");+ return FSE_writeNCount(wksp, sizeof(wksp), norm, max, tableLog);+}++/**+ * Returns the cost in bits of encoding the distribution described by count+ * using the entropy bound.+ */+static size_t ZSTD_entropyCost(unsigned const* count, unsigned const max, size_t const total)+{+ unsigned cost = 0;+ unsigned s;+ for (s = 0; s <= max; ++s) {+ unsigned norm = (unsigned)((256 * count[s]) / total);+ if (count[s] != 0 && norm == 0)+ norm = 1;+ assert(count[s] < total);+ cost += count[s] * kInverseProbabilityLog256[norm];+ }+ return cost >> 8;+}++/**+ * Returns the cost in bits of encoding the distribution in count using ctable.+ * Returns an error if ctable cannot represent all the symbols in count.+ */+size_t ZSTD_fseBitCost(+ FSE_CTable const* ctable,+ unsigned const* count,+ unsigned const max)+{+ unsigned const kAccuracyLog = 8;+ size_t cost = 0;+ unsigned s;+ FSE_CState_t cstate;+ FSE_initCState(&cstate, ctable);+ if (ZSTD_getFSEMaxSymbolValue(ctable) < max) {+ DEBUGLOG(5, "Repeat FSE_CTable has maxSymbolValue %u < %u",+ ZSTD_getFSEMaxSymbolValue(ctable), max);+ return ERROR(GENERIC);+ }+ for (s = 0; s <= max; ++s) {+ unsigned const tableLog = cstate.stateLog;+ unsigned const badCost = (tableLog + 1) << kAccuracyLog;+ unsigned const bitCost = FSE_bitCost(cstate.symbolTT, tableLog, s, kAccuracyLog);+ if (count[s] == 0)+ continue;+ if (bitCost >= badCost) {+ DEBUGLOG(5, "Repeat FSE_CTable has Prob[%u] == 0", s);+ return ERROR(GENERIC);+ }+ cost += (size_t)count[s] * bitCost;+ }+ return cost >> kAccuracyLog;+}++/**+ * Returns the cost in bits of encoding the distribution in count using the+ * table described by norm. The max symbol support by norm is assumed >= max.+ * norm must be valid for every symbol with non-zero probability in count.+ */+size_t ZSTD_crossEntropyCost(short const* norm, unsigned accuracyLog,+ unsigned const* count, unsigned const max)+{+ unsigned const shift = 8 - accuracyLog;+ size_t cost = 0;+ unsigned s;+ assert(accuracyLog <= 8);+ for (s = 0; s <= max; ++s) {+ unsigned const normAcc = (norm[s] != -1) ? (unsigned)norm[s] : 1;+ unsigned const norm256 = normAcc << shift;+ assert(norm256 > 0);+ assert(norm256 < 256);+ cost += count[s] * kInverseProbabilityLog256[norm256];+ }+ return cost >> 8;+}++symbolEncodingType_e+ZSTD_selectEncodingType(+ FSE_repeat* repeatMode, unsigned const* count, unsigned const max,+ size_t const mostFrequent, size_t nbSeq, unsigned const FSELog,+ FSE_CTable const* prevCTable,+ short const* defaultNorm, U32 defaultNormLog,+ ZSTD_defaultPolicy_e const isDefaultAllowed,+ ZSTD_strategy const strategy)+{+ ZSTD_STATIC_ASSERT(ZSTD_defaultDisallowed == 0 && ZSTD_defaultAllowed != 0);+ if (mostFrequent == nbSeq) {+ *repeatMode = FSE_repeat_none;+ if (isDefaultAllowed && nbSeq <= 2) {+ /* Prefer set_basic over set_rle when there are 2 or less symbols,+ * since RLE uses 1 byte, but set_basic uses 5-6 bits per symbol.+ * If basic encoding isn't possible, always choose RLE.+ */+ DEBUGLOG(5, "Selected set_basic");+ return set_basic;+ }+ DEBUGLOG(5, "Selected set_rle");+ return set_rle;+ }+ if (strategy < ZSTD_lazy) {+ if (isDefaultAllowed) {+ size_t const staticFse_nbSeq_max = 1000;+ size_t const mult = 10 - strategy;+ size_t const baseLog = 3;+ size_t const dynamicFse_nbSeq_min = (((size_t)1 << defaultNormLog) * mult) >> baseLog; /* 28-36 for offset, 56-72 for lengths */+ assert(defaultNormLog >= 5 && defaultNormLog <= 6); /* xx_DEFAULTNORMLOG */+ assert(mult <= 9 && mult >= 7);+ if ( (*repeatMode == FSE_repeat_valid)+ && (nbSeq < staticFse_nbSeq_max) ) {+ DEBUGLOG(5, "Selected set_repeat");+ return set_repeat;+ }+ if ( (nbSeq < dynamicFse_nbSeq_min)+ || (mostFrequent < (nbSeq >> (defaultNormLog-1))) ) {+ DEBUGLOG(5, "Selected set_basic");+ /* The format allows default tables to be repeated, but it isn't useful.+ * When using simple heuristics to select encoding type, we don't want+ * to confuse these tables with dictionaries. When running more careful+ * analysis, we don't need to waste time checking both repeating tables+ * and default tables.+ */+ *repeatMode = FSE_repeat_none;+ return set_basic;+ }+ }+ } else {+ size_t const basicCost = isDefaultAllowed ? ZSTD_crossEntropyCost(defaultNorm, defaultNormLog, count, max) : ERROR(GENERIC);+ size_t const repeatCost = *repeatMode != FSE_repeat_none ? ZSTD_fseBitCost(prevCTable, count, max) : ERROR(GENERIC);+ size_t const NCountCost = ZSTD_NCountCost(count, max, nbSeq, FSELog);+ size_t const compressedCost = (NCountCost << 3) + ZSTD_entropyCost(count, max, nbSeq);++ if (isDefaultAllowed) {+ assert(!ZSTD_isError(basicCost));+ assert(!(*repeatMode == FSE_repeat_valid && ZSTD_isError(repeatCost)));+ }+ assert(!ZSTD_isError(NCountCost));+ assert(compressedCost < ERROR(maxCode));+ DEBUGLOG(5, "Estimated bit costs: basic=%u\trepeat=%u\tcompressed=%u",+ (unsigned)basicCost, (unsigned)repeatCost, (unsigned)compressedCost);+ if (basicCost <= repeatCost && basicCost <= compressedCost) {+ DEBUGLOG(5, "Selected set_basic");+ assert(isDefaultAllowed);+ *repeatMode = FSE_repeat_none;+ return set_basic;+ }+ if (repeatCost <= compressedCost) {+ DEBUGLOG(5, "Selected set_repeat");+ assert(!ZSTD_isError(repeatCost));+ return set_repeat;+ }+ assert(compressedCost < basicCost && compressedCost < repeatCost);+ }+ DEBUGLOG(5, "Selected set_compressed");+ *repeatMode = FSE_repeat_check;+ return set_compressed;+}++size_t+ZSTD_buildCTable(void* dst, size_t dstCapacity,+ FSE_CTable* nextCTable, U32 FSELog, symbolEncodingType_e type,+ unsigned* count, U32 max,+ const BYTE* codeTable, size_t nbSeq,+ const S16* defaultNorm, U32 defaultNormLog, U32 defaultMax,+ const FSE_CTable* prevCTable, size_t prevCTableSize,+ void* entropyWorkspace, size_t entropyWorkspaceSize)+{+ BYTE* op = (BYTE*)dst;+ const BYTE* const oend = op + dstCapacity;+ DEBUGLOG(6, "ZSTD_buildCTable (dstCapacity=%u)", (unsigned)dstCapacity);++ switch (type) {+ case set_rle:+ FORWARD_IF_ERROR(FSE_buildCTable_rle(nextCTable, (BYTE)max), "");+ RETURN_ERROR_IF(dstCapacity==0, dstSize_tooSmall, "not enough space");+ *op = codeTable[0];+ return 1;+ case set_repeat:+ memcpy(nextCTable, prevCTable, prevCTableSize);+ return 0;+ case set_basic:+ FORWARD_IF_ERROR(FSE_buildCTable_wksp(nextCTable, defaultNorm, defaultMax, defaultNormLog, entropyWorkspace, entropyWorkspaceSize), ""); /* note : could be pre-calculated */+ return 0;+ case set_compressed: {+ S16 norm[MaxSeq + 1];+ size_t nbSeq_1 = nbSeq;+ const U32 tableLog = FSE_optimalTableLog(FSELog, nbSeq, max);+ if (count[codeTable[nbSeq-1]] > 1) {+ count[codeTable[nbSeq-1]]--;+ nbSeq_1--;+ }+ assert(nbSeq_1 > 1);+ FORWARD_IF_ERROR(FSE_normalizeCount(norm, tableLog, count, nbSeq_1, max), "");+ { size_t const NCountSize = FSE_writeNCount(op, oend - op, norm, max, tableLog); /* overflow protected */+ FORWARD_IF_ERROR(NCountSize, "FSE_writeNCount failed");+ FORWARD_IF_ERROR(FSE_buildCTable_wksp(nextCTable, norm, max, tableLog, entropyWorkspace, entropyWorkspaceSize), "");+ return NCountSize;+ }+ }+ default: assert(0); RETURN_ERROR(GENERIC, "impossible to reach");+ }+}++FORCE_INLINE_TEMPLATE size_t+ZSTD_encodeSequences_body(+ void* dst, size_t dstCapacity,+ FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable,+ FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable,+ FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable,+ seqDef const* sequences, size_t nbSeq, int longOffsets)+{+ BIT_CStream_t blockStream;+ FSE_CState_t stateMatchLength;+ FSE_CState_t stateOffsetBits;+ FSE_CState_t stateLitLength;++ RETURN_ERROR_IF(+ ERR_isError(BIT_initCStream(&blockStream, dst, dstCapacity)),+ dstSize_tooSmall, "not enough space remaining");+ DEBUGLOG(6, "available space for bitstream : %i (dstCapacity=%u)",+ (int)(blockStream.endPtr - blockStream.startPtr),+ (unsigned)dstCapacity);++ /* first symbols */+ FSE_initCState2(&stateMatchLength, CTable_MatchLength, mlCodeTable[nbSeq-1]);+ FSE_initCState2(&stateOffsetBits, CTable_OffsetBits, ofCodeTable[nbSeq-1]);+ FSE_initCState2(&stateLitLength, CTable_LitLength, llCodeTable[nbSeq-1]);+ BIT_addBits(&blockStream, sequences[nbSeq-1].litLength, LL_bits[llCodeTable[nbSeq-1]]);+ if (MEM_32bits()) BIT_flushBits(&blockStream);+ BIT_addBits(&blockStream, sequences[nbSeq-1].matchLength, ML_bits[mlCodeTable[nbSeq-1]]);+ if (MEM_32bits()) BIT_flushBits(&blockStream);+ if (longOffsets) {+ U32 const ofBits = ofCodeTable[nbSeq-1];+ unsigned const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN-1);+ if (extraBits) {+ BIT_addBits(&blockStream, sequences[nbSeq-1].offset, extraBits);+ BIT_flushBits(&blockStream);+ }+ BIT_addBits(&blockStream, sequences[nbSeq-1].offset >> extraBits,+ ofBits - extraBits);+ } else {+ BIT_addBits(&blockStream, sequences[nbSeq-1].offset, ofCodeTable[nbSeq-1]);+ }+ BIT_flushBits(&blockStream);++ { size_t n;+ for (n=nbSeq-2 ; n<nbSeq ; n--) { /* intentional underflow */+ BYTE const llCode = llCodeTable[n];+ BYTE const ofCode = ofCodeTable[n];+ BYTE const mlCode = mlCodeTable[n];+ U32 const llBits = LL_bits[llCode];+ U32 const ofBits = ofCode;+ U32 const mlBits = ML_bits[mlCode];+ DEBUGLOG(6, "encoding: litlen:%2u - matchlen:%2u - offCode:%7u",+ (unsigned)sequences[n].litLength,+ (unsigned)sequences[n].matchLength + MINMATCH,+ (unsigned)sequences[n].offset);+ /* 32b*/ /* 64b*/+ /* (7)*/ /* (7)*/+ FSE_encodeSymbol(&blockStream, &stateOffsetBits, ofCode); /* 15 */ /* 15 */+ FSE_encodeSymbol(&blockStream, &stateMatchLength, mlCode); /* 24 */ /* 24 */+ if (MEM_32bits()) BIT_flushBits(&blockStream); /* (7)*/+ FSE_encodeSymbol(&blockStream, &stateLitLength, llCode); /* 16 */ /* 33 */+ if (MEM_32bits() || (ofBits+mlBits+llBits >= 64-7-(LLFSELog+MLFSELog+OffFSELog)))+ BIT_flushBits(&blockStream); /* (7)*/+ BIT_addBits(&blockStream, sequences[n].litLength, llBits);+ if (MEM_32bits() && ((llBits+mlBits)>24)) BIT_flushBits(&blockStream);+ BIT_addBits(&blockStream, sequences[n].matchLength, mlBits);+ if (MEM_32bits() || (ofBits+mlBits+llBits > 56)) BIT_flushBits(&blockStream);+ if (longOffsets) {+ unsigned const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN-1);+ if (extraBits) {+ BIT_addBits(&blockStream, sequences[n].offset, extraBits);+ BIT_flushBits(&blockStream); /* (7)*/+ }+ BIT_addBits(&blockStream, sequences[n].offset >> extraBits,+ ofBits - extraBits); /* 31 */+ } else {+ BIT_addBits(&blockStream, sequences[n].offset, ofBits); /* 31 */+ }+ BIT_flushBits(&blockStream); /* (7)*/+ DEBUGLOG(7, "remaining space : %i", (int)(blockStream.endPtr - blockStream.ptr));+ } }++ DEBUGLOG(6, "ZSTD_encodeSequences: flushing ML state with %u bits", stateMatchLength.stateLog);+ FSE_flushCState(&blockStream, &stateMatchLength);+ DEBUGLOG(6, "ZSTD_encodeSequences: flushing Off state with %u bits", stateOffsetBits.stateLog);+ FSE_flushCState(&blockStream, &stateOffsetBits);+ DEBUGLOG(6, "ZSTD_encodeSequences: flushing LL state with %u bits", stateLitLength.stateLog);+ FSE_flushCState(&blockStream, &stateLitLength);++ { size_t const streamSize = BIT_closeCStream(&blockStream);+ RETURN_ERROR_IF(streamSize==0, dstSize_tooSmall, "not enough space");+ return streamSize;+ }+}++static size_t+ZSTD_encodeSequences_default(+ void* dst, size_t dstCapacity,+ FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable,+ FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable,+ FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable,+ seqDef const* sequences, size_t nbSeq, int longOffsets)+{+ return ZSTD_encodeSequences_body(dst, dstCapacity,+ CTable_MatchLength, mlCodeTable,+ CTable_OffsetBits, ofCodeTable,+ CTable_LitLength, llCodeTable,+ sequences, nbSeq, longOffsets);+}+++#if DYNAMIC_BMI2++static TARGET_ATTRIBUTE("bmi2") size_t+ZSTD_encodeSequences_bmi2(+ void* dst, size_t dstCapacity,+ FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable,+ FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable,+ FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable,+ seqDef const* sequences, size_t nbSeq, int longOffsets)+{+ return ZSTD_encodeSequences_body(dst, dstCapacity,+ CTable_MatchLength, mlCodeTable,+ CTable_OffsetBits, ofCodeTable,+ CTable_LitLength, llCodeTable,+ sequences, nbSeq, longOffsets);+}++#endif++size_t ZSTD_encodeSequences(+ void* dst, size_t dstCapacity,+ FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable,+ FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable,+ FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable,+ seqDef const* sequences, size_t nbSeq, int longOffsets, int bmi2)+{+ DEBUGLOG(5, "ZSTD_encodeSequences: dstCapacity = %u", (unsigned)dstCapacity);+#if DYNAMIC_BMI2+ if (bmi2) {+ return ZSTD_encodeSequences_bmi2(dst, dstCapacity,+ CTable_MatchLength, mlCodeTable,+ CTable_OffsetBits, ofCodeTable,+ CTable_LitLength, llCodeTable,+ sequences, nbSeq, longOffsets);+ }+#endif+ (void)bmi2;+ return ZSTD_encodeSequences_default(dst, dstCapacity,+ CTable_MatchLength, mlCodeTable,+ CTable_OffsetBits, ofCodeTable,+ CTable_LitLength, llCodeTable,+ sequences, nbSeq, longOffsets);+}
+ zstd/lib/compress/zstd_compress_sequences.h view
@@ -0,0 +1,54 @@+/*+ * Copyright (c) 2016-2020, 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.+ */++#ifndef ZSTD_COMPRESS_SEQUENCES_H+#define ZSTD_COMPRESS_SEQUENCES_H++#include "../common/fse.h" /* FSE_repeat, FSE_CTable */+#include "../common/zstd_internal.h" /* symbolEncodingType_e, ZSTD_strategy */++typedef enum {+ ZSTD_defaultDisallowed = 0,+ ZSTD_defaultAllowed = 1+} ZSTD_defaultPolicy_e;++symbolEncodingType_e+ZSTD_selectEncodingType(+ FSE_repeat* repeatMode, unsigned const* count, unsigned const max,+ size_t const mostFrequent, size_t nbSeq, unsigned const FSELog,+ FSE_CTable const* prevCTable,+ short const* defaultNorm, U32 defaultNormLog,+ ZSTD_defaultPolicy_e const isDefaultAllowed,+ ZSTD_strategy const strategy);++size_t+ZSTD_buildCTable(void* dst, size_t dstCapacity,+ FSE_CTable* nextCTable, U32 FSELog, symbolEncodingType_e type,+ unsigned* count, U32 max,+ const BYTE* codeTable, size_t nbSeq,+ const S16* defaultNorm, U32 defaultNormLog, U32 defaultMax,+ const FSE_CTable* prevCTable, size_t prevCTableSize,+ void* entropyWorkspace, size_t entropyWorkspaceSize);++size_t ZSTD_encodeSequences(+ void* dst, size_t dstCapacity,+ FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable,+ FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable,+ FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable,+ seqDef const* sequences, size_t nbSeq, int longOffsets, int bmi2);++size_t ZSTD_fseBitCost(+ FSE_CTable const* ctable,+ unsigned const* count,+ unsigned const max);++size_t ZSTD_crossEntropyCost(short const* norm, unsigned accuracyLog,+ unsigned const* count, unsigned const max);+#endif /* ZSTD_COMPRESS_SEQUENCES_H */
+ zstd/lib/compress/zstd_compress_superblock.c view
@@ -0,0 +1,845 @@+/*+ * Copyright (c) 2016-2020, 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_superblock.h"++#include "../common/zstd_internal.h" /* ZSTD_getSequenceLength */+#include "hist.h" /* HIST_countFast_wksp */+#include "zstd_compress_internal.h"+#include "zstd_compress_sequences.h"+#include "zstd_compress_literals.h"++/*-*************************************+* Superblock entropy buffer structs+***************************************/+/** ZSTD_hufCTablesMetadata_t :+ * Stores Literals Block Type for a super-block in hType, and+ * huffman tree description in hufDesBuffer.+ * hufDesSize refers to the size of huffman tree description in bytes.+ * This metadata is populated in ZSTD_buildSuperBlockEntropy_literal() */+typedef struct {+ symbolEncodingType_e hType;+ BYTE hufDesBuffer[500]; /* TODO give name to this value */+ size_t hufDesSize;+} ZSTD_hufCTablesMetadata_t;++/** ZSTD_fseCTablesMetadata_t :+ * Stores symbol compression modes for a super-block in {ll, ol, ml}Type, and+ * fse tables in fseTablesBuffer.+ * fseTablesSize refers to the size of fse tables in bytes.+ * This metadata is populated in ZSTD_buildSuperBlockEntropy_sequences() */+typedef struct {+ symbolEncodingType_e llType;+ symbolEncodingType_e ofType;+ symbolEncodingType_e mlType;+ BYTE fseTablesBuffer[500]; /* TODO give name to this value */+ size_t fseTablesSize;+ size_t lastCountSize; /* This is to account for bug in 1.3.4. More detail in ZSTD_compressSubBlock_sequences() */+} ZSTD_fseCTablesMetadata_t;++typedef struct {+ ZSTD_hufCTablesMetadata_t hufMetadata;+ ZSTD_fseCTablesMetadata_t fseMetadata;+} ZSTD_entropyCTablesMetadata_t;+++/** ZSTD_buildSuperBlockEntropy_literal() :+ * Builds entropy for the super-block literals.+ * Stores literals block type (raw, rle, compressed, repeat) and+ * huffman description table to hufMetadata.+ * @return : size of huffman description table or error code */+static size_t ZSTD_buildSuperBlockEntropy_literal(void* const src, size_t srcSize,+ const ZSTD_hufCTables_t* prevHuf,+ ZSTD_hufCTables_t* nextHuf,+ ZSTD_hufCTablesMetadata_t* hufMetadata,+ const int disableLiteralsCompression,+ void* workspace, size_t wkspSize)+{+ BYTE* const wkspStart = (BYTE*)workspace;+ BYTE* const wkspEnd = wkspStart + wkspSize;+ BYTE* const countWkspStart = wkspStart;+ 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;+ unsigned maxSymbolValue = 255;+ unsigned huffLog = HUF_TABLELOG_DEFAULT;+ HUF_repeat repeat = prevHuf->repeatMode;++ DEBUGLOG(5, "ZSTD_buildSuperBlockEntropy_literal (srcSize=%zu)", srcSize);++ /* Prepare nextEntropy assuming reusing the existing table */+ memcpy(nextHuf, prevHuf, sizeof(*prevHuf));++ if (disableLiteralsCompression) {+ DEBUGLOG(5, "set_basic - disabled");+ hufMetadata->hType = set_basic;+ return 0;+ }++ /* 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) {+ 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);+ FORWARD_IF_ERROR(largest, "HIST_count_wksp failed");+ if (largest == srcSize) {+ DEBUGLOG(5, "set_rle");+ hufMetadata->hType = set_rle;+ return 0;+ }+ if (largest <= (srcSize >> 7)+4) {+ 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)) {+ repeat = HUF_repeat_none;+ }++ /* Build Huffman Tree */+ memset(nextHuf->CTable, 0, sizeof(nextHuf->CTable));+ huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue);+ { 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(+ hufMetadata->hufDesBuffer, sizeof(hufMetadata->hufDesBuffer),+ (HUF_CElt*)nextHuf->CTable, maxSymbolValue, huffLog);+ /* 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");+ memcpy(nextHuf, prevHuf, sizeof(*prevHuf));+ hufMetadata->hType = set_repeat;+ return 0;+ }+ }+ if (newCSize + hSize >= srcSize) {+ DEBUGLOG(5, "set_basic - no gains");+ 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;+ }+ }+}++/** ZSTD_buildSuperBlockEntropy_sequences() :+ * Builds entropy for the super-block sequences.+ * Stores symbol compression modes and fse table to fseMetadata.+ * @return : size of fse tables or error code */+static size_t ZSTD_buildSuperBlockEntropy_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)+{+ BYTE* const wkspStart = (BYTE*)workspace;+ BYTE* const wkspEnd = wkspStart + wkspSize;+ BYTE* const countWkspStart = wkspStart;+ unsigned* const countWksp = (unsigned*)workspace;+ const size_t countWkspSize = (MaxSeq + 1) * sizeof(unsigned);+ BYTE* const cTableWksp = countWkspStart + countWkspSize;+ const size_t cTableWkspSize = wkspEnd-cTableWksp;+ ZSTD_strategy const strategy = cctxParams->cParams.strategy;+ FSE_CTable* CTable_LitLength = nextEntropy->litlengthCTable;+ FSE_CTable* CTable_OffsetBits = nextEntropy->offcodeCTable;+ FSE_CTable* CTable_MatchLength = nextEntropy->matchlengthCTable;+ const BYTE* const ofCodeTable = seqStorePtr->ofCode;+ const BYTE* const llCodeTable = seqStorePtr->llCode;+ const BYTE* const mlCodeTable = seqStorePtr->mlCode;+ size_t const nbSeq = seqStorePtr->sequences - seqStorePtr->sequencesStart;+ BYTE* const ostart = fseMetadata->fseTablesBuffer;+ BYTE* const oend = ostart + sizeof(fseMetadata->fseTablesBuffer);+ BYTE* op = ostart;++ assert(cTableWkspSize >= (1 << MaxFSELog) * sizeof(FSE_FUNCTION_TYPE));+ DEBUGLOG(5, "ZSTD_buildSuperBlockEntropy_sequences (nbSeq=%zu)", nbSeq);+ memset(workspace, 0, wkspSize);++ fseMetadata->lastCountSize = 0;+ /* convert length/distances into codes */+ ZSTD_seqToCodes(seqStorePtr);+ /* build CTable for Literal Lengths */+ { U32 LLtype;+ unsigned max = MaxLL;+ size_t const mostFrequent = HIST_countFast_wksp(countWksp, &max, llCodeTable, nbSeq, workspace, wkspSize); /* can't fail */+ DEBUGLOG(5, "Building LL table");+ nextEntropy->litlength_repeatMode = prevEntropy->litlength_repeatMode;+ LLtype = ZSTD_selectEncodingType(&nextEntropy->litlength_repeatMode,+ countWksp, max, mostFrequent, nbSeq,+ LLFSELog, prevEntropy->litlengthCTable,+ LL_defaultNorm, LL_defaultNormLog,+ ZSTD_defaultAllowed, strategy);+ assert(set_basic < set_compressed && set_rle < set_compressed);+ assert(!(LLtype < set_compressed && nextEntropy->litlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */+ { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_LitLength, LLFSELog, (symbolEncodingType_e)LLtype,+ countWksp, max, llCodeTable, nbSeq, LL_defaultNorm, LL_defaultNormLog, MaxLL,+ prevEntropy->litlengthCTable, sizeof(prevEntropy->litlengthCTable),+ cTableWksp, cTableWkspSize);+ FORWARD_IF_ERROR(countSize, "ZSTD_buildCTable for LitLens failed");+ if (LLtype == set_compressed)+ fseMetadata->lastCountSize = countSize;+ op += countSize;+ fseMetadata->llType = (symbolEncodingType_e) LLtype;+ } }+ /* build CTable for Offsets */+ { U32 Offtype;+ unsigned max = MaxOff;+ size_t const mostFrequent = HIST_countFast_wksp(countWksp, &max, ofCodeTable, nbSeq, workspace, wkspSize); /* can't fail */+ /* We can only use the basic table if max <= DefaultMaxOff, otherwise the offsets are too large */+ ZSTD_defaultPolicy_e const defaultPolicy = (max <= DefaultMaxOff) ? ZSTD_defaultAllowed : ZSTD_defaultDisallowed;+ DEBUGLOG(5, "Building OF table");+ nextEntropy->offcode_repeatMode = prevEntropy->offcode_repeatMode;+ Offtype = ZSTD_selectEncodingType(&nextEntropy->offcode_repeatMode,+ countWksp, max, mostFrequent, nbSeq,+ OffFSELog, prevEntropy->offcodeCTable,+ OF_defaultNorm, OF_defaultNormLog,+ defaultPolicy, strategy);+ assert(!(Offtype < set_compressed && nextEntropy->offcode_repeatMode != FSE_repeat_none)); /* We don't copy tables */+ { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_OffsetBits, OffFSELog, (symbolEncodingType_e)Offtype,+ countWksp, max, ofCodeTable, nbSeq, OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff,+ prevEntropy->offcodeCTable, sizeof(prevEntropy->offcodeCTable),+ cTableWksp, cTableWkspSize);+ FORWARD_IF_ERROR(countSize, "ZSTD_buildCTable for Offsets failed");+ if (Offtype == set_compressed)+ fseMetadata->lastCountSize = countSize;+ op += countSize;+ fseMetadata->ofType = (symbolEncodingType_e) Offtype;+ } }+ /* build CTable for MatchLengths */+ { U32 MLtype;+ unsigned max = MaxML;+ size_t const mostFrequent = HIST_countFast_wksp(countWksp, &max, mlCodeTable, nbSeq, workspace, wkspSize); /* can't fail */+ DEBUGLOG(5, "Building ML table (remaining space : %i)", (int)(oend-op));+ nextEntropy->matchlength_repeatMode = prevEntropy->matchlength_repeatMode;+ MLtype = ZSTD_selectEncodingType(&nextEntropy->matchlength_repeatMode,+ countWksp, max, mostFrequent, nbSeq,+ MLFSELog, prevEntropy->matchlengthCTable,+ ML_defaultNorm, ML_defaultNormLog,+ ZSTD_defaultAllowed, strategy);+ assert(!(MLtype < set_compressed && nextEntropy->matchlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */+ { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_MatchLength, MLFSELog, (symbolEncodingType_e)MLtype,+ countWksp, max, mlCodeTable, nbSeq, ML_defaultNorm, ML_defaultNormLog, MaxML,+ prevEntropy->matchlengthCTable, sizeof(prevEntropy->matchlengthCTable),+ cTableWksp, cTableWkspSize);+ FORWARD_IF_ERROR(countSize, "ZSTD_buildCTable for MatchLengths failed");+ if (MLtype == set_compressed)+ fseMetadata->lastCountSize = countSize;+ op += countSize;+ fseMetadata->mlType = (symbolEncodingType_e) MLtype;+ } }+ assert((size_t) (op-ostart) <= sizeof(fseMetadata->fseTablesBuffer));+ return op-ostart;+}+++/** ZSTD_buildSuperBlockEntropy() :+ * Builds entropy for the super-block.+ * @return : 0 on success or error code */+static size_t+ZSTD_buildSuperBlockEntropy(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;+ DEBUGLOG(5, "ZSTD_buildSuperBlockEntropy");+ entropyMetadata->hufMetadata.hufDesSize =+ ZSTD_buildSuperBlockEntropy_literal(seqStorePtr->litStart, litSize,+ &prevEntropy->huf, &nextEntropy->huf,+ &entropyMetadata->hufMetadata,+ ZSTD_disableLiteralsCompression(cctxParams),+ workspace, wkspSize);+ FORWARD_IF_ERROR(entropyMetadata->hufMetadata.hufDesSize, "ZSTD_buildSuperBlockEntropy_literal failed");+ entropyMetadata->fseMetadata.fseTablesSize =+ ZSTD_buildSuperBlockEntropy_sequences(seqStorePtr,+ &prevEntropy->fse, &nextEntropy->fse,+ cctxParams,+ &entropyMetadata->fseMetadata,+ workspace, wkspSize);+ FORWARD_IF_ERROR(entropyMetadata->fseMetadata.fseTablesSize, "ZSTD_buildSuperBlockEntropy_sequences failed");+ return 0;+}++/** ZSTD_compressSubBlock_literal() :+ * Compresses literals section for a sub-block.+ * When we have to write the Huffman table we will sometimes choose a header+ * size larger than necessary. This is because we have to pick the header size+ * before we know the table size + compressed size, so we have a bound on the+ * table size. If we guessed incorrectly, we fall back to uncompressed literals.+ *+ * We write the header when writeEntropy=1 and set entropyWrriten=1 when we succeeded+ * in writing the header, otherwise it is set to 0.+ *+ * hufMetadata->hType has literals block type info.+ * If it is set_basic, all sub-blocks literals section will be Raw_Literals_Block.+ * If it is set_rle, all sub-blocks literals section will be RLE_Literals_Block.+ * If it is set_compressed, first sub-block's literals section will be Compressed_Literals_Block+ * 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 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)+{+ size_t const header = writeEntropy ? 200 : 0;+ size_t const lhSize = 3 + (litSize >= (1 KB - header)) + (litSize >= (16 KB - header));+ BYTE* const ostart = (BYTE*)dst;+ BYTE* const oend = ostart + dstSize;+ BYTE* op = ostart + lhSize;+ U32 const singleStream = lhSize == 3;+ 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;+ if (litSize == 0 || hufMetadata->hType == set_basic) {+ DEBUGLOG(5, "ZSTD_compressSubBlock_literal using raw literal");+ return ZSTD_noCompressLiterals(dst, dstSize, literals, litSize);+ } else if (hufMetadata->hType == set_rle) {+ DEBUGLOG(5, "ZSTD_compressSubBlock_literal using rle literal");+ return ZSTD_compressRleLiteralsBlock(dst, dstSize, literals, litSize);+ }++ assert(litSize > 0);+ assert(hufMetadata->hType == set_compressed || hufMetadata->hType == set_repeat);++ if (writeEntropy && hufMetadata->hType == set_compressed) {+ memcpy(op, hufMetadata->hufDesBuffer, hufMetadata->hufDesSize);+ op += hufMetadata->hufDesSize;+ cLitSize += hufMetadata->hufDesSize;+ 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);+ op += cSize;+ cLitSize += cSize;+ if (cSize == 0 || ERR_isError(cSize)) {+ DEBUGLOG(5, "Failed to write entropy tables %s", ZSTD_getErrorName(cSize));+ return 0;+ }+ /* If we expand and we aren't writing a header then emit uncompressed */+ if (!writeEntropy && cLitSize >= litSize) {+ DEBUGLOG(5, "ZSTD_compressSubBlock_literal using raw literal because uncompressible");+ return ZSTD_noCompressLiterals(dst, dstSize, literals, litSize);+ }+ /* If we are writing headers then allow expansion that doesn't change our header size. */+ if (lhSize < (size_t)(3 + (cLitSize >= 1 KB) + (cLitSize >= 16 KB))) {+ assert(cLitSize > litSize);+ DEBUGLOG(5, "Literals expanded beyond allowed header size");+ return ZSTD_noCompressLiterals(dst, dstSize, literals, litSize);+ }+ DEBUGLOG(5, "ZSTD_compressSubBlock_literal (cSize=%zu)", cSize);+ }++ /* Build header */+ switch(lhSize)+ {+ case 3: /* 2 - 2 - 10 - 10 */+ { U32 const lhc = hType + ((!singleStream) << 2) + ((U32)litSize<<4) + ((U32)cLitSize<<14);+ MEM_writeLE24(ostart, lhc);+ break;+ }+ case 4: /* 2 - 2 - 14 - 14 */+ { U32 const lhc = hType + (2 << 2) + ((U32)litSize<<4) + ((U32)cLitSize<<18);+ MEM_writeLE32(ostart, lhc);+ break;+ }+ case 5: /* 2 - 2 - 18 - 18 */+ { U32 const lhc = hType + (3 << 2) + ((U32)litSize<<4) + ((U32)cLitSize<<22);+ MEM_writeLE32(ostart, lhc);+ ostart[4] = (BYTE)(cLitSize >> 10);+ break;+ }+ default: /* not possible : lhSize is {3,4,5} */+ assert(0);+ }+ *entropyWritten = 1;+ DEBUGLOG(5, "Compressed literals: %u -> %u", (U32)litSize, (U32)(op-ostart));+ return op-ostart;+}++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;+ size_t matchLengthSum = 0;+ size_t litLengthSum = 0;+ while (send-sp > 0) {+ ZSTD_sequenceLength const seqLen = ZSTD_getSequenceLength(seqStore, sp);+ litLengthSum += seqLen.litLength;+ matchLengthSum += seqLen.matchLength;+ sp++;+ }+ assert(litLengthSum <= litSize);+ if (!lastSequence) {+ assert(litLengthSum == litSize);+ }+ return matchLengthSum + litSize;+}++/** ZSTD_compressSubBlock_sequences() :+ * Compresses sequences section for a sub-block.+ * fseMetadata->llType, fseMetadata->ofType, and fseMetadata->mlType have+ * symbol compression modes for the super-block.+ * The first successfully compressed block will have these in its header.+ * We set entropyWritten=1 when we succeed in compressing the sequences.+ * The following sub-blocks will always have repeat mode.+ * @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)+{+ const int longOffsets = cctxParams->cParams.windowLog > STREAM_ACCUMULATOR_MIN;+ BYTE* const ostart = (BYTE*)dst;+ BYTE* const oend = ostart + dstCapacity;+ BYTE* op = ostart;+ BYTE* seqHead;++ DEBUGLOG(5, "ZSTD_compressSubBlock_sequences (nbSeq=%zu, writeEntropy=%d, longOffsets=%d)", nbSeq, writeEntropy, longOffsets);++ *entropyWritten = 0;+ /* Sequences Header */+ RETURN_ERROR_IF((oend-op) < 3 /*max nbSeq Size*/ + 1 /*seqHead*/,+ dstSize_tooSmall, "");+ if (nbSeq < 0x7F)+ *op++ = (BYTE)nbSeq;+ else if (nbSeq < LONGNBSEQ)+ op[0] = (BYTE)((nbSeq>>8) + 0x80), op[1] = (BYTE)nbSeq, op+=2;+ else+ op[0]=0xFF, MEM_writeLE16(op+1, (U16)(nbSeq - LONGNBSEQ)), op+=3;+ if (nbSeq==0) {+ return op - ostart;+ }++ /* seqHead : flags for FSE encoding type */+ seqHead = op++;++ DEBUGLOG(5, "ZSTD_compressSubBlock_sequences (seqHeadSize=%u)", (unsigned)(op-ostart));++ if (writeEntropy) {+ const U32 LLtype = fseMetadata->llType;+ const U32 Offtype = fseMetadata->ofType;+ const U32 MLtype = fseMetadata->mlType;+ DEBUGLOG(5, "ZSTD_compressSubBlock_sequences (fseTablesSize=%zu)", fseMetadata->fseTablesSize);+ *seqHead = (BYTE)((LLtype<<6) + (Offtype<<4) + (MLtype<<2));+ memcpy(op, fseMetadata->fseTablesBuffer, fseMetadata->fseTablesSize);+ op += fseMetadata->fseTablesSize;+ } else {+ const U32 repeat = set_repeat;+ *seqHead = (BYTE)((repeat<<6) + (repeat<<4) + (repeat<<2));+ }++ { size_t const bitstreamSize = ZSTD_encodeSequences(+ op, oend - op,+ fseTables->matchlengthCTable, mlCode,+ fseTables->offcodeCTable, ofCode,+ fseTables->litlengthCTable, llCode,+ sequences, nbSeq,+ longOffsets, bmi2);+ FORWARD_IF_ERROR(bitstreamSize, "ZSTD_encodeSequences failed");+ op += bitstreamSize;+ /* zstd versions <= 1.3.4 mistakenly report corruption when+ * FSE_readNCount() receives a buffer < 4 bytes.+ * Fixed by https://github.com/facebook/zstd/pull/1146.+ * This can happen when the last set_compressed table present is 2+ * bytes and the bitstream is only one byte.+ * In this exceedingly rare case, we will simply emit an uncompressed+ * block, since it isn't worth optimizing.+ */+#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION+ if (writeEntropy && fseMetadata->lastCountSize && fseMetadata->lastCountSize + bitstreamSize < 4) {+ /* NCountSize >= 2 && bitstreamSize > 0 ==> lastCountSize == 3 */+ assert(fseMetadata->lastCountSize + bitstreamSize == 3);+ DEBUGLOG(5, "Avoiding bug in zstd decoder in versions <= 1.3.4 by "+ "emitting an uncompressed block.");+ return 0;+ }+#endif+ DEBUGLOG(5, "ZSTD_compressSubBlock_sequences (bitstreamSize=%zu)", bitstreamSize);+ }++ /* zstd versions <= 1.4.0 mistakenly report error when+ * sequences section body size is less than 3 bytes.+ * Fixed by https://github.com/facebook/zstd/pull/1664.+ * This can happen when the previous sequences section block is compressed+ * with rle mode and the current block's sequences section is compressed+ * with repeat mode where sequences section body size can be 1 byte.+ */+#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION+ if (op-seqHead < 4) {+ DEBUGLOG(5, "Avoiding bug in zstd decoder in versions <= 1.4.0 by emitting "+ "an uncompressed block when sequences are < 4 bytes");+ return 0;+ }+#endif++ *entropyWritten = 1;+ return op - ostart;+}++/** ZSTD_compressSubBlock() :+ * Compresses a single sub-block.+ * @return : compressed size of the sub-block+ * Or 0 if it failed to compress. */+static size_t ZSTD_compressSubBlock(const ZSTD_entropyCTables_t* entropy,+ const ZSTD_entropyCTablesMetadata_t* entropyMetadata,+ const seqDef* sequences, size_t nbSeq,+ const BYTE* literals, size_t litSize,+ const BYTE* llCode, const BYTE* mlCode, const BYTE* ofCode,+ const ZSTD_CCtx_params* cctxParams,+ void* dst, size_t dstCapacity,+ const int bmi2,+ int writeLitEntropy, int writeSeqEntropy,+ int* litEntropyWritten, int* seqEntropyWritten,+ U32 lastBlock)+{+ BYTE* const ostart = (BYTE*)dst;+ BYTE* const oend = ostart + dstCapacity;+ BYTE* op = ostart + ZSTD_blockHeaderSize;+ DEBUGLOG(5, "ZSTD_compressSubBlock (litSize=%zu, nbSeq=%zu, writeLitEntropy=%d, writeSeqEntropy=%d, lastBlock=%d)",+ litSize, nbSeq, writeLitEntropy, writeSeqEntropy, lastBlock);+ { size_t cLitSize = ZSTD_compressSubBlock_literal((const HUF_CElt*)entropy->huf.CTable,+ &entropyMetadata->hufMetadata, literals, litSize,+ op, oend-op, bmi2, writeLitEntropy, litEntropyWritten);+ FORWARD_IF_ERROR(cLitSize, "ZSTD_compressSubBlock_literal failed");+ if (cLitSize == 0) return 0;+ op += cLitSize;+ }+ { size_t cSeqSize = ZSTD_compressSubBlock_sequences(&entropy->fse,+ &entropyMetadata->fseMetadata,+ sequences, nbSeq,+ llCode, mlCode, ofCode,+ cctxParams,+ op, oend-op,+ bmi2, writeSeqEntropy, seqEntropyWritten);+ FORWARD_IF_ERROR(cSeqSize, "ZSTD_compressSubBlock_sequences failed");+ if (cSeqSize == 0) return 0;+ op += cSeqSize;+ }+ /* Write block header */+ { size_t cSize = (op-ostart)-ZSTD_blockHeaderSize;+ U32 const cBlockHeader24 = lastBlock + (((U32)bt_compressed)<<1) + (U32)(cSize << 3);+ MEM_writeLE24(ostart, cBlockHeader24);+ }+ return op-ostart;+}++static size_t ZSTD_estimateSubBlockSize_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 = 255;+ size_t literalSectionHeaderSize = 3; /* Use hard coded size of 3 bytes */++ if (hufMetadata->hType == set_basic) return litSize;+ else if (hufMetadata->hType == set_rle) return 1;+ else if (hufMetadata->hType == set_compressed || hufMetadata->hType == set_repeat) {+ size_t const largest = HIST_count_wksp (countWksp, &maxSymbolValue, (const BYTE*)literals, litSize, workspace, wkspSize);+ if (ZSTD_isError(largest)) return litSize;+ { size_t cLitSizeEstimate = HUF_estimateCompressedSize((const HUF_CElt*)huf->CTable, countWksp, maxSymbolValue);+ if (writeEntropy) cLitSizeEstimate += hufMetadata->hufDesSize;+ return cLitSizeEstimate + literalSectionHeaderSize;+ } }+ assert(0); /* impossible */+ return 0;+}++static size_t ZSTD_estimateSubBlockSize_symbolType(symbolEncodingType_e type,+ const BYTE* codeTable, unsigned maxCode,+ size_t nbSeq, const FSE_CTable* fseCTable,+ const U32* additionalBits,+ short const* defaultNorm, U32 defaultNormLog,+ void* workspace, size_t wkspSize)+{+ unsigned* const countWksp = (unsigned*)workspace;+ const BYTE* ctp = codeTable;+ const BYTE* const ctStart = ctp;+ const BYTE* const ctEnd = ctStart + nbSeq;+ size_t cSymbolTypeSizeEstimateInBits = 0;+ unsigned max = maxCode;++ HIST_countFast_wksp(countWksp, &max, codeTable, nbSeq, workspace, wkspSize); /* can't fail */+ if (type == set_basic) {+ cSymbolTypeSizeEstimateInBits = ZSTD_crossEntropyCost(defaultNorm, defaultNormLog, countWksp, max);+ } else if (type == set_rle) {+ cSymbolTypeSizeEstimateInBits = 0;+ } else if (type == set_compressed || type == set_repeat) {+ cSymbolTypeSizeEstimateInBits = ZSTD_fseBitCost(fseCTable, countWksp, max);+ }+ if (ZSTD_isError(cSymbolTypeSizeEstimateInBits)) return nbSeq * 10;+ while (ctp < ctEnd) {+ if (additionalBits) cSymbolTypeSizeEstimateInBits += additionalBits[*ctp];+ else cSymbolTypeSizeEstimateInBits += *ctp; /* for offset, offset code is also the number of additional bits */+ ctp++;+ }+ return cSymbolTypeSizeEstimateInBits / 8;+}++static size_t ZSTD_estimateSubBlockSize_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 = 3; /* Use hard coded size of 3 bytes */+ size_t cSeqSizeEstimate = 0;+ cSeqSizeEstimate += ZSTD_estimateSubBlockSize_symbolType(fseMetadata->ofType, ofCodeTable, MaxOff,+ nbSeq, fseTables->offcodeCTable, NULL,+ OF_defaultNorm, OF_defaultNormLog,+ workspace, wkspSize);+ cSeqSizeEstimate += ZSTD_estimateSubBlockSize_symbolType(fseMetadata->llType, llCodeTable, MaxLL,+ nbSeq, fseTables->litlengthCTable, LL_bits,+ LL_defaultNorm, LL_defaultNormLog,+ workspace, wkspSize);+ cSeqSizeEstimate += ZSTD_estimateSubBlockSize_symbolType(fseMetadata->mlType, mlCodeTable, MaxML,+ nbSeq, fseTables->matchlengthCTable, ML_bits,+ ML_defaultNorm, ML_defaultNormLog,+ workspace, wkspSize);+ if (writeEntropy) cSeqSizeEstimate += fseMetadata->fseTablesSize;+ return cSeqSizeEstimate + sequencesSectionHeaderSize;+}++static size_t ZSTD_estimateSubBlockSize(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 cSizeEstimate = 0;+ cSizeEstimate += ZSTD_estimateSubBlockSize_literal(literals, litSize,+ &entropy->huf, &entropyMetadata->hufMetadata,+ workspace, wkspSize, writeLitEntropy);+ cSizeEstimate += ZSTD_estimateSubBlockSize_sequences(ofCodeTable, llCodeTable, mlCodeTable,+ nbSeq, &entropy->fse, &entropyMetadata->fseMetadata,+ workspace, wkspSize, writeSeqEntropy);+ return cSizeEstimate + ZSTD_blockHeaderSize;+}++static int ZSTD_needSequenceEntropyTables(ZSTD_fseCTablesMetadata_t const* fseMetadata)+{+ if (fseMetadata->llType == set_compressed || fseMetadata->llType == set_rle)+ return 1;+ if (fseMetadata->mlType == set_compressed || fseMetadata->mlType == set_rle)+ return 1;+ if (fseMetadata->ofType == set_compressed || fseMetadata->ofType == set_rle)+ return 1;+ return 0;+}++/** ZSTD_compressSubBlock_multi() :+ * Breaks super-block into multiple sub-blocks and compresses them.+ * Entropy will be written to the first block.+ * The following blocks will use repeat mode to compress.+ * All sub-blocks are compressed blocks (no raw or rle blocks).+ * @return : compressed size of the super block (which is multiple ZSTD blocks)+ * Or 0 if it failed to compress. */+static size_t ZSTD_compressSubBlock_multi(const seqStore_t* seqStorePtr,+ const ZSTD_compressedBlockState_t* prevCBlock,+ ZSTD_compressedBlockState_t* nextCBlock,+ const ZSTD_entropyCTablesMetadata_t* entropyMetadata,+ const ZSTD_CCtx_params* cctxParams,+ void* dst, size_t dstCapacity,+ const void* src, size_t srcSize,+ const int bmi2, U32 lastBlock,+ void* workspace, size_t wkspSize)+{+ const seqDef* const sstart = seqStorePtr->sequencesStart;+ const seqDef* const send = seqStorePtr->sequences;+ const seqDef* sp = sstart;+ const BYTE* const lstart = seqStorePtr->litStart;+ const BYTE* const lend = seqStorePtr->lit;+ const BYTE* lp = lstart;+ BYTE const* ip = (BYTE const*)src;+ BYTE const* const iend = ip + srcSize;+ BYTE* const ostart = (BYTE*)dst;+ BYTE* const oend = ostart + dstCapacity;+ BYTE* op = ostart;+ const BYTE* llCodePtr = seqStorePtr->llCode;+ const BYTE* mlCodePtr = seqStorePtr->mlCode;+ const BYTE* ofCodePtr = seqStorePtr->ofCode;+ size_t targetCBlockSize = cctxParams->targetCBlockSize;+ size_t litSize, seqCount;+ int writeLitEntropy = entropyMetadata->hufMetadata.hType == set_compressed;+ int writeSeqEntropy = 1;+ int lastSequence = 0;++ DEBUGLOG(5, "ZSTD_compressSubBlock_multi (litSize=%u, nbSeq=%u)",+ (unsigned)(lend-lp), (unsigned)(send-sstart));++ litSize = 0;+ seqCount = 0;+ do {+ size_t cBlockSizeEstimate = 0;+ if (sstart == send) {+ lastSequence = 1;+ } else {+ const seqDef* const sequence = sp + seqCount;+ lastSequence = sequence == send - 1;+ litSize += ZSTD_getSequenceLength(seqStorePtr, sequence).litLength;+ seqCount++;+ }+ if (lastSequence) {+ assert(lp <= lend);+ assert(litSize <= (size_t)(lend - lp));+ litSize = (size_t)(lend - lp);+ }+ /* I think there is an optimization opportunity here.+ * Calling ZSTD_estimateSubBlockSize for every sequence can be wasteful+ * since it recalculates estimate from scratch.+ * For example, it would recount literal distribution and symbol codes everytime.+ */+ cBlockSizeEstimate = ZSTD_estimateSubBlockSize(lp, litSize, ofCodePtr, llCodePtr, mlCodePtr, seqCount,+ &nextCBlock->entropy, entropyMetadata,+ workspace, wkspSize, writeLitEntropy, writeSeqEntropy);+ if (cBlockSizeEstimate > targetCBlockSize || lastSequence) {+ int litEntropyWritten = 0;+ int seqEntropyWritten = 0;+ const size_t decompressedSize = ZSTD_seqDecompressedSize(seqStorePtr, sp, seqCount, litSize, lastSequence);+ const size_t cSize = ZSTD_compressSubBlock(&nextCBlock->entropy, entropyMetadata,+ sp, seqCount,+ lp, litSize,+ llCodePtr, mlCodePtr, ofCodePtr,+ cctxParams,+ op, oend-op,+ bmi2, writeLitEntropy, writeSeqEntropy,+ &litEntropyWritten, &seqEntropyWritten,+ lastBlock && lastSequence);+ FORWARD_IF_ERROR(cSize, "ZSTD_compressSubBlock failed");+ if (cSize > 0 && cSize < decompressedSize) {+ DEBUGLOG(5, "Committed the sub-block");+ assert(ip + decompressedSize <= iend);+ ip += decompressedSize;+ sp += seqCount;+ lp += litSize;+ op += cSize;+ llCodePtr += seqCount;+ mlCodePtr += seqCount;+ ofCodePtr += seqCount;+ litSize = 0;+ seqCount = 0;+ /* Entropy only needs to be written once */+ if (litEntropyWritten) {+ writeLitEntropy = 0;+ }+ if (seqEntropyWritten) {+ writeSeqEntropy = 0;+ }+ }+ }+ } while (!lastSequence);+ if (writeLitEntropy) {+ DEBUGLOG(5, "ZSTD_compressSubBlock_multi has literal entropy tables unwritten");+ memcpy(&nextCBlock->entropy.huf, &prevCBlock->entropy.huf, sizeof(prevCBlock->entropy.huf));+ }+ if (writeSeqEntropy && ZSTD_needSequenceEntropyTables(&entropyMetadata->fseMetadata)) {+ /* If we haven't written our entropy tables, then we've violated our contract and+ * must emit an uncompressed block.+ */+ DEBUGLOG(5, "ZSTD_compressSubBlock_multi has sequence entropy tables unwritten");+ return 0;+ }+ if (ip < iend) {+ size_t const cSize = ZSTD_noCompressBlock(op, oend - op, ip, iend - ip, lastBlock);+ DEBUGLOG(5, "ZSTD_compressSubBlock_multi last sub-block uncompressed, %zu bytes", (size_t)(iend - ip));+ FORWARD_IF_ERROR(cSize, "ZSTD_noCompressBlock failed");+ assert(cSize != 0);+ op += cSize;+ /* We have to regenerate the repcodes because we've skipped some sequences */+ if (sp < send) {+ seqDef const* seq;+ repcodes_t rep;+ memcpy(&rep, prevCBlock->rep, sizeof(rep)); + for (seq = sstart; seq < sp; ++seq) {+ rep = ZSTD_updateRep(rep.rep, seq->offset - 1, ZSTD_getSequenceLength(seqStorePtr, seq).litLength == 0);+ }+ memcpy(nextCBlock->rep, &rep, sizeof(rep));+ }+ }+ DEBUGLOG(5, "ZSTD_compressSubBlock_multi compressed");+ return op-ostart;+}++size_t ZSTD_compressSuperBlock(ZSTD_CCtx* zc,+ void* dst, size_t dstCapacity,+ void const* src, size_t srcSize,+ unsigned lastBlock) {+ ZSTD_entropyCTablesMetadata_t entropyMetadata;++ FORWARD_IF_ERROR(ZSTD_buildSuperBlockEntropy(&zc->seqStore,+ &zc->blockState.prevCBlock->entropy,+ &zc->blockState.nextCBlock->entropy,+ &zc->appliedParams,+ &entropyMetadata,+ zc->entropyWorkspace, HUF_WORKSPACE_SIZE /* statically allocated in resetCCtx */), "");++ return ZSTD_compressSubBlock_multi(&zc->seqStore,+ zc->blockState.prevCBlock,+ zc->blockState.nextCBlock,+ &entropyMetadata,+ &zc->appliedParams,+ dst, dstCapacity,+ src, srcSize,+ zc->bmi2, lastBlock,+ zc->entropyWorkspace, HUF_WORKSPACE_SIZE /* statically allocated in resetCCtx */);+}
+ zstd/lib/compress/zstd_compress_superblock.h view
@@ -0,0 +1,32 @@+/*+ * Copyright (c) 2016-2020, 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.+ */++#ifndef ZSTD_COMPRESS_ADVANCED_H+#define ZSTD_COMPRESS_ADVANCED_H++/*-*************************************+* Dependencies+***************************************/++#include "../zstd.h" /* ZSTD_CCtx */++/*-*************************************+* Target Compressed Block Size+***************************************/++/* ZSTD_compressSuperBlock() :+ * Used to compress a super block when targetCBlockSize is being used.+ * The given block will be compressed into multiple sub blocks that are around targetCBlockSize. */+size_t ZSTD_compressSuperBlock(ZSTD_CCtx* zc,+ void* dst, size_t dstCapacity,+ void const* src, size_t srcSize,+ unsigned lastBlock);++#endif /* ZSTD_COMPRESS_ADVANCED_H */
+ zstd/lib/compress/zstd_cwksp.h view
@@ -0,0 +1,525 @@+/*+ * Copyright (c) 2016-2020, 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.+ */++#ifndef ZSTD_CWKSP_H+#define ZSTD_CWKSP_H++/*-*************************************+* Dependencies+***************************************/+#include "../common/zstd_internal.h"++#if defined (__cplusplus)+extern "C" {+#endif++/*-*************************************+* Constants+***************************************/++/* Since the workspace is effectively its own little malloc implementation /+ * arena, when we run under ASAN, we should similarly insert redzones between+ * each internal element of the workspace, so ASAN will catch overruns that+ * reach outside an object but that stay inside the workspace.+ *+ * This defines the size of that redzone.+ */+#ifndef ZSTD_CWKSP_ASAN_REDZONE_SIZE+#define ZSTD_CWKSP_ASAN_REDZONE_SIZE 128+#endif++/*-*************************************+* Structures+***************************************/+typedef enum {+ ZSTD_cwksp_alloc_objects,+ ZSTD_cwksp_alloc_buffers,+ ZSTD_cwksp_alloc_aligned+} ZSTD_cwksp_alloc_phase_e;++/**+ * Zstd fits all its internal datastructures into a single continuous buffer,+ * so that it only needs to perform a single OS allocation (or so that a buffer+ * can be provided to it and it can perform no allocations at all). This buffer+ * is called the workspace.+ *+ * Several optimizations complicate that process of allocating memory ranges+ * from this workspace for each internal datastructure:+ *+ * - These different internal datastructures have different setup requirements:+ *+ * - The static objects need to be cleared once and can then be trivially+ * reused for each compression.+ *+ * - Various buffers don't need to be initialized at all--they are always+ * written into before they're read.+ *+ * - The matchstate tables have a unique requirement that they don't need+ * their memory to be totally cleared, but they do need the memory to have+ * some bound, i.e., a guarantee that all values in the memory they've been+ * allocated is less than some maximum value (which is the starting value+ * for the indices that they will then use for compression). When this+ * guarantee is provided to them, they can use the memory without any setup+ * work. When it can't, they have to clear the area.+ *+ * - These buffers also have different alignment requirements.+ *+ * - We would like to reuse the objects in the workspace for multiple+ * compressions without having to perform any expensive reallocation or+ * reinitialization work.+ *+ * - We would like to be able to efficiently reuse the workspace across+ * multiple compressions **even when the compression parameters change** and+ * we need to resize some of the objects (where possible).+ *+ * To attempt to manage this buffer, given these constraints, the ZSTD_cwksp+ * abstraction was created. It works as follows:+ *+ * Workspace Layout:+ *+ * [ ... workspace ... ]+ * [objects][tables ... ->] free space [<- ... aligned][<- ... buffers]+ *+ * The various objects that live in the workspace are divided into the+ * following categories, and are allocated separately:+ *+ * - Static objects: this is optionally the enclosing ZSTD_CCtx or ZSTD_CDict,+ * so that literally everything fits in a single buffer. Note: if present,+ * this must be the first object in the workspace, since ZSTD_free{CCtx,+ * CDict}() rely on a pointer comparison to see whether one or two frees are+ * required.+ *+ * - Fixed size objects: these are fixed-size, fixed-count objects that are+ * nonetheless "dynamically" allocated in the workspace so that we can+ * control how they're initialized separately from the broader ZSTD_CCtx.+ * Examples:+ * - Entropy Workspace+ * - 2 x ZSTD_compressedBlockState_t+ * - CDict dictionary contents+ *+ * - Tables: these are any of several different datastructures (hash tables,+ * chain tables, binary trees) that all respect a common format: they are+ * uint32_t arrays, all of whose values are between 0 and (nextSrc - base).+ * Their sizes depend on the cparams.+ *+ * - Aligned: these buffers are used for various purposes that require 4 byte+ * alignment, but don't require any initialization before they're used.+ *+ * - Buffers: these buffers are used for various purposes that don't require+ * any alignment or initialization before they're used. This means they can+ * be moved around at no cost for a new compression.+ *+ * Allocating Memory:+ *+ * The various types of objects must be allocated in order, so they can be+ * correctly packed into the workspace buffer. That order is:+ *+ * 1. Objects+ * 2. Buffers+ * 3. Aligned+ * 4. Tables+ *+ * Attempts to reserve objects of different types out of order will fail.+ */+typedef struct {+ void* workspace;+ void* workspaceEnd;++ void* objectEnd;+ void* tableEnd;+ void* tableValidEnd;+ void* allocStart;++ int allocFailed;+ int workspaceOversizedDuration;+ ZSTD_cwksp_alloc_phase_e phase;+} ZSTD_cwksp;++/*-*************************************+* Functions+***************************************/++MEM_STATIC size_t ZSTD_cwksp_available_space(ZSTD_cwksp* ws);++MEM_STATIC void ZSTD_cwksp_assert_internal_consistency(ZSTD_cwksp* ws) {+ (void)ws;+ assert(ws->workspace <= ws->objectEnd);+ assert(ws->objectEnd <= ws->tableEnd);+ assert(ws->objectEnd <= ws->tableValidEnd);+ assert(ws->tableEnd <= ws->allocStart);+ assert(ws->tableValidEnd <= ws->allocStart);+ assert(ws->allocStart <= ws->workspaceEnd);+}++/**+ * Align must be a power of 2.+ */+MEM_STATIC size_t ZSTD_cwksp_align(size_t size, size_t const align) {+ size_t const mask = align - 1;+ assert((align & mask) == 0);+ return (size + mask) & ~mask;+}++/**+ * Use this to determine how much space in the workspace we will consume to+ * allocate this object. (Normally it should be exactly the size of the object,+ * but under special conditions, like ASAN, where we pad each object, it might+ * be larger.)+ *+ * Since tables aren't currently redzoned, you don't need to call through this+ * to figure out how much space you need for the matchState tables. Everything+ * else is though.+ */+MEM_STATIC size_t ZSTD_cwksp_alloc_size(size_t size) {+#if defined (ADDRESS_SANITIZER) && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)+ return size + 2 * ZSTD_CWKSP_ASAN_REDZONE_SIZE;+#else+ return size;+#endif+}++MEM_STATIC void ZSTD_cwksp_internal_advance_phase(+ ZSTD_cwksp* ws, ZSTD_cwksp_alloc_phase_e phase) {+ assert(phase >= ws->phase);+ if (phase > ws->phase) {+ if (ws->phase < ZSTD_cwksp_alloc_buffers &&+ phase >= ZSTD_cwksp_alloc_buffers) {+ ws->tableValidEnd = ws->objectEnd;+ }+ if (ws->phase < ZSTD_cwksp_alloc_aligned &&+ phase >= ZSTD_cwksp_alloc_aligned) {+ /* If unaligned allocations down from a too-large top have left us+ * unaligned, we need to realign our alloc ptr. Technically, this+ * can consume space that is unaccounted for in the neededSpace+ * calculation. However, I believe this can only happen when the+ * workspace is too large, and specifically when it is too large+ * by a larger margin than the space that will be consumed. */+ /* TODO: cleaner, compiler warning friendly way to do this??? */+ ws->allocStart = (BYTE*)ws->allocStart - ((size_t)ws->allocStart & (sizeof(U32)-1));+ if (ws->allocStart < ws->tableValidEnd) {+ ws->tableValidEnd = ws->allocStart;+ }+ }+ ws->phase = phase;+ }+}++/**+ * Returns whether this object/buffer/etc was allocated in this workspace.+ */+MEM_STATIC int ZSTD_cwksp_owns_buffer(const ZSTD_cwksp* ws, const void* ptr) {+ return (ptr != NULL) && (ws->workspace <= ptr) && (ptr <= ws->workspaceEnd);+}++/**+ * Internal function. Do not use directly.+ */+MEM_STATIC void* ZSTD_cwksp_reserve_internal(+ ZSTD_cwksp* ws, size_t bytes, ZSTD_cwksp_alloc_phase_e phase) {+ void* alloc;+ void* bottom = ws->tableEnd;+ ZSTD_cwksp_internal_advance_phase(ws, phase);+ alloc = (BYTE *)ws->allocStart - bytes;++#if defined (ADDRESS_SANITIZER) && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)+ /* over-reserve space */+ alloc = (BYTE *)alloc - 2 * ZSTD_CWKSP_ASAN_REDZONE_SIZE;+#endif++ DEBUGLOG(5, "cwksp: reserving %p %zd bytes, %zd bytes remaining",+ alloc, bytes, ZSTD_cwksp_available_space(ws) - bytes);+ ZSTD_cwksp_assert_internal_consistency(ws);+ assert(alloc >= bottom);+ if (alloc < bottom) {+ DEBUGLOG(4, "cwksp: alloc failed!");+ ws->allocFailed = 1;+ return NULL;+ }+ if (alloc < ws->tableValidEnd) {+ ws->tableValidEnd = alloc;+ }+ ws->allocStart = alloc;++#if defined (ADDRESS_SANITIZER) && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)+ /* Move alloc so there's ZSTD_CWKSP_ASAN_REDZONE_SIZE unused space on+ * either size. */+ alloc = (BYTE *)alloc + ZSTD_CWKSP_ASAN_REDZONE_SIZE;+ __asan_unpoison_memory_region(alloc, bytes);+#endif++ return alloc;+}++/**+ * Reserves and returns unaligned memory.+ */+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 sizeof(unsigned).+ */+MEM_STATIC void* ZSTD_cwksp_reserve_aligned(ZSTD_cwksp* ws, size_t bytes) {+ assert((bytes & (sizeof(U32)-1)) == 0);+ return ZSTD_cwksp_reserve_internal(ws, ZSTD_cwksp_align(bytes, sizeof(U32)), ZSTD_cwksp_alloc_aligned);+}++/**+ * Aligned on sizeof(unsigned). These buffers have the special property that+ * their values remain constrained, allowing us to re-use them without+ * memset()-ing them.+ */+MEM_STATIC void* ZSTD_cwksp_reserve_table(ZSTD_cwksp* ws, size_t bytes) {+ const ZSTD_cwksp_alloc_phase_e phase = ZSTD_cwksp_alloc_aligned;+ void* alloc = ws->tableEnd;+ void* end = (BYTE *)alloc + bytes;+ void* top = ws->allocStart;++ DEBUGLOG(5, "cwksp: reserving %p table %zd bytes, %zd bytes remaining",+ alloc, bytes, ZSTD_cwksp_available_space(ws) - bytes);+ assert((bytes & (sizeof(U32)-1)) == 0);+ ZSTD_cwksp_internal_advance_phase(ws, phase);+ ZSTD_cwksp_assert_internal_consistency(ws);+ assert(end <= top);+ if (end > top) {+ DEBUGLOG(4, "cwksp: table alloc failed!");+ ws->allocFailed = 1;+ return NULL;+ }+ ws->tableEnd = end;++#if defined (ADDRESS_SANITIZER) && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)+ __asan_unpoison_memory_region(alloc, bytes);+#endif++ return alloc;+}++/**+ * Aligned on sizeof(void*).+ */+MEM_STATIC void* ZSTD_cwksp_reserve_object(ZSTD_cwksp* ws, size_t bytes) {+ size_t roundedBytes = ZSTD_cwksp_align(bytes, sizeof(void*));+ void* alloc = ws->objectEnd;+ void* end = (BYTE*)alloc + roundedBytes;++#if defined (ADDRESS_SANITIZER) && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)+ /* over-reserve space */+ end = (BYTE *)end + 2 * ZSTD_CWKSP_ASAN_REDZONE_SIZE;+#endif++ DEBUGLOG(5,+ "cwksp: reserving %p object %zd bytes (rounded to %zd), %zd bytes remaining",+ alloc, bytes, roundedBytes, ZSTD_cwksp_available_space(ws) - roundedBytes);+ assert(((size_t)alloc & (sizeof(void*)-1)) == 0);+ assert((bytes & (sizeof(void*)-1)) == 0);+ ZSTD_cwksp_assert_internal_consistency(ws);+ /* we must be in the first phase, no advance is possible */+ if (ws->phase != ZSTD_cwksp_alloc_objects || end > ws->workspaceEnd) {+ DEBUGLOG(4, "cwksp: object alloc failed!");+ ws->allocFailed = 1;+ return NULL;+ }+ ws->objectEnd = end;+ ws->tableEnd = end;+ ws->tableValidEnd = end;++#if defined (ADDRESS_SANITIZER) && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)+ /* Move alloc so there's ZSTD_CWKSP_ASAN_REDZONE_SIZE unused space on+ * either size. */+ alloc = (BYTE *)alloc + ZSTD_CWKSP_ASAN_REDZONE_SIZE;+ __asan_unpoison_memory_region(alloc, bytes);+#endif++ return alloc;+}++MEM_STATIC void ZSTD_cwksp_mark_tables_dirty(ZSTD_cwksp* ws) {+ DEBUGLOG(4, "cwksp: ZSTD_cwksp_mark_tables_dirty");++#if defined (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. */+ {+ size_t size = (BYTE*)ws->tableValidEnd - (BYTE*)ws->objectEnd;+ assert(__msan_test_shadow(ws->objectEnd, size) == -1);+ __msan_poison(ws->objectEnd, size);+ }+#endif++ assert(ws->tableValidEnd >= ws->objectEnd);+ assert(ws->tableValidEnd <= ws->allocStart);+ ws->tableValidEnd = ws->objectEnd;+ ZSTD_cwksp_assert_internal_consistency(ws);+}++MEM_STATIC void ZSTD_cwksp_mark_tables_clean(ZSTD_cwksp* ws) {+ DEBUGLOG(4, "cwksp: ZSTD_cwksp_mark_tables_clean");+ assert(ws->tableValidEnd >= ws->objectEnd);+ assert(ws->tableValidEnd <= ws->allocStart);+ if (ws->tableValidEnd < ws->tableEnd) {+ ws->tableValidEnd = ws->tableEnd;+ }+ ZSTD_cwksp_assert_internal_consistency(ws);+}++/**+ * Zero the part of the allocated tables not already marked clean.+ */+MEM_STATIC void ZSTD_cwksp_clean_tables(ZSTD_cwksp* ws) {+ DEBUGLOG(4, "cwksp: ZSTD_cwksp_clean_tables");+ assert(ws->tableValidEnd >= ws->objectEnd);+ assert(ws->tableValidEnd <= ws->allocStart);+ if (ws->tableValidEnd < ws->tableEnd) {+ memset(ws->tableValidEnd, 0, (BYTE*)ws->tableEnd - (BYTE*)ws->tableValidEnd);+ }+ ZSTD_cwksp_mark_tables_clean(ws);+}++/**+ * Invalidates table allocations.+ * All other allocations remain valid.+ */+MEM_STATIC void ZSTD_cwksp_clear_tables(ZSTD_cwksp* ws) {+ DEBUGLOG(4, "cwksp: clearing tables!");++#if defined (ADDRESS_SANITIZER) && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)+ {+ size_t size = (BYTE*)ws->tableValidEnd - (BYTE*)ws->objectEnd;+ __asan_poison_memory_region(ws->objectEnd, size);+ }+#endif++ ws->tableEnd = ws->objectEnd;+ ZSTD_cwksp_assert_internal_consistency(ws);+}++/**+ * Invalidates all buffer, aligned, and table allocations.+ * Object allocations remain valid.+ */+MEM_STATIC void ZSTD_cwksp_clear(ZSTD_cwksp* ws) {+ DEBUGLOG(4, "cwksp: clearing!");++#if defined (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. */+ {+ size_t size = (BYTE*)ws->workspaceEnd - (BYTE*)ws->tableValidEnd;+ __msan_poison(ws->tableValidEnd, size);+ }+#endif++#if defined (ADDRESS_SANITIZER) && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)+ {+ size_t size = (BYTE*)ws->workspaceEnd - (BYTE*)ws->objectEnd;+ __asan_poison_memory_region(ws->objectEnd, size);+ }+#endif++ ws->tableEnd = ws->objectEnd;+ ws->allocStart = ws->workspaceEnd;+ ws->allocFailed = 0;+ if (ws->phase > ZSTD_cwksp_alloc_buffers) {+ ws->phase = ZSTD_cwksp_alloc_buffers;+ }+ ZSTD_cwksp_assert_internal_consistency(ws);+}++/**+ * The provided workspace takes ownership of the buffer [start, start+size).+ * Any existing values in the workspace are ignored (the previously managed+ * buffer, if present, must be separately freed).+ */+MEM_STATIC void ZSTD_cwksp_init(ZSTD_cwksp* ws, void* start, size_t size) {+ DEBUGLOG(4, "cwksp: init'ing workspace with %zd bytes", size);+ assert(((size_t)start & (sizeof(void*)-1)) == 0); /* ensure correct alignment */+ ws->workspace = start;+ ws->workspaceEnd = (BYTE*)start + size;+ ws->objectEnd = ws->workspace;+ ws->tableValidEnd = ws->objectEnd;+ ws->phase = ZSTD_cwksp_alloc_objects;+ ZSTD_cwksp_clear(ws);+ ws->workspaceOversizedDuration = 0;+ ZSTD_cwksp_assert_internal_consistency(ws);+}++MEM_STATIC size_t ZSTD_cwksp_create(ZSTD_cwksp* ws, size_t size, ZSTD_customMem customMem) {+ void* workspace = ZSTD_malloc(size, customMem);+ DEBUGLOG(4, "cwksp: creating new workspace with %zd bytes", size);+ RETURN_ERROR_IF(workspace == NULL, memory_allocation, "NULL pointer!");+ ZSTD_cwksp_init(ws, workspace, size);+ return 0;+}++MEM_STATIC void ZSTD_cwksp_free(ZSTD_cwksp* ws, ZSTD_customMem customMem) {+ void *ptr = ws->workspace;+ DEBUGLOG(4, "cwksp: freeing workspace");+ memset(ws, 0, sizeof(ZSTD_cwksp));+ ZSTD_free(ptr, customMem);+}++/**+ * Moves the management of a workspace from one cwksp to another. The src cwksp+ * is left in an invalid state (src must be re-init()'ed before its used again).+ */+MEM_STATIC void ZSTD_cwksp_move(ZSTD_cwksp* dst, ZSTD_cwksp* src) {+ *dst = *src;+ memset(src, 0, sizeof(ZSTD_cwksp));+}++MEM_STATIC size_t ZSTD_cwksp_sizeof(const ZSTD_cwksp* ws) {+ return (size_t)((BYTE*)ws->workspaceEnd - (BYTE*)ws->workspace);+}++MEM_STATIC int ZSTD_cwksp_reserve_failed(const ZSTD_cwksp* ws) {+ return ws->allocFailed;+}++/*-*************************************+* Functions Checking Free Space+***************************************/++MEM_STATIC size_t ZSTD_cwksp_available_space(ZSTD_cwksp* ws) {+ return (size_t)((BYTE*)ws->allocStart - (BYTE*)ws->tableEnd);+}++MEM_STATIC int ZSTD_cwksp_check_available(ZSTD_cwksp* ws, size_t additionalNeededSpace) {+ return ZSTD_cwksp_available_space(ws) >= additionalNeededSpace;+}++MEM_STATIC int ZSTD_cwksp_check_too_large(ZSTD_cwksp* ws, size_t additionalNeededSpace) {+ return ZSTD_cwksp_check_available(+ ws, additionalNeededSpace * ZSTD_WORKSPACETOOLARGE_FACTOR);+}++MEM_STATIC int ZSTD_cwksp_check_wasteful(ZSTD_cwksp* ws, size_t additionalNeededSpace) {+ return ZSTD_cwksp_check_too_large(ws, additionalNeededSpace)+ && ws->workspaceOversizedDuration > ZSTD_WORKSPACETOOLARGE_MAXDURATION;+}++MEM_STATIC void ZSTD_cwksp_bump_oversized_duration(+ ZSTD_cwksp* ws, size_t additionalNeededSpace) {+ if (ZSTD_cwksp_check_too_large(ws, additionalNeededSpace)) {+ ws->workspaceOversizedDuration++;+ } else {+ ws->workspaceOversizedDuration = 0;+ }+}++#if defined (__cplusplus)+}+#endif++#endif /* ZSTD_CWKSP_H */
zstd/lib/compress/zstd_double_fast.c view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -43,8 +43,7 @@ /* Only load extra positions for ZSTD_dtlm_full */ if (dtlm == ZSTD_dtlm_fast) break;- }- }+ } } } @@ -63,7 +62,9 @@ const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart;- const U32 prefixLowestIndex = ms->window.dictLimit;+ const U32 endIndex = (U32)((size_t)(istart - base) + srcSize);+ /* presumes that, if there is a dictionary, it must be using Attach mode */+ const U32 prefixLowestIndex = ZSTD_getLowestPrefixIndex(ms, endIndex, cParams->windowLog); const BYTE* const prefixLowest = base + prefixLowestIndex; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - HASH_READ_SIZE;@@ -93,14 +94,23 @@ dictCParams->hashLog : hBitsL; const U32 dictHBitsS = dictMode == ZSTD_dictMatchState ? dictCParams->chainLog : hBitsS;- const U32 dictAndPrefixLength = (U32)(ip - prefixLowest + dictEnd - dictStart);+ const U32 dictAndPrefixLength = (U32)((ip - prefixLowest) + (dictEnd - dictStart)); + DEBUGLOG(5, "ZSTD_compressBlock_doubleFast_generic");+ assert(dictMode == ZSTD_noDict || dictMode == ZSTD_dictMatchState); + /* if a dictionary is attached, it must be within window range */+ if (dictMode == ZSTD_dictMatchState) {+ assert(ms->window.dictLimit + (1U << cParams->windowLog) >= endIndex);+ }+ /* init */ ip += (dictAndPrefixLength == 0); if (dictMode == ZSTD_noDict) {- U32 const maxRep = (U32)(ip - prefixLowest);+ 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; }@@ -138,7 +148,7 @@ const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend; mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixLowest) + 4; ip++;- ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH);+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, 0, mLength-MINMATCH); goto _match_stored; } @@ -147,7 +157,7 @@ && ((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, ip-anchor, anchor, 0, mLength-MINMATCH);+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, 0, mLength-MINMATCH); goto _match_stored; } @@ -170,8 +180,7 @@ offset = (U32)(current - dictMatchIndexL - dictIndexDelta); while (((ip>anchor) & (dictMatchL>dictStart)) && (ip[-1] == dictMatchL[-1])) { ip--; dictMatchL--; mLength++; } /* catch up */ goto _match_found;- }- }+ } } if (matchIndexS > prefixLowestIndex) { /* check prefix short match */@@ -186,16 +195,17 @@ if (match > dictStart && MEM_read32(match) == MEM_read32(ip)) { goto _search_next_long;- }- }+ } } ip += ((ip-anchor) >> kSearchStrength) + 1;+#if defined(__aarch64__)+ PREFETCH_L1(ip+256);+#endif continue; _search_next_long: - {- size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8);+ { size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); size_t const dictHLNext = ZSTD_hashPtr(ip+1, dictHBitsL, 8); U32 const matchIndexL3 = hashLong[hl3]; const BYTE* matchL3 = base + matchIndexL3;@@ -221,9 +231,7 @@ offset = (U32)(current + 1 - dictMatchIndexL3 - dictIndexDelta); while (((ip>anchor) & (dictMatchL3>dictStart)) && (ip[-1] == dictMatchL3[-1])) { ip--; dictMatchL3--; mLength++; } /* catch up */ goto _match_found;- }- }- }+ } } } /* if no long +1 match, explore the short match we found */ if (dictMode == ZSTD_dictMatchState && matchIndexS < prefixLowestIndex) {@@ -242,7 +250,7 @@ offset_2 = offset_1; offset_1 = offset; - ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, offset + ZSTD_REP_MOVE, mLength-MINMATCH); _match_stored: /* match found */@@ -250,11 +258,14 @@ anchor = ip; if (ip <= ilimit) {- /* Fill Table */- hashLong[ZSTD_hashPtr(base+current+2, hBitsL, 8)] =- hashSmall[ZSTD_hashPtr(base+current+2, hBitsS, mls)] = current+2; /* here because current+2 could be > iend-8 */- hashLong[ZSTD_hashPtr(ip-2, hBitsL, 8)] =- hashSmall[ZSTD_hashPtr(ip-2, hBitsS, mls)] = (U32)(ip-2-base);+ /* Complementary insertion */+ /* done after iLimit test, as candidates could be > iend-8 */+ { U32 const indexToInsert = current+2;+ hashLong[ZSTD_hashPtr(base+indexToInsert, hBitsL, 8)] = indexToInsert;+ hashLong[ZSTD_hashPtr(ip-2, hBitsL, 8)] = (U32)(ip-2-base);+ hashSmall[ZSTD_hashPtr(base+indexToInsert, hBitsS, mls)] = indexToInsert;+ hashSmall[ZSTD_hashPtr(ip-1, hBitsS, mls)] = (U32)(ip-1-base);+ } /* check immediate repcode */ if (dictMode == ZSTD_dictMatchState) {@@ -263,14 +274,14 @@ U32 const repIndex2 = current2 - offset_2; const BYTE* repMatch2 = dictMode == ZSTD_dictMatchState && repIndex2 < prefixLowestIndex ?- dictBase - dictIndexDelta + repIndex2 :+ dictBase + repIndex2 - dictIndexDelta : base + repIndex2; if ( ((U32)((prefixLowestIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { 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, 0, repLength2-MINMATCH);+ ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, repLength2-MINMATCH); hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = current2; hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = current2; ip += repLength2;@@ -278,8 +289,7 @@ continue; } break;- }- }+ } } if (dictMode == ZSTD_noDict) { while ( (ip <= ilimit)@@ -290,18 +300,19 @@ 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, 0, rLength-MINMATCH);+ ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, rLength-MINMATCH); ip += rLength; anchor = ip; continue; /* faster when present ... (?) */- } } } }+ } } }+ } /* while (ip < ilimit) */ /* save reps for next block */ rep[0] = offset_1 ? offset_1 : offsetSaved; rep[1] = offset_2 ? offset_2 : offsetSaved; /* Return the last literals size */- return iend - anchor;+ return (size_t)(iend - anchor); } @@ -360,10 +371,13 @@ const BYTE* anchor = istart; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - 8;- const U32 prefixStartIndex = ms->window.dictLimit; const BYTE* const base = ms->window.base;+ const U32 endIndex = (U32)((size_t)(istart - base) + srcSize);+ const U32 lowLimit = ZSTD_getLowestMatchIndex(ms, endIndex, cParams->windowLog);+ const U32 dictStartIndex = lowLimit;+ const U32 dictLimit = ms->window.dictLimit;+ const U32 prefixStartIndex = (dictLimit > lowLimit) ? dictLimit : lowLimit; const BYTE* const prefixStart = base + prefixStartIndex;- const U32 dictStartIndex = ms->window.lowLimit; const BYTE* const dictBase = ms->window.dictBase; const BYTE* const dictStart = dictBase + dictStartIndex; const BYTE* const dictEnd = dictBase + prefixStartIndex;@@ -371,6 +385,10 @@ DEBUGLOG(5, "ZSTD_compressBlock_doubleFast_extDict_generic (srcSize=%zu)", srcSize); + /* if extDict is invalidated due to maxDistance, switch to "regular" variant */+ if (prefixStartIndex == dictStartIndex)+ return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, mls, ZSTD_noDict);+ /* Search Loop */ while (ip < ilimit) { /* < instead of <=, because (ip+1) */ const size_t hSmall = ZSTD_hashPtr(ip, hBitsS, mls);@@ -396,7 +414,7 @@ const BYTE* repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend; mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixStart) + 4; ip++;- ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH);+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, 0, mLength-MINMATCH); } else { if ((matchLongIndex > dictStartIndex) && (MEM_read64(matchLong) == MEM_read64(ip))) { const BYTE* const matchEnd = matchLongIndex < prefixStartIndex ? dictEnd : iend;@@ -407,7 +425,7 @@ while (((ip>anchor) & (matchLong>lowMatchPtr)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */ offset_2 = offset_1; offset_1 = offset;- ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, offset + ZSTD_REP_MOVE, mLength-MINMATCH); } else if ((matchIndex > dictStartIndex) && (MEM_read32(match) == MEM_read32(ip))) { size_t const h3 = ZSTD_hashPtr(ip+1, hBitsL, 8);@@ -432,23 +450,27 @@ } offset_2 = offset_1; offset_1 = offset;- ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, offset + ZSTD_REP_MOVE, mLength-MINMATCH); } else { ip += ((ip-anchor) >> kSearchStrength) + 1; continue; } } - /* found a match : store it */+ /* move to next sequence start */ ip += mLength; anchor = ip; if (ip <= ilimit) {- /* Fill Table */- hashSmall[ZSTD_hashPtr(base+current+2, hBitsS, mls)] = current+2;- hashLong[ZSTD_hashPtr(base+current+2, hBitsL, 8)] = current+2;- hashSmall[ZSTD_hashPtr(ip-2, hBitsS, mls)] = (U32)(ip-2-base);- hashLong[ZSTD_hashPtr(ip-2, hBitsL, 8)] = (U32)(ip-2-base);+ /* Complementary insertion */+ /* done after iLimit test, as candidates could be > iend-8 */+ { U32 const indexToInsert = current+2;+ hashLong[ZSTD_hashPtr(base+indexToInsert, hBitsL, 8)] = indexToInsert;+ hashLong[ZSTD_hashPtr(ip-2, hBitsL, 8)] = (U32)(ip-2-base);+ hashSmall[ZSTD_hashPtr(base+indexToInsert, hBitsS, mls)] = indexToInsert;+ hashSmall[ZSTD_hashPtr(ip-1, hBitsS, mls)] = (U32)(ip-1-base);+ }+ /* check immediate repcode */ while (ip <= ilimit) { U32 const current2 = (U32)(ip-base);@@ -460,7 +482,7 @@ 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, 0, repLength2-MINMATCH);+ ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, repLength2-MINMATCH); hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = current2; hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = current2; ip += repLength2;@@ -475,7 +497,7 @@ rep[1] = offset_2; /* Return the last literals size */- return iend - anchor;+ return (size_t)(iend - anchor); }
zstd/lib/compress/zstd_double_fast.h view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -15,7 +15,7 @@ extern "C" { #endif -#include "mem.h" /* U32 */+#include "../common/mem.h" /* U32 */ #include "zstd_compress_internal.h" /* ZSTD_CCtx, size_t */ void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms,
zstd/lib/compress/zstd_fast.c view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -8,12 +8,13 @@ * You may select, at your option, one of the above-listed licenses. */ -#include "zstd_compress_internal.h"+#include "zstd_compress_internal.h" /* ZSTD_hashPtr, ZSTD_count, ZSTD_storeSeq */ #include "zstd_fast.h" void ZSTD_fillHashTable(ZSTD_matchState_t* ms,- void const* end, ZSTD_dictTableLoadMethod_e dtlm)+ const void* const end,+ ZSTD_dictTableLoadMethod_e dtlm) { const ZSTD_compressionParameters* const cParams = &ms->cParams; U32* const hashTable = ms->hashTable;@@ -41,8 +42,9 @@ } } } } } -FORCE_INLINE_TEMPLATE-size_t ZSTD_compressBlock_fast_generic(++FORCE_INLINE_TEMPLATE size_t+ZSTD_compressBlock_fast_generic( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize, U32 const mls)@@ -58,7 +60,8 @@ const BYTE* ip0 = istart; const BYTE* ip1; const BYTE* anchor = istart;- const U32 prefixStartIndex = ms->window.dictLimit;+ const U32 endIndex = (U32)((size_t)(istart - base) + srcSize);+ const U32 prefixStartIndex = ZSTD_getLowestPrefixIndex(ms, endIndex, cParams->windowLog); const BYTE* const prefixStart = base + prefixStartIndex; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - HASH_READ_SIZE;@@ -66,15 +69,24 @@ U32 offsetSaved = 0; /* init */+ DEBUGLOG(5, "ZSTD_compressBlock_fast_generic"); ip0 += (ip0 == prefixStart); ip1 = ip0 + 1;- {- U32 const maxRep = (U32)(ip0 - prefixStart);+ { U32 const current = (U32)(ip0 - 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; } /* Main Search Loop */+#ifdef __INTEL_COMPILER+ /* From intel 'The vector pragma indicates that the loop should be+ * vectorized if it is legal to do so'. Can be used together with+ * #pragma ivdep (but have opted to exclude that because intel+ * warns against using it).*/+ #pragma vector always+#endif while (ip1 < ilimit) { /* < instead of <=, because check at ip0+2 */ size_t mLength; BYTE const* ip2 = ip0 + 2;@@ -86,19 +98,25 @@ U32 const current1 = (U32)(ip1-base); U32 const matchIndex0 = hashTable[h0]; U32 const matchIndex1 = hashTable[h1];- BYTE const* repMatch = ip2-offset_1;+ BYTE const* repMatch = ip2 - offset_1; const BYTE* match0 = base + matchIndex0; const BYTE* match1 = base + matchIndex1; U32 offcode;++#if defined(__aarch64__)+ PREFETCH_L1(ip0+256);+#endif+ hashTable[h0] = current0; /* update hash table */ hashTable[h1] = current1; /* update hash table */ assert(ip0 + 1 == ip1); if ((offset_1 > 0) & (MEM_read32(repMatch) == MEM_read32(ip2))) {- mLength = ip2[-1] == repMatch[-1] ? 1 : 0;+ mLength = (ip2[-1] == repMatch[-1]) ? 1 : 0; ip0 = ip2 - mLength; match0 = repMatch - mLength;+ mLength += 4; offcode = 0; goto _match; }@@ -112,8 +130,7 @@ match0 = match1; goto _offset; }- {- size_t const step = ((ip0-anchor) >> (kSearchStrength - 1)) + stepSize;+ { size_t const step = ((size_t)(ip0-anchor) >> (kSearchStrength - 1)) + stepSize; assert(step >= 2); ip0 += step; ip1 += step;@@ -124,19 +141,18 @@ offset_2 = offset_1; offset_1 = (U32)(ip0-match0); offcode = offset_1 + ZSTD_REP_MOVE;- mLength = 0;+ mLength = 4; /* Count the backwards match length */ while (((ip0>anchor) & (match0>prefixStart)) && (ip0[-1] == match0[-1])) { ip0--; match0--; mLength++; } /* catch up */ _match: /* Requires: ip0, match0, offcode */ /* Count the forward length */- mLength += ZSTD_count(ip0+mLength+4, match0+mLength+4, iend) + 4;- ZSTD_storeSeq(seqStore, ip0-anchor, anchor, offcode, mLength-MINMATCH);+ mLength += ZSTD_count(ip0+mLength, match0+mLength, iend);+ ZSTD_storeSeq(seqStore, (size_t)(ip0-anchor), anchor, iend, offcode, mLength-MINMATCH); /* match found */ ip0 += mLength; anchor = ip0;- ip1 = ip0 + 1; if (ip0 <= ilimit) { /* Fill Table */@@ -144,20 +160,18 @@ 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)- && ( (offset_2>0)- & (MEM_read32(ip0) == MEM_read32(ip0 - offset_2)) )) {- /* store sequence */- size_t const rLength = ZSTD_count(ip0+4, ip0+4-offset_2, iend) + 4;- U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff; /* swap offset_2 <=> offset_1 */- hashTable[ZSTD_hashPtr(ip0, hlog, mls)] = (U32)(ip0-base);- ip0 += rLength;- ip1 = ip0 + 1;- ZSTD_storeSeq(seqStore, 0, anchor, 0, rLength-MINMATCH);- anchor = ip0;- continue; /* faster when present (confirmed on gcc-8) ... (?) */- }- }+ if (offset_2 > 0) { /* offset_2==0 means offset_2 is invalidated */+ while ( (ip0 <= ilimit) && (MEM_read32(ip0) == MEM_read32(ip0 - offset_2)) ) {+ /* store sequence */+ size_t const rLength = ZSTD_count(ip0+4, ip0+4-offset_2, iend) + 4;+ { U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff; } /* swap offset_2 <=> offset_1 */+ hashTable[ZSTD_hashPtr(ip0, hlog, mls)] = (U32)(ip0-base);+ ip0 += rLength;+ ZSTD_storeSeq(seqStore, 0 /*litLen*/, anchor, iend, 0 /*offCode*/, rLength-MINMATCH);+ anchor = ip0;+ continue; /* faster when present (confirmed on gcc-8) ... (?) */+ } } }+ ip1 = ip0 + 1; } /* save reps for next block */@@ -165,7 +179,7 @@ rep[1] = offset_2 ? offset_2 : offsetSaved; /* Return the last literals size */- return iend - anchor;+ return (size_t)(iend - anchor); } @@ -173,8 +187,7 @@ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) {- ZSTD_compressionParameters const* cParams = &ms->cParams;- U32 const mls = cParams->minMatch;+ U32 const mls = ms->cParams.minMatch; assert(ms->dictMatchState == NULL); switch(mls) {@@ -222,11 +235,19 @@ const U32 dictAndPrefixLength = (U32)(ip - prefixStart + dictEnd - dictStart); const U32 dictHLog = dictCParams->hashLog; - /* otherwise, we would get index underflow when translating a dict index- * into a local index */+ /* 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);+ assert(endIndex - prefixStartIndex <= maxDistance);+ (void)maxDistance; (void)endIndex; /* these variables are not used when assert() is disabled */++ /* ensure there will be no no underflow+ * when translating a dict index into a local index */ assert(prefixStartIndex >= (U32)(dictEnd - dictBase)); /* init */+ DEBUGLOG(5, "ZSTD_compressBlock_fast_dictMatchState_generic"); ip += (dictAndPrefixLength == 0); /* dictMatchState repCode checks don't currently handle repCode == 0 * disabling. */@@ -251,7 +272,7 @@ 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, ip-anchor, anchor, 0, mLength-MINMATCH);+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, 0, mLength-MINMATCH); } else if ( (matchIndex <= prefixStartIndex) ) { size_t const dictHash = ZSTD_hashPtr(ip, dictHLog, mls); U32 const dictMatchIndex = dictHashTable[dictHash];@@ -271,7 +292,7 @@ } /* catch up */ offset_2 = offset_1; offset_1 = offset;- ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, offset + ZSTD_REP_MOVE, mLength-MINMATCH); } } else if (MEM_read32(match) != MEM_read32(ip)) { /* it's not a match, and we're not going to check the dictionary */@@ -286,7 +307,7 @@ && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ offset_2 = offset_1; offset_1 = offset;- ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, offset + ZSTD_REP_MOVE, mLength-MINMATCH); } /* match found */@@ -311,7 +332,7 @@ const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend; size_t const repLength2 = ZSTD_count_2segments(ip+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, 0, repLength2-MINMATCH);+ ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, repLength2-MINMATCH); hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2; ip += repLength2; anchor = ip;@@ -327,15 +348,14 @@ rep[1] = offset_2 ? offset_2 : offsetSaved; /* Return the last literals size */- return iend - anchor;+ return (size_t)(iend - anchor); } size_t ZSTD_compressBlock_fast_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) {- ZSTD_compressionParameters const* cParams = &ms->cParams;- U32 const mls = cParams->minMatch;+ U32 const mls = ms->cParams.minMatch; assert(ms->dictMatchState != NULL); switch(mls) {@@ -366,15 +386,24 @@ const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart;- const U32 dictStartIndex = ms->window.lowLimit;+ const U32 endIndex = (U32)((size_t)(istart - base) + srcSize);+ const U32 lowLimit = ZSTD_getLowestMatchIndex(ms, endIndex, cParams->windowLog);+ const U32 dictStartIndex = lowLimit; const BYTE* const dictStart = dictBase + dictStartIndex;- const U32 prefixStartIndex = ms->window.dictLimit;+ const U32 dictLimit = ms->window.dictLimit;+ const U32 prefixStartIndex = dictLimit < lowLimit ? lowLimit : dictLimit; const BYTE* const prefixStart = base + prefixStartIndex; const BYTE* const dictEnd = dictBase + prefixStartIndex; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - 8; U32 offset_1=rep[0], offset_2=rep[1]; + DEBUGLOG(5, "ZSTD_compressBlock_fast_extDict_generic (offset_1=%u)", offset_1);++ /* switch to "regular" variant if extDict is invalidated due to maxDistance */+ if (prefixStartIndex == dictStartIndex)+ return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, mls);+ /* Search Loop */ while (ip < ilimit) { /* < instead of <=, because (ip+1) */ const size_t h = ZSTD_hashPtr(ip, hlog, mls);@@ -385,16 +414,18 @@ const U32 repIndex = current + 1 - offset_1; const BYTE* const repBase = repIndex < prefixStartIndex ? dictBase : base; const BYTE* const repMatch = repBase + repIndex;- size_t mLength; hashTable[h] = current; /* update hash table */+ DEBUGLOG(7, "offset_1 = %u , current = %u", offset_1, current); assert(offset_1 <= current +1); /* check repIndex */ if ( (((U32)((prefixStartIndex-1) - repIndex) >= 3) /* intentional underflow */ & (repIndex > dictStartIndex)) && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) {- const BYTE* repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend;- mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixStart) + 4;+ 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, ip-anchor, anchor, 0, mLength-MINMATCH);+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, 0, rLength-MINMATCH);+ ip += rLength;+ anchor = ip; } else { if ( (matchIndex < dictStartIndex) || (MEM_read32(match) != MEM_read32(ip)) ) {@@ -402,21 +433,17 @@ ip += ((ip-anchor) >> kSearchStrength) + stepSize; continue; }- { const BYTE* matchEnd = matchIndex < prefixStartIndex ? dictEnd : iend;- const BYTE* lowMatchPtr = matchIndex < prefixStartIndex ? dictStart : prefixStart;- U32 offset;- mLength = ZSTD_count_2segments(ip+4, match+4, iend, matchEnd, prefixStart) + 4;+ { const BYTE* const matchEnd = matchIndex < prefixStartIndex ? dictEnd : iend;+ const BYTE* const lowMatchPtr = matchIndex < prefixStartIndex ? dictStart : prefixStart;+ U32 const offset = current - 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 = current - matchIndex;- offset_2 = offset_1;- offset_1 = offset;- ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);+ offset_2 = offset_1; offset_1 = offset; /* update offset history */+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, offset + ZSTD_REP_MOVE, mLength-MINMATCH);+ ip += mLength;+ anchor = ip; } } - /* found a match : store it */- ip += mLength;- anchor = ip;- if (ip <= ilimit) { /* Fill Table */ hashTable[ZSTD_hashPtr(base+current+2, hlog, mls)] = current+2;@@ -425,13 +452,13 @@ while (ip <= ilimit) { U32 const current2 = (U32)(ip-base); U32 const repIndex2 = current2 - offset_2;- const BYTE* repMatch2 = repIndex2 < prefixStartIndex ? dictBase + repIndex2 : base + repIndex2;+ const BYTE* const repMatch2 = repIndex2 < prefixStartIndex ? dictBase + repIndex2 : base + repIndex2; if ( (((U32)((prefixStartIndex-1) - repIndex2) >= 3) & (repIndex2 > 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 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */- ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH);+ { U32 const tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; } /* swap offset_2 <=> offset_1 */+ ZSTD_storeSeq(seqStore, 0 /*litlen*/, anchor, iend, 0 /*offcode*/, repLength2-MINMATCH); hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2; ip += repLength2; anchor = ip;@@ -445,7 +472,7 @@ rep[1] = offset_2; /* Return the last literals size */- return iend - anchor;+ return (size_t)(iend - anchor); } @@ -453,8 +480,7 @@ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) {- ZSTD_compressionParameters const* cParams = &ms->cParams;- U32 const mls = cParams->minMatch;+ U32 const mls = ms->cParams.minMatch; switch(mls) { default: /* includes case 3 */
zstd/lib/compress/zstd_fast.h view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -15,7 +15,7 @@ extern "C" { #endif -#include "mem.h" /* U32 */+#include "../common/mem.h" /* U32 */ #include "zstd_compress_internal.h" void ZSTD_fillHashTable(ZSTD_matchState_t* ms,
zstd/lib/compress/zstd_lazy.c view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -83,8 +83,11 @@ U32* largerPtr = smallerPtr + 1; U32 matchIndex = *smallerPtr; /* this candidate is unsorted : next sorted candidate is reached through *smallerPtr, while *largerPtr contains previous unsorted candidate (which is already saved and can be overwritten) */ U32 dummy32; /* to be nullified at the end */- U32 const windowLow = ms->window.lowLimit;+ U32 const windowValid = ms->window.lowLimit;+ U32 const maxDistance = 1U << cParams->windowLog;+ U32 const windowLow = (current - windowValid > maxDistance) ? current - maxDistance : windowValid; + DEBUGLOG(8, "ZSTD_insertDUBT1(%u) (dictLimit=%u, lowLimit=%u)", current, dictLimit, windowLow); assert(current >= btLow);@@ -239,7 +242,7 @@ const BYTE* const base = ms->window.base; U32 const current = (U32)(ip-base);- U32 const windowLow = ms->window.lowLimit;+ U32 const windowLow = ZSTD_getLowestMatchIndex(ms, current, cParams->windowLog); U32* const bt = ms->chainTable; U32 const btLog = cParams->chainLog - 1;@@ -490,8 +493,12 @@ const U32 dictLimit = ms->window.dictLimit; const BYTE* const prefixStart = base + dictLimit; const BYTE* const dictEnd = dictBase + dictLimit;- const U32 lowLimit = ms->window.lowLimit; const U32 current = (U32)(ip-base);+ const U32 maxDistance = 1U << cParams->windowLog;+ const U32 lowestValid = ms->window.lowLimit;+ const U32 withinMaxDistance = (current - lowestValid > maxDistance) ? current - maxDistance : lowestValid;+ const U32 isDictionary = (ms->loadedDictEnd != 0);+ const U32 lowLimit = isDictionary ? lowestValid : withinMaxDistance; const U32 minChain = current > chainSize ? current - chainSize : 0; U32 nbAttempts = 1U << cParams->searchLog; size_t ml=4-1;@@ -612,12 +619,14 @@ /* ******************************* * Common parser - lazy strategy *********************************/-FORCE_INLINE_TEMPLATE-size_t ZSTD_compressBlock_lazy_generic(+typedef enum { search_hashChain, search_binaryTree } searchMethod_e;++FORCE_INLINE_TEMPLATE size_t+ZSTD_compressBlock_lazy_generic( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], const void* src, size_t srcSize,- const U32 searchMethod, const U32 depth,+ const searchMethod_e searchMethod, const U32 depth, ZSTD_dictMode_e const dictMode) { const BYTE* const istart = (const BYTE*)src;@@ -633,8 +642,10 @@ ZSTD_matchState_t* ms, const BYTE* ip, const BYTE* iLimit, size_t* offsetPtr); searchMax_f const searchMax = dictMode == ZSTD_dictMatchState ?- (searchMethod ? ZSTD_BtFindBestMatch_dictMatchState_selectMLS : ZSTD_HcFindBestMatch_dictMatchState_selectMLS) :- (searchMethod ? ZSTD_BtFindBestMatch_selectMLS : ZSTD_HcFindBestMatch_selectMLS);+ (searchMethod==search_binaryTree ? ZSTD_BtFindBestMatch_dictMatchState_selectMLS+ : ZSTD_HcFindBestMatch_dictMatchState_selectMLS) :+ (searchMethod==search_binaryTree ? ZSTD_BtFindBestMatch_selectMLS+ : ZSTD_HcFindBestMatch_selectMLS); U32 offset_1 = rep[0], offset_2 = rep[1], savedOffset=0; const ZSTD_matchState_t* const dms = ms->dictMatchState;@@ -649,13 +660,16 @@ const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ? prefixLowestIndex - (U32)(dictEnd - dictBase) : 0;- const U32 dictAndPrefixLength = (U32)(ip - prefixLowest + dictEnd - dictLowest);+ const U32 dictAndPrefixLength = (U32)((ip - prefixLowest) + (dictEnd - dictLowest)); + DEBUGLOG(5, "ZSTD_compressBlock_lazy_generic (dictMode=%u)", (U32)dictMode);+ /* init */ ip += (dictAndPrefixLength == 0);- ms->nextToUpdate3 = ms->nextToUpdate; if (dictMode == ZSTD_noDict) {- U32 const maxRep = (U32)(ip - prefixLowest);+ U32 const current = (U32)(ip - base);+ U32 const windowLow = ZSTD_getLowestPrefixIndex(ms, current, ms->cParams.windowLog);+ U32 const maxRep = current - windowLow; if (offset_2 > maxRep) savedOffset = offset_2, offset_2 = 0; if (offset_1 > maxRep) savedOffset = offset_1, offset_1 = 0; }@@ -667,6 +681,12 @@ } /* Match Loop */+#if defined(__GNUC__) && defined(__x86_64__)+ /* I've measured random a 5% speed loss on levels 5 & 6 (greedy) when the+ * code alignment is perturbed. To fix the instability align the loop on 32-bytes.+ */+ __asm__(".p2align 5");+#endif while (ip < ilimit) { size_t matchLength=0; size_t offset=0;@@ -800,7 +820,7 @@ /* store sequence */ _storeSequence: { size_t const litLength = start - anchor;- ZSTD_storeSeq(seqStore, litLength, anchor, (U32)offset, matchLength-MINMATCH);+ ZSTD_storeSeq(seqStore, litLength, anchor, iend, (U32)offset, matchLength-MINMATCH); anchor = ip = start + matchLength; } @@ -818,7 +838,7 @@ const BYTE* const repEnd2 = repIndex < prefixLowestIndex ? dictEnd : iend; matchLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd2, prefixLowest) + 4; offset = offset_2; offset_2 = offset_1; offset_1 = (U32)offset; /* swap offset_2 <=> offset_1 */- ZSTD_storeSeq(seqStore, 0, anchor, 0, matchLength-MINMATCH);+ ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, matchLength-MINMATCH); ip += matchLength; anchor = ip; continue;@@ -833,7 +853,7 @@ /* store sequence */ matchLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4; offset = offset_2; offset_2 = offset_1; offset_1 = (U32)offset; /* swap repcodes */- ZSTD_storeSeq(seqStore, 0, anchor, 0, matchLength-MINMATCH);+ ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, matchLength-MINMATCH); ip += matchLength; anchor = ip; continue; /* faster when present ... (?) */@@ -844,7 +864,7 @@ rep[1] = offset_2 ? offset_2 : savedOffset; /* Return the last literals size */- return iend - anchor;+ return (size_t)(iend - anchor); } @@ -852,56 +872,56 @@ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) {- return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, 1, 2, ZSTD_noDict);+ return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_binaryTree, 2, ZSTD_noDict); } size_t ZSTD_compressBlock_lazy2( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) {- return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, 0, 2, ZSTD_noDict);+ return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_hashChain, 2, ZSTD_noDict); } size_t ZSTD_compressBlock_lazy( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) {- return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, 0, 1, ZSTD_noDict);+ return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_hashChain, 1, ZSTD_noDict); } size_t ZSTD_compressBlock_greedy( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) {- return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, 0, 0, ZSTD_noDict);+ return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_hashChain, 0, ZSTD_noDict); } size_t ZSTD_compressBlock_btlazy2_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) {- return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, 1, 2, ZSTD_dictMatchState);+ return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_binaryTree, 2, ZSTD_dictMatchState); } size_t ZSTD_compressBlock_lazy2_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) {- return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, 0, 2, ZSTD_dictMatchState);+ return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_hashChain, 2, ZSTD_dictMatchState); } size_t ZSTD_compressBlock_lazy_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) {- return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, 0, 1, ZSTD_dictMatchState);+ return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_hashChain, 1, ZSTD_dictMatchState); } size_t ZSTD_compressBlock_greedy_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) {- return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, 0, 0, ZSTD_dictMatchState);+ return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_hashChain, 0, ZSTD_dictMatchState); } @@ -910,7 +930,7 @@ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], const void* src, size_t srcSize,- const U32 searchMethod, const U32 depth)+ const searchMethod_e searchMethod, const U32 depth) { const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart;@@ -919,24 +939,31 @@ const BYTE* const ilimit = iend - 8; const BYTE* const base = ms->window.base; const U32 dictLimit = ms->window.dictLimit;- const U32 lowestIndex = ms->window.lowLimit; const BYTE* const prefixStart = base + dictLimit; const BYTE* const dictBase = ms->window.dictBase; const BYTE* const dictEnd = dictBase + dictLimit;- const BYTE* const dictStart = dictBase + lowestIndex;+ const BYTE* const dictStart = dictBase + ms->window.lowLimit;+ const U32 windowLog = ms->cParams.windowLog; typedef size_t (*searchMax_f)( ZSTD_matchState_t* ms, const BYTE* ip, const BYTE* iLimit, size_t* offsetPtr);- searchMax_f searchMax = searchMethod ? ZSTD_BtFindBestMatch_extDict_selectMLS : ZSTD_HcFindBestMatch_extDict_selectMLS;+ searchMax_f searchMax = searchMethod==search_binaryTree ? ZSTD_BtFindBestMatch_extDict_selectMLS : ZSTD_HcFindBestMatch_extDict_selectMLS; U32 offset_1 = rep[0], offset_2 = rep[1]; + DEBUGLOG(5, "ZSTD_compressBlock_lazy_extDict_generic");+ /* init */- ms->nextToUpdate3 = ms->nextToUpdate; ip += (ip == prefixStart); /* Match Loop */+#if defined(__GNUC__) && defined(__x86_64__)+ /* I've measured random a 5% speed loss on levels 5 & 6 (greedy) when the+ * code alignment is perturbed. To fix the instability align the loop on 32-bytes.+ */+ __asm__(".p2align 5");+#endif while (ip < ilimit) { size_t matchLength=0; size_t offset=0;@@ -944,10 +971,11 @@ U32 current = (U32)(ip-base); /* check repCode */- { const U32 repIndex = (U32)(current+1 - offset_1);+ { const U32 windowLow = ZSTD_getLowestMatchIndex(ms, current+1, windowLog);+ const U32 repIndex = (U32)(current+1 - offset_1); const BYTE* const repBase = repIndex < dictLimit ? dictBase : base; const BYTE* const repMatch = repBase + repIndex;- if (((U32)((dictLimit-1) - repIndex) >= 3) & (repIndex > lowestIndex)) /* intentional overflow */+ if (((U32)((dictLimit-1) - repIndex) >= 3) & (repIndex > windowLow)) /* intentional overflow */ if (MEM_read32(ip+1) == MEM_read32(repMatch)) { /* repcode detected we should take it */ const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend;@@ -974,10 +1002,11 @@ current++; /* check repCode */ if (offset) {+ const U32 windowLow = ZSTD_getLowestMatchIndex(ms, current, windowLog); const U32 repIndex = (U32)(current - offset_1); const BYTE* const repBase = repIndex < dictLimit ? dictBase : base; const BYTE* const repMatch = repBase + repIndex;- if (((U32)((dictLimit-1) - repIndex) >= 3) & (repIndex > lowestIndex)) /* intentional overflow */+ if (((U32)((dictLimit-1) - repIndex) >= 3) & (repIndex > windowLow)) /* intentional overflow */ if (MEM_read32(ip) == MEM_read32(repMatch)) { /* repcode detected */ const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend;@@ -1004,10 +1033,11 @@ current++; /* check repCode */ if (offset) {+ const U32 windowLow = ZSTD_getLowestMatchIndex(ms, current, windowLog); const U32 repIndex = (U32)(current - offset_1); const BYTE* const repBase = repIndex < dictLimit ? dictBase : base; const BYTE* const repMatch = repBase + repIndex;- if (((U32)((dictLimit-1) - repIndex) >= 3) & (repIndex > lowestIndex)) /* intentional overflow */+ if (((U32)((dictLimit-1) - repIndex) >= 3) & (repIndex > windowLow)) /* intentional overflow */ if (MEM_read32(ip) == MEM_read32(repMatch)) { /* repcode detected */ const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend;@@ -1042,22 +1072,24 @@ /* store sequence */ _storeSequence: { size_t const litLength = start - anchor;- ZSTD_storeSeq(seqStore, litLength, anchor, (U32)offset, matchLength-MINMATCH);+ ZSTD_storeSeq(seqStore, litLength, anchor, iend, (U32)offset, matchLength-MINMATCH); anchor = ip = start + matchLength; } /* check immediate repcode */ while (ip <= ilimit) {- const U32 repIndex = (U32)((ip-base) - offset_2);+ const U32 repCurrent = (U32)(ip-base);+ const U32 windowLow = ZSTD_getLowestMatchIndex(ms, repCurrent, windowLog);+ const U32 repIndex = repCurrent - offset_2; const BYTE* const repBase = repIndex < dictLimit ? dictBase : base; const BYTE* const repMatch = repBase + repIndex;- if (((U32)((dictLimit-1) - repIndex) >= 3) & (repIndex > lowestIndex)) /* intentional overflow */+ if (((U32)((dictLimit-1) - repIndex) >= 3) & (repIndex > windowLow)) /* intentional overflow */ if (MEM_read32(ip) == MEM_read32(repMatch)) { /* 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; offset = offset_2; offset_2 = offset_1; offset_1 = (U32)offset; /* swap offset history */- ZSTD_storeSeq(seqStore, 0, anchor, 0, matchLength-MINMATCH);+ ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, matchLength-MINMATCH); ip += matchLength; anchor = ip; continue; /* faster when present ... (?) */@@ -1070,7 +1102,7 @@ rep[1] = offset_2; /* Return the last literals size */- return iend - anchor;+ return (size_t)(iend - anchor); } @@ -1078,7 +1110,7 @@ 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, 0, 0);+ return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, src, srcSize, search_hashChain, 0); } size_t ZSTD_compressBlock_lazy_extDict(@@ -1086,7 +1118,7 @@ void const* src, size_t srcSize) {- return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, src, srcSize, 0, 1);+ return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, src, srcSize, search_hashChain, 1); } size_t ZSTD_compressBlock_lazy2_extDict(@@ -1094,7 +1126,7 @@ void const* src, size_t srcSize) {- return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, src, srcSize, 0, 2);+ return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, src, srcSize, search_hashChain, 2); } size_t ZSTD_compressBlock_btlazy2_extDict(@@ -1102,5 +1134,5 @@ void const* src, size_t srcSize) {- return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, src, srcSize, 1, 2);+ return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, src, srcSize, search_binaryTree, 2); }
zstd/lib/compress/zstd_lazy.h view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the
zstd/lib/compress/zstd_ldm.c view
@@ -1,15 +1,16 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, 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. */ #include "zstd_ldm.h" -#include "debug.h"+#include "../common/debug.h" #include "zstd_fast.h" /* ZSTD_fillHashTable() */ #include "zstd_double_fast.h" /* ZSTD_fillDoubleHashTable() */ @@ -49,9 +50,9 @@ { size_t const ldmHSize = ((size_t)1) << params.hashLog; size_t const ldmBucketSizeLog = MIN(params.bucketSizeLog, params.hashLog);- size_t const ldmBucketSize =- ((size_t)1) << (params.hashLog - ldmBucketSizeLog);- size_t const totalSize = ldmBucketSize + ldmHSize * sizeof(ldmEntry_t);+ size_t const ldmBucketSize = ((size_t)1) << (params.hashLog - ldmBucketSizeLog);+ size_t const totalSize = ZSTD_cwksp_alloc_size(ldmBucketSize)+ + ZSTD_cwksp_alloc_size(ldmHSize * sizeof(ldmEntry_t)); return params.enableLdm ? totalSize : 0; } @@ -223,7 +224,21 @@ return rollingHash; } +void ZSTD_ldm_fillHashTable(+ ldmState_t* state, const BYTE* ip,+ const BYTE* iend, ldmParams_t const* params)+{+ DEBUGLOG(5, "ZSTD_ldm_fillHashTable");+ if ((size_t)(iend - ip) >= params->minMatchLength) {+ U64 startingHash = ZSTD_rollingHash_compute(ip, params->minMatchLength);+ ZSTD_ldm_fillLdmHashTable(+ state, startingHash, ip, iend - params->minMatchLength, state->window.base,+ params->hashLog - params->bucketSizeLog,+ *params);+ }+} + /** ZSTD_ldm_limitTableUpdate() : * * Sets cctx->nextToUpdate to a position corresponding closer to anchor@@ -447,8 +462,10 @@ if (ZSTD_window_needOverflowCorrection(ldmState->window, chunkEnd)) { U32 const ldmHSize = 1U << params->hashLog; U32 const correction = ZSTD_window_correctOverflow(- &ldmState->window, /* cycleLog */ 0, maxDist, src);+ &ldmState->window, /* cycleLog */ 0, maxDist, chunkStart); ZSTD_ldm_reduceTable(ldmState->hashTable, ldmHSize, correction);+ /* invalidate dictionaries on overflow correction */+ ldmState->loadedDictEnd = 0; } /* 2. We enforce the maximum offset allowed. *@@ -457,8 +474,14 @@ * TODO: * Test the chunk size. * * Try invalidation after the sequence generation and test the * the 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+ * be split into two sequences. This condition holds when using+ * ZSTD_window_enforceMaxDist(), but if we move to checking offsets+ * against maxDist directly, we'll have to carefully handle that case. */- ZSTD_window_enforceMaxDist(&ldmState->window, chunkEnd, maxDist, NULL, NULL);+ ZSTD_window_enforceMaxDist(&ldmState->window, chunkEnd, maxDist, &ldmState->loadedDictEnd, NULL); /* 3. Generate the sequences for the chunk, and get newLeftoverSize. */ newLeftoverSize = ZSTD_ldm_generateSequences_internal( ldmState, sequences, params, chunkStart, chunkSize);@@ -566,14 +589,13 @@ if (sequence.offset == 0) break; - assert(sequence.offset <= (1U << cParams->windowLog)); assert(ip + sequence.litLength + sequence.matchLength <= iend); /* Fill tables for block compressor */ ZSTD_ldm_limitTableUpdate(ms, ip); ZSTD_ldm_fillFastTables(ms, ip); /* Run the block compressor */- DEBUGLOG(5, "calling block compressor on segment of size %u", sequence.litLength);+ DEBUGLOG(5, "pos %u : calling block compressor on segment of size %u", (unsigned)(ip-istart), sequence.litLength); { size_t const newLitLength = blockCompressor(ms, seqStore, rep, ip, sequence.litLength);@@ -583,7 +605,7 @@ rep[i] = rep[i-1]; rep[0] = sequence.offset; /* Store the sequence */- ZSTD_storeSeq(seqStore, newLitLength, ip - newLitLength,+ ZSTD_storeSeq(seqStore, newLitLength, ip - newLitLength, iend, sequence.offset + ZSTD_REP_MOVE, sequence.matchLength - MINMATCH); ip += sequence.matchLength;
zstd/lib/compress/zstd_ldm.h view
@@ -1,10 +1,11 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, 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. */ #ifndef ZSTD_LDM_H@@ -15,13 +16,17 @@ #endif #include "zstd_compress_internal.h" /* ldmParams_t, U32 */-#include "zstd.h" /* ZSTD_CCtx, size_t */+#include "../zstd.h" /* ZSTD_CCtx, size_t */ /*-************************************* * Long distance matching ***************************************/ #define ZSTD_LDM_DEFAULT_WINDOW_LOG ZSTD_WINDOWLOG_LIMIT_DEFAULT++void ZSTD_ldm_fillHashTable(+ ldmState_t* state, const BYTE* ip,+ const BYTE* iend, ldmParams_t const* params); /** * ZSTD_ldm_generateSequences():
zstd/lib/compress/zstd_opt.c view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Przemyslaw Skibinski, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Przemyslaw Skibinski, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -249,40 +249,6 @@ } } -/* ZSTD_litLengthContribution() :- * @return ( cost(litlength) - cost(0) )- * this value can then be added to rawLiteralsCost()- * to provide a cost which is directly comparable to a match ending at same position */-static int ZSTD_litLengthContribution(U32 const litLength, const optState_t* const optPtr, int optLevel)-{- if (optPtr->priceType >= zop_predef) return WEIGHT(litLength, optLevel);-- /* dynamic statistics */- { U32 const llCode = ZSTD_LLcode(litLength);- int const contribution = (LL_bits[llCode] * BITCOST_MULTIPLIER)- + WEIGHT(optPtr->litLengthFreq[0], optLevel) /* note: log2litLengthSum cancel out */- - WEIGHT(optPtr->litLengthFreq[llCode], optLevel);-#if 1- return contribution;-#else- return MAX(0, contribution); /* sometimes better, sometimes not ... */-#endif- }-}--/* ZSTD_literalsContribution() :- * creates a fake cost for the literals part of a sequence- * which can be compared to the ending cost of a match- * should a new match start at this position */-static int ZSTD_literalsContribution(const BYTE* const literals, U32 const litLength,- const optState_t* const optPtr,- int optLevel)-{- int const contribution = ZSTD_rawLiteralsCost(literals, litLength, optPtr, optLevel)- + ZSTD_litLengthContribution(litLength, optPtr, optLevel);- return contribution;-}- /* ZSTD_getMatchPrice() : * 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.@@ -372,13 +338,15 @@ /* Update hashTable3 up to ip (excluded) Assumption : always within prefix (i.e. not within extDict) */-static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_matchState_t* ms, const BYTE* const ip)+static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_matchState_t* ms,+ U32* nextToUpdate3,+ const BYTE* const ip) { U32* const hashTable3 = ms->hashTable3; U32 const hashLog3 = ms->hashLog3; const BYTE* const base = ms->window.base;- U32 idx = ms->nextToUpdate3;- U32 const target = ms->nextToUpdate3 = (U32)(ip - base);+ U32 idx = *nextToUpdate3;+ U32 const target = (U32)(ip - base); size_t const hash3 = ZSTD_hash3Ptr(ip, hashLog3); assert(hashLog3 > 0); @@ -387,6 +355,7 @@ idx++; } + *nextToUpdate3 = target; return hashTable3[hash3]; } @@ -503,9 +472,11 @@ } } *smallerPtr = *largerPtr = 0;- if (bestLength > 384) return MIN(192, (U32)(bestLength - 384)); /* speed optimization */- assert(matchEndIdx > current + 8);- return matchEndIdx - (current + 8);+ { U32 positions = 0;+ if (bestLength > 384) positions = MIN(192, (U32)(bestLength - 384)); /* speed optimization */+ assert(matchEndIdx > current + 8);+ return MAX(positions, matchEndIdx - (current + 8));+ } } FORCE_INLINE_TEMPLATE@@ -520,8 +491,13 @@ DEBUGLOG(6, "ZSTD_updateTree_internal, from %u to %u (dictMode:%u)", idx, target, dictMode); - while(idx < target)- idx += ZSTD_insertBt1(ms, base+idx, iend, mls, dictMode == ZSTD_extDict);+ while(idx < target) {+ U32 const forward = ZSTD_insertBt1(ms, base+idx, iend, mls, dictMode == ZSTD_extDict);+ assert(idx < (U32)(idx + forward));+ idx += forward;+ }+ assert((size_t)(ip - base) <= (size_t)(U32)(-1));+ assert((size_t)(iend - base) <= (size_t)(U32)(-1)); ms->nextToUpdate = target; } @@ -531,11 +507,12 @@ 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,- U32 rep[ZSTD_REP_NUM],+ 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 */- ZSTD_match_t* matches, const U32 lengthToBeat, U32 const mls /* template */) {@@ -556,8 +533,8 @@ U32 const dictLimit = ms->window.dictLimit; const BYTE* const dictEnd = dictBase + dictLimit; const BYTE* const prefixStart = base + dictLimit;- U32 const btLow = btMask >= current ? 0 : current - btMask;- U32 const windowLow = ms->window.lowLimit;+ U32 const btLow = (btMask >= current) ? 0 : current - btMask;+ U32 const windowLow = ZSTD_getLowestMatchIndex(ms, current, cParams->windowLog); U32 const matchLow = windowLow ? windowLow : 1; U32* smallerPtr = bt + 2*(current&btMask); U32* largerPtr = bt + 2*(current&btMask) + 1;@@ -592,7 +569,10 @@ U32 repLen = 0; assert(current >= dictLimit); if (repOffset-1 /* intentional overflow, discards 0 and -1 */ < current-dictLimit) { /* equivalent to `current > repIndex >= dictLimit` */- if (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(ip - repOffset, minMatch)) {+ /* We must validate the repcode offset because when we're using a dictionary the+ * valid offset range shrinks when the dictionary goes out of bounds.+ */+ if ((repIndex >= windowLow) & (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(ip - repOffset, minMatch))) { repLen = (U32)ZSTD_count(ip+minMatch, ip+minMatch-repOffset, iLimit) + minMatch; } } else { /* repIndex < dictLimit || repIndex >= current */@@ -627,7 +607,7 @@ /* HC3 match finder */ if ((mls == 3) /*static*/ && (bestLength < mls)) {- U32 const matchIndex3 = ZSTD_insertAndFindFirstIndexHash3(ms, ip);+ U32 const matchIndex3 = ZSTD_insertAndFindFirstIndexHash3(ms, nextToUpdate3, ip); if ((matchIndex3 >= matchLow) & (current - matchIndex3 < (1<<18)) /*heuristic : longer distance likely too expensive*/ ) { size_t mlen;@@ -653,9 +633,7 @@ (ip+mlen == iLimit) ) { /* best possible length */ ms->nextToUpdate = current+1; /* skip insertion */ return 1;- }- }- }+ } } } /* no dictMatchState lookup: dicts don't have a populated HC3 table */ } @@ -663,19 +641,21 @@ while (nbCompares-- && (matchIndex >= matchLow)) { U32* const nextPtr = bt + 2*(matchIndex & btMask);- size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ const BYTE* match;+ size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ assert(current > matchIndex); if ((dictMode == ZSTD_noDict) || (dictMode == ZSTD_dictMatchState) || (matchIndex+matchLength >= dictLimit)) { assert(matchIndex+matchLength >= dictLimit); /* ensure the condition is correct when !extDict */ match = base + matchIndex;+ if (matchIndex >= dictLimit) assert(memcmp(match, ip, matchLength) == 0); /* ensure early section of match is equal as expected */ matchLength += ZSTD_count(ip+matchLength, match+matchLength, iLimit); } else { match = dictBase + matchIndex;+ assert(memcmp(match, ip, matchLength) == 0); /* ensure early section of match is equal as expected */ matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iLimit, dictEnd, prefixStart); if (matchIndex+matchLength >= dictLimit)- match = base + matchIndex; /* prepare for match[matchLength] */+ match = base + matchIndex; /* prepare for match[matchLength] read */ } if (matchLength > bestLength) {@@ -760,10 +740,13 @@ FORCE_INLINE_TEMPLATE U32 ZSTD_BtGetAllMatches (+ ZSTD_match_t* matches, /* store result (match found, increasing size) in this table */ ZSTD_matchState_t* ms,+ U32* nextToUpdate3, const BYTE* ip, const BYTE* const iHighLimit, const ZSTD_dictMode_e dictMode,- U32 rep[ZSTD_REP_NUM], U32 const ll0,- ZSTD_match_t* matches, U32 const lengthToBeat)+ const U32 rep[ZSTD_REP_NUM],+ U32 const ll0,+ U32 const lengthToBeat) { const ZSTD_compressionParameters* const cParams = &ms->cParams; U32 const matchLengthSearch = cParams->minMatch;@@ -772,12 +755,12 @@ ZSTD_updateTree_internal(ms, ip, iHighLimit, matchLengthSearch, dictMode); switch(matchLengthSearch) {- case 3 : return ZSTD_insertBtAndGetAllMatches(ms, ip, iHighLimit, dictMode, rep, ll0, matches, lengthToBeat, 3);+ case 3 : return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, 3); default :- case 4 : return ZSTD_insertBtAndGetAllMatches(ms, ip, iHighLimit, dictMode, rep, ll0, matches, lengthToBeat, 4);- case 5 : return ZSTD_insertBtAndGetAllMatches(ms, ip, iHighLimit, dictMode, rep, ll0, matches, lengthToBeat, 5);+ case 4 : return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, 4);+ case 5 : return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, 5); case 7 :- case 6 : return ZSTD_insertBtAndGetAllMatches(ms, ip, iHighLimit, dictMode, rep, ll0, matches, lengthToBeat, 6);+ case 6 : return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, 6); } } @@ -785,32 +768,8 @@ /*-******************************* * Optimal parser *********************************/-typedef struct repcodes_s {- U32 rep[3];-} repcodes_t; -static repcodes_t ZSTD_updateRep(U32 const rep[3], U32 const offset, U32 const ll0)-{- repcodes_t newReps;- if (offset >= ZSTD_REP_NUM) { /* full offset */- newReps.rep[2] = rep[1];- newReps.rep[1] = rep[0];- newReps.rep[0] = offset - ZSTD_REP_MOVE;- } else { /* repcode */- U32 const repCode = offset + ll0;- if (repCode > 0) { /* note : if repCode==0, no change */- U32 const currentOffset = (repCode==ZSTD_REP_NUM) ? (rep[0] - 1) : rep[repCode];- newReps.rep[2] = (repCode >= 2) ? rep[1] : rep[2];- newReps.rep[1] = rep[0];- newReps.rep[0] = currentOffset;- } else { /* repCode == 0 */- memcpy(&newReps, rep, sizeof(newReps));- }- }- return newReps;-} - static U32 ZSTD_totalLen(ZSTD_optimal_t sol) { return sol.litlen + sol.mlen;@@ -825,7 +784,7 @@ int enb; for (enb=0; enb < nbElts; enb++) { (void)table;- //RAWLOG(2, "%3i:%3i, ", enb, table[enb]);+ /* RAWLOG(2, "%3i:%3i, ", enb, table[enb]); */ RAWLOG(2, "%4i,", table[enb]); } RAWLOG(2, " \n");@@ -853,6 +812,7 @@ U32 const sufficient_len = MIN(cParams->targetLength, ZSTD_OPT_NUM -1); U32 const minMatch = (cParams->minMatch == 3) ? 3 : 4;+ U32 nextToUpdate3 = ms->nextToUpdate; ZSTD_optimal_t* const opt = optStatePtr->priceTable; ZSTD_match_t* const matches = optStatePtr->matchTable;@@ -862,7 +822,6 @@ DEBUGLOG(5, "ZSTD_compressBlock_opt_generic: current=%u, prefix=%u, nextToUpdate=%u", (U32)(ip - base), ms->window.dictLimit, ms->nextToUpdate); assert(optLevel <= 2);- ms->nextToUpdate3 = ms->nextToUpdate; ZSTD_rescaleFreqs(optStatePtr, (const BYTE*)src, srcSize, optLevel); ip += (ip==prefixStart); @@ -873,14 +832,19 @@ /* find first match */ { U32 const litlen = (U32)(ip - anchor); U32 const ll0 = !litlen;- U32 const nbMatches = ZSTD_BtGetAllMatches(ms, ip, iend, dictMode, rep, ll0, matches, minMatch);+ U32 const nbMatches = ZSTD_BtGetAllMatches(matches, ms, &nextToUpdate3, ip, iend, dictMode, rep, ll0, minMatch); if (!nbMatches) { ip++; continue; } /* initialize opt[0] */ { U32 i ; for (i=0; i<ZSTD_REP_NUM; i++) opt[0].rep[i] = rep[i]; } opt[0].mlen = 0; /* means is_a_literal */ opt[0].litlen = litlen;- opt[0].price = ZSTD_literalsContribution(anchor, litlen, optStatePtr, optLevel);+ /* We don't need to include the actual price of the literals because+ * it is static for the duration of the forward pass, and is included+ * in every price. We include the literal length to avoid negative+ * prices when we subtract the previous literal length.+ */+ opt[0].price = ZSTD_litLengthPrice(litlen, optStatePtr, optLevel); /* large match -> immediate encoding */ { U32 const maxML = matches[nbMatches-1].len;@@ -909,7 +873,6 @@ for (matchNb = 0; matchNb < nbMatches; matchNb++) { U32 const offset = matches[matchNb].off; U32 const end = matches[matchNb].len;- repcodes_t const repHistory = ZSTD_updateRep(rep, offset, ll0); for ( ; pos <= end ; pos++ ) { U32 const matchPrice = ZSTD_getMatchPrice(offset, pos, optStatePtr, optLevel); U32 const sequencePrice = literalsPrice + matchPrice;@@ -919,8 +882,6 @@ opt[pos].off = offset; opt[pos].litlen = litlen; opt[pos].price = sequencePrice;- ZSTD_STATIC_ASSERT(sizeof(opt[pos].rep) == sizeof(repHistory));- memcpy(opt[pos].rep, &repHistory, sizeof(repHistory)); } } last_pos = pos-1; }@@ -947,7 +908,6 @@ opt[cur].off = 0; opt[cur].litlen = litlen; opt[cur].price = price;- memcpy(opt[cur].rep, opt[cur-1].rep, sizeof(opt[cur].rep)); } else { DEBUGLOG(7, "cPos:%zi==rPos:%u : literal would cost more (%.2f>%.2f) (hist:%u,%u,%u)", inr-istart, cur, ZSTD_fCost(price), ZSTD_fCost(opt[cur].price),@@ -955,6 +915,21 @@ } } + /* Set the repcodes of the current position. We must do it here+ * because we rely on the repcodes of the 2nd to last sequence being+ * correct to set the next chunks repcodes during the backward+ * traversal.+ */+ ZSTD_STATIC_ASSERT(sizeof(opt[cur].rep) == sizeof(repcodes_t));+ assert(cur >= opt[cur].mlen);+ if (opt[cur].mlen != 0) {+ U32 const prev = cur - opt[cur].mlen;+ repcodes_t newReps = ZSTD_updateRep(opt[prev].rep, opt[cur].off, opt[cur].litlen==0);+ memcpy(opt[cur].rep, &newReps, sizeof(repcodes_t));+ } else {+ memcpy(opt[cur].rep, opt[cur - 1].rep, sizeof(repcodes_t));+ }+ /* last match must start at a minimum distance of 8 from oend */ if (inr > ilimit) continue; @@ -970,7 +945,7 @@ U32 const litlen = (opt[cur].mlen == 0) ? opt[cur].litlen : 0; U32 const previousPrice = opt[cur].price; U32 const basePrice = previousPrice + ZSTD_litLengthPrice(0, optStatePtr, optLevel);- U32 const nbMatches = ZSTD_BtGetAllMatches(ms, inr, iend, dictMode, opt[cur].rep, ll0, matches, minMatch);+ U32 const nbMatches = ZSTD_BtGetAllMatches(matches, ms, &nextToUpdate3, inr, iend, dictMode, opt[cur].rep, ll0, minMatch); U32 matchNb; if (!nbMatches) { DEBUGLOG(7, "rPos:%u : no match found", cur);@@ -995,7 +970,6 @@ /* set prices using matches found at position == cur */ for (matchNb = 0; matchNb < nbMatches; matchNb++) { U32 const offset = matches[matchNb].off;- repcodes_t const repHistory = ZSTD_updateRep(opt[cur].rep, offset, ll0); U32 const lastML = matches[matchNb].len; U32 const startML = (matchNb>0) ? matches[matchNb-1].len+1 : minMatch; U32 mlen;@@ -1015,8 +989,6 @@ opt[pos].off = offset; opt[pos].litlen = litlen; opt[pos].price = price;- ZSTD_STATIC_ASSERT(sizeof(opt[pos].rep) == sizeof(repHistory));- memcpy(opt[pos].rep, &repHistory, sizeof(repHistory)); } else { DEBUGLOG(7, "rPos:%u (ml=%2u) => new price is worse (%.2f>=%.2f)", pos, mlen, ZSTD_fCost(price), ZSTD_fCost(opt[pos].price));@@ -1032,6 +1004,17 @@ _shortestPath: /* cur, last_pos, best_mlen, best_off have to be set */ assert(opt[0].mlen == 0); + /* Set the next chunk's repcodes based on the repcodes of the beginning+ * of the last match, and the last sequence. This avoids us having to+ * update them while traversing the sequences.+ */+ if (lastSequence.mlen != 0) {+ repcodes_t reps = ZSTD_updateRep(opt[cur].rep, lastSequence.off, lastSequence.litlen==0);+ memcpy(rep, &reps, sizeof(reps));+ } else {+ memcpy(rep, opt[cur].rep, sizeof(repcodes_t));+ }+ { U32 const storeEnd = cur + 1; U32 storeStart = storeEnd; U32 seqPos = cur;@@ -1068,33 +1051,18 @@ continue; /* will finish */ } - /* repcodes update : like ZSTD_updateRep(), but update in place */- if (offCode >= ZSTD_REP_NUM) { /* full offset */- rep[2] = rep[1];- rep[1] = rep[0];- rep[0] = offCode - ZSTD_REP_MOVE;- } else { /* repcode */- U32 const repCode = offCode + (llen==0);- if (repCode) { /* note : if repCode==0, no change */- U32 const currentOffset = (repCode==ZSTD_REP_NUM) ? (rep[0] - 1) : rep[repCode];- if (repCode >= 2) rep[2] = rep[1];- rep[1] = rep[0];- rep[0] = currentOffset;- } }- assert(anchor + llen <= iend); ZSTD_updateStats(optStatePtr, llen, anchor, offCode, mlen);- ZSTD_storeSeq(seqStore, llen, anchor, offCode, mlen-MINMATCH);+ ZSTD_storeSeq(seqStore, llen, anchor, iend, offCode, mlen-MINMATCH); anchor += advance; ip = anchor; } } ZSTD_setBasePrices(optStatePtr, optLevel); }- } /* while (ip < ilimit) */ /* Return the last literals size */- return iend - anchor;+ return (size_t)(iend - anchor); } @@ -1158,7 +1126,6 @@ ms->window.dictLimit += (U32)srcSize; ms->window.lowLimit = ms->window.dictLimit; ms->nextToUpdate = ms->window.dictLimit;- ms->nextToUpdate3 = ms->window.dictLimit; /* re-inforce weight of collected statistics */ ZSTD_upscaleStats(&ms->opt);
zstd/lib/compress/zstd_opt.h view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the
zstd/lib/compress/zstdmt_compress.c view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -22,9 +22,9 @@ /* ====== Dependencies ====== */ #include <string.h> /* memcpy, memset */ #include <limits.h> /* INT_MAX, UINT_MAX */-#include "mem.h" /* MEM_STATIC */-#include "pool.h" /* threadpool */-#include "threading.h" /* mutex */+#include "../common/mem.h" /* MEM_STATIC */+#include "../common/pool.h" /* threadpool */+#include "../common/threading.h" /* mutex */ #include "zstd_compress_internal.h" /* MIN, ERROR, ZSTD_*, ZSTD_highbit32 */ #include "zstd_ldm.h" #include "zstdmt_compress.h"@@ -461,7 +461,13 @@ ZSTD_window_t ldmWindow; /* A thread-safe copy of ldmState.window */ } serialState_t; -static int ZSTDMT_serialState_reset(serialState_t* serialState, ZSTDMT_seqPool* seqPool, ZSTD_CCtx_params params, size_t jobSize)+static int+ZSTDMT_serialState_reset(serialState_t* serialState,+ ZSTDMT_seqPool* seqPool,+ ZSTD_CCtx_params params,+ size_t jobSize,+ const void* dict, size_t const dictSize,+ ZSTD_dictContentType_e dictContentType) { /* Adjust parameters */ if (params.ldmParams.enableLdm) {@@ -490,8 +496,7 @@ /* Size the seq pool tables */ ZSTDMT_setNbSeq(seqPool, ZSTD_ldm_getMaxNbSeq(params.ldmParams, jobSize)); /* Reset the window */- ZSTD_window_clear(&serialState->ldmState.window);- serialState->ldmWindow = serialState->ldmState.window;+ ZSTD_window_init(&serialState->ldmState.window); /* Resize tables and output space if necessary. */ if (serialState->ldmState.hashTable == NULL || serialState->params.ldmParams.hashLog < hashLog) { ZSTD_free(serialState->ldmState.hashTable, cMem);@@ -506,7 +511,24 @@ /* Zero the tables */ memset(serialState->ldmState.hashTable, 0, hashSize); memset(serialState->ldmState.bucketOffsets, 0, bucketSize);++ /* Update window state and fill hash table with dict */+ serialState->ldmState.loadedDictEnd = 0;+ if (dictSize > 0) {+ if (dictContentType == ZSTD_dct_rawContent) {+ BYTE const* const dictEnd = (const BYTE*)dict + dictSize;+ ZSTD_window_update(&serialState->ldmState.window, dict, dictSize);+ ZSTD_ldm_fillHashTable(&serialState->ldmState, (const BYTE*)dict, dictEnd, ¶ms.ldmParams);+ serialState->ldmState.loadedDictEnd = params.forceWindow ? 0 : (U32)(dictEnd - serialState->ldmState.window.base);+ } else {+ /* don't even load anything */+ }+ }++ /* Initialize serialState's copy of ldmWindow. */+ serialState->ldmWindow = serialState->ldmState.window; }+ serialState->params = params; serialState->params.jobSize = (U32)jobSize; return 0;@@ -668,7 +690,7 @@ /* init */ if (job->cdict) {- size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, NULL, 0, ZSTD_dct_auto, ZSTD_dtlm_fast, job->cdict, jobParams, job->fullFrameSize);+ size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, NULL, 0, ZSTD_dct_auto, ZSTD_dtlm_fast, job->cdict, &jobParams, job->fullFrameSize); assert(job->firstJob); /* only allowed for first job */ if (ZSTD_isError(initError)) JOB_ERROR(initError); } else { /* srcStart points at reloaded section */@@ -680,7 +702,7 @@ job->prefix.start, job->prefix.size, ZSTD_dct_rawContent, /* load dictionary in "content-only" mode (no header analysis) */ ZSTD_dtlm_fast, NULL, /*cdict*/- jobParams, pledgedSrcSize);+ &jobParams, pledgedSrcSize); if (ZSTD_isError(initError)) JOB_ERROR(initError); } } @@ -927,12 +949,18 @@ unsigned jobID; DEBUGLOG(3, "ZSTDMT_releaseAllJobResources"); for (jobID=0; jobID <= mtctx->jobIDMask; jobID++) {+ /* Copy the mutex/cond out */+ ZSTD_pthread_mutex_t const mutex = mtctx->jobs[jobID].job_mutex;+ ZSTD_pthread_cond_t const cond = mtctx->jobs[jobID].job_cond;+ DEBUGLOG(4, "job%02u: release dst address %08X", jobID, (U32)(size_t)mtctx->jobs[jobID].dstBuff.start); ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[jobID].dstBuff);- mtctx->jobs[jobID].dstBuff = g_nullBuffer;- mtctx->jobs[jobID].cSize = 0;++ /* Clear the job description, but keep the mutex/cond */+ memset(&mtctx->jobs[jobID], 0, sizeof(mtctx->jobs[jobID]));+ mtctx->jobs[jobID].job_mutex = mutex;+ mtctx->jobs[jobID].job_cond = cond; }- memset(mtctx->jobs, 0, (mtctx->jobIDMask+1)*sizeof(ZSTDMT_jobDescription)); mtctx->inBuff.buffer = g_nullBuffer; mtctx->inBuff.filled = 0; mtctx->allJobsCompleted = 1;@@ -1028,9 +1056,9 @@ /* Sets parameters relevant to the compression job, * initializing others to default values. */-static ZSTD_CCtx_params ZSTDMT_initJobCCtxParams(ZSTD_CCtx_params const params)+static ZSTD_CCtx_params ZSTDMT_initJobCCtxParams(const ZSTD_CCtx_params* params) {- ZSTD_CCtx_params jobParams = params;+ ZSTD_CCtx_params jobParams = *params; /* Clear parameters related to multithreading */ jobParams.forceWindow = 0; jobParams.nbWorkers = 0;@@ -1048,7 +1076,7 @@ static size_t ZSTDMT_resize(ZSTDMT_CCtx* mtctx, unsigned nbWorkers) { if (POOL_resize(mtctx->factory, nbWorkers)) return ERROR(memory_allocation);- FORWARD_IF_ERROR( ZSTDMT_expandJobsTable(mtctx, nbWorkers) );+ FORWARD_IF_ERROR( ZSTDMT_expandJobsTable(mtctx, nbWorkers) , ""); mtctx->bufPool = ZSTDMT_expandBufferPool(mtctx->bufPool, nbWorkers); if (mtctx->bufPool == NULL) return ERROR(memory_allocation); mtctx->cctxPool = ZSTDMT_expandCCtxPool(mtctx->cctxPool, nbWorkers);@@ -1070,7 +1098,7 @@ DEBUGLOG(5, "ZSTDMT_updateCParams_whileCompressing (level:%i)", compressionLevel); mtctx->params.compressionLevel = compressionLevel;- { ZSTD_compressionParameters cParams = ZSTD_getCParamsFromCCtxParams(cctxParams, 0, 0);+ { ZSTD_compressionParameters cParams = ZSTD_getCParamsFromCCtxParams(cctxParams, ZSTD_CONTENTSIZE_UNKNOWN, 0); cParams.windowLog = saved_wlog; mtctx->params.cParams = cParams; }@@ -1129,9 +1157,14 @@ size_t const produced = ZSTD_isError(cResult) ? 0 : cResult; size_t const flushed = ZSTD_isError(cResult) ? 0 : jobPtr->dstFlushed; assert(flushed <= produced);+ assert(jobPtr->consumed <= jobPtr->src.size); toFlush = produced - flushed;- if (toFlush==0 && (jobPtr->consumed >= jobPtr->src.size)) {- /* doneJobID is not-fully-flushed, but toFlush==0 : doneJobID should be compressing some more data */+ /* if toFlush==0, nothing is available to flush.+ * However, jobID is expected to still be active:+ * if jobID was already completed and fully flushed,+ * ZSTDMT_flushProduced() should have already moved onto next job.+ * Therefore, some input has not yet been consumed. */+ if (toFlush==0) { assert(jobPtr->consumed < jobPtr->src.size); } }@@ -1146,14 +1179,18 @@ /* ===== Multi-threaded compression ===== */ /* ------------------------------------------ */ -static unsigned ZSTDMT_computeTargetJobLog(ZSTD_CCtx_params const params)+static unsigned ZSTDMT_computeTargetJobLog(const ZSTD_CCtx_params* params) {- if (params.ldmParams.enableLdm)+ unsigned jobLog;+ if (params->ldmParams.enableLdm) { /* In Long Range Mode, the windowLog is typically oversized. * In which case, it's preferable to determine the jobSize * based on chainLog instead. */- return MAX(21, params.cParams.chainLog + 4);- return MAX(20, params.cParams.windowLog + 2);+ jobLog = MAX(21, params->cParams.chainLog + 4);+ } else {+ jobLog = MAX(20, params->cParams.windowLog + 2);+ }+ return MIN(jobLog, (unsigned)ZSTDMT_JOBLOG_MAX); } static int ZSTDMT_overlapLog_default(ZSTD_strategy strat)@@ -1184,27 +1221,27 @@ return ovlog; } -static size_t ZSTDMT_computeOverlapSize(ZSTD_CCtx_params const params)+static size_t ZSTDMT_computeOverlapSize(const ZSTD_CCtx_params* params) {- int const overlapRLog = 9 - ZSTDMT_overlapLog(params.overlapLog, params.cParams.strategy);- int ovLog = (overlapRLog >= 8) ? 0 : (params.cParams.windowLog - overlapRLog);+ int const overlapRLog = 9 - ZSTDMT_overlapLog(params->overlapLog, params->cParams.strategy);+ int ovLog = (overlapRLog >= 8) ? 0 : (params->cParams.windowLog - overlapRLog); assert(0 <= overlapRLog && overlapRLog <= 8);- if (params.ldmParams.enableLdm) {+ if (params->ldmParams.enableLdm) { /* In Long Range Mode, the windowLog is typically oversized. * In which case, it's preferable to determine the jobSize * based on chainLog instead. * Then, ovLog becomes a fraction of the jobSize, rather than windowSize */- ovLog = MIN(params.cParams.windowLog, ZSTDMT_computeTargetJobLog(params) - 2)+ ovLog = MIN(params->cParams.windowLog, ZSTDMT_computeTargetJobLog(params) - 2) - overlapRLog; }- assert(0 <= ovLog && ovLog <= 30);- DEBUGLOG(4, "overlapLog : %i", params.overlapLog);+ assert(0 <= ovLog && ovLog <= ZSTD_WINDOWLOG_MAX);+ DEBUGLOG(4, "overlapLog : %i", params->overlapLog); DEBUGLOG(4, "overlap size : %i", 1 << ovLog); return (ovLog==0) ? 0 : (size_t)1 << ovLog; } static unsigned-ZSTDMT_computeNbJobs(ZSTD_CCtx_params params, size_t srcSize, unsigned nbWorkers)+ZSTDMT_computeNbJobs(const ZSTD_CCtx_params* params, size_t srcSize, unsigned nbWorkers) { assert(nbWorkers>0); { size_t const jobSizeTarget = (size_t)1 << ZSTDMT_computeTargetJobLog(params);@@ -1220,16 +1257,17 @@ /* ZSTDMT_compress_advanced_internal() : * This is a blocking function : it will only give back control to caller after finishing its compression job. */-static size_t ZSTDMT_compress_advanced_internal(+static size_t+ZSTDMT_compress_advanced_internal( ZSTDMT_CCtx* mtctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, const ZSTD_CDict* cdict, ZSTD_CCtx_params params) {- ZSTD_CCtx_params const jobParams = ZSTDMT_initJobCCtxParams(params);- size_t const overlapSize = ZSTDMT_computeOverlapSize(params);- unsigned const nbJobs = ZSTDMT_computeNbJobs(params, srcSize, params.nbWorkers);+ ZSTD_CCtx_params const jobParams = ZSTDMT_initJobCCtxParams(¶ms);+ size_t const overlapSize = ZSTDMT_computeOverlapSize(¶ms);+ unsigned const nbJobs = ZSTDMT_computeNbJobs(¶ms, srcSize, params.nbWorkers); size_t const proposedJobSize = (srcSize + (nbJobs-1)) / nbJobs; size_t const avgJobSize = (((proposedJobSize-1) & 0x1FFFF) < 0x7FFF) ? proposedJobSize + 0xFFFF : proposedJobSize; /* avoid too small last block */ const char* const srcStart = (const char*)src;@@ -1247,15 +1285,16 @@ ZSTD_CCtx* const cctx = mtctx->cctxPool->cctx[0]; DEBUGLOG(4, "ZSTDMT_compress_advanced_internal: fallback to single-thread mode"); if (cdict) return ZSTD_compress_usingCDict_advanced(cctx, dst, dstCapacity, src, srcSize, cdict, jobParams.fParams);- return ZSTD_compress_advanced_internal(cctx, dst, dstCapacity, src, srcSize, NULL, 0, jobParams);+ return ZSTD_compress_advanced_internal(cctx, dst, dstCapacity, src, srcSize, NULL, 0, &jobParams); } assert(avgJobSize >= 256 KB); /* condition for ZSTD_compressBound(A) + ZSTD_compressBound(B) <= ZSTD_compressBound(A+B), required to compress directly into Dst (no additional buffer) */ ZSTDMT_setBufferSize(mtctx->bufPool, ZSTD_compressBound(avgJobSize) );- if (ZSTDMT_serialState_reset(&mtctx->serial, mtctx->seqPool, params, avgJobSize))+ /* LDM doesn't even try to load the dictionary in single-ingestion mode */+ if (ZSTDMT_serialState_reset(&mtctx->serial, mtctx->seqPool, params, avgJobSize, NULL, 0, ZSTD_dct_auto)) return ERROR(memory_allocation); - FORWARD_IF_ERROR( ZSTDMT_expandJobsTable(mtctx, nbJobs) ); /* only expands if necessary */+ FORWARD_IF_ERROR( ZSTDMT_expandJobsTable(mtctx, nbJobs) , ""); /* only expands if necessary */ { unsigned u; for (u=0; u<nbJobs; u++) {@@ -1388,19 +1427,19 @@ /* init */ if (params.nbWorkers != mtctx->params.nbWorkers)- FORWARD_IF_ERROR( ZSTDMT_resize(mtctx, params.nbWorkers) );+ FORWARD_IF_ERROR( ZSTDMT_resize(mtctx, params.nbWorkers) , ""); if (params.jobSize != 0 && params.jobSize < ZSTDMT_JOBSIZE_MIN) params.jobSize = ZSTDMT_JOBSIZE_MIN;- if (params.jobSize > (size_t)ZSTDMT_JOBSIZE_MAX) params.jobSize = ZSTDMT_JOBSIZE_MAX;+ if (params.jobSize > (size_t)ZSTDMT_JOBSIZE_MAX) params.jobSize = (size_t)ZSTDMT_JOBSIZE_MAX; mtctx->singleBlockingThread = (pledgedSrcSize <= ZSTDMT_JOBSIZE_MIN); /* do not trigger multi-threading when srcSize is too small */ if (mtctx->singleBlockingThread) {- ZSTD_CCtx_params const singleThreadParams = ZSTDMT_initJobCCtxParams(params);+ ZSTD_CCtx_params const singleThreadParams = ZSTDMT_initJobCCtxParams(¶ms); DEBUGLOG(5, "ZSTDMT_initCStream_internal: switch to single blocking thread mode"); assert(singleThreadParams.nbWorkers == 0); return ZSTD_initCStream_internal(mtctx->cctxPool->cctx[0], dict, dictSize, cdict,- singleThreadParams, pledgedSrcSize);+ &singleThreadParams, pledgedSrcSize); } DEBUGLOG(4, "ZSTDMT_initCStream_internal: %u workers", params.nbWorkers);@@ -1426,12 +1465,14 @@ mtctx->cdict = cdict; } - mtctx->targetPrefixSize = ZSTDMT_computeOverlapSize(params);+ mtctx->targetPrefixSize = ZSTDMT_computeOverlapSize(¶ms); DEBUGLOG(4, "overlapLog=%i => %u KB", params.overlapLog, (U32)(mtctx->targetPrefixSize>>10)); mtctx->targetSectionSize = params.jobSize; if (mtctx->targetSectionSize == 0) {- mtctx->targetSectionSize = 1ULL << ZSTDMT_computeTargetJobLog(params);+ mtctx->targetSectionSize = 1ULL << ZSTDMT_computeTargetJobLog(¶ms); }+ assert(mtctx->targetSectionSize <= (size_t)ZSTDMT_JOBSIZE_MAX);+ if (params.rsyncable) { /* Aim for the targetsectionSize as the average job size. */ U32 const jobSizeMB = (U32)(mtctx->targetSectionSize >> 20);@@ -1483,7 +1524,8 @@ mtctx->allJobsCompleted = 0; mtctx->consumed = 0; mtctx->produced = 0;- if (ZSTDMT_serialState_reset(&mtctx->serial, mtctx->seqPool, params, mtctx->targetSectionSize))+ if (ZSTDMT_serialState_reset(&mtctx->serial, mtctx->seqPool, params, mtctx->targetSectionSize,+ dict, dictSize, dictContentType)) return ERROR(memory_allocation); return 0; }@@ -1697,9 +1739,11 @@ assert(mtctx->doneJobID < mtctx->nextJobID); assert(cSize >= mtctx->jobs[wJobID].dstFlushed); assert(mtctx->jobs[wJobID].dstBuff.start != NULL);- memcpy((char*)output->dst + output->pos,- (const char*)mtctx->jobs[wJobID].dstBuff.start + mtctx->jobs[wJobID].dstFlushed,- toFlush);+ if (toFlush > 0) {+ memcpy((char*)output->dst + output->pos,+ (const char*)mtctx->jobs[wJobID].dstBuff.start + mtctx->jobs[wJobID].dstFlushed,+ toFlush);+ } output->pos += toFlush; mtctx->jobs[wJobID].dstFlushed += toFlush; /* can write : this value is only used by mtctx */ @@ -1769,7 +1813,7 @@ BYTE const* const bufferStart = (BYTE const*)buffer.start; BYTE const* const bufferEnd = bufferStart + buffer.capacity; BYTE const* const rangeStart = (BYTE const*)range.start;- BYTE const* const rangeEnd = rangeStart + range.size;+ BYTE const* const rangeEnd = range.size != 0 ? rangeStart + range.size : rangeStart; if (rangeStart == NULL || bufferStart == NULL) return 0;@@ -2043,7 +2087,7 @@ || ((endOp == ZSTD_e_end) && (!mtctx->frameEnded)) ) { /* must finish the frame with a zero-size block */ size_t const jobSize = mtctx->inBuff.filled; assert(mtctx->inBuff.filled <= mtctx->targetSectionSize);- FORWARD_IF_ERROR( ZSTDMT_createCompressionJob(mtctx, jobSize, endOp) );+ FORWARD_IF_ERROR( ZSTDMT_createCompressionJob(mtctx, jobSize, endOp) , ""); } /* check for potential compressed data ready to be flushed */@@ -2057,7 +2101,7 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_inBuffer* input) {- FORWARD_IF_ERROR( ZSTDMT_compressStream_generic(mtctx, output, input, ZSTD_e_continue) );+ FORWARD_IF_ERROR( ZSTDMT_compressStream_generic(mtctx, output, input, ZSTD_e_continue) , ""); /* recommended next input size : fill current input buffer */ return mtctx->targetSectionSize - mtctx->inBuff.filled; /* note : could be zero when input buffer is fully filled and no more availability to create new job */@@ -2074,7 +2118,7 @@ || ((endFrame==ZSTD_e_end) && !mtctx->frameEnded)) { /* need a last 0-size block to end frame */ DEBUGLOG(5, "ZSTDMT_flushStream_internal : create a new job (%u bytes, end:%u)", (U32)srcSize, (U32)endFrame);- FORWARD_IF_ERROR( ZSTDMT_createCompressionJob(mtctx, srcSize, endFrame) );+ FORWARD_IF_ERROR( ZSTDMT_createCompressionJob(mtctx, srcSize, endFrame) , ""); } /* check if there is any data available to flush */
zstd/lib/compress/zstdmt_compress.h view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -40,7 +40,7 @@ /* === Dependencies === */ #include <stddef.h> /* size_t */ #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_parameters */-#include "zstd.h" /* ZSTD_inBuffer, ZSTD_outBuffer, ZSTDLIB_API */+#include "../zstd.h" /* ZSTD_inBuffer, ZSTD_outBuffer, ZSTDLIB_API */ /* === Constants === */@@ -50,6 +50,7 @@ #ifndef ZSTDMT_JOBSIZE_MIN # define ZSTDMT_JOBSIZE_MIN (1 MB) #endif+#define ZSTDMT_JOBLOG_MAX (MEM_32bits() ? 29 : 30) #define ZSTDMT_JOBSIZE_MAX (MEM_32bits() ? (512 MB) : (1024 MB))
zstd/lib/decompress/huf_decompress.c view
@@ -1,47 +1,27 @@ /* ******************************************************************- huff0 huffman decoder,- part of Finite State Entropy library- Copyright (C) 2013-present, 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:-- * 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 :- - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy+ * huff0 huffman decoder,+ * part of Finite State Entropy library+ * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc.+ *+ * You can contact the author at :+ * - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy+ *+ * 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 <string.h> /* memcpy, memset */-#include "compiler.h"-#include "bitstream.h" /* BIT_* */-#include "fse.h" /* to compress headers */+#include "../common/compiler.h"+#include "../common/bitstream.h" /* BIT_* */+#include "../common/fse.h" /* to compress headers */ #define HUF_STATIC_LINKING_ONLY-#include "huf.h"-#include "error_private.h"+#include "../common/huf.h"+#include "../common/error_private.h" /* ************************************************************** * Macros@@ -61,7 +41,6 @@ * Error Management ****************************************************************/ #define HUF_isError ERR_isError-#define CHECK_F(f) { size_t const err_ = (f); if (HUF_isError(err_)) return err_; } /* **************************************************************@@ -179,17 +158,29 @@ /* fill DTable */ { U32 n;- for (n=0; n<nbSymbols; n++) {- U32 const w = huffWeight[n];- U32 const length = (1 << w) >> 1;- U32 u;+ size_t const nEnd = nbSymbols;+ for (n=0; n<nEnd; n++) {+ size_t const w = huffWeight[n];+ size_t const length = (1 << w) >> 1;+ size_t const uStart = rankVal[w];+ size_t const uEnd = uStart + length;+ size_t u; HUF_DEltX1 D;- D.byte = (BYTE)n; D.nbBits = (BYTE)(tableLog + 1 - w);- for (u = rankVal[w]; u < rankVal[w] + length; u++)- dt[u] = D;- rankVal[w] += length;- } }-+ D.byte = (BYTE)n;+ D.nbBits = (BYTE)(tableLog + 1 - w);+ rankVal[w] = (U32)uEnd;+ if (length < 4) {+ /* Use length in the loop bound so the compiler knows it is short. */+ for (u = 0; u < length; ++u)+ dt[uStart + u] = D;+ } else {+ /* Unroll the loop 4 times, we know it is a power of 2. */+ for (u = uStart; u < uEnd; u += 4) {+ dt[u + 0] = D;+ dt[u + 1] = D;+ dt[u + 2] = D;+ dt[u + 3] = D;+ } } } } return iSize; } @@ -280,6 +271,7 @@ { const BYTE* const istart = (const BYTE*) cSrc; BYTE* const ostart = (BYTE*) dst; BYTE* const oend = ostart + dstSize;+ BYTE* const olimit = oend - 3; const void* const dtPtr = DTable + 1; const HUF_DEltX1* const dt = (const HUF_DEltX1*)dtPtr; @@ -304,9 +296,9 @@ BYTE* op2 = opStart2; BYTE* op3 = opStart3; BYTE* op4 = opStart4;- U32 endSignal = BIT_DStream_unfinished; DTableDesc const dtd = HUF_getDTableDesc(DTable); U32 const dtLog = dtd.tableLog;+ U32 endSignal = 1; if (length4 > cSrcSize) return ERROR(corruption_detected); /* overflow */ CHECK_F( BIT_initDStream(&bitD1, istart1, length1) );@@ -315,8 +307,7 @@ CHECK_F( BIT_initDStream(&bitD4, istart4, length4) ); /* up to 16 symbols per loop (4 symbols per stream) in 64-bit mode */- endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4);- while ( (endSignal==BIT_DStream_unfinished) && (op4<(oend-3)) ) {+ for ( ; (endSignal) & (op4 < olimit) ; ) { HUF_DECODE_SYMBOLX1_2(op1, &bitD1); HUF_DECODE_SYMBOLX1_2(op2, &bitD2); HUF_DECODE_SYMBOLX1_2(op3, &bitD3);@@ -333,10 +324,10 @@ HUF_DECODE_SYMBOLX1_0(op2, &bitD2); HUF_DECODE_SYMBOLX1_0(op3, &bitD3); HUF_DECODE_SYMBOLX1_0(op4, &bitD4);- BIT_reloadDStream(&bitD1);- BIT_reloadDStream(&bitD2);- BIT_reloadDStream(&bitD3);- BIT_reloadDStream(&bitD4);+ endSignal &= BIT_reloadDStreamFast(&bitD1) == BIT_DStream_unfinished;+ endSignal &= BIT_reloadDStreamFast(&bitD2) == BIT_DStream_unfinished;+ endSignal &= BIT_reloadDStreamFast(&bitD3) == BIT_DStream_unfinished;+ endSignal &= BIT_reloadDStreamFast(&bitD4) == BIT_DStream_unfinished; } /* check corruption */@@ -755,7 +746,6 @@ return dstSize; } - FORCE_INLINE_TEMPLATE size_t HUF_decompress4X2_usingDTable_internal_body( void* dst, size_t dstSize,@@ -767,6 +757,7 @@ { const BYTE* const istart = (const BYTE*) cSrc; BYTE* const ostart = (BYTE*) dst; BYTE* const oend = ostart + dstSize;+ BYTE* const olimit = oend - (sizeof(size_t)-1); const void* const dtPtr = DTable+1; const HUF_DEltX2* const dt = (const HUF_DEltX2*)dtPtr; @@ -791,7 +782,7 @@ BYTE* op2 = opStart2; BYTE* op3 = opStart3; BYTE* op4 = opStart4;- U32 endSignal;+ U32 endSignal = 1; DTableDesc const dtd = HUF_getDTableDesc(DTable); U32 const dtLog = dtd.tableLog; @@ -802,12 +793,33 @@ CHECK_F( BIT_initDStream(&bitD4, istart4, length4) ); /* 16-32 symbols per loop (4-8 symbols per stream) */- endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4);- for ( ; (endSignal==BIT_DStream_unfinished) & (op4<(oend-(sizeof(bitD4.bitContainer)-1))) ; ) {+ for ( ; (endSignal) & (op4 < olimit); ) {+#if defined(__clang__) && (defined(__x86_64__) || defined(__i386__)) HUF_DECODE_SYMBOLX2_2(op1, &bitD1);+ HUF_DECODE_SYMBOLX2_1(op1, &bitD1);+ HUF_DECODE_SYMBOLX2_2(op1, &bitD1);+ HUF_DECODE_SYMBOLX2_0(op1, &bitD1); HUF_DECODE_SYMBOLX2_2(op2, &bitD2);+ HUF_DECODE_SYMBOLX2_1(op2, &bitD2);+ HUF_DECODE_SYMBOLX2_2(op2, &bitD2);+ HUF_DECODE_SYMBOLX2_0(op2, &bitD2);+ endSignal &= BIT_reloadDStreamFast(&bitD1) == BIT_DStream_unfinished;+ endSignal &= BIT_reloadDStreamFast(&bitD2) == BIT_DStream_unfinished; HUF_DECODE_SYMBOLX2_2(op3, &bitD3);+ HUF_DECODE_SYMBOLX2_1(op3, &bitD3);+ HUF_DECODE_SYMBOLX2_2(op3, &bitD3);+ HUF_DECODE_SYMBOLX2_0(op3, &bitD3); HUF_DECODE_SYMBOLX2_2(op4, &bitD4);+ HUF_DECODE_SYMBOLX2_1(op4, &bitD4);+ HUF_DECODE_SYMBOLX2_2(op4, &bitD4);+ HUF_DECODE_SYMBOLX2_0(op4, &bitD4);+ endSignal &= BIT_reloadDStreamFast(&bitD3) == BIT_DStream_unfinished;+ endSignal &= BIT_reloadDStreamFast(&bitD4) == BIT_DStream_unfinished;+#else+ HUF_DECODE_SYMBOLX2_2(op1, &bitD1);+ HUF_DECODE_SYMBOLX2_2(op2, &bitD2);+ HUF_DECODE_SYMBOLX2_2(op3, &bitD3);+ HUF_DECODE_SYMBOLX2_2(op4, &bitD4); HUF_DECODE_SYMBOLX2_1(op1, &bitD1); HUF_DECODE_SYMBOLX2_1(op2, &bitD2); HUF_DECODE_SYMBOLX2_1(op3, &bitD3);@@ -820,8 +832,12 @@ HUF_DECODE_SYMBOLX2_0(op2, &bitD2); HUF_DECODE_SYMBOLX2_0(op3, &bitD3); HUF_DECODE_SYMBOLX2_0(op4, &bitD4);-- endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4);+ endSignal = (U32)LIKELY(+ (BIT_reloadDStreamFast(&bitD1) == BIT_DStream_unfinished)+ & (BIT_reloadDStreamFast(&bitD2) == BIT_DStream_unfinished)+ & (BIT_reloadDStreamFast(&bitD3) == BIT_DStream_unfinished)+ & (BIT_reloadDStreamFast(&bitD4) == BIT_DStream_unfinished));+#endif } /* check corruption */
zstd/lib/decompress/zstd_ddict.c view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -15,17 +15,17 @@ * Dependencies *********************************************************/ #include <string.h> /* memcpy, memmove, memset */-#include "cpu.h" /* bmi2 */-#include "mem.h" /* low level memory routines */+#include "../common/cpu.h" /* bmi2 */+#include "../common/mem.h" /* low level memory routines */ #define FSE_STATIC_LINKING_ONLY-#include "fse.h"+#include "../common/fse.h" #define HUF_STATIC_LINKING_ONLY-#include "huf.h"+#include "../common/huf.h" #include "zstd_decompress_internal.h" #include "zstd_ddict.h" #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1)-# include "zstd_legacy.h"+# include "../legacy/zstd_legacy.h" #endif @@ -65,6 +65,10 @@ dctx->virtualStart = ddict->dictContent; dctx->dictEnd = (const BYTE*)ddict->dictContent + ddict->dictSize; dctx->previousDstEnd = dctx->dictEnd;+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION+ dctx->dictContentBeginForFuzzing = dctx->prefixStart;+ dctx->dictContentEndForFuzzing = dctx->previousDstEnd;+#endif if (ddict->entropyPresent) { dctx->litEntropy = 1; dctx->fseEntropy = 1;@@ -107,7 +111,7 @@ /* load entropy tables */ RETURN_ERROR_IF(ZSTD_isError(ZSTD_loadDEntropy( &ddict->entropy, ddict->dictContent, ddict->dictSize)),- dictionary_corrupted);+ dictionary_corrupted, ""); ddict->entropyPresent = 1; return 0; }@@ -133,7 +137,7 @@ ddict->entropy.hufTable[0] = (HUF_DTable)((HufLog)*0x1000001); /* cover both little and big endian */ /* parse dictionary content */- FORWARD_IF_ERROR( ZSTD_loadEntropy_intoDDict(ddict, dictContentType) );+ FORWARD_IF_ERROR( ZSTD_loadEntropy_intoDDict(ddict, dictContentType) , ""); return 0; }
zstd/lib/decompress/zstd_ddict.h view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -16,7 +16,7 @@ * Dependencies *********************************************************/ #include <stddef.h> /* size_t */-#include "zstd.h" /* ZSTD_DDict, and several public functions */+#include "../zstd.h" /* ZSTD_DDict, and several public functions */ /*-*******************************************************
zstd/lib/decompress/zstd_decompress.c view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -56,19 +56,19 @@ * Dependencies *********************************************************/ #include <string.h> /* memcpy, memmove, memset */-#include "cpu.h" /* bmi2 */-#include "mem.h" /* low level memory routines */+#include "../common/cpu.h" /* bmi2 */+#include "../common/mem.h" /* low level memory routines */ #define FSE_STATIC_LINKING_ONLY-#include "fse.h"+#include "../common/fse.h" #define HUF_STATIC_LINKING_ONLY-#include "huf.h"-#include "zstd_internal.h" /* blockProperties_t */+#include "../common/huf.h"+#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 */ #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1)-# include "zstd_legacy.h"+# include "../legacy/zstd_legacy.h" #endif @@ -88,10 +88,7 @@ static size_t ZSTD_startingInputLength(ZSTD_format_e format) {- size_t const startingInputLength = (format==ZSTD_f_zstd1_magicless) ?- ZSTD_FRAMEHEADERSIZE_PREFIX - ZSTD_FRAMEIDSIZE :- ZSTD_FRAMEHEADERSIZE_PREFIX;- ZSTD_STATIC_ASSERT(ZSTD_FRAMEHEADERSIZE_PREFIX >= ZSTD_FRAMEIDSIZE);+ size_t const startingInputLength = ZSTD_FRAMEHEADERSIZE_PREFIX(format); /* only supports formats ZSTD_f_zstd1 and ZSTD_f_zstd1_magicless */ assert( (format == ZSTD_f_zstd1) || (format == ZSTD_f_zstd1_magicless) ); return startingInputLength;@@ -114,7 +111,12 @@ dctx->legacyContext = NULL; dctx->previousLegacyVersion = 0; dctx->noForwardProgress = 0;+ dctx->oversizedDuration = 0; dctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid());+ dctx->outBufferMode = ZSTD_obm_buffered;+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION+ dctx->dictContentEndForFuzzing = NULL;+#endif } ZSTD_DCtx* ZSTD_initStaticDCtx(void *workspace, size_t workspaceSize)@@ -211,7 +213,7 @@ static size_t ZSTD_frameHeaderSize_internal(const void* src, size_t srcSize, ZSTD_format_e format) { size_t const minInputSize = ZSTD_startingInputLength(format);- RETURN_ERROR_IF(srcSize < minInputSize, srcSize_wrong);+ RETURN_ERROR_IF(srcSize < minInputSize, srcSize_wrong, ""); { BYTE const fhd = ((const BYTE*)src)[minInputSize-1]; U32 const dictID= fhd & 3;@@ -259,7 +261,7 @@ zfhPtr->frameType = ZSTD_skippableFrame; return 0; }- RETURN_ERROR(prefix_unknown);+ RETURN_ERROR(prefix_unknown, ""); } /* ensure there is enough `srcSize` to fully read/decode frame header */@@ -283,7 +285,7 @@ if (!singleSegment) { BYTE const wlByte = ip[pos++]; U32 const windowLog = (wlByte >> 3) + ZSTD_WINDOWLOG_ABSOLUTEMIN;- RETURN_ERROR_IF(windowLog > ZSTD_WINDOWLOG_MAX, frameParameter_windowTooLarge);+ RETURN_ERROR_IF(windowLog > ZSTD_WINDOWLOG_MAX, frameParameter_windowTooLarge, ""); windowSize = (1ULL << windowLog); windowSize += (windowSize >> 3) * (wlByte&7); }@@ -355,14 +357,14 @@ size_t const skippableHeaderSize = ZSTD_SKIPPABLEHEADERSIZE; U32 sizeU32; - RETURN_ERROR_IF(srcSize < ZSTD_SKIPPABLEHEADERSIZE, srcSize_wrong);+ RETURN_ERROR_IF(srcSize < ZSTD_SKIPPABLEHEADERSIZE, srcSize_wrong, ""); sizeU32 = MEM_readLE32((BYTE const*)src + ZSTD_FRAMEIDSIZE); RETURN_ERROR_IF((U32)(sizeU32 + ZSTD_SKIPPABLEHEADERSIZE) < sizeU32,- frameParameter_unsupported);+ frameParameter_unsupported, ""); { size_t const skippableSize = skippableHeaderSize + sizeU32;- RETURN_ERROR_IF(skippableSize > srcSize, srcSize_wrong);+ RETURN_ERROR_IF(skippableSize > srcSize, srcSize_wrong, ""); return skippableSize; } }@@ -376,7 +378,7 @@ { unsigned long long totalDstSize = 0; - while (srcSize >= ZSTD_FRAMEHEADERSIZE_PREFIX) {+ while (srcSize >= ZSTD_startingInputLength(ZSTD_f_zstd1)) { U32 const magicNumber = MEM_readLE32(src); if ((magicNumber & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) {@@ -442,7 +444,7 @@ * harder. */ RETURN_ERROR_IF(dctx->fParams.dictID && (dctx->dictID != dctx->fParams.dictID),- dictionary_wrong);+ dictionary_wrong, ""); #endif if (dctx->fParams.checksumFlag) XXH64_reset(&dctx->xxhState, 0); return 0;@@ -562,21 +564,11 @@ * Frame decoding ***************************************************************/ --void ZSTD_checkContinuity(ZSTD_DCtx* dctx, const void* dst)-{- if (dst != dctx->previousDstEnd) { /* not contiguous */- dctx->dictEnd = dctx->previousDstEnd;- dctx->virtualStart = (const char*)dst - ((const char*)(dctx->previousDstEnd) - (const char*)(dctx->prefixStart));- dctx->prefixStart = dst;- dctx->previousDstEnd = dst;- }-}- /** ZSTD_insertBlock() :- insert `src` block into `dctx` history. Useful to track uncompressed blocks. */+ * insert `src` block into `dctx` history. Useful to track uncompressed blocks. */ size_t ZSTD_insertBlock(ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize) {+ DEBUGLOG(5, "ZSTD_insertBlock: %u bytes", (unsigned)blockSize); ZSTD_checkContinuity(dctx, blockStart); dctx->previousDstEnd = (const char*)blockStart + blockSize; return blockSize;@@ -589,9 +581,9 @@ DEBUGLOG(5, "ZSTD_copyRawBlock"); if (dst == NULL) { if (srcSize == 0) return 0;- RETURN_ERROR(dstBuffer_null);+ RETURN_ERROR(dstBuffer_null, ""); }- RETURN_ERROR_IF(srcSize > dstCapacity, dstSize_tooSmall);+ RETURN_ERROR_IF(srcSize > dstCapacity, dstSize_tooSmall, ""); memcpy(dst, src, srcSize); return srcSize; }@@ -602,9 +594,9 @@ { if (dst == NULL) { if (regenSize == 0) return 0;- RETURN_ERROR(dstBuffer_null);+ RETURN_ERROR(dstBuffer_null, ""); }- RETURN_ERROR_IF(regenSize > dstCapacity, dstSize_tooSmall);+ RETURN_ERROR_IF(regenSize > dstCapacity, dstSize_tooSmall, ""); memset(dst, b, regenSize); return regenSize; }@@ -620,7 +612,7 @@ { const BYTE* ip = (const BYTE*)(*srcPtr); BYTE* const ostart = (BYTE* const)dst;- BYTE* const oend = ostart + dstCapacity;+ BYTE* const oend = dstCapacity != 0 ? ostart + dstCapacity : ostart; BYTE* op = ostart; size_t remainingSrcSize = *srcSizePtr; @@ -628,15 +620,16 @@ /* check */ RETURN_ERROR_IF(- remainingSrcSize < ZSTD_FRAMEHEADERSIZE_MIN+ZSTD_blockHeaderSize,- srcSize_wrong);+ remainingSrcSize < ZSTD_FRAMEHEADERSIZE_MIN(dctx->format)+ZSTD_blockHeaderSize,+ srcSize_wrong, ""); /* Frame Header */- { size_t const frameHeaderSize = ZSTD_frameHeaderSize(ip, ZSTD_FRAMEHEADERSIZE_PREFIX);+ { size_t const frameHeaderSize = ZSTD_frameHeaderSize_internal(+ ip, ZSTD_FRAMEHEADERSIZE_PREFIX(dctx->format), dctx->format); if (ZSTD_isError(frameHeaderSize)) return frameHeaderSize; RETURN_ERROR_IF(remainingSrcSize < frameHeaderSize+ZSTD_blockHeaderSize,- srcSize_wrong);- FORWARD_IF_ERROR( ZSTD_decodeFrameHeader(dctx, ip, frameHeaderSize) );+ srcSize_wrong, "");+ FORWARD_IF_ERROR( ZSTD_decodeFrameHeader(dctx, ip, frameHeaderSize) , ""); ip += frameHeaderSize; remainingSrcSize -= frameHeaderSize; } @@ -649,7 +642,7 @@ ip += ZSTD_blockHeaderSize; remainingSrcSize -= ZSTD_blockHeaderSize;- RETURN_ERROR_IF(cBlockSize > remainingSrcSize, srcSize_wrong);+ RETURN_ERROR_IF(cBlockSize > remainingSrcSize, srcSize_wrong, ""); switch(blockProperties.blockType) {@@ -664,13 +657,15 @@ break; case bt_reserved : default:- RETURN_ERROR(corruption_detected);+ RETURN_ERROR(corruption_detected, "invalid block type"); } if (ZSTD_isError(decodedSize)) return decodedSize; if (dctx->fParams.checksumFlag) XXH64_update(&dctx->xxhState, op, decodedSize);- op += decodedSize;+ if (decodedSize != 0)+ op += decodedSize;+ assert(ip != NULL); ip += cBlockSize; remainingSrcSize -= cBlockSize; if (blockProperties.lastBlock) break;@@ -678,14 +673,14 @@ if (dctx->fParams.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN) { RETURN_ERROR_IF((U64)(op-ostart) != dctx->fParams.frameContentSize,- corruption_detected);+ corruption_detected, ""); } if (dctx->fParams.checksumFlag) { /* Frame content checksum verification */ U32 const checkCalc = (U32)XXH64_digest(&dctx->xxhState); U32 checkRead;- RETURN_ERROR_IF(remainingSrcSize<4, checksum_wrong);+ RETURN_ERROR_IF(remainingSrcSize<4, checksum_wrong, ""); checkRead = MEM_readLE32(ip);- RETURN_ERROR_IF(checkRead != checkCalc, checksum_wrong);+ RETURN_ERROR_IF(checkRead != checkCalc, checksum_wrong, ""); ip += 4; remainingSrcSize -= 4; }@@ -713,7 +708,7 @@ dictSize = ZSTD_DDict_dictSize(ddict); } - while (srcSize >= ZSTD_FRAMEHEADERSIZE_PREFIX) {+ while (srcSize >= ZSTD_startingInputLength(dctx->format)) { #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1) if (ZSTD_isLegacy(src, srcSize)) {@@ -742,7 +737,7 @@ (unsigned)magicNumber, ZSTD_MAGICNUMBER); if ((magicNumber & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) { size_t const skippableSize = readSkippableFrameSize(src, srcSize);- FORWARD_IF_ERROR(skippableSize);+ FORWARD_IF_ERROR(skippableSize, "readSkippableFrameSize failed"); assert(skippableSize <= srcSize); src = (const BYTE *)src + skippableSize;@@ -752,11 +747,11 @@ if (ddict) { /* we were called from ZSTD_decompress_usingDDict */- FORWARD_IF_ERROR(ZSTD_decompressBegin_usingDDict(dctx, ddict));+ FORWARD_IF_ERROR(ZSTD_decompressBegin_usingDDict(dctx, ddict), ""); } else { /* this will initialize correctly with no dict if dict == NULL, so * use this in all cases but ddict */- FORWARD_IF_ERROR(ZSTD_decompressBegin_usingDict(dctx, dict, dictSize));+ FORWARD_IF_ERROR(ZSTD_decompressBegin_usingDict(dctx, dict, dictSize), ""); } ZSTD_checkContinuity(dctx, dst); @@ -777,7 +772,8 @@ "error."); if (ZSTD_isError(res)) return res; assert(res <= dstCapacity);- dst = (BYTE*)dst + res;+ if (res != 0)+ dst = (BYTE*)dst + res; dstCapacity -= res; } moreThan1Frame = 1;@@ -825,7 +821,7 @@ #if defined(ZSTD_HEAPMODE) && (ZSTD_HEAPMODE>=1) size_t regenSize; ZSTD_DCtx* const dctx = ZSTD_createDCtx();- RETURN_ERROR_IF(dctx==NULL, memory_allocation);+ RETURN_ERROR_IF(dctx==NULL, memory_allocation, "NULL pointer!"); regenSize = ZSTD_decompressDCtx(dctx, dst, dstCapacity, src, srcSize); ZSTD_freeDCtx(dctx); return regenSize;@@ -843,6 +839,24 @@ ****************************************/ 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+ * be streamed.+ *+ * For blocks that can be streamed, this allows us to reduce the latency until we produce+ * output, and avoid copying the input.+ *+ * @param inputSize - The total amount of input that the caller currently has.+ */+static size_t ZSTD_nextSrcSizeToDecompressWithInputSize(ZSTD_DCtx* dctx, size_t inputSize) {+ if (!(dctx->stage == ZSTDds_decompressBlock || dctx->stage == ZSTDds_decompressLastBlock))+ return dctx->expected;+ if (dctx->bType != bt_raw)+ return dctx->expected;+ return MIN(MAX(inputSize, 1), dctx->expected);+}+ ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx) { switch(dctx->stage) {@@ -875,7 +889,7 @@ { DEBUGLOG(5, "ZSTD_decompressContinue (srcSize:%u)", (unsigned)srcSize); /* Sanity check */- RETURN_ERROR_IF(srcSize != dctx->expected, srcSize_wrong, "not allowed");+ RETURN_ERROR_IF(srcSize != ZSTD_nextSrcSizeToDecompressWithInputSize(dctx, srcSize), srcSize_wrong, "not allowed"); if (dstCapacity) ZSTD_checkContinuity(dctx, dst); switch (dctx->stage)@@ -900,7 +914,7 @@ case ZSTDds_decodeFrameHeader: assert(src != NULL); memcpy(dctx->headerBuffer + (dctx->headerSize - srcSize), src, srcSize);- FORWARD_IF_ERROR(ZSTD_decodeFrameHeader(dctx, dctx->headerBuffer, dctx->headerSize));+ FORWARD_IF_ERROR(ZSTD_decodeFrameHeader(dctx, dctx->headerBuffer, dctx->headerSize), ""); dctx->expected = ZSTD_blockHeaderSize; dctx->stage = ZSTDds_decodeBlockHeader; return 0;@@ -909,6 +923,7 @@ { blockProperties_t bp; size_t const cBlockSize = ZSTD_getcBlockSize(src, ZSTD_blockHeaderSize, &bp); if (ZSTD_isError(cBlockSize)) return cBlockSize;+ RETURN_ERROR_IF(cBlockSize > dctx->fParams.blockSizeMax, corruption_detected, "Block Size Exceeds Maximum"); dctx->expected = cBlockSize; dctx->bType = bp.blockType; dctx->rleSize = bp.origSize;@@ -941,28 +956,41 @@ case bt_compressed: DEBUGLOG(5, "ZSTD_decompressContinue: case bt_compressed"); rSize = ZSTD_decompressBlock_internal(dctx, dst, dstCapacity, src, srcSize, /* frame */ 1);+ dctx->expected = 0; /* Streaming not supported */ break; case bt_raw :+ assert(srcSize <= dctx->expected); rSize = ZSTD_copyRawBlock(dst, dstCapacity, src, srcSize);+ FORWARD_IF_ERROR(rSize, "ZSTD_copyRawBlock failed");+ assert(rSize == srcSize);+ dctx->expected -= rSize; break; case bt_rle : rSize = ZSTD_setRleBlock(dst, dstCapacity, *(const BYTE*)src, dctx->rleSize);+ dctx->expected = 0; /* Streaming not supported */ break; case bt_reserved : /* should never happen */ default:- RETURN_ERROR(corruption_detected);+ RETURN_ERROR(corruption_detected, "invalid block type"); }- if (ZSTD_isError(rSize)) return rSize;+ FORWARD_IF_ERROR(rSize, "");+ RETURN_ERROR_IF(rSize > dctx->fParams.blockSizeMax, corruption_detected, "Decompressed Block Size Exceeds Maximum"); DEBUGLOG(5, "ZSTD_decompressContinue: decoded size from block : %u", (unsigned)rSize); dctx->decodedSize += rSize; if (dctx->fParams.checksumFlag) XXH64_update(&dctx->xxhState, dst, rSize);+ dctx->previousDstEnd = (char*)dst + rSize; + /* Stay on the same stage until we are finished streaming the block. */+ if (dctx->expected > 0) {+ return rSize;+ }+ if (dctx->stage == ZSTDds_decompressLastBlock) { /* end of frame */ DEBUGLOG(4, "ZSTD_decompressContinue: decoded size from frame : %u", (unsigned)dctx->decodedSize); RETURN_ERROR_IF( dctx->fParams.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN && dctx->decodedSize != dctx->fParams.frameContentSize,- corruption_detected);+ corruption_detected, ""); if (dctx->fParams.checksumFlag) { /* another round for frame checksum */ dctx->expected = 4; dctx->stage = ZSTDds_checkChecksum;@@ -973,7 +1001,6 @@ } else { dctx->stage = ZSTDds_decodeBlockHeader; dctx->expected = ZSTD_blockHeaderSize;- dctx->previousDstEnd = (char*)dst + rSize; } return rSize; }@@ -983,7 +1010,7 @@ { U32 const h32 = (U32)XXH64_digest(&dctx->xxhState); U32 const check32 = MEM_readLE32(src); DEBUGLOG(4, "ZSTD_decompressContinue: checksum : calculated %08X :: %08X read", (unsigned)h32, (unsigned)check32);- RETURN_ERROR_IF(check32 != h32, checksum_wrong);+ RETURN_ERROR_IF(check32 != h32, checksum_wrong, ""); dctx->expected = 0; dctx->stage = ZSTDds_getFrameHeaderSize; return 0;@@ -1004,7 +1031,7 @@ default: assert(0); /* impossible */- RETURN_ERROR(GENERIC); /* some compiler require default to do something */+ RETURN_ERROR(GENERIC, "impossible to reach"); /* some compiler require default to do something */ } } @@ -1015,6 +1042,10 @@ dctx->virtualStart = (const char*)dict - ((const char*)(dctx->previousDstEnd) - (const char*)(dctx->prefixStart)); dctx->prefixStart = dict; dctx->previousDstEnd = (const char*)dict + dictSize;+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION+ dctx->dictContentBeginForFuzzing = dctx->prefixStart;+ dctx->dictContentEndForFuzzing = dctx->previousDstEnd;+#endif return 0; } @@ -1028,7 +1059,7 @@ const BYTE* dictPtr = (const BYTE*)dict; const BYTE* const dictEnd = dictPtr + dictSize; - RETURN_ERROR_IF(dictSize <= 8, dictionary_corrupted);+ RETURN_ERROR_IF(dictSize <= 8, dictionary_corrupted, "dict is too small"); assert(MEM_readLE32(dict) == ZSTD_MAGIC_DICTIONARY); /* dict must be valid */ dictPtr += 8; /* skip header = magic + dictID */ @@ -1047,16 +1078,16 @@ dictPtr, dictEnd - dictPtr, workspace, workspaceSize); #endif- RETURN_ERROR_IF(HUF_isError(hSize), dictionary_corrupted);+ RETURN_ERROR_IF(HUF_isError(hSize), dictionary_corrupted, ""); dictPtr += hSize; } { short offcodeNCount[MaxOff+1]; unsigned offcodeMaxValue = MaxOff, offcodeLog; size_t const offcodeHeaderSize = FSE_readNCount(offcodeNCount, &offcodeMaxValue, &offcodeLog, dictPtr, dictEnd-dictPtr);- RETURN_ERROR_IF(FSE_isError(offcodeHeaderSize), dictionary_corrupted);- RETURN_ERROR_IF(offcodeMaxValue > MaxOff, dictionary_corrupted);- RETURN_ERROR_IF(offcodeLog > OffFSELog, dictionary_corrupted);+ RETURN_ERROR_IF(FSE_isError(offcodeHeaderSize), dictionary_corrupted, "");+ RETURN_ERROR_IF(offcodeMaxValue > MaxOff, dictionary_corrupted, "");+ RETURN_ERROR_IF(offcodeLog > OffFSELog, dictionary_corrupted, ""); ZSTD_buildFSETable( entropy->OFTable, offcodeNCount, offcodeMaxValue, OF_base, OF_bits,@@ -1067,9 +1098,9 @@ { short matchlengthNCount[MaxML+1]; unsigned matchlengthMaxValue = MaxML, matchlengthLog; size_t const matchlengthHeaderSize = FSE_readNCount(matchlengthNCount, &matchlengthMaxValue, &matchlengthLog, dictPtr, dictEnd-dictPtr);- RETURN_ERROR_IF(FSE_isError(matchlengthHeaderSize), dictionary_corrupted);- RETURN_ERROR_IF(matchlengthMaxValue > MaxML, dictionary_corrupted);- RETURN_ERROR_IF(matchlengthLog > MLFSELog, dictionary_corrupted);+ RETURN_ERROR_IF(FSE_isError(matchlengthHeaderSize), dictionary_corrupted, "");+ RETURN_ERROR_IF(matchlengthMaxValue > MaxML, dictionary_corrupted, "");+ RETURN_ERROR_IF(matchlengthLog > MLFSELog, dictionary_corrupted, ""); ZSTD_buildFSETable( entropy->MLTable, matchlengthNCount, matchlengthMaxValue, ML_base, ML_bits,@@ -1080,9 +1111,9 @@ { short litlengthNCount[MaxLL+1]; unsigned litlengthMaxValue = MaxLL, litlengthLog; size_t const litlengthHeaderSize = FSE_readNCount(litlengthNCount, &litlengthMaxValue, &litlengthLog, dictPtr, dictEnd-dictPtr);- RETURN_ERROR_IF(FSE_isError(litlengthHeaderSize), dictionary_corrupted);- RETURN_ERROR_IF(litlengthMaxValue > MaxLL, dictionary_corrupted);- RETURN_ERROR_IF(litlengthLog > LLFSELog, dictionary_corrupted);+ RETURN_ERROR_IF(FSE_isError(litlengthHeaderSize), dictionary_corrupted, "");+ RETURN_ERROR_IF(litlengthMaxValue > MaxLL, dictionary_corrupted, "");+ RETURN_ERROR_IF(litlengthLog > LLFSELog, dictionary_corrupted, ""); ZSTD_buildFSETable( entropy->LLTable, litlengthNCount, litlengthMaxValue, LL_base, LL_bits,@@ -1090,13 +1121,13 @@ dictPtr += litlengthHeaderSize; } - RETURN_ERROR_IF(dictPtr+12 > dictEnd, dictionary_corrupted);+ RETURN_ERROR_IF(dictPtr+12 > dictEnd, dictionary_corrupted, ""); { int i; size_t const dictContentSize = (size_t)(dictEnd - (dictPtr+12)); for (i=0; i<3; i++) { U32 const rep = MEM_readLE32(dictPtr); dictPtr += 4;- RETURN_ERROR_IF(rep==0 || rep >= dictContentSize,- dictionary_corrupted);+ RETURN_ERROR_IF(rep==0 || rep > dictContentSize,+ dictionary_corrupted, ""); entropy->rep[i] = rep; } } @@ -1114,7 +1145,7 @@ /* load entropy tables */ { size_t const eSize = ZSTD_loadDEntropy(&dctx->entropy, dict, dictSize);- RETURN_ERROR_IF(ZSTD_isError(eSize), dictionary_corrupted);+ RETURN_ERROR_IF(ZSTD_isError(eSize), dictionary_corrupted, ""); dict = (const char*)dict + eSize; dictSize -= eSize; }@@ -1137,6 +1168,7 @@ dctx->entropy.hufTable[0] = (HUF_DTable)((HufLog)*0x1000001); /* cover both little and big endian */ dctx->litEntropy = dctx->fseEntropy = 0; dctx->dictID = 0;+ dctx->bType = bt_reserved; ZSTD_STATIC_ASSERT(sizeof(dctx->entropy.rep) == sizeof(repStartValue)); memcpy(dctx->entropy.rep, repStartValue, sizeof(repStartValue)); /* initial repcodes */ dctx->LLTptr = dctx->entropy.LLTable;@@ -1148,11 +1180,11 @@ size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx* dctx, const void* dict, size_t dictSize) {- FORWARD_IF_ERROR( ZSTD_decompressBegin(dctx) );+ FORWARD_IF_ERROR( ZSTD_decompressBegin(dctx) , ""); if (dict && dictSize) RETURN_ERROR_IF( ZSTD_isError(ZSTD_decompress_insertDictionary(dctx, dict, dictSize)),- dictionary_corrupted);+ dictionary_corrupted, ""); return 0; } @@ -1171,7 +1203,7 @@ DEBUGLOG(4, "DDict is %s", dctx->ddictIsCold ? "~cold~" : "hot!"); }- FORWARD_IF_ERROR( ZSTD_decompressBegin(dctx) );+ FORWARD_IF_ERROR( ZSTD_decompressBegin(dctx) , ""); if (ddict) { /* NULL ddict is equivalent to no dictionary */ ZSTD_copyDDictParameters(dctx, ddict); }@@ -1262,11 +1294,11 @@ ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType) {- RETURN_ERROR_IF(dctx->streamStage != zdss_init, stage_wrong);+ RETURN_ERROR_IF(dctx->streamStage != zdss_init, stage_wrong, ""); ZSTD_clearDict(dctx);- if (dict && dictSize >= 8) {+ if (dict && dictSize != 0) { dctx->ddictLocal = ZSTD_createDDict_advanced(dict, dictSize, dictLoadMethod, dictContentType, dctx->customMem);- RETURN_ERROR_IF(dctx->ddictLocal == NULL, memory_allocation);+ RETURN_ERROR_IF(dctx->ddictLocal == NULL, memory_allocation, "NULL pointer!"); dctx->ddict = dctx->ddictLocal; dctx->dictUses = ZSTD_use_indefinitely; }@@ -1285,7 +1317,7 @@ size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType) {- FORWARD_IF_ERROR(ZSTD_DCtx_loadDictionary_advanced(dctx, prefix, prefixSize, ZSTD_dlm_byRef, dictContentType));+ FORWARD_IF_ERROR(ZSTD_DCtx_loadDictionary_advanced(dctx, prefix, prefixSize, ZSTD_dlm_byRef, dictContentType), ""); dctx->dictUses = ZSTD_use_once; return 0; }@@ -1297,14 +1329,14 @@ /* ZSTD_initDStream_usingDict() :- * return : expected size, aka ZSTD_FRAMEHEADERSIZE_PREFIX.+ * return : expected size, aka ZSTD_startingInputLength(). * this function cannot fail */ size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize) { DEBUGLOG(4, "ZSTD_initDStream_usingDict");- FORWARD_IF_ERROR( ZSTD_DCtx_reset(zds, ZSTD_reset_session_only) );- FORWARD_IF_ERROR( ZSTD_DCtx_loadDictionary(zds, dict, dictSize) );- return ZSTD_FRAMEHEADERSIZE_PREFIX;+ FORWARD_IF_ERROR( ZSTD_DCtx_reset(zds, ZSTD_reset_session_only) , "");+ FORWARD_IF_ERROR( ZSTD_DCtx_loadDictionary(zds, dict, dictSize) , "");+ return ZSTD_startingInputLength(zds->format); } /* note : this variant can't fail */@@ -1319,24 +1351,24 @@ * this function cannot fail */ size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* dctx, const ZSTD_DDict* ddict) {- FORWARD_IF_ERROR( ZSTD_DCtx_reset(dctx, ZSTD_reset_session_only) );- FORWARD_IF_ERROR( ZSTD_DCtx_refDDict(dctx, ddict) );- return ZSTD_FRAMEHEADERSIZE_PREFIX;+ FORWARD_IF_ERROR( ZSTD_DCtx_reset(dctx, ZSTD_reset_session_only) , "");+ FORWARD_IF_ERROR( ZSTD_DCtx_refDDict(dctx, ddict) , "");+ return ZSTD_startingInputLength(dctx->format); } /* ZSTD_resetDStream() :- * return : expected size, aka ZSTD_FRAMEHEADERSIZE_PREFIX.+ * return : expected size, aka ZSTD_startingInputLength(). * this function cannot fail */ size_t ZSTD_resetDStream(ZSTD_DStream* dctx) {- FORWARD_IF_ERROR(ZSTD_DCtx_reset(dctx, ZSTD_reset_session_only));- return ZSTD_FRAMEHEADERSIZE_PREFIX;+ FORWARD_IF_ERROR(ZSTD_DCtx_reset(dctx, ZSTD_reset_session_only), "");+ return ZSTD_startingInputLength(dctx->format); } size_t ZSTD_DCtx_refDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict) {- RETURN_ERROR_IF(dctx->streamStage != zdss_init, stage_wrong);+ RETURN_ERROR_IF(dctx->streamStage != zdss_init, stage_wrong, ""); ZSTD_clearDict(dctx); if (ddict) { dctx->ddict = ddict;@@ -1353,9 +1385,9 @@ ZSTD_bounds const bounds = ZSTD_dParam_getBounds(ZSTD_d_windowLogMax); size_t const min = (size_t)1 << bounds.lowerBound; size_t const max = (size_t)1 << bounds.upperBound;- RETURN_ERROR_IF(dctx->streamStage != zdss_init, stage_wrong);- RETURN_ERROR_IF(maxWindowSize < min, parameter_outOfBound);- RETURN_ERROR_IF(maxWindowSize > max, parameter_outOfBound);+ RETURN_ERROR_IF(dctx->streamStage != zdss_init, stage_wrong, "");+ RETURN_ERROR_IF(maxWindowSize < min, parameter_outOfBound, "");+ RETURN_ERROR_IF(maxWindowSize > max, parameter_outOfBound, ""); dctx->maxWindowSize = maxWindowSize; return 0; }@@ -1378,6 +1410,10 @@ bounds.upperBound = (int)ZSTD_f_zstd1_magicless; ZSTD_STATIC_ASSERT(ZSTD_f_zstd1 < ZSTD_f_zstd1_magicless); return bounds;+ case ZSTD_d_stableOutBuffer:+ bounds.lowerBound = (int)ZSTD_obm_buffered;+ bounds.upperBound = (int)ZSTD_obm_stable;+ return bounds; default:; } bounds.error = ERROR(parameter_unsupported);@@ -1397,12 +1433,12 @@ } #define CHECK_DBOUNDS(p,v) { \- RETURN_ERROR_IF(!ZSTD_dParam_withinBounds(p, v), parameter_outOfBound); \+ RETURN_ERROR_IF(!ZSTD_dParam_withinBounds(p, v), parameter_outOfBound, ""); \ } size_t ZSTD_DCtx_setParameter(ZSTD_DCtx* dctx, ZSTD_dParameter dParam, int value) {- RETURN_ERROR_IF(dctx->streamStage != zdss_init, stage_wrong);+ RETURN_ERROR_IF(dctx->streamStage != zdss_init, stage_wrong, ""); switch(dParam) { case ZSTD_d_windowLogMax: if (value == 0) value = ZSTD_WINDOWLOG_LIMIT_DEFAULT;@@ -1413,9 +1449,13 @@ CHECK_DBOUNDS(ZSTD_d_format, value); dctx->format = (ZSTD_format_e)value; return 0;+ case ZSTD_d_stableOutBuffer:+ CHECK_DBOUNDS(ZSTD_d_stableOutBuffer, value);+ dctx->outBufferMode = (ZSTD_outBufferMode_e)value;+ return 0; default:; }- RETURN_ERROR(parameter_unsupported);+ RETURN_ERROR(parameter_unsupported, ""); } size_t ZSTD_DCtx_reset(ZSTD_DCtx* dctx, ZSTD_ResetDirective reset)@@ -1427,7 +1467,7 @@ } if ( (reset == ZSTD_reset_parameters) || (reset == ZSTD_reset_session_and_parameters) ) {- RETURN_ERROR_IF(dctx->streamStage != zdss_init, stage_wrong);+ RETURN_ERROR_IF(dctx->streamStage != zdss_init, stage_wrong, ""); ZSTD_clearDict(dctx); dctx->format = ZSTD_f_zstd1; dctx->maxWindowSize = ZSTD_MAXWINDOWSIZE_DEFAULT;@@ -1448,7 +1488,7 @@ unsigned long long const neededSize = MIN(frameContentSize, neededRBSize); size_t const minRBSize = (size_t) neededSize; RETURN_ERROR_IF((unsigned long long)minRBSize != neededSize,- frameParameter_windowTooLarge);+ frameParameter_windowTooLarge, ""); return minRBSize; } @@ -1466,30 +1506,94 @@ ZSTD_frameHeader zfh; size_t const err = ZSTD_getFrameHeader(&zfh, src, srcSize); if (ZSTD_isError(err)) return err;- RETURN_ERROR_IF(err>0, srcSize_wrong);+ RETURN_ERROR_IF(err>0, srcSize_wrong, ""); RETURN_ERROR_IF(zfh.windowSize > windowSizeMax,- frameParameter_windowTooLarge);+ frameParameter_windowTooLarge, ""); return ZSTD_estimateDStreamSize((size_t)zfh.windowSize); } /* ***** Decompression ***** */ -MEM_STATIC size_t ZSTD_limitCopy(void* dst, size_t dstCapacity, const void* src, size_t srcSize)+static int ZSTD_DCtx_isOverflow(ZSTD_DStream* zds, size_t const neededInBuffSize, size_t const neededOutBuffSize) {- size_t const length = MIN(dstCapacity, srcSize);- memcpy(dst, src, length);- return length;+ return (zds->inBuffSize + zds->outBuffSize) >= (neededInBuffSize + neededOutBuffSize) * ZSTD_WORKSPACETOOLARGE_FACTOR; } +static void ZSTD_DCtx_updateOversizedDuration(ZSTD_DStream* zds, size_t const neededInBuffSize, size_t const neededOutBuffSize)+{+ if (ZSTD_DCtx_isOverflow(zds, neededInBuffSize, neededOutBuffSize))+ zds->oversizedDuration++;+ else + zds->oversizedDuration = 0;+} +static int ZSTD_DCtx_isOversizedTooLong(ZSTD_DStream* zds)+{+ return zds->oversizedDuration >= ZSTD_WORKSPACETOOLARGE_MAXDURATION;+}++/* Checks that the output buffer hasn't changed if ZSTD_obm_stable is used. */+static size_t ZSTD_checkOutBuffer(ZSTD_DStream const* zds, ZSTD_outBuffer const* output)+{+ ZSTD_outBuffer const expect = zds->expectedOutBuffer;+ /* No requirement when ZSTD_obm_stable is not enabled. */+ if (zds->outBufferMode != ZSTD_obm_stable)+ return 0;+ /* Any buffer is allowed in zdss_init, this must be the same for every other call until+ * the context is reset.+ */+ if (zds->streamStage == zdss_init)+ return 0;+ /* The buffer must match our expectation exactly. */+ if (expect.dst == output->dst && expect.pos == output->pos && expect.size == output->size)+ return 0;+ RETURN_ERROR(dstBuffer_wrong, "ZSTD_obm_stable enabled but output differs!");+}++/* Calls ZSTD_decompressContinue() with the right parameters for ZSTD_decompressStream()+ * and updates the stage and the output buffer state. This call is extracted so it can be+ * used both when reading directly from the ZSTD_inBuffer, and in buffered input mode.+ * NOTE: You must break after calling this function since the streamStage is modified.+ */+static size_t ZSTD_decompressContinueStream(+ ZSTD_DStream* zds, char** op, char* oend,+ void const* src, size_t srcSize) {+ int const isSkipFrame = ZSTD_isSkipFrame(zds);+ if (zds->outBufferMode == ZSTD_obm_buffered) {+ size_t const dstSize = isSkipFrame ? 0 : zds->outBuffSize - zds->outStart;+ size_t const decodedSize = ZSTD_decompressContinue(zds,+ zds->outBuff + zds->outStart, dstSize, src, srcSize);+ FORWARD_IF_ERROR(decodedSize, "");+ if (!decodedSize && !isSkipFrame) {+ zds->streamStage = zdss_read;+ } else {+ zds->outEnd = zds->outStart + decodedSize;+ zds->streamStage = zdss_flush;+ }+ } else {+ /* Write directly into the output buffer */+ size_t const dstSize = isSkipFrame ? 0 : oend - *op;+ size_t const decodedSize = ZSTD_decompressContinue(zds, *op, dstSize, src, srcSize);+ FORWARD_IF_ERROR(decodedSize, "");+ *op += decodedSize;+ /* Flushing is not needed. */+ zds->streamStage = zdss_read;+ assert(*op <= oend);+ assert(zds->outBufferMode == ZSTD_obm_stable);+ }+ return 0;+}+ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inBuffer* input) {- const char* const istart = (const char*)(input->src) + input->pos;- const char* const iend = (const char*)(input->src) + input->size;+ const char* const src = (const char*)input->src;+ const char* const istart = input->pos != 0 ? src + input->pos : src;+ const char* const iend = input->size != 0 ? src + input->size : src; const char* ip = istart;- char* const ostart = (char*)(output->dst) + output->pos;- char* const oend = (char*)(output->dst) + output->size;+ char* const dst = (char*)output->dst;+ char* const ostart = output->pos != 0 ? dst + output->pos : dst;+ char* const oend = output->size != 0 ? dst + output->size : dst; char* op = ostart; U32 someMoreWork = 1; @@ -1505,6 +1609,7 @@ "forbidden. out: pos: %u vs size: %u", (U32)output->pos, (U32)output->size); DEBUGLOG(5, "input size : %u", (U32)(input->size - input->pos));+ FORWARD_IF_ERROR(ZSTD_checkOutBuffer(zds, output), ""); while (someMoreWork) { switch(zds->streamStage)@@ -1515,6 +1620,7 @@ zds->lhSize = zds->inPos = zds->outStart = zds->outEnd = 0; zds->legacyVersion = 0; zds->hostageByte = 0;+ zds->expectedOutBuffer = *output; /* fall-through */ case zdss_loadHeader :@@ -1542,7 +1648,7 @@ "legacy support is incompatible with static dctx"); FORWARD_IF_ERROR(ZSTD_initLegacyStream(&zds->legacyContext, zds->previousLegacyVersion, legacyVersion,- dict, dictSize));+ dict, dictSize), ""); zds->legacyVersion = zds->previousLegacyVersion = legacyVersion; { size_t const hint = ZSTD_decompressLegacyStream(zds->legacyContext, legacyVersion, output, input); if (hint==0) zds->streamStage = zdss_init; /* or stay in stage zdss_loadHeader */@@ -1561,7 +1667,7 @@ zds->lhSize += remainingInput; } input->pos = input->size;- return (MAX(ZSTD_FRAMEHEADERSIZE_MIN, hSize) - zds->lhSize) + ZSTD_blockHeaderSize; /* remaining header bytes + next block header */+ return (MAX((size_t)ZSTD_FRAMEHEADERSIZE_MIN(zds->format), hSize) - zds->lhSize) + ZSTD_blockHeaderSize; /* remaining header bytes + next block header */ } assert(ip != NULL); memcpy(zds->headerBuffer + zds->lhSize, ip, toLoad); zds->lhSize = hSize; ip += toLoad;@@ -1569,7 +1675,8 @@ } } /* check for single-pass mode opportunity */- if (zds->fParams.frameContentSize && zds->fParams.windowSize /* skippable frame if == 0 */+ if (zds->fParams.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN+ && zds->fParams.frameType != ZSTD_skippableFrame && (U64)(size_t)(oend-op) >= zds->fParams.frameContentSize) { size_t const cSize = ZSTD_findFrameCompressedSize(istart, iend-istart); if (cSize <= (size_t)(iend-istart)) {@@ -1585,15 +1692,23 @@ break; } } + /* Check output buffer is large enough for ZSTD_odm_stable. */+ if (zds->outBufferMode == ZSTD_obm_stable+ && zds->fParams.frameType != ZSTD_skippableFrame+ && zds->fParams.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN+ && (U64)(size_t)(oend-op) < zds->fParams.frameContentSize) {+ RETURN_ERROR(dstSize_tooSmall, "ZSTD_obm_stable passed but ZSTD_outBuffer is too small");+ }+ /* Consume header (see ZSTDds_decodeFrameHeader) */ DEBUGLOG(4, "Consume header");- FORWARD_IF_ERROR(ZSTD_decompressBegin_usingDDict(zds, ZSTD_getDDict(zds)));+ FORWARD_IF_ERROR(ZSTD_decompressBegin_usingDDict(zds, ZSTD_getDDict(zds)), ""); if ((MEM_readLE32(zds->headerBuffer) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) { /* skippable frame */ zds->expected = MEM_readLE32(zds->headerBuffer + ZSTD_FRAMEIDSIZE); zds->stage = ZSTDds_skipFrame; } else {- FORWARD_IF_ERROR(ZSTD_decodeFrameHeader(zds, zds->headerBuffer, zds->lhSize));+ FORWARD_IF_ERROR(ZSTD_decodeFrameHeader(zds, zds->headerBuffer, zds->lhSize), ""); zds->expected = ZSTD_blockHeaderSize; zds->stage = ZSTDds_decodeBlockHeader; }@@ -1604,40 +1719,48 @@ (U32)(zds->maxWindowSize >> 10) ); zds->fParams.windowSize = MAX(zds->fParams.windowSize, 1U << ZSTD_WINDOWLOG_ABSOLUTEMIN); RETURN_ERROR_IF(zds->fParams.windowSize > zds->maxWindowSize,- frameParameter_windowTooLarge);+ frameParameter_windowTooLarge, ""); /* Adapt buffer sizes to frame header instructions */ { size_t const neededInBuffSize = MAX(zds->fParams.blockSizeMax, 4 /* frame checksum */);- size_t const neededOutBuffSize = ZSTD_decodingBufferSize_min(zds->fParams.windowSize, zds->fParams.frameContentSize);- if ((zds->inBuffSize < neededInBuffSize) || (zds->outBuffSize < neededOutBuffSize)) {- size_t const bufferSize = neededInBuffSize + neededOutBuffSize;- DEBUGLOG(4, "inBuff : from %u to %u",- (U32)zds->inBuffSize, (U32)neededInBuffSize);- DEBUGLOG(4, "outBuff : from %u to %u",- (U32)zds->outBuffSize, (U32)neededOutBuffSize);- if (zds->staticSize) { /* static DCtx */- DEBUGLOG(4, "staticSize : %u", (U32)zds->staticSize);- assert(zds->staticSize >= sizeof(ZSTD_DCtx)); /* controlled at init */- RETURN_ERROR_IF(- bufferSize > zds->staticSize - sizeof(ZSTD_DCtx),- memory_allocation);- } else {- ZSTD_free(zds->inBuff, zds->customMem);- zds->inBuffSize = 0;- zds->outBuffSize = 0;- zds->inBuff = (char*)ZSTD_malloc(bufferSize, zds->customMem);- RETURN_ERROR_IF(zds->inBuff == NULL, memory_allocation);- }- zds->inBuffSize = neededInBuffSize;- zds->outBuff = zds->inBuff + zds->inBuffSize;- zds->outBuffSize = neededOutBuffSize;- } }+ size_t const neededOutBuffSize = zds->outBufferMode == ZSTD_obm_buffered+ ? ZSTD_decodingBufferSize_min(zds->fParams.windowSize, zds->fParams.frameContentSize)+ : 0;++ ZSTD_DCtx_updateOversizedDuration(zds, neededInBuffSize, neededOutBuffSize);++ { int const tooSmall = (zds->inBuffSize < neededInBuffSize) || (zds->outBuffSize < neededOutBuffSize);+ int const tooLarge = ZSTD_DCtx_isOversizedTooLong(zds);+ + if (tooSmall || tooLarge) {+ size_t const bufferSize = neededInBuffSize + neededOutBuffSize;+ DEBUGLOG(4, "inBuff : from %u to %u",+ (U32)zds->inBuffSize, (U32)neededInBuffSize);+ DEBUGLOG(4, "outBuff : from %u to %u",+ (U32)zds->outBuffSize, (U32)neededOutBuffSize);+ if (zds->staticSize) { /* static DCtx */+ DEBUGLOG(4, "staticSize : %u", (U32)zds->staticSize);+ assert(zds->staticSize >= sizeof(ZSTD_DCtx)); /* controlled at init */+ RETURN_ERROR_IF(+ bufferSize > zds->staticSize - sizeof(ZSTD_DCtx),+ memory_allocation, "");+ } else {+ ZSTD_free(zds->inBuff, zds->customMem);+ zds->inBuffSize = 0;+ zds->outBuffSize = 0;+ zds->inBuff = (char*)ZSTD_malloc(bufferSize, zds->customMem);+ RETURN_ERROR_IF(zds->inBuff == NULL, memory_allocation, "");+ }+ zds->inBuffSize = neededInBuffSize;+ zds->outBuff = zds->inBuff + zds->inBuffSize;+ zds->outBuffSize = neededOutBuffSize;+ } } } zds->streamStage = zdss_read; /* fall-through */ case zdss_read: DEBUGLOG(5, "stage zdss_read");- { size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zds);+ { size_t const neededInSize = ZSTD_nextSrcSizeToDecompressWithInputSize(zds, iend - ip); DEBUGLOG(5, "neededInSize = %u", (U32)neededInSize); if (neededInSize==0) { /* end of frame */ zds->streamStage = zdss_init;@@ -1645,15 +1768,9 @@ break; } if ((size_t)(iend-ip) >= neededInSize) { /* decode directly from src */- int const isSkipFrame = ZSTD_isSkipFrame(zds);- size_t const decodedSize = ZSTD_decompressContinue(zds,- zds->outBuff + zds->outStart, (isSkipFrame ? 0 : zds->outBuffSize - zds->outStart),- ip, neededInSize);- if (ZSTD_isError(decodedSize)) return decodedSize;+ FORWARD_IF_ERROR(ZSTD_decompressContinueStream(zds, &op, oend, ip, neededInSize), ""); ip += neededInSize;- if (!decodedSize && !isSkipFrame) break; /* this was just a header */- zds->outEnd = zds->outStart + decodedSize;- zds->streamStage = zdss_flush;+ /* Function modifies the stage so we must break */ break; } } if (ip==iend) { someMoreWork = 0; break; } /* no more input */@@ -1665,6 +1782,8 @@ size_t const toLoad = neededInSize - zds->inPos; 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)); if (isSkipFrame) { loadedSize = MIN(toLoad, (size_t)(iend-ip)); } else {@@ -1678,17 +1797,11 @@ if (loadedSize < toLoad) { someMoreWork = 0; break; } /* not enough input, wait for more */ /* decode loaded input */- { size_t const decodedSize = ZSTD_decompressContinue(zds,- zds->outBuff + zds->outStart, zds->outBuffSize - zds->outStart,- zds->inBuff, neededInSize);- if (ZSTD_isError(decodedSize)) return decodedSize;- zds->inPos = 0; /* input is consumed */- if (!decodedSize && !isSkipFrame) { zds->streamStage = zdss_read; break; } /* this was just a header */- zds->outEnd = zds->outStart + decodedSize;- } }- zds->streamStage = zdss_flush;- /* fall-through */-+ zds->inPos = 0; /* input is consumed */+ FORWARD_IF_ERROR(ZSTD_decompressContinueStream(zds, &op, oend, zds->inBuff, neededInSize), "");+ /* Function modifies the stage so we must break */+ break;+ } case zdss_flush: { size_t const toFlushSize = zds->outEnd - zds->outStart; size_t const flushedSize = ZSTD_limitCopy(op, oend-op, zds->outBuff + zds->outStart, toFlushSize);@@ -1711,17 +1824,21 @@ default: assert(0); /* impossible */- RETURN_ERROR(GENERIC); /* some compiler require default to do something */+ RETURN_ERROR(GENERIC, "impossible to reach"); /* some compiler require default to do something */ } } /* result */ input->pos = (size_t)(ip - (const char*)(input->src)); output->pos = (size_t)(op - (char*)(output->dst));++ /* Update the expected output buffer for ZSTD_obm_stable. */+ zds->expectedOutBuffer = *output;+ 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, dstSize_tooSmall, "");+ RETURN_ERROR_IF(ip==iend, srcSize_wrong, ""); assert(0); } } else {
zstd/lib/decompress/zstd_decompress_block.c view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -15,14 +15,14 @@ * Dependencies *********************************************************/ #include <string.h> /* memcpy, memmove, memset */-#include "compiler.h" /* prefetch */-#include "cpu.h" /* bmi2 */-#include "mem.h" /* low level memory routines */+#include "../common/compiler.h" /* prefetch */+#include "../common/cpu.h" /* bmi2 */+#include "../common/mem.h" /* low level memory routines */ #define FSE_STATIC_LINKING_ONLY-#include "fse.h"+#include "../common/fse.h" #define HUF_STATIC_LINKING_ONLY-#include "huf.h"-#include "zstd_internal.h"+#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"@@ -56,7 +56,7 @@ size_t ZSTD_getcBlockSize(const void* src, size_t srcSize, blockProperties_t* bpPtr) {- RETURN_ERROR_IF(srcSize < ZSTD_blockHeaderSize, srcSize_wrong);+ RETURN_ERROR_IF(srcSize < ZSTD_blockHeaderSize, srcSize_wrong, ""); { U32 const cBlockHeader = MEM_readLE24(src); U32 const cSize = cBlockHeader >> 3;@@ -64,7 +64,7 @@ bpPtr->blockType = (blockType_e)((cBlockHeader >> 1) & 3); bpPtr->origSize = cSize; /* only useful for RLE */ if (bpPtr->blockType == bt_rle) return 1;- RETURN_ERROR_IF(bpPtr->blockType == bt_reserved, corruption_detected);+ RETURN_ERROR_IF(bpPtr->blockType == bt_reserved, corruption_detected, ""); return cSize; } }@@ -79,7 +79,8 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx, const void* src, size_t srcSize) /* note : srcSize < BLOCKSIZE */ {- RETURN_ERROR_IF(srcSize < MIN_CBLOCK_SIZE, corruption_detected);+ DEBUGLOG(5, "ZSTD_decodeLiteralsBlock");+ RETURN_ERROR_IF(srcSize < MIN_CBLOCK_SIZE, corruption_detected, ""); { const BYTE* const istart = (const BYTE*) src; symbolEncodingType_e const litEncType = (symbolEncodingType_e)(istart[0] & 3);@@ -87,7 +88,8 @@ switch(litEncType) { case set_repeat:- RETURN_ERROR_IF(dctx->litEntropy==0, dictionary_corrupted);+ DEBUGLOG(5, "set_repeat flag : re-using stats from previous compressed literals block");+ RETURN_ERROR_IF(dctx->litEntropy==0, dictionary_corrupted, ""); /* fall-through */ case set_compressed:@@ -116,11 +118,11 @@ /* 2 - 2 - 18 - 18 */ lhSize = 5; litSize = (lhc >> 4) & 0x3FFFF;- litCSize = (lhc >> 22) + (istart[4] << 10);+ litCSize = (lhc >> 22) + ((size_t)istart[4] << 10); break; }- RETURN_ERROR_IF(litSize > ZSTD_BLOCKSIZE_MAX, corruption_detected);- RETURN_ERROR_IF(litCSize + lhSize > srcSize, corruption_detected);+ RETURN_ERROR_IF(litSize > ZSTD_BLOCKSIZE_MAX, corruption_detected, "");+ RETURN_ERROR_IF(litCSize + lhSize > srcSize, corruption_detected, ""); /* prefetch huffman table if cold */ if (dctx->ddictIsCold && (litSize > 768 /* heuristic */)) {@@ -158,7 +160,7 @@ } } - RETURN_ERROR_IF(HUF_isError(hufSuccess), corruption_detected);+ RETURN_ERROR_IF(HUF_isError(hufSuccess), corruption_detected, ""); dctx->litPtr = dctx->litBuffer; dctx->litSize = litSize;@@ -188,7 +190,7 @@ } if (lhSize+litSize+WILDCOPY_OVERLENGTH > srcSize) { /* risk reading beyond src buffer with wildcopy */- RETURN_ERROR_IF(litSize+lhSize > srcSize, corruption_detected);+ RETURN_ERROR_IF(litSize+lhSize > srcSize, corruption_detected, ""); memcpy(dctx->litBuffer, istart+lhSize, litSize); dctx->litPtr = dctx->litBuffer; dctx->litSize = litSize;@@ -220,7 +222,7 @@ RETURN_ERROR_IF(srcSize<4, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 3; here we need lhSize+1 = 4"); break; }- RETURN_ERROR_IF(litSize > ZSTD_BLOCKSIZE_MAX, corruption_detected);+ RETURN_ERROR_IF(litSize > ZSTD_BLOCKSIZE_MAX, corruption_detected, ""); memset(dctx->litBuffer, istart[lhSize], litSize + WILDCOPY_OVERLENGTH); dctx->litPtr = dctx->litBuffer; dctx->litSize = litSize;@@ -391,7 +393,8 @@ symbolNext[s] = 1; } else { if (normalizedCounter[s] >= largeLimit) DTableH.fastMode=0;- symbolNext[s] = normalizedCounter[s];+ assert(normalizedCounter[s]>=0);+ symbolNext[s] = (U16)normalizedCounter[s]; } } } memcpy(dt, &DTableH, sizeof(DTableH)); }@@ -437,8 +440,8 @@ switch(type) { case set_rle :- RETURN_ERROR_IF(!srcSize, srcSize_wrong);- RETURN_ERROR_IF((*(const BYTE*)src) > max, corruption_detected);+ RETURN_ERROR_IF(!srcSize, srcSize_wrong, "");+ RETURN_ERROR_IF((*(const BYTE*)src) > max, corruption_detected, ""); { U32 const symbol = *(const BYTE*)src; U32 const baseline = baseValue[symbol]; U32 const nbBits = nbAdditionalBits[symbol];@@ -450,7 +453,7 @@ *DTablePtr = defaultTable; return 0; case set_repeat:- RETURN_ERROR_IF(!flagRepeatTable, corruption_detected);+ RETURN_ERROR_IF(!flagRepeatTable, corruption_detected, ""); /* prefetch FSE table if used */ if (ddictIsCold && (nbSeq > 24 /* heuristic */)) { const void* const pStart = *DTablePtr;@@ -462,8 +465,8 @@ { unsigned tableLog; S16 norm[MaxSeq+1]; size_t const headerSize = FSE_readNCount(norm, &max, &tableLog, src, srcSize);- RETURN_ERROR_IF(FSE_isError(headerSize), corruption_detected);- RETURN_ERROR_IF(tableLog > maxLog, corruption_detected);+ RETURN_ERROR_IF(FSE_isError(headerSize), corruption_detected, "");+ RETURN_ERROR_IF(tableLog > maxLog, corruption_detected, ""); ZSTD_buildFSETable(DTableSpace, norm, max, baseValue, nbAdditionalBits, tableLog); *DTablePtr = DTableSpace; return headerSize;@@ -484,28 +487,28 @@ DEBUGLOG(5, "ZSTD_decodeSeqHeaders"); /* check */- RETURN_ERROR_IF(srcSize < MIN_SEQUENCES_SIZE, srcSize_wrong);+ RETURN_ERROR_IF(srcSize < MIN_SEQUENCES_SIZE, srcSize_wrong, ""); /* SeqHead */ nbSeq = *ip++; if (!nbSeq) { *nbSeqPtr=0;- RETURN_ERROR_IF(srcSize != 1, srcSize_wrong);+ RETURN_ERROR_IF(srcSize != 1, srcSize_wrong, ""); return 1; } if (nbSeq > 0x7F) { if (nbSeq == 0xFF) {- RETURN_ERROR_IF(ip+2 > iend, srcSize_wrong);+ RETURN_ERROR_IF(ip+2 > iend, srcSize_wrong, ""); nbSeq = MEM_readLE16(ip) + LONGNBSEQ, ip+=2; } else {- RETURN_ERROR_IF(ip >= iend, srcSize_wrong);+ RETURN_ERROR_IF(ip >= iend, srcSize_wrong, ""); nbSeq = ((nbSeq-0x80)<<8) + *ip++; } } *nbSeqPtr = nbSeq; /* FSE table descriptors */- RETURN_ERROR_IF(ip+4 > iend, srcSize_wrong); /* minimum possible size */+ RETURN_ERROR_IF(ip+1 > iend, srcSize_wrong, ""); /* minimum possible size: 1 byte for symbol encoding types */ { symbolEncodingType_e const LLtype = (symbolEncodingType_e)(*ip >> 6); symbolEncodingType_e const OFtype = (symbolEncodingType_e)((*ip >> 4) & 3); symbolEncodingType_e const MLtype = (symbolEncodingType_e)((*ip >> 2) & 3);@@ -518,7 +521,7 @@ LL_base, LL_bits, LL_defaultDTable, dctx->fseEntropy, dctx->ddictIsCold, nbSeq);- RETURN_ERROR_IF(ZSTD_isError(llhSize), corruption_detected);+ RETURN_ERROR_IF(ZSTD_isError(llhSize), corruption_detected, "ZSTD_buildSeqTable failed"); ip += llhSize; } @@ -528,7 +531,7 @@ OF_base, OF_bits, OF_defaultDTable, dctx->fseEntropy, dctx->ddictIsCold, nbSeq);- RETURN_ERROR_IF(ZSTD_isError(ofhSize), corruption_detected);+ RETURN_ERROR_IF(ZSTD_isError(ofhSize), corruption_detected, "ZSTD_buildSeqTable failed"); ip += ofhSize; } @@ -538,7 +541,7 @@ ML_base, ML_bits, ML_defaultDTable, dctx->fseEntropy, dctx->ddictIsCold, nbSeq);- RETURN_ERROR_IF(ZSTD_isError(mlhSize), corruption_detected);+ RETURN_ERROR_IF(ZSTD_isError(mlhSize), corruption_detected, "ZSTD_buildSeqTable failed"); ip += mlhSize; } }@@ -570,38 +573,118 @@ size_t pos; } seqState_t; +/*! ZSTD_overlapCopy8() :+ * Copies 8 bytes from ip to op and updates op and ip where ip <= op.+ * If the offset is < 8 then the offset is spread to at least 8 bytes.+ *+ * Precondition: *ip <= *op+ * Postcondition: *op - *op >= 8+ */+HINT_INLINE void ZSTD_overlapCopy8(BYTE** op, BYTE const** ip, size_t offset) {+ assert(*ip <= *op);+ if (offset < 8) {+ /* close range match, overlap */+ static const U32 dec32table[] = { 0, 1, 2, 1, 4, 4, 4, 4 }; /* added */+ static const int dec64table[] = { 8, 8, 8, 7, 8, 9,10,11 }; /* subtracted */+ int const sub2 = dec64table[offset];+ (*op)[0] = (*ip)[0];+ (*op)[1] = (*ip)[1];+ (*op)[2] = (*ip)[2];+ (*op)[3] = (*ip)[3];+ *ip += dec32table[offset];+ ZSTD_copy4(*op+4, *ip);+ *ip -= sub2;+ } else {+ ZSTD_copy8(*op, *ip);+ }+ *ip += 8;+ *op += 8;+ assert(*op - *ip >= 8);+} -/* ZSTD_execSequenceLast7():- * exceptional case : decompress a match starting within last 7 bytes of output buffer.- * requires more careful checks, to ensure there is no overflow.- * performance does not matter though.- * note : this case is supposed to be never generated "naturally" by reference encoder,- * since in most cases it needs at least 8 bytes to look for a match.- * but it's allowed by the specification. */+/*! ZSTD_safecopy() :+ * Specialized version of memcpy() that is allowed to READ up to WILDCOPY_OVERLENGTH past the input buffer+ * and write up to 16 bytes past oend_w (op >= oend_w is allowed).+ * This function is only called in the uncommon case where the sequence is near the end of the block. It+ * should be fast for a single long sequence, but can be slow for several short sequences.+ *+ * @param ovtype controls the overlap detection+ * - ZSTD_no_overlap: The source and destination are guaranteed to be at least WILDCOPY_VECLEN bytes apart.+ * - ZSTD_overlap_src_before_dst: The src and dst may overlap and may be any distance apart.+ * The src buffer must be before the dst buffer.+ */+static void ZSTD_safecopy(BYTE* op, BYTE* const oend_w, BYTE const* ip, ptrdiff_t length, ZSTD_overlap_e ovtype) {+ ptrdiff_t const diff = op - ip;+ BYTE* const oend = op + length;++ assert((ovtype == ZSTD_no_overlap && (diff <= -8 || diff >= 8 || op >= oend_w)) ||+ (ovtype == ZSTD_overlap_src_before_dst && diff >= 0));++ if (length < 8) {+ /* Handle short lengths. */+ while (op < oend) *op++ = *ip++;+ return;+ }+ if (ovtype == ZSTD_overlap_src_before_dst) {+ /* Copy 8 bytes and ensure the offset >= 8 when there can be overlap. */+ assert(length >= 8);+ ZSTD_overlapCopy8(&op, &ip, diff);+ assert(op - ip >= 8);+ assert(op <= oend);+ }++ if (oend <= oend_w) {+ /* No risk of overwrite. */+ ZSTD_wildcopy(op, ip, length, ovtype);+ return;+ }+ if (op <= oend_w) {+ /* Wildcopy until we get close to the end. */+ assert(oend > oend_w);+ ZSTD_wildcopy(op, ip, oend_w - op, ovtype);+ ip += oend_w - op;+ op = oend_w;+ }+ /* Handle the leftovers. */+ while (op < oend) *op++ = *ip++;+}++/* ZSTD_execSequenceEnd():+ * This version handles cases that are near the end of the output buffer. It requires+ * more careful checks to make sure there is no overflow. By separating out these hard+ * and unlikely cases, we can speed up the common cases.+ *+ * NOTE: This function needs to be fast for a single long sequence, but doesn't need+ * to be optimized for many small sequences, since those fall into ZSTD_execSequence().+ */ FORCE_NOINLINE-size_t ZSTD_execSequenceLast7(BYTE* op,- BYTE* const oend, seq_t sequence,- const BYTE** litPtr, const BYTE* const litLimit,- const BYTE* const base, const BYTE* const vBase, const BYTE* const dictEnd)+size_t ZSTD_execSequenceEnd(BYTE* op,+ BYTE* const oend, seq_t sequence,+ const BYTE** litPtr, const BYTE* const litLimit,+ const BYTE* const prefixStart, const BYTE* const virtualStart, const BYTE* const dictEnd) { BYTE* const oLitEnd = op + sequence.litLength; size_t const sequenceLength = sequence.litLength + sequence.matchLength;- BYTE* const oMatchEnd = op + sequenceLength; /* risk : address space overflow (32-bits) */ const BYTE* const iLitEnd = *litPtr + sequence.litLength; const BYTE* match = oLitEnd - sequence.offset;+ BYTE* const oend_w = oend - WILDCOPY_OVERLENGTH; - /* check */- RETURN_ERROR_IF(oMatchEnd>oend, dstSize_tooSmall, "last match must fit within dstBuffer");- RETURN_ERROR_IF(iLitEnd > litLimit, corruption_detected, "try to read beyond literal buffer");+ /* bounds checks : careful of address space overflow in 32-bit mode */+ RETURN_ERROR_IF(sequenceLength > (size_t)(oend - op), dstSize_tooSmall, "last match must fit within dstBuffer");+ RETURN_ERROR_IF(sequence.litLength > (size_t)(litLimit - *litPtr), corruption_detected, "try to read beyond literal buffer");+ assert(op < op + sequenceLength);+ assert(oLitEnd < op + sequenceLength); /* copy literals */- while (op < oLitEnd) *op++ = *(*litPtr)++;+ ZSTD_safecopy(op, oend_w, *litPtr, sequence.litLength, ZSTD_no_overlap);+ op = oLitEnd;+ *litPtr = iLitEnd; /* copy Match */- if (sequence.offset > (size_t)(oLitEnd - base)) {+ if (sequence.offset > (size_t)(oLitEnd - prefixStart)) { /* offset beyond prefix */- RETURN_ERROR_IF(sequence.offset > (size_t)(oLitEnd - vBase),corruption_detected);- match = dictEnd - (base-match);+ RETURN_ERROR_IF(sequence.offset > (size_t)(oLitEnd - virtualStart), corruption_detected, "");+ match = dictEnd - (prefixStart-match); if (match + sequence.matchLength <= dictEnd) { memmove(oLitEnd, match, sequence.matchLength); return sequenceLength;@@ -611,13 +694,12 @@ memmove(oLitEnd, match, length1); op = oLitEnd + length1; sequence.matchLength -= length1;- match = base;+ match = prefixStart; } }- while (op < oMatchEnd) *op++ = *match++;+ ZSTD_safecopy(op, oend_w, match, sequence.matchLength, ZSTD_overlap_src_before_dst); return sequenceLength; } - HINT_INLINE size_t ZSTD_execSequence(BYTE* op, BYTE* const oend, seq_t sequence,@@ -627,26 +709,47 @@ BYTE* const oLitEnd = op + sequence.litLength; size_t const sequenceLength = sequence.litLength + sequence.matchLength; BYTE* const oMatchEnd = op + sequenceLength; /* risk : address space overflow (32-bits) */- BYTE* const oend_w = oend - WILDCOPY_OVERLENGTH;+ BYTE* const oend_w = oend - WILDCOPY_OVERLENGTH; /* risk : address space underflow on oend=NULL */ const BYTE* const iLitEnd = *litPtr + sequence.litLength; const BYTE* match = oLitEnd - sequence.offset; - /* check */- RETURN_ERROR_IF(oMatchEnd>oend, dstSize_tooSmall, "last match must start at a minimum distance of WILDCOPY_OVERLENGTH from oend");- RETURN_ERROR_IF(iLitEnd > litLimit, corruption_detected, "over-read beyond lit buffer");- if (oLitEnd>oend_w) return ZSTD_execSequenceLast7(op, oend, sequence, litPtr, litLimit, prefixStart, virtualStart, dictEnd);+ assert(op != NULL /* Precondition */);+ assert(oend_w < oend /* No underflow */);+ /* Handle edge cases in a slow path:+ * - Read beyond end of literals+ * - Match end is within WILDCOPY_OVERLIMIT of oend+ * - 32-bit mode and the match length overflows+ */+ if (UNLIKELY(+ iLitEnd > litLimit ||+ oMatchEnd > oend_w ||+ (MEM_32bits() && (size_t)(oend - op) < sequenceLength + WILDCOPY_OVERLENGTH)))+ return ZSTD_execSequenceEnd(op, oend, sequence, litPtr, litLimit, prefixStart, virtualStart, dictEnd); - /* copy Literals */- ZSTD_copy8(op, *litPtr);- if (sequence.litLength > 8)- ZSTD_wildcopy(op+8, (*litPtr)+8, sequence.litLength - 8); /* note : since oLitEnd <= oend-WILDCOPY_OVERLENGTH, no risk of overwrite beyond oend */+ /* Assumptions (everything else goes into ZSTD_execSequenceEnd()) */+ assert(op <= oLitEnd /* No overflow */);+ assert(oLitEnd < oMatchEnd /* Non-zero match & no overflow */);+ assert(oMatchEnd <= oend /* No underflow */);+ assert(iLitEnd <= litLimit /* Literal length is in bounds */);+ assert(oLitEnd <= oend_w /* Can wildcopy literals */);+ assert(oMatchEnd <= oend_w /* Can wildcopy matches */);++ /* Copy Literals:+ * Split out litLength <= 16 since it is nearly always true. +1.6% on gcc-9.+ * We likely don't need the full 32-byte wildcopy.+ */+ assert(WILDCOPY_OVERLENGTH >= 16);+ ZSTD_copy16(op, (*litPtr));+ if (UNLIKELY(sequence.litLength > 16)) {+ ZSTD_wildcopy(op+16, (*litPtr)+16, sequence.litLength-16, ZSTD_no_overlap);+ } op = oLitEnd; *litPtr = iLitEnd; /* update for next sequence */ - /* copy Match */+ /* Copy Match */ if (sequence.offset > (size_t)(oLitEnd - prefixStart)) { /* offset beyond prefix -> go into extDict */- RETURN_ERROR_IF(sequence.offset > (size_t)(oLitEnd - virtualStart), corruption_detected);+ RETURN_ERROR_IF(UNLIKELY(sequence.offset > (size_t)(oLitEnd - virtualStart)), corruption_detected, ""); match = dictEnd + (match - prefixStart); if (match + sequence.matchLength <= dictEnd) { memmove(oLitEnd, match, sequence.matchLength);@@ -658,121 +761,33 @@ op = oLitEnd + length1; sequence.matchLength -= length1; match = prefixStart;- if (op > oend_w || sequence.matchLength < MINMATCH) {- U32 i;- for (i = 0; i < sequence.matchLength; ++i) op[i] = match[i];- return sequenceLength;- } } }- /* Requirement: op <= oend_w && sequence.matchLength >= MINMATCH */-- /* match within prefix */- if (sequence.offset < 8) {- /* close range match, overlap */- static const U32 dec32table[] = { 0, 1, 2, 1, 4, 4, 4, 4 }; /* added */- static const int dec64table[] = { 8, 8, 8, 7, 8, 9,10,11 }; /* subtracted */- int const sub2 = dec64table[sequence.offset];- op[0] = match[0];- op[1] = match[1];- op[2] = match[2];- op[3] = match[3];- match += dec32table[sequence.offset];- ZSTD_copy4(op+4, match);- match -= sub2;- } else {- ZSTD_copy8(op, match);- }- op += 8; match += 8;+ /* Match within prefix of 1 or more bytes */+ assert(op <= oMatchEnd);+ assert(oMatchEnd <= oend_w);+ assert(match >= prefixStart);+ assert(sequence.matchLength >= 1); - if (oMatchEnd > oend-(16-MINMATCH)) {- if (op < oend_w) {- ZSTD_wildcopy(op, match, oend_w - op);- match += oend_w - op;- op = oend_w;- }- while (op < oMatchEnd) *op++ = *match++;- } else {- ZSTD_wildcopy(op, match, (ptrdiff_t)sequence.matchLength-8); /* works even if matchLength < 8 */+ /* Nearly all offsets are >= WILDCOPY_VECLEN bytes, which means we can use wildcopy+ * without overlap checking.+ */+ if (LIKELY(sequence.offset >= WILDCOPY_VECLEN)) {+ /* We bet on a full wildcopy for matches, since we expect matches to be+ * longer than literals (in general). In silesia, ~10% of matches are longer+ * than 16 bytes.+ */+ ZSTD_wildcopy(op, match, (ptrdiff_t)sequence.matchLength, ZSTD_no_overlap);+ return sequenceLength; }- return sequenceLength;-}---HINT_INLINE-size_t ZSTD_execSequenceLong(BYTE* op,- BYTE* const oend, seq_t sequence,- const BYTE** litPtr, const BYTE* const litLimit,- const BYTE* const prefixStart, const BYTE* const dictStart, const BYTE* const dictEnd)-{- BYTE* const oLitEnd = op + sequence.litLength;- size_t const sequenceLength = sequence.litLength + sequence.matchLength;- BYTE* const oMatchEnd = op + sequenceLength; /* risk : address space overflow (32-bits) */- BYTE* const oend_w = oend - WILDCOPY_OVERLENGTH;- const BYTE* const iLitEnd = *litPtr + sequence.litLength;- const BYTE* match = sequence.match;-- /* check */- RETURN_ERROR_IF(oMatchEnd > oend, dstSize_tooSmall, "last match must start at a minimum distance of WILDCOPY_OVERLENGTH from oend");- RETURN_ERROR_IF(iLitEnd > litLimit, corruption_detected, "over-read beyond lit buffer");- if (oLitEnd > oend_w) return ZSTD_execSequenceLast7(op, oend, sequence, litPtr, litLimit, prefixStart, dictStart, dictEnd);-- /* copy Literals */- ZSTD_copy8(op, *litPtr); /* note : op <= oLitEnd <= oend_w == oend - 8 */- if (sequence.litLength > 8)- ZSTD_wildcopy(op+8, (*litPtr)+8, sequence.litLength - 8); /* note : since oLitEnd <= oend-WILDCOPY_OVERLENGTH, no risk of overwrite beyond oend */- op = oLitEnd;- *litPtr = iLitEnd; /* update for next sequence */-- /* copy Match */- if (sequence.offset > (size_t)(oLitEnd - prefixStart)) {- /* offset beyond prefix */- RETURN_ERROR_IF(sequence.offset > (size_t)(oLitEnd - dictStart), corruption_detected);- if (match + sequence.matchLength <= dictEnd) {- memmove(oLitEnd, match, sequence.matchLength);- return sequenceLength;- }- /* span extDict & currentPrefixSegment */- { size_t const length1 = dictEnd - match;- memmove(oLitEnd, match, length1);- op = oLitEnd + length1;- sequence.matchLength -= length1;- match = prefixStart;- if (op > oend_w || sequence.matchLength < MINMATCH) {- U32 i;- for (i = 0; i < sequence.matchLength; ++i) op[i] = match[i];- return sequenceLength;- }- } }- assert(op <= oend_w);- assert(sequence.matchLength >= MINMATCH);+ assert(sequence.offset < WILDCOPY_VECLEN); - /* match within prefix */- if (sequence.offset < 8) {- /* close range match, overlap */- static const U32 dec32table[] = { 0, 1, 2, 1, 4, 4, 4, 4 }; /* added */- static const int dec64table[] = { 8, 8, 8, 7, 8, 9,10,11 }; /* subtracted */- int const sub2 = dec64table[sequence.offset];- op[0] = match[0];- op[1] = match[1];- op[2] = match[2];- op[3] = match[3];- match += dec32table[sequence.offset];- ZSTD_copy4(op+4, match);- match -= sub2;- } else {- ZSTD_copy8(op, match);- }- op += 8; match += 8;+ /* Copy 8 bytes and spread the offset to be >= 8. */+ ZSTD_overlapCopy8(&op, &match, sequence.offset); - if (oMatchEnd > oend-(16-MINMATCH)) {- if (op < oend_w) {- ZSTD_wildcopy(op, match, oend_w - op);- match += oend_w - op;- op = oend_w;- }- while (op < oMatchEnd) *op++ = *match++;- } else {- ZSTD_wildcopy(op, match, (ptrdiff_t)sequence.matchLength-8); /* works even if matchLength < 8 */+ /* If the match length is > 8 bytes, then continue with the wildcopy. */+ if (sequence.matchLength > 8) {+ assert(op < oMatchEnd);+ ZSTD_wildcopy(op, match, (ptrdiff_t)sequence.matchLength-8, ZSTD_overlap_src_before_dst); } return sequenceLength; }@@ -798,6 +813,14 @@ DStatePtr->state = DInfo.nextState + lowBits; } +FORCE_INLINE_TEMPLATE void+ZSTD_updateFseStateWithDInfo(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD, ZSTD_seqSymbol const DInfo)+{+ U32 const nbBits = DInfo.nbBits;+ size_t const lowBits = BIT_readBits(bitD, nbBits);+ DStatePtr->state = DInfo.nextState + lowBits;+}+ /* We need to add at most (ZSTD_WINDOWLOG_MAX_32 - 1) bits to read the maximum * offset bits. But we can only read at most (STREAM_ACCUMULATOR_MIN_32 - 1) * bits before reloading. This value is the maximum number of bytes we read@@ -809,25 +832,26 @@ : 0) typedef enum { ZSTD_lo_isRegularOffset, ZSTD_lo_isLongOffset=1 } ZSTD_longOffset_e;+typedef enum { ZSTD_p_noPrefetch=0, ZSTD_p_prefetch=1 } ZSTD_prefetch_e; -#ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG FORCE_INLINE_TEMPLATE seq_t-ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e longOffsets)+ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e longOffsets, const ZSTD_prefetch_e prefetch) { seq_t seq;- U32 const llBits = seqState->stateLL.table[seqState->stateLL.state].nbAdditionalBits;- U32 const mlBits = seqState->stateML.table[seqState->stateML.state].nbAdditionalBits;- U32 const ofBits = seqState->stateOffb.table[seqState->stateOffb.state].nbAdditionalBits;- U32 const totalBits = llBits+mlBits+ofBits;- U32 const llBase = seqState->stateLL.table[seqState->stateLL.state].baseValue;- U32 const mlBase = seqState->stateML.table[seqState->stateML.state].baseValue;- U32 const ofBase = seqState->stateOffb.table[seqState->stateOffb.state].baseValue;+ ZSTD_seqSymbol const llDInfo = seqState->stateLL.table[seqState->stateLL.state];+ ZSTD_seqSymbol const mlDInfo = seqState->stateML.table[seqState->stateML.state];+ ZSTD_seqSymbol const ofDInfo = seqState->stateOffb.table[seqState->stateOffb.state];+ U32 const llBase = llDInfo.baseValue;+ U32 const mlBase = mlDInfo.baseValue;+ U32 const ofBase = ofDInfo.baseValue;+ BYTE const llBits = llDInfo.nbAdditionalBits;+ BYTE const mlBits = mlDInfo.nbAdditionalBits;+ BYTE const ofBits = ofDInfo.nbAdditionalBits;+ BYTE const totalBits = llBits+mlBits+ofBits; /* sequence */ { size_t offset;- if (!ofBits)- offset = 0;- else {+ if (ofBits > 1) { ZSTD_STATIC_ASSERT(ZSTD_lo_isLongOffset == 1); ZSTD_STATIC_ASSERT(LONG_OFFSETS_MAX_EXTRA_BITS_32 == 5); assert(ofBits <= MaxOff);@@ -841,58 +865,138 @@ offset = ofBase + BIT_readBitsFast(&seqState->DStream, ofBits/*>0*/); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); }- }-- if (ofBits <= 1) {- offset += (llBase==0);- if (offset) {- size_t temp = (offset==3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset];- temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */- if (offset != 1) seqState->prevOffset[2] = seqState->prevOffset[1];- seqState->prevOffset[1] = seqState->prevOffset[0];- seqState->prevOffset[0] = offset = temp;- } else { /* offset == 0 */- offset = seqState->prevOffset[0];- }- } else { seqState->prevOffset[2] = seqState->prevOffset[1]; seqState->prevOffset[1] = seqState->prevOffset[0]; seqState->prevOffset[0] = offset;- }+ } else {+ U32 const ll0 = (llBase == 0);+ if (LIKELY((ofBits == 0))) {+ if (LIKELY(!ll0))+ offset = seqState->prevOffset[0];+ else {+ offset = seqState->prevOffset[1];+ seqState->prevOffset[1] = seqState->prevOffset[0];+ seqState->prevOffset[0] = offset;+ }+ } else {+ offset = ofBase + ll0 + BIT_readBitsFast(&seqState->DStream, 1);+ { size_t temp = (offset==3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset];+ temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */+ if (offset != 1) seqState->prevOffset[2] = seqState->prevOffset[1];+ seqState->prevOffset[1] = seqState->prevOffset[0];+ seqState->prevOffset[0] = offset = temp;+ } } } seq.offset = offset; } - seq.matchLength = mlBase- + ((mlBits>0) ? BIT_readBitsFast(&seqState->DStream, mlBits/*>0*/) : 0); /* <= 16 bits */+ seq.matchLength = mlBase;+ if (mlBits > 0)+ seq.matchLength += BIT_readBitsFast(&seqState->DStream, mlBits/*>0*/);+ if (MEM_32bits() && (mlBits+llBits >= STREAM_ACCUMULATOR_MIN_32-LONG_OFFSETS_MAX_EXTRA_BITS_32)) BIT_reloadDStream(&seqState->DStream);- if (MEM_64bits() && (totalBits >= STREAM_ACCUMULATOR_MIN_64-(LLFSELog+MLFSELog+OffFSELog)))+ if (MEM_64bits() && UNLIKELY(totalBits >= STREAM_ACCUMULATOR_MIN_64-(LLFSELog+MLFSELog+OffFSELog))) BIT_reloadDStream(&seqState->DStream); /* 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); - seq.litLength = llBase- + ((llBits>0) ? BIT_readBitsFast(&seqState->DStream, llBits/*>0*/) : 0); /* <= 16 bits */+ seq.litLength = llBase;+ if (llBits > 0)+ seq.litLength += BIT_readBitsFast(&seqState->DStream, llBits/*>0*/);+ if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); DEBUGLOG(6, "seq: litL=%u, matchL=%u, offset=%u", (U32)seq.litLength, (U32)seq.matchLength, (U32)seq.offset); - /* ANS state update */- ZSTD_updateFseState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */- ZSTD_updateFseState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */- if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */- ZSTD_updateFseState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */+ if (prefetch == ZSTD_p_prefetch) {+ size_t const pos = seqState->pos + seq.litLength;+ const BYTE* const matchBase = (seq.offset > pos) ? seqState->dictEnd : seqState->prefixStart;+ seq.match = matchBase + pos - seq.offset; /* note : this operation can overflow when seq.offset is really too large, which can only happen when input is corrupted.+ * No consequence though : no memory access will occur, offset is only used for prefetching */+ seqState->pos = pos + seq.matchLength;+ } + /* ANS state update+ * gcc-9.0.0 does 2.5% worse with ZSTD_updateFseStateWithDInfo().+ * clang-9.2.0 does 7% worse with ZSTD_updateFseState().+ * Naturally it seems like ZSTD_updateFseStateWithDInfo() should be the+ * better option, so it is the default for other compilers. But, if you+ * measure that it is worse, please put up a pull request.+ */+ {+#if defined(__GNUC__) && !defined(__clang__)+ const int kUseUpdateFseState = 1;+#else+ const int kUseUpdateFseState = 0;+#endif+ if (kUseUpdateFseState) {+ ZSTD_updateFseState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */+ ZSTD_updateFseState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */+ if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */+ ZSTD_updateFseState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */+ } else {+ ZSTD_updateFseStateWithDInfo(&seqState->stateLL, &seqState->DStream, llDInfo); /* <= 9 bits */+ ZSTD_updateFseStateWithDInfo(&seqState->stateML, &seqState->DStream, mlDInfo); /* <= 9 bits */+ if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */+ ZSTD_updateFseStateWithDInfo(&seqState->stateOffb, &seqState->DStream, ofDInfo); /* <= 8 bits */+ }+ }+ return seq; } +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION+static int ZSTD_dictionaryIsActive(ZSTD_DCtx const* dctx, BYTE const* prefixStart, BYTE const* oLitEnd)+{+ size_t const windowSize = dctx->fParams.windowSize;+ /* No dictionary used. */+ if (dctx->dictContentEndForFuzzing == NULL) return 0;+ /* Dictionary is our prefix. */+ if (prefixStart == dctx->dictContentBeginForFuzzing) return 1;+ /* Dictionary is not our ext-dict. */+ if (dctx->dictEnd != dctx->dictContentEndForFuzzing) return 0;+ /* Dictionary is not within our window size. */+ if ((size_t)(oLitEnd - prefixStart) >= windowSize) return 0;+ /* Dictionary is active. */+ return 1;+}++MEM_STATIC void ZSTD_assertValidSequence(+ ZSTD_DCtx const* dctx,+ BYTE const* op, BYTE const* oend,+ seq_t const seq,+ BYTE const* prefixStart, BYTE const* virtualStart)+{+ size_t const windowSize = dctx->fParams.windowSize;+ size_t const sequenceSize = seq.litLength + seq.matchLength;+ BYTE const* const oLitEnd = op + seq.litLength;+ DEBUGLOG(6, "Checking sequence: litL=%u matchL=%u offset=%u",+ (U32)seq.litLength, (U32)seq.matchLength, (U32)seq.offset);+ assert(op <= oend);+ assert((size_t)(oend - op) >= sequenceSize);+ assert(sequenceSize <= ZSTD_BLOCKSIZE_MAX);+ if (ZSTD_dictionaryIsActive(dctx, prefixStart, oLitEnd)) {+ size_t const dictSize = (size_t)((char const*)dctx->dictContentEndForFuzzing - (char const*)dctx->dictContentBeginForFuzzing);+ /* Offset must be within the dictionary. */+ assert(seq.offset <= (size_t)(oLitEnd - virtualStart));+ assert(seq.offset <= windowSize + dictSize);+ } else {+ /* Offset must be within our window. */+ assert(seq.offset <= windowSize);+ }+}+#endif++#ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG FORCE_INLINE_TEMPLATE size_t+DONT_VECTORIZE ZSTD_decompressSequences_body( ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, const void* seqStart, size_t seqSize, int nbSeq,- const ZSTD_longOffset_e isLongOffset)+ const ZSTD_longOffset_e isLongOffset,+ const int frame) { const BYTE* ip = (const BYTE*)seqStart; const BYTE* const iend = ip + seqSize;@@ -905,40 +1009,104 @@ const BYTE* const vBase = (const BYTE*) (dctx->virtualStart); const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd); DEBUGLOG(5, "ZSTD_decompressSequences_body");+ (void)frame; /* Regen sequences */ if (nbSeq) { seqState_t seqState;+ size_t error = 0; dctx->fseEntropy = 1; { U32 i; for (i=0; i<ZSTD_REP_NUM; i++) seqState.prevOffset[i] = dctx->entropy.rep[i]; } RETURN_ERROR_IF( ERR_isError(BIT_initDStream(&seqState.DStream, ip, iend-ip)),- corruption_detected);+ corruption_detected, ""); ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr);+ assert(dst != NULL); - for ( ; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && nbSeq ; ) {- nbSeq--;- { seq_t const sequence = ZSTD_decodeSequence(&seqState, isLongOffset);- size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequence, &litPtr, litEnd, prefixStart, vBase, dictEnd);- DEBUGLOG(6, "regenerated sequence size : %u", (U32)oneSeqSize);- if (ZSTD_isError(oneSeqSize)) return oneSeqSize;- op += oneSeqSize;- } }+ ZSTD_STATIC_ASSERT(+ BIT_DStream_unfinished < BIT_DStream_completed &&+ BIT_DStream_endOfBuffer < BIT_DStream_completed &&+ BIT_DStream_completed < BIT_DStream_overflow); +#if defined(__GNUC__) && defined(__x86_64__)+ /* Align the decompression loop to 32 + 16 bytes.+ *+ * zstd compiled with gcc-9 on an Intel i9-9900k shows 10% decompression+ * speed swings based on the alignment of the decompression loop. This+ * performance swing is caused by parts of the decompression loop falling+ * out of the DSB. The entire decompression loop should fit in the DSB,+ * when it can't we get much worse performance. You can measure if you've+ * hit the good case or the bad case with this perf command for some+ * compressed file test.zst:+ *+ * perf stat -e cycles -e instructions -e idq.all_dsb_cycles_any_uops \+ * -e idq.all_mite_cycles_any_uops -- ./zstd -tq test.zst+ *+ * If you see most cycles served out of the MITE you've hit the bad case.+ * If you see most cycles served out of the DSB you've hit the good case.+ * If it is pretty even then you may be in an okay case.+ *+ * I've been able to reproduce this issue on the following CPUs:+ * - Kabylake: Macbook Pro (15-inch, 2019) 2.4 GHz Intel Core i9+ * Use Instruments->Counters to get DSB/MITE cycles.+ * I never got performance swings, but I was able to+ * go from the good case of mostly DSB to half of the+ * cycles served from MITE.+ * - Coffeelake: Intel i9-9900k+ *+ * I haven't been able to reproduce the instability or DSB misses on any+ * of the following CPUS:+ * - Haswell+ * - Broadwell: Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GH+ * - Skylake+ *+ * If you are seeing performance stability this script can help test.+ * It tests on 4 commits in zstd where I saw performance change.+ *+ * https://gist.github.com/terrelln/9889fc06a423fd5ca6e99351564473f4+ */+ __asm__(".p2align 5");+ __asm__("nop");+ __asm__(".p2align 4");+#endif+ for ( ; ; ) {+ seq_t const sequence = ZSTD_decodeSequence(&seqState, isLongOffset, ZSTD_p_noPrefetch);+ size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequence, &litPtr, litEnd, prefixStart, vBase, dictEnd);+#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && defined(FUZZING_ASSERT_VALID_SEQUENCE)+ assert(!ZSTD_isError(oneSeqSize));+ if (frame) ZSTD_assertValidSequence(dctx, op, oend, sequence, prefixStart, vBase);+#endif+ DEBUGLOG(6, "regenerated sequence size : %u", (U32)oneSeqSize);+ BIT_reloadDStream(&(seqState.DStream));+ /* gcc and clang both don't like early returns in this loop.+ * gcc doesn't like early breaks either.+ * Instead save an error and report it at the end.+ * When there is an error, don't increment op, so we don't+ * overwrite.+ */+ if (UNLIKELY(ZSTD_isError(oneSeqSize))) error = oneSeqSize;+ else op += oneSeqSize;+ if (UNLIKELY(!--nbSeq)) break;+ }+ /* check if reached exact end */ DEBUGLOG(5, "ZSTD_decompressSequences_body: after decode loop, remaining nbSeq : %i", nbSeq);- RETURN_ERROR_IF(nbSeq, corruption_detected);+ if (ZSTD_isError(error)) return error;+ RETURN_ERROR_IF(nbSeq, corruption_detected, "");+ RETURN_ERROR_IF(BIT_reloadDStream(&seqState.DStream) < BIT_DStream_completed, corruption_detected, ""); /* save reps for next block */ { U32 i; for (i=0; i<ZSTD_REP_NUM; i++) dctx->entropy.rep[i] = (U32)(seqState.prevOffset[i]); } } /* last literal segment */ { size_t const lastLLSize = litEnd - litPtr;- RETURN_ERROR_IF(lastLLSize > (size_t)(oend-op), dstSize_tooSmall);- memcpy(op, litPtr, lastLLSize);- op += lastLLSize;+ RETURN_ERROR_IF(lastLLSize > (size_t)(oend-op), dstSize_tooSmall, "");+ if (op != NULL) {+ memcpy(op, litPtr, lastLLSize);+ op += lastLLSize;+ } } return op-ostart;@@ -948,99 +1116,21 @@ ZSTD_decompressSequences_default(ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, const void* seqStart, size_t seqSize, int nbSeq,- const ZSTD_longOffset_e isLongOffset)+ const ZSTD_longOffset_e isLongOffset,+ const int frame) {- return ZSTD_decompressSequences_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);+ return ZSTD_decompressSequences_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame); } #endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG */ -- #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT-FORCE_INLINE_TEMPLATE seq_t-ZSTD_decodeSequenceLong(seqState_t* seqState, ZSTD_longOffset_e const longOffsets)-{- seq_t seq;- U32 const llBits = seqState->stateLL.table[seqState->stateLL.state].nbAdditionalBits;- U32 const mlBits = seqState->stateML.table[seqState->stateML.state].nbAdditionalBits;- U32 const ofBits = seqState->stateOffb.table[seqState->stateOffb.state].nbAdditionalBits;- U32 const totalBits = llBits+mlBits+ofBits;- U32 const llBase = seqState->stateLL.table[seqState->stateLL.state].baseValue;- U32 const mlBase = seqState->stateML.table[seqState->stateML.state].baseValue;- U32 const ofBase = seqState->stateOffb.table[seqState->stateOffb.state].baseValue;-- /* sequence */- { size_t offset;- if (!ofBits)- offset = 0;- else {- ZSTD_STATIC_ASSERT(ZSTD_lo_isLongOffset == 1);- ZSTD_STATIC_ASSERT(LONG_OFFSETS_MAX_EXTRA_BITS_32 == 5);- assert(ofBits <= MaxOff);- if (MEM_32bits() && longOffsets) {- U32 const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN_32-1);- offset = ofBase + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits);- if (MEM_32bits() || extraBits) BIT_reloadDStream(&seqState->DStream);- if (extraBits) offset += BIT_readBitsFast(&seqState->DStream, extraBits);- } else {- offset = ofBase + BIT_readBitsFast(&seqState->DStream, ofBits); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */- if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream);- }- }-- if (ofBits <= 1) {- offset += (llBase==0);- if (offset) {- size_t temp = (offset==3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset];- temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */- if (offset != 1) seqState->prevOffset[2] = seqState->prevOffset[1];- seqState->prevOffset[1] = seqState->prevOffset[0];- seqState->prevOffset[0] = offset = temp;- } else {- offset = seqState->prevOffset[0];- }- } else {- seqState->prevOffset[2] = seqState->prevOffset[1];- seqState->prevOffset[1] = seqState->prevOffset[0];- seqState->prevOffset[0] = offset;- }- seq.offset = offset;- }-- seq.matchLength = mlBase + ((mlBits>0) ? BIT_readBitsFast(&seqState->DStream, mlBits) : 0); /* <= 16 bits */- if (MEM_32bits() && (mlBits+llBits >= STREAM_ACCUMULATOR_MIN_32-LONG_OFFSETS_MAX_EXTRA_BITS_32))- BIT_reloadDStream(&seqState->DStream);- if (MEM_64bits() && (totalBits >= STREAM_ACCUMULATOR_MIN_64-(LLFSELog+MLFSELog+OffFSELog)))- BIT_reloadDStream(&seqState->DStream);- /* Verify that there is enough bits to read the rest of the data in 64-bit mode. */- ZSTD_STATIC_ASSERT(16+LLFSELog+MLFSELog+OffFSELog < STREAM_ACCUMULATOR_MIN_64);-- seq.litLength = llBase + ((llBits>0) ? BIT_readBitsFast(&seqState->DStream, llBits) : 0); /* <= 16 bits */- if (MEM_32bits())- BIT_reloadDStream(&seqState->DStream);-- { size_t const pos = seqState->pos + seq.litLength;- const BYTE* const matchBase = (seq.offset > pos) ? seqState->dictEnd : seqState->prefixStart;- seq.match = matchBase + pos - seq.offset; /* note : this operation can overflow when seq.offset is really too large, which can only happen when input is corrupted.- * No consequence though : no memory access will occur, overly large offset will be detected in ZSTD_execSequenceLong() */- seqState->pos = pos + seq.matchLength;- }-- /* ANS state update */- ZSTD_updateFseState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */- ZSTD_updateFseState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */- if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */- ZSTD_updateFseState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */-- return seq;-}- FORCE_INLINE_TEMPLATE size_t ZSTD_decompressSequencesLong_body( ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, const void* seqStart, size_t seqSize, int nbSeq,- const ZSTD_longOffset_e isLongOffset)+ const ZSTD_longOffset_e isLongOffset,+ const int frame) { const BYTE* ip = (const BYTE*)seqStart; const BYTE* const iend = ip + seqSize;@@ -1052,6 +1142,7 @@ const BYTE* const prefixStart = (const BYTE*) (dctx->prefixStart); const BYTE* const dictStart = (const BYTE*) (dctx->virtualStart); const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd);+ (void)frame; /* Regen sequences */ if (nbSeq) {@@ -1067,36 +1158,45 @@ seqState.prefixStart = prefixStart; seqState.pos = (size_t)(op-prefixStart); seqState.dictEnd = dictEnd;+ assert(dst != NULL); assert(iend >= ip); RETURN_ERROR_IF( ERR_isError(BIT_initDStream(&seqState.DStream, ip, iend-ip)),- corruption_detected);+ corruption_detected, ""); ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); /* prepare in advance */ for (seqNb=0; (BIT_reloadDStream(&seqState.DStream) <= BIT_DStream_completed) && (seqNb<seqAdvance); seqNb++) {- sequences[seqNb] = ZSTD_decodeSequenceLong(&seqState, isLongOffset);+ sequences[seqNb] = ZSTD_decodeSequence(&seqState, isLongOffset, ZSTD_p_prefetch); PREFETCH_L1(sequences[seqNb].match); PREFETCH_L1(sequences[seqNb].match + sequences[seqNb].matchLength - 1); /* note : it's safe to invoke PREFETCH() on any memory address, including invalid ones */ }- RETURN_ERROR_IF(seqNb<seqAdvance, corruption_detected);+ RETURN_ERROR_IF(seqNb<seqAdvance, corruption_detected, ""); /* decode and decompress */ for ( ; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && (seqNb<nbSeq) ; seqNb++) {- seq_t const sequence = ZSTD_decodeSequenceLong(&seqState, isLongOffset);- size_t const oneSeqSize = ZSTD_execSequenceLong(op, oend, sequences[(seqNb-ADVANCED_SEQS) & STORED_SEQS_MASK], &litPtr, litEnd, prefixStart, dictStart, dictEnd);+ seq_t const sequence = ZSTD_decodeSequence(&seqState, isLongOffset, ZSTD_p_prefetch);+ size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequences[(seqNb-ADVANCED_SEQS) & STORED_SEQS_MASK], &litPtr, litEnd, prefixStart, dictStart, dictEnd);+#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && defined(FUZZING_ASSERT_VALID_SEQUENCE)+ assert(!ZSTD_isError(oneSeqSize));+ if (frame) ZSTD_assertValidSequence(dctx, op, oend, sequences[(seqNb-ADVANCED_SEQS) & STORED_SEQS_MASK], prefixStart, dictStart);+#endif if (ZSTD_isError(oneSeqSize)) return oneSeqSize; PREFETCH_L1(sequence.match); PREFETCH_L1(sequence.match + sequence.matchLength - 1); /* note : it's safe to invoke PREFETCH() on any memory address, including invalid ones */ sequences[seqNb & STORED_SEQS_MASK] = sequence; op += oneSeqSize; }- RETURN_ERROR_IF(seqNb<nbSeq, corruption_detected);+ RETURN_ERROR_IF(seqNb<nbSeq, corruption_detected, ""); /* finish queue */ seqNb -= seqAdvance; for ( ; seqNb<nbSeq ; seqNb++) {- size_t const oneSeqSize = ZSTD_execSequenceLong(op, oend, sequences[seqNb&STORED_SEQS_MASK], &litPtr, litEnd, prefixStart, dictStart, dictEnd);+ size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequences[seqNb&STORED_SEQS_MASK], &litPtr, litEnd, prefixStart, dictStart, dictEnd);+#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && defined(FUZZING_ASSERT_VALID_SEQUENCE)+ assert(!ZSTD_isError(oneSeqSize));+ if (frame) ZSTD_assertValidSequence(dctx, op, oend, sequences[seqNb&STORED_SEQS_MASK], prefixStart, dictStart);+#endif if (ZSTD_isError(oneSeqSize)) return oneSeqSize; op += oneSeqSize; }@@ -1107,9 +1207,11 @@ /* last literal segment */ { size_t const lastLLSize = litEnd - litPtr;- RETURN_ERROR_IF(lastLLSize > (size_t)(oend-op), dstSize_tooSmall);- memcpy(op, litPtr, lastLLSize);- op += lastLLSize;+ RETURN_ERROR_IF(lastLLSize > (size_t)(oend-op), dstSize_tooSmall, "");+ if (op != NULL) {+ memcpy(op, litPtr, lastLLSize);+ op += lastLLSize;+ } } return op-ostart;@@ -1119,9 +1221,10 @@ ZSTD_decompressSequencesLong_default(ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, const void* seqStart, size_t seqSize, int nbSeq,- const ZSTD_longOffset_e isLongOffset)+ const ZSTD_longOffset_e isLongOffset,+ const int frame) {- return ZSTD_decompressSequencesLong_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);+ return ZSTD_decompressSequencesLong_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame); } #endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT */ @@ -1131,12 +1234,14 @@ #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG static TARGET_ATTRIBUTE("bmi2") size_t+DONT_VECTORIZE ZSTD_decompressSequences_bmi2(ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, const void* seqStart, size_t seqSize, int nbSeq,- const ZSTD_longOffset_e isLongOffset)+ const ZSTD_longOffset_e isLongOffset,+ const int frame) {- return ZSTD_decompressSequences_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);+ return ZSTD_decompressSequences_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame); } #endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG */ @@ -1145,9 +1250,10 @@ ZSTD_decompressSequencesLong_bmi2(ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, const void* seqStart, size_t seqSize, int nbSeq,- const ZSTD_longOffset_e isLongOffset)+ const ZSTD_longOffset_e isLongOffset,+ const int frame) {- return ZSTD_decompressSequencesLong_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);+ return ZSTD_decompressSequencesLong_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame); } #endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT */ @@ -1157,21 +1263,23 @@ ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, const void* seqStart, size_t seqSize, int nbSeq,- const ZSTD_longOffset_e isLongOffset);+ const ZSTD_longOffset_e isLongOffset,+ const int frame); #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG static size_t ZSTD_decompressSequences(ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, const void* seqStart, size_t seqSize, int nbSeq,- const ZSTD_longOffset_e isLongOffset)+ const ZSTD_longOffset_e isLongOffset,+ const int frame) { DEBUGLOG(5, "ZSTD_decompressSequences"); #if DYNAMIC_BMI2 if (dctx->bmi2) {- return ZSTD_decompressSequences_bmi2(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);+ return ZSTD_decompressSequences_bmi2(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame); } #endif- return ZSTD_decompressSequences_default(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);+ return ZSTD_decompressSequences_default(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame); } #endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG */ @@ -1186,15 +1294,16 @@ ZSTD_decompressSequencesLong(ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, const void* seqStart, size_t seqSize, int nbSeq,- const ZSTD_longOffset_e isLongOffset)+ const ZSTD_longOffset_e isLongOffset,+ const int frame) { DEBUGLOG(5, "ZSTD_decompressSequencesLong"); #if DYNAMIC_BMI2 if (dctx->bmi2) {- return ZSTD_decompressSequencesLong_bmi2(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);+ return ZSTD_decompressSequencesLong_bmi2(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame); } #endif- return ZSTD_decompressSequencesLong_default(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);+ return ZSTD_decompressSequencesLong_default(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame); } #endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT */ @@ -1228,7 +1337,6 @@ } #endif - size_t ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity,@@ -1244,7 +1352,7 @@ 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);+ RETURN_ERROR_IF(srcSize >= ZSTD_BLOCKSIZE_MAX, srcSize_wrong, ""); /* Decode literals section */ { size_t const litCSize = ZSTD_decodeLiteralsBlock(dctx, src, srcSize);@@ -1270,6 +1378,8 @@ ip += seqHSize; srcSize -= seqHSize; + RETURN_ERROR_IF(dst == NULL && nbSeq > 0, dstSize_tooSmall, "NULL not handled");+ #if !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT) && \ !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG) if ( !usePrefetchDecoder@@ -1288,13 +1398,24 @@ if (usePrefetchDecoder) #endif #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT- return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset);+ return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset, frame); #endif #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG /* else */- return ZSTD_decompressSequences(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset);+ return ZSTD_decompressSequences(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset, frame); #endif+ }+}+++void ZSTD_checkContinuity(ZSTD_DCtx* dctx, const void* dst)+{+ if (dst != dctx->previousDstEnd) { /* not contiguous */+ dctx->dictEnd = dctx->previousDstEnd;+ dctx->virtualStart = (const char*)dst - ((const char*)(dctx->previousDstEnd) - (const char*)(dctx->prefixStart));+ dctx->prefixStart = dst;+ dctx->previousDstEnd = dst; } }
zstd/lib/decompress/zstd_decompress_block.h view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -16,8 +16,8 @@ * Dependencies *********************************************************/ #include <stddef.h> /* size_t */-#include "zstd.h" /* DCtx, and some public functions */-#include "zstd_internal.h" /* blockProperties_t, and some public functions */+#include "../zstd.h" /* DCtx, and some public functions */+#include "../common/zstd_internal.h" /* blockProperties_t, and some public functions */ #include "zstd_decompress_internal.h" /* ZSTD_seqSymbol */
zstd/lib/decompress/zstd_decompress_internal.h view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -19,8 +19,8 @@ /*-******************************************************* * Dependencies *********************************************************/-#include "mem.h" /* BYTE, U16, U32 */-#include "zstd_internal.h" /* ZSTD_seqSymbol */+#include "../common/mem.h" /* BYTE, U16, U32 */+#include "../common/zstd_internal.h" /* ZSTD_seqSymbol */ @@ -95,6 +95,11 @@ ZSTD_use_once = 1 /* Use the dictionary once and set to ZSTD_dont_use */ } ZSTD_dictUses_e; +typedef enum {+ ZSTD_obm_buffered = 0, /* Buffer the output */+ ZSTD_obm_stable = 1 /* ZSTD_outBuffer is stable */+} ZSTD_outBufferMode_e;+ struct ZSTD_DCtx_s { const ZSTD_seqSymbol* LLTptr;@@ -147,10 +152,19 @@ U32 legacyVersion; U32 hostageByte; int noForwardProgress;+ ZSTD_outBufferMode_e outBufferMode;+ ZSTD_outBuffer expectedOutBuffer; /* workspace */ BYTE litBuffer[ZSTD_BLOCKSIZE_MAX + WILDCOPY_OVERLENGTH]; BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX];++ size_t oversizedDuration;++#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION+ void const* dictContentBeginForFuzzing;+ void const* dictContentEndForFuzzing;+#endif }; /* typedef'd to ZSTD_DCtx within "zstd.h" */ @@ -160,7 +174,7 @@ /*! ZSTD_loadDEntropy() : * dict : must point at beginning of a valid zstd dictionary.- * @return : size of entropy tables read */+ * @return : size of dictionary header (size of magic number + dict ID + entropy tables) */ size_t ZSTD_loadDEntropy(ZSTD_entropyDTables_t* entropy, const void* const dict, size_t const dictSize);
zstd/lib/dictBuilder/cover.c view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -26,11 +26,11 @@ #include <string.h> /* memset */ #include <time.h> /* clock */ -#include "mem.h" /* read */-#include "pool.h"-#include "threading.h"+#include "../common/mem.h" /* read */+#include "../common/pool.h"+#include "../common/threading.h" #include "cover.h"-#include "zstd_internal.h" /* includes zstd.h */+#include "../common/zstd_internal.h" /* includes zstd.h */ #ifndef ZDICT_STATIC_LINKING_ONLY #define ZDICT_STATIC_LINKING_ONLY #endif@@ -526,10 +526,10 @@ * Prepare a context for dictionary building. * The context is only dependent on the parameter `d` and can used multiple * times.- * Returns 1 on success or zero on error.+ * Returns 0 on success or error code on error. * The context must be destroyed with `COVER_ctx_destroy()`. */-static int COVER_ctx_init(COVER_ctx_t *ctx, const void *samplesBuffer,+static size_t COVER_ctx_init(COVER_ctx_t *ctx, const void *samplesBuffer, const size_t *samplesSizes, unsigned nbSamples, unsigned d, double splitPoint) { const BYTE *const samples = (const BYTE *)samplesBuffer;@@ -544,17 +544,17 @@ totalSamplesSize >= (size_t)COVER_MAX_SAMPLES_SIZE) { DISPLAYLEVEL(1, "Total samples size is too large (%u MB), maximum size is %u MB\n", (unsigned)(totalSamplesSize>>20), (COVER_MAX_SAMPLES_SIZE >> 20));- return 0;+ return ERROR(srcSize_wrong); } /* Check if there are at least 5 training samples */ if (nbTrainSamples < 5) { DISPLAYLEVEL(1, "Total number of training samples is %u and is invalid.", nbTrainSamples);- return 0;+ return ERROR(srcSize_wrong); } /* Check if there's testing sample */ if (nbTestSamples < 1) { DISPLAYLEVEL(1, "Total number of testing samples is %u and is invalid.", nbTestSamples);- return 0;+ return ERROR(srcSize_wrong); } /* Zero the context */ memset(ctx, 0, sizeof(*ctx));@@ -577,7 +577,7 @@ if (!ctx->suffix || !ctx->dmerAt || !ctx->offsets) { DISPLAYLEVEL(1, "Failed to allocate scratch buffers\n"); COVER_ctx_destroy(ctx);- return 0;+ return ERROR(memory_allocation); } ctx->freqs = NULL; ctx->d = d;@@ -624,7 +624,7 @@ (ctx->d <= 8 ? &COVER_cmp8 : &COVER_cmp), &COVER_group); ctx->freqs = ctx->suffix; ctx->suffix = NULL;- return 1;+ return 0; } void COVER_warnOnSmallCorpus(size_t maxDictSize, size_t nbDmers, int displayLevel)@@ -638,8 +638,8 @@ "compared to the source size %u! " "size(source)/size(dictionary) = %f, but it should be >= " "10! This may lead to a subpar dictionary! We recommend "- "training on sources at least 10x, and up to 100x the "- "size of the dictionary!\n", (U32)maxDictSize,+ "training on sources at least 10x, and preferably 100x "+ "the size of the dictionary! \n", (U32)maxDictSize, (U32)nbDmers, ratio); } @@ -729,11 +729,11 @@ /* Checks */ if (!COVER_checkParameters(parameters, dictBufferCapacity)) { DISPLAYLEVEL(1, "Cover parameters incorrect\n");- return ERROR(GENERIC);+ return ERROR(parameter_outOfBound); } if (nbSamples == 0) { DISPLAYLEVEL(1, "Cover must have at least one input file\n");- return ERROR(GENERIC);+ return ERROR(srcSize_wrong); } if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) { DISPLAYLEVEL(1, "dictBufferCapacity must be at least %u\n",@@ -741,15 +741,18 @@ return ERROR(dstSize_tooSmall); } /* Initialize context and activeDmers */- if (!COVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples,- parameters.d, parameters.splitPoint)) {- return ERROR(GENERIC);+ {+ size_t const initVal = COVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples,+ parameters.d, parameters.splitPoint);+ if (ZSTD_isError(initVal)) {+ return initVal;+ } } COVER_warnOnSmallCorpus(dictBufferCapacity, ctx.suffixSize, g_displayLevel); if (!COVER_map_init(&activeDmers, parameters.k - parameters.d + 1)) { DISPLAYLEVEL(1, "Failed to allocate dmer map: out of memory\n"); COVER_ctx_destroy(&ctx);- return ERROR(GENERIC);+ return ERROR(memory_allocation); } DISPLAYLEVEL(2, "Building dictionary\n");@@ -810,7 +813,7 @@ cctx, dst, dstCapacity, samples + offsets[i], samplesSizes[i], cdict); if (ZSTD_isError(size)) {- totalCompressedSize = ERROR(GENERIC);+ totalCompressedSize = size; goto _compressCleanup; } totalCompressedSize += size;@@ -886,9 +889,11 @@ * Decrements liveJobs and signals any waiting threads if liveJobs == 0. * If this dictionary is the best so far save it and its parameters. */-void COVER_best_finish(COVER_best_t *best, size_t compressedSize,- ZDICT_cover_params_t parameters, void *dict,- size_t dictSize) {+void COVER_best_finish(COVER_best_t *best, ZDICT_cover_params_t parameters,+ COVER_dictSelection_t selection) {+ void* dict = selection.dictContent;+ size_t compressedSize = selection.totalCompressedSize;+ size_t dictSize = selection.dictSize; if (!best) { return; }@@ -914,10 +919,12 @@ } } /* Save the dictionary, parameters, and size */- memcpy(best->dict, dict, dictSize);- best->dictSize = dictSize;- best->parameters = parameters;- best->compressedSize = compressedSize;+ if (dict) {+ memcpy(best->dict, dict, dictSize);+ best->dictSize = dictSize;+ best->parameters = parameters;+ best->compressedSize = compressedSize;+ } } if (liveJobs == 0) { ZSTD_pthread_cond_broadcast(&best->cond);@@ -926,6 +933,111 @@ } } +COVER_dictSelection_t COVER_dictSelectionError(size_t error) {+ COVER_dictSelection_t selection = { NULL, 0, error };+ return selection;+}++unsigned COVER_dictSelectionIsError(COVER_dictSelection_t selection) {+ return (ZSTD_isError(selection.totalCompressedSize) || !selection.dictContent);+}++void COVER_dictSelectionFree(COVER_dictSelection_t selection){+ free(selection.dictContent);+}++COVER_dictSelection_t COVER_selectDict(BYTE* customDictContent,+ size_t dictContentSize, const BYTE* samplesBuffer, const size_t* samplesSizes, unsigned nbFinalizeSamples,+ size_t nbCheckSamples, size_t nbSamples, ZDICT_cover_params_t params, size_t* offsets, size_t totalCompressedSize) {++ size_t largestDict = 0;+ size_t largestCompressed = 0;+ BYTE* customDictContentEnd = customDictContent + dictContentSize;++ BYTE * largestDictbuffer = (BYTE *)malloc(dictContentSize);+ BYTE * candidateDictBuffer = (BYTE *)malloc(dictContentSize);+ double regressionTolerance = ((double)params.shrinkDictMaxRegression / 100.0) + 1.00;++ if (!largestDictbuffer || !candidateDictBuffer) {+ free(largestDictbuffer);+ free(candidateDictBuffer);+ return COVER_dictSelectionError(dictContentSize);+ }++ /* Initial dictionary size and compressed size */+ memcpy(largestDictbuffer, customDictContent, dictContentSize);+ dictContentSize = ZDICT_finalizeDictionary(+ largestDictbuffer, dictContentSize, customDictContent, dictContentSize,+ samplesBuffer, samplesSizes, nbFinalizeSamples, params.zParams);++ if (ZDICT_isError(dictContentSize)) {+ free(largestDictbuffer);+ free(candidateDictBuffer);+ return COVER_dictSelectionError(dictContentSize);+ }++ totalCompressedSize = COVER_checkTotalCompressedSize(params, samplesSizes,+ samplesBuffer, offsets,+ nbCheckSamples, nbSamples,+ largestDictbuffer, dictContentSize);++ if (ZSTD_isError(totalCompressedSize)) {+ free(largestDictbuffer);+ free(candidateDictBuffer);+ return COVER_dictSelectionError(totalCompressedSize);+ }++ if (params.shrinkDict == 0) {+ COVER_dictSelection_t selection = { largestDictbuffer, dictContentSize, totalCompressedSize };+ free(candidateDictBuffer);+ return selection;+ }++ largestDict = dictContentSize;+ largestCompressed = totalCompressedSize;+ dictContentSize = ZDICT_DICTSIZE_MIN;++ /* Largest dict is initially at least ZDICT_DICTSIZE_MIN */+ while (dictContentSize < largestDict) {+ memcpy(candidateDictBuffer, largestDictbuffer, largestDict);+ dictContentSize = ZDICT_finalizeDictionary(+ candidateDictBuffer, dictContentSize, customDictContentEnd - dictContentSize, dictContentSize,+ samplesBuffer, samplesSizes, nbFinalizeSamples, params.zParams);++ if (ZDICT_isError(dictContentSize)) {+ free(largestDictbuffer);+ free(candidateDictBuffer);+ return COVER_dictSelectionError(dictContentSize);++ }++ totalCompressedSize = COVER_checkTotalCompressedSize(params, samplesSizes,+ samplesBuffer, offsets,+ nbCheckSamples, nbSamples,+ candidateDictBuffer, dictContentSize);++ if (ZSTD_isError(totalCompressedSize)) {+ free(largestDictbuffer);+ free(candidateDictBuffer);+ return COVER_dictSelectionError(totalCompressedSize);+ }++ if (totalCompressedSize <= largestCompressed * regressionTolerance) {+ COVER_dictSelection_t selection = { candidateDictBuffer, dictContentSize, totalCompressedSize };+ free(largestDictbuffer);+ return selection;+ }+ dictContentSize *= 2;+ }+ dictContentSize = largestDict;+ totalCompressedSize = largestCompressed;+ {+ COVER_dictSelection_t selection = { largestDictbuffer, dictContentSize, totalCompressedSize };+ free(candidateDictBuffer);+ return selection;+ }+}+ /** * Parameters for COVER_tryParameters(). */@@ -951,6 +1063,7 @@ /* Allocate space for hash table, dict, and freqs */ COVER_map_t activeDmers; BYTE *const dict = (BYTE * const)malloc(dictBufferCapacity);+ COVER_dictSelection_t selection = COVER_dictSelectionError(ERROR(GENERIC)); U32 *freqs = (U32 *)malloc(ctx->suffixSize * sizeof(U32)); if (!COVER_map_init(&activeDmers, parameters.k - parameters.d + 1)) { DISPLAYLEVEL(1, "Failed to allocate dmer map: out of memory\n");@@ -966,29 +1079,21 @@ { const size_t tail = COVER_buildDictionary(ctx, freqs, &activeDmers, dict, dictBufferCapacity, parameters);- dictBufferCapacity = ZDICT_finalizeDictionary(- dict, dictBufferCapacity, dict + tail, dictBufferCapacity - tail,- ctx->samples, ctx->samplesSizes, (unsigned)ctx->nbTrainSamples,- parameters.zParams);- if (ZDICT_isError(dictBufferCapacity)) {- DISPLAYLEVEL(1, "Failed to finalize dictionary\n");+ selection = COVER_selectDict(dict + tail, dictBufferCapacity - tail,+ ctx->samples, ctx->samplesSizes, (unsigned)ctx->nbTrainSamples, ctx->nbTrainSamples, ctx->nbSamples, parameters, ctx->offsets,+ totalCompressedSize);++ if (COVER_dictSelectionIsError(selection)) {+ DISPLAYLEVEL(1, "Failed to select dictionary\n"); goto _cleanup; } }- /* Check total compressed size */- totalCompressedSize = COVER_checkTotalCompressedSize(parameters, ctx->samplesSizes,- ctx->samples, ctx->offsets,- ctx->nbTrainSamples, ctx->nbSamples,- dict, dictBufferCapacity);- _cleanup:- COVER_best_finish(data->best, totalCompressedSize, parameters, dict,- dictBufferCapacity);+ free(dict);+ COVER_best_finish(data->best, parameters, selection); free(data); COVER_map_destroy(&activeDmers);- if (dict) {- free(dict);- }+ COVER_dictSelectionFree(selection); if (freqs) { free(freqs); }@@ -1010,6 +1115,7 @@ const unsigned kStepSize = MAX((kMaxK - kMinK) / kSteps, 1); const unsigned kIterations = (1 + (kMaxD - kMinD) / 2) * (1 + (kMaxK - kMinK) / kStepSize);+ const unsigned shrinkDict = 0; /* Local variables */ const int displayLevel = parameters->zParams.notificationLevel; unsigned iteration = 1;@@ -1022,15 +1128,15 @@ /* Checks */ if (splitPoint <= 0 || splitPoint > 1) { LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect parameters\n");- return ERROR(GENERIC);+ return ERROR(parameter_outOfBound); } if (kMinK < kMaxD || kMaxK < kMinK) { LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect parameters\n");- return ERROR(GENERIC);+ return ERROR(parameter_outOfBound); } if (nbSamples == 0) { DISPLAYLEVEL(1, "Cover must have at least one input file\n");- return ERROR(GENERIC);+ return ERROR(srcSize_wrong); } if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) { DISPLAYLEVEL(1, "dictBufferCapacity must be at least %u\n",@@ -1054,11 +1160,14 @@ /* Initialize the context for this value of d */ COVER_ctx_t ctx; LOCALDISPLAYLEVEL(displayLevel, 3, "d=%u\n", d);- if (!COVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, d, splitPoint)) {- LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to initialize context\n");- COVER_best_destroy(&best);- POOL_free(pool);- return ERROR(GENERIC);+ {+ const size_t initVal = COVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, d, splitPoint);+ if (ZSTD_isError(initVal)) {+ LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to initialize context\n");+ COVER_best_destroy(&best);+ POOL_free(pool);+ return initVal;+ } } if (!warned) { COVER_warnOnSmallCorpus(dictBufferCapacity, ctx.suffixSize, displayLevel);@@ -1075,7 +1184,7 @@ COVER_best_destroy(&best); COVER_ctx_destroy(&ctx); POOL_free(pool);- return ERROR(GENERIC);+ return ERROR(memory_allocation); } data->ctx = &ctx; data->best = &best;@@ -1085,6 +1194,7 @@ data->parameters.d = d; data->parameters.splitPoint = splitPoint; data->parameters.steps = kSteps;+ data->parameters.shrinkDict = shrinkDict; data->parameters.zParams.notificationLevel = g_displayLevel; /* Check the parameters */ if (!COVER_checkParameters(data->parameters, dictBufferCapacity)) {
zstd/lib/dictBuilder/cover.h view
@@ -1,11 +1,21 @@+/*+ * Copyright (c) 2017-2020, 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.+ */+ #include <stdio.h> /* fprintf */ #include <stdlib.h> /* malloc, free, qsort */ #include <string.h> /* memset */ #include <time.h> /* clock */-#include "mem.h" /* read */-#include "pool.h"-#include "threading.h"-#include "zstd_internal.h" /* includes zstd.h */+#include "../common/mem.h" /* read */+#include "../common/pool.h"+#include "../common/threading.h"+#include "../common/zstd_internal.h" /* includes zstd.h */ #ifndef ZDICT_STATIC_LINKING_ONLY #define ZDICT_STATIC_LINKING_ONLY #endif@@ -47,6 +57,15 @@ } COVER_epoch_info_t; /**+ * Struct used for the dictionary selection function.+ */+typedef struct COVER_dictSelection {+ BYTE* dictContent;+ size_t dictSize;+ size_t totalCompressedSize;+} COVER_dictSelection_t;++/** * Computes the number of epochs and the size of each epoch. * We will make sure that each epoch gets at least 10 * k bytes. *@@ -107,6 +126,32 @@ * Decrements liveJobs and signals any waiting threads if liveJobs == 0. * If this dictionary is the best so far save it and its parameters. */-void COVER_best_finish(COVER_best_t *best, size_t compressedSize,- ZDICT_cover_params_t parameters, void *dict,- size_t dictSize);+void COVER_best_finish(COVER_best_t *best, ZDICT_cover_params_t parameters,+ COVER_dictSelection_t selection);+/**+ * Error function for COVER_selectDict function. Checks if the return+ * value is an error.+ */+unsigned COVER_dictSelectionIsError(COVER_dictSelection_t selection);++ /**+ * Error function for COVER_selectDict function. Returns a struct where+ * return.totalCompressedSize is a ZSTD error.+ */+COVER_dictSelection_t COVER_dictSelectionError(size_t error);++/**+ * Always call after selectDict is called to free up used memory from+ * newly created dictionary.+ */+void COVER_dictSelectionFree(COVER_dictSelection_t selection);++/**+ * Called to finalize the dictionary and select one based on whether or not+ * the shrink-dict flag was enabled. If enabled the dictionary used is the+ * smallest dictionary within a specified regression of the compressed size+ * from the largest dictionary.+ */+ COVER_dictSelection_t COVER_selectDict(BYTE* customDictContent,+ size_t dictContentSize, const BYTE* samplesBuffer, const size_t* samplesSizes, unsigned nbFinalizeSamples,+ size_t nbCheckSamples, size_t nbSamples, ZDICT_cover_params_t params, size_t* offsets, size_t totalCompressedSize);
zstd/lib/dictBuilder/fastcover.c view
@@ -1,3 +1,13 @@+/*+ * Copyright (c) 2018-2020, 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 ***************************************/@@ -6,11 +16,11 @@ #include <string.h> /* memset */ #include <time.h> /* clock */ -#include "mem.h" /* read */-#include "pool.h"-#include "threading.h"+#include "../common/mem.h" /* read */+#include "../common/pool.h"+#include "../common/threading.h" #include "cover.h"-#include "zstd_internal.h" /* includes zstd.h */+#include "../common/zstd_internal.h" /* includes zstd.h */ #ifndef ZDICT_STATIC_LINKING_ONLY #define ZDICT_STATIC_LINKING_ONLY #endif@@ -287,10 +297,10 @@ * Prepare a context for dictionary building. * The context is only dependent on the parameter `d` and can used multiple * times.- * Returns 1 on success or zero on error.+ * Returns 0 on success or error code on error. * The context must be destroyed with `FASTCOVER_ctx_destroy()`. */-static int+static size_t FASTCOVER_ctx_init(FASTCOVER_ctx_t* ctx, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples,@@ -310,19 +320,19 @@ totalSamplesSize >= (size_t)FASTCOVER_MAX_SAMPLES_SIZE) { DISPLAYLEVEL(1, "Total samples size is too large (%u MB), maximum size is %u MB\n", (unsigned)(totalSamplesSize >> 20), (FASTCOVER_MAX_SAMPLES_SIZE >> 20));- return 0;+ return ERROR(srcSize_wrong); } /* Check if there are at least 5 training samples */ if (nbTrainSamples < 5) { DISPLAYLEVEL(1, "Total number of training samples is %u and is invalid\n", nbTrainSamples);- return 0;+ return ERROR(srcSize_wrong); } /* Check if there's testing sample */ if (nbTestSamples < 1) { DISPLAYLEVEL(1, "Total number of testing samples is %u and is invalid.\n", nbTestSamples);- return 0;+ return ERROR(srcSize_wrong); } /* Zero the context */@@ -347,7 +357,7 @@ if (ctx->offsets == NULL) { DISPLAYLEVEL(1, "Failed to allocate scratch buffers \n"); FASTCOVER_ctx_destroy(ctx);- return 0;+ return ERROR(memory_allocation); } /* Fill offsets from the samplesSizes */@@ -364,13 +374,13 @@ if (ctx->freqs == NULL) { DISPLAYLEVEL(1, "Failed to allocate frequency table \n"); FASTCOVER_ctx_destroy(ctx);- return 0;+ return ERROR(memory_allocation); } DISPLAYLEVEL(2, "Computing frequencies\n"); FASTCOVER_computeFrequency(ctx->freqs, ctx); - return 1;+ return 0; } @@ -435,7 +445,6 @@ return tail; } - /** * Parameters for FASTCOVER_tryParameters(). */@@ -464,6 +473,7 @@ U16* segmentFreqs = (U16 *)calloc(((U64)1 << ctx->f), sizeof(U16)); /* Allocate space for hash table, dict, and freqs */ BYTE *const dict = (BYTE * const)malloc(dictBufferCapacity);+ COVER_dictSelection_t selection = COVER_dictSelectionError(ERROR(GENERIC)); U32 *freqs = (U32*) malloc(((U64)1 << ctx->f) * sizeof(U32)); if (!segmentFreqs || !dict || !freqs) { DISPLAYLEVEL(1, "Failed to allocate buffers: out of memory\n");@@ -473,27 +483,24 @@ memcpy(freqs, ctx->freqs, ((U64)1 << ctx->f) * sizeof(U32)); /* Build the dictionary */ { const size_t tail = FASTCOVER_buildDictionary(ctx, freqs, dict, dictBufferCapacity,- parameters, segmentFreqs);+ parameters, segmentFreqs);+ const unsigned nbFinalizeSamples = (unsigned)(ctx->nbTrainSamples * ctx->accelParams.finalize / 100);- dictBufferCapacity = ZDICT_finalizeDictionary(- dict, dictBufferCapacity, dict + tail, dictBufferCapacity - tail,- ctx->samples, ctx->samplesSizes, nbFinalizeSamples, parameters.zParams);- if (ZDICT_isError(dictBufferCapacity)) {- DISPLAYLEVEL(1, "Failed to finalize dictionary\n");+ selection = COVER_selectDict(dict + tail, dictBufferCapacity - tail,+ ctx->samples, ctx->samplesSizes, nbFinalizeSamples, ctx->nbTrainSamples, ctx->nbSamples, parameters, ctx->offsets,+ totalCompressedSize);++ if (COVER_dictSelectionIsError(selection)) {+ DISPLAYLEVEL(1, "Failed to select dictionary\n"); goto _cleanup; } }- /* Check total compressed size */- totalCompressedSize = COVER_checkTotalCompressedSize(parameters, ctx->samplesSizes,- ctx->samples, ctx->offsets,- ctx->nbTrainSamples, ctx->nbSamples,- dict, dictBufferCapacity); _cleanup:- COVER_best_finish(data->best, totalCompressedSize, parameters, dict,- dictBufferCapacity);+ free(dict);+ COVER_best_finish(data->best, parameters, selection); free(data); free(segmentFreqs);- free(dict);+ COVER_dictSelectionFree(selection); free(freqs); } @@ -508,6 +515,7 @@ coverParams->nbThreads = fastCoverParams.nbThreads; coverParams->splitPoint = fastCoverParams.splitPoint; coverParams->zParams = fastCoverParams.zParams;+ coverParams->shrinkDict = fastCoverParams.shrinkDict; } @@ -524,6 +532,7 @@ fastCoverParams->f = f; fastCoverParams->accel = accel; fastCoverParams->zParams = coverParams.zParams;+ fastCoverParams->shrinkDict = coverParams.shrinkDict; } @@ -550,11 +559,11 @@ if (!FASTCOVER_checkParameters(coverParams, dictBufferCapacity, parameters.f, parameters.accel)) { DISPLAYLEVEL(1, "FASTCOVER parameters incorrect\n");- return ERROR(GENERIC);+ return ERROR(parameter_outOfBound); } if (nbSamples == 0) { DISPLAYLEVEL(1, "FASTCOVER must have at least one input file\n");- return ERROR(GENERIC);+ return ERROR(srcSize_wrong); } if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) { DISPLAYLEVEL(1, "dictBufferCapacity must be at least %u\n",@@ -564,11 +573,14 @@ /* Assign corresponding FASTCOVER_accel_t to accelParams*/ accelParams = FASTCOVER_defaultAccelParameters[parameters.accel]; /* Initialize context */- if (!FASTCOVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples,+ {+ size_t const initVal = FASTCOVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, coverParams.d, parameters.splitPoint, parameters.f,- accelParams)) {- DISPLAYLEVEL(1, "Failed to initialize context\n");- return ERROR(GENERIC);+ accelParams);+ if (ZSTD_isError(initVal)) {+ DISPLAYLEVEL(1, "Failed to initialize context\n");+ return initVal;+ } } COVER_warnOnSmallCorpus(dictBufferCapacity, ctx.nbDmers, g_displayLevel); /* Build the dictionary */@@ -616,6 +628,7 @@ (1 + (kMaxD - kMinD) / 2) * (1 + (kMaxK - kMinK) / kStepSize); const unsigned f = parameters->f == 0 ? DEFAULT_F : parameters->f; const unsigned accel = parameters->accel == 0 ? DEFAULT_ACCEL : parameters->accel;+ const unsigned shrinkDict = 0; /* Local variables */ const int displayLevel = parameters->zParams.notificationLevel; unsigned iteration = 1;@@ -627,19 +640,19 @@ /* Checks */ if (splitPoint <= 0 || splitPoint > 1) { LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect splitPoint\n");- return ERROR(GENERIC);+ return ERROR(parameter_outOfBound); } if (accel == 0 || accel > FASTCOVER_MAX_ACCEL) { LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect accel\n");- return ERROR(GENERIC);+ return ERROR(parameter_outOfBound); } if (kMinK < kMaxD || kMaxK < kMinK) { LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect k\n");- return ERROR(GENERIC);+ return ERROR(parameter_outOfBound); } if (nbSamples == 0) { LOCALDISPLAYLEVEL(displayLevel, 1, "FASTCOVER must have at least one input file\n");- return ERROR(GENERIC);+ return ERROR(srcSize_wrong); } if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) { LOCALDISPLAYLEVEL(displayLevel, 1, "dictBufferCapacity must be at least %u\n",@@ -666,11 +679,14 @@ /* Initialize the context for this value of d */ FASTCOVER_ctx_t ctx; LOCALDISPLAYLEVEL(displayLevel, 3, "d=%u\n", d);- if (!FASTCOVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, d, splitPoint, f, accelParams)) {- LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to initialize context\n");- COVER_best_destroy(&best);- POOL_free(pool);- return ERROR(GENERIC);+ {+ size_t const initVal = FASTCOVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, d, splitPoint, f, accelParams);+ if (ZSTD_isError(initVal)) {+ LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to initialize context\n");+ COVER_best_destroy(&best);+ POOL_free(pool);+ return initVal;+ } } if (!warned) { COVER_warnOnSmallCorpus(dictBufferCapacity, ctx.nbDmers, displayLevel);@@ -687,7 +703,7 @@ COVER_best_destroy(&best); FASTCOVER_ctx_destroy(&ctx); POOL_free(pool);- return ERROR(GENERIC);+ return ERROR(memory_allocation); } data->ctx = &ctx; data->best = &best;@@ -697,6 +713,7 @@ data->parameters.d = d; data->parameters.splitPoint = splitPoint; data->parameters.steps = kSteps;+ data->parameters.shrinkDict = shrinkDict; data->parameters.zParams.notificationLevel = g_displayLevel; /* Check the parameters */ if (!FASTCOVER_checkParameters(data->parameters, dictBufferCapacity,
zstd/lib/dictBuilder/zdict.c view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -37,17 +37,18 @@ #include <stdio.h> /* fprintf, fopen, ftello64 */ #include <time.h> /* clock */ -#include "mem.h" /* read */-#include "fse.h" /* FSE_normalizeCount, FSE_writeNCount */+#include "../common/mem.h" /* read */+#include "../common/fse.h" /* FSE_normalizeCount, FSE_writeNCount */ #define HUF_STATIC_LINKING_ONLY-#include "huf.h" /* HUF_buildCTable, HUF_writeCTable */-#include "zstd_internal.h" /* includes zstd.h */-#include "xxhash.h" /* XXH64 */+#include "../common/huf.h" /* HUF_buildCTable, HUF_writeCTable */+#include "../common/zstd_internal.h" /* includes zstd.h */+#include "../common/xxhash.h" /* XXH64 */ #include "divsufsort.h" #ifndef ZDICT_STATIC_LINKING_ONLY # define ZDICT_STATIC_LINKING_ONLY #endif #include "zdict.h"+#include "../compress/zstd_compress_internal.h" /* ZSTD_loadCEntropy() */ /*-*************************************@@ -99,7 +100,30 @@ return MEM_readLE32((const char*)dictBuffer + 4); } +size_t ZDICT_getDictHeaderSize(const void* dictBuffer, size_t dictSize)+{+ size_t headerSize;+ if (dictSize <= 8 || MEM_readLE32(dictBuffer) != ZSTD_MAGIC_DICTIONARY) return ERROR(dictionary_corrupted); + { unsigned offcodeMaxValue = MaxOff;+ ZSTD_compressedBlockState_t* bs = (ZSTD_compressedBlockState_t*)malloc(sizeof(ZSTD_compressedBlockState_t));+ U32* wksp = (U32*)malloc(HUF_WORKSPACE_SIZE);+ short* offcodeNCount = (short*)malloc((MaxOff+1)*sizeof(short));+ if (!bs || !wksp || !offcodeNCount) {+ headerSize = ERROR(memory_allocation);+ } else {+ ZSTD_reset_compressedBlockState(bs);+ headerSize = ZSTD_loadCEntropy(bs, wksp, offcodeNCount, &offcodeMaxValue, dictBuffer, dictSize);+ }++ free(bs);+ free(wksp);+ free(offcodeNCount);+ }++ return headerSize;+}+ /*-******************************************************** * Dictionary training functions **********************************************************/@@ -571,7 +595,7 @@ unsigned const prime1 = 2654435761U; unsigned const prime2 = 2246822519U; unsigned acc = prime1;- size_t p=0;;+ size_t p=0; for (p=0; p<length; p++) { acc *= prime2; ((unsigned char*)buffer)[p] = (unsigned char)(acc >> 21);@@ -588,12 +612,12 @@ #define MAXREPOFFSET 1024 -static void ZDICT_countEStats(EStats_ress_t esr, ZSTD_parameters params,+static void ZDICT_countEStats(EStats_ress_t esr, const ZSTD_parameters* params, unsigned* countLit, unsigned* offsetcodeCount, unsigned* matchlengthCount, unsigned* litlengthCount, U32* repOffsets, const void* src, size_t srcSize, U32 notificationLevel) {- size_t const blockSizeMax = MIN (ZSTD_BLOCKSIZE_MAX, 1 << params.cParams.windowLog);+ size_t const blockSizeMax = MIN (ZSTD_BLOCKSIZE_MAX, 1 << params->cParams.windowLog); size_t cSize; if (srcSize > blockSizeMax) srcSize = blockSizeMax; /* protection vs large samples */@@ -731,7 +755,7 @@ /* collect stats on all samples */ for (u=0; u<nbFiles; u++) {- ZDICT_countEStats(esr, params,+ ZDICT_countEStats(esr, ¶ms, countLit, offcodeCount, matchLengthCount, litLengthCount, repOffset, (const char*)srcBuffer + pos, fileSizes[u], notificationLevel);@@ -741,7 +765,7 @@ /* analyze, build stats, starting with literals */ { size_t maxNbBits = HUF_buildCTable (hufTable, countLit, 255, huffLog); if (HUF_isError(maxNbBits)) {- eSize = ERROR(GENERIC);+ eSize = maxNbBits; DISPLAYLEVEL(1, " HUF_buildCTable error \n"); goto _cleanup; }@@ -764,7 +788,7 @@ total=0; for (u=0; u<=offcodeMax; u++) total+=offcodeCount[u]; errorCode = FSE_normalizeCount(offcodeNCount, Offlog, offcodeCount, total, offcodeMax); if (FSE_isError(errorCode)) {- eSize = ERROR(GENERIC);+ eSize = errorCode; DISPLAYLEVEL(1, "FSE_normalizeCount error with offcodeCount \n"); goto _cleanup; }@@ -773,7 +797,7 @@ total=0; for (u=0; u<=MaxML; u++) total+=matchLengthCount[u]; errorCode = FSE_normalizeCount(matchLengthNCount, mlLog, matchLengthCount, total, MaxML); if (FSE_isError(errorCode)) {- eSize = ERROR(GENERIC);+ eSize = errorCode; DISPLAYLEVEL(1, "FSE_normalizeCount error with matchLengthCount \n"); goto _cleanup; }@@ -782,7 +806,7 @@ total=0; for (u=0; u<=MaxLL; u++) total+=litLengthCount[u]; errorCode = FSE_normalizeCount(litLengthNCount, llLog, litLengthCount, total, MaxLL); if (FSE_isError(errorCode)) {- eSize = ERROR(GENERIC);+ eSize = errorCode; DISPLAYLEVEL(1, "FSE_normalizeCount error with litLengthCount \n"); goto _cleanup; }@@ -791,7 +815,7 @@ /* write result to buffer */ { size_t const hhSize = HUF_writeCTable(dstPtr, maxDstSize, hufTable, 255, huffLog); if (HUF_isError(hhSize)) {- eSize = ERROR(GENERIC);+ eSize = hhSize; DISPLAYLEVEL(1, "HUF_writeCTable error \n"); goto _cleanup; }@@ -802,7 +826,7 @@ { size_t const ohSize = FSE_writeNCount(dstPtr, maxDstSize, offcodeNCount, OFFCODE_MAX, Offlog); if (FSE_isError(ohSize)) {- eSize = ERROR(GENERIC);+ eSize = ohSize; DISPLAYLEVEL(1, "FSE_writeNCount error with offcodeNCount \n"); goto _cleanup; }@@ -813,7 +837,7 @@ { size_t const mhSize = FSE_writeNCount(dstPtr, maxDstSize, matchLengthNCount, MaxML, mlLog); if (FSE_isError(mhSize)) {- eSize = ERROR(GENERIC);+ eSize = mhSize; DISPLAYLEVEL(1, "FSE_writeNCount error with matchLengthNCount \n"); goto _cleanup; }@@ -824,7 +848,7 @@ { size_t const lhSize = FSE_writeNCount(dstPtr, maxDstSize, litLengthNCount, MaxLL, llLog); if (FSE_isError(lhSize)) {- eSize = ERROR(GENERIC);+ eSize = lhSize; DISPLAYLEVEL(1, "FSE_writeNCount error with litlengthNCount \n"); goto _cleanup; }@@ -834,7 +858,7 @@ } if (maxDstSize<12) {- eSize = ERROR(GENERIC);+ eSize = ERROR(dstSize_tooSmall); DISPLAYLEVEL(1, "not enough space to write RepOffsets \n"); goto _cleanup; }
zstd/lib/dictBuilder/zdict.h view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -61,9 +61,57 @@ const void* samplesBuffer, 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) */+} ZDICT_params_t; +/*! ZDICT_finalizeDictionary():+ * Given a custom content as a basis for dictionary, and a set of samples,+ * finalize dictionary by adding headers and statistics according to the zstd+ * dictionary format.+ *+ * Samples must be stored concatenated in a flat buffer `samplesBuffer`,+ * supplied with an array of sizes `samplesSizes`, providing the size of each+ * sample in order. The samples are used to construct the statistics, so they+ * should be representative of what you will compress with this dictionary.+ *+ * The compression level can be set in `parameters`. You should pass the+ * compression level you expect to use in production. The statistics for each+ * compression level differ, so tuning the dictionary for the compression level+ * can help quite a bit.+ *+ * You can set an explicit dictionary ID in `parameters`, or allow us to pick+ * a random dictionary ID for you, but we can't guarantee no collisions.+ *+ * The dstDictBuffer and the dictContent may overlap, and the content will be+ * appended to the end of the header. If the header + the content doesn't fit in+ * maxDictSize the beginning of the content is truncated to make room, since it+ * is presumed that the most profitable content is at the end of the dictionary,+ * since that is the cheapest to reference.+ *+ * `dictContentSize` must be >= ZDICT_CONTENTSIZE_MIN bytes.+ * `maxDictSize` must be >= max(dictContentSize, ZSTD_DICTSIZE_MIN).+ *+ * @return: size of dictionary stored into `dstDictBuffer` (<= `maxDictSize`),+ * or an error code, which can be tested by ZDICT_isError().+ * Note: ZDICT_finalizeDictionary() will push notifications into stderr if+ * instructed to, using notificationLevel>0.+ * NOTE: This function currently may fail in several edge cases including:+ * * Not enough samples+ * * Samples are uncompressible+ * * Samples are all exactly the same+ */+ZDICTLIB_API size_t ZDICT_finalizeDictionary(void* dstDictBuffer, size_t maxDictSize,+ const void* dictContent, size_t dictContentSize,+ const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples,+ ZDICT_params_t parameters);++ /*====== Helper functions ======*/ ZDICTLIB_API unsigned ZDICT_getDictID(const void* dictBuffer, size_t dictSize); /**< extracts dictID; @return zero if error (not a valid dictionary) */+ZDICTLIB_API size_t ZDICT_getDictHeaderSize(const void* dictBuffer, size_t dictSize); /* returns dict header size; returns a ZSTD error code on failure */ ZDICTLIB_API unsigned ZDICT_isError(size_t errorCode); ZDICTLIB_API const char* ZDICT_getErrorName(size_t errorCode); @@ -78,11 +126,8 @@ * Use them only in association with static linking. * ==================================================================================== */ -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) */-} ZDICT_params_t;+#define ZDICT_CONTENTSIZE_MIN 128+#define ZDICT_DICTSIZE_MIN 256 /*! ZDICT_cover_params_t: * k and d are the only required parameters.@@ -94,6 +139,8 @@ unsigned steps; /* Number of steps : Only used for optimization : 0 means default (40) : Higher means more parameters checked */ unsigned nbThreads; /* Number of threads : constraint: 0 < nbThreads : 1 means single-threaded : Only used for optimization : Ignored if ZSTD_MULTITHREAD is not defined */ double splitPoint; /* Percentage of samples used for training: Only used for optimization : the first nbSamples * splitPoint samples will be used to training, the last nbSamples * (1 - splitPoint) samples will be used for testing, 0 means default (1.0), 1.0 when all samples are used for both training and testing */+ unsigned shrinkDict; /* Train dictionaries to shrink in size starting from the minimum size and selects the smallest dictionary that is shrinkDictMaxRegression% worse than the largest dictionary. 0 means no shrinking and 1 means shrinking */+ unsigned shrinkDictMaxRegression; /* Sets shrinkDictMaxRegression so that a smaller dictionary can be at worse shrinkDictMaxRegression% worse than the max dict size dictionary. */ ZDICT_params_t zParams; } ZDICT_cover_params_t; @@ -105,6 +152,9 @@ unsigned nbThreads; /* Number of threads : constraint: 0 < nbThreads : 1 means single-threaded : Only used for optimization : Ignored if ZSTD_MULTITHREAD is not defined */ double splitPoint; /* Percentage of samples used for training: Only used for optimization : the first nbSamples * splitPoint samples will be used to training, the last nbSamples * (1 - splitPoint) samples will be used for testing, 0 means default (0.75), 1.0 when all samples are used for both training and testing */ unsigned accel; /* Acceleration level: constraint: 0 < accel <= 10, higher means faster and less accurate, 0 means default(1) */+ unsigned shrinkDict; /* Train dictionaries to shrink in size starting from the minimum size and selects the smallest dictionary that is shrinkDictMaxRegression% worse than the largest dictionary. 0 means no shrinking and 1 means shrinking */+ unsigned shrinkDictMaxRegression; /* Sets shrinkDictMaxRegression so that a smaller dictionary can be at worse shrinkDictMaxRegression% worse than the max dict size dictionary. */+ ZDICT_params_t zParams; } ZDICT_fastCover_params_t; @@ -192,28 +242,6 @@ size_t dictBufferCapacity, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, ZDICT_fastCover_params_t* parameters);--/*! ZDICT_finalizeDictionary():- * Given a custom content as a basis for dictionary, and a set of samples,- * finalize dictionary by adding headers and statistics.- *- * Samples must be stored concatenated in a flat buffer `samplesBuffer`,- * supplied with an array of sizes `samplesSizes`, providing the size of each sample in order.- *- * dictContentSize must be >= ZDICT_CONTENTSIZE_MIN bytes.- * maxDictSize must be >= dictContentSize, and must be >= ZDICT_DICTSIZE_MIN bytes.- *- * @return: size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`),- * or an error code, which can be tested by ZDICT_isError().- * Note: ZDICT_finalizeDictionary() will push notifications into stderr if instructed to, using notificationLevel>0.- * Note 2: dictBuffer and dictContent can overlap- */-#define ZDICT_CONTENTSIZE_MIN 128-#define ZDICT_DICTSIZE_MIN 256-ZDICTLIB_API size_t ZDICT_finalizeDictionary(void* dictBuffer, size_t dictBufferCapacity,- const void* dictContent, size_t dictContentSize,- const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples,- ZDICT_params_t parameters); typedef struct { unsigned selectivityLevel; /* 0 means default; larger => select more => larger dictionary */
zstd/lib/zstd.h view
@@ -1,5 +1,5 @@ /*- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.+ * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the@@ -15,6 +15,7 @@ #define ZSTD_H_235446 /* ====== Dependency ======*/+#include <limits.h> /* INT_MAX */ #include <stddef.h> /* size_t */ @@ -71,7 +72,7 @@ /*------ Version ------*/ #define ZSTD_VERSION_MAJOR 1 #define ZSTD_VERSION_MINOR 4-#define ZSTD_VERSION_RELEASE 0+#define ZSTD_VERSION_RELEASE 5 #define ZSTD_VERSION_NUMBER (ZSTD_VERSION_MAJOR *100*100 + ZSTD_VERSION_MINOR *100 + ZSTD_VERSION_RELEASE) ZSTDLIB_API unsigned ZSTD_versionNumber(void); /**< to check runtime library version */@@ -82,16 +83,16 @@ #define ZSTD_VERSION_STRING ZSTD_EXPAND_AND_QUOTE(ZSTD_LIB_VERSION) ZSTDLIB_API const char* ZSTD_versionString(void); /* requires v1.3.0+ */ -/***************************************-* Default constant-***************************************/+/* *************************************+ * Default constant+ ***************************************/ #ifndef ZSTD_CLEVEL_DEFAULT # define ZSTD_CLEVEL_DEFAULT 3 #endif -/***************************************-* Constants-***************************************/+/* *************************************+ * Constants+ ***************************************/ /* All magic numbers are supposed read/written to/from files/memory using little-endian convention */ #define ZSTD_MAGICNUMBER 0xFD2FB528 /* valid since v0.8.0 */@@ -183,17 +184,26 @@ ***************************************/ /*= Compression context * When compressing many times,- * it is recommended to allocate a context just once, and re-use it for each successive compression operation.+ * it is recommended to allocate a context just once,+ * and re-use it for each successive compression operation. * This will make workload friendlier for system's memory.- * Use one context per thread for parallel execution in multi-threaded environments. */+ * Note : re-using context is just a speed / resource optimization.+ * It doesn't change the compression ratio, which remains identical.+ * Note 2 : In multi-threaded environments,+ * use one different context per thread for parallel execution.+ */ typedef struct ZSTD_CCtx_s ZSTD_CCtx; ZSTDLIB_API ZSTD_CCtx* ZSTD_createCCtx(void); ZSTDLIB_API size_t ZSTD_freeCCtx(ZSTD_CCtx* cctx); /*! ZSTD_compressCCtx() :- * Same as ZSTD_compress(), using an explicit ZSTD_CCtx- * The function will compress at requested compression level,- * ignoring any other parameter */+ * Same as ZSTD_compress(), using an explicit ZSTD_CCtx.+ * Important : in order to behave similarly to `ZSTD_compress()`,+ * this function compresses at requested compression level,+ * __ignoring any other parameter__ .+ * If any advanced parameter was set using the advanced API,+ * they will all be reset. Only `compressionLevel` remains.+ */ ZSTDLIB_API size_t ZSTD_compressCCtx(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize,@@ -228,7 +238,7 @@ * using ZSTD_CCtx_set*() functions. * Pushed parameters are sticky : they are valid for next compressed frame, and any subsequent frame. * "sticky" parameters are applicable to `ZSTD_compress2()` and `ZSTD_compressStream*()` !- * They do not apply to "simple" one-shot variants such as ZSTD_compressCCtx()+ * __They do not apply to "simple" one-shot variants such as ZSTD_compressCCtx()__ . * * It's possible to reset all parameters to "default" using ZSTD_CCtx_reset(). *@@ -256,18 +266,29 @@ /* compression parameters * Note: When compressing with a ZSTD_CDict these parameters are superseded- * by the parameters used to construct the ZSTD_CDict. See ZSTD_CCtx_refCDict()- * for more info (superseded-by-cdict). */- ZSTD_c_compressionLevel=100, /* Update all compression parameters according to pre-defined cLevel table+ * by the parameters used to construct the ZSTD_CDict.+ * See ZSTD_CCtx_refCDict() for more info (superseded-by-cdict). */+ ZSTD_c_compressionLevel=100, /* Set compression parameters according to pre-defined cLevel table.+ * Note that exact compression parameters are dynamically determined,+ * depending on both compression level and srcSize (when known). * Default level is ZSTD_CLEVEL_DEFAULT==3. * Special: value 0 means default, which is controlled by ZSTD_CLEVEL_DEFAULT. * Note 1 : it's possible to pass a negative compression level.- * Note 2 : setting a level sets all default values of other compression parameters */+ * Note 2 : setting a level does not automatically set all other compression parameters+ * to default. Setting this will however eventually dynamically impact the compression+ * parameters which have not been manually set. The manually set+ * ones will 'stick'. */+ /* Advanced compression parameters :+ * It's possible to pin down compression parameters to some specific values.+ * In which case, these values are no longer dynamically selected by the compressor */ ZSTD_c_windowLog=101, /* Maximum allowed back-reference distance, expressed as power of 2.+ * This will set a memory budget for streaming decompression,+ * with larger values requiring more memory+ * and typically compressing more. * Must be clamped between ZSTD_WINDOWLOG_MIN and ZSTD_WINDOWLOG_MAX. * Special: value 0 means "use default windowLog". * Note: Using a windowLog greater than ZSTD_WINDOWLOG_LIMIT_DEFAULT- * requires explicitly allowing such window size at decompression stage if using streaming. */+ * requires explicitly allowing such size at streaming decompression stage. */ ZSTD_c_hashLog=102, /* Size of the initial probe table, as a power of 2. * Resulting memory usage is (1 << (hashLog+2)). * Must be clamped between ZSTD_HASHLOG_MIN and ZSTD_HASHLOG_MAX.@@ -278,13 +299,13 @@ * Resulting memory usage is (1 << (chainLog+2)). * Must be clamped between ZSTD_CHAINLOG_MIN and ZSTD_CHAINLOG_MAX. * Larger tables result in better and slower compression.- * This parameter is useless when using "fast" strategy.+ * This parameter is useless for "fast" strategy. * It's still useful when using "dfast" strategy, * in which case it defines a secondary probe table. * Special: value 0 means "use default chainLog". */ ZSTD_c_searchLog=104, /* Number of search attempts, as a power of 2. * More attempts result in better and slower compression.- * This parameter is useless when using "fast" and "dFast" strategies.+ * This parameter is useless for "fast" and "dFast" strategies. * Special: value 0 means "use default searchLog". */ ZSTD_c_minMatch=105, /* Minimum size of searched matches. * Note that Zstandard can still find matches of smaller size,@@ -339,7 +360,7 @@ ZSTD_c_contentSizeFlag=200, /* Content size will be written into frame header _whenever known_ (default:1) * Content size must be known at the beginning of compression. * This is automatically the case when using ZSTD_compress2(),- * For streaming variants, content size must be provided with ZSTD_CCtx_setPledgedSrcSize() */+ * For streaming scenarios, content size must be provided with ZSTD_CCtx_setPledgedSrcSize() */ ZSTD_c_checksumFlag=201, /* A 32-bits checksum of content is written at end of frame (default:0) */ ZSTD_c_dictIDFlag=202, /* When applicable, dictionary's ID is written into frame header (default:1) */ @@ -358,7 +379,7 @@ * Each compression job is completed in parallel, so this value can indirectly impact the nb of active threads. * 0 means default, which is dynamically determined based on compression parameters. * Job size must be a minimum of overlap size, or 1 MB, whichever is largest.- * The minimum size is automatically and transparently enforced */+ * The minimum size is automatically and transparently enforced. */ ZSTD_c_overlapLog=402, /* Control the overlap size, as a fraction of window size. * The overlap size is an amount of data reloaded from previous job at the beginning of a new job. * It helps preserve compression ratio, while each job is compressed in parallel.@@ -380,6 +401,8 @@ * ZSTD_c_forceMaxWindow * ZSTD_c_forceAttachDict * ZSTD_c_literalCompressionMode+ * ZSTD_c_targetCBlockSize+ * ZSTD_c_srcSizeHint * 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.@@ -389,6 +412,8 @@ ZSTD_c_experimentalParam3=1000, ZSTD_c_experimentalParam4=1001, ZSTD_c_experimentalParam5=1002,+ ZSTD_c_experimentalParam6=1003,+ ZSTD_c_experimentalParam7=1004 } ZSTD_cParameter; typedef struct {@@ -497,11 +522,13 @@ /* note : additional experimental parameters are also available * within the experimental section of the API. * At the time of this writing, they include :- * ZSTD_c_format+ * ZSTD_d_format+ * ZSTD_d_stableOutBuffer * 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_experimentalParam1=1000,+ ZSTD_d_experimentalParam2=1001 } ZSTD_dParameter; @@ -657,17 +684,33 @@ ZSTD_inBuffer* input, ZSTD_EndDirective endOp); ++/* These buffer sizes are softly recommended.+ * They are not required : ZSTD_compressStream*() happily accepts any buffer size, for both input and output.+ * Respecting the recommended size just makes it a bit easier for ZSTD_compressStream*(),+ * reducing the amount of memory shuffling and buffering, resulting in minor performance savings.+ *+ * However, note that these recommendations are from the perspective of a C caller program.+ * If the streaming interface is invoked from some other language,+ * especially managed ones such as Java or Go, through a foreign function interface such as jni or cgo,+ * a major performance rule is to reduce crossing such interface to an absolute minimum.+ * It's not rare that performance ends being spent more into the interface, rather than compression itself.+ * In which cases, prefer using large buffers, as large as practical,+ * for both input and output, to reduce the nb of roundtrips.+ */ ZSTDLIB_API size_t ZSTD_CStreamInSize(void); /**< recommended size for input buffer */-ZSTDLIB_API size_t ZSTD_CStreamOutSize(void); /**< recommended size for output buffer. Guarantee to successfully flush at least one complete compressed block in all circumstances. */+ZSTDLIB_API size_t ZSTD_CStreamOutSize(void); /**< recommended size for output buffer. Guarantee to successfully flush at least one complete compressed block. */ -/*******************************************************************************- * This is a legacy streaming API, and can be replaced by ZSTD_CCtx_reset() and- * ZSTD_compressStream2(). It is redundant, but is still fully supported.++/* *****************************************************************************+ * This following is a legacy streaming API.+ * It can be replaced by ZSTD_CCtx_reset() and ZSTD_compressStream2().+ * It is redundant, but remains fully supported. * Advanced parameters and dictionary compression can only be used through the * new API. ******************************************************************************/ -/**+/*! * Equivalent to: * * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);@@ -675,16 +718,16 @@ * ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel); */ ZSTDLIB_API size_t ZSTD_initCStream(ZSTD_CStream* zcs, int compressionLevel);-/**+/*! * Alternative for ZSTD_compressStream2(zcs, output, input, ZSTD_e_continue). * NOTE: The return value is different. ZSTD_compressStream() returns a hint for * the next read size (if non-zero and not an error). ZSTD_compressStream2()- * returns the number of bytes left to flush (if non-zero and not an error).+ * returns the minimum nb of bytes left to flush (if non-zero and not an error). */ ZSTDLIB_API size_t ZSTD_compressStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input);-/** Equivalent to ZSTD_compressStream2(zcs, output, &emptyInput, ZSTD_e_flush). */+/*! Equivalent to ZSTD_compressStream2(zcs, output, &emptyInput, ZSTD_e_flush). */ ZSTDLIB_API size_t ZSTD_flushStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output);-/** Equivalent to ZSTD_compressStream2(zcs, output, &emptyInput, ZSTD_e_end). */+/*! Equivalent to ZSTD_compressStream2(zcs, output, &emptyInput, ZSTD_e_end). */ ZSTDLIB_API size_t ZSTD_endStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output); @@ -725,7 +768,7 @@ /* This function is redundant with the advanced API and equivalent to: *- * ZSTD_DCtx_reset(zds);+ * ZSTD_DCtx_reset(zds, ZSTD_reset_session_only); * ZSTD_DCtx_refDDict(zds, NULL); */ ZSTDLIB_API size_t ZSTD_initDStream(ZSTD_DStream* zds);@@ -770,12 +813,17 @@ typedef struct ZSTD_CDict_s ZSTD_CDict; /*! ZSTD_createCDict() :- * When compressing multiple messages / blocks using the same dictionary, it's recommended to load it only once.- * ZSTD_createCDict() will create a digested dictionary, ready to start future compression operations without startup cost.+ * When compressing multiple messages or blocks using the same dictionary,+ * it's recommended to digest the dictionary only once, since it's a costly operation.+ * ZSTD_createCDict() will create a state from digesting a dictionary.+ * The resulting state can be used for future compression operations with very limited startup cost. * ZSTD_CDict can be created once and shared by multiple threads concurrently, since its usage is read-only.- * `dictBuffer` can be released after ZSTD_CDict creation, because its content is copied within CDict.- * Consider experimental function `ZSTD_createCDict_byReference()` if you prefer to not duplicate `dictBuffer` content.- * Note : A ZSTD_CDict can be created from an empty dictBuffer, but it is inefficient when used to compress small data. */+ * @dictBuffer can be released after ZSTD_CDict creation, because its content is copied within CDict.+ * Note 1 : Consider experimental function `ZSTD_createCDict_byReference()` if you prefer to not duplicate @dictBuffer content.+ * Note 2 : A ZSTD_CDict can be created from an empty @dictBuffer,+ * in which case the only thing that it transports is the @compressionLevel.+ * This can be useful in a pipeline featuring ZSTD_compress_usingCDict() exclusively,+ * expecting a ZSTD_CDict parameter with any data, including those without a known dictionary. */ ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict(const void* dictBuffer, size_t dictSize, int compressionLevel); @@ -902,7 +950,7 @@ * Note 3 : Referencing a prefix involves building tables, which are dependent on compression parameters. * It's a CPU consuming operation, with non-negligible impact on latency. * If there is a need to use the same prefix multiple times, consider loadDictionary instead.- * Note 4 : By default, the prefix is interpreted as raw content (ZSTD_dm_rawContent).+ * Note 4 : By default, the prefix is interpreted as raw content (ZSTD_dct_rawContent). * Use experimental ZSTD_CCtx_refPrefix_advanced() to alter dictionary interpretation. */ ZSTDLIB_API size_t ZSTD_CCtx_refPrefix(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize);@@ -946,7 +994,7 @@ * Note 2 : Prefix buffer is referenced. It **must** outlive decompression. * Prefix buffer must remain unmodified up to the end of frame, * reached when ZSTD_decompressStream() returns 0.- * Note 3 : By default, the prefix is treated as raw content (ZSTD_dm_rawContent).+ * Note 3 : By default, the prefix is treated as raw content (ZSTD_dct_rawContent). * Use ZSTD_CCtx_refPrefix_advanced() to alter dictMode (Experimental section) * Note 4 : Referencing a raw content prefix has almost no cpu nor memory cost. * A full dictionary is more costly, as it requires building tables.@@ -969,7 +1017,7 @@ #endif /* ZSTD_H_235446 */ -/****************************************************************************************+/* ************************************************************************************** * ADVANCED AND EXPERIMENTAL FUNCTIONS **************************************************************************************** * The definitions in the following section are considered experimental.@@ -991,8 +1039,8 @@ * Some of them might be removed in the future (especially when redundant with existing stable functions) * ***************************************************************************************/ -#define ZSTD_FRAMEHEADERSIZE_PREFIX 5 /* minimum input size required to query frame header size */-#define ZSTD_FRAMEHEADERSIZE_MIN 6+#define ZSTD_FRAMEHEADERSIZE_PREFIX(format) ((format) == ZSTD_f_zstd1 ? 5 : 1) /* minimum input size required to query frame header size */+#define ZSTD_FRAMEHEADERSIZE_MIN(format) ((format) == ZSTD_f_zstd1 ? 6 : 2) #define ZSTD_FRAMEHEADERSIZE_MAX 18 /* can be useful for static allocation */ #define ZSTD_SKIPPABLEHEADERSIZE 8 @@ -1037,6 +1085,12 @@ #define ZSTD_LDM_HASHRATELOG_MIN 0 #define ZSTD_LDM_HASHRATELOG_MAX (ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN) +/* Advanced parameter bounds */+#define ZSTD_TARGETCBLOCKSIZE_MIN 64+#define ZSTD_TARGETCBLOCKSIZE_MAX ZSTD_BLOCKSIZE_MAX+#define ZSTD_SRCSIZEHINT_MIN 0+#define ZSTD_SRCSIZEHINT_MAX INT_MAX+ /* internal */ #define ZSTD_HASHLOG3_MAX 17 @@ -1046,6 +1100,24 @@ typedef struct ZSTD_CCtx_params_s ZSTD_CCtx_params; typedef struct {+ unsigned int matchPos; /* Match pos in dst */+ /* If seqDef.offset > 3, then this is seqDef.offset - 3+ * If seqDef.offset < 3, then this is the corresponding repeat offset+ * But if seqDef.offset < 3 and litLength == 0, this is the+ * repeat offset before the corresponding repeat offset+ * And if seqDef.offset == 3 and litLength == 0, this is the+ * most recent repeat offset - 1+ */+ unsigned int offset;+ unsigned int litLength; /* Literal length */+ unsigned int matchLength; /* Match length */+ /* 0 when seq not rep and seqDef.offset otherwise+ * when litLength == 0 this will be <= 4, otherwise <= 3 like normal+ */+ unsigned int rep;+} ZSTD_Sequence;++typedef struct { unsigned windowLog; /**< largest match distance : larger == more compression, more memory needed during decompression */ unsigned chainLog; /**< fully searched segment : larger == more compression, slower, more memory (useless for fast) */ unsigned hashLog; /**< dispatch table : larger == faster, more memory */@@ -1074,21 +1146,12 @@ typedef enum { ZSTD_dlm_byCopy = 0, /**< Copy dictionary content internally */- ZSTD_dlm_byRef = 1, /**< Reference dictionary content -- the dictionary buffer must outlive its users. */+ ZSTD_dlm_byRef = 1 /**< Reference dictionary content -- the dictionary buffer must outlive its users. */ } ZSTD_dictLoadMethod_e; typedef enum {- /* Opened question : should we have a format ZSTD_f_auto ?- * Today, it would mean exactly the same as ZSTD_f_zstd1.- * But, in the future, should several formats become supported,- * on the compression side, it would mean "default format".- * On the decompression side, it would mean "automatic format detection",- * so that ZSTD_f_zstd1 would mean "accept *only* zstd frames".- * Since meaning is a little different, another option could be to define different enums for compression and decompression.- * This question could be kept for later, when there are actually multiple formats to support,- * but there is also the question of pinning enum values, and pinning value `0` is especially important */ ZSTD_f_zstd1 = 0, /* zstd frame format, specified in zstd_compression_format.md (default) */- ZSTD_f_zstd1_magicless = 1, /* Variant of zstd frame format, without initial 4-bytes magic number.+ ZSTD_f_zstd1_magicless = 1 /* Variant of zstd frame format, without initial 4-bytes magic number. * Useful to save 4 bytes per generated frame. * Decoder cannot recognise automatically this format, requiring this instruction. */ } ZSTD_format_e;@@ -1099,7 +1162,7 @@ * to evolve and should be considered only in the context of extremely * advanced performance tuning. *- * Zstd currently supports the use of a CDict in two ways:+ * Zstd currently supports the use of a CDict in three ways: * * - The contents of the CDict can be copied into the working context. This * means that the compression can search both the dictionary and input@@ -1115,6 +1178,12 @@ * working context's tables can be reused). For small inputs, this can be * faster than copying the CDict's tables. *+ * - The CDict's tables are not used at all, and instead we use the working+ * context alone to reload the dictionary and use params based on the source+ * size. See ZSTD_compress_insertDictionary() and ZSTD_compress_usingDict().+ * This method is effective when the dictionary sizes are very small relative+ * to the input size, and the input size is fairly large to begin with.+ * * Zstd has a simple internal heuristic that selects which strategy to use * at the beginning of a compression. However, if experimentation shows that * Zstd is making poor choices, it is possible to override that choice with@@ -1123,6 +1192,7 @@ ZSTD_dictDefaultAttach = 0, /* Use the default heuristic. */ ZSTD_dictForceAttach = 1, /* Never copy the dictionary. */ ZSTD_dictForceCopy = 2, /* Always copy the dictionary. */+ ZSTD_dictForceLoad = 3 /* Always reload the dictionary */ } ZSTD_dictAttachPref_e; typedef enum {@@ -1131,7 +1201,7 @@ * levels will be compressed. */ ZSTD_lcm_huffman = 1, /**< Always attempt Huffman compression. Uncompressed literals will still be * emitted if Huffman compression is not profitable. */- ZSTD_lcm_uncompressed = 2, /**< Always emit uncompressed literals. */+ ZSTD_lcm_uncompressed = 2 /**< Always emit uncompressed literals. */ } ZSTD_literalCompressionMode_e; @@ -1162,7 +1232,7 @@ * however it does mean that all frame data must be present and valid. */ ZSTDLIB_API unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize); -/** ZSTD_decompressBound() :+/*! ZSTD_decompressBound() : * `src` should point to the start of a series of ZSTD encoded and/or skippable frames * `srcSize` must be the _exact_ size of this series * (i.e. there should be a frame boundary at `src + srcSize`)@@ -1183,7 +1253,16 @@ * or an error code (if srcSize is too small) */ ZSTDLIB_API size_t ZSTD_frameHeaderSize(const void* src, size_t srcSize); +/*! ZSTD_getSequences() :+ * Extract sequences from the sequence store+ * zc can be used to insert custom compression params.+ * This function invokes ZSTD_compress2+ * @return : number of sequences extracted+ */+ZSTDLIB_API size_t ZSTD_getSequences(ZSTD_CCtx* zc, ZSTD_Sequence* outSeqs,+ size_t outSeqsSize, const void* src, size_t srcSize); + /*************************************** * Memory management ***************************************/@@ -1191,12 +1270,26 @@ /*! ZSTD_estimate*() : * These functions make it possible to estimate memory usage * of a future {D,C}Ctx, before its creation.- * ZSTD_estimateCCtxSize() will provide a budget large enough for any compression level up to selected one.- * It will also consider src size to be arbitrarily "large", which is worst case.- * If srcSize is known to always be small, ZSTD_estimateCCtxSize_usingCParams() can provide a tighter estimation.- * ZSTD_estimateCCtxSize_usingCParams() can be used in tandem with ZSTD_getCParams() to create cParams from compressionLevel.- * ZSTD_estimateCCtxSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParams_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_c_nbWorkers is >= 1.- * Note : CCtx size estimation is only correct for single-threaded compression. */+ *+ * ZSTD_estimateCCtxSize() will provide a memory budget large enough+ * for any compression level up to selected one.+ * Note : Unlike ZSTD_estimateCStreamSize*(), this estimate+ * does not include space for a window buffer.+ * Therefore, the estimation is only guaranteed for single-shot compressions, not streaming.+ * The estimate will assume the input may be arbitrarily large,+ * which is the worst case.+ *+ * When srcSize can be bound by a known and rather "small" value,+ * this fact can be used to provide a tighter estimation+ * because the CCtx compression context will need less memory.+ * This tighter estimation can be provided by more advanced functions+ * ZSTD_estimateCCtxSize_usingCParams(), which can be used in tandem with ZSTD_getCParams(),+ * 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.+ * ZSTD_estimateCCtxSize_usingCCtxParams() will return an error code if ZSTD_c_nbWorkers is >= 1.+ */ ZSTDLIB_API size_t ZSTD_estimateCCtxSize(int compressionLevel); ZSTDLIB_API size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams); ZSTDLIB_API size_t ZSTD_estimateCCtxSize_usingCCtxParams(const ZSTD_CCtx_params* params);@@ -1307,7 +1400,8 @@ * Create a digested dictionary for compression * Dictionary content is just referenced, not duplicated. * As a consequence, `dictBuffer` **must** outlive CDict,- * and its content must remain unmodified throughout the lifetime of CDict. */+ * and its content must remain unmodified throughout the lifetime of CDict.+ * note: equivalent to ZSTD_createCDict_advanced(), with dictLoadMethod==ZSTD_dlm_byRef */ ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_byReference(const void* dictBuffer, size_t dictSize, int compressionLevel); /*! ZSTD_getCParams() :@@ -1334,7 +1428,9 @@ ZSTDLIB_API ZSTD_compressionParameters ZSTD_adjustCParams(ZSTD_compressionParameters cPar, unsigned long long srcSize, size_t dictSize); /*! ZSTD_compress_advanced() :- * Same as ZSTD_compress_usingDict(), with fine-tune control over compression parameters (by structure) */+ * 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 be marked as deprecated and generate compilation warning on reaching v1.5.x */ ZSTDLIB_API size_t ZSTD_compress_advanced(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize,@@ -1342,7 +1438,9 @@ ZSTD_parameters params); /*! ZSTD_compress_usingCDict_advanced() :- * Same as ZSTD_compress_usingCDict(), with fine-tune control over frame parameters */+ * Note : this function is now REDUNDANT.+ * It can be replaced by ZSTD_compress2(), in combination with ZSTD_CCtx_loadDictionary() and other parameter setters.+ * This prototype will be marked as deprecated and generate compilation warning in some future version */ ZSTDLIB_API size_t ZSTD_compress_usingCDict_advanced(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize,@@ -1409,6 +1507,17 @@ */ #define ZSTD_c_literalCompressionMode ZSTD_c_experimentalParam5 +/* Tries to fit compressed block size to be around targetCBlockSize.+ * No target when targetCBlockSize == 0.+ * There is no guarantee on compressed block size (default:0) */+#define ZSTD_c_targetCBlockSize ZSTD_c_experimentalParam6++/* User's best guess of source size.+ * Hint is not valid when srcSizeHint == 0.+ * There is no guarantee that hint is close to actual source size,+ * but compression ratio may regress significantly if guess considerably underestimates */+#define ZSTD_c_srcSizeHint ZSTD_c_experimentalParam7+ /*! ZSTD_CCtx_getParameter() : * Get the requested compression parameter value, selected by enum ZSTD_cParameter, * and store it into int* value.@@ -1543,6 +1652,37 @@ * allowing selection between ZSTD_format_e input compression formats */ #define ZSTD_d_format ZSTD_d_experimentalParam1+/* ZSTD_d_stableOutBuffer+ * Experimental parameter.+ * Default is 0 == disabled. Set to 1 to enable.+ *+ * Tells the decompressor that the ZSTD_outBuffer 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 decompressor, and+ * decompression will fail if it ever changes. Therefore the ZSTD_outBuffer+ * MUST be large enough to fit the entire decompressed frame. This will be+ * checked when the frame content size is known. The data in the ZSTD_outBuffer+ * 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+ * 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.+ * If you need to avoid the input buffer allocation use the buffer-less+ * streaming API.+ *+ * NOTE: So long as the ZSTD_outBuffer always points to valid memory, using+ * this flag is ALWAYS memory safe, and will never access out-of-bounds+ * memory. However, decompression WILL fail if you violate the preconditions.+ *+ * WARNING: The data in the ZSTD_outBuffer in the range [dst, dst + pos) MUST+ * not be modified during decompression or you will get data corruption. This+ * is because zstd needs to reference data in the ZSTD_outBuffer to regenerate+ * matches. Normally zstd maintains its own buffer for this purpose, but passing+ * this flag tells zstd to use the user provided buffer.+ */+#define ZSTD_d_stableOutBuffer ZSTD_d_experimentalParam2 /*! ZSTD_DCtx_setFormat() : * Instruct the decoder context about what kind of data to decode next.@@ -1581,8 +1721,13 @@ * pledgedSrcSize must be correct. If it is not known at init time, use * ZSTD_CONTENTSIZE_UNKNOWN. Note that, for compatibility with older programs, * "0" also disables frame content size field. It may be enabled in the future.+ * Note : this prototype will be marked as deprecated and generate compilation warnings on reaching v1.5.x */-ZSTDLIB_API size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize);+ZSTDLIB_API size_t+ZSTD_initCStream_srcSize(ZSTD_CStream* zcs,+ int compressionLevel,+ unsigned long long pledgedSrcSize);+ /**! ZSTD_initCStream_usingDict() : * This function is deprecated, and is equivalent to: * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);@@ -1591,42 +1736,66 @@ * * Creates of an internal CDict (incompatible with static CCtx), except if * dict == NULL or dictSize < 8, in which case no dict is used.- * Note: dict is loaded with ZSTD_dm_auto (treated as a full zstd dictionary if+ * Note: dict is loaded with ZSTD_dct_auto (treated as a full zstd dictionary if * it begins with ZSTD_MAGIC_DICTIONARY, else as raw content) and ZSTD_dlm_byCopy.+ * Note : this prototype will be marked as deprecated and generate compilation warnings on reaching v1.5.x */-ZSTDLIB_API size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel);+ZSTDLIB_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: * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);- * ZSTD_CCtx_setZstdParams(zcs, params); // Set the zstd params and leave the rest as-is+ * // Pseudocode: Set each zstd parameter and leave the rest as-is.+ * for ((param, value) : params) {+ * ZSTD_CCtx_setParameter(zcs, param, value);+ * } * ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize); * ZSTD_CCtx_loadDictionary(zcs, dict, dictSize); *- * pledgedSrcSize must be correct. If srcSize is not known at init time, use- * value ZSTD_CONTENTSIZE_UNKNOWN. dict is loaded with ZSTD_dm_auto and ZSTD_dlm_byCopy.+ * dict is loaded with ZSTD_dct_auto and ZSTD_dlm_byCopy.+ * pledgedSrcSize must be correct.+ * If srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN.+ * Note : this prototype will be marked as deprecated and generate compilation warnings on reaching v1.5.x */-ZSTDLIB_API size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize,- ZSTD_parameters params, unsigned long long pledgedSrcSize);+ZSTDLIB_API size_t+ZSTD_initCStream_advanced(ZSTD_CStream* zcs,+ const void* dict, size_t dictSize,+ ZSTD_parameters params,+ unsigned long long pledgedSrcSize);+ /**! ZSTD_initCStream_usingCDict() : * This function is deprecated, and equivalent to: * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only); * ZSTD_CCtx_refCDict(zcs, cdict); * * note : cdict will just be referenced, and must outlive compression session+ * Note : this prototype will be marked as deprecated and generate compilation warnings on reaching v1.5.x */ ZSTDLIB_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 approximately equivalent to: * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);- * ZSTD_CCtx_setZstdFrameParams(zcs, fParams); // Set the zstd frame params and leave the rest as-is+ * // Pseudocode: Set each zstd frame parameter and leave the rest as-is.+ * for ((fParam, value) : fParams) {+ * ZSTD_CCtx_setParameter(zcs, fParam, value);+ * } * ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize); * ZSTD_CCtx_refCDict(zcs, cdict); * * same as ZSTD_initCStream_usingCDict(), with control over frame parameters. * pledgedSrcSize must be correct. If srcSize is not known at init time, use * value ZSTD_CONTENTSIZE_UNKNOWN.+ * Note : this prototype will be marked as deprecated and generate compilation warnings on reaching v1.5.x */-ZSTDLIB_API size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const ZSTD_CDict* cdict, ZSTD_frameParameters fParams, unsigned long long pledgedSrcSize);+ZSTDLIB_API size_t+ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs,+ const ZSTD_CDict* cdict,+ ZSTD_frameParameters fParams,+ unsigned long long pledgedSrcSize); /*! ZSTD_resetCStream() : * This function is deprecated, and is equivalent to:@@ -1641,6 +1810,7 @@ * For the time being, pledgedSrcSize==0 is interpreted as "srcSize unknown" for compatibility with older programs, * but it will change to mean "empty" in future version, so use macro ZSTD_CONTENTSIZE_UNKNOWN instead. * @return : 0, or an error code (which can be tested using ZSTD_isError())+ * Note : this prototype will be marked as deprecated and generate compilation warnings on reaching v1.5.x */ ZSTDLIB_API size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize); @@ -1686,8 +1856,10 @@ * 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 */ ZSTDLIB_API size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize);+ /** * This function is deprecated, and is equivalent to: *@@ -1695,14 +1867,17 @@ * 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 */ ZSTDLIB_API size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const ZSTD_DDict* ddict);+ /** * This function is deprecated, and is equivalent to: * * 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 */ ZSTDLIB_API size_t ZSTD_resetDStream(ZSTD_DStream* zds); @@ -1843,7 +2018,7 @@ unsigned checksumFlag; } ZSTD_frameHeader; -/** ZSTD_getFrameHeader() :+/*! 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,@@ -1876,8 +2051,8 @@ /*! Block functions produce and decode raw zstd blocks, without frame metadata.- Frame metadata cost is typically ~18 bytes, which can be non-negligible for very small blocks (< 100 bytes).- User will have to take in charge required information to regenerate data, such as compressed and content sizes.+ 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. A few rules to respect : - Compressing and decompressing require a context structure@@ -1888,12 +2063,14 @@ + 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, really consider using regular ZSTD_compress() instead.- Frame metadata is not that costly, and quickly becomes negligible as source size grows larger.- - When a block is considered not compressible enough, ZSTD_compressBlock() result will be zero.- In which case, nothing is produced into `dst` !- + User must test for such outcome and deal directly with uncompressed data- + ZSTD_decompressBlock() doesn't accept uncompressed data as input !!!+ + For inputs larger than a single block, consider using regular ZSTD_compress() instead.+ Frame metadata is not that costly, and quickly becomes negligible as source size grows larger than a block.+ - When a block is considered not compressible enough, ZSTD_compressBlock() result will be 0 (zero) !+ ===> In which case, nothing is produced into `dst` !+ + User __must__ test for such outcome and deal directly with uncompressed data+ + A block cannot be declared incompressible if ZSTD_compressBlock() return value was != 0.+ Doing so would mess up with statistics history, leading to potential data corruption.+ + ZSTD_decompressBlock() _doesn't accept uncompressed data as input_ !! + In case of multiple successive blocks, should some of them be uncompressed, decoder must be informed of their existence in order to follow proper history. Use ZSTD_insertBlock() for such a case.