pycedar 0.2.0__tar.gz → 0.2.2__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.0 → pycedar-0.2.2}/CHANGELOG.md +70 -1
- {pycedar-0.2.0 → pycedar-0.2.2}/MANIFEST.in +1 -0
- {pycedar-0.2.0/pycedar.egg-info → pycedar-0.2.2}/PKG-INFO +25 -3
- {pycedar-0.2.0 → pycedar-0.2.2}/README.ja.md +23 -2
- {pycedar-0.2.0 → pycedar-0.2.2}/README.md +24 -2
- pycedar-0.2.2/pycedar/VERSION +1 -0
- pycedar-0.2.2/pycedar/core/cedar/README.md +132 -0
- {pycedar-0.2.0 → pycedar-0.2.2}/pycedar/core/cedar/src/cedarpp.h +44 -24
- {pycedar-0.2.0 → pycedar-0.2.2}/pycedar/pycedar.cpp +4799 -8955
- {pycedar-0.2.0 → pycedar-0.2.2}/pycedar/pycedar.pyx +20 -15
- {pycedar-0.2.0 → pycedar-0.2.2/pycedar.egg-info}/PKG-INFO +25 -3
- {pycedar-0.2.0 → pycedar-0.2.2}/pycedar.egg-info/SOURCES.txt +1 -0
- {pycedar-0.2.0 → pycedar-0.2.2}/tests/test_pycedar.py +27 -0
- {pycedar-0.2.0 → pycedar-0.2.2}/tests/test_trie_api.py +69 -0
- pycedar-0.2.0/pycedar/VERSION +0 -1
- {pycedar-0.2.0 → pycedar-0.2.2}/pycedar/core/cedar/AUTHORS +0 -0
- {pycedar-0.2.0 → pycedar-0.2.2}/pycedar/core/cedar/BSD +0 -0
- {pycedar-0.2.0 → pycedar-0.2.2}/pycedar/core/cedar/COPYING +0 -0
- {pycedar-0.2.0 → pycedar-0.2.2}/pycedar/core/cedar/GPL +0 -0
- {pycedar-0.2.0 → pycedar-0.2.2}/pycedar/core/cedar/LGPL +0 -0
- {pycedar-0.2.0 → pycedar-0.2.2}/pycedar/core/cedar/THANKS +0 -0
- {pycedar-0.2.0 → pycedar-0.2.2}/pycedar/pycedar.pxd +0 -0
- {pycedar-0.2.0 → pycedar-0.2.2}/pycedar.egg-info/dependency_links.txt +0 -0
- {pycedar-0.2.0 → pycedar-0.2.2}/pycedar.egg-info/top_level.txt +0 -0
- {pycedar-0.2.0 → pycedar-0.2.2}/pyproject.toml +0 -0
- {pycedar-0.2.0 → pycedar-0.2.2}/setup.cfg +0 -0
- {pycedar-0.2.0 → pycedar-0.2.2}/setup.py +0 -0
- {pycedar-0.2.0 → pycedar-0.2.2}/tests/test_readme.py +0 -0
|
@@ -7,6 +7,73 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.2] - 2026-09-16
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `benchmarks/bench.py`, which times the operations pycedar is used for so that
|
|
15
|
+
a performance change can be measured rather than assumed.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- `dict.get()`, `set()`, `setdefault()`, `update()` and `get_node()` are roughly
|
|
20
|
+
twice as fast. They were declared with a fused `str`/`bytes` parameter, and
|
|
21
|
+
Cython's runtime dispatch for that cost more than the trie operation itself.
|
|
22
|
+
The parameter is now `object`, which is what `d[key]` already used; the key
|
|
23
|
+
type is still enforced by the trie underneath, so behaviour is unchanged.
|
|
24
|
+
- Sentinel checks on the lookup paths compare against C constants instead of
|
|
25
|
+
building a Python tuple of Python ints on every call, which speeds up
|
|
26
|
+
`d[key]`, `key in d` and the traversals.
|
|
27
|
+
|
|
28
|
+
Measured on 20000 random keys, CPython 3.13, nanoseconds per operation:
|
|
29
|
+
|
|
30
|
+
| operation | 0.2.1 | 0.2.2 |
|
|
31
|
+
| --- | --- | --- |
|
|
32
|
+
| `d.get(key)` | 302 | 135 |
|
|
33
|
+
| `d.set(key, value)` | 242 | 108 |
|
|
34
|
+
| `d.update(key, delta)` | 235 | 112 |
|
|
35
|
+
| `d.setdefault(key)` | 290 | 129 |
|
|
36
|
+
| `d.get_node(key)` | 348 | 177 |
|
|
37
|
+
| `d[key]` | 165 | 132 |
|
|
38
|
+
| `key in d` | 133 | 123 |
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- `node.traverse()` compared integers with `is`, which only worked because
|
|
43
|
+
CPython interns small integers. It now uses `!=`.
|
|
44
|
+
|
|
45
|
+
## [0.2.1] - 2026-09-16
|
|
46
|
+
|
|
47
|
+
### Added
|
|
48
|
+
|
|
49
|
+
- `pycedar/core/cedar/README.md`, recording what was modified in the vendored
|
|
50
|
+
copy of cedar and why, that upstream's `cedarpp.h` has been unchanged since
|
|
51
|
+
2017 despite the 2022 tarball, and why that tarball was deliberately not
|
|
52
|
+
re-vendored.
|
|
53
|
+
|
|
54
|
+
### Changed
|
|
55
|
+
|
|
56
|
+
- Allocation failures now raise `MemoryError` instead of `RuntimeError`. The
|
|
57
|
+
vendored cedar throws `std::bad_alloc`, which Cython's `except +` maps to
|
|
58
|
+
`MemoryError`, so every allocation failure reaches Python as the same, and
|
|
59
|
+
the idiomatic, exception. `RuntimeError` is now reserved for the one
|
|
60
|
+
non-allocation case, a zero-length key reaching cedar's `update()`.
|
|
61
|
+
- The vendored `cedarpp.h` adopts upstream's `clear()` layout and its
|
|
62
|
+
`STATIC_ASSERT` pragmas. Behaviour is unchanged; this removes the six
|
|
63
|
+
compiler warnings the file used to emit and reduces the delta against
|
|
64
|
+
upstream.
|
|
65
|
+
|
|
66
|
+
### Fixed
|
|
67
|
+
|
|
68
|
+
- An allocation failure inside `save()` or `load()` no longer terminates the
|
|
69
|
+
interpreter. The vendored cedar called `std::exit(1)` from `shrink_tail()`
|
|
70
|
+
and `open()`, killing the process and discarding buffered output; both now
|
|
71
|
+
throw. `open()` resets itself to a valid empty trie first, so the instance
|
|
72
|
+
stays usable instead of being handed back half-built.
|
|
73
|
+
- `load()` no longer leaks a file descriptor every time it fails. Upstream's
|
|
74
|
+
`open()` returns `-1` from nine places without closing the file; 200 failed
|
|
75
|
+
loads leaked exactly 200 descriptors, eventually exhausting the table.
|
|
76
|
+
|
|
10
77
|
## [0.2.0] - 2026-09-16
|
|
11
78
|
|
|
12
79
|
This release restores compatibility with current Python and Cython toolchains,
|
|
@@ -99,7 +166,9 @@ records; the corresponding tags were added retroactively.
|
|
|
99
166
|
|
|
100
167
|
- Build failure with clang on macOS.
|
|
101
168
|
|
|
102
|
-
[Unreleased]: https://github.com/akivajp/pycedar/compare/v0.2.
|
|
169
|
+
[Unreleased]: https://github.com/akivajp/pycedar/compare/v0.2.2...HEAD
|
|
170
|
+
[0.2.2]: https://github.com/akivajp/pycedar/compare/v0.2.1...v0.2.2
|
|
171
|
+
[0.2.1]: https://github.com/akivajp/pycedar/compare/v0.2.0...v0.2.1
|
|
103
172
|
[0.2.0]: https://github.com/akivajp/pycedar/compare/v0.1.3...v0.2.0
|
|
104
173
|
[0.1.3]: https://github.com/akivajp/pycedar/compare/v0.1.2...v0.1.3
|
|
105
174
|
[0.1.2]: https://github.com/akivajp/pycedar/compare/v0.1.1...v0.1.2
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pycedar
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
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
|
|
@@ -355,6 +355,12 @@ source.
|
|
|
355
355
|
``save()`` / ``load()`` / ``open()`` return ``0`` on success and ``-1`` on
|
|
356
356
|
failure instead of raising; check the return value.
|
|
357
357
|
|
|
358
|
+
Running out of memory is the exception to that rule: ``save()`` and ``load()``
|
|
359
|
+
raise ``MemoryError`` rather than returning ``-1``. A failed ``load()`` leaves
|
|
360
|
+
the trie **empty**, because the previous contents are released before the new
|
|
361
|
+
ones are allocated. The instance stays valid and can be reused. See
|
|
362
|
+
[``pycedar/core/cedar/README.md``](pycedar/core/cedar/README.md).
|
|
363
|
+
|
|
358
364
|
## Development
|
|
359
365
|
|
|
360
366
|
```shell
|
|
@@ -372,6 +378,19 @@ $ python setup.py build_ext --inplace
|
|
|
372
378
|
|
|
373
379
|
``./clean.sh`` removes build artifacts.
|
|
374
380
|
|
|
381
|
+
### Benchmarks
|
|
382
|
+
|
|
383
|
+
``benchmarks/bench.py`` times the operations pycedar is used for. Run it against
|
|
384
|
+
two builds to check whether a change actually paid off:
|
|
385
|
+
|
|
386
|
+
```shell
|
|
387
|
+
$ python benchmarks/bench.py --label before
|
|
388
|
+
$ python benchmarks/bench.py --label after
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
It uses ``rich`` for the table when that is installed, and plain text
|
|
392
|
+
otherwise. ``--help`` lists the knobs.
|
|
393
|
+
|
|
375
394
|
## Releasing
|
|
376
395
|
|
|
377
396
|
The version lives in a single place, [``pycedar/VERSION``](pycedar/VERSION).
|
|
@@ -393,7 +412,10 @@ LGPLv2.1 and BSD-2-Clause. See the license files bundled under
|
|
|
393
412
|
## Credits
|
|
394
413
|
|
|
395
414
|
``cedar`` is written by Naoki Yoshinaga. The copy vendored under
|
|
396
|
-
``pycedar/core/cedar/`` carries
|
|
397
|
-
|
|
415
|
+
``pycedar/core/cedar/`` carries local modifications.
|
|
416
|
+
[``pycedar/core/cedar/README.md``](pycedar/core/cedar/README.md) records what
|
|
417
|
+
was changed and why, why the 2022 upstream tarball was deliberately not
|
|
418
|
+
re-vendored, and what has to be re-applied if anyone syncs with a newer
|
|
419
|
+
release.
|
|
398
420
|
|
|
399
421
|
See [CHANGELOG.md](CHANGELOG.md) for the list of contributors to each release.
|
|
@@ -314,6 +314,12 @@ cedar ネイティブの ``.dat`` 形式は、書き出したマシンのポイ
|
|
|
314
314
|
``save()`` / ``load()`` / ``open()`` は例外を送出せず、成功で ``0``、失敗で
|
|
315
315
|
``-1`` を返します。必ず戻り値を確認してください。
|
|
316
316
|
|
|
317
|
+
唯一の例外がメモリ不足で、``save()`` と ``load()`` は ``-1`` を返すのではなく
|
|
318
|
+
``MemoryError`` を送出します。``load()`` が失敗した場合、cedar は新しい配列を
|
|
319
|
+
確保する前に既存の配列を解放するため、**トライは空になります**。インスタンス
|
|
320
|
+
自体は有効なまま再利用できます。詳細は
|
|
321
|
+
[``pycedar/core/cedar/README.md``](pycedar/core/cedar/README.md) を参照してください。
|
|
322
|
+
|
|
317
323
|
## 開発
|
|
318
324
|
|
|
319
325
|
```shell
|
|
@@ -331,6 +337,19 @@ $ python setup.py build_ext --inplace
|
|
|
331
337
|
|
|
332
338
|
``./clean.sh`` でビルド生成物を削除できます。
|
|
333
339
|
|
|
340
|
+
### ベンチマーク
|
|
341
|
+
|
|
342
|
+
``benchmarks/bench.py`` が主要な操作の実行時間を計測します。変更に効果があった
|
|
343
|
+
かどうかは、2 つのビルドで実行して比較してください。
|
|
344
|
+
|
|
345
|
+
```shell
|
|
346
|
+
$ python benchmarks/bench.py --label before
|
|
347
|
+
$ python benchmarks/bench.py --label after
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
``rich`` が入っていれば表形式で、無ければプレーンテキストで出力します。
|
|
351
|
+
指定できる項目は ``--help`` を参照してください。
|
|
352
|
+
|
|
334
353
|
## リリース手順
|
|
335
354
|
|
|
336
355
|
バージョンは [``pycedar/VERSION``](pycedar/VERSION) の 1 箇所のみで管理します。
|
|
@@ -352,7 +371,9 @@ BSD-2-Clause)。ライセンス文書は
|
|
|
352
371
|
## クレジット
|
|
353
372
|
|
|
354
373
|
``cedar`` は Naoki Yoshinaga 氏によるものです。``pycedar/core/cedar/`` に同梱
|
|
355
|
-
|
|
356
|
-
|
|
374
|
+
しているコピーにはローカル変更があります。変更内容とその理由、2022 年の上流
|
|
375
|
+
tarball をあえて取り込まなかった判断の経緯、より新しい版へ同期する際に再適用
|
|
376
|
+
すべき内容は
|
|
377
|
+
[``pycedar/core/cedar/README.md``](pycedar/core/cedar/README.md) に記録しています。
|
|
357
378
|
|
|
358
379
|
各リリースの貢献者は [CHANGELOG.md](CHANGELOG.md) を参照してください。
|
|
@@ -317,6 +317,12 @@ source.
|
|
|
317
317
|
``save()`` / ``load()`` / ``open()`` return ``0`` on success and ``-1`` on
|
|
318
318
|
failure instead of raising; check the return value.
|
|
319
319
|
|
|
320
|
+
Running out of memory is the exception to that rule: ``save()`` and ``load()``
|
|
321
|
+
raise ``MemoryError`` rather than returning ``-1``. A failed ``load()`` leaves
|
|
322
|
+
the trie **empty**, because the previous contents are released before the new
|
|
323
|
+
ones are allocated. The instance stays valid and can be reused. See
|
|
324
|
+
[``pycedar/core/cedar/README.md``](pycedar/core/cedar/README.md).
|
|
325
|
+
|
|
320
326
|
## Development
|
|
321
327
|
|
|
322
328
|
```shell
|
|
@@ -334,6 +340,19 @@ $ python setup.py build_ext --inplace
|
|
|
334
340
|
|
|
335
341
|
``./clean.sh`` removes build artifacts.
|
|
336
342
|
|
|
343
|
+
### Benchmarks
|
|
344
|
+
|
|
345
|
+
``benchmarks/bench.py`` times the operations pycedar is used for. Run it against
|
|
346
|
+
two builds to check whether a change actually paid off:
|
|
347
|
+
|
|
348
|
+
```shell
|
|
349
|
+
$ python benchmarks/bench.py --label before
|
|
350
|
+
$ python benchmarks/bench.py --label after
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
It uses ``rich`` for the table when that is installed, and plain text
|
|
354
|
+
otherwise. ``--help`` lists the knobs.
|
|
355
|
+
|
|
337
356
|
## Releasing
|
|
338
357
|
|
|
339
358
|
The version lives in a single place, [``pycedar/VERSION``](pycedar/VERSION).
|
|
@@ -355,7 +374,10 @@ LGPLv2.1 and BSD-2-Clause. See the license files bundled under
|
|
|
355
374
|
## Credits
|
|
356
375
|
|
|
357
376
|
``cedar`` is written by Naoki Yoshinaga. The copy vendored under
|
|
358
|
-
``pycedar/core/cedar/`` carries
|
|
359
|
-
|
|
377
|
+
``pycedar/core/cedar/`` carries local modifications.
|
|
378
|
+
[``pycedar/core/cedar/README.md``](pycedar/core/cedar/README.md) records what
|
|
379
|
+
was changed and why, why the 2022 upstream tarball was deliberately not
|
|
380
|
+
re-vendored, and what has to be re-applied if anyone syncs with a newer
|
|
381
|
+
release.
|
|
360
382
|
|
|
361
383
|
See [CHANGELOG.md](CHANGELOG.md) for the list of contributors to each release.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.2.2
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Vendored copy of cedar
|
|
2
|
+
|
|
3
|
+
This directory holds a vendored copy of `cedar`, the double-array trie
|
|
4
|
+
implementation that pycedar wraps.
|
|
5
|
+
|
|
6
|
+
* Upstream: <http://www.tkl.iis.u-tokyo.ac.jp/~ynaga/cedar/>
|
|
7
|
+
* Author: Naoki Yoshinaga
|
|
8
|
+
* License: GPLv2, LGPLv2.1 and BSD-2-Clause (see `BSD`, `COPYING`, `GPL`, `LGPL`)
|
|
9
|
+
|
|
10
|
+
This file records what was changed locally and why, so that a future maintainer
|
|
11
|
+
who finds a newer tarball on the upstream site can decide what to do without
|
|
12
|
+
repeating the investigation.
|
|
13
|
+
(上流サイトに新しい tarball を見つけた人が、調査をやり直さずに判断できるようにするための記録)
|
|
14
|
+
|
|
15
|
+
## What is actually used
|
|
16
|
+
|
|
17
|
+
Only **one** file is compiled and shipped. Everything else is here because the
|
|
18
|
+
original tarball was vendored wholesale, and is dead weight.
|
|
19
|
+
|
|
20
|
+
| Path | Status |
|
|
21
|
+
| --- | --- |
|
|
22
|
+
| `src/cedarpp.h` | **Compiled and shipped.** The only file `pycedar.pxd` declares (`cdef extern from "cedarpp.h"`) and the only source `setup.py` ships in `package_data`. |
|
|
23
|
+
| `AUTHORS`, `BSD`, `COPYING`, `GPL`, `LGPL`, `THANKS` | Shipped, for license compliance. |
|
|
24
|
+
| `src/cedar.h`, `src/cedar.cc`, `src/bench.cc`, `src/bench_static.cc`, `src/mkcedar.cc`, `src/Makefile.am`, `Makefile.in` | Present in git, **never compiled and never shipped.** They differ substantially from upstream, which is irrelevant. |
|
|
25
|
+
|
|
26
|
+
## Local modifications to `src/cedarpp.h`
|
|
27
|
+
|
|
28
|
+
Upstream's error handler terminates the process:
|
|
29
|
+
|
|
30
|
+
```cpp
|
|
31
|
+
static void _err (const char* fn, const int ln, const char* msg)
|
|
32
|
+
{ std::fprintf (stderr, "cedar: %s [%d]: %s", fn, ln, msg); std::exit (1); }
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
For a library loaded into a Python process that is unacceptable, so **four** of
|
|
36
|
+
the five `_err()` call sites were converted to throw instead. The Cython
|
|
37
|
+
declarations in `pycedar.pxd` carry `except +`, which turns the exception into a
|
|
38
|
+
Python one.
|
|
39
|
+
|
|
40
|
+
| Change | Reason |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| `#include <new>`, `#include <stdexcept>` | Needed by the `throw` sites below. |
|
|
43
|
+
| `update()`: inserting a zero-length key throws `std::runtime_error` instead of calling `_err()` | Surfaces as a Python exception. `pycedar` rejects empty keys with `KeyError` before reaching this, so it is a backstop. |
|
|
44
|
+
| `_realloc_array()`: a failed `realloc` throws `std::bad_alloc` instead of calling `_err()` | Surfaces as `MemoryError` rather than killing the interpreter. |
|
|
45
|
+
| `shrink_tail()`: a failed `malloc` throws `std::bad_alloc` instead of calling `_err()` | Reached through `save()`, whose `shrink` argument defaults to `True`. Nothing has been mutated at that point, so the trie survives intact. |
|
|
46
|
+
| `open()`: a failed `malloc` calls `fclose()`, then `clear(true)`, then throws `std::bad_alloc` | Reached through `load()`. Unlike the site above, `clear(false)` has already dropped the previous contents, so the object has to be reset to a valid empty trie before the exception escapes. |
|
|
47
|
+
| `open()`: every early `return -1` closes the file first | Upstream leaks the `FILE*` on all nine of those paths. A loop of failed loads exhausts the descriptor table: 200 failed loads leaked exactly 200 descriptors before the fix. |
|
|
48
|
+
| `_consult()` synced with upstream revision 1916 (2017-07-12) | Upstream bug fix, applied in [#2](https://github.com/akivajp/pycedar/pull/2). |
|
|
49
|
+
| `clear()` layout and the `STATIC_ASSERT` pragmas taken from upstream | Pure formatting, adopted to remove the compiler warnings this copy used to emit. Reduces the delta against upstream rather than adding to it. |
|
|
50
|
+
|
|
51
|
+
The original lines are kept as comments next to the replacements, so the delta
|
|
52
|
+
against upstream stays readable.
|
|
53
|
+
|
|
54
|
+
### The one `_err()` call site that is left
|
|
55
|
+
|
|
56
|
+
`dump()` still calls `_err()`, and therefore still calls `std::exit(1)`. That is
|
|
57
|
+
acceptable because `pycedar.pxd` does not declare `dump()`, so it is
|
|
58
|
+
unreachable from Python. Check with:
|
|
59
|
+
|
|
60
|
+
```shell
|
|
61
|
+
$ grep -n '_err (__FILE__' pycedar/core/cedar/src/cedarpp.h
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Every line that comes back should either be commented out or sit inside
|
|
65
|
+
`dump()`. Anything else is a path that can kill the interpreter.
|
|
66
|
+
(上記以外が出てきたら、インタプリタを落としうる経路が増えたということ)
|
|
67
|
+
|
|
68
|
+
### What an allocation failure looks like from Python
|
|
69
|
+
|
|
70
|
+
Every allocation failure raises `MemoryError`, because Cython's `except +` maps
|
|
71
|
+
`std::bad_alloc` to it. Use `std::bad_alloc` — not `std::runtime_error` — for any
|
|
72
|
+
further allocation site, so that all of them stay on one exception type.
|
|
73
|
+
(確保失敗はすべて `MemoryError`。新たに確保箇所を足すときも `std::bad_alloc` を使うこと)
|
|
74
|
+
|
|
75
|
+
`std::runtime_error` is reserved for the one non-allocation case, the
|
|
76
|
+
zero-length key in `update()`, which reaches Python as `RuntimeError`.
|
|
77
|
+
|
|
78
|
+
`load()` is destructive: recovering from a failed load leaves an **empty** trie,
|
|
79
|
+
not the previous contents. cedar frees the old arrays before allocating the new
|
|
80
|
+
ones, so the old contents are already gone by the time the failure is detected.
|
|
81
|
+
(読み込み失敗後のトライは空になる。cedar は新規確保より前に旧配列を解放するため)
|
|
82
|
+
|
|
83
|
+
## Upstream status, checked 2026-09-16
|
|
84
|
+
|
|
85
|
+
The download page offers `cedar-latest.tar.gz`, which unpacks to
|
|
86
|
+
`cedar-2022-03-18/`. That date is misleading:
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
cedar-2022-03-18/src/cedarpp.h
|
|
90
|
+
$Id: cedarpp.h 1916 2017-07-12 07:30:56Z ynaga $
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**`cedarpp.h` itself has not changed upstream since 2017-07-12.** The 2022
|
|
94
|
+
tarball is a repackage. The single functional change made in 2017 was the
|
|
95
|
+
`_consult()` fix, and pycedar already carries it.
|
|
96
|
+
|
|
97
|
+
Reproduce the comparison with:
|
|
98
|
+
|
|
99
|
+
```shell
|
|
100
|
+
$ curl -O http://www.tkl.iis.u-tokyo.ac.jp/~ynaga/cedar/cedar-latest.tar.gz
|
|
101
|
+
$ tar xzf cedar-latest.tar.gz
|
|
102
|
+
$ diff -u pycedar/core/cedar/src/cedarpp.h cedar-*/src/cedarpp.h
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Decision: the 2022 tarball was not re-vendored
|
|
106
|
+
|
|
107
|
+
Once the `_consult()` fix landed, the remaining delta against upstream is only
|
|
108
|
+
the deliberate local patches listed above, plus comment wording. Everything
|
|
109
|
+
else that upstream had and this copy did not — the `clear()` layout and the
|
|
110
|
+
`STATIC_ASSERT` pragmas — has since been adopted, which silenced the six
|
|
111
|
+
compiler warnings this copy used to emit:
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
5 x -Wmisleading-indentation (from the old clear() formatting)
|
|
115
|
+
1 x -Wunused-local-typedefs (from STATIC_ASSERT)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
There is **no functional difference left to gain**. Re-vendoring wholesale would
|
|
119
|
+
mean re-applying the patches by hand, and silently losing them would convert
|
|
120
|
+
recoverable errors back into `std::exit(1)` and reintroduce the descriptor leak
|
|
121
|
+
— the worst possible regressions for a library. That risk buys nothing.
|
|
122
|
+
|
|
123
|
+
## If you do re-vendor
|
|
124
|
+
|
|
125
|
+
1. Diff the new `src/cedarpp.h` against this copy first, and confirm that the
|
|
126
|
+
upstream `$Id:` revision actually changed. If it did not, stop.
|
|
127
|
+
2. Re-apply every row of the modification table above.
|
|
128
|
+
3. Verify that no reachable `_err()` call site came back:
|
|
129
|
+
`grep -n '_err (__FILE__' pycedar/core/cedar/src/cedarpp.h`
|
|
130
|
+
4. Update the note at the top of `cedarpp.h` and this file.
|
|
131
|
+
5. Run `pytest`. The suite includes randomized insert/delete consistency checks
|
|
132
|
+
that exercise the double-array rebalancing paths where upstream bugs live.
|
|
@@ -2,13 +2,19 @@
|
|
|
2
2
|
// $Id: cedarpp.h 1830 2014-06-16 06:17:42Z ynaga $
|
|
3
3
|
// Copyright (c) 2009-2014 Naoki Yoshinaga <ynaga@tkl.iis.u-tokyo.ac.jp>
|
|
4
4
|
//
|
|
5
|
-
// NOTE (pycedar): this is a vendored copy
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
// Apart from
|
|
5
|
+
// NOTE (pycedar): this is a vendored copy carrying local modifications. Four of
|
|
6
|
+
// the five _err() call sites throw instead of calling std::exit(1) -- allocation
|
|
7
|
+
// failures throw std::bad_alloc, which reaches Python as MemoryError -- and the
|
|
8
|
+
// fifth is in dump(), which pycedar never reaches. open() also closes the file
|
|
9
|
+
// on its early returns, which upstream does not. _consult(), the clear() layout
|
|
10
|
+
// and the STATIC_ASSERT pragmas are synced with upstream revision 1916
|
|
11
|
+
// (2017-07-12). Apart from that, this file is functionally identical to
|
|
12
|
+
// cedar-2022-03-18 -- whose cedarpp.h is itself unchanged since 2017, so there
|
|
13
|
+
// is nothing newer to pick up.
|
|
14
|
+
//
|
|
15
|
+
// Before editing or re-vendoring this file, read ../../README.md in this
|
|
16
|
+
// repository (pycedar/core/cedar/README.md). It records what was changed, why,
|
|
17
|
+
// and what must be re-applied if you ever sync with a newer upstream release.
|
|
12
18
|
#ifndef CEDAR_H
|
|
13
19
|
#define CEDAR_H
|
|
14
20
|
|
|
@@ -17,6 +23,7 @@
|
|
|
17
23
|
#include <cstring>
|
|
18
24
|
#include <climits>
|
|
19
25
|
#include <cassert>
|
|
26
|
+
#include <new> // std::bad_alloc
|
|
20
27
|
#include <stdexcept> // std::runtime_error
|
|
21
28
|
|
|
22
29
|
#ifdef HAVE_CONFIG_H
|
|
@@ -79,9 +86,11 @@ namespace cedar {
|
|
|
79
86
|
block () : prev (0), next (0), num (256), reject (257), trial (0), ehead (0) {}
|
|
80
87
|
};
|
|
81
88
|
da () : tracking_node (), _array (0), _tail (0), _tail0 (0), _ninfo (0), _block (0), _bheadF (0), _bheadC (0), _bheadO (0), _capacity (0), _size (0), _quota (0), _quota0 (0), _no_delete (false), _reject () {
|
|
89
|
+
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
|
|
82
90
|
STATIC_ASSERT(sizeof (value_type) <= sizeof (int),
|
|
83
91
|
value_type_is_not_supported___maintain_a_value_array_by_yourself_and_store_its_index_to_trie
|
|
84
92
|
);
|
|
93
|
+
#pragma GCC diagnostic warning "-Wunused-local-typedefs"
|
|
85
94
|
_initialize ();
|
|
86
95
|
}
|
|
87
96
|
~da () { clear (false); }
|
|
@@ -331,7 +340,10 @@ namespace cedar {
|
|
|
331
340
|
= static_cast <size_t> (*_length)
|
|
332
341
|
- static_cast <size_t> (*_length0) * (1 + sizeof (value_type));
|
|
333
342
|
t.tail = static_cast <char*> (std::malloc (length_));
|
|
334
|
-
if (! t.tail)
|
|
343
|
+
if (! t.tail)
|
|
344
|
+
// pycedar: nothing has been mutated yet, so the trie survives intact.
|
|
345
|
+
//_err (__FILE__, __LINE__, "memory allocation failed\n");
|
|
346
|
+
throw std::bad_alloc ();
|
|
335
347
|
*t.length = static_cast <int> (sizeof (int));
|
|
336
348
|
for (int to = 0; to < _size; ++to) {
|
|
337
349
|
node& n = _array[to];
|
|
@@ -383,16 +395,16 @@ namespace cedar {
|
|
|
383
395
|
if (! fp) return -1;
|
|
384
396
|
// get size
|
|
385
397
|
if (! size_) {
|
|
386
|
-
if (std::fseek (fp, 0, SEEK_END) != 0) return -1;
|
|
398
|
+
if (std::fseek (fp, 0, SEEK_END) != 0) { std::fclose (fp); return -1; }
|
|
387
399
|
size_ = static_cast <size_t> (std::ftell (fp));
|
|
388
|
-
if (std::fseek (fp, 0, SEEK_SET) != 0) return -1;
|
|
400
|
+
if (std::fseek (fp, 0, SEEK_SET) != 0) { std::fclose (fp); return -1; }
|
|
389
401
|
}
|
|
390
|
-
if (size_ <= offset) return -1;
|
|
391
|
-
if (std::fseek (fp, static_cast <long> (offset), SEEK_SET) != 0) return -1;
|
|
402
|
+
if (size_ <= offset) { std::fclose (fp); return -1; }
|
|
403
|
+
if (std::fseek (fp, static_cast <long> (offset), SEEK_SET) != 0) { std::fclose (fp); return -1; }
|
|
392
404
|
int len = 0;
|
|
393
|
-
if (std::fread (&len, sizeof (int), 1, fp) != 1) return -1;
|
|
405
|
+
if (std::fread (&len, sizeof (int), 1, fp) != 1) { std::fclose (fp); return -1; }
|
|
394
406
|
const size_t length_ = static_cast <size_t> (len);
|
|
395
|
-
if (size_ <= offset + length_) return -1;
|
|
407
|
+
if (size_ <= offset + length_) { std::fclose (fp); return -1; }
|
|
396
408
|
// set array
|
|
397
409
|
clear (false);
|
|
398
410
|
size_ = (size_ - offset - length_) / sizeof (node);
|
|
@@ -406,11 +418,18 @@ namespace cedar {
|
|
|
406
418
|
#else
|
|
407
419
|
if (! _array || ! _tail || ! _tail0)
|
|
408
420
|
#endif
|
|
409
|
-
_err (__FILE__, __LINE__, "memory allocation failed\n");
|
|
410
|
-
|
|
421
|
+
//_err (__FILE__, __LINE__, "memory allocation failed\n");
|
|
422
|
+
{ // pycedar: clear (false) above already dropped the previous contents,
|
|
423
|
+
// so reset to a valid empty trie before reporting the failure;
|
|
424
|
+
// otherwise the caller would be handed a half-built object.
|
|
425
|
+
std::fclose (fp);
|
|
426
|
+
clear (true);
|
|
427
|
+
throw std::bad_alloc ();
|
|
428
|
+
}
|
|
429
|
+
if (std::fseek (fp, static_cast <long> (offset), SEEK_SET) != 0) { std::fclose (fp); return -1; }
|
|
411
430
|
if (length_ != std::fread (_tail, sizeof (char), length_, fp) ||
|
|
412
431
|
size_ != std::fread (_array, sizeof (node), size_, fp))
|
|
413
|
-
return -1;
|
|
432
|
+
{ std::fclose (fp); return -1; }
|
|
414
433
|
std::fclose (fp);
|
|
415
434
|
_size = static_cast <int> (size_);
|
|
416
435
|
*_length0 = 0;
|
|
@@ -425,7 +444,7 @@ namespace cedar {
|
|
|
425
444
|
std::fread (&_bheadO, sizeof (int), 1, fp);
|
|
426
445
|
if (size_ != std::fread (_ninfo, sizeof (ninfo), size_, fp) ||
|
|
427
446
|
size_ >> 8 != std::fread (_block, sizeof (block), size_ >> 8, fp))
|
|
428
|
-
return -1;
|
|
447
|
+
{ std::fclose (fp); return -1; }
|
|
429
448
|
std::fclose (fp);
|
|
430
449
|
_capacity = _size;
|
|
431
450
|
_quota = *_length;
|
|
@@ -454,11 +473,12 @@ namespace cedar {
|
|
|
454
473
|
const void* array () const { return _array; }
|
|
455
474
|
void clear (const bool reuse = true) {
|
|
456
475
|
if (_no_delete) _array = 0, _tail = 0;
|
|
457
|
-
if (_array) std::free (_array);
|
|
458
|
-
if (_tail) std::free (_tail);
|
|
459
|
-
if (_tail0) std::free (_tail0);
|
|
460
|
-
if (_ninfo) std::free (_ninfo);
|
|
461
|
-
if (_block) std::free (_block);
|
|
476
|
+
if (_array) std::free (_array);
|
|
477
|
+
if (_tail) std::free (_tail);
|
|
478
|
+
if (_tail0) std::free (_tail0);
|
|
479
|
+
if (_ninfo) std::free (_ninfo);
|
|
480
|
+
if (_block) std::free (_block);
|
|
481
|
+
_array = 0; _tail = 0; _tail0 = 0; _ninfo = 0; _block = 0;
|
|
462
482
|
_bheadF = _bheadC = _bheadO = _capacity = _size = _quota = _quota0 = 0;
|
|
463
483
|
if (reuse) _initialize ();
|
|
464
484
|
_no_delete = false;
|
|
@@ -529,7 +549,7 @@ namespace cedar {
|
|
|
529
549
|
void* tmp = std::realloc (p, sizeof (T) * static_cast <size_t> (size_n));
|
|
530
550
|
if (! tmp)
|
|
531
551
|
//std::free (p), _err (__FILE__, __LINE__, "memory reallocation failed\n");
|
|
532
|
-
throw std::
|
|
552
|
+
throw std::bad_alloc ();
|
|
533
553
|
p = static_cast <T*> (tmp);
|
|
534
554
|
static const T T0 = T ();
|
|
535
555
|
for (T* q (p + size_p), * const r (p + size_n); q != r; ++q) *q = T0;
|