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(&params.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(&params.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(&params.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  }