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 <desc>
+
+Sets the baseline experiment description.
+
+[ -x <desc> ]...
+
+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 @@
+<project name="compression" default="jar">
+ <target name="clean">
+ <delete dir="build/classes" />
+ <delete dir="build/jar" />
+ </target>
+
+ <target name="compile">
+ <mkdir dir="build/classes" />
+ <javac srcdir="src" destdir="build/classes" includeantruntime="false">
+ <compilerarg value="-Xlint" />
+ <classpath>
+ <fileset dir="lib" includes="*.jar" />
+ </classpath>
+ </javac>
+ </target>
+
+ <target name="jar" depends="compile">
+ <mkdir dir="build/jar" />
+ <jar destfile="build/jar/compression.jar" basedir="build/classes">
+ <zipfileset src="lib/eotconverter.jar" />
+ <zipfileset src="lib/guava-11.0.1.jar" />
+ <zipfileset src="lib/sfntly.jar" />
+ <zipfileset src="lib/woffconverter.jar" />
+ <manifest>
+ <attribute name="Main-Class" value="com.google.typography.font.compression.CompressionRunner" />
+ </manifest>
+ </jar>
+ </target>
+
+</project>
+
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
--- /dev/null
+++ b/build/classes/com/google/typography/font/compression/AdvWidth.class
Binary files 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
--- /dev/null
+++ b/build/classes/com/google/typography/font/compression/CmapEncoder.class
Binary files 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
--- /dev/null
+++ b/build/classes/com/google/typography/font/compression/Command$StreamConsumer.class
Binary files 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
--- /dev/null
+++ b/build/classes/com/google/typography/font/compression/Command.class
Binary files 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
--- /dev/null
+++ b/build/classes/com/google/typography/font/compression/CommandException.class
Binary files 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
--- /dev/null
+++ b/build/classes/com/google/typography/font/compression/CommandResult.class
Binary files 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
--- /dev/null
+++ b/build/classes/com/google/typography/font/compression/CompressLzma.class
Binary files 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
--- /dev/null
+++ b/build/classes/com/google/typography/font/compression/CompressionRunner.class
Binary files 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
--- /dev/null
+++ b/build/classes/com/google/typography/font/compression/GlyfEncoder.class
Binary files 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
--- /dev/null
+++ b/build/classes/com/google/typography/font/compression/GzipUtil.class
Binary files 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
--- /dev/null
+++ b/build/classes/com/google/typography/font/compression/KernEncoder.class
Binary files 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
--- /dev/null
+++ b/build/classes/com/google/typography/font/compression/StatsCollector.class
Binary files 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
--- /dev/null
+++ b/build/classes/com/google/typography/font/compression/TestCommand.class
Binary files 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
--- /dev/null
+++ b/build/classes/com/google/typography/font/compression/Woff2Writer$1.class
Binary files 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
--- /dev/null
+++ b/build/classes/com/google/typography/font/compression/Woff2Writer$CompressionType.class
Binary files 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
--- /dev/null
+++ b/build/classes/com/google/typography/font/compression/Woff2Writer$TableDirectoryEntry.class
Binary files 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
--- /dev/null
+++ b/build/classes/com/google/typography/font/compression/Woff2Writer.class
Binary files differ
diff --git a/build/jar/compression.jar b/build/jar/compression.jar
new file mode 100644
index 0000000..9e2b3ca
--- /dev/null
+++ b/build/jar/compression.jar
Binary files differ
diff --git a/cpp/.SConstruct.swp b/cpp/.SConstruct.swp
new file mode 100644
index 0000000..402a853
--- /dev/null
+++ b/cpp/.SConstruct.swp
Binary files differ
diff --git a/cpp/Inconsolata-Regular.woff2 b/cpp/Inconsolata-Regular.woff2
new file mode 100644
index 0000000..ddfefb3
--- /dev/null
+++ b/cpp/Inconsolata-Regular.woff2
Binary files 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 <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <cstdio>
+#include <cstdlib>
+
+#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 <zlib.h>
+#include <vector>
+#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<Point> *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<Point> &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<Point> &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<uint32_t> &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<uint32_t> &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<std::pair<const uint8_t*, size_t> > 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<uint32_t> loca_values(num_glyphs + 1);
+ std::vector<unsigned int> n_points_vec;
+ std::vector<Point> 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<Table> &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<Table> &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<typename T> T Round4(T value) {
+ if (std::numeric_limits<T>::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<Table> &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<Table> *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<Table> *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<Table> 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<uint8_t> 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
--- /dev/null
+++ b/docs/WOFFUltraCondensedfileformat.pdf
Binary files differ
diff --git a/lib/eotconverter.jar b/lib/eotconverter.jar
new file mode 100644
index 0000000..6543d70
--- /dev/null
+++ b/lib/eotconverter.jar
Binary files differ
diff --git a/lib/guava-11.0.1.jar b/lib/guava-11.0.1.jar
new file mode 100644
index 0000000..af4a383
--- /dev/null
+++ b/lib/guava-11.0.1.jar
Binary files differ
diff --git a/lib/sfntly.jar b/lib/sfntly.jar
new file mode 100644
index 0000000..415d5c5
--- /dev/null
+++ b/lib/sfntly.jar
Binary files differ
diff --git a/lib/woffconverter.jar b/lib/woffconverter.jar
new file mode 100644
index 0000000..1ac0133
--- /dev/null
+++ b/lib/woffconverter.jar
Binary files 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 <arpa/inet.h>
++#include <fcntl.h>
++#include <sys/stat.h>
++#include <unistd.h>
++
++#include <cstdio>
++#include <string>
++#include <vector>
++
++#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<uint8_t>* 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<uint8_t>* input, std::vector<uint8_t>* 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<uint8_t>* input, std::vector<uint8_t>* 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<uint8_t>* 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<uint8_t> in_data;
++ if (!ReadFile(argv[2], &in_data)) {
++ std::fprintf(stderr, "Failed to read file!\n");
++ return 1;
++ }
++
++ std::vector<uint8_t> 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 <stddef.h>
++
++#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 <stddef.h>
++
++#ifdef _WIN32
++#include <windows.h>
++#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 <string.h>
++
++#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 <string.h>
++
++#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 <string.h>
++
++/* #define SHOW_STAT */
++/* #define SHOW_STAT2 */
++
++#if defined(SHOW_STAT) || defined(SHOW_STAT2)
++#include <stdio.h>
++#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 <windows.h>
++#endif
++#include <stdlib.h>
++
++#include "Alloc.h"
++
++/* #define _SZ_ALLOC_DEBUG */
++
++/* use _SZ_ALLOC_DEBUG to debug alloc/free operations */
++#ifdef _SZ_ALLOC_DEBUG
++#include <stdio.h>
++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.<MaximumProfileTable>getTable(Tag.maxp).numGlyphs();
+ CMapTable cmapTable = font.getTable(Tag.cmap);
+ CMap cmap = getBestCMap(cmapTable);
+ Map<Integer, Integer> invEncoding = Maps.newHashMap();
+ List<Integer> 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<String> descs = Lists.newArrayList();
+ String baseline = "gzip";
+
+ List<String> 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<String> filenames, String baseline, List<String> descs,
+ boolean generateOutput) throws IOException {
+ PrintWriter o = new PrintWriter(System.out);
+ List<StatsCollector> stats = Lists.newArrayList();
+ for (int i = 0; i < descs.size(); i++) {
+ stats.add(new StatsCollector());
+ }
+ FontFactory fontFactory = FontFactory.getInstance();
+ o.println("<html>");
+ for (String filename : filenames) {
+ byte[] bytes = Files.toByteArray(new File(filename));
+ Font font = fontFactory.loadFonts(bytes)[0];
+ byte[] baselineResult = runExperiment(font, baseline);
+ o.printf("<!-- %s: baseline %d bytes", new File(filename).getName(), baselineResult.length);
+ for (int i = 0; i < descs.size(); i++) {
+ byte[] expResult = runExperiment(font, descs.get(i));
+ if (generateOutput) {
+ String newFilename = filename;
+ if (newFilename.endsWith(".ttf")) {
+ newFilename = newFilename.substring(0, newFilename.length() - 4);
+ }
+ newFilename += ".woff2";
+ Files.write(expResult, new File(newFilename));
+ }
+ double percent = 100. * expResult.length / baselineResult.length;
+ stats.get(i).addStat(percent);
+ o.printf(", %c %.2f%%", 'A' + i, percent);
+ }
+ 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("<p>baseline: %s</p>\n", baseline);
+ for (int i = 0; i < descs.size(); i++) {
+ StatsCollector sc = stats.get(i);
+ o.printf("<p>%c: %s: median %f, mean %f</p>\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<Integer> 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.<Integer>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<Integer> 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<Integer> tagsToStrip = Sets.newHashSet();
+ for (Entry<Integer, Integer> 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<Integer> result = new ArrayList<Integer>();
+ // 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<Integer> 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<Double> 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 <html> before calling this method
+ public void chartHeader(PrintWriter o, int n) {
+ o.println("<head>");
+ o.println("<script type='text/javascript' src='https://www.google.com/jsapi'></script>");
+ o.println("<script type='text/javascript'>");
+ o.println("google.load('visualization', '1', {packages:['corechart']});");
+ o.println("google.setOnLoadCallback(drawChart);");
+ o.println("function drawChart() {");
+ o.println(" var data = new google.visualization.DataTable()");
+ o.println(" data.addColumn('string', 'Font');");
+ if (n == 1) {
+ o.println(" data.addColumn('number', 'Ratio');");
+ } else {
+ for (int i = 0; i < n; i++) {
+ o.printf(" data.addColumn('number', 'Ratio %c');\n", 'A' + i);
+ }
+ }
+ o.printf(" data.addRows(%d);\n", values.size());
+ }
+
+ public void chartData(PrintWriter o, int ix) {
+ Collections.sort(values);
+ int length = values.size();
+ for (int i = 0; i < length; i++) {
+ o.printf(" data.setValue(%d, %d, %f);\n", i, ix, values.get(i));
+ }
+ }
+
+ public void chartEnd(PrintWriter o) {
+ o.println(" var chart = new google.visualization.LineChart(document.getElementById("
+ + "'chart_div'));");
+ o.println(" chart.draw(data, {width:700, height:400, title: 'Compression ratio'});");
+ o.println("}");
+ o.println("</script>");
+ o.println("</head>");
+
+ o.println();
+ o.println("<body>");
+ o.println("<div id='chart_div'></div>");
+ // TODO: split so we can get content into the HTML
+ }
+ public void chartFooter(PrintWriter o) {
+ o.println("</body>");
+ o.println("</html>");
+ }
+}
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<Integer, Integer> 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<Integer, Integer> getTransformMap() {
+ return TRANSFORM_MAP;
+ }
+
+ private static ImmutableBiMap<Integer, Integer> KNOWN_TABLES =
+ new ImmutableBiMap.Builder<Integer, Integer>()
+ .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<TableDirectoryEntry> 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<TableDirectoryEntry> createTableDirectoryEntries(Font font) {
+ List<TableDirectoryEntry> entries = Lists.newArrayList();
+ TreeSet<Integer> tags = new TreeSet<Integer>(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<TableDirectoryEntry> 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<TableDirectoryEntry> entries) {
+ int directorySize = computeDirectoryLength(entries);
+ for (TableDirectoryEntry entry : entries) {
+ offset += entry.writeEntry(writableFontData, offset);
+ }
+ return directorySize;
+ }
+
+ private int writeTables(WritableFontData writableFontData, int offset,
+ List<TableDirectoryEntry> entries) {
+ int start = offset;
+ for (TableDirectoryEntry entry : entries) {
+ offset += entry.writeData(writableFontData, offset);
+ offset = align4(offset);
+ }
+ return offset - start;
+ }
+
+ private int computeDirectoryLength(List<TableDirectoryEntry> 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<TableDirectoryEntry> 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<TableDirectoryEntry> 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()
+