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.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +8 -0
  4. data/CHANGELOG.md +518 -0
  5. data/CLAUDE.md +104 -0
  6. data/LICENSE.md +33 -0
  7. data/README.adoc +1529 -0
  8. data/Rakefile +7 -0
  9. data/TODO.impl/01-architecture.md +217 -0
  10. data/TODO.impl/02-ffi-declarations.md +236 -0
  11. data/TODO.impl/03-document-node-element-nodeset.md +382 -0
  12. data/TODO.impl/04-sax-parser.md +203 -0
  13. data/TODO.impl/05-serialize-c14n-memory-specs-css.md +276 -0
  14. data/benchmark/README.md +168 -0
  15. data/benchmark/taurus_vs_nokogiri.rb +105 -0
  16. data/docs/ARCHITECTURE.adoc +559 -0
  17. data/docs/BUILD.md +395 -0
  18. data/docs/ERROR_MESSAGES.md +458 -0
  19. data/docs/FFI_ARCHITECTURE.md +439 -0
  20. data/docs/FUTURE_VISION.md +303 -0
  21. data/docs/GITHUB_ACTIONS.md +293 -0
  22. data/docs/OPTIMIZATIONS_IMPLEMENTED.adoc +459 -0
  23. data/docs/PERFORMANCE.adoc +668 -0
  24. data/docs/PERFORMANCE.md +448 -0
  25. data/docs/RELEASE_NOTES_v1.0.0.md +515 -0
  26. data/docs/XPATH_SPEC_COMPLIANCE.md +298 -0
  27. data/docs/completion/taurus.bash +86 -0
  28. data/docs/completion/taurus.zsh +74 -0
  29. data/docs/man/taurus-format.1 +227 -0
  30. data/docs/man/taurus-parse.1 +178 -0
  31. data/docs/man/taurus-xpath.1 +312 -0
  32. data/docs/man/taurus.1 +160 -0
  33. data/docs/v0.9.0_PERFORMANCE_IMPROVEMENTS.md +217 -0
  34. data/docs/v0.9.0_RELEASE_SUMMARY.md +281 -0
  35. data/docs/v1.0.0_CONTINUATION_PLAN.md +172 -0
  36. data/docs/v1.0.0_CONTINUATION_PROMPT.md +382 -0
  37. data/docs/v1.0.0_SESSION_6_CONTINUATION.md +434 -0
  38. data/docs/v1.0.0_SESSION_6_PROMPT.md +231 -0
  39. data/docs/v1.0.0_STATUS_TRACKER.md +224 -0
  40. data/docs/v1.1.0_CONTINUATION_PLAN.md +299 -0
  41. data/docs/v1.1.0_FINAL_CONTINUATION_PLAN.md +201 -0
  42. data/docs/v1.1.0_SESSION_3_PROMPT.md +223 -0
  43. data/docs/v1.1.0_STATUS_TRACKER.md +355 -0
  44. data/docs/xml-performance.adoc +115 -0
  45. data/docs/xpath-performance.adoc +379 -0
  46. data/lib/taurus/version.rb +5 -0
  47. data/lib/taurus/xml/attr.rb +43 -0
  48. data/lib/taurus/xml/c14n.rb +23 -0
  49. data/lib/taurus/xml/cdata.rb +16 -0
  50. data/lib/taurus/xml/comment.rb +16 -0
  51. data/lib/taurus/xml/css_to_xpath.rb +177 -0
  52. data/lib/taurus/xml/doc_type.rb +54 -0
  53. data/lib/taurus/xml/document.rb +202 -0
  54. data/lib/taurus/xml/document_fragment.rb +42 -0
  55. data/lib/taurus/xml/element.rb +278 -0
  56. data/lib/taurus/xml/ffi.rb +420 -0
  57. data/lib/taurus/xml/namespace.rb +43 -0
  58. data/lib/taurus/xml/node.rb +221 -0
  59. data/lib/taurus/xml/node_set.rb +143 -0
  60. data/lib/taurus/xml/parse_options.rb +19 -0
  61. data/lib/taurus/xml/processing_instruction.rb +26 -0
  62. data/lib/taurus/xml/sax/document.rb +45 -0
  63. data/lib/taurus/xml/sax/parser.rb +148 -0
  64. data/lib/taurus/xml/sax.rb +12 -0
  65. data/lib/taurus/xml/searchable.rb +93 -0
  66. data/lib/taurus/xml/text.rb +16 -0
  67. data/lib/taurus/xml.rb +29 -0
  68. data/lib/taurus.rb +7 -0
  69. data/taurus.gemspec +42 -0
  70. metadata +157 -0
@@ -0,0 +1,420 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ffi"
4
+
5
+ module Taurus
6
+ module XML
7
+ module FFI
8
+ extend ::FFI::Library
9
+
10
+ ffi_lib [
11
+ ENV["TAURUS_LIB_PATH"],
12
+ File.expand_path("../../libtaurus.dylib", __dir__),
13
+ File.expand_path("../../libtaurus.so", __dir__),
14
+ "/usr/local/lib/libtaurus.dylib",
15
+ "/usr/local/lib/libtaurus.so",
16
+ "taurus",
17
+ ].compact
18
+
19
+ typedef :pointer, :taurus_document
20
+ typedef :pointer, :taurus_element
21
+ typedef :pointer, :taurus_node_ref
22
+ typedef :pointer, :taurus_attribute
23
+ typedef :pointer, :taurus_doctype
24
+ typedef :pointer, :taurus_xpath_result
25
+ typedef :pointer, :taurus_xpath_var_set
26
+ typedef :pointer, :taurus_sax_parser
27
+ typedef :int, :taurus_status
28
+
29
+ class SAXHandler < ::FFI::Struct
30
+ layout \
31
+ :start_document, :pointer,
32
+ :end_document, :pointer,
33
+ :start_element, :pointer,
34
+ :end_element, :pointer,
35
+ :characters, :pointer,
36
+ :comment, :pointer,
37
+ :cdata, :pointer,
38
+ :processing_instruction, :pointer,
39
+ :start_prefix_mapping, :pointer,
40
+ :end_prefix_mapping, :pointer,
41
+ :error, :pointer
42
+ end
43
+
44
+ class SerializeOptions < ::FFI::Struct
45
+ layout \
46
+ :indent, :int,
47
+ :xml_declaration, :int,
48
+ :encoding, :pointer
49
+ end
50
+
51
+ attach_function :taurus_version, [], :string
52
+ attach_function :taurus_version_components, [:pointer, :pointer, :pointer], :void
53
+
54
+ attach_function :taurus_parse_string,
55
+ [:string, :size_t, :pointer], :taurus_document
56
+ attach_function :taurus_parse_string_inplace,
57
+ [:pointer, :size_t, :pointer], :taurus_document
58
+ attach_function :taurus_parse_string_with_encoding,
59
+ [:string, :size_t, :pointer], :taurus_document
60
+ attach_function :taurus_parse_file,
61
+ [:string, :pointer], :taurus_document
62
+ attach_function :taurus_load_file,
63
+ [:string, :pointer], :pointer
64
+ attach_function :taurus_document_free,
65
+ [:taurus_document], :void
66
+ attach_function :taurus_document_root,
67
+ [:taurus_document], :taurus_element
68
+ attach_function :taurus_document_encoding,
69
+ [:taurus_document], :string
70
+ attach_function :taurus_document_finalize_strings,
71
+ [:taurus_document], :int
72
+ attach_function :taurus_document_adopt_child,
73
+ [:taurus_document, :taurus_document], :void
74
+ attach_function :taurus_document_set_strict,
75
+ [:taurus_document, :int], :taurus_status
76
+ attach_function :taurus_document_get_strict,
77
+ [:taurus_document], :int
78
+ attach_function :taurus_document_freeze,
79
+ [:taurus_document], :taurus_status
80
+ attach_function :taurus_document_is_frozen,
81
+ [:taurus_document], :int
82
+ attach_function :taurus_set_strict_mode, [:int], :void
83
+ attach_function :taurus_get_strict_mode, [], :int
84
+ attach_function :taurus_set_max_depth, [:int], :void
85
+ attach_function :taurus_get_max_depth, [], :int
86
+ attach_function :taurus_xinclude_process,
87
+ [:taurus_document, :string], :taurus_status
88
+ attach_function :taurus_xinclude_is_include_element,
89
+ [:taurus_element], :int
90
+ attach_function :taurus_xinclude_is_fallback_element,
91
+ [:taurus_element], :int
92
+ attach_function :taurus_xinclude_get_href, [:taurus_element], :string
93
+ attach_function :taurus_xinclude_get_parse, [:taurus_element], :string
94
+ attach_function :taurus_xinclude_get_xpointer, [:taurus_element], :string
95
+ # taurus_xinclude_get_encoding is declared in taurus.h but not exported
96
+ # from the v0.4.4 dylib (likely same visibility bug as the serialize
97
+ # functions; see upstream issue #166). Re-add once #173 / v0.4.5 ships.
98
+
99
+ attach_function :taurus_node_get_type,
100
+ [:taurus_node_ref], :int
101
+ attach_function :taurus_node_first_child,
102
+ [:taurus_node_ref], :taurus_node_ref
103
+ attach_function :taurus_node_last_child,
104
+ [:taurus_node_ref], :taurus_node_ref
105
+ attach_function :taurus_node_next_sibling,
106
+ [:taurus_node_ref], :taurus_node_ref
107
+ attach_function :taurus_node_previous_sibling,
108
+ [:taurus_node_ref], :taurus_node_ref
109
+ attach_function :taurus_node_child_count,
110
+ [:taurus_node_ref], :size_t
111
+ attach_function :taurus_node_as_element,
112
+ [:taurus_node_ref], :taurus_element
113
+ attach_function :taurus_element_as_node,
114
+ [:taurus_element], :taurus_node_ref
115
+ attach_function :taurus_node_get_binding_wrapper,
116
+ [:taurus_node_ref], :pointer
117
+ attach_function :taurus_node_set_binding_wrapper,
118
+ [:taurus_node_ref, :pointer], :void
119
+ attach_function :taurus_node_parent,
120
+ [:taurus_node_ref], :taurus_element
121
+ attach_function :taurus_node_unlink,
122
+ [:taurus_node_ref], :taurus_status
123
+ attach_function :taurus_node_line,
124
+ [:taurus_node_ref], :int
125
+ attach_function :taurus_node_compare,
126
+ [:taurus_node_ref, :taurus_node_ref], :int
127
+ attach_function :taurus_node_traverse,
128
+ [:taurus_node_ref, :int, :pointer, :pointer], :int
129
+
130
+ attach_function :taurus_text_node_get_content,
131
+ [:taurus_node_ref], :string
132
+ attach_function :taurus_comment_node_get_content,
133
+ [:taurus_node_ref], :string
134
+ attach_function :taurus_cdata_node_get_content,
135
+ [:taurus_node_ref], :string
136
+ attach_function :taurus_pi_node_get_target,
137
+ [:taurus_node_ref], :string
138
+ attach_function :taurus_pi_node_get_data,
139
+ [:taurus_node_ref], :string
140
+ attach_function :taurus_text_node_create,
141
+ [:taurus_document, :string], :taurus_node_ref
142
+ attach_function :taurus_comment_node_create,
143
+ [:taurus_document, :string], :taurus_node_ref
144
+ attach_function :taurus_cdata_node_create,
145
+ [:taurus_document, :string], :taurus_node_ref
146
+ attach_function :taurus_pi_node_create,
147
+ [:taurus_document, :string, :string], :taurus_node_ref
148
+ attach_function :taurus_text_node_set_content,
149
+ [:taurus_node_ref, :string], :taurus_status
150
+ attach_function :taurus_comment_node_set_content,
151
+ [:taurus_node_ref, :string], :taurus_status
152
+ attach_function :taurus_cdata_node_set_content,
153
+ [:taurus_node_ref, :string], :taurus_status
154
+ attach_function :taurus_pi_node_set_target,
155
+ [:taurus_node_ref, :string], :taurus_status
156
+ attach_function :taurus_pi_node_set_data,
157
+ [:taurus_node_ref, :string], :taurus_status
158
+
159
+ attach_function :taurus_element_name,
160
+ [:taurus_element], :string
161
+ attach_function :taurus_element_text,
162
+ [:taurus_element], :string
163
+ attach_function :taurus_element_text_int,
164
+ [:taurus_element, :int], :int
165
+ attach_function :taurus_element_text_uint,
166
+ [:taurus_element, :uint], :uint
167
+ attach_function :taurus_element_text_double,
168
+ [:taurus_element, :double], :double
169
+ attach_function :taurus_element_text_float,
170
+ [:taurus_element, :float], :float
171
+ attach_function :taurus_element_text_bool,
172
+ [:taurus_element, :int], :int
173
+ attach_function :taurus_element_attribute,
174
+ [:taurus_element, :string], :string
175
+ attach_function :taurus_element_attribute_string,
176
+ [:taurus_element, :string, :string], :string
177
+ attach_function :taurus_element_attribute_int,
178
+ [:taurus_element, :string, :int], :int
179
+ attach_function :taurus_element_attribute_uint,
180
+ [:taurus_element, :string, :uint], :uint
181
+ attach_function :taurus_element_attribute_double,
182
+ [:taurus_element, :string, :double], :double
183
+ attach_function :taurus_element_attribute_float,
184
+ [:taurus_element, :string, :float], :float
185
+ attach_function :taurus_element_attribute_bool,
186
+ [:taurus_element, :string, :int], :int
187
+ # taurus_element_has_attribute is declared in taurus.h but not exported
188
+ # from the v0.4.4 dylib (likely visibility bug; see upstream #166).
189
+ # Ruby-level Element#key? will emulate via attribute lookup.
190
+ attach_function :taurus_element_attribute_count,
191
+ [:taurus_element], :size_t
192
+ attach_function :taurus_element_attribute_name_at,
193
+ [:taurus_element, :size_t], :string
194
+ attach_function :taurus_element_attribute_value_at,
195
+ [:taurus_element, :size_t], :string
196
+ attach_function :taurus_element_remove_all_attributes,
197
+ [:taurus_element], :taurus_status
198
+
199
+ attach_function :taurus_element_child_count,
200
+ [:taurus_element], :size_t
201
+ attach_function :taurus_element_child,
202
+ [:taurus_element, :size_t], :taurus_element
203
+ attach_function :taurus_element_parent,
204
+ [:taurus_element], :taurus_element
205
+ attach_function :taurus_element_root,
206
+ [:taurus_element], :taurus_element
207
+ attach_function :taurus_element_child_value,
208
+ [:taurus_element], :string
209
+ attach_function :taurus_element_hash_value,
210
+ [:taurus_element], :size_t
211
+ attach_function :taurus_element_find_child,
212
+ [:taurus_element, :string], :taurus_element
213
+ attach_function :taurus_element_find_child_by_attr,
214
+ [:taurus_element, :string, :string, :string], :taurus_element
215
+ attach_function :taurus_element_next_sibling,
216
+ [:taurus_element, :string], :taurus_element
217
+ attach_function :taurus_element_previous_sibling,
218
+ [:taurus_element, :string], :taurus_element
219
+ attach_function :taurus_element_first_child,
220
+ [:taurus_element, :string], :taurus_element
221
+ attach_function :taurus_element_last_child,
222
+ [:taurus_element, :string], :taurus_element
223
+ attach_function :taurus_element_first_child_any,
224
+ [:taurus_element], :taurus_element
225
+ attach_function :taurus_element_last_child_any,
226
+ [:taurus_element], :taurus_element
227
+ attach_function :taurus_element_next_sibling_any,
228
+ [:taurus_element], :taurus_element
229
+ attach_function :taurus_element_previous_sibling_any,
230
+ [:taurus_element], :taurus_element
231
+
232
+ attach_function :taurus_element_create,
233
+ [:taurus_document, :string], :taurus_element
234
+ attach_function :taurus_element_set_name,
235
+ [:taurus_element, :string], :taurus_status
236
+ attach_function :taurus_element_set_text,
237
+ [:taurus_element, :string], :taurus_status
238
+ attach_function :taurus_element_set_attribute,
239
+ [:taurus_element, :string, :string], :taurus_status
240
+ attach_function :taurus_element_set_attribute_bool,
241
+ [:taurus_element, :string, :int], :taurus_status
242
+ attach_function :taurus_element_set_attribute_double,
243
+ [:taurus_element, :string, :double], :taurus_status
244
+ attach_function :taurus_element_set_attribute_float,
245
+ [:taurus_element, :string, :float], :taurus_status
246
+ attach_function :taurus_element_set_attribute_int,
247
+ [:taurus_element, :string, :int], :taurus_status
248
+ attach_function :taurus_element_set_attribute_uint,
249
+ [:taurus_element, :string, :uint], :taurus_status
250
+ attach_function :taurus_element_remove_attribute,
251
+ [:taurus_element, :string], :taurus_status
252
+ attach_function :taurus_element_append_child,
253
+ [:taurus_element, :taurus_element], :taurus_status
254
+ attach_function :taurus_element_prepend_child,
255
+ [:taurus_element, :taurus_element], :taurus_status
256
+ attach_function :taurus_element_insert_before,
257
+ [:taurus_element, :taurus_element], :taurus_status
258
+ attach_function :taurus_element_insert_after,
259
+ [:taurus_element, :taurus_element], :taurus_status
260
+ attach_function :taurus_element_remove_child,
261
+ [:taurus_element, :taurus_element], :taurus_status
262
+ attach_function :taurus_element_remove_children,
263
+ [:taurus_element], :taurus_status
264
+ attach_function :taurus_element_append_copy,
265
+ [:taurus_element, :taurus_element], :taurus_element
266
+ attach_function :taurus_element_prepend_copy,
267
+ [:taurus_element, :taurus_element], :taurus_element
268
+ attach_function :taurus_element_insert_copy_before,
269
+ [:taurus_element, :taurus_element], :taurus_element
270
+ attach_function :taurus_element_insert_copy_after,
271
+ [:taurus_element, :taurus_element], :taurus_element
272
+
273
+ attach_function :taurus_element_copy,
274
+ [:taurus_element, :taurus_document], :taurus_element
275
+ attach_function :taurus_document_copy,
276
+ [:taurus_document], :taurus_document
277
+ attach_function :taurus_node_get_xpath,
278
+ [:taurus_node_ref], :pointer
279
+ attach_function :taurus_parse_fragment,
280
+ [:string, :size_t, :taurus_document, :pointer], :taurus_element
281
+
282
+ attach_function :taurus_element_namespace,
283
+ [:taurus_element], :string
284
+ attach_function :taurus_element_namespace_for_prefix,
285
+ [:taurus_element, :string], :string
286
+ attach_function :taurus_element_namespace_count,
287
+ [:taurus_element], :size_t
288
+ attach_function :taurus_element_namespace_decl_prefix,
289
+ [:taurus_element, :size_t], :string
290
+ attach_function :taurus_element_namespace_decl_uri,
291
+ [:taurus_element, :size_t], :string
292
+ attach_function :taurus_element_add_namespace_definition,
293
+ [:taurus_element, :string, :string], :taurus_status
294
+ attach_function :taurus_element_set_default_namespace,
295
+ [:taurus_element, :string], :taurus_status
296
+ attach_function :taurus_element_remove_namespace_definition,
297
+ [:taurus_element, :string], :taurus_status
298
+ attach_function :taurus_namespace_uri, [:string], :string
299
+ attach_function :taurus_namespace_prefix, [:string], :string
300
+
301
+ attach_function :taurus_xpath_eval,
302
+ [:taurus_document, :taurus_element, :string], :taurus_xpath_result
303
+ attach_function :taurus_xpath_eval_with_vars,
304
+ [:taurus_document, :string, :taurus_xpath_var_set], :taurus_xpath_result
305
+ attach_function :taurus_xpath_eval_with_vars_context,
306
+ [:taurus_document, :taurus_element, :string, :taurus_xpath_var_set],
307
+ :taurus_xpath_result
308
+ attach_function :taurus_xpath_result_type,
309
+ [:taurus_xpath_result], :int
310
+ attach_function :taurus_xpath_result_count,
311
+ [:taurus_xpath_result], :size_t
312
+ attach_function :taurus_xpath_result_get,
313
+ [:taurus_xpath_result, :size_t], :taurus_element
314
+ attach_function :taurus_xpath_result_get_nodes,
315
+ [:taurus_xpath_result, :pointer, :size_t], :size_t
316
+ attach_function :taurus_xpath_result_boolean,
317
+ [:taurus_xpath_result], :int
318
+ attach_function :taurus_xpath_result_number,
319
+ [:taurus_xpath_result], :double
320
+ attach_function :taurus_xpath_result_string,
321
+ [:taurus_xpath_result], :pointer
322
+ attach_function :taurus_xpath_result_free,
323
+ [:taurus_xpath_result], :void
324
+ attach_function :taurus_xpath_function_supported, [:string], :int
325
+ attach_function :taurus_xpath_supported_functions, [], :pointer
326
+
327
+ attach_function :taurus_xpath_variable_set_new,
328
+ [], :taurus_xpath_var_set
329
+ attach_function :taurus_xpath_variable_set_free,
330
+ [:taurus_xpath_var_set], :void
331
+ attach_function :taurus_xpath_variable_set_boolean,
332
+ [:taurus_xpath_var_set, :string, :int], :taurus_status
333
+ attach_function :taurus_xpath_variable_set_number,
334
+ [:taurus_xpath_var_set, :string, :double], :taurus_status
335
+ attach_function :taurus_xpath_variable_set_string,
336
+ [:taurus_xpath_var_set, :string, :string], :taurus_status
337
+
338
+ attach_function :taurus_sax_parse,
339
+ [:string, :size_t, :pointer, :pointer], :int
340
+ attach_function :taurus_sax_parser_create,
341
+ [:pointer, :pointer], :taurus_sax_parser
342
+ attach_function :taurus_sax_parser_feed,
343
+ [:taurus_sax_parser, :string, :size_t, :int], :int
344
+ attach_function :taurus_sax_parser_free,
345
+ [:taurus_sax_parser], :void
346
+ attach_function :taurus_sax_parser_set_streaming,
347
+ [:taurus_sax_parser, :int], :int
348
+
349
+ attach_function :taurus_document_serialize,
350
+ [:taurus_document, :pointer], :pointer
351
+ attach_function :taurus_element_serialize,
352
+ [:taurus_element, :pointer], :pointer
353
+ attach_function :taurus_document_save_file,
354
+ [:taurus_document, :string, :pointer], :taurus_status
355
+ attach_function :taurus_c14n_canonicalize,
356
+ [:taurus_document, :int, :int], :pointer
357
+ attach_function :taurus_c14n_canonicalize_subtree,
358
+ [:taurus_element, :int, :int], :pointer
359
+ attach_function :taurus_c14n_canonicalize_ex,
360
+ [:taurus_document, :int, :int, :pointer, :int], :pointer
361
+ attach_function :taurus_c14n_canonicalize_subtree_ex,
362
+ [:taurus_element, :int, :int, :pointer, :int], :pointer
363
+
364
+ attach_function :taurus_document_internal_subset,
365
+ [:taurus_document], :taurus_doctype
366
+ attach_function :taurus_doctype_get_name,
367
+ [:taurus_doctype], :string
368
+ attach_function :taurus_doctype_get_root_name,
369
+ [:taurus_doctype], :string
370
+ attach_function :taurus_doctype_get_public_id,
371
+ [:taurus_doctype], :string
372
+ attach_function :taurus_doctype_get_system_id,
373
+ [:taurus_doctype], :string
374
+ attach_function :taurus_doctype_get_internal_subset,
375
+ [:taurus_doctype], :string
376
+
377
+ attach_function :taurus_free_string, [:pointer], :void
378
+ attach_function :taurus_explicit_cleanup, [], :void
379
+ attach_function :taurus_set_memory_management_functions,
380
+ [:pointer, :pointer], :void
381
+ attach_function :taurus_get_memory_allocation_function, [], :pointer
382
+ attach_function :taurus_get_memory_deallocation_function, [], :pointer
383
+ attach_function :taurus_document_set_allocators,
384
+ [:taurus_document, :pointer, :pointer], :taurus_status
385
+ attach_function :taurus_status_string, [:taurus_status], :string
386
+
387
+ TAURUS_OK = 0
388
+ TAURUS_ERROR_MEMORY = -1
389
+ TAURUS_ERROR_PARSE = -2
390
+ TAURUS_ERROR_XPATH = -3
391
+ TAURUS_ERROR_NULL_ARG = -4
392
+ TAURUS_ERROR_INVALID_ARG = -5
393
+ TAURUS_ERROR_NOT_FOUND = -6
394
+ TAURUS_ERROR_IO = -7
395
+ TAURUS_ERROR_NOT_IMPLEMENTED = -8
396
+
397
+ XPATH_NODESET = 0
398
+ XPATH_BOOLEAN = 1
399
+ XPATH_NUMBER = 2
400
+ XPATH_STRING = 3
401
+
402
+ NODE_ELEMENT = 0
403
+ NODE_TEXT = 1
404
+ NODE_COMMENT = 2
405
+ NODE_CDATA = 3
406
+ NODE_PI = 4
407
+ NODE_DOCTYPE = 5
408
+ NODE_ATTRIBUTE = 6
409
+
410
+ C14N_1_0 = 0
411
+ C14N_1_1 = 1
412
+
413
+ C14N_MODE_CANONICAL = 0
414
+ C14N_MODE_EXCLUSIVE = 1
415
+
416
+ TRAVERSE_PRE_ORDER = 0
417
+ TRAVERSE_POST_ORDER = 1
418
+ end
419
+ end
420
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ # libtaurus's TaurusNamespace is `const char*` (the URI directly). The Ruby
4
+ # Namespace wrapper holds (element, uri, optional prefix). When the prefix
5
+ # isn't supplied (e.g. via Element#namespace), it can be derived by walking
6
+ # the element's namespace declarations via taurus_element_namespace_decl_*.
7
+
8
+ class Taurus::XML::Namespace
9
+ attr_reader :element, :href, :prefix
10
+
11
+ def initialize(element, href, prefix: :derive)
12
+ @element = element
13
+ @href = href
14
+ @prefix = prefix == :derive ? derive_prefix : prefix
15
+ end
16
+
17
+ def document
18
+ @element&.document
19
+ end
20
+
21
+ def ==(other)
22
+ other.is_a?(Taurus::XML::Namespace) &&
23
+ @href == other.href && @prefix == other.prefix
24
+ end
25
+
26
+ def inspect
27
+ "#<#{self.class.name} prefix=#{@prefix.inspect} href=#{@href.inspect}>"
28
+ end
29
+
30
+ private
31
+
32
+ def derive_prefix
33
+ return nil if @element.nil?
34
+ count = Taurus::XML::FFI.taurus_element_namespace_count(@element.c_ptr)
35
+ count.times do |i|
36
+ uri = Taurus::XML::FFI.taurus_element_namespace_decl_uri(@element.c_ptr, i)
37
+ next unless uri == @href
38
+ prefix = Taurus::XML::FFI.taurus_element_namespace_decl_prefix(@element.c_ptr, i)
39
+ return prefix.nil? || prefix.empty? ? nil : prefix
40
+ end
41
+ nil
42
+ end
43
+ end
@@ -0,0 +1,221 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Taurus::XML::Node
4
+ attr_reader :c_ptr, :document
5
+
6
+ def initialize(c_ptr, document, parent: nil)
7
+ @c_ptr = c_ptr
8
+ @document = document
9
+ @parent = parent
10
+ end
11
+
12
+ def self.wrap(c_ptr, document, parent: nil)
13
+ # Per-document weak-ref cache. Returns the existing wrapper when the
14
+ # same c_ptr is wrapped twice (common in children/sibling walks,
15
+ # repeated xpath queries, traverse-then-access patterns). The cache
16
+ # dies with the document so no stale entries.
17
+ if document && (cached = document.wrapper_cache[c_ptr.address])
18
+ return cached
19
+ end
20
+
21
+ node =
22
+ case Taurus::XML::FFI.taurus_node_get_type(c_ptr)
23
+ when Taurus::XML::FFI::NODE_ELEMENT
24
+ Taurus::XML::Element.new(c_ptr, document, parent: parent)
25
+ when Taurus::XML::FFI::NODE_TEXT
26
+ Taurus::XML::Text.new(c_ptr, document, parent: parent)
27
+ when Taurus::XML::FFI::NODE_COMMENT
28
+ Taurus::XML::Comment.new(c_ptr, document, parent: parent)
29
+ when Taurus::XML::FFI::NODE_CDATA
30
+ Taurus::XML::CDATA.new(c_ptr, document, parent: parent)
31
+ when Taurus::XML::FFI::NODE_PI
32
+ Taurus::XML::ProcessingInstruction.new(c_ptr, document, parent: parent)
33
+ else
34
+ new(c_ptr, document, parent: parent)
35
+ end
36
+
37
+ document.wrapper_cache[c_ptr.address] = node if document
38
+ node
39
+ end
40
+
41
+ def name
42
+ raise NotImplementedError, "#{self.class}#name not implemented"
43
+ end
44
+
45
+ def content
46
+ raise NotImplementedError, "#{self.class}#content not implemented"
47
+ end
48
+ alias_method :text, :content
49
+ alias_method :inner_text, :content
50
+
51
+ def type
52
+ Taurus::XML::FFI.taurus_node_get_type(@c_ptr)
53
+ end
54
+ alias_method :node_type, :type
55
+
56
+ def element?; type == Taurus::XML::FFI::NODE_ELEMENT; end
57
+ def text?; type == Taurus::XML::FFI::NODE_TEXT; end
58
+ def comment?; type == Taurus::XML::FFI::NODE_COMMENT; end
59
+ def cdata?; type == Taurus::XML::FFI::NODE_CDATA; end
60
+ def processing_instruction?
61
+ type == Taurus::XML::FFI::NODE_PI
62
+ end
63
+ alias_method :pi?, :processing_instruction?
64
+
65
+ def parent
66
+ return @parent if @parent
67
+ ptr = Taurus::XML::FFI.taurus_node_parent(@c_ptr)
68
+ return nil if ptr.null?
69
+ Taurus::XML::Element.new(ptr, @document)
70
+ end
71
+
72
+ def line
73
+ Taurus::XML::FFI.taurus_node_line(@c_ptr)
74
+ end
75
+
76
+ def <=>(other)
77
+ return nil unless other.is_a?(Taurus::XML::Node)
78
+ return nil unless @document == other.document
79
+ Taurus::XML::FFI.taurus_node_compare(@c_ptr, other.c_ptr)
80
+ end
81
+
82
+ def child
83
+ ptr = Taurus::XML::FFI.taurus_node_first_child(@c_ptr)
84
+ return nil if ptr.null?
85
+ Taurus::XML::Node.wrap(ptr, @document, parent: as_element_or_self)
86
+ end
87
+
88
+ def children
89
+ nodes = []
90
+ ptr = Taurus::XML::FFI.taurus_node_first_child(@c_ptr)
91
+ until ptr.nil? || ptr.null?
92
+ nodes << Taurus::XML::Node.wrap(ptr, @document, parent: as_element_or_self)
93
+ ptr = Taurus::XML::FFI.taurus_node_next_sibling(ptr)
94
+ end
95
+ Taurus::XML::NodeSet.new(@document, nodes)
96
+ end
97
+
98
+ def next_sibling
99
+ ptr = Taurus::XML::FFI.taurus_node_next_sibling(@c_ptr)
100
+ return nil if ptr.null?
101
+ Taurus::XML::Node.wrap(ptr, @document, parent: @parent)
102
+ end
103
+ alias_method :next, :next_sibling
104
+
105
+ def previous_sibling
106
+ ptr = Taurus::XML::FFI.taurus_node_previous_sibling(@c_ptr)
107
+ return nil if ptr.null?
108
+ Taurus::XML::Node.wrap(ptr, @document, parent: @parent)
109
+ end
110
+ alias_method :previous, :previous_sibling
111
+
112
+ def first_element_child
113
+ ptr = Taurus::XML::FFI.taurus_node_first_child(@c_ptr)
114
+ until ptr.nil? || ptr.null?
115
+ node = Taurus::XML::Node.wrap(ptr, @document, parent: as_element_or_self)
116
+ return node if node.element?
117
+ ptr = Taurus::XML::FFI.taurus_node_next_sibling(ptr)
118
+ end
119
+ nil
120
+ end
121
+
122
+ def last_element_child
123
+ children.reverse_each.find(&:element?)
124
+ end
125
+
126
+ def element_children
127
+ children.select(&:element?)
128
+ end
129
+ alias_method :elements, :element_children
130
+
131
+ def next_element
132
+ sibling = next_sibling
133
+ sibling = sibling.next_sibling until sibling.nil? || sibling.element?
134
+ sibling
135
+ end
136
+
137
+ def previous_element
138
+ sibling = previous_sibling
139
+ sibling = sibling.previous_sibling until sibling.nil? || sibling.element?
140
+ sibling
141
+ end
142
+
143
+ def unlink
144
+ status = Taurus::XML::FFI.taurus_node_unlink(@c_ptr)
145
+ raise Taurus::XML::Error,
146
+ Taurus::XML::FFI.taurus_status_string(status) unless status == Taurus::XML::FFI::TAURUS_OK
147
+ @parent = nil
148
+ self
149
+ end
150
+ alias_method :remove, :unlink
151
+
152
+ # Walks the subtree in post-order DFS (matches Nokogiri's semantics).
153
+ #
154
+ # Specialized hot path: skips the intermediate NodeSet allocation that
155
+ # Element#children would create, walking via raw FFI calls and wrapping
156
+ # nodes directly. Saves one Array + one NodeSet allocation per parent
157
+ # node. For a tree of N nodes that's ~N fewer allocations on a full
158
+ # traversal.
159
+ #
160
+ # Still pays ~2 FFI calls per visited node (first_child + next_sibling).
161
+ # Beating Nokogiri on this benchmark needs C-side traverse with a
162
+ # callback (libtaurus #273); the per-node FFI cost is the floor.
163
+ def traverse
164
+ return enum_for(:traverse) unless block_given?
165
+ callback = ::FFI::Function.new(:int, [:pointer, :pointer], blocking: true) do |node_ptr, _|
166
+ yield Taurus::XML::Node.wrap(node_ptr, @document)
167
+ 0
168
+ end
169
+ Taurus::XML::FFI.taurus_node_traverse(
170
+ @c_ptr, Taurus::XML::FFI::TRAVERSE_POST_ORDER, callback, nil)
171
+ end
172
+
173
+ def path
174
+ str_ptr = Taurus::XML::FFI.taurus_node_get_xpath(@c_ptr)
175
+ return nil if str_ptr.null?
176
+ str_ptr.read_string.tap { Taurus::XML::FFI.taurus_free_string(str_ptr) }
177
+ end
178
+
179
+ def css_path
180
+ return nil if path.nil?
181
+ path.split("/").filter_map do |part|
182
+ next nil if part.empty?
183
+ part.gsub(/\[(\d+)\]/, ':nth-of-type(\1)')
184
+ end.join(" > ")
185
+ end
186
+
187
+ def dup
188
+ elem_ptr = Taurus::XML::FFI.taurus_node_as_element(@c_ptr)
189
+ raise Taurus::XML::Error, "dup is only supported for element nodes" if elem_ptr.null?
190
+ copy_ptr = Taurus::XML::FFI.taurus_element_copy(elem_ptr, @document.c_ptr)
191
+ raise Taurus::XML::Error, "taurus_element_copy failed" if copy_ptr.null?
192
+ Taurus::XML::Element.new(copy_ptr, @document)
193
+ end
194
+ alias_method :clone, :dup
195
+
196
+ def ==(other)
197
+ return false unless other.is_a?(Taurus::XML::Node)
198
+ @c_ptr == other.c_ptr
199
+ end
200
+
201
+ def inspect
202
+ "#<#{self.class.name} ptr=#{c_ptr}>"
203
+ end
204
+
205
+ protected
206
+
207
+ def as_element_or_self
208
+ is_a?(Taurus::XML::Element) ? self : nil
209
+ end
210
+
211
+ private
212
+
213
+ # Walks the subtree in post-order DFS (matches Nokogiri's semantics).
214
+ #
215
+ # Specialized hot path for #traverse: skips the intermediate NodeSet
216
+ # allocation that Element#children would create, walking via raw FFI
217
+ # calls and wrapping nodes directly. Saves one Array + one NodeSet
218
+ # allocation per parent node.
219
+ #
220
+ include Taurus::XML::Searchable
221
+ end