Class: Leptris::XML::Pull::Parser
- Inherits:
-
Object
- Object
- Leptris::XML::Pull::Parser
- Defined in:
- lib/leptris/xml/pull.rb
Class Method Summary collapse
Instance Method Summary collapse
- #each ⇒ Object
-
#each_batch(max_count = 256) ⇒ Object
Bulk-delivers events (libleptris 1.9.7, upstream #589): ONE C call stages up to
max_countevents — the per-event dispatch that made streaming ~145x slower than a DOM parse collapses to amortized noise. -
#free ⇒ Object
Releases the C parser.
-
#initialize(handle) ⇒ Parser
constructor
A new instance of Parser.
-
#next_event ⇒ Object
Advances the cursor and returns the next Event, or nil after the end of the document.
Constructor Details
#initialize(handle) ⇒ Parser
Returns a new instance of Parser.
50 51 52 53 |
# File 'lib/leptris/xml/pull.rb', line 50 def initialize(handle) raise Leptris::XML::ParseError, "leptris_pull_new failed" if handle.null? @handle = handle end |
Class Method Details
Instance Method Details
#each ⇒ Object
62 63 64 65 66 67 68 69 70 71 |
# File 'lib/leptris/xml/pull.rb', line 62 def each return enum_for(:each) unless block_given? loop do event = next_event break if event.nil? yield event break if event.type == :end_document || event.type == :error end self end |
#each_batch(max_count = 256) ⇒ Object
Bulk-delivers events (libleptris 1.9.7, upstream #589): ONE C
call stages up to max_count events — the per-event dispatch
that made streaming ~145x slower than a DOM parse collapses to
amortized noise. Per the engine's protocol, attributes are
captured only for the LAST start_element of each batch
(earlier starts in the same batch carry nil attrs — segment
boundaries fall where they fall); use #each when every
start's attrs must be captured.
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/leptris/xml/pull.rb', line 81 def each_batch(max_count = 256) return enum_for(:each_batch, max_count) unless block_given? stride = Leptris::XML::FFI::PullEventStruct.size type_off = 0 name_off = Leptris::XML::FFI::PullEventStruct.offset_of(:name) text_off = Leptris::XML::FFI::PullEventStruct.offset_of(:text) buffer = Leptris::XML::FFI.scratch_events(max_count) while (n = Leptris::XML::FFI.leptris_pull_next_batch( @handle, buffer, max_count)) > 0 events = Array.new(n) do |i| base = i * stride name_ptr = buffer.get_pointer(base + name_off) text_ptr = buffer.get_pointer(base + text_off) type = TYPES[buffer.get_int(base + type_off)] name = name_ptr.null? ? nil : name_ptr.read_string.force_encoding(Encoding::UTF_8) # Staging-corruption guard (leptris/leptris#648): the C # batch's staging arena misplaces record strings once the # staged content crosses its block boundary (~185 bytes of # attribute value under nested attr-carrying ancestors) — # names come back empty, control-byte-laced, or # invalid-encoding, and unknown type codes surface as nil. # Element names can never look like that: fail loudly # instead of delivering garbage, and point at the cursor # path, which reads the same documents correctly. if type.nil? || (name && (!name.valid_encoding? || name.match?(/[\x00-\x08\x0e-\x1f]/))) || (%i[start_element end_element pi].include?(type) && (name.nil? || name.empty?)) raise Leptris::XML::Error, "pull batch staging corruption (leptris/leptris#648) — " \ "use Parser#each (cursor) for this document" end text = text_ptr.null? ? nil : text_ptr.read_string.force_encoding(Encoding::UTF_8) text = Leptris::XML::FFI.read_pi_data(text) if type == :pi Event.new(type, name, text, nil) end # The attr mirror holds the batch's most recent start. last_start = nil events.each_with_index do |event, i| last_start = i if event.type == :start_element end events[last_start].attrs = capture_attrs if last_start events.each { |event| yield event } tail = events.last.type break if tail == :end_document || tail == :error end self end |
#free ⇒ Object
Releases the C parser. Safe to call twice.
56 57 58 59 60 |
# File 'lib/leptris/xml/pull.rb', line 56 def free return if @handle.nil? Leptris::XML::FFI.leptris_pull_free(@handle) @handle = nil end |
#next_event ⇒ Object
Advances the cursor and returns the next Event, or nil after the end of the document. Attribute values are captured during start_element because the C accessors are valid only for the most recently returned event.
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/leptris/xml/pull.rb', line 145 def next_event raw = Leptris::XML::FFI.leptris_pull_next(@handle) return nil if raw.null? type = TYPES[raw.get_int(0)] name_ptr = raw.get_pointer(NAME_OFFSET) text_ptr = raw.get_pointer(TEXT_OFFSET) attrs = type == :start_element ? capture_attrs : nil text = text_ptr.null? ? nil : text_ptr.read_string.force_encoding(Encoding::UTF_8) text = Leptris::XML::FFI.read_pi_data(text) if type == :pi Event.new( type, name_ptr.null? ? nil : name_ptr.read_string.force_encoding(Encoding::UTF_8), text, attrs ) end |