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("&lt;", state)) return false;
395		} else if (c == '>') {
396			if (!addString("&gt;", state)) return false;
397		} else if (c == '&') {
398			if (!addString("&amp;", 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("&lt;", state)) return false;
453		} else if (c == '>') {
454			if (!addString("&gt;", state)) return false;
455		} else if (c == '&') {
456			if (!addString("&amp;", 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