예제 #1
0
 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));
 }
예제 #2
0
        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));
        }
예제 #3
0
 private static unsafe void InitBackwardMatch(BackwardMatch *self,
                                              size_t dist, size_t len)
 {
     self->distance        = (uint)dist;
     self->length_and_code = (uint)(len << 5);
 }
예제 #4
0
        private static unsafe size_t BackwardMatchLengthCode(BackwardMatch *self)
        {
            size_t code = self->length_and_code & 31;

            return(code != 0 ? code : BackwardMatchLength(self));
        }
예제 #5
0
 private static unsafe size_t BackwardMatchLength(BackwardMatch *self)
 {
     return(self->length_and_code >> 5);
 }
예제 #6
0
            /* 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));
                }
            }
예제 #7
0
            /* 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);
            }
예제 #8
0
        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);
        }
예제 #9
0
        /* 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));
        }
예제 #10
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);
        }