@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,231 @@
|
|
|
1
|
+
#include "internal.h"
|
|
2
|
+
|
|
3
|
+
#include "../include/narsil_core.h"
|
|
4
|
+
|
|
5
|
+
#include <math.h>
|
|
6
|
+
#include <stdatomic.h>
|
|
7
|
+
#include <stddef.h>
|
|
8
|
+
#include <stdint.h>
|
|
9
|
+
#include <string.h>
|
|
10
|
+
|
|
11
|
+
static int lists_fit_the_workspace(const narsil_graph *graph) {
|
|
12
|
+
int32_t max_neighbours = load_word(graph->header, NARSIL_GRAPH_WORD_M);
|
|
13
|
+
int32_t max_base_neighbours = load_word(graph->header, NARSIL_GRAPH_WORD_MMAX0);
|
|
14
|
+
if (max_neighbours <= 0 || max_base_neighbours <= 0) { return 0; }
|
|
15
|
+
return max_neighbours < MAX_NEIGHBOURS_PER_LIST && max_base_neighbours < MAX_NEIGHBOURS_PER_LIST;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
int graph_is_complete(const narsil_graph *graph) {
|
|
19
|
+
return graph->header != NULL && graph->node_levels != NULL && graph->level0 != NULL && graph->upper_base != NULL &&
|
|
20
|
+
graph->upper != NULL && graph->locks != NULL && graph->tombstones != NULL && graph->incoming != NULL &&
|
|
21
|
+
graph->nearest_link_due != NULL && graph->held_locks != NULL;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static void begin_visit_round(narsil_workspace *workspace) {
|
|
25
|
+
workspace->stamp += 1;
|
|
26
|
+
if (workspace->stamp == UINT32_MAX) {
|
|
27
|
+
memset(workspace->visited, 0, (size_t)workspace->visited_capacity * sizeof *workspace->visited);
|
|
28
|
+
workspace->stamp = 1;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static int claim_unvisited_node(const walk_context *context, int32_t ordinal) {
|
|
33
|
+
narsil_workspace *workspace = context->workspace;
|
|
34
|
+
if (ordinal < 0 || (uint32_t)ordinal >= context->slots || workspace->visited[ordinal] == workspace->stamp) {
|
|
35
|
+
return 0;
|
|
36
|
+
}
|
|
37
|
+
workspace->visited[ordinal] = workspace->stamp;
|
|
38
|
+
return node_top_layer(context, ordinal) >= 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static int accepts(narsil_ordinal_set accepted, int32_t ordinal) {
|
|
42
|
+
if (accepted.bits == NULL) { return 1; }
|
|
43
|
+
uint32_t index = (uint32_t)ordinal / BITS_PER_BYTE;
|
|
44
|
+
if (index >= accepted.bytes) { return 0; }
|
|
45
|
+
return (accepted.bits[index] & (1U << ((uint32_t)ordinal % BITS_PER_BYTE))) != 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static narsil_status keep_candidate(const walk_context *context, layer_search search, scored_node node,
|
|
49
|
+
double *furthest) {
|
|
50
|
+
narsil_workspace *workspace = context->workspace;
|
|
51
|
+
narsil_status status = heap_push(&workspace->frontier, node);
|
|
52
|
+
if (status != NARSIL_OK) { return status; }
|
|
53
|
+
if (search.excludes_tombstones && is_tombstoned(context, node.ordinal)) { return NARSIL_OK; }
|
|
54
|
+
if (!accepts(search.accepted, node.ordinal)) { return NARSIL_OK; }
|
|
55
|
+
status = heap_push(&workspace->found, node);
|
|
56
|
+
if (status != NARSIL_OK) { return status; }
|
|
57
|
+
if (workspace->found.size > search.candidate_limit) { heap_pop(&workspace->found); }
|
|
58
|
+
*furthest = workspace->found.distances[0];
|
|
59
|
+
return NARSIL_OK;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
static narsil_status seed_entry_points(const walk_context *context, layer_search search, double *furthest) {
|
|
63
|
+
narsil_workspace *workspace = context->workspace;
|
|
64
|
+
for (uint32_t i = 0; i < workspace->entry_point_count; i++) {
|
|
65
|
+
int32_t entry = workspace->entry_points[i];
|
|
66
|
+
if (!claim_unvisited_node(context, entry)) { continue; }
|
|
67
|
+
double distance = walk_distance(context, entry);
|
|
68
|
+
if (distance == HUGE_VAL) { continue; }
|
|
69
|
+
scored_node node = {entry, distance};
|
|
70
|
+
narsil_status status = keep_candidate(context, search, node, furthest);
|
|
71
|
+
if (status != NARSIL_OK) { return status; }
|
|
72
|
+
}
|
|
73
|
+
return NARSIL_OK;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
static narsil_status expand_node(const walk_context *context, layer_search search, double *furthest) {
|
|
77
|
+
narsil_workspace *workspace = context->workspace;
|
|
78
|
+
int32_t pending[MAX_NEIGHBOURS_PER_LIST];
|
|
79
|
+
node_layer position = {workspace->frontier.top.ordinal, search.layer};
|
|
80
|
+
uint32_t count = read_neighbours(context, position, workspace->neighbours);
|
|
81
|
+
uint32_t pending_count = 0;
|
|
82
|
+
for (uint32_t i = 0; i < count; i++) {
|
|
83
|
+
int32_t neighbour = workspace->neighbours[i];
|
|
84
|
+
if (!claim_unvisited_node(context, neighbour)) { continue; }
|
|
85
|
+
pending[pending_count] = neighbour;
|
|
86
|
+
pending_count += 1;
|
|
87
|
+
prefetch_for_walk(context, neighbour);
|
|
88
|
+
}
|
|
89
|
+
for (uint32_t i = 0; i < pending_count; i++) {
|
|
90
|
+
double distance = walk_distance(context, pending[i]);
|
|
91
|
+
if (distance == HUGE_VAL) { continue; }
|
|
92
|
+
if (distance < *furthest || workspace->found.size < search.candidate_limit) {
|
|
93
|
+
scored_node node = {pending[i], distance};
|
|
94
|
+
narsil_status status = keep_candidate(context, search, node, furthest);
|
|
95
|
+
if (status != NARSIL_OK) { return status; }
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return NARSIL_OK;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
static narsil_status drain_found(distance_heap *found, narsil_candidates *out) {
|
|
102
|
+
uint32_t size = found->size;
|
|
103
|
+
if (size > out->capacity) { return NARSIL_INVALID_ARGUMENT; }
|
|
104
|
+
out->count = size;
|
|
105
|
+
for (uint32_t i = size; i > 0; i--) {
|
|
106
|
+
heap_pop(found);
|
|
107
|
+
out->ordinals[i - 1] = found->top.ordinal;
|
|
108
|
+
out->distances[i - 1] = found->top.distance;
|
|
109
|
+
}
|
|
110
|
+
return NARSIL_OK;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
narsil_status search_layer(const walk_context *context, layer_search search, narsil_candidates *out) {
|
|
114
|
+
narsil_workspace *workspace = context->workspace;
|
|
115
|
+
begin_visit_round(workspace);
|
|
116
|
+
workspace->frontier.size = 0;
|
|
117
|
+
workspace->found.size = 0;
|
|
118
|
+
double furthest = HUGE_VAL;
|
|
119
|
+
narsil_status status = seed_entry_points(context, search, &furthest);
|
|
120
|
+
if (status != NARSIL_OK) { return status; }
|
|
121
|
+
while (heap_pop(&workspace->frontier)) {
|
|
122
|
+
if (workspace->frontier.top.distance > furthest && workspace->found.size >= search.candidate_limit) { break; }
|
|
123
|
+
status = expand_node(context, search, &furthest);
|
|
124
|
+
if (status != NARSIL_OK) { return status; }
|
|
125
|
+
}
|
|
126
|
+
return drain_found(&workspace->found, out);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
static double centroid_dot_of(const narsil_store *store) {
|
|
130
|
+
double centroid_dot = 0;
|
|
131
|
+
for (uint32_t i = 0; i < store->dimension; i++) {
|
|
132
|
+
centroid_dot += (double)store->centroid[i] * (double)store->centroid[i];
|
|
133
|
+
}
|
|
134
|
+
return centroid_dot;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
narsil_status begin_walk(walk_context *context, narsil_workspace *workspace, const narsil_graph *graph,
|
|
138
|
+
const narsil_store *store, uint32_t metric) {
|
|
139
|
+
if (workspace == NULL || graph == NULL || store == NULL) { return NARSIL_INVALID_ARGUMENT; }
|
|
140
|
+
if (!graph_is_complete(graph) || !store_is_complete(store)) { return NARSIL_INVALID_ARGUMENT; }
|
|
141
|
+
if (store->dimension == 0 || !is_metric(metric) || !is_code_width(store->bits)) { return NARSIL_INVALID_ARGUMENT; }
|
|
142
|
+
if (!lists_fit_the_workspace(graph)) { return NARSIL_INVALID_ARGUMENT; }
|
|
143
|
+
|
|
144
|
+
memset(context, 0, sizeof *context);
|
|
145
|
+
context->graph = graph;
|
|
146
|
+
context->store = store;
|
|
147
|
+
context->workspace = workspace;
|
|
148
|
+
context->metric = (narsil_metric)metric;
|
|
149
|
+
context->fence = &workspace->fence;
|
|
150
|
+
int holds_codes = store->bits != 0 && store->records_per_block > 0;
|
|
151
|
+
int calibrated = holds_codes && load_word(store->header, NARSIL_STORE_WORD_CALIBRATED) == 1;
|
|
152
|
+
context->pairs_use_codes = calibrated;
|
|
153
|
+
context->uses_codes = calibrated && load_word(store->header, NARSIL_STORE_WORD_CODE_COUNT) > 0;
|
|
154
|
+
if (holds_codes) {
|
|
155
|
+
context->code_bytes = code_bytes_of(store->dimension, store->bits);
|
|
156
|
+
context->record_bytes = context->code_bytes + NARSIL_OSQ_TRAILER_BYTES;
|
|
157
|
+
}
|
|
158
|
+
if (context->uses_codes || context->pairs_use_codes) { context->centroid_dot = centroid_dot_of(store); }
|
|
159
|
+
return workspace_reserve_dimension(workspace, store->dimension);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
void walk_with_query(walk_context *context, const float *vector) {
|
|
163
|
+
context->query.values = vector;
|
|
164
|
+
if (context->uses_codes) {
|
|
165
|
+
prepare_code(context->workspace, context->store, context->metric, vector, &context->code);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
context->query.magnitude = (double)kernel_magnitude(vector, context->store->dimension);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
void walk_with_placement_query(walk_context *context, const float *vector) {
|
|
172
|
+
if (!context->uses_codes) {
|
|
173
|
+
walk_with_query(context, vector);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
context->query.values = vector;
|
|
177
|
+
const narsil_store *store = context->store;
|
|
178
|
+
prepare_code_with_bits(context->workspace, store, context->metric, vector, placement_query_bits_of(store->bits),
|
|
179
|
+
&context->code);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
narsil_status read_graph_extent(walk_context *context) {
|
|
183
|
+
int32_t slots = load_word(context->graph->header, NARSIL_GRAPH_WORD_SLOTS);
|
|
184
|
+
int32_t store_slots = load_word(context->store->header, NARSIL_STORE_WORD_SLOTS);
|
|
185
|
+
context->slots = slots < 0 ? 0 : (uint32_t)slots;
|
|
186
|
+
context->store_slots = store_slots < 0 ? 0 : (uint32_t)store_slots;
|
|
187
|
+
return workspace_reserve_visited(context->workspace, context->slots);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
void use_single_entry(narsil_workspace *workspace, int32_t ordinal) {
|
|
191
|
+
workspace->entry_points[0] = ordinal;
|
|
192
|
+
workspace->entry_point_count = 1;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
narsil_status descend_layers(const walk_context *context, int32_t first, int32_t last, narsil_candidates *out) {
|
|
196
|
+
for (int32_t layer = first; layer >= last; layer--) {
|
|
197
|
+
layer_search search = {1, layer, 0, {NULL, 0}};
|
|
198
|
+
narsil_status status = search_layer(context, search, out);
|
|
199
|
+
if (status != NARSIL_OK) { return status; }
|
|
200
|
+
if (out->count > 0) { use_single_entry(context->workspace, out->ordinals[0]); }
|
|
201
|
+
}
|
|
202
|
+
return NARSIL_OK;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
narsil_status narsil_search(narsil_workspace *workspace, const narsil_graph *graph, const narsil_store *store,
|
|
206
|
+
const narsil_search_request *request, narsil_candidates *nearest) {
|
|
207
|
+
if (request == NULL || request->query == NULL || nearest == NULL) { return NARSIL_INVALID_ARGUMENT; }
|
|
208
|
+
if (request->candidate_count == 0 || nearest->capacity < request->candidate_count) { return NARSIL_INVALID_ARGUMENT; }
|
|
209
|
+
walk_context context;
|
|
210
|
+
narsil_status status = begin_walk(&context, workspace, graph, store, request->metric);
|
|
211
|
+
if (status != NARSIL_OK) { return status; }
|
|
212
|
+
if (request->thread_slot >= graph->thread_slots) { return NARSIL_INVALID_ARGUMENT; }
|
|
213
|
+
context.thread_slot = request->thread_slot;
|
|
214
|
+
walk_with_query(&context, request->query);
|
|
215
|
+
nearest->count = 0;
|
|
216
|
+
|
|
217
|
+
graph_lock_shared(graph, request->thread_slot);
|
|
218
|
+
status = read_graph_extent(&context);
|
|
219
|
+
int32_t entry = load_word(graph->header, NARSIL_GRAPH_WORD_ENTRY_POINT);
|
|
220
|
+
if (status == NARSIL_OK && entry != NO_ENTRY_POINT) {
|
|
221
|
+
status = workspace_reserve_entry_points(workspace, 1);
|
|
222
|
+
if (status == NARSIL_OK) {
|
|
223
|
+
use_single_entry(workspace, entry);
|
|
224
|
+
status = descend_layers(&context, load_word(graph->header, NARSIL_GRAPH_WORD_TOP_LAYER), 1, nearest);
|
|
225
|
+
}
|
|
226
|
+
layer_search base = {request->candidate_count, 0, 1, request->accepted};
|
|
227
|
+
if (status == NARSIL_OK) { status = search_layer(&context, base, nearest); }
|
|
228
|
+
}
|
|
229
|
+
graph_unlock_shared(graph, request->thread_slot);
|
|
230
|
+
return status;
|
|
231
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
#include "internal.h"
|
|
2
|
+
|
|
3
|
+
#include "../include/narsil_core.h"
|
|
4
|
+
|
|
5
|
+
#include <stddef.h>
|
|
6
|
+
#include <stdint.h>
|
|
7
|
+
#include <stdlib.h>
|
|
8
|
+
#include <string.h>
|
|
9
|
+
|
|
10
|
+
#define INITIAL_VISITED_CAPACITY 1024
|
|
11
|
+
#define INITIAL_ENTRY_POINT_CAPACITY 256
|
|
12
|
+
#define WIDEST_PLANE_CODE_BITS 2
|
|
13
|
+
#define STORED_VECTOR_COPIES 3
|
|
14
|
+
|
|
15
|
+
int32_t narsil_core_abi_version(void) { return NARSIL_CORE_ABI_VERSION; }
|
|
16
|
+
|
|
17
|
+
uint32_t grown_capacity(uint32_t first, uint32_t needed) {
|
|
18
|
+
uint32_t capacity = first;
|
|
19
|
+
while (capacity < needed) {
|
|
20
|
+
if (capacity > UINT32_MAX / 2) { return needed; }
|
|
21
|
+
capacity *= 2;
|
|
22
|
+
}
|
|
23
|
+
return capacity;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
narsil_status narsil_workspace_create(narsil_workspace **workspace) {
|
|
27
|
+
if (workspace == NULL) { return NARSIL_INVALID_ARGUMENT; }
|
|
28
|
+
narsil_workspace *created = calloc(1, sizeof *created);
|
|
29
|
+
if (created == NULL) { return NARSIL_OUT_OF_MEMORY; }
|
|
30
|
+
created->found.greatest_first = 1;
|
|
31
|
+
*workspace = created;
|
|
32
|
+
return NARSIL_OK;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static void release_code(prepared_code *code) {
|
|
36
|
+
free(code->levels);
|
|
37
|
+
free(code->low_levels);
|
|
38
|
+
free(code->high_levels);
|
|
39
|
+
free(code->planes);
|
|
40
|
+
memset(code, 0, sizeof *code);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static void release_dimension_scratch(narsil_workspace *workspace) {
|
|
44
|
+
release_code(&workspace->code);
|
|
45
|
+
free(workspace->normalised);
|
|
46
|
+
free(workspace->centred);
|
|
47
|
+
free(workspace->placed_vector);
|
|
48
|
+
free(workspace->stored_vector);
|
|
49
|
+
free(workspace->other_vector);
|
|
50
|
+
workspace->normalised = NULL;
|
|
51
|
+
workspace->centred = NULL;
|
|
52
|
+
workspace->placed_vector = NULL;
|
|
53
|
+
workspace->stored_vector = NULL;
|
|
54
|
+
workspace->other_vector = NULL;
|
|
55
|
+
workspace->dimension_capacity = 0;
|
|
56
|
+
workspace->dimension_scratch_bytes = 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
void narsil_workspace_destroy(narsil_workspace *workspace) {
|
|
60
|
+
if (workspace == NULL) { return; }
|
|
61
|
+
heap_release(&workspace->frontier);
|
|
62
|
+
heap_release(&workspace->found);
|
|
63
|
+
release_dimension_scratch(workspace);
|
|
64
|
+
list_release(&workspace->layer_candidates);
|
|
65
|
+
list_release(&workspace->link_candidates);
|
|
66
|
+
list_release(&workspace->repair_candidates);
|
|
67
|
+
list_release(&workspace->dropped_links);
|
|
68
|
+
free(workspace->selections);
|
|
69
|
+
free(workspace->selection_counts);
|
|
70
|
+
free(workspace->visited);
|
|
71
|
+
free(workspace->entry_points);
|
|
72
|
+
free(workspace);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
static size_t list_bytes(const scored_list *list) {
|
|
76
|
+
return (size_t)list->capacity * (sizeof(int32_t) + sizeof(double));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
static size_t link_bytes(const narsil_workspace *workspace) {
|
|
80
|
+
size_t selected = (size_t)workspace->selection_layers * workspace->selection_stride * sizeof(int32_t);
|
|
81
|
+
size_t counts = (size_t)workspace->selection_layers * sizeof(uint32_t);
|
|
82
|
+
return list_bytes(&workspace->layer_candidates) + list_bytes(&workspace->link_candidates) +
|
|
83
|
+
list_bytes(&workspace->repair_candidates) + list_bytes(&workspace->dropped_links) + selected + counts;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
narsil_status workspace_reserve_links(narsil_workspace *workspace, const narsil_graph *graph, uint32_t layers) {
|
|
87
|
+
uint32_t stride = list_limit(graph, 0) > list_limit(graph, 1) ? list_limit(graph, 0) : list_limit(graph, 1);
|
|
88
|
+
if (layers <= workspace->selection_layers && stride <= workspace->selection_stride) { return NARSIL_OK; }
|
|
89
|
+
uint32_t held_layers = layers > workspace->selection_layers ? layers : workspace->selection_layers;
|
|
90
|
+
uint32_t held_stride = stride > workspace->selection_stride ? stride : workspace->selection_stride;
|
|
91
|
+
int32_t *selections = calloc((size_t)held_layers * held_stride, sizeof *selections);
|
|
92
|
+
uint32_t *counts = calloc(held_layers, sizeof *counts);
|
|
93
|
+
if (selections == NULL || counts == NULL) {
|
|
94
|
+
free(selections);
|
|
95
|
+
free(counts);
|
|
96
|
+
return NARSIL_OUT_OF_MEMORY;
|
|
97
|
+
}
|
|
98
|
+
free(workspace->selections);
|
|
99
|
+
free(workspace->selection_counts);
|
|
100
|
+
workspace->selections = selections;
|
|
101
|
+
workspace->selection_counts = counts;
|
|
102
|
+
workspace->selection_layers = held_layers;
|
|
103
|
+
workspace->selection_stride = held_stride;
|
|
104
|
+
return NARSIL_OK;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
size_t narsil_workspace_bytes(const narsil_workspace *workspace) {
|
|
108
|
+
if (workspace == NULL) { return 0; }
|
|
109
|
+
size_t heap_entry = sizeof(int32_t) + sizeof(double);
|
|
110
|
+
return sizeof *workspace + ((size_t)workspace->frontier.capacity * heap_entry) +
|
|
111
|
+
((size_t)workspace->found.capacity * heap_entry) + ((size_t)workspace->visited_capacity * sizeof(uint32_t)) +
|
|
112
|
+
((size_t)workspace->entry_point_capacity * sizeof(int32_t)) + workspace->dimension_scratch_bytes +
|
|
113
|
+
link_bytes(workspace);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
narsil_status workspace_reserve_dimension(narsil_workspace *workspace, uint32_t dimension) {
|
|
117
|
+
if (dimension <= workspace->dimension_capacity) { return NARSIL_OK; }
|
|
118
|
+
release_dimension_scratch(workspace);
|
|
119
|
+
size_t pairs = ((size_t)dimension + 1) / 2;
|
|
120
|
+
size_t planes = (size_t)QUERY_PLANES * code_bytes_of(dimension, WIDEST_PLANE_CODE_BITS);
|
|
121
|
+
workspace->normalised = calloc(dimension, sizeof(double));
|
|
122
|
+
workspace->centred = calloc(dimension, sizeof(double));
|
|
123
|
+
workspace->placed_vector = calloc(dimension, sizeof(float));
|
|
124
|
+
workspace->stored_vector = calloc(dimension, sizeof(float));
|
|
125
|
+
workspace->other_vector = calloc(dimension, sizeof(float));
|
|
126
|
+
workspace->code.levels = calloc(dimension, 1);
|
|
127
|
+
workspace->code.low_levels = calloc(pairs, 1);
|
|
128
|
+
workspace->code.high_levels = calloc(pairs, 1);
|
|
129
|
+
workspace->code.planes = calloc(planes, 1);
|
|
130
|
+
if (workspace->normalised == NULL || workspace->centred == NULL || workspace->placed_vector == NULL ||
|
|
131
|
+
workspace->stored_vector == NULL || workspace->other_vector == NULL || workspace->code.levels == NULL ||
|
|
132
|
+
workspace->code.low_levels == NULL || workspace->code.high_levels == NULL || workspace->code.planes == NULL) {
|
|
133
|
+
release_dimension_scratch(workspace);
|
|
134
|
+
return NARSIL_OUT_OF_MEMORY;
|
|
135
|
+
}
|
|
136
|
+
workspace->dimension_capacity = dimension;
|
|
137
|
+
workspace->dimension_scratch_bytes = (2 * (size_t)dimension * sizeof(double)) +
|
|
138
|
+
(STORED_VECTOR_COPIES * (size_t)dimension * sizeof(float)) + dimension +
|
|
139
|
+
(2 * pairs) + planes;
|
|
140
|
+
return NARSIL_OK;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
narsil_status workspace_reserve_visited(narsil_workspace *workspace, uint32_t slots) {
|
|
144
|
+
if (slots <= workspace->visited_capacity) { return NARSIL_OK; }
|
|
145
|
+
uint32_t held = workspace->visited_capacity == 0 ? INITIAL_VISITED_CAPACITY : workspace->visited_capacity;
|
|
146
|
+
uint32_t capacity = grown_capacity(held, slots);
|
|
147
|
+
uint32_t *visited = calloc(capacity, sizeof *visited);
|
|
148
|
+
if (visited == NULL) { return NARSIL_OUT_OF_MEMORY; }
|
|
149
|
+
if (workspace->visited != NULL) {
|
|
150
|
+
memcpy(visited, workspace->visited, (size_t)workspace->visited_capacity * sizeof *visited);
|
|
151
|
+
free(workspace->visited);
|
|
152
|
+
}
|
|
153
|
+
workspace->visited = visited;
|
|
154
|
+
workspace->visited_capacity = capacity;
|
|
155
|
+
return NARSIL_OK;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
narsil_status workspace_reserve_entry_points(narsil_workspace *workspace, uint32_t count) {
|
|
159
|
+
if (count <= workspace->entry_point_capacity) { return NARSIL_OK; }
|
|
160
|
+
uint32_t held = workspace->entry_point_capacity == 0 ? INITIAL_ENTRY_POINT_CAPACITY : workspace->entry_point_capacity;
|
|
161
|
+
uint32_t capacity = grown_capacity(held, count);
|
|
162
|
+
int32_t *entry_points = realloc(workspace->entry_points, (size_t)capacity * sizeof *entry_points);
|
|
163
|
+
if (entry_points == NULL) { return NARSIL_OUT_OF_MEMORY; }
|
|
164
|
+
workspace->entry_points = entry_points;
|
|
165
|
+
workspace->entry_point_capacity = capacity;
|
|
166
|
+
return NARSIL_OK;
|
|
167
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>NSPrivacyTracking</key>
|
|
6
|
+
<false/>
|
|
7
|
+
<key>NSPrivacyTrackingDomains</key>
|
|
8
|
+
<array/>
|
|
9
|
+
<key>NSPrivacyCollectedDataTypes</key>
|
|
10
|
+
<array/>
|
|
11
|
+
<key>NSPrivacyAccessedAPITypes</key>
|
|
12
|
+
<array>
|
|
13
|
+
<dict>
|
|
14
|
+
<key>NSPrivacyAccessedAPIType</key>
|
|
15
|
+
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
|
16
|
+
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
17
|
+
<array>
|
|
18
|
+
<string>C617.1</string>
|
|
19
|
+
</array>
|
|
20
|
+
</dict>
|
|
21
|
+
</array>
|
|
22
|
+
</dict>
|
|
23
|
+
</plist>
|
package/lib/client.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { PhoneNarsil, PhoneNarsilConfig } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Starts a Narsil engine on the app's own JS runtime and resolves with a
|
|
4
|
+
* client whose methods each return a promise. The engine takes the same
|
|
5
|
+
* settings as on Node.js, function settings such as embedding adapters,
|
|
6
|
+
* plugins, and an id generator included. File reads, file writes, file
|
|
7
|
+
* syncs, and vector searches run on native threads, and a long import or
|
|
8
|
+
* search yields to the app between steps.
|
|
9
|
+
*
|
|
10
|
+
* @param config - The engine settings. `durability` defaults to a
|
|
11
|
+
* write-ahead log in the `narsil` directory of the app's private storage,
|
|
12
|
+
* and `null` keeps every index in memory alone.
|
|
13
|
+
* @returns The client, once the engine has started.
|
|
14
|
+
* @throws A `NarsilError` with `NATIVE_DEPENDENCY_MISSING` when the app's
|
|
15
|
+
* build lacks the native module, as in Expo Go.
|
|
16
|
+
*
|
|
17
|
+
* @public
|
|
18
|
+
*/
|
|
19
|
+
export declare function createNarsil(config?: PhoneNarsilConfig): Promise<PhoneNarsil>;
|
package/lib/client.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { createNarsil as createEngine } from '@delali/narsil';
|
|
2
|
+
import { installEngineHost, installedHost } from './engine/install';
|
|
3
|
+
import { callStreamed, withPhoneDurability } from './engine/streamed';
|
|
4
|
+
import { FORWARDED_METHODS, STREAMED_METHODS } from './methods';
|
|
5
|
+
import { loadNativeObjects } from './native-objects';
|
|
6
|
+
function phoneNarsil(narsil, host) {
|
|
7
|
+
const client = {};
|
|
8
|
+
for (const method of FORWARDED_METHODS) {
|
|
9
|
+
const target = narsil[method];
|
|
10
|
+
client[method] = async (...args) => target.apply(narsil, args);
|
|
11
|
+
}
|
|
12
|
+
for (const method of STREAMED_METHODS) {
|
|
13
|
+
client[method] = async (...args) => callStreamed(narsil, host, method, args);
|
|
14
|
+
}
|
|
15
|
+
client.on = narsil.on.bind(narsil);
|
|
16
|
+
client.off = narsil.off.bind(narsil);
|
|
17
|
+
client.shutdown = async () => narsil.shutdown();
|
|
18
|
+
return client;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Starts a Narsil engine on the app's own JS runtime and resolves with a
|
|
22
|
+
* client whose methods each return a promise. The engine takes the same
|
|
23
|
+
* settings as on Node.js, function settings such as embedding adapters,
|
|
24
|
+
* plugins, and an id generator included. File reads, file writes, file
|
|
25
|
+
* syncs, and vector searches run on native threads, and a long import or
|
|
26
|
+
* search yields to the app between steps.
|
|
27
|
+
*
|
|
28
|
+
* @param config - The engine settings. `durability` defaults to a
|
|
29
|
+
* write-ahead log in the `narsil` directory of the app's private storage,
|
|
30
|
+
* and `null` keeps every index in memory alone.
|
|
31
|
+
* @returns The client, once the engine has started.
|
|
32
|
+
* @throws A `NarsilError` with `NATIVE_DEPENDENCY_MISSING` when the app's
|
|
33
|
+
* build lacks the native module, as in Expo Go.
|
|
34
|
+
*
|
|
35
|
+
* @public
|
|
36
|
+
*/
|
|
37
|
+
export async function createNarsil(config = {}) {
|
|
38
|
+
installEngineHost(await loadNativeObjects(), true);
|
|
39
|
+
const narsil = await createEngine(withPhoneDurability(config));
|
|
40
|
+
return phoneNarsil(narsil, installedHost());
|
|
41
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const FILE_CHUNK_BYTES = 1048576;
|
|
2
|
+
export declare const DEFAULT_DURABILITY_DIRECTORY = "narsil";
|
|
3
|
+
export declare const ENGINE_RUNTIME_NAME = "narsil-engine";
|
|
4
|
+
export declare const WORKLET_TRANSFER_TURN_BUDGET_MS = 8;
|
|
5
|
+
export declare const WORKLET_TRANSFER_PART_TARGET_MS = 4;
|
|
6
|
+
export declare const WORKLET_TRANSFER_FIRST_PART_ITEMS = 32;
|
|
7
|
+
export declare const WORKLET_TRANSFER_LARGEST_PART_ITEMS = 4096;
|
|
8
|
+
export declare const WORKLET_JSON_PART_MAX_DEPTH = 64;
|
|
9
|
+
export declare const APP_TIMER_WAIT_BEFORE_RUN_LOOP_HOP_MS = 100;
|
package/lib/constants.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const FILE_CHUNK_BYTES = 1_048_576;
|
|
2
|
+
export const DEFAULT_DURABILITY_DIRECTORY = 'narsil';
|
|
3
|
+
export const ENGINE_RUNTIME_NAME = 'narsil-engine';
|
|
4
|
+
export const WORKLET_TRANSFER_TURN_BUDGET_MS = 8;
|
|
5
|
+
export const WORKLET_TRANSFER_PART_TARGET_MS = 4;
|
|
6
|
+
export const WORKLET_TRANSFER_FIRST_PART_ITEMS = 32;
|
|
7
|
+
export const WORKLET_TRANSFER_LARGEST_PART_ITEMS = 4_096;
|
|
8
|
+
export const WORKLET_JSON_PART_MAX_DEPTH = 64;
|
|
9
|
+
export const APP_TIMER_WAIT_BEFORE_RUN_LOOP_HOP_MS = 100;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ReactNativeFiles, ReactNativeHost } from '@delali/narsil';
|
|
2
|
+
export declare function resolveAppPath(host: ReactNativeHost, path: string): string;
|
|
3
|
+
export declare function writeStreamToFile(files: ReactNativeFiles, stream: ReadableStream<Uint8Array>, path: string): Promise<number>;
|
|
4
|
+
export declare function fileChunks(files: ReactNativeFiles, path: string): AsyncGenerator<Uint8Array>;
|
|
5
|
+
export declare function collectStream(stream: ReadableStream<Uint8Array>): Promise<Uint8Array>;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { FILE_CHUNK_BYTES } from '../constants';
|
|
2
|
+
const CURRENT_POSITION = -1;
|
|
3
|
+
function invalidPath(message) {
|
|
4
|
+
return Object.assign(new TypeError(message), { code: 'EINVAL' });
|
|
5
|
+
}
|
|
6
|
+
export function resolveAppPath(host, path) {
|
|
7
|
+
if (typeof path !== 'string' || path.length === 0)
|
|
8
|
+
throw invalidPath('A file path must be a non-empty string');
|
|
9
|
+
if (path.includes('\0'))
|
|
10
|
+
throw invalidPath('A file path cannot hold a NUL character');
|
|
11
|
+
if (path.startsWith('/'))
|
|
12
|
+
return path;
|
|
13
|
+
const directory = host.appStorageDirectory();
|
|
14
|
+
return directory.endsWith('/') ? `${directory}${path}` : `${directory}/${path}`;
|
|
15
|
+
}
|
|
16
|
+
async function writeAll(files, descriptor, chunk) {
|
|
17
|
+
let written = 0;
|
|
18
|
+
while (written < chunk.byteLength) {
|
|
19
|
+
const end = Math.min(chunk.byteLength, written + FILE_CHUNK_BYTES);
|
|
20
|
+
const count = await files.writeAsync(descriptor, chunk.subarray(written, end), CURRENT_POSITION);
|
|
21
|
+
if (count <= 0)
|
|
22
|
+
throw Object.assign(new Error('The file accepted no more bytes'), { code: 'EIO' });
|
|
23
|
+
written += count;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function temporaryNameFor(path) {
|
|
27
|
+
const suffix = new Uint8Array(8);
|
|
28
|
+
globalThis.crypto.getRandomValues(suffix);
|
|
29
|
+
let text = '';
|
|
30
|
+
for (const byte of suffix)
|
|
31
|
+
text += byte.toString(16).padStart(2, '0');
|
|
32
|
+
return `${path}.partial-${text}`;
|
|
33
|
+
}
|
|
34
|
+
export async function writeStreamToFile(files, stream, path) {
|
|
35
|
+
const temporary = temporaryNameFor(path);
|
|
36
|
+
const descriptor = files.open(temporary, 'wx');
|
|
37
|
+
const reader = stream.getReader();
|
|
38
|
+
let total = 0;
|
|
39
|
+
try {
|
|
40
|
+
for (;;) {
|
|
41
|
+
const { done, value } = await reader.read();
|
|
42
|
+
if (done)
|
|
43
|
+
break;
|
|
44
|
+
await writeAll(files, descriptor, value);
|
|
45
|
+
total += value.byteLength;
|
|
46
|
+
}
|
|
47
|
+
await files.syncAsync(descriptor);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
await reader.cancel(error).catch(() => undefined);
|
|
51
|
+
files.close(descriptor);
|
|
52
|
+
try {
|
|
53
|
+
files.unlink(temporary);
|
|
54
|
+
}
|
|
55
|
+
catch { }
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
files.close(descriptor);
|
|
59
|
+
files.rename(temporary, path);
|
|
60
|
+
return total;
|
|
61
|
+
}
|
|
62
|
+
export async function* fileChunks(files, path) {
|
|
63
|
+
const descriptor = files.open(path, 'r');
|
|
64
|
+
try {
|
|
65
|
+
let position = 0;
|
|
66
|
+
for (;;) {
|
|
67
|
+
const chunk = new Uint8Array(FILE_CHUNK_BYTES);
|
|
68
|
+
const count = await files.readAsync(descriptor, chunk, position);
|
|
69
|
+
if (count === 0)
|
|
70
|
+
return;
|
|
71
|
+
position += count;
|
|
72
|
+
yield count === chunk.byteLength ? chunk : chunk.subarray(0, count);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
files.close(descriptor);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export async function collectStream(stream) {
|
|
80
|
+
const reader = stream.getReader();
|
|
81
|
+
const chunks = [];
|
|
82
|
+
let total = 0;
|
|
83
|
+
for (;;) {
|
|
84
|
+
const { done, value } = await reader.read();
|
|
85
|
+
if (done)
|
|
86
|
+
break;
|
|
87
|
+
chunks.push(value);
|
|
88
|
+
total += value.byteLength;
|
|
89
|
+
}
|
|
90
|
+
const bytes = new Uint8Array(total);
|
|
91
|
+
let offset = 0;
|
|
92
|
+
for (const chunk of chunks) {
|
|
93
|
+
bytes.set(chunk, offset);
|
|
94
|
+
offset += chunk.byteLength;
|
|
95
|
+
}
|
|
96
|
+
return bytes;
|
|
97
|
+
}
|