react-native-nitro-storage 0.9.0 → 0.10.1
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/CHANGELOG.md +101 -29
- package/README.md +83 -26
- package/android/build.gradle +3 -1
- package/android/src/main/cpp/AndroidStorageAdapterCpp.cpp +5 -2
- package/android/src/main/java/com/nitrostorage/AndroidStorageAdapter.kt +240 -34
- package/cpp/bindings/HybridStorage.cpp +106 -283
- package/cpp/bindings/HybridStorage.hpp +16 -9
- package/docs/api-reference.md +75 -19
- package/docs/benchmarks.md +6 -1
- package/docs/keychain-lifecycle-testing.md +36 -0
- package/docs/recipes.md +1 -2
- package/docs/secure-storage.md +45 -9
- package/ios/IOSStorageAdapterCpp.mm +391 -175
- package/lib/commonjs/capabilities.js +3 -1
- package/lib/commonjs/capabilities.js.map +1 -1
- package/lib/commonjs/core/durability.js +111 -45
- package/lib/commonjs/core/durability.js.map +1 -1
- package/lib/commonjs/index.js +15 -5
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +219 -97
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/internal.js +3 -11
- package/lib/commonjs/internal.js.map +1 -1
- package/lib/commonjs/shared.js +62 -2
- package/lib/commonjs/shared.js.map +1 -1
- package/lib/commonjs/storage-core.js +883 -163
- package/lib/commonjs/storage-core.js.map +1 -1
- package/lib/commonjs/storage-runtime.js +5 -1
- package/lib/commonjs/storage-runtime.js.map +1 -1
- package/lib/commonjs/testing.js +13 -0
- package/lib/commonjs/testing.js.map +1 -1
- package/lib/module/capabilities.js +2 -1
- package/lib/module/capabilities.js.map +1 -1
- package/lib/module/core/durability.js +111 -45
- package/lib/module/core/durability.js.map +1 -1
- package/lib/module/index.js +12 -8
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +215 -99
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/internal.js +2 -9
- package/lib/module/internal.js.map +1 -1
- package/lib/module/shared.js +60 -2
- package/lib/module/shared.js.map +1 -1
- package/lib/module/storage-core.js +884 -164
- package/lib/module/storage-core.js.map +1 -1
- package/lib/module/storage-runtime.js +4 -1
- package/lib/module/storage-runtime.js.map +1 -1
- package/lib/module/testing.js +8 -1
- package/lib/module/testing.js.map +1 -1
- package/lib/typescript/capabilities.d.ts +2 -1
- package/lib/typescript/capabilities.d.ts.map +1 -1
- package/lib/typescript/core/durability.d.ts +8 -4
- package/lib/typescript/core/durability.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +2 -2
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/index.web.d.ts +3 -29
- package/lib/typescript/index.web.d.ts.map +1 -1
- package/lib/typescript/internal.d.ts +0 -2
- package/lib/typescript/internal.d.ts.map +1 -1
- package/lib/typescript/shared.d.ts +19 -0
- package/lib/typescript/shared.d.ts.map +1 -1
- package/lib/typescript/storage-core.d.ts +15 -6
- package/lib/typescript/storage-core.d.ts.map +1 -1
- package/lib/typescript/storage-runtime.d.ts +2 -1
- package/lib/typescript/storage-runtime.d.ts.map +1 -1
- package/lib/typescript/testing.d.ts +1 -1
- package/lib/typescript/testing.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/capabilities.ts +3 -1
- package/src/core/durability.ts +140 -43
- package/src/index.ts +20 -10
- package/src/index.web.ts +328 -166
- package/src/internal.ts +0 -14
- package/src/shared.ts +85 -0
- package/src/storage-core.ts +1198 -232
- package/src/storage-runtime.ts +6 -0
- package/src/testing.ts +8 -1
|
@@ -15,6 +15,15 @@ namespace margelo::nitro::NitroStorage {
|
|
|
15
15
|
|
|
16
16
|
namespace {
|
|
17
17
|
constexpr int kDefaultBiometricLevel = 2;
|
|
18
|
+
|
|
19
|
+
template <typename Map>
|
|
20
|
+
size_t mapMemorySize(const Map& map) noexcept {
|
|
21
|
+
size_t total = map.size() * sizeof(typename Map::value_type);
|
|
22
|
+
if constexpr (requires { map.bucket_count(); }) {
|
|
23
|
+
total += map.bucket_count() * sizeof(void*);
|
|
24
|
+
}
|
|
25
|
+
return total;
|
|
26
|
+
}
|
|
18
27
|
} // namespace
|
|
19
28
|
|
|
20
29
|
HybridStorage::HybridStorage()
|
|
@@ -56,27 +65,16 @@ void HybridStorage::set(const std::string& key, const std::string& value, double
|
|
|
56
65
|
}
|
|
57
66
|
case Scope::Disk:
|
|
58
67
|
ensureAdapter();
|
|
59
|
-
|
|
60
|
-
nativeAdapter_->setDisk(key, value);
|
|
61
|
-
} catch (const std::exception&) {
|
|
62
|
-
throw;
|
|
63
|
-
} catch (...) {
|
|
64
|
-
throw std::runtime_error("NitroStorage: Disk set failed (unknown error)");
|
|
65
|
-
}
|
|
68
|
+
runAdapterOperation(
|
|
69
|
+
[&] { nativeAdapter_->setDisk(key, value); }, "Disk set");
|
|
66
70
|
break;
|
|
67
71
|
case Scope::Secure:
|
|
68
72
|
ensureAdapter();
|
|
69
|
-
|
|
70
|
-
nativeAdapter_->setSecure(key, value);
|
|
71
|
-
} catch (const std::exception&) {
|
|
72
|
-
throw;
|
|
73
|
-
} catch (...) {
|
|
74
|
-
throw std::runtime_error("NitroStorage: Secure set failed (unknown error)");
|
|
75
|
-
}
|
|
73
|
+
runAdapterOperation(
|
|
74
|
+
[&] { nativeAdapter_->setSecure(key, value); }, "Secure set");
|
|
76
75
|
break;
|
|
77
76
|
}
|
|
78
77
|
|
|
79
|
-
onKeySet(static_cast<int>(s), key);
|
|
80
78
|
notifyListeners(static_cast<int>(s), key, value);
|
|
81
79
|
}
|
|
82
80
|
|
|
@@ -94,22 +92,12 @@ std::optional<std::string> HybridStorage::get(const std::string& key, double sco
|
|
|
94
92
|
}
|
|
95
93
|
case Scope::Disk:
|
|
96
94
|
ensureAdapter();
|
|
97
|
-
|
|
98
|
-
return nativeAdapter_->getDisk(key);
|
|
99
|
-
} catch (const std::exception&) {
|
|
100
|
-
throw;
|
|
101
|
-
} catch (...) {
|
|
102
|
-
throw std::runtime_error("NitroStorage: Disk get failed (unknown error)");
|
|
103
|
-
}
|
|
95
|
+
return runAdapterOperation(
|
|
96
|
+
[&] { return nativeAdapter_->getDisk(key); }, "Disk get");
|
|
104
97
|
case Scope::Secure:
|
|
105
98
|
ensureAdapter();
|
|
106
|
-
|
|
107
|
-
return nativeAdapter_->getSecure(key);
|
|
108
|
-
} catch (const std::exception&) {
|
|
109
|
-
throw;
|
|
110
|
-
} catch (...) {
|
|
111
|
-
throw std::runtime_error("NitroStorage: Secure get failed (unknown error)");
|
|
112
|
-
}
|
|
99
|
+
return runAdapterOperation(
|
|
100
|
+
[&] { return nativeAdapter_->getSecure(key); }, "Secure get");
|
|
113
101
|
}
|
|
114
102
|
|
|
115
103
|
return std::nullopt;
|
|
@@ -126,27 +114,16 @@ void HybridStorage::remove(const std::string& key, double scope) {
|
|
|
126
114
|
}
|
|
127
115
|
case Scope::Disk:
|
|
128
116
|
ensureAdapter();
|
|
129
|
-
|
|
130
|
-
nativeAdapter_->deleteDisk(key);
|
|
131
|
-
} catch (const std::exception&) {
|
|
132
|
-
throw;
|
|
133
|
-
} catch (...) {
|
|
134
|
-
throw std::runtime_error("NitroStorage: Disk delete failed (unknown error)");
|
|
135
|
-
}
|
|
117
|
+
runAdapterOperation(
|
|
118
|
+
[&] { nativeAdapter_->deleteDisk(key); }, "Disk delete");
|
|
136
119
|
break;
|
|
137
120
|
case Scope::Secure:
|
|
138
121
|
ensureAdapter();
|
|
139
|
-
|
|
140
|
-
nativeAdapter_->deleteSecure(key);
|
|
141
|
-
} catch (const std::exception&) {
|
|
142
|
-
throw;
|
|
143
|
-
} catch (...) {
|
|
144
|
-
throw std::runtime_error("NitroStorage: Secure delete failed (unknown error)");
|
|
145
|
-
}
|
|
122
|
+
runAdapterOperation(
|
|
123
|
+
[&] { nativeAdapter_->deleteSecure(key); }, "Secure delete");
|
|
146
124
|
break;
|
|
147
125
|
}
|
|
148
126
|
|
|
149
|
-
onKeyRemove(static_cast<int>(s), key);
|
|
150
127
|
notifyListeners(static_cast<int>(s), key, std::nullopt);
|
|
151
128
|
}
|
|
152
129
|
|
|
@@ -159,16 +136,13 @@ bool HybridStorage::has(const std::string& key, double scope) {
|
|
|
159
136
|
return memoryStore_.find(key) != memoryStore_.end();
|
|
160
137
|
}
|
|
161
138
|
case Scope::Disk:
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
return
|
|
169
|
-
}
|
|
170
|
-
return indexIt->second.count(key) > 0;
|
|
171
|
-
}
|
|
139
|
+
ensureAdapter();
|
|
140
|
+
return runAdapterOperation(
|
|
141
|
+
[&] { return nativeAdapter_->hasDisk(key); }, "Disk has");
|
|
142
|
+
case Scope::Secure:
|
|
143
|
+
ensureAdapter();
|
|
144
|
+
return runAdapterOperation(
|
|
145
|
+
[&] { return nativeAdapter_->hasSecure(key); }, "Secure has");
|
|
172
146
|
}
|
|
173
147
|
return false;
|
|
174
148
|
}
|
|
@@ -187,16 +161,13 @@ std::vector<std::string> HybridStorage::getAllKeys(double scope) {
|
|
|
187
161
|
return keys;
|
|
188
162
|
}
|
|
189
163
|
case Scope::Disk:
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
return
|
|
197
|
-
}
|
|
198
|
-
return toVector(indexIt->second);
|
|
199
|
-
}
|
|
164
|
+
ensureAdapter();
|
|
165
|
+
return runAdapterOperation(
|
|
166
|
+
[&] { return nativeAdapter_->getAllKeysDisk(); }, "Disk getAllKeys");
|
|
167
|
+
case Scope::Secure:
|
|
168
|
+
ensureAdapter();
|
|
169
|
+
return runAdapterOperation(
|
|
170
|
+
[&] { return nativeAdapter_->getAllKeysSecure(); }, "Secure getAllKeys");
|
|
200
171
|
}
|
|
201
172
|
return {};
|
|
202
173
|
}
|
|
@@ -220,23 +191,13 @@ std::vector<std::string> HybridStorage::getKeysByPrefix(const std::string& prefi
|
|
|
220
191
|
return keys;
|
|
221
192
|
}
|
|
222
193
|
case Scope::Disk:
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
return keys;
|
|
231
|
-
}
|
|
232
|
-
keys.reserve(indexIt->second.size());
|
|
233
|
-
for (const auto& key : indexIt->second) {
|
|
234
|
-
if (key.rfind(prefix, 0) == 0) {
|
|
235
|
-
keys.push_back(key);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
return keys;
|
|
239
|
-
}
|
|
194
|
+
ensureAdapter();
|
|
195
|
+
return runAdapterOperation(
|
|
196
|
+
[&] { return nativeAdapter_->getKeysByPrefixDisk(prefix); }, "Disk getKeysByPrefix");
|
|
197
|
+
case Scope::Secure:
|
|
198
|
+
ensureAdapter();
|
|
199
|
+
return runAdapterOperation(
|
|
200
|
+
[&] { return nativeAdapter_->getKeysByPrefixSecure(prefix); }, "Secure getKeysByPrefix");
|
|
240
201
|
}
|
|
241
202
|
return {};
|
|
242
203
|
}
|
|
@@ -250,20 +211,46 @@ double HybridStorage::size(double scope) {
|
|
|
250
211
|
return static_cast<double>(memoryStore_.size());
|
|
251
212
|
}
|
|
252
213
|
case Scope::Disk:
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
return
|
|
260
|
-
}
|
|
261
|
-
return static_cast<double>(indexIt->second.size());
|
|
262
|
-
}
|
|
214
|
+
ensureAdapter();
|
|
215
|
+
return static_cast<double>(runAdapterOperation(
|
|
216
|
+
[&] { return nativeAdapter_->sizeDisk(); }, "Disk size"));
|
|
217
|
+
case Scope::Secure:
|
|
218
|
+
ensureAdapter();
|
|
219
|
+
return static_cast<double>(runAdapterOperation(
|
|
220
|
+
[&] { return nativeAdapter_->sizeSecure(); }, "Secure size"));
|
|
263
221
|
}
|
|
264
222
|
return 0.0;
|
|
265
223
|
}
|
|
266
224
|
|
|
225
|
+
size_t HybridStorage::getExternalMemorySize() noexcept {
|
|
226
|
+
return memorySize();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
size_t HybridStorage::memorySize() noexcept {
|
|
230
|
+
size_t total = 0;
|
|
231
|
+
{
|
|
232
|
+
std::lock_guard<std::mutex> lock(memoryMutex_);
|
|
233
|
+
total += mapMemorySize(memoryStore_);
|
|
234
|
+
for (const auto& [key, value] : memoryStore_) {
|
|
235
|
+
total += key.capacity() * sizeof(std::string::value_type);
|
|
236
|
+
total += value.capacity() * sizeof(std::string::value_type);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
{
|
|
240
|
+
std::lock_guard<std::mutex> lock(listenersMutex_);
|
|
241
|
+
total += mapMemorySize(listeners_);
|
|
242
|
+
for (const auto& [scope, listeners] : listeners_) {
|
|
243
|
+
(void)scope;
|
|
244
|
+
total += listeners.capacity() * sizeof(Listener);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
// The adapter interface exposes no memory-sizing contract, so platform
|
|
248
|
+
// preference/keychain caches are intentionally outside this bounded
|
|
249
|
+
// HybridStorage-owned estimate. std::function target allocations are also
|
|
250
|
+
// opaque; the retained Listener wrapper itself is counted above.
|
|
251
|
+
return total;
|
|
252
|
+
}
|
|
253
|
+
|
|
267
254
|
std::function<void()> HybridStorage::addOnChange(
|
|
268
255
|
double scope,
|
|
269
256
|
const std::function<void(const std::string&, const std::optional<std::string>&)>& callback
|
|
@@ -316,27 +303,16 @@ void HybridStorage::clear(double scope) {
|
|
|
316
303
|
}
|
|
317
304
|
case Scope::Disk:
|
|
318
305
|
ensureAdapter();
|
|
319
|
-
|
|
320
|
-
nativeAdapter_->clearDisk();
|
|
321
|
-
} catch (const std::exception&) {
|
|
322
|
-
throw;
|
|
323
|
-
} catch (...) {
|
|
324
|
-
throw std::runtime_error("NitroStorage: Disk clear failed (unknown error)");
|
|
325
|
-
}
|
|
306
|
+
runAdapterOperation(
|
|
307
|
+
[&] { nativeAdapter_->clearDisk(); }, "Disk clear");
|
|
326
308
|
break;
|
|
327
309
|
case Scope::Secure:
|
|
328
310
|
ensureAdapter();
|
|
329
|
-
|
|
330
|
-
nativeAdapter_->clearSecure();
|
|
331
|
-
} catch (const std::exception&) {
|
|
332
|
-
throw;
|
|
333
|
-
} catch (...) {
|
|
334
|
-
throw std::runtime_error("NitroStorage: Secure clear failed (unknown error)");
|
|
335
|
-
}
|
|
311
|
+
runAdapterOperation(
|
|
312
|
+
[&] { nativeAdapter_->clearSecure(); }, "Secure clear");
|
|
336
313
|
break;
|
|
337
314
|
}
|
|
338
315
|
|
|
339
|
-
onScopeClear(static_cast<int>(s));
|
|
340
316
|
notifyListeners(static_cast<int>(s), kClearSentinelKey, std::nullopt);
|
|
341
317
|
}
|
|
342
318
|
|
|
@@ -357,30 +333,17 @@ void HybridStorage::setBatch(const std::vector<std::string>& keys, const std::ve
|
|
|
357
333
|
}
|
|
358
334
|
case Scope::Disk:
|
|
359
335
|
ensureAdapter();
|
|
360
|
-
|
|
361
|
-
nativeAdapter_->setDiskBatch(keys, values);
|
|
362
|
-
} catch (const std::exception&) {
|
|
363
|
-
throw;
|
|
364
|
-
} catch (...) {
|
|
365
|
-
throw std::runtime_error("NitroStorage: Disk setBatch failed (unknown error)");
|
|
366
|
-
}
|
|
336
|
+
runAdapterOperation(
|
|
337
|
+
[&] { nativeAdapter_->setDiskBatch(keys, values); }, "Disk setBatch");
|
|
367
338
|
break;
|
|
368
339
|
case Scope::Secure:
|
|
369
340
|
ensureAdapter();
|
|
370
|
-
|
|
371
|
-
nativeAdapter_->setSecureBatch(keys, values);
|
|
372
|
-
} catch (const std::exception&) {
|
|
373
|
-
throw;
|
|
374
|
-
} catch (...) {
|
|
375
|
-
throw std::runtime_error("NitroStorage: Secure setBatch failed (unknown error)");
|
|
376
|
-
}
|
|
341
|
+
runAdapterOperation(
|
|
342
|
+
[&] { nativeAdapter_->setSecureBatch(keys, values); }, "Secure setBatch");
|
|
377
343
|
break;
|
|
378
344
|
}
|
|
379
345
|
|
|
380
346
|
const auto scopeValue = static_cast<int>(s);
|
|
381
|
-
for (const auto& key : keys) {
|
|
382
|
-
onKeySet(scopeValue, key);
|
|
383
|
-
}
|
|
384
347
|
const auto listeners = copyListenersForScope(scopeValue);
|
|
385
348
|
for (size_t i = 0; i < keys.size(); ++i) {
|
|
386
349
|
notifyListeners(listeners, keys[i], values[i]);
|
|
@@ -408,29 +371,13 @@ std::vector<std::optional<std::string>> HybridStorage::getBatch(const std::vecto
|
|
|
408
371
|
}
|
|
409
372
|
case Scope::Disk: {
|
|
410
373
|
ensureAdapter();
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
values = nativeAdapter_->getDiskBatch(keys);
|
|
414
|
-
} catch (const std::exception&) {
|
|
415
|
-
throw;
|
|
416
|
-
} catch (...) {
|
|
417
|
-
throw std::runtime_error("NitroStorage: Disk getBatch failed (unknown error)");
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
return values;
|
|
374
|
+
return runAdapterOperation(
|
|
375
|
+
[&] { return nativeAdapter_->getDiskBatch(keys); }, "Disk getBatch");
|
|
421
376
|
}
|
|
422
377
|
case Scope::Secure: {
|
|
423
378
|
ensureAdapter();
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
values = nativeAdapter_->getSecureBatch(keys);
|
|
427
|
-
} catch (const std::exception&) {
|
|
428
|
-
throw;
|
|
429
|
-
} catch (...) {
|
|
430
|
-
throw std::runtime_error("NitroStorage: Secure getBatch failed (unknown error)");
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
return values;
|
|
379
|
+
return runAdapterOperation(
|
|
380
|
+
[&] { return nativeAdapter_->getSecureBatch(keys); }, "Secure getBatch");
|
|
434
381
|
}
|
|
435
382
|
}
|
|
436
383
|
|
|
@@ -450,30 +397,17 @@ void HybridStorage::removeBatch(const std::vector<std::string>& keys, double sco
|
|
|
450
397
|
}
|
|
451
398
|
case Scope::Disk:
|
|
452
399
|
ensureAdapter();
|
|
453
|
-
|
|
454
|
-
nativeAdapter_->deleteDiskBatch(keys);
|
|
455
|
-
} catch (const std::exception&) {
|
|
456
|
-
throw;
|
|
457
|
-
} catch (...) {
|
|
458
|
-
throw std::runtime_error("NitroStorage: Disk removeBatch failed (unknown error)");
|
|
459
|
-
}
|
|
400
|
+
runAdapterOperation(
|
|
401
|
+
[&] { nativeAdapter_->deleteDiskBatch(keys); }, "Disk removeBatch");
|
|
460
402
|
break;
|
|
461
403
|
case Scope::Secure:
|
|
462
404
|
ensureAdapter();
|
|
463
|
-
|
|
464
|
-
nativeAdapter_->deleteSecureBatch(keys);
|
|
465
|
-
} catch (const std::exception&) {
|
|
466
|
-
throw;
|
|
467
|
-
} catch (...) {
|
|
468
|
-
throw std::runtime_error("NitroStorage: Secure removeBatch failed (unknown error)");
|
|
469
|
-
}
|
|
405
|
+
runAdapterOperation(
|
|
406
|
+
[&] { nativeAdapter_->deleteSecureBatch(keys); }, "Secure removeBatch");
|
|
470
407
|
break;
|
|
471
408
|
}
|
|
472
409
|
|
|
473
410
|
const auto scopeValue = static_cast<int>(s);
|
|
474
|
-
for (const auto& key : keys) {
|
|
475
|
-
onKeyRemove(scopeValue, key);
|
|
476
|
-
}
|
|
477
411
|
const auto listeners = copyListenersForScope(scopeValue);
|
|
478
412
|
for (const auto& key : keys) {
|
|
479
413
|
notifyListeners(listeners, key, std::nullopt);
|
|
@@ -543,43 +477,23 @@ void HybridStorage::setSecureBiometricWithLevel(const std::string& key, const st
|
|
|
543
477
|
"NitroStorage: Invalid biometric level");
|
|
544
478
|
}
|
|
545
479
|
ensureAdapter();
|
|
546
|
-
|
|
547
|
-
nativeAdapter_->setSecureBiometricWithLevel(
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
intLevel
|
|
551
|
-
);
|
|
552
|
-
onKeySet(static_cast<int>(Scope::Secure), key);
|
|
553
|
-
notifyListeners(static_cast<int>(Scope::Secure), key, value);
|
|
554
|
-
} catch (const std::exception&) {
|
|
555
|
-
throw;
|
|
556
|
-
} catch (...) {
|
|
557
|
-
throw std::runtime_error("NitroStorage: Biometric set failed (unknown error)");
|
|
558
|
-
}
|
|
480
|
+
runAdapterOperation(
|
|
481
|
+
[&] { nativeAdapter_->setSecureBiometricWithLevel(key, value, intLevel); },
|
|
482
|
+
"Biometric set");
|
|
483
|
+
notifyListeners(static_cast<int>(Scope::Secure), key, value);
|
|
559
484
|
}
|
|
560
485
|
|
|
561
486
|
std::optional<std::string> HybridStorage::getSecureBiometric(const std::string& key) {
|
|
562
487
|
ensureAdapter();
|
|
563
|
-
|
|
564
|
-
return nativeAdapter_->getSecureBiometric(key);
|
|
565
|
-
} catch (const std::exception&) {
|
|
566
|
-
throw;
|
|
567
|
-
} catch (...) {
|
|
568
|
-
throw std::runtime_error("NitroStorage: Biometric get failed (unknown error)");
|
|
569
|
-
}
|
|
488
|
+
return runAdapterOperation(
|
|
489
|
+
[&] { return nativeAdapter_->getSecureBiometric(key); }, "Biometric get");
|
|
570
490
|
}
|
|
571
491
|
|
|
572
492
|
void HybridStorage::deleteSecureBiometric(const std::string& key) {
|
|
573
493
|
ensureAdapter();
|
|
574
|
-
|
|
575
|
-
nativeAdapter_->deleteSecureBiometric(key);
|
|
576
|
-
|
|
577
|
-
notifyListeners(static_cast<int>(Scope::Secure), key, std::nullopt);
|
|
578
|
-
} catch (const std::exception&) {
|
|
579
|
-
throw;
|
|
580
|
-
} catch (...) {
|
|
581
|
-
throw std::runtime_error("NitroStorage: Biometric delete failed (unknown error)");
|
|
582
|
-
}
|
|
494
|
+
runAdapterOperation(
|
|
495
|
+
[&] { nativeAdapter_->deleteSecureBiometric(key); }, "Biometric delete");
|
|
496
|
+
notifyListeners(static_cast<int>(Scope::Secure), key, std::nullopt);
|
|
583
497
|
}
|
|
584
498
|
|
|
585
499
|
bool HybridStorage::hasSecureBiometric(const std::string& key) {
|
|
@@ -589,22 +503,9 @@ bool HybridStorage::hasSecureBiometric(const std::string& key) {
|
|
|
589
503
|
|
|
590
504
|
void HybridStorage::clearSecureBiometric() {
|
|
591
505
|
ensureAdapter();
|
|
592
|
-
|
|
593
|
-
nativeAdapter_->clearSecureBiometric();
|
|
594
|
-
|
|
595
|
-
// (which will now correctly exclude the cleared biometric keys).
|
|
596
|
-
// We do NOT call onScopeClear() here because that would also clear the index
|
|
597
|
-
// contents for regular secure keys; marking stale is sufficient.
|
|
598
|
-
{
|
|
599
|
-
std::lock_guard<std::mutex> lock(keyIndexMutex_);
|
|
600
|
-
keyIndexHydrated_[static_cast<int>(Scope::Secure)] = false;
|
|
601
|
-
}
|
|
602
|
-
notifyListeners(static_cast<int>(Scope::Secure), kClearSentinelKey, std::nullopt);
|
|
603
|
-
} catch (const std::exception&) {
|
|
604
|
-
throw;
|
|
605
|
-
} catch (...) {
|
|
606
|
-
throw std::runtime_error("NitroStorage: Biometric clear failed (unknown error)");
|
|
607
|
-
}
|
|
506
|
+
runAdapterOperation(
|
|
507
|
+
[&] { nativeAdapter_->clearSecureBiometric(); }, "Biometric clear");
|
|
508
|
+
notifyListeners(static_cast<int>(Scope::Secure), kClearSentinelKey, std::nullopt);
|
|
608
509
|
}
|
|
609
510
|
|
|
610
511
|
// --- Internal ---
|
|
@@ -651,84 +552,6 @@ void HybridStorage::notifyListeners(
|
|
|
651
552
|
notifyListeners(listeners, key, value);
|
|
652
553
|
}
|
|
653
554
|
|
|
654
|
-
std::vector<std::string> HybridStorage::toVector(const std::unordered_set<std::string>& keys) {
|
|
655
|
-
std::vector<std::string> values;
|
|
656
|
-
values.reserve(keys.size());
|
|
657
|
-
for (const auto& key : keys) {
|
|
658
|
-
values.push_back(key);
|
|
659
|
-
}
|
|
660
|
-
return values;
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
void HybridStorage::ensureKeyIndexHydrated(int scope) {
|
|
664
|
-
if (scope != static_cast<int>(Scope::Disk) && scope != static_cast<int>(Scope::Secure)) {
|
|
665
|
-
return;
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
std::lock_guard<std::mutex> lock(keyIndexMutex_);
|
|
669
|
-
auto hydratedIt = keyIndexHydrated_.find(scope);
|
|
670
|
-
if (hydratedIt != keyIndexHydrated_.end() && hydratedIt->second) {
|
|
671
|
-
return;
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
ensureAdapter();
|
|
675
|
-
std::vector<std::string> keys;
|
|
676
|
-
try {
|
|
677
|
-
if (scope == static_cast<int>(Scope::Disk)) {
|
|
678
|
-
keys = nativeAdapter_->getAllKeysDisk();
|
|
679
|
-
} else {
|
|
680
|
-
keys = nativeAdapter_->getAllKeysSecure();
|
|
681
|
-
}
|
|
682
|
-
} catch (const std::exception&) {
|
|
683
|
-
throw;
|
|
684
|
-
} catch (...) {
|
|
685
|
-
throw std::runtime_error("NitroStorage: Key index hydration failed (unknown error)");
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
auto& index = keyIndex_[scope];
|
|
689
|
-
index.clear();
|
|
690
|
-
for (const auto& key : keys) {
|
|
691
|
-
index.insert(key);
|
|
692
|
-
}
|
|
693
|
-
keyIndexHydrated_[scope] = true;
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
void HybridStorage::onKeySet(int scope, const std::string& key) {
|
|
697
|
-
if (scope != static_cast<int>(Scope::Disk) && scope != static_cast<int>(Scope::Secure)) {
|
|
698
|
-
return;
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
std::lock_guard<std::mutex> lock(keyIndexMutex_);
|
|
702
|
-
auto hydratedIt = keyIndexHydrated_.find(scope);
|
|
703
|
-
if (hydratedIt != keyIndexHydrated_.end() && hydratedIt->second) {
|
|
704
|
-
keyIndex_[scope].insert(key);
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
void HybridStorage::onKeyRemove(int scope, const std::string& key) {
|
|
709
|
-
if (scope != static_cast<int>(Scope::Disk) && scope != static_cast<int>(Scope::Secure)) {
|
|
710
|
-
return;
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
std::lock_guard<std::mutex> lock(keyIndexMutex_);
|
|
714
|
-
auto hydratedIt = keyIndexHydrated_.find(scope);
|
|
715
|
-
if (hydratedIt != keyIndexHydrated_.end() && hydratedIt->second) {
|
|
716
|
-
keyIndex_[scope].erase(key);
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
void HybridStorage::onScopeClear(int scope) {
|
|
721
|
-
if (scope != static_cast<int>(Scope::Disk) && scope != static_cast<int>(Scope::Secure)) {
|
|
722
|
-
return;
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
std::lock_guard<std::mutex> lock(keyIndexMutex_);
|
|
726
|
-
auto hydratedIt = keyIndexHydrated_.find(scope);
|
|
727
|
-
if (hydratedIt != keyIndexHydrated_.end() && hydratedIt->second) {
|
|
728
|
-
keyIndex_[scope].clear();
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
|
|
732
555
|
void HybridStorage::ensureAdapter() const {
|
|
733
556
|
if (!nativeAdapter_) {
|
|
734
557
|
throw std::runtime_error("NitroStorage: Native adapter not initialized");
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
#include <functional>
|
|
11
11
|
#include <memory>
|
|
12
12
|
#include <vector>
|
|
13
|
-
#include <unordered_set>
|
|
14
13
|
#include <atomic>
|
|
15
14
|
#include <array>
|
|
15
|
+
#include <stdexcept>
|
|
16
16
|
|
|
17
17
|
namespace margelo::nitro::NitroStorage {
|
|
18
18
|
|
|
@@ -38,6 +38,7 @@ public:
|
|
|
38
38
|
std::vector<std::string> getAllKeys(double scope) override;
|
|
39
39
|
std::vector<std::string> getKeysByPrefix(const std::string& prefix, double scope) override;
|
|
40
40
|
double size(double scope) override;
|
|
41
|
+
size_t getExternalMemorySize() noexcept override;
|
|
41
42
|
void setBatch(const std::vector<std::string>& keys, const std::vector<std::string>& values, double scope) override;
|
|
42
43
|
std::vector<std::optional<std::string>> getBatch(const std::vector<std::string>& keys, double scope) override;
|
|
43
44
|
void removeBatch(const std::vector<std::string>& keys, double scope) override;
|
|
@@ -79,9 +80,6 @@ private:
|
|
|
79
80
|
// Lock-free fast path: skip locking/copying the listener vector on the
|
|
80
81
|
// write/notify path when no listeners are registered for a scope.
|
|
81
82
|
std::array<std::atomic<size_t>, 3> listenerScopeCounts_{};
|
|
82
|
-
HybridStorageMap<int, std::unordered_set<std::string>> keyIndex_;
|
|
83
|
-
HybridStorageMap<int, bool> keyIndexHydrated_;
|
|
84
|
-
std::mutex keyIndexMutex_;
|
|
85
83
|
|
|
86
84
|
std::vector<Listener> copyListenersForScope(int scope);
|
|
87
85
|
void notifyListeners(
|
|
@@ -90,13 +88,22 @@ private:
|
|
|
90
88
|
const std::optional<std::string>& value
|
|
91
89
|
);
|
|
92
90
|
void notifyListeners(int scope, const std::string& key, const std::optional<std::string>& value);
|
|
93
|
-
std::vector<std::string> toVector(const std::unordered_set<std::string>& keys);
|
|
94
|
-
void ensureKeyIndexHydrated(int scope);
|
|
95
|
-
void onKeySet(int scope, const std::string& key);
|
|
96
|
-
void onKeyRemove(int scope, const std::string& key);
|
|
97
|
-
void onScopeClear(int scope);
|
|
98
91
|
void ensureAdapter() const;
|
|
99
92
|
Scope toScope(double scopeValue);
|
|
93
|
+
size_t memorySize() noexcept;
|
|
94
|
+
|
|
95
|
+
template <typename TOperation>
|
|
96
|
+
auto runAdapterOperation(TOperation&& operation, const char* description) -> decltype(operation()) {
|
|
97
|
+
try {
|
|
98
|
+
return operation();
|
|
99
|
+
} catch (const std::exception&) {
|
|
100
|
+
throw;
|
|
101
|
+
} catch (...) {
|
|
102
|
+
throw std::runtime_error(
|
|
103
|
+
std::string("NitroStorage: ") + description + " failed (unknown error)"
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
100
107
|
|
|
101
108
|
static constexpr const char* kClearSentinelKey = "";
|
|
102
109
|
};
|