leptris 1.9.255.0 → 1.9.255.2

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: 5d1418101d40d96692273fa9327b6ad4c666a0705beeaa3c1500cdea623d3b83
4
- data.tar.gz: f8605c3bd4a6f64e8a2361d4bdb375808665a57ec26f21deaf2d619a7695fa55
3
+ metadata.gz: 5b6e6554da43271e1b86c948f2ecd2a0f35c1db9a8ec62cdcb8c8d422a467da4
4
+ data.tar.gz: f37099846579bda7f01d85fb8b5aeb16da3003c862ad3364ed9e485c72d1e40f
5
5
  SHA512:
6
- metadata.gz: 83c628648a66102c5a29da7f443761cf35c9b238328b22f9a46a4a43146e8a4b83a993aff7e53f1ca8b325abbe98c9f8c28a255ffb2a040ae050c91b01383a52
7
- data.tar.gz: f831136e4a5fe977c2cd6ecc91ffd5e010f278fe0201f644d384ae6e1fa29aa4c887508a660c83fb5d87d38d8104f71e2656eab13876757a48c546e077065804
6
+ metadata.gz: c063a07c01d9c968d506bbefb02ef6f82840b44ea7e13e6af58dffef55ccaf44813fd64b438f2e523739585d78286f196f56f53b293b8c75bdc0388708d2a939
7
+ data.tar.gz: 524629c877881d34319b581f3e9b0908ff74484c8325854ab9cf4d203a066b8dd909c2365ee77a08abd80e771d90c160fadad03bfe6cc3b95e0db15e1a5036de
@@ -627,6 +627,60 @@ static void *doc_ptr_of(VALUE document)
627
627
  /* Per-document identity caches. Both are lazily allocated on the
628
628
  * Ruby side (@x ||= {}), so replicate that here: ivar read, create
629
629
  * and store when unset. */
630
+ /* #340: per-document wrapper-klass registration. The ivar holds an
631
+ * Array of classes/nil indexed by node kind (the same shape
632
+ * install_child_klasses uses at the klass level). When an entry is
633
+ * set, the walk cache AND the create faces mint that klass
634
+ * (NativeNode subclass, TypedData path) instead of the binding
635
+ * wrapper classes — consumers receive their own contract-carrying
636
+ * nodes straight from C, and their re-wrap drops out. */
637
+ static ID id_iv_doc_klass_map;
638
+
639
+ static VALUE doc_klass_for_kind(VALUE document, int kind)
640
+ {
641
+ VALUE map = rb_ivar_get(document, id_iv_doc_klass_map);
642
+ VALUE k;
643
+ if (NIL_P(map)) return Qnil;
644
+ k = rb_ary_entry(map, (long)kind);
645
+ return RB_TYPE_P(k, T_CLASS) ? k : Qnil;
646
+ }
647
+
648
+ static VALUE mint_consumer_node(VALUE klass, VALUE document, void *ptr)
649
+ {
650
+ struct native_node *n;
651
+ VALUE node =
652
+ TypedData_Make_Struct(klass, struct native_node, &nn_type, n);
653
+ n->ptr = ptr;
654
+ n->document = document;
655
+ rb_hash_aset(native_cache_of(document),
656
+ ULL2NUM((uint64_t)(uintptr_t)ptr), node);
657
+ return node;
658
+ }
659
+
660
+ static VALUE nf_register_doc_klasses(VALUE self, VALUE document, VALUE map)
661
+ {
662
+ long i, len;
663
+ (void)self;
664
+ resolve_binding_classes();
665
+ Check_Type(map, T_ARRAY);
666
+ len = RARRAY_LEN(map);
667
+ if (len > 5)
668
+ rb_raise(rb_eArgError,
669
+ "wrapper klasses: Array of classes/nil indexed by "
670
+ "node kind (element, text, comment, cdata, pi)");
671
+ for (i = 0; i < len; i++) {
672
+ VALUE k = rb_ary_entry(map, i);
673
+ if (!NIL_P(k) &&
674
+ !(RB_TYPE_P(k, T_CLASS) &&
675
+ RTEST(rb_class_inherited_p(k, c_native_node))))
676
+ rb_raise(rb_eTypeError,
677
+ "wrapper klasses entries must be nil or a "
678
+ "Leptris::XML::NativeNode subclass");
679
+ }
680
+ rb_ivar_set(document, id_iv_doc_klass_map, map);
681
+ return document;
682
+ }
683
+
630
684
  static VALUE native_cache_of(VALUE document)
631
685
  {
632
686
  VALUE cache = rb_ivar_get(document, id_iv_native_cache);
@@ -877,7 +931,20 @@ static VALUE trav_wrapper_for(struct trav_state *st, void *node_ptr,
877
931
  int kind)
878
932
  {
879
933
  VALUE key = ULL2NUM((uint64_t)(uintptr_t)node_ptr);
880
- VALUE node = rb_hash_aref(st->cache, key);
934
+ VALUE node;
935
+ VALUE ck = doc_klass_for_kind(st->document, kind);
936
+
937
+ /* #340 klass-parameterized walk cache: registered consumer
938
+ * klasses mint (and identity-cache) through the TypedData
939
+ * path — nodes arrive already being the consumer's wrapper. */
940
+ if (!NIL_P(ck)) {
941
+ VALUE ncache = native_cache_of(st->document);
942
+ node = rb_hash_aref(ncache, key);
943
+ if (!NIL_P(node)) return node;
944
+ return mint_consumer_node(ck, st->document, node_ptr);
945
+ }
946
+
947
+ node = rb_hash_aref(st->cache, key);
881
948
  if (!NIL_P(node)) return node;
882
949
 
883
950
  node = rb_obj_alloc(binding_klass_for(kind));
@@ -2290,20 +2357,15 @@ static VALUE nf_plan_structs_typed_p(VALUE self)
2290
2357
  * C->C dlsym dispatches (create_child = create+attach in one
2291
2358
  * engine call; set_attr per attribute). Returns the new node's
2292
2359
  * address for wrapper minting. */
2293
- static VALUE nf_create_element_with_attrs(VALUE self, VALUE document,
2294
- VALUE parent_addr, VALUE name,
2295
- VALUE attrs)
2296
- {
2297
- void *doc_ptr = NIL_P(document)
2298
- ? NULL
2299
- : (void *)(uintptr_t)NUM2ULL(document);
2300
- void *parent = NIL_P(parent_addr)
2301
- ? NULL
2302
- : (void *)(uintptr_t)NUM2ULL(parent_addr);
2303
- const char *cname = StringValueCStr(name);
2304
-
2305
- if (f_elem_new_with_attrs && doc_ptr && parent && RARRAY_LEN(attrs) > 0 &&
2306
- RARRAY_LEN(attrs) % 2 == 0) {
2360
+ /* Creation core shared by the raw-address face and the #340
2361
+ * wrapper-returning variant: single-crossing arena create +
2362
+ * attributes when the engine has #1344, else create + per-pair
2363
+ * sets. Returns the raw element pointer (NULL on failure). */
2364
+ static void *create_elem_core(void *doc_ptr, void *parent,
2365
+ const char *cname, VALUE attrs)
2366
+ {
2367
+ if (f_elem_new_with_attrs && doc_ptr && parent &&
2368
+ RARRAY_LEN(attrs) > 0 && RARRAY_LEN(attrs) % 2 == 0) {
2307
2369
  /* Engine single-crossing construction (1.9.236+): arena
2308
2370
  * create + all attributes with no per-attr calls at all. */
2309
2371
  long n = RARRAY_LEN(attrs) / 2;
@@ -2319,15 +2381,15 @@ static VALUE nf_create_element_with_attrs(VALUE self, VALUE document,
2319
2381
  }
2320
2382
  void *elem = f_elem_new_with_attrs(doc_ptr, cname, names,
2321
2383
  values, n);
2322
- if (!elem) return Qnil;
2384
+ if (!elem) return NULL;
2323
2385
  /* The face creates a floating element owned by the
2324
2386
  * document — append under the parent afterwards. */
2325
- if (parent) f_append_child(parent, elem);
2326
- return ULL2NUM((uintptr_t)elem);
2387
+ f_append_child(parent, elem);
2388
+ return elem;
2327
2389
  }
2328
2390
 
2329
2391
  void *elem = parent ? f_create_child(parent, cname) : NULL;
2330
- if (!elem) return Qnil;
2392
+ if (!elem) return NULL;
2331
2393
 
2332
2394
  long i, n = RARRAY_LEN(attrs);
2333
2395
  for (i = 0; i + 1 < n; i += 2) {
@@ -2335,7 +2397,65 @@ static VALUE nf_create_element_with_attrs(VALUE self, VALUE document,
2335
2397
  VALUE av = RARRAY_AREF(attrs, i + 1);
2336
2398
  f_set_attr(elem, StringValueCStr(an), StringValueCStr(av));
2337
2399
  }
2338
- return ULL2NUM((uintptr_t)elem);
2400
+ return elem;
2401
+ }
2402
+
2403
+ static VALUE nf_create_element_with_attrs(VALUE self, VALUE document,
2404
+ VALUE parent_addr, VALUE name,
2405
+ VALUE attrs)
2406
+ {
2407
+ void *doc_ptr = NIL_P(document)
2408
+ ? NULL
2409
+ : (void *)(uintptr_t)NUM2ULL(document);
2410
+ void *parent = NIL_P(parent_addr)
2411
+ ? NULL
2412
+ : (void *)(uintptr_t)NUM2ULL(parent_addr);
2413
+ void *elem = create_elem_core(doc_ptr, parent,
2414
+ StringValueCStr(name), attrs);
2415
+ return elem ? ULL2NUM((uintptr_t)elem) : Qnil;
2416
+ }
2417
+
2418
+ /* #340 construction registration: create + attach + attributes in
2419
+ * one crossing AND mint the wrapper C-side — the registered
2420
+ * consumer klass when one is installed for elements (TypedData
2421
+ * path + native-cache identity), else the binding Element minted
2422
+ * exactly like nf_create_binding_element. The caller receives a
2423
+ * contract-carrying node straight from the crossing; no separate
2424
+ * Node.wrap, no consumer re-wrap. */
2425
+ static VALUE nf_create_element_with_attrs_wrapped(
2426
+ VALUE self, VALUE document, VALUE parent_addr, VALUE name,
2427
+ VALUE attrs)
2428
+ {
2429
+ void *doc_ptr = doc_ptr_of(document);
2430
+ void *parent = NIL_P(parent_addr)
2431
+ ? NULL
2432
+ : (void *)(uintptr_t)NUM2ULL(parent_addr);
2433
+ void *elem = create_elem_core(doc_ptr, parent,
2434
+ StringValueCStr(name), attrs);
2435
+ VALUE ck, key, node, cache;
2436
+
2437
+ (void)self;
2438
+ if (!elem) return Qnil;
2439
+ resolve_binding_classes();
2440
+ ck = doc_klass_for_kind(document, 0 /* element */);
2441
+ if (!NIL_P(ck))
2442
+ return mint_consumer_node(ck, document, elem);
2443
+
2444
+ key = ULL2NUM((uint64_t)(uintptr_t)elem);
2445
+ cache = binding_cache_of(document);
2446
+ node = rb_hash_aref(cache, key);
2447
+ if (!NIL_P(node)) return node;
2448
+ node = rb_obj_alloc(c_b_element);
2449
+ rb_iv_set(node, "@c_address", key);
2450
+ rb_iv_set(node, "@document", document);
2451
+ rb_iv_set(node, "@parent", Qnil);
2452
+ rb_iv_set(node, "@structure_memoizable", Qtrue);
2453
+ rb_iv_set(node, "@native_fast", Qtrue);
2454
+ rb_iv_set(node, "@pub_document", document);
2455
+ rb_iv_set(node, "@addr_reads_fast", Qtrue);
2456
+ rb_iv_set(node, "@node_type", INT2FIX(0));
2457
+ rb_hash_aset(cache, key, node);
2458
+ return node;
2339
2459
  }
2340
2460
 
2341
2461
  static VALUE nf_attribute_rows(VALUE self, VALUE document, VALUE addr)
@@ -2844,6 +2964,7 @@ LEPTRIS_INIT_EXPORT void Init_native(void)
2844
2964
  id_iv_nn_attrs = rb_intern("@nn_attrs");
2845
2965
  id_iv_nn_attrs_ver = rb_intern("@nn_attrs_ver");
2846
2966
  id_child_klasses = rb_intern("@nn_child_klasses");
2967
+ id_iv_doc_klass_map = rb_intern("@doc_klass_map");
2847
2968
  id_iv_doc_handle = rb_intern("@doc_handle");
2848
2969
  id_freed_new = rb_intern("new");
2849
2970
  id_alive = rb_intern("alive");
@@ -2954,6 +3075,11 @@ LEPTRIS_INIT_EXPORT void Init_native(void)
2954
3075
  nf_attribute_rows, 2);
2955
3076
  rb_define_module_function(m_native, "create_element_with_attrs",
2956
3077
  nf_create_element_with_attrs, 4);
3078
+ rb_define_module_function(m_native, "register_doc_klasses",
3079
+ nf_register_doc_klasses, 2);
3080
+ rb_define_module_function(m_native,
3081
+ "create_element_with_attrs_wrapped",
3082
+ nf_create_element_with_attrs_wrapped, 4);
2957
3083
  rb_define_module_function(m_native, "plan_structs_typed?",
2958
3084
  nf_plan_structs_typed_p, 0);
2959
3085
  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.255.0"
4
+ VERSION = "1.9.255.2"
5
5
  end
@@ -75,6 +75,18 @@ class Leptris::XML::Document
75
75
  Leptris::XML::NativeNode.from(self, r.c_ptr)
76
76
  end
77
77
 
78
+ # #340: per-kind wrapper-klass registration for THIS document.
79
+ # map is an Array of classes/nil indexed by node kind (element,
80
+ # text, comment, cdata, pi) — the same shape
81
+ # NativeNode.install_child_klasses uses. Once registered, the C
82
+ # walk cache (trav/visit) and the create faces mint those
83
+ # klasses directly (TypedData path, native-cache identity), so
84
+ # consumers receive their own contract-carrying nodes straight
85
+ # from the crossing with no re-wrap.
86
+ def register_wrapper_klasses(map)
87
+ Leptris::XML::Native.register_doc_klasses(self, map)
88
+ end
89
+
78
90
  # Builder factories that return NativeNodes (#149): one C call
79
91
  # and a TypedData wrap — no FFI::Pointer, no wrap_fresh path.
80
92
  def native_create_element(name)
@@ -31,33 +31,33 @@ class Leptris::XML::PlanValue
31
31
  end
32
32
 
33
33
  def kind
34
- inc_crossing
34
+ @counter[0] += 1
35
35
  VALUE_KINDS.fetch(Leptris::XML::FFI.leptris_plan_value_kind(@ptr))
36
36
  end
37
37
 
38
38
  # wire_name of the plan row that produced this value.
39
39
  def name
40
- inc_crossing
40
+ @counter[0] += 1
41
41
  Leptris::XML::FFI.leptris_plan_value_name(@ptr)
42
42
  end
43
43
 
44
44
  # Host type_tag echoed verbatim (0 when the row had none).
45
45
  def type_tag
46
- inc_crossing
46
+ @counter[0] += 1
47
47
  Leptris::XML::FFI.leptris_plan_value_type_tag(@ptr)
48
48
  end
49
49
 
50
50
  # #1273: LEPTRIS_NODE_TYPE_* of this value's source node (0 for
51
51
  # ELEMENT/COLLECTION wrappers and synthesized text).
52
52
  def node_kind
53
- inc_crossing
53
+ @counter[0] += 1
54
54
  Leptris::XML::FFI.leptris_plan_value_node_kind(@ptr)
55
55
  end
56
56
 
57
57
  # #1273: dense sibling rank inside the producing element (0 if
58
58
  # unranked) — document-order identity without byte offsets.
59
59
  def order_index
60
- inc_crossing
60
+ @counter[0] += 1
61
61
  Leptris::XML::FFI.leptris_plan_value_order_index(@ptr)
62
62
  end
63
63
 
@@ -85,26 +85,26 @@ class Leptris::XML::PlanValue
85
85
 
86
86
  # SCALAR / RAW / CALLBACK string value.
87
87
  def string_value
88
- inc_crossing
88
+ @counter[0] += 1
89
89
  Leptris::XML::FFI.leptris_plan_value_string(@ptr)
90
90
  end
91
91
 
92
92
  # CALLBACK: document byte offset of the source node (0 unknown).
93
93
  def position
94
- inc_crossing
94
+ @counter[0] += 1
95
95
  Leptris::XML::FFI.leptris_plan_value_position(@ptr)
96
96
  end
97
97
 
98
98
  # ELEMENT: child-value count. COLLECTION: item count.
99
99
  def count
100
- inc_crossing
100
+ @counter[0] += 1
101
101
  Leptris::XML::FFI.leptris_plan_value_count(@ptr)
102
102
  end
103
103
 
104
104
  # ELEMENT child value / COLLECTION item at +i+ (nil out of
105
105
  # range). Borrowed: the owning root must stay alive.
106
106
  def at(i)
107
- inc_crossing
107
+ @counter[0] += 1
108
108
  ptr = Leptris::XML::FFI.leptris_plan_value_at(@ptr, i)
109
109
  return nil if ptr.null?
110
110
  child_plan = child_plan_for_position(ptr)
@@ -114,7 +114,7 @@ class Leptris::XML::PlanValue
114
114
 
115
115
  # ELEMENT: attribute value by wire_name (nil when absent).
116
116
  def attribute(wire_name)
117
- inc_crossing
117
+ @counter[0] += 1
118
118
  Leptris::XML::FFI.leptris_plan_value_attribute(@ptr, wire_name.to_s)
119
119
  end
120
120
 
@@ -149,7 +149,7 @@ class Leptris::XML::PlanValue
149
149
  # (0 when absent). Collection rows appear as Arrays; callbacks
150
150
  # are emitted as +{value:, position:, type_tag:}+ like #to_ruby.
151
151
  def as_kwarg_hash
152
- inc_crossing # the kind probe + name + type_tag are one logical fetch
152
+ @counter[0] += 1 # the kind probe + name + type_tag are one logical fetch
153
153
  case kind
154
154
  when :element
155
155
  {
@@ -194,9 +194,6 @@ class Leptris::XML::PlanValue
194
194
 
195
195
  private
196
196
 
197
- def inc_crossing
198
- @counter[0] += 1
199
- end
200
197
 
201
198
  def kwarg_attributes
202
199
  ((@plan && @plan[:attributes]) || []).each_with_object({}) do |row, result|
@@ -290,7 +287,7 @@ class Leptris::XML::PlanValue
290
287
  rows = @plan[:children] || []
291
288
  row = rows.size == 1 ? rows.first : nil
292
289
  unless row
293
- inc_crossing
290
+ @counter[0] += 1
294
291
  child_name = Leptris::XML::FFI.leptris_plan_value_name(ptr)
295
292
  row = rows.find { |r| r[:name] == child_name }
296
293
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: leptris
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.255.0
4
+ version: 1.9.255.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-09-26 00:00:00.000000000 Z
11
+ date: 2026-09-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi