Skip to content

Commit

Permalink
parser: keep the current namespaces instead of stack of Set
Browse files Browse the repository at this point in the history
It improves namespace resolution performance for deep element.
  • Loading branch information
kou committed Aug 17, 2024
1 parent 2b47b16 commit cb15858
Showing 1 changed file with 35 additions and 10 deletions.
45 changes: 35 additions & 10 deletions lib/rexml/parsers/baseparser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@ def stream=( source )
@tags = []
@stack = []
@entities = []
@nsstack = []
@namespaces = {}
@namespaces_restore_stack = []
end

def position
Expand Down Expand Up @@ -285,7 +286,6 @@ def pull_event
@source.position = start_position
raise REXML::ParseException.new(message, @source)
end
@nsstack.unshift(Set.new)
name = parse_name(base_error_message)
if @source.match(/\s*\[/um, true)
id = [nil, nil, nil]
Expand Down Expand Up @@ -379,7 +379,7 @@ def pull_event
val = attdef[4] if val == "#FIXED "
pairs[attdef[0]] = val
if attdef[0] =~ /^xmlns:(.*)/
@nsstack[0] << $1
@namespaces[$1] = val
end
end
end
Expand Down Expand Up @@ -432,7 +432,7 @@ def pull_event
# here explicitly.
@source.ensure_buffer
if @source.match("/", true)
@nsstack.shift
@namespaces_restore_stack.pop
last_tag = @tags.pop
md = @source.match(Private::CLOSE_PATTERN, true)
if md and !last_tag
Expand Down Expand Up @@ -477,18 +477,18 @@ def pull_event
@document_status = :in_element
@prefixes.clear
@prefixes << md[2] if md[2]
@nsstack.unshift(curr_ns=Set.new)
attributes, closed = parse_attributes(@prefixes, curr_ns)
push_namespaces_restore
attributes, closed = parse_attributes(@prefixes)
# Verify that all of the prefixes have been defined
for prefix in @prefixes
unless @nsstack.find{|k| k.member?(prefix)}
unless @namespaces.key?(prefix)
raise UndefinedNamespaceException.new(prefix,@source,self)
end
end

if closed
@closed = tag
@nsstack.shift
pop_namespaces_restore
else
if @tags.empty? and @have_root
raise ParseException.new("Malformed XML: Extra tag at the end of the document (got '<#{tag}')", @source)
Expand Down Expand Up @@ -599,6 +599,31 @@ def unnormalize( string, entities=nil, filter=nil )
end

private
def add_namespace(prefix, uri)
@namespaces_restore_stack.last[prefix] = @namespaces[prefix]
if uri.nil?
@namespaces.delete(prefix)
else
@namespaces[prefix] = uri
end
end

def push_namespaces_restore
namespaces_restore = {}
@namespaces_restore_stack.push(namespaces_restore)
namespaces_restore
end

def pop_namespaces_restore
namespaces_restore = @namespaces_restore_stack.pop
namespaces_restore.each do |prefix, uri|
if uri.nil?
@namespaces.delete(prefix)
else
@namespaces[prefix] = uri
end
end
end

def record_entity_expansion(delta=1)
@entity_expansion_count += delta
Expand Down Expand Up @@ -727,7 +752,7 @@ def process_instruction
[:processing_instruction, name, content]
end

def parse_attributes(prefixes, curr_ns)
def parse_attributes(prefixes)
attributes = {}
closed = false
while true
Expand Down Expand Up @@ -770,7 +795,7 @@ def parse_attributes(prefixes, curr_ns)
"(http://www.w3.org/TR/REC-xml-names/#ns-decl)"
raise REXML::ParseException.new( msg, @source, self)
end
curr_ns << local_part
add_namespace(local_part, value)
elsif prefix
prefixes << prefix unless prefix == "xml"
end
Expand Down

0 comments on commit cb15858

Please sign in to comment.