1/* 2 * Copyright (c) 1999-2007 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23/* 24 * HISTORY 25 * 26 * 25 Nov 98 from IOSerialize.cpp created by rsulack 27 * 31 Aug 99 sdouglas CF version. 28 * 09 Dec 99 rsulack converted to serialize to "XML" syntax 29 */ 30 31 32#include <CoreFoundation/CoreFoundation.h> 33#include <assert.h> 34#include <syslog.h> 35 36typedef struct { 37 CFMutableDataRef data; 38 39 int idrefNumRefs; 40 41/* For each CFType, we track whether a given plist value hasRefs, 42 * and what the ids used for those refs are. 'hasRefs' is set to 43 * kCFBooleanFalse the first time we see a given value, so we know 44 * we saw it, and then kCFBooleanTrue if we see it again, so we know 45 * we need an id for it. On writing out the XML, we generate ids as 46 * we encounter the need. 47 */ 48 CFMutableDictionaryRef stringIDRefDictionary; 49 CFMutableDictionaryRef numberIDRefDictionary; 50 CFMutableDictionaryRef dataIDRefDictionary; 51 CFMutableDictionaryRef dictionaryIDRefDictionary; 52 CFMutableDictionaryRef arrayIDRefDictionary; 53 CFMutableDictionaryRef setIDRefDictionary; 54 55} IOCFSerializeState; 56 57 58static Boolean 59DoCFSerialize(CFTypeRef object, IOCFSerializeState * state); 60 61static Boolean 62addChar(char chr, IOCFSerializeState * state) 63{ 64 CFDataAppendBytes(state->data, (UInt8 *) &chr, 1); 65 66 return true; 67} 68 69static Boolean 70addString(const char * str, IOCFSerializeState * state) 71{ 72 CFIndex len = strlen(str); 73 CFDataAppendBytes(state->data, (UInt8 *) str, len); 74 75 return true; 76} 77 78static const char * 79getTagString(CFTypeRef object) 80{ 81 CFTypeID type; 82 83 assert(object); 84 85 type = CFGetTypeID(object); 86 87 if (type == CFStringGetTypeID()) return "string"; 88 if (type == CFNumberGetTypeID()) return "integer"; 89 if (type == CFDataGetTypeID()) return "data"; 90 if (type == CFDictionaryGetTypeID()) return "dict"; 91 if (type == CFArrayGetTypeID()) return "array"; 92 if (type == CFSetGetTypeID()) return "set"; 93 94 return "internal error"; 95} 96 97static CFMutableDictionaryRef 98idRefDictionaryForObject( 99 CFTypeRef object, 100 IOCFSerializeState * state) 101{ 102 CFMutableDictionaryRef result = NULL; 103 CFTypeID objectType = CFNullGetTypeID(); // do not release 104 105 objectType = CFGetTypeID(object); 106 107 /* Sorted by rough order of % occurence in big plists. 108 */ 109 if (objectType == CFDictionaryGetTypeID()) { 110 result = state->dictionaryIDRefDictionary; 111 } else if (objectType == CFStringGetTypeID()) { 112 result = state->stringIDRefDictionary; 113 } else if (objectType == CFArrayGetTypeID()) { 114 result = state->arrayIDRefDictionary; 115 } else if (objectType == CFNumberGetTypeID()) { 116 result = state->numberIDRefDictionary; 117 } else if (objectType == CFDataGetTypeID()) { 118 result = state->dataIDRefDictionary; 119 } else if (objectType == CFSetGetTypeID()) { 120 result = state->setIDRefDictionary; 121 } 122 123 return result; 124} 125 126void 127recordObjectInIDRefDictionary( 128 CFTypeRef object, 129 IOCFSerializeState * state) 130{ 131 CFTypeRef refEntry = NULL; // do not release 132 CFMutableDictionaryRef idRefDictionary = NULL; // do not release 133 134 if (!object || !state) { 135 goto finish; 136 } 137 138 idRefDictionary = idRefDictionaryForObject(object, state); 139 if (!idRefDictionary) { 140 goto finish; 141 } 142 143 refEntry = CFDictionaryGetValue(idRefDictionary, object); 144 145 /* If we have never seen this object value, then add an entry 146 * in the dictionary with value false, indicating we have seen 147 * it once. 148 * 149 * If we have seen this object value, then set its entry to true 150 * to indicate that we have now seen a second occurrence 151 * of the object value, which means we will generate an ID and IDREFs 152 * in the XML. 153 */ 154 if (!refEntry) { 155 /* Use AddValue here so as not to overwrite. 156 */ 157 CFDictionaryAddValue(idRefDictionary, object, kCFBooleanFalse); 158 } else { 159 CFDictionarySetValue(idRefDictionary, object, kCFBooleanTrue); 160 } 161 162finish: 163 return; 164} 165 166Boolean 167previouslySerialized( 168 CFTypeRef object, 169 IOCFSerializeState * state) 170{ 171 Boolean result = FALSE; 172 CFMutableDictionaryRef idRefDict = NULL; // do not release 173 CFTypeRef idRefEntry = NULL; // do not release 174 CFTypeID idRefType = CFNullGetTypeID(); // do not release 175 char temp[64]; 176 int idInt = -1; 177 178 if (!object || !state) { 179 goto finish; 180 } 181 182 /* If we don't get a lookup dict or an entry for the object, 183 * then no ID or IDREF will be involved, 184 * so treat is if never before serialized. 185 */ 186 idRefDict = idRefDictionaryForObject(object, state); 187 if (!idRefDict) { 188 goto finish; 189 } 190 191 idRefEntry = (CFTypeRef)CFDictionaryGetValue(idRefDict, object); 192 if (!idRefEntry) { 193 goto finish; 194 } 195 196 idRefType = CFGetTypeID(idRefEntry); 197 198 /* If the entry is of Boolean type, then an ID/IDREF may be involved, 199 * but we haven't created one yet, so not yet serialized. 200 */ 201 if (idRefType == CFBooleanGetTypeID()) { 202 goto finish; 203 } 204 205 /* At this point the entry should be a CFNumber. 206 */ 207 if (idRefType != CFNumberGetTypeID()) { 208 goto finish; 209 } 210 211 /* Finally, get the IDREF value out of the idRef entry and write the 212 * XML for it. Bail on extraction error. 213 */ 214 if (!CFNumberGetValue((CFNumberRef)idRefEntry, kCFNumberIntType, &idInt)) { 215 goto finish; 216 } 217 snprintf(temp, sizeof(temp), "<%s IDREF=\"%d\"/>", getTagString(object), idInt); 218 result = addString(temp, state); 219 220finish: 221 return result; 222} 223 224static Boolean 225addStartTag( 226 CFTypeRef object, 227 const char * additionalTags, 228 IOCFSerializeState * state) 229{ 230 CFMutableDictionaryRef idRefDict = NULL; // do not release 231 CFTypeRef idRefEntry = NULL; // do not release 232 CFNumberRef idRef = NULL; // must release 233 char temp[128]; 234 235 idRefDict = idRefDictionaryForObject(object, state); 236 if (idRefDict) { 237 idRefEntry = (CFTypeRef)CFDictionaryGetValue(idRefDict, object); 238 } 239 240 /* If the IDRef entry is 'true', then we know we have an object value 241 * with multiple references and need to emit an ID. So we create one 242 * by incrementing the state's counter, making a CFNumber, and *replacing* 243 * the boolean value in the IDRef dictionary with that number. 244 */ 245 if (idRefEntry == kCFBooleanTrue) { 246 int idInt = state->idrefNumRefs++; 247 248 idRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &idInt); 249 assert(idRef); 250 251 CFDictionarySetValue(idRefDict, object, idRef); 252 CFRelease(idRef); 253 254 if (additionalTags) { 255 snprintf(temp, sizeof(temp) * sizeof(char), 256 "<%s ID=\"%d\" %s>", getTagString(object), idInt, additionalTags); 257 } else { 258 snprintf(temp, sizeof(temp) * sizeof(char), 259 "<%s ID=\"%d\">", getTagString(object), idInt); 260 } 261 } else { 262 if (additionalTags) { 263 snprintf(temp, sizeof(temp) * sizeof(char), 264 "<%s %s>", getTagString(object), additionalTags); 265 } else { 266 snprintf(temp, sizeof(temp) * sizeof(char), 267 "<%s>", getTagString(object)); 268 } 269 } 270 271 return addString(temp, state); 272} 273 274 275static Boolean 276addEndTag(CFTypeRef object, 277 IOCFSerializeState * state) 278{ 279 char temp[128]; 280 281 snprintf(temp, sizeof(temp) * sizeof(char), "</%s>", getTagString(object)); 282 283 return addString(temp, state); 284} 285 286static Boolean 287DoCFSerializeNumber(CFNumberRef object, IOCFSerializeState * state) 288{ 289 char temp[32]; 290 long long value; 291 int size; 292 293 if (previouslySerialized(object, state)) return true; 294 295 if (!CFNumberGetValue(object, kCFNumberLongLongType, &value)) { 296 return(false); 297 } 298 299 switch(CFNumberGetType(object)) { 300 case kCFNumberSInt8Type: 301 case kCFNumberCharType: 302 size = 8; 303 break; 304 305 case kCFNumberSInt16Type: 306 case kCFNumberShortType: 307 size = 16; 308 break; 309 310 case kCFNumberSInt32Type: 311 case kCFNumberIntType: 312 size = 32; 313 break; 314 315 case kCFNumberLongType: 316#if __LP64__ 317 size = 64; 318#else 319 size = 32; 320#endif 321 break; 322 323 case kCFNumberSInt64Type: 324 case kCFNumberLongLongType: 325 default: 326 size = 64; 327 break; 328 } 329 330 snprintf(temp, sizeof(temp), "size=\"%d\"", size); 331 if (!addStartTag(object, temp, state)) return false; 332 333 if (size <= 32) 334 snprintf(temp, sizeof(temp), "0x%lx", (unsigned long int) value); 335 else 336 snprintf(temp, sizeof(temp), "0x%qx", value); 337 338 if (!addString(temp, state)) return false; 339 340 return addEndTag(object, state); 341} 342 343static Boolean 344DoCFSerializeBoolean(CFBooleanRef object, IOCFSerializeState * state) 345{ 346 return addString(CFBooleanGetValue(object) ? "<true/>" : "<false/>", state); 347} 348 349static Boolean 350DoCFSerializeString(CFStringRef object, IOCFSerializeState * state) 351{ 352 CFDataRef dataBuffer = 0; 353 const char * buffer; 354 CFIndex length; 355 bool conversionFailed = false; 356 char c; 357 int i; 358 359 if (previouslySerialized(object, state)) return true; 360 361 if (!addStartTag(object, 0, state)) return false; 362 363 dataBuffer = CFStringCreateExternalRepresentation(kCFAllocatorDefault, object, kCFStringEncodingUTF8, 0); 364 if (!dataBuffer) { 365 dataBuffer = CFStringCreateExternalRepresentation(kCFAllocatorDefault, object, kCFStringEncodingUTF8, (UInt8)'?'); 366 conversionFailed = true; 367 } 368 369 if (dataBuffer) { 370 length = CFDataGetLength(dataBuffer); 371 buffer = (char *) CFDataGetBytePtr(dataBuffer); 372 } else { 373 length = 0; 374 buffer = ""; 375 conversionFailed = true; 376 } 377 378 if (conversionFailed) { 379 char * tempBuffer; 380 if (buffer && (tempBuffer = malloc(length + 1))) { 381 bcopy(buffer, tempBuffer, length); 382 tempBuffer[length] = 0; 383 384 syslog(LOG_ERR, "FIXME: IOCFSerialize has detected a string that can not be converted to UTF-8, \"%s\"", tempBuffer); 385 386 free(tempBuffer); 387 } 388 } 389 390 // this works because all bytes in a multi-byte utf-8 character have the high order bit set 391 for (i = 0; i < length; i++) { 392 c = buffer[i]; 393 if (c == '<') { 394 if (!addString("<", state)) return false; 395 } else if (c == '>') { 396 if (!addString(">", state)) return false; 397 } else if (c == '&') { 398 if (!addString("&", state)) return false; 399 } else { 400 if (!addChar(c, state)) return false; 401 } 402 } 403 404 if (dataBuffer) CFRelease(dataBuffer); 405 406 return addEndTag(object, state); 407} 408 409static Boolean 410DoCFSerializeKey(CFStringRef object, IOCFSerializeState * state) 411{ 412 CFDataRef dataBuffer = 0; 413 const char * buffer; 414 CFIndex length; 415 bool conversionFailed = false; 416 char c; 417 int i; 418 419 if (!addString("<key>", state)) return false; 420 421 dataBuffer = CFStringCreateExternalRepresentation(kCFAllocatorDefault, object, kCFStringEncodingUTF8, 0); 422 if (!dataBuffer) { 423 dataBuffer = CFStringCreateExternalRepresentation(kCFAllocatorDefault, object, kCFStringEncodingUTF8, (UInt8)'?'); 424 conversionFailed = true; 425 } 426 427 if (dataBuffer) { 428 length = CFDataGetLength(dataBuffer); 429 buffer = (char *) CFDataGetBytePtr(dataBuffer); 430 } else { 431 length = 0; 432 buffer = ""; 433 conversionFailed = true; 434 } 435 436 if (conversionFailed) { 437 char * tempBuffer; 438 if (buffer && (tempBuffer = malloc(length + 1))) { 439 bcopy(buffer, tempBuffer, length); 440 tempBuffer[length] = 0; 441 442 syslog(LOG_ERR, "FIXME: IOCFSerialize has detected a string that can not be converted to UTF-8, \"%s\"", tempBuffer); 443 444 free(tempBuffer); 445 } 446 } 447 448 // this works because all bytes in a multi-byte utf-8 character have the high order bit set 449 for (i = 0; i < length; i++) { 450 c = buffer[i]; 451 if (c == '<') { 452 if (!addString("<", state)) return false; 453 } else if (c == '>') { 454 if (!addString(">", state)) return false; 455 } else if (c == '&') { 456 if (!addString("&", state)) return false; 457 } else { 458 if (!addChar(c, state)) return false; 459 } 460 } 461 462 if (dataBuffer) CFRelease(dataBuffer); 463 464 return addString("</key>", state); 465} 466 467//this was taken from CFPropertyList.c 468static const char __CFPLDataEncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 469 470static Boolean 471DoCFSerializeData(CFDataRef object, IOCFSerializeState * state) 472{ 473 CFIndex length; 474 const UInt8 * bytes; 475 CFIndex i; 476 const unsigned char *p; 477 unsigned char c = 0; 478 479 if (previouslySerialized(object, state)) return true; 480 481 length = CFDataGetLength(object); 482 bytes = CFDataGetBytePtr(object); 483 484 if (!addStartTag(object, 0, state)) return false; 485 486 for (i = 0, p = (unsigned char *)bytes; i < length; i++, p++) { 487 /* 3 bytes are encoded as 4 */ 488 switch (i % 3) { 489 case 0: 490 c = __CFPLDataEncodeTable [ ((p[0] >> 2) & 0x3f)]; 491 if (!addChar(c, state)) return false; 492 break; 493 case 1: 494 c = __CFPLDataEncodeTable [ ((((p[-1] << 8) | p[0]) >> 4) & 0x3f)]; 495 if (!addChar(c, state)) return false; 496 break; 497 case 2: 498 c = __CFPLDataEncodeTable [ ((((p[-1] << 8) | p[0]) >> 6) & 0x3f)]; 499 if (!addChar(c, state)) return false; 500 c = __CFPLDataEncodeTable [ (p[0] & 0x3f)]; 501 if (!addChar(c, state)) return false; 502 break; 503 } 504 } 505 switch (i % 3) { 506 case 0: 507 break; 508 case 1: 509 c = __CFPLDataEncodeTable [ ((p[-1] << 4) & 0x30)]; 510 if (!addChar(c, state)) return false; 511 if (!addChar('=', state)) return false; 512 if (!addChar('=', state)) return false; 513 break; 514 case 2: 515 c = __CFPLDataEncodeTable [ ((p[-1] << 2) & 0x3c)]; 516 if (!addChar(c, state)) return false; 517 if (!addChar('=', state)) return false; 518 break; 519 } 520 521 return addEndTag(object, state); 522} 523 524static Boolean 525DoCFSerializeArray(CFArrayRef object, IOCFSerializeState * state) 526{ 527 CFIndex count, i; 528 Boolean ok = true; 529 530 if (previouslySerialized(object, state)) return true; 531 532 if (!addStartTag(object, 0, state)) return false; 533 534 count = CFArrayGetCount(object); 535 if (count) { 536 for (i = 0; ok && (i < count); i++) { 537 ok = DoCFSerialize((CFTypeRef) CFArrayGetValueAtIndex(object, i), 538 state); 539 } 540 } 541 542 return ok && addEndTag(object, state); 543} 544 545static Boolean 546DoCFSerializeSet(CFSetRef object, IOCFSerializeState * state) 547{ 548 CFIndex count, i; 549 const void ** values; 550 Boolean ok = true; 551 552 if (previouslySerialized(object, state)) return true; 553 554 if (!addStartTag(object, 0, state)) return false; 555 556 count = CFSetGetCount(object); 557 558 if (count) { 559 values = (const void **) malloc(count * sizeof(void *)); 560 if (!values) 561 return false; 562 CFSetGetValues(object, values); 563 } else { 564 values = 0; 565 } 566 567 for (i = 0; ok && (i < count); i++) { 568 ok = DoCFSerialize((CFTypeRef) values[i], state); 569 } 570 571 if (values) 572 free(values); 573 574 return ok && addEndTag(object, state); 575} 576 577static Boolean 578DoCFSerializeDictionary(CFDictionaryRef object, 579 IOCFSerializeState * state) 580{ 581 CFIndex count, i; 582 const void ** values; 583 const void ** keys; 584 Boolean ok = true; 585 586 if (previouslySerialized(object, state)) return true; 587 588 if (!addStartTag(object, 0, state)) return false; 589 590 count = CFDictionaryGetCount(object); 591 592 if (count) { 593 values = (const void **) malloc(2 * count * sizeof(void *)); 594 if (!values) 595 return false; 596 keys = values + count; 597 CFDictionaryGetKeysAndValues(object, keys, values); 598 } else { 599 values = keys = 0; 600 } 601 602 for (i = 0; ok && (i < count); i++) { 603 ok = DoCFSerializeKey((CFTypeRef) keys[i], state); 604 if (!ok) 605 break; 606 if (!(ok = DoCFSerialize((CFTypeRef) values[i], state))) 607 break; 608 } 609 610 if (values) 611 free(values); 612 613 return ok && addEndTag(object, state); 614} 615 616static Boolean 617DoIdrefScan(CFTypeRef object, IOCFSerializeState * state) 618{ 619 CFTypeID type; 620 Boolean ok = true; // such an optimist 621 CFIndex count, i; 622 const void ** keys; 623 const void ** values; 624 625 assert(object); 626 627 recordObjectInIDRefDictionary(object, state); 628 629 type = CFGetTypeID(object); 630 631 if (type == CFDictionaryGetTypeID()) { 632 CFDictionaryRef dict = (CFDictionaryRef)object; 633 count = CFDictionaryGetCount(dict); 634 if (count) { 635 keys = (const void **)malloc(count * sizeof(void *)); 636 values = (const void **)malloc(count * sizeof(void *)); 637 if (!keys || !values) { 638 return false; 639 } 640 CFDictionaryGetKeysAndValues(dict, keys, values); 641 for (i = 0; ok && (i < count); i++) { 642 // don't record dictionary keys 643 ok &= DoIdrefScan((CFTypeRef)values[i], state); 644 } 645 646 free(keys); 647 free(values); 648 if (!ok) { 649 return ok; 650 } 651 } 652 } else if (type == CFArrayGetTypeID()) { 653 CFArrayRef array = (CFArrayRef)object; 654 count = CFArrayGetCount(array); 655 for (i = 0; i < count; i++) { 656 CFTypeRef element = CFArrayGetValueAtIndex(array, i); 657 ok = DoIdrefScan(element, state); 658 if (!ok) { 659 return ok; 660 } 661 } 662 } else if (type == CFSetGetTypeID()) { 663 CFSetRef set = (CFSetRef)object; 664 count = CFSetGetCount(set); 665 if (count) { 666 values = (const void **)malloc(count * sizeof(void *)); 667 if (!values) { 668 return false; 669 } 670 CFSetGetValues(set, values); 671 for (i = 0; ok && (i < count); i++) { 672 ok = DoIdrefScan((CFTypeRef)values[i], state); 673 } 674 675 free(values); 676 if (!ok) { 677 return ok; 678 } 679 } 680 681 } 682 683 return ok; 684} 685 686static Boolean 687DoCFSerialize(CFTypeRef object, IOCFSerializeState * state) 688{ 689 CFTypeID type; 690 Boolean ok; 691 692 assert(object); 693 694 type = CFGetTypeID(object); 695 696 /* Sorted by rough order of % occurrence in big plists. 697 */ 698 if (type == CFDictionaryGetTypeID()) { 699 ok = DoCFSerializeDictionary((CFDictionaryRef) object, state); 700 } else if (type == CFStringGetTypeID()) { 701 ok = DoCFSerializeString((CFStringRef) object, state); 702 } else if (type == CFArrayGetTypeID()) { 703 ok = DoCFSerializeArray((CFArrayRef) object, state); 704 } else if (type == CFNumberGetTypeID()) { 705 ok = DoCFSerializeNumber((CFNumberRef) object, state); 706 } else if (type == CFDataGetTypeID()) { 707 ok = DoCFSerializeData((CFDataRef) object, state); 708 } else if (type == CFBooleanGetTypeID()) { 709 ok = DoCFSerializeBoolean((CFBooleanRef) object, state); 710 } else if (type == CFSetGetTypeID()) { 711 ok = DoCFSerializeSet((CFSetRef) object, state); 712 } else { 713 CFStringRef temp = NULL; 714 temp = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, 715 CFSTR("<string>typeID 0x%x not serializable</string>"), (int) type); 716 if (temp) { 717 ok = DoCFSerializeString(temp, state); 718 CFRelease(temp); 719 } else { 720 ok = false; 721 } 722 723 } 724 725 return ok; 726} 727 728CFDataRef 729IOCFSerialize(CFTypeRef object, CFOptionFlags options) 730{ 731 IOCFSerializeState state; 732 Boolean ok = FALSE; 733 CFDictionaryKeyCallBacks idrefKeyCallbacks; 734 735 if ((!object) || (options)) return 0; 736 737 state.data = CFDataCreateMutable(kCFAllocatorDefault, 0); 738 assert(state.data); 739 740 state.idrefNumRefs = 0; 741 742 idrefKeyCallbacks = kCFTypeDictionaryKeyCallBacks; 743 // only use pointer equality for these keys 744 idrefKeyCallbacks.equal = NULL; 745 746 state.stringIDRefDictionary = CFDictionaryCreateMutable( 747 kCFAllocatorDefault, 0, 748 &idrefKeyCallbacks, &kCFTypeDictionaryValueCallBacks); 749 assert(state.stringIDRefDictionary); 750 751 state.numberIDRefDictionary = CFDictionaryCreateMutable( 752 kCFAllocatorDefault, 0, 753 &idrefKeyCallbacks, &kCFTypeDictionaryValueCallBacks); 754 assert(state.numberIDRefDictionary); 755 756 state.dataIDRefDictionary = CFDictionaryCreateMutable( 757 kCFAllocatorDefault, 0, 758 &idrefKeyCallbacks, &kCFTypeDictionaryValueCallBacks); 759 assert(state.dataIDRefDictionary); 760 761 state.dictionaryIDRefDictionary = CFDictionaryCreateMutable( 762 kCFAllocatorDefault, 0, 763 &idrefKeyCallbacks, &kCFTypeDictionaryValueCallBacks); 764 assert(state.dictionaryIDRefDictionary); 765 766 state.arrayIDRefDictionary = CFDictionaryCreateMutable( 767 kCFAllocatorDefault, 0, 768 &idrefKeyCallbacks, &kCFTypeDictionaryValueCallBacks); 769 assert(state.arrayIDRefDictionary); 770 771 state.setIDRefDictionary = CFDictionaryCreateMutable( 772 kCFAllocatorDefault, 0, 773 &idrefKeyCallbacks, &kCFTypeDictionaryValueCallBacks); 774 assert(state.setIDRefDictionary); 775 776 ok = DoIdrefScan(object, &state); 777 if (!ok) { 778 goto finish; 779 } 780 781 ok = DoCFSerialize(object, &state); 782 783 if (ok) { 784 ok = addChar(0, &state); 785 } 786 if (!ok) { 787 goto finish; 788 } 789 790finish: 791 if (!ok && state.data) { 792 CFRelease(state.data); 793 state.data = NULL; // it's returned 794 } 795 796 if (state.stringIDRefDictionary) CFRelease(state.stringIDRefDictionary); 797 if (state.numberIDRefDictionary) CFRelease(state.numberIDRefDictionary); 798 if (state.dataIDRefDictionary) CFRelease(state.dataIDRefDictionary); 799 if (state.dictionaryIDRefDictionary) CFRelease(state.dictionaryIDRefDictionary); 800 if (state.arrayIDRefDictionary) CFRelease(state.arrayIDRefDictionary); 801 if (state.setIDRefDictionary) CFRelease(state.setIDRefDictionary); 802 803 return state.data; 804} 805