exonware-xwnode 0.0.1.12__py3-none-any.whl
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.
- exonware/__init__.py +14 -0
- exonware/xwnode/__init__.py +127 -0
- exonware/xwnode/base.py +676 -0
- exonware/xwnode/config.py +178 -0
- exonware/xwnode/contracts.py +730 -0
- exonware/xwnode/errors.py +503 -0
- exonware/xwnode/facade.py +460 -0
- exonware/xwnode/strategies/__init__.py +158 -0
- exonware/xwnode/strategies/advisor.py +463 -0
- exonware/xwnode/strategies/edges/__init__.py +32 -0
- exonware/xwnode/strategies/edges/adj_list.py +227 -0
- exonware/xwnode/strategies/edges/adj_matrix.py +391 -0
- exonware/xwnode/strategies/edges/base.py +169 -0
- exonware/xwnode/strategies/flyweight.py +328 -0
- exonware/xwnode/strategies/impls/__init__.py +13 -0
- exonware/xwnode/strategies/impls/_base_edge.py +403 -0
- exonware/xwnode/strategies/impls/_base_node.py +307 -0
- exonware/xwnode/strategies/impls/edge_adj_list.py +353 -0
- exonware/xwnode/strategies/impls/edge_adj_matrix.py +445 -0
- exonware/xwnode/strategies/impls/edge_bidir_wrapper.py +455 -0
- exonware/xwnode/strategies/impls/edge_block_adj_matrix.py +539 -0
- exonware/xwnode/strategies/impls/edge_coo.py +533 -0
- exonware/xwnode/strategies/impls/edge_csc.py +447 -0
- exonware/xwnode/strategies/impls/edge_csr.py +492 -0
- exonware/xwnode/strategies/impls/edge_dynamic_adj_list.py +503 -0
- exonware/xwnode/strategies/impls/edge_flow_network.py +555 -0
- exonware/xwnode/strategies/impls/edge_hyperedge_set.py +516 -0
- exonware/xwnode/strategies/impls/edge_neural_graph.py +650 -0
- exonware/xwnode/strategies/impls/edge_octree.py +574 -0
- exonware/xwnode/strategies/impls/edge_property_store.py +655 -0
- exonware/xwnode/strategies/impls/edge_quadtree.py +519 -0
- exonware/xwnode/strategies/impls/edge_rtree.py +820 -0
- exonware/xwnode/strategies/impls/edge_temporal_edgeset.py +558 -0
- exonware/xwnode/strategies/impls/edge_tree_graph_basic.py +271 -0
- exonware/xwnode/strategies/impls/edge_weighted_graph.py +411 -0
- exonware/xwnode/strategies/manager.py +775 -0
- exonware/xwnode/strategies/metrics.py +538 -0
- exonware/xwnode/strategies/migration.py +432 -0
- exonware/xwnode/strategies/nodes/__init__.py +50 -0
- exonware/xwnode/strategies/nodes/_base_node.py +307 -0
- exonware/xwnode/strategies/nodes/adjacency_list.py +267 -0
- exonware/xwnode/strategies/nodes/aho_corasick.py +345 -0
- exonware/xwnode/strategies/nodes/array_list.py +209 -0
- exonware/xwnode/strategies/nodes/base.py +247 -0
- exonware/xwnode/strategies/nodes/deque.py +200 -0
- exonware/xwnode/strategies/nodes/hash_map.py +135 -0
- exonware/xwnode/strategies/nodes/heap.py +307 -0
- exonware/xwnode/strategies/nodes/linked_list.py +232 -0
- exonware/xwnode/strategies/nodes/node_aho_corasick.py +520 -0
- exonware/xwnode/strategies/nodes/node_array_list.py +175 -0
- exonware/xwnode/strategies/nodes/node_avl_tree.py +371 -0
- exonware/xwnode/strategies/nodes/node_b_plus_tree.py +542 -0
- exonware/xwnode/strategies/nodes/node_bitmap.py +420 -0
- exonware/xwnode/strategies/nodes/node_bitset_dynamic.py +513 -0
- exonware/xwnode/strategies/nodes/node_bloom_filter.py +347 -0
- exonware/xwnode/strategies/nodes/node_btree.py +357 -0
- exonware/xwnode/strategies/nodes/node_count_min_sketch.py +470 -0
- exonware/xwnode/strategies/nodes/node_cow_tree.py +473 -0
- exonware/xwnode/strategies/nodes/node_cuckoo_hash.py +392 -0
- exonware/xwnode/strategies/nodes/node_fenwick_tree.py +301 -0
- exonware/xwnode/strategies/nodes/node_hash_map.py +269 -0
- exonware/xwnode/strategies/nodes/node_heap.py +191 -0
- exonware/xwnode/strategies/nodes/node_hyperloglog.py +407 -0
- exonware/xwnode/strategies/nodes/node_linked_list.py +409 -0
- exonware/xwnode/strategies/nodes/node_lsm_tree.py +400 -0
- exonware/xwnode/strategies/nodes/node_ordered_map.py +390 -0
- exonware/xwnode/strategies/nodes/node_ordered_map_balanced.py +565 -0
- exonware/xwnode/strategies/nodes/node_patricia.py +512 -0
- exonware/xwnode/strategies/nodes/node_persistent_tree.py +378 -0
- exonware/xwnode/strategies/nodes/node_radix_trie.py +452 -0
- exonware/xwnode/strategies/nodes/node_red_black_tree.py +497 -0
- exonware/xwnode/strategies/nodes/node_roaring_bitmap.py +570 -0
- exonware/xwnode/strategies/nodes/node_segment_tree.py +289 -0
- exonware/xwnode/strategies/nodes/node_set_hash.py +354 -0
- exonware/xwnode/strategies/nodes/node_set_tree.py +480 -0
- exonware/xwnode/strategies/nodes/node_skip_list.py +316 -0
- exonware/xwnode/strategies/nodes/node_splay_tree.py +393 -0
- exonware/xwnode/strategies/nodes/node_suffix_array.py +487 -0
- exonware/xwnode/strategies/nodes/node_treap.py +387 -0
- exonware/xwnode/strategies/nodes/node_tree_graph_hybrid.py +1434 -0
- exonware/xwnode/strategies/nodes/node_trie.py +252 -0
- exonware/xwnode/strategies/nodes/node_union_find.py +187 -0
- exonware/xwnode/strategies/nodes/node_xdata_optimized.py +369 -0
- exonware/xwnode/strategies/nodes/priority_queue.py +209 -0
- exonware/xwnode/strategies/nodes/queue.py +161 -0
- exonware/xwnode/strategies/nodes/sparse_matrix.py +206 -0
- exonware/xwnode/strategies/nodes/stack.py +152 -0
- exonware/xwnode/strategies/nodes/trie.py +274 -0
- exonware/xwnode/strategies/nodes/union_find.py +283 -0
- exonware/xwnode/strategies/pattern_detector.py +603 -0
- exonware/xwnode/strategies/performance_monitor.py +487 -0
- exonware/xwnode/strategies/queries/__init__.py +24 -0
- exonware/xwnode/strategies/queries/base.py +236 -0
- exonware/xwnode/strategies/queries/cql.py +201 -0
- exonware/xwnode/strategies/queries/cypher.py +181 -0
- exonware/xwnode/strategies/queries/datalog.py +70 -0
- exonware/xwnode/strategies/queries/elastic_dsl.py +70 -0
- exonware/xwnode/strategies/queries/eql.py +70 -0
- exonware/xwnode/strategies/queries/flux.py +70 -0
- exonware/xwnode/strategies/queries/gql.py +70 -0
- exonware/xwnode/strategies/queries/graphql.py +240 -0
- exonware/xwnode/strategies/queries/gremlin.py +181 -0
- exonware/xwnode/strategies/queries/hiveql.py +214 -0
- exonware/xwnode/strategies/queries/hql.py +70 -0
- exonware/xwnode/strategies/queries/jmespath.py +219 -0
- exonware/xwnode/strategies/queries/jq.py +66 -0
- exonware/xwnode/strategies/queries/json_query.py +66 -0
- exonware/xwnode/strategies/queries/jsoniq.py +248 -0
- exonware/xwnode/strategies/queries/kql.py +70 -0
- exonware/xwnode/strategies/queries/linq.py +238 -0
- exonware/xwnode/strategies/queries/logql.py +70 -0
- exonware/xwnode/strategies/queries/mql.py +68 -0
- exonware/xwnode/strategies/queries/n1ql.py +210 -0
- exonware/xwnode/strategies/queries/partiql.py +70 -0
- exonware/xwnode/strategies/queries/pig.py +215 -0
- exonware/xwnode/strategies/queries/promql.py +70 -0
- exonware/xwnode/strategies/queries/sparql.py +220 -0
- exonware/xwnode/strategies/queries/sql.py +275 -0
- exonware/xwnode/strategies/queries/xml_query.py +66 -0
- exonware/xwnode/strategies/queries/xpath.py +223 -0
- exonware/xwnode/strategies/queries/xquery.py +258 -0
- exonware/xwnode/strategies/queries/xwnode_executor.py +332 -0
- exonware/xwnode/strategies/queries/xwquery_strategy.py +424 -0
- exonware/xwnode/strategies/registry.py +604 -0
- exonware/xwnode/strategies/simple.py +273 -0
- exonware/xwnode/strategies/utils.py +532 -0
- exonware/xwnode/types.py +912 -0
- exonware/xwnode/version.py +78 -0
- exonware_xwnode-0.0.1.12.dist-info/METADATA +169 -0
- exonware_xwnode-0.0.1.12.dist-info/RECORD +132 -0
- exonware_xwnode-0.0.1.12.dist-info/WHEEL +4 -0
- exonware_xwnode-0.0.1.12.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,473 @@
|
|
1
|
+
#exonware\xnode\strategies\impls\node_cow_tree.py
|
2
|
+
"""
|
3
|
+
Copy-on-Write Tree Node Strategy Implementation
|
4
|
+
|
5
|
+
This module implements the COW_TREE strategy for copy-on-write trees with
|
6
|
+
atomic snapshots and versioning capabilities.
|
7
|
+
"""
|
8
|
+
|
9
|
+
from typing import Any, Iterator, List, Dict, Optional, Tuple, Set
|
10
|
+
from .base import ANodeTreeStrategy
|
11
|
+
from ...types import NodeMode, NodeTrait
|
12
|
+
|
13
|
+
|
14
|
+
class COWTreeNode:
|
15
|
+
"""Copy-on-write node in the tree."""
|
16
|
+
|
17
|
+
def __init__(self, key: str, value: Any = None, left: Optional['COWTreeNode'] = None,
|
18
|
+
right: Optional['COWTreeNode'] = None, ref_count: int = 1):
|
19
|
+
self.key = key
|
20
|
+
self.value = value
|
21
|
+
self.left = left
|
22
|
+
self.right = right
|
23
|
+
self.ref_count = ref_count
|
24
|
+
self._frozen = False
|
25
|
+
self._hash = None
|
26
|
+
|
27
|
+
def __hash__(self) -> int:
|
28
|
+
"""Cache hash for performance."""
|
29
|
+
if self._hash is None:
|
30
|
+
self._hash = hash((self.key, self.value, id(self.left), id(self.right)))
|
31
|
+
return self._hash
|
32
|
+
|
33
|
+
def __eq__(self, other) -> bool:
|
34
|
+
"""Structural equality."""
|
35
|
+
if not isinstance(other, COWTreeNode):
|
36
|
+
return False
|
37
|
+
return (self.key == other.key and
|
38
|
+
self.value == other.value and
|
39
|
+
self.left is other.left and
|
40
|
+
self.right is other.right)
|
41
|
+
|
42
|
+
def increment_ref(self) -> None:
|
43
|
+
"""Increment reference count."""
|
44
|
+
if not self._frozen:
|
45
|
+
self.ref_count += 1
|
46
|
+
|
47
|
+
def decrement_ref(self) -> bool:
|
48
|
+
"""Decrement reference count, return True if should be deleted."""
|
49
|
+
if not self._frozen:
|
50
|
+
self.ref_count -= 1
|
51
|
+
return self.ref_count <= 0
|
52
|
+
return False
|
53
|
+
|
54
|
+
def freeze(self) -> None:
|
55
|
+
"""Freeze node to prevent modifications."""
|
56
|
+
self._frozen = True
|
57
|
+
|
58
|
+
def is_shared(self) -> bool:
|
59
|
+
"""Check if node is shared (ref_count > 1)."""
|
60
|
+
return self.ref_count > 1
|
61
|
+
|
62
|
+
|
63
|
+
class COWTreeStrategy(ANodeTreeStrategy):
|
64
|
+
"""
|
65
|
+
Copy-on-write tree node strategy with atomic snapshots.
|
66
|
+
|
67
|
+
Provides instant snapshots, atomic updates, and versioning through
|
68
|
+
copy-on-write semantics with reference counting.
|
69
|
+
"""
|
70
|
+
|
71
|
+
def __init__(self, traits: NodeTrait = NodeTrait.NONE, **options):
|
72
|
+
"""Initialize the COW tree strategy."""
|
73
|
+
super().__init__(NodeMode.COW_TREE, traits, **options)
|
74
|
+
|
75
|
+
self.case_sensitive = options.get('case_sensitive', True)
|
76
|
+
self.balanced = options.get('balanced', True) # Use AVL balancing
|
77
|
+
self.auto_snapshot = options.get('auto_snapshot', False)
|
78
|
+
|
79
|
+
# Core COW tree
|
80
|
+
self._root: Optional[COWTreeNode] = None
|
81
|
+
self._size = 0
|
82
|
+
self._version = 0
|
83
|
+
self._snapshots: List['xCOWTreeStrategy'] = []
|
84
|
+
|
85
|
+
# Statistics
|
86
|
+
self._total_copies = 0
|
87
|
+
self._total_shares = 0
|
88
|
+
self._max_height = 0
|
89
|
+
self._snapshot_count = 0
|
90
|
+
|
91
|
+
def get_supported_traits(self) -> NodeTrait:
|
92
|
+
"""Get the traits supported by the COW tree strategy."""
|
93
|
+
return (NodeTrait.PERSISTENT | NodeTrait.ORDERED | NodeTrait.INDEXED)
|
94
|
+
|
95
|
+
def _normalize_key(self, key: str) -> str:
|
96
|
+
"""Normalize key based on case sensitivity."""
|
97
|
+
return key if self.case_sensitive else key.lower()
|
98
|
+
|
99
|
+
def _create_node(self, key: str, value: Any, left: Optional[COWTreeNode] = None,
|
100
|
+
right: Optional[COWTreeNode] = None) -> COWTreeNode:
|
101
|
+
"""Create new node."""
|
102
|
+
return COWTreeNode(key, value, left, right)
|
103
|
+
|
104
|
+
def _copy_node(self, node: COWTreeNode) -> COWTreeNode:
|
105
|
+
"""Copy node with COW semantics."""
|
106
|
+
if not node.is_shared():
|
107
|
+
# Node is not shared, can modify in place
|
108
|
+
return node
|
109
|
+
|
110
|
+
# Node is shared, need to copy
|
111
|
+
self._total_copies += 1
|
112
|
+
new_node = COWTreeNode(node.key, node.value, node.left, node.right)
|
113
|
+
|
114
|
+
# Increment reference counts for shared children
|
115
|
+
if node.left:
|
116
|
+
node.left.increment_ref()
|
117
|
+
if node.right:
|
118
|
+
node.right.increment_ref()
|
119
|
+
|
120
|
+
return new_node
|
121
|
+
|
122
|
+
def _get_height(self, node: Optional[COWTreeNode]) -> int:
|
123
|
+
"""Get height of node."""
|
124
|
+
if not node:
|
125
|
+
return 0
|
126
|
+
|
127
|
+
left_height = self._get_height(node.left)
|
128
|
+
right_height = self._get_height(node.right)
|
129
|
+
return 1 + max(left_height, right_height)
|
130
|
+
|
131
|
+
def _get_balance(self, node: Optional[COWTreeNode]) -> int:
|
132
|
+
"""Get balance factor of node."""
|
133
|
+
if not node:
|
134
|
+
return 0
|
135
|
+
return self._get_height(node.left) - self._get_height(node.right)
|
136
|
+
|
137
|
+
def _rotate_right(self, node: COWTreeNode) -> COWTreeNode:
|
138
|
+
"""Right rotation for AVL balancing."""
|
139
|
+
left = node.left
|
140
|
+
if not left:
|
141
|
+
return node
|
142
|
+
|
143
|
+
# Copy nodes if shared
|
144
|
+
left = self._copy_node(left)
|
145
|
+
node = self._copy_node(node)
|
146
|
+
|
147
|
+
# Perform rotation
|
148
|
+
new_right = self._create_node(node.key, node.value, left.right, node.right)
|
149
|
+
new_root = self._create_node(left.key, left.value, left.left, new_right)
|
150
|
+
|
151
|
+
return new_root
|
152
|
+
|
153
|
+
def _rotate_left(self, node: COWTreeNode) -> COWTreeNode:
|
154
|
+
"""Left rotation for AVL balancing."""
|
155
|
+
right = node.right
|
156
|
+
if not right:
|
157
|
+
return node
|
158
|
+
|
159
|
+
# Copy nodes if shared
|
160
|
+
right = self._copy_node(right)
|
161
|
+
node = self._copy_node(node)
|
162
|
+
|
163
|
+
# Perform rotation
|
164
|
+
new_left = self._create_node(node.key, node.value, node.left, right.left)
|
165
|
+
new_root = self._create_node(right.key, right.value, new_left, right.right)
|
166
|
+
|
167
|
+
return new_root
|
168
|
+
|
169
|
+
def _balance_node(self, node: COWTreeNode) -> COWTreeNode:
|
170
|
+
"""Balance node using AVL rotations."""
|
171
|
+
if not self.balanced:
|
172
|
+
return node
|
173
|
+
|
174
|
+
balance = self._get_balance(node)
|
175
|
+
|
176
|
+
# Left heavy
|
177
|
+
if balance > 1:
|
178
|
+
if self._get_balance(node.left) < 0:
|
179
|
+
# Left-Right case
|
180
|
+
new_left = self._rotate_left(node.left)
|
181
|
+
new_node = self._create_node(node.key, node.value, new_left, node.right)
|
182
|
+
return self._rotate_right(new_node)
|
183
|
+
else:
|
184
|
+
# Left-Left case
|
185
|
+
return self._rotate_right(node)
|
186
|
+
|
187
|
+
# Right heavy
|
188
|
+
if balance < -1:
|
189
|
+
if self._get_balance(node.right) > 0:
|
190
|
+
# Right-Left case
|
191
|
+
new_right = self._rotate_right(node.right)
|
192
|
+
new_node = self._create_node(node.key, node.value, node.left, new_right)
|
193
|
+
return self._rotate_left(new_node)
|
194
|
+
else:
|
195
|
+
# Right-Right case
|
196
|
+
return self._rotate_left(node)
|
197
|
+
|
198
|
+
return node
|
199
|
+
|
200
|
+
def _insert_node(self, node: Optional[COWTreeNode], key: str, value: Any) -> Tuple[Optional[COWTreeNode], bool]:
|
201
|
+
"""Insert node with COW semantics."""
|
202
|
+
if not node:
|
203
|
+
new_node = self._create_node(key, value)
|
204
|
+
return new_node, True
|
205
|
+
|
206
|
+
# Copy node if shared
|
207
|
+
if node.is_shared():
|
208
|
+
node = self._copy_node(node)
|
209
|
+
|
210
|
+
normalized_key = self._normalize_key(key)
|
211
|
+
node_key = self._normalize_key(node.key)
|
212
|
+
|
213
|
+
if normalized_key < node_key:
|
214
|
+
new_left, inserted = self._insert_node(node.left, key, value)
|
215
|
+
if inserted or new_left is not node.left:
|
216
|
+
new_node = self._create_node(node.key, node.value, new_left, node.right)
|
217
|
+
return self._balance_node(new_node), True
|
218
|
+
else:
|
219
|
+
# Share unchanged node
|
220
|
+
self._total_shares += 1
|
221
|
+
return node, False
|
222
|
+
elif normalized_key > node_key:
|
223
|
+
new_right, inserted = self._insert_node(node.right, key, value)
|
224
|
+
if inserted or new_right is not node.right:
|
225
|
+
new_node = self._create_node(node.key, node.value, node.left, new_right)
|
226
|
+
return self._balance_node(new_node), True
|
227
|
+
else:
|
228
|
+
# Share unchanged node
|
229
|
+
self._total_shares += 1
|
230
|
+
return node, False
|
231
|
+
else:
|
232
|
+
# Key exists, update value
|
233
|
+
if node.value == value:
|
234
|
+
# Share unchanged node
|
235
|
+
self._total_shares += 1
|
236
|
+
return node, False
|
237
|
+
else:
|
238
|
+
new_node = self._create_node(node.key, value, node.left, node.right)
|
239
|
+
return new_node, False
|
240
|
+
|
241
|
+
def _find_node(self, node: Optional[COWTreeNode], key: str) -> Optional[COWTreeNode]:
|
242
|
+
"""Find node by key."""
|
243
|
+
if not node:
|
244
|
+
return None
|
245
|
+
|
246
|
+
normalized_key = self._normalize_key(key)
|
247
|
+
node_key = self._normalize_key(node.key)
|
248
|
+
|
249
|
+
if normalized_key < node_key:
|
250
|
+
return self._find_node(node.left, key)
|
251
|
+
elif normalized_key > node_key:
|
252
|
+
return self._find_node(node.right, key)
|
253
|
+
else:
|
254
|
+
return node
|
255
|
+
|
256
|
+
def _delete_node(self, node: Optional[COWTreeNode], key: str) -> Tuple[Optional[COWTreeNode], bool]:
|
257
|
+
"""Delete node with COW semantics."""
|
258
|
+
if not node:
|
259
|
+
return None, False
|
260
|
+
|
261
|
+
# Copy node if shared
|
262
|
+
if node.is_shared():
|
263
|
+
node = self._copy_node(node)
|
264
|
+
|
265
|
+
normalized_key = self._normalize_key(key)
|
266
|
+
node_key = self._normalize_key(node.key)
|
267
|
+
|
268
|
+
if normalized_key < node_key:
|
269
|
+
new_left, deleted = self._delete_node(node.left, key)
|
270
|
+
if deleted or new_left is not node.left:
|
271
|
+
new_node = self._create_node(node.key, node.value, new_left, node.right)
|
272
|
+
return self._balance_node(new_node), True
|
273
|
+
else:
|
274
|
+
# Share unchanged node
|
275
|
+
self._total_shares += 1
|
276
|
+
return node, False
|
277
|
+
elif normalized_key > node_key:
|
278
|
+
new_right, deleted = self._delete_node(node.right, key)
|
279
|
+
if deleted or new_right is not node.right:
|
280
|
+
new_node = self._create_node(node.key, node.value, node.left, new_right)
|
281
|
+
return self._balance_node(new_node), True
|
282
|
+
else:
|
283
|
+
# Share unchanged node
|
284
|
+
self._total_shares += 1
|
285
|
+
return node, False
|
286
|
+
else:
|
287
|
+
# Found node to delete
|
288
|
+
if not node.left:
|
289
|
+
return node.right, True
|
290
|
+
elif not node.right:
|
291
|
+
return node.left, True
|
292
|
+
else:
|
293
|
+
# Node has both children, find successor
|
294
|
+
successor = self._find_min(node.right)
|
295
|
+
new_right, _ = self._delete_node(node.right, successor.key)
|
296
|
+
new_node = self._create_node(successor.key, successor.value, node.left, new_right)
|
297
|
+
return self._balance_node(new_node), True
|
298
|
+
|
299
|
+
def _find_min(self, node: COWTreeNode) -> COWTreeNode:
|
300
|
+
"""Find minimum node in subtree."""
|
301
|
+
while node.left:
|
302
|
+
node = node.left
|
303
|
+
return node
|
304
|
+
|
305
|
+
def _inorder_traversal(self, node: Optional[COWTreeNode]) -> Iterator[Tuple[str, Any]]:
|
306
|
+
"""In-order traversal of tree."""
|
307
|
+
if node:
|
308
|
+
yield from self._inorder_traversal(node.left)
|
309
|
+
yield (node.key, node.value)
|
310
|
+
yield from self._inorder_traversal(node.right)
|
311
|
+
|
312
|
+
def _freeze_tree(self, node: Optional[COWTreeNode]) -> None:
|
313
|
+
"""Freeze entire tree to prevent modifications."""
|
314
|
+
if node:
|
315
|
+
node.freeze()
|
316
|
+
self._freeze_tree(node.left)
|
317
|
+
self._freeze_tree(node.right)
|
318
|
+
|
319
|
+
# ============================================================================
|
320
|
+
# CORE OPERATIONS
|
321
|
+
# ============================================================================
|
322
|
+
|
323
|
+
def put(self, key: Any, value: Any = None) -> None:
|
324
|
+
"""Store a key-value pair."""
|
325
|
+
if not isinstance(key, str):
|
326
|
+
key = str(key)
|
327
|
+
|
328
|
+
new_root, inserted = self._insert_node(self._root, key, value)
|
329
|
+
self._root = new_root
|
330
|
+
|
331
|
+
if inserted:
|
332
|
+
self._size += 1
|
333
|
+
self._version += 1
|
334
|
+
|
335
|
+
self._max_height = max(self._max_height, self._get_height(self._root))
|
336
|
+
|
337
|
+
# Auto-snapshot if enabled
|
338
|
+
if self.auto_snapshot and inserted:
|
339
|
+
self.snapshot()
|
340
|
+
|
341
|
+
def get(self, key: Any, default: Any = None) -> Any:
|
342
|
+
"""Retrieve a value by key."""
|
343
|
+
if not isinstance(key, str):
|
344
|
+
key = str(key)
|
345
|
+
|
346
|
+
node = self._find_node(self._root, key)
|
347
|
+
return node.value if node else default
|
348
|
+
|
349
|
+
def delete(self, key: Any) -> bool:
|
350
|
+
"""Remove a key-value pair."""
|
351
|
+
if not isinstance(key, str):
|
352
|
+
key = str(key)
|
353
|
+
|
354
|
+
new_root, deleted = self._delete_node(self._root, key)
|
355
|
+
self._root = new_root
|
356
|
+
|
357
|
+
if deleted:
|
358
|
+
self._size -= 1
|
359
|
+
self._version += 1
|
360
|
+
|
361
|
+
return deleted
|
362
|
+
|
363
|
+
def has(self, key: Any) -> bool:
|
364
|
+
"""Check if key exists."""
|
365
|
+
if not isinstance(key, str):
|
366
|
+
key = str(key)
|
367
|
+
|
368
|
+
return self._find_node(self._root, key) is not None
|
369
|
+
|
370
|
+
def clear(self) -> None:
|
371
|
+
"""Clear all data."""
|
372
|
+
self._root = None
|
373
|
+
self._size = 0
|
374
|
+
self._version += 1
|
375
|
+
|
376
|
+
def size(self) -> int:
|
377
|
+
"""Get number of key-value pairs."""
|
378
|
+
return self._size
|
379
|
+
|
380
|
+
def is_empty(self) -> bool:
|
381
|
+
"""Check if tree is empty."""
|
382
|
+
return self._root is None
|
383
|
+
|
384
|
+
# ============================================================================
|
385
|
+
# ITERATION
|
386
|
+
# ============================================================================
|
387
|
+
|
388
|
+
def keys(self) -> Iterator[str]:
|
389
|
+
"""Iterate over keys in sorted order."""
|
390
|
+
for key, _ in self._inorder_traversal(self._root):
|
391
|
+
yield key
|
392
|
+
|
393
|
+
def values(self) -> Iterator[Any]:
|
394
|
+
"""Iterate over values in key order."""
|
395
|
+
for _, value in self._inorder_traversal(self._root):
|
396
|
+
yield value
|
397
|
+
|
398
|
+
def items(self) -> Iterator[Tuple[str, Any]]:
|
399
|
+
"""Iterate over key-value pairs in sorted order."""
|
400
|
+
yield from self._inorder_traversal(self._root)
|
401
|
+
|
402
|
+
def __iter__(self) -> Iterator[str]:
|
403
|
+
"""Iterate over keys."""
|
404
|
+
yield from self.keys()
|
405
|
+
|
406
|
+
# ============================================================================
|
407
|
+
# COW TREE SPECIFIC OPERATIONS
|
408
|
+
# ============================================================================
|
409
|
+
|
410
|
+
def snapshot(self) -> 'xCOWTreeStrategy':
|
411
|
+
"""Create an atomic snapshot of the current tree."""
|
412
|
+
snapshot = xCOWTreeStrategy(self.traits, **self.options)
|
413
|
+
snapshot._root = self._root
|
414
|
+
snapshot._size = self._size
|
415
|
+
snapshot._version = self._version
|
416
|
+
|
417
|
+
# Increment reference counts for shared nodes
|
418
|
+
if self._root:
|
419
|
+
self._root.increment_ref()
|
420
|
+
|
421
|
+
# Freeze the snapshot to prevent modifications
|
422
|
+
snapshot._freeze_tree(snapshot._root)
|
423
|
+
|
424
|
+
self._snapshots.append(snapshot)
|
425
|
+
self._snapshot_count += 1
|
426
|
+
|
427
|
+
return snapshot
|
428
|
+
|
429
|
+
def restore_snapshot(self, snapshot: 'xCOWTreeStrategy') -> None:
|
430
|
+
"""Restore from a snapshot."""
|
431
|
+
# Decrement reference counts for current tree
|
432
|
+
if self._root:
|
433
|
+
self._root.decrement_ref()
|
434
|
+
|
435
|
+
# Copy snapshot state
|
436
|
+
self._root = snapshot._root
|
437
|
+
self._size = snapshot._size
|
438
|
+
self._version = snapshot._version
|
439
|
+
|
440
|
+
# Increment reference counts for restored tree
|
441
|
+
if self._root:
|
442
|
+
self._root.increment_ref()
|
443
|
+
|
444
|
+
def get_snapshots(self) -> List['xCOWTreeStrategy']:
|
445
|
+
"""Get list of all snapshots."""
|
446
|
+
return self._snapshots.copy()
|
447
|
+
|
448
|
+
def cleanup_snapshots(self) -> int:
|
449
|
+
"""Clean up old snapshots, return number of snapshots removed."""
|
450
|
+
removed = len(self._snapshots)
|
451
|
+
self._snapshots.clear()
|
452
|
+
self._snapshot_count = 0
|
453
|
+
return removed
|
454
|
+
|
455
|
+
def get_version(self) -> int:
|
456
|
+
"""Get current version number."""
|
457
|
+
return self._version
|
458
|
+
|
459
|
+
def get_stats(self) -> Dict[str, Any]:
|
460
|
+
"""Get performance statistics."""
|
461
|
+
return {
|
462
|
+
'size': self._size,
|
463
|
+
'height': self._get_height(self._root),
|
464
|
+
'max_height': self._max_height,
|
465
|
+
'version': self._version,
|
466
|
+
'snapshot_count': self._snapshot_count,
|
467
|
+
'total_copies': self._total_copies,
|
468
|
+
'total_shares': self._total_shares,
|
469
|
+
'copy_ratio': self._total_copies / max(1, self._total_shares + self._total_copies),
|
470
|
+
'strategy': 'COW_TREE',
|
471
|
+
'backend': 'Copy-on-write AVL tree with reference counting',
|
472
|
+
'traits': [trait.name for trait in NodeTrait if self.has_trait(trait)]
|
473
|
+
}
|