UNPKG

fortify2-js

Version:

MOST POWERFUL JavaScript Security Library! Military-grade cryptography + 19 enhanced object methods + quantum-resistant algorithms + perfect TypeScript support. More powerful than Lodash with built-in security.

324 lines (321 loc) 9.55 kB
'use strict'; // LRU Node with better type safety and additional metadata class LRUNode { constructor(key, entry) { this.prev = null; this.next = null; this.accessCount = 0; this.key = key; this.entry = entry; this.createdAt = Date.now(); this.lastAccessed = this.createdAt; } updateAccess() { this.lastAccessed = Date.now(); this.accessCount++; } } // Performance-optimized and feature-rich LRU Cache class FastLRU { constructor(config) { this.size = 0; this.map = new Map(); // Statistics tracking this.stats = { hits: 0, misses: 0, evictions: 0, totalAccesses: 0, }; // Support both old constructor signature and new config object if (typeof config === "number") { this.capacity = config; this.enableStats = false; } else { this.capacity = config.capacity; this.ttl = config.ttl; this.enableStats = config.enableStats ?? false; this.onEvict = config.onEvict; } // Initialize sentinel nodes this.head = new LRUNode("__HEAD__", null); this.tail = new LRUNode("__TAIL__", null); this.head.next = this.tail; this.tail.prev = this.head; // Setup TTL cleanup if enabled if (this.ttl) { this.setupTTLCleanup(); } } get(key) { if (this.enableStats) { this.stats.totalAccesses++; } const node = this.map.get(key); if (!node) { if (this.enableStats) this.stats.misses++; return undefined; } // Check TTL expiration if (this.ttl && this.isExpired(node)) { this.delete(key); if (this.enableStats) this.stats.misses++; return undefined; } // Update access tracking node.updateAccess(); this.moveToHead(node); if (this.enableStats) this.stats.hits++; return node.entry; } put(key, entry) { const existingNode = this.map.get(key); if (existingNode) { // Update existing entry existingNode.entry = entry; existingNode.updateAccess(); this.moveToHead(existingNode); return null; } // Create new node const newNode = new LRUNode(key, entry); this.map.set(key, newNode); this.addToHead(newNode); this.size++; // Handle capacity overflow if (this.size > this.capacity) { const evicted = this.removeTail(); if (evicted) { this.map.delete(evicted.key); this.size--; if (this.enableStats) this.stats.evictions++; if (this.onEvict && evicted.entry) { this.onEvict(evicted.key, evicted.entry); } return evicted.entry; } } return null; } has(key) { const node = this.map.get(key); if (!node) return false; // Check TTL without updating access if (this.ttl && this.isExpired(node)) { this.delete(key); return false; } return true; } peek(key) { // Get without updating LRU order const node = this.map.get(key); if (!node) return undefined; if (this.ttl && this.isExpired(node)) { this.delete(key); return undefined; } return node.entry; } delete(key) { const node = this.map.get(key); if (!node) return false; this.removeNode(node); this.map.delete(key); this.size--; if (this.onEvict && node.entry) { this.onEvict(node.key, node.entry); } return true; } clear() { if (this.onEvict) { // Call onEvict for all entries being cleared for (const [key, node] of this.map) { if (node.entry) { this.onEvict(key, node.entry); } } } this.map.clear(); this.size = 0; this.head.next = this.tail; this.tail.prev = this.head; // Reset stats if (this.enableStats) { this.stats = { hits: 0, misses: 0, evictions: 0, totalAccesses: 0 }; } } // iteration methods keys() { return this.map.keys(); } values() { const values = []; for (const node of this.map.values()) { if (node.entry !== null) { values.push(node.entry); } } return values[Symbol.iterator](); } entries() { const entries = []; for (const [key, node] of this.map) { if (node.entry !== null) { entries.push([key, node.entry]); } } return entries[Symbol.iterator](); } // Get all keys as array (for compatibility) getKeys() { return Array.from(this.map.keys()); } // Get keys in LRU order (most recent first) getKeysInOrder() { const keys = []; let current = this.head.next; while (current && current !== this.tail) { keys.push(current.key); current = current.next; } return keys; } // Get internal node (for compatibility with UFSIMC) getNode(key) { const node = this.map.get(key); if (!node || !node.entry) return null; // Check TTL expiration if (this.ttl && this.isExpired(node)) { this.delete(key); return null; } return { entry: node.entry, key: node.key, }; } // Get least recently used entries getLRUEntries(count) { const entries = []; let current = this.tail.prev; let collected = 0; while (current && current !== this.head && collected < count) { if (current.entry !== null) { entries.push({ key: current.key, entry: current.entry, lastAccessed: current.lastAccessed, }); collected++; } current = current.prev; } return entries; } getSize() { return this.size; } getCapacity() { return this.capacity; } // Get comprehensive cache statistics getStats() { if (!this.enableStats) { throw new Error("Statistics are not enabled. Enable stats in constructor config."); } const memoryUsage = this.getMemoryUsage(); return { ...this.stats, hitRate: this.stats.totalAccesses > 0 ? this.stats.hits / this.stats.totalAccesses : 0, size: this.size, capacity: this.capacity, totalSize: memoryUsage.bytes, entryCount: this.size, memoryUsage: { used: memoryUsage.bytes, limit: this.capacity * 1000, // Rough estimate percentage: this.capacity > 0 ? (this.size / this.capacity) * 100 : 0, }, }; } // Memory usage estimation getMemoryUsage() { // Rough estimation - actual memory usage depends on entry content const nodeOverhead = 120; // Approximate bytes per node const mapOverhead = 24; // Approximate bytes per map entry return { approximate: true, bytes: this.size * (nodeOverhead + mapOverhead), }; } // Cleanup and resource management destroy() { if (this.cleanupTimer) { clearInterval(this.cleanupTimer); } this.clear(); } // Private helper methods addToHead(node) { node.prev = this.head; node.next = this.head.next; if (this.head.next) { this.head.next.prev = node; } this.head.next = node; } removeNode(node) { if (node.prev) node.prev.next = node.next; if (node.next) node.next.prev = node.prev; } moveToHead(node) { this.removeNode(node); this.addToHead(node); } removeTail() { const lastNode = this.tail.prev; if (lastNode && lastNode !== this.head) { this.removeNode(lastNode); return lastNode; } return null; } isExpired(node) { if (!this.ttl) return false; return Date.now() - node.createdAt > this.ttl; } setupTTLCleanup() { const cleanupInterval = Math.min(this.ttl / 4, 60000); // Cleanup every 1/4 TTL or 1 minute, whichever is smaller this.cleanupTimer = setInterval(() => { const now = Date.now(); const keysToDelete = []; for (const [key, node] of this.map) { if (now - node.createdAt > this.ttl) { keysToDelete.push(key); } } for (const key of keysToDelete) { this.delete(key); } }, cleanupInterval); } } exports.FastLRU = FastLRU; exports.LRUNode = LRUNode; //# sourceMappingURL=FastLRU.js.map