blob: b73f338cc9c57773a4b53b6b47a42ed96ca7aa20 [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@
*/
/* 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>
#if (DEPLOYMENT_TARGET_WINDOWS && !defined(__GNUC__))
#define isnan _isnan
#define copysign _copysign
#define isinf !_finite
#endif
#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 || DEPLOYMENT_TARGET_LINUX || (DEPLOYMENT_TARGET_WINDOWS && __GNUC__)
#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
#elif (DEPLOYMENT_TARGET_WINDOWS && !defined(__GNUC__))
#define FLOAT_POSITIVE_2_TO_THE_64 18446744073709551616.0
#define FLOAT_NEGATIVE_2_TO_THE_127 -1.7014118346046923173168730371588e38
#define FLOAT_POSITIVE_2_TO_THE_127 -1.7014118346046923173168730371588e38
#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 (_CFAtomicCompareAndSwapPtrBarrier(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