leptris 1.9.174.1 → 1.9.174.3

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: cd97282aa1d1c93dd600491c6209a122c436effa4cf65e9c3f5e71d830343aac
4
- data.tar.gz: 28e2d7a97cb72d10059674d512ffc657b3a22d3c2be28b1002c45192c05cb11a
3
+ metadata.gz: 741e98553c4d88169d4c2f8ec0fb793eeebd80ffd8b00771fcfb0be218721f64
4
+ data.tar.gz: 5e02a23549a4e1c708dc40710b499e64fcd83150772dd08c8bbeac7fc77f0718
5
5
  SHA512:
6
- metadata.gz: 687c5a22ea2c1bff192be56174588d1fdcb6cf6441bee2071e197950cbd095f123761bb92e15da497d0c30d9cdc394ca5c75514be82bcc98fde0ad449a80c44c
7
- data.tar.gz: 468d6e81dbb1c7716da5bff232cde73cf247d04d0f895a842be8511aef07abfb8b2ad2f2f872bca25a8995397be27b2b89fc43d21c2cebb172622856dd106eac
6
+ metadata.gz: b1a3b25cd5b770b6edfb06bc2d448dacd691dc7e5af3fb301454fb1057e419c5fc779e4c9556a3f3b4be654ea5ec36be8f8e5c4e0f7ab16af380b6986da3bcb5
7
+ data.tar.gz: 85e81207c2abaca9a80af5dcfb33ae4e4f83c2ec24950b16d38fcb239bf7fa4d01b40d4d88b7fa19e019ba35a9ce58984c823e3f049fc47492459ebde53aa47b
data/CHANGELOG.md CHANGED
@@ -5,6 +5,39 @@ 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.3] - 2026-09-15
9
+
10
+ ### Changed
11
+
12
+ - **Lazy FFI::Pointer on wrappers (TODO.perf/19)**: the node's
13
+ canonical state is the Integer address; the Pointer
14
+ materializes only when read — the C construction faces stop
15
+ minting one per node (~200ns + one allocation each, and
16
+ proportionally less GC). Public `c_ptr` API and identity
17
+ unchanged. Measured (load 22-25): GC-amortized
18
+ `Document.create` ~5-6µs → ~4.0µs; `NativeNode#[]` 78ns
19
+ (budget 70), `#content` 45ns (budget 58 met).
20
+ - **CSS translation cache (TODO.perf/20)**: repeat selectors skip
21
+ the per-call regex translation straight to the compiled handle;
22
+ the single-path xpath shape no longer allocates a joined
23
+ String per call.
24
+ - **`Element#key?` consults the attribute memo (TODO.perf/21)**
25
+ before the engine round-trip; miss semantics unchanged.
26
+
27
+ ## [1.9.174.2] - 2026-09-15
28
+
29
+ ### Changed
30
+
31
+ - **The native layer returns to Windows, one DLL per Ruby minor
32
+ (#227)**: a PE DLL must bind its build Ruby's runtime, so the
33
+ Windows platform gems now ship `native-3.3.so` / `native-3.4.so`
34
+ / `native-4.0.so` and the loader picks by `RUBY_VERSION` at
35
+ require. The release workflow compiles each minor's artifact on
36
+ the Windows legs (a minor unavailable on a runner degrades to
37
+ the loud FFI fallback for that cell); local `rake compile` on
38
+ Windows builds the current minor's DLL. Windows test legs now
39
+ exercise the full native suite instead of FFI-only.
40
+
8
41
  ## [1.9.174.1] - 2026-09-15
9
42
 
10
43
  ### Fixed
data/Rakefile CHANGED
@@ -128,12 +128,14 @@ task :compile do
128
128
  # the library — no compile at install; resolves libleptris at
129
129
  # require time via dlsym.
130
130
  if Gem.win_platform?
131
- # #207: a PE DLL cannot leave Ruby imports unresolved — the
132
- # bundle would bind to the build Ruby's x64-ucrt-rubyNNN.dll
133
- # and fail on every other minor. No bundle ships for
134
- # Windows; the FFI surface is the Windows contract (the
135
- # auto-enable warns and falls back).
136
- puts "Windows: native read layer not built FFI is the Windows surface (#207)"
131
+ # #207: a PE DLL cannot leave Ruby imports unresolved — it
132
+ # binds the build Ruby's x64-ucrt-rubyNNN.dll. The Windows
133
+ # gems therefore ship one DLL per supported Ruby minor
134
+ # (native-<minor>.so), and native_layer picks by RUBY_VERSION
135
+ # (#227). Local compile builds just the current minor's DLL;
136
+ # the release workflow runs the build script under 3.3/3.4/
137
+ # 4.0 to produce the full set.
138
+ sh "#{RbConfig.ruby} ext/build_windows_native.rb"
137
139
  else
138
140
  ext_dir = "ext/leptris/native"
139
141
  Dir.chdir(ext_dir) do
@@ -283,9 +285,10 @@ platforms.each do |platform|
283
285
  spec.platform = Gem::Platform.new(platform)
284
286
  spec.files += Dir.glob("lib/libleptris.{dll,so,dylib}")
285
287
  spec.files += Dir.glob("lib/{libutf8proc.3.dylib,libutf8proc.so.3,libutf8proc.so,utf8proc.dll}")
286
- # #207: no native bundle in Windows gems (PE cannot resolve
287
- # Ruby imports without binding the build Ruby's DLL).
288
- unless platform.include?("mingw")
288
+ if platform.include?("mingw")
289
+ # #207/#227: per-Ruby-minor DLLs (PE must bind its Ruby).
290
+ spec.files += Dir.glob("lib/leptris/xml/native-*.so")
291
+ else
289
292
  spec.files += Dir.glob("lib/leptris/xml/native.{bundle,so,dll}")
290
293
  end
291
294
  task = Gem::PackageTask.new(spec)
@@ -0,0 +1,39 @@
1
+ # 19 — Lazy FFI::Pointer on wrappers
2
+
3
+ Status: DONE (1.9.174.3)
4
+
5
+ Every binding wrapper stores an FFI::Pointer (@c_ptr), and the C
6
+ construction faces mint one per node via
7
+ rb_funcall(FFI::Pointer, :new) — ~200ns plus a Ruby object and
8
+ its GC visit per node, paid at creation even though most nodes'
9
+ Pointers are never read (the hot reads are memoized or go through
10
+ the address-based native faces).
11
+
12
+ Store @c_address (Integer) as the canonical truth on every
13
+ wrapper; `c_ptr` becomes a lazy materializer
14
+ (@c_ptr ||= FFI::Pointer.new(@c_address)) preserving the public
15
+ API. Internal call sites: `@c_ptr.address` collapses to
16
+ @c_address (also skipping the #address dispatch), and remaining
17
+ internal @c_ptr reads go through the c_ptr reader (same cost as
18
+ the old attr_reader). The C faces stop constructing Pointers
19
+ entirely — they already hold the address.
20
+
21
+ Gates: suite green both modes (a missed site fails loudly — nil
22
+ to FFI raises TypeError); wrapper identity and c_ptr stability
23
+ unchanged; creation faces drop ~200ns and one allocation per
24
+ node; fresh-doc build row improves accordingly.
25
+
26
+ ## Outcome (1.9.174.3)
27
+
28
+ @c_address is the canonical truth on every Node wrapper; c_ptr
29
+ materializes lazily (@c_ptr ||= FFI::Pointer.new(@c_address)),
30
+ preserving the public API and identity. All internal @c_ptr
31
+ reads became c_ptr reader calls; the 15 @c_ptr.address sites
32
+ collapsed to @c_address (also skipping the #address dispatch).
33
+ The C construction faces (create element/text, bulk children,
34
+ every xpath materialization) stopped minting Pointers entirely —
35
+ one allocation and ~200ns saved per node, and proportionally less
36
+ GC. Document keeps its eager reader (documents are few).
37
+ Measured under host load 22-25: GC-amortized Document.create
38
+ ~5-6µs -> ~4.0µs; NativeNode#[] 78ns (budget 70 — clear at
39
+ load <10), content 45ns (budget 58 met).
@@ -0,0 +1,24 @@
1
+ # 20 — CSS translation cache
2
+
3
+ Status: DONE (1.9.174.3)
4
+
5
+ Searchable#css/#at_css run CssToXPath.convert on every call —
6
+ pure Ruby regex/string work (~300ns+) — before the compiled-
7
+ expression cache (TODO.perf/15) can hit on the result. The
8
+ translation is a pure function of (selector, prefix); cache it
9
+ with the same bounded-LRU shape (64 entries, Hash#shift
10
+ eviction, GVL-safe), so repeat selectors skip straight to the
11
+ compiled handle.
12
+
13
+ Gates: css/at_css results identical; repeat-call rows improve by
14
+ the translation cost; the join in xpath()'s expression build is
15
+ also skipped for the single-String-path shape (join minted a
16
+ fresh String per call even for one path).
17
+
18
+ ## Outcome (1.9.174.3)
19
+
20
+ Searchable.css_expression: bounded LRU (64, shared limit) keyed
21
+ on prefix+selector; css/at_css translate through it so repeat
22
+ selectors skip the regex machinery straight to the compiled-
23
+ expression cache. The single-String-path shape of xpath/at_xpath
24
+ no longer joins (join minted a fresh String per call).
@@ -0,0 +1,20 @@
1
+ # 21 — key? consults the attribute memo
2
+
3
+ Status: DONE (1.9.174.3)
4
+
5
+ Element#key?/has_attribute? pays an engine FFI round-trip per
6
+ call even when the versioned @attr_values memo already proves
7
+ presence. Consult the memo first (hit = true; only a miss falls
8
+ through to the engine + written-name fallback — the memo cannot
9
+ prove ABSENCE for a partial fill).
10
+
11
+ Gates: key? true for memoized names, unchanged miss semantics
12
+ (undeclared-prefix fallback intact), readonly/namespace specs
13
+ green.
14
+
15
+ ## Outcome (1.9.174.3)
16
+
17
+ Element#key? consults the versioned @attr_values memo first —
18
+ presence-proven hits skip the engine round-trip; partial-fill
19
+ misses still fall through to the engine + written-name fallback
20
+ (the memo cannot prove absence).
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Builds the native read layer DLL for the CURRENT Ruby and
4
+ # installs it under its minor-versioned name
5
+ # (lib/leptris/xml/native-<major.minor>.so).
6
+ #
7
+ # #207/#227: a PE DLL cannot resolve Ruby imports lazily — it
8
+ # must bind the build Ruby's x64-ucrt-rubyNNN.dll. One DLL per
9
+ # supported minor therefore ships in the Windows platform gems,
10
+ # and native_layer picks by RUBY_VERSION at require.
11
+ #
12
+ # Standalone by design: the release workflow runs this under each
13
+ # Ruby minor (3.3/3.4/4.0) with no bundler context.
14
+
15
+ require "rbconfig"
16
+ require "fileutils"
17
+
18
+ root = File.expand_path("..", __dir__)
19
+ ext_dir = File.join(root, "ext", "leptris", "native")
20
+ minor = RUBY_VERSION[/\A\d+\.\d+/]
21
+
22
+ Dir.chdir(ext_dir) do
23
+ system(RbConfig.ruby, "extconf.rb") or abort "extconf failed under #{RUBY_VERSION}"
24
+ # mkmf's link binds the CURRENT Ruby's runtime DLL — exactly
25
+ # what the versioned naming is for.
26
+ success = system("make")
27
+ abort "make failed under #{RUBY_VERSION}" unless success
28
+ so = Dir.glob("native.{so,dll}").first
29
+ abort "native bundle not produced under #{RUBY_VERSION}" unless so
30
+ dest = File.join(root, "lib", "leptris", "xml", "native-#{minor}.so")
31
+ # The artifact must reference only this minor's Ruby DLL.
32
+ imported = `strings #{so} 2>/dev/null`[/[a-z0-9-]*ruby\d{3,}\.dll/i]
33
+ if imported && !imported.include?("ruby#{minor.delete('.')}")
34
+ abort "#{so} imports #{imported} but was built under #{RUBY_VERSION} — refuse to mis-name it"
35
+ end
36
+ FileUtils.cp(so, dest)
37
+ puts "Installed native layer for Ruby #{minor} -> #{dest} (#{imported || 'no ruby dll string found'})"
38
+ end
@@ -870,9 +870,8 @@ static VALUE bulk_children_impl(VALUE document, VALUE parent_addr,
870
870
  key = ULL2NUM((uint64_t)(uintptr_t)buf[i]);
871
871
  node = rb_hash_aref(cache, key);
872
872
  if (NIL_P(node)) {
873
- VALUE ptr = rb_funcall(c_ffi_pointer, id_ptr_new, 1, key);
874
873
  node = rb_obj_alloc(binding_klass_for(kinds[i]));
875
- rb_iv_set(node, "@c_ptr", ptr);
874
+ rb_iv_set(node, "@c_address", key);
876
875
  rb_iv_set(node, "@document", document);
877
876
  rb_iv_set(node, "@parent", Qnil);
878
877
  rb_iv_set(node, "@structure_memoizable", Qtrue);
@@ -965,9 +964,8 @@ static VALUE materialize_xp_entry(VALUE document, VALUE cache,
965
964
  case 0: /* element */
966
965
  node = rb_hash_aref(cache, key);
967
966
  if (NIL_P(node)) {
968
- VALUE p = rb_funcall(c_ffi_pointer, id_ptr_new, 1, key);
969
967
  node = rb_obj_alloc(c_b_element);
970
- rb_iv_set(node, "@c_ptr", p);
968
+ rb_iv_set(node, "@c_address", key);
971
969
  rb_iv_set(node, "@document", document);
972
970
  rb_iv_set(node, "@parent", Qnil);
973
971
  rb_iv_set(node, "@structure_memoizable", Qtrue);
@@ -980,9 +978,8 @@ static VALUE materialize_xp_entry(VALUE document, VALUE cache,
980
978
  {
981
979
  const char *name = f_xp_node_name(result, idx);
982
980
  const char *value = f_xp_node_value(result, idx);
983
- VALUE p = rb_funcall(c_ffi_pointer, id_ptr_new, 1, key);
984
981
  node = rb_obj_alloc(c_b_result_attr);
985
- rb_iv_set(node, "@c_ptr", p);
982
+ rb_iv_set(node, "@c_address", key);
986
983
  rb_iv_set(node, "@document", document);
987
984
  rb_iv_set(node, "@attr_name",
988
985
  name ? rb_utf8_str_new_cstr(name) : Qnil);
@@ -998,8 +995,7 @@ static VALUE materialize_xp_entry(VALUE document, VALUE cache,
998
995
  if (nt == 8) { /* NODE_SYNTHETIC_TEXT */
999
996
  s = f_xp_node_value(result, idx);
1000
997
  node = rb_obj_alloc(c_b_result_text);
1001
- rb_iv_set(node, "@c_ptr",
1002
- rb_funcall(c_ffi_pointer, id_ptr_new, 1, key));
998
+ rb_iv_set(node, "@c_address", key);
1003
999
  rb_iv_set(node, "@document", document);
1004
1000
  rb_iv_set(node, "@value",
1005
1001
  s ? rb_utf8_str_new_cstr(s) : Qnil);
@@ -1007,9 +1003,8 @@ static VALUE materialize_xp_entry(VALUE document, VALUE cache,
1007
1003
  }
1008
1004
  node = rb_hash_aref(cache, key);
1009
1005
  if (NIL_P(node)) {
1010
- VALUE p = rb_funcall(c_ffi_pointer, id_ptr_new, 1, key);
1011
1006
  node = rb_obj_alloc(binding_klass_for(nt));
1012
- rb_iv_set(node, "@c_ptr", p);
1007
+ rb_iv_set(node, "@c_address", key);
1013
1008
  rb_iv_set(node, "@document", document);
1014
1009
  rb_iv_set(node, "@parent", Qnil);
1015
1010
  rb_iv_set(node, "@structure_memoizable", Qtrue);
@@ -1096,9 +1091,8 @@ static VALUE nf_create_binding_element(VALUE self, VALUE document,
1096
1091
  if (!ptr)
1097
1092
  return Qnil; /* caller raises with the error channel */
1098
1093
  node = rb_obj_alloc(c_b_element);
1099
- rb_iv_set(node, "@c_ptr",
1100
- rb_funcall(c_ffi_pointer, id_ptr_new, 1,
1101
- ULL2NUM((uint64_t)(uintptr_t)ptr)));
1094
+ rb_iv_set(node, "@c_address",
1095
+ ULL2NUM((uint64_t)(uintptr_t)ptr));
1102
1096
  rb_iv_set(node, "@document", document);
1103
1097
  rb_iv_set(node, "@parent", Qnil);
1104
1098
  rb_iv_set(node, "@structure_memoizable", Qtrue);
@@ -1122,9 +1116,8 @@ static VALUE nf_create_binding_text(VALUE self, VALUE document,
1122
1116
  if (!ptr)
1123
1117
  return Qnil;
1124
1118
  node = rb_obj_alloc(c_b_text);
1125
- rb_iv_set(node, "@c_ptr",
1126
- rb_funcall(c_ffi_pointer, id_ptr_new, 1,
1127
- ULL2NUM((uint64_t)(uintptr_t)ptr)));
1119
+ rb_iv_set(node, "@c_address",
1120
+ ULL2NUM((uint64_t)(uintptr_t)ptr));
1128
1121
  rb_iv_set(node, "@document", document);
1129
1122
  rb_iv_set(node, "@parent", Qnil);
1130
1123
  rb_iv_set(node, "@structure_memoizable", Qtrue);
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Leptris
4
- VERSION = "1.9.174.1"
4
+ VERSION = "1.9.174.3"
5
5
  end
@@ -6,7 +6,7 @@ class Leptris::XML::CDATA < Leptris::XML::Text
6
6
  def content
7
7
  return @content if memo_hit?(@content_version)
8
8
  ensure_alive!
9
- result = Leptris::XML::FFI.leptris_cdata_node_get_content(@c_ptr)
9
+ result = Leptris::XML::FFI.leptris_cdata_node_get_content(c_ptr)
10
10
  if @document
11
11
  @content = result
12
12
  @content_version = @document.version
@@ -17,7 +17,7 @@ class Leptris::XML::CDATA < Leptris::XML::Text
17
17
  def content=(new_content)
18
18
  ensure_writable!
19
19
  Leptris::XML::FFI.check_status(
20
- Leptris::XML::FFI.leptris_cdata_node_set_content(@c_ptr, new_content.to_s))
20
+ Leptris::XML::FFI.leptris_cdata_node_set_content(c_ptr, new_content.to_s))
21
21
  new_content
22
22
  end
23
23
  end
@@ -6,7 +6,7 @@ class Leptris::XML::Comment < Leptris::XML::Node
6
6
  def content
7
7
  return @content if memo_hit?(@content_version)
8
8
  ensure_alive!
9
- result = Leptris::XML::FFI.leptris_comment_node_get_content(@c_ptr)
9
+ result = Leptris::XML::FFI.leptris_comment_node_get_content(c_ptr)
10
10
  if @document
11
11
  @content = result
12
12
  @content_version = @document.version
@@ -17,7 +17,7 @@ class Leptris::XML::Comment < Leptris::XML::Node
17
17
  def content=(new_content)
18
18
  ensure_writable!
19
19
  Leptris::XML::FFI.check_status(
20
- Leptris::XML::FFI.leptris_comment_node_set_content(@c_ptr, new_content.to_s))
20
+ Leptris::XML::FFI.leptris_comment_node_set_content(c_ptr, new_content.to_s))
21
21
  new_content
22
22
  end
23
23
  end
@@ -9,24 +9,24 @@ class Leptris::XML::DocType
9
9
  end
10
10
 
11
11
  def name
12
- Leptris::XML::FFI.leptris_doctype_get_name(@c_ptr)
12
+ Leptris::XML::FFI.leptris_doctype_get_name(c_ptr)
13
13
  end
14
14
  alias_method :node_name, :name
15
15
 
16
16
  def root_name
17
- Leptris::XML::FFI.leptris_doctype_get_root_name(@c_ptr)
17
+ Leptris::XML::FFI.leptris_doctype_get_root_name(c_ptr)
18
18
  end
19
19
 
20
20
  def public_id
21
- Leptris::XML::FFI.leptris_doctype_get_public_id(@c_ptr)
21
+ Leptris::XML::FFI.leptris_doctype_get_public_id(c_ptr)
22
22
  end
23
23
 
24
24
  def system_id
25
- Leptris::XML::FFI.leptris_doctype_get_system_id(@c_ptr)
25
+ Leptris::XML::FFI.leptris_doctype_get_system_id(c_ptr)
26
26
  end
27
27
 
28
28
  def internal_subset
29
- Leptris::XML::FFI.leptris_doctype_get_internal_subset(@c_ptr)
29
+ Leptris::XML::FFI.leptris_doctype_get_internal_subset(c_ptr)
30
30
  end
31
31
 
32
32
  def external_id
@@ -32,9 +32,9 @@ class Leptris::XML::Document
32
32
 
33
33
  def initialize(c_ptr = nil, freed = Freed.new(:alive))
34
34
  @c_ptr = c_ptr
35
- # Plain-Integer address twin of @c_ptr: the native layer reads
35
+ # Plain-Integer address twin of c_ptr: the native layer reads
36
36
  # this ivar directly (no method dispatch), nil'ed exactly when
37
- # @c_ptr is.
37
+ # c_ptr is.
38
38
  @c_address = c_ptr&.address
39
39
  @freed = freed
40
40
  @readonly = false
@@ -209,13 +209,13 @@ class Leptris::XML::Document
209
209
 
210
210
  def root
211
211
  raise Leptris::XML::UseAfterFreeError if @freed.state == :freed
212
- return nil if @c_ptr.nil?
212
+ return nil if c_ptr.nil?
213
213
  # Version-stamped memo (TODO.perf/13): root changes only
214
214
  # through mutations that advance @version (root=, unlink) or
215
- # #free (which nils @c_ptr above) — the entry point of every
215
+ # #free (which nils c_ptr above) — the entry point of every
216
216
  # pipeline stops paying FFI + wrap per call.
217
217
  return @root if @root_version == @version
218
- ptr = Leptris::XML::FFI.leptris_document_root(@c_ptr)
218
+ ptr = Leptris::XML::FFI.leptris_document_root(c_ptr)
219
219
  result = ptr.null? ? nil : Leptris::XML::Node.wrap(ptr, self)
220
220
  @root = result
221
221
  @root_version = @version
@@ -231,7 +231,7 @@ class Leptris::XML::Document
231
231
  def node
232
232
  raise Leptris::XML::UseAfterFreeError if @freed.state == :freed
233
233
  @node ||= Leptris::XML::Node.wrap(
234
- Leptris::XML::FFI.leptris_document_node(@c_ptr), self)
234
+ Leptris::XML::FFI.leptris_document_node(c_ptr), self)
235
235
  end
236
236
 
237
237
  # The document's children, via the document node: prolog
@@ -250,7 +250,7 @@ class Leptris::XML::Document
250
250
  doc_node = node
251
251
  return Leptris::XML::NodeSet.new(self, []) if doc_node.nil?
252
252
  kids = doc_node.children.to_a
253
- root_ptr = Leptris::XML::FFI.leptris_document_root(@c_ptr)
253
+ root_ptr = Leptris::XML::FFI.leptris_document_root(c_ptr)
254
254
  return kids if root_ptr.null?
255
255
  return kids if kids.any? { |child| child.c_ptr == root_ptr }
256
256
  root = Leptris::XML::Node.wrap(root_ptr, self)
@@ -289,7 +289,7 @@ class Leptris::XML::Document
289
289
  Leptris::XML::Element.lift_namespaces_for_adoption(element, {})
290
290
  end
291
291
  Leptris::XML::FFI.check_status(
292
- Leptris::XML::FFI.leptris_document_set_root(@c_ptr, element.c_ptr))
292
+ Leptris::XML::FFI.leptris_document_set_root(c_ptr, element.c_ptr))
293
293
  @version += 1
294
294
  # Seed the root memo through wrap: a cross-document element
295
295
  # must enter THIS document's identity cache with @document
@@ -308,7 +308,7 @@ class Leptris::XML::Document
308
308
  raise Leptris::XML::Error, "leptris_element_create failed" if node.nil?
309
309
  return node
310
310
  end
311
- ptr = Leptris::XML::FFI.leptris_element_create(@c_ptr, name)
311
+ ptr = Leptris::XML::FFI.leptris_element_create(c_ptr, name)
312
312
  raise Leptris::XML::Error, "leptris_element_create failed" if ptr.null?
313
313
  Leptris::XML::Node.wrap_fresh(ptr, self, Leptris::XML::FFI::NODE_ELEMENT)
314
314
  end
@@ -319,25 +319,25 @@ class Leptris::XML::Document
319
319
  raise Leptris::XML::Error, "leptris_text_node_create failed" if node.nil?
320
320
  return node
321
321
  end
322
- ptr = Leptris::XML::FFI.leptris_text_node_create(@c_ptr, content.to_s)
322
+ ptr = Leptris::XML::FFI.leptris_text_node_create(c_ptr, content.to_s)
323
323
  raise Leptris::XML::Error, "leptris_text_node_create failed" if ptr.null?
324
324
  Leptris::XML::Node.wrap_fresh(ptr, self, Leptris::XML::FFI::NODE_TEXT)
325
325
  end
326
326
 
327
327
  def create_comment(content)
328
- ptr = Leptris::XML::FFI.leptris_comment_node_create(@c_ptr, content.to_s)
328
+ ptr = Leptris::XML::FFI.leptris_comment_node_create(c_ptr, content.to_s)
329
329
  raise Leptris::XML::Error, "leptris_comment_node_create failed" if ptr.null?
330
330
  Leptris::XML::Node.wrap_fresh(ptr, self, Leptris::XML::FFI::NODE_COMMENT)
331
331
  end
332
332
 
333
333
  def create_cdata(content)
334
- ptr = Leptris::XML::FFI.leptris_cdata_node_create(@c_ptr, content.to_s)
334
+ ptr = Leptris::XML::FFI.leptris_cdata_node_create(c_ptr, content.to_s)
335
335
  raise Leptris::XML::Error, "leptris_cdata_node_create failed" if ptr.null?
336
336
  Leptris::XML::Node.wrap_fresh(ptr, self, Leptris::XML::FFI::NODE_CDATA)
337
337
  end
338
338
 
339
339
  def create_processing_instruction(target, data = "")
340
- ptr = Leptris::XML::FFI.leptris_pi_node_create(@c_ptr, target.to_s, data.to_s)
340
+ ptr = Leptris::XML::FFI.leptris_pi_node_create(c_ptr, target.to_s, data.to_s)
341
341
  raise Leptris::XML::Error, "leptris_pi_node_create failed" if ptr.null?
342
342
  Leptris::XML::Node.wrap_fresh(ptr, self, Leptris::XML::FFI::NODE_PI)
343
343
  end
@@ -347,14 +347,14 @@ class Leptris::XML::Document
347
347
  end
348
348
 
349
349
  def dup
350
- raw = Leptris::XML::FFI.leptris_document_copy(@c_ptr)
350
+ raw = Leptris::XML::FFI.leptris_document_copy(c_ptr)
351
351
  raise Leptris::XML::Error, "leptris_document_copy failed" if raw.null?
352
352
  self.class.wrap(raw)
353
353
  end
354
354
  alias_method :clone, :dup
355
355
 
356
356
  def doctype
357
- ptr = Leptris::XML::FFI.leptris_document_internal_subset(@c_ptr)
357
+ ptr = Leptris::XML::FFI.leptris_document_internal_subset(c_ptr)
358
358
  return nil if ptr.null?
359
359
  Leptris::XML::DocType.new(ptr, self)
360
360
  end
@@ -368,18 +368,18 @@ class Leptris::XML::Document
368
368
  # display-oriented and not round-trip-guaranteed).
369
369
  def to_xml(indent: 0, no_decl: false, encoding: nil, indent_text: false)
370
370
  raise Leptris::XML::UseAfterFreeError if @freed.state == :freed
371
- return "" if @c_ptr.nil?
371
+ return "" if c_ptr.nil?
372
372
  case indent_text
373
373
  when String
374
374
  return Leptris::XML::Serialization.to_xml_indent_unit(
375
- @c_ptr, indent_text, indent: indent, no_decl: no_decl,
375
+ c_ptr, indent_text, indent: indent, no_decl: no_decl,
376
376
  encoding: encoding)
377
377
  when true
378
378
  return Leptris::XML::Serialization.to_xml_display(
379
- @c_ptr, indent: indent, no_decl: no_decl, encoding: encoding)
379
+ c_ptr, indent: indent, no_decl: no_decl, encoding: encoding)
380
380
  end
381
381
  Leptris::XML::Serialization.to_xml(
382
- Leptris::XML::Serialization::DOCUMENT_SERIALIZE_INTO, @c_ptr,
382
+ Leptris::XML::Serialization::DOCUMENT_SERIALIZE_INTO, c_ptr,
383
383
  indent: indent, no_decl: no_decl, encoding: encoding)
384
384
  end
385
385
  alias_method :to_s, :to_xml
@@ -391,7 +391,7 @@ class Leptris::XML::Document
391
391
  no_decl: opts.fetch(:no_decl, false),
392
392
  encoding: opts[:encoding])
393
393
  status = Leptris::XML::FFI.leptris_document_save_file(
394
- @c_ptr, path, opts_struct.pointer)
394
+ c_ptr, path, opts_struct.pointer)
395
395
  Leptris::XML::FFI.check_status(status)
396
396
  self
397
397
  end
@@ -402,11 +402,11 @@ class Leptris::XML::Document
402
402
  exclusive: false,
403
403
  mode: nil)
404
404
  raise Leptris::XML::UseAfterFreeError if @freed.state == :freed
405
- return "" if @c_ptr.nil?
405
+ return "" if c_ptr.nil?
406
406
  resolved_mode = mode || (exclusive ? Leptris::XML::FFI::C14N_MODE_EXCLUSIVE
407
407
  : Leptris::XML::FFI::C14N_MODE_CANONICAL)
408
408
  Leptris::XML::Serialization.canonicalize(
409
- Leptris::XML::FFI.method(:leptris_c14n_canonicalize_ex), @c_ptr,
409
+ Leptris::XML::FFI.method(:leptris_c14n_canonicalize_ex), c_ptr,
410
410
  version: version, mode: resolved_mode,
411
411
  inclusive_namespaces: inclusive_namespaces,
412
412
  with_comments: with_comments)
@@ -416,7 +416,7 @@ class Leptris::XML::Document
416
416
  def free
417
417
  return if @freed.state == :freed
418
418
  @freed.state = :freed
419
- Leptris::XML::FFI.leptris_document_free(@c_ptr) unless @c_ptr.nil?
419
+ Leptris::XML::FFI.leptris_document_free(c_ptr) unless c_ptr.nil?
420
420
  @c_ptr = nil
421
421
  @c_address = nil
422
422
  @wrapper_cache&.clear
@@ -432,7 +432,7 @@ class Leptris::XML::Document
432
432
  # handlers. Returns self for chaining.
433
433
  def exslt
434
434
  Leptris::XML::FFI.check_status(
435
- Leptris::XML::FFI.leptris_exslt_enable(@c_ptr))
435
+ Leptris::XML::FFI.leptris_exslt_enable(c_ptr))
436
436
  self
437
437
  end
438
438
 
@@ -440,11 +440,11 @@ class Leptris::XML::Document
440
440
  # an array of [target, data] pairs in document order.
441
441
  def processing_instructions
442
442
  return @processing_instructions if @pi_version == @version
443
- count = Leptris::XML::FFI.leptris_document_pi_count(@c_ptr)
443
+ count = Leptris::XML::FFI.leptris_document_pi_count(c_ptr)
444
444
  result = count.times.map do |i|
445
- [Leptris::XML::FFI.leptris_document_pi_target(@c_ptr, i),
445
+ [Leptris::XML::FFI.leptris_document_pi_target(c_ptr, i),
446
446
  Leptris::XML::FFI.read_pi_data(
447
- Leptris::XML::FFI.leptris_document_pi_data(@c_ptr, i)).to_s]
447
+ Leptris::XML::FFI.leptris_document_pi_data(c_ptr, i)).to_s]
448
448
  end
449
449
  @processing_instructions = result
450
450
  @pi_version = @version
@@ -460,10 +460,10 @@ class Leptris::XML::Document
460
460
  raise Leptris::XML::UseAfterFreeError if @freed.state == :freed
461
461
  if target_or_index.is_a?(Integer)
462
462
  ptr = Leptris::XML::FFI.leptris_document_remove_pi(
463
- @c_ptr, nil, target_or_index)
463
+ c_ptr, nil, target_or_index)
464
464
  else
465
465
  ptr = Leptris::XML::FFI.leptris_document_remove_pi(
466
- @c_ptr, target_or_index.to_s, 0)
466
+ c_ptr, target_or_index.to_s, 0)
467
467
  end
468
468
  return nil if ptr.null?
469
469
  @version += 1
@@ -473,7 +473,7 @@ class Leptris::XML::Document
473
473
  # Append a document-level processing instruction. Returns self.
474
474
  def add_pi(target, data = "")
475
475
  witness = Leptris::XML::FFI.leptris_document_add_pi(
476
- @c_ptr, target.to_s, data.to_s)
476
+ c_ptr, target.to_s, data.to_s)
477
477
  raise Leptris::XML::Error, "leptris_document_add_pi failed" if witness.null?
478
478
  @version += 1
479
479
  self
@@ -485,7 +485,7 @@ class Leptris::XML::Document
485
485
  # this is the writer). Returns self.
486
486
  def add_comment(content)
487
487
  witness = Leptris::XML::FFI.leptris_document_add_comment(
488
- @c_ptr, content.to_s)
488
+ c_ptr, content.to_s)
489
489
  raise Leptris::XML::Error,
490
490
  "leptris_document_add_comment failed" if witness.null?
491
491
  @version += 1
@@ -497,7 +497,7 @@ class Leptris::XML::Document
497
497
  # (names, content, children, attributes) since they can never go
498
498
  # stale. The C document is also frozen (advisory upstream). One-way.
499
499
  def readonly!
500
- Leptris::XML::FFI.leptris_document_freeze(@c_ptr)
500
+ Leptris::XML::FFI.leptris_document_freeze(c_ptr)
501
501
  @readonly = true
502
502
  self
503
503
  end
@@ -509,7 +509,7 @@ class Leptris::XML::Document
509
509
  # True once #free has run (or the GC finalizer fired) — borrowed
510
510
  # handles check this before dereferencing their c_ptr.
511
511
  def freed?
512
- @freed.state == :freed || @c_ptr.nil?
512
+ @freed.state == :freed || c_ptr.nil?
513
513
  end
514
514
 
515
515
  # Document-level comments — parsed <!-- ... --> outside the
@@ -518,9 +518,9 @@ class Leptris::XML::Document
518
518
  # 1.9.3, upstream #578). Version-memoized like the PI list.
519
519
  def comments
520
520
  return @comments if @comments_version == @version
521
- count = Leptris::XML::FFI.leptris_document_comment_count(@c_ptr)
521
+ count = Leptris::XML::FFI.leptris_document_comment_count(c_ptr)
522
522
  result = Array.new(count) do |i|
523
- Leptris::XML::FFI.leptris_document_comment_content(@c_ptr, i)
523
+ Leptris::XML::FFI.leptris_document_comment_content(c_ptr, i)
524
524
  end
525
525
  @comments = result
526
526
  @comments_version = @version
@@ -545,15 +545,15 @@ class Leptris::XML::Document
545
545
 
546
546
  # The most recent error recorded against this document, or nil.
547
547
  def last_error
548
- msg = Leptris::XML::FFI.leptris_document_last_error(@c_ptr)
548
+ msg = Leptris::XML::FFI.leptris_document_last_error(c_ptr)
549
549
  msg.nil? || msg.empty? ? nil : msg
550
550
  end
551
551
 
552
552
  def name; "document"; end
553
553
  def document; self; end
554
554
  def encoding
555
- return nil if @c_ptr.nil?
556
- Leptris::XML::FFI.leptris_document_encoding(@c_ptr)
555
+ return nil if c_ptr.nil?
556
+ Leptris::XML::FFI.leptris_document_encoding(c_ptr)
557
557
  end
558
558
 
559
559
  include Leptris::XML::Searchable
@@ -25,7 +25,7 @@ class Leptris::XML::DocumentFragment
25
25
  end
26
26
 
27
27
  def children
28
- pointers, kinds = Leptris::XML::FFI.fetch_children(@c_ptr)
28
+ pointers, kinds = Leptris::XML::FFI.fetch_children(c_ptr)
29
29
  nodes = Array.new(pointers.size) do |i|
30
30
  Leptris::XML::Node.wrap(pointers[i], @document, node_type: kinds[i])
31
31
  end