Class: Leptris::XML::SAX::Parser
- Inherits:
-
Object
- Object
- Leptris::XML::SAX::Parser
- Defined in:
- lib/leptris/xml/sax/parser.rb
Instance Attribute Summary collapse
-
#document ⇒ Object
Returns the value of attribute document.
-
#encoding ⇒ Object
readonly
Returns the value of attribute encoding.
Instance Method Summary collapse
- #bulk_dispatch? ⇒ Boolean
-
#dispatched_kinds ⇒ Object
The handler's overridden-kind map — the same decisions build_handler_struct makes, in dispatch form.
-
#handler_struct ⇒ Object
The handler struct (eleven FFI::Function callbacks + the C struct) is built once per handler and reused across parses; the memoized struct is anchored against GC by the instance itself.
-
#initialize(handler = Leptris::XML::SAX::Document.new, encoding = nil, streaming: true) ⇒ Parser
constructor
streaming: true (default, since 1.9.40) delivers through the engine transports — bulk/callback picked by override weight (leptris-ruby#95's attribute corruption is fixed upstream in libleptris 1.9.18, verified on the issue fixture).
-
#parse(input) ⇒ Object
Parse a string, IO, or file path.
- #parse_file(path) ⇒ Object
- #parse_io(io) ⇒ Object
- #parse_memory(string) ⇒ Object
- #parse_memory_bulk(string) ⇒ Object
Constructor Details
#initialize(handler = Leptris::XML::SAX::Document.new, encoding = nil, streaming: true) ⇒ Parser
streaming: true (default, since 1.9.40) delivers through the engine transports — bulk/callback picked by override weight (leptris-ruby#95's attribute corruption is fixed upstream in libleptris 1.9.18, verified on the issue fixture). streaming: false delivers from a DOM parse (SAX::DomDispatch) — the correctness-first fallback kept for engines older than 1.9.18 and for consumers wanting the DOM view's shapes.
18 19 20 21 22 23 |
# File 'lib/leptris/xml/sax/parser.rb', line 18 def initialize(handler = Leptris::XML::SAX::Document.new, encoding = nil, streaming: true) @document = handler @encoding = encoding @streaming = streaming end |
Instance Attribute Details
#document ⇒ Object
Returns the value of attribute document.
9 10 11 |
# File 'lib/leptris/xml/sax/parser.rb', line 9 def document @document end |
#encoding ⇒ Object (readonly)
Returns the value of attribute encoding.
9 10 11 |
# File 'lib/leptris/xml/sax/parser.rb', line 9 def encoding @encoding end |
Instance Method Details
#bulk_dispatch? ⇒ Boolean
143 144 145 146 147 |
# File 'lib/leptris/xml/sax/parser.rb', line 143 def bulk_dispatch? dispatched_kinds.keys.sum do |kind| HOT_KIND_WEIGHTS.fetch(kind, 0.0) end >= DISPATCH_WEIGHT_THRESHOLD end |
#dispatched_kinds ⇒ Object
The handler's overridden-kind map — the same decisions build_handler_struct makes, in dispatch form. Memoized with the handler struct; document= invalidates both.
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/leptris/xml/sax/parser.rb', line 114 def dispatched_kinds @dispatched_kinds ||= begin kinds = {} { start_document: :start_document, end_document: :end_document, start_element: :start_element, end_element: :end_element, characters: :characters, comment: :comment, cdata_block: :cdata, processing_instruction: :pi, start_prefix_mapping: :start_prefix, end_prefix_mapping: :end_prefix, error: :error, }.each do |method_name, kind| next unless overridden?(method_name) kinds[kind] = if method_name == :start_element && @document.method(:start_element).arity == 1 :one_arg else true end end kinds end end |
#handler_struct ⇒ Object
The handler struct (eleven FFI::Function callbacks + the C struct) is built once per handler and reused across parses; the memoized struct is anchored against GC by the instance itself.
36 37 38 |
# File 'lib/leptris/xml/sax/parser.rb', line 36 def handler_struct @handler_struct ||= build_handler_struct end |
#parse(input) ⇒ Object
Parse a string, IO, or file path. Dispatches to parse_memory / parse_io / parse_file based on the argument type.
42 43 44 45 46 47 48 49 |
# File 'lib/leptris/xml/sax/parser.rb', line 42 def parse(input) case input when String then parse_memory(input) when ->(x) { x.respond_to?(:read) } then parse_io(input) else raise ArgumentError, "SAX parser expects a String or IO, got #{input.class}" end end |
#parse_file(path) ⇒ Object
179 180 181 182 183 184 185 |
# File 'lib/leptris/xml/sax/parser.rb', line 179 def parse_file(path) if @streaming File.open(path, "r") { |f| parse_io(f) } else parse_memory(File.read(path)) end end |
#parse_io(io) ⇒ Object
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/leptris/xml/sax/parser.rb', line 149 def parse_io(io) unless @streaming parse_memory(io.read) return self end handler_struct = self.handler_struct parser_ptr = Leptris::XML::FFI.leptris_sax_parser_create( handler_struct.pointer, nil) if parser_ptr.null? raise Leptris::XML::Error, "leptris_sax_parser_create failed" end begin while (chunk = io.read(CHUNK_SIZE)) rc = Leptris::XML::FFI.leptris_sax_parser_feed( parser_ptr, chunk, chunk.bytesize, 0) if rc != 0 raise Leptris::XML::ParseError, "leptris_sax_parser_feed failed (rc=#{rc})" end end # Final flush rc = Leptris::XML::FFI.leptris_sax_parser_feed(parser_ptr, "", 0, 1) if rc != 0 raise Leptris::XML::ParseError, "leptris_sax_parser_feed (final) failed (rc=#{rc})" end ensure Leptris::XML::FFI.leptris_sax_parser_free(parser_ptr) end self end |
#parse_memory(string) ⇒ Object
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/leptris/xml/sax/parser.rb', line 51 def parse_memory(string) string = string.dup.force_encoding("UTF-8") unless @streaming Leptris::XML::SAX::DomDispatch.parse( @document, dispatched_kinds, string) return self end if bulk_dispatch? parse_memory_bulk(string) else handler_struct = self.handler_struct rc = Leptris::XML::FFI.leptris_sax_parse( string, string.bytesize, handler_struct.pointer, nil) if rc != 0 raise Leptris::XML::ParseError, "leptris_sax_parse failed (rc=#{rc})" end end self end |
#parse_memory_bulk(string) ⇒ Object
96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/leptris/xml/sax/parser.rb', line 96 def parse_memory_bulk(string) recorder = Leptris::XML::SAX::Recorder.open begin rc = recorder.feed(string, final: true) recorder.dispatch(@document, dispatched_kinds) if rc != 0 raise Leptris::XML::ParseError, "leptris_sax_parse failed (rc=#{rc})" end ensure recorder.free end self end |