lqft-python-engine 1.0.5__tar.gz → 1.0.6__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.
- {lqft_python_engine-1.0.5 → lqft_python_engine-1.0.6}/PKG-INFO +1 -1
- {lqft_python_engine-1.0.5 → lqft_python_engine-1.0.6}/lqft_engine.c +134 -39
- {lqft_python_engine-1.0.5 → lqft_python_engine-1.0.6}/lqft_engine.py +46 -11
- {lqft_python_engine-1.0.5 → lqft_python_engine-1.0.6}/lqft_python_engine.egg-info/PKG-INFO +1 -1
- {lqft_python_engine-1.0.5 → lqft_python_engine-1.0.6}/setup.py +2 -2
- {lqft_python_engine-1.0.5 → lqft_python_engine-1.0.6}/LICENSE.md +0 -0
- {lqft_python_engine-1.0.5 → lqft_python_engine-1.0.6}/lqft_python_engine.egg-info/SOURCES.txt +0 -0
- {lqft_python_engine-1.0.5 → lqft_python_engine-1.0.6}/lqft_python_engine.egg-info/dependency_links.txt +0 -0
- {lqft_python_engine-1.0.5 → lqft_python_engine-1.0.6}/lqft_python_engine.egg-info/requires.txt +0 -0
- {lqft_python_engine-1.0.5 → lqft_python_engine-1.0.6}/lqft_python_engine.egg-info/top_level.txt +0 -0
- {lqft_python_engine-1.0.5 → lqft_python_engine-1.0.6}/pyproject.toml +0 -0
- {lqft_python_engine-1.0.5 → lqft_python_engine-1.0.6}/setup.cfg +0 -0
|
@@ -134,7 +134,7 @@ static inline void fast_unlock(volatile long* lk) {
|
|
|
134
134
|
// GLOBAL METRIC SHARDING (F-02)
|
|
135
135
|
// ===================================================================
|
|
136
136
|
|
|
137
|
-
#define MAX_TRACKED_THREADS
|
|
137
|
+
#define MAX_TRACKED_THREADS 4096
|
|
138
138
|
|
|
139
139
|
typedef struct {
|
|
140
140
|
int64_t phys_added;
|
|
@@ -146,6 +146,8 @@ typedef struct {
|
|
|
146
146
|
static ALIGN_64 ThreadMetrics global_metrics_array[MAX_TRACKED_THREADS];
|
|
147
147
|
static volatile long registered_threads_count = 0;
|
|
148
148
|
static THREAD_LOCAL ThreadMetrics* my_metrics = NULL;
|
|
149
|
+
static volatile long global_arena_epoch = 1;
|
|
150
|
+
static THREAD_LOCAL long local_arena_epoch = 0;
|
|
149
151
|
|
|
150
152
|
static inline ThreadMetrics* get_my_metrics() {
|
|
151
153
|
if (my_metrics == NULL) {
|
|
@@ -157,7 +159,8 @@ static inline ThreadMetrics* get_my_metrics() {
|
|
|
157
159
|
if (idx < MAX_TRACKED_THREADS) {
|
|
158
160
|
my_metrics = &global_metrics_array[idx];
|
|
159
161
|
} else {
|
|
160
|
-
|
|
162
|
+
// Overflow bucket to avoid distorting thread-0 metrics.
|
|
163
|
+
my_metrics = &global_metrics_array[MAX_TRACKED_THREADS - 1];
|
|
161
164
|
}
|
|
162
165
|
}
|
|
163
166
|
return my_metrics;
|
|
@@ -278,6 +281,66 @@ static THREAD_LOCAL LQFTNode*** local_ret_arr_head = NULL;
|
|
|
278
281
|
static THREAD_LOCAL LQFTNode*** local_ret_arr_tail = NULL;
|
|
279
282
|
static THREAD_LOCAL int local_ret_arr_count = 0;
|
|
280
283
|
|
|
284
|
+
static inline void reset_tls_state_if_needed(void) {
|
|
285
|
+
long ge = global_arena_epoch;
|
|
286
|
+
if (local_arena_epoch == ge) return;
|
|
287
|
+
|
|
288
|
+
// A global purge/free_all occurred. Drop stale per-thread pointers.
|
|
289
|
+
local_arena.current_node_chunk = NULL;
|
|
290
|
+
local_arena.node_chunk_idx = ARENA_CHUNK_SIZE;
|
|
291
|
+
local_arena.node_free_list = NULL;
|
|
292
|
+
local_arena.current_child_chunk = NULL;
|
|
293
|
+
local_arena.child_chunk_idx = ARENA_CHUNK_SIZE;
|
|
294
|
+
local_arena.array_free_list = NULL;
|
|
295
|
+
|
|
296
|
+
local_ret_node_head = NULL;
|
|
297
|
+
local_ret_node_tail = NULL;
|
|
298
|
+
local_ret_node_count = 0;
|
|
299
|
+
local_ret_arr_head = NULL;
|
|
300
|
+
local_ret_arr_tail = NULL;
|
|
301
|
+
local_ret_arr_count = 0;
|
|
302
|
+
|
|
303
|
+
local_arena_epoch = ge;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
static inline NodeChunk* pop_pre_zeroed_node_chunk(void) {
|
|
307
|
+
NodeChunk* head;
|
|
308
|
+
for (;;) {
|
|
309
|
+
head = pre_zeroed_node_chunks;
|
|
310
|
+
if (!head) return NULL;
|
|
311
|
+
#ifdef _MSC_VER
|
|
312
|
+
if (_InterlockedCompareExchangePointer((void* volatile*)&pre_zeroed_node_chunks, (void*)head->next_global, (void*)head) == (void*)head) {
|
|
313
|
+
_InterlockedDecrement(&pre_node_count);
|
|
314
|
+
return head;
|
|
315
|
+
}
|
|
316
|
+
#else
|
|
317
|
+
if (__sync_bool_compare_and_swap(&pre_zeroed_node_chunks, head, head->next_global)) {
|
|
318
|
+
__sync_fetch_and_sub(&pre_node_count, 1);
|
|
319
|
+
return head;
|
|
320
|
+
}
|
|
321
|
+
#endif
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
static inline ChildChunk* pop_pre_zeroed_child_chunk(void) {
|
|
326
|
+
ChildChunk* head;
|
|
327
|
+
for (;;) {
|
|
328
|
+
head = pre_zeroed_child_chunks;
|
|
329
|
+
if (!head) return NULL;
|
|
330
|
+
#ifdef _MSC_VER
|
|
331
|
+
if (_InterlockedCompareExchangePointer((void* volatile*)&pre_zeroed_child_chunks, (void*)head->next_global, (void*)head) == (void*)head) {
|
|
332
|
+
_InterlockedDecrement(&pre_child_count);
|
|
333
|
+
return head;
|
|
334
|
+
}
|
|
335
|
+
#else
|
|
336
|
+
if (__sync_bool_compare_and_swap(&pre_zeroed_child_chunks, head, head->next_global)) {
|
|
337
|
+
__sync_fetch_and_sub(&pre_child_count, 1);
|
|
338
|
+
return head;
|
|
339
|
+
}
|
|
340
|
+
#endif
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
281
344
|
static LQFTNode** registry = NULL;
|
|
282
345
|
|
|
283
346
|
typedef struct {
|
|
@@ -382,6 +445,8 @@ void* background_alloc_thread(void* arg) {
|
|
|
382
445
|
}
|
|
383
446
|
|
|
384
447
|
LQFTNode* create_node(void* value, uint64_t key_hash, LQFTNode** children_src, uint64_t full_hash) {
|
|
448
|
+
reset_tls_state_if_needed();
|
|
449
|
+
|
|
385
450
|
LQFTNode* node = NULL;
|
|
386
451
|
if (!local_arena.node_free_list) {
|
|
387
452
|
#ifdef _MSC_VER
|
|
@@ -405,20 +470,7 @@ LQFTNode* create_node(void* value, uint64_t key_hash, LQFTNode** children_src, u
|
|
|
405
470
|
local_arena.node_free_list = (LQFTNode*)node->children;
|
|
406
471
|
} else {
|
|
407
472
|
if (local_arena.node_chunk_idx >= ARENA_CHUNK_SIZE) {
|
|
408
|
-
NodeChunk* new_chunk =
|
|
409
|
-
#ifdef _MSC_VER
|
|
410
|
-
do {
|
|
411
|
-
new_chunk = pre_zeroed_node_chunks;
|
|
412
|
-
if (!new_chunk) break;
|
|
413
|
-
} while (_InterlockedCompareExchangePointer((void* volatile*)&pre_zeroed_node_chunks, (void*)new_chunk->next_global, (void*)new_chunk) != (void*)new_chunk);
|
|
414
|
-
if (new_chunk) _InterlockedDecrement(&pre_node_count);
|
|
415
|
-
#else
|
|
416
|
-
do {
|
|
417
|
-
new_chunk = pre_zeroed_node_chunks;
|
|
418
|
-
if (!new_chunk) break;
|
|
419
|
-
} while (!__sync_bool_compare_and_swap(&pre_zeroed_node_chunks, new_chunk, new_chunk->next_global));
|
|
420
|
-
if (new_chunk) __sync_fetch_and_sub(&pre_node_count, 1);
|
|
421
|
-
#endif
|
|
473
|
+
NodeChunk* new_chunk = pop_pre_zeroed_node_chunk();
|
|
422
474
|
if (!new_chunk) {
|
|
423
475
|
new_chunk = alloc_node_chunk();
|
|
424
476
|
}
|
|
@@ -461,20 +513,7 @@ LQFTNode* create_node(void* value, uint64_t key_hash, LQFTNode** children_src, u
|
|
|
461
513
|
local_arena.array_free_list = (LQFTNode***)arr[0];
|
|
462
514
|
} else {
|
|
463
515
|
if (local_arena.child_chunk_idx >= ARENA_CHUNK_SIZE) {
|
|
464
|
-
ChildChunk* new_chunk =
|
|
465
|
-
#ifdef _MSC_VER
|
|
466
|
-
do {
|
|
467
|
-
new_chunk = pre_zeroed_child_chunks;
|
|
468
|
-
if (!new_chunk) break;
|
|
469
|
-
} while (_InterlockedCompareExchangePointer((void* volatile*)&pre_zeroed_child_chunks, (void*)new_chunk->next_global, (void*)new_chunk) != (void*)new_chunk);
|
|
470
|
-
if (new_chunk) _InterlockedDecrement(&pre_child_count);
|
|
471
|
-
#else
|
|
472
|
-
do {
|
|
473
|
-
new_chunk = pre_zeroed_child_chunks;
|
|
474
|
-
if (!new_chunk) break;
|
|
475
|
-
} while (!__sync_bool_compare_and_swap(&pre_zeroed_child_chunks, new_chunk, new_chunk->next_global));
|
|
476
|
-
if (new_chunk) __sync_fetch_and_sub(&pre_child_count, 1);
|
|
477
|
-
#endif
|
|
516
|
+
ChildChunk* new_chunk = pop_pre_zeroed_child_chunk();
|
|
478
517
|
if (!new_chunk) {
|
|
479
518
|
new_chunk = alloc_child_chunk();
|
|
480
519
|
}
|
|
@@ -498,8 +537,10 @@ LQFTNode* create_node(void* value, uint64_t key_hash, LQFTNode** children_src, u
|
|
|
498
537
|
|
|
499
538
|
void decref(LQFTNode* start_node) {
|
|
500
539
|
if (!start_node || start_node == TOMBSTONE) return;
|
|
501
|
-
|
|
540
|
+
int cap = 1024;
|
|
502
541
|
int top = 0;
|
|
542
|
+
LQFTNode** cleanup_stack = (LQFTNode**)malloc((size_t)cap * sizeof(LQFTNode*));
|
|
543
|
+
if (!cleanup_stack) return;
|
|
503
544
|
cleanup_stack[top++] = start_node;
|
|
504
545
|
while (top > 0) {
|
|
505
546
|
LQFTNode* node = cleanup_stack[--top];
|
|
@@ -512,7 +553,19 @@ void decref(LQFTNode* start_node) {
|
|
|
512
553
|
fast_unlock(&stripe_locks[stripe].flag);
|
|
513
554
|
if (node->children) {
|
|
514
555
|
for (int i = 0; i < 32; i++) {
|
|
515
|
-
if (node->children[i])
|
|
556
|
+
if (node->children[i]) {
|
|
557
|
+
if (top >= cap) {
|
|
558
|
+
int next_cap = cap * 2;
|
|
559
|
+
LQFTNode** grown = (LQFTNode**)realloc(cleanup_stack, (size_t)next_cap * sizeof(LQFTNode*));
|
|
560
|
+
if (!grown) {
|
|
561
|
+
free(cleanup_stack);
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
cleanup_stack = grown;
|
|
565
|
+
cap = next_cap;
|
|
566
|
+
}
|
|
567
|
+
cleanup_stack[top++] = node->children[i];
|
|
568
|
+
}
|
|
516
569
|
}
|
|
517
570
|
LQFTNode*** arr = (LQFTNode***)node->children;
|
|
518
571
|
arr[0] = (LQFTNode**)local_ret_arr_head;
|
|
@@ -564,6 +617,23 @@ void decref(LQFTNode* start_node) {
|
|
|
564
617
|
get_my_metrics()->phys_freed++;
|
|
565
618
|
}
|
|
566
619
|
}
|
|
620
|
+
free(cleanup_stack);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
static inline int node_matches_signature(const LQFTNode* node, const char* value_ptr, uint64_t key_hash, LQFTNode** children) {
|
|
624
|
+
if (!node) return 0;
|
|
625
|
+
if (node->key_hash != key_hash) return 0;
|
|
626
|
+
|
|
627
|
+
if ((node->value == NULL) != (value_ptr == NULL)) return 0;
|
|
628
|
+
if (node->value && value_ptr && strcmp((const char*)node->value, value_ptr) != 0) return 0;
|
|
629
|
+
|
|
630
|
+
if ((node->children == NULL) != (children == NULL)) return 0;
|
|
631
|
+
if (node->children && children) {
|
|
632
|
+
for (int i = 0; i < 32; i++) {
|
|
633
|
+
if (node->children[i] != children[i]) return 0;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
return 1;
|
|
567
637
|
}
|
|
568
638
|
|
|
569
639
|
LQFTNode* get_canonical_v2(const char* value_ptr, uint64_t key_hash, LQFTNode** children, uint64_t full_hash) {
|
|
@@ -571,20 +641,28 @@ LQFTNode* get_canonical_v2(const char* value_ptr, uint64_t key_hash, LQFTNode**
|
|
|
571
641
|
uint32_t local_idx = (uint32_t)((full_hash ^ (full_hash >> 32)) & STRIPE_MASK);
|
|
572
642
|
uint32_t global_idx = (stripe * STRIPE_SIZE) + local_idx;
|
|
573
643
|
uint32_t start_idx = local_idx;
|
|
644
|
+
|
|
645
|
+
// Minimal stability hardening: protect canonical-registry probing with stripe lock
|
|
646
|
+
// to avoid racing against concurrent tombstoning/reclamation.
|
|
647
|
+
fast_lock_backoff(&stripe_locks[stripe].flag);
|
|
574
648
|
for (;;) {
|
|
575
649
|
LQFTNode* slot = registry[global_idx];
|
|
576
650
|
if (slot == NULL) break;
|
|
577
|
-
if (slot != TOMBSTONE && slot->full_hash_val == full_hash) {
|
|
578
|
-
ATOMIC_INC(&slot->ref_count);
|
|
651
|
+
if (slot != TOMBSTONE && slot->full_hash_val == full_hash && node_matches_signature(slot, value_ptr, key_hash, children)) {
|
|
652
|
+
ATOMIC_INC(&slot->ref_count);
|
|
653
|
+
fast_unlock(&stripe_locks[stripe].flag);
|
|
579
654
|
return slot;
|
|
580
655
|
}
|
|
581
656
|
local_idx = (local_idx + 1) & STRIPE_MASK;
|
|
582
657
|
global_idx = (stripe * STRIPE_SIZE) + local_idx;
|
|
583
|
-
if (local_idx == start_idx) break;
|
|
658
|
+
if (local_idx == start_idx) break;
|
|
584
659
|
}
|
|
660
|
+
fast_unlock(&stripe_locks[stripe].flag);
|
|
661
|
+
|
|
585
662
|
LQFTNode* new_node = create_node(value_ptr ? (void*)portable_strdup(value_ptr) : NULL, key_hash, children, full_hash);
|
|
586
663
|
if (!new_node) return NULL;
|
|
587
|
-
new_node->ref_count = 1;
|
|
664
|
+
new_node->ref_count = 1;
|
|
665
|
+
|
|
588
666
|
fast_lock_backoff(&stripe_locks[stripe].flag);
|
|
589
667
|
local_idx = (uint32_t)((full_hash ^ (full_hash >> 32)) & STRIPE_MASK);
|
|
590
668
|
global_idx = (stripe * STRIPE_SIZE) + local_idx;
|
|
@@ -594,7 +672,7 @@ LQFTNode* get_canonical_v2(const char* value_ptr, uint64_t key_hash, LQFTNode**
|
|
|
594
672
|
LQFTNode* slot = registry[global_idx];
|
|
595
673
|
if (slot == NULL) break;
|
|
596
674
|
if (slot == TOMBSTONE) { if (first_tombstone == -1) first_tombstone = (int)local_idx; }
|
|
597
|
-
else if (slot->full_hash_val == full_hash) {
|
|
675
|
+
else if (slot->full_hash_val == full_hash && node_matches_signature(slot, value_ptr, key_hash, children)) {
|
|
598
676
|
ATOMIC_INC(&slot->ref_count);
|
|
599
677
|
fast_unlock(&stripe_locks[stripe].flag);
|
|
600
678
|
if (new_node->value) free(new_node->value);
|
|
@@ -745,8 +823,18 @@ char* core_search(uint64_t h, LQFTNode* root) {
|
|
|
745
823
|
}
|
|
746
824
|
|
|
747
825
|
static void c_internal_insert_rw(uint64_t h, const char* val_str) {
|
|
748
|
-
|
|
749
|
-
|
|
826
|
+
// Small TLS cache avoids repeated value hashing for hot constants (e.g. "x", "active").
|
|
827
|
+
static THREAD_LOCAL const char* last_val_ptr = NULL;
|
|
828
|
+
static THREAD_LOCAL uint64_t last_pre = 0;
|
|
829
|
+
uint64_t pre;
|
|
830
|
+
if (val_str == last_val_ptr) {
|
|
831
|
+
pre = last_pre;
|
|
832
|
+
} else {
|
|
833
|
+
pre = fnv1a_update(FNV_OFFSET_BASIS, "leaf:", 5);
|
|
834
|
+
pre = fnv1a_update(pre, val_str, strlen(val_str));
|
|
835
|
+
last_val_ptr = val_str;
|
|
836
|
+
last_pre = pre;
|
|
837
|
+
}
|
|
750
838
|
uint32_t shard = (uint32_t)((h >> 48) & ROOT_MASK);
|
|
751
839
|
get_my_metrics()->logical_inserts++;
|
|
752
840
|
int spin = 0;
|
|
@@ -976,6 +1064,13 @@ static PyObject* method_free_all(PyObject* self, PyObject* args) {
|
|
|
976
1064
|
for (int i = 0; i < MAX_TRACKED_THREADS; i++) {
|
|
977
1065
|
global_metrics_array[i].phys_added = 0; global_metrics_array[i].phys_freed = 0; global_metrics_array[i].logical_inserts = 0;
|
|
978
1066
|
}
|
|
1067
|
+
#ifdef _MSC_VER
|
|
1068
|
+
_InterlockedExchange(®istered_threads_count, 0);
|
|
1069
|
+
_InterlockedIncrement(&global_arena_epoch);
|
|
1070
|
+
#else
|
|
1071
|
+
__sync_lock_test_and_set(®istered_threads_count, 0);
|
|
1072
|
+
__sync_add_and_fetch(&global_arena_epoch, 1);
|
|
1073
|
+
#endif
|
|
979
1074
|
for(int i = NUM_STRIPES - 1; i >= 0; i--) fast_unlock(&stripe_locks[i].flag);
|
|
980
1075
|
for(int i = NUM_ROOTS - 1; i >= 0; i--) {
|
|
981
1076
|
global_roots[i].root = NULL;
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import hashlib
|
|
2
|
-
import psutil
|
|
3
1
|
import os
|
|
4
2
|
import sys
|
|
3
|
+
import hashlib
|
|
4
|
+
import threading
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
import psutil
|
|
8
|
+
except Exception:
|
|
9
|
+
psutil = None
|
|
5
10
|
|
|
6
11
|
# ---------------------------------------------------------
|
|
7
12
|
# STRICT NATIVE ENTERPRISE WRAPPER (v1.0.5)
|
|
@@ -18,14 +23,21 @@ except ImportError:
|
|
|
18
23
|
sys.exit(1)
|
|
19
24
|
|
|
20
25
|
class LQFT:
|
|
26
|
+
_instance_lock = threading.Lock()
|
|
27
|
+
_live_instances = 0
|
|
28
|
+
|
|
21
29
|
# F-03 & F-04: Restored migration_threshold to sync API signatures across the suite
|
|
22
30
|
def __init__(self, migration_threshold=50000):
|
|
23
31
|
self.is_native = True
|
|
24
|
-
|
|
25
|
-
self.
|
|
32
|
+
# Keep destructive purge opt-in; global C-engine state can be shared by multiple wrappers.
|
|
33
|
+
self.auto_purge_enabled = False
|
|
34
|
+
self.max_memory_mb = 1000.0
|
|
26
35
|
self.total_ops = 0
|
|
27
|
-
self.migration_threshold = migration_threshold
|
|
28
|
-
self._process = psutil.Process(os.getpid())
|
|
36
|
+
self.migration_threshold = migration_threshold
|
|
37
|
+
self._process = psutil.Process(os.getpid()) if psutil else None
|
|
38
|
+
self._closed = False
|
|
39
|
+
with LQFT._instance_lock:
|
|
40
|
+
LQFT._live_instances += 1
|
|
29
41
|
|
|
30
42
|
def _validate_type(self, key, value=None):
|
|
31
43
|
if not isinstance(key, str):
|
|
@@ -34,13 +46,30 @@ class LQFT:
|
|
|
34
46
|
raise TypeError(f"LQFT values must be strings. Received: {type(value).__name__}")
|
|
35
47
|
|
|
36
48
|
def _get_64bit_hash(self, key):
|
|
37
|
-
|
|
49
|
+
# Deterministic 64-bit hash keeps key mapping stable across processes/runs.
|
|
50
|
+
return int.from_bytes(hashlib.blake2b(key.encode(), digest_size=8).digest(), "little")
|
|
51
|
+
|
|
52
|
+
def _current_memory_mb(self):
|
|
53
|
+
if self._process is None:
|
|
54
|
+
return 0.0
|
|
55
|
+
try:
|
|
56
|
+
return self._process.memory_info().rss / (1024 * 1024)
|
|
57
|
+
except Exception:
|
|
58
|
+
return 0.0
|
|
38
59
|
|
|
39
60
|
def set_auto_purge_threshold(self, threshold: float):
|
|
40
61
|
self.max_memory_mb = threshold
|
|
41
62
|
|
|
42
63
|
def purge(self):
|
|
43
|
-
current_mb = self.
|
|
64
|
+
current_mb = self._current_memory_mb()
|
|
65
|
+
with LQFT._instance_lock:
|
|
66
|
+
live = LQFT._live_instances
|
|
67
|
+
if live > 1:
|
|
68
|
+
print(
|
|
69
|
+
f"\n[⚠️ CIRCUIT Breaker] Memory {current_mb:.1f} MB but purge skipped "
|
|
70
|
+
f"because {live} LQFT instances are active (shared global engine state)."
|
|
71
|
+
)
|
|
72
|
+
return
|
|
44
73
|
print(f"\n[⚠️ CIRCUIT Breaker] Engine exceeded limit (Currently {current_mb:.1f} MB). Auto-Purging!")
|
|
45
74
|
self.clear()
|
|
46
75
|
|
|
@@ -55,6 +84,7 @@ class LQFT:
|
|
|
55
84
|
return stats.get('logical_inserts', 0)
|
|
56
85
|
|
|
57
86
|
def clear(self):
|
|
87
|
+
# Global clear (shared native state). Keep explicit to avoid accidental data loss.
|
|
58
88
|
return lqft_c_engine.free_all()
|
|
59
89
|
|
|
60
90
|
def insert(self, key, value):
|
|
@@ -63,7 +93,7 @@ class LQFT:
|
|
|
63
93
|
|
|
64
94
|
# Heuristic Circuit Breaker check
|
|
65
95
|
if self.auto_purge_enabled and self.total_ops % 5000 == 0:
|
|
66
|
-
current_mb = self.
|
|
96
|
+
current_mb = self._current_memory_mb()
|
|
67
97
|
if current_mb >= self.max_memory_mb:
|
|
68
98
|
self.purge()
|
|
69
99
|
|
|
@@ -97,8 +127,13 @@ class LQFT:
|
|
|
97
127
|
self.delete(key)
|
|
98
128
|
|
|
99
129
|
def __del__(self):
|
|
100
|
-
try:
|
|
101
|
-
|
|
130
|
+
try:
|
|
131
|
+
if not self._closed:
|
|
132
|
+
with LQFT._instance_lock:
|
|
133
|
+
LQFT._live_instances = max(0, LQFT._live_instances - 1)
|
|
134
|
+
self._closed = True
|
|
135
|
+
except Exception:
|
|
136
|
+
pass
|
|
102
137
|
|
|
103
138
|
def status(self):
|
|
104
139
|
return {
|
|
@@ -3,7 +3,7 @@ import os
|
|
|
3
3
|
import sys
|
|
4
4
|
|
|
5
5
|
# ---------------------------------------------------------
|
|
6
|
-
# LQFT BUILD SYSTEM - V1.0.
|
|
6
|
+
# LQFT BUILD SYSTEM - V1.0.6 (Apple Silicon M3 Hotfix)
|
|
7
7
|
# Architect: Parjad Minooei
|
|
8
8
|
# ---------------------------------------------------------
|
|
9
9
|
|
|
@@ -40,7 +40,7 @@ lqft_extension = Extension(
|
|
|
40
40
|
|
|
41
41
|
setup(
|
|
42
42
|
name="lqft-python-engine",
|
|
43
|
-
version="1.0.
|
|
43
|
+
version="1.0.6",
|
|
44
44
|
description="LQFT Engine: 14.3M Ops/sec O(1) Time | O(Σ) Space Data Structure",
|
|
45
45
|
long_description=long_description,
|
|
46
46
|
long_description_content_type="text/markdown",
|
|
File without changes
|
{lqft_python_engine-1.0.5 → lqft_python_engine-1.0.6}/lqft_python_engine.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{lqft_python_engine-1.0.5 → lqft_python_engine-1.0.6}/lqft_python_engine.egg-info/requires.txt
RENAMED
|
File without changes
|
{lqft_python_engine-1.0.5 → lqft_python_engine-1.0.6}/lqft_python_engine.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|