leptris 1.9.331.0-ppc64le-linux → 1.9.334.0-ppc64le-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: fee2111577d248ca3532241fe8809e68918bbd72d336b58a426344e2bd1fa399
4
- data.tar.gz: bc71727942c68d0f44ef2a7a1295b5499d4f54e2e7ad6795c9cf7ce78a58708e
3
+ metadata.gz: 4c687c6b5c09c8f7ec7bd48a26c197318f6f2a396680075a39f2cbfe445eb9c8
4
+ data.tar.gz: 458aac071ab017d4bc2192bb9e1ac2c2c003edf631a058a16eb5446d1051e4f1
5
5
  SHA512:
6
- metadata.gz: 591c7925eaae4994e25c4511f917e8a0e4a673b09d63902325e92135b772710e203c0d0bfda0e6a75c2870e442da589c04763afcdc8423f684e9cef4b290f683
7
- data.tar.gz: 3e5efaef0690b4b96826e21dd8c6065a0437ba6f544f39bcf72afb50a76369f82a94f34f6c7e1a8d8d5c4dbd7692e11144143a33359a0ff8a6907549511b339c
6
+ metadata.gz: f3cacf66947185632eda797cc645d036978912e5acb10726a83c615db019ced9ffe6146411321ee47ffdb521f64d25f7f9789af7dd1938c2d7809bbf260a522d
7
+ data.tar.gz: 4efeebf09318e7c65ef475fc3fc05c44fa7597226fd8f087f26acac28d50b3a79c76fc574ff2937aa2436d2c16ac30aa5f9769813358e005f3a757701afd9c46
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.331"
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.331.0"
4
+ VERSION = "1.9.334.0"
5
5
  end
@@ -31,4 +31,8 @@ class Leptris::XML::CDATA < Leptris::XML::Text
31
31
  Leptris::XML::FFI.leptris_cdata_node_set_content(c_ptr, new_content.to_s))
32
32
  new_content
33
33
  end
34
+
35
+ def to_s
36
+ "<![CDATA[#{content}]]>"
37
+ end
34
38
  end
@@ -31,4 +31,8 @@ class Leptris::XML::Comment < Leptris::XML::Node
31
31
  Leptris::XML::FFI.leptris_comment_node_set_content(c_ptr, new_content.to_s))
32
32
  new_content
33
33
  end
34
+
35
+ def to_s
36
+ "<!--#{content}-->"
37
+ end
34
38
  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
@@ -10,4 +10,8 @@ class Leptris::XML::EntityReference < Leptris::XML::Node
10
10
  Leptris::XML::FFI.leptris_entity_ref_node_name(c_ptr)
11
11
  end
12
12
  alias_method :node_name, :name
13
+
14
+ def to_s
15
+ "&#{name};"
16
+ end
13
17
  end
@@ -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,
@@ -8,6 +8,7 @@ class Leptris::XML::Node
8
8
  @c_ptr ||= ::FFI::Pointer.new(@c_address)
9
9
  end
10
10
 
11
+
11
12
  # The Integer twin of #c_ptr — reading it never materializes a
12
13
  # Pointer (the native faces take addresses).
13
14
  def c_address
@@ -50,6 +50,10 @@ class Leptris::XML::ProcessingInstruction < Leptris::XML::Node
50
50
  new_data
51
51
  end
52
52
 
53
+ def to_s
54
+ content.to_s.empty? ? "<?#{name}?>" : "<?#{name} #{content}?>"
55
+ end
56
+
53
57
  # Document-level PIs have no tree parent for leptris_node_unlink
54
58
  # — route through the document-level removal (libleptris 1.9.9,
55
59
  # #612), identity-matched by index so the right same-target PI
@@ -36,4 +36,8 @@ class Leptris::XML::Text < Leptris::XML::Node
36
36
  Leptris::XML::FFI.leptris_text_node_set_content(c_ptr, new_content.to_s))
37
37
  new_content
38
38
  end
39
+
40
+ def to_s
41
+ Leptris::XML::Serialization.escape_text(content)
42
+ end
39
43
  end
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.331
4
+ - libleptris v1.9.334
5
5
  - utf8proc v2.11.0
6
6
 
7
7
  Rebuild by hand:
@@ -1,5 +1,70 @@
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
+
32
+ ## [1.9.333] - 2026-10-10
33
+
34
+ ### Added
35
+
36
+ - **Regression pin: union `memberTypes` + inline anonymous members
37
+ (#1615, PR #1618).** The reported over-acceptance was not
38
+ reproducible on any engine artifact (main, the v1.9.331 tag,
39
+ every published gem binary — including through the full Ruby
40
+ binding path). The exact schema now asserts in CI through both
41
+ `leptris_xsd_simple_valid` and instance validation, so the
42
+ mixed-member semantics are verifiable binary-independent of any
43
+ binding environment.
44
+
45
+
46
+ ## [1.9.332] - 2026-10-09
47
+
48
+ ### Added
49
+
50
+ - **XSD 1.1 — tier 2 (#1075, PR #1616).** Gated on
51
+ `xs:schema @version="1.1"`: `xs:assert` on complexTypes (direct
52
+ and derivation-nested; compiled once, evaluated per instance
53
+ through the engine's XPath 2 lane; evaluation errors are
54
+ failures); `xs:assertion` on simpleTypes (`$value` binds the
55
+ lexical, element context); `xs:openContent` interleave/suffix
56
+ (wildcard self-loops at the Thompson NFA's junction states;
57
+ `xs:defaultOpenContent` schema-wide with `appliesToEmpty`) and
58
+ `xs:alternative` conditional type assignment (first passing
59
+ test types the instance, declared type as fallback). Ten
60
+ red-first specs; ctest 1951/1951; 1.0 schemas unaffected.
61
+
62
+ ### Fixed
63
+
64
+ - free the defaultOpenContent wildcard with the schema (xsd)
65
+
66
+
67
+
3
68
  ## [1.9.331] - 2026-10-09
4
69
 
5
70
  ### Fixed
@@ -3,7 +3,7 @@
3
3
  cmake_minimum_required(VERSION 3.20)
4
4
 
5
5
  project(leptris
6
- VERSION 1.9.331
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