/*
 * Copyright (c) 2005 Apple Computer, 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@
 */
/*	CFRuntime.h
	Copyright (c) 1999-2005, Apple, Inc. All rights reserved.
*/

#if !defined(__COREFOUNDATION_CFRUNTIME__)
#define __COREFOUNDATION_CFRUNTIME__ 1

#include <CoreFoundation/CFBase.h>
#include <CoreFoundation/CFDictionary.h>

#if defined(__cplusplus)
extern "C" {
#endif

// GC: until we link against ObjC must use indirect functions.  Overridden in CFSetupFoundationBridging
extern bool kCFUseCollectableAllocator;
extern bool (*__CFObjCIsCollectable)(void *);
extern const void* (*__CFObjCAssignIvar)(const void *value, const void *base, const void **slot);
extern const void* (*__CFObjCStrongAssign)(const void *value, const void **slot);
extern void* (*__CFObjCMemmoveCollectable)(void *dest, const void *src, unsigned);
extern void (*__CFObjCWriteBarrierRange)(void *, unsigned);

// GC: primitives.
// is GC on?
#define CF_USING_COLLECTABLE_MEMORY (kCFUseCollectableAllocator)
// is GC on and is this the GC allocator?
#define CF_IS_COLLECTABLE_ALLOCATOR(allocator) (CF_USING_COLLECTABLE_MEMORY && (NULL == (allocator) || kCFAllocatorSystemDefault == (allocator)))
// is this allocated by the collector?
#define CF_IS_COLLECTABLE(obj) (__CFObjCIsCollectable ? __CFObjCIsCollectable((void*)obj) : false)

// XXX_PCB for generational GC support.

CF_INLINE const void* __CFAssignIvar(CFAllocatorRef allocator, const void *rvalue, const void *base, const void **lvalue) {
    if (rvalue && CF_IS_COLLECTABLE_ALLOCATOR(allocator))
        return __CFObjCAssignIvar(rvalue, base, lvalue);
    else
        return (*lvalue = rvalue);
}

CF_INLINE const void* __CFStrongAssign(CFAllocatorRef allocator, const void *rvalue, const void **lvalue) {
    if (rvalue && CF_IS_COLLECTABLE_ALLOCATOR(allocator))
        return __CFObjCStrongAssign(rvalue, lvalue);
    else
        return (*lvalue = rvalue);
}

// Use this form when the base pointer to the object is known.
#define CF_WRITE_BARRIER_BASE_ASSIGN(allocator, base, lvalue, rvalue) __CFAssignIvar(allocator, (const void*)rvalue, (const void*)base, (const void**)&(lvalue))

// Use this form when the base pointer to the object isn't known.
#define CF_WRITE_BARRIER_ASSIGN(allocator, lvalue, rvalue) __CFStrongAssign(allocator, (const void*)rvalue, (const void**)&(lvalue))

// Write-barrier memory move.
#define CF_WRITE_BARRIER_MEMMOVE(dst, src, size) __CFObjCMemmoveCollectable(dst, src, size)

// Used by frameworks to assert they "KNOW WHAT THEY'RE DOING under GC."
CF_EXPORT CFAllocatorRef _CFAllocatorCreateGC(CFAllocatorRef allocator, CFAllocatorContext *context);

// Zero-retain count CFAllocator functions, i.e. memory that will be collected, no dealloc necessary
CF_EXPORT void *_CFAllocatorAllocateGC(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint);
CF_EXPORT void *_CFAllocatorReallocateGC(CFAllocatorRef allocator, void *ptr, CFIndex newsize, CFOptionFlags hint);
CF_EXPORT void _CFAllocatorDeallocateGC(CFAllocatorRef allocator, void *ptr);

enum {
    _kCFRuntimeNotATypeID = 0,
    _kCFRuntimeScannedObject = (1 << 0)
};

typedef struct __CFRuntimeClass {	// Version 0 struct
    CFIndex version;
    const char *className;
    void (*init)(CFTypeRef cf);
    CFTypeRef (*copy)(CFAllocatorRef allocator, CFTypeRef cf);
#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED
    void (*finalize)(CFTypeRef cf);
#else
    void (*dealloc)(CFTypeRef cf);
#endif
    Boolean (*equal)(CFTypeRef cf1, CFTypeRef cf2);
    CFHashCode (*hash)(CFTypeRef cf);
    CFStringRef (*copyFormattingDesc)(CFTypeRef cf, CFDictionaryRef formatOptions);	// str with retain
    CFStringRef (*copyDebugDesc)(CFTypeRef cf);	// str with retain
} CFRuntimeClass;

/* Note that CF runtime class registration and unregistration is not currently
 * thread-safe, which should not currently be a problem, as long as unregistration
 * is done only when valid to do so.
 */

CF_EXPORT CFTypeID _CFRuntimeRegisterClass(const CFRuntimeClass * const cls);
	/* Registers a new class with the CF runtime.  Pass in a
	 * pointer to a CFRuntimeClass structure.  The pointer is
	 * remembered by the CF runtime -- the structure is NOT
	 * copied.
	 *
	 * - version field must be zero currently.
	 * - className field points to a null-terminated C string
	 *   containing only ASCII (0 - 127) characters; this field
	 *   may NOT be NULL.
	 * - init field points to a function which classes can use to
	 *   apply some generic initialization to instances as they
	 *   are created; this function is called by both
	 *   _CFRuntimeCreateInstance and _CFRuntimeInitInstance; if
	 *   this field is NULL, no function is called; the instance
	 *   has been initialized enough that the polymorphic funcs
	 *   CFGetTypeID(), CFRetain(), CFRelease(), CFGetRetainCount(),
	 *   and CFGetAllocator() are valid on it when the init
	 *   function if any is called.
	 * - finalize field points to a function which destroys an
	 *   instance when the retain count has fallen to zero; if
	 *   this is NULL, finalization does nothing. Note that if
	 *   the class-specific functions which create or initialize
	 *   instances more fully decide that a half-initialized
	 *   instance must be destroyed, the finalize function for
	 *   that class has to be able to deal with half-initialized
	 *   instances.  The finalize function should NOT destroy the
	 *   memory for the instance itself; that is done by the
	 *   CF runtime after this finalize callout returns.
	 * - equal field points to an equality-testing function; this
	 *   field may be NULL, in which case only pointer/reference
	 *   equality is performed on instances of this class. 
	 *   Pointer equality is tested, and the type IDs are checked
	 *   for equality, before this function is called (so, the
	 *   two instances are not pointer-equal but are of the same
	 *   class before this function is called).
	 * NOTE: the equal function must implement an immutable
	 *   equality relation, satisfying the reflexive, symmetric,
	 *    and transitive properties, and remains the same across
	 *   time and immutable operations (that is, if equal(A,B) at
	 *   some point, then later equal(A,B) provided neither
	 *   A or B has been mutated).
	 * - hash field points to a hash-code-computing function for
	 *   instances of this class; this field may be NULL in which
	 *   case the pointer value of an instance is converted into
	 *   a hash.
	 * NOTE: the hash function and equal function must satisfy
	 *   the relationship "equal(A,B) implies hash(A) == hash(B)";
	 *   that is, if two instances are equal, their hash codes must
	 *   be equal too. (However, the converse is not true!)
	 * - copyFormattingDesc field points to a function returning a
	 *   CFStringRef with a human-readable description of the
	 *   instance; if this is NULL, the type does not have special
	 *   human-readable string-formats.
	 * - copyDebugDesc field points to a function returning a
	 *   CFStringRef with a debugging description of the instance;
	 *   if this is NULL, a simple description is generated.
	 *
	 * This function returns _kCFRuntimeNotATypeID on failure, or
	 * on success, returns the CFTypeID for the new class.  This
	 * CFTypeID is what the class uses to allocate or initialize
	 * instances of the class. It is also returned from the
	 * conventional *GetTypeID() function, which returns the
	 * class's CFTypeID so that clients can compare the
	 * CFTypeID of instances with that of a class.
	 *
	 * The function to compute a human-readable string is very
	 * optional, and is really only interesting for classes,
	 * like strings or numbers, where it makes sense to format
	 * the instance using just its contents.
	 */

CF_EXPORT const CFRuntimeClass * _CFRuntimeGetClassWithTypeID(CFTypeID typeID);
	/* Returns the pointer to the CFRuntimeClass which was
	 * assigned the specified CFTypeID.
	 */

CF_EXPORT void _CFRuntimeUnregisterClassWithTypeID(CFTypeID typeID);
	/* Unregisters the class with the given type ID.  It is
	 * undefined whether type IDs are reused or not (expect
	 * that they will be).
	 *
	 * Whether or not unregistering the class is a good idea or
	 * not is not CF's responsibility.  In particular you must
	 * be quite sure all instances are gone, and there are no
	 * valid weak refs to such in other threads.
	 */

/* All CF "instances" start with this structure.  Never refer to
 * these fields directly -- they are for CF's use and may be added
 * to or removed or change format without warning.  Binary
 * compatibility for uses of this struct is not guaranteed from
 * release to release.
 */
typedef struct __CFRuntimeBase {
    void *_isa;
#if defined(__ppc__) || defined(__ppc64__)
    uint16_t _rc;
    uint16_t _info;
#elif defined(__i386__)
    uint16_t _info;
    uint16_t _rc;
#else
#error unknown architecture
#endif
} CFRuntimeBase;

#if defined(__ppc__) || defined(__ppc64__)
#define INIT_CFRUNTIME_BASE(isa, info, rc)	{ isa, info, rc }
#elif defined(__i386__)
#define INIT_CFRUNTIME_BASE(isa, info, rc)	{ isa, rc, info }
#else
#error unknown architecture
#endif

CF_EXPORT CFTypeRef _CFRuntimeCreateInstance(CFAllocatorRef allocator, CFTypeID typeID, uint32_t extraBytes, unsigned char *category);
	/* Creates a new CF instance of the class specified by the
	 * given CFTypeID, using the given allocator, and returns it. 
	 * If the allocator returns NULL, this function returns NULL.
	 * A CFRuntimeBase structure is initialized at the beginning
	 * of the returned instance.  extraBytes is the additional
	 * number of bytes to allocate for the instance (BEYOND that
	 * needed for the CFRuntimeBase).  If the specified CFTypeID
	 * is unknown to the CF runtime, this function returns NULL.
	 * No part of the new memory other than base header is
	 * initialized (the extra bytes are not zeroed, for example).
	 * All instances created with this function must be destroyed
	 * only through use of the CFRelease() function -- instances
	 * must not be destroyed by using CFAllocatorDeallocate()
	 * directly, even in the initialization or creation functions
	 * of a class.  Pass NULL for the category parameter.
	 */

CF_EXPORT void _CFRuntimeSetInstanceTypeID(CFTypeRef cf, CFTypeID typeID);
	/* This function changes the typeID of the given instance.
	 * If the specified CFTypeID is unknown to the CF runtime,
	 * this function does nothing.  This function CANNOT be used
	 * to initialize an instance.  It is for advanced usages such
	 * as faulting.
	 */

#if 0
// ========================= EXAMPLE =========================

// Example: EXRange -- a "range" object, which keeps the starting
//       location and length of the range. ("EX" as in "EXample").

// ---- API ----

typedef const struct __EXRange * EXRangeRef;

CFTypeID EXRangeGetTypeID(void);

EXRangeRef EXRangeCreate(CFAllocatorRef allocator, uint32_t location, uint32_t length);

uint32_t EXRangeGetLocation(EXRangeRef rangeref);
uint32_t EXRangeGetLength(EXRangeRef rangeref);


// ---- implementation ----

#include <CoreFoundation/CFBase.h>
#include <CoreFoundation/CFString.h>

struct __EXRange {
    CFRuntimeBase _base;
    uint32_t _location;
    uint32_t _length;
};

static Boolean __EXRangeEqual(CFTypeRef cf1, CFTypeRef cf2) {
    EXRangeRef rangeref1 = (EXRangeRef)cf1;
    EXRangeRef rangeref2 = (EXRangeRef)cf2;
    if (rangeref1->_location != rangeref2->_location) return false;
    if (rangeref1->_length != rangeref2->_length) return false;
    return true;
}

static CFHashCode __EXRangeHash(CFTypeRef cf) {
    EXRangeRef rangeref = (EXRangeRef)cf;
    return (CFHashCode)(rangeref->_location + rangeref->_length);
}

static CFStringRef __EXRangeCopyFormattingDesc(CFTypeRef cf, CFDictionaryRef formatOpts) {
    EXRangeRef rangeref = (EXRangeRef)cf;
    return CFStringCreateWithFormat(CFGetAllocator(rangeref), formatOpts,
		CFSTR("[%u, %u)"),
		rangeref->_location,
		rangeref->_location + rangeref->_length);
}

static CFStringRef __EXRangeCopyDebugDesc(CFTypeRef cf) {
    EXRangeRef rangeref = (EXRangeRef)cf;
    return CFStringCreateWithFormat(CFGetAllocator(rangeref), NULL,
		CFSTR("<EXRange %p [%p]>{loc = %u, len = %u}"),
		rangeref,
		CFGetAllocator(rangeref),
		rangeref->_location,
		rangeref->_length);
}

static void __EXRangeEXRangeFinalize(CFTypeRef cf) {
    EXRangeRef rangeref = (EXRangeRef)cf;
    // nothing to finalize
}

static CFTypeID _kEXRangeID = _kCFRuntimeNotATypeID;

static CFRuntimeClass _kEXRangeClass = {0};

/* Something external to this file is assumed to call this
 * before the EXRange class is used.
 */
void __EXRangeClassInitialize(void) {
    _kEXRangeClass.version = 0;
    _kEXRangeClass.className = "EXRange";
    _kEXRangeClass.init = NULL;
    _kEXRangeClass.copy = NULL;
    _kEXRangeClass.finalize = __EXRangeEXRangeFinalize;
    _kEXRangeClass.equal = __EXRangeEqual;
    _kEXRangeClass.hash = __EXRangeHash;
    _kEXRangeClass.copyFormattingDesc = __EXRangeCopyFormattingDesc;
    _kEXRangeClass.copyDebugDesc = __EXRangeCopyDebugDesc;
    _kEXRangeID = _CFRuntimeRegisterClass((const CFRuntimeClass * const)&_kEXRangeClass);
}

CFTypeID EXRangeGetTypeID(void) {
    return _kEXRangeID;
}

EXRangeRef EXRangeCreate(CFAllocatorRef allocator, uint32_t location, uint32_t length) {
    struct __EXRange *newrange;
    uint32_t extra = sizeof(struct __EXRange) - sizeof(CFRuntimeBase);
    newrange = (struct __EXRange *)_CFRuntimeCreateInstance(allocator, _kEXRangeID, extra, NULL);
    if (NULL == newrange) {
	return NULL;
    }
    newrange->_location = location;
    newrange->_length = length;
    return (EXRangeRef)newrange;
}

uint32_t EXRangeGetLocation(EXRangeRef rangeref) {
    return rangeref->_location;
}

uint32_t EXRangeGetLength(EXRangeRef rangeref) {
    return rangeref->_length;
}

#endif

#if defined(__cplusplus)
}
#endif

#endif /* ! __COREFOUNDATION_CFRUNTIME__ */
