+1
-1
@@ -39,4 +39,4 @@ outlined on that page and do not file a public issue.
|
||||
|
||||
## License
|
||||
By contributing to Zstandard, you agree that your contributions will be licensed
|
||||
under the [LICENSE](LICENSE) file in the root directory of this source tree.
|
||||
under both the [LICENSE](LICENSE) file and the [COPYING](COPYING) file in the root directory of this source tree.
|
||||
|
||||
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
@@ -74,12 +74,9 @@ zstdmt:
|
||||
zlibwrapper:
|
||||
$(MAKE) -C $(ZWRAPDIR) test
|
||||
|
||||
.PHONY: shortest
|
||||
shortest:
|
||||
$(MAKE) -C $(TESTDIR) $@
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
.PHONY: test shortest
|
||||
test shortest:
|
||||
$(MAKE) -C $(PRGDIR) allVariants
|
||||
$(MAKE) -C $(TESTDIR) $@
|
||||
|
||||
.PHONY: examples
|
||||
@@ -146,6 +143,11 @@ gcc6build: clean
|
||||
gcc-6 -v
|
||||
CC=gcc-6 $(MAKE) all MOREFLAGS="-Werror"
|
||||
|
||||
.PHONY: gcc7build
|
||||
gcc7build: clean
|
||||
gcc-7 -v
|
||||
CC=gcc-7 $(MAKE) all MOREFLAGS="-Werror"
|
||||
|
||||
.PHONY: clangbuild
|
||||
clangbuild: clean
|
||||
clang -v
|
||||
@@ -180,7 +182,7 @@ ppc64fuzz: clean
|
||||
CC=powerpc-linux-gnu-gcc QEMU_SYS=qemu-ppc64-static MOREFLAGS="-m64 -static" FUZZER_FLAGS=--no-big-tests $(MAKE) -C $(TESTDIR) fuzztest
|
||||
|
||||
gpptest: clean
|
||||
CC=g++ $(MAKE) -C $(PRGDIR) all CFLAGS="-O3 -Wall -Wextra -Wundef -Wshadow -Wcast-align -Werror"
|
||||
CC=$(CXX) $(MAKE) -C $(PRGDIR) all CFLAGS="-O3 -Wall -Wextra -Wundef -Wshadow -Wcast-align -Werror"
|
||||
|
||||
gcc5test: clean
|
||||
gcc-5 -v
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
v1.3.1
|
||||
New license : BSD + GPLv2
|
||||
perf: substantially decreased memory usage in Multi-threading mode, thanks to reports by Tino Reichardt (@mcmilk)
|
||||
perf: Multi-threading supports up to 256 threads. Cap at 256 when more are requested (#760)
|
||||
cli : improved and fixed --list command, by @ib (#772)
|
||||
cli : command -vV to list supported formats, by @ib (#771)
|
||||
build : fixed binary variants, reported by @svenha (#788)
|
||||
build : fix Visual compilation for non x86/x64 targets, reported by Greg Slazinski (@GregSlazinski) (#718)
|
||||
API exp : breaking change : ZSTD_getframeHeader() provides more information
|
||||
API exp : breaking change : pinned down values of error codes
|
||||
doc : fixed huffman example, by Ulrich Kunitz (@ulikunitz)
|
||||
new : contrib/adaptive-compression, I/O driven compression strength, by Paul Cruz (@paulcruz74)
|
||||
new : contrib/long_distance_matching, statistics by Stella Lau (@stellamplau)
|
||||
updated : contrib/linux-kernel, by Nick Terrell (@terrelln)
|
||||
|
||||
v1.3.0
|
||||
cli : new : `--list` command, by Paul Cruz
|
||||
cli : changed : xz/lzma support enabled by default
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
Additional Grant of Patent Rights Version 2
|
||||
|
||||
"Software" means the Zstandard software distributed by Facebook, Inc.
|
||||
|
||||
Facebook, Inc. ("Facebook") hereby grants to each recipient of the Software
|
||||
("you") a perpetual, worldwide, royalty-free, non-exclusive, irrevocable
|
||||
(subject to the termination provision below) license under any Necessary
|
||||
Claims, to make, have made, use, sell, offer to sell, import, and otherwise
|
||||
transfer the Software. For avoidance of doubt, no license is granted under
|
||||
Facebook’s rights in any patent claims that are infringed by (i) modifications
|
||||
to the Software made by you or any third party or (ii) the Software in
|
||||
combination with any software or other technology.
|
||||
|
||||
The license granted hereunder will terminate, automatically and without notice,
|
||||
if you (or any of your subsidiaries, corporate affiliates or agents) initiate
|
||||
directly or indirectly, or take a direct financial interest in, any Patent
|
||||
Assertion: (i) against Facebook or any of its subsidiaries or corporate
|
||||
affiliates, (ii) against any party if such Patent Assertion arises in whole or
|
||||
in part from any software, technology, product or service of Facebook or any of
|
||||
its subsidiaries or corporate affiliates, or (iii) against any party relating
|
||||
to the Software. Notwithstanding the foregoing, if Facebook or any of its
|
||||
subsidiaries or corporate affiliates files a lawsuit alleging patent
|
||||
infringement against you in the first instance, and you respond by filing a
|
||||
patent infringement counterclaim in that lawsuit against that party that is
|
||||
unrelated to the Software, the license granted hereunder will not terminate
|
||||
under section (i) of this paragraph due to such counterclaim.
|
||||
|
||||
A "Necessary Claim" is a claim of a patent owned by Facebook that is
|
||||
necessarily infringed by the Software standing alone.
|
||||
|
||||
A "Patent Assertion" is any lawsuit or other action alleging direct, indirect,
|
||||
or contributory infringement or inducement to infringe any patent, including a
|
||||
cross-claim or counterclaim.
|
||||
@@ -134,12 +134,12 @@ Going into `build` directory, you will find additional possibilities :
|
||||
|
||||
### Status
|
||||
|
||||
Zstandard is currently deployed within Facebook. It is used daily to compress and decompress very large amounts of data in multiple formats and use cases.
|
||||
Zstandard is currently deployed within Facebook. It is used continuously to compress large amounts of data in multiple formats and use cases.
|
||||
Zstandard is considered safe for production environments.
|
||||
|
||||
### License
|
||||
|
||||
Zstandard is [BSD-licensed](LICENSE). We also provide an [additional patent grant](PATENTS).
|
||||
Zstandard is dual-licensed under [BSD](LICENSE) and [GPLv2](COPYING).
|
||||
|
||||
### Contributing
|
||||
|
||||
|
||||
@@ -31,6 +31,9 @@ ENDIF (MSVC)
|
||||
|
||||
ADD_EXECUTABLE(zstd ${PROGRAMS_DIR}/zstdcli.c ${PROGRAMS_DIR}/fileio.c ${PROGRAMS_DIR}/bench.c ${PROGRAMS_DIR}/datagen.c ${PROGRAMS_DIR}/dibio.c ${PlatformDependResources})
|
||||
TARGET_LINK_LIBRARIES(zstd libzstd_static)
|
||||
IF (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
|
||||
TARGET_LINK_LIBRARIES(zstd rt)
|
||||
ENDIF (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
|
||||
INSTALL(TARGETS zstd RUNTIME DESTINATION "bin")
|
||||
|
||||
IF (UNIX)
|
||||
|
||||
+3
-3
@@ -3,7 +3,7 @@ dependencies:
|
||||
- sudo dpkg --add-architecture i386
|
||||
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test; sudo apt-get -y -qq update
|
||||
- sudo apt-get -y install gcc-powerpc-linux-gnu gcc-arm-linux-gnueabi libc6-dev-armel-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross
|
||||
- sudo apt-get -y install libstdc++-6-dev clang gcc g++ gcc-5 gcc-6 zlib1g-dev liblzma-dev
|
||||
- sudo apt-get -y install libstdc++-7-dev clang gcc g++ gcc-5 gcc-6 gcc-7 zlib1g-dev liblzma-dev
|
||||
- sudo apt-get -y install linux-libc-dev:i386 libc6-dev-i386
|
||||
|
||||
test:
|
||||
@@ -45,7 +45,7 @@ test:
|
||||
parallel: true
|
||||
- ? |
|
||||
if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make ppc64build && make clean; fi &&
|
||||
if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then true && make clean; fi #could add another test here
|
||||
if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make gcc7build && make clean; fi #could add another test here
|
||||
:
|
||||
parallel: true
|
||||
- ? |
|
||||
@@ -64,7 +64,7 @@ test:
|
||||
#- gcc -v; make -C tests test32 MOREFLAGS="-I/usr/include/x86_64-linux-gnu" && make clean
|
||||
#- make uasan && make clean
|
||||
#- make asan32 && make clean
|
||||
#- make -C tests test32 CC=clang MOREFLAGS="-g -fsanitize=address -I/usr/include/x86_64-linux-gnu"
|
||||
#- make -C tests test32 CC=clang MOREFLAGS="-g -fsanitize=address -I/usr/include/x86_64-linux-gnu"
|
||||
# Valgrind tests
|
||||
#- CFLAGS="-O1 -g" make -C zlibWrapper valgrindTest && make clean
|
||||
#- make -C tests valgrindTest && make clean
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
|
||||
ZSTDDIR = ../../lib
|
||||
PRGDIR = ../../programs
|
||||
ZSTDCOMMON_FILES := $(ZSTDDIR)/common/*.c
|
||||
ZSTDCOMP_FILES := $(ZSTDDIR)/compress/*.c
|
||||
ZSTDDECOMP_FILES := $(ZSTDDIR)/decompress/*.c
|
||||
ZSTD_FILES := $(ZSTDDECOMP_FILES) $(ZSTDCOMMON_FILES) $(ZSTDCOMP_FILES)
|
||||
|
||||
MULTITHREAD_LDFLAGS = -pthread
|
||||
DEBUGFLAGS= -g -DZSTD_DEBUG=1
|
||||
CPPFLAGS += -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress \
|
||||
-I$(ZSTDDIR)/dictBuilder -I$(ZSTDDIR)/deprecated -I$(PRGDIR)
|
||||
CFLAGS ?= -O3
|
||||
CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \
|
||||
-Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement \
|
||||
-Wstrict-prototypes -Wundef -Wformat-security \
|
||||
-Wvla -Wformat=2 -Winit-self -Wfloat-equal -Wwrite-strings \
|
||||
-Wredundant-decls
|
||||
CFLAGS += $(DEBUGFLAGS)
|
||||
CFLAGS += $(MOREFLAGS)
|
||||
FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(MULTITHREAD_LDFLAGS)
|
||||
|
||||
all: adapt datagen
|
||||
|
||||
adapt: $(ZSTD_FILES) adapt.c
|
||||
$(CC) $(FLAGS) $^ -o $@
|
||||
|
||||
adapt-debug: $(ZSTD_FILES) adapt.c
|
||||
$(CC) $(FLAGS) -DDEBUG_MODE=2 $^ -o adapt
|
||||
|
||||
datagen : $(PRGDIR)/datagen.c datagencli.c
|
||||
$(CC) $(FLAGS) $^ -o $@
|
||||
|
||||
test-adapt-correctness: datagen adapt
|
||||
@./test-correctness.sh
|
||||
@echo "test correctness complete"
|
||||
|
||||
test-adapt-performance: datagen adapt
|
||||
@./test-performance.sh
|
||||
@echo "test performance complete"
|
||||
|
||||
clean:
|
||||
@$(RM) -f adapt datagen
|
||||
@$(RM) -rf *.dSYM
|
||||
@$(RM) -f tmp*
|
||||
@$(RM) -f tests/*.zst
|
||||
@$(RM) -f tests/tmp*
|
||||
@echo "finished cleaning"
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# make install is validated only for Linux, OSX, BSD, Hurd and Solaris targets
|
||||
#-----------------------------------------------------------------------------
|
||||
ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU OpenBSD FreeBSD NetBSD DragonFly SunOS))
|
||||
|
||||
ifneq (,$(filter $(shell uname),SunOS))
|
||||
INSTALL ?= ginstall
|
||||
else
|
||||
INSTALL ?= install
|
||||
endif
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
DESTDIR ?=
|
||||
BINDIR ?= $(PREFIX)/bin
|
||||
|
||||
INSTALL_PROGRAM ?= $(INSTALL) -m 755
|
||||
|
||||
install: adapt
|
||||
@echo Installing binaries
|
||||
@$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)/
|
||||
@$(INSTALL_PROGRAM) adapt $(DESTDIR)$(BINDIR)/zstd-adaptive
|
||||
@echo zstd-adaptive installation completed
|
||||
|
||||
uninstall:
|
||||
@$(RM) $(DESTDIR)$(BINDIR)/zstd-adaptive
|
||||
@echo zstd-adaptive programs successfully uninstalled
|
||||
endif
|
||||
@@ -0,0 +1,91 @@
|
||||
### Summary
|
||||
|
||||
`adapt` is a new compression tool targeted at optimizing performance across network connections and pipelines. The tool is aimed at sensing network speeds and adapting compression level based on network or pipe speeds.
|
||||
In situations where the compression level does not appropriately match the network/pipe speed, compression may be bottlenecking the entire pipeline or the files may not be compressed as much as they potentially could be, therefore losing efficiency. It also becomes quite impractical to manually measure and set an optimalcompression level (which could potentially change over time).
|
||||
|
||||
### Using `adapt`
|
||||
|
||||
In order to build and use the tool, you can simply run `make adapt` in the `adaptive-compression` directory under `contrib`. This will generate an executable available for use. Another possible method of installation is running `make install`, which will create and install the binary as the command `zstd-adaptive`.
|
||||
|
||||
Similar to many other compression utilities, `zstd-adaptive` can be invoked by using the following format:
|
||||
|
||||
`zstd-adaptive [options] [file(s)]`
|
||||
|
||||
Supported options for the above format are described below.
|
||||
|
||||
`zstd-adaptive` also supports reading from `stdin` and writing to `stdout`, which is potentially more useful. By default, if no files are given, `zstd-adaptive` reads from and writes to standard I/O. Therefore, you can simply insert it within a pipeline like so:
|
||||
|
||||
`cat FILE | zstd-adaptive | ssh "cat - > tmp.zst"`
|
||||
|
||||
If a file is provided, it is also possible to force writing to stdout using the `-c` flag like so:
|
||||
|
||||
`zstd-adaptive -c FILE | ssh "cat - > tmp.zst"`
|
||||
|
||||
Several options described below can be used to control the behavior of `zstd-adaptive`. More specifically, using the `-l#` and `-u#` flags will will set upper and lower bounds so that the compression level will always be within that range. The `-i#` flag can also be used to change the initial compression level. If an initial compression level is not provided, the initial compression level will be chosen such that it is within the appropriate range (it becomes equal to the lower bound).
|
||||
|
||||
### Options
|
||||
`-oFILE` : write output to `FILE`
|
||||
|
||||
`-i#` : provide initial compression level (must within the appropriate bounds)
|
||||
|
||||
`-h` : display help/information
|
||||
|
||||
`-f` : force the compression level to stay constant
|
||||
|
||||
`-c` : force write to `stdout`
|
||||
|
||||
`-p` : hide progress bar
|
||||
|
||||
`-q` : quiet mode -- do not show progress bar or other information
|
||||
|
||||
`-l#` : set a lower bound on the compression level (default is 1)
|
||||
|
||||
`-u#` : set an upper bound on the compression level (default is 22)
|
||||
### Benchmarking / Test results
|
||||
#### Artificial Tests
|
||||
These artificial tests were run by using the `pv` command line utility in order to limit pipe speeds (25 MB/s read and 5 MB/s write limits were chosen to mimic severe throughput constraints). A 40 GB backup file was sent through a pipeline, compressed, and written out to a file. Compression time, size, and ratio were computed. Data for `zstd -15` was excluded from these tests because the test runs quite long.
|
||||
|
||||
<table>
|
||||
<tr><th> 25 MB/s read limit </th></tr>
|
||||
<tr><td>
|
||||
|
||||
| Compressor Name | Ratio | Compressed Size | Compression Time |
|
||||
|:----------------|------:|----------------:|-----------------:|
|
||||
| zstd -3 | 2.108 | 20.718 GB | 29m 48.530s |
|
||||
| zstd-adaptive | 2.230 | 19.581 GB | 29m 48.798s |
|
||||
|
||||
</td><tr>
|
||||
</table>
|
||||
|
||||
<table>
|
||||
<tr><th> 5 MB/s write limit </th></tr>
|
||||
<tr><td>
|
||||
|
||||
| Compressor Name | Ratio | Compressed Size | Compression Time |
|
||||
|:----------------|------:|----------------:|-----------------:|
|
||||
| zstd -3 | 2.108 | 20.718 GB | 1h 10m 43.076s |
|
||||
| zstd-adaptive | 2.249 | 19.412 GB | 1h 06m 15.577s |
|
||||
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
The commands used for this test generally followed the form:
|
||||
|
||||
`cat FILE | pv -L 25m -q | COMPRESSION | pv -q > tmp.zst # impose 25 MB/s read limit`
|
||||
|
||||
`cat FILE | pv -q | COMPRESSION | pv -L 5m -q > tmp.zst # impose 5 MB/s write limit`
|
||||
|
||||
#### SSH Tests
|
||||
|
||||
The following tests were performed by piping a relatively large backup file (approximately 80 GB) through compression and over SSH to be stored on a server. The test data includes statistics for time and compressed size on `zstd` at several compression levels, as well as `zstd-adaptive`. The data highlights the potential advantages that `zstd-adaptive` has over using a low static compression level and the negative imapcts that using an excessively high static compression level can have on
|
||||
pipe throughput.
|
||||
|
||||
| Compressor Name | Ratio | Compressed Size | Compression Time |
|
||||
|:----------------|------:|----------------:|-----------------:|
|
||||
| zstd -3 | 2.212 | 32.426 GB | 1h 17m 59.756s |
|
||||
| zstd -15 | 2.374 | 30.213 GB | 2h 56m 59.441s |
|
||||
| zstd-adaptive | 2.315 | 30.993 GB | 1h 18m 52.860s |
|
||||
|
||||
The commands used for this test generally followed the form:
|
||||
|
||||
`cat FILE | COMPRESSION | ssh dev "cat - > tmp.zst"`
|
||||
@@ -0,0 +1,1137 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#include <stdio.h> /* fprintf */
|
||||
#include <stdlib.h> /* malloc, free */
|
||||
#include <pthread.h> /* pthread functions */
|
||||
#include <string.h> /* memset */
|
||||
#include "zstd_internal.h"
|
||||
#include "util.h"
|
||||
|
||||
#define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
|
||||
#define PRINT(...) fprintf(stdout, __VA_ARGS__)
|
||||
#define DEBUG(l, ...) { if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } }
|
||||
#define FILE_CHUNK_SIZE 4 << 20
|
||||
#define MAX_NUM_JOBS 2
|
||||
#define stdinmark "/*stdin*\\"
|
||||
#define stdoutmark "/*stdout*\\"
|
||||
#define MAX_PATH 256
|
||||
#define DEFAULT_DISPLAY_LEVEL 1
|
||||
#define DEFAULT_COMPRESSION_LEVEL 6
|
||||
#define MAX_COMPRESSION_LEVEL_CHANGE 2
|
||||
#define CONVERGENCE_LOWER_BOUND 5
|
||||
#define CLEVEL_DECREASE_COOLDOWN 5
|
||||
#define CHANGE_BY_TWO_THRESHOLD 0.1
|
||||
#define CHANGE_BY_ONE_THRESHOLD 0.65
|
||||
|
||||
#ifndef DEBUG_MODE
|
||||
static int g_displayLevel = DEFAULT_DISPLAY_LEVEL;
|
||||
#else
|
||||
static int g_displayLevel = DEBUG_MODE;
|
||||
#endif
|
||||
|
||||
static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL;
|
||||
static UTIL_time_t g_startTime;
|
||||
static size_t g_streamedSize = 0;
|
||||
static unsigned g_useProgressBar = 1;
|
||||
static UTIL_freq_t g_ticksPerSecond;
|
||||
static unsigned g_forceCompressionLevel = 0;
|
||||
static unsigned g_minCLevel = 1;
|
||||
static unsigned g_maxCLevel;
|
||||
|
||||
typedef struct {
|
||||
void* start;
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
} buffer_t;
|
||||
|
||||
typedef struct {
|
||||
size_t filled;
|
||||
buffer_t buffer;
|
||||
} inBuff_t;
|
||||
|
||||
typedef struct {
|
||||
buffer_t src;
|
||||
buffer_t dst;
|
||||
unsigned jobID;
|
||||
unsigned lastJobPlusOne;
|
||||
size_t compressedSize;
|
||||
size_t dictSize;
|
||||
} jobDescription;
|
||||
|
||||
typedef struct {
|
||||
pthread_mutex_t pMutex;
|
||||
int noError;
|
||||
} mutex_t;
|
||||
|
||||
typedef struct {
|
||||
pthread_cond_t pCond;
|
||||
int noError;
|
||||
} cond_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned compressionLevel;
|
||||
unsigned numJobs;
|
||||
unsigned nextJobID;
|
||||
unsigned threadError;
|
||||
|
||||
/*
|
||||
* JobIDs for the next jobs to be created, compressed, and written
|
||||
*/
|
||||
unsigned jobReadyID;
|
||||
unsigned jobCompressedID;
|
||||
unsigned jobWriteID;
|
||||
unsigned allJobsCompleted;
|
||||
|
||||
/*
|
||||
* counter for how many jobs in a row the compression level has not changed
|
||||
* if the counter becomes >= CONVERGENCE_LOWER_BOUND, the next time the
|
||||
* compression level tries to change (by non-zero amount) resets the counter
|
||||
* to 1 and does not apply the change
|
||||
*/
|
||||
unsigned convergenceCounter;
|
||||
|
||||
/*
|
||||
* cooldown counter in order to prevent rapid successive decreases in compression level
|
||||
* whenever compression level is decreased, cooldown is set to CLEVEL_DECREASE_COOLDOWN
|
||||
* whenever adaptCompressionLevel() is called and cooldown != 0, it is decremented
|
||||
* as long as cooldown != 0, the compression level cannot be decreased
|
||||
*/
|
||||
unsigned cooldown;
|
||||
|
||||
/*
|
||||
* XWaitYCompletion
|
||||
* Range from 0.0 to 1.0
|
||||
* if the value is not 1.0, then this implies that thread X waited on thread Y to finish
|
||||
* and thread Y was XWaitYCompletion finished at the time of the wait (i.e. compressWaitWriteCompletion=0.5
|
||||
* implies that the compression thread waited on the write thread and it was only 50% finished writing a job)
|
||||
*/
|
||||
double createWaitCompressionCompletion;
|
||||
double compressWaitCreateCompletion;
|
||||
double compressWaitWriteCompletion;
|
||||
double writeWaitCompressionCompletion;
|
||||
|
||||
/*
|
||||
* Completion values
|
||||
* Range from 0.0 to 1.0
|
||||
* Jobs are divided into mini-chunks in order to measure completion
|
||||
* these values are updated each time a thread finishes its operation on the
|
||||
* mini-chunk (i.e. finishes writing out, compressing, etc. this mini-chunk).
|
||||
*/
|
||||
double compressionCompletion;
|
||||
double writeCompletion;
|
||||
double createCompletion;
|
||||
|
||||
mutex_t jobCompressed_mutex;
|
||||
cond_t jobCompressed_cond;
|
||||
mutex_t jobReady_mutex;
|
||||
cond_t jobReady_cond;
|
||||
mutex_t allJobsCompleted_mutex;
|
||||
cond_t allJobsCompleted_cond;
|
||||
mutex_t jobWrite_mutex;
|
||||
cond_t jobWrite_cond;
|
||||
mutex_t compressionCompletion_mutex;
|
||||
mutex_t createCompletion_mutex;
|
||||
mutex_t writeCompletion_mutex;
|
||||
mutex_t compressionLevel_mutex;
|
||||
size_t lastDictSize;
|
||||
inBuff_t input;
|
||||
jobDescription* jobs;
|
||||
ZSTD_CCtx* cctx;
|
||||
} adaptCCtx;
|
||||
|
||||
typedef struct {
|
||||
adaptCCtx* ctx;
|
||||
FILE* dstFile;
|
||||
} outputThreadArg;
|
||||
|
||||
typedef struct {
|
||||
FILE* srcFile;
|
||||
adaptCCtx* ctx;
|
||||
outputThreadArg* otArg;
|
||||
} fcResources;
|
||||
|
||||
static void freeCompressionJobs(adaptCCtx* ctx)
|
||||
{
|
||||
unsigned u;
|
||||
for (u=0; u<ctx->numJobs; u++) {
|
||||
jobDescription job = ctx->jobs[u];
|
||||
free(job.dst.start);
|
||||
free(job.src.start);
|
||||
}
|
||||
}
|
||||
|
||||
static int destroyMutex(mutex_t* mutex)
|
||||
{
|
||||
if (mutex->noError) {
|
||||
int const ret = pthread_mutex_destroy(&mutex->pMutex);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int destroyCond(cond_t* cond)
|
||||
{
|
||||
if (cond->noError) {
|
||||
int const ret = pthread_cond_destroy(&cond->pCond);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int freeCCtx(adaptCCtx* ctx)
|
||||
{
|
||||
if (!ctx) return 0;
|
||||
{
|
||||
int error = 0;
|
||||
error |= destroyMutex(&ctx->jobCompressed_mutex);
|
||||
error |= destroyCond(&ctx->jobCompressed_cond);
|
||||
error |= destroyMutex(&ctx->jobReady_mutex);
|
||||
error |= destroyCond(&ctx->jobReady_cond);
|
||||
error |= destroyMutex(&ctx->allJobsCompleted_mutex);
|
||||
error |= destroyCond(&ctx->allJobsCompleted_cond);
|
||||
error |= destroyMutex(&ctx->jobWrite_mutex);
|
||||
error |= destroyCond(&ctx->jobWrite_cond);
|
||||
error |= destroyMutex(&ctx->compressionCompletion_mutex);
|
||||
error |= destroyMutex(&ctx->createCompletion_mutex);
|
||||
error |= destroyMutex(&ctx->writeCompletion_mutex);
|
||||
error |= destroyMutex(&ctx->compressionLevel_mutex);
|
||||
error |= ZSTD_isError(ZSTD_freeCCtx(ctx->cctx));
|
||||
free(ctx->input.buffer.start);
|
||||
if (ctx->jobs){
|
||||
freeCompressionJobs(ctx);
|
||||
free(ctx->jobs);
|
||||
}
|
||||
free(ctx);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
static int initMutex(mutex_t* mutex)
|
||||
{
|
||||
int const ret = pthread_mutex_init(&mutex->pMutex, NULL);
|
||||
mutex->noError = !ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int initCond(cond_t* cond)
|
||||
{
|
||||
int const ret = pthread_cond_init(&cond->pCond, NULL);
|
||||
cond->noError = !ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int initCCtx(adaptCCtx* ctx, unsigned numJobs)
|
||||
{
|
||||
ctx->compressionLevel = g_compressionLevel;
|
||||
{
|
||||
int pthreadError = 0;
|
||||
pthreadError |= initMutex(&ctx->jobCompressed_mutex);
|
||||
pthreadError |= initCond(&ctx->jobCompressed_cond);
|
||||
pthreadError |= initMutex(&ctx->jobReady_mutex);
|
||||
pthreadError |= initCond(&ctx->jobReady_cond);
|
||||
pthreadError |= initMutex(&ctx->allJobsCompleted_mutex);
|
||||
pthreadError |= initCond(&ctx->allJobsCompleted_cond);
|
||||
pthreadError |= initMutex(&ctx->jobWrite_mutex);
|
||||
pthreadError |= initCond(&ctx->jobWrite_cond);
|
||||
pthreadError |= initMutex(&ctx->compressionCompletion_mutex);
|
||||
pthreadError |= initMutex(&ctx->createCompletion_mutex);
|
||||
pthreadError |= initMutex(&ctx->writeCompletion_mutex);
|
||||
pthreadError |= initMutex(&ctx->compressionLevel_mutex);
|
||||
if (pthreadError) return pthreadError;
|
||||
}
|
||||
ctx->numJobs = numJobs;
|
||||
ctx->jobReadyID = 0;
|
||||
ctx->jobCompressedID = 0;
|
||||
ctx->jobWriteID = 0;
|
||||
ctx->lastDictSize = 0;
|
||||
|
||||
|
||||
ctx->createWaitCompressionCompletion = 1;
|
||||
ctx->compressWaitCreateCompletion = 1;
|
||||
ctx->compressWaitWriteCompletion = 1;
|
||||
ctx->writeWaitCompressionCompletion = 1;
|
||||
ctx->createCompletion = 1;
|
||||
ctx->writeCompletion = 1;
|
||||
ctx->compressionCompletion = 1;
|
||||
ctx->convergenceCounter = 0;
|
||||
ctx->cooldown = 0;
|
||||
|
||||
ctx->jobs = calloc(1, numJobs*sizeof(jobDescription));
|
||||
|
||||
if (!ctx->jobs) {
|
||||
DISPLAY("Error: could not allocate space for jobs during context creation\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* initializing jobs */
|
||||
{
|
||||
unsigned jobNum;
|
||||
for (jobNum=0; jobNum<numJobs; jobNum++) {
|
||||
jobDescription* job = &ctx->jobs[jobNum];
|
||||
job->src.start = malloc(2 * FILE_CHUNK_SIZE);
|
||||
job->dst.start = malloc(ZSTD_compressBound(FILE_CHUNK_SIZE));
|
||||
job->lastJobPlusOne = 0;
|
||||
if (!job->src.start || !job->dst.start) {
|
||||
DISPLAY("Could not allocate buffers for jobs\n");
|
||||
return 1;
|
||||
}
|
||||
job->src.capacity = FILE_CHUNK_SIZE;
|
||||
job->dst.capacity = ZSTD_compressBound(FILE_CHUNK_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
ctx->nextJobID = 0;
|
||||
ctx->threadError = 0;
|
||||
ctx->allJobsCompleted = 0;
|
||||
|
||||
ctx->cctx = ZSTD_createCCtx();
|
||||
if (!ctx->cctx) {
|
||||
DISPLAY("Error: could not allocate ZSTD_CCtx\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ctx->input.filled = 0;
|
||||
ctx->input.buffer.capacity = 2 * FILE_CHUNK_SIZE;
|
||||
|
||||
ctx->input.buffer.start = malloc(ctx->input.buffer.capacity);
|
||||
if (!ctx->input.buffer.start) {
|
||||
DISPLAY("Error: could not allocate input buffer\n");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static adaptCCtx* createCCtx(unsigned numJobs)
|
||||
{
|
||||
|
||||
adaptCCtx* const ctx = calloc(1, sizeof(adaptCCtx));
|
||||
if (ctx == NULL) {
|
||||
DISPLAY("Error: could not allocate space for context\n");
|
||||
return NULL;
|
||||
}
|
||||
{
|
||||
int const error = initCCtx(ctx, numJobs);
|
||||
if (error) {
|
||||
freeCCtx(ctx);
|
||||
return NULL;
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
}
|
||||
|
||||
static void signalErrorToThreads(adaptCCtx* ctx)
|
||||
{
|
||||
ctx->threadError = 1;
|
||||
pthread_mutex_lock(&ctx->jobReady_mutex.pMutex);
|
||||
pthread_cond_signal(&ctx->jobReady_cond.pCond);
|
||||
pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex);
|
||||
|
||||
pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex);
|
||||
pthread_cond_broadcast(&ctx->jobCompressed_cond.pCond);
|
||||
pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex);
|
||||
|
||||
pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex);
|
||||
pthread_cond_signal(&ctx->jobWrite_cond.pCond);
|
||||
pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex);
|
||||
|
||||
pthread_mutex_lock(&ctx->allJobsCompleted_mutex.pMutex);
|
||||
pthread_cond_signal(&ctx->allJobsCompleted_cond.pCond);
|
||||
pthread_mutex_unlock(&ctx->allJobsCompleted_mutex.pMutex);
|
||||
}
|
||||
|
||||
static void waitUntilAllJobsCompleted(adaptCCtx* ctx)
|
||||
{
|
||||
if (!ctx) return;
|
||||
pthread_mutex_lock(&ctx->allJobsCompleted_mutex.pMutex);
|
||||
while (ctx->allJobsCompleted == 0 && !ctx->threadError) {
|
||||
pthread_cond_wait(&ctx->allJobsCompleted_cond.pCond, &ctx->allJobsCompleted_mutex.pMutex);
|
||||
}
|
||||
pthread_mutex_unlock(&ctx->allJobsCompleted_mutex.pMutex);
|
||||
}
|
||||
|
||||
/* map completion percentages to values for changing compression level */
|
||||
static unsigned convertCompletionToChange(double completion)
|
||||
{
|
||||
if (completion < CHANGE_BY_TWO_THRESHOLD) {
|
||||
return 2;
|
||||
}
|
||||
else if (completion < CHANGE_BY_ONE_THRESHOLD) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Compression level is changed depending on which part of the compression process is lagging
|
||||
* Currently, three theads exist for job creation, compression, and file writing respectively.
|
||||
* adaptCompressionLevel() increments or decrements compression level based on which of the threads is lagging
|
||||
* job creation or file writing lag => increased compression level
|
||||
* compression thread lag => decreased compression level
|
||||
* detecting which thread is lagging is done by keeping track of how many calls each thread makes to pthread_cond_wait
|
||||
*/
|
||||
static void adaptCompressionLevel(adaptCCtx* ctx)
|
||||
{
|
||||
double createWaitCompressionCompletion;
|
||||
double compressWaitCreateCompletion;
|
||||
double compressWaitWriteCompletion;
|
||||
double writeWaitCompressionCompletion;
|
||||
double const threshold = 0.00001;
|
||||
unsigned prevCompressionLevel;
|
||||
|
||||
pthread_mutex_lock(&ctx->compressionLevel_mutex.pMutex);
|
||||
prevCompressionLevel = ctx->compressionLevel;
|
||||
pthread_mutex_unlock(&ctx->compressionLevel_mutex.pMutex);
|
||||
|
||||
|
||||
if (g_forceCompressionLevel) {
|
||||
pthread_mutex_lock(&ctx->compressionLevel_mutex.pMutex);
|
||||
ctx->compressionLevel = g_compressionLevel;
|
||||
pthread_mutex_unlock(&ctx->compressionLevel_mutex.pMutex);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
DEBUG(2, "adapting compression level %u\n", prevCompressionLevel);
|
||||
|
||||
/* read and reset completion measurements */
|
||||
pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex);
|
||||
DEBUG(2, "createWaitCompressionCompletion %f\n", ctx->createWaitCompressionCompletion);
|
||||
DEBUG(2, "writeWaitCompressionCompletion %f\n", ctx->writeWaitCompressionCompletion);
|
||||
createWaitCompressionCompletion = ctx->createWaitCompressionCompletion;
|
||||
writeWaitCompressionCompletion = ctx->writeWaitCompressionCompletion;
|
||||
pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex);
|
||||
|
||||
pthread_mutex_lock(&ctx->writeCompletion_mutex.pMutex);
|
||||
DEBUG(2, "compressWaitWriteCompletion %f\n", ctx->compressWaitWriteCompletion);
|
||||
compressWaitWriteCompletion = ctx->compressWaitWriteCompletion;
|
||||
pthread_mutex_unlock(&ctx->writeCompletion_mutex.pMutex);
|
||||
|
||||
pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex);
|
||||
DEBUG(2, "compressWaitCreateCompletion %f\n", ctx->compressWaitCreateCompletion);
|
||||
compressWaitCreateCompletion = ctx->compressWaitCreateCompletion;
|
||||
pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex);
|
||||
DEBUG(2, "convergence counter: %u\n", ctx->convergenceCounter);
|
||||
|
||||
assert(g_minCLevel <= prevCompressionLevel && g_maxCLevel >= prevCompressionLevel);
|
||||
|
||||
/* adaptation logic */
|
||||
if (ctx->cooldown) ctx->cooldown--;
|
||||
|
||||
if ((1-createWaitCompressionCompletion > threshold || 1-writeWaitCompressionCompletion > threshold) && ctx->cooldown == 0) {
|
||||
/* create or write waiting on compression */
|
||||
/* use whichever one waited less because it was slower */
|
||||
double const completion = MAX(createWaitCompressionCompletion, writeWaitCompressionCompletion);
|
||||
unsigned const change = convertCompletionToChange(completion);
|
||||
unsigned const boundChange = MIN(change, prevCompressionLevel - g_minCLevel);
|
||||
if (ctx->convergenceCounter >= CONVERGENCE_LOWER_BOUND && boundChange != 0) {
|
||||
/* reset convergence counter, might have been a spike */
|
||||
ctx->convergenceCounter = 0;
|
||||
DEBUG(2, "convergence counter reset, no change applied\n");
|
||||
}
|
||||
else if (boundChange != 0) {
|
||||
pthread_mutex_lock(&ctx->compressionLevel_mutex.pMutex);
|
||||
ctx->compressionLevel -= boundChange;
|
||||
pthread_mutex_unlock(&ctx->compressionLevel_mutex.pMutex);
|
||||
ctx->cooldown = CLEVEL_DECREASE_COOLDOWN;
|
||||
ctx->convergenceCounter = 1;
|
||||
|
||||
DEBUG(2, "create or write threads waiting on compression, tried to decrease compression level by %u\n\n", boundChange);
|
||||
}
|
||||
}
|
||||
else if (1-compressWaitWriteCompletion > threshold || 1-compressWaitCreateCompletion > threshold) {
|
||||
/* compress waiting on write */
|
||||
double const completion = MIN(compressWaitWriteCompletion, compressWaitCreateCompletion);
|
||||
unsigned const change = convertCompletionToChange(completion);
|
||||
unsigned const boundChange = MIN(change, g_maxCLevel - prevCompressionLevel);
|
||||
if (ctx->convergenceCounter >= CONVERGENCE_LOWER_BOUND && boundChange != 0) {
|
||||
/* reset convergence counter, might have been a spike */
|
||||
ctx->convergenceCounter = 0;
|
||||
DEBUG(2, "convergence counter reset, no change applied\n");
|
||||
}
|
||||
else if (boundChange != 0) {
|
||||
pthread_mutex_lock(&ctx->compressionLevel_mutex.pMutex);
|
||||
ctx->compressionLevel += boundChange;
|
||||
pthread_mutex_unlock(&ctx->compressionLevel_mutex.pMutex);
|
||||
ctx->cooldown = 0;
|
||||
ctx->convergenceCounter = 1;
|
||||
|
||||
DEBUG(2, "compress waiting on write or create, tried to increase compression level by %u\n\n", boundChange);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&ctx->compressionLevel_mutex.pMutex);
|
||||
if (ctx->compressionLevel == prevCompressionLevel) {
|
||||
ctx->convergenceCounter++;
|
||||
}
|
||||
pthread_mutex_unlock(&ctx->compressionLevel_mutex.pMutex);
|
||||
}
|
||||
|
||||
static size_t getUseableDictSize(unsigned compressionLevel)
|
||||
{
|
||||
ZSTD_parameters const params = ZSTD_getParams(compressionLevel, 0, 0);
|
||||
unsigned const overlapLog = compressionLevel >= (unsigned)ZSTD_maxCLevel() ? 0 : 3;
|
||||
size_t const overlapSize = 1 << (params.cParams.windowLog - overlapLog);
|
||||
return overlapSize;
|
||||
}
|
||||
|
||||
static void* compressionThread(void* arg)
|
||||
{
|
||||
adaptCCtx* const ctx = (adaptCCtx*)arg;
|
||||
unsigned currJob = 0;
|
||||
for ( ; ; ) {
|
||||
unsigned const currJobIndex = currJob % ctx->numJobs;
|
||||
jobDescription* const job = &ctx->jobs[currJobIndex];
|
||||
DEBUG(2, "starting compression for job %u\n", currJob);
|
||||
|
||||
{
|
||||
/* check if compression thread will have to wait */
|
||||
unsigned willWaitForCreate = 0;
|
||||
unsigned willWaitForWrite = 0;
|
||||
|
||||
pthread_mutex_lock(&ctx->jobReady_mutex.pMutex);
|
||||
if (currJob + 1 > ctx->jobReadyID) willWaitForCreate = 1;
|
||||
pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex);
|
||||
|
||||
pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex);
|
||||
if (currJob - ctx->jobWriteID >= ctx->numJobs) willWaitForWrite = 1;
|
||||
pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex);
|
||||
|
||||
|
||||
pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex);
|
||||
if (willWaitForCreate) {
|
||||
DEBUG(2, "compression will wait for create on job %u\n", currJob);
|
||||
ctx->compressWaitCreateCompletion = ctx->createCompletion;
|
||||
DEBUG(2, "create completion %f\n", ctx->compressWaitCreateCompletion);
|
||||
|
||||
}
|
||||
else {
|
||||
ctx->compressWaitCreateCompletion = 1;
|
||||
}
|
||||
pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex);
|
||||
|
||||
pthread_mutex_lock(&ctx->writeCompletion_mutex.pMutex);
|
||||
if (willWaitForWrite) {
|
||||
DEBUG(2, "compression will wait for write on job %u\n", currJob);
|
||||
ctx->compressWaitWriteCompletion = ctx->writeCompletion;
|
||||
DEBUG(2, "write completion %f\n", ctx->compressWaitWriteCompletion);
|
||||
}
|
||||
else {
|
||||
ctx->compressWaitWriteCompletion = 1;
|
||||
}
|
||||
pthread_mutex_unlock(&ctx->writeCompletion_mutex.pMutex);
|
||||
|
||||
}
|
||||
|
||||
/* wait until job is ready */
|
||||
pthread_mutex_lock(&ctx->jobReady_mutex.pMutex);
|
||||
while (currJob + 1 > ctx->jobReadyID && !ctx->threadError) {
|
||||
pthread_cond_wait(&ctx->jobReady_cond.pCond, &ctx->jobReady_mutex.pMutex);
|
||||
}
|
||||
pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex);
|
||||
|
||||
/* wait until job previously in this space is written */
|
||||
pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex);
|
||||
while (currJob - ctx->jobWriteID >= ctx->numJobs && !ctx->threadError) {
|
||||
pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex);
|
||||
}
|
||||
pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex);
|
||||
/* reset compression completion */
|
||||
pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex);
|
||||
ctx->compressionCompletion = 0;
|
||||
pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex);
|
||||
|
||||
/* adapt compression level */
|
||||
if (currJob) adaptCompressionLevel(ctx);
|
||||
|
||||
pthread_mutex_lock(&ctx->compressionLevel_mutex.pMutex);
|
||||
DEBUG(2, "job %u compressed with level %u\n", currJob, ctx->compressionLevel);
|
||||
pthread_mutex_unlock(&ctx->compressionLevel_mutex.pMutex);
|
||||
|
||||
/* compress the data */
|
||||
{
|
||||
size_t const compressionBlockSize = ZSTD_BLOCKSIZE_MAX; /* 128 KB */
|
||||
unsigned cLevel;
|
||||
unsigned blockNum = 0;
|
||||
size_t remaining = job->src.size;
|
||||
size_t srcPos = 0;
|
||||
size_t dstPos = 0;
|
||||
|
||||
pthread_mutex_lock(&ctx->compressionLevel_mutex.pMutex);
|
||||
cLevel = ctx->compressionLevel;
|
||||
pthread_mutex_unlock(&ctx->compressionLevel_mutex.pMutex);
|
||||
|
||||
/* reset compressed size */
|
||||
job->compressedSize = 0;
|
||||
DEBUG(2, "calling ZSTD_compressBegin()\n");
|
||||
/* begin compression */
|
||||
{
|
||||
size_t const useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize);
|
||||
size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1);
|
||||
ZSTD_parameters params = ZSTD_getParams(cLevel, 0, useDictSize);
|
||||
params.cParams.windowLog = 23;
|
||||
{
|
||||
size_t const initError = ZSTD_compressBegin_advanced(ctx->cctx, job->src.start + job->dictSize - useDictSize, useDictSize, params, 0);
|
||||
size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1);
|
||||
if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) {
|
||||
DISPLAY("Error: something went wrong while starting compression\n");
|
||||
signalErrorToThreads(ctx);
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
DEBUG(2, "finished with ZSTD_compressBegin()\n");
|
||||
|
||||
do {
|
||||
size_t const actualBlockSize = MIN(remaining, compressionBlockSize);
|
||||
|
||||
/* continue compression */
|
||||
if (currJob != 0 || blockNum != 0) { /* not first block of first job flush/overwrite the frame header */
|
||||
size_t const hSize = ZSTD_compressContinue(ctx->cctx, job->dst.start + dstPos, job->dst.capacity - dstPos, job->src.start + job->dictSize + srcPos, 0);
|
||||
if (ZSTD_isError(hSize)) {
|
||||
DISPLAY("Error: something went wrong while continuing compression\n");
|
||||
job->compressedSize = hSize;
|
||||
signalErrorToThreads(ctx);
|
||||
return arg;
|
||||
}
|
||||
ZSTD_invalidateRepCodes(ctx->cctx);
|
||||
}
|
||||
{
|
||||
size_t const ret = (job->lastJobPlusOne == currJob + 1 && remaining == actualBlockSize) ?
|
||||
ZSTD_compressEnd (ctx->cctx, job->dst.start + dstPos, job->dst.capacity - dstPos, job->src.start + job->dictSize + srcPos, actualBlockSize) :
|
||||
ZSTD_compressContinue(ctx->cctx, job->dst.start + dstPos, job->dst.capacity - dstPos, job->src.start + job->dictSize + srcPos, actualBlockSize);
|
||||
if (ZSTD_isError(ret)) {
|
||||
DISPLAY("Error: something went wrong during compression: %s\n", ZSTD_getErrorName(ret));
|
||||
signalErrorToThreads(ctx);
|
||||
return arg;
|
||||
}
|
||||
job->compressedSize += ret;
|
||||
remaining -= actualBlockSize;
|
||||
srcPos += actualBlockSize;
|
||||
dstPos += ret;
|
||||
blockNum++;
|
||||
|
||||
/* update completion */
|
||||
pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex);
|
||||
ctx->compressionCompletion = 1 - (double)remaining/job->src.size;
|
||||
pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex);
|
||||
}
|
||||
} while (remaining != 0);
|
||||
job->dst.size = job->compressedSize;
|
||||
}
|
||||
pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex);
|
||||
ctx->jobCompressedID++;
|
||||
pthread_cond_broadcast(&ctx->jobCompressed_cond.pCond);
|
||||
pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex);
|
||||
if (job->lastJobPlusOne == currJob + 1 || ctx->threadError) {
|
||||
/* finished compressing all jobs */
|
||||
break;
|
||||
}
|
||||
DEBUG(2, "finished compressing job %u\n", currJob);
|
||||
currJob++;
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
|
||||
static void displayProgress(unsigned cLevel, unsigned last)
|
||||
{
|
||||
UTIL_time_t currTime;
|
||||
UTIL_getTime(&currTime);
|
||||
if (!g_useProgressBar) return;
|
||||
{
|
||||
double const timeElapsed = (double)(UTIL_getSpanTimeMicro(g_ticksPerSecond, g_startTime, currTime) / 1000.0);
|
||||
double const sizeMB = (double)g_streamedSize / (1 << 20);
|
||||
double const avgCompRate = sizeMB * 1000 / timeElapsed;
|
||||
fprintf(stderr, "\r| Comp. Level: %2u | Time Elapsed: %7.2f s | Data Size: %7.1f MB | Avg Comp. Rate: %6.2f MB/s |", cLevel, timeElapsed/1000.0, sizeMB, avgCompRate);
|
||||
if (last) {
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
else {
|
||||
fflush(stderr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void* outputThread(void* arg)
|
||||
{
|
||||
outputThreadArg* const otArg = (outputThreadArg*)arg;
|
||||
adaptCCtx* const ctx = otArg->ctx;
|
||||
FILE* const dstFile = otArg->dstFile;
|
||||
|
||||
unsigned currJob = 0;
|
||||
for ( ; ; ) {
|
||||
unsigned const currJobIndex = currJob % ctx->numJobs;
|
||||
jobDescription* const job = &ctx->jobs[currJobIndex];
|
||||
unsigned willWaitForCompress = 0;
|
||||
DEBUG(2, "starting write for job %u\n", currJob);
|
||||
|
||||
pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex);
|
||||
if (currJob + 1 > ctx->jobCompressedID) willWaitForCompress = 1;
|
||||
pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex);
|
||||
|
||||
|
||||
pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex);
|
||||
if (willWaitForCompress) {
|
||||
/* write thread is waiting on compression thread */
|
||||
ctx->writeWaitCompressionCompletion = ctx->compressionCompletion;
|
||||
DEBUG(2, "writer thread waiting for nextJob: %u, writeWaitCompressionCompletion %f\n", currJob, ctx->writeWaitCompressionCompletion);
|
||||
}
|
||||
else {
|
||||
ctx->writeWaitCompressionCompletion = 1;
|
||||
}
|
||||
pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex);
|
||||
|
||||
pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex);
|
||||
while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) {
|
||||
pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex);
|
||||
}
|
||||
pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex);
|
||||
|
||||
/* reset write completion */
|
||||
pthread_mutex_lock(&ctx->writeCompletion_mutex.pMutex);
|
||||
ctx->writeCompletion = 0;
|
||||
pthread_mutex_unlock(&ctx->writeCompletion_mutex.pMutex);
|
||||
|
||||
{
|
||||
size_t const compressedSize = job->compressedSize;
|
||||
size_t remaining = compressedSize;
|
||||
if (ZSTD_isError(compressedSize)) {
|
||||
DISPLAY("Error: an error occurred during compression\n");
|
||||
signalErrorToThreads(ctx);
|
||||
return arg;
|
||||
}
|
||||
{
|
||||
size_t const blockSize = MAX(compressedSize >> 7, 1 << 10);
|
||||
size_t pos = 0;
|
||||
for ( ; ; ) {
|
||||
size_t const writeSize = MIN(remaining, blockSize);
|
||||
size_t const ret = fwrite(job->dst.start + pos, 1, writeSize, dstFile);
|
||||
if (ret != writeSize) break;
|
||||
pos += ret;
|
||||
remaining -= ret;
|
||||
|
||||
/* update completion variable for writing */
|
||||
pthread_mutex_lock(&ctx->writeCompletion_mutex.pMutex);
|
||||
ctx->writeCompletion = 1 - (double)remaining/compressedSize;
|
||||
pthread_mutex_unlock(&ctx->writeCompletion_mutex.pMutex);
|
||||
|
||||
if (remaining == 0) break;
|
||||
}
|
||||
if (pos != compressedSize) {
|
||||
DISPLAY("Error: an error occurred during file write operation\n");
|
||||
signalErrorToThreads(ctx);
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
unsigned cLevel;
|
||||
pthread_mutex_lock(&ctx->compressionLevel_mutex.pMutex);
|
||||
cLevel = ctx->compressionLevel;
|
||||
pthread_mutex_unlock(&ctx->compressionLevel_mutex.pMutex);
|
||||
displayProgress(cLevel, job->lastJobPlusOne == currJob + 1);
|
||||
}
|
||||
pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex);
|
||||
ctx->jobWriteID++;
|
||||
pthread_cond_signal(&ctx->jobWrite_cond.pCond);
|
||||
pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex);
|
||||
|
||||
if (job->lastJobPlusOne == currJob + 1 || ctx->threadError) {
|
||||
/* finished with all jobs */
|
||||
pthread_mutex_lock(&ctx->allJobsCompleted_mutex.pMutex);
|
||||
ctx->allJobsCompleted = 1;
|
||||
pthread_cond_signal(&ctx->allJobsCompleted_cond.pCond);
|
||||
pthread_mutex_unlock(&ctx->allJobsCompleted_mutex.pMutex);
|
||||
break;
|
||||
}
|
||||
DEBUG(2, "finished writing job %u\n", currJob);
|
||||
currJob++;
|
||||
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
|
||||
static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last)
|
||||
{
|
||||
unsigned const nextJob = ctx->nextJobID;
|
||||
unsigned const nextJobIndex = nextJob % ctx->numJobs;
|
||||
jobDescription* const job = &ctx->jobs[nextJobIndex];
|
||||
|
||||
|
||||
job->src.size = srcSize;
|
||||
job->jobID = nextJob;
|
||||
if (last) job->lastJobPlusOne = nextJob + 1;
|
||||
{
|
||||
/* swap buffer */
|
||||
void* const copy = job->src.start;
|
||||
job->src.start = ctx->input.buffer.start;
|
||||
ctx->input.buffer.start = copy;
|
||||
}
|
||||
job->dictSize = ctx->lastDictSize;
|
||||
|
||||
ctx->nextJobID++;
|
||||
/* if not on the last job, reuse data as dictionary in next job */
|
||||
if (!last) {
|
||||
size_t const oldDictSize = ctx->lastDictSize;
|
||||
memcpy(ctx->input.buffer.start, job->src.start + oldDictSize, srcSize);
|
||||
ctx->lastDictSize = srcSize;
|
||||
ctx->input.filled = srcSize;
|
||||
}
|
||||
|
||||
/* signal job ready */
|
||||
pthread_mutex_lock(&ctx->jobReady_mutex.pMutex);
|
||||
ctx->jobReadyID++;
|
||||
pthread_cond_signal(&ctx->jobReady_cond.pCond);
|
||||
pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadArg* otArg)
|
||||
{
|
||||
/* early error check to exit */
|
||||
if (!ctx || !srcFile || !otArg) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* create output thread */
|
||||
{
|
||||
pthread_t out;
|
||||
if (pthread_create(&out, NULL, &outputThread, otArg)) {
|
||||
DISPLAY("Error: could not create output thread\n");
|
||||
signalErrorToThreads(ctx);
|
||||
return 1;
|
||||
}
|
||||
else if (pthread_detach(out)) {
|
||||
DISPLAY("Error: could not detach output thread\n");
|
||||
signalErrorToThreads(ctx);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* create compression thread */
|
||||
{
|
||||
pthread_t compression;
|
||||
if (pthread_create(&compression, NULL, &compressionThread, ctx)) {
|
||||
DISPLAY("Error: could not create compression thread\n");
|
||||
signalErrorToThreads(ctx);
|
||||
return 1;
|
||||
}
|
||||
else if (pthread_detach(compression)) {
|
||||
DISPLAY("Error: could not detach compression thread\n");
|
||||
signalErrorToThreads(ctx);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
{
|
||||
unsigned currJob = 0;
|
||||
/* creating jobs */
|
||||
for ( ; ; ) {
|
||||
size_t pos = 0;
|
||||
size_t const readBlockSize = 1 << 15;
|
||||
size_t remaining = FILE_CHUNK_SIZE;
|
||||
unsigned const nextJob = ctx->nextJobID;
|
||||
unsigned willWaitForCompress = 0;
|
||||
DEBUG(2, "starting creation of job %u\n", currJob);
|
||||
|
||||
pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex);
|
||||
if (nextJob - ctx->jobCompressedID >= ctx->numJobs) willWaitForCompress = 1;
|
||||
pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex);
|
||||
|
||||
pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex);
|
||||
if (willWaitForCompress) {
|
||||
/* creation thread is waiting, take measurement of completion */
|
||||
ctx->createWaitCompressionCompletion = ctx->compressionCompletion;
|
||||
DEBUG(2, "create thread waiting for nextJob: %u, createWaitCompressionCompletion %f\n", nextJob, ctx->createWaitCompressionCompletion);
|
||||
}
|
||||
else {
|
||||
ctx->createWaitCompressionCompletion = 1;
|
||||
}
|
||||
pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex);
|
||||
|
||||
/* wait until the job has been compressed */
|
||||
pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex);
|
||||
while (nextJob - ctx->jobCompressedID >= ctx->numJobs && !ctx->threadError) {
|
||||
pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex);
|
||||
}
|
||||
pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex);
|
||||
|
||||
/* reset create completion */
|
||||
pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex);
|
||||
ctx->createCompletion = 0;
|
||||
pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex);
|
||||
|
||||
while (remaining != 0 && !feof(srcFile)) {
|
||||
size_t const ret = fread(ctx->input.buffer.start + ctx->input.filled + pos, 1, readBlockSize, srcFile);
|
||||
if (ret != readBlockSize && !feof(srcFile)) {
|
||||
/* error could not read correct number of bytes */
|
||||
DISPLAY("Error: problem occurred during read from src file\n");
|
||||
signalErrorToThreads(ctx);
|
||||
return 1;
|
||||
}
|
||||
pos += ret;
|
||||
remaining -= ret;
|
||||
pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex);
|
||||
ctx->createCompletion = 1 - (double)remaining/((size_t)FILE_CHUNK_SIZE);
|
||||
pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex);
|
||||
}
|
||||
if (remaining != 0 && !feof(srcFile)) {
|
||||
DISPLAY("Error: problem occurred during read from src file\n");
|
||||
signalErrorToThreads(ctx);
|
||||
return 1;
|
||||
}
|
||||
g_streamedSize += pos;
|
||||
/* reading was fine, now create the compression job */
|
||||
{
|
||||
int const last = feof(srcFile);
|
||||
int const error = createCompressionJob(ctx, pos, last);
|
||||
if (error != 0) {
|
||||
signalErrorToThreads(ctx);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
DEBUG(2, "finished creating job %u\n", currJob);
|
||||
currJob++;
|
||||
if (feof(srcFile)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* success -- created all jobs */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static fcResources createFileCompressionResources(const char* const srcFilename, const char* const dstFilenameOrNull)
|
||||
{
|
||||
fcResources fcr;
|
||||
unsigned const stdinUsed = !strcmp(srcFilename, stdinmark);
|
||||
FILE* const srcFile = stdinUsed ? stdin : fopen(srcFilename, "rb");
|
||||
const char* const outFilenameIntermediate = (stdinUsed && !dstFilenameOrNull) ? stdoutmark : dstFilenameOrNull;
|
||||
const char* outFilename = outFilenameIntermediate;
|
||||
char fileAndSuffix[MAX_PATH];
|
||||
size_t const numJobs = MAX_NUM_JOBS;
|
||||
|
||||
memset(&fcr, 0, sizeof(fcr));
|
||||
|
||||
if (!outFilenameIntermediate) {
|
||||
if (snprintf(fileAndSuffix, MAX_PATH, "%s.zst", srcFilename) + 1 > MAX_PATH) {
|
||||
DISPLAY("Error: output filename is too long\n");
|
||||
return fcr;
|
||||
}
|
||||
outFilename = fileAndSuffix;
|
||||
}
|
||||
|
||||
{
|
||||
unsigned const stdoutUsed = !strcmp(outFilename, stdoutmark);
|
||||
FILE* const dstFile = stdoutUsed ? stdout : fopen(outFilename, "wb");
|
||||
fcr.otArg = malloc(sizeof(outputThreadArg));
|
||||
if (!fcr.otArg) {
|
||||
DISPLAY("Error: could not allocate space for output thread argument\n");
|
||||
return fcr;
|
||||
}
|
||||
fcr.otArg->dstFile = dstFile;
|
||||
}
|
||||
/* checking for errors */
|
||||
if (!fcr.otArg->dstFile || !srcFile) {
|
||||
DISPLAY("Error: some file(s) could not be opened\n");
|
||||
return fcr;
|
||||
}
|
||||
|
||||
/* creating context */
|
||||
fcr.ctx = createCCtx(numJobs);
|
||||
fcr.otArg->ctx = fcr.ctx;
|
||||
fcr.srcFile = srcFile;
|
||||
return fcr;
|
||||
}
|
||||
|
||||
static int freeFileCompressionResources(fcResources* fcr)
|
||||
{
|
||||
int ret = 0;
|
||||
waitUntilAllJobsCompleted(fcr->ctx);
|
||||
ret |= (fcr->srcFile != NULL) ? fclose(fcr->srcFile) : 0;
|
||||
ret |= (fcr->ctx != NULL) ? freeCCtx(fcr->ctx) : 0;
|
||||
if (fcr->otArg) {
|
||||
ret |= (fcr->otArg->dstFile != stdout) ? fclose(fcr->otArg->dstFile) : 0;
|
||||
free(fcr->otArg);
|
||||
/* no need to freeCCtx() on otArg->ctx because it should be the same context */
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int compressFilename(const char* const srcFilename, const char* const dstFilenameOrNull)
|
||||
{
|
||||
int ret = 0;
|
||||
fcResources fcr = createFileCompressionResources(srcFilename, dstFilenameOrNull);
|
||||
UTIL_getTime(&g_startTime);
|
||||
g_streamedSize = 0;
|
||||
ret |= performCompression(fcr.ctx, fcr.srcFile, fcr.otArg);
|
||||
ret |= freeFileCompressionResources(&fcr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int compressFilenames(const char** filenameTable, unsigned numFiles, unsigned forceStdout)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned fileNum;
|
||||
for (fileNum=0; fileNum<numFiles; fileNum++) {
|
||||
const char* filename = filenameTable[fileNum];
|
||||
if (!forceStdout) {
|
||||
ret |= compressFilename(filename, NULL);
|
||||
}
|
||||
else {
|
||||
ret |= compressFilename(filename, stdoutmark);
|
||||
}
|
||||
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*! readU32FromChar() :
|
||||
@return : unsigned integer value read from input in `char` format
|
||||
allows and interprets K, KB, KiB, M, MB and MiB suffix.
|
||||
Will also modify `*stringPtr`, advancing it to position where it stopped reading.
|
||||
Note : function result can overflow if digit string > MAX_UINT */
|
||||
static unsigned readU32FromChar(const char** stringPtr)
|
||||
{
|
||||
unsigned result = 0;
|
||||
while ((**stringPtr >='0') && (**stringPtr <='9'))
|
||||
result *= 10, result += **stringPtr - '0', (*stringPtr)++ ;
|
||||
if ((**stringPtr=='K') || (**stringPtr=='M')) {
|
||||
result <<= 10;
|
||||
if (**stringPtr=='M') result <<= 10;
|
||||
(*stringPtr)++ ;
|
||||
if (**stringPtr=='i') (*stringPtr)++;
|
||||
if (**stringPtr=='B') (*stringPtr)++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void help(const char* progPath)
|
||||
{
|
||||
PRINT("Usage:\n");
|
||||
PRINT(" %s [options] [file(s)]\n", progPath);
|
||||
PRINT("\n");
|
||||
PRINT("Options:\n");
|
||||
PRINT(" -oFILE : specify the output file name\n");
|
||||
PRINT(" -i# : provide initial compression level -- default %d, must be in the range [L, U] where L and U are bound values (see below for defaults)\n", DEFAULT_COMPRESSION_LEVEL);
|
||||
PRINT(" -h : display help/information\n");
|
||||
PRINT(" -f : force the compression level to stay constant\n");
|
||||
PRINT(" -c : force write to stdout\n");
|
||||
PRINT(" -p : hide progress bar\n");
|
||||
PRINT(" -q : quiet mode -- do not show progress bar or other information\n");
|
||||
PRINT(" -l# : provide lower bound for compression level -- default 1\n");
|
||||
PRINT(" -u# : provide upper bound for compression level -- default %u\n", ZSTD_maxCLevel());
|
||||
}
|
||||
/* return 0 if successful, else return error */
|
||||
int main(int argCount, const char* argv[])
|
||||
{
|
||||
const char* outFilename = NULL;
|
||||
const char** filenameTable = (const char**)malloc(argCount*sizeof(const char*));
|
||||
unsigned filenameIdx = 0;
|
||||
unsigned forceStdout = 0;
|
||||
unsigned providedInitialCLevel = 0;
|
||||
int ret = 0;
|
||||
int argNum;
|
||||
filenameTable[0] = stdinmark;
|
||||
g_maxCLevel = ZSTD_maxCLevel();
|
||||
|
||||
UTIL_initTimer(&g_ticksPerSecond);
|
||||
|
||||
if (filenameTable == NULL) {
|
||||
DISPLAY("Error: could not allocate sapce for filename table.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (argNum=1; argNum<argCount; argNum++) {
|
||||
const char* argument = argv[argNum];
|
||||
|
||||
/* output filename designated with "-o" */
|
||||
if (argument[0]=='-' && strlen(argument) > 1) {
|
||||
switch (argument[1]) {
|
||||
case 'o':
|
||||
argument += 2;
|
||||
outFilename = argument;
|
||||
break;
|
||||
case 'i':
|
||||
argument += 2;
|
||||
g_compressionLevel = readU32FromChar(&argument);
|
||||
providedInitialCLevel = 1;
|
||||
break;
|
||||
case 'h':
|
||||
help(argv[0]);
|
||||
goto _main_exit;
|
||||
case 'p':
|
||||
g_useProgressBar = 0;
|
||||
break;
|
||||
case 'c':
|
||||
forceStdout = 1;
|
||||
outFilename = stdoutmark;
|
||||
break;
|
||||
case 'f':
|
||||
g_forceCompressionLevel = 1;
|
||||
break;
|
||||
case 'q':
|
||||
g_useProgressBar = 0;
|
||||
g_displayLevel = 0;
|
||||
break;
|
||||
case 'l':
|
||||
argument += 2;
|
||||
g_minCLevel = readU32FromChar(&argument);
|
||||
break;
|
||||
case 'u':
|
||||
argument += 2;
|
||||
g_maxCLevel = readU32FromChar(&argument);
|
||||
break;
|
||||
default:
|
||||
DISPLAY("Error: invalid argument provided\n");
|
||||
ret = 1;
|
||||
goto _main_exit;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* regular files to be compressed */
|
||||
filenameTable[filenameIdx++] = argument;
|
||||
}
|
||||
|
||||
/* check initial, max, and min compression levels */
|
||||
{
|
||||
unsigned const minMaxInconsistent = g_minCLevel > g_maxCLevel;
|
||||
unsigned const initialNotInRange = g_minCLevel > g_compressionLevel || g_maxCLevel < g_compressionLevel;
|
||||
if (minMaxInconsistent || (initialNotInRange && providedInitialCLevel)) {
|
||||
DISPLAY("Error: provided compression level parameters are invalid\n");
|
||||
ret = 1;
|
||||
goto _main_exit;
|
||||
}
|
||||
else if (initialNotInRange) {
|
||||
g_compressionLevel = g_minCLevel;
|
||||
}
|
||||
}
|
||||
|
||||
/* error checking with number of files */
|
||||
if (filenameIdx > 1 && (outFilename != NULL && strcmp(outFilename, stdoutmark))) {
|
||||
DISPLAY("Error: multiple input files provided, cannot use specified output file\n");
|
||||
ret = 1;
|
||||
goto _main_exit;
|
||||
}
|
||||
|
||||
/* compress files */
|
||||
if (filenameIdx <= 1) {
|
||||
ret |= compressFilename(filenameTable[0], outFilename);
|
||||
}
|
||||
else {
|
||||
ret |= compressFilenames(filenameTable, filenameIdx, forceStdout);
|
||||
}
|
||||
_main_exit:
|
||||
free(filenameTable);
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
|
||||
/*-************************************
|
||||
* Dependencies
|
||||
**************************************/
|
||||
#include "util.h" /* Compiler options */
|
||||
#include <stdio.h> /* fprintf, stderr */
|
||||
#include "datagen.h" /* RDG_generate */
|
||||
|
||||
|
||||
/*-************************************
|
||||
* Constants
|
||||
**************************************/
|
||||
#define KB *(1 <<10)
|
||||
#define MB *(1 <<20)
|
||||
#define GB *(1U<<30)
|
||||
|
||||
#define SIZE_DEFAULT ((64 KB) + 1)
|
||||
#define SEED_DEFAULT 0
|
||||
#define COMPRESSIBILITY_DEFAULT 50
|
||||
|
||||
|
||||
/*-************************************
|
||||
* Macros
|
||||
**************************************/
|
||||
#define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
|
||||
#define DISPLAYLEVEL(l, ...) if (displayLevel>=l) { DISPLAY(__VA_ARGS__); }
|
||||
static unsigned displayLevel = 2;
|
||||
|
||||
|
||||
/*-*******************************************************
|
||||
* Command line
|
||||
*********************************************************/
|
||||
static int usage(const char* programName)
|
||||
{
|
||||
DISPLAY( "Compressible data generator\n");
|
||||
DISPLAY( "Usage :\n");
|
||||
DISPLAY( " %s [args]\n", programName);
|
||||
DISPLAY( "\n");
|
||||
DISPLAY( "Arguments :\n");
|
||||
DISPLAY( " -g# : generate # data (default:%i)\n", SIZE_DEFAULT);
|
||||
DISPLAY( " -s# : Select seed (default:%i)\n", SEED_DEFAULT);
|
||||
DISPLAY( " -P# : Select compressibility in %% (default:%i%%)\n",
|
||||
COMPRESSIBILITY_DEFAULT);
|
||||
DISPLAY( " -h : display help and exit\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, const char** argv)
|
||||
{
|
||||
unsigned probaU32 = COMPRESSIBILITY_DEFAULT;
|
||||
double litProba = 0.0;
|
||||
U64 size = SIZE_DEFAULT;
|
||||
U32 seed = SEED_DEFAULT;
|
||||
const char* const programName = argv[0];
|
||||
|
||||
int argNb;
|
||||
for(argNb=1; argNb<argc; argNb++) {
|
||||
const char* argument = argv[argNb];
|
||||
|
||||
if(!argument) continue; /* Protection if argument empty */
|
||||
|
||||
/* Handle commands. Aggregated commands are allowed */
|
||||
if (*argument=='-') {
|
||||
argument++;
|
||||
while (*argument!=0) {
|
||||
switch(*argument)
|
||||
{
|
||||
case 'h':
|
||||
return usage(programName);
|
||||
case 'g':
|
||||
argument++;
|
||||
size=0;
|
||||
while ((*argument>='0') && (*argument<='9'))
|
||||
size *= 10, size += *argument++ - '0';
|
||||
if (*argument=='K') { size <<= 10; argument++; }
|
||||
if (*argument=='M') { size <<= 20; argument++; }
|
||||
if (*argument=='G') { size <<= 30; argument++; }
|
||||
if (*argument=='B') { argument++; }
|
||||
break;
|
||||
case 's':
|
||||
argument++;
|
||||
seed=0;
|
||||
while ((*argument>='0') && (*argument<='9'))
|
||||
seed *= 10, seed += *argument++ - '0';
|
||||
break;
|
||||
case 'P':
|
||||
argument++;
|
||||
probaU32 = 0;
|
||||
while ((*argument>='0') && (*argument<='9'))
|
||||
probaU32 *= 10, probaU32 += *argument++ - '0';
|
||||
if (probaU32>100) probaU32 = 100;
|
||||
break;
|
||||
case 'L': /* hidden argument : Literal distribution probability */
|
||||
argument++;
|
||||
litProba=0.;
|
||||
while ((*argument>='0') && (*argument<='9'))
|
||||
litProba *= 10, litProba += *argument++ - '0';
|
||||
if (litProba>100.) litProba=100.;
|
||||
litProba /= 100.;
|
||||
break;
|
||||
case 'v':
|
||||
displayLevel = 4;
|
||||
argument++;
|
||||
break;
|
||||
default:
|
||||
return usage(programName);
|
||||
}
|
||||
} } } /* for(argNb=1; argNb<argc; argNb++) */
|
||||
|
||||
DISPLAYLEVEL(4, "Compressible data Generator \n");
|
||||
if (probaU32!=COMPRESSIBILITY_DEFAULT)
|
||||
DISPLAYLEVEL(3, "Compressibility : %i%%\n", probaU32);
|
||||
DISPLAYLEVEL(3, "Seed = %u \n", seed);
|
||||
|
||||
RDG_genStdout(size, (double)probaU32/100, litProba, seed);
|
||||
DISPLAYLEVEL(1, "\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
+252
@@ -0,0 +1,252 @@
|
||||
echo "correctness tests -- general"
|
||||
./datagen -s1 -g1GB > tmp
|
||||
./adapt -otmp.zst tmp
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s2 -g500MB > tmp
|
||||
./adapt -otmp.zst tmp
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s3 -g250MB > tmp
|
||||
./adapt -otmp.zst tmp
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s4 -g125MB > tmp
|
||||
./adapt -otmp.zst tmp
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s5 -g50MB > tmp
|
||||
./adapt -otmp.zst tmp
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s6 -g25MB > tmp
|
||||
./adapt -otmp.zst tmp
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s7 -g10MB > tmp
|
||||
./adapt -otmp.zst tmp
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s8 -g5MB > tmp
|
||||
./adapt -otmp.zst tmp
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s9 -g500KB > tmp
|
||||
./adapt -otmp.zst tmp
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
echo -e "\ncorrectness tests -- streaming"
|
||||
./datagen -s10 -g1GB > tmp
|
||||
cat tmp | ./adapt > tmp.zst
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s11 -g100MB > tmp
|
||||
cat tmp | ./adapt > tmp.zst
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s12 -g10MB > tmp
|
||||
cat tmp | ./adapt > tmp.zst
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s13 -g1MB > tmp
|
||||
cat tmp | ./adapt > tmp.zst
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s14 -g100KB > tmp
|
||||
cat tmp | ./adapt > tmp.zst
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s15 -g10KB > tmp
|
||||
cat tmp | ./adapt > tmp.zst
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
echo -e "\ncorrectness tests -- read limit"
|
||||
./datagen -s16 -g1GB > tmp
|
||||
pv -L 50m -q tmp | ./adapt > tmp.zst
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s17 -g100MB > tmp
|
||||
pv -L 50m -q tmp | ./adapt > tmp.zst
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s18 -g10MB > tmp
|
||||
pv -L 50m -q tmp | ./adapt > tmp.zst
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s19 -g1MB > tmp
|
||||
pv -L 50m -q tmp | ./adapt > tmp.zst
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s20 -g100KB > tmp
|
||||
pv -L 50m -q tmp | ./adapt > tmp.zst
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s21 -g10KB > tmp
|
||||
pv -L 50m -q tmp | ./adapt > tmp.zst
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
echo -e "\ncorrectness tests -- write limit"
|
||||
./datagen -s22 -g1GB > tmp
|
||||
pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s23 -g100MB > tmp
|
||||
pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s24 -g10MB > tmp
|
||||
pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s25 -g1MB > tmp
|
||||
pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s26 -g100KB > tmp
|
||||
pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s27 -g10KB > tmp
|
||||
pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
echo -e "\ncorrectness tests -- read and write limits"
|
||||
./datagen -s28 -g1GB > tmp
|
||||
pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s29 -g100MB > tmp
|
||||
pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s30 -g10MB > tmp
|
||||
pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s31 -g1MB > tmp
|
||||
pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s32 -g100KB > tmp
|
||||
pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s33 -g10KB > tmp
|
||||
pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
echo -e "\ncorrectness tests -- forced compression level"
|
||||
./datagen -s34 -g1GB > tmp
|
||||
./adapt tmp -otmp.zst -i11 -f
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s35 -g100MB > tmp
|
||||
./adapt tmp -otmp.zst -i11 -f
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s36 -g10MB > tmp
|
||||
./adapt tmp -otmp.zst -i11 -f
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s37 -g1MB > tmp
|
||||
./adapt tmp -otmp.zst -i11 -f
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s38 -g100KB > tmp
|
||||
./adapt tmp -otmp.zst -i11 -f
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
./datagen -s39 -g10KB > tmp
|
||||
./adapt tmp -otmp.zst -i11 -f
|
||||
zstd -d tmp.zst -o tmp2
|
||||
diff -s -q tmp tmp2
|
||||
rm tmp*
|
||||
|
||||
echo -e "\ncorrectness tests -- window size test"
|
||||
./datagen -s39 -g1GB | pv -L 25m -q | ./adapt -i1 | pv -q > tmp.zst
|
||||
zstd -d tmp.zst
|
||||
rm tmp*
|
||||
|
||||
echo -e "\ncorrectness tests -- testing bounds"
|
||||
./datagen -s40 -g1GB | pv -L 25m -q | ./adapt -i1 -u4 | pv -q > tmp.zst
|
||||
rm tmp*
|
||||
|
||||
./datagen -s41 -g1GB | ./adapt -i14 -l4 > tmp.zst
|
||||
rm tmp*
|
||||
make clean
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
echo "testing time -- no limits set"
|
||||
./datagen -s1 -g1GB > tmp
|
||||
time ./adapt -otmp1.zst tmp
|
||||
time zstd -1 -o tmp2.zst tmp
|
||||
rm tmp*
|
||||
|
||||
./datagen -s2 -g2GB > tmp
|
||||
time ./adapt -otmp1.zst tmp
|
||||
time zstd -1 -o tmp2.zst tmp
|
||||
rm tmp*
|
||||
|
||||
./datagen -s3 -g4GB > tmp
|
||||
time ./adapt -otmp1.zst tmp
|
||||
time zstd -1 -o tmp2.zst tmp
|
||||
rm tmp*
|
||||
|
||||
echo -e "\ntesting compression ratio -- no limits set"
|
||||
./datagen -s4 -g1GB > tmp
|
||||
time ./adapt -otmp1.zst tmp
|
||||
time zstd -1 -o tmp2.zst tmp
|
||||
ls -l tmp1.zst tmp2.zst
|
||||
rm tmp*
|
||||
|
||||
./datagen -s5 -g2GB > tmp
|
||||
time ./adapt -otmp1.zst tmp
|
||||
time zstd -1 -o tmp2.zst tmp
|
||||
ls -l tmp1.zst tmp2.zst
|
||||
rm tmp*
|
||||
|
||||
./datagen -s6 -g4GB > tmp
|
||||
time ./adapt -otmp1.zst tmp
|
||||
time zstd -1 -o tmp2.zst tmp
|
||||
ls -l tmp1.zst tmp2.zst
|
||||
rm tmp*
|
||||
|
||||
echo e "\ntesting performance at various compression levels -- no limits set"
|
||||
./datagen -s7 -g1GB > tmp
|
||||
echo "adapt"
|
||||
time ./adapt -i5 -f tmp -otmp1.zst
|
||||
echo "zstdcli"
|
||||
time zstd -5 tmp -o tmp2.zst
|
||||
ls -l tmp1.zst tmp2.zst
|
||||
rm tmp*
|
||||
|
||||
./datagen -s8 -g1GB > tmp
|
||||
echo "adapt"
|
||||
time ./adapt -i10 -f tmp -otmp1.zst
|
||||
echo "zstdcli"
|
||||
time zstd -10 tmp -o tmp2.zst
|
||||
ls -l tmp1.zst tmp2.zst
|
||||
rm tmp*
|
||||
|
||||
./datagen -s9 -g1GB > tmp
|
||||
echo "adapt"
|
||||
time ./adapt -i15 -f tmp -otmp1.zst
|
||||
echo "zstdcli"
|
||||
time zstd -15 tmp -o tmp2.zst
|
||||
ls -l tmp1.zst tmp2.zst
|
||||
rm tmp*
|
||||
@@ -1,7 +1,7 @@
|
||||
From 8bc9a0ae5c86a6d02d9a5274b9965ddac0e8d330 Mon Sep 17 00:00:00 2001
|
||||
From 308795a7713ca6fcd468b60fba9a2fca99cee6a0 Mon Sep 17 00:00:00 2001
|
||||
From: Nick Terrell <terrelln@fb.com>
|
||||
Date: Wed, 28 Jun 2017 22:00:00 -0700
|
||||
Subject: [PATCH v2 0/4] Add xxhash and zstd modules
|
||||
Date: Tue, 8 Aug 2017 19:20:25 -0700
|
||||
Subject: [PATCH v5 0/5] Add xxhash and zstd modules
|
||||
|
||||
Hi all,
|
||||
|
||||
@@ -16,20 +16,45 @@ Nick Terrell
|
||||
Changelog:
|
||||
|
||||
v1 -> v2:
|
||||
- Make pointer in lib/xxhash.c:394 non-const (1/4)
|
||||
- Use div_u64() for division of u64s (2/4)
|
||||
- Make pointer in lib/xxhash.c:394 non-const (1/5)
|
||||
- Use div_u64() for division of u64s (2/5)
|
||||
- Reduce stack usage of ZSTD_compressSequences(), ZSTD_buildSeqTable(),
|
||||
ZSTD_decompressSequencesLong(), FSE_buildDTable(), FSE_decompress_wksp(),
|
||||
HUF_writeCTable(), HUF_readStats(), HUF_readCTable(),
|
||||
HUF_compressWeights(), HUF_readDTableX2(), and HUF_readDTableX4() (2/4)
|
||||
- No zstd function uses more than 400 B of stack space (2/4)
|
||||
HUF_compressWeights(), HUF_readDTableX2(), and HUF_readDTableX4() (2/5)
|
||||
- No zstd function uses more than 400 B of stack space (2/5)
|
||||
|
||||
Nick Terrell (4):
|
||||
v2 -> v3:
|
||||
- Work around gcc-7 bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81388
|
||||
(2/5)
|
||||
- Fix bug in dictionary compression from upstream commit cc1522351f (2/5)
|
||||
- Port upstream BtrFS commits e1ddce71d6, 389a6cfc2a, and 6acafd1eff (3/5)
|
||||
- Change default compression level for BtrFS to 3 (3/5)
|
||||
|
||||
v3 -> v4:
|
||||
- Fix compiler warnings (2/5)
|
||||
- Add missing includes (3/5)
|
||||
- Fix minor linter warnings (3/5, 4/5)
|
||||
- Add crypto patch (5/5)
|
||||
|
||||
v4 -> v5:
|
||||
- Fix rare compression bug from upstream commit 308047eb5d (2/5)
|
||||
- Fix bug introduced in v3 when working around the gcc-7 bug (2/5)
|
||||
- Fix ZSTD_DStream initialization code in squashfs (4/5)
|
||||
- Fix patch documentation for patches written by Sean Purcell (4/5)
|
||||
|
||||
Nick Terrell (5):
|
||||
lib: Add xxhash module
|
||||
lib: Add zstd modules
|
||||
btrfs: Add zstd support
|
||||
squashfs: Add zstd support
|
||||
crypto: Add zstd support
|
||||
|
||||
crypto/Kconfig | 9 +
|
||||
crypto/Makefile | 1 +
|
||||
crypto/testmgr.c | 10 +
|
||||
crypto/testmgr.h | 71 +
|
||||
crypto/zstd.c | 265 ++++
|
||||
fs/btrfs/Kconfig | 2 +
|
||||
fs/btrfs/Makefile | 2 +-
|
||||
fs/btrfs/compression.c | 1 +
|
||||
@@ -40,13 +65,13 @@ Nick Terrell (4):
|
||||
fs/btrfs/props.c | 6 +
|
||||
fs/btrfs/super.c | 12 +-
|
||||
fs/btrfs/sysfs.c | 2 +
|
||||
fs/btrfs/zstd.c | 433 ++++++
|
||||
fs/btrfs/zstd.c | 432 ++++++
|
||||
fs/squashfs/Kconfig | 14 +
|
||||
fs/squashfs/Makefile | 1 +
|
||||
fs/squashfs/decompressor.c | 7 +
|
||||
fs/squashfs/decompressor.h | 4 +
|
||||
fs/squashfs/squashfs_fs.h | 1 +
|
||||
fs/squashfs/zstd_wrapper.c | 150 ++
|
||||
fs/squashfs/zstd_wrapper.c | 151 ++
|
||||
include/linux/xxhash.h | 236 +++
|
||||
include/linux/zstd.h | 1157 +++++++++++++++
|
||||
include/uapi/linux/btrfs.h | 8 +-
|
||||
@@ -55,21 +80,22 @@ Nick Terrell (4):
|
||||
lib/xxhash.c | 500 +++++++
|
||||
lib/zstd/Makefile | 18 +
|
||||
lib/zstd/bitstream.h | 374 +++++
|
||||
lib/zstd/compress.c | 3479 ++++++++++++++++++++++++++++++++++++++++++++
|
||||
lib/zstd/decompress.c | 2526 ++++++++++++++++++++++++++++++++
|
||||
lib/zstd/entropy_common.c | 243 ++++
|
||||
lib/zstd/compress.c | 3484 ++++++++++++++++++++++++++++++++++++++++++++
|
||||
lib/zstd/decompress.c | 2528 ++++++++++++++++++++++++++++++++
|
||||
lib/zstd/entropy_common.c | 243 +++
|
||||
lib/zstd/error_private.h | 53 +
|
||||
lib/zstd/fse.h | 575 ++++++++
|
||||
lib/zstd/fse_compress.c | 795 ++++++++++
|
||||
lib/zstd/fse_decompress.c | 332 +++++
|
||||
lib/zstd/huf.h | 212 +++
|
||||
lib/zstd/huf_compress.c | 771 ++++++++++
|
||||
lib/zstd/huf_compress.c | 770 ++++++++++
|
||||
lib/zstd/huf_decompress.c | 960 ++++++++++++
|
||||
lib/zstd/mem.h | 151 ++
|
||||
lib/zstd/zstd_common.c | 75 +
|
||||
lib/zstd/zstd_internal.h | 269 ++++
|
||||
lib/zstd/zstd_internal.h | 263 ++++
|
||||
lib/zstd/zstd_opt.h | 1014 +++++++++++++
|
||||
39 files changed, 14400 insertions(+), 12 deletions(-)
|
||||
44 files changed, 14756 insertions(+), 12 deletions(-)
|
||||
create mode 100644 crypto/zstd.c
|
||||
create mode 100644 fs/btrfs/zstd.c
|
||||
create mode 100644 fs/squashfs/zstd_wrapper.c
|
||||
create mode 100644 include/linux/xxhash.h
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 5ac909c415ab4a18fd90794793c96e450795e8c6 Mon Sep 17 00:00:00 2001
|
||||
From a4b1ffb6e89bbccd519f9afa0910635668436105 Mon Sep 17 00:00:00 2001
|
||||
From: Nick Terrell <terrelln@fb.com>
|
||||
Date: Wed, 21 Jun 2017 17:37:36 -0700
|
||||
Subject: [PATCH v2 1/4] lib: Add xxhash module
|
||||
Date: Mon, 17 Jul 2017 17:07:18 -0700
|
||||
Subject: [PATCH v5 1/5] lib: Add xxhash module
|
||||
|
||||
Adds xxhash kernel module with xxh32 and xxh64 hashes. xxhash is an
|
||||
extremely fast non-cryptographic hash algorithm for checksumming.
|
||||
@@ -327,10 +327,10 @@ index 0000000..9e1f42c
|
||||
+
|
||||
+#endif /* XXHASH_H */
|
||||
diff --git a/lib/Kconfig b/lib/Kconfig
|
||||
index 0c8b78a..b6009d7 100644
|
||||
index 6762529..5e7541f 100644
|
||||
--- a/lib/Kconfig
|
||||
+++ b/lib/Kconfig
|
||||
@@ -184,6 +184,9 @@ config CRC8
|
||||
@@ -192,6 +192,9 @@ config CRC8
|
||||
when they need to do cyclic redundancy check according CRC8
|
||||
algorithm. Module will be called crc8.
|
||||
|
||||
@@ -341,10 +341,10 @@ index 0c8b78a..b6009d7 100644
|
||||
bool
|
||||
depends on AUDIT && !AUDIT_ARCH
|
||||
diff --git a/lib/Makefile b/lib/Makefile
|
||||
index 0166fbc..1338226 100644
|
||||
index 40c1837..d06b68a 100644
|
||||
--- a/lib/Makefile
|
||||
+++ b/lib/Makefile
|
||||
@@ -102,6 +102,7 @@ obj-$(CONFIG_CRC32_SELFTEST) += crc32test.o
|
||||
@@ -102,6 +102,7 @@ obj-$(CONFIG_CRC4) += crc4.o
|
||||
obj-$(CONFIG_CRC7) += crc7.o
|
||||
obj-$(CONFIG_LIBCRC32C) += libcrc32c.o
|
||||
obj-$(CONFIG_CRC8) += crc8.o
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From d2626127c6d6e60e940dd9a3ed58323bdcdc4930 Mon Sep 17 00:00:00 2001
|
||||
From b7f044163968d724be55bf4841fd80babe036dc2 Mon Sep 17 00:00:00 2001
|
||||
From: Nick Terrell <terrelln@fb.com>
|
||||
Date: Tue, 16 May 2017 14:55:36 -0700
|
||||
Subject: [PATCH v2 2/4] lib: Add zstd modules
|
||||
Date: Mon, 17 Jul 2017 17:08:19 -0700
|
||||
Subject: [PATCH v5 2/5] lib: Add zstd modules
|
||||
|
||||
Add zstd compression and decompression kernel modules.
|
||||
zstd offers a wide varity of compression speed and quality trade-offs.
|
||||
@@ -110,24 +110,35 @@ v1 -> v2:
|
||||
HUF_compressWeights(), HUF_readDTableX2(), and HUF_readDTableX4()
|
||||
- No function uses more than 400 B of stack space
|
||||
|
||||
v2 -> v3:
|
||||
- Work around gcc-7 bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81388
|
||||
- Fix bug in dictionary compression from upstream commit cc1522351f
|
||||
|
||||
v3 -> v4:
|
||||
- Fix minor compiler warnings
|
||||
|
||||
v4 -> v5:
|
||||
- Fix rare compression bug from upstream commit 308047eb5d
|
||||
- Fix bug introduced in v3 when working around the gcc-7 bug
|
||||
|
||||
include/linux/zstd.h | 1157 +++++++++++++++
|
||||
lib/Kconfig | 8 +
|
||||
lib/Makefile | 2 +
|
||||
lib/zstd/Makefile | 18 +
|
||||
lib/zstd/bitstream.h | 374 +++++
|
||||
lib/zstd/compress.c | 3479 +++++++++++++++++++++++++++++++++++++++++++++
|
||||
lib/zstd/decompress.c | 2526 ++++++++++++++++++++++++++++++++
|
||||
lib/zstd/compress.c | 3484 +++++++++++++++++++++++++++++++++++++++++++++
|
||||
lib/zstd/decompress.c | 2528 ++++++++++++++++++++++++++++++++
|
||||
lib/zstd/entropy_common.c | 243 ++++
|
||||
lib/zstd/error_private.h | 53 +
|
||||
lib/zstd/fse.h | 575 ++++++++
|
||||
lib/zstd/fse_compress.c | 795 +++++++++++
|
||||
lib/zstd/fse_decompress.c | 332 +++++
|
||||
lib/zstd/huf.h | 212 +++
|
||||
lib/zstd/huf_compress.c | 771 ++++++++++
|
||||
lib/zstd/huf_compress.c | 770 ++++++++++
|
||||
lib/zstd/huf_decompress.c | 960 +++++++++++++
|
||||
lib/zstd/mem.h | 151 ++
|
||||
lib/zstd/zstd_common.c | 75 +
|
||||
lib/zstd/zstd_internal.h | 269 ++++
|
||||
lib/zstd/zstd_internal.h | 263 ++++
|
||||
lib/zstd/zstd_opt.h | 1014 +++++++++++++
|
||||
19 files changed, 13014 insertions(+)
|
||||
create mode 100644 include/linux/zstd.h
|
||||
@@ -1312,10 +1323,10 @@ index 0000000..249575e
|
||||
+
|
||||
+#endif /* ZSTD_H */
|
||||
diff --git a/lib/Kconfig b/lib/Kconfig
|
||||
index b6009d7..f00ddab 100644
|
||||
index 5e7541f..0d49ed0 100644
|
||||
--- a/lib/Kconfig
|
||||
+++ b/lib/Kconfig
|
||||
@@ -241,6 +241,14 @@ config LZ4HC_COMPRESS
|
||||
@@ -249,6 +249,14 @@ config LZ4HC_COMPRESS
|
||||
config LZ4_DECOMPRESS
|
||||
tristate
|
||||
|
||||
@@ -1331,7 +1342,7 @@ index b6009d7..f00ddab 100644
|
||||
|
||||
#
|
||||
diff --git a/lib/Makefile b/lib/Makefile
|
||||
index 1338226..4fcef16 100644
|
||||
index d06b68a..d5c8a4f 100644
|
||||
--- a/lib/Makefile
|
||||
+++ b/lib/Makefile
|
||||
@@ -116,6 +116,8 @@ obj-$(CONFIG_LZO_DECOMPRESS) += lzo/
|
||||
@@ -1749,10 +1760,10 @@ index 0000000..a826b99
|
||||
+#endif /* BITSTREAM_H_MODULE */
|
||||
diff --git a/lib/zstd/compress.c b/lib/zstd/compress.c
|
||||
new file mode 100644
|
||||
index 0000000..d60ab7d
|
||||
index 0000000..f9166cf
|
||||
--- /dev/null
|
||||
+++ b/lib/zstd/compress.c
|
||||
@@ -0,0 +1,3479 @@
|
||||
@@ -0,0 +1,3484 @@
|
||||
+/**
|
||||
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
+ * All rights reserved.
|
||||
@@ -2338,7 +2349,7 @@ index 0000000..d60ab7d
|
||||
+ mlCodeTable[seqStorePtr->longLengthPos] = MaxML;
|
||||
+}
|
||||
+
|
||||
+ZSTD_STATIC size_t ZSTD_compressSequences(ZSTD_CCtx *zc, void *dst, size_t dstCapacity, size_t srcSize)
|
||||
+ZSTD_STATIC size_t ZSTD_compressSequences_internal(ZSTD_CCtx *zc, void *dst, size_t dstCapacity)
|
||||
+{
|
||||
+ const int longOffsets = zc->params.cParams.windowLog > STREAM_ACCUMULATOR_MIN;
|
||||
+ const seqStore_t *seqStorePtr = &(zc->seqStore);
|
||||
@@ -2391,7 +2402,7 @@ index 0000000..d60ab7d
|
||||
+ else
|
||||
+ op[0] = 0xFF, ZSTD_writeLE16(op + 1, (U16)(nbSeq - LONGNBSEQ)), op += 3;
|
||||
+ if (nbSeq == 0)
|
||||
+ goto _check_compressibility;
|
||||
+ return op - ostart;
|
||||
+
|
||||
+ /* seqHead : flags for FSE encoding type */
|
||||
+ seqHead = op++;
|
||||
@@ -2581,28 +2592,33 @@ index 0000000..d60ab7d
|
||||
+ op += streamSize;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+/* check compressibility */
|
||||
+_check_compressibility:
|
||||
+ {
|
||||
+ size_t const minGain = ZSTD_minGain(srcSize);
|
||||
+ size_t const maxCSize = srcSize - minGain;
|
||||
+ if ((size_t)(op - ostart) >= maxCSize) {
|
||||
+ zc->flagStaticHufTable = HUF_repeat_none;
|
||||
+ return 0;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* confirm repcodes */
|
||||
+ {
|
||||
+ int i;
|
||||
+ for (i = 0; i < ZSTD_REP_NUM; i++)
|
||||
+ zc->rep[i] = zc->repToConfirm[i];
|
||||
+ }
|
||||
+
|
||||
+ return op - ostart;
|
||||
+}
|
||||
+
|
||||
+ZSTD_STATIC size_t ZSTD_compressSequences(ZSTD_CCtx *zc, void *dst, size_t dstCapacity, size_t srcSize)
|
||||
+{
|
||||
+ size_t const cSize = ZSTD_compressSequences_internal(zc, dst, dstCapacity);
|
||||
+ size_t const minGain = ZSTD_minGain(srcSize);
|
||||
+ size_t const maxCSize = srcSize - minGain;
|
||||
+ /* If the srcSize <= dstCapacity, then there is enough space to write a
|
||||
+ * raw uncompressed block. Since we ran out of space, the block must not
|
||||
+ * be compressible, so fall back to a raw uncompressed block.
|
||||
+ */
|
||||
+ int const uncompressibleError = cSize == ERROR(dstSize_tooSmall) && srcSize <= dstCapacity;
|
||||
+ int i;
|
||||
+
|
||||
+ if (ZSTD_isError(cSize) && !uncompressibleError)
|
||||
+ return cSize;
|
||||
+ if (cSize >= maxCSize || uncompressibleError) {
|
||||
+ zc->flagStaticHufTable = HUF_repeat_none;
|
||||
+ return 0;
|
||||
+ }
|
||||
+ /* confirm repcodes */
|
||||
+ for (i = 0; i < ZSTD_REP_NUM; i++)
|
||||
+ zc->rep[i] = zc->repToConfirm[i];
|
||||
+ return cSize;
|
||||
+}
|
||||
+
|
||||
+/*! ZSTD_storeSeq() :
|
||||
+ Store a sequence (literal length, literals, offset code and match length code) into seqStore_t.
|
||||
+ `offsetCode` : distance to match, or 0 == repCode.
|
||||
@@ -5234,10 +5250,10 @@ index 0000000..d60ab7d
|
||||
+MODULE_DESCRIPTION("Zstd Compressor");
|
||||
diff --git a/lib/zstd/decompress.c b/lib/zstd/decompress.c
|
||||
new file mode 100644
|
||||
index 0000000..62449ae
|
||||
index 0000000..b178467
|
||||
--- /dev/null
|
||||
+++ b/lib/zstd/decompress.c
|
||||
@@ -0,0 +1,2526 @@
|
||||
@@ -0,0 +1,2528 @@
|
||||
+/**
|
||||
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
+ * All rights reserved.
|
||||
@@ -6238,6 +6254,8 @@ index 0000000..62449ae
|
||||
+ BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */
|
||||
+ FSE_updateState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */
|
||||
+
|
||||
+ seq.match = NULL;
|
||||
+
|
||||
+ return seq;
|
||||
+}
|
||||
+
|
||||
@@ -10012,10 +10030,10 @@ index 0000000..2143da2
|
||||
+#endif /* HUF_H_298734234 */
|
||||
diff --git a/lib/zstd/huf_compress.c b/lib/zstd/huf_compress.c
|
||||
new file mode 100644
|
||||
index 0000000..0361f38
|
||||
index 0000000..40055a7
|
||||
--- /dev/null
|
||||
+++ b/lib/zstd/huf_compress.c
|
||||
@@ -0,0 +1,771 @@
|
||||
@@ -0,0 +1,770 @@
|
||||
+/*
|
||||
+ * Huffman encoder, part of New Generation Entropy library
|
||||
+ * Copyright (C) 2013-2016, Yann Collet.
|
||||
@@ -10543,7 +10561,7 @@ index 0000000..0361f38
|
||||
+
|
||||
+size_t HUF_compressBound(size_t size) { return HUF_COMPRESSBOUND(size); }
|
||||
+
|
||||
+#define HUF_FLUSHBITS(s) (fast ? BIT_flushBitsFast(s) : BIT_flushBits(s))
|
||||
+#define HUF_FLUSHBITS(s) BIT_flushBits(s)
|
||||
+
|
||||
+#define HUF_FLUSHBITS_1(stream) \
|
||||
+ if (sizeof((stream)->bitContainer) * 8 < HUF_TABLELOG_MAX * 2 + 7) \
|
||||
@@ -10560,7 +10578,6 @@ index 0000000..0361f38
|
||||
+ BYTE *const oend = ostart + dstSize;
|
||||
+ BYTE *op = ostart;
|
||||
+ size_t n;
|
||||
+ const unsigned fast = (dstSize >= HUF_BLOCKBOUND(srcSize));
|
||||
+ BIT_CStream_t bitC;
|
||||
+
|
||||
+ /* init */
|
||||
@@ -11993,10 +12010,10 @@ index 0000000..a282624
|
||||
+}
|
||||
diff --git a/lib/zstd/zstd_internal.h b/lib/zstd/zstd_internal.h
|
||||
new file mode 100644
|
||||
index 0000000..6748719
|
||||
index 0000000..1a79fab
|
||||
--- /dev/null
|
||||
+++ b/lib/zstd/zstd_internal.h
|
||||
@@ -0,0 +1,269 @@
|
||||
@@ -0,0 +1,263 @@
|
||||
+/**
|
||||
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
+ * All rights reserved.
|
||||
@@ -12125,35 +12142,29 @@ index 0000000..6748719
|
||||
+/*-*******************************************
|
||||
+* Shared functions to include for inlining
|
||||
+*********************************************/
|
||||
+static void ZSTD_copy8(void *dst, const void *src) { memcpy(dst, src, 8); }
|
||||
+#define COPY8(d, s) \
|
||||
+ { \
|
||||
+ ZSTD_copy8(d, s); \
|
||||
+ d += 8; \
|
||||
+ s += 8; \
|
||||
+ }
|
||||
+
|
||||
+ZSTD_STATIC void ZSTD_copy8(void *dst, const void *src) {
|
||||
+ memcpy(dst, src, 8);
|
||||
+}
|
||||
+/*! ZSTD_wildcopy() :
|
||||
+* custom version of memcpy(), can copy up to 7 bytes too many (8 bytes if length==0) */
|
||||
+#define WILDCOPY_OVERLENGTH 8
|
||||
+ZSTD_STATIC void ZSTD_wildcopy(void *dst, const void *src, ptrdiff_t length)
|
||||
+{
|
||||
+ const BYTE *ip = (const BYTE *)src;
|
||||
+ BYTE *op = (BYTE *)dst;
|
||||
+ BYTE *const oend = op + length;
|
||||
+ do
|
||||
+ COPY8(op, ip)
|
||||
+ while (op < oend);
|
||||
+}
|
||||
+
|
||||
+ZSTD_STATIC void ZSTD_wildcopy_e(void *dst, const void *src, void *dstEnd) /* should be faster for decoding, but strangely, not verified on all platform */
|
||||
+{
|
||||
+ const BYTE *ip = (const BYTE *)src;
|
||||
+ BYTE *op = (BYTE *)dst;
|
||||
+ BYTE *const oend = (BYTE *)dstEnd;
|
||||
+ do
|
||||
+ COPY8(op, ip)
|
||||
+ while (op < oend);
|
||||
+ const BYTE* ip = (const BYTE*)src;
|
||||
+ BYTE* op = (BYTE*)dst;
|
||||
+ BYTE* const oend = op + length;
|
||||
+ /* Work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81388.
|
||||
+ * Avoid the bad case where the loop only runs once by handling the
|
||||
+ * special case separately. This doesn't trigger the bug because it
|
||||
+ * doesn't involve pointer/integer overflow.
|
||||
+ */
|
||||
+ if (length <= 8)
|
||||
+ return ZSTD_copy8(dst, src);
|
||||
+ do {
|
||||
+ ZSTD_copy8(op, ip);
|
||||
+ op += 8;
|
||||
+ ip += 8;
|
||||
+ } while (op < oend);
|
||||
+}
|
||||
+
|
||||
+/*-*******************************************
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 599f8f2aaace3df939cb145368574a52268d82d0 Mon Sep 17 00:00:00 2001
|
||||
From 8a9dddfbf6551afea73911e367dd4be64d62b9fd Mon Sep 17 00:00:00 2001
|
||||
From: Nick Terrell <terrelln@fb.com>
|
||||
Date: Wed, 21 Jun 2017 17:31:39 -0700
|
||||
Subject: [PATCH v2 3/4] btrfs: Add zstd support
|
||||
Date: Mon, 17 Jul 2017 17:08:39 -0700
|
||||
Subject: [PATCH v5 3/5] btrfs: Add zstd support
|
||||
|
||||
Add zstd compression and decompression support to BtrFS. zstd at its
|
||||
fastest level compresses almost as well as zlib, while offering much
|
||||
@@ -63,6 +63,14 @@ zstd source repository: https://github.com/facebook/zstd
|
||||
|
||||
Signed-off-by: Nick Terrell <terrelln@fb.com>
|
||||
---
|
||||
v2 -> v3:
|
||||
- Port upstream BtrFS commits e1ddce71d6, 389a6cfc2a, and 6acafd1eff
|
||||
- Change default compression level for BtrFS to 3
|
||||
|
||||
v3 -> v4:
|
||||
- Add missing includes, which fixes the aarch64 build
|
||||
- Fix minor linter warnings
|
||||
|
||||
fs/btrfs/Kconfig | 2 +
|
||||
fs/btrfs/Makefile | 2 +-
|
||||
fs/btrfs/compression.c | 1 +
|
||||
@@ -73,9 +81,9 @@ Signed-off-by: Nick Terrell <terrelln@fb.com>
|
||||
fs/btrfs/props.c | 6 +
|
||||
fs/btrfs/super.c | 12 +-
|
||||
fs/btrfs/sysfs.c | 2 +
|
||||
fs/btrfs/zstd.c | 433 +++++++++++++++++++++++++++++++++++++++++++++
|
||||
fs/btrfs/zstd.c | 432 +++++++++++++++++++++++++++++++++++++++++++++
|
||||
include/uapi/linux/btrfs.h | 8 +-
|
||||
12 files changed, 469 insertions(+), 12 deletions(-)
|
||||
12 files changed, 468 insertions(+), 12 deletions(-)
|
||||
create mode 100644 fs/btrfs/zstd.c
|
||||
|
||||
diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig
|
||||
@@ -105,10 +113,10 @@ index 128ce17..962a95a 100644
|
||||
reada.o backref.o ulist.o qgroup.o send.o dev-replace.o raid56.o \
|
||||
uuid-tree.o props.o hash.o free-space-tree.o
|
||||
diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
|
||||
index 10e6b28..3beb0d0 100644
|
||||
index d2ef9ac..4ff42d1 100644
|
||||
--- a/fs/btrfs/compression.c
|
||||
+++ b/fs/btrfs/compression.c
|
||||
@@ -761,6 +761,7 @@ static struct {
|
||||
@@ -704,6 +704,7 @@ static struct {
|
||||
static const struct btrfs_compress_op * const btrfs_compress_op[] = {
|
||||
&btrfs_zlib_compress,
|
||||
&btrfs_lzo_compress,
|
||||
@@ -117,10 +125,10 @@ index 10e6b28..3beb0d0 100644
|
||||
|
||||
void __init btrfs_init_compress(void)
|
||||
diff --git a/fs/btrfs/compression.h b/fs/btrfs/compression.h
|
||||
index 39ec43a..d99fc21 100644
|
||||
index 87f6d33..2269e00 100644
|
||||
--- a/fs/btrfs/compression.h
|
||||
+++ b/fs/btrfs/compression.h
|
||||
@@ -60,8 +60,9 @@ enum btrfs_compression_type {
|
||||
@@ -99,8 +99,9 @@ enum btrfs_compression_type {
|
||||
BTRFS_COMPRESS_NONE = 0,
|
||||
BTRFS_COMPRESS_ZLIB = 1,
|
||||
BTRFS_COMPRESS_LZO = 2,
|
||||
@@ -132,7 +140,7 @@ index 39ec43a..d99fc21 100644
|
||||
};
|
||||
|
||||
struct btrfs_compress_op {
|
||||
@@ -92,5 +93,6 @@ struct btrfs_compress_op {
|
||||
@@ -128,5 +129,6 @@ struct btrfs_compress_op {
|
||||
|
||||
extern const struct btrfs_compress_op btrfs_zlib_compress;
|
||||
extern const struct btrfs_compress_op btrfs_lzo_compress;
|
||||
@@ -140,10 +148,10 @@ index 39ec43a..d99fc21 100644
|
||||
|
||||
#endif
|
||||
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
|
||||
index 4f8f75d..61dd3dd 100644
|
||||
index 3f3eb7b..845d77c 100644
|
||||
--- a/fs/btrfs/ctree.h
|
||||
+++ b/fs/btrfs/ctree.h
|
||||
@@ -271,6 +271,7 @@ struct btrfs_super_block {
|
||||
@@ -270,6 +270,7 @@ struct btrfs_super_block {
|
||||
BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS | \
|
||||
BTRFS_FEATURE_INCOMPAT_BIG_METADATA | \
|
||||
BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO | \
|
||||
@@ -152,10 +160,10 @@ index 4f8f75d..61dd3dd 100644
|
||||
BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF | \
|
||||
BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA | \
|
||||
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
|
||||
index 5f678dc..49c0e91 100644
|
||||
index 080e2eb..04632f4 100644
|
||||
--- a/fs/btrfs/disk-io.c
|
||||
+++ b/fs/btrfs/disk-io.c
|
||||
@@ -2831,6 +2831,8 @@ int open_ctree(struct super_block *sb,
|
||||
@@ -2828,6 +2828,8 @@ int open_ctree(struct super_block *sb,
|
||||
features |= BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF;
|
||||
if (fs_info->compress_type == BTRFS_COMPRESS_LZO)
|
||||
features |= BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO;
|
||||
@@ -165,7 +173,7 @@ index 5f678dc..49c0e91 100644
|
||||
if (features & BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA)
|
||||
btrfs_info(fs_info, "has skinny extents");
|
||||
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
|
||||
index e176375..f732cfd 100644
|
||||
index fa1b78c..b9963d9 100644
|
||||
--- a/fs/btrfs/ioctl.c
|
||||
+++ b/fs/btrfs/ioctl.c
|
||||
@@ -327,8 +327,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
|
||||
@@ -180,7 +188,7 @@ index e176375..f732cfd 100644
|
||||
ret = btrfs_set_prop(inode, "btrfs.compression",
|
||||
comp, strlen(comp), 0);
|
||||
if (ret)
|
||||
@@ -1463,6 +1465,8 @@ int btrfs_defrag_file(struct inode *inode, struct file *file,
|
||||
@@ -1466,6 +1468,8 @@ int btrfs_defrag_file(struct inode *inode, struct file *file,
|
||||
|
||||
if (range->compress_type == BTRFS_COMPRESS_LZO) {
|
||||
btrfs_set_fs_incompat(fs_info, COMPRESS_LZO);
|
||||
@@ -190,10 +198,10 @@ index e176375..f732cfd 100644
|
||||
|
||||
ret = defrag_count;
|
||||
diff --git a/fs/btrfs/props.c b/fs/btrfs/props.c
|
||||
index d6cb155..162105f 100644
|
||||
index 4b23ae5..20631e9 100644
|
||||
--- a/fs/btrfs/props.c
|
||||
+++ b/fs/btrfs/props.c
|
||||
@@ -383,6 +383,8 @@ static int prop_compression_validate(const char *value, size_t len)
|
||||
@@ -390,6 +390,8 @@ static int prop_compression_validate(const char *value, size_t len)
|
||||
return 0;
|
||||
else if (!strncmp("zlib", value, len))
|
||||
return 0;
|
||||
@@ -202,7 +210,7 @@ index d6cb155..162105f 100644
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -405,6 +407,8 @@ static int prop_compression_apply(struct inode *inode,
|
||||
@@ -412,6 +414,8 @@ static int prop_compression_apply(struct inode *inode,
|
||||
type = BTRFS_COMPRESS_LZO;
|
||||
else if (!strncmp("zlib", value, len))
|
||||
type = BTRFS_COMPRESS_ZLIB;
|
||||
@@ -211,7 +219,7 @@ index d6cb155..162105f 100644
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
@@ -422,6 +426,8 @@ static const char *prop_compression_extract(struct inode *inode)
|
||||
@@ -429,6 +433,8 @@ static const char *prop_compression_extract(struct inode *inode)
|
||||
return "zlib";
|
||||
case BTRFS_COMPRESS_LZO:
|
||||
return "lzo";
|
||||
@@ -221,7 +229,7 @@ index d6cb155..162105f 100644
|
||||
|
||||
return NULL;
|
||||
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
|
||||
index 4f1cdd5..4f792d5 100644
|
||||
index 12540b6..c370dea 100644
|
||||
--- a/fs/btrfs/super.c
|
||||
+++ b/fs/btrfs/super.c
|
||||
@@ -513,6 +513,14 @@ int btrfs_parse_options(struct btrfs_fs_info *info, char *options,
|
||||
@@ -239,7 +247,7 @@ index 4f1cdd5..4f792d5 100644
|
||||
} else if (strncmp(args[0].from, "no", 2) == 0) {
|
||||
compress_type = "no";
|
||||
btrfs_clear_opt(info->mount_opt, COMPRESS);
|
||||
@@ -1240,8 +1248,10 @@ static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry)
|
||||
@@ -1227,8 +1235,10 @@ static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry)
|
||||
if (btrfs_test_opt(info, COMPRESS)) {
|
||||
if (info->compress_type == BTRFS_COMPRESS_ZLIB)
|
||||
compress_type = "zlib";
|
||||
@@ -252,7 +260,7 @@ index 4f1cdd5..4f792d5 100644
|
||||
seq_printf(seq, ",compress-force=%s", compress_type);
|
||||
else
|
||||
diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c
|
||||
index 1f157fb..b0dec90 100644
|
||||
index c2d5f35..2b6d37c 100644
|
||||
--- a/fs/btrfs/sysfs.c
|
||||
+++ b/fs/btrfs/sysfs.c
|
||||
@@ -200,6 +200,7 @@ BTRFS_FEAT_ATTR_INCOMPAT(mixed_backref, MIXED_BACKREF);
|
||||
@@ -273,10 +281,10 @@ index 1f157fb..b0dec90 100644
|
||||
BTRFS_FEAT_ATTR_PTR(raid56),
|
||||
diff --git a/fs/btrfs/zstd.c b/fs/btrfs/zstd.c
|
||||
new file mode 100644
|
||||
index 0000000..838741b
|
||||
index 0000000..607ce47
|
||||
--- /dev/null
|
||||
+++ b/fs/btrfs/zstd.c
|
||||
@@ -0,0 +1,433 @@
|
||||
@@ -0,0 +1,432 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2016-present, Facebook, Inc.
|
||||
+ * All rights reserved.
|
||||
@@ -289,29 +297,27 @@ index 0000000..838741b
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ * General Public License for more details.
|
||||
+ *
|
||||
+ * You should have received a copy of the GNU General Public
|
||||
+ * License along with this program; if not, write to the
|
||||
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
+ * Boston, MA 021110-1307, USA.
|
||||
+ */
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/vmalloc.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/sched.h>
|
||||
+#include <linux/pagemap.h>
|
||||
+#include <linux/bio.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/mm.h>
|
||||
+#include <linux/pagemap.h>
|
||||
+#include <linux/refcount.h>
|
||||
+#include <linux/sched.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/zstd.h>
|
||||
+#include "compression.h"
|
||||
+
|
||||
+#define ZSTD_BTRFS_MAX_WINDOWLOG 17
|
||||
+#define ZSTD_BTRFS_MAX_INPUT (1 << ZSTD_BTRFS_MAX_WINDOWLOG)
|
||||
+#define ZSTD_BTRFS_DEFAULT_LEVEL 3
|
||||
+
|
||||
+static ZSTD_parameters zstd_get_btrfs_parameters(size_t src_len)
|
||||
+{
|
||||
+ ZSTD_parameters params = ZSTD_getParams(1, src_len, 0);
|
||||
+ ZSTD_parameters params = ZSTD_getParams(ZSTD_BTRFS_DEFAULT_LEVEL,
|
||||
+ src_len, 0);
|
||||
+
|
||||
+ if (params.cParams.windowLog > ZSTD_BTRFS_MAX_WINDOWLOG)
|
||||
+ params.cParams.windowLog = ZSTD_BTRFS_MAX_WINDOWLOG;
|
||||
@@ -330,7 +336,7 @@ index 0000000..838741b
|
||||
+{
|
||||
+ struct workspace *workspace = list_entry(ws, struct workspace, list);
|
||||
+
|
||||
+ vfree(workspace->mem);
|
||||
+ kvfree(workspace->mem);
|
||||
+ kfree(workspace->buf);
|
||||
+ kfree(workspace);
|
||||
+}
|
||||
@@ -341,15 +347,15 @@ index 0000000..838741b
|
||||
+ zstd_get_btrfs_parameters(ZSTD_BTRFS_MAX_INPUT);
|
||||
+ struct workspace *workspace;
|
||||
+
|
||||
+ workspace = kzalloc(sizeof(*workspace), GFP_NOFS);
|
||||
+ workspace = kzalloc(sizeof(*workspace), GFP_KERNEL);
|
||||
+ if (!workspace)
|
||||
+ return ERR_PTR(-ENOMEM);
|
||||
+
|
||||
+ workspace->size = max_t(size_t,
|
||||
+ ZSTD_CStreamWorkspaceBound(params.cParams),
|
||||
+ ZSTD_DStreamWorkspaceBound(ZSTD_BTRFS_MAX_INPUT));
|
||||
+ workspace->mem = vmalloc(workspace->size);
|
||||
+ workspace->buf = kmalloc(PAGE_SIZE, GFP_NOFS);
|
||||
+ workspace->mem = kvmalloc(workspace->size, GFP_KERNEL);
|
||||
+ workspace->buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
+ if (!workspace->mem || !workspace->buf)
|
||||
+ goto fail;
|
||||
+
|
||||
@@ -541,12 +547,13 @@ index 0000000..838741b
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int zstd_decompress_bio(struct list_head *ws, struct page **pages_in,
|
||||
+ u64 disk_start,
|
||||
+ struct bio *orig_bio,
|
||||
+ size_t srclen)
|
||||
+static int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb)
|
||||
+{
|
||||
+ struct workspace *workspace = list_entry(ws, struct workspace, list);
|
||||
+ struct page **pages_in = cb->compressed_pages;
|
||||
+ u64 disk_start = cb->start;
|
||||
+ struct bio *orig_bio = cb->orig_bio;
|
||||
+ size_t srclen = cb->compressed_len;
|
||||
+ ZSTD_DStream *stream;
|
||||
+ int ret = 0;
|
||||
+ unsigned long page_in_index = 0;
|
||||
@@ -711,7 +718,7 @@ index 0000000..838741b
|
||||
+ .decompress = zstd_decompress,
|
||||
+};
|
||||
diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h
|
||||
index a456e53..992c150 100644
|
||||
index 9aa74f3..378230c 100644
|
||||
--- a/include/uapi/linux/btrfs.h
|
||||
+++ b/include/uapi/linux/btrfs.h
|
||||
@@ -255,13 +255,7 @@ struct btrfs_ioctl_fs_info_args {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 5ff6a64abaea7b7f11d37cb0fdf08642316a3a90 Mon Sep 17 00:00:00 2001
|
||||
From: Nick Terrell <terrelln@fb.com>
|
||||
Date: Mon, 12 Jun 2017 12:18:23 -0700
|
||||
Subject: [PATCH v2 4/4] squashfs: Add zstd support
|
||||
From 46bf8f6d30d6ddf2446c110f122482b5e5e16933 Mon Sep 17 00:00:00 2001
|
||||
From: Sean Purcell <me@seanp.xyz>
|
||||
Date: Mon, 17 Jul 2017 17:08:59 -0700
|
||||
Subject: [PATCH v5 4/5] squashfs: Add zstd support
|
||||
|
||||
Add zstd compression and decompression support to SquashFS. zstd is a
|
||||
great fit for SquashFS because it can compress at ratios approaching xz,
|
||||
@@ -42,16 +42,23 @@ taking over the submission process.
|
||||
|
||||
zstd source repository: https://github.com/facebook/zstd
|
||||
|
||||
Cc: Sean Purcell <me@seanp.xyz>
|
||||
Signed-off-by: Sean Purcell <me@seanp.xyz>
|
||||
Signed-off-by: Nick Terrell <terrelln@fb.com>
|
||||
---
|
||||
v3 -> v4:
|
||||
- Fix minor linter warnings
|
||||
|
||||
v4 -> v5:
|
||||
- Fix ZSTD_DStream initialization code in squashfs
|
||||
- Fix patch documentation to reflect that Sean Purcell is the author
|
||||
|
||||
fs/squashfs/Kconfig | 14 +++++
|
||||
fs/squashfs/Makefile | 1 +
|
||||
fs/squashfs/decompressor.c | 7 +++
|
||||
fs/squashfs/decompressor.h | 4 ++
|
||||
fs/squashfs/squashfs_fs.h | 1 +
|
||||
fs/squashfs/zstd_wrapper.c | 150 +++++++++++++++++++++++++++++++++++++++++++++
|
||||
6 files changed, 177 insertions(+)
|
||||
fs/squashfs/zstd_wrapper.c | 151 +++++++++++++++++++++++++++++++++++++++++++++
|
||||
6 files changed, 178 insertions(+)
|
||||
create mode 100644 fs/squashfs/zstd_wrapper.c
|
||||
|
||||
diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig
|
||||
@@ -140,10 +147,10 @@ index 506f4ba..24d12fd 100644
|
||||
__le32 s_magic;
|
||||
diff --git a/fs/squashfs/zstd_wrapper.c b/fs/squashfs/zstd_wrapper.c
|
||||
new file mode 100644
|
||||
index 0000000..8cb7c76
|
||||
index 0000000..eeaabf8
|
||||
--- /dev/null
|
||||
+++ b/fs/squashfs/zstd_wrapper.c
|
||||
@@ -0,0 +1,150 @@
|
||||
@@ -0,0 +1,151 @@
|
||||
+/*
|
||||
+ * Squashfs - a compressed read only filesystem for Linux
|
||||
+ *
|
||||
@@ -160,10 +167,6 @@ index 0000000..8cb7c76
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ * You should have received a copy of the GNU General Public License
|
||||
+ * along with this program; if not, write to the Free Software
|
||||
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
+ *
|
||||
+ * zstd_wrapper.c
|
||||
+ */
|
||||
+
|
||||
@@ -182,15 +185,18 @@ index 0000000..8cb7c76
|
||||
+struct workspace {
|
||||
+ void *mem;
|
||||
+ size_t mem_size;
|
||||
+ size_t window_size;
|
||||
+};
|
||||
+
|
||||
+static void *zstd_init(struct squashfs_sb_info *msblk, void *buff)
|
||||
+{
|
||||
+ struct workspace *wksp = kmalloc(sizeof(*wksp), GFP_KERNEL);
|
||||
+
|
||||
+ if (wksp == NULL)
|
||||
+ goto failed;
|
||||
+ wksp->mem_size = ZSTD_DStreamWorkspaceBound(max_t(size_t,
|
||||
+ msblk->block_size, SQUASHFS_METADATA_SIZE));
|
||||
+ wksp->window_size = max_t(size_t,
|
||||
+ msblk->block_size, SQUASHFS_METADATA_SIZE);
|
||||
+ wksp->mem_size = ZSTD_DStreamWorkspaceBound(wksp->window_size);
|
||||
+ wksp->mem = vmalloc(wksp->mem_size);
|
||||
+ if (wksp->mem == NULL)
|
||||
+ goto failed;
|
||||
@@ -226,7 +232,7 @@ index 0000000..8cb7c76
|
||||
+ ZSTD_inBuffer in_buf = { NULL, 0, 0 };
|
||||
+ ZSTD_outBuffer out_buf = { NULL, 0, 0 };
|
||||
+
|
||||
+ stream = ZSTD_initDStream(wksp->mem_size, wksp->mem, wksp->mem_size);
|
||||
+ stream = ZSTD_initDStream(wksp->window_size, wksp->mem, wksp->mem_size);
|
||||
+
|
||||
+ if (!stream) {
|
||||
+ ERROR("Failed to initialize zstd decompressor\n");
|
||||
@@ -239,6 +245,7 @@ index 0000000..8cb7c76
|
||||
+ do {
|
||||
+ if (in_buf.pos == in_buf.size && k < b) {
|
||||
+ int avail = min(length, msblk->devblksize - offset);
|
||||
+
|
||||
+ length -= avail;
|
||||
+ in_buf.src = bh[k]->b_data + offset;
|
||||
+ in_buf.size = avail;
|
||||
@@ -249,8 +256,9 @@ index 0000000..8cb7c76
|
||||
+ if (out_buf.pos == out_buf.size) {
|
||||
+ out_buf.dst = squashfs_next_page(output);
|
||||
+ if (out_buf.dst == NULL) {
|
||||
+ /* shouldn't run out of pages before stream is
|
||||
+ * done */
|
||||
+ /* Shouldn't run out of pages
|
||||
+ * before stream is done.
|
||||
+ */
|
||||
+ squashfs_finish_page(output);
|
||||
+ goto out;
|
||||
+ }
|
||||
|
||||
@@ -0,0 +1,424 @@
|
||||
From 308795a7713ca6fcd468b60fba9a2fca99cee6a0 Mon Sep 17 00:00:00 2001
|
||||
From: Nick Terrell <terrelln@fb.com>
|
||||
Date: Wed, 2 Aug 2017 18:02:13 -0700
|
||||
Subject: [PATCH v5 5/5] crypto: Add zstd support
|
||||
|
||||
Adds zstd support to crypto and scompress. Only supports the default
|
||||
level.
|
||||
|
||||
Signed-off-by: Nick Terrell <terrelln@fb.com>
|
||||
---
|
||||
crypto/Kconfig | 9 ++
|
||||
crypto/Makefile | 1 +
|
||||
crypto/testmgr.c | 10 +++
|
||||
crypto/testmgr.h | 71 +++++++++++++++
|
||||
crypto/zstd.c | 265 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
5 files changed, 356 insertions(+)
|
||||
create mode 100644 crypto/zstd.c
|
||||
|
||||
diff --git a/crypto/Kconfig b/crypto/Kconfig
|
||||
index caa770e..4fc3936 100644
|
||||
--- a/crypto/Kconfig
|
||||
+++ b/crypto/Kconfig
|
||||
@@ -1662,6 +1662,15 @@ config CRYPTO_LZ4HC
|
||||
help
|
||||
This is the LZ4 high compression mode algorithm.
|
||||
|
||||
+config CRYPTO_ZSTD
|
||||
+ tristate "Zstd compression algorithm"
|
||||
+ select CRYPTO_ALGAPI
|
||||
+ select CRYPTO_ACOMP2
|
||||
+ select ZSTD_COMPRESS
|
||||
+ select ZSTD_DECOMPRESS
|
||||
+ help
|
||||
+ This is the zstd algorithm.
|
||||
+
|
||||
comment "Random Number Generation"
|
||||
|
||||
config CRYPTO_ANSI_CPRNG
|
||||
diff --git a/crypto/Makefile b/crypto/Makefile
|
||||
index d41f033..b22e1e8 100644
|
||||
--- a/crypto/Makefile
|
||||
+++ b/crypto/Makefile
|
||||
@@ -133,6 +133,7 @@ obj-$(CONFIG_CRYPTO_USER_API_HASH) += algif_hash.o
|
||||
obj-$(CONFIG_CRYPTO_USER_API_SKCIPHER) += algif_skcipher.o
|
||||
obj-$(CONFIG_CRYPTO_USER_API_RNG) += algif_rng.o
|
||||
obj-$(CONFIG_CRYPTO_USER_API_AEAD) += algif_aead.o
|
||||
+obj-$(CONFIG_CRYPTO_ZSTD) += zstd.o
|
||||
|
||||
ecdh_generic-y := ecc.o
|
||||
ecdh_generic-y += ecdh.o
|
||||
diff --git a/crypto/testmgr.c b/crypto/testmgr.c
|
||||
index 7125ba3..8a124d3 100644
|
||||
--- a/crypto/testmgr.c
|
||||
+++ b/crypto/testmgr.c
|
||||
@@ -3603,6 +3603,16 @@ static const struct alg_test_desc alg_test_descs[] = {
|
||||
.decomp = __VECS(zlib_deflate_decomp_tv_template)
|
||||
}
|
||||
}
|
||||
+ }, {
|
||||
+ .alg = "zstd",
|
||||
+ .test = alg_test_comp,
|
||||
+ .fips_allowed = 1,
|
||||
+ .suite = {
|
||||
+ .comp = {
|
||||
+ .comp = __VECS(zstd_comp_tv_template),
|
||||
+ .decomp = __VECS(zstd_decomp_tv_template)
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
};
|
||||
|
||||
diff --git a/crypto/testmgr.h b/crypto/testmgr.h
|
||||
index 6ceb0e2..e6b5920 100644
|
||||
--- a/crypto/testmgr.h
|
||||
+++ b/crypto/testmgr.h
|
||||
@@ -34631,4 +34631,75 @@ static const struct comp_testvec lz4hc_decomp_tv_template[] = {
|
||||
},
|
||||
};
|
||||
|
||||
+static const struct comp_testvec zstd_comp_tv_template[] = {
|
||||
+ {
|
||||
+ .inlen = 68,
|
||||
+ .outlen = 39,
|
||||
+ .input = "The algorithm is zstd. "
|
||||
+ "The algorithm is zstd. "
|
||||
+ "The algorithm is zstd.",
|
||||
+ .output = "\x28\xb5\x2f\xfd\x00\x50\xf5\x00\x00\xb8\x54\x68\x65"
|
||||
+ "\x20\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x20\x69\x73"
|
||||
+ "\x20\x7a\x73\x74\x64\x2e\x20\x01\x00\x55\x73\x36\x01"
|
||||
+ ,
|
||||
+ },
|
||||
+ {
|
||||
+ .inlen = 244,
|
||||
+ .outlen = 151,
|
||||
+ .input = "zstd, short for Zstandard, is a fast lossless "
|
||||
+ "compression algorithm, targeting real-time "
|
||||
+ "compression scenarios at zlib-level and better "
|
||||
+ "compression ratios. The zstd compression library "
|
||||
+ "provides in-memory compression and decompression "
|
||||
+ "functions.",
|
||||
+ .output = "\x28\xb5\x2f\xfd\x00\x50\x75\x04\x00\x42\x4b\x1e\x17"
|
||||
+ "\x90\x81\x31\x00\xf2\x2f\xe4\x36\xc9\xef\x92\x88\x32"
|
||||
+ "\xc9\xf2\x24\x94\xd8\x68\x9a\x0f\x00\x0c\xc4\x31\x6f"
|
||||
+ "\x0d\x0c\x38\xac\x5c\x48\x03\xcd\x63\x67\xc0\xf3\xad"
|
||||
+ "\x4e\x90\xaa\x78\xa0\xa4\xc5\x99\xda\x2f\xb6\x24\x60"
|
||||
+ "\xe2\x79\x4b\xaa\xb6\x6b\x85\x0b\xc9\xc6\x04\x66\x86"
|
||||
+ "\xe2\xcc\xe2\x25\x3f\x4f\x09\xcd\xb8\x9d\xdb\xc1\x90"
|
||||
+ "\xa9\x11\xbc\x35\x44\x69\x2d\x9c\x64\x4f\x13\x31\x64"
|
||||
+ "\xcc\xfb\x4d\x95\x93\x86\x7f\x33\x7f\x1a\xef\xe9\x30"
|
||||
+ "\xf9\x67\xa1\x94\x0a\x69\x0f\x60\xcd\xc3\xab\x99\xdc"
|
||||
+ "\x42\xed\x97\x05\x00\x33\xc3\x15\x95\x3a\x06\xa0\x0e"
|
||||
+ "\x20\xa9\x0e\x82\xb9\x43\x45\x01",
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static const struct comp_testvec zstd_decomp_tv_template[] = {
|
||||
+ {
|
||||
+ .inlen = 43,
|
||||
+ .outlen = 68,
|
||||
+ .input = "\x28\xb5\x2f\xfd\x04\x50\xf5\x00\x00\xb8\x54\x68\x65"
|
||||
+ "\x20\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x20\x69\x73"
|
||||
+ "\x20\x7a\x73\x74\x64\x2e\x20\x01\x00\x55\x73\x36\x01"
|
||||
+ "\x6b\xf4\x13\x35",
|
||||
+ .output = "The algorithm is zstd. "
|
||||
+ "The algorithm is zstd. "
|
||||
+ "The algorithm is zstd.",
|
||||
+ },
|
||||
+ {
|
||||
+ .inlen = 155,
|
||||
+ .outlen = 244,
|
||||
+ .input = "\x28\xb5\x2f\xfd\x04\x50\x75\x04\x00\x42\x4b\x1e\x17"
|
||||
+ "\x90\x81\x31\x00\xf2\x2f\xe4\x36\xc9\xef\x92\x88\x32"
|
||||
+ "\xc9\xf2\x24\x94\xd8\x68\x9a\x0f\x00\x0c\xc4\x31\x6f"
|
||||
+ "\x0d\x0c\x38\xac\x5c\x48\x03\xcd\x63\x67\xc0\xf3\xad"
|
||||
+ "\x4e\x90\xaa\x78\xa0\xa4\xc5\x99\xda\x2f\xb6\x24\x60"
|
||||
+ "\xe2\x79\x4b\xaa\xb6\x6b\x85\x0b\xc9\xc6\x04\x66\x86"
|
||||
+ "\xe2\xcc\xe2\x25\x3f\x4f\x09\xcd\xb8\x9d\xdb\xc1\x90"
|
||||
+ "\xa9\x11\xbc\x35\x44\x69\x2d\x9c\x64\x4f\x13\x31\x64"
|
||||
+ "\xcc\xfb\x4d\x95\x93\x86\x7f\x33\x7f\x1a\xef\xe9\x30"
|
||||
+ "\xf9\x67\xa1\x94\x0a\x69\x0f\x60\xcd\xc3\xab\x99\xdc"
|
||||
+ "\x42\xed\x97\x05\x00\x33\xc3\x15\x95\x3a\x06\xa0\x0e"
|
||||
+ "\x20\xa9\x0e\x82\xb9\x43\x45\x01\xaa\x6d\xda\x0d",
|
||||
+ .output = "zstd, short for Zstandard, is a fast lossless "
|
||||
+ "compression algorithm, targeting real-time "
|
||||
+ "compression scenarios at zlib-level and better "
|
||||
+ "compression ratios. The zstd compression library "
|
||||
+ "provides in-memory compression and decompression "
|
||||
+ "functions.",
|
||||
+ },
|
||||
+};
|
||||
#endif /* _CRYPTO_TESTMGR_H */
|
||||
diff --git a/crypto/zstd.c b/crypto/zstd.c
|
||||
new file mode 100644
|
||||
index 0000000..9a76b3e
|
||||
--- /dev/null
|
||||
+++ b/crypto/zstd.c
|
||||
@@ -0,0 +1,265 @@
|
||||
+/*
|
||||
+ * Cryptographic API.
|
||||
+ *
|
||||
+ * Copyright (c) 2017-present, Facebook, Inc.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify it
|
||||
+ * under the terms of the GNU General Public License version 2 as published by
|
||||
+ * the Free Software Foundation.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
+ * more details.
|
||||
+ */
|
||||
+#include <linux/crypto.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/interrupt.h>
|
||||
+#include <linux/mm.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/net.h>
|
||||
+#include <linux/vmalloc.h>
|
||||
+#include <linux/zstd.h>
|
||||
+#include <crypto/internal/scompress.h>
|
||||
+
|
||||
+
|
||||
+#define ZSTD_DEF_LEVEL 3
|
||||
+
|
||||
+struct zstd_ctx {
|
||||
+ ZSTD_CCtx *cctx;
|
||||
+ ZSTD_DCtx *dctx;
|
||||
+ void *cwksp;
|
||||
+ void *dwksp;
|
||||
+};
|
||||
+
|
||||
+static ZSTD_parameters zstd_params(void)
|
||||
+{
|
||||
+ return ZSTD_getParams(ZSTD_DEF_LEVEL, 0, 0);
|
||||
+}
|
||||
+
|
||||
+static int zstd_comp_init(struct zstd_ctx *ctx)
|
||||
+{
|
||||
+ int ret = 0;
|
||||
+ const ZSTD_parameters params = zstd_params();
|
||||
+ const size_t wksp_size = ZSTD_CCtxWorkspaceBound(params.cParams);
|
||||
+
|
||||
+ ctx->cwksp = vzalloc(wksp_size);
|
||||
+ if (!ctx->cwksp) {
|
||||
+ ret = -ENOMEM;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ ctx->cctx = ZSTD_initCCtx(ctx->cwksp, wksp_size);
|
||||
+ if (!ctx->cctx) {
|
||||
+ ret = -EINVAL;
|
||||
+ goto out_free;
|
||||
+ }
|
||||
+out:
|
||||
+ return ret;
|
||||
+out_free:
|
||||
+ vfree(ctx->cwksp);
|
||||
+ goto out;
|
||||
+}
|
||||
+
|
||||
+static int zstd_decomp_init(struct zstd_ctx *ctx)
|
||||
+{
|
||||
+ int ret = 0;
|
||||
+ const size_t wksp_size = ZSTD_DCtxWorkspaceBound();
|
||||
+
|
||||
+ ctx->dwksp = vzalloc(wksp_size);
|
||||
+ if (!ctx->dwksp) {
|
||||
+ ret = -ENOMEM;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ ctx->dctx = ZSTD_initDCtx(ctx->dwksp, wksp_size);
|
||||
+ if (!ctx->dctx) {
|
||||
+ ret = -EINVAL;
|
||||
+ goto out_free;
|
||||
+ }
|
||||
+out:
|
||||
+ return ret;
|
||||
+out_free:
|
||||
+ vfree(ctx->dwksp);
|
||||
+ goto out;
|
||||
+}
|
||||
+
|
||||
+static void zstd_comp_exit(struct zstd_ctx *ctx)
|
||||
+{
|
||||
+ vfree(ctx->cwksp);
|
||||
+ ctx->cwksp = NULL;
|
||||
+ ctx->cctx = NULL;
|
||||
+}
|
||||
+
|
||||
+static void zstd_decomp_exit(struct zstd_ctx *ctx)
|
||||
+{
|
||||
+ vfree(ctx->dwksp);
|
||||
+ ctx->dwksp = NULL;
|
||||
+ ctx->dctx = NULL;
|
||||
+}
|
||||
+
|
||||
+static int __zstd_init(void *ctx)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = zstd_comp_init(ctx);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ ret = zstd_decomp_init(ctx);
|
||||
+ if (ret)
|
||||
+ zstd_comp_exit(ctx);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void *zstd_alloc_ctx(struct crypto_scomp *tfm)
|
||||
+{
|
||||
+ int ret;
|
||||
+ struct zstd_ctx *ctx;
|
||||
+
|
||||
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
||||
+ if (!ctx)
|
||||
+ return ERR_PTR(-ENOMEM);
|
||||
+
|
||||
+ ret = __zstd_init(ctx);
|
||||
+ if (ret) {
|
||||
+ kfree(ctx);
|
||||
+ return ERR_PTR(ret);
|
||||
+ }
|
||||
+
|
||||
+ return ctx;
|
||||
+}
|
||||
+
|
||||
+static int zstd_init(struct crypto_tfm *tfm)
|
||||
+{
|
||||
+ struct zstd_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
+
|
||||
+ return __zstd_init(ctx);
|
||||
+}
|
||||
+
|
||||
+static void __zstd_exit(void *ctx)
|
||||
+{
|
||||
+ zstd_comp_exit(ctx);
|
||||
+ zstd_decomp_exit(ctx);
|
||||
+}
|
||||
+
|
||||
+static void zstd_free_ctx(struct crypto_scomp *tfm, void *ctx)
|
||||
+{
|
||||
+ __zstd_exit(ctx);
|
||||
+ kzfree(ctx);
|
||||
+}
|
||||
+
|
||||
+static void zstd_exit(struct crypto_tfm *tfm)
|
||||
+{
|
||||
+ struct zstd_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
+
|
||||
+ __zstd_exit(ctx);
|
||||
+}
|
||||
+
|
||||
+static int __zstd_compress(const u8 *src, unsigned int slen,
|
||||
+ u8 *dst, unsigned int *dlen, void *ctx)
|
||||
+{
|
||||
+ size_t out_len;
|
||||
+ struct zstd_ctx *zctx = ctx;
|
||||
+ const ZSTD_parameters params = zstd_params();
|
||||
+
|
||||
+ out_len = ZSTD_compressCCtx(zctx->cctx, dst, *dlen, src, slen, params);
|
||||
+ if (ZSTD_isError(out_len))
|
||||
+ return -EINVAL;
|
||||
+ *dlen = out_len;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int zstd_compress(struct crypto_tfm *tfm, const u8 *src,
|
||||
+ unsigned int slen, u8 *dst, unsigned int *dlen)
|
||||
+{
|
||||
+ struct zstd_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
+
|
||||
+ return __zstd_compress(src, slen, dst, dlen, ctx);
|
||||
+}
|
||||
+
|
||||
+static int zstd_scompress(struct crypto_scomp *tfm, const u8 *src,
|
||||
+ unsigned int slen, u8 *dst, unsigned int *dlen,
|
||||
+ void *ctx)
|
||||
+{
|
||||
+ return __zstd_compress(src, slen, dst, dlen, ctx);
|
||||
+}
|
||||
+
|
||||
+static int __zstd_decompress(const u8 *src, unsigned int slen,
|
||||
+ u8 *dst, unsigned int *dlen, void *ctx)
|
||||
+{
|
||||
+ size_t out_len;
|
||||
+ struct zstd_ctx *zctx = ctx;
|
||||
+
|
||||
+ out_len = ZSTD_decompressDCtx(zctx->dctx, dst, *dlen, src, slen);
|
||||
+ if (ZSTD_isError(out_len))
|
||||
+ return -EINVAL;
|
||||
+ *dlen = out_len;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int zstd_decompress(struct crypto_tfm *tfm, const u8 *src,
|
||||
+ unsigned int slen, u8 *dst, unsigned int *dlen)
|
||||
+{
|
||||
+ struct zstd_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
+
|
||||
+ return __zstd_decompress(src, slen, dst, dlen, ctx);
|
||||
+}
|
||||
+
|
||||
+static int zstd_sdecompress(struct crypto_scomp *tfm, const u8 *src,
|
||||
+ unsigned int slen, u8 *dst, unsigned int *dlen,
|
||||
+ void *ctx)
|
||||
+{
|
||||
+ return __zstd_decompress(src, slen, dst, dlen, ctx);
|
||||
+}
|
||||
+
|
||||
+static struct crypto_alg alg = {
|
||||
+ .cra_name = "zstd",
|
||||
+ .cra_flags = CRYPTO_ALG_TYPE_COMPRESS,
|
||||
+ .cra_ctxsize = sizeof(struct zstd_ctx),
|
||||
+ .cra_module = THIS_MODULE,
|
||||
+ .cra_init = zstd_init,
|
||||
+ .cra_exit = zstd_exit,
|
||||
+ .cra_u = { .compress = {
|
||||
+ .coa_compress = zstd_compress,
|
||||
+ .coa_decompress = zstd_decompress } }
|
||||
+};
|
||||
+
|
||||
+static struct scomp_alg scomp = {
|
||||
+ .alloc_ctx = zstd_alloc_ctx,
|
||||
+ .free_ctx = zstd_free_ctx,
|
||||
+ .compress = zstd_scompress,
|
||||
+ .decompress = zstd_sdecompress,
|
||||
+ .base = {
|
||||
+ .cra_name = "zstd",
|
||||
+ .cra_driver_name = "zstd-scomp",
|
||||
+ .cra_module = THIS_MODULE,
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
+static int __init zstd_mod_init(void)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = crypto_register_alg(&alg);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = crypto_register_scomp(&scomp);
|
||||
+ if (ret)
|
||||
+ crypto_unregister_alg(&alg);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void __exit zstd_mod_fini(void)
|
||||
+{
|
||||
+ crypto_unregister_alg(&alg);
|
||||
+ crypto_unregister_scomp(&scomp);
|
||||
+}
|
||||
+
|
||||
+module_init(zstd_mod_init);
|
||||
+module_exit(zstd_mod_fini);
|
||||
+
|
||||
+MODULE_LICENSE("GPL");
|
||||
+MODULE_DESCRIPTION("Zstd Compression Algorithm");
|
||||
+MODULE_ALIAS_CRYPTO("zstd");
|
||||
--
|
||||
2.9.3
|
||||
@@ -0,0 +1,420 @@
|
||||
From 57a3cf95b276946559f9e044c7352c11303bb9c1 Mon Sep 17 00:00:00 2001
|
||||
From: Sean Purcell <me@seanp.xyz>
|
||||
Date: Thu, 3 Aug 2017 17:47:03 -0700
|
||||
Subject: [PATCH v6] squashfs-tools: Add zstd support
|
||||
|
||||
This patch adds zstd support to squashfs-tools. It works with zstd
|
||||
versions >= 1.0.0. It was originally written by Sean Purcell.
|
||||
|
||||
Signed-off-by: Sean Purcell <me@seanp.xyz>
|
||||
Signed-off-by: Nick Terrell <terrelln@fb.com>
|
||||
---
|
||||
v4 -> v5:
|
||||
- Fix patch documentation to reflect that Sean Purcell is the author
|
||||
- Don't strip trailing whitespace of unreleated code
|
||||
- Make zstd_display_options() static
|
||||
|
||||
v5 -> v6:
|
||||
- Fix build instructions in Makefile
|
||||
|
||||
squashfs-tools/Makefile | 20 ++++
|
||||
squashfs-tools/compressor.c | 8 ++
|
||||
squashfs-tools/squashfs_fs.h | 1 +
|
||||
squashfs-tools/zstd_wrapper.c | 254 ++++++++++++++++++++++++++++++++++++++++++
|
||||
squashfs-tools/zstd_wrapper.h | 48 ++++++++
|
||||
5 files changed, 331 insertions(+)
|
||||
create mode 100644 squashfs-tools/zstd_wrapper.c
|
||||
create mode 100644 squashfs-tools/zstd_wrapper.h
|
||||
|
||||
diff --git a/squashfs-tools/Makefile b/squashfs-tools/Makefile
|
||||
index 52d2582..22fc559 100644
|
||||
--- a/squashfs-tools/Makefile
|
||||
+++ b/squashfs-tools/Makefile
|
||||
@@ -75,6 +75,18 @@ GZIP_SUPPORT = 1
|
||||
#LZMA_SUPPORT = 1
|
||||
#LZMA_DIR = ../../../../LZMA/lzma465
|
||||
|
||||
+
|
||||
+########### Building ZSTD support ############
|
||||
+#
|
||||
+# The ZSTD library is supported
|
||||
+# ZSTD homepage: http://zstd.net
|
||||
+# ZSTD source repository: https://github.com/facebook/zstd
|
||||
+#
|
||||
+# To build using the ZSTD library - install the library and uncomment the
|
||||
+# ZSTD_SUPPORT line below.
|
||||
+#
|
||||
+#ZSTD_SUPPORT = 1
|
||||
+
|
||||
######## Specifying default compression ########
|
||||
#
|
||||
# The next line specifies which compression algorithm is used by default
|
||||
@@ -177,6 +189,14 @@ LIBS += -llz4
|
||||
COMPRESSORS += lz4
|
||||
endif
|
||||
|
||||
+ifeq ($(ZSTD_SUPPORT),1)
|
||||
+CFLAGS += -DZSTD_SUPPORT
|
||||
+MKSQUASHFS_OBJS += zstd_wrapper.o
|
||||
+UNSQUASHFS_OBJS += zstd_wrapper.o
|
||||
+LIBS += -lzstd
|
||||
+COMPRESSORS += zstd
|
||||
+endif
|
||||
+
|
||||
ifeq ($(XATTR_SUPPORT),1)
|
||||
ifeq ($(XATTR_DEFAULT),1)
|
||||
CFLAGS += -DXATTR_SUPPORT -DXATTR_DEFAULT
|
||||
diff --git a/squashfs-tools/compressor.c b/squashfs-tools/compressor.c
|
||||
index 525e316..02b5e90 100644
|
||||
--- a/squashfs-tools/compressor.c
|
||||
+++ b/squashfs-tools/compressor.c
|
||||
@@ -65,6 +65,13 @@ static struct compressor xz_comp_ops = {
|
||||
extern struct compressor xz_comp_ops;
|
||||
#endif
|
||||
|
||||
+#ifndef ZSTD_SUPPORT
|
||||
+static struct compressor zstd_comp_ops = {
|
||||
+ ZSTD_COMPRESSION, "zstd"
|
||||
+};
|
||||
+#else
|
||||
+extern struct compressor zstd_comp_ops;
|
||||
+#endif
|
||||
|
||||
static struct compressor unknown_comp_ops = {
|
||||
0, "unknown"
|
||||
@@ -77,6 +84,7 @@ struct compressor *compressor[] = {
|
||||
&lzo_comp_ops,
|
||||
&lz4_comp_ops,
|
||||
&xz_comp_ops,
|
||||
+ &zstd_comp_ops,
|
||||
&unknown_comp_ops
|
||||
};
|
||||
|
||||
diff --git a/squashfs-tools/squashfs_fs.h b/squashfs-tools/squashfs_fs.h
|
||||
index 791fe12..afca918 100644
|
||||
--- a/squashfs-tools/squashfs_fs.h
|
||||
+++ b/squashfs-tools/squashfs_fs.h
|
||||
@@ -277,6 +277,7 @@ typedef long long squashfs_inode;
|
||||
#define LZO_COMPRESSION 3
|
||||
#define XZ_COMPRESSION 4
|
||||
#define LZ4_COMPRESSION 5
|
||||
+#define ZSTD_COMPRESSION 6
|
||||
|
||||
struct squashfs_super_block {
|
||||
unsigned int s_magic;
|
||||
diff --git a/squashfs-tools/zstd_wrapper.c b/squashfs-tools/zstd_wrapper.c
|
||||
new file mode 100644
|
||||
index 0000000..dcab75a
|
||||
--- /dev/null
|
||||
+++ b/squashfs-tools/zstd_wrapper.c
|
||||
@@ -0,0 +1,254 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2017
|
||||
+ * Phillip Lougher <phillip@squashfs.org.uk>
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License
|
||||
+ * as published by the Free Software Foundation; either version 2,
|
||||
+ * or (at your option) any later version.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ * zstd_wrapper.c
|
||||
+ *
|
||||
+ * Support for ZSTD compression http://zstd.net
|
||||
+ */
|
||||
+
|
||||
+#include <stdio.h>
|
||||
+#include <string.h>
|
||||
+#include <stdlib.h>
|
||||
+#include <zstd.h>
|
||||
+#include <zstd_errors.h>
|
||||
+
|
||||
+#include "squashfs_fs.h"
|
||||
+#include "zstd_wrapper.h"
|
||||
+#include "compressor.h"
|
||||
+
|
||||
+static int compression_level = ZSTD_DEFAULT_COMPRESSION_LEVEL;
|
||||
+
|
||||
+/*
|
||||
+ * This function is called by the options parsing code in mksquashfs.c
|
||||
+ * to parse any -X compressor option.
|
||||
+ *
|
||||
+ * This function returns:
|
||||
+ * >=0 (number of additional args parsed) on success
|
||||
+ * -1 if the option was unrecognised, or
|
||||
+ * -2 if the option was recognised, but otherwise bad in
|
||||
+ * some way (e.g. invalid parameter)
|
||||
+ *
|
||||
+ * Note: this function sets internal compressor state, but does not
|
||||
+ * pass back the results of the parsing other than success/failure.
|
||||
+ * The zstd_dump_options() function is called later to get the options in
|
||||
+ * a format suitable for writing to the filesystem.
|
||||
+ */
|
||||
+static int zstd_options(char *argv[], int argc)
|
||||
+{
|
||||
+ if (strcmp(argv[0], "-Xcompression-level") == 0) {
|
||||
+ if (argc < 2) {
|
||||
+ fprintf(stderr, "zstd: -Xcompression-level missing "
|
||||
+ "compression level\n");
|
||||
+ fprintf(stderr, "zstd: -Xcompression-level it should "
|
||||
+ "be 1 <= n <= %d\n", ZSTD_maxCLevel());
|
||||
+ goto failed;
|
||||
+ }
|
||||
+
|
||||
+ compression_level = atoi(argv[1]);
|
||||
+ if (compression_level < 1 ||
|
||||
+ compression_level > ZSTD_maxCLevel()) {
|
||||
+ fprintf(stderr, "zstd: -Xcompression-level invalid, it "
|
||||
+ "should be 1 <= n <= %d\n", ZSTD_maxCLevel());
|
||||
+ goto failed;
|
||||
+ }
|
||||
+
|
||||
+ return 1;
|
||||
+ }
|
||||
+
|
||||
+ return -1;
|
||||
+failed:
|
||||
+ return -2;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * This function is called by mksquashfs to dump the parsed
|
||||
+ * compressor options in a format suitable for writing to the
|
||||
+ * compressor options field in the filesystem (stored immediately
|
||||
+ * after the superblock).
|
||||
+ *
|
||||
+ * This function returns a pointer to the compression options structure
|
||||
+ * to be stored (and the size), or NULL if there are no compression
|
||||
+ * options.
|
||||
+ */
|
||||
+static void *zstd_dump_options(int block_size, int *size)
|
||||
+{
|
||||
+ static struct zstd_comp_opts comp_opts;
|
||||
+
|
||||
+ /* don't return anything if the options are all default */
|
||||
+ if (compression_level == ZSTD_DEFAULT_COMPRESSION_LEVEL)
|
||||
+ return NULL;
|
||||
+
|
||||
+ comp_opts.compression_level = compression_level;
|
||||
+
|
||||
+ SQUASHFS_INSWAP_COMP_OPTS(&comp_opts);
|
||||
+
|
||||
+ *size = sizeof(comp_opts);
|
||||
+ return &comp_opts;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * This function is a helper specifically for the append mode of
|
||||
+ * mksquashfs. Its purpose is to set the internal compressor state
|
||||
+ * to the stored compressor options in the passed compressor options
|
||||
+ * structure.
|
||||
+ *
|
||||
+ * In effect this function sets up the compressor options
|
||||
+ * to the same state they were when the filesystem was originally
|
||||
+ * generated, this is to ensure on appending, the compressor uses
|
||||
+ * the same compression options that were used to generate the
|
||||
+ * original filesystem.
|
||||
+ *
|
||||
+ * Note, even if there are no compressor options, this function is still
|
||||
+ * called with an empty compressor structure (size == 0), to explicitly
|
||||
+ * set the default options, this is to ensure any user supplied
|
||||
+ * -X options on the appending mksquashfs command line are over-ridden.
|
||||
+ *
|
||||
+ * This function returns 0 on sucessful extraction of options, and -1 on error.
|
||||
+ */
|
||||
+static int zstd_extract_options(int block_size, void *buffer, int size)
|
||||
+{
|
||||
+ struct zstd_comp_opts *comp_opts = buffer;
|
||||
+
|
||||
+ if (size == 0) {
|
||||
+ /* Set default values */
|
||||
+ compression_level = ZSTD_DEFAULT_COMPRESSION_LEVEL;
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ /* we expect a comp_opts structure of sufficient size to be present */
|
||||
+ if (size < sizeof(*comp_opts))
|
||||
+ goto failed;
|
||||
+
|
||||
+ SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
|
||||
+
|
||||
+ if (comp_opts->compression_level < 1 ||
|
||||
+ comp_opts->compression_level > ZSTD_maxCLevel()) {
|
||||
+ fprintf(stderr, "zstd: bad compression level in compression "
|
||||
+ "options structure\n");
|
||||
+ goto failed;
|
||||
+ }
|
||||
+
|
||||
+ compression_level = comp_opts->compression_level;
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+failed:
|
||||
+ fprintf(stderr, "zstd: error reading stored compressor options from "
|
||||
+ "filesystem!\n");
|
||||
+
|
||||
+ return -1;
|
||||
+}
|
||||
+
|
||||
+static void zstd_display_options(void *buffer, int size)
|
||||
+{
|
||||
+ struct zstd_comp_opts *comp_opts = buffer;
|
||||
+
|
||||
+ /* we expect a comp_opts structure of sufficient size to be present */
|
||||
+ if (size < sizeof(*comp_opts))
|
||||
+ goto failed;
|
||||
+
|
||||
+ SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
|
||||
+
|
||||
+ if (comp_opts->compression_level < 1 ||
|
||||
+ comp_opts->compression_level > ZSTD_maxCLevel()) {
|
||||
+ fprintf(stderr, "zstd: bad compression level in compression "
|
||||
+ "options structure\n");
|
||||
+ goto failed;
|
||||
+ }
|
||||
+
|
||||
+ printf("\tcompression-level %d\n", comp_opts->compression_level);
|
||||
+
|
||||
+ return;
|
||||
+
|
||||
+failed:
|
||||
+ fprintf(stderr, "zstd: error reading stored compressor options from "
|
||||
+ "filesystem!\n");
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * This function is called by mksquashfs to initialise the
|
||||
+ * compressor, before compress() is called.
|
||||
+ *
|
||||
+ * This function returns 0 on success, and -1 on error.
|
||||
+ */
|
||||
+static int zstd_init(void **strm, int block_size, int datablock)
|
||||
+{
|
||||
+ ZSTD_CCtx *cctx = ZSTD_createCCtx();
|
||||
+
|
||||
+ if (!cctx) {
|
||||
+ fprintf(stderr, "zstd: failed to allocate compression "
|
||||
+ "context!\n");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ *strm = cctx;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int zstd_compress(void *strm, void *dest, void *src, int size,
|
||||
+ int block_size, int *error)
|
||||
+{
|
||||
+ const size_t res = ZSTD_compressCCtx((ZSTD_CCtx*)strm, dest, block_size,
|
||||
+ src, size, compression_level);
|
||||
+
|
||||
+ if (ZSTD_isError(res)) {
|
||||
+ /* FIXME:
|
||||
+ * zstd does not expose stable error codes. The error enum may
|
||||
+ * change between versions. Until upstream zstd stablizes the
|
||||
+ * error codes, we have no way of knowing why the error occurs.
|
||||
+ * zstd shouldn't fail to compress any input unless there isn't
|
||||
+ * enough output space. We assume that is the cause and return
|
||||
+ * the special error code for not enough output space.
|
||||
+ */
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ return (int)res;
|
||||
+}
|
||||
+
|
||||
+static int zstd_uncompress(void *dest, void *src, int size, int outsize,
|
||||
+ int *error)
|
||||
+{
|
||||
+ const size_t res = ZSTD_decompress(dest, outsize, src, size);
|
||||
+
|
||||
+ if (ZSTD_isError(res)) {
|
||||
+ fprintf(stderr, "\t%d %d\n", outsize, size);
|
||||
+
|
||||
+ *error = (int)ZSTD_getErrorCode(res);
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ return (int)res;
|
||||
+}
|
||||
+
|
||||
+static void zstd_usage(void)
|
||||
+{
|
||||
+ fprintf(stderr, "\t -Xcompression-level <compression-level>\n");
|
||||
+ fprintf(stderr, "\t\t<compression-level> should be 1 .. %d (default "
|
||||
+ "%d)\n", ZSTD_maxCLevel(), ZSTD_DEFAULT_COMPRESSION_LEVEL);
|
||||
+}
|
||||
+
|
||||
+struct compressor zstd_comp_ops = {
|
||||
+ .init = zstd_init,
|
||||
+ .compress = zstd_compress,
|
||||
+ .uncompress = zstd_uncompress,
|
||||
+ .options = zstd_options,
|
||||
+ .dump_options = zstd_dump_options,
|
||||
+ .extract_options = zstd_extract_options,
|
||||
+ .display_options = zstd_display_options,
|
||||
+ .usage = zstd_usage,
|
||||
+ .id = ZSTD_COMPRESSION,
|
||||
+ .name = "zstd",
|
||||
+ .supported = 1
|
||||
+};
|
||||
diff --git a/squashfs-tools/zstd_wrapper.h b/squashfs-tools/zstd_wrapper.h
|
||||
new file mode 100644
|
||||
index 0000000..4fbef0a
|
||||
--- /dev/null
|
||||
+++ b/squashfs-tools/zstd_wrapper.h
|
||||
@@ -0,0 +1,48 @@
|
||||
+#ifndef ZSTD_WRAPPER_H
|
||||
+#define ZSTD_WRAPPER_H
|
||||
+/*
|
||||
+ * Squashfs
|
||||
+ *
|
||||
+ * Copyright (c) 2017
|
||||
+ * Phillip Lougher <phillip@squashfs.org.uk>
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License
|
||||
+ * as published by the Free Software Foundation; either version 2,
|
||||
+ * or (at your option) any later version.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ * zstd_wrapper.h
|
||||
+ *
|
||||
+ */
|
||||
+
|
||||
+#ifndef linux
|
||||
+#define __BYTE_ORDER BYTE_ORDER
|
||||
+#define __BIG_ENDIAN BIG_ENDIAN
|
||||
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
|
||||
+#else
|
||||
+#include <endian.h>
|
||||
+#endif
|
||||
+
|
||||
+#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
+extern unsigned int inswap_le16(unsigned short);
|
||||
+extern unsigned int inswap_le32(unsigned int);
|
||||
+
|
||||
+#define SQUASHFS_INSWAP_COMP_OPTS(s) { \
|
||||
+ (s)->compression_level = inswap_le32((s)->compression_level); \
|
||||
+}
|
||||
+#else
|
||||
+#define SQUASHFS_INSWAP_COMP_OPTS(s)
|
||||
+#endif
|
||||
+
|
||||
+/* Default compression */
|
||||
+#define ZSTD_DEFAULT_COMPRESSION_LEVEL 15
|
||||
+
|
||||
+struct zstd_comp_opts {
|
||||
+ int compression_level;
|
||||
+};
|
||||
+#endif
|
||||
--
|
||||
2.9.5
|
||||
@@ -1,11 +1,11 @@
|
||||
# Linux Kernel Patch
|
||||
|
||||
There are four pieces, the `xxhash` kernel module, the `zstd_compress` and `zstd_decompress` kernel modules, the BtrFS patch, and the SquashFS patch.
|
||||
The patches are based off of the linux kernel master branch (version 4.10).
|
||||
The patches are based off of the linux kernel master branch.
|
||||
|
||||
## xxHash kernel module
|
||||
|
||||
* The patch is locaed in `xxhash.diff`.
|
||||
* The patch is located in `xxhash.diff`.
|
||||
* The header is in `include/linux/xxhash.h`.
|
||||
* The source is in `lib/xxhash.c`.
|
||||
* `test/XXHashUserLandTest.cpp` contains tests for the patch in userland by mocking the kernel headers.
|
||||
@@ -18,7 +18,7 @@ The patches are based off of the linux kernel master branch (version 4.10).
|
||||
|
||||
## Zstd Kernel modules
|
||||
|
||||
* The (large) patch is locaed in `zstd.diff`, which depends on `xxhash.diff`.
|
||||
* The (large) patch is located in `zstd.diff`, which depends on `xxhash.diff`.
|
||||
* The header is in `include/linux/zstd.h`.
|
||||
* It is split up into `zstd_compress` and `zstd_decompress`, which can be loaded independently.
|
||||
* Source files are in `lib/zstd/`.
|
||||
@@ -42,7 +42,7 @@ The patches are based off of the linux kernel master branch (version 4.10).
|
||||
Benchmarks run on a Ubuntu 14.04 with 2 cores and 4 GiB of RAM.
|
||||
The VM is running on a Macbook Pro with a 3.1 GHz Intel Core i7 processor,
|
||||
16 GB of ram, and a SSD.
|
||||
The kernel running was built from the master branch with the patch (version 4.10).
|
||||
The kernel running was built from the master branch with the patch.
|
||||
|
||||
The compression benchmark is copying 10 copies of the
|
||||
unzipped [silesia corpus](http://mattmahoney.net/dc/silesia.html) into a BtrFS
|
||||
@@ -69,14 +69,14 @@ See `btrfs-benchmark.sh` for details.
|
||||
|
||||
* The patch is located in `squashfs.diff`
|
||||
* Additionally `fs/squashfs/zstd_wrapper.c` is provided as a source for convenience.
|
||||
* The patch has been tested on a 4.10 kernel.
|
||||
* The patch has been tested on the master branch of the kernel.
|
||||
|
||||
### Benchmarks
|
||||
|
||||
Benchmarks run on a Ubuntu 14.04 with 2 cores and 4 GiB of RAM.
|
||||
The VM is running on a Macbook Pro with a 3.1 GHz Intel Core i7 processor,
|
||||
16 GB of ram, and a SSD.
|
||||
The kernel running was built from the master branch with the patch (version 4.10).
|
||||
The kernel running was built from the master branch with the patch.
|
||||
|
||||
The compression benchmark is the file tree from the SquashFS archive found in the
|
||||
Ubuntu 16.10 desktop image (ubuntu-16.10-desktop-amd64.iso).
|
||||
|
||||
@@ -10,29 +10,27 @@
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 021110-1307, USA.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/bio.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/refcount.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/zstd.h>
|
||||
#include "compression.h"
|
||||
|
||||
#define ZSTD_BTRFS_MAX_WINDOWLOG 17
|
||||
#define ZSTD_BTRFS_MAX_INPUT (1 << ZSTD_BTRFS_MAX_WINDOWLOG)
|
||||
#define ZSTD_BTRFS_DEFAULT_LEVEL 3
|
||||
|
||||
static ZSTD_parameters zstd_get_btrfs_parameters(size_t src_len)
|
||||
{
|
||||
ZSTD_parameters params = ZSTD_getParams(1, src_len, 0);
|
||||
ZSTD_parameters params = ZSTD_getParams(ZSTD_BTRFS_DEFAULT_LEVEL,
|
||||
src_len, 0);
|
||||
|
||||
if (params.cParams.windowLog > ZSTD_BTRFS_MAX_WINDOWLOG)
|
||||
params.cParams.windowLog = ZSTD_BTRFS_MAX_WINDOWLOG;
|
||||
@@ -51,7 +49,7 @@ static void zstd_free_workspace(struct list_head *ws)
|
||||
{
|
||||
struct workspace *workspace = list_entry(ws, struct workspace, list);
|
||||
|
||||
vfree(workspace->mem);
|
||||
kvfree(workspace->mem);
|
||||
kfree(workspace->buf);
|
||||
kfree(workspace);
|
||||
}
|
||||
@@ -62,15 +60,15 @@ static struct list_head *zstd_alloc_workspace(void)
|
||||
zstd_get_btrfs_parameters(ZSTD_BTRFS_MAX_INPUT);
|
||||
struct workspace *workspace;
|
||||
|
||||
workspace = kzalloc(sizeof(*workspace), GFP_NOFS);
|
||||
workspace = kzalloc(sizeof(*workspace), GFP_KERNEL);
|
||||
if (!workspace)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
workspace->size = max_t(size_t,
|
||||
ZSTD_CStreamWorkspaceBound(params.cParams),
|
||||
ZSTD_DStreamWorkspaceBound(ZSTD_BTRFS_MAX_INPUT));
|
||||
workspace->mem = vmalloc(workspace->size);
|
||||
workspace->buf = kmalloc(PAGE_SIZE, GFP_NOFS);
|
||||
workspace->mem = kvmalloc(workspace->size, GFP_KERNEL);
|
||||
workspace->buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (!workspace->mem || !workspace->buf)
|
||||
goto fail;
|
||||
|
||||
@@ -262,12 +260,13 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int zstd_decompress_bio(struct list_head *ws, struct page **pages_in,
|
||||
u64 disk_start,
|
||||
struct bio *orig_bio,
|
||||
size_t srclen)
|
||||
static int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb)
|
||||
{
|
||||
struct workspace *workspace = list_entry(ws, struct workspace, list);
|
||||
struct page **pages_in = cb->compressed_pages;
|
||||
u64 disk_start = cb->start;
|
||||
struct bio *orig_bio = cb->orig_bio;
|
||||
size_t srclen = cb->compressed_len;
|
||||
ZSTD_DStream *stream;
|
||||
int ret = 0;
|
||||
unsigned long page_in_index = 0;
|
||||
|
||||
@@ -14,10 +14,6 @@
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* zstd_wrapper.c
|
||||
*/
|
||||
|
||||
@@ -36,15 +32,18 @@
|
||||
struct workspace {
|
||||
void *mem;
|
||||
size_t mem_size;
|
||||
size_t window_size;
|
||||
};
|
||||
|
||||
static void *zstd_init(struct squashfs_sb_info *msblk, void *buff)
|
||||
{
|
||||
struct workspace *wksp = kmalloc(sizeof(*wksp), GFP_KERNEL);
|
||||
|
||||
if (wksp == NULL)
|
||||
goto failed;
|
||||
wksp->mem_size = ZSTD_DStreamWorkspaceBound(max_t(size_t,
|
||||
msblk->block_size, SQUASHFS_METADATA_SIZE));
|
||||
wksp->window_size = max_t(size_t,
|
||||
msblk->block_size, SQUASHFS_METADATA_SIZE);
|
||||
wksp->mem_size = ZSTD_DStreamWorkspaceBound(wksp->window_size);
|
||||
wksp->mem = vmalloc(wksp->mem_size);
|
||||
if (wksp->mem == NULL)
|
||||
goto failed;
|
||||
@@ -80,7 +79,7 @@ static int zstd_uncompress(struct squashfs_sb_info *msblk, void *strm,
|
||||
ZSTD_inBuffer in_buf = { NULL, 0, 0 };
|
||||
ZSTD_outBuffer out_buf = { NULL, 0, 0 };
|
||||
|
||||
stream = ZSTD_initDStream(wksp->mem_size, wksp->mem, wksp->mem_size);
|
||||
stream = ZSTD_initDStream(wksp->window_size, wksp->mem, wksp->mem_size);
|
||||
|
||||
if (!stream) {
|
||||
ERROR("Failed to initialize zstd decompressor\n");
|
||||
@@ -93,6 +92,7 @@ static int zstd_uncompress(struct squashfs_sb_info *msblk, void *strm,
|
||||
do {
|
||||
if (in_buf.pos == in_buf.size && k < b) {
|
||||
int avail = min(length, msblk->devblksize - offset);
|
||||
|
||||
length -= avail;
|
||||
in_buf.src = bh[k]->b_data + offset;
|
||||
in_buf.size = avail;
|
||||
@@ -103,8 +103,9 @@ static int zstd_uncompress(struct squashfs_sb_info *msblk, void *strm,
|
||||
if (out_buf.pos == out_buf.size) {
|
||||
out_buf.dst = squashfs_next_page(output);
|
||||
if (out_buf.dst == NULL) {
|
||||
/* shouldn't run out of pages before stream is
|
||||
* done */
|
||||
/* Shouldn't run out of pages
|
||||
* before stream is done.
|
||||
*/
|
||||
squashfs_finish_page(output);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -583,7 +583,7 @@ void ZSTD_seqToCodes(const seqStore_t *seqStorePtr)
|
||||
mlCodeTable[seqStorePtr->longLengthPos] = MaxML;
|
||||
}
|
||||
|
||||
ZSTD_STATIC size_t ZSTD_compressSequences(ZSTD_CCtx *zc, void *dst, size_t dstCapacity, size_t srcSize)
|
||||
ZSTD_STATIC size_t ZSTD_compressSequences_internal(ZSTD_CCtx *zc, void *dst, size_t dstCapacity)
|
||||
{
|
||||
const int longOffsets = zc->params.cParams.windowLog > STREAM_ACCUMULATOR_MIN;
|
||||
const seqStore_t *seqStorePtr = &(zc->seqStore);
|
||||
@@ -636,7 +636,7 @@ ZSTD_STATIC size_t ZSTD_compressSequences(ZSTD_CCtx *zc, void *dst, size_t dstCa
|
||||
else
|
||||
op[0] = 0xFF, ZSTD_writeLE16(op + 1, (U16)(nbSeq - LONGNBSEQ)), op += 3;
|
||||
if (nbSeq == 0)
|
||||
goto _check_compressibility;
|
||||
return op - ostart;
|
||||
|
||||
/* seqHead : flags for FSE encoding type */
|
||||
seqHead = op++;
|
||||
@@ -826,28 +826,33 @@ ZSTD_STATIC size_t ZSTD_compressSequences(ZSTD_CCtx *zc, void *dst, size_t dstCa
|
||||
op += streamSize;
|
||||
}
|
||||
}
|
||||
|
||||
/* check compressibility */
|
||||
_check_compressibility:
|
||||
{
|
||||
size_t const minGain = ZSTD_minGain(srcSize);
|
||||
size_t const maxCSize = srcSize - minGain;
|
||||
if ((size_t)(op - ostart) >= maxCSize) {
|
||||
zc->flagStaticHufTable = HUF_repeat_none;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* confirm repcodes */
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ZSTD_REP_NUM; i++)
|
||||
zc->rep[i] = zc->repToConfirm[i];
|
||||
}
|
||||
|
||||
return op - ostart;
|
||||
}
|
||||
|
||||
ZSTD_STATIC size_t ZSTD_compressSequences(ZSTD_CCtx *zc, void *dst, size_t dstCapacity, size_t srcSize)
|
||||
{
|
||||
size_t const cSize = ZSTD_compressSequences_internal(zc, dst, dstCapacity);
|
||||
size_t const minGain = ZSTD_minGain(srcSize);
|
||||
size_t const maxCSize = srcSize - minGain;
|
||||
/* If the srcSize <= dstCapacity, then there is enough space to write a
|
||||
* raw uncompressed block. Since we ran out of space, the block must not
|
||||
* be compressible, so fall back to a raw uncompressed block.
|
||||
*/
|
||||
int const uncompressibleError = cSize == ERROR(dstSize_tooSmall) && srcSize <= dstCapacity;
|
||||
int i;
|
||||
|
||||
if (ZSTD_isError(cSize) && !uncompressibleError)
|
||||
return cSize;
|
||||
if (cSize >= maxCSize || uncompressibleError) {
|
||||
zc->flagStaticHufTable = HUF_repeat_none;
|
||||
return 0;
|
||||
}
|
||||
/* confirm repcodes */
|
||||
for (i = 0; i < ZSTD_REP_NUM; i++)
|
||||
zc->rep[i] = zc->repToConfirm[i];
|
||||
return cSize;
|
||||
}
|
||||
|
||||
/*! ZSTD_storeSeq() :
|
||||
Store a sequence (literal length, literals, offset code and match length code) into seqStore_t.
|
||||
`offsetCode` : distance to match, or 0 == repCode.
|
||||
|
||||
@@ -998,6 +998,8 @@ static seq_t ZSTD_decodeSequence(seqState_t *seqState)
|
||||
BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */
|
||||
FSE_updateState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */
|
||||
|
||||
seq.match = NULL;
|
||||
|
||||
return seq;
|
||||
}
|
||||
|
||||
|
||||
@@ -525,7 +525,7 @@ static void HUF_encodeSymbol(BIT_CStream_t *bitCPtr, U32 symbol, const HUF_CElt
|
||||
|
||||
size_t HUF_compressBound(size_t size) { return HUF_COMPRESSBOUND(size); }
|
||||
|
||||
#define HUF_FLUSHBITS(s) (fast ? BIT_flushBitsFast(s) : BIT_flushBits(s))
|
||||
#define HUF_FLUSHBITS(s) BIT_flushBits(s)
|
||||
|
||||
#define HUF_FLUSHBITS_1(stream) \
|
||||
if (sizeof((stream)->bitContainer) * 8 < HUF_TABLELOG_MAX * 2 + 7) \
|
||||
@@ -542,7 +542,6 @@ size_t HUF_compress1X_usingCTable(void *dst, size_t dstSize, const void *src, si
|
||||
BYTE *const oend = ostart + dstSize;
|
||||
BYTE *op = ostart;
|
||||
size_t n;
|
||||
const unsigned fast = (dstSize >= HUF_BLOCKBOUND(srcSize));
|
||||
BIT_CStream_t bitC;
|
||||
|
||||
/* init */
|
||||
|
||||
@@ -126,35 +126,29 @@ static const U32 OF_defaultNormLog = OF_DEFAULTNORMLOG;
|
||||
/*-*******************************************
|
||||
* Shared functions to include for inlining
|
||||
*********************************************/
|
||||
static void ZSTD_copy8(void *dst, const void *src) { memcpy(dst, src, 8); }
|
||||
#define COPY8(d, s) \
|
||||
{ \
|
||||
ZSTD_copy8(d, s); \
|
||||
d += 8; \
|
||||
s += 8; \
|
||||
}
|
||||
|
||||
ZSTD_STATIC void ZSTD_copy8(void *dst, const void *src) {
|
||||
memcpy(dst, src, 8);
|
||||
}
|
||||
/*! ZSTD_wildcopy() :
|
||||
* custom version of memcpy(), can copy up to 7 bytes too many (8 bytes if length==0) */
|
||||
#define WILDCOPY_OVERLENGTH 8
|
||||
ZSTD_STATIC void ZSTD_wildcopy(void *dst, const void *src, ptrdiff_t length)
|
||||
{
|
||||
const BYTE *ip = (const BYTE *)src;
|
||||
BYTE *op = (BYTE *)dst;
|
||||
BYTE *const oend = op + length;
|
||||
do
|
||||
COPY8(op, ip)
|
||||
while (op < oend);
|
||||
}
|
||||
|
||||
ZSTD_STATIC void ZSTD_wildcopy_e(void *dst, const void *src, void *dstEnd) /* should be faster for decoding, but strangely, not verified on all platform */
|
||||
{
|
||||
const BYTE *ip = (const BYTE *)src;
|
||||
BYTE *op = (BYTE *)dst;
|
||||
BYTE *const oend = (BYTE *)dstEnd;
|
||||
do
|
||||
COPY8(op, ip)
|
||||
while (op < oend);
|
||||
const BYTE* ip = (const BYTE*)src;
|
||||
BYTE* op = (BYTE*)dst;
|
||||
BYTE* const oend = op + length;
|
||||
/* Work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81388.
|
||||
* Avoid the bad case where the loop only runs once by handling the
|
||||
* special case separately. This doesn't trigger the bug because it
|
||||
* doesn't involve pointer/integer overflow.
|
||||
*/
|
||||
if (length <= 8)
|
||||
return ZSTD_copy8(dst, src);
|
||||
do {
|
||||
ZSTD_copy8(op, ip);
|
||||
op += 8;
|
||||
ip += 8;
|
||||
} while (op < oend);
|
||||
}
|
||||
|
||||
/*-*******************************************
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# ################################################################
|
||||
# Copyright (c) 2016-present, Facebook, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This source code is licensed under the BSD-style license found in the
|
||||
# LICENSE file in the root directory of this source tree. An additional grant
|
||||
# of patent rights can be found in the PATENTS file in the same directory.
|
||||
# ################################################################
|
||||
|
||||
# This Makefile presumes libzstd is installed, using `sudo make install`
|
||||
|
||||
CPPFLAGS+= -I../../lib/common
|
||||
CFLAGS ?= -O3
|
||||
DEBUGFLAGS = -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \
|
||||
-Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement \
|
||||
-Wstrict-prototypes -Wundef -Wpointer-arith -Wformat-security \
|
||||
-Wvla -Wformat=2 -Winit-self -Wfloat-equal -Wwrite-strings \
|
||||
-Wredundant-decls
|
||||
CFLAGS += $(DEBUGFLAGS) $(MOREFLAGS)
|
||||
FLAGS = $(CPPFLAGS) $(CFLAGS)
|
||||
|
||||
LDFLAGS += -lzstd
|
||||
|
||||
.PHONY: default all clean
|
||||
|
||||
default: all
|
||||
|
||||
all: ldm
|
||||
|
||||
ldm: ldm_common.c ldm.c main.c
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@
|
||||
|
||||
clean:
|
||||
@rm -f core *.o tmp* result* *.ldm *.ldm.dec \
|
||||
ldm
|
||||
@echo Cleaning completed
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
This is a compression algorithm focused on finding long distance matches.
|
||||
|
||||
It is based upon lz4 and uses nearly the same block format (github.com/lz4/lz4/blob/dev/doc/lz4_Block_format.md). The number of bytes to encode the offset is four instead of two in lz4 to reflect the longer distance matching. The block format is described in `ldm.h`.
|
||||
|
||||
### Build
|
||||
|
||||
Run `make`.
|
||||
|
||||
### Compressing a file
|
||||
|
||||
`ldm <filename>`
|
||||
|
||||
Decompression and verification can be enabled by defining `DECOMPRESS_AND_VERIFY` in `main.c`.
|
||||
The output file names are as follows:
|
||||
- `<filename>.ldm` : compressed file
|
||||
- `<filename>.ldm.dec` : decompressed file
|
||||
|
||||
### Parameters
|
||||
|
||||
There are various parameters that can be tuned. These parameters can be tuned in `ldm.h` or, alternatively if `ldm_params.h` is included, in `ldm_params.h` (for easier configuration).
|
||||
|
||||
The parameters are as follows and must all be defined:
|
||||
- `LDM_MEMORY_USAGE` : the memory usage of the underlying hash table in bytes.
|
||||
- `HASH_BUCKET_SIZE_LOG` : the log size of each bucket in the hash table (used in collision resolution).
|
||||
- `LDM_LAG` : the lag (in bytes) in inserting entries into the hash table.
|
||||
- `LDM_WINDOW_SIZE_LOG` : the log maximum window size when searching for matches.
|
||||
- `LDM_MIN_MATCH_LENGTH` : the minimum match length.
|
||||
- `INSERT_BY_TAG` : insert entries into the hash table as a function of the hash. This increases speed by reducing the number of hash table lookups and match comparisons. Certain hashes will never be inserted.
|
||||
- `USE_CHECKSUM` : store a checksum with the hash table entries for faster comparison. This halves the number of entries the hash table can contain.
|
||||
|
||||
The optional parameter `HASH_ONLY_EVERY_LOG` is the log inverse frequency of insertion into the hash table. That is, an entry is inserted approximately every `1 << HASH_ONLY_EVERY_LOG` times. If this parameter is not defined, the value is computed as a function of the window size and memory usage to approximate an even coverage of the window.
|
||||
|
||||
|
||||
### Benchmark
|
||||
|
||||
Below is a comparison of various compression methods on a tar of four versions of llvm (versions `3.9.0`, `3.9.1`, `4.0.0`, `4.0.1`) with a total size of `727900160` B.
|
||||
|
||||
| Method | Size | Ratio |
|
||||
|:---|---:|---:|
|
||||
|lrzip -p 32 -n -w 1 | `369968714` | `1.97`|
|
||||
|ldm | `209391361` | `3.48`|
|
||||
|lz4 | `189954338` | `3.83`|
|
||||
|lrzip -p 32 -l -w 1 | `163940343` | `4.44`|
|
||||
|zstd -1 | `126080293` | `5.77`|
|
||||
|lrzip -p 32 -n | `124821009` | `5.83`|
|
||||
|lrzip -p 32 -n -w 1 & zstd -1 | `120317909` | `6.05`|
|
||||
|zstd -3 -o | `115290952` | `6.31`|
|
||||
|lrzip -p 32 -g -L 9 -w 1 | `107168979` | `6.79`|
|
||||
|zstd -6 -o | `102772098` | `7.08`|
|
||||
|zstd -T16 -9 | `98040470` | `7.42`|
|
||||
|lrzip -p 32 -n -w 1 & zstd -T32 -19 | `88050289` | `8.27`|
|
||||
|zstd -T32 -19 | `83626098` | `8.70`|
|
||||
|lrzip -p 32 -n & zstd -1 | `36335117` | `20.03`|
|
||||
|ldm & zstd -6 | `32856232` | `22.15`|
|
||||
|lrzip -p 32 -g -L 9 | `32243594` | `22.58`|
|
||||
|lrzip -p 32 -n & zstd -6 | `30954572` | `23.52`|
|
||||
|lrzip -p 32 -n & zstd -T32 -19 | `26472064` | `27.50`|
|
||||
|
||||
The method marked `ldm` was run with the following parameters:
|
||||
|
||||
| Parameter | Value |
|
||||
|:---|---:|
|
||||
| `LDM_MEMORY_USAGE` | `23`|
|
||||
|`HASH_BUCKET_SIZE_LOG` | `3`|
|
||||
|`LDM_LAG` | `0`|
|
||||
|`LDM_WINDOW_SIZE_LOG` | `28`|
|
||||
|`LDM_MIN_MATCH_LENGTH`| `64`|
|
||||
|`INSERT_BY_TAG` | `1`|
|
||||
|`USE_CHECKSUM` | `1`|
|
||||
|
||||
The compression speed was `220.5 MB/s`.
|
||||
|
||||
### Parameter selection
|
||||
|
||||
Below is a brief discussion of the effects of the parameters on the speed and compression ratio.
|
||||
|
||||
#### Speed
|
||||
|
||||
A large bottleneck in terms of speed is finding the matches and comparing to see if they are greater than the minimum match length. Generally:
|
||||
- The fewer matches found (or the lower the percentage of the literals matched), the slower the algorithm will behave.
|
||||
- Increasing `HASH_ONLY_EVERY_LOG` results in fewer inserts and, if `INSERT_BY_TAG` is set, fewer lookups in the table. This has a large effect on speed, as well as compression ratio.
|
||||
- If `HASH_ONLY_EVERY_LOG` is not set, its value is calculated based on `LDM_WINDOW_SIZE_LOG` and `LDM_MEMORY_USAGE`. Increasing `LDM_WINDOW_SIZE_LOG` has the effect of increasing `HASH_ONLY_EVERY_LOG` and increasing `LDM_MEMORY_USAGE` decreases `HASH_ONLY_EVERY_LOG`.
|
||||
- `USE_CHECKSUM` generally improves speed with hash table lookups.
|
||||
|
||||
#### Compression ratio
|
||||
|
||||
The compression ratio is highly correlated with the coverage of matches. As a long distance matcher, the algorithm was designed to "optimize" for long distance matches outside the zstd compression window. The compression ratio after recompressing the output of the long-distance matcher with zstd was a more important signal in development than the raw compression ratio itself.
|
||||
|
||||
Generally, increasing `LDM_MEMORY_USAGE` will improve the compression ratio. However when using the default computed value of `HASH_ONLY_EVERY_LOG`, this increases the frequency of insertion and lookup in the table and thus may result in a decrease in speed.
|
||||
|
||||
Below is a table showing the speed and compression ratio when compressing the llvm tar (as described above) using different settings for `LDM_MEMORY_USAGE`. The other parameters were the same as used in the benchmark above.
|
||||
|
||||
| `LDM_MEMORY_USAGE` | Ratio | Speed (MB/s) | Ratio after zstd -6 |
|
||||
|---:| ---: | ---: | ---: |
|
||||
| `18` | `1.85` | `232.4` | `10.92` |
|
||||
| `21` | `2.79` | `233.9` | `15.92` |
|
||||
| `23` | `3.48` | `220.5` | `18.29` |
|
||||
| `25` | `4.56` | `140.8` | `19.21` |
|
||||
|
||||
### Compression statistics
|
||||
|
||||
Compression statistics (and the configuration) can be enabled/disabled via `COMPUTE_STATS` and `OUTPUT_CONFIGURATION` in `ldm.h`.
|
||||
@@ -0,0 +1,857 @@
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ldm.h"
|
||||
|
||||
#define LDM_HASHTABLESIZE (1 << (LDM_MEMORY_USAGE))
|
||||
#define LDM_HASHTABLESIZE_U32 ((LDM_HASHTABLESIZE) >> 2)
|
||||
#define LDM_HASHTABLESIZE_U64 ((LDM_HASHTABLESIZE) >> 3)
|
||||
|
||||
#if USE_CHECKSUM
|
||||
#define LDM_HASH_ENTRY_SIZE_LOG 3
|
||||
#else
|
||||
#define LDM_HASH_ENTRY_SIZE_LOG 2
|
||||
#endif
|
||||
|
||||
// Entries are inserted into the table HASH_ONLY_EVERY + 1 times "on average".
|
||||
#ifndef HASH_ONLY_EVERY_LOG
|
||||
#define HASH_ONLY_EVERY_LOG (LDM_WINDOW_SIZE_LOG-((LDM_MEMORY_USAGE)-(LDM_HASH_ENTRY_SIZE_LOG)))
|
||||
#endif
|
||||
|
||||
#define HASH_ONLY_EVERY ((1 << (HASH_ONLY_EVERY_LOG)) - 1)
|
||||
|
||||
#define HASH_BUCKET_SIZE (1 << (HASH_BUCKET_SIZE_LOG))
|
||||
#define NUM_HASH_BUCKETS_LOG ((LDM_MEMORY_USAGE)-(LDM_HASH_ENTRY_SIZE_LOG)-(HASH_BUCKET_SIZE_LOG))
|
||||
|
||||
#define HASH_CHAR_OFFSET 10
|
||||
|
||||
// Take the first match in the hash bucket only.
|
||||
//#define ZSTD_SKIP
|
||||
|
||||
static const U64 prime8bytes = 11400714785074694791ULL;
|
||||
|
||||
// Type of the small hash used to index into the hash table.
|
||||
typedef U32 hash_t;
|
||||
|
||||
#if USE_CHECKSUM
|
||||
typedef struct LDM_hashEntry {
|
||||
U32 offset;
|
||||
U32 checksum;
|
||||
} LDM_hashEntry;
|
||||
#else
|
||||
typedef struct LDM_hashEntry {
|
||||
U32 offset;
|
||||
} LDM_hashEntry;
|
||||
#endif
|
||||
|
||||
struct LDM_compressStats {
|
||||
U32 windowSizeLog, hashTableSizeLog;
|
||||
U32 numMatches;
|
||||
U64 totalMatchLength;
|
||||
U64 totalLiteralLength;
|
||||
U64 totalOffset;
|
||||
|
||||
U32 matchLengthHistogram[32];
|
||||
|
||||
U32 minOffset, maxOffset;
|
||||
U32 offsetHistogram[32];
|
||||
};
|
||||
|
||||
typedef struct LDM_hashTable LDM_hashTable;
|
||||
|
||||
struct LDM_CCtx {
|
||||
size_t isize; /* Input size */
|
||||
size_t maxOSize; /* Maximum output size */
|
||||
|
||||
const BYTE *ibase; /* Base of input */
|
||||
const BYTE *ip; /* Current input position */
|
||||
const BYTE *iend; /* End of input */
|
||||
|
||||
// Maximum input position such that hashing at the position does not exceed
|
||||
// end of input.
|
||||
const BYTE *ihashLimit;
|
||||
|
||||
// Maximum input position such that finding a match of at least the minimum
|
||||
// match length does not exceed end of input.
|
||||
const BYTE *imatchLimit;
|
||||
|
||||
const BYTE *obase; /* Base of output */
|
||||
BYTE *op; /* Output */
|
||||
|
||||
const BYTE *anchor; /* Anchor to start of current (match) block */
|
||||
|
||||
LDM_compressStats stats; /* Compression statistics */
|
||||
|
||||
LDM_hashTable *hashTable;
|
||||
|
||||
const BYTE *lastPosHashed; /* Last position hashed */
|
||||
U64 lastHash;
|
||||
|
||||
const BYTE *nextIp; // TODO: this is redundant (ip + step)
|
||||
const BYTE *nextPosHashed;
|
||||
U64 nextHash;
|
||||
|
||||
unsigned step; // ip step, should be 1.
|
||||
|
||||
const BYTE *lagIp;
|
||||
U64 lagHash;
|
||||
};
|
||||
|
||||
struct LDM_hashTable {
|
||||
U32 numBuckets; // The number of buckets.
|
||||
U32 numEntries; // numBuckets * HASH_BUCKET_SIZE.
|
||||
|
||||
LDM_hashEntry *entries;
|
||||
BYTE *bucketOffsets; // A pointer (per bucket) to the next insert position.
|
||||
};
|
||||
|
||||
static void HASH_destroyTable(LDM_hashTable *table) {
|
||||
free(table->entries);
|
||||
free(table->bucketOffsets);
|
||||
free(table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a hash table that can contain size elements.
|
||||
* The number of buckets is determined by size >> HASH_BUCKET_SIZE_LOG.
|
||||
*
|
||||
* Returns NULL if table creation failed.
|
||||
*/
|
||||
static LDM_hashTable *HASH_createTable(U32 size) {
|
||||
LDM_hashTable *table = malloc(sizeof(LDM_hashTable));
|
||||
if (!table) return NULL;
|
||||
|
||||
table->numBuckets = size >> HASH_BUCKET_SIZE_LOG;
|
||||
table->numEntries = size;
|
||||
table->entries = calloc(size, sizeof(LDM_hashEntry));
|
||||
table->bucketOffsets = calloc(size >> HASH_BUCKET_SIZE_LOG, sizeof(BYTE));
|
||||
|
||||
if (!table->entries || !table->bucketOffsets) {
|
||||
HASH_destroyTable(table);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
static LDM_hashEntry *getBucket(const LDM_hashTable *table, const hash_t hash) {
|
||||
return table->entries + (hash << HASH_BUCKET_SIZE_LOG);
|
||||
}
|
||||
|
||||
static unsigned ZSTD_NbCommonBytes (register size_t val) {
|
||||
if (MEM_isLittleEndian()) {
|
||||
if (MEM_64bits()) {
|
||||
# if defined(_MSC_VER) && defined(_WIN64)
|
||||
unsigned long r = 0;
|
||||
_BitScanForward64( &r, (U64)val );
|
||||
return (unsigned)(r>>3);
|
||||
# elif defined(__GNUC__) && (__GNUC__ >= 3)
|
||||
return (__builtin_ctzll((U64)val) >> 3);
|
||||
# else
|
||||
static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2,
|
||||
0, 3, 1, 3, 1, 4, 2, 7,
|
||||
0, 2, 3, 6, 1, 5, 3, 5,
|
||||
1, 3, 4, 4, 2, 5, 6, 7,
|
||||
7, 0, 1, 2, 3, 3, 4, 6,
|
||||
2, 6, 5, 5, 3, 4, 5, 6,
|
||||
7, 1, 2, 4, 6, 4, 4, 5,
|
||||
7, 2, 6, 5, 7, 6, 7, 7 };
|
||||
return DeBruijnBytePos[
|
||||
((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58];
|
||||
# endif
|
||||
} else { /* 32 bits */
|
||||
# if defined(_MSC_VER)
|
||||
unsigned long r=0;
|
||||
_BitScanForward( &r, (U32)val );
|
||||
return (unsigned)(r>>3);
|
||||
# elif defined(__GNUC__) && (__GNUC__ >= 3)
|
||||
return (__builtin_ctz((U32)val) >> 3);
|
||||
# else
|
||||
static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0,
|
||||
3, 2, 2, 1, 3, 2, 0, 1,
|
||||
3, 3, 1, 2, 2, 2, 2, 0,
|
||||
3, 1, 2, 0, 1, 0, 1, 1 };
|
||||
return DeBruijnBytePos[
|
||||
((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];
|
||||
# endif
|
||||
}
|
||||
} else { /* Big Endian CPU */
|
||||
if (MEM_64bits()) {
|
||||
# if defined(_MSC_VER) && defined(_WIN64)
|
||||
unsigned long r = 0;
|
||||
_BitScanReverse64( &r, val );
|
||||
return (unsigned)(r>>3);
|
||||
# elif defined(__GNUC__) && (__GNUC__ >= 3)
|
||||
return (__builtin_clzll(val) >> 3);
|
||||
# else
|
||||
unsigned r;
|
||||
/* calculate this way due to compiler complaining in 32-bits mode */
|
||||
const unsigned n32 = sizeof(size_t)*4;
|
||||
if (!(val>>n32)) { r=4; } else { r=0; val>>=n32; }
|
||||
if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }
|
||||
r += (!val);
|
||||
return r;
|
||||
# endif
|
||||
} else { /* 32 bits */
|
||||
# if defined(_MSC_VER)
|
||||
unsigned long r = 0;
|
||||
_BitScanReverse( &r, (unsigned long)val );
|
||||
return (unsigned)(r>>3);
|
||||
# elif defined(__GNUC__) && (__GNUC__ >= 3)
|
||||
return (__builtin_clz((U32)val) >> 3);
|
||||
# else
|
||||
unsigned r;
|
||||
if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; }
|
||||
r += (!val);
|
||||
return r;
|
||||
# endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// From lib/compress/zstd_compress.c
|
||||
static size_t ZSTD_count(const BYTE *pIn, const BYTE *pMatch,
|
||||
const BYTE *const pInLimit) {
|
||||
const BYTE * const pStart = pIn;
|
||||
const BYTE * const pInLoopLimit = pInLimit - (sizeof(size_t)-1);
|
||||
|
||||
while (pIn < pInLoopLimit) {
|
||||
size_t const diff = MEM_readST(pMatch) ^ MEM_readST(pIn);
|
||||
if (!diff) {
|
||||
pIn += sizeof(size_t);
|
||||
pMatch += sizeof(size_t);
|
||||
continue;
|
||||
}
|
||||
pIn += ZSTD_NbCommonBytes(diff);
|
||||
return (size_t)(pIn - pStart);
|
||||
}
|
||||
|
||||
if (MEM_64bits()) {
|
||||
if ((pIn < (pInLimit - 3)) && (MEM_read32(pMatch) == MEM_read32(pIn))) {
|
||||
pIn += 4;
|
||||
pMatch += 4;
|
||||
}
|
||||
}
|
||||
if ((pIn < (pInLimit - 1)) && (MEM_read16(pMatch) == MEM_read16(pIn))) {
|
||||
pIn += 2;
|
||||
pMatch += 2;
|
||||
}
|
||||
if ((pIn < pInLimit) && (*pMatch == *pIn)) {
|
||||
pIn++;
|
||||
}
|
||||
return (size_t)(pIn - pStart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count number of bytes that match backwards before pIn and pMatch.
|
||||
*
|
||||
* We count only bytes where pMatch > pBase and pIn > pAnchor.
|
||||
*/
|
||||
static size_t countBackwardsMatch(const BYTE *pIn, const BYTE *pAnchor,
|
||||
const BYTE *pMatch, const BYTE *pBase) {
|
||||
size_t matchLength = 0;
|
||||
while (pIn > pAnchor && pMatch > pBase && pIn[-1] == pMatch[-1]) {
|
||||
pIn--;
|
||||
pMatch--;
|
||||
matchLength++;
|
||||
}
|
||||
return matchLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pointer to the entry in the hash table matching the hash and
|
||||
* checksum with the "longest match length" as defined below. The forward and
|
||||
* backward match lengths are written to *pForwardMatchLength and
|
||||
* *pBackwardMatchLength.
|
||||
*
|
||||
* The match length is defined based on cctx->ip and the entry's offset.
|
||||
* The forward match is computed from cctx->ip and entry->offset + cctx->ibase.
|
||||
* The backward match is computed backwards from cctx->ip and
|
||||
* cctx->ibase only if the forward match is longer than LDM_MIN_MATCH_LENGTH.
|
||||
*/
|
||||
static LDM_hashEntry *HASH_getBestEntry(const LDM_CCtx *cctx,
|
||||
const hash_t hash,
|
||||
const U32 checksum,
|
||||
U64 *pForwardMatchLength,
|
||||
U64 *pBackwardMatchLength) {
|
||||
LDM_hashTable *table = cctx->hashTable;
|
||||
LDM_hashEntry *bucket = getBucket(table, hash);
|
||||
LDM_hashEntry *cur;
|
||||
LDM_hashEntry *bestEntry = NULL;
|
||||
U64 bestMatchLength = 0;
|
||||
#if !(USE_CHECKSUM)
|
||||
(void)checksum;
|
||||
#endif
|
||||
for (cur = bucket; cur < bucket + HASH_BUCKET_SIZE; ++cur) {
|
||||
const BYTE *pMatch = cur->offset + cctx->ibase;
|
||||
|
||||
// Check checksum for faster check.
|
||||
#if USE_CHECKSUM
|
||||
if (cur->checksum == checksum &&
|
||||
cctx->ip - pMatch <= LDM_WINDOW_SIZE) {
|
||||
#else
|
||||
if (cctx->ip - pMatch <= LDM_WINDOW_SIZE) {
|
||||
#endif
|
||||
U64 forwardMatchLength = ZSTD_count(cctx->ip, pMatch, cctx->iend);
|
||||
U64 backwardMatchLength, totalMatchLength;
|
||||
|
||||
// Only take matches where the forward match length is large enough
|
||||
// for speed.
|
||||
if (forwardMatchLength < LDM_MIN_MATCH_LENGTH) {
|
||||
continue;
|
||||
}
|
||||
|
||||
backwardMatchLength =
|
||||
countBackwardsMatch(cctx->ip, cctx->anchor,
|
||||
cur->offset + cctx->ibase,
|
||||
cctx->ibase);
|
||||
|
||||
totalMatchLength = forwardMatchLength + backwardMatchLength;
|
||||
|
||||
if (totalMatchLength >= bestMatchLength) {
|
||||
bestMatchLength = totalMatchLength;
|
||||
*pForwardMatchLength = forwardMatchLength;
|
||||
*pBackwardMatchLength = backwardMatchLength;
|
||||
|
||||
bestEntry = cur;
|
||||
#ifdef ZSTD_SKIP
|
||||
return cur;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bestEntry != NULL) {
|
||||
return bestEntry;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert an entry into the hash table. The table uses a "circular buffer",
|
||||
* with the oldest entry overwritten.
|
||||
*/
|
||||
static void HASH_insert(LDM_hashTable *table,
|
||||
const hash_t hash, const LDM_hashEntry entry) {
|
||||
*(getBucket(table, hash) + table->bucketOffsets[hash]) = entry;
|
||||
table->bucketOffsets[hash]++;
|
||||
table->bucketOffsets[hash] &= HASH_BUCKET_SIZE - 1;
|
||||
}
|
||||
|
||||
static void HASH_outputTableOccupancy(const LDM_hashTable *table) {
|
||||
U32 ctr = 0;
|
||||
LDM_hashEntry *cur = table->entries;
|
||||
LDM_hashEntry *end = table->entries + (table->numBuckets * HASH_BUCKET_SIZE);
|
||||
for (; cur < end; ++cur) {
|
||||
if (cur->offset == 0) {
|
||||
ctr++;
|
||||
}
|
||||
}
|
||||
|
||||
// The number of buckets is repeated as a check for now.
|
||||
printf("Num buckets, bucket size: %d (2^%d), %d\n",
|
||||
table->numBuckets, NUM_HASH_BUCKETS_LOG, HASH_BUCKET_SIZE);
|
||||
printf("Hash table size, empty slots, %% empty: %u, %u, %.3f\n",
|
||||
table->numEntries, ctr,
|
||||
100.0 * (double)(ctr) / table->numEntries);
|
||||
}
|
||||
|
||||
// TODO: This can be done more efficiently, for example by using builtin
|
||||
// functions (but it is not that important as it is only used for computing
|
||||
// stats).
|
||||
static int intLog2(U64 x) {
|
||||
int ret = 0;
|
||||
while (x >>= 1) {
|
||||
ret++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void LDM_printCompressStats(const LDM_compressStats *stats) {
|
||||
printf("=====================\n");
|
||||
printf("Compression statistics\n");
|
||||
printf("Window size, hash table size (bytes): 2^%u, 2^%u\n",
|
||||
stats->windowSizeLog, stats->hashTableSizeLog);
|
||||
printf("num matches, total match length, %% matched: %u, %llu, %.3f\n",
|
||||
stats->numMatches,
|
||||
stats->totalMatchLength,
|
||||
100.0 * (double)stats->totalMatchLength /
|
||||
(double)(stats->totalMatchLength + stats->totalLiteralLength));
|
||||
printf("avg match length: %.1f\n", ((double)stats->totalMatchLength) /
|
||||
(double)stats->numMatches);
|
||||
printf("avg literal length, total literalLength: %.1f, %llu\n",
|
||||
((double)stats->totalLiteralLength) / (double)stats->numMatches,
|
||||
stats->totalLiteralLength);
|
||||
printf("avg offset length: %.1f\n",
|
||||
((double)stats->totalOffset) / (double)stats->numMatches);
|
||||
printf("min offset, max offset: %u, %u\n",
|
||||
stats->minOffset, stats->maxOffset);
|
||||
|
||||
printf("\n");
|
||||
printf("offset histogram | match length histogram\n");
|
||||
printf("offset/ML, num matches, %% of matches | num matches, %% of matches\n");
|
||||
|
||||
{
|
||||
int i;
|
||||
int logMaxOffset = intLog2(stats->maxOffset);
|
||||
for (i = 0; i <= logMaxOffset; i++) {
|
||||
printf("2^%*d: %10u %6.3f%% |2^%*d: %10u %6.3f \n",
|
||||
2, i,
|
||||
stats->offsetHistogram[i],
|
||||
100.0 * (double) stats->offsetHistogram[i] /
|
||||
(double) stats->numMatches,
|
||||
2, i,
|
||||
stats->matchLengthHistogram[i],
|
||||
100.0 * (double) stats->matchLengthHistogram[i] /
|
||||
(double) stats->numMatches);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
printf("=====================\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the upper (most significant) NUM_HASH_BUCKETS_LOG bits.
|
||||
*/
|
||||
static hash_t getSmallHash(U64 hash) {
|
||||
return hash >> (64 - NUM_HASH_BUCKETS_LOG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the 32 bits after the upper NUM_HASH_BUCKETS_LOG bits.
|
||||
*/
|
||||
static U32 getChecksum(U64 hash) {
|
||||
return (hash >> (64 - 32 - NUM_HASH_BUCKETS_LOG)) & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
#if INSERT_BY_TAG
|
||||
static U32 lowerBitsFromHfHash(U64 hash) {
|
||||
// The number of bits used so far is NUM_HASH_BUCKETS_LOG + 32.
|
||||
// So there are 32 - NUM_HASH_BUCKETS_LOG bits left.
|
||||
// Occasional hashing requires HASH_ONLY_EVERY_LOG bits.
|
||||
// So if 32 - LDMHASHLOG < HASH_ONLY_EVERY_LOG, just return lower bits
|
||||
// allowing for reuse of bits.
|
||||
if (32 - NUM_HASH_BUCKETS_LOG < HASH_ONLY_EVERY_LOG) {
|
||||
return hash & HASH_ONLY_EVERY;
|
||||
} else {
|
||||
// Otherwise shift by
|
||||
// (32 - NUM_HASH_BUCKETS_LOG - HASH_ONLY_EVERY_LOG) bits first.
|
||||
return (hash >> (32 - NUM_HASH_BUCKETS_LOG - HASH_ONLY_EVERY_LOG)) &
|
||||
HASH_ONLY_EVERY;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Get a 64-bit hash using the first len bytes from buf.
|
||||
*
|
||||
* Giving bytes s = s_1, s_2, ... s_k, the hash is defined to be
|
||||
* H(s) = s_1*(a^(k-1)) + s_2*(a^(k-2)) + ... + s_k*(a^0)
|
||||
*
|
||||
* where the constant a is defined to be prime8bytes.
|
||||
*
|
||||
* The implementation adds an offset to each byte, so
|
||||
* H(s) = (s_1 + HASH_CHAR_OFFSET)*(a^(k-1)) + ...
|
||||
*/
|
||||
static U64 getHash(const BYTE *buf, U32 len) {
|
||||
U64 ret = 0;
|
||||
U32 i;
|
||||
for (i = 0; i < len; i++) {
|
||||
ret *= prime8bytes;
|
||||
ret += buf[i] + HASH_CHAR_OFFSET;
|
||||
}
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static U64 ipow(U64 base, U64 exp) {
|
||||
U64 ret = 1;
|
||||
while (exp) {
|
||||
if (exp & 1) {
|
||||
ret *= base;
|
||||
}
|
||||
exp >>= 1;
|
||||
base *= base;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static U64 updateHash(U64 hash, U32 len,
|
||||
BYTE toRemove, BYTE toAdd) {
|
||||
// TODO: this relies on compiler optimization.
|
||||
// The exponential can be calculated explicitly as len is constant.
|
||||
hash -= ((toRemove + HASH_CHAR_OFFSET) *
|
||||
ipow(prime8bytes, len - 1));
|
||||
hash *= prime8bytes;
|
||||
hash += toAdd + HASH_CHAR_OFFSET;
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update cctx->nextHash and cctx->nextPosHashed
|
||||
* based on cctx->lastHash and cctx->lastPosHashed.
|
||||
*
|
||||
* This uses a rolling hash and requires that the last position hashed
|
||||
* corresponds to cctx->nextIp - step.
|
||||
*/
|
||||
static void setNextHash(LDM_CCtx *cctx) {
|
||||
cctx->nextHash = updateHash(
|
||||
cctx->lastHash, LDM_HASH_LENGTH,
|
||||
cctx->lastPosHashed[0],
|
||||
cctx->lastPosHashed[LDM_HASH_LENGTH]);
|
||||
cctx->nextPosHashed = cctx->nextIp;
|
||||
|
||||
#if LDM_LAG
|
||||
if (cctx->ip - cctx->ibase > LDM_LAG) {
|
||||
cctx->lagHash = updateHash(
|
||||
cctx->lagHash, LDM_HASH_LENGTH,
|
||||
cctx->lagIp[0], cctx->lagIp[LDM_HASH_LENGTH]);
|
||||
cctx->lagIp++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void putHashOfCurrentPositionFromHash(LDM_CCtx *cctx, U64 hash) {
|
||||
// Hash only every HASH_ONLY_EVERY times, based on cctx->ip.
|
||||
// Note: this works only when cctx->step is 1.
|
||||
#if LDM_LAG
|
||||
if (cctx -> lagIp - cctx->ibase > 0) {
|
||||
#if INSERT_BY_TAG
|
||||
U32 hashEveryMask = lowerBitsFromHfHash(cctx->lagHash);
|
||||
if (hashEveryMask == HASH_ONLY_EVERY) {
|
||||
#else
|
||||
if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) {
|
||||
#endif
|
||||
U32 smallHash = getSmallHash(cctx->lagHash);
|
||||
|
||||
# if USE_CHECKSUM
|
||||
U32 checksum = getChecksum(cctx->lagHash);
|
||||
const LDM_hashEntry entry = { cctx->lagIp - cctx->ibase, checksum };
|
||||
# else
|
||||
const LDM_hashEntry entry = { cctx->lagIp - cctx->ibase };
|
||||
# endif
|
||||
|
||||
HASH_insert(cctx->hashTable, smallHash, entry);
|
||||
}
|
||||
} else {
|
||||
#endif // LDM_LAG
|
||||
#if INSERT_BY_TAG
|
||||
U32 hashEveryMask = lowerBitsFromHfHash(hash);
|
||||
if (hashEveryMask == HASH_ONLY_EVERY) {
|
||||
#else
|
||||
if (((cctx->ip - cctx->ibase) & HASH_ONLY_EVERY) == HASH_ONLY_EVERY) {
|
||||
#endif
|
||||
U32 smallHash = getSmallHash(hash);
|
||||
|
||||
#if USE_CHECKSUM
|
||||
U32 checksum = getChecksum(hash);
|
||||
const LDM_hashEntry entry = { cctx->ip - cctx->ibase, checksum };
|
||||
#else
|
||||
const LDM_hashEntry entry = { cctx->ip - cctx->ibase };
|
||||
#endif
|
||||
HASH_insert(cctx->hashTable, smallHash, entry);
|
||||
}
|
||||
#if LDM_LAG
|
||||
}
|
||||
#endif
|
||||
|
||||
cctx->lastPosHashed = cctx->ip;
|
||||
cctx->lastHash = hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy over the cctx->lastHash, and cctx->lastPosHashed
|
||||
* fields from the "next" fields.
|
||||
*
|
||||
* This requires that cctx->ip == cctx->nextPosHashed.
|
||||
*/
|
||||
static void LDM_updateLastHashFromNextHash(LDM_CCtx *cctx) {
|
||||
putHashOfCurrentPositionFromHash(cctx, cctx->nextHash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert hash of the current position into the hash table.
|
||||
*/
|
||||
static void LDM_putHashOfCurrentPosition(LDM_CCtx *cctx) {
|
||||
U64 hash = getHash(cctx->ip, LDM_HASH_LENGTH);
|
||||
|
||||
putHashOfCurrentPositionFromHash(cctx, hash);
|
||||
}
|
||||
|
||||
size_t LDM_initializeCCtx(LDM_CCtx *cctx,
|
||||
const void *src, size_t srcSize,
|
||||
void *dst, size_t maxDstSize) {
|
||||
cctx->isize = srcSize;
|
||||
cctx->maxOSize = maxDstSize;
|
||||
|
||||
cctx->ibase = (const BYTE *)src;
|
||||
cctx->ip = cctx->ibase;
|
||||
cctx->iend = cctx->ibase + srcSize;
|
||||
|
||||
cctx->ihashLimit = cctx->iend - LDM_HASH_LENGTH;
|
||||
cctx->imatchLimit = cctx->iend - LDM_MIN_MATCH_LENGTH;
|
||||
|
||||
cctx->obase = (BYTE *)dst;
|
||||
cctx->op = (BYTE *)dst;
|
||||
|
||||
cctx->anchor = cctx->ibase;
|
||||
|
||||
memset(&(cctx->stats), 0, sizeof(cctx->stats));
|
||||
#if USE_CHECKSUM
|
||||
cctx->hashTable = HASH_createTable(LDM_HASHTABLESIZE_U64);
|
||||
#else
|
||||
cctx->hashTable = HASH_createTable(LDM_HASHTABLESIZE_U32);
|
||||
#endif
|
||||
|
||||
if (!cctx->hashTable) return 1;
|
||||
|
||||
cctx->stats.minOffset = UINT_MAX;
|
||||
cctx->stats.windowSizeLog = LDM_WINDOW_SIZE_LOG;
|
||||
cctx->stats.hashTableSizeLog = LDM_MEMORY_USAGE;
|
||||
|
||||
cctx->lastPosHashed = NULL;
|
||||
|
||||
cctx->step = 1; // Fixed to be 1 for now. Changing may break things.
|
||||
cctx->nextIp = cctx->ip + cctx->step;
|
||||
cctx->nextPosHashed = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void LDM_destroyCCtx(LDM_CCtx *cctx) {
|
||||
HASH_destroyTable(cctx->hashTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the "best" match.
|
||||
*
|
||||
* Returns 0 if successful and 1 otherwise (i.e. no match can be found
|
||||
* in the remaining input that is long enough).
|
||||
*
|
||||
* forwardMatchLength contains the forward length of the match.
|
||||
*/
|
||||
static int LDM_findBestMatch(LDM_CCtx *cctx, const BYTE **match,
|
||||
U64 *forwardMatchLength, U64 *backwardMatchLength) {
|
||||
|
||||
LDM_hashEntry *entry = NULL;
|
||||
cctx->nextIp = cctx->ip + cctx->step;
|
||||
|
||||
while (entry == NULL) {
|
||||
U64 hash;
|
||||
hash_t smallHash;
|
||||
U32 checksum;
|
||||
#if INSERT_BY_TAG
|
||||
U32 hashEveryMask;
|
||||
#endif
|
||||
setNextHash(cctx);
|
||||
|
||||
hash = cctx->nextHash;
|
||||
smallHash = getSmallHash(hash);
|
||||
checksum = getChecksum(hash);
|
||||
#if INSERT_BY_TAG
|
||||
hashEveryMask = lowerBitsFromHfHash(hash);
|
||||
#endif
|
||||
|
||||
cctx->ip = cctx->nextIp;
|
||||
cctx->nextIp += cctx->step;
|
||||
|
||||
if (cctx->ip > cctx->imatchLimit) {
|
||||
return 1;
|
||||
}
|
||||
#if INSERT_BY_TAG
|
||||
if (hashEveryMask == HASH_ONLY_EVERY) {
|
||||
|
||||
entry = HASH_getBestEntry(cctx, smallHash, checksum,
|
||||
forwardMatchLength, backwardMatchLength);
|
||||
}
|
||||
#else
|
||||
entry = HASH_getBestEntry(cctx, smallHash, checksum,
|
||||
forwardMatchLength, backwardMatchLength);
|
||||
#endif
|
||||
|
||||
if (entry != NULL) {
|
||||
*match = entry->offset + cctx->ibase;
|
||||
}
|
||||
|
||||
putHashOfCurrentPositionFromHash(cctx, hash);
|
||||
|
||||
}
|
||||
setNextHash(cctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void LDM_encodeLiteralLengthAndLiterals(
|
||||
LDM_CCtx *cctx, BYTE *pToken, const U64 literalLength) {
|
||||
/* Encode the literal length. */
|
||||
if (literalLength >= RUN_MASK) {
|
||||
U64 len = (U64)literalLength - RUN_MASK;
|
||||
*pToken = (RUN_MASK << ML_BITS);
|
||||
for (; len >= 255; len -= 255) {
|
||||
*(cctx->op)++ = 255;
|
||||
}
|
||||
*(cctx->op)++ = (BYTE)len;
|
||||
} else {
|
||||
*pToken = (BYTE)(literalLength << ML_BITS);
|
||||
}
|
||||
|
||||
/* Encode the literals. */
|
||||
memcpy(cctx->op, cctx->anchor, literalLength);
|
||||
cctx->op += literalLength;
|
||||
}
|
||||
|
||||
void LDM_outputBlock(LDM_CCtx *cctx,
|
||||
const U64 literalLength,
|
||||
const U32 offset,
|
||||
const U64 matchLength) {
|
||||
BYTE *pToken = cctx->op++;
|
||||
|
||||
/* Encode the literal length and literals. */
|
||||
LDM_encodeLiteralLengthAndLiterals(cctx, pToken, literalLength);
|
||||
|
||||
/* Encode the offset. */
|
||||
MEM_write32(cctx->op, offset);
|
||||
cctx->op += LDM_OFFSET_SIZE;
|
||||
|
||||
/* Encode the match length. */
|
||||
if (matchLength >= ML_MASK) {
|
||||
U64 matchLengthRemaining = matchLength;
|
||||
*pToken += ML_MASK;
|
||||
matchLengthRemaining -= ML_MASK;
|
||||
MEM_write32(cctx->op, 0xFFFFFFFF);
|
||||
while (matchLengthRemaining >= 4*0xFF) {
|
||||
cctx->op += 4;
|
||||
MEM_write32(cctx->op, 0xffffffff);
|
||||
matchLengthRemaining -= 4*0xFF;
|
||||
}
|
||||
cctx->op += matchLengthRemaining / 255;
|
||||
*(cctx->op)++ = (BYTE)(matchLengthRemaining % 255);
|
||||
} else {
|
||||
*pToken += (BYTE)(matchLength);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: maxDstSize is unused. This function may seg fault when writing
|
||||
// beyond the size of dst, as it does not check maxDstSize. Writing to
|
||||
// a buffer and performing checks is a possible solution.
|
||||
//
|
||||
// This is based upon lz4.
|
||||
size_t LDM_compress(const void *src, size_t srcSize,
|
||||
void *dst, size_t maxDstSize) {
|
||||
LDM_CCtx cctx;
|
||||
const BYTE *match = NULL;
|
||||
U64 forwardMatchLength = 0;
|
||||
U64 backwardsMatchLength = 0;
|
||||
|
||||
if (LDM_initializeCCtx(&cctx, src, srcSize, dst, maxDstSize)) {
|
||||
// Initialization failed.
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef OUTPUT_CONFIGURATION
|
||||
LDM_outputConfiguration();
|
||||
#endif
|
||||
|
||||
/* Hash the first position and put it into the hash table. */
|
||||
LDM_putHashOfCurrentPosition(&cctx);
|
||||
|
||||
cctx.lagIp = cctx.ip;
|
||||
cctx.lagHash = cctx.lastHash;
|
||||
|
||||
/**
|
||||
* Find a match.
|
||||
* If no more matches can be found (i.e. the length of the remaining input
|
||||
* is less than the minimum match length), then stop searching for matches
|
||||
* and encode the final literals.
|
||||
*/
|
||||
while (!LDM_findBestMatch(&cctx, &match, &forwardMatchLength,
|
||||
&backwardsMatchLength)) {
|
||||
|
||||
#ifdef COMPUTE_STATS
|
||||
cctx.stats.numMatches++;
|
||||
#endif
|
||||
|
||||
cctx.ip -= backwardsMatchLength;
|
||||
match -= backwardsMatchLength;
|
||||
|
||||
/**
|
||||
* Write current block (literals, literal length, match offset, match
|
||||
* length) and update pointers and hashes.
|
||||
*/
|
||||
{
|
||||
const U64 literalLength = cctx.ip - cctx.anchor;
|
||||
const U32 offset = cctx.ip - match;
|
||||
const U64 matchLength = forwardMatchLength +
|
||||
backwardsMatchLength -
|
||||
LDM_MIN_MATCH_LENGTH;
|
||||
|
||||
LDM_outputBlock(&cctx, literalLength, offset, matchLength);
|
||||
|
||||
#ifdef COMPUTE_STATS
|
||||
cctx.stats.totalLiteralLength += literalLength;
|
||||
cctx.stats.totalOffset += offset;
|
||||
cctx.stats.totalMatchLength += matchLength + LDM_MIN_MATCH_LENGTH;
|
||||
cctx.stats.minOffset =
|
||||
offset < cctx.stats.minOffset ? offset : cctx.stats.minOffset;
|
||||
cctx.stats.maxOffset =
|
||||
offset > cctx.stats.maxOffset ? offset : cctx.stats.maxOffset;
|
||||
cctx.stats.offsetHistogram[(U32)intLog2(offset)]++;
|
||||
cctx.stats.matchLengthHistogram[
|
||||
(U32)intLog2(matchLength + LDM_MIN_MATCH_LENGTH)]++;
|
||||
#endif
|
||||
|
||||
// Move ip to end of block, inserting hashes at each position.
|
||||
cctx.nextIp = cctx.ip + cctx.step;
|
||||
while (cctx.ip < cctx.anchor + LDM_MIN_MATCH_LENGTH +
|
||||
matchLength + literalLength) {
|
||||
if (cctx.ip > cctx.lastPosHashed) {
|
||||
// TODO: Simplify.
|
||||
LDM_updateLastHashFromNextHash(&cctx);
|
||||
setNextHash(&cctx);
|
||||
}
|
||||
cctx.ip++;
|
||||
cctx.nextIp++;
|
||||
}
|
||||
}
|
||||
|
||||
// Set start of next block to current input pointer.
|
||||
cctx.anchor = cctx.ip;
|
||||
LDM_updateLastHashFromNextHash(&cctx);
|
||||
}
|
||||
|
||||
/* Encode the last literals (no more matches). */
|
||||
{
|
||||
const U64 lastRun = cctx.iend - cctx.anchor;
|
||||
BYTE *pToken = cctx.op++;
|
||||
LDM_encodeLiteralLengthAndLiterals(&cctx, pToken, lastRun);
|
||||
}
|
||||
|
||||
#ifdef COMPUTE_STATS
|
||||
LDM_printCompressStats(&cctx.stats);
|
||||
HASH_outputTableOccupancy(cctx.hashTable);
|
||||
#endif
|
||||
|
||||
{
|
||||
const size_t ret = cctx.op - cctx.obase;
|
||||
LDM_destroyCCtx(&cctx);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
void LDM_outputConfiguration(void) {
|
||||
printf("=====================\n");
|
||||
printf("Configuration\n");
|
||||
printf("LDM_WINDOW_SIZE_LOG: %d\n", LDM_WINDOW_SIZE_LOG);
|
||||
printf("LDM_MIN_MATCH_LENGTH, LDM_HASH_LENGTH: %d, %d\n",
|
||||
LDM_MIN_MATCH_LENGTH, LDM_HASH_LENGTH);
|
||||
printf("LDM_MEMORY_USAGE: %d\n", LDM_MEMORY_USAGE);
|
||||
printf("HASH_ONLY_EVERY_LOG: %d\n", HASH_ONLY_EVERY_LOG);
|
||||
printf("HASH_BUCKET_SIZE_LOG: %d\n", HASH_BUCKET_SIZE_LOG);
|
||||
printf("LDM_LAG: %d\n", LDM_LAG);
|
||||
printf("USE_CHECKSUM: %d\n", USE_CHECKSUM);
|
||||
printf("INSERT_BY_TAG: %d\n", INSERT_BY_TAG);
|
||||
printf("HASH_CHAR_OFFSET: %d\n", HASH_CHAR_OFFSET);
|
||||
printf("=====================\n");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
#ifndef LDM_H
|
||||
#define LDM_H
|
||||
|
||||
#include "mem.h" // from /lib/common/mem.h
|
||||
|
||||
//#include "ldm_params.h"
|
||||
|
||||
// =============================================================================
|
||||
// Modify the parameters in ldm_params.h if "ldm_params.h" is included.
|
||||
// Otherwise, modify the parameters here.
|
||||
// =============================================================================
|
||||
|
||||
#ifndef LDM_PARAMS_H
|
||||
// Defines the size of the hash table.
|
||||
// Note that this is not the number of buckets.
|
||||
// Currently this should be less than WINDOW_SIZE_LOG + 4.
|
||||
#define LDM_MEMORY_USAGE 23
|
||||
|
||||
// The number of entries in a hash bucket.
|
||||
#define HASH_BUCKET_SIZE_LOG 3 // The maximum is 4 for now.
|
||||
|
||||
// Defines the lag in inserting elements into the hash table.
|
||||
#define LDM_LAG 0
|
||||
|
||||
// The maximum window size when searching for matches.
|
||||
// The maximum value is 30
|
||||
#define LDM_WINDOW_SIZE_LOG 28
|
||||
|
||||
// The minimum match length.
|
||||
// This should be a multiple of four.
|
||||
#define LDM_MIN_MATCH_LENGTH 64
|
||||
|
||||
// If INSERT_BY_TAG, insert entries into the hash table as a function of the
|
||||
// hash. Certain hashes will not be inserted.
|
||||
//
|
||||
// Otherwise, insert as a function of the position.
|
||||
#define INSERT_BY_TAG 1
|
||||
|
||||
// Store a checksum with the hash table entries for faster comparison.
|
||||
// This halves the number of entries the hash table can contain.
|
||||
#define USE_CHECKSUM 1
|
||||
#endif
|
||||
|
||||
// Output compression statistics.
|
||||
#define COMPUTE_STATS
|
||||
|
||||
// Output the configuration.
|
||||
#define OUTPUT_CONFIGURATION
|
||||
|
||||
// If defined, forces the probability of insertion to be approximately
|
||||
// one per (1 << HASH_ONLY_EVERY_LOG). If not defined, the probability will be
|
||||
// calculated based on the memory usage and window size for "even" insertion
|
||||
// throughout the window.
|
||||
|
||||
// #define HASH_ONLY_EVERY_LOG 8
|
||||
|
||||
// =============================================================================
|
||||
|
||||
// The number of bytes storing the compressed and decompressed size
|
||||
// in the header.
|
||||
#define LDM_COMPRESSED_SIZE 8
|
||||
#define LDM_DECOMPRESSED_SIZE 8
|
||||
#define LDM_HEADER_SIZE ((LDM_COMPRESSED_SIZE)+(LDM_DECOMPRESSED_SIZE))
|
||||
|
||||
#define ML_BITS 4
|
||||
#define ML_MASK ((1U<<ML_BITS)-1)
|
||||
#define RUN_BITS (8-ML_BITS)
|
||||
#define RUN_MASK ((1U<<RUN_BITS)-1)
|
||||
|
||||
// The number of bytes storing the offset.
|
||||
#define LDM_OFFSET_SIZE 4
|
||||
|
||||
#define LDM_WINDOW_SIZE (1 << (LDM_WINDOW_SIZE_LOG))
|
||||
|
||||
// TODO: Match lengths that are too small do not use the hash table efficiently.
|
||||
// There should be a minimum hash length given the hash table size.
|
||||
#define LDM_HASH_LENGTH LDM_MIN_MATCH_LENGTH
|
||||
|
||||
typedef struct LDM_compressStats LDM_compressStats;
|
||||
typedef struct LDM_CCtx LDM_CCtx;
|
||||
typedef struct LDM_DCtx LDM_DCtx;
|
||||
|
||||
/**
|
||||
* Compresses src into dst.
|
||||
* Returns the compressed size if successful, 0 otherwise.
|
||||
*
|
||||
* NB: This currently ignores maxDstSize and assumes enough space is available.
|
||||
*
|
||||
* Block format (see lz4 documentation for more information):
|
||||
* github.com/lz4/lz4/blob/dev/doc/lz4_Block_format.md
|
||||
*
|
||||
* A block is composed of sequences. Each sequence begins with a token, which
|
||||
* is a one-byte value separated into two 4-bit fields.
|
||||
*
|
||||
* The first field uses the four high bits of the token and encodes the literal
|
||||
* length. If the field value is 0, there is no literal. If it is 15,
|
||||
* additional bytes are added (each ranging from 0 to 255) to the previous
|
||||
* value to produce a total length.
|
||||
*
|
||||
* Following the token and optional length bytes are the literals.
|
||||
*
|
||||
* Next are the 4 bytes representing the offset of the match (2 in lz4),
|
||||
* representing the position to copy the literals.
|
||||
*
|
||||
* The lower four bits of the token encode the match length. With additional
|
||||
* bytes added similarly to the additional literal length bytes after the offset.
|
||||
*
|
||||
* The last sequence is incomplete and stops right after the literals.
|
||||
*/
|
||||
size_t LDM_compress(const void *src, size_t srcSize,
|
||||
void *dst, size_t maxDstSize);
|
||||
|
||||
/**
|
||||
* Initialize the compression context.
|
||||
*
|
||||
* Allocates memory for the hash table.
|
||||
*
|
||||
* Returns 0 if successful, 1 otherwise.
|
||||
*/
|
||||
size_t LDM_initializeCCtx(LDM_CCtx *cctx,
|
||||
const void *src, size_t srcSize,
|
||||
void *dst, size_t maxDstSize);
|
||||
|
||||
/**
|
||||
* Frees up memory allocated in LDM_initializeCCtx().
|
||||
*/
|
||||
void LDM_destroyCCtx(LDM_CCtx *cctx);
|
||||
|
||||
/**
|
||||
* Prints the distribution of offsets in the hash table.
|
||||
*
|
||||
* The offsets are defined as the distance of the hash table entry from the
|
||||
* current input position of the cctx.
|
||||
*/
|
||||
void LDM_outputHashTableOffsetHistogram(const LDM_CCtx *cctx);
|
||||
|
||||
/**
|
||||
* Outputs compression statistics to stdout.
|
||||
*/
|
||||
void LDM_printCompressStats(const LDM_compressStats *stats);
|
||||
|
||||
/**
|
||||
* Encode the literal length followed by the literals.
|
||||
*
|
||||
* The literal length is written to the upper four bits of pToken, with
|
||||
* additional bytes written to the output as needed (see lz4).
|
||||
*
|
||||
* This is followed by literalLength bytes corresponding to the literals.
|
||||
*/
|
||||
void LDM_encodeLiteralLengthAndLiterals(LDM_CCtx *cctx, BYTE *pToken,
|
||||
const U64 literalLength);
|
||||
|
||||
/**
|
||||
* Write current block (literals, literal length, match offset,
|
||||
* match length).
|
||||
*/
|
||||
void LDM_outputBlock(LDM_CCtx *cctx,
|
||||
const U64 literalLength,
|
||||
const U32 offset,
|
||||
const U64 matchLength);
|
||||
|
||||
/**
|
||||
* Decompresses src into dst.
|
||||
*
|
||||
* Note: assumes src does not have a header.
|
||||
*/
|
||||
size_t LDM_decompress(const void *src, size_t srcSize,
|
||||
void *dst, size_t maxDstSize);
|
||||
|
||||
/**
|
||||
* Initialize the decompression context.
|
||||
*/
|
||||
void LDM_initializeDCtx(LDM_DCtx *dctx,
|
||||
const void *src, size_t compressedSize,
|
||||
void *dst, size_t maxDecompressedSize);
|
||||
|
||||
/**
|
||||
* Reads the header from src and writes the compressed size and
|
||||
* decompressed size into compressedSize and decompressedSize respectively.
|
||||
*
|
||||
* NB: LDM_compress and LDM_decompress currently do not add/read headers.
|
||||
*/
|
||||
void LDM_readHeader(const void *src, U64 *compressedSize,
|
||||
U64 *decompressedSize);
|
||||
|
||||
/**
|
||||
* Write the compressed and decompressed size.
|
||||
*/
|
||||
void LDM_writeHeader(void *memPtr, U64 compressedSize,
|
||||
U64 decompressedSize);
|
||||
|
||||
/**
|
||||
* Output the configuration used.
|
||||
*/
|
||||
void LDM_outputConfiguration(void);
|
||||
|
||||
#endif /* LDM_H */
|
||||
@@ -0,0 +1,109 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "ldm.h"
|
||||
|
||||
/**
|
||||
* This function reads the header at the beginning of src and writes
|
||||
* the compressed and decompressed size to compressedSize and
|
||||
* decompressedSize.
|
||||
*
|
||||
* The header consists of 16 bytes: 8 bytes each in little-endian format
|
||||
* of the compressed size and the decompressed size.
|
||||
*/
|
||||
void LDM_readHeader(const void *src, U64 *compressedSize,
|
||||
U64 *decompressedSize) {
|
||||
const BYTE *ip = (const BYTE *)src;
|
||||
*compressedSize = MEM_readLE64(ip);
|
||||
*decompressedSize = MEM_readLE64(ip + 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the 16-byte header (8-bytes each of the compressedSize and
|
||||
* decompressedSize in little-endian format) to memPtr.
|
||||
*/
|
||||
void LDM_writeHeader(void *memPtr, U64 compressedSize,
|
||||
U64 decompressedSize) {
|
||||
MEM_writeLE64(memPtr, compressedSize);
|
||||
MEM_writeLE64((BYTE *)memPtr + 8, decompressedSize);
|
||||
}
|
||||
|
||||
struct LDM_DCtx {
|
||||
size_t compressedSize;
|
||||
size_t maxDecompressedSize;
|
||||
|
||||
const BYTE *ibase; /* Base of input */
|
||||
const BYTE *ip; /* Current input position */
|
||||
const BYTE *iend; /* End of source */
|
||||
|
||||
const BYTE *obase; /* Base of output */
|
||||
BYTE *op; /* Current output position */
|
||||
const BYTE *oend; /* End of output */
|
||||
};
|
||||
|
||||
void LDM_initializeDCtx(LDM_DCtx *dctx,
|
||||
const void *src, size_t compressedSize,
|
||||
void *dst, size_t maxDecompressedSize) {
|
||||
dctx->compressedSize = compressedSize;
|
||||
dctx->maxDecompressedSize = maxDecompressedSize;
|
||||
|
||||
dctx->ibase = src;
|
||||
dctx->ip = (const BYTE *)src;
|
||||
dctx->iend = dctx->ip + dctx->compressedSize;
|
||||
dctx->op = dst;
|
||||
dctx->oend = dctx->op + dctx->maxDecompressedSize;
|
||||
}
|
||||
|
||||
size_t LDM_decompress(const void *src, size_t compressedSize,
|
||||
void *dst, size_t maxDecompressedSize) {
|
||||
|
||||
LDM_DCtx dctx;
|
||||
LDM_initializeDCtx(&dctx, src, compressedSize, dst, maxDecompressedSize);
|
||||
|
||||
while (dctx.ip < dctx.iend) {
|
||||
BYTE *cpy;
|
||||
const BYTE *match;
|
||||
size_t length, offset;
|
||||
|
||||
/* Get the literal length. */
|
||||
const unsigned token = *(dctx.ip)++;
|
||||
if ((length = (token >> ML_BITS)) == RUN_MASK) {
|
||||
unsigned s;
|
||||
do {
|
||||
s = *(dctx.ip)++;
|
||||
length += s;
|
||||
} while (s == 255);
|
||||
}
|
||||
|
||||
/* Copy the literals. */
|
||||
cpy = dctx.op + length;
|
||||
memcpy(dctx.op, dctx.ip, length);
|
||||
dctx.ip += length;
|
||||
dctx.op = cpy;
|
||||
|
||||
//TODO: dynamic offset size?
|
||||
/* Encode the offset. */
|
||||
offset = MEM_read32(dctx.ip);
|
||||
dctx.ip += LDM_OFFSET_SIZE;
|
||||
match = dctx.op - offset;
|
||||
|
||||
/* Get the match length. */
|
||||
length = token & ML_MASK;
|
||||
if (length == ML_MASK) {
|
||||
unsigned s;
|
||||
do {
|
||||
s = *(dctx.ip)++;
|
||||
length += s;
|
||||
} while (s == 255);
|
||||
}
|
||||
length += LDM_MIN_MATCH_LENGTH;
|
||||
|
||||
/* Copy match. */
|
||||
cpy = dctx.op + length;
|
||||
|
||||
// TODO: this can be made more efficient.
|
||||
while (match < cpy - offset && dctx.op < dctx.oend) {
|
||||
*(dctx.op)++ = *match++;
|
||||
}
|
||||
}
|
||||
return dctx.op - (BYTE *)dst;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef LDM_PARAMS_H
|
||||
#define LDM_PARAMS_H
|
||||
|
||||
#define LDM_MEMORY_USAGE 23
|
||||
#define HASH_BUCKET_SIZE_LOG 3
|
||||
#define LDM_LAG 0
|
||||
#define LDM_WINDOW_SIZE_LOG 28
|
||||
#define LDM_MIN_MATCH_LENGTH 64
|
||||
#define INSERT_BY_TAG 1
|
||||
#define USE_CHECKSUM 1
|
||||
|
||||
#endif // LDM_PARAMS_H
|
||||
@@ -0,0 +1,269 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <zstd.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include "ldm.h"
|
||||
#include "zstd.h"
|
||||
|
||||
// #define DECOMPRESS_AND_VERIFY
|
||||
|
||||
/* Compress file given by fname and output to oname.
|
||||
* Returns 0 if successful, error code otherwise.
|
||||
*
|
||||
* This adds a header from LDM_writeHeader to the beginning of the output.
|
||||
*
|
||||
* This might seg fault if the compressed size is > the decompress
|
||||
* size due to the mmapping and output file size allocated to be the input size
|
||||
* The compress function should check before writing or buffer writes.
|
||||
*/
|
||||
static int compress(const char *fname, const char *oname) {
|
||||
int fdin, fdout;
|
||||
struct stat statbuf;
|
||||
char *src, *dst;
|
||||
size_t maxCompressedSize, compressedSize;
|
||||
|
||||
struct timeval tv1, tv2;
|
||||
double timeTaken;
|
||||
|
||||
|
||||
/* Open the input file. */
|
||||
if ((fdin = open(fname, O_RDONLY)) < 0) {
|
||||
perror("Error in file opening");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Open the output file. */
|
||||
if ((fdout = open(oname, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600)) < 0) {
|
||||
perror("Can't create output file");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Find the size of the input file. */
|
||||
if (fstat (fdin, &statbuf) < 0) {
|
||||
perror("Fstat error");
|
||||
return 1;
|
||||
}
|
||||
|
||||
maxCompressedSize = (statbuf.st_size + LDM_HEADER_SIZE);
|
||||
|
||||
// Handle case where compressed size is > decompressed size.
|
||||
// TODO: The compress function should check before writing or buffer writes.
|
||||
maxCompressedSize += statbuf.st_size / 255;
|
||||
|
||||
ftruncate(fdout, maxCompressedSize);
|
||||
|
||||
/* mmap the input file. */
|
||||
if ((src = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fdin, 0))
|
||||
== (caddr_t) - 1) {
|
||||
perror("mmap error for input");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* mmap the output file. */
|
||||
if ((dst = mmap(0, maxCompressedSize, PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED, fdout, 0)) == (caddr_t) - 1) {
|
||||
perror("mmap error for output");
|
||||
return 1;
|
||||
}
|
||||
|
||||
gettimeofday(&tv1, NULL);
|
||||
|
||||
compressedSize = LDM_HEADER_SIZE +
|
||||
LDM_compress(src, statbuf.st_size,
|
||||
dst + LDM_HEADER_SIZE, maxCompressedSize);
|
||||
|
||||
gettimeofday(&tv2, NULL);
|
||||
|
||||
// Write the header.
|
||||
LDM_writeHeader(dst, compressedSize, statbuf.st_size);
|
||||
|
||||
// Truncate file to compressedSize.
|
||||
ftruncate(fdout, compressedSize);
|
||||
|
||||
printf("%25s : %10lu -> %10lu - %s \n", fname,
|
||||
(size_t)statbuf.st_size, (size_t)compressedSize, oname);
|
||||
printf("Compression ratio: %.2fx --- %.1f%%\n",
|
||||
(double)statbuf.st_size / (double)compressedSize,
|
||||
(double)compressedSize / (double)(statbuf.st_size) * 100.0);
|
||||
|
||||
timeTaken = (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 +
|
||||
(double) (tv2.tv_sec - tv1.tv_sec),
|
||||
|
||||
printf("Total compress time = %.3f seconds, Average scanning speed: %.3f MB/s\n",
|
||||
timeTaken,
|
||||
((double)statbuf.st_size / (double) (1 << 20)) / timeTaken);
|
||||
|
||||
// Close files.
|
||||
close(fdin);
|
||||
close(fdout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef DECOMPRESS_AND_VERIFY
|
||||
/* Decompress file compressed using LDM_compress.
|
||||
* The input file should have the LDM_HEADER followed by payload.
|
||||
* Returns 0 if succesful, and an error code otherwise.
|
||||
*/
|
||||
static int decompress(const char *fname, const char *oname) {
|
||||
int fdin, fdout;
|
||||
struct stat statbuf;
|
||||
char *src, *dst;
|
||||
U64 compressedSize, decompressedSize;
|
||||
size_t outSize;
|
||||
|
||||
/* Open the input file. */
|
||||
if ((fdin = open(fname, O_RDONLY)) < 0) {
|
||||
perror("Error in file opening");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Open the output file. */
|
||||
if ((fdout = open(oname, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600)) < 0) {
|
||||
perror("Can't create output file");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Find the size of the input file. */
|
||||
if (fstat (fdin, &statbuf) < 0) {
|
||||
perror("Fstat error");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* mmap the input file. */
|
||||
if ((src = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fdin, 0))
|
||||
== (caddr_t) - 1) {
|
||||
perror("mmap error for input");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Read the header. */
|
||||
LDM_readHeader(src, &compressedSize, &decompressedSize);
|
||||
|
||||
ftruncate(fdout, decompressedSize);
|
||||
|
||||
/* mmap the output file */
|
||||
if ((dst = mmap(0, decompressedSize, PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED, fdout, 0)) == (caddr_t) - 1) {
|
||||
perror("mmap error for output");
|
||||
return 1;
|
||||
}
|
||||
|
||||
outSize = LDM_decompress(
|
||||
src + LDM_HEADER_SIZE, statbuf.st_size - LDM_HEADER_SIZE,
|
||||
dst, decompressedSize);
|
||||
printf("Ret size out: %zu\n", outSize);
|
||||
|
||||
close(fdin);
|
||||
close(fdout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Compare two files.
|
||||
* Returns 0 iff they are the same.
|
||||
*/
|
||||
static int compare(FILE *fp0, FILE *fp1) {
|
||||
int result = 0;
|
||||
while (result == 0) {
|
||||
char b0[1024];
|
||||
char b1[1024];
|
||||
const size_t r0 = fread(b0, 1, sizeof(b0), fp0);
|
||||
const size_t r1 = fread(b1, 1, sizeof(b1), fp1);
|
||||
|
||||
result = (int)r0 - (int)r1;
|
||||
|
||||
if (0 == r0 || 0 == r1) break;
|
||||
|
||||
if (0 == result) result = memcmp(b0, b1, r0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Verify the input file is the same as the decompressed file. */
|
||||
static int verify(const char *inpFilename, const char *decFilename) {
|
||||
FILE *inpFp, *decFp;
|
||||
|
||||
if ((inpFp = fopen(inpFilename, "rb")) == NULL) {
|
||||
perror("Could not open input file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((decFp = fopen(decFilename, "rb")) == NULL) {
|
||||
perror("Could not open decompressed file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("verify : %s <-> %s\n", inpFilename, decFilename);
|
||||
{
|
||||
const int cmp = compare(inpFp, decFp);
|
||||
if(0 == cmp) {
|
||||
printf("verify : OK\n");
|
||||
} else {
|
||||
printf("verify : NG\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(decFp);
|
||||
fclose(inpFp);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
const char * const exeName = argv[0];
|
||||
char inpFilename[256] = { 0 };
|
||||
char ldmFilename[256] = { 0 };
|
||||
char decFilename[256] = { 0 };
|
||||
|
||||
if (argc < 2) {
|
||||
printf("Wrong arguments\n");
|
||||
printf("Usage:\n");
|
||||
printf("%s FILE\n", exeName);
|
||||
return 1;
|
||||
}
|
||||
|
||||
snprintf(inpFilename, 256, "%s", argv[1]);
|
||||
snprintf(ldmFilename, 256, "%s.ldm", argv[1]);
|
||||
snprintf(decFilename, 256, "%s.ldm.dec", argv[1]);
|
||||
|
||||
printf("inp = [%s]\n", inpFilename);
|
||||
printf("ldm = [%s]\n", ldmFilename);
|
||||
printf("dec = [%s]\n", decFilename);
|
||||
|
||||
/* Compress */
|
||||
{
|
||||
if (compress(inpFilename, ldmFilename)) {
|
||||
printf("Compress error\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DECOMPRESS_AND_VERIFY
|
||||
/* Decompress */
|
||||
{
|
||||
struct timeval tv1, tv2;
|
||||
gettimeofday(&tv1, NULL);
|
||||
if (decompress(ldmFilename, decFilename)) {
|
||||
printf("Decompress error\n");
|
||||
return 1;
|
||||
}
|
||||
gettimeofday(&tv2, NULL);
|
||||
printf("Total decompress time = %f seconds\n",
|
||||
(double) (tv2.tv_usec - tv1.tv_usec) / 1000000 +
|
||||
(double) (tv2.tv_sec - tv1.tv_sec));
|
||||
}
|
||||
/* verify */
|
||||
if (verify(inpFilename, decFilename)) {
|
||||
printf("Verification error\n");
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
HARNESS_FILES=*.c
|
||||
|
||||
MULTITHREAD_LDFLAGS = -pthread
|
||||
DEBUGFLAGS= -g -DZSTD_DEBUG=1
|
||||
CPPFLAGS += -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress \
|
||||
-I$(ZSTDDIR)/dictBuilder -I$(ZSTDDIR)/deprecated -I$(PRGDIR)
|
||||
CFLAGS ?= -O3
|
||||
CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \
|
||||
-Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement \
|
||||
-Wstrict-prototypes -Wundef -Wformat-security \
|
||||
-Wvla -Wformat=2 -Winit-self -Wfloat-equal -Wwrite-strings \
|
||||
-Wredundant-decls
|
||||
CFLAGS += $(DEBUGFLAGS)
|
||||
CFLAGS += $(MOREFLAGS)
|
||||
FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(MULTITHREAD_LDFLAGS)
|
||||
|
||||
harness: $(HARNESS_FILES)
|
||||
$(CC) $(FLAGS) $^ -o $@
|
||||
|
||||
clean:
|
||||
@$(RM) -f harness
|
||||
@$(RM) -rf harness.dSYM
|
||||
|
||||
test: harness
|
||||
@zstd README.md -o tmp.zst
|
||||
@./harness tmp.zst tmp
|
||||
@diff -s tmp README.md
|
||||
@$(RM) -f tmp*
|
||||
@zstd --train harness.c zstd_decompress.c zstd_decompress.h README.md
|
||||
@zstd -D dictionary README.md -o tmp.zst
|
||||
@./harness tmp.zst tmp dictionary
|
||||
@diff -s tmp README.md
|
||||
@$(RM) -f tmp* dictionary
|
||||
@make clean
|
||||
@@ -87,7 +87,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
size_t decompressed_size = ZSTD_get_decompressed_size(input, input_size);
|
||||
if (decompressed_size == -1) {
|
||||
if (decompressed_size == (size_t)-1) {
|
||||
decompressed_size = MAX_COMPRESSION_RATIO * input_size;
|
||||
fprintf(stderr, "WARNING: Compressed data does not contain "
|
||||
"decompressed size, going to assume the compression "
|
||||
@@ -106,9 +106,15 @@ int main(int argc, char **argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
dictionary_t* const parsed_dict = create_dictionary();
|
||||
if (dict) {
|
||||
parse_dictionary(parsed_dict, dict, dict_size);
|
||||
}
|
||||
size_t decompressed =
|
||||
ZSTD_decompress_with_dict(output, decompressed_size,
|
||||
input, input_size, dict, dict_size);
|
||||
input, input_size, parsed_dict);
|
||||
|
||||
free_dictionary(parsed_dict);
|
||||
|
||||
write_file(argv[2], output, decompressed);
|
||||
|
||||
@@ -117,4 +123,3 @@ int main(int argc, char **argv) {
|
||||
free(dict);
|
||||
input = output = dict = NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,21 +14,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/// Zstandard decompression functions.
|
||||
/// `dst` must point to a space at least as large as the reconstructed output.
|
||||
size_t ZSTD_decompress(void *const dst, const size_t dst_len,
|
||||
const void *const src, const size_t src_len);
|
||||
/// If `dict != NULL` and `dict_len >= 8`, does the same thing as
|
||||
/// `ZSTD_decompress` but uses the provided dict
|
||||
size_t ZSTD_decompress_with_dict(void *const dst, const size_t dst_len,
|
||||
const void *const src, const size_t src_len,
|
||||
const void *const dict, const size_t dict_len);
|
||||
|
||||
/// Get the decompressed size of an input stream so memory can be allocated in
|
||||
/// advance
|
||||
/// Returns -1 if the size can't be determined
|
||||
size_t ZSTD_get_decompressed_size(const void *const src, const size_t src_len);
|
||||
#include "zstd_decompress.h"
|
||||
|
||||
/******* UTILITY MACROS AND TYPES *********************************************/
|
||||
// Max block size decompressed size is 128 KB and literal blocks can't be
|
||||
@@ -108,10 +94,10 @@ static inline size_t IO_istream_len(const istream_t *const in);
|
||||
|
||||
/// Advances the stream by `len` bytes, and returns a pointer to the chunk that
|
||||
/// was skipped. The stream must be byte aligned.
|
||||
static inline const u8 *IO_read_bytes(istream_t *const in, size_t len);
|
||||
static inline const u8 *IO_get_read_ptr(istream_t *const in, size_t len);
|
||||
/// Advances the stream by `len` bytes, and returns a pointer to the chunk that
|
||||
/// was skipped so it can be written to.
|
||||
static inline u8 *IO_write_bytes(ostream_t *const out, size_t len);
|
||||
static inline u8 *IO_get_write_ptr(ostream_t *const out, size_t len);
|
||||
|
||||
/// Advance the inner state by `len` bytes. The stream must be byte aligned.
|
||||
static inline void IO_advance_input(istream_t *const in, size_t len);
|
||||
@@ -307,7 +293,7 @@ typedef struct {
|
||||
|
||||
/// The decoded contents of a dictionary so that it doesn't have to be repeated
|
||||
/// for each frame that uses it
|
||||
typedef struct {
|
||||
struct dictionary_s {
|
||||
// Entropy tables
|
||||
HUF_dtable literals_dtable;
|
||||
FSE_dtable ll_dtable;
|
||||
@@ -322,7 +308,7 @@ typedef struct {
|
||||
u64 previous_offsets[3];
|
||||
|
||||
u32 dictionary_id;
|
||||
} dictionary_t;
|
||||
};
|
||||
|
||||
/// A tuple containing the parts necessary to decode and execute a ZSTD sequence
|
||||
/// command
|
||||
@@ -367,27 +353,36 @@ static void execute_sequences(frame_context_t *const ctx, ostream_t *const out,
|
||||
const sequence_command_t *const sequences,
|
||||
const size_t num_sequences);
|
||||
|
||||
// Parse a provided dictionary blob for use in decompression
|
||||
static void parse_dictionary(dictionary_t *const dict, const u8 *src,
|
||||
size_t src_len);
|
||||
static void free_dictionary(dictionary_t *const dict);
|
||||
// Copies literals and returns the total literal length that was copied
|
||||
static u32 copy_literals(const size_t seq, istream_t *litstream,
|
||||
ostream_t *const out);
|
||||
|
||||
// Given an offset code from a sequence command (either an actual offset value
|
||||
// or an index for previous offset), computes the correct offset and udpates
|
||||
// the offset history
|
||||
static size_t compute_offset(sequence_command_t seq, u64 *const offset_hist);
|
||||
|
||||
// Given an offset, match length, and total output, as well as the frame
|
||||
// context for the dictionary, determines if the dictionary is used and
|
||||
// executes the copy operation
|
||||
static void execute_match_copy(frame_context_t *const ctx, size_t offset,
|
||||
size_t match_length, size_t total_output,
|
||||
ostream_t *const out);
|
||||
|
||||
/******* END ZSTD HELPER STRUCTS AND PROTOTYPES *******************************/
|
||||
|
||||
size_t ZSTD_decompress(void *const dst, const size_t dst_len,
|
||||
const void *const src, const size_t src_len) {
|
||||
return ZSTD_decompress_with_dict(dst, dst_len, src, src_len, NULL, 0);
|
||||
dictionary_t* uninit_dict = create_dictionary();
|
||||
size_t const decomp_size = ZSTD_decompress_with_dict(dst, dst_len, src,
|
||||
src_len, uninit_dict);
|
||||
free_dictionary(uninit_dict);
|
||||
return decomp_size;
|
||||
}
|
||||
|
||||
size_t ZSTD_decompress_with_dict(void *const dst, const size_t dst_len,
|
||||
const void *const src, const size_t src_len,
|
||||
const void *const dict,
|
||||
const size_t dict_len) {
|
||||
dictionary_t parsed_dict;
|
||||
memset(&parsed_dict, 0, sizeof(dictionary_t));
|
||||
// dict_len < 8 is not a valid dictionary
|
||||
if (dict && dict_len > 8) {
|
||||
parse_dictionary(&parsed_dict, (const u8 *)dict, dict_len);
|
||||
}
|
||||
dictionary_t* parsed_dict) {
|
||||
|
||||
istream_t in = IO_make_istream(src, src_len);
|
||||
ostream_t out = IO_make_ostream(dst, dst_len);
|
||||
@@ -396,11 +391,9 @@ size_t ZSTD_decompress_with_dict(void *const dst, const size_t dst_len,
|
||||
// Multiple frames can be appended into a single file or stream. A frame is
|
||||
// totally independent, has a defined beginning and end, and a set of
|
||||
// parameters which tells the decoder how to decompress it."
|
||||
while (IO_istream_len(&in) > 0) {
|
||||
decode_frame(&out, &in, &parsed_dict);
|
||||
}
|
||||
|
||||
free_dictionary(&parsed_dict);
|
||||
/* this decoder assumes decompression of a single frame */
|
||||
decode_frame(&out, &in, parsed_dict);
|
||||
|
||||
return out.ptr - (u8 *)dst;
|
||||
}
|
||||
@@ -424,30 +417,6 @@ static void decompress_data(frame_context_t *const ctx, ostream_t *const out,
|
||||
static void decode_frame(ostream_t *const out, istream_t *const in,
|
||||
const dictionary_t *const dict) {
|
||||
const u32 magic_number = IO_read_bits(in, 32);
|
||||
|
||||
// Skippable frame
|
||||
//
|
||||
// "Magic_Number
|
||||
//
|
||||
// 4 Bytes, little-endian format. Value : 0x184D2A5?, which means any value
|
||||
// from 0x184D2A50 to 0x184D2A5F. All 16 values are valid to identify a
|
||||
// skippable frame."
|
||||
if ((magic_number & ~0xFU) == 0x184D2A50U) {
|
||||
// "Skippable frames allow the insertion of user-defined data into a
|
||||
// flow of concatenated frames. Its design is pretty straightforward,
|
||||
// with the sole objective to allow the decoder to quickly skip over
|
||||
// user-defined data and continue decoding.
|
||||
//
|
||||
// Skippable frames defined in this specification are compatible with
|
||||
// LZ4 ones."
|
||||
const size_t frame_size = IO_read_bits(in, 32);
|
||||
|
||||
// skip over frame
|
||||
IO_advance_input(in, frame_size);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Zstandard frame
|
||||
//
|
||||
// "Magic_Number
|
||||
@@ -460,8 +429,8 @@ static void decode_frame(ostream_t *const out, istream_t *const in,
|
||||
return;
|
||||
}
|
||||
|
||||
// not a real frame
|
||||
ERROR("Invalid magic number");
|
||||
// not a real frame or a skippable frame
|
||||
ERROR("Tried to decode non-ZSTD frame");
|
||||
}
|
||||
|
||||
/// Decode a frame that contains compressed data. Not all frames do as there
|
||||
@@ -672,8 +641,8 @@ static void decompress_data(frame_context_t *const ctx, ostream_t *const out,
|
||||
case 0: {
|
||||
// "Raw_Block - this is an uncompressed block. Block_Size is the
|
||||
// number of bytes to read and copy."
|
||||
const u8 *const read_ptr = IO_read_bytes(in, block_len);
|
||||
u8 *const write_ptr = IO_write_bytes(out, block_len);
|
||||
const u8 *const read_ptr = IO_get_read_ptr(in, block_len);
|
||||
u8 *const write_ptr = IO_get_write_ptr(out, block_len);
|
||||
|
||||
// Copy the raw data into the output
|
||||
memcpy(write_ptr, read_ptr, block_len);
|
||||
@@ -685,8 +654,8 @@ static void decompress_data(frame_context_t *const ctx, ostream_t *const out,
|
||||
// "RLE_Block - this is a single byte, repeated N times. In which
|
||||
// case, Block_Size is the size to regenerate, while the
|
||||
// "compressed" block is just 1 byte (the byte to repeat)."
|
||||
const u8 *const read_ptr = IO_read_bytes(in, 1);
|
||||
u8 *const write_ptr = IO_write_bytes(out, block_len);
|
||||
const u8 *const read_ptr = IO_get_read_ptr(in, 1);
|
||||
u8 *const write_ptr = IO_get_write_ptr(out, block_len);
|
||||
|
||||
// Copy `block_len` copies of `read_ptr[0]` to the output
|
||||
memset(write_ptr, read_ptr[0], block_len);
|
||||
@@ -832,13 +801,13 @@ static size_t decode_literals_simple(istream_t *const in, u8 **const literals,
|
||||
switch (block_type) {
|
||||
case 0: {
|
||||
// "Raw_Literals_Block - Literals are stored uncompressed."
|
||||
const u8 *const read_ptr = IO_read_bytes(in, size);
|
||||
const u8 *const read_ptr = IO_get_read_ptr(in, size);
|
||||
memcpy(*literals, read_ptr, size);
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
// "RLE_Literals_Block - Literals consist of a single byte value repeated N times."
|
||||
const u8 *const read_ptr = IO_read_bytes(in, 1);
|
||||
const u8 *const read_ptr = IO_get_read_ptr(in, 1);
|
||||
memset(*literals, read_ptr[0], size);
|
||||
break;
|
||||
}
|
||||
@@ -949,7 +918,7 @@ static void decode_huf_table(HUF_dtable *const dtable, istream_t *const in) {
|
||||
num_symbs = header - 127;
|
||||
const size_t bytes = (num_symbs + 1) / 2;
|
||||
|
||||
const u8 *const weight_src = IO_read_bytes(in, bytes);
|
||||
const u8 *const weight_src = IO_get_read_ptr(in, bytes);
|
||||
|
||||
for (int i = 0; i < num_symbs; i++) {
|
||||
// "They are encoded forward, 2
|
||||
@@ -1157,7 +1126,7 @@ static void decompress_sequences(frame_context_t *const ctx, istream_t *in,
|
||||
}
|
||||
|
||||
const size_t len = IO_istream_len(in);
|
||||
const u8 *const src = IO_read_bytes(in, len);
|
||||
const u8 *const src = IO_get_read_ptr(in, len);
|
||||
|
||||
// "After writing the last bit containing information, the compressor writes
|
||||
// a single 1-bit and then fills the byte with 0-7 0 bits of padding."
|
||||
@@ -1262,7 +1231,7 @@ static void decode_seq_table(FSE_dtable *const table, istream_t *const in,
|
||||
}
|
||||
case seq_rle: {
|
||||
// "RLE_Mode : it's a single code, repeated Number_of_Sequences times."
|
||||
const u8 symb = IO_read_bytes(in, 1)[0];
|
||||
const u8 symb = IO_get_read_ptr(in, 1)[0];
|
||||
FSE_init_dtable_rle(table, symb);
|
||||
break;
|
||||
}
|
||||
@@ -1303,145 +1272,146 @@ static void execute_sequences(frame_context_t *const ctx, ostream_t *const out,
|
||||
|
||||
for (size_t i = 0; i < num_sequences; i++) {
|
||||
const sequence_command_t seq = sequences[i];
|
||||
|
||||
{
|
||||
// If the sequence asks for more literals than are left, the
|
||||
// sequence must be corrupted
|
||||
if (seq.literal_length > IO_istream_len(&litstream)) {
|
||||
CORRUPTION();
|
||||
}
|
||||
|
||||
u8 *const write_ptr = IO_write_bytes(out, seq.literal_length);
|
||||
const u8 *const read_ptr =
|
||||
IO_read_bytes(&litstream, seq.literal_length);
|
||||
// Copy literals to output
|
||||
memcpy(write_ptr, read_ptr, seq.literal_length);
|
||||
|
||||
total_output += seq.literal_length;
|
||||
const u32 literals_size = copy_literals(seq.literal_length, &litstream, out);
|
||||
total_output += literals_size;
|
||||
}
|
||||
|
||||
size_t offset;
|
||||
size_t const offset = compute_offset(seq, offset_hist);
|
||||
|
||||
// Offsets are special, we need to handle the repeat offsets
|
||||
if (seq.offset <= 3) {
|
||||
// "The first 3 values define a repeated offset and we will call
|
||||
// them Repeated_Offset1, Repeated_Offset2, and Repeated_Offset3.
|
||||
// They are sorted in recency order, with Repeated_Offset1 meaning
|
||||
// 'most recent one'".
|
||||
size_t const match_length = seq.match_length;
|
||||
|
||||
// Use 0 indexing for the array
|
||||
u32 idx = seq.offset - 1;
|
||||
if (seq.literal_length == 0) {
|
||||
// "There is an exception though, when current sequence's
|
||||
// literals length is 0. In this case, repeated offsets are
|
||||
// shifted by one, so Repeated_Offset1 becomes Repeated_Offset2,
|
||||
// Repeated_Offset2 becomes Repeated_Offset3, and
|
||||
// Repeated_Offset3 becomes Repeated_Offset1 - 1_byte."
|
||||
idx++;
|
||||
}
|
||||
execute_match_copy(ctx, offset, match_length, total_output, out);
|
||||
|
||||
if (idx == 0) {
|
||||
offset = offset_hist[0];
|
||||
} else {
|
||||
// If idx == 3 then literal length was 0 and the offset was 3,
|
||||
// as per the exception listed above
|
||||
offset = idx < 3 ? offset_hist[idx] : offset_hist[0] - 1;
|
||||
|
||||
// If idx == 1 we don't need to modify offset_hist[2], since
|
||||
// we're using the second-most recent code
|
||||
if (idx > 1) {
|
||||
offset_hist[2] = offset_hist[1];
|
||||
}
|
||||
offset_hist[1] = offset_hist[0];
|
||||
offset_hist[0] = offset;
|
||||
}
|
||||
} else {
|
||||
// When it's not a repeat offset:
|
||||
// "if (Offset_Value > 3) offset = Offset_Value - 3;"
|
||||
offset = seq.offset - 3;
|
||||
|
||||
// Shift back history
|
||||
offset_hist[2] = offset_hist[1];
|
||||
offset_hist[1] = offset_hist[0];
|
||||
offset_hist[0] = offset;
|
||||
}
|
||||
|
||||
size_t match_length = seq.match_length;
|
||||
|
||||
u8 *write_ptr = IO_write_bytes(out, match_length);
|
||||
if (total_output <= ctx->header.window_size) {
|
||||
// In this case offset might go back into the dictionary
|
||||
if (offset > total_output + ctx->dict_content_len) {
|
||||
// The offset goes beyond even the dictionary
|
||||
CORRUPTION();
|
||||
}
|
||||
|
||||
if (offset > total_output) {
|
||||
// "The rest of the dictionary is its content. The content act
|
||||
// as a "past" in front of data to compress or decompress, so it
|
||||
// can be referenced in sequence commands."
|
||||
const size_t dict_copy =
|
||||
MIN(offset - total_output, match_length);
|
||||
const size_t dict_offset =
|
||||
ctx->dict_content_len - (offset - total_output);
|
||||
|
||||
memcpy(write_ptr, ctx->dict_content + dict_offset, dict_copy);
|
||||
write_ptr += dict_copy;
|
||||
match_length -= dict_copy;
|
||||
}
|
||||
} else if (offset > ctx->header.window_size) {
|
||||
CORRUPTION();
|
||||
}
|
||||
|
||||
// We must copy byte by byte because the match length might be larger
|
||||
// than the offset
|
||||
// ex: if the output so far was "abc", a command with offset=3 and
|
||||
// match_length=6 would produce "abcabcabc" as the new output
|
||||
for (size_t i = 0; i < match_length; i++) {
|
||||
*write_ptr = *(write_ptr - offset);
|
||||
write_ptr++;
|
||||
}
|
||||
|
||||
total_output += seq.match_length;
|
||||
total_output += match_length;
|
||||
}
|
||||
|
||||
// Copy any leftover literals
|
||||
{
|
||||
size_t len = IO_istream_len(&litstream);
|
||||
u8 *const write_ptr = IO_write_bytes(out, len);
|
||||
const u8 *const read_ptr = IO_read_bytes(&litstream, len);
|
||||
memcpy(write_ptr, read_ptr, len);
|
||||
|
||||
copy_literals(len, &litstream, out);
|
||||
total_output += len;
|
||||
}
|
||||
|
||||
ctx->current_total_output = total_output;
|
||||
}
|
||||
|
||||
static u32 copy_literals(const size_t literal_length, istream_t *litstream,
|
||||
ostream_t *const out) {
|
||||
// If the sequence asks for more literals than are left, the
|
||||
// sequence must be corrupted
|
||||
if (literal_length > IO_istream_len(litstream)) {
|
||||
CORRUPTION();
|
||||
}
|
||||
|
||||
u8 *const write_ptr = IO_get_write_ptr(out, literal_length);
|
||||
const u8 *const read_ptr =
|
||||
IO_get_read_ptr(litstream, literal_length);
|
||||
// Copy literals to output
|
||||
memcpy(write_ptr, read_ptr, literal_length);
|
||||
|
||||
return literal_length;
|
||||
}
|
||||
|
||||
static size_t compute_offset(sequence_command_t seq, u64 *const offset_hist) {
|
||||
size_t offset;
|
||||
// Offsets are special, we need to handle the repeat offsets
|
||||
if (seq.offset <= 3) {
|
||||
// "The first 3 values define a repeated offset and we will call
|
||||
// them Repeated_Offset1, Repeated_Offset2, and Repeated_Offset3.
|
||||
// They are sorted in recency order, with Repeated_Offset1 meaning
|
||||
// 'most recent one'".
|
||||
|
||||
// Use 0 indexing for the array
|
||||
u32 idx = seq.offset - 1;
|
||||
if (seq.literal_length == 0) {
|
||||
// "There is an exception though, when current sequence's
|
||||
// literals length is 0. In this case, repeated offsets are
|
||||
// shifted by one, so Repeated_Offset1 becomes Repeated_Offset2,
|
||||
// Repeated_Offset2 becomes Repeated_Offset3, and
|
||||
// Repeated_Offset3 becomes Repeated_Offset1 - 1_byte."
|
||||
idx++;
|
||||
}
|
||||
|
||||
if (idx == 0) {
|
||||
offset = offset_hist[0];
|
||||
} else {
|
||||
// If idx == 3 then literal length was 0 and the offset was 3,
|
||||
// as per the exception listed above
|
||||
offset = idx < 3 ? offset_hist[idx] : offset_hist[0] - 1;
|
||||
|
||||
// If idx == 1 we don't need to modify offset_hist[2], since
|
||||
// we're using the second-most recent code
|
||||
if (idx > 1) {
|
||||
offset_hist[2] = offset_hist[1];
|
||||
}
|
||||
offset_hist[1] = offset_hist[0];
|
||||
offset_hist[0] = offset;
|
||||
}
|
||||
} else {
|
||||
// When it's not a repeat offset:
|
||||
// "if (Offset_Value > 3) offset = Offset_Value - 3;"
|
||||
offset = seq.offset - 3;
|
||||
|
||||
// Shift back history
|
||||
offset_hist[2] = offset_hist[1];
|
||||
offset_hist[1] = offset_hist[0];
|
||||
offset_hist[0] = offset;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
static void execute_match_copy(frame_context_t *const ctx, size_t offset,
|
||||
size_t match_length, size_t total_output,
|
||||
ostream_t *const out) {
|
||||
u8 *write_ptr = IO_get_write_ptr(out, match_length);
|
||||
if (total_output <= ctx->header.window_size) {
|
||||
// In this case offset might go back into the dictionary
|
||||
if (offset > total_output + ctx->dict_content_len) {
|
||||
// The offset goes beyond even the dictionary
|
||||
CORRUPTION();
|
||||
}
|
||||
|
||||
if (offset > total_output) {
|
||||
// "The rest of the dictionary is its content. The content act
|
||||
// as a "past" in front of data to compress or decompress, so it
|
||||
// can be referenced in sequence commands."
|
||||
const size_t dict_copy =
|
||||
MIN(offset - total_output, match_length);
|
||||
const size_t dict_offset =
|
||||
ctx->dict_content_len - (offset - total_output);
|
||||
|
||||
memcpy(write_ptr, ctx->dict_content + dict_offset, dict_copy);
|
||||
write_ptr += dict_copy;
|
||||
match_length -= dict_copy;
|
||||
}
|
||||
} else if (offset > ctx->header.window_size) {
|
||||
CORRUPTION();
|
||||
}
|
||||
|
||||
// We must copy byte by byte because the match length might be larger
|
||||
// than the offset
|
||||
// ex: if the output so far was "abc", a command with offset=3 and
|
||||
// match_length=6 would produce "abcabcabc" as the new output
|
||||
for (size_t j = 0; j < match_length; j++) {
|
||||
*write_ptr = *(write_ptr - offset);
|
||||
write_ptr++;
|
||||
}
|
||||
}
|
||||
/******* END SEQUENCE EXECUTION ***********************************************/
|
||||
|
||||
/******* OUTPUT SIZE COUNTING *************************************************/
|
||||
static void traverse_frame(const frame_header_t *const header, istream_t *const in);
|
||||
|
||||
/// Get the decompressed size of an input stream so memory can be allocated in
|
||||
/// advance.
|
||||
/// This is more complex than the implementation in the reference
|
||||
/// implementation, as this API allows for the decompression of multiple
|
||||
/// concatenated frames.
|
||||
/// This implementation assumes `src` points to a single ZSTD-compressed frame
|
||||
size_t ZSTD_get_decompressed_size(const void *src, const size_t src_len) {
|
||||
istream_t in = IO_make_istream(src, src_len);
|
||||
size_t dst_size = 0;
|
||||
|
||||
// Each frame header only gives us the size of its frame, so iterate over
|
||||
// all
|
||||
// frames
|
||||
while (IO_istream_len(&in) > 0) {
|
||||
// get decompressed size from ZSTD frame header
|
||||
{
|
||||
const u32 magic_number = IO_read_bits(&in, 32);
|
||||
|
||||
if ((magic_number & ~0xFU) == 0x184D2A50U) {
|
||||
// skippable frame, this has no impact on output size
|
||||
const size_t frame_size = IO_read_bits(&in, 32);
|
||||
IO_advance_input(&in, frame_size);
|
||||
} else if (magic_number == 0xFD2FB528U) {
|
||||
if (magic_number == 0xFD2FB528U) {
|
||||
// ZSTD frame
|
||||
frame_header_t header;
|
||||
parse_frame_header(&header, &in);
|
||||
@@ -1451,68 +1421,42 @@ size_t ZSTD_get_decompressed_size(const void *src, const size_t src_len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
dst_size += header.frame_content_size;
|
||||
|
||||
// Consume the input from the frame to reach the start of the next
|
||||
traverse_frame(&header, &in);
|
||||
return header.frame_content_size;
|
||||
} else {
|
||||
// not a real frame
|
||||
ERROR("Invalid magic number");
|
||||
// not a real frame or skippable frame
|
||||
ERROR("ZSTD frame magic number did not match");
|
||||
}
|
||||
}
|
||||
|
||||
return dst_size;
|
||||
}
|
||||
|
||||
/// Iterate over each block in a frame to find the end of it, to get to the
|
||||
/// start of the next frame
|
||||
static void traverse_frame(const frame_header_t *const header, istream_t *const in) {
|
||||
int last_block = 0;
|
||||
|
||||
do {
|
||||
// Parse the block header
|
||||
last_block = IO_read_bits(in, 1);
|
||||
const int block_type = IO_read_bits(in, 2);
|
||||
const size_t block_len = IO_read_bits(in, 21);
|
||||
|
||||
switch (block_type) {
|
||||
case 0: // Raw block, block_len bytes
|
||||
IO_advance_input(in, block_len);
|
||||
break;
|
||||
case 1: // RLE block, 1 byte
|
||||
IO_advance_input(in, 1);
|
||||
break;
|
||||
case 2: // Compressed block, compressed size is block_len
|
||||
IO_advance_input(in, block_len);
|
||||
break;
|
||||
case 3:
|
||||
// Reserved block type
|
||||
CORRUPTION();
|
||||
break;
|
||||
default:
|
||||
IMPOSSIBLE();
|
||||
}
|
||||
} while (!last_block);
|
||||
|
||||
if (header->content_checksum_flag) {
|
||||
IO_advance_input(in, 4);
|
||||
}
|
||||
}
|
||||
|
||||
/******* END OUTPUT SIZE COUNTING *********************************************/
|
||||
|
||||
/******* DICTIONARY PARSING ***************************************************/
|
||||
#define DICT_SIZE_ERROR() ERROR("Dictionary size cannot be less than 8 bytes")
|
||||
#define NULL_SRC() ERROR("Tried to create dictionary with pointer to null src");
|
||||
|
||||
dictionary_t* create_dictionary() {
|
||||
dictionary_t* dict = calloc(1, sizeof(dictionary_t));
|
||||
if (!dict) {
|
||||
BAD_ALLOC();
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
|
||||
static void init_dictionary_content(dictionary_t *const dict,
|
||||
istream_t *const in);
|
||||
|
||||
static void parse_dictionary(dictionary_t *const dict, const u8 *src,
|
||||
void parse_dictionary(dictionary_t *const dict, const void *src,
|
||||
size_t src_len) {
|
||||
const u8 *byte_src = (const u8 *)src;
|
||||
memset(dict, 0, sizeof(dictionary_t));
|
||||
if (src == NULL) { /* cannot initialize dictionary with null src */
|
||||
NULL_SRC();
|
||||
}
|
||||
if (src_len < 8) {
|
||||
INP_SIZE();
|
||||
DICT_SIZE_ERROR();
|
||||
}
|
||||
|
||||
istream_t in = IO_make_istream(src, src_len);
|
||||
istream_t in = IO_make_istream(byte_src, src_len);
|
||||
|
||||
const u32 magic_number = IO_read_bits(&in, 32);
|
||||
if (magic_number != 0xEC30A437) {
|
||||
@@ -1564,13 +1508,13 @@ static void init_dictionary_content(dictionary_t *const dict,
|
||||
BAD_ALLOC();
|
||||
}
|
||||
|
||||
const u8 *const content = IO_read_bytes(in, dict->content_size);
|
||||
const u8 *const content = IO_get_read_ptr(in, dict->content_size);
|
||||
|
||||
memcpy(dict->content, content, dict->content_size);
|
||||
}
|
||||
|
||||
/// Free an allocated dictionary
|
||||
static void free_dictionary(dictionary_t *const dict) {
|
||||
void free_dictionary(dictionary_t *const dict) {
|
||||
HUF_free_dtable(&dict->literals_dtable);
|
||||
FSE_free_dtable(&dict->ll_dtable);
|
||||
FSE_free_dtable(&dict->of_dtable);
|
||||
@@ -1579,6 +1523,8 @@ static void free_dictionary(dictionary_t *const dict) {
|
||||
free(dict->content);
|
||||
|
||||
memset(dict, 0, sizeof(dictionary_t));
|
||||
|
||||
free(dict);
|
||||
}
|
||||
/******* END DICTIONARY PARSING ***********************************************/
|
||||
|
||||
@@ -1657,7 +1603,7 @@ static inline size_t IO_istream_len(const istream_t *const in) {
|
||||
|
||||
/// Returns a pointer where `len` bytes can be read, and advances the internal
|
||||
/// state. The stream must be byte aligned.
|
||||
static inline const u8 *IO_read_bytes(istream_t *const in, size_t len) {
|
||||
static inline const u8 *IO_get_read_ptr(istream_t *const in, size_t len) {
|
||||
if (len > in->len) {
|
||||
INP_SIZE();
|
||||
}
|
||||
@@ -1671,7 +1617,7 @@ static inline const u8 *IO_read_bytes(istream_t *const in, size_t len) {
|
||||
return ptr;
|
||||
}
|
||||
/// Returns a pointer to write `len` bytes to, and advances the internal state
|
||||
static inline u8 *IO_write_bytes(ostream_t *const out, size_t len) {
|
||||
static inline u8 *IO_get_write_ptr(ostream_t *const out, size_t len) {
|
||||
if (len > out->len) {
|
||||
OUT_SIZE();
|
||||
}
|
||||
@@ -1710,7 +1656,7 @@ static inline istream_t IO_make_istream(const u8 *in, size_t len) {
|
||||
/// `in` must be byte aligned
|
||||
static inline istream_t IO_make_sub_istream(istream_t *const in, size_t len) {
|
||||
// Consume `len` bytes of the parent stream
|
||||
const u8 *const ptr = IO_read_bytes(in, len);
|
||||
const u8 *const ptr = IO_get_read_ptr(in, len);
|
||||
|
||||
// Make a substream using the pointer to those `len` bytes
|
||||
return IO_make_istream(ptr, len);
|
||||
@@ -1814,7 +1760,7 @@ static size_t HUF_decompress_1stream(const HUF_dtable *const dtable,
|
||||
if (len == 0) {
|
||||
INP_SIZE();
|
||||
}
|
||||
const u8 *const src = IO_read_bytes(in, len);
|
||||
const u8 *const src = IO_get_read_ptr(in, len);
|
||||
|
||||
// "Each bitstream must be read backward, that is starting from the end down
|
||||
// to the beginning. Therefore it's necessary to know the size of each
|
||||
@@ -2065,7 +2011,7 @@ static size_t FSE_decompress_interleaved2(const FSE_dtable *const dtable,
|
||||
if (len == 0) {
|
||||
INP_SIZE();
|
||||
}
|
||||
const u8 *const src = IO_read_bytes(in, len);
|
||||
const u8 *const src = IO_get_read_ptr(in, len);
|
||||
|
||||
// "Each bitstream must be read backward, that is starting from the end down
|
||||
// to the beginning. Therefore it's necessary to know the size of each
|
||||
@@ -2192,7 +2138,7 @@ static void FSE_init_dtable(FSE_dtable *const dtable,
|
||||
}
|
||||
|
||||
// Now we can fill baseline and num bits
|
||||
for (int i = 0; i < size; i++) {
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
u8 symbol = dtable->symbols[i];
|
||||
u16 next_state_desc = state_desc[symbol]++;
|
||||
// Fills in the table appropriately, next_state_desc increases by symbol
|
||||
@@ -2355,4 +2301,3 @@ static void FSE_copy_dtable(FSE_dtable *const dst, const FSE_dtable *const src)
|
||||
memcpy(dst->new_state_base, src->new_state_base, size * sizeof(u16));
|
||||
}
|
||||
/******* END FSE PRIMITIVES ***************************************************/
|
||||
|
||||
|
||||
@@ -7,10 +7,52 @@
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
size_t ZSTD_decompress(void *const dst, const size_t dst_len,
|
||||
const void *const src, const size_t src_len);
|
||||
size_t ZSTD_decompress_with_dict(void *const dst, const size_t dst_len,
|
||||
const void *const src, const size_t src_len,
|
||||
const void *const dict, const size_t dict_len);
|
||||
size_t ZSTD_get_decompressed_size(const void *const src, const size_t src_len);
|
||||
/******* EXPOSED TYPES ********************************************************/
|
||||
/*
|
||||
* Contains the parsed contents of a dictionary
|
||||
* This includes Huffman and FSE tables used for decoding and data on offsets
|
||||
*/
|
||||
typedef struct dictionary_s dictionary_t;
|
||||
/******* END EXPOSED TYPES ****************************************************/
|
||||
|
||||
/******* DECOMPRESSION FUNCTIONS **********************************************/
|
||||
/// Zstandard decompression functions.
|
||||
/// `dst` must point to a space at least as large as the reconstructed output.
|
||||
size_t ZSTD_decompress(void *const dst, const size_t dst_len,
|
||||
const void *const src, const size_t src_len);
|
||||
|
||||
/// If `dict != NULL` and `dict_len >= 8`, does the same thing as
|
||||
/// `ZSTD_decompress` but uses the provided dict
|
||||
size_t ZSTD_decompress_with_dict(void *const dst, const size_t dst_len,
|
||||
const void *const src, const size_t src_len,
|
||||
dictionary_t* parsed_dict);
|
||||
|
||||
/// Get the decompressed size of an input stream so memory can be allocated in
|
||||
/// advance
|
||||
/// Returns -1 if the size can't be determined
|
||||
/// Assumes decompression of a single frame
|
||||
size_t ZSTD_get_decompressed_size(const void *const src, const size_t src_len);
|
||||
/******* END DECOMPRESSION FUNCTIONS ******************************************/
|
||||
|
||||
/******* DICTIONARY MANAGEMENT ***********************************************/
|
||||
/*
|
||||
* Return a valid dictionary_t pointer for use with dictionary initialization
|
||||
* or decompression
|
||||
*/
|
||||
dictionary_t* create_dictionary();
|
||||
|
||||
/*
|
||||
* Parse a provided dictionary blob for use in decompression
|
||||
* `src` -- must point to memory space representing the dictionary
|
||||
* `src_len` -- must provide the dictionary size
|
||||
* `dict` -- will contain the parsed contents of the dictionary and
|
||||
* can be used for decompression
|
||||
*/
|
||||
void parse_dictionary(dictionary_t *const dict, const void *src,
|
||||
size_t src_len);
|
||||
|
||||
/*
|
||||
* Free internal Huffman tables, FSE tables, and dictionary content
|
||||
*/
|
||||
void free_dictionary(dictionary_t *const dict);
|
||||
/******* END DICTIONARY MANAGEMENT *******************************************/
|
||||
|
||||
@@ -16,7 +16,7 @@ Distribution of this document is unlimited.
|
||||
|
||||
### Version
|
||||
|
||||
0.2.5 (31/03/17)
|
||||
0.2.6 (19/08/17)
|
||||
|
||||
|
||||
Introduction
|
||||
@@ -106,7 +106,7 @@ The structure of a single Zstandard frame is following:
|
||||
|
||||
| `Magic_Number` | `Frame_Header` |`Data_Block`| [More data blocks] | [`Content_Checksum`] |
|
||||
|:--------------:|:--------------:|:----------:| ------------------ |:--------------------:|
|
||||
| 4 bytes | 2-14 bytes | n bytes | | 0-4 bytes |
|
||||
| 4 bytes | 2-14 bytes | n bytes | | 0-4 bytes |
|
||||
|
||||
__`Magic_Number`__
|
||||
|
||||
@@ -1249,23 +1249,30 @@ Consequently, a last byte of `0` is not possible.
|
||||
And the final-bit-flag itself is not part of the useful bitstream.
|
||||
Hence, the last byte contains between 0 and 7 useful bits.
|
||||
|
||||
For example, if the literal sequence "0145" was encoded using the prefix codes above,
|
||||
it would be encoded as:
|
||||
```
|
||||
00000001 01110000
|
||||
```
|
||||
Starting from the end,
|
||||
it's possible to read the bitstream in a __little-endian__ fashion,
|
||||
keeping track of already used bits. Since the bitstream is encoded in reverse
|
||||
order, starting from the end read symbols in forward order.
|
||||
|
||||
For example, if the literal sequence "0145" was encoded using above prefix code,
|
||||
it would be encoded (in reverse order) as:
|
||||
|
||||
|Symbol | 5 | 4 | 1 | 0 | Padding |
|
||||
|--------|------|------|----|---|---------|
|
||||
|Encoding|`0000`|`0001`|`01`|`1`| `10000` |
|
||||
|Encoding|`0000`|`0001`|`01`|`1`| `00001` |
|
||||
|
||||
Starting from the end,
|
||||
it's possible to read the bitstream in a __little-endian__ fashion,
|
||||
keeping track of already used bits. Since the bitstream is encoded in reverse
|
||||
order, by starting at the end the symbols can be read in forward order.
|
||||
Resulting in following 2-bytes bitstream :
|
||||
```
|
||||
00010000 00001101
|
||||
```
|
||||
|
||||
Reading the last `Max_Number_of_Bits` bits,
|
||||
it's then possible to compare extracted value to decoding table,
|
||||
Here is an alternative representation with the symbol codes separated by underscore:
|
||||
```
|
||||
0001_0000 00001_1_01
|
||||
```
|
||||
|
||||
Reading highest `Max_Number_of_Bits` bits,
|
||||
it's possible to compare extracted value to decoding table,
|
||||
determining the symbol to decode and number of bits to discard.
|
||||
|
||||
The process continues up to reading the required number of symbols per stream.
|
||||
@@ -1516,12 +1523,13 @@ to crosscheck that an implementation build its decoding tables correctly.
|
||||
|
||||
Version changes
|
||||
---------------
|
||||
- 0.2.6 : fixed an error in huffman example, by Ulrich Kunitz
|
||||
- 0.2.5 : minor typos and clarifications
|
||||
- 0.2.4 : section restructuring, by Sean Purcell
|
||||
- 0.2.3 : clarified several details, by Sean Purcell
|
||||
- 0.2.2 : added predefined codes, by Johannes Rudolph
|
||||
- 0.2.1 : clarify field names, by Przemyslaw Skibinski
|
||||
- 0.2.0 : numerous format adjustments for zstd v0.8
|
||||
- 0.2.0 : numerous format adjustments for zstd v0.8+
|
||||
- 0.1.2 : limit Huffman tree depth to 11 bits
|
||||
- 0.1.1 : reserved dictID ranges
|
||||
- 0.1.0 : initial release
|
||||
|
||||
+55
-49
@@ -1,10 +1,10 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<title>zstd 1.3.0 Manual</title>
|
||||
<title>zstd 1.3.1 Manual</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>zstd 1.3.0 Manual</h1>
|
||||
<h1>zstd 1.3.1 Manual</h1>
|
||||
<hr>
|
||||
<a name="Contents"></a><h2>Contents</h2>
|
||||
<ol>
|
||||
@@ -73,27 +73,41 @@
|
||||
or an errorCode if it fails (which can be tested using ZSTD_isError()).
|
||||
</p></pre><BR>
|
||||
|
||||
<pre><b>unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize);
|
||||
</b><p> NOTE: This function is planned to be obsolete, in favor of ZSTD_getFrameContentSize().
|
||||
ZSTD_getFrameContentSize() works the same way,
|
||||
returning the decompressed size of a single frame,
|
||||
but distinguishes empty frames from frames with an unknown size, or errors.
|
||||
|
||||
'src' is the start of a zstd compressed frame.
|
||||
@return : content size to be decompressed, as a 64-bits value _if known_, 0 otherwise.
|
||||
note 1 : decompressed size is an optional field, it may not be present, typically in streaming mode.
|
||||
When `return==0`, data to decompress could be any size.
|
||||
<pre><b>#define ZSTD_CONTENTSIZE_UNKNOWN (0ULL - 1)
|
||||
#define ZSTD_CONTENTSIZE_ERROR (0ULL - 2)
|
||||
unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize);
|
||||
</b><p> `src` should point to the start of a ZSTD encoded frame.
|
||||
`srcSize` must be at least as large as the frame header.
|
||||
hint : any size >= `ZSTD_frameHeaderSize_max` is large enough.
|
||||
@return : - decompressed size of the frame in `src`, if known
|
||||
- ZSTD_CONTENTSIZE_UNKNOWN if the size cannot be determined
|
||||
- ZSTD_CONTENTSIZE_ERROR if an error occurred (e.g. invalid magic number, srcSize too small)
|
||||
note 1 : a 0 return value means the frame is valid but "empty".
|
||||
note 2 : decompressed size is an optional field, it may not be present, typically in streaming mode.
|
||||
When `return==ZSTD_CONTENTSIZE_UNKNOWN`, data to decompress could be any size.
|
||||
In which case, it's necessary to use streaming mode to decompress data.
|
||||
Optionally, application can use ZSTD_decompress() while relying on implied limits.
|
||||
(For example, data may be necessarily cut into blocks <= 16 KB).
|
||||
note 2 : decompressed size is always present when compression is done with ZSTD_compress()
|
||||
note 3 : decompressed size can be very large (64-bits value),
|
||||
Optionally, application can rely on some implicit limit,
|
||||
as ZSTD_decompress() only needs an upper bound of decompressed size.
|
||||
(For example, data could be necessarily cut into blocks <= 16 KB).
|
||||
note 3 : decompressed size is always present when compression is done with ZSTD_compress()
|
||||
note 4 : decompressed size can be very large (64-bits value),
|
||||
potentially larger than what local system can handle as a single memory segment.
|
||||
In which case, it's necessary to use streaming mode to decompress data.
|
||||
note 4 : If source is untrusted, decompressed size could be wrong or intentionally modified.
|
||||
Always ensure result fits within application's authorized limits.
|
||||
note 5 : If source is untrusted, decompressed size could be wrong or intentionally modified.
|
||||
Always ensure return value fits within application's authorized limits.
|
||||
Each application can set its own limits.
|
||||
note 5 : when `return==0`, if precise failure cause is needed, use ZSTD_getFrameHeader() to know more.
|
||||
note 6 : This function replaces ZSTD_getDecompressedSize()
|
||||
</p></pre><BR>
|
||||
|
||||
<pre><b>unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize);
|
||||
</b><p> NOTE: This function is now obsolete, in favor of ZSTD_getFrameContentSize().
|
||||
Both functions work the same way,
|
||||
but ZSTD_getDecompressedSize() blends
|
||||
"empty", "unknown" and "error" results in the same return value (0),
|
||||
while ZSTD_getFrameContentSize() distinguishes them.
|
||||
|
||||
'src' is the start of a zstd compressed frame.
|
||||
@return : content size to be decompressed, as a 64-bits value _if known and not empty_, 0 otherwise.
|
||||
</p></pre><BR>
|
||||
|
||||
<h3>Helper functions</h3><pre></pre><b><pre>int ZSTD_maxCLevel(void); </b>/*!< maximum compression level available */<b>
|
||||
@@ -298,8 +312,8 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
|
||||
<pre><b>size_t ZSTD_DStreamOutSize(void); </b>/*!< recommended size for output buffer. Guarantee to successfully flush at least one complete block in all circumstances. */<b>
|
||||
</b></pre><BR>
|
||||
<a name="Chapter10"></a><h2>START OF ADVANCED AND EXPERIMENTAL FUNCTIONS</h2><pre> The definitions in this section are considered experimental.
|
||||
They should never be used with a dynamic library, as they may change in the future.
|
||||
They are provided for advanced usages.
|
||||
They should never be used with a dynamic library, as prototypes may change in the future.
|
||||
They are provided for advanced scenarios.
|
||||
Use them only in association with static linking.
|
||||
|
||||
<BR></pre>
|
||||
@@ -330,13 +344,6 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
|
||||
ZSTD_frameParameters fParams;
|
||||
} ZSTD_parameters;
|
||||
</b></pre><BR>
|
||||
<pre><b>typedef struct {
|
||||
unsigned long long frameContentSize;
|
||||
size_t windowSize;
|
||||
unsigned dictID;
|
||||
unsigned checksumFlag;
|
||||
} ZSTD_frameHeader;
|
||||
</b></pre><BR>
|
||||
<h3>Custom memory allocation functions</h3><pre></pre><b><pre>typedef void* (*ZSTD_allocFunction) (void* opaque, size_t size);
|
||||
typedef void (*ZSTD_freeFunction) (void* opaque, void* address);
|
||||
typedef struct { ZSTD_allocFunction customAlloc; ZSTD_freeFunction customFree; void* opaque; } ZSTD_customMem;
|
||||
@@ -348,26 +355,15 @@ static const ZSTD_customMem ZSTD_defaultCMem = { NULL, NULL, NULL };
|
||||
<pre><b>size_t ZSTD_findFrameCompressedSize(const void* src, size_t srcSize);
|
||||
</b><p> `src` should point to the start of a ZSTD encoded frame or skippable frame
|
||||
`srcSize` must be at least as large as the frame
|
||||
@return : the compressed size of the frame pointed to by `src`,
|
||||
@return : the compressed size of the first frame starting at `src`,
|
||||
suitable to pass to `ZSTD_decompress` or similar,
|
||||
or an error code if given invalid input.
|
||||
</p></pre><BR>
|
||||
|
||||
<pre><b>#define ZSTD_CONTENTSIZE_UNKNOWN (0ULL - 1)
|
||||
#define ZSTD_CONTENTSIZE_ERROR (0ULL - 2)
|
||||
unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize);
|
||||
</b><p> `src` should point to the start of a ZSTD encoded frame.
|
||||
`srcSize` must be at least as large as the frame header.
|
||||
A value >= `ZSTD_frameHeaderSize_max` is guaranteed to be large enough.
|
||||
@return : - decompressed size of the frame pointed to be `src` if known
|
||||
- ZSTD_CONTENTSIZE_UNKNOWN if the size cannot be determined
|
||||
- ZSTD_CONTENTSIZE_ERROR if an error occurred (e.g. invalid magic number, srcSize too small)
|
||||
or an error code if input is invalid
|
||||
</p></pre><BR>
|
||||
|
||||
<pre><b>unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize);
|
||||
</b><p> `src` should point the start of a series of ZSTD encoded and/or skippable frames
|
||||
`srcSize` must be the _exact_ size of this series
|
||||
(i.e. there should be a frame boundary exactly `srcSize` bytes after `src`)
|
||||
(i.e. there should be a frame boundary exactly at `srcSize` bytes after `src`)
|
||||
@return : - decompressed size of all data in all successive frames
|
||||
- if the decompressed size cannot be determined: ZSTD_CONTENTSIZE_UNKNOWN
|
||||
- if an error occurred: ZSTD_CONTENTSIZE_ERROR
|
||||
@@ -375,8 +371,6 @@ unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize);
|
||||
note 1 : decompressed size is an optional field, that may not be present, especially in streaming mode.
|
||||
When `return==ZSTD_CONTENTSIZE_UNKNOWN`, data to decompress could be any size.
|
||||
In which case, it's necessary to use streaming mode to decompress data.
|
||||
Optionally, application can still use ZSTD_decompress() while relying on implied limits.
|
||||
(For example, data may be necessarily cut into blocks <= 16 KB).
|
||||
note 2 : decompressed size is always present when compression is done with ZSTD_compress()
|
||||
note 3 : decompressed size can be very large (64-bits value),
|
||||
potentially larger than what local system can handle as a single memory segment.
|
||||
@@ -385,7 +379,7 @@ unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize);
|
||||
Always ensure result fits within application's authorized limits.
|
||||
Each application can set its own limits.
|
||||
note 5 : ZSTD_findDecompressedSize handles multiple frames, and so it must traverse the input to
|
||||
read each contained frame header. This is efficient as most of the data is skipped,
|
||||
read each contained frame header. This is fast as most of the data is skipped,
|
||||
however it does mean that all frame data must be present and valid.
|
||||
</p></pre><BR>
|
||||
|
||||
@@ -483,14 +477,15 @@ size_t ZSTD_estimateDDictSize(size_t dictSize, unsigned byReference);
|
||||
It is important that dictBuffer outlives CDict, it must remain read accessible throughout the lifetime of CDict
|
||||
</p></pre><BR>
|
||||
|
||||
<pre><b>typedef enum { ZSTD_dm_auto=0, </b>/* dictionary is "full" if it starts with ZSTD_MAGIC_DICTIONARY, rawContent otherwize */<b>
|
||||
<pre><b>typedef enum { ZSTD_dm_auto=0, </b>/* dictionary is "full" if it starts with ZSTD_MAGIC_DICTIONARY, otherwise it is "rawContent" */<b>
|
||||
ZSTD_dm_rawContent, </b>/* ensures dictionary is always loaded as rawContent, even if it starts with ZSTD_MAGIC_DICTIONARY */<b>
|
||||
ZSTD_dm_fullDict </b>/* refuses to load a dictionary if it does not respect Zstandard's specification */<b>
|
||||
} ZSTD_dictMode_e;
|
||||
</b></pre><BR>
|
||||
<pre><b>ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictSize,
|
||||
unsigned byReference, ZSTD_dictMode_e dictMode,
|
||||
ZSTD_compressionParameters cParams, ZSTD_customMem customMem);
|
||||
ZSTD_compressionParameters cParams,
|
||||
ZSTD_customMem customMem);
|
||||
</b><p> Create a ZSTD_CDict using external alloc and free, and customized compression parameters
|
||||
</p></pre><BR>
|
||||
|
||||
@@ -760,7 +755,16 @@ size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned lo
|
||||
It also returns Frame Size as fparamsPtr->frameContentSize.
|
||||
<BR></pre>
|
||||
|
||||
<h3>Buffer-less streaming decompression functions</h3><pre></pre><b><pre>size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize); </b>/**< doesn't consume input */<b>
|
||||
<h3>Buffer-less streaming decompression functions</h3><pre></pre><b><pre>typedef enum { ZSTD_frame, ZSTD_skippableFrame } ZSTD_frameType_e;
|
||||
typedef struct {
|
||||
unsigned long long frameContentSize; </b>/* ZSTD_CONTENTSIZE_UNKNOWN means this field is not available. 0 means "empty" */<b>
|
||||
unsigned long long windowSize; </b>/* can be very large, up to <= frameContentSize */<b>
|
||||
ZSTD_frameType_e frameType; </b>/* if == ZSTD_skippableFrame, frameContentSize is the size of skippable content */<b>
|
||||
unsigned headerSize;
|
||||
unsigned dictID;
|
||||
unsigned checksumFlag;
|
||||
} ZSTD_frameHeader;
|
||||
size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize); </b>/**< doesn't consume input */<b>
|
||||
size_t ZSTD_decompressBegin(ZSTD_DCtx* dctx);
|
||||
size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);
|
||||
size_t ZSTD_decompressBegin_usingDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict);
|
||||
@@ -809,7 +813,9 @@ void ZSTD_copyDCtx(ZSTD_DCtx* dctx, const ZSTD_DCtx* preparedDCtx);
|
||||
* Special: value 0 means "do not change strategy". */
|
||||
|
||||
</b>/* frame parameters */<b>
|
||||
ZSTD_p_contentSizeFlag=200, </b>/* Content size is written into frame header _whenever known_ (default:1) */<b>
|
||||
ZSTD_p_contentSizeFlag=200, </b>/* Content size is written into frame header _whenever known_ (default:1)<b>
|
||||
* note that content size must be known at the beginning,
|
||||
* it is sent using ZSTD_CCtx_setPledgedSrcSize() */
|
||||
ZSTD_p_checksumFlag, </b>/* A 32-bits checksum of content is written at end of frame (default:0) */<b>
|
||||
ZSTD_p_dictIDFlag, </b>/* When applicable, dictID of dictionary is provided in frame header (default:1) */<b>
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
/**
|
||||
* Copyright 2016-present, Yann Collet, Facebook, Inc.
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the license found in the
|
||||
* LICENSE-examples file in the root directory of this source tree.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
/**
|
||||
* Copyright 2016-present, Yann Collet, Facebook, Inc.
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the license found in the
|
||||
* LICENSE-examples file in the root directory of this source tree.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
/**
|
||||
* Copyright 2016-present, Yann Collet, Facebook, Inc.
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the license found in the
|
||||
* LICENSE-examples file in the root directory of this source tree.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
/**
|
||||
* Copyright 2016-present, Yann Collet, Facebook, Inc.
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the license found in the
|
||||
* LICENSE-examples file in the root directory of this source tree.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
/**
|
||||
* Copyright 2016-present, Yann Collet, Facebook, Inc.
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the license found in the
|
||||
* LICENSE-examples file in the root directory of this source tree.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
#include <stdlib.h> // malloc, exit
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
/**
|
||||
* Copyright 2016-present, Yann Collet, Facebook, Inc.
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the license found in the
|
||||
* LICENSE-examples file in the root directory of this source tree.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
/**
|
||||
* Copyright 2016-present, Yann Collet, Facebook, Inc.
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the license found in the
|
||||
* LICENSE-examples file in the root directory of this source tree.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
+40
-41
@@ -80,9 +80,9 @@ extern "C" {
|
||||
* bitStream encoding API (write forward)
|
||||
********************************************/
|
||||
/* bitStream can mix input from multiple sources.
|
||||
* A critical property of these streams is that they encode and decode in **reverse** direction.
|
||||
* So the first bit sequence you add will be the last to be read, like a LIFO stack.
|
||||
*/
|
||||
* A critical property of these streams is that they encode and decode in **reverse** direction.
|
||||
* So the first bit sequence you add will be the last to be read, like a LIFO stack.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
size_t bitContainer;
|
||||
@@ -203,7 +203,7 @@ static const unsigned BIT_mask[] = { 0, 1, 3, 7, 0xF, 0x1F, 0x3F, 0x7F,
|
||||
/*! BIT_initCStream() :
|
||||
* `dstCapacity` must be > sizeof(size_t)
|
||||
* @return : 0 if success,
|
||||
otherwise an error code (can be tested using ERR_isError() ) */
|
||||
* otherwise an error code (can be tested using ERR_isError()) */
|
||||
MEM_STATIC size_t BIT_initCStream(BIT_CStream_t* bitC,
|
||||
void* startPtr, size_t dstCapacity)
|
||||
{
|
||||
@@ -217,8 +217,8 @@ MEM_STATIC size_t BIT_initCStream(BIT_CStream_t* bitC,
|
||||
}
|
||||
|
||||
/*! BIT_addBits() :
|
||||
can add up to 26 bits into `bitC`.
|
||||
Does not check for register overflow ! */
|
||||
* can add up to 26 bits into `bitC`.
|
||||
* Note : does not check for register overflow ! */
|
||||
MEM_STATIC void BIT_addBits(BIT_CStream_t* bitC,
|
||||
size_t value, unsigned nbBits)
|
||||
{
|
||||
@@ -268,7 +268,7 @@ MEM_STATIC void BIT_flushBits(BIT_CStream_t* bitC)
|
||||
|
||||
/*! BIT_closeCStream() :
|
||||
* @return : size of CStream, in bytes,
|
||||
or 0 if it could not fit into dstBuffer */
|
||||
* or 0 if it could not fit into dstBuffer */
|
||||
MEM_STATIC size_t BIT_closeCStream(BIT_CStream_t* bitC)
|
||||
{
|
||||
BIT_addBitsFast(bitC, 1, 1); /* endMark */
|
||||
@@ -279,14 +279,14 @@ MEM_STATIC size_t BIT_closeCStream(BIT_CStream_t* bitC)
|
||||
|
||||
|
||||
/*-********************************************************
|
||||
* bitStream decoding
|
||||
* bitStream decoding
|
||||
**********************************************************/
|
||||
/*! BIT_initDStream() :
|
||||
* Initialize a BIT_DStream_t.
|
||||
* `bitD` : a pointer to an already allocated BIT_DStream_t structure.
|
||||
* `srcSize` must be the *exact* size of the bitStream, in bytes.
|
||||
* @return : size of stream (== srcSize) or an errorCode if a problem is detected
|
||||
*/
|
||||
* Initialize a BIT_DStream_t.
|
||||
* `bitD` : a pointer to an already allocated BIT_DStream_t structure.
|
||||
* `srcSize` must be the *exact* size of the bitStream, in bytes.
|
||||
* @return : size of stream (== srcSize), or an errorCode if a problem is detected
|
||||
*/
|
||||
MEM_STATIC size_t BIT_initDStream(BIT_DStream_t* bitD, const void* srcBuffer, size_t srcSize)
|
||||
{
|
||||
if (srcSize < 1) { memset(bitD, 0, sizeof(*bitD)); return ERROR(srcSize_wrong); }
|
||||
@@ -305,29 +305,30 @@ MEM_STATIC size_t BIT_initDStream(BIT_DStream_t* bitD, const void* srcBuffer, si
|
||||
bitD->bitContainer = *(const BYTE*)(bitD->start);
|
||||
switch(srcSize)
|
||||
{
|
||||
case 7: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[6]) << (sizeof(bitD->bitContainer)*8 - 16);
|
||||
/* fall-through */
|
||||
case 7: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[6]) << (sizeof(bitD->bitContainer)*8 - 16);
|
||||
/* fall-through */
|
||||
|
||||
case 6: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[5]) << (sizeof(bitD->bitContainer)*8 - 24);
|
||||
/* fall-through */
|
||||
case 6: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[5]) << (sizeof(bitD->bitContainer)*8 - 24);
|
||||
/* fall-through */
|
||||
|
||||
case 5: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[4]) << (sizeof(bitD->bitContainer)*8 - 32);
|
||||
/* fall-through */
|
||||
case 5: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[4]) << (sizeof(bitD->bitContainer)*8 - 32);
|
||||
/* fall-through */
|
||||
|
||||
case 4: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[3]) << 24;
|
||||
/* fall-through */
|
||||
case 4: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[3]) << 24;
|
||||
/* fall-through */
|
||||
|
||||
case 3: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[2]) << 16;
|
||||
/* fall-through */
|
||||
case 3: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[2]) << 16;
|
||||
/* fall-through */
|
||||
|
||||
case 2: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[1]) << 8;
|
||||
/* fall-through */
|
||||
case 2: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[1]) << 8;
|
||||
/* fall-through */
|
||||
|
||||
default: break;
|
||||
default: break;
|
||||
}
|
||||
{ BYTE const lastByte = ((const BYTE*)srcBuffer)[srcSize-1];
|
||||
bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0;
|
||||
if (lastByte == 0) return ERROR(corruption_detected); /* endMark not present */
|
||||
}
|
||||
{ BYTE const lastByte = ((const BYTE*)srcBuffer)[srcSize-1];
|
||||
bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0;
|
||||
if (lastByte == 0) return ERROR(GENERIC); /* endMark not present */ }
|
||||
bitD->bitsConsumed += (U32)(sizeof(bitD->bitContainer) - srcSize)*8;
|
||||
}
|
||||
|
||||
@@ -363,9 +364,8 @@ MEM_STATIC size_t BIT_getLowerBits(size_t bitContainer, U32 const nbBits)
|
||||
* local register is not modified.
|
||||
* On 32-bits, maxNbBits==24.
|
||||
* On 64-bits, maxNbBits==56.
|
||||
* @return : value extracted
|
||||
*/
|
||||
MEM_STATIC size_t BIT_lookBits(const BIT_DStream_t* bitD, U32 nbBits)
|
||||
* @return : value extracted */
|
||||
MEM_STATIC size_t BIT_lookBits(const BIT_DStream_t* bitD, U32 nbBits)
|
||||
{
|
||||
#if defined(__BMI__) && defined(__GNUC__) /* experimental; fails if bitD->bitsConsumed + nbBits > sizeof(bitD->bitContainer)*8 */
|
||||
return BIT_getMiddleBits(bitD->bitContainer, (sizeof(bitD->bitContainer)*8) - bitD->bitsConsumed - nbBits, nbBits);
|
||||
@@ -392,8 +392,7 @@ MEM_STATIC void BIT_skipBits(BIT_DStream_t* bitD, U32 nbBits)
|
||||
/*! BIT_readBits() :
|
||||
* Read (consume) next n bits from local register and update.
|
||||
* Pay attention to not read more than nbBits contained into local register.
|
||||
* @return : extracted value.
|
||||
*/
|
||||
* @return : extracted value. */
|
||||
MEM_STATIC size_t BIT_readBits(BIT_DStream_t* bitD, U32 nbBits)
|
||||
{
|
||||
size_t const value = BIT_lookBits(bitD, nbBits);
|
||||
@@ -402,7 +401,7 @@ MEM_STATIC size_t BIT_readBits(BIT_DStream_t* bitD, U32 nbBits)
|
||||
}
|
||||
|
||||
/*! BIT_readBitsFast() :
|
||||
* unsafe version; only works only if nbBits >= 1 */
|
||||
* unsafe version; only works only if nbBits >= 1 */
|
||||
MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, U32 nbBits)
|
||||
{
|
||||
size_t const value = BIT_lookBitsFast(bitD, nbBits);
|
||||
@@ -412,10 +411,10 @@ MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, U32 nbBits)
|
||||
}
|
||||
|
||||
/*! BIT_reloadDStream() :
|
||||
* Refill `bitD` from buffer previously set in BIT_initDStream() .
|
||||
* This function is safe, it guarantees it will not read beyond src buffer.
|
||||
* @return : status of `BIT_DStream_t` internal register.
|
||||
if status == BIT_DStream_unfinished, internal register is filled with >= (sizeof(bitD->bitContainer)*8 - 7) bits */
|
||||
* Refill `bitD` from buffer previously set in BIT_initDStream() .
|
||||
* This function is safe, it guarantees it will not read beyond src buffer.
|
||||
* @return : status of `BIT_DStream_t` internal register.
|
||||
* when status == BIT_DStream_unfinished, internal register is filled with at least 25 or 57 bits */
|
||||
MEM_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD)
|
||||
{
|
||||
if (bitD->bitsConsumed > (sizeof(bitD->bitContainer)*8)) /* overflow detected, like end of stream */
|
||||
@@ -446,8 +445,8 @@ MEM_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD)
|
||||
}
|
||||
|
||||
/*! BIT_endOfDStream() :
|
||||
* @return Tells if DStream has exactly reached its end (all bits consumed).
|
||||
*/
|
||||
* @return : 1 if DStream has _exactly_ reached its end (all bits consumed).
|
||||
*/
|
||||
MEM_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t* DStream)
|
||||
{
|
||||
return ((DStream->ptr == DStream->start) && (DStream->bitsConsumed == sizeof(DStream->bitContainer)*8));
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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).
|
||||
*/
|
||||
|
||||
#ifndef ZSTD_COMPILER_H
|
||||
#define ZSTD_COMPILER_H
|
||||
|
||||
/*-*******************************************************
|
||||
* Compiler specifics
|
||||
*********************************************************/
|
||||
/* force inlining */
|
||||
#if defined (__GNUC__) || defined(__cplusplus) || defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
|
||||
# define INLINE_KEYWORD inline
|
||||
#else
|
||||
# define INLINE_KEYWORD
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
# define FORCE_INLINE_ATTR __attribute__((always_inline))
|
||||
#elif defined(_MSC_VER)
|
||||
# define FORCE_INLINE_ATTR __forceinline
|
||||
#else
|
||||
# define FORCE_INLINE_ATTR
|
||||
#endif
|
||||
|
||||
/**
|
||||
* FORCE_INLINE_TEMPLATE is used to define C "templates", which take constant
|
||||
* parameters. They must be inlined for the compiler to elimininate the constant
|
||||
* branches.
|
||||
*/
|
||||
#define FORCE_INLINE_TEMPLATE static INLINE_KEYWORD FORCE_INLINE_ATTR
|
||||
/**
|
||||
* HINT_INLINE is used to help the compiler generate better code. It is *not*
|
||||
* used for "templates", so it can be tweaked based on the compilers
|
||||
* performance.
|
||||
*
|
||||
* gcc-4.8 and gcc-4.9 have been shown to benefit from leaving off the
|
||||
* always_inline attribute.
|
||||
*
|
||||
* clang up to 5.0.0 (trunk) benefit tremendously from the always_inline
|
||||
* attribute.
|
||||
*/
|
||||
#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 8 && __GNUC__ < 5
|
||||
# define HINT_INLINE static INLINE_KEYWORD
|
||||
#else
|
||||
# define HINT_INLINE static INLINE_KEYWORD FORCE_INLINE_ATTR
|
||||
#endif
|
||||
|
||||
/* force no inlining */
|
||||
#ifdef _MSC_VER
|
||||
# define FORCE_NOINLINE static __declspec(noinline)
|
||||
#else
|
||||
# ifdef __GNUC__
|
||||
# define FORCE_NOINLINE static __attribute__((__noinline__))
|
||||
# else
|
||||
# define FORCE_NOINLINE static
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* prefetch */
|
||||
#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_I86)) /* _mm_prefetch() is not defined outside of x86/x64 */
|
||||
# include <mmintrin.h> /* https://msdn.microsoft.com/fr-fr/library/84szxsww(v=vs.90).aspx */
|
||||
# define PREFETCH(ptr) _mm_prefetch((const char*)ptr, _MM_HINT_T0)
|
||||
#elif defined(__GNUC__)
|
||||
# define PREFETCH(ptr) __builtin_prefetch(ptr, 0, 0)
|
||||
#else
|
||||
# define PREFETCH(ptr) /* disabled */
|
||||
#endif
|
||||
|
||||
/* disable warnings */
|
||||
#ifdef _MSC_VER /* Visual Studio */
|
||||
# include <intrin.h> /* For Visual 2005 */
|
||||
# pragma warning(disable : 4100) /* disable: C4100: unreferenced formal parameter */
|
||||
# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
|
||||
# pragma warning(disable : 4204) /* disable: C4204: non-constant aggregate initializer */
|
||||
# pragma warning(disable : 4214) /* disable: C4214: non-int bitfields */
|
||||
# pragma warning(disable : 4324) /* disable: C4324: padded structure */
|
||||
#endif
|
||||
|
||||
#endif /* ZSTD_COMPILER_H */
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
/* The purpose of this file is to have a single list of error strings embedded in binary */
|
||||
@@ -20,19 +20,17 @@ const char* ERR_getErrorString(ERR_enum code)
|
||||
case PREFIX(GENERIC): return "Error (generic)";
|
||||
case PREFIX(prefix_unknown): return "Unknown frame descriptor";
|
||||
case PREFIX(version_unsupported): return "Version not supported";
|
||||
case PREFIX(parameter_unknown): return "Unknown parameter type";
|
||||
case PREFIX(frameParameter_unsupported): return "Unsupported frame parameter";
|
||||
case PREFIX(frameParameter_unsupportedBy32bits): return "Frame parameter unsupported in 32-bits mode";
|
||||
case PREFIX(frameParameter_windowTooLarge): return "Frame requires too much memory for decoding";
|
||||
case PREFIX(compressionParameter_unsupported): return "Compression parameter is not supported";
|
||||
case PREFIX(compressionParameter_outOfBound): return "Compression parameter is out of bound";
|
||||
case PREFIX(corruption_detected): return "Corrupted block detected";
|
||||
case PREFIX(checksum_wrong): return "Restored data doesn't match checksum";
|
||||
case PREFIX(parameter_unsupported): return "Unsupported parameter";
|
||||
case PREFIX(parameter_outOfBound): return "Parameter is out of bound";
|
||||
case PREFIX(init_missing): return "Context should be init first";
|
||||
case PREFIX(memory_allocation): return "Allocation error : not enough memory";
|
||||
case PREFIX(stage_wrong): return "Operation not authorized at current processing stage";
|
||||
case PREFIX(dstSize_tooSmall): return "Destination buffer is too small";
|
||||
case PREFIX(srcSize_wrong): return "Src size is incorrect";
|
||||
case PREFIX(corruption_detected): return "Corrupted block detected";
|
||||
case PREFIX(checksum_wrong): return "Restored data doesn't match checksum";
|
||||
case PREFIX(tableLog_tooLarge): return "tableLog requires too much memory : unsupported";
|
||||
case PREFIX(maxSymbolValue_tooLarge): return "Unsupported max Symbol Value : too large";
|
||||
case PREFIX(maxSymbolValue_tooSmall): return "Specified maxSymbolValue is too small";
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
/* Note : this module is expected to remain private, do not expose it */
|
||||
|
||||
+11
-5
@@ -31,13 +31,14 @@
|
||||
You can contact the author at :
|
||||
- Source repository : https://github.com/Cyan4973/FiniteStateEntropy
|
||||
****************************************************************** */
|
||||
#ifndef FSE_H
|
||||
#define FSE_H
|
||||
|
||||
#if defined (__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef FSE_H
|
||||
#define FSE_H
|
||||
|
||||
|
||||
/*-*****************************************
|
||||
* Dependencies
|
||||
@@ -297,8 +298,10 @@ FSE_decompress_usingDTable() result will tell how many bytes were regenerated (<
|
||||
If there is an error, the function will return an error code, which can be tested using FSE_isError(). (ex: dst buffer too small)
|
||||
*/
|
||||
|
||||
#endif /* FSE_H */
|
||||
|
||||
#ifdef FSE_STATIC_LINKING_ONLY
|
||||
#if defined(FSE_STATIC_LINKING_ONLY) && !defined(FSE_H_FSE_STATIC_LINKING_ONLY)
|
||||
#define FSE_H_FSE_STATIC_LINKING_ONLY
|
||||
|
||||
/* *** Dependency *** */
|
||||
#include "bitstream.h"
|
||||
@@ -381,6 +384,11 @@ size_t FSE_buildDTable_rle (FSE_DTable* dt, unsigned char symbolValue);
|
||||
size_t FSE_decompress_wksp(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, FSE_DTable* workSpace, unsigned maxLog);
|
||||
/**< same as FSE_decompress(), using an externally allocated `workSpace` produced with `FSE_DTABLE_SIZE_U32(maxLog)` */
|
||||
|
||||
typedef enum {
|
||||
FSE_repeat_none, /**< Cannot use the previous table */
|
||||
FSE_repeat_check, /**< Can use the previous table but it must be checked */
|
||||
FSE_repeat_valid /**< Can use the previous table and it is asumed to be valid */
|
||||
} FSE_repeat;
|
||||
|
||||
/* *****************************************
|
||||
* FSE symbol compression API
|
||||
@@ -694,5 +702,3 @@ MEM_STATIC unsigned FSE_endOfDState(const FSE_DState_t* DStatePtr)
|
||||
#if defined (__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FSE_H */
|
||||
|
||||
@@ -33,35 +33,16 @@
|
||||
****************************************************************** */
|
||||
|
||||
|
||||
/* **************************************************************
|
||||
* Compiler specifics
|
||||
****************************************************************/
|
||||
#ifdef _MSC_VER /* Visual Studio */
|
||||
# define FORCE_INLINE static __forceinline
|
||||
# include <intrin.h> /* For Visual 2005 */
|
||||
# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
|
||||
# pragma warning(disable : 4214) /* disable: C4214: non-int bitfields */
|
||||
#else
|
||||
# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
|
||||
# ifdef __GNUC__
|
||||
# define FORCE_INLINE static inline __attribute__((always_inline))
|
||||
# else
|
||||
# define FORCE_INLINE static inline
|
||||
# endif
|
||||
# else
|
||||
# define FORCE_INLINE static
|
||||
# endif /* __STDC_VERSION__ */
|
||||
#endif
|
||||
|
||||
|
||||
/* **************************************************************
|
||||
* Includes
|
||||
****************************************************************/
|
||||
#include <stdlib.h> /* malloc, free, qsort */
|
||||
#include <string.h> /* memcpy, memset */
|
||||
#include "bitstream.h"
|
||||
#include "compiler.h"
|
||||
#define FSE_STATIC_LINKING_ONLY
|
||||
#include "fse.h"
|
||||
#include "error_private.h"
|
||||
|
||||
|
||||
/* **************************************************************
|
||||
@@ -216,7 +197,7 @@ size_t FSE_buildDTable_raw (FSE_DTable* dt, unsigned nbBits)
|
||||
return 0;
|
||||
}
|
||||
|
||||
FORCE_INLINE size_t FSE_decompress_usingDTable_generic(
|
||||
FORCE_INLINE_TEMPLATE size_t FSE_decompress_usingDTable_generic(
|
||||
void* dst, size_t maxDstSize,
|
||||
const void* cSrc, size_t cSrcSize,
|
||||
const FSE_DTable* dt, const unsigned fast)
|
||||
|
||||
+5
-6
@@ -31,13 +31,13 @@
|
||||
You can contact the author at :
|
||||
- Source repository : https://github.com/Cyan4973/FiniteStateEntropy
|
||||
****************************************************************** */
|
||||
#ifndef HUF_H_298734234
|
||||
#define HUF_H_298734234
|
||||
|
||||
#if defined (__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef HUF_H_298734234
|
||||
#define HUF_H_298734234
|
||||
|
||||
/* *** Dependencies *** */
|
||||
#include <stddef.h> /* size_t */
|
||||
@@ -124,6 +124,7 @@ HUF_PUBLIC_API size_t HUF_compress4X_wksp (void* dst, size_t dstCapacity, const
|
||||
#define HUF_DECOMPRESS_WORKSPACE_SIZE (2 << 10)
|
||||
#define HUF_DECOMPRESS_WORKSPACE_SIZE_U32 (HUF_DECOMPRESS_WORKSPACE_SIZE / sizeof(U32))
|
||||
|
||||
#endif /* HUF_H_298734234 */
|
||||
|
||||
/* ******************************************************************
|
||||
* WARNING !!
|
||||
@@ -132,7 +133,8 @@ HUF_PUBLIC_API size_t HUF_compress4X_wksp (void* dst, size_t dstCapacity, const
|
||||
* because they are not guaranteed to remain stable in the future.
|
||||
* Only consider them in association with static linking.
|
||||
*******************************************************************/
|
||||
#ifdef HUF_STATIC_LINKING_ONLY
|
||||
#if defined(HUF_STATIC_LINKING_ONLY) && !defined(HUF_H_HUF_STATIC_LINKING_ONLY)
|
||||
#define HUF_H_HUF_STATIC_LINKING_ONLY
|
||||
|
||||
/* *** Dependencies *** */
|
||||
#include "mem.h" /* U32 */
|
||||
@@ -295,9 +297,6 @@ size_t HUF_decompress1X4_usingDTable(void* dst, size_t maxDstSize, const void* c
|
||||
|
||||
#endif /* HUF_STATIC_LINKING_ONLY */
|
||||
|
||||
|
||||
#if defined (__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HUF_H_298734234 */
|
||||
|
||||
+6
-6
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
#ifndef MEM_H_MODULE
|
||||
@@ -110,7 +110,7 @@ Only use if no other choice to achieve best performance on target platform */
|
||||
MEM_STATIC U16 MEM_read16(const void* memPtr) { return *(const U16*) memPtr; }
|
||||
MEM_STATIC U32 MEM_read32(const void* memPtr) { return *(const U32*) memPtr; }
|
||||
MEM_STATIC U64 MEM_read64(const void* memPtr) { return *(const U64*) memPtr; }
|
||||
MEM_STATIC U64 MEM_readST(const void* memPtr) { return *(const size_t*) memPtr; }
|
||||
MEM_STATIC size_t MEM_readST(const void* memPtr) { return *(const size_t*) memPtr; }
|
||||
|
||||
MEM_STATIC void MEM_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; }
|
||||
MEM_STATIC void MEM_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; }
|
||||
@@ -131,7 +131,7 @@ MEM_STATIC void MEM_write64(void* memPtr, U64 value) { *(U64*)memPtr = value; }
|
||||
MEM_STATIC U16 MEM_read16(const void* ptr) { return ((const unalign*)ptr)->u16; }
|
||||
MEM_STATIC U32 MEM_read32(const void* ptr) { return ((const unalign*)ptr)->u32; }
|
||||
MEM_STATIC U64 MEM_read64(const void* ptr) { return ((const unalign*)ptr)->u64; }
|
||||
MEM_STATIC U64 MEM_readST(const void* ptr) { return ((const unalign*)ptr)->st; }
|
||||
MEM_STATIC size_t MEM_readST(const void* ptr) { return ((const unalign*)ptr)->st; }
|
||||
|
||||
MEM_STATIC void MEM_write16(void* memPtr, U16 value) { ((unalign*)memPtr)->u16 = value; }
|
||||
MEM_STATIC void MEM_write32(void* memPtr, U32 value) { ((unalign*)memPtr)->u32 = value; }
|
||||
|
||||
+61
-27
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) 2016-present, Facebook, Inc.
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
@@ -39,6 +39,12 @@ struct POOL_ctx_s {
|
||||
size_t queueHead;
|
||||
size_t queueTail;
|
||||
size_t queueSize;
|
||||
|
||||
/* The number of threads working on jobs */
|
||||
size_t numThreadsBusy;
|
||||
/* Indicates if the queue is empty */
|
||||
int queueEmpty;
|
||||
|
||||
/* The mutex protects the queue */
|
||||
pthread_mutex_t queueMutex;
|
||||
/* Condition variable for pushers to wait on when the queue is full */
|
||||
@@ -60,30 +66,41 @@ static void* POOL_thread(void* opaque) {
|
||||
for (;;) {
|
||||
/* Lock the mutex and wait for a non-empty queue or until shutdown */
|
||||
pthread_mutex_lock(&ctx->queueMutex);
|
||||
while (ctx->queueHead == ctx->queueTail && !ctx->shutdown) {
|
||||
|
||||
while (ctx->queueEmpty && !ctx->shutdown) {
|
||||
pthread_cond_wait(&ctx->queuePopCond, &ctx->queueMutex);
|
||||
}
|
||||
/* empty => shutting down: so stop */
|
||||
if (ctx->queueHead == ctx->queueTail) {
|
||||
if (ctx->queueEmpty) {
|
||||
pthread_mutex_unlock(&ctx->queueMutex);
|
||||
return opaque;
|
||||
}
|
||||
/* Pop a job off the queue */
|
||||
{ POOL_job const job = ctx->queue[ctx->queueHead];
|
||||
ctx->queueHead = (ctx->queueHead + 1) % ctx->queueSize;
|
||||
ctx->numThreadsBusy++;
|
||||
ctx->queueEmpty = ctx->queueHead == ctx->queueTail;
|
||||
/* Unlock the mutex, signal a pusher, and run the job */
|
||||
pthread_mutex_unlock(&ctx->queueMutex);
|
||||
pthread_cond_signal(&ctx->queuePushCond);
|
||||
|
||||
job.function(job.opaque);
|
||||
}
|
||||
}
|
||||
|
||||
/* If the intended queue size was 0, signal after finishing job */
|
||||
if (ctx->queueSize == 1) {
|
||||
pthread_mutex_lock(&ctx->queueMutex);
|
||||
ctx->numThreadsBusy--;
|
||||
pthread_mutex_unlock(&ctx->queueMutex);
|
||||
pthread_cond_signal(&ctx->queuePushCond);
|
||||
} }
|
||||
} /* for (;;) */
|
||||
/* Unreachable */
|
||||
}
|
||||
|
||||
POOL_ctx *POOL_create(size_t numThreads, size_t queueSize) {
|
||||
POOL_ctx *ctx;
|
||||
/* Check the parameters */
|
||||
if (!numThreads || !queueSize) { return NULL; }
|
||||
if (!numThreads) { return NULL; }
|
||||
/* Allocate the context and zero initialize */
|
||||
ctx = (POOL_ctx *)calloc(1, sizeof(POOL_ctx));
|
||||
if (!ctx) { return NULL; }
|
||||
@@ -92,15 +109,17 @@ POOL_ctx *POOL_create(size_t numThreads, size_t queueSize) {
|
||||
* and full queues.
|
||||
*/
|
||||
ctx->queueSize = queueSize + 1;
|
||||
ctx->queue = (POOL_job *)malloc(ctx->queueSize * sizeof(POOL_job));
|
||||
ctx->queue = (POOL_job*) malloc(ctx->queueSize * sizeof(POOL_job));
|
||||
ctx->queueHead = 0;
|
||||
ctx->queueTail = 0;
|
||||
pthread_mutex_init(&ctx->queueMutex, NULL);
|
||||
pthread_cond_init(&ctx->queuePushCond, NULL);
|
||||
pthread_cond_init(&ctx->queuePopCond, NULL);
|
||||
ctx->numThreadsBusy = 0;
|
||||
ctx->queueEmpty = 1;
|
||||
(void)pthread_mutex_init(&ctx->queueMutex, NULL);
|
||||
(void)pthread_cond_init(&ctx->queuePushCond, NULL);
|
||||
(void)pthread_cond_init(&ctx->queuePopCond, NULL);
|
||||
ctx->shutdown = 0;
|
||||
/* Allocate space for the thread handles */
|
||||
ctx->threads = (pthread_t *)malloc(numThreads * sizeof(pthread_t));
|
||||
ctx->threads = (pthread_t*)malloc(numThreads * sizeof(pthread_t));
|
||||
ctx->numThreads = 0;
|
||||
/* Check for errors */
|
||||
if (!ctx->threads || !ctx->queue) { POOL_free(ctx); return NULL; }
|
||||
@@ -153,22 +172,37 @@ size_t POOL_sizeof(POOL_ctx *ctx) {
|
||||
+ ctx->numThreads * sizeof(pthread_t);
|
||||
}
|
||||
|
||||
void POOL_add(void *ctxVoid, POOL_function function, void *opaque) {
|
||||
POOL_ctx *ctx = (POOL_ctx *)ctxVoid;
|
||||
/**
|
||||
* Returns 1 if the queue is full and 0 otherwise.
|
||||
*
|
||||
* If the queueSize is 1 (the pool was created with an intended queueSize of 0),
|
||||
* then a queue is empty if there is a thread free and no job is waiting.
|
||||
*/
|
||||
static int isQueueFull(POOL_ctx const* ctx) {
|
||||
if (ctx->queueSize > 1) {
|
||||
return ctx->queueHead == ((ctx->queueTail + 1) % ctx->queueSize);
|
||||
} else {
|
||||
return ctx->numThreadsBusy == ctx->numThreads ||
|
||||
!ctx->queueEmpty;
|
||||
}
|
||||
}
|
||||
|
||||
void POOL_add(void* ctxVoid, POOL_function function, void *opaque) {
|
||||
POOL_ctx* const ctx = (POOL_ctx*)ctxVoid;
|
||||
if (!ctx) { return; }
|
||||
|
||||
pthread_mutex_lock(&ctx->queueMutex);
|
||||
{ POOL_job const job = {function, opaque};
|
||||
|
||||
/* Wait until there is space in the queue for the new job */
|
||||
size_t newTail = (ctx->queueTail + 1) % ctx->queueSize;
|
||||
while (ctx->queueHead == newTail && !ctx->shutdown) {
|
||||
while (isQueueFull(ctx) && !ctx->shutdown) {
|
||||
pthread_cond_wait(&ctx->queuePushCond, &ctx->queueMutex);
|
||||
newTail = (ctx->queueTail + 1) % ctx->queueSize;
|
||||
}
|
||||
/* The queue is still going => there is space */
|
||||
if (!ctx->shutdown) {
|
||||
ctx->queueEmpty = 0;
|
||||
ctx->queue[ctx->queueTail] = job;
|
||||
ctx->queueTail = newTail;
|
||||
ctx->queueTail = (ctx->queueTail + 1) % ctx->queueSize;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&ctx->queueMutex);
|
||||
@@ -183,22 +217,22 @@ struct POOL_ctx_s {
|
||||
int data;
|
||||
};
|
||||
|
||||
POOL_ctx *POOL_create(size_t numThreads, size_t queueSize) {
|
||||
POOL_ctx* POOL_create(size_t numThreads, size_t queueSize) {
|
||||
(void)numThreads;
|
||||
(void)queueSize;
|
||||
return (POOL_ctx *)malloc(sizeof(POOL_ctx));
|
||||
return (POOL_ctx*)malloc(sizeof(POOL_ctx));
|
||||
}
|
||||
|
||||
void POOL_free(POOL_ctx *ctx) {
|
||||
if (ctx) free(ctx);
|
||||
void POOL_free(POOL_ctx* ctx) {
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
void POOL_add(void *ctx, POOL_function function, void *opaque) {
|
||||
void POOL_add(void* ctx, POOL_function function, void* opaque) {
|
||||
(void)ctx;
|
||||
function(opaque);
|
||||
}
|
||||
|
||||
size_t POOL_sizeof(POOL_ctx *ctx) {
|
||||
size_t POOL_sizeof(POOL_ctx* ctx) {
|
||||
if (ctx==NULL) return 0; /* supports sizeof NULL */
|
||||
return sizeof(*ctx);
|
||||
}
|
||||
|
||||
+10
-10
@@ -1,11 +1,12 @@
|
||||
/**
|
||||
* Copyright (c) 2016-present, Facebook, Inc.
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
#ifndef POOL_H
|
||||
#define POOL_H
|
||||
|
||||
@@ -19,11 +20,10 @@ extern "C" {
|
||||
typedef struct POOL_ctx_s POOL_ctx;
|
||||
|
||||
/*! POOL_create() :
|
||||
Create a thread pool with at most `numThreads` threads.
|
||||
`numThreads` must be at least 1.
|
||||
The maximum number of queued jobs before blocking is `queueSize`.
|
||||
`queueSize` must be at least 1.
|
||||
@return : The POOL_ctx pointer on success else NULL.
|
||||
* Create a thread pool with at most `numThreads` threads.
|
||||
* `numThreads` must be at least 1.
|
||||
* The maximum number of queued jobs before blocking is `queueSize`.
|
||||
* @return : POOL_ctx pointer on success, else NULL.
|
||||
*/
|
||||
POOL_ctx *POOL_create(size_t numThreads, size_t queueSize);
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* Copyright (c) 2016 Tino Reichardt
|
||||
* All rights reserved.
|
||||
@@ -42,14 +41,14 @@ extern "C" {
|
||||
|
||||
/* mutex */
|
||||
#define pthread_mutex_t CRITICAL_SECTION
|
||||
#define pthread_mutex_init(a,b) InitializeCriticalSection((a))
|
||||
#define pthread_mutex_init(a,b) (InitializeCriticalSection((a)), 0)
|
||||
#define pthread_mutex_destroy(a) DeleteCriticalSection((a))
|
||||
#define pthread_mutex_lock(a) EnterCriticalSection((a))
|
||||
#define pthread_mutex_unlock(a) LeaveCriticalSection((a))
|
||||
|
||||
/* condition variable */
|
||||
#define pthread_cond_t CONDITION_VARIABLE
|
||||
#define pthread_cond_init(a, b) InitializeConditionVariable((a))
|
||||
#define pthread_cond_init(a, b) (InitializeConditionVariable((a)), 0)
|
||||
#define pthread_cond_destroy(a) /* No delete */
|
||||
#define pthread_cond_wait(a, b) SleepConditionVariableCS((a), (b), INFINITE)
|
||||
#define pthread_cond_signal(a) WakeConditionVariable((a))
|
||||
@@ -80,14 +79,14 @@ int _pthread_join(pthread_t* thread, void** value_ptr);
|
||||
#else /* ZSTD_MULTITHREAD not defined */
|
||||
/* No multithreading support */
|
||||
|
||||
#define pthread_mutex_t int /* #define rather than typedef, as sometimes pthread support is implicit, resulting in duplicated symbols */
|
||||
#define pthread_mutex_init(a,b)
|
||||
#define pthread_mutex_t int /* #define rather than typedef, because sometimes pthread support is implicit, resulting in duplicated symbols */
|
||||
#define pthread_mutex_init(a,b) ((void)a, 0)
|
||||
#define pthread_mutex_destroy(a)
|
||||
#define pthread_mutex_lock(a)
|
||||
#define pthread_mutex_unlock(a)
|
||||
|
||||
#define pthread_cond_t int
|
||||
#define pthread_cond_init(a,b)
|
||||
#define pthread_cond_init(a,b) ((void)a, 0)
|
||||
#define pthread_cond_destroy(a)
|
||||
#define pthread_cond_wait(a,b)
|
||||
#define pthread_cond_signal(a)
|
||||
|
||||
+28
-22
@@ -113,19 +113,25 @@ static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcp
|
||||
/* *************************************
|
||||
* Compiler Specific Options
|
||||
***************************************/
|
||||
#ifdef _MSC_VER /* Visual Studio */
|
||||
# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
|
||||
# define FORCE_INLINE static __forceinline
|
||||
#if defined (__GNUC__) || defined(__cplusplus) || defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
|
||||
# define INLINE_KEYWORD inline
|
||||
#else
|
||||
# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
|
||||
# ifdef __GNUC__
|
||||
# define FORCE_INLINE static inline __attribute__((always_inline))
|
||||
# else
|
||||
# define FORCE_INLINE static inline
|
||||
# endif
|
||||
# else
|
||||
# define FORCE_INLINE static
|
||||
# endif /* __STDC_VERSION__ */
|
||||
# define INLINE_KEYWORD
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
# define FORCE_INLINE_ATTR __attribute__((always_inline))
|
||||
#elif defined(_MSC_VER)
|
||||
# define FORCE_INLINE_ATTR __forceinline
|
||||
#else
|
||||
# define FORCE_INLINE_ATTR
|
||||
#endif
|
||||
|
||||
#define FORCE_INLINE_TEMPLATE static INLINE_KEYWORD FORCE_INLINE_ATTR
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
|
||||
#endif
|
||||
|
||||
|
||||
@@ -248,7 +254,7 @@ typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess;
|
||||
*****************************/
|
||||
typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment;
|
||||
|
||||
FORCE_INLINE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_alignment align)
|
||||
FORCE_INLINE_TEMPLATE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_alignment align)
|
||||
{
|
||||
if (align==XXH_unaligned)
|
||||
return endian==XXH_littleEndian ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr));
|
||||
@@ -256,7 +262,7 @@ FORCE_INLINE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_a
|
||||
return endian==XXH_littleEndian ? *(const U32*)ptr : XXH_swap32(*(const U32*)ptr);
|
||||
}
|
||||
|
||||
FORCE_INLINE U32 XXH_readLE32(const void* ptr, XXH_endianess endian)
|
||||
FORCE_INLINE_TEMPLATE U32 XXH_readLE32(const void* ptr, XXH_endianess endian)
|
||||
{
|
||||
return XXH_readLE32_align(ptr, endian, XXH_unaligned);
|
||||
}
|
||||
@@ -266,7 +272,7 @@ static U32 XXH_readBE32(const void* ptr)
|
||||
return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr);
|
||||
}
|
||||
|
||||
FORCE_INLINE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_alignment align)
|
||||
FORCE_INLINE_TEMPLATE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_alignment align)
|
||||
{
|
||||
if (align==XXH_unaligned)
|
||||
return endian==XXH_littleEndian ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr));
|
||||
@@ -274,7 +280,7 @@ FORCE_INLINE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_a
|
||||
return endian==XXH_littleEndian ? *(const U64*)ptr : XXH_swap64(*(const U64*)ptr);
|
||||
}
|
||||
|
||||
FORCE_INLINE U64 XXH_readLE64(const void* ptr, XXH_endianess endian)
|
||||
FORCE_INLINE_TEMPLATE U64 XXH_readLE64(const void* ptr, XXH_endianess endian)
|
||||
{
|
||||
return XXH_readLE64_align(ptr, endian, XXH_unaligned);
|
||||
}
|
||||
@@ -335,7 +341,7 @@ static U32 XXH32_round(U32 seed, U32 input)
|
||||
return seed;
|
||||
}
|
||||
|
||||
FORCE_INLINE U32 XXH32_endian_align(const void* input, size_t len, U32 seed, XXH_endianess endian, XXH_alignment align)
|
||||
FORCE_INLINE_TEMPLATE U32 XXH32_endian_align(const void* input, size_t len, U32 seed, XXH_endianess endian, XXH_alignment align)
|
||||
{
|
||||
const BYTE* p = (const BYTE*)input;
|
||||
const BYTE* bEnd = p + len;
|
||||
@@ -435,7 +441,7 @@ static U64 XXH64_mergeRound(U64 acc, U64 val)
|
||||
return acc;
|
||||
}
|
||||
|
||||
FORCE_INLINE U64 XXH64_endian_align(const void* input, size_t len, U64 seed, XXH_endianess endian, XXH_alignment align)
|
||||
FORCE_INLINE_TEMPLATE U64 XXH64_endian_align(const void* input, size_t len, U64 seed, XXH_endianess endian, XXH_alignment align)
|
||||
{
|
||||
const BYTE* p = (const BYTE*)input;
|
||||
const BYTE* const bEnd = p + len;
|
||||
@@ -584,7 +590,7 @@ XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, unsigned long
|
||||
}
|
||||
|
||||
|
||||
FORCE_INLINE XXH_errorcode XXH32_update_endian (XXH32_state_t* state, const void* input, size_t len, XXH_endianess endian)
|
||||
FORCE_INLINE_TEMPLATE XXH_errorcode XXH32_update_endian (XXH32_state_t* state, const void* input, size_t len, XXH_endianess endian)
|
||||
{
|
||||
const BYTE* p = (const BYTE*)input;
|
||||
const BYTE* const bEnd = p + len;
|
||||
@@ -654,7 +660,7 @@ XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* state_in, const void*
|
||||
|
||||
|
||||
|
||||
FORCE_INLINE U32 XXH32_digest_endian (const XXH32_state_t* state, XXH_endianess endian)
|
||||
FORCE_INLINE_TEMPLATE U32 XXH32_digest_endian (const XXH32_state_t* state, XXH_endianess endian)
|
||||
{
|
||||
const BYTE * p = (const BYTE*)state->mem32;
|
||||
const BYTE* const bEnd = (const BYTE*)(state->mem32) + state->memsize;
|
||||
@@ -704,7 +710,7 @@ XXH_PUBLIC_API unsigned int XXH32_digest (const XXH32_state_t* state_in)
|
||||
|
||||
/* **** XXH64 **** */
|
||||
|
||||
FORCE_INLINE XXH_errorcode XXH64_update_endian (XXH64_state_t* state, const void* input, size_t len, XXH_endianess endian)
|
||||
FORCE_INLINE_TEMPLATE XXH_errorcode XXH64_update_endian (XXH64_state_t* state, const void* input, size_t len, XXH_endianess endian)
|
||||
{
|
||||
const BYTE* p = (const BYTE*)input;
|
||||
const BYTE* const bEnd = p + len;
|
||||
@@ -771,7 +777,7 @@ XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* state_in, const void*
|
||||
|
||||
|
||||
|
||||
FORCE_INLINE U64 XXH64_digest_endian (const XXH64_state_t* state, XXH_endianess endian)
|
||||
FORCE_INLINE_TEMPLATE U64 XXH64_digest_endian (const XXH64_state_t* state, XXH_endianess endian)
|
||||
{
|
||||
const BYTE * p = (const BYTE*)state->mem64;
|
||||
const BYTE* const bEnd = (const BYTE*)state->mem64 + state->memsize;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
+30
-32
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
#ifndef ZSTD_ERRORS_H_398273423
|
||||
@@ -37,43 +37,41 @@ extern "C" {
|
||||
/*-****************************************
|
||||
* error codes list
|
||||
* note : this API is still considered unstable
|
||||
* it should not be used with a dynamic library
|
||||
* and shall not be used with a dynamic library.
|
||||
* only static linking is allowed
|
||||
******************************************/
|
||||
typedef enum {
|
||||
ZSTD_error_no_error,
|
||||
ZSTD_error_GENERIC,
|
||||
ZSTD_error_prefix_unknown,
|
||||
ZSTD_error_version_unsupported,
|
||||
ZSTD_error_parameter_unknown,
|
||||
ZSTD_error_frameParameter_unsupported,
|
||||
ZSTD_error_frameParameter_unsupportedBy32bits,
|
||||
ZSTD_error_frameParameter_windowTooLarge,
|
||||
ZSTD_error_compressionParameter_unsupported,
|
||||
ZSTD_error_compressionParameter_outOfBound,
|
||||
ZSTD_error_init_missing,
|
||||
ZSTD_error_memory_allocation,
|
||||
ZSTD_error_stage_wrong,
|
||||
ZSTD_error_dstSize_tooSmall,
|
||||
ZSTD_error_srcSize_wrong,
|
||||
ZSTD_error_corruption_detected,
|
||||
ZSTD_error_checksum_wrong,
|
||||
ZSTD_error_tableLog_tooLarge,
|
||||
ZSTD_error_maxSymbolValue_tooLarge,
|
||||
ZSTD_error_maxSymbolValue_tooSmall,
|
||||
ZSTD_error_dictionary_corrupted,
|
||||
ZSTD_error_dictionary_wrong,
|
||||
ZSTD_error_dictionaryCreation_failed,
|
||||
ZSTD_error_frameIndex_tooLarge,
|
||||
ZSTD_error_seekableIO,
|
||||
ZSTD_error_maxCode
|
||||
ZSTD_error_no_error = 0,
|
||||
ZSTD_error_GENERIC = 1,
|
||||
ZSTD_error_prefix_unknown = 10,
|
||||
ZSTD_error_version_unsupported = 12,
|
||||
ZSTD_error_frameParameter_unsupported = 14,
|
||||
ZSTD_error_frameParameter_windowTooLarge = 16,
|
||||
ZSTD_error_corruption_detected = 20,
|
||||
ZSTD_error_checksum_wrong = 22,
|
||||
ZSTD_error_dictionary_corrupted = 30,
|
||||
ZSTD_error_dictionary_wrong = 32,
|
||||
ZSTD_error_dictionaryCreation_failed = 34,
|
||||
ZSTD_error_parameter_unsupported = 40,
|
||||
ZSTD_error_parameter_outOfBound = 42,
|
||||
ZSTD_error_tableLog_tooLarge = 44,
|
||||
ZSTD_error_maxSymbolValue_tooLarge = 46,
|
||||
ZSTD_error_maxSymbolValue_tooSmall = 48,
|
||||
ZSTD_error_stage_wrong = 60,
|
||||
ZSTD_error_init_missing = 62,
|
||||
ZSTD_error_memory_allocation = 64,
|
||||
ZSTD_error_dstSize_tooSmall = 70,
|
||||
ZSTD_error_srcSize_wrong = 72,
|
||||
ZSTD_error_frameIndex_tooLarge = 100,
|
||||
ZSTD_error_seekableIO = 102,
|
||||
ZSTD_error_maxCode = 120 /* never EVER use this value directly, it may change in future versions! Use ZSTD_isError() instead */
|
||||
} ZSTD_ErrorCode;
|
||||
|
||||
/*! ZSTD_getErrorCode() :
|
||||
convert a `size_t` function result into a `ZSTD_ErrorCode` enum type,
|
||||
which can be used to compare with enum list published above */
|
||||
ZSTDERRORLIB_API ZSTD_ErrorCode ZSTD_getErrorCode(size_t functionResult);
|
||||
ZSTDERRORLIB_API const char* ZSTD_getErrorString(ZSTD_ErrorCode code);
|
||||
ZSTDERRORLIB_API const char* ZSTD_getErrorString(ZSTD_ErrorCode code); /**< Same as ZSTD_getErrorName, but using a `ZSTD_ErrorCode` enum argument */
|
||||
|
||||
|
||||
#if defined (__cplusplus)
|
||||
|
||||
+57
-56
@@ -1,55 +1,28 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
#ifndef ZSTD_CCOMMON_H_MODULE
|
||||
#define ZSTD_CCOMMON_H_MODULE
|
||||
|
||||
/*-*******************************************************
|
||||
* Compiler specifics
|
||||
*********************************************************/
|
||||
#ifdef _MSC_VER /* Visual Studio */
|
||||
# define FORCE_INLINE static __forceinline
|
||||
# include <intrin.h> /* For Visual 2005 */
|
||||
# pragma warning(disable : 4100) /* disable: C4100: unreferenced formal parameter */
|
||||
# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
|
||||
# pragma warning(disable : 4204) /* disable: C4204: non-constant aggregate initializer */
|
||||
# pragma warning(disable : 4324) /* disable: C4324: padded structure */
|
||||
#else
|
||||
# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
|
||||
# ifdef __GNUC__
|
||||
# define FORCE_INLINE static inline __attribute__((always_inline))
|
||||
# else
|
||||
# define FORCE_INLINE static inline
|
||||
# endif
|
||||
# else
|
||||
# define FORCE_INLINE static
|
||||
# endif /* __STDC_VERSION__ */
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define FORCE_NOINLINE static __declspec(noinline)
|
||||
#else
|
||||
# ifdef __GNUC__
|
||||
# define FORCE_NOINLINE static __attribute__((__noinline__))
|
||||
# else
|
||||
# define FORCE_NOINLINE static
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
/*-*************************************
|
||||
* Dependencies
|
||||
***************************************/
|
||||
#include "compiler.h"
|
||||
#include "mem.h"
|
||||
#include "error_private.h"
|
||||
#define ZSTD_STATIC_LINKING_ONLY
|
||||
#include "zstd.h"
|
||||
#define FSE_STATIC_LINKING_ONLY
|
||||
#include "fse.h"
|
||||
#define HUF_STATIC_LINKING_ONLY
|
||||
#include "huf.h"
|
||||
#ifndef XXH_STATIC_LINKING_ONLY
|
||||
# define XXH_STATIC_LINKING_ONLY /* XXH64_state_t */
|
||||
#endif
|
||||
@@ -211,20 +184,6 @@ MEM_STATIC void ZSTD_wildcopy_e(void* dst, const void* src, void* dstEnd) /* s
|
||||
*********************************************/
|
||||
typedef struct ZSTD_stats_s ZSTD_stats_t;
|
||||
|
||||
typedef struct {
|
||||
U32 off;
|
||||
U32 len;
|
||||
} ZSTD_match_t;
|
||||
|
||||
typedef struct {
|
||||
U32 price;
|
||||
U32 off;
|
||||
U32 mlen;
|
||||
U32 litlen;
|
||||
U32 rep[ZSTD_REP_NUM];
|
||||
} ZSTD_optimal_t;
|
||||
|
||||
|
||||
typedef struct seqDef_s {
|
||||
U32 offset;
|
||||
U16 litLength;
|
||||
@@ -242,13 +201,31 @@ typedef struct {
|
||||
BYTE* ofCode;
|
||||
U32 longLengthID; /* 0 == no longLength; 1 == Lit.longLength; 2 == Match.longLength; */
|
||||
U32 longLengthPos;
|
||||
/* opt */
|
||||
ZSTD_optimal_t* priceTable;
|
||||
ZSTD_match_t* matchTable;
|
||||
U32* matchLengthFreq;
|
||||
U32* litLengthFreq;
|
||||
U32 rep[ZSTD_REP_NUM];
|
||||
U32 repToConfirm[ZSTD_REP_NUM];
|
||||
} seqStore_t;
|
||||
|
||||
typedef struct {
|
||||
U32 off;
|
||||
U32 len;
|
||||
} ZSTD_match_t;
|
||||
|
||||
typedef struct {
|
||||
U32 price;
|
||||
U32 off;
|
||||
U32 mlen;
|
||||
U32 litlen;
|
||||
U32 rep[ZSTD_REP_NUM];
|
||||
} ZSTD_optimal_t;
|
||||
|
||||
typedef struct {
|
||||
U32* litFreq;
|
||||
U32* litLengthFreq;
|
||||
U32* matchLengthFreq;
|
||||
U32* offCodeFreq;
|
||||
ZSTD_match_t* matchTable;
|
||||
ZSTD_optimal_t* priceTable;
|
||||
|
||||
U32 matchLengthSum;
|
||||
U32 matchSum;
|
||||
U32 litLengthSum;
|
||||
@@ -264,7 +241,19 @@ typedef struct {
|
||||
U32 cachedPrice;
|
||||
U32 cachedLitLength;
|
||||
const BYTE* cachedLiterals;
|
||||
} seqStore_t;
|
||||
} optState_t;
|
||||
|
||||
typedef struct {
|
||||
U32 hufCTable[HUF_CTABLE_SIZE_U32(255)];
|
||||
FSE_CTable offcodeCTable[FSE_CTABLE_SIZE_U32(OffFSELog, MaxOff)];
|
||||
FSE_CTable matchlengthCTable[FSE_CTABLE_SIZE_U32(MLFSELog, MaxML)];
|
||||
FSE_CTable litlengthCTable[FSE_CTABLE_SIZE_U32(LLFSELog, MaxLL)];
|
||||
U32 workspace[HUF_WORKSPACE_SIZE_U32];
|
||||
HUF_repeat hufCTable_repeatMode;
|
||||
FSE_repeat offcode_repeatMode;
|
||||
FSE_repeat matchlength_repeatMode;
|
||||
FSE_repeat litlength_repeatMode;
|
||||
} ZSTD_entropyCTables_t;
|
||||
|
||||
const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx);
|
||||
void ZSTD_seqToCodes(const seqStore_t* seqStorePtr);
|
||||
@@ -331,4 +320,16 @@ size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs,
|
||||
ZSTD_parameters ZSTD_getParamsFromCDict(const ZSTD_CDict* cdict);
|
||||
|
||||
|
||||
typedef struct {
|
||||
blockType_e blockType;
|
||||
U32 lastBlock;
|
||||
U32 origSize;
|
||||
} blockProperties_t;
|
||||
|
||||
/*! ZSTD_getcBlockSize() :
|
||||
* Provides the size of compressed block from block header `src` */
|
||||
size_t ZSTD_getcBlockSize(const void* src, size_t srcSize,
|
||||
blockProperties_t* bpPtr);
|
||||
|
||||
|
||||
#endif /* ZSTD_CCOMMON_H_MODULE */
|
||||
|
||||
@@ -32,27 +32,6 @@
|
||||
- Public forum : https://groups.google.com/forum/#!forum/lz4c
|
||||
****************************************************************** */
|
||||
|
||||
/* **************************************************************
|
||||
* Compiler specifics
|
||||
****************************************************************/
|
||||
#ifdef _MSC_VER /* Visual Studio */
|
||||
# define FORCE_INLINE static __forceinline
|
||||
# include <intrin.h> /* For Visual 2005 */
|
||||
# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
|
||||
# pragma warning(disable : 4214) /* disable: C4214: non-int bitfields */
|
||||
#else
|
||||
# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
|
||||
# ifdef __GNUC__
|
||||
# define FORCE_INLINE static inline __attribute__((always_inline))
|
||||
# else
|
||||
# define FORCE_INLINE static inline
|
||||
# endif
|
||||
# else
|
||||
# define FORCE_INLINE static
|
||||
# endif /* __STDC_VERSION__ */
|
||||
#endif
|
||||
|
||||
|
||||
/* **************************************************************
|
||||
* Includes
|
||||
****************************************************************/
|
||||
@@ -60,13 +39,16 @@
|
||||
#include <string.h> /* memcpy, memset */
|
||||
#include <stdio.h> /* printf (debug) */
|
||||
#include "bitstream.h"
|
||||
#include "compiler.h"
|
||||
#define FSE_STATIC_LINKING_ONLY
|
||||
#include "fse.h"
|
||||
#include "error_private.h"
|
||||
|
||||
|
||||
/* **************************************************************
|
||||
* Error Management
|
||||
****************************************************************/
|
||||
#define FSE_isError ERR_isError
|
||||
#define FSE_STATIC_ASSERT(c) { enum { FSE_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */
|
||||
|
||||
|
||||
@@ -781,7 +763,7 @@ size_t FSE_compress_usingCTable (void* dst, size_t dstSize,
|
||||
|
||||
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 f
|
||||
#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() :
|
||||
|
||||
@@ -50,13 +50,15 @@
|
||||
#include "fse.h" /* header compression */
|
||||
#define HUF_STATIC_LINKING_ONLY
|
||||
#include "huf.h"
|
||||
#include "error_private.h"
|
||||
|
||||
|
||||
/* **************************************************************
|
||||
* Error Management
|
||||
****************************************************************/
|
||||
#define HUF_isError ERR_isError
|
||||
#define HUF_STATIC_ASSERT(c) { enum { HUF_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */
|
||||
#define CHECK_V_F(e, f) size_t const e = f; if (ERR_isError(e)) return f
|
||||
#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); }
|
||||
|
||||
|
||||
@@ -436,7 +438,7 @@ static void HUF_encodeSymbol(BIT_CStream_t* bitCPtr, U32 symbol, const HUF_CElt*
|
||||
|
||||
size_t HUF_compressBound(size_t size) { return HUF_COMPRESSBOUND(size); }
|
||||
|
||||
#define HUF_FLUSHBITS(s) (fast ? BIT_flushBitsFast(s) : BIT_flushBits(s))
|
||||
#define HUF_FLUSHBITS(s) BIT_flushBits(s)
|
||||
|
||||
#define HUF_FLUSHBITS_1(stream) \
|
||||
if (sizeof((stream)->bitContainer)*8 < HUF_TABLELOG_MAX*2+7) HUF_FLUSHBITS(stream)
|
||||
@@ -451,7 +453,6 @@ size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, si
|
||||
BYTE* const oend = ostart + dstSize;
|
||||
BYTE* op = ostart;
|
||||
size_t n;
|
||||
const unsigned fast = (dstSize >= HUF_BLOCKBOUND(srcSize));
|
||||
BIT_CStream_t bitC;
|
||||
|
||||
/* init */
|
||||
|
||||
+316
-306
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
@@ -36,13 +36,6 @@ static const U32 g_searchStrength = 8; /* control skip over incompressible dat
|
||||
#define HASH_READ_SIZE 8
|
||||
typedef enum { ZSTDcs_created=0, ZSTDcs_init, ZSTDcs_ongoing, ZSTDcs_ending } ZSTD_compressionStage_e;
|
||||
|
||||
/* entropy tables always have same size */
|
||||
static size_t const hufCTable_size = HUF_CTABLE_SIZE(255);
|
||||
static size_t const litlengthCTable_size = FSE_CTABLE_SIZE(LLFSELog, MaxLL);
|
||||
static size_t const offcodeCTable_size = FSE_CTABLE_SIZE(OffFSELog, MaxOff);
|
||||
static size_t const matchlengthCTable_size = FSE_CTABLE_SIZE(MLFSELog, MaxML);
|
||||
static size_t const entropyScratchSpace_size = HUF_WORKSPACE_SIZE;
|
||||
|
||||
|
||||
/*-*************************************
|
||||
* Helper functions
|
||||
@@ -89,8 +82,6 @@ struct ZSTD_CCtx_s {
|
||||
U32 loadedDictEnd; /* index of end of dictionary */
|
||||
U32 forceWindow; /* force back-references to respect limit of 1<<wLog, even for dictionary */
|
||||
ZSTD_compressionStage_e stage;
|
||||
U32 rep[ZSTD_REP_NUM];
|
||||
U32 repToConfirm[ZSTD_REP_NUM];
|
||||
U32 dictID;
|
||||
int compressionLevel;
|
||||
ZSTD_parameters requestedParams;
|
||||
@@ -105,16 +96,11 @@ struct ZSTD_CCtx_s {
|
||||
size_t staticSize;
|
||||
|
||||
seqStore_t seqStore; /* sequences storage ptrs */
|
||||
optState_t optState;
|
||||
U32* hashTable;
|
||||
U32* hashTable3;
|
||||
U32* chainTable;
|
||||
HUF_repeat hufCTable_repeatMode;
|
||||
HUF_CElt* hufCTable;
|
||||
U32 fseCTables_ready;
|
||||
FSE_CTable* offcodeCTable;
|
||||
FSE_CTable* matchlengthCTable;
|
||||
FSE_CTable* litlengthCTable;
|
||||
unsigned* entropyScratchSpace;
|
||||
ZSTD_entropyCTables_t* entropy;
|
||||
|
||||
/* streaming */
|
||||
char* inBuff;
|
||||
@@ -174,19 +160,9 @@ ZSTD_CCtx* ZSTD_initStaticCCtx(void *workspace, size_t workspaceSize)
|
||||
cctx->workSpaceSize = workspaceSize - sizeof(ZSTD_CCtx);
|
||||
|
||||
/* entropy space (never moves) */
|
||||
/* note : this code should be shared with resetCCtx, rather than copy/pasted */
|
||||
{ void* ptr = cctx->workSpace;
|
||||
cctx->hufCTable = (HUF_CElt*)ptr;
|
||||
ptr = (char*)cctx->hufCTable + hufCTable_size;
|
||||
cctx->offcodeCTable = (FSE_CTable*) ptr;
|
||||
ptr = (char*)ptr + offcodeCTable_size;
|
||||
cctx->matchlengthCTable = (FSE_CTable*) ptr;
|
||||
ptr = (char*)ptr + matchlengthCTable_size;
|
||||
cctx->litlengthCTable = (FSE_CTable*) ptr;
|
||||
ptr = (char*)ptr + litlengthCTable_size;
|
||||
assert(((size_t)ptr & 3) == 0); /* ensure correct alignment */
|
||||
cctx->entropyScratchSpace = (unsigned*) ptr;
|
||||
}
|
||||
if (cctx->workSpaceSize < sizeof(ZSTD_entropyCTables_t)) return NULL;
|
||||
assert(((size_t)cctx->workSpace & 7) == 0); /* ensure correct alignment */
|
||||
cctx->entropy = (ZSTD_entropyCTables_t*)cctx->workSpace;
|
||||
|
||||
return cctx;
|
||||
}
|
||||
@@ -237,7 +213,7 @@ size_t ZSTD_setCCtxParameter(ZSTD_CCtx* cctx, ZSTD_CCtxParameter param, unsigned
|
||||
ZSTD_STATIC_ASSERT(ZSTD_dm_auto==0);
|
||||
ZSTD_STATIC_ASSERT(ZSTD_dm_rawContent==1);
|
||||
case ZSTD_p_forceRawDict : cctx->dictMode = (ZSTD_dictMode_e)(value>0); return 0;
|
||||
default: return ERROR(parameter_unknown);
|
||||
default: return ERROR(parameter_unsupported);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,9 +227,9 @@ static void ZSTD_cLevelToCParams(ZSTD_CCtx* cctx)
|
||||
cctx->compressionLevel = ZSTD_CLEVEL_CUSTOM;
|
||||
}
|
||||
|
||||
#define CLAMPCHECK(val,min,max) { \
|
||||
if (((val)<(min)) | ((val)>(max))) { \
|
||||
return ERROR(compressionParameter_outOfBound); \
|
||||
#define CLAMPCHECK(val,min,max) { \
|
||||
if (((val)<(min)) | ((val)>(max))) { \
|
||||
return ERROR(parameter_outOfBound); \
|
||||
} }
|
||||
|
||||
size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned value)
|
||||
@@ -349,7 +325,7 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned v
|
||||
/* restrict dictionary mode, to "rawContent" or "fullDict" only */
|
||||
ZSTD_STATIC_ASSERT((U32)ZSTD_dm_fullDict > (U32)ZSTD_dm_rawContent);
|
||||
if (value > (unsigned)ZSTD_dm_fullDict)
|
||||
return ERROR(compressionParameter_outOfBound);
|
||||
return ERROR(parameter_outOfBound);
|
||||
cctx->dictMode = (ZSTD_dictMode_e)value;
|
||||
return 0;
|
||||
|
||||
@@ -370,31 +346,31 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned v
|
||||
if (value==0) return 0;
|
||||
DEBUGLOG(5, " setting nbThreads : %u", value);
|
||||
#ifndef ZSTD_MULTITHREAD
|
||||
if (value > 1) return ERROR(compressionParameter_unsupported);
|
||||
if (value > 1) return ERROR(parameter_unsupported);
|
||||
#endif
|
||||
if ((value>1) && (cctx->nbThreads != value)) {
|
||||
if (cctx->staticSize) /* MT not compatible with static alloc */
|
||||
return ERROR(compressionParameter_unsupported);
|
||||
return ERROR(parameter_unsupported);
|
||||
ZSTDMT_freeCCtx(cctx->mtctx);
|
||||
cctx->nbThreads = 1;
|
||||
cctx->mtctx = ZSTDMT_createCCtx(value);
|
||||
cctx->mtctx = ZSTDMT_createCCtx_advanced(value, cctx->customMem);
|
||||
if (cctx->mtctx == NULL) return ERROR(memory_allocation);
|
||||
}
|
||||
cctx->nbThreads = value;
|
||||
return 0;
|
||||
|
||||
case ZSTD_p_jobSize:
|
||||
if (cctx->nbThreads <= 1) return ERROR(compressionParameter_unsupported);
|
||||
if (cctx->nbThreads <= 1) return ERROR(parameter_unsupported);
|
||||
assert(cctx->mtctx != NULL);
|
||||
return ZSTDMT_setMTCtxParameter(cctx->mtctx, ZSTDMT_p_sectionSize, value);
|
||||
|
||||
case ZSTD_p_overlapSizeLog:
|
||||
DEBUGLOG(5, " setting overlap with nbThreads == %u", cctx->nbThreads);
|
||||
if (cctx->nbThreads <= 1) return ERROR(compressionParameter_unsupported);
|
||||
if (cctx->nbThreads <= 1) return ERROR(parameter_unsupported);
|
||||
assert(cctx->mtctx != NULL);
|
||||
return ZSTDMT_setMTCtxParameter(cctx->mtctx, ZSTDMT_p_overlapSectionLog, value);
|
||||
|
||||
default: return ERROR(parameter_unknown);
|
||||
default: return ERROR(parameter_unsupported);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,7 +450,8 @@ size_t ZSTD_checkCParams(ZSTD_compressionParameters cParams)
|
||||
CLAMPCHECK(cParams.searchLog, ZSTD_SEARCHLOG_MIN, ZSTD_SEARCHLOG_MAX);
|
||||
CLAMPCHECK(cParams.searchLength, ZSTD_SEARCHLENGTH_MIN, ZSTD_SEARCHLENGTH_MAX);
|
||||
CLAMPCHECK(cParams.targetLength, ZSTD_TARGETLENGTH_MIN, ZSTD_TARGETLENGTH_MAX);
|
||||
if ((U32)(cParams.strategy) > (U32)ZSTD_btultra) return ERROR(compressionParameter_unsupported);
|
||||
if ((U32)(cParams.strategy) > (U32)ZSTD_btultra)
|
||||
return ERROR(parameter_unsupported);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -551,9 +528,7 @@ size_t ZSTD_estimateCCtxSize_advanced(ZSTD_compressionParameters cParams)
|
||||
size_t const hSize = ((size_t)1) << cParams.hashLog;
|
||||
U32 const hashLog3 = (cParams.searchLength>3) ? 0 : MIN(ZSTD_HASHLOG3_MAX, cParams.windowLog);
|
||||
size_t const h3Size = ((size_t)1) << hashLog3;
|
||||
size_t const entropySpace = hufCTable_size + litlengthCTable_size
|
||||
+ offcodeCTable_size + matchlengthCTable_size
|
||||
+ entropyScratchSpace_size;
|
||||
size_t const entropySpace = sizeof(ZSTD_entropyCTables_t);
|
||||
size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32);
|
||||
|
||||
size_t const optBudget = ((MaxML+1) + (MaxLL+1) + (MaxOff+1) + (1<<Litbits))*sizeof(U32)
|
||||
@@ -620,8 +595,8 @@ static size_t ZSTD_continueCCtx(ZSTD_CCtx* cctx, ZSTD_parameters params, U64 ple
|
||||
cctx->stage = ZSTDcs_init;
|
||||
cctx->dictID = 0;
|
||||
cctx->loadedDictEnd = 0;
|
||||
{ int i; for (i=0; i<ZSTD_REP_NUM; i++) cctx->rep[i] = repStartValue[i]; }
|
||||
cctx->seqStore.litLengthSum = 0; /* force reset of btopt stats */
|
||||
{ int i; for (i=0; i<ZSTD_REP_NUM; i++) cctx->seqStore.rep[i] = repStartValue[i]; }
|
||||
cctx->optState.litLengthSum = 0; /* force reset of btopt stats */
|
||||
XXH64_reset(&cctx->xxhState, 0);
|
||||
return 0;
|
||||
}
|
||||
@@ -641,8 +616,10 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
|
||||
if (crp == ZSTDcrp_continue) {
|
||||
if (ZSTD_equivalentParams(params.cParams, zc->appliedParams.cParams)) {
|
||||
DEBUGLOG(5, "ZSTD_equivalentParams()==1");
|
||||
zc->fseCTables_ready = 0;
|
||||
zc->hufCTable_repeatMode = HUF_repeat_none;
|
||||
zc->entropy->hufCTable_repeatMode = HUF_repeat_none;
|
||||
zc->entropy->offcode_repeatMode = FSE_repeat_none;
|
||||
zc->entropy->matchlength_repeatMode = FSE_repeat_none;
|
||||
zc->entropy->litlength_repeatMode = FSE_repeat_none;
|
||||
return ZSTD_continueCCtx(zc, params, pledgedSrcSize);
|
||||
} }
|
||||
|
||||
@@ -662,9 +639,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
|
||||
void* ptr;
|
||||
|
||||
/* Check if workSpace is large enough, alloc a new one if needed */
|
||||
{ size_t const entropySpace = hufCTable_size + litlengthCTable_size
|
||||
+ offcodeCTable_size + matchlengthCTable_size
|
||||
+ entropyScratchSpace_size;
|
||||
{ size_t const entropySpace = sizeof(ZSTD_entropyCTables_t);
|
||||
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 = ( (params.cParams.strategy == ZSTD_btopt)
|
||||
@@ -689,16 +664,9 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
|
||||
ptr = zc->workSpace;
|
||||
|
||||
/* entropy space */
|
||||
zc->hufCTable = (HUF_CElt*)ptr;
|
||||
ptr = (char*)zc->hufCTable + hufCTable_size; /* note : HUF_CElt* is incomplete type, size is estimated via macro */
|
||||
zc->offcodeCTable = (FSE_CTable*) ptr;
|
||||
ptr = (char*)ptr + offcodeCTable_size;
|
||||
zc->matchlengthCTable = (FSE_CTable*) ptr;
|
||||
ptr = (char*)ptr + matchlengthCTable_size;
|
||||
zc->litlengthCTable = (FSE_CTable*) ptr;
|
||||
ptr = (char*)ptr + litlengthCTable_size;
|
||||
assert(((size_t)ptr & 3) == 0); /* ensure correct alignment */
|
||||
zc->entropyScratchSpace = (unsigned*) ptr;
|
||||
assert(((size_t)zc->workSpace & 3) == 0); /* ensure correct alignment */
|
||||
assert(zc->workSpaceSize >= sizeof(ZSTD_entropyCTables_t));
|
||||
zc->entropy = (ZSTD_entropyCTables_t*)zc->workSpace;
|
||||
} }
|
||||
|
||||
/* init params */
|
||||
@@ -715,39 +683,35 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
|
||||
zc->stage = ZSTDcs_init;
|
||||
zc->dictID = 0;
|
||||
zc->loadedDictEnd = 0;
|
||||
zc->fseCTables_ready = 0;
|
||||
zc->hufCTable_repeatMode = HUF_repeat_none;
|
||||
zc->entropy->hufCTable_repeatMode = HUF_repeat_none;
|
||||
zc->entropy->offcode_repeatMode = FSE_repeat_none;
|
||||
zc->entropy->matchlength_repeatMode = FSE_repeat_none;
|
||||
zc->entropy->litlength_repeatMode = FSE_repeat_none;
|
||||
zc->nextToUpdate = 1;
|
||||
zc->nextSrc = NULL;
|
||||
zc->base = NULL;
|
||||
zc->dictBase = NULL;
|
||||
zc->dictLimit = 0;
|
||||
zc->lowLimit = 0;
|
||||
{ int i; for (i=0; i<ZSTD_REP_NUM; i++) zc->rep[i] = repStartValue[i]; }
|
||||
{ int i; for (i=0; i<ZSTD_REP_NUM; i++) zc->seqStore.rep[i] = repStartValue[i]; }
|
||||
zc->hashLog3 = hashLog3;
|
||||
zc->seqStore.litLengthSum = 0;
|
||||
zc->optState.litLengthSum = 0;
|
||||
|
||||
/* ensure entropy tables are close together at the beginning */
|
||||
assert((void*)zc->hufCTable == zc->workSpace);
|
||||
assert((char*)zc->offcodeCTable == (char*)zc->hufCTable + hufCTable_size);
|
||||
assert((char*)zc->matchlengthCTable == (char*)zc->offcodeCTable + offcodeCTable_size);
|
||||
assert((char*)zc->litlengthCTable == (char*)zc->matchlengthCTable + matchlengthCTable_size);
|
||||
assert((char*)zc->entropyScratchSpace == (char*)zc->litlengthCTable + litlengthCTable_size);
|
||||
ptr = (char*)zc->entropyScratchSpace + entropyScratchSpace_size;
|
||||
ptr = zc->entropy + 1;
|
||||
|
||||
/* opt parser space */
|
||||
if ((params.cParams.strategy == ZSTD_btopt) || (params.cParams.strategy == ZSTD_btultra)) {
|
||||
DEBUGLOG(5, "reserving optimal parser space");
|
||||
assert(((size_t)ptr & 3) == 0); /* ensure ptr is properly aligned */
|
||||
zc->seqStore.litFreq = (U32*)ptr;
|
||||
zc->seqStore.litLengthFreq = zc->seqStore.litFreq + (1<<Litbits);
|
||||
zc->seqStore.matchLengthFreq = zc->seqStore.litLengthFreq + (MaxLL+1);
|
||||
zc->seqStore.offCodeFreq = zc->seqStore.matchLengthFreq + (MaxML+1);
|
||||
ptr = zc->seqStore.offCodeFreq + (MaxOff+1);
|
||||
zc->seqStore.matchTable = (ZSTD_match_t*)ptr;
|
||||
ptr = zc->seqStore.matchTable + ZSTD_OPT_NUM+1;
|
||||
zc->seqStore.priceTable = (ZSTD_optimal_t*)ptr;
|
||||
ptr = zc->seqStore.priceTable + ZSTD_OPT_NUM+1;
|
||||
zc->optState.litFreq = (U32*)ptr;
|
||||
zc->optState.litLengthFreq = zc->optState.litFreq + (1<<Litbits);
|
||||
zc->optState.matchLengthFreq = zc->optState.litLengthFreq + (MaxLL+1);
|
||||
zc->optState.offCodeFreq = zc->optState.matchLengthFreq + (MaxML+1);
|
||||
ptr = zc->optState.offCodeFreq + (MaxOff+1);
|
||||
zc->optState.matchTable = (ZSTD_match_t*)ptr;
|
||||
ptr = zc->optState.matchTable + ZSTD_OPT_NUM+1;
|
||||
zc->optState.priceTable = (ZSTD_optimal_t*)ptr;
|
||||
ptr = zc->optState.priceTable + ZSTD_OPT_NUM+1;
|
||||
}
|
||||
|
||||
/* table Space */
|
||||
@@ -783,7 +747,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
|
||||
* do not use with extDict variant ! */
|
||||
void ZSTD_invalidateRepCodes(ZSTD_CCtx* cctx) {
|
||||
int i;
|
||||
for (i=0; i<ZSTD_REP_NUM; i++) cctx->rep[i] = 0;
|
||||
for (i=0; i<ZSTD_REP_NUM; i++) cctx->seqStore.rep[i] = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -830,16 +794,7 @@ static size_t ZSTD_copyCCtx_internal(ZSTD_CCtx* dstCCtx,
|
||||
dstCCtx->dictID = srcCCtx->dictID;
|
||||
|
||||
/* copy entropy tables */
|
||||
dstCCtx->fseCTables_ready = srcCCtx->fseCTables_ready;
|
||||
if (srcCCtx->fseCTables_ready) {
|
||||
memcpy(dstCCtx->litlengthCTable, srcCCtx->litlengthCTable, litlengthCTable_size);
|
||||
memcpy(dstCCtx->matchlengthCTable, srcCCtx->matchlengthCTable, matchlengthCTable_size);
|
||||
memcpy(dstCCtx->offcodeCTable, srcCCtx->offcodeCTable, offcodeCTable_size);
|
||||
}
|
||||
dstCCtx->hufCTable_repeatMode = srcCCtx->hufCTable_repeatMode;
|
||||
if (srcCCtx->hufCTable_repeatMode) {
|
||||
memcpy(dstCCtx->hufCTable, srcCCtx->hufCTable, hufCTable_size);
|
||||
}
|
||||
memcpy(dstCCtx->entropy, srcCCtx->entropy, sizeof(ZSTD_entropyCTables_t));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -956,7 +911,8 @@ static size_t ZSTD_compressRleLiteralsBlock (void* dst, size_t dstCapacity, cons
|
||||
|
||||
static size_t ZSTD_minGain(size_t srcSize) { return (srcSize >> 6) + 2; }
|
||||
|
||||
static size_t ZSTD_compressLiterals (ZSTD_CCtx* zc,
|
||||
static size_t ZSTD_compressLiterals (ZSTD_entropyCTables_t * entropy,
|
||||
ZSTD_strategy strategy,
|
||||
void* dst, size_t dstCapacity,
|
||||
const void* src, size_t srcSize)
|
||||
{
|
||||
@@ -970,28 +926,28 @@ static size_t ZSTD_compressLiterals (ZSTD_CCtx* zc,
|
||||
|
||||
/* small ? don't even attempt compression (speed opt) */
|
||||
# define LITERAL_NOENTROPY 63
|
||||
{ size_t const minLitSize = zc->hufCTable_repeatMode == HUF_repeat_valid ? 6 : LITERAL_NOENTROPY;
|
||||
{ size_t const minLitSize = entropy->hufCTable_repeatMode == HUF_repeat_valid ? 6 : LITERAL_NOENTROPY;
|
||||
if (srcSize <= minLitSize) return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize);
|
||||
}
|
||||
|
||||
if (dstCapacity < lhSize+1) return ERROR(dstSize_tooSmall); /* not enough space for compression */
|
||||
{ HUF_repeat repeat = zc->hufCTable_repeatMode;
|
||||
int const preferRepeat = zc->appliedParams.cParams.strategy < ZSTD_lazy ? srcSize <= 1024 : 0;
|
||||
{ HUF_repeat repeat = entropy->hufCTable_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,
|
||||
zc->entropyScratchSpace, entropyScratchSpace_size, zc->hufCTable, &repeat, preferRepeat)
|
||||
entropy->workspace, sizeof(entropy->workspace), (HUF_CElt*)entropy->hufCTable, &repeat, preferRepeat)
|
||||
: HUF_compress4X_repeat(ostart+lhSize, dstCapacity-lhSize, src, srcSize, 255, 11,
|
||||
zc->entropyScratchSpace, entropyScratchSpace_size, zc->hufCTable, &repeat, preferRepeat);
|
||||
entropy->workspace, sizeof(entropy->workspace), (HUF_CElt*)entropy->hufCTable, &repeat, preferRepeat);
|
||||
if (repeat != HUF_repeat_none) { hType = set_repeat; } /* reused the existing table */
|
||||
else { zc->hufCTable_repeatMode = HUF_repeat_check; } /* now have a table to reuse */
|
||||
else { entropy->hufCTable_repeatMode = HUF_repeat_check; } /* now have a table to reuse */
|
||||
}
|
||||
|
||||
if ((cLitSize==0) | (cLitSize >= srcSize - minGain)) {
|
||||
zc->hufCTable_repeatMode = HUF_repeat_none;
|
||||
if ((cLitSize==0) | (cLitSize >= srcSize - minGain) | ERR_isError(cLitSize)) {
|
||||
entropy->hufCTable_repeatMode = HUF_repeat_none;
|
||||
return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize);
|
||||
}
|
||||
if (cLitSize==1) {
|
||||
zc->hufCTable_repeatMode = HUF_repeat_none;
|
||||
entropy->hufCTable_repeatMode = HUF_repeat_none;
|
||||
return ZSTD_compressRleLiteralsBlock(dst, dstCapacity, src, srcSize);
|
||||
}
|
||||
|
||||
@@ -1062,17 +1018,154 @@ void ZSTD_seqToCodes(const seqStore_t* seqStorePtr)
|
||||
mlCodeTable[seqStorePtr->longLengthPos] = MaxML;
|
||||
}
|
||||
|
||||
MEM_STATIC size_t ZSTD_compressSequences (ZSTD_CCtx* zc,
|
||||
void* dst, size_t dstCapacity,
|
||||
size_t srcSize)
|
||||
MEM_STATIC symbolEncodingType_e ZSTD_selectEncodingType(FSE_repeat* repeatMode,
|
||||
size_t const mostFrequent, size_t nbSeq, U32 defaultNormLog)
|
||||
{
|
||||
const int longOffsets = zc->appliedParams.cParams.windowLog > STREAM_ACCUMULATOR_MIN;
|
||||
const seqStore_t* seqStorePtr = &(zc->seqStore);
|
||||
#define MIN_SEQ_FOR_DYNAMIC_FSE 64
|
||||
#define MAX_SEQ_FOR_STATIC_FSE 1000
|
||||
|
||||
if ((mostFrequent == nbSeq) && (nbSeq > 2)) {
|
||||
*repeatMode = FSE_repeat_check;
|
||||
return set_rle;
|
||||
}
|
||||
if ((*repeatMode == FSE_repeat_valid) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) {
|
||||
return set_repeat;
|
||||
}
|
||||
if ((nbSeq < MIN_SEQ_FOR_DYNAMIC_FSE) || (mostFrequent < (nbSeq >> (defaultNormLog-1)))) {
|
||||
*repeatMode = FSE_repeat_valid;
|
||||
return set_basic;
|
||||
}
|
||||
*repeatMode = FSE_repeat_check;
|
||||
return set_compressed;
|
||||
}
|
||||
|
||||
MEM_STATIC size_t ZSTD_buildCTable(void* dst, size_t dstCapacity,
|
||||
FSE_CTable* CTable, U32 FSELog, symbolEncodingType_e type,
|
||||
U32* count, U32 max,
|
||||
BYTE const* codeTable, size_t nbSeq,
|
||||
S16 const* defaultNorm, U32 defaultNormLog, U32 defaultMax,
|
||||
void* workspace, size_t workspaceSize)
|
||||
{
|
||||
BYTE* op = (BYTE*)dst;
|
||||
BYTE const* const oend = op + dstCapacity;
|
||||
|
||||
switch (type) {
|
||||
case set_rle:
|
||||
*op = codeTable[0];
|
||||
CHECK_F(FSE_buildCTable_rle(CTable, (BYTE)max));
|
||||
return 1;
|
||||
case set_repeat:
|
||||
return 0;
|
||||
case set_basic:
|
||||
CHECK_F(FSE_buildCTable_wksp(CTable, defaultNorm, defaultMax, defaultNormLog, workspace, workspaceSize));
|
||||
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--;
|
||||
}
|
||||
CHECK_F(FSE_normalizeCount(norm, tableLog, count, nbSeq_1, max));
|
||||
{ size_t const NCountSize = FSE_writeNCount(op, oend - op, norm, max, tableLog); /* overflow protected */
|
||||
if (FSE_isError(NCountSize)) return NCountSize;
|
||||
CHECK_F(FSE_buildCTable_wksp(CTable, norm, max, tableLog, workspace, workspaceSize));
|
||||
return NCountSize;
|
||||
}
|
||||
}
|
||||
default: return assert(0), ERROR(GENERIC);
|
||||
}
|
||||
}
|
||||
|
||||
MEM_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)
|
||||
{
|
||||
BIT_CStream_t blockStream;
|
||||
FSE_CState_t stateMatchLength;
|
||||
FSE_CState_t stateOffsetBits;
|
||||
FSE_CState_t stateLitLength;
|
||||
|
||||
CHECK_E(BIT_initCStream(&blockStream, dst, dstCapacity), dstSize_tooSmall); /* not enough space remaining */
|
||||
|
||||
/* first symbols */
|
||||
FSE_initCState2(&stateMatchLength, CTable_MatchLength, mlCodeTable[nbSeq-1]);
|
||||
FSE_initCState2(&stateOffsetBits, CTable_OffsetBits, ofCodeTable[nbSeq-1]);
|
||||
FSE_initCState2(&stateLitLength, CTable_LitLength, llCodeTable[nbSeq-1]);
|
||||
BIT_addBits(&blockStream, sequences[nbSeq-1].litLength, LL_bits[llCodeTable[nbSeq-1]]);
|
||||
if (MEM_32bits()) BIT_flushBits(&blockStream);
|
||||
BIT_addBits(&blockStream, sequences[nbSeq-1].matchLength, ML_bits[mlCodeTable[nbSeq-1]]);
|
||||
if (MEM_32bits()) BIT_flushBits(&blockStream);
|
||||
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; /* 32b*/ /* 64b*/
|
||||
U32 const mlBits = ML_bits[mlCode];
|
||||
/* (7)*/ /* (7)*/
|
||||
FSE_encodeSymbol(&blockStream, &stateOffsetBits, ofCode); /* 15 */ /* 15 */
|
||||
FSE_encodeSymbol(&blockStream, &stateMatchLength, mlCode); /* 24 */ /* 24 */
|
||||
if (MEM_32bits()) BIT_flushBits(&blockStream); /* (7)*/
|
||||
FSE_encodeSymbol(&blockStream, &stateLitLength, llCode); /* 16 */ /* 33 */
|
||||
if (MEM_32bits() || (ofBits+mlBits+llBits >= 64-7-(LLFSELog+MLFSELog+OffFSELog)))
|
||||
BIT_flushBits(&blockStream); /* (7)*/
|
||||
BIT_addBits(&blockStream, sequences[n].litLength, llBits);
|
||||
if (MEM_32bits() && ((llBits+mlBits)>24)) BIT_flushBits(&blockStream);
|
||||
BIT_addBits(&blockStream, sequences[n].matchLength, mlBits);
|
||||
if (MEM_32bits()) BIT_flushBits(&blockStream); /* (7)*/
|
||||
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)*/
|
||||
} }
|
||||
|
||||
FSE_flushCState(&blockStream, &stateMatchLength);
|
||||
FSE_flushCState(&blockStream, &stateOffsetBits);
|
||||
FSE_flushCState(&blockStream, &stateLitLength);
|
||||
|
||||
{ size_t const streamSize = BIT_closeCStream(&blockStream);
|
||||
if (streamSize==0) return ERROR(dstSize_tooSmall); /* not enough space */
|
||||
return streamSize;
|
||||
}
|
||||
}
|
||||
|
||||
MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr,
|
||||
ZSTD_entropyCTables_t* entropy,
|
||||
ZSTD_compressionParameters const* cParams,
|
||||
void* dst, size_t dstCapacity)
|
||||
{
|
||||
const int longOffsets = cParams->windowLog > STREAM_ACCUMULATOR_MIN;
|
||||
U32 count[MaxSeq+1];
|
||||
S16 norm[MaxSeq+1];
|
||||
FSE_CTable* CTable_LitLength = zc->litlengthCTable;
|
||||
FSE_CTable* CTable_OffsetBits = zc->offcodeCTable;
|
||||
FSE_CTable* CTable_MatchLength = zc->matchlengthCTable;
|
||||
FSE_CTable* CTable_LitLength = entropy->litlengthCTable;
|
||||
FSE_CTable* CTable_OffsetBits = entropy->offcodeCTable;
|
||||
FSE_CTable* CTable_MatchLength = entropy->matchlengthCTable;
|
||||
U32 LLtype, Offtype, MLtype; /* compressed, raw or rle */
|
||||
const seqDef* const sequences = seqStorePtr->sequencesStart;
|
||||
const BYTE* const ofCodeTable = seqStorePtr->ofCode;
|
||||
@@ -1083,13 +1176,16 @@ MEM_STATIC size_t ZSTD_compressSequences (ZSTD_CCtx* zc,
|
||||
BYTE* op = ostart;
|
||||
size_t const nbSeq = seqStorePtr->sequences - seqStorePtr->sequencesStart;
|
||||
BYTE* seqHead;
|
||||
BYTE scratchBuffer[1<<MAX(MLFSELog,LLFSELog)];
|
||||
|
||||
ZSTD_STATIC_ASSERT(sizeof(entropy->workspace) >= (1<<MAX(MLFSELog,LLFSELog)));
|
||||
|
||||
/* Compress literals */
|
||||
{ const BYTE* const literals = seqStorePtr->litStart;
|
||||
size_t const litSize = seqStorePtr->lit - literals;
|
||||
size_t const cSize = ZSTD_compressLiterals(zc, op, dstCapacity, literals, litSize);
|
||||
if (ZSTD_isError(cSize)) return cSize;
|
||||
size_t const cSize = ZSTD_compressLiterals(
|
||||
entropy, cParams->strategy, op, dstCapacity, literals, litSize);
|
||||
if (ZSTD_isError(cSize))
|
||||
return cSize;
|
||||
op += cSize;
|
||||
}
|
||||
|
||||
@@ -1098,179 +1194,91 @@ MEM_STATIC size_t ZSTD_compressSequences (ZSTD_CCtx* zc,
|
||||
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) goto _check_compressibility;
|
||||
if (nbSeq==0) return op - ostart;
|
||||
|
||||
/* seqHead : flags for FSE encoding type */
|
||||
seqHead = op++;
|
||||
|
||||
#define MIN_SEQ_FOR_DYNAMIC_FSE 64
|
||||
#define MAX_SEQ_FOR_STATIC_FSE 1000
|
||||
|
||||
/* convert length/distances into codes */
|
||||
ZSTD_seqToCodes(seqStorePtr);
|
||||
|
||||
/* CTable for Literal Lengths */
|
||||
{ U32 max = MaxLL;
|
||||
size_t const mostFrequent = FSE_countFast_wksp(count, &max, llCodeTable, nbSeq, zc->entropyScratchSpace);
|
||||
if ((mostFrequent == nbSeq) && (nbSeq > 2)) {
|
||||
*op++ = llCodeTable[0];
|
||||
FSE_buildCTable_rle(CTable_LitLength, (BYTE)max);
|
||||
LLtype = set_rle;
|
||||
} else if ((zc->fseCTables_ready) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) {
|
||||
LLtype = set_repeat;
|
||||
} else if ((nbSeq < MIN_SEQ_FOR_DYNAMIC_FSE) || (mostFrequent < (nbSeq >> (LL_defaultNormLog-1)))) {
|
||||
FSE_buildCTable_wksp(CTable_LitLength, LL_defaultNorm, MaxLL, LL_defaultNormLog, scratchBuffer, sizeof(scratchBuffer));
|
||||
LLtype = set_basic;
|
||||
} else {
|
||||
size_t nbSeq_1 = nbSeq;
|
||||
const U32 tableLog = FSE_optimalTableLog(LLFSELog, nbSeq, max);
|
||||
if (count[llCodeTable[nbSeq-1]]>1) { count[llCodeTable[nbSeq-1]]--; nbSeq_1--; }
|
||||
FSE_normalizeCount(norm, tableLog, count, nbSeq_1, max);
|
||||
{ size_t const NCountSize = FSE_writeNCount(op, oend-op, norm, max, tableLog); /* overflow protected */
|
||||
if (FSE_isError(NCountSize)) return NCountSize;
|
||||
op += NCountSize; }
|
||||
FSE_buildCTable_wksp(CTable_LitLength, norm, max, tableLog, scratchBuffer, sizeof(scratchBuffer));
|
||||
LLtype = set_compressed;
|
||||
size_t const mostFrequent = FSE_countFast_wksp(count, &max, llCodeTable, nbSeq, entropy->workspace);
|
||||
LLtype = ZSTD_selectEncodingType(&entropy->litlength_repeatMode, mostFrequent, nbSeq, LL_defaultNormLog);
|
||||
{ size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_LitLength, LLFSELog, (symbolEncodingType_e)LLtype,
|
||||
count, max, llCodeTable, nbSeq, LL_defaultNorm, LL_defaultNormLog, MaxLL,
|
||||
entropy->workspace, sizeof(entropy->workspace));
|
||||
if (ZSTD_isError(countSize)) return countSize;
|
||||
op += countSize;
|
||||
} }
|
||||
|
||||
/* CTable for Offsets */
|
||||
{ U32 max = MaxOff;
|
||||
size_t const mostFrequent = FSE_countFast_wksp(count, &max, ofCodeTable, nbSeq, zc->entropyScratchSpace);
|
||||
if ((mostFrequent == nbSeq) && (nbSeq > 2)) {
|
||||
*op++ = ofCodeTable[0];
|
||||
FSE_buildCTable_rle(CTable_OffsetBits, (BYTE)max);
|
||||
Offtype = set_rle;
|
||||
} else if ((zc->fseCTables_ready) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) {
|
||||
Offtype = set_repeat;
|
||||
} else if ((nbSeq < MIN_SEQ_FOR_DYNAMIC_FSE) || (mostFrequent < (nbSeq >> (OF_defaultNormLog-1)))) {
|
||||
FSE_buildCTable_wksp(CTable_OffsetBits, OF_defaultNorm, MaxOff, OF_defaultNormLog, scratchBuffer, sizeof(scratchBuffer));
|
||||
Offtype = set_basic;
|
||||
} else {
|
||||
size_t nbSeq_1 = nbSeq;
|
||||
const U32 tableLog = FSE_optimalTableLog(OffFSELog, nbSeq, max);
|
||||
if (count[ofCodeTable[nbSeq-1]]>1) { count[ofCodeTable[nbSeq-1]]--; nbSeq_1--; }
|
||||
FSE_normalizeCount(norm, tableLog, count, nbSeq_1, max);
|
||||
{ size_t const NCountSize = FSE_writeNCount(op, oend-op, norm, max, tableLog); /* overflow protected */
|
||||
if (FSE_isError(NCountSize)) return NCountSize;
|
||||
op += NCountSize; }
|
||||
FSE_buildCTable_wksp(CTable_OffsetBits, norm, max, tableLog, scratchBuffer, sizeof(scratchBuffer));
|
||||
Offtype = set_compressed;
|
||||
size_t const mostFrequent = FSE_countFast_wksp(count, &max, ofCodeTable, nbSeq, entropy->workspace);
|
||||
Offtype = ZSTD_selectEncodingType(&entropy->offcode_repeatMode, mostFrequent, nbSeq, OF_defaultNormLog);
|
||||
{ size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_OffsetBits, OffFSELog, (symbolEncodingType_e)Offtype,
|
||||
count, max, ofCodeTable, nbSeq, OF_defaultNorm, OF_defaultNormLog, MaxOff,
|
||||
entropy->workspace, sizeof(entropy->workspace));
|
||||
if (ZSTD_isError(countSize)) return countSize;
|
||||
op += countSize;
|
||||
} }
|
||||
|
||||
/* CTable for MatchLengths */
|
||||
{ U32 max = MaxML;
|
||||
size_t const mostFrequent = FSE_countFast_wksp(count, &max, mlCodeTable, nbSeq, zc->entropyScratchSpace);
|
||||
if ((mostFrequent == nbSeq) && (nbSeq > 2)) {
|
||||
*op++ = *mlCodeTable;
|
||||
FSE_buildCTable_rle(CTable_MatchLength, (BYTE)max);
|
||||
MLtype = set_rle;
|
||||
} else if ((zc->fseCTables_ready) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) {
|
||||
MLtype = set_repeat;
|
||||
} else if ((nbSeq < MIN_SEQ_FOR_DYNAMIC_FSE) || (mostFrequent < (nbSeq >> (ML_defaultNormLog-1)))) {
|
||||
FSE_buildCTable_wksp(CTable_MatchLength, ML_defaultNorm, MaxML, ML_defaultNormLog, scratchBuffer, sizeof(scratchBuffer));
|
||||
MLtype = set_basic;
|
||||
} else {
|
||||
size_t nbSeq_1 = nbSeq;
|
||||
const U32 tableLog = FSE_optimalTableLog(MLFSELog, nbSeq, max);
|
||||
if (count[mlCodeTable[nbSeq-1]]>1) { count[mlCodeTable[nbSeq-1]]--; nbSeq_1--; }
|
||||
FSE_normalizeCount(norm, tableLog, count, nbSeq_1, max);
|
||||
{ size_t const NCountSize = FSE_writeNCount(op, oend-op, norm, max, tableLog); /* overflow protected */
|
||||
if (FSE_isError(NCountSize)) return NCountSize;
|
||||
op += NCountSize; }
|
||||
FSE_buildCTable_wksp(CTable_MatchLength, norm, max, tableLog, scratchBuffer, sizeof(scratchBuffer));
|
||||
MLtype = set_compressed;
|
||||
size_t const mostFrequent = FSE_countFast_wksp(count, &max, mlCodeTable, nbSeq, entropy->workspace);
|
||||
MLtype = ZSTD_selectEncodingType(&entropy->matchlength_repeatMode, mostFrequent, nbSeq, ML_defaultNormLog);
|
||||
{ size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_MatchLength, MLFSELog, (symbolEncodingType_e)MLtype,
|
||||
count, max, mlCodeTable, nbSeq, ML_defaultNorm, ML_defaultNormLog, MaxML,
|
||||
entropy->workspace, sizeof(entropy->workspace));
|
||||
if (ZSTD_isError(countSize)) return countSize;
|
||||
op += countSize;
|
||||
} }
|
||||
|
||||
*seqHead = (BYTE)((LLtype<<6) + (Offtype<<4) + (MLtype<<2));
|
||||
zc->fseCTables_ready = 0;
|
||||
|
||||
/* Encoding Sequences */
|
||||
{ BIT_CStream_t blockStream;
|
||||
FSE_CState_t stateMatchLength;
|
||||
FSE_CState_t stateOffsetBits;
|
||||
FSE_CState_t stateLitLength;
|
||||
|
||||
CHECK_E(BIT_initCStream(&blockStream, op, oend-op), dstSize_tooSmall); /* not enough space remaining */
|
||||
|
||||
/* first symbols */
|
||||
FSE_initCState2(&stateMatchLength, CTable_MatchLength, mlCodeTable[nbSeq-1]);
|
||||
FSE_initCState2(&stateOffsetBits, CTable_OffsetBits, ofCodeTable[nbSeq-1]);
|
||||
FSE_initCState2(&stateLitLength, CTable_LitLength, llCodeTable[nbSeq-1]);
|
||||
BIT_addBits(&blockStream, sequences[nbSeq-1].litLength, LL_bits[llCodeTable[nbSeq-1]]);
|
||||
if (MEM_32bits()) BIT_flushBits(&blockStream);
|
||||
BIT_addBits(&blockStream, sequences[nbSeq-1].matchLength, ML_bits[mlCodeTable[nbSeq-1]]);
|
||||
if (MEM_32bits()) BIT_flushBits(&blockStream);
|
||||
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; /* 32b*/ /* 64b*/
|
||||
U32 const mlBits = ML_bits[mlCode];
|
||||
/* (7)*/ /* (7)*/
|
||||
FSE_encodeSymbol(&blockStream, &stateOffsetBits, ofCode); /* 15 */ /* 15 */
|
||||
FSE_encodeSymbol(&blockStream, &stateMatchLength, mlCode); /* 24 */ /* 24 */
|
||||
if (MEM_32bits()) BIT_flushBits(&blockStream); /* (7)*/
|
||||
FSE_encodeSymbol(&blockStream, &stateLitLength, llCode); /* 16 */ /* 33 */
|
||||
if (MEM_32bits() || (ofBits+mlBits+llBits >= 64-7-(LLFSELog+MLFSELog+OffFSELog)))
|
||||
BIT_flushBits(&blockStream); /* (7)*/
|
||||
BIT_addBits(&blockStream, sequences[n].litLength, llBits);
|
||||
if (MEM_32bits() && ((llBits+mlBits)>24)) BIT_flushBits(&blockStream);
|
||||
BIT_addBits(&blockStream, sequences[n].matchLength, mlBits);
|
||||
if (MEM_32bits()) BIT_flushBits(&blockStream); /* (7)*/
|
||||
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)*/
|
||||
} }
|
||||
|
||||
FSE_flushCState(&blockStream, &stateMatchLength);
|
||||
FSE_flushCState(&blockStream, &stateOffsetBits);
|
||||
FSE_flushCState(&blockStream, &stateLitLength);
|
||||
|
||||
{ size_t const streamSize = BIT_closeCStream(&blockStream);
|
||||
if (streamSize==0) return ERROR(dstSize_tooSmall); /* not enough space */
|
||||
op += streamSize;
|
||||
} }
|
||||
|
||||
/* check compressibility */
|
||||
_check_compressibility:
|
||||
{ size_t const minGain = ZSTD_minGain(srcSize);
|
||||
size_t const maxCSize = srcSize - minGain;
|
||||
if ((size_t)(op-ostart) >= maxCSize) {
|
||||
zc->hufCTable_repeatMode = HUF_repeat_none;
|
||||
return 0;
|
||||
} }
|
||||
|
||||
/* confirm repcodes */
|
||||
{ int i; for (i=0; i<ZSTD_REP_NUM; i++) zc->rep[i] = zc->repToConfirm[i]; }
|
||||
{ size_t const streamSize = ZSTD_encodeSequences(op, oend - op,
|
||||
CTable_MatchLength, mlCodeTable,
|
||||
CTable_OffsetBits, ofCodeTable,
|
||||
CTable_LitLength, llCodeTable,
|
||||
sequences, nbSeq, longOffsets);
|
||||
if (ZSTD_isError(streamSize)) return streamSize;
|
||||
op += streamSize;
|
||||
}
|
||||
|
||||
return op - ostart;
|
||||
}
|
||||
|
||||
MEM_STATIC size_t ZSTD_compressSequences(seqStore_t* seqStorePtr,
|
||||
ZSTD_entropyCTables_t* entropy,
|
||||
ZSTD_compressionParameters const* cParams,
|
||||
void* dst, size_t dstCapacity,
|
||||
size_t srcSize)
|
||||
{
|
||||
size_t const cSize = ZSTD_compressSequences_internal(seqStorePtr, entropy, cParams,
|
||||
dst, dstCapacity);
|
||||
size_t const minGain = ZSTD_minGain(srcSize);
|
||||
size_t const maxCSize = srcSize - minGain;
|
||||
/* If the srcSize <= dstCapacity, then there is enough space to write a
|
||||
* raw uncompressed block. Since we ran out of space, the block must not
|
||||
* be compressible, so fall back to a raw uncompressed block.
|
||||
*/
|
||||
int const uncompressibleError = cSize == ERROR(dstSize_tooSmall) && srcSize <= dstCapacity;
|
||||
|
||||
if (ZSTD_isError(cSize) && !uncompressibleError)
|
||||
return cSize;
|
||||
/* Check compressibility */
|
||||
if (cSize >= maxCSize || uncompressibleError) {
|
||||
entropy->hufCTable_repeatMode = HUF_repeat_none;
|
||||
entropy->offcode_repeatMode = FSE_repeat_none;
|
||||
entropy->matchlength_repeatMode = FSE_repeat_none;
|
||||
entropy->litlength_repeatMode = FSE_repeat_none;
|
||||
return 0;
|
||||
}
|
||||
assert(!ZSTD_isError(cSize));
|
||||
|
||||
/* confirm repcodes */
|
||||
{ int i; for (i=0; i<ZSTD_REP_NUM; i++) seqStorePtr->rep[i] = seqStorePtr->repToConfirm[i]; }
|
||||
return cSize;
|
||||
}
|
||||
|
||||
|
||||
/*! ZSTD_storeSeq() :
|
||||
Store a sequence (literal length, literals, offset code and match length code) into seqStore_t.
|
||||
@@ -1475,7 +1483,7 @@ static void ZSTD_fillHashTable (ZSTD_CCtx* zc, const void* end, const U32 mls)
|
||||
}
|
||||
|
||||
|
||||
FORCE_INLINE
|
||||
FORCE_INLINE_TEMPLATE
|
||||
void ZSTD_compressBlock_fast_generic(ZSTD_CCtx* cctx,
|
||||
const void* src, size_t srcSize,
|
||||
const U32 mls)
|
||||
@@ -1491,7 +1499,7 @@ void ZSTD_compressBlock_fast_generic(ZSTD_CCtx* cctx,
|
||||
const BYTE* const lowest = base + lowestIndex;
|
||||
const BYTE* const iend = istart + srcSize;
|
||||
const BYTE* const ilimit = iend - HASH_READ_SIZE;
|
||||
U32 offset_1=cctx->rep[0], offset_2=cctx->rep[1];
|
||||
U32 offset_1=seqStorePtr->rep[0], offset_2=seqStorePtr->rep[1];
|
||||
U32 offsetSaved = 0;
|
||||
|
||||
/* init */
|
||||
@@ -1552,8 +1560,8 @@ void ZSTD_compressBlock_fast_generic(ZSTD_CCtx* cctx,
|
||||
} } }
|
||||
|
||||
/* save reps for next block */
|
||||
cctx->repToConfirm[0] = offset_1 ? offset_1 : offsetSaved;
|
||||
cctx->repToConfirm[1] = offset_2 ? offset_2 : offsetSaved;
|
||||
seqStorePtr->repToConfirm[0] = offset_1 ? offset_1 : offsetSaved;
|
||||
seqStorePtr->repToConfirm[1] = offset_2 ? offset_2 : offsetSaved;
|
||||
|
||||
/* Last Literals */
|
||||
{ size_t const lastLLSize = iend - anchor;
|
||||
@@ -1601,7 +1609,7 @@ static void ZSTD_compressBlock_fast_extDict_generic(ZSTD_CCtx* ctx,
|
||||
const BYTE* const dictEnd = dictBase + dictLimit;
|
||||
const BYTE* const iend = istart + srcSize;
|
||||
const BYTE* const ilimit = iend - 8;
|
||||
U32 offset_1=ctx->rep[0], offset_2=ctx->rep[1];
|
||||
U32 offset_1=seqStorePtr->rep[0], offset_2=seqStorePtr->rep[1];
|
||||
|
||||
/* Search Loop */
|
||||
while (ip < ilimit) { /* < instead of <=, because (ip+1) */
|
||||
@@ -1667,7 +1675,7 @@ static void ZSTD_compressBlock_fast_extDict_generic(ZSTD_CCtx* ctx,
|
||||
} } }
|
||||
|
||||
/* save reps for next block */
|
||||
ctx->repToConfirm[0] = offset_1; ctx->repToConfirm[1] = offset_2;
|
||||
seqStorePtr->repToConfirm[0] = offset_1; seqStorePtr->repToConfirm[1] = offset_2;
|
||||
|
||||
/* Last Literals */
|
||||
{ size_t const lastLLSize = iend - anchor;
|
||||
@@ -1718,7 +1726,7 @@ static void ZSTD_fillDoubleHashTable (ZSTD_CCtx* cctx, const void* end, const U3
|
||||
}
|
||||
|
||||
|
||||
FORCE_INLINE
|
||||
FORCE_INLINE_TEMPLATE
|
||||
void ZSTD_compressBlock_doubleFast_generic(ZSTD_CCtx* cctx,
|
||||
const void* src, size_t srcSize,
|
||||
const U32 mls)
|
||||
@@ -1736,7 +1744,7 @@ void ZSTD_compressBlock_doubleFast_generic(ZSTD_CCtx* cctx,
|
||||
const BYTE* const lowest = base + lowestIndex;
|
||||
const BYTE* const iend = istart + srcSize;
|
||||
const BYTE* const ilimit = iend - HASH_READ_SIZE;
|
||||
U32 offset_1=cctx->rep[0], offset_2=cctx->rep[1];
|
||||
U32 offset_1=seqStorePtr->rep[0], offset_2=seqStorePtr->rep[1];
|
||||
U32 offsetSaved = 0;
|
||||
|
||||
/* init */
|
||||
@@ -1823,8 +1831,8 @@ void ZSTD_compressBlock_doubleFast_generic(ZSTD_CCtx* cctx,
|
||||
} } }
|
||||
|
||||
/* save reps for next block */
|
||||
cctx->repToConfirm[0] = offset_1 ? offset_1 : offsetSaved;
|
||||
cctx->repToConfirm[1] = offset_2 ? offset_2 : offsetSaved;
|
||||
seqStorePtr->repToConfirm[0] = offset_1 ? offset_1 : offsetSaved;
|
||||
seqStorePtr->repToConfirm[1] = offset_2 ? offset_2 : offsetSaved;
|
||||
|
||||
/* Last Literals */
|
||||
{ size_t const lastLLSize = iend - anchor;
|
||||
@@ -1873,7 +1881,7 @@ static void ZSTD_compressBlock_doubleFast_extDict_generic(ZSTD_CCtx* ctx,
|
||||
const BYTE* const dictEnd = dictBase + dictLimit;
|
||||
const BYTE* const iend = istart + srcSize;
|
||||
const BYTE* const ilimit = iend - 8;
|
||||
U32 offset_1=ctx->rep[0], offset_2=ctx->rep[1];
|
||||
U32 offset_1=seqStorePtr->rep[0], offset_2=seqStorePtr->rep[1];
|
||||
|
||||
/* Search Loop */
|
||||
while (ip < ilimit) { /* < instead of <=, because (ip+1) */
|
||||
@@ -1973,7 +1981,7 @@ static void ZSTD_compressBlock_doubleFast_extDict_generic(ZSTD_CCtx* ctx,
|
||||
} } }
|
||||
|
||||
/* save reps for next block */
|
||||
ctx->repToConfirm[0] = offset_1; ctx->repToConfirm[1] = offset_2;
|
||||
seqStorePtr->repToConfirm[0] = offset_1; seqStorePtr->repToConfirm[1] = offset_2;
|
||||
|
||||
/* Last Literals */
|
||||
{ size_t const lastLLSize = iend - anchor;
|
||||
@@ -2276,7 +2284,7 @@ static size_t ZSTD_BtFindBestMatch_selectMLS_extDict (
|
||||
|
||||
/* Update chains up to ip (excluded)
|
||||
Assumption : always within prefix (i.e. not within extDict) */
|
||||
FORCE_INLINE
|
||||
FORCE_INLINE_TEMPLATE
|
||||
U32 ZSTD_insertAndFindFirstIndex (ZSTD_CCtx* zc, const BYTE* ip, U32 mls)
|
||||
{
|
||||
U32* const hashTable = zc->hashTable;
|
||||
@@ -2300,7 +2308,7 @@ U32 ZSTD_insertAndFindFirstIndex (ZSTD_CCtx* zc, const BYTE* ip, U32 mls)
|
||||
|
||||
|
||||
/* inlining is important to hardwire a hot branch (template emulation) */
|
||||
FORCE_INLINE
|
||||
FORCE_INLINE_TEMPLATE
|
||||
size_t ZSTD_HcFindBestMatch_generic (
|
||||
ZSTD_CCtx* zc, /* Index table will be updated */
|
||||
const BYTE* const ip, const BYTE* const iLimit,
|
||||
@@ -2352,7 +2360,7 @@ size_t ZSTD_HcFindBestMatch_generic (
|
||||
}
|
||||
|
||||
|
||||
FORCE_INLINE size_t ZSTD_HcFindBestMatch_selectMLS (
|
||||
FORCE_INLINE_TEMPLATE size_t ZSTD_HcFindBestMatch_selectMLS (
|
||||
ZSTD_CCtx* zc,
|
||||
const BYTE* ip, const BYTE* const iLimit,
|
||||
size_t* offsetPtr,
|
||||
@@ -2369,7 +2377,7 @@ FORCE_INLINE size_t ZSTD_HcFindBestMatch_selectMLS (
|
||||
}
|
||||
|
||||
|
||||
FORCE_INLINE size_t ZSTD_HcFindBestMatch_extDict_selectMLS (
|
||||
FORCE_INLINE_TEMPLATE size_t ZSTD_HcFindBestMatch_extDict_selectMLS (
|
||||
ZSTD_CCtx* zc,
|
||||
const BYTE* ip, const BYTE* const iLimit,
|
||||
size_t* offsetPtr,
|
||||
@@ -2389,7 +2397,7 @@ FORCE_INLINE size_t ZSTD_HcFindBestMatch_extDict_selectMLS (
|
||||
/* *******************************
|
||||
* Common parser - lazy strategy
|
||||
*********************************/
|
||||
FORCE_INLINE
|
||||
FORCE_INLINE_TEMPLATE
|
||||
void ZSTD_compressBlock_lazy_generic(ZSTD_CCtx* ctx,
|
||||
const void* src, size_t srcSize,
|
||||
const U32 searchMethod, const U32 depth)
|
||||
@@ -2409,7 +2417,7 @@ void ZSTD_compressBlock_lazy_generic(ZSTD_CCtx* ctx,
|
||||
size_t* offsetPtr,
|
||||
U32 maxNbAttempts, U32 matchLengthSearch);
|
||||
searchMax_f const searchMax = searchMethod ? ZSTD_BtFindBestMatch_selectMLS : ZSTD_HcFindBestMatch_selectMLS;
|
||||
U32 offset_1 = ctx->rep[0], offset_2 = ctx->rep[1], savedOffset=0;
|
||||
U32 offset_1 = seqStorePtr->rep[0], offset_2 = seqStorePtr->rep[1], savedOffset=0;
|
||||
|
||||
/* init */
|
||||
ip += (ip==base);
|
||||
@@ -2519,8 +2527,8 @@ _storeSequence:
|
||||
} }
|
||||
|
||||
/* Save reps for next block */
|
||||
ctx->repToConfirm[0] = offset_1 ? offset_1 : savedOffset;
|
||||
ctx->repToConfirm[1] = offset_2 ? offset_2 : savedOffset;
|
||||
seqStorePtr->repToConfirm[0] = offset_1 ? offset_1 : savedOffset;
|
||||
seqStorePtr->repToConfirm[1] = offset_2 ? offset_2 : savedOffset;
|
||||
|
||||
/* Last Literals */
|
||||
{ size_t const lastLLSize = iend - anchor;
|
||||
@@ -2551,7 +2559,7 @@ static void ZSTD_compressBlock_greedy(ZSTD_CCtx* ctx, const void* src, size_t sr
|
||||
}
|
||||
|
||||
|
||||
FORCE_INLINE
|
||||
FORCE_INLINE_TEMPLATE
|
||||
void ZSTD_compressBlock_lazy_extDict_generic(ZSTD_CCtx* ctx,
|
||||
const void* src, size_t srcSize,
|
||||
const U32 searchMethod, const U32 depth)
|
||||
@@ -2578,7 +2586,7 @@ void ZSTD_compressBlock_lazy_extDict_generic(ZSTD_CCtx* ctx,
|
||||
U32 maxNbAttempts, U32 matchLengthSearch);
|
||||
searchMax_f searchMax = searchMethod ? ZSTD_BtFindBestMatch_selectMLS_extDict : ZSTD_HcFindBestMatch_extDict_selectMLS;
|
||||
|
||||
U32 offset_1 = ctx->rep[0], offset_2 = ctx->rep[1];
|
||||
U32 offset_1 = seqStorePtr->rep[0], offset_2 = seqStorePtr->rep[1];
|
||||
|
||||
/* init */
|
||||
ctx->nextToUpdate3 = ctx->nextToUpdate;
|
||||
@@ -2714,7 +2722,7 @@ _storeSequence:
|
||||
} }
|
||||
|
||||
/* Save reps for next block */
|
||||
ctx->repToConfirm[0] = offset_1; ctx->repToConfirm[1] = offset_2;
|
||||
seqStorePtr->repToConfirm[0] = offset_1; seqStorePtr->repToConfirm[1] = offset_2;
|
||||
|
||||
/* Last Literals */
|
||||
{ size_t const lastLLSize = iend - anchor;
|
||||
@@ -2823,7 +2831,7 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, void* dst, size_t dstCa
|
||||
if (current > zc->nextToUpdate + 384)
|
||||
zc->nextToUpdate = current - MIN(192, (U32)(current - zc->nextToUpdate - 384)); /* limited update after finding a very long match */
|
||||
blockCompressor(zc, src, srcSize);
|
||||
return ZSTD_compressSequences(zc, dst, dstCapacity, srcSize);
|
||||
return ZSTD_compressSequences(&zc->seqStore, zc->entropy, &zc->appliedParams.cParams, dst, dstCapacity, srcSize);
|
||||
}
|
||||
|
||||
|
||||
@@ -3000,7 +3008,6 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx,
|
||||
return fhSize;
|
||||
}
|
||||
|
||||
|
||||
size_t ZSTD_compressContinue (ZSTD_CCtx* cctx,
|
||||
void* dst, size_t dstCapacity,
|
||||
const void* src, size_t srcSize)
|
||||
@@ -3106,13 +3113,14 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_CCtx* cctx, const void* dict, size_t
|
||||
const BYTE* const dictEnd = dictPtr + dictSize;
|
||||
short offcodeNCount[MaxOff+1];
|
||||
unsigned offcodeMaxValue = MaxOff;
|
||||
BYTE scratchBuffer[1<<MAX(MLFSELog,LLFSELog)];
|
||||
|
||||
ZSTD_STATIC_ASSERT(sizeof(cctx->entropy->workspace) >= (1<<MAX(MLFSELog,LLFSELog)));
|
||||
|
||||
dictPtr += 4; /* skip magic number */
|
||||
cctx->dictID = cctx->appliedParams.fParams.noDictIDFlag ? 0 : MEM_readLE32(dictPtr);
|
||||
dictPtr += 4;
|
||||
|
||||
{ size_t const hufHeaderSize = HUF_readCTable(cctx->hufCTable, 255, dictPtr, dictEnd-dictPtr);
|
||||
{ size_t const hufHeaderSize = HUF_readCTable((HUF_CElt*)cctx->entropy->hufCTable, 255, dictPtr, dictEnd-dictPtr);
|
||||
if (HUF_isError(hufHeaderSize)) return ERROR(dictionary_corrupted);
|
||||
dictPtr += hufHeaderSize;
|
||||
}
|
||||
@@ -3122,7 +3130,7 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_CCtx* cctx, const void* dict, size_t
|
||||
if (FSE_isError(offcodeHeaderSize)) return ERROR(dictionary_corrupted);
|
||||
if (offcodeLog > OffFSELog) return ERROR(dictionary_corrupted);
|
||||
/* Defer checking offcodeMaxValue because we need to know the size of the dictionary content */
|
||||
CHECK_E( FSE_buildCTable_wksp(cctx->offcodeCTable, offcodeNCount, offcodeMaxValue, offcodeLog, scratchBuffer, sizeof(scratchBuffer)),
|
||||
CHECK_E( FSE_buildCTable_wksp(cctx->entropy->offcodeCTable, offcodeNCount, offcodeMaxValue, offcodeLog, cctx->entropy->workspace, sizeof(cctx->entropy->workspace)),
|
||||
dictionary_corrupted);
|
||||
dictPtr += offcodeHeaderSize;
|
||||
}
|
||||
@@ -3134,7 +3142,7 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_CCtx* cctx, const void* dict, size_t
|
||||
if (matchlengthLog > MLFSELog) return ERROR(dictionary_corrupted);
|
||||
/* Every match length code must have non-zero probability */
|
||||
CHECK_F( ZSTD_checkDictNCount(matchlengthNCount, matchlengthMaxValue, MaxML));
|
||||
CHECK_E( FSE_buildCTable_wksp(cctx->matchlengthCTable, matchlengthNCount, matchlengthMaxValue, matchlengthLog, scratchBuffer, sizeof(scratchBuffer)),
|
||||
CHECK_E( FSE_buildCTable_wksp(cctx->entropy->matchlengthCTable, matchlengthNCount, matchlengthMaxValue, matchlengthLog, cctx->entropy->workspace, sizeof(cctx->entropy->workspace)),
|
||||
dictionary_corrupted);
|
||||
dictPtr += matchlengthHeaderSize;
|
||||
}
|
||||
@@ -3146,15 +3154,15 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_CCtx* cctx, const void* dict, size_t
|
||||
if (litlengthLog > LLFSELog) return ERROR(dictionary_corrupted);
|
||||
/* Every literal length code must have non-zero probability */
|
||||
CHECK_F( ZSTD_checkDictNCount(litlengthNCount, litlengthMaxValue, MaxLL));
|
||||
CHECK_E( FSE_buildCTable_wksp(cctx->litlengthCTable, litlengthNCount, litlengthMaxValue, litlengthLog, scratchBuffer, sizeof(scratchBuffer)),
|
||||
CHECK_E( FSE_buildCTable_wksp(cctx->entropy->litlengthCTable, litlengthNCount, litlengthMaxValue, litlengthLog, cctx->entropy->workspace, sizeof(cctx->entropy->workspace)),
|
||||
dictionary_corrupted);
|
||||
dictPtr += litlengthHeaderSize;
|
||||
}
|
||||
|
||||
if (dictPtr+12 > dictEnd) return ERROR(dictionary_corrupted);
|
||||
cctx->rep[0] = MEM_readLE32(dictPtr+0);
|
||||
cctx->rep[1] = MEM_readLE32(dictPtr+4);
|
||||
cctx->rep[2] = MEM_readLE32(dictPtr+8);
|
||||
cctx->seqStore.rep[0] = MEM_readLE32(dictPtr+0);
|
||||
cctx->seqStore.rep[1] = MEM_readLE32(dictPtr+4);
|
||||
cctx->seqStore.rep[2] = MEM_readLE32(dictPtr+8);
|
||||
dictPtr += 12;
|
||||
|
||||
{ size_t const dictContentSize = (size_t)(dictEnd - dictPtr);
|
||||
@@ -3168,12 +3176,14 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_CCtx* cctx, const void* dict, size_t
|
||||
/* All repCodes must be <= dictContentSize and != 0*/
|
||||
{ U32 u;
|
||||
for (u=0; u<3; u++) {
|
||||
if (cctx->rep[u] == 0) return ERROR(dictionary_corrupted);
|
||||
if (cctx->rep[u] > dictContentSize) return ERROR(dictionary_corrupted);
|
||||
if (cctx->seqStore.rep[u] == 0) return ERROR(dictionary_corrupted);
|
||||
if (cctx->seqStore.rep[u] > dictContentSize) return ERROR(dictionary_corrupted);
|
||||
} }
|
||||
|
||||
cctx->fseCTables_ready = 1;
|
||||
cctx->hufCTable_repeatMode = HUF_repeat_valid;
|
||||
cctx->entropy->hufCTable_repeatMode = HUF_repeat_valid;
|
||||
cctx->entropy->offcode_repeatMode = FSE_repeat_valid;
|
||||
cctx->entropy->matchlength_repeatMode = FSE_repeat_valid;
|
||||
cctx->entropy->litlength_repeatMode = FSE_repeat_valid;
|
||||
return ZSTD_loadDictionaryContent(cctx, dictPtr, dictContentSize);
|
||||
}
|
||||
}
|
||||
|
||||
+118
-116
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) 2016-present, Przemyslaw Skibinski, Yann Collet, Facebook, Inc.
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
@@ -22,173 +22,173 @@
|
||||
/*-*************************************
|
||||
* Price functions for optimal parser
|
||||
***************************************/
|
||||
FORCE_INLINE void ZSTD_setLog2Prices(seqStore_t* ssPtr)
|
||||
static void ZSTD_setLog2Prices(optState_t* optPtr)
|
||||
{
|
||||
ssPtr->log2matchLengthSum = ZSTD_highbit32(ssPtr->matchLengthSum+1);
|
||||
ssPtr->log2litLengthSum = ZSTD_highbit32(ssPtr->litLengthSum+1);
|
||||
ssPtr->log2litSum = ZSTD_highbit32(ssPtr->litSum+1);
|
||||
ssPtr->log2offCodeSum = ZSTD_highbit32(ssPtr->offCodeSum+1);
|
||||
ssPtr->factor = 1 + ((ssPtr->litSum>>5) / ssPtr->litLengthSum) + ((ssPtr->litSum<<1) / (ssPtr->litSum + ssPtr->matchSum));
|
||||
optPtr->log2matchLengthSum = ZSTD_highbit32(optPtr->matchLengthSum+1);
|
||||
optPtr->log2litLengthSum = ZSTD_highbit32(optPtr->litLengthSum+1);
|
||||
optPtr->log2litSum = ZSTD_highbit32(optPtr->litSum+1);
|
||||
optPtr->log2offCodeSum = ZSTD_highbit32(optPtr->offCodeSum+1);
|
||||
optPtr->factor = 1 + ((optPtr->litSum>>5) / optPtr->litLengthSum) + ((optPtr->litSum<<1) / (optPtr->litSum + optPtr->matchSum));
|
||||
}
|
||||
|
||||
|
||||
MEM_STATIC void ZSTD_rescaleFreqs(seqStore_t* ssPtr, const BYTE* src, size_t srcSize)
|
||||
static void ZSTD_rescaleFreqs(optState_t* optPtr, const BYTE* src, size_t srcSize)
|
||||
{
|
||||
unsigned u;
|
||||
|
||||
ssPtr->cachedLiterals = NULL;
|
||||
ssPtr->cachedPrice = ssPtr->cachedLitLength = 0;
|
||||
ssPtr->staticPrices = 0;
|
||||
optPtr->cachedLiterals = NULL;
|
||||
optPtr->cachedPrice = optPtr->cachedLitLength = 0;
|
||||
optPtr->staticPrices = 0;
|
||||
|
||||
if (ssPtr->litLengthSum == 0) {
|
||||
if (srcSize <= 1024) ssPtr->staticPrices = 1;
|
||||
if (optPtr->litLengthSum == 0) {
|
||||
if (srcSize <= 1024) optPtr->staticPrices = 1;
|
||||
|
||||
assert(ssPtr->litFreq!=NULL);
|
||||
assert(optPtr->litFreq!=NULL);
|
||||
for (u=0; u<=MaxLit; u++)
|
||||
ssPtr->litFreq[u] = 0;
|
||||
optPtr->litFreq[u] = 0;
|
||||
for (u=0; u<srcSize; u++)
|
||||
ssPtr->litFreq[src[u]]++;
|
||||
optPtr->litFreq[src[u]]++;
|
||||
|
||||
ssPtr->litSum = 0;
|
||||
ssPtr->litLengthSum = MaxLL+1;
|
||||
ssPtr->matchLengthSum = MaxML+1;
|
||||
ssPtr->offCodeSum = (MaxOff+1);
|
||||
ssPtr->matchSum = (ZSTD_LITFREQ_ADD<<Litbits);
|
||||
optPtr->litSum = 0;
|
||||
optPtr->litLengthSum = MaxLL+1;
|
||||
optPtr->matchLengthSum = MaxML+1;
|
||||
optPtr->offCodeSum = (MaxOff+1);
|
||||
optPtr->matchSum = (ZSTD_LITFREQ_ADD<<Litbits);
|
||||
|
||||
for (u=0; u<=MaxLit; u++) {
|
||||
ssPtr->litFreq[u] = 1 + (ssPtr->litFreq[u]>>ZSTD_FREQ_DIV);
|
||||
ssPtr->litSum += ssPtr->litFreq[u];
|
||||
optPtr->litFreq[u] = 1 + (optPtr->litFreq[u]>>ZSTD_FREQ_DIV);
|
||||
optPtr->litSum += optPtr->litFreq[u];
|
||||
}
|
||||
for (u=0; u<=MaxLL; u++)
|
||||
ssPtr->litLengthFreq[u] = 1;
|
||||
optPtr->litLengthFreq[u] = 1;
|
||||
for (u=0; u<=MaxML; u++)
|
||||
ssPtr->matchLengthFreq[u] = 1;
|
||||
optPtr->matchLengthFreq[u] = 1;
|
||||
for (u=0; u<=MaxOff; u++)
|
||||
ssPtr->offCodeFreq[u] = 1;
|
||||
optPtr->offCodeFreq[u] = 1;
|
||||
} else {
|
||||
ssPtr->matchLengthSum = 0;
|
||||
ssPtr->litLengthSum = 0;
|
||||
ssPtr->offCodeSum = 0;
|
||||
ssPtr->matchSum = 0;
|
||||
ssPtr->litSum = 0;
|
||||
optPtr->matchLengthSum = 0;
|
||||
optPtr->litLengthSum = 0;
|
||||
optPtr->offCodeSum = 0;
|
||||
optPtr->matchSum = 0;
|
||||
optPtr->litSum = 0;
|
||||
|
||||
for (u=0; u<=MaxLit; u++) {
|
||||
ssPtr->litFreq[u] = 1 + (ssPtr->litFreq[u]>>(ZSTD_FREQ_DIV+1));
|
||||
ssPtr->litSum += ssPtr->litFreq[u];
|
||||
optPtr->litFreq[u] = 1 + (optPtr->litFreq[u]>>(ZSTD_FREQ_DIV+1));
|
||||
optPtr->litSum += optPtr->litFreq[u];
|
||||
}
|
||||
for (u=0; u<=MaxLL; u++) {
|
||||
ssPtr->litLengthFreq[u] = 1 + (ssPtr->litLengthFreq[u]>>(ZSTD_FREQ_DIV+1));
|
||||
ssPtr->litLengthSum += ssPtr->litLengthFreq[u];
|
||||
optPtr->litLengthFreq[u] = 1 + (optPtr->litLengthFreq[u]>>(ZSTD_FREQ_DIV+1));
|
||||
optPtr->litLengthSum += optPtr->litLengthFreq[u];
|
||||
}
|
||||
for (u=0; u<=MaxML; u++) {
|
||||
ssPtr->matchLengthFreq[u] = 1 + (ssPtr->matchLengthFreq[u]>>ZSTD_FREQ_DIV);
|
||||
ssPtr->matchLengthSum += ssPtr->matchLengthFreq[u];
|
||||
ssPtr->matchSum += ssPtr->matchLengthFreq[u] * (u + 3);
|
||||
optPtr->matchLengthFreq[u] = 1 + (optPtr->matchLengthFreq[u]>>ZSTD_FREQ_DIV);
|
||||
optPtr->matchLengthSum += optPtr->matchLengthFreq[u];
|
||||
optPtr->matchSum += optPtr->matchLengthFreq[u] * (u + 3);
|
||||
}
|
||||
ssPtr->matchSum *= ZSTD_LITFREQ_ADD;
|
||||
optPtr->matchSum *= ZSTD_LITFREQ_ADD;
|
||||
for (u=0; u<=MaxOff; u++) {
|
||||
ssPtr->offCodeFreq[u] = 1 + (ssPtr->offCodeFreq[u]>>ZSTD_FREQ_DIV);
|
||||
ssPtr->offCodeSum += ssPtr->offCodeFreq[u];
|
||||
optPtr->offCodeFreq[u] = 1 + (optPtr->offCodeFreq[u]>>ZSTD_FREQ_DIV);
|
||||
optPtr->offCodeSum += optPtr->offCodeFreq[u];
|
||||
}
|
||||
}
|
||||
|
||||
ZSTD_setLog2Prices(ssPtr);
|
||||
ZSTD_setLog2Prices(optPtr);
|
||||
}
|
||||
|
||||
|
||||
FORCE_INLINE U32 ZSTD_getLiteralPrice(seqStore_t* ssPtr, U32 litLength, const BYTE* literals)
|
||||
static U32 ZSTD_getLiteralPrice(optState_t* optPtr, U32 litLength, const BYTE* literals)
|
||||
{
|
||||
U32 price, u;
|
||||
|
||||
if (ssPtr->staticPrices)
|
||||
if (optPtr->staticPrices)
|
||||
return ZSTD_highbit32((U32)litLength+1) + (litLength*6);
|
||||
|
||||
if (litLength == 0)
|
||||
return ssPtr->log2litLengthSum - ZSTD_highbit32(ssPtr->litLengthFreq[0]+1);
|
||||
return optPtr->log2litLengthSum - ZSTD_highbit32(optPtr->litLengthFreq[0]+1);
|
||||
|
||||
/* literals */
|
||||
if (ssPtr->cachedLiterals == literals) {
|
||||
U32 const additional = litLength - ssPtr->cachedLitLength;
|
||||
const BYTE* literals2 = ssPtr->cachedLiterals + ssPtr->cachedLitLength;
|
||||
price = ssPtr->cachedPrice + additional * ssPtr->log2litSum;
|
||||
if (optPtr->cachedLiterals == literals) {
|
||||
U32 const additional = litLength - optPtr->cachedLitLength;
|
||||
const BYTE* literals2 = optPtr->cachedLiterals + optPtr->cachedLitLength;
|
||||
price = optPtr->cachedPrice + additional * optPtr->log2litSum;
|
||||
for (u=0; u < additional; u++)
|
||||
price -= ZSTD_highbit32(ssPtr->litFreq[literals2[u]]+1);
|
||||
ssPtr->cachedPrice = price;
|
||||
ssPtr->cachedLitLength = litLength;
|
||||
price -= ZSTD_highbit32(optPtr->litFreq[literals2[u]]+1);
|
||||
optPtr->cachedPrice = price;
|
||||
optPtr->cachedLitLength = litLength;
|
||||
} else {
|
||||
price = litLength * ssPtr->log2litSum;
|
||||
price = litLength * optPtr->log2litSum;
|
||||
for (u=0; u < litLength; u++)
|
||||
price -= ZSTD_highbit32(ssPtr->litFreq[literals[u]]+1);
|
||||
price -= ZSTD_highbit32(optPtr->litFreq[literals[u]]+1);
|
||||
|
||||
if (litLength >= 12) {
|
||||
ssPtr->cachedLiterals = literals;
|
||||
ssPtr->cachedPrice = price;
|
||||
ssPtr->cachedLitLength = litLength;
|
||||
optPtr->cachedLiterals = literals;
|
||||
optPtr->cachedPrice = price;
|
||||
optPtr->cachedLitLength = litLength;
|
||||
}
|
||||
}
|
||||
|
||||
/* literal Length */
|
||||
{ const BYTE LL_deltaCode = 19;
|
||||
const BYTE llCode = (litLength>63) ? (BYTE)ZSTD_highbit32(litLength) + LL_deltaCode : LL_Code[litLength];
|
||||
price += LL_bits[llCode] + ssPtr->log2litLengthSum - ZSTD_highbit32(ssPtr->litLengthFreq[llCode]+1);
|
||||
price += LL_bits[llCode] + optPtr->log2litLengthSum - ZSTD_highbit32(optPtr->litLengthFreq[llCode]+1);
|
||||
}
|
||||
|
||||
return price;
|
||||
}
|
||||
|
||||
|
||||
FORCE_INLINE U32 ZSTD_getPrice(seqStore_t* seqStorePtr, U32 litLength, const BYTE* literals, U32 offset, U32 matchLength, const int ultra)
|
||||
FORCE_INLINE_TEMPLATE U32 ZSTD_getPrice(optState_t* optPtr, U32 litLength, const BYTE* literals, U32 offset, U32 matchLength, const int ultra)
|
||||
{
|
||||
/* offset */
|
||||
U32 price;
|
||||
BYTE const offCode = (BYTE)ZSTD_highbit32(offset+1);
|
||||
|
||||
if (seqStorePtr->staticPrices)
|
||||
return ZSTD_getLiteralPrice(seqStorePtr, litLength, literals) + ZSTD_highbit32((U32)matchLength+1) + 16 + offCode;
|
||||
if (optPtr->staticPrices)
|
||||
return ZSTD_getLiteralPrice(optPtr, litLength, literals) + ZSTD_highbit32((U32)matchLength+1) + 16 + offCode;
|
||||
|
||||
price = offCode + seqStorePtr->log2offCodeSum - ZSTD_highbit32(seqStorePtr->offCodeFreq[offCode]+1);
|
||||
price = offCode + optPtr->log2offCodeSum - ZSTD_highbit32(optPtr->offCodeFreq[offCode]+1);
|
||||
if (!ultra && offCode >= 20) price += (offCode-19)*2;
|
||||
|
||||
/* match Length */
|
||||
{ const BYTE ML_deltaCode = 36;
|
||||
const BYTE mlCode = (matchLength>127) ? (BYTE)ZSTD_highbit32(matchLength) + ML_deltaCode : ML_Code[matchLength];
|
||||
price += ML_bits[mlCode] + seqStorePtr->log2matchLengthSum - ZSTD_highbit32(seqStorePtr->matchLengthFreq[mlCode]+1);
|
||||
price += ML_bits[mlCode] + optPtr->log2matchLengthSum - ZSTD_highbit32(optPtr->matchLengthFreq[mlCode]+1);
|
||||
}
|
||||
|
||||
return price + ZSTD_getLiteralPrice(seqStorePtr, litLength, literals) + seqStorePtr->factor;
|
||||
return price + ZSTD_getLiteralPrice(optPtr, litLength, literals) + optPtr->factor;
|
||||
}
|
||||
|
||||
|
||||
MEM_STATIC void ZSTD_updatePrice(seqStore_t* seqStorePtr, U32 litLength, const BYTE* literals, U32 offset, U32 matchLength)
|
||||
static void ZSTD_updatePrice(optState_t* optPtr, U32 litLength, const BYTE* literals, U32 offset, U32 matchLength)
|
||||
{
|
||||
U32 u;
|
||||
|
||||
/* literals */
|
||||
seqStorePtr->litSum += litLength*ZSTD_LITFREQ_ADD;
|
||||
optPtr->litSum += litLength*ZSTD_LITFREQ_ADD;
|
||||
for (u=0; u < litLength; u++)
|
||||
seqStorePtr->litFreq[literals[u]] += ZSTD_LITFREQ_ADD;
|
||||
optPtr->litFreq[literals[u]] += ZSTD_LITFREQ_ADD;
|
||||
|
||||
/* literal Length */
|
||||
{ const BYTE LL_deltaCode = 19;
|
||||
const BYTE llCode = (litLength>63) ? (BYTE)ZSTD_highbit32(litLength) + LL_deltaCode : LL_Code[litLength];
|
||||
seqStorePtr->litLengthFreq[llCode]++;
|
||||
seqStorePtr->litLengthSum++;
|
||||
optPtr->litLengthFreq[llCode]++;
|
||||
optPtr->litLengthSum++;
|
||||
}
|
||||
|
||||
/* match offset */
|
||||
{ BYTE const offCode = (BYTE)ZSTD_highbit32(offset+1);
|
||||
seqStorePtr->offCodeSum++;
|
||||
seqStorePtr->offCodeFreq[offCode]++;
|
||||
optPtr->offCodeSum++;
|
||||
optPtr->offCodeFreq[offCode]++;
|
||||
}
|
||||
|
||||
/* match Length */
|
||||
{ const BYTE ML_deltaCode = 36;
|
||||
const BYTE mlCode = (matchLength>127) ? (BYTE)ZSTD_highbit32(matchLength) + ML_deltaCode : ML_Code[matchLength];
|
||||
seqStorePtr->matchLengthFreq[mlCode]++;
|
||||
seqStorePtr->matchLengthSum++;
|
||||
optPtr->matchLengthFreq[mlCode]++;
|
||||
optPtr->matchLengthSum++;
|
||||
}
|
||||
|
||||
ZSTD_setLog2Prices(seqStorePtr);
|
||||
ZSTD_setLog2Prices(optPtr);
|
||||
}
|
||||
|
||||
|
||||
@@ -203,7 +203,7 @@ MEM_STATIC void ZSTD_updatePrice(seqStore_t* seqStorePtr, U32 litLength, const B
|
||||
|
||||
|
||||
/* function safe only for comparisons */
|
||||
MEM_STATIC U32 ZSTD_readMINMATCH(const void* memPtr, U32 length)
|
||||
static U32 ZSTD_readMINMATCH(const void* memPtr, U32 length)
|
||||
{
|
||||
switch (length)
|
||||
{
|
||||
@@ -219,7 +219,7 @@ MEM_STATIC U32 ZSTD_readMINMATCH(const void* memPtr, U32 length)
|
||||
|
||||
/* Update hashTable3 up to ip (excluded)
|
||||
Assumption : always within prefix (i.e. not within extDict) */
|
||||
FORCE_INLINE
|
||||
static
|
||||
U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_CCtx* zc, const BYTE* ip)
|
||||
{
|
||||
U32* const hashTable3 = zc->hashTable3;
|
||||
@@ -412,11 +412,12 @@ static U32 ZSTD_BtGetAllMatches_selectMLS_extDict (
|
||||
/*-*******************************
|
||||
* Optimal parser
|
||||
*********************************/
|
||||
FORCE_INLINE
|
||||
FORCE_INLINE_TEMPLATE
|
||||
void ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx,
|
||||
const void* src, size_t srcSize, const int ultra)
|
||||
{
|
||||
seqStore_t* seqStorePtr = &(ctx->seqStore);
|
||||
optState_t* optStatePtr = &(ctx->optState);
|
||||
const BYTE* const istart = (const BYTE*)src;
|
||||
const BYTE* ip = istart;
|
||||
const BYTE* anchor = istart;
|
||||
@@ -430,16 +431,16 @@ void ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx,
|
||||
const U32 mls = ctx->appliedParams.cParams.searchLength;
|
||||
const U32 minMatch = (ctx->appliedParams.cParams.searchLength == 3) ? 3 : 4;
|
||||
|
||||
ZSTD_optimal_t* opt = seqStorePtr->priceTable;
|
||||
ZSTD_match_t* matches = seqStorePtr->matchTable;
|
||||
ZSTD_optimal_t* opt = optStatePtr->priceTable;
|
||||
ZSTD_match_t* matches = optStatePtr->matchTable;
|
||||
const BYTE* inr;
|
||||
U32 offset, rep[ZSTD_REP_NUM];
|
||||
|
||||
/* init */
|
||||
ctx->nextToUpdate3 = ctx->nextToUpdate;
|
||||
ZSTD_rescaleFreqs(seqStorePtr, (const BYTE*)src, srcSize);
|
||||
ZSTD_rescaleFreqs(optStatePtr, (const BYTE*)src, srcSize);
|
||||
ip += (ip==prefixStart);
|
||||
{ U32 i; for (i=0; i<ZSTD_REP_NUM; i++) rep[i]=ctx->rep[i]; }
|
||||
{ U32 i; for (i=0; i<ZSTD_REP_NUM; i++) rep[i]=seqStorePtr->rep[i]; }
|
||||
|
||||
/* Match Loop */
|
||||
while (ip < ilimit) {
|
||||
@@ -462,7 +463,7 @@ void ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx,
|
||||
}
|
||||
best_off = i - (ip == anchor);
|
||||
do {
|
||||
price = ZSTD_getPrice(seqStorePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra);
|
||||
price = ZSTD_getPrice(optStatePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra);
|
||||
if (mlen > last_pos || price < opt[mlen].price)
|
||||
SET_PRICE(mlen, mlen, i, litlen, price); /* note : macro modifies last_pos */
|
||||
mlen--;
|
||||
@@ -487,7 +488,7 @@ void ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx,
|
||||
mlen = (u>0) ? matches[u-1].len+1 : best_mlen;
|
||||
best_mlen = matches[u].len;
|
||||
while (mlen <= best_mlen) {
|
||||
price = ZSTD_getPrice(seqStorePtr, litlen, anchor, matches[u].off-1, mlen - MINMATCH, ultra);
|
||||
price = ZSTD_getPrice(optStatePtr, litlen, anchor, matches[u].off-1, mlen - MINMATCH, ultra);
|
||||
if (mlen > last_pos || price < opt[mlen].price)
|
||||
SET_PRICE(mlen, mlen, matches[u].off, litlen, price); /* note : macro modifies last_pos */
|
||||
mlen++;
|
||||
@@ -507,12 +508,12 @@ void ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx,
|
||||
if (opt[cur-1].mlen == 1) {
|
||||
litlen = opt[cur-1].litlen + 1;
|
||||
if (cur > litlen) {
|
||||
price = opt[cur - litlen].price + ZSTD_getLiteralPrice(seqStorePtr, litlen, inr-litlen);
|
||||
price = opt[cur - litlen].price + ZSTD_getLiteralPrice(optStatePtr, litlen, inr-litlen);
|
||||
} else
|
||||
price = ZSTD_getLiteralPrice(seqStorePtr, litlen, anchor);
|
||||
price = ZSTD_getLiteralPrice(optStatePtr, litlen, anchor);
|
||||
} else {
|
||||
litlen = 1;
|
||||
price = opt[cur - 1].price + ZSTD_getLiteralPrice(seqStorePtr, litlen, inr-1);
|
||||
price = opt[cur - 1].price + ZSTD_getLiteralPrice(optStatePtr, litlen, inr-1);
|
||||
}
|
||||
|
||||
if (cur > last_pos || price <= opt[cur].price)
|
||||
@@ -554,12 +555,12 @@ void ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx,
|
||||
if (opt[cur].mlen == 1) {
|
||||
litlen = opt[cur].litlen;
|
||||
if (cur > litlen) {
|
||||
price = opt[cur - litlen].price + ZSTD_getPrice(seqStorePtr, litlen, inr-litlen, best_off, mlen - MINMATCH, ultra);
|
||||
price = opt[cur - litlen].price + ZSTD_getPrice(optStatePtr, litlen, inr-litlen, best_off, mlen - MINMATCH, ultra);
|
||||
} else
|
||||
price = ZSTD_getPrice(seqStorePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra);
|
||||
price = ZSTD_getPrice(optStatePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra);
|
||||
} else {
|
||||
litlen = 0;
|
||||
price = opt[cur].price + ZSTD_getPrice(seqStorePtr, 0, NULL, best_off, mlen - MINMATCH, ultra);
|
||||
price = opt[cur].price + ZSTD_getPrice(optStatePtr, 0, NULL, best_off, mlen - MINMATCH, ultra);
|
||||
}
|
||||
|
||||
if (cur + mlen > last_pos || price <= opt[cur + mlen].price)
|
||||
@@ -586,12 +587,12 @@ void ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx,
|
||||
if (opt[cur].mlen == 1) {
|
||||
litlen = opt[cur].litlen;
|
||||
if (cur > litlen)
|
||||
price = opt[cur - litlen].price + ZSTD_getPrice(seqStorePtr, litlen, ip+cur-litlen, matches[u].off-1, mlen - MINMATCH, ultra);
|
||||
price = opt[cur - litlen].price + ZSTD_getPrice(optStatePtr, litlen, ip+cur-litlen, matches[u].off-1, mlen - MINMATCH, ultra);
|
||||
else
|
||||
price = ZSTD_getPrice(seqStorePtr, litlen, anchor, matches[u].off-1, mlen - MINMATCH, ultra);
|
||||
price = ZSTD_getPrice(optStatePtr, litlen, anchor, matches[u].off-1, mlen - MINMATCH, ultra);
|
||||
} else {
|
||||
litlen = 0;
|
||||
price = opt[cur].price + ZSTD_getPrice(seqStorePtr, 0, NULL, matches[u].off-1, mlen - MINMATCH, ultra);
|
||||
price = opt[cur].price + ZSTD_getPrice(optStatePtr, 0, NULL, matches[u].off-1, mlen - MINMATCH, ultra);
|
||||
}
|
||||
|
||||
if (cur + mlen > last_pos || (price < opt[cur + mlen].price))
|
||||
@@ -645,13 +646,13 @@ _storeSequence: /* cur, last_pos, best_mlen, best_off have to be set */
|
||||
if (litLength==0) offset--;
|
||||
}
|
||||
|
||||
ZSTD_updatePrice(seqStorePtr, litLength, anchor, offset, mlen-MINMATCH);
|
||||
ZSTD_updatePrice(optStatePtr, litLength, anchor, offset, mlen-MINMATCH);
|
||||
ZSTD_storeSeq(seqStorePtr, litLength, anchor, offset, mlen-MINMATCH);
|
||||
anchor = ip = ip + mlen;
|
||||
} } /* for (cur=0; cur < last_pos; ) */
|
||||
|
||||
/* Save reps for next block */
|
||||
{ int i; for (i=0; i<ZSTD_REP_NUM; i++) ctx->repToConfirm[i] = rep[i]; }
|
||||
{ int i; for (i=0; i<ZSTD_REP_NUM; i++) seqStorePtr->repToConfirm[i] = rep[i]; }
|
||||
|
||||
/* Last Literals */
|
||||
{ size_t const lastLLSize = iend - anchor;
|
||||
@@ -661,11 +662,12 @@ _storeSequence: /* cur, last_pos, best_mlen, best_off have to be set */
|
||||
}
|
||||
|
||||
|
||||
FORCE_INLINE
|
||||
FORCE_INLINE_TEMPLATE
|
||||
void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx* ctx,
|
||||
const void* src, size_t srcSize, const int ultra)
|
||||
{
|
||||
seqStore_t* seqStorePtr = &(ctx->seqStore);
|
||||
optState_t* optStatePtr = &(ctx->optState);
|
||||
const BYTE* const istart = (const BYTE*)src;
|
||||
const BYTE* ip = istart;
|
||||
const BYTE* anchor = istart;
|
||||
@@ -683,16 +685,16 @@ void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx* ctx,
|
||||
const U32 mls = ctx->appliedParams.cParams.searchLength;
|
||||
const U32 minMatch = (ctx->appliedParams.cParams.searchLength == 3) ? 3 : 4;
|
||||
|
||||
ZSTD_optimal_t* opt = seqStorePtr->priceTable;
|
||||
ZSTD_match_t* matches = seqStorePtr->matchTable;
|
||||
ZSTD_optimal_t* opt = optStatePtr->priceTable;
|
||||
ZSTD_match_t* matches = optStatePtr->matchTable;
|
||||
const BYTE* inr;
|
||||
|
||||
/* init */
|
||||
U32 offset, rep[ZSTD_REP_NUM];
|
||||
{ U32 i; for (i=0; i<ZSTD_REP_NUM; i++) rep[i]=ctx->rep[i]; }
|
||||
{ U32 i; for (i=0; i<ZSTD_REP_NUM; i++) rep[i]=seqStorePtr->rep[i]; }
|
||||
|
||||
ctx->nextToUpdate3 = ctx->nextToUpdate;
|
||||
ZSTD_rescaleFreqs(seqStorePtr, (const BYTE*)src, srcSize);
|
||||
ZSTD_rescaleFreqs(optStatePtr, (const BYTE*)src, srcSize);
|
||||
ip += (ip==prefixStart);
|
||||
|
||||
/* Match Loop */
|
||||
@@ -726,7 +728,7 @@ void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx* ctx,
|
||||
best_off = i - (ip==anchor);
|
||||
litlen = opt[0].litlen;
|
||||
do {
|
||||
price = ZSTD_getPrice(seqStorePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra);
|
||||
price = ZSTD_getPrice(optStatePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra);
|
||||
if (mlen > last_pos || price < opt[mlen].price)
|
||||
SET_PRICE(mlen, mlen, i, litlen, price); /* note : macro modifies last_pos */
|
||||
mlen--;
|
||||
@@ -756,7 +758,7 @@ void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx* ctx,
|
||||
best_mlen = matches[u].len;
|
||||
litlen = opt[0].litlen;
|
||||
while (mlen <= best_mlen) {
|
||||
price = ZSTD_getPrice(seqStorePtr, litlen, anchor, matches[u].off-1, mlen - MINMATCH, ultra);
|
||||
price = ZSTD_getPrice(optStatePtr, litlen, anchor, matches[u].off-1, mlen - MINMATCH, ultra);
|
||||
if (mlen > last_pos || price < opt[mlen].price)
|
||||
SET_PRICE(mlen, mlen, matches[u].off, litlen, price);
|
||||
mlen++;
|
||||
@@ -773,12 +775,12 @@ void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx* ctx,
|
||||
if (opt[cur-1].mlen == 1) {
|
||||
litlen = opt[cur-1].litlen + 1;
|
||||
if (cur > litlen) {
|
||||
price = opt[cur - litlen].price + ZSTD_getLiteralPrice(seqStorePtr, litlen, inr-litlen);
|
||||
price = opt[cur - litlen].price + ZSTD_getLiteralPrice(optStatePtr, litlen, inr-litlen);
|
||||
} else
|
||||
price = ZSTD_getLiteralPrice(seqStorePtr, litlen, anchor);
|
||||
price = ZSTD_getLiteralPrice(optStatePtr, litlen, anchor);
|
||||
} else {
|
||||
litlen = 1;
|
||||
price = opt[cur - 1].price + ZSTD_getLiteralPrice(seqStorePtr, litlen, inr-1);
|
||||
price = opt[cur - 1].price + ZSTD_getLiteralPrice(optStatePtr, litlen, inr-1);
|
||||
}
|
||||
|
||||
if (cur > last_pos || price <= opt[cur].price)
|
||||
@@ -826,12 +828,12 @@ void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx* ctx,
|
||||
if (opt[cur].mlen == 1) {
|
||||
litlen = opt[cur].litlen;
|
||||
if (cur > litlen) {
|
||||
price = opt[cur - litlen].price + ZSTD_getPrice(seqStorePtr, litlen, inr-litlen, best_off, mlen - MINMATCH, ultra);
|
||||
price = opt[cur - litlen].price + ZSTD_getPrice(optStatePtr, litlen, inr-litlen, best_off, mlen - MINMATCH, ultra);
|
||||
} else
|
||||
price = ZSTD_getPrice(seqStorePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra);
|
||||
price = ZSTD_getPrice(optStatePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra);
|
||||
} else {
|
||||
litlen = 0;
|
||||
price = opt[cur].price + ZSTD_getPrice(seqStorePtr, 0, NULL, best_off, mlen - MINMATCH, ultra);
|
||||
price = opt[cur].price + ZSTD_getPrice(optStatePtr, 0, NULL, best_off, mlen - MINMATCH, ultra);
|
||||
}
|
||||
|
||||
if (cur + mlen > last_pos || price <= opt[cur + mlen].price)
|
||||
@@ -858,12 +860,12 @@ void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx* ctx,
|
||||
if (opt[cur].mlen == 1) {
|
||||
litlen = opt[cur].litlen;
|
||||
if (cur > litlen)
|
||||
price = opt[cur - litlen].price + ZSTD_getPrice(seqStorePtr, litlen, ip+cur-litlen, matches[u].off-1, mlen - MINMATCH, ultra);
|
||||
price = opt[cur - litlen].price + ZSTD_getPrice(optStatePtr, litlen, ip+cur-litlen, matches[u].off-1, mlen - MINMATCH, ultra);
|
||||
else
|
||||
price = ZSTD_getPrice(seqStorePtr, litlen, anchor, matches[u].off-1, mlen - MINMATCH, ultra);
|
||||
price = ZSTD_getPrice(optStatePtr, litlen, anchor, matches[u].off-1, mlen - MINMATCH, ultra);
|
||||
} else {
|
||||
litlen = 0;
|
||||
price = opt[cur].price + ZSTD_getPrice(seqStorePtr, 0, NULL, matches[u].off-1, mlen - MINMATCH, ultra);
|
||||
price = opt[cur].price + ZSTD_getPrice(optStatePtr, 0, NULL, matches[u].off-1, mlen - MINMATCH, ultra);
|
||||
}
|
||||
|
||||
if (cur + mlen > last_pos || (price < opt[cur + mlen].price))
|
||||
@@ -918,13 +920,13 @@ _storeSequence: /* cur, last_pos, best_mlen, best_off have to be set */
|
||||
if (litLength==0) offset--;
|
||||
}
|
||||
|
||||
ZSTD_updatePrice(seqStorePtr, litLength, anchor, offset, mlen-MINMATCH);
|
||||
ZSTD_updatePrice(optStatePtr, litLength, anchor, offset, mlen-MINMATCH);
|
||||
ZSTD_storeSeq(seqStorePtr, litLength, anchor, offset, mlen-MINMATCH);
|
||||
anchor = ip = ip + mlen;
|
||||
} } /* for (cur=0; cur < last_pos; ) */
|
||||
|
||||
/* Save reps for next block */
|
||||
{ int i; for (i=0; i<ZSTD_REP_NUM; i++) ctx->repToConfirm[i] = rep[i]; }
|
||||
{ int i; for (i=0; i<ZSTD_REP_NUM; i++) seqStorePtr->repToConfirm[i] = rep[i]; }
|
||||
|
||||
/* Last Literals */
|
||||
{ size_t lastLLSize = iend - anchor;
|
||||
|
||||
+222
-155
@@ -1,15 +1,16 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
/* ====== Tuning parameters ====== */
|
||||
#define ZSTDMT_NBTHREADS_MAX 128
|
||||
#define ZSTDMT_NBTHREADS_MAX 256
|
||||
#define ZSTDMT_OVERLAPLOG_DEFAULT 6
|
||||
|
||||
|
||||
/* ====== Compiler specifics ====== */
|
||||
@@ -73,6 +74,7 @@ static unsigned long long GetCurrentClockTimeMicroseconds(void)
|
||||
|
||||
|
||||
/* ===== Buffer Pool ===== */
|
||||
/* a single Buffer Pool can be invoked from multiple threads in parallel */
|
||||
|
||||
typedef struct buffer_s {
|
||||
void* start;
|
||||
@@ -82,6 +84,8 @@ typedef struct buffer_s {
|
||||
static const buffer_t g_nullBuffer = { NULL, 0 };
|
||||
|
||||
typedef struct ZSTDMT_bufferPool_s {
|
||||
pthread_mutex_t poolMutex;
|
||||
size_t bufferSize;
|
||||
unsigned totalBuffers;
|
||||
unsigned nbBuffers;
|
||||
ZSTD_customMem cMem;
|
||||
@@ -90,10 +94,15 @@ typedef struct ZSTDMT_bufferPool_s {
|
||||
|
||||
static ZSTDMT_bufferPool* ZSTDMT_createBufferPool(unsigned nbThreads, ZSTD_customMem cMem)
|
||||
{
|
||||
unsigned const maxNbBuffers = 2*nbThreads + 2;
|
||||
unsigned const maxNbBuffers = 2*nbThreads + 3;
|
||||
ZSTDMT_bufferPool* const bufPool = (ZSTDMT_bufferPool*)ZSTD_calloc(
|
||||
sizeof(ZSTDMT_bufferPool) + (maxNbBuffers-1) * sizeof(buffer_t), cMem);
|
||||
if (bufPool==NULL) return NULL;
|
||||
if (pthread_mutex_init(&bufPool->poolMutex, NULL)) {
|
||||
ZSTD_free(bufPool, cMem);
|
||||
return NULL;
|
||||
}
|
||||
bufPool->bufferSize = 64 KB;
|
||||
bufPool->totalBuffers = maxNbBuffers;
|
||||
bufPool->nbBuffers = 0;
|
||||
bufPool->cMem = cMem;
|
||||
@@ -106,6 +115,7 @@ static void ZSTDMT_freeBufferPool(ZSTDMT_bufferPool* bufPool)
|
||||
if (!bufPool) return; /* compatibility with free on NULL */
|
||||
for (u=0; u<bufPool->totalBuffers; u++)
|
||||
ZSTD_free(bufPool->bTable[u].start, bufPool->cMem);
|
||||
pthread_mutex_destroy(&bufPool->poolMutex);
|
||||
ZSTD_free(bufPool, bufPool->cMem);
|
||||
}
|
||||
|
||||
@@ -116,65 +126,85 @@ static size_t ZSTDMT_sizeof_bufferPool(ZSTDMT_bufferPool* bufPool)
|
||||
+ (bufPool->totalBuffers - 1) * sizeof(buffer_t);
|
||||
unsigned u;
|
||||
size_t totalBufferSize = 0;
|
||||
pthread_mutex_lock(&bufPool->poolMutex);
|
||||
for (u=0; u<bufPool->totalBuffers; u++)
|
||||
totalBufferSize += bufPool->bTable[u].size;
|
||||
pthread_mutex_unlock(&bufPool->poolMutex);
|
||||
|
||||
return poolSize + totalBufferSize;
|
||||
}
|
||||
|
||||
/** ZSTDMT_getBuffer() :
|
||||
* assumption : invocation from main thread only ! */
|
||||
static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* pool, size_t bSize)
|
||||
static void ZSTDMT_setBufferSize(ZSTDMT_bufferPool* bufPool, size_t bSize)
|
||||
{
|
||||
if (pool->nbBuffers) { /* try to use an existing buffer */
|
||||
buffer_t const buf = pool->bTable[--(pool->nbBuffers)];
|
||||
bufPool->bufferSize = bSize;
|
||||
}
|
||||
|
||||
/** ZSTDMT_getBuffer() :
|
||||
* assumption : bufPool must be valid */
|
||||
static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* bufPool)
|
||||
{
|
||||
size_t const bSize = bufPool->bufferSize;
|
||||
DEBUGLOG(5, "ZSTDMT_getBuffer");
|
||||
pthread_mutex_lock(&bufPool->poolMutex);
|
||||
if (bufPool->nbBuffers) { /* try to use an existing buffer */
|
||||
buffer_t const buf = bufPool->bTable[--(bufPool->nbBuffers)];
|
||||
size_t const availBufferSize = buf.size;
|
||||
if ((availBufferSize >= bSize) & (availBufferSize <= 10*bSize))
|
||||
if ((availBufferSize >= bSize) & (availBufferSize <= 10*bSize)) {
|
||||
/* large enough, but not too much */
|
||||
pthread_mutex_unlock(&bufPool->poolMutex);
|
||||
return buf;
|
||||
}
|
||||
/* size conditions not respected : scratch this buffer, create new one */
|
||||
ZSTD_free(buf.start, pool->cMem);
|
||||
DEBUGLOG(5, "existing buffer does not meet size conditions => freeing");
|
||||
ZSTD_free(buf.start, bufPool->cMem);
|
||||
}
|
||||
pthread_mutex_unlock(&bufPool->poolMutex);
|
||||
/* create new buffer */
|
||||
DEBUGLOG(5, "create a new buffer");
|
||||
{ buffer_t buffer;
|
||||
void* const start = ZSTD_malloc(bSize, pool->cMem);
|
||||
if (start==NULL) bSize = 0;
|
||||
void* const start = ZSTD_malloc(bSize, bufPool->cMem);
|
||||
buffer.start = start; /* note : start can be NULL if malloc fails ! */
|
||||
buffer.size = bSize;
|
||||
buffer.size = (start==NULL) ? 0 : bSize;
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
/* store buffer for later re-use, up to pool capacity */
|
||||
static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* pool, buffer_t buf)
|
||||
static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* bufPool, buffer_t buf)
|
||||
{
|
||||
if (buf.start == NULL) return; /* release on NULL */
|
||||
if (pool->nbBuffers < pool->totalBuffers) {
|
||||
pool->bTable[pool->nbBuffers++] = buf; /* store for later re-use */
|
||||
if (buf.start == NULL) return; /* compatible with release on NULL */
|
||||
DEBUGLOG(5, "ZSTDMT_releaseBuffer");
|
||||
pthread_mutex_lock(&bufPool->poolMutex);
|
||||
if (bufPool->nbBuffers < bufPool->totalBuffers) {
|
||||
bufPool->bTable[bufPool->nbBuffers++] = buf; /* stored for later use */
|
||||
pthread_mutex_unlock(&bufPool->poolMutex);
|
||||
return;
|
||||
}
|
||||
pthread_mutex_unlock(&bufPool->poolMutex);
|
||||
/* Reached bufferPool capacity (should not happen) */
|
||||
ZSTD_free(buf.start, pool->cMem);
|
||||
DEBUGLOG(5, "buffer pool capacity reached => freeing ");
|
||||
ZSTD_free(buf.start, bufPool->cMem);
|
||||
}
|
||||
|
||||
|
||||
/* ===== CCtx Pool ===== */
|
||||
/* a single CCtx Pool can be invoked from multiple threads in parallel */
|
||||
|
||||
typedef struct {
|
||||
pthread_mutex_t poolMutex;
|
||||
unsigned totalCCtx;
|
||||
unsigned availCCtx;
|
||||
ZSTD_customMem cMem;
|
||||
ZSTD_CCtx* cctx[1]; /* variable size */
|
||||
} ZSTDMT_CCtxPool;
|
||||
|
||||
/* assumption : CCtxPool invocation only from main thread */
|
||||
|
||||
/* note : all CCtx borrowed from the pool should be released back to the pool _before_ freeing the pool */
|
||||
static void ZSTDMT_freeCCtxPool(ZSTDMT_CCtxPool* pool)
|
||||
{
|
||||
unsigned u;
|
||||
for (u=0; u<pool->totalCCtx; u++)
|
||||
ZSTD_freeCCtx(pool->cctx[u]); /* note : compatible with free on NULL */
|
||||
pthread_mutex_destroy(&pool->poolMutex);
|
||||
ZSTD_free(pool, pool->cMem);
|
||||
}
|
||||
|
||||
@@ -186,6 +216,10 @@ static ZSTDMT_CCtxPool* ZSTDMT_createCCtxPool(unsigned nbThreads,
|
||||
ZSTDMT_CCtxPool* const cctxPool = (ZSTDMT_CCtxPool*) ZSTD_calloc(
|
||||
sizeof(ZSTDMT_CCtxPool) + (nbThreads-1)*sizeof(ZSTD_CCtx*), cMem);
|
||||
if (!cctxPool) return NULL;
|
||||
if (pthread_mutex_init(&cctxPool->poolMutex, NULL)) {
|
||||
ZSTD_free(cctxPool, cMem);
|
||||
return NULL;
|
||||
}
|
||||
cctxPool->cMem = cMem;
|
||||
cctxPool->totalCCtx = nbThreads;
|
||||
cctxPool->availCCtx = 1; /* at least one cctx for single-thread mode */
|
||||
@@ -198,50 +232,57 @@ static ZSTDMT_CCtxPool* ZSTDMT_createCCtxPool(unsigned nbThreads,
|
||||
/* only works during initialization phase, not during compression */
|
||||
static size_t ZSTDMT_sizeof_CCtxPool(ZSTDMT_CCtxPool* cctxPool)
|
||||
{
|
||||
unsigned const nbThreads = cctxPool->totalCCtx;
|
||||
size_t const poolSize = sizeof(*cctxPool)
|
||||
+ (nbThreads-1)*sizeof(ZSTD_CCtx*);
|
||||
unsigned u;
|
||||
size_t totalCCtxSize = 0;
|
||||
for (u=0; u<nbThreads; u++)
|
||||
totalCCtxSize += ZSTD_sizeof_CCtx(cctxPool->cctx[u]);
|
||||
|
||||
return poolSize + totalCCtxSize;
|
||||
pthread_mutex_lock(&cctxPool->poolMutex);
|
||||
{ unsigned const nbThreads = cctxPool->totalCCtx;
|
||||
size_t const poolSize = sizeof(*cctxPool)
|
||||
+ (nbThreads-1)*sizeof(ZSTD_CCtx*);
|
||||
unsigned u;
|
||||
size_t totalCCtxSize = 0;
|
||||
for (u=0; u<nbThreads; u++) {
|
||||
totalCCtxSize += ZSTD_sizeof_CCtx(cctxPool->cctx[u]);
|
||||
}
|
||||
pthread_mutex_unlock(&cctxPool->poolMutex);
|
||||
return poolSize + totalCCtxSize;
|
||||
}
|
||||
}
|
||||
|
||||
static ZSTD_CCtx* ZSTDMT_getCCtx(ZSTDMT_CCtxPool* pool)
|
||||
static ZSTD_CCtx* ZSTDMT_getCCtx(ZSTDMT_CCtxPool* cctxPool)
|
||||
{
|
||||
if (pool->availCCtx) {
|
||||
pool->availCCtx--;
|
||||
return pool->cctx[pool->availCCtx];
|
||||
}
|
||||
return ZSTD_createCCtx(); /* note : can be NULL, when creation fails ! */
|
||||
DEBUGLOG(5, "ZSTDMT_getCCtx");
|
||||
pthread_mutex_lock(&cctxPool->poolMutex);
|
||||
if (cctxPool->availCCtx) {
|
||||
cctxPool->availCCtx--;
|
||||
{ ZSTD_CCtx* const cctx = cctxPool->cctx[cctxPool->availCCtx];
|
||||
pthread_mutex_unlock(&cctxPool->poolMutex);
|
||||
return cctx;
|
||||
} }
|
||||
pthread_mutex_unlock(&cctxPool->poolMutex);
|
||||
DEBUGLOG(5, "create one more CCtx");
|
||||
return ZSTD_createCCtx_advanced(cctxPool->cMem); /* note : can be NULL, when creation fails ! */
|
||||
}
|
||||
|
||||
static void ZSTDMT_releaseCCtx(ZSTDMT_CCtxPool* pool, ZSTD_CCtx* cctx)
|
||||
{
|
||||
if (cctx==NULL) return; /* compatibility with release on NULL */
|
||||
pthread_mutex_lock(&pool->poolMutex);
|
||||
if (pool->availCCtx < pool->totalCCtx)
|
||||
pool->cctx[pool->availCCtx++] = cctx;
|
||||
else
|
||||
else {
|
||||
/* pool overflow : should not happen, since totalCCtx==nbThreads */
|
||||
DEBUGLOG(5, "CCtx pool overflow : free cctx");
|
||||
ZSTD_freeCCtx(cctx);
|
||||
}
|
||||
pthread_mutex_unlock(&pool->poolMutex);
|
||||
}
|
||||
|
||||
|
||||
/* ===== Thread worker ===== */
|
||||
|
||||
typedef struct {
|
||||
buffer_t buffer;
|
||||
size_t filled;
|
||||
} inBuff_t;
|
||||
|
||||
typedef struct {
|
||||
ZSTD_CCtx* cctx;
|
||||
buffer_t src;
|
||||
const void* srcStart;
|
||||
size_t srcSize;
|
||||
size_t dictSize;
|
||||
size_t srcSize;
|
||||
buffer_t dstBuff;
|
||||
size_t cSize;
|
||||
size_t dstFlushed;
|
||||
@@ -253,6 +294,8 @@ typedef struct {
|
||||
pthread_cond_t* jobCompleted_cond;
|
||||
ZSTD_parameters params;
|
||||
const ZSTD_CDict* cdict;
|
||||
ZSTDMT_CCtxPool* cctxPool;
|
||||
ZSTDMT_bufferPool* bufPool;
|
||||
unsigned long long fullFrameSize;
|
||||
} ZSTDMT_jobDescription;
|
||||
|
||||
@@ -260,37 +303,56 @@ typedef struct {
|
||||
void ZSTDMT_compressChunk(void* jobDescription)
|
||||
{
|
||||
ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription;
|
||||
ZSTD_CCtx* cctx = ZSTDMT_getCCtx(job->cctxPool);
|
||||
const void* const src = (const char*)job->srcStart + job->dictSize;
|
||||
buffer_t const dstBuff = job->dstBuff;
|
||||
buffer_t dstBuff = job->dstBuff;
|
||||
DEBUGLOG(5, "job (first:%u) (last:%u) : dictSize %u, srcSize %u",
|
||||
job->firstChunk, job->lastChunk, (U32)job->dictSize, (U32)job->srcSize);
|
||||
|
||||
if (cctx==NULL) {
|
||||
job->cSize = ERROR(memory_allocation);
|
||||
goto _endJob;
|
||||
}
|
||||
|
||||
if (dstBuff.start == NULL) {
|
||||
dstBuff = ZSTDMT_getBuffer(job->bufPool);
|
||||
if (dstBuff.start==NULL) {
|
||||
job->cSize = ERROR(memory_allocation);
|
||||
goto _endJob;
|
||||
}
|
||||
job->dstBuff = dstBuff;
|
||||
}
|
||||
|
||||
if (job->cdict) { /* should only happen for first segment */
|
||||
size_t const initError = ZSTD_compressBegin_usingCDict_advanced(job->cctx, job->cdict, job->params.fParams, job->fullFrameSize);
|
||||
size_t const initError = ZSTD_compressBegin_usingCDict_advanced(cctx, job->cdict, job->params.fParams, job->fullFrameSize);
|
||||
DEBUGLOG(5, "using CDict");
|
||||
if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; }
|
||||
} else { /* srcStart points at reloaded section */
|
||||
if (!job->firstChunk) job->params.fParams.contentSizeFlag = 0; /* ensure no srcSize control */
|
||||
{ size_t const dictModeError = ZSTD_setCCtxParameter(job->cctx, ZSTD_p_forceRawDict, 1); /* Force loading dictionary in "content-only" mode (no header analysis) */
|
||||
size_t const initError = ZSTD_compressBegin_advanced(job->cctx, job->srcStart, job->dictSize, job->params, job->fullFrameSize);
|
||||
{ size_t const dictModeError = ZSTD_setCCtxParameter(cctx, ZSTD_p_forceRawDict, 1); /* Force loading dictionary in "content-only" mode (no header analysis) */
|
||||
size_t const initError = ZSTD_compressBegin_advanced(cctx, job->srcStart, job->dictSize, job->params, job->fullFrameSize);
|
||||
if (ZSTD_isError(initError) || ZSTD_isError(dictModeError)) { job->cSize = initError; goto _endJob; }
|
||||
ZSTD_setCCtxParameter(job->cctx, ZSTD_p_forceWindow, 1);
|
||||
ZSTD_setCCtxParameter(cctx, ZSTD_p_forceWindow, 1);
|
||||
} }
|
||||
if (!job->firstChunk) { /* flush and overwrite frame header when it's not first segment */
|
||||
size_t const hSize = ZSTD_compressContinue(job->cctx, dstBuff.start, dstBuff.size, src, 0);
|
||||
size_t const hSize = ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.size, src, 0);
|
||||
if (ZSTD_isError(hSize)) { job->cSize = hSize; goto _endJob; }
|
||||
ZSTD_invalidateRepCodes(job->cctx);
|
||||
ZSTD_invalidateRepCodes(cctx);
|
||||
}
|
||||
|
||||
DEBUGLOG(5, "Compressing : ");
|
||||
DEBUG_PRINTHEX(4, job->srcStart, 12);
|
||||
job->cSize = (job->lastChunk) ?
|
||||
ZSTD_compressEnd (job->cctx, dstBuff.start, dstBuff.size, src, job->srcSize) :
|
||||
ZSTD_compressContinue(job->cctx, dstBuff.start, dstBuff.size, src, job->srcSize);
|
||||
ZSTD_compressEnd (cctx, dstBuff.start, dstBuff.size, src, job->srcSize) :
|
||||
ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.size, src, job->srcSize);
|
||||
DEBUGLOG(5, "compressed %u bytes into %u bytes (first:%u) (last:%u)",
|
||||
(unsigned)job->srcSize, (unsigned)job->cSize, job->firstChunk, job->lastChunk);
|
||||
DEBUGLOG(5, "dstBuff.size : %u ; => %s", (U32)dstBuff.size, ZSTD_getErrorName(job->cSize));
|
||||
|
||||
_endJob:
|
||||
ZSTDMT_releaseCCtx(job->cctxPool, cctx);
|
||||
ZSTDMT_releaseBuffer(job->bufPool, job->src);
|
||||
job->src = g_nullBuffer; job->srcStart = NULL;
|
||||
PTHREAD_MUTEX_LOCK(job->jobCompleted_mutex);
|
||||
job->jobCompleted = 1;
|
||||
job->jobScanned = 0;
|
||||
@@ -303,15 +365,19 @@ _endJob:
|
||||
/* ===== Multi-threaded compression ===== */
|
||||
/* ------------------------------------------ */
|
||||
|
||||
typedef struct {
|
||||
buffer_t buffer;
|
||||
size_t filled;
|
||||
} inBuff_t;
|
||||
|
||||
struct ZSTDMT_CCtx_s {
|
||||
POOL_ctx* factory;
|
||||
ZSTDMT_jobDescription* jobs;
|
||||
ZSTDMT_bufferPool* buffPool;
|
||||
ZSTDMT_bufferPool* bufPool;
|
||||
ZSTDMT_CCtxPool* cctxPool;
|
||||
pthread_mutex_t jobCompleted_mutex;
|
||||
pthread_cond_t jobCompleted_cond;
|
||||
size_t targetSectionSize;
|
||||
size_t marginSize;
|
||||
size_t inBuffSize;
|
||||
size_t dictSize;
|
||||
size_t targetDictSize;
|
||||
@@ -324,7 +390,7 @@ struct ZSTDMT_CCtx_s {
|
||||
unsigned nextJobID;
|
||||
unsigned frameEnded;
|
||||
unsigned allJobsCompleted;
|
||||
unsigned overlapRLog;
|
||||
unsigned overlapLog;
|
||||
unsigned long long frameContentSize;
|
||||
size_t sectionSize;
|
||||
ZSTD_customMem cMem;
|
||||
@@ -347,7 +413,8 @@ ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbThreads, ZSTD_customMem cMem)
|
||||
U32 nbJobs = nbThreads + 2;
|
||||
DEBUGLOG(3, "ZSTDMT_createCCtx_advanced");
|
||||
|
||||
if ((nbThreads < 1) | (nbThreads > ZSTDMT_NBTHREADS_MAX)) return NULL;
|
||||
if (nbThreads < 1) return NULL;
|
||||
nbThreads = MIN(nbThreads , ZSTDMT_NBTHREADS_MAX);
|
||||
if ((cMem.customAlloc!=NULL) ^ (cMem.customFree!=NULL))
|
||||
/* invalid custom allocator */
|
||||
return NULL;
|
||||
@@ -358,18 +425,24 @@ ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbThreads, ZSTD_customMem cMem)
|
||||
mtctx->nbThreads = nbThreads;
|
||||
mtctx->allJobsCompleted = 1;
|
||||
mtctx->sectionSize = 0;
|
||||
mtctx->overlapRLog = 3;
|
||||
mtctx->factory = POOL_create(nbThreads, 1);
|
||||
mtctx->overlapLog = ZSTDMT_OVERLAPLOG_DEFAULT;
|
||||
mtctx->factory = POOL_create(nbThreads, 0);
|
||||
mtctx->jobs = ZSTDMT_allocJobsTable(&nbJobs, cMem);
|
||||
mtctx->jobIDMask = nbJobs - 1;
|
||||
mtctx->buffPool = ZSTDMT_createBufferPool(nbThreads, cMem);
|
||||
mtctx->bufPool = ZSTDMT_createBufferPool(nbThreads, cMem);
|
||||
mtctx->cctxPool = ZSTDMT_createCCtxPool(nbThreads, cMem);
|
||||
if (!mtctx->factory | !mtctx->jobs | !mtctx->buffPool | !mtctx->cctxPool) {
|
||||
if (!mtctx->factory | !mtctx->jobs | !mtctx->bufPool | !mtctx->cctxPool) {
|
||||
ZSTDMT_freeCCtx(mtctx);
|
||||
return NULL;
|
||||
}
|
||||
if (pthread_mutex_init(&mtctx->jobCompleted_mutex, NULL)) {
|
||||
ZSTDMT_freeCCtx(mtctx);
|
||||
return NULL;
|
||||
}
|
||||
if (pthread_cond_init(&mtctx->jobCompleted_cond, NULL)) {
|
||||
ZSTDMT_freeCCtx(mtctx);
|
||||
return NULL;
|
||||
}
|
||||
pthread_mutex_init(&mtctx->jobCompleted_mutex, NULL); /* Todo : check init function return */
|
||||
pthread_cond_init(&mtctx->jobCompleted_cond, NULL);
|
||||
DEBUGLOG(3, "mt_cctx created, for %u threads", nbThreads);
|
||||
return mtctx;
|
||||
}
|
||||
@@ -386,15 +459,13 @@ static void ZSTDMT_releaseAllJobResources(ZSTDMT_CCtx* mtctx)
|
||||
unsigned jobID;
|
||||
DEBUGLOG(3, "ZSTDMT_releaseAllJobResources");
|
||||
for (jobID=0; jobID <= mtctx->jobIDMask; jobID++) {
|
||||
ZSTDMT_releaseBuffer(mtctx->buffPool, mtctx->jobs[jobID].dstBuff);
|
||||
ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[jobID].dstBuff);
|
||||
mtctx->jobs[jobID].dstBuff = g_nullBuffer;
|
||||
ZSTDMT_releaseBuffer(mtctx->buffPool, mtctx->jobs[jobID].src);
|
||||
ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[jobID].src);
|
||||
mtctx->jobs[jobID].src = g_nullBuffer;
|
||||
ZSTDMT_releaseCCtx(mtctx->cctxPool, mtctx->jobs[jobID].cctx);
|
||||
mtctx->jobs[jobID].cctx = NULL;
|
||||
}
|
||||
memset(mtctx->jobs, 0, (mtctx->jobIDMask+1)*sizeof(ZSTDMT_jobDescription));
|
||||
ZSTDMT_releaseBuffer(mtctx->buffPool, mtctx->inBuff.buffer);
|
||||
ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->inBuff.buffer);
|
||||
mtctx->inBuff.buffer = g_nullBuffer;
|
||||
mtctx->allJobsCompleted = 1;
|
||||
}
|
||||
@@ -404,7 +475,7 @@ size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx)
|
||||
if (mtctx==NULL) return 0; /* compatible with free on NULL */
|
||||
POOL_free(mtctx->factory);
|
||||
if (!mtctx->allJobsCompleted) ZSTDMT_releaseAllJobResources(mtctx); /* stop workers first */
|
||||
ZSTDMT_freeBufferPool(mtctx->buffPool); /* release job resources into pools first */
|
||||
ZSTDMT_freeBufferPool(mtctx->bufPool); /* release job resources into pools first */
|
||||
ZSTD_free(mtctx->jobs, mtctx->cMem);
|
||||
ZSTDMT_freeCCtxPool(mtctx->cctxPool);
|
||||
ZSTD_freeCDict(mtctx->cdictLocal);
|
||||
@@ -418,11 +489,11 @@ size_t ZSTDMT_sizeof_CCtx(ZSTDMT_CCtx* mtctx)
|
||||
{
|
||||
if (mtctx == NULL) return 0; /* supports sizeof NULL */
|
||||
return sizeof(*mtctx)
|
||||
+ POOL_sizeof(mtctx->factory)
|
||||
+ ZSTDMT_sizeof_bufferPool(mtctx->buffPool)
|
||||
+ (mtctx->jobIDMask+1) * sizeof(ZSTDMT_jobDescription)
|
||||
+ ZSTDMT_sizeof_CCtxPool(mtctx->cctxPool)
|
||||
+ ZSTD_sizeof_CDict(mtctx->cdictLocal);
|
||||
+ POOL_sizeof(mtctx->factory)
|
||||
+ ZSTDMT_sizeof_bufferPool(mtctx->bufPool)
|
||||
+ (mtctx->jobIDMask+1) * sizeof(ZSTDMT_jobDescription)
|
||||
+ ZSTDMT_sizeof_CCtxPool(mtctx->cctxPool)
|
||||
+ ZSTD_sizeof_CDict(mtctx->cdictLocal);
|
||||
}
|
||||
|
||||
size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSDTMT_parameter parameter, unsigned value)
|
||||
@@ -434,10 +505,10 @@ size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSDTMT_parameter parameter,
|
||||
return 0;
|
||||
case ZSTDMT_p_overlapSectionLog :
|
||||
DEBUGLOG(5, "ZSTDMT_p_overlapSectionLog : %u", value);
|
||||
mtctx->overlapRLog = (value >= 9) ? 0 : 9 - value;
|
||||
mtctx->overlapLog = (value >= 9) ? 9 : value;
|
||||
return 0;
|
||||
default :
|
||||
return ERROR(compressionParameter_unsupported);
|
||||
return ERROR(parameter_unsupported);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,12 +530,13 @@ static unsigned computeNbChunks(size_t srcSize, unsigned windowLog, unsigned nbT
|
||||
|
||||
|
||||
size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx,
|
||||
void* dst, size_t dstCapacity,
|
||||
const void* src, size_t srcSize,
|
||||
const ZSTD_CDict* cdict,
|
||||
ZSTD_parameters const params,
|
||||
unsigned overlapRLog)
|
||||
void* dst, size_t dstCapacity,
|
||||
const void* src, size_t srcSize,
|
||||
const ZSTD_CDict* cdict,
|
||||
ZSTD_parameters const params,
|
||||
unsigned overlapLog)
|
||||
{
|
||||
unsigned const overlapRLog = (overlapLog>9) ? 0 : 9-overlapLog;
|
||||
size_t const overlapSize = (overlapRLog>=9) ? 0 : (size_t)1 << (params.cParams.windowLog - overlapRLog);
|
||||
unsigned nbChunks = computeNbChunks(srcSize, params.cParams.windowLog, mtctx->nbThreads);
|
||||
size_t const proposedChunkSize = (srcSize + (nbChunks-1)) / nbChunks;
|
||||
@@ -473,6 +545,7 @@ size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx,
|
||||
size_t remainingSrcSize = srcSize;
|
||||
unsigned const compressWithinDst = (dstCapacity >= ZSTD_compressBound(srcSize)) ? nbChunks : (unsigned)(dstCapacity / ZSTD_compressBound(avgChunkSize)); /* presumes avgChunkSize >= 256 KB, which should be the case */
|
||||
size_t frameStartPos = 0, dstBufferPos = 0;
|
||||
XXH64_state_t xxh64;
|
||||
|
||||
DEBUGLOG(4, "nbChunks : %2u (chunkSize : %u bytes) ", nbChunks, (U32)avgChunkSize);
|
||||
if (nbChunks==1) { /* fallback to single-thread mode */
|
||||
@@ -480,7 +553,9 @@ size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx,
|
||||
if (cdict) return ZSTD_compress_usingCDict_advanced(cctx, dst, dstCapacity, src, srcSize, cdict, params.fParams);
|
||||
return ZSTD_compress_advanced(cctx, dst, dstCapacity, src, srcSize, NULL, 0, params);
|
||||
}
|
||||
assert(avgChunkSize >= 256 KB); /* condition for ZSTD_compressBound(A) + ZSTD_compressBound(B) <= ZSTD_compressBound(A+B), which is useful to avoid allocating extra buffers */
|
||||
assert(avgChunkSize >= 256 KB); /* condition for ZSTD_compressBound(A) + ZSTD_compressBound(B) <= ZSTD_compressBound(A+B), which is required for compressWithinDst */
|
||||
ZSTDMT_setBufferSize(mtctx->bufPool, ZSTD_compressBound(avgChunkSize) );
|
||||
XXH64_reset(&xxh64, 0);
|
||||
|
||||
if (nbChunks > mtctx->jobIDMask+1) { /* enlarge job table */
|
||||
U32 nbJobs = nbChunks;
|
||||
@@ -496,17 +571,10 @@ size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx,
|
||||
size_t const chunkSize = MIN(remainingSrcSize, avgChunkSize);
|
||||
size_t const dstBufferCapacity = ZSTD_compressBound(chunkSize);
|
||||
buffer_t const dstAsBuffer = { (char*)dst + dstBufferPos, dstBufferCapacity };
|
||||
buffer_t const dstBuffer = u < compressWithinDst ? dstAsBuffer : ZSTDMT_getBuffer(mtctx->buffPool, dstBufferCapacity);
|
||||
ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(mtctx->cctxPool);
|
||||
buffer_t const dstBuffer = u < compressWithinDst ? dstAsBuffer : g_nullBuffer;
|
||||
size_t dictSize = u ? overlapSize : 0;
|
||||
|
||||
if ((cctx==NULL) || (dstBuffer.start==NULL)) {
|
||||
mtctx->jobs[u].cSize = ERROR(memory_allocation); /* job result */
|
||||
mtctx->jobs[u].jobCompleted = 1;
|
||||
nbChunks = u+1; /* only wait and free u jobs, instead of initially expected nbChunks ones */
|
||||
break; /* let's wait for previous jobs to complete, but don't start new ones */
|
||||
}
|
||||
|
||||
mtctx->jobs[u].src = g_nullBuffer;
|
||||
mtctx->jobs[u].srcStart = srcStart + frameStartPos - dictSize;
|
||||
mtctx->jobs[u].dictSize = dictSize;
|
||||
mtctx->jobs[u].srcSize = chunkSize;
|
||||
@@ -516,13 +584,18 @@ size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx,
|
||||
/* do not calculate checksum within sections, but write it in header for first section */
|
||||
if (u!=0) mtctx->jobs[u].params.fParams.checksumFlag = 0;
|
||||
mtctx->jobs[u].dstBuff = dstBuffer;
|
||||
mtctx->jobs[u].cctx = cctx;
|
||||
mtctx->jobs[u].cctxPool = mtctx->cctxPool;
|
||||
mtctx->jobs[u].bufPool = mtctx->bufPool;
|
||||
mtctx->jobs[u].firstChunk = (u==0);
|
||||
mtctx->jobs[u].lastChunk = (u==nbChunks-1);
|
||||
mtctx->jobs[u].jobCompleted = 0;
|
||||
mtctx->jobs[u].jobCompleted_mutex = &mtctx->jobCompleted_mutex;
|
||||
mtctx->jobs[u].jobCompleted_cond = &mtctx->jobCompleted_cond;
|
||||
|
||||
if (params.fParams.checksumFlag) {
|
||||
XXH64_update(&xxh64, srcStart + frameStartPos, chunkSize);
|
||||
}
|
||||
|
||||
DEBUGLOG(5, "posting job %u (%u bytes)", u, (U32)chunkSize);
|
||||
DEBUG_PRINTHEX(6, mtctx->jobs[u].srcStart, 12);
|
||||
POOL_add(mtctx->factory, ZSTDMT_compressChunk, &mtctx->jobs[u]);
|
||||
@@ -533,8 +606,8 @@ size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx,
|
||||
} }
|
||||
|
||||
/* collect result */
|
||||
{ unsigned chunkID;
|
||||
size_t error = 0, dstPos = 0;
|
||||
{ size_t error = 0, dstPos = 0;
|
||||
unsigned chunkID;
|
||||
for (chunkID=0; chunkID<nbChunks; chunkID++) {
|
||||
DEBUGLOG(5, "waiting for chunk %u ", chunkID);
|
||||
PTHREAD_MUTEX_LOCK(&mtctx->jobCompleted_mutex);
|
||||
@@ -545,8 +618,6 @@ size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx,
|
||||
pthread_mutex_unlock(&mtctx->jobCompleted_mutex);
|
||||
DEBUGLOG(5, "ready to write chunk %u ", chunkID);
|
||||
|
||||
ZSTDMT_releaseCCtx(mtctx->cctxPool, mtctx->jobs[chunkID].cctx);
|
||||
mtctx->jobs[chunkID].cctx = NULL;
|
||||
mtctx->jobs[chunkID].srcStart = NULL;
|
||||
{ size_t const cSize = mtctx->jobs[chunkID].cSize;
|
||||
if (ZSTD_isError(cSize)) error = cSize;
|
||||
@@ -556,13 +627,25 @@ size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx,
|
||||
memmove((char*)dst + dstPos, mtctx->jobs[chunkID].dstBuff.start, cSize); /* may overlap when chunk compressed within dst */
|
||||
if (chunkID >= compressWithinDst) { /* chunk compressed into its own buffer, which must be released */
|
||||
DEBUGLOG(5, "releasing buffer %u>=%u", chunkID, compressWithinDst);
|
||||
ZSTDMT_releaseBuffer(mtctx->buffPool, mtctx->jobs[chunkID].dstBuff);
|
||||
ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[chunkID].dstBuff);
|
||||
}
|
||||
mtctx->jobs[chunkID].dstBuff = g_nullBuffer;
|
||||
}
|
||||
dstPos += cSize ;
|
||||
}
|
||||
}
|
||||
} /* for (chunkID=0; chunkID<nbChunks; chunkID++) */
|
||||
|
||||
DEBUGLOG(4, "checksumFlag : %u ", params.fParams.checksumFlag);
|
||||
if (params.fParams.checksumFlag) {
|
||||
U32 const checksum = (U32)XXH64_digest(&xxh64);
|
||||
if (dstPos + 4 > dstCapacity) {
|
||||
error = ERROR(dstSize_tooSmall);
|
||||
} else {
|
||||
DEBUGLOG(4, "writing checksum : %08X \n", checksum);
|
||||
MEM_writeLE32((char*)dst + dstPos, checksum);
|
||||
dstPos += 4;
|
||||
} }
|
||||
|
||||
if (!error) DEBUGLOG(4, "compressed size : %u ", (U32)dstPos);
|
||||
return error ? error : dstPos;
|
||||
}
|
||||
@@ -574,10 +657,10 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx,
|
||||
const void* src, size_t srcSize,
|
||||
int compressionLevel)
|
||||
{
|
||||
U32 const overlapRLog = (compressionLevel >= ZSTD_maxCLevel()) ? 0 : 3;
|
||||
U32 const overlapLog = (compressionLevel >= ZSTD_maxCLevel()) ? 9 : ZSTDMT_OVERLAPLOG_DEFAULT;
|
||||
ZSTD_parameters params = ZSTD_getParams(compressionLevel, srcSize, 0);
|
||||
params.fParams.contentSizeFlag = 1;
|
||||
return ZSTDMT_compress_advanced(mtctx, dst, dstCapacity, src, srcSize, NULL, params, overlapRLog);
|
||||
return ZSTDMT_compress_advanced(mtctx, dst, dstCapacity, src, srcSize, NULL, params, overlapLog);
|
||||
}
|
||||
|
||||
|
||||
@@ -615,8 +698,8 @@ size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs,
|
||||
if (zcs->nbThreads==1) {
|
||||
DEBUGLOG(4, "single thread mode");
|
||||
return ZSTD_initCStream_internal(zcs->cctxPool->cctx[0],
|
||||
dict, dictSize, cdict,
|
||||
params, pledgedSrcSize);
|
||||
dict, dictSize, cdict,
|
||||
params, pledgedSrcSize);
|
||||
}
|
||||
|
||||
if (zcs->allJobsCompleted == 0) { /* previous compression not correctly finished */
|
||||
@@ -642,18 +725,16 @@ size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs,
|
||||
zcs->cdict = cdict;
|
||||
}
|
||||
|
||||
zcs->targetDictSize = (zcs->overlapRLog>=9) ? 0 : (size_t)1 << (zcs->params.cParams.windowLog - zcs->overlapRLog);
|
||||
DEBUGLOG(4, "overlapRLog : %u ", zcs->overlapRLog);
|
||||
zcs->targetDictSize = (zcs->overlapLog==0) ? 0 : (size_t)1 << (zcs->params.cParams.windowLog - (9 - zcs->overlapLog));
|
||||
DEBUGLOG(4, "overlapLog : %u ", zcs->overlapLog);
|
||||
DEBUGLOG(4, "overlap Size : %u KB", (U32)(zcs->targetDictSize>>10));
|
||||
zcs->targetSectionSize = zcs->sectionSize ? zcs->sectionSize : (size_t)1 << (zcs->params.cParams.windowLog + 2);
|
||||
zcs->targetSectionSize = MAX(ZSTDMT_SECTION_SIZE_MIN, zcs->targetSectionSize);
|
||||
zcs->targetSectionSize = MAX(zcs->targetDictSize, zcs->targetSectionSize);
|
||||
DEBUGLOG(4, "Section Size : %u KB", (U32)(zcs->targetSectionSize>>10));
|
||||
zcs->marginSize = zcs->targetSectionSize >> 2;
|
||||
zcs->inBuffSize = zcs->targetDictSize + zcs->targetSectionSize + zcs->marginSize;
|
||||
zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize);
|
||||
if (zcs->inBuff.buffer.start == NULL) return ERROR(memory_allocation);
|
||||
zcs->inBuff.filled = 0;
|
||||
zcs->inBuffSize = zcs->targetDictSize + zcs->targetSectionSize;
|
||||
ZSTDMT_setBufferSize(zcs->bufPool, MAX(zcs->inBuffSize, ZSTD_compressBound(zcs->targetSectionSize)) );
|
||||
zcs->inBuff.buffer = g_nullBuffer;
|
||||
zcs->dictSize = 0;
|
||||
zcs->doneJobID = 0;
|
||||
zcs->nextJobID = 0;
|
||||
@@ -664,8 +745,9 @@ size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs,
|
||||
}
|
||||
|
||||
size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* mtctx,
|
||||
const void* dict, size_t dictSize,
|
||||
ZSTD_parameters params, unsigned long long pledgedSrcSize)
|
||||
const void* dict, size_t dictSize,
|
||||
ZSTD_parameters params,
|
||||
unsigned long long pledgedSrcSize)
|
||||
{
|
||||
DEBUGLOG(5, "ZSTDMT_initCStream_advanced");
|
||||
return ZSTDMT_initCStream_internal(mtctx, dict, dictSize, NULL, params, pledgedSrcSize);
|
||||
@@ -701,19 +783,8 @@ size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel) {
|
||||
|
||||
static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsigned endFrame)
|
||||
{
|
||||
size_t const dstBufferCapacity = ZSTD_compressBound(srcSize);
|
||||
buffer_t const dstBuffer = ZSTDMT_getBuffer(zcs->buffPool, dstBufferCapacity);
|
||||
ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(zcs->cctxPool);
|
||||
unsigned const jobID = zcs->nextJobID & zcs->jobIDMask;
|
||||
|
||||
if ((cctx==NULL) || (dstBuffer.start==NULL)) {
|
||||
zcs->jobs[jobID].jobCompleted = 1;
|
||||
zcs->nextJobID++;
|
||||
ZSTDMT_waitForAllJobsCompleted(zcs);
|
||||
ZSTDMT_releaseAllJobResources(zcs);
|
||||
return ERROR(memory_allocation);
|
||||
}
|
||||
|
||||
DEBUGLOG(4, "preparing job %u to compress %u bytes with %u preload ",
|
||||
zcs->nextJobID, (U32)srcSize, (U32)zcs->dictSize);
|
||||
zcs->jobs[jobID].src = zcs->inBuff.buffer;
|
||||
@@ -726,8 +797,9 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi
|
||||
if (zcs->nextJobID) zcs->jobs[jobID].params.fParams.checksumFlag = 0;
|
||||
zcs->jobs[jobID].cdict = zcs->nextJobID==0 ? zcs->cdict : NULL;
|
||||
zcs->jobs[jobID].fullFrameSize = zcs->frameContentSize;
|
||||
zcs->jobs[jobID].dstBuff = dstBuffer;
|
||||
zcs->jobs[jobID].cctx = cctx;
|
||||
zcs->jobs[jobID].dstBuff = g_nullBuffer;
|
||||
zcs->jobs[jobID].cctxPool = zcs->cctxPool;
|
||||
zcs->jobs[jobID].bufPool = zcs->bufPool;
|
||||
zcs->jobs[jobID].firstChunk = (zcs->nextJobID==0);
|
||||
zcs->jobs[jobID].lastChunk = endFrame;
|
||||
zcs->jobs[jobID].jobCompleted = 0;
|
||||
@@ -735,11 +807,13 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi
|
||||
zcs->jobs[jobID].jobCompleted_mutex = &zcs->jobCompleted_mutex;
|
||||
zcs->jobs[jobID].jobCompleted_cond = &zcs->jobCompleted_cond;
|
||||
|
||||
if (zcs->params.fParams.checksumFlag)
|
||||
XXH64_update(&zcs->xxhState, (const char*)zcs->inBuff.buffer.start + zcs->dictSize, srcSize);
|
||||
|
||||
/* get a new buffer for next input */
|
||||
if (!endFrame) {
|
||||
size_t const newDictSize = MIN(srcSize + zcs->dictSize, zcs->targetDictSize);
|
||||
DEBUGLOG(5, "ZSTDMT_createCompressionJob::endFrame = %u", endFrame);
|
||||
zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize);
|
||||
zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->bufPool);
|
||||
if (zcs->inBuff.buffer.start == NULL) { /* not enough memory to allocate next input buffer */
|
||||
zcs->jobs[jobID].jobCompleted = 1;
|
||||
zcs->nextJobID++;
|
||||
@@ -747,26 +821,20 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi
|
||||
ZSTDMT_releaseAllJobResources(zcs);
|
||||
return ERROR(memory_allocation);
|
||||
}
|
||||
DEBUGLOG(5, "inBuff currently filled to %u", (U32)zcs->inBuff.filled);
|
||||
zcs->inBuff.filled -= srcSize + zcs->dictSize - newDictSize;
|
||||
DEBUGLOG(5, "new job : inBuff filled to %u, with %u dict and %u src",
|
||||
(U32)zcs->inBuff.filled, (U32)newDictSize,
|
||||
(U32)(zcs->inBuff.filled - newDictSize));
|
||||
memmove(zcs->inBuff.buffer.start,
|
||||
(const char*)zcs->jobs[jobID].srcStart + zcs->dictSize + srcSize - newDictSize,
|
||||
zcs->inBuff.filled);
|
||||
DEBUGLOG(5, "new inBuff pre-filled");
|
||||
zcs->dictSize = newDictSize;
|
||||
} else { /* if (endFrame==1) */
|
||||
DEBUGLOG(5, "ZSTDMT_createCompressionJob::endFrame = %u", endFrame);
|
||||
zcs->inBuff.buffer = g_nullBuffer;
|
||||
zcs->inBuff.filled = 0;
|
||||
zcs->dictSize = 0;
|
||||
zcs->frameEnded = 1;
|
||||
if (zcs->nextJobID == 0)
|
||||
if (zcs->nextJobID == 0) {
|
||||
/* single chunk exception : checksum is calculated directly within worker thread */
|
||||
zcs->params.fParams.checksumFlag = 0;
|
||||
}
|
||||
} }
|
||||
|
||||
DEBUGLOG(4, "posting job %u : %u bytes (end:%u) (note : doneJob = %u=>%u)",
|
||||
zcs->nextJobID,
|
||||
@@ -804,11 +872,8 @@ static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsi
|
||||
ZSTDMT_releaseAllJobResources(zcs);
|
||||
return job.cSize;
|
||||
}
|
||||
ZSTDMT_releaseCCtx(zcs->cctxPool, job.cctx);
|
||||
zcs->jobs[wJobID].cctx = NULL;
|
||||
DEBUGLOG(5, "zcs->params.fParams.checksumFlag : %u ", zcs->params.fParams.checksumFlag);
|
||||
if (zcs->params.fParams.checksumFlag) {
|
||||
XXH64_update(&zcs->xxhState, (const char*)job.srcStart + job.dictSize, job.srcSize);
|
||||
if (zcs->frameEnded && (zcs->doneJobID+1 == zcs->nextJobID)) { /* write checksum at end of last section */
|
||||
U32 const checksum = (U32)XXH64_digest(&zcs->xxhState);
|
||||
DEBUGLOG(5, "writing checksum : %08X \n", checksum);
|
||||
@@ -816,9 +881,6 @@ static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsi
|
||||
job.cSize += 4;
|
||||
zcs->jobs[wJobID].cSize += 4;
|
||||
} }
|
||||
ZSTDMT_releaseBuffer(zcs->buffPool, job.src);
|
||||
zcs->jobs[wJobID].srcStart = NULL;
|
||||
zcs->jobs[wJobID].src = g_nullBuffer;
|
||||
zcs->jobs[wJobID].jobScanned = 1;
|
||||
}
|
||||
{ size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos);
|
||||
@@ -828,7 +890,7 @@ static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsi
|
||||
job.dstFlushed += toWrite;
|
||||
}
|
||||
if (job.dstFlushed == job.cSize) { /* output buffer fully flushed => move to next one */
|
||||
ZSTDMT_releaseBuffer(zcs->buffPool, job.dstBuff);
|
||||
ZSTDMT_releaseBuffer(zcs->bufPool, job.dstBuff);
|
||||
zcs->jobs[wJobID].dstBuff = g_nullBuffer;
|
||||
zcs->jobs[wJobID].jobCompleted = 0;
|
||||
zcs->doneJobID++;
|
||||
@@ -852,18 +914,18 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
|
||||
ZSTD_inBuffer* input,
|
||||
ZSTD_EndDirective endOp)
|
||||
{
|
||||
size_t const newJobThreshold = mtctx->dictSize + mtctx->targetSectionSize + mtctx->marginSize;
|
||||
size_t const newJobThreshold = mtctx->dictSize + mtctx->targetSectionSize;
|
||||
assert(output->pos <= output->size);
|
||||
assert(input->pos <= input->size);
|
||||
if ((mtctx->frameEnded) && (endOp==ZSTD_e_continue)) {
|
||||
/* current frame being ended. Only flush/end are allowed. Or start new frame with init */
|
||||
return ERROR(stage_wrong);
|
||||
}
|
||||
if (mtctx->nbThreads==1) {
|
||||
if (mtctx->nbThreads==1) { /* delegate to single-thread (synchronous) */
|
||||
return ZSTD_compressStream_generic(mtctx->cctxPool->cctx[0], output, input, endOp);
|
||||
}
|
||||
|
||||
/* single-pass shortcut (note : this is blocking-mode) */
|
||||
/* single-pass shortcut (note : this is synchronous-mode) */
|
||||
if ( (mtctx->nextJobID==0) /* just started */
|
||||
&& (mtctx->inBuff.filled==0) /* nothing buffered */
|
||||
&& (endOp==ZSTD_e_end) /* end order */
|
||||
@@ -871,24 +933,29 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
|
||||
size_t const cSize = ZSTDMT_compress_advanced(mtctx,
|
||||
(char*)output->dst + output->pos, output->size - output->pos,
|
||||
(const char*)input->src + input->pos, input->size - input->pos,
|
||||
mtctx->cdict, mtctx->params, mtctx->overlapRLog);
|
||||
mtctx->cdict, mtctx->params, mtctx->overlapLog);
|
||||
if (ZSTD_isError(cSize)) return cSize;
|
||||
input->pos = input->size;
|
||||
output->pos += cSize;
|
||||
ZSTDMT_releaseBuffer(mtctx->buffPool, mtctx->inBuff.buffer); /* was allocated in initStream */
|
||||
ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->inBuff.buffer); /* was allocated in initStream */
|
||||
mtctx->allJobsCompleted = 1;
|
||||
mtctx->frameEnded = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* fill input buffer */
|
||||
if ((input->src) && (mtctx->inBuff.buffer.start)) { /* support NULL input */
|
||||
size_t const toLoad = MIN(input->size - input->pos, mtctx->inBuffSize - mtctx->inBuff.filled);
|
||||
DEBUGLOG(2, "inBuff:%08X; inBuffSize=%u; ToCopy=%u", (U32)(size_t)mtctx->inBuff.buffer.start, (U32)mtctx->inBuffSize, (U32)toLoad);
|
||||
memcpy((char*)mtctx->inBuff.buffer.start + mtctx->inBuff.filled, (const char*)input->src + input->pos, toLoad);
|
||||
input->pos += toLoad;
|
||||
mtctx->inBuff.filled += toLoad;
|
||||
}
|
||||
if (input->size > input->pos) { /* support NULL input */
|
||||
if (mtctx->inBuff.buffer.start == NULL) {
|
||||
mtctx->inBuff.buffer = ZSTDMT_getBuffer(mtctx->bufPool);
|
||||
if (mtctx->inBuff.buffer.start == NULL) return ERROR(memory_allocation);
|
||||
mtctx->inBuff.filled = 0;
|
||||
}
|
||||
{ size_t const toLoad = MIN(input->size - input->pos, mtctx->inBuffSize - mtctx->inBuff.filled);
|
||||
DEBUGLOG(5, "inBuff:%08X; inBuffSize=%u; ToCopy=%u", (U32)(size_t)mtctx->inBuff.buffer.start, (U32)mtctx->inBuffSize, (U32)toLoad);
|
||||
memcpy((char*)mtctx->inBuff.buffer.start + mtctx->inBuff.filled, (const char*)input->src + input->pos, toLoad);
|
||||
input->pos += toLoad;
|
||||
mtctx->inBuff.filled += toLoad;
|
||||
} }
|
||||
|
||||
if ( (mtctx->inBuff.filled >= newJobThreshold) /* filled enough : let's compress */
|
||||
&& (mtctx->nextJobID <= mtctx->doneJobID + mtctx->jobIDMask) ) { /* avoid overwriting job round buffer */
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
#ifndef ZSTDMT_COMPRESS_H
|
||||
@@ -15,10 +15,11 @@
|
||||
#endif
|
||||
|
||||
|
||||
/* Note : All prototypes defined in this file are labelled experimental.
|
||||
* No guarantee of API continuity is provided on any of them.
|
||||
* In fact, the expectation is that these prototypes will be replaced
|
||||
* by ZSTD_compress_generic() API in the near future */
|
||||
/* Note : This is an internal API.
|
||||
* Some methods are still exposed (ZSTDLIB_API),
|
||||
* because it used to be the only way to invoke MT compression.
|
||||
* Now, it's recommended to use ZSTD_compress_generic() instead.
|
||||
* These methods will stop being exposed in a future version */
|
||||
|
||||
/* === Dependencies === */
|
||||
#include <stddef.h> /* size_t */
|
||||
@@ -67,7 +68,7 @@ ZSTDLIB_API size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx,
|
||||
const void* src, size_t srcSize,
|
||||
const ZSTD_CDict* cdict,
|
||||
ZSTD_parameters const params,
|
||||
unsigned overlapRLog);
|
||||
unsigned overlapLog);
|
||||
|
||||
ZSTDLIB_API size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* mtctx,
|
||||
const void* dict, size_t dictSize, /* dict can be released after init, a local copy is preserved within zcs */
|
||||
|
||||
@@ -32,38 +32,22 @@
|
||||
- Public forum : https://groups.google.com/forum/#!forum/lz4c
|
||||
****************************************************************** */
|
||||
|
||||
/* **************************************************************
|
||||
* Compiler specifics
|
||||
****************************************************************/
|
||||
#ifdef _MSC_VER /* Visual Studio */
|
||||
# define FORCE_INLINE static __forceinline
|
||||
# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
|
||||
#else
|
||||
# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
|
||||
# ifdef __GNUC__
|
||||
# define FORCE_INLINE static inline __attribute__((always_inline))
|
||||
# else
|
||||
# define FORCE_INLINE static inline
|
||||
# endif
|
||||
# else
|
||||
# define FORCE_INLINE static
|
||||
# endif /* __STDC_VERSION__ */
|
||||
#endif
|
||||
|
||||
|
||||
/* **************************************************************
|
||||
* Dependencies
|
||||
****************************************************************/
|
||||
#include <string.h> /* memcpy, memset */
|
||||
#include "bitstream.h" /* BIT_* */
|
||||
#include "compiler.h"
|
||||
#include "fse.h" /* header compression */
|
||||
#define HUF_STATIC_LINKING_ONLY
|
||||
#include "huf.h"
|
||||
#include "error_private.h"
|
||||
|
||||
|
||||
/* **************************************************************
|
||||
* Error Management
|
||||
****************************************************************/
|
||||
#define HUF_isError ERR_isError
|
||||
#define HUF_STATIC_ASSERT(c) { enum { HUF_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */
|
||||
|
||||
|
||||
@@ -180,7 +164,7 @@ static BYTE HUF_decodeSymbolX2(BIT_DStream_t* Dstream, const HUF_DEltX2* dt, con
|
||||
if (MEM_64bits()) \
|
||||
HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr)
|
||||
|
||||
FORCE_INLINE size_t HUF_decodeStreamX2(BYTE* p, BIT_DStream_t* const bitDPtr, BYTE* const pEnd, const HUF_DEltX2* const dt, const U32 dtLog)
|
||||
HINT_INLINE size_t HUF_decodeStreamX2(BYTE* p, BIT_DStream_t* const bitDPtr, BYTE* const pEnd, const HUF_DEltX2* const dt, const U32 dtLog)
|
||||
{
|
||||
BYTE* const pStart = p;
|
||||
|
||||
@@ -639,7 +623,7 @@ static U32 HUF_decodeLastSymbolX4(void* op, BIT_DStream_t* DStream, const HUF_DE
|
||||
if (MEM_64bits()) \
|
||||
ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog)
|
||||
|
||||
FORCE_INLINE size_t HUF_decodeStreamX4(BYTE* p, BIT_DStream_t* bitDPtr, BYTE* const pEnd, const HUF_DEltX4* const dt, const U32 dtLog)
|
||||
HINT_INLINE size_t HUF_decodeStreamX4(BYTE* p, BIT_DStream_t* bitDPtr, BYTE* const pEnd, const HUF_DEltX4* const dt, const U32 dtLog)
|
||||
{
|
||||
BYTE* const pStart = p;
|
||||
|
||||
@@ -917,11 +901,11 @@ static const algo_time_t algoTime[16 /* Quantization */][3 /* single, double, qu
|
||||
* Tells which decoder is likely to decode faster,
|
||||
* based on a set of pre-determined metrics.
|
||||
* @return : 0==HUF_decompress4X2, 1==HUF_decompress4X4 .
|
||||
* Assumption : 0 < cSrcSize < dstSize <= 128 KB */
|
||||
* Assumption : 0 < cSrcSize, dstSize <= 128 KB */
|
||||
U32 HUF_selectDecoder (size_t dstSize, size_t cSrcSize)
|
||||
{
|
||||
/* decoder timing evaluation */
|
||||
U32 const Q = (U32)(cSrcSize * 16 / dstSize); /* Q < 16 since dstSize > cSrcSize */
|
||||
U32 const Q = cSrcSize >= dstSize ? 15 : (U32)(cSrcSize * 16 / dstSize); /* Q < 16 */
|
||||
U32 const D256 = (U32)(dstSize >> 8);
|
||||
U32 const DTime0 = algoTime[Q][0].tableTime + (algoTime[Q][0].decode256Time * D256);
|
||||
U32 DTime1 = algoTime[Q][1].tableTime + (algoTime[Q][1].decode256Time * D256);
|
||||
@@ -977,7 +961,7 @@ size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable* dctx, void* dst,
|
||||
{
|
||||
/* validation checks */
|
||||
if (dstSize == 0) return ERROR(dstSize_tooSmall);
|
||||
if ((cSrcSize >= dstSize) || (cSrcSize <= 1)) return ERROR(corruption_detected); /* invalid */
|
||||
if (cSrcSize == 0) return ERROR(corruption_detected);
|
||||
|
||||
{ U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize);
|
||||
return algoNb ? HUF_decompress4X4_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize):
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
@@ -53,15 +53,6 @@
|
||||
# include "zstd_legacy.h"
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined(_M_IA64) /* _mm_prefetch() is not defined for ia64 */
|
||||
# include <mmintrin.h> /* https://msdn.microsoft.com/fr-fr/library/84szxsww(v=vs.90).aspx */
|
||||
# define ZSTD_PREFETCH(ptr) _mm_prefetch((const char*)ptr, _MM_HINT_T0)
|
||||
#elif defined(__GNUC__)
|
||||
# define ZSTD_PREFETCH(ptr) __builtin_prefetch(ptr, 0, 0)
|
||||
#else
|
||||
# define ZSTD_PREFETCH(ptr) /* disabled */
|
||||
#endif
|
||||
|
||||
|
||||
/*-*************************************
|
||||
* Errors
|
||||
@@ -95,7 +86,7 @@ typedef struct {
|
||||
HUF_DTable hufTable[HUF_DTABLE_SIZE(HufLog)]; /* can accommodate HUF_decompress4X */
|
||||
U32 workspace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
|
||||
U32 rep[ZSTD_REP_NUM];
|
||||
} ZSTD_entropyTables_t;
|
||||
} ZSTD_entropyDTables_t;
|
||||
|
||||
struct ZSTD_DCtx_s
|
||||
{
|
||||
@@ -103,7 +94,7 @@ struct ZSTD_DCtx_s
|
||||
const FSE_DTable* MLTptr;
|
||||
const FSE_DTable* OFTptr;
|
||||
const HUF_DTable* HUFptr;
|
||||
ZSTD_entropyTables_t entropy;
|
||||
ZSTD_entropyDTables_t entropy;
|
||||
const void* previousDstEnd; /* detect continuity */
|
||||
const void* base; /* start of current segment */
|
||||
const void* vBase; /* virtual start of previous segment if it was just before current one */
|
||||
@@ -304,15 +295,18 @@ size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t src
|
||||
return ZSTD_skippableHeaderSize; /* magic number + frame length */
|
||||
memset(zfhPtr, 0, sizeof(*zfhPtr));
|
||||
zfhPtr->frameContentSize = MEM_readLE32((const char *)src + 4);
|
||||
zfhPtr->windowSize = 0; /* windowSize==0 means a frame is skippable */
|
||||
zfhPtr->frameType = ZSTD_skippableFrame;
|
||||
zfhPtr->windowSize = 0;
|
||||
return 0;
|
||||
}
|
||||
return ERROR(prefix_unknown);
|
||||
}
|
||||
|
||||
/* ensure there is enough `srcSize` to fully read/decode frame header */
|
||||
{ size_t const fhsize = ZSTD_frameHeaderSize(src, srcSize);
|
||||
if (srcSize < fhsize) return fhsize; }
|
||||
{ size_t const fhsize = ZSTD_frameHeaderSize(src, srcSize);
|
||||
if (srcSize < fhsize) return fhsize;
|
||||
zfhPtr->headerSize = (U32)fhsize;
|
||||
}
|
||||
|
||||
{ BYTE const fhdByte = ip[4];
|
||||
size_t pos = 5;
|
||||
@@ -320,24 +314,23 @@ size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t src
|
||||
U32 const checksumFlag = (fhdByte>>2)&1;
|
||||
U32 const singleSegment = (fhdByte>>5)&1;
|
||||
U32 const fcsID = fhdByte>>6;
|
||||
U32 const windowSizeMax = 1U << ZSTD_WINDOWLOG_MAX;
|
||||
U32 windowSize = 0;
|
||||
U64 windowSize = 0;
|
||||
U32 dictID = 0;
|
||||
U64 frameContentSize = 0;
|
||||
U64 frameContentSize = ZSTD_CONTENTSIZE_UNKNOWN;
|
||||
if ((fhdByte & 0x08) != 0)
|
||||
return ERROR(frameParameter_unsupported); /* reserved bits, must be zero */
|
||||
return ERROR(frameParameter_unsupported); /* reserved bits, must be zero */
|
||||
|
||||
if (!singleSegment) {
|
||||
BYTE const wlByte = ip[pos++];
|
||||
U32 const windowLog = (wlByte >> 3) + ZSTD_WINDOWLOG_ABSOLUTEMIN;
|
||||
if (windowLog > ZSTD_WINDOWLOG_MAX)
|
||||
return ERROR(frameParameter_windowTooLarge);
|
||||
windowSize = (1U << windowLog);
|
||||
windowSize = (1ULL << windowLog);
|
||||
windowSize += (windowSize >> 3) * (wlByte&7);
|
||||
}
|
||||
|
||||
switch(dictIDSizeCode)
|
||||
{
|
||||
default: /* impossible */
|
||||
default: assert(0); /* impossible */
|
||||
case 0 : break;
|
||||
case 1 : dictID = ip[pos]; pos++; break;
|
||||
case 2 : dictID = MEM_readLE16(ip+pos); pos+=2; break;
|
||||
@@ -345,14 +338,15 @@ size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t src
|
||||
}
|
||||
switch(fcsID)
|
||||
{
|
||||
default: /* impossible */
|
||||
default: assert(0); /* impossible */
|
||||
case 0 : if (singleSegment) frameContentSize = ip[pos]; break;
|
||||
case 1 : frameContentSize = MEM_readLE16(ip+pos)+256; break;
|
||||
case 2 : frameContentSize = MEM_readLE32(ip+pos); break;
|
||||
case 3 : frameContentSize = MEM_readLE64(ip+pos); break;
|
||||
}
|
||||
if (!windowSize) windowSize = (U32)frameContentSize;
|
||||
if (windowSize > windowSizeMax) return ERROR(frameParameter_windowTooLarge);
|
||||
if (singleSegment) windowSize = frameContentSize;
|
||||
|
||||
zfhPtr->frameType = ZSTD_frame;
|
||||
zfhPtr->frameContentSize = frameContentSize;
|
||||
zfhPtr->windowSize = windowSize;
|
||||
zfhPtr->dictID = dictID;
|
||||
@@ -362,10 +356,10 @@ size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t src
|
||||
}
|
||||
|
||||
/** ZSTD_getFrameContentSize() :
|
||||
* compatible with legacy mode
|
||||
* @return : decompressed size of the single frame pointed to be `src` if known, otherwise
|
||||
* - ZSTD_CONTENTSIZE_UNKNOWN if the size cannot be determined
|
||||
* - ZSTD_CONTENTSIZE_ERROR if an error occurred (e.g. invalid magic number, srcSize too small) */
|
||||
* compatible with legacy mode
|
||||
* @return : decompressed size of the single frame pointed to be `src` if known, otherwise
|
||||
* - ZSTD_CONTENTSIZE_UNKNOWN if the size cannot be determined
|
||||
* - ZSTD_CONTENTSIZE_ERROR if an error occurred (e.g. invalid magic number, srcSize too small) */
|
||||
unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize)
|
||||
{
|
||||
#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
|
||||
@@ -374,17 +368,14 @@ unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize)
|
||||
return ret == 0 ? ZSTD_CONTENTSIZE_UNKNOWN : ret;
|
||||
}
|
||||
#endif
|
||||
{ ZSTD_frameHeader fParams;
|
||||
if (ZSTD_getFrameHeader(&fParams, src, srcSize) != 0) return ZSTD_CONTENTSIZE_ERROR;
|
||||
if (fParams.windowSize == 0) {
|
||||
/* Either skippable or empty frame, size == 0 either way */
|
||||
{ ZSTD_frameHeader zfh;
|
||||
if (ZSTD_getFrameHeader(&zfh, src, srcSize) != 0)
|
||||
return ZSTD_CONTENTSIZE_ERROR;
|
||||
if (zfh.frameType == ZSTD_skippableFrame) {
|
||||
return 0;
|
||||
} else if (fParams.frameContentSize != 0) {
|
||||
return fParams.frameContentSize;
|
||||
} else {
|
||||
return ZSTD_CONTENTSIZE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
return zfh.frameContentSize;
|
||||
} }
|
||||
}
|
||||
|
||||
/** ZSTD_findDecompressedSize() :
|
||||
@@ -442,7 +433,8 @@ unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize)
|
||||
* compatible with legacy mode
|
||||
* @return : decompressed size if known, 0 otherwise
|
||||
note : 0 can mean any of the following :
|
||||
- decompressed size is not present within frame header
|
||||
- frame content is empty
|
||||
- decompressed size field is not present in frame header
|
||||
- frame header unknown / not supported
|
||||
- frame header not complete (`srcSize` too small) */
|
||||
unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize)
|
||||
@@ -460,19 +452,13 @@ static size_t ZSTD_decodeFrameHeader(ZSTD_DCtx* dctx, const void* src, size_t he
|
||||
size_t const result = ZSTD_getFrameHeader(&(dctx->fParams), src, headerSize);
|
||||
if (ZSTD_isError(result)) return result; /* invalid header */
|
||||
if (result>0) return ERROR(srcSize_wrong); /* headerSize too small */
|
||||
if (dctx->fParams.dictID && (dctx->dictID != dctx->fParams.dictID)) return ERROR(dictionary_wrong);
|
||||
if (dctx->fParams.dictID && (dctx->dictID != dctx->fParams.dictID))
|
||||
return ERROR(dictionary_wrong);
|
||||
if (dctx->fParams.checksumFlag) XXH64_reset(&dctx->xxhState, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
blockType_e blockType;
|
||||
U32 lastBlock;
|
||||
U32 origSize;
|
||||
} blockProperties_t;
|
||||
|
||||
/*! ZSTD_getcBlockSize() :
|
||||
* Provides the size of compressed block from block header `src` */
|
||||
size_t ZSTD_getcBlockSize(const void* src, size_t srcSize,
|
||||
@@ -958,7 +944,7 @@ static seq_t ZSTD_decodeSequence(seqState_t* seqState)
|
||||
}
|
||||
|
||||
|
||||
FORCE_INLINE
|
||||
HINT_INLINE
|
||||
size_t ZSTD_execSequence(BYTE* op,
|
||||
BYTE* const oend, seq_t sequence,
|
||||
const BYTE** litPtr, const BYTE* const litLimit,
|
||||
@@ -1102,7 +1088,7 @@ static size_t ZSTD_decompressSequences(
|
||||
}
|
||||
|
||||
|
||||
FORCE_INLINE seq_t ZSTD_decodeSequenceLong_generic(seqState_t* seqState, int const longOffsets)
|
||||
FORCE_INLINE_TEMPLATE seq_t ZSTD_decodeSequenceLong_generic(seqState_t* seqState, int const longOffsets)
|
||||
{
|
||||
seq_t seq;
|
||||
|
||||
@@ -1202,7 +1188,7 @@ static seq_t ZSTD_decodeSequenceLong(seqState_t* seqState, unsigned const window
|
||||
}
|
||||
}
|
||||
|
||||
FORCE_INLINE
|
||||
HINT_INLINE
|
||||
size_t ZSTD_execSequenceLong(BYTE* op,
|
||||
BYTE* const oend, seq_t sequence,
|
||||
const BYTE** litPtr, const BYTE* const litLimit,
|
||||
@@ -1338,7 +1324,7 @@ static size_t ZSTD_decompressSequencesLong(
|
||||
seq_t const sequence = ZSTD_decodeSequenceLong(&seqState, windowSize32);
|
||||
size_t const oneSeqSize = ZSTD_execSequenceLong(op, oend, sequences[(seqNb-ADVANCED_SEQS) & STOSEQ_MASK], &litPtr, litEnd, base, vBase, dictEnd);
|
||||
if (ZSTD_isError(oneSeqSize)) return oneSeqSize;
|
||||
ZSTD_PREFETCH(sequence.match);
|
||||
PREFETCH(sequence.match);
|
||||
sequences[seqNb&STOSEQ_MASK] = sequence;
|
||||
op += oneSeqSize;
|
||||
}
|
||||
@@ -1440,28 +1426,26 @@ size_t ZSTD_generateNxBytes(void* dst, size_t dstCapacity, BYTE byte, size_t len
|
||||
size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize)
|
||||
{
|
||||
#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
|
||||
if (ZSTD_isLegacy(src, srcSize)) return ZSTD_findFrameCompressedSizeLegacy(src, srcSize);
|
||||
if (ZSTD_isLegacy(src, srcSize))
|
||||
return ZSTD_findFrameCompressedSizeLegacy(src, srcSize);
|
||||
#endif
|
||||
if (srcSize >= ZSTD_skippableHeaderSize &&
|
||||
(MEM_readLE32(src) & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) {
|
||||
if ( (srcSize >= ZSTD_skippableHeaderSize)
|
||||
&& (MEM_readLE32(src) & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START ) {
|
||||
return ZSTD_skippableHeaderSize + MEM_readLE32((const BYTE*)src + 4);
|
||||
} else {
|
||||
const BYTE* ip = (const BYTE*)src;
|
||||
const BYTE* const ipstart = ip;
|
||||
size_t remainingSize = srcSize;
|
||||
ZSTD_frameHeader fParams;
|
||||
ZSTD_frameHeader zfh;
|
||||
|
||||
size_t const headerSize = ZSTD_frameHeaderSize(ip, remainingSize);
|
||||
if (ZSTD_isError(headerSize)) return headerSize;
|
||||
|
||||
/* Frame Header */
|
||||
{ size_t const ret = ZSTD_getFrameHeader(&fParams, ip, remainingSize);
|
||||
/* Extract Frame Header */
|
||||
{ size_t const ret = ZSTD_getFrameHeader(&zfh, src, srcSize);
|
||||
if (ZSTD_isError(ret)) return ret;
|
||||
if (ret > 0) return ERROR(srcSize_wrong);
|
||||
}
|
||||
|
||||
ip += headerSize;
|
||||
remainingSize -= headerSize;
|
||||
ip += zfh.headerSize;
|
||||
remainingSize -= zfh.headerSize;
|
||||
|
||||
/* Loop on each block */
|
||||
while (1) {
|
||||
@@ -1469,7 +1453,8 @@ size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize)
|
||||
size_t const cBlockSize = ZSTD_getcBlockSize(ip, remainingSize, &blockProperties);
|
||||
if (ZSTD_isError(cBlockSize)) return cBlockSize;
|
||||
|
||||
if (ZSTD_blockHeaderSize + cBlockSize > remainingSize) return ERROR(srcSize_wrong);
|
||||
if (ZSTD_blockHeaderSize + cBlockSize > remainingSize)
|
||||
return ERROR(srcSize_wrong);
|
||||
|
||||
ip += ZSTD_blockHeaderSize + cBlockSize;
|
||||
remainingSize -= ZSTD_blockHeaderSize + cBlockSize;
|
||||
@@ -1477,7 +1462,7 @@ size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize)
|
||||
if (blockProperties.lastBlock) break;
|
||||
}
|
||||
|
||||
if (fParams.checksumFlag) { /* Frame content checksum */
|
||||
if (zfh.checksumFlag) { /* Final frame content checksum */
|
||||
if (remainingSize < 4) return ERROR(srcSize_wrong);
|
||||
ip += 4;
|
||||
remainingSize -= 4;
|
||||
@@ -1490,8 +1475,8 @@ size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize)
|
||||
/*! ZSTD_decompressFrame() :
|
||||
* @dctx must be properly initialized */
|
||||
static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx,
|
||||
void* dst, size_t dstCapacity,
|
||||
const void** srcPtr, size_t *srcSizePtr)
|
||||
void* dst, size_t dstCapacity,
|
||||
const void** srcPtr, size_t *srcSizePtr)
|
||||
{
|
||||
const BYTE* ip = (const BYTE*)(*srcPtr);
|
||||
BYTE* const ostart = (BYTE* const)dst;
|
||||
@@ -1500,13 +1485,15 @@ static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx,
|
||||
size_t remainingSize = *srcSizePtr;
|
||||
|
||||
/* check */
|
||||
if (remainingSize < ZSTD_frameHeaderSize_min+ZSTD_blockHeaderSize) return ERROR(srcSize_wrong);
|
||||
if (remainingSize < ZSTD_frameHeaderSize_min+ZSTD_blockHeaderSize)
|
||||
return ERROR(srcSize_wrong);
|
||||
|
||||
/* Frame Header */
|
||||
{ size_t const frameHeaderSize = ZSTD_frameHeaderSize(ip, ZSTD_frameHeaderSize_prefix);
|
||||
if (ZSTD_isError(frameHeaderSize)) return frameHeaderSize;
|
||||
if (remainingSize < frameHeaderSize+ZSTD_blockHeaderSize) return ERROR(srcSize_wrong);
|
||||
CHECK_F(ZSTD_decodeFrameHeader(dctx, ip, frameHeaderSize));
|
||||
if (remainingSize < frameHeaderSize+ZSTD_blockHeaderSize)
|
||||
return ERROR(srcSize_wrong);
|
||||
CHECK_F( ZSTD_decodeFrameHeader(dctx, ip, frameHeaderSize) );
|
||||
ip += frameHeaderSize; remainingSize -= frameHeaderSize;
|
||||
}
|
||||
|
||||
@@ -1538,14 +1525,15 @@ static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx,
|
||||
}
|
||||
|
||||
if (ZSTD_isError(decodedSize)) return decodedSize;
|
||||
if (dctx->fParams.checksumFlag) XXH64_update(&dctx->xxhState, op, decodedSize);
|
||||
if (dctx->fParams.checksumFlag)
|
||||
XXH64_update(&dctx->xxhState, op, decodedSize);
|
||||
op += decodedSize;
|
||||
ip += cBlockSize;
|
||||
remainingSize -= cBlockSize;
|
||||
if (blockProperties.lastBlock) break;
|
||||
}
|
||||
|
||||
if (dctx->fParams.checksumFlag) { /* Frame content checksum verification */
|
||||
if (dctx->fParams.checksumFlag) { /* Frame content checksum verification */
|
||||
U32 const checkCalc = (U32)XXH64_digest(&dctx->xxhState);
|
||||
U32 checkRead;
|
||||
if (remainingSize<4) return ERROR(checksum_wrong);
|
||||
@@ -1567,17 +1555,13 @@ static size_t ZSTD_DDictDictSize(const ZSTD_DDict* ddict);
|
||||
static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx,
|
||||
void* dst, size_t dstCapacity,
|
||||
const void* src, size_t srcSize,
|
||||
const void *dict, size_t dictSize,
|
||||
const void* dict, size_t dictSize,
|
||||
const ZSTD_DDict* ddict)
|
||||
{
|
||||
void* const dststart = dst;
|
||||
assert(dict==NULL || ddict==NULL); /* either dict or ddict set, not both */
|
||||
|
||||
if (ddict) {
|
||||
if (dict) {
|
||||
/* programmer error, these two cases should be mutually exclusive */
|
||||
return ERROR(GENERIC);
|
||||
}
|
||||
|
||||
dict = ZSTD_DDictDictContent(ddict);
|
||||
dictSize = ZSTD_DDictDictSize(ddict);
|
||||
}
|
||||
@@ -1590,7 +1574,7 @@ static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx,
|
||||
size_t decodedSize;
|
||||
size_t const frameSize = ZSTD_findFrameCompressedSizeLegacy(src, srcSize);
|
||||
if (ZSTD_isError(frameSize)) return frameSize;
|
||||
/* legacy support is incompatible with static dctx */
|
||||
/* legacy support is not compatible with static dctx */
|
||||
if (dctx->staticSize) return ERROR(memory_allocation);
|
||||
|
||||
decodedSize = ZSTD_decompressLegacy(dst, dstCapacity, src, frameSize, dict, dictSize);
|
||||
@@ -1613,16 +1597,13 @@ static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx,
|
||||
return ERROR(srcSize_wrong);
|
||||
skippableSize = MEM_readLE32((const BYTE *)src + 4) +
|
||||
ZSTD_skippableHeaderSize;
|
||||
if (srcSize < skippableSize) {
|
||||
return ERROR(srcSize_wrong);
|
||||
}
|
||||
if (srcSize < skippableSize) return ERROR(srcSize_wrong);
|
||||
|
||||
src = (const BYTE *)src + skippableSize;
|
||||
srcSize -= skippableSize;
|
||||
continue;
|
||||
} else {
|
||||
return ERROR(prefix_unknown);
|
||||
}
|
||||
return ERROR(prefix_unknown);
|
||||
}
|
||||
|
||||
if (ddict) {
|
||||
@@ -1638,12 +1619,11 @@ static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx,
|
||||
{ const size_t res = ZSTD_decompressFrame(dctx, dst, dstCapacity,
|
||||
&src, &srcSize);
|
||||
if (ZSTD_isError(res)) return res;
|
||||
/* don't need to bounds check this, ZSTD_decompressFrame will have
|
||||
* already */
|
||||
/* no need to bound check, ZSTD_decompressFrame already has */
|
||||
dst = (BYTE*)dst + res;
|
||||
dstCapacity -= res;
|
||||
}
|
||||
}
|
||||
} /* while (srcSize >= ZSTD_frameHeaderSize_prefix) */
|
||||
|
||||
if (srcSize) return ERROR(srcSize_wrong); /* input not entirely consumed */
|
||||
|
||||
@@ -1742,7 +1722,7 @@ size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, c
|
||||
return 0;
|
||||
}
|
||||
dctx->expected = 0; /* not necessary to copy more */
|
||||
|
||||
/* fall-through */
|
||||
case ZSTDds_decodeFrameHeader:
|
||||
assert(src != NULL);
|
||||
memcpy(dctx->headerBuffer + ZSTD_frameHeaderSize_prefix, src, dctx->expected);
|
||||
@@ -1853,7 +1833,7 @@ static size_t ZSTD_refDictContent(ZSTD_DCtx* dctx, const void* dict, size_t dict
|
||||
/* ZSTD_loadEntropy() :
|
||||
* dict : must point at beginning of a valid zstd dictionary
|
||||
* @return : size of entropy tables read */
|
||||
static size_t ZSTD_loadEntropy(ZSTD_entropyTables_t* entropy, const void* const dict, size_t const dictSize)
|
||||
static size_t ZSTD_loadEntropy(ZSTD_entropyDTables_t* entropy, const void* const dict, size_t const dictSize)
|
||||
{
|
||||
const BYTE* dictPtr = (const BYTE*)dict;
|
||||
const BYTE* const dictEnd = dictPtr + dictSize;
|
||||
@@ -1931,8 +1911,9 @@ static size_t ZSTD_decompress_insertDictionary(ZSTD_DCtx* dctx, const void* dict
|
||||
|
||||
size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx* dctx, const void* dict, size_t dictSize)
|
||||
{
|
||||
CHECK_F(ZSTD_decompressBegin(dctx));
|
||||
if (dict && dictSize) CHECK_E(ZSTD_decompress_insertDictionary(dctx, dict, dictSize), dictionary_corrupted);
|
||||
CHECK_F( ZSTD_decompressBegin(dctx) );
|
||||
if (dict && dictSize)
|
||||
CHECK_E(ZSTD_decompress_insertDictionary(dctx, dict, dictSize), dictionary_corrupted);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1943,7 +1924,7 @@ struct ZSTD_DDict_s {
|
||||
void* dictBuffer;
|
||||
const void* dictContent;
|
||||
size_t dictSize;
|
||||
ZSTD_entropyTables_t entropy;
|
||||
ZSTD_entropyDTables_t entropy;
|
||||
U32 dictID;
|
||||
U32 entropyPresent;
|
||||
ZSTD_customMem cMem;
|
||||
@@ -1961,7 +1942,7 @@ static size_t ZSTD_DDictDictSize(const ZSTD_DDict* ddict)
|
||||
|
||||
size_t ZSTD_decompressBegin_usingDDict(ZSTD_DCtx* dstDCtx, const ZSTD_DDict* ddict)
|
||||
{
|
||||
CHECK_F(ZSTD_decompressBegin(dstDCtx));
|
||||
CHECK_F( ZSTD_decompressBegin(dstDCtx) );
|
||||
if (ddict) { /* support begin on NULL */
|
||||
dstDCtx->dictID = ddict->dictID;
|
||||
dstDCtx->base = ddict->dictContent;
|
||||
@@ -2142,7 +2123,7 @@ unsigned ZSTD_getDictID_fromDDict(const ZSTD_DDict* ddict)
|
||||
* ZSTD_getFrameHeader(), which will provide a more precise error code. */
|
||||
unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize)
|
||||
{
|
||||
ZSTD_frameHeader zfp = { 0 , 0 , 0 , 0 };
|
||||
ZSTD_frameHeader zfp = { 0, 0, ZSTD_frame, 0, 0, 0 };
|
||||
size_t const hError = ZSTD_getFrameHeader(&zfp, src, srcSize);
|
||||
if (ZSTD_isError(hError)) return 0;
|
||||
return zfp.dictID;
|
||||
@@ -2237,7 +2218,7 @@ size_t ZSTD_setDStreamParameter(ZSTD_DStream* zds,
|
||||
{
|
||||
switch(paramType)
|
||||
{
|
||||
default : return ERROR(parameter_unknown);
|
||||
default : return ERROR(parameter_unsupported);
|
||||
case DStream_p_maxWindowSize : zds->maxWindowSize = paramValue ? paramValue : (U32)(-1); break;
|
||||
}
|
||||
return 0;
|
||||
@@ -2254,16 +2235,19 @@ size_t ZSTD_estimateDStreamSize(size_t windowSize)
|
||||
size_t const blockSize = MIN(windowSize, ZSTD_BLOCKSIZE_MAX);
|
||||
size_t const inBuffSize = blockSize; /* no block can be larger */
|
||||
size_t const outBuffSize = windowSize + blockSize + (WILDCOPY_OVERLENGTH * 2);
|
||||
return sizeof(ZSTD_DStream) + ZSTD_estimateDCtxSize() + inBuffSize + outBuffSize;
|
||||
return ZSTD_estimateDCtxSize() + inBuffSize + outBuffSize;
|
||||
}
|
||||
|
||||
ZSTDLIB_API size_t ZSTD_estimateDStreamSize_fromFrame(const void* src, size_t srcSize)
|
||||
{
|
||||
ZSTD_frameHeader fh;
|
||||
size_t const err = ZSTD_getFrameHeader(&fh, src, srcSize);
|
||||
U32 const windowSizeMax = 1U << ZSTD_WINDOWLOG_MAX;
|
||||
ZSTD_frameHeader zfh;
|
||||
size_t const err = ZSTD_getFrameHeader(&zfh, src, srcSize);
|
||||
if (ZSTD_isError(err)) return err;
|
||||
if (err>0) return ERROR(srcSize_wrong);
|
||||
return ZSTD_estimateDStreamSize(fh.windowSize);
|
||||
if (zfh.windowSize > windowSizeMax)
|
||||
return ERROR(frameParameter_windowTooLarge);
|
||||
return ZSTD_estimateDStreamSize((size_t)zfh.windowSize);
|
||||
}
|
||||
|
||||
|
||||
@@ -2314,16 +2298,14 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
|
||||
size_t const dictSize = zds->ddict ? zds->ddict->dictSize : 0;
|
||||
/* legacy support is incompatible with static dctx */
|
||||
if (zds->staticSize) return ERROR(memory_allocation);
|
||||
CHECK_F(ZSTD_initLegacyStream(&zds->legacyContext, zds->previousLegacyVersion, legacyVersion,
|
||||
dict, dictSize));
|
||||
CHECK_F(ZSTD_initLegacyStream(&zds->legacyContext,
|
||||
zds->previousLegacyVersion, legacyVersion,
|
||||
dict, dictSize));
|
||||
zds->legacyVersion = zds->previousLegacyVersion = legacyVersion;
|
||||
return ZSTD_decompressLegacyStream(zds->legacyContext, zds->legacyVersion, output, input);
|
||||
} else {
|
||||
return hSize; /* error */
|
||||
return ZSTD_decompressLegacyStream(zds->legacyContext, legacyVersion, output, input);
|
||||
}
|
||||
#else
|
||||
return hSize;
|
||||
#endif
|
||||
return hSize; /* error */
|
||||
}
|
||||
if (hSize != 0) { /* need more input */
|
||||
size_t const toLoad = hSize - zds->lhSize; /* if hSize!=0, hSize > zds->lhSize */
|
||||
@@ -2374,8 +2356,8 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
|
||||
if (zds->fParams.windowSize > zds->maxWindowSize) return ERROR(frameParameter_windowTooLarge);
|
||||
|
||||
/* Adapt buffer sizes to frame header instructions */
|
||||
{ size_t const blockSize = MIN(zds->fParams.windowSize, ZSTD_BLOCKSIZE_MAX);
|
||||
size_t const neededOutSize = zds->fParams.windowSize + blockSize + WILDCOPY_OVERLENGTH * 2;
|
||||
{ size_t const blockSize = (size_t)(MIN(zds->fParams.windowSize, ZSTD_BLOCKSIZE_MAX));
|
||||
size_t const neededOutSize = (size_t)(zds->fParams.windowSize + blockSize + WILDCOPY_OVERLENGTH * 2);
|
||||
zds->blockSize = blockSize;
|
||||
if ((zds->inBuffSize < blockSize) || (zds->outBuffSize < neededOutSize)) {
|
||||
size_t const bufferSize = blockSize + neededOutSize;
|
||||
@@ -2400,7 +2382,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
|
||||
zds->outBuffSize = neededOutSize;
|
||||
} }
|
||||
zds->streamStage = zdss_read;
|
||||
/* pass-through */
|
||||
/* fall-through */
|
||||
|
||||
case zdss_read:
|
||||
DEBUGLOG(5, "stage zdss_read");
|
||||
@@ -2425,8 +2407,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
|
||||
} }
|
||||
if (ip==iend) { someMoreWork = 0; break; } /* no more input */
|
||||
zds->streamStage = zdss_load;
|
||||
/* pass-through */
|
||||
|
||||
/* fall-through */
|
||||
case zdss_load:
|
||||
{ size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zds);
|
||||
size_t const toLoad = neededInSize - zds->inPos; /* should always be <= remaining space within inBuff */
|
||||
@@ -2448,8 +2429,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
|
||||
zds->outEnd = zds->outStart + decodedSize;
|
||||
} }
|
||||
zds->streamStage = zdss_flush;
|
||||
/* pass-through */
|
||||
|
||||
/* fall-through */
|
||||
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);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
/* ***************************************************************
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
/*-*************************************
|
||||
@@ -23,4 +23,3 @@ unsigned ZBUFF_isError(size_t errorCode) { return ERR_isError(errorCode); }
|
||||
/*! ZBUFF_getErrorName() :
|
||||
* provides error code string from function result (useful for debugging) */
|
||||
const char* ZBUFF_getErrorName(size_t errorCode) { return ERR_getErrorName(errorCode); }
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
/* *****************************************************************************
|
||||
@@ -714,11 +714,9 @@ typedef struct COVER_best_s {
|
||||
* Initialize the `COVER_best_t`.
|
||||
*/
|
||||
static void COVER_best_init(COVER_best_t *best) {
|
||||
if (!best) {
|
||||
return;
|
||||
}
|
||||
pthread_mutex_init(&best->mutex, NULL);
|
||||
pthread_cond_init(&best->cond, NULL);
|
||||
if (best==NULL) return; /* compatible with init on NULL */
|
||||
(void)pthread_mutex_init(&best->mutex, NULL);
|
||||
(void)pthread_cond_init(&best->cond, NULL);
|
||||
best->liveJobs = 0;
|
||||
best->dict = NULL;
|
||||
best->dictSize = 0;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
@@ -695,7 +695,7 @@ static size_t ZDICT_analyzeEntropy(void* dstBuffer, size_t maxDstSize,
|
||||
DISPLAYLEVEL(1, "Not enough memory \n");
|
||||
goto _cleanup;
|
||||
}
|
||||
if (offcodeMax>OFFCODE_MAX) { eSize = ERROR(dictionary_wrong); goto _cleanup; } /* too large dictionary */
|
||||
if (offcodeMax>OFFCODE_MAX) { eSize = ERROR(dictionaryCreation_failed); goto _cleanup; } /* too large dictionary */
|
||||
for (u=0; u<256; u++) countLit[u] = 1; /* any character must be described */
|
||||
for (u=0; u<=offcodeMax; u++) offcodeCount[u] = 1;
|
||||
for (u=0; u<=MaxML; u++) matchLengthCount[u] = 1;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
#ifndef DICTBUILDER_H_001
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
#ifndef ZSTD_LEGACY_H
|
||||
@@ -123,6 +123,7 @@ MEM_STATIC size_t ZSTD_decompressLegacy(
|
||||
const void* dict,size_t dictSize)
|
||||
{
|
||||
U32 const version = ZSTD_isLegacy(src, compressedSize);
|
||||
(void)dst; (void)dstCapacity; (void)dict; (void)dictSize; /* unused when ZSTD_LEGACY_SUPPORT >= 8 */
|
||||
switch(version)
|
||||
{
|
||||
#if (ZSTD_LEGACY_SUPPORT <= 1)
|
||||
@@ -223,6 +224,7 @@ MEM_STATIC size_t ZSTD_freeLegacyStreamContext(void* legacyContext, U32 version)
|
||||
case 1 :
|
||||
case 2 :
|
||||
case 3 :
|
||||
(void)legacyContext;
|
||||
return ERROR(version_unsupported);
|
||||
#if (ZSTD_LEGACY_SUPPORT <= 4)
|
||||
case 4 : return ZBUFFv04_freeDCtx((ZBUFFv04_DCtx*)legacyContext);
|
||||
@@ -250,6 +252,7 @@ MEM_STATIC size_t ZSTD_initLegacyStream(void** legacyContext, U32 prevVersion, U
|
||||
case 1 :
|
||||
case 2 :
|
||||
case 3 :
|
||||
(void)dict; (void)dictSize;
|
||||
return 0;
|
||||
#if (ZSTD_LEGACY_SUPPORT <= 4)
|
||||
case 4 :
|
||||
@@ -306,6 +309,7 @@ MEM_STATIC size_t ZSTD_decompressLegacyStream(void* legacyContext, U32 version,
|
||||
case 1 :
|
||||
case 2 :
|
||||
case 3 :
|
||||
(void)legacyContext; (void)output; (void)input;
|
||||
return ERROR(version_unsupported);
|
||||
#if (ZSTD_LEGACY_SUPPORT <= 4)
|
||||
case 4 :
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
#ifndef ZSTD_V01_H_28739879432
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
#ifndef ZSTD_V02_H_4174539423
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
#ifndef ZSTD_V03_H_298734209782
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
@@ -2776,7 +2776,7 @@ static size_t ZSTD_decodeFrameHeader_Part2(ZSTD_DCtx* zc, const void* src, size_
|
||||
size_t result;
|
||||
if (srcSize != zc->headerSize) return ERROR(srcSize_wrong);
|
||||
result = ZSTD_getFrameParams(&(zc->params), src, srcSize);
|
||||
if ((MEM_32bits()) && (zc->params.windowLog > 25)) return ERROR(frameParameter_unsupportedBy32bits);
|
||||
if ((MEM_32bits()) && (zc->params.windowLog > 25)) return ERROR(frameParameter_unsupported);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
#ifndef ZSTD_V04_H_91868324769238
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
@@ -2888,7 +2888,7 @@ static size_t ZSTDv05_decodeFrameHeader_Part2(ZSTDv05_DCtx* zc, const void* src,
|
||||
if (srcSize != zc->headerSize)
|
||||
return ERROR(srcSize_wrong);
|
||||
result = ZSTDv05_getFrameParams(&(zc->params), src, srcSize);
|
||||
if ((MEM_32bits()) && (zc->params.windowLog > 25)) return ERROR(frameParameter_unsupportedBy32bits);
|
||||
if ((MEM_32bits()) && (zc->params.windowLog > 25)) return ERROR(frameParameter_unsupported);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
#ifndef ZSTDv05_H
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
@@ -3084,7 +3084,7 @@ size_t ZSTDv06_getFrameParams(ZSTDv06_frameParams* fparamsPtr, const void* src,
|
||||
static size_t ZSTDv06_decodeFrameHeader(ZSTDv06_DCtx* zc, const void* src, size_t srcSize)
|
||||
{
|
||||
size_t const result = ZSTDv06_getFrameParams(&(zc->fParams), src, srcSize);
|
||||
if ((MEM_32bits()) && (zc->fParams.windowLog > 25)) return ERROR(frameParameter_unsupportedBy32bits);
|
||||
if ((MEM_32bits()) && (zc->fParams.windowLog > 25)) return ERROR(frameParameter_unsupported);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
#ifndef ZSTDv06_H
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
#ifndef ZSTDv07_H_235446
|
||||
|
||||
+16
-14
@@ -2,11 +2,10 @@
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
#if defined (__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -59,7 +58,7 @@ extern "C" {
|
||||
/*------ Version ------*/
|
||||
#define ZSTD_VERSION_MAJOR 1
|
||||
#define ZSTD_VERSION_MINOR 3
|
||||
#define ZSTD_VERSION_RELEASE 0
|
||||
#define ZSTD_VERSION_RELEASE 1
|
||||
|
||||
#define ZSTD_VERSION_NUMBER (ZSTD_VERSION_MAJOR *100*100 + ZSTD_VERSION_MINOR *100 + ZSTD_VERSION_RELEASE)
|
||||
ZSTDLIB_API unsigned ZSTD_versionNumber(void); /**< useful to check dll version */
|
||||
@@ -425,13 +424,6 @@ typedef struct {
|
||||
ZSTD_frameParameters fParams;
|
||||
} ZSTD_parameters;
|
||||
|
||||
typedef struct {
|
||||
unsigned long long frameContentSize;
|
||||
size_t windowSize;
|
||||
unsigned dictID;
|
||||
unsigned checksumFlag;
|
||||
} ZSTD_frameHeader;
|
||||
|
||||
/*= Custom memory allocation functions */
|
||||
typedef void* (*ZSTD_allocFunction) (void* opaque, size_t size);
|
||||
typedef void (*ZSTD_freeFunction) (void* opaque, void* address);
|
||||
@@ -809,7 +801,6 @@ ZSTDLIB_API size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, void* dst, size_t dstC
|
||||
ZSTDLIB_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
|
||||
|
||||
|
||||
|
||||
/*-
|
||||
Buffer-less streaming decompression (synchronous mode)
|
||||
|
||||
@@ -874,6 +865,15 @@ ZSTDLIB_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapaci
|
||||
*/
|
||||
|
||||
/*===== Buffer-less streaming decompression functions =====*/
|
||||
typedef enum { ZSTD_frame, ZSTD_skippableFrame } ZSTD_frameType_e;
|
||||
typedef struct {
|
||||
unsigned long long frameContentSize; /* ZSTD_CONTENTSIZE_UNKNOWN means this field is not available. 0 means "empty" */
|
||||
unsigned long long windowSize; /* can be very large, up to <= frameContentSize */
|
||||
ZSTD_frameType_e frameType; /* if == ZSTD_skippableFrame, frameContentSize is the size of skippable content */
|
||||
unsigned headerSize;
|
||||
unsigned dictID;
|
||||
unsigned checksumFlag;
|
||||
} ZSTD_frameHeader;
|
||||
ZSTDLIB_API size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize); /**< doesn't consume input */
|
||||
ZSTDLIB_API size_t ZSTD_decompressBegin(ZSTD_DCtx* dctx);
|
||||
ZSTDLIB_API size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);
|
||||
@@ -953,7 +953,9 @@ typedef enum {
|
||||
* Special: value 0 means "do not change strategy". */
|
||||
|
||||
/* frame parameters */
|
||||
ZSTD_p_contentSizeFlag=200, /* Content size is written into frame header _whenever known_ (default:1) */
|
||||
ZSTD_p_contentSizeFlag=200, /* Content size is written into frame header _whenever known_ (default:1)
|
||||
* note that content size must be known at the beginning,
|
||||
* it is sent using ZSTD_CCtx_setPledgedSrcSize() */
|
||||
ZSTD_p_checksumFlag, /* A 32-bits checksum of content is written at end of frame (default:0) */
|
||||
ZSTD_p_dictIDFlag, /* When applicable, dictID of dictionary is provided in frame header (default:1) */
|
||||
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
# local binary (Makefile)
|
||||
zstd
|
||||
zstd32
|
||||
zstd4
|
||||
zstd-compress
|
||||
zstd-decompress
|
||||
zstd-frugal
|
||||
zstd-small
|
||||
zstd-nolegacy
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
|
||||
+27
-12
@@ -39,9 +39,10 @@ endif
|
||||
|
||||
CPPFLAGS+= -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress \
|
||||
-I$(ZSTDDIR)/dictBuilder \
|
||||
-DZSTD_NEWAPI \
|
||||
-DXXH_NAMESPACE=ZSTD_ # because xxhash.o already compiled with this macro from library
|
||||
CFLAGS ?= -O3
|
||||
DEBUGFLAGS = -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \
|
||||
DEBUGFLAGS= -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \
|
||||
-Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement \
|
||||
-Wstrict-prototypes -Wundef -Wpointer-arith -Wformat-security \
|
||||
-Wvla -Wformat=2 -Winit-self -Wfloat-equal -Wwrite-strings \
|
||||
@@ -131,12 +132,15 @@ else
|
||||
LZ4_MSG := $(NO_LZ4_MSG)
|
||||
endif
|
||||
|
||||
.PHONY: default all clean clean_decomp_o install uninstall generate_res
|
||||
|
||||
.PHONY: default
|
||||
default: zstd-release
|
||||
|
||||
.PHONY: all
|
||||
all: zstd
|
||||
|
||||
.PHONY: allVariants
|
||||
allVariants: zstd zstd-compress zstd-decompress zstd-small zstd-nolegacy
|
||||
|
||||
$(ZSTDDECOMP_O): CFLAGS += $(ALIGN_LOOP)
|
||||
|
||||
zstd zstd4 : CPPFLAGS += $(THREAD_CPP) $(ZLIBCPP) $(LZMACPP)
|
||||
@@ -153,8 +157,9 @@ zstd zstd4 : $(ZSTDLIB_FILES) zstdcli.o fileio.o bench.o datagen.o dibio.o
|
||||
ifneq (,$(filter Windows%,$(OS)))
|
||||
windres/generate_res.bat
|
||||
endif
|
||||
$(CC) $(FLAGS) $^ $(RES_FILE) -o zstd$(EXT) $(LDFLAGS)
|
||||
$(CC) $(FLAGS) $^ $(RES_FILE) -o $@$(EXT) $(LDFLAGS)
|
||||
|
||||
.PHONY: zstd-release
|
||||
zstd-release: DEBUGFLAGS :=
|
||||
zstd-release: zstd
|
||||
|
||||
@@ -165,8 +170,8 @@ ifneq (,$(filter Windows%,$(OS)))
|
||||
endif
|
||||
$(CC) -m32 $(FLAGS) $^ $(RES32_FILE) -o $@$(EXT)
|
||||
|
||||
zstd-nolegacy : clean_decomp_o
|
||||
$(MAKE) zstd ZSTD_LEGACY_SUPPORT=0
|
||||
zstd-nolegacy : $(ZSTD_FILES) $(ZDICT_FILES) zstdcli.o fileio.c bench.o datagen.o dibio.o
|
||||
$(CC) $(FLAGS) $^ -o $@$(EXT) $(LDFLAGS)
|
||||
|
||||
zstd-nomt : THREAD_CPP :=
|
||||
zstd-nomt : THREAD_LD :=
|
||||
@@ -197,9 +202,9 @@ zstd-pgo : clean zstd
|
||||
$(MAKE) zstd MOREFLAGS=-fprofile-use
|
||||
|
||||
# minimal target, with only zstd compression and decompression. no bench. no legacy.
|
||||
zstd-small: CFLAGS = "-Os -s"
|
||||
zstd-small: CFLAGS = -Os -s
|
||||
zstd-frugal zstd-small: $(ZSTD_FILES) zstdcli.c fileio.c
|
||||
$(CC) $(FLAGS) -DZSTD_NOBENCH -DZSTD_NODICT $^ -o zstd$(EXT)
|
||||
$(CC) $(FLAGS) -DZSTD_NOBENCH -DZSTD_NODICT $^ -o $@$(EXT)
|
||||
|
||||
zstd-decompress: $(ZSTDCOMMON_FILES) $(ZSTDDECOMP_FILES) zstdcli.c fileio.c
|
||||
$(CC) $(FLAGS) -DZSTD_NOBENCH -DZSTD_NODICT -DZSTD_NOCOMPRESS $^ -o $@$(EXT)
|
||||
@@ -207,34 +212,37 @@ zstd-decompress: $(ZSTDCOMMON_FILES) $(ZSTDDECOMP_FILES) zstdcli.c fileio.c
|
||||
zstd-compress: $(ZSTDCOMMON_FILES) $(ZSTDCOMP_FILES) zstdcli.c fileio.c
|
||||
$(CC) $(FLAGS) -DZSTD_NOBENCH -DZSTD_NODICT -DZSTD_NODECOMPRESS $^ -o $@$(EXT)
|
||||
|
||||
# zstd is now built with Multi-threading by default
|
||||
# zstd is now built with multithreading enabled y default
|
||||
zstdmt: zstd
|
||||
|
||||
.PHONY: generate_res
|
||||
generate_res:
|
||||
windres/generate_res.bat
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
$(MAKE) -C $(ZSTDDIR) clean
|
||||
@$(RM) $(ZSTDDIR)/decompress/*.o $(ZSTDDIR)/decompress/zstd_decompress.gcda
|
||||
@$(RM) core *.o tmp* result* *.gcda dictionary *.zst \
|
||||
zstd$(EXT) zstd32$(EXT) zstd-compress$(EXT) zstd-decompress$(EXT) \
|
||||
zstd-small$(EXT) zstd-frugal$(EXT) zstd-nolegacy$(EXT) zstd4$(EXT) \
|
||||
*.gcda default.profraw have_zlib$(EXT)
|
||||
@echo Cleaning completed
|
||||
|
||||
clean_decomp_o:
|
||||
@$(RM) $(ZSTDDECOMP_O)
|
||||
|
||||
MD2ROFF = ronn
|
||||
MD2ROFF_FLAGS = --roff --warnings --manual="User Commands" --organization="zstd $(ZSTD_VERSION)"
|
||||
|
||||
zstd.1: zstd.1.md
|
||||
cat $^ | $(MD2ROFF) $(MD2ROFF_FLAGS) | sed -n '/^\.\\\".*/!p' > $@
|
||||
|
||||
.PHONY: man
|
||||
man: zstd.1
|
||||
|
||||
.PHONY: clean-man
|
||||
clean-man:
|
||||
rm zstd.1
|
||||
|
||||
.PHONY: preview-man
|
||||
preview-man: clean-man man
|
||||
man ./zstd.1
|
||||
|
||||
@@ -243,6 +251,10 @@ preview-man: clean-man man
|
||||
#-----------------------------------------------------------------------------
|
||||
ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU OpenBSD FreeBSD NetBSD DragonFly SunOS))
|
||||
|
||||
.PHONY: list
|
||||
list:
|
||||
@$(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | sort | egrep -v -e '^[^[:alnum:]]' -e '^$@$$' | xargs
|
||||
|
||||
ifneq (,$(filter $(shell uname),SunOS))
|
||||
INSTALL ?= ginstall
|
||||
else
|
||||
@@ -263,6 +275,7 @@ INSTALL_PROGRAM ?= $(INSTALL) -m 755
|
||||
INSTALL_SCRIPT ?= $(INSTALL) -m 755
|
||||
INSTALL_MAN ?= $(INSTALL) -m 644
|
||||
|
||||
.PHONY: install
|
||||
install: zstd
|
||||
@echo Installing binaries
|
||||
@$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)/ $(DESTDIR)$(MANDIR)/
|
||||
@@ -278,6 +291,7 @@ install: zstd
|
||||
@ln -sf zstd.1 $(DESTDIR)$(MANDIR)/unzstd.1
|
||||
@echo zstd installation completed
|
||||
|
||||
.PHONY: uninstall
|
||||
uninstall:
|
||||
@$(RM) $(DESTDIR)$(BINDIR)/zstdgrep
|
||||
@$(RM) $(DESTDIR)$(BINDIR)/zstdless
|
||||
@@ -288,4 +302,5 @@ uninstall:
|
||||
@$(RM) $(DESTDIR)$(MANDIR)/unzstd.1
|
||||
@$(RM) $(DESTDIR)$(MANDIR)/zstd.1
|
||||
@echo zstd programs successfully uninstalled
|
||||
|
||||
endif
|
||||
|
||||
+28
-17
@@ -3,44 +3,54 @@ Command Line Interface for Zstandard library
|
||||
|
||||
Command Line Interface (CLI) can be created using the `make` command without any additional parameters.
|
||||
There are however other Makefile targets that create different variations of CLI:
|
||||
- `zstd` : default CLI supporting gzip-like arguments; includes dictionary builder, benchmark, and support for decompression of legacy zstd versions
|
||||
- `zstd32` : Same as `zstd`, but forced to compile in 32-bits mode
|
||||
- `zstd_nolegacy` : Same as `zstd` except of support for decompression of legacy zstd versions
|
||||
- `zstd-small` : CLI optimized for minimal size; without dictionary builder, benchmark, and support for decompression of legacy zstd versions
|
||||
- `zstd-compress` : compressor-only version of CLI; without dictionary builder, benchmark, and support for decompression of legacy zstd versions
|
||||
- `zstd-decompress` : decompressor-only version of CLI; without dictionary builder, benchmark, and support for decompression of legacy zstd versions
|
||||
- `zstd` : default CLI supporting gzip-like arguments; includes dictionary builder, benchmark, and support for decompression of legacy zstd formats
|
||||
- `zstd_nolegacy` : Same as `zstd` but without support for legacy zstd formats
|
||||
- `zstd-small` : CLI optimized for minimal size; no dictionary builder, no benchmark, and no support for legacy zstd formats
|
||||
- `zstd-compress` : version of CLI which can only compress into zstd format
|
||||
- `zstd-decompress` : version of CLI which can only decompress zstd format
|
||||
|
||||
|
||||
#### Compilation variables
|
||||
`zstd` tries to detect and use the following features automatically :
|
||||
`zstd` scope can be altered by modifying the following compilation variables :
|
||||
|
||||
- __HAVE_THREAD__ : multithreading is automatically enabled when `pthread` is detected.
|
||||
It's possible to disable multithread support, by either compiling `zstd-nomt` target or using HAVE_THREAD=0 variable.
|
||||
It's possible to disable multithread support, by setting HAVE_THREAD=0 .
|
||||
Example : make zstd HAVE_THREAD=0
|
||||
It's also possible to force compilation with multithread support, using HAVE_THREAD=1.
|
||||
In which case, linking stage will fail if `pthread` library cannot be found.
|
||||
This might be useful to prevent silent feature disabling.
|
||||
|
||||
- __HAVE_ZLIB__ : `zstd` can compress and decompress files in `.gz` format.
|
||||
This is done through command `--format=gzip`.
|
||||
This is ordered through command `--format=gzip`.
|
||||
Alternatively, symlinks named `gzip` or `gunzip` will mimic intended behavior.
|
||||
`.gz` support is automatically enabled when `zlib` library is detected at build time.
|
||||
It's possible to disable `.gz` support, by either compiling `zstd-nogz` target or using HAVE_ZLIB=0 variable.
|
||||
It's possible to disable `.gz` support, by setting HAVE_ZLIB=0.
|
||||
Example : make zstd HAVE_ZLIB=0
|
||||
It's also possible to force compilation with zlib support, using HAVE_ZLIB=1.
|
||||
In which case, linking stage will fail if `zlib` library cannot be found.
|
||||
This might be useful to prevent silent feature disabling.
|
||||
|
||||
- __HAVE_LZMA__ : `zstd` can compress and decompress files in `.xz` and `.lzma` formats.
|
||||
This is done through commands `--format=xz` and `--format=lzma` respectively.
|
||||
This is ordered through commands `--format=xz` and `--format=lzma` respectively.
|
||||
Alternatively, symlinks named `xz`, `unxz`, `lzma`, or `unlzma` will mimic intended behavior.
|
||||
`.xz` and `.lzma` support is automatically enabled when `lzma` library is detected at build time.
|
||||
It's possible to disable `.xz` and `.lzma` support, by either compiling `zstd-noxz` target or using HAVE_LZMA=0 variable.
|
||||
It's possible to disable `.xz` and `.lzma` support, by setting HAVE_LZMA=0 .
|
||||
Example : make zstd HAVE_LZMA=0
|
||||
It's also possible to force compilation with lzma support, using HAVE_LZMA=1.
|
||||
In which case, linking stage will fail if `lzma` library cannot be found.
|
||||
This might be useful to prevent silent feature disabling.
|
||||
|
||||
- __ZSTD_LEGACY_SUPPORT__ : `zstd` can decompress files compressed by older versions of `zstd`.
|
||||
Starting v0.8.0, all versions of `zstd` produce frames compliant with the [specification](../doc/zstd_compression_format.md), and are therefore compatible.
|
||||
But older versions (< v0.8.0) produced different, incompatible, frames.
|
||||
By default, `zstd` supports decoding legacy formats >= v0.4.0 (`ZSTD_LEGACY_SUPPORT=4`).
|
||||
This can be altered by modifying this compilation variable.
|
||||
`ZSTD_LEGACY_SUPPORT=1` means "support all formats >= v0.1.0".
|
||||
`ZSTD_LEGACY_SUPPORT=2` means "support all formats >= v0.2.0", and so on.
|
||||
`ZSTD_LEGACY_SUPPORT=0` means _DO NOT_ support any legacy format.
|
||||
if `ZSTD_LEGACY_SUPPORT >= 8`, it's the same as `0`, since there is no legacy format after `7`.
|
||||
Note : `zstd` only supports decoding older formats, and cannot generate any legacy format.
|
||||
|
||||
|
||||
#### Aggregation of parameters
|
||||
CLI supports aggregation of parameters i.e. `-b1`, `-e18`, and `-i1` can be joined into `-b1e18i1`.
|
||||
@@ -61,7 +71,7 @@ will rely more and more on previously decoded content to compress the rest of th
|
||||
|
||||
Usage of the dictionary builder and created dictionaries with CLI:
|
||||
|
||||
1. Create the dictionary : `zstd --train FullPathToTrainingSet/* -o dictionaryName`
|
||||
1. Create the dictionary : `zstd --train PathToTrainingSet/* -o dictionaryName`
|
||||
2. Compress with the dictionary: `zstd FILE -D dictionaryName`
|
||||
3. Decompress with the dictionary: `zstd --decompress FILE.zst -D dictionaryName`
|
||||
|
||||
@@ -70,8 +80,8 @@ Usage of the dictionary builder and created dictionaries with CLI:
|
||||
CLI includes in-memory compression benchmark module for zstd.
|
||||
The benchmark is conducted using given filenames. The files are read into memory and joined together.
|
||||
It makes benchmark more precise as it eliminates I/O overhead.
|
||||
Many filenames can be supplied as multiple parameters, parameters with wildcards or
|
||||
names of directories can be used as parameters with the `-r` option.
|
||||
Multiple filenames can be supplied, as multiple parameters, with wildcards,
|
||||
or names of directories can be used as parameters with `-r` option.
|
||||
|
||||
The benchmark measures ratio, compressed size, compression and decompression speed.
|
||||
One can select compression levels starting from `-b` and ending with `-e`.
|
||||
@@ -101,13 +111,14 @@ Advanced arguments :
|
||||
-v : verbose mode; specify multiple times to increase verbosity
|
||||
-q : suppress warnings; specify twice to suppress errors too
|
||||
-c : force write to standard output, even if it is the console
|
||||
-l : print information about zstd compressed files
|
||||
--ultra : enable levels beyond 19, up to 22 (requires more memory)
|
||||
-T# : use # threads for compression (default:1)
|
||||
-B# : select size of each job (default:0==automatic)
|
||||
--no-dictID : don't write dictID into header (dictionary compression)
|
||||
--[no-]check : integrity check (default:enabled)
|
||||
-r : operate recursively on directories
|
||||
--format=gzip : compress files to the .gz format
|
||||
--format=xz : compress files to the .xz format
|
||||
--format=lzma : compress files to the .lzma format
|
||||
--test : test compressed file integrity
|
||||
--[no-]sparse : sparse mode (default:disabled)
|
||||
-M# : Set a memory usage limit for decompression
|
||||
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
+6
-4
@@ -1,11 +1,13 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
#ifndef DATAGEN_H
|
||||
#define DATAGEN_H
|
||||
|
||||
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
/* This library is designed for a single-threaded console application.
|
||||
|
||||
+234
-233
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
@@ -925,230 +925,6 @@ int FIO_compressFilename(const char* dstFileName, const char* srcFileName,
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int numActualFrames;
|
||||
int numSkippableFrames;
|
||||
unsigned long long decompressedSize;
|
||||
int decompUnavailable;
|
||||
unsigned long long compressedSize;
|
||||
int usesCheck;
|
||||
} fileInfo_t;
|
||||
|
||||
/*
|
||||
* Reads information from file, stores in *info
|
||||
* if successful, returns 0, returns 1 for frame analysis error, returns 2 for file not compressed with zstd
|
||||
* returns 3 for cases in which file could not be opened.
|
||||
*/
|
||||
static int getFileInfo(fileInfo_t* info, const char* inFileName){
|
||||
int detectError = 0;
|
||||
FILE* const srcFile = FIO_openSrcFile(inFileName);
|
||||
if (srcFile == NULL) {
|
||||
DISPLAY("Error: could not open source file %s\n", inFileName);
|
||||
return 3;
|
||||
}
|
||||
info->compressedSize = (unsigned long long)UTIL_getFileSize(inFileName);
|
||||
/* begin analyzing frame */
|
||||
for ( ; ; ) {
|
||||
BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX];
|
||||
size_t const numBytesRead = fread(headerBuffer, 1, sizeof(headerBuffer), srcFile);
|
||||
if (numBytesRead < ZSTD_frameHeaderSize_min) {
|
||||
if (feof(srcFile) && numBytesRead == 0 && info->compressedSize > 0) {
|
||||
break;
|
||||
}
|
||||
else if (feof(srcFile)) {
|
||||
DISPLAY("Error: reached end of file with incomplete frame\n");
|
||||
detectError = 2;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
DISPLAY("Error: did not reach end of file but ran out of frames\n");
|
||||
detectError = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
{
|
||||
U32 const magicNumber = MEM_readLE32(headerBuffer);
|
||||
if (magicNumber == ZSTD_MAGICNUMBER) {
|
||||
U64 const frameContentSize = ZSTD_getFrameContentSize(headerBuffer, numBytesRead);
|
||||
if (frameContentSize == ZSTD_CONTENTSIZE_ERROR || frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN) {
|
||||
info->decompUnavailable = 1;
|
||||
}
|
||||
else {
|
||||
info->decompressedSize += frameContentSize;
|
||||
}
|
||||
{
|
||||
/* move to the end of the frame header */
|
||||
size_t const headerSize = ZSTD_frameHeaderSize(headerBuffer, numBytesRead);
|
||||
if (ZSTD_isError(headerSize)) {
|
||||
DISPLAY("Error: could not determine frame header size\n");
|
||||
detectError = 1;
|
||||
break;
|
||||
}
|
||||
{
|
||||
int const ret = fseek(srcFile, ((long)headerSize)-((long)numBytesRead), SEEK_CUR);
|
||||
if (ret != 0) {
|
||||
DISPLAY("Error: could not move to end of frame header\n");
|
||||
detectError = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* skip the rest of the blocks in the frame */
|
||||
{
|
||||
int lastBlock = 0;
|
||||
do {
|
||||
BYTE blockHeaderBuffer[3];
|
||||
U32 blockHeader;
|
||||
int blockSize;
|
||||
size_t const readBytes = fread(blockHeaderBuffer, 1, 3, srcFile);
|
||||
if (readBytes != 3) {
|
||||
DISPLAY("There was a problem reading the block header\n");
|
||||
detectError = 1;
|
||||
break;
|
||||
}
|
||||
blockHeader = MEM_readLE24(blockHeaderBuffer);
|
||||
lastBlock = blockHeader & 1;
|
||||
blockSize = blockHeader >> 3;
|
||||
{
|
||||
int const ret = fseek(srcFile, blockSize, SEEK_CUR);
|
||||
if (ret != 0) {
|
||||
DISPLAY("Error: could not skip to end of block\n");
|
||||
detectError = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (lastBlock != 1);
|
||||
|
||||
if (detectError) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
{
|
||||
/* check if checksum is used */
|
||||
BYTE const frameHeaderDescriptor = headerBuffer[4];
|
||||
int const contentChecksumFlag = (frameHeaderDescriptor & (1 << 2)) >> 2;
|
||||
if (contentChecksumFlag) {
|
||||
int const ret = fseek(srcFile, 4, SEEK_CUR);
|
||||
info->usesCheck = 1;
|
||||
if (ret != 0) {
|
||||
DISPLAY("Error: could not skip past checksum\n");
|
||||
detectError = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
info->numActualFrames++;
|
||||
}
|
||||
else if (magicNumber == ZSTD_MAGIC_SKIPPABLE_START) {
|
||||
BYTE frameSizeBuffer[4];
|
||||
size_t const readBytes = fread(frameSizeBuffer, 1, 4, srcFile);
|
||||
if (readBytes != 4) {
|
||||
DISPLAY("There was an error reading skippable frame size");
|
||||
detectError = 1;
|
||||
break;
|
||||
}
|
||||
{
|
||||
U32 const frameSize = MEM_readLE32(frameSizeBuffer);
|
||||
int const ret = LONG_SEEK(srcFile, frameSize, SEEK_CUR);
|
||||
if (ret != 0) {
|
||||
DISPLAY("Error: could not find end of skippable frame\n");
|
||||
detectError = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
info->numSkippableFrames++;
|
||||
}
|
||||
else {
|
||||
detectError = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(srcFile);
|
||||
return detectError;
|
||||
}
|
||||
|
||||
static void displayInfo(const char* inFileName, fileInfo_t* info, int displayLevel){
|
||||
double const compressedSizeMB = (double)info->compressedSize/(1 MB);
|
||||
double const decompressedSizeMB = (double)info->decompressedSize/(1 MB);
|
||||
double const ratio = (info->compressedSize == 0) ? 0 : ((double)info->decompressedSize)/info->compressedSize;
|
||||
const char* const checkString = (info->usesCheck ? "XXH64" : "None");
|
||||
if (displayLevel <= 2) {
|
||||
if (!info->decompUnavailable) {
|
||||
DISPLAYOUT("Skippable Non-Skippable Compressed Uncompressed Ratio Check Filename\n");
|
||||
DISPLAYOUT("%9d %13d %7.2f MB %9.2f MB %5.3f %5s %s\n",
|
||||
info->numSkippableFrames, info->numActualFrames, compressedSizeMB, decompressedSizeMB,
|
||||
ratio, checkString, inFileName);
|
||||
}
|
||||
else {
|
||||
DISPLAYOUT("Skippable Non-Skippable Compressed Check Filename\n");
|
||||
DISPLAYOUT("%9d %13d %7.2f MB %5s %s\n",
|
||||
info->numSkippableFrames, info->numActualFrames, compressedSizeMB, checkString, inFileName);
|
||||
}
|
||||
}
|
||||
else{
|
||||
DISPLAYOUT("# Zstandard Frames: %d\n", info->numActualFrames);
|
||||
DISPLAYOUT("# Skippable Frames: %d\n", info->numSkippableFrames);
|
||||
DISPLAYOUT("Compressed Size: %.2f MB (%llu B)\n", compressedSizeMB, info->compressedSize);
|
||||
if (!info->decompUnavailable) {
|
||||
DISPLAYOUT("Decompressed Size: %.2f MB (%llu B)\n", decompressedSizeMB, info->decompressedSize);
|
||||
DISPLAYOUT("Ratio: %.4f\n", ratio);
|
||||
}
|
||||
DISPLAYOUT("Check: %s\n", checkString);
|
||||
DISPLAYOUT("\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int FIO_listFile(const char* inFileName, int displayLevel, unsigned fileNo, unsigned numFiles){
|
||||
/* initialize info to avoid warnings */
|
||||
fileInfo_t info;
|
||||
memset(&info, 0, sizeof(info));
|
||||
DISPLAYOUT("%s (%u/%u):\n", inFileName, fileNo, numFiles);
|
||||
{
|
||||
int const error = getFileInfo(&info, inFileName);
|
||||
if (error == 1) {
|
||||
/* display error, but provide output */
|
||||
DISPLAY("An error occurred with getting file info\n");
|
||||
}
|
||||
else if (error == 2) {
|
||||
DISPLAYOUT("File %s not compressed with zstd\n", inFileName);
|
||||
if (displayLevel > 2) {
|
||||
DISPLAYOUT("\n");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
else if (error == 3) {
|
||||
/* error occurred with opening the file */
|
||||
if (displayLevel > 2) {
|
||||
DISPLAYOUT("\n");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
displayInfo(inFileName, &info, displayLevel);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
int FIO_listMultipleFiles(unsigned numFiles, const char** filenameTable, int displayLevel){
|
||||
if (numFiles == 0) {
|
||||
DISPLAYOUT("No files given\n");
|
||||
return 0;
|
||||
}
|
||||
DISPLAYOUT("===========================================\n");
|
||||
DISPLAYOUT("Printing information about compressed files\n");
|
||||
DISPLAYOUT("===========================================\n");
|
||||
DISPLAYOUT("Number of files listed: %u\n", numFiles);
|
||||
{
|
||||
int error = 0;
|
||||
unsigned u;
|
||||
for (u=0; u<numFiles;u++) {
|
||||
error |= FIO_listFile(filenameTable[u], displayLevel, u+1, numFiles);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
int FIO_compressMultipleFilenames(const char** inFileNamesTable, unsigned nbFiles,
|
||||
const char* suffix,
|
||||
@@ -1194,10 +970,8 @@ int FIO_compressMultipleFilenames(const char** inFileNamesTable, unsigned nbFile
|
||||
missed_files += FIO_compressFilename_dstFile(ress, dstFileName, inFileNamesTable[u], compressionLevel);
|
||||
} }
|
||||
|
||||
/* Close & Free */
|
||||
FIO_freeCResources(ress);
|
||||
free(dstFileName);
|
||||
|
||||
return missed_files;
|
||||
}
|
||||
|
||||
@@ -1208,8 +982,8 @@ int FIO_compressMultipleFilenames(const char** inFileNamesTable, unsigned nbFile
|
||||
#ifndef ZSTD_NODECOMPRESS
|
||||
|
||||
/* **************************************************************************
|
||||
* Decompression
|
||||
****************************************************************************/
|
||||
* Decompression
|
||||
***************************************************************************/
|
||||
typedef struct {
|
||||
void* srcBuffer;
|
||||
size_t srcBufferLoaded;
|
||||
@@ -1616,7 +1390,7 @@ static unsigned long long FIO_decompressLz4Frame(dRess_t* ress,
|
||||
/* Write Block */
|
||||
if (decodedBytes) {
|
||||
if (fwrite(ress->dstBuffer, 1, decodedBytes, ress->dstFile) != decodedBytes) {
|
||||
DISPLAYLEVEL(1, "zstd: %s \n", strerr(errno));
|
||||
DISPLAYLEVEL(1, "zstd: %s \n", strerror(errno));
|
||||
decodingError = 1; break;
|
||||
}
|
||||
filesize += decodedBytes;
|
||||
@@ -1878,4 +1652,231 @@ int FIO_decompressMultipleFilenames(const char** srcNamesTable, unsigned nbFiles
|
||||
return missingFiles + skippedFiles;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* **************************************************************************
|
||||
* .zst file info (--list command)
|
||||
***************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
int numActualFrames;
|
||||
int numSkippableFrames;
|
||||
unsigned long long decompressedSize;
|
||||
int decompUnavailable;
|
||||
unsigned long long compressedSize;
|
||||
int usesCheck;
|
||||
} fileInfo_t;
|
||||
|
||||
/** getFileInfo() :
|
||||
* Reads information from file, stores in *info
|
||||
* @return : 0 if successful
|
||||
* 1 for frame analysis error
|
||||
* 2 for file not compressed with zstd
|
||||
* 3 for cases in which file could not be opened.
|
||||
*/
|
||||
static int getFileInfo(fileInfo_t* info, const char* inFileName){
|
||||
int detectError = 0;
|
||||
FILE* const srcFile = FIO_openSrcFile(inFileName);
|
||||
if (srcFile == NULL) {
|
||||
DISPLAY("Error: could not open source file %s\n", inFileName);
|
||||
return 3;
|
||||
}
|
||||
info->compressedSize = (unsigned long long)UTIL_getFileSize(inFileName);
|
||||
|
||||
/* begin analyzing frame */
|
||||
for ( ; ; ) {
|
||||
BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX];
|
||||
size_t const numBytesRead = fread(headerBuffer, 1, sizeof(headerBuffer), srcFile);
|
||||
if (numBytesRead < ZSTD_frameHeaderSize_min) {
|
||||
if (feof(srcFile) && numBytesRead == 0 && info->compressedSize > 0) {
|
||||
break;
|
||||
}
|
||||
else if (feof(srcFile)) {
|
||||
DISPLAY("Error: reached end of file with incomplete frame\n");
|
||||
detectError = 2;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
DISPLAY("Error: did not reach end of file but ran out of frames\n");
|
||||
detectError = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
{ U32 const magicNumber = MEM_readLE32(headerBuffer);
|
||||
/* Zstandard frame */
|
||||
if (magicNumber == ZSTD_MAGICNUMBER) {
|
||||
U64 const frameContentSize = ZSTD_getFrameContentSize(headerBuffer, numBytesRead);
|
||||
if (frameContentSize == ZSTD_CONTENTSIZE_ERROR || frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN) {
|
||||
info->decompUnavailable = 1;
|
||||
} else {
|
||||
info->decompressedSize += frameContentSize;
|
||||
}
|
||||
/* move to the end of the frame header */
|
||||
{ size_t const headerSize = ZSTD_frameHeaderSize(headerBuffer, numBytesRead);
|
||||
if (ZSTD_isError(headerSize)) {
|
||||
DISPLAY("Error: could not determine frame header size\n");
|
||||
detectError = 1;
|
||||
break;
|
||||
}
|
||||
{ int const ret = fseek(srcFile, ((long)headerSize)-((long)numBytesRead), SEEK_CUR);
|
||||
if (ret != 0) {
|
||||
DISPLAY("Error: could not move to end of frame header\n");
|
||||
detectError = 1;
|
||||
break;
|
||||
} } }
|
||||
|
||||
/* skip the rest of the blocks in the frame */
|
||||
{ int lastBlock = 0;
|
||||
do {
|
||||
BYTE blockHeaderBuffer[3];
|
||||
size_t const readBytes = fread(blockHeaderBuffer, 1, 3, srcFile);
|
||||
if (readBytes != 3) {
|
||||
DISPLAY("There was a problem reading the block header\n");
|
||||
detectError = 1;
|
||||
break;
|
||||
}
|
||||
{ U32 const blockHeader = MEM_readLE24(blockHeaderBuffer);
|
||||
U32 const blockTypeID = (blockHeader >> 1) & 3;
|
||||
U32 const isRLE = (blockTypeID == 1);
|
||||
U32 const isWrongBlock = (blockTypeID == 3);
|
||||
long const blockSize = isRLE ? 1 : (long)(blockHeader >> 3);
|
||||
if (isWrongBlock) {
|
||||
DISPLAY("Error: unsupported block type \n");
|
||||
detectError = 1;
|
||||
break;
|
||||
}
|
||||
lastBlock = blockHeader & 1;
|
||||
{ int const ret = fseek(srcFile, blockSize, SEEK_CUR);
|
||||
if (ret != 0) {
|
||||
DISPLAY("Error: could not skip to end of block\n");
|
||||
detectError = 1;
|
||||
break;
|
||||
} } }
|
||||
} while (lastBlock != 1);
|
||||
|
||||
if (detectError) break;
|
||||
}
|
||||
|
||||
/* check if checksum is used */
|
||||
{ BYTE const frameHeaderDescriptor = headerBuffer[4];
|
||||
int const contentChecksumFlag = (frameHeaderDescriptor & (1 << 2)) >> 2;
|
||||
if (contentChecksumFlag) {
|
||||
int const ret = fseek(srcFile, 4, SEEK_CUR);
|
||||
info->usesCheck = 1;
|
||||
if (ret != 0) {
|
||||
DISPLAY("Error: could not skip past checksum\n");
|
||||
detectError = 1;
|
||||
break;
|
||||
} } }
|
||||
info->numActualFrames++;
|
||||
}
|
||||
/* Skippable frame */
|
||||
else if ((magicNumber & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) {
|
||||
U32 const frameSize = MEM_readLE32(headerBuffer + 4);
|
||||
long const seek = (long)(8 + frameSize - numBytesRead);
|
||||
int const ret = LONG_SEEK(srcFile, seek, SEEK_CUR);
|
||||
if (ret != 0) {
|
||||
DISPLAY("Error: could not find end of skippable frame\n");
|
||||
detectError = 1;
|
||||
break;
|
||||
}
|
||||
info->numSkippableFrames++;
|
||||
}
|
||||
/* unknown content */
|
||||
else {
|
||||
detectError = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} /* end analyzing frame */
|
||||
fclose(srcFile);
|
||||
return detectError;
|
||||
}
|
||||
|
||||
static void displayInfo(const char* inFileName, fileInfo_t* info, int displayLevel){
|
||||
unsigned const unit = info->compressedSize < (1 MB) ? (1 KB) : (1 MB);
|
||||
const char* const unitStr = info->compressedSize < (1 MB) ? "KB" : "MB";
|
||||
double const compressedSizeUnit = (double)info->compressedSize / unit;
|
||||
double const decompressedSizeUnit = (double)info->decompressedSize / unit;
|
||||
double const ratio = (info->compressedSize == 0) ? 0 : ((double)info->decompressedSize)/info->compressedSize;
|
||||
const char* const checkString = (info->usesCheck ? "XXH64" : "None");
|
||||
if (displayLevel <= 2) {
|
||||
if (!info->decompUnavailable) {
|
||||
DISPLAYOUT("Skippable Non-Skippable Compressed Uncompressed Ratio Check Filename\n");
|
||||
DISPLAYOUT("%9d %13d %7.2f %2s %9.2f %2s %5.3f %5s %s\n",
|
||||
info->numSkippableFrames, info->numActualFrames,
|
||||
compressedSizeUnit, unitStr, decompressedSizeUnit, unitStr,
|
||||
ratio, checkString, inFileName);
|
||||
} else {
|
||||
DISPLAYOUT("Skippable Non-Skippable Compressed Check Filename\n");
|
||||
DISPLAYOUT("%9d %13d %7.2f MB %5s %s\n",
|
||||
info->numSkippableFrames, info->numActualFrames,
|
||||
compressedSizeUnit, checkString, inFileName);
|
||||
}
|
||||
} else {
|
||||
DISPLAYOUT("# Zstandard Frames: %d\n", info->numActualFrames);
|
||||
DISPLAYOUT("# Skippable Frames: %d\n", info->numSkippableFrames);
|
||||
DISPLAYOUT("Compressed Size: %.2f %2s (%llu B)\n",
|
||||
compressedSizeUnit, unitStr, info->compressedSize);
|
||||
if (!info->decompUnavailable) {
|
||||
DISPLAYOUT("Decompressed Size: %.2f %2s (%llu B)\n",
|
||||
decompressedSizeUnit, unitStr, info->decompressedSize);
|
||||
DISPLAYOUT("Ratio: %.4f\n", ratio);
|
||||
}
|
||||
DISPLAYOUT("Check: %s\n", checkString);
|
||||
DISPLAYOUT("\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int FIO_listFile(const char* inFileName, int displayLevel, unsigned fileNo, unsigned numFiles){
|
||||
/* initialize info to avoid warnings */
|
||||
fileInfo_t info;
|
||||
memset(&info, 0, sizeof(info));
|
||||
DISPLAYOUT("%s (%u/%u):\n", inFileName, fileNo, numFiles);
|
||||
{
|
||||
int const error = getFileInfo(&info, inFileName);
|
||||
if (error == 1) {
|
||||
/* display error, but provide output */
|
||||
DISPLAY("An error occurred with getting file info\n");
|
||||
}
|
||||
else if (error == 2) {
|
||||
DISPLAYOUT("File %s not compressed with zstd\n", inFileName);
|
||||
if (displayLevel > 2) {
|
||||
DISPLAYOUT("\n");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
else if (error == 3) {
|
||||
/* error occurred with opening the file */
|
||||
if (displayLevel > 2) {
|
||||
DISPLAYOUT("\n");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
displayInfo(inFileName, &info, displayLevel);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
int FIO_listMultipleFiles(unsigned numFiles, const char** filenameTable, int displayLevel){
|
||||
if (numFiles == 0) {
|
||||
DISPLAYOUT("No files given\n");
|
||||
return 0;
|
||||
}
|
||||
DISPLAYOUT("===========================================\n");
|
||||
DISPLAYOUT("Printing information about compressed files\n");
|
||||
DISPLAYOUT("===========================================\n");
|
||||
DISPLAYOUT("Number of files listed: %u\n", numFiles);
|
||||
{
|
||||
int error = 0;
|
||||
unsigned u;
|
||||
for (u=0; u<numFiles;u++) {
|
||||
error |= FIO_listFile(filenameTable[u], displayLevel, u+1, numFiles);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif /* #ifndef ZSTD_NODECOMPRESS */
|
||||
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
+5
-7
@@ -1,12 +1,10 @@
|
||||
/**
|
||||
* platform.h - compiler and OS detection
|
||||
*
|
||||
* Copyright (c) 2016-present, Przemyslaw Skibinski, Yann Collet, Facebook, Inc.
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
#ifndef PLATFORM_H_MODULE
|
||||
|
||||
+5
-7
@@ -1,12 +1,10 @@
|
||||
/**
|
||||
* util.h - utility functions
|
||||
*
|
||||
* Copyright (c) 2016-present, Przemyslaw Skibinski, Yann Collet, Facebook, Inc.
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
#ifndef UTIL_H_MODULE
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
.
|
||||
.TH "ZSTD" "1" "June 2017" "zstd 1.3.0" "User Commands"
|
||||
.TH "ZSTD" "1" "August 2017" "zstd 1.3.1" "User Commands"
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBzstd\fR \- zstd, zstdmt, unzstd, zstdcat \- Compress or decompress \.zst files
|
||||
@@ -105,7 +105,7 @@ unlocks high compression levels 20+ (maximum 22), using a lot more memory\. Note
|
||||
.
|
||||
.TP
|
||||
\fB\-T#\fR, \fB\-\-threads=#\fR
|
||||
Compress using \fB#\fR threads (default: 1)\. If \fB#\fR is 0, attempt to detect and use the number of physical CPU cores\. This modifier does nothing if \fBzstd\fR is compiled without multithread support\.
|
||||
Compress using \fB#\fR threads (default: 1)\. If \fB#\fR is 0, attempt to detect and use the number of physical CPU cores\. In all cases, the nb of threads is capped to ZSTDMT_NBTHREADS_MAX==256\. This modifier does nothing if \fBzstd\fR is compiled without multithread support\.
|
||||
.
|
||||
.TP
|
||||
\fB\-D file\fR
|
||||
@@ -149,7 +149,7 @@ display help/long help and exit
|
||||
.
|
||||
.TP
|
||||
\fB\-V\fR, \fB\-\-version\fR
|
||||
display version number and exit
|
||||
display version number and exit\. Advanced : \fB\-vV\fR also displays supported formats\. \fB\-vvV\fR also displays POSIX support\.
|
||||
.
|
||||
.TP
|
||||
\fB\-v\fR
|
||||
|
||||
+4
-1
@@ -108,6 +108,7 @@ the last one takes effect.
|
||||
* `-T#`, `--threads=#`:
|
||||
Compress using `#` threads (default: 1).
|
||||
If `#` is 0, attempt to detect and use the number of physical CPU cores.
|
||||
In all cases, the nb of threads is capped to ZSTDMT_NBTHREADS_MAX==256.
|
||||
This modifier does nothing if `zstd` is compiled without multithread support.
|
||||
* `-D file`:
|
||||
use `file` as Dictionary to compress or decompress FILE(s)
|
||||
@@ -139,7 +140,9 @@ the last one takes effect.
|
||||
* `-h`/`-H`, `--help`:
|
||||
display help/long help and exit
|
||||
* `-V`, `--version`:
|
||||
display version number and exit
|
||||
display version number and exit.
|
||||
Advanced : `-vV` also displays supported formats.
|
||||
`-vvV` also displays POSIX support.
|
||||
* `-v`:
|
||||
verbose mode
|
||||
* `-q`, `--quiet`:
|
||||
|
||||
+70
-36
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef ZSTDCLI_CLEVEL_MAX
|
||||
# define ZSTDCLI_CLEVEL_MAX 19 /* when not using --ultra */
|
||||
# define ZSTDCLI_CLEVEL_MAX 19 /* without using --ultra */
|
||||
#endif
|
||||
|
||||
|
||||
@@ -26,14 +26,15 @@
|
||||
**************************************/
|
||||
#include "platform.h" /* IS_CONSOLE, PLATFORM_POSIX_VERSION */
|
||||
#include "util.h" /* UTIL_HAS_CREATEFILELIST, UTIL_createFileList */
|
||||
#include <stdio.h> /* fprintf(), stdin, stdout, stderr */
|
||||
#include <string.h> /* strcmp, strlen */
|
||||
#include <errno.h> /* errno */
|
||||
#include "fileio.h"
|
||||
#include "fileio.h" /* stdinmark, stdoutmark, ZSTD_EXTENSION */
|
||||
#ifndef ZSTD_NOBENCH
|
||||
# include "bench.h" /* BMK_benchFiles, BMK_SetNbSeconds */
|
||||
#endif
|
||||
#ifndef ZSTD_NODICT
|
||||
# include "dibio.h"
|
||||
# include "dibio.h" /* ZDICT_cover_params_t, DiB_trainFromFiles() */
|
||||
#endif
|
||||
#define ZSTD_STATIC_LINKING_ONLY /* ZSTD_maxCLevel */
|
||||
#include "zstd.h" /* ZSTD_VERSION_STRING */
|
||||
@@ -64,7 +65,7 @@
|
||||
#define MB *(1 <<20)
|
||||
#define GB *(1U<<30)
|
||||
|
||||
#define DEFAULT_DISPLAY_LEVEL 2
|
||||
#define DISPLAY_LEVEL_DEFAULT 2
|
||||
|
||||
static const char* g_defaultDictName = "dictionary";
|
||||
static const unsigned g_defaultMaxDictSize = 110 KB;
|
||||
@@ -79,7 +80,7 @@ static U32 g_overlapLog = OVERLAP_LOG_DEFAULT;
|
||||
**************************************/
|
||||
#define DISPLAY(...) fprintf(g_displayOut, __VA_ARGS__)
|
||||
#define DISPLAYLEVEL(l, ...) { if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } }
|
||||
static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; /* 0 : no display, 1: errors, 2 : + result + interaction + warnings, 3 : + progression, 4 : + information */
|
||||
static int g_displayLevel = DISPLAY_LEVEL_DEFAULT; /* 0 : no display, 1: errors, 2 : + result + interaction + warnings, 3 : + progression, 4 : + information */
|
||||
static FILE* g_displayOut;
|
||||
|
||||
|
||||
@@ -88,12 +89,12 @@ static FILE* g_displayOut;
|
||||
**************************************/
|
||||
static int usage(const char* programName)
|
||||
{
|
||||
DISPLAY( "Usage :\n");
|
||||
DISPLAY( " %s [args] [FILE(s)] [-o file]\n", programName);
|
||||
DISPLAY( "Usage : \n");
|
||||
DISPLAY( " %s [args] [FILE(s)] [-o file] \n", programName);
|
||||
DISPLAY( "\n");
|
||||
DISPLAY( "FILE : a filename\n");
|
||||
DISPLAY( "FILE : a filename \n");
|
||||
DISPLAY( " with no FILE, or when FILE is - , read standard input\n");
|
||||
DISPLAY( "Arguments :\n");
|
||||
DISPLAY( "Arguments : \n");
|
||||
#ifndef ZSTD_NOCOMPRESS
|
||||
DISPLAY( " -# : # compression level (1-%d, default:%d) \n", ZSTDCLI_CLEVEL_MAX, ZSTDCLI_CLEVEL_DEFAULT);
|
||||
#endif
|
||||
@@ -105,7 +106,7 @@ static int usage(const char* programName)
|
||||
DISPLAY( " -f : overwrite output without prompting and (de)compress links \n");
|
||||
DISPLAY( "--rm : remove source file(s) after successful de/compression \n");
|
||||
DISPLAY( " -k : preserve source file(s) (default) \n");
|
||||
DISPLAY( " -h/-H : display help/long help and exit\n");
|
||||
DISPLAY( " -h/-H : display help/long help and exit \n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -114,12 +115,12 @@ static int usage_advanced(const char* programName)
|
||||
DISPLAY(WELCOME_MESSAGE);
|
||||
usage(programName);
|
||||
DISPLAY( "\n");
|
||||
DISPLAY( "Advanced arguments :\n");
|
||||
DISPLAY( " -V : display Version number and exit\n");
|
||||
DISPLAY( "Advanced arguments : \n");
|
||||
DISPLAY( " -V : display Version number and exit \n");
|
||||
DISPLAY( " -v : verbose mode; specify multiple times to increase verbosity\n");
|
||||
DISPLAY( " -q : suppress warnings; specify twice to suppress errors too\n");
|
||||
DISPLAY( " -c : force write to standard output, even if it is the console\n");
|
||||
DISPLAY( " -l : print information about zstd compressed files.\n");
|
||||
DISPLAY( " -l : print information about zstd compressed files \n");
|
||||
#ifndef ZSTD_NOCOMPRESS
|
||||
DISPLAY( "--ultra : enable levels beyond %i, up to %i (requires more memory)\n", ZSTDCLI_CLEVEL_MAX, ZSTD_maxCLevel());
|
||||
#ifdef ZSTD_MULTITHREAD
|
||||
@@ -151,11 +152,10 @@ static int usage_advanced(const char* programName)
|
||||
#endif
|
||||
#endif
|
||||
DISPLAY( " -M# : Set a memory usage limit for decompression \n");
|
||||
DISPLAY( "--list : list information about a zstd compressed file \n");
|
||||
DISPLAY( "-- : All arguments after \"--\" are treated as files \n");
|
||||
#ifndef ZSTD_NODICT
|
||||
DISPLAY( "\n");
|
||||
DISPLAY( "Dictionary builder :\n");
|
||||
DISPLAY( "Dictionary builder : \n");
|
||||
DISPLAY( "--train ## : create a dictionary from a training set of files \n");
|
||||
DISPLAY( "--train-cover[=k=#,d=#,steps=#] : use the cover algorithm with optional args\n");
|
||||
DISPLAY( "--train-legacy[=s=#] : use the legacy algorithm with selectivity (default: %u)\n", g_defaultSelectivityLevel);
|
||||
@@ -165,12 +165,12 @@ static int usage_advanced(const char* programName)
|
||||
#endif
|
||||
#ifndef ZSTD_NOBENCH
|
||||
DISPLAY( "\n");
|
||||
DISPLAY( "Benchmark arguments :\n");
|
||||
DISPLAY( "Benchmark arguments : \n");
|
||||
DISPLAY( " -b# : benchmark file(s), using # compression level (default : 1) \n");
|
||||
DISPLAY( " -e# : test all compression levels from -bX to # (default: 1)\n");
|
||||
DISPLAY( " -i# : minimum evaluation time in seconds (default : 3s)\n");
|
||||
DISPLAY( " -i# : minimum evaluation time in seconds (default : 3s) \n");
|
||||
DISPLAY( " -B# : cut file into independent blocks of size # (default: no block)\n");
|
||||
DISPLAY( "--priority=rt : set process priority to real-time\n");
|
||||
DISPLAY( "--priority=rt : set process priority to real-time \n");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
@@ -313,6 +313,35 @@ static unsigned parseCompressionParameters(const char* stringPtr, ZSTD_compressi
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void printVersion(void)
|
||||
{
|
||||
DISPLAY(WELCOME_MESSAGE);
|
||||
/* format support */
|
||||
DISPLAYLEVEL(3, "*** supports: zstd");
|
||||
#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>0) && (ZSTD_LEGACY_SUPPORT<8)
|
||||
DISPLAYLEVEL(3, ", zstd legacy v0.%d+", ZSTD_LEGACY_SUPPORT);
|
||||
#endif
|
||||
#ifdef ZSTD_GZCOMPRESS
|
||||
DISPLAYLEVEL(3, ", gzip");
|
||||
#endif
|
||||
#ifdef ZSTD_LZ4COMPRESS
|
||||
DISPLAYLEVEL(3, ", lz4");
|
||||
#endif
|
||||
#ifdef ZSTD_LZMACOMPRESS
|
||||
DISPLAYLEVEL(3, ", lzma, xz ");
|
||||
#endif
|
||||
DISPLAYLEVEL(3, "\n");
|
||||
/* posix support */
|
||||
#ifdef _POSIX_C_SOURCE
|
||||
DISPLAYLEVEL(4, "_POSIX_C_SOURCE defined: %ldL\n", (long) _POSIX_C_SOURCE);
|
||||
#endif
|
||||
#ifdef _POSIX_VERSION
|
||||
DISPLAYLEVEL(4, "_POSIX_VERSION defined: %ldL \n", (long) _POSIX_VERSION);
|
||||
#endif
|
||||
#ifdef PLATFORM_POSIX_VERSION
|
||||
DISPLAYLEVEL(4, "PLATFORM_POSIX_VERSION defined: %ldL\n", (long) PLATFORM_POSIX_VERSION);
|
||||
#endif
|
||||
}
|
||||
|
||||
typedef enum { zom_compress, zom_decompress, zom_test, zom_bench, zom_train, zom_list } zstd_operation_mode;
|
||||
|
||||
@@ -492,7 +521,7 @@ int main(int argCount, const char* argv[])
|
||||
switch(argument[0])
|
||||
{
|
||||
/* Display help */
|
||||
case 'V': g_displayOut=stdout; DISPLAY(WELCOME_MESSAGE); CLEAN_RETURN(0); /* Version Only */
|
||||
case 'V': g_displayOut=stdout; printVersion(); CLEAN_RETURN(0); /* Version Only */
|
||||
case 'H':
|
||||
case 'h': g_displayOut=stdout; CLEAN_RETURN(usage_advanced(programName));
|
||||
|
||||
@@ -635,24 +664,18 @@ int main(int argCount, const char* argv[])
|
||||
filenameTable[filenameIdx++] = argument;
|
||||
}
|
||||
|
||||
if (lastCommand) { DISPLAY("error : command must be followed by argument \n"); CLEAN_RETURN(1); } /* forgotten argument */
|
||||
if (lastCommand) { /* forgotten argument */
|
||||
DISPLAY("error : command must be followed by argument \n");
|
||||
CLEAN_RETURN(1);
|
||||
}
|
||||
|
||||
/* Welcome message (if verbose) */
|
||||
DISPLAYLEVEL(3, WELCOME_MESSAGE);
|
||||
#ifdef _POSIX_C_SOURCE
|
||||
DISPLAYLEVEL(4, "_POSIX_C_SOURCE defined: %ldL\n", (long) _POSIX_C_SOURCE);
|
||||
#endif
|
||||
#ifdef _POSIX_VERSION
|
||||
DISPLAYLEVEL(4, "_POSIX_VERSION defined: %ldL\n", (long) _POSIX_VERSION);
|
||||
#endif
|
||||
#ifdef PLATFORM_POSIX_VERSION
|
||||
DISPLAYLEVEL(4, "PLATFORM_POSIX_VERSION defined: %ldL\n", (long) PLATFORM_POSIX_VERSION);
|
||||
#endif
|
||||
|
||||
if (nbThreads == 0) {
|
||||
/* try to guess */
|
||||
nbThreads = UTIL_countPhysicalCores();
|
||||
DISPLAYLEVEL(3, "Note: %d physical core(s) detected\n", nbThreads);
|
||||
DISPLAYLEVEL(3, "Note: %d physical core(s) detected \n", nbThreads);
|
||||
}
|
||||
|
||||
g_utilDisplayLevel = g_displayLevel;
|
||||
@@ -679,10 +702,17 @@ int main(int argCount, const char* argv[])
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (operation == zom_list) {
|
||||
#ifndef ZSTD_NODECOMPRESS
|
||||
int const ret = FIO_listMultipleFiles(filenameIdx, filenameTable, g_displayLevel);
|
||||
CLEAN_RETURN(ret);
|
||||
#else
|
||||
DISPLAY("file information is not supported \n");
|
||||
CLEAN_RETURN(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Check if benchmark is selected */
|
||||
if (operation==zom_bench) {
|
||||
#ifndef ZSTD_NOBENCH
|
||||
@@ -692,7 +722,7 @@ int main(int argCount, const char* argv[])
|
||||
BMK_setNbSeconds(bench_nbSeconds);
|
||||
BMK_benchFiles(filenameTable, filenameIdx, dictFileName, cLevel, cLevelLast, &compressionParams, setRealTimePrio);
|
||||
#endif
|
||||
(void)bench_nbSeconds;
|
||||
(void)bench_nbSeconds; (void)blockSize; (void)setRealTimePrio;
|
||||
goto _end;
|
||||
}
|
||||
|
||||
@@ -719,6 +749,10 @@ int main(int argCount, const char* argv[])
|
||||
goto _end;
|
||||
}
|
||||
|
||||
#ifndef ZSTD_NODECOMPRESS
|
||||
if (operation==zom_test) { outFileName=nulmark; FIO_setRemoveSrcFile(0); } /* test mode */
|
||||
#endif
|
||||
|
||||
/* No input filename ==> use stdin and stdout */
|
||||
filenameIdx += !filenameIdx; /* filenameTable[0] is stdin by default */
|
||||
if (!strcmp(filenameTable[0], stdinmark) && !outFileName) outFileName = stdoutmark; /* when input is stdin, default output is stdout */
|
||||
@@ -759,11 +793,11 @@ int main(int argCount, const char* argv[])
|
||||
else
|
||||
operationResult = FIO_compressMultipleFilenames(filenameTable, filenameIdx, outFileName ? outFileName : suffix, dictFileName, cLevel, &compressionParams);
|
||||
#else
|
||||
(void)suffix;
|
||||
DISPLAY("Compression not supported\n");
|
||||
#endif
|
||||
} else { /* decompression or test */
|
||||
#ifndef ZSTD_NODECOMPRESS
|
||||
if (operation==zom_test) { outFileName=nulmark; FIO_setRemoveSrcFile(0); } /* test mode */
|
||||
FIO_setMemLimit(memLimit);
|
||||
if (filenameIdx==1 && outFileName)
|
||||
operationResult = FIO_decompressFilename(outFileName, filenameTable[0], dictFileName);
|
||||
|
||||
+14
-14
@@ -73,13 +73,13 @@ DECODECORPUS_TESTTIME ?= -T30
|
||||
|
||||
default: fullbench
|
||||
|
||||
all: fullbench fuzzer zstreamtest paramgrill datagen zbufftest decodecorpus
|
||||
all: fullbench fuzzer zstreamtest paramgrill datagen decodecorpus
|
||||
|
||||
all32: fullbench32 fuzzer32 zstreamtest32 zbufftest32
|
||||
all32: fullbench32 fuzzer32 zstreamtest32
|
||||
|
||||
allnothread: fullbench fuzzer paramgrill datagen zbufftest decodecorpus
|
||||
allnothread: fullbench fuzzer paramgrill datagen decodecorpus
|
||||
|
||||
dll: fuzzer-dll zstreamtest-dll zbufftest-dll
|
||||
dll: fuzzer-dll zstreamtest-dll
|
||||
|
||||
zstd:
|
||||
$(MAKE) -C $(PRGDIR) $@
|
||||
@@ -108,11 +108,11 @@ fullbench-dll: $(PRGDIR)/datagen.c fullbench.c
|
||||
$(MAKE) -C $(ZSTDDIR) libzstd
|
||||
$(CC) $(FLAGS) $^ -o $@$(EXT) -DZSTD_DLL_IMPORT=1 $(ZSTDDIR)/dll/libzstd.dll
|
||||
|
||||
fuzzer : $(ZSTD_FILES) $(ZDICT_FILES) $(PRGDIR)/datagen.c fuzzer.c
|
||||
$(CC) $(FLAGS) $^ -o $@$(EXT)
|
||||
|
||||
fuzzer32 : $(ZSTD_FILES) $(ZDICT_FILES) $(PRGDIR)/datagen.c fuzzer.c
|
||||
$(CC) -m32 $(FLAGS) $^ -o $@$(EXT)
|
||||
fuzzer : CPPFLAGS += $(MULTITHREAD_CPP)
|
||||
fuzzer : LDFLAGS += $(MULTITHREAD_LD)
|
||||
fuzzer32: CFLAGS += -m32
|
||||
fuzzer fuzzer32 : $(ZSTD_FILES) $(ZDICT_FILES) $(PRGDIR)/datagen.c fuzzer.c
|
||||
$(CC) $(FLAGS) $^ -o $@$(EXT)
|
||||
|
||||
fuzzer-dll : LDFLAGS+= -L$(ZSTDDIR) -lzstd
|
||||
fuzzer-dll : $(ZSTDDIR)/common/xxhash.c $(PRGDIR)/datagen.c fuzzer.c
|
||||
@@ -192,7 +192,7 @@ else
|
||||
$(CC) $(FLAGS) $^ -o $@$(EXT) -Wl,-rpath=$(ZSTDDIR) $(ZSTDDIR)/libzstd.so
|
||||
endif
|
||||
|
||||
pool : pool.c $(ZSTDDIR)/common/pool.c $(ZSTDDIR)/common/threading.c
|
||||
poolTests : poolTests.c $(ZSTDDIR)/common/pool.c $(ZSTDDIR)/common/threading.c
|
||||
$(CC) $(FLAGS) $(MULTITHREAD) $^ -o $@$(EXT)
|
||||
|
||||
namespaceTest:
|
||||
@@ -213,7 +213,7 @@ clean:
|
||||
fuzzer-dll$(EXT) zstreamtest-dll$(EXT) zbufftest-dll$(EXT)\
|
||||
zstreamtest$(EXT) zstreamtest32$(EXT) \
|
||||
datagen$(EXT) paramgrill$(EXT) roundTripCrash$(EXT) longmatch$(EXT) \
|
||||
symbols$(EXT) invalidDictionaries$(EXT) legacy$(EXT) pool$(EXT) \
|
||||
symbols$(EXT) invalidDictionaries$(EXT) legacy$(EXT) poolTests$(EXT) \
|
||||
decodecorpus$(EXT)
|
||||
@echo Cleaning completed
|
||||
|
||||
@@ -280,7 +280,7 @@ test-zstd: zstd zstd-playTests
|
||||
test-zstd32: ZSTD = $(PRGDIR)/zstd32
|
||||
test-zstd32: zstd32 zstd-playTests
|
||||
|
||||
test-zstd-nolegacy: ZSTD = $(PRGDIR)/zstd
|
||||
test-zstd-nolegacy: ZSTD = $(PRGDIR)/zstd-nolegacy
|
||||
test-zstd-nolegacy: zstd-nolegacy zstd-playTests
|
||||
|
||||
test-gzstd: gzstd
|
||||
@@ -375,7 +375,7 @@ test-decodecorpus-cli: decodecorpus
|
||||
cd ..
|
||||
@rm -rf testdir
|
||||
|
||||
test-pool: pool
|
||||
$(QEMU_SYS) ./pool
|
||||
test-pool: poolTests
|
||||
$(QEMU_SYS) ./poolTests
|
||||
|
||||
endif
|
||||
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
Binary file not shown.
+4
-11
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
@@ -91,12 +91,6 @@ static size_t BMK_findMaxMem(U64 requiredMem)
|
||||
/*_*******************************************************
|
||||
* Benchmark wrappers
|
||||
*********************************************************/
|
||||
typedef struct {
|
||||
blockType_e blockType;
|
||||
U32 unusedBits;
|
||||
U32 origSize;
|
||||
} blockProperties_t;
|
||||
|
||||
size_t local_ZSTD_compress(void* dst, size_t dstSize, void* buff2, const void* src, size_t srcSize)
|
||||
{
|
||||
(void)buff2;
|
||||
@@ -120,7 +114,6 @@ size_t local_ZSTD_decodeLiteralsBlock(void* dst, size_t dstSize, void* buff2, co
|
||||
return ZSTD_decodeLiteralsBlock((ZSTD_DCtx*)g_zdc, buff2, g_cSize);
|
||||
}
|
||||
|
||||
extern size_t ZSTD_getcBlockSize(const void* src, size_t srcSize, blockProperties_t* bpPtr);
|
||||
extern size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeq, const void* src, size_t srcSize);
|
||||
size_t local_ZSTD_decodeSeqHeaders(void* dst, size_t dstSize, void* buff2, const void* src, size_t srcSize)
|
||||
{
|
||||
|
||||
+275
-30
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
@@ -51,14 +51,14 @@ static const U32 nbTestsDefault = 30000;
|
||||
/*-************************************
|
||||
* Display Macros
|
||||
**************************************/
|
||||
#define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
|
||||
#define DISPLAY(...) fprintf(stdout, __VA_ARGS__)
|
||||
#define DISPLAYLEVEL(l, ...) if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); }
|
||||
static U32 g_displayLevel = 2;
|
||||
|
||||
#define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \
|
||||
if ((FUZ_clockSpan(g_displayClock) > g_refreshRate) || (g_displayLevel>=4)) \
|
||||
{ g_displayClock = clock(); DISPLAY(__VA_ARGS__); \
|
||||
if (g_displayLevel>=4) fflush(stderr); } }
|
||||
if (g_displayLevel>=4) fflush(stdout); } }
|
||||
static const clock_t g_refreshRate = CLOCKS_PER_SEC / 6;
|
||||
static clock_t g_displayClock = 0;
|
||||
|
||||
@@ -97,7 +97,165 @@ static unsigned FUZ_highbit32(U32 v32)
|
||||
|
||||
|
||||
/*=============================================
|
||||
* Basic Unit tests
|
||||
* Memory Tests
|
||||
=============================================*/
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
|
||||
#include <malloc/malloc.h> /* malloc_size */
|
||||
|
||||
typedef struct {
|
||||
unsigned long long totalMalloc;
|
||||
size_t currentMalloc;
|
||||
size_t peakMalloc;
|
||||
unsigned nbMalloc;
|
||||
unsigned nbFree;
|
||||
} mallocCounter_t;
|
||||
|
||||
static const mallocCounter_t INIT_MALLOC_COUNTER = { 0, 0, 0, 0, 0 };
|
||||
|
||||
static void* FUZ_mallocDebug(void* counter, size_t size)
|
||||
{
|
||||
mallocCounter_t* const mcPtr = (mallocCounter_t*)counter;
|
||||
void* const ptr = malloc(size);
|
||||
if (ptr==NULL) return NULL;
|
||||
DISPLAYLEVEL(4, "allocating %u KB => effectively %u KB \n",
|
||||
(U32)(size >> 10), (U32)(malloc_size(ptr) >> 10)); /* OS-X specific */
|
||||
mcPtr->totalMalloc += size;
|
||||
mcPtr->currentMalloc += size;
|
||||
if (mcPtr->currentMalloc > mcPtr->peakMalloc)
|
||||
mcPtr->peakMalloc = mcPtr->currentMalloc;
|
||||
mcPtr->nbMalloc += 1;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void FUZ_freeDebug(void* counter, void* address)
|
||||
{
|
||||
mallocCounter_t* const mcPtr = (mallocCounter_t*)counter;
|
||||
DISPLAYLEVEL(4, "freeing %u KB \n", (U32)(malloc_size(address) >> 10));
|
||||
mcPtr->nbFree += 1;
|
||||
mcPtr->currentMalloc -= malloc_size(address); /* OS-X specific */
|
||||
free(address);
|
||||
}
|
||||
|
||||
static void FUZ_displayMallocStats(mallocCounter_t count)
|
||||
{
|
||||
DISPLAYLEVEL(3, "peak:%6u KB, nbMallocs:%2u, total:%6u KB \n",
|
||||
(U32)(count.peakMalloc >> 10),
|
||||
count.nbMalloc,
|
||||
(U32)(count.totalMalloc >> 10));
|
||||
}
|
||||
|
||||
#define CHECK_Z(f) { \
|
||||
size_t const err = f; \
|
||||
if (ZSTD_isError(err)) { \
|
||||
DISPLAY("Error => %s : %s ", \
|
||||
#f, ZSTD_getErrorName(err)); \
|
||||
exit(1); \
|
||||
} }
|
||||
|
||||
static int FUZ_mallocTests(unsigned seed, double compressibility, unsigned part)
|
||||
{
|
||||
size_t const inSize = 64 MB + 16 MB + 4 MB + 1 MB + 256 KB + 64 KB; /* 85.3 MB */
|
||||
size_t const outSize = ZSTD_compressBound(inSize);
|
||||
void* const inBuffer = malloc(inSize);
|
||||
void* const outBuffer = malloc(outSize);
|
||||
|
||||
/* test only played in verbose mode, as they are long */
|
||||
if (g_displayLevel<3) return 0;
|
||||
|
||||
/* Create compressible noise */
|
||||
if (!inBuffer || !outBuffer) {
|
||||
DISPLAY("Not enough memory, aborting\n");
|
||||
exit(1);
|
||||
}
|
||||
RDG_genBuffer(inBuffer, inSize, compressibility, 0. /*auto*/, seed);
|
||||
|
||||
/* simple compression tests */
|
||||
if (part <= 1)
|
||||
{ int compressionLevel;
|
||||
for (compressionLevel=1; compressionLevel<=6; compressionLevel++) {
|
||||
mallocCounter_t malcount = INIT_MALLOC_COUNTER;
|
||||
ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount };
|
||||
ZSTD_CCtx* const cctx = ZSTD_createCCtx_advanced(cMem);
|
||||
CHECK_Z( ZSTD_compressCCtx(cctx, outBuffer, outSize, inBuffer, inSize, compressionLevel) );
|
||||
ZSTD_freeCCtx(cctx);
|
||||
DISPLAYLEVEL(3, "compressCCtx level %i : ", compressionLevel);
|
||||
FUZ_displayMallocStats(malcount);
|
||||
} }
|
||||
|
||||
/* streaming compression tests */
|
||||
if (part <= 2)
|
||||
{ int compressionLevel;
|
||||
for (compressionLevel=1; compressionLevel<=6; compressionLevel++) {
|
||||
mallocCounter_t malcount = INIT_MALLOC_COUNTER;
|
||||
ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount };
|
||||
ZSTD_CCtx* const cstream = ZSTD_createCStream_advanced(cMem);
|
||||
ZSTD_outBuffer out = { outBuffer, outSize, 0 };
|
||||
ZSTD_inBuffer in = { inBuffer, inSize, 0 };
|
||||
CHECK_Z( ZSTD_initCStream(cstream, compressionLevel) );
|
||||
CHECK_Z( ZSTD_compressStream(cstream, &out, &in) );
|
||||
CHECK_Z( ZSTD_endStream(cstream, &out) );
|
||||
ZSTD_freeCStream(cstream);
|
||||
DISPLAYLEVEL(3, "compressStream level %i : ", compressionLevel);
|
||||
FUZ_displayMallocStats(malcount);
|
||||
} }
|
||||
|
||||
/* advanced MT API test */
|
||||
if (part <= 3)
|
||||
{ U32 nbThreads;
|
||||
for (nbThreads=1; nbThreads<=4; nbThreads++) {
|
||||
int compressionLevel;
|
||||
for (compressionLevel=1; compressionLevel<=6; compressionLevel++) {
|
||||
mallocCounter_t malcount = INIT_MALLOC_COUNTER;
|
||||
ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount };
|
||||
ZSTD_CCtx* const cctx = ZSTD_createCCtx_advanced(cMem);
|
||||
ZSTD_outBuffer out = { outBuffer, outSize, 0 };
|
||||
ZSTD_inBuffer in = { inBuffer, inSize, 0 };
|
||||
CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_p_compressionLevel, (U32)compressionLevel) );
|
||||
CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_p_nbThreads, nbThreads) );
|
||||
while ( ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_end) ) {}
|
||||
ZSTD_freeCCtx(cctx);
|
||||
DISPLAYLEVEL(3, "compress_generic,-T%u,end level %i : ",
|
||||
nbThreads, compressionLevel);
|
||||
FUZ_displayMallocStats(malcount);
|
||||
} } }
|
||||
|
||||
/* advanced MT streaming API test */
|
||||
if (part <= 4)
|
||||
{ U32 nbThreads;
|
||||
for (nbThreads=1; nbThreads<=4; nbThreads++) {
|
||||
int compressionLevel;
|
||||
for (compressionLevel=1; compressionLevel<=6; compressionLevel++) {
|
||||
mallocCounter_t malcount = INIT_MALLOC_COUNTER;
|
||||
ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount };
|
||||
ZSTD_CCtx* const cctx = ZSTD_createCCtx_advanced(cMem);
|
||||
ZSTD_outBuffer out = { outBuffer, outSize, 0 };
|
||||
ZSTD_inBuffer in = { inBuffer, inSize, 0 };
|
||||
CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_p_compressionLevel, (U32)compressionLevel) );
|
||||
CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_p_nbThreads, nbThreads) );
|
||||
CHECK_Z( ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_continue) );
|
||||
while ( ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_end) ) {}
|
||||
ZSTD_freeCCtx(cctx);
|
||||
DISPLAYLEVEL(3, "compress_generic,-T%u,continue level %i : ",
|
||||
nbThreads, compressionLevel);
|
||||
FUZ_displayMallocStats(malcount);
|
||||
} } }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int FUZ_mallocTests(unsigned seed, double compressibility, unsigned part)
|
||||
{
|
||||
(void)seed; (void)compressibility; (void)part;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*=============================================
|
||||
* Unit tests
|
||||
=============================================*/
|
||||
|
||||
#define CHECK_V(var, fn) size_t const var = fn; if (ZSTD_isError(var)) goto _output_error
|
||||
@@ -108,7 +266,8 @@ static int basicUnitTests(U32 seed, double compressibility)
|
||||
{
|
||||
size_t const CNBuffSize = 5 MB;
|
||||
void* const CNBuffer = malloc(CNBuffSize);
|
||||
void* const compressedBuffer = malloc(ZSTD_compressBound(CNBuffSize));
|
||||
size_t const compressedBufferSize = ZSTD_compressBound(CNBuffSize);
|
||||
void* const compressedBuffer = malloc(compressedBufferSize);
|
||||
void* const decodedBuffer = malloc(CNBuffSize);
|
||||
ZSTD_DCtx* dctx = ZSTD_createDCtx();
|
||||
int testResult = 0;
|
||||
@@ -136,10 +295,20 @@ static int basicUnitTests(U32 seed, double compressibility)
|
||||
|
||||
|
||||
DISPLAYLEVEL(4, "test%3i : compress %u bytes : ", testNb++, (U32)CNBuffSize);
|
||||
CHECKPLUS(r, ZSTD_compress(compressedBuffer, ZSTD_compressBound(CNBuffSize),
|
||||
CNBuffer, CNBuffSize, 1),
|
||||
cSize=r );
|
||||
DISPLAYLEVEL(4, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100);
|
||||
{ ZSTD_CCtx* cctx = ZSTD_createCCtx();
|
||||
if (cctx==NULL) goto _output_error;
|
||||
CHECKPLUS(r, ZSTD_compressCCtx(cctx,
|
||||
compressedBuffer, compressedBufferSize,
|
||||
CNBuffer, CNBuffSize, 1),
|
||||
cSize=r );
|
||||
DISPLAYLEVEL(4, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100);
|
||||
|
||||
DISPLAYLEVEL(4, "test%3i : size of cctx for level 1 : ", testNb++);
|
||||
{ size_t const cctxSize = ZSTD_sizeof_CCtx(cctx);
|
||||
DISPLAYLEVEL(4, "%u bytes \n", (U32)cctxSize);
|
||||
}
|
||||
ZSTD_freeCCtx(cctx);
|
||||
}
|
||||
|
||||
|
||||
DISPLAYLEVEL(4, "test%3i : ZSTD_getFrameContentSize test : ", testNb++);
|
||||
@@ -216,7 +385,7 @@ static int basicUnitTests(U32 seed, double compressibility)
|
||||
|
||||
DISPLAYLEVEL(4, "test%3i : simple compression test with static CCtx : ", testNb++);
|
||||
CHECKPLUS(r, ZSTD_compressCCtx(staticCCtx,
|
||||
compressedBuffer, ZSTD_compressBound(CNBuffSize),
|
||||
compressedBuffer, compressedBufferSize,
|
||||
CNBuffer, CNBuffSize, STATIC_CCTX_LEVEL),
|
||||
cSize=r );
|
||||
DISPLAYLEVEL(4, "OK (%u bytes : %.2f%%)\n",
|
||||
@@ -285,7 +454,7 @@ static int basicUnitTests(U32 seed, double compressibility)
|
||||
|
||||
DISPLAYLEVEL(4, "test%3i : compress %u bytes with 2 threads : ", testNb++, (U32)CNBuffSize);
|
||||
CHECKPLUS(r, ZSTDMT_compressCCtx(mtctx,
|
||||
compressedBuffer, ZSTD_compressBound(CNBuffSize),
|
||||
compressedBuffer, compressedBufferSize,
|
||||
CNBuffer, CNBuffSize,
|
||||
1),
|
||||
cSize=r );
|
||||
@@ -311,6 +480,23 @@ static int basicUnitTests(U32 seed, double compressibility)
|
||||
} }
|
||||
DISPLAYLEVEL(4, "OK \n");
|
||||
|
||||
DISPLAYLEVEL(4, "test%3i : compress -T2 with checksum : ", testNb++);
|
||||
{ ZSTD_parameters params = ZSTD_getParams(1, CNBuffSize, 0);
|
||||
params.fParams.checksumFlag = 1;
|
||||
params.fParams.contentSizeFlag = 1;
|
||||
CHECKPLUS(r, ZSTDMT_compress_advanced(mtctx,
|
||||
compressedBuffer, compressedBufferSize,
|
||||
CNBuffer, CNBuffSize,
|
||||
NULL, params, 3 /*overlapRLog*/),
|
||||
cSize=r );
|
||||
}
|
||||
DISPLAYLEVEL(4, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100);
|
||||
|
||||
DISPLAYLEVEL(4, "test%3i : decompress %u bytes : ", testNb++, (U32)CNBuffSize);
|
||||
{ size_t const r = ZSTD_decompress(decodedBuffer, CNBuffSize, compressedBuffer, cSize);
|
||||
if (r != CNBuffSize) goto _output_error; }
|
||||
DISPLAYLEVEL(4, "OK \n");
|
||||
|
||||
ZSTDMT_freeCCtx(mtctx);
|
||||
}
|
||||
|
||||
@@ -372,7 +558,7 @@ static int basicUnitTests(U32 seed, double compressibility)
|
||||
|
||||
DISPLAYLEVEL(4, "test%3i : compress with flat dictionary : ", testNb++);
|
||||
cSize = 0;
|
||||
CHECKPLUS(r, ZSTD_compressEnd(ctxOrig, compressedBuffer, ZSTD_compressBound(CNBuffSize),
|
||||
CHECKPLUS(r, ZSTD_compressEnd(ctxOrig, compressedBuffer, compressedBufferSize,
|
||||
(const char*)CNBuffer + dictSize, CNBuffSize - dictSize),
|
||||
cSize += r);
|
||||
DISPLAYLEVEL(4, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100);
|
||||
@@ -388,7 +574,7 @@ static int basicUnitTests(U32 seed, double compressibility)
|
||||
DISPLAYLEVEL(4, "test%3i : compress with duplicated context : ", testNb++);
|
||||
{ size_t const cSizeOrig = cSize;
|
||||
cSize = 0;
|
||||
CHECKPLUS(r, ZSTD_compressEnd(ctxDuplicated, compressedBuffer, ZSTD_compressBound(CNBuffSize),
|
||||
CHECKPLUS(r, ZSTD_compressEnd(ctxDuplicated, compressedBuffer, compressedBufferSize,
|
||||
(const char*)CNBuffer + dictSize, CNBuffSize - dictSize),
|
||||
cSize += r);
|
||||
if (cSize != cSizeOrig) goto _output_error; /* should be identical ==> same size */
|
||||
@@ -434,9 +620,9 @@ static int basicUnitTests(U32 seed, double compressibility)
|
||||
CHECKPLUS(r, ZSTD_compressEnd(ctxDuplicated, compressedBuffer, ZSTD_compressBound(testSize),
|
||||
(const char*)CNBuffer + dictSize, testSize),
|
||||
cSize = r);
|
||||
{ ZSTD_frameHeader fp;
|
||||
if (ZSTD_getFrameHeader(&fp, compressedBuffer, cSize)) goto _output_error;
|
||||
if ((fp.frameContentSize != testSize) && (fp.frameContentSize != 0)) goto _output_error;
|
||||
{ ZSTD_frameHeader zfh;
|
||||
if (ZSTD_getFrameHeader(&zfh, compressedBuffer, cSize)) goto _output_error;
|
||||
if ((zfh.frameContentSize != testSize) && (zfh.frameContentSize != 0)) goto _output_error;
|
||||
} }
|
||||
DISPLAYLEVEL(4, "OK \n");
|
||||
|
||||
@@ -473,7 +659,7 @@ static int basicUnitTests(U32 seed, double compressibility)
|
||||
DISPLAYLEVEL(4, "OK : %u \n", dictID);
|
||||
|
||||
DISPLAYLEVEL(4, "test%3i : compress with dictionary : ", testNb++);
|
||||
cSize = ZSTD_compress_usingDict(cctx, compressedBuffer, ZSTD_compressBound(CNBuffSize),
|
||||
cSize = ZSTD_compress_usingDict(cctx, compressedBuffer, compressedBufferSize,
|
||||
CNBuffer, CNBuffSize,
|
||||
dictBuffer, dictSize, 4);
|
||||
if (ZSTD_isError(cSize)) goto _output_error;
|
||||
@@ -511,7 +697,7 @@ static int basicUnitTests(U32 seed, double compressibility)
|
||||
1 /* byReference */, ZSTD_dm_auto,
|
||||
cParams, ZSTD_defaultCMem);
|
||||
DISPLAYLEVEL(4, "(size : %u) : ", (U32)ZSTD_sizeof_CDict(cdict));
|
||||
cSize = ZSTD_compress_usingCDict(cctx, compressedBuffer, ZSTD_compressBound(CNBuffSize),
|
||||
cSize = ZSTD_compress_usingCDict(cctx, compressedBuffer, compressedBufferSize,
|
||||
CNBuffer, CNBuffSize, cdict);
|
||||
ZSTD_freeCDict(cdict);
|
||||
if (ZSTD_isError(cSize)) goto _output_error;
|
||||
@@ -546,7 +732,7 @@ static int basicUnitTests(U32 seed, double compressibility)
|
||||
goto _output_error;
|
||||
}
|
||||
cSize = ZSTD_compress_usingCDict(cctx,
|
||||
compressedBuffer, ZSTD_compressBound(CNBuffSize),
|
||||
compressedBuffer, compressedBufferSize,
|
||||
CNBuffer, CNBuffSize, cdict);
|
||||
if (ZSTD_isError(cSize)) {
|
||||
DISPLAY("ZSTD_compress_usingCDict failed ");
|
||||
@@ -560,7 +746,7 @@ static int basicUnitTests(U32 seed, double compressibility)
|
||||
{ ZSTD_frameParameters const fParams = { 0 /* frameSize */, 1 /* checksum */, 1 /* noDictID*/ };
|
||||
ZSTD_compressionParameters const cParams = ZSTD_getCParams(1, CNBuffSize, dictSize);
|
||||
ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictBuffer, dictSize, 1 /*byRef*/, ZSTD_dm_auto, cParams, ZSTD_defaultCMem);
|
||||
cSize = ZSTD_compress_usingCDict_advanced(cctx, compressedBuffer, ZSTD_compressBound(CNBuffSize),
|
||||
cSize = ZSTD_compress_usingCDict_advanced(cctx, compressedBuffer, compressedBufferSize,
|
||||
CNBuffer, CNBuffSize, cdict, fParams);
|
||||
ZSTD_freeCDict(cdict);
|
||||
if (ZSTD_isError(cSize)) goto _output_error;
|
||||
@@ -584,7 +770,7 @@ static int basicUnitTests(U32 seed, double compressibility)
|
||||
DISPLAYLEVEL(4, "test%3i : ZSTD_compress_advanced, no dictID : ", testNb++);
|
||||
{ ZSTD_parameters p = ZSTD_getParams(3, CNBuffSize, dictSize);
|
||||
p.fParams.noDictIDFlag = 1;
|
||||
cSize = ZSTD_compress_advanced(cctx, compressedBuffer, ZSTD_compressBound(CNBuffSize),
|
||||
cSize = ZSTD_compress_advanced(cctx, compressedBuffer, compressedBufferSize,
|
||||
CNBuffer, CNBuffSize,
|
||||
dictBuffer, dictSize, p);
|
||||
if (ZSTD_isError(cSize)) goto _output_error;
|
||||
@@ -821,6 +1007,42 @@ static int basicUnitTests(U32 seed, double compressibility)
|
||||
if (r != _3BYTESTESTLENGTH) goto _output_error; }
|
||||
DISPLAYLEVEL(4, "OK \n");
|
||||
|
||||
DISPLAYLEVEL(4, "test%3i : incompressible data and ill suited dictionary : ", testNb++);
|
||||
RDG_genBuffer(CNBuffer, CNBuffSize, 0.0, 0.1, seed);
|
||||
{ /* Train a dictionary on low characters */
|
||||
size_t dictSize = 16 KB;
|
||||
void* const dictBuffer = malloc(dictSize);
|
||||
size_t const totalSampleSize = 1 MB;
|
||||
size_t const sampleUnitSize = 8 KB;
|
||||
U32 const nbSamples = (U32)(totalSampleSize / sampleUnitSize);
|
||||
size_t* const samplesSizes = (size_t*) malloc(nbSamples * sizeof(size_t));
|
||||
if (!dictBuffer || !samplesSizes) goto _output_error;
|
||||
{ U32 u; for (u=0; u<nbSamples; u++) samplesSizes[u] = sampleUnitSize; }
|
||||
dictSize = ZDICT_trainFromBuffer(dictBuffer, dictSize, CNBuffer, samplesSizes, nbSamples);
|
||||
if (ZDICT_isError(dictSize)) goto _output_error;
|
||||
/* Reverse the characters to make the dictionary ill suited */
|
||||
{ U32 u;
|
||||
for (u = 0; u < CNBuffSize; ++u) {
|
||||
((BYTE*)CNBuffer)[u] = 255 - ((BYTE*)CNBuffer)[u];
|
||||
}
|
||||
}
|
||||
{ /* Compress the data */
|
||||
size_t const inputSize = 500;
|
||||
size_t const outputSize = ZSTD_compressBound(inputSize);
|
||||
void* const outputBuffer = malloc(outputSize);
|
||||
ZSTD_CCtx* const cctx = ZSTD_createCCtx();
|
||||
if (!outputBuffer || !cctx) goto _output_error;
|
||||
CHECK(ZSTD_compress_usingDict(cctx, outputBuffer, outputSize, CNBuffer, inputSize, dictBuffer, dictSize, 1));
|
||||
free(outputBuffer);
|
||||
ZSTD_freeCCtx(cctx);
|
||||
}
|
||||
|
||||
free(dictBuffer);
|
||||
free(samplesSizes);
|
||||
}
|
||||
DISPLAYLEVEL(4, "OK \n");
|
||||
|
||||
|
||||
/* findFrameCompressedSize on skippable frames */
|
||||
DISPLAYLEVEL(4, "test%3i : frame compressed size of skippable frame : ", testNb++);
|
||||
{ const char* frame = "\x50\x2a\x4d\x18\x05\x0\x0\0abcde";
|
||||
@@ -892,6 +1114,7 @@ static size_t FUZ_randomLength(U32* seed, U32 maxLog)
|
||||
goto _output_error; \
|
||||
} }
|
||||
|
||||
#undef CHECK_Z
|
||||
#define CHECK_Z(f) { \
|
||||
size_t const err = f; \
|
||||
if (ZSTD_isError(err)) { \
|
||||
@@ -1006,17 +1229,17 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, U32 const maxD
|
||||
CHECK(endCheck != endMark, "ZSTD_compressCCtx : dst buffer overflow"); }
|
||||
} }
|
||||
|
||||
/* frame header decompression test */
|
||||
{ ZSTD_frameHeader zfh;
|
||||
CHECK_Z( ZSTD_getFrameHeader(&zfh, cBuffer, cSize) );
|
||||
CHECK(zfh.frameContentSize != sampleSize, "Frame content size incorrect");
|
||||
}
|
||||
|
||||
/* Decompressed size test */
|
||||
{ unsigned long long const rSize = ZSTD_findDecompressedSize(cBuffer, cSize);
|
||||
CHECK(rSize != sampleSize, "decompressed size incorrect");
|
||||
}
|
||||
|
||||
/* frame header decompression test */
|
||||
{ ZSTD_frameHeader dParams;
|
||||
CHECK_Z( ZSTD_getFrameHeader(&dParams, cBuffer, cSize) );
|
||||
CHECK(dParams.frameContentSize != sampleSize, "Frame content size incorrect");
|
||||
}
|
||||
|
||||
/* successful decompression test */
|
||||
{ size_t const margin = (FUZ_rand(&lseed) & 1) ? 0 : (FUZ_rand(&lseed) & 31) + 1;
|
||||
size_t const dSize = ZSTD_decompress(dstBuffer, sampleSize + margin, cBuffer, cSize);
|
||||
@@ -1223,6 +1446,19 @@ static unsigned readU32FromChar(const char** stringPtr)
|
||||
return result;
|
||||
}
|
||||
|
||||
/** longCommandWArg() :
|
||||
* check if *stringPtr is the same as longCommand.
|
||||
* If yes, @return 1 and advances *stringPtr to the position which immediately follows longCommand.
|
||||
* @return 0 and doesn't modify *stringPtr otherwise.
|
||||
*/
|
||||
static unsigned longCommandWArg(const char** stringPtr, const char* longCommand)
|
||||
{
|
||||
size_t const comSize = strlen(longCommand);
|
||||
int const result = !strncmp(*stringPtr, longCommand, comSize);
|
||||
if (result) *stringPtr += comSize;
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(int argc, const char** argv)
|
||||
{
|
||||
U32 seed = 0;
|
||||
@@ -1235,6 +1471,7 @@ int main(int argc, const char** argv)
|
||||
U32 mainPause = 0;
|
||||
U32 maxDuration = 0;
|
||||
int bigTests = 1;
|
||||
U32 memTestsOnly = 0;
|
||||
const char* const programName = argv[0];
|
||||
|
||||
/* Check command line */
|
||||
@@ -1245,6 +1482,9 @@ int main(int argc, const char** argv)
|
||||
/* Handle commands. Aggregated commands are allowed */
|
||||
if (argument[0]=='-') {
|
||||
|
||||
if (longCommandWArg(&argument, "--memtest=")) { memTestsOnly = readU32FromChar(&argument); continue; }
|
||||
|
||||
if (!strcmp(argument, "--memtest")) { memTestsOnly=1; continue; }
|
||||
if (!strcmp(argument, "--no-big-tests")) { bigTests=0; continue; }
|
||||
|
||||
argument++;
|
||||
@@ -1316,6 +1556,11 @@ int main(int argc, const char** argv)
|
||||
DISPLAY("Seed = %u\n", seed);
|
||||
if (proba!=FUZ_compressibility_default) DISPLAY("Compressibility : %u%%\n", proba);
|
||||
|
||||
if (memTestsOnly) {
|
||||
g_displayLevel = MAX(3, g_displayLevel);
|
||||
return FUZ_mallocTests(seed, ((double)proba) / 100, memTestsOnly);
|
||||
}
|
||||
|
||||
if (nbTests < testNb) nbTests = testNb;
|
||||
|
||||
if (testNb==0)
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
/*
|
||||
* 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).
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include "zstd.h"
|
||||
|
||||
|
||||
+5
-6
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -226,4 +226,3 @@ const char* const EXPECTED =
|
||||
"snowden is snowed in / he's now then in his snow den / when does the snow end?\n"
|
||||
"goodbye little dog / you dug some holes in your day / they'll be hard to fill.\n"
|
||||
"when life shuts a door, / just open it. it’s a door. / that is how doors work.\n";
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
+25
-17
@@ -7,17 +7,17 @@ die() {
|
||||
|
||||
roundTripTest() {
|
||||
if [ -n "$3" ]; then
|
||||
local_c="$3"
|
||||
local_p="$2"
|
||||
cLevel="$3"
|
||||
proba="$2"
|
||||
else
|
||||
local_c="$2"
|
||||
local_p=""
|
||||
cLevel="$2"
|
||||
proba=""
|
||||
fi
|
||||
|
||||
rm -f tmp1 tmp2
|
||||
$ECHO "roundTripTest: ./datagen $1 $local_p | $ZSTD -v$local_c | $ZSTD -d"
|
||||
./datagen $1 $local_p | $MD5SUM > tmp1
|
||||
./datagen $1 $local_p | $ZSTD --ultra -v$local_c | $ZSTD -d | $MD5SUM > tmp2
|
||||
$ECHO "roundTripTest: ./datagen $1 $proba | $ZSTD -v$cLevel | $ZSTD -d"
|
||||
./datagen $1 $proba | $MD5SUM > tmp1
|
||||
./datagen $1 $proba | $ZSTD --ultra -v$cLevel | $ZSTD -d | $MD5SUM > tmp2
|
||||
$DIFF -q tmp1 tmp2
|
||||
}
|
||||
|
||||
@@ -383,6 +383,14 @@ $ZSTD -t --rm tmp1.zst
|
||||
test -f tmp1.zst # check file is still present
|
||||
split -b16384 tmp1.zst tmpSplit.
|
||||
$ZSTD -t tmpSplit.* && die "bad file not detected !"
|
||||
./datagen | $ZSTD -c | $ZSTD -t
|
||||
|
||||
|
||||
|
||||
$ECHO "\n**** golden files tests **** "
|
||||
|
||||
$ZSTD -t -r files
|
||||
$ZSTD -c -r files | $ZSTD -t
|
||||
|
||||
|
||||
$ECHO "\n**** benchmark mode tests **** "
|
||||
@@ -625,16 +633,15 @@ roundTripTest -g35000000 -P75 10
|
||||
roundTripTest -g35000000 -P75 11
|
||||
roundTripTest -g35000000 -P75 12
|
||||
|
||||
roundTripTest -g18000000 -P80 13
|
||||
roundTripTest -g18000000 -P80 14
|
||||
roundTripTest -g18000000 -P80 15
|
||||
roundTripTest -g18000000 -P80 16
|
||||
roundTripTest -g18000000 -P80 17
|
||||
roundTripTest -g18000013 -P80 13
|
||||
roundTripTest -g18000014 -P80 14
|
||||
roundTripTest -g18000015 -P80 15
|
||||
roundTripTest -g18000016 -P80 16
|
||||
roundTripTest -g18000017 -P80 17
|
||||
roundTripTest -g18000018 -P94 18
|
||||
roundTripTest -g18000019 -P94 19
|
||||
|
||||
roundTripTest -g50000000 -P94 18
|
||||
roundTripTest -g50000000 -P94 19
|
||||
|
||||
roundTripTest -g99000000 -P99 20
|
||||
roundTripTest -g68000020 -P99 20
|
||||
roundTripTest -g6000000000 -P99 1
|
||||
|
||||
fileRoundTripTest -g4193M -P99 1
|
||||
@@ -644,7 +651,8 @@ then
|
||||
$ECHO "\n**** zstdmt long round-trip tests **** "
|
||||
roundTripTest -g99000000 -P99 "20 -T2"
|
||||
roundTripTest -g6000000000 -P99 "1 -T2"
|
||||
fileRoundTripTest -g4193M -P98 " -T0"
|
||||
roundTripTest -g1500000000 -P97 "1 -T999"
|
||||
fileRoundTripTest -g4195M -P98 " -T0"
|
||||
else
|
||||
$ECHO "\n**** no multithreading, skipping zstdmt tests **** "
|
||||
fi
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
/*
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
#include "pool.h"
|
||||
#include "threading.h"
|
||||
#include "util.h"
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -50,21 +61,45 @@ int testOrder(size_t numThreads, size_t queueSize) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void waitFn(void *opaque) {
|
||||
(void)opaque;
|
||||
UTIL_sleepMilli(1);
|
||||
}
|
||||
|
||||
/* Tests for deadlock */
|
||||
int testWait(size_t numThreads, size_t queueSize) {
|
||||
struct data data;
|
||||
POOL_ctx *ctx = POOL_create(numThreads, queueSize);
|
||||
ASSERT_TRUE(ctx);
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < 16; ++i) {
|
||||
POOL_add(ctx, &waitFn, &data);
|
||||
}
|
||||
}
|
||||
POOL_free(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
size_t numThreads;
|
||||
for (numThreads = 1; numThreads <= 4; ++numThreads) {
|
||||
size_t queueSize;
|
||||
for (queueSize = 1; queueSize <= 2; ++queueSize) {
|
||||
for (queueSize = 0; queueSize <= 2; ++queueSize) {
|
||||
if (testOrder(numThreads, queueSize)) {
|
||||
printf("FAIL: testOrder\n");
|
||||
return 1;
|
||||
}
|
||||
if (testWait(numThreads, queueSize)) {
|
||||
printf("FAIL: testWait\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("PASS: testOrder\n");
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
return (POOL_create(0, 1) || POOL_create(1, 0)) ? printf("FAIL: testInvalid\n"), 1
|
||||
: printf("PASS: testInvalid\n"), 0;
|
||||
return (POOL_create(0, 1)) ? printf("FAIL: testInvalid\n"), 1
|
||||
: printf("PASS: testInvalid\n"), 0;
|
||||
return 0;
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include "zstd_errors.h"
|
||||
#define ZSTD_STATIC_LINKING_ONLY
|
||||
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
+8
-26
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
@@ -95,19 +95,6 @@ unsigned int FUZ_rand(unsigned int* seedPtr)
|
||||
return rand32 >> 5;
|
||||
}
|
||||
|
||||
static void* allocFunction(void* opaque, size_t size)
|
||||
{
|
||||
void* address = malloc(size);
|
||||
(void)opaque;
|
||||
return address;
|
||||
}
|
||||
|
||||
static void freeFunction(void* opaque, void* address)
|
||||
{
|
||||
(void)opaque;
|
||||
free(address);
|
||||
}
|
||||
|
||||
|
||||
/*======================================================
|
||||
* Basic Unit tests
|
||||
@@ -1390,13 +1377,12 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double
|
||||
/* multi-segments compression test */
|
||||
XXH64_reset(&xxhState, 0);
|
||||
{ ZSTD_outBuffer outBuff = { cBuffer, cBufferSize, 0 } ;
|
||||
U32 n;
|
||||
for (n=0, cSize=0, totalTestSize=0 ; totalTestSize < maxTestSize ; n++) {
|
||||
for (cSize=0, totalTestSize=0 ; (totalTestSize < maxTestSize) ; ) {
|
||||
/* compress random chunks into randomly sized dst buffers */
|
||||
size_t const randomSrcSize = FUZ_randomLength(&lseed, maxSampleLog);
|
||||
size_t const srcSize = MIN(maxTestSize-totalTestSize, randomSrcSize);
|
||||
size_t const srcStart = FUZ_rand(&lseed) % (srcBufferSize - srcSize);
|
||||
size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog);
|
||||
size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog+1);
|
||||
size_t const dstBuffSize = MIN(cBufferSize - cSize, randomDstSize);
|
||||
ZSTD_EndDirective const flush = (FUZ_rand(&lseed) & 15) ? ZSTD_e_continue : ZSTD_e_flush;
|
||||
ZSTD_inBuffer inBuff = { srcBuffer+srcStart, srcSize, 0 };
|
||||
@@ -1415,7 +1401,7 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double
|
||||
{ size_t remainingToFlush = (size_t)(-1);
|
||||
while (remainingToFlush) {
|
||||
ZSTD_inBuffer inBuff = { NULL, 0, 0 };
|
||||
size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog);
|
||||
size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog+1);
|
||||
size_t const adjustedDstSize = MIN(cBufferSize - cSize, randomDstSize);
|
||||
outBuff.size = outBuff.pos + adjustedDstSize;
|
||||
DISPLAYLEVEL(5, "End-flush into dst buffer of size %u \n", (U32)adjustedDstSize);
|
||||
@@ -1543,7 +1529,6 @@ int main(int argc, const char** argv)
|
||||
int bigTests = (sizeof(size_t) == 8);
|
||||
e_api selected_api = simple_api;
|
||||
const char* const programName = argv[0];
|
||||
ZSTD_customMem const customMem = { allocFunction, freeFunction, NULL };
|
||||
ZSTD_customMem const customNULL = ZSTD_defaultCMem;
|
||||
|
||||
/* Check command line */
|
||||
@@ -1657,10 +1642,7 @@ int main(int argc, const char** argv)
|
||||
|
||||
if (testNb==0) {
|
||||
result = basicUnitTests(0, ((double)proba) / 100, customNULL); /* constant seed for predictability */
|
||||
if (!result) {
|
||||
DISPLAYLEVEL(3, "Unit tests using customMem :\n")
|
||||
result = basicUnitTests(0, ((double)proba) / 100, customMem); /* use custom memory allocation functions */
|
||||
} }
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
switch(selected_api)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) 2016-present, Yann Collet, Przemyslaw Skibinski, Facebook, Inc.
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) 2016-present, Przemyslaw Skibinski, Facebook, Inc.
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
#if ZLIB_VERNUM <= 0x1240
|
||||
ZEXTERN int ZEXPORT gzclose_r OF((gzFile file));
|
||||
ZEXTERN int ZEXPORT gzclose_w OF((gzFile file));
|
||||
ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size));
|
||||
ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size));
|
||||
ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file));
|
||||
|
||||
|
||||
#if !defined(_WIN32) && defined(Z_LARGE64)
|
||||
# define z_off64_t off64_t
|
||||
#else
|
||||
@@ -38,7 +38,7 @@ struct gzFile_s {
|
||||
|
||||
#if ZLIB_VERNUM <= 0x1270
|
||||
#if defined(_WIN32) && !defined(Z_SOLO)
|
||||
# include <stddef.h> /* for wchar_t */
|
||||
# include <stddef.h> /* for wchar_t */
|
||||
ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path,
|
||||
const char *mode));
|
||||
#endif
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/* gzlib.c contains minimal changes required to be compiled with zlibWrapper:
|
||||
* - gz_statep was converted to union to work with -Wstrict-aliasing=1 */
|
||||
* - gz_statep was converted to union to work with -Wstrict-aliasing=1 */
|
||||
|
||||
/* gzlib.c -- zlib functions common to reading and writing gzip files
|
||||
* Copyright (C) 2004-2017 Mark Adler
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* gzread.c contains minimal changes required to be compiled with zlibWrapper:
|
||||
* - gz_statep was converted to union to work with -Wstrict-aliasing=1 */
|
||||
|
||||
* - gz_statep was converted to union to work with -Wstrict-aliasing=1 */
|
||||
|
||||
/* gzread.c -- zlib functions for reading gzip files
|
||||
* Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler
|
||||
* For conditions of distribution and use, see http://www.zlib.net/zlib_license.html
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* gzwrite.c contains minimal changes required to be compiled with zlibWrapper:
|
||||
* - gz_statep was converted to union to work with -Wstrict-aliasing=1 */
|
||||
|
||||
* - gz_statep was converted to union to work with -Wstrict-aliasing=1 */
|
||||
|
||||
/* gzwrite.c -- zlib functions for writing gzip files
|
||||
* Copyright (C) 2004-2017 Mark Adler
|
||||
* For conditions of distribution and use, see http://www.zlib.net/zlib_license.html
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) 2016-present, Przemyslaw Skibinski, Facebook, Inc.
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) 2016-present, Przemyslaw Skibinski, Facebook, Inc.
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* 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).
|
||||
*/
|
||||
|
||||
#ifndef ZSTD_ZLIBWRAPPER_H
|
||||
@@ -32,7 +32,7 @@ const char * zstdVersion(void);
|
||||
/*** COMPRESSION ***/
|
||||
/* ZWRAP_useZSTDcompression() enables/disables zstd compression during runtime.
|
||||
By default zstd compression is disabled. To enable zstd compression please use one of the methods:
|
||||
- compilation with the additional option -DZWRAP_USE_ZSTD=1
|
||||
- compilation with the additional option -DZWRAP_USE_ZSTD=1
|
||||
- using '#define ZWRAP_USE_ZSTD 1' in source code before '#include "zstd_zlibwrapper.h"'
|
||||
- calling ZWRAP_useZSTDcompression(1)
|
||||
All above-mentioned methods will enable zstd compression for all threads.
|
||||
@@ -45,13 +45,13 @@ int ZWRAP_isUsingZSTDcompression(void);
|
||||
/* Changes a pledged source size for a given compression stream.
|
||||
It will change ZSTD compression parameters what may improve compression speed and/or ratio.
|
||||
The function should be called just after deflateInit() or deflateReset() and before deflate() or deflateSetDictionary().
|
||||
It's only helpful when data is compressed in blocks.
|
||||
There will be no change in case of deflateInit() or deflateReset() immediately followed by deflate(strm, Z_FINISH)
|
||||
It's only helpful when data is compressed in blocks.
|
||||
There will be no change in case of deflateInit() or deflateReset() immediately followed by deflate(strm, Z_FINISH)
|
||||
as this case is automatically detected. */
|
||||
int ZWRAP_setPledgedSrcSize(z_streamp strm, unsigned long long pledgedSrcSize);
|
||||
|
||||
/* Similar to deflateReset but preserves dictionary set using deflateSetDictionary.
|
||||
It should improve compression speed because there will be less calls to deflateSetDictionary
|
||||
It should improve compression speed because there will be less calls to deflateSetDictionary
|
||||
When using zlib compression this method redirects to deflateReset. */
|
||||
int ZWRAP_deflateReset_keepDict(z_streamp strm);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user