github.com/andybalholm/brotli@v1.0.6/backward_references.go (about) 1 package brotli 2 3 import ( 4 "sync" 5 ) 6 7 /* Copyright 2013 Google Inc. All Rights Reserved. 8 9 Distributed under MIT license. 10 See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 11 */ 12 13 /* Function to find backward reference copies. */ 14 15 func computeDistanceCode(distance uint, max_distance uint, dist_cache []int) uint { 16 if distance <= max_distance { 17 var distance_plus_3 uint = distance + 3 18 var offset0 uint = distance_plus_3 - uint(dist_cache[0]) 19 var offset1 uint = distance_plus_3 - uint(dist_cache[1]) 20 if distance == uint(dist_cache[0]) { 21 return 0 22 } else if distance == uint(dist_cache[1]) { 23 return 1 24 } else if offset0 < 7 { 25 return (0x9750468 >> (4 * offset0)) & 0xF 26 } else if offset1 < 7 { 27 return (0xFDB1ACE >> (4 * offset1)) & 0xF 28 } else if distance == uint(dist_cache[2]) { 29 return 2 30 } else if distance == uint(dist_cache[3]) { 31 return 3 32 } 33 } 34 35 return distance + numDistanceShortCodes - 1 36 } 37 38 var hasherSearchResultPool sync.Pool 39 40 func createBackwardReferences(num_bytes uint, position uint, ringbuffer []byte, ringbuffer_mask uint, params *encoderParams, hasher hasherHandle, dist_cache []int, last_insert_len *uint, commands *[]command, num_literals *uint) { 41 var max_backward_limit uint = maxBackwardLimit(params.lgwin) 42 var insert_length uint = *last_insert_len 43 var pos_end uint = position + num_bytes 44 var store_end uint 45 if num_bytes >= hasher.StoreLookahead() { 46 store_end = position + num_bytes - hasher.StoreLookahead() + 1 47 } else { 48 store_end = position 49 } 50 var random_heuristics_window_size uint = literalSpreeLengthForSparseSearch(params) 51 var apply_random_heuristics uint = position + random_heuristics_window_size 52 var gap uint = 0 53 /* Set maximum distance, see section 9.1. of the spec. */ 54 55 const kMinScore uint = scoreBase + 100 56 57 /* For speed up heuristics for random data. */ 58 59 /* Minimum score to accept a backward reference. */ 60 hasher.PrepareDistanceCache(dist_cache) 61 sr2, _ := hasherSearchResultPool.Get().(*hasherSearchResult) 62 if sr2 == nil { 63 sr2 = &hasherSearchResult{} 64 } 65 sr, _ := hasherSearchResultPool.Get().(*hasherSearchResult) 66 if sr == nil { 67 sr = &hasherSearchResult{} 68 } 69 70 for position+hasher.HashTypeLength() < pos_end { 71 var max_length uint = pos_end - position 72 var max_distance uint = brotli_min_size_t(position, max_backward_limit) 73 sr.len = 0 74 sr.len_code_delta = 0 75 sr.distance = 0 76 sr.score = kMinScore 77 hasher.FindLongestMatch(¶ms.dictionary, ringbuffer, ringbuffer_mask, dist_cache, position, max_length, max_distance, gap, params.dist.max_distance, sr) 78 if sr.score > kMinScore { 79 /* Found a match. Let's look for something even better ahead. */ 80 var delayed_backward_references_in_row int = 0 81 max_length-- 82 for ; ; max_length-- { 83 var cost_diff_lazy uint = 175 84 if params.quality < minQualityForExtensiveReferenceSearch { 85 sr2.len = brotli_min_size_t(sr.len-1, max_length) 86 } else { 87 sr2.len = 0 88 } 89 sr2.len_code_delta = 0 90 sr2.distance = 0 91 sr2.score = kMinScore 92 max_distance = brotli_min_size_t(position+1, max_backward_limit) 93 hasher.FindLongestMatch(¶ms.dictionary, ringbuffer, ringbuffer_mask, dist_cache, position+1, max_length, max_distance, gap, params.dist.max_distance, sr2) 94 if sr2.score >= sr.score+cost_diff_lazy { 95 /* Ok, let's just write one byte for now and start a match from the 96 next byte. */ 97 position++ 98 99 insert_length++ 100 *sr = *sr2 101 delayed_backward_references_in_row++ 102 if delayed_backward_references_in_row < 4 && position+hasher.HashTypeLength() < pos_end { 103 continue 104 } 105 } 106 107 break 108 } 109 110 apply_random_heuristics = position + 2*sr.len + random_heuristics_window_size 111 max_distance = brotli_min_size_t(position, max_backward_limit) 112 { 113 /* The first 16 codes are special short-codes, 114 and the minimum offset is 1. */ 115 var distance_code uint = computeDistanceCode(sr.distance, max_distance+gap, dist_cache) 116 if (sr.distance <= (max_distance + gap)) && distance_code > 0 { 117 dist_cache[3] = dist_cache[2] 118 dist_cache[2] = dist_cache[1] 119 dist_cache[1] = dist_cache[0] 120 dist_cache[0] = int(sr.distance) 121 hasher.PrepareDistanceCache(dist_cache) 122 } 123 124 *commands = append(*commands, makeCommand(¶ms.dist, insert_length, sr.len, sr.len_code_delta, distance_code)) 125 } 126 127 *num_literals += insert_length 128 insert_length = 0 129 /* Put the hash keys into the table, if there are enough bytes left. 130 Depending on the hasher implementation, it can push all positions 131 in the given range or only a subset of them. 132 Avoid hash poisoning with RLE data. */ 133 { 134 var range_start uint = position + 2 135 var range_end uint = brotli_min_size_t(position+sr.len, store_end) 136 if sr.distance < sr.len>>2 { 137 range_start = brotli_min_size_t(range_end, brotli_max_size_t(range_start, position+sr.len-(sr.distance<<2))) 138 } 139 140 hasher.StoreRange(ringbuffer, ringbuffer_mask, range_start, range_end) 141 } 142 143 position += sr.len 144 } else { 145 insert_length++ 146 position++ 147 148 /* If we have not seen matches for a long time, we can skip some 149 match lookups. Unsuccessful match lookups are very very expensive 150 and this kind of a heuristic speeds up compression quite 151 a lot. */ 152 if position > apply_random_heuristics { 153 /* Going through uncompressible data, jump. */ 154 if position > apply_random_heuristics+4*random_heuristics_window_size { 155 var kMargin uint = brotli_max_size_t(hasher.StoreLookahead()-1, 4) 156 /* It is quite a long time since we saw a copy, so we assume 157 that this data is not compressible, and store hashes less 158 often. Hashes of non compressible data are less likely to 159 turn out to be useful in the future, too, so we store less of 160 them to not to flood out the hash table of good compressible 161 data. */ 162 163 var pos_jump uint = brotli_min_size_t(position+16, pos_end-kMargin) 164 for ; position < pos_jump; position += 4 { 165 hasher.Store(ringbuffer, ringbuffer_mask, position) 166 insert_length += 4 167 } 168 } else { 169 var kMargin uint = brotli_max_size_t(hasher.StoreLookahead()-1, 2) 170 var pos_jump uint = brotli_min_size_t(position+8, pos_end-kMargin) 171 for ; position < pos_jump; position += 2 { 172 hasher.Store(ringbuffer, ringbuffer_mask, position) 173 insert_length += 2 174 } 175 } 176 } 177 } 178 } 179 180 insert_length += pos_end - position 181 *last_insert_len = insert_length 182 183 hasherSearchResultPool.Put(sr) 184 hasherSearchResultPool.Put(sr2) 185 }