@zenfs/core 2.6.4 → 2.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backends/cow.js +11 -3
- package/dist/internal/file_index.d.ts +2 -0
- package/dist/internal/file_index.js +10 -2
- package/dist/polyfills.js +4 -1
- package/package.json +1 -5
- package/tests/backend/single-buffer.test.ts +1 -1
- package/tests/backend/single-buffer.worker.js +1 -1
- package/tests/common/file_index.test.ts +51 -0
- package/tests/common/inode.test.ts +1 -1
package/dist/backends/cow.js
CHANGED
|
@@ -116,11 +116,19 @@ export class CopyOnWriteFS extends FileSystem {
|
|
|
116
116
|
_nextIno = 1;
|
|
117
117
|
/** Moves the public inode numbers for a rename, including a directory's descendants */
|
|
118
118
|
renameInos(oldPath, newPath) {
|
|
119
|
-
|
|
119
|
+
const changes = [];
|
|
120
|
+
for (const [path, ino] of this.inos) {
|
|
120
121
|
if (path != oldPath && !path.startsWith(oldPath + '/'))
|
|
121
122
|
continue;
|
|
122
|
-
|
|
123
|
-
|
|
123
|
+
changes.push({
|
|
124
|
+
path,
|
|
125
|
+
add: newPath + path.slice(oldPath.length),
|
|
126
|
+
ino,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
for (const change of changes) {
|
|
130
|
+
this.inos.delete(change.path);
|
|
131
|
+
this.inos.set(change.add, change.ino);
|
|
124
132
|
}
|
|
125
133
|
}
|
|
126
134
|
/** Replaces the layer's inode number with the public one for `path` */
|
|
@@ -17,6 +17,7 @@ export declare const version = 1;
|
|
|
17
17
|
* @internal
|
|
18
18
|
*/
|
|
19
19
|
export declare class Index extends Map<string, Inode> {
|
|
20
|
+
#private;
|
|
20
21
|
maxSize: number;
|
|
21
22
|
/**
|
|
22
23
|
* Converts the index to JSON
|
|
@@ -26,6 +27,7 @@ export declare class Index extends Map<string, Inode> {
|
|
|
26
27
|
* Converts the index to a string
|
|
27
28
|
*/
|
|
28
29
|
toString(): string;
|
|
30
|
+
clear(): void;
|
|
29
31
|
/**
|
|
30
32
|
* Get the size in bytes of the index (including the size reported for each entry)
|
|
31
33
|
*/
|
|
@@ -21,7 +21,8 @@ export class Index extends Map {
|
|
|
21
21
|
return {
|
|
22
22
|
version,
|
|
23
23
|
maxSize: this.maxSize,
|
|
24
|
-
|
|
24
|
+
// @todo use `Iterator.map` #296
|
|
25
|
+
entries: Object.fromEntries(Array.from(this).map(([k, v]) => [k, v.toJSON()])),
|
|
25
26
|
};
|
|
26
27
|
}
|
|
27
28
|
/**
|
|
@@ -30,6 +31,10 @@ export class Index extends Map {
|
|
|
30
31
|
toString() {
|
|
31
32
|
return JSON.stringify(this.toJSON());
|
|
32
33
|
}
|
|
34
|
+
clear() {
|
|
35
|
+
super.clear();
|
|
36
|
+
this.#lastAlloc = 0;
|
|
37
|
+
}
|
|
33
38
|
/**
|
|
34
39
|
* Get the size in bytes of the index (including the size reported for each entry)
|
|
35
40
|
*/
|
|
@@ -74,12 +79,15 @@ export class Index extends Map {
|
|
|
74
79
|
}
|
|
75
80
|
return entries;
|
|
76
81
|
}
|
|
82
|
+
#lastAlloc = 0;
|
|
77
83
|
/**
|
|
78
84
|
* Get the next available ID in the index
|
|
79
85
|
* @internal
|
|
80
86
|
*/
|
|
81
87
|
_alloc() {
|
|
82
|
-
|
|
88
|
+
// @todo use `Iterator.flatMap` #296
|
|
89
|
+
this.#lastAlloc ||= Array.from(this.values()).reduce((max, inode) => Math.max(max, inode.ino, inode.data), 0);
|
|
90
|
+
return ++this.#lastAlloc;
|
|
83
91
|
}
|
|
84
92
|
/**
|
|
85
93
|
* Gets a list of entries for each directory in the index.
|
package/dist/polyfills.js
CHANGED
|
@@ -43,7 +43,10 @@ Uint8Array.fromBase64 ??=
|
|
|
43
43
|
Uint8Array.prototype.toHex ??=
|
|
44
44
|
(warn('Using a polyfill of Uint8Array.prototype.toHex'),
|
|
45
45
|
function toHex() {
|
|
46
|
-
|
|
46
|
+
// @todo use `Iterator.map` #296
|
|
47
|
+
return Array.from(this)
|
|
48
|
+
.map(b => b.toString(16).padStart(2, '0'))
|
|
49
|
+
.join('');
|
|
47
50
|
});
|
|
48
51
|
Uint8Array.fromHex ??=
|
|
49
52
|
(warn('Using a polyfill of Uint8Array.fromHex'),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zenfs/core",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.5",
|
|
4
4
|
"description": "A filesystem, anywhere",
|
|
5
5
|
"funding": {
|
|
6
6
|
"type": "individual",
|
|
@@ -90,12 +90,8 @@
|
|
|
90
90
|
"eslint": "^10.1.0",
|
|
91
91
|
"globals": "^17.3.0",
|
|
92
92
|
"prettier": "^3.2.5",
|
|
93
|
-
"tsx": "^4.19.1",
|
|
94
93
|
"typedoc": "^0.28.18",
|
|
95
94
|
"typescript": "^6.0.0",
|
|
96
95
|
"typescript-eslint": "^8.58.0"
|
|
97
|
-
},
|
|
98
|
-
"allowScripts": {
|
|
99
|
-
"esbuild": true
|
|
100
96
|
}
|
|
101
97
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
2
|
+
import { Index, Inode } from '@zenfs/core';
|
|
3
|
+
import assert from 'node:assert/strict';
|
|
4
|
+
import { suite, test } from 'node:test';
|
|
5
|
+
|
|
6
|
+
/** Build an index with `n` file entries, using IDs 2..2n+1 */
|
|
7
|
+
function buildIndex(n: number): Index {
|
|
8
|
+
const index = new Index();
|
|
9
|
+
index.set('/', new Inode({ ino: 0, data: 1, mode: 0o40755, nlink: 1 }));
|
|
10
|
+
for (let i = 0; i < n; i++) {
|
|
11
|
+
index.set(`/f${i}`, new Inode({ ino: 2 * i + 2, data: 2 * i + 3, mode: 0o100644, nlink: 1 }));
|
|
12
|
+
}
|
|
13
|
+
return index;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Calls `fn` with `depth` extra frames on the stack */
|
|
17
|
+
function atDepth<T>(depth: number, fn: () => T): T {
|
|
18
|
+
return depth > 0 ? atDepth(depth - 1, fn) : fn();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
suite('Index', () => {
|
|
22
|
+
test('_alloc returns an unused ID', () => {
|
|
23
|
+
const index = buildIndex(10);
|
|
24
|
+
assert.equal(index._alloc(), 22);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('_alloc does not re-use IDs', () => {
|
|
28
|
+
const index = buildIndex(10);
|
|
29
|
+
const ids = new Set<number>();
|
|
30
|
+
|
|
31
|
+
for (let i = 0; i < 100; i++) {
|
|
32
|
+
const id = index._alloc();
|
|
33
|
+
assert.equal(index.getByID(id), undefined, `\`_alloc\` returned in-use ID ${id}`);
|
|
34
|
+
assert(!ids.has(id), `\`_alloc\` returned duplicate ID ${id}`);
|
|
35
|
+
ids.add(id);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('_alloc works with a large index #312', () => {
|
|
40
|
+
const index = buildIndex(100_000);
|
|
41
|
+
assert.equal(index._alloc(), 200_002);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('_alloc works with a large index and a deep stack #312', () => {
|
|
45
|
+
const index = buildIndex(60_000);
|
|
46
|
+
assert.equal(
|
|
47
|
+
atDepth(1000, () => index._alloc()),
|
|
48
|
+
120_002
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
});
|