Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/sync/editor/generic/patch-value-cache.ts
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45/*6The PatchValueCache is used to cache values returned7by SortedPatchList.value. Caching is critical, since otherwise8the client may have to apply hundreds of patches after ever9few keystrokes, which would make CoCalc unusable. Also, the10history browser is very painful to use without caching.11*/1213import { cmp_Date, keys, cmp, len } from "@cocalc/util/misc";14import { Document } from "./types";1516export interface Entry {17time: Date;18value: Document;19start: number;20last_used: Date;21}2223export class PatchValueCache {24// keys in the cache are integers converted to strings.25private cache: { [time: string]: Entry } = {};2627// Remove everything from the value cache that has timestamp >= time.28// If time not defined, removes everything, thus emptying the cache.29public invalidate(time: Date): void {30const time0: number = time.valueOf();31for (const time in this.cache) {32if (parseInt(time) >= time0) {33delete this.cache[time];34}35}36}3738// Ensure the value cache doesn't have too many entries in it by39// removing all but n of the ones that have not been accessed recently.40public prune(n: number): void {41if (this.size() <= n) {42// nothing to do43return;44}45const v: { time: string; last_used: Date }[] = [];46for (const time in this.cache) {47const x = this.cache[time];48if (x != null) {49v.push({ time, last_used: x.last_used });50}51}52v.sort((a, b) => cmp_Date(a.last_used, b.last_used));53// Delete oldest n entries.54for (const x of v.slice(0, v.length - n)) {55delete this.cache[x.time];56}57}5859// Include the given value at the given point in time, which should be60// the output of this.value(time), and should involve applying all patches61// up to this.patches[start-1].62public include(time: Date, value: Document, start: number) {63this.cache[`${time.valueOf()}`] = {64time,65value,66start,67last_used: new Date(),68};69}7071private keys(): number[] {72return keys(this.cache).map((x) => parseInt(x));73}7475/* Return the newest value x with x.time <= time in the cache as an object7677x={time:time, value:value, start:start},7879where this.value(time) is the given value, and it was obtained80by applying the elements of this.patches up to this.patches[start-1]81Return undefined if there are no cached values.82If time is undefined, returns the newest value in the cache.83If strict is true, returns newest value at time strictly older than time84*/85public newest_value_at_most(86time?: Date,87strict: boolean = false88): Entry | undefined {89const v: number[] = this.keys();90if (v.length === 0) {91return;92}93v.sort(cmp);94v.reverse();95if (time == null) {96return this.get(v[0]);97}98const time0 = time.valueOf();99for (const t of v) {100if ((!strict && t <= time0) || (strict && t < time0)) {101return this.get(t);102}103}104}105106/* Return cached entry corresponding to the given point in time.107Here time must be either a new Date() object, or a number (ms since epoch).108If there is nothing in the cache for the given time, returns undefined.109** YOU BETTER NOT mutate the returned value! ** It's not a copy!!110*/111public get(time: Date | number): Entry | undefined {112if (typeof time !== "number") {113// also allow dates114time = time.valueOf();115}116const x = this.cache[time];117if (x == null) {118return;119}120x.last_used = new Date(); // this is only for the client cache, so fine to use browser's clock121return x;122}123124public oldest_time(): Date | undefined {125const v = this.keys();126if (v.length === 0) {127return;128}129v.sort(cmp);130return new Date(v[0]);131}132133// Number of cached values134public size(): number {135return len(this.cache);136}137}138139140