+
+ /* Choose the current list of items that constitute the minimum-cost
+ * path to the current position. */
+ xpress_declare_item_list(c, cur_optimum_ptr, next_chosen_item);
+ goto begin;
+}
+
+/* Near-optimal parsing */
+static u32
+xpress_choose_near_optimal_items(struct xpress_compressor *c)
+{
+ u32 num_passes_remaining = c->params.num_optim_passes;
+ struct xpress_item *next_chosen_item;
+ struct xpress_item **next_chosen_item_ptr;
+
+ /* Choose appropriate match-finder wrapper functions. */
+ if (c->params.num_optim_passes > 1) {
+ c->get_matches_func = xpress_get_matches_fillcache;
+ c->skip_bytes_func = xpress_skip_bytes_fillcache;
+ } else {
+ c->get_matches_func = xpress_get_matches_noncaching;
+ c->skip_bytes_func = xpress_skip_bytes_noncaching;
+ }
+
+ /* The first optimization pass will use a default cost model. Each
+ * additional optimization pass will use a cost model computed from the
+ * previous pass.
+ *
+ * To improve performance, we only generate the array containing the
+ * matches and literals in intermediate form on the final pass. For
+ * earlier passes, tallying symbol frequencies is sufficient. */
+ xpress_set_default_costs(c->costs);
+
+ next_chosen_item_ptr = NULL;
+ do {
+ /* Reset the match-finder wrapper. */
+ c->cache_ptr = c->cached_matches;
+
+ if (num_passes_remaining == 1) {
+ /* Last pass: actually generate the items. */
+ next_chosen_item = c->chosen_items;
+ next_chosen_item_ptr = &next_chosen_item;
+ }
+
+ /* Choose the items. */
+ xpress_optim_pass(c, next_chosen_item_ptr);
+
+ if (num_passes_remaining > 1) {
+ /* This isn't the last pass. */
+
+ /* Make the Huffman code from the symbol frequencies. */
+ c->freqs[XPRESS_END_OF_DATA]++;
+ xpress_make_huffman_code(c);
+
+ /* Reset symbol frequencies. */
+ memset(c->freqs, 0, sizeof(c->freqs));
+
+ /* Update symbol costs. */
+ xpress_set_costs(c->costs, c->lens);
+
+ /* Choose appopriate match-finder wrapper functions. */
+ if (c->cache_ptr <= c->cache_limit) {
+ c->get_matches_func = xpress_get_matches_usecache_nocheck;
+ c->skip_bytes_func = xpress_skip_bytes_usecache_nocheck;
+ } else {
+ c->get_matches_func = xpress_get_matches_usecache;
+ c->skip_bytes_func = xpress_skip_bytes_usecache;
+ }
+ }
+ } while (--num_passes_remaining);
+
+ /* Return the number of items chosen. */
+ return next_chosen_item - c->chosen_items;
+}
+
+/* Lazy parsing */
+static u32
+xpress_choose_lazy_items(struct xpress_compressor *c)
+{
+ const u8 *window_ptr = c->cur_window;
+ const u8 *window_end = &c->cur_window[c->cur_window_size];
+ struct xpress_item *next_chosen_item = c->chosen_items;
+ u32 len_3_too_far;
+ struct lz_mf *mf = c->mf;
+ struct lz_match *matches = c->cached_matches;
+ unsigned num_matches;
+ struct lz_match prev_match;
+
+ if (c->cur_window_size <= 8192)
+ len_3_too_far = 2048;
+ else
+ len_3_too_far = 4096;
+
+ do {
+ /* Don't have match at previous position */
+
+ num_matches = lz_mf_get_matches(mf, matches);
+ window_ptr++;
+
+ if (num_matches == 0 ||
+ (matches[num_matches - 1].len == 3 &&
+ matches[num_matches - 1].offset >= len_3_too_far))
+ {
+ /* No matches found => output literal */
+ xpress_declare_literal(c, *(window_ptr - 1),
+ &next_chosen_item);
+ continue;
+ }
+
+ prev_match = matches[num_matches - 1];
+
+ have_prev_match:
+ /* Have match at previous position */
+
+ if (prev_match.len >= c->params.nice_match_length) {
+ /* Very long match found => output immediately */
+ xpress_declare_match(c, prev_match.len,
+ prev_match.offset,
+ &next_chosen_item);
+ lz_mf_skip_positions(mf, prev_match.len - 1);
+ window_ptr += prev_match.len - 1;
+ continue;
+ }
+
+ num_matches = lz_mf_get_matches(mf, matches);
+ window_ptr++;
+
+ if (num_matches == 0 ||
+ (matches[num_matches - 1].len <= prev_match.len))
+ {
+ /* Next match is not longer => output previous match */
+ xpress_declare_match(c, prev_match.len,
+ prev_match.offset,
+ &next_chosen_item);
+ lz_mf_skip_positions(mf, prev_match.len - 2);
+ window_ptr += prev_match.len - 2;
+ continue;
+ }
+
+ /* Next match is longer => output literal */
+
+ xpress_declare_literal(c, *(window_ptr - 2), &next_chosen_item);
+
+ prev_match = matches[num_matches - 1];
+
+ goto have_prev_match;
+
+ } while (window_ptr != window_end);
+
+ return next_chosen_item - c->chosen_items;
+}
+
+/* Greedy parsing */
+static u32
+xpress_choose_greedy_items(struct xpress_compressor *c)
+{
+ const u8 *window_ptr = c->cur_window;
+ const u8 *window_end = &c->cur_window[c->cur_window_size];
+ struct xpress_item *next_chosen_item = c->chosen_items;
+ u32 len_3_too_far;
+ struct lz_mf *mf = c->mf;
+ struct lz_match *matches = c->cached_matches;
+ unsigned num_matches;
+
+ if (c->cur_window_size <= 8192)
+ len_3_too_far = 2048;
+ else
+ len_3_too_far = 4096;
+
+ do {
+ /* Get longest match at the current position. */
+ num_matches = lz_mf_get_matches(mf, matches);
+
+ if (num_matches == 0 ||
+ (matches[num_matches - 1].len == 3 &&
+ matches[num_matches - 1].offset >= len_3_too_far))
+ {
+ /* No match, or length 3 match with large offset.
+ * Choose a literal. */
+ xpress_declare_literal(c, *window_ptr, &next_chosen_item);
+ window_ptr += 1;
+ } else {
+ /* Match found. Choose it. */
+ unsigned len = matches[num_matches - 1].len;
+ unsigned offset = matches[num_matches - 1].offset;
+
+ xpress_declare_match(c, len, offset, &next_chosen_item);
+ lz_mf_skip_positions(mf, len - 1);
+ window_ptr += len;
+ }
+ } while (window_ptr != window_end);
+
+ return next_chosen_item - c->chosen_items;
+}
+
+/* Literals-only parsing */
+static u32
+xpress_choose_literals(struct xpress_compressor *c)
+{
+ const u8 *window_ptr = c->cur_window;
+ const u8 *window_end = &c->cur_window[c->cur_window_size];
+ struct xpress_item *next_chosen_item = c->chosen_items;
+
+ do {
+ xpress_declare_literal(c, *window_ptr++, &next_chosen_item);
+ } while (window_ptr != window_end);
+
+ return next_chosen_item - c->chosen_items;
+}
+
+/*
+ * 'choose_items_func' is provided a data buffer c->cur_window of length
+ * c->cur_window_size bytes. This data buffer will have already been loaded
+ * into the match-finder c->mf. 'choose_items_func' must choose the
+ * match/literal sequence to output to represent this data buffer. The
+ * intermediate representation of this match/literal sequence must be recorded
+ * in c->chosen_items, and the Huffman symbols used must be tallied in c->freqs.
+ * The return value must be the number of items written to c->chosen_items.
+ */
+static u32
+xpress_choose_items(struct xpress_compressor *c)
+{
+ return (*c->params.choose_items_func)(c);
+}
+
+/* Set internal compression parameters for the specified compression level and
+ * maximum window size. */
+static void
+xpress_build_params(unsigned int compression_level, u32 max_window_size,
+ struct xpress_compressor_params *xpress_params)
+{
+ memset(xpress_params, 0, sizeof(*xpress_params));
+ xpress_params->num_optim_passes = 1;
+
+ if (compression_level == 1) {
+
+ /* Literal-only parsing */
+ xpress_params->choose_items_func = xpress_choose_literals;
+ xpress_params->mf_algo = LZ_MF_NULL;
+
+ } else if (compression_level < 30) {
+
+ /* Greedy parsing */
+ xpress_params->choose_items_func = xpress_choose_greedy_items;
+ xpress_params->mf_algo = LZ_MF_HASH_CHAINS;
+ xpress_params->nice_match_length = compression_level;
+ xpress_params->max_search_depth = compression_level / 2;
+
+ } else if (compression_level < 60) {
+
+ /* Lazy parsing */
+ xpress_params->choose_items_func = xpress_choose_lazy_items;
+ xpress_params->mf_algo = LZ_MF_HASH_CHAINS;
+ xpress_params->nice_match_length = compression_level;
+ xpress_params->max_search_depth = compression_level / 2;
+
+ } else {
+
+ /* Near-optimal parsing */
+ xpress_params->choose_items_func = xpress_choose_near_optimal_items;
+ if (max_window_size >= 16384)
+ xpress_params->mf_algo = LZ_MF_BINARY_TREES;
+ else
+ xpress_params->mf_algo = LZ_MF_HASH_CHAINS;
+ xpress_params->num_optim_passes = compression_level / 40;
+ xpress_params->nice_match_length = min(compression_level / 2,
+ XPRESS_MAX_MATCH_LEN);
+ xpress_params->max_search_depth = min(compression_level,
+ XPRESS_MAX_MATCH_LEN);
+ }
+}
+
+/* Given the internal compression parameters and maximum window size, build the
+ * Lempel-Ziv match-finder parameters. */
+static void
+xpress_build_mf_params(const struct xpress_compressor_params *xpress_params,
+ u32 max_window_size, struct lz_mf_params *mf_params)
+{
+ memset(mf_params, 0, sizeof(*mf_params));
+
+ mf_params->algorithm = xpress_params->mf_algo;
+ mf_params->max_window_size = max_window_size;
+ mf_params->min_match_len = XPRESS_MIN_MATCH_LEN;
+ mf_params->max_match_len = XPRESS_MAX_MATCH_LEN;
+ mf_params->max_search_depth = xpress_params->max_search_depth;
+ mf_params->nice_match_len = xpress_params->nice_match_length;
+}
+
+static void
+xpress_free_compressor(void *_c);
+
+static u64
+xpress_get_needed_memory(size_t max_window_size, unsigned int compression_level)
+{
+ u64 size = 0;
+ struct xpress_compressor_params params;
+
+ if (max_window_size > XPRESS_MAX_OFFSET + 1)
+ return 0;
+
+ xpress_build_params(compression_level, max_window_size, ¶ms);
+
+ size += sizeof(struct xpress_compressor);
+
+ /* mf */
+ size += lz_mf_get_needed_memory(params.mf_algo, max_window_size);
+
+ /* optimum */
+ if (params.choose_items_func == xpress_choose_near_optimal_items) {
+ size += (XPRESS_OPTIM_ARRAY_LENGTH + params.nice_match_length) *
+ sizeof(struct xpress_mc_pos_data);
+ }
+
+ /* cached_matches */
+ if (params.num_optim_passes > 1) {
+ size_t cache_len = max(max_window_size * XPRESS_CACHE_PER_POS,
+ params.max_search_depth + 1);
+ size += cache_len * sizeof(struct lz_match);
+ } else {
+ size += params.max_search_depth * sizeof(struct lz_match);
+ }
+
+ /* chosen_items */
+ size += max_window_size * sizeof(struct xpress_item);
+
+ return size;
+}
+
+static int
+xpress_create_compressor(size_t max_window_size, unsigned int compression_level,
+ void **c_ret)
+{
+ struct xpress_compressor *c;
+ struct xpress_compressor_params params;
+ struct lz_mf_params mf_params;
+
+ if (max_window_size > XPRESS_MAX_OFFSET + 1)
+ return WIMLIB_ERR_INVALID_PARAM;
+
+ xpress_build_params(compression_level, max_window_size, ¶ms);
+ xpress_build_mf_params(¶ms, max_window_size, &mf_params);
+
+ c = CALLOC(1, sizeof(struct xpress_compressor));
+ if (!c)
+ goto oom;
+
+ c->params = params;
+
+ c->mf = lz_mf_alloc(&mf_params);
+ if (!c->mf)
+ goto oom;
+
+ if (params.choose_items_func == xpress_choose_near_optimal_items) {
+ c->optimum = MALLOC((XPRESS_OPTIM_ARRAY_LENGTH +
+ params.nice_match_length) *
+ sizeof(struct xpress_mc_pos_data));
+ if (!c->optimum)
+ goto oom;
+ }
+
+ if (params.num_optim_passes > 1) {
+ size_t cache_len = max(max_window_size * XPRESS_CACHE_PER_POS,
+ params.max_search_depth + 1);
+ c->cached_matches = MALLOC(cache_len * sizeof(struct lz_match));
+ if (!c->cached_matches)
+ goto oom;
+ c->cache_limit = c->cached_matches + cache_len -
+ (params.max_search_depth + 1);
+ } else {
+ c->cached_matches = MALLOC(params.max_search_depth *
+ sizeof(struct lz_match));
+ if (!c->cached_matches)
+ goto oom;
+ }
+
+ c->chosen_items = MALLOC(max_window_size * sizeof(struct xpress_item));
+ if (!c->chosen_items)
+ goto oom;
+
+ *c_ret = c;