taurus 0.1.0
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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +8 -0
- data/CHANGELOG.md +518 -0
- data/CLAUDE.md +104 -0
- data/LICENSE.md +33 -0
- data/README.adoc +1529 -0
- data/Rakefile +7 -0
- data/TODO.impl/01-architecture.md +217 -0
- data/TODO.impl/02-ffi-declarations.md +236 -0
- data/TODO.impl/03-document-node-element-nodeset.md +382 -0
- data/TODO.impl/04-sax-parser.md +203 -0
- data/TODO.impl/05-serialize-c14n-memory-specs-css.md +276 -0
- data/benchmark/README.md +168 -0
- data/benchmark/taurus_vs_nokogiri.rb +105 -0
- data/docs/ARCHITECTURE.adoc +559 -0
- data/docs/BUILD.md +395 -0
- data/docs/ERROR_MESSAGES.md +458 -0
- data/docs/FFI_ARCHITECTURE.md +439 -0
- data/docs/FUTURE_VISION.md +303 -0
- data/docs/GITHUB_ACTIONS.md +293 -0
- data/docs/OPTIMIZATIONS_IMPLEMENTED.adoc +459 -0
- data/docs/PERFORMANCE.adoc +668 -0
- data/docs/PERFORMANCE.md +448 -0
- data/docs/RELEASE_NOTES_v1.0.0.md +515 -0
- data/docs/XPATH_SPEC_COMPLIANCE.md +298 -0
- data/docs/completion/taurus.bash +86 -0
- data/docs/completion/taurus.zsh +74 -0
- data/docs/man/taurus-format.1 +227 -0
- data/docs/man/taurus-parse.1 +178 -0
- data/docs/man/taurus-xpath.1 +312 -0
- data/docs/man/taurus.1 +160 -0
- data/docs/v0.9.0_PERFORMANCE_IMPROVEMENTS.md +217 -0
- data/docs/v0.9.0_RELEASE_SUMMARY.md +281 -0
- data/docs/v1.0.0_CONTINUATION_PLAN.md +172 -0
- data/docs/v1.0.0_CONTINUATION_PROMPT.md +382 -0
- data/docs/v1.0.0_SESSION_6_CONTINUATION.md +434 -0
- data/docs/v1.0.0_SESSION_6_PROMPT.md +231 -0
- data/docs/v1.0.0_STATUS_TRACKER.md +224 -0
- data/docs/v1.1.0_CONTINUATION_PLAN.md +299 -0
- data/docs/v1.1.0_FINAL_CONTINUATION_PLAN.md +201 -0
- data/docs/v1.1.0_SESSION_3_PROMPT.md +223 -0
- data/docs/v1.1.0_STATUS_TRACKER.md +355 -0
- data/docs/xml-performance.adoc +115 -0
- data/docs/xpath-performance.adoc +379 -0
- data/lib/taurus/version.rb +5 -0
- data/lib/taurus/xml/attr.rb +43 -0
- data/lib/taurus/xml/c14n.rb +23 -0
- data/lib/taurus/xml/cdata.rb +16 -0
- data/lib/taurus/xml/comment.rb +16 -0
- data/lib/taurus/xml/css_to_xpath.rb +177 -0
- data/lib/taurus/xml/doc_type.rb +54 -0
- data/lib/taurus/xml/document.rb +202 -0
- data/lib/taurus/xml/document_fragment.rb +42 -0
- data/lib/taurus/xml/element.rb +278 -0
- data/lib/taurus/xml/ffi.rb +420 -0
- data/lib/taurus/xml/namespace.rb +43 -0
- data/lib/taurus/xml/node.rb +221 -0
- data/lib/taurus/xml/node_set.rb +143 -0
- data/lib/taurus/xml/parse_options.rb +19 -0
- data/lib/taurus/xml/processing_instruction.rb +26 -0
- data/lib/taurus/xml/sax/document.rb +45 -0
- data/lib/taurus/xml/sax/parser.rb +148 -0
- data/lib/taurus/xml/sax.rb +12 -0
- data/lib/taurus/xml/searchable.rb +93 -0
- data/lib/taurus/xml/text.rb +16 -0
- data/lib/taurus/xml.rb +29 -0
- data/lib/taurus.rb +7 -0
- data/taurus.gemspec +42 -0
- metadata +157 -0
data/Rakefile
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# TODO 1 — Architecture: C-backed Nokogiri-compatible Ruby binding
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Rewrite `taurus-ruby` to be a **thin FFI wrapper** around libtaurus 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 `taurus-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 libtaurus'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
|
+
Taurus::XML::Document / Node / NodeSet (thin Ruby wrappers)
|
|
25
|
+
↓ FFI
|
|
26
|
+
libtaurus 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
|
+
`taurus_xpath_eval` directly. No Ruby XPath engine.
|
|
35
|
+
- **SAX goes through C.** `Taurus::XML::SAX::Parser` wraps
|
|
36
|
+
`taurus_sax_parse` with Ruby callback dispatch.
|
|
37
|
+
|
|
38
|
+
## Module structure (Nokogiri-compatible)
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
module Taurus
|
|
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. `Taurus::XML::Document.new`
|
|
168
|
+
calls `taurus_parse_string` → returns a `TaurusDocument` pointer.
|
|
169
|
+
`Document#free` calls `taurus_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 `TaurusXPathResult` pointer from
|
|
177
|
+
`taurus_xpath_eval`. Freeing a NodeSet calls
|
|
178
|
+
`taurus_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()`. Taurus
|
|
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
|
+
# taurus.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 libtaurus 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
|
+
- libtaurus public headers: `src/include/taurus/`
|
|
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
|
+
- libtaurus v0.4.2: tag `v0.4.2` on `github.com:lutaml/taurus`
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# TODO 2 — FFI declarations: complete libtaurus v0.4.2 public API
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Create `lib/taurus/xml/ffi.rb` that attaches to EVERY public function
|
|
6
|
+
in libtaurus 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 Taurus
|
|
13
|
+
module XML
|
|
14
|
+
module FFI
|
|
15
|
+
extend ::FFI
|
|
16
|
+
|
|
17
|
+
ffi_lib [
|
|
18
|
+
ENV['TAURUS_LIB_PATH'],
|
|
19
|
+
'taurus',
|
|
20
|
+
'/usr/local/lib/libtaurus.dylib',
|
|
21
|
+
'/usr/local/lib/libtaurus.so',
|
|
22
|
+
File.expand_path('../../../build/src/libtaurus.dylib', __dir__),
|
|
23
|
+
File.expand_path('../../../build/src/libtaurus.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/taurus/types.h`, `src/include/taurus.h`, and
|
|
44
|
+
`src/include/taurus/*.h`.
|
|
45
|
+
|
|
46
|
+
### Version
|
|
47
|
+
```ruby
|
|
48
|
+
attach_function :taurus_version, [], :string
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Document lifecycle
|
|
52
|
+
```ruby
|
|
53
|
+
attach_function :taurus_parse_string, [:string, :size_t, :pointer], :document
|
|
54
|
+
attach_function :taurus_document_free, [:document], :void
|
|
55
|
+
attach_function :taurus_document_root, [:document], :element
|
|
56
|
+
attach_function :taurus_document_serialize, [:document, :pointer], :pointer
|
|
57
|
+
attach_function :taurus_document_set_strict, [:document, :int], :void
|
|
58
|
+
attach_function :taurus_xinclude_process, [:document, :string], :int
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Node access
|
|
62
|
+
```ruby
|
|
63
|
+
attach_function :taurus_node_get_type, [:node_ref], :int
|
|
64
|
+
attach_function :taurus_node_first_child, [:node_ref], :node_ref
|
|
65
|
+
attach_function :taurus_node_next_sibling, [:node_ref], :node_ref
|
|
66
|
+
attach_function :taurus_node_previous_sibling, [:node_ref], :node_ref
|
|
67
|
+
attach_function :taurus_node_child_count, [:node_ref], :size_t
|
|
68
|
+
attach_function :taurus_node_as_element, [:node_ref], :element
|
|
69
|
+
attach_function :taurus_element_as_node, [:element], :node_ref
|
|
70
|
+
attach_function :taurus_element_first_child_any, [:element], :element
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Element queries
|
|
74
|
+
```ruby
|
|
75
|
+
attach_function :taurus_element_name, [:element], :string
|
|
76
|
+
attach_function :taurus_element_text, [:element], :string
|
|
77
|
+
attach_function :taurus_element_attribute, [:element, :string, :string], :string
|
|
78
|
+
attach_function :taurus_element_attribute_count, [:element], :size_t
|
|
79
|
+
attach_function :taurus_element_first_attribute, [:element], :pointer
|
|
80
|
+
attach_function :taurus_element_parent, [:element], :element
|
|
81
|
+
attach_function :taurus_element_next_sibling_any, [:element], :element
|
|
82
|
+
attach_function :taurus_element_get_namespace_uri, [:element], :string
|
|
83
|
+
attach_function :taurus_element_get_prefix, [:element], :string
|
|
84
|
+
attach_function :taurus_element_get_name, [:element], :string
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Element mutation
|
|
88
|
+
```ruby
|
|
89
|
+
attach_function :taurus_element_set_name, [:element, :string], :void
|
|
90
|
+
attach_function :taurus_element_set_attribute, [:element, :string, :string], :void
|
|
91
|
+
attach_function :taurus_element_remove_attribute, [:element, :string], :void
|
|
92
|
+
attach_function :taurus_element_append_child, [:element, :element], :int
|
|
93
|
+
attach_function :taurus_element_create_child, [:element, :string], :element
|
|
94
|
+
attach_function :taurus_element_set_text, [:element, :string], :void
|
|
95
|
+
attach_function :taurus_element_remove_child, [:element, :element], :void
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Element creation
|
|
99
|
+
```ruby
|
|
100
|
+
attach_function :taurus_element_create, [:string], :element
|
|
101
|
+
attach_function :taurus_text_node_create, [:string], :element
|
|
102
|
+
attach_function :taurus_comment_node_create, [:string], :element
|
|
103
|
+
attach_function :taurus_cdata_node_create, [:string], :element
|
|
104
|
+
attach_function :taurus_pi_node_create, [:string, :string], :element
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Text / Comment / CDATA / PI access
|
|
108
|
+
```ruby
|
|
109
|
+
attach_function :taurus_text_node_get_content, [:node_ref], :string
|
|
110
|
+
attach_function :taurus_comment_node_get_content, [:node_ref], :string
|
|
111
|
+
attach_function :taurus_cdata_node_get_content, [:node_ref], :string
|
|
112
|
+
attach_function :taurus_pi_node_get_target, [:node_ref], :string
|
|
113
|
+
attach_function :taurus_pi_node_get_data, [:node_ref], :string
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### XPath
|
|
117
|
+
```ruby
|
|
118
|
+
attach_function :taurus_xpath_eval,
|
|
119
|
+
[:document, :element, :string], :xpath_result
|
|
120
|
+
attach_function :taurus_xpath_eval_with_vars,
|
|
121
|
+
[:document, :string, :pointer], :xpath_result
|
|
122
|
+
attach_function :taurus_xpath_result_type, [:xpath_result], :int
|
|
123
|
+
attach_function :taurus_xpath_result_count, [:xpath_result], :size_t
|
|
124
|
+
attach_function :taurus_xpath_result_get, [:xpath_result, :size_t], :element
|
|
125
|
+
attach_function :taurus_xpath_result_boolean, [:xpath_result], :int
|
|
126
|
+
attach_function :taurus_xpath_result_number, [:xpath_result], :double
|
|
127
|
+
attach_function :taurus_xpath_result_string, [:xpath_result], :pointer
|
|
128
|
+
attach_function :taurus_xpath_result_free, [:xpath_result], :void
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### XPath variables
|
|
132
|
+
```ruby
|
|
133
|
+
attach_function :taurus_xpath_variable_set_new, [], :pointer
|
|
134
|
+
attach_function :taurus_xpath_variable_set_free, [:pointer], :void
|
|
135
|
+
attach_function :taurus_xpath_variable_set_boolean, [:pointer, :string, :int], :int
|
|
136
|
+
attach_function :taurus_xpath_variable_set_number, [:pointer, :string, :double], :int
|
|
137
|
+
attach_function :taurus_xpath_variable_set_string, [:pointer, :string, :string], :int
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### SAX
|
|
141
|
+
```ruby
|
|
142
|
+
# TaurusSAXHandler 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 :taurus_sax_parse,
|
|
159
|
+
[:string, :size_t, SAXHandler.by_pointer, :pointer], :int
|
|
160
|
+
attach_function :taurus_sax_parser_create,
|
|
161
|
+
[SAXHandler.by_pointer, :pointer], :sax_parser
|
|
162
|
+
attach_function :taurus_sax_parser_feed,
|
|
163
|
+
[:sax_parser, :string, :size_t, :int], :int
|
|
164
|
+
attach_function :taurus_sax_parser_free, [:sax_parser], :void
|
|
165
|
+
attach_function :taurus_sax_parser_set_streaming,
|
|
166
|
+
[:sax_parser, :int], :int
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Serialization
|
|
170
|
+
```ruby
|
|
171
|
+
# TaurusSerializeOptions 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 :taurus_serialize_document,
|
|
181
|
+
[:document, :pointer], :pointer
|
|
182
|
+
attach_function :taurus_c14n_canonicalize,
|
|
183
|
+
[:document, :int, :int], :pointer
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Memory
|
|
187
|
+
```ruby
|
|
188
|
+
attach_function :taurus_free_string, [:pointer], :void
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Status codes
|
|
192
|
+
|
|
193
|
+
```ruby
|
|
194
|
+
TAURUS_OK = 0
|
|
195
|
+
TAURUS_ERROR_MEMORY = -1
|
|
196
|
+
TAURUS_ERROR_PARSE = -2
|
|
197
|
+
TAURUS_ERROR_XPATH = -3
|
|
198
|
+
TAURUS_ERROR_NULL_ARG = -4
|
|
199
|
+
TAURUS_ERROR_INVALID_ARG = -5
|
|
200
|
+
TAURUS_ERROR_NOT_FOUND = -6
|
|
201
|
+
TAURUS_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 `taurus_element_*` functions that return `:string` return
|
|
232
|
+
document-owned strings (valid until `taurus_document_free`).
|
|
233
|
+
Ruby copies them automatically on FFI return — safe.
|
|
234
|
+
- Functions that return `:pointer` for strings (like
|
|
235
|
+
`taurus_xpath_result_string`, `taurus_serialize_document`) return
|
|
236
|
+
heap-owned strings that the caller must free via `taurus_free_string`.
|