96 lines
4.0 KiB
C++
96 lines
4.0 KiB
C++
#pragma once
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
|
|
// Forward declaration keeps miniz out of consumer translation units; the
|
|
// decompressor state is heap-allocated in the .cpp where the type is complete.
|
|
struct tinfl_decompressor_tag;
|
|
|
|
// Streaming deflate decompressor wrapping miniz's tinfl.
|
|
//
|
|
// Replaces the uzlib-backed InflateReader on the throughput paths (EPUB zip
|
|
// entries, PNG IDAT). tinfl decodes via lookup tables where uzlib walks the
|
|
// Huffman tree bit-by-bit -- several times faster on this CPU -- at the cost
|
|
// of a larger decompressor state (~11KB, transient for the scope of the
|
|
// stream; taken from the lent framebuffer bytes via buildscratch::claim()
|
|
// when a FrameBufferLoan is active, heap otherwise). FontDecompressor
|
|
// intentionally stays on InflateReader:
|
|
// its one-shot flash-resident group decompressions are tiny, and the render
|
|
// path should not carry the extra state allocation.
|
|
//
|
|
// Two modes:
|
|
// init(false) -- one-shot: the destination buffer holds the ENTIRE output,
|
|
// so back-references resolve inside it and no 32KB window is
|
|
// allocated. read()/readAtMost() must be driven with
|
|
// contiguous, forward-only slices of that one buffer
|
|
// (a single read(dest, totalSize) is the common case).
|
|
// init(true) -- streaming: allocates a 32KB window; output can go to any
|
|
// buffer in any-sized chunks across calls.
|
|
//
|
|
// Input is either a single contiguous buffer (setSource) or pulled on demand
|
|
// through a fill callback (setFill): return the number of bytes available and
|
|
// point *data at them (valid until the next fill call); return 0 at end of
|
|
// input. Call setZlibWrapped() before the first read when the stream has a
|
|
// zlib header (e.g. PNG IDAT).
|
|
class InflateStream {
|
|
public:
|
|
enum class Status {
|
|
Ok, // Output buffer full; more decompressed data remains.
|
|
Done, // Stream ended cleanly. produced may be < maxLen.
|
|
Error, // Corrupt/truncated stream, or decompression failed.
|
|
};
|
|
|
|
using FillFn = size_t (*)(void* ctx, const uint8_t** data);
|
|
|
|
InflateStream() = default;
|
|
~InflateStream();
|
|
InflateStream(const InflateStream&) = delete;
|
|
InflateStream& operator=(const InflateStream&) = delete;
|
|
|
|
// Allocate decompressor state (and the 32KB window when streaming) and reset
|
|
// stream state. Reuses existing allocations on repeated calls. Returns false
|
|
// on OOM.
|
|
bool init(bool streaming);
|
|
|
|
// Free the decompressor state and window.
|
|
void deinit();
|
|
|
|
// Provide the entire compressed input as one contiguous buffer.
|
|
void setSource(const uint8_t* src, size_t len);
|
|
|
|
// Provide compressed input on demand. ctx is passed back to fn verbatim.
|
|
void setFill(FillFn fn, void* ctx);
|
|
|
|
// Declare the input zlib-wrapped (2-byte header + trailing adler32).
|
|
void setZlibWrapped() { zlibWrapped = true; }
|
|
|
|
// Decompress exactly len bytes into dest. Returns false if the stream ends
|
|
// or errors before producing len bytes.
|
|
bool read(uint8_t* dest, size_t len);
|
|
|
|
// Decompress up to maxLen bytes into dest; *produced gets the byte count.
|
|
Status readAtMost(uint8_t* dest, size_t maxLen, size_t* produced);
|
|
|
|
private:
|
|
tinfl_decompressor_tag* state = nullptr; // ~11KB: heap, or inside the claimed build scratch
|
|
uint8_t* window = nullptr; // 32KB ring, streaming mode only
|
|
uint8_t* arenaBase = nullptr; // non-null when state/window live in lent framebuffer bytes
|
|
size_t windowPos = 0; // ring write cursor
|
|
// Decompressed-but-undelivered region of the window (tinfl can overshoot the
|
|
// caller's requested length; the overshoot waits here for the next read).
|
|
size_t pendingStart = 0;
|
|
size_t pendingLen = 0;
|
|
|
|
const uint8_t* inPtr = nullptr;
|
|
size_t inAvail = 0;
|
|
FillFn fill = nullptr;
|
|
void* fillCtx = nullptr;
|
|
bool inputExhausted = false;
|
|
bool zlibWrapped = false;
|
|
bool finished = false;
|
|
|
|
// One-shot mode: tinfl needs the output buffer start for back-references.
|
|
uint8_t* oneShotStart = nullptr;
|
|
};
|