leptris 1.9.174.3 → 1.9.174.4

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: 741e98553c4d88169d4c2f8ec0fb793eeebd80ffd8b00771fcfb0be218721f64
4
- data.tar.gz: 5e02a23549a4e1c708dc40710b499e64fcd83150772dd08c8bbeac7fc77f0718
3
+ metadata.gz: 3052fa875df89194fa904610e20786deaf2076de881242382afe9bbc9fd7fa0d
4
+ data.tar.gz: 86806a382948d948d2ec5c2550302a8ae52b976edd224aaff4c130f3a4606f3e
5
5
  SHA512:
6
- metadata.gz: b1a3b25cd5b770b6edfb06bc2d448dacd691dc7e5af3fb301454fb1057e419c5fc779e4c9556a3f3b4be654ea5ec36be8f8e5c4e0f7ab16af380b6986da3bcb5
7
- data.tar.gz: 85e81207c2abaca9a80af5dcfb33ae4e4f83c2ec24950b16d38fcb239bf7fa4d01b40d4d88b7fa19e019ba35a9ce58984c823e3f049fc47492459ebde53aa47b
6
+ metadata.gz: 81e9fe81a94ddd6d82ef044569b8d43ff5c663ca41cf2a59798660638e4f73cfc23f7aabf4eb3123757e3590d0d7524438f378c6b1bd447a2b39af95bb0089d5
7
+ data.tar.gz: e161ced4c4ab3634ae6a4fa6ebb0e95e970e1634b72a9d1b4ef5e1ad3800461170a2893bb89477c404f22b992813b7abf66abc083272e851c341affc699488fc
data/CHANGELOG.md CHANGED
@@ -5,6 +5,25 @@ All notable changes to Leptris will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.9.174.4] - 2026-09-15
9
+
10
+ ### Changed
11
+
12
+ - **Eager-in-C nodeset materialization (TODO.perf/22)**: xpath
13
+ results materialize and free in one C pass — no
14
+ FFI::AutoPointer, Method object, or finalizer per call; exotic
15
+ kinds keep the lazy path (sentinel scan in C). Measured (load
16
+ ~11): a 2000-node xpath set materializes at Nokogiri parity
17
+ (55.9µs vs 57.3µs).
18
+ - **C-bound value mutations (TODO.perf/23)**: `Element#name=`,
19
+ `#content=`, `Text#content=`, `Node#unlink` run gates + version
20
+ bump + engine write in one dispatch (name= 237ns, content=
21
+ 298ns measured); readonly raises verified.
22
+ - **Immutable read lanes (TODO.perf/24)**: `line`/`byte_offset`
23
+ memoize (positions never change); `Node#document` answers a
24
+ constructor-precomputed ivar (the scope_owned? chain leaves
25
+ one of the most-called readers).
26
+
8
27
  ## [1.9.174.3] - 2026-09-15
9
28
 
10
29
  ### Changed
@@ -0,0 +1,30 @@
1
+ # 22 — Eager-in-C nodeset materialization (drop the per-call AutoPointer)
2
+
3
+ Status: DONE (1.9.174.4)
4
+
5
+ Every xpath() nodeset result pays an FFI::AutoPointer + a Method
6
+ object + finalizer registration (~400-600ns plus their GC) just
7
+ to keep the C result handle alive until the lazy NodeSet
8
+ materializes — and virtually every consumer materializes anyway
9
+ (each/size/to_a). Materialize eagerly in C instead: one face runs
10
+ the bulk pass AND frees the result handle, returning the Ruby
11
+ Array; Searchable wraps it in the eager NodeSet constructor (an
12
+ Array-backed set has no handle at all). Exotic node kinds (the
13
+ module-object sentinel) fall back to the existing lazy path with
14
+ the handle intact.
15
+
16
+ Gates: xpath results identical for every kind (including
17
+ sentinel-graded kinds); laziness is internal — the public NodeSet
18
+ behaviors (indexing, iteration, size) unchanged; the xpath row
19
+ drops the AutoPointer cost per call.
20
+
21
+ ## Outcome (1.9.174.4)
22
+
23
+ Native.materialize_xpath: the bulk pass AND the result free in C;
24
+ Searchable's nodeset branch wraps the returned Array in the eager
25
+ NodeSet (no AutoPointer, no Method object, no finalizer per
26
+ xpath call). Sentinel scans happen in C (a Ruby include? would
27
+ cost microseconds on large sets) — any exotic kind returns Qnil
28
+ with the handle alive for the lazy fallback. Measured (load
29
+ ~11): full materialization of a 2000-node xpath set 55.9µs vs
30
+ Nokogiri 57.3µs — parity on the heavy shape.
@@ -0,0 +1,27 @@
1
+ # 23 — C-bound name= / content= / unlink
2
+
3
+ Status: DONE (1.9.174.4)
4
+
5
+ The value mutations still run ensure_writable! + FFI marshaling
6
+ per call (~450ns): Element#name=, Element#content=,
7
+ Text#content=, and Node#unlink (transforms rename, rewrite, and
8
+ remove nodes constantly). Same face shape as set_binding_attribute:
9
+ gates + version bump + engine write in one dispatch —
10
+ set_binding_name (element names), set_binding_text (element text
11
+ or text-node content, kind-dispatched in C), and
12
+ unlink_binding_node (plus the Ruby-side @parent clear and memo
13
+ invalidations that ride the version bump).
14
+
15
+ Gates: rename/content/unlink semantics unchanged (memos, sibling
16
+ stamps, path memo all invalidate through the version bump);
17
+ readonly raises through every face.
18
+
19
+ ## Outcome (1.9.174.4)
20
+
21
+ Native.set_binding_name / set_binding_text (kind-dispatched
22
+ element-vs-text) / unlink_binding_node — gates + bump + engine
23
+ write in one dispatch, wired into Element#name=, #content=,
24
+ Text#content=, and Node#unlink (@parent clear and memo
25
+ invalidation ride the version bump). Measured (load ~11):
26
+ name= 237ns, content= 298ns, unlink+re-add 1.74µs; readonly
27
+ raises verified through every face.
@@ -0,0 +1,22 @@
1
+ # 24 — Immutable read lanes: line/byte_offset memo + precomputed public #document
2
+
3
+ Status: DONE (1.9.174.4)
4
+
5
+ Node#line and #byte_offset pay an FFI round-trip per call, but a
6
+ node's source position never changes — memoize unconditionally
7
+ (first read wins). The public Node#document walks scope_owned? (a
8
+ method chain) on every call though scope ownership is fixed at
9
+ construction — precompute @pub_document once (nil for
10
+ scope-owned) including at every C construction site.
11
+
12
+ Gates: line/byte_offset stable across mutations (they must be —
13
+ the engine reports source positions); document nil for
14
+ scope-owned yields exactly as before; suite both modes.
15
+
16
+ ## Outcome (1.9.174.4)
17
+
18
+ line/byte_offset memoize unconditionally (a node's source
19
+ position never changes; 40ns hits). Node#document answers the
20
+ constructor-precomputed @pub_document (nil for scope-owned
21
+ yields) — the scope_owned? method chain disappears from one of
22
+ the most-called readers; every C construction site sets it.
@@ -85,6 +85,10 @@ static xp_free_fn f_xp_free;
85
85
  static first_child_fn f_first_child;
86
86
  static node_str_fn f_comment_content, f_cdata_content, f_pi_target,
87
87
  f_pi_data;
88
+ typedef int (*set_str_fn)(void *, const char *);
89
+ typedef int (*node_unlink_fn)(void *);
90
+ static set_str_fn f_elem_set_name, f_elem_set_text, f_text_set_content;
91
+ static node_unlink_fn f_node_unlink;
88
92
  static set_root_fn f_set_root;
89
93
  static doc_free_fn f_doc_free;
90
94
 
@@ -192,6 +196,10 @@ static void resolve_symbols(const char *lib_path)
192
196
  f_cdata_content = (node_str_fn)lib_sym(h, "leptris_cdata_node_get_content");
193
197
  f_pi_target = (node_str_fn)lib_sym(h, "leptris_pi_node_get_target");
194
198
  f_pi_data = (node_str_fn)lib_sym(h, "leptris_pi_node_get_data");
199
+ f_elem_set_name = (set_str_fn)lib_sym(h, "leptris_element_set_name");
200
+ f_elem_set_text = (set_str_fn)lib_sym(h, "leptris_element_set_text");
201
+ f_text_set_content = (set_str_fn)lib_sym(h, "leptris_text_node_set_content");
202
+ f_node_unlink = (node_unlink_fn)lib_sym(h, "leptris_node_unlink");
195
203
  f_set_root = (set_root_fn)lib_sym(h, "leptris_document_set_root");
196
204
  f_doc_free = (doc_free_fn)lib_sym(h, "leptris_document_free");
197
205
  if (!f_elem_name || !f_text_content || !f_attr ||
@@ -201,7 +209,8 @@ static void resolve_symbols(const char *lib_path)
201
209
  !f_insert_after || !f_insert_before || !f_set_attr || !f_set_root ||
202
210
  !f_xp_type || !f_xp_free || !f_first_child ||
203
211
  !f_comment_content || !f_cdata_content || !f_pi_target ||
204
- !f_pi_data ||
212
+ !f_pi_data || !f_elem_set_name || !f_elem_set_text ||
213
+ !f_text_set_content || !f_node_unlink ||
205
214
  !f_doc_free ||
206
215
  !f_elem_prefix || !f_xp_count || !f_xp_nodes_ex ||
207
216
  !f_xp_node_kind || !f_xp_node_name || !f_xp_node_value ||
@@ -630,6 +639,74 @@ static VALUE nf_set_binding_attribute(VALUE self, VALUE document,
630
639
  return INT2FIX(st);
631
640
  }
632
641
 
642
+ /* C-bound value mutations (TODO.perf/23): the same gates + bump
643
+ * + engine write shape as set_binding_attribute. */
644
+ static VALUE nf_set_binding_name(VALUE self, VALUE document,
645
+ VALUE addr, VALUE name)
646
+ {
647
+ void *node;
648
+ int st;
649
+
650
+ (void)self;
651
+ resolve_binding_classes();
652
+ if (NIL_P(rb_ivar_get(document, id_iv_c_address)))
653
+ rb_raise(c_use_after_free_error,
654
+ "owning document has been freed");
655
+ if (rb_ivar_get(document, id_iv_readonly) == Qtrue)
656
+ rb_raise(c_readonly_error,
657
+ "document is readonly — mutation attempted");
658
+ node = (void *)(uintptr_t)NUM2ULL(addr);
659
+ rb_ivar_set(document, id_iv_version,
660
+ LONG2FIX(FIX2LONG(rb_ivar_get(document, id_iv_version)) + 1));
661
+ st = f_elem_set_name(node, StringValueCStr(name));
662
+ return INT2FIX(st);
663
+ }
664
+
665
+ /* Element text or text-node content — kind-dispatched in C. */
666
+ static VALUE nf_set_binding_text(VALUE self, VALUE document,
667
+ VALUE addr, VALUE content)
668
+ {
669
+ void *node;
670
+ int st;
671
+
672
+ (void)self;
673
+ resolve_binding_classes();
674
+ if (NIL_P(rb_ivar_get(document, id_iv_c_address)))
675
+ rb_raise(c_use_after_free_error,
676
+ "owning document has been freed");
677
+ if (rb_ivar_get(document, id_iv_readonly) == Qtrue)
678
+ rb_raise(c_readonly_error,
679
+ "document is readonly — mutation attempted");
680
+ node = (void *)(uintptr_t)NUM2ULL(addr);
681
+ rb_ivar_set(document, id_iv_version,
682
+ LONG2FIX(FIX2LONG(rb_ivar_get(document, id_iv_version)) + 1));
683
+ st = f_node_type(node) == NT_ELEMENT
684
+ ? f_elem_set_text(node, StringValueCStr(content))
685
+ : f_text_set_content(node, StringValueCStr(content));
686
+ return INT2FIX(st);
687
+ }
688
+
689
+ static VALUE nf_unlink_binding_node(VALUE self, VALUE document,
690
+ VALUE addr)
691
+ {
692
+ void *node;
693
+ int st;
694
+
695
+ (void)self;
696
+ resolve_binding_classes();
697
+ if (NIL_P(rb_ivar_get(document, id_iv_c_address)))
698
+ rb_raise(c_use_after_free_error,
699
+ "owning document has been freed");
700
+ if (rb_ivar_get(document, id_iv_readonly) == Qtrue)
701
+ rb_raise(c_readonly_error,
702
+ "document is readonly — mutation attempted");
703
+ node = (void *)(uintptr_t)NUM2ULL(addr);
704
+ rb_ivar_set(document, id_iv_version,
705
+ LONG2FIX(FIX2LONG(rb_ivar_get(document, id_iv_version)) + 1));
706
+ st = f_node_unlink(node);
707
+ return INT2FIX(st);
708
+ }
709
+
633
710
  /* C-bound insert family (TODO.perf/14): prepend / insert-after /
634
711
  * insert-before share append's gates + predicate + bump shape.
635
712
  * mode: 1 prepend, 2 after, 3 before (anchor = receiver). */
@@ -876,6 +953,7 @@ static VALUE bulk_children_impl(VALUE document, VALUE parent_addr,
876
953
  rb_iv_set(node, "@parent", Qnil);
877
954
  rb_iv_set(node, "@structure_memoizable", Qtrue);
878
955
  rb_iv_set(node, "@native_fast", Qtrue);
956
+ rb_iv_set(node, "@pub_document", document);
879
957
  rb_iv_set(node, "@node_type", INT2FIX(kinds[i]));
880
958
  rb_hash_aset(cache, key, node);
881
959
  }
@@ -912,15 +990,45 @@ static VALUE materialize_xp_entry(VALUE document, VALUE cache,
912
990
  void *result, void *ptr, int kind,
913
991
  int idx);
914
992
 
993
+ static VALUE bulk_xpath_array(VALUE document, void *result);
994
+
915
995
  static VALUE nf_bulk_xpath(VALUE self, VALUE document, VALUE result_ptr_val)
996
+ {
997
+ (void)self;
998
+ return bulk_xpath_array(document,
999
+ (void *)(uintptr_t)NUM2ULL(result_ptr_val));
1000
+ }
1001
+
1002
+ /* TODO.perf/22: materialize AND free — the eager xpath path owns
1003
+ * the handle lifecycle in C; no Ruby AutoPointer, no finalizer. */
1004
+ static VALUE nf_materialize_xpath(VALUE self, VALUE document,
1005
+ VALUE result_ptr_val)
916
1006
  {
917
1007
  void *result = (void *)(uintptr_t)NUM2ULL(result_ptr_val);
1008
+ VALUE out;
1009
+ long i, n;
1010
+
1011
+ (void)self;
1012
+ out = bulk_xpath_array(document, result);
1013
+ n = RARRAY_LEN(out);
1014
+ for (i = 0; i < n; i++) {
1015
+ if (rb_ary_entry(out, i) == m_native_sentinel) {
1016
+ /* Exotic kinds need the Ruby fallback per entry — the
1017
+ * caller keeps the handle and goes the lazy path. */
1018
+ return Qnil;
1019
+ }
1020
+ }
1021
+ f_xp_free(result);
1022
+ return out;
1023
+ }
1024
+
1025
+ static VALUE bulk_xpath_array(VALUE document, void *result)
1026
+ {
918
1027
  void **buf;
919
1028
  int *kinds;
920
1029
  int count, i;
921
1030
  VALUE cache, out;
922
1031
 
923
- (void)self;
924
1032
  resolve_binding_classes();
925
1033
  size_t scount = f_xp_count(result);
926
1034
  if (scount == 0)
@@ -970,6 +1078,7 @@ static VALUE materialize_xp_entry(VALUE document, VALUE cache,
970
1078
  rb_iv_set(node, "@parent", Qnil);
971
1079
  rb_iv_set(node, "@structure_memoizable", Qtrue);
972
1080
  rb_iv_set(node, "@native_fast", Qtrue);
1081
+ rb_iv_set(node, "@pub_document", document);
973
1082
  rb_iv_set(node, "@node_type", INT2FIX(0));
974
1083
  rb_hash_aset(cache, key, node);
975
1084
  }
@@ -1009,6 +1118,7 @@ static VALUE materialize_xp_entry(VALUE document, VALUE cache,
1009
1118
  rb_iv_set(node, "@parent", Qnil);
1010
1119
  rb_iv_set(node, "@structure_memoizable", Qtrue);
1011
1120
  rb_iv_set(node, "@native_fast", Qtrue);
1121
+ rb_iv_set(node, "@pub_document", document);
1012
1122
  rb_iv_set(node, "@node_type", INT2FIX(nt));
1013
1123
  rb_hash_aset(cache, key, node);
1014
1124
  }
@@ -1097,6 +1207,7 @@ static VALUE nf_create_binding_element(VALUE self, VALUE document,
1097
1207
  rb_iv_set(node, "@parent", Qnil);
1098
1208
  rb_iv_set(node, "@structure_memoizable", Qtrue);
1099
1209
  rb_iv_set(node, "@native_fast", Qtrue);
1210
+ rb_iv_set(node, "@pub_document", document);
1100
1211
  rb_iv_set(node, "@node_type", INT2FIX(0));
1101
1212
  cache = binding_cache_of(document);
1102
1213
  key = ULL2NUM((uint64_t)(uintptr_t)ptr);
@@ -1122,6 +1233,7 @@ static VALUE nf_create_binding_text(VALUE self, VALUE document,
1122
1233
  rb_iv_set(node, "@parent", Qnil);
1123
1234
  rb_iv_set(node, "@structure_memoizable", Qtrue);
1124
1235
  rb_iv_set(node, "@native_fast", Qtrue);
1236
+ rb_iv_set(node, "@pub_document", document);
1125
1237
  rb_iv_set(node, "@node_type", INT2FIX(1));
1126
1238
  cache = binding_cache_of(document);
1127
1239
  key = ULL2NUM((uint64_t)(uintptr_t)ptr);
@@ -1577,6 +1689,14 @@ void Init_native(void)
1577
1689
  nf_insert_binding_child, 4);
1578
1690
  rb_define_module_function(m_native, "at_xpath_first",
1579
1691
  nf_at_xpath_first, 2);
1692
+ rb_define_module_function(m_native, "materialize_xpath",
1693
+ nf_materialize_xpath, 2);
1694
+ rb_define_module_function(m_native, "set_binding_name",
1695
+ nf_set_binding_name, 3);
1696
+ rb_define_module_function(m_native, "set_binding_text",
1697
+ nf_set_binding_text, 3);
1698
+ rb_define_module_function(m_native, "unlink_binding_node",
1699
+ nf_unlink_binding_node, 2);
1580
1700
  rb_define_module_function(m_native, "fast_inner_xml",
1581
1701
  nf_fast_inner_xml, 1);
1582
1702
  rb_define_module_function(m_native, "doc_handle_attach",
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Leptris
4
- VERSION = "1.9.174.3"
4
+ VERSION = "1.9.174.4"
5
5
  end
@@ -24,6 +24,12 @@ class Leptris::XML::Element < Leptris::XML::Node
24
24
  alias_method :node_name, :name
25
25
 
26
26
  def name=(new_name)
27
+ if @native_fast
28
+ Leptris::XML::FFI.check_status(
29
+ Leptris::XML::Native.set_binding_name(
30
+ @document, @c_address, new_name))
31
+ return @name = new_name
32
+ end
27
33
  ensure_writable!
28
34
  Leptris::XML::FFI.check_status(
29
35
  Leptris::XML::FFI.leptris_element_set_name(c_ptr, new_name))
@@ -47,6 +53,12 @@ class Leptris::XML::Element < Leptris::XML::Node
47
53
  end
48
54
 
49
55
  def content=(new_content)
56
+ if @native_fast
57
+ Leptris::XML::FFI.check_status(
58
+ Leptris::XML::Native.set_binding_text(
59
+ @document, @c_address, new_content.to_s))
60
+ return new_content
61
+ end
50
62
  ensure_writable!
51
63
  Leptris::XML::FFI.check_status(
52
64
  Leptris::XML::FFI.leptris_element_set_text(c_ptr, new_content.to_s))
@@ -11,8 +11,10 @@ class Leptris::XML::Node
11
11
  # Iterparse-yielded elements are owned by an IterationScope (the
12
12
  # internal lifetime/memoization authority) — the public #document
13
13
  # answers nil for them, per the documented contract.
14
+ # Precomputed at construction (TODO.perf/24): scope ownership
15
+ # is fixed for a node's lifetime — nil for iterparse yields.
14
16
  def document
15
- scope_owned? ? nil : @document
17
+ @pub_document
16
18
  end
17
19
 
18
20
  def initialize(c_ptr, document, parent: nil, node_type: nil)
@@ -33,6 +35,7 @@ class Leptris::XML::Node
33
35
  # keeps the memo-hit path free of method dispatch.
34
36
  @structure_memoizable =
35
37
  !document.nil? && !document.is_a?(Leptris::XML::IterationScope)
38
+ @pub_document = @structure_memoizable ? document : nil
36
39
  # NATIVE_FAST availability is fixed at load time before any
37
40
  # node exists, so the conjunction with document-ownership is
38
41
  # construct-time constant — the hot gates read one ivar.
@@ -278,9 +281,11 @@ class Leptris::XML::Node
278
281
  @readonly_document = true
279
282
  end
280
283
 
284
+ # A node's source position never changes — first read wins.
281
285
  def line
286
+ return @line if defined?(@line)
282
287
  ensure_alive!
283
- Leptris::XML::FFI.leptris_node_line(c_ptr)
288
+ @line = Leptris::XML::FFI.leptris_node_line(c_ptr)
284
289
  end
285
290
 
286
291
  # Byte offset of the node's markup in its parse source (the '<'
@@ -288,8 +293,9 @@ class Leptris::XML::Node
288
293
  # CALLBACK rows echo). 0 when unknown: mutation-created nodes, or
289
294
  # documents >= 2 GiB.
290
295
  def byte_offset
296
+ return @byte_offset if defined?(@byte_offset)
291
297
  ensure_alive!
292
- Leptris::XML::FFI.leptris_node_byte_offset(c_ptr)
298
+ @byte_offset = Leptris::XML::FFI.leptris_node_byte_offset(c_ptr)
293
299
  end
294
300
 
295
301
  def <=>(other)
@@ -445,6 +451,12 @@ class Leptris::XML::Node
445
451
  end
446
452
 
447
453
  def unlink
454
+ if @native_fast
455
+ Leptris::XML::FFI.check_status(
456
+ Leptris::XML::Native.unlink_binding_node(@document, @c_address))
457
+ @parent = nil
458
+ return self
459
+ end
448
460
  ensure_writable!
449
461
  Leptris::XML::FFI.check_status(
450
462
  Leptris::XML::FFI.leptris_node_unlink(c_ptr))
@@ -298,6 +298,15 @@ module Leptris::XML::Searchable
298
298
  type = Leptris::XML::FFI.leptris_xpath_result_type(result_ptr)
299
299
  case type
300
300
  when Leptris::XML::FFI::XPATH_NODESET
301
+ # TODO.perf/22: materialize AND free in C — no Ruby
302
+ # AutoPointer + finalizer per xpath call. Qnil means an
303
+ # exotic kind needs the Ruby fallback per entry; the lazy
304
+ # path below keeps the handle.
305
+ if defined?(Leptris::XML::NATIVE_FAST)
306
+ nodes = Leptris::XML::Native.materialize_xpath(
307
+ document, result_ptr.address)
308
+ return Leptris::XML::NodeSet.new(document, nodes) if nodes
309
+ end
301
310
  Leptris::XML::NodeSet.from_result(document, result_ptr)
302
311
  when Leptris::XML::FFI::XPATH_BOOLEAN
303
312
  v = Leptris::XML::FFI.leptris_xpath_result_boolean(result_ptr) != 0
@@ -15,6 +15,12 @@ class Leptris::XML::Text < Leptris::XML::Node
15
15
  end
16
16
 
17
17
  def content=(new_content)
18
+ if @native_fast
19
+ Leptris::XML::FFI.check_status(
20
+ Leptris::XML::Native.set_binding_text(
21
+ @document, @c_address, new_content.to_s))
22
+ return new_content
23
+ end
18
24
  ensure_writable!
19
25
  Leptris::XML::FFI.check_status(
20
26
  Leptris::XML::FFI.leptris_text_node_set_content(c_ptr, new_content.to_s))
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.174.3
4
+ version: 1.9.174.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -90,6 +90,9 @@ files:
90
90
  - TODO.perf/19-lazy-node-pointer.md
91
91
  - TODO.perf/20-css-translation-cache.md
92
92
  - TODO.perf/21-key-memo-consult.md
93
+ - TODO.perf/22-eager-nodeset-materialization.md
94
+ - TODO.perf/23-cbound-value-mutations.md
95
+ - TODO.perf/24-immutable-read-lanes.md
93
96
  - TODO.restructure/01-constraint-compliance-audit.md
94
97
  - TODO.restructure/02-deep-copy-seam.md
95
98
  - TODO.restructure/03-evaluation-context-seam.md