chore: Remove miniz and modularise inflation logic (#1073)
## Summary * Remove miniz and move completely to uzlib * Move uzlib interfacing to InflateReader to better modularise inflation code --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? Yes, Claude helped with the extraction and refactor
This commit is contained in:
78
lib/InflateReader/InflateReader.cpp
Normal file
78
lib/InflateReader/InflateReader.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#include "InflateReader.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
|
||||
namespace {
|
||||
constexpr size_t INFLATE_DICT_SIZE = 32768;
|
||||
}
|
||||
|
||||
// Guarantee the cast pattern in the header comment is valid.
|
||||
static_assert(std::is_standard_layout<InflateReader>::value,
|
||||
"InflateReader must be standard-layout for the uzlib callback cast to work");
|
||||
|
||||
InflateReader::~InflateReader() { deinit(); }
|
||||
|
||||
bool InflateReader::init(const bool streaming) {
|
||||
deinit(); // free any previously allocated ring buffer and reset state
|
||||
|
||||
if (streaming) {
|
||||
ringBuffer = static_cast<uint8_t*>(malloc(INFLATE_DICT_SIZE));
|
||||
if (!ringBuffer) return false;
|
||||
memset(ringBuffer, 0, INFLATE_DICT_SIZE);
|
||||
}
|
||||
|
||||
uzlib_uncompress_init(&decomp, ringBuffer, ringBuffer ? INFLATE_DICT_SIZE : 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void InflateReader::deinit() {
|
||||
if (ringBuffer) {
|
||||
free(ringBuffer);
|
||||
ringBuffer = nullptr;
|
||||
}
|
||||
memset(&decomp, 0, sizeof(decomp));
|
||||
}
|
||||
|
||||
void InflateReader::setSource(const uint8_t* src, size_t len) {
|
||||
decomp.source = src;
|
||||
decomp.source_limit = src + len;
|
||||
}
|
||||
|
||||
void InflateReader::setReadCallback(int (*cb)(struct uzlib_uncomp*)) { decomp.source_read_cb = cb; }
|
||||
|
||||
void InflateReader::skipZlibHeader() {
|
||||
uzlib_get_byte(&decomp);
|
||||
uzlib_get_byte(&decomp);
|
||||
}
|
||||
|
||||
bool InflateReader::read(uint8_t* dest, size_t len) {
|
||||
if (!ringBuffer) {
|
||||
// One-shot mode: back-references use absolute offset from dest_start.
|
||||
// Valid only when read() is called once with the full output buffer.
|
||||
decomp.dest_start = dest;
|
||||
}
|
||||
decomp.dest = dest;
|
||||
decomp.dest_limit = dest + len;
|
||||
|
||||
const int res = uzlib_uncompress(&decomp);
|
||||
if (res < 0) return false;
|
||||
return decomp.dest == decomp.dest_limit;
|
||||
}
|
||||
|
||||
InflateStatus InflateReader::readAtMost(uint8_t* dest, size_t maxLen, size_t* produced) {
|
||||
if (!ringBuffer) {
|
||||
// One-shot mode: back-references use absolute offset from dest_start.
|
||||
// Valid only when readAtMost() is called once with the full output buffer.
|
||||
decomp.dest_start = dest;
|
||||
}
|
||||
decomp.dest = dest;
|
||||
decomp.dest_limit = dest + maxLen;
|
||||
|
||||
const int res = uzlib_uncompress(&decomp);
|
||||
*produced = static_cast<size_t>(decomp.dest - dest);
|
||||
|
||||
if (res == TINF_DONE) return InflateStatus::Done;
|
||||
if (res < 0) return InflateStatus::Error;
|
||||
return InflateStatus::Ok;
|
||||
}
|
||||
85
lib/InflateReader/InflateReader.h
Normal file
85
lib/InflateReader/InflateReader.h
Normal file
@@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
#include <uzlib.h>
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
// Return value for readAtMost().
|
||||
enum class InflateStatus {
|
||||
Ok, // Output buffer full; more compressed data remains.
|
||||
Done, // Stream ended cleanly (TINF_DONE). produced may be < maxLen.
|
||||
Error, // Decompression failed.
|
||||
};
|
||||
|
||||
// Streaming deflate decompressor wrapping uzlib.
|
||||
//
|
||||
// Two modes:
|
||||
// init(false) — one-shot: input is a contiguous buffer, call read() once.
|
||||
// init(true) — streaming: allocates a 32KB ring buffer for back-references
|
||||
// across multiple read() / readAtMost() calls.
|
||||
//
|
||||
// Streaming callback pattern:
|
||||
// The uzlib read callback receives a `struct uzlib_uncomp*` with no separate
|
||||
// context pointer. To attach context, make InflateReader the *first member* of
|
||||
// your context struct, then cast inside the callback:
|
||||
//
|
||||
// struct MyCtx {
|
||||
// InflateReader reader; // must be first
|
||||
// FsFile* file;
|
||||
// // ...
|
||||
// };
|
||||
// static int myCb(struct uzlib_uncomp* u) {
|
||||
// MyCtx* ctx = reinterpret_cast<MyCtx*>(u); // valid: reader.decomp is at offset 0
|
||||
// // ... fill u->source / u->source_limit, return first byte
|
||||
// }
|
||||
// MyCtx ctx;
|
||||
// ctx.reader.init(true);
|
||||
// ctx.reader.setReadCallback(myCb);
|
||||
//
|
||||
class InflateReader {
|
||||
public:
|
||||
InflateReader() = default;
|
||||
~InflateReader();
|
||||
|
||||
InflateReader(const InflateReader&) = delete;
|
||||
InflateReader& operator=(const InflateReader&) = delete;
|
||||
|
||||
// Initialise decompressor. streaming=true allocates a 32KB ring buffer needed
|
||||
// when read() or readAtMost() will be called multiple times.
|
||||
// Returns false only in streaming mode if the ring buffer allocation fails.
|
||||
bool init(bool streaming = false);
|
||||
|
||||
// Release the ring buffer and reset internal state.
|
||||
void deinit();
|
||||
|
||||
// Set the entire compressed input as a contiguous memory buffer.
|
||||
// Used in one-shot mode; not needed when a read callback is set.
|
||||
void setSource(const uint8_t* src, size_t len);
|
||||
|
||||
// Set a uzlib-compatible read callback for streaming input.
|
||||
// See class-level comment for the expected callback/context struct pattern.
|
||||
void setReadCallback(int (*cb)(uzlib_uncomp*));
|
||||
|
||||
// Consume the 2-byte zlib header (CMF + FLG) from the input stream.
|
||||
// Call this once before the first read() when input is zlib-wrapped (e.g. PNG IDAT).
|
||||
void skipZlibHeader();
|
||||
|
||||
// Decompress exactly len bytes into dest.
|
||||
// Returns false if the stream ends before producing len bytes, or on error.
|
||||
bool read(uint8_t* dest, size_t len);
|
||||
|
||||
// Decompress up to maxLen bytes into dest.
|
||||
// Sets *produced to the number of bytes written.
|
||||
// Returns Done when the stream ends cleanly, Ok when there is more to read,
|
||||
// and Error on failure.
|
||||
InflateStatus readAtMost(uint8_t* dest, size_t maxLen, size_t* produced);
|
||||
|
||||
// Returns a pointer to the underlying TINF_DATA.
|
||||
// Useful for advanced streaming setups where the callback needs access to the
|
||||
// uzlib struct directly (e.g. updating source/source_limit).
|
||||
uzlib_uncomp* raw() { return &decomp; }
|
||||
|
||||
private:
|
||||
uzlib_uncomp decomp = {};
|
||||
uint8_t* ringBuffer = nullptr;
|
||||
};
|
||||
Reference in New Issue
Block a user