public override unsafe void Initialize(HasherHandle handle, BrotliEncoderParams *params_) { HasherCommon * common = GetHasherCommon(handle); HashLongestMatch *self = Self(handle); self->hash_shift_ = 32 - common->params_.bucket_bits; self->bucket_size_ = (size_t)1 << common->params_.bucket_bits; self->block_size_ = (size_t)1 << common->params_.block_bits; self->block_mask_ = (uint)(self->block_size_ - 1); }
/* Look at 4 bytes at &data[ix & mask]. * Compute a hash from these, and store the value of ix at that position. */ public override unsafe void Store(HasherHandle handle, byte *data, size_t mask, size_t ix) { HashLongestMatch *self = Self(handle); ushort * num = Num(self); uint key = HashBytes(&data[ix & mask], self->hash_shift_); size_t minor_ix = num[key] & self->block_mask_; size_t offset = minor_ix + (key << GetHasherCommon(handle)->params_.block_bits); Buckets(self)[offset] = (uint)ix; ++num[key]; }
public override unsafe void Prepare(HasherHandle handle, bool one_shot, SizeT input_size, byte *data) { HashLongestMatch *self = Self(handle); ushort * num = Num(self); /* Partial preparation is 100 times slower (per socket). */ size_t partial_prepare_threshold = self->bucket_size_ >> 6; if (one_shot && input_size <= partial_prepare_threshold) { size_t i; for (i = 0; i < input_size; ++i) { uint key = HashBytes(&data[i], self->hash_shift_); num[key] = 0; } } else { memset(num, 0, self->bucket_size_ * sizeof(ushort)); } }
private static unsafe uint *Buckets(HashLongestMatch *self) { return((uint *)(&Num(self)[self->bucket_size_])); }
private static unsafe ushort *Num(HashLongestMatch *self) { return((ushort *)(&self[1])); }
/* Find a longest backward match of &data[cur_ix] up to the length of * max_length and stores the position cur_ix in the hash table. * * REQUIRES: FN(PrepareDistanceCache) must be invoked for current distance cache * values; if this method is invoked repeatedly with the same distance * cache values, it is enough to invoke FN(PrepareDistanceCache) once. * * Does not look for matches longer than max_length. * Does not look for matches further away than max_backward. * Writes the best match into |out|. * Returns true when match is found, otherwise false. */ public override unsafe bool FindLongestMatch(HasherHandle handle, ushort *dictionary_hash, byte *data, size_t ring_buffer_mask, int *distance_cache, size_t cur_ix, size_t max_length, size_t max_backward, HasherSearchResult *out_) { HasherCommon * common = GetHasherCommon(handle); HashLongestMatch *self = Self(handle); ushort * num = Num(self); uint * buckets = Buckets(self); size_t cur_ix_masked = cur_ix & ring_buffer_mask; bool is_match_found = false; /* Don't accept a short copy from far away. */ score_t best_score = out_->score; size_t best_len = out_->len; size_t i; out_->len = 0; out_->len_x_code = 0; /* Try last distance first. */ for (i = 0; i < (size_t)common->params_.num_last_distances_to_check; ++i) { size_t backward = (size_t)distance_cache[i]; size_t prev_ix = (size_t)(cur_ix - backward); if (prev_ix >= cur_ix) { continue; } if ((backward > max_backward)) { continue; } prev_ix &= ring_buffer_mask; if (cur_ix_masked + best_len > ring_buffer_mask || prev_ix + best_len > ring_buffer_mask || data[cur_ix_masked + best_len] != data[prev_ix + best_len]) { continue; } { size_t len = FindMatchLengthWithLimit(&data[prev_ix], &data[cur_ix_masked], max_length); if (len >= 3 || (len == 2 && i < 2)) { /* Comparing for >= 2 does not change the semantics, but just saves for * a few unnecessary binary logarithms in backward reference score, * since we are not interested in such short matches. */ score_t score = BackwardReferenceScoreUsingLastDistance(len); if (best_score < score) { if (i != 0) { score -= BackwardReferencePenaltyUsingLastDistance(i); } if (best_score < score) { best_score = score; best_len = len; out_->len = best_len; out_->distance = backward; out_->score = best_score; is_match_found = true; } } } } } { uint key = HashBytes(&data[cur_ix_masked], self->hash_shift_); uint *bucket = &buckets[key << common->params_.block_bits]; size_t down = (num[key] > self->block_size_) ? (num[key] - self->block_size_) : 0; for (i = num[key]; i > down;) { size_t prev_ix = bucket[--i & self->block_mask_]; size_t backward = cur_ix - prev_ix; if ((backward > max_backward)) { break; } prev_ix &= ring_buffer_mask; if (cur_ix_masked + best_len > ring_buffer_mask || prev_ix + best_len > ring_buffer_mask || data[cur_ix_masked + best_len] != data[prev_ix + best_len]) { continue; } { size_t len = FindMatchLengthWithLimit(&data[prev_ix], &data[cur_ix_masked], max_length); if (len >= 4) { /* Comparing for >= 3 does not change the semantics, but just saves * for a few unnecessary binary logarithms in backward reference * score, since we are not interested in such short matches. */ score_t score = BackwardReferenceScore(len, backward); if (best_score < score) { best_score = score; best_len = len; out_->len = best_len; out_->distance = backward; out_->score = best_score; is_match_found = true; } } } } bucket[num[key] & self->block_mask_] = (uint)cur_ix; ++num[key]; } if (!is_match_found) { is_match_found = SearchInStaticDictionary(dictionary_hash, handle, &data[cur_ix_masked], max_length, max_backward, out_, false); } return(is_match_found); }