mirror of
https://frontier.innolan.net/github/AmigaExamples.git
synced 2026-09-12 13:21:57 +00:00
134 lines
3.4 KiB
C++
134 lines
3.4 KiB
C++
// Copyright 1999-2015 Aske Simon Christensen. See LICENSE.txt for usage terms.
|
|
|
|
/*
|
|
|
|
Pack a data block in multiple iterations, reporting progress along the way.
|
|
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "RangeCoder.h"
|
|
#include "MatchFinder.h"
|
|
#include "CountingCoder.h"
|
|
#include "SizeMeasuringCoder.h"
|
|
#include "LZEncoder.h"
|
|
#include "LZParser.h"
|
|
|
|
struct PackParams {
|
|
int iterations;
|
|
int length_margin;
|
|
int skip_length;
|
|
int match_patience;
|
|
int max_same_length;
|
|
};
|
|
|
|
class PackProgress : public LZProgress {
|
|
int size;
|
|
int steps;
|
|
int next_step_threshold;
|
|
int textlength;
|
|
|
|
void print() {
|
|
textlength = printf("[%d.%d%%]", steps / 10, steps % 10);
|
|
fflush(stdout);
|
|
}
|
|
|
|
void rewind() {
|
|
printf("\033[%dD", textlength);
|
|
}
|
|
public:
|
|
virtual void begin(int size) {
|
|
this->size = size;
|
|
steps = 0;
|
|
next_step_threshold = size / 1000;
|
|
print();
|
|
}
|
|
|
|
virtual void update(int pos) {
|
|
if (pos < next_step_threshold) return;
|
|
while (pos >= next_step_threshold) {
|
|
steps += 1;
|
|
next_step_threshold = (long long) size * (steps + 1) / 1000;
|
|
}
|
|
rewind();
|
|
print();
|
|
}
|
|
|
|
virtual void end() {
|
|
rewind();
|
|
printf("\033[K");
|
|
fflush(stdout);
|
|
}
|
|
};
|
|
|
|
class NoProgress : public LZProgress {
|
|
public:
|
|
virtual void begin(int size) {
|
|
fflush(stdout);
|
|
}
|
|
|
|
virtual void update(int pos) {
|
|
}
|
|
|
|
virtual void end() {
|
|
}
|
|
};
|
|
|
|
void packData(unsigned char *data, int data_length, int zero_padding, PackParams *params, Coder *result_coder, RefEdgeFactory *edge_factory, bool show_progress) {
|
|
MatchFinder finder(data, data_length, 2, params->match_patience, params->max_same_length);
|
|
LZParser parser(data, data_length, zero_padding, finder, params->length_margin, params->skip_length, edge_factory);
|
|
int real_size = 0;
|
|
int best_size = 999999999;
|
|
int best_result = 0;
|
|
vector<LZParseResult> results(2);
|
|
CountingCoder *counting_coder = new CountingCoder(LZEncoder::NUM_CONTEXTS);
|
|
LZProgress *progress;
|
|
if (show_progress) {
|
|
progress = new PackProgress();
|
|
} else {
|
|
progress = new NoProgress();
|
|
}
|
|
printf("%8d", data_length);
|
|
for (int i = 0 ; i < params->iterations ; i++) {
|
|
printf(" ");
|
|
|
|
// Parse data into LZ symbols
|
|
LZParseResult& result = results[1 - best_result];
|
|
Coder *measurer = new SizeMeasuringCoder(counting_coder);
|
|
measurer->setNumberContexts(LZEncoder::NUMBER_CONTEXT_OFFSET, LZEncoder::NUM_NUMBER_CONTEXTS, data_length);
|
|
finder.reset();
|
|
result = parser.parse(LZEncoder(measurer), progress);
|
|
|
|
// Encode result using adaptive range coding
|
|
vector<unsigned> dummy_result;
|
|
RangeCoder *range_coder = new RangeCoder(LZEncoder::NUM_CONTEXTS, dummy_result);
|
|
real_size = result.encode(LZEncoder(range_coder));
|
|
range_coder->finish();
|
|
delete range_coder;
|
|
|
|
// Choose if best
|
|
if (real_size < best_size) {
|
|
best_result = 1 - best_result;
|
|
best_size = real_size;
|
|
}
|
|
|
|
// Print size
|
|
printf("%14.3f", real_size / (double) (8 << Coder::BIT_PRECISION));
|
|
|
|
// Count symbol frequencies
|
|
CountingCoder *new_counting_coder = new CountingCoder(LZEncoder::NUM_CONTEXTS);
|
|
result.encode(LZEncoder(counting_coder));
|
|
|
|
// New size measurer based on frequencies
|
|
CountingCoder *old_counting_coder = counting_coder;
|
|
counting_coder = new CountingCoder(old_counting_coder, new_counting_coder);
|
|
delete old_counting_coder;
|
|
delete new_counting_coder;
|
|
}
|
|
delete progress;
|
|
delete counting_coder;
|
|
|
|
results[best_result].encode(LZEncoder(result_coder));
|
|
}
|