Structural generation, bumped only on clear() (a full reset/reload),
never on append-family mutations (append/appendBatch/updateLast).
Lets caches that assume append-mostly growth (e.g. RangePyramid) detect a
setData reload even when it re-establishes the same length/timestamps,
and fall back to a full rebuild. (prepend/evictHead move the head, so
they're already detectable via the first timestamp.)
Monotonically increasing revision counter, bumped on every mutation
(append, appendBatch, updateLast, prepend, clear). Consumers
such as indicators key their memoization cache on this value so they
only recompute when the underlying data actually changed — not on every
render frame.
Batch append with pre-grow.
Incoming candles must be sorted ASC by time. If they are not, this
method sorts them in place (after copying to avoid mutating the
caller's array) before writing — otherwise binary search over
_time would silently return wrong indices. Also filters out any
candles with t <= lastTime() to preserve strict monotonicity when
combined with existing buffer data.
Drop the oldest count candles in O(1) by advancing the logical head.
Returns the number actually evicted (clamped to the current length).
Callers that track logical indices outside the buffer (viewport
startIndex, drawing anchors) must shift them by -evicted since every
remaining candle's logical index decreases by that amount.
When the dead head space comes to dominate the live region the buffer compacts in place so trailing growth can't run away — this keeps a capped (maxCandles) buffer's memory bounded instead of doubling forever as the head marches right.
Binary search for index by timestamp. Returns exact index or -1
Map a timestamp to a (possibly fractional) logical index by linearly interpolating between the two bracketing candle times — the inverse of "index → time" for off-grid timestamps. Used to align a foreign, time-keyed overlay series (e.g. the C2 compare symbol, whose bars need not share the main series' timestamps) onto the same X axis.
Behavior:
i and i+1 → i + frac (frac by time);Pure read; allocation-free; O(log n).
Prepend candles (for lazy history loading). O(1) per candle when leftPad headroom is available; O(n) when growth is required. The growth path reserves leftPad equal to the current length so a sequence of equal-sized history pages amortizes to O(1) per candle — TradingView-style infinite scroll without GC churn.
Strict monotonicity: incoming is sorted ASC if not already, and the entire prepended block must be older than the current first candle. Candles violating that invariant are dropped.
Storage for OHLCVT candles backed by parallel Float64Arrays.
Internal model: the raw arrays have a logical
_headoffset. Logical indexireads_open[_head + i]etc. This letsprepend()run in O(1) when there's leftPad headroom — the raw arrays don't have to be re-allocated and copied on every history-page load. When headroom runs out we grow with extra leftPad reserved for future prepends, amortizing the cost to O(1) per candle.Invariants: _head >= 0 _head + _length <= _capacity