pycedar 0.2.2__tar.gz → 0.3.1__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.
- {pycedar-0.2.2 → pycedar-0.3.1}/CHANGELOG.md +66 -1
- {pycedar-0.2.2/pycedar.egg-info → pycedar-0.3.1}/PKG-INFO +31 -9
- {pycedar-0.2.2 → pycedar-0.3.1}/README.ja.md +29 -8
- {pycedar-0.2.2 → pycedar-0.3.1}/README.md +30 -8
- pycedar-0.3.1/pycedar/VERSION +1 -0
- {pycedar-0.2.2 → pycedar-0.3.1}/pycedar/pycedar.cpp +6336 -4011
- {pycedar-0.2.2 → pycedar-0.3.1}/pycedar/pycedar.pyx +141 -42
- {pycedar-0.2.2 → pycedar-0.3.1/pycedar.egg-info}/PKG-INFO +31 -9
- {pycedar-0.2.2 → pycedar-0.3.1}/tests/test_pycedar.py +56 -29
- {pycedar-0.2.2 → pycedar-0.3.1}/tests/test_trie_api.py +58 -0
- pycedar-0.2.2/pycedar/VERSION +0 -1
- {pycedar-0.2.2 → pycedar-0.3.1}/MANIFEST.in +0 -0
- {pycedar-0.2.2 → pycedar-0.3.1}/pycedar/core/cedar/AUTHORS +0 -0
- {pycedar-0.2.2 → pycedar-0.3.1}/pycedar/core/cedar/BSD +0 -0
- {pycedar-0.2.2 → pycedar-0.3.1}/pycedar/core/cedar/COPYING +0 -0
- {pycedar-0.2.2 → pycedar-0.3.1}/pycedar/core/cedar/GPL +0 -0
- {pycedar-0.2.2 → pycedar-0.3.1}/pycedar/core/cedar/LGPL +0 -0
- {pycedar-0.2.2 → pycedar-0.3.1}/pycedar/core/cedar/README.md +0 -0
- {pycedar-0.2.2 → pycedar-0.3.1}/pycedar/core/cedar/THANKS +0 -0
- {pycedar-0.2.2 → pycedar-0.3.1}/pycedar/core/cedar/src/cedarpp.h +0 -0
- {pycedar-0.2.2 → pycedar-0.3.1}/pycedar/pycedar.pxd +0 -0
- {pycedar-0.2.2 → pycedar-0.3.1}/pycedar.egg-info/SOURCES.txt +0 -0
- {pycedar-0.2.2 → pycedar-0.3.1}/pycedar.egg-info/dependency_links.txt +0 -0
- {pycedar-0.2.2 → pycedar-0.3.1}/pycedar.egg-info/top_level.txt +0 -0
- {pycedar-0.2.2 → pycedar-0.3.1}/pyproject.toml +0 -0
- {pycedar-0.2.2 → pycedar-0.3.1}/setup.cfg +0 -0
- {pycedar-0.2.2 → pycedar-0.3.1}/setup.py +0 -0
- {pycedar-0.2.2 → pycedar-0.3.1}/tests/test_readme.py +0 -0
|
@@ -7,6 +7,69 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.3.1] - 2026-09-16
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Lookups, writes and traversals are faster, with no change in behaviour.
|
|
15
|
+
|
|
16
|
+
`base_trie` declared only the operations that do not depend on the key type.
|
|
17
|
+
Everything else — `exact_match_search()`, `suffix()`, `set()`, `update()`,
|
|
18
|
+
`traverse()`, `erase()` and the prefix queries — lived only on the
|
|
19
|
+
specialisations, while the shared helpers and `pycedar.dict` hold a
|
|
20
|
+
`base_trie` reference. Every one of those calls was therefore a Python method
|
|
21
|
+
lookup by name, with each C integer argument boxed into a Python `int` on the
|
|
22
|
+
way in. They are now declared on `base_trie`, so the calls dispatch through
|
|
23
|
+
the vtable instead. The key parameter is typed as `object` because the three
|
|
24
|
+
specialisations accept different types; each one still validates what it is
|
|
25
|
+
given.
|
|
26
|
+
- `common_prefix_search()` no longer walks the trie twice. cedar advances one
|
|
27
|
+
position per byte of the key, so the result can never exceed `len(key)`;
|
|
28
|
+
sizing the buffer to that removes the counting pass entirely.
|
|
29
|
+
`common_prefix_predict()` has no such bound, so it starts from a speculative
|
|
30
|
+
buffer and retries once, at the exact size cedar reports, only when the
|
|
31
|
+
results do not fit.
|
|
32
|
+
|
|
33
|
+
Measured on 20000 random keys, CPython 3.13, nanoseconds per operation:
|
|
34
|
+
|
|
35
|
+
| operation | 0.3.0 | 0.3.1 |
|
|
36
|
+
| --- | --- | --- |
|
|
37
|
+
| `key in d` | 98 | 52 |
|
|
38
|
+
| `d[key]` | 107 | 61 |
|
|
39
|
+
| `d.get(key)` | 114 | 67 |
|
|
40
|
+
| `d.setdefault(key)` | 103 | 67 |
|
|
41
|
+
| `d.get_node(key)` | 145 | 99 |
|
|
42
|
+
| `d[key] = value` | 78 | 61 |
|
|
43
|
+
| `d.set(key, value)` | 85 | 70 |
|
|
44
|
+
| `d.update(key, delta)` | 90 | 75 |
|
|
45
|
+
| `common_prefix_predict()` | 3568 | 2572 |
|
|
46
|
+
| `list(d.items())` per key | 188 | 163 |
|
|
47
|
+
|
|
48
|
+
`exact_match_search()` called directly on a specialisation stayed at 82 ns,
|
|
49
|
+
as it should: that call was never the dynamic one.
|
|
50
|
+
|
|
51
|
+
## [0.3.0] - 2026-09-16
|
|
52
|
+
|
|
53
|
+
### Changed
|
|
54
|
+
|
|
55
|
+
- **Breaking**: storing `-1` or `-2` now raises `ValueError`. They are cedar's
|
|
56
|
+
sentinels, `base_trie.NO_VALUE` and `base_trie.NO_PATH`, and accepting them
|
|
57
|
+
silently corrupted the trie: `-1` made a key invisible to `in`, `get()` and
|
|
58
|
+
`d[key]` while leaving it visible to iteration, and `-2` terminated every
|
|
59
|
+
traversal early, hiding each key stored after it. Anyone affected was already
|
|
60
|
+
unable to read those values back, so the exception replaces silent data loss
|
|
61
|
+
rather than working behaviour.
|
|
62
|
+
|
|
63
|
+
The check lives in the writers shared by both layers, so `d[key] = value`,
|
|
64
|
+
`set()`, `setdefault()` and the trie level `set()` all enforce it.
|
|
65
|
+
`update()` validates the resulting value instead of the delta, because an
|
|
66
|
+
ordinary delta can still land on a sentinel; when it does, the delta is
|
|
67
|
+
rolled back and the stored value is left untouched. A key created by such a
|
|
68
|
+
rejected `update()` is left registered with the value `0`.
|
|
69
|
+
|
|
70
|
+
As a consequence, `get()` returning `NO_VALUE` is now unambiguous: no stored
|
|
71
|
+
value can collide with it.
|
|
72
|
+
|
|
10
73
|
## [0.2.2] - 2026-09-16
|
|
11
74
|
|
|
12
75
|
### Added
|
|
@@ -166,7 +229,9 @@ records; the corresponding tags were added retroactively.
|
|
|
166
229
|
|
|
167
230
|
- Build failure with clang on macOS.
|
|
168
231
|
|
|
169
|
-
[Unreleased]: https://github.com/akivajp/pycedar/compare/v0.
|
|
232
|
+
[Unreleased]: https://github.com/akivajp/pycedar/compare/v0.3.1...HEAD
|
|
233
|
+
[0.3.1]: https://github.com/akivajp/pycedar/compare/v0.3.0...v0.3.1
|
|
234
|
+
[0.3.0]: https://github.com/akivajp/pycedar/compare/v0.2.2...v0.3.0
|
|
170
235
|
[0.2.2]: https://github.com/akivajp/pycedar/compare/v0.2.1...v0.2.2
|
|
171
236
|
[0.2.1]: https://github.com/akivajp/pycedar/compare/v0.2.0...v0.2.1
|
|
172
237
|
[0.2.0]: https://github.com/akivajp/pycedar/compare/v0.1.3...v0.2.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pycedar
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Python binding of cedar (implementation of efficiently-updatable double-array trie) using Cython
|
|
5
5
|
Home-page: https://github.com/akivajp/pycedar
|
|
6
6
|
Author: Akiva Miura
|
|
@@ -332,16 +332,38 @@ A ``dict``-like façade over a trie. ``pycedar.dict(key_type)`` accepts ``str``
|
|
|
332
332
|
Values are stored as C ``int``, so they must fit in ``-2**31 .. 2**31-1``;
|
|
333
333
|
anything larger raises ``OverflowError``.
|
|
334
334
|
|
|
335
|
-
|
|
335
|
+
``-1`` and ``-2`` are cedar's sentinels — ``base_trie.NO_VALUE`` and
|
|
336
|
+
``base_trie.NO_PATH`` — and every writer rejects them:
|
|
336
337
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
338
|
+
```python
|
|
339
|
+
>>> limited = pycedar.dict()
|
|
340
|
+
>>> limited['key'] = -1
|
|
341
|
+
Traceback (most recent call last):
|
|
342
|
+
...
|
|
343
|
+
ValueError: -1 is reserved and cannot be stored: ...
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
``update()`` checks the resulting value rather than the delta, because a
|
|
347
|
+
perfectly ordinary delta can still land on a sentinel. When it does, the delta
|
|
348
|
+
is rolled back and the stored value is left untouched:
|
|
349
|
+
|
|
350
|
+
```python
|
|
351
|
+
>>> limited['counter'] = 1
|
|
352
|
+
>>> limited.update('counter', -3) # 1 + (-3) == -2
|
|
353
|
+
Traceback (most recent call last):
|
|
354
|
+
...
|
|
355
|
+
ValueError: -2 is reserved and cannot be stored: ...
|
|
356
|
+
>>> limited['counter']
|
|
357
|
+
1
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Every other value round trips, negative ones included.
|
|
342
361
|
|
|
343
|
-
|
|
344
|
-
|
|
362
|
+
Before 0.3.0 these two were accepted and silently corrupted the trie: ``-1``
|
|
363
|
+
made a key invisible to ``in``, ``get()`` and ``d[key]`` while leaving it
|
|
364
|
+
visible to iteration, and ``-2`` ended every traversal early, hiding each key
|
|
365
|
+
that came after it. If you are upgrading and were storing either, the values
|
|
366
|
+
were not being read back correctly in the first place.
|
|
345
367
|
|
|
346
368
|
### The serialization format is platform-dependent and unauthenticated
|
|
347
369
|
|
|
@@ -291,16 +291,37 @@ applet 2
|
|
|
291
291
|
値は C の ``int`` として格納されるため、``-2**31 .. 2**31-1`` に収まる必要が
|
|
292
292
|
あります。これを超えると ``OverflowError`` になります。
|
|
293
293
|
|
|
294
|
-
|
|
294
|
+
``-1`` と ``-2`` は cedar の番兵値(``base_trie.NO_VALUE`` と
|
|
295
|
+
``base_trie.NO_PATH``)であり、すべての書き込み経路が拒否します。
|
|
295
296
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
297
|
+
```python
|
|
298
|
+
>>> limited = pycedar.dict()
|
|
299
|
+
>>> limited['key'] = -1
|
|
300
|
+
Traceback (most recent call last):
|
|
301
|
+
...
|
|
302
|
+
ValueError: -1 is reserved and cannot be stored: ...
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
``update()`` はデルタではなく**加算結果**を検査します。ごく普通のデルタでも
|
|
306
|
+
結果が番兵値に着地しうるためです。着地した場合はデルタを巻き戻すので、
|
|
307
|
+
格納されている値は変化しません。
|
|
308
|
+
|
|
309
|
+
```python
|
|
310
|
+
>>> limited['counter'] = 1
|
|
311
|
+
>>> limited.update('counter', -3) # 1 + (-3) == -2
|
|
312
|
+
Traceback (most recent call last):
|
|
313
|
+
...
|
|
314
|
+
ValueError: -2 is reserved and cannot be stored: ...
|
|
315
|
+
>>> limited['counter']
|
|
316
|
+
1
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
これ以外の値は、負数を含めて通常どおり往復します。
|
|
301
320
|
|
|
302
|
-
|
|
303
|
-
|
|
321
|
+
0.3.0 より前はこの 2 つを受け付けてしまい、トライを静かに壊していました。
|
|
322
|
+
``-1`` はキーを ``in`` / ``get()`` / ``d[key]`` から不可視にする一方で走査には
|
|
323
|
+
現れ、``-2`` は走査をそこで打ち切って以降のキーをすべて隠していました。
|
|
324
|
+
いずれかを格納していた場合、そもそも正しく読み出せていなかったことになります。
|
|
304
325
|
|
|
305
326
|
### シリアライズ形式はプラットフォーム依存であり、検証機構を持たない
|
|
306
327
|
|
|
@@ -294,16 +294,38 @@ A ``dict``-like façade over a trie. ``pycedar.dict(key_type)`` accepts ``str``
|
|
|
294
294
|
Values are stored as C ``int``, so they must fit in ``-2**31 .. 2**31-1``;
|
|
295
295
|
anything larger raises ``OverflowError``.
|
|
296
296
|
|
|
297
|
-
|
|
297
|
+
``-1`` and ``-2`` are cedar's sentinels — ``base_trie.NO_VALUE`` and
|
|
298
|
+
``base_trie.NO_PATH`` — and every writer rejects them:
|
|
298
299
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
300
|
+
```python
|
|
301
|
+
>>> limited = pycedar.dict()
|
|
302
|
+
>>> limited['key'] = -1
|
|
303
|
+
Traceback (most recent call last):
|
|
304
|
+
...
|
|
305
|
+
ValueError: -1 is reserved and cannot be stored: ...
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
``update()`` checks the resulting value rather than the delta, because a
|
|
309
|
+
perfectly ordinary delta can still land on a sentinel. When it does, the delta
|
|
310
|
+
is rolled back and the stored value is left untouched:
|
|
311
|
+
|
|
312
|
+
```python
|
|
313
|
+
>>> limited['counter'] = 1
|
|
314
|
+
>>> limited.update('counter', -3) # 1 + (-3) == -2
|
|
315
|
+
Traceback (most recent call last):
|
|
316
|
+
...
|
|
317
|
+
ValueError: -2 is reserved and cannot be stored: ...
|
|
318
|
+
>>> limited['counter']
|
|
319
|
+
1
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Every other value round trips, negative ones included.
|
|
304
323
|
|
|
305
|
-
|
|
306
|
-
|
|
324
|
+
Before 0.3.0 these two were accepted and silently corrupted the trie: ``-1``
|
|
325
|
+
made a key invisible to ``in``, ``get()`` and ``d[key]`` while leaving it
|
|
326
|
+
visible to iteration, and ``-2`` ended every traversal early, hiding each key
|
|
327
|
+
that came after it. If you are upgrading and were storing either, the values
|
|
328
|
+
were not being read back correctly in the first place.
|
|
307
329
|
|
|
308
330
|
### The serialization format is platform-dependent and unauthenticated
|
|
309
331
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.3.1
|