diff options
| author | 2018-12-14 04:23:05 +0100 | |
|---|---|---|
| committer | 2018-12-14 04:23:05 +0100 | |
| commit | ac3be565c413f6964f0188e1c608c1bf8848db29 (patch) | |
| tree | 12c1a4809a11dd51ee0d758a2c9763366d28aa6a /src | |
| parent | 3007c970a6d61efe453b51b76cb7a79c94fc6a06 (diff) | |
| download | libplist-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.h | 2 | ||||
| -rw-r--r-- | src/xplist.c | 129 |
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 | ||
| 27 | typedef struct bytearray_t strbuf_t; | 27 | typedef 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 | |||
| 72 | static const char XML_PLIST_PROLOG[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\ | 75 | static 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 */ | ||
| 409 | static 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 */ | ||
| 430 | static 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 | |||
| 444 | static 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 | |||
| 403 | PLIST_API void plist_to_xml(plist_t plist, char **plist_xml, uint32_t * length) | 524 | PLIST_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 | ||
