+ while (end_node < cur_node + rep_len)
+ (++end_node)->cost = INFINITE_COST;
+
+ u32 base_cost = cur_node->cost +
+ lzms_bit_1_cost(cur_node->state.main_state,
+ c->probs.main) +
+ lzms_bit_0_cost(cur_node->state.match_state,
+ c->probs.match) +
+ lzms_bit_1_cost(cur_node->state.lz_state,
+ c->probs.lz);
+
+ for (int i = 0; i < rep_idx; i++)
+ base_cost += lzms_bit_1_cost(cur_node->state.lz_rep_states[i],
+ c->probs.lz_rep[i]);
+
+ if (rep_idx < LZMS_NUM_LZ_REP_DECISIONS)
+ base_cost += lzms_bit_0_cost(cur_node->state.lz_rep_states[rep_idx],
+ c->probs.lz_rep[rep_idx]);
+
+ u32 len = 2;
+ do {
+ u32 cost = base_cost + lzms_fast_length_cost(c, len);
+ if (cost < (cur_node + len)->cost) {
+ (cur_node + len)->cost = cost;
+ (cur_node + len)->item = (struct lzms_item) {
+ .length = len,
+ .source = rep_idx,
+ };
+ (cur_node + len)->num_extra_items = 0;
+ }
+ } while (++len <= rep_len);
+
+
+ /* try LZ-rep + lit + LZ-rep0 */
+ if (c->try_lzrep_lit_lzrep0 &&
+ in_end - (in_next + rep_len) >= 3 &&
+ load_u16_unaligned(in_next + rep_len + 1) ==
+ load_u16_unaligned(matchptr + rep_len + 1))
+ {
+ const u32 rep0_len = lz_extend(in_next + rep_len + 1,
+ matchptr + rep_len + 1,
+ 2,
+ min(c->mf.nice_match_len,
+ in_end - (in_next + rep_len + 1)));
+
+ unsigned main_state = cur_node->state.main_state;
+ unsigned match_state = cur_node->state.match_state;
+ unsigned lz_state = cur_node->state.lz_state;
+ unsigned lz_rep0_state = cur_node->state.lz_rep_states[0];
+
+ /* update states for LZ-rep */
+ main_state = ((main_state << 1) | 1) % LZMS_NUM_MAIN_PROBS;
+ match_state = ((match_state << 1) | 0) % LZMS_NUM_MATCH_PROBS;
+ lz_state = ((lz_state << 1) | 1) % LZMS_NUM_LZ_PROBS;
+ lz_rep0_state = ((lz_rep0_state << 1) | (rep_idx > 0)) %
+ LZMS_NUM_LZ_REP_PROBS;
+
+ /* LZ-rep cost */
+ u32 cost = base_cost + lzms_fast_length_cost(c, rep_len);
+
+ /* add literal cost */
+ cost += lzms_literal_cost(c, main_state, *(in_next + rep_len));
+
+ /* update state for literal */
+ main_state = ((main_state << 1) | 0) % LZMS_NUM_MAIN_PROBS;
+
+ /* add LZ-rep0 cost */
+ cost += lzms_bit_1_cost(main_state, c->probs.main) +
+ lzms_bit_0_cost(match_state, c->probs.match) +
+ lzms_bit_1_cost(lz_state, c->probs.lz) +
+ lzms_bit_0_cost(lz_rep0_state, c->probs.lz_rep[0]) +
+ lzms_fast_length_cost(c, rep0_len);
+
+ const u32 total_len = rep_len + 1 + rep0_len;
+
+ while (end_node < cur_node + total_len)
+ (++end_node)->cost = INFINITE_COST;
+
+ if (cost < (cur_node + total_len)->cost) {
+ (cur_node + total_len)->cost = cost;
+ (cur_node + total_len)->item = (struct lzms_item) {
+ .length = rep0_len,
+ .source = 0,
+ };
+ (cur_node + total_len)->extra_items[0] = (struct lzms_item) {
+ .length = 1,
+ .source = *(in_next + rep_len),
+ };
+ (cur_node + total_len)->extra_items[1] = (struct lzms_item) {
+ .length = rep_len,
+ .source = rep_idx,
+ };
+ (cur_node + total_len)->num_extra_items = 2;
+ }
+ }
+ }
+ }
+
+ /* Repeat offset delta matches */
+ if (c->use_delta_matches &&
+ likely(in_next - c->in_buffer >= LZMS_NUM_DELTA_REPS + 1 &&
+ (in_end - in_next >= 2)))
+ {
+ for (int rep_idx = 0; rep_idx < LZMS_NUM_DELTA_REPS; rep_idx++) {
+
+ /* Looking for a repeat offset delta match at
+ * queue index @rep_idx */
+
+ const u32 pair = cur_node->state.recent_delta_pairs[rep_idx];
+ const u32 power = pair >> DELTA_SOURCE_POWER_SHIFT;
+ const u32 raw_offset = pair & DELTA_SOURCE_RAW_OFFSET_MASK;
+ const u32 span = (u32)1 << power;
+ const u32 offset = raw_offset << power;
+ const u8 * const matchptr = in_next - offset;
+
+ /* Check the first 2 bytes before entering the
+ * extension loop. */
+ if (((u8)(*(in_next + 0) - *(in_next + 0 - span)) !=
+ (u8)(*(matchptr + 0) - *(matchptr + 0 - span))) ||
+ ((u8)(*(in_next + 1) - *(in_next + 1 - span)) !=
+ (u8)(*(matchptr + 1) - *(matchptr + 1 - span))))
+ continue;
+
+ /* Extend the match to its full length. */
+ const u32 rep_len = lzms_extend_delta_match(in_next, matchptr,
+ 2, in_end - in_next,
+ span);
+
+ /* Early out for long repeat offset delta match */
+ if (rep_len >= c->mf.nice_match_len) {
+
+ in_next = lzms_skip_bytes(c, rep_len, in_next);
+
+ lzms_encode_item_list(c, cur_node);
+ lzms_encode_item(c, rep_len, DELTA_SOURCE_TAG | rep_idx);
+
+ c->optimum_nodes[0].state = cur_node->state;
+ cur_node = &c->optimum_nodes[0];
+
+ cur_node->state.upcoming_delta_pair = pair;
+ cur_node->state.upcoming_lz_offset = 0;
+ for (int i = rep_idx; i < LZMS_NUM_DELTA_REPS; i++)
+ cur_node->state.recent_delta_pairs[i] =
+ cur_node->state.recent_delta_pairs[i + 1];
+ lzms_update_lru_queues(&cur_node->state);
+ lzms_update_main_state(&cur_node->state, 1);
+ lzms_update_match_state(&cur_node->state, 1);
+ lzms_update_delta_state(&cur_node->state, 1);
+ lzms_update_delta_rep_states(&cur_node->state, rep_idx);
+ goto begin;
+ }