Skip to content

Commit 7cb5eae

Browse files
committed
parser tree: improve namespace conflicted attribute check performance
It was slow for deep element. Reported by l33thaxor. Thanks!!!
1 parent 6109e01 commit 7cb5eae

File tree

4 files changed

+33
-11
lines changed

4 files changed

+33
-11
lines changed

lib/rexml/element.rb

-11
Original file line numberDiff line numberDiff line change
@@ -2384,17 +2384,6 @@ def []=( name, value )
23842384
elsif old_attr.kind_of? Hash
23852385
old_attr[value.prefix] = value
23862386
elsif old_attr.prefix != value.prefix
2387-
# Check for conflicting namespaces
2388-
if value.prefix != "xmlns" and old_attr.prefix != "xmlns"
2389-
old_namespace = old_attr.namespace
2390-
new_namespace = value.namespace
2391-
if old_namespace == new_namespace
2392-
raise ParseException.new(
2393-
"Namespace conflict in adding attribute \"#{value.name}\": "+
2394-
"Prefix \"#{old_attr.prefix}\" = \"#{old_namespace}\" and "+
2395-
"prefix \"#{value.prefix}\" = \"#{new_namespace}\"")
2396-
end
2397-
end
23982387
store value.name, {old_attr.prefix => old_attr,
23992388
value.prefix => value}
24002389
else

lib/rexml/parsers/baseparser.rb

+15
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,7 @@ def process_instruction
754754

755755
def parse_attributes(prefixes)
756756
attributes = {}
757+
expanded_names = {}
757758
closed = false
758759
while true
759760
if @source.match(">", true)
@@ -805,6 +806,20 @@ def parse_attributes(prefixes)
805806
raise REXML::ParseException.new(msg, @source, self)
806807
end
807808

809+
unless prefix == "xmlns"
810+
uri = @namespaces[prefix]
811+
expanded_name = [uri, local_part]
812+
existing_prefix = expanded_names[expanded_name]
813+
if existing_prefix
814+
message = "Namespace conflict in adding attribute " +
815+
"\"#{local_part}\": " +
816+
"Prefix \"#{existing_prefix}\" = \"#{uri}\" and " +
817+
"prefix \"#{prefix}\" = \"#{uri}\""
818+
raise REXML::ParseException.new(message, @source, self)
819+
end
820+
expanded_names[expanded_name] = prefix
821+
end
822+
808823
attributes[name] = value
809824
else
810825
message = "Invalid attribute name: <#{@source.buffer.split(%r{[/>\s]}).first}>"

test/parse/test_element.rb

+14
Original file line numberDiff line numberDiff line change
@@ -131,5 +131,19 @@ def test_linear_performance_attribute_value_gt
131131
REXML::Document.new('<test testing="' + ">" * n + '"></test>')
132132
end
133133
end
134+
135+
def test_linear_performance_deep_same_name_attributes
136+
seq = [100, 500, 1000, 1500, 2000]
137+
assert_linear_performance(seq, rehearsal: 10) do |n|
138+
xml = <<-XML
139+
<?xml version="1.0"?>
140+
<root xmlns:ns="ns-uri">
141+
#{"<x ns:name='ns-value' name='value'>\n" * n}
142+
#{"</x>\n" * n}
143+
</root>
144+
XML
145+
REXML::Document.new(xml)
146+
end
147+
end
134148
end
135149
end

test/test_core.rb

+4
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ def test_attribute_namespace_conflict
136136
# https://www.w3.org/TR/xml-names/#uniqAttrs
137137
message = <<-MESSAGE.chomp
138138
Namespace conflict in adding attribute "a": Prefix "n1" = "http://www.w3.org" and prefix "n2" = "http://www.w3.org"
139+
Line: 4
140+
Position: 140
141+
Last 80 unconsumed characters:
142+
/>
139143
MESSAGE
140144
assert_raise(REXML::ParseException.new(message)) do
141145
Document.new(<<-XML)

0 commit comments

Comments
 (0)