github.com/andybalholm/brotli@v1.0.6/metablock.go (about)

     1  package brotli
     2  
     3  import (
     4  	"sync"
     5  )
     6  
     7  /* Copyright 2014 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  /* Algorithms for distributing the literals and commands of a metablock between
    14     block types and contexts. */
    15  
    16  type metaBlockSplit struct {
    17  	literal_split             blockSplit
    18  	command_split             blockSplit
    19  	distance_split            blockSplit
    20  	literal_context_map       []uint32
    21  	literal_context_map_size  uint
    22  	distance_context_map      []uint32
    23  	distance_context_map_size uint
    24  	literal_histograms        []histogramLiteral
    25  	literal_histograms_size   uint
    26  	command_histograms        []histogramCommand
    27  	command_histograms_size   uint
    28  	distance_histograms       []histogramDistance
    29  	distance_histograms_size  uint
    30  }
    31  
    32  var metaBlockPool sync.Pool
    33  
    34  func getMetaBlockSplit() *metaBlockSplit {
    35  	mb, _ := metaBlockPool.Get().(*metaBlockSplit)
    36  
    37  	if mb == nil {
    38  		mb = &metaBlockSplit{}
    39  	} else {
    40  		initBlockSplit(&mb.literal_split)
    41  		initBlockSplit(&mb.command_split)
    42  		initBlockSplit(&mb.distance_split)
    43  		mb.literal_context_map = mb.literal_context_map[:0]
    44  		mb.literal_context_map_size = 0
    45  		mb.distance_context_map = mb.distance_context_map[:0]
    46  		mb.distance_context_map_size = 0
    47  		mb.literal_histograms = mb.literal_histograms[:0]
    48  		mb.command_histograms = mb.command_histograms[:0]
    49  		mb.distance_histograms = mb.distance_histograms[:0]
    50  	}
    51  	return mb
    52  }
    53  
    54  func freeMetaBlockSplit(mb *metaBlockSplit) {
    55  	metaBlockPool.Put(mb)
    56  }
    57  
    58  func initDistanceParams(params *encoderParams, npostfix uint32, ndirect uint32) {
    59  	var dist_params *distanceParams = &params.dist
    60  	var alphabet_size uint32
    61  	var max_distance uint32
    62  
    63  	dist_params.distance_postfix_bits = npostfix
    64  	dist_params.num_direct_distance_codes = ndirect
    65  
    66  	alphabet_size = uint32(distanceAlphabetSize(uint(npostfix), uint(ndirect), maxDistanceBits))
    67  	max_distance = ndirect + (1 << (maxDistanceBits + npostfix + 2)) - (1 << (npostfix + 2))
    68  
    69  	if params.large_window {
    70  		var bound = [maxNpostfix + 1]uint32{0, 4, 12, 28}
    71  		var postfix uint32 = 1 << npostfix
    72  		alphabet_size = uint32(distanceAlphabetSize(uint(npostfix), uint(ndirect), largeMaxDistanceBits))
    73  
    74  		/* The maximum distance is set so that no distance symbol used can encode
    75  		   a distance larger than BROTLI_MAX_ALLOWED_DISTANCE with all
    76  		   its extra bits set. */
    77  		if ndirect < bound[npostfix] {
    78  			max_distance = maxAllowedDistance - (bound[npostfix] - ndirect)
    79  		} else if ndirect >= bound[npostfix]+postfix {
    80  			max_distance = (3 << 29) - 4 + (ndirect - bound[npostfix])
    81  		} else {
    82  			max_distance = maxAllowedDistance
    83  		}
    84  	}
    85  
    86  	dist_params.alphabet_size = alphabet_size
    87  	dist_params.max_distance = uint(max_distance)
    88  }
    89  
    90  func recomputeDistancePrefixes(cmds []command, orig_params *distanceParams, new_params *distanceParams) {
    91  	if orig_params.distance_postfix_bits == new_params.distance_postfix_bits && orig_params.num_direct_distance_codes == new_params.num_direct_distance_codes {
    92  		return
    93  	}
    94  
    95  	for i := range cmds {
    96  		var cmd *command = &cmds[i]
    97  		if commandCopyLen(cmd) != 0 && cmd.cmd_prefix_ >= 128 {
    98  			prefixEncodeCopyDistance(uint(commandRestoreDistanceCode(cmd, orig_params)), uint(new_params.num_direct_distance_codes), uint(new_params.distance_postfix_bits), &cmd.dist_prefix_, &cmd.dist_extra_)
    99  		}
   100  	}
   101  }
   102  
   103  func computeDistanceCost(cmds []command, orig_params *distanceParams, new_params *distanceParams, cost *float64) bool {
   104  	var equal_params bool = false
   105  	var dist_prefix uint16
   106  	var dist_extra uint32
   107  	var extra_bits float64 = 0.0
   108  	var histo histogramDistance
   109  	histogramClearDistance(&histo)
   110  
   111  	if orig_params.distance_postfix_bits == new_params.distance_postfix_bits && orig_params.num_direct_distance_codes == new_params.num_direct_distance_codes {
   112  		equal_params = true
   113  	}
   114  
   115  	for i := range cmds {
   116  		cmd := &cmds[i]
   117  		if commandCopyLen(cmd) != 0 && cmd.cmd_prefix_ >= 128 {
   118  			if equal_params {
   119  				dist_prefix = cmd.dist_prefix_
   120  			} else {
   121  				var distance uint32 = commandRestoreDistanceCode(cmd, orig_params)
   122  				if distance > uint32(new_params.max_distance) {
   123  					return false
   124  				}
   125  
   126  				prefixEncodeCopyDistance(uint(distance), uint(new_params.num_direct_distance_codes), uint(new_params.distance_postfix_bits), &dist_prefix, &dist_extra)
   127  			}
   128  
   129  			histogramAddDistance(&histo, uint(dist_prefix)&0x3FF)
   130  			extra_bits += float64(dist_prefix >> 10)
   131  		}
   132  	}
   133  
   134  	*cost = populationCostDistance(&histo) + extra_bits
   135  	return true
   136  }
   137  
   138  var buildMetaBlock_kMaxNumberOfHistograms uint = 256
   139  
   140  func buildMetaBlock(ringbuffer []byte, pos uint, mask uint, params *encoderParams, prev_byte byte, prev_byte2 byte, cmds []command, literal_context_mode int, mb *metaBlockSplit) {
   141  	var distance_histograms []histogramDistance
   142  	var literal_histograms []histogramLiteral
   143  	var literal_context_modes []int = nil
   144  	var literal_histograms_size uint
   145  	var distance_histograms_size uint
   146  	var i uint
   147  	var literal_context_multiplier uint = 1
   148  	var npostfix uint32
   149  	var ndirect_msb uint32 = 0
   150  	var check_orig bool = true
   151  	var best_dist_cost float64 = 1e99
   152  	var orig_params encoderParams = *params
   153  	/* Histogram ids need to fit in one byte. */
   154  
   155  	var new_params encoderParams = *params
   156  
   157  	for npostfix = 0; npostfix <= maxNpostfix; npostfix++ {
   158  		for ; ndirect_msb < 16; ndirect_msb++ {
   159  			var ndirect uint32 = ndirect_msb << npostfix
   160  			var skip bool
   161  			var dist_cost float64
   162  			initDistanceParams(&new_params, npostfix, ndirect)
   163  			if npostfix == orig_params.dist.distance_postfix_bits && ndirect == orig_params.dist.num_direct_distance_codes {
   164  				check_orig = false
   165  			}
   166  
   167  			skip = !computeDistanceCost(cmds, &orig_params.dist, &new_params.dist, &dist_cost)
   168  			if skip || (dist_cost > best_dist_cost) {
   169  				break
   170  			}
   171  
   172  			best_dist_cost = dist_cost
   173  			params.dist = new_params.dist
   174  		}
   175  
   176  		if ndirect_msb > 0 {
   177  			ndirect_msb--
   178  		}
   179  		ndirect_msb /= 2
   180  	}
   181  
   182  	if check_orig {
   183  		var dist_cost float64
   184  		computeDistanceCost(cmds, &orig_params.dist, &orig_params.dist, &dist_cost)
   185  		if dist_cost < best_dist_cost {
   186  			/* NB: currently unused; uncomment when more param tuning is added. */
   187  			/* best_dist_cost = dist_cost; */
   188  			params.dist = orig_params.dist
   189  		}
   190  	}
   191  
   192  	recomputeDistancePrefixes(cmds, &orig_params.dist, &params.dist)
   193  
   194  	splitBlock(cmds, ringbuffer, pos, mask, params, &mb.literal_split, &mb.command_split, &mb.distance_split)
   195  
   196  	if !params.disable_literal_context_modeling {
   197  		literal_context_multiplier = 1 << literalContextBits
   198  		literal_context_modes = make([]int, (mb.literal_split.num_types))
   199  		for i = 0; i < mb.literal_split.num_types; i++ {
   200  			literal_context_modes[i] = literal_context_mode
   201  		}
   202  	}
   203  
   204  	literal_histograms_size = mb.literal_split.num_types * literal_context_multiplier
   205  	literal_histograms = make([]histogramLiteral, literal_histograms_size)
   206  	clearHistogramsLiteral(literal_histograms, literal_histograms_size)
   207  
   208  	distance_histograms_size = mb.distance_split.num_types << distanceContextBits
   209  	distance_histograms = make([]histogramDistance, distance_histograms_size)
   210  	clearHistogramsDistance(distance_histograms, distance_histograms_size)
   211  
   212  	mb.command_histograms_size = mb.command_split.num_types
   213  	if cap(mb.command_histograms) < int(mb.command_histograms_size) {
   214  		mb.command_histograms = make([]histogramCommand, (mb.command_histograms_size))
   215  	} else {
   216  		mb.command_histograms = mb.command_histograms[:mb.command_histograms_size]
   217  	}
   218  	clearHistogramsCommand(mb.command_histograms, mb.command_histograms_size)
   219  
   220  	buildHistogramsWithContext(cmds, &mb.literal_split, &mb.command_split, &mb.distance_split, ringbuffer, pos, mask, prev_byte, prev_byte2, literal_context_modes, literal_histograms, mb.command_histograms, distance_histograms)
   221  	literal_context_modes = nil
   222  
   223  	mb.literal_context_map_size = mb.literal_split.num_types << literalContextBits
   224  	if cap(mb.literal_context_map) < int(mb.literal_context_map_size) {
   225  		mb.literal_context_map = make([]uint32, (mb.literal_context_map_size))
   226  	} else {
   227  		mb.literal_context_map = mb.literal_context_map[:mb.literal_context_map_size]
   228  	}
   229  
   230  	mb.literal_histograms_size = mb.literal_context_map_size
   231  	if cap(mb.literal_histograms) < int(mb.literal_histograms_size) {
   232  		mb.literal_histograms = make([]histogramLiteral, (mb.literal_histograms_size))
   233  	} else {
   234  		mb.literal_histograms = mb.literal_histograms[:mb.literal_histograms_size]
   235  	}
   236  
   237  	clusterHistogramsLiteral(literal_histograms, literal_histograms_size, buildMetaBlock_kMaxNumberOfHistograms, mb.literal_histograms, &mb.literal_histograms_size, mb.literal_context_map)
   238  	literal_histograms = nil
   239  
   240  	if params.disable_literal_context_modeling {
   241  		/* Distribute assignment to all contexts. */
   242  		for i = mb.literal_split.num_types; i != 0; {
   243  			var j uint = 0
   244  			i--
   245  			for ; j < 1<<literalContextBits; j++ {
   246  				mb.literal_context_map[(i<<literalContextBits)+j] = mb.literal_context_map[i]
   247  			}
   248  		}
   249  	}
   250  
   251  	mb.distance_context_map_size = mb.distance_split.num_types << distanceContextBits
   252  	if cap(mb.distance_context_map) < int(mb.distance_context_map_size) {
   253  		mb.distance_context_map = make([]uint32, (mb.distance_context_map_size))
   254  	} else {
   255  		mb.distance_context_map = mb.distance_context_map[:mb.distance_context_map_size]
   256  	}
   257  
   258  	mb.distance_histograms_size = mb.distance_context_map_size
   259  	if cap(mb.distance_histograms) < int(mb.distance_histograms_size) {
   260  		mb.distance_histograms = make([]histogramDistance, (mb.distance_histograms_size))
   261  	} else {
   262  		mb.distance_histograms = mb.distance_histograms[:mb.distance_histograms_size]
   263  	}
   264  
   265  	clusterHistogramsDistance(distance_histograms, mb.distance_context_map_size, buildMetaBlock_kMaxNumberOfHistograms, mb.distance_histograms, &mb.distance_histograms_size, mb.distance_context_map)
   266  	distance_histograms = nil
   267  }
   268  
   269  const maxStaticContexts = 13
   270  
   271  /* Greedy block splitter for one block category (literal, command or distance).
   272     Gathers histograms for all context buckets. */
   273  type contextBlockSplitter struct {
   274  	alphabet_size_     uint
   275  	num_contexts_      uint
   276  	max_block_types_   uint
   277  	min_block_size_    uint
   278  	split_threshold_   float64
   279  	num_blocks_        uint
   280  	split_             *blockSplit
   281  	histograms_        []histogramLiteral
   282  	histograms_size_   *uint
   283  	target_block_size_ uint
   284  	block_size_        uint
   285  	curr_histogram_ix_ uint
   286  	last_histogram_ix_ [2]uint
   287  	last_entropy_      [2 * maxStaticContexts]float64
   288  	merge_last_count_  uint
   289  }
   290  
   291  func initContextBlockSplitter(self *contextBlockSplitter, alphabet_size uint, num_contexts uint, min_block_size uint, split_threshold float64, num_symbols uint, split *blockSplit, histograms *[]histogramLiteral, histograms_size *uint) {
   292  	var max_num_blocks uint = num_symbols/min_block_size + 1
   293  	var max_num_types uint
   294  	assert(num_contexts <= maxStaticContexts)
   295  
   296  	self.alphabet_size_ = alphabet_size
   297  	self.num_contexts_ = num_contexts
   298  	self.max_block_types_ = maxNumberOfBlockTypes / num_contexts
   299  	self.min_block_size_ = min_block_size
   300  	self.split_threshold_ = split_threshold
   301  	self.num_blocks_ = 0
   302  	self.split_ = split
   303  	self.histograms_size_ = histograms_size
   304  	self.target_block_size_ = min_block_size
   305  	self.block_size_ = 0
   306  	self.curr_histogram_ix_ = 0
   307  	self.merge_last_count_ = 0
   308  
   309  	/* We have to allocate one more histogram than the maximum number of block
   310  	   types for the current histogram when the meta-block is too big. */
   311  	max_num_types = brotli_min_size_t(max_num_blocks, self.max_block_types_+1)
   312  
   313  	brotli_ensure_capacity_uint8_t(&split.types, &split.types_alloc_size, max_num_blocks)
   314  	brotli_ensure_capacity_uint32_t(&split.lengths, &split.lengths_alloc_size, max_num_blocks)
   315  	split.num_blocks = max_num_blocks
   316  	*histograms_size = max_num_types * num_contexts
   317  	if histograms == nil || cap(*histograms) < int(*histograms_size) {
   318  		*histograms = make([]histogramLiteral, (*histograms_size))
   319  	} else {
   320  		*histograms = (*histograms)[:*histograms_size]
   321  	}
   322  	self.histograms_ = *histograms
   323  
   324  	/* Clear only current histogram. */
   325  	clearHistogramsLiteral(self.histograms_[0:], num_contexts)
   326  
   327  	self.last_histogram_ix_[1] = 0
   328  	self.last_histogram_ix_[0] = self.last_histogram_ix_[1]
   329  }
   330  
   331  /* Does either of three things:
   332     (1) emits the current block with a new block type;
   333     (2) emits the current block with the type of the second last block;
   334     (3) merges the current block with the last block. */
   335  func contextBlockSplitterFinishBlock(self *contextBlockSplitter, is_final bool) {
   336  	var split *blockSplit = self.split_
   337  	var num_contexts uint = self.num_contexts_
   338  	var last_entropy []float64 = self.last_entropy_[:]
   339  	var histograms []histogramLiteral = self.histograms_
   340  
   341  	if self.block_size_ < self.min_block_size_ {
   342  		self.block_size_ = self.min_block_size_
   343  	}
   344  
   345  	if self.num_blocks_ == 0 {
   346  		var i uint
   347  
   348  		/* Create first block. */
   349  		split.lengths[0] = uint32(self.block_size_)
   350  
   351  		split.types[0] = 0
   352  
   353  		for i = 0; i < num_contexts; i++ {
   354  			last_entropy[i] = bitsEntropy(histograms[i].data_[:], self.alphabet_size_)
   355  			last_entropy[num_contexts+i] = last_entropy[i]
   356  		}
   357  
   358  		self.num_blocks_++
   359  		split.num_types++
   360  		self.curr_histogram_ix_ += num_contexts
   361  		if self.curr_histogram_ix_ < *self.histograms_size_ {
   362  			clearHistogramsLiteral(self.histograms_[self.curr_histogram_ix_:], self.num_contexts_)
   363  		}
   364  
   365  		self.block_size_ = 0
   366  	} else if self.block_size_ > 0 {
   367  		var entropy [maxStaticContexts]float64
   368  		var combined_histo []histogramLiteral = make([]histogramLiteral, (2 * num_contexts))
   369  		var combined_entropy [2 * maxStaticContexts]float64
   370  		var diff = [2]float64{0.0}
   371  		/* Try merging the set of histograms for the current block type with the
   372  		   respective set of histograms for the last and second last block types.
   373  		   Decide over the split based on the total reduction of entropy across
   374  		   all contexts. */
   375  
   376  		var i uint
   377  		for i = 0; i < num_contexts; i++ {
   378  			var curr_histo_ix uint = self.curr_histogram_ix_ + i
   379  			var j uint
   380  			entropy[i] = bitsEntropy(histograms[curr_histo_ix].data_[:], self.alphabet_size_)
   381  			for j = 0; j < 2; j++ {
   382  				var jx uint = j*num_contexts + i
   383  				var last_histogram_ix uint = self.last_histogram_ix_[j] + i
   384  				combined_histo[jx] = histograms[curr_histo_ix]
   385  				histogramAddHistogramLiteral(&combined_histo[jx], &histograms[last_histogram_ix])
   386  				combined_entropy[jx] = bitsEntropy(combined_histo[jx].data_[0:], self.alphabet_size_)
   387  				diff[j] += combined_entropy[jx] - entropy[i] - last_entropy[jx]
   388  			}
   389  		}
   390  
   391  		if split.num_types < self.max_block_types_ && diff[0] > self.split_threshold_ && diff[1] > self.split_threshold_ {
   392  			/* Create new block. */
   393  			split.lengths[self.num_blocks_] = uint32(self.block_size_)
   394  
   395  			split.types[self.num_blocks_] = byte(split.num_types)
   396  			self.last_histogram_ix_[1] = self.last_histogram_ix_[0]
   397  			self.last_histogram_ix_[0] = split.num_types * num_contexts
   398  			for i = 0; i < num_contexts; i++ {
   399  				last_entropy[num_contexts+i] = last_entropy[i]
   400  				last_entropy[i] = entropy[i]
   401  			}
   402  
   403  			self.num_blocks_++
   404  			split.num_types++
   405  			self.curr_histogram_ix_ += num_contexts
   406  			if self.curr_histogram_ix_ < *self.histograms_size_ {
   407  				clearHistogramsLiteral(self.histograms_[self.curr_histogram_ix_:], self.num_contexts_)
   408  			}
   409  
   410  			self.block_size_ = 0
   411  			self.merge_last_count_ = 0
   412  			self.target_block_size_ = self.min_block_size_
   413  		} else if diff[1] < diff[0]-20.0 {
   414  			split.lengths[self.num_blocks_] = uint32(self.block_size_)
   415  			split.types[self.num_blocks_] = split.types[self.num_blocks_-2]
   416  			/* Combine this block with second last block. */
   417  
   418  			var tmp uint = self.last_histogram_ix_[0]
   419  			self.last_histogram_ix_[0] = self.last_histogram_ix_[1]
   420  			self.last_histogram_ix_[1] = tmp
   421  			for i = 0; i < num_contexts; i++ {
   422  				histograms[self.last_histogram_ix_[0]+i] = combined_histo[num_contexts+i]
   423  				last_entropy[num_contexts+i] = last_entropy[i]
   424  				last_entropy[i] = combined_entropy[num_contexts+i]
   425  				histogramClearLiteral(&histograms[self.curr_histogram_ix_+i])
   426  			}
   427  
   428  			self.num_blocks_++
   429  			self.block_size_ = 0
   430  			self.merge_last_count_ = 0
   431  			self.target_block_size_ = self.min_block_size_
   432  		} else {
   433  			/* Combine this block with last block. */
   434  			split.lengths[self.num_blocks_-1] += uint32(self.block_size_)
   435  
   436  			for i = 0; i < num_contexts; i++ {
   437  				histograms[self.last_histogram_ix_[0]+i] = combined_histo[i]
   438  				last_entropy[i] = combined_entropy[i]
   439  				if split.num_types == 1 {
   440  					last_entropy[num_contexts+i] = last_entropy[i]
   441  				}
   442  
   443  				histogramClearLiteral(&histograms[self.curr_histogram_ix_+i])
   444  			}
   445  
   446  			self.block_size_ = 0
   447  			self.merge_last_count_++
   448  			if self.merge_last_count_ > 1 {
   449  				self.target_block_size_ += self.min_block_size_
   450  			}
   451  		}
   452  
   453  		combined_histo = nil
   454  	}
   455  
   456  	if is_final {
   457  		*self.histograms_size_ = split.num_types * num_contexts
   458  		split.num_blocks = self.num_blocks_
   459  	}
   460  }
   461  
   462  /* Adds the next symbol to the current block type and context. When the
   463     current block reaches the target size, decides on merging the block. */
   464  func contextBlockSplitterAddSymbol(self *contextBlockSplitter, symbol uint, context uint) {
   465  	histogramAddLiteral(&self.histograms_[self.curr_histogram_ix_+context], symbol)
   466  	self.block_size_++
   467  	if self.block_size_ == self.target_block_size_ {
   468  		contextBlockSplitterFinishBlock(self, false) /* is_final = */
   469  	}
   470  }
   471  
   472  func mapStaticContexts(num_contexts uint, static_context_map []uint32, mb *metaBlockSplit) {
   473  	var i uint
   474  	mb.literal_context_map_size = mb.literal_split.num_types << literalContextBits
   475  	if cap(mb.literal_context_map) < int(mb.literal_context_map_size) {
   476  		mb.literal_context_map = make([]uint32, (mb.literal_context_map_size))
   477  	} else {
   478  		mb.literal_context_map = mb.literal_context_map[:mb.literal_context_map_size]
   479  	}
   480  
   481  	for i = 0; i < mb.literal_split.num_types; i++ {
   482  		var offset uint32 = uint32(i * num_contexts)
   483  		var j uint
   484  		for j = 0; j < 1<<literalContextBits; j++ {
   485  			mb.literal_context_map[(i<<literalContextBits)+j] = offset + static_context_map[j]
   486  		}
   487  	}
   488  }
   489  
   490  func buildMetaBlockGreedyInternal(ringbuffer []byte, pos uint, mask uint, prev_byte byte, prev_byte2 byte, literal_context_lut contextLUT, num_contexts uint, static_context_map []uint32, commands []command, mb *metaBlockSplit) {
   491  	var lit_blocks struct {
   492  		plain blockSplitterLiteral
   493  		ctx   contextBlockSplitter
   494  	}
   495  	var cmd_blocks blockSplitterCommand
   496  	var dist_blocks blockSplitterDistance
   497  	var num_literals uint = 0
   498  	for i := range commands {
   499  		num_literals += uint(commands[i].insert_len_)
   500  	}
   501  
   502  	if num_contexts == 1 {
   503  		initBlockSplitterLiteral(&lit_blocks.plain, 256, 512, 400.0, num_literals, &mb.literal_split, &mb.literal_histograms, &mb.literal_histograms_size)
   504  	} else {
   505  		initContextBlockSplitter(&lit_blocks.ctx, 256, num_contexts, 512, 400.0, num_literals, &mb.literal_split, &mb.literal_histograms, &mb.literal_histograms_size)
   506  	}
   507  
   508  	initBlockSplitterCommand(&cmd_blocks, numCommandSymbols, 1024, 500.0, uint(len(commands)), &mb.command_split, &mb.command_histograms, &mb.command_histograms_size)
   509  	initBlockSplitterDistance(&dist_blocks, 64, 512, 100.0, uint(len(commands)), &mb.distance_split, &mb.distance_histograms, &mb.distance_histograms_size)
   510  
   511  	for _, cmd := range commands {
   512  		var j uint
   513  		blockSplitterAddSymbolCommand(&cmd_blocks, uint(cmd.cmd_prefix_))
   514  		for j = uint(cmd.insert_len_); j != 0; j-- {
   515  			var literal byte = ringbuffer[pos&mask]
   516  			if num_contexts == 1 {
   517  				blockSplitterAddSymbolLiteral(&lit_blocks.plain, uint(literal))
   518  			} else {
   519  				var context uint = uint(getContext(prev_byte, prev_byte2, literal_context_lut))
   520  				contextBlockSplitterAddSymbol(&lit_blocks.ctx, uint(literal), uint(static_context_map[context]))
   521  			}
   522  
   523  			prev_byte2 = prev_byte
   524  			prev_byte = literal
   525  			pos++
   526  		}
   527  
   528  		pos += uint(commandCopyLen(&cmd))
   529  		if commandCopyLen(&cmd) != 0 {
   530  			prev_byte2 = ringbuffer[(pos-2)&mask]
   531  			prev_byte = ringbuffer[(pos-1)&mask]
   532  			if cmd.cmd_prefix_ >= 128 {
   533  				blockSplitterAddSymbolDistance(&dist_blocks, uint(cmd.dist_prefix_)&0x3FF)
   534  			}
   535  		}
   536  	}
   537  
   538  	if num_contexts == 1 {
   539  		blockSplitterFinishBlockLiteral(&lit_blocks.plain, true) /* is_final = */
   540  	} else {
   541  		contextBlockSplitterFinishBlock(&lit_blocks.ctx, true) /* is_final = */
   542  	}
   543  
   544  	blockSplitterFinishBlockCommand(&cmd_blocks, true)   /* is_final = */
   545  	blockSplitterFinishBlockDistance(&dist_blocks, true) /* is_final = */
   546  
   547  	if num_contexts > 1 {
   548  		mapStaticContexts(num_contexts, static_context_map, mb)
   549  	}
   550  }
   551  
   552  func buildMetaBlockGreedy(ringbuffer []byte, pos uint, mask uint, prev_byte byte, prev_byte2 byte, literal_context_lut contextLUT, num_contexts uint, static_context_map []uint32, commands []command, mb *metaBlockSplit) {
   553  	if num_contexts == 1 {
   554  		buildMetaBlockGreedyInternal(ringbuffer, pos, mask, prev_byte, prev_byte2, literal_context_lut, 1, nil, commands, mb)
   555  	} else {
   556  		buildMetaBlockGreedyInternal(ringbuffer, pos, mask, prev_byte, prev_byte2, literal_context_lut, num_contexts, static_context_map, commands, mb)
   557  	}
   558  }
   559  
   560  func optimizeHistograms(num_distance_codes uint32, mb *metaBlockSplit) {
   561  	var good_for_rle [numCommandSymbols]byte
   562  	var i uint
   563  	for i = 0; i < mb.literal_histograms_size; i++ {
   564  		optimizeHuffmanCountsForRLE(256, mb.literal_histograms[i].data_[:], good_for_rle[:])
   565  	}
   566  
   567  	for i = 0; i < mb.command_histograms_size; i++ {
   568  		optimizeHuffmanCountsForRLE(numCommandSymbols, mb.command_histograms[i].data_[:], good_for_rle[:])
   569  	}
   570  
   571  	for i = 0; i < mb.distance_histograms_size; i++ {
   572  		optimizeHuffmanCountsForRLE(uint(num_distance_codes), mb.distance_histograms[i].data_[:], good_for_rle[:])
   573  	}
   574  }