UNPKG

@phroun/paged-buffer

Version:

High-performance buffer system for editing massive files with intelligent memory management and undo/redo capabilities

164 lines 5.96 kB
"use strict"; /** * Enhanced Page metadata with simplified line tracking (marks moved to global system) */ const crypto = require('crypto'); /** * Enhanced Page metadata with simplified line tracking and no marks management * (Marks are now handled globally by the page coordinate system) */ class PageInfo { constructor(pageId, fileOffset, originalSize, checksum = null) { this.pageId = pageId; this.fileOffset = fileOffset; // Original offset in source file this.originalSize = originalSize; // Original size in source file this.checksum = checksum; // Fast checksum for change detection // Runtime state this.isDirty = false; // Has been modified this.isLoaded = false; // Currently in memory this.isDetached = false; // Conflicts with source file this.currentSize = originalSize; // Current size (may differ if modified) this.data = null; // In-memory data buffer this.lastAccess = Date.now(); // Simplified line tracking - just store newline positions this.newlinePositions = []; // Array of relative positions of \n characters this.linesCacheValid = false; // Whether newline positions are up to date } /** * Calculate fast checksum for change detection * @param {Buffer} data - Data to checksum * @returns {string} - Checksum */ static calculateChecksum(data) { return crypto.createHash('md5').update(data).digest('hex'); } /** * Update page with new data and invalidate caches as needed * @param {Buffer} data - New data */ updateData(data) { this.data = data; this.currentSize = data.length; this.isDirty = true; this.isLoaded = true; this.lastAccess = Date.now(); // Invalidate caches when data changes this.linesCacheValid = false; // Rebuild line information immediately for loaded pages this._rebuildLineCache(data); } /** * Rebuild the newline positions cache * @param {Buffer} data - Page data * @private */ _rebuildLineCache(data) { this.newlinePositions = []; // Single pass through data to find all newlines for (let i = 0; i < data.length; i++) { if (data[i] === 0x0A) { // \n this.newlinePositions.push(i); } } this.linesCacheValid = true; } /** * Ensure line cache is valid and up to date */ ensureLineCacheValid() { if (!this.linesCacheValid && this.isLoaded && this.data) { this._rebuildLineCache(this.data); } } /** * Get the number of newlines in this page * @returns {number} - Number of \n characters */ getNewlineCount() { this.ensureLineCacheValid(); return this.newlinePositions.length; } /** * Get global line starts contributed by this page * @param {number} pageVirtualStart - Virtual start address of this page * @returns {number[]} - Array of global line start positions */ getGlobalLineStarts(pageVirtualStart) { this.ensureLineCacheValid(); const starts = []; // Each newline creates a line start at position + 1 for (const nlPos of this.newlinePositions) { const globalLineStart = pageVirtualStart + nlPos + 1; starts.push(globalLineStart); } return starts; } /** * Update line cache after a modification within this page * @param {number} offset - Offset within page where modification occurred * @param {number} deletedBytes - Number of bytes deleted * @param {Buffer} insertedData - Data that was inserted */ updateAfterModification(_offset, _deletedBytes, _insertedData) { // Invalidate cache - we'll rebuild on next access this.linesCacheValid = false; } /** * Verify page integrity against original file * @param {Buffer} originalData - Data from original file * @returns {boolean} - True if page matches original */ verifyIntegrity(originalData) { if (!this.checksum) return false; const currentChecksum = PageInfo.calculateChecksum(originalData); return currentChecksum === this.checksum; } /** * Get memory usage statistics for this page * @returns {Object} - Memory usage info */ getMemoryStats() { let memoryUsed = 0; if (this.data) { memoryUsed += this.data.length; } // Newline positions memory (much smaller than before) memoryUsed += this.newlinePositions.length * 4; // 4 bytes per position return { dataSize: this.data ? this.data.length : 0, newlineCount: this.newlinePositions.length, newlinePositionsSize: this.newlinePositions.length, marksCount: 0, // No marks stored here anymore estimatedMemoryUsed: memoryUsed, isLoaded: this.isLoaded, isDirty: this.isDirty, linesCacheValid: this.linesCacheValid, marksValid: true // Always valid since no marks }; } } // Backwards compatibility exports class LineInfo { constructor(startOffset, length, endsWithNewline = false) { this.startOffset = startOffset; this.length = length; this.endsWithNewline = endsWithNewline; } get endOffset() { return this.startOffset + this.length; } get contentLength() { return this.endsWithNewline ? this.length - 1 : this.length; } } // Legacy class kept for compatibility but no longer used class MarkInfo { constructor(name, pageOffset, virtualAddress) { this.name = name; this.pageOffset = pageOffset; this.virtualAddress = virtualAddress; } } module.exports = { PageInfo, LineInfo, MarkInfo }; //# sourceMappingURL=page-info.js.map