From e6a7d5674cb44e8a5726ef52643bc15af1d2ba80 Mon Sep 17 00:00:00 2001 From: Kwangsoo Yeo Date: Wed, 22 May 2024 10:38:13 -0700 Subject: [PATCH 1/3] add cache to idb --- package.json | 2 +- src/pv_file_idb.ts | 85 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 83 insertions(+), 4 deletions(-) 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 ... From a4d999d75fcb2ff46b74473fd2d8f7d4fe7f019a Mon Sep 17 00:00:00 2001 From: Kwangsoo Yeo Date: Wed, 22 May 2024 10:50:30 -0700 Subject: [PATCH 2/3] update --- src/pv_file_idb.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pv_file_idb.ts b/src/pv_file_idb.ts index faeda2e..2968d61 100644 --- a/src/pv_file_idb.ts +++ b/src/pv_file_idb.ts @@ -246,7 +246,7 @@ export class PvFileIDB extends PvFile { } }; - req.onerror = () => { + store.transaction.onerror = () => { reject(store.transaction.error); }; From 1be05da8f5689803e3d8e967a9dcc0c20d48e940 Mon Sep 17 00:00:00 2001 From: Kwangsoo Yeo Date: Wed, 22 May 2024 15:46:01 -0700 Subject: [PATCH 3/3] final changes --- src/pv_file.ts | 5 +++++ src/pv_file_idb.ts | 18 ++++++++---------- src/utils.ts | 6 ++++-- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/pv_file.ts b/src/pv_file.ts index 597fce2..55092ab 100644 --- a/src/pv_file.ts +++ b/src/pv_file.ts @@ -17,6 +17,7 @@ export type PvFileMeta = { size: number; numPages: number; version?: number; + pageSize?: number; } /** @@ -41,6 +42,10 @@ export abstract class PvFile { }; } + get pageSize(): number | undefined { + return undefined; + } + public abstract close(): Promise | void; public abstract read(size: number, count: number): Promise | Uint8Array; diff --git a/src/pv_file_idb.ts b/src/pv_file_idb.ts index 2968d61..fa47a33 100644 --- a/src/pv_file_idb.ts +++ b/src/pv_file_idb.ts @@ -77,7 +77,7 @@ class PvCache { * IndexedDB is REQUIRED. */ export class PvFileIDB extends PvFile { - private _pageSize = 65536; + private readonly _pageSize = 512 * 1024; // 512KB private readonly _db: IDBDatabase; private readonly _mode: IDBTransactionMode; @@ -87,6 +87,10 @@ export class PvFileIDB extends PvFile { private _cache: PvCache; + get pageSize() { + return this._pageSize; + } + /** * Constructor of PvFile instance. * @param path The path of a file. @@ -202,7 +206,7 @@ export class PvFileIDB extends PvFile { this._pageOffset = 0; } - if (maxToCopy === copied) { + if (totalElems === copied) { resolve(res); return; } @@ -294,7 +298,8 @@ export class PvFileIDB extends PvFile { const newMeta: PvFileMeta = { size: newSize, numPages: Math.ceil(newSize / this._pageSize), - version: version + version: version, + pageSize: this._pageSize, }; store.put(newMeta, this._path); @@ -421,13 +426,6 @@ 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 ... diff --git a/src/utils.ts b/src/utils.ts index 8f64bab..6f800d7 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -198,7 +198,8 @@ export async function fromBase64( if ( forceWrite || pvFile.meta === undefined || - version > pvFile.meta.version! + version > pvFile.meta.version! || + (pvFile.meta.pageSize !== pvFile.pageSize) ) { await pvFile.write(base64ToUint8Array(modelBase64), version); } @@ -222,7 +223,8 @@ export async function fromPublicDirectory( if ( forceWrite || pvFile.meta === undefined || - version > pvFile.meta.version! + version > pvFile.meta.version! || + (pvFile.meta.pageSize !== pvFile.pageSize) ) { if (numFetchReties < 0) { throw Error('numFetchRetries must be a positive number');