Module: Leptris::XML::FFI
- Extended by:
- FFI::Library
- Defined in:
- lib/leptris/xml/ffi.rb
Defined Under Namespace
Classes: ParseOptionsStruct, PullEventStruct, SAXHandler, SaxEventRecord, SerializeOptions
Constant Summary collapse
- SAX_EVENT_START_DOCUMENT =
0- SAX_EVENT_END_DOCUMENT =
1- SAX_EVENT_START_ELEMENT =
2- SAX_EVENT_END_ELEMENT =
3- SAX_EVENT_CHARACTERS =
4- SAX_EVENT_COMMENT =
5- SAX_EVENT_CDATA =
6- SAX_EVENT_PI =
7- SAX_EVENT_START_PREFIX =
8- SAX_EVENT_END_PREFIX =
9- SAX_EVENT_ERROR =
10- ITERPARSE_TOP_LEVEL =
0- ITERPARSE_FULL_DOCUMENT =
1- LEPTRIS_OK =
0- LEPTRIS_ERROR_MEMORY =
-1
- LEPTRIS_ERROR_PARSE =
-2
- LEPTRIS_ERROR_XPATH =
-3
- LEPTRIS_ERROR_NULL_ARG =
-4
- LEPTRIS_ERROR_INVALID_ARG =
-5
- LEPTRIS_ERROR_NOT_FOUND =
-6
- LEPTRIS_ERROR_IO =
-7
- LEPTRIS_ERROR_NOT_IMPLEMENTED =
-8
- XPATH_NODESET =
0- XPATH_BOOLEAN =
1- XPATH_NUMBER =
2- XPATH_STRING =
3- XPATH_NODE_ELEMENT =
0- XPATH_NODE_ATTRIBUTE =
1- XPATH_NODE_TEXT =
2- XPATH_NODE_OTHER =
3- NODE_ELEMENT =
0- NODE_TEXT =
1- NODE_COMMENT =
2- NODE_CDATA =
3- NODE_PI =
4- NODE_DOCTYPE =
5- NODE_ATTRIBUTE =
6- C14N_1_0 =
0- C14N_1_1 =
1- C14N_MODE_CANONICAL =
0- C14N_MODE_EXCLUSIVE =
1- TRAVERSE_PRE_ORDER =
0- TRAVERSE_POST_ORDER =
1- PULL_START_ELEMENT =
0- PULL_END_ELEMENT =
1- PULL_TEXT =
2- PULL_COMMENT =
3- PULL_CDATA =
4- PULL_PI =
5- PULL_END_DOCUMENT =
6- PULL_ERROR =
7- LEPTRIS_PARSE_DEFAULT =
0- LEPTRIS_PARSE_DROP_WS_TEXT =
1- XPATH_KIND_HINT =
XPath result-set batch (leptris_xpath_result_get_nodes_ex): the call fills out_kinds in the 4-value XPATH_NODE space (ELEMENT/ATTRIBUTE/TEXT/OTHER). ELEMENT is the only value that maps unambiguously — XPath's data model reports CDATA as TEXT, so a TEXT hint cannot distinguish Text from CDATA (different content getters) — everything else falls back to get_type in the wrapper. Returns [pointers, element_hints].
{ XPATH_NODE_ELEMENT => NODE_ELEMENT }.freeze
Class Method Summary collapse
-
.check_status(status) ⇒ Object
Single status seam: turns a C status code into a Ruby error, appending the library's last-error detail when present.
- .fetch_children(c_ptr) ⇒ Object
- .fetch_result_nodes(result_ptr, count) ⇒ Object
-
.parse_fragment_with_status(xml, document_ptr) ⇒ Object
Fragment parse with the status out-param read: returns [document-or-fragment pointer, status].
-
.read_owned_string(ptr) ⇒ Object
Reads an libleptris-owned char* result and frees it as one unit, so a call site can neither leak nor double-free.
- .scratch_ints(count) ⇒ Object
- .scratch_pointers(count) ⇒ Object
-
.serialize_into_string(ffi_function, c_ptr, options) ⇒ Object
Caller-buffer serialization cycle: size query (buf=NULL), allocate exactly, fill, read.
- .status_message(status) ⇒ Object
-
.with_ns_set(hash) ⇒ Object
Namespace-binding lifecycle, written once: flatten the prefix/URI hash into the alternating CStringArray wire format, build the caller-owned set, yield it, free it under all outcomes.
Class Method Details
.check_status(status) ⇒ Object
Single status seam: turns a C status code into a Ruby error, appending the library's last-error detail when present. The detail is a library-global string, so it can only be treated as best-effort context.
768 769 770 771 772 773 |
# File 'lib/leptris/xml/ffi.rb', line 768 def self.check_status(status) unless status == LEPTRIS_OK raise Leptris::XML::Error, (status) end status end |
.fetch_children(c_ptr) ⇒ Object
828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 |
# File 'lib/leptris/xml/ffi.rb', line 828 def self.fetch_children(c_ptr) scratch = (Thread.current[:leptris_scratch] ||= {}) buffer = scratch[:pointers] if buffer.nil? buffer = scratch[:pointers] = ::FFI::MemoryPointer.new(:pointer, FETCH_INITIAL) end cap = buffer.size / PTR_BYTES copied = leptris_node_children(c_ptr, buffer, cap) if copied == cap total = leptris_node_children(c_ptr, nil, 0) if total > cap buffer.free buffer = scratch[:pointers] = ::FFI::MemoryPointer.new(:pointer, total) copied = leptris_node_children(c_ptr, buffer, total) end end buffer.get_array_of_pointer(0, copied) end |
.fetch_result_nodes(result_ptr, count) ⇒ Object
858 859 860 861 862 863 864 865 866 867 |
# File 'lib/leptris/xml/ffi.rb', line 858 def self.fetch_result_nodes(result_ptr, count) return [[], nil] if count.zero? buffer = scratch_pointers(count) kinds = scratch_ints(count) copied = leptris_xpath_result_get_nodes_ex( result_ptr, buffer, kinds, count) hints = kinds.get_array_of_int(0, copied) .map { |k| XPATH_KIND_HINT[k] } [buffer.get_array_of_pointer(0, copied), hints] end |
.parse_fragment_with_status(xml, document_ptr) ⇒ Object
Fragment parse with the status out-param read: returns [document-or-fragment pointer, status].
871 872 873 874 875 876 |
# File 'lib/leptris/xml/ffi.rb', line 871 def self.parse_fragment_with_status(xml, document_ptr) status = ::FFI::MemoryPointer.new(:int) raw = leptris_parse_fragment( xml, xml.bytesize, document_ptr, status) [raw, status.read_int] end |
.read_owned_string(ptr) ⇒ Object
Reads an libleptris-owned char* result and frees it as one unit, so a call site can neither leak nor double-free.
758 759 760 761 762 |
# File 'lib/leptris/xml/ffi.rb', line 758 def self.read_owned_string(ptr) return "" if ptr.nil? || ptr.null? ptr.read_string.tap { leptris_free_string(ptr) } .force_encoding(Encoding::UTF_8) end |
.scratch_ints(count) ⇒ Object
902 903 904 905 906 907 908 909 910 |
# File 'lib/leptris/xml/ffi.rb', line 902 def self.scratch_ints(count) scratch = (Thread.current[:leptris_scratch] ||= {}) ptr = scratch[:ints] if ptr.nil? || ptr.size / INT_BYTES < count ptr&.free ptr = scratch[:ints] = ::FFI::MemoryPointer.new(:int, count) end ptr end |
.scratch_pointers(count) ⇒ Object
892 893 894 895 896 897 898 899 900 |
# File 'lib/leptris/xml/ffi.rb', line 892 def self.scratch_pointers(count) scratch = (Thread.current[:leptris_scratch] ||= {}) ptr = scratch[:pointers] if ptr.nil? || ptr.size / PTR_BYTES < count ptr&.free ptr = scratch[:pointers] = ::FFI::MemoryPointer.new(:pointer, count) end ptr end |
.serialize_into_string(ffi_function, c_ptr, options) ⇒ Object
Caller-buffer serialization cycle: size query (buf=NULL),
allocate exactly, fill, read. Since libleptris 1.9.0 the
pair reuses one serialization through the per-document
cache. ffi_function is leptris_document_serialize_into or
leptris_element_serialize_into. The buffer holds the
serialization + NUL and XML output is NUL-free, so the
bounded read is exact.
808 809 810 811 812 813 814 815 816 817 818 |
# File 'lib/leptris/xml/ffi.rb', line 808 def self.serialize_into_string(ffi_function, c_ptr, ) need = ffi_function.call(c_ptr, nil, 0, nil, ) return "" if need.zero? buffer = ::FFI::MemoryPointer.new(:char, need) begin ffi_function.call(c_ptr, buffer, need, nil, ) buffer.read_string ensure buffer.free end end |
.status_message(status) ⇒ Object
775 776 777 778 779 |
# File 'lib/leptris/xml/ffi.rb', line 775 def self.(status) base = leptris_status_string(status).to_s detail = leptris_last_error.to_s detail.empty? ? base : "#{base} (#{detail})" end |
.with_ns_set(hash) ⇒ Object
Namespace-binding lifecycle, written once: flatten the prefix/URI hash into the alternating CStringArray wire format, build the caller-owned set, yield it, free it under all outcomes. Every eval variant uses this; none of them knows how a set is born or dies.
786 787 788 789 790 791 792 793 794 795 796 797 798 799 |
# File 'lib/leptris/xml/ffi.rb', line 786 def self.with_ns_set(hash) flat = hash.flat_map { |prefix, uri| [prefix.to_s, uri.to_s] } buffer, _anchors = Leptris::XML::CStringArray.to_c(flat) set = leptris_xpath_ns_set_new_from_pairs(buffer, flat.length / 2) if set.null? raise Leptris::XML::Error, "leptris_xpath_ns_set_new_from_pairs failed" end begin yield set ensure leptris_xpath_ns_set_free(set) end end |