2 * Copyright (c) 2005 Apple Computer, 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 1999-2002, Apple, Inc. All rights reserved.
25 Responsibility: Ali Ozer
29 2-3 tree storing arbitrary sized values.
30 ??? Currently elementSize cannot be greater than storage->maxLeafCapacity, which is less than or equal to __CFStorageMaxLeafCapacity
33 #include "CFStorage.h"
34 #include "CFInternal.h"
37 #include <mach/mach.h>
45 __CFStorageMaxLeafCapacity
= 65536
48 #define COPYMEM(src,dst,n) CF_WRITE_BARRIER_MEMMOVE((dst), (src), (n))
49 #define PAGE_LIMIT ((CFIndex)vm_page_size / 2)
51 CF_INLINE
int roundToPage(int num
) {
52 return (num
+ vm_page_size
- 1) & ~(vm_page_size
- 1);
55 typedef struct __CFStorageNode
{
56 CFIndex numBytes
; /* Number of actual bytes in this node and all its children */
60 CFIndex capacityInBytes
; // capacityInBytes is capacity of memory; this is either 0, or >= numBytes
64 struct __CFStorageNode
*child
[3];
72 CFRange cachedRange
; // In terms of values, not bytes
73 CFStorageNode
*cachedNode
; // If cachedRange is valid, then either this or
74 uint8_t *cachedNodeMemory
; // this should be non-NULL
75 CFIndex maxLeafCapacity
; // In terms of bytes
76 CFStorageNode rootNode
;
77 CFOptionFlags nodeHint
; // auto_memory_type_t, AUTO_MEMORY_SCANNED or AUTO_MEMORY_UNSCANNED.
80 /* Allocates the memory and initializes the capacity in a leaf. __CFStorageAllocLeafNodeMemory() is the entry point; __CFStorageAllocLeafNodeMemoryAux is called if actual reallocation is needed.
82 static void __CFStorageAllocLeafNodeMemoryAux(CFAllocatorRef allocator
, CFStorageRef storage
, CFStorageNode
*node
, CFIndex cap
) {
83 CF_WRITE_BARRIER_ASSIGN(allocator
, node
->info
.leaf
.memory
, _CFAllocatorReallocateGC(allocator
, node
->info
.leaf
.memory
, cap
, storage
->nodeHint
)); // This will free... ??? Use allocator
84 if (__CFOASafe
) __CFSetLastAllocationEventName(node
->info
.leaf
.memory
, "CFStorage (node bytes)");
85 node
->info
.leaf
.capacityInBytes
= cap
;
88 CF_INLINE
void __CFStorageAllocLeafNodeMemory(CFAllocatorRef allocator
, CFStorageRef storage
, CFStorageNode
*node
, CFIndex cap
, bool compact
) {
89 if (cap
> PAGE_LIMIT
) {
90 cap
= roundToPage(cap
);
91 if (cap
> storage
->maxLeafCapacity
) cap
= storage
->maxLeafCapacity
;
93 cap
= (((cap
+ 63) / 64) * 64);
95 if (compact
? (cap
!= node
->info
.leaf
.capacityInBytes
) : (cap
> node
->info
.leaf
.capacityInBytes
)) __CFStorageAllocLeafNodeMemoryAux(allocator
, storage
, node
, cap
);
98 /* Sets the cache to point at the specified node or memory. loc and len are in terms of values, not bytes. To clear the cache set these two to 0.
99 At least one of node or memory should be non-NULL. memory is consulted first when using the cache.
101 CF_INLINE
void __CFStorageSetCache(CFStorageRef storage
, CFStorageNode
*node
, uint8_t *memory
, CFIndex loc
, CFIndex len
) {
102 CFAllocatorRef allocator
= __CFGetAllocator(storage
);
103 storage
->cachedRange
.location
= loc
;
104 storage
->cachedRange
.length
= len
;
105 CF_WRITE_BARRIER_BASE_ASSIGN(allocator
, storage
, storage
->cachedNode
, node
);
106 CF_WRITE_BARRIER_BASE_ASSIGN(allocator
, storage
, storage
->cachedNodeMemory
, memory
);
109 /* Gets the location for the specified absolute loc from the cached info; call only after verifying that the cache is OK to use.
110 Note that we assume if !storage->cachedNodeMemory, then storage->cachedNode must be non-NULL.
111 However, it is possible to have storage->cachedNodeMemory without storage->cachedNode. We check the memory before node.
113 CF_INLINE
uint8_t *__CFStorageGetFromCache(CFStorageRef storage
, CFIndex loc
) {
114 if (!storage
->cachedNodeMemory
&& !(storage
->cachedNodeMemory
= storage
->cachedNode
->info
.leaf
.memory
)) {
115 CFAllocatorRef allocator
= CFGetAllocator(storage
);
116 __CFStorageAllocLeafNodeMemory(allocator
, storage
, storage
->cachedNode
, storage
->cachedNode
->numBytes
, false);
117 CF_WRITE_BARRIER_BASE_ASSIGN(allocator
, storage
, storage
->cachedNodeMemory
, storage
->cachedNode
->info
.leaf
.memory
);
119 return storage
->cachedNodeMemory
+ (loc
- storage
->cachedRange
.location
) * storage
->valueSize
;
122 /* Returns the number of the child containing the desired value and the relative index of the value in that child.
123 forInsertion = true means that we are looking for the child in which to insert; this changes the behavior when the index is at the end of a child
124 relativeByteNum (not optional, for performance reasons) returns the relative byte number of the specified byte in the child.
125 Don't call with leaf nodes!
127 CF_INLINE
void __CFStorageFindChild(CFStorageNode
*node
, CFIndex byteNum
, bool forInsertion
, CFIndex
*childNum
, CFIndex
*relativeByteNum
) {
128 if (forInsertion
) byteNum
--; /* If for insertion, we do <= checks, not <, so this accomplishes the same thing */
129 if (byteNum
< node
->info
.notLeaf
.child
[0]->numBytes
) *childNum
= 0;
131 byteNum
-= node
->info
.notLeaf
.child
[0]->numBytes
;
132 if (byteNum
< node
->info
.notLeaf
.child
[1]->numBytes
) *childNum
= 1;
134 byteNum
-= node
->info
.notLeaf
.child
[1]->numBytes
;
138 if (forInsertion
) byteNum
++;
139 *relativeByteNum
= byteNum
;
142 /* Finds the location where the specified byte is stored. If validConsecutiveByteRange is not NULL, returns
143 the range of bytes that are consecutive with this one.
144 !!! Assumes the byteNum is within the range of this node.
146 static void *__CFStorageFindByte(CFStorageRef storage
, CFStorageNode
*node
, CFIndex byteNum
, CFRange
*validConsecutiveByteRange
) {
148 if (validConsecutiveByteRange
) *validConsecutiveByteRange
= CFRangeMake(0, node
->numBytes
);
149 __CFStorageAllocLeafNodeMemory(CFGetAllocator(storage
), storage
, node
, node
->numBytes
, false);
150 return node
->info
.leaf
.memory
+ byteNum
;
154 CFIndex relativeByteNum
;
155 __CFStorageFindChild(node
, byteNum
, false, &childNum
, &relativeByteNum
);
156 result
= __CFStorageFindByte(storage
, node
->info
.notLeaf
.child
[childNum
], relativeByteNum
, validConsecutiveByteRange
);
157 if (validConsecutiveByteRange
) {
158 if (childNum
> 0) validConsecutiveByteRange
->location
+= node
->info
.notLeaf
.child
[0]->numBytes
;
159 if (childNum
> 1) validConsecutiveByteRange
->location
+= node
->info
.notLeaf
.child
[1]->numBytes
;
165 /* Guts of CFStorageGetValueAtIndex(); note that validConsecutiveValueRange is not optional.
166 Consults and updates cache.
168 CF_INLINE
void *__CFStorageGetValueAtIndex(CFStorageRef storage
, CFIndex idx
, CFRange
*validConsecutiveValueRange
) {
170 if (idx
< storage
->cachedRange
.location
+ storage
->cachedRange
.length
&& idx
>= storage
->cachedRange
.location
) {
171 result
= __CFStorageGetFromCache(storage
, idx
);
174 result
= __CFStorageFindByte(storage
, &storage
->rootNode
, idx
* storage
->valueSize
, &range
);
175 __CFStorageSetCache(storage
, NULL
, result
- (idx
* storage
->valueSize
- range
.location
), range
.location
/ storage
->valueSize
, range
.length
/ storage
->valueSize
);
177 *validConsecutiveValueRange
= storage
->cachedRange
;
181 static CFStorageNode
*__CFStorageCreateNode(CFAllocatorRef allocator
, bool isLeaf
, CFIndex numBytes
) {
182 CFStorageNode
*newNode
= _CFAllocatorAllocateGC(allocator
, sizeof(CFStorageNode
), 0);
183 if (__CFOASafe
) __CFSetLastAllocationEventName(newNode
, "CFStorage (node)");
184 newNode
->isLeaf
= isLeaf
;
185 newNode
->numBytes
= numBytes
;
187 newNode
->info
.leaf
.capacityInBytes
= 0;
188 newNode
->info
.leaf
.memory
= NULL
;
190 newNode
->info
.notLeaf
.child
[0] = newNode
->info
.notLeaf
.child
[1] = newNode
->info
.notLeaf
.child
[2] = NULL
;
195 static void __CFStorageNodeDealloc(CFAllocatorRef allocator
, CFStorageNode
*node
, bool freeNodeItself
) {
197 _CFAllocatorDeallocateGC(allocator
, node
->info
.leaf
.memory
);
200 for (cnt
= 0; cnt
< 3; cnt
++) if (node
->info
.notLeaf
.child
[cnt
]) __CFStorageNodeDealloc(allocator
, node
->info
.notLeaf
.child
[cnt
], true);
202 if (freeNodeItself
) _CFAllocatorDeallocateGC(allocator
, node
);
205 static CFIndex
__CFStorageGetNumChildren(CFStorageNode
*node
) {
206 if (!node
|| node
->isLeaf
) return 0;
207 if (node
->info
.notLeaf
.child
[2]) return 3;
208 if (node
->info
.notLeaf
.child
[1]) return 2;
209 if (node
->info
.notLeaf
.child
[0]) return 1;
213 /* The boolean compact indicates whether leaf nodes that get smaller should be realloced.
215 static void __CFStorageDelete(CFAllocatorRef allocator
, CFStorageRef storage
, CFStorageNode
*node
, CFRange range
, bool compact
) {
217 node
->numBytes
-= range
.length
;
218 // If this node had memory allocated, readjust the bytes...
219 if (node
->info
.leaf
.memory
) {
220 COPYMEM(node
->info
.leaf
.memory
+ range
.location
+ range
.length
, node
->info
.leaf
.memory
+ range
.location
, node
->numBytes
- range
.location
);
221 if (compact
) __CFStorageAllocLeafNodeMemory(allocator
, storage
, node
, node
->numBytes
, true);
224 bool childrenAreLeaves
= node
->info
.notLeaf
.child
[0]->isLeaf
;
225 node
->numBytes
-= range
.length
;
226 while (range
.length
> 0) {
227 CFRange rangeToDelete
;
228 CFIndex relativeByteNum
;
230 __CFStorageFindChild(node
, range
.location
+ range
.length
, true, &childNum
, &relativeByteNum
);
231 if (range
.length
> relativeByteNum
) {
232 rangeToDelete
.length
= relativeByteNum
;
233 rangeToDelete
.location
= 0;
235 rangeToDelete
.length
= range
.length
;
236 rangeToDelete
.location
= relativeByteNum
- range
.length
;
238 __CFStorageDelete(allocator
, storage
, node
->info
.notLeaf
.child
[childNum
], rangeToDelete
, compact
);
239 if (node
->info
.notLeaf
.child
[childNum
]->numBytes
== 0) { // Delete empty node and compact
241 _CFAllocatorDeallocateGC(allocator
, node
->info
.notLeaf
.child
[childNum
]);
242 for (cnt
= childNum
; cnt
< 2; cnt
++) {
243 CF_WRITE_BARRIER_ASSIGN(allocator
, node
->info
.notLeaf
.child
[cnt
], node
->info
.notLeaf
.child
[cnt
+1]);
245 node
->info
.notLeaf
.child
[2] = NULL
;
247 range
.length
-= rangeToDelete
.length
;
249 // At this point the remaining children are packed
250 if (childrenAreLeaves
) {
251 // Children are leaves; if their total bytes is smaller than a leaf's worth, collapse into one...
252 if (node
->numBytes
> 0 && node
->numBytes
<= storage
->maxLeafCapacity
) {
253 __CFStorageAllocLeafNodeMemory(allocator
, storage
, node
->info
.notLeaf
.child
[0], node
->numBytes
, false);
254 if (node
->info
.notLeaf
.child
[1] && node
->info
.notLeaf
.child
[1]->numBytes
) {
255 COPYMEM(node
->info
.notLeaf
.child
[1]->info
.leaf
.memory
, node
->info
.notLeaf
.child
[0]->info
.leaf
.memory
+ node
->info
.notLeaf
.child
[0]->numBytes
, node
->info
.notLeaf
.child
[1]->numBytes
);
256 if (node
->info
.notLeaf
.child
[2] && node
->info
.notLeaf
.child
[2]->numBytes
) {
257 COPYMEM(node
->info
.notLeaf
.child
[2]->info
.leaf
.memory
, node
->info
.notLeaf
.child
[0]->info
.leaf
.memory
+ node
->info
.notLeaf
.child
[0]->numBytes
+ node
->info
.notLeaf
.child
[1]->numBytes
, node
->info
.notLeaf
.child
[2]->numBytes
);
258 __CFStorageNodeDealloc(allocator
, node
->info
.notLeaf
.child
[2], true);
259 node
->info
.notLeaf
.child
[2] = NULL
;
261 __CFStorageNodeDealloc(allocator
, node
->info
.notLeaf
.child
[1], true);
262 node
->info
.notLeaf
.child
[1] = NULL
;
264 node
->info
.notLeaf
.child
[0]->numBytes
= node
->numBytes
;
267 // Children are not leaves; combine their children to assure each node has 2 or 3 children...
268 // (Could try to bypass all this by noting up above whether the number of grandchildren changed...)
269 CFStorageNode
*gChildren
[9];
270 CFIndex cCnt
, gCnt
, cnt
;
271 CFIndex totalG
= 0; // Total number of grandchildren
272 for (cCnt
= 0; cCnt
< 3; cCnt
++) {
273 CFStorageNode
*child
= node
->info
.notLeaf
.child
[cCnt
];
275 for (gCnt
= 0; gCnt
< 3; gCnt
++) if (child
->info
.notLeaf
.child
[gCnt
]) {
276 gChildren
[totalG
++] = child
->info
.notLeaf
.child
[gCnt
];
277 child
->info
.notLeaf
.child
[gCnt
] = NULL
;
282 gCnt
= 0; // Total number of grandchildren placed
283 for (cCnt
= 0; cCnt
< 3; cCnt
++) {
284 // These tables indicate how many children each child should have, given the total number of grandchildren (last child gets remainder)
285 static const unsigned char forChild0
[10] = {0, 1, 2, 3, 2, 3, 3, 3, 3, 3};
286 static const unsigned char forChild1
[10] = {0, 0, 0, 0, 2, 2, 3, 2, 3, 3};
287 // sCnt is the number of grandchildren to be placed into child cCnt
288 // Depending on child number, pick the right number
289 CFIndex sCnt
= (cCnt
== 0) ? forChild0
[totalG
] : ((cCnt
== 1) ? forChild1
[totalG
] : totalG
);
290 // Assure we have that many grandchildren...
291 if (sCnt
> totalG
- gCnt
) sCnt
= totalG
- gCnt
;
293 if (!node
->info
.notLeaf
.child
[cCnt
]) {
294 CFStorageNode
*newNode
= __CFStorageCreateNode(allocator
, false, 0);
295 CF_WRITE_BARRIER_ASSIGN(allocator
, node
->info
.notLeaf
.child
[cCnt
], newNode
);
297 for (cnt
= 0; cnt
< sCnt
; cnt
++) {
298 node
->info
.notLeaf
.child
[cCnt
]->numBytes
+= gChildren
[gCnt
]->numBytes
;
299 CF_WRITE_BARRIER_ASSIGN(allocator
, node
->info
.notLeaf
.child
[cCnt
]->info
.notLeaf
.child
[cnt
], gChildren
[gCnt
++]);
302 if (node
->info
.notLeaf
.child
[cCnt
]) {
303 _CFAllocatorDeallocateGC(allocator
, node
->info
.notLeaf
.child
[cCnt
]);
304 node
->info
.notLeaf
.child
[cCnt
] = NULL
;
313 /* Returns NULL or additional node to come after this node
314 Assumption: size is never > storage->maxLeafCapacity
316 static CFStorageNode
*__CFStorageInsert(CFAllocatorRef allocator
, CFStorageRef storage
, CFStorageNode
*node
, CFIndex byteNum
, CFIndex size
, CFIndex absoluteByteNum
) {
318 if (size
+ node
->numBytes
> storage
->maxLeafCapacity
) { // Need to create more child nodes
319 if (byteNum
== node
->numBytes
) { // Inserting at end; easy...
320 CFStorageNode
*newNode
= __CFStorageCreateNode(allocator
, true, size
);
321 __CFStorageSetCache(storage
, newNode
, NULL
, absoluteByteNum
/ storage
->valueSize
, size
/ storage
->valueSize
);
323 } else if (byteNum
== 0) { // Inserting at front; also easy, but we need to swap node and newNode
324 CFStorageNode
*newNode
= __CFStorageCreateNode(allocator
, true, 0);
325 CF_WRITE_BARRIER_MEMMOVE(newNode
, node
, sizeof(CFStorageNode
));
327 node
->numBytes
= size
;
328 node
->info
.leaf
.capacityInBytes
= 0;
329 node
->info
.leaf
.memory
= NULL
;
330 __CFStorageSetCache(storage
, node
, NULL
, absoluteByteNum
/ storage
->valueSize
, size
/ storage
->valueSize
);
332 } else if (byteNum
+ size
<= storage
->maxLeafCapacity
) { // Inserting at middle; inserted region will fit into existing child
333 // Create new node to hold the overflow
334 CFStorageNode
*newNode
= __CFStorageCreateNode(allocator
, true, node
->numBytes
- byteNum
);
335 if (node
->info
.leaf
.memory
) { // We allocate memory lazily...
336 __CFStorageAllocLeafNodeMemory(allocator
, storage
, newNode
, node
->numBytes
- byteNum
, false);
337 COPYMEM(node
->info
.leaf
.memory
+ byteNum
, newNode
->info
.leaf
.memory
, node
->numBytes
- byteNum
);
338 __CFStorageAllocLeafNodeMemory(allocator
, storage
, node
, byteNum
+ size
, false);
340 node
->numBytes
= byteNum
+ size
;
341 __CFStorageSetCache(storage
, node
, node
->info
.leaf
.memory
, (absoluteByteNum
- byteNum
) / storage
->valueSize
, node
->numBytes
/ storage
->valueSize
);
343 } else { // Inserting some of new into one node, rest into another; remember that the assumption is size <= storage->maxLeafCapacity
344 CFStorageNode
*newNode
= __CFStorageCreateNode(allocator
, true, node
->numBytes
+ size
- storage
->maxLeafCapacity
); // New stuff
345 if (node
->info
.leaf
.memory
) { // We allocate memory lazily...
346 __CFStorageAllocLeafNodeMemory(allocator
, storage
, newNode
, node
->numBytes
+ size
- storage
->maxLeafCapacity
, false);
347 COPYMEM(node
->info
.leaf
.memory
+ byteNum
, newNode
->info
.leaf
.memory
+ byteNum
+ size
- storage
->maxLeafCapacity
, node
->numBytes
- byteNum
);
348 __CFStorageAllocLeafNodeMemory(allocator
, storage
, node
, storage
->maxLeafCapacity
, false);
350 node
->numBytes
= storage
->maxLeafCapacity
;
351 __CFStorageSetCache(storage
, node
, node
->info
.leaf
.memory
, (absoluteByteNum
- byteNum
) / storage
->valueSize
, node
->numBytes
/ storage
->valueSize
);
352 //__CFStorageSetCache(storage, NULL, NULL, 0, 0);
355 } else { // No need to create new nodes!
356 if (node
->info
.leaf
.memory
) {
357 __CFStorageAllocLeafNodeMemory(allocator
, storage
, node
, node
->numBytes
+ size
, false);
358 COPYMEM(node
->info
.leaf
.memory
+ byteNum
, node
->info
.leaf
.memory
+ byteNum
+ size
, node
->numBytes
- byteNum
);
360 node
->numBytes
+= size
;
361 __CFStorageSetCache(storage
, node
, node
->info
.leaf
.memory
, (absoluteByteNum
- byteNum
) / storage
->valueSize
, node
->numBytes
/ storage
->valueSize
);
365 CFIndex relativeByteNum
;
367 CFStorageNode
*newNode
;
368 __CFStorageFindChild(node
, byteNum
, true, &childNum
, &relativeByteNum
);
369 newNode
= __CFStorageInsert(allocator
, storage
, node
->info
.notLeaf
.child
[childNum
], relativeByteNum
, size
, absoluteByteNum
);
371 if (node
->info
.notLeaf
.child
[2] == NULL
) { // There's an empty slot for the new node, cool
372 if (childNum
== 0) CF_WRITE_BARRIER_ASSIGN(allocator
, node
->info
.notLeaf
.child
[2], node
->info
.notLeaf
.child
[1]); // Make room
373 CF_WRITE_BARRIER_ASSIGN(allocator
, node
->info
.notLeaf
.child
[childNum
+ 1], newNode
);
374 node
->numBytes
+= size
;
377 CFStorageNode
*anotherNode
= __CFStorageCreateNode(allocator
, false, 0); // Create another node
378 if (childNum
== 0) { // Last two children go to new node
379 CF_WRITE_BARRIER_ASSIGN(allocator
, anotherNode
->info
.notLeaf
.child
[0], node
->info
.notLeaf
.child
[1]);
380 CF_WRITE_BARRIER_ASSIGN(allocator
, anotherNode
->info
.notLeaf
.child
[1], node
->info
.notLeaf
.child
[2]);
381 CF_WRITE_BARRIER_ASSIGN(allocator
, node
->info
.notLeaf
.child
[1], newNode
);
382 node
->info
.notLeaf
.child
[2] = NULL
;
383 } else if (childNum
== 1) { // Last child goes to new node
384 CF_WRITE_BARRIER_ASSIGN(allocator
, anotherNode
->info
.notLeaf
.child
[0], newNode
);
385 CF_WRITE_BARRIER_ASSIGN(allocator
, anotherNode
->info
.notLeaf
.child
[1], node
->info
.notLeaf
.child
[2]);
386 node
->info
.notLeaf
.child
[2] = NULL
;
387 } else { // New node contains the new comers...
388 CF_WRITE_BARRIER_ASSIGN(allocator
, anotherNode
->info
.notLeaf
.child
[0], node
->info
.notLeaf
.child
[2]);
389 CF_WRITE_BARRIER_ASSIGN(allocator
, anotherNode
->info
.notLeaf
.child
[1], newNode
);
390 node
->info
.notLeaf
.child
[2] = NULL
;
392 node
->numBytes
= node
->info
.notLeaf
.child
[0]->numBytes
+ node
->info
.notLeaf
.child
[1]->numBytes
;
393 anotherNode
->numBytes
= anotherNode
->info
.notLeaf
.child
[0]->numBytes
+ anotherNode
->info
.notLeaf
.child
[1]->numBytes
;
397 node
->numBytes
+= size
;
403 CF_INLINE CFIndex
__CFStorageGetCount(CFStorageRef storage
) {
404 return storage
->rootNode
.numBytes
/ storage
->valueSize
;
407 static bool __CFStorageEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
408 CFStorageRef storage1
= (CFStorageRef
)cf1
;
409 CFStorageRef storage2
= (CFStorageRef
)cf2
;
410 CFIndex loc
, count
, valueSize
;
411 CFRange range1
, range2
;
412 uint8_t *ptr1
, *ptr2
;
414 count
= __CFStorageGetCount(storage1
);
415 if (count
!= __CFStorageGetCount(storage2
)) return false;
417 valueSize
= __CFStorageGetValueSize(storage1
);
418 if (valueSize
!= __CFStorageGetValueSize(storage2
)) return false;
420 loc
= range1
.location
= range1
.length
= range2
.location
= range2
.length
= 0;
423 while (loc
< count
) {
425 if (loc
>= range1
.location
+ range1
.length
) ptr1
= CFStorageGetValueAtIndex(storage1
, loc
, &range1
);
426 if (loc
>= range2
.location
+ range2
.length
) ptr2
= CFStorageGetValueAtIndex(storage2
, loc
, &range2
);
427 cntThisTime
= range1
.location
+ range1
.length
;
428 if (range2
.location
+ range2
.length
< cntThisTime
) cntThisTime
= range2
.location
+ range2
.length
;
430 if (memcmp(ptr1
, ptr2
, valueSize
* cntThisTime
) != 0) return false;
431 ptr1
+= valueSize
* cntThisTime
;
432 ptr2
+= valueSize
* cntThisTime
;
438 static CFHashCode
__CFStorageHash(CFTypeRef cf
) {
439 CFStorageRef Storage
= (CFStorageRef
)cf
;
440 return __CFStorageGetCount(Storage
);
443 static void __CFStorageDescribeNode(CFStorageNode
*node
, CFMutableStringRef str
, CFIndex level
) {
445 for (cnt
= 0; cnt
< level
; cnt
++) CFStringAppendCString(str
, " ", CFStringGetSystemEncoding());
448 CFStringAppendFormat(str
, NULL
, CFSTR("Leaf %d/%d\n"), node
->numBytes
, node
->info
.leaf
.capacityInBytes
);
450 CFStringAppendFormat(str
, NULL
, CFSTR("Node %d\n"), node
->numBytes
);
451 for (cnt
= 0; cnt
< 3; cnt
++) if (node
->info
.notLeaf
.child
[cnt
]) __CFStorageDescribeNode(node
->info
.notLeaf
.child
[cnt
], str
, level
+1);
455 static CFIndex
__CFStorageGetNodeCapacity(CFStorageNode
*node
) {
457 if (node
->isLeaf
) return node
->info
.leaf
.capacityInBytes
;
458 return __CFStorageGetNodeCapacity(node
->info
.notLeaf
.child
[0]) + __CFStorageGetNodeCapacity(node
->info
.notLeaf
.child
[1]) + __CFStorageGetNodeCapacity(node
->info
.notLeaf
.child
[2]);
461 CFIndex
__CFStorageGetCapacity(CFStorageRef storage
) {
462 return __CFStorageGetNodeCapacity(&storage
->rootNode
) / storage
->valueSize
;
465 CFIndex
__CFStorageGetValueSize(CFStorageRef storage
) {
466 return storage
->valueSize
;
469 static CFStringRef
__CFStorageCopyDescription(CFTypeRef cf
) {
470 CFStorageRef storage
= (CFStorageRef
)cf
;
471 CFMutableStringRef result
;
472 CFAllocatorRef allocator
= CFGetAllocator(storage
);
473 result
= CFStringCreateMutable(allocator
, 0);
474 CFStringAppendFormat(result
, NULL
, CFSTR("<CFStorage %p [%p]>[count = %u, capacity = %u]\n"), storage
, allocator
, __CFStorageGetCount(storage
), __CFStorageGetCapacity(storage
));
475 __CFStorageDescribeNode(&storage
->rootNode
, result
, 0);
479 static void __CFStorageDeallocate(CFTypeRef cf
) {
480 CFStorageRef storage
= (CFStorageRef
)cf
;
481 CFAllocatorRef allocator
= CFGetAllocator(storage
);
482 if (CF_IS_COLLECTABLE_ALLOCATOR(allocator
)) return; // XXX_PCB GC will take care of us.
483 __CFStorageNodeDealloc(allocator
, &storage
->rootNode
, false);
486 static CFTypeID __kCFStorageTypeID
= _kCFRuntimeNotATypeID
;
488 static const CFRuntimeClass __CFStorageClass
= {
489 _kCFRuntimeScannedObject
,
493 __CFStorageDeallocate
,
494 (void *)__CFStorageEqual
,
497 __CFStorageCopyDescription
500 __private_extern__
void __CFStorageInitialize(void) {
501 __kCFStorageTypeID
= _CFRuntimeRegisterClass(&__CFStorageClass
);
506 CFStorageRef
CFStorageCreate(CFAllocatorRef allocator
, CFIndex valueSize
) {
507 CFStorageRef storage
;
508 CFIndex size
= sizeof(struct __CFStorage
) - sizeof(CFRuntimeBase
);
509 storage
= (CFStorageRef
)_CFRuntimeCreateInstance(allocator
, __kCFStorageTypeID
, size
, NULL
);
510 if (NULL
== storage
) {
513 storage
->valueSize
= valueSize
;
514 storage
->cachedRange
.location
= 0;
515 storage
->cachedRange
.length
= 0;
516 storage
->cachedNode
= NULL
;
517 storage
->cachedNodeMemory
= NULL
;
518 storage
->maxLeafCapacity
= __CFStorageMaxLeafCapacity
;
519 if (valueSize
&& ((storage
->maxLeafCapacity
% valueSize
) != 0)) {
520 storage
->maxLeafCapacity
= (storage
->maxLeafCapacity
/ valueSize
) * valueSize
; // Make it fit perfectly (3406853)
522 memset(&(storage
->rootNode
), 0, sizeof(CFStorageNode
));
523 storage
->rootNode
.isLeaf
= true;
524 storage
->nodeHint
= AUTO_MEMORY_SCANNED
;
525 if (__CFOASafe
) __CFSetLastAllocationEventName(storage
, "CFStorage");
529 CFTypeID
CFStorageGetTypeID(void) {
530 return __kCFStorageTypeID
;
533 CFIndex
CFStorageGetCount(CFStorageRef storage
) {
534 return __CFStorageGetCount(storage
);
537 /* Returns pointer to the specified value
538 index and validConsecutiveValueRange are in terms of values
540 void *CFStorageGetValueAtIndex(CFStorageRef storage
, CFIndex idx
, CFRange
*validConsecutiveValueRange
) {
542 return __CFStorageGetValueAtIndex(storage
, idx
, validConsecutiveValueRange
? validConsecutiveValueRange
: &range
);
545 /* Makes space for range.length values at location range.location
546 This function deepens the tree if necessary...
548 void CFStorageInsertValues(CFStorageRef storage
, CFRange range
) {
549 CFIndex numBytesToInsert
= range
.length
* storage
->valueSize
;
550 CFIndex byteNum
= range
.location
* storage
->valueSize
;
551 while (numBytesToInsert
> 0) {
552 CFStorageNode
*newNode
;
553 CFAllocatorRef allocator
= CFGetAllocator(storage
);
554 CFIndex insertThisTime
= numBytesToInsert
;
555 if (insertThisTime
> storage
->maxLeafCapacity
) {
556 insertThisTime
= (storage
->maxLeafCapacity
/ storage
->valueSize
) * storage
->valueSize
;
558 newNode
= __CFStorageInsert(allocator
, storage
, &storage
->rootNode
, byteNum
, insertThisTime
, byteNum
);
560 CFStorageNode
*tempRootNode
= __CFStorageCreateNode(allocator
, false, 0); // Will copy the (static) rootNode over to this
561 CF_WRITE_BARRIER_MEMMOVE(tempRootNode
, &storage
->rootNode
, sizeof(CFStorageNode
));
562 storage
->rootNode
.isLeaf
= false;
563 CF_WRITE_BARRIER_BASE_ASSIGN(allocator
, storage
, storage
->rootNode
.info
.notLeaf
.child
[0], tempRootNode
);
564 CF_WRITE_BARRIER_BASE_ASSIGN(allocator
, storage
, storage
->rootNode
.info
.notLeaf
.child
[1], newNode
);
565 storage
->rootNode
.info
.notLeaf
.child
[2] = NULL
;
566 storage
->rootNode
.numBytes
= tempRootNode
->numBytes
+ newNode
->numBytes
;
567 if (storage
->cachedNode
== &(storage
->rootNode
))
568 CF_WRITE_BARRIER_BASE_ASSIGN(allocator
, storage
, storage
->cachedNode
, tempRootNode
); // The cache should follow the node
570 numBytesToInsert
-= insertThisTime
;
571 byteNum
+= insertThisTime
;
575 /* Deletes the values in the specified range
576 This function gets rid of levels if necessary...
578 void CFStorageDeleteValues(CFStorageRef storage
, CFRange range
) {
579 CFAllocatorRef allocator
= CFGetAllocator(storage
);
580 range
.location
*= storage
->valueSize
;
581 range
.length
*= storage
->valueSize
;
582 __CFStorageDelete(allocator
, storage
, &storage
->rootNode
, range
, true);
583 while (__CFStorageGetNumChildren(&storage
->rootNode
) == 1) {
584 CFStorageNode
*child
= storage
->rootNode
.info
.notLeaf
.child
[0]; // The single child
585 CF_WRITE_BARRIER_MEMMOVE(&storage
->rootNode
, child
, sizeof(CFStorageNode
));
586 _CFAllocatorDeallocateGC(allocator
, child
);
588 if (__CFStorageGetNumChildren(&storage
->rootNode
) == 0 && !storage
->rootNode
.isLeaf
) {
589 storage
->rootNode
.isLeaf
= true;
590 storage
->rootNode
.info
.leaf
.capacityInBytes
= 0;
591 storage
->rootNode
.info
.leaf
.memory
= NULL
;
593 // ??? Need to update the cache
594 storage
->cachedRange
= CFRangeMake(0, 0);
597 void CFStorageGetValues(CFStorageRef storage
, CFRange range
, void *values
) {
598 while (range
.length
> 0) {
600 void *storagePtr
= __CFStorageGetValueAtIndex(storage
, range
.location
, &leafRange
);
601 CFIndex cntThisTime
= range
.length
;
602 if (cntThisTime
> leafRange
.length
- (range
.location
- leafRange
.location
)) cntThisTime
= leafRange
.length
- (range
.location
- leafRange
.location
);
603 COPYMEM(storagePtr
, values
, cntThisTime
* storage
->valueSize
);
604 ((uint8_t *)values
) += cntThisTime
* storage
->valueSize
;
605 range
.location
+= cntThisTime
;
606 range
.length
-= cntThisTime
;
610 void CFStorageApplyFunction(CFStorageRef storage
, CFRange range
, CFStorageApplierFunction applier
, void *context
) {
611 while (0 < range
.length
) {
613 const void *storagePtr
;
615 storagePtr
= CFStorageGetValueAtIndex(storage
, range
.location
, &leafRange
);
616 cnt
= __CFMin(range
.length
, leafRange
.location
+ leafRange
.length
- range
.location
);
617 for (idx
= 0; idx
< cnt
; idx
++) {
618 applier(storagePtr
, context
);
619 storagePtr
= (const char *)storagePtr
+ storage
->valueSize
;
622 range
.location
+= cnt
;
626 void CFStorageReplaceValues(CFStorageRef storage
, CFRange range
, const void *values
) {
627 while (range
.length
> 0) {
629 void *storagePtr
= __CFStorageGetValueAtIndex(storage
, range
.location
, &leafRange
);
630 CFIndex cntThisTime
= range
.length
;
631 if (cntThisTime
> leafRange
.length
- (range
.location
- leafRange
.location
)) cntThisTime
= leafRange
.length
- (range
.location
- leafRange
.location
);
632 COPYMEM(values
, storagePtr
, cntThisTime
* storage
->valueSize
);
633 ((const uint8_t *)values
) += cntThisTime
* storage
->valueSize
;
634 range
.location
+= cntThisTime
;
635 range
.length
-= cntThisTime
;
639 /* Used by CFArray.c */
641 static void __CFStorageNodeSetLayoutType(CFStorageNode
*node
, auto_zone_t
*zone
, auto_memory_type_t type
) {
643 auto_zone_set_layout_type(zone
, node
->info
.leaf
.memory
, type
);
645 CFStorageNode
**children
= node
->info
.notLeaf
.child
;
646 if (children
[0]) __CFStorageNodeSetLayoutType(children
[0], zone
, type
);
647 if (children
[1]) __CFStorageNodeSetLayoutType(children
[1], zone
, type
);
648 if (children
[2]) __CFStorageNodeSetLayoutType(children
[2], zone
, type
);
652 __private_extern__
void _CFStorageSetWeak(CFStorageRef storage
) {
653 storage
->nodeHint
= AUTO_MEMORY_UNSCANNED
;
654 __CFStorageNodeSetLayoutType(&storage
->rootNode
, __CFCollectableZone
, storage
->nodeHint
);