@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,234 @@
|
|
|
1
|
+
#include "AttachedField.hpp"
|
|
2
|
+
#include "HostMemory.hpp"
|
|
3
|
+
|
|
4
|
+
#include <cmath>
|
|
5
|
+
#include <string>
|
|
6
|
+
|
|
7
|
+
namespace margelo::nitro::narsil {
|
|
8
|
+
|
|
9
|
+
namespace {
|
|
10
|
+
|
|
11
|
+
struct Region {
|
|
12
|
+
uint8_t* bytes;
|
|
13
|
+
size_t elements;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
Region heldRegion(const MemoryView& view, size_t elementBytes, std::vector<std::shared_ptr<ArrayBuffer>>& held,
|
|
17
|
+
const char* what) {
|
|
18
|
+
if (!isHostOwned(view.buffer)) {
|
|
19
|
+
throwInvalid(std::string(what) + " lies outside the memory that this module allocated");
|
|
20
|
+
}
|
|
21
|
+
uint8_t* bytes = bytesWithin(view.buffer, view.byteOffset, view.byteLength, what);
|
|
22
|
+
if (bytes == nullptr || reinterpret_cast<uintptr_t>(bytes) % elementBytes != 0 ||
|
|
23
|
+
static_cast<size_t>(view.byteLength) % elementBytes != 0) {
|
|
24
|
+
throwInvalid(std::string(what) + " is empty or misaligned");
|
|
25
|
+
}
|
|
26
|
+
held.push_back(view.buffer);
|
|
27
|
+
return Region{bytes, static_cast<size_t>(view.byteLength) / elementBytes};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
uint32_t wholeCount(double value, const char* what) {
|
|
31
|
+
if (!std::isfinite(value) || value < 0 || value > UINT32_MAX || std::floor(value) != value) {
|
|
32
|
+
throwInvalid(std::string(what) + " must be a whole number from 0 to 4294967295");
|
|
33
|
+
}
|
|
34
|
+
return static_cast<uint32_t>(value);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
int32_t headerWord(const int32_t* words, uint32_t index) { return __atomic_load_n(words + index, __ATOMIC_SEQ_CST); }
|
|
38
|
+
|
|
39
|
+
bool covers(size_t length, uint64_t required) { return static_cast<uint64_t>(length) >= required; }
|
|
40
|
+
|
|
41
|
+
void rememberWake(void* context, int32_t ordinal) {
|
|
42
|
+
auto* wakes = static_cast<PendingWakes*>(context);
|
|
43
|
+
if (wakes->count >= WAKES_KEPT_PER_CALL) return;
|
|
44
|
+
wakes->ordinals[wakes->count] = ordinal;
|
|
45
|
+
wakes->count += 1;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
uint64_t bytesInUse(uint64_t slots, uint64_t entriesPerBlock, uint64_t entryBytes, uint32_t index) {
|
|
49
|
+
uint64_t first = static_cast<uint64_t>(index) * entriesPerBlock;
|
|
50
|
+
if (first >= slots) return 0;
|
|
51
|
+
uint64_t remaining = slots - first;
|
|
52
|
+
return (remaining < entriesPerBlock ? remaining : entriesPerBlock) * entryBytes;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
template <typename Pointer>
|
|
56
|
+
void readBlocks(const std::vector<MemoryView>& views, uint64_t slots, uint64_t entriesPerBlock, uint64_t entryBytes,
|
|
57
|
+
size_t alignment, std::vector<std::shared_ptr<ArrayBuffer>>& held, std::vector<Pointer>& out,
|
|
58
|
+
const char* what) {
|
|
59
|
+
out.assign(views.size(), nullptr);
|
|
60
|
+
for (size_t index = 0; index < views.size(); index++) {
|
|
61
|
+
const MemoryView& view = views[index];
|
|
62
|
+
if (view.byteLength == 0) continue;
|
|
63
|
+
Region region = heldRegion(view, 1, held, what);
|
|
64
|
+
if (reinterpret_cast<uintptr_t>(region.bytes) % alignment != 0 ||
|
|
65
|
+
!covers(region.elements, bytesInUse(slots, entriesPerBlock, entryBytes, static_cast<uint32_t>(index)))) {
|
|
66
|
+
throwInvalid(std::string(what) + " is misaligned or smaller than the records it holds");
|
|
67
|
+
}
|
|
68
|
+
out[index] = reinterpret_cast<Pointer>(region.bytes);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
HybridNarsilAttachedGraphImpl::HybridNarsilAttachedGraphImpl(const GraphMemory& memory) : HybridObject(TAG) {
|
|
75
|
+
Region header = heldRegion(memory.graphHeader, sizeof(int32_t), _held, "The graph header");
|
|
76
|
+
if (header.elements < NARSIL_GRAPH_HEADER_WORDS) throwInvalid("The graph header is too short");
|
|
77
|
+
Region levels = heldRegion(memory.nodeLevels, 1, _held, "The node levels");
|
|
78
|
+
Region level0 = heldRegion(memory.level0, sizeof(int32_t), _held, "The base layer");
|
|
79
|
+
Region upperBase = heldRegion(memory.upperBase, sizeof(int32_t), _held, "The upper layer starts");
|
|
80
|
+
Region upper = heldRegion(memory.upper, sizeof(int32_t), _held, "The upper layers");
|
|
81
|
+
Region locks = heldRegion(memory.locks, sizeof(int32_t), _held, "The node locks");
|
|
82
|
+
Region tombstones = heldRegion(memory.tombstones, 1, _held, "The tombstones");
|
|
83
|
+
Region incoming = heldRegion(memory.incoming, sizeof(int32_t), _held, "The incoming counts");
|
|
84
|
+
Region due = heldRegion(memory.nearestLinkDue, 1, _held, "The nearest link flags");
|
|
85
|
+
Region heldLocks = heldRegion(memory.heldLocks, sizeof(int32_t), _held, "The held locks");
|
|
86
|
+
graph.header = reinterpret_cast<int32_t*>(header.bytes);
|
|
87
|
+
graph.node_levels = levels.bytes;
|
|
88
|
+
graph.level0 = reinterpret_cast<int32_t*>(level0.bytes);
|
|
89
|
+
graph.upper_base = reinterpret_cast<int32_t*>(upperBase.bytes);
|
|
90
|
+
graph.upper = reinterpret_cast<int32_t*>(upper.bytes);
|
|
91
|
+
graph.locks = reinterpret_cast<int32_t*>(locks.bytes);
|
|
92
|
+
graph.tombstones = tombstones.bytes;
|
|
93
|
+
graph.incoming = reinterpret_cast<int32_t*>(incoming.bytes);
|
|
94
|
+
graph.nearest_link_due = due.bytes;
|
|
95
|
+
graph.held_locks = reinterpret_cast<int32_t*>(heldLocks.bytes);
|
|
96
|
+
size_t threadSlots = heldLocks.elements / NARSIL_HELD_WORDS_PER_THREAD;
|
|
97
|
+
graph.thread_slots = threadSlots > UINT32_MAX ? UINT32_MAX : static_cast<uint32_t>(threadSlots);
|
|
98
|
+
if (graph.thread_slots == 0) throwInvalid("The held locks hold no thread slot");
|
|
99
|
+
int32_t slots = headerWord(graph.header, NARSIL_GRAPH_WORD_SLOTS);
|
|
100
|
+
int32_t upperUsed = headerWord(graph.header, NARSIL_GRAPH_WORD_UPPER_USED);
|
|
101
|
+
int32_t maxBaseNeighbours = headerWord(graph.header, NARSIL_GRAPH_WORD_MMAX0);
|
|
102
|
+
if (slots < 0 || upperUsed < 0 || maxBaseNeighbours <= 0) throwInvalid("The graph header is inconsistent");
|
|
103
|
+
uint64_t ordinals = static_cast<uint64_t>(slots);
|
|
104
|
+
uint64_t baseStride = static_cast<uint64_t>(maxBaseNeighbours) + NARSIL_LIST_WORDS_OVER_NEIGHBOURS;
|
|
105
|
+
if (!covers(levels.elements, ordinals) || !covers(level0.elements, ordinals * baseStride) ||
|
|
106
|
+
!covers(upperBase.elements, ordinals) || !covers(locks.elements, ordinals) ||
|
|
107
|
+
!covers(tombstones.elements, ordinals) || !covers(incoming.elements, ordinals) ||
|
|
108
|
+
!covers(due.elements, ordinals) || !covers(upper.elements, static_cast<uint64_t>(upperUsed))) {
|
|
109
|
+
throwInvalid("The graph regions are smaller than the graph they hold");
|
|
110
|
+
}
|
|
111
|
+
graph.wake = rememberWake;
|
|
112
|
+
graph.wake_context = &wakes;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
HybridNarsilAttachedStoreImpl::HybridNarsilAttachedStoreImpl(const StoreMemory& memory) : HybridObject(TAG) {
|
|
116
|
+
store.dimension = wholeCount(memory.dimension, "The dimension");
|
|
117
|
+
store.bits = wholeCount(memory.bits, "The code bits");
|
|
118
|
+
store.records_per_block = wholeCount(memory.recordsPerBlock, "The records per block");
|
|
119
|
+
store.vectors_per_block = wholeCount(memory.vectorsPerBlock, "The vectors per block");
|
|
120
|
+
uint32_t strideBytes = wholeCount(memory.vectorStrideBytes, "The vector stride");
|
|
121
|
+
if (store.dimension == 0 || strideBytes % sizeof(float) != 0) throwInvalid("The vector layout is invalid");
|
|
122
|
+
store.vector_stride_floats = strideBytes / sizeof(float);
|
|
123
|
+
if (store.vector_stride_floats < store.dimension) throwInvalid("The vector stride is shorter than a vector");
|
|
124
|
+
Region header = heldRegion(memory.storeHeader, sizeof(int32_t), _held, "The store header");
|
|
125
|
+
if (header.elements < NARSIL_STORE_HEADER_WORDS) throwInvalid("The store header is too short");
|
|
126
|
+
store.header = reinterpret_cast<int32_t*>(header.bytes);
|
|
127
|
+
Region codePresent = heldRegion(memory.codePresent, 1, _held, "The code flags");
|
|
128
|
+
Region centroid = heldRegion(memory.centroid, sizeof(float), _held, "The centroid");
|
|
129
|
+
if (centroid.elements < store.dimension) throwInvalid("The centroid is shorter than a vector");
|
|
130
|
+
Region magnitudes = heldRegion(memory.magnitudes, sizeof(double), _held, "The magnitudes");
|
|
131
|
+
Region present = heldRegion(memory.present, 1, _held, "The presence flags");
|
|
132
|
+
Region vectorFile = heldRegion(memory.vectorFile, sizeof(int32_t), _held, "The vector file table");
|
|
133
|
+
Region vectorOffset = heldRegion(memory.vectorOffset, sizeof(uint32_t), _held, "The vector offsets");
|
|
134
|
+
store.code_present = codePresent.bytes;
|
|
135
|
+
store.centroid = reinterpret_cast<float*>(centroid.bytes);
|
|
136
|
+
store.magnitudes = reinterpret_cast<const double*>(magnitudes.bytes);
|
|
137
|
+
store.present = present.bytes;
|
|
138
|
+
store.vector_file = reinterpret_cast<const int32_t*>(vectorFile.bytes);
|
|
139
|
+
store.vector_offset = reinterpret_cast<const uint32_t*>(vectorOffset.bytes);
|
|
140
|
+
int32_t storedSlots = headerWord(store.header, NARSIL_STORE_WORD_SLOTS);
|
|
141
|
+
if (storedSlots < 0) throwInvalid("The store header is inconsistent");
|
|
142
|
+
uint64_t slots = static_cast<uint64_t>(storedSlots);
|
|
143
|
+
if (!covers(codePresent.elements, slots) || !covers(magnitudes.elements, slots) || !covers(present.elements, slots) ||
|
|
144
|
+
!covers(vectorFile.elements, slots) || !covers(vectorOffset.elements, slots)) {
|
|
145
|
+
throwInvalid("The store regions are smaller than the vectors they hold");
|
|
146
|
+
}
|
|
147
|
+
readBlocks(memory.codeBlocks, slots, store.records_per_block, narsil_record_bytes(store.dimension, store.bits), 1,
|
|
148
|
+
_held, _codeBlocks, "A code block");
|
|
149
|
+
readBlocks(memory.vectorBlocks, slots, store.vectors_per_block, strideBytes, alignof(float), _held, _vectorBlocks,
|
|
150
|
+
"A vector block");
|
|
151
|
+
store.code_blocks = _codeBlocks.data();
|
|
152
|
+
store.code_block_count = static_cast<uint32_t>(_codeBlocks.size());
|
|
153
|
+
store.vector_blocks = _vectorBlocks.data();
|
|
154
|
+
store.vector_block_count = static_cast<uint32_t>(_vectorBlocks.size());
|
|
155
|
+
_files.assign(memory.vectorFiles.size(), narsil_vector_file{nullptr, 0, nullptr});
|
|
156
|
+
for (size_t index = 0; index < memory.vectorFiles.size(); index++) {
|
|
157
|
+
const std::string& path = memory.vectorFiles[index];
|
|
158
|
+
if (path.empty()) continue;
|
|
159
|
+
if (narsil_vector_file_map(path.c_str(), &_files[index]) != NARSIL_OK) {
|
|
160
|
+
unmapFiles();
|
|
161
|
+
throwInvalid("The vector file " + path + " cannot be mapped");
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
store.files = _files.data();
|
|
165
|
+
store.file_count = static_cast<uint32_t>(_files.size());
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
HybridNarsilAttachedStoreImpl::~HybridNarsilAttachedStoreImpl() { unmapFiles(); }
|
|
169
|
+
|
|
170
|
+
void HybridNarsilAttachedStoreImpl::unmapFiles() {
|
|
171
|
+
for (narsil_vector_file& file : _files) narsil_vector_file_unmap(&file);
|
|
172
|
+
_files.clear();
|
|
173
|
+
store.files = nullptr;
|
|
174
|
+
store.file_count = 0;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
double HybridNarsilAttachedStoreImpl::detach() {
|
|
178
|
+
std::lock_guard<std::mutex> guard(_useLock);
|
|
179
|
+
_detached = true;
|
|
180
|
+
if (_backgroundUses > 0) {
|
|
181
|
+
_unmapAfterUse = true;
|
|
182
|
+
return 0;
|
|
183
|
+
}
|
|
184
|
+
unmapFiles();
|
|
185
|
+
return 0;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
bool HybridNarsilAttachedStoreImpl::beginBackgroundUse() {
|
|
189
|
+
std::lock_guard<std::mutex> guard(_useLock);
|
|
190
|
+
if (_detached) return false;
|
|
191
|
+
_backgroundUses += 1;
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
void HybridNarsilAttachedStoreImpl::endBackgroundUse() {
|
|
196
|
+
std::lock_guard<std::mutex> guard(_useLock);
|
|
197
|
+
_backgroundUses -= 1;
|
|
198
|
+
if (_backgroundUses == 0 && _unmapAfterUse) {
|
|
199
|
+
_unmapAfterUse = false;
|
|
200
|
+
unmapFiles();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
std::shared_ptr<HybridNarsilAttachedGraphImpl> attachedGraphOf(
|
|
205
|
+
const std::shared_ptr<HybridNarsilAttachedGraphSpec>& graph) {
|
|
206
|
+
auto attached = std::dynamic_pointer_cast<HybridNarsilAttachedGraphImpl>(graph);
|
|
207
|
+
if (attached == nullptr) throwInvalid("The call takes a graph that this core attached");
|
|
208
|
+
return attached;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
std::shared_ptr<HybridNarsilAttachedStoreImpl> attachedStoreOf(
|
|
212
|
+
const std::shared_ptr<HybridNarsilAttachedStoreSpec>& store) {
|
|
213
|
+
auto attached = std::dynamic_pointer_cast<HybridNarsilAttachedStoreImpl>(store);
|
|
214
|
+
if (attached == nullptr || attached->isDetached()) throwInvalid("The call takes a store that this core attached");
|
|
215
|
+
return attached;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
uint32_t handOverWakes(PendingWakes& wakes, const MemoryView& target) {
|
|
219
|
+
uint8_t* bytes = bytesWithin(target.buffer, target.byteOffset, target.byteLength, "The wake list");
|
|
220
|
+
size_t capacity = static_cast<size_t>(target.byteLength) / sizeof(int32_t);
|
|
221
|
+
if (bytes == nullptr || capacity < 1 || reinterpret_cast<uintptr_t>(bytes) % sizeof(int32_t) != 0) {
|
|
222
|
+
wakes.count = 0;
|
|
223
|
+
throwInvalid("The wake list must be an aligned Int32Array with room for its count");
|
|
224
|
+
}
|
|
225
|
+
auto* out = reinterpret_cast<int32_t*>(bytes);
|
|
226
|
+
uint32_t kept = wakes.count;
|
|
227
|
+
if (kept > capacity - 1) kept = static_cast<uint32_t>(capacity - 1);
|
|
228
|
+
for (uint32_t index = 0; index < kept; index++) out[index + 1] = wakes.ordinals[index];
|
|
229
|
+
out[0] = static_cast<int32_t>(kept);
|
|
230
|
+
wakes.count = 0;
|
|
231
|
+
return kept;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "GraphMemory.hpp"
|
|
4
|
+
#include "HybridNarsilAttachedGraphSpec.hpp"
|
|
5
|
+
#include "HybridNarsilAttachedStoreSpec.hpp"
|
|
6
|
+
#include "StoreMemory.hpp"
|
|
7
|
+
#include "narsil_core.h"
|
|
8
|
+
|
|
9
|
+
#include <cstdint>
|
|
10
|
+
#include <memory>
|
|
11
|
+
#include <mutex>
|
|
12
|
+
#include <vector>
|
|
13
|
+
|
|
14
|
+
namespace margelo::nitro::narsil {
|
|
15
|
+
|
|
16
|
+
constexpr uint32_t WAKES_KEPT_PER_CALL = 256;
|
|
17
|
+
|
|
18
|
+
struct PendingWakes {
|
|
19
|
+
int32_t ordinals[WAKES_KEPT_PER_CALL];
|
|
20
|
+
uint32_t count = 0;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
class HybridNarsilAttachedGraphImpl final : public HybridNarsilAttachedGraphSpec {
|
|
24
|
+
public:
|
|
25
|
+
explicit HybridNarsilAttachedGraphImpl(const GraphMemory& memory);
|
|
26
|
+
narsil_graph graph{};
|
|
27
|
+
PendingWakes wakes;
|
|
28
|
+
|
|
29
|
+
private:
|
|
30
|
+
std::vector<std::shared_ptr<ArrayBuffer>> _held;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
class HybridNarsilAttachedStoreImpl final : public HybridNarsilAttachedStoreSpec {
|
|
34
|
+
public:
|
|
35
|
+
explicit HybridNarsilAttachedStoreImpl(const StoreMemory& memory);
|
|
36
|
+
~HybridNarsilAttachedStoreImpl() override;
|
|
37
|
+
double detach() override;
|
|
38
|
+
bool isDetached() const { return _detached; }
|
|
39
|
+
bool beginBackgroundUse();
|
|
40
|
+
void endBackgroundUse();
|
|
41
|
+
narsil_store store{};
|
|
42
|
+
|
|
43
|
+
private:
|
|
44
|
+
void unmapFiles();
|
|
45
|
+
std::vector<std::shared_ptr<ArrayBuffer>> _held;
|
|
46
|
+
std::vector<uint8_t*> _codeBlocks;
|
|
47
|
+
std::vector<const float*> _vectorBlocks;
|
|
48
|
+
std::vector<narsil_vector_file> _files;
|
|
49
|
+
std::mutex _useLock;
|
|
50
|
+
uint32_t _backgroundUses = 0;
|
|
51
|
+
bool _unmapAfterUse = false;
|
|
52
|
+
bool _detached = false;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
std::shared_ptr<HybridNarsilAttachedGraphImpl> attachedGraphOf(
|
|
56
|
+
const std::shared_ptr<HybridNarsilAttachedGraphSpec>& graph);
|
|
57
|
+
std::shared_ptr<HybridNarsilAttachedStoreImpl> attachedStoreOf(
|
|
58
|
+
const std::shared_ptr<HybridNarsilAttachedStoreSpec>& store);
|
|
59
|
+
uint32_t handOverWakes(PendingWakes& wakes, const MemoryView& target);
|
|
60
|
+
|
|
61
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#include "Compression.hpp"
|
|
2
|
+
#include "HostMemory.hpp"
|
|
3
|
+
|
|
4
|
+
#include <algorithm>
|
|
5
|
+
#include <cerrno>
|
|
6
|
+
#include <climits>
|
|
7
|
+
#include <string>
|
|
8
|
+
#include <vector>
|
|
9
|
+
#include <zlib.h>
|
|
10
|
+
|
|
11
|
+
namespace margelo::nitro::narsil {
|
|
12
|
+
|
|
13
|
+
namespace {
|
|
14
|
+
|
|
15
|
+
constexpr int RAW_DEFLATE_WINDOW_BITS = -15;
|
|
16
|
+
constexpr int GZIP_WINDOW_BITS = 31;
|
|
17
|
+
constexpr int DEFAULT_MEMORY_LEVEL = 8;
|
|
18
|
+
constexpr size_t LARGEST_ZLIB_STEP = static_cast<size_t>(UINT_MAX);
|
|
19
|
+
constexpr size_t FIRST_INFLATE_CAPACITY = 64 * 1024;
|
|
20
|
+
|
|
21
|
+
int windowBitsOf(CompressionFormat format) {
|
|
22
|
+
return format == CompressionFormat::Gzip ? GZIP_WINDOW_BITS : RAW_DEFLATE_WINDOW_BITS;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
uInt stepOf(size_t remaining) { return static_cast<uInt>(std::min(remaining, LARGEST_ZLIB_STEP)); }
|
|
26
|
+
|
|
27
|
+
class DeflateStream {
|
|
28
|
+
public:
|
|
29
|
+
explicit DeflateStream(CompressionFormat format) {
|
|
30
|
+
if (deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, windowBitsOf(format), DEFAULT_MEMORY_LEVEL,
|
|
31
|
+
Z_DEFAULT_STRATEGY) != Z_OK) {
|
|
32
|
+
throwSystemError(ENOMEM, "start compression", "");
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
~DeflateStream() { deflateEnd(&stream); }
|
|
36
|
+
z_stream stream{};
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
class InflateStream {
|
|
40
|
+
public:
|
|
41
|
+
explicit InflateStream(CompressionFormat format) {
|
|
42
|
+
if (inflateInit2(&stream, windowBitsOf(format)) != Z_OK) throwSystemError(ENOMEM, "start decompression", "");
|
|
43
|
+
}
|
|
44
|
+
~InflateStream() { inflateEnd(&stream); }
|
|
45
|
+
z_stream stream{};
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
std::shared_ptr<ArrayBuffer> compressBytes(const uint8_t* source, size_t length, CompressionFormat format) {
|
|
51
|
+
DeflateStream deflater(format);
|
|
52
|
+
z_stream& stream = deflater.stream;
|
|
53
|
+
std::vector<uint8_t> output(static_cast<size_t>(deflateBound(&stream, static_cast<uLong>(length))) + 64);
|
|
54
|
+
size_t consumed = 0;
|
|
55
|
+
size_t produced = 0;
|
|
56
|
+
int status = Z_OK;
|
|
57
|
+
while (status != Z_STREAM_END) {
|
|
58
|
+
size_t remainingInput = length - consumed;
|
|
59
|
+
if (output.size() == produced) output.resize(output.size() * 2);
|
|
60
|
+
stream.next_in = const_cast<Bytef*>(source + consumed);
|
|
61
|
+
stream.avail_in = stepOf(remainingInput);
|
|
62
|
+
stream.next_out = output.data() + produced;
|
|
63
|
+
stream.avail_out = stepOf(output.size() - produced);
|
|
64
|
+
uInt offeredIn = stream.avail_in;
|
|
65
|
+
uInt offeredOut = stream.avail_out;
|
|
66
|
+
bool lastStep = offeredIn == remainingInput;
|
|
67
|
+
status = deflate(&stream, lastStep ? Z_FINISH : Z_NO_FLUSH);
|
|
68
|
+
if (status == Z_STREAM_ERROR) throwInvalid("The compressor rejected its input");
|
|
69
|
+
consumed += offeredIn - stream.avail_in;
|
|
70
|
+
produced += offeredOut - stream.avail_out;
|
|
71
|
+
}
|
|
72
|
+
output.resize(produced);
|
|
73
|
+
return ArrayBuffer::move(std::move(output));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
std::shared_ptr<ArrayBuffer> inflateBytes(const uint8_t* source, size_t length, CompressionFormat format,
|
|
77
|
+
size_t maxLength) {
|
|
78
|
+
InflateStream inflater(format);
|
|
79
|
+
z_stream& stream = inflater.stream;
|
|
80
|
+
size_t capacityLimit = maxLength + 1;
|
|
81
|
+
std::vector<uint8_t> output(std::min(capacityLimit, std::max(FIRST_INFLATE_CAPACITY, length * 4)));
|
|
82
|
+
size_t consumed = 0;
|
|
83
|
+
size_t produced = 0;
|
|
84
|
+
for (;;) {
|
|
85
|
+
if (produced > maxLength) throwSystemError(EFBIG, "decompress past " + std::to_string(maxLength) + " bytes", "");
|
|
86
|
+
if (produced == output.size()) output.resize(std::min(capacityLimit, output.size() * 2));
|
|
87
|
+
stream.next_in = const_cast<Bytef*>(source + consumed);
|
|
88
|
+
stream.avail_in = stepOf(length - consumed);
|
|
89
|
+
stream.next_out = output.data() + produced;
|
|
90
|
+
stream.avail_out = stepOf(output.size() - produced);
|
|
91
|
+
uInt offeredIn = stream.avail_in;
|
|
92
|
+
uInt offeredOut = stream.avail_out;
|
|
93
|
+
int status = inflate(&stream, Z_NO_FLUSH);
|
|
94
|
+
consumed += offeredIn - stream.avail_in;
|
|
95
|
+
produced += offeredOut - stream.avail_out;
|
|
96
|
+
if (status == Z_STREAM_END) {
|
|
97
|
+
if (consumed != length) throwInvalid("The compressed data carries bytes after its end");
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
if (status == Z_NEED_DICT || status == Z_DATA_ERROR || status == Z_STREAM_ERROR) {
|
|
101
|
+
throwInvalid("The compressed data is corrupt");
|
|
102
|
+
}
|
|
103
|
+
if (status == Z_MEM_ERROR) throwSystemError(ENOMEM, "decompress", "");
|
|
104
|
+
bool madeProgress = offeredIn != stream.avail_in || offeredOut != stream.avail_out;
|
|
105
|
+
if (consumed == length && produced < output.size()) throwInvalid("The compressed data ends early");
|
|
106
|
+
if (!madeProgress && produced < output.size()) throwInvalid("The compressed data ends early");
|
|
107
|
+
}
|
|
108
|
+
if (produced > maxLength) throwSystemError(EFBIG, "decompress past " + std::to_string(maxLength) + " bytes", "");
|
|
109
|
+
output.resize(produced);
|
|
110
|
+
return ArrayBuffer::move(std::move(output));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <NitroModules/ArrayBuffer.hpp>
|
|
4
|
+
#include <cstddef>
|
|
5
|
+
#include <cstdint>
|
|
6
|
+
#include <memory>
|
|
7
|
+
|
|
8
|
+
namespace margelo::nitro::narsil {
|
|
9
|
+
|
|
10
|
+
enum class CompressionFormat { RawDeflate, Gzip };
|
|
11
|
+
|
|
12
|
+
std::shared_ptr<ArrayBuffer> compressBytes(const uint8_t* source, size_t length, CompressionFormat format);
|
|
13
|
+
std::shared_ptr<ArrayBuffer> inflateBytes(const uint8_t* source, size_t length, CompressionFormat format,
|
|
14
|
+
size_t maxLength);
|
|
15
|
+
|
|
16
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#include "GraphHeldAlone.hpp"
|
|
2
|
+
#include "narsil_core.h"
|
|
3
|
+
|
|
4
|
+
namespace margelo::nitro::narsil {
|
|
5
|
+
|
|
6
|
+
namespace {
|
|
7
|
+
|
|
8
|
+
constexpr int32_t GRAPH_LOCK_FREE = 0;
|
|
9
|
+
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
GraphHeldAlone::GraphHeldAlone(int32_t* header) : _header(header), _held(false) {
|
|
13
|
+
int32_t expected = GRAPH_LOCK_FREE;
|
|
14
|
+
_held = __atomic_compare_exchange_n(_header + NARSIL_GRAPH_WORD_LOCK, &expected, NARSIL_GRAPH_HELD_ALONE, false,
|
|
15
|
+
__ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
GraphHeldAlone::~GraphHeldAlone() { release(); }
|
|
19
|
+
|
|
20
|
+
void GraphHeldAlone::markEveryNodeLinked() {
|
|
21
|
+
if (_held && !_released.load()) __atomic_store_n(_header + NARSIL_GRAPH_WORD_UNLINKED_NODES, 0, __ATOMIC_SEQ_CST);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
void GraphHeldAlone::release() {
|
|
25
|
+
if (!_held || _released.exchange(true)) return;
|
|
26
|
+
__atomic_store_n(_header + NARSIL_GRAPH_WORD_LOCK, GRAPH_LOCK_FREE, __ATOMIC_SEQ_CST);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <atomic>
|
|
4
|
+
#include <cstdint>
|
|
5
|
+
|
|
6
|
+
namespace margelo::nitro::narsil {
|
|
7
|
+
|
|
8
|
+
class GraphHeldAlone {
|
|
9
|
+
public:
|
|
10
|
+
explicit GraphHeldAlone(int32_t* header);
|
|
11
|
+
~GraphHeldAlone();
|
|
12
|
+
GraphHeldAlone(const GraphHeldAlone&) = delete;
|
|
13
|
+
GraphHeldAlone& operator=(const GraphHeldAlone&) = delete;
|
|
14
|
+
|
|
15
|
+
bool held() const { return _held; }
|
|
16
|
+
void markEveryNodeLinked();
|
|
17
|
+
void release();
|
|
18
|
+
|
|
19
|
+
private:
|
|
20
|
+
int32_t* _header;
|
|
21
|
+
bool _held;
|
|
22
|
+
std::atomic<bool> _released{false};
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#include "HostMemory.hpp"
|
|
2
|
+
|
|
3
|
+
#include <cerrno>
|
|
4
|
+
#include <cmath>
|
|
5
|
+
#include <cstdlib>
|
|
6
|
+
#include <cstring>
|
|
7
|
+
#include <stdexcept>
|
|
8
|
+
#include <sys/mman.h>
|
|
9
|
+
#include <unistd.h>
|
|
10
|
+
|
|
11
|
+
namespace margelo::nitro::narsil {
|
|
12
|
+
|
|
13
|
+
namespace {
|
|
14
|
+
|
|
15
|
+
constexpr double LARGEST_EXACT_BYTE_COUNT = 9007199254740991.0;
|
|
16
|
+
|
|
17
|
+
const char* errorName(int number) {
|
|
18
|
+
switch (number) {
|
|
19
|
+
case ENOENT: return "ENOENT";
|
|
20
|
+
case EEXIST: return "EEXIST";
|
|
21
|
+
case EISDIR: return "EISDIR";
|
|
22
|
+
case ENOTDIR: return "ENOTDIR";
|
|
23
|
+
case EACCES: return "EACCES";
|
|
24
|
+
case EPERM: return "EPERM";
|
|
25
|
+
case EINVAL: return "EINVAL";
|
|
26
|
+
case ENOTSUP: return "ENOTSUP";
|
|
27
|
+
case ENOSPC: return "ENOSPC";
|
|
28
|
+
case EIO: return "EIO";
|
|
29
|
+
case EBADF: return "EBADF";
|
|
30
|
+
case ENOTEMPTY: return "ENOTEMPTY";
|
|
31
|
+
case EXDEV: return "EXDEV";
|
|
32
|
+
case EMFILE: return "EMFILE";
|
|
33
|
+
case ENFILE: return "ENFILE";
|
|
34
|
+
case EROFS: return "EROFS";
|
|
35
|
+
case EBUSY: return "EBUSY";
|
|
36
|
+
case ENAMETOOLONG: return "ENAMETOOLONG";
|
|
37
|
+
case ELOOP: return "ELOOP";
|
|
38
|
+
case EAGAIN: return "EAGAIN";
|
|
39
|
+
case EFBIG: return "EFBIG";
|
|
40
|
+
case EDQUOT: return "EDQUOT";
|
|
41
|
+
case ENOMEM: return "ENOMEM";
|
|
42
|
+
case ESRCH: return "ESRCH";
|
|
43
|
+
default: return "EUNKNOWN";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
size_t pageBytes() {
|
|
48
|
+
long page = sysconf(_SC_PAGESIZE);
|
|
49
|
+
return page > 0 ? static_cast<size_t>(page) : 4096;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
ReservedRegion::ReservedRegion(uint8_t* data, size_t size, size_t mappedSize)
|
|
55
|
+
: _data(data), _size(size), _mappedSize(mappedSize) {}
|
|
56
|
+
|
|
57
|
+
ReservedRegion::~ReservedRegion() { munmap(_data, _mappedSize); }
|
|
58
|
+
|
|
59
|
+
AllocatedBuffer::AllocatedBuffer(uint8_t* data, size_t size) : _data(data), _size(size) {}
|
|
60
|
+
|
|
61
|
+
AllocatedBuffer::~AllocatedBuffer() { std::free(_data); }
|
|
62
|
+
|
|
63
|
+
void throwSystemError(int number, const std::string& operation, const std::string& subject) {
|
|
64
|
+
std::string message = std::string("[") + errorName(number) + "] " + operation;
|
|
65
|
+
if (!subject.empty()) message += " '" + subject + "'";
|
|
66
|
+
message += ": ";
|
|
67
|
+
message += std::strerror(number);
|
|
68
|
+
throw std::runtime_error(message);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
void throwInvalid(const std::string& message) { throw std::invalid_argument("[EINVAL] " + message); }
|
|
72
|
+
|
|
73
|
+
size_t requireByteCount(double value, const char* what) {
|
|
74
|
+
if (!std::isfinite(value) || value < 0 || value > LARGEST_EXACT_BYTE_COUNT || std::floor(value) != value) {
|
|
75
|
+
throwInvalid(std::string(what) + " must be a whole number of bytes from 0 up");
|
|
76
|
+
}
|
|
77
|
+
return static_cast<size_t>(value);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
uint8_t* bytesWithin(const std::shared_ptr<ArrayBuffer>& buffer, double byteOffset, double byteLength,
|
|
81
|
+
const char* what) {
|
|
82
|
+
if (buffer == nullptr) throwInvalid(std::string(what) + " has no buffer");
|
|
83
|
+
size_t offset = requireByteCount(byteOffset, what);
|
|
84
|
+
size_t length = requireByteCount(byteLength, what);
|
|
85
|
+
size_t held = buffer->size();
|
|
86
|
+
if (offset > held || length > held - offset) {
|
|
87
|
+
throwInvalid(std::string(what) + " reaches past the end of its buffer");
|
|
88
|
+
}
|
|
89
|
+
uint8_t* base = buffer->data();
|
|
90
|
+
if (base == nullptr) {
|
|
91
|
+
if (length == 0) return nullptr;
|
|
92
|
+
throwInvalid(std::string(what) + " has a detached buffer");
|
|
93
|
+
}
|
|
94
|
+
return base + offset;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
std::shared_ptr<ArrayBuffer> reserveRegion(double bytes) {
|
|
98
|
+
size_t size = requireByteCount(bytes, "A reservation");
|
|
99
|
+
if (size == 0) throwInvalid("A reservation needs at least one byte");
|
|
100
|
+
size_t page = pageBytes();
|
|
101
|
+
if (size > SIZE_MAX - page) throwSystemError(ENOMEM, "reserve memory", std::to_string(size) + " bytes");
|
|
102
|
+
size_t mapped = (size + page - 1) / page * page;
|
|
103
|
+
int flags = MAP_PRIVATE | MAP_ANON;
|
|
104
|
+
#ifdef MAP_NORESERVE
|
|
105
|
+
flags |= MAP_NORESERVE;
|
|
106
|
+
#endif
|
|
107
|
+
void* address = mmap(nullptr, mapped, PROT_READ | PROT_WRITE, flags, -1, 0);
|
|
108
|
+
if (address == MAP_FAILED) throwSystemError(errno, "reserve memory", std::to_string(size) + " bytes");
|
|
109
|
+
return std::make_shared<ReservedRegion>(static_cast<uint8_t*>(address), size, mapped);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
std::shared_ptr<ArrayBuffer> allocateZeroed(double bytes) {
|
|
113
|
+
size_t size = requireByteCount(bytes, "An allocation");
|
|
114
|
+
void* data = std::calloc(size == 0 ? 1 : size, 1);
|
|
115
|
+
if (data == nullptr) throwSystemError(ENOMEM, "allocate memory", std::to_string(size) + " bytes");
|
|
116
|
+
return std::make_shared<AllocatedBuffer>(static_cast<uint8_t*>(data), size);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
bool isHostOwned(const std::shared_ptr<ArrayBuffer>& buffer) {
|
|
120
|
+
return std::dynamic_pointer_cast<HostOwnedBuffer>(buffer) != nullptr;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <NitroModules/ArrayBuffer.hpp>
|
|
4
|
+
#include <cstddef>
|
|
5
|
+
#include <cstdint>
|
|
6
|
+
#include <memory>
|
|
7
|
+
#include <string>
|
|
8
|
+
|
|
9
|
+
namespace margelo::nitro::narsil {
|
|
10
|
+
|
|
11
|
+
class HostOwnedBuffer : public ArrayBuffer {
|
|
12
|
+
public:
|
|
13
|
+
bool isOwner() const noexcept override { return true; }
|
|
14
|
+
size_t getExternalMemorySize() const override { return 0; }
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
class ReservedRegion final : public HostOwnedBuffer {
|
|
18
|
+
public:
|
|
19
|
+
ReservedRegion(uint8_t* data, size_t size, size_t mappedSize);
|
|
20
|
+
~ReservedRegion() override;
|
|
21
|
+
uint8_t* data() override { return _data; }
|
|
22
|
+
size_t size() const override { return _size; }
|
|
23
|
+
|
|
24
|
+
private:
|
|
25
|
+
uint8_t* _data;
|
|
26
|
+
size_t _size;
|
|
27
|
+
size_t _mappedSize;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
class AllocatedBuffer final : public HostOwnedBuffer {
|
|
31
|
+
public:
|
|
32
|
+
AllocatedBuffer(uint8_t* data, size_t size);
|
|
33
|
+
~AllocatedBuffer() override;
|
|
34
|
+
uint8_t* data() override { return _data; }
|
|
35
|
+
size_t size() const override { return _size; }
|
|
36
|
+
|
|
37
|
+
private:
|
|
38
|
+
uint8_t* _data;
|
|
39
|
+
size_t _size;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
std::shared_ptr<ArrayBuffer> reserveRegion(double bytes);
|
|
43
|
+
std::shared_ptr<ArrayBuffer> allocateZeroed(double bytes);
|
|
44
|
+
bool isHostOwned(const std::shared_ptr<ArrayBuffer>& buffer);
|
|
45
|
+
|
|
46
|
+
size_t requireByteCount(double value, const char* what);
|
|
47
|
+
uint8_t* bytesWithin(const std::shared_ptr<ArrayBuffer>& buffer, double byteOffset, double byteLength,
|
|
48
|
+
const char* what);
|
|
49
|
+
|
|
50
|
+
[[noreturn]] void throwSystemError(int number, const std::string& operation, const std::string& subject);
|
|
51
|
+
[[noreturn]] void throwInvalid(const std::string& message);
|
|
52
|
+
|
|
53
|
+
}
|