]> git.saurik.com Git - apple/cf.git/blame - CFStorage.c
CF-476.19.tar.gz
[apple/cf.git] / CFStorage.c
CommitLineData
9ce05555 1/*
bd5b749c 2 * Copyright (c) 2008 Apple Inc. All rights reserved.
9ce05555
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
9ce05555
A
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/* CFStorage.c
bd5b749c 24 Copyright 1999-2007, Apple, Inc. All rights reserved.
9ce05555
A
25 Responsibility: Ali Ozer
26*/
27
28/*
292-3 tree storing arbitrary sized values.
bd5b749c 30
9ce05555 31??? Currently elementSize cannot be greater than storage->maxLeafCapacity, which is less than or equal to __CFStorageMaxLeafCapacity
bd5b749c
A
32
33CFStorage is thread-safe for multiple readers, but not thread safe for simultaneous reading and writing.
9ce05555
A
34*/
35
36#include "CFStorage.h"
37#include "CFInternal.h"
38
bd5b749c
A
39#if !defined(PAGE_SIZE)
40#define PAGE_SIZE 4096
9ce05555
A
41#endif
42
bd5b749c
A
43// Above 15K, malloc switches (??? or used to in early Leopard) to using vm allocates for blocks; it seems best to avoid that.
44// Also, tests with StorageTimer.c done in 4/07 indicate that 4096 * 3 is better than smaller or larger node sizes.
45#define __CFStorageMaxLeafCapacity (4096 * 3)
46
47// If the max length of a node is less than can fit in a half-word (half of a long), we can get away without ever caching the high half-word the cachedRange
48#if __LP64__
49#if __CFStorageMaxLeafCapacity > 0xFFFFFFFFULL
50#define POSSIBLE_TO_HAVE_LENGTH_MORE_THAN_HALFWORD 1
51#endif
52#else
53#if __CFStorageMaxLeafCapacity > 0xFFFFUL
54#define POSSIBLE_TO_HAVE_LENGTH_MORE_THAN_HALFWORD 1
55#endif
56#endif
9ce05555 57
d8925383 58#define COPYMEM(src,dst,n) CF_WRITE_BARRIER_MEMMOVE((dst), (src), (n))
bd5b749c 59#define PAGE_LIMIT ((CFIndex)PAGE_SIZE / 2)
9ce05555 60
bd5b749c
A
61CF_INLINE int32_t roundToPage(int32_t num) {
62 return (num + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
9ce05555
A
63}
64
bd5b749c
A
65
66
67/* Each node in the storage. isLeaf determines whether the node is a leaf node or a node inside the tree. If latter, number of children are determined by the number of non-NULL entries in child[]. (NULL entries are at the end.)
68*/
9ce05555
A
69typedef struct __CFStorageNode {
70 CFIndex numBytes; /* Number of actual bytes in this node and all its children */
71 bool isLeaf;
72 union {
73 struct {
d8925383 74 CFIndex capacityInBytes; // capacityInBytes is capacity of memory; this is either 0, or >= numBytes
9ce05555
A
75 uint8_t *memory;
76 } leaf;
77 struct {
78 struct __CFStorageNode *child[3];
79 } notLeaf;
80 } info;
81} CFStorageNode;
82
bd5b749c
A
83/* This is the struct used to store the cache in the CFStorage; it enables us to make the cache thread-safe for multiple readers (which update the cache). The values in this cache store half the desired value in the top half, and the genCount of the writer in the low half. This cache is consistent only if all of the genCounts are the same. Note that the cache does not provide thread-safety for readers in the presence of a writer.
84
85The cached range (location, length) is in terms of values; if the cached range is not (0,0), then the cached node needs to be non-NULL and pointing at a leaf node.
86*/
87typedef struct {
88 unsigned long locationHi, locationLo; // cachedRange.location
89#if POSSIBLE_TO_HAVE_LENGTH_MORE_THAN_HALFWORD
90 unsigned long lengthHi;
91#endif
92 unsigned long lengthLo; // cachedRange.length; note that we actually do not bother with lengthHi if __CFStorageMaxLeafCapacity is less than half-word
93 unsigned long cachedNodeHi, cachedNodeLo; // cachedNode
94} CFStorageAccessCacheParts;
95
96/* The CFStorage object.
97*/
9ce05555
A
98struct __CFStorage {
99 CFRuntimeBase base;
100 CFIndex valueSize;
bd5b749c
A
101 CFSpinLock_t cacheReaderMemoryAllocationLock;
102 int cacheGenerationCount;
103 CFStorageAccessCacheParts cacheParts;
104 CFIndex maxLeafCapacity; // In terms of bytes
9ce05555 105 CFStorageNode rootNode;
bd5b749c 106 CFOptionFlags nodeHint; // __kCFAllocatorGCScannedMemory or 0.
9ce05555
A
107};
108
bd5b749c
A
109
110
111
112/* Allocates the memory and initializes the capacity in a leaf. __CFStorageAllocLeafNodeMemory() is the entry point; __CFStorageAllocLeafNodeMemoryAux is called if actual reallocation is needed. __CFStorageAllocLeafNodeMemoryAux() locks not for mutations (mutations are not thread-safe in general), but for lazy allocation of storage during reading.
9ce05555 113*/
d8925383 114static void __CFStorageAllocLeafNodeMemoryAux(CFAllocatorRef allocator, CFStorageRef storage, CFStorageNode *node, CFIndex cap) {
bd5b749c
A
115 __CFSpinLock(&(storage->cacheReaderMemoryAllocationLock));
116 CF_WRITE_BARRIER_ASSIGN(allocator, node->info.leaf.memory, _CFAllocatorReallocateGC(allocator, node->info.leaf.memory, cap, storage->nodeHint)); // This will free...
d8925383
A
117 if (__CFOASafe) __CFSetLastAllocationEventName(node->info.leaf.memory, "CFStorage (node bytes)");
118 node->info.leaf.capacityInBytes = cap;
bd5b749c 119 __CFSpinUnlock(&(storage->cacheReaderMemoryAllocationLock));
9ce05555
A
120}
121
d8925383 122CF_INLINE void __CFStorageAllocLeafNodeMemory(CFAllocatorRef allocator, CFStorageRef storage, CFStorageNode *node, CFIndex cap, bool compact) {
9ce05555
A
123 if (cap > PAGE_LIMIT) {
124 cap = roundToPage(cap);
125 if (cap > storage->maxLeafCapacity) cap = storage->maxLeafCapacity;
126 } else {
127 cap = (((cap + 63) / 64) * 64);
128 }
d8925383
A
129 if (compact ? (cap != node->info.leaf.capacityInBytes) : (cap > node->info.leaf.capacityInBytes)) __CFStorageAllocLeafNodeMemoryAux(allocator, storage, node, cap);
130}
131
bd5b749c
A
132
133
134
135#if __LP64__
136#define genCountMask 0x00000000FFFFFFFFUL
137#define dataMask 0xFFFFFFFF00000000UL
138#define shiftLowWordBy 32
139#else
140#define genCountMask 0x0000FFFFUL
141#define dataMask 0xFFFF0000UL
142#define shiftLowWordBy 16
143#endif
144
145/* Sets the cache to point at the specified node. loc and len are in terms of values, not bytes. To clear the cache set these two to 0.
d8925383
A
146 At least one of node or memory should be non-NULL. memory is consulted first when using the cache.
147*/
bd5b749c
A
148CF_INLINE void __CFStorageSetCache(CFStorageRef storage, CFStorageNode *node, CFIndex loc, CFIndex len) {
149 unsigned int genCount = ((unsigned int)OSAtomicIncrement32(&storage->cacheGenerationCount)) & genCountMask;
150 CFStorageAccessCacheParts cacheParts;
151 cacheParts.locationHi = (loc & dataMask) | genCount;
152 cacheParts.locationLo = (loc << shiftLowWordBy) | genCount;
153#if POSSIBLE_TO_HAVE_LENGTH_MORE_THAN_HALFWORD
154 cacheParts.lengthHi = (len & dataMask) | genCount;
155#endif
156 cacheParts.lengthLo = (len << shiftLowWordBy) | genCount;
157 cacheParts.cachedNodeHi = ((unsigned long)node & dataMask) | genCount;
158 cacheParts.cachedNodeLo = ((unsigned long)node << shiftLowWordBy) | genCount;
159 storage->cacheParts = cacheParts;
d8925383
A
160}
161
bd5b749c 162/* Thread-safely get the cached range and node info.
d8925383 163*/
bd5b749c
A
164CF_INLINE Boolean __CFStorageGetCache(CFStorageRef storage, CFRange *cachedRangePtr, CFStorageNode **cachedNodePtr) {
165 CFStorageAccessCacheParts cacheParts = storage->cacheParts;
166
167 unsigned int genCount = cacheParts.locationHi & genCountMask;
168
169 // Check to make sure the genCounts of all the items are the same; if not, the cache was inconsistent
170 if ((cacheParts.locationLo & genCountMask) == genCount &&
171#if POSSIBLE_TO_HAVE_LENGTH_MORE_THAN_HALFWORD
172 (cacheParts.lengthHi & genCountMask) == genCount &&
173#endif
174 (cacheParts.lengthLo & genCountMask) == genCount &&
175 (cacheParts.cachedNodeHi & genCountMask) == genCount &&
176 (cacheParts.cachedNodeLo & genCountMask) == genCount) {
177
178 *cachedNodePtr = (CFStorageNode *)((cacheParts.cachedNodeHi & dataMask) | ((cacheParts.cachedNodeLo & dataMask) >> shiftLowWordBy));
179 cachedRangePtr->location = (cacheParts.locationHi & dataMask) | ((cacheParts.locationLo & dataMask) >> shiftLowWordBy);
180 cachedRangePtr->length = (cacheParts.lengthLo & dataMask) >> shiftLowWordBy;
181#if POSSIBLE_TO_HAVE_LENGTH_MORE_THAN_HALFWORD
182 cachedRangePtr->length |= (cacheParts.lengthHi & dataMask);
183#endif
184
185 return true;
9ce05555 186 }
bd5b749c
A
187 return false;
188}
189
190/* Gets the location for the specified absolute loc from the cached info.
191 Returns NULL if the location is not in the cache.
192*/
193CF_INLINE uint8_t *__CFStorageGetFromCache(CFStorageRef storage, CFIndex loc, CFRange *validConsecutiveValueRange) {
194 CFRange cachedRange = {0, 0};
195 CFStorageNode *cachedNode = 0;
196
197 // If we can't get values from the cache, return NULL
198 if (!__CFStorageGetCache(storage, &cachedRange, &cachedNode)) return NULL;
199
200 // Check to see if the index is in the cache
201 if (loc < cachedRange.location || loc >= cachedRange.location + cachedRange.length) return NULL;
202
203 // If the cached node has no memory, return here; it will be allocated as a result of the non-cached lookup.
204 if (!cachedNode->info.leaf.memory) return NULL;
205
206 // The cache has consistent values, and in fact, the values we're looking for!
207 uint8_t *result = cachedNode->info.leaf.memory + (loc - cachedRange.location) * storage->valueSize;
208 *validConsecutiveValueRange = cachedRange;
209
210 return result;
9ce05555
A
211}
212
bd5b749c
A
213
214
215
d8925383
A
216/* Returns the number of the child containing the desired value and the relative index of the value in that child.
217 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
218 relativeByteNum (not optional, for performance reasons) returns the relative byte number of the specified byte in the child.
219 Don't call with leaf nodes!
220*/
221CF_INLINE void __CFStorageFindChild(CFStorageNode *node, CFIndex byteNum, bool forInsertion, CFIndex *childNum, CFIndex *relativeByteNum) {
222 if (forInsertion) byteNum--; /* If for insertion, we do <= checks, not <, so this accomplishes the same thing */
223 if (byteNum < node->info.notLeaf.child[0]->numBytes) *childNum = 0;
224 else {
225 byteNum -= node->info.notLeaf.child[0]->numBytes;
226 if (byteNum < node->info.notLeaf.child[1]->numBytes) *childNum = 1;
227 else {
228 byteNum -= node->info.notLeaf.child[1]->numBytes;
229 *childNum = 2;
230 }
9ce05555 231 }
d8925383
A
232 if (forInsertion) byteNum++;
233 *relativeByteNum = byteNum;
9ce05555
A
234}
235
236/* Finds the location where the specified byte is stored. If validConsecutiveByteRange is not NULL, returns
237 the range of bytes that are consecutive with this one.
238 !!! Assumes the byteNum is within the range of this node.
239*/
bd5b749c 240static void *__CFStorageFindByte(CFStorageRef storage, CFStorageNode *node, CFIndex byteNum, CFStorageNode **resultNode, CFRange *validConsecutiveByteRange) {
9ce05555
A
241 if (node->isLeaf) {
242 if (validConsecutiveByteRange) *validConsecutiveByteRange = CFRangeMake(0, node->numBytes);
d8925383 243 __CFStorageAllocLeafNodeMemory(CFGetAllocator(storage), storage, node, node->numBytes, false);
bd5b749c 244 if (resultNode) *resultNode = node;
9ce05555
A
245 return node->info.leaf.memory + byteNum;
246 } else {
247 void *result;
248 CFIndex childNum;
249 CFIndex relativeByteNum;
250 __CFStorageFindChild(node, byteNum, false, &childNum, &relativeByteNum);
bd5b749c 251 result = __CFStorageFindByte(storage, node->info.notLeaf.child[childNum], relativeByteNum, resultNode, validConsecutiveByteRange);
9ce05555
A
252 if (validConsecutiveByteRange) {
253 if (childNum > 0) validConsecutiveByteRange->location += node->info.notLeaf.child[0]->numBytes;
254 if (childNum > 1) validConsecutiveByteRange->location += node->info.notLeaf.child[1]->numBytes;
255 }
256 return result;
257 }
258}
259
d8925383
A
260/* Guts of CFStorageGetValueAtIndex(); note that validConsecutiveValueRange is not optional.
261 Consults and updates cache.
262*/
263CF_INLINE void *__CFStorageGetValueAtIndex(CFStorageRef storage, CFIndex idx, CFRange *validConsecutiveValueRange) {
264 uint8_t *result;
bd5b749c
A
265 if (!(result = __CFStorageGetFromCache(storage, idx, validConsecutiveValueRange))) {
266 CFRange rangeInBytes;
267 CFStorageNode *resultNode;
268 result = (uint8_t *)__CFStorageFindByte(storage, &storage->rootNode, idx * storage->valueSize, &resultNode, &rangeInBytes);
269 CFRange rangeInValues = CFRangeMake(rangeInBytes.location / storage->valueSize, rangeInBytes.length / storage->valueSize);
270 __CFStorageSetCache(storage, resultNode, rangeInValues.location, rangeInValues.length);
271 *validConsecutiveValueRange = rangeInValues;
d8925383 272 }
d8925383
A
273 return result;
274}
275
276static CFStorageNode *__CFStorageCreateNode(CFAllocatorRef allocator, bool isLeaf, CFIndex numBytes) {
bd5b749c 277 CFStorageNode *newNode = (CFStorageNode *)_CFAllocatorAllocateGC(allocator, sizeof(CFStorageNode), __kCFAllocatorGCScannedMemory);
d8925383
A
278 if (__CFOASafe) __CFSetLastAllocationEventName(newNode, "CFStorage (node)");
279 newNode->isLeaf = isLeaf;
280 newNode->numBytes = numBytes;
281 if (isLeaf) {
282 newNode->info.leaf.capacityInBytes = 0;
283 newNode->info.leaf.memory = NULL;
284 } else {
285 newNode->info.notLeaf.child[0] = newNode->info.notLeaf.child[1] = newNode->info.notLeaf.child[2] = NULL;
286 }
287 return newNode;
288}
289
290static void __CFStorageNodeDealloc(CFAllocatorRef allocator, CFStorageNode *node, bool freeNodeItself) {
291 if (node->isLeaf) {
292 _CFAllocatorDeallocateGC(allocator, node->info.leaf.memory);
293 } else {
294 int cnt;
295 for (cnt = 0; cnt < 3; cnt++) if (node->info.notLeaf.child[cnt]) __CFStorageNodeDealloc(allocator, node->info.notLeaf.child[cnt], true);
296 }
297 if (freeNodeItself) _CFAllocatorDeallocateGC(allocator, node);
298}
299
9ce05555
A
300static CFIndex __CFStorageGetNumChildren(CFStorageNode *node) {
301 if (!node || node->isLeaf) return 0;
302 if (node->info.notLeaf.child[2]) return 3;
303 if (node->info.notLeaf.child[1]) return 2;
304 if (node->info.notLeaf.child[0]) return 1;
305 return 0;
306}
307
308/* The boolean compact indicates whether leaf nodes that get smaller should be realloced.
309*/
d8925383 310static void __CFStorageDelete(CFAllocatorRef allocator, CFStorageRef storage, CFStorageNode *node, CFRange range, bool compact) {
9ce05555
A
311 if (node->isLeaf) {
312 node->numBytes -= range.length;
313 // If this node had memory allocated, readjust the bytes...
314 if (node->info.leaf.memory) {
315 COPYMEM(node->info.leaf.memory + range.location + range.length, node->info.leaf.memory + range.location, node->numBytes - range.location);
d8925383 316 if (compact) __CFStorageAllocLeafNodeMemory(allocator, storage, node, node->numBytes, true);
9ce05555
A
317 }
318 } else {
319 bool childrenAreLeaves = node->info.notLeaf.child[0]->isLeaf;
320 node->numBytes -= range.length;
321 while (range.length > 0) {
322 CFRange rangeToDelete;
323 CFIndex relativeByteNum;
324 CFIndex childNum;
325 __CFStorageFindChild(node, range.location + range.length, true, &childNum, &relativeByteNum);
326 if (range.length > relativeByteNum) {
327 rangeToDelete.length = relativeByteNum;
328 rangeToDelete.location = 0;
329 } else {
330 rangeToDelete.length = range.length;
331 rangeToDelete.location = relativeByteNum - range.length;
332 }
d8925383 333 __CFStorageDelete(allocator, storage, node->info.notLeaf.child[childNum], rangeToDelete, compact);
9ce05555
A
334 if (node->info.notLeaf.child[childNum]->numBytes == 0) { // Delete empty node and compact
335 int cnt;
d8925383 336 _CFAllocatorDeallocateGC(allocator, node->info.notLeaf.child[childNum]);
9ce05555 337 for (cnt = childNum; cnt < 2; cnt++) {
d8925383 338 CF_WRITE_BARRIER_ASSIGN(allocator, node->info.notLeaf.child[cnt], node->info.notLeaf.child[cnt+1]);
9ce05555
A
339 }
340 node->info.notLeaf.child[2] = NULL;
341 }
342 range.length -= rangeToDelete.length;
343 }
344 // At this point the remaining children are packed
345 if (childrenAreLeaves) {
346 // Children are leaves; if their total bytes is smaller than a leaf's worth, collapse into one...
347 if (node->numBytes > 0 && node->numBytes <= storage->maxLeafCapacity) {
d8925383 348 __CFStorageAllocLeafNodeMemory(allocator, storage, node->info.notLeaf.child[0], node->numBytes, false);
9ce05555
A
349 if (node->info.notLeaf.child[1] && node->info.notLeaf.child[1]->numBytes) {
350 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);
351 if (node->info.notLeaf.child[2] && node->info.notLeaf.child[2]->numBytes) {
352 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);
d8925383 353 __CFStorageNodeDealloc(allocator, node->info.notLeaf.child[2], true);
9ce05555
A
354 node->info.notLeaf.child[2] = NULL;
355 }
d8925383 356 __CFStorageNodeDealloc(allocator, node->info.notLeaf.child[1], true);
9ce05555
A
357 node->info.notLeaf.child[1] = NULL;
358 }
359 node->info.notLeaf.child[0]->numBytes = node->numBytes;
360 }
361 } else {
362 // Children are not leaves; combine their children to assure each node has 2 or 3 children...
363 // (Could try to bypass all this by noting up above whether the number of grandchildren changed...)
364 CFStorageNode *gChildren[9];
365 CFIndex cCnt, gCnt, cnt;
366 CFIndex totalG = 0; // Total number of grandchildren
367 for (cCnt = 0; cCnt < 3; cCnt++) {
368 CFStorageNode *child = node->info.notLeaf.child[cCnt];
369 if (child) {
370 for (gCnt = 0; gCnt < 3; gCnt++) if (child->info.notLeaf.child[gCnt]) {
371 gChildren[totalG++] = child->info.notLeaf.child[gCnt];
372 child->info.notLeaf.child[gCnt] = NULL;
373 }
374 child->numBytes = 0;
375 }
376 }
377 gCnt = 0; // Total number of grandchildren placed
378 for (cCnt = 0; cCnt < 3; cCnt++) {
379 // These tables indicate how many children each child should have, given the total number of grandchildren (last child gets remainder)
380 static const unsigned char forChild0[10] = {0, 1, 2, 3, 2, 3, 3, 3, 3, 3};
381 static const unsigned char forChild1[10] = {0, 0, 0, 0, 2, 2, 3, 2, 3, 3};
382 // sCnt is the number of grandchildren to be placed into child cCnt
383 // Depending on child number, pick the right number
384 CFIndex sCnt = (cCnt == 0) ? forChild0[totalG] : ((cCnt == 1) ? forChild1[totalG] : totalG);
385 // Assure we have that many grandchildren...
386 if (sCnt > totalG - gCnt) sCnt = totalG - gCnt;
387 if (sCnt) {
d8925383
A
388 if (!node->info.notLeaf.child[cCnt]) {
389 CFStorageNode *newNode = __CFStorageCreateNode(allocator, false, 0);
390 CF_WRITE_BARRIER_ASSIGN(allocator, node->info.notLeaf.child[cCnt], newNode);
391 }
9ce05555
A
392 for (cnt = 0; cnt < sCnt; cnt++) {
393 node->info.notLeaf.child[cCnt]->numBytes += gChildren[gCnt]->numBytes;
d8925383 394 CF_WRITE_BARRIER_ASSIGN(allocator, node->info.notLeaf.child[cCnt]->info.notLeaf.child[cnt], gChildren[gCnt++]);
9ce05555
A
395 }
396 } else {
397 if (node->info.notLeaf.child[cCnt]) {
d8925383 398 _CFAllocatorDeallocateGC(allocator, node->info.notLeaf.child[cCnt]);
9ce05555
A
399 node->info.notLeaf.child[cCnt] = NULL;
400 }
401 }
402 }
403 }
404 }
405}
406
d8925383 407
9ce05555
A
408/* Returns NULL or additional node to come after this node
409 Assumption: size is never > storage->maxLeafCapacity
410*/
d8925383 411static CFStorageNode *__CFStorageInsert(CFAllocatorRef allocator, CFStorageRef storage, CFStorageNode *node, CFIndex byteNum, CFIndex size, CFIndex absoluteByteNum) {
9ce05555
A
412 if (node->isLeaf) {
413 if (size + node->numBytes > storage->maxLeafCapacity) { // Need to create more child nodes
414 if (byteNum == node->numBytes) { // Inserting at end; easy...
d8925383 415 CFStorageNode *newNode = __CFStorageCreateNode(allocator, true, size);
bd5b749c 416 __CFStorageSetCache(storage, newNode, absoluteByteNum / storage->valueSize, size / storage->valueSize);
9ce05555
A
417 return newNode;
418 } else if (byteNum == 0) { // Inserting at front; also easy, but we need to swap node and newNode
d8925383
A
419 CFStorageNode *newNode = __CFStorageCreateNode(allocator, true, 0);
420 CF_WRITE_BARRIER_MEMMOVE(newNode, node, sizeof(CFStorageNode));
9ce05555
A
421 node->isLeaf = true;
422 node->numBytes = size;
423 node->info.leaf.capacityInBytes = 0;
424 node->info.leaf.memory = NULL;
bd5b749c 425 __CFStorageSetCache(storage, node, absoluteByteNum / storage->valueSize, size / storage->valueSize);
9ce05555
A
426 return newNode;
427 } else if (byteNum + size <= storage->maxLeafCapacity) { // Inserting at middle; inserted region will fit into existing child
428 // Create new node to hold the overflow
d8925383 429 CFStorageNode *newNode = __CFStorageCreateNode(allocator, true, node->numBytes - byteNum);
9ce05555 430 if (node->info.leaf.memory) { // We allocate memory lazily...
d8925383 431 __CFStorageAllocLeafNodeMemory(allocator, storage, newNode, node->numBytes - byteNum, false);
9ce05555 432 COPYMEM(node->info.leaf.memory + byteNum, newNode->info.leaf.memory, node->numBytes - byteNum);
d8925383 433 __CFStorageAllocLeafNodeMemory(allocator, storage, node, byteNum + size, false);
9ce05555 434 }
d8925383 435 node->numBytes = byteNum + size;
bd5b749c 436 __CFStorageSetCache(storage, node, (absoluteByteNum - byteNum) / storage->valueSize, node->numBytes / storage->valueSize);
9ce05555
A
437 return newNode;
438 } else { // Inserting some of new into one node, rest into another; remember that the assumption is size <= storage->maxLeafCapacity
d8925383 439 CFStorageNode *newNode = __CFStorageCreateNode(allocator, true, node->numBytes + size - storage->maxLeafCapacity); // New stuff
9ce05555 440 if (node->info.leaf.memory) { // We allocate memory lazily...
d8925383 441 __CFStorageAllocLeafNodeMemory(allocator, storage, newNode, node->numBytes + size - storage->maxLeafCapacity, false);
9ce05555 442 COPYMEM(node->info.leaf.memory + byteNum, newNode->info.leaf.memory + byteNum + size - storage->maxLeafCapacity, node->numBytes - byteNum);
d8925383 443 __CFStorageAllocLeafNodeMemory(allocator, storage, node, storage->maxLeafCapacity, false);
9ce05555
A
444 }
445 node->numBytes = storage->maxLeafCapacity;
bd5b749c 446 __CFStorageSetCache(storage, node, (absoluteByteNum - byteNum) / storage->valueSize, node->numBytes / storage->valueSize);
9ce05555
A
447 return newNode;
448 }
449 } else { // No need to create new nodes!
450 if (node->info.leaf.memory) {
d8925383 451 __CFStorageAllocLeafNodeMemory(allocator, storage, node, node->numBytes + size, false);
9ce05555
A
452 COPYMEM(node->info.leaf.memory + byteNum, node->info.leaf.memory + byteNum + size, node->numBytes - byteNum);
453 }
454 node->numBytes += size;
bd5b749c 455 __CFStorageSetCache(storage, node, (absoluteByteNum - byteNum) / storage->valueSize, node->numBytes / storage->valueSize);
9ce05555
A
456 return NULL;
457 }
458 } else {
459 CFIndex relativeByteNum;
460 CFIndex childNum;
461 CFStorageNode *newNode;
462 __CFStorageFindChild(node, byteNum, true, &childNum, &relativeByteNum);
d8925383 463 newNode = __CFStorageInsert(allocator, storage, node->info.notLeaf.child[childNum], relativeByteNum, size, absoluteByteNum);
9ce05555
A
464 if (newNode) {
465 if (node->info.notLeaf.child[2] == NULL) { // There's an empty slot for the new node, cool
d8925383
A
466 if (childNum == 0) CF_WRITE_BARRIER_ASSIGN(allocator, node->info.notLeaf.child[2], node->info.notLeaf.child[1]); // Make room
467 CF_WRITE_BARRIER_ASSIGN(allocator, node->info.notLeaf.child[childNum + 1], newNode);
9ce05555
A
468 node->numBytes += size;
469 return NULL;
470 } else {
d8925383 471 CFStorageNode *anotherNode = __CFStorageCreateNode(allocator, false, 0); // Create another node
9ce05555 472 if (childNum == 0) { // Last two children go to new node
d8925383
A
473 CF_WRITE_BARRIER_ASSIGN(allocator, anotherNode->info.notLeaf.child[0], node->info.notLeaf.child[1]);
474 CF_WRITE_BARRIER_ASSIGN(allocator, anotherNode->info.notLeaf.child[1], node->info.notLeaf.child[2]);
475 CF_WRITE_BARRIER_ASSIGN(allocator, node->info.notLeaf.child[1], newNode);
9ce05555
A
476 node->info.notLeaf.child[2] = NULL;
477 } else if (childNum == 1) { // Last child goes to new node
d8925383
A
478 CF_WRITE_BARRIER_ASSIGN(allocator, anotherNode->info.notLeaf.child[0], newNode);
479 CF_WRITE_BARRIER_ASSIGN(allocator, anotherNode->info.notLeaf.child[1], node->info.notLeaf.child[2]);
9ce05555
A
480 node->info.notLeaf.child[2] = NULL;
481 } else { // New node contains the new comers...
d8925383
A
482 CF_WRITE_BARRIER_ASSIGN(allocator, anotherNode->info.notLeaf.child[0], node->info.notLeaf.child[2]);
483 CF_WRITE_BARRIER_ASSIGN(allocator, anotherNode->info.notLeaf.child[1], newNode);
9ce05555
A
484 node->info.notLeaf.child[2] = NULL;
485 }
486 node->numBytes = node->info.notLeaf.child[0]->numBytes + node->info.notLeaf.child[1]->numBytes;
487 anotherNode->numBytes = anotherNode->info.notLeaf.child[0]->numBytes + anotherNode->info.notLeaf.child[1]->numBytes;
488 return anotherNode;
489 }
490 } else {
491 node->numBytes += size;
492 }
493 }
494 return NULL;
495}
496
497CF_INLINE CFIndex __CFStorageGetCount(CFStorageRef storage) {
498 return storage->rootNode.numBytes / storage->valueSize;
499}
500
bd5b749c 501static Boolean __CFStorageEqual(CFTypeRef cf1, CFTypeRef cf2) {
9ce05555
A
502 CFStorageRef storage1 = (CFStorageRef)cf1;
503 CFStorageRef storage2 = (CFStorageRef)cf2;
504 CFIndex loc, count, valueSize;
505 CFRange range1, range2;
506 uint8_t *ptr1, *ptr2;
507
508 count = __CFStorageGetCount(storage1);
509 if (count != __CFStorageGetCount(storage2)) return false;
510
511 valueSize = __CFStorageGetValueSize(storage1);
512 if (valueSize != __CFStorageGetValueSize(storage2)) return false;
513
514 loc = range1.location = range1.length = range2.location = range2.length = 0;
515 ptr1 = ptr2 = NULL;
516
517 while (loc < count) {
518 CFIndex cntThisTime;
bd5b749c
A
519 if (loc >= range1.location + range1.length) ptr1 = (uint8_t *)CFStorageGetValueAtIndex(storage1, loc, &range1);
520 if (loc >= range2.location + range2.length) ptr2 = (uint8_t *)CFStorageGetValueAtIndex(storage2, loc, &range2);
9ce05555
A
521 cntThisTime = range1.location + range1.length;
522 if (range2.location + range2.length < cntThisTime) cntThisTime = range2.location + range2.length;
523 cntThisTime -= loc;
524 if (memcmp(ptr1, ptr2, valueSize * cntThisTime) != 0) return false;
525 ptr1 += valueSize * cntThisTime;
526 ptr2 += valueSize * cntThisTime;
527 loc += cntThisTime;
528 }
529 return true;
530}
531
532static CFHashCode __CFStorageHash(CFTypeRef cf) {
533 CFStorageRef Storage = (CFStorageRef)cf;
534 return __CFStorageGetCount(Storage);
535}
536
537static void __CFStorageDescribeNode(CFStorageNode *node, CFMutableStringRef str, CFIndex level) {
538 int cnt;
539 for (cnt = 0; cnt < level; cnt++) CFStringAppendCString(str, " ", CFStringGetSystemEncoding());
540
541 if (node->isLeaf) {
542 CFStringAppendFormat(str, NULL, CFSTR("Leaf %d/%d\n"), node->numBytes, node->info.leaf.capacityInBytes);
543 } else {
544 CFStringAppendFormat(str, NULL, CFSTR("Node %d\n"), node->numBytes);
545 for (cnt = 0; cnt < 3; cnt++) if (node->info.notLeaf.child[cnt]) __CFStorageDescribeNode(node->info.notLeaf.child[cnt], str, level+1);
546 }
547}
548
549static CFIndex __CFStorageGetNodeCapacity(CFStorageNode *node) {
550 if (!node) return 0;
551 if (node->isLeaf) return node->info.leaf.capacityInBytes;
552 return __CFStorageGetNodeCapacity(node->info.notLeaf.child[0]) + __CFStorageGetNodeCapacity(node->info.notLeaf.child[1]) + __CFStorageGetNodeCapacity(node->info.notLeaf.child[2]);
553}
554
555CFIndex __CFStorageGetCapacity(CFStorageRef storage) {
556 return __CFStorageGetNodeCapacity(&storage->rootNode) / storage->valueSize;
557}
558
559CFIndex __CFStorageGetValueSize(CFStorageRef storage) {
560 return storage->valueSize;
561}
562
563static CFStringRef __CFStorageCopyDescription(CFTypeRef cf) {
564 CFStorageRef storage = (CFStorageRef)cf;
565 CFMutableStringRef result;
d8925383
A
566 CFAllocatorRef allocator = CFGetAllocator(storage);
567 result = CFStringCreateMutable(allocator, 0);
568 CFStringAppendFormat(result, NULL, CFSTR("<CFStorage %p [%p]>[count = %u, capacity = %u]\n"), storage, allocator, __CFStorageGetCount(storage), __CFStorageGetCapacity(storage));
9ce05555
A
569 __CFStorageDescribeNode(&storage->rootNode, result, 0);
570 return result;
571}
572
9ce05555
A
573static void __CFStorageDeallocate(CFTypeRef cf) {
574 CFStorageRef storage = (CFStorageRef)cf;
575 CFAllocatorRef allocator = CFGetAllocator(storage);
d8925383 576 if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) return; // XXX_PCB GC will take care of us.
9ce05555
A
577 __CFStorageNodeDealloc(allocator, &storage->rootNode, false);
578}
579
580static CFTypeID __kCFStorageTypeID = _kCFRuntimeNotATypeID;
581
582static const CFRuntimeClass __CFStorageClass = {
d8925383 583 _kCFRuntimeScannedObject,
9ce05555
A
584 "CFStorage",
585 NULL, // init
586 NULL, // copy
587 __CFStorageDeallocate,
bd5b749c 588 __CFStorageEqual,
9ce05555
A
589 __CFStorageHash,
590 NULL, //
591 __CFStorageCopyDescription
592};
593
594__private_extern__ void __CFStorageInitialize(void) {
595 __kCFStorageTypeID = _CFRuntimeRegisterClass(&__CFStorageClass);
596}
597
598/*** Public API ***/
599
600CFStorageRef CFStorageCreate(CFAllocatorRef allocator, CFIndex valueSize) {
601 CFStorageRef storage;
602 CFIndex size = sizeof(struct __CFStorage) - sizeof(CFRuntimeBase);
603 storage = (CFStorageRef)_CFRuntimeCreateInstance(allocator, __kCFStorageTypeID, size, NULL);
604 if (NULL == storage) {
605 return NULL;
606 }
607 storage->valueSize = valueSize;
bd5b749c
A
608 CF_SPINLOCK_INIT_FOR_STRUCTS(storage->cacheReaderMemoryAllocationLock);
609 storage->cacheGenerationCount = 0;
610 storage->cacheParts.locationHi = 0;
611 storage->cacheParts.locationLo = 0;
612#if POSSIBLE_TO_HAVE_LENGTH_MORE_THAN_HALFWORD
613 storage->cacheParts.lengthHi = 0;
614#endif
615 storage->cacheParts.lengthLo = 0;
616 storage->cacheParts.cachedNodeHi = 0;
617 storage->cacheParts.cachedNodeLo = 0;
9ce05555
A
618 storage->maxLeafCapacity = __CFStorageMaxLeafCapacity;
619 if (valueSize && ((storage->maxLeafCapacity % valueSize) != 0)) {
620 storage->maxLeafCapacity = (storage->maxLeafCapacity / valueSize) * valueSize; // Make it fit perfectly (3406853)
621 }
622 memset(&(storage->rootNode), 0, sizeof(CFStorageNode));
623 storage->rootNode.isLeaf = true;
bd5b749c 624 storage->nodeHint = __kCFAllocatorGCScannedMemory;
9ce05555
A
625 if (__CFOASafe) __CFSetLastAllocationEventName(storage, "CFStorage");
626 return storage;
627}
628
629CFTypeID CFStorageGetTypeID(void) {
630 return __kCFStorageTypeID;
631}
632
633CFIndex CFStorageGetCount(CFStorageRef storage) {
634 return __CFStorageGetCount(storage);
635}
636
637/* Returns pointer to the specified value
638 index and validConsecutiveValueRange are in terms of values
9ce05555
A
639*/
640void *CFStorageGetValueAtIndex(CFStorageRef storage, CFIndex idx, CFRange *validConsecutiveValueRange) {
d8925383
A
641 CFRange range;
642 return __CFStorageGetValueAtIndex(storage, idx, validConsecutiveValueRange ? validConsecutiveValueRange : &range);
9ce05555
A
643}
644
645/* Makes space for range.length values at location range.location
646 This function deepens the tree if necessary...
647*/
648void CFStorageInsertValues(CFStorageRef storage, CFRange range) {
649 CFIndex numBytesToInsert = range.length * storage->valueSize;
650 CFIndex byteNum = range.location * storage->valueSize;
651 while (numBytesToInsert > 0) {
652 CFStorageNode *newNode;
d8925383 653 CFAllocatorRef allocator = CFGetAllocator(storage);
9ce05555
A
654 CFIndex insertThisTime = numBytesToInsert;
655 if (insertThisTime > storage->maxLeafCapacity) {
656 insertThisTime = (storage->maxLeafCapacity / storage->valueSize) * storage->valueSize;
657 }
d8925383 658 newNode = __CFStorageInsert(allocator, storage, &storage->rootNode, byteNum, insertThisTime, byteNum);
9ce05555 659 if (newNode) {
d8925383
A
660 CFStorageNode *tempRootNode = __CFStorageCreateNode(allocator, false, 0); // Will copy the (static) rootNode over to this
661 CF_WRITE_BARRIER_MEMMOVE(tempRootNode, &storage->rootNode, sizeof(CFStorageNode));
9ce05555 662 storage->rootNode.isLeaf = false;
d8925383
A
663 CF_WRITE_BARRIER_BASE_ASSIGN(allocator, storage, storage->rootNode.info.notLeaf.child[0], tempRootNode);
664 CF_WRITE_BARRIER_BASE_ASSIGN(allocator, storage, storage->rootNode.info.notLeaf.child[1], newNode);
9ce05555
A
665 storage->rootNode.info.notLeaf.child[2] = NULL;
666 storage->rootNode.numBytes = tempRootNode->numBytes + newNode->numBytes;
bd5b749c
A
667#if 1
668 // ???
669 __CFStorageSetCache(storage, NULL, 0, 0);
670#else
671 if (storage->cache.cachedNode == &(storage->rootNode)) CF_WRITE_BARRIER_BASE_ASSIGN(allocator, storage, storage->cache.cachedNode, tempRootNode); // The cache should follow the node
672#endif
673 }
9ce05555
A
674 numBytesToInsert -= insertThisTime;
675 byteNum += insertThisTime;
676 }
9ce05555
A
677}
678
679/* Deletes the values in the specified range
680 This function gets rid of levels if necessary...
681*/
682void CFStorageDeleteValues(CFStorageRef storage, CFRange range) {
d8925383 683 CFAllocatorRef allocator = CFGetAllocator(storage);
9ce05555
A
684 range.location *= storage->valueSize;
685 range.length *= storage->valueSize;
d8925383 686 __CFStorageDelete(allocator, storage, &storage->rootNode, range, true);
9ce05555
A
687 while (__CFStorageGetNumChildren(&storage->rootNode) == 1) {
688 CFStorageNode *child = storage->rootNode.info.notLeaf.child[0]; // The single child
d8925383
A
689 CF_WRITE_BARRIER_MEMMOVE(&storage->rootNode, child, sizeof(CFStorageNode));
690 _CFAllocatorDeallocateGC(allocator, child);
9ce05555
A
691 }
692 if (__CFStorageGetNumChildren(&storage->rootNode) == 0 && !storage->rootNode.isLeaf) {
693 storage->rootNode.isLeaf = true;
694 storage->rootNode.info.leaf.capacityInBytes = 0;
695 storage->rootNode.info.leaf.memory = NULL;
696 }
bd5b749c
A
697 // !!! Need to update the cache
698 __CFStorageSetCache(storage, NULL, 0, 0);
9ce05555
A
699}
700
701void CFStorageGetValues(CFStorageRef storage, CFRange range, void *values) {
702 while (range.length > 0) {
703 CFRange leafRange;
d8925383 704 void *storagePtr = __CFStorageGetValueAtIndex(storage, range.location, &leafRange);
9ce05555
A
705 CFIndex cntThisTime = range.length;
706 if (cntThisTime > leafRange.length - (range.location - leafRange.location)) cntThisTime = leafRange.length - (range.location - leafRange.location);
707 COPYMEM(storagePtr, values, cntThisTime * storage->valueSize);
bd5b749c 708 values = (uint8_t *)values + (cntThisTime * storage->valueSize);
9ce05555
A
709 range.location += cntThisTime;
710 range.length -= cntThisTime;
711 }
712}
713
bd5b749c
A
714unsigned long _CFStorageFastEnumeration(CFStorageRef storage, struct __objcFastEnumerationStateEquivalent *state, void *stackbuffer, unsigned long count) {
715 // without trying to understand the data structure, each time through search for block containing index
716 CFRange leafRange;
717 if (state->state == 0) { /* first time, get length */
718 state->extra[0] = __CFStorageGetCount(storage);
719 }
720 if (state->state >= state->extra[0]) return 0;
721 state->itemsPtr = (unsigned long *)CFStorageGetValueAtIndex(storage, state->state, &leafRange);
722 state->state += leafRange.length;
723 return leafRange.length;
724}
725
9ce05555
A
726void CFStorageApplyFunction(CFStorageRef storage, CFRange range, CFStorageApplierFunction applier, void *context) {
727 while (0 < range.length) {
728 CFRange leafRange;
729 const void *storagePtr;
d8925383 730 CFIndex idx, cnt;
9ce05555 731 storagePtr = CFStorageGetValueAtIndex(storage, range.location, &leafRange);
d8925383
A
732 cnt = __CFMin(range.length, leafRange.location + leafRange.length - range.location);
733 for (idx = 0; idx < cnt; idx++) {
734 applier(storagePtr, context);
735 storagePtr = (const char *)storagePtr + storage->valueSize;
736 }
9ce05555
A
737 range.length -= cnt;
738 range.location += cnt;
739 }
740}
741
742void CFStorageReplaceValues(CFStorageRef storage, CFRange range, const void *values) {
743 while (range.length > 0) {
744 CFRange leafRange;
d8925383 745 void *storagePtr = __CFStorageGetValueAtIndex(storage, range.location, &leafRange);
9ce05555
A
746 CFIndex cntThisTime = range.length;
747 if (cntThisTime > leafRange.length - (range.location - leafRange.location)) cntThisTime = leafRange.length - (range.location - leafRange.location);
748 COPYMEM(values, storagePtr, cntThisTime * storage->valueSize);
bd5b749c 749 values = (const uint8_t *)values + (cntThisTime * storage->valueSize);
9ce05555
A
750 range.location += cntThisTime;
751 range.length -= cntThisTime;
752 }
753}
754
d8925383
A
755/* Used by CFArray.c */
756
757static void __CFStorageNodeSetLayoutType(CFStorageNode *node, auto_zone_t *zone, auto_memory_type_t type) {
758 if (node->isLeaf) {
759 auto_zone_set_layout_type(zone, node->info.leaf.memory, type);
760 } else {
761 CFStorageNode **children = node->info.notLeaf.child;
762 if (children[0]) __CFStorageNodeSetLayoutType(children[0], zone, type);
763 if (children[1]) __CFStorageNodeSetLayoutType(children[1], zone, type);
764 if (children[2]) __CFStorageNodeSetLayoutType(children[2], zone, type);
765 }
766}
767
768__private_extern__ void _CFStorageSetWeak(CFStorageRef storage) {
bd5b749c
A
769 storage->nodeHint = 0;
770 __CFStorageNodeSetLayoutType(&storage->rootNode, __CFCollectableZone, CF_GET_GC_MEMORY_TYPE(storage->nodeHint));
d8925383
A
771}
772
9ce05555
A
773#undef COPYMEM
774#undef PAGE_LIMIT
775