CFNumber.c   [plain text]


/*
 * 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@
 */
/*	CFNumber.c
	Copyright 1999-2002, Apple, Inc. All rights reserved.
	Responsibility: Ali Ozer
*/

#include <CoreFoundation/CFNumber.h>
#include "CFInternal.h"
#include "CFPriv.h"
#include <math.h>
#include <float.h>

#define __CFAssertIsBoolean(cf) __CFGenericValidateType(cf, __kCFBooleanTypeID)

struct __CFBoolean {
    CFRuntimeBase _base;
};

static struct __CFBoolean __kCFBooleanTrue = {
    INIT_CFRUNTIME_BASE()
};
const CFBooleanRef kCFBooleanTrue = &__kCFBooleanTrue;

static struct __CFBoolean __kCFBooleanFalse = {
    INIT_CFRUNTIME_BASE()
};
const CFBooleanRef kCFBooleanFalse = &__kCFBooleanFalse;

static CFStringRef __CFBooleanCopyDescription(CFTypeRef cf) {
    CFBooleanRef boolean = (CFBooleanRef)cf;
    return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFBoolean %p [%p]>{value = %s}"), cf, CFGetAllocator(cf), (boolean == kCFBooleanTrue) ? "true" : "false");
}

static CFStringRef __CFBooleanCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
    CFBooleanRef boolean = (CFBooleanRef)cf;
    return (CFStringRef)CFRetain((boolean == kCFBooleanTrue) ? CFSTR("true") : CFSTR("false"));
}

static void __CFBooleanDeallocate(CFTypeRef cf) {
    CFAssert(false, __kCFLogAssertion, "Deallocated CFBoolean!");
}

static CFTypeID __kCFBooleanTypeID = _kCFRuntimeNotATypeID;

static const CFRuntimeClass __CFBooleanClass = {
    0,
    "CFBoolean",
    NULL,      // init
    NULL,      // copy
    __CFBooleanDeallocate,
    NULL,
    NULL,
    __CFBooleanCopyFormattingDescription,
    __CFBooleanCopyDescription
};

__private_extern__ void __CFBooleanInitialize(void) {
    __kCFBooleanTypeID = _CFRuntimeRegisterClass(&__CFBooleanClass);
    _CFRuntimeSetInstanceTypeID(&__kCFBooleanTrue, __kCFBooleanTypeID);
    __kCFBooleanTrue._base._cfisa = __CFISAForTypeID(__kCFBooleanTypeID);
    _CFRuntimeSetInstanceTypeID(&__kCFBooleanFalse, __kCFBooleanTypeID);
    __kCFBooleanFalse._base._cfisa = __CFISAForTypeID(__kCFBooleanTypeID);
}

CFTypeID CFBooleanGetTypeID(void) {
    return __kCFBooleanTypeID;
}

Boolean CFBooleanGetValue(CFBooleanRef boolean) {
    CF_OBJC_FUNCDISPATCH0(__kCFBooleanTypeID, Boolean, boolean, "boolValue");
    return (boolean == kCFBooleanTrue) ? true : false;
}


/*** CFNumber ***/

#define __CFAssertIsNumber(cf) __CFGenericValidateType(cf, __kCFNumberTypeID)
#define __CFAssertIsValidNumberType(type) CFAssert2((0 < type && type <= kCFNumberMaxType) || (type == kCFNumberSInt128Type), __kCFLogAssertion, "%s(): bad CFNumber type %d", __PRETTY_FUNCTION__, type);

/* The IEEE bit patterns... Also have:
0x7f800000		float +Inf
0x7fc00000		float NaN
0xff800000		float -Inf
*/
#define BITSFORDOUBLENAN	((uint64_t)0x7ff8000000000000ULL)
#define BITSFORDOUBLEPOSINF	((uint64_t)0x7ff0000000000000ULL)
#define BITSFORDOUBLENEGINF	((uint64_t)0xfff0000000000000ULL)

#if DEPLOYMENT_TARGET_MACOSX
#define FLOAT_POSITIVE_2_TO_THE_64	0x1.0p+64L
#define FLOAT_NEGATIVE_2_TO_THE_127	-0x1.0p+127L
#define FLOAT_POSITIVE_2_TO_THE_127	0x1.0p+127L
#endif

typedef struct {	// NOTE WELL: these two fields may switch position someday, do not use '= {high, low}' -style initialization
    int64_t high;
    uint64_t low;
} CFSInt128Struct;

enum {
    kCFNumberSInt128Type = 17
};

static uint8_t isNeg128(const CFSInt128Struct *in) {
    return in->high < 0;
}

static CFComparisonResult cmp128(const CFSInt128Struct *in1, const CFSInt128Struct *in2) {
    if (in1->high < in2->high) return kCFCompareLessThan;
    if (in1->high > in2->high) return kCFCompareGreaterThan;
    if (in1->low < in2->low) return kCFCompareLessThan;
    if (in1->low > in2->low) return kCFCompareGreaterThan;
    return kCFCompareEqualTo;
}

// allows out to be the same as in1 or in2
static void add128(CFSInt128Struct *out, CFSInt128Struct *in1, CFSInt128Struct *in2) {
    CFSInt128Struct tmp;
    tmp.low = in1->low + in2->low;
    tmp.high = in1->high + in2->high;
    if (UINT64_MAX - in1->low < in2->low) {
        tmp.high++;
    }
    *out = tmp;
}

// allows out to be the same as in
static void neg128(CFSInt128Struct *out, CFSInt128Struct *in) {
    uint64_t tmplow = ~in->low;
    out->low = tmplow + 1;
    out->high = ~in->high;
    if (UINT64_MAX == tmplow) {
	out->high++;
    }
}

static const CFSInt128Struct powersOf10[] = {
    { 0x4B3B4CA85A86C47ALL, 0x098A224000000000ULL },
    { 0x0785EE10D5DA46D9LL, 0x00F436A000000000ULL },
    { 0x00C097CE7BC90715LL, 0xB34B9F1000000000ULL },
    { 0x0013426172C74D82LL, 0x2B878FE800000000ULL },
    { 0x0001ED09BEAD87C0LL, 0x378D8E6400000000ULL },
    { 0x0000314DC6448D93LL, 0x38C15B0A00000000ULL },
    { 0x000004EE2D6D415BLL, 0x85ACEF8100000000ULL },
    { 0x0000007E37BE2022LL, 0xC0914B2680000000ULL },
    { 0x0000000C9F2C9CD0LL, 0x4674EDEA40000000ULL },
    { 0x00000001431E0FAELL, 0x6D7217CAA0000000ULL },
    { 0x00000000204FCE5ELL, 0x3E25026110000000ULL },
    { 0x00000000033B2E3CLL, 0x9FD0803CE8000000ULL },
    { 0x000000000052B7D2LL, 0xDCC80CD2E4000000ULL },
    { 0x0000000000084595LL, 0x161401484A000000ULL },
    { 0x000000000000D3C2LL, 0x1BCECCEDA1000000ULL },
    { 0x000000000000152DLL, 0x02C7E14AF6800000ULL },
    { 0x000000000000021ELL, 0x19E0C9BAB2400000ULL },
    { 0x0000000000000036LL, 0x35C9ADC5DEA00000ULL },
    { 0x0000000000000005LL, 0x6BC75E2D63100000ULL },
    { 0x0000000000000000LL, 0x8AC7230489E80000ULL },
    { 0x0000000000000000LL, 0x0DE0B6B3A7640000ULL },
    { 0x0000000000000000LL, 0x016345785D8A0000ULL },
    { 0x0000000000000000LL, 0x002386F26FC10000ULL },
    { 0x0000000000000000LL, 0x00038D7EA4C68000ULL },
    { 0x0000000000000000LL, 0x00005AF3107A4000ULL },
    { 0x0000000000000000LL, 0x000009184E72A000ULL },
    { 0x0000000000000000LL, 0x000000E8D4A51000ULL },
    { 0x0000000000000000LL, 0x000000174876E800ULL },
    { 0x0000000000000000LL, 0x00000002540BE400ULL },
    { 0x0000000000000000LL, 0x000000003B9ACA00ULL },
    { 0x0000000000000000LL, 0x0000000005F5E100ULL },
    { 0x0000000000000000LL, 0x0000000000989680ULL },
    { 0x0000000000000000LL, 0x00000000000F4240ULL },
    { 0x0000000000000000LL, 0x00000000000186A0ULL },
    { 0x0000000000000000LL, 0x0000000000002710ULL },
    { 0x0000000000000000LL, 0x00000000000003E8ULL },
    { 0x0000000000000000LL, 0x0000000000000064ULL },
    { 0x0000000000000000LL, 0x000000000000000AULL },
    { 0x0000000000000000LL, 0x0000000000000001ULL },
};

static const CFSInt128Struct neg_powersOf10[] = {
    { 0xB4C4B357A5793B85LL, 0xF675DDC000000000ULL },
    { 0xF87A11EF2A25B926LL, 0xFF0BC96000000000ULL },
    { 0xFF3F68318436F8EALL, 0x4CB460F000000000ULL },
    { 0xFFECBD9E8D38B27DLL, 0xD478701800000000ULL },
    { 0xFFFE12F64152783FLL, 0xC872719C00000000ULL },
    { 0xFFFFCEB239BB726CLL, 0xC73EA4F600000000ULL },
    { 0xFFFFFB11D292BEA4LL, 0x7A53107F00000000ULL },
    { 0xFFFFFF81C841DFDDLL, 0x3F6EB4D980000000ULL },
    { 0xFFFFFFF360D3632FLL, 0xB98B1215C0000000ULL },
    { 0xFFFFFFFEBCE1F051LL, 0x928DE83560000000ULL },
    { 0xFFFFFFFFDFB031A1LL, 0xC1DAFD9EF0000000ULL },
    { 0xFFFFFFFFFCC4D1C3LL, 0x602F7FC318000000ULL },
    { 0xFFFFFFFFFFAD482DLL, 0x2337F32D1C000000ULL },
    { 0xFFFFFFFFFFF7BA6ALL, 0xE9EBFEB7B6000000ULL },
    { 0xFFFFFFFFFFFF2C3DLL, 0xE43133125F000000ULL },
    { 0xFFFFFFFFFFFFEAD2LL, 0xFD381EB509800000ULL },
    { 0xFFFFFFFFFFFFFDE1LL, 0xE61F36454DC00000ULL },
    { 0xFFFFFFFFFFFFFFC9LL, 0xCA36523A21600000ULL },
    { 0xFFFFFFFFFFFFFFFALL, 0x9438A1D29CF00000ULL },
    { 0xFFFFFFFFFFFFFFFFLL, 0x7538DCFB76180000ULL },
    { 0xFFFFFFFFFFFFFFFFLL, 0xF21F494C589C0000ULL },
    { 0xFFFFFFFFFFFFFFFFLL, 0xFE9CBA87A2760000ULL },
    { 0xFFFFFFFFFFFFFFFFLL, 0xFFDC790D903F0000ULL },
    { 0xFFFFFFFFFFFFFFFFLL, 0xFFFC72815B398000ULL },
    { 0xFFFFFFFFFFFFFFFFLL, 0xFFFFA50CEF85C000ULL },
    { 0xFFFFFFFFFFFFFFFFLL, 0xFFFFF6E7B18D6000ULL },
    { 0xFFFFFFFFFFFFFFFFLL, 0xFFFFFF172B5AF000ULL },
    { 0xFFFFFFFFFFFFFFFFLL, 0xFFFFFFE8B7891800ULL },
    { 0xFFFFFFFFFFFFFFFFLL, 0xFFFFFFFDABF41C00ULL },
    { 0xFFFFFFFFFFFFFFFFLL, 0xFFFFFFFFC4653600ULL },
    { 0xFFFFFFFFFFFFFFFFLL, 0xFFFFFFFFFA0A1F00ULL },
    { 0xFFFFFFFFFFFFFFFFLL, 0xFFFFFFFFFF676980ULL },
    { 0xFFFFFFFFFFFFFFFFLL, 0xFFFFFFFFFFF0BDC0ULL },
    { 0xFFFFFFFFFFFFFFFFLL, 0xFFFFFFFFFFFE7960ULL },
    { 0xFFFFFFFFFFFFFFFFLL, 0xFFFFFFFFFFFFD8F0ULL },
    { 0xFFFFFFFFFFFFFFFFLL, 0xFFFFFFFFFFFFFC18ULL },
    { 0xFFFFFFFFFFFFFFFFLL, 0xFFFFFFFFFFFFFF9CULL },
    { 0xFFFFFFFFFFFFFFFFLL, 0xFFFFFFFFFFFFFFF6ULL },
    { 0xFFFFFFFFFFFFFFFFLL, 0xFFFFFFFFFFFFFFFFULL },
};

static void emit128(char *buffer, const CFSInt128Struct *in, Boolean forcePlus) {
    CFSInt128Struct tmp = *in;
    if (isNeg128(&tmp)) {
	neg128(&tmp, &tmp);
	*buffer++ = '-';
    } else if (forcePlus) {
	*buffer++ = '+';
    }
    Boolean doneOne = false;
    int idx;
    for (idx = 0; idx < sizeof(powersOf10) / sizeof(powersOf10[0]); idx++) {
	int count = 0;
        while (cmp128(&powersOf10[idx], &tmp) <= 0) {
	    add128(&tmp, &tmp, (CFSInt128Struct *)&neg_powersOf10[idx]);
	    count++;
	}
	if (0 != count || doneOne) {
	    *buffer++ = '0' + count;
	    doneOne = true;
	}
    }
    if (!doneOne) {
	*buffer++ = '0';
    }
    *buffer = '\0';
}

static void cvtSInt128ToFloat64(Float64 *out, const CFSInt128Struct *in) {
    // switching to a positive number results in better accuracy
    // for negative numbers close to zero, because the multiply
    // of -1 by 2^64 (scaling the Float64 high) is avoided
    Boolean wasNeg = false;
    CFSInt128Struct tmp = *in;
    if (isNeg128(&tmp)) {
	neg128(&tmp, &tmp);
	wasNeg = true;
    }
    Float64 d = (Float64)tmp.high * FLOAT_POSITIVE_2_TO_THE_64 + (Float64)tmp.low;
    if (wasNeg) d = -d;
    *out = d;
}

static void cvtFloat64ToSInt128(CFSInt128Struct *out, const Float64 *in) {
    CFSInt128Struct i;
    Float64 d = *in;
    if (d < FLOAT_NEGATIVE_2_TO_THE_127) {
	i.high = 0x8000000000000000LL;
	i.low = 0x0000000000000000ULL;
	*out = i;
	return;
    }
    if (FLOAT_POSITIVE_2_TO_THE_127<= d) {
	i.high = 0x7fffffffffffffffLL;
	i.low = 0xffffffffffffffffULL;
	*out = i;
	return;
    }
    Float64 t = floor(d / FLOAT_POSITIVE_2_TO_THE_64);
    i.high = (int64_t)t;
    i.low = (uint64_t)(d - t * FLOAT_POSITIVE_2_TO_THE_64);
    *out = i;
}

struct __CFNumber {
    CFRuntimeBase _base;
    uint64_t _pad; // need this space here for the constant objects
    /* 0 or 8 more bytes allocated here */
};

/* Seven bits in base:
    Bits 6..5: unused
    Bits 4..0: CFNumber type
*/

static struct __CFNumber __kCFNumberNaN = {
    INIT_CFRUNTIME_BASE(), 0ULL
};
const CFNumberRef kCFNumberNaN = &__kCFNumberNaN;

static struct __CFNumber __kCFNumberNegativeInfinity = {
    INIT_CFRUNTIME_BASE(), 0ULL
};
const CFNumberRef kCFNumberNegativeInfinity = &__kCFNumberNegativeInfinity;

static struct __CFNumber __kCFNumberPositiveInfinity = {
    INIT_CFRUNTIME_BASE(), 0ULL
};
const CFNumberRef kCFNumberPositiveInfinity = &__kCFNumberPositiveInfinity;

static const struct {
    uint16_t canonicalType:5;	// canonical fixed-width type
    uint16_t floatBit:1;	// is float
    uint16_t storageBit:1;	// storage size (0: (float ? 4 : 8), 1: (float ? 8 : 16) bits)
    uint16_t lgByteSize:3;	// base-2 log byte size of public type
    uint16_t unused:6;
} __CFNumberTypeTable[] = {
    /* 0 */			{0, 0, 0, 0},

    /* kCFNumberSInt8Type */	{kCFNumberSInt8Type, 0, 0, 0, 0},
    /* kCFNumberSInt16Type */	{kCFNumberSInt16Type, 0, 0, 1, 0},
    /* kCFNumberSInt32Type */	{kCFNumberSInt32Type, 0, 0, 2, 0},
    /* kCFNumberSInt64Type */	{kCFNumberSInt64Type, 0, 0, 3, 0},
    /* kCFNumberFloat32Type */	{kCFNumberFloat32Type, 1, 0, 2, 0},
    /* kCFNumberFloat64Type */	{kCFNumberFloat64Type, 1, 1, 3, 0},

    /* kCFNumberCharType */	{kCFNumberSInt8Type, 0, 0, 0, 0},
    /* kCFNumberShortType */	{kCFNumberSInt16Type, 0, 0, 1, 0},
    /* kCFNumberIntType */	{kCFNumberSInt32Type, 0, 0, 2, 0},
#if __LP64__
    /* kCFNumberLongType */	{kCFNumberSInt64Type, 0, 0, 3, 0},
#else
    /* kCFNumberLongType */	{kCFNumberSInt32Type, 0, 0, 2, 0},
#endif
    /* kCFNumberLongLongType */	{kCFNumberSInt64Type, 0, 0, 3, 0},
    /* kCFNumberFloatType */	{kCFNumberFloat32Type, 1, 0, 2, 0},
    /* kCFNumberDoubleType */	{kCFNumberFloat64Type, 1, 1, 3, 0},

#if __LP64__
    /* kCFNumberCFIndexType */	{kCFNumberSInt64Type, 0, 0, 3, 0},
    /* kCFNumberNSIntegerType */ {kCFNumberSInt64Type, 0, 0, 3, 0},
    /* kCFNumberCGFloatType */	{kCFNumberFloat64Type, 1, 1, 3, 0},
#else
    /* kCFNumberCFIndexType */	{kCFNumberSInt32Type, 0, 0, 2, 0},
    /* kCFNumberNSIntegerType */ {kCFNumberSInt32Type, 0, 0, 2, 0},
    /* kCFNumberCGFloatType */	{kCFNumberFloat32Type, 1, 0, 2, 0},
#endif

    /* kCFNumberSInt128Type */	{kCFNumberSInt128Type, 0, 1, 4, 0},
};

CF_INLINE CFNumberType __CFNumberGetType(CFNumberRef num) {
    return __CFBitfieldGetValue(num->_base._cfinfo[CF_INFO_BITS], 4, 0);
}

#define CVT(SRC_TYPE, DST_TYPE, DST_MIN, DST_MAX) do { \
	SRC_TYPE sv; memmove(&sv, data, sizeof(SRC_TYPE)); \
	DST_TYPE dv = (sv < DST_MIN) ? (DST_TYPE)DST_MIN : (DST_TYPE)(((DST_MAX < sv) ? DST_MAX : sv)); \
	memmove(valuePtr, &dv, sizeof(DST_TYPE)); \
	SRC_TYPE vv = (SRC_TYPE)dv; return (vv == sv); \
	} while (0)

#define CVT128ToInt(SRC_TYPE, DST_TYPE, DST_MIN, DST_MAX) do { \
        SRC_TYPE sv; memmove(&sv, data, sizeof(SRC_TYPE)); \
	DST_TYPE dv; Boolean noLoss = false; \
	if (0 < sv.high || (0 == sv.high && (int64_t)DST_MAX < sv.low)) { \
	    dv = DST_MAX; \
	} else if (sv.high < -1 || (-1 == sv.high && sv.low < (int64_t)DST_MIN)) { \
	    dv = DST_MIN; \
	} else { \
	    dv = (DST_TYPE)sv.low; \
	    noLoss = true; \
	} \
        memmove(valuePtr, &dv, sizeof(DST_TYPE)); \
        return noLoss; \
        } while (0)

// returns false if the output value is not the same as the number's value, which
// can occur due to accuracy loss and the value not being within the target range
static Boolean __CFNumberGetValue(CFNumberRef number, CFNumberType type, void *valuePtr) {
    type = __CFNumberTypeTable[type].canonicalType;
    CFNumberType ntype = __CFNumberGetType(number);
    const void *data = &(number->_pad);
    switch (type) {
    case kCFNumberSInt8Type:
	if (__CFNumberTypeTable[ntype].floatBit) {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
		CVT(Float32, int8_t, INT8_MIN, INT8_MAX);
	    } else {
		CVT(Float64, int8_t, INT8_MIN, INT8_MAX);
	    }
	} else {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
		CVT(int64_t, int8_t, INT8_MIN, INT8_MAX);
	    } else {
		CVT128ToInt(CFSInt128Struct, int8_t, INT8_MIN, INT8_MAX);
	    }
	}
	return true;
    case kCFNumberSInt16Type:
	if (__CFNumberTypeTable[ntype].floatBit) {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
                CVT(Float32, int16_t, INT16_MIN, INT16_MAX);
	    } else {
                CVT(Float64, int16_t, INT16_MIN, INT16_MAX);
	    }
	} else {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
                CVT(int64_t, int16_t, INT16_MIN, INT16_MAX);
	    } else {
		CVT128ToInt(CFSInt128Struct, int16_t, INT16_MIN, INT16_MAX);
	    }
	}
	return true;
    case kCFNumberSInt32Type:
	if (__CFNumberTypeTable[ntype].floatBit) {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
                CVT(Float32, int32_t, INT32_MIN, INT32_MAX);
	    } else {
                CVT(Float64, int32_t, INT32_MIN, INT32_MAX);
	    }
	} else {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
                CVT(int64_t, int32_t, INT32_MIN, INT32_MAX);
	    } else {
		CVT128ToInt(CFSInt128Struct, int32_t, INT32_MIN, INT32_MAX);
	    }
	}
	return true;
    case kCFNumberSInt64Type:
	if (__CFNumberTypeTable[ntype].floatBit) {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
                CVT(Float32, int64_t, INT64_MIN, INT64_MAX);
	    } else {
                CVT(Float64, int64_t, INT64_MIN, INT64_MAX);
	    }
	} else {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
		memmove(valuePtr, data, 8);
	    } else {
		CVT128ToInt(CFSInt128Struct, int64_t, INT64_MIN, INT64_MAX);
	    }
	}
	return true;
    case kCFNumberSInt128Type:
	if (__CFNumberTypeTable[ntype].floatBit) {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
		Float32 f;
		memmove(&f, data, 4);
		Float64 d = f;
		CFSInt128Struct i;
		cvtFloat64ToSInt128(&i, &d);
		memmove(valuePtr, &i, 16);
		Float64 d2;
		cvtSInt128ToFloat64(&d2, &i);
		Float32 f2 = (Float32)d2;
		return (f2 == f);
	    } else {
		Float64 d;
		memmove(&d, data, 8);
		CFSInt128Struct i;
		cvtFloat64ToSInt128(&i, &d);
		memmove(valuePtr, &i, 16);
		Float64 d2;
		cvtSInt128ToFloat64(&d2, &i);
		return (d2 == d);
	    }
	} else {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
		int64_t j;
		memmove(&j, data, 8);
		CFSInt128Struct i;
		i.low = j;
		i.high = (j < 0) ? -1LL : 0LL;
		memmove(valuePtr, &i, 16);
	    } else {
		memmove(valuePtr, data, 16);
	    }
	}
	return true;
    case kCFNumberFloat32Type:
	if (__CFNumberTypeTable[ntype].floatBit) {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
		memmove(valuePtr, data, 4);
	    } else {
		double d;
		memmove(&d, data, 8);
		if (isnan(d)) {
		    uint32_t l = 0x7fc00000;
		    memmove(valuePtr, &l, 4);
		    return true;
		} else if (isinf(d)) {
		    uint32_t l = 0x7f800000;
		    if (d < 0.0) l += 0x80000000UL;
		    memmove(valuePtr, &l, 4);
		    return true;
		}
		CVT(Float64, Float32, -FLT_MAX, FLT_MAX);
	    }
	} else {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
		CVT(int64_t, Float32, -FLT_MAX, FLT_MAX);
	    } else {
		CFSInt128Struct i;
		memmove(&i, data, 16);
		Float64 d;
		cvtSInt128ToFloat64(&d, &i);
		Float32 f = (Float32)d;
		memmove(valuePtr, &f, 4);
		d = f;
		CFSInt128Struct i2;
		cvtFloat64ToSInt128(&i2, &d);
		return cmp128(&i2, &i) == kCFCompareEqualTo;
	    }
	}
	return true;
    case kCFNumberFloat64Type:
	if (__CFNumberTypeTable[ntype].floatBit) {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
		float f;
		memmove(&f, data, 4);
		if (isnan(f)) {
		    uint64_t l = BITSFORDOUBLENAN;
		    memmove(valuePtr, &l, 8);
		    return true;
		} else if (isinf(f)) {
		    uint64_t l = BITSFORDOUBLEPOSINF;
		    if (f < 0.0) l += 0x8000000000000000ULL;
		    memmove(valuePtr, &l, 8);
		    return true;
		}
		CVT(Float32, Float64, -DBL_MAX, DBL_MAX);
	    } else {
		memmove(valuePtr, data, 8);
	    }
	} else {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
		CVT(int64_t, Float64, -DBL_MAX, DBL_MAX);
	    } else {
                CFSInt128Struct i;
                memmove(&i, data, 16);
                Float64 d;
                cvtSInt128ToFloat64(&d, &i);
                memmove(valuePtr, &d, 8);
                CFSInt128Struct i2;
                cvtFloat64ToSInt128(&i2, &d);
                return cmp128(&i2, &i) == kCFCompareEqualTo;
	    }
	}
	return true;
    }
    return false;
}

#define CVT_COMPAT(SRC_TYPE, DST_TYPE, FT) do { \
	SRC_TYPE sv; memmove(&sv, data, sizeof(SRC_TYPE)); \
	DST_TYPE dv = (DST_TYPE)(sv); \
	memmove(valuePtr, &dv, sizeof(DST_TYPE)); \
	SRC_TYPE vv = (SRC_TYPE)dv; return (FT) || (vv == sv); \
	} while (0)

#define CVT128ToInt_COMPAT(SRC_TYPE, DST_TYPE) do { \
        SRC_TYPE sv; memmove(&sv, data, sizeof(SRC_TYPE)); \
	DST_TYPE dv; dv = (DST_TYPE)sv.low; \
        memmove(valuePtr, &dv, sizeof(DST_TYPE)); \
	uint64_t vv = (uint64_t)dv; return (vv == sv.low); \
        } while (0)

// this has the old cast-style behavior
static Boolean __CFNumberGetValueCompat(CFNumberRef number, CFNumberType type, void *valuePtr) {
    type = __CFNumberTypeTable[type].canonicalType;
    CFNumberType ntype = __CFNumberGetType(number);
    const void *data = &(number->_pad);
    switch (type) {
    case kCFNumberSInt8Type:
	if (__CFNumberTypeTable[ntype].floatBit) {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
		CVT_COMPAT(Float32, int8_t, 0);
	    } else {
		CVT_COMPAT(Float64, int8_t, 0);
	    }
	} else {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
                CVT_COMPAT(int64_t, int8_t, 1);
	    } else {
		CVT128ToInt_COMPAT(CFSInt128Struct, int8_t);
	    }
	}
	return true;
    case kCFNumberSInt16Type:
	if (__CFNumberTypeTable[ntype].floatBit) {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
		CVT_COMPAT(Float32, int16_t, 0);
	    } else {
		CVT_COMPAT(Float64, int16_t, 0);
	    }
	} else {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
                CVT_COMPAT(int64_t, int16_t, 1);
	    } else {
		CVT128ToInt_COMPAT(CFSInt128Struct, int16_t);
	    }
	}
	return true;
    case kCFNumberSInt32Type:
	if (__CFNumberTypeTable[ntype].floatBit) {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
		CVT_COMPAT(Float32, int32_t, 0);
	    } else {
		CVT_COMPAT(Float64, int32_t, 0);
	    }
	} else {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
                CVT_COMPAT(int64_t, int32_t, 0);
	    } else {
		CVT128ToInt_COMPAT(CFSInt128Struct, int32_t);
	    }
	}
	return true;
    case kCFNumberSInt64Type:
	if (__CFNumberTypeTable[ntype].floatBit) {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
		CVT_COMPAT(Float32, int64_t, 0);
	    } else {
		CVT_COMPAT(Float64, int64_t, 0);
	    }
	} else {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
                CVT_COMPAT(int64_t, int64_t, 0);
	    } else {
		CVT128ToInt_COMPAT(CFSInt128Struct, int64_t);
	    }
	}
	return true;
    case kCFNumberSInt128Type:
	if (__CFNumberTypeTable[ntype].floatBit) {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
		Float32 f;
		memmove(&f, data, 4);
		Float64 d = f;
		CFSInt128Struct i;
		cvtFloat64ToSInt128(&i, &d);
		memmove(valuePtr, &i, 16);
		Float64 d2;
		cvtSInt128ToFloat64(&d2, &i);
		Float32 f2 = (Float32)d2;
		return (f2 == f);
	    } else {
		Float64 d;
		memmove(&d, data, 8);
		CFSInt128Struct i;
		cvtFloat64ToSInt128(&i, &d);
		memmove(valuePtr, &i, 16);
		Float64 d2;
		cvtSInt128ToFloat64(&d2, &i);
		return (d2 == d);
	    }
	} else {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
		int64_t j;
		memmove(&j, data, 8);
		CFSInt128Struct i;
		i.low = j;
		i.high = (j < 0) ? -1LL : 0LL;
		memmove(valuePtr, &i, 16);
	    } else {
		memmove(valuePtr, data, 16);
	    }
	}
	return true;
    case kCFNumberFloat32Type:
	if (__CFNumberTypeTable[ntype].floatBit) {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
		memmove(valuePtr, data, 4);
	    } else {
		CVT_COMPAT(Float64, Float32, 0);
	    }
	} else {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
		CVT_COMPAT(int64_t, Float32, 0);
	    } else {
		CFSInt128Struct i;
		memmove(&i, data, 16);
		Float64 d;
		cvtSInt128ToFloat64(&d, &i);
		Float32 f = (Float32)d;
		memmove(valuePtr, &f, 4);
		d = f;
		CFSInt128Struct i2;
		cvtFloat64ToSInt128(&i2, &d);
		return cmp128(&i2, &i) == kCFCompareEqualTo;
	    }
	}
	return true;
    case kCFNumberFloat64Type:
	if (__CFNumberTypeTable[ntype].floatBit) {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
		CVT_COMPAT(Float32, Float64, 0);
	    } else {
		memmove(valuePtr, data, 8);
	    }
	} else {
	    if (0 == __CFNumberTypeTable[ntype].storageBit) {
		CVT_COMPAT(int64_t, Float64, 0);
	    } else {
		CFSInt128Struct i;
		memmove(&i, data, 16);
		Float64 d;
		cvtSInt128ToFloat64(&d, &i);
		memmove(valuePtr, &d, 8);
		CFSInt128Struct i2;
		cvtFloat64ToSInt128(&i2, &d);
		return cmp128(&i2, &i) == kCFCompareEqualTo;
	    }
	}
	return true;
    }
    return false;
}

static CFStringRef __CFNumberCopyDescription(CFTypeRef cf) {
    CFNumberRef number = (CFNumberRef)cf;
    CFNumberType type = __CFNumberGetType(number);
    CFMutableStringRef mstr = CFStringCreateMutable(kCFAllocatorSystemDefault, 0);
    CFStringAppendFormat(mstr, NULL, CFSTR("<CFNumber %p [%p]>{value = "), cf, CFGetAllocator(cf));
    if (__CFNumberTypeTable[type].floatBit) {
	Float64 d;
        __CFNumberGetValue(number, kCFNumberFloat64Type, &d);
	if (isnan(d)) {
	    CFStringAppend(mstr, CFSTR("nan"));
	} else if (isinf(d)) {
	    CFStringAppend(mstr, (0.0 < d) ? CFSTR("+infinity") : CFSTR("-infinity"));
	} else if (0.0 == d) {
	    CFStringAppend(mstr, (copysign(1.0, d) < 0.0) ? CFSTR("-0.0") : CFSTR("+0.0"));
	} else {
	    CFStringAppendFormat(mstr, NULL, CFSTR("%+.*f"), (__CFNumberTypeTable[type].storageBit ? 20 : 10), d);
	}
	const char *typeName = "unknown float";
	switch (type) {
	case kCFNumberFloat32Type: typeName = "kCFNumberFloat32Type"; break;
	case kCFNumberFloat64Type: typeName = "kCFNumberFloat64Type"; break;
	}
	CFStringAppendFormat(mstr, NULL, CFSTR(", type = %s}"), typeName);
    } else {
	CFSInt128Struct i;
	__CFNumberGetValue(number, kCFNumberSInt128Type, &i);
	char buffer[128];
	emit128(buffer, &i, true);
	const char *typeName = "unknown integer";
	switch (type) {
	case kCFNumberSInt8Type: typeName = "kCFNumberSInt8Type"; break;
	case kCFNumberSInt16Type: typeName = "kCFNumberSInt16Type"; break;
	case kCFNumberSInt32Type: typeName = "kCFNumberSInt32Type"; break;
	case kCFNumberSInt64Type: typeName = "kCFNumberSInt64Type"; break;
	case kCFNumberSInt128Type: typeName = "kCFNumberSInt128Type"; break;
	}
	CFStringAppendFormat(mstr, NULL, CFSTR("%s, type = %s}"), buffer, typeName);
    }
    return mstr;
}

// This function separated out from __CFNumberCopyFormattingDescription() so the plist creation can use it as well.

static CFStringRef __CFNumberCopyFormattingDescriptionAsFloat64_new(CFTypeRef cf) {
    Float64 d;
    CFNumberGetValue((CFNumberRef)cf, kCFNumberFloat64Type, &d);
    if (isnan(d)) {
	return (CFStringRef)CFRetain(CFSTR("nan"));
    }
    if (isinf(d)) {
	return (CFStringRef)CFRetain((0.0 < d) ? CFSTR("+infinity") : CFSTR("-infinity"));
    }
    if (0.0 == d) {
	return (CFStringRef)CFRetain(CFSTR("0.0"));
    }
    // if %g is used here, need to use DBL_DIG + 2 on Mac OS X, but %f needs +1
    return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%.*g"), DBL_DIG + 2, d);
}

__private_extern__ CFStringRef __CFNumberCopyFormattingDescriptionAsFloat64(CFTypeRef cf) {
    CFStringRef result = __CFNumberCopyFormattingDescriptionAsFloat64_new(cf);
    return result;
}

static CFStringRef __CFNumberCopyFormattingDescription_new(CFTypeRef cf, CFDictionaryRef formatOptions) {
    CFNumberRef number = (CFNumberRef)cf;
    CFNumberType type = __CFNumberGetType(number);
    if (__CFNumberTypeTable[type].floatBit) {
        return __CFNumberCopyFormattingDescriptionAsFloat64(number);
    }
    CFSInt128Struct i;
    __CFNumberGetValue(number, kCFNumberSInt128Type, &i);
    char buffer[128];
    emit128(buffer, &i, false);
    return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%s"), buffer);
}

static CFStringRef __CFNumberCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
    CFStringRef result = __CFNumberCopyFormattingDescription_new(cf, formatOptions);
    return result;
}


static Boolean __CFNumberEqual(CFTypeRef cf1, CFTypeRef cf2) {
    Boolean b = CFNumberCompare((CFNumberRef)cf1, (CFNumberRef)cf2, 0) == kCFCompareEqualTo;
    return b;
}

static CFHashCode __CFNumberHash(CFTypeRef cf) {
    CFHashCode h;
    CFNumberRef number = (CFNumberRef)cf;
    switch (__CFNumberGetType(number)) {
	case kCFNumberSInt8Type:
	case kCFNumberSInt16Type:
	case kCFNumberSInt32Type: {
	    SInt32 i;
	    __CFNumberGetValue(number, kCFNumberSInt32Type, &i);
	    h = _CFHashInt(i);
	    break;
	}
	default: {
	    Float64 d;
	    __CFNumberGetValue(number, kCFNumberFloat64Type, &d);
	    h = _CFHashDouble((double)d);
	    break;
	}
    }
    return h;
}

static CFTypeID __kCFNumberTypeID = _kCFRuntimeNotATypeID;

static const CFRuntimeClass __CFNumberClass = {
    0,
    "CFNumber",
    NULL,      // init
    NULL,      // copy
    NULL,
    __CFNumberEqual,
    __CFNumberHash,
    __CFNumberCopyFormattingDescription,
    __CFNumberCopyDescription
};

__private_extern__ void __CFNumberInitialize(void) {
    __kCFNumberTypeID = _CFRuntimeRegisterClass(&__CFNumberClass);

    _CFRuntimeSetInstanceTypeID(&__kCFNumberNaN, __kCFNumberTypeID);
    __kCFNumberNaN._base._cfisa = __CFISAForTypeID(__kCFNumberTypeID);
    __CFBitfieldSetValue(__kCFNumberNaN._base._cfinfo[CF_INFO_BITS], 4, 0, kCFNumberFloat64Type);
    __kCFNumberNaN._pad = BITSFORDOUBLENAN;

    _CFRuntimeSetInstanceTypeID(& __kCFNumberNegativeInfinity, __kCFNumberTypeID);
    __kCFNumberNegativeInfinity._base._cfisa = __CFISAForTypeID(__kCFNumberTypeID);
    __CFBitfieldSetValue(__kCFNumberNegativeInfinity._base._cfinfo[CF_INFO_BITS], 4, 0, kCFNumberFloat64Type);
    __kCFNumberNegativeInfinity._pad = BITSFORDOUBLENEGINF;

    _CFRuntimeSetInstanceTypeID(& __kCFNumberPositiveInfinity, __kCFNumberTypeID);
    __kCFNumberPositiveInfinity._base._cfisa = __CFISAForTypeID(__kCFNumberTypeID);
    __CFBitfieldSetValue(__kCFNumberPositiveInfinity._base._cfinfo[CF_INFO_BITS], 4, 0, kCFNumberFloat64Type);
    __kCFNumberPositiveInfinity._pad = BITSFORDOUBLEPOSINF;
}

CFTypeID CFNumberGetTypeID(void) {
    return __kCFNumberTypeID;
}

#define MinCachedInt (-1)
#define MaxCachedInt (12)
#define NotToBeCached (MinCachedInt - 1)
static CFNumberRef __CFNumberCache[MaxCachedInt - MinCachedInt + 1] = {NULL};	// Storing CFNumberRefs for range MinCachedInt..MaxCachedInt

CFNumberRef CFNumberCreate(CFAllocatorRef allocator, CFNumberType type, const void *valuePtr) {
    __CFAssertIsValidNumberType(type);
//printf("+ [%p] CFNumberCreate(%p, %d, %p)\n", pthread_self(), allocator, type, valuePtr);

    // Look for cases where we can return a cached instance.
    // We only use cached objects if the allocator is the system
    // default allocator, except for the special floating point
    // constant objects, where we return the cached object
    // regardless of allocator, since that is what has always
    // been done (and now must for compatibility).
    if (!allocator) allocator = __CFGetDefaultAllocator();
    int64_t valToBeCached = NotToBeCached;

    if (__CFNumberTypeTable[type].floatBit) {
	CFNumberRef cached = NULL;
	if (0 == __CFNumberTypeTable[type].storageBit) {
	    Float32 f = *(Float32 *)valuePtr;
	    if (isnan(f)) cached = kCFNumberNaN;
	    if (isinf(f)) cached = (f < 0.0) ? kCFNumberNegativeInfinity : kCFNumberPositiveInfinity;
	} else {
            Float64 d = *(Float64 *)valuePtr;
	    if (isnan(d)) cached = kCFNumberNaN;
	    if (isinf(d)) cached = (d < 0.0) ? kCFNumberNegativeInfinity : kCFNumberPositiveInfinity;
	}
	if (cached) return (CFNumberRef)CFRetain(cached);
    } else if (kCFAllocatorSystemDefault == allocator) {
	switch (__CFNumberTypeTable[type].canonicalType) {
	case kCFNumberSInt8Type:   {int8_t  val = *(int8_t *)valuePtr;  if (MinCachedInt <= val && val <= MaxCachedInt) valToBeCached = (int64_t)val; break;}
	case kCFNumberSInt16Type:  {int16_t val = *(int16_t *)valuePtr; if (MinCachedInt <= val && val <= MaxCachedInt) valToBeCached = (int64_t)val; break;}
	case kCFNumberSInt32Type:  {int32_t val = *(int32_t *)valuePtr; if (MinCachedInt <= val && val <= MaxCachedInt) valToBeCached = (int64_t)val; break;}
	case kCFNumberSInt64Type:  {int64_t val = *(int64_t *)valuePtr; if (MinCachedInt <= val && val <= MaxCachedInt) valToBeCached = (int64_t)val; break;}
	}
	if (NotToBeCached != valToBeCached) {
	    CFNumberRef cached = __CFNumberCache[valToBeCached - MinCachedInt];	    // Atomic to access the value in the cache
	    if (NULL != cached) return (CFNumberRef)CFRetain(cached);
	}
    }

    CFIndex size = 8 + ((!__CFNumberTypeTable[type].floatBit && __CFNumberTypeTable[type].storageBit) ? 8 : 0);
    CFNumberRef result = (CFNumberRef)_CFRuntimeCreateInstance(allocator, __kCFNumberTypeID, size, NULL);
    if (NULL == result) {
	return NULL;
    }
    __CFBitfieldSetValue(((struct __CFNumber *)result)->_base._cfinfo[CF_INFO_BITS], 4, 0, (uint8_t)__CFNumberTypeTable[type].canonicalType);


    // for a value to be cached, we already have the value handy
    if (NotToBeCached != valToBeCached) {
	memmove((void *)&result->_pad, &valToBeCached, 8);
	// Put this in the cache unless the cache is already filled (by another thread).  If we do put it in the cache, retain it an extra time for the cache.
	// Note that we don't bother freeing this result and returning the cached value if the cache was filled, since cached CFNumbers are not guaranteed unique.
	// Barrier assures that the number that is placed in the cache is properly formed.
	CFNumberType origType = __CFNumberGetType(result);
	// Force all cached numbers to have the same type, so that the type does not
	// depend on the order and original type in/with which the numbers are created.
	// Forcing the type AFTER it was cached would cause a race condition with other
	// threads pulling the number object out of the cache and using it.
	__CFBitfieldSetValue(((struct __CFNumber *)result)->_base._cfinfo[CF_INFO_BITS], 4, 0, (uint8_t)kCFNumberSInt32Type);
	if (OSAtomicCompareAndSwapPtrBarrier(NULL, (void *)result, (void *volatile *)&__CFNumberCache[valToBeCached - MinCachedInt])) {
	    CFRetain(result);
	} else {
	    // Did not cache the number object, put original type back.
	    __CFBitfieldSetValue(((struct __CFNumber *)result)->_base._cfinfo[CF_INFO_BITS], 4, 0, (uint8_t)origType);
	}
	return result;
    }

    uint64_t value;
    switch (__CFNumberTypeTable[type].canonicalType) {
    case kCFNumberSInt8Type:   value = (uint64_t)(int64_t)*(int8_t *)valuePtr; goto smallVal;
    case kCFNumberSInt16Type:  value = (uint64_t)(int64_t)*(int16_t *)valuePtr; goto smallVal;
    case kCFNumberSInt32Type:  value = (uint64_t)(int64_t)*(int32_t *)valuePtr; goto smallVal;
			smallVal: memmove((void *)&result->_pad, &value, 8); break;
    case kCFNumberSInt64Type:  memmove((void *)&result->_pad, valuePtr, 8); break;
    case kCFNumberSInt128Type: memmove((void *)&result->_pad, valuePtr, 16); break;
    case kCFNumberFloat32Type: memmove((void *)&result->_pad, valuePtr, 4); break;
    case kCFNumberFloat64Type: memmove((void *)&result->_pad, valuePtr, 8); break;
    }
//printf("  => %p\n", result);
    return result;
}

CFNumberType CFNumberGetType(CFNumberRef number) {
//printf("+ [%p] CFNumberGetType(%p)\n", pthread_self(), number);
    CF_OBJC_FUNCDISPATCH0(__kCFNumberTypeID, CFNumberType, number, "_cfNumberType");
    __CFAssertIsNumber(number);
    CFNumberType type = __CFNumberGetType(number);
    if (kCFNumberSInt128Type == type) type = kCFNumberSInt64Type; // must hide this type, since it is not public
//printf("  => %d\n", type);
    return type;
}

CFNumberType _CFNumberGetType2(CFNumberRef number) {
    __CFAssertIsNumber(number);
    return __CFNumberGetType(number);
}

CFIndex CFNumberGetByteSize(CFNumberRef number) {
//printf("+ [%p] CFNumberGetByteSize(%p)\n", pthread_self(), number);
    __CFAssertIsNumber(number);
    CFIndex r = 1 << __CFNumberTypeTable[CFNumberGetType(number)].lgByteSize;
//printf("  => %d\n", r);
    return r;
}

Boolean CFNumberIsFloatType(CFNumberRef number) {
//printf("+ [%p] CFNumberIsFloatType(%p)\n", pthread_self(), number);
    __CFAssertIsNumber(number);
    Boolean r = __CFNumberTypeTable[CFNumberGetType(number)].floatBit;
//printf("  => %d\n", r);
    return r;
}

Boolean	CFNumberGetValue(CFNumberRef number, CFNumberType type, void *valuePtr) {
//printf("+ [%p] CFNumberGetValue(%p, %d, %p)\n", pthread_self(), number, type, valuePtr);
    CF_OBJC_FUNCDISPATCH2(__kCFNumberTypeID, Boolean, number, "_getValue:forType:", valuePtr, __CFNumberTypeTable[type].canonicalType);
    __CFAssertIsNumber(number);
    __CFAssertIsValidNumberType(type);
    uint8_t localMemory[128];
    Boolean r = __CFNumberGetValueCompat(number, type, valuePtr ? valuePtr : localMemory);
//printf("  => %d\n", r);
    return r;
}

static CFComparisonResult CFNumberCompare_new(CFNumberRef number1, CFNumberRef number2, void *context) {
    CF_OBJC_FUNCDISPATCH1(__kCFNumberTypeID, CFComparisonResult, number1, "compare:", number2);
    CF_OBJC_FUNCDISPATCH1(__kCFNumberTypeID, CFComparisonResult, number2, "_reverseCompare:", number1);
    __CFAssertIsNumber(number1);
    __CFAssertIsNumber(number2);

    CFNumberType type1 = __CFNumberGetType(number1);
    CFNumberType type2 = __CFNumberGetType(number2);
    // Both numbers are integers
    if (!__CFNumberTypeTable[type1].floatBit && !__CFNumberTypeTable[type2].floatBit) {
        CFSInt128Struct i1, i2;
        __CFNumberGetValue(number1, kCFNumberSInt128Type, &i1);
        __CFNumberGetValue(number2, kCFNumberSInt128Type, &i2);
        return cmp128(&i1, &i2);
    }
    // Both numbers are floats
    if (__CFNumberTypeTable[type1].floatBit && __CFNumberTypeTable[type2].floatBit) {
	Float64 d1, d2;
        __CFNumberGetValue(number1, kCFNumberFloat64Type, &d1);
        __CFNumberGetValue(number2, kCFNumberFloat64Type, &d2);
	double s1 = copysign(1.0, d1);
	double s2 = copysign(1.0, d2);
	if (isnan(d1) && isnan(d2)) return kCFCompareEqualTo;
	if (isnan(d1)) return (s2 < 0.0) ? kCFCompareGreaterThan : kCFCompareLessThan;
	if (isnan(d2)) return (s1 < 0.0) ? kCFCompareLessThan : kCFCompareGreaterThan;
	// at this point, we know we don't have any NaNs
	if (s1 < s2) return kCFCompareLessThan;
	if (s2 < s1) return kCFCompareGreaterThan;
	// at this point, we know the signs are the same; do not combine these tests
	if (d1 < d2) return kCFCompareLessThan;
	if (d2 < d1) return kCFCompareGreaterThan;
        return kCFCompareEqualTo;
    }
    // One float, one integer; swap if necessary so number1 is the float
    Boolean swapResult = false;
    if (__CFNumberTypeTable[type2].floatBit) {
        CFNumberRef tmp = number1;
	number1 = number2;
	number2 = tmp;
	swapResult = true;
    }
    // At large integer values, the precision of double is quite low
    // e.g. all values roughly 2^127 +- 2^73 are represented by 1 double, 2^127.
    // If we just used double compare, that would make the 2^73 largest 128-bit
    // integers look equal, so we have to use integer comparison when possible.
    Float64 d1, d2;
    __CFNumberGetValue(number1, kCFNumberFloat64Type, &d1);
    // if the double value is really big, cannot be equal to integer
    // nan d1 will not compare true here
    if (d1 < FLOAT_NEGATIVE_2_TO_THE_127) {
	return !swapResult ? kCFCompareLessThan : kCFCompareGreaterThan;
    }
    if (FLOAT_POSITIVE_2_TO_THE_127 <= d1) {
	return !swapResult ? kCFCompareGreaterThan : kCFCompareLessThan;
    }
    CFSInt128Struct i1, i2;
    __CFNumberGetValue(number1, kCFNumberSInt128Type, &i1);
    __CFNumberGetValue(number2, kCFNumberSInt128Type, &i2);
    CFComparisonResult res = cmp128(&i1, &i2);
    if (kCFCompareEqualTo != res) {
	return !swapResult ? res : -res;
    }
    // now things are equal, but perhaps due to rounding or nan
    if (isnan(d1)) {
	if (isNeg128(&i2)) {
	    return !swapResult ? kCFCompareGreaterThan : kCFCompareLessThan;
	}
	// nan compares less than positive 0 too
	return !swapResult ? kCFCompareLessThan : kCFCompareGreaterThan;
    }
    // at this point, we know we don't have NaN
    double s1 = copysign(1.0, d1);
    double s2 = isNeg128(&i2) ? -1.0 : 1.0;
    if (s1 < s2) return !swapResult ? kCFCompareLessThan : kCFCompareGreaterThan;
    if (s2 < s1) return !swapResult ? kCFCompareGreaterThan : kCFCompareLessThan;
    // at this point, we know the signs are the same; do not combine these tests
    __CFNumberGetValue(number2, kCFNumberFloat64Type, &d2);
    if (d1 < d2) return !swapResult ? kCFCompareLessThan : kCFCompareGreaterThan;
    if (d2 < d1) return !swapResult ? kCFCompareGreaterThan : kCFCompareLessThan;
    return kCFCompareEqualTo;
}

CFComparisonResult CFNumberCompare(CFNumberRef number1, CFNumberRef number2, void *context) {
//printf("+ [%p] CFNumberCompare(%p, %p, %p)\n", pthread_self(), number1, number2, context);
    CFComparisonResult r = CFNumberCompare_new(number1, number2, context);
//printf("  => %d\n", r);
    return r;
}

#undef __CFAssertIsBoolean
#undef __CFAssertIsNumber
#undef __CFAssertIsValidNumberType
#undef BITSFORDOUBLENAN
#undef BITSFORDOUBLEPOSINF
#undef BITSFORDOUBLENEGINF
#undef MinCachedInt
#undef MaxCachedInt
#undef NotToBeCached