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.
Files changed (61) hide show
  1. data/CHANGELOG.ja.rdoc +56 -12
  2. data/CHANGELOG.rdoc +49 -0
  3. data/C_CODING_STYLE.rdoc +27 -0
  4. data/Manifest.txt +4 -0
  5. data/README.rdoc +11 -7
  6. data/Rakefile +42 -27
  7. data/bin/nokogiri +10 -2
  8. data/ext/nokogiri/extconf.rb +11 -3
  9. data/ext/nokogiri/html_document.c +16 -0
  10. data/ext/nokogiri/html_sax_parser_context.c +59 -37
  11. data/ext/nokogiri/html_sax_push_parser.c +87 -0
  12. data/ext/nokogiri/html_sax_push_parser.h +9 -0
  13. data/ext/nokogiri/nokogiri.c +7 -9
  14. data/ext/nokogiri/nokogiri.h +3 -0
  15. data/ext/nokogiri/xml_document.c +101 -3
  16. data/ext/nokogiri/xml_document.h +3 -3
  17. data/ext/nokogiri/xml_node.c +151 -58
  18. data/ext/nokogiri/xml_node_set.c +169 -120
  19. data/ext/nokogiri/xml_node_set.h +5 -0
  20. data/ext/nokogiri/xml_sax_parser_context.c +64 -41
  21. data/ext/nokogiri/xml_text.c +2 -0
  22. data/ext/nokogiri/xml_xpath_context.c +31 -25
  23. data/ext/nokogiri/xslt_stylesheet.c +62 -16
  24. data/ext/nokogiri/xslt_stylesheet.h +5 -0
  25. data/lib/nokogiri/css/parser.rb +165 -159
  26. data/lib/nokogiri/css/parser.y +6 -3
  27. data/lib/nokogiri/css/tokenizer.rb +1 -1
  28. data/lib/nokogiri/css/tokenizer.rex +1 -1
  29. data/lib/nokogiri/html.rb +1 -0
  30. data/lib/nokogiri/html/document.rb +82 -42
  31. data/lib/nokogiri/html/sax/push_parser.rb +16 -0
  32. data/lib/nokogiri/version.rb +1 -1
  33. data/lib/nokogiri/xml.rb +6 -0
  34. data/lib/nokogiri/xml/builder.rb +7 -1
  35. data/lib/nokogiri/xml/document.rb +32 -17
  36. data/lib/nokogiri/xml/document_fragment.rb +6 -1
  37. data/lib/nokogiri/xml/node.rb +40 -9
  38. data/lib/nokogiri/xslt.rb +5 -1
  39. data/tasks/cross_compile.rb +1 -0
  40. data/tasks/nokogiri.org.rb +6 -0
  41. data/tasks/test.rb +1 -0
  42. data/test/css/test_xpath_visitor.rb +6 -0
  43. data/test/helper.rb +1 -0
  44. data/test/html/test_document.rb +26 -0
  45. data/test/html/test_document_fragment.rb +1 -2
  46. data/test/test_memory_leak.rb +81 -1
  47. data/test/test_xslt_transforms.rb +152 -123
  48. data/test/xml/test_builder.rb +24 -2
  49. data/test/xml/test_c14n.rb +151 -0
  50. data/test/xml/test_document.rb +48 -0
  51. data/test/xml/test_namespace.rb +5 -0
  52. data/test/xml/test_node.rb +82 -1
  53. data/test/xml/test_node_attributes.rb +19 -0
  54. data/test/xml/test_node_inheritance.rb +32 -0
  55. data/test/xml/test_node_reparenting.rb +32 -0
  56. data/test/xml/test_node_set.rb +16 -8
  57. data/test/xml/test_reader_encoding.rb +16 -0
  58. data/test/xml/test_unparented_node.rb +32 -0
  59. data/test/xml/test_xinclude.rb +83 -0
  60. data/test/xml/test_xpath.rb +22 -0
  61. metadata +208 -241
@@ -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
- xmlNodeSetPtr node_set;
77
+ nokogiriNodeSetTuple *tuple;
15
78
  xmlNodeSetPtr dupl;
16
79
 
17
- Data_Get_Struct(self, xmlNodeSet, node_set);
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
- xmlNodeSetPtr node_set;
33
- Data_Get_Struct(self, xmlNodeSet, node_set);
95
+ nokogiriNodeSetTuple *tuple;
96
+ Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
34
97
 
35
- if(node_set)
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
- xmlNodeSetPtr node_set;
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, xmlNodeSet, node_set);
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 delete(VALUE self, VALUE rb_node)
128
+ static VALUE
129
+ delete(VALUE self, VALUE rb_node)
69
130
  {
70
- xmlNodeSetPtr node_set ;
71
- xmlNodePtr node ;
72
-
73
- if(!( rb_obj_is_kind_of_(rb_node, cNokogiriXmlNode) || rb_obj_is_kind_of_(rb_node, cNokogiriXmlNamespace)))
74
- rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node or Nokogiri::XML::Namespace");
75
-
76
- Data_Get_Struct(self, xmlNodeSet, node_set);
77
- Data_Get_Struct(rb_node, xmlNode, node);
78
-
79
- if (xmlXPathNodeSetContains(node_set, node)) {
80
- xmlXPathNodeSetDel(node_set, node);
81
- return rb_node ;
82
- }
83
-
84
- return Qnil ;
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
- xmlNodeSetPtr node_set;
97
- xmlNodeSetPtr other;
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, xmlNodeSet, node_set);
103
- Data_Get_Struct(rb_other, xmlNodeSet, other);
171
+ Data_Get_Struct(self, nokogiriNodeSetTuple, tuple);
172
+ Data_Get_Struct(rb_other, nokogiriNodeSetTuple, other);
104
173
 
105
- return Nokogiri_wrap_xml_node_set(xmlXPathIntersection(node_set, other), rb_iv_get(self, "@document"));
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
- xmlNodeSetPtr node_set;
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, xmlNodeSet, node_set);
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
- xmlNodeSetPtr node_set;
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, xmlNodeSet, node_set);
147
- Data_Get_Struct(rb_other, xmlNodeSet, 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
- xmlNodeSetPtr node_set;
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, xmlNodeSet, node_set);
173
- Data_Get_Struct(rb_other, xmlNodeSet, 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
- Data_Get_Struct(self, xmlNodeSet, node_set);
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(offset >= node_set->nodeNr || abs((int)offset) > node_set->nodeNr) return Qnil;
190
- if(offset < 0) offset = offset + node_set->nodeNr;
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, xmlNodeSet, node_set);
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
- Data_Get_Struct(self, xmlNodeSet, node_set);
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, xmlNodeSet, set);
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
- } else {
293
- xmlNodePtr node = set->nodeTab[i];
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, xmlNodeSet, node_set);
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
- new_set = Data_Wrap_Struct(cNokogiriXmlNodeSet, 0, deallocate, node_set);
388
- if (document != Qnil) {
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 = rb_define_module("Nokogiri");
399
- VALUE xml = rb_define_module_under(nokogiri, "XML");
400
- VALUE klass = rb_define_class_under(xml, "NodeSet", rb_cObject);
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 = rb_intern("decorate");
466
+ decorate = rb_intern("decorate");
418
467
  }
@@ -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