@samitouri / QOSami-HFS / commits / 5e3c8e3f

optimization: reduce number of stat calls

Massimo Melina committed Aug 25, 2024 at 15:55 UTC 5e3c8e3f20d8d1c663e2063faf08d68a7e4028a9
4 files changed +9 -7
src/api.get_file_list.ts
+1 -1
@@ -114,7 +114,7 @@ export const get_file_list: ApiHandler = async ({ uri='/', offset, limit, search
114 return name ? { n: name, url, target: node.target } : null
115 const isFolder = await nodeIsDirectory(node)
116 try {
117 - const st = source ? await stat(source) : undefined
117 + const st = source ? node.stats || await stat(source) : undefined
118 const pl = node.can_list === WHO_NO_ONE ? 'l'
119 : !hasPermission(node, 'can_list', ctx) ? 'L'
120 : ''
src/api.vfs.ts
+2 -2
@@ -32,7 +32,7 @@ const apis: ApiHandlers = {
32
33 async function recur(node=vfs): Promise<VfsNodeAdminSend> {
34 const { source } = node
35 - const stats = !source ? undefined : await stat(source!).catch(() => undefined)
35 + const stats = !source ? undefined : (node.stats || await stat(source!).catch(() => undefined))
36 const isDir = !nodeIsLink(node) && (!source || (stats?.isDirectory() ?? node.children?.length! > 0))
37 const copyStats: Pick<VfsNodeAdminSend, 'size' | 'ctime' | 'mtime'> = stats ? _.pick(stats, ['size', 'ctime', 'mtime'])
38 : { size: source ? -1 : undefined }
@@ -208,7 +208,7 @@ const apis: ApiHandlers = {
208 if (!files || fileMask && !matching(name))
209 continue
210 try {
211 - const stats = await stat(join(path, name))
211 + const stats = entry.stats || await stat(join(path, name))
212 list.add({
213 n: name,
214 s: stats.size,
src/vfs.ts
+5 -3
@@ -28,6 +28,7 @@ import { HTTP_FORBIDDEN, HTTP_UNAUTHORIZED, IS_MAC, IS_WINDOWS, MIME_AUTO } from
28 import events from './events'
29 import { expandUsername } from './perm'
30 import { getCurrentUsername } from './auth'
31 +import { Stats } from 'node:fs'
32
33 type Masks = Record<string, VfsNode & { maskOnly?: 'files' | 'folders' }>
34
@@ -50,6 +51,7 @@ export interface VfsNode extends VfsNodeStored { // include fields that are only
51 original?: VfsNode // if this is a temp node but reflecting an existing node
52 parent?: VfsNode // available when original is available
53 isFolder?: boolean
54 + stats?: Stats
55 }
56
57 export function permsFromParent(parent: VfsNode, child: VfsNode) {
@@ -125,7 +127,7 @@ export async function urlToNode(url: string, ctx?: Koa.Context, parent: VfsNode=
127 return urlToNode(rest, ctx, ret, getRest)
128 if (ret.source)
129 try {
128 - const st = await fs.stat(ret.source) // check existence
130 + const st = ret.stats || await fs.stat(ret.source) // check existence
131 ret.isFolder = st.isDirectory()
132 }
133 catch {
@@ -196,13 +198,13 @@ export function getNodeName(node: VfsNode) {
198 export async function nodeIsDirectory(node: VfsNode) {
199 if (node.isFolder !== undefined)
200 return node.isFolder
199 - const isFolder = Boolean(node.children?.length || !nodeIsLink(node) && (!node.source || await isDirectory(node.source)))
201 + const isFolder = Boolean(node.children?.length || !nodeIsLink(node) && (node.stats?.isDirectory() ?? (!node.source || await isDirectory(node.source))))
202 setHidden(node, { isFolder }) // don't make it to the storage (a node.isTemp doesn't need it to be hidden)
203 return isFolder
204 }
205
206 export async function hasDefaultFile(node: VfsNode, ctx: Koa.Context) {
205 - return node.default && await urlToNode(node.default, ctx, node) || undefined
207 + return node.default && await nodeIsDirectory(node) && await urlToNode(node.default, ctx, node) || undefined
208 }
209
210 export function nodeIsLink(node: VfsNode) {
src/zip.ts
+1 -1
@@ -50,7 +50,7 @@ export async function zipStreamFromFolder(node: VfsNode, ctx: Koa.Context) {
50 if (el.isFolder)
51 return { path: name + '/' }
52 if (!source) return
53 - const st = await fs.stat(source)
53 + const st = el.stats || await fs.stat(source)
54 if (!st || !st.isFile())
55 return
56 return {