leptris 1.9.307.0 → 1.9.309.0

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -0
  3. data/Rakefile +1 -1
  4. data/lib/leptris/vendor/aarch64-linux/libleptris.so +0 -0
  5. data/lib/leptris/vendor/aarch64-linux-musl/libleptris.so +0 -0
  6. data/lib/leptris/vendor/arm-linux/libleptris.so +0 -0
  7. data/lib/leptris/vendor/arm-linux-musl/libleptris.so +0 -0
  8. data/lib/leptris/vendor/arm64-darwin/libleptris.dylib +0 -0
  9. data/lib/leptris/vendor/ppc64le-linux/libleptris.so +0 -0
  10. data/lib/leptris/vendor/s390x-linux-musl/libleptris.so +0 -0
  11. data/lib/leptris/vendor/x86_64-darwin/libleptris.dylib +0 -0
  12. data/lib/leptris/vendor/x86_64-linux/libleptris.so +0 -0
  13. data/lib/leptris/vendor/x86_64-linux-musl/libleptris.so +0 -0
  14. data/lib/leptris/version.rb +1 -1
  15. data/vendor-src/README.md +1 -1
  16. data/vendor-src/libleptris/CHANGELOG.md +100 -0
  17. data/vendor-src/libleptris/CMakeLists.txt +1 -1
  18. data/vendor-src/libleptris/bindings/rust/Cargo.lock +7 -0
  19. data/vendor-src/libleptris/bindings/rust/Cargo.toml +4 -0
  20. data/vendor-src/libleptris/bindings/rust/README.md +19 -0
  21. data/vendor-src/libleptris/bindings/rust/build.rs +4 -0
  22. data/vendor-src/libleptris/bindings/rust/descriptor/Cargo.toml +16 -0
  23. data/vendor-src/libleptris/bindings/rust/descriptor/src/lib.rs +214 -0
  24. data/vendor-src/libleptris/bindings/rust/descriptor/tests/layout.rs +50 -0
  25. data/vendor-src/libleptris/bindings/rust/tests/descriptor_abi.rs +194 -0
  26. data/vendor-src/libleptris/src/leptris/xpath/evaluator.c +4 -0
  27. data/vendor-src/libleptris/src/leptris/xpath/evaluator_operators.c +225 -17
  28. data/vendor-src/libleptris/src/leptris/xpath/evaluator_types.c +6 -4
  29. data/vendor-src/libleptris/src/leptris/xpath/functions.c +75 -3
  30. data/vendor-src/libleptris/src/leptris/xpath/functions_ext31.c +551 -36
  31. data/vendor-src/libleptris/src/leptris/xpath/lexer.c +9 -0
  32. data/vendor-src/libleptris/src/leptris/xpath/parser.c +46 -1
  33. data/vendor-src/libleptris/src/leptris/xpath/xpath_public.c +1 -1
  34. data/vendor-src/libleptris/src/leptris/xquery/xquery.c +16 -1
  35. data/vendor-src/libleptris/test/xquery/test_qt3.cpp +64 -65
  36. data/vendor-src/libleptris/test/xquery/test_xquery.cpp +5 -1
  37. data/vendor-src/libleptris/vcpkg.json +1 -1
  38. metadata +6 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e764f89b28a825bc247e1d9ee4acaca9509c2d85ca76bb4ab1536c95b2b6367c
4
- data.tar.gz: f5d982d19eba0753368eb607beee093babf6163bfef26b109327842ec096f5d2
3
+ metadata.gz: 3cc7498cffb29da21d5b13691fd7ac8de3b4abdf89765699ecf79d03b186c1e1
4
+ data.tar.gz: 897683cbf8a8b510f733e51e3990e508d3d72ef45b1ef47985d8c601aecc68ff
5
5
  SHA512:
6
- metadata.gz: a11a89a2b41b0a9c630308e7f9e9c5bd390ed8b6d6683c2b5a324b6f9aa9d7c726b868ea20ad27260e2b3a78a91aa51046de882fac0099580631718f899d8795
7
- data.tar.gz: 1cfc39514919c05a95dee4940da80e94ca24a5bed352e4663101c904649de3a1452686ac74461555eac18017dfdce6b408fad2b0bc8eb6a2287f7c81d5a39f8e
6
+ metadata.gz: 481e075161468c9de96a2e6cdb66cb904b003f007aeb53188e5ad1919b059265b176c87ddcc944d43d3ef093431733a1d8525630f879d0e102888e02e895d51e
7
+ data.tar.gz: 0b31da872763118c3bad5f0b6fa1398e758fc6732863dbb27b3f25858eed3f5824073248ebacdd51332ba7fa406a62144933cd9e7cf9cd6e72a36fe8a4411325
data/CHANGELOG.md CHANGED
@@ -1,3 +1,34 @@
1
+ ## [1.9.308.0] - 2026-10-05
2
+
3
+ ### Engine sync (libleptris 1.9.308)
4
+
5
+ - **QT3 #1182 tail (leptris#1540)** — the 13 lever cases:
6
+ fn:avg's numeric promotion ladder with int64-exact decimal
7
+ division, the O(xs:double) carrier mark, typeswitch case `$v as
8
+ TYPE` binding arms, fn:apply function conversion (zero-arity,
9
+ atomization, sequence members), and fn:sum's per-type canonical
10
+ duration zero. Full QT3 61/61.
11
+ - **Per-engine crates, step 1 (leptris#1541)** — the shared
12
+ leptris-descriptor ABI crate in a new Cargo workspace, with the
13
+ runtime layout gate (Rust size_of vs engine introspection +
14
+ build/walk round trip) and the build.rs stale-link cache fix.
15
+ Ruby-side impact: none (Rust-side surface; zero header deltas,
16
+ audit unchanged 369/368, suite 866/0).
17
+
18
+ ## [1.9.307.0] - 2026-10-05
19
+
20
+ ### Fixed
21
+
22
+ - **Cross-document LEAF adoption (libleptris 1.9.307, engine
23
+ #1534 — completing the #1528 fix)**: the #1528 adoption gate
24
+ covered ELEMENT splice arguments only — a TEXT/CDATA/COMMENT/PI
25
+ node from another document was still spliced raw, so appending
26
+ scratch-document leaves (the `add_child(String)` fragment path's
27
+ text children) and freeing the scratch left the tree pointing
28
+ into freed memory. The four splice seams now adopt non-element
29
+ arguments by copy too; same-document moves stay zero-copy.
30
+ Leaf-adoption specs pin the free-then-walk shape.
31
+
1
32
  ## 1.9.306.0 — 2026-10-05
2
33
 
3
34
  Rides libleptris v1.9.306 (pin 1.9.304 -> 1.9.306): the Lane 18
data/Rakefile CHANGED
@@ -8,7 +8,7 @@ RSpec::Core::RakeTask.new(:spec)
8
8
  # Pin for `rake compile` and the platform-gem builds. Keep in lockstep
9
9
  # with .github/workflows/build.yml (which calls `rake compile`) and the
10
10
  # CHANGELOG when libleptris releases.
11
- LIBLEPTRIS_VERSION = "1.9.307"
11
+ LIBLEPTRIS_VERSION = "1.9.309"
12
12
  # Vendored alongside libleptris for fn:normalize-unicode (TODO
13
13
  # .restructure/20): built per platform with a RELOCATABLE @rpath
14
14
  # install name, loaded by ffi.rb before libleptris so the
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Leptris
4
- VERSION = "1.9.307.0"
4
+ VERSION = "1.9.309.0"
5
5
  end
data/vendor-src/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  Sources vendored in this gem (recompile rights; the ruby
2
2
  variant builds them at install via extconf.rb):
3
3
 
4
- - libleptris v1.9.307
4
+ - libleptris v1.9.309
5
5
  - utf8proc v2.11.0
6
6
 
7
7
  Rebuild by hand:
@@ -1,5 +1,95 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.9.309] - 2026-10-05
4
+
5
+ ### Fixed
6
+
7
+ - **XPath lexer: signed exponent literals truncated at the `E` —
8
+ `1E+1` evaluated to `2` and `3.567812E+3 eq 3567.812` was false.**
9
+ The number scanner consumed the exponent digits but not its sign,
10
+ so `1E+1` tokenized as `1E` followed by `+ 1`. The sign is now part
11
+ of the literal (also `5.2E-3` and friends).
12
+
13
+ ### Added
14
+
15
+ - **XQuery numeric literal typing (XQuery 3.1 §3.13.1) with
16
+ end-to-end type discrimination — 17 more QT3 cases adopted
17
+ (abs +5, ceiling +3, floor +3, round-half-to-even +6), 32 with the
18
+ same-batch string-to-codepoints and min/max gates.**
19
+ - Exponent literals are `xs:double`, decimals with a point are
20
+ `xs:decimal`, plain integers `xs:integer`; `1.1 instance of
21
+ xs:double` is now `false` per spec.
22
+ - `instance of` / `typeswitch` match labeled numeric results by
23
+ XDM derivation (`xs:integer` <: `xs:decimal`); `case $v as TYPE`
24
+ arms bind their variable inside the arm. Unlabeled numerics keep
25
+ the historical any-numeric match — no XPath 1.0 surface change.
26
+ - `fn:abs` preserves its argument's numeric type (integer/decimal/
27
+ float/double, float through float32); `fn:ceiling`/`fn:floor`
28
+ label float results; `fn:round-half-to-even` computes its scale
29
+ in double (no `int` truncation UB at 2^32) and guards huge
30
+ scales; `fn:min`/`fn:max` label results by promoted input type;
31
+ FLWOR bindings preserve float/decimal/double marks.
32
+ - Vendor corpus: `fn/string-to-codepoints.xml` (+28), `fn/min`
33
+ and `fn/max` +1 each.
34
+ - Remaining pinned QT3 reds are documented by lever in the test
35
+ harness (HOF node-carrier, map iteration order, typed grouping).
36
+
37
+
38
+ ## [1.9.308] - 2026-10-05
39
+
40
+ ### Added
41
+
42
+ - **XPath: `fn:avg` computes in the widest numeric type present
43
+ (F&O 15.2), 13 QT3 cases adopted (fn/avg 175→182, fn/sum 209→210,
44
+ fn/apply 5→10).** Every item converts to the common type before
45
+ summing: with `xs:float` present (no double) the result is
46
+ `xs:float` and every addend rounds through float32 — the lexical
47
+ of `xs:float("-3.4028235E38")` is *not* the float32's value
48
+ (fn-avg-mix-args-012..015); a new `O` typed-scalar mark
49
+ distinguishes `xs:double` from the N catch-all so float+double
50
+ promotes to `xs:double` (cbcl-avg-008); an all-integer sequence
51
+ sums exactly in int64 and divides by long division, spelling the
52
+ exact decimal where a double carrier loses digits past 2^53
53
+ (fn-avgintg2args-2/4: −84503251441487847.5). Untyped atomics
54
+ classify as double (K-SeqAVGFunc-26).
55
+
56
+ - **XQuery: `typeswitch` accepts the full `case $v as TYPE` arm
57
+ form**, binding the variable to the operand inside its arm
58
+ (entries encode `VAR\x02TYPE`). Previously only the type-only
59
+ form parsed — the binding form was a parse failure.
60
+
61
+ - **XQuery: `fn:apply` applies XDM function conversion.** An empty
62
+ array is a legal zero-arity argument list (`apply(true#0, array
63
+ {})`); node-valued members atomize to their string value
64
+ (`apply(lower-case#1, [<a>ABC</a>])` = `abc`, built-ins and
65
+ user-defined functions); a multi-item member of a literal square
66
+ array stays a SEQUENCE argument — fn:apply evaluates the member
67
+ expressions directly and the call paths expand the sub-carrier
68
+ (`fn-apply-03/07`).
69
+
70
+ - **Rust: the shared descriptor-ABI crate `leptris-descriptor`
71
+ (ruby#294 topology step 1).** `bindings/rust` is now a Cargo
72
+ workspace: the `leptris` crate keeps its name and shape, and the
73
+ new `no_std` `#[repr(C)]` mirror of the descriptor-plan ABI
74
+ (descriptor.h) is the one record layout the per-engine crate
75
+ family targets (leptris XML, yeptris YAML, teptris TOML/JSON).
76
+ The `link` feature declares the engine FFI; the authoritative
77
+ layout gate compares Rust `size_of` against the engine's own
78
+ row-size introspection plus a build/walk round trip. `build.rs`
79
+ now re-runs on `LEPTRIS_LIB_PATH` changes (the link directives
80
+ were cached, silently linking a stale library).
81
+
82
+ ### Fixed
83
+
84
+ - `fn:sum`'s duration zero answers the empty sequence in the
85
+ TYPE's canonical spelling: `PT0S` for `xs:duration` (fn-sum-8),
86
+ `P0M` kept for `xs:yearMonthDuration` (fn-sum-5).
87
+ - MSVC link failure from `__builtin_add_overflow` (portable
88
+ int64 range check), and two LSan leaks in the new avg/apply
89
+ paths — both caught by CI legs that have no local equivalent.
90
+
91
+
92
+
3
93
  ## [1.9.307] - 2026-10-05
4
94
 
5
95
  ### Fixed
@@ -52,6 +142,16 @@
52
142
  - (describe changes here)
53
143
 
54
144
 
145
+ ## [1.9.305] - 2026-10-05
146
+
147
+ <!-- Edit this section with the actual release notes. -->
148
+ <!-- See https://keepachangelog.com for format guidance. -->
149
+
150
+ ### Changed
151
+
152
+ - (describe changes here)
153
+
154
+
55
155
  ## [1.9.305] - 2026-10-05
56
156
 
57
157
  ### Performance
@@ -3,7 +3,7 @@
3
3
  cmake_minimum_required(VERSION 3.20)
4
4
 
5
5
  project(leptris
6
- VERSION 1.9.307
6
+ VERSION 1.9.309
7
7
  DESCRIPTION "Fast XML parser and XPath evaluator in pure C"
8
8
  LANGUAGES C CXX
9
9
  )
@@ -5,3 +5,10 @@ version = 4
5
5
  [[package]]
6
6
  name = "leptris"
7
7
  version = "1.4.1"
8
+ dependencies = [
9
+ "leptris-descriptor",
10
+ ]
11
+
12
+ [[package]]
13
+ name = "leptris-descriptor"
14
+ version = "0.1.0"
@@ -1,3 +1,6 @@
1
+ [workspace]
2
+ members = ["descriptor"]
3
+
1
4
  [package]
2
5
  name = "leptris"
3
6
  version = "1.4.1"
@@ -13,3 +16,4 @@ build = "build.rs"
13
16
  [dependencies]
14
17
 
15
18
  [dev-dependencies]
19
+ leptris-descriptor = { path = "descriptor", features = ["link"] }
@@ -59,3 +59,22 @@ Mirrors the Ruby (`leptris-ruby`) and Python (`pyleptris`) bindings:
59
59
  a hand-maintained FFI layer (`src/ffi.rs`) mirroring the public
60
60
  headers, with safe wrappers above it. The FFI drift gate keeps the
61
61
  mirror in sync with the C surface.
62
+
63
+ ## Workspace: the shared descriptor-ABI crate
64
+
65
+ `bindings/rust` is a Cargo workspace. Besides the `leptris crate
66
+ (unchanged name and shape — links-only, resolved via
67
+ `LEPTRIS_LIB_PATH`), it contains the `descriptor` member, published
68
+ as **`leptris-descriptor`**: the `no_std` `#[repr(C)]` mirror of the
69
+ tree-shaped descriptor-plan ABI (`src/include/leptris/descriptor.h`).
70
+ It is the one record layout the whole engine family targets — the
71
+ per-engine crates (`leptris` XML, `yeptris` YAML, `teptris`
72
+ TOML/JSON — topology decided 2026-10-03, leptris-ruby#294) — so
73
+ hosts build plan rows once for every engine. The `link` feature
74
+ declares the engine FFI for the descriptor surface; enable it on
75
+ exactly one crate in the graph.
76
+
77
+ The authoritative layout gate is `tests/descriptor_abi.rs` in the
78
+ `leptris` crate: it compares Rust `size_of` against the engine's own
79
+ `leptris_plan_*_struct_size` introspection and walks a plan built
80
+ from the mirrored rows.
@@ -10,6 +10,10 @@ use std::path::{Path, PathBuf};
10
10
  use std::{env, fs};
11
11
 
12
12
  fn main() {
13
+ // The lib path changes the emitted link directives; without
14
+ // this the build script output is cached across LEPTRIS_LIB_PATH
15
+ // changes and tests link a stale library.
16
+ println!("cargo:rerun-if-env-changed=LEPTRIS_LIB_PATH");
13
17
  // Windows: the DLL is libleptris.dll, so the import library is
14
18
  // libleptris.lib — link.exe resolves `-l <name>` as <name>.lib.
15
19
  // (TODO.concurrency/05 renamed the DLL from leptris.dll.)
@@ -0,0 +1,16 @@
1
+ [package]
2
+ name = "leptris-descriptor"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+ description = "Shared descriptor-plan ABI for the leptris engine family (leptris, yeptris, teptris) — repr(C) mirrors of the tree-shaped descriptor plan records"
6
+ license = "MIT"
7
+ repository = "https://github.com/leptris/leptris"
8
+ homepage = "https://www.leptris.org"
9
+
10
+ [features]
11
+ # Declares the engine FFI (extern "C") for the descriptor surface.
12
+ # Enable on exactly one binding crate in the dependency graph —
13
+ # the declarations carry no symbols of their own.
14
+ link = []
15
+
16
+ [dependencies]
@@ -0,0 +1,214 @@
1
+ //! Shared descriptor-plan ABI for the leptris engine family.
2
+ //!
3
+ //! One crate per engine (`leptris` XML, `yeptris` YAML, `teptris`
4
+ //! TOML/JSON) targets this record layout, so hosts build plan rows
5
+ //! once and every engine's compiled-descriptor walk speaks the same
6
+ //! shape. The types mirror `src/include/leptris/descriptor.h` in the
7
+ //! leptris engine — field order is the ABI; NEVER renumber or
8
+ //! reorder. Additive trailing fields only, guarded by an ABI version
9
+ //! bump.
10
+ //!
11
+ //! Layout is verified at runtime against the engine's own
12
+ //! introspection (`leptris_plan_*_struct_size`) by the linking
13
+ //! binding crate's tests.
14
+ //!
15
+ //! This crate is `no_std`: types and constants only, plus the FFI
16
+ //! declarations behind the `link` feature.
17
+
18
+ #![no_std]
19
+ #![allow(non_camel_case_types, non_snake_case)]
20
+
21
+ use core::ffi::c_char;
22
+
23
+ /// Must equal `leptris_plan_abi_version()` reported by the engine.
24
+ pub const PLAN_ABI_VERSION: u32 = 1;
25
+
26
+ /// Child / attribute plan kinds. Append-only.
27
+ pub type PlanKind = u8;
28
+ pub const PLAN_KIND_SCALAR: PlanKind = 1;
29
+ pub const PLAN_KIND_COLLECTION: PlanKind = 2;
30
+ pub const PLAN_KIND_NESTED: PlanKind = 3;
31
+ pub const PLAN_KIND_RAW: PlanKind = 4;
32
+ pub const PLAN_KIND_CONTENT: PlanKind = 5;
33
+ pub const PLAN_KIND_CALLBACK: PlanKind = 6;
34
+
35
+ /// Element plan flags.
36
+ pub const PLAN_FLAG_MIXED_CONTENT: u32 = 0x1;
37
+ pub const PLAN_FLAG_ORDERED: u32 = 0x2;
38
+ pub const PLAN_FLAG_CDATA: u32 = 0x4;
39
+ pub const PLAN_FLAG_NS_LENIENT: u32 = 0x8;
40
+ pub const PLAN_FLAG_EMIT_ORDER_SPINE: u32 = 0x10;
41
+
42
+ /// Namespace matching form for binding an element or attribute.
43
+ pub type PlanNsForm = u8;
44
+ pub const PLAN_NS_NONE: PlanNsForm = 0;
45
+ pub const PLAN_NS_EXACT: PlanNsForm = 1;
46
+ pub const PLAN_NS_ANY: PlanNsForm = 2;
47
+
48
+ /// Result node kinds (the value tree a walk returns).
49
+ pub type PlanValueKind = i32;
50
+ pub const PLAN_VALUE_ELEMENT: PlanValueKind = 0;
51
+ pub const PLAN_VALUE_SCALAR: PlanValueKind = 1;
52
+ pub const PLAN_VALUE_COLLECTION: PlanValueKind = 2;
53
+ pub const PLAN_VALUE_RAW: PlanValueKind = 3;
54
+ pub const PLAN_VALUE_CALLBACK: PlanValueKind = 4;
55
+
56
+ /// Attribute-match row: requires every (name, expected value) pair.
57
+ #[repr(C)]
58
+ #[derive(Clone, Copy)]
59
+ pub struct AttrPredicate {
60
+ /// XML attribute name as it appears on the wire.
61
+ pub wire_name: *const c_char,
62
+ /// String-equal match (non-null); host-owned.
63
+ pub expected_value: *const c_char,
64
+ }
65
+
66
+ /// Attribute binding row.
67
+ #[repr(C)]
68
+ #[derive(Clone, Copy)]
69
+ pub struct AttrPlan {
70
+ /// XML attribute name as it appears on the wire.
71
+ pub wire_name: *const c_char,
72
+ /// `PlanKind`: SCALAR | COLLECTION | CALLBACK.
73
+ pub kind: u8,
74
+ /// Host-defined; echoed back verbatim.
75
+ pub type_tag: u8,
76
+ /// Number of `predicates` rows (0 = historical behavior).
77
+ pub predicate_count: u16,
78
+ pub pad_pred: u16,
79
+ pub predicates: *const AttrPredicate,
80
+ /// Attribute-level namespace form; `wire_name` is the LOCAL
81
+ /// name when set. NONE (0) keeps wire-name lookup.
82
+ pub ns_form: u8,
83
+ pub pad_ns: u8,
84
+ /// `PLAN_NS_EXACT` only.
85
+ pub ns_uri: *const c_char,
86
+ }
87
+
88
+ /// Child binding row.
89
+ #[repr(C)]
90
+ #[derive(Clone, Copy)]
91
+ pub struct ChildPlan {
92
+ /// XML element name as it appears on the wire: the prefixed
93
+ /// form ("w:b") binds that literal prefix+local pair; the bare
94
+ /// local form ("b") binds by local name and lets `ns_form` do
95
+ /// the namespace work.
96
+ pub wire_name: *const c_char,
97
+ pub kind: u8,
98
+ pub type_tag: u8,
99
+ /// NESTED: index into `PlanSpec::plans`; -1 otherwise.
100
+ pub child_plan_index: i32,
101
+ /// Rule-level namespace form, consulted at child-match time.
102
+ pub ns_form: u8,
103
+ pub pad0: u8,
104
+ /// `PLAN_NS_EXACT` only.
105
+ pub ns_uri: *const c_char,
106
+ /// Element-side predicates; same semantics as `AttrPredicate`.
107
+ pub predicate_count: u16,
108
+ pub pad_pred: u16,
109
+ pub predicates: *const AttrPredicate,
110
+ }
111
+
112
+ /// One element plan: the attribute and child rows bound to a name.
113
+ #[repr(C)]
114
+ #[derive(Clone, Copy)]
115
+ pub struct ElementPlan {
116
+ /// Local name this plan binds; matching uses the CHILD row's
117
+ /// `wire_name`.
118
+ pub element_name: *const c_char,
119
+ pub ns_form: u8,
120
+ pub pad0: u8,
121
+ /// `PLAN_NS_EXACT` only.
122
+ pub ns_uri: *const c_char,
123
+ pub attribute_count: u32,
124
+ pub child_count: u32,
125
+ pub attribute_plans: *const AttrPlan,
126
+ pub child_plans: *const ChildPlan,
127
+ /// `PLAN_FLAG_*`.
128
+ pub flags: u16,
129
+ pub pad1: u16,
130
+ }
131
+
132
+ /// Top-level plan specification handed to the engine.
133
+ #[repr(C)]
134
+ #[derive(Clone, Copy)]
135
+ pub struct PlanSpec {
136
+ /// Must equal the engine's `leptris_plan_abi_version()`.
137
+ pub abi_version: u32,
138
+ /// `plans[0]` is the root plan applied to the walk context.
139
+ pub plan_count: u32,
140
+ pub plans: *const ElementPlan,
141
+ }
142
+
143
+ #[cfg(feature = "link")]
144
+ pub mod ffi {
145
+ //! Engine FFI for the descriptor surface. Enable this feature on
146
+ //! exactly one crate in the graph (the binding that links an
147
+ //! engine).
148
+
149
+ use super::PlanSpec;
150
+ use core::ffi::{c_char, c_void};
151
+
152
+ /// Opaque engine document handle (leptris: `LeptrisDocument`).
153
+ pub type DocumentHandle = *mut c_void;
154
+ /// Opaque engine element handle (leptris: `LeptrisElement`).
155
+ pub type ElementHandle = *mut c_void;
156
+ /// Status out-parameter (leptris: `LeptrisStatus*`).
157
+ pub type StatusPtr = *mut i32;
158
+ /// Opaque compiled plan.
159
+ pub type Plan = *mut c_void;
160
+ /// Opaque walk result value.
161
+ pub type PlanResult = *const c_void;
162
+
163
+ extern "C" {
164
+ pub fn leptris_plan_abi_version() -> u32;
165
+ pub fn leptris_plan_spec_struct_size() -> usize;
166
+ pub fn leptris_plan_element_row_size() -> usize;
167
+ pub fn leptris_plan_child_row_size() -> usize;
168
+ pub fn leptris_plan_attr_row_size() -> usize;
169
+ pub fn leptris_plan_predicate_row_size() -> usize;
170
+
171
+ pub fn leptris_plan_build(spec: *const PlanSpec, status: StatusPtr) -> Plan;
172
+ pub fn leptris_plan_free(plan: Plan);
173
+
174
+ pub fn leptris_plan_walk(
175
+ doc: DocumentHandle,
176
+ ctx: ElementHandle,
177
+ plan: Plan,
178
+ status: StatusPtr,
179
+ ) -> PlanResult;
180
+ pub fn leptris_plan_materialize(
181
+ source: *const c_char,
182
+ source_len: usize,
183
+ plan: Plan,
184
+ status: StatusPtr,
185
+ ) -> PlanResult;
186
+ pub fn leptris_plan_result_free(result: PlanResult);
187
+
188
+ pub fn leptris_plan_value_kind(v: PlanResult) -> i32;
189
+ pub fn leptris_plan_value_name(v: PlanResult) -> *const c_char;
190
+ pub fn leptris_plan_value_type_tag(v: PlanResult) -> u8;
191
+ pub fn leptris_plan_value_string(v: PlanResult) -> *const c_char;
192
+ pub fn leptris_plan_value_length(v: PlanResult) -> usize;
193
+ pub fn leptris_plan_value_position(v: PlanResult) -> usize;
194
+ pub fn leptris_plan_value_node_kind(v: PlanResult) -> u8;
195
+ pub fn leptris_plan_value_order_index(v: PlanResult) -> u32;
196
+ pub fn leptris_plan_value_count(v: PlanResult) -> usize;
197
+ pub fn leptris_plan_value_at(v: PlanResult, i: usize) -> PlanResult;
198
+ pub fn leptris_plan_value_children_snapshot(
199
+ v: PlanResult,
200
+ names_blob: *mut c_char,
201
+ blob_cap: usize,
202
+ name_offsets: *mut usize,
203
+ type_tags: *mut u8,
204
+ child_handles: *mut PlanResult,
205
+ ) -> usize;
206
+ pub fn leptris_plan_value_attribute(
207
+ v: PlanResult,
208
+ wire_name: *const c_char,
209
+ ) -> *const c_char;
210
+ pub fn leptris_plan_value_int(v: PlanResult, out: *mut i64) -> i32;
211
+ pub fn leptris_plan_value_float(v: PlanResult, out: *mut f64) -> i32;
212
+ pub fn leptris_plan_value_bool(v: PlanResult, out: *mut i32) -> i32;
213
+ }
214
+ }
@@ -0,0 +1,50 @@
1
+ //! Compile-time layout invariants for the descriptor ABI mirror.
2
+ //!
3
+ //! These check the crate's internal consistency (trailing-field
4
+ //! discipline, alignment) — the BINDING crate's integration test
5
+ //! verifies the sizes against the engine's own introspection, which
6
+ //! is the authoritative gate.
7
+
8
+ use leptris_descriptor::*;
9
+ use std::mem::{align_of, size_of};
10
+
11
+ #[test]
12
+ fn trailing_field_discipline() {
13
+ // Trailing additive fields must not change the size when zeroed:
14
+ // zero-extended aggregate initializers stay valid across ABI
15
+ // additions. repr(C) + these sizes are the contract.
16
+ assert_eq!(size_of::<PlanSpec>(), 4 + 4 + size_of::<*const u8>());
17
+ assert_eq!(align_of::<PlanSpec>(), align_of::<*const u8>());
18
+ }
19
+
20
+ #[test]
21
+ fn kind_and_flag_constants_match_engine_headers() {
22
+ assert_eq!(PLAN_ABI_VERSION, 1);
23
+ assert_eq!(PLAN_KIND_SCALAR, 1);
24
+ assert_eq!(PLAN_KIND_COLLECTION, 2);
25
+ assert_eq!(PLAN_KIND_NESTED, 3);
26
+ assert_eq!(PLAN_KIND_RAW, 4);
27
+ assert_eq!(PLAN_KIND_CONTENT, 5);
28
+ assert_eq!(PLAN_KIND_CALLBACK, 6);
29
+ assert_eq!(PLAN_FLAG_MIXED_CONTENT, 0x1);
30
+ assert_eq!(PLAN_FLAG_ORDERED, 0x2);
31
+ assert_eq!(PLAN_FLAG_CDATA, 0x4);
32
+ assert_eq!(PLAN_FLAG_NS_LENIENT, 0x8);
33
+ assert_eq!(PLAN_FLAG_EMIT_ORDER_SPINE, 0x10);
34
+ assert_eq!(PLAN_NS_NONE, 0);
35
+ assert_eq!(PLAN_NS_EXACT, 1);
36
+ assert_eq!(PLAN_NS_ANY, 2);
37
+ assert_eq!(PLAN_VALUE_ELEMENT, 0);
38
+ assert_eq!(PLAN_VALUE_SCALAR, 1);
39
+ assert_eq!(PLAN_VALUE_COLLECTION, 2);
40
+ assert_eq!(PLAN_VALUE_RAW, 3);
41
+ assert_eq!(PLAN_VALUE_CALLBACK, 4);
42
+ }
43
+
44
+ #[test]
45
+ fn row_layouts_are_pointer_sized_aligned() {
46
+ assert_eq!(align_of::<AttrPredicate>(), align_of::<*const u8>());
47
+ assert_eq!(align_of::<AttrPlan>(), align_of::<*const u8>());
48
+ assert_eq!(align_of::<ChildPlan>(), align_of::<*const u8>());
49
+ assert_eq!(align_of::<ElementPlan>(), align_of::<*const u8>());
50
+ }