Compress-Zopfli
view release on metacpan or search on metacpan
zopflib/src/zopfli/squeeze.c view on Meta::CPAN
}
}
/* Calculates the entropy of the statistics */
static void CalculateStatistics(SymbolStats* stats) {
ZopfliCalculateEntropy(stats->litlens, ZOPFLI_NUM_LL, stats->ll_symbols);
ZopfliCalculateEntropy(stats->dists, ZOPFLI_NUM_D, stats->d_symbols);
}
/* Appends the symbol statistics from the store. */
static void GetStatistics(const ZopfliLZ77Store* store, SymbolStats* stats) {
size_t i;
for (i = 0; i < store->size; i++) {
if (store->dists[i] == 0) {
stats->litlens[store->litlens[i]]++;
} else {
stats->litlens[ZopfliGetLengthSymbol(store->litlens[i])]++;
stats->dists[ZopfliGetDistSymbol(store->dists[i])]++;
}
}
stats->litlens[256] = 1; /* End symbol. */
CalculateStatistics(stats);
}
/*
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,
const unsigned char* in, size_t instart, size_t inend,
unsigned short** path, size_t* pathsize,
unsigned short* length_array, CostModelFun* costmodel,
void* costcontext, ZopfliLZ77Store* store,
ZopfliHash* h, float* costs) {
double cost = GetBestLengths(s, in, instart, inend, costmodel,
costcontext, length_array, h, costs);
free(*path);
*path = 0;
*pathsize = 0;
TraceBackwards(inend - instart, length_array, path, pathsize);
FollowPath(s, in, instart, inend, *path, *pathsize, store, h);
assert(cost < ZOPFLI_LARGE_FLOAT);
return cost;
}
void ZopfliLZ77Optimal(ZopfliBlockState *s,
const unsigned char* in, size_t instart, size_t inend,
int numiterations,
ZopfliLZ77Store* store) {
/* Dist to get to here with smallest cost. */
size_t blocksize = inend - instart;
unsigned short* length_array =
(unsigned short*)malloc(sizeof(unsigned short) * (blocksize + 1));
unsigned short* path = 0;
size_t pathsize = 0;
ZopfliLZ77Store currentstore;
ZopfliHash hash;
ZopfliHash* h = &hash;
SymbolStats stats, beststats, laststats;
int i;
float* costs = (float*)malloc(sizeof(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;
int lastrandomstep = -1;
if (!costs) exit(-1); /* Allocation failed. */
if (!length_array) exit(-1); /* Allocation failed. */
InitRanState(&ran_state);
InitStats(&stats);
ZopfliInitLZ77Store(in, ¤tstore);
ZopfliAllocHash(ZOPFLI_WINDOW_SIZE, h);
/* Do regular deflate, then loop multiple shortest path runs, each time using
the statistics of the previous run. */
/* Initial run. */
ZopfliLZ77Greedy(s, in, instart, inend, ¤tstore, h);
GetStatistics(¤tstore, &stats);
/* Repeat statistics with each time the cost model from the previous stat
run. */
for (i = 0; i < numiterations; i++) {
ZopfliCleanLZ77Store(¤tstore);
ZopfliInitLZ77Store(in, ¤tstore);
LZ77OptimalRun(s, in, instart, inend, &path, &pathsize,
length_array, GetCostStat, (void*)&stats,
¤tstore, h, costs);
cost = ZopfliCalculateBlockSize(¤tstore, 0, currentstore.size, 2);
if (s->options->verbose_more || (s->options->verbose && cost < bestcost)) {
fprintf(stderr, "Iteration %d: %d bit\n", i, (int) cost);
}
if (cost < bestcost) {
/* Copy to the output store. */
ZopfliCopyLZ77Store(¤tstore, store);
CopyStats(&stats, &beststats);
bestcost = cost;
}
CopyStats(&stats, &laststats);
ClearStatFreqs(&stats);
GetStatistics(¤tstore, &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);
CalculateStatistics(&stats);
}
if (i > 5 && cost == lastcost) {
CopyStats(&beststats, &stats);
RandomizeStatFreqs(&ran_state, &stats);
CalculateStatistics(&stats);
lastrandomstep = i;
}
lastcost = cost;
}
free(length_array);
free(path);
free(costs);
ZopfliCleanLZ77Store(¤tstore);
ZopfliCleanHash(h);
}
void ZopfliLZ77OptimalFixed(ZopfliBlockState *s,
const unsigned char* in,
size_t instart, size_t inend,
ZopfliLZ77Store* store)
{
/* Dist to get to here with smallest cost. */
size_t blocksize = inend - instart;
unsigned short* length_array =
(unsigned short*)malloc(sizeof(unsigned short) * (blocksize + 1));
unsigned short* path = 0;
size_t pathsize = 0;
ZopfliHash hash;
ZopfliHash* h = &hash;
float* costs = (float*)malloc(sizeof(float) * (blocksize + 1));
if (!costs) exit(-1); /* Allocation failed. */
if (!length_array) exit(-1); /* Allocation failed. */
ZopfliAllocHash(ZOPFLI_WINDOW_SIZE, h);
s->blockstart = instart;
s->blockend = 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, in, instart, inend, &path, &pathsize,
length_array, GetCostFixed, 0, store, h, costs);
free(length_array);
free(path);
free(costs);
ZopfliCleanHash(h);
}
( run in 0.763 second using v1.01-cache-2.11-cpan-71847e10f99 )