/* Maintains "ZopfliNode array invariant" and pushes node to the queue, if it * is eligible. */ private static unsafe void EvaluateNode( size_t block_start, size_t pos, size_t max_backward_limit, int *starting_dist_cache, ZopfliCostModel *model, StartPosQueue *queue, ZopfliNode *nodes) { /* Save cost, because ComputeDistanceCache invalidates it. */ float node_cost = nodes[pos].u.cost; nodes[pos].u.shortcut = ComputeDistanceShortcut( block_start, pos, max_backward_limit, nodes); if (node_cost <= ZopfliCostModelGetLiteralCosts(model, 0, pos)) { PosData posdata; posdata.pos = pos; posdata.cost = node_cost; posdata.costdiff = node_cost - ZopfliCostModelGetLiteralCosts(model, 0, pos); ComputeDistanceCache( pos, starting_dist_cache, nodes, posdata.distance_cache); StartPosQueuePush(queue, &posdata); } }
private static unsafe void StartPosQueuePush(StartPosQueue *self, PosData *posdata) { size_t offset = ~(self->idx_++) & 7; size_t len = StartPosQueueSize(self); size_t i; PosData *q = self->q_; q[offset] = *posdata; /* Restore the sorted order. In the list of |len| items at most |len - 1| * adjacent element comparisons / swaps are required. */ for (i = 1; i < len; ++i) { if (q[offset & 7].costdiff > q[(offset + 1) & 7].costdiff) { PosData tmp = q[offset & 7]; q[offset & 7] = q[(offset + 1) & 7]; q[(offset + 1) & 7] = tmp; } ++offset; } }
private static unsafe void InitStartPosQueue(StartPosQueue *self) { self->idx_ = 0; }
/* Returns longest copy length. */ private static unsafe size_t UpdateNodes( size_t num_bytes, size_t block_start, size_t pos, byte *ringbuffer, size_t ringbuffer_mask, BrotliEncoderParams *params_, size_t max_backward_limit, int *starting_dist_cache, size_t num_matches, BackwardMatch *matches, ZopfliCostModel *model, StartPosQueue *queue, ZopfliNode *nodes) { size_t cur_ix = block_start + pos; size_t cur_ix_masked = cur_ix & ringbuffer_mask; size_t max_distance = Math.Min(cur_ix, max_backward_limit); size_t max_len = num_bytes - pos; size_t max_zopfli_len = MaxZopfliLen(params_); size_t max_iters = MaxZopfliCandidates(params_); size_t min_len; size_t result = 0; size_t k; EvaluateNode(block_start, pos, max_backward_limit, starting_dist_cache, model, queue, nodes); { PosData *posdata = StartPosQueueAt(queue, 0); float min_cost = (posdata->cost + ZopfliCostModelGetMinCostCmd(model) + ZopfliCostModelGetLiteralCosts(model, posdata->pos, pos)); min_len = ComputeMinimumCopyLength(min_cost, nodes, num_bytes, pos); } /* Go over the command starting positions in order of increasing cost * difference. */ for (k = 0; k < max_iters && k < StartPosQueueSize(queue); ++k) { PosData *posdata = StartPosQueueAt(queue, k); size_t start = posdata->pos; ushort inscode = GetInsertLengthCode(pos - start); float start_costdiff = posdata->costdiff; float base_cost = start_costdiff + (float)GetInsertExtra(inscode) + ZopfliCostModelGetLiteralCosts(model, 0, pos); /* Look for last distance matches using the distance cache from this * starting position. */ size_t best_len = min_len - 1; size_t j = 0; for (; j < BROTLI_NUM_DISTANCE_SHORT_CODES && best_len < max_len; ++j) { size_t idx = kDistanceCacheIndex[j]; size_t backward = (size_t)(posdata->distance_cache[idx] + kDistanceCacheOffset[j]); size_t prev_ix = cur_ix - backward; if (prev_ix >= cur_ix) { continue; } if ((backward > max_distance)) { continue; } prev_ix &= ringbuffer_mask; if (cur_ix_masked + best_len > ringbuffer_mask || prev_ix + best_len > ringbuffer_mask || ringbuffer[cur_ix_masked + best_len] != ringbuffer[prev_ix + best_len]) { continue; } { size_t len = FindMatchLengthWithLimit(&ringbuffer[prev_ix], &ringbuffer[cur_ix_masked], max_len); float dist_cost = base_cost + ZopfliCostModelGetDistanceCost(model, j); size_t l; for (l = best_len + 1; l <= len; ++l) { ushort copycode = GetCopyLengthCode(l); ushort cmdcode = CombineLengthCodes(inscode, copycode, j == 0); float cost = (cmdcode < 128 ? base_cost : dist_cost) + (float)GetCopyExtra(copycode) + ZopfliCostModelGetCommandCost(model, cmdcode); if (cost < nodes[pos + l].u.cost) { UpdateZopfliNode(nodes, pos, start, l, l, backward, j + 1, cost); result = Math.Max(result, l); } best_len = l; } } } /* At higher iterations look only for new last distance matches, since * looking only for new command start positions with the same distances * does not help much. */ if (k >= 2) { continue; } { /* Loop through all possible copy lengths at this position. */ size_t len = min_len; for (j = 0; j < num_matches; ++j) { BackwardMatch match = matches[j]; size_t dist = match.distance; bool is_dictionary_match = (dist > max_distance); /* We already tried all possible last distance matches, so we can use * normal distance code here. */ size_t dist_code = dist + BROTLI_NUM_DISTANCE_SHORT_CODES - 1; ushort dist_symbol; uint distextra; uint distnumextra; float dist_cost; size_t max_match_len; PrefixEncodeCopyDistance(dist_code, 0, 0, &dist_symbol, &distextra); distnumextra = distextra >> 24; dist_cost = base_cost + (float)distnumextra + ZopfliCostModelGetDistanceCost(model, dist_symbol); /* Try all copy lengths up until the maximum copy length corresponding * to this distance. If the distance refers to the private static unsafe dictionary, or * the maximum length is long enough, try only one maximum length. */ max_match_len = BackwardMatchLength(&match); if (len < max_match_len && (is_dictionary_match || max_match_len > max_zopfli_len)) { len = max_match_len; } for (; len <= max_match_len; ++len) { size_t len_code = is_dictionary_match ? BackwardMatchLengthCode(&match) : len; ushort copycode = GetCopyLengthCode(len_code); ushort cmdcode = CombineLengthCodes(inscode, copycode, false); float cost = dist_cost + (float)GetCopyExtra(copycode) + ZopfliCostModelGetCommandCost(model, cmdcode); if (cost < nodes[pos + len].u.cost) { UpdateZopfliNode(nodes, pos, start, len, len_code, dist, 0, cost); result = Math.Max(result, len); } } } } } return(result); }
private static unsafe PosData *StartPosQueueAt(StartPosQueue *self, size_t k) { return(&self->q_[(k - self->idx_) & 7]); }
private static unsafe size_t StartPosQueueSize(StartPosQueue *self) { return(Math.Min(self->idx_, 8)); }