Mercurial > pidgin
annotate src/xmlnode.c @ 12233:02833a0ae716
[gaim-migrate @ 14535]
SF Patch #1367116 from Michael Carlson
"In profiling gaim, I noticed that on simply starting
CVS gaim, xmlnode_insert_child is using up by far the
most CPU time. After some testing, I realized the
reason why: xmlnode_insert_child is called some 18,000
times on startup, and it is inserting the child at the
end of the list each time, simply by traversing through
the entire linked list. Sometimes this list can have as
many as 800 elements.
This patch adds a variable to the _xmlnode struct,
lastchild, which simply keeps track of the last node in
the list of children. This is then used by
xmlnode_insert_child to insert at the end of the list,
instead of traversing through the whole list each time.
The two relevant functions in xmlnode.c that need to be
updated to keep track of this function appropriately
have been updated.
Running 3 times with and without the change, the
results from oprofile say it all. Here are the measured
number of clock cycles / % of total clock cycles /
function used to simply start and close gaim before the
change:
204 60.7143 xmlnode_insert_child
210 61.4035 xmlnode_insert_child
230 61.8280 xmlnode_insert_child
And after (note that one time no clock cycles were
caught at all)
3 2.5862 xmlnode_insert_child
3 2.5641 xmlnode_insert_child
This affects other areas of the program than just
starting up, but this seems to be the most noticeable
place."
Speed is good. As I was verifying this patch, I added some g_return_val_if_fail() checks.
committer: Tailor Script <tailor@pidgin.im>
author | Richard Laager <rlaager@wiktel.com> |
---|---|
date | Sun, 27 Nov 2005 03:42:39 +0000 |
parents | 9679b615edb8 |
children | 25e63008d3bb |
rev | line source |
---|---|
7131 | 1 /** |
2 * @file xmlnode.c XML DOM functions | |
3 * | |
4 * gaim | |
5 * | |
8046 | 6 * Gaim is the legal property of its developers, whose names are too numerous |
7 * to list here. Please refer to the COPYRIGHT file distributed with this | |
8 * source distribution. | |
7131 | 9 * |
10 * This program is free software; you can redistribute it and/or modify | |
11 * it under the terms of the GNU General Public License as published by | |
12 * the Free Software Foundation; either version 2 of the License, or | |
13 * (at your option) any later version. | |
14 * | |
15 * This program is distributed in the hope that it will be useful, | |
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 * GNU General Public License for more details. | |
19 * | |
20 * You should have received a copy of the GNU General Public License | |
21 * along with this program; if not, write to the Free Software | |
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
23 */ | |
24 | |
25 /* A lot of this code at least resembles the code in libxode, but since | |
26 * libxode uses memory pools that we simply have no need for, I decided to | |
27 * write my own stuff. Also, re-writing this lets me be as lightweight | |
28 * as I want to be. Thank you libxode for giving me a good starting point */ | |
29 | |
30 #include "internal.h" | |
31 | |
32 #include <string.h> | |
33 #include <glib.h> | |
34 | |
35 #include "xmlnode.h" | |
36 | |
12041
da44f68fb4d2
[gaim-migrate @ 14334]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11705
diff
changeset
|
37 #ifdef _WIN32 |
da44f68fb4d2
[gaim-migrate @ 14334]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11705
diff
changeset
|
38 # define NEWLINE_S "\r\n" |
da44f68fb4d2
[gaim-migrate @ 14334]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11705
diff
changeset
|
39 #else |
da44f68fb4d2
[gaim-migrate @ 14334]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11705
diff
changeset
|
40 # define NEWLINE_S "\n" |
da44f68fb4d2
[gaim-migrate @ 14334]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11705
diff
changeset
|
41 #endif |
da44f68fb4d2
[gaim-migrate @ 14334]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11705
diff
changeset
|
42 |
7131 | 43 static xmlnode* |
8135 | 44 new_node(const char *name, XMLNodeType type) |
7131 | 45 { |
46 xmlnode *node = g_new0(xmlnode, 1); | |
10423 | 47 |
7131 | 48 if(name) |
49 node->name = g_strdup(name); | |
50 node->type = type; | |
51 | |
52 return node; | |
53 } | |
54 | |
55 xmlnode* | |
56 xmlnode_new(const char *name) | |
57 { | |
58 g_return_val_if_fail(name != NULL, NULL); | |
59 | |
8135 | 60 return new_node(name, XMLNODE_TYPE_TAG); |
7131 | 61 } |
62 | |
10423 | 63 xmlnode * |
64 xmlnode_new_child(xmlnode *parent, const char *name) | |
7131 | 65 { |
66 xmlnode *node; | |
67 | |
68 g_return_val_if_fail(parent != NULL, NULL); | |
69 g_return_val_if_fail(name != NULL, NULL); | |
70 | |
8135 | 71 node = new_node(name, XMLNODE_TYPE_TAG); |
7131 | 72 |
73 xmlnode_insert_child(parent, node); | |
74 | |
75 return node; | |
76 } | |
77 | |
78 void | |
79 xmlnode_insert_child(xmlnode *parent, xmlnode *child) | |
80 { | |
81 g_return_if_fail(parent != NULL); | |
82 g_return_if_fail(child != NULL); | |
83 | |
84 child->parent = parent; | |
85 | |
12233
02833a0ae716
[gaim-migrate @ 14535]
Richard Laager <rlaager@wiktel.com>
parents:
12198
diff
changeset
|
86 if(parent->lastchild) { |
02833a0ae716
[gaim-migrate @ 14535]
Richard Laager <rlaager@wiktel.com>
parents:
12198
diff
changeset
|
87 parent->lastchild->next = child; |
7131 | 88 } else { |
89 parent->child = child; | |
90 } | |
12233
02833a0ae716
[gaim-migrate @ 14535]
Richard Laager <rlaager@wiktel.com>
parents:
12198
diff
changeset
|
91 |
02833a0ae716
[gaim-migrate @ 14535]
Richard Laager <rlaager@wiktel.com>
parents:
12198
diff
changeset
|
92 parent->lastchild = child; |
7131 | 93 } |
94 | |
95 void | |
10848 | 96 xmlnode_insert_data(xmlnode *node, const char *data, gssize size) |
7131 | 97 { |
10415 | 98 xmlnode *child; |
10848 | 99 gsize real_size; |
7131 | 100 |
10415 | 101 g_return_if_fail(node != NULL); |
7131 | 102 g_return_if_fail(data != NULL); |
103 g_return_if_fail(size != 0); | |
104 | |
105 real_size = size == -1 ? strlen(data) : size; | |
106 | |
10415 | 107 child = new_node(NULL, XMLNODE_TYPE_DATA); |
7131 | 108 |
10415 | 109 child->data = g_memdup(data, real_size); |
110 child->data_sz = real_size; | |
7131 | 111 |
10415 | 112 xmlnode_insert_child(node, child); |
7131 | 113 } |
114 | |
115 void | |
116 xmlnode_remove_attrib(xmlnode *node, const char *attr) | |
117 { | |
118 xmlnode *attr_node, *sibling = NULL; | |
119 | |
120 g_return_if_fail(node != NULL); | |
121 g_return_if_fail(attr != NULL); | |
122 | |
123 for(attr_node = node->child; attr_node; attr_node = attr_node->next) | |
124 { | |
8135 | 125 if(attr_node->type == XMLNODE_TYPE_ATTRIB && |
7131 | 126 !strcmp(attr_node->name, attr)) { |
127 if(node->child == attr_node) { | |
128 node->child = attr_node->next; | |
12233
02833a0ae716
[gaim-migrate @ 14535]
Richard Laager <rlaager@wiktel.com>
parents:
12198
diff
changeset
|
129 } else if (node->lastchild == attr_node) { |
02833a0ae716
[gaim-migrate @ 14535]
Richard Laager <rlaager@wiktel.com>
parents:
12198
diff
changeset
|
130 node->lastchild = sibling; |
7131 | 131 } else { |
132 sibling->next = attr_node->next; | |
133 } | |
134 xmlnode_free(attr_node); | |
135 return; | |
136 } | |
137 sibling = attr_node; | |
138 } | |
139 } | |
140 | |
141 void | |
142 xmlnode_set_attrib(xmlnode *node, const char *attr, const char *value) | |
143 { | |
144 xmlnode *attrib_node; | |
145 | |
146 g_return_if_fail(node != NULL); | |
147 g_return_if_fail(attr != NULL); | |
148 g_return_if_fail(value != NULL); | |
149 | |
150 xmlnode_remove_attrib(node, attr); | |
151 | |
8135 | 152 attrib_node = new_node(attr, XMLNODE_TYPE_ATTRIB); |
7131 | 153 |
154 attrib_node->data = g_strdup(value); | |
155 | |
156 xmlnode_insert_child(node, attrib_node); | |
157 } | |
158 | |
10425 | 159 const char * |
7131 | 160 xmlnode_get_attrib(xmlnode *node, const char *attr) |
161 { | |
162 xmlnode *x; | |
163 | |
164 g_return_val_if_fail(node != NULL, NULL); | |
165 | |
166 for(x = node->child; x; x = x->next) { | |
8135 | 167 if(x->type == XMLNODE_TYPE_ATTRIB && !strcmp(attr, x->name)) { |
7131 | 168 return x->data; |
169 } | |
170 } | |
171 | |
172 return NULL; | |
173 } | |
174 | |
10423 | 175 void |
176 xmlnode_free(xmlnode *node) | |
7131 | 177 { |
178 xmlnode *x, *y; | |
179 | |
180 g_return_if_fail(node != NULL); | |
181 | |
182 x = node->child; | |
183 while(x) { | |
184 y = x->next; | |
185 xmlnode_free(x); | |
186 x = y; | |
187 } | |
188 | |
189 if(node->name) | |
190 g_free(node->name); | |
191 if(node->data) | |
192 g_free(node->data); | |
193 g_free(node); | |
194 } | |
195 | |
196 xmlnode* | |
10736 | 197 xmlnode_get_child(const xmlnode *parent, const char *name) |
198 { | |
199 return xmlnode_get_child_with_namespace(parent, name, NULL); | |
200 } | |
201 | |
202 xmlnode * | |
203 xmlnode_get_child_with_namespace(const xmlnode *parent, const char *name, const char *ns) | |
7131 | 204 { |
205 xmlnode *x, *ret = NULL; | |
206 char **names; | |
207 char *parent_name, *child_name; | |
208 | |
209 g_return_val_if_fail(parent != NULL, NULL); | |
12233
02833a0ae716
[gaim-migrate @ 14535]
Richard Laager <rlaager@wiktel.com>
parents:
12198
diff
changeset
|
210 g_return_val_if_fail(name != NULL, NULL); |
7131 | 211 |
212 names = g_strsplit(name, "/", 2); | |
213 parent_name = names[0]; | |
214 child_name = names[1]; | |
215 | |
216 for(x = parent->child; x; x = x->next) { | |
8262 | 217 const char *xmlns = NULL; |
218 if(ns) | |
219 xmlns = xmlnode_get_attrib(x, "xmlns"); | |
220 | |
221 if(x->type == XMLNODE_TYPE_TAG && name && !strcmp(parent_name, x->name) | |
222 && (!ns || (xmlns && !strcmp(ns, xmlns)))) { | |
7131 | 223 ret = x; |
224 break; | |
225 } | |
226 } | |
227 | |
228 if(child_name && ret) | |
8262 | 229 ret = xmlnode_get_child(ret, child_name); |
7131 | 230 |
231 g_strfreev(names); | |
232 return ret; | |
233 } | |
234 | |
235 char * | |
236 xmlnode_get_data(xmlnode *node) | |
237 { | |
238 GString *str = NULL; | |
239 xmlnode *c; | |
240 | |
241 g_return_val_if_fail(node != NULL, NULL); | |
242 | |
243 for(c = node->child; c; c = c->next) { | |
8135 | 244 if(c->type == XMLNODE_TYPE_DATA) { |
7131 | 245 if(!str) |
246 str = g_string_new(""); | |
247 str = g_string_append_len(str, c->data, c->data_sz); | |
248 } | |
249 } | |
250 | |
10331 | 251 if (str == NULL) |
252 return NULL; | |
7131 | 253 |
10331 | 254 return g_string_free(str, FALSE); |
7131 | 255 } |
256 | |
10425 | 257 static char * |
10423 | 258 xmlnode_to_str_helper(xmlnode *node, int *len, gboolean formatting, int depth) |
7131 | 259 { |
260 GString *text = g_string_new(""); | |
261 xmlnode *c; | |
9837 | 262 char *node_name, *esc, *esc2, *tab = NULL; |
9838 | 263 gboolean need_end = FALSE, pretty = formatting; |
9837 | 264 |
12233
02833a0ae716
[gaim-migrate @ 14535]
Richard Laager <rlaager@wiktel.com>
parents:
12198
diff
changeset
|
265 g_return_val_if_fail(node != NULL, NULL); |
02833a0ae716
[gaim-migrate @ 14535]
Richard Laager <rlaager@wiktel.com>
parents:
12198
diff
changeset
|
266 |
9837 | 267 if(pretty && depth) { |
268 tab = g_strnfill(depth, '\t'); | |
269 text = g_string_append(text, tab); | |
270 } | |
7131 | 271 |
272 node_name = g_markup_escape_text(node->name, -1); | |
273 g_string_append_printf(text, "<%s", node_name); | |
274 | |
275 for(c = node->child; c; c = c->next) | |
276 { | |
8135 | 277 if(c->type == XMLNODE_TYPE_ATTRIB) { |
7131 | 278 esc = g_markup_escape_text(c->name, -1); |
279 esc2 = g_markup_escape_text(c->data, -1); | |
280 g_string_append_printf(text, " %s='%s'", esc, esc2); | |
281 g_free(esc); | |
282 g_free(esc2); | |
8135 | 283 } else if(c->type == XMLNODE_TYPE_TAG || c->type == XMLNODE_TYPE_DATA) { |
9837 | 284 if(c->type == XMLNODE_TYPE_DATA) |
9838 | 285 pretty = FALSE; |
7131 | 286 need_end = TRUE; |
287 } | |
288 } | |
289 | |
290 if(need_end) { | |
12041
da44f68fb4d2
[gaim-migrate @ 14334]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11705
diff
changeset
|
291 g_string_append_printf(text, ">%s", pretty ? NEWLINE_S : ""); |
7131 | 292 |
293 for(c = node->child; c; c = c->next) | |
294 { | |
8135 | 295 if(c->type == XMLNODE_TYPE_TAG) { |
7642 | 296 int esc_len; |
9838 | 297 esc = xmlnode_to_str_helper(c, &esc_len, pretty, depth+1); |
7642 | 298 text = g_string_append_len(text, esc, esc_len); |
7131 | 299 g_free(esc); |
12198 | 300 } else if(c->type == XMLNODE_TYPE_DATA && c->data_sz > 0) { |
7131 | 301 esc = g_markup_escape_text(c->data, c->data_sz); |
7642 | 302 text = g_string_append(text, esc); |
7131 | 303 g_free(esc); |
304 } | |
305 } | |
306 | |
9838 | 307 if(tab && pretty) |
9837 | 308 text = g_string_append(text, tab); |
12041
da44f68fb4d2
[gaim-migrate @ 14334]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11705
diff
changeset
|
309 g_string_append_printf(text, "</%s>%s", node_name, formatting ? NEWLINE_S : ""); |
7131 | 310 } else { |
12041
da44f68fb4d2
[gaim-migrate @ 14334]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11705
diff
changeset
|
311 g_string_append_printf(text, "/>%s", formatting ? NEWLINE_S : ""); |
7131 | 312 } |
313 | |
314 g_free(node_name); | |
315 | |
9837 | 316 if(tab) |
317 g_free(tab); | |
318 | |
7642 | 319 if(len) |
320 *len = text->len; | |
10331 | 321 |
322 return g_string_free(text, FALSE); | |
7131 | 323 } |
324 | |
10425 | 325 char * |
10423 | 326 xmlnode_to_str(xmlnode *node, int *len) |
327 { | |
9837 | 328 return xmlnode_to_str_helper(node, len, FALSE, 0); |
329 } | |
330 | |
10425 | 331 char * |
10423 | 332 xmlnode_to_formatted_str(xmlnode *node, int *len) |
333 { | |
10425 | 334 char *xml, *xml_with_declaration; |
10415 | 335 |
12233
02833a0ae716
[gaim-migrate @ 14535]
Richard Laager <rlaager@wiktel.com>
parents:
12198
diff
changeset
|
336 g_return_val_if_fail(node != NULL, NULL); |
02833a0ae716
[gaim-migrate @ 14535]
Richard Laager <rlaager@wiktel.com>
parents:
12198
diff
changeset
|
337 |
10415 | 338 xml = xmlnode_to_str_helper(node, len, TRUE, 0); |
339 xml_with_declaration = | |
12041
da44f68fb4d2
[gaim-migrate @ 14334]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11705
diff
changeset
|
340 g_strdup_printf("<?xml version='1.0' encoding='UTF-8' ?>" NEWLINE_S NEWLINE_S "%s", xml); |
10415 | 341 g_free(xml); |
342 | |
343 return xml_with_declaration; | |
9837 | 344 } |
345 | |
7131 | 346 struct _xmlnode_parser_data { |
347 xmlnode *current; | |
348 }; | |
349 | |
350 static void | |
351 xmlnode_parser_element_start(GMarkupParseContext *context, | |
352 const char *element_name, const char **attrib_names, | |
353 const char **attrib_values, gpointer user_data, GError **error) | |
354 { | |
355 struct _xmlnode_parser_data *xpd = user_data; | |
356 xmlnode *node; | |
357 int i; | |
358 | |
359 if(!element_name) { | |
360 return; | |
361 } else { | |
362 if(xpd->current) | |
363 node = xmlnode_new_child(xpd->current, element_name); | |
364 else | |
365 node = xmlnode_new(element_name); | |
366 | |
367 for(i=0; attrib_names[i]; i++) | |
368 xmlnode_set_attrib(node, attrib_names[i], attrib_values[i]); | |
369 | |
370 xpd->current = node; | |
371 } | |
372 } | |
373 | |
374 static void | |
375 xmlnode_parser_element_end(GMarkupParseContext *context, | |
376 const char *element_name, gpointer user_data, GError **error) | |
377 { | |
378 struct _xmlnode_parser_data *xpd = user_data; | |
379 | |
380 if(!element_name || !xpd->current) | |
381 return; | |
382 | |
383 if(xpd->current->parent) { | |
384 if(!strcmp(xpd->current->name, element_name)) | |
385 xpd->current = xpd->current->parent; | |
386 } | |
387 } | |
388 | |
389 static void | |
390 xmlnode_parser_element_text(GMarkupParseContext *context, const char *text, | |
391 gsize text_len, gpointer user_data, GError **error) | |
392 { | |
393 struct _xmlnode_parser_data *xpd = user_data; | |
394 | |
395 if(!xpd->current) | |
396 return; | |
397 | |
398 if(!text || !text_len) | |
399 return; | |
400 | |
401 xmlnode_insert_data(xpd->current, text, text_len); | |
402 } | |
403 | |
404 static GMarkupParser xmlnode_parser = { | |
405 xmlnode_parser_element_start, | |
406 xmlnode_parser_element_end, | |
407 xmlnode_parser_element_text, | |
408 NULL, | |
409 NULL | |
410 }; | |
411 | |
412 | |
10423 | 413 xmlnode * |
10848 | 414 xmlnode_from_str(const char *str, gssize size) |
7131 | 415 { |
11390 | 416 struct _xmlnode_parser_data *xpd; |
7131 | 417 xmlnode *ret; |
418 GMarkupParseContext *context; | |
11390 | 419 gsize real_size; |
7131 | 420 |
11390 | 421 g_return_val_if_fail(str != NULL, NULL); |
422 | |
11705
0906a3e9626c
[gaim-migrate @ 13996]
Richard Laager <rlaager@wiktel.com>
parents:
11390
diff
changeset
|
423 real_size = size < 0 ? strlen(str) : size; |
11390 | 424 xpd = g_new0(struct _xmlnode_parser_data, 1); |
7131 | 425 context = g_markup_parse_context_new(&xmlnode_parser, 0, xpd, NULL); |
426 | |
427 if(!g_markup_parse_context_parse(context, str, real_size, NULL)) { | |
428 while(xpd->current && xpd->current->parent) | |
429 xpd->current = xpd->current->parent; | |
430 if(xpd->current) | |
431 xmlnode_free(xpd->current); | |
432 xpd->current = NULL; | |
433 } | |
434 g_markup_parse_context_free(context); | |
435 | |
436 ret = xpd->current; | |
437 g_free(xpd); | |
438 return ret; | |
439 } | |
8135 | 440 |
10423 | 441 xmlnode * |
442 xmlnode_copy(xmlnode *src) | |
8135 | 443 { |
444 xmlnode *ret; | |
445 xmlnode *child; | |
446 xmlnode *sibling = NULL; | |
447 | |
12233
02833a0ae716
[gaim-migrate @ 14535]
Richard Laager <rlaager@wiktel.com>
parents:
12198
diff
changeset
|
448 g_return_val_if_fail(src != NULL, NULL); |
8135 | 449 |
450 ret = new_node(src->name, src->type); | |
451 if(src->data) { | |
8167 | 452 if(src->data_sz) { |
453 ret->data = g_memdup(src->data, src->data_sz); | |
454 ret->data_sz = src->data_sz; | |
455 } else { | |
456 ret->data = g_strdup(src->data); | |
457 } | |
8135 | 458 } |
459 | |
460 for(child = src->child; child; child = child->next) { | |
461 if(sibling) { | |
462 sibling->next = xmlnode_copy(child); | |
463 sibling = sibling->next; | |
464 } else { | |
465 ret->child = xmlnode_copy(child); | |
466 sibling = ret->child; | |
467 } | |
468 sibling->parent = ret; | |
469 } | |
470 | |
12233
02833a0ae716
[gaim-migrate @ 14535]
Richard Laager <rlaager@wiktel.com>
parents:
12198
diff
changeset
|
471 ret->lastchild = sibling; |
02833a0ae716
[gaim-migrate @ 14535]
Richard Laager <rlaager@wiktel.com>
parents:
12198
diff
changeset
|
472 |
8135 | 473 return ret; |
474 } | |
475 | |
10423 | 476 xmlnode * |
477 xmlnode_get_next_twin(xmlnode *node) | |
478 { | |
8135 | 479 xmlnode *sibling; |
8262 | 480 const char *ns = xmlnode_get_attrib(node, "xmlns"); |
8135 | 481 |
482 g_return_val_if_fail(node != NULL, NULL); | |
483 g_return_val_if_fail(node->type == XMLNODE_TYPE_TAG, NULL); | |
484 | |
485 for(sibling = node->next; sibling; sibling = sibling->next) { | |
8283 | 486 const char *xmlns = NULL; |
8262 | 487 if(ns) |
488 xmlns = xmlnode_get_attrib(sibling, "xmlns"); | |
489 | |
490 if(sibling->type == XMLNODE_TYPE_TAG && !strcmp(node->name, sibling->name) && | |
491 (!ns || (xmlns && !strcmp(ns, xmlns)))) | |
8135 | 492 return sibling; |
493 } | |
494 | |
495 return NULL; | |
496 } |