summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2018-12-14 04:23:05 +0100
committerGravatar Nikias Bassen2018-12-14 04:23:05 +0100
commitac3be565c413f6964f0188e1c608c1bf8848db29 (patch)
tree12c1a4809a11dd51ee0d758a2c9763366d28aa6a /src
parent3007c970a6d61efe453b51b76cb7a79c94fc6a06 (diff)
downloadlibplist-ac3be565c413f6964f0188e1c608c1bf8848db29.tar.gz
libplist-ac3be565c413f6964f0188e1c608c1bf8848db29.tar.bz2
xplist: Improve memory usage by estimating output buffer size
Diffstat (limited to 'src')
-rw-r--r--src/strbuf.h2
-rw-r--r--src/xplist.c129
2 files changed, 128 insertions, 3 deletions
diff --git a/src/strbuf.h b/src/strbuf.h
index b805892..0d28edf 100644
--- a/src/strbuf.h
+++ b/src/strbuf.h
@@ -26,7 +26,7 @@
26 26
27typedef struct bytearray_t strbuf_t; 27typedef struct bytearray_t strbuf_t;
28 28
29#define str_buf_new() byte_array_new(32768) 29#define str_buf_new(__sz) byte_array_new(__sz)
30#define str_buf_free(__ba) byte_array_free(__ba) 30#define str_buf_free(__ba) byte_array_free(__ba)
31#define str_buf_grow(__ba, __am) byte_array_grow(__ba, __am) 31#define str_buf_grow(__ba, __am) byte_array_grow(__ba, __am)
32#define str_buf_append(__ba, __str, __len) byte_array_append(__ba, (void*)(__str), __len) 32#define str_buf_append(__ba, __str, __len) byte_array_append(__ba, (void*)(__str), __len)
diff --git a/src/xplist.c b/src/xplist.c
index bb27f53..2eeccf9 100644
--- a/src/xplist.c
+++ b/src/xplist.c
@@ -37,6 +37,7 @@
37 37
38#include <inttypes.h> 38#include <inttypes.h>
39#include <math.h> 39#include <math.h>
40#include <limits.h>
40 41
41#include <node.h> 42#include <node.h>
42#include <node_list.h> 43#include <node_list.h>
@@ -69,6 +70,8 @@
69 70
70#define MAC_EPOCH 978307200 71#define MAC_EPOCH 978307200
71 72
73#define MAX_DATA_BYTES_PER_LINE(__i) (((76 - (__i << 3)) >> 2) * 3)
74
72static const char XML_PLIST_PROLOG[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\ 75static const char XML_PLIST_PROLOG[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
73<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n\ 76<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n\
74<plist version=\"1.0\">\n"; 77<plist version=\"1.0\">\n";
@@ -290,7 +293,7 @@ static void node_to_xml(node_t* node, bytearray_t **outbuf, uint32_t depth)
290 char *buf = malloc(80); 293 char *buf = malloc(80);
291 uint32_t j = 0; 294 uint32_t j = 0;
292 uint32_t indent = (depth > 8) ? 8 : depth; 295 uint32_t indent = (depth > 8) ? 8 : depth;
293 uint32_t maxread = ((76 - indent*8) / 4) * 3; 296 uint32_t maxread = MAX_DATA_BYTES_PER_LINE(indent);
294 size_t count = 0; 297 size_t count = 0;
295 size_t b64count = 0; 298 size_t b64count = 0;
296 size_t amount = (node_data->length / 3 * 4) + 4 + (((node_data->length / maxread) + 1) * (indent+1)); 299 size_t amount = (node_data->length / 3 * 4) + 4 + (((node_data->length / maxread) + 1) * (indent+1));
@@ -400,9 +403,131 @@ static void parse_date(const char *strval, struct TM *btime)
400 btime->tm_isdst=0; 403 btime->tm_isdst=0;
401} 404}
402 405
406#define PO10i_LIMIT (INT64_MAX/10)
407
408/* based on https://stackoverflow.com/a/4143288 */
409static int num_digits_i(int64_t i)
410{
411 int n;
412 int64_t po10;
413 n=1;
414 if (i < 0) {
415 i = -i;
416 n++;
417 }
418 po10=10;
419 while (i>=po10) {
420 n++;
421 if (po10 > PO10i_LIMIT) break;
422 po10*=10;
423 }
424 return n;
425}
426
427#define PO10u_LIMIT (UINT64_MAX/10)
428
429/* based on https://stackoverflow.com/a/4143288 */
430static int num_digits_u(uint64_t i)
431{
432 int n;
433 uint64_t po10;
434 n=1;
435 po10=10;
436 while (i>=po10) {
437 n++;
438 if (po10 > PO10u_LIMIT) break;
439 po10*=10;
440 }
441 return n;
442}
443
444static void node_estimate_size(node_t *node, uint64_t *size, uint32_t depth)
445{
446 plist_data_t data;
447 if (!node) {
448 return;
449 }
450 data = plist_get_data(node);
451 if (node->children) {
452 node_t *ch;
453 for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
454 node_estimate_size(ch, size, depth + 1);
455 }
456 switch (data->type) {
457 case PLIST_DICT:
458 *size += (XPLIST_DICT_LEN << 1) + 7;
459 break;
460 case PLIST_ARRAY:
461 *size += (XPLIST_ARRAY_LEN << 1) + 7;
462 break;
463 default:
464 break;
465 }
466 *size += (depth << 1);
467 } else {
468 uint32_t indent = (depth > 8) ? 8 : depth;
469 switch (data->type) {
470 case PLIST_DATA: {
471 uint32_t req_lines = (data->length / MAX_DATA_BYTES_PER_LINE(indent)) + 1;
472 uint32_t b64len = data->length + (data->length / 3);
473 b64len += b64len % 4;
474 *size += b64len;
475 *size += (XPLIST_DATA_LEN << 1) + 5 + (indent+1) * (req_lines+1) + 1;
476 } break;
477 case PLIST_STRING:
478 *size += data->length;
479 *size += (XPLIST_STRING_LEN << 1) + 6;
480 break;
481 case PLIST_KEY:
482 *size += data->length;
483 *size += (XPLIST_KEY_LEN << 1) + 6;
484 break;
485 case PLIST_UINT:
486 if (data->length == 16) {
487 *size += num_digits_u(data->intval);
488 } else {
489 *size += num_digits_i((int64_t)data->intval);
490 }
491 *size += (XPLIST_INT_LEN << 1) + 6;
492 break;
493 case PLIST_REAL:
494 *size += num_digits_i((int64_t)data->realval) + 7;
495 *size += (XPLIST_REAL_LEN << 1) + 6;
496 break;
497 case PLIST_DATE:
498 *size += 20; /* YYYY-MM-DDThh:mm:ssZ */
499 *size += (XPLIST_DATE_LEN << 1) + 6;
500 break;
501 case PLIST_BOOLEAN:
502 *size += ((data->boolval) ? XPLIST_TRUE_LEN : XPLIST_FALSE_LEN) + 4;
503 break;
504 case PLIST_DICT:
505 *size += XPLIST_DICT_LEN + 4; /* <dict/> */
506 break;
507 case PLIST_ARRAY:
508 *size += XPLIST_ARRAY_LEN + 4; /* <array/> */
509 break;
510 case PLIST_UID:
511 *size += num_digits_i((int64_t)data->intval);
512 *size += (XPLIST_DICT_LEN << 1) + 7;
513 *size += indent + ((indent+1) << 1);
514 *size += 18; /* <key>CF$UID</key> */
515 *size += (XPLIST_INT_LEN << 1) + 6;
516 break;
517 default:
518 break;
519 }
520 *size += indent;
521 }
522}
523
403PLIST_API void plist_to_xml(plist_t plist, char **plist_xml, uint32_t * length) 524PLIST_API void plist_to_xml(plist_t plist, char **plist_xml, uint32_t * length)
404{ 525{
405 strbuf_t *outbuf = str_buf_new(); 526 uint64_t size = 0;
527 node_estimate_size(plist, &size, 0);
528 size += sizeof(XML_PLIST_PROLOG) + sizeof(XML_PLIST_EPILOG) - 1;
529
530 strbuf_t *outbuf = str_buf_new(size);
406 531
407 str_buf_append(outbuf, XML_PLIST_PROLOG, sizeof(XML_PLIST_PROLOG)-1); 532 str_buf_append(outbuf, XML_PLIST_PROLOG, sizeof(XML_PLIST_PROLOG)-1);
408 533