2 * Copyright (c) 2008 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 Copyright 1998-2002, Apple, Inc. All rights reserved.
25 Responsibility: Christopher Kane
28 #include <CoreFoundation/CFTree.h>
29 #include "CFInternal.h"
32 struct __CFTreeCallBacks
{
33 CFTreeRetainCallBack retain
;
34 CFTreeReleaseCallBack release
;
35 CFTreeCopyDescriptionCallBack copyDescription
;
40 CFTreeRef _parent
; /* Not retained */
41 CFTreeRef _sibling
; /* Not retained */
42 CFTreeRef _child
; /* All children get a retain from the parent */
43 CFTreeRef _rightmostChild
; /* Not retained */
44 /* This is the context, exploded.
45 * Currently the only valid version is 0, so we do not store that.
46 * _callbacks initialized if not a special form. */
48 struct __CFTreeCallBacks
*_callbacks
;
51 static const struct __CFTreeCallBacks __kCFTypeTreeCallBacks
= {CFRetain
, CFRelease
, CFCopyDescription
};
52 static const struct __CFTreeCallBacks __kCFNullTreeCallBacks
= {NULL
, NULL
, NULL
};
55 __kCFTreeHasNullCallBacks
= 0,
56 __kCFTreeHasCFTypeCallBacks
= 1,
57 __kCFTreeHasCustomCallBacks
= 3 /* callbacks pointed to by _callbacks */
60 CF_INLINE
uint32_t __CFTreeGetCallBacksType(CFTreeRef tree
) {
61 return (__CFBitfieldGetValue(tree
->_base
._cfinfo
[CF_INFO_BITS
], 1, 0));
64 CF_INLINE
const struct __CFTreeCallBacks
*__CFTreeGetCallBacks(CFTreeRef tree
) {
65 switch (__CFTreeGetCallBacksType(tree
)) {
66 case __kCFTreeHasNullCallBacks
:
67 return &__kCFNullTreeCallBacks
;
68 case __kCFTreeHasCFTypeCallBacks
:
69 return &__kCFTypeTreeCallBacks
;
70 case __kCFTreeHasCustomCallBacks
:
73 return tree
->_callbacks
;
76 CF_INLINE
bool __CFTreeCallBacksMatchNull(const CFTreeContext
*c
) {
77 return (NULL
== c
|| (c
->retain
== NULL
&& c
->release
== NULL
&& c
->copyDescription
== NULL
));
80 CF_INLINE
bool __CFTreeCallBacksMatchCFType(const CFTreeContext
*c
) {
81 return (NULL
!= c
&& (c
->retain
== CFRetain
&& c
->release
== CFRelease
&& c
->copyDescription
== CFCopyDescription
));
84 static CFStringRef
__CFTreeCopyDescription(CFTypeRef cf
) {
85 CFTreeRef tree
= (CFTreeRef
)cf
;
86 CFMutableStringRef result
;
87 CFStringRef contextDesc
= NULL
;
88 Boolean safeToReleaseContextDesc
= true;
89 const struct __CFTreeCallBacks
*cb
;
90 CFAllocatorRef allocator
;
91 allocator
= CFGetAllocator(tree
);
92 result
= CFStringCreateMutable(allocator
, 0);
93 cb
= __CFTreeGetCallBacks(tree
);
94 if (NULL
!= cb
->copyDescription
) {
95 contextDesc
= (CFStringRef
)INVOKE_CALLBACK1(cb
->copyDescription
, tree
->_info
);
96 safeToReleaseContextDesc
= _CFExecutableLinkedOnOrAfter(CFSystemVersionTiger
); // Because it came from elsewhere, only free it compatibly (3593254)
98 if (NULL
== contextDesc
) {
99 contextDesc
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("<CFTree context %p>"), tree
->_info
);
101 CFStringAppendFormat(result
, NULL
, CFSTR("<CFTree %p [%p]>{children = %u, context = %@}"), cf
, allocator
, CFTreeGetChildCount(tree
), contextDesc
);
102 if (contextDesc
&& safeToReleaseContextDesc
) CFRelease(contextDesc
);
106 static void __CFTreeDeallocate(CFTypeRef cf
) {
107 CFTreeRef tree
= (CFTreeRef
)cf
;
108 const struct __CFTreeCallBacks
*cb
;
109 CFAllocatorRef allocator
= __CFGetAllocator(tree
);
110 if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator
)) {
111 // GC: keep the tree intact during finalization.
112 CFTreeRemoveAllChildren(tree
);
114 cb
= __CFTreeGetCallBacks(tree
);
115 if (NULL
!= cb
->release
) {
116 INVOKE_CALLBACK1(cb
->release
, tree
->_info
);
118 if (__kCFTreeHasCustomCallBacks
== __CFTreeGetCallBacksType(tree
)) {
119 _CFAllocatorDeallocateGC(CFGetAllocator(tree
), tree
->_callbacks
);
124 static CFTypeID __kCFTreeTypeID
= _kCFRuntimeNotATypeID
;
126 static const CFRuntimeClass __CFTreeClass
= {
127 _kCFRuntimeScannedObject
,
135 __CFTreeCopyDescription
138 __private_extern__
void __CFTreeInitialize(void) {
139 __kCFTreeTypeID
= _CFRuntimeRegisterClass(&__CFTreeClass
);
142 CFTypeID
CFTreeGetTypeID(void) {
143 return __kCFTreeTypeID
;
146 CFTreeRef
CFTreeCreate(CFAllocatorRef allocator
, const CFTreeContext
*context
) {
150 CFAssert1(NULL
!= context
, __kCFLogAssertion
, "%s(): pointer to context may not be NULL", __PRETTY_FUNCTION__
);
151 CFAssert1(0 == context
->version
, __kCFLogAssertion
, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__
);
152 size
= sizeof(struct __CFTree
) - sizeof(CFRuntimeBase
);
153 memory
= (CFTreeRef
)_CFRuntimeCreateInstance(allocator
, __kCFTreeTypeID
, size
, NULL
);
154 if (NULL
== memory
) {
157 memory
->_parent
= NULL
;
158 memory
->_sibling
= NULL
;
159 memory
->_child
= NULL
;
160 memory
->_rightmostChild
= NULL
;
162 /* Start the context off in a recognizable state */
163 __CFBitfieldSetValue(memory
->_base
._cfinfo
[CF_INFO_BITS
], 1, 0, __kCFTreeHasNullCallBacks
);
164 CFTreeSetContext(memory
, context
);
168 CFIndex
CFTreeGetChildCount(CFTreeRef tree
) {
170 __CFGenericValidateType(tree
, __kCFTreeTypeID
);
172 while (NULL
!= tree
) {
174 tree
= tree
->_sibling
;
179 CFTreeRef
CFTreeGetParent(CFTreeRef tree
) {
180 __CFGenericValidateType(tree
, __kCFTreeTypeID
);
181 return tree
->_parent
;
184 CFTreeRef
CFTreeGetNextSibling(CFTreeRef tree
) {
185 __CFGenericValidateType(tree
, __kCFTreeTypeID
);
186 return tree
->_sibling
;
189 CFTreeRef
CFTreeGetFirstChild(CFTreeRef tree
) {
190 __CFGenericValidateType(tree
, __kCFTreeTypeID
);
194 CFTreeRef
CFTreeFindRoot(CFTreeRef tree
) {
195 __CFGenericValidateType(tree
, __kCFTreeTypeID
);
196 while (NULL
!= tree
->_parent
) {
197 tree
= tree
->_parent
;
202 void CFTreeGetContext(CFTreeRef tree
, CFTreeContext
*context
) {
203 const struct __CFTreeCallBacks
*cb
;
204 __CFGenericValidateType(tree
, __kCFTreeTypeID
);
205 CFAssert1(0 == context
->version
, __kCFLogAssertion
, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__
);
206 cb
= __CFTreeGetCallBacks(tree
);
207 context
->version
= 0;
208 context
->info
= tree
->_info
;
209 context
->retain
= cb
->retain
;
210 context
->release
= cb
->release
;
211 context
->copyDescription
= cb
->copyDescription
;
212 UNFAULT_CALLBACK(context
->retain
);
213 UNFAULT_CALLBACK(context
->release
);
214 UNFAULT_CALLBACK(context
->copyDescription
);
217 void CFTreeSetContext(CFTreeRef tree
, const CFTreeContext
*context
) {
218 uint32_t newtype
, oldtype
= __CFTreeGetCallBacksType(tree
);
219 struct __CFTreeCallBacks
*oldcb
= (struct __CFTreeCallBacks
*)__CFTreeGetCallBacks(tree
);
220 struct __CFTreeCallBacks
*newcb
;
221 void *oldinfo
= tree
->_info
;
222 CFAllocatorRef allocator
= CFGetAllocator(tree
);
224 if (__CFTreeCallBacksMatchNull(context
)) {
225 newtype
= __kCFTreeHasNullCallBacks
;
226 } else if (__CFTreeCallBacksMatchCFType(context
)) {
227 newtype
= __kCFTreeHasCFTypeCallBacks
;
229 newtype
= __kCFTreeHasCustomCallBacks
;
230 CF_WRITE_BARRIER_BASE_ASSIGN(allocator
, tree
, tree
->_callbacks
, _CFAllocatorAllocateGC(allocator
, sizeof(struct __CFTreeCallBacks
), 0));
231 if (__CFOASafe
) __CFSetLastAllocationEventName(tree
->_callbacks
, "CFTree (callbacks)");
232 tree
->_callbacks
->retain
= context
->retain
;
233 tree
->_callbacks
->release
= context
->release
;
234 tree
->_callbacks
->copyDescription
= context
->copyDescription
;
235 FAULT_CALLBACK((void **)&(tree
->_callbacks
->retain
));
236 FAULT_CALLBACK((void **)&(tree
->_callbacks
->release
));
237 FAULT_CALLBACK((void **)&(tree
->_callbacks
->copyDescription
));
239 __CFBitfieldSetValue(tree
->_base
._cfinfo
[CF_INFO_BITS
], 1, 0, newtype
);
240 newcb
= (struct __CFTreeCallBacks
*)__CFTreeGetCallBacks(tree
);
241 if (NULL
!= newcb
->retain
) {
242 tree
->_info
= (void *)INVOKE_CALLBACK1(newcb
->retain
, context
->info
);
244 CF_WRITE_BARRIER_BASE_ASSIGN(allocator
, tree
, tree
->_info
, context
->info
);
246 if (NULL
!= oldcb
->release
) {
247 INVOKE_CALLBACK1(oldcb
->release
, oldinfo
);
249 if (oldtype
== __kCFTreeHasCustomCallBacks
) {
250 _CFAllocatorDeallocateGC(allocator
, oldcb
);
255 CFTreeRef
CFTreeFindNextSibling(CFTreeRef tree
, const void *info
) {
256 __CFGenericValidateType(tree
, __kCFTreeTypeID
);
257 tree
= tree
->_sibling
;
258 while (NULL
!= tree
) {
259 if (info
== tree
->_context
.info
|| (tree
->_context
.equal
&& tree
->_context
.equal(info
, tree
->_context
.info
))) {
262 tree
= tree
->_sibling
;
267 CFTreeRef
CFTreeFindFirstChild(CFTreeRef tree
, const void *info
) {
268 __CFGenericValidateType(tree
, __kCFTreeTypeID
);
270 while (NULL
!= tree
) {
271 if (info
== tree
->_context
.info
|| (tree
->_context
.equal
&& tree
->_context
.equal(info
, tree
->_context
.info
))) {
274 tree
= tree
->_sibling
;
279 CFTreeRef
CFTreeFind(CFTreeRef tree
, const void *info
) {
280 __CFGenericValidateType(tree
, __kCFTreeTypeID
);
281 if (info
== tree
->_context
.info
|| (tree
->_context
.equal
&& tree
->_context
.equal(info
, tree
->info
))) {
285 while (NULL
!= tree
) {
286 CFTreeRef found
= CFTreeFind(tree
, info
);
290 tree
= tree
->_sibling
;
296 CFTreeRef
CFTreeGetChildAtIndex(CFTreeRef tree
, CFIndex idx
) {
297 __CFGenericValidateType(tree
, __kCFTreeTypeID
);
299 while (NULL
!= tree
) {
300 if (0 == idx
) return tree
;
302 tree
= tree
->_sibling
;
307 void CFTreeGetChildren(CFTreeRef tree
, CFTreeRef
*children
) {
308 __CFGenericValidateType(tree
, __kCFTreeTypeID
);
310 while (NULL
!= tree
) {
312 tree
= tree
->_sibling
;
316 void CFTreeApplyFunctionToChildren(CFTreeRef tree
, CFTreeApplierFunction applier
, void *context
) {
317 __CFGenericValidateType(tree
, __kCFTreeTypeID
);
318 CFAssert1(NULL
!= applier
, __kCFLogAssertion
, "%s(): pointer to applier function may not be NULL", __PRETTY_FUNCTION__
);
320 while (NULL
!= tree
) {
321 applier(tree
, context
);
322 tree
= tree
->_sibling
;
326 void CFTreePrependChild(CFTreeRef tree
, CFTreeRef newChild
) {
327 CFAllocatorRef allocator
= CFGetAllocator(tree
);
328 __CFGenericValidateType(tree
, __kCFTreeTypeID
);
329 __CFGenericValidateType(newChild
, __kCFTreeTypeID
);
330 CFAssert1(NULL
== newChild
->_parent
, __kCFLogAssertion
, "%s(): must remove newChild from previous parent first", __PRETTY_FUNCTION__
);
331 CFAssert1(NULL
== newChild
->_sibling
, __kCFLogAssertion
, "%s(): must remove newChild from previous parent first", __PRETTY_FUNCTION__
);
332 _CFRetainGC(newChild
);
333 CF_WRITE_BARRIER_BASE_ASSIGN(allocator
, newChild
, newChild
->_parent
, tree
);
334 CF_WRITE_BARRIER_BASE_ASSIGN(allocator
, newChild
, newChild
->_sibling
, tree
->_child
);
336 CF_WRITE_BARRIER_BASE_ASSIGN(allocator
, tree
, tree
->_rightmostChild
, newChild
);
338 CF_WRITE_BARRIER_BASE_ASSIGN(allocator
, tree
, tree
->_child
, newChild
);
341 void CFTreeAppendChild(CFTreeRef tree
, CFTreeRef newChild
) {
342 CFAllocatorRef allocator
;
343 __CFGenericValidateType(tree
, __kCFTreeTypeID
);
344 __CFGenericValidateType(newChild
, __kCFTreeTypeID
);
345 CFAssert1(NULL
== newChild
->_parent
, __kCFLogAssertion
, "%s(): must remove newChild from previous parent first", __PRETTY_FUNCTION__
);
346 CFAssert1(NULL
== newChild
->_sibling
, __kCFLogAssertion
, "%s(): must remove newChild from previous parent first", __PRETTY_FUNCTION__
);
347 if (newChild
->_parent
) {
350 _CFRetainGC(newChild
);
351 allocator
= CFGetAllocator(tree
);
352 CF_WRITE_BARRIER_BASE_ASSIGN(allocator
, newChild
, newChild
->_parent
, tree
);
353 newChild
->_sibling
= NULL
;
355 CF_WRITE_BARRIER_BASE_ASSIGN(allocator
, tree
, tree
->_child
, newChild
);
357 CF_WRITE_BARRIER_BASE_ASSIGN(allocator
, tree
->_rightmostChild
, tree
->_rightmostChild
->_sibling
, newChild
);
359 CF_WRITE_BARRIER_BASE_ASSIGN(allocator
, tree
, tree
->_rightmostChild
, newChild
);
362 void CFTreeInsertSibling(CFTreeRef tree
, CFTreeRef newSibling
) {
363 CFAllocatorRef allocator
;
364 __CFGenericValidateType(tree
, __kCFTreeTypeID
);
365 __CFGenericValidateType(newSibling
, __kCFTreeTypeID
);
366 CFAssert1(NULL
!= tree
->_parent
, __kCFLogAssertion
, "%s(): tree must have a parent", __PRETTY_FUNCTION__
);
367 CFAssert1(NULL
== newSibling
->_parent
, __kCFLogAssertion
, "%s(): must remove newSibling from previous parent first", __PRETTY_FUNCTION__
);
368 CFAssert1(NULL
== newSibling
->_sibling
, __kCFLogAssertion
, "%s(): must remove newSibling from previous parent first", __PRETTY_FUNCTION__
);
369 _CFRetainGC(newSibling
);
370 allocator
= CFGetAllocator(tree
);
371 CF_WRITE_BARRIER_BASE_ASSIGN(allocator
, newSibling
, newSibling
->_parent
, tree
->_parent
);
372 CF_WRITE_BARRIER_BASE_ASSIGN(allocator
, newSibling
, newSibling
->_sibling
, tree
->_sibling
);
373 CF_WRITE_BARRIER_BASE_ASSIGN(allocator
, tree
, tree
->_sibling
, newSibling
);
375 if (tree
->_parent
->_rightmostChild
== tree
) {
376 CF_WRITE_BARRIER_BASE_ASSIGN(allocator
, tree
->_parent
, tree
->_parent
->_rightmostChild
, newSibling
);
381 void CFTreeRemove(CFTreeRef tree
) {
382 __CFGenericValidateType(tree
, __kCFTreeTypeID
);
383 if (NULL
!= tree
->_parent
) {
384 CFAllocatorRef allocator
= CFGetAllocator(tree
);
385 if (tree
== tree
->_parent
->_child
) {
386 CF_WRITE_BARRIER_BASE_ASSIGN(allocator
, tree
->_parent
, tree
->_parent
->_child
, tree
->_sibling
);
387 if (tree
->_sibling
== NULL
) {
388 tree
->_parent
->_rightmostChild
= NULL
;
391 CFTreeRef prevSibling
= NULL
;
392 for (prevSibling
= tree
->_parent
->_child
; prevSibling
; prevSibling
= prevSibling
->_sibling
) {
393 if (prevSibling
->_sibling
== tree
) {
394 CF_WRITE_BARRIER_BASE_ASSIGN(allocator
, prevSibling
, prevSibling
->_sibling
, tree
->_sibling
);
395 if (tree
->_parent
->_rightmostChild
== tree
) {
396 CF_WRITE_BARRIER_BASE_ASSIGN(allocator
, tree
->_parent
, tree
->_parent
->_rightmostChild
, prevSibling
);
402 tree
->_parent
= NULL
;
403 tree
->_sibling
= NULL
;
408 void CFTreeRemoveAllChildren(CFTreeRef tree
) {
410 __CFGenericValidateType(tree
, __kCFTreeTypeID
);
411 nextChild
= tree
->_child
;
413 tree
->_rightmostChild
= NULL
;
414 while (NULL
!= nextChild
) {
415 CFTreeRef nextSibling
= nextChild
->_sibling
;
416 nextChild
->_parent
= NULL
;
417 nextChild
->_sibling
= NULL
;
418 _CFReleaseGC(nextChild
);
419 nextChild
= nextSibling
;
423 struct _tcompareContext
{
424 CFComparatorFunction func
;
428 static CFComparisonResult
__CFTreeCompareValues(const void *v1
, const void *v2
, struct _tcompareContext
*context
) {
429 const void **val1
= (const void **)v1
;
430 const void **val2
= (const void **)v2
;
431 return (CFComparisonResult
)(INVOKE_CALLBACK3(context
->func
, *val1
, *val2
, context
->context
));
434 void CFTreeSortChildren(CFTreeRef tree
, CFComparatorFunction comparator
, void *context
) {
436 __CFGenericValidateType(tree
, __kCFTreeTypeID
);
437 CFAssert1(NULL
!= comparator
, __kCFLogAssertion
, "%s(): pointer to comparator function may not be NULL", __PRETTY_FUNCTION__
);
438 children
= CFTreeGetChildCount(tree
);
442 struct _tcompareContext ctx
;
443 CFTreeRef
*list
, buffer
[128];
444 CFAllocatorRef allocator
= __CFGetAllocator(tree
);
446 list
= (children
< 128) ? buffer
: (CFTreeRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, children
* sizeof(CFTreeRef
), 0); // XXX_PCB GC OK
447 if (__CFOASafe
&& list
!= buffer
) __CFSetLastAllocationEventName(tree
->_callbacks
, "CFTree (temp)");
448 nextChild
= tree
->_child
;
449 for (idx
= 0; NULL
!= nextChild
; idx
++) {
450 list
[idx
] = nextChild
;
451 nextChild
= nextChild
->_sibling
;
454 ctx
.func
= comparator
;
455 ctx
.context
= context
;
456 CFQSortArray(list
, children
, sizeof(CFTreeRef
), (CFComparatorFunction
)__CFTreeCompareValues
, &ctx
);
458 CF_WRITE_BARRIER_BASE_ASSIGN(allocator
, tree
, tree
->_child
, list
[0]);
459 for (idx
= 1; idx
< children
; idx
++) {
460 CF_WRITE_BARRIER_BASE_ASSIGN(allocator
, list
[idx
- 1], list
[idx
- 1]->_sibling
, list
[idx
]);
462 list
[idx
- 1]->_sibling
= NULL
;
463 tree
->_rightmostChild
= list
[children
- 1];
464 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
); // XXX_PCB GC OK