fix: Add framebuffer release/realloc and improved lazy indexing (#2563)
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
#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;
|
||||
};
|
||||
Reference in New Issue
Block a user