static void ZopfliLZ77OptimalFixed(ZopfliBlockState s, byte[] InFile, ulong instart, ulong inend, ZopfliLZ77Store store) { /* Dist to get to here with smallest cost. */ ulong blocksize = inend - instart; ushort[] length_array = new ushort[blocksize + 1]; List <ushort> path = new List <ushort>(); path.Add(0); ZopfliHash h = new ZopfliHash(); float[] costs = new float[blocksize + 1]; SymbolStats stats = new SymbolStats(); s.blockstart = (int)instart; s.blockend = (int)inend; /* Shortest path for fixed tree This one should give the shortest possible * result for fixed tree, no repeated runs are needed since the tree is known. */ LZ77OptimalRun(s, InFile, (int)instart, (int)inend, path, length_array, stats, store, h, costs, true); }
/* * Does a single run for ZopfliLZ77Optimal. For good compression, repeated runs * with updated statistics should be performed. * s: the block state * in: the input data array * instart: where to start * inend: where to stop (not inclusive) * path: pointer to dynamically allocated memory to store the path * pathsize: pointer to the size of the dynamic path array * length_array: array of size (inend - instart) used to store lengths * costmodel: function to use as the cost model for this squeeze run * costcontext: abstract context for the costmodel function * store: place to output the LZ77 data * returns the cost that was, according to the costmodel, needed to get to the end. * This is not the actual cost. */ static double LZ77OptimalRun(ZopfliBlockState s, byte[] InFile, int instart, int inend, List <ushort> path, ushort[] length_array, SymbolStats stats, ZopfliLZ77Store store, ZopfliHash h, float[] costs, bool fixedcosts) { double cost = GetBestLengths(s, InFile, instart, inend, stats, length_array, h, costs, fixedcosts); ulong pathsize = 0; path.Clear(); TraceBackwards((ulong)(inend - instart), length_array, path, ref pathsize); FollowPath(s, InFile, instart, inend, path, pathsize, store, h); Debug.Assert(cost < ZOPFLI_LARGE_FLOAT); return(cost); }
static void ZopfliLZ77Optimal(ZopfliBlockState s, byte[] InFile, int instart, int inend, int numiterations, ZopfliLZ77Store store) { /* Dist to get to here with smallest cost. */ int blocksize = inend - instart; ushort[] length_array = new ushort[blocksize + 1]; List <ushort> path = new List <ushort>(); ZopfliLZ77Store currentstore = new ZopfliLZ77Store(InFile); ZopfliHash h = new ZopfliHash(); SymbolStats stats = new SymbolStats(); SymbolStats beststats = new SymbolStats(); SymbolStats laststats = new SymbolStats(); int i; float[] costs = new float [blocksize + 1]; double cost; double bestcost = ZOPFLI_LARGE_FLOAT; double lastcost = 0; /* Try randomizing the costs a bit once the size stabilizes. */ RanState ran_state = new RanState(); int lastrandomstep = -1; /* Do regular deflate, then loop multiple shortest path runs, each time using * the statistics of the previous run. */ /* Initial run. */ ZopfliLZ77Greedy(s, InFile, instart, inend, currentstore, h); GetStatistics(currentstore, stats); /* Repeat statistics with each time the cost model from the previous stat * run. */ for (i = 0; i < numiterations; i++) { currentstore.ResetStore(InFile); LZ77OptimalRun(s, InFile, instart, inend, path, length_array, stats, currentstore, h, costs, false); cost = ZopfliCalculateBlockSize(currentstore, 0, currentstore.size, 2); if (Globals.verbose_more > 0 || (Globals.verbose > 0 && cost < bestcost)) { Console.WriteLine("Iteration " + i + ": " + cost + " bit"); } if (cost < bestcost) { /* Copy to the output store. */ ZopfliCopyLZ77Store(currentstore, store); CopyStats(stats, ref beststats); bestcost = cost; } CopyStats(stats, ref laststats); ClearStatFreqs(stats); GetStatistics(currentstore, stats); if (lastrandomstep != -1) { /* This makes it converge slower but better. Do it only once the * randomness kicks in so that if the user does few iterations, it gives a * better result sooner. */ AddWeighedStatFreqs(stats, 1.0, laststats, 0.5, stats); stats.CalculateStatistics(); } if (i > 5 && cost == lastcost) { CopyStats(beststats, ref stats); RandomizeStatFreqs(ran_state, stats); stats.CalculateStatistics(); lastrandomstep = i; } lastcost = cost; } }
static void FollowPath(ZopfliBlockState s, byte[] InFile, int instart, int inend, List <ushort> path, ulong pathsize, ZopfliLZ77Store store, ZopfliHash h) { int i, j, pos; int windowstart = instart > ZopfliHash.ZOPFLI_WINDOW_SIZE ? instart - ZopfliHash.ZOPFLI_WINDOW_SIZE : 0; ulong total_length_test = 0; if (instart == inend) { return; } h.ZopfliResetHash(); h.ZopfliWarmupHash(InFile, windowstart, inend); for (i = windowstart; i < instart; i++) { h.ZopfliUpdateHash(InFile, i, inend); } pos = instart; for (i = 0; i < (int)pathsize; i++) { ushort length = path[i]; ushort dummy_length = 0; ushort dist = 0; Debug.Assert(pos < inend); h.ZopfliUpdateHash(InFile, pos, inend); /* Add to output. */ if (length >= ZOPFLI_MIN_MATCH) { /* Get the distance by recalculating longest match. The found length * should match the length from the path. */ ZopfliFindLongestMatch(s, h, InFile, pos, inend, length, null, ref dist, ref dummy_length); Debug.Assert(!(dummy_length != length && length > 2 && dummy_length > 2)); ZopfliVerifyLenDist(InFile, inend, pos, dist, length); ZopfliStoreLitLenDist(length, dist, pos, store); total_length_test += length; } else { length = 1; ZopfliStoreLitLenDist(InFile[pos], 0, pos, store); total_length_test++; } Debug.Assert(pos + length <= inend); for (j = 1; j < length; j++) { h.ZopfliUpdateHash(InFile, pos + j, inend); } pos += length; } }
/* * Performs the forward pass for "squeeze". Gets the most optimal length to reach * every byte from a previous byte, using cost calculations. * s: the ZopfliBlockState * in: the input data array * instart: where to start * inend: where to stop (not inclusive) * costmodel: function to calculate the cost of some lit/len/dist pair. * costcontext: abstract context for the costmodel function * length_array: output array of size (inend - instart) which will receive the best * length to reach this byte from a previous byte. * returns the cost that was, according to the costmodel, needed to get to the end. */ static double GetBestLengths(ZopfliBlockState s, byte[] InFile, int instart, int inend, SymbolStats stats, ushort[] length_array, ZopfliHash h, float[] costs, bool fixedcosts) { /* Best cost to get here so far. */ ulong blocksize = (ulong)(inend - instart); ulong i, k, kend; ushort leng = 0; //bogus value ushort dist = 0; //bogusvalue ushort[] sublen = new ushort[259]; ulong windowstart = (ulong)(instart > ZopfliHash.ZOPFLI_WINDOW_SIZE ? instart - ZopfliHash.ZOPFLI_WINDOW_SIZE : 0); double result; double mincost = GetCostModelMinCost(stats); double mincostaddcostj; if (instart == inend) { return(0); } h.ZopfliResetHash(); h.ZopfliWarmupHash(InFile, (int)windowstart, inend); for (i = windowstart; i < (ulong)instart; i++) { h.ZopfliUpdateHash(InFile, (int)i, inend); } Array.Fill(costs, (float)Compress.ZOPFLI_LARGE_FLOAT); costs[0] = 0; /* Because it's the start. */ length_array[0] = 0; for (i = (ulong)instart; i < (ulong)inend; i++) { ulong j = i - (ulong)instart; /* Index in the costs array and length_array. */ h.ZopfliUpdateHash(InFile, (int)i, inend); /* If we're in a long repetition of the same character and have more than * ZOPFLI_MAX_MATCH characters before and after our position. */ if (h.same[i & ZopfliHash.ZOPFLI_WINDOW_MASK] > ZOPFLI_MAX_MATCH * 2 && (int)i > instart + ZOPFLI_MAX_MATCH + 1 && i + ZOPFLI_MAX_MATCH * 2 + 1 < (ulong)inend && h.same[(i - ZOPFLI_MAX_MATCH) & ZopfliHash.ZOPFLI_WINDOW_MASK] > ZOPFLI_MAX_MATCH) { double symbolcost; if (fixedcosts) { symbolcost = GetCostFixed(ZOPFLI_MAX_MATCH, 1); } else { symbolcost = GetCostStat(ZOPFLI_MAX_MATCH, 1, stats); } /* Set the length to reach each one to ZOPFLI_MAX_MATCH, and the cost to * the cost corresponding to that length. Doing this, we skip * ZOPFLI_MAX_MATCH values to avoid calling ZopfliFindLongestMatch. */ for (k = 0; k < ZOPFLI_MAX_MATCH; k++) { costs[j + ZOPFLI_MAX_MATCH] = costs[j] + (float)symbolcost; length_array[j + ZOPFLI_MAX_MATCH] = ZOPFLI_MAX_MATCH; i++; j++; h.ZopfliUpdateHash(InFile, (int)i, inend); } } ZopfliFindLongestMatch(s, h, InFile, (int)i, inend, ZOPFLI_MAX_MATCH, sublen, ref dist, ref leng); /* Literal. */ if (i + 1 <= (ulong)inend) { double newCost; if (fixedcosts) { newCost = GetCostFixed(InFile[i], 0) + costs[j]; } else { newCost = GetCostStat(InFile[i], 0, stats) + costs[j]; } Debug.Assert(newCost >= 0); if (newCost < costs[j + 1]) { costs[j + 1] = (float)newCost; length_array[j + 1] = 1; } } /* Lengths. */ kend = zopfli_min(leng, (ulong)inend - i); mincostaddcostj = mincost + costs[j]; for (k = 3; k <= kend; k++) { double newCost; /* Calling the cost model is expensive, avoid this if we are already at * the minimum possible cost that it can return. */ if (costs[j + k] <= mincostaddcostj) { continue; } if (fixedcosts) { newCost = GetCostFixed((int)k, sublen[k]) + costs[j]; } else { newCost = GetCostStat((int)k, sublen[k], stats) + costs[j]; } Debug.Assert(newCost >= 0); if (newCost < costs[j + k]) { Debug.Assert(k <= ZOPFLI_MAX_MATCH); costs[j + k] = (float)newCost; length_array[j + k] = (ushort)k; } } } Debug.Assert(costs[blocksize] >= 0); result = costs[blocksize]; return(result); }