2 * Copyright (c) 2000-2016 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_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. 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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
28 /* IOSymbol.cpp created by gvdl on Fri 1998-11-17 */
31 #include <sys/cdefs.h>
33 #include <kern/locks.h>
35 #include <libkern/c++/OSSymbol.h>
36 #include <libkern/c++/OSLib.h>
39 #define super OSString
41 typedef struct { unsigned int i
, j
; } OSSymbolPoolState
;
43 #define INITIAL_POOL_SIZE (exp2ml(1 + log2(kInitBucketCount)))
45 #define GROW_FACTOR (1)
46 #define SHRINK_FACTOR (3)
48 #define GROW_POOL() do \
49 if (count * GROW_FACTOR > nBuckets) { \
50 reconstructSymbols(true); \
54 #define SHRINK_POOL() do \
55 if (count * SHRINK_FACTOR < nBuckets && \
56 nBuckets > INITIAL_POOL_SIZE) { \
57 reconstructSymbols(false); \
64 static const unsigned int kInitBucketCount
= 16;
66 typedef struct { unsigned int count
; OSSymbol
**symbolP
; } Bucket
;
69 unsigned int nBuckets
;
74 hashSymbol(const char *s
,
78 unsigned int hash
= 0;
81 /* Unroll the loop. */
90 len
++; hash
^= *s
++ << 8;
94 len
++; hash
^= *s
++ << 16;
98 len
++; hash
^= *s
++ << 24;
104 static unsigned long log2(unsigned int x
);
105 static unsigned long exp2ml(unsigned int x
);
107 void reconstructSymbols(void);
108 void reconstructSymbols(bool grow
);
111 static void *operator new(size_t size
);
112 static void operator delete(void *mem
, size_t size
);
117 OSSymbolPool(const OSSymbolPool
*old
);
126 lck_rw_lock(poolGate
, LCK_RW_TYPE_SHARED
);
132 lck_rw_unlock(poolGate
, LCK_RW_TYPE_SHARED
);
139 lck_rw_lock(poolGate
, LCK_RW_TYPE_EXCLUSIVE
);
145 lck_rw_unlock(poolGate
, LCK_RW_TYPE_EXCLUSIVE
);
148 LIBKERN_RETURNS_RETAINED OSSymbol
*findSymbol(const char *cString
) const;
149 LIBKERN_RETURNS_RETAINED OSSymbol
*insertSymbol(OSSymbol
*sym
);
150 void removeSymbol(OSSymbol
*sym
);
152 OSSymbolPoolState
initHashState();
153 LIBKERN_RETURNS_NOT_RETAINED OSSymbol
*nextHashState(OSSymbolPoolState
*stateP
);
157 OSSymbolPool::operator new(size_t size
)
159 void *mem
= (void *)kalloc_tag(size
, VM_KERN_MEMORY_LIBKERN
);
160 OSMETA_ACCUMSIZE(size
);
168 OSSymbolPool::operator delete(void *mem
, size_t size
)
171 OSMETA_ACCUMSIZE(-size
);
174 extern lck_grp_t
*IOLockGroup
;
180 nBuckets
= INITIAL_POOL_SIZE
;
181 buckets
= (Bucket
*) kalloc_tag(nBuckets
* sizeof(Bucket
), VM_KERN_MEMORY_LIBKERN
);
182 OSMETA_ACCUMSIZE(nBuckets
* sizeof(Bucket
));
187 bzero(buckets
, nBuckets
* sizeof(Bucket
));
189 poolGate
= lck_rw_alloc_init(IOLockGroup
, LCK_ATTR_NULL
);
191 return poolGate
!= NULL
;
194 OSSymbolPool::OSSymbolPool(const OSSymbolPool
*old
)
197 nBuckets
= old
->nBuckets
;
198 buckets
= old
->buckets
;
200 poolGate
= NULL
; // Do not duplicate the poolGate
203 OSSymbolPool::~OSSymbolPool()
207 for (thisBucket
= &buckets
[0]; thisBucket
< &buckets
[nBuckets
]; thisBucket
++) {
208 if (thisBucket
->count
> 1) {
209 kfree(thisBucket
->symbolP
, thisBucket
->count
* sizeof(OSSymbol
*));
210 OSMETA_ACCUMSIZE(-(thisBucket
->count
* sizeof(OSSymbol
*)));
213 kfree(buckets
, nBuckets
* sizeof(Bucket
));
214 OSMETA_ACCUMSIZE(-(nBuckets
* sizeof(Bucket
)));
218 lck_rw_free(poolGate
, IOLockGroup
);
223 OSSymbolPool::log2(unsigned int x
)
227 for (i
= 0; x
> 1; i
++) {
234 OSSymbolPool::exp2ml(unsigned int x
)
240 OSSymbolPool::initHashState()
242 OSSymbolPoolState newState
= { nBuckets
, 0 };
247 OSSymbolPool::nextHashState(OSSymbolPoolState
*stateP
)
249 Bucket
*thisBucket
= &buckets
[stateP
->i
];
257 stateP
->j
= thisBucket
->count
;
261 if (thisBucket
->count
== 1) {
262 return (OSSymbol
*) thisBucket
->symbolP
;
264 return thisBucket
->symbolP
[stateP
->j
];
269 OSSymbolPool::reconstructSymbols(void)
271 this->reconstructSymbols(true);
275 OSSymbolPool::reconstructSymbols(bool grow
)
277 unsigned int new_nBuckets
= nBuckets
;
279 OSSymbolPoolState state
;
282 new_nBuckets
+= new_nBuckets
+ 1;
284 /* Don't shrink the pool below the default initial size.
286 if (nBuckets
<= INITIAL_POOL_SIZE
) {
289 new_nBuckets
= (new_nBuckets
- 1) / 2;
292 /* Create old pool to iterate after doing above check, cause it
293 * gets finalized at return.
295 OSSymbolPool
old(this);
298 nBuckets
= new_nBuckets
;
299 buckets
= (Bucket
*) kalloc_tag(nBuckets
* sizeof(Bucket
), VM_KERN_MEMORY_LIBKERN
);
300 OSMETA_ACCUMSIZE(nBuckets
* sizeof(Bucket
));
301 /* @@@ gvdl: Zero test and panic if can't set up pool */
302 bzero(buckets
, nBuckets
* sizeof(Bucket
));
304 state
= old
.initHashState();
305 while ((insert
= old
.nextHashState(&state
))) {
306 insertSymbol(insert
);
311 OSSymbolPool::findSymbol(const char *cString
) const
314 unsigned int j
, inLen
, hash
;
315 OSSymbol
*probeSymbol
, **list
;
317 hashSymbol(cString
, &hash
, &inLen
); inLen
++;
318 thisBucket
= &buckets
[hash
% nBuckets
];
319 j
= thisBucket
->count
;
326 probeSymbol
= (OSSymbol
*) thisBucket
->symbolP
;
328 if (inLen
== probeSymbol
->length
329 && strncmp(probeSymbol
->string
, cString
, probeSymbol
->length
) == 0
330 && probeSymbol
->taggedTryRetain(nullptr)) {
336 for (list
= thisBucket
->symbolP
; j
--; list
++) {
338 if (inLen
== probeSymbol
->length
339 && strncmp(probeSymbol
->string
, cString
, probeSymbol
->length
) == 0
340 && probeSymbol
->taggedTryRetain(nullptr)) {
349 OSSymbolPool::insertSymbol(OSSymbol
*sym
)
351 const char *cString
= sym
->string
;
353 unsigned int j
, inLen
, hash
;
354 OSSymbol
*probeSymbol
, **list
;
356 hashSymbol(cString
, &hash
, &inLen
); inLen
++;
357 thisBucket
= &buckets
[hash
% nBuckets
];
358 j
= thisBucket
->count
;
361 thisBucket
->symbolP
= (OSSymbol
**) sym
;
368 probeSymbol
= (OSSymbol
*) thisBucket
->symbolP
;
370 if (inLen
== probeSymbol
->length
371 && strncmp(probeSymbol
->string
, cString
, probeSymbol
->length
) == 0
372 && probeSymbol
->taggedTryRetain(nullptr)) {
376 list
= (OSSymbol
**) kalloc_tag(2 * sizeof(OSSymbol
*), VM_KERN_MEMORY_LIBKERN
);
377 OSMETA_ACCUMSIZE(2 * sizeof(OSSymbol
*));
378 /* @@@ gvdl: Zero test and panic if can't set up pool */
380 list
[1] = probeSymbol
;
381 thisBucket
->symbolP
= list
;
389 for (list
= thisBucket
->symbolP
; j
--; list
++) {
391 if (inLen
== probeSymbol
->length
392 && strncmp(probeSymbol
->string
, cString
, probeSymbol
->length
) == 0
393 && probeSymbol
->taggedTryRetain(nullptr)) {
398 j
= thisBucket
->count
++;
400 list
= (OSSymbol
**) kalloc_tag(thisBucket
->count
* sizeof(OSSymbol
*), VM_KERN_MEMORY_LIBKERN
);
401 OSMETA_ACCUMSIZE(thisBucket
->count
* sizeof(OSSymbol
*));
402 /* @@@ gvdl: Zero test and panic if can't set up pool */
404 bcopy(thisBucket
->symbolP
, list
+ 1, j
* sizeof(OSSymbol
*));
405 kfree(thisBucket
->symbolP
, j
* sizeof(OSSymbol
*));
406 OSMETA_ACCUMSIZE(-(j
* sizeof(OSSymbol
*)));
407 thisBucket
->symbolP
= list
;
414 OSSymbolPool::removeSymbol(OSSymbol
*sym
)
417 unsigned int j
, inLen
, hash
;
418 OSSymbol
*probeSymbol
, **list
;
420 hashSymbol(sym
->string
, &hash
, &inLen
); inLen
++;
421 thisBucket
= &buckets
[hash
% nBuckets
];
422 j
= thisBucket
->count
;
423 list
= thisBucket
->symbolP
;
426 // couldn't find the symbol; probably means string hash changed
427 panic("removeSymbol %s count %d ", sym
->string
? sym
->string
: "no string", count
);
432 probeSymbol
= (OSSymbol
*) list
;
434 if (probeSymbol
== sym
) {
435 thisBucket
->symbolP
= NULL
;
441 // couldn't find the symbol; probably means string hash changed
442 panic("removeSymbol %s count %d ", sym
->string
? sym
->string
: "no string", count
);
447 probeSymbol
= list
[0];
448 if (probeSymbol
== sym
) {
449 thisBucket
->symbolP
= (OSSymbol
**) list
[1];
450 kfree(list
, 2 * sizeof(OSSymbol
*));
451 OSMETA_ACCUMSIZE(-(2 * sizeof(OSSymbol
*)));
458 probeSymbol
= list
[1];
459 if (probeSymbol
== sym
) {
460 thisBucket
->symbolP
= (OSSymbol
**) list
[0];
461 kfree(list
, 2 * sizeof(OSSymbol
*));
462 OSMETA_ACCUMSIZE(-(2 * sizeof(OSSymbol
*)));
468 // couldn't find the symbol; probably means string hash changed
469 panic("removeSymbol %s count %d ", sym
->string
? sym
->string
: "no string", count
);
473 for (; j
--; list
++) {
475 if (probeSymbol
== sym
) {
477 kalloc_tag((thisBucket
->count
- 1) * sizeof(OSSymbol
*), VM_KERN_MEMORY_LIBKERN
);
478 OSMETA_ACCUMSIZE((thisBucket
->count
- 1) * sizeof(OSSymbol
*));
479 if (thisBucket
->count
- 1 != j
) {
480 bcopy(thisBucket
->symbolP
, list
,
481 (thisBucket
->count
- 1 - j
) * sizeof(OSSymbol
*));
484 bcopy(thisBucket
->symbolP
+ thisBucket
->count
- j
,
485 list
+ thisBucket
->count
- 1 - j
,
486 j
* sizeof(OSSymbol
*));
488 kfree(thisBucket
->symbolP
, thisBucket
->count
* sizeof(OSSymbol
*));
489 OSMETA_ACCUMSIZE(-(thisBucket
->count
* sizeof(OSSymbol
*)));
490 thisBucket
->symbolP
= list
;
496 // couldn't find the symbol; probably means string hash changed
497 panic("removeSymbol %s count %d ", sym
->string
? sym
->string
: "no string", count
);
501 *********************************************************************
502 * From here on we are actually implementing the OSSymbol class
503 *********************************************************************
505 OSDefineMetaClassAndStructorsWithInit(OSSymbol
, OSString
,
506 OSSymbol::initialize())
507 OSMetaClassDefineReservedUnused(OSSymbol
, 0);
508 OSMetaClassDefineReservedUnused(OSSymbol
, 1);
509 OSMetaClassDefineReservedUnused(OSSymbol
, 2);
510 OSMetaClassDefineReservedUnused(OSSymbol
, 3);
511 OSMetaClassDefineReservedUnused(OSSymbol
, 4);
512 OSMetaClassDefineReservedUnused(OSSymbol
, 5);
513 OSMetaClassDefineReservedUnused(OSSymbol
, 6);
514 OSMetaClassDefineReservedUnused(OSSymbol
, 7);
516 static OSSymbolPool
*pool
;
519 OSSymbol::initialize()
521 pool
= new OSSymbolPool
;
524 if (pool
&& !pool
->init()) {
532 OSSymbol::initWithCStringNoCopy(const char *)
537 OSSymbol::initWithCString(const char *)
542 OSSymbol::initWithString(const OSString
*)
548 OSSymbol::withString(const OSString
*aString
)
550 // This string may be a OSSymbol already, cheap check.
551 if (OSDynamicCast(OSSymbol
, aString
)) {
553 return (const OSSymbol
*) aString
;
554 } else if (((const OSSymbol
*) aString
)->flags
& kOSStringNoCopy
) {
555 return OSSymbol::withCStringNoCopy(aString
->getCStringNoCopy());
557 return OSSymbol::withCString(aString
->getCStringNoCopy());
562 OSSymbol::withCString(const char *cString
)
564 const OSSymbol
*symbol
;
566 // Check if the symbol exists already, we don't need to take a lock here,
567 // since existingSymbolForCString will take the shared lock.
568 symbol
= OSSymbol::existingSymbolForCString(cString
);
573 OSSymbol
*newSymb
= new OSSymbol
;
578 if (newSymb
->OSString::initWithCString(cString
)) {
579 pool
->closeWriteGate();
580 symbol
= pool
->insertSymbol(newSymb
);
581 pool
->openWriteGate();
584 // Somebody must have inserted the new symbol so free our copy
585 newSymb
->OSString::free();
590 return newSymb
; // return the newly created & inserted symbol.
594 OSSymbol::withCStringNoCopy(const char *cString
)
596 const OSSymbol
*symbol
;
599 // Check if the symbol exists already, we don't need to take a lock here,
600 // since existingSymbolForCString will take the shared lock.
601 symbol
= OSSymbol::existingSymbolForCString(cString
);
606 newSymb
= new OSSymbol
;
611 if (newSymb
->OSString::initWithCStringNoCopy(cString
)) {
612 pool
->closeWriteGate();
613 symbol
= pool
->insertSymbol(newSymb
);
614 pool
->openWriteGate();
617 // Somebody must have inserted the new symbol so free our copy
618 newSymb
->OSString::free();
623 return newSymb
; // return the newly created & inserted symbol.
627 OSSymbol::existingSymbolForString(const OSString
*aString
)
629 if (OSDynamicCast(OSSymbol
, aString
)) {
631 return (const OSSymbol
*) aString
;
634 return OSSymbol::existingSymbolForCString(aString
->getCStringNoCopy());
638 OSSymbol::existingSymbolForCString(const char *cString
)
642 pool
->closeReadGate();
643 symbol
= pool
->findSymbol(cString
);
644 pool
->openReadGate();
650 OSSymbol::checkForPageUnload(void *startAddr
, void *endAddr
)
652 OSSymbol
*probeSymbol
;
653 OSSymbolPoolState state
;
655 pool
->closeWriteGate();
656 state
= pool
->initHashState();
657 while ((probeSymbol
= pool
->nextHashState(&state
))) {
658 if (probeSymbol
->string
>= startAddr
&& probeSymbol
->string
< endAddr
) {
659 probeSymbol
->OSString::initWithCString(probeSymbol
->string
);
662 pool
->openWriteGate();
666 OSSymbol::taggedRelease(const void *tag
) const
668 super::taggedRelease(tag
);
672 OSSymbol::taggedRelease(const void *tag
, const int when
) const
674 super::taggedRelease(tag
, when
);
680 pool
->closeWriteGate();
681 pool
->removeSymbol(this);
682 pool
->openWriteGate();
687 OSSymbol::isEqualTo(const char *aCString
) const
689 return super::isEqualTo(aCString
);
693 OSSymbol::isEqualTo(const OSSymbol
*aSymbol
) const
695 return aSymbol
== this;
699 OSSymbol::isEqualTo(const OSMetaClassBase
*obj
) const
704 if ((sym
= OSDynamicCast(OSSymbol
, obj
))) {
705 return isEqualTo(sym
);
706 } else if ((str
= OSDynamicCast(OSString
, obj
))) {
707 return super::isEqualTo(str
);
717 unsigned int arrayCount
,
721 unsigned int baseIdx
= 0;
724 for (lim
= arrayCount
; lim
; lim
>>= 1) {
725 p
= (typeof(p
))(((uintptr_t) array
) + (baseIdx
+ (lim
>> 1)) * memberSize
);
727 return baseIdx
+ (lim
>> 1);
731 baseIdx
+= (lim
>> 1) + 1;
736 // not found, insertion point here
737 return baseIdx
+ (lim
>> 1);