tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

data_cache.js (5235B)


      1 /**
      2 * AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
      3 **/ /**
      4 * Utilities to improve the performance of the CTS, by caching data that is
      5 * expensive to build using a two-level cache (in-memory, pre-computed file).
      6 */import { assert } from '../util/util.js';
      7 
      8 
      9 
     10 
     11 
     12 /** Logger is a basic debug logger function */
     13 
     14 
     15 /**
     16 * DataCacheNode represents a single cache entry in the LRU DataCache.
     17 * DataCacheNode is a doubly linked list, so that least-recently-used entries can be removed, and
     18 * cache hits can move the node to the front of the list.
     19 */
     20 class DataCacheNode {
     21  constructor(path, data) {
     22    this.path = path;
     23    this.data = data;
     24  }
     25 
     26  /** insertAfter() re-inserts this node in the doubly-linked list after `prev` */
     27  insertAfter(prev) {
     28    this.unlink();
     29    this.next = prev.next;
     30    this.prev = prev;
     31    prev.next = this;
     32    if (this.next) {
     33      this.next.prev = this;
     34    }
     35  }
     36 
     37  /** unlink() removes this node from the doubly-linked list */
     38  unlink() {
     39    const prev = this.prev;
     40    const next = this.next;
     41    if (prev) {
     42      prev.next = next;
     43    }
     44    if (next) {
     45      next.prev = prev;
     46    }
     47    this.prev = null;
     48    this.next = null;
     49  }
     50 
     51  // The file path this node represents
     52  // The deserialized data for this node
     53  prev = null; // The previous node in the doubly-linked list
     54  next = null; // The next node in the doubly-linked list
     55 }
     56 
     57 /** DataCache is an interface to a LRU-cached data store used to hold data cached by path */
     58 export class DataCache {
     59  constructor() {
     60    this.lruHeadNode.next = this.lruTailNode;
     61    this.lruTailNode.prev = this.lruHeadNode;
     62  }
     63 
     64  /** setDataStore() sets the backing data store used by the data cache */
     65  setStore(dataStore) {
     66    this.dataStore = dataStore;
     67  }
     68 
     69  /** setDebugLogger() sets the verbose logger */
     70  setDebugLogger(logger) {
     71    this.debugLogger = logger;
     72  }
     73 
     74  /**
     75   * fetch() retrieves cacheable data from the data cache, first checking the
     76   * in-memory cache, then the data store (if specified), then resorting to
     77   * building the data and storing it in the cache.
     78   */
     79  async fetch(cacheable) {
     80    {
     81      // First check the in-memory cache
     82      const node = this.cache.get(cacheable.path);
     83      if (node !== undefined) {
     84        this.log('in-memory cache hit');
     85        node.insertAfter(this.lruHeadNode);
     86        return Promise.resolve(node.data);
     87      }
     88    }
     89    this.log('in-memory cache miss');
     90    // In in-memory cache miss.
     91    // Next, try the data store.
     92    if (this.dataStore !== null && !this.unavailableFiles.has(cacheable.path)) {
     93      let serialized;
     94      try {
     95        serialized = await this.dataStore.load(cacheable.path);
     96        this.log('loaded serialized');
     97      } catch (err) {
     98        // not found in data store
     99        this.log(`failed to load (${cacheable.path}): ${err}`);
    100        this.unavailableFiles.add(cacheable.path);
    101      }
    102      if (serialized !== undefined) {
    103        this.log(`deserializing`);
    104        const data = cacheable.deserialize(serialized);
    105        this.addToCache(cacheable.path, data);
    106        return data;
    107      }
    108    }
    109    // Not found anywhere. Build the data, and cache for future lookup.
    110    this.log(`cache: building (${cacheable.path})`);
    111    const data = await cacheable.build();
    112    this.addToCache(cacheable.path, data);
    113    return data;
    114  }
    115 
    116  /**
    117   * addToCache() creates a new node for `path` and `data`, inserting the new node at the front of
    118   * the doubly-linked list. If the number of entries in the cache exceeds this.maxCount, then the
    119   * least recently used entry is evicted
    120   * @param path the file path for the data
    121   * @param data the deserialized data
    122   */
    123  addToCache(path, data) {
    124    if (this.cache.size >= this.maxCount) {
    125      const toEvict = this.lruTailNode.prev;
    126      assert(toEvict !== null);
    127      toEvict.unlink();
    128      this.cache.delete(toEvict.path);
    129      this.log(`evicting ${toEvict.path}`);
    130    }
    131    const node = new DataCacheNode(path, data);
    132    node.insertAfter(this.lruHeadNode);
    133    this.cache.set(path, node);
    134    this.log(`added ${path}. new count: ${this.cache.size}`);
    135  }
    136 
    137  log(msg) {
    138    if (this.debugLogger !== null) {
    139      this.debugLogger(`DataCache: ${msg}`);
    140    }
    141  }
    142 
    143  // Max number of entries in the cache before LRU entries are evicted.
    144  maxCount = 4;
    145 
    146  cache = new Map();
    147  lruHeadNode = new DataCacheNode('', null); // placeholder node (no path or data)
    148  lruTailNode = new DataCacheNode('', null); // placeholder node (no path or data)
    149  unavailableFiles = new Set();
    150  dataStore = null;
    151  debugLogger = null;
    152 }
    153 
    154 /** The data cache */
    155 export const dataCache = new DataCache();
    156 
    157 /** true if the current process is building the cache */
    158 let isBuildingDataCache = false;
    159 
    160 /** @returns true if the data cache is currently being built */
    161 export function getIsBuildingDataCache() {
    162  return isBuildingDataCache;
    163 }
    164 
    165 /** Sets whether the data cache is currently being built */
    166 export function setIsBuildingDataCache(value = true) {
    167  isBuildingDataCache = value;
    168 }
    169 
    170 /**
    171 * Cacheable is the interface to something that can be stored into the
    172 * DataCache.
    173 * The 'npm run gen_cache' tool will look for module-scope variables of this
    174 * interface, with the name `d`.
    175 */