leptris 1.9.306.0 → 1.9.308.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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -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 +107 -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/dom/element_modify.c +160 -30
  27. data/vendor-src/libleptris/src/leptris/xpath/evaluator_operators.c +175 -16
  28. data/vendor-src/libleptris/src/leptris/xpath/evaluator_types.c +6 -4
  29. data/vendor-src/libleptris/src/leptris/xpath/functions.c +37 -3
  30. data/vendor-src/libleptris/src/leptris/xpath/functions_ext31.c +395 -28
  31. data/vendor-src/libleptris/src/leptris/xpath/parser.c +36 -1
  32. data/vendor-src/libleptris/src/leptris/xpath/xpath_public.c +1 -1
  33. data/vendor-src/libleptris/test/abi/test_dom_mutation_bugs.cpp +253 -0
  34. data/vendor-src/libleptris/test/xquery/test_qt3.cpp +3 -10
  35. data/vendor-src/libleptris/vcpkg.json +1 -1
  36. metadata +5 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d1bcc695e02ca89b9081d35ea441a4a720219633d0ce010f0d06727cb79bab15
4
- data.tar.gz: 38ea48f33b444754fdc3e73dba84645e6d8c95c8178a819e7bae07ace867df5f
3
+ metadata.gz: 7ef02878e818136f9074d6bdeeb54a55db40d0d6287974a96c81979afe7ed525
4
+ data.tar.gz: 23468d7fdb2db0aae806d90c0d4d312a9190fe2c69f28333ef202a125c504675
5
5
  SHA512:
6
- metadata.gz: 7bd7934e1bbc72620d14a66aa344ad786669772bfe4ce5541daa0ccd2a7cb668f271bd79ba4e8647e76ce50a178fc43ae3edeed24d7ef39ab1bee8f348e6af50
7
- data.tar.gz: 98d8025f8aab98735ef23955b4ddeb2f474aa17c0f4aea23768292ca99205452b3d8f99f74c63373d405895925620f527aa29d70a549e0b1410592b8ae8ded00
6
+ metadata.gz: 25aa1f2055a658fbbafdf9031d765311c6a83de1bdbc61d4535a755ae103315e49c4cd99531faa5db5389c2c889175dde051747c86411e278999083c442d161f
7
+ data.tar.gz: 1a5b9875f8fb26486d82e19ad6f70d0a76ce8cb5128e2b7e1fffa5f6c3ac284085199e067e6dc97aa6d2ecbc67909df490d055dd8728b55bf4e8e4179d4073b3
data/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [1.9.307.0] - 2026-10-05
2
+
3
+ ### Fixed
4
+
5
+ - **Cross-document LEAF adoption (libleptris 1.9.307, engine
6
+ #1534 — completing the #1528 fix)**: the #1528 adoption gate
7
+ covered ELEMENT splice arguments only — a TEXT/CDATA/COMMENT/PI
8
+ node from another document was still spliced raw, so appending
9
+ scratch-document leaves (the `add_child(String)` fragment path's
10
+ text children) and freeing the scratch left the tree pointing
11
+ into freed memory. The four splice seams now adopt non-element
12
+ arguments by copy too; same-document moves stay zero-copy.
13
+ Leaf-adoption specs pin the free-then-walk shape.
14
+
1
15
  ## 1.9.306.0 — 2026-10-05
2
16
 
3
17
  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.306"
11
+ LIBLEPTRIS_VERSION = "1.9.308"
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.306.0"
4
+ VERSION = "1.9.308.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.306
4
+ - libleptris v1.9.308
5
5
  - utf8proc v2.11.0
6
6
 
7
7
  Rebuild by hand:
@@ -1,5 +1,102 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.9.308] - 2026-10-05
4
+
5
+ ### Added
6
+
7
+ - **XPath: `fn:avg` computes in the widest numeric type present
8
+ (F&O 15.2), 13 QT3 cases adopted (fn/avg 175→182, fn/sum 209→210,
9
+ fn/apply 5→10).** Every item converts to the common type before
10
+ summing: with `xs:float` present (no double) the result is
11
+ `xs:float` and every addend rounds through float32 — the lexical
12
+ of `xs:float("-3.4028235E38")` is *not* the float32's value
13
+ (fn-avg-mix-args-012..015); a new `O` typed-scalar mark
14
+ distinguishes `xs:double` from the N catch-all so float+double
15
+ promotes to `xs:double` (cbcl-avg-008); an all-integer sequence
16
+ sums exactly in int64 and divides by long division, spelling the
17
+ exact decimal where a double carrier loses digits past 2^53
18
+ (fn-avgintg2args-2/4: −84503251441487847.5). Untyped atomics
19
+ classify as double (K-SeqAVGFunc-26).
20
+
21
+ - **XQuery: `typeswitch` accepts the full `case $v as TYPE` arm
22
+ form**, binding the variable to the operand inside its arm
23
+ (entries encode `VAR\x02TYPE`). Previously only the type-only
24
+ form parsed — the binding form was a parse failure.
25
+
26
+ - **XQuery: `fn:apply` applies XDM function conversion.** An empty
27
+ array is a legal zero-arity argument list (`apply(true#0, array
28
+ {})`); node-valued members atomize to their string value
29
+ (`apply(lower-case#1, [<a>ABC</a>])` = `abc`, built-ins and
30
+ user-defined functions); a multi-item member of a literal square
31
+ array stays a SEQUENCE argument — fn:apply evaluates the member
32
+ expressions directly and the call paths expand the sub-carrier
33
+ (`fn-apply-03/07`).
34
+
35
+ - **Rust: the shared descriptor-ABI crate `leptris-descriptor`
36
+ (ruby#294 topology step 1).** `bindings/rust` is now a Cargo
37
+ workspace: the `leptris` crate keeps its name and shape, and the
38
+ new `no_std` `#[repr(C)]` mirror of the descriptor-plan ABI
39
+ (descriptor.h) is the one record layout the per-engine crate
40
+ family targets (leptris XML, yeptris YAML, teptris TOML/JSON).
41
+ The `link` feature declares the engine FFI; the authoritative
42
+ layout gate compares Rust `size_of` against the engine's own
43
+ row-size introspection plus a build/walk round trip. `build.rs`
44
+ now re-runs on `LEPTRIS_LIB_PATH` changes (the link directives
45
+ were cached, silently linking a stale library).
46
+
47
+ ### Fixed
48
+
49
+ - `fn:sum`'s duration zero answers the empty sequence in the
50
+ TYPE's canonical spelling: `PT0S` for `xs:duration` (fn-sum-8),
51
+ `P0M` kept for `xs:yearMonthDuration` (fn-sum-5).
52
+ - MSVC link failure from `__builtin_add_overflow` (portable
53
+ int64 range check), and two LSan leaks in the new avg/apply
54
+ paths — both caught by CI legs that have no local equivalent.
55
+
56
+
57
+
58
+ ## [1.9.307] - 2026-10-05
59
+
60
+ ### Fixed
61
+
62
+ - **DOM: cross-document leaf splices are adopted by copy (#1534,
63
+ follow-up to #1528).** The #1528 adoption gate covered ELEMENT
64
+ splice arguments only — a TEXT/CDATA/COMMENT/PI node arriving from
65
+ a different document was still spliced raw, so replacing an
66
+ element with a scratch-document leaf and then freeing the scratch
67
+ document (the `Document.parse` cleanup lifecycle) left the tree
68
+ pointing into freed memory; serialize → re-parse round trips
69
+ failed with "malformed input". `append_child` / `prepend_child` /
70
+ `insert_before` / `insert_after` now dispatch non-element
71
+ arguments through the #1320 owner-doc stamps and copy by kind;
72
+ same-document moves and unstamped detached chains keep their
73
+ zero-copy / link-only behavior, and the append fast path is
74
+ untouched (element arguments take the unchanged mut-block bail).
75
+
76
+ - **DOM: `leptris_element_remove_child` walks and relinks the child
77
+ chain with type-safe accessors (#1534).** The element-filtered
78
+ getters it used skip non-element siblings, so removing `<a>` from
79
+ `[<a>, text]` read the next sibling as NULL and set `first_child`
80
+ to NULL — orphaning the leaf tail (the #1220 class; same-document
81
+ chains lose plain text too: `<r><a/>t</r>` minus `<a>` serialized
82
+ as `<r/>`). The walker now uses the node-form accessors, clears
83
+ the removed child's parent through the per-kind setter, and only
84
+ decrements `child_count` for element victims (issue #213
85
+ semantics: it counts element children only). Removing a
86
+ non-element child cast through the ABI-stable `LeptrisElement`
87
+ signature (same convention as `insert_before`) now works.
88
+
89
+ ### Tests
90
+
91
+ - New `CrossDocumentLeafAdoption` suite: the exact issue pipeline —
92
+ scratch leaf splice → scratch free → serialize → RE-PARSE — for
93
+ text and the other leaf kinds, plus the append-only variant.
94
+ - New `RemoveChildMixedChain` suite: every position of a removed
95
+ child against non-element siblings, direct text-child removal,
96
+ child_count semantics, and the non-child rejection.
97
+
98
+
99
+
3
100
  ## [1.9.306] - 2026-10-05
4
101
 
5
102
  <!-- Edit this section with the actual release notes. -->
@@ -10,6 +107,16 @@
10
107
  - (describe changes here)
11
108
 
12
109
 
110
+ ## [1.9.305] - 2026-10-05
111
+
112
+ <!-- Edit this section with the actual release notes. -->
113
+ <!-- See https://keepachangelog.com for format guidance. -->
114
+
115
+ ### Changed
116
+
117
+ - (describe changes here)
118
+
119
+
13
120
  ## [1.9.305] - 2026-10-05
14
121
 
15
122
  ### Performance
@@ -3,7 +3,7 @@
3
3
  cmake_minimum_required(VERSION 3.20)
4
4
 
5
5
  project(leptris
6
- VERSION 1.9.306
6
+ VERSION 1.9.308
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
+ }