@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,168 @@
|
|
|
1
|
+
#include "internal.h"
|
|
2
|
+
|
|
3
|
+
#include "../include/narsil_core.h"
|
|
4
|
+
|
|
5
|
+
#include <stdatomic.h>
|
|
6
|
+
#include <stddef.h>
|
|
7
|
+
#include <stdint.h>
|
|
8
|
+
|
|
9
|
+
#ifdef _WIN32
|
|
10
|
+
#include <windows.h>
|
|
11
|
+
#else
|
|
12
|
+
#include <sched.h>
|
|
13
|
+
#endif
|
|
14
|
+
|
|
15
|
+
#define LOCK_SPIN_ITERATIONS 128
|
|
16
|
+
#define LIST_COUNT_WORDS 1
|
|
17
|
+
#define LOCK_FREE 0
|
|
18
|
+
#define LOCK_HELD_ALONE (-1)
|
|
19
|
+
|
|
20
|
+
static inline void yield_thread(void) {
|
|
21
|
+
#ifdef _WIN32
|
|
22
|
+
SwitchToThread();
|
|
23
|
+
#else
|
|
24
|
+
sched_yield();
|
|
25
|
+
#endif
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static inline void wait_for_the_lock(uint32_t *waits) {
|
|
29
|
+
if (*waits < LOCK_SPIN_ITERATIONS) {
|
|
30
|
+
*waits += 1;
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
yield_thread();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static inline uint32_t held_index(uint32_t thread_slot, uint32_t kind) {
|
|
37
|
+
return (thread_slot * NARSIL_HELD_WORDS_PER_THREAD) + kind;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
void graph_lock_shared(const narsil_graph *graph, uint32_t thread_slot) {
|
|
41
|
+
_Atomic(int32_t) *lock = atomic_word(graph->header, NARSIL_GRAPH_WORD_LOCK);
|
|
42
|
+
_Atomic(int32_t) *writers_waiting = atomic_word(graph->header, NARSIL_GRAPH_WORD_WRITERS_WAITING);
|
|
43
|
+
uint32_t waits = 0;
|
|
44
|
+
for (;;) {
|
|
45
|
+
int32_t seen = atomic_load(lock);
|
|
46
|
+
if (seen >= 0 && atomic_load(writers_waiting) == 0) {
|
|
47
|
+
if (atomic_compare_exchange_strong(lock, &seen, seen + 1)) {
|
|
48
|
+
atomic_store(atomic_word(graph->held_locks, held_index(thread_slot, NARSIL_HELD_WORD_GRAPH)), 1);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
wait_for_the_lock(&waits);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
void graph_unlock_shared(const narsil_graph *graph, uint32_t thread_slot) {
|
|
58
|
+
atomic_store(atomic_word(graph->held_locks, held_index(thread_slot, NARSIL_HELD_WORD_GRAPH)), 0);
|
|
59
|
+
atomic_fetch_sub(atomic_word(graph->header, NARSIL_GRAPH_WORD_LOCK), 1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
void node_lock(const walk_context *context, int32_t ordinal) {
|
|
63
|
+
const narsil_graph *graph = context->graph;
|
|
64
|
+
uint32_t thread_slot = context->thread_slot;
|
|
65
|
+
_Atomic(int32_t) *word = atomic_word(graph->locks, (uint32_t)ordinal);
|
|
66
|
+
uint32_t waits = 0;
|
|
67
|
+
for (;;) {
|
|
68
|
+
int32_t seen = atomic_load(word);
|
|
69
|
+
if ((seen & NODE_WRITE_HELD) == 0) {
|
|
70
|
+
int32_t claimed = seen | NODE_WRITE_HELD;
|
|
71
|
+
if (atomic_compare_exchange_strong(word, &seen, claimed)) {
|
|
72
|
+
atomic_store(atomic_word(graph->held_locks, held_index(thread_slot, NARSIL_HELD_WORD_NODE_VALUE)), claimed);
|
|
73
|
+
atomic_store(atomic_word(graph->held_locks, held_index(thread_slot, NARSIL_HELD_WORD_NODE)), ordinal + 1);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
wait_for_the_lock(&waits);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
void node_unlock(const walk_context *context, int32_t ordinal) {
|
|
83
|
+
const narsil_graph *graph = context->graph;
|
|
84
|
+
_Atomic(int32_t) *word = atomic_word(graph->locks, (uint32_t)ordinal);
|
|
85
|
+
uint32_t seen = (uint32_t)atomic_load(word);
|
|
86
|
+
uint32_t released = (seen & ~(uint32_t)(NODE_WRITE_HELD | NODE_WRITER_WAITING)) + NODE_VERSION_STEP;
|
|
87
|
+
int32_t previous = atomic_exchange(word, (int32_t)released);
|
|
88
|
+
if ((previous & NODE_WRITER_WAITING) != 0 && graph->wake != NULL) { graph->wake(graph->wake_context, ordinal); }
|
|
89
|
+
atomic_store(atomic_word(graph->held_locks, held_index(context->thread_slot, NARSIL_HELD_WORD_NODE)), 0);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
void entry_lock(const walk_context *context) {
|
|
93
|
+
const narsil_graph *graph = context->graph;
|
|
94
|
+
_Atomic(int32_t) *lock = atomic_word(graph->header, NARSIL_GRAPH_WORD_ENTRY_LOCK);
|
|
95
|
+
uint32_t waits = 0;
|
|
96
|
+
for (;;) {
|
|
97
|
+
int32_t expected = LOCK_FREE;
|
|
98
|
+
if (atomic_compare_exchange_strong(lock, &expected, LOCK_HELD_ALONE)) {
|
|
99
|
+
atomic_store(atomic_word(graph->held_locks, held_index(context->thread_slot, NARSIL_HELD_WORD_ENTRY)), 1);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
wait_for_the_lock(&waits);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
void entry_unlock(const walk_context *context) {
|
|
107
|
+
const narsil_graph *graph = context->graph;
|
|
108
|
+
atomic_store(atomic_word(graph->held_locks, held_index(context->thread_slot, NARSIL_HELD_WORD_ENTRY)), 0);
|
|
109
|
+
atomic_store(atomic_word(graph->header, NARSIL_GRAPH_WORD_ENTRY_LOCK), LOCK_FREE);
|
|
110
|
+
if (graph->wake != NULL) { graph->wake(graph->wake_context, NARSIL_WAKE_ENTRY_LOCK); }
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
int32_t node_top_layer(const walk_context *context, int32_t ordinal) {
|
|
114
|
+
if (ordinal < 0 || (uint32_t)ordinal >= context->slots) { return NO_NODE; }
|
|
115
|
+
return (int32_t)atomic_load(atomic_byte(context->graph->node_levels, (uint32_t)ordinal)) - 1;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
uint32_t list_limit(const narsil_graph *graph, int32_t layer) {
|
|
119
|
+
int32_t limit = load_word(graph->header, layer == 0 ? NARSIL_GRAPH_WORD_MMAX0 : NARSIL_GRAPH_WORD_M);
|
|
120
|
+
return limit <= 0 ? 0 : (uint32_t)limit;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
int32_t *list_at(const walk_context *context, node_layer position, uint32_t *capacity) {
|
|
124
|
+
const narsil_graph *graph = context->graph;
|
|
125
|
+
*capacity = 0;
|
|
126
|
+
int32_t level = node_top_layer(context, position.ordinal);
|
|
127
|
+
if (level < 0 || position.layer < 0 || position.layer > level) { return NULL; }
|
|
128
|
+
if (graph->level0 == NULL || graph->upper == NULL || graph->upper_base == NULL) { return NULL; }
|
|
129
|
+
uint32_t limit = list_limit(graph, position.layer);
|
|
130
|
+
if (limit == 0 || limit >= MAX_NEIGHBOURS_PER_LIST) { return NULL; }
|
|
131
|
+
uint64_t stride = (uint64_t)limit + NARSIL_LIST_WORDS_OVER_NEIGHBOURS;
|
|
132
|
+
if (position.layer == 0) {
|
|
133
|
+
*capacity = (uint32_t)(stride - LIST_COUNT_WORDS);
|
|
134
|
+
return graph->level0 + ((size_t)position.ordinal * stride);
|
|
135
|
+
}
|
|
136
|
+
int32_t base = graph->upper_base[position.ordinal];
|
|
137
|
+
if (base <= 0) { return NULL; }
|
|
138
|
+
uint64_t start = (uint64_t)(base - 1) + ((uint64_t)(position.layer - 1) * stride);
|
|
139
|
+
int32_t upper_used = load_word(graph->header, NARSIL_GRAPH_WORD_UPPER_USED);
|
|
140
|
+
if (upper_used < 0 || start + stride > (uint64_t)upper_used) { return NULL; }
|
|
141
|
+
*capacity = (uint32_t)(stride - LIST_COUNT_WORDS);
|
|
142
|
+
return graph->upper + start;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
uint32_t read_neighbours(const walk_context *context, node_layer position, int32_t *out) {
|
|
146
|
+
const narsil_graph *graph = context->graph;
|
|
147
|
+
_Atomic(int32_t) *word = atomic_word(graph->locks, (uint32_t)position.ordinal);
|
|
148
|
+
_Atomic(int32_t) *fence = atomic_word(context->fence, 0);
|
|
149
|
+
uint32_t waits = 0;
|
|
150
|
+
for (;;) {
|
|
151
|
+
int32_t before = atomic_load(word);
|
|
152
|
+
if ((before & NODE_WRITE_HELD) != 0) {
|
|
153
|
+
wait_for_the_lock(&waits);
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
uint32_t capacity = 0;
|
|
157
|
+
const int32_t *list = list_at(context, position, &capacity);
|
|
158
|
+
uint32_t count = 0;
|
|
159
|
+
if (list != NULL) {
|
|
160
|
+
int32_t stored = list[0];
|
|
161
|
+
count = stored < 0 ? 0 : (uint32_t)stored;
|
|
162
|
+
if (count > capacity) { count = capacity; }
|
|
163
|
+
for (uint32_t i = 0; i < count; i++) { out[i] = list[i + 1]; }
|
|
164
|
+
}
|
|
165
|
+
atomic_fetch_add(fence, 0);
|
|
166
|
+
if (atomic_load(word) == before) { return count; }
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
#include "internal.h"
|
|
2
|
+
|
|
3
|
+
#include "../include/narsil_core.h"
|
|
4
|
+
|
|
5
|
+
#include <stdatomic.h>
|
|
6
|
+
#include <stddef.h>
|
|
7
|
+
#include <stdint.h>
|
|
8
|
+
#include <stdlib.h>
|
|
9
|
+
|
|
10
|
+
#define NEAREST_LINK_FALLBACK_HOSTS 4U
|
|
11
|
+
#define NEAREST_NOT_YET_FOUND (-2)
|
|
12
|
+
|
|
13
|
+
typedef struct {
|
|
14
|
+
const walk_context *context;
|
|
15
|
+
int32_t *nearest;
|
|
16
|
+
} nearest_pass;
|
|
17
|
+
|
|
18
|
+
typedef struct {
|
|
19
|
+
int32_t host;
|
|
20
|
+
int32_t node;
|
|
21
|
+
} back_link;
|
|
22
|
+
|
|
23
|
+
static int nearer(scored_node first, scored_node second) {
|
|
24
|
+
return first.distance < second.distance || (first.distance == second.distance && first.ordinal < second.ordinal);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
static int32_t *base_list(const walk_context *context, int32_t ordinal, uint32_t *count) {
|
|
28
|
+
uint32_t capacity = 0;
|
|
29
|
+
node_layer base = {ordinal, 0};
|
|
30
|
+
int32_t *list = list_at(context, base, &capacity);
|
|
31
|
+
*count = list == NULL ? 0 : stored_count(list, capacity);
|
|
32
|
+
return list;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static int32_t nearest_in_list(const nearest_pass *pass, int32_t ordinal) {
|
|
36
|
+
if (pass->nearest[ordinal] != NEAREST_NOT_YET_FOUND) { return pass->nearest[ordinal]; }
|
|
37
|
+
const walk_context *context = pass->context;
|
|
38
|
+
uint32_t count = 0;
|
|
39
|
+
const int32_t *list = base_list(context, ordinal, &count);
|
|
40
|
+
scored_node best = {NO_NODE, 0};
|
|
41
|
+
for (uint32_t i = 1; i <= count; i++) {
|
|
42
|
+
int32_t neighbour = list[i];
|
|
43
|
+
if (neighbour == ordinal || !is_live(context, neighbour)) { continue; }
|
|
44
|
+
scored_node scored = {neighbour, pair_distance(context, ordinal, neighbour)};
|
|
45
|
+
if (best.ordinal == NO_NODE || nearer(scored, best)) { best = scored; }
|
|
46
|
+
}
|
|
47
|
+
pass->nearest[ordinal] = best.ordinal;
|
|
48
|
+
return best.ordinal;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
static uint32_t fallback_hosts_for(const nearest_pass *pass, back_link first, int32_t *hosts) {
|
|
52
|
+
int32_t ordinal = first.node;
|
|
53
|
+
int32_t nearest = first.host;
|
|
54
|
+
const walk_context *context = pass->context;
|
|
55
|
+
scored_node around[NEAREST_LINK_FALLBACK_HOSTS];
|
|
56
|
+
uint32_t kept = 0;
|
|
57
|
+
uint32_t count = 0;
|
|
58
|
+
const int32_t *list = base_list(context, nearest, &count);
|
|
59
|
+
for (uint32_t i = 1; i <= count; i++) {
|
|
60
|
+
int32_t neighbour = list[i];
|
|
61
|
+
if (neighbour == ordinal || !is_live(context, neighbour)) { continue; }
|
|
62
|
+
scored_node scored = {neighbour, pair_distance(context, ordinal, neighbour)};
|
|
63
|
+
if (kept == NEAREST_LINK_FALLBACK_HOSTS && !nearer(scored, around[kept - 1])) { continue; }
|
|
64
|
+
uint32_t slot = kept < NEAREST_LINK_FALLBACK_HOSTS ? kept++ : kept - 1;
|
|
65
|
+
while (slot > 0 && nearer(scored, around[slot - 1])) {
|
|
66
|
+
around[slot] = around[slot - 1];
|
|
67
|
+
slot -= 1;
|
|
68
|
+
}
|
|
69
|
+
around[slot] = scored;
|
|
70
|
+
}
|
|
71
|
+
for (uint32_t i = 0; i < kept; i++) { hosts[i] = around[i].ordinal; }
|
|
72
|
+
return kept;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
static int64_t replaceable_slot(const nearest_pass *pass, int32_t host, const int32_t *list, uint32_t count) {
|
|
76
|
+
const walk_context *context = pass->context;
|
|
77
|
+
int64_t farthest = -1;
|
|
78
|
+
scored_node farthest_node = {NO_NODE, 0};
|
|
79
|
+
for (uint32_t i = 1; i <= count; i++) {
|
|
80
|
+
int32_t neighbour = list[i];
|
|
81
|
+
if (incoming_count(context, neighbour) < 2) { continue; }
|
|
82
|
+
if (is_live(context, neighbour) && nearest_in_list(pass, neighbour) == host) { continue; }
|
|
83
|
+
scored_node scored = {neighbour, pair_distance(context, host, neighbour)};
|
|
84
|
+
if (farthest == -1 || nearer(farthest_node, scored)) {
|
|
85
|
+
farthest = i;
|
|
86
|
+
farthest_node = scored;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return farthest;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
static int link_from(const nearest_pass *pass, back_link link) {
|
|
93
|
+
const walk_context *context = pass->context;
|
|
94
|
+
int32_t host = link.host;
|
|
95
|
+
int32_t ordinal = link.node;
|
|
96
|
+
node_lock(context, host);
|
|
97
|
+
uint32_t count = 0;
|
|
98
|
+
int32_t *list = base_list(context, host, &count);
|
|
99
|
+
int linked = 0;
|
|
100
|
+
if (list != NULL) {
|
|
101
|
+
for (uint32_t i = 1; i <= count && !linked; i++) { linked = list[i] == ordinal; }
|
|
102
|
+
if (!linked && count < list_limit(context->graph, 0)) {
|
|
103
|
+
list[count + 1] = ordinal;
|
|
104
|
+
list[0] = (int32_t)(count + 1);
|
|
105
|
+
note_linked(context, ordinal);
|
|
106
|
+
pass->nearest[host] = NEAREST_NOT_YET_FOUND;
|
|
107
|
+
linked = 1;
|
|
108
|
+
}
|
|
109
|
+
int64_t slot = linked ? -1 : replaceable_slot(pass, host, list, count);
|
|
110
|
+
if (slot != -1) {
|
|
111
|
+
int32_t replaced = list[slot];
|
|
112
|
+
list[slot] = ordinal;
|
|
113
|
+
note_unlinked(context, replaced);
|
|
114
|
+
note_linked(context, ordinal);
|
|
115
|
+
pass->nearest[host] = NEAREST_NOT_YET_FOUND;
|
|
116
|
+
linked = 1;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
node_unlock(context, host);
|
|
120
|
+
return linked;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
narsil_status link_from_nearest(const walk_context *context) {
|
|
124
|
+
size_t slots = context->slots;
|
|
125
|
+
nearest_pass pass = {context, malloc((slots == 0 ? 1 : slots) * sizeof(int32_t))};
|
|
126
|
+
if (pass.nearest == NULL) { return NARSIL_OUT_OF_MEMORY; }
|
|
127
|
+
for (size_t i = 0; i < slots; i++) { pass.nearest[i] = NEAREST_NOT_YET_FOUND; }
|
|
128
|
+
int32_t hosts[NEAREST_LINK_FALLBACK_HOSTS];
|
|
129
|
+
for (uint32_t ordinal = 0; ordinal < slots; ordinal++) {
|
|
130
|
+
_Atomic(uint8_t) *due = atomic_byte(context->graph->nearest_link_due, ordinal);
|
|
131
|
+
if (atomic_load(due) != 1) { continue; }
|
|
132
|
+
atomic_store(due, 0);
|
|
133
|
+
int32_t node = (int32_t)ordinal;
|
|
134
|
+
if (!is_live(context, node)) { continue; }
|
|
135
|
+
int32_t nearest = nearest_in_list(&pass, node);
|
|
136
|
+
if (nearest == NO_NODE) { continue; }
|
|
137
|
+
back_link first = {nearest, node};
|
|
138
|
+
if (link_from(&pass, first)) { continue; }
|
|
139
|
+
uint32_t host_count = fallback_hosts_for(&pass, first, hosts);
|
|
140
|
+
for (uint32_t i = 0; i < host_count; i++) {
|
|
141
|
+
back_link link = {hosts[i], node};
|
|
142
|
+
if (link_from(&pass, link)) { break; }
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
free(pass.nearest);
|
|
146
|
+
return NARSIL_OK;
|
|
147
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
#include "internal.h"
|
|
2
|
+
|
|
3
|
+
#include "../include/narsil_core.h"
|
|
4
|
+
|
|
5
|
+
#include <stdatomic.h>
|
|
6
|
+
#include <stddef.h>
|
|
7
|
+
#include <stdint.h>
|
|
8
|
+
#include <string.h>
|
|
9
|
+
|
|
10
|
+
static int32_t capacity_word(const narsil_graph *graph, uint32_t word) {
|
|
11
|
+
int32_t capacity = load_word(graph->header, word);
|
|
12
|
+
return capacity < 0 ? 0 : capacity;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static narsil_status claim_upper_lists(const narsil_graph *graph, int32_t top_layer, int32_t *upper_base) {
|
|
16
|
+
*upper_base = 0;
|
|
17
|
+
if (top_layer < 1) { return NARSIL_OK; }
|
|
18
|
+
int64_t words = (int64_t)top_layer * ((int64_t)list_limit(graph, 1) + NARSIL_LIST_WORDS_OVER_NEIGHBOURS);
|
|
19
|
+
_Atomic(int32_t) *used = atomic_word(graph->header, NARSIL_GRAPH_WORD_UPPER_USED);
|
|
20
|
+
for (;;) {
|
|
21
|
+
int32_t offset = atomic_load(used);
|
|
22
|
+
if (offset < 0 || (int64_t)offset + words > capacity_word(graph, NARSIL_GRAPH_WORD_UPPER_CAPACITY)) {
|
|
23
|
+
return NARSIL_NEEDS_ROOM;
|
|
24
|
+
}
|
|
25
|
+
if (!atomic_compare_exchange_strong(used, &offset, (int32_t)(offset + words))) { continue; }
|
|
26
|
+
memset(graph->upper + offset, 0, (size_t)words * sizeof(int32_t));
|
|
27
|
+
*upper_base = offset + 1;
|
|
28
|
+
return NARSIL_OK;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static void raise_slots(const narsil_graph *graph, int32_t slots) {
|
|
33
|
+
_Atomic(int32_t) *word = atomic_word(graph->header, NARSIL_GRAPH_WORD_SLOTS);
|
|
34
|
+
int32_t seen = atomic_load(word);
|
|
35
|
+
while (seen < slots && !atomic_compare_exchange_strong(word, &seen, slots)) {}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static narsil_status create_node(const narsil_graph *graph, const narsil_place_request *request) {
|
|
39
|
+
int32_t ordinal = request->ordinal;
|
|
40
|
+
if (ordinal >= capacity_word(graph, NARSIL_GRAPH_WORD_SLOT_CAPACITY)) { return NARSIL_NEEDS_ROOM; }
|
|
41
|
+
if (atomic_load(atomic_byte(graph->node_levels, (uint32_t)ordinal)) != 0) { return NARSIL_INVALID_ARGUMENT; }
|
|
42
|
+
int32_t upper_base = 0;
|
|
43
|
+
narsil_status status = claim_upper_lists(graph, request->top_layer, &upper_base);
|
|
44
|
+
if (status != NARSIL_OK) { return status; }
|
|
45
|
+
|
|
46
|
+
_Atomic(uint8_t) *tombstone = atomic_byte(graph->tombstones, (uint32_t)ordinal);
|
|
47
|
+
if (atomic_load(tombstone) == 1) {
|
|
48
|
+
atomic_fetch_sub(atomic_word(graph->header, NARSIL_GRAPH_WORD_TOMBSTONE_COUNT), 1);
|
|
49
|
+
atomic_store(tombstone, 0);
|
|
50
|
+
}
|
|
51
|
+
size_t base_stride = (size_t)list_limit(graph, 0) + NARSIL_LIST_WORDS_OVER_NEIGHBOURS;
|
|
52
|
+
graph->upper_base[ordinal] = upper_base;
|
|
53
|
+
graph->level0[(size_t)ordinal * base_stride] = 0;
|
|
54
|
+
raise_slots(graph, ordinal + 1);
|
|
55
|
+
atomic_store(atomic_byte(graph->nearest_link_due, (uint32_t)ordinal), 1);
|
|
56
|
+
atomic_store(atomic_byte(graph->node_levels, (uint32_t)ordinal), (uint8_t)(request->top_layer + 1));
|
|
57
|
+
atomic_fetch_add(atomic_word(graph->header, NARSIL_GRAPH_WORD_NODE_COUNT), 1);
|
|
58
|
+
return NARSIL_OK;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static void retire_unlinked_node(const narsil_graph *graph, int32_t ordinal) {
|
|
62
|
+
uint8_t live = 0;
|
|
63
|
+
if (atomic_compare_exchange_strong(atomic_byte(graph->tombstones, (uint32_t)ordinal), &live, 1)) {
|
|
64
|
+
atomic_fetch_add(atomic_word(graph->header, NARSIL_GRAPH_WORD_TOMBSTONE_COUNT), 1);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static int claim_entry_if_empty(const walk_context *context, const narsil_place_request *request) {
|
|
69
|
+
int32_t *header = context->graph->header;
|
|
70
|
+
if (load_word(header, NARSIL_GRAPH_WORD_ENTRY_POINT) != NO_ENTRY_POINT) { return 0; }
|
|
71
|
+
entry_lock(context);
|
|
72
|
+
int claimed = load_word(header, NARSIL_GRAPH_WORD_ENTRY_POINT) == NO_ENTRY_POINT;
|
|
73
|
+
if (claimed) {
|
|
74
|
+
atomic_store(atomic_word(header, NARSIL_GRAPH_WORD_ENTRY_POINT), request->ordinal);
|
|
75
|
+
atomic_store(atomic_word(header, NARSIL_GRAPH_WORD_TOP_LAYER), request->top_layer);
|
|
76
|
+
}
|
|
77
|
+
entry_unlock(context);
|
|
78
|
+
return claimed;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
static void raise_entry(const walk_context *context, const narsil_place_request *request) {
|
|
82
|
+
int32_t *header = context->graph->header;
|
|
83
|
+
entry_lock(context);
|
|
84
|
+
if (request->top_layer > load_word(header, NARSIL_GRAPH_WORD_TOP_LAYER)) {
|
|
85
|
+
atomic_store(atomic_word(header, NARSIL_GRAPH_WORD_ENTRY_POINT), request->ordinal);
|
|
86
|
+
atomic_store(atomic_word(header, NARSIL_GRAPH_WORD_TOP_LAYER), request->top_layer);
|
|
87
|
+
}
|
|
88
|
+
entry_unlock(context);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
static int32_t *selection_of(const narsil_workspace *workspace, int32_t layer) {
|
|
92
|
+
return workspace->selections + ((size_t)layer * workspace->selection_stride);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
static narsil_status reserve_for_placement(const walk_context *context, const narsil_place_request *request) {
|
|
96
|
+
narsil_workspace *workspace = context->workspace;
|
|
97
|
+
int32_t ef_construction = load_word(context->graph->header, NARSIL_GRAPH_WORD_EF_CONSTRUCTION);
|
|
98
|
+
if (ef_construction <= 0) { return NARSIL_INVALID_ARGUMENT; }
|
|
99
|
+
if (request->ordinal >= capacity_word(context->graph, NARSIL_GRAPH_WORD_SLOT_CAPACITY)) { return NARSIL_NEEDS_ROOM; }
|
|
100
|
+
narsil_status status = list_reserve(&workspace->layer_candidates, (uint32_t)ef_construction);
|
|
101
|
+
if (status == NARSIL_OK) { status = list_reserve(&workspace->link_candidates, MAX_NEIGHBOURS_PER_LIST); }
|
|
102
|
+
if (status == NARSIL_OK) { status = workspace_reserve_entry_points(workspace, (uint32_t)ef_construction); }
|
|
103
|
+
if (status == NARSIL_OK) { status = workspace_reserve_visited(workspace, (uint32_t)request->ordinal + 1); }
|
|
104
|
+
if (status == NARSIL_OK) { status = reserve_last_links(workspace); }
|
|
105
|
+
if (status != NARSIL_OK) { return status; }
|
|
106
|
+
return workspace_reserve_links(workspace, context->graph, (uint32_t)request->top_layer + 1);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
static narsil_status select_every_layer(const walk_context *context, const narsil_place_request *request,
|
|
110
|
+
link_span span) {
|
|
111
|
+
narsil_workspace *workspace = context->workspace;
|
|
112
|
+
uint32_t ef_construction = (uint32_t)load_word(context->graph->header, NARSIL_GRAPH_WORD_EF_CONSTRUCTION);
|
|
113
|
+
scored_list *found = &workspace->layer_candidates;
|
|
114
|
+
if (ef_construction == 0 || ef_construction > found->capacity) { return NARSIL_INVALID_ARGUMENT; }
|
|
115
|
+
narsil_candidates candidates = {found->ordinals, found->distances, found->capacity, 0};
|
|
116
|
+
use_single_entry(workspace, load_word(context->graph->header, NARSIL_GRAPH_WORD_ENTRY_POINT));
|
|
117
|
+
narsil_status status = descend_layers(context, span.graph_top, request->top_layer + 1, &candidates);
|
|
118
|
+
for (int32_t layer = span.link_top; layer >= 0 && status == NARSIL_OK; layer--) {
|
|
119
|
+
layer_search search = {ef_construction, layer, 0, {NULL, 0}};
|
|
120
|
+
status = search_layer(context, search, &candidates);
|
|
121
|
+
if (status != NARSIL_OK) { break; }
|
|
122
|
+
if (candidates.count > 0) {
|
|
123
|
+
memcpy(workspace->entry_points, candidates.ordinals, (size_t)candidates.count * sizeof(int32_t));
|
|
124
|
+
workspace->entry_point_count = candidates.count;
|
|
125
|
+
}
|
|
126
|
+
found->count = candidates.count;
|
|
127
|
+
workspace->selection_counts[layer] =
|
|
128
|
+
select_neighbours(context, found, list_limit(context->graph, layer), selection_of(workspace, layer));
|
|
129
|
+
}
|
|
130
|
+
return status;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
static void write_own_lists(const walk_context *context, const narsil_place_request *request, int32_t link_top) {
|
|
134
|
+
const narsil_workspace *workspace = context->workspace;
|
|
135
|
+
node_lock(context, request->ordinal);
|
|
136
|
+
for (int32_t layer = link_top; layer >= 0; layer--) {
|
|
137
|
+
node_layer position = {request->ordinal, layer};
|
|
138
|
+
list_write_counted(context, position, selection_of(workspace, layer), workspace->selection_counts[layer]);
|
|
139
|
+
}
|
|
140
|
+
node_unlock(context, request->ordinal);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
static void link_neighbours_back(const walk_context *context, const narsil_place_request *request, int32_t link_top) {
|
|
144
|
+
const narsil_workspace *workspace = context->workspace;
|
|
145
|
+
for (int32_t layer = link_top; layer >= 0; layer--) {
|
|
146
|
+
const int32_t *selected = selection_of(workspace, layer);
|
|
147
|
+
for (uint32_t i = 0; i < workspace->selection_counts[layer]; i++) {
|
|
148
|
+
node_layer position = {selected[i], layer};
|
|
149
|
+
if (layer > node_top_layer(context, selected[i])) { continue; }
|
|
150
|
+
node_lock(context, selected[i]);
|
|
151
|
+
list_add(context, position, request->ordinal);
|
|
152
|
+
list_prune(context, position);
|
|
153
|
+
node_unlock(context, selected[i]);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
static narsil_status link_node(walk_context *context, const narsil_place_request *request) {
|
|
159
|
+
link_span span = {load_word(context->graph->header, NARSIL_GRAPH_WORD_TOP_LAYER), 0};
|
|
160
|
+
int32_t former_entry = load_word(context->graph->header, NARSIL_GRAPH_WORD_ENTRY_POINT);
|
|
161
|
+
span.link_top = request->top_layer < span.graph_top ? request->top_layer : span.graph_top;
|
|
162
|
+
if (span.link_top >= 0) {
|
|
163
|
+
narsil_status status = select_every_layer(context, request, span);
|
|
164
|
+
if (status != NARSIL_OK) { return status; }
|
|
165
|
+
write_own_lists(context, request, span.link_top);
|
|
166
|
+
link_neighbours_back(context, request, span.link_top);
|
|
167
|
+
}
|
|
168
|
+
if (request->top_layer <= span.graph_top) { return NARSIL_OK; }
|
|
169
|
+
raise_entry(context, request);
|
|
170
|
+
int demoted = former_entry != NO_NODE &&
|
|
171
|
+
load_word(context->graph->header, NARSIL_GRAPH_WORD_ENTRY_POINT) != former_entry &&
|
|
172
|
+
is_live(context, former_entry);
|
|
173
|
+
if (demoted && incoming_count(context, former_entry) == 0) { note_node_left_unlinked(context); }
|
|
174
|
+
return NARSIL_OK;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
static narsil_status place_under_the_graph_lock(walk_context *context, const narsil_place_request *request) {
|
|
178
|
+
narsil_status status = reserve_for_placement(context, request);
|
|
179
|
+
if (status == NARSIL_OK) { status = create_node(context->graph, request); }
|
|
180
|
+
if (status != NARSIL_OK) { return status; }
|
|
181
|
+
status = read_graph_extent(context);
|
|
182
|
+
if (status == NARSIL_OK && !claim_entry_if_empty(context, request)) { status = link_node(context, request); }
|
|
183
|
+
if (status != NARSIL_OK) { retire_unlinked_node(context->graph, request->ordinal); }
|
|
184
|
+
return status;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
narsil_status narsil_place(narsil_workspace *workspace, const narsil_graph *graph, const narsil_store *store,
|
|
188
|
+
const narsil_place_request *request) {
|
|
189
|
+
if (request == NULL || request->ordinal < 0) { return NARSIL_INVALID_ARGUMENT; }
|
|
190
|
+
if (request->top_layer < 0 || request->top_layer >= NARSIL_MAX_PLACEMENT_LAYERS) { return NARSIL_INVALID_ARGUMENT; }
|
|
191
|
+
walk_context context;
|
|
192
|
+
narsil_status status = begin_walk(&context, workspace, graph, store, request->metric);
|
|
193
|
+
if (status != NARSIL_OK) { return status; }
|
|
194
|
+
if (request->thread_slot >= graph->thread_slots) { return NARSIL_INVALID_ARGUMENT; }
|
|
195
|
+
const float *vector = stored_vector(store, request->ordinal, workspace->placed_vector);
|
|
196
|
+
if (vector == NULL) { return NARSIL_INVALID_ARGUMENT; }
|
|
197
|
+
context.thread_slot = request->thread_slot;
|
|
198
|
+
if (store->file_count == 0) {
|
|
199
|
+
walk_with_query(&context, vector);
|
|
200
|
+
} else {
|
|
201
|
+
walk_with_placement_query(&context, vector);
|
|
202
|
+
}
|
|
203
|
+
if (!context.uses_codes) { context.query.magnitude = store->magnitudes[request->ordinal]; }
|
|
204
|
+
|
|
205
|
+
if (request->graph_held_alone) {
|
|
206
|
+
if (load_word(graph->header, NARSIL_GRAPH_WORD_LOCK) != NARSIL_GRAPH_HELD_ALONE) {
|
|
207
|
+
return NARSIL_GRAPH_NOT_HELD_ALONE;
|
|
208
|
+
}
|
|
209
|
+
return place_under_the_graph_lock(&context, request);
|
|
210
|
+
}
|
|
211
|
+
graph_lock_shared(graph, request->thread_slot);
|
|
212
|
+
status = place_under_the_graph_lock(&context, request);
|
|
213
|
+
graph_unlock_shared(graph, request->thread_slot);
|
|
214
|
+
return status;
|
|
215
|
+
}
|