Files
crosspoint-reader-mod/lib/InflateReader/InflateReader.h
Dave Allie ecb5b1b4e5 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
2026-02-22 21:38:03 +11:00

86 lines
3.0 KiB
C++

#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;
};