UNPKG

mem100x

Version:

⚡ The FASTEST MCP memory server ever built - 66k+ entities/sec with intelligent context detection

254 lines 9.12 kB
"use strict"; /** * Memory-efficient Radix Tree (Compressed Trie) Cache implementation * Optimized for string keys with shared prefixes (like entity names) * Provides O(k) lookup where k is key length, with minimal memory overhead */ Object.defineProperty(exports, "__esModule", { value: true }); exports.RadixTreeCache = void 0; class RadixTreeCache { root; size = 0; maxSize; // LRU eviction tracking accessOrder = new Map(); accessCounter = 0; // Performance metrics hits = 0; misses = 0; constructor(maxSize = 1000) { this.maxSize = maxSize; this.root = this.createNode(); } createNode() { return { children: new Map(), isLeaf: false, accessCount: 0, lastAccess: 0 }; } get(key) { const result = this.findNode(key); if (result && result.node.isLeaf && result.remainingKey === '') { // Update access tracking this.accessCounter++; result.node.lastAccess = this.accessCounter; this.accessOrder.set(key, this.accessCounter); result.node.accessCount++; this.hits++; return result.node.value; } this.misses++; return undefined; } set(key, value) { // Insert or update this.insert(key, value); // Track access order for LRU this.accessCounter++; this.accessOrder.set(key, this.accessCounter); // Evict if necessary if (this.size > this.maxSize) { this.evictLRU(); } } insert(key, value) { let current = this.root; let remainingKey = key; while (remainingKey.length > 0) { let matchFound = false; // Try to find a child with common prefix for (const [edge, child] of current.children) { const commonPrefix = this.getCommonPrefix(remainingKey, edge); if (commonPrefix.length > 0) { matchFound = true; if (commonPrefix === edge) { // Full edge match, continue down current = child; remainingKey = remainingKey.substring(commonPrefix.length); } else { // Partial match, need to split edge const newMiddle = this.createNode(); const oldEdgeSuffix = edge.substring(commonPrefix.length); const newEdgeSuffix = remainingKey.substring(commonPrefix.length); // Update parent to point to new middle node current.children.delete(edge); current.children.set(commonPrefix, newMiddle); // Old child becomes child of middle node newMiddle.children.set(oldEdgeSuffix, child); // Create new leaf node if we have remaining key if (newEdgeSuffix.length > 0) { const newLeaf = this.createNode(); newLeaf.isLeaf = true; newLeaf.value = value; newLeaf.lastAccess = this.accessCounter; newMiddle.children.set(newEdgeSuffix, newLeaf); this.size++; } else { // New value goes in middle node newMiddle.isLeaf = true; newMiddle.value = value; newMiddle.lastAccess = this.accessCounter; this.size++; } return; } break; } } if (!matchFound) { // No matching edge, create new leaf const newLeaf = this.createNode(); newLeaf.isLeaf = true; newLeaf.value = value; newLeaf.lastAccess = this.accessCounter; current.children.set(remainingKey, newLeaf); this.size++; return; } } // Key already exists, update value if (current.isLeaf) { current.value = value; current.lastAccess = this.accessCounter; } else { // Make current node a leaf current.isLeaf = true; current.value = value; current.lastAccess = this.accessCounter; this.size++; } } findNode(key) { let current = this.root; let remainingKey = key; while (remainingKey.length > 0) { let matchFound = false; for (const [edge, child] of current.children) { if (remainingKey.startsWith(edge)) { current = child; remainingKey = remainingKey.substring(edge.length); matchFound = true; break; } } if (!matchFound) { return null; } } return { node: current, remainingKey }; } getCommonPrefix(str1, str2) { let i = 0; while (i < str1.length && i < str2.length && str1[i] === str2[i]) { i++; } return str1.substring(0, i); } has(key) { const result = this.findNode(key); return result !== null && result.node.isLeaf && result.remainingKey === ''; } delete(key) { // For simplicity, we'll mark as deleted rather than restructuring tree const result = this.findNode(key); if (result && result.node.isLeaf && result.remainingKey === '') { result.node.isLeaf = false; result.node.value = undefined; this.accessOrder.delete(key); this.size--; return true; } return false; } evictLRU() { // Find oldest accessed key let oldestKey = null; let oldestTime = Infinity; for (const [key, time] of this.accessOrder) { if (time < oldestTime) { oldestTime = time; oldestKey = key; } } if (oldestKey) { this.delete(oldestKey); } } clear() { this.root = this.createNode(); this.size = 0; this.accessOrder.clear(); this.accessCounter = 0; this.hits = 0; this.misses = 0; } getStats() { const total = this.hits + this.misses; return { hits: this.hits, misses: this.misses, hitRate: total > 0 ? this.hits / total : 0, size: this.size, memoryInfo: { nodeCount: this.countNodes(), averageEdgeLength: this.getAverageEdgeLength(), maxDepth: this.getMaxDepth() } }; } countNodes(node = this.root) { let count = 1; for (const child of node.children.values()) { count += this.countNodes(child); } return count; } getAverageEdgeLength(node = this.root, sum = 0, count = 0) { for (const [edge, child] of node.children) { sum += edge.length; count++; const result = this.getAverageEdgeLength(child, sum, count); sum = result * count; } return count > 0 ? sum / count : 0; } getMaxDepth(node = this.root, depth = 0) { let maxDepth = depth; for (const child of node.children.values()) { maxDepth = Math.max(maxDepth, this.getMaxDepth(child, depth + 1)); } return maxDepth; } // Memory estimation (approximate) estimateMemoryUsage() { // Each node: object overhead + Map overhead + value reference const nodeOverhead = 64; // Approximate object overhead const mapOverhead = 48; // Map overhead per instance const stringOverhead = 24; // String object overhead const pointerSize = 8; // 64-bit pointers let totalMemory = 0; const traverse = (node) => { totalMemory += nodeOverhead + mapOverhead; // Count edge strings for (const [edge, child] of node.children) { totalMemory += stringOverhead + edge.length * 2; // UTF-16 totalMemory += pointerSize; // Reference to child traverse(child); } if (node.isLeaf && node.value) { totalMemory += pointerSize; // Reference to value } }; traverse(this.root); // Add accessOrder Map overhead totalMemory += mapOverhead + this.accessOrder.size * (stringOverhead + 8 + pointerSize * 2); return totalMemory; } } exports.RadixTreeCache = RadixTreeCache; //# sourceMappingURL=radix-tree-cache.js.map