small-cache 0.0.1__tar.gz → 0.0.3__tar.gz
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.
- {small_cache-0.0.1 → small_cache-0.0.3}/.github/workflows/pip.yml +2 -2
- {small_cache-0.0.1 → small_cache-0.0.3}/.github/workflows/wheels.yml +7 -7
- {small_cache-0.0.1 → small_cache-0.0.3}/CMakeLists.txt +5 -5
- {small_cache-0.0.1 → small_cache-0.0.3}/PKG-INFO +1 -1
- {small_cache-0.0.1 → small_cache-0.0.3}/pyproject.toml +1 -1
- {small_cache-0.0.1 → small_cache-0.0.3}/src/small_cache.cpp +112 -94
- {small_cache-0.0.1 → small_cache-0.0.3}/.github/dependabot.yml +0 -0
- {small_cache-0.0.1 → small_cache-0.0.3}/.gitignore +0 -0
- {small_cache-0.0.1 → small_cache-0.0.3}/README.md +0 -0
- {small_cache-0.0.1 → small_cache-0.0.3}/src/small_cache/__init__.py +0 -0
- {small_cache-0.0.1 → small_cache-0.0.3}/tests/test_basic.py +0 -0
|
@@ -11,7 +11,7 @@ jobs:
|
|
|
11
11
|
name: Build SDist
|
|
12
12
|
runs-on: ubuntu-latest
|
|
13
13
|
steps:
|
|
14
|
-
- uses: actions/checkout@
|
|
14
|
+
- uses: actions/checkout@v6
|
|
15
15
|
with:
|
|
16
16
|
submodules: true
|
|
17
17
|
|
|
@@ -21,7 +21,7 @@ jobs:
|
|
|
21
21
|
- name: Check metadata
|
|
22
22
|
run: pipx run twine check dist/*
|
|
23
23
|
|
|
24
|
-
- uses: actions/upload-artifact@
|
|
24
|
+
- uses: actions/upload-artifact@v5
|
|
25
25
|
with:
|
|
26
26
|
name: dist-sdist
|
|
27
27
|
path: dist/*.tar.gz
|
|
@@ -36,18 +36,18 @@ jobs:
|
|
|
36
36
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
37
37
|
|
|
38
38
|
steps:
|
|
39
|
-
- uses: actions/checkout@
|
|
39
|
+
- uses: actions/checkout@v6
|
|
40
40
|
with:
|
|
41
41
|
submodules: true
|
|
42
42
|
|
|
43
|
-
- uses: pypa/cibuildwheel@v3.
|
|
43
|
+
- uses: pypa/cibuildwheel@v3.3
|
|
44
44
|
|
|
45
45
|
- name: Verify clean directory
|
|
46
46
|
run: git diff --exit-code
|
|
47
47
|
shell: bash
|
|
48
48
|
|
|
49
49
|
- name: Upload wheels
|
|
50
|
-
uses: actions/upload-artifact@
|
|
50
|
+
uses: actions/upload-artifact@v5
|
|
51
51
|
with:
|
|
52
52
|
path: wheelhouse/*.whl
|
|
53
53
|
name: dist-${{ matrix.os }}
|
|
@@ -59,8 +59,8 @@ jobs:
|
|
|
59
59
|
if: github.event_name == 'release' && github.event.action == 'published'
|
|
60
60
|
|
|
61
61
|
steps:
|
|
62
|
-
- uses: actions/setup-python@
|
|
63
|
-
- uses: actions/download-artifact@
|
|
62
|
+
- uses: actions/setup-python@v6
|
|
63
|
+
- uses: actions/download-artifact@v6
|
|
64
64
|
with:
|
|
65
65
|
path: dist
|
|
66
66
|
pattern: dist-*
|
|
@@ -66,7 +66,7 @@ include(FetchContent)
|
|
|
66
66
|
FetchContent_Declare(
|
|
67
67
|
glaze
|
|
68
68
|
GIT_REPOSITORY https://github.com/stephenberry/glaze.git
|
|
69
|
-
GIT_TAG
|
|
69
|
+
GIT_TAG v6.1.0
|
|
70
70
|
GIT_SHALLOW TRUE
|
|
71
71
|
)
|
|
72
72
|
|
|
@@ -75,7 +75,7 @@ FetchContent_MakeAvailable(glaze)
|
|
|
75
75
|
FetchContent_Declare(
|
|
76
76
|
tsl_sparse_map
|
|
77
77
|
GIT_REPOSITORY https://github.com/Tessil/sparse-map
|
|
78
|
-
GIT_TAG
|
|
78
|
+
GIT_TAG v0.7.0
|
|
79
79
|
GIT_SHALLOW TRUE
|
|
80
80
|
)
|
|
81
81
|
FetchContent_MakeAvailable(tsl_sparse_map)
|
|
@@ -83,7 +83,7 @@ FetchContent_MakeAvailable(tsl_sparse_map)
|
|
|
83
83
|
FetchContent_Declare(
|
|
84
84
|
absl
|
|
85
85
|
GIT_REPOSITORY https://github.com/abseil/abseil-cpp.git
|
|
86
|
-
GIT_TAG
|
|
86
|
+
GIT_TAG 20250814.1
|
|
87
87
|
GIT_SHALLOW TRUE
|
|
88
88
|
OVERRIDE_FIND_PACKAGE TRUE
|
|
89
89
|
EXCLUDE_FROM_ALL
|
|
@@ -92,14 +92,14 @@ FetchContent_Declare(
|
|
|
92
92
|
find_package(
|
|
93
93
|
absl
|
|
94
94
|
REQUIRED
|
|
95
|
-
COMPONENTS flat_hash_map hash
|
|
95
|
+
COMPONENTS flat_hash_map flat_hash_set hash
|
|
96
96
|
)
|
|
97
97
|
|
|
98
98
|
Set(FETCHCONTENT_QUIET FALSE)
|
|
99
99
|
FetchContent_Declare(
|
|
100
100
|
Boost
|
|
101
101
|
URL https://github.com/boostorg/boost/releases/download/boost-1.88.0/boost-1.88.0-cmake.7z
|
|
102
|
-
GIT_TAG "boost-1.
|
|
102
|
+
GIT_TAG "boost-1.89.0"
|
|
103
103
|
USES_TERMINAL_DOWNLOAD TRUE
|
|
104
104
|
GIT_PROGRESS TRUE
|
|
105
105
|
DOWNLOAD_NO_EXTRACT FALSE
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
#include <tsl/sparse_map.h>
|
|
9
9
|
#include <absl/hash/hash.h>
|
|
10
10
|
#include <absl/container/flat_hash_map.h>
|
|
11
|
+
#include <absl/container/flat_hash_set.h>
|
|
11
12
|
#include <boost/flyweight.hpp>
|
|
12
13
|
#include <optional>
|
|
13
14
|
#include <algorithm>
|
|
@@ -55,7 +56,7 @@ class SmallCache {
|
|
|
55
56
|
using str = std::string;
|
|
56
57
|
using strVec = std::vector<str>;
|
|
57
58
|
using fwStr = boost::flyweight<str>;
|
|
58
|
-
using strVecUPtr = std::unique_ptr<std::vector<fwStr
|
|
59
|
+
using strVecUPtr = std::unique_ptr<std::vector<fwStr>>;
|
|
59
60
|
|
|
60
61
|
using AttributeValue = std::variant<std::monostate, double, bool, fwStr, strVecUPtr>;
|
|
61
62
|
using pyAttrValue = std::variant<std::monostate, bool, double, str, strVec>;
|
|
@@ -109,7 +110,7 @@ public:
|
|
|
109
110
|
return (attrs_flags[w] >> b) & 1u;
|
|
110
111
|
}
|
|
111
112
|
|
|
112
|
-
[[nodiscard]] std::optional<std::reference_wrapper<AttributeValue
|
|
113
|
+
[[nodiscard]] std::optional<std::reference_wrapper<AttributeValue>> getValue(size_t idx) noexcept {
|
|
113
114
|
if (!hasIdx(idx))
|
|
114
115
|
return std::nullopt;
|
|
115
116
|
|
|
@@ -130,7 +131,7 @@ public:
|
|
|
130
131
|
return value.size() > pos ? std::optional{std::ref(value[pos])} : std::nullopt; // just-in-case
|
|
131
132
|
}
|
|
132
133
|
|
|
133
|
-
[[nodiscard]] std::optional<std::reference_wrapper<const AttributeValue
|
|
134
|
+
[[nodiscard]] std::optional<std::reference_wrapper<const AttributeValue>> getValue(size_t idx) const noexcept {
|
|
134
135
|
if (auto ref = const_cast<MarkedItem *>(this)->getValue(idx))
|
|
135
136
|
return std::cref(ref->get());
|
|
136
137
|
return std::nullopt;
|
|
@@ -142,7 +143,7 @@ public:
|
|
|
142
143
|
item.attrs_flags.fill(0);
|
|
143
144
|
|
|
144
145
|
// build a slot for each possible attr index
|
|
145
|
-
std::vector<std::optional<AttributeValue
|
|
146
|
+
std::vector<std::optional<AttributeValue>> slots(attrMap.size());
|
|
146
147
|
|
|
147
148
|
// 1) collect into slots[] by index
|
|
148
149
|
for (auto &[name, pyVal]: attrs) {
|
|
@@ -185,29 +186,29 @@ public:
|
|
|
185
186
|
|
|
186
187
|
static AttributeValue convert_value(const json::AttributeValue &src) {
|
|
187
188
|
return std::visit(overloaded{
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
189
|
+
[](bool b) -> AttributeValue { return b; },
|
|
190
|
+
[](double d) -> AttributeValue { return d; },
|
|
191
|
+
[](const str &s) -> AttributeValue {
|
|
192
|
+
// if (s.empty()) return std::monostate{};
|
|
193
|
+
return fwStr{s};
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
[](const std::vector<glz::raw_json> &json_vec) -> AttributeValue {
|
|
197
|
+
auto out = std::make_unique<std::vector<fwStr>>();
|
|
198
|
+
out->reserve(json_vec.size());
|
|
199
|
+
for (auto &r: json_vec)
|
|
200
|
+
if (!r.str.empty())
|
|
201
|
+
out->emplace_back(r.str);
|
|
202
|
+
// if (out->empty()) {
|
|
203
|
+
// return std::monostate{};
|
|
204
|
+
// }
|
|
205
|
+
out->shrink_to_fit();
|
|
206
|
+
return out;
|
|
207
|
+
},
|
|
208
|
+
[](const std::optional<glz::raw_json> &o) -> AttributeValue {
|
|
209
|
+
// if (!o.has_value() || o->str.empty()) return std::monostate{};
|
|
210
|
+
return fwStr{o->str};
|
|
211
|
+
},
|
|
211
212
|
|
|
212
213
|
},
|
|
213
214
|
src);
|
|
@@ -216,17 +217,17 @@ public:
|
|
|
216
217
|
|
|
217
218
|
static pyAttrValue convert_valueJ(const json::AttributeValue &src) {
|
|
218
219
|
return std::visit(overloaded{
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
220
|
+
[](std::monostate b) -> pyAttrValue { return b; },
|
|
221
|
+
[](bool b) -> pyAttrValue { return b; },
|
|
222
|
+
[](double d) -> pyAttrValue { return d; },
|
|
223
|
+
[](const str &s) -> pyAttrValue { return s; },
|
|
224
|
+
[](const std::vector<glz::raw_json> &json_vec) -> pyAttrValue {
|
|
225
|
+
return json_vec | std::views::transform([](const glz::raw_json &j) -> str {
|
|
226
|
+
return j.str;
|
|
227
|
+
}) |
|
|
228
|
+
std::ranges::to<strVec>();
|
|
229
|
+
},
|
|
230
|
+
[](const std::optional<glz::raw_json> &o) -> pyAttrValue { return o ? o->str : ""; },
|
|
230
231
|
|
|
231
232
|
},
|
|
232
233
|
src);
|
|
@@ -234,54 +235,54 @@ public:
|
|
|
234
235
|
|
|
235
236
|
static pyAttrValue convert_value(const AttributeValue &src) {
|
|
236
237
|
return std::visit(overloaded{
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
238
|
+
[](std::monostate b) -> pyAttrValue { return b; },
|
|
239
|
+
[](bool b) -> pyAttrValue { return b; },
|
|
240
|
+
[](double d) -> pyAttrValue { return d; },
|
|
241
|
+
[](const fwStr &s) -> pyAttrValue { return s; },
|
|
242
|
+
[](const strVecUPtr &fw_vec) -> pyAttrValue {
|
|
243
|
+
return *fw_vec | std::views::transform([](const fwStr &s) -> str { return s; }) |
|
|
244
|
+
std::ranges::to<strVec>();
|
|
245
|
+
},
|
|
245
246
|
},
|
|
246
247
|
src);
|
|
247
248
|
}
|
|
248
249
|
|
|
249
250
|
static AttributeValue convert_value(const pyAttrValue &src) {
|
|
250
251
|
return std::visit(overloaded{
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
252
|
+
[](std::monostate b) -> AttributeValue { return b; },
|
|
253
|
+
[](bool b) -> AttributeValue { return b; },
|
|
254
|
+
[](double d) -> AttributeValue { return d; },
|
|
255
|
+
[](const str &s) -> AttributeValue { return fwStr{s}; },
|
|
256
|
+
[](const strVec &vec) -> AttributeValue {
|
|
257
|
+
auto out = std::make_unique<std::vector<fwStr>>();
|
|
258
|
+
out->reserve(vec.size());
|
|
259
|
+
for (auto &r: vec) {
|
|
260
|
+
out->emplace_back(r);
|
|
261
|
+
}
|
|
262
|
+
out->shrink_to_fit();
|
|
263
|
+
return out;
|
|
264
|
+
},
|
|
264
265
|
},
|
|
265
266
|
src);
|
|
266
267
|
}
|
|
267
268
|
|
|
268
269
|
static str to_string(const pyAttrValue &src) {
|
|
269
270
|
return std::visit(overloaded{
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
271
|
+
[](std::monostate) -> str { return "null"; },
|
|
272
|
+
[](bool b) -> str { return b ? "true" : "false"; },
|
|
273
|
+
[](double d) -> str { return std::to_string(d); },
|
|
274
|
+
[](const str &s) -> str { return s; },
|
|
275
|
+
|
|
276
|
+
[](const strVec &vecptr) -> str {
|
|
277
|
+
str out = "[";
|
|
278
|
+
|
|
279
|
+
for (auto &r: vecptr) {
|
|
280
|
+
out.append(r);
|
|
281
|
+
out.append(",");
|
|
282
|
+
}
|
|
283
|
+
out.append("]");
|
|
284
|
+
return out;
|
|
285
|
+
},
|
|
285
286
|
},
|
|
286
287
|
src);
|
|
287
288
|
}
|
|
@@ -295,7 +296,7 @@ public:
|
|
|
295
296
|
increaseIteratorVersion();
|
|
296
297
|
}
|
|
297
298
|
|
|
298
|
-
std::vector<pyAttrValue>
|
|
299
|
+
std::vector<pyAttrValue> get_one(const str &id, const strVec &attributes) {
|
|
299
300
|
if (attributes.empty()) {
|
|
300
301
|
return {};
|
|
301
302
|
}
|
|
@@ -311,41 +312,49 @@ public:
|
|
|
311
312
|
return {};
|
|
312
313
|
return convert_value(*attr_value);
|
|
313
314
|
}) |
|
|
314
|
-
std::ranges::to<std::vector<pyAttrValue
|
|
315
|
+
std::ranges::to<std::vector<pyAttrValue>>();
|
|
315
316
|
}
|
|
316
317
|
return {};
|
|
317
318
|
}
|
|
318
319
|
|
|
319
|
-
|
|
320
|
-
struct getAllResponse {
|
|
320
|
+
struct getManyResponse {
|
|
321
321
|
uint64_t iterator_version{};
|
|
322
322
|
uint64_t iterator{};
|
|
323
|
-
std::vector<std::pair<str, std::vector<pyAttrValue
|
|
323
|
+
std::vector<std::pair<str, std::vector<pyAttrValue>>> result{};
|
|
324
324
|
};
|
|
325
325
|
|
|
326
|
-
str
|
|
327
|
-
|
|
326
|
+
str get_many(const strVec &ids, const strVec &attributes, uint64_t per_iteration = 10000,
|
|
327
|
+
uint64_t iterator_version = 0, uint64_t iterator = 0) {
|
|
328
328
|
if (transactionOpened)
|
|
329
|
-
throw std::runtime_error("Can't get
|
|
329
|
+
throw std::runtime_error("Can't get many when transaction is opened");
|
|
330
330
|
if (iterator && iterator_version == 0) {
|
|
331
331
|
throw std::runtime_error("Invalid iterator version");
|
|
332
332
|
}
|
|
333
333
|
if (iterator && iterator_version != this->iterator_version)
|
|
334
334
|
throw std::runtime_error("Iterator already invalidated");
|
|
335
|
-
|
|
336
|
-
std::vector<str> keys
|
|
335
|
+
getManyResponse resp{};
|
|
336
|
+
std::vector<str> keys;
|
|
337
|
+
if (ids.empty()) {
|
|
338
|
+
keys = cache | std::views::keys | std::ranges::to<std::vector>();
|
|
339
|
+
std::ranges::sort(keys);
|
|
340
|
+
} else {
|
|
341
|
+
absl::flat_hash_set<str> set_of_ids(ids.begin(), ids.end());
|
|
342
|
+
keys = cache | std::views::keys |
|
|
343
|
+
std::views::filter([&](const auto &key) { return set_of_ids.contains(key); }) |
|
|
344
|
+
std::ranges::to<std::vector>();
|
|
345
|
+
}
|
|
337
346
|
std::ranges::sort(keys);
|
|
338
347
|
|
|
339
|
-
uint64_t start = iterator;
|
|
340
|
-
uint64_t available = keys.size() > start ? keys.size() - start : 0;
|
|
341
|
-
uint64_t to_take = std::min<uint64_t>(available, per_iteration);
|
|
348
|
+
const uint64_t start = iterator;
|
|
349
|
+
const uint64_t available = keys.size() > start ? keys.size() - start : 0;
|
|
350
|
+
const uint64_t to_take = std::min<uint64_t>(available, per_iteration);
|
|
342
351
|
|
|
343
352
|
resp.result.reserve(to_take);
|
|
344
353
|
|
|
345
354
|
auto i = iterator;
|
|
346
355
|
while (i < iterator + per_iteration && i < keys.size()) {
|
|
347
356
|
const auto &key = keys[i];
|
|
348
|
-
resp.result.emplace_back(key,
|
|
357
|
+
resp.result.emplace_back(key, get_one(key, attributes));
|
|
349
358
|
i++;
|
|
350
359
|
}
|
|
351
360
|
resp.iterator_version = this->iterator_version;
|
|
@@ -354,7 +363,7 @@ public:
|
|
|
354
363
|
}
|
|
355
364
|
|
|
356
365
|
|
|
357
|
-
void begin_transaction(uint64_t estimated_number_of_items = 0) {
|
|
366
|
+
void begin_transaction(uint64_t estimated_number_of_items = 0, bool remove_old_items = true) {
|
|
358
367
|
if (transactionOpened) {
|
|
359
368
|
throw std::runtime_error("Transaction already open");
|
|
360
369
|
}
|
|
@@ -364,6 +373,7 @@ public:
|
|
|
364
373
|
}
|
|
365
374
|
oldCacheSize = cache.size();
|
|
366
375
|
transactionOpened = true;
|
|
376
|
+
transactionShouldRemoveOldItems = remove_old_items;
|
|
367
377
|
}
|
|
368
378
|
|
|
369
379
|
void end_transaction() {
|
|
@@ -375,11 +385,16 @@ public:
|
|
|
375
385
|
it.value().isNew = false;
|
|
376
386
|
++it;
|
|
377
387
|
} else {
|
|
378
|
-
|
|
388
|
+
if (transactionShouldRemoveOldItems) {
|
|
389
|
+
it = cache.erase(it);
|
|
390
|
+
} else {
|
|
391
|
+
++it;
|
|
392
|
+
}
|
|
379
393
|
}
|
|
380
394
|
}
|
|
381
395
|
increaseIteratorVersion();
|
|
382
396
|
transactionOpened = false;
|
|
397
|
+
transactionShouldRemoveOldItems = true;
|
|
383
398
|
}
|
|
384
399
|
|
|
385
400
|
size_t load_page(const str &json_text) {
|
|
@@ -408,11 +423,12 @@ public:
|
|
|
408
423
|
}
|
|
409
424
|
|
|
410
425
|
tsl::sparse_map<str, MarkedItem> cache;
|
|
411
|
-
absl::flat_hash_map<str, uint8_t, absl::Hash<str
|
|
426
|
+
absl::flat_hash_map<str, uint8_t, absl::Hash<str>> attrMap;
|
|
412
427
|
strVec attrIdx;
|
|
413
428
|
const uint8_t numberOfAttributes;
|
|
414
429
|
size_t oldCacheSize = 0;
|
|
415
430
|
bool transactionOpened = false;
|
|
431
|
+
bool transactionShouldRemoveOldItems = true;
|
|
416
432
|
uint64_t iterator_version = 1; // for get_all()
|
|
417
433
|
};
|
|
418
434
|
|
|
@@ -421,11 +437,13 @@ NB_MODULE(_small_cache_impl, m) {
|
|
|
421
437
|
nb::class_<SmallCache> cache(m, "SmallCache");
|
|
422
438
|
cache
|
|
423
439
|
.def(nb::init<std::vector<std::string> >(), nb::arg("attribute_names"))
|
|
424
|
-
.def("begin_transaction", &SmallCache::begin_transaction,
|
|
440
|
+
.def("begin_transaction", &SmallCache::begin_transaction,
|
|
441
|
+
nb::arg("estimated_number_of_items") = 0,
|
|
442
|
+
nb::arg("remove_old_items") = true)
|
|
425
443
|
.def("end_transaction", &SmallCache::end_transaction)
|
|
426
444
|
.def("add", &SmallCache::add_item, nb::arg("item_id"), nb::arg("attributes"))
|
|
427
|
-
.def("
|
|
428
|
-
.def("
|
|
445
|
+
.def("get_one", &SmallCache::get_one, nb::arg("id"), nb::arg("attributes"))
|
|
446
|
+
.def("get_many", &SmallCache::get_many, nb::arg("ids"), nb::arg("attributes"), nb::arg("per_iteration") = 10000,
|
|
429
447
|
nb::arg("iterator_version") = 0,
|
|
430
448
|
nb::arg("iterator") = 0)
|
|
431
449
|
.def("load_page", &SmallCache::load_page, nb::arg("json_text"));
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|