moxml 0.5.81 → 0.5.83

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 957697bcb855cea040ac0d8b2f202a7cd8fd230482137602fcf7d50fc2712ebb
4
- data.tar.gz: 22815ac3e9920a8939b0076f3b4222a154e0420d4fe1a21d9b7e56bd9b0818b8
3
+ metadata.gz: 47eeb00aed4df2a6ea9474713ed65ba577889375e2ea7a49a8b36ee7fde86090
4
+ data.tar.gz: 947e4a25bd867ce42a52015ceea6fe576ded8b1ccf9a232cc5b1a69e1c22b166
5
5
  SHA512:
6
- metadata.gz: 422b076bb191e01b27693af02a66d4e985f574dbcc49451c9af395c1905f9cabfdb2faf0e1b15303a5444c17a8fba98e70e5bea7d5c61f1f152d9fa09a550329
7
- data.tar.gz: 2d75b66d322414ea8c99b79fe1ed78c7aa758b90b7f5b7f56a685e9f861bca12d6f641803aa04ced575a812677a085619bd40ee435963b035cdbe005a36d1b55
6
+ metadata.gz: daaf3e2d077a2fee6b710d61b512a1e8c89922727cbf147aca2fb69147602aa3c50369bdc990cb385e3522296151102fa5498f0df5e11955d1b16424fa18c2b1
7
+ data.tar.gz: 55f51e85aa61abcb1bc9ab735cd1e863152c46f284a85ff78355306b0698305772b968c1690d7c0c991715afdd433911ac70fce6ce0fde5f133fa27001748ffc
data/Rakefile CHANGED
@@ -263,6 +263,14 @@ namespace :benchmark do
263
263
  sh "bundle exec ruby benchmark/plan_bench.rb"
264
264
  end
265
265
 
266
+ # SAX bench row (leptris#1298 / #279): the bulk record drain vs
267
+ # the callback Recorder. With LEPTRIS_BENCH_LOCK=1 (CI) it exits 1
268
+ # unless records-walk allocations stay under half the recorder's.
269
+ desc "SAX parse benchmark: record drain vs callback recorder"
270
+ task :sax do
271
+ sh "bundle exec ruby benchmark/sax_bench.rb"
272
+ end
273
+
266
274
  desc "Generate adapter benchmark report"
267
275
  task :report do
268
276
  ruby "benchmarks/generate_report.rb"
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SAX bench row (leptris#1298 follow-up / moxml#279 item 4): the
4
+ # bulk record drain vs the callback Recorder on the canon parse-lane
5
+ # shape. Run via `rake benchmark:sax`; with LEPTRIS_BENCH_LOCK=1
6
+ # (CI) it exits 1 unless the drain stays materially cheaper than the
7
+ # recorder — records-walk allocations must land under half the
8
+ # recorder's, so the win stays visible and a regression re-opens
9
+ # the lane.
10
+ require "moxml"
11
+
12
+ item = "<item n=\"1\" kind=\"k\" id=\"i1\">value text</item>"
13
+ DOC = "<r>#{item * 225}<t>tail</t></r>".freeze # rubocop:disable Lint/ConstantDefinitionInBlock
14
+ DOCS = [DOC, DOC].freeze # rubocop:disable Lint/ConstantDefinitionInBlock
15
+
16
+ module SaxBenchHandlers
17
+ class NullHandler < Moxml::SAX::Handler
18
+ def on_start_element(_name, _attrs = {}, _namespaces = {}); end
19
+
20
+ def on_end_element(_name); end
21
+
22
+ def on_characters(_text); end
23
+ end
24
+
25
+ class RecordWalkHandler < Moxml::SAX::Handler
26
+ attr_reader :elements
27
+
28
+ def initialize
29
+ super
30
+ @elements = 0
31
+ end
32
+
33
+ # The hot-loop shape: walk the records, materialize one name per
34
+ # element (the #1298 target — consumer-chosen allocations).
35
+ def on_sax_records(table)
36
+ i = 0
37
+ while i < table.count
38
+ @elements += 1 if table.kind(i).zero? && table.view(i)
39
+ i += 1
40
+ end
41
+ :claimed
42
+ end
43
+ end
44
+ end
45
+
46
+ CTX = Moxml.new(:leptris).freeze # rubocop:disable Lint/ConstantDefinitionInBlock
47
+ BRIDGE = Moxml::Adapter::Leptris::LeptrisSAXBridge # rubocop:disable Lint/ConstantDefinitionInBlock
48
+
49
+ def allocations
50
+ GC.start
51
+ before = GC.stat(:total_allocated_objects)
52
+ GC.disable
53
+ yield
54
+ GC.enable
55
+ GC.stat(:total_allocated_objects) - before
56
+ end
57
+
58
+ def cpu_micros
59
+ GC.start
60
+ t0 = Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :microsecond)
61
+ yield
62
+ Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :microsecond) - t0
63
+ end
64
+
65
+ # warm both lanes
66
+ handler = SaxBenchHandlers::NullHandler.new
67
+ 3.times { DOCS.each { |doc| Moxml::Adapter::Leptris.sax_parse(doc, handler) } }
68
+ 3.times { DOCS.each { |doc| Leptris::XML::SAX::Parser.new(BRIDGE.new(handler)).parse(doc) } }
69
+ walker = SaxBenchHandlers::RecordWalkHandler.new
70
+ 3.times { DOCS.each { |doc| Moxml::Adapter::Leptris.sax_parse(doc, walker) } }
71
+
72
+ recorder_allocs = allocations do
73
+ DOCS.each { |doc| Leptris::XML::SAX::Parser.new(BRIDGE.new(handler)).parse(doc) }
74
+ end
75
+ replay_allocs = allocations do
76
+ DOCS.each { |doc| Moxml::Adapter::Leptris.sax_parse(doc, handler) }
77
+ end
78
+ walk_allocs = allocations do
79
+ DOCS.each { |doc| Moxml::Adapter::Leptris.sax_parse(doc, walker) }
80
+ end
81
+
82
+ replay_time = cpu_micros { 5.times { DOCS.each { |doc| Moxml::Adapter::Leptris.sax_parse(doc, handler) } } }
83
+ recorder_time = cpu_micros { 5.times { DOCS.each { |doc| Leptris::XML::SAX::Parser.new(BRIDGE.new(handler)).parse(doc) } } }
84
+ walk_time = cpu_micros { 5.times { DOCS.each { |doc| Moxml::Adapter::Leptris.sax_parse(doc, walker) } } }
85
+
86
+ puts format("recorder %<allocs>6d allocs %<time>6dµs", allocs: recorder_allocs, time: recorder_time / 5)
87
+ puts format("drain replay %<allocs>6d allocs %<time>6dµs", allocs: replay_allocs, time: replay_time / 5)
88
+ puts format("drain records-walk %<allocs>6d allocs %<time>6dµs", allocs: walk_allocs, time: walk_time / 5)
89
+ puts "(#{walker.elements} elements walked cumulatively; 452 per rep)"
90
+
91
+ if ENV["LEPTRIS_BENCH_LOCK"]
92
+ if walk_allocs >= recorder_allocs / 2
93
+ warn "LEPTRIS_BENCH_LOCK: records-walk allocs (#{walk_allocs}) " \
94
+ "not under half the recorder's (#{recorder_allocs}) — the " \
95
+ "#1298 win regressed"
96
+ exit 1
97
+ end
98
+ puts "lock: records-walk under half the recorder's allocations ✓"
99
+ end
data/lib/moxml/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Moxml
4
- VERSION = "0.5.81"
4
+ VERSION = "0.5.83"
5
5
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ # Duplicate attributes are a fatal XML 1.0 §3.1 error (moxml#281,
6
+ # issue #130 banner semantics): the SAX lane must surface them via
7
+ # on_error exactly like the DOM lane's parse_diagnostics. On
8
+ # bindings whose drain claims dup-containing documents (engine <
9
+ # 1.9.242 — leptris#1374), the drain swallows the error; those
10
+ # bindings skip this spec until the ride lands.
11
+ module SaxDupAttrHandlers
12
+ class ErrorCollector < Moxml::SAX::Handler
13
+ attr_reader :errors, :elements
14
+
15
+ def initialize
16
+ super
17
+ @errors = []
18
+ @elements = 0
19
+ end
20
+
21
+ def on_error(error)
22
+ @errors << error.message
23
+ end
24
+
25
+ def on_start_element(_name, _attrs = {}, _namespaces = {})
26
+ @elements += 1
27
+ end
28
+ end
29
+ end
30
+
31
+ RSpec.describe "SAX duplicate-attribute error surfacing" do
32
+ let(:context) { Moxml.new(:leptris) }
33
+ let(:dup_xml) do
34
+ '<body lang="en" xml:lang="en" xml:lang="en"><div>x</div></body>'
35
+ end
36
+
37
+ it "surfaces the redefined-attribute error through sax_parse" do
38
+ if defined?(Leptris::XML::SAX::Records) &&
39
+ !Leptris::XML::SAX::Records.open(dup_xml).nil?
40
+ skip "engine < 1.9.242 — the drain claims dup-attr documents " \
41
+ "(leptris#1374)"
42
+ end
43
+
44
+ handler = SaxDupAttrHandlers::ErrorCollector.new
45
+ context.sax_parse(dup_xml, handler)
46
+
47
+ expect(handler.errors.join).to include("xml:lang redefined")
48
+ expect(handler.elements).to eq(2)
49
+ end
50
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: moxml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.81
4
+ version: 0.5.83
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -41,6 +41,7 @@ files:
41
41
  - Rakefile
42
42
  - benchmark/pipeline_bench.rb
43
43
  - benchmark/plan_bench.rb
44
+ - benchmark/sax_bench.rb
44
45
  - benchmarks/.gitignore
45
46
  - benchmarks/generate_report.rb
46
47
  - bin/console
@@ -437,6 +438,7 @@ files:
437
438
  - spec/moxml/adapter/entity_restoration_spec.rb
438
439
  - spec/moxml/adapter/headed_ox_spec.rb
439
440
  - spec/moxml/adapter/leptris_doc_parts_spec.rb
441
+ - spec/moxml/adapter/leptris_sax_dup_attr_spec.rb
440
442
  - spec/moxml/adapter/leptris_sax_records_spec.rb
441
443
  - spec/moxml/adapter/leptris_spec.rb
442
444
  - spec/moxml/adapter/libxml_internals_spec.rb