@delali/narsil-react-native 0.0.0-stage → 0.3.1-next.146

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.
Files changed (186) hide show
  1. package/LICENSE +201 -0
  2. package/NarsilReactNative.podspec +57 -0
  3. package/README.md +46 -2
  4. package/android/CMakeLists.txt +48 -0
  5. package/android/build.gradle +151 -0
  6. package/android/gradle.properties +5 -0
  7. package/android/src/main/AndroidManifest.xml +2 -0
  8. package/android/src/main/cpp/AndroidEnvironment.cpp +44 -0
  9. package/android/src/main/cpp/AndroidEnvironment.hpp +11 -0
  10. package/android/src/main/cpp/cpp-adapter.cpp +11 -0
  11. package/android/src/main/java/com/margelo/nitro/narsil/NarsilAndroidEnvironment.kt +29 -0
  12. package/android/src/main/java/com/margelo/nitro/narsil/NarsilReactNativePackage.kt +22 -0
  13. package/app.plugin.js +25 -0
  14. package/cpp/AttachedField.cpp +234 -0
  15. package/cpp/AttachedField.hpp +61 -0
  16. package/cpp/Compression.cpp +113 -0
  17. package/cpp/Compression.hpp +16 -0
  18. package/cpp/GraphHeldAlone.cpp +29 -0
  19. package/cpp/GraphHeldAlone.hpp +25 -0
  20. package/cpp/HostMemory.cpp +123 -0
  21. package/cpp/HostMemory.hpp +53 -0
  22. package/cpp/HybridNarsilCoreImpl.cpp +390 -0
  23. package/cpp/HybridNarsilCoreImpl.hpp +68 -0
  24. package/cpp/HybridNarsilFilesImpl.cpp +245 -0
  25. package/cpp/HybridNarsilFilesImpl.hpp +34 -0
  26. package/cpp/HybridNarsilHostImpl.cpp +126 -0
  27. package/cpp/HybridNarsilHostImpl.hpp +42 -0
  28. package/cpp/NativeAtomics.cpp +110 -0
  29. package/cpp/NativeAtomics.hpp +11 -0
  30. package/cpp/ProcessInfo.cpp +83 -0
  31. package/cpp/ProcessInfo.hpp +11 -0
  32. package/cpp/ThreadPriority.cpp +77 -0
  33. package/cpp/ThreadPriority.hpp +8 -0
  34. package/cpp/WorkletRuntimeDispatcher.cpp +61 -0
  35. package/cpp/WorkletRuntimeDispatcher.hpp +9 -0
  36. package/cpp/core/include/narsil_core.h +208 -0
  37. package/cpp/core/src/connect.c +213 -0
  38. package/cpp/core/src/distance.c +103 -0
  39. package/cpp/core/src/files.c +99 -0
  40. package/cpp/core/src/heap.c +84 -0
  41. package/cpp/core/src/internal.h +284 -0
  42. package/cpp/core/src/kernels_code.c +209 -0
  43. package/cpp/core/src/kernels_float.c +197 -0
  44. package/cpp/core/src/kernels_pair.c +133 -0
  45. package/cpp/core/src/last_links.c +145 -0
  46. package/cpp/core/src/links.c +195 -0
  47. package/cpp/core/src/locks.c +168 -0
  48. package/cpp/core/src/nearest_links.c +147 -0
  49. package/cpp/core/src/place.c +215 -0
  50. package/cpp/core/src/quantize.c +226 -0
  51. package/cpp/core/src/records.c +97 -0
  52. package/cpp/core/src/remove.c +205 -0
  53. package/cpp/core/src/vectors.c +139 -0
  54. package/cpp/core/src/walk.c +231 -0
  55. package/cpp/core/src/workspace.c +167 -0
  56. package/ios/PrivacyInfo.xcprivacy +23 -0
  57. package/lib/client.d.ts +19 -0
  58. package/lib/client.js +41 -0
  59. package/lib/constants.d.ts +9 -0
  60. package/lib/constants.js +9 -0
  61. package/lib/engine/byte-files.d.ts +5 -0
  62. package/lib/engine/byte-files.js +97 -0
  63. package/lib/engine/core.d.ts +3 -0
  64. package/lib/engine/core.js +113 -0
  65. package/lib/engine/files.d.ts +3 -0
  66. package/lib/engine/files.js +188 -0
  67. package/lib/engine/host.d.ts +13 -0
  68. package/lib/engine/host.js +102 -0
  69. package/lib/engine/install.d.ts +4 -0
  70. package/lib/engine/install.js +29 -0
  71. package/lib/engine/native-error.d.ts +1 -0
  72. package/lib/engine/native-error.js +12 -0
  73. package/lib/engine/stand-ins/atomics.d.ts +5 -0
  74. package/lib/engine/stand-ins/atomics.js +236 -0
  75. package/lib/engine/stand-ins/crypto.d.ts +2 -0
  76. package/lib/engine/stand-ins/crypto.js +65 -0
  77. package/lib/engine/stand-ins/process.d.ts +2 -0
  78. package/lib/engine/stand-ins/process.js +22 -0
  79. package/lib/engine/stand-ins/streams.d.ts +1 -0
  80. package/lib/engine/stand-ins/streams.js +31 -0
  81. package/lib/engine/stand-ins/structured-clone.d.ts +1 -0
  82. package/lib/engine/stand-ins/structured-clone.js +121 -0
  83. package/lib/engine/streamed.d.ts +6 -0
  84. package/lib/engine/streamed.js +29 -0
  85. package/lib/engine/transfer.d.ts +3 -0
  86. package/lib/engine/transfer.js +77 -0
  87. package/lib/errors.d.ts +25 -0
  88. package/lib/errors.js +59 -0
  89. package/lib/index.d.ts +3 -0
  90. package/lib/index.js +2 -0
  91. package/lib/methods.d.ts +5 -0
  92. package/lib/methods.js +47 -0
  93. package/lib/native-objects.d.ts +2 -0
  94. package/lib/native-objects.js +24 -0
  95. package/lib/specs/core.nitro.d.ts +79 -0
  96. package/lib/specs/core.nitro.js +1 -0
  97. package/lib/specs/files.nitro.d.ts +36 -0
  98. package/lib/specs/files.nitro.js +1 -0
  99. package/lib/specs/host.nitro.d.ts +24 -0
  100. package/lib/specs/host.nitro.js +1 -0
  101. package/lib/transferred.d.ts +15 -0
  102. package/lib/transferred.js +6 -0
  103. package/lib/types.d.ts +136 -0
  104. package/lib/types.js +1 -0
  105. package/lib/worklets/app-turns.d.ts +1 -0
  106. package/lib/worklets/app-turns.js +44 -0
  107. package/lib/worklets/client.d.ts +30 -0
  108. package/lib/worklets/client.js +144 -0
  109. package/lib/worklets/index.d.ts +4 -0
  110. package/lib/worklets/index.js +2 -0
  111. package/lib/worklets/instances.d.ts +15 -0
  112. package/lib/worklets/instances.js +194 -0
  113. package/lib/worklets/json-part.d.ts +1 -0
  114. package/lib/worklets/json-part.js +42 -0
  115. package/lib/worklets/runtime.d.ts +25 -0
  116. package/lib/worklets/runtime.js +80 -0
  117. package/lib/worklets/transfer-queue.d.ts +7 -0
  118. package/lib/worklets/transfer-queue.js +79 -0
  119. package/lib/worklets/types.d.ts +26 -0
  120. package/lib/worklets/types.js +1 -0
  121. package/nitro.json +32 -0
  122. package/nitrogen/generated/.gitattributes +1 -0
  123. package/nitrogen/generated/android/NarsilReactNative+autolinking.cmake +85 -0
  124. package/nitrogen/generated/android/NarsilReactNative+autolinking.gradle +27 -0
  125. package/nitrogen/generated/android/NarsilReactNativeOnLoad.cpp +69 -0
  126. package/nitrogen/generated/android/NarsilReactNativeOnLoad.hpp +34 -0
  127. package/nitrogen/generated/android/kotlin/com/margelo/nitro/narsil/NarsilReactNativeOnLoad.kt +35 -0
  128. package/nitrogen/generated/ios/NarsilReactNative+autolinking.rb +62 -0
  129. package/nitrogen/generated/ios/NarsilReactNative-Swift-Cxx-Bridge.cpp +17 -0
  130. package/nitrogen/generated/ios/NarsilReactNative-Swift-Cxx-Bridge.hpp +27 -0
  131. package/nitrogen/generated/ios/NarsilReactNative-Swift-Cxx-Umbrella.hpp +38 -0
  132. package/nitrogen/generated/ios/NarsilReactNativeAutolinking.mm +55 -0
  133. package/nitrogen/generated/ios/NarsilReactNativeAutolinking.swift +16 -0
  134. package/nitrogen/generated/shared/c++/DirectoryEntry.hpp +91 -0
  135. package/nitrogen/generated/shared/c++/FileStatus.hpp +103 -0
  136. package/nitrogen/generated/shared/c++/GraphMemory.hpp +120 -0
  137. package/nitrogen/generated/shared/c++/HybridNarsilAttachedGraphSpec.cpp +21 -0
  138. package/nitrogen/generated/shared/c++/HybridNarsilAttachedGraphSpec.hpp +62 -0
  139. package/nitrogen/generated/shared/c++/HybridNarsilAttachedStoreSpec.cpp +21 -0
  140. package/nitrogen/generated/shared/c++/HybridNarsilAttachedStoreSpec.hpp +62 -0
  141. package/nitrogen/generated/shared/c++/HybridNarsilCoreSpec.cpp +35 -0
  142. package/nitrogen/generated/shared/c++/HybridNarsilCoreSpec.hpp +100 -0
  143. package/nitrogen/generated/shared/c++/HybridNarsilFilesSpec.cpp +37 -0
  144. package/nitrogen/generated/shared/c++/HybridNarsilFilesSpec.hpp +86 -0
  145. package/nitrogen/generated/shared/c++/HybridNarsilHostSpec.cpp +34 -0
  146. package/nitrogen/generated/shared/c++/HybridNarsilHostSpec.hpp +78 -0
  147. package/nitrogen/generated/shared/c++/MemoryView.hpp +91 -0
  148. package/nitrogen/generated/shared/c++/NativeConnectOutcome.hpp +87 -0
  149. package/nitrogen/generated/shared/c++/NativeScores.hpp +87 -0
  150. package/nitrogen/generated/shared/c++/NativeSearchHits.hpp +91 -0
  151. package/nitrogen/generated/shared/c++/ProcessMemory.hpp +87 -0
  152. package/nitrogen/generated/shared/c++/StoreMemory.hpp +142 -0
  153. package/package.json +87 -3
  154. package/react-native.config.js +8 -0
  155. package/src/client.ts +44 -0
  156. package/src/constants.ts +9 -0
  157. package/src/engine/byte-files.ts +99 -0
  158. package/src/engine/core.ts +162 -0
  159. package/src/engine/files.ts +186 -0
  160. package/src/engine/host.ts +111 -0
  161. package/src/engine/install.ts +31 -0
  162. package/src/engine/native-error.ts +10 -0
  163. package/src/engine/stand-ins/atomics.ts +288 -0
  164. package/src/engine/stand-ins/crypto.ts +81 -0
  165. package/src/engine/stand-ins/process.ts +30 -0
  166. package/src/engine/stand-ins/streams.ts +45 -0
  167. package/src/engine/stand-ins/structured-clone.ts +118 -0
  168. package/src/engine/streamed.ts +40 -0
  169. package/src/engine/transfer.ts +72 -0
  170. package/src/errors.ts +73 -0
  171. package/src/index.ts +3 -0
  172. package/src/methods.ts +52 -0
  173. package/src/native-objects.ts +30 -0
  174. package/src/specs/core.nitro.ts +140 -0
  175. package/src/specs/files.nitro.ts +42 -0
  176. package/src/specs/host.nitro.ts +30 -0
  177. package/src/transferred.ts +18 -0
  178. package/src/types.ts +164 -0
  179. package/src/worklets/app-turns.ts +48 -0
  180. package/src/worklets/client.ts +165 -0
  181. package/src/worklets/index.ts +4 -0
  182. package/src/worklets/instances.ts +231 -0
  183. package/src/worklets/json-part.ts +38 -0
  184. package/src/worklets/runtime.ts +190 -0
  185. package/src/worklets/transfer-queue.ts +98 -0
  186. 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
+ }