/*
* lz_sarray.c
*
- * Suffix array match-finder for LZ (Lempel-Ziv) compression.
+ * Suffix array match-finder for Lempel-Ziv compression.
*/
/*
- * Copyright (c) 2013 Eric Biggers. All rights reserved.
+ * Copyright (c) 2013, 2014 Eric Biggers. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
#include "divsufsort/divsufsort.h"
#include <string.h>
-/* Initialize the suffix array match-finder with the specified parameters.
+#define DIVSUFSORT_TMP1_SIZE (256 * sizeof(saidx_t)) /* bucket_A */
+#define DIVSUFSORT_TMP2_SIZE (256 * 256 * sizeof(saidx_t)) /* bucket_B */
+
+/* If ENABLE_LZ_DEBUG is defined, verify that the suffix array satisfies its
+ * definition.
*
- * After initialization, it can be used for any number of input strings of
- * length less than or equal to @max_window_size. */
-bool
-lz_sarray_init(struct lz_sarray *mf,
- input_idx_t max_window_size,
- input_idx_t min_match_len,
- input_idx_t max_match_len,
- u32 max_matches_to_consider,
- u32 max_matches_to_return)
+ * @SA The constructed suffix array.
+ * @T The original data.
+ * @found Temporary 'bool' array of length @n.
+ * @n Length of the data (length of @SA, @T, and @found arrays).
+ *
+ * WARNING: this is for debug use only as it does not necessarily run in linear
+ * time!!! */
+static void
+verify_suffix_array(const lz_sarray_pos_t SA[restrict],
+ const u8 T[restrict],
+ bool found[restrict],
+ lz_sarray_pos_t n)
{
- mf->max_window_size = max_window_size;
- mf->min_match_len = min_match_len;
- mf->max_match_len = max_match_len;
- mf->max_matches_to_consider = max_matches_to_consider;
- mf->max_matches_to_return = max_matches_to_return;
+#ifdef ENABLE_LZ_DEBUG
+ /* Ensure the SA contains exactly one of each i in [0, n - 1]. */
+ for (lz_sarray_pos_t i = 0; i < n; i++)
+ found[i] = false;
+ for (lz_sarray_pos_t r = 0; r < n; r++) {
+ lz_sarray_pos_t i = SA[r];
+ LZ_ASSERT(i < n);
+ LZ_ASSERT(!found[i]);
+ found[i] = true;
+ }
- mf->SA = MALLOC(3U * max_window_size * sizeof(mf->SA[0]));
- if (mf->SA == NULL)
- return false;
+ /* Ensure the suffix with rank r is lexicographically lesser than the
+ * suffix with rank (r + 1) for all r in [0, n - 2]. */
+ for (lz_sarray_pos_t r = 0; r < n - 1; r++) {
- mf->salink = MALLOC(max_window_size * sizeof(mf->salink[0]));
- if (mf->salink == NULL)
- return false;
+ lz_sarray_pos_t i1 = SA[r];
+ lz_sarray_pos_t i2 = SA[r + 1];
- return true;
-}
+ lz_sarray_pos_t n1 = n - i1;
+ lz_sarray_pos_t n2 = n - i2;
-/* Free memory allocated for the suffix array match-finder. */
-void
-lz_sarray_destroy(struct lz_sarray *mf)
-{
- FREE(mf->SA);
- FREE(mf->salink);
+ int res = memcmp(&T[i1], &T[i2], min(n1, n2));
+ LZ_ASSERT(res < 0 || (res == 0 && n1 < n2));
+ }
+#endif /* ENABLE_LZ_DEBUG */
}
-/* Initialize the suffix array match-finder for the specified input. */
-void
-lz_sarray_load_window(struct lz_sarray *mf, const u8 window[],
- input_idx_t window_size)
+/* Compute the inverse suffix array @ISA from the suffix array @SA in linear
+ * time.
+ *
+ * Whereas the suffix array is a mapping from suffix rank to suffix position,
+ * the inverse suffix array is a mapping from suffix position to suffix rank.
+ */
+static void
+compute_inverse_suffix_array(lz_sarray_pos_t ISA[restrict],
+ const lz_sarray_pos_t SA[restrict],
+ lz_sarray_pos_t n)
{
- /* Load variables */
- const u8 * const restrict T = window;
- const input_idx_t n = window_size;
- const input_idx_t max_match_len = mf->max_match_len;
- input_idx_t * const restrict SA = mf->SA;
- input_idx_t * const restrict ISA = mf->ISA = SA + window_size;
- input_idx_t * const restrict LCP = mf->LCP = ISA + window_size;
- struct salink * const restrict link = mf->salink;
-
- /* Compute SA (Suffix Array). */
- {
- /* ISA and link are used as temporary space. */
- LZ_ASSERT(mf->max_window_size * sizeof(ISA[0]) >= 256 * sizeof(saidx_t));
- LZ_ASSERT(mf->max_window_size * 2 * sizeof(link[0]) >= 256 * 256 * sizeof(saidx_t));
-
- if (sizeof(input_idx_t) == sizeof(saidx_t)) {
- divsufsort(T, SA, n, (saidx_t*)ISA, (saidx_t*)link);
- } else {
- saidx_t sa[n];
- divsufsort(T, sa, n, (saidx_t*)ISA, (saidx_t*)link);
- for (input_idx_t i = 0; i < n; i++)
- SA[i] = sa[i];
- }
- }
+ lz_sarray_pos_t r;
-#ifdef ENABLE_LZ_DEBUG
-
- LZ_ASSERT(n > 0);
-
- /* Verify suffix array. */
- {
- bool found[n];
- ZERO_ARRAY(found);
- for (input_idx_t r = 0; r < n; r++) {
- input_idx_t i = SA[r];
- LZ_ASSERT(i < n);
- LZ_ASSERT(!found[i]);
- found[i] = true;
- }
- }
-
- for (input_idx_t r = 0; r < n - 1; r++) {
-
- input_idx_t i1 = SA[r];
- input_idx_t i2 = SA[r + 1];
+ for (r = 0; r < n; r++)
+ ISA[SA[r]] = r;
+}
- input_idx_t n1 = n - i1;
- input_idx_t n2 = n - i2;
- LZ_ASSERT(memcmp(&T[i1], &T[i2], min(n1, n2)) <= 0);
- }
- LZ_DEBUG("Verified SA (len %u)", n);
-#endif /* ENABLE_LZ_DEBUG */
+/* Compute the LCP (Longest Common Prefix) array in linear time.
+ *
+ * LCP[r] will be the length of the longest common prefix between the suffixes
+ * with positions SA[r - 1] and SA[r]. LCP[0] will be undefined.
+ *
+ * Algorithm adapted from Kasai et al. 2001: "Linear-Time Longest-Common-Prefix
+ * Computation in Suffix Arrays and Its Applications". Modified slightly to
+ * take into account that with bytes in the real world, there is no unique
+ * symbol at the end of the string. */
+static void
+compute_lcp_array(lz_sarray_pos_t LCP[restrict],
+ const lz_sarray_pos_t SA[restrict],
+ const lz_sarray_pos_t ISA[restrict],
+ const u8 T[restrict],
+ lz_sarray_pos_t n)
+{
+ lz_sarray_pos_t h, i, r, j, lim;
- /* Compute ISA (Inverse Suffix Array) */
- for (input_idx_t r = 0; r < n; r++)
- ISA[SA[r]] = r;
+ h = 0;
+ for (i = 0; i < n; i++) {
+ r = ISA[i];
+ if (r > 0) {
+ j = SA[r - 1];
+ lim = min(n - i, n - j);
- /* 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--;
- }
+ while (h < lim && T[i + h] == T[j + h])
+ h++;
+ LCP[r] = h;
+ if (h > 0)
+ h--;
}
}
+}
+/* If ENABLE_LZ_DEBUG is defined, verify that the LCP (Longest Common Prefix)
+ * array satisfies its definition.
+ *
+ * WARNING: this is for debug use only as it does not necessarily run in linear
+ * time!!! */
+static void
+verify_lcp_array(lz_sarray_pos_t LCP[restrict],
+ const lz_sarray_pos_t SA[restrict],
+ const u8 T[restrict],
+ lz_sarray_pos_t n)
+{
#ifdef ENABLE_LZ_DEBUG
- /* Verify LCP array. */
- for (input_idx_t r = 0; r < n - 1; r++) {
- LZ_ASSERT(ISA[SA[r]] == r);
- LZ_ASSERT(ISA[SA[r + 1]] == r + 1);
-
- input_idx_t i1 = SA[r];
- input_idx_t i2 = SA[r + 1];
- input_idx_t lcp = LCP[r + 1];
+ for (lz_sarray_pos_t r = 0; r < n - 1; r++) {
+ lz_sarray_pos_t i1 = SA[r];
+ lz_sarray_pos_t i2 = SA[r + 1];
+ lz_sarray_pos_t lcp = LCP[r + 1];
- input_idx_t n1 = n - i1;
- input_idx_t n2 = n - i2;
+ lz_sarray_pos_t n1 = n - i1;
+ lz_sarray_pos_t n2 = n - i2;
LZ_ASSERT(lcp <= min(n1, n2));
LZ_ASSERT(T[i1 + lcp] != T[i2 + lcp]);
}
#endif /* ENABLE_LZ_DEBUG */
+}
- /* Compute salink.next and salink.lcpnext.
+/* Initialize the SA link array in linear time.
+ *
+ * This is similar to computing the LPF (Longest Previous Factor) array, which
+ * is addressed in several papers. In particular the algorithms below are based
+ * on Crochemore et al. 2009: "LPF computation revisited". However, this
+ * match-finder does not actually compute or use the LPF array per se. Rather,
+ * this function sets up some information necessary to compute the LPF array,
+ * but later lz_sarray_get_matches() actually uses this information to search
+ * the suffix array directly and can keep searching beyond the first (longest)
+ * match whose length would be placed in the LPF array. This difference from
+ * the theoretical work is necessary because in many real compression formats
+ * matches take variable numbers of bits to encode, so a decent parser needs to
+ * consider more than just the longest match with unspecified offset.
+ *
+ * Note: We cap the lcpprev and lcpnext values to the maximum match length so
+ * that the match-finder need not worry about it later, in the inner loop.
+ *
+ * Note: the LCP array is one of the inputs to this function, but it is used as
+ * temporary space and therefore will be invalidated.
+ */
+static void
+init_salink(struct salink link[restrict],
+ lz_sarray_pos_t LCP[restrict],
+ const lz_sarray_pos_t SA[restrict],
+ const u8 T[restrict],
+ lz_sarray_pos_t n,
+ lz_sarray_len_t min_match_len,
+ lz_sarray_len_t max_match_len)
+{
+ /* Calculate salink.dist_to_next and salink.lcpnext.
*
- * Algorithm adapted from Crochemore et al. 2009:
- * "LPF computation revisited".
+ * Pass 1 calculates, for each suffix rank, the corresponding
+ * "next_initial" value which is the smallest larger rank that
+ * corresponds to a suffix starting earlier in the string. It also
+ * calculates "lcpnext_initial", which is the longest common prefix with
+ * that suffix, although to eliminate checks in lz_sarray_get_matches(),
+ * "lcpnext_initial" is set to 0 if it's less than the minimum match
+ * length or set to the maximum match length if it's greater than the
+ * maximum match length.
*
- * 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)0;
- link[n - 1].prev = ~(input_idx_t)0;
- link[n - 1].lcpnext = 0;
- link[n - 1].lcpprev = 0;
- for (input_idx_t r = n - 2; r != ~(input_idx_t)0; 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;
+ * Pass 2 translates each absolute "next_initial", a 4-byte value, into
+ * a relative "dist_to_next", a 1-byte value. This is done to save
+ * memory. In the case that the exact relative distance cannot be
+ * encoded in 1 byte, it is capped to 255. This is valid as long as
+ * lz_sarray_get_matches() validates each position before using it.
+ * Note that "lcpnext" need not be updated in this case because it will
+ * not be used until the actual next rank has been found anyway.
+ */
+ link[n - 1].next_initial = LZ_SARRAY_POS_MAX;
+ link[n - 1].lcpnext_initial = 0;
+ for (lz_sarray_pos_t r = n - 2; r != LZ_SARRAY_POS_MAX; r--) {
+ lz_sarray_pos_t t = r + 1;
+ lz_sarray_pos_t l = LCP[t];
+ while (t != LZ_SARRAY_POS_MAX && SA[t] > SA[r]) {
+ l = min(l, link[t].lcpnext_initial);
+ t = link[t].next_initial;
}
- link[r].next = t;
- link[r].lcpnext = min(l, max_match_len);
- LZ_ASSERT(t == ~(input_idx_t)0 || l <= n - SA[t]);
- LZ_ASSERT(l <= n - SA[r]);
- if (t == ~(input_idx_t)0)
- LZ_ASSERT(l == 0);
+ link[r].next_initial = t;
+
+ if (l < min_match_len)
+ l = 0;
+ else if (l > max_match_len)
+ l = max_match_len;
+ link[r].lcpnext_initial = l;
+ }
+ for (lz_sarray_pos_t r = 0; r < n; r++) {
+ lz_sarray_pos_t next;
+ lz_sarray_len_t l;
+ lz_sarray_delta_t dist_to_next;
+
+ next = link[r].next_initial;
+ l = link[r].lcpnext_initial;
+
+ if (next == LZ_SARRAY_POS_MAX)
+ dist_to_next = 0;
+ else if (next - r <= LZ_SARRAY_DELTA_MAX)
+ dist_to_next = next - r;
else
- LZ_ASSERT(memcmp(&T[SA[r]], &T[SA[t]], l) == 0);
+ dist_to_next = LZ_SARRAY_DELTA_MAX;
+
+ link[r].lcpnext = l;
+ link[r].dist_to_next = dist_to_next;
}
- /* Compute salink.prev and salink.lcpprev.
+ /* Calculate salink.dist_to_prev and salink.lcpprev.
*
- * Algorithm adapted from Crochemore et al. 2009:
- * "LPF computation revisited".
+ * This is analgous to dist_to_next and lcpnext as described above, but
+ * in the other direction. That is, here we're interested in, for each
+ * rank, the largest smaller rank that corresponds to a suffix starting
+ * earlier in the string.
*
- * 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;
+ * To save memory we don't have a "prev_initial" field, but rather store
+ * those values in the LCP array. */
+ LCP[0] = LZ_SARRAY_POS_MAX;
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]) {
+ for (lz_sarray_pos_t r = 1; r < n; r++) {
+ lz_sarray_pos_t t = r - 1;
+ lz_sarray_pos_t l = LCP[r];
+ while (t != LZ_SARRAY_POS_MAX && SA[t] > SA[r]) {
l = min(l, link[t].lcpprev);
- t = link[t].prev;
+ t = LCP[t];
}
- link[r].prev = t;
- link[r].lcpprev = min(l, max_match_len);
- LZ_ASSERT(t == ~(input_idx_t)0 || l <= n - SA[t]);
- LZ_ASSERT(l <= n - SA[r]);
- if (t == ~(input_idx_t)0)
- LZ_ASSERT(l == 0);
+ LCP[r] = t;
+
+ if (l < min_match_len)
+ l = 0;
+ else if (l > max_match_len)
+ l = max_match_len;
+
+ link[r].lcpprev = l;
+ }
+ for (lz_sarray_pos_t r = 0; r < n; r++) {
+
+ lz_sarray_pos_t prev = LCP[r];
+
+ if (prev == LZ_SARRAY_POS_MAX)
+ link[r].dist_to_prev = 0;
+ else if (r - prev <= LZ_SARRAY_DELTA_MAX)
+ link[r].dist_to_prev = r - prev;
else
- LZ_ASSERT(memcmp(&T[SA[r]], &T[SA[t]], l) == 0);
+ link[r].dist_to_prev = LZ_SARRAY_DELTA_MAX;
}
+}
+
+/* If ENABLE_LZ_DEBUG is defined, verify the values computed by init_salink().
+ *
+ * WARNING: this is for debug use only as it does not necessarily run in linear
+ * time!!! */
+static void
+verify_salink(const struct salink link[],
+ const lz_sarray_pos_t SA[],
+ const u8 T[],
+ lz_sarray_pos_t n,
+ lz_sarray_len_t min_match_len,
+ lz_sarray_len_t max_match_len)
+{
+#ifdef ENABLE_LZ_DEBUG
+ for (lz_sarray_pos_t r = 0; r < n; r++) {
+ for (lz_sarray_pos_t prev = r; ; ) {
+ if (prev == 0) {
+ LZ_ASSERT(link[r].dist_to_prev == 0);
+ LZ_ASSERT(link[r].lcpprev == 0);
+ break;
+ }
+
+ prev--;
+
+ if (SA[prev] < SA[r]) {
+ LZ_ASSERT(link[r].dist_to_prev == min(r - prev, LZ_SARRAY_DELTA_MAX));
+
+ lz_sarray_pos_t lcpprev;
+ for (lcpprev = 0;
+ lcpprev < min(n - SA[prev], n - SA[r]) &&
+ T[SA[prev] + lcpprev] == T[SA[r] + lcpprev];
+ lcpprev++)
+ ;
+ if (lcpprev < min_match_len)
+ lcpprev = 0;
+ else if (lcpprev > max_match_len)
+ lcpprev = max_match_len;
+
+ LZ_ASSERT(lcpprev == link[r].lcpprev);
+ break;
+ }
+ }
+
+ for (lz_sarray_pos_t next = r; ; ) {
+ if (next == n - 1) {
+ LZ_ASSERT(link[r].dist_to_next == 0);
+ LZ_ASSERT(link[r].lcpnext == 0);
+ break;
+ }
+ next++;
+
+ if (SA[next] < SA[r]) {
+ LZ_ASSERT(link[r].dist_to_next == min(next - r, LZ_SARRAY_DELTA_MAX));
+
+ lz_sarray_pos_t lcpnext;
+ for (lcpnext = 0;
+ lcpnext < min(n - SA[next], n - SA[r]) &&
+ T[SA[next] + lcpnext] == T[SA[r] + lcpnext];
+ lcpnext++)
+ ;
+ if (lcpnext < min_match_len)
+ lcpnext = 0;
+ else if (lcpnext > max_match_len)
+ lcpnext = max_match_len;
+
+ LZ_ASSERT(lcpnext == link[r].lcpnext);
+ break;
+ }
+ }
+ }
+#endif
+}
+
+/*
+ * Initialize the suffix array match-finder.
+ *
+ * @mf
+ * The suffix array match-finder structure to initialize. This structure
+ * is expected to be zeroed before this function is called. In the case
+ * that this function fails, lz_sarray_destroy() should be called to free
+ * any memory that may have been allocated.
+ *
+ * @max_window_size
+ * The maximum window size to support. This must be greater than 0.
+ *
+ * The amount of needed memory will depend on this value; see
+ * lz_sarray_get_needed_memory() for details.
+ *
+ * @min_match_len
+ * The minimum length of each match to be found. Must be greater than 0.
+ *
+ * @max_match_len
+ * The maximum length of each match to be found. Must be greater than or
+ * equal to @min_match_len.
+ *
+ * @max_matches_to_consider
+ * The maximum number of matches to consider at each position. This should
+ * be greater than @max_matches_to_return because @max_matches_to_consider
+ * counts all the returned matches as well as matches of equal length to
+ * returned matches that were not returned. This parameter bounds the
+ * amount of work the match-finder does at any one position. This could be
+ * anywhere from 1 to 100+ depending on the compression ratio and
+ * performance desired.
+ *
+ * @max_matches_to_return
+ * Maximum number of matches to return at each position. Because of the
+ * suffix array search algorithm, the order in which matches are returned
+ * will be from longest to shortest, so cut-offs due to this parameter will
+ * only result in shorter matches being discarded. This parameter could be
+ * anywhere from 1 to (@max_match_len - @min_match_len + 1) depending on
+ * the compression performance desired. However, making it even moderately
+ * large (say, greater than 3) may not be very helpful due to the property
+ * that the matches are returned from longest to shortest. But the main
+ * thing to keep in mind is that if the compressor decides to output a
+ * shorter-than-possible match, ideally it would be best to choose the best
+ * match of the desired length rather than truncate a longer match to that
+ * length.
+ *
+ * After initialization, the suffix-array match-finder can be used for any
+ * number of input strings (windows) of length less than or equal to
+ * @max_window_size by successive calls to lz_sarray_load_window().
+ *
+ * Returns %true on success, or %false if sufficient memory could not be
+ * allocated. See the note for @max_window_size above regarding the needed
+ * memory size.
+ */
+bool
+lz_sarray_init(struct lz_sarray *mf,
+ lz_sarray_pos_t max_window_size,
+ lz_sarray_len_t min_match_len,
+ lz_sarray_len_t max_match_len,
+ u32 max_matches_to_consider,
+ u32 max_matches_to_return)
+{
+ LZ_ASSERT(min_match_len > 0);
+ LZ_ASSERT(max_window_size > 0);
+ LZ_ASSERT(max_match_len >= min_match_len);
+
+ mf->max_window_size = max_window_size;
+ mf->min_match_len = min_match_len;
+ mf->max_match_len = max_match_len;
+ mf->max_matches_to_consider = max_matches_to_consider;
+ mf->max_matches_to_return = max_matches_to_return;
+
+ /* SA and ISA will share the same storage block. */
+ if ((u64)2 * max_window_size * sizeof(mf->SA[0]) !=
+ 2 * max_window_size * sizeof(mf->SA[0]))
+ return false;
+ mf->SA = MALLOC(max_window_size * sizeof(mf->SA[0]) +
+ max(DIVSUFSORT_TMP1_SIZE,
+ max_window_size * sizeof(mf->SA[0])));
+ if (mf->SA == NULL)
+ return false;
+
+ if ((u64)max_window_size * sizeof(mf->salink[0]) !=
+ max_window_size * sizeof(mf->salink[0]))
+ return false;
+ mf->salink = MALLOC(max(DIVSUFSORT_TMP2_SIZE,
+ max_window_size * sizeof(mf->salink[0])));
+ if (mf->salink == NULL)
+ return false;
+
+ return true;
+}
+
+/*
+ * Return the number of bytes of memory that lz_sarray_init() would allocate for
+ * the specified maximum window size.
+ *
+ * This should be (14 * @max_window_size) unless the type definitions have been
+ * changed.
+ */
+u64
+lz_sarray_get_needed_memory(lz_sarray_pos_t max_window_size)
+{
+ u64 size = 0;
+
+ /* SA and ISA: 8 bytes per position */
+ size += (u64)max_window_size * sizeof(((struct lz_sarray*)0)->SA[0]) +
+ max(DIVSUFSORT_TMP1_SIZE,
+ (u64)max_window_size * sizeof(((struct lz_sarray*)0)->SA[0]));
+
+ /* salink: 6 bytes per position */
+ size += max(DIVSUFSORT_TMP2_SIZE,
+ (u64)max_window_size * sizeof(((struct lz_sarray*)0)->salink[0]));
+
+ return size;
+}
+
+/*
+ * Prepare the suffix array match-finder to scan the specified window for
+ * matches.
+ *
+ * @mf Suffix array match-finder previously initialized with lz_sarray_init().
+ *
+ * @T Window, or "block", in which to find matches.
+ *
+ * @n Size of window in bytes. This must be positive and less than or equal
+ * to the @max_window_size passed to lz_sarray_init().
+ *
+ * This function runs in linear time (relative to @n).
+ */
+void
+lz_sarray_load_window(struct lz_sarray *mf, const u8 T[], lz_sarray_pos_t n)
+{
+ lz_sarray_pos_t *ISA, *LCP;
+
+ LZ_ASSERT(n > 0 && n <= mf->max_window_size);
+
+ /* Compute SA (Suffix Array).
+ *
+ * divsufsort() needs temporary space --- one array with 256 spaces and
+ * one array with 65536 spaces. The implementation of divsufsort() has
+ * been modified from the original to use the provided temporary space
+ * instead of allocating its own.
+ *
+ * We also check at build-time that divsufsort() uses the same integer
+ * size expected by this code. Unfortunately, divsufsort breaks if
+ * 'sa_idx_t' is defined to be a 16-bit integer; however, that would
+ * limit blocks to only 65536 bytes anyway. */
+ BUILD_BUG_ON(sizeof(lz_sarray_pos_t) != sizeof(saidx_t));
+
+ divsufsort(T, mf->SA, n, (saidx_t*)&mf->SA[n], (saidx_t*)mf->salink);
+
+ BUILD_BUG_ON(sizeof(bool) > sizeof(mf->salink[0]));
+ verify_suffix_array(mf->SA, T, (bool*)mf->salink, n);
+
+ /* Compute ISA (Inverse Suffix Array) in a preliminary position.
+ *
+ * This is just a trick to save memory. Since LCP is unneeded after
+ * this function, it can be computed in any available space. The
+ * storage for the ISA is the best choice because the ISA can be built
+ * quickly in salink for now, then re-built in its real location at the
+ * end. This is probably worth it because computing the ISA from the SA
+ * is very fast, and since this match-finder is memory-hungry we'd like
+ * to save as much memory as possible. */
+ BUILD_BUG_ON(sizeof(mf->salink[0]) < sizeof(mf->ISA[0]));
+ ISA = (lz_sarray_pos_t*)mf->salink;
+ compute_inverse_suffix_array(ISA, mf->SA, n);
+
+ /* Compute LCP (Longest Common Prefix) array. */
+ LCP = mf->SA + n;
+ compute_lcp_array(LCP, mf->SA, ISA, T, n);
+ verify_lcp_array(LCP, mf->SA, T, n);
+
+ /* Initialize suffix array links. */
+ init_salink(mf->salink, LCP, mf->SA, T, n,
+ mf->min_match_len, mf->max_match_len);
+ verify_salink(mf->salink, mf->SA, T, n,
+ mf->min_match_len, mf->max_match_len);
+
+ /* Compute ISA (Inverse Suffix Array) in its final position. */
+ ISA = mf->SA + n;
+ compute_inverse_suffix_array(ISA, mf->SA, n);
+
+ /* Save new variables and return. */
+ mf->ISA = ISA;
mf->cur_pos = 0;
mf->window_size = n;
}
+
+/* Free memory allocated for the suffix array match-finder. */
+void
+lz_sarray_destroy(struct lz_sarray *mf)
+{
+ FREE(mf->SA);
+ FREE(mf->salink);
+}