diff --git a/package.json b/package.json index b25f517..c1e2e8f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@picovoice/web-utils", - "version": "1.4.0", + "version": "1.4.1", "description": "Picovoice web utility functions", "author": "Picovoice", "license": "Apache-2.0", diff --git a/src/pv_file_idb.ts b/src/pv_file_idb.ts index 034c2db..faeda2e 100644 --- a/src/pv_file_idb.ts +++ b/src/pv_file_idb.ts @@ -38,13 +38,46 @@ export function getDB(): Promise { }); } +/** + * Cache Settings + */ +class PvCache { + private _pos: number; + private _data: Uint8Array; + + constructor() { + this._pos = 0; + this._data = new Uint8Array(); + } + + public get(bytes: number): Uint8Array | undefined { + if (this._pos >= this._data.length) { + return undefined; + } + + const res = this._data.slice(this._pos, this._pos + bytes); + this._pos += bytes; + return res; + } + + public set(pos: number, data: Uint8Array) { + this._pos = pos; + this._data = data; + } + + public clear() { + this._pos = 0; + this._data = new Uint8Array(); + } +} + /** * PvFile Class * This class mocks the file system using IndexedDB. * IndexedDB is REQUIRED. */ export class PvFileIDB extends PvFile { - private readonly _pageSize = 65536; + private _pageSize = 65536; private readonly _db: IDBDatabase; private readonly _mode: IDBTransactionMode; @@ -52,6 +85,8 @@ export class PvFileIDB extends PvFile { private _pagePtr = 0; private _pageOffset = 0; + private _cache: PvCache; + /** * Constructor of PvFile instance. * @param path The path of a file. @@ -65,6 +100,8 @@ export class PvFileIDB extends PvFile { this._meta = meta; this._db = db; this._mode = mode; + + this._cache = new PvCache(); } /** @@ -155,9 +192,27 @@ export class PvFileIDB extends PvFile { const totalElems = maxToCopy - (maxToCopy % size); const buffer = new Uint8Array(totalElems); + // check if there's data in cache + const res = this._cache.get(totalElems); + if (res) { + copied += res.length; + this._pageOffset += res.length; + if (this._pageOffset === this._pageSize) { + this._pagePtr += 1; + this._pageOffset = 0; + } + + if (maxToCopy === copied) { + resolve(res); + return; + } + + buffer.set(res); + } + const keyRange = IDBKeyRange.bound( `${this._path}-${PvFileIDB.createPage(this._pagePtr)}`, - `${this._path}-${PvFileIDB.createPage(this._meta!.numPages)}` + `${this._path}-${PvFileIDB.createPage(this._pagePtr + Math.floor(totalElems / this._pageSize) + 1)}` ); const store = this._store; @@ -178,12 +233,23 @@ export class PvFileIDB extends PvFile { } if (copied < totalElems) { cursor.continue(); + } else { + if (this._pageOffset !== 0) { + this._cache.set(this._pageOffset, cursor.value); + } else { + this._cache.clear(); + } + + if (store.transaction?.commit) { + store.transaction?.commit(); + } } }; - store.transaction.onerror = () => { + req.onerror = () => { reject(store.transaction.error); }; + store.transaction.oncomplete = () => { resolve(buffer.slice(0, copied)); }; @@ -247,6 +313,10 @@ export class PvFileIDB extends PvFile { store.delete(keyRange); } + if (store.transaction?.commit) { + store.transaction?.commit(); + } + store.transaction.onerror = () => { reject(store.transaction.error); }; @@ -296,6 +366,8 @@ export class PvFileIDB extends PvFile { this._pageOffset = newOffset % this._pageSize; this._pagePtr = Math.floor(newOffset / this._pageSize); + + this._cache.clear(); } /** @@ -349,6 +421,13 @@ export class PvFileIDB extends PvFile { return (this._pagePtr >= (this._meta!.numPages - 1)) && (this._pageOffset >= (this._meta!.size % this._pageSize)); } + /** + * Set page size. + */ + public setPageSize(size: number) { + this._pageSize = size; + } + /** * Creates an index which as a key to save page data to IndexedDB. * This formats the file into 0000, 0001, 0002 ...