@pistonite/pure 0.28.0 → 0.29.0
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/LICENSE +1 -1
- package/README.md +23 -4
- package/dist/log/index.js +57 -0
- package/dist/log/index.js.map +1 -0
- package/dist/memory/index.js +92 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/result/index.js +29 -0
- package/dist/result/index.js.map +1 -0
- package/dist/sync/index.js +252 -0
- package/dist/sync/index.js.map +1 -0
- package/package.json +22 -13
- package/src/env.d.ts +1 -0
- package/src/log/index.ts +36 -11
- package/src/log/logger.ts +93 -115
- package/src/memory/cell.ts +21 -11
- package/src/memory/emp.ts +34 -23
- package/src/memory/idgen.test.ts +1 -1
- package/src/memory/index.ts +1 -4
- package/src/memory/persist.ts +9 -12
- package/src/result/index.ts +12 -4
- package/src/sync/batch.test.ts +1 -1
- package/src/sync/batch.ts +12 -17
- package/src/sync/capture.ts +2 -0
- package/src/sync/debounce.test.ts +1 -1
- package/src/sync/debounce.ts +12 -15
- package/src/sync/index.ts +2 -3
- package/src/sync/latest.test.ts +1 -1
- package/src/sync/latest.ts +19 -16
- package/src/sync/once.test.ts +1 -1
- package/src/sync/once.ts +13 -8
- package/src/sync/serial.test.ts +1 -1
- package/src/sync/serial.ts +14 -12
- package/src/sync/util.ts +2 -2
- package/src/fs/FsError.ts +0 -55
- package/src/fs/FsFile.ts +0 -67
- package/src/fs/FsFileImpl.ts +0 -219
- package/src/fs/FsFileMgr.ts +0 -29
- package/src/fs/FsFileStandalone.ts +0 -21
- package/src/fs/FsFileStandaloneImplFileAPI.ts +0 -54
- package/src/fs/FsFileStandaloneImplHandleAPI.ts +0 -147
- package/src/fs/FsFileSystem.ts +0 -71
- package/src/fs/FsFileSystemInternal.ts +0 -30
- package/src/fs/FsImplEntryAPI.ts +0 -149
- package/src/fs/FsImplFileAPI.ts +0 -116
- package/src/fs/FsImplHandleAPI.ts +0 -199
- package/src/fs/FsOpen.ts +0 -271
- package/src/fs/FsOpenFile.ts +0 -256
- package/src/fs/FsPath.ts +0 -137
- package/src/fs/FsSave.ts +0 -216
- package/src/fs/FsSupportStatus.ts +0 -87
- package/src/fs/index.ts +0 -123
- package/src/log/internal.ts +0 -14
- package/src/memory/async_erc.ts +0 -186
- package/src/memory/erc.test.ts +0 -258
- package/src/memory/erc.ts +0 -320
- package/src/pref/dark.ts +0 -151
- package/src/pref/device.ts +0 -118
- package/src/pref/index.ts +0 -13
- package/src/pref/inject_style.ts +0 -22
- package/src/pref/locale.ts +0 -296
package/src/fs/index.ts
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* High level browser to file system integration library.
|
|
3
|
-
*
|
|
4
|
-
* This library integrates the `File`, `FileEntry` and `FileSystemAccess` API
|
|
5
|
-
* to provide different levels of integration with file system in web apps.
|
|
6
|
-
*
|
|
7
|
-
* Basically, user can select a directory as a mount point, and browser can access
|
|
8
|
-
* read and sometimes write in the directory.
|
|
9
|
-
*
|
|
10
|
-
* ## Support
|
|
11
|
-
* Use `fsGetSupportStatus()` to inspect which implementation will be used.
|
|
12
|
-
*
|
|
13
|
-
* ```typescript
|
|
14
|
-
* import { fsGetSupportStatus } from "@pistonite/pure/fs";
|
|
15
|
-
*
|
|
16
|
-
* const { implementation, isSecureContext } = fsGetSupportStatus();
|
|
17
|
-
* ```
|
|
18
|
-
*
|
|
19
|
-
* `implementation` can be 3 values:
|
|
20
|
-
* 1. `FileSystemAccess`: This is used for Google Chrome and Edge, and possibly other browsers, under secure context.
|
|
21
|
-
* 2. `FileEntry`: This is used for Firefox when the FS is mounted through a drag-and-drop interface.
|
|
22
|
-
* 3. `File`: This is used for Firefox when the FS is mounted by a directory picker dialog
|
|
23
|
-
*
|
|
24
|
-
* The implementation is also chosen in this order and the first supported one is selected. If you are on Chrome/Edge and `FileSystemAccess` is not used, you can use `isSecureContext` to narrow down the reason.
|
|
25
|
-
*
|
|
26
|
-
* If you are wondering why Safari is not mentioned, it's because Apple made it so I have to buy a Mac to test, which I didn't.
|
|
27
|
-
*
|
|
28
|
-
* After you get an instance of `FsFileSystem`, you can use `capabilities` to inspect
|
|
29
|
-
* what is and is not supported.
|
|
30
|
-
*
|
|
31
|
-
* See `FsCapabilities` for more info. This is the support matrix:
|
|
32
|
-
* |Implementation|`write`?|`live`?|
|
|
33
|
-
* |--------------|--------|-------|
|
|
34
|
-
* |`FileSystemAccess`|Yes*|Yes |
|
|
35
|
-
* |`FileEntry` |No |Yes |
|
|
36
|
-
* |`File` |No |No |
|
|
37
|
-
*
|
|
38
|
-
* `*` = Need to request permission from user.
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* ## Usage
|
|
42
|
-
* First you need to get an instance of `FsFileSystem`. You can:
|
|
43
|
-
* 1. Call `fsOpenRead()` or `fsOpenReadWrite()` to show a directory picker,
|
|
44
|
-
* 2. Call `fsOpenReadFrom` or `fsOpenReadWriteFrom()` and pass in a `DataTransferItem` from a drag-and-drop interface.
|
|
45
|
-
*
|
|
46
|
-
* NOTE: `fsOpenReadWrite` does not guarantee the implementation supports writing. You should check
|
|
47
|
-
* with `capabilities` afterward.
|
|
48
|
-
*
|
|
49
|
-
* This is an example drop zone implementation in TypeScript
|
|
50
|
-
* ```typescript
|
|
51
|
-
* import { fsOpenReadWriteFrom } from "@pistonite/pure/fs";
|
|
52
|
-
*
|
|
53
|
-
* const div = document.createElement("div");
|
|
54
|
-
*
|
|
55
|
-
* div.addEventListener("dragover", (e) => {
|
|
56
|
-
* if (e.dataTransfer) {
|
|
57
|
-
* // setting this will allow dropping
|
|
58
|
-
* e.dataTransfer.dropEffect = "link";
|
|
59
|
-
* }
|
|
60
|
-
* });
|
|
61
|
-
*
|
|
62
|
-
* div.addEventListener("drop", async (e) => {
|
|
63
|
-
* const item = e.dataTransfer?.items[0];
|
|
64
|
-
* if (!item) {
|
|
65
|
-
* console.error("no item");
|
|
66
|
-
* return;
|
|
67
|
-
* }
|
|
68
|
-
*
|
|
69
|
-
* const result = await fsOpenReadWriteFrom(item);
|
|
70
|
-
* if (result.err) {
|
|
71
|
-
* console.error(result.err);
|
|
72
|
-
* return;
|
|
73
|
-
* }
|
|
74
|
-
*
|
|
75
|
-
* const fs = result.val;
|
|
76
|
-
* const { write, live } = fs.capabilities;
|
|
77
|
-
* // check capabilities and use fs
|
|
78
|
-
* // ...
|
|
79
|
-
* });
|
|
80
|
-
* ```
|
|
81
|
-
*
|
|
82
|
-
* ## Retry open
|
|
83
|
-
* You can pass in a retry handler and return true to retry, when opening fails.
|
|
84
|
-
* The handler is async so you can ask user.
|
|
85
|
-
*
|
|
86
|
-
* ```typescript
|
|
87
|
-
* import { FsError, FsResult } from "@pistonite/pure/fs";
|
|
88
|
-
*
|
|
89
|
-
* async function shouldRetry(error: FsError, attempt: number): Promise<FsResult<boolean>> {
|
|
90
|
-
* if (attempt < 10 && error === FsError.PermissionDenied) {
|
|
91
|
-
* alert("you must give permission to use this feature!");
|
|
92
|
-
* return { val: true };
|
|
93
|
-
* }
|
|
94
|
-
* return { val: false };
|
|
95
|
-
* }
|
|
96
|
-
*
|
|
97
|
-
* const result = await fsOpenReadWrite(shouldRetry);
|
|
98
|
-
* ```
|
|
99
|
-
*
|
|
100
|
-
* @module
|
|
101
|
-
*/
|
|
102
|
-
export { fsSave } from "./FsSave.ts";
|
|
103
|
-
export { fsOpenRead, fsOpenReadWrite, fsOpenReadFrom, fsOpenReadWriteFrom } from "./FsOpen.ts";
|
|
104
|
-
export { fsOpenFile, fsOpenFileMultiple } from "./FsOpenFile.ts";
|
|
105
|
-
export { fsGetSupportStatus } from "./FsSupportStatus.ts";
|
|
106
|
-
export {
|
|
107
|
-
fsRoot,
|
|
108
|
-
fsIsRoot,
|
|
109
|
-
fsGetBase,
|
|
110
|
-
fsGetName,
|
|
111
|
-
fsNormalize,
|
|
112
|
-
fsJoin,
|
|
113
|
-
fsComponents,
|
|
114
|
-
} from "./FsPath.ts";
|
|
115
|
-
export { FsErr, fsErr, fsFail } from "./FsError.ts";
|
|
116
|
-
|
|
117
|
-
export type { FsOpenRetryHandler } from "./FsOpen.ts";
|
|
118
|
-
export type * from "./FsOpenFile.ts";
|
|
119
|
-
export type { FsSupportStatus } from "./FsSupportStatus.ts";
|
|
120
|
-
export type { FsFileSystem, FsFileSystemUninit, FsCapabilities } from "./FsFileSystem.ts";
|
|
121
|
-
export type { FsFile } from "./FsFile.ts";
|
|
122
|
-
export type { FsFileStandalone } from "./FsFileStandalone.ts";
|
|
123
|
-
export type { FsError, FsResult, FsVoid } from "./FsError.ts";
|
package/src/log/internal.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { resettableLogger } from "./logger.ts";
|
|
2
|
-
|
|
3
|
-
const { logger, off, debug, info } = resettableLogger("pure", "gray");
|
|
4
|
-
|
|
5
|
-
export const ilog = logger;
|
|
6
|
-
|
|
7
|
-
/** Set the internal log level of calls in this library to off */
|
|
8
|
-
export const internalLogOff = off;
|
|
9
|
-
|
|
10
|
-
/** Set the internal log level of calls in this library to debug */
|
|
11
|
-
export const internalLogDebug = debug;
|
|
12
|
-
|
|
13
|
-
/** Set the internal log level of calls in this library to info */
|
|
14
|
-
export const internalLogInfo = info;
|
package/src/memory/async_erc.ts
DELETED
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A holder for an externally ref-counted object, where free and addref
|
|
3
|
-
* operations are asynchronous.
|
|
4
|
-
*
|
|
5
|
-
* See {@link makeErcType} for how to use
|
|
6
|
-
*
|
|
7
|
-
* @deprecated use Emp
|
|
8
|
-
*/
|
|
9
|
-
export type AsyncErc<TName, TRepr = number> = {
|
|
10
|
-
readonly type: TName;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Underlying object representation.
|
|
14
|
-
*
|
|
15
|
-
* The repr should not be undefinable. undefined means nullptr
|
|
16
|
-
*/
|
|
17
|
-
readonly value: TRepr | undefined;
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Free the underlying object.
|
|
21
|
-
*
|
|
22
|
-
* All weak references will be immediately invalidated, and this Erc becomes
|
|
23
|
-
* empty. Awaiting on the promise will ensure that the object is freed externally
|
|
24
|
-
*/
|
|
25
|
-
free: () => Promise<void>;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Assign a new value to this Erc.
|
|
29
|
-
*
|
|
30
|
-
* The old value will be freed, and all weak references will be invalidated.
|
|
31
|
-
*/
|
|
32
|
-
assign: (value: TRepr | undefined) => Promise<void>;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Take the inner value without freeing it.
|
|
36
|
-
*
|
|
37
|
-
* All weak references will be invalidated, and this Erc will become
|
|
38
|
-
* empty
|
|
39
|
-
*/
|
|
40
|
-
take: () => TRepr | undefined;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Create a weak reference to the inner value.
|
|
44
|
-
*
|
|
45
|
-
* When this Erc is freed, all weak references will be invalidated.
|
|
46
|
-
*/
|
|
47
|
-
getWeak: () => AsyncErcRef<TName, TRepr>;
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Create a strong reference to the inner value, essentially
|
|
51
|
-
* incrementing the ref count.
|
|
52
|
-
*/
|
|
53
|
-
getStrong: () => Promise<AsyncErc<TName, TRepr>>;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Weak reference to an externally ref-counted object.
|
|
58
|
-
*
|
|
59
|
-
* See {@link makeErcType} for how to use
|
|
60
|
-
*
|
|
61
|
-
* @deprecated use Emp
|
|
62
|
-
*/
|
|
63
|
-
export type AsyncErcRef<TName, TRepr = number> = {
|
|
64
|
-
readonly type: TName;
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* The underlying object representation.
|
|
68
|
-
*
|
|
69
|
-
* This may become undefined across async calls if the weak reference
|
|
70
|
-
* is invalidated
|
|
71
|
-
*/
|
|
72
|
-
readonly value: TRepr | undefined;
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Create a strong reference to the inner value, essentially
|
|
76
|
-
* incrementing the ref count.
|
|
77
|
-
*/
|
|
78
|
-
getStrong: () => Promise<AsyncErc<TName, TRepr>>;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* @deprecated use Emp
|
|
83
|
-
*/
|
|
84
|
-
export type AsyncErcRefType<T> =
|
|
85
|
-
T extends AsyncErc<infer TName, infer TRepr> ? AsyncErcRef<TName, TRepr> : never;
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* @deprecated use Emp
|
|
89
|
-
*/
|
|
90
|
-
export type AsyncErcTypeConstructor<TName, TRepr> = {
|
|
91
|
-
/**
|
|
92
|
-
* A marker value for the underlying object type.
|
|
93
|
-
*
|
|
94
|
-
* This is commonly a string literal or a symbol.
|
|
95
|
-
*/
|
|
96
|
-
marker: TName;
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* The function to free the underlying object.
|
|
100
|
-
*/
|
|
101
|
-
free: (value: TRepr) => Promise<void> | void;
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Given a value, increase the ref count and return the new reference.
|
|
105
|
-
* The returned representation should be a different value if double indirection
|
|
106
|
-
* is used (each value is a pointer to the smart pointer), or the same value
|
|
107
|
-
* if single indirection is used (the value is pointing to the object itself).
|
|
108
|
-
*/
|
|
109
|
-
addRef: (value: TRepr) => Promise<TRepr> | TRepr;
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* @deprecated use Emp
|
|
114
|
-
*/
|
|
115
|
-
export const makeAsyncErcType = <TName, TRepr>({
|
|
116
|
-
marker,
|
|
117
|
-
free,
|
|
118
|
-
addRef,
|
|
119
|
-
}: AsyncErcTypeConstructor<TName, TRepr>): ((
|
|
120
|
-
value: TRepr | undefined,
|
|
121
|
-
) => AsyncErc<TName, TRepr>) => {
|
|
122
|
-
const createStrongRef = (value: TRepr | undefined): AsyncErc<TName, TRepr> => {
|
|
123
|
-
let weakRef: (AsyncErcRef<TName, TRepr> & { invalidate: () => void }) | undefined =
|
|
124
|
-
undefined;
|
|
125
|
-
const invalidateWeakRef = () => {
|
|
126
|
-
if (!weakRef) {
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
const oldWeakRef = weakRef;
|
|
130
|
-
weakRef = undefined;
|
|
131
|
-
oldWeakRef.invalidate();
|
|
132
|
-
};
|
|
133
|
-
const createWeakRef = (initialValue: TRepr | undefined) => {
|
|
134
|
-
const weak = {
|
|
135
|
-
type: marker,
|
|
136
|
-
value: initialValue,
|
|
137
|
-
invalidate: () => {
|
|
138
|
-
weak.value = undefined;
|
|
139
|
-
},
|
|
140
|
-
getStrong: async () => {
|
|
141
|
-
if (weak.value === undefined) {
|
|
142
|
-
return createStrongRef(undefined);
|
|
143
|
-
}
|
|
144
|
-
return createStrongRef(await addRef(weak.value));
|
|
145
|
-
},
|
|
146
|
-
};
|
|
147
|
-
return weak;
|
|
148
|
-
};
|
|
149
|
-
const erc = {
|
|
150
|
-
type: marker,
|
|
151
|
-
value,
|
|
152
|
-
free: async () => {
|
|
153
|
-
if (erc.value !== undefined) {
|
|
154
|
-
invalidateWeakRef();
|
|
155
|
-
const freePromise = free(erc.value);
|
|
156
|
-
erc.value = undefined;
|
|
157
|
-
await freePromise;
|
|
158
|
-
}
|
|
159
|
-
},
|
|
160
|
-
assign: async (newValue: TRepr | undefined) => {
|
|
161
|
-
await erc.free();
|
|
162
|
-
erc.value = newValue;
|
|
163
|
-
},
|
|
164
|
-
take: () => {
|
|
165
|
-
invalidateWeakRef();
|
|
166
|
-
const oldValue = erc.value;
|
|
167
|
-
erc.value = undefined;
|
|
168
|
-
return oldValue;
|
|
169
|
-
},
|
|
170
|
-
getWeak: () => {
|
|
171
|
-
if (!weakRef) {
|
|
172
|
-
weakRef = createWeakRef(erc.value);
|
|
173
|
-
}
|
|
174
|
-
return weakRef;
|
|
175
|
-
},
|
|
176
|
-
getStrong: async () => {
|
|
177
|
-
if (erc.value === undefined) {
|
|
178
|
-
return createStrongRef(undefined);
|
|
179
|
-
}
|
|
180
|
-
return createStrongRef(await addRef(erc.value));
|
|
181
|
-
},
|
|
182
|
-
};
|
|
183
|
-
return erc;
|
|
184
|
-
};
|
|
185
|
-
return createStrongRef;
|
|
186
|
-
};
|
package/src/memory/erc.test.ts
DELETED
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
import { describe, afterEach, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { type Erc, makeErcType } from "./erc.ts";
|
|
4
|
-
|
|
5
|
-
type Rc = { value: string; refCount: number };
|
|
6
|
-
const Marker = Symbol("test");
|
|
7
|
-
type Marker = typeof Marker;
|
|
8
|
-
class Allocator {
|
|
9
|
-
private mockMemory: (Rc | undefined)[] = [];
|
|
10
|
-
public makeTestErc: (ptr: number) => Erc<Marker>;
|
|
11
|
-
|
|
12
|
-
// double indirection means each Erc holds a pointer to the external smart pointer,
|
|
13
|
-
// which also means external smart pointers all need to be heap allocated
|
|
14
|
-
// addRef() will return a pointer to a new external smart pointer
|
|
15
|
-
//
|
|
16
|
-
// single indirection on the other hand, means each Erc holds a pointer
|
|
17
|
-
// to the object directly. and addRef() will return the same pointer
|
|
18
|
-
//
|
|
19
|
-
// The Erc implementation must work with both
|
|
20
|
-
constructor(isDoubleIndirection: boolean) {
|
|
21
|
-
this.makeTestErc = makeErcType({
|
|
22
|
-
marker: Marker,
|
|
23
|
-
free: (ptr: number) => {
|
|
24
|
-
if (ptr < 0 || ptr >= this.mockMemory.length) {
|
|
25
|
-
throw new Error(
|
|
26
|
-
`Invalid index into mock memory. Length is ${this.mockMemory.length} but index is ${ptr}`,
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const rc = this.mockMemory[ptr];
|
|
31
|
-
if (!rc) {
|
|
32
|
-
throw new Error("Double free detected");
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
rc.refCount--;
|
|
36
|
-
if (isDoubleIndirection) {
|
|
37
|
-
this.mockMemory[ptr] = undefined;
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (rc.refCount === 0) {
|
|
42
|
-
this.mockMemory[ptr] = undefined;
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
addRef: (value: number) => {
|
|
46
|
-
if (value < 0 || value >= this.mockMemory.length) {
|
|
47
|
-
throw new Error(
|
|
48
|
-
`Invalid index into mock memory. Length is ${this.mockMemory.length} but index is ${value}`,
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const rc = this.mockMemory[value];
|
|
53
|
-
if (!rc) {
|
|
54
|
-
throw new Error("AddRef on freed memory detected");
|
|
55
|
-
}
|
|
56
|
-
rc.refCount++;
|
|
57
|
-
if (isDoubleIndirection) {
|
|
58
|
-
for (let i = 0; i < this.mockMemory.length; i++) {
|
|
59
|
-
if (this.mockMemory[i] === undefined) {
|
|
60
|
-
this.mockMemory[i] = rc;
|
|
61
|
-
return i;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
this.mockMemory.push(rc);
|
|
65
|
-
return this.mockMemory.length - 1;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return value;
|
|
69
|
-
},
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
allocValue(value: string): number {
|
|
74
|
-
const rc: Rc = { value, refCount: 1 };
|
|
75
|
-
for (let i = 0; i < this.mockMemory.length; i++) {
|
|
76
|
-
if (this.mockMemory[i] === undefined) {
|
|
77
|
-
this.mockMemory[i] = rc;
|
|
78
|
-
return i;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
this.mockMemory.push(rc);
|
|
82
|
-
return this.mockMemory.length - 1;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
getValue(ptr: number | undefined): string {
|
|
86
|
-
if (ptr === undefined) {
|
|
87
|
-
throw new Error("Dereference of nullptr");
|
|
88
|
-
}
|
|
89
|
-
if (ptr < 0 || ptr >= this.mockMemory.length) {
|
|
90
|
-
throw new Error(
|
|
91
|
-
`Invalid index into mock memory. Length is ${this.mockMemory.length} but index is ${ptr}`,
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const rc = this.mockMemory[ptr];
|
|
96
|
-
if (!rc) {
|
|
97
|
-
throw new Error("Dangling pointer");
|
|
98
|
-
}
|
|
99
|
-
return rc.value;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
expectNoLeak(): void {
|
|
103
|
-
for (let i = 0; i < this.mockMemory.length; i++) {
|
|
104
|
-
const rc = this.mockMemory[i];
|
|
105
|
-
if (rc) {
|
|
106
|
-
throw new Error(
|
|
107
|
-
`Memory leak detected at index ${i}. Value: ${rc.value}, RefCount: ${rc.refCount}`,
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
cleanup(): void {
|
|
114
|
-
this.mockMemory = [];
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
describe.each`
|
|
119
|
-
indirection | allocator
|
|
120
|
-
${"single"} | ${new Allocator(false)}
|
|
121
|
-
${"double"} | ${new Allocator(true)}
|
|
122
|
-
`("Erc - $indirection indirection", ({ allocator }: { allocator: Allocator }) => {
|
|
123
|
-
afterEach(() => {
|
|
124
|
-
allocator.cleanup();
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it("allocate and deallocate correctly", () => {
|
|
128
|
-
const test = allocator.makeTestErc(allocator.allocValue("Hello"));
|
|
129
|
-
expect(allocator.getValue(test.value)).toBe("Hello");
|
|
130
|
-
test.free();
|
|
131
|
-
allocator.expectNoLeak();
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it("frees if assigned new value", () => {
|
|
135
|
-
const test = allocator.makeTestErc(allocator.allocValue("Hello"));
|
|
136
|
-
test.assign(allocator.allocValue("World"));
|
|
137
|
-
expect(allocator.getValue(test.value)).toBe("World");
|
|
138
|
-
test.free();
|
|
139
|
-
allocator.expectNoLeak();
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it("does not free when taking value", () => {
|
|
143
|
-
const test = allocator.makeTestErc(allocator.allocValue("Hello"));
|
|
144
|
-
const raw = test.take();
|
|
145
|
-
expect(allocator.getValue(raw)).toBe("Hello");
|
|
146
|
-
expect(test.value).toBeUndefined();
|
|
147
|
-
if (raw === undefined) {
|
|
148
|
-
throw new Error("Raw value is undefined");
|
|
149
|
-
}
|
|
150
|
-
allocator.makeTestErc(raw).free();
|
|
151
|
-
allocator.expectNoLeak();
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
it("invalidates weak references on free", () => {
|
|
155
|
-
const test = allocator.makeTestErc(allocator.allocValue("Hello"));
|
|
156
|
-
const testWeak = test.getWeak();
|
|
157
|
-
expect(allocator.getValue(testWeak.value)).toBe("Hello");
|
|
158
|
-
test.free();
|
|
159
|
-
expect(testWeak.value).toBeUndefined();
|
|
160
|
-
allocator.expectNoLeak();
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it("invalidates weak references on assign", () => {
|
|
164
|
-
const test = allocator.makeTestErc(allocator.allocValue("Hello"));
|
|
165
|
-
const testWeak = test.getWeak();
|
|
166
|
-
expect(allocator.getValue(testWeak.value)).toBe("Hello");
|
|
167
|
-
test.assign(allocator.allocValue("World"));
|
|
168
|
-
expect(testWeak.value).toBeUndefined();
|
|
169
|
-
test.free();
|
|
170
|
-
allocator.expectNoLeak();
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
it("handles assign and take of different references correctly", () => {
|
|
174
|
-
const test1 = allocator.makeTestErc(allocator.allocValue("Hello"));
|
|
175
|
-
const test2 = allocator.makeTestErc(allocator.allocValue("World"));
|
|
176
|
-
expect(allocator.getValue(test1.value)).toBe("Hello");
|
|
177
|
-
expect(allocator.getValue(test2.value)).toBe("World");
|
|
178
|
-
test1.assign(test2.take());
|
|
179
|
-
expect(allocator.getValue(test1.value)).toBe("World");
|
|
180
|
-
test1.free();
|
|
181
|
-
allocator.expectNoLeak();
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it("handles assign and take of same references correctly", () => {
|
|
185
|
-
const test1 = allocator.makeTestErc(allocator.allocValue("Hello"));
|
|
186
|
-
const test2 = test1.getStrong();
|
|
187
|
-
test1.assign(test2.take());
|
|
188
|
-
expect(allocator.getValue(test1.value)).toBe("Hello");
|
|
189
|
-
test1.free();
|
|
190
|
-
test2.free(); // should be no-op
|
|
191
|
-
allocator.expectNoLeak();
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
it("assigning another Erc directly should cause double free", async () => {
|
|
195
|
-
const test1 = allocator.makeTestErc(allocator.allocValue("Hello"));
|
|
196
|
-
const test2 = test1.getStrong();
|
|
197
|
-
test1.assign(test2.value);
|
|
198
|
-
expect(allocator.getValue(test1.value)).toBe("Hello");
|
|
199
|
-
test1.free();
|
|
200
|
-
|
|
201
|
-
const freeTest2 = async () => {
|
|
202
|
-
test2.free();
|
|
203
|
-
};
|
|
204
|
-
await expect(freeTest2).rejects.toThrow("Double free detected");
|
|
205
|
-
allocator.expectNoLeak();
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
it("handles assign and take of same references correctly (same Erc)", () => {
|
|
209
|
-
const test1 = allocator.makeTestErc(allocator.allocValue("Hello"));
|
|
210
|
-
test1.assign(test1.take());
|
|
211
|
-
expect(allocator.getValue(test1.value)).toBe("Hello");
|
|
212
|
-
test1.free();
|
|
213
|
-
allocator.expectNoLeak();
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
it("assigning another Erc directly should cause double free (same Erc)", async () => {
|
|
217
|
-
const test1 = allocator.makeTestErc(allocator.allocValue("Hello"));
|
|
218
|
-
test1.assign(test1.value);
|
|
219
|
-
const getTest1Value = async () => {
|
|
220
|
-
allocator.getValue(test1.value);
|
|
221
|
-
};
|
|
222
|
-
await expect(getTest1Value).rejects.toThrow("Dangling pointer");
|
|
223
|
-
|
|
224
|
-
const freeTest1 = async () => {
|
|
225
|
-
test1.free();
|
|
226
|
-
};
|
|
227
|
-
await expect(freeTest1).rejects.toThrow("Double free detected");
|
|
228
|
-
allocator.expectNoLeak();
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
it("inc ref count with strong reference", () => {
|
|
232
|
-
const test = allocator.makeTestErc(allocator.allocValue("Hello"));
|
|
233
|
-
const test2 = test.getStrong();
|
|
234
|
-
expect(allocator.getValue(test.value)).toBe("Hello");
|
|
235
|
-
expect(allocator.getValue(test2.value)).toBe("Hello");
|
|
236
|
-
test.free();
|
|
237
|
-
expect(allocator.getValue(test2.value)).toBe("Hello");
|
|
238
|
-
test2.free();
|
|
239
|
-
allocator.expectNoLeak();
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
it("inc ref count with strong reference from weak reference", () => {
|
|
243
|
-
const test = allocator.makeTestErc(allocator.allocValue("Hello"));
|
|
244
|
-
const testWeak = test.getWeak();
|
|
245
|
-
expect(allocator.getValue(testWeak.value)).toBe("Hello");
|
|
246
|
-
const test2 = testWeak.getStrong();
|
|
247
|
-
expect(allocator.getValue(testWeak.value)).toBe("Hello");
|
|
248
|
-
expect(allocator.getValue(test2.value)).toBe("Hello");
|
|
249
|
-
const test2Weak = test2.getWeak();
|
|
250
|
-
test.free();
|
|
251
|
-
expect(testWeak.value).toBeUndefined();
|
|
252
|
-
expect(allocator.getValue(test2.value)).toBe("Hello");
|
|
253
|
-
expect(allocator.getValue(test2Weak.value)).toBe("Hello");
|
|
254
|
-
test2.free();
|
|
255
|
-
expect(test2Weak.value).toBeUndefined();
|
|
256
|
-
allocator.expectNoLeak();
|
|
257
|
-
});
|
|
258
|
-
});
|