Improve LZ4 decompression
This commit is contained in:
parent
da2bfa77d8
commit
9c77967c7f
|
@ -49,13 +49,6 @@ namespace JabyEngine {
|
|||
|
||||
static bool obtain_any_length(ArrayRange<const uint8_t>& data, uint32_t &dst_length);
|
||||
|
||||
pair<bool, Result> read_token(ArrayRange<const uint8_t>& data);
|
||||
pair<bool, Result> obtain_literal_length(ArrayRange<const uint8_t>& data);
|
||||
pair<bool, Result> copy_literals(ArrayRange<const uint8_t>& data);
|
||||
pair<bool, Result> obtain_match_offset(ArrayRange<const uint8_t>& data);
|
||||
pair<bool, Result> obtain_match_length(ArrayRange<const uint8_t>& data);
|
||||
pair<bool, Result> copy_match(ArrayRange<const uint8_t>& data);
|
||||
|
||||
public:
|
||||
LZ4Decompressor() = default;
|
||||
LZ4Decompressor(uint8_t* dst_adr) : LZ4Decompressor() {
|
||||
|
@ -65,7 +58,7 @@ namespace JabyEngine {
|
|||
void setup(uint8_t* dst_adr);
|
||||
void reset();
|
||||
|
||||
Result process(ArrayRange<const uint8_t> data);
|
||||
Result process(ArrayRange<const uint8_t> data, bool is_last);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#include <PSX/Auxiliary/lz4_decompressor.hpp>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
namespace JabyEngine {
|
||||
static void memcpy(uint8_t* &dst, ArrayRange<const uint8_t> &src, size_t size) {
|
||||
for(size_t n = 0; n < size; n++) {
|
||||
|
@ -23,130 +21,6 @@ namespace JabyEngine {
|
|||
return false;
|
||||
}
|
||||
|
||||
pair<bool, LZ4Decompressor::Result> LZ4Decompressor :: read_token(ArrayRange<const uint8_t>& data) {
|
||||
if(data) {
|
||||
const auto token = data.pop();
|
||||
|
||||
/*if(token == 0) {
|
||||
return {false, Result::new_done(0)};
|
||||
}*/
|
||||
|
||||
this->state.literal_length = (token & 0xF0) >> 4;
|
||||
this->state.match_length = (token & 0x0F);
|
||||
|
||||
printf("LiteralLength: %llu MatchLength: %llu\n", this->state.literal_length, this->state.match_length);
|
||||
|
||||
if(this->state.literal_length == 15) {
|
||||
this->state.step = State::Step::ObtainLiteralLength;
|
||||
}
|
||||
|
||||
else if(this->state.literal_length == 0) {
|
||||
this->state.step = State::Step::ObtainMatchOffset;
|
||||
}
|
||||
|
||||
else {
|
||||
this->state.step = State::Step::CopyLiterals;
|
||||
}
|
||||
return {true, Result::new_in_progress(0)};
|
||||
}
|
||||
|
||||
return {false, Result::new_in_progress(0)};
|
||||
}
|
||||
|
||||
pair<bool, LZ4Decompressor::Result> LZ4Decompressor :: obtain_literal_length(ArrayRange<const uint8_t>& data) {
|
||||
if(LZ4Decompressor::obtain_any_length(data, this->state.literal_length)) {
|
||||
printf("New LiteralLength: %llu\n", this->state.literal_length);
|
||||
this->state.step = State::Step::CopyLiterals;
|
||||
return {true, Result::new_in_progress(0)};
|
||||
}
|
||||
|
||||
return {false, Result::new_in_progress(0)};
|
||||
}
|
||||
|
||||
pair<bool, LZ4Decompressor::Result> LZ4Decompressor :: copy_literals(ArrayRange<const uint8_t>& data) {
|
||||
if(data) {
|
||||
const auto bytes_copy = (this->state.literal_length > data.size) ? data.size : this->state.literal_length;
|
||||
|
||||
printf("Copy %llu bytes of literal\n", bytes_copy);
|
||||
memcpy(this->dst_adr, data, bytes_copy);
|
||||
|
||||
this->state.literal_length -= bytes_copy;
|
||||
if(this->state.literal_length == 0) {
|
||||
this->state.step = State::Step::ObtainMatchOffset;
|
||||
return {true, Result::new_in_progress(bytes_copy)};
|
||||
}
|
||||
}
|
||||
|
||||
return {false, Result::new_in_progress(0)};
|
||||
}
|
||||
|
||||
pair<bool, LZ4Decompressor::Result> LZ4Decompressor :: obtain_match_offset(ArrayRange<const uint8_t>& data) {
|
||||
static const auto state_complete = [](State& state) -> pair<bool, Result> {
|
||||
if(state.match_length == 15) {
|
||||
state.step = State::Step::ObtainMatchLength;
|
||||
}
|
||||
|
||||
else {
|
||||
state.step = State::Step::CopyMatch;
|
||||
}
|
||||
|
||||
printf("New MatchOffset: %llu\n", state.match_offset);
|
||||
return {true, Result::new_in_progress(0)};
|
||||
};
|
||||
|
||||
if(data) {
|
||||
if(this->state.match_offset == 0xFFFF) {
|
||||
// We are unused and invalid
|
||||
if(data.size >= sizeof(uint16_t)) {
|
||||
// We can read all
|
||||
this->state.match_offset = *reinterpret_cast<const uint16_t*>(data.start);
|
||||
data.skip(sizeof(uint16_t));
|
||||
|
||||
return state_complete(this->state);
|
||||
}
|
||||
|
||||
else {
|
||||
this->state.match_offset = static_cast<uint16_t>(data.pop());
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
this->state.match_offset |= (static_cast<uint16_t>(data.pop()) << 8);
|
||||
return state_complete(this->state);
|
||||
}
|
||||
}
|
||||
|
||||
return {false, Result::new_in_progress(0)};
|
||||
}
|
||||
|
||||
pair<bool, LZ4Decompressor::Result> LZ4Decompressor :: obtain_match_length(ArrayRange<const uint8_t>& data) {
|
||||
if(LZ4Decompressor::obtain_any_length(data, this->state.match_length)) {
|
||||
this->state.step = State::Step::CopyMatch;
|
||||
|
||||
printf("New match length: %llu\n", this->state.match_length);
|
||||
return {true, Result::new_in_progress(0)};
|
||||
}
|
||||
|
||||
return {false, Result::new_in_progress(0)};
|
||||
}
|
||||
|
||||
pair<bool, LZ4Decompressor::Result> LZ4Decompressor :: copy_match(ArrayRange<const uint8_t>& data) {
|
||||
static constexpr size_t min_match_length = 4;
|
||||
|
||||
this->state.match_length += min_match_length;
|
||||
|
||||
const uint8_t* src = this->dst_adr - this->state.match_offset;
|
||||
ArrayRange src_data(src, this->state.match_length);
|
||||
|
||||
memcpy(this->dst_adr, src_data, this->state.match_length);
|
||||
|
||||
this->state.match_offset = 0xFFFF;
|
||||
this->state.step = State::Step::ReadToken;
|
||||
|
||||
const bool keep_going = data;
|
||||
return {keep_going, data ? Result::new_in_progress(this->state.match_length) : Result::new_done(this->state.match_length)};
|
||||
}
|
||||
|
||||
void LZ4Decompressor :: setup(uint8_t* dst_adr) {
|
||||
this->dst_adr = dst_adr;
|
||||
LZ4Decompressor::reset();
|
||||
|
@ -156,47 +30,117 @@ namespace JabyEngine {
|
|||
this->state = State();
|
||||
}
|
||||
|
||||
LZ4Decompressor::Result LZ4Decompressor :: process(ArrayRange<const uint8_t> data) {
|
||||
const auto do_call = [this](ArrayRange<const uint8_t>& data, size_t bytes_ready) -> pair<bool, Result> {
|
||||
LZ4Decompressor::Result LZ4Decompressor :: process(ArrayRange<const uint8_t> data, bool is_last) {
|
||||
size_t bytes_ready = 0;
|
||||
|
||||
while(data) {
|
||||
switch(this->state.step) {
|
||||
case State::Step::ReadToken:
|
||||
printf("Read Token! %llu bytes left (%llu bytes ready)\n", data.size, bytes_ready);
|
||||
return LZ4Decompressor::read_token(data);
|
||||
case State::Step::ReadToken: {
|
||||
const auto token = data.pop();
|
||||
|
||||
case State::Step::ObtainLiteralLength:
|
||||
printf("Obtain literal length! %llu bytes left (%llu bytes ready)\n", data.size, bytes_ready);
|
||||
return LZ4Decompressor::obtain_literal_length(data);
|
||||
this->state.literal_length = (token & 0xF0) >> 4;
|
||||
this->state.match_length = (token & 0x0F);
|
||||
|
||||
case State::Step::CopyLiterals:
|
||||
printf("Copy Literals! %llu bytes left (%llu bytes ready)\n", data.size, bytes_ready);
|
||||
return LZ4Decompressor::copy_literals(data);
|
||||
if(this->state.literal_length == 15) {
|
||||
this->state.step = State::Step::ObtainLiteralLength;
|
||||
}
|
||||
|
||||
case State::Step::ObtainMatchOffset:
|
||||
printf("Obtain match offset! %llu bytes left (%llu bytes ready)\n", data.size, bytes_ready);
|
||||
return LZ4Decompressor::obtain_match_offset(data);
|
||||
else if(this->state.literal_length == 0) {
|
||||
this->state.step = State::Step::ObtainMatchOffset;
|
||||
}
|
||||
|
||||
case State::Step::ObtainMatchLength:
|
||||
printf("Obtain match length! %llu bytes left (%llu bytes ready)\n", data.size, bytes_ready);
|
||||
return LZ4Decompressor::obtain_match_length(data);
|
||||
else {
|
||||
this->state.step = State::Step::CopyLiterals;
|
||||
}
|
||||
} break;
|
||||
|
||||
case State::Step::CopyMatch:
|
||||
printf("Copy match! %llu bytes left\n", data.size);
|
||||
return LZ4Decompressor::copy_match(data);
|
||||
case State::Step::ObtainLiteralLength: {
|
||||
if(LZ4Decompressor::obtain_any_length(data, this->state.literal_length)) {
|
||||
this->state.step = State::Step::CopyLiterals;
|
||||
}
|
||||
} break;
|
||||
|
||||
case State::Step::CopyLiterals: {
|
||||
const auto bytes_copy = (this->state.literal_length > data.size) ? data.size : this->state.literal_length;
|
||||
|
||||
memcpy(this->dst_adr, data, bytes_copy);
|
||||
|
||||
this->state.literal_length -= bytes_copy;
|
||||
if(this->state.literal_length == 0) {
|
||||
this->state.step = State::Step::ObtainMatchOffset;
|
||||
bytes_ready += bytes_copy;
|
||||
}
|
||||
} break;
|
||||
|
||||
case State::Step::ObtainMatchOffset: {
|
||||
static const auto state_complete = [](State& state) -> bool {
|
||||
if(state.match_offset == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
else {
|
||||
if(state.match_length == 15) {
|
||||
state.step = State::Step::ObtainMatchLength;
|
||||
}
|
||||
|
||||
else {
|
||||
state.step = State::Step::CopyMatch;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
if(this->state.match_offset == 0xFFFF) {
|
||||
// We are unused and invalid
|
||||
if(data.size >= sizeof(uint16_t)) {
|
||||
// We can read all
|
||||
this->state.match_offset = *reinterpret_cast<const uint16_t*>(data.start);
|
||||
data.skip(sizeof(uint16_t));
|
||||
|
||||
if(!state_complete(this->state)) {
|
||||
return Result::new_done(bytes_ready);
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
this->state.match_offset = static_cast<uint16_t>(data.pop());
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
this->state.match_offset |= (static_cast<uint16_t>(data.pop()) << 8);
|
||||
if(!state_complete(this->state)) {
|
||||
return Result::new_done(bytes_ready);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case State::Step::ObtainMatchLength: {
|
||||
if(LZ4Decompressor::obtain_any_length(data, this->state.match_length)) {
|
||||
this->state.step = State::Step::CopyMatch;
|
||||
}
|
||||
} break;
|
||||
|
||||
case State::Step::CopyMatch: {
|
||||
static constexpr size_t min_match_length = 4;
|
||||
|
||||
this->state.match_length += min_match_length;
|
||||
|
||||
const uint8_t* src = this->dst_adr - this->state.match_offset;
|
||||
ArrayRange src_data(src, this->state.match_length);
|
||||
|
||||
memcpy(this->dst_adr, src_data, this->state.match_length);
|
||||
|
||||
this->state.match_offset = 0xFFFF;
|
||||
this->state.step = State::Step::ReadToken;
|
||||
|
||||
bytes_ready += this->state.match_length;
|
||||
} break;
|
||||
|
||||
default:
|
||||
return {false, Result::new_error()};
|
||||
}
|
||||
};
|
||||
|
||||
size_t bytes_ready = 0;
|
||||
while(true) {
|
||||
auto [keep_going, result] = do_call(data, bytes_ready);
|
||||
|
||||
bytes_ready += result.bytes_ready;
|
||||
if(!keep_going) {
|
||||
result.bytes_ready = bytes_ready;
|
||||
return result;
|
||||
return Result::new_error();
|
||||
}
|
||||
}
|
||||
return is_last ? Result::new_done(bytes_ready) : Result::new_in_progress(bytes_ready);
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@ namespace JabyEngine {
|
|||
static size_t decompress_logo() {
|
||||
LZ4Decompressor lz4_decomp(reinterpret_cast<uint8_t*>(&__boot_loader_end));
|
||||
|
||||
const auto [progress, bytes_ready] = lz4_decomp.process(ArrayRange(SplashScreen, sizeof(SplashScreen)));
|
||||
const auto [progress, bytes_ready] = lz4_decomp.process(ArrayRange(SplashScreen, sizeof(SplashScreen)), true);
|
||||
switch(progress) {
|
||||
case Progress::InProgress:
|
||||
printf("Decompressing still in progress... %llu\n", bytes_ready);
|
||||
|
|
Loading…
Reference in New Issue