nokogiri-maglev- 1.5.0.1 → 1.5.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.ja.rdoc +56 -12
- data/CHANGELOG.rdoc +49 -0
- data/C_CODING_STYLE.rdoc +27 -0
- data/Manifest.txt +4 -0
- data/README.rdoc +11 -7
- data/Rakefile +42 -27
- data/bin/nokogiri +10 -2
- data/ext/nokogiri/extconf.rb +11 -3
- data/ext/nokogiri/html_document.c +16 -0
- data/ext/nokogiri/html_sax_parser_context.c +59 -37
- data/ext/nokogiri/html_sax_push_parser.c +87 -0
- data/ext/nokogiri/html_sax_push_parser.h +9 -0
- data/ext/nokogiri/nokogiri.c +7 -9
- data/ext/nokogiri/nokogiri.h +3 -0
- data/ext/nokogiri/xml_document.c +101 -3
- data/ext/nokogiri/xml_document.h +3 -3
- data/ext/nokogiri/xml_node.c +151 -58
- data/ext/nokogiri/xml_node_set.c +169 -120
- data/ext/nokogiri/xml_node_set.h +5 -0
- data/ext/nokogiri/xml_sax_parser_context.c +64 -41
- data/ext/nokogiri/xml_text.c +2 -0
- data/ext/nokogiri/xml_xpath_context.c +31 -25
- data/ext/nokogiri/xslt_stylesheet.c +62 -16
- data/ext/nokogiri/xslt_stylesheet.h +5 -0
- data/lib/nokogiri/css/parser.rb +165 -159
- data/lib/nokogiri/css/parser.y +6 -3
- data/lib/nokogiri/css/tokenizer.rb +1 -1
- data/lib/nokogiri/css/tokenizer.rex +1 -1
- data/lib/nokogiri/html.rb +1 -0
- data/lib/nokogiri/html/document.rb +82 -42
- data/lib/nokogiri/html/sax/push_parser.rb +16 -0
- data/lib/nokogiri/version.rb +1 -1
- data/lib/nokogiri/xml.rb +6 -0
- data/lib/nokogiri/xml/builder.rb +7 -1
- data/lib/nokogiri/xml/document.rb +32 -17
- data/lib/nokogiri/xml/document_fragment.rb +6 -1
- data/lib/nokogiri/xml/node.rb +40 -9
- data/lib/nokogiri/xslt.rb +5 -1
- data/tasks/cross_compile.rb +1 -0
- data/tasks/nokogiri.org.rb +6 -0
- data/tasks/test.rb +1 -0
- data/test/css/test_xpath_visitor.rb +6 -0
- data/test/helper.rb +1 -0
- data/test/html/test_document.rb +26 -0
- data/test/html/test_document_fragment.rb +1 -2
- data/test/test_memory_leak.rb +81 -1
- data/test/test_xslt_transforms.rb +152 -123
- data/test/xml/test_builder.rb +24 -2
- data/test/xml/test_c14n.rb +151 -0
- data/test/xml/test_document.rb +48 -0
- data/test/xml/test_namespace.rb +5 -0
- data/test/xml/test_node.rb +82 -1
- data/test/xml/test_node_attributes.rb +19 -0
- data/test/xml/test_node_inheritance.rb +32 -0
- data/test/xml/test_node_reparenting.rb +32 -0
- data/test/xml/test_node_set.rb +16 -8
- data/test/xml/test_reader_encoding.rb +16 -0
- data/test/xml/test_unparented_node.rb +32 -0
- data/test/xml/test_xinclude.rb +83 -0
- data/test/xml/test_xpath.rb +22 -0
- metadata +208 -241
data/ext/nokogiri/xml_node_set.c
CHANGED
@@ -3,6 +3,69 @@
|
|
3
3
|
|
4
4
|
static ID decorate ;
|
5
5
|
|
6
|
+
static int dealloc_namespace(xmlNsPtr ns)
|
7
|
+
{
|
8
|
+
if (ns->href)
|
9
|
+
xmlFree((xmlChar *)ns->href);
|
10
|
+
if (ns->prefix)
|
11
|
+
xmlFree((xmlChar *)ns->prefix);
|
12
|
+
xmlFree(ns);
|
13
|
+
return ST_CONTINUE;
|
14
|
+
}
|
15
|
+
|
16
|
+
static void deallocate(nokogiriNodeSetTuple *tuple)
|
17
|
+
{
|
18
|
+
/*
|
19
|
+
* xmlXPathFreeNodeSet() contains an implicit assumption that it is being
|
20
|
+
* called before any of its pointed-to nodes have been free()d. this
|
21
|
+
* assumption lies in the operation where it dereferences nodeTab pointers
|
22
|
+
* while searching for namespace nodes to free.
|
23
|
+
*
|
24
|
+
* however, since Ruby's GC mechanism cannot guarantee the strict order in
|
25
|
+
* which ruby objects will be GC'd, nodes may be garbage collected before a
|
26
|
+
* nodeset containing pointers to those nodes. (this is true regardless of
|
27
|
+
* how we declare dependencies between objects with rb_gc_mark().)
|
28
|
+
*
|
29
|
+
* as a result, xmlXPathFreeNodeSet() will perform unsafe memory operations,
|
30
|
+
* and calling it would be evil.
|
31
|
+
*
|
32
|
+
* so here, we *manually* free the set of namespace nodes that was
|
33
|
+
* constructed at initialization time (see Nokogiri_wrap_xml_node_set()), as
|
34
|
+
* well as the NodeSet, without using the official xmlXPathFreeNodeSet().
|
35
|
+
*
|
36
|
+
* there's probably a lesson in here somewhere about intermingling, within a
|
37
|
+
* single array, structs with different memory-ownership semantics. or more
|
38
|
+
* generally, a lesson about building an API in C/C++ that does not contain
|
39
|
+
* assumptions about the strict order in which memory will be released. hey,
|
40
|
+
* that sounds like a great idea for a blog post! get to it!
|
41
|
+
*
|
42
|
+
* "In Valgrind We Trust." seriously.
|
43
|
+
*/
|
44
|
+
xmlNodeSetPtr node_set;
|
45
|
+
|
46
|
+
node_set = tuple->node_set;
|
47
|
+
|
48
|
+
if (!node_set)
|
49
|
+
return;
|
50
|
+
|
51
|
+
NOKOGIRI_DEBUG_START(node_set) ;
|
52
|
+
st_foreach(tuple->namespaces, dealloc_namespace, 0);
|
53
|
+
|
54
|
+
if (node_set->nodeTab != NULL)
|
55
|
+
xmlFree(node_set->nodeTab);
|
56
|
+
|
57
|
+
xmlFree(node_set);
|
58
|
+
st_free_table(tuple->namespaces);
|
59
|
+
free(tuple);
|
60
|
+
NOKOGIRI_DEBUG_END(node_set) ;
|
61
|
+
}
|
62
|
+
|
63
|
+
static VALUE allocate(VALUE klass)
|
64
|
+
{
|
65
|
+
return Nokogiri_wrap_xml_node_set(xmlXPathNodeSetCreate(NULL), Qnil);
|
66
|
+
}
|
67
|
+
|
68
|
+
|
6
69
|
/*
|
7
70
|
* call-seq:
|
8
71
|
* dup
|
@@ -11,12 +74,12 @@ static ID decorate ;
|
|
11
74
|
*/
|
12
75
|
static VALUE duplicate(VALUE self)
|
13
76
|
{
|
14
|
-
|
77
|
+
nokogiriNodeSetTuple *tuple;
|
15
78
|
xmlNodeSetPtr dupl;
|
16
79
|
|
17
|
-
Data_Get_Struct(self,
|
80
|
+
Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
|
18
81
|
|
19
|
-
dupl = xmlXPathNodeSetMerge(NULL, node_set);
|
82
|
+
dupl = xmlXPathNodeSetMerge(NULL, tuple->node_set);
|
20
83
|
|
21
84
|
return Nokogiri_wrap_xml_node_set(dupl, rb_iv_get(self, "@document"));
|
22
85
|
}
|
@@ -29,13 +92,10 @@ static VALUE duplicate(VALUE self)
|
|
29
92
|
*/
|
30
93
|
static VALUE length(VALUE self)
|
31
94
|
{
|
32
|
-
|
33
|
-
Data_Get_Struct(self,
|
95
|
+
nokogiriNodeSetTuple *tuple;
|
96
|
+
Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
|
34
97
|
|
35
|
-
|
36
|
-
return INT2NUM(node_set->nodeNr);
|
37
|
-
|
38
|
-
return INT2NUM(0);
|
98
|
+
return tuple->node_set ? INT2NUM(tuple->node_set->nodeNr) : INT2NUM(0);
|
39
99
|
}
|
40
100
|
|
41
101
|
/*
|
@@ -46,15 +106,15 @@ static VALUE length(VALUE self)
|
|
46
106
|
*/
|
47
107
|
static VALUE push(VALUE self, VALUE rb_node)
|
48
108
|
{
|
49
|
-
|
109
|
+
nokogiriNodeSetTuple *tuple;
|
50
110
|
xmlNodePtr node;
|
51
111
|
|
52
112
|
if(!( rb_obj_is_kind_of_(rb_node, cNokogiriXmlNode) || rb_obj_is_kind_of_(rb_node, cNokogiriXmlNamespace)))
|
53
113
|
rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node or Nokogiri::XML::Namespace");
|
54
114
|
|
55
|
-
Data_Get_Struct(self,
|
115
|
+
Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
|
56
116
|
Data_Get_Struct(rb_node, xmlNode, node);
|
57
|
-
xmlXPathNodeSetAdd(node_set, node);
|
117
|
+
xmlXPathNodeSetAdd(tuple->node_set, node);
|
58
118
|
return self;
|
59
119
|
}
|
60
120
|
|
@@ -65,23 +125,32 @@ static VALUE push(VALUE self, VALUE rb_node)
|
|
65
125
|
* Delete +node+ from the Nodeset, if it is a member. Returns the deleted node
|
66
126
|
* if found, otherwise returns nil.
|
67
127
|
*/
|
68
|
-
static VALUE
|
128
|
+
static VALUE
|
129
|
+
delete(VALUE self, VALUE rb_node)
|
69
130
|
{
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
131
|
+
nokogiriNodeSetTuple *tuple;
|
132
|
+
xmlNodePtr node;
|
133
|
+
xmlNodeSetPtr cur;
|
134
|
+
int i;
|
135
|
+
|
136
|
+
if (!(RTEST(rb_obj_is_kind_of(rb_node, cNokogiriXmlNode)) || RTEST(rb_obj_is_kind_of(rb_node, cNokogiriXmlNamespace))))
|
137
|
+
rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node or Nokogiri::XML::Namespace");
|
138
|
+
|
139
|
+
Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
|
140
|
+
Data_Get_Struct(rb_node, xmlNode, node);
|
141
|
+
cur = tuple->node_set;
|
142
|
+
|
143
|
+
if (xmlXPathNodeSetContains(cur, node)) {
|
144
|
+
for (i = 0; i < cur->nodeNr; i++)
|
145
|
+
if (cur->nodeTab[i] == node) break;
|
146
|
+
|
147
|
+
cur->nodeNr--;
|
148
|
+
for (;i < cur->nodeNr;i++)
|
149
|
+
cur->nodeTab[i] = cur->nodeTab[i + 1];
|
150
|
+
cur->nodeTab[cur->nodeNr] = NULL;
|
151
|
+
return rb_node;
|
152
|
+
}
|
153
|
+
return Qnil ;
|
85
154
|
}
|
86
155
|
|
87
156
|
|
@@ -93,16 +162,17 @@ static VALUE delete(VALUE self, VALUE rb_node)
|
|
93
162
|
*/
|
94
163
|
static VALUE intersection(VALUE self, VALUE rb_other)
|
95
164
|
{
|
96
|
-
|
97
|
-
xmlNodeSetPtr
|
165
|
+
nokogiriNodeSetTuple *tuple, *other;
|
166
|
+
xmlNodeSetPtr intersection;
|
98
167
|
|
99
168
|
if(! rb_obj_is_kind_of_(rb_other, cNokogiriXmlNodeSet))
|
100
169
|
rb_raise(rb_eArgError, "node_set must be a Nokogiri::XML::NodeSet");
|
101
170
|
|
102
|
-
Data_Get_Struct(self,
|
103
|
-
Data_Get_Struct(rb_other,
|
171
|
+
Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
|
172
|
+
Data_Get_Struct(rb_other, nokogiriNodeSetTuple, other);
|
104
173
|
|
105
|
-
|
174
|
+
intersection = xmlXPathIntersection(tuple->node_set, other->node_set);
|
175
|
+
return Nokogiri_wrap_xml_node_set(intersection, rb_iv_get(self, "@document"));
|
106
176
|
}
|
107
177
|
|
108
178
|
|
@@ -114,16 +184,16 @@ static VALUE intersection(VALUE self, VALUE rb_other)
|
|
114
184
|
*/
|
115
185
|
static VALUE include_eh(VALUE self, VALUE rb_node)
|
116
186
|
{
|
117
|
-
|
187
|
+
nokogiriNodeSetTuple *tuple;
|
118
188
|
xmlNodePtr node;
|
119
189
|
|
120
190
|
if(!( rb_obj_is_kind_of_(rb_node, cNokogiriXmlNode) || rb_obj_is_kind_of_(rb_node, cNokogiriXmlNamespace)))
|
121
191
|
rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node or Nokogiri::XML::Namespace");
|
122
192
|
|
123
|
-
Data_Get_Struct(self,
|
193
|
+
Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
|
124
194
|
Data_Get_Struct(rb_node, xmlNode, node);
|
125
195
|
|
126
|
-
return (xmlXPathNodeSetContains(node_set, node) ? Qtrue : Qfalse);
|
196
|
+
return (xmlXPathNodeSetContains(tuple->node_set, node) ? Qtrue : Qfalse);
|
127
197
|
}
|
128
198
|
|
129
199
|
|
@@ -136,18 +206,17 @@ static VALUE include_eh(VALUE self, VALUE rb_node)
|
|
136
206
|
*/
|
137
207
|
static VALUE set_union(VALUE self, VALUE rb_other)
|
138
208
|
{
|
139
|
-
|
140
|
-
xmlNodeSetPtr other;
|
209
|
+
nokogiriNodeSetTuple *tuple, *other;
|
141
210
|
xmlNodeSetPtr new;
|
142
211
|
|
143
212
|
if(! rb_obj_is_kind_of_(rb_other, cNokogiriXmlNodeSet))
|
144
213
|
rb_raise(rb_eArgError, "node_set must be a Nokogiri::XML::NodeSet");
|
145
214
|
|
146
|
-
Data_Get_Struct(self,
|
147
|
-
Data_Get_Struct(rb_other,
|
215
|
+
Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
|
216
|
+
Data_Get_Struct(rb_other, nokogiriNodeSetTuple, other);
|
148
217
|
|
149
|
-
new = xmlXPathNodeSetMerge(NULL, node_set);
|
150
|
-
new = xmlXPathNodeSetMerge(new, other);
|
218
|
+
new = xmlXPathNodeSetMerge(NULL, tuple->node_set);
|
219
|
+
new = xmlXPathNodeSetMerge(new, other->node_set);
|
151
220
|
|
152
221
|
return Nokogiri_wrap_xml_node_set(new, rb_iv_get(self, "@document"));
|
153
222
|
}
|
@@ -161,20 +230,19 @@ static VALUE set_union(VALUE self, VALUE rb_other)
|
|
161
230
|
*/
|
162
231
|
static VALUE minus(VALUE self, VALUE rb_other)
|
163
232
|
{
|
164
|
-
|
165
|
-
xmlNodeSetPtr other;
|
233
|
+
nokogiriNodeSetTuple *tuple, *other;
|
166
234
|
xmlNodeSetPtr new;
|
167
235
|
int j ;
|
168
236
|
|
169
237
|
if(! rb_obj_is_kind_of_(rb_other, cNokogiriXmlNodeSet))
|
170
238
|
rb_raise(rb_eArgError, "node_set must be a Nokogiri::XML::NodeSet");
|
171
239
|
|
172
|
-
Data_Get_Struct(self,
|
173
|
-
Data_Get_Struct(rb_other,
|
240
|
+
Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
|
241
|
+
Data_Get_Struct(rb_other, nokogiriNodeSetTuple, other);
|
174
242
|
|
175
|
-
new = xmlXPathNodeSetMerge(NULL, node_set);
|
176
|
-
for (j = 0 ; j < other->nodeNr ; ++j) {
|
177
|
-
xmlXPathNodeSetDel(new, other->nodeTab[j]);
|
243
|
+
new = xmlXPathNodeSetMerge(NULL, tuple->node_set);
|
244
|
+
for (j = 0 ; j < other->node_set->nodeNr ; ++j) {
|
245
|
+
xmlXPathNodeSetDel(new, other->node_set->nodeTab[j]);
|
178
246
|
}
|
179
247
|
|
180
248
|
return Nokogiri_wrap_xml_node_set(new, rb_iv_get(self, "@document"));
|
@@ -184,10 +252,16 @@ static VALUE minus(VALUE self, VALUE rb_other)
|
|
184
252
|
static VALUE index_at(VALUE self, long offset)
|
185
253
|
{
|
186
254
|
xmlNodeSetPtr node_set;
|
187
|
-
|
255
|
+
nokogiriNodeSetTuple *tuple;
|
256
|
+
|
257
|
+
Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
|
258
|
+
node_set = tuple->node_set;
|
259
|
+
|
260
|
+
if (offset >= node_set->nodeNr || abs((int)offset) > node_set->nodeNr)
|
261
|
+
return Qnil;
|
188
262
|
|
189
|
-
if
|
190
|
-
|
263
|
+
if (offset < 0)
|
264
|
+
offset += node_set->nodeNr;
|
191
265
|
|
192
266
|
if (XML_NAMESPACE_DECL == node_set->nodeTab[offset]->type)
|
193
267
|
return Nokogiri_wrap_xml_namespace2(rb_iv_get(self, "@document"), (xmlNsPtr)(node_set->nodeTab[offset]));
|
@@ -197,10 +271,12 @@ static VALUE index_at(VALUE self, long offset)
|
|
197
271
|
static VALUE subseq(VALUE self, long beg, long len)
|
198
272
|
{
|
199
273
|
long j;
|
274
|
+
nokogiriNodeSetTuple *tuple;
|
200
275
|
xmlNodeSetPtr node_set;
|
201
276
|
xmlNodeSetPtr new_set ;
|
202
277
|
|
203
|
-
Data_Get_Struct(self,
|
278
|
+
Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
|
279
|
+
node_set = tuple->node_set;
|
204
280
|
|
205
281
|
if (beg > node_set->nodeNr) return Qnil ;
|
206
282
|
if (beg < 0 || len < 0) return Qnil ;
|
@@ -236,7 +312,9 @@ static VALUE slice(int argc, VALUE *argv, VALUE self)
|
|
236
312
|
VALUE arg ;
|
237
313
|
long beg, len ;
|
238
314
|
xmlNodeSetPtr node_set;
|
239
|
-
|
315
|
+
nokogiriNodeSetTuple *tuple;
|
316
|
+
Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
|
317
|
+
node_set = tuple->node_set;
|
240
318
|
|
241
319
|
if (argc == 2) {
|
242
320
|
beg = NUM2LONG(argv[0]);
|
@@ -282,25 +360,17 @@ static VALUE to_array(VALUE self, VALUE rb_node)
|
|
282
360
|
VALUE *elts;
|
283
361
|
VALUE list;
|
284
362
|
int i;
|
363
|
+
nokogiriNodeSetTuple *tuple;
|
285
364
|
|
286
|
-
Data_Get_Struct(self,
|
365
|
+
Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
|
366
|
+
set = tuple->node_set;
|
287
367
|
|
288
368
|
elts = calloc((size_t)set->nodeNr, sizeof(VALUE *));
|
289
369
|
for(i = 0; i < set->nodeNr; i++) {
|
290
|
-
if (XML_NAMESPACE_DECL == set->nodeTab[i]->type)
|
370
|
+
if (XML_NAMESPACE_DECL == set->nodeTab[i]->type)
|
291
371
|
elts[i] = Nokogiri_wrap_xml_namespace2(rb_iv_get(self, "@document"), (xmlNsPtr)(set->nodeTab[i]));
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
if(node->_private) {
|
296
|
-
if(node->type == XML_DOCUMENT_NODE || node->type == XML_HTML_DOCUMENT_NODE)
|
297
|
-
elts[i] = DOC_RUBY_OBJECT(node->doc);
|
298
|
-
else
|
299
|
-
elts[i] = (VALUE)node->_private;
|
300
|
-
} else {
|
301
|
-
elts[i] = Nokogiri_wrap_xml_node(Qnil, node);
|
302
|
-
}
|
303
|
-
}
|
372
|
+
else
|
373
|
+
elts[i] = Nokogiri_wrap_xml_node(Qnil, set->nodeTab[i]);
|
304
374
|
}
|
305
375
|
|
306
376
|
list = rb_ary_new4((long)set->nodeNr, elts);
|
@@ -320,8 +390,10 @@ static VALUE unlink_nodeset(VALUE self)
|
|
320
390
|
{
|
321
391
|
xmlNodeSetPtr node_set;
|
322
392
|
int j, nodeNr ;
|
393
|
+
nokogiriNodeSetTuple *tuple;
|
323
394
|
|
324
|
-
Data_Get_Struct(self,
|
395
|
+
Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
|
396
|
+
node_set = tuple->node_set;
|
325
397
|
nodeNr = node_set->nodeNr ;
|
326
398
|
for (j = 0 ; j < nodeNr ; j++) {
|
327
399
|
if (XML_NAMESPACE_DECL != node_set->nodeTab[j]->type) {
|
@@ -336,68 +408,45 @@ static VALUE unlink_nodeset(VALUE self)
|
|
336
408
|
return self ;
|
337
409
|
}
|
338
410
|
|
339
|
-
|
340
|
-
static void deallocate(xmlNodeSetPtr node_set)
|
341
|
-
{
|
342
|
-
/*
|
343
|
-
* xmlXPathFreeNodeSet() contains an implicit assumption that it is being
|
344
|
-
* called before any of its pointed-to nodes have been free()d. this
|
345
|
-
* assumption lies in the operation where it dereferences nodeTab pointers
|
346
|
-
* while searching for namespace nodes to free.
|
347
|
-
*
|
348
|
-
* however, since Ruby's GC mechanism cannot guarantee the strict order in
|
349
|
-
* which ruby objects will be GC'd, nodes may be garbage collected before a
|
350
|
-
* nodeset containing pointers to those nodes. (this is true regardless of
|
351
|
-
* how we declare dependencies between objects with rb_gc_mark().)
|
352
|
-
*
|
353
|
-
* as a result, xmlXPathFreeNodeSet() will perform unsafe memory operations,
|
354
|
-
* and calling it would be evil.
|
355
|
-
*
|
356
|
-
* on the bright side, though, Nokogiri's API currently does not cause
|
357
|
-
* namespace nodes to be included in node sets, ever.
|
358
|
-
*
|
359
|
-
* armed with that fact, we examined xmlXPathFreeNodeSet() and related libxml
|
360
|
-
* code and determined that, within the Nokogiri abstraction, we will not
|
361
|
-
* leak memory if we simply free the node set's memory directly. that's only
|
362
|
-
* quasi-evil!
|
363
|
-
*
|
364
|
-
* there's probably a lesson in here somewhere about intermingling, within a
|
365
|
-
* single array, structs with different memory-ownership semantics. or more
|
366
|
-
* generally, a lesson about building an API in C/C++ that does not contain
|
367
|
-
* assumptions about the strict order in which memory will be released. hey,
|
368
|
-
* that sounds like a great idea for a blog post! get to it!
|
369
|
-
*
|
370
|
-
* "In Valgrind We Trust." seriously.
|
371
|
-
*/
|
372
|
-
NOKOGIRI_DEBUG_START(node_set) ;
|
373
|
-
if (node_set->nodeTab != NULL)
|
374
|
-
xmlFree(node_set->nodeTab);
|
375
|
-
xmlFree(node_set);
|
376
|
-
NOKOGIRI_DEBUG_END(node_set) ;
|
377
|
-
}
|
378
|
-
|
379
|
-
static VALUE allocate(VALUE klass)
|
380
|
-
{
|
381
|
-
return Nokogiri_wrap_xml_node_set(xmlXPathNodeSetCreate(NULL), Qnil);
|
382
|
-
}
|
383
|
-
|
384
411
|
VALUE Nokogiri_wrap_xml_node_set(xmlNodeSetPtr node_set, VALUE document)
|
385
412
|
{
|
386
413
|
VALUE new_set ;
|
387
|
-
|
388
|
-
|
414
|
+
int i;
|
415
|
+
xmlNodePtr cur;
|
416
|
+
xmlNsPtr ns;
|
417
|
+
nokogiriNodeSetTuple *tuple;
|
418
|
+
|
419
|
+
new_set = Data_Make_Struct(cNokogiriXmlNodeSet, nokogiriNodeSetTuple, 0,
|
420
|
+
deallocate, tuple);
|
421
|
+
|
422
|
+
tuple->node_set = node_set;
|
423
|
+
tuple->namespaces = st_init_numtable();
|
424
|
+
|
425
|
+
if (!NIL_P(document)) {
|
389
426
|
rb_iv_set(new_set, "@document", document);
|
390
427
|
rb_funcall(document, decorate, 1, new_set);
|
391
428
|
}
|
429
|
+
|
430
|
+
if (node_set->nodeTab) {
|
431
|
+
for (i = 0; i < node_set->nodeNr; i++) {
|
432
|
+
cur = node_set->nodeTab[i];
|
433
|
+
if (cur && cur->type == XML_NAMESPACE_DECL) {
|
434
|
+
ns = (xmlNsPtr)cur;
|
435
|
+
if (ns->next && ns->next->type != XML_NAMESPACE_DECL)
|
436
|
+
st_insert(tuple->namespaces, (st_data_t)cur, (st_data_t)0);
|
437
|
+
}
|
438
|
+
}
|
439
|
+
}
|
440
|
+
|
392
441
|
return new_set ;
|
393
442
|
}
|
394
443
|
|
395
444
|
VALUE cNokogiriXmlNodeSet ;
|
396
445
|
void init_xml_node_set(void)
|
397
446
|
{
|
398
|
-
VALUE nokogiri
|
399
|
-
VALUE xml
|
400
|
-
VALUE klass
|
447
|
+
VALUE nokogiri = rb_define_module("Nokogiri");
|
448
|
+
VALUE xml = rb_define_module_under(nokogiri, "XML");
|
449
|
+
VALUE klass = rb_define_class_under(xml, "NodeSet", rb_cObject);
|
401
450
|
cNokogiriXmlNodeSet = klass;
|
402
451
|
|
403
452
|
rb_define_alloc_func(klass, allocate);
|
@@ -414,5 +463,5 @@ void init_xml_node_set(void)
|
|
414
463
|
rb_define_method(klass, "&", intersection, 1);
|
415
464
|
rb_define_method(klass, "include?", include_eh, 1);
|
416
465
|
|
417
|
-
decorate
|
466
|
+
decorate = rb_intern("decorate");
|
418
467
|
}
|
data/ext/nokogiri/xml_node_set.h
CHANGED
@@ -6,4 +6,9 @@ void init_xml_node_set();
|
|
6
6
|
|
7
7
|
extern VALUE cNokogiriXmlNodeSet ;
|
8
8
|
VALUE Nokogiri_wrap_xml_node_set(xmlNodeSetPtr node_set, VALUE document) ;
|
9
|
+
|
10
|
+
typedef struct _nokogiriNodeSetTuple {
|
11
|
+
xmlNodeSetPtr node_set;
|
12
|
+
st_table *namespaces;
|
13
|
+
} nokogiriNodeSetTuple;
|
9
14
|
#endif
|