#define HashLongestMatch HASHER()
static
BROTLI_INLINE
size_t
FN(HashTypeLength)(
void
) {
return
4; }
static
BROTLI_INLINE
size_t
FN(StoreLookahead)(
void
) {
return
4; }
static
uint32_t FN(HashBytes)(
const
uint8_t* data,
const
int
shift) {
uint32_t h = BROTLI_UNALIGNED_LOAD32LE(data) * kHashMul32;
return
(uint32_t)(h >> shift);
}
typedef
struct
HashLongestMatch {
size_t
bucket_size_;
size_t
block_size_;
int
hash_shift_;
uint32_t block_mask_;
} HashLongestMatch;
static
BROTLI_INLINE HashLongestMatch* FN(Self)(HasherHandle handle) {
return
(HashLongestMatch*)&(GetHasherCommon(handle)[1]);
}
static
BROTLI_INLINE uint16_t* FN(Num)(HashLongestMatch* self) {
return
(uint16_t*)(&self[1]);
}
static
BROTLI_INLINE uint32_t* FN(Buckets)(HashLongestMatch* self) {
return
(uint32_t*)(&FN(Num)(self)[self->bucket_size_]);
}
static
void
FN(Initialize)(
HasherHandle handle,
const
BrotliEncoderParams* params) {
HasherCommon* common = GetHasherCommon(handle);
HashLongestMatch* self = FN(Self)(handle);
BROTLI_UNUSED(params);
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_ = (uint32_t)(self->block_size_ - 1);
}
static
void
FN(Prepare)(HasherHandle handle, BROTLI_BOOL one_shot,
size_t
input_size,
const
uint8_t* data) {
HashLongestMatch* self = FN(Self)(handle);
uint16_t* num = FN(Num)(self);
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) {
const
uint32_t key = FN(HashBytes)(&data[i], self->hash_shift_);
num[key] = 0;
}
}
else
{
memset
(num, 0, self->bucket_size_ *
sizeof
(num[0]));
}
}
static
BROTLI_INLINE
size_t
FN(HashMemAllocInBytes)(
const
BrotliEncoderParams* params, BROTLI_BOOL one_shot,
size_t
input_size) {
size_t
bucket_size = (
size_t
)1 << params->hasher.bucket_bits;
size_t
block_size = (
size_t
)1 << params->hasher.block_bits;
BROTLI_UNUSED(one_shot);
BROTLI_UNUSED(input_size);
return
sizeof
(HashLongestMatch) + bucket_size * (2 + 4 * block_size);
}
static
BROTLI_INLINE
void
FN(Store)(HasherHandle handle,
const
uint8_t* data,
const
size_t
mask,
const
size_t
ix) {
HashLongestMatch* self = FN(Self)(handle);
uint16_t* num = FN(Num)(self);
const
uint32_t key = FN(HashBytes)(&data[ix & mask], self->hash_shift_);
const
size_t
minor_ix = num[key] & self->block_mask_;
const
size_t
offset =
minor_ix + (key << GetHasherCommon(handle)->params.block_bits);
FN(Buckets)(self)[offset] = (uint32_t)ix;
++num[key];
}
static
BROTLI_INLINE
void
FN(StoreRange)(HasherHandle handle,
const
uint8_t* data,
const
size_t
mask,
const
size_t
ix_start,
const
size_t
ix_end) {
size_t
i;
for
(i = ix_start; i < ix_end; ++i) {
FN(Store)(handle, data, mask, i);
}
}
static
BROTLI_INLINE
void
FN(StitchToPreviousBlock)(HasherHandle handle,
size_t
num_bytes,
size_t
position,
const
uint8_t* ringbuffer,
size_t
ringbuffer_mask) {
if
(num_bytes >= FN(HashTypeLength)() - 1 && position >= 3) {
FN(Store)(handle, ringbuffer, ringbuffer_mask, position - 3);
FN(Store)(handle, ringbuffer, ringbuffer_mask, position - 2);
FN(Store)(handle, ringbuffer, ringbuffer_mask, position - 1);
}
}
static
BROTLI_INLINE
void
FN(PrepareDistanceCache)(
HasherHandle handle,
int
* BROTLI_RESTRICT distance_cache) {
PrepareDistanceCache(distance_cache,
GetHasherCommon(handle)->params.num_last_distances_to_check);
}
static
BROTLI_INLINE
void
FN(FindLongestMatch)(HasherHandle handle,
const
BrotliEncoderDictionary* dictionary,
const
uint8_t* BROTLI_RESTRICT data,
const
size_t
ring_buffer_mask,
const
int
* BROTLI_RESTRICT distance_cache,
const
size_t
cur_ix,
const
size_t
max_length,
const
size_t
max_backward,
const
size_t
gap,
const
size_t
max_distance,
HasherSearchResult* BROTLI_RESTRICT out) {
HasherCommon* common = GetHasherCommon(handle);
HashLongestMatch* self = FN(Self)(handle);
uint16_t* num = FN(Num)(self);
uint32_t* buckets = FN(Buckets)(self);
const
size_t
cur_ix_masked = cur_ix & ring_buffer_mask;
score_t min_score = out->score;
score_t best_score = out->score;
size_t
best_len = out->len;
size_t
i;
out->len = 0;
out->len_code_delta = 0;
for
(i = 0; i < (
size_t
)common->params.num_last_distances_to_check; ++i) {
const
size_t
backward = (
size_t
)distance_cache[i];
size_t
prev_ix = (
size_t
)(cur_ix - backward);
if
(prev_ix >= cur_ix) {
continue
;
}
if
(BROTLI_PREDICT_FALSE(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
;
}
{
const
size_t
len = FindMatchLengthWithLimit(&data[prev_ix],
&data[cur_ix_masked],
max_length);
if
(len >= 3 || (len == 2 && i < 2)) {
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;
}
}
}
}
}
{
const
uint32_t key =
FN(HashBytes)(&data[cur_ix_masked], self->hash_shift_);
uint32_t* BROTLI_RESTRICT bucket =
&buckets[key << common->params.block_bits];
const
size_t
down =
(num[key] > self->block_size_) ? (num[key] - self->block_size_) : 0u;
for
(i = num[key]; i > down;) {
size_t
prev_ix = bucket[--i & self->block_mask_];
const
size_t
backward = cur_ix - prev_ix;
if
(BROTLI_PREDICT_FALSE(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
;
}
{
const
size_t
len = FindMatchLengthWithLimit(&data[prev_ix],
&data[cur_ix_masked],
max_length);
if
(len >= 4) {
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;
}
}
}
}
bucket[num[key] & self->block_mask_] = (uint32_t)cur_ix;
++num[key];
}
if
(min_score == out->score) {
SearchInStaticDictionary(dictionary,
handle, &data[cur_ix_masked], max_length, max_backward + gap,
max_distance, out, BROTLI_FALSE);
}
}
#undef HashLongestMatch