public override unsafe void Initialize(HasherHandle handle, BrotliEncoderParams *params_) { HashToBinaryTree *self = Self(handle); self->window_mask_ = (1u << params_->lgwin) - 1u; self->invalid_pos_ = (uint)(0 - self->window_mask_); }
public override unsafe void StitchToPreviousBlock(HasherHandle handle, size_t num_bytes, size_t position, byte *ringbuffer, size_t ringbuffer_mask) { HashToBinaryTree *self = Self(handle); if (num_bytes >= HashTypeLength() - 1 && position >= MAX_TREE_COMP_LENGTH) { /* Store the last `MAX_TREE_COMP_LENGTH - 1` positions in the hasher. * These could not be calculated before, since they require knowledge * of both the previous and the current block. */ size_t i_start = position - MAX_TREE_COMP_LENGTH + 1; size_t i_end = Math.Min(position, i_start + num_bytes); size_t i; for (i = i_start; i < i_end; ++i) { /* Maximum distance is window size - 16, see section 9.1. of the spec. * Furthermore, we have to make sure that we don't look further back * from the start of the next block than the window size, otherwise we * could access already overwritten areas of the ring-buffer. */ size_t max_backward = self->window_mask_ - Math.Max( BROTLI_WINDOW_GAP - 1, position - i); /* We know that i + MAX_TREE_COMP_LENGTH <= position + num_bytes, i.e. the * end of the current block and that we have at least * MAX_TREE_COMP_LENGTH tail in the ring-buffer. */ StoreAndFindMatches(self, ringbuffer, i, ringbuffer_mask, MAX_TREE_COMP_LENGTH, max_backward, null, null); } } }
/* Stores the hash of the next 4 bytes and re-roots the binary tree at the * current sequence, without returning any matches. * REQUIRES: ix + MAX_TREE_COMP_LENGTH <= end-of-current-block */ public override unsafe void Store(HasherHandle handle, byte *data, size_t mask, size_t ix) { HashToBinaryTree *self = Self(handle); /* Maximum distance is window size - 16, see section 9.1. of the spec. */ size_t max_backward = self->window_mask_ - BROTLI_WINDOW_GAP + 1; StoreAndFindMatches(self, data, ix, mask, MAX_TREE_COMP_LENGTH, max_backward, null, null); }
public override unsafe void Prepare(HasherHandle handle, bool one_shot, size_t input_size, byte *data) { HashToBinaryTree *self = Self(handle); uint invalid_pos = self->invalid_pos_; uint i; for (i = 0; i < BUCKET_SIZE; i++) { self->buckets_[i] = invalid_pos; } }
private static unsafe size_t LeftChildIndex(HashToBinaryTree *self, size_t pos) { return(2 * (pos & self->window_mask_)); }
private static unsafe uint *Forest(HashToBinaryTree *self) { return((uint *)(&self[1])); }
/* 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); }