UNPKG

@zenfs/core

Version:

A filesystem, anywhere

147 lines (123 loc) 3.66 kB
import type { Ino } from '../../inode.js'; import { SyncTransaction, type Store } from './store.js'; /** * An interface for simple synchronous stores that don't have special support for transactions and such. */ export interface SimpleSyncStore extends Store { get(ino: Ino): Uint8Array | undefined; set(ino: Ino, data: Uint8Array): void; delete(ino: Ino): void; } /** * An interface for simple asynchronous stores that don't have special support for transactions and such. * This class adds caching at the store level. */ export abstract class SimpleAsyncStore implements SimpleSyncStore { public abstract name: string; protected cache: Map<Ino, Uint8Array> = new Map(); protected queue: Set<Promise<unknown>> = new Set(); protected abstract entries(): Promise<Iterable<[Ino, Uint8Array]>>; public get(ino: Ino): Uint8Array | undefined { return this.cache.get(ino); } public set(ino: Ino, data: Uint8Array): void { this.cache.set(ino, data); this.queue.add(this._set(ino, data)); } protected abstract _set(ino: Ino, data: Uint8Array): Promise<void>; public delete(ino: Ino): void { this.cache.delete(ino); this.queue.add(this._delete(ino)); } protected abstract _delete(ino: Ino): Promise<void>; public clearSync(): void { this.cache.clear(); this.queue.add(this.clear()); } public abstract clear(): Promise<void>; public async sync(): Promise<void> { for (const [ino, data] of await this.entries()) { if (!this.cache.has(ino)) { this.cache.set(ino, data); } } for (const promise of this.queue) { await promise; } } public transaction(): SimpleTransaction { return new SimpleTransaction(this); } } /** * Transaction for simple stores. * @see SimpleSyncStore * @see SimpleAsyncStore */ export class SimpleTransaction extends SyncTransaction<SimpleSyncStore> { /** * Stores data in the keys we modify prior to modifying them. * Allows us to roll back commits. */ protected originalData: Map<Ino, Uint8Array | void> = new Map(); /** * List of keys modified in this transaction, if any. */ protected modifiedKeys: Set<Ino> = new Set(); protected declare store: SimpleSyncStore; public getSync(ino: Ino): Uint8Array { const val = this.store.get(ino); this.stashOldValue(ino, val); return val!; } public setSync(ino: Ino, data: Uint8Array): void { this.markModified(ino); return this.store.set(ino, data); } public removeSync(ino: Ino): void { this.markModified(ino); this.store.delete(ino); } public commitSync(): void { this.done = true; } public abortSync(): void { if (!this.done) { return; } // Rollback old values. for (const key of this.modifiedKeys) { const value = this.originalData.get(key); if (!value) { // Key didn't exist. this.store.delete(key); } else { // Key existed. Store old value. this.store.set(key, value); } } this.done = true; } /** * Stashes given key value pair into `originalData` if it doesn't already * exist. Allows us to stash values the program is requesting anyway to * prevent needless `get` requests if the program modifies the data later * on during the transaction. */ protected stashOldValue(ino: Ino, value?: Uint8Array): void { // Keep only the earliest value in the transaction. if (!this.originalData.has(ino)) { this.originalData.set(ino, value); } } /** * Marks `ino` as modified, and stashes its value if it has not been * stashed already. */ protected markModified(ino: Ino): void { this.modifiedKeys.add(ino); if (!this.originalData.has(ino)) { this.originalData.set(ino, this.store.get(ino)); } } }