1
0
mirror of https://frontier.innolan.net/github/AmigaExamples.git synced 2026-05-10 08:57:26 +00:00
Files
AmigaExamples/tools/external/shrinkler/LZDecoder.h
2016-03-15 16:25:03 +11:00

75 lines
1.7 KiB
C++

// Copyright 1999-2015 Aske Simon Christensen. See LICENSE.txt for usage terms.
/*
Decoder for the LZ encoder.
*/
#pragma once
#include "Decoder.h"
#include "LZEncoder.h"
class LZReceiver {
public:
virtual bool receiveLiteral(unsigned char value) = 0;
virtual bool receiveReference(int offset, int length) = 0;
virtual ~LZReceiver() {}
};
class LZDecoder {
Decoder *decoder;
int decode(int context) const {
return decoder->decode(LZEncoder::NUM_SINGLE_CONTEXTS + context);
}
int decodeNumber(int context_group) const {
return decoder->decodeNumber(LZEncoder::NUM_SINGLE_CONTEXTS + (context_group << 8));
}
public:
LZDecoder(Decoder *decoder) : decoder(decoder) {
}
bool decode(LZReceiver& receiver) {
bool ref = false;
bool prev_was_ref = false;
int pos = 0;
int offset = 0;
do {
if (ref) {
bool repeated = false;
if (!prev_was_ref) {
repeated = decode(LZEncoder::CONTEXT_REPEATED);
}
if (!repeated) {
offset = decodeNumber(LZEncoder::CONTEXT_GROUP_OFFSET) - 2;
if (offset == 0) break;
}
int length = decodeNumber(LZEncoder::CONTEXT_GROUP_LENGTH);
if (!receiver.receiveReference(offset, length)) return false;
pos += length;
prev_was_ref = true;
} else {
int parity = pos & 1;
int context = 1;
for (int i = 7 ; i >= 0 ; i--) {
int bit = decode((parity << 8) | context);
context = (context << 1) | bit;
}
unsigned char lit = context;
if (!receiver.receiveLiteral(lit)) return false;
pos += 1;
prev_was_ref = false;
}
int parity = pos & 1;
ref = decode(LZEncoder::CONTEXT_KIND + (parity << 8));
} while (true);
return true;
}
};