blob: fcf42f60addb18922637a9252ec541ab124fd961 [file] [log] [blame]
/*
* Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. All rights reserved.
* Copyright (c) 2009 Grant Erickson <gerickson@nuovations.com>. All rights reserved.
*
* This source code is a modified version of the CoreFoundation sources released by Apple Inc. under
* the terms of the APSL version 2.0 (see below).
*
* For information about changes from the original Apple source release can be found by reviewing the
* source control system for the project at https://sourceforge.net/svn/?group_id=246198.
*
* The original license information is as follows:
*
* Copyright (c) 2008 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/* CFBase.c
Copyright 1998-2002, Apple, Inc. All rights reserved.
Responsibility: Christopher Kane
*/
#include <CoreFoundation/CFBase.h>
#include "CFInternal.h"
#if DEPLOYMENT_TARGET_MACOSX
#include <pthread.h>
#include <malloc/malloc.h>
#include <mach/mach.h>
#include <dlfcn.h>
#elif DEPLOYMENT_TARGET_LINUX
#if defined(DEBUG)
#include <mcheck.h>
#endif
#include <pthread.h>
#elif DEPLOYMENT_TARGET_WINDOWS
#include <windows.h>
#endif
#include <stdlib.h>
#include <string.h>
extern size_t malloc_good_size(size_t size);
#if defined(__CYGWIN32__) || defined (D__CYGWIN_)
#error CoreFoundation is currently built with the Microsoft C Runtime, which is incompatible with the Cygwin DLL. You must either use the -mno-cygwin flag, or complete a port of CF to the Cygwin environment.
#endif
// -------- -------- -------- -------- -------- -------- -------- --------
// CFAllocator structure must match struct _malloc_zone_t!
// The first two reserved fields in struct _malloc_zone_t are for us with CFRuntimeBase
struct __CFAllocator {
CFRuntimeBase _base;
#if DEPLOYMENT_TARGET_MACOSX
size_t (*size)(struct _malloc_zone_t *zone, const void *ptr); /* returns the size of a block or 0 if not in this zone; must be fast, especially for negative answers */
void *(*malloc)(struct _malloc_zone_t *zone, size_t size);
void *(*calloc)(struct _malloc_zone_t *zone, size_t num_items, size_t size); /* same as malloc, but block returned is set to zero */
void *(*valloc)(struct _malloc_zone_t *zone, size_t size); /* same as malloc, but block returned is set to zero and is guaranteed to be page aligned */
void (*free)(struct _malloc_zone_t *zone, void *ptr);
void *(*realloc)(struct _malloc_zone_t *zone, void *ptr, size_t size);
void (*destroy)(struct _malloc_zone_t *zone); /* zone is destroyed and all memory reclaimed */
const char *zone_name;
unsigned (*batch_malloc)(struct _malloc_zone_t *zone, size_t size, void **results, unsigned num_requested); /* given a size, returns pointers capable of holding that size; returns the number of pointers allocated (maybe 0 or less than num_requested) */
void (*batch_free)(struct _malloc_zone_t *zone, void **to_be_freed, unsigned num_to_be_freed); /* frees all the pointers in to_be_freed; note that to_be_freed may be overwritten during the process */
struct malloc_introspection_t *introspect;
void *reserved5;
#endif
CFAllocatorRef _allocator;
CFAllocatorContext _context;
};
CF_INLINE CFAllocatorRetainCallBack __CFAllocatorGetRetainFunction(const CFAllocatorContext *context) {
CFAllocatorRetainCallBack retval = NULL;
retval = context->retain;
return retval;
}
CF_INLINE CFAllocatorReleaseCallBack __CFAllocatorGetReleaseFunction(const CFAllocatorContext *context) {
CFAllocatorReleaseCallBack retval = NULL;
retval = context->release;
return retval;
}
CF_INLINE CFAllocatorCopyDescriptionCallBack __CFAllocatorGetCopyDescriptionFunction(const CFAllocatorContext *context) {
CFAllocatorCopyDescriptionCallBack retval = NULL;
retval = context->copyDescription;
return retval;
}
CF_INLINE CFAllocatorAllocateCallBack __CFAllocatorGetAllocateFunction(const CFAllocatorContext *context) {
CFAllocatorAllocateCallBack retval = NULL;
retval = context->allocate;
return retval;
}
CF_INLINE CFAllocatorReallocateCallBack __CFAllocatorGetReallocateFunction(const CFAllocatorContext *context) {
CFAllocatorReallocateCallBack retval = NULL;
retval = context->reallocate;
return retval;
}
CF_INLINE CFAllocatorDeallocateCallBack __CFAllocatorGetDeallocateFunction(const CFAllocatorContext *context) {
CFAllocatorDeallocateCallBack retval = NULL;
retval = context->deallocate;
return retval;
}
CF_INLINE CFAllocatorPreferredSizeCallBack __CFAllocatorGetPreferredSizeFunction(const CFAllocatorContext *context) {
CFAllocatorPreferredSizeCallBack retval = NULL;
retval = context->preferredSize;
return retval;
}
#if DEPLOYMENT_TARGET_MACOSX
__private_extern__ void __CFAllocatorDeallocate(CFTypeRef cf);
static kern_return_t __CFAllocatorZoneIntrospectNoOp(void) {
return 0;
}
static boolean_t __CFAllocatorZoneIntrospectTrue(void) {
return 1;
}
static size_t __CFAllocatorCustomSize(malloc_zone_t *zone, const void *ptr) {
return 0;
// The only way to implement this with a version 0 allocator would be
// for CFAllocator to keep track of all blocks allocated itself, which
// could be done, but would be bad for performance, so we don't do it.
// size_t (*size)(struct _malloc_zone_t *zone, const void *ptr);
/* returns the size of a block or 0 if not in this zone;
* must be fast, especially for negative answers */
}
static void *__CFAllocatorCustomMalloc(malloc_zone_t *zone, size_t size) {
CFAllocatorRef allocator = (CFAllocatorRef)zone;
return CFAllocatorAllocate(allocator, size, 0);
}
static void *__CFAllocatorCustomCalloc(malloc_zone_t *zone, size_t num_items, size_t size) {
CFAllocatorRef allocator = (CFAllocatorRef)zone;
void *newptr = CFAllocatorAllocate(allocator, size, 0);
if (newptr) memset(newptr, 0, size);
return newptr;
}
static void *__CFAllocatorCustomValloc(malloc_zone_t *zone, size_t size) {
CFAllocatorRef allocator = (CFAllocatorRef)zone;
void *newptr = CFAllocatorAllocate(allocator, size + vm_page_size, 0);
newptr = (void *)round_page((uintptr_t)newptr);
return newptr;
}
static void __CFAllocatorCustomFree(malloc_zone_t *zone, void *ptr) {
CFAllocatorRef allocator = (CFAllocatorRef)zone;
CFAllocatorDeallocate(allocator, ptr);
}
static void *__CFAllocatorCustomRealloc(malloc_zone_t *zone, void *ptr, size_t size) {
CFAllocatorRef allocator = (CFAllocatorRef)zone;
return CFAllocatorReallocate(allocator, ptr, size, 0);
}
static void __CFAllocatorCustomDestroy(malloc_zone_t *zone) {
CFAllocatorRef allocator = (CFAllocatorRef)zone;
// !!! we do it, and caller of malloc_destroy_zone() assumes
// COMPLETE responsibility for the result; NO Apple library
// code should be modified as a result of discovering that
// some activity results in inconveniences to developers
// trying to use malloc_destroy_zone() with a CFAllocatorRef;
// that's just too bad for them.
__CFAllocatorDeallocate(allocator);
}
static size_t __CFAllocatorCustomGoodSize(malloc_zone_t *zone, size_t size) {
CFAllocatorRef allocator = (CFAllocatorRef)zone;
return CFAllocatorGetPreferredSizeForSize(allocator, size, 0);
}
// These should live in the malloc.h heaer file
typedef double (*Fx)(double);
typedef kern_return_t (*mit_EnumType)(task_t task, void *, unsigned type_mask, vm_address_t zone_address, memory_reader_t reader, vm_range_recorder_t recorder);
typedef size_t (*mit_GoodSize)(malloc_zone_t *zone, size_t size);
typedef boolean_t (*mit_Check)(malloc_zone_t *zone);
typedef void (*mit_Print)(malloc_zone_t *zone, boolean_t verbose);
typedef void (*mit_Log)(malloc_zone_t *zone, void *address);
typedef void (*mit_ZoneAction)(malloc_zone_t *zone);
typedef void (*mit_Statistics)(malloc_zone_t *zone, malloc_statistics_t *stats);
static struct malloc_introspection_t __CFAllocatorZoneIntrospect = {
(mit_EnumType)__CFAllocatorZoneIntrospectNoOp,
(mit_GoodSize)__CFAllocatorCustomGoodSize,
(mit_Check)__CFAllocatorZoneIntrospectTrue,
(mit_Print)__CFAllocatorZoneIntrospectNoOp,
(mit_Log)__CFAllocatorZoneIntrospectNoOp,
(mit_ZoneAction)__CFAllocatorZoneIntrospectNoOp,
(mit_ZoneAction)__CFAllocatorZoneIntrospectNoOp,
(mit_Statistics)__CFAllocatorZoneIntrospectNoOp
};
static size_t __CFAllocatorNullSize(malloc_zone_t *zone, const void *ptr) {
return 0;
}
static void * __CFAllocatorNullMalloc(malloc_zone_t *zone, size_t size) {
return NULL;
}
static void * __CFAllocatorNullCalloc(malloc_zone_t *zone, size_t num_items, size_t size) {
return NULL;
}
static void * __CFAllocatorNullValloc(malloc_zone_t *zone, size_t size) {
return NULL;
}
static void __CFAllocatorNullFree(malloc_zone_t *zone, void *ptr) {
}
static void * __CFAllocatorNullRealloc(malloc_zone_t *zone, void *ptr, size_t size) {
return NULL;
}
static void __CFAllocatorNullDestroy(malloc_zone_t *zone) {
}
static size_t __CFAllocatorNullGoodSize(malloc_zone_t *zone, size_t size) {
return size;
}
static struct malloc_introspection_t __CFAllocatorNullZoneIntrospect = {
(mit_EnumType)__CFAllocatorZoneIntrospectNoOp,
(mit_GoodSize)__CFAllocatorNullGoodSize,
(mit_Check)__CFAllocatorZoneIntrospectTrue,
(mit_Print)__CFAllocatorZoneIntrospectNoOp,
(mit_Log)__CFAllocatorZoneIntrospectNoOp,
(mit_ZoneAction)__CFAllocatorZoneIntrospectNoOp,
(mit_ZoneAction)__CFAllocatorZoneIntrospectNoOp,
(mit_Statistics)__CFAllocatorZoneIntrospectNoOp
};
static void *__CFAllocatorSystemAllocate(CFIndex size, CFOptionFlags hint, void *info) {
return malloc_zone_malloc((malloc_zone_t*)info, size);
}
static void *__CFAllocatorSystemReallocate(void *ptr, CFIndex newsize, CFOptionFlags hint, void *info) {
return malloc_zone_realloc((malloc_zone_t*)info, ptr, newsize);
}
static void __CFAllocatorSystemDeallocate(void *ptr, void *info) {
#if defined(DEBUG)
size_t size = malloc_size(ptr);
if (size) memset(ptr, 0xCC, size);
#endif
malloc_zone_free((malloc_zone_t*)info, ptr);
}
#endif /* DEPLOYMENT_TARGET_MACOSX */
#if DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
static void *__CFAllocatorSystemAllocate(CFIndex size, CFOptionFlags hint, void *info) {
return malloc(size);
}
static void *__CFAllocatorSystemReallocate(void *ptr, CFIndex newsize, CFOptionFlags hint, void *info) {
return realloc(ptr, newsize);
}
static void __CFAllocatorSystemDeallocate(void *ptr, void *info) {
#if defined(DEBUG)
const enum mcheck_status status = mprobe(ptr);
CFAssert3(status == MCHECK_OK || status == MCHECK_DISABLED,
__kCFLogAssertion, "%s: ptr %p status %d",
__PRETTY_FUNCTION__, ptr, status);
#endif
free(ptr);
}
#endif
static void *__CFAllocatorNullAllocate(CFIndex size, CFOptionFlags hint, void *info) {
return NULL;
}
static void *__CFAllocatorNullReallocate(void *ptr, CFIndex newsize, CFOptionFlags hint, void *info) {
return NULL;
}
#if defined (__cplusplus)
static void * __CFAllocatorCPPMalloc(CFIndex allocSize, CFOptionFlags hint, void *info)
{
return malloc(allocSize);
}
static void * __CFAllocatorCPPReAlloc(void *ptr, CFIndex newsize, CFOptionFlags hint, void *info)
{
return realloc(ptr, newsize);
}
static void __CFAllocatorCPPFree(void *ptr, void *info)
{
free(ptr);
}
#endif // C++
static struct __CFAllocator __kCFAllocatorMalloc = {
INIT_CFRUNTIME_BASE(),
#if DEPLOYMENT_TARGET_MACOSX
__CFAllocatorCustomSize,
__CFAllocatorCustomMalloc,
__CFAllocatorCustomCalloc,
__CFAllocatorCustomValloc,
__CFAllocatorCustomFree,
__CFAllocatorCustomRealloc,
__CFAllocatorNullDestroy,
"kCFAllocatorMalloc",
NULL,
NULL,
&__CFAllocatorZoneIntrospect,
NULL,
#endif
NULL, // _allocator
// Using the malloc functions directly is a total cheat, but works (in C)
// because the function signatures match in their common prefix of arguments.
// This saves us one hop through an adaptor function.
#if !defined (__cplusplus)
{0, NULL, NULL, NULL, NULL, (void *)malloc, (void *)realloc, (void *)free, NULL}
#else
{0, NULL, NULL, NULL, NULL, __CFAllocatorCPPMalloc,__CFAllocatorCPPReAlloc, __CFAllocatorCPPFree, NULL}
#endif // __cplusplus
};
static struct __CFAllocator __kCFAllocatorMallocZone = {
INIT_CFRUNTIME_BASE(),
#if DEPLOYMENT_TARGET_MACOSX
__CFAllocatorCustomSize,
__CFAllocatorCustomMalloc,
__CFAllocatorCustomCalloc,
__CFAllocatorCustomValloc,
__CFAllocatorCustomFree,
__CFAllocatorCustomRealloc,
__CFAllocatorNullDestroy,
"kCFAllocatorMallocZone",
NULL,
NULL,
&__CFAllocatorZoneIntrospect,
NULL,
#endif
NULL, // _allocator
{0, NULL, NULL, NULL, NULL, __CFAllocatorSystemAllocate, __CFAllocatorSystemReallocate, __CFAllocatorSystemDeallocate, NULL}
};
static struct __CFAllocator __kCFAllocatorSystemDefault = {
INIT_CFRUNTIME_BASE(),
#if DEPLOYMENT_TARGET_MACOSX
__CFAllocatorCustomSize,
__CFAllocatorCustomMalloc,
__CFAllocatorCustomCalloc,
__CFAllocatorCustomValloc,
__CFAllocatorCustomFree,
__CFAllocatorCustomRealloc,
__CFAllocatorNullDestroy,
"kCFAllocatorSystemDefault",
NULL,
NULL,
&__CFAllocatorZoneIntrospect,
NULL,
#endif
NULL, // _allocator
{0, NULL, NULL, NULL, NULL, __CFAllocatorSystemAllocate, __CFAllocatorSystemReallocate, __CFAllocatorSystemDeallocate, NULL}
};
static struct __CFAllocator __kCFAllocatorNull = {
INIT_CFRUNTIME_BASE(),
#if DEPLOYMENT_TARGET_MACOSX
__CFAllocatorNullSize,
__CFAllocatorNullMalloc,
__CFAllocatorNullCalloc,
__CFAllocatorNullValloc,
__CFAllocatorNullFree,
__CFAllocatorNullRealloc,
__CFAllocatorNullDestroy,
"kCFAllocatorNull",
NULL,
NULL,
&__CFAllocatorNullZoneIntrospect,
NULL,
#endif
NULL, // _allocator
{0, NULL, NULL, NULL, NULL, __CFAllocatorNullAllocate, __CFAllocatorNullReallocate, NULL, NULL}
};
const CFAllocatorRef kCFAllocatorDefault = NULL;
const CFAllocatorRef kCFAllocatorSystemDefault = &__kCFAllocatorSystemDefault;
const CFAllocatorRef kCFAllocatorMalloc = &__kCFAllocatorMalloc;
const CFAllocatorRef kCFAllocatorMallocZone = &__kCFAllocatorMallocZone;
const CFAllocatorRef kCFAllocatorNull = &__kCFAllocatorNull;
const CFAllocatorRef kCFAllocatorUseContext = (CFAllocatorRef)0x0257;
static CFStringRef __CFAllocatorCopyDescription(CFTypeRef cf) {
CFAllocatorRef self = (CFAllocatorRef)cf;
CFAllocatorRef allocator = (kCFAllocatorUseContext == self->_allocator) ? self : self->_allocator;
return CFStringCreateWithFormat(allocator, NULL, CFSTR("<CFAllocator %p [%p]>{info = %p}"), cf, allocator, self->_context.info);
// CF: should use copyDescription function here to describe info field
// remember to release value returned from copydescr function when this happens
}
__private_extern__ CFAllocatorRef __CFAllocatorGetAllocator(CFTypeRef cf) {
CFAllocatorRef allocator = (CFAllocatorRef)cf;
return (kCFAllocatorUseContext == allocator->_allocator) ? allocator : allocator->_allocator;
}
__private_extern__ void __CFAllocatorDeallocate(CFTypeRef cf) {
CFAllocatorRef self = (CFAllocatorRef)cf;
CFAllocatorRef allocator = self->_allocator;
CFAllocatorReleaseCallBack releaseFunc = __CFAllocatorGetReleaseFunction(&self->_context);
if (kCFAllocatorUseContext == allocator) {
/* Rather a chicken and egg problem here, so we do things
in the reverse order from what was done at create time. */
CFAllocatorDeallocateCallBack deallocateFunc = __CFAllocatorGetDeallocateFunction(&self->_context);
void *info = self->_context.info;
if (NULL != deallocateFunc) {
INVOKE_CALLBACK2(deallocateFunc, (void *)self, info);
}
if (NULL != releaseFunc) {
INVOKE_CALLBACK1(releaseFunc, info);
}
} else {
if (NULL != releaseFunc) {
INVOKE_CALLBACK1(releaseFunc, self->_context.info);
}
_CFAllocatorDeallocateGC(allocator, (void *)self);
}
}
static CFTypeID __kCFAllocatorTypeID = _kCFRuntimeNotATypeID;
static const CFRuntimeClass __CFAllocatorClass = {
0,
"CFAllocator",
NULL, // init
NULL, // copy
__CFAllocatorDeallocate,
NULL, // equal
NULL, // hash
NULL, //
__CFAllocatorCopyDescription
};
__private_extern__ void __CFAllocatorInitialize(void) {
__kCFAllocatorTypeID = _CFRuntimeRegisterClass(&__CFAllocatorClass);
_CFRuntimeSetInstanceTypeID(&__kCFAllocatorSystemDefault, __kCFAllocatorTypeID);
__kCFAllocatorSystemDefault._base._cfisa = __CFISAForTypeID(__kCFAllocatorTypeID);
#if DEPLOYMENT_TARGET_MACOSX
__kCFAllocatorSystemDefault._context.info = (CF_USING_COLLECTABLE_MEMORY ? __CFCollectableZone : malloc_default_zone());
memset(malloc_default_zone(), 0, 2 * sizeof(void *));
#endif
__kCFAllocatorSystemDefault._allocator = kCFAllocatorSystemDefault;
#ifdef DEPLOYMENT_TARGET_WINDOWS
__kCFAllocatorSystemDefault._context.allocate = __CFAllocatorSystemAllocate;
__kCFAllocatorSystemDefault._context.reallocate = __CFAllocatorSystemReallocate;
__kCFAllocatorSystemDefault._context.deallocate = __CFAllocatorSystemDeallocate;
#endif // DEPLOYMENT_TARGET_WINDOWS
_CFRuntimeSetInstanceTypeID(&__kCFAllocatorMalloc, __kCFAllocatorTypeID);
__kCFAllocatorMalloc._base._cfisa = __CFISAForTypeID(__kCFAllocatorTypeID);
__kCFAllocatorMalloc._allocator = kCFAllocatorSystemDefault;
#if DEPLOYMENT_TARGET_MACOSX
_CFRuntimeSetInstanceTypeID(&__kCFAllocatorMallocZone, __kCFAllocatorTypeID);
__kCFAllocatorMallocZone._base._cfisa = __CFISAForTypeID(__kCFAllocatorTypeID);
__kCFAllocatorMallocZone._allocator = kCFAllocatorSystemDefault;
__kCFAllocatorMallocZone._context.info = malloc_default_zone();
#endif //__MACH__
_CFRuntimeSetInstanceTypeID(&__kCFAllocatorNull, __kCFAllocatorTypeID);
__kCFAllocatorNull._base._cfisa = __CFISAForTypeID(__kCFAllocatorTypeID);
__kCFAllocatorNull._allocator = kCFAllocatorSystemDefault;
}
CFTypeID CFAllocatorGetTypeID(void) {
return __kCFAllocatorTypeID;
}
CFAllocatorRef CFAllocatorGetDefault(void) {
CFAllocatorRef allocator = (CFAllocatorRef)__CFGetThreadSpecificData_inline()->_allocator;
if (NULL == allocator) {
allocator = kCFAllocatorSystemDefault;
}
return allocator;
}
void CFAllocatorSetDefault(CFAllocatorRef allocator) {
CFAllocatorRef current = (CFAllocatorRef)__CFGetThreadSpecificData_inline()->_allocator;
#if defined(DEBUG)
if (NULL != allocator) {
__CFGenericValidateType(allocator, __kCFAllocatorTypeID);
}
#endif
#if DEPLOYMENT_TARGET_MACOSX
if (allocator && allocator->_base._cfisa != __CFISAForTypeID(__kCFAllocatorTypeID)) { // malloc_zone_t *
return; // require allocator to this function to be an allocator
}
#endif
if (NULL != allocator && allocator != current) {
if (current) CFRelease(current);
CFRetain(allocator);
// We retain an extra time so that anything set as the default
// allocator never goes away.
CFRetain(allocator);
__CFGetThreadSpecificData_inline()->_allocator = (void *)allocator;
}
}
static CFAllocatorRef __CFAllocatorCreate(CFAllocatorRef allocator, CFAllocatorContext *context) {
struct __CFAllocator *memory = NULL;
CFAllocatorRetainCallBack retainFunc;
CFAllocatorAllocateCallBack allocateFunc;
void *retainedInfo;
#if DEPLOYMENT_TARGET_MACOSX
if (allocator && kCFAllocatorUseContext != allocator && allocator->_base._cfisa != __CFISAForTypeID(__kCFAllocatorTypeID)) { // malloc_zone_t *
return NULL; // require allocator to this function to be an allocator
}
#endif
retainFunc = context->retain;
FAULT_CALLBACK((void **)&retainFunc);
allocateFunc = context->allocate;
FAULT_CALLBACK((void **)&allocateFunc);
if (NULL != retainFunc) {
retainedInfo = (void *)INVOKE_CALLBACK1(retainFunc, context->info);
} else {
retainedInfo = context->info;
}
// We don't use _CFRuntimeCreateInstance()
if (kCFAllocatorUseContext == allocator) {
memory = NULL;
if (allocateFunc) {
memory = (struct __CFAllocator *)INVOKE_CALLBACK3(allocateFunc, sizeof(struct __CFAllocator), 0, retainedInfo);
}
if (NULL == memory) {
return NULL;
}
} else {
allocator = (NULL == allocator) ? __CFGetDefaultAllocator() : allocator;
memory = (struct __CFAllocator *)CFAllocatorAllocate(allocator, sizeof(struct __CFAllocator), __kCFAllocatorGCObjectMemory);
if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFAllocator");
if (NULL == memory) {
return NULL;
}
}
memory->_base._cfisa = 0;
#if __LP64__
memory->_base._rc = 1;
#else
memory->_base._cfinfo[CF_RC_BITS] = 1;
#endif
memory->_base._cfinfo[CF_INFO_BITS] = 0;
_CFRuntimeSetInstanceTypeID(memory, __kCFAllocatorTypeID);
memory->_base._cfisa = __CFISAForTypeID(__kCFAllocatorTypeID);
#if DEPLOYMENT_TARGET_MACOSX
memory->size = __CFAllocatorCustomSize;
memory->malloc = __CFAllocatorCustomMalloc;
memory->calloc = __CFAllocatorCustomCalloc;
memory->valloc = __CFAllocatorCustomValloc;
memory->free = __CFAllocatorCustomFree;
memory->realloc = __CFAllocatorCustomRealloc;
memory->destroy = __CFAllocatorCustomDestroy;
memory->zone_name = "Custom CFAllocator";
memory->batch_malloc = NULL;
memory->batch_free = NULL;
memory->introspect = &__CFAllocatorZoneIntrospect;
memory->reserved5 = NULL;
#endif
memory->_allocator = allocator;
memory->_context.version = context->version;
memory->_context.info = retainedInfo;
memory->_context.retain = retainFunc;
memory->_context.release = context->release;
FAULT_CALLBACK((void **)&(memory->_context.release));
memory->_context.copyDescription = context->copyDescription;
FAULT_CALLBACK((void **)&(memory->_context.copyDescription));
memory->_context.allocate = allocateFunc;
memory->_context.reallocate = context->reallocate;
FAULT_CALLBACK((void **)&(memory->_context.reallocate));
memory->_context.deallocate = context->deallocate;
FAULT_CALLBACK((void **)&(memory->_context.deallocate));
memory->_context.preferredSize = context->preferredSize;
FAULT_CALLBACK((void **)&(memory->_context.preferredSize));
return memory;
}
CFAllocatorRef CFAllocatorCreate(CFAllocatorRef allocator, CFAllocatorContext *context) {
CFAssert1(!CF_USING_COLLECTABLE_MEMORY, __kCFLogAssertion, "%s(): Shouldn't be called when GC is enabled!", __PRETTY_FUNCTION__);
#if defined(DEBUG)
if (CF_USING_COLLECTABLE_MEMORY)
HALT;
#endif
return __CFAllocatorCreate(allocator, context);
}
CFAllocatorRef _CFAllocatorCreateGC(CFAllocatorRef allocator, CFAllocatorContext *context) {
return __CFAllocatorCreate(allocator, context);
}
void *CFAllocatorAllocate(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint) {
CFAllocatorAllocateCallBack allocateFunc;
void *newptr = NULL;
allocator = (NULL == allocator) ? __CFGetDefaultAllocator() : allocator;
#if (DEPLOYMENT_TARGET_MACOSX) && defined(DEBUG)
if (allocator->_base._cfisa == __CFISAForTypeID(__kCFAllocatorTypeID)) {
__CFGenericValidateType(allocator, __kCFAllocatorTypeID);
}
#else
__CFGenericValidateType(allocator, __kCFAllocatorTypeID);
#endif
if (0 == size) return NULL;
#if DEPLOYMENT_TARGET_MACOSX
if (allocator->_base._cfisa != __CFISAForTypeID(__kCFAllocatorTypeID)) { // malloc_zone_t *
return malloc_zone_malloc((malloc_zone_t *)allocator, size);
}
#endif
if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) {
newptr = auto_zone_allocate_object((auto_zone_t*)allocator->_context.info, size, CF_GET_GC_MEMORY_TYPE(hint), true, false);
} else {
newptr = NULL;
allocateFunc = __CFAllocatorGetAllocateFunction(&allocator->_context);
if (allocateFunc) {
newptr = (void *)INVOKE_CALLBACK3(allocateFunc, size, hint, allocator->_context.info);
}
}
return newptr;
}
void *CFAllocatorReallocate(CFAllocatorRef allocator, void *ptr, CFIndex newsize, CFOptionFlags hint) {
CFAllocatorAllocateCallBack allocateFunc;
CFAllocatorReallocateCallBack reallocateFunc;
CFAllocatorDeallocateCallBack deallocateFunc;
void *newptr;
allocator = (NULL == allocator) ? __CFGetDefaultAllocator() : allocator;
#if (DEPLOYMENT_TARGET_MACOSX) && defined(DEBUG)
if (allocator->_base._cfisa == __CFISAForTypeID(__kCFAllocatorTypeID)) {
__CFGenericValidateType(allocator, __kCFAllocatorTypeID);
}
#else
__CFGenericValidateType(allocator, __kCFAllocatorTypeID);
#endif
if (NULL == ptr && 0 < newsize) {
#if DEPLOYMENT_TARGET_MACOSX
if (allocator->_base._cfisa != __CFISAForTypeID(__kCFAllocatorTypeID)) { // malloc_zone_t *
return malloc_zone_malloc((malloc_zone_t *)allocator, newsize);
}
#endif
newptr = NULL;
allocateFunc = __CFAllocatorGetAllocateFunction(&allocator->_context);
if (allocateFunc) {
newptr = (void *)INVOKE_CALLBACK3(allocateFunc, newsize, hint, allocator->_context.info);
}
return newptr;
}
if (NULL != ptr && 0 == newsize) {
#if DEPLOYMENT_TARGET_MACOSX
if (allocator->_base._cfisa != __CFISAForTypeID(__kCFAllocatorTypeID)) { // malloc_zone_t *
#if defined(DEBUG)
size_t size = malloc_size(ptr);
if (size) memset(ptr, 0xCC, size);
#endif
malloc_zone_free((malloc_zone_t *)allocator, ptr);
return NULL;
}
#endif /* DEPLOYMENT_TARGET_MACOSX */
deallocateFunc = __CFAllocatorGetDeallocateFunction(&allocator->_context);
if (NULL != deallocateFunc) {
INVOKE_CALLBACK2(deallocateFunc, ptr, allocator->_context.info);
}
return NULL;
}
if (NULL == ptr && 0 == newsize) return NULL;
#if DEPLOYMENT_TARGET_MACOSX
if (allocator->_base._cfisa != __CFISAForTypeID(__kCFAllocatorTypeID)) { // malloc_zone_t *
return malloc_zone_realloc((malloc_zone_t *)allocator, ptr, newsize);
}
#endif
reallocateFunc = __CFAllocatorGetReallocateFunction(&allocator->_context);
if (NULL == reallocateFunc) return NULL;
newptr = (void *)INVOKE_CALLBACK4(reallocateFunc, ptr, newsize, hint, allocator->_context.info);
return newptr;
}
void CFAllocatorDeallocate(CFAllocatorRef allocator, void *ptr) {
CFAllocatorDeallocateCallBack deallocateFunc;
allocator = (NULL == allocator) ? __CFGetDefaultAllocator() : allocator;
#if (DEPLOYMENT_TARGET_MACOSX) && defined(DEBUG)
if (allocator->_base._cfisa == __CFISAForTypeID(__kCFAllocatorTypeID)) {
__CFGenericValidateType(allocator, __kCFAllocatorTypeID);
}
#else
__CFGenericValidateType(allocator, __kCFAllocatorTypeID);
#endif
#if DEPLOYMENT_TARGET_MACOSX
if (allocator->_base._cfisa != __CFISAForTypeID(__kCFAllocatorTypeID)) { // malloc_zone_t *
#if defined(DEBUG)
size_t size = malloc_size(ptr);
if (size) memset(ptr, 0xCC, size);
#endif
return malloc_zone_free((malloc_zone_t *)allocator, ptr);
}
#endif /* DEPLOYMENT_TARGET_MACOSX */
deallocateFunc = __CFAllocatorGetDeallocateFunction(&allocator->_context);
if (NULL != ptr && NULL != deallocateFunc) {
INVOKE_CALLBACK2(deallocateFunc, ptr, allocator->_context.info);
}
}
CFIndex CFAllocatorGetPreferredSizeForSize(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint) {
CFAllocatorPreferredSizeCallBack prefFunc;
CFIndex newsize = 0;
allocator = (NULL == allocator) ? __CFGetDefaultAllocator() : allocator;
#if (DEPLOYMENT_TARGET_MACOSX) && defined(DEBUG)
if (allocator->_base._cfisa == __CFISAForTypeID(__kCFAllocatorTypeID)) {
__CFGenericValidateType(allocator, __kCFAllocatorTypeID);
}
#else
__CFGenericValidateType(allocator, __kCFAllocatorTypeID);
#endif
#if DEPLOYMENT_TARGET_MACOSX
if (allocator->_base._cfisa != __CFISAForTypeID(__kCFAllocatorTypeID)) { // malloc_zone_t *
return malloc_good_size(size);
}
#endif
prefFunc = __CFAllocatorGetPreferredSizeFunction(&allocator->_context);
if (0 < size && NULL != prefFunc) {
newsize = (CFIndex)(INVOKE_CALLBACK3(prefFunc, size, hint, allocator->_context.info));
}
if (newsize < size) newsize = size;
return newsize;
}
void CFAllocatorGetContext(CFAllocatorRef allocator, CFAllocatorContext *context) {
allocator = (NULL == allocator) ? __CFGetDefaultAllocator() : allocator;
#if (DEPLOYMENT_TARGET_MACOSX) && defined(DEBUG)
if (allocator->_base._cfisa == __CFISAForTypeID(__kCFAllocatorTypeID)) {
__CFGenericValidateType(allocator, __kCFAllocatorTypeID);
}
#else
__CFGenericValidateType(allocator, __kCFAllocatorTypeID);
#endif
CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__);
#if DEPLOYMENT_TARGET_MACOSX
if (allocator->_base._cfisa != __CFISAForTypeID(__kCFAllocatorTypeID)) { // malloc_zone_t *
return;
}
#endif
context->version = 0;
context->info = allocator->_context.info;
context->retain = __CFAllocatorGetRetainFunction(&allocator->_context);
context->release = __CFAllocatorGetReleaseFunction(&allocator->_context);
context->copyDescription = __CFAllocatorGetCopyDescriptionFunction(&allocator->_context);
context->allocate = __CFAllocatorGetAllocateFunction(&allocator->_context);
context->reallocate = __CFAllocatorGetReallocateFunction(&allocator->_context);
context->deallocate = __CFAllocatorGetDeallocateFunction(&allocator->_context);
context->preferredSize = __CFAllocatorGetPreferredSizeFunction(&allocator->_context);
#if (DEPLOYMENT_TARGET_MACOSX) && defined(__ppc__)
context->retain = (void *)((uintptr_t)context->retain & ~0x3);
context->release = (void *)((uintptr_t)context->release & ~0x3);
context->copyDescription = (void *)((uintptr_t)context->copyDescription & ~0x3);
context->allocate = (void *)((uintptr_t)context->allocate & ~0x3);
context->reallocate = (void *)((uintptr_t)context->reallocate & ~0x3);
context->deallocate = (void *)((uintptr_t)context->deallocate & ~0x3);
context->preferredSize = (void *)((uintptr_t)context->preferredSize & ~0x3);
#endif
}
void *_CFAllocatorAllocateGC(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint)
{
if (CF_IS_COLLECTABLE_ALLOCATOR(allocator))
return auto_zone_allocate_object((auto_zone_t*)kCFAllocatorSystemDefault->_context.info, size, CF_GET_GC_MEMORY_TYPE(hint), false, false);
else
return CFAllocatorAllocate(allocator, size, hint);
}
void *_CFAllocatorReallocateGC(CFAllocatorRef allocator, void *ptr, CFIndex newsize, CFOptionFlags hint)
{
if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) {
if (ptr && (newsize == 0)) {
return NULL; // equivalent to _CFAllocatorDeallocateGC.
}
if (ptr == NULL) {
return auto_zone_allocate_object((auto_zone_t*)kCFAllocatorSystemDefault->_context.info, newsize, CF_GET_GC_MEMORY_TYPE(hint), false, false); // eq. to _CFAllocator
}
}
// otherwise, auto_realloc() now preserves layout type and refCount.
return CFAllocatorReallocate(allocator, ptr, newsize, hint);
}
void _CFAllocatorDeallocateGC(CFAllocatorRef allocator, void *ptr)
{
// when running GC, don't deallocate.
if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator)) CFAllocatorDeallocate(allocator, ptr);
}
// -------- -------- -------- -------- -------- -------- -------- --------
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
__private_extern__ pthread_key_t __CFTSDKey = (pthread_key_t)NULL;
#elif DEPLOYMENT_TARGET_WINDOWS
__private_extern__ DWORD __CFTSDKey = 0xFFFFFFFF;
#endif
extern void _CFRunLoop1(void);
// Called for each thread as it exits
__private_extern__ void __CFFinalizeThreadData(void *arg) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
__CFThreadSpecificData *tsd = (__CFThreadSpecificData *)arg;
#elif DEPLOYMENT_TARGET_WINDOWS
__CFThreadSpecificData *tsd = (__CFThreadSpecificData*)TlsGetValue(__CFTSDKey);
TlsSetValue(__CFTSDKey, NULL);
#endif
if (NULL == tsd) return;
if (tsd->_allocator) CFRelease(tsd->_allocator);
#if DEPLOYMENT_TARGET_MACOSX
_CFRunLoop1();
#endif
#if DEPLOYMENT_TARGET_WINDOWS || 0
if (tsd->_messageHook) UnhookWindowsHookEx(tsd->_messageHook);
#endif
CFAllocatorDeallocate(kCFAllocatorSystemDefault, tsd);
}
__private_extern__ __CFThreadSpecificData *__CFGetThreadSpecificData(void) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
__CFThreadSpecificData *data;
data = (__CFThreadSpecificData*)pthread_getspecific(__CFTSDKey);
if (data) {
return data;
}
data =(__CFThreadSpecificData*)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(__CFThreadSpecificData), 0);
if (__CFOASafe) __CFSetLastAllocationEventName(data, "CFUtilities (thread-data)");
memset(data, 0, sizeof(__CFThreadSpecificData));
pthread_setspecific(__CFTSDKey, data);
return data;
#elif DEPLOYMENT_TARGET_WINDOWS
__CFThreadSpecificData *data;
data = (__CFThreadSpecificData *)TlsGetValue(__CFTSDKey);
if (data) {
return data;
}
data = (__CFThreadSpecificData *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(__CFThreadSpecificData), 0);
if (__CFOASafe) __CFSetLastAllocationEventName(data, "CFUtilities (thread-data)");
memset(data, 0, sizeof(__CFThreadSpecificData));
TlsSetValue(__CFTSDKey, data);
return data;
#endif
}
__private_extern__ void __CFBaseInitialize(void) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
pthread_key_create(&__CFTSDKey, __CFFinalizeThreadData);
#elif DEPLOYMENT_TARGET_WINDOWS || 0
__CFTSDKey = TlsAlloc();
#endif
}
#if DEPLOYMENT_TARGET_WINDOWS || 0
__private_extern__ void __CFBaseCleanup(void) {
TlsFree(__CFTSDKey);
}
#endif
static CFBadErrorCallBack __CFOutOfMemoryCallBack = NULL;
CFBadErrorCallBack _CFGetOutOfMemoryErrorCallBack(void) {
return __CFOutOfMemoryCallBack;
}
void _CFSetOutOfMemoryErrorCallBack(CFBadErrorCallBack callBack) {
__CFOutOfMemoryCallBack = callBack;
}
CFRange __CFRangeMake(CFIndex loc, CFIndex len) {
CFRange range;
range.location = loc;
range.length = len;
return range;
}
struct __CFNull {
CFRuntimeBase _base;
};
static struct __CFNull __kCFNull = {
INIT_CFRUNTIME_BASE()
};
const CFNullRef kCFNull = &__kCFNull;
static CFStringRef __CFNullCopyDescription(CFTypeRef cf) {
return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFNull %p [%p]>"), cf, CFGetAllocator(cf));
}
static CFStringRef __CFNullCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
return (CFStringRef)CFRetain(CFSTR("null"));
}
static void __CFNullDeallocate(CFTypeRef cf) {
CFAssert(false, __kCFLogAssertion, "Deallocated CFNull!");
}
static CFTypeID __kCFNullTypeID = _kCFRuntimeNotATypeID;
static const CFRuntimeClass __CFNullClass = {
0,
"CFNull",
NULL, // init
NULL, // copy
__CFNullDeallocate,
NULL,
NULL,
__CFNullCopyFormattingDescription,
__CFNullCopyDescription
};
__private_extern__ void __CFNullInitialize(void) {
__kCFNullTypeID = _CFRuntimeRegisterClass(&__CFNullClass);
_CFRuntimeSetInstanceTypeID(&__kCFNull, __kCFNullTypeID);
__kCFNull._base._cfisa = __CFISAForTypeID(__kCFNullTypeID);
}
CFTypeID CFNullGetTypeID(void) {
return __kCFNullTypeID;
}
void CFCollection_non_gc_storage_error(void) { }
static int hasCFM = 0;
void _CFRuntimeSetCFMPresent(void *addr) {
hasCFM = 1;
}
#if (DEPLOYMENT_TARGET_MACOSX) && defined(__ppc__)
/* See comments below */
__private_extern__ void __CF_FAULT_CALLBACK(void **ptr) {
uintptr_t p = (uintptr_t)*ptr;
if ((0 == p) || (p & 0x1)) return;
if (0 == hasCFM || (0x90000000 <= p && p < 0xA0000000)) {
*ptr = (void *)(p | 0x1);
} else {
static CFMutableDictionaryRef cache = NULL;
static CFSpinLock_t lock = CFSpinLockInit;
uintptr_t known = ~0;
__CFSpinLock(&lock);
if (!cache || !CFDictionaryGetValueIfPresent(cache, (const void *)p, (const void **)&known)) {
if (!cache) {
cache = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, NULL);
}
Dl_info info;
known = dladdr((void *)p, &info);
CFDictionarySetValue(cache, (const void *)p, (const void *)known);
}
__CFSpinUnlock(&lock);
*ptr = (void *)(p | (known ? 0x1 : 0x3));
}
}
/*
Jump to callback function. r2 is not saved and restored
in the jump-to-CFM case, since we assume that dyld code
never uses that register and that CF is dyld.
There are three states for (ptr & 0x3):
0b00: check not yet done (or not going to be done, and is a dyld func ptr)
0b01: check done, dyld function pointer
0b11: check done, CFM tvector pointer
(but a NULL callback just stays NULL)
There may be up to 5 word-sized arguments. Floating point
arguments can be done, but count as two word arguments.
Return value can be integral or real.
*/
/* Keep this assembly at the bottom of the source file! */
__asm__ (
".text\n"
" .align 2\n"
".private_extern ___CF_INVOKE_CALLBACK\n"
"___CF_INVOKE_CALLBACK:\n"
"rlwinm r12,r3,0,0,29\n"
"andi. r0,r3,0x2\n"
"or r3,r4,r4\n"
"or r4,r5,r5\n"
"or r5,r6,r6\n"
"or r6,r7,r7\n"
"or r7,r8,r8\n"
"beq- Lcall\n"
"lwz r2,0x4(r12)\n"
"lwz r12,0x0(r12)\n"
"Lcall: mtspr ctr,r12\n"
"bctr\n");
#endif
// void __HALT(void);
#if defined(__powerpc__) || defined(__ppc__) || defined(__ppc64__)
__asm__ (
".text\n"
" .align 2\n"
#if DEPLOYMENT_TARGET_MACOSX
".private_extern ___HALT\n"
"___HALT:\n"
#elif DEPLOYMENT_TARGET_LINUX
".globl __HALT\n"
"__HALT:\n"
#else
".globl ___HALT\n"
"___HALT:\n"
#endif
" trap\n"
);
#endif
#if defined(__i386__) || defined(__x86_64__)
#if defined(_MSC_VER)
void __HALT() {
__asm int 3;
}
#else
__asm__ (
".text\n"
" .align 2, 0x90\n"
#if DEPLOYMENT_TARGET_MACOSX
".private_extern ___HALT\n"
"___HALT:\n"
#elif DEPLOYMENT_TARGET_LINUX
".globl __HALT\n"
"__HALT:\n"
#else
".globl ___HALT\n"
"___HALT:\n"
#endif
" int3\n"
);
#endif
#endif
#if defined(__arm__)
__asm__ (
".text\n"
" .align 2\n"
#if DEPLOYMENT_TARGET_MACOSX
".private_extern ___HALT\n"
"___HALT:\n"
#elif DEPLOYMENT_TARGET_LINUX
".globl __HALT\n"
"__HALT:\n"
#else
".globl ___HALT\n"
"___HALT:\n"
#endif
" bkpt\n"
);
#endif /* defined(__arm__) */
#if defined(__aarch64__)
__asm__ (
".text\n"
" .align 2\n"
#if DEPLOYMENT_TARGET_MACOSX
".private_extern ___HALT\n"
"___HALT:\n"
#elif DEPLOYMENT_TARGET_LINUX
".globl __HALT\n"
"__HALT:\n"
#else
".globl ___HALT\n"
"___HALT:\n"
#endif
" brk #0000\n"
);
#endif /* defined(__aarch64__) */