@delali/narsil-react-native 0.0.0-stage → 0.3.1-next.151
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 +201 -0
- package/NarsilReactNative.podspec +57 -0
- package/README.md +46 -2
- package/android/CMakeLists.txt +48 -0
- package/android/build.gradle +151 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/AndroidEnvironment.cpp +44 -0
- package/android/src/main/cpp/AndroidEnvironment.hpp +11 -0
- package/android/src/main/cpp/cpp-adapter.cpp +11 -0
- package/android/src/main/java/com/margelo/nitro/narsil/NarsilAndroidEnvironment.kt +29 -0
- package/android/src/main/java/com/margelo/nitro/narsil/NarsilReactNativePackage.kt +22 -0
- package/app.plugin.js +25 -0
- package/cpp/AttachedField.cpp +234 -0
- package/cpp/AttachedField.hpp +61 -0
- package/cpp/Compression.cpp +113 -0
- package/cpp/Compression.hpp +16 -0
- package/cpp/GraphHeldAlone.cpp +29 -0
- package/cpp/GraphHeldAlone.hpp +25 -0
- package/cpp/HostMemory.cpp +123 -0
- package/cpp/HostMemory.hpp +53 -0
- package/cpp/HybridNarsilCoreImpl.cpp +390 -0
- package/cpp/HybridNarsilCoreImpl.hpp +68 -0
- package/cpp/HybridNarsilFilesImpl.cpp +245 -0
- package/cpp/HybridNarsilFilesImpl.hpp +34 -0
- package/cpp/HybridNarsilHostImpl.cpp +126 -0
- package/cpp/HybridNarsilHostImpl.hpp +42 -0
- package/cpp/NativeAtomics.cpp +110 -0
- package/cpp/NativeAtomics.hpp +11 -0
- package/cpp/ProcessInfo.cpp +83 -0
- package/cpp/ProcessInfo.hpp +11 -0
- package/cpp/ThreadPriority.cpp +77 -0
- package/cpp/ThreadPriority.hpp +8 -0
- package/cpp/WorkletRuntimeDispatcher.cpp +61 -0
- package/cpp/WorkletRuntimeDispatcher.hpp +9 -0
- package/cpp/core/include/narsil_core.h +208 -0
- package/cpp/core/src/connect.c +213 -0
- package/cpp/core/src/distance.c +103 -0
- package/cpp/core/src/files.c +99 -0
- package/cpp/core/src/heap.c +84 -0
- package/cpp/core/src/internal.h +284 -0
- package/cpp/core/src/kernels_code.c +209 -0
- package/cpp/core/src/kernels_float.c +197 -0
- package/cpp/core/src/kernels_pair.c +133 -0
- package/cpp/core/src/last_links.c +145 -0
- package/cpp/core/src/links.c +195 -0
- package/cpp/core/src/locks.c +168 -0
- package/cpp/core/src/nearest_links.c +147 -0
- package/cpp/core/src/place.c +215 -0
- package/cpp/core/src/quantize.c +226 -0
- package/cpp/core/src/records.c +97 -0
- package/cpp/core/src/remove.c +205 -0
- package/cpp/core/src/vectors.c +139 -0
- package/cpp/core/src/walk.c +231 -0
- package/cpp/core/src/workspace.c +167 -0
- package/ios/PrivacyInfo.xcprivacy +23 -0
- package/lib/client.d.ts +19 -0
- package/lib/client.js +41 -0
- package/lib/constants.d.ts +9 -0
- package/lib/constants.js +9 -0
- package/lib/engine/byte-files.d.ts +5 -0
- package/lib/engine/byte-files.js +97 -0
- package/lib/engine/core.d.ts +3 -0
- package/lib/engine/core.js +113 -0
- package/lib/engine/files.d.ts +3 -0
- package/lib/engine/files.js +188 -0
- package/lib/engine/host.d.ts +13 -0
- package/lib/engine/host.js +102 -0
- package/lib/engine/install.d.ts +4 -0
- package/lib/engine/install.js +29 -0
- package/lib/engine/native-error.d.ts +1 -0
- package/lib/engine/native-error.js +12 -0
- package/lib/engine/stand-ins/atomics.d.ts +5 -0
- package/lib/engine/stand-ins/atomics.js +236 -0
- package/lib/engine/stand-ins/crypto.d.ts +2 -0
- package/lib/engine/stand-ins/crypto.js +65 -0
- package/lib/engine/stand-ins/process.d.ts +2 -0
- package/lib/engine/stand-ins/process.js +22 -0
- package/lib/engine/stand-ins/streams.d.ts +1 -0
- package/lib/engine/stand-ins/streams.js +31 -0
- package/lib/engine/stand-ins/structured-clone.d.ts +1 -0
- package/lib/engine/stand-ins/structured-clone.js +121 -0
- package/lib/engine/streamed.d.ts +6 -0
- package/lib/engine/streamed.js +29 -0
- package/lib/engine/transfer.d.ts +3 -0
- package/lib/engine/transfer.js +77 -0
- package/lib/errors.d.ts +25 -0
- package/lib/errors.js +59 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +2 -0
- package/lib/methods.d.ts +5 -0
- package/lib/methods.js +47 -0
- package/lib/native-objects.d.ts +2 -0
- package/lib/native-objects.js +24 -0
- package/lib/specs/core.nitro.d.ts +79 -0
- package/lib/specs/core.nitro.js +1 -0
- package/lib/specs/files.nitro.d.ts +36 -0
- package/lib/specs/files.nitro.js +1 -0
- package/lib/specs/host.nitro.d.ts +24 -0
- package/lib/specs/host.nitro.js +1 -0
- package/lib/transferred.d.ts +15 -0
- package/lib/transferred.js +6 -0
- package/lib/types.d.ts +136 -0
- package/lib/types.js +1 -0
- package/lib/worklets/app-turns.d.ts +1 -0
- package/lib/worklets/app-turns.js +44 -0
- package/lib/worklets/client.d.ts +30 -0
- package/lib/worklets/client.js +144 -0
- package/lib/worklets/index.d.ts +4 -0
- package/lib/worklets/index.js +2 -0
- package/lib/worklets/instances.d.ts +15 -0
- package/lib/worklets/instances.js +194 -0
- package/lib/worklets/json-part.d.ts +1 -0
- package/lib/worklets/json-part.js +42 -0
- package/lib/worklets/runtime.d.ts +25 -0
- package/lib/worklets/runtime.js +80 -0
- package/lib/worklets/transfer-queue.d.ts +7 -0
- package/lib/worklets/transfer-queue.js +79 -0
- package/lib/worklets/types.d.ts +26 -0
- package/lib/worklets/types.js +1 -0
- package/nitro.json +32 -0
- package/nitrogen/generated/.gitattributes +1 -0
- package/nitrogen/generated/android/NarsilReactNative+autolinking.cmake +85 -0
- package/nitrogen/generated/android/NarsilReactNative+autolinking.gradle +27 -0
- package/nitrogen/generated/android/NarsilReactNativeOnLoad.cpp +69 -0
- package/nitrogen/generated/android/NarsilReactNativeOnLoad.hpp +34 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/narsil/NarsilReactNativeOnLoad.kt +35 -0
- package/nitrogen/generated/ios/NarsilReactNative+autolinking.rb +62 -0
- package/nitrogen/generated/ios/NarsilReactNative-Swift-Cxx-Bridge.cpp +17 -0
- package/nitrogen/generated/ios/NarsilReactNative-Swift-Cxx-Bridge.hpp +27 -0
- package/nitrogen/generated/ios/NarsilReactNative-Swift-Cxx-Umbrella.hpp +38 -0
- package/nitrogen/generated/ios/NarsilReactNativeAutolinking.mm +55 -0
- package/nitrogen/generated/ios/NarsilReactNativeAutolinking.swift +16 -0
- package/nitrogen/generated/shared/c++/DirectoryEntry.hpp +91 -0
- package/nitrogen/generated/shared/c++/FileStatus.hpp +103 -0
- package/nitrogen/generated/shared/c++/GraphMemory.hpp +120 -0
- package/nitrogen/generated/shared/c++/HybridNarsilAttachedGraphSpec.cpp +21 -0
- package/nitrogen/generated/shared/c++/HybridNarsilAttachedGraphSpec.hpp +62 -0
- package/nitrogen/generated/shared/c++/HybridNarsilAttachedStoreSpec.cpp +21 -0
- package/nitrogen/generated/shared/c++/HybridNarsilAttachedStoreSpec.hpp +62 -0
- package/nitrogen/generated/shared/c++/HybridNarsilCoreSpec.cpp +35 -0
- package/nitrogen/generated/shared/c++/HybridNarsilCoreSpec.hpp +100 -0
- package/nitrogen/generated/shared/c++/HybridNarsilFilesSpec.cpp +37 -0
- package/nitrogen/generated/shared/c++/HybridNarsilFilesSpec.hpp +86 -0
- package/nitrogen/generated/shared/c++/HybridNarsilHostSpec.cpp +34 -0
- package/nitrogen/generated/shared/c++/HybridNarsilHostSpec.hpp +78 -0
- package/nitrogen/generated/shared/c++/MemoryView.hpp +91 -0
- package/nitrogen/generated/shared/c++/NativeConnectOutcome.hpp +87 -0
- package/nitrogen/generated/shared/c++/NativeScores.hpp +87 -0
- package/nitrogen/generated/shared/c++/NativeSearchHits.hpp +91 -0
- package/nitrogen/generated/shared/c++/ProcessMemory.hpp +87 -0
- package/nitrogen/generated/shared/c++/StoreMemory.hpp +142 -0
- package/package.json +87 -3
- package/react-native.config.js +8 -0
- package/src/client.ts +44 -0
- package/src/constants.ts +9 -0
- package/src/engine/byte-files.ts +99 -0
- package/src/engine/core.ts +162 -0
- package/src/engine/files.ts +186 -0
- package/src/engine/host.ts +111 -0
- package/src/engine/install.ts +31 -0
- package/src/engine/native-error.ts +10 -0
- package/src/engine/stand-ins/atomics.ts +288 -0
- package/src/engine/stand-ins/crypto.ts +81 -0
- package/src/engine/stand-ins/process.ts +30 -0
- package/src/engine/stand-ins/streams.ts +45 -0
- package/src/engine/stand-ins/structured-clone.ts +118 -0
- package/src/engine/streamed.ts +40 -0
- package/src/engine/transfer.ts +72 -0
- package/src/errors.ts +73 -0
- package/src/index.ts +3 -0
- package/src/methods.ts +52 -0
- package/src/native-objects.ts +30 -0
- package/src/specs/core.nitro.ts +140 -0
- package/src/specs/files.nitro.ts +42 -0
- package/src/specs/host.nitro.ts +30 -0
- package/src/transferred.ts +18 -0
- package/src/types.ts +164 -0
- package/src/worklets/app-turns.ts +48 -0
- package/src/worklets/client.ts +165 -0
- package/src/worklets/index.ts +4 -0
- package/src/worklets/instances.ts +231 -0
- package/src/worklets/json-part.ts +38 -0
- package/src/worklets/runtime.ts +190 -0
- package/src/worklets/transfer-queue.ts +98 -0
- package/src/worklets/types.ts +37 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { ReactNativeHost } from '@delali/narsil'
|
|
2
|
+
import type { NarsilCore } from '../specs/core.nitro'
|
|
3
|
+
import type { NarsilFiles } from '../specs/files.nitro'
|
|
4
|
+
import type { NarsilHost } from '../specs/host.nitro'
|
|
5
|
+
import { adaptCore } from './core'
|
|
6
|
+
import { adaptFiles } from './files'
|
|
7
|
+
import { withNativeCode } from './native-error'
|
|
8
|
+
|
|
9
|
+
export interface NativeObjects {
|
|
10
|
+
host: NarsilHost
|
|
11
|
+
files: NarsilFiles
|
|
12
|
+
core: NarsilCore
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function bufferOf(view: ArrayBufferView): ArrayBuffer {
|
|
16
|
+
return view.buffer as ArrayBuffer
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function collectGarbageOn(host: NarsilHost): boolean {
|
|
20
|
+
const collect: unknown = Reflect.get(host, 'collectGarbage')
|
|
21
|
+
if (typeof collect !== 'function') return false
|
|
22
|
+
return Reflect.apply(collect, host, []) === true
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function installWorkletDispatcherOn(host: NarsilHost): boolean {
|
|
26
|
+
const install: unknown = Reflect.get(host, 'installWorkletDispatcher')
|
|
27
|
+
if (typeof install !== 'function') return false
|
|
28
|
+
return Reflect.apply(install, host, []) === true
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function threadPriorityOf(host: NarsilHost): number | null {
|
|
32
|
+
const read: unknown = Reflect.get(host, 'threadPriority')
|
|
33
|
+
if (typeof read !== 'function') return null
|
|
34
|
+
const priority: unknown = Reflect.apply(read, host, [])
|
|
35
|
+
return typeof priority === 'number' && Number.isFinite(priority) ? priority : null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function adoptAppThreadPriorityOn(host: NarsilHost, appThreadPriority: number): boolean {
|
|
39
|
+
const adopt: unknown = Reflect.get(host, 'adoptAppThreadPriority')
|
|
40
|
+
if (typeof adopt !== 'function') return false
|
|
41
|
+
return Reflect.apply(adopt, host, [appThreadPriority]) === true
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function adaptHost({ host, files, core }: NativeObjects, nativeThreads: boolean): ReactNativeHost {
|
|
45
|
+
return {
|
|
46
|
+
reserveMemory(bytes: number): ArrayBuffer {
|
|
47
|
+
try {
|
|
48
|
+
return host.reserveMemory(bytes)
|
|
49
|
+
} catch (error) {
|
|
50
|
+
throw withNativeCode(error)
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
allocateMemory(bytes: number): ArrayBuffer {
|
|
54
|
+
try {
|
|
55
|
+
return host.allocateMemory(bytes)
|
|
56
|
+
} catch (error) {
|
|
57
|
+
throw withNativeCode(error)
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
ownsMemory: (buffer: ArrayBufferLike) => host.ownsMemory(buffer as ArrayBuffer),
|
|
61
|
+
fillRandom(target: Uint8Array): void {
|
|
62
|
+
host.fillRandom(bufferOf(target), target.byteOffset, target.byteLength)
|
|
63
|
+
},
|
|
64
|
+
processId: () => host.processId(),
|
|
65
|
+
processStartedAtMs: () => host.processStartedAtMs(),
|
|
66
|
+
processIsAlive: (processId: number) => host.processIsAlive(processId),
|
|
67
|
+
processMemory: () => host.processMemory(),
|
|
68
|
+
collectGarbage: () => collectGarbageOn(host),
|
|
69
|
+
appStorageDirectory(): string {
|
|
70
|
+
try {
|
|
71
|
+
return host.appStorageDirectory()
|
|
72
|
+
} catch (error) {
|
|
73
|
+
throw withNativeCode(error)
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
deflateRaw(raw: Uint8Array): Uint8Array {
|
|
77
|
+
try {
|
|
78
|
+
return new Uint8Array(host.deflateRaw(bufferOf(raw), raw.byteOffset, raw.byteLength))
|
|
79
|
+
} catch (error) {
|
|
80
|
+
throw withNativeCode(error)
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
inflateRaw(compressed: Uint8Array, rawByteLength: number): Uint8Array {
|
|
84
|
+
try {
|
|
85
|
+
return new Uint8Array(
|
|
86
|
+
host.inflateRaw(bufferOf(compressed), compressed.byteOffset, compressed.byteLength, rawByteLength),
|
|
87
|
+
)
|
|
88
|
+
} catch (error) {
|
|
89
|
+
throw withNativeCode(error)
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
gzip(data: Uint8Array): Uint8Array {
|
|
93
|
+
try {
|
|
94
|
+
return new Uint8Array(host.gzip(bufferOf(data), data.byteOffset, data.byteLength))
|
|
95
|
+
} catch (error) {
|
|
96
|
+
throw withNativeCode(error)
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
gunzip(compressed: Uint8Array, maxByteLength: number): Uint8Array {
|
|
100
|
+
try {
|
|
101
|
+
return new Uint8Array(
|
|
102
|
+
host.gunzip(bufferOf(compressed), compressed.byteOffset, compressed.byteLength, maxByteLength),
|
|
103
|
+
)
|
|
104
|
+
} catch (error) {
|
|
105
|
+
throw withNativeCode(error)
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
files: adaptFiles(files, nativeThreads),
|
|
109
|
+
core: adaptCore(core, nativeThreads),
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ReactNativeHost } from '@delali/narsil'
|
|
2
|
+
import { adaptHost, type NativeObjects } from './host'
|
|
3
|
+
import { installAtomics } from './stand-ins/atomics'
|
|
4
|
+
import { installCrypto } from './stand-ins/crypto'
|
|
5
|
+
import { installProcess } from './stand-ins/process'
|
|
6
|
+
import { installStreams } from './stand-ins/streams'
|
|
7
|
+
import { installStructuredClone } from './stand-ins/structured-clone'
|
|
8
|
+
|
|
9
|
+
const REACT_NATIVE_HOST_KEY = Symbol.for('@delali/narsil-react-native/host')
|
|
10
|
+
|
|
11
|
+
export function installedHost(): ReactNativeHost {
|
|
12
|
+
const host = (globalThis as Record<symbol, unknown>)[REACT_NATIVE_HOST_KEY]
|
|
13
|
+
if (host === undefined) throw new Error('The Narsil engine runtime started without its native host')
|
|
14
|
+
return host as ReactNativeHost
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function installEngineHost(natives: NativeObjects, nativeThreads: boolean): void {
|
|
18
|
+
const scope = globalThis as Record<symbol, unknown>
|
|
19
|
+
if (scope[REACT_NATIVE_HOST_KEY] !== undefined) return
|
|
20
|
+
installStructuredClone()
|
|
21
|
+
installStreams()
|
|
22
|
+
installAtomics(natives.host)
|
|
23
|
+
installCrypto(natives.host)
|
|
24
|
+
installProcess(natives.host)
|
|
25
|
+
Object.defineProperty(globalThis, REACT_NATIVE_HOST_KEY, {
|
|
26
|
+
value: adaptHost(natives, nativeThreads),
|
|
27
|
+
writable: false,
|
|
28
|
+
configurable: false,
|
|
29
|
+
enumerable: false,
|
|
30
|
+
})
|
|
31
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const NATIVE_ERROR_CODE = /^[^[]*\[(E[A-Z0-9_]+)\]/
|
|
2
|
+
|
|
3
|
+
export function withNativeCode(error: unknown): unknown {
|
|
4
|
+
if (!(error instanceof Error)) return error
|
|
5
|
+
if ((error as { code?: unknown }).code !== undefined) return error
|
|
6
|
+
const match = NATIVE_ERROR_CODE.exec(error.message)
|
|
7
|
+
if (match === null) return error
|
|
8
|
+
Object.defineProperty(error, 'code', { value: match[1], enumerable: true, configurable: true, writable: true })
|
|
9
|
+
return error
|
|
10
|
+
}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
type IntegerArray =
|
|
2
|
+
| Int8Array
|
|
3
|
+
| Uint8Array
|
|
4
|
+
| Int16Array
|
|
5
|
+
| Uint16Array
|
|
6
|
+
| Int32Array
|
|
7
|
+
| Uint32Array
|
|
8
|
+
| BigInt64Array
|
|
9
|
+
| BigUint64Array
|
|
10
|
+
|
|
11
|
+
type Element = number | bigint
|
|
12
|
+
|
|
13
|
+
type WaitResult = 'ok' | 'not-equal' | 'timed-out'
|
|
14
|
+
|
|
15
|
+
interface Waiter {
|
|
16
|
+
settle(result: WaitResult): void
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const INTEGER_ARRAY_TYPES: ReadonlyArray<abstract new (...args: never[]) => IntegerArray> = [
|
|
20
|
+
Int8Array,
|
|
21
|
+
Uint8Array,
|
|
22
|
+
Int16Array,
|
|
23
|
+
Uint16Array,
|
|
24
|
+
Int32Array,
|
|
25
|
+
Uint32Array,
|
|
26
|
+
BigInt64Array,
|
|
27
|
+
BigUint64Array,
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
const WAITABLE_ARRAY_TYPES: ReadonlyArray<abstract new (...args: never[]) => IntegerArray> = [Int32Array, BigInt64Array]
|
|
31
|
+
|
|
32
|
+
const waitersByBuffer = new WeakMap<ArrayBufferLike, Map<number, Waiter[]>>()
|
|
33
|
+
|
|
34
|
+
export interface NativeAtomics {
|
|
35
|
+
ownsMemory(buffer: ArrayBuffer): boolean
|
|
36
|
+
atomic(
|
|
37
|
+
operation: number,
|
|
38
|
+
elementKind: number,
|
|
39
|
+
buffer: ArrayBuffer,
|
|
40
|
+
byteIndex: number,
|
|
41
|
+
value: number,
|
|
42
|
+
replacement: number,
|
|
43
|
+
): number
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const NATIVE_LOAD = 0
|
|
47
|
+
const NATIVE_STORE = 1
|
|
48
|
+
const NATIVE_ADD = 2
|
|
49
|
+
const NATIVE_SUB = 3
|
|
50
|
+
const NATIVE_AND = 4
|
|
51
|
+
const NATIVE_OR = 5
|
|
52
|
+
const NATIVE_XOR = 6
|
|
53
|
+
const NATIVE_EXCHANGE = 7
|
|
54
|
+
const NATIVE_COMPARE_EXCHANGE = 8
|
|
55
|
+
|
|
56
|
+
let nativeAtomics: NativeAtomics | null = null
|
|
57
|
+
const nativeOwnership = new WeakMap<ArrayBufferLike, boolean>()
|
|
58
|
+
|
|
59
|
+
function nativeElementKind(array: IntegerArray): number {
|
|
60
|
+
if (array instanceof Int8Array) return 0
|
|
61
|
+
if (array instanceof Uint8Array) return 1
|
|
62
|
+
if (array instanceof Int16Array) return 2
|
|
63
|
+
if (array instanceof Uint16Array) return 3
|
|
64
|
+
if (array instanceof Int32Array) return 4
|
|
65
|
+
if (array instanceof Uint32Array) return 5
|
|
66
|
+
return -1
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function onNativeMemory(array: IntegerArray): number {
|
|
70
|
+
if (nativeAtomics === null) return -1
|
|
71
|
+
const kind = nativeElementKind(array)
|
|
72
|
+
if (kind < 0) return -1
|
|
73
|
+
let owned = nativeOwnership.get(array.buffer)
|
|
74
|
+
if (owned === undefined) {
|
|
75
|
+
owned = nativeAtomics.ownsMemory(array.buffer as ArrayBuffer)
|
|
76
|
+
nativeOwnership.set(array.buffer, owned)
|
|
77
|
+
}
|
|
78
|
+
return owned ? kind : -1
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function nativeAtomic(
|
|
82
|
+
atomics: NativeAtomics,
|
|
83
|
+
operation: number,
|
|
84
|
+
kind: number,
|
|
85
|
+
array: IntegerArray,
|
|
86
|
+
position: number,
|
|
87
|
+
value: unknown,
|
|
88
|
+
replacement: unknown,
|
|
89
|
+
): number {
|
|
90
|
+
const byteIndex = array.byteOffset + position * array.BYTES_PER_ELEMENT
|
|
91
|
+
return atomics.atomic(operation, kind, array.buffer as ArrayBuffer, byteIndex, Number(value), Number(replacement))
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function isOneOf(array: unknown, types: ReadonlyArray<abstract new (...args: never[]) => IntegerArray>): boolean {
|
|
95
|
+
for (const type of types) {
|
|
96
|
+
if (array instanceof type) return true
|
|
97
|
+
}
|
|
98
|
+
return false
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function requireIndex(array: IntegerArray, index: unknown): number {
|
|
102
|
+
const position = Number(index)
|
|
103
|
+
if (!Number.isInteger(position) || position < 0 || position >= array.length) {
|
|
104
|
+
throw new RangeError(`Atomics index ${String(index)} lies outside the array of ${array.length} elements`)
|
|
105
|
+
}
|
|
106
|
+
return position
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function requireIntegerArray(array: unknown): IntegerArray {
|
|
110
|
+
if (!isOneOf(array, INTEGER_ARRAY_TYPES)) throw new TypeError('Atomics needs an integer typed array')
|
|
111
|
+
return array as IntegerArray
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function asElement(array: IntegerArray, value: unknown): Element {
|
|
115
|
+
if (array instanceof BigInt64Array || array instanceof BigUint64Array) return BigInt(value as bigint)
|
|
116
|
+
return Number(value)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function readModifyWrite(
|
|
120
|
+
array: unknown,
|
|
121
|
+
index: unknown,
|
|
122
|
+
value: unknown,
|
|
123
|
+
operation: number,
|
|
124
|
+
combine: (previous: Element, operand: Element) => Element,
|
|
125
|
+
): Element {
|
|
126
|
+
const target = requireIntegerArray(array)
|
|
127
|
+
const position = requireIndex(target, index)
|
|
128
|
+
const kind = onNativeMemory(target)
|
|
129
|
+
if (kind >= 0 && nativeAtomics !== null) {
|
|
130
|
+
return nativeAtomic(nativeAtomics, operation, kind, target, position, value, 0)
|
|
131
|
+
}
|
|
132
|
+
const previous = target[position] as Element
|
|
133
|
+
;(target as unknown as Element[])[position] = combine(previous, asElement(target, value))
|
|
134
|
+
return previous
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function sameAsStored(array: IntegerArray, stored: Element, value: unknown): boolean {
|
|
138
|
+
const scratch = new (array.constructor as new (length: number) => IntegerArray)(1)
|
|
139
|
+
;(scratch as unknown as Element[])[0] = asElement(array, value)
|
|
140
|
+
return scratch[0] === stored
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function waitersAt(array: IntegerArray, position: number): Waiter[] {
|
|
144
|
+
let byIndex = waitersByBuffer.get(array.buffer)
|
|
145
|
+
if (byIndex === undefined) {
|
|
146
|
+
byIndex = new Map()
|
|
147
|
+
waitersByBuffer.set(array.buffer, byIndex)
|
|
148
|
+
}
|
|
149
|
+
const byteIndex = array.byteOffset + position * array.BYTES_PER_ELEMENT
|
|
150
|
+
let waiters = byIndex.get(byteIndex)
|
|
151
|
+
if (waiters === undefined) {
|
|
152
|
+
waiters = []
|
|
153
|
+
byIndex.set(byteIndex, waiters)
|
|
154
|
+
}
|
|
155
|
+
return waiters
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function forgetWaiters(array: IntegerArray, position: number, waiters: Waiter[]): void {
|
|
159
|
+
if (waiters.length > 0) return
|
|
160
|
+
const byIndex = waitersByBuffer.get(array.buffer)
|
|
161
|
+
if (byIndex === undefined) return
|
|
162
|
+
byIndex.delete(array.byteOffset + position * array.BYTES_PER_ELEMENT)
|
|
163
|
+
if (byIndex.size === 0) waitersByBuffer.delete(array.buffer)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function requireWaitable(array: unknown, index: unknown): { target: IntegerArray; position: number } {
|
|
167
|
+
if (!isOneOf(array, WAITABLE_ARRAY_TYPES))
|
|
168
|
+
throw new TypeError('Atomics waits only on an Int32Array or a BigInt64Array')
|
|
169
|
+
const target = array as IntegerArray
|
|
170
|
+
return { target, position: requireIndex(target, index) }
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function waitAsync(
|
|
174
|
+
array: unknown,
|
|
175
|
+
index: unknown,
|
|
176
|
+
value: unknown,
|
|
177
|
+
timeout?: number,
|
|
178
|
+
): { async: false; value: WaitResult } | { async: true; value: Promise<WaitResult> } {
|
|
179
|
+
const { target, position } = requireWaitable(array, index)
|
|
180
|
+
if (!sameAsStored(target, target[position] as Element, value)) return { async: false, value: 'not-equal' }
|
|
181
|
+
const limit = timeout === undefined || Number.isNaN(Number(timeout)) ? Infinity : Math.max(0, Number(timeout))
|
|
182
|
+
if (limit === 0) return { async: false, value: 'timed-out' }
|
|
183
|
+
const waiters = waitersAt(target, position)
|
|
184
|
+
const settled = new Promise<WaitResult>(resolve => {
|
|
185
|
+
let timer: ReturnType<typeof setTimeout> | undefined
|
|
186
|
+
const waiter: Waiter = {
|
|
187
|
+
settle(result) {
|
|
188
|
+
if (timer !== undefined) clearTimeout(timer)
|
|
189
|
+
resolve(result)
|
|
190
|
+
},
|
|
191
|
+
}
|
|
192
|
+
waiters.push(waiter)
|
|
193
|
+
if (Number.isFinite(limit)) {
|
|
194
|
+
timer = setTimeout(() => {
|
|
195
|
+
const at = waiters.indexOf(waiter)
|
|
196
|
+
if (at !== -1) waiters.splice(at, 1)
|
|
197
|
+
forgetWaiters(target, position, waiters)
|
|
198
|
+
resolve('timed-out')
|
|
199
|
+
}, limit)
|
|
200
|
+
}
|
|
201
|
+
})
|
|
202
|
+
return { async: true, value: settled }
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function notify(array: unknown, index: unknown, count?: number): number {
|
|
206
|
+
const target = requireIntegerArray(array)
|
|
207
|
+
const position = requireIndex(target, index)
|
|
208
|
+
if (!isOneOf(target, WAITABLE_ARRAY_TYPES)) return 0
|
|
209
|
+
const byIndex = waitersByBuffer.get(target.buffer)
|
|
210
|
+
const waiters = byIndex?.get(target.byteOffset + position * target.BYTES_PER_ELEMENT)
|
|
211
|
+
if (waiters === undefined) return 0
|
|
212
|
+
const wanted = count === undefined ? waiters.length : Math.max(0, Math.floor(Number(count)))
|
|
213
|
+
const woken = waiters.splice(0, wanted)
|
|
214
|
+
forgetWaiters(target, position, waiters)
|
|
215
|
+
for (const waiter of woken) waiter.settle('ok')
|
|
216
|
+
return woken.length
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function wait(array: unknown, index: unknown, value: unknown): WaitResult {
|
|
220
|
+
const { target, position } = requireWaitable(array, index)
|
|
221
|
+
if (!sameAsStored(target, target[position] as Element, value)) return 'not-equal'
|
|
222
|
+
throw new TypeError('Atomics.wait would block the only thread of this runtime, so it cannot wait here')
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const standInAtomics = {
|
|
226
|
+
add: (array: unknown, index: unknown, value: unknown) =>
|
|
227
|
+
readModifyWrite(array, index, value, NATIVE_ADD, (a, b) => (a as number) + (b as number)),
|
|
228
|
+
sub: (array: unknown, index: unknown, value: unknown) =>
|
|
229
|
+
readModifyWrite(array, index, value, NATIVE_SUB, (a, b) => (a as number) - (b as number)),
|
|
230
|
+
and: (array: unknown, index: unknown, value: unknown) =>
|
|
231
|
+
readModifyWrite(array, index, value, NATIVE_AND, (a, b) => (a as number) & (b as number)),
|
|
232
|
+
or: (array: unknown, index: unknown, value: unknown) =>
|
|
233
|
+
readModifyWrite(array, index, value, NATIVE_OR, (a, b) => (a as number) | (b as number)),
|
|
234
|
+
xor: (array: unknown, index: unknown, value: unknown) =>
|
|
235
|
+
readModifyWrite(array, index, value, NATIVE_XOR, (a, b) => (a as number) ^ (b as number)),
|
|
236
|
+
exchange: (array: unknown, index: unknown, value: unknown) =>
|
|
237
|
+
readModifyWrite(array, index, value, NATIVE_EXCHANGE, (_a, b) => b),
|
|
238
|
+
compareExchange(array: unknown, index: unknown, expected: unknown, replacement: unknown): Element {
|
|
239
|
+
const target = requireIntegerArray(array)
|
|
240
|
+
const position = requireIndex(target, index)
|
|
241
|
+
const kind = onNativeMemory(target)
|
|
242
|
+
if (kind >= 0 && nativeAtomics !== null) {
|
|
243
|
+
return nativeAtomic(nativeAtomics, NATIVE_COMPARE_EXCHANGE, kind, target, position, expected, replacement)
|
|
244
|
+
}
|
|
245
|
+
const previous = target[position] as Element
|
|
246
|
+
if (sameAsStored(target, previous, expected)) {
|
|
247
|
+
;(target as unknown as Element[])[position] = asElement(target, replacement)
|
|
248
|
+
}
|
|
249
|
+
return previous
|
|
250
|
+
},
|
|
251
|
+
load(array: unknown, index: unknown): Element {
|
|
252
|
+
const target = requireIntegerArray(array)
|
|
253
|
+
const position = requireIndex(target, index)
|
|
254
|
+
const kind = onNativeMemory(target)
|
|
255
|
+
if (kind >= 0 && nativeAtomics !== null) {
|
|
256
|
+
return nativeAtomic(nativeAtomics, NATIVE_LOAD, kind, target, position, 0, 0)
|
|
257
|
+
}
|
|
258
|
+
return target[position] as Element
|
|
259
|
+
},
|
|
260
|
+
store(array: unknown, index: unknown, value: unknown): Element {
|
|
261
|
+
const target = requireIntegerArray(array)
|
|
262
|
+
const position = requireIndex(target, index)
|
|
263
|
+
const kind = onNativeMemory(target)
|
|
264
|
+
if (kind >= 0 && nativeAtomics !== null) {
|
|
265
|
+
nativeAtomic(nativeAtomics, NATIVE_STORE, kind, target, position, value, 0)
|
|
266
|
+
return Math.trunc(Number(value))
|
|
267
|
+
}
|
|
268
|
+
const element = asElement(target, value)
|
|
269
|
+
;(target as unknown as Element[])[position] = element
|
|
270
|
+
return typeof element === 'bigint' ? element : Math.trunc(element)
|
|
271
|
+
},
|
|
272
|
+
isLockFree: (size: number) => size === 1 || size === 2 || size === 4 || size === 8,
|
|
273
|
+
wait,
|
|
274
|
+
waitAsync,
|
|
275
|
+
notify,
|
|
276
|
+
pause: () => {},
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export function installAtomics(native: NativeAtomics): void {
|
|
280
|
+
if (typeof (globalThis as { Atomics?: unknown }).Atomics === 'object') return
|
|
281
|
+
nativeAtomics = native
|
|
282
|
+
Object.defineProperty(globalThis, 'Atomics', {
|
|
283
|
+
value: standInAtomics,
|
|
284
|
+
writable: true,
|
|
285
|
+
configurable: true,
|
|
286
|
+
enumerable: false,
|
|
287
|
+
})
|
|
288
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { NarsilHost } from '../../specs/host.nitro'
|
|
2
|
+
|
|
3
|
+
const MAX_RANDOM_BYTES_PER_CALL = 65_536
|
|
4
|
+
const UUID_BYTES = 16
|
|
5
|
+
const UUID_VERSION_BYTE = 6
|
|
6
|
+
const UUID_VARIANT_BYTE = 8
|
|
7
|
+
const UUID_HYPHEN_AFTER_BYTES = new Set([4, 6, 8, 10])
|
|
8
|
+
|
|
9
|
+
interface CryptoLike {
|
|
10
|
+
getRandomValues?: unknown
|
|
11
|
+
randomUUID?: unknown
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function isIntegerArray(view: ArrayBufferView): boolean {
|
|
15
|
+
return (
|
|
16
|
+
view instanceof Int8Array ||
|
|
17
|
+
view instanceof Uint8Array ||
|
|
18
|
+
view instanceof Uint8ClampedArray ||
|
|
19
|
+
view instanceof Int16Array ||
|
|
20
|
+
view instanceof Uint16Array ||
|
|
21
|
+
view instanceof Int32Array ||
|
|
22
|
+
view instanceof Uint32Array ||
|
|
23
|
+
view instanceof BigInt64Array ||
|
|
24
|
+
view instanceof BigUint64Array
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function namedError(message: string, name: string): Error {
|
|
29
|
+
const error = new Error(message)
|
|
30
|
+
error.name = name
|
|
31
|
+
return error
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function installCrypto(host: NarsilHost): void {
|
|
35
|
+
const scope = globalThis as { crypto?: CryptoLike }
|
|
36
|
+
const existing = scope.crypto
|
|
37
|
+
if (typeof existing?.getRandomValues === 'function' && typeof existing.randomUUID === 'function') return
|
|
38
|
+
|
|
39
|
+
function getRandomValues<T extends ArrayBufferView>(array: T): T {
|
|
40
|
+
if (!ArrayBuffer.isView(array) || !isIntegerArray(array)) {
|
|
41
|
+
throw namedError('getRandomValues fills only an integer typed array', 'TypeMismatchError')
|
|
42
|
+
}
|
|
43
|
+
if (array.byteLength > MAX_RANDOM_BYTES_PER_CALL) {
|
|
44
|
+
throw namedError(
|
|
45
|
+
`getRandomValues fills at most ${MAX_RANDOM_BYTES_PER_CALL} bytes per call, not ${array.byteLength}`,
|
|
46
|
+
'QuotaExceededError',
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
host.fillRandom(array.buffer as ArrayBuffer, array.byteOffset, array.byteLength)
|
|
50
|
+
return array
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function randomUUID(): string {
|
|
54
|
+
const bytes = new Uint8Array(UUID_BYTES)
|
|
55
|
+
host.fillRandom(bytes.buffer, 0, UUID_BYTES)
|
|
56
|
+
bytes[UUID_VERSION_BYTE] = ((bytes[UUID_VERSION_BYTE] ?? 0) & 0x0f) | 0x40
|
|
57
|
+
bytes[UUID_VARIANT_BYTE] = ((bytes[UUID_VARIANT_BYTE] ?? 0) & 0x3f) | 0x80
|
|
58
|
+
let text = ''
|
|
59
|
+
for (let index = 0; index < UUID_BYTES; index++) {
|
|
60
|
+
if (UUID_HYPHEN_AFTER_BYTES.has(index)) text += '-'
|
|
61
|
+
text += (bytes[index] ?? 0).toString(16).padStart(2, '0')
|
|
62
|
+
}
|
|
63
|
+
return text
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const crypto: CryptoLike = existing ?? {}
|
|
67
|
+
if (typeof crypto.getRandomValues !== 'function') {
|
|
68
|
+
Object.defineProperty(crypto, 'getRandomValues', { value: getRandomValues, writable: true, configurable: true })
|
|
69
|
+
}
|
|
70
|
+
if (typeof crypto.randomUUID !== 'function') {
|
|
71
|
+
Object.defineProperty(crypto, 'randomUUID', { value: randomUUID, writable: true, configurable: true })
|
|
72
|
+
}
|
|
73
|
+
if (existing === undefined) {
|
|
74
|
+
Object.defineProperty(globalThis, 'crypto', {
|
|
75
|
+
value: crypto,
|
|
76
|
+
writable: true,
|
|
77
|
+
configurable: true,
|
|
78
|
+
enumerable: false,
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { NarsilHost } from '../../specs/host.nitro'
|
|
2
|
+
|
|
3
|
+
const MILLISECONDS_PER_SECOND = 1_000
|
|
4
|
+
|
|
5
|
+
interface ProcessLike {
|
|
6
|
+
pid?: unknown
|
|
7
|
+
uptime?: unknown
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function installProcess(host: NarsilHost): void {
|
|
11
|
+
const scope = globalThis as { process?: ProcessLike }
|
|
12
|
+
const existing = scope.process
|
|
13
|
+
const processInfo: ProcessLike = existing ?? {}
|
|
14
|
+
if (typeof processInfo.pid !== 'number') {
|
|
15
|
+
Object.defineProperty(processInfo, 'pid', { value: host.processId(), writable: true, configurable: true })
|
|
16
|
+
}
|
|
17
|
+
if (typeof processInfo.uptime !== 'function') {
|
|
18
|
+
const startedAtMs = host.processStartedAtMs()
|
|
19
|
+
const uptime = () => Math.max(0, Date.now() - startedAtMs) / MILLISECONDS_PER_SECOND
|
|
20
|
+
Object.defineProperty(processInfo, 'uptime', { value: uptime, writable: true, configurable: true })
|
|
21
|
+
}
|
|
22
|
+
if (existing === undefined) {
|
|
23
|
+
Object.defineProperty(globalThis, 'process', {
|
|
24
|
+
value: processInfo,
|
|
25
|
+
writable: true,
|
|
26
|
+
configurable: true,
|
|
27
|
+
enumerable: false,
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ByteLengthQueuingStrategy,
|
|
3
|
+
CountQueuingStrategy,
|
|
4
|
+
ReadableByteStreamController,
|
|
5
|
+
ReadableStream,
|
|
6
|
+
ReadableStreamBYOBReader,
|
|
7
|
+
ReadableStreamBYOBRequest,
|
|
8
|
+
ReadableStreamDefaultController,
|
|
9
|
+
ReadableStreamDefaultReader,
|
|
10
|
+
TransformStream,
|
|
11
|
+
TransformStreamDefaultController,
|
|
12
|
+
WritableStream,
|
|
13
|
+
WritableStreamDefaultController,
|
|
14
|
+
WritableStreamDefaultWriter,
|
|
15
|
+
} from 'web-streams-polyfill'
|
|
16
|
+
|
|
17
|
+
const STREAM_CLASSES: Readonly<Record<string, unknown>> = {
|
|
18
|
+
ByteLengthQueuingStrategy,
|
|
19
|
+
CountQueuingStrategy,
|
|
20
|
+
ReadableByteStreamController,
|
|
21
|
+
ReadableStream,
|
|
22
|
+
ReadableStreamBYOBReader,
|
|
23
|
+
ReadableStreamBYOBRequest,
|
|
24
|
+
ReadableStreamDefaultController,
|
|
25
|
+
ReadableStreamDefaultReader,
|
|
26
|
+
TransformStream,
|
|
27
|
+
TransformStreamDefaultController,
|
|
28
|
+
WritableStream,
|
|
29
|
+
WritableStreamDefaultController,
|
|
30
|
+
WritableStreamDefaultWriter,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function installStreams(): void {
|
|
34
|
+
const scope = globalThis as Record<string, unknown>
|
|
35
|
+
if (typeof scope.ReadableStream === 'function') return
|
|
36
|
+
for (const [name, streamClass] of Object.entries(STREAM_CLASSES)) {
|
|
37
|
+
if (scope[name] !== undefined) continue
|
|
38
|
+
Object.defineProperty(globalThis, name, {
|
|
39
|
+
value: streamClass,
|
|
40
|
+
writable: true,
|
|
41
|
+
configurable: true,
|
|
42
|
+
enumerable: false,
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
}
|