/
xml_namespace.c
156 lines (133 loc) · 4.26 KB
/
xml_namespace.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#include <nokogiri.h>
/*
* The lifecycle of a Namespace node is more complicated than other Nodes, for two reasons:
*
* 1. the underlying C structure has a different layout than all the other node structs, with the
* `_private` member where we store a pointer to Ruby object data not being in first position.
* 2. xmlNs structures returned in an xmlNodeset from an XPath query are copies of the document's
* namespaces, and so do not share the same memory lifecycle as everything else in a document.
*
* As a result of 1, you may see special handling of XML_NAMESPACE_DECL node types throughout the
* Nokogiri C code, though I intend to wrap up that logic in ruby_object_{get,set} functions
* shortly.
*
* As a result of 2, you will see we have special handling in this file and in xml_node_set.c to
* carefully manage the memory lifecycle of xmlNs structs to match the Ruby object's GC
* lifecycle. In xml_node_set.c we have local versions of xmlXPathNodeSetDel() and
* xmlXPathFreeNodeSet() that avoid freeing xmlNs structs in the node set. In this file, we decide
* whether or not to call dealloc_namespace() depending on whether the xmlNs struct appears to be
* in an xmlNodeSet (and thus the result of an XPath query) or not.
*
* Yes, this is madness.
*/
VALUE cNokogiriXmlNamespace ;
static void
_xml_namespace_dealloc(void *ptr)
{
/*
* this deallocator is only used for namespace nodes that are part of an xpath
* node set. see noko_xml_namespace_wrap().
*/
xmlNsPtr ns = ptr;
NOKOGIRI_DEBUG_START(ns) ;
if (ns->href) {
xmlFree(DISCARD_CONST_QUAL_XMLCHAR(ns->href));
}
if (ns->prefix) {
xmlFree(DISCARD_CONST_QUAL_XMLCHAR(ns->prefix));
}
xmlFree(ns);
NOKOGIRI_DEBUG_END(ns) ;
}
#ifdef HAVE_RB_GC_LOCATION
static void
_xml_namespace_update_references(void *ptr)
{
xmlNsPtr ns = ptr;
if (ns->_private) {
ns->_private = (void *)rb_gc_location((VALUE)ns->_private);
}
}
#else
# define _xml_namespace_update_references 0
#endif
static const rb_data_type_t nokogiri_xml_namespace_type_with_dealloc = {
"Nokogiri/XMLNamespace/WithDealloc",
{0, _xml_namespace_dealloc, 0, _xml_namespace_update_references},
0, 0,
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
RUBY_TYPED_FREE_IMMEDIATELY,
#endif
};
static const rb_data_type_t nokogiri_xml_namespace_type_without_dealloc = {
"Nokogiri/XMLNamespace/WithoutDealloc",
{0, 0, 0, _xml_namespace_update_references},
0, 0,
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
RUBY_TYPED_FREE_IMMEDIATELY,
#endif
};
/*
* call-seq:
* prefix
*
* Get the prefix for this namespace. Returns +nil+ if there is no prefix.
*/
static VALUE
prefix(VALUE self)
{
xmlNsPtr ns;
Noko_Namespace_Get_Struct(self, xmlNs, ns);
if (!ns->prefix) { return Qnil; }
return NOKOGIRI_STR_NEW2(ns->prefix);
}
/*
* call-seq:
* href
*
* Get the href for this namespace
*/
static VALUE
href(VALUE self)
{
xmlNsPtr ns;
Noko_Namespace_Get_Struct(self, xmlNs, ns);
if (!ns->href) { return Qnil; }
return NOKOGIRI_STR_NEW2(ns->href);
}
VALUE
noko_xml_namespace_wrap(xmlNsPtr c_namespace, xmlDocPtr c_document)
{
VALUE rb_namespace;
if (c_namespace->_private) {
return (VALUE)c_namespace->_private;
}
if (c_document) {
rb_namespace = TypedData_Wrap_Struct(cNokogiriXmlNamespace,
&nokogiri_xml_namespace_type_without_dealloc,
c_namespace);
if (DOC_RUBY_OBJECT_TEST(c_document)) {
rb_iv_set(rb_namespace, "@document", DOC_RUBY_OBJECT(c_document));
rb_ary_push(DOC_NODE_CACHE(c_document), rb_namespace);
}
} else {
rb_namespace = TypedData_Wrap_Struct(cNokogiriXmlNamespace,
&nokogiri_xml_namespace_type_with_dealloc,
c_namespace);
}
c_namespace->_private = (void *)rb_namespace;
return rb_namespace;
}
VALUE
noko_xml_namespace_wrap_xpath_copy(xmlNsPtr c_namespace)
{
return noko_xml_namespace_wrap(c_namespace, NULL);
}
void
noko_init_xml_namespace()
{
cNokogiriXmlNamespace = rb_define_class_under(mNokogiriXml, "Namespace", rb_cObject);
rb_undef_alloc_func(cNokogiriXmlNamespace);
rb_define_method(cNokogiriXmlNamespace, "prefix", prefix, 0);
rb_define_method(cNokogiriXmlNamespace, "href", href, 0);
}