gedcomkit 0.1.14__tar.gz → 0.1.16__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.
Files changed (99) hide show
  1. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/CHANGELOG.md +66 -0
  2. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/Cargo.lock +1 -1
  3. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/Cargo.toml +1 -1
  4. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/PKG-INFO +1 -1
  5. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/bindings/gedcomkit-py/Cargo.lock +2 -2
  6. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/bindings/gedcomkit-py/Cargo.toml +1 -1
  7. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/bindings/gedcomkit-py/src/lib.rs +30 -7
  8. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/src/gedzip.rs +1557 -119
  9. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/src/lib.rs +240 -1
  10. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/LICENSE +0 -0
  11. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/README.md +0 -0
  12. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/bindings/gedcomkit-py/LICENSE +0 -0
  13. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/bindings/gedcomkit-py/README.md +0 -0
  14. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/bindings/gedcomkit-py/gedcomkit.pyi +0 -0
  15. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom-spec/LICENSE +0 -0
  16. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom-spec/README.md +0 -0
  17. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom-spec/cardinalities.tsv +0 -0
  18. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom-spec/enumerations.tsv +0 -0
  19. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom-spec/enumerationsets.tsv +0 -0
  20. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom-spec/grammar.abnf +0 -0
  21. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom-spec/grammar.gedstruct +0 -0
  22. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom-spec/payloads.tsv +0 -0
  23. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom-spec/substructures.tsv +0 -0
  24. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/5/age-invalid.ged +0 -0
  25. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/5/age-valid.ged +0 -0
  26. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/5/atsign.ged +0 -0
  27. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/5/char_ascii_1.ged +0 -0
  28. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/5/char_ascii_2.ged +0 -0
  29. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/5/char_utf16be-1.ged +0 -0
  30. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/5/char_utf16be-2.ged +0 -0
  31. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/5/char_utf16le-1.ged +0 -0
  32. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/5/char_utf16le-2.ged +0 -0
  33. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/5/char_utf8-1.ged +0 -0
  34. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/5/char_utf8-2.ged +0 -0
  35. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/5/char_utf8-3.ged +0 -0
  36. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/5/date-all.ged +0 -0
  37. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/5/date-dual-invalid.ged +0 -0
  38. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/5/date-dual-valid.ged +0 -0
  39. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/5/enum-ext.ged +0 -0
  40. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/5/filename-1.ged +0 -0
  41. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/5/lang-all.ged +0 -0
  42. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/5/notes-1.ged +0 -0
  43. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/5/obje-1.ged +0 -0
  44. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/5/obsolete-1.ged +0 -0
  45. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/5/pedi-1.ged +0 -0
  46. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/5/rela_1.ged +0 -0
  47. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/5/sour-1.ged +0 -0
  48. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/5/tiny-1.ged +0 -0
  49. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/5/xref-case.ged +0 -0
  50. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/7/age-invalid.ged +0 -0
  51. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/7/age-valid.ged +0 -0
  52. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/7/atsign.ged +0 -0
  53. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/7/char_ascii_1.ged +0 -0
  54. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/7/char_ascii_2.ged +0 -0
  55. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/7/char_utf16be-1.ged +0 -0
  56. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/7/char_utf16be-2.ged +0 -0
  57. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/7/char_utf16le-1.ged +0 -0
  58. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/7/char_utf16le-2.ged +0 -0
  59. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/7/char_utf8-1.ged +0 -0
  60. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/7/char_utf8-2.ged +0 -0
  61. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/7/char_utf8-3.ged +0 -0
  62. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/7/date-all.ged +0 -0
  63. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/7/date-dual-invalid.ged +0 -0
  64. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/7/enum-ext.ged +0 -0
  65. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/7/filename-1.ged +0 -0
  66. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/7/lang-all.ged +0 -0
  67. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/7/notes-1.ged +0 -0
  68. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/7/obje-1.ged +0 -0
  69. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/7/obsolete-1.ged +0 -0
  70. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/7/pedi-1.ged +0 -0
  71. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/7/rela_1.ged +0 -0
  72. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/7/sour-1.ged +0 -0
  73. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/7/tiny-1.ged +0 -0
  74. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/7/xref-case.ged +0 -0
  75. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/LICENSE +0 -0
  76. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/gedcom7code/UPSTREAM-README.md +0 -0
  77. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/zip-interop/README.md +0 -0
  78. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/zip-interop/python-zip64.gdz +0 -0
  79. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/fixtures/vendored/zip-interop/python-zipfile.gdz +0 -0
  80. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/pyproject.toml +0 -0
  81. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/src/build.rs +0 -0
  82. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/src/convert.rs +0 -0
  83. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/src/dates.rs +0 -0
  84. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/src/decode.rs +0 -0
  85. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/src/family.rs +0 -0
  86. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/src/names.rs +0 -0
  87. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/src/schema.rs +0 -0
  88. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/src/synthetic.rs +0 -0
  89. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/src/tags.rs +0 -0
  90. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/src/validate.rs +0 -0
  91. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/src/view.rs +0 -0
  92. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/tests/endurance.rs +0 -0
  93. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/tests/external_charset.rs +0 -0
  94. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/tests/properties.rs +0 -0
  95. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/tests/requirement_corpus.rs +0 -0
  96. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/tests/spec_conformance.rs +0 -0
  97. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/tests/typescript.rs +0 -0
  98. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/tests/vendored_corpus.rs +0 -0
  99. {gedcomkit-0.1.14 → gedcomkit-0.1.16}/tests/version_conversion.rs +0 -0
@@ -5,6 +5,72 @@ format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
5
5
  the project uses semantic versioning within the usual pre-1.0 compatibility
6
6
  rules.
7
7
 
8
+ ## [0.1.16] - 2026-10-14
9
+
10
+ ### Added
11
+
12
+ - `Document::record_index` builds a `RecordIndex` that answers
13
+ `get(xref)`, `position(xref)`, and `contains(xref)` in constant time,
14
+ exactly as `Document::record` does: ASCII case folded, other characters
15
+ compared as themselves, the first of two records with one identifier
16
+ winning. It borrows the document, so the compiler refuses any edit to
17
+ `records` while it is alive and no answer can be stale; build one per
18
+ batch of lookups (a page of rows, a chart) instead of calling `record`
19
+ per item, which scans. No identifier is copied to build it.
20
+ - `gedzip::ArchiveWriter::add_streamed(name, source, size)`, for a sink that
21
+ is also `Seek`, writes an entry from any `Read` in 64 KiB chunks, so an
22
+ entry costs one buffer rather than about twice its size. Its size is
23
+ bounded by `ArchiveLimits::entry_bytes` alone (1 GiB by default); a writer
24
+ made with `new_with` and a higher figure takes entries past 4 GiB, through
25
+ Zip64. The local header is written first, with a Zip64 extra when the
26
+ entry needs one; the CRC-32 is computed as the bytes pass, and the writer
27
+ seeks back to fill it in and the compressed size, so the header is as
28
+ complete as `add`'s (no data descriptor). A stored entry is byte-for-byte
29
+ what `add` writes. `size` is checked against `entry_bytes` before anything
30
+ is written; a source that yields more or fewer bytes than `size`, a failing
31
+ source, a failed seek, or a sink that does not overwrite in place after a
32
+ seek (a file opened for appending) poisons the writer, since the sink then
33
+ holds a partial entry. A name `add` would deflate is deflated here too, but
34
+ kept deflated even when that did not make it smaller; and since its Zip64
35
+ need is settled against a bound before the compressed size is known, a
36
+ deflated entry within about an eighth of 4 GiB may carry a Zip64 extra in
37
+ its local header while its directory record is plain, which the format
38
+ allows.
39
+
40
+ ### Changed
41
+
42
+ - The Python binding's `ArchiveFileWriter.add_file` now streams the file
43
+ through `add_streamed` instead of reading it into memory, so adding a file
44
+ costs one buffer; the per-entry limit stays the default 1 GiB. It refuses
45
+ anything but a regular file before writing. The binding surfaces are
46
+ unchanged.
47
+ - `Document::xref_index` documents that its owned map can go stale after an
48
+ edit, and points to `record_index`.
49
+
50
+ ## [0.1.15] - 2026-10-11
51
+
52
+ ### Added
53
+
54
+ - `gedzip::Archive::copy_entry` decompresses one entry into any
55
+ `std::io::Write` in 64 KiB chunks and returns the byte count, so an entry
56
+ of any size can be extracted without holding it in memory. It enforces the
57
+ same rules as `read_entry`: the local header must agree with the directory,
58
+ the entry may not outgrow its declared size, the per-file and ratio limits
59
+ are checked against the bytes as they accumulate, and the CRC-32 is
60
+ verified at the end. The sink may already have received bytes when an error
61
+ is returned, so write to a temporary file and move it into place on success.
62
+
63
+ ### Changed
64
+
65
+ - `Archive::read_entry` is now `copy_entry` into a `Vec`, so the two share one
66
+ validation path and cannot disagree. Results and error messages are
67
+ unchanged.
68
+ - The per-file and compression-ratio checks at open and at extraction are one
69
+ function, and `copy_entry` re-applies them to the entry it is handed.
70
+ - `Archive::open_with` follows the Zip64 directory when the end record's
71
+ directory size is the `0xFFFF_FFFF` marker, as the writer promotes on it.
72
+ Before, only the entry count and offset were consulted.
73
+
8
74
  ## [0.1.14] - 2026-10-10
9
75
 
10
76
  ### Fixed
@@ -35,7 +35,7 @@ dependencies = [
35
35
 
36
36
  [[package]]
37
37
  name = "gedcomkit"
38
- version = "0.1.14"
38
+ version = "0.1.16"
39
39
  dependencies = [
40
40
  "flate2",
41
41
  "serde",
@@ -3,7 +3,7 @@
3
3
  # was checked on crates.io, npm, and PyPI before the initial release.
4
4
  [package]
5
5
  name = "gedcomkit"
6
- version = "0.1.14"
6
+ version = "0.1.16"
7
7
  description = "A byte-preserving GEDCOM document model: decoding, parsing, readings, version conversion, plausibility checks, and the GEDZIP container, for GEDCOM 5.5 through 7.x."
8
8
  edition = "2024"
9
9
  # The true minimum, not the workspace's toolchain: a parser library is a leaf
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gedcomkit
3
- Version: 0.1.14
3
+ Version: 0.1.16
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Programming Language :: Rust
6
6
  Classifier: Programming Language :: Python :: Implementation :: CPython
@@ -35,7 +35,7 @@ dependencies = [
35
35
 
36
36
  [[package]]
37
37
  name = "gedcomkit"
38
- version = "0.1.14"
38
+ version = "0.1.16"
39
39
  dependencies = [
40
40
  "flate2",
41
41
  "serde",
@@ -43,7 +43,7 @@ dependencies = [
43
43
 
44
44
  [[package]]
45
45
  name = "gedcomkit-py"
46
- version = "0.1.14"
46
+ version = "0.1.16"
47
47
  dependencies = [
48
48
  "gedcomkit",
49
49
  "pyo3",
@@ -2,7 +2,7 @@
2
2
  # abi3 keeps it to one wheel per platform across Python versions.
3
3
  [package]
4
4
  name = "gedcomkit-py"
5
- version = "0.1.14"
5
+ version = "0.1.16"
6
6
  description = "Python bindings for gedcomkit: an archival-grade GEDCOM library — byte-preserving round-trip, encoding detection, GEDCOM 5.5 through 7.x, GEDZIP."
7
7
  edition = "2024"
8
8
  rust-version = "1.88"
@@ -20,7 +20,7 @@ use pyo3::types::{PyBytes, PyModule};
20
20
  use pyo3::wrap_pyfunction;
21
21
  use serde::Serialize;
22
22
  use std::fs::File;
23
- use std::io::{BufWriter, Write as _};
23
+ use std::io::{BufReader, BufWriter, Write as _};
24
24
  use std::path::PathBuf;
25
25
 
26
26
  /// A parsed GEDCOM document and the report of how its bytes were read.
@@ -432,8 +432,11 @@ impl Default for ArchiveWriter {
432
432
  }
433
433
 
434
434
  /// Writes a GEDZIP archive straight to a file, one entry at a time, so
435
- /// memory stays a small multiple of the largest single entry however large
436
- /// the archive grows. Python only: the WASM binding has no filesystem.
435
+ /// memory stays a small multiple of the largest entry passed as bytes to
436
+ /// `add` — and one 64 KiB buffer for an entry streamed with `add_file` —
437
+ /// however large the archive grows. Each entry may be at most the library's
438
+ /// default per-entry limit, 1 GiB (`ArchiveLimits` has no Python binding
439
+ /// yet). Python only: the WASM binding has no filesystem.
437
440
  ///
438
441
  /// The file is created at once and is complete only after `finish`; an
439
442
  /// archive abandoned part-way (an error, or no `finish`) leaves a partial
@@ -466,12 +469,32 @@ impl ArchiveFileWriter {
466
469
  self.writer()?.add(name, data).map_err(value_error)
467
470
  }
468
471
 
469
- /// Adds one entry read from the file at `source`, without the bytes ever
470
- /// passing through Python. Raises `OSError` when it cannot be opened and
471
- /// `ValueError` as `add` does.
472
+ /// Adds one entry read from the regular file at `source`, without the
473
+ /// bytes ever passing through Python, and without holding the file in
474
+ /// memory: it streams in 64 KiB chunks, so it costs one buffer. The file
475
+ /// may be at most the writer's per-entry limit, the library's default of
476
+ /// 1 GiB. Raises `OSError` when it cannot be opened, `ValueError` when it
477
+ /// is not a regular file or as `add` does, and `ValueError` when the
478
+ /// file changes size while it is read — after which the archive cannot
479
+ /// be finished.
472
480
  fn add_file(&mut self, name: &str, source: PathBuf) -> PyResult<()> {
473
481
  let file = File::open(&source).map_err(|error| os_error(&source, &error))?;
474
- self.writer()?.add(name, file).map_err(value_error)
482
+ let metadata = file
483
+ .metadata()
484
+ .map_err(|error| os_error(&source, &error))?;
485
+ // A directory, pipe, or device reports no usable length, and would
486
+ // fail part-way through the entry; refuse it before anything is
487
+ // written, which leaves the archive usable.
488
+ if !metadata.is_file() {
489
+ return Err(PyValueError::new_err(format!(
490
+ "{} is not a regular file",
491
+ source.display()
492
+ )));
493
+ }
494
+ let size = metadata.len();
495
+ self.writer()?
496
+ .add_streamed(name, BufReader::new(file), size)
497
+ .map_err(value_error)
475
498
  }
476
499
 
477
500
  /// Writes the central directory, flushes, and closes the file, returning