leptris 1.9.333.0-arm-linux → 1.9.334.0-arm-linux

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e7fb374110980f3dae43c0ee0d7f6dbf883db4e9e971c8bac8486f9da34f497b
4
- data.tar.gz: ee1da2b6bda8ddf0265f1fa07e28ceedff83c40980193ffbeeebc718c4ceef84
3
+ metadata.gz: cd2cbe214147c78280cf50d20d970f9e66ab0d4348651327e75e3dbd8cfac964
4
+ data.tar.gz: 0ccd0d2b41ed6cec2f5af1bacdc944de14bca5a5a4b4dcdbd8729ae610d291ac
5
5
  SHA512:
6
- metadata.gz: e705f7c8c5038486c32dc099d9867656b0283822c079822a045663202940d22b84c350d6c50564a1913332846d89e2da85207cfd58b547ad4eea7e9771c8e4c3
7
- data.tar.gz: 1490690097937f74123198a36f011b740247ee1f2c2e7ce41bb635810e61fbb78ffd6c8d47dbcf3573f5f749928802eca88a83004b4a81b30f3333969767e5fb
6
+ metadata.gz: 41ee708352f94d4795bf2066961e335eb16e8d537b0afd5af1cc63785ae5d5c9735600677fee0fd609986d4c9f591c711233de345396999771fc59c27f90ab59
7
+ data.tar.gz: 82c5ddfa78c6051e4431a59f84f613d3320ec7bd30dfc3b168d8135dda25e58ca9ee107f8c9a0b03fb55df1808054080740c5990ae0a235ec3f8cc444550e489
data/CHANGELOG.md CHANGED
@@ -1,3 +1,34 @@
1
+ ## [1.9.334.0] - 2026-10-10
2
+
3
+ ### Added — engine sync (libleptris 1.9.332-1.9.334)
4
+
5
+ - **Descriptor#build_result — serialize-side construction
6
+ (#408's face)**: `leptris_plan_result_build` builds a result
7
+ tree from row ops (:element structural opens, :scalar nesting
8
+ inside its producing row's element implicitly, :attr by name,
9
+ :end closes) that `#serialize` renders — the reverse of #walk,
10
+ byte-identical round-trip pinned. `leptris_element_add_child_addr`
11
+ attached (the #408 address-based attach; sugar is #408's design
12
+ work). Audit 384/383.
13
+ - **XSD 1.1 tier 2 (1.9.332)**: `xs:assert` on complexTypes
14
+ (XPath 2 lane, per-instance), `xs:assertion` on simpleTypes,
15
+ `xs:openContent` — `xs:assert` pinned.
16
+ - **Completion wave (1.9.334)**: `xs:all` order-free with foreign
17
+ children rejected; strict validation of undeclared elements
18
+ and attributes — both pinned.
19
+
20
+ ### Fixed
21
+
22
+ - **#1615 resolved as a malformed repro**: the well-formed mixed
23
+ union (memberTypes + inline member) validates correctly on
24
+ both paths — my filed schema was missing a `</xs:restriction>`
25
+ close. On 1.9.334 the malformed schema compiles to an
26
+ error-carrying handle ("schema document is not well-formed
27
+ XML") and fails validation CLOSED (it over-accepted silently
28
+ on 1.9.331 — the real defect, fixed engine-side by their
29
+ regression pin). Both behaviors spec-pinned; leptris#1615
30
+ closed with the correction.
31
+
1
32
  ## [1.9.331.0] - 2026-10-10
2
33
 
3
34
  ### Added — engine sync (libleptris 1.9.325-1.9.331)
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.333"
11
+ LIBLEPTRIS_VERSION = "1.9.334"
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
@@ -31,6 +31,22 @@ CMAKE_FLAGS = %w[
31
31
  require "tmpdir"
32
32
 
33
33
  desc "Build libleptris #{LIBLEPTRIS_VERSION} (+ utf8proc) from release tarballs into lib/"
34
+ # leptris#1623: macOS 14's dyld intermittently SIGKILLs
35
+ # linker-signed adhoc images at load ("CODESIGNING Invalid Page" —
36
+ # the same kill the atomic-replace dance below already guards in
37
+ # dev). The linker's implicit signature is the bare minimum; an
38
+ # explicit codesign at package time writes a complete, deterministic
39
+ # CodeDirectory that survives the gem pipeline (cp, packaging,
40
+ # install). Re-sign every shipped darwin binary and verify it.
41
+ def sign_darwin_binaries(*bins)
42
+ return unless RUBY_PLATFORM =~ /darwin/
43
+
44
+ bins.flatten.compact.each do |bin|
45
+ sh "codesign --force --sign - #{bin}"
46
+ sh "codesign --verify #{bin}"
47
+ end
48
+ end
49
+
34
50
  task :compile do
35
51
  # GitHub tarball downloads intermittently return a non-gzip body
36
52
  # (rate-limit/redirect HTML) — the piped curl|tar form then fails
@@ -256,6 +272,7 @@ task :compile do
256
272
  Dir.glob("#{u8_prefix}/{lib,bin}/utf8proc.dll").first
257
273
  raise "utf8proc shared library not found after build" unless u8_lib
258
274
  cp(u8_lib, "lib/#{File.basename(u8_lib)}")
275
+ sign_darwin_binaries("lib/libleptris#{ext}", "lib/#{File.basename(u8_lib)}")
259
276
  puts "Vendored #{File.basename(lib)} + #{File.basename(u8_lib)} into lib/"
260
277
 
261
278
  # Opt-in native read layer ext (#185): built and vendored beside
@@ -319,6 +336,7 @@ task :compile do
319
336
  dest = "lib/leptris/xml/#{File.basename(bundle)}"
320
337
  cp(bundle, "#{dest}.new")
321
338
  mv("#{dest}.new", dest)
339
+ sign_darwin_binaries(dest)
322
340
  puts "Vendored native layer (#{File.basename(bundle)}) into lib/leptris/xml/"
323
341
  end
324
342
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Leptris
4
- VERSION = "1.9.333.0"
4
+ VERSION = "1.9.334.0"
5
5
  end
@@ -347,6 +347,61 @@ class Leptris::XML::Descriptor
347
347
  owner: true, plans: @plans, plan: @plans[0])
348
348
  end
349
349
 
350
+ # Serialize-side construction (leptris_plan_result_build): a
351
+ # result tree from +ops+ — hashes of {kind: :element / :scalar /
352
+ # :attr / :end, plan_index:, row_index:, name:, value:} — that
353
+ # #serialize renders through this plan. Encoding: an :element op
354
+ # opens a structural element (row_index UINT32_MAX = the root);
355
+ # a :scalar op carries its PRODUCING row and nests inside that
356
+ # row's element implicitly; :attr attaches by name; :end closes.
357
+ # Returns a root-owned PlanValue.
358
+ ROW_OP_KINDS = {
359
+ scalar: Leptris::XML::FFI::PLAN_OP_SCALAR,
360
+ element: Leptris::XML::FFI::PLAN_OP_ELEMENT,
361
+ attr: Leptris::XML::FFI::PLAN_OP_ATTR,
362
+ end: Leptris::XML::FFI::PLAN_OP_END,
363
+ }.freeze
364
+ private_constant :ROW_OP_KINDS
365
+
366
+ def build_result(ops)
367
+ memory = ::FFI::MemoryPointer.new(Leptris::XML::FFI::PlanRowOp, ops.size)
368
+ anchors = [memory]
369
+ ops.each_with_index do |op, i|
370
+ row = Leptris::XML::FFI::PlanRowOp.new(memory[i])
371
+ kind = ROW_OP_KINDS.fetch(op.fetch(:kind)) do
372
+ raise ArgumentError,
373
+ "op kind must be one of #{ROW_OP_KINDS.keys.inspect}"
374
+ end
375
+ value = op[:value]
376
+ row[:kind] = kind
377
+ row[:plan_index] = op.fetch(:plan_index, 0)
378
+ row[:row_index] = op.fetch(:row_index, 0xFFFF_FFFF)
379
+ row[:name] =
380
+ if (n = op[:name])
381
+ anchors << (ptr = ::FFI::MemoryPointer.from_string(n.to_s))
382
+ ptr
383
+ end
384
+ if value.nil?
385
+ row[:value] = nil
386
+ row[:value_len] = 0
387
+ else
388
+ s = value.to_s
389
+ anchors << (ptr = ::FFI::MemoryPointer.from_string(s))
390
+ row[:value] = ptr
391
+ row[:value_len] = s.bytesize
392
+ end
393
+ end
394
+ status = ::FFI::MemoryPointer.new(:int)
395
+ raw = Leptris::XML::FFI.leptris_plan_result_build(
396
+ @handle, memory, ops.size, status)
397
+ if raw.null?
398
+ raise Leptris::XML::Error,
399
+ "plan result build failed (status=#{status.read_int})"
400
+ end
401
+ Leptris::XML::PlanValue.new(ResultHandle.new(raw),
402
+ owner: true, plans: @plans, plan: @plans[0])
403
+ end
404
+
350
405
  # libleptris 1.9.312 (#1551): serialize a walk result back to
351
406
  # XML guided by this plan — the plan supplies element wrappers
352
407
  # (row wire_name + ns_prefix), the result the content; children
@@ -418,6 +418,26 @@ module Leptris
418
418
  :leptris_plan_result
419
419
  attach_function :leptris_plan_result_free,
420
420
  [:leptris_plan_result], :void
421
+ # libleptris 1.9.324+: construct a result tree from row ops
422
+ # (the serialize-side fast path — the reverse of the walk):
423
+ # ELEMENT opens under the current parent, SCALAR/ATTR carry
424
+ # values, END closes. Strings are copied at build; free with
425
+ # leptris_plan_result_free (the ResultHandle does).
426
+ class PlanRowOp < ::FFI::Struct
427
+ layout :kind, :int,
428
+ :plan_index, :uint32,
429
+ :row_index, :uint32,
430
+ :name, :pointer,
431
+ :value, :pointer,
432
+ :value_len, :size_t
433
+ end
434
+ PLAN_OP_SCALAR = 0
435
+ PLAN_OP_ELEMENT = 1
436
+ PLAN_OP_ATTR = 2
437
+ PLAN_OP_END = 3
438
+ attach_function :leptris_plan_result_build,
439
+ [:leptris_plan, :pointer, :size_t, :pointer], :leptris_plan_result
440
+
421
441
  # libleptris 1.9.312 (#1551): serialize a walk result back
422
442
  # to XML guided by its plan — the plan supplies element
423
443
  # wrappers (row wire_name + ns_prefix), the result the
@@ -858,6 +878,11 @@ attach_function :leptris_parse_string,
858
878
  [:leptris_element, :string], :leptris_status
859
879
  attach_function :leptris_element_append_child,
860
880
  [:leptris_element, :leptris_element], :leptris_status
881
+ # #408: address-based attach — bindings pass raw addresses
882
+ # and skip the per-node FFI pointer-wrapper mint. Same
883
+ # semantics and status contract as the pointer face.
884
+ attach_function :leptris_element_add_child_addr,
885
+ [:ulong_long, :ulong_long], :leptris_status
861
886
  # Fused create+append (libleptris 1.9.153): one document
862
887
  # resolution, identical tree semantics to the two-call pair.
863
888
  attach_function :leptris_element_create_child,
data/lib/libleptris.so CHANGED
Binary file
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.333
4
+ - libleptris v1.9.334
5
5
  - utf8proc v2.11.0
6
6
 
7
7
  Rebuild by hand:
@@ -1,5 +1,34 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.9.334] - 2026-10-10
4
+
5
+ ### Added
6
+
7
+ - **XSD completion wave (#1075, PR #1620).** `xs:all` at the model
8
+ root is order-free now (counting check: each member at most
9
+ maxOccurs, minOccurs floors, foreign children rejected) — it
10
+ previously enforced declaration order. Strict validation:
11
+ undeclared elements (including the document root) and undeclared
12
+ attributes fail with `not declared (strict)`, unless the type
13
+ captures `xs:anyAttribute`, whose namespace grammar
14
+ (`##any`/`##other`/`##targetNamespace`/`##local`/URI list) is
15
+ enforced. Unresolved named types are loud — `type not found in
16
+ the schema` — closing the silent-accept class behind #1615.
17
+ `$value` binds typed: the simple type's numeric chain decides
18
+ number-vs-string, not the lexical's shape. Eight red-first
19
+ specs; zero fallout in existing pins.
20
+ - **Native plan-result builder + address-based attach (leptris-
21
+ ruby#408 asks, PR #1621).** `leptris_plan_result_build` builds
22
+ the standalone result tree from a flat op program —
23
+ ELEMENT/SCALAR/ATTR/END, stamped exactly as a walk stamps it —
24
+ so `leptris_plan_serialize` serves standalone serialization with
25
+ zero DOM mint on the host (the to_xml → to_json-floor path).
26
+ `leptris_element_add_child_addr` attaches by raw addresses,
27
+ dropping the per-node FFI pointer-wrapper mint (~2 allocs/row).
28
+ The Ruby-side wiring rides the binding repo.
29
+
30
+
31
+
3
32
  ## [1.9.333] - 2026-10-10
4
33
 
5
34
  ### Added
@@ -3,7 +3,7 @@
3
3
  cmake_minimum_required(VERSION 3.20)
4
4
 
5
5
  project(leptris
6
- VERSION 1.9.333
6
+ VERSION 1.9.334
7
7
  DESCRIPTION "Fast XML parser and XPath evaluator in pure C"
8
8
  LANGUAGES C CXX
9
9
  )
@@ -259,6 +259,36 @@ LEPTRIS_API LeptrisPlanResult leptris_plan_walk(LeptrisDocument doc,
259
259
  LeptrisStatus* status);
260
260
  LEPTRIS_API void leptris_plan_result_free(LeptrisPlanResult result);
261
261
 
262
+ /* #408 ask 2: build a standalone result natively from a flat op
263
+ * program — the same tree a walk produces (wrappers, ns bindings,
264
+ * kid order), so leptris_plan_serialize serves standalone
265
+ * serialization with no DOM mint on the host side. ELEMENT ops
266
+ * reference the plan row that captured the member (plan_index +
267
+ * row_index; the walk root uses row_index = UINT32_MAX); SCALAR
268
+ * and ATTR carry the value; END pops the open element. Programs
269
+ * must balance (every ELEMENT closed; no END without an open
270
+ * element) and open with the root element op. Strings are copied.
271
+ * Memory: free with leptris_plan_result_free. */
272
+ typedef enum {
273
+ LEPTRIS_PLAN_OP_SCALAR = 0,
274
+ LEPTRIS_PLAN_OP_ELEMENT = 1,
275
+ LEPTRIS_PLAN_OP_ATTR = 2,
276
+ LEPTRIS_PLAN_OP_END = 3
277
+ } LeptrisPlanRowOpKind;
278
+
279
+ typedef struct {
280
+ LeptrisPlanRowOpKind kind;
281
+ uint32_t plan_index; /* ELEMENT: the plan whose row captured it */
282
+ uint32_t row_index; /* producing row (UINT32_MAX = root) */
283
+ const char* name; /* ATTR: attribute name */
284
+ const char* value; /* SCALAR/ATTR: value (NULL = empty) */
285
+ size_t value_len;
286
+ } leptris_plan_row_op;
287
+
288
+ LEPTRIS_API LeptrisPlanResult leptris_plan_result_build(
289
+ LeptrisPlan plan, const leptris_plan_row_op* ops, size_t count,
290
+ LeptrisStatus* status);
291
+
262
292
  /* #1551: serialize a walk result back to XML, guided by its plan.
263
293
  * The plan supplies element wrappers (row wire_name + ns_prefix)
264
294
  * and the result supplies content; children emit in document order
@@ -2153,6 +2153,11 @@ LEPTRIS_API LeptrisStatus leptris_element_set_name(LeptrisElement elem, const ch
2153
2153
  * @param child Child element to append
2154
2154
  * @return LEPTRIS_OK on success, error code otherwise
2155
2155
  */
2156
+ /* #408: address-based attach — bindings pass raw addresses and
2157
+ * skip the per-node FFI pointer-wrapper mint. Same semantics and
2158
+ * status contract as leptris_element_append_child. */
2159
+ LEPTRIS_API LeptrisStatus leptris_element_add_child_addr(uintptr_t parent_addr,
2160
+ uintptr_t child_addr);
2156
2161
  LEPTRIS_API LeptrisStatus leptris_element_append_child(LeptrisElement parent, LeptrisElement child);
2157
2162
 
2158
2163
  /**
@@ -1530,6 +1530,150 @@ LEPTRIS_API char* leptris_plan_serialize(LeptrisPlan plan,
1530
1530
  return b.s;
1531
1531
  }
1532
1532
 
1533
+ LEPTRIS_API LeptrisPlanResult leptris_plan_result_build(
1534
+ LeptrisPlan plan, const leptris_plan_row_op* ops, size_t count,
1535
+ LeptrisStatus* status) {
1536
+ if (status) *status = LEPTRIS_OK;
1537
+ if (!plan || !ops || count == 0) {
1538
+ if (status) *status = LEPTRIS_ERROR_INVALID_ARG;
1539
+ return NULL;
1540
+ }
1541
+ const struct leptris_plan* pool =
1542
+ (const struct leptris_plan*)plan;
1543
+ /* op stack of open elements; depth is program-bounded */
1544
+ struct leptris_plan_result** stack =
1545
+ (struct leptris_plan_result**)calloc(count,
1546
+ sizeof(*stack));
1547
+ if (!stack) {
1548
+ if (status) *status = LEPTRIS_ERROR_MEMORY;
1549
+ return NULL;
1550
+ }
1551
+ int sp = 0;
1552
+ struct leptris_plan_result* root = NULL;
1553
+ for (size_t i = 0; i < count; i++) {
1554
+ const leptris_plan_row_op* op = &ops[i];
1555
+ if (op->kind == LEPTRIS_PLAN_OP_ELEMENT) {
1556
+ if (op->plan_index >= pool->plan_count ||
1557
+ (op->row_index != UINT32_MAX &&
1558
+ op->row_index >= pool->plans[op->plan_index]
1559
+ .child_count)) {
1560
+ free(stack);
1561
+ if (status) *status = LEPTRIS_ERROR_INVALID_ARG;
1562
+ return NULL;
1563
+ }
1564
+ struct leptris_plan_result* v = dp_value_new(
1565
+ LEPTRIS_PLAN_VALUE_ELEMENT);
1566
+ if (!v) goto oom;
1567
+ const dp_plan* cp = &pool->plans[op->plan_index];
1568
+ const char* wire =
1569
+ op->row_index == UINT32_MAX
1570
+ ? cp->element_name
1571
+ : cp->child_plans[op->row_index].wire_name;
1572
+ v->name = dp_strdup(wire);
1573
+ v->container_plan = op->plan_index;
1574
+ v->row_index = op->row_index;
1575
+ if (op->row_index != UINT32_MAX) {
1576
+ const leptris_child_plan* cr =
1577
+ &cp->child_plans[op->row_index];
1578
+ v->ns_prefix = dp_strdup(cr->ns_prefix);
1579
+ v->ns_uri = dp_strdup(cr->ns_uri);
1580
+ }
1581
+ if (sp == 0) {
1582
+ if (root) { /* multiple roots */
1583
+ dp_result_free_rec(v);
1584
+ free(stack);
1585
+ if (status) *status = LEPTRIS_ERROR_INVALID_ARG;
1586
+ return NULL;
1587
+ }
1588
+ root = v;
1589
+ } else if (!dp_value_push(stack[sp - 1], v)) {
1590
+ dp_result_free_rec(v);
1591
+ goto oom;
1592
+ }
1593
+ v->order_index = (uint32_t)
1594
+ (sp == 0 ? 0 : stack[sp - 1]->kid_count);
1595
+ stack[sp++] = v;
1596
+ } else if (op->kind == LEPTRIS_PLAN_OP_SCALAR) {
1597
+ if (sp == 0) {
1598
+ free(stack);
1599
+ if (status) *status = LEPTRIS_ERROR_INVALID_ARG;
1600
+ return NULL;
1601
+ }
1602
+ struct leptris_plan_result* v =
1603
+ dp_value_new(LEPTRIS_PLAN_VALUE_SCALAR);
1604
+ if (!v) goto oom;
1605
+ const dp_plan* cp = &pool->plans[op->plan_index];
1606
+ if (op->row_index < cp->child_count)
1607
+ v->name = dp_strdup(
1608
+ cp->child_plans[op->row_index].wire_name);
1609
+ if (!dp_value_set_str(v, op->value ? op->value : "",
1610
+ op->value ? op->value_len : 0))
1611
+ dp_value_set_str(v, "", 0);
1612
+ if (!dp_value_push(stack[sp - 1], v)) {
1613
+ dp_result_free_rec(v);
1614
+ goto oom;
1615
+ }
1616
+ } else if (op->kind == LEPTRIS_PLAN_OP_ATTR) {
1617
+ if (sp == 0 || !op->name) {
1618
+ free(stack);
1619
+ if (status) *status = LEPTRIS_ERROR_INVALID_ARG;
1620
+ return NULL;
1621
+ }
1622
+ struct leptris_plan_result* el = stack[sp - 1];
1623
+ char** gn = (char**)realloc(
1624
+ el->attr_names,
1625
+ (el->attr_count + 1) * sizeof(char*));
1626
+ char** gv = (char**)realloc(
1627
+ el->attr_values,
1628
+ (el->attr_count + 1) * sizeof(char*));
1629
+ char** gp = (char**)realloc(
1630
+ el->attr_ns_prefixes,
1631
+ (el->attr_count + 1) * sizeof(char*));
1632
+ char** gu = (char**)realloc(
1633
+ el->attr_ns_uris,
1634
+ (el->attr_count + 1) * sizeof(char*));
1635
+ if (!gn || !gv || !gp || !gu) {
1636
+ free(gn); free(gv); free(gp); free(gu);
1637
+ goto oom;
1638
+ }
1639
+ el->attr_names = gn;
1640
+ el->attr_values = gv;
1641
+ el->attr_ns_prefixes = gp;
1642
+ el->attr_ns_uris = gu;
1643
+ gn[el->attr_count] = dp_strdup(op->name);
1644
+ gv[el->attr_count] =
1645
+ dp_strdup_n(op->value ? op->value : "",
1646
+ op->value ? op->value_len : 0);
1647
+ gp[el->attr_count] = NULL;
1648
+ gu[el->attr_count] = NULL;
1649
+ if (!gn[el->attr_count] || !gv[el->attr_count])
1650
+ goto oom;
1651
+ el->attr_count++;
1652
+ } else if (op->kind == LEPTRIS_PLAN_OP_END) {
1653
+ if (sp == 0) {
1654
+ free(stack);
1655
+ if (status) *status = LEPTRIS_ERROR_INVALID_ARG;
1656
+ return NULL;
1657
+ }
1658
+ sp--;
1659
+ }
1660
+ }
1661
+ int balanced = (sp == 0 && root != NULL);
1662
+ free(stack);
1663
+ if (!balanced) {
1664
+ dp_result_free_rec(root);
1665
+ if (status) *status = LEPTRIS_ERROR_INVALID_ARG;
1666
+ return NULL;
1667
+ }
1668
+ return (LeptrisPlanResult)root;
1669
+
1670
+ oom:
1671
+ if (root) dp_result_free_rec(root);
1672
+ free(stack);
1673
+ if (status) *status = LEPTRIS_ERROR_MEMORY;
1674
+ return NULL;
1675
+ }
1676
+
1533
1677
  LEPTRIS_API void leptris_plan_result_free(LeptrisPlanResult result) {
1534
1678
  dp_result_free_rec((struct leptris_plan_result*)result);
1535
1679
  }
@@ -493,6 +493,13 @@ static LeptrisNode* adopt_for_splice(LeptrisNode* node,
493
493
  return copy ? (LeptrisNode*)copy : NULL;
494
494
  }
495
495
 
496
+ LeptrisStatus leptris_element_add_child_addr(uintptr_t parent_addr,
497
+ uintptr_t child_addr) {
498
+ if (!parent_addr || !child_addr) return LEPTRIS_ERROR_NULL_ARG;
499
+ return leptris_element_append_child((LeptrisElement)parent_addr,
500
+ (LeptrisElement)child_addr);
501
+ }
502
+
496
503
  LeptrisStatus leptris_element_append_child(LeptrisElement parent, LeptrisElement child) {
497
504
  if (!parent || !child) return LEPTRIS_ERROR_NULL_ARG;
498
505
 
@@ -79,13 +79,15 @@ static char* xsd_read_file(const char* path, size_t* out_len);
79
79
  static XsdAttrDecl* xsd_copy_attr_rows(const XsdAttrDecl* src);
80
80
  static void xsd_register_complex(struct leptris_xsd_schema* s,
81
81
  XsdCm* root, XsdAttrDecl* attrs,
82
- const char* name);
82
+ const char* name,
83
+ char* any_attr_ns);
83
84
  static XsdCm* xsd_capture_complex_model(struct leptris_xsd_schema* s,
84
85
  LeptrisElement ct,
85
86
  const char* name);
86
87
  static XsdAssert* xsd_capture_assert(struct leptris_xsd_schema* s,
87
88
  LeptrisElement e);
88
89
  static XsdCm* xsd_capture_any(LeptrisElement any_child);
90
+ static char* xsd_capture_any_attr_ns(LeptrisElement container);
89
91
  static void xsd_capture_alternatives(struct leptris_xsd_schema* s,
90
92
  LeptrisElement decl_elem,
91
93
  XsdElementDecl* d);
@@ -900,6 +902,7 @@ LEPTRIS_API void leptris_xsd_free(LeptrisXsdSchema schema) {
900
902
  a = an;
901
903
  }
902
904
  free(ta->type_name);
905
+ free(ta->any_attr_ns);
903
906
  free(ta);
904
907
  ta = tn;
905
908
  }
@@ -998,19 +1001,23 @@ static XsdCm* xsd_cm_copy_list(const XsdCm* head) {
998
1001
 
999
1002
  static void xsd_register_complex(struct leptris_xsd_schema* s,
1000
1003
  XsdCm* root, XsdAttrDecl* attrs,
1001
- const char* name) {
1004
+ const char* name,
1005
+ char* any_attr_ns) {
1002
1006
  if (!root) return;
1003
1007
  root->name = xsd_strdup(name);
1004
1008
  root->next = s->complex_types;
1005
1009
  s->complex_types = root;
1006
- if (attrs) {
1010
+ if (attrs || any_attr_ns) {
1007
1011
  XsdTypeAttrs* ta = (XsdTypeAttrs*)calloc(1, sizeof(*ta));
1008
1012
  if (ta) {
1009
1013
  ta->type_name = xsd_strdup(name);
1010
1014
  ta->attrs = attrs;
1015
+ ta->any_attr_ns = any_attr_ns;
1011
1016
  ta->next = s->type_attrs;
1012
1017
  s->type_attrs = ta;
1013
1018
  }
1019
+ } else if (any_attr_ns) {
1020
+ free(any_attr_ns);
1014
1021
  }
1015
1022
  }
1016
1023
 
@@ -1108,6 +1115,7 @@ static XsdCm* xsd_capture_complex_model(struct leptris_xsd_schema* s,
1108
1115
  XsdCm* root = xsd_cm_new(XSD_CM_SEQ); /* wrapper keeps the name */
1109
1116
  if (!root) return NULL;
1110
1117
  XsdAttrDecl* attrs = NULL;
1118
+ char* any_attr_ns = NULL;
1111
1119
 
1112
1120
  for (LeptrisNodeRef n =
1113
1121
  leptris_node_first_child(leptris_element_as_node(ct));
@@ -1175,6 +1183,8 @@ static XsdCm* xsd_capture_complex_model(struct leptris_xsd_schema* s,
1175
1183
  /* XSD 1.1: asserts nested under the derivation */
1176
1184
  if (s->version_11)
1177
1185
  xsd_capture_assert_children(s, dq, root);
1186
+ if (!any_attr_ns)
1187
+ any_attr_ns = xsd_capture_any_attr_ns(dq);
1178
1188
  }
1179
1189
  } else if (strcmp(local, "simpleContent") == 0) {
1180
1190
  for (LeptrisNodeRef q = leptris_node_first_child(
@@ -1241,6 +1251,7 @@ static XsdCm* xsd_capture_complex_model(struct leptris_xsd_schema* s,
1241
1251
  * re-capturing would orphan those rows. */
1242
1252
  root->first_child = xsd_cm_parse(s, ct, NULL);
1243
1253
  if (!attrs) attrs = xsd_capture_attr_rows(s, ct, 0);
1254
+ if (!any_attr_ns) any_attr_ns = xsd_capture_any_attr_ns(ct);
1244
1255
  }
1245
1256
  /* XSD 1.1: openContent on the type, else the schema default */
1246
1257
  if (s->version_11) {
@@ -1308,7 +1319,7 @@ static XsdCm* xsd_capture_complex_model(struct leptris_xsd_schema* s,
1308
1319
  xsd_cm_free(root->open_any);
1309
1320
  root->open_any = NULL;
1310
1321
  }
1311
- xsd_register_complex(s, root, attrs, name);
1322
+ xsd_register_complex(s, root, attrs, name, any_attr_ns);
1312
1323
  return root;
1313
1324
  }
1314
1325
 
@@ -1474,6 +1485,23 @@ static XsdAttrDecl* xsd_capture_attr_rows(struct leptris_xsd_schema* s,
1474
1485
  return head;
1475
1486
  }
1476
1487
 
1488
+ /* One container's xs:anyAttribute grammar (NULL when absent). */
1489
+ static char* xsd_capture_any_attr_ns(LeptrisElement container) {
1490
+ for (LeptrisNodeRef ac = leptris_node_first_child(
1491
+ leptris_element_as_node(container));
1492
+ ac; ac = leptris_node_next_sibling(ac)) {
1493
+ if (leptris_node_get_type(ac) != LEPTRIS_NODE_TYPE_ELEMENT)
1494
+ continue;
1495
+ LeptrisElement ae = (LeptrisElement)ac;
1496
+ const char* al = NULL;
1497
+ leptris_element_expanded_name(ae, &al, NULL, NULL);
1498
+ if (al && strcmp(al, "anyAttribute") == 0)
1499
+ return xsd_strdup(
1500
+ leptris_element_attribute(ae, "namespace"));
1501
+ }
1502
+ return NULL;
1503
+ }
1504
+
1477
1505
  LEPTRIS_API int leptris_xsd_builtin_valid(const char* builtin,
1478
1506
  const char* lexical) {
1479
1507
  extern int xsd_builtin_valid(const char* type, const char* v);
@@ -208,10 +208,45 @@ static void ss_close(StateSet* ss, CmNfa* n) {
208
208
  }
209
209
  }
210
210
 
211
+ /* XSD xs:all at the model root: order-free, each particle at
212
+ * most maxOccurs times, minOccurs floors — a counting check over
213
+ * the particle list (XSD 1.0 restricts xs:all to the model root,
214
+ * each child a particle, maxOccurs <= 1). */
215
+ static int all_root_valid(XsdCm* all, const char* target_ns,
216
+ const char* const* names,
217
+ const char* const* ns_uris, size_t count) {
218
+ for (XsdCm* p = all->first_child; p; p = p->next) {
219
+ size_t hits = 0;
220
+ for (size_t i = 0; i < count; i++) {
221
+ const char* uri = ns_uris ? ns_uris[i] : NULL;
222
+ if (p->name && strcmp(p->name, names[i]) == 0 &&
223
+ (!p->ns || (uri && strcmp(uri, p->ns) == 0)))
224
+ hits++;
225
+ }
226
+ if ((long)hits < p->min) return 0;
227
+ if (p->max >= 0 && (long)hits > p->max) return 0;
228
+ }
229
+ /* every instance child must belong to a particle */
230
+ for (size_t i = 0; i < count; i++) {
231
+ int matched = 0;
232
+ const char* uri = ns_uris ? ns_uris[i] : NULL;
233
+ for (XsdCm* p = all->first_child; p && !matched; p = p->next) {
234
+ if (p->name && strcmp(p->name, names[i]) == 0 &&
235
+ (!p->ns || (uri && strcmp(uri, p->ns) == 0)))
236
+ matched = 1;
237
+ }
238
+ if (!matched) return 0;
239
+ }
240
+ (void)target_ns;
241
+ return 1;
242
+ }
243
+
211
244
  int xsd_content_valid(struct leptris_xsd_schema* s, XsdCm* model,
212
245
  const char* target_ns, const char* const* names,
213
246
  const char* const* ns_uris, size_t count) {
214
247
  if (!model) return -1;
248
+ if (model->kind == XSD_CM_ALL && model->first_child)
249
+ return all_root_valid(model, target_ns, names, ns_uris, count);
215
250
  CmNfa n;
216
251
  memset(&n, 0, sizeof(n));
217
252
  n.s = s;
@@ -88,6 +88,9 @@ static void verrf(struct xsd_validator* v, const char* what,
88
88
 
89
89
  /* ---- lexical helpers ---------------------------------------------- */
90
90
 
91
+ static int simple_is_numeric(struct leptris_xsd_schema* s,
92
+ const XsdSimple* st);
93
+
91
94
  static int text_valid(struct xsd_validator* v,
92
95
  struct leptris_xsd_schema* s, const char* type,
93
96
  const char* lexical, const char* what,
@@ -128,12 +131,10 @@ static int text_valid(struct xsd_validator* v,
128
131
  }
129
132
  if (mut->assertion_q) {
130
133
  char sel[256];
131
- int numeric = 1;
132
- for (const char* p = lexical; *p; p++)
133
- if (!isdigit((unsigned char)*p) && *p != '.' &&
134
- *p != '-' && *p != '+' && *p != 'e' && *p != 'E')
135
- numeric = 0;
136
- if (numeric && *lexical)
134
+ int numeric =
135
+ simple_is_numeric(s, st) && *lexical &&
136
+ strspn(lexical, "0123456789.+-eE") == strlen(lexical);
137
+ if (numeric)
137
138
  snprintf(sel, sizeof(sel), "%s", lexical);
138
139
  else {
139
140
  size_t w = 0;
@@ -259,6 +260,59 @@ static void validate_children(struct xsd_validator* v,
259
260
  }
260
261
  }
261
262
 
263
+ /* $value binds typed: a numeric-derived simple type binds the
264
+ * lexical as a number literal, everything else as a string — the
265
+ * TYPE decides, not the lexical's shape ("5" under xs:string is
266
+ * the string '5'). */
267
+ static int numeric_builtin(const char* b) {
268
+ static const char* nums[] = {
269
+ "xs:integer", "xs:decimal", "xs:double", "xs:float",
270
+ "xs:int", "xs:long", "xs:short", "xs:byte",
271
+ "xs:nonNegativeInteger", "xs:positiveInteger",
272
+ "xs:negativeInteger", "xs:nonPositiveInteger",
273
+ "xs:unsignedLong", "xs:unsignedInt",
274
+ "xs:unsignedShort", "xs:unsignedByte", NULL};
275
+ for (int i = 0; nums[i]; i++)
276
+ if (strcmp(b, nums[i]) == 0) return 1;
277
+ return 0;
278
+ }
279
+
280
+ static int simple_is_numeric(struct leptris_xsd_schema* s,
281
+ const XsdSimple* st) {
282
+ for (int d = 0; st && d < 32; d++) {
283
+ if (!st->base) return 0;
284
+ if (strncmp(st->base, "xs:", 3) == 0)
285
+ return numeric_builtin(st->base);
286
+ st = xsd_find_simple_pub(s, st->base);
287
+ }
288
+ return 0;
289
+ }
290
+
291
+ /* xs:anyAttribute grammar: ##any (or an absent attribute),
292
+ * ##other, ##targetNamespace, ##local, or a space-separated URI
293
+ * list. */
294
+ static int any_attr_matches(const char* grammar, const char* uri,
295
+ const char* target_ns) {
296
+ if (!grammar || strcmp(grammar, "##any") == 0) return 1;
297
+ if (strcmp(grammar, "##other") == 0)
298
+ return uri && target_ns && strcmp(uri, target_ns) != 0;
299
+ if (strcmp(grammar, "##targetNamespace") == 0)
300
+ return uri && target_ns && strcmp(uri, target_ns) == 0;
301
+ if (strcmp(grammar, "##local") == 0) return uri == NULL;
302
+ /* URI list */
303
+ const char* p = grammar;
304
+ while (*p) {
305
+ while (*p == ' ' || *p == '\t') p++;
306
+ if (!*p) break;
307
+ const char* start = p;
308
+ while (*p && *p != ' ' && *p != '\t') p++;
309
+ size_t len = (size_t)(p - start);
310
+ if (uri && strlen(uri) == len && strncmp(uri, start, len) == 0)
311
+ return 1;
312
+ }
313
+ return 0;
314
+ }
315
+
262
316
  static void validate_attributes(struct xsd_validator* v,
263
317
  struct leptris_xsd_schema* s,
264
318
  LeptrisElement elem,
@@ -294,6 +348,32 @@ static void validate_attributes(struct xsd_validator* v,
294
348
  verrf(v, "attribute", a->name,
295
349
  "value does not match the fixed value");
296
350
  }
351
+ /* strict: every instance attribute must be declared (or pass
352
+ * the type's xs:anyAttribute grammar). Namespace declarations
353
+ * are not attributes. */
354
+ for (LeptrisAttribute at = leptris_element_first_attribute(elem);
355
+ at; at = leptris_attribute_next(at)) {
356
+ const char* qn = leptris_attribute_get_name(at);
357
+ if (!qn || strncmp(qn, "xmlns", 5) == 0) continue;
358
+ const char* colon = strchr(qn, ':');
359
+ const char* local = colon ? colon + 1 : qn;
360
+ int declared = 0;
361
+ for (XsdAttrDecl* a = ta->attrs; a && !declared; a = a->next)
362
+ if (strcmp(a->name, local) == 0 ||
363
+ strcmp(a->name, qn) == 0)
364
+ declared = 1;
365
+ if (declared) continue;
366
+ if (ta->any_attr_ns) {
367
+ /* namespace of the attribute: engine-side attributes
368
+ * carry their uri through the expanded form */
369
+ const char* auri =
370
+ leptris_attribute_namespace_uri(at);
371
+ if (any_attr_matches(ta->any_attr_ns, auri,
372
+ xsd_target_ns(s)))
373
+ continue;
374
+ }
375
+ verrf(v, "attribute", local, "not declared (strict)");
376
+ }
297
377
  }
298
378
 
299
379
  static void validate_element(struct xsd_validator* v,
@@ -304,7 +384,12 @@ static void validate_element(struct xsd_validator* v,
304
384
  leptris_element_expanded_name(elem, &local, &prefix, &uri);
305
385
  if (!local) return;
306
386
  const XsdElementDecl* d = xsd_find_element(s, local);
307
- if (!d) return; /* undeclared: lax at this depth (strict later) */
387
+ if (!d) {
388
+ /* strict: the instance uses an element the schema never
389
+ * declared (a local particle would have typed it) */
390
+ verrf(v, "element", local, "not declared (strict)");
391
+ return;
392
+ }
308
393
 
309
394
  /* XSD 1.1: the first alternative whose test passes types the
310
395
  * element for this instance (context = the element). */
@@ -377,7 +462,11 @@ static void validate_local(struct xsd_validator* v,
377
462
  const char* text = leptris_element_text(elem);
378
463
  if (text && *text)
379
464
  text_valid(v, s, type, text, "element", local, elem);
465
+ return;
380
466
  }
467
+ /* neither complex nor simple nor builtin: the schema is
468
+ * incomplete — loud, never a silent accept (#1615 class) */
469
+ verrf(v, "element", local, "type not found in the schema");
381
470
  }
382
471
 
383
472
  /* ---- slice 6: identity constraints -------------------------------- */
@@ -190,6 +190,7 @@ typedef struct xsd_attrgroup_def {
190
190
 
191
191
  typedef struct xsd_type_attrs {
192
192
  char* type_name;
193
+ char* any_attr_ns; /* xs:anyAttribute grammar (NULL = absent) */
193
194
  XsdAttrDecl* attrs;
194
195
  struct xsd_type_attrs* next;
195
196
  } XsdTypeAttrs;
@@ -2131,3 +2131,98 @@ TEST(Plan1586, ExactUriRowStillWinsAndUnqualifiedStillBinds) {
2131
2131
  leptris_document_free(doc);
2132
2132
  leptris_plan_free(plan);
2133
2133
  }
2134
+
2135
+ // ---- #408: native result builder + address attach -------------------
2136
+ namespace {
2137
+
2138
+ /* leptris_plan_result_build: a flat op program produces the same
2139
+ * standalone result tree a walk would — wrappers, ns bindings, kid
2140
+ * order — so leptris_plan_serialize serves standalone
2141
+ * serialization with no DOM mint on the host side. */
2142
+ TEST(Plan408, ResultBuildFeedsSerialize) {
2143
+ LeptrisStatus st = LEPTRIS_OK;
2144
+ leptris_child_plan kids[1] = {};
2145
+ kids[0].wire_name = "p";
2146
+ kids[0].kind = LEPTRIS_PLAN_KIND_SCALAR;
2147
+ kids[0].type_tag = 1;
2148
+ kids[0].child_plan_index = -1;
2149
+ leptris_element_plan plans[1] = {};
2150
+ plans[0].element_name = "b";
2151
+ plans[0].ns_form = LEPTRIS_PLAN_NS_ANY;
2152
+ plans[0].child_count = 1;
2153
+ plans[0].child_plans = kids;
2154
+ leptris_plan_spec spec = {};
2155
+ spec.abi_version = leptris_plan_abi_version();
2156
+ spec.plan_count = 1;
2157
+ spec.plans = plans;
2158
+ LeptrisPlan plan = leptris_plan_build(&spec, &st);
2159
+ ASSERT_NE(plan, nullptr);
2160
+
2161
+ /* a SCALAR op IS the row's value (name and binding come from
2162
+ * the plan row) — no ELEMENT wrapper needed for scalar rows */
2163
+ leptris_plan_row_op ops[] = {
2164
+ {LEPTRIS_PLAN_OP_ELEMENT, 0, UINT32_MAX, NULL, NULL, 0},
2165
+ {LEPTRIS_PLAN_OP_ATTR, 0, UINT32_MAX, "id", "7", 1},
2166
+ {LEPTRIS_PLAN_OP_SCALAR, 0, 0, NULL, "first", 5},
2167
+ {LEPTRIS_PLAN_OP_END, 0, UINT32_MAX, NULL, NULL, 0},
2168
+ };
2169
+ LeptrisPlanResult r =
2170
+ leptris_plan_result_build(plan, ops, 4, &st);
2171
+ ASSERT_NE(r, nullptr);
2172
+ char* out = leptris_plan_serialize(plan, r, &st);
2173
+ ASSERT_NE(out, nullptr);
2174
+ EXPECT_STREQ(out, "<b id=\"7\"><p>first</p></b>");
2175
+ leptris_free_string(out);
2176
+ leptris_plan_result_free(r);
2177
+ leptris_plan_free(plan);
2178
+ }
2179
+
2180
+ TEST(Plan408, ResultBuildRejectsBadPrograms) {
2181
+ LeptrisStatus st = LEPTRIS_OK;
2182
+ leptris_element_plan plans[1] = {};
2183
+ plans[0].element_name = "b";
2184
+ plans[0].ns_form = LEPTRIS_PLAN_NS_ANY;
2185
+ leptris_plan_spec spec = {};
2186
+ spec.abi_version = leptris_plan_abi_version();
2187
+ spec.plan_count = 1;
2188
+ spec.plans = plans;
2189
+ LeptrisPlan plan = leptris_plan_build(&spec, &st);
2190
+ ASSERT_NE(plan, nullptr);
2191
+
2192
+ /* END without an open element */
2193
+ leptris_plan_row_op bad1[] = {
2194
+ {LEPTRIS_PLAN_OP_END, 0, UINT32_MAX, NULL, NULL, 0}};
2195
+ EXPECT_EQ(leptris_plan_result_build(plan, bad1, 1, &st), nullptr);
2196
+ /* unclosed element */
2197
+ leptris_plan_row_op bad2[] = {
2198
+ {LEPTRIS_PLAN_OP_ELEMENT, 0, UINT32_MAX, NULL, NULL, 0}};
2199
+ EXPECT_EQ(leptris_plan_result_build(plan, bad2, 1, &st), nullptr);
2200
+ /* NULL/empty args */
2201
+ EXPECT_EQ(leptris_plan_result_build(NULL, bad2, 1, &st), nullptr);
2202
+ EXPECT_EQ(leptris_plan_result_build(plan, NULL, 0, &st), nullptr);
2203
+ leptris_plan_free(plan);
2204
+ }
2205
+
2206
+ /* leptris_element_add_child_addr: attach by raw addresses — the
2207
+ * binding's per-node FFI::Pointer mint (#408 ask 1) disappears. */
2208
+ TEST(Plan408, AddChildByAddress) {
2209
+ LeptrisStatus st = LEPTRIS_OK;
2210
+ LeptrisDocument doc = leptris_parse_string("<r/>", 4, &st);
2211
+ ASSERT_NE(doc, nullptr);
2212
+ LeptrisElement parent = leptris_document_root(doc);
2213
+ LeptrisElement child = leptris_element_create(doc, "c");
2214
+ ASSERT_NE(child, nullptr);
2215
+ EXPECT_EQ(leptris_element_add_child_addr(
2216
+ (uintptr_t)parent, (uintptr_t)child),
2217
+ LEPTRIS_OK);
2218
+ EXPECT_EQ(leptris_element_child_count(parent), 1u);
2219
+ LeptrisElement got = leptris_element_first_child_any(parent);
2220
+ ASSERT_NE(got, nullptr);
2221
+ EXPECT_STREQ(leptris_element_name(got), "c");
2222
+ /* NULL-shaped addresses are clean errors */
2223
+ EXPECT_NE(leptris_element_add_child_addr(0, (uintptr_t)child),
2224
+ LEPTRIS_OK);
2225
+ leptris_document_free(doc);
2226
+ }
2227
+
2228
+ } // namespace
@@ -1396,3 +1396,106 @@ TEST(XsdDatatypes, UnionMixesNamedAndInlineMembers) {
1396
1396
  EXPECT_EQ(validate_doc(x, "<v>42</v>"), 1);
1397
1397
  EXPECT_EQ(validate_doc(x, "<v>auto</v>"), 1);
1398
1398
  }
1399
+
1400
+ // ---- completion wave: xs:all order-free semantics ------------------
1401
+ namespace {
1402
+
1403
+ const char* k_all_schema =
1404
+ "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
1405
+ "<xs:complexType name=\"aT\">"
1406
+ "<xs:all>"
1407
+ "<xs:element name=\"x\" type=\"xs:token\"/>"
1408
+ "<xs:element name=\"y\" type=\"xs:token\" minOccurs=\"0\"/>"
1409
+ "</xs:all>"
1410
+ "</xs:complexType>"
1411
+ "<xs:element name=\"r\" type=\"aT\"/>"
1412
+ "</xs:schema>";
1413
+
1414
+ TEST(XsdContent, AllGroupIsOrderFree) {
1415
+ /* declaration order */
1416
+ EXPECT_EQ(validate_doc(k_all_schema, "<r><x>1</x><y>2</y></r>"), 1);
1417
+ /* reversed order: xs:all is order-free */
1418
+ EXPECT_EQ(validate_doc(k_all_schema, "<r><y>2</y><x>1</x></r>"), 1);
1419
+ /* optional member absent */
1420
+ EXPECT_EQ(validate_doc(k_all_schema, "<r><x>1</x></r>"), 1);
1421
+ /* a member appears twice: at most once in xs:all */
1422
+ EXPECT_EQ(validate_doc(k_all_schema, "<r><x>1</x><x>1</x></r>"), 0);
1423
+ /* required member missing */
1424
+ EXPECT_EQ(validate_doc(k_all_schema, "<r><y>2</y></r>"), 0);
1425
+ /* undeclared child inside xs:all */
1426
+ EXPECT_EQ(validate_doc(k_all_schema, "<r><x>1</x><z/></r>"), 0);
1427
+ }
1428
+
1429
+ } // namespace
1430
+
1431
+ // ---- completion wave: loud unresolved types ------------------------
1432
+ namespace {
1433
+
1434
+ TEST(XsdValidate, UnresolvedTypeIsALoudFailure) {
1435
+ /* the schema references a type that does not exist — the
1436
+ * instance must not silently validate (the #1615 symptom
1437
+ * class: stale engines accepted unresolved types) */
1438
+ const char* x =
1439
+ "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
1440
+ "<xs:element name=\"v\" type=\"noSuchType\"/>"
1441
+ "</xs:schema>";
1442
+ EXPECT_EQ(validate_doc(x, "<v>anything</v>"), 0);
1443
+ }
1444
+
1445
+ } // namespace
1446
+
1447
+ // ---- completion wave: strict undeclared elements/attributes -------
1448
+ namespace {
1449
+
1450
+ TEST(XsdValidate, UndeclaredElementIsStrict) {
1451
+ const char* x =
1452
+ "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
1453
+ "<xs:element name=\"r\">"
1454
+ "<xs:complexType><xs:sequence>"
1455
+ "<xs:element name=\"a\" type=\"xs:token\"/>"
1456
+ "</xs:sequence></xs:complexType>"
1457
+ "</xs:element>"
1458
+ "</xs:schema>";
1459
+ EXPECT_EQ(validate_doc(x, "<r><a>1</a></r>"), 1);
1460
+ /* a child the schema never declared */
1461
+ EXPECT_EQ(validate_doc(x, "<r><a>1</a><ghost/></r>"), 0);
1462
+ /* an element outside the sequence */
1463
+ EXPECT_EQ(validate_doc(x, "<r><ghost/><a>1</a></r>"), 0);
1464
+ /* an undeclared ROOT element */
1465
+ EXPECT_EQ(validate_doc(x, "<ghost/>"), 0);
1466
+ }
1467
+
1468
+ TEST(XsdValidate, UndeclaredAttributeIsStrictUnlessAnyAttribute) {
1469
+ const char* strict =
1470
+ "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
1471
+ "<xs:element name=\"r\">"
1472
+ "<xs:complexType><xs:sequence>"
1473
+ "<xs:element name=\"a\" type=\"xs:token\"/>"
1474
+ "</xs:sequence>"
1475
+ "<xs:attribute name=\"id\" type=\"xs:integer\"/>"
1476
+ "</xs:complexType>"
1477
+ "</xs:element>"
1478
+ "</xs:schema>";
1479
+ EXPECT_EQ(validate_doc(strict, "<r id=\"1\"><a>x</a></r>"), 1);
1480
+ /* an attribute the schema never declared */
1481
+ EXPECT_EQ(validate_doc(strict, "<r id=\"1\" note=\"hi\"><a>x</a></r>"),
1482
+ 0);
1483
+
1484
+ /* xs:anyAttribute opens the wildcard: undeclared attrs pass */
1485
+ const char* open_attrs =
1486
+ "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
1487
+ "<xs:element name=\"r\">"
1488
+ "<xs:complexType><xs:sequence>"
1489
+ "<xs:element name=\"a\" type=\"xs:token\"/>"
1490
+ "</xs:sequence>"
1491
+ "<xs:attribute name=\"id\" type=\"xs:integer\"/>"
1492
+ "<xs:anyAttribute namespace=\"##any\" processContents=\"lax\"/>"
1493
+ "</xs:complexType>"
1494
+ "</xs:element>"
1495
+ "</xs:schema>";
1496
+ EXPECT_EQ(validate_doc(open_attrs,
1497
+ "<r id=\"1\" note=\"hi\" tag=\"x\"><a>x</a></r>"),
1498
+ 1);
1499
+ }
1500
+
1501
+ } // namespace
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json",
3
3
  "name": "leptris",
4
- "version": "1.9.333",
4
+ "version": "1.9.334",
5
5
  "description": "High-performance XML parser and XPath 1.0 engine in C (libleptris)",
6
6
  "homepage": "https://github.com/leptris/leptris",
7
7
  "license": "MIT",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: leptris
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.333.0
4
+ version: 1.9.334.0
5
5
  platform: arm-linux
6
6
  authors:
7
7
  - Ribose Inc.