UNPKG

fastfile

Version:

fast cached read write of big binary files

229 lines (173 loc) 6.29 kB
const PAGE_SIZE = 1<<22; export function createNew(o) { const initialSize = o.initialSize || 0; const fd = new BigMemFile(); fd.o = o; const nPages = initialSize ? Math.floor((initialSize - 1) / PAGE_SIZE)+1 : 0; fd.o.data = []; for (let i=0; i<nPages-1; i++) { fd.o.data.push( new Uint8Array(PAGE_SIZE)); } if (nPages) fd.o.data.push( new Uint8Array(initialSize - PAGE_SIZE*(nPages-1))); fd.totalSize = 0; fd.readOnly = false; fd.pos = 0; return fd; } export function readExisting(o) { const fd = new BigMemFile(); fd.o = o; fd.totalSize = (o.data.length-1)* PAGE_SIZE + o.data[o.data.length-1].byteLength; fd.readOnly = true; fd.pos = 0; return fd; } export function readWriteExisting(o) { const fd = new BigMemFile(); fd.o = o; fd.totalSize = (o.data.length-1)* PAGE_SIZE + o.data[o.data.length-1].byteLength; fd.readOnly = false; fd.pos = 0; return fd; } const tmpBuff32 = new Uint8Array(4); const tmpBuff32v = new DataView(tmpBuff32.buffer); const tmpBuff64 = new Uint8Array(8); const tmpBuff64v = new DataView(tmpBuff64.buffer); class BigMemFile { constructor() { this.pageSize = 1 << 14; // for compatibility } _resizeIfNeeded(newLen) { if (newLen <= this.totalSize) return; if (this.readOnly) throw new Error("Reading out of file bounds"); const nPages = Math.floor((newLen - 1) / PAGE_SIZE)+1; for (let i= Math.max(this.o.data.length-1, 0); i<nPages; i++) { const newSize = i<nPages-1 ? PAGE_SIZE : newLen - (nPages-1)*PAGE_SIZE; const p = new Uint8Array(newSize); if (i == this.o.data.length-1) p.set(this.o.data[i]); this.o.data[i] = p; } this.totalSize = newLen; } async write(buff, pos) { const self =this; if (typeof pos == "undefined") pos = self.pos; if (this.readOnly) throw new Error("Writing a read only file"); this._resizeIfNeeded(pos + buff.byteLength); const firstPage = Math.floor(pos / PAGE_SIZE); let p = firstPage; let o = pos % PAGE_SIZE; let r = buff.byteLength; while (r>0) { const l = (o+r > PAGE_SIZE) ? (PAGE_SIZE -o) : r; const srcView = buff.slice(buff.byteLength - r, buff.byteLength - r + l); const dstView = new Uint8Array(self.o.data[p].buffer, o, l); dstView.set(srcView); r = r-l; p ++; o = 0; } this.pos = pos + buff.byteLength; } async readToBuffer(buffDst, offset, len, pos) { const self = this; if (typeof pos == "undefined") pos = self.pos; if (this.readOnly) { if (pos + len > this.totalSize) throw new Error("Reading out of bounds"); } this._resizeIfNeeded(pos + len); const firstPage = Math.floor(pos / PAGE_SIZE); let p = firstPage; let o = pos % PAGE_SIZE; // Remaining bytes to read let r = len; while (r>0) { // bytes to copy from this page const l = (o+r > PAGE_SIZE) ? (PAGE_SIZE -o) : r; const srcView = new Uint8Array(self.o.data[p].buffer, o, l); buffDst.set(srcView, offset+len-r); r = r-l; p ++; o = 0; } this.pos = pos + len; } async read(len, pos) { const self = this; const buff = new Uint8Array(len); await self.readToBuffer(buff, 0, len, pos); return buff; } close() { } async discard() { } async writeULE32(v, pos) { const self = this; tmpBuff32v.setUint32(0, v, true); await self.write(tmpBuff32, pos); } async writeUBE32(v, pos) { const self = this; tmpBuff32v.setUint32(0, v, false); await self.write(tmpBuff32, pos); } async writeULE64(v, pos) { const self = this; tmpBuff64v.setUint32(0, v & 0xFFFFFFFF, true); tmpBuff64v.setUint32(4, Math.floor(v / 0x100000000) , true); await self.write(tmpBuff64, pos); } async readULE32(pos) { const self = this; const b = await self.read(4, pos); const view = new Uint32Array(b.buffer); return view[0]; } async readUBE32(pos) { const self = this; const b = await self.read(4, pos); const view = new DataView(b.buffer); return view.getUint32(0, false); } async readULE64(pos) { const self = this; const b = await self.read(8, pos); const view = new Uint32Array(b.buffer); return view[1] * 0x100000000 + view[0]; } async readString(pos) { const self = this; const fixedSize = 2048; let currentPosition = typeof pos == "undefined" ? self.pos : pos; if (currentPosition > this.totalSize) { if (this.readOnly) { throw new Error("Reading out of bounds"); } this._resizeIfNeeded(pos); } let endOfStringFound = false; let str = ""; while (!endOfStringFound) { let currentPage = Math.floor(currentPosition / PAGE_SIZE); let offsetOnPage = currentPosition % PAGE_SIZE; if (self.o.data[currentPage] === undefined) { throw new Error("ERROR"); } let readLength = Math.min(fixedSize, self.o.data[currentPage].length - offsetOnPage); const dataArray = new Uint8Array(self.o.data[currentPage].buffer, offsetOnPage, readLength); let indexEndOfString = dataArray.findIndex(element => element === 0); endOfStringFound = indexEndOfString !== -1; if (endOfStringFound) { str += new TextDecoder().decode(dataArray.slice(0, indexEndOfString)); self.pos = currentPage * PAGE_SIZE + offsetOnPage + indexEndOfString + 1; } else { str += new TextDecoder().decode(dataArray); self.pos = currentPage * PAGE_SIZE + offsetOnPage + dataArray.length; } currentPosition = self.pos; } return str; } }