Skip to content

Commit

Permalink
Clean up cache
Browse files Browse the repository at this point in the history
  • Loading branch information
james-pre committed Nov 12, 2024
1 parent 5b4e391 commit d5431c0
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 90 deletions.
2 changes: 1 addition & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ export async function configure<T extends ConfigMounts>(configuration: Partial<C

Object.assign(credentials, { uid, gid, suid: uid, sgid: gid, euid: uid, egid: gid });

cache.setEnabled(configuration.cacheStats ?? false);
cache.stats.isEnabled = configuration.cacheStats ?? false;
config.checkAccess = !configuration.disableAccessChecks;
config.updateOnRead = !configuration.disableUpdateOnRead;
config.syncImmediately = !configuration.onlySyncOnClose;
Expand Down
128 changes: 61 additions & 67 deletions src/emulation/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,72 +2,66 @@

import type { Stats } from '../stats.js';

/**
* Whether the cache is enabled
*/
export let isEnabled = false;

/**
* Sets whether the cache is enabled or not
*/
export function setEnabled(value: boolean): void {
isEnabled = value;
export class Cache<T> {
public isEnabled: boolean = false;

protected sync = new Map<string, T>();

protected async = new Map<string, Promise<T | undefined>>();

/**
* Gets data from the cache, if is exists and the cache is enabled.
*/
getSync(path: string): T | undefined {
if (!this.isEnabled) return;

return this.sync.get(path);
}

/**
* Adds data if the cache is enabled
*/
setSync(path: string, value: T): void {
if (!this.isEnabled) return;

this.sync.set(path, value);
}

/**
* Clears the cache if it is enabled
*/
clearSync(): void {
if (!this.isEnabled) return;

this.sync.clear();
}

/**
* Gets data from the cache, if it exists and the cache is enabled.
*/
get(path: string): Promise<T | undefined> | undefined {
if (!this.isEnabled) return;

return this.async.get(path);
}

/**
* Adds data if the cache is enabled
*/
set(path: string, value: Promise<T | undefined>): void {
if (!this.isEnabled) return;

this.async.set(path, value);
}

/**
* Clears the cache if it is enabled
*/
clear(): void {
if (!this.isEnabled) return;

this.async.clear();
}
}

const statsSync = new Map<string, Stats>();

/**
* Gets stats from the cache, if they exist and the cache is enabled.
*/
export function getStatsSync(path: string): Stats | undefined {
if (!isEnabled) return;

return statsSync.get(path);
}

/**
* Adds stats if the cache is enabled
*/
export function setStatsSync(path: string, value: Stats): void {
if (!isEnabled) return;

statsSync.set(path, value);
}

/**
* Clears the cache if it is enabled
*/
export function clearStatsSync(): void {
if (!isEnabled) return;

statsSync.clear();
}

const stats = new Map<string, Promise<Stats | undefined>>();

/**
* Gets stats from the cache, if they exist and the cache is enabled.
*/
export function getStats(path: string): Promise<Stats | undefined> | undefined {
if (!isEnabled) return;

return stats.get(path);
}

/**
* Adds stats if the cache is enabled
*/
export function setStats(path: string, value: Promise<Stats | undefined>): void {
if (!isEnabled) return;

stats.set(path, value);
}

/**
* Clears the cache if it is enabled
*/
export function clearStats(): void {
if (!isEnabled) return;

stats.clear();
}
export const stats = new Cache<Stats>();
22 changes: 11 additions & 11 deletions src/emulation/promises.ts
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ export async function unlink(path: fs.PathLike): Promise<void> {
path = normalizePath(path);
const { fs, path: resolved } = resolveMount(path);
try {
if (config.checkAccess && !(await (cache.getStats(path) || fs.stat(resolved)))!.hasAccess(constants.W_OK)) {
if (config.checkAccess && !(await (cache.stats.get(path) || fs.stat(resolved)))!.hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', resolved, 'unlink');
}
await fs.unlink(resolved);
Expand Down Expand Up @@ -623,7 +623,7 @@ export async function rmdir(path: fs.PathLike): Promise<void> {
path = await realpath(path);
const { fs, path: resolved } = resolveMount(path);
try {
const stats = await (cache.getStats(path) || fs.stat(resolved));
const stats = await (cache.stats.get(path) || fs.stat(resolved));
if (!stats) {
throw ErrnoError.With('ENOENT', path, 'rmdir');
}
Expand Down Expand Up @@ -718,8 +718,8 @@ export async function readdir(

const { fs, path: resolved } = resolveMount(path);

const _stats = cache.getStats(path) || fs.stat(resolved).catch(handleError);
cache.setStats(path, _stats);
const _stats = cache.stats.get(path) || fs.stat(resolved).catch(handleError);
cache.stats.set(path, _stats);
const stats = await _stats;

if (!stats) {
Expand All @@ -740,8 +740,8 @@ export async function readdir(
const addEntry = async (entry: string) => {
let entryStats: Stats | undefined;
if (options?.recursive || options?.withFileTypes) {
const _entryStats = cache.getStats(join(path, entry)) || fs.stat(join(resolved, entry)).catch(handleError);
cache.setStats(join(path, entry), _entryStats);
const _entryStats = cache.stats.get(join(path, entry)) || fs.stat(join(resolved, entry)).catch(handleError);
cache.stats.set(join(path, entry), _entryStats);
entryStats = await _entryStats;
}
if (options?.withFileTypes) {
Expand All @@ -768,7 +768,7 @@ export async function readdir(
};
await Promise.all(entries.map(addEntry));
if (!options?._isIndirect) {
cache.clearStats();
cache.stats.clear();
}

return values as string[] | Dirent[];
Expand Down Expand Up @@ -966,13 +966,13 @@ export async function rm(path: fs.PathLike, options?: fs.RmOptions & InternalOpt
path = normalizePath(path);

const _stats =
cache.getStats(path) ||
cache.stats.get(path) ||
stat(path).catch((error: ErrnoError) => {
if (error.code == 'ENOENT' && options?.force) return undefined;
throw error;
});

cache.setStats(path, _stats);
cache.stats.set(path, _stats);
const stats = await _stats;

if (!stats) {
Expand All @@ -998,12 +998,12 @@ export async function rm(path: fs.PathLike, options?: fs.RmOptions & InternalOpt
case constants.S_IFIFO:
case constants.S_IFSOCK:
default:
cache.clearStats();
cache.stats.clear();
throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm');
}

if (!options?._isIndirect) {
cache.clearStats();
cache.stats.clear();
}
}
rm satisfies typeof promises.rm;
Expand Down
22 changes: 11 additions & 11 deletions src/emulation/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export function unlinkSync(path: fs.PathLike): void {
path = normalizePath(path);
const { fs, path: resolved } = resolveMount(path);
try {
if (config.checkAccess && !(cache.getStatsSync(path) || fs.statSync(resolved)).hasAccess(constants.W_OK)) {
if (config.checkAccess && !(cache.stats.getSync(path) || fs.statSync(resolved)).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', resolved, 'unlink');
}
fs.unlinkSync(resolved);
Expand Down Expand Up @@ -386,7 +386,7 @@ export function rmdirSync(path: fs.PathLike): void {
path = normalizePath(path);
const { fs, path: resolved } = resolveMount(realpathSync(path));
try {
const stats = cache.getStatsSync(path) || fs.statSync(resolved);
const stats = cache.stats.getSync(path) || fs.statSync(resolved);
if (!stats.isDirectory()) {
throw ErrnoError.With('ENOTDIR', resolved, 'rmdir');
}
Expand Down Expand Up @@ -459,8 +459,8 @@ export function readdirSync(
const { fs, path: resolved } = resolveMount(realpathSync(path));
let entries: string[];
try {
const stats = cache.getStatsSync(path) || fs.statSync(resolved);
cache.setStatsSync(path, stats);
const stats = cache.stats.getSync(path) || fs.statSync(resolved);
cache.stats.setSync(path, stats);
if (config.checkAccess && !stats.hasAccess(constants.R_OK)) {
throw ErrnoError.With('EACCES', resolved, 'readdir');
}
Expand All @@ -475,8 +475,8 @@ export function readdirSync(
// Iterate over entries and handle recursive case if needed
const values: (string | Dirent | Buffer)[] = [];
for (const entry of entries) {
const entryStat = cache.getStatsSync(join(path, entry)) || fs.statSync(join(resolved, entry));
cache.setStatsSync(join(path, entry), entryStat);
const entryStat = cache.stats.getSync(join(path, entry)) || fs.statSync(join(resolved, entry));
cache.stats.setSync(join(path, entry), entryStat);

if (options?.withFileTypes) {
values.push(new Dirent(entry, entryStat));
Expand All @@ -500,7 +500,7 @@ export function readdirSync(
}

if (!options?._isIndirect) {
cache.clearStatsSync();
cache.stats.clearSync();
}
return values as string[] | Dirent[] | Buffer[];
}
Expand Down Expand Up @@ -658,7 +658,7 @@ export function rmSync(path: fs.PathLike, options?: fs.RmOptions & InternalOptio

let stats: Stats | undefined;
try {
stats = cache.getStatsSync(path) || statSync(path);
stats = cache.stats.getSync(path) || statSync(path);
} catch (error) {
if ((error as ErrnoError).code != 'ENOENT' || !options?.force) throw error;
}
Expand All @@ -667,7 +667,7 @@ export function rmSync(path: fs.PathLike, options?: fs.RmOptions & InternalOptio
return;
}

cache.setStatsSync(path, stats);
cache.stats.setSync(path, stats);

switch (stats.mode & constants.S_IFMT) {
case constants.S_IFDIR:
Expand All @@ -688,12 +688,12 @@ export function rmSync(path: fs.PathLike, options?: fs.RmOptions & InternalOptio
case constants.S_IFIFO:
case constants.S_IFSOCK:
default:
cache.clearStatsSync();
cache.stats.clearSync();
throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm');
}

if (!options?._isIndirect) {
cache.clearStatsSync();
cache.stats.clearSync();
}
}
rmSync satisfies typeof fs.rmSync;
Expand Down

0 comments on commit d5431c0

Please sign in to comment.