commit dcecdd883a73835da28c862c10d906e8cad3fa49 Author: Raph Levien Date: Fri Mar 23 11:21:16 2012 -0700 Initial commit of font compression code into public project. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e74c256 --- /dev/null +++ b/LICENSE @@ -0,0 +1,27 @@ +// Copyright (c) 2011 Google Inc. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README b/README new file mode 100644 index 0000000..88dd8fa --- /dev/null +++ b/README @@ -0,0 +1,155 @@ +This is a README for the font compression reference code. It’s very rough in +this snapshot, but will be cleaned up some for public release. + += How to run the compression test tool = + +This document documents how to run the compression reference code. At this +writing, the code, while it is intended to produce a bytestream that can be +reconstructed into a working font, the reference decompression code is not +done, and the exact format of that bytestream is subject to change. + +== Building the tool == + +On a standard Unix-style environment, it should be as simple as running “ant”. +A couple of paths to compression subprocesses are hardcoded in +CompressionRunner.java, namely “usr/bin/lzma” and “/bin/bzip2”. These are the +default locations in Ubuntu, but if they’re elsewhere on your system, you’ll +need to change that. + +The tool depends on sfntly for much of the font work. The lib/ directory +contains a snapshot jar. If you want to use the latest sfntly sources, then cd +to the java subdirectory, run “ant”, then copy these files dist/lib/sfntly.jar +dist/tools/conversion/eot/eotconverter.jar and +dist.tools/conversion/woff/woffconverter.jar to $(thisproject)/lib: + +dist/lib/sfntly.jar dist/tools/conversion/eot/eotconverter.jar +dist.tools/conversion/woff/woffconverter.jar + +There’s also a dependency on guava (see references below). + +The dependencies are subject to their own licenses. + +== Setting up the test == + +A run of the tool evaluates a “base” configuration plus one or more test +configurations, for each font. It measures the file size of the test as a ratio +over the base file size, then graphs the value of that ratio sorted across all +files given on the command line. + +The test parameters are set by command line options (an improvement from the +last snapshot). The base is set by the -b command line option, and the +additional tests are specified by repeated -x command line options (see below). + +Each test is specified by a string description. It is a colon-separated list of +stages. The final stage is entropy compression and can be one of “gzip”, +“lzma”, “bzip2”, “woff”, “eot” (with actual wire-format MTX compression), or +“uncomp” (for raw, uncompressed TTF’s). Also, the new wire-format draft +WOFF2 spec is available as "woff2", and takes an entropy coding as an +optional argument, as in "woff2/gzip" or "woff2/lzma". + +Other stages may optionally include subparameters (following a slash, and +comma-separated). The stages are: + +glyf: performs glyf-table preprocessing based on MTX. There are subparameters: +1. cbbox (composite bounding box). When specified, the bounding box for +composite glyphs is included, otherwise stripped 2. sbbox (simple bounding +box). When specified, the bounding box for simple glyphs is included 3. code: +the bytecode is separated out into a separate stream 4. triplet: triplet coding +(as in MTX) is used 5. push: push sequences are separated; if unset, pushes are +kept inline in the bytecode 6. reslice: components of the glyf table are +separated into individual streams, taking the MTX idea of separating the +bytecodes further. + +hmtx: strips lsb’s from the hmtx table. Based on the idea that lsb’s can be +reconstructed from bbox. + +hdmx: performs the delta coding on hdmx, essentially the same as MTX. + +cmap: compresses cmap table: wire format representation is inverse of cmap +table plus exceptions (one glyph encoded by multiple character codes). + +kern: compresses kern table (not robust, intended just for rough testing). + +strip: the subparameters are a list of tables to be stripped entirely +(comma-separated). + +The string roughly corresponding to MTX is: + +glyf/cbbox,code,triplet,push,hop:hdmx:gzip + +Meaning: glyph encoding is used, with simple glyph bboxes stripped (but +composite glyph bboxes included), triplet coding, push sequences, and hop +codes. The hdmx table is compressed. And finally, gzip is used as the entropy +coder. + +This differs from MTX in a number of small ways: LZCOMP is not exactly the same +as gzip. MTX uses three separate compression streams (the base font including +triplet-coded glyph data), the bytecodes, and the push sequences, while this +test uses a single stream. MTX also compresses the CVT table (an upper bound on +the impact of this can be estimated by testing strip/cvt) + +Lastly, as a point of methodology, the code by default strips the “dsig” table, +which would be invalidated by any non-bit-identical change to the font data. If +it is desired to keep this table, add the “keepdsig” stage. + +The string representing the currently most aggressive optimization level is: + +glyf/triplet,code,push,reslice:hdmx:hmtx:cmap:kern:lzma + +In addition to the MTX one above, it strips the bboxes from composite glyphs, +reslices the glyf table, compresses the htmx, cmap, and kern tables, and uses +lzma as the entropy coding. + +The string corresponding to the current WOFF Ultra Condensed draft spec +document is: + +glyf/cbbox,triplet,code,reslice:woff2/lzma + +The current C++ codebase can roundtrip compressed files as long as no per-table +entropy coding is specified, as below (this will be fixed soon). + +glyf/cbbox,triplet,code,reslice:woff2 + + +== Running the tool == + +java -jar build/jar/compression.jar *.ttf > chart.html + +The tool takes a list of OpenType fonts on the commandline, and generates an +HTML chart, which it simply outputs to stdout. This chart uses the Google Chart +API for plotting. + +Options: + +-b + +Sets the baseline experiment description. + +[ -x ]... + +Sets an experiment description. Can be used multiple times. + +-o + +Outputs the actual compressed file, substituting ".wof2" for ".ttf" in +the input file name. Only useful when a single -x parameter is specified. + += Decompressing the fonts = + +See the cpp/ directory (including cpp/README) for the C++ implementation of +decompression. This code is based on OTS, and successfully roundtrips the +basic compression as described in the draft spec. + += References = + +sfntly: http://code.google.com/p/sfntly/ Guava: +http://code.google.com/p/guava-libraries/ MTX: +http://www.w3.org/Submission/MTX/ + +Also please refer to documents (currently Google Docs): + +WOFF Ultra Condensed file format: proposals and discussion of wire format +issues + +WIFF Ultra Condensed: more discussion of results and compression techniques. +This tool was used to prepare the data in that document. diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..8d48995 --- /dev/null +++ b/build.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/classes/com/google/typography/font/compression/AdvWidth.class b/build/classes/com/google/typography/font/compression/AdvWidth.class new file mode 100644 index 0000000..80c4c03 Binary files /dev/null and b/build/classes/com/google/typography/font/compression/AdvWidth.class differ diff --git a/build/classes/com/google/typography/font/compression/CmapEncoder.class b/build/classes/com/google/typography/font/compression/CmapEncoder.class new file mode 100644 index 0000000..4aced8e Binary files /dev/null and b/build/classes/com/google/typography/font/compression/CmapEncoder.class differ diff --git a/build/classes/com/google/typography/font/compression/Command$StreamConsumer.class b/build/classes/com/google/typography/font/compression/Command$StreamConsumer.class new file mode 100644 index 0000000..e6c24fb Binary files /dev/null and b/build/classes/com/google/typography/font/compression/Command$StreamConsumer.class differ diff --git a/build/classes/com/google/typography/font/compression/Command.class b/build/classes/com/google/typography/font/compression/Command.class new file mode 100644 index 0000000..dd609f4 Binary files /dev/null and b/build/classes/com/google/typography/font/compression/Command.class differ diff --git a/build/classes/com/google/typography/font/compression/CommandException.class b/build/classes/com/google/typography/font/compression/CommandException.class new file mode 100644 index 0000000..3fe78f6 Binary files /dev/null and b/build/classes/com/google/typography/font/compression/CommandException.class differ diff --git a/build/classes/com/google/typography/font/compression/CommandResult.class b/build/classes/com/google/typography/font/compression/CommandResult.class new file mode 100644 index 0000000..eb0387d Binary files /dev/null and b/build/classes/com/google/typography/font/compression/CommandResult.class differ diff --git a/build/classes/com/google/typography/font/compression/CompressLzma.class b/build/classes/com/google/typography/font/compression/CompressLzma.class new file mode 100644 index 0000000..5ee9e25 Binary files /dev/null and b/build/classes/com/google/typography/font/compression/CompressLzma.class differ diff --git a/build/classes/com/google/typography/font/compression/CompressionRunner.class b/build/classes/com/google/typography/font/compression/CompressionRunner.class new file mode 100644 index 0000000..6d08ce8 Binary files /dev/null and b/build/classes/com/google/typography/font/compression/CompressionRunner.class differ diff --git a/build/classes/com/google/typography/font/compression/GlyfEncoder.class b/build/classes/com/google/typography/font/compression/GlyfEncoder.class new file mode 100644 index 0000000..51a003f Binary files /dev/null and b/build/classes/com/google/typography/font/compression/GlyfEncoder.class differ diff --git a/build/classes/com/google/typography/font/compression/GzipUtil.class b/build/classes/com/google/typography/font/compression/GzipUtil.class new file mode 100644 index 0000000..b63b757 Binary files /dev/null and b/build/classes/com/google/typography/font/compression/GzipUtil.class differ diff --git a/build/classes/com/google/typography/font/compression/KernEncoder.class b/build/classes/com/google/typography/font/compression/KernEncoder.class new file mode 100644 index 0000000..ca4bf20 Binary files /dev/null and b/build/classes/com/google/typography/font/compression/KernEncoder.class differ diff --git a/build/classes/com/google/typography/font/compression/StatsCollector.class b/build/classes/com/google/typography/font/compression/StatsCollector.class new file mode 100644 index 0000000..69f6cce Binary files /dev/null and b/build/classes/com/google/typography/font/compression/StatsCollector.class differ diff --git a/build/classes/com/google/typography/font/compression/TestCommand.class b/build/classes/com/google/typography/font/compression/TestCommand.class new file mode 100644 index 0000000..402eda0 Binary files /dev/null and b/build/classes/com/google/typography/font/compression/TestCommand.class differ diff --git a/build/classes/com/google/typography/font/compression/Woff2Writer$1.class b/build/classes/com/google/typography/font/compression/Woff2Writer$1.class new file mode 100644 index 0000000..be891b2 Binary files /dev/null and b/build/classes/com/google/typography/font/compression/Woff2Writer$1.class differ diff --git a/build/classes/com/google/typography/font/compression/Woff2Writer$CompressionType.class b/build/classes/com/google/typography/font/compression/Woff2Writer$CompressionType.class new file mode 100644 index 0000000..6addfb9 Binary files /dev/null and b/build/classes/com/google/typography/font/compression/Woff2Writer$CompressionType.class differ diff --git a/build/classes/com/google/typography/font/compression/Woff2Writer$TableDirectoryEntry.class b/build/classes/com/google/typography/font/compression/Woff2Writer$TableDirectoryEntry.class new file mode 100644 index 0000000..7c80eca Binary files /dev/null and b/build/classes/com/google/typography/font/compression/Woff2Writer$TableDirectoryEntry.class differ diff --git a/build/classes/com/google/typography/font/compression/Woff2Writer.class b/build/classes/com/google/typography/font/compression/Woff2Writer.class new file mode 100644 index 0000000..6e6f217 Binary files /dev/null and b/build/classes/com/google/typography/font/compression/Woff2Writer.class differ diff --git a/build/jar/compression.jar b/build/jar/compression.jar new file mode 100644 index 0000000..9e2b3ca Binary files /dev/null and b/build/jar/compression.jar differ diff --git a/cpp/.SConstruct.swp b/cpp/.SConstruct.swp new file mode 100644 index 0000000..402a853 Binary files /dev/null and b/cpp/.SConstruct.swp differ diff --git a/cpp/Inconsolata-Regular.woff2 b/cpp/Inconsolata-Regular.woff2 new file mode 100644 index 0000000..ddfefb3 Binary files /dev/null and b/cpp/Inconsolata-Regular.woff2 differ diff --git a/cpp/README b/cpp/README new file mode 100644 index 0000000..6c40119 --- /dev/null +++ b/cpp/README @@ -0,0 +1,44 @@ +To build: + +Check out a recent version of OTS, so that ots-read-only/ is alongside cpp/ + +Cd into cpp, and run "scons". + +Then, decompress a font using ./woff2-decompress font.wof2 > font.ttf . + +An example font is provided (Inconsolata-Regular.wof2). In this snapshot, +it contains no actual compression, but does apply the glyf table transform +and has the file format and structure as described in the current draft +of the "WOFF Ultra Condensed file format" doc. + +That said, it is possible to get reliable estimates, and at the very least, +bounds, on the compression efficiency, with the confidence that the +compression is reversible. Running the compressor and doing a whole-file +compression with a standard entropy coder such as gzip or lzma will yield +a file size within a few dozen bytes or so of using a single entropy coded +stream in the final file format. + +Another limitation of the current implementation snapshot is that it +doesn't implement continue streams. These will follow shortly. + += Building with lzma = + +The lzma-enabled build is made with gyp instead of scons. Right now, the build +requires patching a clean copy of OTS. (And thanks to Bashi for the patch!) + +- Download GYP from http://code.google.com/p/gyp/ +- Download clean OTS sources: +% svn checkout http://ots.googlecode.com/svn/trunk/ ots-read-only +- Apply patch +% cd ots-read-only; patch -p0 < ../ots-lzma.patch +- Run gyp to generate Makefile +% cd ../cpp; gyp --depth=. -f make woff2.gyp +- Build +% make + +Now run: + +out/Default/woff2-decompress Inconsolata-Regular-lzma.wof2 > i.ttf + +We expect the build recipes to be cleaned up before the code is ready for +production, but this should be good enough for testing. diff --git a/cpp/SConstruct b/cpp/SConstruct new file mode 100644 index 0000000..4ec2e45 --- /dev/null +++ b/cpp/SConstruct @@ -0,0 +1,52 @@ +# Build script for Linux +# Note: this script DOESN'T include LZMA. Use the gyp-based build instead. +# +# Usage: +# $ cd ots/test/ +# $ scons -c # clean +# $ scons # build +# + +# Since the validator-checker tool might handle malicious font files, all hardening options for recent g++/ld are enabled just in case. +# See http://wiki.debian.org/Hardening for details. +env = Environment(CCFLAGS = ['-O2', '-I../ots-read-only/include', '-I../ots-read-only/src', '-I/usr/include/freetype2', '-ggdb', '-Wall', '-W', '-Wno-unused-parameter', '-fno-strict-aliasing', '-fPIE', '-fstack-protector', '-D_FORTIFY_SOURCE=2', '-DOTS_DEBUG'], LINKFLAGS = ['-ggdb', '-Wl,-z,relro', '-Wl,-z,now', '-pie', '-lz']) + +env.Library('libwoff2.a', + [ + 'woff2.cc', + + # Just build all of OTS to keep things simple for now. We could + # refactor so that we only compile the few support routines from + # ots.cc that we need. + '../ots-read-only/src/cff.cc', + '../ots-read-only/src/cff_type2_charstring.cc', + '../ots-read-only/src/cmap.cc', + '../ots-read-only/src/cvt.cc', + '../ots-read-only/src/fpgm.cc', + '../ots-read-only/src/gasp.cc', + '../ots-read-only/src/gdef.cc', + '../ots-read-only/src/glyf.cc', + '../ots-read-only/src/gpos.cc', + '../ots-read-only/src/gsub.cc', + '../ots-read-only/src/hdmx.cc', + '../ots-read-only/src/head.cc', + '../ots-read-only/src/hhea.cc', + '../ots-read-only/src/hmtx.cc', + '../ots-read-only/src/kern.cc', + '../ots-read-only/src/layout.cc', + '../ots-read-only/src/loca.cc', + '../ots-read-only/src/ltsh.cc', + '../ots-read-only/src/maxp.cc', + '../ots-read-only/src/metrics.cc', + '../ots-read-only/src/name.cc', + '../ots-read-only/src/os2.cc', + '../ots-read-only/src/ots.cc', + '../ots-read-only/src/post.cc', + '../ots-read-only/src/prep.cc', + '../ots-read-only/src/vdmx.cc', + '../ots-read-only/src/vhea.cc', + '../ots-read-only/src/vmtx.cc', + '../ots-read-only/src/vorg.cc' + ]) + +env.Program('woff2-decompress.cc', LIBS = ['woff2'], LIBPATH='.') diff --git a/cpp/woff2-decompress.cc b/cpp/woff2-decompress.cc new file mode 100644 index 0000000..dc9a281 --- /dev/null +++ b/cpp/woff2-decompress.cc @@ -0,0 +1,61 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// A very simple commandline tool for decompressing woff2 format files +// (given as argc[1]), writing the decompressed version to stdout. + +#include +#include +#include + +#include +#include + +#include "opentype-sanitiser.h" +#include "woff2.h" + +namespace { + +int Usage(const char *argv0) { + std::fprintf(stderr, "Usage: %s woff2_file > dest_ttf_file\n", argv0); + return 1; +} + +} // namespace + +int main(int argc, char **argv) { + if (argc != 2) return Usage(argv[0]); + if (::isatty(1)) return Usage(argv[0]); + + const int fd = ::open(argv[1], O_RDONLY); + if (fd < 0) { + ::perror("open"); + return 1; + } + + struct stat st; + ::fstat(fd, &st); + + uint8_t *data = new uint8_t[st.st_size]; + if (::read(fd, data, st.st_size) != st.st_size) { + ::perror("read"); + return 1; + } + ::close(fd); + + size_t decompressed_size = ots::ComputeWOFF2FinalSize(data, st.st_size); + if (decompressed_size == 0) { + std::fprintf(stderr, "Error computing decompressed file size!\n"); + return 1; + } + uint8_t *buf = new uint8_t[decompressed_size]; + const bool result = ots::ConvertWOFF2ToTTF(buf, decompressed_size, + data, st.st_size); + + if (!result) { + std::fprintf(stderr, "Failed to decompress file!\n"); + } + fwrite(buf, 1, decompressed_size, stdout); + return !result; +} diff --git a/cpp/woff2.cc b/cpp/woff2.cc new file mode 100644 index 0000000..902f4dd --- /dev/null +++ b/cpp/woff2.cc @@ -0,0 +1,948 @@ +// Copyright (c) 2012 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This is the implementation of decompression of the proposed WOFF Ultra +// Condensed file format. + +// For now, use of LZMA is conditional, because the build is trickier. When +// that gets all sorted out, we can get rid of these ifdefs. +#define USE_LZMA + +#ifdef USE_LZMA +#include "third_party/lzma_sdk/LzmaLib.h" +#endif + +#include +#include +#include "opentype-sanitiser.h" +#include "ots-memory-stream.h" + +#include "ots.h" +#include "woff2.h" + +namespace { + +// simple glyph flags +const int kGlyfOnCurve = 1 << 0; +const int kGlyfXShort = 1 << 1; +const int kGlyfYShort = 1 << 2; +const int kGlyfRepeat = 1 << 3; +const int kGlyfThisXIsSame = 1 << 4; +const int kGlyfThisYIsSame = 1 << 5; + +// composite glyph flags +const int FLAG_ARG_1_AND_2_ARE_WORDS = 1 << 0; +const int FLAG_ARGS_ARE_XY_VALUES = 1 << 1; +const int FLAG_ROUND_XY_TO_GRID = 1 << 2; +const int FLAG_WE_HAVE_A_SCALE = 1 << 3; +const int FLAG_RESERVED = 1 << 4; +const int FLAG_MORE_COMPONENTS = 1 << 5; +const int FLAG_WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6; +const int FLAG_WE_HAVE_A_TWO_BY_TWO = 1 << 7; +const int FLAG_WE_HAVE_INSTRUCTIONS = 1 << 8; +const int FLAG_USE_MY_METRICS = 1 << 9; +const int FLAG_OVERLAP_COMPOUND = 1 << 10; +const int FLAG_SCALED_COMPONENT_OFFSET = 1 << 11; +const int FLAG_UNSCALED_COMPONENT_OFFSET = 1 << 12; + +const size_t kSfntHeaderSize = 12; +const size_t kSfntEntrySize = 16; +const size_t kCheckSumAdjustmentOffset = 8; + +const size_t kEndPtsOfContoursOffset = 10; +const size_t kCompositeGlyphBegin = 10; + +// Note that the byte order is big-endian, not the same as ots.cc +#define TAG(a, b, c, d) ((a << 24) | (b << 16) | (c << 8) | d) + +const unsigned int kWoff2FlagsContinueStream = 1 << 4; +const unsigned int kWoff2FlagsTransform = 1 << 5; + +const size_t kWoff2HeaderSize = 44; +const size_t kWoff2EntrySize = 20; + +const size_t kLzmaHeaderSize = 13; + +const uint32_t kCompressionTypeMask = 0xf; +const uint32_t kCompressionTypeNone = 0; +const uint32_t kCompressionTypeGzip = 1; +const uint32_t kCompressionTypeLzma = 2; + + +struct Point { + int x; + int y; + bool on_curve; +}; + +struct Table { + uint32_t tag; + uint32_t flags; + uint32_t src_offset; + uint32_t src_length; + + uint32_t transform_length; + + uint32_t dst_offset; + uint32_t dst_length; +}; + +// Based on section 6.1.1 of MicroType Express draft spec +bool Read255UShort(ots::Buffer *buf, unsigned int *value) { + const int kWordCode = 253; + const int kOneMoreByteCode2 = 254; + const int kOneMoreByteCode1 = 255; + const int kLowestUCode = 253; + uint8_t code = 0; + if (!buf->ReadU8(&code)) { + return OTS_FAILURE(); + } + if (code == kWordCode) { + uint16_t result = 0; + if (!buf->ReadU16(&result)) { + return OTS_FAILURE(); + } + *value = result; + return true; + } else if (code == kOneMoreByteCode1) { + uint8_t result = 0; + if (!buf->ReadU8(&result)) { + return OTS_FAILURE(); + } + *value = result + kLowestUCode; + return true; + } else if (code == kOneMoreByteCode2) { + uint8_t result = 0; + if (!buf->ReadU8(&result)) { + return OTS_FAILURE(); + } + *value = result + kLowestUCode * 2; + return true; + } else { + *value = code; + return true; + } +} + +bool ReadBase128(ots::Buffer *buf, uint32_t *value) { + uint32_t result = 0; + for (size_t i = 0; i < 5; ++i) { + uint8_t code = 0; + if (!buf->ReadU8(&code)) { + return OTS_FAILURE(); + } + result = (result << 7) | (code & 0x7f); + if ((code & 0x80) == 0) { + *value = result; + return true; + } + } + // Make sure not to exceed the size bound + return OTS_FAILURE(); +} + +size_t StoreU32(uint8_t *dst, size_t offset, uint32_t x) { + dst[offset] = x >> 24; + dst[offset + 1] = (x >> 16) & 0xff; + dst[offset + 2] = (x >> 8) & 0xff; + dst[offset + 3] = x & 0xff; + return offset + 4; +} + +size_t Store16(uint8_t *dst, size_t offset, int x) { + dst[offset] = x >> 8; + dst[offset + 1] = x & 0xff; + return offset + 2; +} + +int WithSign(int flag, int baseval) { + return (flag & 1) ? baseval : -baseval; +} + +bool TripletDecode(const uint8_t *flags_in, const uint8_t *in, size_t in_size, + unsigned int n_points, std::vector *result, + size_t *in_bytes_consumed) { + int x = 0; + int y = 0; + + if (n_points > in_size) { + return false; + } + unsigned int triplet_index = 0; + + for (unsigned int i = 0; i < n_points; ++i) { + uint8_t flag = flags_in[i]; + bool on_curve = !(flag >> 7); + flag &= 0x7f; + int n_data_bytes; + if (flag < 84) { + n_data_bytes = 1; + } else if (flag < 120) { + n_data_bytes = 2; + } else if (flag < 124) { + n_data_bytes = 3; + } else { + n_data_bytes = 4; + } +#if 0 + fprintf(stderr, "flag = %d:", flag); + for (int j = 0; j < n_data_bytes; ++j) { + fprintf(stderr, " %d", in[triplet_index + j]); + } + fprintf(stderr, "\n"); +#endif + if (triplet_index + n_data_bytes > in_size || + triplet_index + n_data_bytes < triplet_index) { + return OTS_FAILURE(); + } + int dx, dy; + if (flag < 10) { + dx = 0; + dy = WithSign(flag, ((flag & 14) << 7) + in[triplet_index]); + } else if (flag < 20) { + dx = WithSign(flag, (((flag - 10) & 14) << 7) + in[triplet_index]); + dy = 0; + } else if (flag < 84) { + int b0 = flag - 20; + int b1 = in[triplet_index]; + dx = WithSign(flag, 1 + (b0 & 0x30) + (b1 >> 4)); + dy = WithSign(flag >> 1, 1 + ((b0 & 0x0c) << 2) + (b1 & 0x0f)); + } else if (flag < 120) { + int b0 = flag - 84; + dx = WithSign(flag, 1 + ((b0 / 12) << 8) + in[triplet_index]); + dy = WithSign(flag >> 1, + 1 + (((b0 % 12) >> 2) << 8) + in[triplet_index + 1]); + } else if (flag < 124) { + int b2 = in[triplet_index + 1]; + dx = WithSign(flag, (in[triplet_index] << 4) + (b2 >> 4)); + dy = WithSign(flag >> 1, ((b2 & 0x0f) << 8) + in[triplet_index + 2]); + } else { + dx = WithSign(flag, (in[triplet_index] << 8) + in[triplet_index + 1]); + dy = WithSign(flag >> 1, + (in[triplet_index + 2] << 8) + in[triplet_index + 3]); + } + triplet_index += n_data_bytes; + x += dx; + y += dy; + result->push_back(Point()); + Point &back = result->back(); + back.x = x; + back.y = y; + back.on_curve = on_curve; + // fprintf(stderr, "point %d: %d %d %s (delta %d %d)\n", + // i, x, y, on_curve ? "on" : "off", dx, dy); + } + *in_bytes_consumed = triplet_index; + return true; +} + +// This function stores just the point data. On entry, dst points to the +// beginning of a simple glyph. Returns true on success. +bool StorePoints(const std::vector &points, + unsigned int n_contours, unsigned int instruction_length, + uint8_t *dst, size_t dst_size, size_t *glyph_size) { + unsigned int flag_offset = kEndPtsOfContoursOffset + 2 * n_contours + 2 + + instruction_length; + int last_flag = -1; + int repeat_count = 0; + int last_x = 0; + int last_y = 0; + int x_bytes = 0; + int y_bytes = 0; + + for (unsigned int i = 0; i < points.size(); ++i) { + const Point &point = points[i]; + int flag = point.on_curve ? kGlyfOnCurve : 0; + int dx = point.x - last_x; + int dy = point.y - last_y; + if (dx == 0) { + flag |= kGlyfThisXIsSame; + } else if (dx > -256 && dx < 256) { + flag |= kGlyfXShort | (dx > 0 ? kGlyfThisXIsSame : 0); + x_bytes += 1; + } else { + x_bytes += 2; + } + if (dy == 0) { + flag |= kGlyfThisYIsSame; + } else if (dy > -256 && dy < 256) { + flag |= kGlyfYShort | (dy > 0 ? kGlyfThisYIsSame : 0); + y_bytes += 1; + } else { + y_bytes += 2; + } + // fprintf(stderr, "nominal flag = %d\n", flag); + if (flag == last_flag && repeat_count != 255) { + dst[flag_offset - 1] |= kGlyfRepeat; + repeat_count++; + } else { + if (repeat_count != 0) { + if (flag_offset >= dst_size) return OTS_FAILURE(); + dst[flag_offset++] = repeat_count; + } + if (flag_offset >= dst_size) return OTS_FAILURE(); + dst[flag_offset++] = flag; + repeat_count = 0; + } + last_x = point.x; + last_y = point.y; + last_flag = flag; + } + + if (repeat_count != 0) { + if (flag_offset >= dst_size) return OTS_FAILURE(); + dst[flag_offset++] = repeat_count; + } + if (flag_offset + x_bytes + y_bytes > dst_size || + flag_offset + x_bytes + y_bytes < flag_offset) { + return OTS_FAILURE(); + } + + int x_offset = flag_offset; + int y_offset = flag_offset + x_bytes; + last_x = 0; + last_y = 0; + for (unsigned int i = 0; i < points.size(); ++i) { + int dx = points[i].x - last_x; + if (dx == 0) { + // pass + } else if (dx > -256 && dx < 256) { + dst[x_offset++] = std::abs(dx); + } else { + x_offset = Store16(dst, x_offset, dx); + } + last_x += dx; + int dy = points[i].y - last_y; + if (dy == 0) { + // pass + } else if (dy > -256 && dy < 256) { + dst[y_offset++] = std::abs(dy); + } else { + y_offset = Store16(dst, y_offset, dy); + } + last_y += dy; + } + *glyph_size = y_offset; + return true; +} + +// Compute the bounding box of the coordinates, and store into a glyf buffer. +// A precondition is that there are at least 10 bytes available. +void ComputeBbox(const std::vector &points, uint8_t *dst) { + int x_min = 0; + int y_min = 0; + int x_max = 0; + int y_max = 0; + + for (unsigned int i = 0; i < points.size(); ++i) { + int x = points[i].x; + int y = points[i].y; + if (i == 0 || x < x_min) x_min = x; + if (i == 0 || x > x_max) x_max = x; + if (i == 0 || y < y_min) y_min = y; + if (i == 0 || y > y_max) y_max = y; + } + size_t offset = 2; + offset = Store16(dst, offset, x_min); + offset = Store16(dst, offset, y_min); + offset = Store16(dst, offset, x_max); + offset = Store16(dst, offset, y_max); +} + +// Process entire bbox stream. This is done as a separate pass to allow for +// composite bbox computations (an optional more aggressive transform). +bool ProcessBboxStream(ots::Buffer *bbox_stream, unsigned int n_glyphs, + const std::vector &loca_values, uint8_t *glyf_buf) { + const uint8_t *buf = bbox_stream->buffer(); + unsigned int bitmap_length = ((n_glyphs + 31) >> 5) << 2; + if (bbox_stream->length() < bitmap_length) { + return OTS_FAILURE(); + } + bbox_stream->Skip(bitmap_length); + for (unsigned int i = 0; i < n_glyphs; ++i) { + if (buf[i >> 3] & (0x80 >> (i & 7))) { + uint32_t loca_offset = loca_values[i]; + if (loca_values[i + 1] - loca_offset < kEndPtsOfContoursOffset) { + return OTS_FAILURE(); + } + bbox_stream->Read(glyf_buf + loca_offset + 2, 8); + } + } + return true; +} + +bool ProcessComposite(ots::Buffer *composite_stream, uint8_t *dst, + size_t dst_size, size_t *glyph_size, bool *have_instructions) { + size_t start_offset = composite_stream->offset(); + bool we_have_instructions = false; + + uint16_t flags = FLAG_MORE_COMPONENTS; + while (flags & FLAG_MORE_COMPONENTS) { + if (!composite_stream->ReadU16(&flags)) { + return OTS_FAILURE(); + } + we_have_instructions |= (flags & FLAG_WE_HAVE_INSTRUCTIONS) != 0; + size_t arg_size = 2; // glyph index + if (flags & FLAG_ARG_1_AND_2_ARE_WORDS) { + arg_size += 4; + } else { + arg_size += 2; + } + if (flags & FLAG_WE_HAVE_A_SCALE) { + arg_size += 2; + } else if (flags & FLAG_WE_HAVE_AN_X_AND_Y_SCALE) { + arg_size += 4; + } else if (flags & FLAG_WE_HAVE_A_TWO_BY_TWO) { + arg_size += 8; + } + if (!composite_stream->Skip(arg_size)) { + return OTS_FAILURE(); + } + //fprintf(stderr, "flags = %04x, arg_size = %d\n", flags, arg_size); + } + size_t composite_glyph_size = composite_stream->offset() - start_offset; + if (composite_glyph_size + kCompositeGlyphBegin > dst_size) { + return OTS_FAILURE(); + } + Store16(dst, 0, 0xffff); // nContours = -1 for composite glyph + std::memcpy(dst + kCompositeGlyphBegin, + composite_stream->buffer() + start_offset, + composite_glyph_size); + *glyph_size = kCompositeGlyphBegin + composite_glyph_size; + *have_instructions = we_have_instructions; + return true; +} + +// Build TrueType loca table +bool StoreLoca(const std::vector &loca_values, int index_format, + uint8_t *dst, size_t dst_size) { + size_t loca_size = loca_values.size(); + size_t offset_size = index_format ? 4 : 2; + if (offset_size * loca_size > dst_size) { + return OTS_FAILURE(); + } + size_t offset = 0; + for (size_t i = 0; i < loca_values.size(); ++i) { + int value = loca_values[i]; + if (index_format) { + offset = StoreU32(dst, offset, value); + } else { + offset = Store16(dst, offset, value >> 1); + } + } + return true; +} + +// Reconstruct entire glyf table based on transformed original +bool ReconstructGlyf(const uint8_t *data, size_t data_size, + uint8_t *dst, size_t dst_size, + uint8_t *loca_buf, size_t loca_size) { + ots::Buffer file(data, data_size); + uint32_t version; + const int kNumSubStreams = 7; + std::vector > substreams(kNumSubStreams); + + if (!file.ReadU32(&version)) { + return OTS_FAILURE(); + } + uint16_t num_glyphs = 0; + uint16_t index_format = 0; + if (!file.ReadU16(&num_glyphs) || + !file.ReadU16(&index_format)) { + return OTS_FAILURE(); + } + // fprintf(stderr, "num_glyphs = %d\n", num_glyphs); + unsigned int offset = (2 + kNumSubStreams) * 4; + for (int i = 0; i < kNumSubStreams; ++i) { + uint32_t substream_size = 0; + if (!file.ReadU32(&substream_size)) { + return OTS_FAILURE(); + } + if (substream_size > data_size - offset) { + return OTS_FAILURE(); + } + // fprintf(stderr, "substream size = %d\n", substream_size); + substreams[i] = std::make_pair(data + offset, substream_size); + offset += substream_size; + } + ots::Buffer n_contour_stream(substreams[0].first, substreams[0].second); + ots::Buffer n_points_stream(substreams[1].first, substreams[1].second); + ots::Buffer flag_stream(substreams[2].first, substreams[2].second); + ots::Buffer glyph_stream(substreams[3].first, substreams[3].second); + ots::Buffer composite_stream(substreams[4].first, substreams[4].second); + ots::Buffer bbox_stream(substreams[5].first, substreams[5].second); + ots::Buffer instruction_stream(substreams[6].first, substreams[6].second); + + std::vector loca_values(num_glyphs + 1); + std::vector n_points_vec; + std::vector points; + uint32_t loca_offset = 0; + for (unsigned int i = 0; i < num_glyphs; ++i) { + size_t glyph_size = 0; + uint16_t n_contours = 0; + if (!n_contour_stream.ReadU16(&n_contours)) { + return OTS_FAILURE(); + } + // fprintf(stderr, "n_contours[%d] = %d\n", i, n_contours); + uint8_t *glyf_dst = dst + loca_offset; + size_t glyf_dst_size = dst_size - loca_offset; + if (n_contours == 0xffff) { + // composite glyph + bool have_instructions = false; + unsigned int instruction_size = 0; + if (!ProcessComposite(&composite_stream, glyf_dst, glyf_dst_size, + &glyph_size, &have_instructions)) { + return OTS_FAILURE(); + } + if (have_instructions) { + if (!Read255UShort(&glyph_stream, &instruction_size)) { + return OTS_FAILURE(); + } + if (instruction_size + 2 > glyf_dst_size - glyph_size) { + return OTS_FAILURE(); + } + Store16(glyf_dst, glyph_size, instruction_size); + if (!instruction_stream.Read(glyf_dst + glyph_size + 2, + instruction_size)) { + return OTS_FAILURE(); + } + glyph_size += instruction_size + 2; + } + } else if (n_contours > 0) { + // simple glyph + n_points_vec.clear(); + points.clear(); + unsigned int total_n_points = 0; + unsigned int n_points_contour; + for (unsigned int j = 0; j < n_contours; ++j) { + if (!Read255UShort(&n_points_stream, &n_points_contour)) { + return OTS_FAILURE(); + } + n_points_vec.push_back(n_points_contour); + total_n_points += n_points_contour; + } + unsigned int flag_size = total_n_points; + if (flag_size > flag_stream.length() - flag_stream.offset()) { + return OTS_FAILURE(); + } + const uint8_t *flags_buf = flag_stream.buffer() + flag_stream.offset(); + const uint8_t *triplet_buf = glyph_stream.buffer() + + glyph_stream.offset(); + size_t triplet_size = glyph_stream.length() - glyph_stream.offset(); + size_t triplet_bytes_consumed = 0; + if (!TripletDecode(flags_buf, triplet_buf, triplet_size, total_n_points, + &points, &triplet_bytes_consumed)) { + return OTS_FAILURE(); + } + if (glyf_dst_size < kEndPtsOfContoursOffset + 2 * n_contours) { + return OTS_FAILURE(); + } + Store16(glyf_dst, 0, n_contours); + ComputeBbox(points, glyf_dst); + size_t offset = kEndPtsOfContoursOffset; + int end_point = -1; + for (unsigned int contour_ix = 0; contour_ix < n_contours; ++contour_ix) { + end_point += n_points_vec[contour_ix]; + offset = Store16(glyf_dst, offset, end_point); + } + flag_stream.Skip(flag_size); + glyph_stream.Skip(triplet_bytes_consumed); + unsigned int instruction_size; + if (!Read255UShort(&glyph_stream, &instruction_size)) { + return OTS_FAILURE(); + } + // fprintf(stderr, "%d: instruction size = %d\n", i, instruction_size); + uint8_t *instruction_dst = glyf_dst + kEndPtsOfContoursOffset + + 2 * n_contours; + Store16(instruction_dst, 0, instruction_size); + if (!instruction_stream.Read(instruction_dst + 2, instruction_size)) { + return OTS_FAILURE(); + } + if (!StorePoints(points, n_contours, instruction_size, + glyf_dst, glyf_dst_size, &glyph_size)) { + return OTS_FAILURE(); + } + } else { + glyph_size = 0; + } + loca_values[i] = loca_offset; + if (glyph_size + 3 < glyph_size) { + return OTS_FAILURE(); + } + // Round up to 4-byte alignment + glyph_size = (glyph_size + 3) & -4; + if (glyph_size > dst_size - loca_offset) { + // This shouldn't happen, but this test defensively maintains the + // invariant that loca_offset <= dst_size. + return OTS_FAILURE(); + } + loca_offset += glyph_size; + } + loca_values[num_glyphs] = loca_offset; + if (!ProcessBboxStream(&bbox_stream, num_glyphs, loca_values, dst)) { + return OTS_FAILURE(); + } + return StoreLoca(loca_values, index_format, loca_buf, loca_size); +} + +// This is linear search, but could be changed to binary because we +// do have a guarantee that the tables are sorted by tag. But the total +// cpu time is expected to be very small in any case. +const Table *FindTable(const std::vector &tables, uint32_t tag) { + size_t n_tables = tables.size(); + for (size_t i = 0; i < n_tables; ++i) { + if (tables[i].tag == tag) { + return &tables[i]; + } + } + return NULL; +} + +bool ReconstructTransformed(const std::vector
&tables, uint32_t tag, + const uint8_t *transformed_buf, size_t transformed_size, + uint8_t *dst) { + if (tag == TAG('g', 'l', 'y', 'f')) { + const Table *glyf_table = FindTable(tables, tag); + const Table *loca_table = FindTable(tables, TAG('l', 'o', 'c', 'a')); + if (glyf_table == NULL || loca_table == NULL) { + return OTS_FAILURE(); + } + return ReconstructGlyf(transformed_buf, transformed_size, + dst + glyf_table->dst_offset, glyf_table->dst_length, + dst + loca_table->dst_offset, loca_table->dst_offset); + } else if (tag == TAG('l', 'o', 'c', 'a')) { + // processing was already done by glyf table, but validate + if (!FindTable(tables, TAG('g', 'l', 'y', 'f'))) { + return OTS_FAILURE(); + } + } else { + // transform for the tag is not known + return OTS_FAILURE(); + } + return true; +} + +// TODO: copied from ots.cc, probably shouldn't be duplicated. +// Round a value up to the nearest multiple of 4. Don't round the value in the +// case that rounding up overflows. +template T Round4(T value) { + if (std::numeric_limits::max() - value < 3) { + return value; + } + return (value + 3) & ~3; +} + +uint32_t ComputeChecksum(const uint8_t *buf, size_t size) { + uint32_t checksum = 0; + for (size_t i = 0; i < size; i += 4) { + // We assume the addition is mod 2^32. This is a pretty safe assumption, + // but technically it's undefined behavior. + checksum += (buf[i] << 24) | (buf[i + 1] << 16) | + (buf[i + 2] << 8) | buf[i + 3]; + } + return checksum; +} + +bool FixChecksums(const std::vector
&tables, uint8_t *dst) { + const Table *head_table = FindTable(tables, TAG('h', 'e', 'a', 'd')); + if (head_table == NULL || + head_table->dst_length < kCheckSumAdjustmentOffset + 4) { + return OTS_FAILURE(); + } + size_t adjustment_offset = head_table->dst_offset + kCheckSumAdjustmentOffset; + StoreU32(dst, adjustment_offset, 0); + size_t n_tables = tables.size(); + uint32_t file_checksum = 0; + for (size_t i = 0; i < n_tables; ++i) { + const Table *table = &tables[i]; + size_t table_length = table->dst_length; + uint8_t *table_data = dst + table->dst_offset; + uint32_t checksum = ComputeChecksum(table_data, table_length); + StoreU32(dst, kSfntHeaderSize + i * kSfntEntrySize + 4, checksum); + file_checksum += checksum; + } + file_checksum += ComputeChecksum(dst, + kSfntHeaderSize + kSfntEntrySize * n_tables); + uint32_t checksum_adjustment = 0xb1b0afba - file_checksum; + StoreU32(dst, adjustment_offset, checksum_adjustment); + return true; +} + +bool Woff2Uncompress(uint8_t *dst_buf, size_t dst_size, + const uint8_t *src_buf, size_t src_size, uint32_t compression_type) { + if (compression_type == kCompressionTypeGzip) { + uLongf uncompressed_length = dst_size; + int r = uncompress((Bytef *)dst_buf, &uncompressed_length, + src_buf, src_size); + if (r != Z_OK || uncompressed_length != src_size) { + return OTS_FAILURE(); + } + return true; +#ifdef USE_LZMA + } else if (compression_type == kCompressionTypeLzma) { + if (src_size < kLzmaHeaderSize) { + // Make sure we have at least a full Lzma header + return OTS_FAILURE(); + } + // TODO: check that size matches (or elide size?) + size_t uncompressed_size = dst_size; + size_t compressed_size = src_size; + int result = LzmaUncompress(dst_buf, &dst_size, + src_buf + kLzmaHeaderSize, &compressed_size, + src_buf, LZMA_PROPS_SIZE); + if (result != SZ_OK || uncompressed_size != dst_size) { + return OTS_FAILURE(); + } + return true; +#endif + } + // Unknown compression type + return OTS_FAILURE(); +} + +bool ReadLongDirectory(ots::Buffer *file, std::vector
*tables, + size_t num_tables) { + for (size_t i = 0; i < num_tables; ++i) { + Table *table = &(*tables)[i]; + if (!file->ReadU32(&table->tag) || + !file->ReadU32(&table->flags) || + !file->ReadU32(&table->src_length) || + !file->ReadU32(&table->transform_length) || + !file->ReadU32(&table->dst_length)) { + return OTS_FAILURE(); + } + } + return true; +} + +const uint32_t known_tags[29] = { + TAG('c', 'm', 'a', 'p'), // 0 + TAG('h', 'e', 'a', 'd'), // 1 + TAG('h', 'h', 'e', 'a'), // 2 + TAG('h', 'm', 't', 'x'), // 3 + TAG('m', 'a', 'x', 'p'), // 4 + TAG('n', 'a', 'm', 'e'), // 5 + TAG('O', 'S', '/', '2'), // 6 + TAG('p', 'o', 's', 't'), // 7 + TAG('c', 'v', 't', ' '), // 8 + TAG('f', 'p', 'g', 'm'), // 9 + TAG('g', 'l', 'y', 'f'), // 10 + TAG('l', 'o', 'c', 'a'), // 11 + TAG('p', 'r', 'e', 'p'), // 12 + TAG('C', 'F', 'F', ' '), // 13 + TAG('V', 'O', 'R', 'G'), // 14 + TAG('E', 'B', 'D', 'T'), // 15 + TAG('E', 'B', 'L', 'C'), // 16 + TAG('g', 'a', 's', 'p'), // 17 + TAG('h', 'd', 'm', 'x'), // 18 + TAG('k', 'e', 'r', 'n'), // 19 + TAG('L', 'T', 'S', 'H'), // 20 + TAG('P', 'C', 'L', 'T'), // 21 + TAG('V', 'D', 'M', 'X'), // 22 + TAG('v', 'h', 'e', 'a'), // 23 + TAG('v', 'm', 't', 'x'), // 24 + TAG('B', 'A', 'S', 'E'), // 25 + TAG('G', 'D', 'E', 'F'), // 26 + TAG('G', 'P', 'O', 'S'), // 27 + TAG('G', 'S', 'U', 'B'), // 28 +}; + +bool ReadShortDirectory(ots::Buffer *file, std::vector
*tables, + size_t num_tables) { + uint32_t last_compression_type = 0; + for (size_t i = 0; i < num_tables; ++i) { + Table *table = &(*tables)[i]; + uint8_t flag_byte = 0; + if (!file->ReadU8(&flag_byte)) { + return OTS_FAILURE(); + } + uint32_t tag = 0; + if ((flag_byte & 0x1f) == 0x1f) { + if (!file->ReadU32(&tag)) { + return OTS_FAILURE(); + } + } else { + tag = known_tags[flag_byte & 0x1f]; + } + uint32_t flags = flag_byte >> 6; + if (flags == 3) { + flags = last_compression_type | kWoff2FlagsContinueStream; + } else { + last_compression_type = flags; + } + if ((flag_byte & 0x20) != 0) { + flags |= kWoff2FlagsTransform; + } + uint32_t dst_length = 0; + if (!ReadBase128(file, &dst_length)) { + return OTS_FAILURE(); + } + uint32_t transform_length = dst_length; + if ((flags & kWoff2FlagsTransform) != 0) { + if (!ReadBase128(file, &transform_length)) { + return OTS_FAILURE(); + } + } + uint32_t src_length = transform_length; + if ((flag_byte >> 6) == 1 | (flag_byte >> 6) == 2) { + if (!ReadBase128(file, &src_length)) { + return OTS_FAILURE(); + } + } + table->tag = tag; + table->flags = flags; + table->src_length = src_length; + table->transform_length = transform_length; + table->dst_length = dst_length; + } + return true; +} + +} // namespace + +namespace ots { + +size_t ComputeWOFF2FinalSize(const uint8_t *data, size_t length) { + ots::Buffer file(data, length); + + file.Skip(16); + uint32_t total_length = 0; + if (!file.ReadU32(&total_length)) { + return OTS_FAILURE(); + } + return total_length; +} + +bool ConvertWOFF2ToTTF(uint8_t *result, size_t result_length, + const uint8_t *data, size_t length) { + ots::Buffer file(data, length); + + uint32_t signature = 0; + uint32_t flavor = 0; + if (!file.ReadU32(&signature) || signature != 0x774f4632 || + !file.ReadU32(&flavor)) { + return OTS_FAILURE(); + } + file.Skip(4); + uint16_t num_tables = 0; + if (!file.ReadU16(&num_tables)) { + return OTS_FAILURE(); + } + file.Skip(30); + std::vector
tables(num_tables); + // Note: change below to ReadLongDirectory to enable long format. + if (!ReadShortDirectory(&file, &tables, num_tables)) { + return OTS_FAILURE(); + } + size_t src_offset = file.offset(); + size_t dst_offset = kSfntHeaderSize + kSfntEntrySize * num_tables; + size_t uncompressed_sum = 0; + for (int i = 0; i < num_tables; ++i) { + Table *table = &tables[i]; + table->src_offset = src_offset; + if (src_offset + table->src_length < src_offset) { + return OTS_FAILURE(); + } + src_offset += table->src_length; + src_offset = Round4(src_offset); // TODO: reconsider + table->dst_offset = dst_offset; + if (dst_offset + table->dst_length < dst_offset) { + return OTS_FAILURE(); + } + dst_offset += table->dst_length; + dst_offset = Round4(dst_offset); + if ((table->flags & kCompressionTypeMask) != kCompressionTypeNone) { + if (uncompressed_sum + table->src_length < uncompressed_sum) { + return OTS_FAILURE(); + } + uncompressed_sum += table->src_length; + } + } + // Enforce same 30M limit on uncompressed tables as OTS + if (uncompressed_sum > 30 * 1024 * 1024) { + return OTS_FAILURE(); + } + if (dst_offset > result_length) { + return OTS_FAILURE(); + } + + // Start building the font + size_t offset = 0; + offset = StoreU32(result, offset, flavor); + offset = Store16(result, offset, num_tables); + unsigned max_pow2 = 0; + while (1u << (max_pow2 + 1) <= num_tables) { + max_pow2++; + } + const uint16_t output_search_range = (1u << max_pow2) << 4; + offset = Store16(result, offset, output_search_range); + offset = Store16(result, offset, max_pow2); + offset = Store16(result, offset, (num_tables << 4) - output_search_range); + for (int i = 0; i < num_tables; ++i) { + const Table *table = &tables[i]; + offset = StoreU32(result, offset, table->tag); + offset = StoreU32(result, offset, 0); // checksum, to fill in later + offset = StoreU32(result, offset, table->dst_offset); + offset = StoreU32(result, offset, table->dst_length); + } + std::vector uncompressed_buf; + bool continue_valid = false; + for (int i = 0; i < num_tables; ++i) { + const Table *table = &tables[i]; + uint32_t flags = table->flags; + const uint8_t *src_buf = data + table->src_offset; + uint32_t compression_type = flags & kCompressionTypeMask; + const uint8_t *transform_buf = NULL; + size_t transform_length = table->transform_length; + if ((flags & kWoff2FlagsContinueStream) != 0) { + if (!continue_valid) { + return OTS_FAILURE(); + } + } else if (compression_type == kCompressionTypeNone) { + if (transform_length != table->src_length) { + return OTS_FAILURE(); + } + transform_buf = src_buf; + continue_valid = false; + } else if ((flags & kWoff2FlagsContinueStream) == 0) { + size_t total_size = transform_length; + for (int j = i + 1; j < num_tables; ++j) { + if ((tables[j].flags & kWoff2FlagsContinueStream) == 0) { + break; + } + if (total_size + tables[j].transform_length < total_size) { + return OTS_FAILURE(); + } + total_size += tables[j].transform_length; + } + uncompressed_buf.resize(total_size); + if (!Woff2Uncompress(&uncompressed_buf[0], total_size, + src_buf, table->src_length, compression_type)) { + return OTS_FAILURE(); + } + transform_buf = &uncompressed_buf[0]; + continue_valid = true; + } + + if ((flags & kWoff2FlagsTransform) == 0) { + if (transform_length != table->dst_length) { + return OTS_FAILURE(); + } + std::memcpy(result + table->dst_offset, transform_buf, + transform_length); + } else { + if (!ReconstructTransformed(tables, table->tag, + transform_buf, transform_length, result)) { + return OTS_FAILURE(); + } + } + if (continue_valid) { + transform_buf += transform_length; + } + } + + return FixChecksums(tables, result); +} + +} // namespace ots diff --git a/cpp/woff2.gyp b/cpp/woff2.gyp new file mode 100644 index 0000000..6fd3dc9 --- /dev/null +++ b/cpp/woff2.gyp @@ -0,0 +1,43 @@ +{ + 'variables': { + 'ots_include_dirs': [ + # This isn't particularly elegant, but it works + '../ots-read-only/include', + '../ots-read-only/src', + ], + }, + 'target_defaults': { + 'defines': [ + 'OTS_DEBUG', + ], + }, + 'targets': [ + { + 'target_name': 'woff2', + 'type': 'static_library', + 'sources': [ + 'woff2.cc', + ], + 'include_dirs': [ + '<@(ots_include_dirs)', + ], + 'dependencies': [ + '../ots-read-only/ots-standalone.gyp:ots', + ], + }, + { + 'target_name': 'woff2-decompress', + 'type': 'executable', + 'sources': [ + 'woff2-decompress.cc', + ], + 'include_dirs': [ + '<@(ots_include_dirs)', + ], + 'dependencies': [ + 'woff2', + ], + }, + ], +} + diff --git a/cpp/woff2.h b/cpp/woff2.h new file mode 100644 index 0000000..64ae6df --- /dev/null +++ b/cpp/woff2.h @@ -0,0 +1,21 @@ +// Copyright (c) 2012 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef OTS_WOFF2_H_ +#define OTS_WOFF2_H_ + +namespace ots { + +// Compute the size of the final uncompressed font, or 0 on error. +size_t ComputeWOFF2FinalSize(const uint8_t *data, size_t length); + +// Decompresses the font into the target buffer. The result_length should +// be the same as determined by ComputeFinalSize(). Returns true on successful +// decompression. +bool ConvertWOFF2ToTTF(uint8_t *result, size_t result_length, + const uint8_t *data, size_t length); + +} + +#endif // OTS_WOFF2_H_ diff --git a/docs/WOFFUltraCondensedfileformat.pdf b/docs/WOFFUltraCondensedfileformat.pdf new file mode 100644 index 0000000..24c1e04 Binary files /dev/null and b/docs/WOFFUltraCondensedfileformat.pdf differ diff --git a/lib/eotconverter.jar b/lib/eotconverter.jar new file mode 100644 index 0000000..6543d70 Binary files /dev/null and b/lib/eotconverter.jar differ diff --git a/lib/guava-11.0.1.jar b/lib/guava-11.0.1.jar new file mode 100644 index 0000000..af4a383 Binary files /dev/null and b/lib/guava-11.0.1.jar differ diff --git a/lib/sfntly.jar b/lib/sfntly.jar new file mode 100644 index 0000000..415d5c5 Binary files /dev/null and b/lib/sfntly.jar differ diff --git a/lib/woffconverter.jar b/lib/woffconverter.jar new file mode 100644 index 0000000..1ac0133 Binary files /dev/null and b/lib/woffconverter.jar differ diff --git a/ots-lzma.patch b/ots-lzma.patch new file mode 100644 index 0000000..0cf22a5 --- /dev/null +++ b/ots-lzma.patch @@ -0,0 +1,5500 @@ +Index: test/lzma.cc +=================================================================== +--- test/lzma.cc (revision 0) ++++ test/lzma.cc (revision 0) +@@ -0,0 +1,139 @@ ++// Copyright (c) 2012 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "opentype-sanitiser.h" ++#include "ots-memory-stream.h" ++ ++#include "third_party/lzma_sdk/LzmaLib.h" ++ ++namespace { ++ ++static const size_t kCompressedLengthFieldSize = 4; ++ ++int Usage(const char *argv0) { ++ std::fprintf(stderr, "Usage: %s (compress|decompress) filename\n", argv0); ++ return 1; ++} ++ ++bool ReadFile(const char *file_name, std::vector* data) { ++ const int fd = open(file_name, O_RDONLY); ++ if (fd < 0) { ++ return false; ++ } ++ ++ struct stat st; ++ fstat(fd, &st); ++ ++ data->resize(st.st_size); ++ if (read(fd, &(*data)[0], st.st_size) != st.st_size) { ++ close(fd); ++ return false; ++ } ++ close(fd); ++ return true; ++} ++ ++bool Compress(std::vector* input, std::vector* output) { ++ size_t props_size = LZMA_PROPS_SIZE; ++ size_t out_len = input->size() * 2; ++ output->resize(out_len + props_size + kCompressedLengthFieldSize); ++ ++ uint8_t* output_start = &(*output)[kCompressedLengthFieldSize]; ++ ++ int result = LzmaCompress(output_start + LZMA_PROPS_SIZE, &out_len, ++ &(*input)[0], input->size(), ++ output_start, &props_size, ++ -1, 0, -1, -1, -1, -1, 1); ++ if (props_size != LZMA_PROPS_SIZE || result != SZ_OK) ++ return false; ++ ++ output->resize(props_size + out_len + kCompressedLengthFieldSize); ++ // Store the uncompressed length at the beginning of buffer. ++ uint32_t uncompressed_length = htonl(input->size()); ++ memcpy(&(*output)[0], &uncompressed_length, kCompressedLengthFieldSize); ++ return true; ++} ++ ++bool Decompress(std::vector* input, std::vector* output) { ++ if (input->size() < kCompressedLengthFieldSize + LZMA_PROPS_SIZE) ++ return false; ++ ++ // Assume the uncompressed length is stored at the beginning of the buffer ++ // in network byte order. ++ uint32_t uncompressed_length = 0; ++ memcpy(&uncompressed_length, &(*input)[0], kCompressedLengthFieldSize); ++ uncompressed_length = ntohl(uncompressed_length); ++ ++ output->resize(uncompressed_length); ++ uint8_t* input_start = &(*input)[kCompressedLengthFieldSize]; ++ size_t in_len = input->size() - LZMA_PROPS_SIZE; ++ size_t out_len = output->size(); ++ int result = LzmaUncompress(&(*output)[0], &out_len, ++ input_start + LZMA_PROPS_SIZE, ++ &in_len, input_start, LZMA_PROPS_SIZE); ++ ++ return result == SZ_OK; ++} ++ ++bool DumpResult(std::vector* result, const std::string* file_name) { ++ int fd = open(file_name->c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0600); ++ if (fd < 0) { ++ perror("opening output file"); ++ return false; ++ } ++ if (write(fd, &(*result)[0], result->size()) < 0) { ++ perror("writing output file"); ++ close(fd); ++ return false; ++ } ++ close(fd); ++ return true; ++} ++ ++} // namespace ++ ++int main(int argc, char** argv) { ++ if (argc != 3) return Usage(argv[0]); ++ ++ std::vector in_data; ++ if (!ReadFile(argv[2], &in_data)) { ++ std::fprintf(stderr, "Failed to read file!\n"); ++ return 1; ++ } ++ ++ std::vector out_data; ++ std::string file_name; ++ if (std::strncmp("compress", argv[1], 8) == 0) { ++ if (!Compress(&in_data, &out_data)) { ++ std::fprintf(stderr, "Failed to compress file.\n"); ++ return 1; ++ } ++ file_name = "compressed.dat"; ++ } else if (std::strncmp("decompress", argv[1], 10) == 0) { ++ if (!Decompress(&in_data, &out_data)) { ++ std::fprintf(stderr, "Failed to decompress file.\n"); ++ return 1; ++ } ++ file_name = "decompressed.dat"; ++ } else { ++ std::fprintf( ++ stderr, ++ "The second argument must be either 'compress' or 'decompress'."); ++ return 1; ++ } ++ ++ if (!DumpResult(&out_data, &file_name)) { ++ std::fprintf(stderr, "Failed to write the result.\n"); ++ return 1; ++ } ++} + +Property changes on: test/lzma.cc +___________________________________________________________________ +Added: svn:eol-style + + LF + +Index: ots-common.gypi +=================================================================== +--- ots-common.gypi (revision 83) ++++ ots-common.gypi (working copy) +@@ -68,6 +68,7 @@ + ], + 'ots_include_dirs': [ + 'include', ++ '.', + ], + }, + } +Index: ots-standalone.gyp +=================================================================== +--- ots-standalone.gyp (revision 83) ++++ ots-standalone.gyp (working copy) +@@ -96,6 +96,12 @@ + '<@(ots_include_dirs)', + ], + }, ++ 'dependencies': [ ++ 'third_party/lzma_sdk/lzma_sdk.gyp:lzma_sdk', ++ ], ++ 'export_dependent_settings': [ ++ 'third_party/lzma_sdk/lzma_sdk.gyp:lzma_sdk', ++ ], + }, + { + 'target_name': 'idempotent', +@@ -117,5 +123,15 @@ + }], + ], + }, ++ { ++ 'target_name': 'lzma', ++ 'type': 'executable', ++ 'sources': [ ++ 'test/lzma.cc', ++ ], ++ 'dependencies': [ ++ 'ots', ++ ], ++ }, + ], + } +Index: third_party/lzma_sdk/LzmaEnc.h +=================================================================== +--- third_party/lzma_sdk/LzmaEnc.h (revision 0) ++++ third_party/lzma_sdk/LzmaEnc.h (revision 0) +@@ -0,0 +1,80 @@ ++/* LzmaEnc.h -- LZMA Encoder ++2009-02-07 : Igor Pavlov : Public domain */ ++ ++#ifndef __LZMA_ENC_H ++#define __LZMA_ENC_H ++ ++#include "Types.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define LZMA_PROPS_SIZE 5 ++ ++typedef struct _CLzmaEncProps ++{ ++ int level; /* 0 <= level <= 9 */ ++ UInt32 dictSize; /* (1 << 12) <= dictSize <= (1 << 27) for 32-bit version ++ (1 << 12) <= dictSize <= (1 << 30) for 64-bit version ++ default = (1 << 24) */ ++ int lc; /* 0 <= lc <= 8, default = 3 */ ++ int lp; /* 0 <= lp <= 4, default = 0 */ ++ int pb; /* 0 <= pb <= 4, default = 2 */ ++ int algo; /* 0 - fast, 1 - normal, default = 1 */ ++ int fb; /* 5 <= fb <= 273, default = 32 */ ++ int btMode; /* 0 - hashChain Mode, 1 - binTree mode - normal, default = 1 */ ++ int numHashBytes; /* 2, 3 or 4, default = 4 */ ++ UInt32 mc; /* 1 <= mc <= (1 << 30), default = 32 */ ++ unsigned writeEndMark; /* 0 - do not write EOPM, 1 - write EOPM, default = 0 */ ++ int numThreads; /* 1 or 2, default = 2 */ ++} CLzmaEncProps; ++ ++void LzmaEncProps_Init(CLzmaEncProps *p); ++void LzmaEncProps_Normalize(CLzmaEncProps *p); ++UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2); ++ ++ ++/* ---------- CLzmaEncHandle Interface ---------- */ ++ ++/* LzmaEnc_* functions can return the following exit codes: ++Returns: ++ SZ_OK - OK ++ SZ_ERROR_MEM - Memory allocation error ++ SZ_ERROR_PARAM - Incorrect paramater in props ++ SZ_ERROR_WRITE - Write callback error. ++ SZ_ERROR_PROGRESS - some break from progress callback ++ SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) ++*/ ++ ++typedef void * CLzmaEncHandle; ++ ++CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc); ++void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig); ++SRes LzmaEnc_SetProps(CLzmaEncHandle p, const CLzmaEncProps *props); ++SRes LzmaEnc_WriteProperties(CLzmaEncHandle p, Byte *properties, SizeT *size); ++SRes LzmaEnc_Encode(CLzmaEncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream, ++ ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); ++SRes LzmaEnc_MemEncode(CLzmaEncHandle p, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, ++ int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); ++ ++/* ---------- One Call Interface ---------- */ ++ ++/* LzmaEncode ++Return code: ++ SZ_OK - OK ++ SZ_ERROR_MEM - Memory allocation error ++ SZ_ERROR_PARAM - Incorrect paramater ++ SZ_ERROR_OUTPUT_EOF - output buffer overflow ++ SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) ++*/ ++ ++SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, ++ const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, ++ ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif + +Property changes on: third_party/lzma_sdk/LzmaEnc.h +___________________________________________________________________ +Added: svn:eol-style + + LF + +Index: third_party/lzma_sdk/LzHash.h +=================================================================== +--- third_party/lzma_sdk/LzHash.h (revision 0) ++++ third_party/lzma_sdk/LzHash.h (revision 0) +@@ -0,0 +1,54 @@ ++/* LzHash.h -- HASH functions for LZ algorithms ++2009-02-07 : Igor Pavlov : Public domain */ ++ ++#ifndef __LZ_HASH_H ++#define __LZ_HASH_H ++ ++#define kHash2Size (1 << 10) ++#define kHash3Size (1 << 16) ++#define kHash4Size (1 << 20) ++ ++#define kFix3HashSize (kHash2Size) ++#define kFix4HashSize (kHash2Size + kHash3Size) ++#define kFix5HashSize (kHash2Size + kHash3Size + kHash4Size) ++ ++#define HASH2_CALC hashValue = cur[0] | ((UInt32)cur[1] << 8); ++ ++#define HASH3_CALC { \ ++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ ++ hash2Value = temp & (kHash2Size - 1); \ ++ hashValue = (temp ^ ((UInt32)cur[2] << 8)) & p->hashMask; } ++ ++#define HASH4_CALC { \ ++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ ++ hash2Value = temp & (kHash2Size - 1); \ ++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ ++ hashValue = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & p->hashMask; } ++ ++#define HASH5_CALC { \ ++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ ++ hash2Value = temp & (kHash2Size - 1); \ ++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ ++ hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)); \ ++ hashValue = (hash4Value ^ (p->crc[cur[4]] << 3)) & p->hashMask; \ ++ hash4Value &= (kHash4Size - 1); } ++ ++/* #define HASH_ZIP_CALC hashValue = ((cur[0] | ((UInt32)cur[1] << 8)) ^ p->crc[cur[2]]) & 0xFFFF; */ ++#define HASH_ZIP_CALC hashValue = ((cur[2] | ((UInt32)cur[0] << 8)) ^ p->crc[cur[1]]) & 0xFFFF; ++ ++ ++#define MT_HASH2_CALC \ ++ hash2Value = (p->crc[cur[0]] ^ cur[1]) & (kHash2Size - 1); ++ ++#define MT_HASH3_CALC { \ ++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ ++ hash2Value = temp & (kHash2Size - 1); \ ++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); } ++ ++#define MT_HASH4_CALC { \ ++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ ++ hash2Value = temp & (kHash2Size - 1); \ ++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ ++ hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & (kHash4Size - 1); } ++ ++#endif + +Property changes on: third_party/lzma_sdk/LzHash.h +___________________________________________________________________ +Added: svn:executable + + * +Added: svn:eol-style + + LF + +Index: third_party/lzma_sdk/Alloc.h +=================================================================== +--- third_party/lzma_sdk/Alloc.h (revision 0) ++++ third_party/lzma_sdk/Alloc.h (revision 0) +@@ -0,0 +1,38 @@ ++/* Alloc.h -- Memory allocation functions ++2009-02-07 : Igor Pavlov : Public domain */ ++ ++#ifndef __COMMON_ALLOC_H ++#define __COMMON_ALLOC_H ++ ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++void *MyAlloc(size_t size); ++void MyFree(void *address); ++ ++#ifdef _WIN32 ++ ++void SetLargePageSize(); ++ ++void *MidAlloc(size_t size); ++void MidFree(void *address); ++void *BigAlloc(size_t size); ++void BigFree(void *address); ++ ++#else ++ ++#define MidAlloc(size) MyAlloc(size) ++#define MidFree(address) MyFree(address) ++#define BigAlloc(size) MyAlloc(size) ++#define BigFree(address) MyFree(address) ++ ++#endif ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif + +Property changes on: third_party/lzma_sdk/Alloc.h +___________________________________________________________________ +Added: svn:eol-style + + LF + +Index: third_party/lzma_sdk/LzmaLib.h +=================================================================== +--- third_party/lzma_sdk/LzmaLib.h (revision 0) ++++ third_party/lzma_sdk/LzmaLib.h (revision 0) +@@ -0,0 +1,135 @@ ++/* LzmaLib.h -- LZMA library interface ++2009-04-07 : Igor Pavlov : Public domain */ ++ ++#ifndef __LZMA_LIB_H ++#define __LZMA_LIB_H ++ ++#include "Types.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define MY_STDAPI int MY_STD_CALL ++ ++#define LZMA_PROPS_SIZE 5 ++ ++/* ++RAM requirements for LZMA: ++ for compression: (dictSize * 11.5 + 6 MB) + state_size ++ for decompression: dictSize + state_size ++ state_size = (4 + (1.5 << (lc + lp))) KB ++ by default (lc=3, lp=0), state_size = 16 KB. ++ ++LZMA properties (5 bytes) format ++ Offset Size Description ++ 0 1 lc, lp and pb in encoded form. ++ 1 4 dictSize (little endian). ++*/ ++ ++/* ++LzmaCompress ++------------ ++ ++outPropsSize - ++ In: the pointer to the size of outProps buffer; *outPropsSize = LZMA_PROPS_SIZE = 5. ++ Out: the pointer to the size of written properties in outProps buffer; *outPropsSize = LZMA_PROPS_SIZE = 5. ++ ++ LZMA Encoder will use defult values for any parameter, if it is ++ -1 for any from: level, loc, lp, pb, fb, numThreads ++ 0 for dictSize ++ ++level - compression level: 0 <= level <= 9; ++ ++ level dictSize algo fb ++ 0: 16 KB 0 32 ++ 1: 64 KB 0 32 ++ 2: 256 KB 0 32 ++ 3: 1 MB 0 32 ++ 4: 4 MB 0 32 ++ 5: 16 MB 1 32 ++ 6: 32 MB 1 32 ++ 7+: 64 MB 1 64 ++ ++ The default value for "level" is 5. ++ ++ algo = 0 means fast method ++ algo = 1 means normal method ++ ++dictSize - The dictionary size in bytes. The maximum value is ++ 128 MB = (1 << 27) bytes for 32-bit version ++ 1 GB = (1 << 30) bytes for 64-bit version ++ The default value is 16 MB = (1 << 24) bytes. ++ It's recommended to use the dictionary that is larger than 4 KB and ++ that can be calculated as (1 << N) or (3 << N) sizes. ++ ++lc - The number of literal context bits (high bits of previous literal). ++ It can be in the range from 0 to 8. The default value is 3. ++ Sometimes lc=4 gives the gain for big files. ++ ++lp - The number of literal pos bits (low bits of current position for literals). ++ It can be in the range from 0 to 4. The default value is 0. ++ The lp switch is intended for periodical data when the period is equal to 2^lp. ++ For example, for 32-bit (4 bytes) periodical data you can use lp=2. Often it's ++ better to set lc=0, if you change lp switch. ++ ++pb - The number of pos bits (low bits of current position). ++ It can be in the range from 0 to 4. The default value is 2. ++ The pb switch is intended for periodical data when the period is equal 2^pb. ++ ++fb - Word size (the number of fast bytes). ++ It can be in the range from 5 to 273. The default value is 32. ++ Usually, a big number gives a little bit better compression ratio and ++ slower compression process. ++ ++numThreads - The number of thereads. 1 or 2. The default value is 2. ++ Fast mode (algo = 0) can use only 1 thread. ++ ++Out: ++ destLen - processed output size ++Returns: ++ SZ_OK - OK ++ SZ_ERROR_MEM - Memory allocation error ++ SZ_ERROR_PARAM - Incorrect paramater ++ SZ_ERROR_OUTPUT_EOF - output buffer overflow ++ SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) ++*/ ++ ++MY_STDAPI LzmaCompress(unsigned char *dest, size_t *destLen, const unsigned char *src, size_t srcLen, ++ unsigned char *outProps, size_t *outPropsSize, /* *outPropsSize must be = 5 */ ++ int level, /* 0 <= level <= 9, default = 5 */ ++ unsigned dictSize, /* default = (1 << 24) */ ++ int lc, /* 0 <= lc <= 8, default = 3 */ ++ int lp, /* 0 <= lp <= 4, default = 0 */ ++ int pb, /* 0 <= pb <= 4, default = 2 */ ++ int fb, /* 5 <= fb <= 273, default = 32 */ ++ int numThreads /* 1 or 2, default = 2 */ ++ ); ++ ++/* ++LzmaUncompress ++-------------- ++In: ++ dest - output data ++ destLen - output data size ++ src - input data ++ srcLen - input data size ++Out: ++ destLen - processed output size ++ srcLen - processed input size ++Returns: ++ SZ_OK - OK ++ SZ_ERROR_DATA - Data error ++ SZ_ERROR_MEM - Memory allocation arror ++ SZ_ERROR_UNSUPPORTED - Unsupported properties ++ SZ_ERROR_INPUT_EOF - it needs more bytes in input buffer (src) ++*/ ++ ++MY_STDAPI LzmaUncompress(unsigned char *dest, size_t *destLen, const unsigned char *src, SizeT *srcLen, ++ const unsigned char *props, size_t propsSize); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif + +Property changes on: third_party/lzma_sdk/LzmaLib.h +___________________________________________________________________ +Added: svn:eol-style + + LF + +Index: third_party/lzma_sdk/LICENSE +=================================================================== +--- third_party/lzma_sdk/LICENSE (revision 0) ++++ third_party/lzma_sdk/LICENSE (revision 0) +@@ -0,0 +1 @@ ++LZMA SDK is placed in the public domain. +Index: third_party/lzma_sdk/Types.h +=================================================================== +--- third_party/lzma_sdk/Types.h (revision 0) ++++ third_party/lzma_sdk/Types.h (revision 0) +@@ -0,0 +1,254 @@ ++/* Types.h -- Basic types ++2010-10-09 : Igor Pavlov : Public domain */ ++ ++#ifndef __7Z_TYPES_H ++#define __7Z_TYPES_H ++ ++#include ++ ++#ifdef _WIN32 ++#include ++#endif ++ ++#ifndef EXTERN_C_BEGIN ++#ifdef __cplusplus ++#define EXTERN_C_BEGIN extern "C" { ++#define EXTERN_C_END } ++#else ++#define EXTERN_C_BEGIN ++#define EXTERN_C_END ++#endif ++#endif ++ ++EXTERN_C_BEGIN ++ ++#define SZ_OK 0 ++ ++#define SZ_ERROR_DATA 1 ++#define SZ_ERROR_MEM 2 ++#define SZ_ERROR_CRC 3 ++#define SZ_ERROR_UNSUPPORTED 4 ++#define SZ_ERROR_PARAM 5 ++#define SZ_ERROR_INPUT_EOF 6 ++#define SZ_ERROR_OUTPUT_EOF 7 ++#define SZ_ERROR_READ 8 ++#define SZ_ERROR_WRITE 9 ++#define SZ_ERROR_PROGRESS 10 ++#define SZ_ERROR_FAIL 11 ++#define SZ_ERROR_THREAD 12 ++ ++#define SZ_ERROR_ARCHIVE 16 ++#define SZ_ERROR_NO_ARCHIVE 17 ++ ++typedef int SRes; ++ ++#ifdef _WIN32 ++typedef DWORD WRes; ++#else ++typedef int WRes; ++#endif ++ ++#ifndef RINOK ++#define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; } ++#endif ++ ++typedef unsigned char Byte; ++typedef short Int16; ++typedef unsigned short UInt16; ++ ++#ifdef _LZMA_UINT32_IS_ULONG ++typedef long Int32; ++typedef unsigned long UInt32; ++#else ++typedef int Int32; ++typedef unsigned int UInt32; ++#endif ++ ++#ifdef _SZ_NO_INT_64 ++ ++/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers. ++ NOTES: Some code will work incorrectly in that case! */ ++ ++typedef long Int64; ++typedef unsigned long UInt64; ++ ++#else ++ ++#if defined(_MSC_VER) || defined(__BORLANDC__) ++typedef __int64 Int64; ++typedef unsigned __int64 UInt64; ++#define UINT64_CONST(n) n ++#else ++typedef long long int Int64; ++typedef unsigned long long int UInt64; ++#define UINT64_CONST(n) n ## ULL ++#endif ++ ++#endif ++ ++#ifdef _LZMA_NO_SYSTEM_SIZE_T ++typedef UInt32 SizeT; ++#else ++typedef size_t SizeT; ++#endif ++ ++typedef int Bool; ++#define True 1 ++#define False 0 ++ ++ ++#ifdef _WIN32 ++#define MY_STD_CALL __stdcall ++#else ++#define MY_STD_CALL ++#endif ++ ++#ifdef _MSC_VER ++ ++#if _MSC_VER >= 1300 ++#define MY_NO_INLINE __declspec(noinline) ++#else ++#define MY_NO_INLINE ++#endif ++ ++#define MY_CDECL __cdecl ++#define MY_FAST_CALL __fastcall ++ ++#else ++ ++#define MY_CDECL ++#define MY_FAST_CALL ++ ++#endif ++ ++ ++/* The following interfaces use first parameter as pointer to structure */ ++ ++typedef struct ++{ ++ Byte (*Read)(void *p); /* reads one byte, returns 0 in case of EOF or error */ ++} IByteIn; ++ ++typedef struct ++{ ++ void (*Write)(void *p, Byte b); ++} IByteOut; ++ ++typedef struct ++{ ++ SRes (*Read)(void *p, void *buf, size_t *size); ++ /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. ++ (output(*size) < input(*size)) is allowed */ ++} ISeqInStream; ++ ++/* it can return SZ_ERROR_INPUT_EOF */ ++SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size); ++SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType); ++SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf); ++ ++typedef struct ++{ ++ size_t (*Write)(void *p, const void *buf, size_t size); ++ /* Returns: result - the number of actually written bytes. ++ (result < size) means error */ ++} ISeqOutStream; ++ ++typedef enum ++{ ++ SZ_SEEK_SET = 0, ++ SZ_SEEK_CUR = 1, ++ SZ_SEEK_END = 2 ++} ESzSeek; ++ ++typedef struct ++{ ++ SRes (*Read)(void *p, void *buf, size_t *size); /* same as ISeqInStream::Read */ ++ SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); ++} ISeekInStream; ++ ++typedef struct ++{ ++ SRes (*Look)(void *p, const void **buf, size_t *size); ++ /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. ++ (output(*size) > input(*size)) is not allowed ++ (output(*size) < input(*size)) is allowed */ ++ SRes (*Skip)(void *p, size_t offset); ++ /* offset must be <= output(*size) of Look */ ++ ++ SRes (*Read)(void *p, void *buf, size_t *size); ++ /* reads directly (without buffer). It's same as ISeqInStream::Read */ ++ SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); ++} ILookInStream; ++ ++SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size); ++SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset); ++ ++/* reads via ILookInStream::Read */ ++SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType); ++SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size); ++ ++#define LookToRead_BUF_SIZE (1 << 14) ++ ++typedef struct ++{ ++ ILookInStream s; ++ ISeekInStream *realStream; ++ size_t pos; ++ size_t size; ++ Byte buf[LookToRead_BUF_SIZE]; ++} CLookToRead; ++ ++void LookToRead_CreateVTable(CLookToRead *p, int lookahead); ++void LookToRead_Init(CLookToRead *p); ++ ++typedef struct ++{ ++ ISeqInStream s; ++ ILookInStream *realStream; ++} CSecToLook; ++ ++void SecToLook_CreateVTable(CSecToLook *p); ++ ++typedef struct ++{ ++ ISeqInStream s; ++ ILookInStream *realStream; ++} CSecToRead; ++ ++void SecToRead_CreateVTable(CSecToRead *p); ++ ++typedef struct ++{ ++ SRes (*Progress)(void *p, UInt64 inSize, UInt64 outSize); ++ /* Returns: result. (result != SZ_OK) means break. ++ Value (UInt64)(Int64)-1 for size means unknown value. */ ++} ICompressProgress; ++ ++typedef struct ++{ ++ void *(*Alloc)(void *p, size_t size); ++ void (*Free)(void *p, void *address); /* address can be 0 */ ++} ISzAlloc; ++ ++#define IAlloc_Alloc(p, size) (p)->Alloc((p), size) ++#define IAlloc_Free(p, a) (p)->Free((p), a) ++ ++#ifdef _WIN32 ++ ++#define CHAR_PATH_SEPARATOR '\\' ++#define WCHAR_PATH_SEPARATOR L'\\' ++#define STRING_PATH_SEPARATOR "\\" ++#define WSTRING_PATH_SEPARATOR L"\\" ++ ++#else ++ ++#define CHAR_PATH_SEPARATOR '/' ++#define WCHAR_PATH_SEPARATOR L'/' ++#define STRING_PATH_SEPARATOR "/" ++#define WSTRING_PATH_SEPARATOR L"/" ++ ++#endif ++ ++EXTERN_C_END ++ ++#endif + +Property changes on: third_party/lzma_sdk/Types.h +___________________________________________________________________ +Added: svn:eol-style + + LF + +Index: third_party/lzma_sdk/LzmaDec.c +=================================================================== +--- third_party/lzma_sdk/LzmaDec.c (revision 0) ++++ third_party/lzma_sdk/LzmaDec.c (revision 0) +@@ -0,0 +1,999 @@ ++/* LzmaDec.c -- LZMA Decoder ++2009-09-20 : Igor Pavlov : Public domain */ ++ ++#include "LzmaDec.h" ++ ++#include ++ ++#define kNumTopBits 24 ++#define kTopValue ((UInt32)1 << kNumTopBits) ++ ++#define kNumBitModelTotalBits 11 ++#define kBitModelTotal (1 << kNumBitModelTotalBits) ++#define kNumMoveBits 5 ++ ++#define RC_INIT_SIZE 5 ++ ++#define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } ++ ++#define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) ++#define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); ++#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); ++#define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \ ++ { UPDATE_0(p); i = (i + i); A0; } else \ ++ { UPDATE_1(p); i = (i + i) + 1; A1; } ++#define GET_BIT(p, i) GET_BIT2(p, i, ; , ;) ++ ++#define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); } ++#define TREE_DECODE(probs, limit, i) \ ++ { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; } ++ ++/* #define _LZMA_SIZE_OPT */ ++ ++#ifdef _LZMA_SIZE_OPT ++#define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i) ++#else ++#define TREE_6_DECODE(probs, i) \ ++ { i = 1; \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ i -= 0x40; } ++#endif ++ ++#define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } ++ ++#define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) ++#define UPDATE_0_CHECK range = bound; ++#define UPDATE_1_CHECK range -= bound; code -= bound; ++#define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \ ++ { UPDATE_0_CHECK; i = (i + i); A0; } else \ ++ { UPDATE_1_CHECK; i = (i + i) + 1; A1; } ++#define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;) ++#define TREE_DECODE_CHECK(probs, limit, i) \ ++ { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; } ++ ++ ++#define kNumPosBitsMax 4 ++#define kNumPosStatesMax (1 << kNumPosBitsMax) ++ ++#define kLenNumLowBits 3 ++#define kLenNumLowSymbols (1 << kLenNumLowBits) ++#define kLenNumMidBits 3 ++#define kLenNumMidSymbols (1 << kLenNumMidBits) ++#define kLenNumHighBits 8 ++#define kLenNumHighSymbols (1 << kLenNumHighBits) ++ ++#define LenChoice 0 ++#define LenChoice2 (LenChoice + 1) ++#define LenLow (LenChoice2 + 1) ++#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) ++#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) ++#define kNumLenProbs (LenHigh + kLenNumHighSymbols) ++ ++ ++#define kNumStates 12 ++#define kNumLitStates 7 ++ ++#define kStartPosModelIndex 4 ++#define kEndPosModelIndex 14 ++#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) ++ ++#define kNumPosSlotBits 6 ++#define kNumLenToPosStates 4 ++ ++#define kNumAlignBits 4 ++#define kAlignTableSize (1 << kNumAlignBits) ++ ++#define kMatchMinLen 2 ++#define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) ++ ++#define IsMatch 0 ++#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) ++#define IsRepG0 (IsRep + kNumStates) ++#define IsRepG1 (IsRepG0 + kNumStates) ++#define IsRepG2 (IsRepG1 + kNumStates) ++#define IsRep0Long (IsRepG2 + kNumStates) ++#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) ++#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) ++#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) ++#define LenCoder (Align + kAlignTableSize) ++#define RepLenCoder (LenCoder + kNumLenProbs) ++#define Literal (RepLenCoder + kNumLenProbs) ++ ++#define LZMA_BASE_SIZE 1846 ++#define LZMA_LIT_SIZE 768 ++ ++#define LzmaProps_GetNumProbs(p) ((UInt32)LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) ++ ++#if Literal != LZMA_BASE_SIZE ++StopCompilingDueBUG ++#endif ++ ++#define LZMA_DIC_MIN (1 << 12) ++ ++/* First LZMA-symbol is always decoded. ++And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization ++Out: ++ Result: ++ SZ_OK - OK ++ SZ_ERROR_DATA - Error ++ p->remainLen: ++ < kMatchSpecLenStart : normal remain ++ = kMatchSpecLenStart : finished ++ = kMatchSpecLenStart + 1 : Flush marker ++ = kMatchSpecLenStart + 2 : State Init Marker ++*/ ++ ++static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit) ++{ ++ CLzmaProb *probs = p->probs; ++ ++ unsigned state = p->state; ++ UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3]; ++ unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1; ++ unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1; ++ unsigned lc = p->prop.lc; ++ ++ Byte *dic = p->dic; ++ SizeT dicBufSize = p->dicBufSize; ++ SizeT dicPos = p->dicPos; ++ ++ UInt32 processedPos = p->processedPos; ++ UInt32 checkDicSize = p->checkDicSize; ++ unsigned len = 0; ++ ++ const Byte *buf = p->buf; ++ UInt32 range = p->range; ++ UInt32 code = p->code; ++ ++ do ++ { ++ CLzmaProb *prob; ++ UInt32 bound; ++ unsigned ttt; ++ unsigned posState = processedPos & pbMask; ++ ++ prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; ++ IF_BIT_0(prob) ++ { ++ unsigned symbol; ++ UPDATE_0(prob); ++ prob = probs + Literal; ++ if (checkDicSize != 0 || processedPos != 0) ++ prob += (LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + ++ (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); ++ ++ if (state < kNumLitStates) ++ { ++ state -= (state < 4) ? state : 3; ++ symbol = 1; ++ do { GET_BIT(prob + symbol, symbol) } while (symbol < 0x100); ++ } ++ else ++ { ++ unsigned matchByte = p->dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; ++ unsigned offs = 0x100; ++ state -= (state < 10) ? 3 : 6; ++ symbol = 1; ++ do ++ { ++ unsigned bit; ++ CLzmaProb *probLit; ++ matchByte <<= 1; ++ bit = (matchByte & offs); ++ probLit = prob + offs + bit + symbol; ++ GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit) ++ } ++ while (symbol < 0x100); ++ } ++ dic[dicPos++] = (Byte)symbol; ++ processedPos++; ++ continue; ++ } ++ else ++ { ++ UPDATE_1(prob); ++ prob = probs + IsRep + state; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ state += kNumStates; ++ prob = probs + LenCoder; ++ } ++ else ++ { ++ UPDATE_1(prob); ++ if (checkDicSize == 0 && processedPos == 0) ++ return SZ_ERROR_DATA; ++ prob = probs + IsRepG0 + state; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; ++ dicPos++; ++ processedPos++; ++ state = state < kNumLitStates ? 9 : 11; ++ continue; ++ } ++ UPDATE_1(prob); ++ } ++ else ++ { ++ UInt32 distance; ++ UPDATE_1(prob); ++ prob = probs + IsRepG1 + state; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ distance = rep1; ++ } ++ else ++ { ++ UPDATE_1(prob); ++ prob = probs + IsRepG2 + state; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ distance = rep2; ++ } ++ else ++ { ++ UPDATE_1(prob); ++ distance = rep3; ++ rep3 = rep2; ++ } ++ rep2 = rep1; ++ } ++ rep1 = rep0; ++ rep0 = distance; ++ } ++ state = state < kNumLitStates ? 8 : 11; ++ prob = probs + RepLenCoder; ++ } ++ { ++ unsigned limit, offset; ++ CLzmaProb *probLen = prob + LenChoice; ++ IF_BIT_0(probLen) ++ { ++ UPDATE_0(probLen); ++ probLen = prob + LenLow + (posState << kLenNumLowBits); ++ offset = 0; ++ limit = (1 << kLenNumLowBits); ++ } ++ else ++ { ++ UPDATE_1(probLen); ++ probLen = prob + LenChoice2; ++ IF_BIT_0(probLen) ++ { ++ UPDATE_0(probLen); ++ probLen = prob + LenMid + (posState << kLenNumMidBits); ++ offset = kLenNumLowSymbols; ++ limit = (1 << kLenNumMidBits); ++ } ++ else ++ { ++ UPDATE_1(probLen); ++ probLen = prob + LenHigh; ++ offset = kLenNumLowSymbols + kLenNumMidSymbols; ++ limit = (1 << kLenNumHighBits); ++ } ++ } ++ TREE_DECODE(probLen, limit, len); ++ len += offset; ++ } ++ ++ if (state >= kNumStates) ++ { ++ UInt32 distance; ++ prob = probs + PosSlot + ++ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); ++ TREE_6_DECODE(prob, distance); ++ if (distance >= kStartPosModelIndex) ++ { ++ unsigned posSlot = (unsigned)distance; ++ int numDirectBits = (int)(((distance >> 1) - 1)); ++ distance = (2 | (distance & 1)); ++ if (posSlot < kEndPosModelIndex) ++ { ++ distance <<= numDirectBits; ++ prob = probs + SpecPos + distance - posSlot - 1; ++ { ++ UInt32 mask = 1; ++ unsigned i = 1; ++ do ++ { ++ GET_BIT2(prob + i, i, ; , distance |= mask); ++ mask <<= 1; ++ } ++ while (--numDirectBits != 0); ++ } ++ } ++ else ++ { ++ numDirectBits -= kNumAlignBits; ++ do ++ { ++ NORMALIZE ++ range >>= 1; ++ ++ { ++ UInt32 t; ++ code -= range; ++ t = (0 - ((UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */ ++ distance = (distance << 1) + (t + 1); ++ code += range & t; ++ } ++ /* ++ distance <<= 1; ++ if (code >= range) ++ { ++ code -= range; ++ distance |= 1; ++ } ++ */ ++ } ++ while (--numDirectBits != 0); ++ prob = probs + Align; ++ distance <<= kNumAlignBits; ++ { ++ unsigned i = 1; ++ GET_BIT2(prob + i, i, ; , distance |= 1); ++ GET_BIT2(prob + i, i, ; , distance |= 2); ++ GET_BIT2(prob + i, i, ; , distance |= 4); ++ GET_BIT2(prob + i, i, ; , distance |= 8); ++ } ++ if (distance == (UInt32)0xFFFFFFFF) ++ { ++ len += kMatchSpecLenStart; ++ state -= kNumStates; ++ break; ++ } ++ } ++ } ++ rep3 = rep2; ++ rep2 = rep1; ++ rep1 = rep0; ++ rep0 = distance + 1; ++ if (checkDicSize == 0) ++ { ++ if (distance >= processedPos) ++ return SZ_ERROR_DATA; ++ } ++ else if (distance >= checkDicSize) ++ return SZ_ERROR_DATA; ++ state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; ++ } ++ ++ len += kMatchMinLen; ++ ++ if (limit == dicPos) ++ return SZ_ERROR_DATA; ++ { ++ SizeT rem = limit - dicPos; ++ unsigned curLen = ((rem < len) ? (unsigned)rem : len); ++ SizeT pos = (dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0); ++ ++ processedPos += curLen; ++ ++ len -= curLen; ++ if (pos + curLen <= dicBufSize) ++ { ++ Byte *dest = dic + dicPos; ++ ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos; ++ const Byte *lim = dest + curLen; ++ dicPos += curLen; ++ do ++ *(dest) = (Byte)*(dest + src); ++ while (++dest != lim); ++ } ++ else ++ { ++ do ++ { ++ dic[dicPos++] = dic[pos]; ++ if (++pos == dicBufSize) ++ pos = 0; ++ } ++ while (--curLen != 0); ++ } ++ } ++ } ++ } ++ while (dicPos < limit && buf < bufLimit); ++ NORMALIZE; ++ p->buf = buf; ++ p->range = range; ++ p->code = code; ++ p->remainLen = len; ++ p->dicPos = dicPos; ++ p->processedPos = processedPos; ++ p->reps[0] = rep0; ++ p->reps[1] = rep1; ++ p->reps[2] = rep2; ++ p->reps[3] = rep3; ++ p->state = state; ++ ++ return SZ_OK; ++} ++ ++static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit) ++{ ++ if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart) ++ { ++ Byte *dic = p->dic; ++ SizeT dicPos = p->dicPos; ++ SizeT dicBufSize = p->dicBufSize; ++ unsigned len = p->remainLen; ++ UInt32 rep0 = p->reps[0]; ++ if (limit - dicPos < len) ++ len = (unsigned)(limit - dicPos); ++ ++ if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len) ++ p->checkDicSize = p->prop.dicSize; ++ ++ p->processedPos += len; ++ p->remainLen -= len; ++ while (len-- != 0) ++ { ++ dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; ++ dicPos++; ++ } ++ p->dicPos = dicPos; ++ } ++} ++ ++static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit) ++{ ++ do ++ { ++ SizeT limit2 = limit; ++ if (p->checkDicSize == 0) ++ { ++ UInt32 rem = p->prop.dicSize - p->processedPos; ++ if (limit - p->dicPos > rem) ++ limit2 = p->dicPos + rem; ++ } ++ RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit)); ++ if (p->processedPos >= p->prop.dicSize) ++ p->checkDicSize = p->prop.dicSize; ++ LzmaDec_WriteRem(p, limit); ++ } ++ while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart); ++ ++ if (p->remainLen > kMatchSpecLenStart) ++ { ++ p->remainLen = kMatchSpecLenStart; ++ } ++ return 0; ++} ++ ++typedef enum ++{ ++ DUMMY_ERROR, /* unexpected end of input stream */ ++ DUMMY_LIT, ++ DUMMY_MATCH, ++ DUMMY_REP ++} ELzmaDummy; ++ ++static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize) ++{ ++ UInt32 range = p->range; ++ UInt32 code = p->code; ++ const Byte *bufLimit = buf + inSize; ++ CLzmaProb *probs = p->probs; ++ unsigned state = p->state; ++ ELzmaDummy res; ++ ++ { ++ CLzmaProb *prob; ++ UInt32 bound; ++ unsigned ttt; ++ unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1); ++ ++ prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK ++ ++ /* if (bufLimit - buf >= 7) return DUMMY_LIT; */ ++ ++ prob = probs + Literal; ++ if (p->checkDicSize != 0 || p->processedPos != 0) ++ prob += (LZMA_LIT_SIZE * ++ ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) + ++ (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc)))); ++ ++ if (state < kNumLitStates) ++ { ++ unsigned symbol = 1; ++ do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100); ++ } ++ else ++ { ++ unsigned matchByte = p->dic[p->dicPos - p->reps[0] + ++ ((p->dicPos < p->reps[0]) ? p->dicBufSize : 0)]; ++ unsigned offs = 0x100; ++ unsigned symbol = 1; ++ do ++ { ++ unsigned bit; ++ CLzmaProb *probLit; ++ matchByte <<= 1; ++ bit = (matchByte & offs); ++ probLit = prob + offs + bit + symbol; ++ GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit) ++ } ++ while (symbol < 0x100); ++ } ++ res = DUMMY_LIT; ++ } ++ else ++ { ++ unsigned len; ++ UPDATE_1_CHECK; ++ ++ prob = probs + IsRep + state; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ state = 0; ++ prob = probs + LenCoder; ++ res = DUMMY_MATCH; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ res = DUMMY_REP; ++ prob = probs + IsRepG0 + state; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ NORMALIZE_CHECK; ++ return DUMMY_REP; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ } ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ prob = probs + IsRepG1 + state; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ prob = probs + IsRepG2 + state; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ } ++ } ++ } ++ state = kNumStates; ++ prob = probs + RepLenCoder; ++ } ++ { ++ unsigned limit, offset; ++ CLzmaProb *probLen = prob + LenChoice; ++ IF_BIT_0_CHECK(probLen) ++ { ++ UPDATE_0_CHECK; ++ probLen = prob + LenLow + (posState << kLenNumLowBits); ++ offset = 0; ++ limit = 1 << kLenNumLowBits; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ probLen = prob + LenChoice2; ++ IF_BIT_0_CHECK(probLen) ++ { ++ UPDATE_0_CHECK; ++ probLen = prob + LenMid + (posState << kLenNumMidBits); ++ offset = kLenNumLowSymbols; ++ limit = 1 << kLenNumMidBits; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ probLen = prob + LenHigh; ++ offset = kLenNumLowSymbols + kLenNumMidSymbols; ++ limit = 1 << kLenNumHighBits; ++ } ++ } ++ TREE_DECODE_CHECK(probLen, limit, len); ++ len += offset; ++ } ++ ++ if (state < 4) ++ { ++ unsigned posSlot; ++ prob = probs + PosSlot + ++ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << ++ kNumPosSlotBits); ++ TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot); ++ if (posSlot >= kStartPosModelIndex) ++ { ++ int numDirectBits = ((posSlot >> 1) - 1); ++ ++ /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */ ++ ++ if (posSlot < kEndPosModelIndex) ++ { ++ prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1; ++ } ++ else ++ { ++ numDirectBits -= kNumAlignBits; ++ do ++ { ++ NORMALIZE_CHECK ++ range >>= 1; ++ code -= range & (((code - range) >> 31) - 1); ++ /* if (code >= range) code -= range; */ ++ } ++ while (--numDirectBits != 0); ++ prob = probs + Align; ++ numDirectBits = kNumAlignBits; ++ } ++ { ++ unsigned i = 1; ++ do ++ { ++ GET_BIT_CHECK(prob + i, i); ++ } ++ while (--numDirectBits != 0); ++ } ++ } ++ } ++ } ++ } ++ NORMALIZE_CHECK; ++ return res; ++} ++ ++ ++static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data) ++{ ++ p->code = ((UInt32)data[1] << 24) | ((UInt32)data[2] << 16) | ((UInt32)data[3] << 8) | ((UInt32)data[4]); ++ p->range = 0xFFFFFFFF; ++ p->needFlush = 0; ++} ++ ++void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) ++{ ++ p->needFlush = 1; ++ p->remainLen = 0; ++ p->tempBufSize = 0; ++ ++ if (initDic) ++ { ++ p->processedPos = 0; ++ p->checkDicSize = 0; ++ p->needInitState = 1; ++ } ++ if (initState) ++ p->needInitState = 1; ++} ++ ++void LzmaDec_Init(CLzmaDec *p) ++{ ++ p->dicPos = 0; ++ LzmaDec_InitDicAndState(p, True, True); ++} ++ ++static void LzmaDec_InitStateReal(CLzmaDec *p) ++{ ++ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (p->prop.lc + p->prop.lp)); ++ UInt32 i; ++ CLzmaProb *probs = p->probs; ++ for (i = 0; i < numProbs; i++) ++ probs[i] = kBitModelTotal >> 1; ++ p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1; ++ p->state = 0; ++ p->needInitState = 0; ++} ++ ++SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, ++ ELzmaFinishMode finishMode, ELzmaStatus *status) ++{ ++ SizeT inSize = *srcLen; ++ (*srcLen) = 0; ++ LzmaDec_WriteRem(p, dicLimit); ++ ++ *status = LZMA_STATUS_NOT_SPECIFIED; ++ ++ while (p->remainLen != kMatchSpecLenStart) ++ { ++ int checkEndMarkNow; ++ ++ if (p->needFlush != 0) ++ { ++ for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) ++ p->tempBuf[p->tempBufSize++] = *src++; ++ if (p->tempBufSize < RC_INIT_SIZE) ++ { ++ *status = LZMA_STATUS_NEEDS_MORE_INPUT; ++ return SZ_OK; ++ } ++ if (p->tempBuf[0] != 0) ++ return SZ_ERROR_DATA; ++ ++ LzmaDec_InitRc(p, p->tempBuf); ++ p->tempBufSize = 0; ++ } ++ ++ checkEndMarkNow = 0; ++ if (p->dicPos >= dicLimit) ++ { ++ if (p->remainLen == 0 && p->code == 0) ++ { ++ *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK; ++ return SZ_OK; ++ } ++ if (finishMode == LZMA_FINISH_ANY) ++ { ++ *status = LZMA_STATUS_NOT_FINISHED; ++ return SZ_OK; ++ } ++ if (p->remainLen != 0) ++ { ++ *status = LZMA_STATUS_NOT_FINISHED; ++ return SZ_ERROR_DATA; ++ } ++ checkEndMarkNow = 1; ++ } ++ ++ if (p->needInitState) ++ LzmaDec_InitStateReal(p); ++ ++ if (p->tempBufSize == 0) ++ { ++ SizeT processed; ++ const Byte *bufLimit; ++ if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) ++ { ++ int dummyRes = LzmaDec_TryDummy(p, src, inSize); ++ if (dummyRes == DUMMY_ERROR) ++ { ++ memcpy(p->tempBuf, src, inSize); ++ p->tempBufSize = (unsigned)inSize; ++ (*srcLen) += inSize; ++ *status = LZMA_STATUS_NEEDS_MORE_INPUT; ++ return SZ_OK; ++ } ++ if (checkEndMarkNow && dummyRes != DUMMY_MATCH) ++ { ++ *status = LZMA_STATUS_NOT_FINISHED; ++ return SZ_ERROR_DATA; ++ } ++ bufLimit = src; ++ } ++ else ++ bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX; ++ p->buf = src; ++ if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0) ++ return SZ_ERROR_DATA; ++ processed = (SizeT)(p->buf - src); ++ (*srcLen) += processed; ++ src += processed; ++ inSize -= processed; ++ } ++ else ++ { ++ unsigned rem = p->tempBufSize, lookAhead = 0; ++ while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize) ++ p->tempBuf[rem++] = src[lookAhead++]; ++ p->tempBufSize = rem; ++ if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) ++ { ++ int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem); ++ if (dummyRes == DUMMY_ERROR) ++ { ++ (*srcLen) += lookAhead; ++ *status = LZMA_STATUS_NEEDS_MORE_INPUT; ++ return SZ_OK; ++ } ++ if (checkEndMarkNow && dummyRes != DUMMY_MATCH) ++ { ++ *status = LZMA_STATUS_NOT_FINISHED; ++ return SZ_ERROR_DATA; ++ } ++ } ++ p->buf = p->tempBuf; ++ if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0) ++ return SZ_ERROR_DATA; ++ lookAhead -= (rem - (unsigned)(p->buf - p->tempBuf)); ++ (*srcLen) += lookAhead; ++ src += lookAhead; ++ inSize -= lookAhead; ++ p->tempBufSize = 0; ++ } ++ } ++ if (p->code == 0) ++ *status = LZMA_STATUS_FINISHED_WITH_MARK; ++ return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA; ++} ++ ++SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) ++{ ++ SizeT outSize = *destLen; ++ SizeT inSize = *srcLen; ++ *srcLen = *destLen = 0; ++ for (;;) ++ { ++ SizeT inSizeCur = inSize, outSizeCur, dicPos; ++ ELzmaFinishMode curFinishMode; ++ SRes res; ++ if (p->dicPos == p->dicBufSize) ++ p->dicPos = 0; ++ dicPos = p->dicPos; ++ if (outSize > p->dicBufSize - dicPos) ++ { ++ outSizeCur = p->dicBufSize; ++ curFinishMode = LZMA_FINISH_ANY; ++ } ++ else ++ { ++ outSizeCur = dicPos + outSize; ++ curFinishMode = finishMode; ++ } ++ ++ res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status); ++ src += inSizeCur; ++ inSize -= inSizeCur; ++ *srcLen += inSizeCur; ++ outSizeCur = p->dicPos - dicPos; ++ memcpy(dest, p->dic + dicPos, outSizeCur); ++ dest += outSizeCur; ++ outSize -= outSizeCur; ++ *destLen += outSizeCur; ++ if (res != 0) ++ return res; ++ if (outSizeCur == 0 || outSize == 0) ++ return SZ_OK; ++ } ++} ++ ++void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->probs); ++ p->probs = 0; ++} ++ ++static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->dic); ++ p->dic = 0; ++} ++ ++void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc) ++{ ++ LzmaDec_FreeProbs(p, alloc); ++ LzmaDec_FreeDict(p, alloc); ++} ++ ++SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size) ++{ ++ UInt32 dicSize; ++ Byte d; ++ ++ if (size < LZMA_PROPS_SIZE) ++ return SZ_ERROR_UNSUPPORTED; ++ else ++ dicSize = data[1] | ((UInt32)data[2] << 8) | ((UInt32)data[3] << 16) | ((UInt32)data[4] << 24); ++ ++ if (dicSize < LZMA_DIC_MIN) ++ dicSize = LZMA_DIC_MIN; ++ p->dicSize = dicSize; ++ ++ d = data[0]; ++ if (d >= (9 * 5 * 5)) ++ return SZ_ERROR_UNSUPPORTED; ++ ++ p->lc = d % 9; ++ d /= 9; ++ p->pb = d / 5; ++ p->lp = d % 5; ++ ++ return SZ_OK; ++} ++ ++static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc) ++{ ++ UInt32 numProbs = LzmaProps_GetNumProbs(propNew); ++ if (p->probs == 0 || numProbs != p->numProbs) ++ { ++ LzmaDec_FreeProbs(p, alloc); ++ p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb)); ++ p->numProbs = numProbs; ++ if (p->probs == 0) ++ return SZ_ERROR_MEM; ++ } ++ return SZ_OK; ++} ++ ++SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) ++{ ++ CLzmaProps propNew; ++ RINOK(LzmaProps_Decode(&propNew, props, propsSize)); ++ RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); ++ p->prop = propNew; ++ return SZ_OK; ++} ++ ++SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) ++{ ++ CLzmaProps propNew; ++ SizeT dicBufSize; ++ RINOK(LzmaProps_Decode(&propNew, props, propsSize)); ++ RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); ++ dicBufSize = propNew.dicSize; ++ if (p->dic == 0 || dicBufSize != p->dicBufSize) ++ { ++ LzmaDec_FreeDict(p, alloc); ++ p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize); ++ if (p->dic == 0) ++ { ++ LzmaDec_FreeProbs(p, alloc); ++ return SZ_ERROR_MEM; ++ } ++ } ++ p->dicBufSize = dicBufSize; ++ p->prop = propNew; ++ return SZ_OK; ++} ++ ++SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ++ const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, ++ ELzmaStatus *status, ISzAlloc *alloc) ++{ ++ CLzmaDec p; ++ SRes res; ++ SizeT inSize = *srcLen; ++ SizeT outSize = *destLen; ++ *srcLen = *destLen = 0; ++ if (inSize < RC_INIT_SIZE) ++ return SZ_ERROR_INPUT_EOF; ++ ++ LzmaDec_Construct(&p); ++ res = LzmaDec_AllocateProbs(&p, propData, propSize, alloc); ++ if (res != 0) ++ return res; ++ p.dic = dest; ++ p.dicBufSize = outSize; ++ ++ LzmaDec_Init(&p); ++ ++ *srcLen = inSize; ++ res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status); ++ ++ if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) ++ res = SZ_ERROR_INPUT_EOF; ++ ++ (*destLen) = p.dicPos; ++ LzmaDec_FreeProbs(&p, alloc); ++ return res; ++} + +Property changes on: third_party/lzma_sdk/LzmaDec.c +___________________________________________________________________ +Added: svn:eol-style + + LF + +Index: third_party/lzma_sdk/README.ots +=================================================================== +--- third_party/lzma_sdk/README.ots (revision 0) ++++ third_party/lzma_sdk/README.ots (revision 0) +@@ -0,0 +1,8 @@ ++Name: LZMA SDK ++URL: http://www.7-zip.org/sdk.html ++Version: 9.20 ++ ++Description: ++The LZMA SDK provides the documentation, samples, header files, libraries, and tools you need to develop applications that use LZMA compression. ++ ++This contains only the C code required to decompress LZMA. +Index: third_party/lzma_sdk/lzma_sdk.gyp +=================================================================== +--- third_party/lzma_sdk/lzma_sdk.gyp (revision 0) ++++ third_party/lzma_sdk/lzma_sdk.gyp (revision 0) +@@ -0,0 +1,33 @@ ++# Copyright (c) 2012 The Chromium Authors. All rights reserved. ++# Use of this source code is governed by a BSD-style license that can be ++# found in the LICENSE file. ++ ++{ ++ 'targets': [ ++ { ++ 'target_name': 'lzma_sdk', ++ 'type': 'static_library', ++ 'defines': [ ++ '_7ZIP_ST', ++ '_LZMA_PROB32', ++ ], ++ 'sources': [ ++ 'Alloc.c', ++ 'Alloc.h', ++ 'LzFind.c', ++ 'LzFind.h', ++ 'LzHash.h', ++ 'LzmaEnc.c', ++ 'LzmaEnc.h', ++ 'LzmaDec.c', ++ 'LzmaDec.h', ++ 'LzmaLib.c', ++ 'LzmaLib.h', ++ 'Types.h', ++ ], ++ 'include_dirs': [ ++ '.', ++ ], ++ }, ++ ], ++} + +Property changes on: third_party/lzma_sdk/lzma_sdk.gyp +___________________________________________________________________ +Added: svn:eol-style + + LF + +Index: third_party/lzma_sdk/LzmaDec.h +=================================================================== +--- third_party/lzma_sdk/LzmaDec.h (revision 0) ++++ third_party/lzma_sdk/LzmaDec.h (revision 0) +@@ -0,0 +1,231 @@ ++/* LzmaDec.h -- LZMA Decoder ++2009-02-07 : Igor Pavlov : Public domain */ ++ ++#ifndef __LZMA_DEC_H ++#define __LZMA_DEC_H ++ ++#include "Types.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/* #define _LZMA_PROB32 */ ++/* _LZMA_PROB32 can increase the speed on some CPUs, ++ but memory usage for CLzmaDec::probs will be doubled in that case */ ++ ++#ifdef _LZMA_PROB32 ++#define CLzmaProb UInt32 ++#else ++#define CLzmaProb UInt16 ++#endif ++ ++ ++/* ---------- LZMA Properties ---------- */ ++ ++#define LZMA_PROPS_SIZE 5 ++ ++typedef struct _CLzmaProps ++{ ++ unsigned lc, lp, pb; ++ UInt32 dicSize; ++} CLzmaProps; ++ ++/* LzmaProps_Decode - decodes properties ++Returns: ++ SZ_OK ++ SZ_ERROR_UNSUPPORTED - Unsupported properties ++*/ ++ ++SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size); ++ ++ ++/* ---------- LZMA Decoder state ---------- */ ++ ++/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case. ++ Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */ ++ ++#define LZMA_REQUIRED_INPUT_MAX 20 ++ ++typedef struct ++{ ++ CLzmaProps prop; ++ CLzmaProb *probs; ++ Byte *dic; ++ const Byte *buf; ++ UInt32 range, code; ++ SizeT dicPos; ++ SizeT dicBufSize; ++ UInt32 processedPos; ++ UInt32 checkDicSize; ++ unsigned state; ++ UInt32 reps[4]; ++ unsigned remainLen; ++ int needFlush; ++ int needInitState; ++ UInt32 numProbs; ++ unsigned tempBufSize; ++ Byte tempBuf[LZMA_REQUIRED_INPUT_MAX]; ++} CLzmaDec; ++ ++#define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; } ++ ++void LzmaDec_Init(CLzmaDec *p); ++ ++/* There are two types of LZMA streams: ++ 0) Stream with end mark. That end mark adds about 6 bytes to compressed size. ++ 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */ ++ ++typedef enum ++{ ++ LZMA_FINISH_ANY, /* finish at any point */ ++ LZMA_FINISH_END /* block must be finished at the end */ ++} ELzmaFinishMode; ++ ++/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!! ++ ++ You must use LZMA_FINISH_END, when you know that current output buffer ++ covers last bytes of block. In other cases you must use LZMA_FINISH_ANY. ++ ++ If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK, ++ and output value of destLen will be less than output buffer size limit. ++ You can check status result also. ++ ++ You can use multiple checks to test data integrity after full decompression: ++ 1) Check Result and "status" variable. ++ 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. ++ 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. ++ You must use correct finish mode in that case. */ ++ ++typedef enum ++{ ++ LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */ ++ LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ ++ LZMA_STATUS_NOT_FINISHED, /* stream was not finished */ ++ LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */ ++ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */ ++} ELzmaStatus; ++ ++/* ELzmaStatus is used only as output value for function call */ ++ ++ ++/* ---------- Interfaces ---------- */ ++ ++/* There are 3 levels of interfaces: ++ 1) Dictionary Interface ++ 2) Buffer Interface ++ 3) One Call Interface ++ You can select any of these interfaces, but don't mix functions from different ++ groups for same object. */ ++ ++ ++/* There are two variants to allocate state for Dictionary Interface: ++ 1) LzmaDec_Allocate / LzmaDec_Free ++ 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs ++ You can use variant 2, if you set dictionary buffer manually. ++ For Buffer Interface you must always use variant 1. ++ ++LzmaDec_Allocate* can return: ++ SZ_OK ++ SZ_ERROR_MEM - Memory allocation error ++ SZ_ERROR_UNSUPPORTED - Unsupported properties ++*/ ++ ++SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc); ++void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc); ++ ++SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc); ++void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc); ++ ++/* ---------- Dictionary Interface ---------- */ ++ ++/* You can use it, if you want to eliminate the overhead for data copying from ++ dictionary to some other external buffer. ++ You must work with CLzmaDec variables directly in this interface. ++ ++ STEPS: ++ LzmaDec_Constr() ++ LzmaDec_Allocate() ++ for (each new stream) ++ { ++ LzmaDec_Init() ++ while (it needs more decompression) ++ { ++ LzmaDec_DecodeToDic() ++ use data from CLzmaDec::dic and update CLzmaDec::dicPos ++ } ++ } ++ LzmaDec_Free() ++*/ ++ ++/* LzmaDec_DecodeToDic ++ ++ The decoding to internal dictionary buffer (CLzmaDec::dic). ++ You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!! ++ ++finishMode: ++ It has meaning only if the decoding reaches output limit (dicLimit). ++ LZMA_FINISH_ANY - Decode just dicLimit bytes. ++ LZMA_FINISH_END - Stream must be finished after dicLimit. ++ ++Returns: ++ SZ_OK ++ status: ++ LZMA_STATUS_FINISHED_WITH_MARK ++ LZMA_STATUS_NOT_FINISHED ++ LZMA_STATUS_NEEDS_MORE_INPUT ++ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK ++ SZ_ERROR_DATA - Data error ++*/ ++ ++SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, ++ const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); ++ ++ ++/* ---------- Buffer Interface ---------- */ ++ ++/* It's zlib-like interface. ++ See LzmaDec_DecodeToDic description for information about STEPS and return results, ++ but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need ++ to work with CLzmaDec variables manually. ++ ++finishMode: ++ It has meaning only if the decoding reaches output limit (*destLen). ++ LZMA_FINISH_ANY - Decode just destLen bytes. ++ LZMA_FINISH_END - Stream must be finished after (*destLen). ++*/ ++ ++SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, ++ const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); ++ ++ ++/* ---------- One Call Interface ---------- */ ++ ++/* LzmaDecode ++ ++finishMode: ++ It has meaning only if the decoding reaches output limit (*destLen). ++ LZMA_FINISH_ANY - Decode just destLen bytes. ++ LZMA_FINISH_END - Stream must be finished after (*destLen). ++ ++Returns: ++ SZ_OK ++ status: ++ LZMA_STATUS_FINISHED_WITH_MARK ++ LZMA_STATUS_NOT_FINISHED ++ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK ++ SZ_ERROR_DATA - Data error ++ SZ_ERROR_MEM - Memory allocation error ++ SZ_ERROR_UNSUPPORTED - Unsupported properties ++ SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). ++*/ ++ ++SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ++ const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, ++ ELzmaStatus *status, ISzAlloc *alloc); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif + +Property changes on: third_party/lzma_sdk/LzmaDec.h +___________________________________________________________________ +Added: svn:eol-style + + LF + +Index: third_party/lzma_sdk/LzFind.c +=================================================================== +--- third_party/lzma_sdk/LzFind.c (revision 0) ++++ third_party/lzma_sdk/LzFind.c (revision 0) +@@ -0,0 +1,761 @@ ++/* LzFind.c -- Match finder for LZ algorithms ++2009-04-22 : Igor Pavlov : Public domain */ ++ ++#include ++ ++#include "LzFind.h" ++#include "LzHash.h" ++ ++#define kEmptyHashValue 0 ++#define kMaxValForNormalize ((UInt32)0xFFFFFFFF) ++#define kNormalizeStepMin (1 << 10) /* it must be power of 2 */ ++#define kNormalizeMask (~(kNormalizeStepMin - 1)) ++#define kMaxHistorySize ((UInt32)3 << 30) ++ ++#define kStartMaxLen 3 ++ ++static void LzInWindow_Free(CMatchFinder *p, ISzAlloc *alloc) ++{ ++ if (!p->directInput) ++ { ++ alloc->Free(alloc, p->bufferBase); ++ p->bufferBase = 0; ++ } ++} ++ ++/* keepSizeBefore + keepSizeAfter + keepSizeReserv must be < 4G) */ ++ ++static int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAlloc *alloc) ++{ ++ UInt32 blockSize = p->keepSizeBefore + p->keepSizeAfter + keepSizeReserv; ++ if (p->directInput) ++ { ++ p->blockSize = blockSize; ++ return 1; ++ } ++ if (p->bufferBase == 0 || p->blockSize != blockSize) ++ { ++ LzInWindow_Free(p, alloc); ++ p->blockSize = blockSize; ++ p->bufferBase = (Byte *)alloc->Alloc(alloc, (size_t)blockSize); ++ } ++ return (p->bufferBase != 0); ++} ++ ++Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; } ++Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; } ++ ++UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; } ++ ++void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue) ++{ ++ p->posLimit -= subValue; ++ p->pos -= subValue; ++ p->streamPos -= subValue; ++} ++ ++static void MatchFinder_ReadBlock(CMatchFinder *p) ++{ ++ if (p->streamEndWasReached || p->result != SZ_OK) ++ return; ++ if (p->directInput) ++ { ++ UInt32 curSize = 0xFFFFFFFF - p->streamPos; ++ if (curSize > p->directInputRem) ++ curSize = (UInt32)p->directInputRem; ++ p->directInputRem -= curSize; ++ p->streamPos += curSize; ++ if (p->directInputRem == 0) ++ p->streamEndWasReached = 1; ++ return; ++ } ++ for (;;) ++ { ++ Byte *dest = p->buffer + (p->streamPos - p->pos); ++ size_t size = (p->bufferBase + p->blockSize - dest); ++ if (size == 0) ++ return; ++ p->result = p->stream->Read(p->stream, dest, &size); ++ if (p->result != SZ_OK) ++ return; ++ if (size == 0) ++ { ++ p->streamEndWasReached = 1; ++ return; ++ } ++ p->streamPos += (UInt32)size; ++ if (p->streamPos - p->pos > p->keepSizeAfter) ++ return; ++ } ++} ++ ++void MatchFinder_MoveBlock(CMatchFinder *p) ++{ ++ memmove(p->bufferBase, ++ p->buffer - p->keepSizeBefore, ++ (size_t)(p->streamPos - p->pos + p->keepSizeBefore)); ++ p->buffer = p->bufferBase + p->keepSizeBefore; ++} ++ ++int MatchFinder_NeedMove(CMatchFinder *p) ++{ ++ if (p->directInput) ++ return 0; ++ /* if (p->streamEndWasReached) return 0; */ ++ return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter); ++} ++ ++void MatchFinder_ReadIfRequired(CMatchFinder *p) ++{ ++ if (p->streamEndWasReached) ++ return; ++ if (p->keepSizeAfter >= p->streamPos - p->pos) ++ MatchFinder_ReadBlock(p); ++} ++ ++static void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p) ++{ ++ if (MatchFinder_NeedMove(p)) ++ MatchFinder_MoveBlock(p); ++ MatchFinder_ReadBlock(p); ++} ++ ++static void MatchFinder_SetDefaultSettings(CMatchFinder *p) ++{ ++ p->cutValue = 32; ++ p->btMode = 1; ++ p->numHashBytes = 4; ++ p->bigHash = 0; ++} ++ ++#define kCrcPoly 0xEDB88320 ++ ++void MatchFinder_Construct(CMatchFinder *p) ++{ ++ UInt32 i; ++ p->bufferBase = 0; ++ p->directInput = 0; ++ p->hash = 0; ++ MatchFinder_SetDefaultSettings(p); ++ ++ for (i = 0; i < 256; i++) ++ { ++ UInt32 r = i; ++ int j; ++ for (j = 0; j < 8; j++) ++ r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); ++ p->crc[i] = r; ++ } ++} ++ ++static void MatchFinder_FreeThisClassMemory(CMatchFinder *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->hash); ++ p->hash = 0; ++} ++ ++void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc) ++{ ++ MatchFinder_FreeThisClassMemory(p, alloc); ++ LzInWindow_Free(p, alloc); ++} ++ ++static CLzRef* AllocRefs(UInt32 num, ISzAlloc *alloc) ++{ ++ size_t sizeInBytes = (size_t)num * sizeof(CLzRef); ++ if (sizeInBytes / sizeof(CLzRef) != num) ++ return 0; ++ return (CLzRef *)alloc->Alloc(alloc, sizeInBytes); ++} ++ ++int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, ++ UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ++ ISzAlloc *alloc) ++{ ++ UInt32 sizeReserv; ++ if (historySize > kMaxHistorySize) ++ { ++ MatchFinder_Free(p, alloc); ++ return 0; ++ } ++ sizeReserv = historySize >> 1; ++ if (historySize > ((UInt32)2 << 30)) ++ sizeReserv = historySize >> 2; ++ sizeReserv += (keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + (1 << 19); ++ ++ p->keepSizeBefore = historySize + keepAddBufferBefore + 1; ++ p->keepSizeAfter = matchMaxLen + keepAddBufferAfter; ++ /* we need one additional byte, since we use MoveBlock after pos++ and before dictionary using */ ++ if (LzInWindow_Create(p, sizeReserv, alloc)) ++ { ++ UInt32 newCyclicBufferSize = historySize + 1; ++ UInt32 hs; ++ p->matchMaxLen = matchMaxLen; ++ { ++ p->fixedHashSize = 0; ++ if (p->numHashBytes == 2) ++ hs = (1 << 16) - 1; ++ else ++ { ++ hs = historySize - 1; ++ hs |= (hs >> 1); ++ hs |= (hs >> 2); ++ hs |= (hs >> 4); ++ hs |= (hs >> 8); ++ hs >>= 1; ++ hs |= 0xFFFF; /* don't change it! It's required for Deflate */ ++ if (hs > (1 << 24)) ++ { ++ if (p->numHashBytes == 3) ++ hs = (1 << 24) - 1; ++ else ++ hs >>= 1; ++ } ++ } ++ p->hashMask = hs; ++ hs++; ++ if (p->numHashBytes > 2) p->fixedHashSize += kHash2Size; ++ if (p->numHashBytes > 3) p->fixedHashSize += kHash3Size; ++ if (p->numHashBytes > 4) p->fixedHashSize += kHash4Size; ++ hs += p->fixedHashSize; ++ } ++ ++ { ++ UInt32 prevSize = p->hashSizeSum + p->numSons; ++ UInt32 newSize; ++ p->historySize = historySize; ++ p->hashSizeSum = hs; ++ p->cyclicBufferSize = newCyclicBufferSize; ++ p->numSons = (p->btMode ? newCyclicBufferSize * 2 : newCyclicBufferSize); ++ newSize = p->hashSizeSum + p->numSons; ++ if (p->hash != 0 && prevSize == newSize) ++ return 1; ++ MatchFinder_FreeThisClassMemory(p, alloc); ++ p->hash = AllocRefs(newSize, alloc); ++ if (p->hash != 0) ++ { ++ p->son = p->hash + p->hashSizeSum; ++ return 1; ++ } ++ } ++ } ++ MatchFinder_Free(p, alloc); ++ return 0; ++} ++ ++static void MatchFinder_SetLimits(CMatchFinder *p) ++{ ++ UInt32 limit = kMaxValForNormalize - p->pos; ++ UInt32 limit2 = p->cyclicBufferSize - p->cyclicBufferPos; ++ if (limit2 < limit) ++ limit = limit2; ++ limit2 = p->streamPos - p->pos; ++ if (limit2 <= p->keepSizeAfter) ++ { ++ if (limit2 > 0) ++ limit2 = 1; ++ } ++ else ++ limit2 -= p->keepSizeAfter; ++ if (limit2 < limit) ++ limit = limit2; ++ { ++ UInt32 lenLimit = p->streamPos - p->pos; ++ if (lenLimit > p->matchMaxLen) ++ lenLimit = p->matchMaxLen; ++ p->lenLimit = lenLimit; ++ } ++ p->posLimit = p->pos + limit; ++} ++ ++void MatchFinder_Init(CMatchFinder *p) ++{ ++ UInt32 i; ++ for (i = 0; i < p->hashSizeSum; i++) ++ p->hash[i] = kEmptyHashValue; ++ p->cyclicBufferPos = 0; ++ p->buffer = p->bufferBase; ++ p->pos = p->streamPos = p->cyclicBufferSize; ++ p->result = SZ_OK; ++ p->streamEndWasReached = 0; ++ MatchFinder_ReadBlock(p); ++ MatchFinder_SetLimits(p); ++} ++ ++static UInt32 MatchFinder_GetSubValue(CMatchFinder *p) ++{ ++ return (p->pos - p->historySize - 1) & kNormalizeMask; ++} ++ ++void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems) ++{ ++ UInt32 i; ++ for (i = 0; i < numItems; i++) ++ { ++ UInt32 value = items[i]; ++ if (value <= subValue) ++ value = kEmptyHashValue; ++ else ++ value -= subValue; ++ items[i] = value; ++ } ++} ++ ++static void MatchFinder_Normalize(CMatchFinder *p) ++{ ++ UInt32 subValue = MatchFinder_GetSubValue(p); ++ MatchFinder_Normalize3(subValue, p->hash, p->hashSizeSum + p->numSons); ++ MatchFinder_ReduceOffsets(p, subValue); ++} ++ ++static void MatchFinder_CheckLimits(CMatchFinder *p) ++{ ++ if (p->pos == kMaxValForNormalize) ++ MatchFinder_Normalize(p); ++ if (!p->streamEndWasReached && p->keepSizeAfter == p->streamPos - p->pos) ++ MatchFinder_CheckAndMoveAndRead(p); ++ if (p->cyclicBufferPos == p->cyclicBufferSize) ++ p->cyclicBufferPos = 0; ++ MatchFinder_SetLimits(p); ++} ++ ++static UInt32 * Hc_GetMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, ++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, ++ UInt32 *distances, UInt32 maxLen) ++{ ++ son[_cyclicBufferPos] = curMatch; ++ for (;;) ++ { ++ UInt32 delta = pos - curMatch; ++ if (cutValue-- == 0 || delta >= _cyclicBufferSize) ++ return distances; ++ { ++ const Byte *pb = cur - delta; ++ curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)]; ++ if (pb[maxLen] == cur[maxLen] && *pb == *cur) ++ { ++ UInt32 len = 0; ++ while (++len != lenLimit) ++ if (pb[len] != cur[len]) ++ break; ++ if (maxLen < len) ++ { ++ *distances++ = maxLen = len; ++ *distances++ = delta - 1; ++ if (len == lenLimit) ++ return distances; ++ } ++ } ++ } ++ } ++} ++ ++UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, ++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, ++ UInt32 *distances, UInt32 maxLen) ++{ ++ CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; ++ CLzRef *ptr1 = son + (_cyclicBufferPos << 1); ++ UInt32 len0 = 0, len1 = 0; ++ for (;;) ++ { ++ UInt32 delta = pos - curMatch; ++ if (cutValue-- == 0 || delta >= _cyclicBufferSize) ++ { ++ *ptr0 = *ptr1 = kEmptyHashValue; ++ return distances; ++ } ++ { ++ CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); ++ const Byte *pb = cur - delta; ++ UInt32 len = (len0 < len1 ? len0 : len1); ++ if (pb[len] == cur[len]) ++ { ++ if (++len != lenLimit && pb[len] == cur[len]) ++ while (++len != lenLimit) ++ if (pb[len] != cur[len]) ++ break; ++ if (maxLen < len) ++ { ++ *distances++ = maxLen = len; ++ *distances++ = delta - 1; ++ if (len == lenLimit) ++ { ++ *ptr1 = pair[0]; ++ *ptr0 = pair[1]; ++ return distances; ++ } ++ } ++ } ++ if (pb[len] < cur[len]) ++ { ++ *ptr1 = curMatch; ++ ptr1 = pair + 1; ++ curMatch = *ptr1; ++ len1 = len; ++ } ++ else ++ { ++ *ptr0 = curMatch; ++ ptr0 = pair; ++ curMatch = *ptr0; ++ len0 = len; ++ } ++ } ++ } ++} ++ ++static void SkipMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, ++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue) ++{ ++ CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; ++ CLzRef *ptr1 = son + (_cyclicBufferPos << 1); ++ UInt32 len0 = 0, len1 = 0; ++ for (;;) ++ { ++ UInt32 delta = pos - curMatch; ++ if (cutValue-- == 0 || delta >= _cyclicBufferSize) ++ { ++ *ptr0 = *ptr1 = kEmptyHashValue; ++ return; ++ } ++ { ++ CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); ++ const Byte *pb = cur - delta; ++ UInt32 len = (len0 < len1 ? len0 : len1); ++ if (pb[len] == cur[len]) ++ { ++ while (++len != lenLimit) ++ if (pb[len] != cur[len]) ++ break; ++ { ++ if (len == lenLimit) ++ { ++ *ptr1 = pair[0]; ++ *ptr0 = pair[1]; ++ return; ++ } ++ } ++ } ++ if (pb[len] < cur[len]) ++ { ++ *ptr1 = curMatch; ++ ptr1 = pair + 1; ++ curMatch = *ptr1; ++ len1 = len; ++ } ++ else ++ { ++ *ptr0 = curMatch; ++ ptr0 = pair; ++ curMatch = *ptr0; ++ len0 = len; ++ } ++ } ++ } ++} ++ ++#define MOVE_POS \ ++ ++p->cyclicBufferPos; \ ++ p->buffer++; \ ++ if (++p->pos == p->posLimit) MatchFinder_CheckLimits(p); ++ ++#define MOVE_POS_RET MOVE_POS return offset; ++ ++static void MatchFinder_MovePos(CMatchFinder *p) { MOVE_POS; } ++ ++#define GET_MATCHES_HEADER2(minLen, ret_op) \ ++ UInt32 lenLimit; UInt32 hashValue; const Byte *cur; UInt32 curMatch; \ ++ lenLimit = p->lenLimit; { if (lenLimit < minLen) { MatchFinder_MovePos(p); ret_op; }} \ ++ cur = p->buffer; ++ ++#define GET_MATCHES_HEADER(minLen) GET_MATCHES_HEADER2(minLen, return 0) ++#define SKIP_HEADER(minLen) GET_MATCHES_HEADER2(minLen, continue) ++ ++#define MF_PARAMS(p) p->pos, p->buffer, p->son, p->cyclicBufferPos, p->cyclicBufferSize, p->cutValue ++ ++#define GET_MATCHES_FOOTER(offset, maxLen) \ ++ offset = (UInt32)(GetMatchesSpec1(lenLimit, curMatch, MF_PARAMS(p), \ ++ distances + offset, maxLen) - distances); MOVE_POS_RET; ++ ++#define SKIP_FOOTER \ ++ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS; ++ ++static UInt32 Bt2_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 offset; ++ GET_MATCHES_HEADER(2) ++ HASH2_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ offset = 0; ++ GET_MATCHES_FOOTER(offset, 1) ++} ++ ++UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 offset; ++ GET_MATCHES_HEADER(3) ++ HASH_ZIP_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ offset = 0; ++ GET_MATCHES_FOOTER(offset, 2) ++} ++ ++static UInt32 Bt3_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 hash2Value, delta2, maxLen, offset; ++ GET_MATCHES_HEADER(3) ++ ++ HASH3_CALC; ++ ++ delta2 = p->pos - p->hash[hash2Value]; ++ curMatch = p->hash[kFix3HashSize + hashValue]; ++ ++ p->hash[hash2Value] = ++ p->hash[kFix3HashSize + hashValue] = p->pos; ++ ++ ++ maxLen = 2; ++ offset = 0; ++ if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) ++ { ++ for (; maxLen != lenLimit; maxLen++) ++ if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) ++ break; ++ distances[0] = maxLen; ++ distances[1] = delta2 - 1; ++ offset = 2; ++ if (maxLen == lenLimit) ++ { ++ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); ++ MOVE_POS_RET; ++ } ++ } ++ GET_MATCHES_FOOTER(offset, maxLen) ++} ++ ++static UInt32 Bt4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; ++ GET_MATCHES_HEADER(4) ++ ++ HASH4_CALC; ++ ++ delta2 = p->pos - p->hash[ hash2Value]; ++ delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; ++ curMatch = p->hash[kFix4HashSize + hashValue]; ++ ++ p->hash[ hash2Value] = ++ p->hash[kFix3HashSize + hash3Value] = ++ p->hash[kFix4HashSize + hashValue] = p->pos; ++ ++ maxLen = 1; ++ offset = 0; ++ if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) ++ { ++ distances[0] = maxLen = 2; ++ distances[1] = delta2 - 1; ++ offset = 2; ++ } ++ if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) ++ { ++ maxLen = 3; ++ distances[offset + 1] = delta3 - 1; ++ offset += 2; ++ delta2 = delta3; ++ } ++ if (offset != 0) ++ { ++ for (; maxLen != lenLimit; maxLen++) ++ if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) ++ break; ++ distances[offset - 2] = maxLen; ++ if (maxLen == lenLimit) ++ { ++ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); ++ MOVE_POS_RET; ++ } ++ } ++ if (maxLen < 3) ++ maxLen = 3; ++ GET_MATCHES_FOOTER(offset, maxLen) ++} ++ ++static UInt32 Hc4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; ++ GET_MATCHES_HEADER(4) ++ ++ HASH4_CALC; ++ ++ delta2 = p->pos - p->hash[ hash2Value]; ++ delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; ++ curMatch = p->hash[kFix4HashSize + hashValue]; ++ ++ p->hash[ hash2Value] = ++ p->hash[kFix3HashSize + hash3Value] = ++ p->hash[kFix4HashSize + hashValue] = p->pos; ++ ++ maxLen = 1; ++ offset = 0; ++ if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) ++ { ++ distances[0] = maxLen = 2; ++ distances[1] = delta2 - 1; ++ offset = 2; ++ } ++ if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) ++ { ++ maxLen = 3; ++ distances[offset + 1] = delta3 - 1; ++ offset += 2; ++ delta2 = delta3; ++ } ++ if (offset != 0) ++ { ++ for (; maxLen != lenLimit; maxLen++) ++ if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) ++ break; ++ distances[offset - 2] = maxLen; ++ if (maxLen == lenLimit) ++ { ++ p->son[p->cyclicBufferPos] = curMatch; ++ MOVE_POS_RET; ++ } ++ } ++ if (maxLen < 3) ++ maxLen = 3; ++ offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), ++ distances + offset, maxLen) - (distances)); ++ MOVE_POS_RET ++} ++ ++UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 offset; ++ GET_MATCHES_HEADER(3) ++ HASH_ZIP_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), ++ distances, 2) - (distances)); ++ MOVE_POS_RET ++} ++ ++static void Bt2_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ SKIP_HEADER(2) ++ HASH2_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ SKIP_FOOTER ++ } ++ while (--num != 0); ++} ++ ++void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ SKIP_HEADER(3) ++ HASH_ZIP_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ SKIP_FOOTER ++ } ++ while (--num != 0); ++} ++ ++static void Bt3_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ UInt32 hash2Value; ++ SKIP_HEADER(3) ++ HASH3_CALC; ++ curMatch = p->hash[kFix3HashSize + hashValue]; ++ p->hash[hash2Value] = ++ p->hash[kFix3HashSize + hashValue] = p->pos; ++ SKIP_FOOTER ++ } ++ while (--num != 0); ++} ++ ++static void Bt4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ UInt32 hash2Value, hash3Value; ++ SKIP_HEADER(4) ++ HASH4_CALC; ++ curMatch = p->hash[kFix4HashSize + hashValue]; ++ p->hash[ hash2Value] = ++ p->hash[kFix3HashSize + hash3Value] = p->pos; ++ p->hash[kFix4HashSize + hashValue] = p->pos; ++ SKIP_FOOTER ++ } ++ while (--num != 0); ++} ++ ++static void Hc4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ UInt32 hash2Value, hash3Value; ++ SKIP_HEADER(4) ++ HASH4_CALC; ++ curMatch = p->hash[kFix4HashSize + hashValue]; ++ p->hash[ hash2Value] = ++ p->hash[kFix3HashSize + hash3Value] = ++ p->hash[kFix4HashSize + hashValue] = p->pos; ++ p->son[p->cyclicBufferPos] = curMatch; ++ MOVE_POS ++ } ++ while (--num != 0); ++} ++ ++void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ SKIP_HEADER(3) ++ HASH_ZIP_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ p->son[p->cyclicBufferPos] = curMatch; ++ MOVE_POS ++ } ++ while (--num != 0); ++} ++ ++void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable) ++{ ++ vTable->Init = (Mf_Init_Func)MatchFinder_Init; ++ vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinder_GetIndexByte; ++ vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinder_GetNumAvailableBytes; ++ vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinder_GetPointerToCurrentPos; ++ if (!p->btMode) ++ { ++ vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches; ++ vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip; ++ } ++ else if (p->numHashBytes == 2) ++ { ++ vTable->GetMatches = (Mf_GetMatches_Func)Bt2_MatchFinder_GetMatches; ++ vTable->Skip = (Mf_Skip_Func)Bt2_MatchFinder_Skip; ++ } ++ else if (p->numHashBytes == 3) ++ { ++ vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches; ++ vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip; ++ } ++ else ++ { ++ vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches; ++ vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip; ++ } ++} + +Property changes on: third_party/lzma_sdk/LzFind.c +___________________________________________________________________ +Added: svn:eol-style + + LF + +Index: third_party/lzma_sdk/LzmaEnc.c +=================================================================== +--- third_party/lzma_sdk/LzmaEnc.c (revision 0) ++++ third_party/lzma_sdk/LzmaEnc.c (revision 0) +@@ -0,0 +1,2268 @@ ++/* LzmaEnc.c -- LZMA Encoder ++2010-04-16 : Igor Pavlov : Public domain */ ++ ++#include ++ ++/* #define SHOW_STAT */ ++/* #define SHOW_STAT2 */ ++ ++#if defined(SHOW_STAT) || defined(SHOW_STAT2) ++#include ++#endif ++ ++#include "LzmaEnc.h" ++ ++#include "LzFind.h" ++#ifndef _7ZIP_ST ++#include "LzFindMt.h" ++#endif ++ ++#ifdef SHOW_STAT ++static int ttt = 0; ++#endif ++ ++#define kBlockSizeMax ((1 << LZMA_NUM_BLOCK_SIZE_BITS) - 1) ++ ++#define kBlockSize (9 << 10) ++#define kUnpackBlockSize (1 << 18) ++#define kMatchArraySize (1 << 21) ++#define kMatchRecordMaxSize ((LZMA_MATCH_LEN_MAX * 2 + 3) * LZMA_MATCH_LEN_MAX) ++ ++#define kNumMaxDirectBits (31) ++ ++#define kNumTopBits 24 ++#define kTopValue ((UInt32)1 << kNumTopBits) ++ ++#define kNumBitModelTotalBits 11 ++#define kBitModelTotal (1 << kNumBitModelTotalBits) ++#define kNumMoveBits 5 ++#define kProbInitValue (kBitModelTotal >> 1) ++ ++#define kNumMoveReducingBits 4 ++#define kNumBitPriceShiftBits 4 ++#define kBitPrice (1 << kNumBitPriceShiftBits) ++ ++void LzmaEncProps_Init(CLzmaEncProps *p) ++{ ++ p->level = 5; ++ p->dictSize = p->mc = 0; ++ p->lc = p->lp = p->pb = p->algo = p->fb = p->btMode = p->numHashBytes = p->numThreads = -1; ++ p->writeEndMark = 0; ++} ++ ++void LzmaEncProps_Normalize(CLzmaEncProps *p) ++{ ++ int level = p->level; ++ if (level < 0) level = 5; ++ p->level = level; ++ if (p->dictSize == 0) p->dictSize = (level <= 5 ? (1 << (level * 2 + 14)) : (level == 6 ? (1 << 25) : (1 << 26))); ++ if (p->lc < 0) p->lc = 3; ++ if (p->lp < 0) p->lp = 0; ++ if (p->pb < 0) p->pb = 2; ++ if (p->algo < 0) p->algo = (level < 5 ? 0 : 1); ++ if (p->fb < 0) p->fb = (level < 7 ? 32 : 64); ++ if (p->btMode < 0) p->btMode = (p->algo == 0 ? 0 : 1); ++ if (p->numHashBytes < 0) p->numHashBytes = 4; ++ if (p->mc == 0) p->mc = (16 + (p->fb >> 1)) >> (p->btMode ? 0 : 1); ++ if (p->numThreads < 0) ++ p->numThreads = ++ #ifndef _7ZIP_ST ++ ((p->btMode && p->algo) ? 2 : 1); ++ #else ++ 1; ++ #endif ++} ++ ++UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2) ++{ ++ CLzmaEncProps props = *props2; ++ LzmaEncProps_Normalize(&props); ++ return props.dictSize; ++} ++ ++/* #define LZMA_LOG_BSR */ ++/* Define it for Intel's CPU */ ++ ++ ++#ifdef LZMA_LOG_BSR ++ ++#define kDicLogSizeMaxCompress 30 ++ ++#define BSR2_RET(pos, res) { unsigned long i; _BitScanReverse(&i, (pos)); res = (i + i) + ((pos >> (i - 1)) & 1); } ++ ++UInt32 GetPosSlot1(UInt32 pos) ++{ ++ UInt32 res; ++ BSR2_RET(pos, res); ++ return res; ++} ++#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } ++#define GetPosSlot(pos, res) { if (pos < 2) res = pos; else BSR2_RET(pos, res); } ++ ++#else ++ ++#define kNumLogBits (9 + (int)sizeof(size_t) / 2) ++#define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7) ++ ++void LzmaEnc_FastPosInit(Byte *g_FastPos) ++{ ++ int c = 2, slotFast; ++ g_FastPos[0] = 0; ++ g_FastPos[1] = 1; ++ ++ for (slotFast = 2; slotFast < kNumLogBits * 2; slotFast++) ++ { ++ UInt32 k = (1 << ((slotFast >> 1) - 1)); ++ UInt32 j; ++ for (j = 0; j < k; j++, c++) ++ g_FastPos[c] = (Byte)slotFast; ++ } ++} ++ ++#define BSR2_RET(pos, res) { UInt32 i = 6 + ((kNumLogBits - 1) & \ ++ (0 - (((((UInt32)1 << (kNumLogBits + 6)) - 1) - pos) >> 31))); \ ++ res = p->g_FastPos[pos >> i] + (i * 2); } ++/* ++#define BSR2_RET(pos, res) { res = (pos < (1 << (kNumLogBits + 6))) ? \ ++ p->g_FastPos[pos >> 6] + 12 : \ ++ p->g_FastPos[pos >> (6 + kNumLogBits - 1)] + (6 + (kNumLogBits - 1)) * 2; } ++*/ ++ ++#define GetPosSlot1(pos) p->g_FastPos[pos] ++#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } ++#define GetPosSlot(pos, res) { if (pos < kNumFullDistances) res = p->g_FastPos[pos]; else BSR2_RET(pos, res); } ++ ++#endif ++ ++ ++#define LZMA_NUM_REPS 4 ++ ++typedef unsigned CState; ++ ++typedef struct ++{ ++ UInt32 price; ++ ++ CState state; ++ int prev1IsChar; ++ int prev2; ++ ++ UInt32 posPrev2; ++ UInt32 backPrev2; ++ ++ UInt32 posPrev; ++ UInt32 backPrev; ++ UInt32 backs[LZMA_NUM_REPS]; ++} COptimal; ++ ++#define kNumOpts (1 << 12) ++ ++#define kNumLenToPosStates 4 ++#define kNumPosSlotBits 6 ++#define kDicLogSizeMin 0 ++#define kDicLogSizeMax 32 ++#define kDistTableSizeMax (kDicLogSizeMax * 2) ++ ++ ++#define kNumAlignBits 4 ++#define kAlignTableSize (1 << kNumAlignBits) ++#define kAlignMask (kAlignTableSize - 1) ++ ++#define kStartPosModelIndex 4 ++#define kEndPosModelIndex 14 ++#define kNumPosModels (kEndPosModelIndex - kStartPosModelIndex) ++ ++#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) ++ ++#ifdef _LZMA_PROB32 ++#define CLzmaProb UInt32 ++#else ++#define CLzmaProb UInt16 ++#endif ++ ++#define LZMA_PB_MAX 4 ++#define LZMA_LC_MAX 8 ++#define LZMA_LP_MAX 4 ++ ++#define LZMA_NUM_PB_STATES_MAX (1 << LZMA_PB_MAX) ++ ++ ++#define kLenNumLowBits 3 ++#define kLenNumLowSymbols (1 << kLenNumLowBits) ++#define kLenNumMidBits 3 ++#define kLenNumMidSymbols (1 << kLenNumMidBits) ++#define kLenNumHighBits 8 ++#define kLenNumHighSymbols (1 << kLenNumHighBits) ++ ++#define kLenNumSymbolsTotal (kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) ++ ++#define LZMA_MATCH_LEN_MIN 2 ++#define LZMA_MATCH_LEN_MAX (LZMA_MATCH_LEN_MIN + kLenNumSymbolsTotal - 1) ++ ++#define kNumStates 12 ++ ++typedef struct ++{ ++ CLzmaProb choice; ++ CLzmaProb choice2; ++ CLzmaProb low[LZMA_NUM_PB_STATES_MAX << kLenNumLowBits]; ++ CLzmaProb mid[LZMA_NUM_PB_STATES_MAX << kLenNumMidBits]; ++ CLzmaProb high[kLenNumHighSymbols]; ++} CLenEnc; ++ ++typedef struct ++{ ++ CLenEnc p; ++ UInt32 prices[LZMA_NUM_PB_STATES_MAX][kLenNumSymbolsTotal]; ++ UInt32 tableSize; ++ UInt32 counters[LZMA_NUM_PB_STATES_MAX]; ++} CLenPriceEnc; ++ ++typedef struct ++{ ++ UInt32 range; ++ Byte cache; ++ UInt64 low; ++ UInt64 cacheSize; ++ Byte *buf; ++ Byte *bufLim; ++ Byte *bufBase; ++ ISeqOutStream *outStream; ++ UInt64 processed; ++ SRes res; ++} CRangeEnc; ++ ++typedef struct ++{ ++ CLzmaProb *litProbs; ++ ++ CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; ++ CLzmaProb isRep[kNumStates]; ++ CLzmaProb isRepG0[kNumStates]; ++ CLzmaProb isRepG1[kNumStates]; ++ CLzmaProb isRepG2[kNumStates]; ++ CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; ++ ++ CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; ++ CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; ++ CLzmaProb posAlignEncoder[1 << kNumAlignBits]; ++ ++ CLenPriceEnc lenEnc; ++ CLenPriceEnc repLenEnc; ++ ++ UInt32 reps[LZMA_NUM_REPS]; ++ UInt32 state; ++} CSaveState; ++ ++typedef struct ++{ ++ IMatchFinder matchFinder; ++ void *matchFinderObj; ++ ++ #ifndef _7ZIP_ST ++ Bool mtMode; ++ CMatchFinderMt matchFinderMt; ++ #endif ++ ++ CMatchFinder matchFinderBase; ++ ++ #ifndef _7ZIP_ST ++ Byte pad[128]; ++ #endif ++ ++ UInt32 optimumEndIndex; ++ UInt32 optimumCurrentIndex; ++ ++ UInt32 longestMatchLength; ++ UInt32 numPairs; ++ UInt32 numAvail; ++ COptimal opt[kNumOpts]; ++ ++ #ifndef LZMA_LOG_BSR ++ Byte g_FastPos[1 << kNumLogBits]; ++ #endif ++ ++ UInt32 ProbPrices[kBitModelTotal >> kNumMoveReducingBits]; ++ UInt32 matches[LZMA_MATCH_LEN_MAX * 2 + 2 + 1]; ++ UInt32 numFastBytes; ++ UInt32 additionalOffset; ++ UInt32 reps[LZMA_NUM_REPS]; ++ UInt32 state; ++ ++ UInt32 posSlotPrices[kNumLenToPosStates][kDistTableSizeMax]; ++ UInt32 distancesPrices[kNumLenToPosStates][kNumFullDistances]; ++ UInt32 alignPrices[kAlignTableSize]; ++ UInt32 alignPriceCount; ++ ++ UInt32 distTableSize; ++ ++ unsigned lc, lp, pb; ++ unsigned lpMask, pbMask; ++ ++ CLzmaProb *litProbs; ++ ++ CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; ++ CLzmaProb isRep[kNumStates]; ++ CLzmaProb isRepG0[kNumStates]; ++ CLzmaProb isRepG1[kNumStates]; ++ CLzmaProb isRepG2[kNumStates]; ++ CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; ++ ++ CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; ++ CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; ++ CLzmaProb posAlignEncoder[1 << kNumAlignBits]; ++ ++ CLenPriceEnc lenEnc; ++ CLenPriceEnc repLenEnc; ++ ++ unsigned lclp; ++ ++ Bool fastMode; ++ ++ CRangeEnc rc; ++ ++ Bool writeEndMark; ++ UInt64 nowPos64; ++ UInt32 matchPriceCount; ++ Bool finished; ++ Bool multiThread; ++ ++ SRes result; ++ UInt32 dictSize; ++ UInt32 matchFinderCycles; ++ ++ int needInit; ++ ++ CSaveState saveState; ++} CLzmaEnc; ++ ++void LzmaEnc_SaveState(CLzmaEncHandle pp) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ CSaveState *dest = &p->saveState; ++ int i; ++ dest->lenEnc = p->lenEnc; ++ dest->repLenEnc = p->repLenEnc; ++ dest->state = p->state; ++ ++ for (i = 0; i < kNumStates; i++) ++ { ++ memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); ++ memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); ++ } ++ for (i = 0; i < kNumLenToPosStates; i++) ++ memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); ++ memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); ++ memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); ++ memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); ++ memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); ++ memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); ++ memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); ++ memcpy(dest->reps, p->reps, sizeof(p->reps)); ++ memcpy(dest->litProbs, p->litProbs, (0x300 << p->lclp) * sizeof(CLzmaProb)); ++} ++ ++void LzmaEnc_RestoreState(CLzmaEncHandle pp) ++{ ++ CLzmaEnc *dest = (CLzmaEnc *)pp; ++ const CSaveState *p = &dest->saveState; ++ int i; ++ dest->lenEnc = p->lenEnc; ++ dest->repLenEnc = p->repLenEnc; ++ dest->state = p->state; ++ ++ for (i = 0; i < kNumStates; i++) ++ { ++ memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); ++ memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); ++ } ++ for (i = 0; i < kNumLenToPosStates; i++) ++ memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); ++ memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); ++ memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); ++ memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); ++ memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); ++ memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); ++ memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); ++ memcpy(dest->reps, p->reps, sizeof(p->reps)); ++ memcpy(dest->litProbs, p->litProbs, (0x300 << dest->lclp) * sizeof(CLzmaProb)); ++} ++ ++SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ CLzmaEncProps props = *props2; ++ LzmaEncProps_Normalize(&props); ++ ++ if (props.lc > LZMA_LC_MAX || props.lp > LZMA_LP_MAX || props.pb > LZMA_PB_MAX || ++ props.dictSize > ((UInt32)1 << kDicLogSizeMaxCompress) || props.dictSize > ((UInt32)1 << 30)) ++ return SZ_ERROR_PARAM; ++ p->dictSize = props.dictSize; ++ p->matchFinderCycles = props.mc; ++ { ++ unsigned fb = props.fb; ++ if (fb < 5) ++ fb = 5; ++ if (fb > LZMA_MATCH_LEN_MAX) ++ fb = LZMA_MATCH_LEN_MAX; ++ p->numFastBytes = fb; ++ } ++ p->lc = props.lc; ++ p->lp = props.lp; ++ p->pb = props.pb; ++ p->fastMode = (props.algo == 0); ++ p->matchFinderBase.btMode = props.btMode; ++ { ++ UInt32 numHashBytes = 4; ++ if (props.btMode) ++ { ++ if (props.numHashBytes < 2) ++ numHashBytes = 2; ++ else if (props.numHashBytes < 4) ++ numHashBytes = props.numHashBytes; ++ } ++ p->matchFinderBase.numHashBytes = numHashBytes; ++ } ++ ++ p->matchFinderBase.cutValue = props.mc; ++ ++ p->writeEndMark = props.writeEndMark; ++ ++ #ifndef _7ZIP_ST ++ /* ++ if (newMultiThread != _multiThread) ++ { ++ ReleaseMatchFinder(); ++ _multiThread = newMultiThread; ++ } ++ */ ++ p->multiThread = (props.numThreads > 1); ++ #endif ++ ++ return SZ_OK; ++} ++ ++static const int kLiteralNextStates[kNumStates] = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5}; ++static const int kMatchNextStates[kNumStates] = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; ++static const int kRepNextStates[kNumStates] = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11}; ++static const int kShortRepNextStates[kNumStates]= {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11}; ++ ++#define IsCharState(s) ((s) < 7) ++ ++#define GetLenToPosState(len) (((len) < kNumLenToPosStates + 1) ? (len) - 2 : kNumLenToPosStates - 1) ++ ++#define kInfinityPrice (1 << 30) ++ ++static void RangeEnc_Construct(CRangeEnc *p) ++{ ++ p->outStream = 0; ++ p->bufBase = 0; ++} ++ ++#define RangeEnc_GetProcessed(p) ((p)->processed + ((p)->buf - (p)->bufBase) + (p)->cacheSize) ++ ++#define RC_BUF_SIZE (1 << 16) ++static int RangeEnc_Alloc(CRangeEnc *p, ISzAlloc *alloc) ++{ ++ if (p->bufBase == 0) ++ { ++ p->bufBase = (Byte *)alloc->Alloc(alloc, RC_BUF_SIZE); ++ if (p->bufBase == 0) ++ return 0; ++ p->bufLim = p->bufBase + RC_BUF_SIZE; ++ } ++ return 1; ++} ++ ++static void RangeEnc_Free(CRangeEnc *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->bufBase); ++ p->bufBase = 0; ++} ++ ++static void RangeEnc_Init(CRangeEnc *p) ++{ ++ /* Stream.Init(); */ ++ p->low = 0; ++ p->range = 0xFFFFFFFF; ++ p->cacheSize = 1; ++ p->cache = 0; ++ ++ p->buf = p->bufBase; ++ ++ p->processed = 0; ++ p->res = SZ_OK; ++} ++ ++static void RangeEnc_FlushStream(CRangeEnc *p) ++{ ++ size_t num; ++ if (p->res != SZ_OK) ++ return; ++ num = p->buf - p->bufBase; ++ if (num != p->outStream->Write(p->outStream, p->bufBase, num)) ++ p->res = SZ_ERROR_WRITE; ++ p->processed += num; ++ p->buf = p->bufBase; ++} ++ ++static void MY_FAST_CALL RangeEnc_ShiftLow(CRangeEnc *p) ++{ ++ if ((UInt32)p->low < (UInt32)0xFF000000 || (int)(p->low >> 32) != 0) ++ { ++ Byte temp = p->cache; ++ do ++ { ++ Byte *buf = p->buf; ++ *buf++ = (Byte)(temp + (Byte)(p->low >> 32)); ++ p->buf = buf; ++ if (buf == p->bufLim) ++ RangeEnc_FlushStream(p); ++ temp = 0xFF; ++ } ++ while (--p->cacheSize != 0); ++ p->cache = (Byte)((UInt32)p->low >> 24); ++ } ++ p->cacheSize++; ++ p->low = (UInt32)p->low << 8; ++} ++ ++static void RangeEnc_FlushData(CRangeEnc *p) ++{ ++ int i; ++ for (i = 0; i < 5; i++) ++ RangeEnc_ShiftLow(p); ++} ++ ++static void RangeEnc_EncodeDirectBits(CRangeEnc *p, UInt32 value, int numBits) ++{ ++ do ++ { ++ p->range >>= 1; ++ p->low += p->range & (0 - ((value >> --numBits) & 1)); ++ if (p->range < kTopValue) ++ { ++ p->range <<= 8; ++ RangeEnc_ShiftLow(p); ++ } ++ } ++ while (numBits != 0); ++} ++ ++static void RangeEnc_EncodeBit(CRangeEnc *p, CLzmaProb *prob, UInt32 symbol) ++{ ++ UInt32 ttt = *prob; ++ UInt32 newBound = (p->range >> kNumBitModelTotalBits) * ttt; ++ if (symbol == 0) ++ { ++ p->range = newBound; ++ ttt += (kBitModelTotal - ttt) >> kNumMoveBits; ++ } ++ else ++ { ++ p->low += newBound; ++ p->range -= newBound; ++ ttt -= ttt >> kNumMoveBits; ++ } ++ *prob = (CLzmaProb)ttt; ++ if (p->range < kTopValue) ++ { ++ p->range <<= 8; ++ RangeEnc_ShiftLow(p); ++ } ++} ++ ++static void LitEnc_Encode(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol) ++{ ++ symbol |= 0x100; ++ do ++ { ++ RangeEnc_EncodeBit(p, probs + (symbol >> 8), (symbol >> 7) & 1); ++ symbol <<= 1; ++ } ++ while (symbol < 0x10000); ++} ++ ++static void LitEnc_EncodeMatched(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol, UInt32 matchByte) ++{ ++ UInt32 offs = 0x100; ++ symbol |= 0x100; ++ do ++ { ++ matchByte <<= 1; ++ RangeEnc_EncodeBit(p, probs + (offs + (matchByte & offs) + (symbol >> 8)), (symbol >> 7) & 1); ++ symbol <<= 1; ++ offs &= ~(matchByte ^ symbol); ++ } ++ while (symbol < 0x10000); ++} ++ ++void LzmaEnc_InitPriceTables(UInt32 *ProbPrices) ++{ ++ UInt32 i; ++ for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; i += (1 << kNumMoveReducingBits)) ++ { ++ const int kCyclesBits = kNumBitPriceShiftBits; ++ UInt32 w = i; ++ UInt32 bitCount = 0; ++ int j; ++ for (j = 0; j < kCyclesBits; j++) ++ { ++ w = w * w; ++ bitCount <<= 1; ++ while (w >= ((UInt32)1 << 16)) ++ { ++ w >>= 1; ++ bitCount++; ++ } ++ } ++ ProbPrices[i >> kNumMoveReducingBits] = ((kNumBitModelTotalBits << kCyclesBits) - 15 - bitCount); ++ } ++} ++ ++ ++#define GET_PRICE(prob, symbol) \ ++ p->ProbPrices[((prob) ^ (((-(int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; ++ ++#define GET_PRICEa(prob, symbol) \ ++ ProbPrices[((prob) ^ ((-((int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; ++ ++#define GET_PRICE_0(prob) p->ProbPrices[(prob) >> kNumMoveReducingBits] ++#define GET_PRICE_1(prob) p->ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] ++ ++#define GET_PRICE_0a(prob) ProbPrices[(prob) >> kNumMoveReducingBits] ++#define GET_PRICE_1a(prob) ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] ++ ++static UInt32 LitEnc_GetPrice(const CLzmaProb *probs, UInt32 symbol, UInt32 *ProbPrices) ++{ ++ UInt32 price = 0; ++ symbol |= 0x100; ++ do ++ { ++ price += GET_PRICEa(probs[symbol >> 8], (symbol >> 7) & 1); ++ symbol <<= 1; ++ } ++ while (symbol < 0x10000); ++ return price; ++} ++ ++static UInt32 LitEnc_GetPriceMatched(const CLzmaProb *probs, UInt32 symbol, UInt32 matchByte, UInt32 *ProbPrices) ++{ ++ UInt32 price = 0; ++ UInt32 offs = 0x100; ++ symbol |= 0x100; ++ do ++ { ++ matchByte <<= 1; ++ price += GET_PRICEa(probs[offs + (matchByte & offs) + (symbol >> 8)], (symbol >> 7) & 1); ++ symbol <<= 1; ++ offs &= ~(matchByte ^ symbol); ++ } ++ while (symbol < 0x10000); ++ return price; ++} ++ ++ ++static void RcTree_Encode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol) ++{ ++ UInt32 m = 1; ++ int i; ++ for (i = numBitLevels; i != 0;) ++ { ++ UInt32 bit; ++ i--; ++ bit = (symbol >> i) & 1; ++ RangeEnc_EncodeBit(rc, probs + m, bit); ++ m = (m << 1) | bit; ++ } ++} ++ ++static void RcTree_ReverseEncode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol) ++{ ++ UInt32 m = 1; ++ int i; ++ for (i = 0; i < numBitLevels; i++) ++ { ++ UInt32 bit = symbol & 1; ++ RangeEnc_EncodeBit(rc, probs + m, bit); ++ m = (m << 1) | bit; ++ symbol >>= 1; ++ } ++} ++ ++static UInt32 RcTree_GetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices) ++{ ++ UInt32 price = 0; ++ symbol |= (1 << numBitLevels); ++ while (symbol != 1) ++ { ++ price += GET_PRICEa(probs[symbol >> 1], symbol & 1); ++ symbol >>= 1; ++ } ++ return price; ++} ++ ++static UInt32 RcTree_ReverseGetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices) ++{ ++ UInt32 price = 0; ++ UInt32 m = 1; ++ int i; ++ for (i = numBitLevels; i != 0; i--) ++ { ++ UInt32 bit = symbol & 1; ++ symbol >>= 1; ++ price += GET_PRICEa(probs[m], bit); ++ m = (m << 1) | bit; ++ } ++ return price; ++} ++ ++ ++static void LenEnc_Init(CLenEnc *p) ++{ ++ unsigned i; ++ p->choice = p->choice2 = kProbInitValue; ++ for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumLowBits); i++) ++ p->low[i] = kProbInitValue; ++ for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumMidBits); i++) ++ p->mid[i] = kProbInitValue; ++ for (i = 0; i < kLenNumHighSymbols; i++) ++ p->high[i] = kProbInitValue; ++} ++ ++static void LenEnc_Encode(CLenEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState) ++{ ++ if (symbol < kLenNumLowSymbols) ++ { ++ RangeEnc_EncodeBit(rc, &p->choice, 0); ++ RcTree_Encode(rc, p->low + (posState << kLenNumLowBits), kLenNumLowBits, symbol); ++ } ++ else ++ { ++ RangeEnc_EncodeBit(rc, &p->choice, 1); ++ if (symbol < kLenNumLowSymbols + kLenNumMidSymbols) ++ { ++ RangeEnc_EncodeBit(rc, &p->choice2, 0); ++ RcTree_Encode(rc, p->mid + (posState << kLenNumMidBits), kLenNumMidBits, symbol - kLenNumLowSymbols); ++ } ++ else ++ { ++ RangeEnc_EncodeBit(rc, &p->choice2, 1); ++ RcTree_Encode(rc, p->high, kLenNumHighBits, symbol - kLenNumLowSymbols - kLenNumMidSymbols); ++ } ++ } ++} ++ ++static void LenEnc_SetPrices(CLenEnc *p, UInt32 posState, UInt32 numSymbols, UInt32 *prices, UInt32 *ProbPrices) ++{ ++ UInt32 a0 = GET_PRICE_0a(p->choice); ++ UInt32 a1 = GET_PRICE_1a(p->choice); ++ UInt32 b0 = a1 + GET_PRICE_0a(p->choice2); ++ UInt32 b1 = a1 + GET_PRICE_1a(p->choice2); ++ UInt32 i = 0; ++ for (i = 0; i < kLenNumLowSymbols; i++) ++ { ++ if (i >= numSymbols) ++ return; ++ prices[i] = a0 + RcTree_GetPrice(p->low + (posState << kLenNumLowBits), kLenNumLowBits, i, ProbPrices); ++ } ++ for (; i < kLenNumLowSymbols + kLenNumMidSymbols; i++) ++ { ++ if (i >= numSymbols) ++ return; ++ prices[i] = b0 + RcTree_GetPrice(p->mid + (posState << kLenNumMidBits), kLenNumMidBits, i - kLenNumLowSymbols, ProbPrices); ++ } ++ for (; i < numSymbols; i++) ++ prices[i] = b1 + RcTree_GetPrice(p->high, kLenNumHighBits, i - kLenNumLowSymbols - kLenNumMidSymbols, ProbPrices); ++} ++ ++static void MY_FAST_CALL LenPriceEnc_UpdateTable(CLenPriceEnc *p, UInt32 posState, UInt32 *ProbPrices) ++{ ++ LenEnc_SetPrices(&p->p, posState, p->tableSize, p->prices[posState], ProbPrices); ++ p->counters[posState] = p->tableSize; ++} ++ ++static void LenPriceEnc_UpdateTables(CLenPriceEnc *p, UInt32 numPosStates, UInt32 *ProbPrices) ++{ ++ UInt32 posState; ++ for (posState = 0; posState < numPosStates; posState++) ++ LenPriceEnc_UpdateTable(p, posState, ProbPrices); ++} ++ ++static void LenEnc_Encode2(CLenPriceEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState, Bool updatePrice, UInt32 *ProbPrices) ++{ ++ LenEnc_Encode(&p->p, rc, symbol, posState); ++ if (updatePrice) ++ if (--p->counters[posState] == 0) ++ LenPriceEnc_UpdateTable(p, posState, ProbPrices); ++} ++ ++ ++ ++ ++static void MovePos(CLzmaEnc *p, UInt32 num) ++{ ++ #ifdef SHOW_STAT ++ ttt += num; ++ printf("\n MovePos %d", num); ++ #endif ++ if (num != 0) ++ { ++ p->additionalOffset += num; ++ p->matchFinder.Skip(p->matchFinderObj, num); ++ } ++} ++ ++static UInt32 ReadMatchDistances(CLzmaEnc *p, UInt32 *numDistancePairsRes) ++{ ++ UInt32 lenRes = 0, numPairs; ++ p->numAvail = p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); ++ numPairs = p->matchFinder.GetMatches(p->matchFinderObj, p->matches); ++ #ifdef SHOW_STAT ++ printf("\n i = %d numPairs = %d ", ttt, numPairs / 2); ++ ttt++; ++ { ++ UInt32 i; ++ for (i = 0; i < numPairs; i += 2) ++ printf("%2d %6d | ", p->matches[i], p->matches[i + 1]); ++ } ++ #endif ++ if (numPairs > 0) ++ { ++ lenRes = p->matches[numPairs - 2]; ++ if (lenRes == p->numFastBytes) ++ { ++ const Byte *pby = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; ++ UInt32 distance = p->matches[numPairs - 1] + 1; ++ UInt32 numAvail = p->numAvail; ++ if (numAvail > LZMA_MATCH_LEN_MAX) ++ numAvail = LZMA_MATCH_LEN_MAX; ++ { ++ const Byte *pby2 = pby - distance; ++ for (; lenRes < numAvail && pby[lenRes] == pby2[lenRes]; lenRes++); ++ } ++ } ++ } ++ p->additionalOffset++; ++ *numDistancePairsRes = numPairs; ++ return lenRes; ++} ++ ++ ++#define MakeAsChar(p) (p)->backPrev = (UInt32)(-1); (p)->prev1IsChar = False; ++#define MakeAsShortRep(p) (p)->backPrev = 0; (p)->prev1IsChar = False; ++#define IsShortRep(p) ((p)->backPrev == 0) ++ ++static UInt32 GetRepLen1Price(CLzmaEnc *p, UInt32 state, UInt32 posState) ++{ ++ return ++ GET_PRICE_0(p->isRepG0[state]) + ++ GET_PRICE_0(p->isRep0Long[state][posState]); ++} ++ ++static UInt32 GetPureRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 state, UInt32 posState) ++{ ++ UInt32 price; ++ if (repIndex == 0) ++ { ++ price = GET_PRICE_0(p->isRepG0[state]); ++ price += GET_PRICE_1(p->isRep0Long[state][posState]); ++ } ++ else ++ { ++ price = GET_PRICE_1(p->isRepG0[state]); ++ if (repIndex == 1) ++ price += GET_PRICE_0(p->isRepG1[state]); ++ else ++ { ++ price += GET_PRICE_1(p->isRepG1[state]); ++ price += GET_PRICE(p->isRepG2[state], repIndex - 2); ++ } ++ } ++ return price; ++} ++ ++static UInt32 GetRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 len, UInt32 state, UInt32 posState) ++{ ++ return p->repLenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN] + ++ GetPureRepPrice(p, repIndex, state, posState); ++} ++ ++static UInt32 Backward(CLzmaEnc *p, UInt32 *backRes, UInt32 cur) ++{ ++ UInt32 posMem = p->opt[cur].posPrev; ++ UInt32 backMem = p->opt[cur].backPrev; ++ p->optimumEndIndex = cur; ++ do ++ { ++ if (p->opt[cur].prev1IsChar) ++ { ++ MakeAsChar(&p->opt[posMem]) ++ p->opt[posMem].posPrev = posMem - 1; ++ if (p->opt[cur].prev2) ++ { ++ p->opt[posMem - 1].prev1IsChar = False; ++ p->opt[posMem - 1].posPrev = p->opt[cur].posPrev2; ++ p->opt[posMem - 1].backPrev = p->opt[cur].backPrev2; ++ } ++ } ++ { ++ UInt32 posPrev = posMem; ++ UInt32 backCur = backMem; ++ ++ backMem = p->opt[posPrev].backPrev; ++ posMem = p->opt[posPrev].posPrev; ++ ++ p->opt[posPrev].backPrev = backCur; ++ p->opt[posPrev].posPrev = cur; ++ cur = posPrev; ++ } ++ } ++ while (cur != 0); ++ *backRes = p->opt[0].backPrev; ++ p->optimumCurrentIndex = p->opt[0].posPrev; ++ return p->optimumCurrentIndex; ++} ++ ++#define LIT_PROBS(pos, prevByte) (p->litProbs + ((((pos) & p->lpMask) << p->lc) + ((prevByte) >> (8 - p->lc))) * 0x300) ++ ++static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes) ++{ ++ UInt32 numAvail, mainLen, numPairs, repMaxIndex, i, posState, lenEnd, len, cur; ++ UInt32 matchPrice, repMatchPrice, normalMatchPrice; ++ UInt32 reps[LZMA_NUM_REPS], repLens[LZMA_NUM_REPS]; ++ UInt32 *matches; ++ const Byte *data; ++ Byte curByte, matchByte; ++ if (p->optimumEndIndex != p->optimumCurrentIndex) ++ { ++ const COptimal *opt = &p->opt[p->optimumCurrentIndex]; ++ UInt32 lenRes = opt->posPrev - p->optimumCurrentIndex; ++ *backRes = opt->backPrev; ++ p->optimumCurrentIndex = opt->posPrev; ++ return lenRes; ++ } ++ p->optimumCurrentIndex = p->optimumEndIndex = 0; ++ ++ if (p->additionalOffset == 0) ++ mainLen = ReadMatchDistances(p, &numPairs); ++ else ++ { ++ mainLen = p->longestMatchLength; ++ numPairs = p->numPairs; ++ } ++ ++ numAvail = p->numAvail; ++ if (numAvail < 2) ++ { ++ *backRes = (UInt32)(-1); ++ return 1; ++ } ++ if (numAvail > LZMA_MATCH_LEN_MAX) ++ numAvail = LZMA_MATCH_LEN_MAX; ++ ++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; ++ repMaxIndex = 0; ++ for (i = 0; i < LZMA_NUM_REPS; i++) ++ { ++ UInt32 lenTest; ++ const Byte *data2; ++ reps[i] = p->reps[i]; ++ data2 = data - (reps[i] + 1); ++ if (data[0] != data2[0] || data[1] != data2[1]) ++ { ++ repLens[i] = 0; ++ continue; ++ } ++ for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); ++ repLens[i] = lenTest; ++ if (lenTest > repLens[repMaxIndex]) ++ repMaxIndex = i; ++ } ++ if (repLens[repMaxIndex] >= p->numFastBytes) ++ { ++ UInt32 lenRes; ++ *backRes = repMaxIndex; ++ lenRes = repLens[repMaxIndex]; ++ MovePos(p, lenRes - 1); ++ return lenRes; ++ } ++ ++ matches = p->matches; ++ if (mainLen >= p->numFastBytes) ++ { ++ *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; ++ MovePos(p, mainLen - 1); ++ return mainLen; ++ } ++ curByte = *data; ++ matchByte = *(data - (reps[0] + 1)); ++ ++ if (mainLen < 2 && curByte != matchByte && repLens[repMaxIndex] < 2) ++ { ++ *backRes = (UInt32)-1; ++ return 1; ++ } ++ ++ p->opt[0].state = (CState)p->state; ++ ++ posState = (position & p->pbMask); ++ ++ { ++ const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); ++ p->opt[1].price = GET_PRICE_0(p->isMatch[p->state][posState]) + ++ (!IsCharState(p->state) ? ++ LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : ++ LitEnc_GetPrice(probs, curByte, p->ProbPrices)); ++ } ++ ++ MakeAsChar(&p->opt[1]); ++ ++ matchPrice = GET_PRICE_1(p->isMatch[p->state][posState]); ++ repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[p->state]); ++ ++ if (matchByte == curByte) ++ { ++ UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, p->state, posState); ++ if (shortRepPrice < p->opt[1].price) ++ { ++ p->opt[1].price = shortRepPrice; ++ MakeAsShortRep(&p->opt[1]); ++ } ++ } ++ lenEnd = ((mainLen >= repLens[repMaxIndex]) ? mainLen : repLens[repMaxIndex]); ++ ++ if (lenEnd < 2) ++ { ++ *backRes = p->opt[1].backPrev; ++ return 1; ++ } ++ ++ p->opt[1].posPrev = 0; ++ for (i = 0; i < LZMA_NUM_REPS; i++) ++ p->opt[0].backs[i] = reps[i]; ++ ++ len = lenEnd; ++ do ++ p->opt[len--].price = kInfinityPrice; ++ while (len >= 2); ++ ++ for (i = 0; i < LZMA_NUM_REPS; i++) ++ { ++ UInt32 repLen = repLens[i]; ++ UInt32 price; ++ if (repLen < 2) ++ continue; ++ price = repMatchPrice + GetPureRepPrice(p, i, p->state, posState); ++ do ++ { ++ UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][repLen - 2]; ++ COptimal *opt = &p->opt[repLen]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = 0; ++ opt->backPrev = i; ++ opt->prev1IsChar = False; ++ } ++ } ++ while (--repLen >= 2); ++ } ++ ++ normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[p->state]); ++ ++ len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2); ++ if (len <= mainLen) ++ { ++ UInt32 offs = 0; ++ while (len > matches[offs]) ++ offs += 2; ++ for (; ; len++) ++ { ++ COptimal *opt; ++ UInt32 distance = matches[offs + 1]; ++ ++ UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN]; ++ UInt32 lenToPosState = GetLenToPosState(len); ++ if (distance < kNumFullDistances) ++ curAndLenPrice += p->distancesPrices[lenToPosState][distance]; ++ else ++ { ++ UInt32 slot; ++ GetPosSlot2(distance, slot); ++ curAndLenPrice += p->alignPrices[distance & kAlignMask] + p->posSlotPrices[lenToPosState][slot]; ++ } ++ opt = &p->opt[len]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = 0; ++ opt->backPrev = distance + LZMA_NUM_REPS; ++ opt->prev1IsChar = False; ++ } ++ if (len == matches[offs]) ++ { ++ offs += 2; ++ if (offs == numPairs) ++ break; ++ } ++ } ++ } ++ ++ cur = 0; ++ ++ #ifdef SHOW_STAT2 ++ if (position >= 0) ++ { ++ unsigned i; ++ printf("\n pos = %4X", position); ++ for (i = cur; i <= lenEnd; i++) ++ printf("\nprice[%4X] = %d", position - cur + i, p->opt[i].price); ++ } ++ #endif ++ ++ for (;;) ++ { ++ UInt32 numAvailFull, newLen, numPairs, posPrev, state, posState, startLen; ++ UInt32 curPrice, curAnd1Price, matchPrice, repMatchPrice; ++ Bool nextIsChar; ++ Byte curByte, matchByte; ++ const Byte *data; ++ COptimal *curOpt; ++ COptimal *nextOpt; ++ ++ cur++; ++ if (cur == lenEnd) ++ return Backward(p, backRes, cur); ++ ++ newLen = ReadMatchDistances(p, &numPairs); ++ if (newLen >= p->numFastBytes) ++ { ++ p->numPairs = numPairs; ++ p->longestMatchLength = newLen; ++ return Backward(p, backRes, cur); ++ } ++ position++; ++ curOpt = &p->opt[cur]; ++ posPrev = curOpt->posPrev; ++ if (curOpt->prev1IsChar) ++ { ++ posPrev--; ++ if (curOpt->prev2) ++ { ++ state = p->opt[curOpt->posPrev2].state; ++ if (curOpt->backPrev2 < LZMA_NUM_REPS) ++ state = kRepNextStates[state]; ++ else ++ state = kMatchNextStates[state]; ++ } ++ else ++ state = p->opt[posPrev].state; ++ state = kLiteralNextStates[state]; ++ } ++ else ++ state = p->opt[posPrev].state; ++ if (posPrev == cur - 1) ++ { ++ if (IsShortRep(curOpt)) ++ state = kShortRepNextStates[state]; ++ else ++ state = kLiteralNextStates[state]; ++ } ++ else ++ { ++ UInt32 pos; ++ const COptimal *prevOpt; ++ if (curOpt->prev1IsChar && curOpt->prev2) ++ { ++ posPrev = curOpt->posPrev2; ++ pos = curOpt->backPrev2; ++ state = kRepNextStates[state]; ++ } ++ else ++ { ++ pos = curOpt->backPrev; ++ if (pos < LZMA_NUM_REPS) ++ state = kRepNextStates[state]; ++ else ++ state = kMatchNextStates[state]; ++ } ++ prevOpt = &p->opt[posPrev]; ++ if (pos < LZMA_NUM_REPS) ++ { ++ UInt32 i; ++ reps[0] = prevOpt->backs[pos]; ++ for (i = 1; i <= pos; i++) ++ reps[i] = prevOpt->backs[i - 1]; ++ for (; i < LZMA_NUM_REPS; i++) ++ reps[i] = prevOpt->backs[i]; ++ } ++ else ++ { ++ UInt32 i; ++ reps[0] = (pos - LZMA_NUM_REPS); ++ for (i = 1; i < LZMA_NUM_REPS; i++) ++ reps[i] = prevOpt->backs[i - 1]; ++ } ++ } ++ curOpt->state = (CState)state; ++ ++ curOpt->backs[0] = reps[0]; ++ curOpt->backs[1] = reps[1]; ++ curOpt->backs[2] = reps[2]; ++ curOpt->backs[3] = reps[3]; ++ ++ curPrice = curOpt->price; ++ nextIsChar = False; ++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; ++ curByte = *data; ++ matchByte = *(data - (reps[0] + 1)); ++ ++ posState = (position & p->pbMask); ++ ++ curAnd1Price = curPrice + GET_PRICE_0(p->isMatch[state][posState]); ++ { ++ const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); ++ curAnd1Price += ++ (!IsCharState(state) ? ++ LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : ++ LitEnc_GetPrice(probs, curByte, p->ProbPrices)); ++ } ++ ++ nextOpt = &p->opt[cur + 1]; ++ ++ if (curAnd1Price < nextOpt->price) ++ { ++ nextOpt->price = curAnd1Price; ++ nextOpt->posPrev = cur; ++ MakeAsChar(nextOpt); ++ nextIsChar = True; ++ } ++ ++ matchPrice = curPrice + GET_PRICE_1(p->isMatch[state][posState]); ++ repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[state]); ++ ++ if (matchByte == curByte && !(nextOpt->posPrev < cur && nextOpt->backPrev == 0)) ++ { ++ UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, state, posState); ++ if (shortRepPrice <= nextOpt->price) ++ { ++ nextOpt->price = shortRepPrice; ++ nextOpt->posPrev = cur; ++ MakeAsShortRep(nextOpt); ++ nextIsChar = True; ++ } ++ } ++ numAvailFull = p->numAvail; ++ { ++ UInt32 temp = kNumOpts - 1 - cur; ++ if (temp < numAvailFull) ++ numAvailFull = temp; ++ } ++ ++ if (numAvailFull < 2) ++ continue; ++ numAvail = (numAvailFull <= p->numFastBytes ? numAvailFull : p->numFastBytes); ++ ++ if (!nextIsChar && matchByte != curByte) /* speed optimization */ ++ { ++ /* try Literal + rep0 */ ++ UInt32 temp; ++ UInt32 lenTest2; ++ const Byte *data2 = data - (reps[0] + 1); ++ UInt32 limit = p->numFastBytes + 1; ++ if (limit > numAvailFull) ++ limit = numAvailFull; ++ ++ for (temp = 1; temp < limit && data[temp] == data2[temp]; temp++); ++ lenTest2 = temp - 1; ++ if (lenTest2 >= 2) ++ { ++ UInt32 state2 = kLiteralNextStates[state]; ++ UInt32 posStateNext = (position + 1) & p->pbMask; ++ UInt32 nextRepMatchPrice = curAnd1Price + ++ GET_PRICE_1(p->isMatch[state2][posStateNext]) + ++ GET_PRICE_1(p->isRep[state2]); ++ /* for (; lenTest2 >= 2; lenTest2--) */ ++ { ++ UInt32 curAndLenPrice; ++ COptimal *opt; ++ UInt32 offset = cur + 1 + lenTest2; ++ while (lenEnd < offset) ++ p->opt[++lenEnd].price = kInfinityPrice; ++ curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); ++ opt = &p->opt[offset]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = cur + 1; ++ opt->backPrev = 0; ++ opt->prev1IsChar = True; ++ opt->prev2 = False; ++ } ++ } ++ } ++ } ++ ++ startLen = 2; /* speed optimization */ ++ { ++ UInt32 repIndex; ++ for (repIndex = 0; repIndex < LZMA_NUM_REPS; repIndex++) ++ { ++ UInt32 lenTest; ++ UInt32 lenTestTemp; ++ UInt32 price; ++ const Byte *data2 = data - (reps[repIndex] + 1); ++ if (data[0] != data2[0] || data[1] != data2[1]) ++ continue; ++ for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); ++ while (lenEnd < cur + lenTest) ++ p->opt[++lenEnd].price = kInfinityPrice; ++ lenTestTemp = lenTest; ++ price = repMatchPrice + GetPureRepPrice(p, repIndex, state, posState); ++ do ++ { ++ UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][lenTest - 2]; ++ COptimal *opt = &p->opt[cur + lenTest]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = cur; ++ opt->backPrev = repIndex; ++ opt->prev1IsChar = False; ++ } ++ } ++ while (--lenTest >= 2); ++ lenTest = lenTestTemp; ++ ++ if (repIndex == 0) ++ startLen = lenTest + 1; ++ ++ /* if (_maxMode) */ ++ { ++ UInt32 lenTest2 = lenTest + 1; ++ UInt32 limit = lenTest2 + p->numFastBytes; ++ UInt32 nextRepMatchPrice; ++ if (limit > numAvailFull) ++ limit = numAvailFull; ++ for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); ++ lenTest2 -= lenTest + 1; ++ if (lenTest2 >= 2) ++ { ++ UInt32 state2 = kRepNextStates[state]; ++ UInt32 posStateNext = (position + lenTest) & p->pbMask; ++ UInt32 curAndLenCharPrice = ++ price + p->repLenEnc.prices[posState][lenTest - 2] + ++ GET_PRICE_0(p->isMatch[state2][posStateNext]) + ++ LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), ++ data[lenTest], data2[lenTest], p->ProbPrices); ++ state2 = kLiteralNextStates[state2]; ++ posStateNext = (position + lenTest + 1) & p->pbMask; ++ nextRepMatchPrice = curAndLenCharPrice + ++ GET_PRICE_1(p->isMatch[state2][posStateNext]) + ++ GET_PRICE_1(p->isRep[state2]); ++ ++ /* for (; lenTest2 >= 2; lenTest2--) */ ++ { ++ UInt32 curAndLenPrice; ++ COptimal *opt; ++ UInt32 offset = cur + lenTest + 1 + lenTest2; ++ while (lenEnd < offset) ++ p->opt[++lenEnd].price = kInfinityPrice; ++ curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); ++ opt = &p->opt[offset]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = cur + lenTest + 1; ++ opt->backPrev = 0; ++ opt->prev1IsChar = True; ++ opt->prev2 = True; ++ opt->posPrev2 = cur; ++ opt->backPrev2 = repIndex; ++ } ++ } ++ } ++ } ++ } ++ } ++ /* for (UInt32 lenTest = 2; lenTest <= newLen; lenTest++) */ ++ if (newLen > numAvail) ++ { ++ newLen = numAvail; ++ for (numPairs = 0; newLen > matches[numPairs]; numPairs += 2); ++ matches[numPairs] = newLen; ++ numPairs += 2; ++ } ++ if (newLen >= startLen) ++ { ++ UInt32 normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[state]); ++ UInt32 offs, curBack, posSlot; ++ UInt32 lenTest; ++ while (lenEnd < cur + newLen) ++ p->opt[++lenEnd].price = kInfinityPrice; ++ ++ offs = 0; ++ while (startLen > matches[offs]) ++ offs += 2; ++ curBack = matches[offs + 1]; ++ GetPosSlot2(curBack, posSlot); ++ for (lenTest = /*2*/ startLen; ; lenTest++) ++ { ++ UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][lenTest - LZMA_MATCH_LEN_MIN]; ++ UInt32 lenToPosState = GetLenToPosState(lenTest); ++ COptimal *opt; ++ if (curBack < kNumFullDistances) ++ curAndLenPrice += p->distancesPrices[lenToPosState][curBack]; ++ else ++ curAndLenPrice += p->posSlotPrices[lenToPosState][posSlot] + p->alignPrices[curBack & kAlignMask]; ++ ++ opt = &p->opt[cur + lenTest]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = cur; ++ opt->backPrev = curBack + LZMA_NUM_REPS; ++ opt->prev1IsChar = False; ++ } ++ ++ if (/*_maxMode && */lenTest == matches[offs]) ++ { ++ /* Try Match + Literal + Rep0 */ ++ const Byte *data2 = data - (curBack + 1); ++ UInt32 lenTest2 = lenTest + 1; ++ UInt32 limit = lenTest2 + p->numFastBytes; ++ UInt32 nextRepMatchPrice; ++ if (limit > numAvailFull) ++ limit = numAvailFull; ++ for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); ++ lenTest2 -= lenTest + 1; ++ if (lenTest2 >= 2) ++ { ++ UInt32 state2 = kMatchNextStates[state]; ++ UInt32 posStateNext = (position + lenTest) & p->pbMask; ++ UInt32 curAndLenCharPrice = curAndLenPrice + ++ GET_PRICE_0(p->isMatch[state2][posStateNext]) + ++ LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), ++ data[lenTest], data2[lenTest], p->ProbPrices); ++ state2 = kLiteralNextStates[state2]; ++ posStateNext = (posStateNext + 1) & p->pbMask; ++ nextRepMatchPrice = curAndLenCharPrice + ++ GET_PRICE_1(p->isMatch[state2][posStateNext]) + ++ GET_PRICE_1(p->isRep[state2]); ++ ++ /* for (; lenTest2 >= 2; lenTest2--) */ ++ { ++ UInt32 offset = cur + lenTest + 1 + lenTest2; ++ UInt32 curAndLenPrice; ++ COptimal *opt; ++ while (lenEnd < offset) ++ p->opt[++lenEnd].price = kInfinityPrice; ++ curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); ++ opt = &p->opt[offset]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = cur + lenTest + 1; ++ opt->backPrev = 0; ++ opt->prev1IsChar = True; ++ opt->prev2 = True; ++ opt->posPrev2 = cur; ++ opt->backPrev2 = curBack + LZMA_NUM_REPS; ++ } ++ } ++ } ++ offs += 2; ++ if (offs == numPairs) ++ break; ++ curBack = matches[offs + 1]; ++ if (curBack >= kNumFullDistances) ++ GetPosSlot2(curBack, posSlot); ++ } ++ } ++ } ++ } ++} ++ ++#define ChangePair(smallDist, bigDist) (((bigDist) >> 7) > (smallDist)) ++ ++static UInt32 GetOptimumFast(CLzmaEnc *p, UInt32 *backRes) ++{ ++ UInt32 numAvail, mainLen, mainDist, numPairs, repIndex, repLen, i; ++ const Byte *data; ++ const UInt32 *matches; ++ ++ if (p->additionalOffset == 0) ++ mainLen = ReadMatchDistances(p, &numPairs); ++ else ++ { ++ mainLen = p->longestMatchLength; ++ numPairs = p->numPairs; ++ } ++ ++ numAvail = p->numAvail; ++ *backRes = (UInt32)-1; ++ if (numAvail < 2) ++ return 1; ++ if (numAvail > LZMA_MATCH_LEN_MAX) ++ numAvail = LZMA_MATCH_LEN_MAX; ++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; ++ ++ repLen = repIndex = 0; ++ for (i = 0; i < LZMA_NUM_REPS; i++) ++ { ++ UInt32 len; ++ const Byte *data2 = data - (p->reps[i] + 1); ++ if (data[0] != data2[0] || data[1] != data2[1]) ++ continue; ++ for (len = 2; len < numAvail && data[len] == data2[len]; len++); ++ if (len >= p->numFastBytes) ++ { ++ *backRes = i; ++ MovePos(p, len - 1); ++ return len; ++ } ++ if (len > repLen) ++ { ++ repIndex = i; ++ repLen = len; ++ } ++ } ++ ++ matches = p->matches; ++ if (mainLen >= p->numFastBytes) ++ { ++ *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; ++ MovePos(p, mainLen - 1); ++ return mainLen; ++ } ++ ++ mainDist = 0; /* for GCC */ ++ if (mainLen >= 2) ++ { ++ mainDist = matches[numPairs - 1]; ++ while (numPairs > 2 && mainLen == matches[numPairs - 4] + 1) ++ { ++ if (!ChangePair(matches[numPairs - 3], mainDist)) ++ break; ++ numPairs -= 2; ++ mainLen = matches[numPairs - 2]; ++ mainDist = matches[numPairs - 1]; ++ } ++ if (mainLen == 2 && mainDist >= 0x80) ++ mainLen = 1; ++ } ++ ++ if (repLen >= 2 && ( ++ (repLen + 1 >= mainLen) || ++ (repLen + 2 >= mainLen && mainDist >= (1 << 9)) || ++ (repLen + 3 >= mainLen && mainDist >= (1 << 15)))) ++ { ++ *backRes = repIndex; ++ MovePos(p, repLen - 1); ++ return repLen; ++ } ++ ++ if (mainLen < 2 || numAvail <= 2) ++ return 1; ++ ++ p->longestMatchLength = ReadMatchDistances(p, &p->numPairs); ++ if (p->longestMatchLength >= 2) ++ { ++ UInt32 newDistance = matches[p->numPairs - 1]; ++ if ((p->longestMatchLength >= mainLen && newDistance < mainDist) || ++ (p->longestMatchLength == mainLen + 1 && !ChangePair(mainDist, newDistance)) || ++ (p->longestMatchLength > mainLen + 1) || ++ (p->longestMatchLength + 1 >= mainLen && mainLen >= 3 && ChangePair(newDistance, mainDist))) ++ return 1; ++ } ++ ++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; ++ for (i = 0; i < LZMA_NUM_REPS; i++) ++ { ++ UInt32 len, limit; ++ const Byte *data2 = data - (p->reps[i] + 1); ++ if (data[0] != data2[0] || data[1] != data2[1]) ++ continue; ++ limit = mainLen - 1; ++ for (len = 2; len < limit && data[len] == data2[len]; len++); ++ if (len >= limit) ++ return 1; ++ } ++ *backRes = mainDist + LZMA_NUM_REPS; ++ MovePos(p, mainLen - 2); ++ return mainLen; ++} ++ ++static void WriteEndMarker(CLzmaEnc *p, UInt32 posState) ++{ ++ UInt32 len; ++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); ++ RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); ++ p->state = kMatchNextStates[p->state]; ++ len = LZMA_MATCH_LEN_MIN; ++ LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); ++ RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, (1 << kNumPosSlotBits) - 1); ++ RangeEnc_EncodeDirectBits(&p->rc, (((UInt32)1 << 30) - 1) >> kNumAlignBits, 30 - kNumAlignBits); ++ RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, kAlignMask); ++} ++ ++static SRes CheckErrors(CLzmaEnc *p) ++{ ++ if (p->result != SZ_OK) ++ return p->result; ++ if (p->rc.res != SZ_OK) ++ p->result = SZ_ERROR_WRITE; ++ if (p->matchFinderBase.result != SZ_OK) ++ p->result = SZ_ERROR_READ; ++ if (p->result != SZ_OK) ++ p->finished = True; ++ return p->result; ++} ++ ++static SRes Flush(CLzmaEnc *p, UInt32 nowPos) ++{ ++ /* ReleaseMFStream(); */ ++ p->finished = True; ++ if (p->writeEndMark) ++ WriteEndMarker(p, nowPos & p->pbMask); ++ RangeEnc_FlushData(&p->rc); ++ RangeEnc_FlushStream(&p->rc); ++ return CheckErrors(p); ++} ++ ++static void FillAlignPrices(CLzmaEnc *p) ++{ ++ UInt32 i; ++ for (i = 0; i < kAlignTableSize; i++) ++ p->alignPrices[i] = RcTree_ReverseGetPrice(p->posAlignEncoder, kNumAlignBits, i, p->ProbPrices); ++ p->alignPriceCount = 0; ++} ++ ++static void FillDistancesPrices(CLzmaEnc *p) ++{ ++ UInt32 tempPrices[kNumFullDistances]; ++ UInt32 i, lenToPosState; ++ for (i = kStartPosModelIndex; i < kNumFullDistances; i++) ++ { ++ UInt32 posSlot = GetPosSlot1(i); ++ UInt32 footerBits = ((posSlot >> 1) - 1); ++ UInt32 base = ((2 | (posSlot & 1)) << footerBits); ++ tempPrices[i] = RcTree_ReverseGetPrice(p->posEncoders + base - posSlot - 1, footerBits, i - base, p->ProbPrices); ++ } ++ ++ for (lenToPosState = 0; lenToPosState < kNumLenToPosStates; lenToPosState++) ++ { ++ UInt32 posSlot; ++ const CLzmaProb *encoder = p->posSlotEncoder[lenToPosState]; ++ UInt32 *posSlotPrices = p->posSlotPrices[lenToPosState]; ++ for (posSlot = 0; posSlot < p->distTableSize; posSlot++) ++ posSlotPrices[posSlot] = RcTree_GetPrice(encoder, kNumPosSlotBits, posSlot, p->ProbPrices); ++ for (posSlot = kEndPosModelIndex; posSlot < p->distTableSize; posSlot++) ++ posSlotPrices[posSlot] += ((((posSlot >> 1) - 1) - kNumAlignBits) << kNumBitPriceShiftBits); ++ ++ { ++ UInt32 *distancesPrices = p->distancesPrices[lenToPosState]; ++ UInt32 i; ++ for (i = 0; i < kStartPosModelIndex; i++) ++ distancesPrices[i] = posSlotPrices[i]; ++ for (; i < kNumFullDistances; i++) ++ distancesPrices[i] = posSlotPrices[GetPosSlot1(i)] + tempPrices[i]; ++ } ++ } ++ p->matchPriceCount = 0; ++} ++ ++void LzmaEnc_Construct(CLzmaEnc *p) ++{ ++ RangeEnc_Construct(&p->rc); ++ MatchFinder_Construct(&p->matchFinderBase); ++ #ifndef _7ZIP_ST ++ MatchFinderMt_Construct(&p->matchFinderMt); ++ p->matchFinderMt.MatchFinder = &p->matchFinderBase; ++ #endif ++ ++ { ++ CLzmaEncProps props; ++ LzmaEncProps_Init(&props); ++ LzmaEnc_SetProps(p, &props); ++ } ++ ++ #ifndef LZMA_LOG_BSR ++ LzmaEnc_FastPosInit(p->g_FastPos); ++ #endif ++ ++ LzmaEnc_InitPriceTables(p->ProbPrices); ++ p->litProbs = 0; ++ p->saveState.litProbs = 0; ++} ++ ++CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc) ++{ ++ void *p; ++ p = alloc->Alloc(alloc, sizeof(CLzmaEnc)); ++ if (p != 0) ++ LzmaEnc_Construct((CLzmaEnc *)p); ++ return p; ++} ++ ++void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->litProbs); ++ alloc->Free(alloc, p->saveState.litProbs); ++ p->litProbs = 0; ++ p->saveState.litProbs = 0; ++} ++ ++void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ #ifndef _7ZIP_ST ++ MatchFinderMt_Destruct(&p->matchFinderMt, allocBig); ++ #endif ++ MatchFinder_Free(&p->matchFinderBase, allocBig); ++ LzmaEnc_FreeLits(p, alloc); ++ RangeEnc_Free(&p->rc, alloc); ++} ++ ++void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ LzmaEnc_Destruct((CLzmaEnc *)p, alloc, allocBig); ++ alloc->Free(alloc, p); ++} ++ ++static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, Bool useLimits, UInt32 maxPackSize, UInt32 maxUnpackSize) ++{ ++ UInt32 nowPos32, startPos32; ++ if (p->needInit) ++ { ++ p->matchFinder.Init(p->matchFinderObj); ++ p->needInit = 0; ++ } ++ ++ if (p->finished) ++ return p->result; ++ RINOK(CheckErrors(p)); ++ ++ nowPos32 = (UInt32)p->nowPos64; ++ startPos32 = nowPos32; ++ ++ if (p->nowPos64 == 0) ++ { ++ UInt32 numPairs; ++ Byte curByte; ++ if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) ++ return Flush(p, nowPos32); ++ ReadMatchDistances(p, &numPairs); ++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][0], 0); ++ p->state = kLiteralNextStates[p->state]; ++ curByte = p->matchFinder.GetIndexByte(p->matchFinderObj, 0 - p->additionalOffset); ++ LitEnc_Encode(&p->rc, p->litProbs, curByte); ++ p->additionalOffset--; ++ nowPos32++; ++ } ++ ++ if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) != 0) ++ for (;;) ++ { ++ UInt32 pos, len, posState; ++ ++ if (p->fastMode) ++ len = GetOptimumFast(p, &pos); ++ else ++ len = GetOptimum(p, nowPos32, &pos); ++ ++ #ifdef SHOW_STAT2 ++ printf("\n pos = %4X, len = %d pos = %d", nowPos32, len, pos); ++ #endif ++ ++ posState = nowPos32 & p->pbMask; ++ if (len == 1 && pos == (UInt32)-1) ++ { ++ Byte curByte; ++ CLzmaProb *probs; ++ const Byte *data; ++ ++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 0); ++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; ++ curByte = *data; ++ probs = LIT_PROBS(nowPos32, *(data - 1)); ++ if (IsCharState(p->state)) ++ LitEnc_Encode(&p->rc, probs, curByte); ++ else ++ LitEnc_EncodeMatched(&p->rc, probs, curByte, *(data - p->reps[0] - 1)); ++ p->state = kLiteralNextStates[p->state]; ++ } ++ else ++ { ++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); ++ if (pos < LZMA_NUM_REPS) ++ { ++ RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 1); ++ if (pos == 0) ++ { ++ RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 0); ++ RangeEnc_EncodeBit(&p->rc, &p->isRep0Long[p->state][posState], ((len == 1) ? 0 : 1)); ++ } ++ else ++ { ++ UInt32 distance = p->reps[pos]; ++ RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 1); ++ if (pos == 1) ++ RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 0); ++ else ++ { ++ RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 1); ++ RangeEnc_EncodeBit(&p->rc, &p->isRepG2[p->state], pos - 2); ++ if (pos == 3) ++ p->reps[3] = p->reps[2]; ++ p->reps[2] = p->reps[1]; ++ } ++ p->reps[1] = p->reps[0]; ++ p->reps[0] = distance; ++ } ++ if (len == 1) ++ p->state = kShortRepNextStates[p->state]; ++ else ++ { ++ LenEnc_Encode2(&p->repLenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); ++ p->state = kRepNextStates[p->state]; ++ } ++ } ++ else ++ { ++ UInt32 posSlot; ++ RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); ++ p->state = kMatchNextStates[p->state]; ++ LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); ++ pos -= LZMA_NUM_REPS; ++ GetPosSlot(pos, posSlot); ++ RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, posSlot); ++ ++ if (posSlot >= kStartPosModelIndex) ++ { ++ UInt32 footerBits = ((posSlot >> 1) - 1); ++ UInt32 base = ((2 | (posSlot & 1)) << footerBits); ++ UInt32 posReduced = pos - base; ++ ++ if (posSlot < kEndPosModelIndex) ++ RcTree_ReverseEncode(&p->rc, p->posEncoders + base - posSlot - 1, footerBits, posReduced); ++ else ++ { ++ RangeEnc_EncodeDirectBits(&p->rc, posReduced >> kNumAlignBits, footerBits - kNumAlignBits); ++ RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, posReduced & kAlignMask); ++ p->alignPriceCount++; ++ } ++ } ++ p->reps[3] = p->reps[2]; ++ p->reps[2] = p->reps[1]; ++ p->reps[1] = p->reps[0]; ++ p->reps[0] = pos; ++ p->matchPriceCount++; ++ } ++ } ++ p->additionalOffset -= len; ++ nowPos32 += len; ++ if (p->additionalOffset == 0) ++ { ++ UInt32 processed; ++ if (!p->fastMode) ++ { ++ if (p->matchPriceCount >= (1 << 7)) ++ FillDistancesPrices(p); ++ if (p->alignPriceCount >= kAlignTableSize) ++ FillAlignPrices(p); ++ } ++ if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) ++ break; ++ processed = nowPos32 - startPos32; ++ if (useLimits) ++ { ++ if (processed + kNumOpts + 300 >= maxUnpackSize || ++ RangeEnc_GetProcessed(&p->rc) + kNumOpts * 2 >= maxPackSize) ++ break; ++ } ++ else if (processed >= (1 << 15)) ++ { ++ p->nowPos64 += nowPos32 - startPos32; ++ return CheckErrors(p); ++ } ++ } ++ } ++ p->nowPos64 += nowPos32 - startPos32; ++ return Flush(p, nowPos32); ++} ++ ++#define kBigHashDicLimit ((UInt32)1 << 24) ++ ++static SRes LzmaEnc_Alloc(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ UInt32 beforeSize = kNumOpts; ++ Bool btMode; ++ if (!RangeEnc_Alloc(&p->rc, alloc)) ++ return SZ_ERROR_MEM; ++ btMode = (p->matchFinderBase.btMode != 0); ++ #ifndef _7ZIP_ST ++ p->mtMode = (p->multiThread && !p->fastMode && btMode); ++ #endif ++ ++ { ++ unsigned lclp = p->lc + p->lp; ++ if (p->litProbs == 0 || p->saveState.litProbs == 0 || p->lclp != lclp) ++ { ++ LzmaEnc_FreeLits(p, alloc); ++ p->litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); ++ p->saveState.litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); ++ if (p->litProbs == 0 || p->saveState.litProbs == 0) ++ { ++ LzmaEnc_FreeLits(p, alloc); ++ return SZ_ERROR_MEM; ++ } ++ p->lclp = lclp; ++ } ++ } ++ ++ p->matchFinderBase.bigHash = (p->dictSize > kBigHashDicLimit); ++ ++ if (beforeSize + p->dictSize < keepWindowSize) ++ beforeSize = keepWindowSize - p->dictSize; ++ ++ #ifndef _7ZIP_ST ++ if (p->mtMode) ++ { ++ RINOK(MatchFinderMt_Create(&p->matchFinderMt, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)); ++ p->matchFinderObj = &p->matchFinderMt; ++ MatchFinderMt_CreateVTable(&p->matchFinderMt, &p->matchFinder); ++ } ++ else ++ #endif ++ { ++ if (!MatchFinder_Create(&p->matchFinderBase, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)) ++ return SZ_ERROR_MEM; ++ p->matchFinderObj = &p->matchFinderBase; ++ MatchFinder_CreateVTable(&p->matchFinderBase, &p->matchFinder); ++ } ++ return SZ_OK; ++} ++ ++void LzmaEnc_Init(CLzmaEnc *p) ++{ ++ UInt32 i; ++ p->state = 0; ++ for (i = 0 ; i < LZMA_NUM_REPS; i++) ++ p->reps[i] = 0; ++ ++ RangeEnc_Init(&p->rc); ++ ++ ++ for (i = 0; i < kNumStates; i++) ++ { ++ UInt32 j; ++ for (j = 0; j < LZMA_NUM_PB_STATES_MAX; j++) ++ { ++ p->isMatch[i][j] = kProbInitValue; ++ p->isRep0Long[i][j] = kProbInitValue; ++ } ++ p->isRep[i] = kProbInitValue; ++ p->isRepG0[i] = kProbInitValue; ++ p->isRepG1[i] = kProbInitValue; ++ p->isRepG2[i] = kProbInitValue; ++ } ++ ++ { ++ UInt32 num = 0x300 << (p->lp + p->lc); ++ for (i = 0; i < num; i++) ++ p->litProbs[i] = kProbInitValue; ++ } ++ ++ { ++ for (i = 0; i < kNumLenToPosStates; i++) ++ { ++ CLzmaProb *probs = p->posSlotEncoder[i]; ++ UInt32 j; ++ for (j = 0; j < (1 << kNumPosSlotBits); j++) ++ probs[j] = kProbInitValue; ++ } ++ } ++ { ++ for (i = 0; i < kNumFullDistances - kEndPosModelIndex; i++) ++ p->posEncoders[i] = kProbInitValue; ++ } ++ ++ LenEnc_Init(&p->lenEnc.p); ++ LenEnc_Init(&p->repLenEnc.p); ++ ++ for (i = 0; i < (1 << kNumAlignBits); i++) ++ p->posAlignEncoder[i] = kProbInitValue; ++ ++ p->optimumEndIndex = 0; ++ p->optimumCurrentIndex = 0; ++ p->additionalOffset = 0; ++ ++ p->pbMask = (1 << p->pb) - 1; ++ p->lpMask = (1 << p->lp) - 1; ++} ++ ++void LzmaEnc_InitPrices(CLzmaEnc *p) ++{ ++ if (!p->fastMode) ++ { ++ FillDistancesPrices(p); ++ FillAlignPrices(p); ++ } ++ ++ p->lenEnc.tableSize = ++ p->repLenEnc.tableSize = ++ p->numFastBytes + 1 - LZMA_MATCH_LEN_MIN; ++ LenPriceEnc_UpdateTables(&p->lenEnc, 1 << p->pb, p->ProbPrices); ++ LenPriceEnc_UpdateTables(&p->repLenEnc, 1 << p->pb, p->ProbPrices); ++} ++ ++static SRes LzmaEnc_AllocAndInit(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ UInt32 i; ++ for (i = 0; i < (UInt32)kDicLogSizeMaxCompress; i++) ++ if (p->dictSize <= ((UInt32)1 << i)) ++ break; ++ p->distTableSize = i * 2; ++ ++ p->finished = False; ++ p->result = SZ_OK; ++ RINOK(LzmaEnc_Alloc(p, keepWindowSize, alloc, allocBig)); ++ LzmaEnc_Init(p); ++ LzmaEnc_InitPrices(p); ++ p->nowPos64 = 0; ++ return SZ_OK; ++} ++ ++static SRes LzmaEnc_Prepare(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ++ ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ p->matchFinderBase.stream = inStream; ++ p->needInit = 1; ++ p->rc.outStream = outStream; ++ return LzmaEnc_AllocAndInit(p, 0, alloc, allocBig); ++} ++ ++SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp, ++ ISeqInStream *inStream, UInt32 keepWindowSize, ++ ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ p->matchFinderBase.stream = inStream; ++ p->needInit = 1; ++ return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); ++} ++ ++static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen) ++{ ++ p->matchFinderBase.directInput = 1; ++ p->matchFinderBase.bufferBase = (Byte *)src; ++ p->matchFinderBase.directInputRem = srcLen; ++} ++ ++SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen, ++ UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ LzmaEnc_SetInputBuf(p, src, srcLen); ++ p->needInit = 1; ++ ++ return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); ++} ++ ++void LzmaEnc_Finish(CLzmaEncHandle pp) ++{ ++ #ifndef _7ZIP_ST ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ if (p->mtMode) ++ MatchFinderMt_ReleaseStream(&p->matchFinderMt); ++ #else ++ pp = pp; ++ #endif ++} ++ ++typedef struct ++{ ++ ISeqOutStream funcTable; ++ Byte *data; ++ SizeT rem; ++ Bool overflow; ++} CSeqOutStreamBuf; ++ ++static size_t MyWrite(void *pp, const void *data, size_t size) ++{ ++ CSeqOutStreamBuf *p = (CSeqOutStreamBuf *)pp; ++ if (p->rem < size) ++ { ++ size = p->rem; ++ p->overflow = True; ++ } ++ memcpy(p->data, data, size); ++ p->rem -= size; ++ p->data += size; ++ return size; ++} ++ ++ ++UInt32 LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp) ++{ ++ const CLzmaEnc *p = (CLzmaEnc *)pp; ++ return p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); ++} ++ ++const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp) ++{ ++ const CLzmaEnc *p = (CLzmaEnc *)pp; ++ return p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; ++} ++ ++SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit, ++ Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ UInt64 nowPos64; ++ SRes res; ++ CSeqOutStreamBuf outStream; ++ ++ outStream.funcTable.Write = MyWrite; ++ outStream.data = dest; ++ outStream.rem = *destLen; ++ outStream.overflow = False; ++ ++ p->writeEndMark = False; ++ p->finished = False; ++ p->result = SZ_OK; ++ ++ if (reInit) ++ LzmaEnc_Init(p); ++ LzmaEnc_InitPrices(p); ++ nowPos64 = p->nowPos64; ++ RangeEnc_Init(&p->rc); ++ p->rc.outStream = &outStream.funcTable; ++ ++ res = LzmaEnc_CodeOneBlock(p, True, desiredPackSize, *unpackSize); ++ ++ *unpackSize = (UInt32)(p->nowPos64 - nowPos64); ++ *destLen -= outStream.rem; ++ if (outStream.overflow) ++ return SZ_ERROR_OUTPUT_EOF; ++ ++ return res; ++} ++ ++static SRes LzmaEnc_Encode2(CLzmaEnc *p, ICompressProgress *progress) ++{ ++ SRes res = SZ_OK; ++ ++ #ifndef _7ZIP_ST ++ Byte allocaDummy[0x300]; ++ int i = 0; ++ for (i = 0; i < 16; i++) ++ allocaDummy[i] = (Byte)i; ++ #endif ++ ++ for (;;) ++ { ++ res = LzmaEnc_CodeOneBlock(p, False, 0, 0); ++ if (res != SZ_OK || p->finished != 0) ++ break; ++ if (progress != 0) ++ { ++ res = progress->Progress(progress, p->nowPos64, RangeEnc_GetProcessed(&p->rc)); ++ if (res != SZ_OK) ++ { ++ res = SZ_ERROR_PROGRESS; ++ break; ++ } ++ } ++ } ++ LzmaEnc_Finish(p); ++ return res; ++} ++ ++SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress, ++ ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ RINOK(LzmaEnc_Prepare(pp, outStream, inStream, alloc, allocBig)); ++ return LzmaEnc_Encode2((CLzmaEnc *)pp, progress); ++} ++ ++SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, Byte *props, SizeT *size) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ int i; ++ UInt32 dictSize = p->dictSize; ++ if (*size < LZMA_PROPS_SIZE) ++ return SZ_ERROR_PARAM; ++ *size = LZMA_PROPS_SIZE; ++ props[0] = (Byte)((p->pb * 5 + p->lp) * 9 + p->lc); ++ ++ for (i = 11; i <= 30; i++) ++ { ++ if (dictSize <= ((UInt32)2 << i)) ++ { ++ dictSize = (2 << i); ++ break; ++ } ++ if (dictSize <= ((UInt32)3 << i)) ++ { ++ dictSize = (3 << i); ++ break; ++ } ++ } ++ ++ for (i = 0; i < 4; i++) ++ props[1 + i] = (Byte)(dictSize >> (8 * i)); ++ return SZ_OK; ++} ++ ++SRes LzmaEnc_MemEncode(CLzmaEncHandle pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, ++ int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ SRes res; ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ ++ CSeqOutStreamBuf outStream; ++ ++ LzmaEnc_SetInputBuf(p, src, srcLen); ++ ++ outStream.funcTable.Write = MyWrite; ++ outStream.data = dest; ++ outStream.rem = *destLen; ++ outStream.overflow = False; ++ ++ p->writeEndMark = writeEndMark; ++ ++ p->rc.outStream = &outStream.funcTable; ++ res = LzmaEnc_MemPrepare(pp, src, srcLen, 0, alloc, allocBig); ++ if (res == SZ_OK) ++ res = LzmaEnc_Encode2(p, progress); ++ ++ *destLen -= outStream.rem; ++ if (outStream.overflow) ++ return SZ_ERROR_OUTPUT_EOF; ++ return res; ++} ++ ++SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, ++ const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, ++ ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)LzmaEnc_Create(alloc); ++ SRes res; ++ if (p == 0) ++ return SZ_ERROR_MEM; ++ ++ res = LzmaEnc_SetProps(p, props); ++ if (res == SZ_OK) ++ { ++ res = LzmaEnc_WriteProperties(p, propsEncoded, propsSize); ++ if (res == SZ_OK) ++ res = LzmaEnc_MemEncode(p, dest, destLen, src, srcLen, ++ writeEndMark, progress, alloc, allocBig); ++ } ++ ++ LzmaEnc_Destroy(p, alloc, allocBig); ++ return res; ++} + +Property changes on: third_party/lzma_sdk/LzmaEnc.c +___________________________________________________________________ +Added: svn:eol-style + + LF + +Index: third_party/lzma_sdk/Alloc.c +=================================================================== +--- third_party/lzma_sdk/Alloc.c (revision 0) ++++ third_party/lzma_sdk/Alloc.c (revision 0) +@@ -0,0 +1,127 @@ ++/* Alloc.c -- Memory allocation functions ++2008-09-24 ++Igor Pavlov ++Public domain */ ++ ++#ifdef _WIN32 ++#include ++#endif ++#include ++ ++#include "Alloc.h" ++ ++/* #define _SZ_ALLOC_DEBUG */ ++ ++/* use _SZ_ALLOC_DEBUG to debug alloc/free operations */ ++#ifdef _SZ_ALLOC_DEBUG ++#include ++int g_allocCount = 0; ++int g_allocCountMid = 0; ++int g_allocCountBig = 0; ++#endif ++ ++void *MyAlloc(size_t size) ++{ ++ if (size == 0) ++ return 0; ++ #ifdef _SZ_ALLOC_DEBUG ++ { ++ void *p = malloc(size); ++ fprintf(stderr, "\nAlloc %10d bytes, count = %10d, addr = %8X", size, g_allocCount++, (unsigned)p); ++ return p; ++ } ++ #else ++ return malloc(size); ++ #endif ++} ++ ++void MyFree(void *address) ++{ ++ #ifdef _SZ_ALLOC_DEBUG ++ if (address != 0) ++ fprintf(stderr, "\nFree; count = %10d, addr = %8X", --g_allocCount, (unsigned)address); ++ #endif ++ free(address); ++} ++ ++#ifdef _WIN32 ++ ++void *MidAlloc(size_t size) ++{ ++ if (size == 0) ++ return 0; ++ #ifdef _SZ_ALLOC_DEBUG ++ fprintf(stderr, "\nAlloc_Mid %10d bytes; count = %10d", size, g_allocCountMid++); ++ #endif ++ return VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE); ++} ++ ++void MidFree(void *address) ++{ ++ #ifdef _SZ_ALLOC_DEBUG ++ if (address != 0) ++ fprintf(stderr, "\nFree_Mid; count = %10d", --g_allocCountMid); ++ #endif ++ if (address == 0) ++ return; ++ VirtualFree(address, 0, MEM_RELEASE); ++} ++ ++#ifndef MEM_LARGE_PAGES ++#undef _7ZIP_LARGE_PAGES ++#endif ++ ++#ifdef _7ZIP_LARGE_PAGES ++SIZE_T g_LargePageSize = 0; ++typedef SIZE_T (WINAPI *GetLargePageMinimumP)(); ++#endif ++ ++void SetLargePageSize() ++{ ++ #ifdef _7ZIP_LARGE_PAGES ++ SIZE_T size = 0; ++ GetLargePageMinimumP largePageMinimum = (GetLargePageMinimumP) ++ GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetLargePageMinimum"); ++ if (largePageMinimum == 0) ++ return; ++ size = largePageMinimum(); ++ if (size == 0 || (size & (size - 1)) != 0) ++ return; ++ g_LargePageSize = size; ++ #endif ++} ++ ++ ++void *BigAlloc(size_t size) ++{ ++ if (size == 0) ++ return 0; ++ #ifdef _SZ_ALLOC_DEBUG ++ fprintf(stderr, "\nAlloc_Big %10d bytes; count = %10d", size, g_allocCountBig++); ++ #endif ++ ++ #ifdef _7ZIP_LARGE_PAGES ++ if (g_LargePageSize != 0 && g_LargePageSize <= (1 << 30) && size >= (1 << 18)) ++ { ++ void *res = VirtualAlloc(0, (size + g_LargePageSize - 1) & (~(g_LargePageSize - 1)), ++ MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); ++ if (res != 0) ++ return res; ++ } ++ #endif ++ return VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE); ++} ++ ++void BigFree(void *address) ++{ ++ #ifdef _SZ_ALLOC_DEBUG ++ if (address != 0) ++ fprintf(stderr, "\nFree_Big; count = %10d", --g_allocCountBig); ++ #endif ++ ++ if (address == 0) ++ return; ++ VirtualFree(address, 0, MEM_RELEASE); ++} ++ ++#endif + +Property changes on: third_party/lzma_sdk/Alloc.c +___________________________________________________________________ +Added: svn:eol-style + + LF + +Index: third_party/lzma_sdk/LzmaLib.c +=================================================================== +--- third_party/lzma_sdk/LzmaLib.c (revision 0) ++++ third_party/lzma_sdk/LzmaLib.c (revision 0) +@@ -0,0 +1,46 @@ ++/* LzmaLib.c -- LZMA library wrapper ++2008-08-05 ++Igor Pavlov ++Public domain */ ++ ++#include "LzmaEnc.h" ++#include "LzmaDec.h" ++#include "Alloc.h" ++#include "LzmaLib.h" ++ ++static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); } ++static void SzFree(void *p, void *address) { p = p; MyFree(address); } ++static ISzAlloc g_Alloc = { SzAlloc, SzFree }; ++ ++MY_STDAPI LzmaCompress(unsigned char *dest, size_t *destLen, const unsigned char *src, size_t srcLen, ++ unsigned char *outProps, size_t *outPropsSize, ++ int level, /* 0 <= level <= 9, default = 5 */ ++ unsigned dictSize, /* use (1 << N) or (3 << N). 4 KB < dictSize <= 128 MB */ ++ int lc, /* 0 <= lc <= 8, default = 3 */ ++ int lp, /* 0 <= lp <= 4, default = 0 */ ++ int pb, /* 0 <= pb <= 4, default = 2 */ ++ int fb, /* 5 <= fb <= 273, default = 32 */ ++ int numThreads /* 1 or 2, default = 2 */ ++) ++{ ++ CLzmaEncProps props; ++ LzmaEncProps_Init(&props); ++ props.level = level; ++ props.dictSize = dictSize; ++ props.lc = lc; ++ props.lp = lp; ++ props.pb = pb; ++ props.fb = fb; ++ props.numThreads = numThreads; ++ ++ return LzmaEncode(dest, destLen, src, srcLen, &props, outProps, outPropsSize, 0, ++ NULL, &g_Alloc, &g_Alloc); ++} ++ ++ ++MY_STDAPI LzmaUncompress(unsigned char *dest, size_t *destLen, const unsigned char *src, size_t *srcLen, ++ const unsigned char *props, size_t propsSize) ++{ ++ ELzmaStatus status; ++ return LzmaDecode(dest, destLen, src, srcLen, props, (unsigned)propsSize, LZMA_FINISH_ANY, &status, &g_Alloc); ++} + +Property changes on: third_party/lzma_sdk/LzmaLib.c +___________________________________________________________________ +Added: svn:eol-style + + LF + +Index: third_party/lzma_sdk/LzFind.h +=================================================================== +--- third_party/lzma_sdk/LzFind.h (revision 0) ++++ third_party/lzma_sdk/LzFind.h (revision 0) +@@ -0,0 +1,115 @@ ++/* LzFind.h -- Match finder for LZ algorithms ++2009-04-22 : Igor Pavlov : Public domain */ ++ ++#ifndef __LZ_FIND_H ++#define __LZ_FIND_H ++ ++#include "Types.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++typedef UInt32 CLzRef; ++ ++typedef struct _CMatchFinder ++{ ++ Byte *buffer; ++ UInt32 pos; ++ UInt32 posLimit; ++ UInt32 streamPos; ++ UInt32 lenLimit; ++ ++ UInt32 cyclicBufferPos; ++ UInt32 cyclicBufferSize; /* it must be = (historySize + 1) */ ++ ++ UInt32 matchMaxLen; ++ CLzRef *hash; ++ CLzRef *son; ++ UInt32 hashMask; ++ UInt32 cutValue; ++ ++ Byte *bufferBase; ++ ISeqInStream *stream; ++ int streamEndWasReached; ++ ++ UInt32 blockSize; ++ UInt32 keepSizeBefore; ++ UInt32 keepSizeAfter; ++ ++ UInt32 numHashBytes; ++ int directInput; ++ size_t directInputRem; ++ int btMode; ++ int bigHash; ++ UInt32 historySize; ++ UInt32 fixedHashSize; ++ UInt32 hashSizeSum; ++ UInt32 numSons; ++ SRes result; ++ UInt32 crc[256]; ++} CMatchFinder; ++ ++#define Inline_MatchFinder_GetPointerToCurrentPos(p) ((p)->buffer) ++#define Inline_MatchFinder_GetIndexByte(p, index) ((p)->buffer[(Int32)(index)]) ++ ++#define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos) ++ ++int MatchFinder_NeedMove(CMatchFinder *p); ++Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p); ++void MatchFinder_MoveBlock(CMatchFinder *p); ++void MatchFinder_ReadIfRequired(CMatchFinder *p); ++ ++void MatchFinder_Construct(CMatchFinder *p); ++ ++/* Conditions: ++ historySize <= 3 GB ++ keepAddBufferBefore + matchMaxLen + keepAddBufferAfter < 511MB ++*/ ++int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, ++ UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ++ ISzAlloc *alloc); ++void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc); ++void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems); ++void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue); ++ ++UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *buffer, CLzRef *son, ++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue, ++ UInt32 *distances, UInt32 maxLen); ++ ++/* ++Conditions: ++ Mf_GetNumAvailableBytes_Func must be called before each Mf_GetMatchLen_Func. ++ Mf_GetPointerToCurrentPos_Func's result must be used only before any other function ++*/ ++ ++typedef void (*Mf_Init_Func)(void *object); ++typedef Byte (*Mf_GetIndexByte_Func)(void *object, Int32 index); ++typedef UInt32 (*Mf_GetNumAvailableBytes_Func)(void *object); ++typedef const Byte * (*Mf_GetPointerToCurrentPos_Func)(void *object); ++typedef UInt32 (*Mf_GetMatches_Func)(void *object, UInt32 *distances); ++typedef void (*Mf_Skip_Func)(void *object, UInt32); ++ ++typedef struct _IMatchFinder ++{ ++ Mf_Init_Func Init; ++ Mf_GetIndexByte_Func GetIndexByte; ++ Mf_GetNumAvailableBytes_Func GetNumAvailableBytes; ++ Mf_GetPointerToCurrentPos_Func GetPointerToCurrentPos; ++ Mf_GetMatches_Func GetMatches; ++ Mf_Skip_Func Skip; ++} IMatchFinder; ++ ++void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable); ++ ++void MatchFinder_Init(CMatchFinder *p); ++UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); ++UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); ++void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); ++void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif + +Property changes on: third_party/lzma_sdk/LzFind.h +___________________________________________________________________ +Added: svn:eol-style + + LF + diff --git a/src/com/google/typography/font/compression/AdvWidth.java b/src/com/google/typography/font/compression/AdvWidth.java new file mode 100644 index 0000000..a95a1cd --- /dev/null +++ b/src/com/google/typography/font/compression/AdvWidth.java @@ -0,0 +1,27 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package com.google.typography.font.compression; + +import com.google.typography.font.sfntly.Font; +import com.google.typography.font.sfntly.Tag; +import com.google.typography.font.sfntly.data.WritableFontData; +import com.google.typography.font.sfntly.table.core.HorizontalMetricsTable; + +/** + * Extract just advance widths from hmtx table. + * + * @author raph@google.com (Raph Levien) + */ +public class AdvWidth { + public static WritableFontData encode(Font font) { + HorizontalMetricsTable hmtx = font.getTable(Tag.hmtx); + int nMetrics = hmtx.numberOfHMetrics(); + WritableFontData result = WritableFontData.createWritableFontData(nMetrics * 2); + for (int i = 0; i < nMetrics; i++) { + result.writeShort(i * 2, hmtx.hMetricAdvanceWidth(i)); + } + return result; + } +} diff --git a/src/com/google/typography/font/compression/CmapEncoder.java b/src/com/google/typography/font/compression/CmapEncoder.java new file mode 100644 index 0000000..f461831 --- /dev/null +++ b/src/com/google/typography/font/compression/CmapEncoder.java @@ -0,0 +1,82 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package com.google.typography.font.compression; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.typography.font.sfntly.Font; +import com.google.typography.font.sfntly.Tag; +import com.google.typography.font.sfntly.table.core.CMap; +import com.google.typography.font.sfntly.table.core.CMapTable; +import com.google.typography.font.sfntly.table.core.MaximumProfileTable; + +import java.io.ByteArrayOutputStream; +import java.util.List; +import java.util.Map; + +/** + * @author raph@google.com (Raph Levien) + * + * Experimental CMap encoder, based primarily on writing the _inverse_ encoding. + */ +public class CmapEncoder { + public static byte[] encode(Font font) { + int nGlyphs = font.getTable(Tag.maxp).numGlyphs(); + CMapTable cmapTable = font.getTable(Tag.cmap); + CMap cmap = getBestCMap(cmapTable); + Map invEncoding = Maps.newHashMap(); + List exceptions = Lists.newArrayList(); + for (Integer i : cmap) { + int glyphId = cmap.glyphId(i); + if (invEncoding.containsKey(glyphId)) { + exceptions.add(i); + exceptions.add(glyphId); + } else { + invEncoding.put(glyphId, i); + } + } + ByteArrayOutputStream os = new ByteArrayOutputStream(); + int last = -1; + for (int i = 0; i < nGlyphs; i++) { + if (invEncoding.containsKey(i)) { + int value = invEncoding.get(i); + int delta = value - last; + writeVShort(os, delta); + last = value; + } else { + writeVShort(os, 0); + } + } + for (int i : exceptions) { + writeVShort(os, i); + } + return os.toByteArray(); + } + + private static CMap getBestCMap(CMapTable cmapTable) { + for (CMap cmap : cmapTable) { + if (cmap.format() == CMap.CMapFormat.Format12.value()) { + return cmap; + } + } + for (CMap cmap : cmapTable) { + if (cmap.format() == CMap.CMapFormat.Format4.value()) { + return cmap; + } + } + return null; + } + + // A simple signed varint encoding + static void writeVShort(ByteArrayOutputStream os, int value) { + if (value >= 0x2000 || value < -0x2000) { + os.write((byte)(0x80 | ((value >> 14) & 0x7f))); + } + if (value >= 0x40 || value < -0x40) { + os.write((byte)(0x80 | ((value >> 7) & 0x7f))); + } + os.write((byte)(value & 0x7f)); + } +} diff --git a/src/com/google/typography/font/compression/Command.java b/src/com/google/typography/font/compression/Command.java new file mode 100644 index 0000000..6b11f31 --- /dev/null +++ b/src/com/google/typography/font/compression/Command.java @@ -0,0 +1,72 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package com.google.typography.font.compression; + +import com.google.common.io.ByteStreams; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * This is a simple wrapper to run a commandline as a pipe. It's not + * particularly efficient, robust, or featureful. + */ +public class Command { + + private class StreamConsumer extends Thread { + private final InputStream is; + private byte[] result = null; + + public StreamConsumer(InputStream is) { + this.is = is; + } + + @Override + public void run() { + try { + result = ByteStreams.toByteArray(is); + } catch (IOException e) { + // TODO: handle this well + } + } + + public byte[] getResult() { + return result; + } + } + + private final ProcessBuilder processBuilder; + + public Command(String[] args) { + processBuilder = new ProcessBuilder(args); + } + + public CommandResult execute(byte[] input) throws CommandException { + Process process; + try { + process = processBuilder.start(); + } catch (IOException e) { + throw new CommandException("exec failed"); + } + StreamConsumer sc = new StreamConsumer(process.getInputStream()); + sc.start(); + OutputStream stdin = process.getOutputStream(); + try { + stdin.write(input); + stdin.close(); + } catch (IOException e) { + throw new CommandException("error writing input"); + } + try { + process.waitFor(); + sc.join(); + } catch (InterruptedException e) { + throw new CommandException("interrupted"); + } + return new CommandResult(sc.getResult()); + } +} + diff --git a/src/com/google/typography/font/compression/CommandException.java b/src/com/google/typography/font/compression/CommandException.java new file mode 100644 index 0000000..9f9125f --- /dev/null +++ b/src/com/google/typography/font/compression/CommandException.java @@ -0,0 +1,12 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package com.google.typography.font.compression; + +public class CommandException extends Exception { + public CommandException(String message) { + super(message); + } +} + diff --git a/src/com/google/typography/font/compression/CommandResult.java b/src/com/google/typography/font/compression/CommandResult.java new file mode 100644 index 0000000..4f1e787 --- /dev/null +++ b/src/com/google/typography/font/compression/CommandResult.java @@ -0,0 +1,18 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package com.google.typography.font.compression; + +public final class CommandResult { + private final byte[] stdout; + + public CommandResult(byte[] stdout) { + this.stdout = stdout; + } + + public byte[] getStdout() { + return stdout; + } +} + diff --git a/src/com/google/typography/font/compression/CompressLzma.java b/src/com/google/typography/font/compression/CompressLzma.java new file mode 100644 index 0000000..5b749c1 --- /dev/null +++ b/src/com/google/typography/font/compression/CompressLzma.java @@ -0,0 +1,22 @@ +// Copyright 2012 Google Inc. All Rights Reserved. + +package com.google.typography.font.compression; + +/** + * @author raph@google.com (Raph Levien) + */ +public class CompressLzma { + // This is currently implemented by shelling out to a command line helper, + // which is fine for research purposes, but obviously problematic for + // production. + public static byte[] compress(byte[] input) { + try { + String[] args = {"/usr/bin/lzma"}; + CommandResult result = new Command(args).execute(input); + return result.getStdout(); + } catch (CommandException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/com/google/typography/font/compression/CompressionRunner.java b/src/com/google/typography/font/compression/CompressionRunner.java new file mode 100644 index 0000000..e7263f0 --- /dev/null +++ b/src/com/google/typography/font/compression/CompressionRunner.java @@ -0,0 +1,283 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package com.google.typography.font.compression; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.common.io.Files; +import com.google.typography.font.sfntly.Font; +import com.google.typography.font.sfntly.FontFactory; +import com.google.typography.font.sfntly.Tag; +import com.google.typography.font.sfntly.data.ReadableFontData; +import com.google.typography.font.tools.conversion.eot.EOTWriter; +import com.google.typography.font.tools.conversion.eot.HdmxEncoder; +import com.google.typography.font.tools.conversion.woff.WoffWriter; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; + +/** + * A command-line tool for running different experimental compression code over + * a corpus of fonts, and gathering statistics, particularly compression + * efficiency. + * + * This is not intended to be production code, and for certain codecs it will + * shell out to helper binaries, which is fine for the purposes of gathering + * statistics, but obviously not much else. + * + * @author raph@google.com (Raph Levien) + */ +public class CompressionRunner { + // private static final boolean DEBUG = false; + + public static void main(String[] args) throws IOException { + boolean generateOutput = false; + List descs = Lists.newArrayList(); + String baseline = "gzip"; + + List filenames = Lists.newArrayList(); + for (int i = 0; i < args.length; i++) { + if (args[i].charAt(0) == '-') { + if (args[i].equals("-o")) { + generateOutput = true; + } else if (args[i].equals("-x")) { + descs.add(args[i + 1]); + i++; + } else if (args[i].equals("-b")) { + baseline = args[i + 1]; + i++; + } + } else { + filenames.add(args[i]); + } + } + + // String baseline = "glyf/triplet,code,push:lzma"; + // String baseline = "glyf/cbbox,triplet,code,push:hdmx:lzma"; + // descs.add("woff2"); + if (descs.isEmpty()) { + descs.add("glyf/cbbox,triplet,code,reslice:woff2"); + } + runTest(filenames, baseline, descs, generateOutput); + } + + private static void runTest(List filenames, String baseline, List descs, + boolean generateOutput) throws IOException { + PrintWriter o = new PrintWriter(System.out); + List stats = Lists.newArrayList(); + for (int i = 0; i < descs.size(); i++) { + stats.add(new StatsCollector()); + } + FontFactory fontFactory = FontFactory.getInstance(); + o.println(""); + for (String filename : filenames) { + byte[] bytes = Files.toByteArray(new File(filename)); + Font font = fontFactory.loadFonts(bytes)[0]; + byte[] baselineResult = runExperiment(font, baseline); + o.printf("\n"); + } + stats.get(0).chartHeader(o, descs.size()); + for (int i = 0; i < descs.size(); i++) { + stats.get(i).chartData(o, i + 1); + } + stats.get(0).chartEnd(o); + o.printf("

baseline: %s

\n", baseline); + for (int i = 0; i < descs.size(); i++) { + StatsCollector sc = stats.get(i); + o.printf("

%c: %s: median %f, mean %f

\n", + 'A' + i, descs.get(i), sc.median(), sc.mean()); + } + stats.get(0).chartFooter(o); + o.close(); + } + + private static Font.Builder stripTags(Font srcFont, Set removeTags) { + FontFactory fontFactory = FontFactory.getInstance(); + Font.Builder fontBuilder = fontFactory.newFontBuilder(); + + for (Integer tag : srcFont.tableMap().keySet()) { + if (!removeTags.contains(tag)) { + fontBuilder.newTableBuilder(tag, srcFont.getTable(tag).readFontData()); + } + } + return fontBuilder; + } + + private static Font.Builder preprocessMtxGlyf(Font srcFont, String options) { + Font.Builder fontBuilder = stripTags(srcFont, ImmutableSet.of()); + GlyfEncoder glyfEncoder = new GlyfEncoder(options); + glyfEncoder.encode(srcFont); + addTableBytes(fontBuilder, Tag.intValue(new byte[] {'g', 'l', 'z', '1'}), + glyfEncoder.getGlyfBytes()); + addTableBytes(fontBuilder, Tag.intValue(new byte[] {'l', 'o', 'c', 'z'}), + glyfEncoder.getLocaBytes()); + if (!Arrays.asList(options.split(",")).contains("reslice")) { + addTableBytes(fontBuilder, Tag.intValue(new byte[] {'g', 'l', 'z', '2'}), + glyfEncoder.getCodeBytes()); + addTableBytes(fontBuilder, Tag.intValue(new byte[] {'g', 'l', 'z', '3'}), + glyfEncoder.getPushBytes()); + } + return fontBuilder; + } + + private static Font.Builder preprocessHmtx(Font srcFont) { + Font.Builder fontBuilder = stripTags(srcFont, ImmutableSet.of(Tag.hmtx)); + addTableBytes(fontBuilder, Tag.intValue(new byte[] {'h', 'm', 't', 'z'}), + toBytes(AdvWidth.encode(srcFont))); + return fontBuilder; + } + + private static Font.Builder preprocessHdmx(Font srcFont) { + Font.Builder fontBuilder = stripTags(srcFont, ImmutableSet.of(Tag.hdmx)); + if (srcFont.hasTable(Tag.hdmx)) { + addTableBytes(fontBuilder, Tag.hdmx, toBytes(new HdmxEncoder().encode(srcFont))); + } + return fontBuilder; + } + + private static Font.Builder preprocessCmap(Font srcFont) { + Font.Builder fontBuilder = stripTags(srcFont, ImmutableSet.of(Tag.cmap)); + addTableBytes(fontBuilder, Tag.intValue(new byte[] {'c', 'm', 'a', 'z'}), + CmapEncoder.encode(srcFont)); + return fontBuilder; + } + + private static Font.Builder preprocessKern(Font srcFont) { + Font.Builder fontBuilder = stripTags(srcFont, ImmutableSet.of(Tag.kern)); + if (srcFont.hasTable(Tag.kern)) { + addTableBytes(fontBuilder, Tag.intValue(new byte[] {'k', 'e', 'r', 'z'}), + toBytes(KernEncoder.encode(srcFont))); + } + return fontBuilder; + } + + private static void addTableBytes(Font.Builder fontBuilder, int tag, byte[] contents) { + fontBuilder.newTableBuilder(tag, ReadableFontData.createReadableFontData(contents)); + } + + private static byte[] fontToBytes(FontFactory fontFactory, Font font) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + fontFactory.serializeFont(font, baos); + return baos.toByteArray(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static byte[] toBytes(ReadableFontData rfd) { + byte[] result = new byte[rfd.length()]; + rfd.readBytes(0, result, 0, rfd.length()); + return result; + } + + // This is currently implemented by shelling out to a command line helper, + // which is fine for research purposes, but obviously problematic for + // production. + private static byte[] compressBzip2(byte[] input) { + try { + String[] args = {"/bin/bzip2"}; + CommandResult result = new Command(args).execute(input); + return result.getStdout(); + } catch (CommandException e) { + throw new RuntimeException(e); + } + } + + /** + * Does one experimental compression on a font, using the string to guide what + * gets done. + * + * @param srcFont Source font + * @param desc experiment description string; the exact format is probably + * still evolving + * @return serialization of compressed font + * @throws IOException + */ + private static byte[] runExperiment(Font srcFont, String desc) throws IOException { + Font font = srcFont; + FontFactory fontFactory = FontFactory.getInstance(); + String[] pieces = desc.split(":"); + boolean keepDsig = false; + for (int i = 0; i < pieces.length - 1; i++) { + String[] piece = pieces[i].split("/"); + String cmd = piece[0]; + if (cmd.equals("glyf")) { + font = preprocessMtxGlyf(font, piece.length > 1 ? piece[1] : "").build(); + } else if (cmd.equals("hmtx")) { + font = preprocessHmtx(font).build(); + } else if (cmd.equals("hdmx")) { + font = preprocessHdmx(font).build(); + } else if (cmd.equals("cmap")) { + font = preprocessCmap(font).build(); + } else if (cmd.equals("kern")) { + font = preprocessKern(font).build(); + } else if (cmd.equals("keepdsig")) { + keepDsig = true; + } else if (cmd.equals("strip")) { + Set removeTags = Sets.newTreeSet(); + for (String tag : piece[1].split(",")) { + removeTags.add(Tag.intValue(tag.getBytes())); + } + font = stripTags(font, removeTags).build(); + } + } + if (!keepDsig) { + font = stripTags(font, ImmutableSet.of(Tag.DSIG)).build(); + } + String last = pieces[pieces.length - 1]; + String[] lastPieces = last.split("/"); + String lastBase = lastPieces[0]; + String lastArgs = lastPieces.length > 1 ? lastPieces[1] : ""; + if (!lastBase.equals("woff2")) { + Set tagsToStrip = Sets.newHashSet(); + for (Entry mapping : Woff2Writer.getTransformMap().entrySet()) { + if (font.hasTable(mapping.getValue())) { + tagsToStrip.add(mapping.getKey()); + } + } + font = stripTags(font, tagsToStrip).build(); + } + byte[] result = null; + if (lastBase.equals("gzip")) { + result = GzipUtil.deflate(fontToBytes(fontFactory, font)); + } else if (lastBase.equals("lzma")) { + result = CompressLzma.compress(fontToBytes(fontFactory, font)); + } else if (lastBase.equals("bzip2")) { + result = compressBzip2(fontToBytes(fontFactory, font)); + } else if (lastBase.equals("woff")) { + result = toBytes(new WoffWriter().convert(font)); + } else if (lastBase.equals("woff2")) { + result = toBytes(new Woff2Writer(lastArgs).convert(srcFont, font)); + } else if (lastBase.equals("eot")) { + result = toBytes(new EOTWriter(true).convert(font)); + } else if (lastBase.equals("uncomp")) { + result = fontToBytes(fontFactory, font); + } + return result; + } +} diff --git a/src/com/google/typography/font/compression/GlyfEncoder.java b/src/com/google/typography/font/compression/GlyfEncoder.java new file mode 100644 index 0000000..ebaa80e --- /dev/null +++ b/src/com/google/typography/font/compression/GlyfEncoder.java @@ -0,0 +1,481 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package com.google.typography.font.compression; + +import com.google.typography.font.sfntly.Font; +import com.google.typography.font.sfntly.Tag; +import com.google.typography.font.sfntly.data.ReadableFontData; +import com.google.typography.font.sfntly.table.core.FontHeaderTable; +import com.google.typography.font.sfntly.table.truetype.CompositeGlyph; +import com.google.typography.font.sfntly.table.truetype.Glyph; +import com.google.typography.font.sfntly.table.truetype.GlyphTable; +import com.google.typography.font.sfntly.table.truetype.LocaTable; +import com.google.typography.font.sfntly.table.truetype.SimpleGlyph; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Raph Levien + * + * Implementation of compression of CTF glyph data, as per sections 5.6-5.10 and 6 of the spec. + * This is a hacked-up version with a number of options, for experimenting. + */ +public class GlyfEncoder { + + private final ByteArrayOutputStream nContourStream; + private final ByteArrayOutputStream nPointsStream; + private final ByteArrayOutputStream flagBytesStream; + private final ByteArrayOutputStream compositeStream; + private final ByteArrayOutputStream bboxStream; + private final ByteArrayOutputStream glyfStream; + private final ByteArrayOutputStream pushStream; + private final ByteArrayOutputStream codeStream; + private final boolean sbbox; + private final boolean cbbox; + private final boolean code; + private final boolean triplet; + private final boolean doPush; + private final boolean doHop; + private final boolean push2byte; + private final boolean reslice; + + private int nGlyphs; + private byte[] bboxBitmap; + private FontHeaderTable.IndexToLocFormat indexFmt; + + public GlyfEncoder(String options) { + glyfStream = new ByteArrayOutputStream(); + pushStream = new ByteArrayOutputStream(); + codeStream = new ByteArrayOutputStream(); + nContourStream = new ByteArrayOutputStream(); + nPointsStream = new ByteArrayOutputStream(); + flagBytesStream = new ByteArrayOutputStream(); + compositeStream = new ByteArrayOutputStream(); + bboxStream = new ByteArrayOutputStream(); + boolean sbbox = false; + boolean cbbox = false; + boolean code = false; + boolean triplet = false; + boolean doPush = false; + boolean reslice = false; + boolean doHop = false; + boolean push2byte = false; + for (String option : options.split(",")) { + if (option.equals("sbbox")) { + sbbox = true; + } else if (option.equals("cbbox")) { + cbbox = true; + } else if (option.equals("code")) { + code = true; + } else if (option.equals("triplet")) { + triplet = true; + } else if (option.equals("push")) { + doPush = true; + } else if (option.equals("hop")) { + doHop = true; + } else if (option.equals("push2byte")) { + push2byte = true; + } else if (option.equals("reslice")) { + reslice = true; + } + } + this.sbbox = sbbox; + this.cbbox = cbbox; + this.code = code; + this.triplet = triplet; + this.doPush = doPush; + this.doHop = doHop; + this.push2byte = push2byte; + this.reslice = reslice; + } + + public void encode(Font sourceFont) { + FontHeaderTable head = sourceFont.getTable(Tag.head); + indexFmt = head.indexToLocFormat(); + LocaTable loca = sourceFont.getTable(Tag.loca); + nGlyphs = loca.numGlyphs(); + GlyphTable glyf = sourceFont.getTable(Tag.glyf); + bboxBitmap = new byte[((nGlyphs + 31) >> 5) << 2]; + + for (int glyphId = 0; glyphId < nGlyphs; glyphId++) { + int sourceOffset = loca.glyphOffset(glyphId); + int length = loca.glyphLength(glyphId); + Glyph glyph = glyf.glyph(sourceOffset, length); + writeGlyph(glyphId, glyph); + } + } + + private void writeGlyph(int glyphId, Glyph glyph) { + try { + if (glyph == null || glyph.dataLength() == 0) { + writeNContours(0); + } else if (glyph instanceof SimpleGlyph) { + writeSimpleGlyph(glyphId, (SimpleGlyph)glyph); + } else if (glyph instanceof CompositeGlyph) { + writeCompositeGlyph(glyphId, (CompositeGlyph)glyph); + } + } catch (IOException e) { + throw new RuntimeException("unexpected IOException writing glyph data", e); + } + } + + private void writeInstructions(Glyph glyph) throws IOException{ + if (doPush) { + splitPush(glyph); + } else { + int pushCount = 0; + int codeSize = glyph.instructionSize(); + if (!reslice) { + write255UShort(glyfStream, pushCount); + } + write255UShort(glyfStream, codeSize); + if (codeSize > 0) { + if (code) { + glyph.instructions().copyTo(codeStream); + } else { + glyph.instructions().copyTo(glyfStream); + } + } + } + } + + private void writeSimpleGlyph(int glyphId, SimpleGlyph glyph) throws IOException { + int numContours = glyph.numberOfContours(); + writeNContours(numContours); + if (sbbox) { + writeBbox(glyphId, glyph); + } + // TODO: check that bbox matches, write bbox if not + for (int i = 0; i < numContours; i++) { + if (reslice) { + write255UShort(nPointsStream, glyph.numberOfPoints(i)); + } else { + write255UShort(glyfStream, glyph.numberOfPoints(i) - (i == 0 ? 1 : 0)); + } + } + ByteArrayOutputStream os = new ByteArrayOutputStream(); + int lastX = 0; + int lastY = 0; + for (int i = 0; i < numContours; i++) { + int numPoints = glyph.numberOfPoints(i); + for (int j = 0; j < numPoints; j++) { + int x = glyph.xCoordinate(i, j); + int y = glyph.yCoordinate(i, j); + int dx = x - lastX; + int dy = y - lastY; + if (triplet) { + writeTriplet(os, glyph.onCurve(i, j), dx, dy); + } else { + writeVShort(os, dx * 2 + (glyph.onCurve(i, j) ? 1 : 0)); + writeVShort(os, dy); + } + lastX = x; + lastY = y; + } + } + os.writeTo(glyfStream); + if (numContours > 0) { + writeInstructions(glyph); + } + } + + private void writeCompositeGlyph(int glyphId, CompositeGlyph glyph) throws IOException { + boolean haveInstructions = false; + writeNContours(-1); + if (cbbox) { + writeBbox(glyphId, glyph); + } + ByteArrayOutputStream outStream = reslice ? compositeStream : glyfStream; + for (int i = 0; i < glyph.numGlyphs(); i++) { + int flags = glyph.flags(i); + writeUShort(outStream, flags); + haveInstructions = (flags & CompositeGlyph.FLAG_WE_HAVE_INSTRUCTIONS) != 0; + writeUShort(outStream, glyph.glyphIndex(i)); + if ((flags & CompositeGlyph.FLAG_ARG_1_AND_2_ARE_WORDS) == 0) { + outStream.write(glyph.argument1(i)); + outStream.write(glyph.argument2(i)); + } else { + writeUShort(outStream, glyph.argument1(i)); + writeUShort(outStream, glyph.argument2(i)); + } + if (glyph.transformationSize(i) != 0) { + try { + outStream.write(glyph.transformation(i)); + } catch (IOException e) { + } + } + } + if (haveInstructions) { + writeInstructions(glyph); + } + } + + private void writeNContours(int nContours) { + if (reslice) { + writeUShort(nContourStream, nContours); + } else { + writeUShort(nContours); + } + } + + private void writeBbox(int glyphId, Glyph glyph) { + if (reslice) { + bboxBitmap[glyphId >> 3] |= 0x80 >> (glyphId & 7); + } + ByteArrayOutputStream outStream = reslice ? bboxStream : glyfStream; + writeUShort(outStream, glyph.xMin()); + writeUShort(outStream, glyph.yMin()); + writeUShort(outStream, glyph.xMax()); + writeUShort(outStream, glyph.yMax()); + } + + private void writeUShort(ByteArrayOutputStream os, int value) { + os.write(value >> 8); + os.write(value & 255); + } + + private void writeUShort(int value) { + writeUShort(glyfStream, value); + } + + private void writeLong(OutputStream os, int value) throws IOException { + os.write((value >> 24) & 255); + os.write((value >> 16) & 255); + os.write((value >> 8) & 255); + os.write(value & 255); + } + + // As per 6.1.1 of spec + // visible for testing + static void write255UShort(ByteArrayOutputStream os, int value) { + if (value < 0) { + throw new IllegalArgumentException(); + } + if (value < 253) { + os.write((byte)value); + } else if (value < 506) { + os.write(255); + os.write((byte)(value - 253)); + } else if (value < 762) { + os.write(254); + os.write((byte)(value - 506)); + } else { + os.write(253); + os.write((byte)(value >> 8)); + os.write((byte)(value & 0xff)); + } + } + + // As per 6.1.1 of spec + // visible for testing + static void write255Short(OutputStream os, int value) throws IOException { + int absValue = Math.abs(value); + if (value < 0) { + // spec is unclear about whether words should be signed. This code is conservative, but we + // can test once the implementation is working. + os.write(250); + } + if (absValue < 250) { + os.write((byte)absValue); + } else if (absValue < 500) { + os.write(255); + os.write((byte)(absValue - 250)); + } else if (absValue < 756) { + os.write(254); + os.write((byte)(absValue - 500)); + } else { + os.write(253); + os.write((byte)(absValue >> 8)); + os.write((byte)(absValue & 0xff)); + } + } + + // A simple signed varint encoding + static void writeVShort(ByteArrayOutputStream os, int value) { + if (value >= 0x2000 || value < -0x2000) { + os.write((byte)(0x80 | ((value >> 14) & 0x7f))); + } + if (value >= 0x40 || value < -0x40) { + os.write((byte)(0x80 | ((value >> 7) & 0x7f))); + } + os.write((byte)(value & 0x7f)); + } + + // As in section 5.11 of the spec + // visible for testing + void writeTriplet(OutputStream os, boolean onCurve, int x, int y) throws IOException { + int absX = Math.abs(x); + int absY = Math.abs(y); + int onCurveBit = onCurve ? 0 : 128; + int xSignBit = (x < 0) ? 0 : 1; + int ySignBit = (y < 0) ? 0 : 1; + int xySignBits = xSignBit + 2 * ySignBit; + ByteArrayOutputStream flagStream = reslice ? flagBytesStream : glyfStream; + + if (x == 0 && absY < 1280) { + flagStream.write(onCurveBit + ((absY & 0xf00) >> 7) + ySignBit); + os.write(absY & 0xff); + } else if (y == 0 && absX < 1280) { + flagStream.write(onCurveBit + 10 + ((absX & 0xf00) >> 7) + xSignBit); + os.write(absX & 0xff); + } else if (absX < 65 && absY < 65) { + flagStream.write(onCurveBit + 20 + ((absX - 1) & 0x30) + (((absY - 1) & 0x30) >> 2) + + xySignBits); + os.write((((absX - 1) & 0xf) << 4) | ((absY - 1) & 0xf)); + } else if (absX < 769 && absY < 769) { + flagStream.write(onCurveBit + 84 + 12 * (((absX - 1) & 0x300) >> 8) + + (((absY - 1) & 0x300) >> 6) + xySignBits); + os.write((absX - 1) & 0xff); + os.write((absY - 1) & 0xff); + } else if (absX < 4096 && absY < 4096) { + flagStream.write(onCurveBit + 120 + xySignBits); + os.write(absX >> 4); + os.write(((absX & 0xf) << 4) | (absY >> 8)); + os.write(absY & 0xff); + } else { + flagStream.write(onCurveBit + 124 + xySignBits); + os.write(absX >> 8); + os.write(absX & 0xff); + os.write(absY >> 8); + os.write(absY & 0xff); + } + } + + /** + * Split the instructions into a push sequence and the remainder of the instructions. + * Writes both streams, and the counts to the glyfStream. + * + * @param glyph + */ + private void splitPush(Glyph glyph) throws IOException { + int instrSize = glyph.instructionSize(); + ReadableFontData data = glyph.instructions(); + int i = 0; + List result = new ArrayList(); + // All push sequences are at least two bytes, make sure there's enough room + while (i + 1 < instrSize) { + int ix = i; + int instr = data.readUByte(ix++); + int n = 0; + int size = 0; + if (instr == 0x40 || instr == 0x41) { + // NPUSHB, NPUSHW + n = data.readUByte(ix++); + size = (instr & 1) + 1; + } else if (instr >= 0xB0 && instr < 0xC0) { + // PUSHB, PUSHW + n = 1 + (instr & 7); + size = ((instr & 8) >> 3) + 1; + } else { + break; + } + if (i + size * n > instrSize) { + // This is a broken font, and a potential buffer overflow, but in the interest + // of preserving the original, we just put the broken instruction sequence in + // the stream. + break; + } + for (int j = 0; j < n; j++) { + if (size == 1) { + result.add(data.readUByte(ix)); + } else { + result.add(data.readShort(ix)); + } + ix += size; + } + i = ix; + } + int pushCount = result.size(); + int codeSize = instrSize - i; + write255UShort(glyfStream, pushCount); + write255UShort(glyfStream, codeSize); + encodePushSequence(pushStream, result); + if (codeSize > 0) { + data.slice(i).copyTo(codeStream); + } + } + + // As per section 6.2.2 of the spec + private void encodePushSequence(ByteArrayOutputStream os, List data) throws IOException { + int n = data.size(); + int hopSkip = 0; + for (int i = 0; i < n; i++) { + if ((hopSkip & 1) == 0) { + int val = data.get(i); + if (doHop && hopSkip == 0 && i >= 2 && + i + 2 < n && val == data.get(i - 2) && val == data.get(i + 2)) { + if (i + 4 < n && val == data.get(i + 4)) { + // Hop4 code + os.write(252); + hopSkip = 0x14; + } else { + // Hop3 code + os.write(251); + hopSkip = 4; + } + } else { + if (push2byte) { + // Measure relative effectiveness of 255Short literal encoding vs 2-byte ushort. + writeUShort(os, data.get(i)); + } else { + write255Short(os, data.get(i)); + } + } + } + hopSkip >>= 1; + } + } + + public byte[] getGlyfBytes() { + if (reslice) { + ByteArrayOutputStream newStream = new ByteArrayOutputStream(); + try { + // Pack all the glyf streams in a sensible way + writeLong(newStream, 0); // version + writeUShort(newStream, nGlyphs); + writeUShort(newStream, indexFmt.value()); + writeLong(newStream, nContourStream.size()); + writeLong(newStream, nPointsStream.size()); + writeLong(newStream, flagBytesStream.size()); + writeLong(newStream, glyfStream.size()); + writeLong(newStream, compositeStream.size()); + writeLong(newStream, bboxBitmap.length + bboxStream.size()); + writeLong(newStream, codeStream.size()); + System.out.printf("stream sizes = %d %d %d %d %d %d %d\n", + nContourStream.size(), nPointsStream.size(), flagBytesStream.size(), glyfStream.size(), + compositeStream.size(), bboxStream.size(), codeStream.size()); + nContourStream.writeTo(newStream); + nPointsStream.writeTo(newStream); + flagBytesStream.writeTo(newStream); + glyfStream.writeTo(newStream); + compositeStream.writeTo(newStream); + newStream.write(bboxBitmap); + bboxStream.writeTo(newStream); + codeStream.writeTo(newStream); + } catch (IOException e) { + throw new RuntimeException("Can't happen, world must have come to end", e); + } + return newStream.toByteArray(); + } else { + return glyfStream.toByteArray(); + } + } + + public byte[] getPushBytes() { + return pushStream.toByteArray(); + } + + public byte[] getCodeBytes() { + return codeStream.toByteArray(); + } + + public byte[] getLocaBytes() { + return new byte[]{ }; + } +} diff --git a/src/com/google/typography/font/compression/GzipUtil.java b/src/com/google/typography/font/compression/GzipUtil.java new file mode 100644 index 0000000..af04b8a --- /dev/null +++ b/src/com/google/typography/font/compression/GzipUtil.java @@ -0,0 +1,28 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package com.google.typography.font.compression; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; + +/** + * Simple utility for GZIP compression + */ +public class GzipUtil { + public static byte[] deflate(byte[] bytes) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DeflaterOutputStream dos = new DeflaterOutputStream(baos, new Deflater()); + dos.write(bytes, 0, bytes.length); + dos.close(); + return baos.toByteArray(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} + diff --git a/src/com/google/typography/font/compression/KernEncoder.java b/src/com/google/typography/font/compression/KernEncoder.java new file mode 100644 index 0000000..c3df4c7 --- /dev/null +++ b/src/com/google/typography/font/compression/KernEncoder.java @@ -0,0 +1,37 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package com.google.typography.font.compression; + +import com.google.typography.font.sfntly.Font; +import com.google.typography.font.sfntly.Tag; +import com.google.typography.font.sfntly.data.ReadableFontData; +import com.google.typography.font.sfntly.data.WritableFontData; +import com.google.typography.font.sfntly.table.Table; + +/** + * @author raph@google.com (Raph Levien) + * + * Encoder for "kern" table. This probably won't go in the spec because an even more + * effective technique would be to do class kerning in the GDEF tables, but, even so, I wanted + * to capture the stats. + */ +public class KernEncoder { + public static WritableFontData encode(Font font) { + Table kernTable = font.getTable(Tag.kern); + ReadableFontData data = kernTable.readFontData(); + WritableFontData newData = WritableFontData.createWritableFontData(data.size()); + data.copyTo(newData); + if (data.readUShort(0) == 0 && data.readUShort(4) == 0) { + int base = 18; + int nPairs = data.readUShort(10); + for (int i = 0; i < nPairs; i++) { + newData.writeUShort(base + i * 2, data.readUShort(base + i * 6)); + newData.writeUShort(base + nPairs * 2 + i * 2, data.readUShort(base + i * 6 + 2)); + newData.writeUShort(base + nPairs * 4 + i * 2, data.readUShort(base + i * 6 + 4)); + } + } + return newData; + } +} diff --git a/src/com/google/typography/font/compression/StatsCollector.java b/src/com/google/typography/font/compression/StatsCollector.java new file mode 100644 index 0000000..6eb7154 --- /dev/null +++ b/src/com/google/typography/font/compression/StatsCollector.java @@ -0,0 +1,92 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package com.google.typography.font.compression; + +import com.google.common.collect.Lists; + +import java.io.PrintWriter; +import java.util.Collections; +import java.util.List; + +/** + * Class for gathering up stats, for summarizing and graphing. + * + * @author raph@google.com (Raph Levien) + */ +public class StatsCollector { + private final List values; + + public StatsCollector() { + values = Lists.newArrayList(); + } + + public void addStat(double value) { + values.add(value); + } + + public double mean() { + double sum = 0; + for (Double value : values) { + sum += value; + } + return sum / values.size(); + } + + public double median() { + Collections.sort(values); + int length = values.size(); + if (length % 2 == 1) { + return values.get((length - 1) / 2); + } else { + return 0.5 * (values.get(length / 2 - 1) + values.get(length / 2)); + } + } + + // Need to print before calling this method + public void chartHeader(PrintWriter o, int n) { + o.println(""); + o.println(""); + o.println(""); + o.println(""); + + o.println(); + o.println(""); + o.println("
"); + // TODO: split so we can get content into the HTML + } + public void chartFooter(PrintWriter o) { + o.println(""); + o.println(""); + } +} diff --git a/src/com/google/typography/font/compression/TestCommand.java b/src/com/google/typography/font/compression/TestCommand.java new file mode 100644 index 0000000..d527f6b --- /dev/null +++ b/src/com/google/typography/font/compression/TestCommand.java @@ -0,0 +1,28 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package com.google.typography.font.compression; + +import java.io.IOException; + +/** + * A simple test for the command mechanism. Quick and dirty run with: + * java -cp 'build/classes:lib/guava-11.0.1.jar' com/google/typography/font/compression/TestCommand + */ +public class TestCommand { + public static void main(String[] args) throws IOException { + String[] commandArgs = {"/usr/bin/lzma"}; + byte[] input = new byte[16384]; + try { + CommandResult result = new Command(commandArgs).execute(input); + byte[] output = result.getStdout(); + for (int i = 0; i < output.length; i++) { + System.out.printf("%02x\n", output[i] & 0xff); + } + } catch (CommandException e) { + e.printStackTrace(); + }; + } +} + diff --git a/src/com/google/typography/font/compression/Woff2Writer.java b/src/com/google/typography/font/compression/Woff2Writer.java new file mode 100644 index 0000000..7f51d0f --- /dev/null +++ b/src/com/google/typography/font/compression/Woff2Writer.java @@ -0,0 +1,352 @@ +// Copyright 2012 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package com.google.typography.font.compression; + +import com.google.common.collect.ImmutableBiMap; +import com.google.common.collect.Lists; +import com.google.typography.font.sfntly.Font; +import com.google.typography.font.sfntly.Tag; +import com.google.typography.font.sfntly.data.WritableFontData; +import com.google.typography.font.sfntly.table.Table; +import com.google.typography.font.sfntly.table.core.FontHeaderTable; + +import java.util.List; +import java.util.TreeSet; + +public class Woff2Writer { + private static final long SIGNATURE = 0x774f4632; + private static final int WOFF2_HEADER_SIZE = 44; + private static final int TABLE_ENTRY_SIZE = 5 * 4; + private static final int FLAG_CONTINUE_STREAM = 1 << 4; + private static final int FLAG_APPLY_TRANSFORM = 1 << 5; + + private final CompressionType compressionType; + private final boolean longForm; + + Woff2Writer(String args) { + CompressionType compressionType = CompressionType.NONE; + boolean longForm = false; + for (String arg : args.split(",")) { + if ("lzma".equals(arg)) { + compressionType = CompressionType.LZMA; + } else if ("gzip".equals(arg)) { + compressionType = CompressionType.GZIP; + } else if ("short".equals(arg)) { + longForm = false; + } else if ("long".equals(arg)) { + longForm = true; + } + } + this.compressionType = compressionType; + this.longForm = longForm; + } + + private static ImmutableBiMap TRANSFORM_MAP = ImmutableBiMap.of( + Tag.glyf, Tag.intValue(new byte[] {'g', 'l', 'z', '1'}), + Tag.loca, Tag.intValue(new byte[] {'l', 'o', 'c', 'z'}) + ); + + public static ImmutableBiMap getTransformMap() { + return TRANSFORM_MAP; + } + + private static ImmutableBiMap KNOWN_TABLES = + new ImmutableBiMap.Builder() + .put(Tag.intValue(new byte[] {'c', 'm', 'a', 'p'}), 0) + .put(Tag.intValue(new byte[] {'h', 'e', 'a', 'd'}), 1) + .put(Tag.intValue(new byte[] {'h', 'h', 'e', 'a'}), 2) + .put(Tag.intValue(new byte[] {'h', 'm', 't', 'x'}), 3) + .put(Tag.intValue(new byte[] {'m', 'a', 'x', 'p'}), 4) + .put(Tag.intValue(new byte[] {'n', 'a', 'm', 'e'}), 5) + .put(Tag.intValue(new byte[] {'O', 'S', '/', '2'}), 6) + .put(Tag.intValue(new byte[] {'p', 'o', 's', 't'}), 7) + .put(Tag.intValue(new byte[] {'c', 'v', 't', ' '}), 8) + .put(Tag.intValue(new byte[] {'f', 'p', 'g', 'm'}), 9) + .put(Tag.intValue(new byte[] {'g', 'l', 'y', 'f'}), 10) + .put(Tag.intValue(new byte[] {'l', 'o', 'c', 'a'}), 11) + .put(Tag.intValue(new byte[] {'p', 'r', 'e', 'p'}), 12) + .put(Tag.intValue(new byte[] {'C', 'F', 'F', ' '}), 13) + .put(Tag.intValue(new byte[] {'V', 'O', 'R', 'G'}), 14) + .put(Tag.intValue(new byte[] {'E', 'B', 'D', 'T'}), 15) + .put(Tag.intValue(new byte[] {'E', 'B', 'L', 'C'}), 16) + .put(Tag.intValue(new byte[] {'g', 'a', 's', 'p'}), 17) + .put(Tag.intValue(new byte[] {'h', 'd', 'm', 'x'}), 18) + .put(Tag.intValue(new byte[] {'k', 'e', 'r', 'n'}), 19) + .put(Tag.intValue(new byte[] {'L', 'T', 'S', 'H'}), 20) + .put(Tag.intValue(new byte[] {'P', 'C', 'L', 'T'}), 21) + .put(Tag.intValue(new byte[] {'V', 'D', 'M', 'X'}), 22) + .put(Tag.intValue(new byte[] {'v', 'h', 'e', 'a'}), 23) + .put(Tag.intValue(new byte[] {'v', 'm', 't', 'x'}), 24) + .put(Tag.intValue(new byte[] {'B', 'A', 'S', 'E'}), 25) + .put(Tag.intValue(new byte[] {'G', 'D', 'E', 'F'}), 26) + .put(Tag.intValue(new byte[] {'G', 'P', 'O', 'S'}), 27) + .put(Tag.intValue(new byte[] {'G', 'S', 'U', 'B'}), 28) + .build(); + + public WritableFontData convert(Font srcFont, Font font) { + List entries = createTableDirectoryEntries(font); + int size = computeCompressedFontSize(entries); + WritableFontData writableFontData = WritableFontData.createWritableFontData(size); + int index = 0; + FontHeaderTable head = font.getTable(Tag.head); + index += writeWoff2Header(writableFontData, entries, font.sfntVersion(), size, + head.fontRevision()); + System.out.printf("Wrote header, index = %d\n", index); + index += writeDirectory(writableFontData, index, entries); + System.out.printf("Wrote directory, index = %d\n", index); + index += writeTables(writableFontData, index, entries); + System.out.printf("Wrote tables, index = %d\n", index); + return writableFontData; + } + + private List createTableDirectoryEntries(Font font) { + List entries = Lists.newArrayList(); + TreeSet tags = new TreeSet(font.tableMap().keySet()); + + for (int tag : tags) { + Table table = font.getTable(tag); + byte[] uncompressedBytes = bytesFromTable(table); + byte[] transformedBytes = null; + if (TRANSFORM_MAP.containsValue(tag)) { + // Don't store the intermediate transformed tables under the nonstandard tags. + continue; + } + if (TRANSFORM_MAP.containsKey(tag)) { + int transformedTag = TRANSFORM_MAP.get(tag); + Table transformedTable = font.getTable(transformedTag); + if (transformedTable != null) { + transformedBytes = bytesFromTable(transformedTable); + } + } + if (transformedBytes == null) { + entries.add(new TableDirectoryEntry(tag, uncompressedBytes, compressionType)); + } else { + entries.add(new TableDirectoryEntry(tag, uncompressedBytes, transformedBytes, + FLAG_APPLY_TRANSFORM, compressionType)); + } + } + return entries; + } + + private byte[] bytesFromTable(Table table) { + int length = table.dataLength(); + byte[] bytes = new byte[length]; + table.readFontData().readBytes(0, bytes, 0, length); + return bytes; + } + + private int writeWoff2Header(WritableFontData writableFontData, + List entries, + int flavor, + int length, + int version) { + int index = 0; + index += writableFontData.writeULong(index, SIGNATURE); + index += writableFontData.writeULong(index, flavor); + index += writableFontData.writeULong(index, length); + index += writableFontData.writeUShort(index, entries.size()); // numTables + index += writableFontData.writeUShort(index, 0); // reserved + int uncompressedFontSize = computeUncompressedSize(entries); + index += writableFontData.writeULong(index, uncompressedFontSize); + index += writableFontData.writeFixed(index, version); + index += writableFontData.writeULong(index, 0); // metaOffset + index += writableFontData.writeULong(index, 0); // metaLength + index += writableFontData.writeULong(index, 0); // metaOrigLength + index += writableFontData.writeULong(index, 0); // privOffset + index += writableFontData.writeULong(index, 0); // privLength + return index; + } + + private int writeDirectory(WritableFontData writableFontData, int offset, + List entries) { + int directorySize = computeDirectoryLength(entries); + for (TableDirectoryEntry entry : entries) { + offset += entry.writeEntry(writableFontData, offset); + } + return directorySize; + } + + private int writeTables(WritableFontData writableFontData, int offset, + List entries) { + int start = offset; + for (TableDirectoryEntry entry : entries) { + offset += entry.writeData(writableFontData, offset); + offset = align4(offset); + } + return offset - start; + } + + private int computeDirectoryLength(List entries) { + if (longForm) { + return TABLE_ENTRY_SIZE * entries.size(); + } else { + int size = 0; + for (TableDirectoryEntry entry : entries) { + size += entry.writeEntry(null, size); + } + return size; + } + } + + private int align4(int value) { + return (value + 3) & -4; + } + + private int computeUncompressedSize(List entries) { + int size = 20 + 16 * entries.size(); // sfnt header length + for (TableDirectoryEntry entry : entries) { + size += entry.getOrigLength(); + size = align4(size); + } + return size; + } + + private int computeCompressedFontSize(List entries) { + int fontSize = WOFF2_HEADER_SIZE; + fontSize += computeDirectoryLength(entries); + for (TableDirectoryEntry entry : entries) { + fontSize += entry.getCompLength(); + fontSize = align4(fontSize); + } + return fontSize; + } + + private enum CompressionType { + NONE, GZIP, LZMA + } + + private static long flagsForCompression(CompressionType compressionType) { + switch (compressionType) { + case NONE: + return 0; + case GZIP: + return 1; + case LZMA: + return 2; + } + return 0; + } + + private static byte[] compress(byte[] input, CompressionType compressionType) { + switch (compressionType) { + case NONE: + return input; + case GZIP: + return GzipUtil.deflate(input); + case LZMA: + return CompressLzma.compress(input); + } + return null; + } + + // Note: if writableFontData is null, just return the size + private static int writeBase128(WritableFontData writableFontData, long value, int offset) { + int size = 1; + long tmpValue = value; + while (tmpValue >= 128) { + size += 1; + tmpValue = tmpValue >> 7; + } + for (int i = 0; i < size; i++) { + int b = (int)(value >> (7 * (size - i - 1))) & 0x7f; + if (i < size - 1) { + b |= 0x80; + } + if (writableFontData != null) { + writableFontData.writeByte(offset, (byte)b); + } + offset += 1; + } + return size; + } + + private class TableDirectoryEntry { + private final long tag; + private final long flags; + private final long origLength; + private final long transformLength; + private final byte[] bytes; + + // This is the constructor for tables that don't have transforms + public TableDirectoryEntry(long tag, byte[] uncompressedBytes, + CompressionType compressionType) { + this(tag, uncompressedBytes, uncompressedBytes, 0, compressionType); + } + + public TableDirectoryEntry(long tag, byte[] uncompressedBytes, byte[] transformedBytes, + long transformFlags, CompressionType compressionType) { + byte[] compressedBytes = compress(transformedBytes, compressionType); + if (compressedBytes.length >= transformedBytes.length) { + compressedBytes = transformedBytes; + compressionType = CompressionType.NONE; + } + this.tag = tag; + this.flags = transformFlags | flagsForCompression(compressionType); + this.origLength = uncompressedBytes.length; + this.transformLength = transformedBytes.length; + this.bytes = compressedBytes; + } + + public long getOrigLength() { + return origLength; + } + + public long getCompLength() { + return bytes.length; + } + + // Note: if writableFontData is null, just return the size + public int writeEntry(WritableFontData writableFontData, int offset) { + if (longForm) { + if (writableFontData != null) { + offset += writableFontData.writeULong(offset, tag); + offset += writableFontData.writeULong(offset, flags); + offset += writableFontData.writeULong(offset, getCompLength()); + offset += writableFontData.writeULong(offset, transformLength); + offset += writableFontData.writeULong(offset, getOrigLength()); + } + return TABLE_ENTRY_SIZE; + } else { + int start = offset; + int flag_byte = 0x1f; + if (KNOWN_TABLES.containsKey((int)tag)) { + flag_byte = KNOWN_TABLES.get((int)tag); + } + if ((flags & FLAG_APPLY_TRANSFORM) != 0) { + flag_byte |= 0x20; + } + if ((flags & FLAG_CONTINUE_STREAM) != 0) { + flag_byte |= 0xc0; + } else { + flag_byte |= (flags & 3) << 6; + } + if (writableFontData != null) { + System.out.printf("%d: tag = %08x, flag = %02x\n", offset, tag, flag_byte); + writableFontData.writeByte(offset, (byte)flag_byte); + } + offset += 1; + if ((flag_byte & 0x1f) == 0x1f) { + if (writableFontData != null) { + writableFontData.writeULong(offset, tag); + } + offset += 4; + } + offset += writeBase128(writableFontData, getOrigLength(), offset); + if ((flag_byte & 0x20) != 0) { + offset += writeBase128(writableFontData, transformLength, offset); + } + if ((flag_byte & 0xc0) == 0x40 || (flag_byte & 0xc0) == 0x80) { + offset += writeBase128(writableFontData, getCompLength(), offset); + } + return offset - start; + } + } + + public int writeData(WritableFontData writableFontData, int offset) { + writableFontData.writeBytes(offset, bytes); + return bytes.length; + } + } +} diff --git a/woff2_header_dump.py b/woff2_header_dump.py new file mode 100644 index 0000000..b352d50 --- /dev/null +++ b/woff2_header_dump.py @@ -0,0 +1,38 @@ +# Copyright (c) 2012 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# This is a simple utility for dumping out the header of a compressed file, and +# is suitable for doing spot checks of compressed. files. However, this only +# implements the "long" form of the table directory. + +import struct +import sys + +def dump_woff2_header(header): + header_values = struct.unpack('>IIIHHIHHIIIII', header[:44]) + for i, key in enumerate([ + 'signature', + 'flavor', + 'length', + 'numTables', + 'reserved', + 'totalSfntSize', + 'majorVersion', + 'minorVersion', + 'metaOffset', + 'metaOrigLength', + 'privOffset', + 'privLength']): + print key, header_values[i] + numTables = header_values[3] + for i in range(numTables): + entry = struct.unpack('>IIIII', header[44+20*i:44+20*(i+1)]) + print '%08x %d %d %d %d' % entry + +def main(): + header = file(sys.argv[1]).read() + dump_woff2_header(header) + +main() +