| /* |
| * 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 |
| |