Skip to content

Commit 0100017

Browse files
committed
XML: refactor XML::NodeSet to wrap a Slice(XML::Node)
The main advantage is a much simpler interface. The disadvantages are: 1. `XML::NodeSet` can now only hold 2.1 billion entries because `Slice#size` is an Int32; it shouldn't be an issue in practice. 2. `XML::Node#children` will now allocate an `XML::Node` for every child node, but will only allocate once, not on every access.
1 parent 7053f50 commit 0100017

File tree

3 files changed

+28
-59
lines changed

3 files changed

+28
-59
lines changed

src/xml/node.cr

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -81,18 +81,21 @@ class XML::Node
8181
# Gets the list of children for this node as a `XML::NodeSet`.
8282
def children : XML::NodeSet
8383
child = @node.value.children
84+
return NodeSet.new unless child
8485

85-
set = LibXML.xmlXPathNodeSetCreate(child)
86+
size = 1
87+
while child = child.value.next
88+
size += 1
89+
end
8690

87-
if child
91+
child = @node.value.children
92+
nodes = Slice(Node).new(size) do
93+
node = Node.new(child, @document)
8894
child = child.value.next
89-
while child
90-
LibXML.xmlXPathNodeSetAddUnique(set, child)
91-
child = child.value.next
92-
end
95+
node
9396
end
9497

95-
NodeSet.new(@document, set)
98+
NodeSet.new(nodes)
9699
end
97100

98101
# Returns `true` if this is a comment node.

src/xml/node_set.cr

Lines changed: 14 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,33 @@
1-
class XML::NodeSet
1+
struct XML::NodeSet
22
include Enumerable(Node)
33

44
# :nodoc:
5-
def initialize(@doc : Node, @xpath_object : LibXML::XPathObject*)
6-
@set = @xpath_object.value.nodesetval
7-
end
5+
def self.new(set : LibXML::NodeSet*, document : Node)
6+
return NodeSet.new unless set || set.value.node_nr > 0
87

9-
# :nodoc:
10-
def initialize(@doc : Node, @set : LibXML::NodeSet*)
11-
@xpath_object = Pointer(LibXML::XPathObject).null
12-
end
13-
14-
# :nodoc:
15-
def initialize(@doc : Node)
16-
@xpath_object = Pointer(LibXML::XPathObject).null
17-
@set = LibXML.xmlXPathNodeSetCreate(nil)
8+
nodes = Slice(Node).new(set.value.node_nr) do |i|
9+
Node.new(set.value.node_tab[i], document)
10+
end
11+
NodeSet.new(nodes)
1812
end
1913

2014
# :nodoc:
21-
def finalize
22-
if @xpath_object.null?
23-
LibXML.xmlXPathFreeNodeSet(@set)
24-
else
25-
LibXML.xmlXPathFreeObject(@xpath_object)
26-
end
15+
def initialize(nodes : Slice(Node)? = nil)
16+
@nodes = nodes || Slice(Node).new(0, Pointer(Void).null.as(Node))
2717
end
2818

29-
def [](index : Int) : XML::Node
30-
index += size if index < 0
31-
32-
unless 0 <= index < size
33-
raise IndexError.new
34-
end
35-
36-
internal_at(index)
19+
def [](index : Int) : Node
20+
@nodes[index]
3721
end
3822

3923
def each(&) : Nil
40-
size.times do |i|
41-
yield internal_at(i)
42-
end
24+
@nodes.each { |node| yield node }
4325
end
4426

4527
def empty? : Bool
46-
size == 0
28+
@nodes.empty?
4729
end
4830

49-
# See `Object#hash(hasher)`
50-
def_hash object_id
51-
5231
def inspect(io : IO) : Nil
5332
io << '['
5433
join io, ", ", &.inspect(io)
@@ -60,22 +39,10 @@ class XML::NodeSet
6039
end
6140

6241
def size : Int32
63-
@set.value.node_nr
64-
end
65-
66-
def object_id
67-
@set.address
42+
@nodes.size
6843
end
6944

7045
def to_s(io : IO) : Nil
7146
join io, '\n'
7247
end
73-
74-
def to_unsafe
75-
@set
76-
end
77-
78-
private def internal_at(index)
79-
Node.new(@set.value.node_tab[index], @doc)
80-
end
8148
end

src/xml/xpath_context.cr

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,13 @@ class XML::XPathContext
4242
when LibXML::XPathObjectType::BOOLEAN
4343
xpath_object.value.boolval != 0
4444
when LibXML::XPathObjectType::NODESET
45-
if xpath_object.value.nodesetval
46-
# don't free xpath object: that would free the nodeset
47-
return NodeSet.new(@node.document, xpath_object)
48-
end
45+
NodeSet.new(xpath_object.value.nodesetval, @node.document)
46+
else
47+
NodeSet.new
4948
end
5049

5150
LibXML.xmlXPathFreeObject(xpath_object)
52-
retval || NodeSet.new(@node.document)
51+
retval
5352
end
5453

5554
def register_namespaces(namespaces) : Nil

0 commit comments

Comments
 (0)