xref: /xnu-11215/libkern/c++/OSSymbol.cpp (revision 3ca3bd55)
1 /*
2  * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_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. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 /* IOSymbol.cpp created by gvdl on Fri 1998-11-17 */
29 
30 #include <string.h>
31 #include <sys/cdefs.h>
32 
33 __BEGIN_DECLS
34 #include <kern/lock.h>
35 __END_DECLS
36 
37 #include <libkern/c++/OSSymbol.h>
38 #include <libkern/c++/OSLib.h>
39 #include <string.h>
40 
41 #define super OSString
42 
43 typedef struct { int i, j; } OSSymbolPoolState;
44 
45 #if OSALLOCDEBUG
46 extern "C" {
47     extern int debug_container_malloc_size;
48 };
49 #define ACCUMSIZE(s) do { debug_container_malloc_size += (s); } while(0)
50 #else
51 #define ACCUMSIZE(s)
52 #endif
53 
54 #define INITIAL_POOL_SIZE  (exp2ml(1 + log2(kInitBucketCount)))
55 
56 #define GROW_FACTOR   (1)
57 #define SHRINK_FACTOR (3)
58 
59 #define GROW_POOL()     do \
60     if (count * GROW_FACTOR > nBuckets) { \
61         reconstructSymbols(true); \
62     } \
63 while (0)
64 
65 #define SHRINK_POOL()     do \
66     if (count * SHRINK_FACTOR < nBuckets && \
67         nBuckets > INITIAL_POOL_SIZE) { \
68         reconstructSymbols(false); \
69     } \
70 while (0)
71 
72 class OSSymbolPool
73 {
74 private:
75     static const unsigned int kInitBucketCount = 16;
76 
77     typedef struct { unsigned int count; OSSymbol **symbolP; } Bucket;
78 
79     Bucket *buckets;
80     unsigned int nBuckets;
81     unsigned int count;
82     lck_mtx_t *poolGate;
83 
84     static inline void hashSymbol(const char *s,
85                                   unsigned int *hashP,
86                                   unsigned int *lenP)
87     {
88         unsigned int hash = 0;
89         unsigned int len = 0;
90 
91         /* Unroll the loop. */
92         for (;;) {
93             if (!*s) break; len++; hash ^= *s++;
94             if (!*s) break; len++; hash ^= *s++ <<  8;
95             if (!*s) break; len++; hash ^= *s++ << 16;
96             if (!*s) break; len++; hash ^= *s++ << 24;
97         }
98         *lenP = len;
99         *hashP = hash;
100     }
101 
102     static unsigned long log2(unsigned int x);
103     static unsigned long exp2ml(unsigned int x);
104 
105     void reconstructSymbols(void);
106     void reconstructSymbols(bool grow);
107 
108 public:
109     static void *operator new(size_t size);
110     static void operator delete(void *mem, size_t size);
111 
112     OSSymbolPool() { };
113     OSSymbolPool(const OSSymbolPool *old);
114     virtual ~OSSymbolPool();
115 
116     bool init();
117 
118     inline void closeGate() { lck_mtx_lock(poolGate); };
119     inline void openGate()  { lck_mtx_unlock(poolGate); };
120 
121     OSSymbol *findSymbol(const char *cString) const;
122     OSSymbol *insertSymbol(OSSymbol *sym);
123     void removeSymbol(OSSymbol *sym);
124 
125     OSSymbolPoolState initHashState();
126     OSSymbol *nextHashState(OSSymbolPoolState *stateP);
127 };
128 
129 void * OSSymbolPool::operator new(size_t size)
130 {
131     void *mem = (void *)kalloc(size);
132     ACCUMSIZE(size);
133     assert(mem);
134     bzero(mem, size);
135 
136     return mem;
137 }
138 
139 void OSSymbolPool::operator delete(void *mem, size_t size)
140 {
141     kfree(mem, size);
142     ACCUMSIZE(-size);
143 }
144 
145 extern lck_grp_t *IOLockGroup;
146 
147 bool OSSymbolPool::init()
148 {
149     count = 0;
150     nBuckets = INITIAL_POOL_SIZE;
151     buckets = (Bucket *) kalloc(nBuckets * sizeof(Bucket));
152     ACCUMSIZE(nBuckets * sizeof(Bucket));
153     if (!buckets)
154         return false;
155 
156     bzero(buckets, nBuckets * sizeof(Bucket));
157 
158     poolGate = lck_mtx_alloc_init(IOLockGroup, LCK_ATTR_NULL);
159 
160     return poolGate != 0;
161 }
162 
163 OSSymbolPool::OSSymbolPool(const OSSymbolPool *old)
164 {
165     count = old->count;
166     nBuckets = old->nBuckets;
167     buckets = old->buckets;
168 
169     poolGate = 0;	// Do not duplicate the poolGate
170 }
171 
172 OSSymbolPool::~OSSymbolPool()
173 {
174     if (buckets) {
175         kfree(buckets, nBuckets * sizeof(Bucket));
176         ACCUMSIZE(-(nBuckets * sizeof(Bucket)));
177     }
178 
179     if (poolGate)
180         lck_mtx_free(poolGate, IOLockGroup);
181 }
182 
183 unsigned long OSSymbolPool::log2(unsigned int x)
184 {
185     unsigned long i;
186 
187     for (i = 0; x > 1 ; i++)
188         x >>= 1;
189     return i;
190 }
191 
192 unsigned long OSSymbolPool::exp2ml(unsigned int x)
193 {
194     return (1 << x) - 1;
195 }
196 
197 OSSymbolPoolState OSSymbolPool::initHashState()
198 {
199     OSSymbolPoolState newState = { nBuckets, 0 };
200     return newState;
201 }
202 
203 OSSymbol *OSSymbolPool::nextHashState(OSSymbolPoolState *stateP)
204 {
205     Bucket *thisBucket = &buckets[stateP->i];
206 
207     while (!stateP->j) {
208         if (!stateP->i)
209             return 0;
210         stateP->i--;
211         thisBucket--;
212         stateP->j = thisBucket->count;
213     }
214 
215     stateP->j--;
216     if (thisBucket->count == 1)
217         return (OSSymbol *) thisBucket->symbolP;
218     else
219         return thisBucket->symbolP[stateP->j];
220 }
221 
222 void OSSymbolPool::reconstructSymbols(void)
223 {
224     this->reconstructSymbols(true);
225 }
226 
227 void OSSymbolPool::reconstructSymbols(bool grow)
228 {
229     unsigned int new_nBuckets = nBuckets;
230     OSSymbol *insert;
231     OSSymbolPoolState state;
232 
233     if (grow) {
234         new_nBuckets += new_nBuckets + 1;
235     } else {
236        /* Don't shrink the pool below the default initial size.
237         */
238         if (nBuckets <= INITIAL_POOL_SIZE) {
239             return;
240         }
241         new_nBuckets = (new_nBuckets - 1) / 2;
242     }
243 
244    /* Create old pool to iterate after doing above check, cause it
245     * gets finalized at return.
246     */
247     OSSymbolPool old(this);
248 
249     count = 0;
250     nBuckets = new_nBuckets;
251     buckets = (Bucket *) kalloc(nBuckets * sizeof(Bucket));
252     ACCUMSIZE(nBuckets * sizeof(Bucket));
253     /* @@@ gvdl: Zero test and panic if can't set up pool */
254     bzero(buckets, nBuckets * sizeof(Bucket));
255 
256     state = old.initHashState();
257     while ( (insert = old.nextHashState(&state)) )
258         insertSymbol(insert);
259 }
260 
261 OSSymbol *OSSymbolPool::findSymbol(const char *cString) const
262 {
263     Bucket *thisBucket;
264     unsigned int j, inLen, hash;
265     OSSymbol *probeSymbol, **list;
266 
267     hashSymbol(cString, &hash, &inLen); inLen++;
268     thisBucket = &buckets[hash % nBuckets];
269     j = thisBucket->count;
270 
271     if (!j)
272         return 0;
273 
274     if (j == 1) {
275         probeSymbol = (OSSymbol *) thisBucket->symbolP;
276 
277         if (inLen == probeSymbol->length
278         &&  (strncmp(probeSymbol->string, cString, probeSymbol->length) == 0))
279             return probeSymbol;
280 	return 0;
281     }
282 
283     for (list = thisBucket->symbolP; j--; list++) {
284         probeSymbol = *list;
285         if (inLen == probeSymbol->length
286         &&  (strncmp(probeSymbol->string, cString, probeSymbol->length) == 0))
287             return probeSymbol;
288     }
289 
290     return 0;
291 }
292 
293 OSSymbol *OSSymbolPool::insertSymbol(OSSymbol *sym)
294 {
295     const char *cString = sym->string;
296     Bucket *thisBucket;
297     unsigned int j, inLen, hash;
298     OSSymbol *probeSymbol, **list;
299 
300     hashSymbol(cString, &hash, &inLen); inLen++;
301     thisBucket = &buckets[hash % nBuckets];
302     j = thisBucket->count;
303 
304     if (!j) {
305         thisBucket->symbolP = (OSSymbol **) sym;
306         thisBucket->count++;
307         count++;
308         return sym;
309     }
310 
311     if (j == 1) {
312         probeSymbol = (OSSymbol *) thisBucket->symbolP;
313 
314         if (inLen == probeSymbol->length
315         &&  strncmp(probeSymbol->string, cString, probeSymbol->length) == 0)
316             return probeSymbol;
317 
318         list = (OSSymbol **) kalloc(2 * sizeof(OSSymbol *));
319         ACCUMSIZE(2 * sizeof(OSSymbol *));
320         /* @@@ gvdl: Zero test and panic if can't set up pool */
321         list[0] = sym;
322         list[1] = probeSymbol;
323         thisBucket->symbolP = list;
324         thisBucket->count++;
325         count++;
326         GROW_POOL();
327 
328         return sym;
329     }
330 
331     for (list = thisBucket->symbolP; j--; list++) {
332         probeSymbol = *list;
333         if (inLen == probeSymbol->length
334         &&  strncmp(probeSymbol->string, cString, probeSymbol->length) == 0)
335             return probeSymbol;
336     }
337 
338     j = thisBucket->count++;
339     count++;
340     list = (OSSymbol **) kalloc(thisBucket->count * sizeof(OSSymbol *));
341     ACCUMSIZE(thisBucket->count * sizeof(OSSymbol *));
342     /* @@@ gvdl: Zero test and panic if can't set up pool */
343     list[0] = sym;
344     bcopy(thisBucket->symbolP, list + 1, j * sizeof(OSSymbol *));
345     kfree(thisBucket->symbolP, j * sizeof(OSSymbol *));
346     ACCUMSIZE(-(j * sizeof(OSSymbol *)));
347     thisBucket->symbolP = list;
348     GROW_POOL();
349 
350     return sym;
351 }
352 
353 void OSSymbolPool::removeSymbol(OSSymbol *sym)
354 {
355     Bucket *thisBucket;
356     unsigned int j, inLen, hash;
357     OSSymbol *probeSymbol, **list;
358 
359     hashSymbol(sym->string, &hash, &inLen); inLen++;
360     thisBucket = &buckets[hash % nBuckets];
361     j = thisBucket->count;
362     list = thisBucket->symbolP;
363 
364     if (!j)
365         return;
366 
367     if (j == 1) {
368         probeSymbol = (OSSymbol *) list;
369 
370         if (probeSymbol == sym) {
371             thisBucket->symbolP = 0;
372             count--;
373             thisBucket->count--;
374             SHRINK_POOL();
375             return;
376         }
377         return;
378     }
379 
380     if (j == 2) {
381         probeSymbol = list[0];
382         if (probeSymbol == sym) {
383             thisBucket->symbolP = (OSSymbol **) list[1];
384             kfree(list, 2 * sizeof(OSSymbol *));
385 	    ACCUMSIZE(-(2 * sizeof(OSSymbol *)));
386             count--;
387             thisBucket->count--;
388             SHRINK_POOL();
389             return;
390         }
391 
392         probeSymbol = list[1];
393         if (probeSymbol == sym) {
394             thisBucket->symbolP = (OSSymbol **) list[0];
395             kfree(list, 2 * sizeof(OSSymbol *));
396 	    ACCUMSIZE(-(2 * sizeof(OSSymbol *)));
397             count--;
398             thisBucket->count--;
399             SHRINK_POOL();
400             return;
401         }
402         return;
403     }
404 
405     for (; j--; list++) {
406         probeSymbol = *list;
407         if (probeSymbol == sym) {
408 
409             list = (OSSymbol **)
410                 kalloc((thisBucket->count-1) * sizeof(OSSymbol *));
411 	    ACCUMSIZE((thisBucket->count-1) * sizeof(OSSymbol *));
412             if (thisBucket->count-1 != j)
413                 bcopy(thisBucket->symbolP, list,
414                       (thisBucket->count-1-j) * sizeof(OSSymbol *));
415             if (j)
416                 bcopy(thisBucket->symbolP + thisBucket->count-j,
417                       list + thisBucket->count-1-j,
418                       j * sizeof(OSSymbol *));
419             kfree(thisBucket->symbolP, thisBucket->count * sizeof(OSSymbol *));
420 	    ACCUMSIZE(-(thisBucket->count * sizeof(OSSymbol *)));
421             thisBucket->symbolP = list;
422             count--;
423             thisBucket->count--;
424             return;
425         }
426     }
427 }
428 
429 /*
430  *********************************************************************
431  * From here on we are actually implementing the OSSymbol class
432  *********************************************************************
433  */
434 OSDefineMetaClassAndStructorsWithInit(OSSymbol, OSString,
435                                       OSSymbol::initialize())
436 OSMetaClassDefineReservedUnused(OSSymbol, 0);
437 OSMetaClassDefineReservedUnused(OSSymbol, 1);
438 OSMetaClassDefineReservedUnused(OSSymbol, 2);
439 OSMetaClassDefineReservedUnused(OSSymbol, 3);
440 OSMetaClassDefineReservedUnused(OSSymbol, 4);
441 OSMetaClassDefineReservedUnused(OSSymbol, 5);
442 OSMetaClassDefineReservedUnused(OSSymbol, 6);
443 OSMetaClassDefineReservedUnused(OSSymbol, 7);
444 
445 static OSSymbolPool *pool;
446 
447 void OSSymbol::initialize()
448 {
449     pool = new OSSymbolPool;
450     assert(pool);
451 
452     if (!pool->init()) {
453         delete pool;
454         assert(false);
455     };
456 }
457 
458 bool OSSymbol::initWithCStringNoCopy(const char *) { return false; }
459 bool OSSymbol::initWithCString(const char *) { return false; }
460 bool OSSymbol::initWithString(const OSString *) { return false; }
461 
462 const OSSymbol *OSSymbol::withString(const OSString *aString)
463 {
464     // This string may be a OSSymbol already, cheap check.
465     if (OSDynamicCast(OSSymbol, aString)) {
466 	aString->retain();
467 	return (const OSSymbol *) aString;
468     }
469     else if (((const OSSymbol *) aString)->flags & kOSStringNoCopy)
470         return OSSymbol::withCStringNoCopy(aString->getCStringNoCopy());
471     else
472         return OSSymbol::withCString(aString->getCStringNoCopy());
473 }
474 
475 const OSSymbol *OSSymbol::withCString(const char *cString)
476 {
477     pool->closeGate();
478 
479     OSSymbol *oldSymb = pool->findSymbol(cString);
480     if (!oldSymb) {
481         OSSymbol *newSymb = new OSSymbol;
482         if (!newSymb) {
483             pool->openGate();
484             return newSymb;
485         }
486 
487 	if (newSymb->OSString::initWithCString(cString))
488 	    oldSymb = pool->insertSymbol(newSymb);
489 
490         if (newSymb == oldSymb) {
491             pool->openGate();
492             return newSymb;	// return the newly created & inserted symbol.
493         }
494         else
495             // Somebody else inserted the new symbol so free our copy
496 	    newSymb->OSString::free();
497     }
498 
499     oldSymb->retain();	// Retain the old symbol before releasing the lock.
500 
501     pool->openGate();
502     return oldSymb;
503 }
504 
505 const OSSymbol *OSSymbol::withCStringNoCopy(const char *cString)
506 {
507     pool->closeGate();
508 
509     OSSymbol *oldSymb = pool->findSymbol(cString);
510     if (!oldSymb) {
511         OSSymbol *newSymb = new OSSymbol;
512         if (!newSymb) {
513             pool->openGate();
514             return newSymb;
515         }
516 
517 	if (newSymb->OSString::initWithCStringNoCopy(cString))
518 	    oldSymb = pool->insertSymbol(newSymb);
519 
520         if (newSymb == oldSymb) {
521             pool->openGate();
522             return newSymb;	// return the newly created & inserted symbol.
523         }
524         else
525             // Somebody else inserted the new symbol so free our copy
526 	    newSymb->OSString::free();
527     }
528 
529     oldSymb->retain();	// Retain the old symbol before releasing the lock.
530 
531     pool->openGate();
532     return oldSymb;
533 }
534 
535 void OSSymbol::checkForPageUnload(void *startAddr, void *endAddr)
536 {
537     OSSymbol *probeSymbol;
538     OSSymbolPoolState state;
539 
540     pool->closeGate();
541     state = pool->initHashState();
542     while ( (probeSymbol = pool->nextHashState(&state)) ) {
543         if (probeSymbol->string >= startAddr && probeSymbol->string < endAddr) {
544             const char *oldString = probeSymbol->string;
545 
546             probeSymbol->string = (char *) kalloc(probeSymbol->length);
547 	    ACCUMSIZE(probeSymbol->length);
548             bcopy(oldString, probeSymbol->string, probeSymbol->length);
549             probeSymbol->flags &= ~kOSStringNoCopy;
550         }
551     }
552     pool->openGate();
553 }
554 
555 void OSSymbol::taggedRelease(const void *tag) const
556 {
557     super::taggedRelease(tag);
558 }
559 
560 void OSSymbol::taggedRelease(const void *tag, const int when) const
561 {
562     pool->closeGate();
563     super::taggedRelease(tag, when);
564     pool->openGate();
565 }
566 
567 void OSSymbol::free()
568 {
569     pool->removeSymbol(this);
570     super::free();
571 }
572 
573 bool OSSymbol::isEqualTo(const char *aCString) const
574 {
575     return super::isEqualTo(aCString);
576 }
577 
578 bool OSSymbol::isEqualTo(const OSSymbol *aSymbol) const
579 {
580     return aSymbol == this;
581 }
582 
583 bool OSSymbol::isEqualTo(const OSMetaClassBase *obj) const
584 {
585     OSSymbol *	sym;
586     OSString *	str;
587 
588     if ((sym = OSDynamicCast(OSSymbol, obj)))
589 	return isEqualTo(sym);
590     else if ((str = OSDynamicCast(OSString, obj)))
591 	return super::isEqualTo(str);
592     else
593 	return false;
594 }
595