import { assert } from "@fluidframework/common-utils" ; import { IVectorConsumer } from "@tiny-calc/nano" ; import { Handle , isHandleValid } from "./handletable" ; import { PermutationVector , PermutationSegment } from "./permutationvector" ; import { ensureRange } from "./range" ; /**
 * Used by PermutationVector to cache position -> handle lookups.
 *
 * Perf: Possibly, this should eventually be inlined into PermutationVector itself, but
 *       so far there's no measurable perf penalty for being a separate object (node 12 x64)
 */ export class HandleCache implements IVectorConsumer < Handle > { private handles : Handle [ ] = [ ] ; private start = 0 ; constructor ( public readonly vector ) { } /**
     * Returns the index of the given position in the 'handles' array as a Uint32.
     * (If the position is not in the array, returns an integer greater than 'handles.length').
     */ private getIndex ( position ) { return ( position - this . start ) >>> 0 ; } /**
     * Returns the handle currently assigned to the given 'position' (if any).  Check
     * the result with 'isValidHandle(..)' to see if a handle has been allocated for
     * the given position.
     *
     * Throws a 'RangeError' if the provided 'position' is out-of-bounds wrt. the
     * PermutationVector's length.
     */ public getHandle ( position ) { const index = this . getIndex ( position ) ; return index < this . handles . length ? this . handles [ index ] : this . cacheMiss ( position ) ; } /** Update the cache when a handle has been allocated for a given position. */ public addHandle ( position , handle ) { assert ( isHandleValid ( handle ) , 0x017 ) ; const index = this . getIndex ( position ) ; if ( index < this . handles . length ) { assert ( ! isHandleValid ( this . handles [ index ] ) , 0x018 ) ; this . handles [ index ] = handle ; } } /** Used by 'CacheMiss()' to retrieve handles for a range of positions. */ private getHandles ( start , end ) { const handles = [ ] ; const { vector } = this ; for ( let pos = start ; pos < end ; pos ++ ) { const { segment , offset } = vector . getContainingSegment ( pos ) ; const asPerm = segment as PermutationSegment ; handles . push ( asPerm . start + offset ! ) ; } return handles ; } private cacheMiss ( position ) { const _position = position >>> 0 ; if ( _position < this . start ) { this . handles = this . getHandles ( _position , this . start ) . concat ( this . handles ) ; this . start = _position ; return this . handles [ 0 ] ; } else { ensureRange ( _position , this . vector . getLength ( ) ) ; this . handles = this . handles . concat ( this . getHandles ( this . start + this . handles . length , _position + 1 ) ) ; return this . handles [ this . handles . length - 1 ] ; } } itemsChanged ( start , removedCount , insertedCount ) { const index = this . getIndex ( start ) ; if ( index < this . handles . length ) { this . handles . length = index ; } } }