Merge LZX compressor updates
authorEric Biggers <ebiggers3@gmail.com>
Sun, 8 Dec 2013 06:07:37 +0000 (00:07 -0600)
committerEric Biggers <ebiggers3@gmail.com>
Sun, 8 Dec 2013 06:21:38 +0000 (00:21 -0600)
- Use a suffix array-based match-finder (construction code borrowed from
  libdivsufsort).  Ideal for this case because compression in WIM chunks
  does not use a sliding window.
- Disable block splitting by default (no efficient and effective
  algorithm is available yet)

22 files changed:
Makefile.am
NEWS
README
README.WINDOWS
TODO [deleted file]
configure.ac
doc/imagex-capture.1.in
doc/imagex-optimize.1.in
doc/imagex.1.in
include/wimlib.h
include/wimlib/lzx.h
include/wimlib/resource.h
programs/imagex.c
src/divsufsort/divsufsort.c [new file with mode: 0644]
src/divsufsort/divsufsort.h [new file with mode: 0644]
src/divsufsort/divsufsort_private.h [new file with mode: 0644]
src/divsufsort/sssort.c [new file with mode: 0644]
src/divsufsort/trsort.c [new file with mode: 0644]
src/lzx-common.c
src/lzx-compress.c
src/lzx-decompress.c
src/write.c

index ad8be04..f7bcada 100644 (file)
@@ -34,6 +34,11 @@ libwim_la_SOURCES =          \
        src/join.c              \
        src/lookup_table.c      \
        src/lz77.c              \
+       src/divsufsort/divsufsort.c             \
+       src/divsufsort/divsufsort.h             \
+       src/divsufsort/divsufsort_private.h     \
+       src/divsufsort/sssort.c \
+       src/divsufsort/trsort.c \
        src/lzx-common.c        \
        src/lzx-compress.c      \
        src/lzx-decompress.c    \
diff --git a/NEWS b/NEWS
index 56fe21c..435a3f7 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,29 @@
 Only the most important changes more recent than version 0.6 are noted here.
 
+Version 1.5.3:
+       The new LZX compressor added in v1.5.2 has been improved and is now
+       enabled by default, except when `wimcapture' or `wimappend' is run
+       *without* the '--compress' option, in which case the faster LZX
+       compressor is used (the same as before).  This behavior is reasonably
+       consistent with ImageX which actually uses "fast" (XPRESS) compression
+       by default.  In those cases, use '--compress=maximum' to explicitly
+       capture a WIM image using the new (slower but better) LZX compressor.
+
+       The '--compress-slow' option still exists to `wimlib-imagex optimize',
+       but its new behavior is to tweak the new LZX compressor even more to
+       produce an even better compression ratio at the cost of more time spent
+       compressing.
+
+       Added the wimlib_get_xml_data() function to allow library clients to
+       easily retrieve the raw XML data from a WIM file if needed.
+
+       Fixed a bug that could cause an error code to be incorrectly returned
+       when writing XML data containing a <WINDOWS> element.
+
+       Mounted WIM images will now correctly show the default file stream even
+       if appears in the alternate data stream entries of the corresponding WIM
+       directory entry.
+
 Version 1.5.2:
        Added a new experimental LZX compressor which can be enabled by passing
        '--compress-slow' to `wimlib-imagex capture' or `wimlib-imagex
diff --git a/README b/README
index fcc3e90..ebae172 100644 (file)
--- a/README
+++ b/README
@@ -1,10 +1,11 @@
                                   INTRODUCTION
 
-This is wimlib version 1.5.2 (November 2013).  wimlib is a C library for creating,
-modifying, extracting, and mounting files in the Windows Imaging Format (WIM
-files).  These files are normally created by using the `imagex.exe' utility on
-Windows, but wimlib is distributed with a free implementation of ImageX called
-"wimlib-imagex" for both UNIX-like systems and Windows.
+This is wimlib version 1.5.2 (November 2013).  wimlib is a C library for
+creating, modifying, extracting, and mounting files in the Windows Imaging
+Format (WIM files).  These files are normally created using the ImageX
+(imagex.exe) or Dism (Dism.exe) utilities on Windows, but wimlib is distributed
+with a free implementation of ImageX called "wimlib-imagex" for both UNIX-like
+systems and Windows.
 
                                   INSTALLATION
 
@@ -65,34 +66,34 @@ commands and their syntax.  For additional documentation:
                                 COMPRESSION RATIO
 
 wimlib (and wimlib-imagex) can create XPRESS or LZX compressed WIM archives.
-Currently, the XPRESS compression ratio is slightly better than that provided by
-Microsoft's software, while by default the LZX compression ratio is approaching
-that of Microsoft's software but is not quite there yet.  Running time is as
-good as or better than Microsoft's software, especially with multithreaded
-compression, available in wimlib v1.1.0 and later.
-
 The following tables compare the compression ratio and performance for creating
 a compressed x86_64 Windows PE image.  Note: these timings were done on Windows
-7 so that the times would be fully comparable; however, wimlib-imagex may have
-even better performance on Linux.
+so that the times would be fully comparable; however, wimlib may have even
+better performance on other operating systems such as Linux.  Timings were done
+with 2 CPUs available, both of which automatically are used by wimlib for both
+XPRESS and LZX, and also by imagex.exe but apparently only for LZX.
 
         Table 1. WIM size
 
                                            XPRESS Compression      LZX Compression
-        wimlib-imagex (v1.4.0):            165,301,379 bytes       155,254,385 bytes
-        Microsoft imagex.exe:              167,212,939 bytes       149,973,212 bytes
+        wimlib-imagex (v1.5.3):            207,444,390 bytes       188,106,091 bytes
+        Microsoft imagex.exe (Windows 7):  209,960,209 bytes       188,224,481 bytes
 
         Table 2. Time to create WIM
 
                                            XPRESS Compression      LZX Compression
-        wimlib-imagex (v1.4.0, 2 threads): 18 sec                  51 sec
-        Microsoft imagex.exe:              25 sec                  93 sec
-
-The above LZX values are using the default LZX compressor.  wimlib v1.5.2
-introduced a new experimental LZX compressor which can be enabled by passing
-'--compress-slow' to `wimlib-imagex capture' or `wimlib-imagex optimize'.  This
-compressor is much slower but compresses the data slightly more --- currently
-usually to within a fraction of a percent of the results from imagex.exe.
+        wimlib-imagex (v1.5.3):            73 sec                  202 sec
+        Microsoft imagex.exe (Windows 7):  90 sec                  149 sec
+
+The above LZX data are using explicitly specified maximum compression
+('--compress=maximum') as of wimlib v1.5.3.  If `wimlib-imagex capture' or
+`wimlib-imagex capture' is instead run with no '--compress' argument, then a
+faster LZX compressor is used; it will produce results in between those given
+for XPRESS and LZX above.
+
+Note: if the absolute maximum compression ratio is desired, `wimlib-imagex
+optimize WIMFILE --recompress --compress-slow' on one of the above
+LZX-compressed WIMs produces a WIM of 187,089,943 bytes in about 400 seconds.
 
                                   NTFS SUPPORT
 
index d9fa790..93c20dd 100644 (file)
@@ -48,19 +48,12 @@ ImageX, including but not limited to the following:
     compared to mounting and unmounting images with Microsoft's ImageX, so you
     may prefer them anyway.
 
-  * wimlib-imagex offers fast multithreaded compression, so making WIM images
-    can be much faster.
-
-  * wimlib-imagex provides a better XPRESS ("fast") compression ratio than
-    Microsoft's ImageX.
-
   * wimlib-imagex provides an easy-to-use "optimize" command to remove wasted
     space from WIM files.
 
   * In some cases, wimlib-imagex uses simpler command-line syntax.  For
-    example, `wimlib-imagex capture' (or simply `wimcapture') defaults to
-    maximum compression by default, integrity tables are treated as persistent,
-    and in some cases image names need not be explicitly specified.
+    example, integrity tables are treated as persistent, and in some cases image
+    names need not be explicitly specified.
 
   * Whenever possible, wimlib-imagex includes improved documentation and
     informational output compared to Microsoft's software.
diff --git a/TODO b/TODO
deleted file mode 100644 (file)
index 8fc45af..0000000
--- a/TODO
+++ /dev/null
@@ -1,4 +0,0 @@
-- Find and fix bugs
-- Add more test cases (including for Win32 builds)
-- Improve LZX compression ratio, possibly by implementing LZX block splitting
-  and/or by improving the LZ77 match-finding code.
index c8b0989..5f55418 100644 (file)
@@ -61,6 +61,7 @@ AC_CHECK_HEADERS([alloca.h            \
                  glob.h                \
                  machine/endian.h      \
                  stdarg.h              \
+                 stddef.h              \
                  stdlib.h              \
                  sys/byteorder.h       \
                  sys/endian.h          \
index debc0bc..3d9466f 100644 (file)
@@ -180,27 +180,16 @@ table if and only if one was present before.
 \fB--compress\fR=\fITYPE\fR
 Specifies the compression type for the new WIM file.  This flag is only valid
 for \fB@IMAGEX_PROGNAME@ capture\fR, since the compression mode for
-\fB@IMAGEX_PROGNAME@ append\fR must be the same as that of the existing WIM (and
-is automatically set as such).
-\fITYPE\fR may be "none", "fast", or "maximum".  By default, it is "maximum".
-This default behavior is different from Microsoft's ImageX, where the default is
-"fast". \fB@IMAGEX_PROGNAME@ capture\fR instead gives you the better compression
-ratio by default and makes up for the slightly slower compression by being
-faster than Microsoft's software in the first place and using multiple CPUs when
-available.
+\fB@IMAGEX_PROGNAME@ append\fR must be the same as that of the existing
+WIM (and is automatically set as such).  \fITYPE\fR may be "none",
+"fast", or "maximum".  As of wimlib v1.5.3, the default is LZX compression, but
+in a special mode that is somewhere in between "fast" and "maximum" in terms of
+speed and compression ratio.  Use \fB--compress\fR=\fImaximum\fR to explicitly
+request a better compression ratio at the cost of more time spent compressing.
 .IP ""
 You may also specify the actual names of the compression algorithms, "XPRESS"
 and "LZX", instead of "fast" and "maximum", respectively.
 .TP
-\fB--compress-slow\fR
-Spend even more time compressing the data in order to achieve a higher
-compression ratio.  Currently, this only has an effect with LZX ("maximum")
-compression.  Depending on the data, compressing with this option will take
-around 10 to 15 times longer and produce a LZX-compressed WIM about 1% to 5%
-smaller than one produced with the default LZX compressor.  Depending on the
-data, the resulting WIM may be approximately the same size (typically no more
-than 0.4% different) as a LZX-compressed WIM produced by WIMGAPI.
-.TP
 \fB--threads\fR=\fINUM_THREADS\fR
 Number of threads to use for compressing data.  Default: autodetect (number of
 available CPUs).
@@ -508,12 +497,10 @@ the \fB--pipable\fR flag was specified.
 .SH EXAMPLES
 First example:  Create a new WIM 'mywim.wim' with "maximum" (LZX) compression
 that will contain a captured image of the directory tree 'somedir'.  Note that
-\fB@IMAGEX_PROGNAME@\fR uses "maximum" (LZX) compression by default, so
-\fB--compress\fR does \fInot\fR need to be specified; furthermore, the image
-name need not be specified and will default to 'somedir':
+the image name need not be specified and will default to 'somedir':
 .RS
 .PP
-@IMAGEX_PROGNAME@ capture somedir mywim.wim
+@IMAGEX_PROGNAME@ capture somedir mywim.wim --compress=maximum
 .RE
 .PP
 or, if the \fBwimcapture\fR hard link or batch file has been installed, the
index f0ecc54..8bcc4ec 100644 (file)
@@ -33,20 +33,14 @@ better job than the program that wrote the original file.  A side effect of this
 is that every stream in the original WIM will be checksummed, so this can help
 verify that the WIM is intact (equivalent to applying all the images from it).
 .IP ""
-Note: as mentioned in the README, wimlib generally provides a slightly better
-XPRESS compression ratio than Microsoft's software, while it generally provides
-a slightly worse LZX compression ratio than Microsoft's software.  So, you may
-not want to specify \fB--recompress\fR when optimizing a LZX-compressed WIM
-created on Windows with Microsoft's ImageX.
+Note: as of wimlib v1.5.3, wimlib's LZX compressor usually achieves the same or
+better compression than Microsoft's, but is slightly slower.
 .TP
-\fB--compress-slow\fR
-With \fB--recompress\fR: Spend even more time compressing the data in order to
-achieve a higher compression ratio.  Currently, this only has an effect with LZX
-("maximum") compression.  Depending on the data, compressing with this option
-will take around 10 to 15 times longer and produce a LZX-compressed WIM about 1%
-to 5% smaller than one produced with the default LZX compressor.  Depending on
-the data, the resulting WIM may be approximately the same size (typically no
-more than 0.4% different) as a LZX-compressed WIM produced by WIMGAPI.
+\fB--compress-slow
+For LZX compression only: spend even more time compressing the data in order to
+achieve a more optimal compression ratio.  This will make compression about
+twice as slow and will increase the compression ratio by maybe 1%, if you're
+lucky.
 .TP
 \fB--threads\fR=\fINUM_THREADS\fR
 Number of threads to use for compressing data.  Default: autodetect (number of
index 7c52233..95dff64 100644 (file)
@@ -125,10 +125,6 @@ mode, similar to Microsoft's ImageX.
 wimlib supports multithreaded compression, which can make it much faster to
 create compressed WIM files.
 .IP \[bu]
-\fB@IMAGEX_PROGNAME@ capture\fR defaults to LZX ("maximum") compression for new
-WIMs, as opposed to Microsoft's software which defaults to XPRESS ("fast")
-compression.
-.IP \[bu]
 \fB@IMAGEX_PROGNAME@\fR offers the extra commands \fB@IMAGEX_PROGNAME@
 extract\fR and \fB@IMAGEX_PROGNAME@ update\fR, which let you quickly extract
 files from or make changes to a WIM image without mounting it.
index 7bab4a3..40192fe 100644 (file)
@@ -1501,12 +1501,7 @@ typedef int (*wimlib_iterate_lookup_table_callback_t)(const struct wimlib_resour
  * already implied for wimlib_overwrite().  */
 #define WIMLIB_WRITE_FLAG_STREAMS_OK                   0x00000400
 
-/** Use the slow LZX compression algorithm (rather than the default fast LZX
- * compression algorithm) to try to achieve a higher compression ratio.  Only
- * has an effect if the WIM uses LZX compression; not to be confused with "fast"
- * (XPRESS) compression.  This can be combined with
- * ::WIMLIB_WRITE_FLAG_RECOMPRESS.  */
-#define WIMLIB_WRITE_FLAG_COMPRESS_SLOW                        0x00000800
+#define WIMLIB_WRITE_FLAG_RESERVED                     0x00000800
 
 /** @} */
 /** @ingroup G_general
@@ -1692,11 +1687,9 @@ struct wimlib_lzx_params {
 
                        uint32_t slow_reserved1 : 31;
 
-                       /** This is the maximum match length to return from the
-                        * binary tree match-finder.  Any match reaching this
-                        * limit is still extended as far as possible.  Must be
-                        * at least 3 and no more than 257.  Suggested value:
-                        * 32.  */
+                       /** Matches with length (in bytes) longer than this
+                        * value are immediately taken without spending time on
+                        * minimum-cost measurements.  Suggested value: 32.  */
                        uint32_t num_fast_bytes;
 
                        /** Number of passes to compute a match/literal sequence
@@ -1704,18 +1697,26 @@ struct wimlib_lzx_params {
                         * algorithm that attempts to minimize the cost of the
                         * match/literal sequence by using a cost model provided
                         * by the previous iteration.  Must be at least 1.
-                        * Suggested value: 3.  */
+                        * Suggested value: 2.  */
                        uint32_t num_optim_passes;
 
                        /** The number of times to attempt to recursively split
                         * each LZX block.  Up to (2**(num_split_passes)
                         * sub-blocks can be created for a given input.  This
                         * parameter can be 0, in which case the full input is
-                        * always output as one block.  Suggested value: 3.
+                        * always output as one block.  Suggested value: 0.
                         */
                        uint32_t num_split_passes;
 
-                       uint32_t slow_reserved2[4];
+                       /** Maximum depth to search for matches at each
+                        * position.  Suggested value: 50.  */
+                       uint32_t max_search_depth;
+
+                       /** Maximum number of potentially good matches to
+                        * consider for each position.  Suggested value: 3.  */
+                       uint32_t max_matches_per_pos;
+
+                       uint32_t slow_reserved2[2];
 
                        /** Assumed cost of a main symbol with zero frequency.
                         * Must be at least 1 and no more than 16.  Suggested
@@ -2765,8 +2766,7 @@ wimlib_lzx_compress2(const void *chunk, unsigned chunk_size, void *out,
  * purpose.
  *
  * @param params
- *     Compression parameters to use, or @c NULL to use the default algorithm
- *     and parameters.
+ *     Compression parameters to use, or @c NULL to use the default parameters.
  *
  * @param ctx_ret
  *     A pointer to either @c NULL or an existing ::wimlib_lzx_context.  If
@@ -2789,15 +2789,6 @@ wimlib_lzx_alloc_context(const struct wimlib_lzx_params *params,
 /**
  * @ingroup G_compression
  *
- * Free the specified LZX compression context, allocated with
- * wimlib_lzx_alloc_context().
- */
-extern void
-wimlib_lzx_free_context(struct wimlib_lzx_context *ctx);
-
-/**
- * @ingroup G_compression
- *
  * Decompresses a block of LZX-compressed data as used in the WIM file format.
  *
  * Note that this will NOT work unmodified for LZX as used in the cabinet
@@ -2826,6 +2817,44 @@ extern int
 wimlib_lzx_decompress(const void *compressed_data, unsigned compressed_len,
                      void *uncompressed_data, unsigned uncompressed_len);
 
+/**
+ * @ingroup G_compression
+ *
+ * Free the specified LZX compression context, allocated with
+ * wimlib_lzx_alloc_context().
+ */
+extern void
+wimlib_lzx_free_context(struct wimlib_lzx_context *ctx);
+
+/**
+ * @ingroup G_compression
+ *
+ * Set the global default LZX compression parameters.
+ *
+ * @param params
+ *     The LZX compression parameters to set.  These default parameters will be
+ *     used by any calls to wimlib_lzx_alloc_context() with @c NULL LZX
+ *     parameters specified, as well as by any future compression performed by
+ *     the library itself.  Passing @p NULL here resets the default LZX
+ *     parameters to their original value.
+ *
+ * @return 0 on success; nonzero on error.
+ *
+ * @retval ::WIMLIB_ERR_INVALID_PARAM
+ *     The compression parameters were invalid.
+ */
+extern int
+wimlib_lzx_set_default_params(const struct wimlib_lzx_params *params);
+
+/**
+ * @ingroup G_compression
+ *
+ * Free the specified LZX compression context, allocated with
+ * wimlib_lzx_alloc_context().
+ */
+extern void
+wimlib_lzx_free_context(struct wimlib_lzx_context *ctx);
+
 
 /**
  * @ingroup G_mounting_wim_images
index 6061e73..b6d1ec2 100644 (file)
@@ -16,8 +16,8 @@
 /* Constants, most of which are defined by the LZX specification: */
 
 /* The smallest and largest allowed match lengths. */
-#define LZX_MIN_MATCH                2
-#define LZX_MAX_MATCH                257
+#define LZX_MIN_MATCH_LEN            2
+#define LZX_MAX_MATCH_LEN            257
 
 /* Number of values an uncompressed literal byte can represent. */
 #define LZX_NUM_CHARS                256
  *
  * The ALIGNED tree is only present on ALIGNED blocks.
  *
- * A PRETREE is used to encode the code lengths for the main tree and the length
+ * A PRECODE is used to encode the code lengths for the main tree and the length
  * tree.  There is a separate pretree for each half of the main tree.  */
 
-#define LZX_MAINTREE_NUM_SYMBOLS        (LZX_NUM_CHARS + \
+#define LZX_MAINCODE_NUM_SYMBOLS        (LZX_NUM_CHARS + \
                                        (LZX_NUM_POSITION_SLOTS << 3))
-#define LZX_MAINTREE_TABLEBITS         11
+#define LZX_MAINCODE_TABLEBITS         11
 
-#define LZX_LENTREE_NUM_SYMBOLS                249
-#define LZX_LENTREE_TABLEBITS          10
+#define LZX_LENCODE_NUM_SYMBOLS                249
+#define LZX_LENCODE_TABLEBITS          10
 
-#define LZX_PRETREE_NUM_SYMBOLS                20
-#define LZX_PRETREE_TABLEBITS          6
-#define LZX_PRETREE_ELEMENT_SIZE       4
+#define LZX_PRECODE_NUM_SYMBOLS                20
+#define LZX_PRECODE_TABLEBITS          6
+#define LZX_PRECODE_ELEMENT_SIZE       4
 
-#define LZX_ALIGNEDTREE_NUM_SYMBOLS    8
-#define LZX_ALIGNEDTREE_TABLEBITS      7
-#define LZX_ALIGNEDTREE_ELEMENT_SIZE   3
+#define LZX_ALIGNEDCODE_NUM_SYMBOLS    8
+#define LZX_ALIGNEDCODE_TABLEBITS      7
+#define LZX_ALIGNEDCODE_ELEMENT_SIZE   3
 
-/* Maximum allowed length of a Huffman code. */
-#define LZX_MAX_CODEWORD_LEN           16
+/* Maximum allowed length of Huffman codewords.  */
+#define LZX_MAX_MAIN_CODEWORD_LEN      16
+#define LZX_MAX_LEN_CODEWORD_LEN       16
+#define LZX_MAX_PRE_CODEWORD_LEN       16
+#define LZX_MAX_ALIGNED_CODEWORD_LEN   8
 
 /* For the LZX-compressed blocks in WIM files, this value is always used as the
  * filesize parameter for the call instruction (0xe8 byte) preprocessing, even
@@ -81,7 +84,7 @@
 #define USE_LZX_EXTRA_BITS_ARRAY
 
 #ifdef USE_LZX_EXTRA_BITS_ARRAY
-extern const u8 lzx_extra_bits[LZX_NUM_POSITION_SLOTS];
+extern const u8 lzx_extra_bits[];
 #endif
 
 /* Given the number of a LZX position slot, return the number of extra bits that
@@ -94,18 +97,29 @@ lzx_get_num_extra_bits(unsigned position_slot)
        return lzx_extra_bits[position_slot];
 #else
        /* Calculate directly using a shift and subtraction. */
-       wimlib_assert(position_slot >= 2 && position_slot <= 37);
+       LZX_ASSERT(position_slot >= 2 && position_slot <= 37);
        return (position_slot >> 1) - 1;
 #endif
 }
 
-extern const u32 lzx_position_base[LZX_NUM_POSITION_SLOTS];
+extern const u32 lzx_position_base[];
 
-/* Least-recently used queue for match offsets. */
+#define LZX_NUM_RECENT_OFFSETS 3
+
+/* Least-recently used queue for match offsets.  */
 struct lzx_lru_queue {
-       u32 R0;
-       u32 R1;
-       u32 R2;
+       u32 R[LZX_NUM_RECENT_OFFSETS];
 };
 
+/* In the LZX format, an offset of n bytes is actually encoded
+ * as (n + LZX_OFFSET_OFFSET).  */
+#define LZX_OFFSET_OFFSET      (LZX_NUM_RECENT_OFFSETS - 1)
+
+static inline void
+lzx_lru_queue_init(struct lzx_lru_queue *queue)
+{
+       for (unsigned i = 0; i < LZX_NUM_RECENT_OFFSETS; i++)
+               queue->R[i] = 1;
+}
+
 #endif /* _WIMLIB_LZX_H */
index 1d257a5..799cb74 100644 (file)
@@ -94,8 +94,7 @@ put_resource_entry(const struct resource_entry *entry,
 
 /* wimlib internal flags used when reading or writing resources.  */
 #define WIMLIB_WRITE_RESOURCE_FLAG_RECOMPRESS          0x00000001
-#define WIMLIB_WRITE_RESOURCE_FLAG_COMPRESS_SLOW       0x00000002
-#define WIMLIB_WRITE_RESOURCE_FLAG_PIPABLE             0x00000004
+#define WIMLIB_WRITE_RESOURCE_FLAG_PIPABLE             0x00000002
 #define WIMLIB_WRITE_RESOURCE_MASK                     0x0000ffff
 
 #define WIMLIB_READ_RESOURCE_FLAG_RAW_FULL             0x80000000
index f23f043..5af55eb 100644 (file)
@@ -418,6 +418,30 @@ get_compression_type(const tchar *optarg)
        }
 }
 
+static void
+set_compress_slow(void)
+{
+       static const struct wimlib_lzx_params slow_params = {
+               .size_of_this = sizeof(struct wimlib_lzx_params),
+               .algorithm = WIMLIB_LZX_ALGORITHM_SLOW,
+               .alg_params = {
+                       .slow = {
+                               .use_len2_matches = 1,
+                               .num_fast_bytes = 96,
+                               .num_optim_passes = 4,
+                               .num_split_passes = 0,
+                               .max_search_depth = 100,
+                               .max_matches_per_pos = 10,
+                               .main_nostat_cost = 15,
+                               .len_nostat_cost = 15,
+                               .aligned_nostat_cost = 7,
+                       },
+               },
+       };
+       if (wimlib_lzx_set_default_params(&slow_params))
+               imagex_error(T("Couldn't set slow compression parameters.!"));
+}
+
 struct string_set {
        const tchar **strings;
        unsigned num_strings;
@@ -1642,7 +1666,7 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd)
        int add_image_flags = WIMLIB_ADD_IMAGE_FLAG_EXCLUDE_VERBOSE |
                              WIMLIB_ADD_IMAGE_FLAG_WINCONFIG;
        int write_flags = 0;
-       int compression_type = WIMLIB_COMPRESSION_TYPE_LZX;
+       int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
        const tchar *wimfile;
        int wim_fd;
        const tchar *name;
@@ -1698,7 +1722,7 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd)
                                goto out_err;
                        break;
                case IMAGEX_COMPRESS_SLOW_OPTION:
-                       write_flags |= WIMLIB_WRITE_FLAG_COMPRESS_SLOW;
+                       set_compress_slow();
                        break;
                case IMAGEX_FLAGS_OPTION:
                        flags_element = optarg;
@@ -1784,6 +1808,18 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd)
        source = argv[0];
        wimfile = argv[1];
 
+       /* Set default compression type.  */
+       if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
+               struct wimlib_lzx_params params;
+               memset(&params, 0, sizeof(params));
+               params.size_of_this = sizeof(params);
+               params.algorithm = WIMLIB_LZX_ALGORITHM_FAST;
+               params.use_defaults = 1;
+
+               wimlib_lzx_set_default_params(&params);
+               compression_type = WIMLIB_COMPRESSION_TYPE_LZX;
+       }
+
        if (!tstrcmp(wimfile, T("-"))) {
                /* Writing captured WIM to standard output.  */
        #if 0
@@ -3224,7 +3260,7 @@ imagex_optimize(int argc, tchar **argv, int cmd)
                        write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
                        break;
                case IMAGEX_COMPRESS_SLOW_OPTION:
-                       write_flags |= WIMLIB_WRITE_FLAG_COMPRESS_SLOW;
+                       set_compress_slow();
                        break;
                case IMAGEX_THREADS_OPTION:
                        num_threads = parse_num_threads(optarg);
@@ -3640,11 +3676,11 @@ static const tchar *usage_strings[] = {
 T(
 "    %"TS" (DIRECTORY | NTFS_VOLUME) WIMFILE\n"
 "                    [IMAGE_NAME [IMAGE_DESCRIPTION]] [--boot]\n"
-"                    [--check] [--nocheck] [--compress-slow]\n"
-"                    [--flags EDITION_ID] [--verbose] [--dereference]\n"
-"                    [--config=FILE] [--threads=NUM_THREADS] [--source-list]\n"
-"                    [--no-acls] [--strict-acls] [--rpfix] [--norpfix]\n"
-"                    [--unix-data] [--pipable] [--update-of=[WIMFILE:]IMAGE]\n"
+"                    [--check] [--nocheck] [--flags EDITION_ID] [--verbose]\n"
+"                    [--dereference] [--config=FILE] [--threads=NUM_THREADS]\n"
+"                    [--source-list] [--no-acls] [--strict-acls] [--rpfix]\n"
+"                    [--norpfix] [--unix-data] [--pipable]\n"
+"                    [--update-of=[WIMFILE:]IMAGE]\n"
 ),
 [CMD_APPLY] =
 T(
@@ -3658,7 +3694,7 @@ T(
 T(
 "    %"TS" (DIRECTORY | NTFS_VOLUME) WIMFILE\n"
 "                   [IMAGE_NAME [IMAGE_DESCRIPTION]] [--boot]\n"
-"                    [--check] [--nocheck] [--compress=TYPE] [--compress-slow]\n"
+"                    [--check] [--nocheck] [--compress=TYPE]\n"
 "                    [--flags EDITION_ID] [--verbose] [--dereference]\n"
 "                    [--config=FILE] [--threads=NUM_THREADS] [--source-list]\n"
 "                    [--no-acls] [--strict-acls] [--rpfix] [--norpfix]\n"
@@ -3717,8 +3753,7 @@ T(
 [CMD_OPTIMIZE] =
 T(
 "    %"TS" WIMFILE [--check] [--nocheck] [--recompress]\n"
-"                    [--compress-slow] [--threads=NUM_THREADS]\n"
-"                    [--pipable] [--not-pipable]\n"
+"                    [--threads=NUM_THREADS] [--pipable] [--not-pipable]\n"
 ),
 [CMD_SPLIT] =
 T(
diff --git a/src/divsufsort/divsufsort.c b/src/divsufsort/divsufsort.c
new file mode 100644 (file)
index 0000000..94ba7e2
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+ * divsufsort.c for libdivsufsort
+ * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "divsufsort_private.h"
+#ifdef _OPENMP
+# include <omp.h>
+#endif
+
+/*- Private Functions -*/
+
+/* Sorts suffixes of type B*. */
+static
+saidx_t
+sort_typeBstar(const sauchar_t *T, saidx_t *SA,
+               saidx_t *bucket_A, saidx_t *bucket_B,
+               saidx_t n) {
+  saidx_t *PAb, *ISAb, *buf;
+  saidx_t i, j, k, t, m, bufsize;
+  saint_t c0, c1;
+
+  /* Initialize bucket arrays. */
+  for(i = 0; i < BUCKET_A_SIZE; ++i) { bucket_A[i] = 0; }
+  for(uint32_t i = 0; i < BUCKET_B_SIZE; ++i) { bucket_B[i] = 0; }
+
+  /* Count the number of occurrences of the first one or two characters of each
+     type A, B and B* suffix. Moreover, store the beginning position of all
+     type B* suffixes into the array SA. */
+
+  for(i = n - 1, m = n, c0 = T[n - 1]; 0 <= i;) {
+    /* type A suffix. */
+    do { ++BUCKET_A(c1 = c0); } while((0 <= --i) && ((c0 = T[i]) >= c1));
+    if(0 <= i) {
+      /* type B* suffix. */
+      ++BUCKET_BSTAR(c0, c1);
+      SA[--m] = i;
+      /* type B suffix. */
+      for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) <= c1); --i, c1 = c0) {
+        ++BUCKET_B(c0, c1);
+      }
+    }
+  }
+  m = n - m;
+/*
+note:
+  A type B* suffix is lexicographically smaller than a type B suffix that
+  begins with the same first two characters.
+*/
+
+  /* Calculate the index of start/end point of each bucket. */
+  for(c0 = 0, i = 0, j = 0; c0 < ALPHABET_SIZE; ++c0) {
+    t = i + BUCKET_A(c0);
+    BUCKET_A(c0) = i + j; /* start point */
+    i = t + BUCKET_B(c0, c0);
+    for(c1 = c0 + 1; c1 < ALPHABET_SIZE; ++c1) {
+      j += BUCKET_BSTAR(c0, c1);
+      BUCKET_BSTAR(c0, c1) = j; /* end point */
+      i += BUCKET_B(c0, c1);
+    }
+  }
+
+  if(0 < m) {
+    /* Sort the type B* suffixes by their first two characters. */
+    PAb = SA + n - m; ISAb = SA + m;
+    for(i = m - 2; 0 <= i; --i) {
+      t = PAb[i], c0 = T[t], c1 = T[t + 1];
+      SA[--BUCKET_BSTAR(c0, c1)] = i;
+    }
+    t = PAb[m - 1], c0 = T[t], c1 = T[t + 1];
+    SA[--BUCKET_BSTAR(c0, c1)] = m - 1;
+
+    /* Sort the type B* substrings using sssort. */
+    buf = SA + m, bufsize = n - (2 * m);
+    for(c0 = ALPHABET_SIZE - 2, j = m; 0 < j; --c0) {
+      for(c1 = ALPHABET_SIZE - 1; c0 < c1; j = i, --c1) {
+        i = BUCKET_BSTAR(c0, c1);
+        if(1 < (j - i)) {
+          sssort(T, PAb, SA + i, SA + j,
+                 buf, bufsize, 2, n, *(SA + i) == (m - 1));
+        }
+      }
+    }
+
+    /* Compute ranks of type B* substrings. */
+    for(i = m - 1; 0 <= i; --i) {
+      if(0 <= SA[i]) {
+        j = i;
+        do { ISAb[SA[i]] = i; } while((0 <= --i) && (0 <= SA[i]));
+        SA[i + 1] = i - j;
+        if(i <= 0) { break; }
+      }
+      j = i;
+      do { ISAb[SA[i] = ~SA[i]] = j; } while(SA[--i] < 0);
+      ISAb[SA[i]] = j;
+    }
+
+    /* Construct the inverse suffix array of type B* suffixes using trsort. */
+    trsort(ISAb, SA, m, 1);
+
+    /* Set the sorted order of tyoe B* suffixes. */
+    for(i = n - 1, j = m, c0 = T[n - 1]; 0 <= i;) {
+      for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) >= c1); --i, c1 = c0) { }
+      if(0 <= i) {
+        t = i;
+        for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) <= c1); --i, c1 = c0) { }
+        SA[ISAb[--j]] = ((t == 0) || (1 < (t - i))) ? t : ~t;
+      }
+    }
+
+    /* Calculate the index of start/end point of each bucket. */
+    BUCKET_B(ALPHABET_SIZE - 1, ALPHABET_SIZE - 1) = n; /* end point */
+    for(c0 = ALPHABET_SIZE - 2, k = m - 1; 0 <= c0; --c0) {
+      i = BUCKET_A(c0 + 1) - 1;
+      for(c1 = ALPHABET_SIZE - 1; c0 < c1; --c1) {
+        t = i - BUCKET_B(c0, c1);
+        BUCKET_B(c0, c1) = i; /* end point */
+
+        /* Move all type B* suffixes to the correct position. */
+        for(i = t, j = BUCKET_BSTAR(c0, c1);
+            j <= k;
+            --i, --k) { SA[i] = SA[k]; }
+      }
+      BUCKET_BSTAR(c0, c0 + 1) = i - BUCKET_B(c0, c0) + 1; /* start point */
+      BUCKET_B(c0, c0) = i; /* end point */
+    }
+  }
+
+  return m;
+}
+
+/* Constructs the suffix array by using the sorted order of type B* suffixes. */
+static
+void
+construct_SA(const sauchar_t *T, saidx_t *SA,
+             saidx_t *bucket_A, saidx_t *bucket_B,
+             saidx_t n, saidx_t m) {
+  saidx_t *i, *j, *k;
+  saidx_t s;
+  saint_t c0, c1, c2;
+
+  if(0 < m) {
+    /* Construct the sorted order of type B suffixes by using
+       the sorted order of type B* suffixes. */
+    for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) {
+      /* Scan the suffix array from right to left. */
+      for(i = SA + BUCKET_BSTAR(c1, c1 + 1),
+          j = SA + BUCKET_A(c1 + 1) - 1, k = NULL, c2 = -1;
+          i <= j;
+          --j) {
+        if(0 < (s = *j)) {
+          assert(T[s] == c1);
+          assert(((s + 1) < n) && (T[s] <= T[s + 1]));
+          assert(T[s - 1] <= T[s]);
+          *j = ~s;
+          c0 = T[--s];
+          if((0 < s) && (T[s - 1] > c0)) { s = ~s; }
+          if(c0 != c2) {
+            if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; }
+            k = SA + BUCKET_B(c2 = c0, c1);
+          }
+          assert(k < j);
+          *k-- = s;
+        } else {
+          assert(((s == 0) && (T[s] == c1)) || (s < 0));
+          *j = ~s;
+        }
+      }
+    }
+  }
+
+  /* Construct the suffix array by using
+     the sorted order of type B suffixes. */
+  k = SA + BUCKET_A(c2 = T[n - 1]);
+  *k++ = (T[n - 2] < c2) ? ~(n - 1) : (n - 1);
+  /* Scan the suffix array from left to right. */
+  for(i = SA, j = SA + n; i < j; ++i) {
+    if(0 < (s = *i)) {
+      assert(T[s - 1] >= T[s]);
+      c0 = T[--s];
+      if((s == 0) || (T[s - 1] < c0)) { s = ~s; }
+      if(c0 != c2) {
+        BUCKET_A(c2) = k - SA;
+        k = SA + BUCKET_A(c2 = c0);
+      }
+      assert(i < k);
+      *k++ = s;
+    } else {
+      assert(s < 0);
+      *i = ~s;
+    }
+  }
+}
+
+#if 0
+/* Constructs the burrows-wheeler transformed string directly
+   by using the sorted order of type B* suffixes. */
+static
+saidx_t
+construct_BWT(const sauchar_t *T, saidx_t *SA,
+              saidx_t *bucket_A, saidx_t *bucket_B,
+              saidx_t n, saidx_t m) {
+  saidx_t *i, *j, *k, *orig;
+  saidx_t s;
+  saint_t c0, c1, c2;
+
+  if(0 < m) {
+    /* Construct the sorted order of type B suffixes by using
+       the sorted order of type B* suffixes. */
+    for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) {
+      /* Scan the suffix array from right to left. */
+      for(i = SA + BUCKET_BSTAR(c1, c1 + 1),
+          j = SA + BUCKET_A(c1 + 1) - 1, k = NULL, c2 = -1;
+          i <= j;
+          --j) {
+        if(0 < (s = *j)) {
+          assert(T[s] == c1);
+          assert(((s + 1) < n) && (T[s] <= T[s + 1]));
+          assert(T[s - 1] <= T[s]);
+          c0 = T[--s];
+          *j = ~((saidx_t)c0);
+          if((0 < s) && (T[s - 1] > c0)) { s = ~s; }
+          if(c0 != c2) {
+            if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; }
+            k = SA + BUCKET_B(c2 = c0, c1);
+          }
+          assert(k < j);
+          *k-- = s;
+        } else if(s != 0) {
+          *j = ~s;
+#ifndef NDEBUG
+        } else {
+          assert(T[s] == c1);
+#endif
+        }
+      }
+    }
+  }
+
+  /* Construct the BWTed string by using
+     the sorted order of type B suffixes. */
+  k = SA + BUCKET_A(c2 = T[n - 1]);
+  *k++ = (T[n - 2] < c2) ? ~((saidx_t)T[n - 2]) : (n - 1);
+  /* Scan the suffix array from left to right. */
+  for(i = SA, j = SA + n, orig = SA; i < j; ++i) {
+    if(0 < (s = *i)) {
+      assert(T[s - 1] >= T[s]);
+      c0 = T[--s];
+      *i = c0;
+      if((0 < s) && (T[s - 1] < c0)) { s = ~((saidx_t)T[s - 1]); }
+      if(c0 != c2) {
+        BUCKET_A(c2) = k - SA;
+        k = SA + BUCKET_A(c2 = c0);
+      }
+      assert(i < k);
+      *k++ = s;
+    } else if(s != 0) {
+      *i = ~s;
+    } else {
+      orig = i;
+    }
+  }
+
+  return orig - SA;
+}
+#endif
+
+
+/*---------------------------------------------------------------------------*/
+
+/*- Function -*/
+
+/* XXX Modified from original: use provided temporary space instead of
+ * allocating it.  */
+saint_t
+divsufsort(const sauchar_t *T, saidx_t *SA, saidx_t n,
+     saidx_t *bucket_A, saidx_t *bucket_B) {
+  saidx_t m;
+
+  switch (n) {
+    case 0:
+      break;
+
+    case 1:
+      SA[0] = 0;
+      break;
+
+    case 2:
+      m = (T[0] < T[1]);
+      SA[m ^ 1] = 0;
+      SA[m] = 1;
+      break;
+
+    default:
+      m = sort_typeBstar(T, SA, bucket_A, bucket_B, n);
+      construct_SA(T, SA, bucket_A, bucket_B, n, m);
+      break;
+  }
+  return 0;
+}
+
+#if 0
+saidx_t
+divbwt(const sauchar_t *T, sauchar_t *U, saidx_t *A, saidx_t n) {
+  saidx_t *B;
+  saidx_t *bucket_A, *bucket_B;
+  saidx_t m, pidx, i;
+
+  /* Check arguments. */
+  if((T == NULL) || (U == NULL) || (n < 0)) { return -1; }
+  else if(n <= 1) { if(n == 1) { U[0] = T[0]; } return n; }
+
+  if((B = A) == NULL) { B = (saidx_t *)malloc((size_t)(n + 1) * sizeof(saidx_t)); }
+  bucket_A = (saidx_t *)malloc(BUCKET_A_SIZE * sizeof(saidx_t));
+  bucket_B = (saidx_t *)malloc(BUCKET_B_SIZE * sizeof(saidx_t));
+
+  /* Burrows-Wheeler Transform. */
+  if((B != NULL) && (bucket_A != NULL) && (bucket_B != NULL)) {
+    m = sort_typeBstar(T, B, bucket_A, bucket_B, n);
+    pidx = construct_BWT(T, B, bucket_A, bucket_B, n, m);
+
+    /* Copy to output string. */
+    U[0] = T[n - 1];
+    for(i = 0; i < pidx; ++i) { U[i + 1] = (sauchar_t)B[i]; }
+    for(i += 1; i < n; ++i) { U[i] = (sauchar_t)B[i]; }
+    pidx += 1;
+  } else {
+    pidx = -2;
+  }
+
+  free(bucket_B);
+  free(bucket_A);
+  if(A == NULL) { free(B); }
+
+  return pidx;
+}
+#endif
+
+#if 0
+const char *
+divsufsort_version(void) {
+  return PROJECT_VERSION_FULL;
+}
+#endif
diff --git a/src/divsufsort/divsufsort.h b/src/divsufsort/divsufsort.h
new file mode 100644 (file)
index 0000000..8df39bc
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * divsufsort.h for libdivsufsort
+ * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _DIVSUFSORT_H
+#define _DIVSUFSORT_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include <inttypes.h>
+
+#ifndef DIVSUFSORT_API
+# ifdef DIVSUFSORT_BUILD_DLL
+#  define DIVSUFSORT_API 
+# else
+#  define DIVSUFSORT_API 
+# endif
+#endif
+
+/*- Datatypes -*/
+#ifndef SAUCHAR_T
+#define SAUCHAR_T
+typedef uint8_t sauchar_t;
+#endif /* SAUCHAR_T */
+#ifndef SAINT_T
+#define SAINT_T
+typedef int32_t saint_t;
+#endif /* SAINT_T */
+#ifndef SAIDX_T
+#define SAIDX_T
+typedef int32_t saidx_t;
+#endif /* SAIDX_T */
+#ifndef PRIdSAINT_T
+#define PRIdSAINT_T PRId32
+#endif /* PRIdSAINT_T */
+#ifndef PRIdSAIDX_T
+#define PRIdSAIDX_T PRId32
+#endif /* PRIdSAIDX_T */
+
+
+/*- Prototypes -*/
+
+/**
+ * Constructs the suffix array of a given string.
+ * @param T[0..n-1] The input string.
+ * @param SA[0..n-1] The output array of suffixes.
+ * @param n The length of the given string.
+ * @param bucket_A Temporary space (size >= 256)
+ * @param bucket_B Temporary space (size >= 256 * 256)
+ *
+ * @return 0
+ */
+DIVSUFSORT_API
+saint_t
+divsufsort(const sauchar_t *T, saidx_t *SA, saidx_t n,
+     saidx_t *bucket_A, saidx_t *bucket_B);
+
+#if 0
+
+/**
+ * Constructs the burrows-wheeler transformed string of a given string.
+ * @param T[0..n-1] The input string.
+ * @param U[0..n-1] The output string. (can be T)
+ * @param A[0..n-1] The temporary array. (can be NULL)
+ * @param n The length of the given string.
+ * @return The primary index if no error occurred, -1 or -2 otherwise.
+ */
+DIVSUFSORT_API
+saidx_t
+divbwt(const sauchar_t *T, sauchar_t *U, saidx_t *A, saidx_t n);
+
+/**
+ * Returns the version of the divsufsort library.
+ * @return The version number string.
+ */
+DIVSUFSORT_API
+const char *
+divsufsort_version(void);
+
+
+/**
+ * Constructs the burrows-wheeler transformed string of a given string and suffix array.
+ * @param T[0..n-1] The input string.
+ * @param U[0..n-1] The output string. (can be T)
+ * @param SA[0..n-1] The suffix array. (can be NULL)
+ * @param n The length of the given string.
+ * @param idx The output primary index.
+ * @return 0 if no error occurred, -1 or -2 otherwise.
+ */
+DIVSUFSORT_API
+saint_t
+bw_transform(const sauchar_t *T, sauchar_t *U,
+             saidx_t *SA /* can NULL */,
+             saidx_t n, saidx_t *idx);
+
+/**
+ * Inverse BW-transforms a given BWTed string.
+ * @param T[0..n-1] The input string.
+ * @param U[0..n-1] The output string. (can be T)
+ * @param A[0..n-1] The temporary array. (can be NULL)
+ * @param n The length of the given string.
+ * @param idx The primary index.
+ * @return 0 if no error occurred, -1 or -2 otherwise.
+ */
+DIVSUFSORT_API
+saint_t
+inverse_bw_transform(const sauchar_t *T, sauchar_t *U,
+                     saidx_t *A /* can NULL */,
+                     saidx_t n, saidx_t idx);
+
+/**
+ * Checks the correctness of a given suffix array.
+ * @param T[0..n-1] The input string.
+ * @param SA[0..n-1] The input suffix array.
+ * @param n The length of the given string.
+ * @param verbose The verbose mode.
+ * @return 0 if no error occurred.
+ */
+DIVSUFSORT_API
+saint_t
+sufcheck(const sauchar_t *T, const saidx_t *SA, saidx_t n, saint_t verbose);
+
+/**
+ * Search for the pattern P in the string T.
+ * @param T[0..Tsize-1] The input string.
+ * @param Tsize The length of the given string.
+ * @param P[0..Psize-1] The input pattern string.
+ * @param Psize The length of the given pattern string.
+ * @param SA[0..SAsize-1] The input suffix array.
+ * @param SAsize The length of the given suffix array.
+ * @param idx The output index.
+ * @return The count of matches if no error occurred, -1 otherwise.
+ */
+DIVSUFSORT_API
+saidx_t
+sa_search(const sauchar_t *T, saidx_t Tsize,
+          const sauchar_t *P, saidx_t Psize,
+          const saidx_t *SA, saidx_t SAsize,
+          saidx_t *left);
+
+/**
+ * Search for the character c in the string T.
+ * @param T[0..Tsize-1] The input string.
+ * @param Tsize The length of the given string.
+ * @param SA[0..SAsize-1] The input suffix array.
+ * @param SAsize The length of the given suffix array.
+ * @param c The input character.
+ * @param idx The output index.
+ * @return The count of matches if no error occurred, -1 otherwise.
+ */
+DIVSUFSORT_API
+saidx_t
+sa_simplesearch(const sauchar_t *T, saidx_t Tsize,
+                const saidx_t *SA, saidx_t SAsize,
+                saint_t c, saidx_t *left);
+#endif
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif /* __cplusplus */
+
+#endif /* _DIVSUFSORT_H */
diff --git a/src/divsufsort/divsufsort_private.h b/src/divsufsort/divsufsort_private.h
new file mode 100644 (file)
index 0000000..7e261c1
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * divsufsort_private.h for libdivsufsort
+ * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _DIVSUFSORT_PRIVATE_H
+#define _DIVSUFSORT_PRIVATE_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <assert.h>
+#include <stdio.h>
+#if HAVE_STRING_H
+# include <string.h>
+#endif
+#if HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#if HAVE_MEMORY_H
+# include <memory.h>
+#endif
+#if HAVE_STDDEF_H
+# include <stddef.h>
+#endif
+#if HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# if HAVE_STDINT_H
+#  include <stdint.h>
+# endif
+#endif
+#if defined(BUILD_DIVSUFSORT64)
+# include "divsufsort64.h"
+# ifndef SAIDX_T
+#  define SAIDX_T
+#  define saidx_t saidx64_t
+# endif /* SAIDX_T */
+# ifndef PRIdSAIDX_T
+#  define PRIdSAIDX_T PRIdSAIDX64_T
+# endif /* PRIdSAIDX_T */
+# define divsufsort divsufsort64
+# define divbwt divbwt64
+# define divsufsort_version divsufsort64_version
+# define bw_transform bw_transform64
+# define inverse_bw_transform inverse_bw_transform64
+# define sufcheck sufcheck64
+# define sa_search sa_search64
+# define sa_simplesearch sa_simplesearch64
+# define sssort sssort64
+# define trsort trsort64
+#else
+# include "divsufsort.h"
+#endif
+
+
+/*- Constants -*/
+#if !defined(UINT8_MAX)
+# define UINT8_MAX (255)
+#endif /* UINT8_MAX */
+#if defined(ALPHABET_SIZE) && (ALPHABET_SIZE < 1)
+# undef ALPHABET_SIZE
+#endif
+#if !defined(ALPHABET_SIZE)
+# define ALPHABET_SIZE (UINT8_MAX + 1)
+#endif
+/* for divsufsort.c */
+#define BUCKET_A_SIZE (ALPHABET_SIZE)
+#define BUCKET_B_SIZE (ALPHABET_SIZE * ALPHABET_SIZE)
+/* for sssort.c */
+#if defined(SS_INSERTIONSORT_THRESHOLD)
+# if SS_INSERTIONSORT_THRESHOLD < 1
+#  undef SS_INSERTIONSORT_THRESHOLD
+#  define SS_INSERTIONSORT_THRESHOLD (1)
+# endif
+#else
+# define SS_INSERTIONSORT_THRESHOLD (8)
+#endif
+#if defined(SS_BLOCKSIZE)
+# if SS_BLOCKSIZE < 0
+#  undef SS_BLOCKSIZE
+#  define SS_BLOCKSIZE (0)
+# elif 32768 <= SS_BLOCKSIZE
+#  undef SS_BLOCKSIZE
+#  define SS_BLOCKSIZE (32767)
+# endif
+#else
+# define SS_BLOCKSIZE (1024)
+#endif
+/* minstacksize = log(SS_BLOCKSIZE) / log(3) * 2 */
+#if SS_BLOCKSIZE == 0
+# if defined(BUILD_DIVSUFSORT64)
+#  define SS_MISORT_STACKSIZE (96)
+# else
+#  define SS_MISORT_STACKSIZE (64)
+# endif
+#elif SS_BLOCKSIZE <= 4096
+# define SS_MISORT_STACKSIZE (16)
+#else
+# define SS_MISORT_STACKSIZE (24)
+#endif
+#if defined(BUILD_DIVSUFSORT64)
+# define SS_SMERGE_STACKSIZE (64)
+#else
+# define SS_SMERGE_STACKSIZE (32)
+#endif
+/* for trsort.c */
+#define TR_INSERTIONSORT_THRESHOLD (8)
+#if defined(BUILD_DIVSUFSORT64)
+# define TR_STACKSIZE (96)
+#else
+# define TR_STACKSIZE (64)
+#endif
+
+
+/*- Macros -*/
+#ifndef SWAP
+# define SWAP(_a, _b) do { t = (_a); (_a) = (_b); (_b) = t; } while(0)
+#endif /* SWAP */
+#ifndef MIN
+# define MIN(_a, _b) (((_a) < (_b)) ? (_a) : (_b))
+#endif /* MIN */
+#ifndef MAX
+# define MAX(_a, _b) (((_a) > (_b)) ? (_a) : (_b))
+#endif /* MAX */
+#define STACK_PUSH(_a, _b, _c, _d)\
+  do {\
+    assert(ssize < STACK_SIZE);\
+    stack[ssize].a = (_a), stack[ssize].b = (_b),\
+    stack[ssize].c = (_c), stack[ssize++].d = (_d);\
+  } while(0)
+#define STACK_PUSH5(_a, _b, _c, _d, _e)\
+  do {\
+    assert(ssize < STACK_SIZE);\
+    stack[ssize].a = (_a), stack[ssize].b = (_b),\
+    stack[ssize].c = (_c), stack[ssize].d = (_d), stack[ssize++].e = (_e);\
+  } while(0)
+#define STACK_POP(_a, _b, _c, _d)\
+  do {\
+    assert(0 <= ssize);\
+    if(ssize == 0) { return; }\
+    (_a) = stack[--ssize].a, (_b) = stack[ssize].b,\
+    (_c) = stack[ssize].c, (_d) = stack[ssize].d;\
+  } while(0)
+#define STACK_POP5(_a, _b, _c, _d, _e)\
+  do {\
+    assert(0 <= ssize);\
+    if(ssize == 0) { return; }\
+    (_a) = stack[--ssize].a, (_b) = stack[ssize].b,\
+    (_c) = stack[ssize].c, (_d) = stack[ssize].d, (_e) = stack[ssize].e;\
+  } while(0)
+/* for divsufsort.c */
+#define BUCKET_A(_c0) bucket_A[(_c0)]
+#if ALPHABET_SIZE == 256
+#define BUCKET_B(_c0, _c1) (bucket_B[((_c1) << 8) | (_c0)])
+#define BUCKET_BSTAR(_c0, _c1) (bucket_B[((_c0) << 8) | (_c1)])
+#else
+#define BUCKET_B(_c0, _c1) (bucket_B[(_c1) * ALPHABET_SIZE + (_c0)])
+#define BUCKET_BSTAR(_c0, _c1) (bucket_B[(_c0) * ALPHABET_SIZE + (_c1)])
+#endif
+
+
+/*- Private Prototypes -*/
+/* sssort.c */
+void
+sssort(const sauchar_t *Td, const saidx_t *PA,
+       saidx_t *first, saidx_t *last,
+       saidx_t *buf, saidx_t bufsize,
+       saidx_t depth, saidx_t n, saint_t lastsuffix);
+/* trsort.c */
+void
+trsort(saidx_t *ISA, saidx_t *SA, saidx_t n, saidx_t depth);
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif /* __cplusplus */
+
+#endif /* _DIVSUFSORT_PRIVATE_H */
diff --git a/src/divsufsort/sssort.c b/src/divsufsort/sssort.c
new file mode 100644 (file)
index 0000000..0c3d9ed
--- /dev/null
@@ -0,0 +1,815 @@
+/*
+ * sssort.c for libdivsufsort
+ * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "divsufsort_private.h"
+
+
+/*- Private Functions -*/
+
+static const saint_t lg_table[256]= {
+ -1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+  5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+  6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+  6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
+};
+
+#if (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE)
+
+static inline
+saint_t
+ss_ilg(saidx_t n) {
+#if SS_BLOCKSIZE == 0
+# if defined(BUILD_DIVSUFSORT64)
+  return (n >> 32) ?
+          ((n >> 48) ?
+            ((n >> 56) ?
+              56 + lg_table[(n >> 56) & 0xff] :
+              48 + lg_table[(n >> 48) & 0xff]) :
+            ((n >> 40) ?
+              40 + lg_table[(n >> 40) & 0xff] :
+              32 + lg_table[(n >> 32) & 0xff])) :
+          ((n & 0xffff0000) ?
+            ((n & 0xff000000) ?
+              24 + lg_table[(n >> 24) & 0xff] :
+              16 + lg_table[(n >> 16) & 0xff]) :
+            ((n & 0x0000ff00) ?
+               8 + lg_table[(n >>  8) & 0xff] :
+               0 + lg_table[(n >>  0) & 0xff]));
+# else
+  return (n & 0xffff0000) ?
+          ((n & 0xff000000) ?
+            24 + lg_table[(n >> 24) & 0xff] :
+            16 + lg_table[(n >> 16) & 0xff]) :
+          ((n & 0x0000ff00) ?
+             8 + lg_table[(n >>  8) & 0xff] :
+             0 + lg_table[(n >>  0) & 0xff]);
+# endif
+#elif SS_BLOCKSIZE < 256
+  return lg_table[n];
+#else
+  return (n & 0xff00) ?
+          8 + lg_table[(n >> 8) & 0xff] :
+          0 + lg_table[(n >> 0) & 0xff];
+#endif
+}
+
+#endif /* (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) */
+
+#if SS_BLOCKSIZE != 0
+
+static const saint_t sqq_table[256] = {
+  0,  16,  22,  27,  32,  35,  39,  42,  45,  48,  50,  53,  55,  57,  59,  61,
+ 64,  65,  67,  69,  71,  73,  75,  76,  78,  80,  81,  83,  84,  86,  87,  89,
+ 90,  91,  93,  94,  96,  97,  98,  99, 101, 102, 103, 104, 106, 107, 108, 109,
+110, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126,
+128, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
+143, 144, 144, 145, 146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, 155,
+156, 157, 158, 159, 160, 160, 161, 162, 163, 163, 164, 165, 166, 167, 167, 168,
+169, 170, 170, 171, 172, 173, 173, 174, 175, 176, 176, 177, 178, 178, 179, 180,
+181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191,
+192, 192, 193, 193, 194, 195, 195, 196, 197, 197, 198, 199, 199, 200, 201, 201,
+202, 203, 203, 204, 204, 205, 206, 206, 207, 208, 208, 209, 209, 210, 211, 211,
+212, 212, 213, 214, 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 221,
+221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227, 227, 228, 229, 229, 230,
+230, 231, 231, 232, 232, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238,
+239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 247,
+247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253, 253, 254, 254, 255
+};
+
+static inline
+saidx_t
+ss_isqrt(saidx_t x) {
+  saidx_t y, e;
+
+  if(x >= (SS_BLOCKSIZE * SS_BLOCKSIZE)) { return SS_BLOCKSIZE; }
+  e = (x & 0xffff0000) ?
+        ((x & 0xff000000) ?
+          24 + lg_table[(x >> 24) & 0xff] :
+          16 + lg_table[(x >> 16) & 0xff]) :
+        ((x & 0x0000ff00) ?
+           8 + lg_table[(x >>  8) & 0xff] :
+           0 + lg_table[(x >>  0) & 0xff]);
+
+  if(e >= 16) {
+    y = sqq_table[x >> ((e - 6) - (e & 1))] << ((e >> 1) - 7);
+    if(e >= 24) { y = (y + 1 + x / y) >> 1; }
+    y = (y + 1 + x / y) >> 1;
+  } else if(e >= 8) {
+    y = (sqq_table[x >> ((e - 6) - (e & 1))] >> (7 - (e >> 1))) + 1;
+  } else {
+    return sqq_table[x] >> 4;
+  }
+
+  return (x < (y * y)) ? y - 1 : y;
+}
+
+#endif /* SS_BLOCKSIZE != 0 */
+
+
+/*---------------------------------------------------------------------------*/
+
+/* Compares two suffixes. */
+static inline
+saint_t
+ss_compare(const sauchar_t *T,
+           const saidx_t *p1, const saidx_t *p2,
+           saidx_t depth) {
+  const sauchar_t *U1, *U2, *U1n, *U2n;
+
+  for(U1 = T + depth + *p1,
+      U2 = T + depth + *p2,
+      U1n = T + *(p1 + 1) + 2,
+      U2n = T + *(p2 + 1) + 2;
+      (U1 < U1n) && (U2 < U2n) && (*U1 == *U2);
+      ++U1, ++U2) {
+  }
+
+  return U1 < U1n ?
+        (U2 < U2n ? *U1 - *U2 : 1) :
+        (U2 < U2n ? -1 : 0);
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+#if (SS_BLOCKSIZE != 1) && (SS_INSERTIONSORT_THRESHOLD != 1)
+
+/* Insertionsort for small size groups */
+static
+void
+ss_insertionsort(const sauchar_t *T, const saidx_t *PA,
+                 saidx_t *first, saidx_t *last, saidx_t depth) {
+  saidx_t *i, *j;
+  saidx_t t;
+  saint_t r;
+
+  for(i = last - 2; first <= i; --i) {
+    for(t = *i, j = i + 1; 0 < (r = ss_compare(T, PA + t, PA + *j, depth));) {
+      do { *(j - 1) = *j; } while((++j < last) && (*j < 0));
+      if(last <= j) { break; }
+    }
+    if(r == 0) { *j = ~*j; }
+    *(j - 1) = t;
+  }
+}
+
+#endif /* (SS_BLOCKSIZE != 1) && (SS_INSERTIONSORT_THRESHOLD != 1) */
+
+
+/*---------------------------------------------------------------------------*/
+
+#if (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE)
+
+static inline
+void
+ss_fixdown(const sauchar_t *Td, const saidx_t *PA,
+           saidx_t *SA, saidx_t i, saidx_t size) {
+  saidx_t j, k;
+  saidx_t v;
+  saint_t c, d, e;
+
+  for(v = SA[i], c = Td[PA[v]]; (j = 2 * i + 1) < size; SA[i] = SA[k], i = k) {
+    d = Td[PA[SA[k = j++]]];
+    if(d < (e = Td[PA[SA[j]]])) { k = j; d = e; }
+    if(d <= c) { break; }
+  }
+  SA[i] = v;
+}
+
+/* Simple top-down heapsort. */
+static
+void
+ss_heapsort(const sauchar_t *Td, const saidx_t *PA, saidx_t *SA, saidx_t size) {
+  saidx_t i, m;
+  saidx_t t;
+
+  m = size;
+  if((size % 2) == 0) {
+    m--;
+    if(Td[PA[SA[m / 2]]] < Td[PA[SA[m]]]) { SWAP(SA[m], SA[m / 2]); }
+  }
+
+  for(i = m / 2 - 1; 0 <= i; --i) { ss_fixdown(Td, PA, SA, i, m); }
+  if((size % 2) == 0) { SWAP(SA[0], SA[m]); ss_fixdown(Td, PA, SA, 0, m); }
+  for(i = m - 1; 0 < i; --i) {
+    t = SA[0], SA[0] = SA[i];
+    ss_fixdown(Td, PA, SA, 0, i);
+    SA[i] = t;
+  }
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+/* Returns the median of three elements. */
+static inline
+saidx_t *
+ss_median3(const sauchar_t *Td, const saidx_t *PA,
+           saidx_t *v1, saidx_t *v2, saidx_t *v3) {
+  saidx_t *t;
+  if(Td[PA[*v1]] > Td[PA[*v2]]) { SWAP(v1, v2); }
+  if(Td[PA[*v2]] > Td[PA[*v3]]) {
+    if(Td[PA[*v1]] > Td[PA[*v3]]) { return v1; }
+    else { return v3; }
+  }
+  return v2;
+}
+
+/* Returns the median of five elements. */
+static inline
+saidx_t *
+ss_median5(const sauchar_t *Td, const saidx_t *PA,
+           saidx_t *v1, saidx_t *v2, saidx_t *v3, saidx_t *v4, saidx_t *v5) {
+  saidx_t *t;
+  if(Td[PA[*v2]] > Td[PA[*v3]]) { SWAP(v2, v3); }
+  if(Td[PA[*v4]] > Td[PA[*v5]]) { SWAP(v4, v5); }
+  if(Td[PA[*v2]] > Td[PA[*v4]]) { SWAP(v2, v4); SWAP(v3, v5); }
+  if(Td[PA[*v1]] > Td[PA[*v3]]) { SWAP(v1, v3); }
+  if(Td[PA[*v1]] > Td[PA[*v4]]) { SWAP(v1, v4); SWAP(v3, v5); }
+  if(Td[PA[*v3]] > Td[PA[*v4]]) { return v4; }
+  return v3;
+}
+
+/* Returns the pivot element. */
+static inline
+saidx_t *
+ss_pivot(const sauchar_t *Td, const saidx_t *PA, saidx_t *first, saidx_t *last) {
+  saidx_t *middle;
+  saidx_t t;
+
+  t = last - first;
+  middle = first + t / 2;
+
+  if(t <= 512) {
+    if(t <= 32) {
+      return ss_median3(Td, PA, first, middle, last - 1);
+    } else {
+      t >>= 2;
+      return ss_median5(Td, PA, first, first + t, middle, last - 1 - t, last - 1);
+    }
+  }
+  t >>= 3;
+  first  = ss_median3(Td, PA, first, first + t, first + (t << 1));
+  middle = ss_median3(Td, PA, middle - t, middle, middle + t);
+  last   = ss_median3(Td, PA, last - 1 - (t << 1), last - 1 - t, last - 1);
+  return ss_median3(Td, PA, first, middle, last);
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+/* Binary partition for substrings. */
+static inline
+saidx_t *
+ss_partition(const saidx_t *PA,
+                    saidx_t *first, saidx_t *last, saidx_t depth) {
+  saidx_t *a, *b;
+  saidx_t t;
+  for(a = first - 1, b = last;;) {
+    for(; (++a < b) && ((PA[*a] + depth) >= (PA[*a + 1] + 1));) { *a = ~*a; }
+    for(; (a < --b) && ((PA[*b] + depth) <  (PA[*b + 1] + 1));) { }
+    if(b <= a) { break; }
+    t = ~*b;
+    *b = *a;
+    *a = t;
+  }
+  if(first < a) { *first = ~*first; }
+  return a;
+}
+
+/* Multikey introsort for medium size groups. */
+static
+void
+ss_mintrosort(const sauchar_t *T, const saidx_t *PA,
+              saidx_t *first, saidx_t *last,
+              saidx_t depth) {
+#define STACK_SIZE SS_MISORT_STACKSIZE
+  struct { saidx_t *a, *b, c; saint_t d; } stack[STACK_SIZE];
+  const sauchar_t *Td;
+  saidx_t *a, *b, *c, *d, *e, *f;
+  saidx_t s, t;
+  saint_t ssize;
+  saint_t limit;
+  saint_t v, x = 0;
+
+  for(ssize = 0, limit = ss_ilg(last - first);;) {
+
+    if((last - first) <= SS_INSERTIONSORT_THRESHOLD) {
+#if 1 < SS_INSERTIONSORT_THRESHOLD
+      if(1 < (last - first)) { ss_insertionsort(T, PA, first, last, depth); }
+#endif
+      STACK_POP(first, last, depth, limit);
+      continue;
+    }
+
+    Td = T + depth;
+    if(limit-- == 0) { ss_heapsort(Td, PA, first, last - first); }
+    if(limit < 0) {
+      for(a = first + 1, v = Td[PA[*first]]; a < last; ++a) {
+        if((x = Td[PA[*a]]) != v) {
+          if(1 < (a - first)) { break; }
+          v = x;
+          first = a;
+        }
+      }
+      if(Td[PA[*first] - 1] < v) {
+        first = ss_partition(PA, first, a, depth);
+      }
+      if((a - first) <= (last - a)) {
+        if(1 < (a - first)) {
+          STACK_PUSH(a, last, depth, -1);
+          last = a, depth += 1, limit = ss_ilg(a - first);
+        } else {
+          first = a, limit = -1;
+        }
+      } else {
+        if(1 < (last - a)) {
+          STACK_PUSH(first, a, depth + 1, ss_ilg(a - first));
+          first = a, limit = -1;
+        } else {
+          last = a, depth += 1, limit = ss_ilg(a - first);
+        }
+      }
+      continue;
+    }
+
+    /* choose pivot */
+    a = ss_pivot(Td, PA, first, last);
+    v = Td[PA[*a]];
+    SWAP(*first, *a);
+
+    /* partition */
+    for(b = first; (++b < last) && ((x = Td[PA[*b]]) == v);) { }
+    if(((a = b) < last) && (x < v)) {
+      for(; (++b < last) && ((x = Td[PA[*b]]) <= v);) {
+        if(x == v) { SWAP(*b, *a); ++a; }
+      }
+    }
+    for(c = last; (b < --c) && ((x = Td[PA[*c]]) == v);) { }
+    if((b < (d = c)) && (x > v)) {
+      for(; (b < --c) && ((x = Td[PA[*c]]) >= v);) {
+        if(x == v) { SWAP(*c, *d); --d; }
+      }
+    }
+    for(; b < c;) {
+      SWAP(*b, *c);
+      for(; (++b < c) && ((x = Td[PA[*b]]) <= v);) {
+        if(x == v) { SWAP(*b, *a); ++a; }
+      }
+      for(; (b < --c) && ((x = Td[PA[*c]]) >= v);) {
+        if(x == v) { SWAP(*c, *d); --d; }
+      }
+    }
+
+    if(a <= d) {
+      c = b - 1;
+
+      if((s = a - first) > (t = b - a)) { s = t; }
+      for(e = first, f = b - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); }
+      if((s = d - c) > (t = last - d - 1)) { s = t; }
+      for(e = b, f = last - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); }
+
+      a = first + (b - a), c = last - (d - c);
+      b = (v <= Td[PA[*a] - 1]) ? a : ss_partition(PA, a, c, depth);
+
+      if((a - first) <= (last - c)) {
+        if((last - c) <= (c - b)) {
+          STACK_PUSH(b, c, depth + 1, ss_ilg(c - b));
+          STACK_PUSH(c, last, depth, limit);
+          last = a;
+        } else if((a - first) <= (c - b)) {
+          STACK_PUSH(c, last, depth, limit);
+          STACK_PUSH(b, c, depth + 1, ss_ilg(c - b));
+          last = a;
+        } else {
+          STACK_PUSH(c, last, depth, limit);
+          STACK_PUSH(first, a, depth, limit);
+          first = b, last = c, depth += 1, limit = ss_ilg(c - b);
+        }
+      } else {
+        if((a - first) <= (c - b)) {
+          STACK_PUSH(b, c, depth + 1, ss_ilg(c - b));
+          STACK_PUSH(first, a, depth, limit);
+          first = c;
+        } else if((last - c) <= (c - b)) {
+          STACK_PUSH(first, a, depth, limit);
+          STACK_PUSH(b, c, depth + 1, ss_ilg(c - b));
+          first = c;
+        } else {
+          STACK_PUSH(first, a, depth, limit);
+          STACK_PUSH(c, last, depth, limit);
+          first = b, last = c, depth += 1, limit = ss_ilg(c - b);
+        }
+      }
+    } else {
+      limit += 1;
+      if(Td[PA[*first] - 1] < v) {
+        first = ss_partition(PA, first, last, depth);
+        limit = ss_ilg(last - first);
+      }
+      depth += 1;
+    }
+  }
+#undef STACK_SIZE
+}
+
+#endif /* (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) */
+
+
+/*---------------------------------------------------------------------------*/
+
+#if SS_BLOCKSIZE != 0
+
+static inline
+void
+ss_blockswap(saidx_t *a, saidx_t *b, saidx_t n) {
+  saidx_t t;
+  for(; 0 < n; --n, ++a, ++b) {
+    t = *a, *a = *b, *b = t;
+  }
+}
+
+static inline
+void
+ss_rotate(saidx_t *first, saidx_t *middle, saidx_t *last) {
+  saidx_t *a, *b, t;
+  saidx_t l, r;
+  l = middle - first, r = last - middle;
+  for(; (0 < l) && (0 < r);) {
+    if(l == r) { ss_blockswap(first, middle, l); break; }
+    if(l < r) {
+      a = last - 1, b = middle - 1;
+      t = *a;
+      do {
+        *a-- = *b, *b-- = *a;
+        if(b < first) {
+          *a = t;
+          last = a;
+          if((r -= l + 1) <= l) { break; }
+          a -= 1, b = middle - 1;
+          t = *a;
+        }
+      } while(1);
+    } else {
+      a = first, b = middle;
+      t = *a;
+      do {
+        *a++ = *b, *b++ = *a;
+        if(last <= b) {
+          *a = t;
+          first = a + 1;
+          if((l -= r + 1) <= r) { break; }
+          a += 1, b = middle;
+          t = *a;
+        }
+      } while(1);
+    }
+  }
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+static
+void
+ss_inplacemerge(const sauchar_t *T, const saidx_t *PA,
+                saidx_t *first, saidx_t *middle, saidx_t *last,
+                saidx_t depth) {
+  const saidx_t *p;
+  saidx_t *a, *b;
+  saidx_t len, half;
+  saint_t q, r;
+  saint_t x;
+
+  for(;;) {
+    if(*(last - 1) < 0) { x = 1; p = PA + ~*(last - 1); }
+    else                { x = 0; p = PA +  *(last - 1); }
+    for(a = first, len = middle - first, half = len >> 1, r = -1;
+        0 < len;
+        len = half, half >>= 1) {
+      b = a + half;
+      q = ss_compare(T, PA + ((0 <= *b) ? *b : ~*b), p, depth);
+      if(q < 0) {
+        a = b + 1;
+        half -= (len & 1) ^ 1;
+      } else {
+        r = q;
+      }
+    }
+    if(a < middle) {
+      if(r == 0) { *a = ~*a; }
+      ss_rotate(a, middle, last);
+      last -= middle - a;
+      middle = a;
+      if(first == middle) { break; }
+    }
+    --last;
+    if(x != 0) { while(*--last < 0) { } }
+    if(middle == last) { break; }
+  }
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+/* Merge-forward with internal buffer. */
+static
+void
+ss_mergeforward(const sauchar_t *T, const saidx_t *PA,
+                saidx_t *first, saidx_t *middle, saidx_t *last,
+                saidx_t *buf, saidx_t depth) {
+  saidx_t *a, *b, *c, *bufend;
+  saidx_t t;
+  saint_t r;
+
+  bufend = buf + (middle - first) - 1;
+  ss_blockswap(buf, first, middle - first);
+
+  for(t = *(a = first), b = buf, c = middle;;) {
+    r = ss_compare(T, PA + *b, PA + *c, depth);
+    if(r < 0) {
+      do {
+        *a++ = *b;
+        if(bufend <= b) { *bufend = t; return; }
+        *b++ = *a;
+      } while(*b < 0);
+    } else if(r > 0) {
+      do {
+        *a++ = *c, *c++ = *a;
+        if(last <= c) {
+          while(b < bufend) { *a++ = *b, *b++ = *a; }
+          *a = *b, *b = t;
+          return;
+        }
+      } while(*c < 0);
+    } else {
+      *c = ~*c;
+      do {
+        *a++ = *b;
+        if(bufend <= b) { *bufend = t; return; }
+        *b++ = *a;
+      } while(*b < 0);
+
+      do {
+        *a++ = *c, *c++ = *a;
+        if(last <= c) {
+          while(b < bufend) { *a++ = *b, *b++ = *a; }
+          *a = *b, *b = t;
+          return;
+        }
+      } while(*c < 0);
+    }
+  }
+}
+
+/* Merge-backward with internal buffer. */
+static
+void
+ss_mergebackward(const sauchar_t *T, const saidx_t *PA,
+                 saidx_t *first, saidx_t *middle, saidx_t *last,
+                 saidx_t *buf, saidx_t depth) {
+  const saidx_t *p1, *p2;
+  saidx_t *a, *b, *c, *bufend;
+  saidx_t t;
+  saint_t r;
+  saint_t x;
+
+  bufend = buf + (last - middle) - 1;
+  ss_blockswap(buf, middle, last - middle);
+
+  x = 0;
+  if(*bufend < 0)       { p1 = PA + ~*bufend; x |= 1; }
+  else                  { p1 = PA +  *bufend; }
+  if(*(middle - 1) < 0) { p2 = PA + ~*(middle - 1); x |= 2; }
+  else                  { p2 = PA +  *(middle - 1); }
+  for(t = *(a = last - 1), b = bufend, c = middle - 1;;) {
+    r = ss_compare(T, p1, p2, depth);
+    if(0 < r) {
+      if(x & 1) { do { *a-- = *b, *b-- = *a; } while(*b < 0); x ^= 1; }
+      *a-- = *b;
+      if(b <= buf) { *buf = t; break; }
+      *b-- = *a;
+      if(*b < 0) { p1 = PA + ~*b; x |= 1; }
+      else       { p1 = PA +  *b; }
+    } else if(r < 0) {
+      if(x & 2) { do { *a-- = *c, *c-- = *a; } while(*c < 0); x ^= 2; }
+      *a-- = *c, *c-- = *a;
+      if(c < first) {
+        while(buf < b) { *a-- = *b, *b-- = *a; }
+        *a = *b, *b = t;
+        break;
+      }
+      if(*c < 0) { p2 = PA + ~*c; x |= 2; }
+      else       { p2 = PA +  *c; }
+    } else {
+      if(x & 1) { do { *a-- = *b, *b-- = *a; } while(*b < 0); x ^= 1; }
+      *a-- = ~*b;
+      if(b <= buf) { *buf = t; break; }
+      *b-- = *a;
+      if(x & 2) { do { *a-- = *c, *c-- = *a; } while(*c < 0); x ^= 2; }
+      *a-- = *c, *c-- = *a;
+      if(c < first) {
+        while(buf < b) { *a-- = *b, *b-- = *a; }
+        *a = *b, *b = t;
+        break;
+      }
+      if(*b < 0) { p1 = PA + ~*b; x |= 1; }
+      else       { p1 = PA +  *b; }
+      if(*c < 0) { p2 = PA + ~*c; x |= 2; }
+      else       { p2 = PA +  *c; }
+    }
+  }
+}
+
+/* D&C based merge. */
+static
+void
+ss_swapmerge(const sauchar_t *T, const saidx_t *PA,
+             saidx_t *first, saidx_t *middle, saidx_t *last,
+             saidx_t *buf, saidx_t bufsize, saidx_t depth) {
+#define STACK_SIZE SS_SMERGE_STACKSIZE
+#define GETIDX(a) ((0 <= (a)) ? (a) : (~(a)))
+#define MERGE_CHECK(a, b, c)\
+  do {\
+    if(((c) & 1) ||\
+       (((c) & 2) && (ss_compare(T, PA + GETIDX(*((a) - 1)), PA + *(a), depth) == 0))) {\
+      *(a) = ~*(a);\
+    }\
+    if(((c) & 4) && ((ss_compare(T, PA + GETIDX(*((b) - 1)), PA + *(b), depth) == 0))) {\
+      *(b) = ~*(b);\
+    }\
+  } while(0)
+  struct { saidx_t *a, *b, *c; saint_t d; } stack[STACK_SIZE];
+  saidx_t *l, *r, *lm, *rm;
+  saidx_t m, len, half;
+  saint_t ssize;
+  saint_t check, next;
+
+  for(check = 0, ssize = 0;;) {
+    if((last - middle) <= bufsize) {
+      if((first < middle) && (middle < last)) {
+        ss_mergebackward(T, PA, first, middle, last, buf, depth);
+      }
+      MERGE_CHECK(first, last, check);
+      STACK_POP(first, middle, last, check);
+      continue;
+    }
+
+    if((middle - first) <= bufsize) {
+      if(first < middle) {
+        ss_mergeforward(T, PA, first, middle, last, buf, depth);
+      }
+      MERGE_CHECK(first, last, check);
+      STACK_POP(first, middle, last, check);
+      continue;
+    }
+
+    for(m = 0, len = MIN(middle - first, last - middle), half = len >> 1;
+        0 < len;
+        len = half, half >>= 1) {
+      if(ss_compare(T, PA + GETIDX(*(middle + m + half)),
+                       PA + GETIDX(*(middle - m - half - 1)), depth) < 0) {
+        m += half + 1;
+        half -= (len & 1) ^ 1;
+      }
+    }
+
+    if(0 < m) {
+      lm = middle - m, rm = middle + m;
+      ss_blockswap(lm, middle, m);
+      l = r = middle, next = 0;
+      if(rm < last) {
+        if(*rm < 0) {
+          *rm = ~*rm;
+          if(first < lm) { for(; *--l < 0;) { } next |= 4; }
+          next |= 1;
+        } else if(first < lm) {
+          for(; *r < 0; ++r) { }
+          next |= 2;
+        }
+      }
+
+      if((l - first) <= (last - r)) {
+        STACK_PUSH(r, rm, last, (next & 3) | (check & 4));
+        middle = lm, last = l, check = (check & 3) | (next & 4);
+      } else {
+        if((next & 2) && (r == middle)) { next ^= 6; }
+        STACK_PUSH(first, lm, l, (check & 3) | (next & 4));
+        first = r, middle = rm, check = (next & 3) | (check & 4);
+      }
+    } else {
+      if(ss_compare(T, PA + GETIDX(*(middle - 1)), PA + *middle, depth) == 0) {
+        *middle = ~*middle;
+      }
+      MERGE_CHECK(first, last, check);
+      STACK_POP(first, middle, last, check);
+    }
+  }
+#undef STACK_SIZE
+}
+
+#endif /* SS_BLOCKSIZE != 0 */
+
+
+/*---------------------------------------------------------------------------*/
+
+/*- Function -*/
+
+/* Substring sort */
+void
+sssort(const sauchar_t *T, const saidx_t *PA,
+       saidx_t *first, saidx_t *last,
+       saidx_t *buf, saidx_t bufsize,
+       saidx_t depth, saidx_t n, saint_t lastsuffix) {
+  saidx_t *a;
+#if SS_BLOCKSIZE != 0
+  saidx_t *b, *middle, *curbuf;
+  saidx_t j, k, curbufsize, limit;
+#endif
+  saidx_t i;
+
+  if(lastsuffix != 0) { ++first; }
+
+#if SS_BLOCKSIZE == 0
+  ss_mintrosort(T, PA, first, last, depth);
+#else
+  if((bufsize < SS_BLOCKSIZE) &&
+      (bufsize < (last - first)) &&
+      (bufsize < (limit = ss_isqrt(last - first)))) {
+    if(SS_BLOCKSIZE < limit) { limit = SS_BLOCKSIZE; }
+    buf = middle = last - limit, bufsize = limit;
+  } else {
+    middle = last, limit = 0;
+  }
+  for(a = first, i = 0; SS_BLOCKSIZE < (middle - a); a += SS_BLOCKSIZE, ++i) {
+#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE
+    ss_mintrosort(T, PA, a, a + SS_BLOCKSIZE, depth);
+#elif 1 < SS_BLOCKSIZE
+    ss_insertionsort(T, PA, a, a + SS_BLOCKSIZE, depth);
+#endif
+    curbufsize = last - (a + SS_BLOCKSIZE);
+    curbuf = a + SS_BLOCKSIZE;
+    if(curbufsize <= bufsize) { curbufsize = bufsize, curbuf = buf; }
+    for(b = a, k = SS_BLOCKSIZE, j = i; j & 1; b -= k, k <<= 1, j >>= 1) {
+      ss_swapmerge(T, PA, b - k, b, b + k, curbuf, curbufsize, depth);
+    }
+  }
+#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE
+  ss_mintrosort(T, PA, a, middle, depth);
+#elif 1 < SS_BLOCKSIZE
+  ss_insertionsort(T, PA, a, middle, depth);
+#endif
+  for(k = SS_BLOCKSIZE; i != 0; k <<= 1, i >>= 1) {
+    if(i & 1) {
+      ss_swapmerge(T, PA, a - k, a, middle, buf, bufsize, depth);
+      a -= k;
+    }
+  }
+  if(limit != 0) {
+#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE
+    ss_mintrosort(T, PA, middle, last, depth);
+#elif 1 < SS_BLOCKSIZE
+    ss_insertionsort(T, PA, middle, last, depth);
+#endif
+    ss_inplacemerge(T, PA, first, middle, last, depth);
+  }
+#endif
+
+  if(lastsuffix != 0) {
+    /* Insert last type B* suffix. */
+    saidx_t PAi[2]; PAi[0] = PA[*(first - 1)], PAi[1] = n - 2;
+    for(a = first, i = *(first - 1);
+        (a < last) && ((*a < 0) || (0 < ss_compare(T, &(PAi[0]), PA + *a, depth)));
+        ++a) {
+      *(a - 1) = *a;
+    }
+    *(a - 1) = i;
+  }
+}
diff --git a/src/divsufsort/trsort.c b/src/divsufsort/trsort.c
new file mode 100644 (file)
index 0000000..f954378
--- /dev/null
@@ -0,0 +1,586 @@
+/*
+ * trsort.c for libdivsufsort
+ * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "divsufsort_private.h"
+
+
+/*- Private Functions -*/
+
+static const saint_t lg_table[256]= {
+ -1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+  5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+  6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+  6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
+};
+
+static inline
+saint_t
+tr_ilg(saidx_t n) {
+#if defined(BUILD_DIVSUFSORT64)
+  return (n >> 32) ?
+          ((n >> 48) ?
+            ((n >> 56) ?
+              56 + lg_table[(n >> 56) & 0xff] :
+              48 + lg_table[(n >> 48) & 0xff]) :
+            ((n >> 40) ?
+              40 + lg_table[(n >> 40) & 0xff] :
+              32 + lg_table[(n >> 32) & 0xff])) :
+          ((n & 0xffff0000) ?
+            ((n & 0xff000000) ?
+              24 + lg_table[(n >> 24) & 0xff] :
+              16 + lg_table[(n >> 16) & 0xff]) :
+            ((n & 0x0000ff00) ?
+               8 + lg_table[(n >>  8) & 0xff] :
+               0 + lg_table[(n >>  0) & 0xff]));
+#else
+  return (n & 0xffff0000) ?
+          ((n & 0xff000000) ?
+            24 + lg_table[(n >> 24) & 0xff] :
+            16 + lg_table[(n >> 16) & 0xff]) :
+          ((n & 0x0000ff00) ?
+             8 + lg_table[(n >>  8) & 0xff] :
+             0 + lg_table[(n >>  0) & 0xff]);
+#endif
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+/* Simple insertionsort for small size groups. */
+static
+void
+tr_insertionsort(const saidx_t *ISAd, saidx_t *first, saidx_t *last) {
+  saidx_t *a, *b;
+  saidx_t t, r;
+
+  for(a = first + 1; a < last; ++a) {
+    for(t = *a, b = a - 1; 0 > (r = ISAd[t] - ISAd[*b]);) {
+      do { *(b + 1) = *b; } while((first <= --b) && (*b < 0));
+      if(b < first) { break; }
+    }
+    if(r == 0) { *b = ~*b; }
+    *(b + 1) = t;
+  }
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+static inline
+void
+tr_fixdown(const saidx_t *ISAd, saidx_t *SA, saidx_t i, saidx_t size) {
+  saidx_t j, k;
+  saidx_t v;
+  saidx_t c, d, e;
+
+  for(v = SA[i], c = ISAd[v]; (j = 2 * i + 1) < size; SA[i] = SA[k], i = k) {
+    d = ISAd[SA[k = j++]];
+    if(d < (e = ISAd[SA[j]])) { k = j; d = e; }
+    if(d <= c) { break; }
+  }
+  SA[i] = v;
+}
+
+/* Simple top-down heapsort. */
+static
+void
+tr_heapsort(const saidx_t *ISAd, saidx_t *SA, saidx_t size) {
+  saidx_t i, m;
+  saidx_t t;
+
+  m = size;
+  if((size % 2) == 0) {
+    m--;
+    if(ISAd[SA[m / 2]] < ISAd[SA[m]]) { SWAP(SA[m], SA[m / 2]); }
+  }
+
+  for(i = m / 2 - 1; 0 <= i; --i) { tr_fixdown(ISAd, SA, i, m); }
+  if((size % 2) == 0) { SWAP(SA[0], SA[m]); tr_fixdown(ISAd, SA, 0, m); }
+  for(i = m - 1; 0 < i; --i) {
+    t = SA[0], SA[0] = SA[i];
+    tr_fixdown(ISAd, SA, 0, i);
+    SA[i] = t;
+  }
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+/* Returns the median of three elements. */
+static inline
+saidx_t *
+tr_median3(const saidx_t *ISAd, saidx_t *v1, saidx_t *v2, saidx_t *v3) {
+  saidx_t *t;
+  if(ISAd[*v1] > ISAd[*v2]) { SWAP(v1, v2); }
+  if(ISAd[*v2] > ISAd[*v3]) {
+    if(ISAd[*v1] > ISAd[*v3]) { return v1; }
+    else { return v3; }
+  }
+  return v2;
+}
+
+/* Returns the median of five elements. */
+static inline
+saidx_t *
+tr_median5(const saidx_t *ISAd,
+           saidx_t *v1, saidx_t *v2, saidx_t *v3, saidx_t *v4, saidx_t *v5) {
+  saidx_t *t;
+  if(ISAd[*v2] > ISAd[*v3]) { SWAP(v2, v3); }
+  if(ISAd[*v4] > ISAd[*v5]) { SWAP(v4, v5); }
+  if(ISAd[*v2] > ISAd[*v4]) { SWAP(v2, v4); SWAP(v3, v5); }
+  if(ISAd[*v1] > ISAd[*v3]) { SWAP(v1, v3); }
+  if(ISAd[*v1] > ISAd[*v4]) { SWAP(v1, v4); SWAP(v3, v5); }
+  if(ISAd[*v3] > ISAd[*v4]) { return v4; }
+  return v3;
+}
+
+/* Returns the pivot element. */
+static inline
+saidx_t *
+tr_pivot(const saidx_t *ISAd, saidx_t *first, saidx_t *last) {
+  saidx_t *middle;
+  saidx_t t;
+
+  t = last - first;
+  middle = first + t / 2;
+
+  if(t <= 512) {
+    if(t <= 32) {
+      return tr_median3(ISAd, first, middle, last - 1);
+    } else {
+      t >>= 2;
+      return tr_median5(ISAd, first, first + t, middle, last - 1 - t, last - 1);
+    }
+  }
+  t >>= 3;
+  first  = tr_median3(ISAd, first, first + t, first + (t << 1));
+  middle = tr_median3(ISAd, middle - t, middle, middle + t);
+  last   = tr_median3(ISAd, last - 1 - (t << 1), last - 1 - t, last - 1);
+  return tr_median3(ISAd, first, middle, last);
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+typedef struct _trbudget_t trbudget_t;
+struct _trbudget_t {
+  saidx_t chance;
+  saidx_t remain;
+  saidx_t incval;
+  saidx_t count;
+};
+
+static inline
+void
+trbudget_init(trbudget_t *budget, saidx_t chance, saidx_t incval) {
+  budget->chance = chance;
+  budget->remain = budget->incval = incval;
+}
+
+static inline
+saint_t
+trbudget_check(trbudget_t *budget, saidx_t size) {
+  if(size <= budget->remain) { budget->remain -= size; return 1; }
+  if(budget->chance == 0) { budget->count += size; return 0; }
+  budget->remain += budget->incval - size;
+  budget->chance -= 1;
+  return 1;
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+static inline
+void
+tr_partition(const saidx_t *ISAd,
+             saidx_t *first, saidx_t *middle, saidx_t *last,
+             saidx_t **pa, saidx_t **pb, saidx_t v) {
+  saidx_t *a, *b, *c, *d, *e, *f;
+  saidx_t t, s;
+  saidx_t x = 0;
+
+  for(b = middle - 1; (++b < last) && ((x = ISAd[*b]) == v);) { }
+  if(((a = b) < last) && (x < v)) {
+    for(; (++b < last) && ((x = ISAd[*b]) <= v);) {
+      if(x == v) { SWAP(*b, *a); ++a; }
+    }
+  }
+  for(c = last; (b < --c) && ((x = ISAd[*c]) == v);) { }
+  if((b < (d = c)) && (x > v)) {
+    for(; (b < --c) && ((x = ISAd[*c]) >= v);) {
+      if(x == v) { SWAP(*c, *d); --d; }
+    }
+  }
+  for(; b < c;) {
+    SWAP(*b, *c);
+    for(; (++b < c) && ((x = ISAd[*b]) <= v);) {
+      if(x == v) { SWAP(*b, *a); ++a; }
+    }
+    for(; (b < --c) && ((x = ISAd[*c]) >= v);) {
+      if(x == v) { SWAP(*c, *d); --d; }
+    }
+  }
+
+  if(a <= d) {
+    c = b - 1;
+    if((s = a - first) > (t = b - a)) { s = t; }
+    for(e = first, f = b - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); }
+    if((s = d - c) > (t = last - d - 1)) { s = t; }
+    for(e = b, f = last - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); }
+    first += (b - a), last -= (d - c);
+  }
+  *pa = first, *pb = last;
+}
+
+static
+void
+tr_copy(saidx_t *ISA, const saidx_t *SA,
+        saidx_t *first, saidx_t *a, saidx_t *b, saidx_t *last,
+        saidx_t depth) {
+  /* sort suffixes of middle partition
+     by using sorted order of suffixes of left and right partition. */
+  saidx_t *c, *d, *e;
+  saidx_t s, v;
+
+  v = b - SA - 1;
+  for(c = first, d = a - 1; c <= d; ++c) {
+    if((0 <= (s = *c - depth)) && (ISA[s] == v)) {
+      *++d = s;
+      ISA[s] = d - SA;
+    }
+  }
+  for(c = last - 1, e = d + 1, d = b; e < d; --c) {
+    if((0 <= (s = *c - depth)) && (ISA[s] == v)) {
+      *--d = s;
+      ISA[s] = d - SA;
+    }
+  }
+}
+
+static
+void
+tr_partialcopy(saidx_t *ISA, const saidx_t *SA,
+               saidx_t *first, saidx_t *a, saidx_t *b, saidx_t *last,
+               saidx_t depth) {
+  saidx_t *c, *d, *e;
+  saidx_t s, v;
+  saidx_t rank, lastrank, newrank = -1;
+
+  v = b - SA - 1;
+  lastrank = -1;
+  for(c = first, d = a - 1; c <= d; ++c) {
+    if((0 <= (s = *c - depth)) && (ISA[s] == v)) {
+      *++d = s;
+      rank = ISA[s + depth];
+      if(lastrank != rank) { lastrank = rank; newrank = d - SA; }
+      ISA[s] = newrank;
+    }
+  }
+
+  lastrank = -1;
+  for(e = d; first <= e; --e) {
+    rank = ISA[*e];
+    if(lastrank != rank) { lastrank = rank; newrank = e - SA; }
+    if(newrank != rank) { ISA[*e] = newrank; }
+  }
+
+  lastrank = -1;
+  for(c = last - 1, e = d + 1, d = b; e < d; --c) {
+    if((0 <= (s = *c - depth)) && (ISA[s] == v)) {
+      *--d = s;
+      rank = ISA[s + depth];
+      if(lastrank != rank) { lastrank = rank; newrank = d - SA; }
+      ISA[s] = newrank;
+    }
+  }
+}
+
+static
+void
+tr_introsort(saidx_t *ISA, const saidx_t *ISAd,
+             saidx_t *SA, saidx_t *first, saidx_t *last,
+             trbudget_t *budget) {
+#define STACK_SIZE TR_STACKSIZE
+  struct { const saidx_t *a; saidx_t *b, *c; saint_t d, e; }stack[STACK_SIZE];
+  saidx_t *a, *b, *c;
+  saidx_t t;
+  saidx_t v, x = 0;
+  saidx_t incr = ISAd - ISA;
+  saint_t limit, next;
+  saint_t ssize, trlink = -1;
+
+  for(ssize = 0, limit = tr_ilg(last - first);;) {
+
+    if(limit < 0) {
+      if(limit == -1) {
+        /* tandem repeat partition */
+        tr_partition(ISAd - incr, first, first, last, &a, &b, last - SA - 1);
+
+        /* update ranks */
+        if(a < last) {
+          for(c = first, v = a - SA - 1; c < a; ++c) { ISA[*c] = v; }
+        }
+        if(b < last) {
+          for(c = a, v = b - SA - 1; c < b; ++c) { ISA[*c] = v; }
+        }
+
+        /* push */
+        if(1 < (b - a)) {
+          STACK_PUSH5(NULL, a, b, 0, 0);
+          STACK_PUSH5(ISAd - incr, first, last, -2, trlink);
+          trlink = ssize - 2;
+        }
+        if((a - first) <= (last - b)) {
+          if(1 < (a - first)) {
+            STACK_PUSH5(ISAd, b, last, tr_ilg(last - b), trlink);
+            last = a, limit = tr_ilg(a - first);
+          } else if(1 < (last - b)) {
+            first = b, limit = tr_ilg(last - b);
+          } else {
+            STACK_POP5(ISAd, first, last, limit, trlink);
+          }
+        } else {
+          if(1 < (last - b)) {
+            STACK_PUSH5(ISAd, first, a, tr_ilg(a - first), trlink);
+            first = b, limit = tr_ilg(last - b);
+          } else if(1 < (a - first)) {
+            last = a, limit = tr_ilg(a - first);
+          } else {
+            STACK_POP5(ISAd, first, last, limit, trlink);
+          }
+        }
+      } else if(limit == -2) {
+        /* tandem repeat copy */
+        a = stack[--ssize].b, b = stack[ssize].c;
+        if(stack[ssize].d == 0) {
+          tr_copy(ISA, SA, first, a, b, last, ISAd - ISA);
+        } else {
+          if(0 <= trlink) { stack[trlink].d = -1; }
+          tr_partialcopy(ISA, SA, first, a, b, last, ISAd - ISA);
+        }
+        STACK_POP5(ISAd, first, last, limit, trlink);
+      } else {
+        /* sorted partition */
+        if(0 <= *first) {
+          a = first;
+          do { ISA[*a] = a - SA; } while((++a < last) && (0 <= *a));
+          first = a;
+        }
+        if(first < last) {
+          a = first; do { *a = ~*a; } while(*++a < 0);
+          next = (ISA[*a] != ISAd[*a]) ? tr_ilg(a - first + 1) : -1;
+          if(++a < last) { for(b = first, v = a - SA - 1; b < a; ++b) { ISA[*b] = v; } }
+
+          /* push */
+          if(trbudget_check(budget, a - first)) {
+            if((a - first) <= (last - a)) {
+              STACK_PUSH5(ISAd, a, last, -3, trlink);
+              ISAd += incr, last = a, limit = next;
+            } else {
+              if(1 < (last - a)) {
+                STACK_PUSH5(ISAd + incr, first, a, next, trlink);
+                first = a, limit = -3;
+              } else {
+                ISAd += incr, last = a, limit = next;
+              }
+            }
+          } else {
+            if(0 <= trlink) { stack[trlink].d = -1; }
+            if(1 < (last - a)) {
+              first = a, limit = -3;
+            } else {
+              STACK_POP5(ISAd, first, last, limit, trlink);
+            }
+          }
+        } else {
+          STACK_POP5(ISAd, first, last, limit, trlink);
+        }
+      }
+      continue;
+    }
+
+    if((last - first) <= TR_INSERTIONSORT_THRESHOLD) {
+      tr_insertionsort(ISAd, first, last);
+      limit = -3;
+      continue;
+    }
+
+    if(limit-- == 0) {
+      tr_heapsort(ISAd, first, last - first);
+      for(a = last - 1; first < a; a = b) {
+        for(x = ISAd[*a], b = a - 1; (first <= b) && (ISAd[*b] == x); --b) { *b = ~*b; }
+      }
+      limit = -3;
+      continue;
+    }
+
+    /* choose pivot */
+    a = tr_pivot(ISAd, first, last);
+    SWAP(*first, *a);
+    v = ISAd[*first];
+
+    /* partition */
+    tr_partition(ISAd, first, first + 1, last, &a, &b, v);
+    if((last - first) != (b - a)) {
+      next = (ISA[*a] != v) ? tr_ilg(b - a) : -1;
+
+      /* update ranks */
+      for(c = first, v = a - SA - 1; c < a; ++c) { ISA[*c] = v; }
+      if(b < last) { for(c = a, v = b - SA - 1; c < b; ++c) { ISA[*c] = v; } }
+
+      /* push */
+      if((1 < (b - a)) && (trbudget_check(budget, b - a))) {
+        if((a - first) <= (last - b)) {
+          if((last - b) <= (b - a)) {
+            if(1 < (a - first)) {
+              STACK_PUSH5(ISAd + incr, a, b, next, trlink);
+              STACK_PUSH5(ISAd, b, last, limit, trlink);
+              last = a;
+            } else if(1 < (last - b)) {
+              STACK_PUSH5(ISAd + incr, a, b, next, trlink);
+              first = b;
+            } else {
+              ISAd += incr, first = a, last = b, limit = next;
+            }
+          } else if((a - first) <= (b - a)) {
+            if(1 < (a - first)) {
+              STACK_PUSH5(ISAd, b, last, limit, trlink);
+              STACK_PUSH5(ISAd + incr, a, b, next, trlink);
+              last = a;
+            } else {
+              STACK_PUSH5(ISAd, b, last, limit, trlink);
+              ISAd += incr, first = a, last = b, limit = next;
+            }
+          } else {
+            STACK_PUSH5(ISAd, b, last, limit, trlink);
+            STACK_PUSH5(ISAd, first, a, limit, trlink);
+            ISAd += incr, first = a, last = b, limit = next;
+          }
+        } else {
+          if((a - first) <= (b - a)) {
+            if(1 < (last - b)) {
+              STACK_PUSH5(ISAd + incr, a, b, next, trlink);
+              STACK_PUSH5(ISAd, first, a, limit, trlink);
+              first = b;
+            } else if(1 < (a - first)) {
+              STACK_PUSH5(ISAd + incr, a, b, next, trlink);
+              last = a;
+            } else {
+              ISAd += incr, first = a, last = b, limit = next;
+            }
+          } else if((last - b) <= (b - a)) {
+            if(1 < (last - b)) {
+              STACK_PUSH5(ISAd, first, a, limit, trlink);
+              STACK_PUSH5(ISAd + incr, a, b, next, trlink);
+              first = b;
+            } else {
+              STACK_PUSH5(ISAd, first, a, limit, trlink);
+              ISAd += incr, first = a, last = b, limit = next;
+            }
+          } else {
+            STACK_PUSH5(ISAd, first, a, limit, trlink);
+            STACK_PUSH5(ISAd, b, last, limit, trlink);
+            ISAd += incr, first = a, last = b, limit = next;
+          }
+        }
+      } else {
+        if((1 < (b - a)) && (0 <= trlink)) { stack[trlink].d = -1; }
+        if((a - first) <= (last - b)) {
+          if(1 < (a - first)) {
+            STACK_PUSH5(ISAd, b, last, limit, trlink);
+            last = a;
+          } else if(1 < (last - b)) {
+            first = b;
+          } else {
+            STACK_POP5(ISAd, first, last, limit, trlink);
+          }
+        } else {
+          if(1 < (last - b)) {
+            STACK_PUSH5(ISAd, first, a, limit, trlink);
+            first = b;
+          } else if(1 < (a - first)) {
+            last = a;
+          } else {
+            STACK_POP5(ISAd, first, last, limit, trlink);
+          }
+        }
+      }
+    } else {
+      if(trbudget_check(budget, last - first)) {
+        limit = tr_ilg(last - first), ISAd += incr;
+      } else {
+        if(0 <= trlink) { stack[trlink].d = -1; }
+        STACK_POP5(ISAd, first, last, limit, trlink);
+      }
+    }
+  }
+#undef STACK_SIZE
+}
+
+
+
+/*---------------------------------------------------------------------------*/
+
+/*- Function -*/
+
+/* Tandem repeat sort */
+void
+trsort(saidx_t *ISA, saidx_t *SA, saidx_t n, saidx_t depth) {
+  saidx_t *ISAd;
+  saidx_t *first, *last;
+  trbudget_t budget;
+  saidx_t t, skip, unsorted;
+
+  trbudget_init(&budget, tr_ilg(n) * 2 / 3, n);
+/*  trbudget_init(&budget, tr_ilg(n) * 3 / 4, n); */
+  for(ISAd = ISA + depth; -n < *SA; ISAd += ISAd - ISA) {
+    first = SA;
+    skip = 0;
+    unsorted = 0;
+    do {
+      if((t = *first) < 0) { first -= t; skip += t; }
+      else {
+        if(skip != 0) { *(first + skip) = skip; skip = 0; }
+        last = SA + ISA[t] + 1;
+        if(1 < (last - first)) {
+          budget.count = 0;
+          tr_introsort(ISA, ISAd, SA, first, last, &budget);
+          if(budget.count != 0) { unsorted += budget.count; }
+          else { skip = first - last; }
+        } else if((last - first) == 1) {
+          skip = -1;
+        }
+        first = last;
+      }
+    } while(first < (SA + n));
+    if(skip != 0) { *(first + skip) = skip; }
+    if(unsorted == 0) { break; }
+  }
+}
index a7bca00..dfd6256 100644 (file)
  * - lzx_position_base is an index to the position slot bases
  * - lzx_extra_bits states how many bits of offset-from-base data is needed.
  */
-const u8 lzx_extra_bits[LZX_NUM_POSITION_SLOTS] = {
+const u8 lzx_extra_bits[] = {
        0 , 0 , 0 , 0 , 1 ,
        1 , 2 , 2 , 3 , 3 ,
        4 , 4 , 5 , 5 , 6 ,
        6 , 7 , 7 , 8 , 8 ,
        9 , 9 , 10, 10, 11,
        11, 12, 12, 13, 13,
-       /*14, 14, 15, 15, 16,*/
-       /*16, 17, 17, 17, 17,*/
-       /*17, 17, 17, 17, 17,*/
-       /*17, 17, 17, 17, 17,*/
-       /*17*/
+       14, 14, 15, 15, 16,
+       16, 17, 17, 17, 17,
+       17, 17, 17, 17, 17,
+       17, 17, 17, 17, 17,
+       17
 };
 #endif
 
-const u32 lzx_position_base[LZX_NUM_POSITION_SLOTS] = {
+const u32 lzx_position_base[] = {
        0      , 1      , 2      , 3      , 4      ,
        6      , 8      , 12     , 16     , 24     ,
        32     , 48     , 64     , 96     , 128    ,
        192    , 256    , 384    , 512    , 768    ,
        1024   , 1536   , 2048   , 3072   , 4096   ,
        6144   , 8192   , 12288  , 16384  , 24576  ,
-       /*32768  , 49152  , 65536  , 98304  , 131072 ,*/
-       /*196608 , 262144 , 393216 , 524288 , 655360 ,*/
-       /*786432 , 917504 , 1048576, 1179648, 1310720,*/
-       /*1441792, 1572864, 1703936, 1835008, 1966080,*/
-       /*2097152*/
+       32768  , 49152  , 65536  , 98304  , 131072 ,
+       196608 , 262144 , 393216 , 524288 , 655360 ,
+       786432 , 917504 , 1048576, 1179648, 1310720,
+       1441792, 1572864, 1703936, 1835008, 1966080,
+       2097152
 };
 
index a6883ac..a31fc2a 100644 (file)
  * - LZX preprocesses the data before attempting to compress it.
  * - LZX uses a "main" alphabet which combines literals and matches, with the
  *   match symbols containing a "length header" (giving all or part of the match
- *   length) and a "position footer" (giving, roughly speaking, the order of
+ *   length) and a "position slot" (giving, roughly speaking, the order of
  *   magnitude of the match offset).
  * - LZX does not have static Huffman blocks; however it does have two types of
  *   dynamic Huffman blocks ("aligned offset" and "verbatim").
  * - LZX has a minimum match length of 2 rather than 3.
- * - In LZX, match offsets 0 through 2 actually represent entries in a LRU queue
- *   of match offsets.
+ * - In LZX, match offsets 0 through 2 actually represent entries in an LRU
+ *   queue of match offsets.
  *
  * Algorithms
  * ==========
  * 1. Preprocess the input data to translate the targets of x86 call instructions
  *    to absolute offsets.
  *
- * 2. Determine the best known sequence of LZ77 matches ((offset, length) pairs)
- *    and literal bytes to divide the input into.  Raw match-finding is done
- *    using a very clever binary tree search based on the "Bt3" algorithm from
- *    7-Zip.  Parsing, or match-choosing, is solved essentially as a
- *    minimum-cost path problem, but using a heuristic forward search based on
- *    the Deflate encoder from 7-Zip rather than a more intuitive backward
- *    search, the latter of which would naively require that all matches be
- *    found.  This heuristic search, as well as other heuristics such as limits
- *    on the matches considered, considerably speed up this part of the
- *    algorithm, which is the main bottleneck.  Finally, after matches and
- *    literals are chosen, the needed Huffman codes needed to output them are
- *    built.
- *
- * 3. Up to a certain number of iterations, use the resulting Huffman codes to
- *    refine a cost model and go back to Step #2 to determine an improved
- *    sequence of matches and literals.
+ * 2. Build the suffix array and inverse suffix array for the input data.
+ *
+ * 3. Build the longest common prefix array corresponding to the suffix array.
+ *
+ * 4. For each suffix rank, find the highest lower suffix rank that has a
+ *    lower position, the lowest higher suffix rank that has a lower position,
+ *    and the length of the common prefix shared between each.  (Position =
+ *    index of suffix in original string, rank = index of suffix in suffix
+ *    array.)  This information is later used to link suffix ranks into a
+ *    doubly-linked list for searching the suffix array.
+ *
+ * 5. Set a default cost model for matches/literals.
  *
- * 4. Up to a certain depth, try splitting the current block to see if the
- *    compression ratio can be improved.  This may be the case if parts of the
- *    input differ greatly from each other and could benefit from different
- *    Huffman codes.
+ * 6. Determine the lowest cost sequence of LZ77 matches ((offset, length) pairs)
+ *    and literal bytes to divide the input into.  Raw match-finding is done by
+ *    searching the suffix array using a linked list to avoid considering any
+ *    suffixes that start after the current position.  Each run of the
+ *    match-finder returns the lowest-cost longest match as well as any shorter
+ *    matches that have even lower costs.  Each such run also adds the suffix
+ *    rank of the current position into the linked list being used to search the
+ *    suffix array.  Parsing, or match-choosing, is solved as a minimum-cost
+ *    path problem using a forward "optimal parsing" algorithm based on the
+ *    Deflate encoder from 7-Zip.  This algorithm moves forward calculating the
+ *    minimum cost to reach each byte until either a very long match is found or
+ *    until a position is found at which no matches start or overlap.
  *
- * 5. Output the resulting block(s) using the match/literal sequences and the
- *    Huffman codes that were computed for each block.
+ * 7. Build the Huffman codes needed to output the matches/literals.
+ *
+ * 8. Up to a certain number of iterations, use the resulting Huffman codes to
+ *    refine a cost model and go back to Step #6 to determine an improved
+ *    sequence of matches and literals.
+ *
+ * 9. Output the resulting block using the match/literal sequences and the
+ *    Huffman codes that were computed for the block.
  *
  * Fast algorithm
  * --------------
  * Acknowledgments
  * ===============
  *
- * Acknowledgments to several other open-source projects that made it possible
- * to implement this code:
+ * Acknowledgments to several open-source projects and research papers that made
+ * it possible to implement this code:
+ *
+ * - divsufsort (author: Yuta Mori), for the suffix array construction code.
  *
- * - 7-Zip (author: Igor Pavlov), for the binary tree match-finding
- *   algorithm, the heuristic near-optimal forward match-choosing
- *   algorithm, and the block splitting algorithm.
+ * - "Linear-Time Longest-Common-Prefix Computation in Suffix Arrays and Its
+ *   Applications" (Kasai et al. 2001), for the LCP array computation.
+ *
+ * - "LPF computation revisited" (Crochemore et al. 2009) for the prev and next
+ *   array computations.
+ *
+ * - 7-Zip (author: Igor Pavlov) for the algorithm for forward optimal parsing
+ *   (match-choosing).
  *
  * - zlib (author: Jean-loup Gailly and Mark Adler), for the hash table
  *   match-finding algorithm.
 #include "wimlib/error.h"
 #include "wimlib/lzx.h"
 #include "wimlib/util.h"
+#include <pthread.h>
+#include <math.h>
+#include <string.h>
 
 #ifdef ENABLE_LZX_DEBUG
 #  include <wimlib/decompress.h>
 #endif
 
-#include <string.h>
+#include "divsufsort/divsufsort.h"
 
-/* Experimental parameters not exposed through the API  */
-#define LZX_PARAM_OPTIM_ARRAY_SIZE     1024
-#define LZX_PARAM_ACCOUNT_FOR_LRU      1
-#define LZX_PARAM_DONT_SKIP_MATCHES    0
-#define LZX_PARAM_USE_EMPIRICAL_DEFAULT_COSTS 1
+typedef freq_t input_idx_t;
+typedef u32 sym_cost_t;
+typedef u32 block_cost_t;
+#define INFINITE_SYM_COST      ((sym_cost_t)~0U)
+#define INFINITE_BLOCK_COST    ((block_cost_t)~0U)
+
+#define LZX_OPTIM_ARRAY_SIZE   4096
 
 /* Currently, this constant can't simply be changed because the code currently
  * uses a static number of position slots (and may make other assumptions as
 /* This may be WIM-specific  */
 #define LZX_DEFAULT_BLOCK_SIZE  32768
 
-#define LZX_LZ_HASH_BITS       15
-#define LZX_LZ_HASH_SIZE       (1 << LZX_LZ_HASH_BITS)
-#define LZX_LZ_HASH_MASK       (LZX_LZ_HASH_SIZE - 1)
-#define LZX_LZ_HASH_SHIFT      5
+#define LZX_MAX_CACHE_PER_POS  10
 
 /* Codewords for the LZX main, length, and aligned offset Huffman codes  */
 struct lzx_codewords {
-       u16 main[LZX_MAINTREE_NUM_SYMBOLS];
-       u16 len[LZX_LENTREE_NUM_SYMBOLS];
-       u16 aligned[LZX_ALIGNEDTREE_NUM_SYMBOLS];
+       u16 main[LZX_MAINCODE_NUM_SYMBOLS];
+       u16 len[LZX_LENCODE_NUM_SYMBOLS];
+       u16 aligned[LZX_ALIGNEDCODE_NUM_SYMBOLS];
 };
 
-/* Lengths for the LZX main, length, and aligned offset Huffman codes  */
+/* Codeword lengths (in bits) for the LZX main, length, and aligned offset
+ * Huffman codes.
+ *
+ * A 0 length means the codeword has zero frequency.
+ */
 struct lzx_lens {
-       u8 main[LZX_MAINTREE_NUM_SYMBOLS];
-       u8 len[LZX_LENTREE_NUM_SYMBOLS];
-       u8 aligned[LZX_ALIGNEDTREE_NUM_SYMBOLS];
+       u8 main[LZX_MAINCODE_NUM_SYMBOLS];
+       u8 len[LZX_LENCODE_NUM_SYMBOLS];
+       u8 aligned[LZX_ALIGNEDCODE_NUM_SYMBOLS];
+};
+
+/* Costs for the LZX main, length, and aligned offset Huffman symbols.
+ *
+ * If a codeword has zero frequency, it must still be assigned some nonzero cost
+ * --- generally a high cost, since even if it gets used in the next iteration,
+ * it probably will not be used very times.  */
+struct lzx_costs {
+       sym_cost_t main[LZX_MAINCODE_NUM_SYMBOLS];
+       sym_cost_t len[LZX_LENCODE_NUM_SYMBOLS];
+       sym_cost_t aligned[LZX_ALIGNEDCODE_NUM_SYMBOLS];
 };
 
 /* The LZX main, length, and aligned offset Huffman codes  */
@@ -208,9 +242,9 @@ struct lzx_codes {
 
 /* Tables for tallying symbol frequencies in the three LZX alphabets  */
 struct lzx_freqs {
-       freq_t main[LZX_MAINTREE_NUM_SYMBOLS];
-       freq_t len[LZX_LENTREE_NUM_SYMBOLS];
-       freq_t aligned[LZX_ALIGNEDTREE_NUM_SYMBOLS];
+       freq_t main[LZX_MAINCODE_NUM_SYMBOLS];
+       freq_t len[LZX_LENCODE_NUM_SYMBOLS];
+       freq_t aligned[LZX_ALIGNEDCODE_NUM_SYMBOLS];
 };
 
 /* LZX intermediate match/literal format  */
@@ -227,48 +261,42 @@ struct lzx_match {
         *         (since lzx_extra_bits[LZX_NUM_POSITION_SLOTS - 1] is 17).
         *
         * 0-7     length of match, minus 2.  This can be at most
-        *         (LZX_MAX_MATCH - 2) == 255, so it will fit in 8 bits.  */
+        *         (LZX_MAX_MATCH_LEN - 2) == 255, so it will fit in 8 bits.  */
        u32 data;
 };
 
 /* Raw LZ match/literal format: just a length and offset.
  *
  * The length is the number of bytes of the match, and the offset is the number
- * of bytes back in the input the match is from the matched text.
+ * of bytes back in the input the match is from the current position.
  *
- * If @len < LZX_MIN_MATCH, then it's really just a literal byte and @offset is
+ * If @len < LZX_MIN_MATCH_LEN, then it's really just a literal byte and @offset is
  * meaningless.  */
 struct raw_match {
        u16 len;
-       u16 offset;
+       input_idx_t offset;
 };
 
-/* Specification for a LZX block  */
+/* Specification for an LZX block.  */
 struct lzx_block_spec {
 
-       /* Set to 1 if this block has been split (in two --- we only considser
-        * binary splits).  In such cases the rest of the fields are
-        * unimportant, since the relevant information is rather in the
-        * structures for the sub-blocks.  */
-       u8 is_split : 1;
-
        /* One of the LZX_BLOCKTYPE_* constants indicating which type of this
         * block.  */
-       u8 block_type : 2;
+       int block_type;
 
        /* 0-based position in the window at which this block starts.  */
-       u16 window_pos;
+       input_idx_t window_pos;
 
        /* The number of bytes of uncompressed data this block represents.  */
-       u16 block_size;
+       input_idx_t block_size;
 
        /* The position in the 'chosen_matches' array in the `struct
         * lzx_compressor' at which the match/literal specifications for
         * this block begin.  */
-       unsigned chosen_matches_start_pos;
+       input_idx_t chosen_matches_start_pos;
 
        /* The number of match/literal specifications for this block.  */
-       u16 num_chosen_matches;
+       input_idx_t num_chosen_matches;
 
        /* Huffman codes for this block.  */
        struct lzx_codes codes;
@@ -283,7 +311,7 @@ struct lzx_block_spec {
 struct lzx_optimal {
        /* The approximate minimum cost, in bits, to reach this position in the
         * window which has been found so far.  */
-       u32 cost;
+       block_cost_t cost;
 
        /* The union here is just for clarity, since the fields are used in two
         * slightly different ways.  Initially, the @prev structure is filled in
@@ -295,30 +323,58 @@ struct lzx_optimal {
                        /* Position of the start of the match or literal that
                         * was taken to get to this position in the approximate
                         * minimum-cost parse.  */
-                       u16 link;
+                       input_idx_t link;
 
-                       /* Offset (as in a LZ (length, offset) pair) of the
+                       /* Offset (as in an LZ (length, offset) pair) of the
                         * match or literal that was taken to get to this
                         * position in the approximate minimum-cost parse.  */
-                       u16 match_offset;
+                       input_idx_t match_offset;
                } prev;
                struct {
                        /* Position at which the match or literal starting at
                         * this position ends in the minimum-cost parse.  */
-                       u16 link;
+                       input_idx_t link;
 
-                       /* Offset (as in a LZ (length, offset) pair) of the
+                       /* Offset (as in an LZ (length, offset) pair) of the
                         * match or literal starting at this position in the
                         * approximate minimum-cost parse.  */
-                       u16 match_offset;
+                       input_idx_t match_offset;
                } next;
        };
-#if LZX_PARAM_ACCOUNT_FOR_LRU
+
+       /* The match offset LRU queue that will exist when the approximate
+        * minimum-cost path to reach this position is taken.  */
        struct lzx_lru_queue queue;
-#endif
 };
 
-/* State of the LZX compressor  */
+/* Suffix array link  */
+struct salink {
+       /* Rank of highest ranked suffix that has rank lower than the suffix
+        * corresponding to this structure and either has a lower position
+        * (initially) or has a position lower than the highest position at
+        * which matches have been searched for so far, or -1 if there is no
+        * such suffix.  */
+       input_idx_t prev;
+
+       /* Rank of lowest ranked suffix that has rank greater than the suffix
+        * corresponding to this structure and either has a lower position
+        * (intially) or has a position lower than the highest position at which
+        * matches have been searched for so far, or -1 if there is no such
+        * suffix.  */
+       input_idx_t next;
+
+       /* Length of longest common prefix between the suffix corresponding to
+        * this structure and the suffix with rank @prev, or 0 if @prev is -1.
+        */
+       input_idx_t lcpprev;
+
+       /* Length of longest common prefix between the suffix corresponding to
+        * this structure and the suffix with rank @next, or 0 if @next is -1.
+        */
+       input_idx_t lcpnext;
+};
+
+/* State of the LZX compressor.  */
 struct lzx_compressor {
 
        /* The parameters that were used to create the compressor.  */
@@ -329,8 +385,8 @@ struct lzx_compressor {
         * 0xe8 byte preprocessing is done directly on the data here before
         * further compression.
         *
-        * Note that this compressor does *not* use a sliding window!!!!
-        * It's not needed in the WIM format, since every chunk is compressed
+        * Note that this compressor does *not* use a sliding window!!!!  It's
+        * not needed in the WIM format, since every chunk is compressed
         * independently.  This is by design, to allow random access to the
         * chunks.
         *
@@ -341,27 +397,27 @@ struct lzx_compressor {
 
        /* Number of bytes of data to be compressed, which is the number of
         * bytes of data in @window that are actually valid.  */
-       unsigned window_size;
+       input_idx_t window_size;
 
        /* The current match offset LRU queue.  */
        struct lzx_lru_queue queue;
 
-       /* Space for sequence of matches/literals that were chosen.
-        *
-        * Each LZX_MAX_WINDOW_SIZE-sized portion of this array is used for a
-        * different block splitting level.  */
+       /* Space for the sequences of matches/literals that were chosen for each
+        * block.  */
        struct lzx_match *chosen_matches;
 
-       /* Structures used during block splitting.
-        *
-        * This can be thought of as a binary tree.  block_specs[(1) - 1]
-        * represents to the top-level block (root node), and block_specs[(i*2)
-        * - 1] and block_specs[(i*2+1) - 1] represent the sub-blocks (child
-        * nodes) resulting from a binary split of the block represented by
-        * block_spec[(i) - 1].
-        */
+       struct raw_match *cached_matches;
+       unsigned cached_matches_pos;
+       bool matches_cached;
+
+       /* Information about the LZX blocks the preprocessed input was divided
+        * into.  */
        struct lzx_block_spec *block_specs;
 
+       /* Number of LZX blocks the input was divided into; a.k.a. the number of
+        * elements of @block_specs that are valid.  */
+       unsigned num_blocks;
+
        /* This is simply filled in with zeroes and used to avoid special-casing
         * the output of the first compressed Huffman code, which conceptually
         * has a delta taken from a code with all symbols having zero-length
@@ -369,50 +425,54 @@ struct lzx_compressor {
        struct lzx_codes zero_codes;
 
        /* Slow algorithm only: The current cost model.  */
-       struct lzx_lens costs;
-
-       /* Slow algorithm only:  Table that maps the hash codes for 3 character
-        * sequences to the most recent position that sequence (or a sequence
-        * sharing the same hash code) appeared in the window.  */
-       u16 *hash_tab;
-
-       /* Slow algorithm only:  Table that maps 2-character sequences to the
-        * most recent position that sequence appeared in the window.  */
-       u16 *digram_tab;
+       struct lzx_costs costs;
 
-       /* Slow algorithm only: Table that contains the logical child pointers
-        * in the binary trees in the match-finding code.
+       /* Slow algorithm only: Suffix array for window.
+        * This is a mapping from suffix rank to suffix position.
         *
-        * child_tab[i*2] and child_tab[i*2+1] are the left and right pointers,
-        * respectively, from the binary tree root corresponding to window
-        * position i.  */
-       u16 *child_tab;
-
-       /* Slow algorithm only: Matches that were already found and are saved in
-        * memory for subsequent queries (e.g. when block splitting).  */
-       struct raw_match *cached_matches;
+        * Suffix rank means the index of the suffix in the sorted list of
+        * suffixes, whereas suffix position means the index in the string at
+        * which the suffix starts.
+        */
+       input_idx_t *SA;
 
-       /* Slow algorithm only: Next position in 'cached_matches' to either
-        * return or fill in.  */
-       unsigned cached_matches_pos;
+       /* Slow algorithm only: Inverse suffix array for window.
+        * This is a mapping from suffix position to suffix rank.
+        * In other words, if 0 <= r < window_size, then ISA[SA[r]] == r.  */
+       input_idx_t *ISA;
 
-       /* Slow algorithm only: %true if reading from 'cached_matches'; %false
-        * if writing to 'cached_matches'.  */
-       bool matches_already_found;
+       /* Slow algorithm only: Longest Common Prefix array.  LCP[i] is the
+        * number of initial bytes that the suffixes at positions SA[i - 1] and
+        * SA[i] share.  LCP[0] is undefined.  */
+       input_idx_t *LCP;
 
-       /* Slow algorithm only: Position in window of next match to return.  */
-       unsigned match_window_pos;
+       /* Slow algorithm only: Suffix array links.
+        *
+        * During a linear scan of the input string to find matches, this array
+        * used to keep track of which rank suffixes in the suffix array appear
+        * before the current position.  Instead of searching in the original
+        * suffix array, scans for matches at a given position traverse a linked
+        * list containing only suffixes that appear before that position.  */
+       struct salink *salink;
+
+       /* Slow algorithm only: Position in window of next match to return.
+        * This cannot simply be modified, as the match-finder must still be
+        * synchronized on the same position.  To seek forwards or backwards,
+        * use lzx_lz_skip_bytes() or lzx_lz_rewind_matchfinder(), respectively.
+        */
+       input_idx_t match_window_pos;
 
-       /* Slow algorithm only: No matches returned shall reach past this
-        * position.  */
-       unsigned match_window_end;
+       /* Slow algorithm only: The match-finder shall ensure the length of
+        * matches does not exceed this position in the input.  */
+       input_idx_t match_window_end;
 
        /* Slow algorithm only: Temporary space used for match-choosing
         * algorithm.
         *
-        * The size of this array must be at least LZX_MAX_MATCH but otherwise
-        * is arbitrary.  More space simply allows the match-choosing algorithm
-        * to find better matches (depending on the input, as always).  */
+        * The size of this array must be at least LZX_MAX_MATCH_LEN but
+        * otherwise is arbitrary.  More space simply allows the match-choosing
+        * algorithm to potentially find better matches (depending on the input,
+        * as always).  */
        struct lzx_optimal *optimum;
 
        /* Slow algorithm only: Variables used by the match-choosing algorithm.
@@ -434,8 +494,8 @@ struct lzx_compressor {
  * numbers in the lzx_position_base array to calculate the slot directly from
  * the formatted offset without actually looking at the array.
  */
-static unsigned
-lzx_get_position_slot(unsigned formatted_offset)
+static _always_inline_attribute unsigned
+lzx_get_position_slot_raw(unsigned formatted_offset)
 {
 #if 0
        /*
@@ -457,55 +517,78 @@ lzx_get_position_slot(unsigned formatted_offset)
                 * increases starting at the 655360 entry, and it is >= 2
                 * because the below calculation fails if the most significant
                 * bit is lower than the 2's place. */
-               LZX_ASSERT(2 <= formatted_offset  && formatted_offset < 655360);
+               LZX_ASSERT(2 <= formatted_offset && formatted_offset < 655360);
                unsigned mssb_idx = bsr32(formatted_offset);
                return (mssb_idx << 1) |
                        ((formatted_offset >> (mssb_idx - 1)) & 1);
        }
 }
 
-/* Compute the hash code for the next 3-character sequence in the window.  */
-static unsigned
-lzx_lz_compute_hash(const u8 *window)
+
+/* Returns the LZX position slot that corresponds to a given match offset,
+ * taking into account the recent offset queue (and optionally updating it).  */
+static _always_inline_attribute unsigned
+lzx_get_position_slot(unsigned offset, struct lzx_lru_queue *queue)
 {
-       unsigned hash;
-
-       hash = window[0];
-       hash <<= LZX_LZ_HASH_SHIFT;
-       hash ^= window[1];
-       hash <<= LZX_LZ_HASH_SHIFT;
-       hash ^= window[2];
-       return hash & LZX_LZ_HASH_MASK;
+       unsigned position_slot;
+
+       /* See if the offset was recently used.  */
+       for (unsigned i = 0; i < LZX_NUM_RECENT_OFFSETS; i++) {
+               if (offset == queue->R[i]) {
+                       /* Found it.  */
+
+                       /* Bring the repeat offset to the front of the
+                        * queue.  Note: this is, in fact, not a real
+                        * LRU queue because repeat matches are simply
+                        * swapped to the front.  */
+                       swap(queue->R[0], queue->R[i]);
+                       /* For recent offsets, the position slot is simply the
+                        * index of the entry in the queue.  */
+
+                       return i;
+               }
+       }
+
+       /* The offset was not recently used; look up its real position slot.  */
+       position_slot = lzx_get_position_slot_raw(offset + LZX_OFFSET_OFFSET);
+
+       /* Bring the new offset to the front of the queue.  */
+       for (unsigned i = LZX_NUM_RECENT_OFFSETS - 1; i > 0; i--)
+               queue->R[i] = queue->R[i - 1];
+       queue->R[0] = offset;
+
+       return position_slot;
 }
 
 /* Build the main, length, and aligned offset Huffman codes used in LZX.
  *
  * This takes as input the frequency tables for each code and produces as output
- * a set of tables that map symbols to codewords and lengths.  */
+ * a set of tables that map symbols to codewords and codeword lengths.  */
 static void
 lzx_make_huffman_codes(const struct lzx_freqs *freqs,
                       struct lzx_codes *codes)
 {
-       make_canonical_huffman_code(LZX_MAINTREE_NUM_SYMBOLS,
-                                   LZX_MAX_CODEWORD_LEN,
+       make_canonical_huffman_code(LZX_MAINCODE_NUM_SYMBOLS,
+                                   LZX_MAX_MAIN_CODEWORD_LEN,
                                    freqs->main,
                                    codes->lens.main,
                                    codes->codewords.main);
 
-       make_canonical_huffman_code(LZX_LENTREE_NUM_SYMBOLS,
-                                   LZX_MAX_CODEWORD_LEN,
+       make_canonical_huffman_code(LZX_LENCODE_NUM_SYMBOLS,
+                                   LZX_MAX_LEN_CODEWORD_LEN,
                                    freqs->len,
                                    codes->lens.len,
                                    codes->codewords.len);
 
-       make_canonical_huffman_code(LZX_ALIGNEDTREE_NUM_SYMBOLS, 8,
+       make_canonical_huffman_code(LZX_ALIGNEDCODE_NUM_SYMBOLS,
+                                   LZX_MAX_ALIGNED_CODEWORD_LEN,
                                    freqs->aligned,
                                    codes->lens.aligned,
                                    codes->codewords.aligned);
 }
 
 /*
- * Output a LZX match.
+ * Output an LZX match.
  *
  * @out:         The bitstream to write the match to.
  * @block_type:  The type of the LZX block (LZX_BLOCKTYPE_ALIGNED or LZX_BLOCKTYPE_VERBATIM)
@@ -525,21 +608,20 @@ lzx_write_match(struct output_bitstream *out, int block_type,
        unsigned position_slot = (match.data >> 25) & 0x3f;     /* 6 bits */
        unsigned len_header;
        unsigned len_footer;
-       unsigned len_pos_header;
        unsigned main_symbol;
        unsigned num_extra_bits;
        unsigned verbatim_bits;
        unsigned aligned_bits;
 
-       /* If the match length is less than MIN_MATCH (= 2) +
+       /* If the match length is less than MIN_MATCH_LEN (= 2) +
         * NUM_PRIMARY_LENS (= 7), the length header contains
-        * the match length minus MIN_MATCH, and there is no
+        * the match length minus MIN_MATCH_LEN, and there is no
         * length footer.
         *
         * Otherwise, the length header contains
         * NUM_PRIMARY_LENS, and the length footer contains
         * the match length minus NUM_PRIMARY_LENS minus
-        * MIN_MATCH. */
+        * MIN_MATCH_LEN. */
        if (match_len_minus_2 < LZX_NUM_PRIMARY_LENS) {
                len_header = match_len_minus_2;
                /* No length footer-- mark it with a special
@@ -550,15 +632,13 @@ lzx_write_match(struct output_bitstream *out, int block_type,
                len_footer = match_len_minus_2 - LZX_NUM_PRIMARY_LENS;
        }
 
-       /* Combine the position slot with the length header into
-        * a single symbol that will be encoded with the main
-        * tree. */
-       len_pos_header = (position_slot << 3) | len_header;
-
-       /* The actual main symbol is offset by LZX_NUM_CHARS because
-        * values under LZX_NUM_CHARS are used to indicate a literal
-        * byte rather than a match. */
-       main_symbol = len_pos_header + LZX_NUM_CHARS;
+       /* Combine the position slot with the length header into a single symbol
+        * that will be encoded with the main tree.
+        *
+        * The actual main symbol is offset by LZX_NUM_CHARS because values
+        * under LZX_NUM_CHARS are used to indicate a literal byte rather than a
+        * match.  */
+       main_symbol = ((position_slot << 3) | len_header) + LZX_NUM_CHARS;
 
        /* Output main symbol. */
        bitstream_put_bits(out, codes->codewords.main[main_symbol],
@@ -597,23 +677,15 @@ lzx_write_match(struct output_bitstream *out, int block_type,
 static unsigned
 lzx_build_precode(const u8 lens[restrict],
                  const u8 prev_lens[restrict],
-                 unsigned num_syms,
-                 freq_t precode_freqs[restrict LZX_PRETREE_NUM_SYMBOLS],
+                 const unsigned num_syms,
+                 freq_t precode_freqs[restrict LZX_PRECODE_NUM_SYMBOLS],
                  u8 output_syms[restrict num_syms],
-                 u8 precode_lens[restrict LZX_PRETREE_NUM_SYMBOLS],
-                 u16 precode_codewords[restrict LZX_PRETREE_NUM_SYMBOLS],
-                 unsigned * num_additional_bits_ret)
+                 u8 precode_lens[restrict LZX_PRECODE_NUM_SYMBOLS],
+                 u16 precode_codewords[restrict LZX_PRECODE_NUM_SYMBOLS],
+                 unsigned *num_additional_bits_ret)
 {
-       unsigned output_syms_idx;
-       unsigned cur_run_len;
-       unsigned i;
-       unsigned len_in_run;
-       unsigned additional_bits;
-       signed char delta;
-       unsigned num_additional_bits = 0;
-
        memset(precode_freqs, 0,
-              LZX_PRETREE_NUM_SYMBOLS * sizeof(precode_freqs[0]));
+              LZX_PRECODE_NUM_SYMBOLS * sizeof(precode_freqs[0]));
 
        /* Since the code word lengths use a form of RLE encoding, the goal here
         * is to find each run of identical lengths when going through them in
@@ -625,11 +697,11 @@ lzx_build_precode(const u8 lens[restrict],
         * output, including RLE codes, not yet encoded using the pre-tree.
         *
         * cur_run_len keeps track of how many code word lengths are in the
-        * current run of identical lengths.
-        */
-       output_syms_idx = 0;
-       cur_run_len = 1;
-       for (i = 1; i <= num_syms; i++) {
+        * current run of identical lengths.  */
+       unsigned output_syms_idx = 0;
+       unsigned cur_run_len = 1;
+       unsigned num_additional_bits = 0;
+       for (unsigned i = 1; i <= num_syms; i++) {
 
                if (i != num_syms && lens[i] == lens[i - 1]) {
                        /* Still in a run--- keep going. */
@@ -642,7 +714,7 @@ lzx_build_precode(const u8 lens[restrict],
 
                /* The symbol that was repeated in the run--- not to be confused
                 * with the length *of* the run (cur_run_len) */
-               len_in_run = lens[i - 1];
+               unsigned len_in_run = lens[i - 1];
 
                if (len_in_run == 0) {
                        /* A run of 0's.  Encode it in as few length
@@ -652,6 +724,7 @@ lzx_build_precode(const u8 lens[restrict],
                         * where n is an uncompressed literal 5-bit integer that
                         * follows the magic length. */
                        while (cur_run_len >= 20) {
+                               unsigned additional_bits;
 
                                additional_bits = min(cur_run_len - 20, 0x1f);
                                num_additional_bits += 5;
@@ -665,6 +738,8 @@ lzx_build_precode(const u8 lens[restrict],
                         * where n is an uncompressed literal 4-bit integer that
                         * follows the magic length. */
                        while (cur_run_len >= 4) {
+                               unsigned additional_bits;
+
                                additional_bits = min(cur_run_len - 4, 0xf);
                                num_additional_bits += 4;
                                precode_freqs[17]++;
@@ -688,6 +763,9 @@ lzx_build_precode(const u8 lens[restrict],
                         * in the run in the previous tree.
                         * */
                        while (cur_run_len >= 4) {
+                               unsigned additional_bits;
+                               signed char delta;
+
                                additional_bits = (cur_run_len > 4);
                                num_additional_bits += 1;
                                delta = (signed char)prev_lens[i - cur_run_len] -
@@ -707,6 +785,8 @@ lzx_build_precode(const u8 lens[restrict],
                 * as a difference from the length of that codeword in the
                 * previous tree. */
                while (cur_run_len > 0) {
+                       signed char delta;
+
                        delta = (signed char)prev_lens[i - cur_run_len] -
                                (signed char)len_in_run;
                        if (delta < 0)
@@ -722,13 +802,12 @@ lzx_build_precode(const u8 lens[restrict],
 
        /* Build the precode from the frequencies of the length symbols. */
 
-       make_canonical_huffman_code(LZX_PRETREE_NUM_SYMBOLS,
-                                   LZX_MAX_CODEWORD_LEN,
+       make_canonical_huffman_code(LZX_PRECODE_NUM_SYMBOLS,
+                                   LZX_MAX_PRE_CODEWORD_LEN,
                                    precode_freqs, precode_lens,
                                    precode_codewords);
 
-       if (num_additional_bits_ret)
-               *num_additional_bits_ret = num_additional_bits;
+       *num_additional_bits_ret = num_additional_bits;
 
        return output_syms_idx;
 }
@@ -740,7 +819,7 @@ lzx_build_precode(const u8 lens[restrict],
  * The Huffman code is represented in the output as a series of path lengths
  * from which the canonical Huffman code can be reconstructed.  The path lengths
  * themselves are compressed using a separate Huffman code, the precode, which
- * consists of LZX_PRETREE_NUM_SYMBOLS (= 20) symbols that cover all possible
+ * consists of LZX_PRECODE_NUM_SYMBOLS (= 20) symbols that cover all possible
  * code lengths, plus extra codes for repeated lengths.  The path lengths of the
  * precode precede the path lengths of the larger code and are uncompressed,
  * consisting of 20 entries of 4 bits each.
@@ -758,13 +837,14 @@ lzx_write_compressed_code(struct output_bitstream *out,
                          const u8 prev_lens[restrict],
                          unsigned num_syms)
 {
-       freq_t precode_freqs[LZX_PRETREE_NUM_SYMBOLS];
+       freq_t precode_freqs[LZX_PRECODE_NUM_SYMBOLS];
        u8 output_syms[num_syms];
-       u8 precode_lens[LZX_PRETREE_NUM_SYMBOLS];
-       u16 precode_codewords[LZX_PRETREE_NUM_SYMBOLS];
+       u8 precode_lens[LZX_PRECODE_NUM_SYMBOLS];
+       u16 precode_codewords[LZX_PRECODE_NUM_SYMBOLS];
        unsigned i;
        unsigned num_output_syms;
        u8 precode_sym;
+       unsigned dummy;
 
        num_output_syms = lzx_build_precode(lens,
                                            prev_lens,
@@ -773,12 +853,12 @@ lzx_write_compressed_code(struct output_bitstream *out,
                                            output_syms,
                                            precode_lens,
                                            precode_codewords,
-                                           NULL);
+                                           &dummy);
 
        /* Write the lengths of the precode codes to the output. */
-       for (i = 0; i < LZX_PRETREE_NUM_SYMBOLS; i++)
+       for (i = 0; i < LZX_PRECODE_NUM_SYMBOLS; i++)
                bitstream_put_bits(out, precode_lens[i],
-                                  LZX_PRETREE_ELEMENT_SIZE);
+                                  LZX_PRECODE_ELEMENT_SIZE);
 
        /* Write the length symbols, encoded with the precode, to the output. */
 
@@ -808,7 +888,7 @@ lzx_write_compressed_code(struct output_bitstream *out,
 }
 
 /*
- * Writes all compressed matches and literal bytes in a LZX block to the the
+ * Writes all compressed matches and literal bytes in an LZX block to the the
  * output bitstream.
  *
  * @ostream
@@ -848,45 +928,44 @@ lzx_write_matches_and_literals(struct output_bitstream *ostream,
        }
 }
 
-
 static void
 lzx_assert_codes_valid(const struct lzx_codes * codes)
 {
 #ifdef ENABLE_LZX_DEBUG
        unsigned i;
 
-       for (i = 0; i < LZX_MAINTREE_NUM_SYMBOLS; i++)
-               LZX_ASSERT(codes->lens.main[i] <= LZX_MAX_CODEWORD_LEN);
+       for (i = 0; i < LZX_MAINCODE_NUM_SYMBOLS; i++)
+               LZX_ASSERT(codes->lens.main[i] <= LZX_MAX_MAIN_CODEWORD_LEN);
 
-       for (i = 0; i < LZX_LENTREE_NUM_SYMBOLS; i++)
-               LZX_ASSERT(codes->lens.len[i] <= LZX_MAX_CODEWORD_LEN);
+       for (i = 0; i < LZX_LENCODE_NUM_SYMBOLS; i++)
+               LZX_ASSERT(codes->lens.len[i] <= LZX_MAX_LEN_CODEWORD_LEN);
 
-       for (i = 0; i < LZX_ALIGNEDTREE_NUM_SYMBOLS; i++)
-               LZX_ASSERT(codes->lens.aligned[i] <= 8);
+       for (i = 0; i < LZX_ALIGNEDCODE_NUM_SYMBOLS; i++)
+               LZX_ASSERT(codes->lens.aligned[i] <= LZX_MAX_ALIGNED_CODEWORD_LEN);
 
        const unsigned tablebits = 10;
        u16 decode_table[(1 << tablebits) +
-                        (2 * max(LZX_MAINTREE_NUM_SYMBOLS, LZX_LENTREE_NUM_SYMBOLS))]
+                        (2 * max(LZX_MAINCODE_NUM_SYMBOLS, LZX_LENCODE_NUM_SYMBOLS))]
                         _aligned_attribute(DECODE_TABLE_ALIGNMENT);
        LZX_ASSERT(0 == make_huffman_decode_table(decode_table,
-                                                 LZX_MAINTREE_NUM_SYMBOLS,
-                                                 tablebits,
+                                                 LZX_MAINCODE_NUM_SYMBOLS,
+                                                 min(tablebits, LZX_MAINCODE_TABLEBITS),
                                                  codes->lens.main,
-                                                 LZX_MAX_CODEWORD_LEN));
+                                                 LZX_MAX_MAIN_CODEWORD_LEN));
        LZX_ASSERT(0 == make_huffman_decode_table(decode_table,
-                                                 LZX_LENTREE_NUM_SYMBOLS,
-                                                 tablebits,
+                                                 LZX_LENCODE_NUM_SYMBOLS,
+                                                 min(tablebits, LZX_LENCODE_TABLEBITS),
                                                  codes->lens.len,
-                                                 LZX_MAX_CODEWORD_LEN));
+                                                 LZX_MAX_LEN_CODEWORD_LEN));
        LZX_ASSERT(0 == make_huffman_decode_table(decode_table,
-                                                 LZX_ALIGNEDTREE_NUM_SYMBOLS,
-                                                 min(tablebits, 6),
+                                                 LZX_ALIGNEDCODE_NUM_SYMBOLS,
+                                                 min(tablebits, LZX_ALIGNEDCODE_TABLEBITS),
                                                  codes->lens.aligned,
-                                                 8));
+                                                 LZX_MAX_ALIGNED_CODEWORD_LEN));
 #endif /* ENABLE_LZX_DEBUG */
 }
 
-/* Write a LZX aligned offset or verbatim block to the output.  */
+/* Write an LZX aligned offset or verbatim block to the output.  */
 static void
 lzx_write_compressed_block(int block_type,
                           unsigned block_size,
@@ -923,9 +1002,9 @@ lzx_write_compressed_block(int block_type,
         * length code, but in fact it is the very first tree to be written
         * (before the main code).  */
        if (block_type == LZX_BLOCKTYPE_ALIGNED)
-               for (i = 0; i < LZX_ALIGNEDTREE_NUM_SYMBOLS; i++)
+               for (i = 0; i < LZX_ALIGNEDCODE_NUM_SYMBOLS; i++)
                        bitstream_put_bits(ostream, codes->lens.aligned[i],
-                                          LZX_ALIGNEDTREE_ELEMENT_SIZE);
+                                          LZX_ALIGNEDCODE_ELEMENT_SIZE);
 
        LZX_DEBUG("Writing main code...");
 
@@ -941,7 +1020,7 @@ lzx_write_compressed_block(int block_type,
        lzx_write_compressed_code(ostream,
                                  codes->lens.main + LZX_NUM_CHARS,
                                  prev_codes->lens.main + LZX_NUM_CHARS,
-                                 LZX_MAINTREE_NUM_SYMBOLS - LZX_NUM_CHARS);
+                                 LZX_MAINCODE_NUM_SYMBOLS - LZX_NUM_CHARS);
 
        LZX_DEBUG("Writing length code...");
 
@@ -949,7 +1028,7 @@ lzx_write_compressed_block(int block_type,
        lzx_write_compressed_code(ostream,
                                  codes->lens.len,
                                  prev_codes->lens.len,
-                                 LZX_LENTREE_NUM_SYMBOLS);
+                                 LZX_LENCODE_NUM_SYMBOLS);
 
        LZX_DEBUG("Writing matches and literals...");
 
@@ -961,30 +1040,19 @@ lzx_write_compressed_block(int block_type,
        LZX_DEBUG("Done writing block.");
 }
 
-/* Write the LZX block of index @block_number, or write its children recursively
- * if it is a split block.
- *
- * @prev_codes is a pointer to the Huffman codes for the most recent block
- * written, or all zeroes if this is the first block.
- *
- * Return a pointer to the Huffman codes for the last block written.  */
-static struct lzx_codes *
-lzx_write_block_recursive(struct lzx_compressor *ctx,
-                         unsigned block_number,
-                         struct lzx_codes * prev_codes,
-                         struct output_bitstream *ostream)
+/* Write out the LZX blocks that were computed.  */
+static void
+lzx_write_all_blocks(struct lzx_compressor *ctx, struct output_bitstream *ostream)
 {
-       struct lzx_block_spec *spec = &ctx->block_specs[block_number - 1];
+       const struct lzx_codes *prev_codes = &ctx->zero_codes;
+       for (unsigned i = 0; i < ctx->num_blocks; i++) {
+               const struct lzx_block_spec *spec = &ctx->block_specs[i];
 
-       if (spec->is_split) {
-               prev_codes = lzx_write_block_recursive(ctx, block_number * 2 + 0,
-                                                      prev_codes, ostream);
-               prev_codes = lzx_write_block_recursive(ctx, block_number * 2 + 1,
-                                                      prev_codes, ostream);
-       } else {
-               LZX_DEBUG("Writing block #%u (type=%d, size=%u, num_chosen_matches=%u)...",
-                         block_number, spec->block_type, spec->block_size,
+               LZX_DEBUG("Writing block %u/%u (type=%d, size=%u, num_chosen_matches=%u)...",
+                         i + 1, ctx->num_blocks,
+                         spec->block_type, spec->block_size,
                          spec->num_chosen_matches);
+
                lzx_write_compressed_block(spec->block_type,
                                           spec->block_size,
                                           &ctx->chosen_matches[spec->chosen_matches_start_pos],
@@ -994,16 +1062,10 @@ lzx_write_block_recursive(struct lzx_compressor *ctx,
                                           ostream);
                prev_codes = &spec->codes;
        }
-       return prev_codes;
-}
-
-/* Write out the LZX blocks that were computed.  */
-static void
-lzx_write_all_blocks(struct lzx_compressor *ctx, struct output_bitstream *ostream)
-{
-       lzx_write_block_recursive(ctx, 1, &ctx->zero_codes, ostream);
 }
 
+/* Constructs an LZX match from a literal byte and updates the main code symbol
+ * frequencies.  */
 static u32
 lzx_record_literal(u8 literal, void *_freqs)
 {
@@ -1014,9 +1076,9 @@ lzx_record_literal(u8 literal, void *_freqs)
        return (u32)literal;
 }
 
-/* Constructs a match from an offset and a length, and updates the LRU queue and
- * the frequency of symbols in the main, length, and aligned offset alphabets.
- * The return value is a 32-bit number that provides the match in an
+/* Constructs an LZX match from an offset and a length, and updates the LRU
+ * queue and the frequency of symbols in the main, length, and aligned offset
+ * alphabets.  The return value is a 32-bit number that provides the match in an
  * intermediate representation documented below.  */
 static u32
 lzx_record_match(unsigned match_offset, unsigned match_len,
@@ -1025,70 +1087,49 @@ lzx_record_match(unsigned match_offset, unsigned match_len,
        struct lzx_freqs *freqs = _freqs;
        struct lzx_lru_queue *queue = _queue;
        unsigned position_slot;
-       unsigned position_footer = 0;
+       unsigned position_footer;
        u32 len_header;
-       u32 len_pos_header;
+       unsigned main_symbol;
        unsigned len_footer;
        unsigned adjusted_match_len;
 
-       LZX_ASSERT(match_len >= LZX_MIN_MATCH && match_len <= LZX_MAX_MATCH);
-
-       /* If possible, encode this offset as a repeated offset. */
-       if (match_offset == queue->R0) {
-               position_slot = 0;
-       } else if (match_offset == queue->R1) {
-               swap(queue->R0, queue->R1);
-               position_slot = 1;
-       } else if (match_offset == queue->R2) {
-               swap(queue->R0, queue->R2);
-               position_slot = 2;
-       } else {
-               /* Not a repeated offset. */
-
-               /* offsets of 0, 1, and 2 are reserved for the repeated offset
-                * codes, so non-repeated offsets must be encoded as 3+.  The
-                * minimum offset is 1, so encode the offsets offset by 2. */
-               unsigned formatted_offset = match_offset + 2;
-
-               queue->R2 = queue->R1;
-               queue->R1 = queue->R0;
-               queue->R0 = match_offset;
-
-               /* The (now-formatted) offset will actually be encoded as a
-                * small position slot number that maps to a certain hard-coded
-                * offset (position base), followed by a number of extra bits---
-                * the position footer--- that are added to the position base to
-                * get the original formatted offset. */
-
-               position_slot = lzx_get_position_slot(formatted_offset);
-               position_footer = formatted_offset &
-                                 ((1 << lzx_get_num_extra_bits(position_slot)) - 1);
-       }
+       LZX_ASSERT(match_len >= LZX_MIN_MATCH_LEN && match_len <= LZX_MAX_MATCH_LEN);
 
-       adjusted_match_len = match_len - LZX_MIN_MATCH;
+       /* The match offset shall be encoded as a position slot (itself encoded
+        * as part of the main symbol) and a position footer.  */
+       position_slot = lzx_get_position_slot(match_offset, queue);
+       position_footer = (match_offset + LZX_OFFSET_OFFSET) &
+                               ((1U << lzx_get_num_extra_bits(position_slot)) - 1);
 
-
-       /* The match length must be at least 2, so let the adjusted match length
-        * be the match length minus 2.
-        *
-        * If it is less than 7, the adjusted match length is encoded as a 3-bit
-        * number offset by 2.  Otherwise, the 3-bit length header is all 1's
-        * and the actual adjusted length is given as a symbol encoded with the
-        * length tree, offset by 7.
-        */
+       /* The match length shall be encoded as a length header (itself encoded
+        * as part of the main symbol) and an optional length footer.  */
+       adjusted_match_len = match_len - LZX_MIN_MATCH_LEN;
        if (adjusted_match_len < LZX_NUM_PRIMARY_LENS) {
+               /* No length footer needed.  */
                len_header = adjusted_match_len;
        } else {
+               /* Length footer needed.  It will be encoded using the length
+                * code.  */
                len_header = LZX_NUM_PRIMARY_LENS;
                len_footer = adjusted_match_len - LZX_NUM_PRIMARY_LENS;
                freqs->len[len_footer]++;
        }
-       len_pos_header = (position_slot << 3) | len_header;
 
-       freqs->main[len_pos_header + LZX_NUM_CHARS]++;
+       /* Account for the main symbol.  */
+       main_symbol = ((position_slot << 3) | len_header) + LZX_NUM_CHARS;
+
+       freqs->main[main_symbol]++;
+
+       /* In an aligned offset block, 3 bits of the position footer are output
+        * as an aligned offset symbol.  Account for this, although we may
+        * ultimately decide to output the block as verbatim.  */
 
-       /* Equivalent to:
-        * if (lzx_extra_bits[position_slot] >= 3) */
+       /* The following check is equivalent to:
+        *
+        * if (lzx_extra_bits[position_slot] >= 3)
+        *
+        * Note that this correctly excludes position slots that correspond to
+        * recent offsets.  */
        if (position_slot >= 8)
                freqs->aligned[position_footer & 7]++;
 
@@ -1108,44 +1149,20 @@ lzx_record_match(unsigned match_offset, unsigned match_len,
         *         (since lzx_extra_bits[LZX_NUM_POSITION_SLOTS - 1] is 17).
         *
         * 0-7     length of match, offset by 2.  This can be at most
-        *         (LZX_MAX_MATCH - 2) == 255, so it will fit in 8 bits.  */
+        *         (LZX_MAX_MATCH_LEN - 2) == 255, so it will fit in 8 bits.  */
+       BUILD_BUG_ON(LZX_NUM_POSITION_SLOTS > 64);
+       LZX_ASSERT(lzx_get_num_extra_bits(LZX_NUM_POSITION_SLOTS - 1) <= 17);
+       BUILD_BUG_ON(LZX_MAX_MATCH_LEN - LZX_MIN_MATCH_LEN + 1 > 256);
        return 0x80000000 |
                (position_slot << 25) |
                (position_footer << 8) |
                (adjusted_match_len);
 }
 
-/* Set the cost model @ctx->costs from the Huffman codeword lengths specified in
- * @lens.
- *
- * These are basically the same thing, except that the Huffman codewords with
- * length 0 correspond to symbols with zero frequency that still need to be
- * assigned actual costs.  The specific values assigned are arbitrary, but they
- * should be fairly high (near the maximum codeword length) to take into account
- * the fact that uses of these symbols are expected to be rare.
- */
-static void
-lzx_set_costs(struct lzx_compressor * ctx, const struct lzx_lens * lens)
-{
-       unsigned i;
-
-       memcpy(&ctx->costs, lens, sizeof(struct lzx_lens));
-
-       for (i = 0; i < LZX_MAINTREE_NUM_SYMBOLS; i++)
-               if (ctx->costs.main[i] == 0)
-                       ctx->costs.main[i] = ctx->params.alg_params.slow.main_nostat_cost;
-
-       for (i = 0; i < LZX_LENTREE_NUM_SYMBOLS; i++)
-               if (ctx->costs.len[i] == 0)
-                       ctx->costs.len[i] = ctx->params.alg_params.slow.len_nostat_cost;
-
-       for (i = 0; i < LZX_ALIGNEDTREE_NUM_SYMBOLS; i++)
-               if (ctx->costs.aligned[i] == 0)
-                       ctx->costs.aligned[i] = ctx->params.alg_params.slow.aligned_nostat_cost;
-}
-
-static u32
-lzx_literal_cost(u8 c, const struct lzx_lens * costs)
+/* Returns the cost, in bits, to output a literal byte using the specified cost
+ * model.  */
+static sym_cost_t
+lzx_literal_cost(u8 c, const struct lzx_costs * costs)
 {
        return costs->main[c];
 }
@@ -1153,36 +1170,20 @@ lzx_literal_cost(u8 c, const struct lzx_lens * costs)
 /* Given a (length, offset) pair that could be turned into a valid LZX match as
  * well as costs for the codewords in the main, length, and aligned Huffman
  * codes, return the approximate number of bits it will take to represent this
- * match in the compressed output.  */
-static unsigned
-lzx_match_cost(unsigned length, unsigned offset, const struct lzx_lens *costs
-
-#if LZX_PARAM_ACCOUNT_FOR_LRU
-              , struct lzx_lru_queue *queue
-#endif
-       )
+ * match in the compressed output.  Take into account the match offset LRU
+ * queue and optionally update it.  */
+static sym_cost_t
+lzx_match_cost(unsigned length, unsigned offset, const struct lzx_costs *costs,
+              struct lzx_lru_queue *queue)
 {
-       unsigned position_slot, len_header, main_symbol;
-       unsigned cost = 0;
-
-       /* Calculate position slot and length header, then combine them into the
-        * main symbol.  */
-
-#if LZX_PARAM_ACCOUNT_FOR_LRU
-       if (offset == queue->R0) {
-               position_slot = 0;
-       } else if (offset == queue->R1) {
-               swap(queue->R0, queue->R1);
-               position_slot = 1;
-       } else if (offset == queue->R2) {
-               swap(queue->R0, queue->R2);
-               position_slot = 2;
-       } else
-#endif
-               position_slot = lzx_get_position_slot(offset + 2);
+       unsigned position_slot;
+       unsigned len_header, main_symbol;
+       sym_cost_t cost = 0;
 
-       len_header = min(length - LZX_MIN_MATCH, LZX_NUM_PRIMARY_LENS);
-       main_symbol = ((position_slot << 3) | len_header) + LZX_NUM_CHARS;
+       position_slot = lzx_get_position_slot(offset, queue);
+
+       len_header = min(length - LZX_MIN_MATCH_LEN, LZX_NUM_PRIMARY_LENS);
+       main_symbol = (position_slot << 3) | len_header | LZX_NUM_CHARS;
 
        /* Account for main symbol.  */
        cost += costs->main[main_symbol];
@@ -1191,392 +1192,392 @@ lzx_match_cost(unsigned length, unsigned offset, const struct lzx_lens *costs
        unsigned num_extra_bits = lzx_get_num_extra_bits(position_slot);
        if (num_extra_bits >= 3) {
                cost += num_extra_bits - 3;
-               cost += costs->aligned[(offset + LZX_MIN_MATCH) & 7];
+               cost += costs->aligned[(offset + LZX_OFFSET_OFFSET) & 7];
        } else {
                cost += num_extra_bits;
        }
 
        /* Account for extra length information.  */
-       if (length - LZX_MIN_MATCH >= LZX_NUM_PRIMARY_LENS)
-               cost += costs->len[length - LZX_MIN_MATCH - LZX_NUM_PRIMARY_LENS];
+       if (len_header == LZX_NUM_PRIMARY_LENS)
+               cost += costs->len[length - LZX_MIN_MATCH_LEN - LZX_NUM_PRIMARY_LENS];
 
        return cost;
+
 }
 
-/* This procedure effectively creates a new binary tree corresponding to the
- * current string at the same time that it searches the existing tree nodes for
- * matches.  This is the same algorithm as that used in GetMatchesSpec1() in
- * 7-Zip, but it is hopefully explained a little more clearly below.  */
-static unsigned
-lzx_lz_get_matches(const u8 window[restrict],
-                  const unsigned bytes_remaining,
-                  const unsigned strstart,
-                  const unsigned max_length,
-                  u16 child_tab[restrict],
-                  unsigned cur_match,
-                  const unsigned prev_len,
-                  struct raw_match * const matches)
+/* Very fast heuristic cost evaluation to use in the inner loop of the
+ * match-finder.  */
+static sym_cost_t
+lzx_match_cost_fast(unsigned offset, const struct lzx_lru_queue *queue)
+{
+       for (unsigned i = 0; i < LZX_NUM_RECENT_OFFSETS; i++)
+               if (offset == queue->R[i])
+                       return i;
+
+       BUILD_BUG_ON(LZX_MAX_WINDOW_SIZE >= (sym_cost_t)~0U);
+       return offset;
+}
+
+/* Set the cost model @ctx->costs from the Huffman codeword lengths specified in
+ * @lens.
+ *
+ * The cost model and codeword lengths are almost the same thing, but the
+ * Huffman codewords with length 0 correspond to symbols with zero frequency
+ * that still need to be assigned actual costs.  The specific values assigned
+ * are arbitrary, but they should be fairly high (near the maximum codeword
+ * length) to take into account the fact that uses of these symbols are expected
+ * to be rare.  */
+static void
+lzx_set_costs(struct lzx_compressor * ctx, const struct lzx_lens * lens)
 {
-       u16 *new_tree_lt_ptr = &child_tab[strstart * 2];
-       u16 *new_tree_gt_ptr = &child_tab[strstart * 2 + 1];
+       unsigned i;
 
-       u16 longest_lt_match_len = 0;
-       u16 longest_gt_match_len = 0;
+       /* Main code  */
+       for (i = 0; i < LZX_MAINCODE_NUM_SYMBOLS; i++) {
+               ctx->costs.main[i] = lens->main[i];
+               if (ctx->costs.main[i] == 0)
+                       ctx->costs.main[i] = ctx->params.alg_params.slow.main_nostat_cost;
+       }
 
-       /* Maximum number of nodes to walk down before stopping  */
-       unsigned depth = max_length;
+       /* Length code  */
+       for (i = 0; i < LZX_LENCODE_NUM_SYMBOLS; i++) {
+               ctx->costs.len[i] = lens->len[i];
+               if (ctx->costs.len[i] == 0)
+                       ctx->costs.len[i] = ctx->params.alg_params.slow.len_nostat_cost;
+       }
 
-       /* Length of longest match found so far  */
-       unsigned longest_match_len = prev_len;
+       /* Aligned offset code  */
+       for (i = 0; i < LZX_ALIGNEDCODE_NUM_SYMBOLS; i++) {
+               ctx->costs.aligned[i] = lens->aligned[i];
+               if (ctx->costs.aligned[i] == 0)
+                       ctx->costs.aligned[i] = ctx->params.alg_params.slow.aligned_nostat_cost;
+       }
+}
 
-       /* Maximum length of match to return  */
-       unsigned len_limit = min(bytes_remaining, max_length);
+/* Advance the suffix array match-finder to the next position.  */
+static void
+lzx_lz_update_salink(input_idx_t i,
+                    const input_idx_t SA[restrict],
+                    const input_idx_t ISA[restrict],
+                    struct salink link[restrict])
+{
+       /* r = Rank of the suffix at the current position.  */
+       const input_idx_t r = ISA[i];
 
-       /* Number of matches found so far  */
-       unsigned num_matches = 0;
+       /* next = rank of LOWEST ranked suffix that is ranked HIGHER than the
+        * current suffix AND has a LOWER position, or -1 if none exists.  */
+       const input_idx_t next = link[r].next;
 
-       for (;;) {
+       /* prev = rank of HIGHEST ranked suffix that is ranked LOWER than the
+        * current suffix AND has a LOWER position, or -1 if none exists.  */
+       const input_idx_t prev = link[r].prev;
 
-               /* Stop if too many nodes were traversed or if there is no next
-                * node  */
-               if (depth-- == 0 || cur_match == 0) {
-                       *new_tree_gt_ptr = 0;
-                       *new_tree_lt_ptr = 0;
-                       return num_matches;
-               }
+       /* Link the suffix at the current position into the linked list that
+        * contains all suffixes in the suffix array that are appear at or
+        * before the current position, sorted by rank.
+        *
+        * Save the values of all fields we overwrite so that rollback is
+        * possible.  */
+       if (next != (input_idx_t)~0U) {
 
-               /* Load the pointers to the children of the binary tree node
-                * corresponding to the current match  */
-               u16 * const cur_match_ptrs = &child_tab[cur_match * 2];
-
-               /* Set up pointers to the current match and to the current
-                * string  */
-               const u8 * const matchptr = &window[cur_match];
-               const u8 * const strptr = &window[strstart];
-
-               /* Determine position at which to start comparing  */
-               u16 len = min(longest_lt_match_len,
-                             longest_gt_match_len);
-
-               if (matchptr[len] == strptr[len]) {
-
-                       /* Extend the match as far as possible.  */
-                       while (++len != len_limit)
-                               if (matchptr[len] != strptr[len])
-                                       break;
-
-                       /* Record this match if it is the longest found so far.
-                        */
-                       if (len > longest_match_len) {
-                               longest_match_len = len;
-                               matches[num_matches].len = len;
-                               matches[num_matches].offset = strstart - cur_match;
-                               num_matches++;
-
-                               if (len == len_limit) {
-                                       /* Length limit was reached.  Link left pointer
-                                        * in the new tree with left subtree of current
-                                        * match tree, and link the right pointer in the
-                                        * new tree with the right subtree of the
-                                        * current match tree.  This in effect deletes
-                                        * the node for the currrent match, which is
-                                        * desirable because the current match is the
-                                        * same as the current string up until the
-                                        * length limit, so in subsequent queries it
-                                        * will never be preferable to the current
-                                        * position.  */
-                                       *new_tree_lt_ptr = cur_match_ptrs[0];
-                                       *new_tree_gt_ptr = cur_match_ptrs[1];
-                                       return num_matches;
-                               }
-                       }
-               }
+               link[next].prev = r;
+               link[next].lcpprev = link[r].lcpnext;
+       }
 
-               if (matchptr[len] < strptr[len]) {
-                       /* Case 1:  The current match is lexicographically less
-                        * than the current string.
-                        *
-                        * Since we are searching the binary tree structures, we
-                        * need to walk down to the *right* subtree of the
-                        * current match's node to get to a match that is
-                        * lexicographically *greater* than the current match
-                        * but still lexicographically *lesser* than the current
-                        * string.
-                        *
-                        * At the same time, we link the entire binary tree
-                        * corresponding to the current match into the
-                        * appropriate place in the new binary tree being built
-                        * for the current string.  */
-                       *new_tree_lt_ptr = cur_match;
-                       new_tree_lt_ptr = &cur_match_ptrs[1];
-                       cur_match = *new_tree_lt_ptr;
-                       longest_lt_match_len = len;
-               } else {
-                       /* Case 2:  The current match is lexicographically
-                        * greater than the current string.
-                        *
-                        * This is analogous to Case 1 above, but everything
-                        * happens in the other direction.
-                        */
-                       *new_tree_gt_ptr = cur_match;
-                       new_tree_gt_ptr = &cur_match_ptrs[0];
-                       cur_match = *new_tree_gt_ptr;
-                       longest_gt_match_len = len;
-               }
+       if (prev != (input_idx_t)~0U) {
+
+               link[prev].next = r;
+               link[prev].lcpnext = link[r].lcpprev;
        }
 }
 
-/* Equivalent to lzx_lz_get_matches(), but only updates the tree and doesn't
- * return matches.  See that function for details (including comments).  */
+/* Rewind the suffix array match-finder to the specified position.
+ *
+ * This undoes a series of updates by lzx_lz_update_salink().  */
 static void
-lzx_lz_skip_matches(const u8 window[restrict],
-                   const unsigned bytes_remaining,
-                   const unsigned strstart,
-                   const unsigned max_length,
-                   u16 child_tab[restrict],
-                   unsigned cur_match,
-                   const unsigned prev_len)
+lzx_lz_rewind_matchfinder(struct lzx_compressor *ctx,
+                         const unsigned orig_pos)
+{
+       LZX_DEBUG("Rewind match-finder %u => %u", ctx->match_window_pos, orig_pos);
+
+       if (ctx->match_window_pos == orig_pos)
+               return;
+
+       LZX_ASSERT(ctx->match_window_pos > orig_pos);
+       LZX_ASSERT(orig_pos == 0);
+       ctx->matches_cached = true;
+       ctx->cached_matches_pos = 0;
+       ctx->match_window_pos = orig_pos;
+}
+
+/*
+ * Use the suffix array match-finder to retrieve a list of LZ matches at the
+ * current position.
+ *
+ * [in]    @i          Current position in the window.
+ * [in]    @SA         Suffix array for the window.
+ * [in]    @ISA                Inverse suffix array for the window.
+ * [inout] @link       Suffix array links used internally by the match-finder.
+ * [out]   @matches    The (length, offset) pairs of the resulting matches will
+ *                             be written here, sorted in decreasing order by
+ *                             length.  All returned lengths will be unique.
+ * [in]    @queue      Recently used match offsets, used when evaluating the
+ *                             cost of matches.
+ * [in]           @min_match_len       Minimum match length to return.
+ * [in]           @max_matches_to_consider     Maximum number of matches to consider at
+ *                                     the position.
+ * [in]           @max_matches_to_return       Maximum number of matches to return.
+ *
+ * The return value is the number of matches found and written to @matches.
+ */
+static unsigned
+lzx_lz_get_matches(const input_idx_t i,
+                  const input_idx_t SA[const restrict],
+                  const input_idx_t ISA[const restrict],
+                  struct salink link[const restrict],
+                  struct raw_match matches[const restrict],
+                  const struct lzx_lru_queue * const restrict queue,
+                  const unsigned min_match_len,
+                  const uint32_t max_matches_to_consider,
+                  const uint32_t max_matches_to_return)
 {
-       u16 *new_tree_lt_ptr = &child_tab[strstart * 2];
-       u16 *new_tree_gt_ptr = &child_tab[strstart * 2 + 1];
+       /* r = Rank of the suffix at the current position.  */
+       const input_idx_t r = ISA[i];
 
-       u16 longest_lt_match_len = 0;
-       u16 longest_gt_match_len = 0;
+       /* Prepare for searching the current position.  */
+       lzx_lz_update_salink(i, SA, ISA, link);
+
+       /* L = rank of next suffix to the left;
+        * R = rank of next suffix to the right;
+        * lenL = length of match between current position and the suffix with rank L;
+        * lenR = length of match between current position and the suffix with rank R.
+        *
+        * This is left and right relative to the rank of the current suffix.
+        * Since the suffixes in the suffix array are sorted, the longest
+        * matches are immediately to the left and right (using the linked list
+        * to ignore all suffixes that occur later in the window).  The match
+        * length decreases the farther left and right we go.  We shall keep the
+        * length on both sides in sync in order to choose the lowest-cost match
+        * of each length.
+        */
+       input_idx_t L = link[r].prev;
+       input_idx_t R = link[r].next;
+       input_idx_t lenL = link[r].lcpprev;
+       input_idx_t lenR = link[r].lcpnext;
 
-       unsigned depth = max_length;
+       /* nmatches = number of matches found so far.  */
+       unsigned nmatches = 0;
 
-       unsigned longest_match_len = prev_len;
+       /* best_cost = cost of lowest-cost match found so far.
+        *
+        * We keep track of this so that we can ignore shorter matches that do
+        * not have lower costs than a longer matches already found.
+        */
+       sym_cost_t best_cost = INFINITE_SYM_COST;
 
-       unsigned len_limit = min(bytes_remaining, max_length);
+       /* count_remaining = maximum number of possible matches remaining to be
+        * considered.  */
+       uint32_t count_remaining = max_matches_to_consider;
 
-       for (;;) {
-               if (depth-- == 0 || cur_match == 0) {
-                       *new_tree_gt_ptr = 0;
-                       *new_tree_lt_ptr = 0;
-                       return;
-               }
+       /* pending = match currently being considered for a specific length.  */
+       struct raw_match pending;
 
-               u16 * const cur_match_ptrs = &child_tab[cur_match * 2];
+       while (lenL >= min_match_len || lenR >= min_match_len)
+       {
+               pending.len = lenL;
+               pending.offset = (input_idx_t)~0U;
+               sym_cost_t pending_cost = INFINITE_SYM_COST;
+               sym_cost_t cost;
 
-               const u8 * const matchptr = &window[cur_match];
-               const u8 * const strptr = &window[strstart];
+               /* Extend left.  */
+               if (lenL >= min_match_len && lenL >= lenR) {
+                       for (;;) {
 
-               u16 len = min(longest_lt_match_len,
-                             longest_gt_match_len);
+                               if (--count_remaining == 0)
+                                       goto out_save_pending;
 
-               if (matchptr[len] == strptr[len]) {
-                       while (++len != len_limit)
-                               if (matchptr[len] != strptr[len])
-                                       break;
+                               input_idx_t offset = i - SA[L];
 
-                       if (len > longest_match_len) {
-                               longest_match_len = len;
+                               /* Save match if it has smaller cost.  */
+                               cost = lzx_match_cost_fast(offset, queue);
+                               if (cost < pending_cost) {
+                                       pending.offset = offset;
+                                       pending_cost = cost;
+                               }
 
-                               if (len == len_limit) {
-                                       *new_tree_lt_ptr = cur_match_ptrs[0];
-                                       *new_tree_gt_ptr = cur_match_ptrs[1];
-                                       return;
+                               if (link[L].lcpprev < lenL) {
+                                       /* Match length decreased.  */
+
+                                       lenL = link[L].lcpprev;
+
+                                       /* Save the pending match unless the
+                                        * right side still may have matches of
+                                        * this length to be scanned, or if a
+                                        * previous (longer) match had lower
+                                        * cost.  */
+                                       if (pending.len > lenR) {
+                                               if (pending_cost < best_cost) {
+                                                       best_cost = pending_cost;
+                                                       matches[nmatches++] = pending;
+                                                       if (nmatches == max_matches_to_return)
+                                                               return nmatches;
+                                               }
+                                               pending.len = lenL;
+                                               pending.offset = (input_idx_t)~0U;
+                                               pending_cost = INFINITE_SYM_COST;
+                                       }
+                                       if (lenL < min_match_len || lenL < lenR)
+                                               break;
                                }
+                               L = link[L].prev;
                        }
                }
 
-               if (matchptr[len] < strptr[len]) {
-                       *new_tree_lt_ptr = cur_match;
-                       new_tree_lt_ptr = &cur_match_ptrs[1];
-                       cur_match = *new_tree_lt_ptr;
-                       longest_lt_match_len = len;
-               } else {
-                       *new_tree_gt_ptr = cur_match;
-                       new_tree_gt_ptr = &cur_match_ptrs[0];
-                       cur_match = *new_tree_gt_ptr;
-                       longest_gt_match_len = len;
+               pending.len = lenR;
+
+               /* Extend right.  */
+               if (lenR >= min_match_len && lenR > lenL) {
+                       for (;;) {
+
+                               if (--count_remaining == 0)
+                                       goto out_save_pending;
+
+                               input_idx_t offset = i - SA[R];
+
+                               /* Save match if it has smaller cost.  */
+                               cost = lzx_match_cost_fast(offset, queue);
+                               if (cost < pending_cost) {
+                                       pending.offset = offset;
+                                       pending_cost = cost;
+                               }
+
+                               if (link[R].lcpnext < lenR) {
+                                       /* Match length decreased.  */
+
+                                       lenR = link[R].lcpnext;
+
+                                       /* Save the pending match unless a
+                                        * previous (longer) match had lower
+                                        * cost.  */
+                                       if (pending_cost < best_cost) {
+                                               matches[nmatches++] = pending;
+                                               best_cost = pending_cost;
+                                               if (nmatches == max_matches_to_return)
+                                                       return nmatches;
+                                       }
+
+                                       if (lenR < min_match_len || lenR <= lenL)
+                                               break;
+
+                                       pending.len = lenR;
+                                       pending.offset = (input_idx_t)~0U;
+                                       pending_cost = INFINITE_SYM_COST;
+                               }
+                               R = link[R].next;
+                       }
                }
        }
+       goto out;
+
+out_save_pending:
+       if (pending.offset != (input_idx_t)~0U)
+               matches[nmatches++] = pending;
+
+out:
+       return nmatches;
 }
 
-static unsigned
-lzx_lz_get_matches_caching(struct lzx_compressor *ctx,
-                          struct raw_match **matches_ret);
 
 /* Tell the match-finder to skip the specified number of bytes (@n) in the
  * input.  */
 static void
 lzx_lz_skip_bytes(struct lzx_compressor *ctx, unsigned n)
 {
-
-#if LZX_PARAM_DONT_SKIP_MATCHES
-       /* Option 1: Still cache the matches from the positions skipped.  They
-        * will then be available in later passes.  */
-       struct raw_match *matches;
-       while (n--)
-               lzx_lz_get_matches_caching(ctx, &matches);
-#else
-       /* Option 2: Mark the positions skipped as having no matches available,
-        * but we still need to update the binary tree in case subsequent
-        * positions have matches at the current position.  */
        LZX_ASSERT(n <= ctx->match_window_end - ctx->match_window_pos);
-       if (ctx->matches_already_found) {
+       if (ctx->matches_cached) {
+               ctx->match_window_pos += n;
                while (n--) {
-                       LZX_ASSERT(ctx->cached_matches[ctx->cached_matches_pos].offset ==
-                                  ctx->match_window_pos);
-                       ctx->cached_matches_pos += ctx->cached_matches[ctx->cached_matches_pos].len + 1;
-                       ctx->match_window_pos++;
+                       ctx->cached_matches_pos +=
+                               ctx->cached_matches[ctx->cached_matches_pos].len + 1;
                }
        } else {
                while (n--) {
-                       if (ctx->params.alg_params.slow.use_len2_matches &&
-                           ctx->match_window_end - ctx->match_window_pos >= 2) {
-                               unsigned c1 = ctx->window[ctx->match_window_pos];
-                               unsigned c2 = ctx->window[ctx->match_window_pos + 1];
-                               unsigned digram = c1 | (c2 << 8);
-                               ctx->digram_tab[digram] = ctx->match_window_pos;
-                       }
-                       if (ctx->match_window_end - ctx->match_window_pos >= 3) {
-                               unsigned hash;
-                               unsigned cur_match;
-
-                               hash = lzx_lz_compute_hash(&ctx->window[ctx->match_window_pos]);
-
-                               cur_match = ctx->hash_tab[hash];
-                               ctx->hash_tab[hash] = ctx->match_window_pos;
-
-                               lzx_lz_skip_matches(ctx->window,
-                                                   ctx->match_window_end - ctx->match_window_pos,
-                                                   ctx->match_window_pos,
-                                                   ctx->params.alg_params.slow.num_fast_bytes,
-                                                   ctx->child_tab,
-                                                   cur_match, 1);
-                       }
-                       ctx->cached_matches[ctx->cached_matches_pos].len = 0;
-                       ctx->cached_matches[ctx->cached_matches_pos].offset = ctx->match_window_pos;
-                       ctx->cached_matches_pos++;
-                       ctx->match_window_pos++;
+                       ctx->cached_matches[ctx->cached_matches_pos++].len = 0;
+                       lzx_lz_update_salink(ctx->match_window_pos++, ctx->SA,
+                                            ctx->ISA, ctx->salink);
                }
        }
-#endif /* !LZX_PARAM_DONT_SKIP_MATCHES */
 }
 
 /* Retrieve a list of matches available at the next position in the input.
  *
- * The return value is the number of matches found, and a pointer to them is
- * written to @matches_ret.  The matches will be sorted in order by length.
- *
- * This is essentially a wrapper around lzx_lz_get_matches() that caches its
- * output the first time and also performs the needed hashing.
- */
+ * The matches are written to ctx->matches in decreasing order of length, and
+ * the return value is the number of matches found.  */
 static unsigned
 lzx_lz_get_matches_caching(struct lzx_compressor *ctx,
+                          const struct lzx_lru_queue *queue,
                           struct raw_match **matches_ret)
 {
        unsigned num_matches;
        struct raw_match *matches;
 
-       LZX_ASSERT(ctx->match_window_end >= ctx->match_window_pos);
+       LZX_ASSERT(ctx->match_window_pos <= ctx->match_window_end);
 
        matches = &ctx->cached_matches[ctx->cached_matches_pos + 1];
 
-       if (ctx->matches_already_found) {
-               num_matches = ctx->cached_matches[ctx->cached_matches_pos].len;
-               LZX_ASSERT(ctx->cached_matches[ctx->cached_matches_pos].offset == ctx->match_window_pos);
-
-               for (int i = (int)num_matches - 1; i >= 0; i--) {
-                       if (ctx->match_window_pos + matches[i].len > ctx->match_window_end)
-                               matches[i].len = ctx->match_window_end - ctx->match_window_pos;
-                       else
-                               break;
-               }
+       if (ctx->matches_cached) {
+               num_matches = matches[-1].len;
        } else {
-               unsigned prev_len = 1;
-               struct raw_match * matches_ret = &ctx->cached_matches[ctx->cached_matches_pos + 1];
-               num_matches = 0;
-
-               if (ctx->params.alg_params.slow.use_len2_matches &&
-                   ctx->match_window_end - ctx->match_window_pos >= 3) {
-                       unsigned c1 = ctx->window[ctx->match_window_pos];
-                       unsigned c2 = ctx->window[ctx->match_window_pos + 1];
-                       unsigned digram = c1 | (c2 << 8);
-                       unsigned cur_match;
-
-                       cur_match = ctx->digram_tab[digram];
-                       ctx->digram_tab[digram] = ctx->match_window_pos;
-                       if (cur_match != 0 &&
-                           ctx->window[cur_match + 2] != ctx->window[ctx->match_window_pos + 2])
-                       {
-                               matches_ret->len = 2;
-                               matches_ret->offset = ctx->match_window_pos - cur_match;
-                               matches_ret++;
-                               num_matches++;
-                               prev_len = 2;
-                       }
-               }
-               if (ctx->match_window_end - ctx->match_window_pos >= 3) {
-                       unsigned hash;
-                       unsigned cur_match;
-
-                       hash = lzx_lz_compute_hash(&ctx->window[ctx->match_window_pos]);
-
-                       cur_match = ctx->hash_tab[hash];
-                       ctx->hash_tab[hash] = ctx->match_window_pos;
-                       num_matches += lzx_lz_get_matches(ctx->window,
-                                                         ctx->match_window_end - ctx->match_window_pos,
-                                                         ctx->match_window_pos,
-                                                         ctx->params.alg_params.slow.num_fast_bytes,
-                                                         ctx->child_tab,
-                                                         cur_match,
-                                                         prev_len,
-                                                         matches_ret);
-               }
-
-               ctx->cached_matches[ctx->cached_matches_pos].len = num_matches;
-               ctx->cached_matches[ctx->cached_matches_pos].offset = ctx->match_window_pos;
-
-               if (num_matches) {
-                       struct raw_match *longest_match_ptr =
-                               &ctx->cached_matches[ctx->cached_matches_pos + 1 +
-                                                    num_matches - 1];
-                       u16 len = longest_match_ptr->len;
-
-                       /* If the longest match returned by the match-finder
-                        * reached the number of fast bytes, extend it as much
-                        * as possible.  */
-                       if (len == ctx->params.alg_params.slow.num_fast_bytes) {
-                               const unsigned maxlen =
-                                       min(ctx->match_window_end - ctx->match_window_pos,
-                                           LZX_MAX_MATCH);
-
-                               const u8 * const matchptr =
-                                       &ctx->window[ctx->match_window_pos - longest_match_ptr->offset];
-
-                               const u8 * const strptr =
-                                       &ctx->window[ctx->match_window_pos];
-
-                               while (len < maxlen && matchptr[len] == strptr[len])
-                                       len++;
-                       }
-                       longest_match_ptr->len = len;
-               }
+               unsigned min_match_len = LZX_MIN_MATCH_LEN;
+               if (min_match_len <= 2 && !ctx->params.alg_params.slow.use_len2_matches)
+                       min_match_len = 3;
+               const uint32_t max_search_depth = ctx->params.alg_params.slow.max_search_depth;
+               const uint32_t max_matches_per_pos = ctx->params.alg_params.slow.max_matches_per_pos;
+
+               if (unlikely(max_search_depth == 0 || max_matches_per_pos == 0))
+                       num_matches = 0;
+               else
+                       num_matches = lzx_lz_get_matches(ctx->match_window_pos,
+                                                        ctx->SA,
+                                                        ctx->ISA,
+                                                        ctx->salink,
+                                                        matches,
+                                                        queue,
+                                                        min_match_len,
+                                                        max_search_depth,
+                                                        max_matches_per_pos);
+               matches[-1].len = num_matches;
        }
        ctx->cached_matches_pos += num_matches + 1;
        *matches_ret = matches;
 
+       /* Cap the length of returned matches to the number of bytes remaining,
+        * if it is not the whole window.  */
+       if (ctx->match_window_end < ctx->window_size) {
+               unsigned maxlen = ctx->match_window_end - ctx->match_window_pos;
+               for (unsigned i = 0; i < num_matches; i++)
+                       if (matches[i].len > maxlen)
+                               matches[i].len = maxlen;
+       }
 #if 0
-       printf("\n");
+       fprintf(stderr, "Pos %u/%u: %u matches\n",
+               ctx->match_window_pos, ctx->match_window_end, num_matches);
        for (unsigned i = 0; i < num_matches; i++)
-       {
-               printf("Len %u Offset %u\n", matches[i].len, matches[i].offset);
-       }
+               fprintf(stderr, "\tLen %u Offset %u\n", matches[i].len, matches[i].offset);
 #endif
 
+#ifdef ENABLE_LZX_DEBUG
        for (unsigned i = 0; i < num_matches; i++) {
-               LZX_ASSERT(matches[i].len <= LZX_MAX_MATCH);
-               if (matches[i].len >= LZX_MIN_MATCH) {
-                       LZX_ASSERT(matches[i].offset <= ctx->match_window_pos);
-                       LZX_ASSERT(matches[i].len <= ctx->match_window_end - ctx->match_window_pos);
-                       LZX_ASSERT(!memcmp(&ctx->window[ctx->match_window_pos],
-                                          &ctx->window[ctx->match_window_pos - matches[i].offset],
-                                          matches[i].len));
-               }
+               LZX_ASSERT(matches[i].len >= LZX_MIN_MATCH_LEN);
+               LZX_ASSERT(matches[i].len <= LZX_MAX_MATCH_LEN);
+               LZX_ASSERT(matches[i].len <= ctx->match_window_end - ctx->match_window_pos);
+               LZX_ASSERT(matches[i].offset > 0);
+               LZX_ASSERT(matches[i].offset <= ctx->match_window_pos);
+               LZX_ASSERT(!memcmp(&ctx->window[ctx->match_window_pos],
+                                  &ctx->window[ctx->match_window_pos - matches[i].offset],
+                                  matches[i].len));
        }
+#endif
 
        ctx->match_window_pos++;
        return num_matches;
@@ -1621,22 +1622,117 @@ lzx_lz_reverse_near_optimal_match_list(struct lzx_compressor *ctx,
                };
 }
 
+#if 0
+static struct raw_match
+lzx_lz_get_greedy_match(struct lzx_compressor * ctx)
+{
+       struct raw_match *matches;
+
+       if (!lzx_lz_get_matches_caching(ctx, &ctx->queue, &matches))
+               return (struct raw_match) {.len = 0};
+
+       lzx_lz_skip_bytes(ctx, matches[0].len - 1);
+       return matches[0];
+}
+#endif
+
+#if 0
+static struct raw_match
+lzx_lz_get_lazy_match(struct lzx_compressor * ctx)
+{
+       unsigned num_matches;
+       struct raw_match *matches;
+       struct raw_match prev_match;
+       struct lzx_lru_queue queue;
+
+       if (ctx->optimum_cur_idx != ctx->optimum_end_idx)
+               goto retopt;
+
+       /* Check for matches at first position.  */
+       num_matches = lzx_lz_get_matches_caching(ctx, &ctx->queue, &matches);
+
+       /* Return literal if no matches were found.  */
+       if (num_matches == 0)
+               return (struct raw_match) { .len = 0 };
+
+       /* Immediately choose match if longer than threshold.  */
+       if (matches[0].len > ctx->params.alg_params.slow.num_fast_bytes)
+               goto savecur;
+
+       ctx->optimum_cur_idx = ctx->optimum_end_idx = 0;
+       for (;;) {
+               prev_match = matches[0];
+
+               /* Check for matches at next position.  */
+               num_matches = lzx_lz_get_matches_caching(ctx, &ctx->queue, &matches);
+
+               /* Choose previous match if there is not a match at this
+                * position.  */
+               if (num_matches == 0)
+                       goto saveprev;
+
+               /* Choose previous match the longest match at the next position
+                * is the same place, just one character shifted over.  */
+               if (matches[0].offset == prev_match.offset ||
+                   matches[0].len < prev_match.len)
+                       goto saveprev;
+
+               struct lzx_lru_queue q1 = ctx->queue, q2 = ctx->queue;
+               double lazycost = lzx_literal_cost(ctx->window[ctx->match_window_pos - 2],
+                                                    &ctx->costs) +
+                                   lzx_match_cost(matches[0].len, matches[0].offset,
+                                                  &ctx->costs, &q1);
+               double greedycost = lzx_match_cost(prev_match.len, prev_match.offset,
+                                                    &ctx->costs, &q2);
+               lazycost *= (double)prev_match.len / (1 + matches[0].len);
+
+               /* Choose previous match if greedy cost was lower.  */
+               if (greedycost <= lazycost)
+                       goto saveprev;
+
+               /* Choose literal at the previous position.  */
+               ctx->optimum[ctx->optimum_end_idx++].next.link = 0;
+
+
+               /* Immediately choose match if longer than threshold.  */
+               if (matches[0].len > ctx->params.alg_params.slow.num_fast_bytes)
+                       goto savecur;
+       }
+
+savecur:
+       lzx_lz_skip_bytes(ctx, 1);
+       prev_match = matches[0];
+
+saveprev:
+       lzx_lz_skip_bytes(ctx, prev_match.len - 2);
+       ctx->optimum[ctx->optimum_end_idx].next.link = prev_match.len;
+       ctx->optimum[ctx->optimum_end_idx].next.match_offset = prev_match.offset;
+       ctx->optimum_end_idx++;
+retopt:
+       prev_match.len = ctx->optimum[ctx->optimum_cur_idx].next.link;
+       prev_match.offset = ctx->optimum[ctx->optimum_cur_idx].next.match_offset;
+       ctx->optimum_cur_idx++;
+       return prev_match;
+}
+#endif
+
+
 /*
  * lzx_lz_get_near_optimal_match() -
  *
- * Choose the "best" match or literal to use at the next position in the input.
+ * Choose the optimal match or literal to use at the next position in the input.
  *
- * Unlike a "greedy" parser that always takes the longest match, or even a
+ * Unlike a greedy parser that always takes the longest match, or even a
  * parser with one match/literal look-ahead like zlib, the algorithm used here
- * may look ahead many matches/literals to determine the best match/literal to
+ * may look ahead many matches/literals to determine the optimal match/literal to
  * output next.  The motivation is that the compression ratio is improved if the
  * compressor can do things like use a shorter-than-possible match in order to
  * allow a longer match later, and also take into account the Huffman code cost
- * model rather than simply assuming that longer is better.  It is not a true
- * "optimal" parser, however, since some shortcuts can be taken; for example, if
- * a match is very long, the parser just chooses it immediately before too much
- * time is wasting considering many different alternatives that are unlikely to
- * be better.
+ * model rather than simply assuming that longer is better.
+ *
+ * Still, this is not truly an optimal parser because very long matches are
+ * taken immediately.  This is done to avoid considering many different
+ * alternatives that are unlikely to significantly be better.
  *
  * This algorithm is based on that used in 7-Zip's DEFLATE encoder.
  *
@@ -1658,11 +1754,9 @@ lzx_lz_reverse_near_optimal_match_list(struct lzx_compressor *ctx,
  *     ctx->optimum         (internal state; leave uninitialized)
  *     ctx->optimum_cur_idx (must set to 0 before first call)
  *     ctx->optimum_end_idx (must set to 0 before first call)
- *     ctx->hash_tab        (must set to 0 before first call)
- *     ctx->cached_matches  (internal state; leave uninitialized)
- *     ctx->cached_matches_pos (initialize to 0 before first call; save and
- *                              restore value if restarting parse from a
- *                              certain position)
+ *     ctx->SA              (must be built before first call)
+ *     ctx->ISA             (must be built before first call)
+ *     ctx->salink          (must be built before first call)
  *     ctx->match_window_pos (must initialize to position of next match to
  *                            return; subsequent calls return subsequent
  *                            matches)
@@ -1670,33 +1764,16 @@ lzx_lz_reverse_near_optimal_match_list(struct lzx_compressor *ctx,
  *                            subsequent calls use the same limit)
  *
  * The return value is a (length, offset) pair specifying the match or literal
- * chosen.  For literals, length is either 0 or 1 and offset is meaningless.
+ * chosen.  For literals, the length is less than LZX_MIN_MATCH_LEN and the
+ * offset is meaningless.
  */
 static struct raw_match
 lzx_lz_get_near_optimal_match(struct lzx_compressor * ctx)
 {
-#if 0
-       /* Testing: literals only  */
-       ctx->match_window_pos++;
-       return (struct raw_match) { .len = 0 };
-#elif 0
-       /* Testing: greedy parsing  */
-       struct raw_match *matches;
-       unsigned num_matches;
-       struct raw_match match = {.len = 0};
-
-       num_matches = lzx_lz_get_matches_caching(ctx, &matches);
-       if (num_matches) {
-               match = matches[num_matches - 1];
-               lzx_lz_skip_bytes(ctx, match.len - 1);
-       }
-       return match;
-#else
        unsigned num_possible_matches;
        struct raw_match *possible_matches;
        struct raw_match match;
        unsigned longest_match_len;
-       unsigned len, match_idx;
 
        if (ctx->optimum_cur_idx != ctx->optimum_end_idx) {
                /* Case 2: Return the next match/literal already found.  */
@@ -1714,30 +1791,28 @@ lzx_lz_get_near_optimal_match(struct lzx_compressor * ctx)
        ctx->optimum_end_idx = 0;
 
        /* Get matches at this position.  */
-       num_possible_matches = lzx_lz_get_matches_caching(ctx, &possible_matches);
+       num_possible_matches = lzx_lz_get_matches_caching(ctx, &ctx->queue, &possible_matches);
 
        /* If no matches found, return literal.  */
        if (num_possible_matches == 0)
                return (struct raw_match){ .len = 0 };
 
-       /* The matches that were found are sorted by length.  Get the length of
-        * the longest one.  */
-       longest_match_len = possible_matches[num_possible_matches - 1].len;
+       /* The matches that were found are sorted in decreasing order by length.
+        * Get the length of the longest one.  */
+       longest_match_len = possible_matches[0].len;
 
        /* Greedy heuristic:  if the longest match that was found is greater
         * than the number of fast bytes, return it immediately; don't both
         * doing more work.  */
        if (longest_match_len > ctx->params.alg_params.slow.num_fast_bytes) {
                lzx_lz_skip_bytes(ctx, longest_match_len - 1);
-               return possible_matches[num_possible_matches - 1];
+               return possible_matches[0];
        }
 
        /* Calculate the cost to reach the next position by outputting a
         * literal.  */
-#if LZX_PARAM_ACCOUNT_FOR_LRU
        ctx->optimum[0].queue = ctx->queue;
        ctx->optimum[1].queue = ctx->optimum[0].queue;
-#endif
        ctx->optimum[1].cost = lzx_literal_cost(ctx->window[ctx->match_window_pos],
                                                &ctx->costs);
        ctx->optimum[1].prev.link = 0;
@@ -1745,26 +1820,21 @@ lzx_lz_get_near_optimal_match(struct lzx_compressor * ctx)
        /* Calculate the cost to reach any position up to and including that
         * reached by the longest match, using the shortest (i.e. closest) match
         * that reaches each position.  */
-       match_idx = 0;
-       BUILD_BUG_ON(LZX_MIN_MATCH != 2);
-       for (len = LZX_MIN_MATCH; len <= longest_match_len; len++) {
+       BUILD_BUG_ON(LZX_MIN_MATCH_LEN != 2);
+       for (unsigned len = LZX_MIN_MATCH_LEN, match_idx = num_possible_matches - 1;
+            len <= longest_match_len; len++) {
 
                LZX_ASSERT(match_idx < num_possible_matches);
 
-       #if LZX_PARAM_ACCOUNT_FOR_LRU
                ctx->optimum[len].queue = ctx->optimum[0].queue;
-       #endif
                ctx->optimum[len].prev.link = 0;
                ctx->optimum[len].prev.match_offset = possible_matches[match_idx].offset;
                ctx->optimum[len].cost = lzx_match_cost(len,
                                                        possible_matches[match_idx].offset,
-                                                       &ctx->costs
-                                               #if LZX_PARAM_ACCOUNT_FOR_LRU
-                                                       , &ctx->optimum[len].queue
-                                               #endif
-                                                       );
+                                                       &ctx->costs,
+                                                       &ctx->optimum[len].queue);
                if (len == possible_matches[match_idx].len)
-                       match_idx++;
+                       match_idx--;
        }
 
        unsigned cur_pos = 0;
@@ -1773,22 +1843,21 @@ lzx_lz_get_near_optimal_match(struct lzx_compressor * ctx)
         * so far  */
        unsigned len_end = longest_match_len;
 
-
        for (;;) {
                /* Advance to next position.  */
                cur_pos++;
 
-               if (cur_pos == len_end || cur_pos == LZX_PARAM_OPTIM_ARRAY_SIZE)
+               if (cur_pos == len_end || cur_pos == LZX_OPTIM_ARRAY_SIZE)
                        return lzx_lz_reverse_near_optimal_match_list(ctx, cur_pos);
 
                /* retrieve the number of matches available at this position  */
-               num_possible_matches = lzx_lz_get_matches_caching(ctx,
+               num_possible_matches = lzx_lz_get_matches_caching(ctx, &ctx->optimum[cur_pos].queue,
                                                                  &possible_matches);
 
                unsigned new_len = 0;
 
                if (num_possible_matches != 0) {
-                       new_len = possible_matches[num_possible_matches - 1].len;
+                       new_len = possible_matches[0].len;
 
                        /* Greedy heuristic:  if we found a match greater than
                         * the number of fast bytes, stop immediately.  */
@@ -1800,7 +1869,7 @@ lzx_lz_get_near_optimal_match(struct lzx_compressor * ctx)
 
                                /* Append the long match to the end of the list.  */
                                ctx->optimum[cur_pos].next.match_offset =
-                                       possible_matches[num_possible_matches - 1].offset;
+                                       possible_matches[0].offset;
                                ctx->optimum[cur_pos].next.link = cur_pos + new_len;
                                ctx->optimum_end_idx = cur_pos + new_len;
 
@@ -1813,16 +1882,14 @@ lzx_lz_get_near_optimal_match(struct lzx_compressor * ctx)
                }
 
                /* Consider proceeding with a literal byte.  */
-               u32 cur_cost = ctx->optimum[cur_pos].cost;
-               u32 cur_plus_literal_cost = cur_cost +
+               block_cost_t cur_cost = ctx->optimum[cur_pos].cost;
+               block_cost_t cur_plus_literal_cost = cur_cost +
                        lzx_literal_cost(ctx->window[ctx->match_window_pos - 1],
                                         &ctx->costs);
                if (cur_plus_literal_cost < ctx->optimum[cur_pos + 1].cost) {
                        ctx->optimum[cur_pos + 1].cost = cur_plus_literal_cost;
                        ctx->optimum[cur_pos + 1].prev.link = cur_pos;
-               #if LZX_PARAM_ACCOUNT_FOR_LRU
                        ctx->optimum[cur_pos + 1].queue = ctx->optimum[cur_pos].queue;
-               #endif
                }
 
                if (num_possible_matches == 0)
@@ -1831,575 +1898,611 @@ lzx_lz_get_near_optimal_match(struct lzx_compressor * ctx)
                /* Consider proceeding with a match.  */
 
                while (len_end < cur_pos + new_len)
-                       ctx->optimum[++len_end].cost = ~(u32)0;
+                       ctx->optimum[++len_end].cost = INFINITE_BLOCK_COST;
 
-               match_idx = 0;
-               for (len = LZX_MIN_MATCH; len <= new_len; len++) {
+               for (unsigned len = LZX_MIN_MATCH_LEN, match_idx = num_possible_matches - 1;
+                    len <= new_len; len++) {
                        LZX_ASSERT(match_idx < num_possible_matches);
-               #if LZX_PARAM_ACCOUNT_FOR_LRU
                        struct lzx_lru_queue q = ctx->optimum[cur_pos].queue;
-               #endif
-                       u32 cost = cur_cost + lzx_match_cost(len,
-                                                            possible_matches[match_idx].offset,
-                                                            &ctx->costs
-                                                       #if LZX_PARAM_ACCOUNT_FOR_LRU
-                                                            , &q
-                                                       #endif
-                                                            );
+                       block_cost_t cost = cur_cost + lzx_match_cost(len,
+                                                                     possible_matches[match_idx].offset,
+                                                                     &ctx->costs,
+                                                                     &q);
 
                        if (cost < ctx->optimum[cur_pos + len].cost) {
                                ctx->optimum[cur_pos + len].cost = cost;
                                ctx->optimum[cur_pos + len].prev.link = cur_pos;
                                ctx->optimum[cur_pos + len].prev.match_offset =
                                                possible_matches[match_idx].offset;
-                       #if LZX_PARAM_ACCOUNT_FOR_LRU
                                ctx->optimum[cur_pos + len].queue = q;
-                       #endif
                        }
 
                        if (len == possible_matches[match_idx].len)
-                               match_idx++;
+                               match_idx--;
                }
        }
-#endif
 }
 
-static unsigned
-lzx_huffman_code_output_cost(const u8 lens[restrict],
-                            const freq_t freqs[restrict],
-                            unsigned num_syms)
+/*
+ * Set default symbol costs.
+ */
+static void
+lzx_set_default_costs(struct lzx_costs * costs)
 {
-       unsigned cost = 0;
+       unsigned i;
+
+       /* Literal symbols  */
+       for (i = 0; i < LZX_NUM_CHARS; i++)
+               costs->main[i] = 8;
 
-       for (unsigned i = 0; i < num_syms; i++)
-               cost += (unsigned)lens[i] * (unsigned)freqs[i];
+       /* Match header symbols  */
+       for (; i < LZX_MAINCODE_NUM_SYMBOLS; i++)
+               costs->main[i] = 10;
 
-       return cost;
+       /* Length symbols  */
+       for (i = 0; i < LZX_LENCODE_NUM_SYMBOLS; i++)
+               costs->len[i] = 8;
+
+       /* Aligned offset symbols  */
+       for (i = 0; i < LZX_ALIGNEDCODE_NUM_SYMBOLS; i++)
+               costs->aligned[i] = 3;
 }
 
-/* Return the number of bits required to output the lengths for the specified
- * Huffman code in compressed format (encoded with a precode).  */
-static unsigned
-lzx_code_cost(const u8 lens[], const u8 prev_lens[], unsigned num_syms)
+/* Given the frequencies of symbols in a compressed block and the corresponding
+ * Huffman codes, return LZX_BLOCKTYPE_ALIGNED or LZX_BLOCKTYPE_VERBATIM if an
+ * aligned offset or verbatim block, respectively, will take fewer bits to
+ * output.  */
+static int
+lzx_choose_verbatim_or_aligned(const struct lzx_freqs * freqs,
+                              const struct lzx_codes * codes)
 {
-       u8 output_syms[num_syms];
-       freq_t precode_freqs[LZX_PRETREE_NUM_SYMBOLS];
-       u8 precode_lens[LZX_PRETREE_NUM_SYMBOLS];
-       u16 precode_codewords[LZX_PRETREE_NUM_SYMBOLS];
-       unsigned cost = 0;
-       unsigned num_additional_bits;
+       unsigned aligned_cost = 0;
+       unsigned verbatim_cost = 0;
 
-       /* Acount for the lengths of the precode itself.  */
-       cost += LZX_PRETREE_NUM_SYMBOLS * LZX_PRETREE_ELEMENT_SIZE;
+       /* Verbatim blocks have a constant 3 bits per position footer.  Aligned
+        * offset blocks have an aligned offset symbol per position footer, plus
+        * an extra 24 bits to output the lengths necessary to reconstruct the
+        * aligned offset code itself.  */
+       for (unsigned i = 0; i < LZX_ALIGNEDCODE_NUM_SYMBOLS; i++) {
+               verbatim_cost += 3 * freqs->aligned[i];
+               aligned_cost += codes->lens.aligned[i] * freqs->aligned[i];
+       }
+       aligned_cost += LZX_ALIGNEDCODE_ELEMENT_SIZE * LZX_ALIGNEDCODE_NUM_SYMBOLS;
+       if (aligned_cost < verbatim_cost)
+               return LZX_BLOCKTYPE_ALIGNED;
+       else
+               return LZX_BLOCKTYPE_VERBATIM;
+}
 
-       lzx_build_precode(lens, prev_lens, num_syms,
-                         precode_freqs, output_syms,
-                         precode_lens, precode_codewords,
-                         &num_additional_bits);
+/* Find a near-optimal sequence of matches/literals with which to output the
+ * specified LZX block, and set its type to that which has the minimum cost to
+ * output.  */
+static void
+lzx_optimize_block(struct lzx_compressor *ctx, struct lzx_block_spec *spec,
+                  unsigned num_passes)
+{
+       struct lzx_lru_queue orig_queue = ctx->queue;
+       struct lzx_freqs freqs;
 
-       /* Account for all precode symbols output.  */
-       cost += lzx_huffman_code_output_cost(precode_lens, precode_freqs,
-                                            LZX_PRETREE_NUM_SYMBOLS);
+       ctx->match_window_end = spec->window_pos + spec->block_size;
+       spec->chosen_matches_start_pos = spec->window_pos;
 
-       /* Account for additional bits.  */
-       cost += num_additional_bits;
+       LZX_ASSERT(num_passes >= 1);
 
-       return cost;
-}
+       /* The first optimal parsing pass is done using the cost model already
+        * set in ctx->costs.  Each later pass is done using a cost model
+        * computed from the previous pass.  */
+       for (unsigned pass = 0; pass < num_passes; pass++) {
 
-/* Account for extra bits in the main symbols.  */
-static void
-lzx_update_mainsym_match_costs(int block_type,
-                              u8 main_lens[LZX_MAINTREE_NUM_SYMBOLS])
-{
-       unsigned i;
+               lzx_lz_rewind_matchfinder(ctx, spec->window_pos);
+               ctx->queue = orig_queue;
+               spec->num_chosen_matches = 0;
+               memset(&freqs, 0, sizeof(freqs));
 
-       LZX_ASSERT(block_type == LZX_BLOCKTYPE_ALIGNED ||
-                  block_type == LZX_BLOCKTYPE_VERBATIM);
+               for (unsigned i = spec->window_pos; i < spec->window_pos + spec->block_size; ) {
+                       struct raw_match raw_match;
+                       struct lzx_match lzx_match;
 
-       for (i = LZX_NUM_CHARS; i < LZX_MAINTREE_NUM_SYMBOLS; i++) {
-               unsigned position_slot = (i >> 3) & 0x1f;
+                       raw_match = lzx_lz_get_near_optimal_match(ctx);
+                       if (raw_match.len >= LZX_MIN_MATCH_LEN) {
+                               lzx_match.data = lzx_record_match(raw_match.offset, raw_match.len,
+                                                                 &freqs, &ctx->queue);
+                               i += raw_match.len;
+                       } else {
+                               lzx_match.data = lzx_record_literal(ctx->window[i], &freqs);
+                               i += 1;
+                       }
+                       ctx->chosen_matches[spec->chosen_matches_start_pos +
+                                           spec->num_chosen_matches++] = lzx_match;
+               }
 
-               /* If it's a verbatim block, add the number of extra bits
-                * corresponding to the position slot.
-                *
-                * If it's an aligned block and there would normally be at least
-                * 3 extra bits, count 3 less because they will be output as an
-                * aligned offset symbol instead.  */
-               unsigned num_extra_bits = lzx_get_num_extra_bits(position_slot);
-
-               if (block_type == LZX_BLOCKTYPE_ALIGNED && num_extra_bits >= 3)
-                       num_extra_bits -= 3;
-               main_lens[i] += num_extra_bits;
+               lzx_make_huffman_codes(&freqs, &spec->codes);
+               if (pass < num_passes - 1)
+                       lzx_set_costs(ctx, &spec->codes.lens);
        }
+       spec->block_type = lzx_choose_verbatim_or_aligned(&freqs, &spec->codes);
 }
 
-/*
- * Compute the costs, in bits, to output a compressed block as aligned offset
- * and verbatim.
- *
- * @block_size
- *     Number of bytes of uncompressed data the block represents.
- * @codes
- *     Huffman codes that will be used when outputting the block.
- * @prev_codes
- *     Huffman codes for the previous block, or all zeroes if this is the first
- *     block.
- * @freqs
- *     Frequencies of Huffman symbols that will be output in the block.
- * @aligned_cost_ret
- *     Cost of aligned block will be returned here.
- * @verbatim_cost_ret
- *     Cost of verbatim block will be returned here.
- */
 static void
-lzx_compute_compressed_block_costs(unsigned block_size,
-                                  const struct lzx_codes *codes,
-                                  const struct lzx_codes *prev_codes,
-                                  const struct lzx_freqs *freqs,
-                                  unsigned * aligned_cost_ret,
-                                  unsigned * verbatim_cost_ret)
+lzx_optimize_blocks(struct lzx_compressor *ctx)
 {
-       unsigned common_cost = 0;
-       unsigned aligned_cost = 0;
-       unsigned verbatim_cost = 0;
+       lzx_lru_queue_init(&ctx->queue);
+       ctx->optimum_cur_idx = 0;
+       ctx->optimum_end_idx = 0;
 
-       u8 updated_main_lens[LZX_MAINTREE_NUM_SYMBOLS];
+       const unsigned num_passes = ctx->params.alg_params.slow.num_optim_passes;
 
-       /* Account for cost of block header.  */
-       common_cost += LZX_BLOCKTYPE_NBITS;
-       if (block_size == LZX_DEFAULT_BLOCK_SIZE)
-               common_cost += 1;
-       else
-               common_cost += LZX_BLOCKSIZE_NBITS;
-
-       /* Account for cost of outputting aligned offset code.  */
-       aligned_cost += LZX_ALIGNEDTREE_NUM_SYMBOLS * LZX_ALIGNEDTREE_ELEMENT_SIZE;
-
-       /* Account for cost of outputting main and length codes.  */
-       common_cost += lzx_code_cost(codes->lens.main,
-                                    prev_codes->lens.main,
-                                    LZX_NUM_CHARS);
-       common_cost += lzx_code_cost(codes->lens.main + LZX_NUM_CHARS,
-                                    prev_codes->lens.main + LZX_NUM_CHARS,
-                                    LZX_MAINTREE_NUM_SYMBOLS - LZX_NUM_CHARS);
-       common_cost += lzx_code_cost(codes->lens.len,
-                                    prev_codes->lens.len,
-                                    LZX_LENTREE_NUM_SYMBOLS);
-
-       /* Account for cost to output main, length, and aligned symbols, taking
-        * into account extra position bits.  */
-
-       memcpy(updated_main_lens, codes->lens.main, LZX_MAINTREE_NUM_SYMBOLS);
-       lzx_update_mainsym_match_costs(LZX_BLOCKTYPE_VERBATIM, updated_main_lens);
-       verbatim_cost += lzx_huffman_code_output_cost(updated_main_lens,
-                                                     freqs->main,
-                                                     LZX_MAINTREE_NUM_SYMBOLS);
-       memcpy(updated_main_lens, codes->lens.main, LZX_MAINTREE_NUM_SYMBOLS);
-       lzx_update_mainsym_match_costs(LZX_BLOCKTYPE_ALIGNED, updated_main_lens);
-       aligned_cost += lzx_huffman_code_output_cost(updated_main_lens,
-                                                    freqs->main,
-                                                    LZX_MAINTREE_NUM_SYMBOLS);
-
-       common_cost += lzx_huffman_code_output_cost(codes->lens.len,
-                                                   freqs->len,
-                                                   LZX_LENTREE_NUM_SYMBOLS);
-
-       aligned_cost += lzx_huffman_code_output_cost(codes->lens.aligned,
-                                                    freqs->aligned,
-                                                    LZX_ALIGNEDTREE_NUM_SYMBOLS);
-
-       *aligned_cost_ret = aligned_cost + common_cost;
-       *verbatim_cost_ret = verbatim_cost + common_cost;
+       for (unsigned i = 0; i < ctx->num_blocks; i++)
+               lzx_optimize_block(ctx, &ctx->block_specs[i], num_passes);
 }
 
-/* Prepare a (nonsplit) compressed block.  */
-static unsigned
-lzx_prepare_compressed_block(struct lzx_compressor *ctx, unsigned block_number,
-                            struct lzx_codes *prev_codes)
-{
-       struct lzx_block_spec *spec = &ctx->block_specs[block_number - 1];
-       unsigned orig_cached_matches_pos = ctx->cached_matches_pos;
-       struct lzx_lru_queue orig_queue = ctx->queue;
-       struct lzx_freqs freqs;
-       unsigned cost;
-
-       /* Here's where the real work happens.  The following loop runs one or
-        * more times, each time using a cost model based on the Huffman codes
-        * computed from the previous iteration (the first iteration uses a
-        * default model).  Each iteration of the loop uses a heuristic
-        * algorithm to divide the block into near-optimal matches/literals from
-        * beginning to end.  */
-       LZX_ASSERT(ctx->params.alg_params.slow.num_optim_passes >= 1);
-       spec->num_chosen_matches = 0;
-       for (unsigned pass = 0; pass < ctx->params.alg_params.slow.num_optim_passes; pass++)
-       {
-               LZX_DEBUG("Block %u: Match-choosing pass %u of %u",
-                         block_number, pass + 1,
-                         ctx->params.alg_params.slow.num_optim_passes);
-
-               /* Reset frequency tables.  */
-               memset(&freqs, 0, sizeof(freqs));
+static bool entropy_val_tab_inited = false;
+static double entropy_val_tab[LZX_MAX_WINDOW_SIZE];
+static pthread_mutex_t entropy_val_tab_mutex = PTHREAD_MUTEX_INITIALIZER;
 
-               /* Reset match offset LRU queue.  */
-               ctx->queue = orig_queue;
+static double entropy_val(unsigned count)
+{
+       /*return count * log(count);*/
+       return entropy_val_tab[count];
+}
 
-               /* Reset match-finding position.  */
-               ctx->cached_matches_pos = orig_cached_matches_pos;
-               ctx->match_window_pos = spec->window_pos;
-               ctx->match_window_end = spec->window_pos + spec->block_size;
+/* Split a LZX block into several if it is advantageous to do so.
+ *
+ * TODO:  This doesn't work very well yet.  Should optimal parsing be done
+ * before or after splitting?  */
+static void
+lzx_block_split(const u32 matches[restrict],
+               const input_idx_t n,
+               const double epsilon,
+               const unsigned max_num_blocks,
+               const unsigned min_block_len,
+               struct lzx_block_spec block_specs[restrict],
+               unsigned * const restrict num_blocks_ret)
+{
+       const double block_overhead = 1500;
+
+       if (!entropy_val_tab_inited) {
+               pthread_mutex_lock(&entropy_val_tab_mutex);
+               if (!entropy_val_tab_inited) {
+                       entropy_val_tab[0] = 0;
+                       for (input_idx_t i = 1; i < LZX_MAX_WINDOW_SIZE; i++)
+                               entropy_val_tab[i] = i * log2(i);
+                       entropy_val_tab_inited = true;
+               }
+               pthread_mutex_unlock(&entropy_val_tab_mutex);
+       }
 
-               /* Set cost model.  */
-               lzx_set_costs(ctx, &spec->codes.lens);
+       u16 main_syms[n];
+       u8 len_syms[n];
+       u8 aligned_syms[n];
+       input_idx_t orig_input_indices[n + 1];
+
+       LZX_ASSERT(epsilon >= 0);
+       LZX_ASSERT(max_num_blocks >= 1);
+
+       /* For convenience, extract the main, length, and aligned symbols from
+        * the matches.  Every position will have a main symbol, but not every
+        * position will have a length and aligned symbol.  Special values
+        * larger than the valid symbols are used to indicate the absense of a
+        * symbol. */
+       orig_input_indices[0] = 0;
+       for (input_idx_t i = 0, orig_input_idx = 0; i < n; i++) {
+               u32 match = matches[i];
+               u16 main_sym;
+               u8 len_sym = LZX_LENCODE_NUM_SYMBOLS;
+               u8 aligned_sym = LZX_ALIGNEDCODE_NUM_SYMBOLS;
+               if (match & 0x80000000) {
+                       unsigned match_len_minus_2 = match & 0xff;
+                       unsigned position_footer = (match >> 8) & 0x1ffff;
+                       unsigned position_slot = (match >> 25) & 0x3f;
+                       unsigned len_header;
+
+                       if (match_len_minus_2 < LZX_NUM_PRIMARY_LENS) {
+                               len_header = match_len_minus_2;
+                       } else {
+                               len_header = LZX_NUM_PRIMARY_LENS;
+                               len_sym = match_len_minus_2 - LZX_NUM_PRIMARY_LENS;
+                       }
+                       main_sym = ((position_slot << 3) | len_header) + LZX_NUM_CHARS;
+                       if (position_slot >= 8)
+                               aligned_sym = position_footer & 7;
+                       orig_input_idx += match_len_minus_2 + 2;
+               } else {
+                       main_sym = match;
+                       orig_input_idx++;
+               }
+               main_syms[i] = main_sym;
+               len_syms[i] = len_sym;
+               aligned_syms[i] = aligned_sym;
+               orig_input_indices[i + 1] = orig_input_idx;
+       }
 
-               unsigned window_pos = spec->window_pos;
-               unsigned end = window_pos + spec->block_size;
+       /* Compute the number of sliding windows that will be used for the
+        * entropy calculations. */
+       int num_windows = 0;
+       unsigned window_len;
+       {
+               double e = min_block_len;
+               do {
+                       window_len = e;
+                       num_windows++;
+                       e *= epsilon + 1;
+               } while (window_len < n);
+       }
 
-               while (window_pos < end) {
-                       struct raw_match match;
-                       struct lzx_match lzx_match;
+       /* Compute the length of each sliding window. */
+       unsigned window_lens[num_windows];
+       {
+               double e = min_block_len;
+               unsigned window_idx = 0;
+               do {
+                       window_len = e;
+                       window_lens[window_idx++] = min(window_len, n);
+                       e *= epsilon + 1;
+               } while (window_len < n);
+       }
 
-                       match = lzx_lz_get_near_optimal_match(ctx);
+       /* Best estimated compression size, in bits, found so far for the input
+        * matches up to each position. */
+       unsigned shortest_paths[n + 1];
 
-                       if (match.len >= LZX_MIN_MATCH) {
+       /* Pointers to follow to get the sequence of blocks that represents the
+        * shortest path (in terms of estimated compressed size) up to each
+        * position in the input matches. */
+       input_idx_t back_ptrs[n + 1];
 
-                               /* Best to output a match here.  */
+       for (input_idx_t i = 0; i < n + 1; i++) {
+               shortest_paths[i] = ~0U;
+               back_ptrs[i] = 0;
+       }
+       shortest_paths[0] = 0;
 
-                               LZX_ASSERT(match.len <= LZX_MAX_MATCH);
-                               LZX_ASSERT(!memcmp(&ctx->window[window_pos],
-                                                  &ctx->window[window_pos - match.offset],
-                                                  match.len));
+       {
+               /* Initialize the per-window symbol and entropy counters */
+               input_idx_t mainsym_ctrs[num_windows][LZX_MAINCODE_NUM_SYMBOLS];
+               input_idx_t lensym_ctrs[num_windows][LZX_LENCODE_NUM_SYMBOLS + 1];
+               input_idx_t alignedsym_ctrs[num_windows][LZX_ALIGNEDCODE_NUM_SYMBOLS + 1];
+               ZERO_ARRAY(mainsym_ctrs);
+               ZERO_ARRAY(lensym_ctrs);
+               ZERO_ARRAY(alignedsym_ctrs);
 
-                               /* Tally symbol frequencies.  */
-                               lzx_match.data = lzx_record_match(match.offset,
-                                                                 match.len,
-                                                                 &freqs,
-                                                                 &ctx->queue);
+               {
+                       int start_win_idx = 0;
+                       for (input_idx_t i = 0; i < n; i++) {
+                               while (i >= window_lens[start_win_idx])
+                                       start_win_idx++;
+                               for (int j = start_win_idx; j < num_windows; j++) {
+                                       mainsym_ctrs[j][main_syms[i]]++;
+                                       lensym_ctrs[j][len_syms[i]]++;
+                                       alignedsym_ctrs[j][aligned_syms[i]]++;
+                               }
+                       }
+               }
 
-                               window_pos += match.len;
-                       } else {
-                               /* Best to output a literal here.  */
+               double entropy_ctrs[num_windows];
+               for (int i = 0; i < num_windows; i++) {
+                       entropy_ctrs[i] = 0;
+                       for (unsigned j = 0; j < LZX_MAINCODE_NUM_SYMBOLS; j++)
+                               entropy_ctrs[i] += entropy_val(mainsym_ctrs[i][j]);
+                       for (unsigned j = 0; j < LZX_LENCODE_NUM_SYMBOLS; j++)
+                               entropy_ctrs[i] += entropy_val(lensym_ctrs[i][j]);
+                       for (unsigned j = 0; j < LZX_ALIGNEDCODE_NUM_SYMBOLS; j++)
+                               entropy_ctrs[i] += entropy_val(alignedsym_ctrs[i][j]);
+               }
 
-                               /* Tally symbol frequencies.  */
-                               lzx_match.data = lzx_record_literal(ctx->window[window_pos],
-                                                                   &freqs);
+               /* Slide the windows along the input and compute the shortest
+                * path to each position in the matches. */
+               int end_window_idx = (int)num_windows - 1;
+               for (input_idx_t i = 0; i < n; i++) {
+                       for (int j = 0; j <= end_window_idx; j++) {
+                               if (shortest_paths[i] == ~0U)
+                                       continue;
+                               unsigned num_mainsyms = window_lens[j];
+                               unsigned num_lensyms = window_lens[j] -
+                                                      lensym_ctrs[j][LZX_LENCODE_NUM_SYMBOLS];
+                               unsigned num_alignedsyms = window_lens[j] -
+                                                          alignedsym_ctrs[j][LZX_ALIGNEDCODE_NUM_SYMBOLS];
+                               unsigned entropy = entropy_val(num_mainsyms) +
+                                                  entropy_val(num_lensyms) +
+                                                  entropy_val(num_alignedsyms) -
+                                                  entropy_ctrs[j];
+                               unsigned est_csize = entropy + block_overhead;
+
+                               unsigned end_idx = i + window_lens[j];
+                               if (est_csize + shortest_paths[i] < shortest_paths[end_idx]) {
+                                       shortest_paths[end_idx] = est_csize + shortest_paths[i];
+                                       back_ptrs[end_idx] = i;
+                               }
+                       }
+                       /* Remove left symbol from windows */
+                       for (int j = 0; j <= end_window_idx; j++) {
+                               input_idx_t orig_maincnt = mainsym_ctrs[j][main_syms[i]]--;
+                               entropy_ctrs[j] -= entropy_val(orig_maincnt);
+                               entropy_ctrs[j] += entropy_val(orig_maincnt - 1);
+
+                               input_idx_t orig_lencnt =
+                                               lensym_ctrs[j][len_syms[i]]--;
+                               if (len_syms[i] != LZX_LENCODE_NUM_SYMBOLS) {
+                                       entropy_ctrs[j] -= entropy_val(orig_lencnt);
+                                       entropy_ctrs[j] += entropy_val(orig_lencnt - 1);
+                               }
 
-                               window_pos += 1;
+                               input_idx_t orig_alignedcnt =
+                                               alignedsym_ctrs[j][aligned_syms[i]]--;
+                               if (aligned_syms[i] != LZX_ALIGNEDCODE_NUM_SYMBOLS) {
+                                       entropy_ctrs[j] -= entropy_val(orig_alignedcnt);
+                                       entropy_ctrs[j] += entropy_val(orig_alignedcnt - 1);
+                               }
                        }
 
-                       /* If it's the last pass, save the match/literal in
-                        * intermediate form.  */
-                       if (pass == ctx->params.alg_params.slow.num_optim_passes - 1) {
-                               ctx->chosen_matches[spec->chosen_matches_start_pos +
-                                                   spec->num_chosen_matches] = lzx_match;
+                       /* Calculate index of longest window remaining */
+                       while (end_window_idx >= 0 && window_lens[end_window_idx] >= n - i)
+                               end_window_idx--;
+
+                       /* Append right symbol to windows */
+                       for (int j = 0; j <= end_window_idx; j++) {
+                               input_idx_t orig_maincnt = mainsym_ctrs[j][
+                                                               main_syms[i + window_lens[j]]]++;
+                               entropy_ctrs[j] -= entropy_val(orig_maincnt);
+                               entropy_ctrs[j] += entropy_val(orig_maincnt + 1);
+
+                               input_idx_t orig_lencnt =
+                                       lensym_ctrs[j][len_syms[i + window_lens[j]]]++;
+                               if (len_syms[i + window_lens[j]] != LZX_LENCODE_NUM_SYMBOLS) {
+                                       entropy_ctrs[j] -= entropy_val(orig_lencnt);
+                                       entropy_ctrs[j] += entropy_val(orig_lencnt + 1);
+                               }
 
-                               spec->num_chosen_matches++;
+                               input_idx_t orig_alignedcnt =
+                                       alignedsym_ctrs[j][aligned_syms[i + window_lens[j]]]++;
+                               if (aligned_syms[i + window_lens[j]] != LZX_ALIGNEDCODE_NUM_SYMBOLS) {
+                                       entropy_ctrs[j] -= entropy_val(orig_alignedcnt);
+                                       entropy_ctrs[j] += entropy_val(orig_alignedcnt + 1);
+                               }
                        }
                }
-               LZX_ASSERT(window_pos == end);
+       }
 
-               /* Build Huffman codes using the new frequencies.  */
-               lzx_make_huffman_codes(&freqs, &spec->codes);
+#if 0
+       /* If no cost was computed for the first block (due to it being shorter
+        * than all the windows), merge it with the second block. */
+       for (input_idx_t i = n; i != 0; i = back_ptrs[i])
+               if (back_ptrs[i] != 0 && shortest_paths[back_ptrs[i]] == ~0U)
+                       back_ptrs[i] = 0;
+#endif
+
+       /* Calculate number of blocks */
+       input_idx_t num_blocks = 0;
+       for (input_idx_t i = n; i != 0; i = back_ptrs[i])
+               num_blocks++;
 
-               /* The first time we get here is when the full input has been
-                * processed, so the match-finding is done.  */
-               ctx->matches_already_found = true;
+       while (num_blocks > max_num_blocks) {
+               LZX_DEBUG("Joining blocks to bring total under max_num_blucks=%u",
+                         max_num_blocks);
+               back_ptrs[n] = back_ptrs[back_ptrs[n]];
+               num_blocks--;
        }
 
-       LZX_DEBUG("Block %u: saved %u matches/literals @ %u",
-                 block_number, spec->num_chosen_matches,
-                 spec->chosen_matches_start_pos);
+       LZX_ASSERT(num_blocks != 0);
 
-       unsigned aligned_cost;
-       unsigned verbatim_cost;
+       /* fill in the 'struct lzx_block_spec' for each block */
+       for (input_idx_t i = n, j = num_blocks - 1; i != 0; i = back_ptrs[i], j--) {
 
-       lzx_compute_compressed_block_costs(spec->block_size,
-                                          &spec->codes,
-                                          prev_codes,
-                                          &freqs,
-                                          &aligned_cost,
-                                          &verbatim_cost);
-
-       /* Choose whether to make the block aligned offset or verbatim.  */
-       if (aligned_cost < verbatim_cost) {
-               spec->block_type = LZX_BLOCKTYPE_ALIGNED;
-               cost = aligned_cost;
-               LZX_DEBUG("Using aligned block (cost %u vs %u for verbatim)",
-                         aligned_cost, verbatim_cost);
-       } else {
-               spec->block_type = LZX_BLOCKTYPE_VERBATIM;
-               cost = verbatim_cost;
-               LZX_DEBUG("Using verbatim block (cost %u vs %u for aligned)",
-                         verbatim_cost, aligned_cost);
-       }
+               block_specs[j].chosen_matches_start_pos = back_ptrs[i];
+               block_specs[j].num_chosen_matches = i - back_ptrs[i];
+               block_specs[j].window_pos = orig_input_indices[back_ptrs[i]];
+               block_specs[j].block_size = orig_input_indices[i] -
+                                           orig_input_indices[back_ptrs[i]];
+               /*block_specs[j].est_csize = (shortest_paths[i] -*/
+                                          /*shortest_paths[back_ptrs[i]]) / 8;*/
 
-       LZX_DEBUG("Block %u is %u => %u bytes unsplit.",
-                 block_number, spec->block_size, cost / 8);
+               LZX_DEBUG("block match_indices [%u, %u) est_csize %u bits\n",
+                         back_ptrs[i], i,
+                         shortest_paths[i] - shortest_paths[back_ptrs[i]]);
 
-       return cost;
+               struct lzx_freqs freqs = {};
+
+               for (input_idx_t k = back_ptrs[i]; k < i; k++) {
+                       freqs.main[main_syms[k]]++;
+                       if (len_syms[k] != LZX_LENCODE_NUM_SYMBOLS)
+                               freqs.len[len_syms[k]]++;
+                       if (aligned_syms[k] != LZX_LENCODE_NUM_SYMBOLS)
+                               freqs.aligned[aligned_syms[k]]++;
+               }
+               lzx_make_huffman_codes(&freqs, &block_specs[j].codes);
+
+               block_specs[j].block_type = lzx_choose_verbatim_or_aligned(&freqs,
+                                                                          &block_specs[j].codes);
+       }
+       *num_blocks_ret = num_blocks;
 }
 
-/*
- * lzx_prepare_block_recursive() -
- *
- * Given a (possibly nonproper) sub-sequence of the preprocessed input, compute
- * the LZX block(s) that it should be output as.
- *
- * This function initially considers the case where the given sub-sequence of
- * the preprocessed input be output as a single block.  This block is calculated
- * and its cost (number of bits required to output it) is computed.
- *
- * Then, if @max_split_level is greater than zero, a split into two evenly sized
- * subblocks is considered.  The block is recursively split in this way,
- * potentially up to the depth specified by @max_split_level.  The cost of the
- * split block is compared to the cost of the single block, and the lower cost
- * solution is used.
- *
- * For each compressed output block computed, the sequence of matches/literals
- * and the corresponding Huffman codes for the block are produced and saved.
- *
- * The return value is the approximate number of bits the block (or all
- * subblocks, in the case that the split block had lower cost), will take up
- * when written to the compressed output.
- */
-static unsigned
-lzx_prepare_block_recursive(struct lzx_compressor * ctx,
-                           unsigned block_number,
-                           unsigned max_split_level,
-                           struct lzx_codes **prev_codes_p)
-{
-       struct lzx_block_spec *spec = &ctx->block_specs[block_number - 1];
-       unsigned cost;
-       unsigned orig_cached_matches_pos;
-       struct lzx_lru_queue orig_queue, nonsplit_queue;
-       struct lzx_codes *prev_codes = *prev_codes_p;
 
-       LZX_DEBUG("Preparing block %u...", block_number);
+/* Initialize the suffix array match-finder for the specified input.  */
+static void
+lzx_lz_init_matchfinder(const u8 T[const restrict],
+                       const input_idx_t n,
+                       input_idx_t SA[const restrict],
+                       input_idx_t ISA[const restrict],
+                       input_idx_t LCP[const restrict],
+                       struct salink link[const restrict],
+                       const unsigned max_match_len)
+{
+       /* Compute SA (Suffix Array).  */
 
-       /* Save positions of chosen and cached matches, and the match offset LRU
-        * queue, so that they can be restored if splitting is attempted.  */
-       orig_cached_matches_pos = ctx->cached_matches_pos;
-       orig_queue = ctx->queue;
+       {
+               saidx_t sa[n];
+               /* ISA and link are used as temporary space.  */
+               BUILD_BUG_ON(LZX_MAX_WINDOW_SIZE * sizeof(ISA[0]) < 256 * sizeof(saidx_t));
+               BUILD_BUG_ON(LZX_MAX_WINDOW_SIZE * 2 * sizeof(link[0]) < 256 * 256 * sizeof(saidx_t));
+               divsufsort(T, sa, n, (saidx_t*)ISA, (saidx_t*)link);
+               for (input_idx_t i = 0; i < n; i++)
+                       SA[i] = sa[i];
+       }
 
-       /* Consider outputting the input subsequence as a single block.  */
-       spec->is_split = 0;
-       cost = lzx_prepare_compressed_block(ctx, block_number, prev_codes);
-       nonsplit_queue = ctx->queue;
+#ifdef ENABLE_LZX_DEBUG
 
-       *prev_codes_p = &spec->codes;
+       LZX_ASSERT(n > 0);
 
-       /* If the maximum split level is at least one, consider splitting the
-        * block in two.  */
-       if (max_split_level--) {
+       /* Verify suffix array.  */
+       {
+               bool found[n];
+               ZERO_ARRAY(found);
+               for (input_idx_t r = 0; r < n; r++) {
+                       input_idx_t i = SA[r];
+                       LZX_ASSERT(i < n);
+                       LZX_ASSERT(!found[i]);
+                       found[i] = true;
+               }
+       }
 
-               LZX_DEBUG("Calculating split of block %u...", block_number);
+       for (input_idx_t r = 0; r < n - 1; r++) {
 
-               struct lzx_block_spec *spec1, *spec2;
-               unsigned split_cost;
+               input_idx_t i1 = SA[r];
+               input_idx_t i2 = SA[r + 1];
 
-               ctx->cached_matches_pos = orig_cached_matches_pos;
-               ctx->queue = orig_queue;
+               input_idx_t n1 = n - i1;
+               input_idx_t n2 = n - i2;
 
-               /* Prepare and get the cost of the first sub-block.  */
-               spec1 = &ctx->block_specs[block_number * 2 - 1];
-               spec1->codes.lens = spec->codes.lens;
-               spec1->window_pos = spec->window_pos;
-               spec1->block_size = spec->block_size / 2;
-               spec1->chosen_matches_start_pos = spec->chosen_matches_start_pos +
-                                                 LZX_MAX_WINDOW_SIZE;
-               split_cost = lzx_prepare_block_recursive(ctx,
-                                                        block_number * 2,
-                                                        max_split_level,
-                                                        &prev_codes);
-
-               /* Prepare and get the cost of the second sub-block.  */
-               spec2 = spec1 + 1;
-               spec2->codes.lens = spec->codes.lens;
-               spec2->window_pos = spec->window_pos + spec1->block_size;
-               spec2->block_size = spec->block_size - spec1->block_size;
-               spec2->chosen_matches_start_pos = spec1->chosen_matches_start_pos +
-                                                 spec1->block_size;
-               split_cost += lzx_prepare_block_recursive(ctx,
-                                                         block_number * 2 + 1,
-                                                         max_split_level,
-                                                         &prev_codes);
-
-               /* Compare the cost of the whole block with that of the split
-                * block.  Choose the lower cost solution.  */
-               if (split_cost < cost) {
-                       LZX_DEBUG("Splitting block %u is worth it "
-                                 "(%u => %u bytes).",
-                                 block_number, cost / 8, split_cost / 8);
-                       spec->is_split = 1;
-                       cost = split_cost;
-                       *prev_codes_p = prev_codes;
-               } else {
-                       LZX_DEBUG("Splitting block %u is NOT worth it "
-                                 "(%u => %u bytes).",
-                                 block_number, cost / 8, split_cost / 8);
-                       ctx->queue = nonsplit_queue;
-               }
+               LZX_ASSERT(memcmp(&T[i1], &T[i2], min(n1, n2)) <= 0);
        }
+       LZX_DEBUG("Verified SA (len %u)", n);
+#endif /* ENABLE_LZX_DEBUG */
 
-       return cost;
-}
+       /* Compute ISA (Inverse Suffix Array)  */
+       for (input_idx_t r = 0; r < n; r++)
+               ISA[SA[r]] = r;
 
-/* Empirical averages  */
-static const u8 lzx_default_mainsym_costs[LZX_MAINTREE_NUM_SYMBOLS] = {
-       7, 9, 9, 10, 9, 10, 10, 10, 9, 10, 9, 10, 10, 9, 10, 10, 9, 10, 10, 11,
-       10, 10, 10, 11, 10, 11, 11, 11, 10, 11, 11, 11, 8, 11, 9, 10, 9, 10, 11,
-       11, 9, 9, 11, 10, 10, 9, 9, 9, 8, 8, 8, 8, 8, 9, 9, 9, 8, 8, 9, 9, 9, 9,
-       10, 10, 10, 8, 9, 8, 8, 8, 8, 9, 9, 9, 10, 10, 8, 8, 9, 9, 8, 10, 9, 8,
-       8, 9, 8, 9, 9, 10, 10, 10, 9, 10, 11, 9, 10, 8, 9, 8, 8, 8, 8, 9, 8, 8,
-       9, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 8, 9, 9, 9, 9, 10, 11, 10, 10, 11, 11,
-       10, 11, 11, 10, 10, 11, 11, 11, 10, 10, 11, 10, 11, 10, 11, 11, 10, 11,
-       11, 12, 11, 11, 11, 12, 11, 11, 11, 11, 11, 11, 11, 12, 10, 11, 11, 11,
-       11, 11, 11, 12, 11, 11, 11, 11, 11, 12, 11, 11, 10, 11, 11, 11, 11, 11,
-       11, 11, 10, 11, 11, 11, 11, 11, 11, 11, 10, 11, 11, 11, 11, 11, 11, 11,
-       10, 11, 11, 11, 11, 11, 11, 11, 10, 11, 11, 11, 11, 12, 11, 11, 10, 11,
-       11, 11, 11, 12, 11, 11, 10, 11, 11, 11, 10, 12, 11, 11, 10, 10, 11, 10,
-       10, 11, 11, 11, 10, 11, 11, 11, 10, 11, 11, 11, 10, 11, 11, 11, 10, 11,
-       10, 9, 8, 7, 10, 10, 11, 10, 11, 7, 9, 9, 11, 11, 11, 12, 11, 9, 10, 10,
-       12, 12, 13, 13, 12, 11, 10, 12, 12, 14, 14, 14, 13, 12, 9, 12, 13, 14,
-       14, 14, 14, 14, 9, 10, 13, 14, 14, 14, 14, 14, 9, 9, 11, 11, 13, 13, 13,
-       14, 9, 9, 11, 12, 12, 13, 13, 13, 8, 8, 11, 11, 12, 12, 12, 11, 9, 9,
-       10, 11, 12, 12, 12, 11, 8, 9, 10, 10, 11, 12, 11, 10, 9, 9, 10, 11, 11,
-       12, 11, 10, 8, 9, 10, 10, 11, 11, 11, 9, 9, 9, 10, 11, 11, 11, 11, 9, 8,
-       8, 10, 10, 11, 11, 11, 9, 9, 9, 10, 10, 11, 11, 11, 9, 9, 8, 9, 10, 11,
-       11, 11, 9, 10, 9, 10, 11, 11, 11, 11, 9, 14, 9, 9, 10, 10, 11, 10, 9,
-       14, 9, 10, 11, 11, 11, 11, 9, 14, 9, 10, 10, 11, 11, 11, 9, 14, 10, 10,
-       11, 11, 12, 11, 10, 14, 10, 10, 10, 11, 11, 11, 10, 14, 11, 11, 11, 11,
-       12, 12, 10, 14, 10, 11, 11, 11, 12, 11, 10, 14, 11, 11, 11, 12, 12, 12,
-       11, 15, 11, 11, 11, 12, 12, 12, 11, 14, 12, 12, 12, 12, 13, 12, 11, 15,
-       12, 12, 12, 13, 13, 13, 12, 15, 14, 13, 14, 14, 14, 14, 13,
-};
+       /* Compute LCP (longest common prefix) array.
+        *
+        * Algorithm adapted from Kasai et al. 2001: "Linear-Time
+        * Longest-Common-Prefix Computation in Suffix Arrays and Its
+        * Applications".  */
+       {
+               input_idx_t h = 0;
+               for (input_idx_t i = 0; i < n; i++) {
+                       input_idx_t r = ISA[i];
+                       if (r > 0) {
+                               input_idx_t j = SA[r - 1];
+
+                               input_idx_t lim = min(n - i, n - j);
+
+                               while (h < lim && T[i + h] == T[j + h])
+                                       h++;
+                               LCP[r] = h;
+                               if (h > 0)
+                                       h--;
+                       }
+               }
+       }
 
-/* Empirical averages  */
-static const u8 lzx_default_lensym_costs[LZX_LENTREE_NUM_SYMBOLS] = {
-       5, 5, 5, 5, 5, 6, 5, 5, 6, 7, 7, 7, 8, 8, 7, 8, 9, 9, 9, 9, 10, 9, 9,
-       10, 9, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 11, 12, 12,
-       12, 12, 12, 12, 13, 12, 12, 12, 13, 12, 13, 13, 12, 12, 13, 12, 13, 13,
-       13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 13, 14, 13, 14, 13,
-       14, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
-       14, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
-       14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
-       14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
-       14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
-       14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
-       14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
-       14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
-       14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
-       14, 14, 14, 14, 14, 14, 14, 14, 14, 10,
-};
+#ifdef ENABLE_LZX_DEBUG
+       /* Verify LCP array.  */
+       for (input_idx_t r = 0; r < n - 1; r++) {
+               LZX_ASSERT(ISA[SA[r]] == r);
+               LZX_ASSERT(ISA[SA[r + 1]] == r + 1);
 
-/*
- * Set default symbol costs.
- */
-static void
-lzx_set_default_costs(struct lzx_lens * lens)
-{
-       unsigned i;
+               input_idx_t i1 = SA[r];
+               input_idx_t i2 = SA[r + 1];
+               input_idx_t lcp = LCP[r + 1];
 
-#if LZX_PARAM_USE_EMPIRICAL_DEFAULT_COSTS
-       memcpy(&lens->main, lzx_default_mainsym_costs, LZX_MAINTREE_NUM_SYMBOLS);
-       memcpy(&lens->len, lzx_default_lensym_costs, LZX_LENTREE_NUM_SYMBOLS);
+               input_idx_t n1 = n - i1;
+               input_idx_t n2 = n - i2;
 
-#else
-       /* Literal symbols  */
-       for (i = 0; i < LZX_NUM_CHARS; i++)
-               lens->main[i] = 8;
+               LZX_ASSERT(lcp <= min(n1, n2));
 
-       /* Match header symbols  */
-       for (; i < LZX_MAINTREE_NUM_SYMBOLS; i++)
-               lens->main[i] = 10;
+               LZX_ASSERT(memcmp(&T[i1], &T[i2], lcp) == 0);
+               if (lcp < min(n1, n2))
+                       LZX_ASSERT(T[i1 + lcp] != T[i2 + lcp]);
+       }
+#endif /* ENABLE_LZX_DEBUG */
 
-       /* Length symbols  */
-       for (i = 0; i < LZX_LENTREE_NUM_SYMBOLS; i++)
-               lens->len[i] = 8;
-#endif
+       /* Compute salink.next and salink.lcpnext.
+        *
+        * Algorithm adapted from Crochemore et al. 2009:
+        * "LPF computation revisited".
+        *
+        * Note: we cap lcpnext to the maximum match length so that the
+        * match-finder need not worry about it later.  */
+       link[n - 1].next = (input_idx_t)~0U;
+       link[n - 1].prev = (input_idx_t)~0U;
+       link[n - 1].lcpnext = 0;
+       link[n - 1].lcpprev = 0;
+       for (input_idx_t r = n - 2; r != (input_idx_t)~0U; r--) {
+               input_idx_t t = r + 1;
+               input_idx_t l = LCP[t];
+               while (t != (input_idx_t)~0 && SA[t] > SA[r]) {
+                       l = min(l, link[t].lcpnext);
+                       t = link[t].next;
+               }
+               link[r].next = t;
+               link[r].lcpnext = min(l, max_match_len);
+               LZX_ASSERT(t == (input_idx_t)~0 || l <= n - SA[t]);
+               LZX_ASSERT(l <= n - SA[r]);
+               LZX_ASSERT(memcmp(&T[SA[r]], &T[SA[t]], l) == 0);
+       }
 
-       /* Aligned offset symbols  */
-       for (i = 0; i < LZX_ALIGNEDTREE_NUM_SYMBOLS; i++)
-               lens->aligned[i] = 3;
+       /* Compute salink.prev and salink.lcpprev.
+        *
+        * Algorithm adapted from Crochemore et al. 2009:
+        * "LPF computation revisited".
+        *
+        * Note: we cap lcpprev to the maximum match length so that the
+        * match-finder need not worry about it later.  */
+       link[0].prev = (input_idx_t)~0;
+       link[0].next = (input_idx_t)~0;
+       link[0].lcpprev = 0;
+       link[0].lcpnext = 0;
+       for (input_idx_t r = 1; r < n; r++) {
+               input_idx_t t = r - 1;
+               input_idx_t l = LCP[r];
+               while (t != (input_idx_t)~0 && SA[t] > SA[r]) {
+                       l = min(l, link[t].lcpprev);
+                       t = link[t].prev;
+               }
+               link[r].prev = t;
+               link[r].lcpprev = min(l, max_match_len);
+               LZX_ASSERT(t == (input_idx_t)~0 || l <= n - SA[t]);
+               LZX_ASSERT(l <= n - SA[r]);
+               LZX_ASSERT(memcmp(&T[SA[r]], &T[SA[t]], l) == 0);
+       }
 }
 
-/*
- * lzx_prepare_blocks() -
- *
- * Calculate the blocks to split the preprocessed data into.
- *
- * Input ---  the preprocessed data:
- *
- *     ctx->window[]
- *     ctx->window_size
- *
- * Working space:
- *     Match finding:
- *             ctx->hash_tab
- *             ctx->child_tab
- *             ctx->cached_matches
- *             ctx->cached_matches_pos
- *             ctx->matches_already_found
- *
- *     Block cost modeling:
- *             ctx->costs
- *             ctx->block_specs (also an output)
- *
- *     Match choosing:
- *             ctx->optimum
- *             ctx->optimum_cur_idx
- *             ctx->optimum_end_idx
- *             ctx->chosen_matches (also an output)
- *
- * Output --- the block specifications and the corresponding match/literal data:
- *
- *     ctx->block_specs[]
- *     ctx->chosen_matches[]
- *
- * The return value is the approximate number of bits the compressed data will
- * take up.
- */
-static unsigned
+/* Prepare the input window into one or more LZX blocks ready to be output.  */
+static void
 lzx_prepare_blocks(struct lzx_compressor * ctx)
 {
-       /* This function merely does some initializations, then passes control
-        * to lzx_prepare_block_recursive().  */
-
-       /* 1. Initialize match-finding variables.  */
-
-       /* Zero all entries in the hash table, indicating that no length-3
-        * character sequences have been discovered in the input yet.  */
-       memset(ctx->hash_tab, 0, LZX_LZ_HASH_SIZE * 2 * sizeof(ctx->hash_tab[0]));
-       if (ctx->params.alg_params.slow.use_len2_matches)
-               memset(ctx->digram_tab, 0, 256 * 256 * sizeof(ctx->digram_tab[0]));
-       /* Note: ctx->child_tab need not be initialized.  */
-
-       /* No matches have been found and cached yet.  */
+       /* Initialize the match-finder.  */
+       lzx_lz_init_matchfinder(ctx->window, ctx->window_size,
+                               ctx->SA, ctx->ISA, ctx->LCP, ctx->salink,
+                               LZX_MAX_MATCH_LEN);
        ctx->cached_matches_pos = 0;
-       ctx->matches_already_found = false;
+       ctx->matches_cached = false;
+       ctx->match_window_pos = 0;
 
-       /* 2. Initialize match-choosing variables.  */
-       ctx->optimum_cur_idx = 0;
-       ctx->optimum_end_idx = 0;
-       /* Note: ctx->optimum need not be initialized.  */
-       ctx->block_specs[0].chosen_matches_start_pos = 0;
+       /* Set up a default cost model.  */
+       lzx_set_default_costs(&ctx->costs);
 
-       /* 3. Set block 1 (index 0) to represent the entire input data.  */
-       ctx->block_specs[0].block_size = ctx->window_size;
+       /* Initially assume that the entire input will be one LZX block.  */
+       ctx->block_specs[0].block_type = LZX_BLOCKTYPE_ALIGNED;
        ctx->block_specs[0].window_pos = 0;
+       ctx->block_specs[0].block_size = ctx->window_size;
+       ctx->num_blocks = 1;
 
-       /* 4. Set up a default Huffman symbol cost model for block 1 (index 0).
-        * The model will be refined later.  */
-       lzx_set_default_costs(&ctx->block_specs[0].codes.lens);
+       /* Perform near-optimal LZ parsing.  */
+       lzx_optimize_blocks(ctx);
 
-       /* 5. Initialize the match offset LRU queue.  */
-       ctx->queue = (struct lzx_lru_queue){1, 1, 1};
+       /* Possibly divide up the LZX block.  */
+       const unsigned max_num_blocks = 1U << ctx->params.alg_params.slow.num_split_passes;
+       if (max_num_blocks > 1) {
+               const double epsilon = 0.2;
+               const unsigned min_block_len = 500;
 
-       /* 6. Pass control to recursive procedure.  */
-       struct lzx_codes * prev_codes = &ctx->zero_codes;
-       return lzx_prepare_block_recursive(ctx, 1,
-                                          ctx->params.alg_params.slow.num_split_passes,
-                                          &prev_codes);
+               lzx_block_split((const u32*)ctx->chosen_matches,
+                               ctx->block_specs[0].num_chosen_matches,
+                               epsilon, max_num_blocks, min_block_len,
+                               ctx->block_specs, &ctx->num_blocks);
+       }
 }
 
 /*
@@ -2416,9 +2519,10 @@ lzx_prepare_blocks(struct lzx_compressor * ctx)
  * Working space:
  *     ctx->queue
  *
- * Output --- the block specifications and the corresponding match/literal data:
+ * Output --- the block specification and the corresponding match/literal data:
  *
  *     ctx->block_specs[]
+ *     ctx->num_blocks
  *     ctx->chosen_matches[]
  */
 static void
@@ -2428,23 +2532,23 @@ lzx_prepare_block_fast(struct lzx_compressor * ctx)
        struct lzx_freqs freqs;
        struct lzx_block_spec *spec;
 
-       /* Parameters to hash chain LZ match finder  */
+       /* Parameters to hash chain LZ match finder
+        * (lazy with 1 match lookahead)  */
        static const struct lz_params lzx_lz_params = {
-               /* LZX_MIN_MATCH == 2, but 2-character matches are rarely
-                * useful; the minimum match for compression is set to 3
-                * instead. */
+               /* Although LZX_MIN_MATCH_LEN == 2, length 2 matches typically
+                * aren't worth choosing when using greedy or lazy parsing.  */
                .min_match      = 3,
-               .max_match      = LZX_MAX_MATCH,
-               .good_match     = LZX_MAX_MATCH,
-               .nice_match     = LZX_MAX_MATCH,
-               .max_chain_len  = LZX_MAX_MATCH,
-               .max_lazy_match = LZX_MAX_MATCH,
+               .max_match      = LZX_MAX_MATCH_LEN,
+               .good_match     = LZX_MAX_MATCH_LEN,
+               .nice_match     = LZX_MAX_MATCH_LEN,
+               .max_chain_len  = LZX_MAX_MATCH_LEN,
+               .max_lazy_match = LZX_MAX_MATCH_LEN,
                .too_far        = 4096,
        };
 
        /* Initialize symbol frequencies and match offset LRU queue.  */
        memset(&freqs, 0, sizeof(struct lzx_freqs));
-       ctx->queue = (struct lzx_lru_queue){ 1, 1, 1 };
+       lzx_lru_queue_init(&ctx->queue);
 
        /* Determine series of matches/literals to output.  */
        num_matches = lz_analyze_block(ctx->window,
@@ -2460,13 +2564,13 @@ lzx_prepare_block_fast(struct lzx_compressor * ctx)
 
        /* Set up block specification.  */
        spec = &ctx->block_specs[0];
-       spec->is_split = 0;
        spec->block_type = LZX_BLOCKTYPE_ALIGNED;
        spec->window_pos = 0;
        spec->block_size = ctx->window_size;
        spec->num_chosen_matches = num_matches;
        spec->chosen_matches_start_pos = 0;
        lzx_make_huffman_codes(&freqs, &spec->codes);
+       ctx->num_blocks = 1;
 }
 
 static void
@@ -2572,12 +2676,16 @@ wimlib_lzx_compress2(const void                 * const restrict uncompressed_data,
        LZX_DEBUG("Done: compressed %u => %u bytes.",
                  uncompressed_len, compressed_len);
 
-#if defined(ENABLE_LZX_DEBUG) || defined(ENABLE_VERIFY_COMPRESSION)
-       /* Verify that we really get the same thing back when decompressing.  */
+       /* Verify that we really get the same thing back when decompressing.
+        * TODO: Disable this check by default on the slow algorithm.  */
+       if (ctx->params.algorithm == WIMLIB_LZX_ALGORITHM_SLOW
+       #if defined(ENABLE_LZX_DEBUG) || defined(ENABLE_VERIFY_COMPRESSION)
+           || 1
+       #endif
+           )
        {
                u8 buf[uncompressed_len];
                int ret;
-               unsigned i;
 
                ret = wimlib_lzx_decompress(compressed_data, compressed_len,
                                            buf, uncompressed_len);
@@ -2588,23 +2696,13 @@ wimlib_lzx_compress2(const void                 * const restrict uncompressed_data,
                        return 0;
                }
 
-               bool bad = false;
-               const u8 * udata = uncompressed_data;
-               for (i = 0; i < uncompressed_len; i++) {
-                       if (buf[i] != udata[i]) {
-                               bad = true;
-                               ERROR("Data we compressed using LZX algorithm "
-                                     "didn't decompress to original "
-                                     "(difference at idx %u: c %#02x, u %#02x)",
-                                     i, buf[i], udata[i]);
-                       }
-               }
-               if (bad) {
+               if (memcmp(uncompressed_data, buf, uncompressed_len)) {
+                       ERROR("Data we compressed using LZX algorithm "
+                             "didn't decompress to original");
                        wimlib_assert(0);
                        return 0;
                }
        }
-#endif
        return compressed_len;
 }
 
@@ -2615,6 +2713,75 @@ lzx_params_compatible(const struct wimlib_lzx_params *oldparams,
        return 0 == memcmp(oldparams, newparams, sizeof(struct wimlib_lzx_params));
 }
 
+static struct wimlib_lzx_params lzx_user_default_params;
+static struct wimlib_lzx_params *lzx_user_default_params_ptr;
+
+static bool
+lzx_params_valid(const struct wimlib_lzx_params *params)
+{
+       /* Validate parameters.  */
+       if (params->size_of_this != sizeof(struct wimlib_lzx_params)) {
+               LZX_DEBUG("Invalid parameter structure size!");
+               return false;
+       }
+
+       if (params->algorithm != WIMLIB_LZX_ALGORITHM_SLOW &&
+           params->algorithm != WIMLIB_LZX_ALGORITHM_FAST)
+       {
+               LZX_DEBUG("Invalid algorithm.");
+               return false;
+       }
+
+       if (params->algorithm == WIMLIB_LZX_ALGORITHM_SLOW) {
+               if (params->alg_params.slow.num_optim_passes < 1)
+               {
+                       LZX_DEBUG("Invalid number of optimization passes!");
+                       return false;
+               }
+
+               if (params->alg_params.slow.main_nostat_cost < 1 ||
+                   params->alg_params.slow.main_nostat_cost > 16)
+               {
+                       LZX_DEBUG("Invalid main_nostat_cost!");
+                       return false;
+               }
+
+               if (params->alg_params.slow.len_nostat_cost < 1 ||
+                   params->alg_params.slow.len_nostat_cost > 16)
+               {
+                       LZX_DEBUG("Invalid len_nostat_cost!");
+                       return false;
+               }
+
+               if (params->alg_params.slow.aligned_nostat_cost < 1 ||
+                   params->alg_params.slow.aligned_nostat_cost > 8)
+               {
+                       LZX_DEBUG("Invalid aligned_nostat_cost!");
+                       return false;
+               }
+
+               if (params->alg_params.slow.num_split_passes > 31) {
+                       LZX_DEBUG("Invalid num_split_passes!");
+                       return false;
+               }
+       }
+       return true;
+}
+
+WIMLIBAPI int
+wimlib_lzx_set_default_params(const struct wimlib_lzx_params * params)
+{
+       if (params) {
+               if (!lzx_params_valid(params))
+                       return WIMLIB_ERR_INVALID_PARAM;
+               lzx_user_default_params = *params;
+               lzx_user_default_params_ptr = &lzx_user_default_params;
+       } else {
+               lzx_user_default_params_ptr = NULL;
+       }
+       return 0;
+}
+
 /* API function documented in wimlib.h  */
 WIMLIBAPI int
 wimlib_lzx_alloc_context(const struct wimlib_lzx_params *params,
@@ -2642,8 +2809,10 @@ wimlib_lzx_alloc_context(const struct wimlib_lzx_params *params,
                        .slow = {
                                .use_len2_matches = 1,
                                .num_fast_bytes = 32,
-                               .num_optim_passes = 3,
-                               .num_split_passes = 3,
+                               .num_optim_passes = 2,
+                               .num_split_passes = 0,
+                               .max_search_depth = 50,
+                               .max_matches_per_pos = 3,
                                .main_nostat_cost = 15,
                                .len_nostat_cost = 15,
                                .aligned_nostat_cost = 7,
@@ -2651,16 +2820,15 @@ wimlib_lzx_alloc_context(const struct wimlib_lzx_params *params,
                },
        };
 
-       if (params == NULL) {
+       if (params) {
+               if (!lzx_params_valid(params))
+                       return WIMLIB_ERR_INVALID_PARAM;
+       } else {
                LZX_DEBUG("Using default algorithm and parameters.");
-               params = &fast_default;
-       }
-
-       if (params->algorithm != WIMLIB_LZX_ALGORITHM_SLOW &&
-           params->algorithm != WIMLIB_LZX_ALGORITHM_FAST)
-       {
-               LZX_DEBUG("Invalid algorithm.");
-               return WIMLIB_ERR_INVALID_PARAM;
+               if (lzx_user_default_params_ptr)
+                       params = lzx_user_default_params_ptr;
+               else
+                       params = &slow_default;
        }
 
        if (params->use_defaults) {
@@ -2670,57 +2838,16 @@ wimlib_lzx_alloc_context(const struct wimlib_lzx_params *params,
                        params = &fast_default;
        }
 
-       if (params->size_of_this != sizeof(struct wimlib_lzx_params)) {
-               LZX_DEBUG("Invalid parameter structure size!");
-               return WIMLIB_ERR_INVALID_PARAM;
-       }
-
-       if (params->algorithm == WIMLIB_LZX_ALGORITHM_SLOW) {
-               if (params->alg_params.slow.num_fast_bytes < 3 ||
-                   params->alg_params.slow.num_fast_bytes > 257)
-               {
-                       LZX_DEBUG("Invalid number of fast bytes!");
-                       return WIMLIB_ERR_INVALID_PARAM;
-               }
-
-               if (params->alg_params.slow.num_optim_passes < 1)
-               {
-                       LZX_DEBUG("Invalid number of optimization passes!");
-                       return WIMLIB_ERR_INVALID_PARAM;
-               }
-
-               if (params->alg_params.slow.main_nostat_cost < 1 ||
-                   params->alg_params.slow.main_nostat_cost > 16)
-               {
-                       LZX_DEBUG("Invalid main_nostat_cost!");
-                       return WIMLIB_ERR_INVALID_PARAM;
-               }
-
-               if (params->alg_params.slow.len_nostat_cost < 1 ||
-                   params->alg_params.slow.len_nostat_cost > 16)
-               {
-                       LZX_DEBUG("Invalid len_nostat_cost!");
-                       return WIMLIB_ERR_INVALID_PARAM;
-               }
-
-               if (params->alg_params.slow.aligned_nostat_cost < 1 ||
-                   params->alg_params.slow.aligned_nostat_cost > 8)
-               {
-                       LZX_DEBUG("Invalid aligned_nostat_cost!");
-                       return WIMLIB_ERR_INVALID_PARAM;
-               }
-       }
+       if (ctx_pp) {
+               ctx = *(struct lzx_compressor**)ctx_pp;
 
-       if (ctx_pp == NULL) {
+               if (ctx && lzx_params_compatible(&ctx->params, params))
+                       return 0;
+       } else {
                LZX_DEBUG("Check parameters only.");
                return 0;
        }
 
-       ctx = *(struct lzx_compressor**)ctx_pp;
-
-       if (ctx && lzx_params_compatible(&ctx->params, params))
-               return 0;
-
        LZX_DEBUG("Allocating memory.");
 
        ctx = MALLOC(sizeof(struct lzx_compressor));
@@ -2730,63 +2857,57 @@ wimlib_lzx_alloc_context(const struct wimlib_lzx_params *params,
        size_t block_specs_length;
 
        if (params->algorithm == WIMLIB_LZX_ALGORITHM_SLOW)
-               block_specs_length = ((1 << (params->alg_params.slow.num_split_passes + 1)) - 1);
+               block_specs_length = 1U << params->alg_params.slow.num_split_passes;
        else
-               block_specs_length = 1;
+               block_specs_length = 1U;
        ctx->block_specs = MALLOC(block_specs_length * sizeof(ctx->block_specs[0]));
        if (ctx->block_specs == NULL)
                goto err_free_ctx;
 
        if (params->algorithm == WIMLIB_LZX_ALGORITHM_SLOW) {
-               ctx->hash_tab = MALLOC((LZX_LZ_HASH_SIZE + 2 * LZX_MAX_WINDOW_SIZE) *
-                                       sizeof(ctx->hash_tab[0]));
-               if (ctx->hash_tab == NULL)
+               ctx->SA = MALLOC(3U * LZX_MAX_WINDOW_SIZE * sizeof(ctx->SA[0]));
+               if (ctx->SA == NULL)
                        goto err_free_block_specs;
-               ctx->child_tab = ctx->hash_tab + LZX_LZ_HASH_SIZE;
+               ctx->ISA = ctx->SA + LZX_MAX_WINDOW_SIZE;
+               ctx->LCP = ctx->ISA + LZX_MAX_WINDOW_SIZE;
+               ctx->salink = MALLOC(LZX_MAX_WINDOW_SIZE * sizeof(ctx->salink[0]));
+               if (ctx->salink == NULL)
+                       goto err_free_SA;
        } else {
-               ctx->hash_tab = NULL;
-               ctx->child_tab = NULL;
+               ctx->SA = NULL;
+               ctx->ISA = NULL;
+               ctx->LCP = NULL;
+               ctx->salink = NULL;
        }
 
-       if (params->algorithm == WIMLIB_LZX_ALGORITHM_SLOW &&
-           params->alg_params.slow.use_len2_matches)
-       {
-               ctx->digram_tab = MALLOC(256 * 256 * sizeof(ctx->digram_tab[0]));
-               if (ctx->digram_tab == NULL)
-                       goto err_free_hash_tab;
+       if (params->algorithm == WIMLIB_LZX_ALGORITHM_SLOW) {
+               ctx->optimum = MALLOC((LZX_OPTIM_ARRAY_SIZE + LZX_MAX_MATCH_LEN) *
+                                      sizeof(ctx->optimum[0]));
+               if (ctx->optimum == NULL)
+                       goto err_free_salink;
        } else {
-               ctx->digram_tab = NULL;
+               ctx->optimum = NULL;
        }
 
        if (params->algorithm == WIMLIB_LZX_ALGORITHM_SLOW) {
-               ctx->cached_matches = MALLOC(10 * LZX_MAX_WINDOW_SIZE *
+               uint32_t cache_per_pos;
+
+               cache_per_pos = params->alg_params.slow.max_matches_per_pos;
+               if (cache_per_pos > LZX_MAX_CACHE_PER_POS)
+                       cache_per_pos = LZX_MAX_CACHE_PER_POS;
+
+               ctx->cached_matches = MALLOC(LZX_MAX_WINDOW_SIZE * (cache_per_pos + 1) *
                                             sizeof(ctx->cached_matches[0]));
                if (ctx->cached_matches == NULL)
-                       goto err_free_digram_tab;
+                       goto err_free_optimum;
        } else {
                ctx->cached_matches = NULL;
        }
 
-       if (params->algorithm == WIMLIB_LZX_ALGORITHM_SLOW) {
-               ctx->optimum = MALLOC((LZX_PARAM_OPTIM_ARRAY_SIZE + LZX_MAX_MATCH) *
-                                      sizeof(ctx->optimum[0]));
-               if (ctx->optimum == NULL)
-                       goto err_free_cached_matches;
-       } else {
-               ctx->optimum = NULL;
-       }
-
-       size_t chosen_matches_length;
-       if (params->algorithm == WIMLIB_LZX_ALGORITHM_SLOW)
-               chosen_matches_length = LZX_MAX_WINDOW_SIZE *
-                                       (params->alg_params.slow.num_split_passes + 1);
-       else
-               chosen_matches_length = LZX_MAX_WINDOW_SIZE;
-
-       ctx->chosen_matches = MALLOC(chosen_matches_length *
+       ctx->chosen_matches = MALLOC(LZX_MAX_WINDOW_SIZE *
                                     sizeof(ctx->chosen_matches[0]));
        if (ctx->chosen_matches == NULL)
-               goto err_free_optimum;
+               goto err_free_cached_matches;
 
        memcpy(&ctx->params, params, sizeof(struct wimlib_lzx_params));
        memset(&ctx->zero_codes, 0, sizeof(ctx->zero_codes));
@@ -2797,14 +2918,14 @@ wimlib_lzx_alloc_context(const struct wimlib_lzx_params *params,
        *ctx_pp = (struct wimlib_lzx_context*)ctx;
        return 0;
 
-err_free_optimum:
-       FREE(ctx->optimum);
 err_free_cached_matches:
        FREE(ctx->cached_matches);
-err_free_digram_tab:
-       FREE(ctx->digram_tab);
-err_free_hash_tab:
-       FREE(ctx->hash_tab);
+err_free_optimum:
+       FREE(ctx->optimum);
+err_free_salink:
+       FREE(ctx->salink);
+err_free_SA:
+       FREE(ctx->SA);
 err_free_block_specs:
        FREE(ctx->block_specs);
 err_free_ctx:
@@ -2821,11 +2942,11 @@ wimlib_lzx_free_context(struct wimlib_lzx_context *_ctx)
        struct lzx_compressor *ctx = (struct lzx_compressor*)_ctx;
 
        if (ctx) {
+               FREE(ctx->cached_matches);
                FREE(ctx->chosen_matches);
                FREE(ctx->optimum);
-               FREE(ctx->cached_matches);
-               FREE(ctx->digram_tab);
-               FREE(ctx->hash_tab);
+               FREE(ctx->SA);
+               FREE(ctx->salink);
                FREE(ctx->block_specs);
                FREE(ctx);
        }
index d25d188..935c636 100644 (file)
 /* Huffman decoding tables and maps from symbols to code lengths. */
 struct lzx_tables {
 
-       u16 maintree_decode_table[(1 << LZX_MAINTREE_TABLEBITS) +
-                                       (LZX_MAINTREE_NUM_SYMBOLS * 2)]
+       u16 maintree_decode_table[(1 << LZX_MAINCODE_TABLEBITS) +
+                                       (LZX_MAINCODE_NUM_SYMBOLS * 2)]
                                        _aligned_attribute(DECODE_TABLE_ALIGNMENT);
-       u8 maintree_lens[LZX_MAINTREE_NUM_SYMBOLS];
+       u8 maintree_lens[LZX_MAINCODE_NUM_SYMBOLS];
 
 
-       u16 lentree_decode_table[(1 << LZX_LENTREE_TABLEBITS) +
-                                       (LZX_LENTREE_NUM_SYMBOLS * 2)]
+       u16 lentree_decode_table[(1 << LZX_LENCODE_TABLEBITS) +
+                                       (LZX_LENCODE_NUM_SYMBOLS * 2)]
                                        _aligned_attribute(DECODE_TABLE_ALIGNMENT);
-       u8 lentree_lens[LZX_LENTREE_NUM_SYMBOLS];
+       u8 lentree_lens[LZX_LENCODE_NUM_SYMBOLS];
 
 
-       u16 alignedtree_decode_table[(1 << LZX_ALIGNEDTREE_TABLEBITS) +
-                                       (LZX_ALIGNEDTREE_NUM_SYMBOLS * 2)]
+       u16 alignedtree_decode_table[(1 << LZX_ALIGNEDCODE_TABLEBITS) +
+                                       (LZX_ALIGNEDCODE_NUM_SYMBOLS * 2)]
                                        _aligned_attribute(DECODE_TABLE_ALIGNMENT);
-       u8 alignedtree_lens[LZX_ALIGNEDTREE_NUM_SYMBOLS];
+       u8 alignedtree_lens[LZX_ALIGNEDCODE_NUM_SYMBOLS];
 } _aligned_attribute(DECODE_TABLE_ALIGNMENT);
 
 
@@ -148,8 +148,8 @@ read_huffsym_using_pretree(struct input_bitstream *istream,
                           const u8 pretree_lens[], unsigned *n)
 {
        return read_huffsym(istream, pretree_decode_table, pretree_lens,
-                           LZX_PRETREE_NUM_SYMBOLS, LZX_PRETREE_TABLEBITS, n,
-                           LZX_MAX_CODEWORD_LEN);
+                           LZX_PRECODE_NUM_SYMBOLS, LZX_PRECODE_TABLEBITS, n,
+                           LZX_MAX_PRE_CODEWORD_LEN);
 }
 
 /* Reads a Huffman-encoded symbol using the main tree. */
@@ -159,8 +159,8 @@ read_huffsym_using_maintree(struct input_bitstream *istream,
                            unsigned *n)
 {
        return read_huffsym(istream, tables->maintree_decode_table,
-                           tables->maintree_lens, LZX_MAINTREE_NUM_SYMBOLS,
-                           LZX_MAINTREE_TABLEBITS, n, LZX_MAX_CODEWORD_LEN);
+                           tables->maintree_lens, LZX_MAINCODE_NUM_SYMBOLS,
+                           LZX_MAINCODE_TABLEBITS, n, LZX_MAX_MAIN_CODEWORD_LEN);
 }
 
 /* Reads a Huffman-encoded symbol using the length tree. */
@@ -170,8 +170,8 @@ read_huffsym_using_lentree(struct input_bitstream *istream,
                           unsigned *n)
 {
        return read_huffsym(istream, tables->lentree_decode_table,
-                           tables->lentree_lens, LZX_LENTREE_NUM_SYMBOLS,
-                           LZX_LENTREE_TABLEBITS, n, LZX_MAX_CODEWORD_LEN);
+                           tables->lentree_lens, LZX_LENCODE_NUM_SYMBOLS,
+                           LZX_LENCODE_TABLEBITS, n, LZX_MAX_LEN_CODEWORD_LEN);
 }
 
 /* Reads a Huffman-encoded symbol using the aligned offset tree. */
@@ -182,8 +182,9 @@ read_huffsym_using_alignedtree(struct input_bitstream *istream,
 {
        return read_huffsym(istream, tables->alignedtree_decode_table,
                            tables->alignedtree_lens,
-                           LZX_ALIGNEDTREE_NUM_SYMBOLS,
-                           LZX_ALIGNEDTREE_TABLEBITS, n, 8);
+                           LZX_ALIGNEDCODE_NUM_SYMBOLS,
+                           LZX_ALIGNEDCODE_TABLEBITS, n,
+                           LZX_MAX_ALIGNED_CODEWORD_LEN);
 }
 
 /*
@@ -203,31 +204,31 @@ lzx_read_code_lens(struct input_bitstream *istream, u8 lens[],
                   unsigned num_lens)
 {
        /* Declare the decoding table and length table for the pretree. */
-       u16 pretree_decode_table[(1 << LZX_PRETREE_TABLEBITS) +
-                                       (LZX_PRETREE_NUM_SYMBOLS * 2)]
+       u16 pretree_decode_table[(1 << LZX_PRECODE_TABLEBITS) +
+                                       (LZX_PRECODE_NUM_SYMBOLS * 2)]
                                        _aligned_attribute(DECODE_TABLE_ALIGNMENT);
-       u8 pretree_lens[LZX_PRETREE_NUM_SYMBOLS];
+       u8 pretree_lens[LZX_PRECODE_NUM_SYMBOLS];
        unsigned i;
        unsigned len;
        int ret;
 
        /* Read the code lengths of the pretree codes.  There are 20 lengths of
         * 4 bits each. */
-       for (i = 0; i < LZX_PRETREE_NUM_SYMBOLS; i++) {
-               ret = bitstream_read_bits(istream, LZX_PRETREE_ELEMENT_SIZE,
+       for (i = 0; i < LZX_PRECODE_NUM_SYMBOLS; i++) {
+               ret = bitstream_read_bits(istream, LZX_PRECODE_ELEMENT_SIZE,
                                          &len);
-               if (ret != 0)
+               if (ret)
                        return ret;
                pretree_lens[i] = len;
        }
 
        /* Make the decoding table for the pretree. */
        ret = make_huffman_decode_table(pretree_decode_table,
-                                       LZX_PRETREE_NUM_SYMBOLS,
-                                       LZX_PRETREE_TABLEBITS,
+                                       LZX_PRECODE_NUM_SYMBOLS,
+                                       LZX_PRECODE_TABLEBITS,
                                        pretree_lens,
-                                       LZX_MAX_CODEWORD_LEN);
-       if (ret != 0)
+                                       LZX_MAX_PRE_CODEWORD_LEN);
+       if (ret)
                return ret;
 
        /* Pointer past the last length value that needs to be filled in. */
@@ -249,12 +250,12 @@ lzx_read_code_lens(struct input_bitstream *istream, u8 lens[],
 
                ret = read_huffsym_using_pretree(istream, pretree_decode_table,
                                                 pretree_lens, &tree_code);
-               if (ret != 0)
+               if (ret)
                        return ret;
                switch (tree_code) {
                case 17: /* Run of 0's */
                        ret = bitstream_read_bits(istream, 4, &num_zeroes);
-                       if (ret != 0)
+                       if (ret)
                                return ret;
                        num_zeroes += 4;
                        while (num_zeroes--) {
@@ -265,7 +266,7 @@ lzx_read_code_lens(struct input_bitstream *istream, u8 lens[],
                        break;
                case 18: /* Longer run of 0's */
                        ret = bitstream_read_bits(istream, 5, &num_zeroes);
-                       if (ret != 0)
+                       if (ret)
                                return ret;
                        num_zeroes += 20;
                        while (num_zeroes--) {
@@ -276,14 +277,14 @@ lzx_read_code_lens(struct input_bitstream *istream, u8 lens[],
                        break;
                case 19: /* Run of identical lengths */
                        ret = bitstream_read_bits(istream, 1, &num_same);
-                       if (ret != 0)
+                       if (ret)
                                return ret;
                        num_same += 4;
                        ret = read_huffsym_using_pretree(istream,
                                                         pretree_decode_table,
                                                         pretree_lens,
                                                         &code);
-                       if (ret != 0)
+                       if (ret)
                                return ret;
                        value = (signed char)*lens - (signed char)code;
                        if (value < 0)
@@ -364,9 +365,9 @@ lzx_read_block_header(struct input_bitstream *istream,
                /* Read the path lengths for the elements of the aligned tree,
                 * then build it. */
 
-               for (i = 0; i < LZX_ALIGNEDTREE_NUM_SYMBOLS; i++) {
+               for (i = 0; i < LZX_ALIGNEDCODE_NUM_SYMBOLS; i++) {
                        ret = bitstream_read_bits(istream,
-                                                 LZX_ALIGNEDTREE_ELEMENT_SIZE,
+                                                 LZX_ALIGNEDCODE_ELEMENT_SIZE,
                                                  &len);
                        if (ret)
                                return ret;
@@ -375,10 +376,10 @@ lzx_read_block_header(struct input_bitstream *istream,
 
                LZX_DEBUG("Building the aligned tree.");
                ret = make_huffman_decode_table(tables->alignedtree_decode_table,
-                                               LZX_ALIGNEDTREE_NUM_SYMBOLS,
-                                               LZX_ALIGNEDTREE_TABLEBITS,
+                                               LZX_ALIGNEDCODE_NUM_SYMBOLS,
+                                               LZX_ALIGNEDCODE_TABLEBITS,
                                                tables->alignedtree_lens,
-                                               8);
+                                               LZX_MAX_ALIGNED_CODEWORD_LEN);
                if (ret) {
                        LZX_DEBUG("Failed to make the decode table for the "
                                  "aligned offset tree");
@@ -407,10 +408,10 @@ lzx_read_block_header(struct input_bitstream *istream,
                 * tree. */
                LZX_DEBUG("Reading path lengths for remaining elements of "
                          "main tree (%d elements).",
-                         LZX_MAINTREE_NUM_SYMBOLS - LZX_NUM_CHARS);
+                         LZX_MAINCODE_NUM_SYMBOLS - LZX_NUM_CHARS);
                ret = lzx_read_code_lens(istream,
                                         tables->maintree_lens + LZX_NUM_CHARS,
-                                        LZX_MAINTREE_NUM_SYMBOLS - LZX_NUM_CHARS);
+                                        LZX_MAINCODE_NUM_SYMBOLS - LZX_NUM_CHARS);
                if (ret) {
                        LZX_DEBUG("Failed to read the path lengths for the "
                                  "remaining elements of the main tree");
@@ -421,10 +422,10 @@ lzx_read_block_header(struct input_bitstream *istream,
                          "table for the main tree.");
 
                ret = make_huffman_decode_table(tables->maintree_decode_table,
-                                               LZX_MAINTREE_NUM_SYMBOLS,
-                                               LZX_MAINTREE_TABLEBITS,
+                                               LZX_MAINCODE_NUM_SYMBOLS,
+                                               LZX_MAINCODE_TABLEBITS,
                                                tables->maintree_lens,
-                                               LZX_MAX_CODEWORD_LEN);
+                                               LZX_MAX_MAIN_CODEWORD_LEN);
                if (ret) {
                        LZX_DEBUG("Failed to make the decode "
                                  "table for the main tree");
@@ -433,7 +434,7 @@ lzx_read_block_header(struct input_bitstream *istream,
 
                LZX_DEBUG("Reading path lengths for the length tree.");
                ret = lzx_read_code_lens(istream, tables->lentree_lens,
-                                        LZX_LENTREE_NUM_SYMBOLS);
+                                        LZX_LENCODE_NUM_SYMBOLS);
                if (ret) {
                        LZX_DEBUG("Failed to read the path "
                                  "lengths for the length tree");
@@ -442,10 +443,10 @@ lzx_read_block_header(struct input_bitstream *istream,
 
                LZX_DEBUG("Building the length tree.");
                ret = make_huffman_decode_table(tables->lentree_decode_table,
-                                               LZX_LENTREE_NUM_SYMBOLS,
-                                               LZX_LENTREE_TABLEBITS,
+                                               LZX_LENCODE_NUM_SYMBOLS,
+                                               LZX_LENCODE_TABLEBITS,
                                                tables->lentree_lens,
-                                               LZX_MAX_CODEWORD_LEN);
+                                               LZX_MAX_LEN_CODEWORD_LEN);
                if (ret) {
                        LZX_DEBUG("Failed to build the length Huffman tree");
                        return ret;
@@ -478,9 +479,9 @@ lzx_read_block_header(struct input_bitstream *istream,
                        istream->bitsleft = 0;
                        istream->bitbuf = 0;
                }
-               queue->R0 = le32_to_cpu(*(u32*)(istream->data + 0));
-               queue->R1 = le32_to_cpu(*(u32*)(istream->data + 4));
-               queue->R2 = le32_to_cpu(*(u32*)(istream->data + 8));
+               queue->R[0] = le32_to_cpu(*(u32*)(istream->data + 0));
+               queue->R[1] = le32_to_cpu(*(u32*)(istream->data + 4));
+               queue->R[2] = le32_to_cpu(*(u32*)(istream->data + 8));
                istream->data += 12;
                istream->data_bytes_left -= 12;
                /* The uncompressed data of this block directly follows and will
@@ -561,14 +562,15 @@ lzx_decode_match(unsigned main_element, int block_type,
        position_slot = main_element >> 3;
 
        /* If the length_header is less than LZX_NUM_PRIMARY_LENS (= 7), it
-        * gives the match length as the offset from LZX_MIN_MATCH.  Otherwise,
-        * the length is given by an additional symbol encoded using the length
-        * tree, offset by 9 (LZX_MIN_MATCH + LZX_NUM_PRIMARY_LENS) */
-       match_len = LZX_MIN_MATCH + length_header;
+        * gives the match length as the offset from LZX_MIN_MATCH_LEN.
+        * Otherwise, the length is given by an additional symbol encoded using
+        * the length tree, offset by 9 (LZX_MIN_MATCH_LEN +
+        * LZX_NUM_PRIMARY_LENS) */
+       match_len = LZX_MIN_MATCH_LEN + length_header;
        if (length_header == LZX_NUM_PRIMARY_LENS) {
                ret = read_huffsym_using_lentree(istream, tables,
                                                 &additional_len);
-               if (ret != 0)
+               if (ret)
                        return ret;
                match_len += additional_len;
        }
@@ -579,18 +581,18 @@ lzx_decode_match(unsigned main_element, int block_type,
         * queue. */
        switch (position_slot) {
        case 0:
-               match_offset = queue->R0;
+               match_offset = queue->R[0];
                break;
        case 1:
-               match_offset = queue->R1;
-               swap(queue->R0, queue->R1);
+               match_offset = queue->R[1];
+               swap(queue->R[0], queue->R[1]);
                break;
        case 2:
                /* The queue doesn't work quite the same as a real LRU queue,
                 * since using the R2 offset doesn't bump the R1 offset down to
                 * R2. */
-               match_offset = queue->R2;
-               swap(queue->R0, queue->R2);
+               match_offset = queue->R[2];
+               swap(queue->R[0], queue->R[2]);
                break;
        default:
                /* Otherwise, the offset was not encoded as one the offsets in
@@ -617,14 +619,14 @@ lzx_decode_match(unsigned main_element, int block_type,
                         * will just set it to 0. ) */
                        ret = bitstream_read_bits(istream, num_extra_bits - 3,
                                                  &verbatim_bits);
-                       if (ret != 0)
+                       if (ret)
                                return ret;
 
                        verbatim_bits <<= 3;
 
                        ret = read_huffsym_using_alignedtree(istream, tables,
                                                             &aligned_bits);
-                       if (ret != 0)
+                       if (ret)
                                return ret;
                } else {
                        /* For non-aligned blocks, or for aligned blocks with
@@ -633,7 +635,7 @@ lzx_decode_match(unsigned main_element, int block_type,
                         * the alignment is taken to be 0. */
                        ret = bitstream_read_bits(istream, num_extra_bits,
                                                  &verbatim_bits);
-                       if (ret != 0)
+                       if (ret)
                                return ret;
 
                        aligned_bits = 0;
@@ -641,12 +643,12 @@ lzx_decode_match(unsigned main_element, int block_type,
 
                /* Calculate the match offset. */
                match_offset = lzx_position_base[position_slot] +
-                              verbatim_bits + aligned_bits - 2;
+                              verbatim_bits + aligned_bits - LZX_OFFSET_OFFSET;
 
                /* Update the LRU queue. */
-               queue->R2 = queue->R1;
-               queue->R1 = queue->R0;
-               queue->R0 = match_offset;
+               queue->R[2] = queue->R[1];
+               queue->R[1] = queue->R[0];
+               queue->R[0] = match_offset;
                break;
        }
 
@@ -784,7 +786,7 @@ lzx_decompress_block(int block_type, unsigned block_size,
                        /* literal: 0 to LZX_NUM_CHARS - 1 */
                        window[window_pos++] = main_element;
                } else {
-                       /* match: LZX_NUM_CHARS to LZX_MAINTREE_NUM_SYMBOLS - 1 */
+                       /* match: LZX_NUM_CHARS to LZX_MAINCODE_NUM_SYMBOLS - 1 */
                        match_len = lzx_decode_match(main_element,
                                                     block_type,
                                                     end - window_pos,
@@ -824,9 +826,7 @@ wimlib_lzx_decompress(const void *compressed_data, unsigned compressed_len,
 
        memset(tables.maintree_lens, 0, sizeof(tables.maintree_lens));
        memset(tables.lentree_lens, 0, sizeof(tables.lentree_lens));
-       queue.R0 = 1;
-       queue.R1 = 1;
-       queue.R2 = 1;
+       lzx_lru_queue_init(&queue);
        init_input_bitstream(&istream, compressed_data, compressed_len);
 
        e8_preprocessing_done = false; /* Set to true if there may be 0xe8 bytes
index d728e54..ca5a230 100644 (file)
 #  include <sys/uio.h> /* for `struct iovec' */
 #endif
 
-static int
-alloc_lzx_context(int write_resource_flags, struct wimlib_lzx_context **ctx_pp)
-{
-       struct wimlib_lzx_params params;
-       params.size_of_this = sizeof(params);
-       if (write_resource_flags & WIMLIB_WRITE_RESOURCE_FLAG_COMPRESS_SLOW)
-               params.algorithm = WIMLIB_LZX_ALGORITHM_SLOW;
-       else
-               params.algorithm = WIMLIB_LZX_ALGORITHM_FAST;
-       params.use_defaults = 1;
-       return wimlib_lzx_alloc_context(&params, ctx_pp);
-}
-
 static unsigned
 compress_chunk(const void * uncompressed_data,
               unsigned uncompressed_len,
@@ -378,8 +365,6 @@ error:
  * @resource_flags:
  *     * WIMLIB_WRITE_RESOURCE_FLAG_RECOMPRESS to force data to be recompressed even
  *       if it could otherwise be copied directly from the input;
- *     * WIMLIB_WRITE_RESOURCE_FLAG_COMPRESS_SLOW to compress the data as much
- *       as possible;
  *     * WIMLIB_WRITE_RESOURCE_FLAG_PIPABLE if writing a resource for a pipable WIM
  *       (and the output file descriptor may be a pipe).
  *
@@ -452,7 +437,7 @@ write_wim_resource(struct wim_lookup_table_entry *lte,
                if (!(resource_flags & WIMLIB_READ_RESOURCE_FLAG_RAW)) {
                        write_ctx.out_ctype = out_ctype;
                        if (out_ctype == WIMLIB_COMPRESSION_TYPE_LZX) {
-                               ret = alloc_lzx_context(resource_flags, comp_ctx);
+                               ret = wimlib_lzx_alloc_context(NULL, comp_ctx);
                                if (ret)
                                        goto out;
                        }
@@ -950,8 +935,6 @@ write_flags_to_resource_flags(int write_flags)
 
        if (write_flags & WIMLIB_WRITE_FLAG_RECOMPRESS)
                resource_flags |= WIMLIB_WRITE_RESOURCE_FLAG_RECOMPRESS;
-       if (write_flags & WIMLIB_WRITE_FLAG_COMPRESS_SLOW)
-               resource_flags |= WIMLIB_WRITE_RESOURCE_FLAG_COMPRESS_SLOW;
        if (write_flags & WIMLIB_WRITE_FLAG_PIPABLE)
                resource_flags |= WIMLIB_WRITE_RESOURCE_FLAG_PIPABLE;
        return resource_flags;
@@ -1540,8 +1523,7 @@ write_stream_list_parallel(struct list_head *stream_list,
                params[i].compressed_res_queue = &compressed_res_queue;
                params[i].out_ctype = out_ctype;
                if (out_ctype == WIMLIB_COMPRESSION_TYPE_LZX) {
-                       ret = alloc_lzx_context(write_resource_flags,
-                                               &params[i].comp_ctx);
+                       ret = wimlib_lzx_alloc_context(NULL, &params[i].comp_ctx);
                        if (ret)
                                goto out_free_params;
                }