Class: Leptris::XML::Iterparse

Inherits:
Object
  • Object
show all
Defined in:
lib/leptris/xml/iterparse.rb

Overview

Incremental tree iteration (libleptris v1.6.0): yields each completed TOP-LEVEL child element of the document root as it parses. The previous element's whole subtree is released when the next is produced — memory stays bounded by the largest subtree, not the document.

Leptris::XML::Iterparse.parse(huge_xml) do |element|
  process(element)   # subtree valid until the next iteration
end

v1 limitation (upstream): element names are the QName as written; namespace prefixes are not re-resolved — use the DOM path when namespace URIs matter. The yielded elements have no parent Document; #document returns nil and they must not outlive the iteration.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(handle) ⇒ Iterparse

Returns a new instance of Iterparse.



35
36
37
38
# File 'lib/leptris/xml/iterparse.rb', line 35

def initialize(handle)
  raise Leptris::XML::ParseError, "leptris_iterparse_new failed" if handle.null?
  @handle = handle
end

Class Method Details

.parse(xml_or_io, &block) ⇒ Object

NOTE: an IO argument is read fully into memory before the C iterator starts — the bounded-memory path is parse_file (C-side file streaming). The C API takes one (xml, len) buffer.



24
25
26
27
28
# File 'lib/leptris/xml/iterparse.rb', line 24

def self.parse(xml_or_io, &block)
  xml = xml_or_io.respond_to?(:read) ? xml_or_io.read : xml_or_io.to_s
  iterator = new(Leptris::XML::FFI.leptris_iterparse_new(xml, xml.bytesize))
  iterator.run(&block)
end

.parse_file(path, &block) ⇒ Object



30
31
32
33
# File 'lib/leptris/xml/iterparse.rb', line 30

def self.parse_file(path, &block)
  iterator = new(Leptris::XML::FFI.leptris_iterparse_new_file(path))
  iterator.run(&block)
end

Instance Method Details

#freeObject



55
56
57
58
59
# File 'lib/leptris/xml/iterparse.rb', line 55

def free
  return if @handle.nil?
  Leptris::XML::FFI.leptris_iterparse_free(@handle)
  @handle = nil
end

#runObject



40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/leptris/xml/iterparse.rb', line 40

def run
  return enum_for(:run) unless block_given?
  loop do
    ptr = Leptris::XML::FFI.leptris_iterparse_next(@handle)
    break if ptr.null?
    # document: nil — the subtree belongs to the iterator's pool,
    # not a long-lived Document, so wrapper caching is skipped and
    # #document is nil on the yielded elements.
    yield Leptris::XML::Node.wrap(ptr, nil)
  end
  self
ensure
  free
end