leptris 1.6.0-aarch64-linux → 1.6.1-aarch64-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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -0
  3. data/CLAUDE.md +76 -87
  4. data/lib/leptris/version.rb +1 -1
  5. data/lib/leptris/xml/cdata.rb +1 -0
  6. data/lib/leptris/xml/comment.rb +1 -0
  7. data/lib/leptris/xml/document.rb +29 -14
  8. data/lib/leptris/xml/element.rb +21 -3
  9. data/lib/leptris/xml/ffi.rb +3 -13
  10. data/lib/leptris/xml/node.rb +32 -10
  11. data/lib/leptris/xml/node_set.rb +6 -5
  12. data/lib/leptris/xml/processing_instruction.rb +2 -0
  13. data/lib/leptris/xml/searchable.rb +1 -1
  14. data/lib/leptris/xml/serialization.rb +20 -2
  15. data/lib/leptris/xml/text.rb +1 -0
  16. data/lib/leptris/xml.rb +3 -2
  17. metadata +1 -36
  18. data/TODO.impl/01-architecture.md +0 -217
  19. data/TODO.impl/02-ffi-declarations.md +0 -236
  20. data/TODO.impl/03-document-node-element-nodeset.md +0 -382
  21. data/TODO.impl/04-sax-parser.md +0 -203
  22. data/TODO.impl/05-serialize-c14n-memory-specs-css.md +0 -276
  23. data/docs/ARCHITECTURE.adoc +0 -559
  24. data/docs/BUILD.md +0 -395
  25. data/docs/ERROR_MESSAGES.md +0 -458
  26. data/docs/FFI_ARCHITECTURE.md +0 -439
  27. data/docs/FUTURE_VISION.md +0 -303
  28. data/docs/GITHUB_ACTIONS.md +0 -293
  29. data/docs/OPTIMIZATIONS_IMPLEMENTED.adoc +0 -459
  30. data/docs/PERFORMANCE.adoc +0 -668
  31. data/docs/PERFORMANCE.md +0 -448
  32. data/docs/RELEASE_NOTES_v1.0.0.md +0 -515
  33. data/docs/XPATH_SPEC_COMPLIANCE.md +0 -298
  34. data/docs/completion/leptris.bash +0 -86
  35. data/docs/completion/leptris.zsh +0 -74
  36. data/docs/man/leptris-format.1 +0 -227
  37. data/docs/man/leptris-parse.1 +0 -178
  38. data/docs/man/leptris-xpath.1 +0 -312
  39. data/docs/man/leptris.1 +0 -160
  40. data/docs/v0.9.0_PERFORMANCE_IMPROVEMENTS.md +0 -217
  41. data/docs/v0.9.0_RELEASE_SUMMARY.md +0 -281
  42. data/docs/v1.0.0_CONTINUATION_PLAN.md +0 -172
  43. data/docs/v1.0.0_CONTINUATION_PROMPT.md +0 -382
  44. data/docs/v1.0.0_SESSION_6_CONTINUATION.md +0 -434
  45. data/docs/v1.0.0_SESSION_6_PROMPT.md +0 -231
  46. data/docs/v1.0.0_STATUS_TRACKER.md +0 -224
  47. data/docs/v1.1.0_CONTINUATION_PLAN.md +0 -299
  48. data/docs/v1.1.0_FINAL_CONTINUATION_PLAN.md +0 -201
  49. data/docs/v1.1.0_SESSION_3_PROMPT.md +0 -223
  50. data/docs/v1.1.0_STATUS_TRACKER.md +0 -355
  51. data/docs/xml-performance.adoc +0 -115
  52. data/docs/xpath-performance.adoc +0 -379
@@ -63,15 +63,16 @@ class Leptris::XML::NodeSet
63
63
  def each
64
64
  return enum_for(:each) unless block_given?
65
65
  if @result_ptr
66
- # Batch-fetch all node pointers in one FFI call (leptris_xpath_result_get_nodes,
67
- # libleptris v0.11.4) and wrap each. Saves N-1 FFI calls vs the per-index
68
- # leptris_xpath_result_get loop. Wrappers are still cached per-Document via
69
- # Node.wrap, so a re-iteration of the same NodeSet hits the cache.
66
+ # Batch-fetch all node pointers in one FFI call
67
+ # (leptris_xpath_result_get_nodes_ex copies every node kind, not
68
+ # just elements) and wrap each. Wrappers are cached per-Document
69
+ # via Node.wrap, so re-iteration of the same NodeSet hits it.
70
70
  n = length
71
71
  if n > 0
72
72
  buf = ::FFI::MemoryPointer.new(:pointer, n)
73
73
  begin
74
- copied = Leptris::XML::FFI.leptris_xpath_result_get_nodes(@result_ptr, buf, n)
74
+ copied = Leptris::XML::FFI.leptris_xpath_result_get_nodes_ex(
75
+ @result_ptr, buf, nil, n)
75
76
  copied.times do |i|
76
77
  ptr = buf.get_pointer(i * ::FFI.type_size(:pointer))
77
78
  next if ptr.null?
@@ -11,12 +11,14 @@ class Leptris::XML::ProcessingInstruction < Leptris::XML::Node
11
11
  end
12
12
 
13
13
  def target=(new_target)
14
+ ensure_writable!
14
15
  Leptris::XML::FFI.check_status(
15
16
  Leptris::XML::FFI.leptris_pi_node_set_target(@c_ptr, new_target.to_s))
16
17
  new_target
17
18
  end
18
19
 
19
20
  def data=(new_data)
21
+ ensure_writable!
20
22
  Leptris::XML::FFI.check_status(
21
23
  Leptris::XML::FFI.leptris_pi_node_set_data(@c_ptr, new_data.to_s))
22
24
  new_data
@@ -89,7 +89,7 @@ module Leptris::XML::Searchable
89
89
  end
90
90
 
91
91
  # Shared by Searchable#xpath and Leptris::XML::XPath (compiled
92
- # expressions): wraps a raw TaurusXPathResult pointer into the
92
+ # expressions): wraps a raw XPathResult pointer into the
93
93
  # Ruby-typed result and frees the C handle.
94
94
  def self.wrap_xpath_result(document, result_ptr)
95
95
  type = Leptris::XML::FFI.leptris_xpath_result_type(result_ptr)
@@ -6,11 +6,29 @@
6
6
  # struct, the encoding anchor, the C call, and the read-and-free all
7
7
  # live here.
8
8
  module Leptris::XML::Serialization
9
+ # The default options struct (indent 0, declaration on, no encoding
10
+ # override) is constant: build it once and reuse its pointer on the
11
+ # fast path instead of allocating a fresh struct per call.
12
+ DEFAULT_OPTIONS, _default_anchor = begin
13
+ opts = Leptris::XML::FFI::SerializeOptions.new
14
+ opts[:indent] = 0
15
+ opts[:xml_declaration] = 1
16
+ opts[:encoding] = nil
17
+ [opts, nil]
18
+ end
19
+ private_constant :DEFAULT_OPTIONS
20
+
9
21
  # +ffi_function+ is a bound FFI function taking (c_ptr, opts_ptr):
10
22
  # leptris_document_serialize or leptris_element_serialize.
11
23
  def self.to_xml(ffi_function, c_ptr, indent: 0, no_decl: false, encoding: nil)
12
- opts, _encoding_anchor = build_options(
13
- indent: indent, no_decl: no_decl, encoding: encoding)
24
+ opts =
25
+ if indent.to_i.zero? && !no_decl && encoding.nil?
26
+ DEFAULT_OPTIONS
27
+ else
28
+ opts, _encoding_anchor = build_options(
29
+ indent: indent, no_decl: no_decl, encoding: encoding)
30
+ opts
31
+ end
14
32
  str_ptr = ffi_function.call(c_ptr, opts.pointer)
15
33
  Leptris::XML::FFI.read_owned_string(str_ptr)
16
34
  end
@@ -8,6 +8,7 @@ class Leptris::XML::Text < Leptris::XML::Node
8
8
  end
9
9
 
10
10
  def content=(new_content)
11
+ ensure_writable!
11
12
  Leptris::XML::FFI.check_status(
12
13
  Leptris::XML::FFI.leptris_text_node_set_content(@c_ptr, new_content.to_s))
13
14
  new_content
data/lib/leptris/xml.rb CHANGED
@@ -27,8 +27,8 @@ module Leptris
27
27
 
28
28
  # Nokogiri-style top-level entry points (Nokogiri::XML(...) /
29
29
  # Nokogiri::XML.parse). Delegates to Document.parse.
30
- def self.parse(xml_or_io, options: nil)
31
- Document.parse(xml_or_io, options: options)
30
+ def self.parse(xml_or_io, options: nil, readonly: false)
31
+ Document.parse(xml_or_io, options: options, readonly: readonly)
32
32
  end
33
33
 
34
34
  def self.parse_file(path)
@@ -37,6 +37,7 @@ module Leptris
37
37
 
38
38
  class Error < StandardError; end
39
39
  class ParseError < Error; end
40
+ class ReadOnlyError < Error; end
40
41
  class XPathError < Error; end
41
42
  class UseAfterFreeError < Error; end
42
43
  end
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.6.0
4
+ version: 1.6.1
5
5
  platform: aarch64-linux
6
6
  authors:
7
7
  - Ribose Inc.
@@ -70,43 +70,8 @@ files:
70
70
  - LICENSE.md
71
71
  - README.adoc
72
72
  - Rakefile
73
- - TODO.impl/01-architecture.md
74
- - TODO.impl/02-ffi-declarations.md
75
- - TODO.impl/03-document-node-element-nodeset.md
76
- - TODO.impl/04-sax-parser.md
77
- - TODO.impl/05-serialize-c14n-memory-specs-css.md
78
73
  - benchmark/README.md
79
74
  - benchmark/leptris_vs_nokogiri.rb
80
- - docs/ARCHITECTURE.adoc
81
- - docs/BUILD.md
82
- - docs/ERROR_MESSAGES.md
83
- - docs/FFI_ARCHITECTURE.md
84
- - docs/FUTURE_VISION.md
85
- - docs/GITHUB_ACTIONS.md
86
- - docs/OPTIMIZATIONS_IMPLEMENTED.adoc
87
- - docs/PERFORMANCE.adoc
88
- - docs/PERFORMANCE.md
89
- - docs/RELEASE_NOTES_v1.0.0.md
90
- - docs/XPATH_SPEC_COMPLIANCE.md
91
- - docs/completion/leptris.bash
92
- - docs/completion/leptris.zsh
93
- - docs/man/leptris-format.1
94
- - docs/man/leptris-parse.1
95
- - docs/man/leptris-xpath.1
96
- - docs/man/leptris.1
97
- - docs/v0.9.0_PERFORMANCE_IMPROVEMENTS.md
98
- - docs/v0.9.0_RELEASE_SUMMARY.md
99
- - docs/v1.0.0_CONTINUATION_PLAN.md
100
- - docs/v1.0.0_CONTINUATION_PROMPT.md
101
- - docs/v1.0.0_SESSION_6_CONTINUATION.md
102
- - docs/v1.0.0_SESSION_6_PROMPT.md
103
- - docs/v1.0.0_STATUS_TRACKER.md
104
- - docs/v1.1.0_CONTINUATION_PLAN.md
105
- - docs/v1.1.0_FINAL_CONTINUATION_PLAN.md
106
- - docs/v1.1.0_SESSION_3_PROMPT.md
107
- - docs/v1.1.0_STATUS_TRACKER.md
108
- - docs/xml-performance.adoc
109
- - docs/xpath-performance.adoc
110
75
  - leptris.gemspec
111
76
  - lib/leptris.rb
112
77
  - lib/leptris/version.rb
@@ -1,217 +0,0 @@
1
- # TODO 1 — Architecture: C-backed Nokogiri-compatible Ruby binding
2
-
3
- ## Goal
4
-
5
- Rewrite `leptris-ruby` to be a **thin FFI wrapper** around libleptris v0.4.2,
6
- exposing a **Nokogiri-compatible API**. The C DOM is the single source of
7
- truth — no Ruby-side tree copy.
8
-
9
- ## Current state (problem)
10
-
11
- The existing `leptris-ruby` has:
12
- - A **pure-Ruby** XML tree model (Document < Element, Node, NodeSet)
13
- - A FFI bridge that does a **one-shot copy** from C to Ruby on parse
14
- - A **pure-Ruby XPath engine** (lexer, parser, compiler, VM in Ruby)
15
-
16
- This defeats libleptris's performance: the C library's optimized bytecode
17
- VM + element index are never used. XPath goes through the slow Ruby engine.
18
-
19
- ## Target architecture
20
-
21
- ```
22
- User Ruby code
23
- ↓
24
- Leptris::XML::Document / Node / NodeSet (thin Ruby wrappers)
25
- ↓ FFI
26
- libleptris v0.4.2 (C99: DOM, XPath bytecode VM, element index)
27
- ```
28
-
29
- Key principles:
30
- - **Every Ruby method is a single FFI call** to the C library.
31
- - **No Ruby-side tree copy.** The C DOM is the truth; Ruby objects are
32
- handles (wrapping opaque pointers).
33
- - **XPath goes through C.** `doc.xpath('//book')` calls
34
- `leptris_xpath_eval` directly. No Ruby XPath engine.
35
- - **SAX goes through C.** `Leptris::XML::SAX::Parser` wraps
36
- `leptris_sax_parse` with Ruby callback dispatch.
37
-
38
- ## Module structure (Nokogiri-compatible)
39
-
40
- ```ruby
41
- module Leptris
42
- module XML
43
- # Top-level parse entry points
44
- def self.parse(string_or_io) → Document
45
- def self.parse_options → ParseOptions
46
-
47
- class Document < Node
48
- def root → Element (or nil)
49
- def create_element(name) → Element
50
- def create_text_node(text) → Text
51
- def to_xml(options) → String
52
- def xpath(expr) → NodeSet | Float | String | Boolean
53
- def at_xpath(expr) → Node (or nil)
54
- def search(expr) → NodeSet
55
- def canonicalize(...) → String
56
- def free → void (explicit)
57
- end
58
-
59
- class Node
60
- include Searchable
61
-
62
- def name → String
63
- def content / text → String
64
- def [](attr_name) → String (or nil)
65
- def []=(attr_name, value)
66
- def attributes → Hash {String => Attr}
67
- def children → NodeSet
68
- def child → Node (or nil)
69
- def first_element_child → Element (or nil)
70
- def last_element_child → Element (or nil)
71
- def next_sibling → Node (or nil)
72
- def previous_sibling → Node (or nil)
73
- def parent → Node (or nil)
74
- def document → Document
75
- def type → Integer (element/text/comment/cdata/pi)
76
- def element? → Boolean
77
- def text? → Boolean
78
- def comment? → Boolean
79
- def cdata? → Boolean
80
- def processing_instruction? → Boolean
81
- def add_child(node) → Node
82
- def add_next_sibling(node) → Node
83
- def add_previous_sibling(node) → Node
84
- def remove → Node
85
- def replace(node) → Node
86
- def to_xml(options) → String
87
- def inner_html → String
88
- def traverse(&block)
89
- end
90
-
91
- class Element < Node
92
- def add_class(name)
93
- def remove_class(name)
94
- def classes → Array<String>
95
- end
96
-
97
- class Text < Node; end
98
- class Comment < Node; end
99
- class CDATA < Node; end
100
- class ProcessingInstruction < Node; end
101
- class Attr
102
- def name → String
103
- def value → String
104
- def value=(val)
105
- def parent → Element
106
- def remove
107
- end
108
-
109
- class NodeSet
110
- include Enumerable
111
- include Searchable
112
-
113
- def length / size → Integer
114
- def first(n) → Node | NodeSet
115
- def last → Node
116
- def [](index) → Node
117
- def each(&block)
118
- def empty? → Boolean
119
- def xpath(expr) → NodeSet
120
- def search(expr) → NodeSet
121
- def to_xml → String
122
- def inner_text → String
123
- end
124
-
125
- module Searchable
126
- def xpath(*paths) → NodeSet | Float | String | Boolean
127
- def at_xpath(*paths) → Node (or nil)
128
- def css(*selectors) → NodeSet (converts CSS to XPath)
129
- def at_css(*selectors) → Node (or nil)
130
- def search(*args) → NodeSet (auto-detect CSS/XPath)
131
- def at(*args) → Node (or nil)
132
- end
133
-
134
- class ParseOptions
135
- DEFAULT_XML = ...
136
- RECOVER = ...
137
- NOERROR = ...
138
- NOWARNING = ...
139
- NOCDATA = ...
140
- STRICT = ...
141
- end
142
-
143
- module SAX
144
- class Parser
145
- def initialize(handler = DocHandler.new)
146
- def parse(io_or_string)
147
- end
148
-
149
- class Document
150
- def start_element(name, attrs = [])
151
- def end_element(name)
152
- def characters(string)
153
- def start_document
154
- def end_document
155
- def comment(string)
156
- def cdata(string)
157
- def processing_instruction(name, content)
158
- def error(message, line, column)
159
- end
160
- end
161
- end
162
- end
163
- ```
164
-
165
- ## Memory model
166
-
167
- - **Document** owns the C document pool. `Leptris::XML::Document.new`
168
- calls `leptris_parse_string` → returns a `LeptrisDocument` pointer.
169
- `Document#free` calls `leptris_document_free`. Auto-free via
170
- `ObjectSpace.define_finalizer` as a safety net (but callers should
171
- call `#free` explicitly for predictable lifecycle).
172
- - **Node / Element / Text etc.** are **non-owning handles** wrapping
173
- a C pointer. The pointer is valid as long as the parent Document
174
- is alive. Freeing a Node just drops the Ruby wrapper; the C node
175
- lives until `Document#free`.
176
- - **NodeSet** wraps a `LeptrisXPathResult` pointer from
177
- `leptris_xpath_eval`. Freeing a NodeSet calls
178
- `leptris_xpath_result_free`.
179
- - **Attr** wraps a C attribute pointer (owned by the parent element's
180
- pool). Non-owning.
181
-
182
- ## CSS support
183
-
184
- Nokogiri supports CSS selectors via `css()` and `at_css()`. Leptris
185
- doesn't have a CSS engine in C, so CSS-to-XPath conversion must be
186
- done in Ruby. Options:
187
- 1. Use the `css_parser` gem (depends on `racc`).
188
- 2. Write a minimal CSS-to-XPath converter in pure Ruby.
189
-
190
- For v0.4.2 compatibility, option 2 (minimal converter) is recommended.
191
- Nokogiri's CSS selector support is comprehensive but the common subset
192
- is small: `tag`, `.class`, `#id`, `> child`, `descendant`,
193
- `[attr]`, `[attr=value]`, `:first-child`, `:last-child`, `:not(...)`.
194
-
195
- ## Dependencies
196
-
197
- ```ruby
198
- # leptris.gemspec
199
- spec.add_dependency 'ffi', '~> 1.16'
200
- ```
201
-
202
- No other runtime dependencies. No C extension compilation needed —
203
- just FFI to the pre-built libleptris shared library.
204
-
205
- ## Reference material
206
-
207
- - Nokogiri source: `~/src/external/nokogiri/`
208
- - `lib/nokogiri/xml/node.rb` — 77 public methods
209
- - `lib/nokogiri/xml/node_set.rb` — 31 public methods
210
- - `lib/nokogiri/xml/document.rb` — 22 public methods
211
- - `lib/nokogiri/xml/searchable.rb` — xpath/css/search module
212
- - libleptris public headers: `src/include/leptris/`
213
- - `types.h` — opaque handle typedefs
214
- - `dom/document.h`, `dom/element.h`, `dom/serialize.h`
215
- - `xpath/xpath.h` — XPath eval API
216
- - `sax/sax.h` — SAX parser API
217
- - libleptris v0.4.2: tag `v0.4.2` on `github.com:leptris/leptris`
@@ -1,236 +0,0 @@
1
- # TODO 2 — FFI declarations: complete libleptris v0.4.2 public API
2
-
3
- ## Goal
4
-
5
- Create `lib/leptris/xml/ffi.rb` that attaches to EVERY public function
6
- in libleptris v0.4.2 via the `ffi` gem. This is the single source of
7
- truth for the C ↔ Ruby boundary.
8
-
9
- ## Library loading
10
-
11
- ```ruby
12
- module Leptris
13
- module XML
14
- module FFI
15
- extend ::FFI
16
-
17
- ffi_lib [
18
- ENV['LEPTRIS_LIB_PATH'],
19
- 'leptris',
20
- '/usr/local/lib/libleptris.dylib',
21
- '/usr/local/lib/libleptris.so',
22
- File.expand_path('../../../build/src/libleptris.dylib', __dir__),
23
- File.expand_path('../../../build/src/libleptris.so', __dir__),
24
- ].compact
25
- end
26
- end
27
- end
28
- ```
29
-
30
- ## Opaque type declarations
31
-
32
- ```ruby
33
- typedef :pointer, :document
34
- typedef :pointer, :element
35
- typedef :pointer, :node_ref
36
- typedef :pointer, :xpath_result
37
- typedef :pointer, :sax_parser
38
- typedef :pointer, :attribute
39
- ```
40
-
41
- ## Complete function list (attach all of these)
42
-
43
- Source: `src/include/leptris/types.h`, `src/include/leptris.h`, and
44
- `src/include/leptris/*.h`.
45
-
46
- ### Version
47
- ```ruby
48
- attach_function :leptris_version, [], :string
49
- ```
50
-
51
- ### Document lifecycle
52
- ```ruby
53
- attach_function :leptris_parse_string, [:string, :size_t, :pointer], :document
54
- attach_function :leptris_document_free, [:document], :void
55
- attach_function :leptris_document_root, [:document], :element
56
- attach_function :leptris_document_serialize, [:document, :pointer], :pointer
57
- attach_function :leptris_document_set_strict, [:document, :int], :void
58
- attach_function :leptris_xinclude_process, [:document, :string], :int
59
- ```
60
-
61
- ### Node access
62
- ```ruby
63
- attach_function :leptris_node_get_type, [:node_ref], :int
64
- attach_function :leptris_node_first_child, [:node_ref], :node_ref
65
- attach_function :leptris_node_next_sibling, [:node_ref], :node_ref
66
- attach_function :leptris_node_previous_sibling, [:node_ref], :node_ref
67
- attach_function :leptris_node_child_count, [:node_ref], :size_t
68
- attach_function :leptris_node_as_element, [:node_ref], :element
69
- attach_function :leptris_element_as_node, [:element], :node_ref
70
- attach_function :leptris_element_first_child_any, [:element], :element
71
- ```
72
-
73
- ### Element queries
74
- ```ruby
75
- attach_function :leptris_element_name, [:element], :string
76
- attach_function :leptris_element_text, [:element], :string
77
- attach_function :leptris_element_attribute, [:element, :string, :string], :string
78
- attach_function :leptris_element_attribute_count, [:element], :size_t
79
- attach_function :leptris_element_first_attribute, [:element], :pointer
80
- attach_function :leptris_element_parent, [:element], :element
81
- attach_function :leptris_element_next_sibling_any, [:element], :element
82
- attach_function :leptris_element_get_namespace_uri, [:element], :string
83
- attach_function :leptris_element_get_prefix, [:element], :string
84
- attach_function :leptris_element_get_name, [:element], :string
85
- ```
86
-
87
- ### Element mutation
88
- ```ruby
89
- attach_function :leptris_element_set_name, [:element, :string], :void
90
- attach_function :leptris_element_set_attribute, [:element, :string, :string], :void
91
- attach_function :leptris_element_remove_attribute, [:element, :string], :void
92
- attach_function :leptris_element_append_child, [:element, :element], :int
93
- attach_function :leptris_element_create_child, [:element, :string], :element
94
- attach_function :leptris_element_set_text, [:element, :string], :void
95
- attach_function :leptris_element_remove_child, [:element, :element], :void
96
- ```
97
-
98
- ### Element creation
99
- ```ruby
100
- attach_function :leptris_element_create, [:string], :element
101
- attach_function :leptris_text_node_create, [:string], :element
102
- attach_function :leptris_comment_node_create, [:string], :element
103
- attach_function :leptris_cdata_node_create, [:string], :element
104
- attach_function :leptris_pi_node_create, [:string, :string], :element
105
- ```
106
-
107
- ### Text / Comment / CDATA / PI access
108
- ```ruby
109
- attach_function :leptris_text_node_get_content, [:node_ref], :string
110
- attach_function :leptris_comment_node_get_content, [:node_ref], :string
111
- attach_function :leptris_cdata_node_get_content, [:node_ref], :string
112
- attach_function :leptris_pi_node_get_target, [:node_ref], :string
113
- attach_function :leptris_pi_node_get_data, [:node_ref], :string
114
- ```
115
-
116
- ### XPath
117
- ```ruby
118
- attach_function :leptris_xpath_eval,
119
- [:document, :element, :string], :xpath_result
120
- attach_function :leptris_xpath_eval_with_vars,
121
- [:document, :string, :pointer], :xpath_result
122
- attach_function :leptris_xpath_result_type, [:xpath_result], :int
123
- attach_function :leptris_xpath_result_count, [:xpath_result], :size_t
124
- attach_function :leptris_xpath_result_get, [:xpath_result, :size_t], :element
125
- attach_function :leptris_xpath_result_boolean, [:xpath_result], :int
126
- attach_function :leptris_xpath_result_number, [:xpath_result], :double
127
- attach_function :leptris_xpath_result_string, [:xpath_result], :pointer
128
- attach_function :leptris_xpath_result_free, [:xpath_result], :void
129
- ```
130
-
131
- ### XPath variables
132
- ```ruby
133
- attach_function :leptris_xpath_variable_set_new, [], :pointer
134
- attach_function :leptris_xpath_variable_set_free, [:pointer], :void
135
- attach_function :leptris_xpath_variable_set_boolean, [:pointer, :string, :int], :int
136
- attach_function :leptris_xpath_variable_set_number, [:pointer, :string, :double], :int
137
- attach_function :leptris_xpath_variable_set_string, [:pointer, :string, :string], :int
138
- ```
139
-
140
- ### SAX
141
- ```ruby
142
- # LeptrisSAXHandler is a struct of function pointers. Use FFI::Struct.
143
- class SAXHandler < ::FFI::Struct
144
- layout \
145
- :start_document, :pointer,
146
- :end_document, :pointer,
147
- :start_element, :pointer,
148
- :end_element, :pointer,
149
- :characters, :pointer,
150
- :comment, :pointer,
151
- :cdata, :pointer,
152
- :processing_instruction, :pointer,
153
- :start_prefix_mapping, :pointer,
154
- :end_prefix_mapping, :pointer,
155
- :error, :pointer
156
- end
157
-
158
- attach_function :leptris_sax_parse,
159
- [:string, :size_t, SAXHandler.by_pointer, :pointer], :int
160
- attach_function :leptris_sax_parser_create,
161
- [SAXHandler.by_pointer, :pointer], :sax_parser
162
- attach_function :leptris_sax_parser_feed,
163
- [:sax_parser, :string, :size_t, :int], :int
164
- attach_function :leptris_sax_parser_free, [:sax_parser], :void
165
- attach_function :leptris_sax_parser_set_streaming,
166
- [:sax_parser, :int], :int
167
- ```
168
-
169
- ### Serialization
170
- ```ruby
171
- # LeptrisSerializeOptions struct
172
- class SerializeOptions < ::FFI::Struct
173
- layout \
174
- :indent, :int,
175
- :xml_declaration, :int,
176
- :no_empty_tags, :int,
177
- :preserve_whitespace, :int
178
- end
179
-
180
- attach_function :leptris_serialize_document,
181
- [:document, :pointer], :pointer
182
- attach_function :leptris_c14n_canonicalize,
183
- [:document, :int, :int], :pointer
184
- ```
185
-
186
- ### Memory
187
- ```ruby
188
- attach_function :leptris_free_string, [:pointer], :void
189
- ```
190
-
191
- ## Status codes
192
-
193
- ```ruby
194
- LEPTRIS_OK = 0
195
- LEPTRIS_ERROR_MEMORY = -1
196
- LEPTRIS_ERROR_PARSE = -2
197
- LEPTRIS_ERROR_XPATH = -3
198
- LEPTRIS_ERROR_NULL_ARG = -4
199
- LEPTRIS_ERROR_INVALID_ARG = -5
200
- LEPTRIS_ERROR_NOT_FOUND = -6
201
- LEPTRIS_ERROR_IO = -7
202
- ```
203
-
204
- ## Node type constants
205
-
206
- ```ruby
207
- NODE_ELEMENT = 0
208
- NODE_ATTRIBUTE = 1
209
- NODE_TEXT = 2
210
- NODE_COMMENT = 3
211
- NODE_CDATA = 4
212
- NODE_PI = 5
213
- NODE_DOCTYPE = 6
214
- ```
215
-
216
- ## XPath result type constants
217
-
218
- ```ruby
219
- XPATH_NODESET = 0
220
- XPATH_BOOLEAN = 1
221
- XPATH_NUMBER = 2
222
- XPATH_STRING = 3
223
- ```
224
-
225
- ## Notes
226
-
227
- - Use `Blocking: true` for SAX callbacks (FFI::Function).
228
- - Use `AutoPointer` for document and xpath_result to get automatic
229
- cleanup. But ALSO provide explicit `#free` methods since GC timing
230
- is non-deterministic.
231
- - The `leptris_element_*` functions that return `:string` return
232
- document-owned strings (valid until `leptris_document_free`).
233
- Ruby copies them automatically on FFI return — safe.
234
- - Functions that return `:pointer` for strings (like
235
- `leptris_xpath_result_string`, `leptris_serialize_document`) return
236
- heap-owned strings that the caller must free via `leptris_free_string`.