private static unsafe void InitDictionaryBackwardMatch(BackwardMatch *self, size_t dist, size_t len, size_t len_code) { self->distance = (uint)dist; self->length_and_code = (uint)((len << 5) | (len == len_code ? 0 : len_code)); }
private static unsafe size_t ZopfliIterate(size_t num_bytes, size_t position, byte *ringbuffer, size_t ringbuffer_mask, BrotliEncoderParams *params_, size_t max_backward_limit, int *dist_cache, ZopfliCostModel *model, uint *num_matches, BackwardMatch *matches, ZopfliNode *nodes) { size_t max_zopfli_len = MaxZopfliLen(params_); StartPosQueue queue; size_t cur_match_pos = 0; size_t i; nodes[0].length = 0; nodes[0].u.cost = 0; InitStartPosQueue(&queue); for (i = 0; i + 3 < num_bytes; i++) { size_t skip = UpdateNodes(num_bytes, position, i, ringbuffer, ringbuffer_mask, params_, max_backward_limit, dist_cache, num_matches[i], &matches[cur_match_pos], model, &queue, nodes); if (skip < BROTLI_LONG_COPY_QUICK_STEP) { skip = 0; } cur_match_pos += num_matches[i]; if (num_matches[i] == 1 && BackwardMatchLength(&matches[cur_match_pos - 1]) > max_zopfli_len) { skip = Math.Max( BackwardMatchLength(&matches[cur_match_pos - 1]), skip); } if (skip > 1) { skip--; while (skip != 0) { i++; if (i + 3 >= num_bytes) { break; } EvaluateNode( position, i, max_backward_limit, dist_cache, model, &queue, nodes); cur_match_pos += num_matches[i]; skip--; } } } return(ComputeShortestPathFromNodes(num_bytes, nodes)); }
private static unsafe void InitBackwardMatch(BackwardMatch *self, size_t dist, size_t len) { self->distance = (uint)dist; self->length_and_code = (uint)(len << 5); }
private static unsafe size_t BackwardMatchLengthCode(BackwardMatch *self) { size_t code = self->length_and_code & 31; return(code != 0 ? code : BackwardMatchLength(self)); }
private static unsafe size_t BackwardMatchLength(BackwardMatch *self) { return(self->length_and_code >> 5); }
/* Finds all backward matches of &data[cur_ix & ring_buffer_mask] up to the * length of max_length and stores the position cur_ix in the hash table. * * Sets *num_matches to the number of matches found, and stores the found * matches in matches[0] to matches[*num_matches - 1]. The matches will be * sorted by strictly increasing length and (non-strictly) increasing * distance. */ public static unsafe size_t FindAllMatches(HasherHandle handle, byte *data, size_t ring_buffer_mask, size_t cur_ix, size_t max_length, size_t max_backward, BrotliEncoderParams *params_, BackwardMatch *matches) { BackwardMatch *orig_matches = matches; size_t cur_ix_masked = cur_ix & ring_buffer_mask; size_t best_len = 1; size_t short_match_max_backward = params_->quality != HQ_ZOPFLIFICATION_QUALITY ? 16 : 64; size_t stop = cur_ix - short_match_max_backward; uint[] dict_matches_arr = new uint[BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN + 1]; fixed(uint *dict_matches = dict_matches_arr) { size_t i; if (cur_ix < short_match_max_backward) { stop = 0; } for (i = cur_ix - 1; i > stop && best_len <= 2; --i) { size_t prev_ix = i; size_t backward = cur_ix - prev_ix; if ((backward > max_backward)) { break; } prev_ix &= ring_buffer_mask; if (data[cur_ix_masked] != data[prev_ix] || data[cur_ix_masked + 1] != data[prev_ix + 1]) { continue; } { size_t len = FindMatchLengthWithLimit(&data[prev_ix], &data[cur_ix_masked], max_length); if (len > best_len) { best_len = len; InitBackwardMatch(matches++, backward, len); } } } if (best_len < max_length) { matches = StoreAndFindMatches(Self(handle), data, cur_ix, ring_buffer_mask, max_length, max_backward, &best_len, matches); } for (i = 0; i <= BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN; ++i) { dict_matches[i] = kInvalidMatch; } { size_t minlen = Math.Max(4, best_len + 1); if (BrotliFindAllStaticDictionaryMatches( &data[cur_ix_masked], minlen, max_length, &dict_matches[0])) { size_t maxlen = Math.Min( BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN, max_length); size_t l; for (l = minlen; l <= maxlen; ++l) { uint dict_id = dict_matches[l]; if (dict_id < kInvalidMatch) { InitDictionaryBackwardMatch(matches++, max_backward + (dict_id >> 5) + 1, l, dict_id & 31); } } } } return((size_t)(matches - orig_matches)); } }
/* Stores the hash of the next 4 bytes and in a single tree-traversal, the * hash bucket's binary tree is searched for matches and is re-rooted at the * current position. * * If less than MAX_TREE_COMP_LENGTH data is available, the hash bucket of the * current position is searched for matches, but the state of the hash table * is not changed, since we can not know the final sorting order of the * current (incomplete) sequence. * * This function must be called with increasing cur_ix positions. */ private static unsafe BackwardMatch *StoreAndFindMatches( HashToBinaryTree *self, byte *data, size_t cur_ix, size_t ring_buffer_mask, size_t max_length, size_t max_backward, size_t *best_len, BackwardMatch *matches) { size_t cur_ix_masked = cur_ix & ring_buffer_mask; size_t max_comp_len = Math.Min(max_length, MAX_TREE_COMP_LENGTH); bool should_reroot_tree = max_length >= MAX_TREE_COMP_LENGTH; uint key = HashBytes(&data[cur_ix_masked]); uint * forest = Forest(self); size_t prev_ix = self->buckets_[key]; /* The forest index of the rightmost node of the left subtree of the new * root, updated as we traverse and re-root the tree of the hash bucket. */ size_t node_left = LeftChildIndex(self, cur_ix); /* The forest index of the leftmost node of the right subtree of the new * root, updated as we traverse and re-root the tree of the hash bucket. */ size_t node_right = RightChildIndex(self, cur_ix); /* The match length of the rightmost node of the left subtree of the new * root, updated as we traverse and re-root the tree of the hash bucket. */ size_t best_len_left = 0; /* The match length of the leftmost node of the right subtree of the new * root, updated as we traverse and re-root the tree of the hash bucket. */ size_t best_len_right = 0; size_t depth_remaining; if (should_reroot_tree) { self->buckets_[key] = (uint)cur_ix; } for (depth_remaining = MAX_TREE_SEARCH_DEPTH;; --depth_remaining) { size_t backward = cur_ix - prev_ix; size_t prev_ix_masked = prev_ix & ring_buffer_mask; if (backward == 0 || backward > max_backward || depth_remaining == 0) { if (should_reroot_tree) { forest[node_left] = self->invalid_pos_; forest[node_right] = self->invalid_pos_; } break; } { size_t cur_len = Math.Min(best_len_left, best_len_right); size_t len; len = cur_len + FindMatchLengthWithLimit(&data[cur_ix_masked + cur_len], &data[prev_ix_masked + cur_len], max_length - cur_len); if (matches != null && len > *best_len) { *best_len = len; InitBackwardMatch(matches++, backward, len); } if (len >= max_comp_len) { if (should_reroot_tree) { forest[node_left] = forest[LeftChildIndex(self, prev_ix)]; forest[node_right] = forest[RightChildIndex(self, prev_ix)]; } break; } if (data[cur_ix_masked + len] > data[prev_ix_masked + len]) { best_len_left = len; if (should_reroot_tree) { forest[node_left] = (uint)prev_ix; } node_left = RightChildIndex(self, prev_ix); prev_ix = forest[node_left]; } else { best_len_right = len; if (should_reroot_tree) { forest[node_right] = (uint)prev_ix; } node_right = LeftChildIndex(self, prev_ix); prev_ix = forest[node_right]; } } } return(matches); }
private static unsafe void BrotliCreateHqZopfliBackwardReferences( ref MemoryManager m, size_t num_bytes, size_t position, byte *ringbuffer, size_t ringbuffer_mask, BrotliEncoderParams *params_, HasherHandle hasher, int *dist_cache, size_t *last_insert_len, Command *commands, size_t *num_commands, size_t *num_literals) { size_t max_backward_limit = BROTLI_MAX_BACKWARD_LIMIT(params_->lgwin); uint * num_matches = (uint *)BrotliAllocate(ref m, num_bytes * sizeof(uint)); size_t matches_size = 4 * num_bytes; Hasher h = kHashers[10]; size_t store_end = num_bytes >= h.StoreLookahead() ? position + num_bytes - h.StoreLookahead() + 1 : position; size_t cur_match_pos = 0; size_t i; size_t orig_num_literals; size_t orig_last_insert_len; int * orig_dist_cache = stackalloc int[4]; size_t orig_num_commands; ZopfliCostModel model; ZopfliNode * nodes; BackwardMatch * matches = (BackwardMatch *)BrotliAllocate(ref m, matches_size * sizeof(BackwardMatch)); for (i = 0; i + h.HashTypeLength() - 1 < num_bytes; ++i) { size_t pos = position + i; size_t max_distance = Math.Min(pos, max_backward_limit); size_t max_length = num_bytes - i; size_t num_found_matches; size_t cur_match_end; /* Ensure that we have enough free slots. */ BrotliEnsureCapacity(ref m, sizeof(BackwardMatch), (void **)&matches, &matches_size, cur_match_pos + MAX_NUM_MATCHES_H10); num_found_matches = HashToBinaryTreeH10.FindAllMatches(hasher, ringbuffer, ringbuffer_mask, pos, max_length, max_distance, params_, &matches[cur_match_pos]); cur_match_end = cur_match_pos + num_found_matches; num_matches[i] = (uint)num_found_matches; if (num_found_matches > 0) { size_t match_len = BackwardMatchLength(&matches[cur_match_end - 1]); if (match_len > MAX_ZOPFLI_LEN_QUALITY_11) { size_t skip = match_len - 1; matches[cur_match_pos++] = matches[cur_match_end - 1]; num_matches[i] = 1; /* Add the tail of the copy to the hasher. */ h.StoreRange(hasher, ringbuffer, ringbuffer_mask, pos + 1, Math.Min(pos + match_len, store_end)); memset(&num_matches[i + 1], 0, skip * sizeof(uint)); i += skip; } else { cur_match_pos = cur_match_end; } } } orig_num_literals = *num_literals; orig_last_insert_len = *last_insert_len; memcpy(orig_dist_cache, dist_cache, 4 * sizeof(int)); orig_num_commands = *num_commands; nodes = (ZopfliNode *)BrotliAllocate(ref m, (num_bytes + 1) * sizeof(ZopfliNode)); InitZopfliCostModel(ref m, &model, num_bytes); for (i = 0; i < 2; i++) { BrotliInitZopfliNodes(nodes, num_bytes + 1); if (i == 0) { ZopfliCostModelSetFromLiteralCosts( &model, position, ringbuffer, ringbuffer_mask); } else { ZopfliCostModelSetFromCommands(&model, position, ringbuffer, ringbuffer_mask, commands, *num_commands - orig_num_commands, orig_last_insert_len); } *num_commands = orig_num_commands; *num_literals = orig_num_literals; *last_insert_len = orig_last_insert_len; memcpy(dist_cache, orig_dist_cache, 4 * sizeof(int)); *num_commands += ZopfliIterate(num_bytes, position, ringbuffer, ringbuffer_mask, params_, max_backward_limit, dist_cache, &model, num_matches, matches, nodes); BrotliZopfliCreateCommands(num_bytes, position, max_backward_limit, nodes, dist_cache, last_insert_len, commands, num_literals); } CleanupZopfliCostModel(ref m, &model); BrotliFree(ref m, nodes); BrotliFree(ref m, matches); BrotliFree(ref m, num_matches); }
/* REQUIRES: nodes != NULL and len(nodes) >= num_bytes + 1 */ private static unsafe size_t BrotliZopfliComputeShortestPath(ref MemoryManager m, size_t num_bytes, size_t position, byte *ringbuffer, size_t ringbuffer_mask, BrotliEncoderParams *params_, size_t max_backward_limit, int *dist_cache, HasherHandle hasher, ZopfliNode *nodes) { size_t max_zopfli_len = MaxZopfliLen(params_); ZopfliCostModel model; StartPosQueue queue; BackwardMatch * matches = stackalloc BackwardMatch[MAX_NUM_MATCHES_H10]; HashToBinaryTreeH10 h10 = (HashToBinaryTreeH10)kHashers[10]; size_t store_end = num_bytes >= h10.StoreLookahead() ? position + num_bytes - h10.StoreLookahead() + 1 : position; size_t i; nodes[0].length = 0; nodes[0].u.cost = 0; InitZopfliCostModel(ref m, &model, num_bytes); ZopfliCostModelSetFromLiteralCosts( &model, position, ringbuffer, ringbuffer_mask); InitStartPosQueue(&queue); for (i = 0; i + h10.HashTypeLength() - 1 < num_bytes; i++) { size_t pos = position + i; size_t max_distance = Math.Min(pos, max_backward_limit); size_t num_matches = HashToBinaryTreeH10.FindAllMatches(hasher, ringbuffer, ringbuffer_mask, pos, num_bytes - i, max_distance, params_, matches); size_t skip; if (num_matches > 0 && BackwardMatchLength(&matches[num_matches - 1]) > max_zopfli_len) { matches[0] = matches[num_matches - 1]; num_matches = 1; } skip = UpdateNodes(num_bytes, position, i, ringbuffer, ringbuffer_mask, params_, max_backward_limit, dist_cache, num_matches, matches, &model, &queue, nodes); if (skip < BROTLI_LONG_COPY_QUICK_STEP) { skip = 0; } if (num_matches == 1 && BackwardMatchLength(&matches[0]) > max_zopfli_len) { skip = Math.Max(BackwardMatchLength(&matches[0]), skip); } if (skip > 1) { /* Add the tail of the copy to the hasher. */ h10.StoreRange(hasher, ringbuffer, ringbuffer_mask, pos + 1, Math.Min( pos + skip, store_end)); skip--; while (skip != 0) { i++; if (i + h10.HashTypeLength() - 1 >= num_bytes) { break; } EvaluateNode( position, i, max_backward_limit, dist_cache, &model, &queue, nodes); skip--; } } } CleanupZopfliCostModel(ref m, &model); return(ComputeShortestPathFromNodes(num_bytes, nodes)); }
/* 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); }