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

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_message(status)
  end
  status
end

.fetch_children(c_ptr) ⇒ Object

All-kind child handle batch (libleptris 1.7.0): count-only query sizes the buffer, one fetch copies every child kind, one bulk read materializes the pointers.



823
824
825
826
827
828
829
830
# File 'lib/leptris/xml/ffi.rb', line 823

def self.fetch_children(c_ptr)
  count = leptris_node_children(c_ptr, nil, 0)
  return [] if count.zero?
  with_pointer_buffer(count) do |buffer|
    copied = leptris_node_children(c_ptr, buffer, count)
    buffer.get_array_of_pointer(0, copied)
  end
end

.fetch_result_nodes(result_ptr, count) ⇒ Object



841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
# File 'lib/leptris/xml/ffi.rb', line 841

def self.fetch_result_nodes(result_ptr, count)
  return [[], nil] if count.zero?
  with_pointer_buffer(count) do |buffer|
    kinds = ::FFI::MemoryPointer.new(:int, count)
    begin
      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]
    ensure
      kinds.free
    end
  end
end

.parse_fragment_with_status(xml, document_ptr) ⇒ Object

Fragment parse with the status out-param read: returns [document-or-fragment pointer, status].



859
860
861
862
863
864
# File 'lib/leptris/xml/ffi.rb', line 859

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

.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, options)
  need = ffi_function.call(c_ptr, nil, 0, nil, options)
  return "" if need.zero?
  buffer = ::FFI::MemoryPointer.new(:char, need)
  begin
    ffi_function.call(c_ptr, buffer, need, nil, options)
    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_message(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

.with_pointer_buffer(count) ⇒ Object

The one buffer-allocation point for handle-array fetches: everything MemoryPointer-related above this line is either an attached C call or another seam helper.



869
870
871
872
873
874
875
876
# File 'lib/leptris/xml/ffi.rb', line 869

def self.with_pointer_buffer(count)
  buffer = ::FFI::MemoryPointer.new(:pointer, count)
  begin
    yield buffer
  ensure
    buffer.free
  end
end