Project import
diff --git a/CoreFoundation/APPLE_LICENSE b/CoreFoundation/APPLE_LICENSE new file mode 100644 index 0000000..fe81a60 --- /dev/null +++ b/CoreFoundation/APPLE_LICENSE
@@ -0,0 +1,367 @@ +APPLE PUBLIC SOURCE LICENSE +Version 2.0 - August 6, 2003 + +Please read this License carefully before downloading this software. +By downloading or using this software, you are agreeing to be bound by +the terms of this License. If you do not or cannot agree to the terms +of this License, please do not download or use the software. + +1. General; Definitions. This License applies to any program or other +work which Apple Computer, Inc. ("Apple") makes publicly available and +which contains a notice placed by Apple identifying such program or +work as "Original Code" and stating that it is subject to the terms of +this Apple Public Source License version 2.0 ("License"). As used in +this License: + +1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is +the grantor of rights, (i) claims of patents that are now or hereafter +acquired, owned by or assigned to Apple and (ii) that cover subject +matter contained in the Original Code, but only to the extent +necessary to use, reproduce and/or distribute the Original Code +without infringement; and (b) in the case where You are the grantor of +rights, (i) claims of patents that are now or hereafter acquired, +owned by or assigned to You and (ii) that cover subject matter in Your +Modifications, taken alone or in combination with Original Code. + +1.2 "Contributor" means any person or entity that creates or +contributes to the creation of Modifications. + +1.3 "Covered Code" means the Original Code, Modifications, the +combination of Original Code and any Modifications, and/or any +respective portions thereof. + +1.4 "Externally Deploy" means: (a) to sublicense, distribute or +otherwise make Covered Code available, directly or indirectly, to +anyone other than You; and/or (b) to use Covered Code, alone or as +part of a Larger Work, in any way to provide a service, including but +not limited to delivery of content, through electronic communication +with a client other than You. + +1.5 "Larger Work" means a work which combines Covered Code or portions +thereof with code not governed by the terms of this License. + +1.6 "Modifications" mean any addition to, deletion from, and/or change +to, the substance and/or structure of the Original Code, any previous +Modifications, the combination of Original Code and any previous +Modifications, and/or any respective portions thereof. When code is +released as a series of files, a Modification is: (a) any addition to +or deletion from the contents of a file containing Covered Code; +and/or (b) any new file or other representation of computer program +statements that contains any part of Covered Code. + +1.7 "Original Code" means (a) the Source Code of a program or other +work as originally made available by Apple under this License, +including the Source Code of any updates or upgrades to such programs +or works made available by Apple under this License, and that has been +expressly identified by Apple as such in the header file(s) of such +work; and (b) the object code compiled from such Source Code and +originally made available by Apple under this License. + +1.8 "Source Code" means the human readable form of a program or other +work that is suitable for making modifications to it, including all +modules it contains, plus any associated interface definition files, +scripts used to control compilation and installation of an executable +(object code). + +1.9 "You" or "Your" means an individual or a legal entity exercising +rights under this License. For legal entities, "You" or "Your" +includes any entity which controls, is controlled by, or is under +common control with, You, where "control" means (a) the power, direct +or indirect, to cause the direction or management of such entity, +whether by contract or otherwise, or (b) ownership of fifty percent +(50%) or more of the outstanding shares or beneficial ownership of +such entity. + +2. Permitted Uses; Conditions & Restrictions. Subject to the terms +and conditions of this License, Apple hereby grants You, effective on +the date You accept this License and download the Original Code, a +world-wide, royalty-free, non-exclusive license, to the extent of +Apple's Applicable Patent Rights and copyrights covering the Original +Code, to do the following: + +2.1 Unmodified Code. You may use, reproduce, display, perform, +internally distribute within Your organization, and Externally Deploy +verbatim, unmodified copies of the Original Code, for commercial or +non-commercial purposes, provided that in each instance: + +(a) You must retain and reproduce in all copies of Original Code the +copyright and other proprietary notices and disclaimers of Apple as +they appear in the Original Code, and keep intact all notices in the +Original Code that refer to this License; and + +(b) You must include a copy of this License with every copy of Source +Code of Covered Code and documentation You distribute or Externally +Deploy, and You may not offer or impose any terms on such Source Code +that alter or restrict this License or the recipients' rights +hereunder, except as permitted under Section 6. + +2.2 Modified Code. You may modify Covered Code and use, reproduce, +display, perform, internally distribute within Your organization, and +Externally Deploy Your Modifications and Covered Code, for commercial +or non-commercial purposes, provided that in each instance You also +meet all of these conditions: + +(a) You must satisfy all the conditions of Section 2.1 with respect to +the Source Code of the Covered Code; + +(b) You must duplicate, to the extent it does not already exist, the +notice in Exhibit A in each file of the Source Code of all Your +Modifications, and cause the modified files to carry prominent notices +stating that You changed the files and the date of any change; and + +(c) If You Externally Deploy Your Modifications, You must make +Source Code of all Your Externally Deployed Modifications either +available to those to whom You have Externally Deployed Your +Modifications, or publicly available. Source Code of Your Externally +Deployed Modifications must be released under the terms set forth in +this License, including the license grants set forth in Section 3 +below, for as long as you Externally Deploy the Covered Code or twelve +(12) months from the date of initial External Deployment, whichever is +longer. You should preferably distribute the Source Code of Your +Externally Deployed Modifications electronically (e.g. download from a +web site). + +2.3 Distribution of Executable Versions. In addition, if You +Externally Deploy Covered Code (Original Code and/or Modifications) in +object code, executable form only, You must include a prominent +notice, in the code itself as well as in related documentation, +stating that Source Code of the Covered Code is available under the +terms of this License with information on how and where to obtain such +Source Code. + +2.4 Third Party Rights. You expressly acknowledge and agree that +although Apple and each Contributor grants the licenses to their +respective portions of the Covered Code set forth herein, no +assurances are provided by Apple or any Contributor that the Covered +Code does not infringe the patent or other intellectual property +rights of any other entity. Apple and each Contributor disclaim any +liability to You for claims brought by any other entity based on +infringement of intellectual property rights or otherwise. As a +condition to exercising the rights and licenses granted hereunder, You +hereby assume sole responsibility to secure any other intellectual +property rights needed, if any. For example, if a third party patent +license is required to allow You to distribute the Covered Code, it is +Your responsibility to acquire that license before distributing the +Covered Code. + +3. Your Grants. In consideration of, and as a condition to, the +licenses granted to You under this License, You hereby grant to any +person or entity receiving or distributing Covered Code under this +License a non-exclusive, royalty-free, perpetual, irrevocable license, +under Your Applicable Patent Rights and other intellectual property +rights (other than patent) owned or controlled by You, to use, +reproduce, display, perform, modify, sublicense, distribute and +Externally Deploy Your Modifications of the same scope and extent as +Apple's licenses under Sections 2.1 and 2.2 above. + +4. Larger Works. You may create a Larger Work by combining Covered +Code with other code not governed by the terms of this License and +distribute the Larger Work as a single product. In each such instance, +You must make sure the requirements of this License are fulfilled for +the Covered Code or any portion thereof. + +5. Limitations on Patent License. Except as expressly stated in +Section 2, no other patent rights, express or implied, are granted by +Apple herein. Modifications and/or Larger Works may require additional +patent licenses from Apple which Apple may grant in its sole +discretion. + +6. Additional Terms. You may choose to offer, and to charge a fee for, +warranty, support, indemnity or liability obligations and/or other +rights consistent with the scope of the license granted herein +("Additional Terms") to one or more recipients of Covered Code. +However, You may do so only on Your own behalf and as Your sole +responsibility, and not on behalf of Apple or any Contributor. You +must obtain the recipient's agreement that any such Additional Terms +are offered by You alone, and You hereby agree to indemnify, defend +and hold Apple and every Contributor harmless for any liability +incurred by or claims asserted against Apple or such Contributor by +reason of any such Additional Terms. + +7. Versions of the License. Apple may publish revised and/or new +versions of this License from time to time. Each version will be given +a distinguishing version number. Once Original Code has been published +under a particular version of this License, You may continue to use it +under the terms of that version. You may also choose to use such +Original Code under the terms of any subsequent version of this +License published by Apple. No one other than Apple has the right to +modify the terms applicable to Covered Code created under this +License. + +8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in +part pre-release, untested, or not fully tested works. The Covered +Code may contain errors that could cause failures or loss of data, and +may be incomplete or contain inaccuracies. You expressly acknowledge +and agree that use of the Covered Code, or any portion thereof, is at +Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND +WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND +APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" FOR THE +PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM +ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT +NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF +MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR +PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD +PARTY RIGHTS. APPLE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST +INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE +FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS, +THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR +ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO +ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE +AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY. +You acknowledge that the Covered Code is not intended for use in the +operation of nuclear facilities, aircraft navigation, communication +systems, or air traffic control machines in which case the failure of +the Covered Code could lead to death, personal injury, or severe +physical or environmental damage. + +9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO +EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL, +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING +TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR +ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY, +TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF +APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY +REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF +INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY +TO YOU. In no event shall Apple's total liability to You for all +damages (other than as may be required by applicable law) under this +License exceed the amount of fifty dollars ($50.00). + +10. Trademarks. This License does not grant any rights to use the +trademarks or trade names "Apple", "Apple Computer", "Mac", "Mac OS", +"QuickTime", "QuickTime Streaming Server" or any other trademarks, +service marks, logos or trade names belonging to Apple (collectively +"Apple Marks") or to any trademark, service mark, logo or trade name +belonging to any Contributor. You agree not to use any Apple Marks in +or as part of the name of products derived from the Original Code or +to endorse or promote products derived from the Original Code other +than as expressly permitted by and in strict compliance at all times +with Apple's third party trademark usage guidelines which are posted +at http://www.apple.com/legal/guidelinesfor3rdparties.html. + +11. Ownership. Subject to the licenses granted under this License, +each Contributor retains all rights, title and interest in and to any +Modifications made by such Contributor. Apple retains all rights, +title and interest in and to the Original Code and any Modifications +made by or on behalf of Apple ("Apple Modifications"), and such Apple +Modifications will not be automatically subject to this License. Apple +may, at its sole discretion, choose to license such Apple +Modifications under this License, or on different terms from those +contained in this License or may choose not to license them at all. + +12. Termination. + +12.1 Termination. This License and the rights granted hereunder will +terminate: + +(a) automatically without notice from Apple if You fail to comply with +any term(s) of this License and fail to cure such breach within 30 +days of becoming aware of such breach; + +(b) immediately in the event of the circumstances described in Section +13.5(b); or + +(c) automatically without notice from Apple if You, at any time during +the term of this License, commence an action for patent infringement +against Apple; provided that Apple did not first commence +an action for patent infringement against You in that instance. + +12.2 Effect of Termination. Upon termination, You agree to immediately +stop any further use, reproduction, modification, sublicensing and +distribution of the Covered Code. All sublicenses to the Covered Code +which have been properly granted prior to termination shall survive +any termination of this License. Provisions which, by their nature, +should remain in effect beyond the termination of this License shall +survive, including but not limited to Sections 3, 5, 8, 9, 10, 11, +12.2 and 13. No party will be liable to any other for compensation, +indemnity or damages of any sort solely as a result of terminating +this License in accordance with its terms, and termination of this +License will be without prejudice to any other right or remedy of +any party. + +13. Miscellaneous. + +13.1 Government End Users. The Covered Code is a "commercial item" as +defined in FAR 2.101. Government software and technical data rights in +the Covered Code include only those rights customarily provided to the +public as defined in this License. This customary commercial license +in technical data and software is provided in accordance with FAR +12.211 (Technical Data) and 12.212 (Computer Software) and, for +Department of Defense purchases, DFAR 252.227-7015 (Technical Data -- +Commercial Items) and 227.7202-3 (Rights in Commercial Computer +Software or Computer Software Documentation). Accordingly, all U.S. +Government End Users acquire Covered Code with only those rights set +forth herein. + +13.2 Relationship of Parties. This License will not be construed as +creating an agency, partnership, joint venture or any other form of +legal association between or among You, Apple or any Contributor, and +You will not represent to the contrary, whether expressly, by +implication, appearance or otherwise. + +13.3 Independent Development. Nothing in this License will impair +Apple's right to acquire, license, develop, have others develop for +it, market and/or distribute technology or products that perform the +same or similar functions as, or otherwise compete with, +Modifications, Larger Works, technology or products that You may +develop, produce, market or distribute. + +13.4 Waiver; Construction. Failure by Apple or any Contributor to +enforce any provision of this License will not be deemed a waiver of +future enforcement of that or any other provision. Any law or +regulation which provides that the language of a contract shall be +construed against the drafter will not apply to this License. + +13.5 Severability. (a) If for any reason a court of competent +jurisdiction finds any provision of this License, or portion thereof, +to be unenforceable, that provision of the License will be enforced to +the maximum extent permissible so as to effect the economic benefits +and intent of the parties, and the remainder of this License will +continue in full force and effect. (b) Notwithstanding the foregoing, +if applicable law prohibits or restricts You from fully and/or +specifically complying with Sections 2 and/or 3 or prevents the +enforceability of either of those Sections, this License will +immediately terminate and You must immediately discontinue any use of +the Covered Code and destroy all copies of it that are in your +possession or control. + +13.6 Dispute Resolution. Any litigation or other dispute resolution +between You and Apple relating to this License shall take place in the +Northern District of California, and You and Apple hereby consent to +the personal jurisdiction of, and venue in, the state and federal +courts within that District with respect to this License. The +application of the United Nations Convention on Contracts for the +International Sale of Goods is expressly excluded. + +13.7 Entire Agreement; Governing Law. This License constitutes the +entire agreement between the parties with respect to the subject +matter hereof. This License shall be governed by the laws of the +United States and the State of California, except that body of +California law concerning conflicts of law. + +Where You are located in the province of Quebec, Canada, the following +clause applies: The parties hereby confirm that they have requested +that this License and all related documents be drafted in English. Les +parties ont exige que le present contrat et tous les documents +connexes soient rediges en anglais. + +EXHIBIT A. + +"Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights +Reserved. + +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."
diff --git a/CoreFoundation/Android.mk b/CoreFoundation/Android.mk new file mode 100644 index 0000000..124957f --- /dev/null +++ b/CoreFoundation/Android.mk
@@ -0,0 +1,131 @@ +LOCAL_PATH := $(call my-dir) + +opencflite_src_files := \ +CFApplicationPreferences.c \ +CFArray.c \ +CFBag.c \ +CFBase.c \ +CFBinaryHeap.c \ +CFBinaryPList.c \ +CFBitVector.c \ +CFBuiltinConverters.c \ +CFBundle.c \ +CFBundle_Resources.c \ +CFCalendar.c \ +CFCharacterSet.c \ +CFConcreteStreams.c \ +CFData.c \ +CFDate.c \ +CFDateFormatter.c \ +CFDictionary.c \ +CFError.c \ +CFFileUtilities.c \ +CFLocale.c \ +CFLocaleIdentifier.c \ +CFNumber.c \ +CFNumberFormatter.c \ +CFPlatform.c \ +CFPlugIn.c \ +CFPlugIn_Factory.c \ +CFPlugIn_PlugIn.c \ +CFPreferences.c \ +CFPropertyList.c \ +CFRunLoop.c \ +CFRuntime.c \ +CFSet.c \ +CFSocket.c \ +CFSocketStream.c \ +CFSortFunctions.c \ +CFStorage.c \ +CFStream.c \ +CFString.c \ +CFStringEncodingConverter.c \ +CFStringEncodings.c \ +CFStringScanner.c \ +CFStringUtilities.c \ +CFSystemDirectories.c \ +CFTimeZone.c \ +CFTree.c \ +CFUniChar.c \ +CFUnicodeDecomposition.c \ +CFUnicodePrecomposition.c \ +CFURLAccess.c \ +CFURL.c \ +CFUtilities.c \ +CFUUID.c \ +CFVersion.c \ +CFWindowsMessageQueue.c \ +CFXMLPreferencesDomain.c \ +compat/flsl.c + +# disabled for now +#CFMachPort.c +#CFMessagePort.c +#CFPlugIn_Instance.c +#CFUserNotification.c +#CFXMLInputStream.c +#CFXMLNode.c +#CFXMLParser.c +#CFXMLTree.c + +include $(CLEAR_VARS) +LOCAL_MODULE := libopencflite +LOCAL_SRC_FILES := $(opencflite_src_files) +LOCAL_C_INCLUDES := $(LOCAL_PATH)/.. +LOCAL_C_INCLUDES += $(LOCAL_PATH)/include +LOCAL_C_INCLUDES += ${ANDROID_BUILD_TOP}/external/icu/icu4c/source/common/ +LOCAL_C_INCLUDES += ${ANDROID_BUILD_TOP}/external/icu/icu4c/source/i18n/ +LOCAL_C_INCLUDES += ${ANDROID_BUILD_TOP}/external/icu/icu4c/source/tools/tzcode/ +LOCAL_C_INCLUDES += ${ANDROID_BUILD_TOP}/external/opencflite/libBlocksRuntime/ +# todo: remove Linux case +LOCAL_EXPORT_CFLAGS := -DDEPLOYMENT_TARGET_LINUX -DDEPLOYMENT_TARGET_ANDROID +LOCAL_CFLAGS = -x c -fblocks -std=gnu99 -Wno-trigraphs -fexceptions -DCF_BUILDING_CF=1 -DDISABLE_GCD -nostdlibinc -D__kCFDataDir=\"/etc/opencflite\" $(LOCAL_EXPORT_CFLAGS) -Wno-infinite-recursion -Wno-unused-parameter -Wno-unsequenced -Wno-date-time -Wno-implicit -Wno-tautological-pointer-compare +#note: not all flags from MakefileLinux have been ported over. +LOCAL_LDFLAGS = -fpic +LOCAL_SHARED_LIBRARIES += libicuuc libicui18n +include $(BUILD_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := libopencflite +LOCAL_SRC_FILES := $(opencflite_src_files) +LOCAL_C_INCLUDES := $(LOCAL_PATH)/.. +LOCAL_C_INCLUDES += $(LOCAL_PATH)/include +LOCAL_C_INCLUDES += ${ANDROID_BUILD_TOP}/external/icu/icu4c/source/common/ +LOCAL_C_INCLUDES += ${ANDROID_BUILD_TOP}/external/icu/icu4c/source/i18n/ +LOCAL_C_INCLUDES += ${ANDROID_BUILD_TOP}/external/icu/icu4c/source/tools/tzcode/ +LOCAL_C_INCLUDES += ${ANDROID_BUILD_TOP}/external/opencflite/libBlocksRuntime/ +# todo: remove Linux case +LOCAL_EXPORT_CFLAGS := -DDEPLOYMENT_TARGET_LINUX -DDEPLOYMENT_TARGET_ANDROID +LOCAL_CFLAGS = -x c -fblocks -std=gnu99 -Wno-trigraphs -fexceptions -DCF_BUILDING_CF=1 -DDISABLE_GCD -nostdlibinc -D__kCFDataDir=\"/etc/opencflite\" $(LOCAL_EXPORT_CFLAGS) -Wno-infinite-recursion -Wno-unused-parameter -Wno-unsequenced -Wno-date-time -Wno-implicit -Wno-tautological-pointer-compare +#note: not all flags from MakefileLinux have been ported over. +LOCAL_LDFLAGS = -fpic +LOCAL_STATIC_LIBRARIES += libicuuc_static libicui18n_static +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := CFCharacterSetBitmaps.bitmap +LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/opencflite/CoreFoundation/CharacterSets +LOCAL_MODULE_CLASS := ETC +LOCAL_SRC_FILES := CFCharacterSetBitmaps.bitmap +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := CFUnicodeData-B.mapping +LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/opencflite/CoreFoundation/CharacterSets +LOCAL_MODULE_CLASS := ETC +LOCAL_SRC_FILES := CFUnicodeData-B.mapping +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := CFUniCharPropertyDatabase.data +LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/opencflite/CoreFoundation/CharacterSets +LOCAL_MODULE_CLASS := ETC +LOCAL_SRC_FILES := CFUniCharPropertyDatabase.data +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := CFUnicodeData-L.mapping +LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/opencflite/CoreFoundation/CharacterSets +LOCAL_MODULE_CLASS := ETC +LOCAL_SRC_FILES := CFUnicodeData-L.mapping +include $(BUILD_PREBUILT)
diff --git a/CoreFoundation/BuildCFLite b/CoreFoundation/BuildCFLite new file mode 100644 index 0000000..bbde7dd --- /dev/null +++ b/CoreFoundation/BuildCFLite
@@ -0,0 +1,92 @@ +#/bin/sh + +echo "Setup ..." + +ALL_CFILES=`ls *.c` +ALL_HFILES=`ls *.h` + +MACHINE_TYPE=`uname -p` +UNICODE_DATA_FILE="UNKNOWN" + +if [ "$MACHINE_TYPE" == "i386" ]; then + UNICODE_DATA_FILE="CFUnicodeData-L.mapping" +fi + +if [ "$MACHINE_TYPE" == "powerpc" ]; then + UNICODE_DATA_FILE="CFUnicodeData-B.mapping" +fi + + + +PUBLIC_HEADERS="CFArray.h CFBag.h CFBase.h CFBinaryHeap.h CFBitVector.h CFBundle.h CFByteOrder.h CFCalendar.h CFCharacterSet.h CFData.h CFDate.h CFDateFormatter.h CFDictionary.h CFError.h CFLocale.h CFMachPort.h CFMessagePort.h CFNumber.h CFNumberFormatter.h CFPlugIn.h CFPlugInCOM.h CFPreferences.h CFPropertyList.h CFRunLoop.h CFSet.h CFSocket.h CFStream.h CFString.h CFStringEncodingExt.h CFTimeZone.h CFTree.h CFURL.h CFURLAccess.h CFUUID.h CFUserNotification.h CFXMLNode.h CFXMLParser.h CoreFoundation.h" +PRIVATE_HEADERS="CFBundlePriv.h CFCharacterSetPriv.h CFError_Private.h CFLogUtilities.h CFPriv.h CFRuntime.h CFStorage.h CFStreamAbstract.h CFStreamPriv.h CFStreamInternal.h CFStringDefaultEncoding.h CFStringEncodingConverter.h CFStringEncodingConverterExt.h CFUniChar.h CFUnicodeDecomposition.h CFUnicodePrecomposition.h ForFoundationOnly.h" + +OBJBASE=../CF-Objects +ARCHFLAGS="-arch ppc -arch ppc64 -arch i386 -arch x86_64" +CFLAGS="-c -pipe -std=gnu99 -g -Wmost -Wno-trigraphs -mmacosx-version-min=10.5 -fconstant-cfstrings -fexceptions -DCF_BUILDING_CF=1 -DDEPLOYMENT_TARGET_MACOSX=1 -DMAC_OS_X_VERSION_MAX_ALLOWED=MAC_OS_X_VERSION_10_5 -DU_SHOW_DRAFT_API=1 -I$OBJBASE -I. -I./include/mach_support -DVERSION=476.17" +LFLAGS="-dynamiclib -mmacosx-version-min=10.5 -twolevel_namespace -init ___CFInitialize -compatibility_version 150 -current_version 476 -sectcreate __UNICODE __csbitmaps CFCharacterSetBitmaps.bitmap -sectcreate __UNICODE __properties CFUniCharPropertyDatabase.data -sectcreate __UNICODE __data $UNICODE_DATA_FILE -segprot __UNICODE r r" + +/bin/rm -rf $OBJBASE +/bin/mkdir -p $OBJBASE +/bin/mkdir $OBJBASE/normal +/bin/mkdir $OBJBASE/CoreFoundation +/bin/cp $ALL_HFILES $OBJBASE/CoreFoundation +if [ $? -ne 0 ]; then + echo "Setup failed" + exit 1 +fi + +Build () { + echo "Compiling $STYLE ..." + for F in $ALL_CFILES ; do + echo /usr/bin/gcc $STYLE_CFLAGS $ARCHFLAGS $CFLAGS $F -o $OBJBASE/$STYLE/`basename $F .c`.o + /usr/bin/gcc $STYLE_CFLAGS $ARCHFLAGS $CFLAGS $F -o $OBJBASE/$STYLE/`basename $F .c`.o + if [ $? -ne 0 ]; then + echo "*** Compiling $STYLE failed ***" + exit 1 + fi + done + echo "Linking $STYLE ..." + echo /usr/bin/gcc $STYLE_LFLAGS -install_name /System/Library/Frameworks/CFLite.framework/Versions/A/CFLite_$STYLE $ARCHFLAGS $LFLAGS $OBJBASE/$STYLE/*.o -licucore.A -lobjc -o $OBJBASE/CFLite_$STYLE + /usr/bin/gcc $STYLE_LFLAGS -install_name /System/Library/Frameworks/CFLite.framework/Versions/A/CFLite_$STYLE $ARCHFLAGS $LFLAGS $OBJBASE/$STYLE/*.o -licucore.A -lobjc -o $OBJBASE/CFLite_$STYLE + if [ $? -ne 0 ]; then + echo "*** Linking $STYLE failed ***" + exit 1 + fi +} + +STYLE=normal +STYLE_CFLAGS="-O0" +STYLE_LFLAGS= +Build + +echo "Building done." + +echo "Installing ..." +if [ -z "$DSTBASE" ]; then DSTBASE=../CF-Root ; fi + +/bin/rm -rf $DSTBASE/CFLite.framework +/bin/mkdir -p $DSTBASE/CFLite.framework/Versions/A/Resources +/bin/mkdir -p $DSTBASE/CFLite.framework/Versions/A/Headers +/bin/mkdir -p $DSTBASE/CFLite.framework/Versions/A/PrivateHeaders +/bin/ln -sf A $DSTBASE/CFLite.framework/Versions/Current +/bin/ln -sf Versions/Current/Resources $DSTBASE/CFLite.framework/Resources +/bin/ln -sf Versions/Current/Headers $DSTBASE/CFLite.framework/Headers +/bin/ln -sf Versions/Current/PrivateHeaders $DSTBASE/CFLite.framework/PrivateHeaders +/bin/ln -sf Versions/Current/CFLite $DSTBASE/CFLite.framework/CFLite +/bin/cp Info.plist $DSTBASE/CFLite.framework/Versions/A/Resources +/bin/mkdir -p $DSTBASE/CFLite.framework/Versions/A/Resources/en.lproj +/bin/cp $PUBLIC_HEADERS $DSTBASE/CFLite.framework/Versions/A/Headers +/bin/cp $PRIVATE_HEADERS $DSTBASE/CFLite.framework/Versions/A/PrivateHeaders +#/usr/bin/strip -S -o $DSTBASE/CFLite.framework/Versions/A/CFLite $OBJBASE/CFLite_normal +/bin/cp $OBJBASE/CFLite_normal $DSTBASE/CFLite.framework/Versions/A/CFLite +/usr/sbin/chown -RH -f root:wheel $DSTBASE/CFLite.framework +/bin/chmod -RH a-w,a+rX $DSTBASE/CFLite.framework +/bin/chmod -RH u+w $DSTBASE + +install_name_tool -id /System/Library/Frameworks/CFLite.framework/Versions/A/CFLite $DSTBASE/CFLite.framework/Versions/A/CFLite + +echo "Installing done. The framework is in $DSTBASE" + +exit 0 +
diff --git a/CoreFoundation/BuildCFLite.windows b/CoreFoundation/BuildCFLite.windows new file mode 100644 index 0000000..ba0ab1e --- /dev/null +++ b/CoreFoundation/BuildCFLite.windows
@@ -0,0 +1,101 @@ +#/bin/sh + +echo "Setup ..." + +ALL_CFILES=`ls *.c` +ALL_HFILES=`ls *.h` +CC="/mingw/bin/gcc" +AR="/mingw/bin/ar" + +MACHINE_TYPE=`uname -p` +UNICODE_DATA_FILE="UNKNOWN" +# DEPLOYMENT_TARGET="-DDEPLOYMENT_TARGET_MACOSX=1" +DEPLOYMENT_TARGET="-DDEPLOYMENT_TARGET_WIN32=1" +#MACOSVERS="-mmacosx-version-min=10.5 -fconstant-cfstrings" +MACOSVERS="" +#WARNINGS="-Wmost" +WARNINGS="" + +if [ "$MACHINE_TYPE" == "i386" ]; then + UNICODE_DATA_FILE="CFUnicodeData-L.mapping" +fi + +if [ "$MACHINE_TYPE" == "powerpc" ]; then + UNICODE_DATA_FILE="CFUnicodeData-B.mapping" +fi + + + +PUBLIC_HEADERS="CFArray.h CFBag.h CFBase.h CFBinaryHeap.h CFBitVector.h CFBundle.h CFByteOrder.h CFCalendar.h CFCharacterSet.h CFData.h CFDate.h CFDateFormatter.h CFDictionary.h CFError.h CFLocale.h CFMachPort.h CFMessagePort.h CFNumber.h CFNumberFormatter.h CFPlugIn.h CFPlugInCOM.h CFPreferences.h CFPropertyList.h CFRunLoop.h CFSet.h CFSocket.h CFStream.h CFString.h CFStringEncodingExt.h CFTimeZone.h CFTree.h CFURL.h CFURLAccess.h CFUUID.h CFUserNotification.h CFXMLNode.h CFXMLParser.h CoreFoundation.h" +PRIVATE_HEADERS="CFBundlePriv.h CFCharacterSetPriv.h CFError_Private.h CFLogUtilities.h CFPriv.h CFRuntime.h CFStorage.h CFStreamAbstract.h CFStreamPriv.h CFStreamInternal.h CFStringDefaultEncoding.h CFStringEncodingConverter.h CFStringEncodingConverterExt.h CFUniChar.h CFUnicodeDecomposition.h CFUnicodePrecomposition.h ForFoundationOnly.h" + +OBJBASE=../CF-Objects +#ARCHFLAGS="-arch ppc -arch ppc64 -arch i386 -arch x86_64" +ARCHFLAGS="" +CFLAGS="-c -pipe -std=gnu99 -g $WARNINGS -Wno-trigraphs $MACOSVERS -fexceptions -DCF_BUILDING_CF=1 $DEPLOYMENT_TARGET -DMAC_OS_X_VERSION_MAX_ALLOWED=MAC_OS_X_VERSION_10_5 -DU_SHOW_DRAFT_API=1 -I. -I./Compatability -I$OBJBASE -DVERSION=476.17 -DWINVER=0x0500 -D_WIN32_WINNT=0x0501" +# LFLAGS="-dynamiclib -mmacosx-version-min=10.5 -twolevel_namespace -init ___CFInitialize -compatibility_version 150 -current_version 476 -sectcreate __UNICODE __csbitmaps CFCharacterSetBitmaps.bitmap -sectcreate __UNICODE __properties CFUniCharPropertyDatabase.data -sectcreate __UNICODE __data $UNICODE_DATA_FILE -segprot __UNICODE r r" +LFLAGS="-lmsvcrt -lnetapi32 -lobjc -lole32 -lws2_32 -lkernel32 -lrpcrt4" + +/bin/rm -rf $OBJBASE +/bin/mkdir -p $OBJBASE +/bin/mkdir $OBJBASE/normal +/bin/mkdir $OBJBASE/CoreFoundation +/bin/cp $ALL_HFILES $OBJBASE/CoreFoundation +if [ $? -ne 0 ]; then + echo "Setup failed" + exit 1 +fi + +Build () { + echo "Compiling $STYLE ..." + for F in $ALL_CFILES ; do + echo $CC $STYLE_CFLAGS $ARCHFLAGS $CFLAGS $F -o $OBJBASE/$STYLE/`basename $F .c`.o + $CC $STYLE_CFLAGS $ARCHFLAGS $CFLAGS $F -o $OBJBASE/$STYLE/`basename $F .c`.o + if [ $? -ne 0 ]; then + echo "*** Compiling $STYLE failed ***" + exit 1 + fi + done + echo "Linking $STYLE ..." + echo $CC $STYLE_LFLAGS $ARCHFLAGS $OBJBASE/$STYLE/*.o $LFLAGS -licuuc -licuin -lobjc -o $OBJBASE/CoreFoundation_$STYLE + $CC -shared $STYLE_LFLAGS $ARCHFLAGS $OBJBASE/$STYLE/*.o $LFLAGS lib/icuuc.lib lib/icuin.lib -lobjc -o $OBJBASE/libCFlite_476_17.dll + if [ $? -ne 0 ]; then + echo "*** Linking $STYLE failed ***" + exit 1 + fi +} + +STYLE=normal +STYLE_CFLAGS="-O2" +STYLE_LFLAGS= +Build + +echo "Building done." + +echo "Installing ..." +if [ -z "$DSTBASE" ]; then DSTBASE=../CF-Root ; fi + +/bin/rm -rf $DSTBASE/CoreFoundation.framework +/bin/mkdir -p $DSTBASE/CoreFoundation.framework/Versions/A/Resources +/bin/mkdir -p $DSTBASE/CoreFoundation.framework/Versions/A/Headers +/bin/mkdir -p $DSTBASE/CoreFoundation.framework/Versions/A/PrivateHeaders +/bin/ln -sf A $DSTBASE/CoreFoundation.framework/Versions/Current +/bin/ln -sf Versions/Current/Resources $DSTBASE/CoreFoundation.framework/Resources +/bin/ln -sf Versions/Current/Headers $DSTBASE/CoreFoundation.framework/Headers +/bin/ln -sf Versions/Current/PrivateHeaders $DSTBASE/CoreFoundation.framework/PrivateHeaders +/bin/ln -sf Versions/Current/CoreFoundation $DSTBASE/CoreFoundation.framework/CoreFoundation +/bin/cp Info.plist $DSTBASE/CoreFoundation.framework/Versions/A/Resources +/bin/mkdir -p $DSTBASE/CoreFoundation.framework/Versions/A/Resources/en.lproj +/bin/cp $PUBLIC_HEADERS $DSTBASE/CoreFoundation.framework/Versions/A/Headers +/bin/cp $PRIVATE_HEADERS $DSTBASE/CoreFoundation.framework/Versions/A/PrivateHeaders +/usr/bin/strip -S -o $DSTBASE/CoreFoundation.framework/Versions/A/CoreFoundation $OBJBASE/CoreFoundation_normal +/usr/sbin/chown -RH -f root:wheel $DSTBASE/CoreFoundation.framework +/bin/chmod -RH a-w,a+rX $DSTBASE/CoreFoundation.framework +/bin/chmod -RH u+w $DSTBASE + +install_name_tool -id /System/Library/Frameworks/CoreFoundation/Versions/A/CoreFoundation $DSTBASE/CoreFoundation.framework/Versions/A/CoreFoundation + +echo "Installing done. The framework is in $DSTBASE" + +exit 0 +
diff --git a/CoreFoundation/CFApplicationPreferences.c b/CoreFoundation/CFApplicationPreferences.c new file mode 100644 index 0000000..2f5c68f --- /dev/null +++ b/CoreFoundation/CFApplicationPreferences.c
@@ -0,0 +1,682 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFApplicationPreferences.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Chris Parker +*/ + +#include "CFInternal.h" +#include <CoreFoundation/CFPreferences.h> +#include <CoreFoundation/CFUniChar.h> +#include <CoreFoundation/CFNumber.h> +#include <CoreFoundation/CFString.h> +#include <CoreFoundation/CFLocale.h> +#include <CoreFoundation/CFNumberFormatter.h> +#include <CoreFoundation/CFDateFormatter.h> +#include <sys/types.h> +#if __MACH__ +#include <unistd.h> +#endif + +static Boolean _CFApplicationPreferencesSynchronizeNoLock(_CFApplicationPreferences *self); +void _CFPreferencesDomainSetMultiple(CFPreferencesDomainRef domain, CFDictionaryRef dict); +static void updateDictRep(_CFApplicationPreferences *self); +static void _CFApplicationPreferencesSetSearchList(_CFApplicationPreferences *self, CFArrayRef newSearchList); +Boolean _CFApplicationPreferencesContainsDomainNoLock(_CFApplicationPreferences *self, CFPreferencesDomainRef domain); +static CFTypeRef _CFApplicationPreferencesCreateValueForKey2(_CFApplicationPreferences *self, CFStringRef defaultName); + +// Right now, nothing is getting destroyed pretty much ever. We probably don't want this to be the case, but it's very tricky - domains live in the cache as well as a given application's preferences, and since they're not CFTypes, there's no reference count. Also, it's not clear we ever want domains destroyed. When they're synchronized, they clear their internal state (to force reading from the disk again), so they're not very big.... REW, 12/17/98 + +CFPropertyListRef CFPreferencesCopyAppValue(CFStringRef key, CFStringRef appName) { + _CFApplicationPreferences *standardPrefs; + CFAssert1(appName != NULL, __kCFLogAssertion, "%s(): Cannot access application preferences with a NULL application name", __PRETTY_FUNCTION__); + CFAssert1(key != NULL, __kCFLogAssertion, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__); + + standardPrefs = _CFStandardApplicationPreferences(appName); + return standardPrefs ? _CFApplicationPreferencesCreateValueForKey2(standardPrefs, key) : NULL; +} + +CF_EXPORT Boolean CFPreferencesAppBooleanValue(CFStringRef key, CFStringRef appName, Boolean *keyExistsAndHasValidFormat) { + CFPropertyListRef value; + Boolean result, valid; + CFTypeID typeID = 0; + CFAssert1(appName != NULL, __kCFLogAssertion, "%s(): Cannot access application preferences with a NULL application name", __PRETTY_FUNCTION__); + CFAssert1(key != NULL, __kCFLogAssertion, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__); + + if (!keyExistsAndHasValidFormat) { + keyExistsAndHasValidFormat = &valid; + } + value = CFPreferencesCopyAppValue(key, appName); + if (!value) { + *keyExistsAndHasValidFormat = false; + return false; + } + typeID = CFGetTypeID(value); + if (typeID == CFStringGetTypeID()) { + if (CFStringCompare((CFStringRef)value, CFSTR("true"), kCFCompareCaseInsensitive) == kCFCompareEqualTo || CFStringCompare((CFStringRef)value, CFSTR("YES"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { + *keyExistsAndHasValidFormat = true; + result = true; + } else if (CFStringCompare((CFStringRef)value, CFSTR("false"), kCFCompareCaseInsensitive) == kCFCompareEqualTo || CFStringCompare((CFStringRef)value, CFSTR("NO"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { + *keyExistsAndHasValidFormat = true; + result = false; + } else { + *keyExistsAndHasValidFormat = false; + result = false; + } + } else if (typeID == CFNumberGetTypeID()) { + if (CFNumberIsFloatType((CFNumberRef)value)) { + *keyExistsAndHasValidFormat = false; + result = false; + } else { + int i; + *keyExistsAndHasValidFormat = true; + CFNumberGetValue((CFNumberRef)value, kCFNumberIntType, &i); + result = (i == 0) ? false : true; + } + } else if (typeID == CFBooleanGetTypeID()) { + result = (value == kCFBooleanTrue); + *keyExistsAndHasValidFormat = true; + } else { + // Unknown type + result = false; + *keyExistsAndHasValidFormat = false; + } + CFRelease(value); + return result; +} + +__private_extern__ CFIndex CFPreferencesAppIntegerValue(CFStringRef key, CFStringRef appName, Boolean *keyExistsAndHasValidFormat) { + CFPropertyListRef value; + CFIndex result; + CFTypeID typeID = 0; + Boolean valid; + CFAssert1(appName != NULL, __kCFLogAssertion, "%s(): Cannot access application preferences with a NULL application name", __PRETTY_FUNCTION__); + CFAssert1(key != NULL, __kCFLogAssertion, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__); + + value = CFPreferencesCopyAppValue(key, appName); + if (!keyExistsAndHasValidFormat) { + keyExistsAndHasValidFormat = &valid; + } + if (!value) { + *keyExistsAndHasValidFormat = false; + return 0; + } + typeID = CFGetTypeID(value); + if (typeID == CFStringGetTypeID()) { + SInt32 charIndex = 0; + SInt32 intVal; + CFStringInlineBuffer buf; + Boolean success; + CFStringInitInlineBuffer((CFStringRef)value, &buf, CFRangeMake(0, CFStringGetLength((CFStringRef)value))); + success = __CFStringScanInteger(&buf, NULL, &charIndex, false, &intVal); + *keyExistsAndHasValidFormat = (success && charIndex == CFStringGetLength((CFStringRef)value)); + result = (*keyExistsAndHasValidFormat) ? intVal : 0; + } else if (typeID == CFNumberGetTypeID()) { + *keyExistsAndHasValidFormat = !CFNumberIsFloatType((CFNumberRef)value); + if (*keyExistsAndHasValidFormat) { + CFNumberGetValue((CFNumberRef)value, kCFNumberCFIndexType, &result); + } else { + result = 0; + } + } else { + // Unknown type + result = 0; + *keyExistsAndHasValidFormat = false; + } + CFRelease(value); + return result; +} + +Boolean CFPreferencesGetAppBooleanValue(CFStringRef key, CFStringRef appName, Boolean *keyExistsAndHasValidFormat) { + CFAssert1(appName != NULL, __kCFLogAssertion, "%s(): Cannot access application preferences with a NULL application name", __PRETTY_FUNCTION__); + CFAssert1(key != NULL, __kCFLogAssertion, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__); + return CFPreferencesAppBooleanValue(key, appName, keyExistsAndHasValidFormat); +} + +CFIndex CFPreferencesGetAppIntegerValue(CFStringRef key, CFStringRef appName, Boolean *keyExistsAndHasValidFormat) { + CFAssert1(appName != NULL, __kCFLogAssertion, "%s(): Cannot access application preferences with a NULL application name", __PRETTY_FUNCTION__); + CFAssert1(key != NULL, __kCFLogAssertion, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__); + return CFPreferencesAppIntegerValue(key, appName, keyExistsAndHasValidFormat); +} + +void CFPreferencesSetAppValue(CFStringRef key, CFTypeRef value, CFStringRef appName) { + _CFApplicationPreferences *standardPrefs; + CFAssert1(appName != NULL, __kCFLogAssertion, "%s(): Cannot access application preferences with a NULL application name", __PRETTY_FUNCTION__); + CFAssert1(key != NULL, __kCFLogAssertion, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__); + + standardPrefs = _CFStandardApplicationPreferences(appName); + if (standardPrefs) { + if (value) _CFApplicationPreferencesSet(standardPrefs, key, value); + else _CFApplicationPreferencesRemove(standardPrefs, key); + } +} + + +static CFSpinLock_t __CFApplicationPreferencesLock = CFSpinLockInit; // Locks access to __CFStandardUserPreferences +static CFMutableDictionaryRef __CFStandardUserPreferences = NULL; // Mutable dictionary; keys are app names, values are _CFApplicationPreferences + +Boolean CFPreferencesAppSynchronize(CFStringRef appName) { + _CFApplicationPreferences *standardPrefs; + Boolean result; + CFAssert1(appName != NULL, __kCFLogAssertion, "%s(): Cannot access application preferences with a NULL application name", __PRETTY_FUNCTION__); + + // Do not call _CFStandardApplicationPreferences(), as we do not want to create the preferences only to synchronize + __CFSpinLock(&__CFApplicationPreferencesLock); + if (__CFStandardUserPreferences) { + standardPrefs = (_CFApplicationPreferences *)CFDictionaryGetValue(__CFStandardUserPreferences, appName); + } else { + standardPrefs = NULL; + } + + result = standardPrefs ? _CFApplicationPreferencesSynchronizeNoLock(standardPrefs) : _CFSynchronizeDomainCache(); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + return result; +} + +void CFPreferencesFlushCaches(void) { + CFAllocatorRef alloc = __CFPreferencesAllocator(); + __CFSpinLock(&__CFApplicationPreferencesLock); + if (__CFStandardUserPreferences) { + _CFApplicationPreferences **prefsArray, *prefsBuf[32]; + CFIndex idx, count = CFDictionaryGetCount(__CFStandardUserPreferences); + if (count < 32) { + prefsArray = prefsBuf; + } else { + prefsArray = (_CFApplicationPreferences **)CFAllocatorAllocate(alloc, count * sizeof(_CFApplicationPreferences *), 0); + } + CFDictionaryGetKeysAndValues(__CFStandardUserPreferences, NULL, (const void **)prefsArray); + + __CFSpinUnlock(&__CFApplicationPreferencesLock); + // DeallocateApplicationPreferences needs the lock + for (idx = 0; idx < count; idx ++) { + _CFApplicationPreferences *appPrefs = prefsArray[idx]; + _CFApplicationPreferencesSynchronize(appPrefs); + _CFDeallocateApplicationPreferences(appPrefs); + } + __CFSpinLock(&__CFApplicationPreferencesLock); + + CFRelease(__CFStandardUserPreferences); + __CFStandardUserPreferences = NULL; + if(prefsArray != prefsBuf) CFAllocatorDeallocate(alloc, prefsArray); + } + __CFSpinUnlock(&__CFApplicationPreferencesLock); + _CFPreferencesPurgeDomainCache(); +} + +// quick message to indicate that the given domain has changed, and we should go through and invalidate any dictReps that involve this domain. +void _CFApplicationPreferencesDomainHasChanged(CFPreferencesDomainRef changedDomain) { + CFAllocatorRef alloc = __CFPreferencesAllocator(); + __CFSpinLock(&__CFApplicationPreferencesLock); + if(__CFStandardUserPreferences) { // only grovel over the prefs if there's something there to grovel + _CFApplicationPreferences **prefsArray, *prefsBuf[32]; + CFIndex idx, count = CFDictionaryGetCount(__CFStandardUserPreferences); + if(count < 32) { + prefsArray = prefsBuf; + } else { + prefsArray = (_CFApplicationPreferences **)CFAllocatorAllocate(alloc, count * sizeof(_CFApplicationPreferences *), 0); + } + CFDictionaryGetKeysAndValues(__CFStandardUserPreferences, NULL, (const void **)prefsArray); + // For this operation, giving up the lock is the last thing we want to do, so use the modified flavor of _CFApplicationPreferencesContainsDomain + for(idx = 0; idx < count; idx++) { + _CFApplicationPreferences *appPrefs = prefsArray[idx]; + if(_CFApplicationPreferencesContainsDomainNoLock(appPrefs, changedDomain)) { + updateDictRep(appPrefs); + } + } + if(prefsArray != prefsBuf) _CFAllocatorDeallocateGC(alloc, prefsArray); + } + __CFSpinUnlock(&__CFApplicationPreferencesLock); +} + + +// Begin ported code from NSUserDefaults.m + + +static void updateDictRep(_CFApplicationPreferences *self) { + if (self->_dictRep) { + CFRelease(self->_dictRep); + self->_dictRep = NULL; + } +} + +static void __addKeysAndValues(const void *key, const void *value, void *context) { + CFDictionarySetValue((CFMutableDictionaryRef)context, key, value); +} + +static CFMutableDictionaryRef computeDictRep(_CFApplicationPreferences *self, Boolean skipC0C0A) { + CFAllocatorRef alloc = __CFPreferencesAllocator(); + CFMutableArrayRef searchList = self->_search; + CFIndex idx; + CFIndex cnt = CFArrayGetCount(searchList); + CFDictionaryRef subdomainDict; + CFMutableDictionaryRef dictRep; + + dictRep = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, & kCFTypeDictionaryValueCallBacks); + _CFDictionarySetCapacity(dictRep, 260); // avoid lots of rehashing + + // For each subdomain triplet in the domain, iterate over dictionaries, adding them if necessary to the dictRep + for (idx = cnt; idx--;) { + CFPreferencesDomainRef domain = (CFPreferencesDomainRef)CFArrayGetValueAtIndex(searchList, idx); + + if (!domain) continue; + + subdomainDict = _CFPreferencesDomainDeepCopyDictionary(domain); + if (subdomainDict) { + CFDictionaryApplyFunction(subdomainDict, __addKeysAndValues, dictRep); + CFRelease(subdomainDict); + } + } + return dictRep; +} + +CFTypeRef _CFApplicationPreferencesSearchDownToDomain(_CFApplicationPreferences *self, CFPreferencesDomainRef stopper, CFStringRef key) { + return NULL; +} + + +void _CFApplicationPreferencesUpdate(_CFApplicationPreferences *self) { + __CFSpinLock(&__CFApplicationPreferencesLock); + updateDictRep(self); + __CFSpinUnlock(&__CFApplicationPreferencesLock); +} + +CF_EXPORT CFDictionaryRef _CFApplicationPreferencesCopyRepresentation(_CFApplicationPreferences *self); + +__private_extern__ CFDictionaryRef __CFApplicationPreferencesCopyCurrentState(void) { + _CFApplicationPreferences *self = _CFStandardApplicationPreferences(kCFPreferencesCurrentApplication); + CFDictionaryRef result = _CFApplicationPreferencesCopyRepresentation(self); + return result; +} + +// CACHING here - we will only return a value as current as the last time computeDictRep() was called +static CFTypeRef _CFApplicationPreferencesCreateValueForKey2(_CFApplicationPreferences *self, CFStringRef defaultName) { + CFTypeRef result; + __CFSpinLock(&__CFApplicationPreferencesLock); + if (!self->_dictRep) { + self->_dictRep = computeDictRep(self, true); + } + result = (self->_dictRep) ? (CFTypeRef )CFDictionaryGetValue(self->_dictRep, defaultName) : NULL; + if (result) { + CFRetain(result); + } + __CFSpinUnlock(&__CFApplicationPreferencesLock); + return result; +} + + +void _CFApplicationPreferencesSet(_CFApplicationPreferences *self, CFStringRef defaultName, CFTypeRef value) { + CFPreferencesDomainRef applicationDomain; + + __CFSpinLock(&__CFApplicationPreferencesLock); + applicationDomain = _CFPreferencesStandardDomain(self->_appName, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); + if(applicationDomain) { + _CFPreferencesDomainSet(applicationDomain, defaultName, value); + if (CFArrayContainsValue(self->_search, CFRangeMake(0, CFArrayGetCount(self->_search)), applicationDomain)) { + // Expensive; can't we just check the relevant value throughout the appropriate sets of domains? -- REW, 7/19/99 + updateDictRep(self); + } + } + __CFSpinUnlock(&__CFApplicationPreferencesLock); +} + +void _CFApplicationPreferencesRemove(_CFApplicationPreferences *self, CFStringRef defaultName) { + CFPreferencesDomainRef appDomain; + + __CFSpinLock(&__CFApplicationPreferencesLock); + appDomain = _CFPreferencesStandardDomain(self->_appName, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); + if(appDomain) { + _CFPreferencesDomainSet(appDomain, defaultName, NULL); + if (CFArrayContainsValue(self->_search, CFRangeMake(0, CFArrayGetCount(self->_search)), appDomain)) { + // If key exists, it will be in the _dictRep (but possibly overridden) + updateDictRep(self); + } + } + __CFSpinUnlock(&__CFApplicationPreferencesLock); +} + +static Boolean _CFApplicationPreferencesSynchronizeNoLock(_CFApplicationPreferences *self) { + Boolean success = _CFSynchronizeDomainCache(); + updateDictRep(self); + return success; +} + +Boolean _CFApplicationPreferencesSynchronize(_CFApplicationPreferences *self) { + Boolean result; + __CFSpinLock(&__CFApplicationPreferencesLock); + result = _CFApplicationPreferencesSynchronizeNoLock(self); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + return result; +} + +// appName should not be kCFPreferencesCurrentApplication going in to this call +_CFApplicationPreferences *_CFApplicationPreferencesCreateWithUser(CFStringRef userName, CFStringRef appName) { + CFAllocatorRef alloc = __CFPreferencesAllocator(); + _CFApplicationPreferences *self = (_CFApplicationPreferences*)CFAllocatorAllocate(alloc, sizeof(_CFApplicationPreferences), 0); + if (self) { + self->_dictRep = NULL; + self->_appName = (CFStringRef)CFRetain(appName); + self->_search = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks); + if (!self->_search) { + CFAllocatorDeallocate(alloc, self); + CFRelease(appName); + self = NULL; + } + } + return self; +} + +// Do NOT release the domain after adding it to the array; domain_expression should not return a retained object -- REW, 8/26/99 +#define ADD_DOMAIN(domain_expression) domain = domain_expression; if (domain) {CFArrayAppendValue(search, domain);} +void _CFApplicationPreferencesSetStandardSearchList(_CFApplicationPreferences *appPreferences) { + /* Here is how the domains end up in priority order in a search list. Only a subset of these are setup by default. + argument domain + this app, this user, managed + this app, any user, managed + this app, this user, this host + this app, this user, any host (AppDomain) + suiteN, this user, this host + suiteN, this user, any host + ... + suite0, this user, this host + suite0, this user, any host + any app, this user, this host + any app, this user, any host (GlobalDomain) + NSUserDefaults backwards-compat ICU domain + this app, any user, this host + this app, any user, any host + suiteN, any user, this host + suiteN, any user, any host + ... + suite0, any user, this host + suite0, any user, any host + any app, any user, this host + any app, any user, any host + registration domain + */ + CFPreferencesDomainRef domain; + CFMutableArrayRef search = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); + if (!search) { + // couldn't allocate memory! + return; + } + + ADD_DOMAIN(_CFPreferencesStandardDomain(appPreferences->_appName, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost)); + ADD_DOMAIN(_CFPreferencesStandardDomain(appPreferences->_appName, kCFPreferencesCurrentUser, kCFPreferencesAnyHost)); + ADD_DOMAIN(_CFPreferencesStandardDomain(kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost)); + ADD_DOMAIN(_CFPreferencesStandardDomain(kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost)); + ADD_DOMAIN(_CFPreferencesStandardDomain(appPreferences->_appName, kCFPreferencesAnyUser, kCFPreferencesCurrentHost)); + ADD_DOMAIN(_CFPreferencesStandardDomain(appPreferences->_appName, kCFPreferencesAnyUser, kCFPreferencesAnyHost)); + ADD_DOMAIN(_CFPreferencesStandardDomain(kCFPreferencesAnyApplication, kCFPreferencesAnyUser, kCFPreferencesCurrentHost)); + ADD_DOMAIN(_CFPreferencesStandardDomain(kCFPreferencesAnyApplication, kCFPreferencesAnyUser, kCFPreferencesAnyHost)); + + _CFApplicationPreferencesSetSearchList(appPreferences, search); + CFRelease(search); +} +#undef ADD_DOMAIN + + +__private_extern__ _CFApplicationPreferences *_CFStandardApplicationPreferences(CFStringRef appName) { + _CFApplicationPreferences *appPreferences; +// CFAssert(appName != kCFPreferencesAnyApplication, __kCFLogAssertion, "Cannot use any of the CFPreferences...App... functions with an appName of kCFPreferencesAnyApplication"); + __CFSpinLock(&__CFApplicationPreferencesLock); + if (!__CFStandardUserPreferences) { + __CFStandardUserPreferences = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, & kCFTypeDictionaryKeyCallBacks, NULL); + } + if (!__CFStandardUserPreferences) { + // Couldn't create + __CFSpinUnlock(&__CFApplicationPreferencesLock); + return NULL; + } + if ((appPreferences = (_CFApplicationPreferences *)CFDictionaryGetValue(__CFStandardUserPreferences, appName)) == NULL ) { + appPreferences = _CFApplicationPreferencesCreateWithUser(kCFPreferencesCurrentUser, appName); + CFDictionarySetValue(__CFStandardUserPreferences, appName, appPreferences); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + _CFApplicationPreferencesSetStandardSearchList(appPreferences); + } else { + __CFSpinUnlock(&__CFApplicationPreferencesLock); + } + return appPreferences; +} + +// Exclusively for Foundation's use +void _CFApplicationPreferencesSetCacheForApp(_CFApplicationPreferences *appPrefs, CFStringRef appName) { + __CFSpinLock(&__CFApplicationPreferencesLock); + if (!__CFStandardUserPreferences) { + __CFStandardUserPreferences = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL); + CFDictionarySetValue(__CFStandardUserPreferences, appName, appPrefs); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + } else { + _CFApplicationPreferences *oldPrefs = (_CFApplicationPreferences *)CFDictionaryGetValue(__CFStandardUserPreferences, appName); + CFDictionarySetValue(__CFStandardUserPreferences, appName, appPrefs); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + if (oldPrefs) { + _CFDeallocateApplicationPreferences(oldPrefs); + } + } +} + + +void _CFDeallocateApplicationPreferences(_CFApplicationPreferences *self) { + CFAllocatorRef alloc = __CFPreferencesAllocator(); + _CFApplicationPreferences *cachedPrefs = NULL; + __CFSpinLock(&__CFApplicationPreferencesLock); + + // Get us out of the cache before destroying! + if (__CFStandardUserPreferences) { + cachedPrefs = (_CFApplicationPreferences *)CFDictionaryGetValue(__CFStandardUserPreferences, self->_appName); + } + if (cachedPrefs == self) { + CFDictionaryRemoveValue(__CFStandardUserPreferences, self->_appName); + } + + if (self->_dictRep) CFRelease(self->_dictRep); + CFRelease(self->_search); + CFRelease(self->_appName); + CFAllocatorDeallocate(alloc, self); + __CFSpinUnlock(&__CFApplicationPreferencesLock); +} + + +CF_EXPORT +CFDictionaryRef _CFApplicationPreferencesCopyRepresentation(_CFApplicationPreferences *self) { + CFDictionaryRef dict; + __CFSpinLock(&__CFApplicationPreferencesLock); + if (!self->_dictRep) { + self->_dictRep = computeDictRep(self, true); + } + if (self->_dictRep) { + CFRetain(self->_dictRep); + } + dict = self->_dictRep; + __CFSpinUnlock(&__CFApplicationPreferencesLock); + return dict; +} + +static void _CFApplicationPreferencesSetSearchList(_CFApplicationPreferences *self, CFArrayRef newSearchList) { + CFIndex idx, count; + __CFSpinLock(&__CFApplicationPreferencesLock); + CFArrayRemoveAllValues(self->_search); + count = CFArrayGetCount(newSearchList); + for (idx = 0; idx < count; idx ++) { + CFArrayAppendValue(self->_search, CFArrayGetValueAtIndex(newSearchList, idx)); + } + updateDictRep(self); + __CFSpinUnlock(&__CFApplicationPreferencesLock); +} + +void CFPreferencesAddSuitePreferencesToApp(CFStringRef appName, CFStringRef suiteName) { + _CFApplicationPreferences *appPrefs; + + appPrefs = _CFStandardApplicationPreferences(appName); + _CFApplicationPreferencesAddSuitePreferences(appPrefs, suiteName); +} + +void _CFApplicationPreferencesAddSuitePreferences(_CFApplicationPreferences *appPrefs, CFStringRef suiteName) { + CFPreferencesDomainRef domain; + CFIndex idx; + CFRange range; + + // Find where to insert the new suite + __CFSpinLock(&__CFApplicationPreferencesLock); + domain = _CFPreferencesStandardDomain(appPrefs->_appName, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); + range.location = 0; + range.length = CFArrayGetCount(appPrefs->_search); + idx = domain ? CFArrayGetFirstIndexOfValue(appPrefs->_search, range, domain) : kCFNotFound; + __CFSpinUnlock(&__CFApplicationPreferencesLock); + idx ++; // We want just below the app domain. Coincidentally, this gives us the top of the list if the app domain has been removed. + domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); + if (domain) { + __CFSpinLock(&__CFApplicationPreferencesLock); + CFArrayInsertValueAtIndex(appPrefs->_search, idx, domain); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + range.length ++; + } + domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost); + if (domain) { + __CFSpinLock(&__CFApplicationPreferencesLock); + CFArrayInsertValueAtIndex(appPrefs->_search, idx, domain); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + range.length ++; + } + + // Now the AnyUser domains + domain = _CFPreferencesStandardDomain(appPrefs->_appName, kCFPreferencesAnyUser, kCFPreferencesAnyHost); + idx = domain ? CFArrayGetFirstIndexOfValue(appPrefs->_search, range, domain) : kCFNotFound; + if (idx == kCFNotFound) { + // Someone blew away the app domain. For the any user case, we look for right below the global domain + domain = _CFPreferencesStandardDomain(kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); + idx = domain ? CFArrayGetFirstIndexOfValue(appPrefs->_search, range, domain) : kCFNotFound; + if (idx == kCFNotFound) { + // Try the "any host" choice + domain = _CFPreferencesStandardDomain(kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost); + idx = domain ? CFArrayGetFirstIndexOfValue(appPrefs->_search, range, domain) : kCFNotFound; + if (idx == kCFNotFound) { + // We give up; put the new domains at the bottom + idx = CFArrayGetCount(appPrefs->_search) - 1; + } + } + } + idx ++; + domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesAnyUser, kCFPreferencesAnyHost); + if (domain) { + __CFSpinLock(&__CFApplicationPreferencesLock); + CFArrayInsertValueAtIndex(appPrefs->_search, idx, domain); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + } + domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesAnyUser, kCFPreferencesCurrentHost); + if (domain) { + __CFSpinLock(&__CFApplicationPreferencesLock); + CFArrayInsertValueAtIndex(appPrefs->_search, idx, domain); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + } + __CFSpinLock(&__CFApplicationPreferencesLock); + updateDictRep(appPrefs); + __CFSpinUnlock(&__CFApplicationPreferencesLock); +} + +void CFPreferencesRemoveSuitePreferencesFromApp(CFStringRef appName, CFStringRef suiteName) { + _CFApplicationPreferences *appPrefs; + + appPrefs = _CFStandardApplicationPreferences(appName); + + _CFApplicationPreferencesRemoveSuitePreferences(appPrefs, suiteName); +} + +void _CFApplicationPreferencesRemoveSuitePreferences(_CFApplicationPreferences *appPrefs, CFStringRef suiteName) { + CFPreferencesDomainRef domain; + + __CFSpinLock(&__CFApplicationPreferencesLock); + domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + if (domain) _CFApplicationPreferencesRemoveDomain(appPrefs, domain); + + __CFSpinLock(&__CFApplicationPreferencesLock); + domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + if (domain) _CFApplicationPreferencesRemoveDomain(appPrefs, domain); + + __CFSpinLock(&__CFApplicationPreferencesLock); + domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesAnyUser, kCFPreferencesAnyHost); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + if (domain) _CFApplicationPreferencesRemoveDomain(appPrefs, domain); + + __CFSpinLock(&__CFApplicationPreferencesLock); + domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesAnyUser, kCFPreferencesCurrentHost); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + if (domain) _CFApplicationPreferencesRemoveDomain(appPrefs, domain); +} + +void _CFApplicationPreferencesAddDomain(_CFApplicationPreferences *self, CFPreferencesDomainRef domain, Boolean addAtTop) { + __CFSpinLock(&__CFApplicationPreferencesLock); + if (addAtTop) { + CFArrayInsertValueAtIndex(self->_search, 0, domain); + } else { + CFArrayAppendValue(self->_search, domain); + } + updateDictRep(self); + __CFSpinUnlock(&__CFApplicationPreferencesLock); +} + +Boolean _CFApplicationPreferencesContainsDomain(_CFApplicationPreferences *self, CFPreferencesDomainRef domain) { + Boolean result; + + if (!domain) { + return false; + } + + __CFSpinLock(&__CFApplicationPreferencesLock); + result = CFArrayContainsValue(self->_search, CFRangeMake(0, CFArrayGetCount(self->_search)), domain); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + return result; +} + +Boolean _CFApplicationPreferencesContainsDomainNoLock(_CFApplicationPreferences *self, CFPreferencesDomainRef domain) { + Boolean result; + result = CFArrayContainsValue(self->_search, CFRangeMake(0, CFArrayGetCount(self->_search)), domain); + return result; +} + +void _CFApplicationPreferencesRemoveDomain(_CFApplicationPreferences *self, CFPreferencesDomainRef domain) { + CFIndex idx; + CFRange range; + __CFSpinLock(&__CFApplicationPreferencesLock); + range.location = 0; + range.length = CFArrayGetCount(self->_search); + while ((idx = CFArrayGetFirstIndexOfValue(self->_search, range, domain)) != kCFNotFound) { + CFArrayRemoveValueAtIndex(self->_search, idx); + range.location = idx; + range.length = range.length - idx - 1; + } + updateDictRep(self); + __CFSpinUnlock(&__CFApplicationPreferencesLock); +}
diff --git a/CoreFoundation/CFArray.c b/CoreFoundation/CFArray.c new file mode 100644 index 0000000..6003c5e --- /dev/null +++ b/CoreFoundation/CFArray.c
@@ -0,0 +1,1201 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFArray.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include "CFInternal.h" +#include <CoreFoundation/CFArray.h> +#include "CFStorage.h" +#include "CFPriv.h" +#include <string.h> +#include "auto_stubs.h" + +#if DEPLOYMENT_TARGET_WINDOWS +#define bzero(buf, bytes) ((void) memset ((buf), 0x00, (bytes))) +CF_EXPORT bool _CFArrayIsMutable(CFArrayRef array); +#endif + +__private_extern__ void _CFStorageSetWeak(CFStorageRef storage); + +const CFArrayCallBacks kCFTypeArrayCallBacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, CFCopyDescription, CFEqual}; +static const CFArrayCallBacks __kCFNullArrayCallBacks = {0, NULL, NULL, NULL, NULL}; + +struct __CFArrayBucket { + const void *_item; +}; + +enum { + __CF_MAX_BUCKETS_PER_DEQUE = 262140 +}; + +CF_INLINE CFIndex __CFArrayDequeRoundUpCapacity(CFIndex capacity) { + if (capacity < 4) return 4; + return __CFMin((1 << flsl(capacity)), __CF_MAX_BUCKETS_PER_DEQUE); +} + +struct __CFArrayDeque { + uint32_t _leftIdx; + uint32_t _capacity; + int32_t _bias; +#if __LP64__ + uint32_t _pad; // GC: pointers must be 8-byte aligned for the collector to find them. +#endif + /* struct __CFArrayBucket buckets follow here */ +}; + +struct __CFArray { + CFRuntimeBase _base; + CFIndex _count; /* number of objects */ + CFIndex _mutations; + void *_store; /* can be NULL when MutableDeque */ +}; + +/* Flag bits */ +enum { /* Bits 0-1 */ + __kCFArrayImmutable = 0, + __kCFArrayDeque = 2, + __kCFArrayStorage = 3 +}; + +enum { /* Bits 2-3 */ + __kCFArrayHasNullCallBacks = 0, + __kCFArrayHasCFTypeCallBacks = 1, + __kCFArrayHasCustomCallBacks = 3 /* callbacks are at end of header */ +}; + +/* + Bits 4 & 5 are reserved for GC use. + Bit 4, if set, indicates that the array is weak. + Bit 5 marks whether finalization has occured and, thus, whether to continue to do special retain/release processing of elements. + */ + +CF_INLINE bool isStrongMemory(CFTypeRef collection) { + return __CFBitfieldGetValue(((const CFRuntimeBase *)collection)->_cfinfo[CF_INFO_BITS], 4, 4) == 0; +} + +CF_INLINE bool isWeakMemory(CFTypeRef collection) { + return __CFBitfieldGetValue(((const CFRuntimeBase *)collection)->_cfinfo[CF_INFO_BITS], 4, 4) != 0; +} + +CF_INLINE bool hasBeenFinalized(CFTypeRef collection) { + return __CFBitfieldGetValue(((const CFRuntimeBase *)collection)->_cfinfo[CF_INFO_BITS], 5, 5) != 0; +} + +CF_INLINE void markFinalized(CFTypeRef collection) { + __CFBitfieldSetValue(((CFRuntimeBase *)collection)->_cfinfo[CF_INFO_BITS], 5, 5, 1); +} + +CF_INLINE CFIndex __CFArrayGetType(CFArrayRef array) { + return __CFBitfieldGetValue(((const CFRuntimeBase *)array)->_cfinfo[CF_INFO_BITS], 1, 0); +} + +CF_INLINE CFIndex __CFArrayGetSizeOfType(CFIndex t) { + CFIndex size = 0; + size += sizeof(struct __CFArray); + if (__CFBitfieldGetValue(t, 3, 2) == __kCFArrayHasCustomCallBacks) { + size += sizeof(CFArrayCallBacks); + } + return size; +} + +CF_INLINE CFIndex __CFArrayGetCount(CFArrayRef array) { + return array->_count; +} + +CF_INLINE void __CFArraySetCount(CFArrayRef array, CFIndex v) { + ((struct __CFArray *)array)->_count = v; +} + +/* Only applies to immutable and mutable-deque-using arrays; + * Returns the bucket holding the left-most real value in the latter case. */ +CF_INLINE struct __CFArrayBucket *__CFArrayGetBucketsPtr(CFArrayRef array) { + switch (__CFArrayGetType(array)) { + case __kCFArrayImmutable: + return (struct __CFArrayBucket *)((uint8_t *)array + __CFArrayGetSizeOfType(((CFRuntimeBase *)array)->_cfinfo[CF_INFO_BITS])); + case __kCFArrayDeque: { + struct __CFArrayDeque *deque = (struct __CFArrayDeque *)array->_store; + return (struct __CFArrayBucket *)((uint8_t *)deque + sizeof(struct __CFArrayDeque) + deque->_leftIdx * sizeof(struct __CFArrayBucket)); + } + } + return NULL; +} + +/* This shouldn't be called if the array count is 0. */ +CF_INLINE struct __CFArrayBucket *__CFArrayGetBucketAtIndex(CFArrayRef array, CFIndex idx) { + switch (__CFArrayGetType(array)) { + case __kCFArrayImmutable: + case __kCFArrayDeque: + return __CFArrayGetBucketsPtr(array) + idx; + case __kCFArrayStorage: { + CFStorageRef store = (CFStorageRef)array->_store; + return (struct __CFArrayBucket *)CFStorageGetValueAtIndex(store, idx, NULL); + } + } + return NULL; +} + +CF_INLINE CFArrayCallBacks *__CFArrayGetCallBacks(CFArrayRef array) { + CFArrayCallBacks *result = NULL; + switch (__CFBitfieldGetValue(((const CFRuntimeBase *)array)->_cfinfo[CF_INFO_BITS], 3, 2)) { + case __kCFArrayHasNullCallBacks: + return (CFArrayCallBacks *)&__kCFNullArrayCallBacks; + case __kCFArrayHasCFTypeCallBacks: + return (CFArrayCallBacks *)&kCFTypeArrayCallBacks; + case __kCFArrayHasCustomCallBacks: + break; + } + switch (__CFArrayGetType(array)) { + case __kCFArrayImmutable: + result = (CFArrayCallBacks *)((uint8_t *)array + sizeof(struct __CFArray)); + break; + case __kCFArrayDeque: + case __kCFArrayStorage: + result = (CFArrayCallBacks *)((uint8_t *)array + sizeof(struct __CFArray)); + break; + } + return result; +} + +CF_INLINE bool __CFArrayCallBacksMatchNull(const CFArrayCallBacks *c) { + return (NULL == c || + (c->retain == __kCFNullArrayCallBacks.retain && + c->release == __kCFNullArrayCallBacks.release && + c->copyDescription == __kCFNullArrayCallBacks.copyDescription && + c->equal == __kCFNullArrayCallBacks.equal)); +} + +CF_INLINE bool __CFArrayCallBacksMatchCFType(const CFArrayCallBacks *c) { + return (&kCFTypeArrayCallBacks == c || + (c->retain == kCFTypeArrayCallBacks.retain && + c->release == kCFTypeArrayCallBacks.release && + c->copyDescription == kCFTypeArrayCallBacks.copyDescription && + c->equal == kCFTypeArrayCallBacks.equal)); +} + +struct _releaseContext { + void (*release)(CFAllocatorRef, const void *); + CFAllocatorRef allocator; +}; + +static void __CFArrayStorageRelease(const void *itemptr, void *context) { + struct _releaseContext *rc = (struct _releaseContext *)context; + INVOKE_CALLBACK2(rc->release, rc->allocator, *(const void **)itemptr); + *(const void **)itemptr = NULL; // GC: clear item to break strong reference. +} + +static void __CFArrayReleaseValues(CFArrayRef array, CFRange range, bool releaseStorageIfPossible) { + const CFArrayCallBacks *cb = __CFArrayGetCallBacks(array); + CFAllocatorRef allocator; + CFIndex idx; + switch (__CFArrayGetType(array)) { + case __kCFArrayImmutable: + if (NULL != cb->release && 0 < range.length && !hasBeenFinalized(array)) { + // if we've been finalized then we know that + // 1) we're using the standard callback on GC memory + // 2) the slots don't' need to be zeroed + struct __CFArrayBucket *buckets = __CFArrayGetBucketsPtr(array); + allocator = __CFGetAllocator(array); + for (idx = 0; idx < range.length; idx++) { + INVOKE_CALLBACK2(cb->release, allocator, buckets[idx + range.location]._item); + buckets[idx + range.location]._item = NULL; // GC: break strong reference. + } + } + break; + case __kCFArrayDeque: { + struct __CFArrayDeque *deque = (struct __CFArrayDeque *)array->_store; + if (0 < range.length && NULL != deque && !hasBeenFinalized(array)) { + struct __CFArrayBucket *buckets = __CFArrayGetBucketsPtr(array); + if (NULL != cb->release) { + allocator = __CFGetAllocator(array); + for (idx = 0; idx < range.length; idx++) { + INVOKE_CALLBACK2(cb->release, allocator, buckets[idx + range.location]._item); + buckets[idx + range.location]._item = NULL; // GC: break strong reference. + } + } else { + for (idx = 0; idx < range.length; idx++) { + buckets[idx + range.location]._item = NULL; // GC: break strong reference. + } + } + } + if (releaseStorageIfPossible && 0 == range.location && __CFArrayGetCount(array) == range.length) { + allocator = __CFGetAllocator(array); + if (NULL != deque) _CFAllocatorDeallocateGC(allocator, deque); + __CFArraySetCount(array, 0); // GC: _count == 0 ==> _store == NULL. + ((struct __CFArray *)array)->_store = NULL; + } + break; + } + case __kCFArrayStorage: { + CFStorageRef store = (CFStorageRef)array->_store; + if (NULL != cb->release && 0 < range.length && !hasBeenFinalized(array)) { + struct _releaseContext context; + allocator = __CFGetAllocator(array); + context.release = cb->release; + context.allocator = allocator; + CFStorageApplyFunction(store, range, __CFArrayStorageRelease, &context); + } + if (releaseStorageIfPossible && 0 == range.location && __CFArrayGetCount(array) == range.length) { + _CFReleaseGC(store); + __CFArraySetCount(array, 0); // GC: _count == 0 ==> _store == NULL. + ((struct __CFArray *)array)->_store = NULL; + __CFBitfieldSetValue(((CFRuntimeBase *)array)->_cfinfo[CF_INFO_BITS], 1, 0, __kCFArrayDeque); + } + break; + } + } +} + +#if defined(DEBUG) +CF_INLINE void __CFArrayValidateRange(CFArrayRef array, CFRange range, const char *func) { + CFAssert3(0 <= range.location && range.location <= CFArrayGetCount(array), __kCFLogAssertion, "%s(): range.location index (%d) out of bounds (0, %d)", func, range.location, CFArrayGetCount(array)); + CFAssert2(0 <= range.length, __kCFLogAssertion, "%s(): range.length (%d) cannot be less than zero", func, range.length); + CFAssert3(range.location + range.length <= CFArrayGetCount(array), __kCFLogAssertion, "%s(): ending index (%d) out of bounds (0, %d)", func, range.location + range.length, CFArrayGetCount(array)); +} +#else +#define __CFArrayValidateRange(a,r,f) +#endif + +static Boolean __CFArrayEqual(CFTypeRef cf1, CFTypeRef cf2) { + CFArrayRef array1 = (CFArrayRef)cf1; + CFArrayRef array2 = (CFArrayRef)cf2; + const CFArrayCallBacks *cb1, *cb2; + CFIndex idx, cnt; + if (array1 == array2) return true; + cnt = __CFArrayGetCount(array1); + if (cnt != __CFArrayGetCount(array2)) return false; + cb1 = __CFArrayGetCallBacks(array1); + cb2 = __CFArrayGetCallBacks(array2); + if (cb1->equal != cb2->equal) return false; + if (0 == cnt) return true; /* after function comparison! */ + for (idx = 0; idx < cnt; idx++) { + const void *val1 = __CFArrayGetBucketAtIndex(array1, idx)->_item; + const void *val2 = __CFArrayGetBucketAtIndex(array2, idx)->_item; + if (val1 != val2) { + if (NULL == cb1->equal) return false; + if (!INVOKE_CALLBACK2(cb1->equal, val1, val2)) return false; + } + } + return true; +} + +static CFHashCode __CFArrayHash(CFTypeRef cf) { + CFArrayRef array = (CFArrayRef)cf; + return __CFArrayGetCount(array); +} + +static CFStringRef __CFArrayCopyDescription(CFTypeRef cf) { + CFArrayRef array = (CFArrayRef)cf; + CFMutableStringRef result; + const CFArrayCallBacks *cb; + CFAllocatorRef allocator; + CFIndex idx, cnt; + cnt = __CFArrayGetCount(array); + allocator = CFGetAllocator(array); + result = CFStringCreateMutable(allocator, 0); + switch (__CFArrayGetType(array)) { + case __kCFArrayImmutable: + CFStringAppendFormat(result, NULL, CFSTR("<CFArray %p [%p]>{type = immutable, count = %u, values = (\n"), cf, allocator, cnt); + break; + case __kCFArrayDeque: + CFStringAppendFormat(result, NULL, CFSTR("<CFArray %p [%p]>{type = mutable-small, count = %u, values = (\n"), cf, allocator, cnt); + break; + case __kCFArrayStorage: + CFStringAppendFormat(result, NULL, CFSTR("<CFArray %p [%p]>{type = mutable-large, count = %u, values = (\n"), cf, allocator, cnt); + break; + } + cb = __CFArrayGetCallBacks(array); + for (idx = 0; idx < cnt; idx++) { + CFStringRef desc = NULL; + const void *val = __CFArrayGetBucketAtIndex(array, idx)->_item; + if (NULL != cb->copyDescription) { + desc = (CFStringRef)INVOKE_CALLBACK1(cb->copyDescription, val); + } + if (NULL != desc) { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : %@\n"), idx, desc); + CFRelease(desc); + } else { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : <%p>\n"), idx, val); + } + } + CFStringAppend(result, CFSTR(")}")); + return result; +} + + +static void __CFResourceRelease(CFTypeRef cf, const void *ignored) { + kCFTypeArrayCallBacks.release(kCFAllocatorSystemDefault, cf); +} + +static void __CFArrayDeallocate(CFTypeRef cf) { + CFArrayRef array = (CFArrayRef)cf; + // Under GC, keep contents alive when we know we can, either standard callbacks or NULL + // if (__CFBitfieldGetValue(cf->info, 5, 4)) return; // bits only ever set under GC + CFAllocatorRef allocator = __CFGetAllocator(array); + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + // XXX_PCB keep array intact during finalization. + const CFArrayCallBacks *cb = __CFArrayGetCallBacks(array); + if (cb->retain == NULL && cb->release == NULL) + return; + if (cb == &kCFTypeArrayCallBacks || cb->release == kCFTypeArrayCallBacks.release) { + markFinalized(cf); + CFArrayApplyFunction((CFArrayRef)cf, CFRangeMake(0, __CFArrayGetCount(array)), (CFArrayApplierFunction)__CFResourceRelease, 0); + return; + } + } + __CFArrayReleaseValues(array, CFRangeMake(0, __CFArrayGetCount(array)), true); +} + +static CFTypeID __kCFArrayTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFArrayClass = { + _kCFRuntimeScannedObject, + "CFArray", + NULL, // init + NULL, // copy + __CFArrayDeallocate, + __CFArrayEqual, + __CFArrayHash, + NULL, // + __CFArrayCopyDescription +}; + +__private_extern__ void __CFArrayInitialize(void) { + __kCFArrayTypeID = _CFRuntimeRegisterClass(&__CFArrayClass); +} + +CFTypeID CFArrayGetTypeID(void) { + return __kCFArrayTypeID; +} + +static CFArrayRef __CFArrayInit(CFAllocatorRef allocator, UInt32 flags, CFIndex capacity, const CFArrayCallBacks *callBacks) { + struct __CFArray *memory; + UInt32 size; + __CFBitfieldSetValue(flags, 31, 2, 0); + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + if (!callBacks || (callBacks->retain == NULL && callBacks->release == NULL)) { + __CFBitfieldSetValue(flags, 4, 4, 1); // setWeak + } + } + if (__CFArrayCallBacksMatchNull(callBacks)) { + __CFBitfieldSetValue(flags, 3, 2, __kCFArrayHasNullCallBacks); + } else if (__CFArrayCallBacksMatchCFType(callBacks)) { + __CFBitfieldSetValue(flags, 3, 2, __kCFArrayHasCFTypeCallBacks); + } else { + __CFBitfieldSetValue(flags, 3, 2, __kCFArrayHasCustomCallBacks); + } + size = __CFArrayGetSizeOfType(flags) - sizeof(CFRuntimeBase); + switch (__CFBitfieldGetValue(flags, 1, 0)) { + case __kCFArrayImmutable: + size += capacity * sizeof(struct __CFArrayBucket); + break; + case __kCFArrayDeque: + case __kCFArrayStorage: + break; + } + memory = (struct __CFArray*)_CFRuntimeCreateInstance(allocator, __kCFArrayTypeID, size, NULL); + if (NULL == memory) { + return NULL; + } + __CFBitfieldSetValue(memory->_base._cfinfo[CF_INFO_BITS], 6, 0, flags); + __CFArraySetCount((CFArrayRef)memory, 0); + switch (__CFBitfieldGetValue(flags, 1, 0)) { + case __kCFArrayImmutable: + if (isWeakMemory(memory)) { // if weak, don't scan + auto_zone_set_layout_type(__CFCollectableZone, memory, AUTO_OBJECT_UNSCANNED); + } + if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFArray (immutable)"); + break; + case __kCFArrayDeque: + case __kCFArrayStorage: + if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFArray (mutable-variable)"); + ((struct __CFArray *)memory)->_mutations = 1; + ((struct __CFArray*)memory)->_store = NULL; + break; + } + if (__kCFArrayHasCustomCallBacks == __CFBitfieldGetValue(flags, 3, 2)) { + CFArrayCallBacks *cb = (CFArrayCallBacks *)__CFArrayGetCallBacks((CFArrayRef)memory); + *cb = *callBacks; + FAULT_CALLBACK((void **)&(cb->retain)); + FAULT_CALLBACK((void **)&(cb->release)); + FAULT_CALLBACK((void **)&(cb->copyDescription)); + FAULT_CALLBACK((void **)&(cb->equal)); + } + return (CFArrayRef)memory; +} + +CFArrayRef CFArrayCreate(CFAllocatorRef allocator, const void **values, CFIndex numValues, const CFArrayCallBacks *callBacks) { + CFArrayRef result; + const CFArrayCallBacks *cb; + struct __CFArrayBucket *buckets; + CFAllocatorRef bucketsAllocator; + void* bucketsBase; + CFIndex idx; + CFAssert2(0 <= numValues, __kCFLogAssertion, "%s(): numValues (%d) cannot be less than zero", __PRETTY_FUNCTION__, numValues); + result = __CFArrayInit(allocator, __kCFArrayImmutable, numValues, callBacks); + cb = __CFArrayGetCallBacks(result); + buckets = __CFArrayGetBucketsPtr(result); + bucketsAllocator = isStrongMemory(result) ? allocator : kCFAllocatorNull; + bucketsBase = CF_IS_COLLECTABLE_ALLOCATOR(bucketsAllocator) ? (void *)auto_zone_base_pointer(__CFCollectableZone, buckets) : NULL; + if (NULL != cb->retain) { + for (idx = 0; idx < numValues; idx++) { + CF_WRITE_BARRIER_BASE_ASSIGN(bucketsAllocator, bucketsBase, buckets->_item, (void *)INVOKE_CALLBACK2(cb->retain, allocator, *values)); + values++; + buckets++; + } + } + else { + for (idx = 0; idx < numValues; idx++) { + CF_WRITE_BARRIER_BASE_ASSIGN(bucketsAllocator, bucketsBase, buckets->_item, *values); + values++; + buckets++; + } + } + __CFArraySetCount(result, numValues); + return result; +} + +CFMutableArrayRef CFArrayCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFArrayCallBacks *callBacks) { + CFAssert2(0 <= capacity, __kCFLogAssertion, "%s(): capacity (%d) cannot be less than zero", __PRETTY_FUNCTION__, capacity); + CFAssert2(capacity <= (CFIndex)(LONG_MAX / sizeof(void *)), __kCFLogAssertion, "%s(): capacity (%d) is too large for this architecture", __PRETTY_FUNCTION__, capacity); + return (CFMutableArrayRef)__CFArrayInit(allocator, __kCFArrayDeque, capacity, callBacks); +} + +// This creates an array which is for CFTypes or NSObjects, with an ownership transfer -- +// the array does not take a retain, and the caller does not need to release the inserted objects. +// The incoming objects must also be collectable if allocated out of a collectable allocator. +CFArrayRef _CFArrayCreate_ex(CFAllocatorRef allocator, Boolean isMutable, const void **values, CFIndex numValues) { + CFArrayRef result; + result = __CFArrayInit(allocator, isMutable ? __kCFArrayDeque : __kCFArrayImmutable, numValues, &kCFTypeArrayCallBacks); + if (!isMutable) { + struct __CFArrayBucket *buckets = __CFArrayGetBucketsPtr(result); + CF_WRITE_BARRIER_MEMMOVE(buckets, values, numValues * sizeof(struct __CFArrayBucket)); + } else { + if (__CF_MAX_BUCKETS_PER_DEQUE <= numValues) { + CFStorageRef store = (CFStorageRef)CFMakeCollectable(CFStorageCreate(allocator, sizeof(const void *))); + if (__CFOASafe) __CFSetLastAllocationEventName(store, "CFArray (store-storage)"); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, result, result->_store, store); + CFStorageInsertValues(store, CFRangeMake(0, numValues)); + CFStorageReplaceValues(store, CFRangeMake(0, numValues), values); + __CFBitfieldSetValue(((CFRuntimeBase *)result)->_cfinfo[CF_INFO_BITS], 1, 0, __kCFArrayStorage); + } else if (0 <= numValues) { + struct __CFArrayDeque *deque; + struct __CFArrayBucket *raw_buckets; + CFIndex capacity = __CFArrayDequeRoundUpCapacity(numValues); + CFIndex size = sizeof(struct __CFArrayDeque) + capacity * sizeof(struct __CFArrayBucket); + deque = (struct __CFArrayDeque *)_CFAllocatorAllocateGC(allocator, size, isStrongMemory(result) ? __kCFAllocatorGCScannedMemory : 0); + if (__CFOASafe) __CFSetLastAllocationEventName(deque, "CFArray (store-deque)"); + deque->_leftIdx = (capacity - numValues) / 2; + deque->_capacity = capacity; + deque->_bias = 0; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, result, result->_store, deque); + raw_buckets = (struct __CFArrayBucket *)((uint8_t *)deque + sizeof(struct __CFArrayDeque)); + CF_WRITE_BARRIER_MEMMOVE(raw_buckets + deque->_leftIdx + 0, values, numValues * sizeof(struct __CFArrayBucket)); + __CFBitfieldSetValue(((CFRuntimeBase *)result)->_cfinfo[CF_INFO_BITS], 1, 0, __kCFArrayDeque); + } + } + __CFArraySetCount(result, numValues); + return result; +} + +CFArrayRef CFArrayCreateCopy(CFAllocatorRef allocator, CFArrayRef array) { + CFArrayRef result; + const CFArrayCallBacks *cb; + struct __CFArrayBucket *buckets; + CFAllocatorRef bucketsAllocator; + void* bucketsBase; + CFIndex numValues = CFArrayGetCount(array); + CFIndex idx; + if (CF_IS_OBJC(__kCFArrayTypeID, array)) { + cb = &kCFTypeArrayCallBacks; + } else { + cb = __CFArrayGetCallBacks(array); + } + result = __CFArrayInit(allocator, __kCFArrayImmutable, numValues, cb); + cb = __CFArrayGetCallBacks(result); // GC: use the new array's callbacks so we don't leak. + buckets = __CFArrayGetBucketsPtr(result); + bucketsAllocator = isStrongMemory(result) ? allocator : kCFAllocatorNull; + bucketsBase = CF_IS_COLLECTABLE_ALLOCATOR(bucketsAllocator) ? (void *)auto_zone_base_pointer(__CFCollectableZone, buckets) : NULL; + for (idx = 0; idx < numValues; idx++) { + const void *value = CFArrayGetValueAtIndex(array, idx); + if (NULL != cb->retain) { + value = (void *)INVOKE_CALLBACK2(cb->retain, allocator, value); + } + CF_WRITE_BARRIER_BASE_ASSIGN(bucketsAllocator, bucketsBase, buckets->_item, value); + buckets++; + } + __CFArraySetCount(result, numValues); + return result; +} + +CFMutableArrayRef CFArrayCreateMutableCopy(CFAllocatorRef allocator, CFIndex capacity, CFArrayRef array) { + CFMutableArrayRef result; + const CFArrayCallBacks *cb; + CFIndex idx, numValues = CFArrayGetCount(array); + UInt32 flags; + if (CF_IS_OBJC(__kCFArrayTypeID, array)) { + cb = &kCFTypeArrayCallBacks; + } + else { + cb = __CFArrayGetCallBacks(array); + } + flags = __kCFArrayDeque; + result = (CFMutableArrayRef)__CFArrayInit(allocator, flags, capacity, cb); + if (0 == capacity) _CFArraySetCapacity(result, numValues); + for (idx = 0; idx < numValues; idx++) { + const void *value = CFArrayGetValueAtIndex(array, idx); + CFArrayAppendValue(result, value); + } + return result; +} + +CFIndex CFArrayGetCount(CFArrayRef array) { + CF_OBJC_FUNCDISPATCH0(__kCFArrayTypeID, CFIndex, array, "count"); + __CFGenericValidateType(array, __kCFArrayTypeID); + return __CFArrayGetCount(array); +} + + +CFIndex CFArrayGetCountOfValue(CFArrayRef array, CFRange range, const void *value) { + const CFArrayCallBacks *cb; + CFIndex idx, count = 0; +// CF: this ignores range + CF_OBJC_FUNCDISPATCH1(__kCFArrayTypeID, CFIndex, array, "countOccurrences:", value); + __CFGenericValidateType(array, __kCFArrayTypeID); + __CFArrayValidateRange(array, range, __PRETTY_FUNCTION__); + cb = __CFArrayGetCallBacks(array); + for (idx = 0; idx < range.length; idx++) { + const void *item = __CFArrayGetBucketAtIndex(array, range.location + idx)->_item; + if (value == item || (cb->equal && INVOKE_CALLBACK2(cb->equal, value, item))) { + count++; + } + } + return count; +} + +Boolean CFArrayContainsValue(CFArrayRef array, CFRange range, const void *value) { + CFIndex idx; + CF_OBJC_FUNCDISPATCH2(__kCFArrayTypeID, char, array, "containsObject:inRange:", value, range); + __CFGenericValidateType(array, __kCFArrayTypeID); + __CFArrayValidateRange(array, range, __PRETTY_FUNCTION__); + for (idx = 0; idx < range.length; idx++) { + const void *item = __CFArrayGetBucketAtIndex(array, range.location + idx)->_item; + if (value == item) { + return true; + } + } + const CFArrayCallBacks *cb = __CFArrayGetCallBacks(array); + if (cb->equal) { + for (idx = 0; idx < range.length; idx++) { + const void *item = __CFArrayGetBucketAtIndex(array, range.location + idx)->_item; + if (INVOKE_CALLBACK2(cb->equal, value, item)) { + return true; + } + } + } + return false; +} + +const void *CFArrayGetValueAtIndex(CFArrayRef array, CFIndex idx) { + CF_OBJC_FUNCDISPATCH1(__kCFArrayTypeID, void *, array, "objectAtIndex:", idx); + __CFGenericValidateType(array, __kCFArrayTypeID); + CFAssert2(0 <= idx && idx < __CFArrayGetCount(array), __kCFLogAssertion, "%s(): index (%d) out of bounds", __PRETTY_FUNCTION__, idx); + return __CFArrayGetBucketAtIndex(array, idx)->_item; +} + +// This is for use by NSCFArray; it avoids ObjC dispatch, and checks for out of bounds +const void *_CFArrayCheckAndGetValueAtIndex(CFArrayRef array, CFIndex idx) { + if (0 <= idx && idx < __CFArrayGetCount(array)) return __CFArrayGetBucketAtIndex(array, idx)->_item; + return (void *)(-1); +} + + +void CFArrayGetValues(CFArrayRef array, CFRange range, const void **values) { + CF_OBJC_FUNCDISPATCH2(__kCFArrayTypeID, void, array, "getObjects:range:", values, range); + __CFGenericValidateType(array, __kCFArrayTypeID); + __CFArrayValidateRange(array, range, __PRETTY_FUNCTION__); + CFAssert1(NULL != values, __kCFLogAssertion, "%s(): pointer to values may not be NULL", __PRETTY_FUNCTION__); + if (0 < range.length) { + switch (__CFArrayGetType(array)) { + case __kCFArrayImmutable: + case __kCFArrayDeque: + CF_WRITE_BARRIER_MEMMOVE(values, __CFArrayGetBucketsPtr(array) + range.location, range.length * sizeof(struct __CFArrayBucket)); + break; + case __kCFArrayStorage: { + CFStorageRef store = (CFStorageRef)array->_store; + CFStorageGetValues(store, range, values); + break; + } + } + } +} + + +unsigned long _CFArrayFastEnumeration(CFArrayRef array, struct __objcFastEnumerationStateEquivalent *state, void *stackbuffer, unsigned long count) { + if (array->_count == 0) return 0; + enum { ATSTART = 0, ATEND = 1 }; + switch (__CFArrayGetType(array)) { + case __kCFArrayImmutable: + if (state->state == ATSTART) { /* first time */ + static const unsigned long const_mu = 1; + state->state = ATEND; + state->mutationsPtr = (unsigned long *)&const_mu; + state->itemsPtr = (unsigned long *)__CFArrayGetBucketsPtr(array); + return array->_count; + } + return 0; + case __kCFArrayDeque: + if (state->state == ATSTART) { /* first time */ + state->state = ATEND; + state->mutationsPtr = (unsigned long *)&array->_mutations; + state->itemsPtr = (unsigned long *)__CFArrayGetBucketsPtr(array); + return array->_count; + } + return 0; + case __kCFArrayStorage: + state->mutationsPtr = (unsigned long *)&array->_mutations; + return _CFStorageFastEnumeration((CFStorageRef)array->_store, state, stackbuffer, count); + } + return 0; +} + + +void CFArrayApplyFunction(CFArrayRef array, CFRange range, CFArrayApplierFunction applier, void *context) { + CFIndex idx; + FAULT_CALLBACK((void **)&(applier)); + CF_OBJC_FUNCDISPATCH2(__kCFArrayTypeID, void, array, "apply:context:", applier, context); + __CFGenericValidateType(array, __kCFArrayTypeID); + __CFArrayValidateRange(array, range, __PRETTY_FUNCTION__); + CFAssert1(NULL != applier, __kCFLogAssertion, "%s(): pointer to applier function may not be NULL", __PRETTY_FUNCTION__); + for (idx = 0; idx < range.length; idx++) { + const void *item = __CFArrayGetBucketAtIndex(array, range.location + idx)->_item; + INVOKE_CALLBACK2(applier, item, context); + } +} + +CFIndex CFArrayGetFirstIndexOfValue(CFArrayRef array, CFRange range, const void *value) { + const CFArrayCallBacks *cb; + CFIndex idx; + CF_OBJC_FUNCDISPATCH2(__kCFArrayTypeID, CFIndex, array, "_cfindexOfObject:inRange:", value, range); + __CFGenericValidateType(array, __kCFArrayTypeID); + __CFArrayValidateRange(array, range, __PRETTY_FUNCTION__); + cb = __CFArrayGetCallBacks(array); + for (idx = 0; idx < range.length; idx++) { + const void *item = __CFArrayGetBucketAtIndex(array, range.location + idx)->_item; + if (value == item || (cb->equal && INVOKE_CALLBACK2(cb->equal, value, item))) + return idx + range.location; + } + return kCFNotFound; +} + +CFIndex CFArrayGetLastIndexOfValue(CFArrayRef array, CFRange range, const void *value) { + const CFArrayCallBacks *cb; + CFIndex idx; + CF_OBJC_FUNCDISPATCH2(__kCFArrayTypeID, CFIndex, array, "_cflastIndexOfObject:inRange:", value, range); + __CFGenericValidateType(array, __kCFArrayTypeID); + __CFArrayValidateRange(array, range, __PRETTY_FUNCTION__); + cb = __CFArrayGetCallBacks(array); + for (idx = range.length; idx--;) { + const void *item = __CFArrayGetBucketAtIndex(array, range.location + idx)->_item; + if (value == item || (cb->equal && INVOKE_CALLBACK2(cb->equal, value, item))) + return idx + range.location; + } + return kCFNotFound; +} + +void CFArrayAppendValue(CFMutableArrayRef array, const void *value) { + CF_OBJC_FUNCDISPATCH1(__kCFArrayTypeID, void, array, "addObject:", value); + __CFGenericValidateType(array, __kCFArrayTypeID); + CFAssert1(__CFArrayGetType(array) != __kCFArrayImmutable, __kCFLogAssertion, "%s(): array is immutable", __PRETTY_FUNCTION__); + _CFArrayReplaceValues(array, CFRangeMake(__CFArrayGetCount(array), 0), &value, 1); +} + +void CFArraySetValueAtIndex(CFMutableArrayRef array, CFIndex idx, const void *value) { + CF_OBJC_FUNCDISPATCH2(__kCFArrayTypeID, void, array, "setObject:atIndex:", value, idx); + __CFGenericValidateType(array, __kCFArrayTypeID); + CFAssert1(__CFArrayGetType(array) != __kCFArrayImmutable, __kCFLogAssertion, "%s(): array is immutable", __PRETTY_FUNCTION__); + CFAssert2(0 <= idx && idx <= __CFArrayGetCount(array), __kCFLogAssertion, "%s(): index (%d) out of bounds", __PRETTY_FUNCTION__, idx); + if (idx == __CFArrayGetCount(array)) { + _CFArrayReplaceValues(array, CFRangeMake(idx, 0), &value, 1); + } else { + const void *old_value; + const CFArrayCallBacks *cb = __CFArrayGetCallBacks(array); + CFAllocatorRef allocator = __CFGetAllocator(array); + CFAllocatorRef bucketsAllocator = isStrongMemory(array) ? allocator : kCFAllocatorNull; + struct __CFArrayBucket *bucket = __CFArrayGetBucketAtIndex(array, idx); + if (NULL != cb->retain && !hasBeenFinalized(array)) { + value = (void *)INVOKE_CALLBACK2(cb->retain, allocator, value); + } + old_value = bucket->_item; + CF_WRITE_BARRIER_ASSIGN(bucketsAllocator, bucket->_item, value); // GC: handles deque/CFStorage cases. + if (NULL != cb->release && !hasBeenFinalized(array)) { + INVOKE_CALLBACK2(cb->release, allocator, old_value); + } + array->_mutations++; + } +} + +void CFArrayInsertValueAtIndex(CFMutableArrayRef array, CFIndex idx, const void *value) { + CF_OBJC_FUNCDISPATCH2(__kCFArrayTypeID, void, array, "insertObject:atIndex:", value, idx); + __CFGenericValidateType(array, __kCFArrayTypeID); + CFAssert1(__CFArrayGetType(array) != __kCFArrayImmutable, __kCFLogAssertion, "%s(): array is immutable", __PRETTY_FUNCTION__); + CFAssert2(0 <= idx && idx <= __CFArrayGetCount(array), __kCFLogAssertion, "%s(): index (%d) out of bounds", __PRETTY_FUNCTION__, idx); + _CFArrayReplaceValues(array, CFRangeMake(idx, 0), &value, 1); +} + +void CFArrayExchangeValuesAtIndices(CFMutableArrayRef array, CFIndex idx1, CFIndex idx2) { + const void *tmp; + struct __CFArrayBucket *bucket1, *bucket2; + CFAllocatorRef bucketsAllocator; + CF_OBJC_FUNCDISPATCH2(__kCFArrayTypeID, void, array, "exchange::", idx1, idx2); + __CFGenericValidateType(array, __kCFArrayTypeID); + CFAssert2(0 <= idx1 && idx1 < __CFArrayGetCount(array), __kCFLogAssertion, "%s(): index #1 (%d) out of bounds", __PRETTY_FUNCTION__, idx1); + CFAssert2(0 <= idx2 && idx2 < __CFArrayGetCount(array), __kCFLogAssertion, "%s(): index #2 (%d) out of bounds", __PRETTY_FUNCTION__, idx2); + CFAssert1(__CFArrayGetType(array) != __kCFArrayImmutable, __kCFLogAssertion, "%s(): array is immutable", __PRETTY_FUNCTION__); + bucket1 = __CFArrayGetBucketAtIndex(array, idx1); + bucket2 = __CFArrayGetBucketAtIndex(array, idx2); + tmp = bucket1->_item; + bucketsAllocator = isStrongMemory(array) ? __CFGetAllocator(array) : kCFAllocatorNull; + // XXX these aren't needed. + CF_WRITE_BARRIER_ASSIGN(bucketsAllocator, bucket1->_item, bucket2->_item); + CF_WRITE_BARRIER_ASSIGN(bucketsAllocator, bucket2->_item, tmp); + array->_mutations++; + +} + +void CFArrayRemoveValueAtIndex(CFMutableArrayRef array, CFIndex idx) { + CF_OBJC_FUNCDISPATCH1(__kCFArrayTypeID, void, array, "removeObjectAtIndex:", idx); + __CFGenericValidateType(array, __kCFArrayTypeID); + CFAssert1(__CFArrayGetType(array) != __kCFArrayImmutable, __kCFLogAssertion, "%s(): array is immutable", __PRETTY_FUNCTION__); + CFAssert2(0 <= idx && idx < __CFArrayGetCount(array), __kCFLogAssertion, "%s(): index (%d) out of bounds", __PRETTY_FUNCTION__, idx); + _CFArrayReplaceValues(array, CFRangeMake(idx, 1), NULL, 0); +} + +void CFArrayRemoveAllValues(CFMutableArrayRef array) { + CF_OBJC_FUNCDISPATCH0(__kCFArrayTypeID, void, array, "removeAllObjects"); + __CFGenericValidateType(array, __kCFArrayTypeID); + CFAssert1(__CFArrayGetType(array) != __kCFArrayImmutable, __kCFLogAssertion, "%s(): array is immutable", __PRETTY_FUNCTION__); + __CFArrayReleaseValues(array, CFRangeMake(0, __CFArrayGetCount(array)), true); + __CFArraySetCount(array, 0); + array->_mutations++; +} + +static void __CFArrayConvertDequeToStore(CFMutableArrayRef array) { + struct __CFArrayDeque *deque = (struct __CFArrayDeque *)array->_store; + struct __CFArrayBucket *raw_buckets = (struct __CFArrayBucket *)((uint8_t *)deque + sizeof(struct __CFArrayDeque)); + CFStorageRef store; + CFIndex count = CFArrayGetCount(array); + CFAllocatorRef allocator = __CFGetAllocator(array); + store = (CFStorageRef)CFMakeCollectable(CFStorageCreate(allocator, sizeof(const void *))); + if (__CFOASafe) __CFSetLastAllocationEventName(store, "CFArray (store-storage)"); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, array, array->_store, store); + CFStorageInsertValues(store, CFRangeMake(0, count)); + CFStorageReplaceValues(store, CFRangeMake(0, count), raw_buckets + deque->_leftIdx); + _CFAllocatorDeallocateGC(__CFGetAllocator(array), deque); + __CFBitfieldSetValue(((CFRuntimeBase *)array)->_cfinfo[CF_INFO_BITS], 1, 0, __kCFArrayStorage); +} + +static void __CFArrayConvertStoreToDeque(CFMutableArrayRef array) { + CFStorageRef store = (CFStorageRef)array->_store; + struct __CFArrayDeque *deque; + struct __CFArrayBucket *raw_buckets; + CFIndex count = CFStorageGetCount(store);// storage, not array, has correct count at this point + // do not resize down to a completely tight deque + CFIndex capacity = __CFArrayDequeRoundUpCapacity(count + 6); + CFIndex size = sizeof(struct __CFArrayDeque) + capacity * sizeof(struct __CFArrayBucket); + CFAllocatorRef allocator = __CFGetAllocator(array); + deque = (struct __CFArrayDeque *)_CFAllocatorAllocateGC(allocator, size, isStrongMemory(array) ? __kCFAllocatorGCScannedMemory : 0); + if (__CFOASafe) __CFSetLastAllocationEventName(deque, "CFArray (store-deque)"); + deque->_leftIdx = (capacity - count) / 2; + deque->_capacity = capacity; + deque->_bias = 0; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, array, array->_store, deque); + raw_buckets = (struct __CFArrayBucket *)((uint8_t *)deque + sizeof(struct __CFArrayDeque)); + CFStorageGetValues(store, CFRangeMake(0, count), raw_buckets + deque->_leftIdx); + _CFReleaseGC(store); // GC: balances CFMakeCollectable() above. + __CFBitfieldSetValue(((CFRuntimeBase *)array)->_cfinfo[CF_INFO_BITS], 1, 0, __kCFArrayDeque); +} + +// may move deque storage, as it may need to grow deque +static void __CFArrayRepositionDequeRegions(CFMutableArrayRef array, CFRange range, CFIndex newCount) { + // newCount elements are going to replace the range, and the result will fit in the deque + struct __CFArrayDeque *deque = (struct __CFArrayDeque *)array->_store; + struct __CFArrayBucket *buckets; + CFIndex cnt, futureCnt, numNewElems; + CFIndex L, A, B, C, R; + + buckets = (struct __CFArrayBucket *)((uint8_t *)deque + sizeof(struct __CFArrayDeque)); + cnt = __CFArrayGetCount(array); + futureCnt = cnt - range.length + newCount; + + L = deque->_leftIdx; // length of region to left of deque + A = range.location; // length of region in deque to left of replaced range + B = range.length; // length of replaced range + C = cnt - B - A; // length of region in deque to right of replaced range + R = deque->_capacity - cnt - L; // length of region to right of deque + numNewElems = newCount - B; + + CFIndex wiggle = deque->_capacity >> 17; + if (wiggle < 4) wiggle = 4; + if (deque->_capacity < (uint32_t)futureCnt || (cnt < futureCnt && L + R < wiggle)) { + // must be inserting or space is tight, reallocate and re-center everything + CFIndex capacity = __CFArrayDequeRoundUpCapacity(futureCnt + wiggle); + CFIndex size = sizeof(struct __CFArrayDeque) + capacity * sizeof(struct __CFArrayBucket); + CFAllocatorRef allocator = __CFGetAllocator(array); + struct __CFArrayDeque *newDeque = (struct __CFArrayDeque *)_CFAllocatorAllocateGC(allocator, size, isStrongMemory(array) ? __kCFAllocatorGCScannedMemory : 0); + if (__CFOASafe) __CFSetLastAllocationEventName(newDeque, "CFArray (store-deque)"); + struct __CFArrayBucket *newBuckets = (struct __CFArrayBucket *)((uint8_t *)newDeque + sizeof(struct __CFArrayDeque)); + CFIndex oldL = L; + CFIndex newL = (capacity - futureCnt) / 2; + CFIndex oldC0 = oldL + A + B; + CFIndex newC0 = newL + A + newCount; + newDeque->_leftIdx = newL; + newDeque->_capacity = capacity; + newDeque->_bias = 0; + if (0 < A) CF_WRITE_BARRIER_MEMMOVE(newBuckets + newL, buckets + oldL, A * sizeof(struct __CFArrayBucket)); + if (0 < C) CF_WRITE_BARRIER_MEMMOVE(newBuckets + newC0, buckets + oldC0, C * sizeof(struct __CFArrayBucket)); + if (deque) _CFAllocatorDeallocateGC(allocator, deque); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, array, array->_store, newDeque); + return; + } + + if ((numNewElems < 0 && C < A) || (numNewElems <= R && C < A)) { // move C + // deleting: C is smaller + // inserting: C is smaller and R has room + CFIndex oldC0 = L + A + B; + CFIndex newC0 = L + A + newCount; + if (0 < C) CF_WRITE_BARRIER_MEMMOVE(buckets + newC0, buckets + oldC0, C * sizeof(struct __CFArrayBucket)); + // GrP GC: zero-out newly exposed space on the right, if any + if (oldC0 > newC0) memset(buckets + newC0 + C, 0x00, (oldC0 - newC0) * sizeof(struct __CFArrayBucket)); + } else if ((numNewElems < 0) || (numNewElems <= L && A <= C)) { // move A + // deleting: A is smaller or equal (covers remaining delete cases) + // inserting: A is smaller and L has room + CFIndex oldL = L; + CFIndex newL = L - numNewElems; + deque->_leftIdx = newL; + if (0 < A) CF_WRITE_BARRIER_MEMMOVE(buckets + newL, buckets + oldL, A * sizeof(struct __CFArrayBucket)); + // GrP GC: zero-out newly exposed space on the left, if any + if (newL > oldL) memset(buckets + oldL, 0x00, (newL - oldL) * sizeof(struct __CFArrayBucket)); + } else { + // now, must be inserting, and either: + // A<=C, but L doesn't have room (R might have, but don't care) + // C<A, but R doesn't have room (L might have, but don't care) + // re-center everything + CFIndex oldL = L; + CFIndex newL = (L + R - numNewElems) / 2; + CFIndex oldBias = deque->_bias; + deque->_bias = (newL < oldL) ? -1 : 1; + if (oldBias < 0) { + newL = newL - newL / 2; + } else if (0 < oldBias) { + newL = newL + newL / 2; + } + CFIndex oldC0 = oldL + A + B; + CFIndex newC0 = newL + A + newCount; + deque->_leftIdx = newL; + if (newL < oldL) { + if (0 < A) CF_WRITE_BARRIER_MEMMOVE(buckets + newL, buckets + oldL, A * sizeof(struct __CFArrayBucket)); + if (0 < C) CF_WRITE_BARRIER_MEMMOVE(buckets + newC0, buckets + oldC0, C * sizeof(struct __CFArrayBucket)); + // GrP GC: zero-out newly exposed space on the right, if any + if (oldC0 > newC0) memset(buckets + newC0 + C, 0x00, (oldC0 - newC0) * sizeof(struct __CFArrayBucket)); + } else { + if (0 < C) CF_WRITE_BARRIER_MEMMOVE(buckets + newC0, buckets + oldC0, C * sizeof(struct __CFArrayBucket)); + if (0 < A) CF_WRITE_BARRIER_MEMMOVE(buckets + newL, buckets + oldL, A * sizeof(struct __CFArrayBucket)); + // GrP GC: zero-out newly exposed space on the left, if any + if (newL > oldL) memset(buckets + oldL, 0x00, (newL - oldL) * sizeof(struct __CFArrayBucket)); + } + } +} + +static void __CFArrayHandleOutOfMemory(CFTypeRef obj, CFIndex numBytes) { + CFStringRef msg = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("Attempt to allocate %ld bytes for CFArray failed"), numBytes); + CFBadErrorCallBack cb = _CFGetOutOfMemoryErrorCallBack(); + if (NULL == cb || !cb(obj, CFSTR("NS/CFArray"), msg)) { + CFLog(kCFLogLevelCritical, CFSTR("%@"), msg); + HALT; + } + CFRelease(msg); +} + +// This function is for Foundation's benefit; no one else should use it. +void _CFArraySetCapacity(CFMutableArrayRef array, CFIndex cap) { + if (CF_IS_OBJC(__kCFArrayTypeID, array)) return; + __CFGenericValidateType(array, __kCFArrayTypeID); + CFAssert1(__CFArrayGetType(array) != __kCFArrayImmutable, __kCFLogAssertion, "%s(): array is immutable", __PRETTY_FUNCTION__); + CFAssert3(__CFArrayGetCount(array) <= cap, __kCFLogAssertion, "%s(): desired capacity (%d) is less than count (%d)", __PRETTY_FUNCTION__, cap, __CFArrayGetCount(array)); + // Currently, attempting to set the capacity of an array which is the CFStorage + // variant, or set the capacity larger than __CF_MAX_BUCKETS_PER_DEQUE, has no + // effect. The primary purpose of this API is to help avoid a bunch of the + // resizes at the small capacities 4, 8, 16, etc. + if (__CFArrayGetType(array) == __kCFArrayDeque) { + struct __CFArrayDeque *deque = (struct __CFArrayDeque *)array->_store; + CFIndex capacity = __CFArrayDequeRoundUpCapacity(cap); + CFIndex size = sizeof(struct __CFArrayDeque) + capacity * sizeof(struct __CFArrayBucket); + CFAllocatorRef allocator = __CFGetAllocator(array); + if (NULL == deque) { + deque = (struct __CFArrayDeque *)_CFAllocatorAllocateGC(allocator, size, isStrongMemory(array) ? __kCFAllocatorGCScannedMemory : 0); + if (NULL == deque) __CFArrayHandleOutOfMemory(array, size); + if (__CFOASafe) __CFSetLastAllocationEventName(deque, "CFArray (store-deque)"); + deque->_leftIdx = capacity / 2; + } else { + struct __CFArrayDeque *olddeque = deque; + CFIndex oldcap = deque->_capacity; + deque = (struct __CFArrayDeque *)_CFAllocatorAllocateGC(allocator, size, isStrongMemory(array) ? __kCFAllocatorGCScannedMemory : 0); + if (NULL == deque) __CFArrayHandleOutOfMemory(array, size); + CF_WRITE_BARRIER_MEMMOVE(deque, olddeque, sizeof(struct __CFArrayDeque) + oldcap * sizeof(struct __CFArrayBucket)); + _CFAllocatorDeallocateGC(allocator, olddeque); + if (__CFOASafe) __CFSetLastAllocationEventName(deque, "CFArray (store-deque)"); + } + deque->_capacity = capacity; + deque->_bias = 0; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, array, array->_store, deque); + } +} + +// This function is for Foundation's benefit; no one else should use it. +bool _CFArrayIsMutable(CFArrayRef array) { + return (__CFArrayGetType(array) != __kCFArrayImmutable); +} + +void CFArrayReplaceValues(CFMutableArrayRef array, CFRange range, const void **newValues, CFIndex newCount) { + CF_OBJC_FUNCDISPATCH3(__kCFArrayTypeID, void, array, "replaceObjectsInRange:withObjects:count:", range, (void **)newValues, newCount); + __CFGenericValidateType(array, __kCFArrayTypeID); + __CFArrayValidateRange(array, range, __PRETTY_FUNCTION__); + CFAssert1(__CFArrayGetType(array) != __kCFArrayImmutable, __kCFLogAssertion, "%s(): array is immutable", __PRETTY_FUNCTION__); + CFAssert2(0 <= newCount, __kCFLogAssertion, "%s(): newCount (%d) cannot be less than zero", __PRETTY_FUNCTION__, newCount); + return _CFArrayReplaceValues(array, range, newValues, newCount); +} + +// This function does no ObjC dispatch or argument checking; +// It should only be called from places where that dispatch and check has already been done, or NSCFArray +void _CFArrayReplaceValues(CFMutableArrayRef array, CFRange range, const void **newValues, CFIndex newCount) { + const CFArrayCallBacks *cb; + CFAllocatorRef allocator; + CFIndex idx, cnt, futureCnt; + const void **newv, *buffer[256]; + cnt = __CFArrayGetCount(array); + futureCnt = cnt - range.length + newCount; + CFAssert1(newCount <= futureCnt, __kCFLogAssertion, "%s(): internal error 1", __PRETTY_FUNCTION__); + cb = __CFArrayGetCallBacks(array); + allocator = __CFGetAllocator(array); + /* Retain new values if needed, possibly allocating a temporary buffer for them */ + if (NULL != cb->retain && !hasBeenFinalized(array)) { + newv = (newCount <= 256) ? (const void **)buffer : (const void **)CFAllocatorAllocate(allocator, newCount * sizeof(void *), 0); // GC OK + if (newv != buffer && __CFOASafe) __CFSetLastAllocationEventName(newv, "CFArray (temp)"); + for (idx = 0; idx < newCount; idx++) { + newv[idx] = (void *)INVOKE_CALLBACK2(cb->retain, allocator, (void *)newValues[idx]); + } + } else { + newv = newValues; + } + array->_mutations++; + + /* Now, there are three regions of interest, each of which may be empty: + * A: the region from index 0 to one less than the range.location + * B: the region of the range + * C: the region from range.location + range.length to the end + * Note that index 0 is not necessarily at the lowest-address edge + * of the available storage. The values in region B need to get + * released, and the values in regions A and C (depending) need + * to get shifted if the number of new values is different from + * the length of the range being replaced. + */ + if (0 < range.length) { + __CFArrayReleaseValues(array, range, false); + } + // region B elements are now "dead" + if (__kCFArrayStorage == __CFArrayGetType(array)) { + CFStorageRef store = (CFStorageRef)array->_store; + // reposition regions A and C for new region B elements in gap + if (range.length < newCount) { + CFStorageInsertValues(store, CFRangeMake(range.location + range.length, newCount - range.length)); + } else if (newCount < range.length) { + CFStorageDeleteValues(store, CFRangeMake(range.location + newCount, range.length - newCount)); + } + if (futureCnt <= __CF_MAX_BUCKETS_PER_DEQUE / 2) { + __CFArrayConvertStoreToDeque(array); + } + } else if (NULL == array->_store) { + if (__CF_MAX_BUCKETS_PER_DEQUE <= futureCnt) { + CFStorageRef store = (CFStorageRef)CFMakeCollectable(CFStorageCreate(allocator, sizeof(const void *))); + if (! isStrongMemory(array)) _CFStorageSetWeak(store); + if (__CFOASafe) __CFSetLastAllocationEventName(store, "CFArray (store-storage)"); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, array, array->_store, store); + CFStorageInsertValues(store, CFRangeMake(0, newCount)); + __CFBitfieldSetValue(((CFRuntimeBase *)array)->_cfinfo[CF_INFO_BITS], 1, 0, __kCFArrayStorage); + } else if (0 <= futureCnt) { + struct __CFArrayDeque *deque; + CFIndex capacity = __CFArrayDequeRoundUpCapacity(futureCnt); + CFIndex size = sizeof(struct __CFArrayDeque) + capacity * sizeof(struct __CFArrayBucket); + deque = (struct __CFArrayDeque *)_CFAllocatorAllocateGC(allocator, size, isStrongMemory(array) ? __kCFAllocatorGCScannedMemory : 0); + if (__CFOASafe) __CFSetLastAllocationEventName(deque, "CFArray (store-deque)"); + deque->_leftIdx = (capacity - newCount) / 2; + deque->_capacity = capacity; + deque->_bias = 0; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, array, array->_store, deque); + } + } else { // Deque + // reposition regions A and C for new region B elements in gap + if (__CF_MAX_BUCKETS_PER_DEQUE <= futureCnt) { + CFStorageRef store; + __CFArrayConvertDequeToStore(array); + store = (CFStorageRef)array->_store; + if (range.length < newCount) { + CFStorageInsertValues(store, CFRangeMake(range.location + range.length, newCount - range.length)); + } else if (newCount < range.length) { // this won't happen, but is here for completeness + CFStorageDeleteValues(store, CFRangeMake(range.location + newCount, range.length - newCount)); + } + } else if (range.length != newCount) { + __CFArrayRepositionDequeRegions(array, range, newCount); + } + } + // copy in new region B elements + if (0 < newCount) { + if (__kCFArrayStorage == __CFArrayGetType(array)) { + CFStorageRef store = (CFStorageRef)array->_store; + CFStorageReplaceValues(store, CFRangeMake(range.location, newCount), newv); + } else { // Deque + struct __CFArrayDeque *deque = (struct __CFArrayDeque *)array->_store; + struct __CFArrayBucket *raw_buckets = (struct __CFArrayBucket *)((uint8_t *)deque + sizeof(struct __CFArrayDeque)); + CFAllocatorRef bucketsAllocator = isStrongMemory(array) ? allocator : kCFAllocatorNull; + if (newCount == 1) + CF_WRITE_BARRIER_ASSIGN(bucketsAllocator, *((const void **)raw_buckets + deque->_leftIdx + range.location), newv[0]); + else + CF_WRITE_BARRIER_MEMMOVE(raw_buckets + deque->_leftIdx + range.location, newv, newCount * sizeof(struct __CFArrayBucket)); + } + } + __CFArraySetCount(array, futureCnt); + if (newv != buffer && newv != newValues) CFAllocatorDeallocate(allocator, newv); +} + +struct _acompareContext { + CFComparatorFunction func; + void *context; +}; + +static CFComparisonResult __CFArrayCompareValues(const void *v1, const void *v2, struct _acompareContext *context) { + const void **val1 = (const void **)v1; + const void **val2 = (const void **)v2; + return (CFComparisonResult)(INVOKE_CALLBACK3(context->func, *val1, *val2, context->context)); +} + +void CFArraySortValues(CFMutableArrayRef array, CFRange range, CFComparatorFunction comparator, void *context) { + FAULT_CALLBACK((void **)&(comparator)); + CF_OBJC_FUNCDISPATCH3(__kCFArrayTypeID, void, array, "sortUsingFunction:context:range:", comparator, context, range); + __CFGenericValidateType(array, __kCFArrayTypeID); + __CFArrayValidateRange(array, range, __PRETTY_FUNCTION__); + CFAssert1(__CFArrayGetType(array) != __kCFArrayImmutable, __kCFLogAssertion, "%s(): array is immutable", __PRETTY_FUNCTION__); + CFAssert1(NULL != comparator, __kCFLogAssertion, "%s(): pointer to comparator function may not be NULL", __PRETTY_FUNCTION__); + array->_mutations++; + + if (1 < range.length) { + struct _acompareContext ctx; + struct __CFArrayBucket *bucket; + ctx.func = comparator; + ctx.context = context; + switch (__CFArrayGetType(array)) { + case __kCFArrayDeque: + bucket = __CFArrayGetBucketsPtr(array) + range.location; + if (CF_USING_COLLECTABLE_MEMORY && isStrongMemory(array)) { + size_t size = range.length * sizeof(void*); + __CFObjCWriteBarrierRange(bucket, size); + CFQSortArray(bucket, range.length, sizeof(void *), (CFComparatorFunction)__CFArrayCompareValues, &ctx); + } else { + CFQSortArray(bucket, range.length, sizeof(void *), (CFComparatorFunction)__CFArrayCompareValues, &ctx); + } + break; + case __kCFArrayStorage: { + CFStorageRef store = (CFStorageRef)array->_store; + CFAllocatorRef allocator = __CFGetAllocator(array); + const void **values, *buffer[256]; + values = (range.length <= 256) ? (const void **)buffer : (const void **)CFAllocatorAllocate(allocator, range.length * sizeof(void *), 0); // GC OK + if (values != buffer && __CFOASafe) __CFSetLastAllocationEventName(values, "CFArray (temp)"); + CFStorageGetValues(store, range, values); + CFQSortArray(values, range.length, sizeof(void *), (CFComparatorFunction)__CFArrayCompareValues, &ctx); + CFStorageReplaceValues(store, range, values); + if (values != buffer) CFAllocatorDeallocate(allocator, values); // GC OK + break; + } + } + } +} + +CFIndex CFArrayBSearchValues(CFArrayRef array, CFRange range, const void *value, CFComparatorFunction comparator, void *context) { + __CFGenericValidateType(array, __kCFArrayTypeID); + __CFArrayValidateRange(array, range, __PRETTY_FUNCTION__); + CFAssert1(NULL != comparator, __kCFLogAssertion, "%s(): pointer to comparator function may not be NULL", __PRETTY_FUNCTION__); + bool isObjC = CF_IS_OBJC(__kCFArrayTypeID, array); + FAULT_CALLBACK((void **)&(comparator)); + CFIndex idx = 0; + if (range.length <= 0) return range.location; + if (isObjC || __kCFArrayStorage == __CFArrayGetType(array)) { + const void *item; + SInt32 lg; + item = CFArrayGetValueAtIndex(array, range.location + range.length - 1); + if ((CFComparisonResult)(INVOKE_CALLBACK3(comparator, item, value, context)) < 0) { + return range.location + range.length; + } + item = CFArrayGetValueAtIndex(array, range.location); + if ((CFComparisonResult)(INVOKE_CALLBACK3(comparator, value, item, context)) < 0) { + return range.location; + } + lg = flsl(range.length) - 1; // lg2(range.length) + item = CFArrayGetValueAtIndex(array, range.location + -1 + (1 << lg)); + idx = range.location + ((CFComparisonResult)(INVOKE_CALLBACK3(comparator, item, value, context)) < 0) ? range.length - (1 << lg) : -1; + while (lg--) { + item = CFArrayGetValueAtIndex(array, range.location + idx + (1 << lg)); + if ((CFComparisonResult)(INVOKE_CALLBACK3(comparator, item, value, context)) < 0) { + idx += (1 << lg); + } + } + idx++; + } else { + struct _acompareContext ctx; + ctx.func = comparator; + ctx.context = context; + idx = CFBSearch(&value, sizeof(void *), __CFArrayGetBucketsPtr(array) + range.location, range.length, (CFComparatorFunction)__CFArrayCompareValues, &ctx); + } + return idx + range.location; +} + +void CFArrayAppendArray(CFMutableArrayRef array, CFArrayRef otherArray, CFRange otherRange) { + CFIndex idx; + __CFGenericValidateType(array, __kCFArrayTypeID); + __CFGenericValidateType(otherArray, __kCFArrayTypeID); + CFAssert1(__CFArrayGetType(array) != __kCFArrayImmutable, __kCFLogAssertion, "%s(): array is immutable", __PRETTY_FUNCTION__); + __CFArrayValidateRange(otherArray, otherRange, __PRETTY_FUNCTION__); + for (idx = otherRange.location; idx < otherRange.location + otherRange.length; idx++) { + CFArrayAppendValue(array, CFArrayGetValueAtIndex(otherArray, idx)); + } +}
diff --git a/CoreFoundation/CFArray.h b/CoreFoundation/CFArray.h new file mode 100644 index 0000000..5b84595 --- /dev/null +++ b/CoreFoundation/CFArray.h
@@ -0,0 +1,703 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFArray.h + Copyright (c) 1998-2007, Apple Inc. All rights reserved. +*/ + +/*! + @header CFArray + CFArray implements an ordered, compact container of pointer-sized + values. Values are accessed via integer keys (indices), from the + range 0 to N-1, where N is the number of values in the array when + an operation is performed. The array is said to be "compact" because + deleted or inserted values do not leave a gap in the key space -- + the values with higher-numbered indices have their indices + renumbered lower (or higher, in the case of insertion) so that the + set of valid indices is always in the integer range [0, N-1]. Thus, + the index to access a particular value in the array may change over + time as other values are inserted into or deleted from the array. + + Arrays come in two flavors, immutable, which cannot have values + added to them or removed from them after the array is created, and + mutable, to which you can add values or from which remove values. + Mutable arrays can have an unlimited number of values (or rather, + limited only by constraints external to CFArray, like the amount + of available memory). + + As with all CoreFoundation collection types, arrays maintain hard + references on the values you put in them, but the retaining and + releasing functions are user-defined callbacks that can actually do + whatever the user wants (for example, nothing). + + Computational Complexity + The access time for a value in the array is guaranteed to be at + worst O(lg N) for any implementation, current and future, but will + often be O(1) (constant time). Linear search operations similarly + have a worst case complexity of O(N*lg N), though typically the + bounds will be tighter, and so on. Insertion or deletion operations + will typically be linear in the number of values in the array, but + may be O(N*lg N) clearly in the worst case in some implementations. + There are no favored positions within the array for performance; + that is, it is not necessarily faster to access values with low + indices, or to insert or delete values with high indices, or + whatever. +*/ + +#if !defined(__COREFOUNDATION_CFARRAY__) +#define __COREFOUNDATION_CFARRAY__ 1 + +#include <CoreFoundation/CFBase.h> + +CF_EXTERN_C_BEGIN + +/*! + @typedef CFArrayCallBacks + Structure containing the callbacks of a CFArray. + @field version The version number of the structure type being passed + in as a parameter to the CFArray creation functions. This + structure is version 0. + @field retain The callback used to add a retain for the array on + values as they are put into the array. This callback returns + the value to store in the array, which is usually the value + parameter passed to this callback, but may be a different + value if a different value should be stored in the array. + The array's allocator is passed as the first argument. + @field release The callback used to remove a retain previously added + for the array from values as they are removed from the + array. The array's allocator is passed as the first + argument. + @field copyDescription The callback used to create a descriptive + string representation of each value in the array. This is + used by the CFCopyDescription() function. + @field equal The callback used to compare values in the array for + equality for some operations. +*/ +typedef const void * (*CFArrayRetainCallBack)(CFAllocatorRef allocator, const void *value); +typedef void (*CFArrayReleaseCallBack)(CFAllocatorRef allocator, const void *value); +typedef CFStringRef (*CFArrayCopyDescriptionCallBack)(const void *value); +typedef Boolean (*CFArrayEqualCallBack)(const void *value1, const void *value2); +typedef struct { + CFIndex version; + CFArrayRetainCallBack retain; + CFArrayReleaseCallBack release; + CFArrayCopyDescriptionCallBack copyDescription; + CFArrayEqualCallBack equal; +} CFArrayCallBacks; + +/*! + @constant kCFTypeArrayCallBacks + Predefined CFArrayCallBacks structure containing a set of callbacks + appropriate for use when the values in a CFArray are all CFTypes. +*/ +CF_EXPORT +const CFArrayCallBacks kCFTypeArrayCallBacks; + +/*! + @typedef CFArrayApplierFunction + Type of the callback function used by the apply functions of + CFArrays. + @param value The current value from the array. + @param context The user-defined context parameter given to the apply + function. +*/ +typedef void (*CFArrayApplierFunction)(const void *value, void *context); + +/*! + @typedef CFArrayRef + This is the type of a reference to immutable CFArrays. +*/ +typedef const struct __CFArray * CFArrayRef; + +/*! + @typedef CFMutableArrayRef + This is the type of a reference to mutable CFArrays. +*/ +typedef struct __CFArray * CFMutableArrayRef; + +/*! + @function CFArrayGetTypeID + Returns the type identifier of all CFArray instances. +*/ +CF_EXPORT +CFTypeID CFArrayGetTypeID(void); + +/*! + @function CFArrayCreate + Creates a new immutable array with the given values. + @param allocator The CFAllocator which should be used to allocate + memory for the array and its storage for values. This + parameter may be NULL in which case the current default + CFAllocator is used. If this reference is not a valid + CFAllocator, the behavior is undefined. + @param values A C array of the pointer-sized values to be in the + array. The values in the array are ordered in the same order + in which they appear in this C array. This parameter may be + NULL if the numValues parameter is 0. This C array is not + changed or freed by this function. If this parameter is not + a valid pointer to a C array of at least numValues pointers, + the behavior is undefined. + @param numValues The number of values to copy from the values C + array into the CFArray. This number will be the count of the + array. + If this parameter is negative, or greater than the number of + values actually in the value's C array, the behavior is + undefined. + @param callBacks A pointer to a CFArrayCallBacks structure + initialized with the callbacks for the array to use on each + value in the array. The retain callback will be used within + this function, for example, to retain all of the new values + from the values C array. A copy of the contents of the + callbacks structure is made, so that a pointer to a + structure on the stack can be passed in, or can be reused + for multiple array creations. If the version field of this + callbacks structure is not one of the defined ones for + CFArray, the behavior is undefined. The retain field may be + NULL, in which case the CFArray will do nothing to add a + retain to the contained values for the array. The release + field may be NULL, in which case the CFArray will do nothing + to remove the array's retain (if any) on the values when the + array is destroyed. If the copyDescription field is NULL, + the array will create a simple description for the value. If + the equal field is NULL, the array will use pointer equality + to test for equality of values. This callbacks parameter + itself may be NULL, which is treated as if a valid structure + of version 0 with all fields NULL had been passed in. + Otherwise, if any of the fields are not valid pointers to + functions of the correct type, or this parameter is not a + valid pointer to a CFArrayCallBacks callbacks structure, + the behavior is undefined. If any of the values put into the + array is not one understood by one of the callback functions + the behavior when that callback function is used is + undefined. + @result A reference to the new immutable CFArray. +*/ +CF_EXPORT +CFArrayRef CFArrayCreate(CFAllocatorRef allocator, const void **values, CFIndex numValues, const CFArrayCallBacks *callBacks); + +/*! + @function CFArrayCreateCopy + Creates a new immutable array with the values from the given array. + @param allocator The CFAllocator which should be used to allocate + memory for the array and its storage for values. This + parameter may be NULL in which case the current default + CFAllocator is used. If this reference is not a valid + CFAllocator, the behavior is undefined. + @param theArray The array which is to be copied. The values from the + array are copied as pointers into the new array (that is, + the values themselves are copied, not that which the values + point to, if anything). However, the values are also + retained by the new array. The count of the new array will + be the same as the given array. The new array uses the same + callbacks as the array to be copied. If this parameter is + not a valid CFArray, the behavior is undefined. + @result A reference to the new immutable CFArray. +*/ +CF_EXPORT +CFArrayRef CFArrayCreateCopy(CFAllocatorRef allocator, CFArrayRef theArray); + +/*! + @function CFArrayCreateMutable + Creates a new empty mutable array. + @param allocator The CFAllocator which should be used to allocate + memory for the array and its storage for values. This + parameter may be NULL in which case the current default + CFAllocator is used. If this reference is not a valid + CFAllocator, the behavior is undefined. + @param capacity A hint about the number of values that will be held + by the CFArray. Pass 0 for no hint. The implementation may + ignore this hint, or may use it to optimize various + operations. An array's actual capacity is only limited by + address space and available memory constraints). If this + parameter is negative, the behavior is undefined. + @param callBacks A pointer to a CFArrayCallBacks structure + initialized with the callbacks for the array to use on each + value in the array. A copy of the contents of the + callbacks structure is made, so that a pointer to a + structure on the stack can be passed in, or can be reused + for multiple array creations. If the version field of this + callbacks structure is not one of the defined ones for + CFArray, the behavior is undefined. The retain field may be + NULL, in which case the CFArray will do nothing to add a + retain to the contained values for the array. The release + field may be NULL, in which case the CFArray will do nothing + to remove the array's retain (if any) on the values when the + array is destroyed. If the copyDescription field is NULL, + the array will create a simple description for the value. If + the equal field is NULL, the array will use pointer equality + to test for equality of values. This callbacks parameter + itself may be NULL, which is treated as if a valid structure + of version 0 with all fields NULL had been passed in. + Otherwise, if any of the fields are not valid pointers to + functions of the correct type, or this parameter is not a + valid pointer to a CFArrayCallBacks callbacks structure, + the behavior is undefined. If any of the values put into the + array is not one understood by one of the callback functions + the behavior when that callback function is used is + undefined. + @result A reference to the new mutable CFArray. +*/ +CF_EXPORT +CFMutableArrayRef CFArrayCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFArrayCallBacks *callBacks); + +/*! + @function CFArrayCreateMutableCopy + Creates a new mutable array with the values from the given array. + @param allocator The CFAllocator which should be used to allocate + memory for the array and its storage for values. This + parameter may be NULL in which case the current default + CFAllocator is used. If this reference is not a valid + CFAllocator, the behavior is undefined. + @param capacity A hint about the number of values that will be held + by the CFArray. Pass 0 for no hint. The implementation may + ignore this hint, or may use it to optimize various + operations. An array's actual capacity is only limited by + address space and available memory constraints). + This parameter must be greater than or equal + to the count of the array which is to be copied, or the + behavior is undefined. If this parameter is negative, the + behavior is undefined. + @param theArray The array which is to be copied. The values from the + array are copied as pointers into the new array (that is, + the values themselves are copied, not that which the values + point to, if anything). However, the values are also + retained by the new array. The count of the new array will + be the same as the given array. The new array uses the same + callbacks as the array to be copied. If this parameter is + not a valid CFArray, the behavior is undefined. + @result A reference to the new mutable CFArray. +*/ +CF_EXPORT +CFMutableArrayRef CFArrayCreateMutableCopy(CFAllocatorRef allocator, CFIndex capacity, CFArrayRef theArray); + +/*! + @function CFArrayGetCount + Returns the number of values currently in the array. + @param theArray The array to be queried. If this parameter is not a valid + CFArray, the behavior is undefined. + @result The number of values in the array. +*/ +CF_EXPORT +CFIndex CFArrayGetCount(CFArrayRef theArray); + +/*! + @function CFArrayGetCountOfValue + Counts the number of times the given value occurs in the array. + @param theArray The array to be searched. If this parameter is not a + valid CFArray, the behavior is undefined. + @param range The range within the array to search. If the range + location or end point (defined by the location plus length + minus 1) is outside the index space of the array (0 to + N-1 inclusive, where N is the count of the array), the + behavior is undefined. If the range length is negative, the + behavior is undefined. The range may be empty (length 0). + @param value The value for which to find matches in the array. The + equal() callback provided when the array was created is + used to compare. If the equal() callback was NULL, pointer + equality (in C, ==) is used. If value, or any of the values + in the array, are not understood by the equal() callback, + the behavior is undefined. + @result The number of times the given value occurs in the array, + within the specified range. +*/ +CF_EXPORT +CFIndex CFArrayGetCountOfValue(CFArrayRef theArray, CFRange range, const void *value); + +/*! + @function CFArrayContainsValue + Reports whether or not the value is in the array. + @param theArray The array to be searched. If this parameter is not a + valid CFArray, the behavior is undefined. + @param range The range within the array to search. If the range + location or end point (defined by the location plus length + minus 1) is outside the index space of the array (0 to + N-1 inclusive, where N is the count of the array), the + behavior is undefined. If the range length is negative, the + behavior is undefined. The range may be empty (length 0). + @param value The value for which to find matches in the array. The + equal() callback provided when the array was created is + used to compare. If the equal() callback was NULL, pointer + equality (in C, ==) is used. If value, or any of the values + in the array, are not understood by the equal() callback, + the behavior is undefined. + @result true, if the value is in the specified range of the array, + otherwise false. +*/ +CF_EXPORT +Boolean CFArrayContainsValue(CFArrayRef theArray, CFRange range, const void *value); + +/*! + @function CFArrayGetValueAtIndex + Retrieves the value at the given index. + @param theArray The array to be queried. If this parameter is not a + valid CFArray, the behavior is undefined. + @param idx The index of the value to retrieve. If the index is + outside the index space of the array (0 to N-1 inclusive, + where N is the count of the array), the behavior is + undefined. + @result The value with the given index in the array. +*/ +CF_EXPORT +const void *CFArrayGetValueAtIndex(CFArrayRef theArray, CFIndex idx); + +/*! + @function CFArrayGetValues + Fills the buffer with values from the array. + @param theArray The array to be queried. If this parameter is not a + valid CFArray, the behavior is undefined. + @param range The range of values within the array to retrieve. If + the range location or end point (defined by the location + plus length minus 1) is outside the index space of the + array (0 to N-1 inclusive, where N is the count of the + array), the behavior is undefined. If the range length is + negative, the behavior is undefined. The range may be empty + (length 0), in which case no values are put into the buffer. + @param values A C array of pointer-sized values to be filled with + values from the array. The values in the C array are ordered + in the same order in which they appear in the array. If this + parameter is not a valid pointer to a C array of at least + range.length pointers, the behavior is undefined. +*/ +CF_EXPORT +void CFArrayGetValues(CFArrayRef theArray, CFRange range, const void **values); + +/*! + @function CFArrayApplyFunction + Calls a function once for each value in the array. + @param theArray The array to be operated upon. If this parameter is not + a valid CFArray, the behavior is undefined. + @param range The range of values within the array to which to apply + the function. If the range location or end point (defined by + the location plus length minus 1) is outside the index + space of the array (0 to N-1 inclusive, where N is the count + of the array), the behavior is undefined. If the range + length is negative, the behavior is undefined. The range may + be empty (length 0). + @param applier The callback function to call once for each value in + the given range in the array. If this parameter is not a + pointer to a function of the correct prototype, the behavior + is undefined. If there are values in the range which the + applier function does not expect or cannot properly apply + to, the behavior is undefined. + @param context A pointer-sized user-defined value, which is passed + as the second parameter to the applier function, but is + otherwise unused by this function. If the context is not + what is expected by the applier function, the behavior is + undefined. +*/ +CF_EXPORT +void CFArrayApplyFunction(CFArrayRef theArray, CFRange range, CFArrayApplierFunction applier, void *context); + +/*! + @function CFArrayGetFirstIndexOfValue + Searches the array for the value. + @param theArray The array to be searched. If this parameter is not a + valid CFArray, the behavior is undefined. + @param range The range within the array to search. If the range + location or end point (defined by the location plus length + minus 1) is outside the index space of the array (0 to + N-1 inclusive, where N is the count of the array), the + behavior is undefined. If the range length is negative, the + behavior is undefined. The range may be empty (length 0). + The search progresses from the smallest index defined by + the range to the largest. + @param value The value for which to find a match in the array. The + equal() callback provided when the array was created is + used to compare. If the equal() callback was NULL, pointer + equality (in C, ==) is used. If value, or any of the values + in the array, are not understood by the equal() callback, + the behavior is undefined. + @result The lowest index of the matching values in the range, or + kCFNotFound if no value in the range matched. +*/ +CF_EXPORT +CFIndex CFArrayGetFirstIndexOfValue(CFArrayRef theArray, CFRange range, const void *value); + +/*! + @function CFArrayGetLastIndexOfValue + Searches the array for the value. + @param theArray The array to be searched. If this parameter is not a + valid CFArray, the behavior is undefined. + @param range The range within the array to search. If the range + location or end point (defined by the location plus length + minus 1) is outside the index space of the array (0 to + N-1 inclusive, where N is the count of the array), the + behavior is undefined. If the range length is negative, the + behavior is undefined. The range may be empty (length 0). + The search progresses from the largest index defined by the + range to the smallest. + @param value The value for which to find a match in the array. The + equal() callback provided when the array was created is + used to compare. If the equal() callback was NULL, pointer + equality (in C, ==) is used. If value, or any of the values + in the array, are not understood by the equal() callback, + the behavior is undefined. + @result The highest index of the matching values in the range, or + kCFNotFound if no value in the range matched. +*/ +CF_EXPORT +CFIndex CFArrayGetLastIndexOfValue(CFArrayRef theArray, CFRange range, const void *value); + +/*! + @function CFArrayBSearchValues + Searches the array for the value using a binary search algorithm. + @param theArray The array to be searched. If this parameter is not a + valid CFArray, the behavior is undefined. If the array is + not sorted from least to greatest according to the + comparator function, the behavior is undefined. + @param range The range within the array to search. If the range + location or end point (defined by the location plus length + minus 1) is outside the index space of the array (0 to + N-1 inclusive, where N is the count of the array), the + behavior is undefined. If the range length is negative, the + behavior is undefined. The range may be empty (length 0). + @param value The value for which to find a match in the array. If + value, or any of the values in the array, are not understood + by the comparator callback, the behavior is undefined. + @param comparator The function with the comparator function type + signature which is used in the binary search operation to + compare values in the array with the given value. If this + parameter is not a pointer to a function of the correct + prototype, the behavior is undefined. If there are values + in the range which the comparator function does not expect + or cannot properly compare, the behavior is undefined. + @param context A pointer-sized user-defined value, which is passed + as the third parameter to the comparator function, but is + otherwise unused by this function. If the context is not + what is expected by the comparator function, the behavior is + undefined. + @result The return value is either 1) the index of a value that + matched, if the target value matches one or more in the + range, 2) greater than or equal to the end point of the + range, if the value is greater than all the values in the + range, or 3) the index of the value greater than the target + value, if the value lies between two of (or less than all + of) the values in the range. +*/ +CF_EXPORT +CFIndex CFArrayBSearchValues(CFArrayRef theArray, CFRange range, const void *value, CFComparatorFunction comparator, void *context); + +/*! + @function CFArrayAppendValue + Adds the value to the array giving it a new largest index. + @param theArray The array to which the value is to be added. If this + parameter is not a valid mutable CFArray, the behavior is + undefined. + @param value The value to add to the array. The value is retained by + the array using the retain callback provided when the array + was created. If the value is not of the sort expected by the + retain callback, the behavior is undefined. The value is + assigned to the index one larger than the previous largest + index, and the count of the array is increased by one. +*/ +CF_EXPORT +void CFArrayAppendValue(CFMutableArrayRef theArray, const void *value); + +/*! + @function CFArrayInsertValueAtIndex + Adds the value to the array, giving it the given index. + @param theArray The array to which the value is to be added. If this + parameter is not a valid mutable CFArray, the behavior is + undefined. + @param idx The index to which to add the new value. If the index is + outside the index space of the array (0 to N inclusive, + where N is the count of the array before the operation), the + behavior is undefined. If the index is the same as N, this + function has the same effect as CFArrayAppendValue(). + @param value The value to add to the array. The value is retained by + the array using the retain callback provided when the array + was created. If the value is not of the sort expected by the + retain callback, the behavior is undefined. The value is + assigned to the given index, and all values with equal and + larger indices have their indexes increased by one. +*/ +CF_EXPORT +void CFArrayInsertValueAtIndex(CFMutableArrayRef theArray, CFIndex idx, const void *value); + +/*! + @function CFArraySetValueAtIndex + Changes the value with the given index in the array. + @param theArray The array in which the value is to be changed. If this + parameter is not a valid mutable CFArray, the behavior is + undefined. + @param idx The index to which to set the new value. If the index is + outside the index space of the array (0 to N inclusive, + where N is the count of the array before the operation), the + behavior is undefined. If the index is the same as N, this + function has the same effect as CFArrayAppendValue(). + @param value The value to set in the array. The value is retained by + the array using the retain callback provided when the array + was created, and the previous value with that index is + released. If the value is not of the sort expected by the + retain callback, the behavior is undefined. The indices of + other values is not affected. +*/ +CF_EXPORT +void CFArraySetValueAtIndex(CFMutableArrayRef theArray, CFIndex idx, const void *value); + +/*! + @function CFArrayRemoveValueAtIndex + Removes the value with the given index from the array. + @param theArray The array from which the value is to be removed. If + this parameter is not a valid mutable CFArray, the behavior + is undefined. + @param idx The index from which to remove the value. If the index is + outside the index space of the array (0 to N-1 inclusive, + where N is the count of the array before the operation), the + behavior is undefined. +*/ +CF_EXPORT +void CFArrayRemoveValueAtIndex(CFMutableArrayRef theArray, CFIndex idx); + +/*! + @function CFArrayRemoveAllValues + Removes all the values from the array, making it empty. + @param theArray The array from which all of the values are to be + removed. If this parameter is not a valid mutable CFArray, + the behavior is undefined. +*/ +CF_EXPORT +void CFArrayRemoveAllValues(CFMutableArrayRef theArray); + +/*! + @function CFArrayReplaceValues + Replaces a range of values in the array. + @param theArray The array from which all of the values are to be + removed. If this parameter is not a valid mutable CFArray, + the behavior is undefined. + @param range The range of values within the array to replace. If the + range location or end point (defined by the location plus + length minus 1) is outside the index space of the array (0 + to N inclusive, where N is the count of the array), the + behavior is undefined. If the range length is negative, the + behavior is undefined. The range may be empty (length 0), + in which case the new values are merely inserted at the + range location. + @param newValues A C array of the pointer-sized values to be placed + into the array. The new values in the array are ordered in + the same order in which they appear in this C array. This + parameter may be NULL if the newCount parameter is 0. This + C array is not changed or freed by this function. If this + parameter is not a valid pointer to a C array of at least + newCount pointers, the behavior is undefined. + @param newCount The number of values to copy from the values C + array into the CFArray. If this parameter is different than + the range length, the excess newCount values will be + inserted after the range, or the excess range values will be + deleted. This parameter may be 0, in which case no new + values are replaced into the array and the values in the + range are simply removed. If this parameter is negative, or + greater than the number of values actually in the newValues + C array, the behavior is undefined. +*/ +CF_EXPORT +void CFArrayReplaceValues(CFMutableArrayRef theArray, CFRange range, const void **newValues, CFIndex newCount); + +/*! + @function CFArrayExchangeValuesAtIndices + Exchanges the values at two indices of the array. + @param theArray The array of which the values are to be swapped. If + this parameter is not a valid mutable CFArray, the behavior + is undefined. + @param idx1 The first index whose values should be swapped. If the + index is outside the index space of the array (0 to N-1 + inclusive, where N is the count of the array before the + operation), the behavior is undefined. + @param idx2 The second index whose values should be swapped. If the + index is outside the index space of the array (0 to N-1 + inclusive, where N is the count of the array before the + operation), the behavior is undefined. +*/ +CF_EXPORT +void CFArrayExchangeValuesAtIndices(CFMutableArrayRef theArray, CFIndex idx1, CFIndex idx2); + +/*! + @function CFArraySortValues + Sorts the values in the array using the given comparison function. + @param theArray The array whose values are to be sorted. If this + parameter is not a valid mutable CFArray, the behavior is + undefined. + @param range The range of values within the array to sort. If the + range location or end point (defined by the location plus + length minus 1) is outside the index space of the array (0 + to N-1 inclusive, where N is the count of the array), the + behavior is undefined. If the range length is negative, the + behavior is undefined. The range may be empty (length 0). + @param comparator The function with the comparator function type + signature which is used in the sort operation to compare + values in the array with the given value. If this parameter + is not a pointer to a function of the correct prototype, the + the behavior is undefined. If there are values in the array + which the comparator function does not expect or cannot + properly compare, the behavior is undefined. The values in + the range are sorted from least to greatest according to + this function. + @param context A pointer-sized user-defined value, which is passed + as the third parameter to the comparator function, but is + otherwise unused by this function. If the context is not + what is expected by the comparator function, the behavior is + undefined. +*/ +CF_EXPORT +void CFArraySortValues(CFMutableArrayRef theArray, CFRange range, CFComparatorFunction comparator, void *context); + +/*! + @function CFArrayAppendArray + Adds the values from an array to another array. + @param theArray The array to which values from the otherArray are to + be added. If this parameter is not a valid mutable CFArray, + the behavior is undefined. + @param otherArray The array providing the values to be added to the + array. If this parameter is not a valid CFArray, the + behavior is undefined. + @param otherRange The range within the otherArray from which to add + the values to the array. If the range location or end point + (defined by the location plus length minus 1) is outside + the index space of the otherArray (0 to N-1 inclusive, where + N is the count of the otherArray), the behavior is + undefined. The new values are retained by the array using + the retain callback provided when the array was created. If + the values are not of the sort expected by the retain + callback, the behavior is undefined. The values are assigned + to the indices one larger than the previous largest index + in the array, and beyond, and the count of the array is + increased by range.length. The values are assigned new + indices in the array from smallest to largest index in the + order in which they appear in the otherArray. +*/ +CF_EXPORT +void CFArrayAppendArray(CFMutableArrayRef theArray, CFArrayRef otherArray, CFRange otherRange); + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFARRAY__ */ +
diff --git a/CoreFoundation/CFBag.c b/CoreFoundation/CFBag.c new file mode 100644 index 0000000..ee56261 --- /dev/null +++ b/CoreFoundation/CFBag.c
@@ -0,0 +1,1479 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFBag.c + Copyright 1998-2006, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane + Machine generated from Notes/HashingCode.template +*/ + + + + +#include "CFInternal.h" +#include <CoreFoundation/CFBag.h> +#if DEPLOYMENT_TARGET_MACOSX +#include <mach-o/dyld.h> +#endif + +#define CFDictionary 0 +#define CFSet 0 +#define CFBag 0 +#undef CFBag +#define CFBag 1 + +#if CFDictionary +const CFBagKeyCallBacks kCFTypeBagKeyCallBacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; +const CFBagKeyCallBacks kCFCopyStringBagKeyCallBacks = {0, __CFStringCollectionCopy, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; +const CFBagValueCallBacks kCFTypeBagValueCallBacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, CFCopyDescription, CFEqual}; +static const CFBagKeyCallBacks __kCFNullBagKeyCallBacks = {0, NULL, NULL, NULL, NULL, NULL}; +static const CFBagValueCallBacks __kCFNullBagValueCallBacks = {0, NULL, NULL, NULL, NULL}; + +#define CFHashRef CFDictionaryRef +#define CFMutableHashRef CFMutableDictionaryRef +#define __kCFHashTypeID __kCFDictionaryTypeID +#endif + +#if CFSet +const CFBagCallBacks kCFTypeBagCallBacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; +const CFBagCallBacks kCFCopyStringBagCallBacks = {0, __CFStringCollectionCopy, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; +static const CFBagCallBacks __kCFNullBagCallBacks = {0, NULL, NULL, NULL, NULL, NULL}; + +#define CFBagKeyCallBacks CFBagCallBacks +#define CFBagValueCallBacks CFBagCallBacks +#define kCFTypeBagKeyCallBacks kCFTypeBagCallBacks +#define kCFTypeBagValueCallBacks kCFTypeBagCallBacks +#define __kCFNullBagKeyCallBacks __kCFNullBagCallBacks +#define __kCFNullBagValueCallBacks __kCFNullBagCallBacks + +#define CFHashRef CFSetRef +#define CFMutableHashRef CFMutableSetRef +#define __kCFHashTypeID __kCFSetTypeID +#endif + +#if CFBag +const CFBagCallBacks kCFTypeBagCallBacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; +const CFBagCallBacks kCFCopyStringBagCallBacks = {0, __CFStringCollectionCopy, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; +static const CFBagCallBacks __kCFNullBagCallBacks = {0, NULL, NULL, NULL, NULL, NULL}; + +#define CFBagKeyCallBacks CFBagCallBacks +#define CFBagValueCallBacks CFBagCallBacks +#define kCFTypeBagKeyCallBacks kCFTypeBagCallBacks +#define kCFTypeBagValueCallBacks kCFTypeBagCallBacks +#define __kCFNullBagKeyCallBacks __kCFNullBagCallBacks +#define __kCFNullBagValueCallBacks __kCFNullBagCallBacks + +#define CFHashRef CFBagRef +#define CFMutableHashRef CFMutableBagRef +#define __kCFHashTypeID __kCFBagTypeID +#endif + +#define GETNEWKEY(newKey, oldKey) \ + any_t (*kretain)(CFAllocatorRef, any_t, any_pointer_t) = \ + !hasBeenFinalized(hc) \ + ? (any_t (*)(CFAllocatorRef,any_t,any_pointer_t))__CFBagGetKeyCallBacks(hc)->retain \ + : (any_t (*)(CFAllocatorRef,any_t,any_pointer_t))0; \ + any_t newKey = kretain ? (any_t)INVOKE_CALLBACK3(kretain, allocator, (any_t)key, hc->_context) : (any_t)oldKey + +#define RELEASEKEY(oldKey) \ + void (*krelease)(CFAllocatorRef, any_t, any_pointer_t) = \ + !hasBeenFinalized(hc) \ + ? (void (*)(CFAllocatorRef,any_t,any_pointer_t))__CFBagGetKeyCallBacks(hc)->release \ + : (void (*)(CFAllocatorRef,any_t,any_pointer_t))0; \ + if (krelease) INVOKE_CALLBACK3(krelease, allocator, oldKey, hc->_context) + +#if CFDictionary +#define GETNEWVALUE(newValue) \ + any_t (*vretain)(CFAllocatorRef, any_t, any_pointer_t) = \ + !hasBeenFinalized(hc) \ + ? (any_t (*)(CFAllocatorRef,any_t,any_pointer_t))__CFBagGetValueCallBacks(hc)->retain \ + : (any_t (*)(CFAllocatorRef,any_t,any_pointer_t))0; \ + any_t newValue = vretain ? (any_t)INVOKE_CALLBACK3(vretain, allocator, (any_t)value, hc->_context) : (any_t)value + +#define RELEASEVALUE(oldValue) \ + void (*vrelease)(CFAllocatorRef, any_t, any_pointer_t) = \ + !hasBeenFinalized(hc) \ + ? (void (*)(CFAllocatorRef,any_t,any_pointer_t))__CFBagGetValueCallBacks(hc)->release \ + : (void (*)(CFAllocatorRef,any_t,any_pointer_t))0; \ + if (vrelease) INVOKE_CALLBACK3(vrelease, allocator, oldValue, hc->_context) + +#endif + +static void __CFBagHandleOutOfMemory(CFTypeRef obj, CFIndex numBytes) { + CFStringRef msg = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("Attempt to allocate %ld bytes for NS/CFBag failed"), numBytes); + CFBadErrorCallBack cb = _CFGetOutOfMemoryErrorCallBack(); + if (NULL == cb || !cb(obj, CFSTR("NS/CFBag"), msg)) { + CFLog(kCFLogLevelCritical, CFSTR("%@"), msg); + HALT; + } + CFRelease(msg); +} + + +// Max load is 3/4 number of buckets +CF_INLINE CFIndex __CFHashRoundUpCapacity(CFIndex capacity) { + return 3 * ((CFIndex)1 << (flsl((capacity - 1) / 3))); +} + +// Returns next power of two higher than the capacity +// threshold for the given input capacity. +CF_INLINE CFIndex __CFHashNumBucketsForCapacity(CFIndex capacity) { + return 4 * ((CFIndex)1 << (flsl((capacity - 1) / 3))); +} + +enum { /* Bits 1-0 */ + __kCFHashImmutable = 0, /* unchangable and fixed capacity */ + __kCFHashMutable = 1, /* changeable and variable capacity */ +}; + +enum { /* Bits 5-4 (value), 3-2 (key) */ + __kCFHashHasNullCallBacks = 0, + __kCFHashHasCFTypeCallBacks = 1, + __kCFHashHasCustomCallBacks = 3 /* callbacks are at end of header */ +}; + +// Under GC, we fudge the key/value memory in two ways +// First, if we had null callbacks or null for both retain/release, we use unscanned memory and get +// standard 'dangling' references. +// This means that if people were doing addValue:[xxx new] and never removing, well, that doesn't work +// +// Second, if we notice standard retain/release implementations we use scanned memory, and fudge the +// standard callbacks to generally do nothing if the collection was allocated in GC memory. On special +// CF objects, however, like those used for precious resources like video-card buffers, we do indeed +// do CFRetain on input and CFRelease on output. The tricky case is GC finalization; we need to remember +// that we did the CFReleases so that subsequent collection operations, like removal, don't double CFRelease. +// (In fact we don't really use CFRetain/CFRelease but go directly to the collector) +// + +enum { + __kCFHashFinalized = (1 << 7), + __kCFHashWeakKeys = (1 << 8), + __kCFHashWeakValues = (1 << 9) +}; + +typedef uintptr_t any_t; +typedef const void * const_any_pointer_t; +typedef void * any_pointer_t; + +struct __CFBag { + CFRuntimeBase _base; + CFIndex _count; /* number of values */ + CFIndex _bucketsNum; /* number of buckets */ + CFIndex _bucketsUsed; /* number of used buckets */ + CFIndex _bucketsCap; /* maximum number of used buckets */ + CFIndex _mutations; + CFIndex _deletes; + any_pointer_t _context; /* private */ + CFOptionFlags _xflags; + any_t _marker; + any_t *_keys; /* can be NULL if not allocated yet */ + any_t *_values; /* can be NULL if not allocated yet */ +}; + +/* Bits 1-0 of the _xflags are used for mutability variety */ +/* Bits 3-2 of the _xflags are used for key callback indicator bits */ +/* Bits 5-4 of the _xflags are used for value callback indicator bits */ +/* Bit 6 of the _xflags is special KVO actions bit */ +/* Bits 7,8,9 are GC use */ + +CF_INLINE bool hasBeenFinalized(CFTypeRef collection) { + return __CFBitfieldGetValue(((const struct __CFBag *)collection)->_xflags, 7, 7) != 0; +} + +CF_INLINE void markFinalized(CFTypeRef collection) { + __CFBitfieldSetValue(((struct __CFBag *)collection)->_xflags, 7, 7, 1); +} + + +CF_INLINE CFIndex __CFHashGetType(CFHashRef hc) { + return __CFBitfieldGetValue(hc->_xflags, 1, 0); +} + +CF_INLINE CFIndex __CFBagGetSizeOfType(CFIndex t) { + CFIndex size = sizeof(struct __CFBag); + if (__CFBitfieldGetValue(t, 3, 2) == __kCFHashHasCustomCallBacks) { + size += sizeof(CFBagKeyCallBacks); + } + if (__CFBitfieldGetValue(t, 5, 4) == __kCFHashHasCustomCallBacks) { + size += sizeof(CFBagValueCallBacks); + } + return size; +} + +CF_INLINE const CFBagKeyCallBacks *__CFBagGetKeyCallBacks(CFHashRef hc) { + CFBagKeyCallBacks *result = NULL; + switch (__CFBitfieldGetValue(hc->_xflags, 3, 2)) { + case __kCFHashHasNullCallBacks: + return &__kCFNullBagKeyCallBacks; + case __kCFHashHasCFTypeCallBacks: + return &kCFTypeBagKeyCallBacks; + case __kCFHashHasCustomCallBacks: + break; + } + result = (CFBagKeyCallBacks *)((uint8_t *)hc + sizeof(struct __CFBag)); + return result; +} + +CF_INLINE Boolean __CFBagKeyCallBacksMatchNull(const CFBagKeyCallBacks *c) { + return (NULL == c || + (c->retain == __kCFNullBagKeyCallBacks.retain && + c->release == __kCFNullBagKeyCallBacks.release && + c->copyDescription == __kCFNullBagKeyCallBacks.copyDescription && + c->equal == __kCFNullBagKeyCallBacks.equal && + c->hash == __kCFNullBagKeyCallBacks.hash)); +} + +CF_INLINE Boolean __CFBagKeyCallBacksMatchCFType(const CFBagKeyCallBacks *c) { + return (&kCFTypeBagKeyCallBacks == c || + (c->retain == kCFTypeBagKeyCallBacks.retain && + c->release == kCFTypeBagKeyCallBacks.release && + c->copyDescription == kCFTypeBagKeyCallBacks.copyDescription && + c->equal == kCFTypeBagKeyCallBacks.equal && + c->hash == kCFTypeBagKeyCallBacks.hash)); +} + +CF_INLINE const CFBagValueCallBacks *__CFBagGetValueCallBacks(CFHashRef hc) { + CFBagValueCallBacks *result = NULL; + switch (__CFBitfieldGetValue(hc->_xflags, 5, 4)) { + case __kCFHashHasNullCallBacks: + return &__kCFNullBagValueCallBacks; + case __kCFHashHasCFTypeCallBacks: + return &kCFTypeBagValueCallBacks; + case __kCFHashHasCustomCallBacks: + break; + } + if (__CFBitfieldGetValue(hc->_xflags, 3, 2) == __kCFHashHasCustomCallBacks) { + result = (CFBagValueCallBacks *)((uint8_t *)hc + sizeof(struct __CFBag) + sizeof(CFBagKeyCallBacks)); + } else { + result = (CFBagValueCallBacks *)((uint8_t *)hc + sizeof(struct __CFBag)); + } + return result; +} + +CF_INLINE Boolean __CFBagValueCallBacksMatchNull(const CFBagValueCallBacks *c) { + return (NULL == c || + (c->retain == __kCFNullBagValueCallBacks.retain && + c->release == __kCFNullBagValueCallBacks.release && + c->copyDescription == __kCFNullBagValueCallBacks.copyDescription && + c->equal == __kCFNullBagValueCallBacks.equal)); +} + +CF_INLINE Boolean __CFBagValueCallBacksMatchCFType(const CFBagValueCallBacks *c) { + return (&kCFTypeBagValueCallBacks == c || + (c->retain == kCFTypeBagValueCallBacks.retain && + c->release == kCFTypeBagValueCallBacks.release && + c->copyDescription == kCFTypeBagValueCallBacks.copyDescription && + c->equal == kCFTypeBagValueCallBacks.equal)); +} + +CFIndex _CFBagGetKVOBit(CFHashRef hc) { + return __CFBitfieldGetValue(hc->_xflags, 6, 6); +} + +void _CFBagSetKVOBit(CFHashRef hc, CFIndex bit) { + __CFBitfieldSetValue(((CFMutableHashRef)hc)->_xflags, 6, 6, ((uintptr_t)bit & 0x1)); +} + +CF_INLINE Boolean __CFBagShouldShrink(CFHashRef hc) { + return (__kCFHashMutable == __CFHashGetType(hc)) && + !(CF_USING_COLLECTABLE_MEMORY && auto_zone_is_finalized(__CFCollectableZone, hc)) && /* GC: don't shrink finalizing hcs! */ + (hc->_bucketsNum < 4 * hc->_deletes || (256 <= hc->_bucketsCap && hc-> _bucketsUsed < 3 * hc->_bucketsCap / 16)); +} + +CF_INLINE CFIndex __CFHashGetOccurrenceCount(CFHashRef hc, CFIndex idx) { +#if CFBag + return (CFIndex)hc->_values[idx]; +#endif + return 1; +} + +CF_INLINE Boolean __CFHashKeyIsValue(CFHashRef hc, any_t key) { + return (hc->_marker != key && ~hc->_marker != key) ? true : false; +} + +CF_INLINE Boolean __CFHashKeyIsMagic(CFHashRef hc, any_t key) { + return (hc->_marker == key || ~hc->_marker == key) ? true : false; +} + + +#if !defined(CF_OBJC_KVO_WILLCHANGE) +#define CF_OBJC_KVO_WILLCHANGE(obj, key) +#define CF_OBJC_KVO_DIDCHANGE(obj, key) +#endif + +CF_INLINE uintptr_t __CFBagScrambleHash(uintptr_t k) { +#if 0 + return k; +#else +#if __LP64__ + uintptr_t a = 0x4368726973746F70ULL; + uintptr_t b = 0x686572204B616E65ULL; +#else + uintptr_t a = 0x4B616E65UL; + uintptr_t b = 0x4B616E65UL; +#endif + uintptr_t c = 1; + a += k; +#if __LP64__ + a -= b; a -= c; a ^= (c >> 43); + b -= c; b -= a; b ^= (a << 9); + c -= a; c -= b; c ^= (b >> 8); + a -= b; a -= c; a ^= (c >> 38); + b -= c; b -= a; b ^= (a << 23); + c -= a; c -= b; c ^= (b >> 5); + a -= b; a -= c; a ^= (c >> 35); + b -= c; b -= a; b ^= (a << 49); + c -= a; c -= b; c ^= (b >> 11); + a -= b; a -= c; a ^= (c >> 12); + b -= c; b -= a; b ^= (a << 18); + c -= a; c -= b; c ^= (b >> 22); +#else + a -= b; a -= c; a ^= (c >> 13); + b -= c; b -= a; b ^= (a << 8); + c -= a; c -= b; c ^= (b >> 13); + a -= b; a -= c; a ^= (c >> 12); + b -= c; b -= a; b ^= (a << 16); + c -= a; c -= b; c ^= (b >> 5); + a -= b; a -= c; a ^= (c >> 3); + b -= c; b -= a; b ^= (a << 10); + c -= a; c -= b; c ^= (b >> 15); +#endif + return c; +#endif +} + +static CFIndex __CFBagFindBuckets1a(CFHashRef hc, any_t key) { + CFHashCode keyHash = (CFHashCode)key; + keyHash = (CFHashCode)__CFBagScrambleHash(keyHash); + any_t *keys = hc->_keys; + any_t marker = hc->_marker; + CFIndex probe = keyHash & (hc->_bucketsNum - 1); + CFIndex probeskip = 1; // See RemoveValue() for notes before changing this value + CFIndex start = probe; + for (;;) { + any_t currKey = keys[probe]; + if (marker == currKey) { /* empty */ + return kCFNotFound; + } else if (~marker == currKey) { /* deleted */ + /* do nothing */ + } else if (currKey == key) { + return probe; + } + probe = probe + probeskip; + // This alternative to probe % buckets assumes that + // probeskip is always positive and less than the + // number of buckets. + if (hc->_bucketsNum <= probe) { + probe -= hc->_bucketsNum; + } + if (start == probe) { + return kCFNotFound; + } + } +} + +static CFIndex __CFBagFindBuckets1b(CFHashRef hc, any_t key) { + const CFBagKeyCallBacks *cb = __CFBagGetKeyCallBacks(hc); + CFHashCode keyHash = cb->hash ? (CFHashCode)INVOKE_CALLBACK2(((CFHashCode (*)(any_t, any_pointer_t))cb->hash), key, hc->_context) : (CFHashCode)key; + keyHash = (CFHashCode)__CFBagScrambleHash(keyHash); + any_t *keys = hc->_keys; + any_t marker = hc->_marker; + CFIndex probe = keyHash & (hc->_bucketsNum - 1); + CFIndex probeskip = 1; // See RemoveValue() for notes before changing this value + CFIndex start = probe; + for (;;) { + any_t currKey = keys[probe]; + if (marker == currKey) { /* empty */ + return kCFNotFound; + } else if (~marker == currKey) { /* deleted */ + /* do nothing */ + } else if (currKey == key || (cb->equal && INVOKE_CALLBACK3((Boolean (*)(any_t, any_t, any_pointer_t))cb->equal, currKey, key, hc->_context))) { + return probe; + } + probe = probe + probeskip; + // This alternative to probe % buckets assumes that + // probeskip is always positive and less than the + // number of buckets. + if (hc->_bucketsNum <= probe) { + probe -= hc->_bucketsNum; + } + if (start == probe) { + return kCFNotFound; + } + } +} + +CF_INLINE CFIndex __CFBagFindBuckets1(CFHashRef hc, any_t key) { + if (__kCFHashHasNullCallBacks == __CFBitfieldGetValue(hc->_xflags, 3, 2)) { + return __CFBagFindBuckets1a(hc, key); + } + return __CFBagFindBuckets1b(hc, key); +} + +static void __CFBagFindBuckets2(CFHashRef hc, any_t key, CFIndex *match, CFIndex *nomatch) { + const CFBagKeyCallBacks *cb = __CFBagGetKeyCallBacks(hc); + CFHashCode keyHash = cb->hash ? (CFHashCode)INVOKE_CALLBACK2(((CFHashCode (*)(any_t, any_pointer_t))cb->hash), key, hc->_context) : (CFHashCode)key; + keyHash = (CFHashCode)__CFBagScrambleHash(keyHash); + any_t *keys = hc->_keys; + any_t marker = hc->_marker; + CFIndex probe = keyHash & (hc->_bucketsNum - 1); + CFIndex probeskip = 1; // See RemoveValue() for notes before changing this value + CFIndex start = probe; + *match = kCFNotFound; + *nomatch = kCFNotFound; + for (;;) { + any_t currKey = keys[probe]; + if (marker == currKey) { /* empty */ + if (nomatch) *nomatch = probe; + return; + } else if (~marker == currKey) { /* deleted */ + if (nomatch) { + *nomatch = probe; + nomatch = NULL; + } + } else if (currKey == key || (cb->equal && INVOKE_CALLBACK3((Boolean (*)(any_t, any_t, any_pointer_t))cb->equal, currKey, key, hc->_context))) { + *match = probe; + return; + } + probe = probe + probeskip; + // This alternative to probe % buckets assumes that + // probeskip is always positive and less than the + // number of buckets. + if (hc->_bucketsNum <= probe) { + probe -= hc->_bucketsNum; + } + if (start == probe) { + return; + } + } +} + +static void __CFBagFindNewMarker(CFHashRef hc) { + any_t *keys = hc->_keys; + any_t newMarker; + CFIndex idx, nbuckets; + Boolean hit; + + nbuckets = hc->_bucketsNum; + newMarker = hc->_marker; + do { + newMarker--; + hit = false; + for (idx = 0; idx < nbuckets; idx++) { + if (newMarker == keys[idx] || ~newMarker == keys[idx]) { + hit = true; + break; + } + } + } while (hit); + for (idx = 0; idx < nbuckets; idx++) { + if (hc->_marker == keys[idx]) { + keys[idx] = newMarker; + } else if (~hc->_marker == keys[idx]) { + keys[idx] = ~newMarker; + } + } + ((struct __CFBag *)hc)->_marker = newMarker; +} + +static Boolean __CFBagEqual(CFTypeRef cf1, CFTypeRef cf2) { + CFHashRef hc1 = (CFHashRef)cf1; + CFHashRef hc2 = (CFHashRef)cf2; + const CFBagKeyCallBacks *cb1, *cb2; + const CFBagValueCallBacks *vcb1, *vcb2; + any_t *keys; + CFIndex idx, nbuckets; + if (hc1 == hc2) return true; + if (hc1->_count != hc2->_count) return false; + cb1 = __CFBagGetKeyCallBacks(hc1); + cb2 = __CFBagGetKeyCallBacks(hc2); + if (cb1->equal != cb2->equal) return false; + vcb1 = __CFBagGetValueCallBacks(hc1); + vcb2 = __CFBagGetValueCallBacks(hc2); + if (vcb1->equal != vcb2->equal) return false; + if (0 == hc1->_bucketsUsed) return true; /* after function comparison! */ + keys = hc1->_keys; + nbuckets = hc1->_bucketsNum; + for (idx = 0; idx < nbuckets; idx++) { + if (hc1->_marker != keys[idx] && ~hc1->_marker != keys[idx]) { +#if CFDictionary + const_any_pointer_t value; + if (!CFBagGetValueIfPresent(hc2, (any_pointer_t)keys[idx], &value)) return false; + if (hc1->_values[idx] != (any_t)value) { + if (NULL == vcb1->equal) return false; + if (!INVOKE_CALLBACK3((Boolean (*)(any_t, any_t, any_pointer_t))vcb1->equal, hc1->_values[idx], (any_t)value, hc1->_context)) return false; + } +#endif +#if CFSet + const_any_pointer_t value; + if (!CFBagGetValueIfPresent(hc2, (any_pointer_t)keys[idx], &value)) return false; +#endif +#if CFBag + if (hc1->_values[idx] != CFBagGetCountOfValue(hc2, (any_pointer_t)keys[idx])) return false; +#endif + } + } + return true; +} + +static CFHashCode __CFBagHash(CFTypeRef cf) { + CFHashRef hc = (CFHashRef)cf; + return hc->_count; +} + +static CFStringRef __CFBagCopyDescription(CFTypeRef cf) { + CFHashRef hc = (CFHashRef)cf; + CFAllocatorRef allocator; + const CFBagKeyCallBacks *cb; + const CFBagValueCallBacks *vcb; + any_t *keys; + CFIndex idx, nbuckets; + CFMutableStringRef result; + cb = __CFBagGetKeyCallBacks(hc); + vcb = __CFBagGetValueCallBacks(hc); + keys = hc->_keys; + nbuckets = hc->_bucketsNum; + allocator = CFGetAllocator(hc); + result = CFStringCreateMutable(allocator, 0); + const char *type = "?"; + switch (__CFHashGetType(hc)) { + case __kCFHashImmutable: type = "immutable"; break; + case __kCFHashMutable: type = "mutable"; break; + } + CFStringAppendFormat(result, NULL, CFSTR("<CFBag %p [%p]>{type = %s, count = %u, capacity = %u, pairs = (\n"), cf, allocator, type, hc->_count, hc->_bucketsCap); + for (idx = 0; idx < nbuckets; idx++) { + if (__CFHashKeyIsValue(hc, keys[idx])) { + CFStringRef kDesc = NULL, vDesc = NULL; + if (NULL != cb->copyDescription) { + kDesc = (CFStringRef)INVOKE_CALLBACK2(((CFStringRef (*)(any_t, any_pointer_t))cb->copyDescription), keys[idx], hc->_context); + } + if (NULL != vcb->copyDescription) { + vDesc = (CFStringRef)INVOKE_CALLBACK2(((CFStringRef (*)(any_t, any_pointer_t))vcb->copyDescription), hc->_values[idx], hc->_context); + } +#if CFDictionary + if (NULL != kDesc && NULL != vDesc) { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : %@ = %@\n"), idx, kDesc, vDesc); + CFRelease(kDesc); + CFRelease(vDesc); + } else if (NULL != kDesc) { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : %@ = <%p>\n"), idx, kDesc, hc->_values[idx]); + CFRelease(kDesc); + } else if (NULL != vDesc) { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : <%p> = %@\n"), idx, keys[idx], vDesc); + CFRelease(vDesc); + } else { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : <%p> = <%p>\n"), idx, keys[idx], hc->_values[idx]); + } +#endif +#if CFSet + if (NULL != kDesc) { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : %@\n"), idx, kDesc); + CFRelease(kDesc); + } else { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : <%p>\n"), idx, keys[idx]); + } +#endif +#if CFBag + if (NULL != kDesc) { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : %@ (%ld)\n"), idx, kDesc, hc->_values[idx]); + CFRelease(kDesc); + } else { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : <%p> (%ld)\n"), idx, keys[idx], hc->_values[idx]); + } +#endif + } + } + CFStringAppend(result, CFSTR(")}")); + return result; +} + +static void __CFBagDeallocate(CFTypeRef cf) { + CFMutableHashRef hc = (CFMutableHashRef)cf; + CFAllocatorRef allocator = __CFGetAllocator(hc); + const CFBagKeyCallBacks *cb = __CFBagGetKeyCallBacks(hc); + const CFBagValueCallBacks *vcb = __CFBagGetValueCallBacks(hc); + + // mark now in case any callout somehow tries to add an entry back in + markFinalized(cf); + if (vcb->release || cb->release) { + any_t *keys = hc->_keys; + CFIndex idx, nbuckets = hc->_bucketsNum; + for (idx = 0; idx < nbuckets; idx++) { + any_t oldkey = keys[idx]; + if (hc->_marker != oldkey && ~hc->_marker != oldkey) { + if (vcb->release) { + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, any_t, any_pointer_t))vcb->release), allocator, hc->_values[idx], hc->_context); + } + if (cb->release) { + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, any_t, any_pointer_t))cb->release), allocator, oldkey, hc->_context); + } + } + } + } + + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + // return early so that contents are preserved after finalization + return; + } + + _CFAllocatorDeallocateGC(allocator, hc->_keys); +#if CFDictionary || CFBag + _CFAllocatorDeallocateGC(allocator, hc->_values); +#endif + hc->_keys = NULL; + hc->_values = NULL; + hc->_count = 0; // GC: also zero count, so the hc will appear empty. + hc->_bucketsUsed = 0; + hc->_bucketsNum = 0; +} + +static CFTypeID __kCFBagTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFBagClass = { + _kCFRuntimeScannedObject, + "CFBag", + NULL, // init + NULL, // copy + __CFBagDeallocate, + __CFBagEqual, + __CFBagHash, + NULL, // + __CFBagCopyDescription +}; + +__private_extern__ void __CFBagInitialize(void) { + __kCFHashTypeID = _CFRuntimeRegisterClass(&__CFBagClass); +} + +CFTypeID CFBagGetTypeID(void) { + return __kCFHashTypeID; +} + +static CFMutableHashRef __CFBagInit(CFAllocatorRef allocator, CFOptionFlags flags, CFIndex capacity, const CFBagKeyCallBacks *keyCallBacks +#if CFDictionary +, const CFBagValueCallBacks *valueCallBacks +#endif +) { + struct __CFBag *hc; + CFIndex size; + __CFBitfieldSetValue(flags, 31, 2, 0); + CFOptionFlags xflags = 0; + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + // preserve NULL for key or value CB, otherwise fix up. + if (!keyCallBacks || (keyCallBacks->retain == NULL && keyCallBacks->release == NULL)) { + xflags = __kCFHashWeakKeys; + } +#if CFDictionary + if (!valueCallBacks || (valueCallBacks->retain == NULL && valueCallBacks->release == NULL)) { + xflags |= __kCFHashWeakValues; + } +#endif +#if CFBag + xflags |= __kCFHashWeakValues; +#endif + } + if (__CFBagKeyCallBacksMatchNull(keyCallBacks)) { + __CFBitfieldSetValue(flags, 3, 2, __kCFHashHasNullCallBacks); + } else if (__CFBagKeyCallBacksMatchCFType(keyCallBacks)) { + __CFBitfieldSetValue(flags, 3, 2, __kCFHashHasCFTypeCallBacks); + } else { + __CFBitfieldSetValue(flags, 3, 2, __kCFHashHasCustomCallBacks); + } +#if CFDictionary + if (__CFBagValueCallBacksMatchNull(valueCallBacks)) { + __CFBitfieldSetValue(flags, 5, 4, __kCFHashHasNullCallBacks); + } else if (__CFBagValueCallBacksMatchCFType(valueCallBacks)) { + __CFBitfieldSetValue(flags, 5, 4, __kCFHashHasCFTypeCallBacks); + } else { + __CFBitfieldSetValue(flags, 5, 4, __kCFHashHasCustomCallBacks); + } +#endif + size = __CFBagGetSizeOfType(flags) - sizeof(CFRuntimeBase); + hc = (struct __CFBag *)_CFRuntimeCreateInstance(allocator, __kCFHashTypeID, size, NULL); + if (NULL == hc) { + return NULL; + } + hc->_count = 0; + hc->_bucketsUsed = 0; + hc->_marker = (any_t)0xa1b1c1d3; + hc->_context = NULL; + hc->_deletes = 0; + hc->_mutations = 1; + hc->_xflags = xflags | flags; + switch (__CFBitfieldGetValue(flags, 1, 0)) { + case __kCFHashImmutable: + if (__CFOASafe) __CFSetLastAllocationEventName(hc, "CFBag (immutable)"); + break; + case __kCFHashMutable: + if (__CFOASafe) __CFSetLastAllocationEventName(hc, "CFBag (mutable-variable)"); + break; + } + hc->_bucketsCap = __CFHashRoundUpCapacity(1); + hc->_bucketsNum = 0; + hc->_keys = NULL; + hc->_values = NULL; + if (__kCFHashHasCustomCallBacks == __CFBitfieldGetValue(flags, 3, 2)) { + CFBagKeyCallBacks *cb = (CFBagKeyCallBacks *)__CFBagGetKeyCallBacks((CFHashRef)hc); + *cb = *keyCallBacks; + FAULT_CALLBACK((void **)&(cb->retain)); + FAULT_CALLBACK((void **)&(cb->release)); + FAULT_CALLBACK((void **)&(cb->copyDescription)); + FAULT_CALLBACK((void **)&(cb->equal)); + FAULT_CALLBACK((void **)&(cb->hash)); + } +#if CFDictionary + if (__kCFHashHasCustomCallBacks == __CFBitfieldGetValue(flags, 5, 4)) { + CFBagValueCallBacks *vcb = (CFBagValueCallBacks *)__CFBagGetValueCallBacks((CFHashRef)hc); + *vcb = *valueCallBacks; + FAULT_CALLBACK((void **)&(vcb->retain)); + FAULT_CALLBACK((void **)&(vcb->release)); + FAULT_CALLBACK((void **)&(vcb->copyDescription)); + FAULT_CALLBACK((void **)&(vcb->equal)); + } +#endif + return hc; +} + +#if CFDictionary +CFHashRef CFBagCreate(CFAllocatorRef allocator, const_any_pointer_t *keys, const_any_pointer_t *values, CFIndex numValues, const CFBagKeyCallBacks *keyCallBacks, const CFBagValueCallBacks *valueCallBacks) { +#endif +#if CFSet || CFBag +CFHashRef CFBagCreate(CFAllocatorRef allocator, const_any_pointer_t *keys, CFIndex numValues, const CFBagKeyCallBacks *keyCallBacks) { +#endif + CFAssert2(0 <= numValues, __kCFLogAssertion, "%s(): numValues (%ld) cannot be less than zero", __PRETTY_FUNCTION__, numValues); +#if CFDictionary + CFMutableHashRef hc = __CFBagInit(allocator, __kCFHashImmutable, numValues, keyCallBacks, valueCallBacks); +#endif +#if CFSet || CFBag + CFMutableHashRef hc = __CFBagInit(allocator, __kCFHashImmutable, numValues, keyCallBacks); +#endif + __CFBitfieldSetValue(hc->_xflags, 1, 0, __kCFHashMutable); + for (CFIndex idx = 0; idx < numValues; idx++) { +#if CFDictionary + CFBagAddValue(hc, keys[idx], values[idx]); +#endif +#if CFSet || CFBag + CFBagAddValue(hc, keys[idx]); +#endif + } + __CFBitfieldSetValue(hc->_xflags, 1, 0, __kCFHashImmutable); + return (CFHashRef)hc; +} + +#if CFDictionary +CFMutableHashRef CFBagCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFBagKeyCallBacks *keyCallBacks, const CFBagValueCallBacks *valueCallBacks) { +#endif +#if CFSet || CFBag +CFMutableHashRef CFBagCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFBagKeyCallBacks *keyCallBacks) { +#endif + CFAssert2(0 <= capacity, __kCFLogAssertion, "%s(): capacity (%ld) cannot be less than zero", __PRETTY_FUNCTION__, capacity); +#if CFDictionary + CFMutableHashRef hc = __CFBagInit(allocator, __kCFHashMutable, capacity, keyCallBacks, valueCallBacks); +#endif +#if CFSet || CFBag + CFMutableHashRef hc = __CFBagInit(allocator, __kCFHashMutable, capacity, keyCallBacks); +#endif + return hc; +} + +#if CFDictionary || CFSet +// does not have Add semantics for Bag; it has Set semantics ... is that best? +static void __CFBagGrow(CFMutableHashRef hc, CFIndex numNewValues); + +// This creates a hc which is for CFTypes or NSObjects, with a CFRetain style ownership transfer; +// the hc does not take a retain (since it claims 1), and the caller does not need to release the inserted objects (since we do it). +// The incoming objects must also be collectable if allocated out of a collectable allocator - and are neither released nor retained. +#if CFDictionary +CFHashRef _CFBagCreate_ex(CFAllocatorRef allocator, Boolean isMutable, const_any_pointer_t *keys, const_any_pointer_t *values, CFIndex numValues) { +#endif +#if CFSet || CFBag +CFHashRef _CFBagCreate_ex(CFAllocatorRef allocator, Boolean isMutable, const_any_pointer_t *keys, CFIndex numValues) { +#endif + CFAssert2(0 <= numValues, __kCFLogAssertion, "%s(): numValues (%ld) cannot be less than zero", __PRETTY_FUNCTION__, numValues); +#if CFDictionary + CFMutableHashRef hc = __CFBagInit(allocator, __kCFHashMutable, numValues, &kCFTypeBagKeyCallBacks, &kCFTypeBagValueCallBacks); +#endif +#if CFSet || CFBag + CFMutableHashRef hc = __CFBagInit(allocator, __kCFHashMutable, numValues, &kCFTypeBagKeyCallBacks); +#endif + __CFBagGrow(hc, numValues); + for (CFIndex idx = 0; idx < numValues; idx++) { + CFIndex match, nomatch; + __CFBagFindBuckets2(hc, (any_t)keys[idx], &match, &nomatch); + if (kCFNotFound == match) { + CFAllocatorRef allocator = __CFGetAllocator(hc); + any_t newKey = (any_t)keys[idx]; + if (__CFHashKeyIsMagic(hc, newKey)) { + __CFBagFindNewMarker(hc); + } + if (hc->_keys[nomatch] == ~hc->_marker) { + hc->_deletes--; + } + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[nomatch], newKey); +#if CFDictionary + any_t newValue = (any_t)values[idx]; + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, hc->_values[nomatch], newValue); +#endif +#if CFBag + hc->_values[nomatch] = 1; +#endif + hc->_bucketsUsed++; + hc->_count++; + } else { + CFAllocatorRef allocator = __CFGetAllocator(hc); +#if CFSet || CFBag + any_t oldKey = hc->_keys[match]; + any_t newKey = (any_t)keys[idx]; + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[match], ~hc->_marker); + if (__CFHashKeyIsMagic(hc, newKey)) { + __CFBagFindNewMarker(hc); + } + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[match], newKey); + RELEASEKEY(oldKey); +#endif +#if CFDictionary + any_t oldValue = hc->_values[match]; + any_t newValue = (any_t)values[idx]; + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, hc->_values[match], newValue); + RELEASEVALUE(oldValue); +#endif + } + } + if (!isMutable) __CFBitfieldSetValue(hc->_xflags, 1, 0, __kCFHashImmutable); + return (CFHashRef)hc; +} +#endif + +CFHashRef CFBagCreateCopy(CFAllocatorRef allocator, CFHashRef other) { + CFMutableHashRef hc = CFBagCreateMutableCopy(allocator, CFBagGetCount(other), other); + __CFBitfieldSetValue(hc->_xflags, 1, 0, __kCFHashImmutable); + if (__CFOASafe) __CFSetLastAllocationEventName(hc, "CFBag (immutable)"); + return hc; +} + +CFMutableHashRef CFBagCreateMutableCopy(CFAllocatorRef allocator, CFIndex capacity, CFHashRef other) { + CFIndex numValues = CFBagGetCount(other); + const_any_pointer_t *list, buffer[256]; + list = (numValues <= 256) ? buffer : (const_any_pointer_t *)CFAllocatorAllocate(allocator, numValues * sizeof(const_any_pointer_t), 0); + if (list != buffer && __CFOASafe) __CFSetLastAllocationEventName(list, "CFBag (temp)"); +#if CFDictionary + const_any_pointer_t *vlist, vbuffer[256]; + vlist = (numValues <= 256) ? vbuffer : (const_any_pointer_t *)CFAllocatorAllocate(allocator, numValues * sizeof(const_any_pointer_t), 0); + if (vlist != vbuffer && __CFOASafe) __CFSetLastAllocationEventName(vlist, "CFBag (temp)"); +#endif +#if CFSet || CFBag + CFBagGetValues(other, list); +#endif +#if CFDictionary + CFBagGetKeysAndValues(other, list, vlist); +#endif + const CFBagKeyCallBacks *kcb; + const CFBagValueCallBacks *vcb; + if (CF_IS_OBJC(__kCFHashTypeID, other)) { + kcb = &kCFTypeBagKeyCallBacks; + vcb = &kCFTypeBagValueCallBacks; + } else { + kcb = __CFBagGetKeyCallBacks(other); + vcb = __CFBagGetValueCallBacks(other); + } +#if CFDictionary + CFMutableHashRef hc = __CFBagInit(allocator, __kCFHashMutable, capacity, kcb, vcb); +#endif +#if CFSet || CFBag + CFMutableHashRef hc = __CFBagInit(allocator, __kCFHashMutable, capacity, kcb); +#endif + if (0 == capacity) _CFBagSetCapacity(hc, numValues); + for (CFIndex idx = 0; idx < numValues; idx++) { +#if CFDictionary + CFBagAddValue(hc, list[idx], vlist[idx]); +#endif +#if CFSet || CFBag + CFBagAddValue(hc, list[idx]); +#endif + } + if (list != buffer) CFAllocatorDeallocate(allocator, list); +#if CFDictionary + if (vlist != vbuffer) CFAllocatorDeallocate(allocator, vlist); +#endif + return hc; +} + +// Used by NSHashTables/NSMapTables and KVO +void _CFBagSetContext(CFHashRef hc, any_pointer_t context) { + __CFGenericValidateType(hc, __kCFHashTypeID); + CF_WRITE_BARRIER_BASE_ASSIGN(__CFGetAllocator(hc), hc, hc->_context, context); +} + +any_pointer_t _CFBagGetContext(CFHashRef hc) { + __CFGenericValidateType(hc, __kCFHashTypeID); + return hc->_context; +} + +CFIndex CFBagGetCount(CFHashRef hc) { + if (CFDictionary || CFSet) CF_OBJC_FUNCDISPATCH0(__kCFHashTypeID, CFIndex, hc, "count"); + __CFGenericValidateType(hc, __kCFHashTypeID); + return hc->_count; +} + +#if CFDictionary +CFIndex CFBagGetCountOfKey(CFHashRef hc, const_any_pointer_t key) { +#endif +#if CFSet || CFBag +CFIndex CFBagGetCountOfValue(CFHashRef hc, const_any_pointer_t key) { +#endif + if (CFDictionary) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, CFIndex, hc, "countForKey:", key); + if (CFSet) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, CFIndex, hc, "countForObject:", key); + __CFGenericValidateType(hc, __kCFHashTypeID); + if (0 == hc->_bucketsUsed) return 0; + CFIndex match = __CFBagFindBuckets1(hc, (any_t)key); + return (kCFNotFound != match ? __CFHashGetOccurrenceCount(hc, match) : 0); +} + +#if CFDictionary +Boolean CFBagContainsKey(CFHashRef hc, const_any_pointer_t key) { +#endif +#if CFSet || CFBag +Boolean CFBagContainsValue(CFHashRef hc, const_any_pointer_t key) { +#endif + if (CFDictionary) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, char, hc, "containsKey:", key); + if (CFSet) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, char, hc, "containsObject:", key); + __CFGenericValidateType(hc, __kCFHashTypeID); + if (0 == hc->_bucketsUsed) return false; + CFIndex match = __CFBagFindBuckets1(hc, (any_t)key); + return (kCFNotFound != match ? true : false); +} + +#if CFDictionary +CFIndex CFBagGetCountOfValue(CFHashRef hc, const_any_pointer_t value) { + CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, CFIndex, hc, "countForObject:", value); + __CFGenericValidateType(hc, __kCFHashTypeID); + if (0 == hc->_bucketsUsed) return 0; + any_t *keys = hc->_keys; + Boolean (*equal)(any_t, any_t, any_pointer_t) = (Boolean (*)(any_t, any_t, any_pointer_t))__CFBagGetValueCallBacks(hc)->equal; + CFIndex cnt = 0; + for (CFIndex idx = 0, nbuckets = hc->_bucketsNum; idx < nbuckets; idx++) { + if (__CFHashKeyIsValue(hc, keys[idx])) { + if ((hc->_values[idx] == (any_t)value) || (equal && INVOKE_CALLBACK3(equal, hc->_values[idx], (any_t)value, hc->_context))) { + cnt++; + } + } + } + return cnt; +} + +Boolean CFBagContainsValue(CFHashRef hc, const_any_pointer_t value) { + CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, char, hc, "containsObject:", value); + __CFGenericValidateType(hc, __kCFHashTypeID); + if (0 == hc->_bucketsUsed) return false; + any_t *keys = hc->_keys; + Boolean (*equal)(any_t, any_t, any_pointer_t) = (Boolean (*)(any_t, any_t, any_pointer_t))__CFBagGetValueCallBacks(hc)->equal; + for (CFIndex idx = 0, nbuckets = hc->_bucketsNum; idx < nbuckets; idx++) { + if (__CFHashKeyIsValue(hc, keys[idx])) { + if ((hc->_values[idx] == (any_t)value) || (equal && INVOKE_CALLBACK3(equal, hc->_values[idx], (any_t)value, hc->_context))) { + return true; + } + } + } + return false; +} +#endif + +const_any_pointer_t CFBagGetValue(CFHashRef hc, const_any_pointer_t key) { + if (CFDictionary) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, const_any_pointer_t, hc, "objectForKey:", key); + if (CFSet) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, const_any_pointer_t, hc, "member:", key); + __CFGenericValidateType(hc, __kCFHashTypeID); + if (0 == hc->_bucketsUsed) return 0; + CFIndex match = __CFBagFindBuckets1(hc, (any_t)key); + return (kCFNotFound != match ? (const_any_pointer_t)(CFDictionary ? hc->_values[match] : hc->_keys[match]) : 0); +} + +Boolean CFBagGetValueIfPresent(CFHashRef hc, const_any_pointer_t key, const_any_pointer_t *value) { + if (CFDictionary) CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, Boolean, hc, "_getValue:forKey:", (any_t *)value, key); + if (CFSet) CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, Boolean, hc, "_getValue:forObj:", (any_t *)value, key); + __CFGenericValidateType(hc, __kCFHashTypeID); + if (0 == hc->_bucketsUsed) return false; + CFIndex match = __CFBagFindBuckets1(hc, (any_t)key); + return (kCFNotFound != match ? ((value ? __CFObjCStrongAssign((const_any_pointer_t)(CFDictionary ? hc->_values[match] : hc->_keys[match]), value) : 0), true) : false); +} + +#if CFDictionary +Boolean CFBagGetKeyIfPresent(CFHashRef hc, const_any_pointer_t key, const_any_pointer_t *actualkey) { + CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, Boolean, hc, "getActualKey:forKey:", actualkey, key); + __CFGenericValidateType(hc, __kCFHashTypeID); + if (0 == hc->_bucketsUsed) return false; + CFIndex match = __CFBagFindBuckets1(hc, (any_t)key); + return (kCFNotFound != match ? ((actualkey ? __CFObjCStrongAssign((const_any_pointer_t)hc->_keys[match], actualkey) : NULL), true) : false); +} +#endif + +#if CFDictionary +void CFBagGetKeysAndValues(CFHashRef hc, const_any_pointer_t *keybuf, const_any_pointer_t *valuebuf) { +#endif +#if CFSet || CFBag +void CFBagGetValues(CFHashRef hc, const_any_pointer_t *keybuf) { + const_any_pointer_t *valuebuf = 0; +#endif + if (CFDictionary) CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, void, hc, "getObjects:andKeys:", (any_t *)valuebuf, (any_t *)keybuf); + if (CFSet) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, void, hc, "getObjects:", (any_t *)keybuf); + __CFGenericValidateType(hc, __kCFHashTypeID); + if (CF_USING_COLLECTABLE_MEMORY) { + // GC: speculatively issue a write-barrier on the copied to buffers + __CFObjCWriteBarrierRange(keybuf, hc->_count * sizeof(any_t)); + __CFObjCWriteBarrierRange(valuebuf, hc->_count * sizeof(any_t)); + } + any_t *keys = hc->_keys; + for (CFIndex idx = 0, nbuckets = hc->_bucketsNum; idx < nbuckets; idx++) { + if (__CFHashKeyIsValue(hc, keys[idx])) { + for (CFIndex cnt = __CFHashGetOccurrenceCount(hc, idx); cnt--;) { + if (keybuf) *keybuf++ = (const_any_pointer_t)keys[idx]; + if (valuebuf) *valuebuf++ = (const_any_pointer_t)hc->_values[idx]; + } + } + } +} + +#if CFDictionary || CFSet +unsigned long _CFBagFastEnumeration(CFHashRef hc, struct __objcFastEnumerationStateEquivalent *state, void *stackbuffer, unsigned long count) { + /* copy as many as count items over */ + if (0 == state->state) { /* first time */ + state->mutationsPtr = (unsigned long *)&hc->_mutations; + } + state->itemsPtr = (unsigned long *)stackbuffer; + CFIndex cnt = 0; + any_t *keys = hc->_keys; + for (CFIndex idx = (CFIndex)state->state, nbuckets = hc->_bucketsNum; idx < nbuckets && cnt < (CFIndex)count; idx++) { + if (__CFHashKeyIsValue(hc, keys[idx])) { + state->itemsPtr[cnt++] = (unsigned long)keys[idx]; + } + state->state++; + } + return cnt; +} +#endif + +void CFBagApplyFunction(CFHashRef hc, CFBagApplierFunction applier, any_pointer_t context) { + FAULT_CALLBACK((void **)&(applier)); + if (CFDictionary) CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, void, hc, "_apply:context:", applier, context); + if (CFSet) CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, void, hc, "_applyValues:context:", applier, context); + __CFGenericValidateType(hc, __kCFHashTypeID); + any_t *keys = hc->_keys; + for (CFIndex idx = 0, nbuckets = hc->_bucketsNum; idx < nbuckets; idx++) { + if (__CFHashKeyIsValue(hc, keys[idx])) { + for (CFIndex cnt = __CFHashGetOccurrenceCount(hc, idx); cnt--;) { +#if CFDictionary + INVOKE_CALLBACK3(applier, (const_any_pointer_t)keys[idx], (const_any_pointer_t)hc->_values[idx], context); +#endif +#if CFSet || CFBag + INVOKE_CALLBACK2(applier, (const_any_pointer_t)keys[idx], context); +#endif + } + } + } +} + +static void __CFBagGrow(CFMutableHashRef hc, CFIndex numNewValues) { + any_t *oldkeys = hc->_keys; + any_t *oldvalues = hc->_values; + CFIndex nbuckets = hc->_bucketsNum; + hc->_bucketsCap = __CFHashRoundUpCapacity(hc->_bucketsUsed + numNewValues); + hc->_bucketsNum = __CFHashNumBucketsForCapacity(hc->_bucketsCap); + hc->_deletes = 0; + CFAllocatorRef allocator = __CFGetAllocator(hc); + CFOptionFlags weakOrStrong = (hc->_xflags & __kCFHashWeakKeys) ? 0 : __kCFAllocatorGCScannedMemory; + any_t *mem = (any_t *)_CFAllocatorAllocateGC(allocator, hc->_bucketsNum * sizeof(any_t), weakOrStrong); + if (NULL == mem) __CFBagHandleOutOfMemory(hc, hc->_bucketsNum * sizeof(any_t)); + if (__CFOASafe) __CFSetLastAllocationEventName(mem, "CFBag (key-store)"); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, hc, hc->_keys, mem); + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; // GC: avoids write-barrier in weak case. + any_t *keysBase = mem; +#if CFDictionary || CFBag + weakOrStrong = (hc->_xflags & __kCFHashWeakValues) ? 0 : __kCFAllocatorGCScannedMemory; + mem = (any_t *)_CFAllocatorAllocateGC(allocator, hc->_bucketsNum * sizeof(any_t), weakOrStrong); + if (NULL == mem) __CFBagHandleOutOfMemory(hc, hc->_bucketsNum * sizeof(any_t)); + if (__CFOASafe) __CFSetLastAllocationEventName(mem, "CFBag (value-store)"); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, hc, hc->_values, mem); +#endif +#if CFDictionary + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; // GC: avoids write-barrier in weak case. + any_t *valuesBase = mem; +#endif + for (CFIndex idx = 0, nbuckets = hc->_bucketsNum; idx < nbuckets; idx++) { + hc->_keys[idx] = hc->_marker; +#if CFDictionary || CFBag + hc->_values[idx] = 0; +#endif + } + if (NULL == oldkeys) return; + for (CFIndex idx = 0; idx < nbuckets; idx++) { + if (__CFHashKeyIsValue(hc, oldkeys[idx])) { + CFIndex match, nomatch; + __CFBagFindBuckets2(hc, oldkeys[idx], &match, &nomatch); + CFAssert3(kCFNotFound == match, __kCFLogAssertion, "%s(): two values (%p, %p) now hash to the same slot; mutable value changed while in table or hash value is not immutable", __PRETTY_FUNCTION__, oldkeys[idx], hc->_keys[match]); + if (kCFNotFound != nomatch) { + CF_WRITE_BARRIER_BASE_ASSIGN(keysAllocator, keysBase, hc->_keys[nomatch], oldkeys[idx]); +#if CFDictionary + CF_WRITE_BARRIER_BASE_ASSIGN(valuesAllocator, valuesBase, hc->_values[nomatch], oldvalues[idx]); +#endif +#if CFBag + hc->_values[nomatch] = oldvalues[idx]; +#endif + } + } + } + _CFAllocatorDeallocateGC(allocator, oldkeys); + _CFAllocatorDeallocateGC(allocator, oldvalues); +} + +// This function is for Foundation's benefit; no one else should use it. +void _CFBagSetCapacity(CFMutableHashRef hc, CFIndex cap) { + if (CF_IS_OBJC(__kCFHashTypeID, hc)) return; + __CFGenericValidateType(hc, __kCFHashTypeID); + CFAssert1(__CFHashGetType(hc) != __kCFHashImmutable, __kCFLogAssertion, "%s(): collection is immutable", __PRETTY_FUNCTION__); + CFAssert3(hc->_bucketsUsed <= cap, __kCFLogAssertion, "%s(): desired capacity (%ld) is less than bucket count (%ld)", __PRETTY_FUNCTION__, cap, hc->_bucketsUsed); + __CFBagGrow(hc, cap - hc->_bucketsUsed); +} + + +#if CFDictionary +void CFBagAddValue(CFMutableHashRef hc, const_any_pointer_t key, const_any_pointer_t value) { +#endif +#if CFSet || CFBag +void CFBagAddValue(CFMutableHashRef hc, const_any_pointer_t key) { + #define value 0 +#endif + if (CFDictionary) CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, void, hc, "_addObject:forKey:", value, key); + if (CFSet) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, void, hc, "addObject:", key); + __CFGenericValidateType(hc, __kCFHashTypeID); + switch (__CFHashGetType(hc)) { + case __kCFHashMutable: + if (hc->_bucketsUsed == hc->_bucketsCap || NULL == hc->_keys) { + __CFBagGrow(hc, 1); + } + break; + default: + CFAssert2(__CFHashGetType(hc) != __kCFHashImmutable, __kCFLogAssertion, "%s(): immutable collection %p passed to mutating operation", __PRETTY_FUNCTION__, hc); + break; + } + hc->_mutations++; + CFIndex match, nomatch; + __CFBagFindBuckets2(hc, (any_t)key, &match, &nomatch); + if (kCFNotFound != match) { +#if CFBag + CF_OBJC_KVO_WILLCHANGE(hc, hc->_keys[match]); + hc->_values[match]++; + hc->_count++; + CF_OBJC_KVO_DIDCHANGE(hc, hc->_keys[match]); +#endif + } else { + CFAllocatorRef allocator = __CFGetAllocator(hc); + GETNEWKEY(newKey, key); +#if CFDictionary + GETNEWVALUE(newValue); +#endif + if (__CFHashKeyIsMagic(hc, newKey)) { + __CFBagFindNewMarker(hc); + } + if (hc->_keys[nomatch] == ~hc->_marker) { + hc->_deletes--; + } + CF_OBJC_KVO_WILLCHANGE(hc, key); + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[nomatch], newKey); +#if CFDictionary + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, hc->_values[nomatch], newValue); +#endif +#if CFBag + hc->_values[nomatch] = 1; +#endif + hc->_bucketsUsed++; + hc->_count++; + CF_OBJC_KVO_DIDCHANGE(hc, key); + } +} + +#if CFDictionary +void CFBagReplaceValue(CFMutableHashRef hc, const_any_pointer_t key, const_any_pointer_t value) { +#endif +#if CFSet || CFBag +void CFBagReplaceValue(CFMutableHashRef hc, const_any_pointer_t key) { + #define value 0 +#endif + if (CFDictionary) CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, void, hc, "_replaceObject:forKey:", value, key); + if (CFSet) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, void, hc, "_replaceObject:", key); + __CFGenericValidateType(hc, __kCFHashTypeID); + switch (__CFHashGetType(hc)) { + case __kCFHashMutable: + break; + default: + CFAssert2(__CFHashGetType(hc) != __kCFHashImmutable, __kCFLogAssertion, "%s(): immutable collection %p passed to mutating operation", __PRETTY_FUNCTION__, hc); + break; + } + hc->_mutations++; + if (0 == hc->_bucketsUsed) return; + CFIndex match = __CFBagFindBuckets1(hc, (any_t)key); + if (kCFNotFound == match) return; + CFAllocatorRef allocator = __CFGetAllocator(hc); +#if CFSet || CFBag + GETNEWKEY(newKey, key); +#endif +#if CFDictionary + GETNEWVALUE(newValue); +#endif + any_t oldKey = hc->_keys[match]; + CF_OBJC_KVO_WILLCHANGE(hc, oldKey); +#if CFSet || CFBag + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[match], ~hc->_marker); + if (__CFHashKeyIsMagic(hc, newKey)) { + __CFBagFindNewMarker(hc); + } + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[match], newKey); +#endif +#if CFDictionary + any_t oldValue = hc->_values[match]; + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, hc->_values[match], newValue); +#endif + CF_OBJC_KVO_DIDCHANGE(hc, oldKey); +#if CFSet || CFBag + RELEASEKEY(oldKey); +#endif +#if CFDictionary + RELEASEVALUE(oldValue); +#endif +} + +#if CFDictionary +void CFBagSetValue(CFMutableHashRef hc, const_any_pointer_t key, const_any_pointer_t value) { +#endif +#if CFSet || CFBag +void CFBagSetValue(CFMutableHashRef hc, const_any_pointer_t key) { + #define value 0 +#endif + if (CFDictionary) CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, void, hc, "setObject:forKey:", value, key); + if (CFSet) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, void, hc, "_setObject:", key); + __CFGenericValidateType(hc, __kCFHashTypeID); + switch (__CFHashGetType(hc)) { + case __kCFHashMutable: + if (hc->_bucketsUsed == hc->_bucketsCap || NULL == hc->_keys) { + __CFBagGrow(hc, 1); + } + break; + default: + CFAssert2(__CFHashGetType(hc) != __kCFHashImmutable, __kCFLogAssertion, "%s(): immutable collection %p passed to mutating operation", __PRETTY_FUNCTION__, hc); + break; + } + hc->_mutations++; + CFIndex match, nomatch; + __CFBagFindBuckets2(hc, (any_t)key, &match, &nomatch); + if (kCFNotFound == match) { + CFAllocatorRef allocator = __CFGetAllocator(hc); + GETNEWKEY(newKey, key); +#if CFDictionary + GETNEWVALUE(newValue); +#endif + if (__CFHashKeyIsMagic(hc, newKey)) { + __CFBagFindNewMarker(hc); + } + if (hc->_keys[nomatch] == ~hc->_marker) { + hc->_deletes--; + } + CF_OBJC_KVO_WILLCHANGE(hc, key); + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[nomatch], newKey); +#if CFDictionary + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, hc->_values[nomatch], newValue); +#endif +#if CFBag + hc->_values[nomatch] = 1; +#endif + hc->_bucketsUsed++; + hc->_count++; + CF_OBJC_KVO_DIDCHANGE(hc, key); + } else { + CFAllocatorRef allocator = __CFGetAllocator(hc); +#if CFSet || CFBag + GETNEWKEY(newKey, key); +#endif +#if CFDictionary + GETNEWVALUE(newValue); +#endif + any_t oldKey = hc->_keys[match]; + CF_OBJC_KVO_WILLCHANGE(hc, oldKey); +#if CFSet || CFBag + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[match], ~hc->_marker); + if (__CFHashKeyIsMagic(hc, newKey)) { + __CFBagFindNewMarker(hc); + } + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[match], newKey); +#endif +#if CFDictionary + any_t oldValue = hc->_values[match]; + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, hc->_values[match], newValue); +#endif + CF_OBJC_KVO_DIDCHANGE(hc, oldKey); +#if CFSet || CFBag + RELEASEKEY(oldKey); +#endif +#if CFDictionary + RELEASEVALUE(oldValue); +#endif + } +} + +void CFBagRemoveValue(CFMutableHashRef hc, const_any_pointer_t key) { + if (CFDictionary) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, void, hc, "removeObjectForKey:", key); + if (CFSet) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, void, hc, "removeObject:", key); + __CFGenericValidateType(hc, __kCFHashTypeID); + switch (__CFHashGetType(hc)) { + case __kCFHashMutable: + break; + default: + CFAssert2(__CFHashGetType(hc) != __kCFHashImmutable, __kCFLogAssertion, "%s(): immutable collection %p passed to mutating operation", __PRETTY_FUNCTION__, hc); + break; + } + hc->_mutations++; + if (0 == hc->_bucketsUsed) return; + CFIndex match = __CFBagFindBuckets1(hc, (any_t)key); + if (kCFNotFound == match) return; + if (1 < __CFHashGetOccurrenceCount(hc, match)) { +#if CFBag + CF_OBJC_KVO_WILLCHANGE(hc, hc->_keys[match]); + hc->_values[match]--; + hc->_count--; + CF_OBJC_KVO_DIDCHANGE(hc, hc->_keys[match]); +#endif + } else { + CFAllocatorRef allocator = __CFGetAllocator(hc); + any_t oldKey = hc->_keys[match]; + CF_OBJC_KVO_WILLCHANGE(hc, oldKey); + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[match], ~hc->_marker); +#if CFDictionary + any_t oldValue = hc->_values[match]; + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, hc->_values[match], 0); +#endif +#if CFBag + hc->_values[match] = 0; +#endif + hc->_count--; + hc->_bucketsUsed--; + hc->_deletes++; + CF_OBJC_KVO_DIDCHANGE(hc, oldKey); + RELEASEKEY(oldKey); +#if CFDictionary + RELEASEVALUE(oldValue); +#endif + if (__CFBagShouldShrink(hc)) { + __CFBagGrow(hc, 0); + } else { + // When the probeskip == 1 always and only, a DELETED slot followed by an EMPTY slot + // can be converted to an EMPTY slot. By extension, a chain of DELETED slots followed + // by an EMPTY slot can be converted to EMPTY slots, which is what we do here. + if (match < hc->_bucketsNum - 1 && hc->_keys[match + 1] == hc->_marker) { + while (0 <= match && hc->_keys[match] == ~hc->_marker) { + hc->_keys[match] = hc->_marker; + hc->_deletes--; + match--; + } + } + } + } +} + +void CFBagRemoveAllValues(CFMutableHashRef hc) { + if (CFDictionary) CF_OBJC_FUNCDISPATCH0(__kCFHashTypeID, void, hc, "removeAllObjects"); + if (CFSet) CF_OBJC_FUNCDISPATCH0(__kCFHashTypeID, void, hc, "removeAllObjects"); + __CFGenericValidateType(hc, __kCFHashTypeID); + switch (__CFHashGetType(hc)) { + case __kCFHashMutable: + break; + default: + CFAssert2(__CFHashGetType(hc) != __kCFHashImmutable, __kCFLogAssertion, "%s(): immutable collection %p passed to mutating operation", __PRETTY_FUNCTION__, hc); + break; + } + hc->_mutations++; + if (0 == hc->_bucketsUsed) return; + CFAllocatorRef allocator = __CFGetAllocator(hc); + any_t *keys = hc->_keys; + for (CFIndex idx = 0, nbuckets = hc->_bucketsNum; idx < nbuckets; idx++) { + if (__CFHashKeyIsValue(hc, keys[idx])) { + any_t oldKey = keys[idx]; + CF_OBJC_KVO_WILLCHANGE(hc, oldKey); +#if CFDictionary || CFSet + hc->_count--; +#endif +#if CFBag + hc->_count -= (CFIndex)hc->_values[idx]; +#endif + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[idx], ~hc->_marker); +#if CFDictionary + any_t oldValue = hc->_values[idx]; + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, hc->_values[idx], 0); +#endif +#if CFBag + hc->_values[idx] = 0; +#endif + hc->_bucketsUsed--; + hc->_deletes++; + CF_OBJC_KVO_DIDCHANGE(hc, oldKey); + RELEASEKEY(oldKey); +#if CFDictionary + RELEASEVALUE(oldValue); +#endif + } + } + for (CFIndex idx = 0, nbuckets = hc->_bucketsNum; idx < nbuckets; idx++) { + keys[idx] = hc->_marker; + } + hc->_deletes = 0; + hc->_bucketsUsed = 0; + hc->_count = 0; + if (__CFBagShouldShrink(hc) && (256 <= hc->_bucketsCap)) { + __CFBagGrow(hc, 128); + } +} + +#undef CF_OBJC_KVO_WILLCHANGE +#undef CF_OBJC_KVO_DIDCHANGE +
diff --git a/CoreFoundation/CFBag.h b/CoreFoundation/CFBag.h new file mode 100644 index 0000000..e23cf84 --- /dev/null +++ b/CoreFoundation/CFBag.h
@@ -0,0 +1,122 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFBag.h + Copyright (c) 1998-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFBAG__) +#define __COREFOUNDATION_CFBAG__ 1 + +#include <CoreFoundation/CFBase.h> + +CF_EXTERN_C_BEGIN + +typedef const void * (*CFBagRetainCallBack)(CFAllocatorRef allocator, const void *value); +typedef void (*CFBagReleaseCallBack)(CFAllocatorRef allocator, const void *value); +typedef CFStringRef (*CFBagCopyDescriptionCallBack)(const void *value); +typedef Boolean (*CFBagEqualCallBack)(const void *value1, const void *value2); +typedef CFHashCode (*CFBagHashCallBack)(const void *value); +typedef struct { + CFIndex version; + CFBagRetainCallBack retain; + CFBagReleaseCallBack release; + CFBagCopyDescriptionCallBack copyDescription; + CFBagEqualCallBack equal; + CFBagHashCallBack hash; +} CFBagCallBacks; + +CF_EXPORT +const CFBagCallBacks kCFTypeBagCallBacks; +CF_EXPORT +const CFBagCallBacks kCFCopyStringBagCallBacks; + +typedef void (*CFBagApplierFunction)(const void *value, void *context); + +typedef const struct __CFBag * CFBagRef; +typedef struct __CFBag * CFMutableBagRef; + +CF_EXPORT +CFTypeID CFBagGetTypeID(void); + +CF_EXPORT +CFBagRef CFBagCreate(CFAllocatorRef allocator, const void **values, CFIndex numValues, const CFBagCallBacks *callBacks); + +CF_EXPORT +CFBagRef CFBagCreateCopy(CFAllocatorRef allocator, CFBagRef theBag); + +CF_EXPORT +CFMutableBagRef CFBagCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFBagCallBacks *callBacks); + +CF_EXPORT +CFMutableBagRef CFBagCreateMutableCopy(CFAllocatorRef allocator, CFIndex capacity, CFBagRef theBag); + +CF_EXPORT +CFIndex CFBagGetCount(CFBagRef theBag); + +CF_EXPORT +CFIndex CFBagGetCountOfValue(CFBagRef theBag, const void *value); + +CF_EXPORT +Boolean CFBagContainsValue(CFBagRef theBag, const void *value); + +CF_EXPORT +const void *CFBagGetValue(CFBagRef theBag, const void *value); + +CF_EXPORT +Boolean CFBagGetValueIfPresent(CFBagRef theBag, const void *candidate, const void **value); + +CF_EXPORT +void CFBagGetValues(CFBagRef theBag, const void **values); + +CF_EXPORT +void CFBagApplyFunction(CFBagRef theBag, CFBagApplierFunction applier, void *context); + +CF_EXPORT +void CFBagAddValue(CFMutableBagRef theBag, const void *value); + +CF_EXPORT +void CFBagReplaceValue(CFMutableBagRef theBag, const void *value); + +CF_EXPORT +void CFBagSetValue(CFMutableBagRef theBag, const void *value); + +CF_EXPORT +void CFBagRemoveValue(CFMutableBagRef theBag, const void *value); + +CF_EXPORT +void CFBagRemoveAllValues(CFMutableBagRef theBag); + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFBAG__ */ +
diff --git a/CoreFoundation/CFBase.c b/CoreFoundation/CFBase.c new file mode 100644 index 0000000..fcf42f6 --- /dev/null +++ b/CoreFoundation/CFBase.c
@@ -0,0 +1,1110 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. All rights reserved. + * Copyright (c) 2009 Grant Erickson <gerickson@nuovations.com>. All rights reserved. + * + * This source code is a modified version of the CoreFoundation sources released by Apple Inc. under + * the terms of the APSL version 2.0 (see below). + * + * For information about changes from the original Apple source release can be found by reviewing the + * source control system for the project at https://sourceforge.net/svn/?group_id=246198. + * + * The original license information is as follows: + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFBase.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include <CoreFoundation/CFBase.h> +#include "CFInternal.h" +#if DEPLOYMENT_TARGET_MACOSX +#include <pthread.h> +#include <malloc/malloc.h> +#include <mach/mach.h> +#include <dlfcn.h> +#elif DEPLOYMENT_TARGET_LINUX +#if defined(DEBUG) +#include <mcheck.h> +#endif +#include <pthread.h> +#elif DEPLOYMENT_TARGET_WINDOWS +#include <windows.h> +#endif +#include <stdlib.h> +#include <string.h> + +extern size_t malloc_good_size(size_t size); + +#if defined(__CYGWIN32__) || defined (D__CYGWIN_) +#error CoreFoundation is currently built with the Microsoft C Runtime, which is incompatible with the Cygwin DLL. You must either use the -mno-cygwin flag, or complete a port of CF to the Cygwin environment. +#endif + +// -------- -------- -------- -------- -------- -------- -------- -------- + +// CFAllocator structure must match struct _malloc_zone_t! +// The first two reserved fields in struct _malloc_zone_t are for us with CFRuntimeBase +struct __CFAllocator { + CFRuntimeBase _base; +#if DEPLOYMENT_TARGET_MACOSX + size_t (*size)(struct _malloc_zone_t *zone, const void *ptr); /* returns the size of a block or 0 if not in this zone; must be fast, especially for negative answers */ + void *(*malloc)(struct _malloc_zone_t *zone, size_t size); + void *(*calloc)(struct _malloc_zone_t *zone, size_t num_items, size_t size); /* same as malloc, but block returned is set to zero */ + void *(*valloc)(struct _malloc_zone_t *zone, size_t size); /* same as malloc, but block returned is set to zero and is guaranteed to be page aligned */ + void (*free)(struct _malloc_zone_t *zone, void *ptr); + void *(*realloc)(struct _malloc_zone_t *zone, void *ptr, size_t size); + void (*destroy)(struct _malloc_zone_t *zone); /* zone is destroyed and all memory reclaimed */ + const char *zone_name; + unsigned (*batch_malloc)(struct _malloc_zone_t *zone, size_t size, void **results, unsigned num_requested); /* given a size, returns pointers capable of holding that size; returns the number of pointers allocated (maybe 0 or less than num_requested) */ + void (*batch_free)(struct _malloc_zone_t *zone, void **to_be_freed, unsigned num_to_be_freed); /* frees all the pointers in to_be_freed; note that to_be_freed may be overwritten during the process */ + struct malloc_introspection_t *introspect; + void *reserved5; +#endif + CFAllocatorRef _allocator; + CFAllocatorContext _context; +}; + +CF_INLINE CFAllocatorRetainCallBack __CFAllocatorGetRetainFunction(const CFAllocatorContext *context) { + CFAllocatorRetainCallBack retval = NULL; + retval = context->retain; + return retval; +} + +CF_INLINE CFAllocatorReleaseCallBack __CFAllocatorGetReleaseFunction(const CFAllocatorContext *context) { + CFAllocatorReleaseCallBack retval = NULL; + retval = context->release; + return retval; +} + +CF_INLINE CFAllocatorCopyDescriptionCallBack __CFAllocatorGetCopyDescriptionFunction(const CFAllocatorContext *context) { + CFAllocatorCopyDescriptionCallBack retval = NULL; + retval = context->copyDescription; + return retval; +} + +CF_INLINE CFAllocatorAllocateCallBack __CFAllocatorGetAllocateFunction(const CFAllocatorContext *context) { + CFAllocatorAllocateCallBack retval = NULL; + retval = context->allocate; + return retval; +} + +CF_INLINE CFAllocatorReallocateCallBack __CFAllocatorGetReallocateFunction(const CFAllocatorContext *context) { + CFAllocatorReallocateCallBack retval = NULL; + retval = context->reallocate; + return retval; +} + +CF_INLINE CFAllocatorDeallocateCallBack __CFAllocatorGetDeallocateFunction(const CFAllocatorContext *context) { + CFAllocatorDeallocateCallBack retval = NULL; + retval = context->deallocate; + return retval; +} + +CF_INLINE CFAllocatorPreferredSizeCallBack __CFAllocatorGetPreferredSizeFunction(const CFAllocatorContext *context) { + CFAllocatorPreferredSizeCallBack retval = NULL; + retval = context->preferredSize; + return retval; +} + +#if DEPLOYMENT_TARGET_MACOSX + +__private_extern__ void __CFAllocatorDeallocate(CFTypeRef cf); + +static kern_return_t __CFAllocatorZoneIntrospectNoOp(void) { + return 0; +} + +static boolean_t __CFAllocatorZoneIntrospectTrue(void) { + return 1; +} + +static size_t __CFAllocatorCustomSize(malloc_zone_t *zone, const void *ptr) { + return 0; + + // The only way to implement this with a version 0 allocator would be + // for CFAllocator to keep track of all blocks allocated itself, which + // could be done, but would be bad for performance, so we don't do it. + // size_t (*size)(struct _malloc_zone_t *zone, const void *ptr); + /* returns the size of a block or 0 if not in this zone; + * must be fast, especially for negative answers */ +} + +static void *__CFAllocatorCustomMalloc(malloc_zone_t *zone, size_t size) { + CFAllocatorRef allocator = (CFAllocatorRef)zone; + return CFAllocatorAllocate(allocator, size, 0); +} + +static void *__CFAllocatorCustomCalloc(malloc_zone_t *zone, size_t num_items, size_t size) { + CFAllocatorRef allocator = (CFAllocatorRef)zone; + void *newptr = CFAllocatorAllocate(allocator, size, 0); + if (newptr) memset(newptr, 0, size); + return newptr; +} + +static void *__CFAllocatorCustomValloc(malloc_zone_t *zone, size_t size) { + CFAllocatorRef allocator = (CFAllocatorRef)zone; + void *newptr = CFAllocatorAllocate(allocator, size + vm_page_size, 0); + newptr = (void *)round_page((uintptr_t)newptr); + return newptr; +} + +static void __CFAllocatorCustomFree(malloc_zone_t *zone, void *ptr) { + CFAllocatorRef allocator = (CFAllocatorRef)zone; + CFAllocatorDeallocate(allocator, ptr); +} + +static void *__CFAllocatorCustomRealloc(malloc_zone_t *zone, void *ptr, size_t size) { + CFAllocatorRef allocator = (CFAllocatorRef)zone; + return CFAllocatorReallocate(allocator, ptr, size, 0); +} + +static void __CFAllocatorCustomDestroy(malloc_zone_t *zone) { + CFAllocatorRef allocator = (CFAllocatorRef)zone; + // !!! we do it, and caller of malloc_destroy_zone() assumes + // COMPLETE responsibility for the result; NO Apple library + // code should be modified as a result of discovering that + // some activity results in inconveniences to developers + // trying to use malloc_destroy_zone() with a CFAllocatorRef; + // that's just too bad for them. + __CFAllocatorDeallocate(allocator); +} + +static size_t __CFAllocatorCustomGoodSize(malloc_zone_t *zone, size_t size) { + CFAllocatorRef allocator = (CFAllocatorRef)zone; + return CFAllocatorGetPreferredSizeForSize(allocator, size, 0); +} + +// These should live in the malloc.h heaer file +typedef double (*Fx)(double); +typedef kern_return_t (*mit_EnumType)(task_t task, void *, unsigned type_mask, vm_address_t zone_address, memory_reader_t reader, vm_range_recorder_t recorder); +typedef size_t (*mit_GoodSize)(malloc_zone_t *zone, size_t size); +typedef boolean_t (*mit_Check)(malloc_zone_t *zone); +typedef void (*mit_Print)(malloc_zone_t *zone, boolean_t verbose); +typedef void (*mit_Log)(malloc_zone_t *zone, void *address); +typedef void (*mit_ZoneAction)(malloc_zone_t *zone); +typedef void (*mit_Statistics)(malloc_zone_t *zone, malloc_statistics_t *stats); + +static struct malloc_introspection_t __CFAllocatorZoneIntrospect = { + (mit_EnumType)__CFAllocatorZoneIntrospectNoOp, + (mit_GoodSize)__CFAllocatorCustomGoodSize, + (mit_Check)__CFAllocatorZoneIntrospectTrue, + (mit_Print)__CFAllocatorZoneIntrospectNoOp, + (mit_Log)__CFAllocatorZoneIntrospectNoOp, + (mit_ZoneAction)__CFAllocatorZoneIntrospectNoOp, + (mit_ZoneAction)__CFAllocatorZoneIntrospectNoOp, + (mit_Statistics)__CFAllocatorZoneIntrospectNoOp +}; + +static size_t __CFAllocatorNullSize(malloc_zone_t *zone, const void *ptr) { + return 0; +} + +static void * __CFAllocatorNullMalloc(malloc_zone_t *zone, size_t size) { + return NULL; +} + +static void * __CFAllocatorNullCalloc(malloc_zone_t *zone, size_t num_items, size_t size) { + return NULL; +} + +static void * __CFAllocatorNullValloc(malloc_zone_t *zone, size_t size) { + return NULL; +} + +static void __CFAllocatorNullFree(malloc_zone_t *zone, void *ptr) { +} + +static void * __CFAllocatorNullRealloc(malloc_zone_t *zone, void *ptr, size_t size) { + return NULL; +} + +static void __CFAllocatorNullDestroy(malloc_zone_t *zone) { +} + +static size_t __CFAllocatorNullGoodSize(malloc_zone_t *zone, size_t size) { + return size; +} + +static struct malloc_introspection_t __CFAllocatorNullZoneIntrospect = { + (mit_EnumType)__CFAllocatorZoneIntrospectNoOp, + (mit_GoodSize)__CFAllocatorNullGoodSize, + (mit_Check)__CFAllocatorZoneIntrospectTrue, + (mit_Print)__CFAllocatorZoneIntrospectNoOp, + (mit_Log)__CFAllocatorZoneIntrospectNoOp, + (mit_ZoneAction)__CFAllocatorZoneIntrospectNoOp, + (mit_ZoneAction)__CFAllocatorZoneIntrospectNoOp, + (mit_Statistics)__CFAllocatorZoneIntrospectNoOp +}; + +static void *__CFAllocatorSystemAllocate(CFIndex size, CFOptionFlags hint, void *info) { + return malloc_zone_malloc((malloc_zone_t*)info, size); +} + +static void *__CFAllocatorSystemReallocate(void *ptr, CFIndex newsize, CFOptionFlags hint, void *info) { + return malloc_zone_realloc((malloc_zone_t*)info, ptr, newsize); +} + +static void __CFAllocatorSystemDeallocate(void *ptr, void *info) { +#if defined(DEBUG) + size_t size = malloc_size(ptr); + if (size) memset(ptr, 0xCC, size); +#endif + malloc_zone_free((malloc_zone_t*)info, ptr); +} + +#endif /* DEPLOYMENT_TARGET_MACOSX */ + +#if DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD +static void *__CFAllocatorSystemAllocate(CFIndex size, CFOptionFlags hint, void *info) { + return malloc(size); +} + +static void *__CFAllocatorSystemReallocate(void *ptr, CFIndex newsize, CFOptionFlags hint, void *info) { + return realloc(ptr, newsize); +} + +static void __CFAllocatorSystemDeallocate(void *ptr, void *info) { +#if defined(DEBUG) + const enum mcheck_status status = mprobe(ptr); + + CFAssert3(status == MCHECK_OK || status == MCHECK_DISABLED, + __kCFLogAssertion, "%s: ptr %p status %d", + __PRETTY_FUNCTION__, ptr, status); +#endif + + free(ptr); +} +#endif + +static void *__CFAllocatorNullAllocate(CFIndex size, CFOptionFlags hint, void *info) { + return NULL; +} + +static void *__CFAllocatorNullReallocate(void *ptr, CFIndex newsize, CFOptionFlags hint, void *info) { + return NULL; +} + +#if defined (__cplusplus) +static void * __CFAllocatorCPPMalloc(CFIndex allocSize, CFOptionFlags hint, void *info) +{ + return malloc(allocSize); +} +static void * __CFAllocatorCPPReAlloc(void *ptr, CFIndex newsize, CFOptionFlags hint, void *info) +{ + return realloc(ptr, newsize); +} +static void __CFAllocatorCPPFree(void *ptr, void *info) +{ + free(ptr); +} +#endif // C++ + + +static struct __CFAllocator __kCFAllocatorMalloc = { + INIT_CFRUNTIME_BASE(), +#if DEPLOYMENT_TARGET_MACOSX + __CFAllocatorCustomSize, + __CFAllocatorCustomMalloc, + __CFAllocatorCustomCalloc, + __CFAllocatorCustomValloc, + __CFAllocatorCustomFree, + __CFAllocatorCustomRealloc, + __CFAllocatorNullDestroy, + "kCFAllocatorMalloc", + NULL, + NULL, + &__CFAllocatorZoneIntrospect, + NULL, +#endif + NULL, // _allocator + // Using the malloc functions directly is a total cheat, but works (in C) + // because the function signatures match in their common prefix of arguments. + // This saves us one hop through an adaptor function. +#if !defined (__cplusplus) + {0, NULL, NULL, NULL, NULL, (void *)malloc, (void *)realloc, (void *)free, NULL} +#else + {0, NULL, NULL, NULL, NULL, __CFAllocatorCPPMalloc,__CFAllocatorCPPReAlloc, __CFAllocatorCPPFree, NULL} +#endif // __cplusplus +}; + +static struct __CFAllocator __kCFAllocatorMallocZone = { + INIT_CFRUNTIME_BASE(), +#if DEPLOYMENT_TARGET_MACOSX + __CFAllocatorCustomSize, + __CFAllocatorCustomMalloc, + __CFAllocatorCustomCalloc, + __CFAllocatorCustomValloc, + __CFAllocatorCustomFree, + __CFAllocatorCustomRealloc, + __CFAllocatorNullDestroy, + "kCFAllocatorMallocZone", + NULL, + NULL, + &__CFAllocatorZoneIntrospect, + NULL, +#endif + NULL, // _allocator + {0, NULL, NULL, NULL, NULL, __CFAllocatorSystemAllocate, __CFAllocatorSystemReallocate, __CFAllocatorSystemDeallocate, NULL} +}; + +static struct __CFAllocator __kCFAllocatorSystemDefault = { + INIT_CFRUNTIME_BASE(), +#if DEPLOYMENT_TARGET_MACOSX + __CFAllocatorCustomSize, + __CFAllocatorCustomMalloc, + __CFAllocatorCustomCalloc, + __CFAllocatorCustomValloc, + __CFAllocatorCustomFree, + __CFAllocatorCustomRealloc, + __CFAllocatorNullDestroy, + "kCFAllocatorSystemDefault", + NULL, + NULL, + &__CFAllocatorZoneIntrospect, + NULL, +#endif + NULL, // _allocator + {0, NULL, NULL, NULL, NULL, __CFAllocatorSystemAllocate, __CFAllocatorSystemReallocate, __CFAllocatorSystemDeallocate, NULL} +}; + +static struct __CFAllocator __kCFAllocatorNull = { + INIT_CFRUNTIME_BASE(), +#if DEPLOYMENT_TARGET_MACOSX + __CFAllocatorNullSize, + __CFAllocatorNullMalloc, + __CFAllocatorNullCalloc, + __CFAllocatorNullValloc, + __CFAllocatorNullFree, + __CFAllocatorNullRealloc, + __CFAllocatorNullDestroy, + "kCFAllocatorNull", + NULL, + NULL, + &__CFAllocatorNullZoneIntrospect, + NULL, +#endif + NULL, // _allocator + {0, NULL, NULL, NULL, NULL, __CFAllocatorNullAllocate, __CFAllocatorNullReallocate, NULL, NULL} +}; + +const CFAllocatorRef kCFAllocatorDefault = NULL; +const CFAllocatorRef kCFAllocatorSystemDefault = &__kCFAllocatorSystemDefault; +const CFAllocatorRef kCFAllocatorMalloc = &__kCFAllocatorMalloc; +const CFAllocatorRef kCFAllocatorMallocZone = &__kCFAllocatorMallocZone; +const CFAllocatorRef kCFAllocatorNull = &__kCFAllocatorNull; +const CFAllocatorRef kCFAllocatorUseContext = (CFAllocatorRef)0x0257; + +static CFStringRef __CFAllocatorCopyDescription(CFTypeRef cf) { + CFAllocatorRef self = (CFAllocatorRef)cf; + CFAllocatorRef allocator = (kCFAllocatorUseContext == self->_allocator) ? self : self->_allocator; + return CFStringCreateWithFormat(allocator, NULL, CFSTR("<CFAllocator %p [%p]>{info = %p}"), cf, allocator, self->_context.info); +// CF: should use copyDescription function here to describe info field +// remember to release value returned from copydescr function when this happens +} + +__private_extern__ CFAllocatorRef __CFAllocatorGetAllocator(CFTypeRef cf) { + CFAllocatorRef allocator = (CFAllocatorRef)cf; + return (kCFAllocatorUseContext == allocator->_allocator) ? allocator : allocator->_allocator; +} + +__private_extern__ void __CFAllocatorDeallocate(CFTypeRef cf) { + CFAllocatorRef self = (CFAllocatorRef)cf; + CFAllocatorRef allocator = self->_allocator; + CFAllocatorReleaseCallBack releaseFunc = __CFAllocatorGetReleaseFunction(&self->_context); + if (kCFAllocatorUseContext == allocator) { + /* Rather a chicken and egg problem here, so we do things + in the reverse order from what was done at create time. */ + CFAllocatorDeallocateCallBack deallocateFunc = __CFAllocatorGetDeallocateFunction(&self->_context); + void *info = self->_context.info; + if (NULL != deallocateFunc) { + INVOKE_CALLBACK2(deallocateFunc, (void *)self, info); + } + if (NULL != releaseFunc) { + INVOKE_CALLBACK1(releaseFunc, info); + } + } else { + if (NULL != releaseFunc) { + INVOKE_CALLBACK1(releaseFunc, self->_context.info); + } + _CFAllocatorDeallocateGC(allocator, (void *)self); + } +} + +static CFTypeID __kCFAllocatorTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFAllocatorClass = { + 0, + "CFAllocator", + NULL, // init + NULL, // copy + __CFAllocatorDeallocate, + NULL, // equal + NULL, // hash + NULL, // + __CFAllocatorCopyDescription +}; + +__private_extern__ void __CFAllocatorInitialize(void) { + __kCFAllocatorTypeID = _CFRuntimeRegisterClass(&__CFAllocatorClass); + + _CFRuntimeSetInstanceTypeID(&__kCFAllocatorSystemDefault, __kCFAllocatorTypeID); + __kCFAllocatorSystemDefault._base._cfisa = __CFISAForTypeID(__kCFAllocatorTypeID); +#if DEPLOYMENT_TARGET_MACOSX + __kCFAllocatorSystemDefault._context.info = (CF_USING_COLLECTABLE_MEMORY ? __CFCollectableZone : malloc_default_zone()); + memset(malloc_default_zone(), 0, 2 * sizeof(void *)); +#endif + __kCFAllocatorSystemDefault._allocator = kCFAllocatorSystemDefault; +#ifdef DEPLOYMENT_TARGET_WINDOWS + __kCFAllocatorSystemDefault._context.allocate = __CFAllocatorSystemAllocate; + __kCFAllocatorSystemDefault._context.reallocate = __CFAllocatorSystemReallocate; + __kCFAllocatorSystemDefault._context.deallocate = __CFAllocatorSystemDeallocate; +#endif // DEPLOYMENT_TARGET_WINDOWS + + _CFRuntimeSetInstanceTypeID(&__kCFAllocatorMalloc, __kCFAllocatorTypeID); + __kCFAllocatorMalloc._base._cfisa = __CFISAForTypeID(__kCFAllocatorTypeID); + __kCFAllocatorMalloc._allocator = kCFAllocatorSystemDefault; + +#if DEPLOYMENT_TARGET_MACOSX + _CFRuntimeSetInstanceTypeID(&__kCFAllocatorMallocZone, __kCFAllocatorTypeID); + __kCFAllocatorMallocZone._base._cfisa = __CFISAForTypeID(__kCFAllocatorTypeID); + __kCFAllocatorMallocZone._allocator = kCFAllocatorSystemDefault; + __kCFAllocatorMallocZone._context.info = malloc_default_zone(); +#endif //__MACH__ + + _CFRuntimeSetInstanceTypeID(&__kCFAllocatorNull, __kCFAllocatorTypeID); + __kCFAllocatorNull._base._cfisa = __CFISAForTypeID(__kCFAllocatorTypeID); + __kCFAllocatorNull._allocator = kCFAllocatorSystemDefault; + +} + +CFTypeID CFAllocatorGetTypeID(void) { + return __kCFAllocatorTypeID; +} + +CFAllocatorRef CFAllocatorGetDefault(void) { + CFAllocatorRef allocator = (CFAllocatorRef)__CFGetThreadSpecificData_inline()->_allocator; + if (NULL == allocator) { + allocator = kCFAllocatorSystemDefault; + } + return allocator; +} + +void CFAllocatorSetDefault(CFAllocatorRef allocator) { + CFAllocatorRef current = (CFAllocatorRef)__CFGetThreadSpecificData_inline()->_allocator; +#if defined(DEBUG) + if (NULL != allocator) { + __CFGenericValidateType(allocator, __kCFAllocatorTypeID); + } +#endif +#if DEPLOYMENT_TARGET_MACOSX + if (allocator && allocator->_base._cfisa != __CFISAForTypeID(__kCFAllocatorTypeID)) { // malloc_zone_t * + return; // require allocator to this function to be an allocator + } +#endif + if (NULL != allocator && allocator != current) { + if (current) CFRelease(current); + CFRetain(allocator); + // We retain an extra time so that anything set as the default + // allocator never goes away. + CFRetain(allocator); + __CFGetThreadSpecificData_inline()->_allocator = (void *)allocator; + } +} + +static CFAllocatorRef __CFAllocatorCreate(CFAllocatorRef allocator, CFAllocatorContext *context) { + struct __CFAllocator *memory = NULL; + CFAllocatorRetainCallBack retainFunc; + CFAllocatorAllocateCallBack allocateFunc; + void *retainedInfo; +#if DEPLOYMENT_TARGET_MACOSX + if (allocator && kCFAllocatorUseContext != allocator && allocator->_base._cfisa != __CFISAForTypeID(__kCFAllocatorTypeID)) { // malloc_zone_t * + return NULL; // require allocator to this function to be an allocator + } +#endif + retainFunc = context->retain; + FAULT_CALLBACK((void **)&retainFunc); + allocateFunc = context->allocate; + FAULT_CALLBACK((void **)&allocateFunc); + if (NULL != retainFunc) { + retainedInfo = (void *)INVOKE_CALLBACK1(retainFunc, context->info); + } else { + retainedInfo = context->info; + } + // We don't use _CFRuntimeCreateInstance() + if (kCFAllocatorUseContext == allocator) { + memory = NULL; + if (allocateFunc) { + memory = (struct __CFAllocator *)INVOKE_CALLBACK3(allocateFunc, sizeof(struct __CFAllocator), 0, retainedInfo); + } + if (NULL == memory) { + return NULL; + } + } else { + allocator = (NULL == allocator) ? __CFGetDefaultAllocator() : allocator; + memory = (struct __CFAllocator *)CFAllocatorAllocate(allocator, sizeof(struct __CFAllocator), __kCFAllocatorGCObjectMemory); + if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFAllocator"); + if (NULL == memory) { + return NULL; + } + } + memory->_base._cfisa = 0; +#if __LP64__ + memory->_base._rc = 1; +#else + memory->_base._cfinfo[CF_RC_BITS] = 1; +#endif + memory->_base._cfinfo[CF_INFO_BITS] = 0; + _CFRuntimeSetInstanceTypeID(memory, __kCFAllocatorTypeID); + memory->_base._cfisa = __CFISAForTypeID(__kCFAllocatorTypeID); +#if DEPLOYMENT_TARGET_MACOSX + memory->size = __CFAllocatorCustomSize; + memory->malloc = __CFAllocatorCustomMalloc; + memory->calloc = __CFAllocatorCustomCalloc; + memory->valloc = __CFAllocatorCustomValloc; + memory->free = __CFAllocatorCustomFree; + memory->realloc = __CFAllocatorCustomRealloc; + memory->destroy = __CFAllocatorCustomDestroy; + memory->zone_name = "Custom CFAllocator"; + memory->batch_malloc = NULL; + memory->batch_free = NULL; + memory->introspect = &__CFAllocatorZoneIntrospect; + memory->reserved5 = NULL; +#endif + memory->_allocator = allocator; + memory->_context.version = context->version; + memory->_context.info = retainedInfo; + memory->_context.retain = retainFunc; + memory->_context.release = context->release; + FAULT_CALLBACK((void **)&(memory->_context.release)); + memory->_context.copyDescription = context->copyDescription; + FAULT_CALLBACK((void **)&(memory->_context.copyDescription)); + memory->_context.allocate = allocateFunc; + memory->_context.reallocate = context->reallocate; + FAULT_CALLBACK((void **)&(memory->_context.reallocate)); + memory->_context.deallocate = context->deallocate; + FAULT_CALLBACK((void **)&(memory->_context.deallocate)); + memory->_context.preferredSize = context->preferredSize; + FAULT_CALLBACK((void **)&(memory->_context.preferredSize)); + + return memory; +} + +CFAllocatorRef CFAllocatorCreate(CFAllocatorRef allocator, CFAllocatorContext *context) { + CFAssert1(!CF_USING_COLLECTABLE_MEMORY, __kCFLogAssertion, "%s(): Shouldn't be called when GC is enabled!", __PRETTY_FUNCTION__); +#if defined(DEBUG) + if (CF_USING_COLLECTABLE_MEMORY) + HALT; +#endif + return __CFAllocatorCreate(allocator, context); +} + +CFAllocatorRef _CFAllocatorCreateGC(CFAllocatorRef allocator, CFAllocatorContext *context) { + return __CFAllocatorCreate(allocator, context); +} + +void *CFAllocatorAllocate(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint) { + CFAllocatorAllocateCallBack allocateFunc; + void *newptr = NULL; + allocator = (NULL == allocator) ? __CFGetDefaultAllocator() : allocator; +#if (DEPLOYMENT_TARGET_MACOSX) && defined(DEBUG) + if (allocator->_base._cfisa == __CFISAForTypeID(__kCFAllocatorTypeID)) { + __CFGenericValidateType(allocator, __kCFAllocatorTypeID); + } +#else + __CFGenericValidateType(allocator, __kCFAllocatorTypeID); +#endif + if (0 == size) return NULL; +#if DEPLOYMENT_TARGET_MACOSX + if (allocator->_base._cfisa != __CFISAForTypeID(__kCFAllocatorTypeID)) { // malloc_zone_t * + return malloc_zone_malloc((malloc_zone_t *)allocator, size); + } +#endif + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + newptr = auto_zone_allocate_object((auto_zone_t*)allocator->_context.info, size, CF_GET_GC_MEMORY_TYPE(hint), true, false); + } else { + newptr = NULL; + allocateFunc = __CFAllocatorGetAllocateFunction(&allocator->_context); + if (allocateFunc) { + newptr = (void *)INVOKE_CALLBACK3(allocateFunc, size, hint, allocator->_context.info); + } + } + return newptr; +} + +void *CFAllocatorReallocate(CFAllocatorRef allocator, void *ptr, CFIndex newsize, CFOptionFlags hint) { + CFAllocatorAllocateCallBack allocateFunc; + CFAllocatorReallocateCallBack reallocateFunc; + CFAllocatorDeallocateCallBack deallocateFunc; + void *newptr; + allocator = (NULL == allocator) ? __CFGetDefaultAllocator() : allocator; +#if (DEPLOYMENT_TARGET_MACOSX) && defined(DEBUG) + if (allocator->_base._cfisa == __CFISAForTypeID(__kCFAllocatorTypeID)) { + __CFGenericValidateType(allocator, __kCFAllocatorTypeID); + } +#else + __CFGenericValidateType(allocator, __kCFAllocatorTypeID); +#endif + if (NULL == ptr && 0 < newsize) { +#if DEPLOYMENT_TARGET_MACOSX + if (allocator->_base._cfisa != __CFISAForTypeID(__kCFAllocatorTypeID)) { // malloc_zone_t * + return malloc_zone_malloc((malloc_zone_t *)allocator, newsize); + } +#endif + newptr = NULL; + allocateFunc = __CFAllocatorGetAllocateFunction(&allocator->_context); + if (allocateFunc) { + newptr = (void *)INVOKE_CALLBACK3(allocateFunc, newsize, hint, allocator->_context.info); + } + return newptr; + } + if (NULL != ptr && 0 == newsize) { +#if DEPLOYMENT_TARGET_MACOSX + if (allocator->_base._cfisa != __CFISAForTypeID(__kCFAllocatorTypeID)) { // malloc_zone_t * +#if defined(DEBUG) + size_t size = malloc_size(ptr); + if (size) memset(ptr, 0xCC, size); +#endif + malloc_zone_free((malloc_zone_t *)allocator, ptr); + return NULL; + } +#endif /* DEPLOYMENT_TARGET_MACOSX */ + deallocateFunc = __CFAllocatorGetDeallocateFunction(&allocator->_context); + if (NULL != deallocateFunc) { + INVOKE_CALLBACK2(deallocateFunc, ptr, allocator->_context.info); + } + return NULL; + } + if (NULL == ptr && 0 == newsize) return NULL; +#if DEPLOYMENT_TARGET_MACOSX + if (allocator->_base._cfisa != __CFISAForTypeID(__kCFAllocatorTypeID)) { // malloc_zone_t * + return malloc_zone_realloc((malloc_zone_t *)allocator, ptr, newsize); + } +#endif + reallocateFunc = __CFAllocatorGetReallocateFunction(&allocator->_context); + if (NULL == reallocateFunc) return NULL; + newptr = (void *)INVOKE_CALLBACK4(reallocateFunc, ptr, newsize, hint, allocator->_context.info); + return newptr; +} + +void CFAllocatorDeallocate(CFAllocatorRef allocator, void *ptr) { + CFAllocatorDeallocateCallBack deallocateFunc; + allocator = (NULL == allocator) ? __CFGetDefaultAllocator() : allocator; +#if (DEPLOYMENT_TARGET_MACOSX) && defined(DEBUG) + if (allocator->_base._cfisa == __CFISAForTypeID(__kCFAllocatorTypeID)) { + __CFGenericValidateType(allocator, __kCFAllocatorTypeID); + } +#else + __CFGenericValidateType(allocator, __kCFAllocatorTypeID); +#endif +#if DEPLOYMENT_TARGET_MACOSX + if (allocator->_base._cfisa != __CFISAForTypeID(__kCFAllocatorTypeID)) { // malloc_zone_t * +#if defined(DEBUG) + size_t size = malloc_size(ptr); + if (size) memset(ptr, 0xCC, size); +#endif + return malloc_zone_free((malloc_zone_t *)allocator, ptr); + } +#endif /* DEPLOYMENT_TARGET_MACOSX */ + deallocateFunc = __CFAllocatorGetDeallocateFunction(&allocator->_context); + if (NULL != ptr && NULL != deallocateFunc) { + INVOKE_CALLBACK2(deallocateFunc, ptr, allocator->_context.info); + } +} + +CFIndex CFAllocatorGetPreferredSizeForSize(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint) { + CFAllocatorPreferredSizeCallBack prefFunc; + CFIndex newsize = 0; + allocator = (NULL == allocator) ? __CFGetDefaultAllocator() : allocator; +#if (DEPLOYMENT_TARGET_MACOSX) && defined(DEBUG) + if (allocator->_base._cfisa == __CFISAForTypeID(__kCFAllocatorTypeID)) { + __CFGenericValidateType(allocator, __kCFAllocatorTypeID); + } +#else + __CFGenericValidateType(allocator, __kCFAllocatorTypeID); +#endif +#if DEPLOYMENT_TARGET_MACOSX + if (allocator->_base._cfisa != __CFISAForTypeID(__kCFAllocatorTypeID)) { // malloc_zone_t * + return malloc_good_size(size); + } +#endif + prefFunc = __CFAllocatorGetPreferredSizeFunction(&allocator->_context); + if (0 < size && NULL != prefFunc) { + newsize = (CFIndex)(INVOKE_CALLBACK3(prefFunc, size, hint, allocator->_context.info)); + } + if (newsize < size) newsize = size; + return newsize; +} + +void CFAllocatorGetContext(CFAllocatorRef allocator, CFAllocatorContext *context) { + allocator = (NULL == allocator) ? __CFGetDefaultAllocator() : allocator; +#if (DEPLOYMENT_TARGET_MACOSX) && defined(DEBUG) + if (allocator->_base._cfisa == __CFISAForTypeID(__kCFAllocatorTypeID)) { + __CFGenericValidateType(allocator, __kCFAllocatorTypeID); + } +#else + __CFGenericValidateType(allocator, __kCFAllocatorTypeID); +#endif + CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__); +#if DEPLOYMENT_TARGET_MACOSX + if (allocator->_base._cfisa != __CFISAForTypeID(__kCFAllocatorTypeID)) { // malloc_zone_t * + return; + } +#endif + context->version = 0; + context->info = allocator->_context.info; + context->retain = __CFAllocatorGetRetainFunction(&allocator->_context); + context->release = __CFAllocatorGetReleaseFunction(&allocator->_context); + context->copyDescription = __CFAllocatorGetCopyDescriptionFunction(&allocator->_context); + context->allocate = __CFAllocatorGetAllocateFunction(&allocator->_context); + context->reallocate = __CFAllocatorGetReallocateFunction(&allocator->_context); + context->deallocate = __CFAllocatorGetDeallocateFunction(&allocator->_context); + context->preferredSize = __CFAllocatorGetPreferredSizeFunction(&allocator->_context); +#if (DEPLOYMENT_TARGET_MACOSX) && defined(__ppc__) + context->retain = (void *)((uintptr_t)context->retain & ~0x3); + context->release = (void *)((uintptr_t)context->release & ~0x3); + context->copyDescription = (void *)((uintptr_t)context->copyDescription & ~0x3); + context->allocate = (void *)((uintptr_t)context->allocate & ~0x3); + context->reallocate = (void *)((uintptr_t)context->reallocate & ~0x3); + context->deallocate = (void *)((uintptr_t)context->deallocate & ~0x3); + context->preferredSize = (void *)((uintptr_t)context->preferredSize & ~0x3); +#endif +} + +void *_CFAllocatorAllocateGC(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint) +{ + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) + return auto_zone_allocate_object((auto_zone_t*)kCFAllocatorSystemDefault->_context.info, size, CF_GET_GC_MEMORY_TYPE(hint), false, false); + else + return CFAllocatorAllocate(allocator, size, hint); +} + +void *_CFAllocatorReallocateGC(CFAllocatorRef allocator, void *ptr, CFIndex newsize, CFOptionFlags hint) +{ + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + if (ptr && (newsize == 0)) { + return NULL; // equivalent to _CFAllocatorDeallocateGC. + } + if (ptr == NULL) { + return auto_zone_allocate_object((auto_zone_t*)kCFAllocatorSystemDefault->_context.info, newsize, CF_GET_GC_MEMORY_TYPE(hint), false, false); // eq. to _CFAllocator + } + } + // otherwise, auto_realloc() now preserves layout type and refCount. + return CFAllocatorReallocate(allocator, ptr, newsize, hint); +} + +void _CFAllocatorDeallocateGC(CFAllocatorRef allocator, void *ptr) +{ + // when running GC, don't deallocate. + if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator)) CFAllocatorDeallocate(allocator, ptr); +} + +// -------- -------- -------- -------- -------- -------- -------- -------- + +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD +__private_extern__ pthread_key_t __CFTSDKey = (pthread_key_t)NULL; +#elif DEPLOYMENT_TARGET_WINDOWS +__private_extern__ DWORD __CFTSDKey = 0xFFFFFFFF; +#endif + +extern void _CFRunLoop1(void); + +// Called for each thread as it exits +__private_extern__ void __CFFinalizeThreadData(void *arg) { +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD + __CFThreadSpecificData *tsd = (__CFThreadSpecificData *)arg; +#elif DEPLOYMENT_TARGET_WINDOWS + __CFThreadSpecificData *tsd = (__CFThreadSpecificData*)TlsGetValue(__CFTSDKey); + TlsSetValue(__CFTSDKey, NULL); +#endif + if (NULL == tsd) return; + if (tsd->_allocator) CFRelease(tsd->_allocator); +#if DEPLOYMENT_TARGET_MACOSX + _CFRunLoop1(); +#endif +#if DEPLOYMENT_TARGET_WINDOWS || 0 + + if (tsd->_messageHook) UnhookWindowsHookEx(tsd->_messageHook); + +#endif + + CFAllocatorDeallocate(kCFAllocatorSystemDefault, tsd); +} + +__private_extern__ __CFThreadSpecificData *__CFGetThreadSpecificData(void) { +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD + __CFThreadSpecificData *data; + data = (__CFThreadSpecificData*)pthread_getspecific(__CFTSDKey); + if (data) { + return data; + } + data =(__CFThreadSpecificData*)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(__CFThreadSpecificData), 0); + if (__CFOASafe) __CFSetLastAllocationEventName(data, "CFUtilities (thread-data)"); + memset(data, 0, sizeof(__CFThreadSpecificData)); + pthread_setspecific(__CFTSDKey, data); + return data; +#elif DEPLOYMENT_TARGET_WINDOWS + __CFThreadSpecificData *data; + data = (__CFThreadSpecificData *)TlsGetValue(__CFTSDKey); + if (data) { + return data; + } + data = (__CFThreadSpecificData *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(__CFThreadSpecificData), 0); + if (__CFOASafe) __CFSetLastAllocationEventName(data, "CFUtilities (thread-data)"); + memset(data, 0, sizeof(__CFThreadSpecificData)); + TlsSetValue(__CFTSDKey, data); + return data; +#endif +} + +__private_extern__ void __CFBaseInitialize(void) { +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD + pthread_key_create(&__CFTSDKey, __CFFinalizeThreadData); +#elif DEPLOYMENT_TARGET_WINDOWS || 0 + __CFTSDKey = TlsAlloc(); +#endif +} + +#if DEPLOYMENT_TARGET_WINDOWS || 0 +__private_extern__ void __CFBaseCleanup(void) { + TlsFree(__CFTSDKey); +} +#endif + + +static CFBadErrorCallBack __CFOutOfMemoryCallBack = NULL; + +CFBadErrorCallBack _CFGetOutOfMemoryErrorCallBack(void) { + return __CFOutOfMemoryCallBack; +} + +void _CFSetOutOfMemoryErrorCallBack(CFBadErrorCallBack callBack) { + __CFOutOfMemoryCallBack = callBack; +} + + +CFRange __CFRangeMake(CFIndex loc, CFIndex len) { + CFRange range; + range.location = loc; + range.length = len; + return range; +} + + +struct __CFNull { + CFRuntimeBase _base; +}; + +static struct __CFNull __kCFNull = { + INIT_CFRUNTIME_BASE() +}; +const CFNullRef kCFNull = &__kCFNull; + +static CFStringRef __CFNullCopyDescription(CFTypeRef cf) { + return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFNull %p [%p]>"), cf, CFGetAllocator(cf)); +} + +static CFStringRef __CFNullCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) { + return (CFStringRef)CFRetain(CFSTR("null")); +} + +static void __CFNullDeallocate(CFTypeRef cf) { + CFAssert(false, __kCFLogAssertion, "Deallocated CFNull!"); +} + +static CFTypeID __kCFNullTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFNullClass = { + 0, + "CFNull", + NULL, // init + NULL, // copy + __CFNullDeallocate, + NULL, + NULL, + __CFNullCopyFormattingDescription, + __CFNullCopyDescription +}; + +__private_extern__ void __CFNullInitialize(void) { + __kCFNullTypeID = _CFRuntimeRegisterClass(&__CFNullClass); + _CFRuntimeSetInstanceTypeID(&__kCFNull, __kCFNullTypeID); + __kCFNull._base._cfisa = __CFISAForTypeID(__kCFNullTypeID); +} + +CFTypeID CFNullGetTypeID(void) { + return __kCFNullTypeID; +} + +void CFCollection_non_gc_storage_error(void) { } + + +static int hasCFM = 0; + +void _CFRuntimeSetCFMPresent(void *addr) { + hasCFM = 1; +} + +#if (DEPLOYMENT_TARGET_MACOSX) && defined(__ppc__) + +/* See comments below */ +__private_extern__ void __CF_FAULT_CALLBACK(void **ptr) { + uintptr_t p = (uintptr_t)*ptr; + if ((0 == p) || (p & 0x1)) return; + if (0 == hasCFM || (0x90000000 <= p && p < 0xA0000000)) { + *ptr = (void *)(p | 0x1); + } else { + static CFMutableDictionaryRef cache = NULL; + static CFSpinLock_t lock = CFSpinLockInit; + uintptr_t known = ~0; + __CFSpinLock(&lock); + if (!cache || !CFDictionaryGetValueIfPresent(cache, (const void *)p, (const void **)&known)) { + if (!cache) { + cache = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, NULL); + } + Dl_info info; + known = dladdr((void *)p, &info); + CFDictionarySetValue(cache, (const void *)p, (const void *)known); + } + __CFSpinUnlock(&lock); + *ptr = (void *)(p | (known ? 0x1 : 0x3)); + } +} + +/* +Jump to callback function. r2 is not saved and restored +in the jump-to-CFM case, since we assume that dyld code +never uses that register and that CF is dyld. + +There are three states for (ptr & 0x3): + 0b00: check not yet done (or not going to be done, and is a dyld func ptr) + 0b01: check done, dyld function pointer + 0b11: check done, CFM tvector pointer +(but a NULL callback just stays NULL) + +There may be up to 5 word-sized arguments. Floating point +arguments can be done, but count as two word arguments. +Return value can be integral or real. +*/ + +/* Keep this assembly at the bottom of the source file! */ + +__asm__ ( +".text\n" +" .align 2\n" +".private_extern ___CF_INVOKE_CALLBACK\n" +"___CF_INVOKE_CALLBACK:\n" + "rlwinm r12,r3,0,0,29\n" + "andi. r0,r3,0x2\n" + "or r3,r4,r4\n" + "or r4,r5,r5\n" + "or r5,r6,r6\n" + "or r6,r7,r7\n" + "or r7,r8,r8\n" + "beq- Lcall\n" + "lwz r2,0x4(r12)\n" + "lwz r12,0x0(r12)\n" +"Lcall: mtspr ctr,r12\n" + "bctr\n"); + +#endif + + +// void __HALT(void); + +#if defined(__powerpc__) || defined(__ppc__) || defined(__ppc64__) +__asm__ ( +".text\n" +" .align 2\n" +#if DEPLOYMENT_TARGET_MACOSX +".private_extern ___HALT\n" +"___HALT:\n" +#elif DEPLOYMENT_TARGET_LINUX +".globl __HALT\n" +"__HALT:\n" +#else +".globl ___HALT\n" +"___HALT:\n" +#endif +" trap\n" +); +#endif + +#if defined(__i386__) || defined(__x86_64__) +#if defined(_MSC_VER) +void __HALT() { + __asm int 3; +} +#else +__asm__ ( +".text\n" +" .align 2, 0x90\n" +#if DEPLOYMENT_TARGET_MACOSX +".private_extern ___HALT\n" +"___HALT:\n" +#elif DEPLOYMENT_TARGET_LINUX +".globl __HALT\n" +"__HALT:\n" +#else +".globl ___HALT\n" +"___HALT:\n" +#endif +" int3\n" +); +#endif +#endif + +#if defined(__arm__) +__asm__ ( +".text\n" +" .align 2\n" +#if DEPLOYMENT_TARGET_MACOSX +".private_extern ___HALT\n" +"___HALT:\n" +#elif DEPLOYMENT_TARGET_LINUX +".globl __HALT\n" +"__HALT:\n" +#else +".globl ___HALT\n" +"___HALT:\n" +#endif +" bkpt\n" +); +#endif /* defined(__arm__) */ + +#if defined(__aarch64__) +__asm__ ( +".text\n" +" .align 2\n" +#if DEPLOYMENT_TARGET_MACOSX +".private_extern ___HALT\n" +"___HALT:\n" +#elif DEPLOYMENT_TARGET_LINUX +".globl __HALT\n" +"__HALT:\n" +#else +".globl ___HALT\n" +"___HALT:\n" +#endif +" brk #0000\n" +); +#endif /* defined(__aarch64__) */
diff --git a/CoreFoundation/CFBase.h b/CoreFoundation/CFBase.h new file mode 100644 index 0000000..55fe89b --- /dev/null +++ b/CoreFoundation/CFBase.h
@@ -0,0 +1,469 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. All rights reserved. + * Copyright (c) 2009 Grant Erickson <gerickson@nuovations.com>. All rights reserved. + * + * This source code is a modified version of the CoreFoundation sources released by Apple Inc. under + * the terms of the APSL version 2.0 (see below). + * + * For information about changes from the original Apple source release can be found by reviewing the + * source control system for the project at https://sourceforge.net/svn/?group_id=246198. + * + * The original license information is as follows: + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFBase.h + Copyright (c) 1998-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFBASE__) +#define __COREFOUNDATION_CFBASE__ 1 + +#if (defined(__CYGWIN32__) || defined(_WIN32) || defined(WIN32)) && !defined (__WIN32__) +#define __WIN32__ 1 +#endif + +#if defined(_MSC_VER) && defined(_M_IX86) +#define __i386__ 1 +#endif + +#if (defined(__i386__) || defined(__x86_64__)) && !defined(__LITTLE_ENDIAN__) + #define __LITTLE_ENDIAN__ 1 +#endif + +#if (defined(__ARMEL__) && !defined(__LITTLE_ENDIAN__)) + #define __LITTLE_ENDIAN__ 1 +#endif + +#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__) +#error Do not know the endianess of this architecture +#endif + +#if !__BIG_ENDIAN__ && !__LITTLE_ENDIAN__ +#error Both __BIG_ENDIAN__ and __LITTLE_ENDIAN__ cannot be false +#endif + +#if __BIG_ENDIAN__ && __LITTLE_ENDIAN__ +#error Both __BIG_ENDIAN__ and __LITTLE_ENDIAN__ cannot be true +#endif + +#if defined(__WIN32__) +#include <windows.h> +#include <winsock2.h> +#endif + +#include <stdint.h> +#include <stdbool.h> + +#if !(defined(__APPLE__) || defined(__GNUC__)) +#define weak_import +#define __private_extern__ +#elif defined(__GNUC__) +#define weak_import +#define __private_extern__ __attribute__((visibility("hidden"))) +#endif + +#include <AvailabilityMacros.h> + +#if defined(__MACH__) + #include <libkern/OSTypes.h> +#endif + +#if !defined(__MACTYPES__) +#if !defined(_OS_OSTYPES_H) + typedef unsigned char Boolean; + typedef unsigned char UInt8; + typedef signed char SInt8; + typedef unsigned short UInt16; + typedef signed short SInt16; + typedef unsigned int UInt32; + typedef signed int SInt32; + typedef uint64_t UInt64; + typedef int64_t SInt64; + typedef SInt32 OSStatus; +#endif + typedef float Float32; + typedef double Float64; + typedef unsigned short UniChar; + typedef unsigned char * StringPtr; + typedef const unsigned char * ConstStringPtr; + typedef unsigned char Str255[256]; + typedef const unsigned char * ConstStr255Param; + typedef SInt16 OSErr; + typedef SInt16 RegionCode; + typedef SInt16 LangCode; +#endif +#if !defined(__MACTYPES__) || (defined(UNIVERSAL_INTERFACES_VERSION) && UNIVERSAL_INTERFACES_VERSION < 0x0340) + typedef UInt32 UTF32Char; + typedef UInt16 UTF16Char; + typedef UInt8 UTF8Char; +#endif + +// Select correct deployment type +#if defined(__APPLE__) && !defined(DEPLOYMENT_TARGET_MACOSX) +#define DEPLOYMENT_TARGET_MACOSX 1 +#elif (defined (_WIN32) || defined(__WIN32__)) && defined(_MSC_VER) && !defined(DEPLOYMENT_TARGET_WINDOWS) +#define DEPLOYMENT_TARGET_WINDOWS 1 +#elif (defined(linux) || defined(__linux) || defined (__linux__)) && !defined(DEPLOYMENT_TARGET_LINUX) +#define DEPLOYMENT_TARGET_LINUX 1 +#endif + +#if !defined(CF_EXTERN_C_BEGIN) +#if defined(__cplusplus) +#define CF_EXTERN_C_BEGIN extern "C" { +#define CF_EXTERN_C_END } +#else +#define CF_EXTERN_C_BEGIN +#define CF_EXTERN_C_END +#endif +#endif + +CF_EXTERN_C_BEGIN + +#if !defined(NULL) +#if defined(__GNUG__) + #define NULL __null +#elif defined(__cplusplus) + #define NULL 0 +#else + #define NULL ((void *)0) +#endif +#endif + +#if !defined(TRUE) + #define TRUE 1 +#endif + +#if !defined(FALSE) + #define FALSE 0 +#endif + +#if defined(__WIN32__) + #undef CF_EXPORT + #if defined(CF_BUILDING_CF) + #define CF_EXPORT __declspec(dllexport) extern + #else + #define CF_EXPORT __declspec(dllimport) extern + #endif + #ifdef __GNUC__ + #define WIN_STDCALL __attribute__((stdcall)) + #else + #define WIN_STDCALL __stdcall + #endif +#elif defined(macintosh) + #if defined(__MWERKS__) + #define CF_EXPORT __declspec(export) extern + #endif +#endif + +#if !defined(CF_EXPORT) + #define CF_EXPORT extern +#endif + +#if !defined(CF_INLINE) + #if defined(__GNUC__) && (__GNUC__ == 4) && !defined(DEBUG) + #define CF_INLINE static __inline__ __attribute__((always_inline)) + #elif defined(__GNUC__) + #define CF_INLINE static __inline__ + #elif defined(__MWERKS__) || defined(__cplusplus) + #define CF_INLINE static inline + #elif defined(_MSC_VER) + #define CF_INLINE static __inline + #elif defined(__WIN32__) + #define CF_INLINE static __inline__ + #endif +#endif + + +CF_EXPORT double kCFCoreFoundationVersionNumber; +CF_EXPORT const unsigned char kCFCoreFoundationVersionString[]; + +#define kCFCoreFoundationVersionNumber10_0 196.40 +#define kCFCoreFoundationVersionNumber10_0_3 196.50 +#define kCFCoreFoundationVersionNumber10_1 226.00 +#define kCFCoreFoundationVersionNumber10_1_1 226.00 +/* Note the next three do not follow the usual numbering policy from the base release */ +#define kCFCoreFoundationVersionNumber10_1_2 227.20 +#define kCFCoreFoundationVersionNumber10_1_3 227.20 +#define kCFCoreFoundationVersionNumber10_1_4 227.30 +#define kCFCoreFoundationVersionNumber10_2 263.00 +#define kCFCoreFoundationVersionNumber10_2_1 263.10 +#define kCFCoreFoundationVersionNumber10_2_2 263.10 +#define kCFCoreFoundationVersionNumber10_2_3 263.30 +#define kCFCoreFoundationVersionNumber10_2_4 263.30 +#define kCFCoreFoundationVersionNumber10_2_5 263.50 +#define kCFCoreFoundationVersionNumber10_2_6 263.50 +#define kCFCoreFoundationVersionNumber10_2_7 263.50 +#define kCFCoreFoundationVersionNumber10_2_8 263.50 +#define kCFCoreFoundationVersionNumber10_3 299.00 +#define kCFCoreFoundationVersionNumber10_3_1 299.00 +#define kCFCoreFoundationVersionNumber10_3_2 299.00 +#define kCFCoreFoundationVersionNumber10_3_3 299.30 +#define kCFCoreFoundationVersionNumber10_3_4 299.31 +#define kCFCoreFoundationVersionNumber10_3_5 299.31 +#define kCFCoreFoundationVersionNumber10_3_6 299.32 +#define kCFCoreFoundationVersionNumber10_3_7 299.33 +#define kCFCoreFoundationVersionNumber10_3_8 299.33 +#define kCFCoreFoundationVersionNumber10_3_9 299.35 +#define kCFCoreFoundationVersionNumber10_4 368.00 +#define kCFCoreFoundationVersionNumber10_4_1 368.10 +#define kCFCoreFoundationVersionNumber10_4_2 368.11 +#define kCFCoreFoundationVersionNumber10_4_3 368.18 +#define kCFCoreFoundationVersionNumber10_4_4_Intel 368.26 +#define kCFCoreFoundationVersionNumber10_4_4_PowerPC 368.25 +#define kCFCoreFoundationVersionNumber10_4_5_Intel 368.26 +#define kCFCoreFoundationVersionNumber10_4_5_PowerPC 368.25 +#define kCFCoreFoundationVersionNumber10_4_6_Intel 368.26 +#define kCFCoreFoundationVersionNumber10_4_6_PowerPC 368.25 +#define kCFCoreFoundationVersionNumber10_4_7 368.27 +#define kCFCoreFoundationVersionNumber10_4_8 368.27 +#define kCFCoreFoundationVersionNumber10_4_9 368.28 +#define kCFCoreFoundationVersionNumber10_4_10 368.28 +#define kCFCoreFoundationVersionNumber10_4_11 368.31 + +typedef unsigned long CFTypeID; +typedef unsigned long CFOptionFlags; +typedef unsigned long CFHashCode; +typedef signed long CFIndex; + +/* Base "type" of all "CF objects", and polymorphic functions on them */ +typedef const void * CFTypeRef; + +typedef const struct __CFString * CFStringRef; +typedef struct __CFString * CFMutableStringRef; + +/* + Type to mean any instance of a property list type; + currently, CFString, CFData, CFNumber, CFBoolean, CFDate, + CFArray, and CFDictionary. +*/ +typedef CFTypeRef CFPropertyListRef; + +/* Values returned from comparison functions */ +enum { + kCFCompareLessThan = -1, + kCFCompareEqualTo = 0, + kCFCompareGreaterThan = 1 +}; +typedef CFIndex CFComparisonResult; + +/* A standard comparison function */ +typedef CFComparisonResult (*CFComparatorFunction)(const void *val1, const void *val2, void *context); + +/* Constant used by some functions to indicate failed searches. */ +/* This is of type CFIndex. */ +enum { + kCFNotFound = -1 +}; + + +/* Range type */ +typedef struct { + CFIndex location; + CFIndex length; +} CFRange; + +#if defined(CF_INLINE) +CF_INLINE CFRange CFRangeMake(CFIndex loc, CFIndex len) { + CFRange range; + range.location = loc; + range.length = len; + return range; +} +#else +#define CFRangeMake(LOC, LEN) __CFRangeMake(LOC, LEN) +#endif + +/* Private; do not use */ +CF_EXPORT +CFRange __CFRangeMake(CFIndex loc, CFIndex len); + + +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED +/* Null representant */ + +typedef const struct __CFNull * CFNullRef; + +CF_EXPORT +CFTypeID CFNullGetTypeID(void); + +CF_EXPORT +const CFNullRef kCFNull; // the singleton null instance + +#endif + + +/* Allocator API + + Most of the time when specifying an allocator to Create functions, the NULL + argument indicates "use the default"; this is the same as using kCFAllocatorDefault + or the return value from CFAllocatorGetDefault(). This assures that you will use + the allocator in effect at that time. + + You should rarely use kCFAllocatorSystemDefault, the default default allocator. +*/ +typedef const struct __CFAllocator * CFAllocatorRef; + +/* This is a synonym for NULL, if you'd rather use a named constant. */ +CF_EXPORT +const CFAllocatorRef kCFAllocatorDefault; + +/* Default system allocator; you rarely need to use this. */ +CF_EXPORT +const CFAllocatorRef kCFAllocatorSystemDefault; + +/* This allocator uses malloc(), realloc(), and free(). This should not be + generally used; stick to kCFAllocatorDefault whenever possible. This + allocator is useful as the "bytesDeallocator" in CFData or + "contentsDeallocator" in CFString where the memory was obtained as a + result of malloc() type functions. +*/ +CF_EXPORT +const CFAllocatorRef kCFAllocatorMalloc; + +/* This allocator explicitly uses the default malloc zone, returned by + malloc_default_zone(). It should only be used when an object is + safe to be allocated in non-scanned memory. + */ +CF_EXPORT +const CFAllocatorRef kCFAllocatorMallocZone AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +/* Null allocator which does nothing and allocates no memory. This allocator + is useful as the "bytesDeallocator" in CFData or "contentsDeallocator" + in CFString where the memory should not be freed. +*/ +CF_EXPORT +const CFAllocatorRef kCFAllocatorNull; + +/* Special allocator argument to CFAllocatorCreate() which means + "use the functions given in the context to allocate the allocator + itself as well". +*/ +CF_EXPORT +const CFAllocatorRef kCFAllocatorUseContext; + +typedef const void * (*CFAllocatorRetainCallBack)(const void *info); +typedef void (*CFAllocatorReleaseCallBack)(const void *info); +typedef CFStringRef (*CFAllocatorCopyDescriptionCallBack)(const void *info); +typedef void * (*CFAllocatorAllocateCallBack)(CFIndex allocSize, CFOptionFlags hint, void *info); +typedef void * (*CFAllocatorReallocateCallBack)(void *ptr, CFIndex newsize, CFOptionFlags hint, void *info); +typedef void (*CFAllocatorDeallocateCallBack)(void *ptr, void *info); +typedef CFIndex (*CFAllocatorPreferredSizeCallBack)(CFIndex size, CFOptionFlags hint, void *info); +typedef struct { + CFIndex version; + void * info; + CFAllocatorRetainCallBack retain; + CFAllocatorReleaseCallBack release; + CFAllocatorCopyDescriptionCallBack copyDescription; + CFAllocatorAllocateCallBack allocate; + CFAllocatorReallocateCallBack reallocate; + CFAllocatorDeallocateCallBack deallocate; + CFAllocatorPreferredSizeCallBack preferredSize; +} CFAllocatorContext; + +CF_EXPORT +CFTypeID CFAllocatorGetTypeID(void); + +/* + CFAllocatorSetDefault() sets the allocator that is used in the current + thread whenever NULL is specified as an allocator argument. This means + that most, if not all allocations will go through this allocator. It + also means that any allocator set as the default needs to be ready to + deal with arbitrary memory allocation requests; in addition, the size + and number of requests will change between releases. + + An allocator set as the default will never be released, even if later + another allocator replaces it as the default. Not only is it impractical + for it to be released (as there might be caches created under the covers + that refer to the allocator), in general it's also safer and more + efficient to keep it around. + + If you wish to use a custom allocator in a context, it's best to provide + it as the argument to the various creation functions rather than setting + it as the default. Setting the default allocator is not encouraged. + + If you do set an allocator as the default, either do it for all time in + your app, or do it in a nested fashion (by restoring the previous allocator + when you exit your context). The latter might be appropriate for plug-ins + or libraries that wish to set the default allocator. +*/ +CF_EXPORT +void CFAllocatorSetDefault(CFAllocatorRef allocator); + +CF_EXPORT +CFAllocatorRef CFAllocatorGetDefault(void); + +CF_EXPORT +CFAllocatorRef CFAllocatorCreate(CFAllocatorRef allocator, CFAllocatorContext *context); + +CF_EXPORT +void *CFAllocatorAllocate(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint); + +CF_EXPORT +void *CFAllocatorReallocate(CFAllocatorRef allocator, void *ptr, CFIndex newsize, CFOptionFlags hint); + +CF_EXPORT +void CFAllocatorDeallocate(CFAllocatorRef allocator, void *ptr); + +CF_EXPORT +CFIndex CFAllocatorGetPreferredSizeForSize(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint); + +CF_EXPORT +void CFAllocatorGetContext(CFAllocatorRef allocator, CFAllocatorContext *context); + + +/* Polymorphic CF functions */ + +CF_EXPORT +CFTypeID CFGetTypeID(CFTypeRef cf); + +CF_EXPORT +CFStringRef CFCopyTypeIDDescription(CFTypeID type_id); + +CF_EXPORT +CFTypeRef CFRetain(CFTypeRef cf); + +CF_EXPORT +void CFRelease(CFTypeRef cf); + +CF_EXPORT +CFIndex CFGetRetainCount(CFTypeRef cf); + +CF_EXPORT +CFTypeRef CFMakeCollectable(CFTypeRef cf) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +CF_EXPORT +Boolean CFEqual(CFTypeRef cf1, CFTypeRef cf2); + +CF_EXPORT +CFHashCode CFHash(CFTypeRef cf); + +CF_EXPORT +CFStringRef CFCopyDescription(CFTypeRef cf); + +CF_EXPORT +CFAllocatorRef CFGetAllocator(CFTypeRef cf); + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFBASE__ */ +
diff --git a/CoreFoundation/CFBinaryHeap.c b/CoreFoundation/CFBinaryHeap.c new file mode 100644 index 0000000..885428d --- /dev/null +++ b/CoreFoundation/CFBinaryHeap.c
@@ -0,0 +1,459 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFBinaryHeap.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include <CoreFoundation/CFBinaryHeap.h> +#include "CFPriv.h" +#include "CFInternal.h" + +const CFBinaryHeapCallBacks kCFStringBinaryHeapCallBacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, CFCopyDescription, (CFComparisonResult (*)(const void *, const void *, void *))CFStringCompare}; + +struct __CFBinaryHeapBucket { + void *_item; +}; + +CF_INLINE CFIndex __CFBinaryHeapRoundUpCapacity(CFIndex capacity) { + if (capacity < 4) return 4; + return (1 << flsl(capacity)); +} + +CF_INLINE CFIndex __CFBinaryHeapNumBucketsForCapacity(CFIndex capacity) { + return capacity; +} + +struct __CFBinaryHeap { + CFRuntimeBase _base; + CFIndex _count; /* number of objects */ + CFIndex _capacity; /* maximum number of objects */ + CFBinaryHeapCallBacks _callbacks; + CFBinaryHeapCompareContext _context; + struct __CFBinaryHeapBucket *_buckets; +}; + +CF_INLINE CFIndex __CFBinaryHeapCount(CFBinaryHeapRef heap) { + return heap->_count; +} + +CF_INLINE void __CFBinaryHeapSetCount(CFBinaryHeapRef heap, CFIndex v) { + /* for a CFBinaryHeap, _bucketsUsed == _count */ +} + +CF_INLINE CFIndex __CFBinaryHeapCapacity(CFBinaryHeapRef heap) { + return heap->_capacity; +} + +CF_INLINE void __CFBinaryHeapSetCapacity(CFBinaryHeapRef heap, CFIndex v) { + /* for a CFBinaryHeap, _bucketsNum == _capacity */ +} + +CF_INLINE CFIndex __CFBinaryHeapNumBucketsUsed(CFBinaryHeapRef heap) { + return heap->_count; +} + +CF_INLINE void __CFBinaryHeapSetNumBucketsUsed(CFBinaryHeapRef heap, CFIndex v) { + heap->_count = v; +} + +CF_INLINE CFIndex __CFBinaryHeapNumBuckets(CFBinaryHeapRef heap) { + return heap->_capacity; +} + +CF_INLINE void __CFBinaryHeapSetNumBuckets(CFBinaryHeapRef heap, CFIndex v) { + heap->_capacity = v; +} + +enum { /* bits 1-0 */ + kCFBinaryHeapMutable = 0x1, /* changeable and variable capacity */ +}; + +/* Bits 4-5 are used by GC */ + +CF_INLINE bool isStrongMemory_Heap(CFTypeRef collection) { + return __CFBitfieldGetValue(((const CFRuntimeBase *)collection)->_cfinfo[CF_INFO_BITS], 4, 4) == 0; +} + +CF_INLINE bool isWeakMemory_Heap(CFTypeRef collection) { + return __CFBitfieldGetValue(((const CFRuntimeBase *)collection)->_cfinfo[CF_INFO_BITS], 4, 4) != 0; +} + +CF_INLINE UInt32 __CFBinaryHeapMutableVariety(const void *cf) { + return __CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 3, 2); +} + +CF_INLINE void __CFBinaryHeapSetMutableVariety(void *cf, UInt32 v) { + __CFBitfieldSetValue(((CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 3, 2, v); +} + +CF_INLINE UInt32 __CFBinaryHeapMutableVarietyFromFlags(UInt32 flags) { + return __CFBitfieldGetValue(flags, 1, 0); +} + +static Boolean __CFBinaryHeapEqual(CFTypeRef cf1, CFTypeRef cf2) { + CFBinaryHeapRef heap1 = (CFBinaryHeapRef)cf1; + CFBinaryHeapRef heap2 = (CFBinaryHeapRef)cf2; + CFComparisonResult (*compare)(const void *, const void *, void *); + CFIndex idx; + CFIndex cnt; + const void **list1, **list2, *buffer[256]; + cnt = __CFBinaryHeapCount(heap1); + if (cnt != __CFBinaryHeapCount(heap2)) return false; + compare = heap1->_callbacks.compare; + if (compare != heap2->_callbacks.compare) return false; + if (0 == cnt) return true; /* after function comparison */ + list1 = (cnt <= 128) ? (const void **)buffer : (const void **)CFAllocatorAllocate(kCFAllocatorSystemDefault, 2 * cnt * sizeof(void *), 0); // GC OK + if (__CFOASafe && list1 != buffer) __CFSetLastAllocationEventName(list1, "CFBinaryHeap (temp)"); + list2 = (cnt <= 128) ? buffer + 128 : list1 + cnt; + CFBinaryHeapGetValues(heap1, list1); + CFBinaryHeapGetValues(heap2, list2); + for (idx = 0; idx < cnt; idx++) { + const void *val1 = list1[idx]; + const void *val2 = list2[idx]; +// CF: which context info should be passed in? both? +// CF: if the context infos are not equal, should the heaps not be equal? + if (val1 != val2) { + if (NULL == compare) return false; + if (!compare(val1, val2, heap1->_context.info)) return false; + } + } + if (list1 != buffer) CFAllocatorDeallocate(CFGetAllocator(heap1), list1); // GC OK + return true; +} + +static CFHashCode __CFBinaryHeapHash(CFTypeRef cf) { + CFBinaryHeapRef heap = (CFBinaryHeapRef)cf; + return __CFBinaryHeapCount(heap); +} + +static CFStringRef __CFBinaryHeapCopyDescription(CFTypeRef cf) { + CFBinaryHeapRef heap = (CFBinaryHeapRef)cf; + CFMutableStringRef result; + CFIndex idx; + CFIndex cnt; + const void **list, *buffer[256]; + cnt = __CFBinaryHeapCount(heap); + result = CFStringCreateMutable(CFGetAllocator(heap), 0); + CFStringAppendFormat(result, NULL, CFSTR("<CFBinaryHeap %p [%p]>{count = %u, capacity = %u, objects = (\n"), cf, CFGetAllocator(heap), cnt, __CFBinaryHeapCapacity(heap)); + list = (cnt <= 128) ? (const void **)buffer : (const void **)CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(void *), 0); // GC OK + if (__CFOASafe && list != buffer) __CFSetLastAllocationEventName(list, "CFBinaryHeap (temp)"); + CFBinaryHeapGetValues(heap, list); + for (idx = 0; idx < cnt; idx++) { + CFStringRef desc = NULL; + const void *item = list[idx]; + if (NULL != heap->_callbacks.copyDescription) { + desc = heap->_callbacks.copyDescription(item); + } + if (NULL != desc) { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : %s\n"), idx, desc); + CFRelease(desc); + } else { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : <%p>\n"), idx, item); + } + } + CFStringAppend(result, CFSTR(")}")); + if (list != buffer) CFAllocatorDeallocate(CFGetAllocator(heap), list); // GC OK + return result; +} + +static void __CFBinaryHeapDeallocate(CFTypeRef cf) { + CFBinaryHeapRef heap = (CFBinaryHeapRef)cf; + CFAllocatorRef allocator = CFGetAllocator(heap); + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + if (heap->_callbacks.retain == NULL && heap->_callbacks.release == NULL) + return; // GC: keep heap intact during finalization. + } +// CF: should make the heap mutable here first, a la CFArrayDeallocate + CFBinaryHeapRemoveAllValues(heap); +// CF: does not release the context info + if (__CFBinaryHeapMutableVariety(heap) == kCFBinaryHeapMutable) { + _CFAllocatorDeallocateGC(allocator, heap->_buckets); + } +} + +static CFTypeID __kCFBinaryHeapTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFBinaryHeapClass = { + _kCFRuntimeScannedObject, + "CFBinaryHeap", + NULL, // init + NULL, // copy + __CFBinaryHeapDeallocate, + __CFBinaryHeapEqual, + __CFBinaryHeapHash, + NULL, // + __CFBinaryHeapCopyDescription +}; + +__private_extern__ void __CFBinaryHeapInitialize(void) { + __kCFBinaryHeapTypeID = _CFRuntimeRegisterClass(&__CFBinaryHeapClass); +} + +CFTypeID CFBinaryHeapGetTypeID(void) { + return __kCFBinaryHeapTypeID; +} + +static CFBinaryHeapRef __CFBinaryHeapInit(CFAllocatorRef allocator, UInt32 flags, CFIndex capacity, const void **values, CFIndex numValues, const CFBinaryHeapCallBacks *callBacks, const CFBinaryHeapCompareContext *compareContext) { + CFBinaryHeapRef memory; + CFIndex idx; + CFIndex size; + + CFAssert2(0 <= capacity, __kCFLogAssertion, "%s(): capacity (%d) cannot be less than zero", __PRETTY_FUNCTION__, capacity); + CFAssert2(0 <= numValues, __kCFLogAssertion, "%s(): numValues (%d) cannot be less than zero", __PRETTY_FUNCTION__, numValues); + size = sizeof(struct __CFBinaryHeap) - sizeof(CFRuntimeBase); + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + if (!callBacks || (callBacks->retain == NULL && callBacks->release == NULL)) { + __CFBitfieldSetValue(flags, 4, 4, 1); // setWeak + } + } + + memory = (CFBinaryHeapRef)_CFRuntimeCreateInstance(allocator, __kCFBinaryHeapTypeID, size, NULL); + if (NULL == memory) { + return NULL; + } + __CFBinaryHeapSetCapacity(memory, __CFBinaryHeapRoundUpCapacity(1)); + __CFBinaryHeapSetNumBuckets(memory, __CFBinaryHeapNumBucketsForCapacity(__CFBinaryHeapRoundUpCapacity(1))); + void *buckets = _CFAllocatorAllocateGC(allocator, __CFBinaryHeapNumBuckets(memory) * sizeof(struct __CFBinaryHeapBucket), isStrongMemory_Heap(memory) ? __kCFAllocatorGCScannedMemory : 0); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, memory, memory->_buckets, buckets); + if (__CFOASafe) __CFSetLastAllocationEventName(memory->_buckets, "CFBinaryHeap (store)"); + if (NULL == memory->_buckets) { + CFRelease(memory); + return NULL; + } + __CFBinaryHeapSetNumBucketsUsed(memory, 0); + __CFBinaryHeapSetCount(memory, 0); + if (NULL != callBacks) { + memory->_callbacks.retain = callBacks->retain; + memory->_callbacks.release = callBacks->release; + memory->_callbacks.copyDescription = callBacks->copyDescription; + memory->_callbacks.compare = callBacks->compare; + } else { + memory->_callbacks.retain = 0; + memory->_callbacks.release = 0; + memory->_callbacks.copyDescription = 0; + memory->_callbacks.compare = 0; + } + __CFBinaryHeapSetMutableVariety(memory, kCFBinaryHeapMutable); + for (idx = 0; idx < numValues; idx++) { + CFBinaryHeapAddValue(memory, values[idx]); + } + __CFBinaryHeapSetMutableVariety(memory, __CFBinaryHeapMutableVarietyFromFlags(flags)); + if (compareContext) memcpy(&memory->_context, compareContext, sizeof(CFBinaryHeapCompareContext)); + return memory; +} + +CFBinaryHeapRef CFBinaryHeapCreate(CFAllocatorRef allocator, CFIndex capacity, const CFBinaryHeapCallBacks *callBacks, const CFBinaryHeapCompareContext *compareContext) { + return __CFBinaryHeapInit(allocator, kCFBinaryHeapMutable, capacity, NULL, 0, callBacks, compareContext); +} + +CFBinaryHeapRef CFBinaryHeapCreateCopy(CFAllocatorRef allocator, CFIndex capacity, CFBinaryHeapRef heap) { + __CFGenericValidateType(heap, __kCFBinaryHeapTypeID); + return __CFBinaryHeapInit(allocator, kCFBinaryHeapMutable, capacity, (const void **)heap->_buckets, __CFBinaryHeapCount(heap), &(heap->_callbacks), &(heap->_context)); +} + +CFIndex CFBinaryHeapGetCount(CFBinaryHeapRef heap) { + __CFGenericValidateType(heap, __kCFBinaryHeapTypeID); + return __CFBinaryHeapCount(heap); +} + +CFIndex CFBinaryHeapGetCountOfValue(CFBinaryHeapRef heap, const void *value) { + CFComparisonResult (*compare)(const void *, const void *, void *); + CFIndex idx; + CFIndex cnt = 0, length; + __CFGenericValidateType(heap, __kCFBinaryHeapTypeID); + compare = heap->_callbacks.compare; + length = __CFBinaryHeapCount(heap); + for (idx = 0; idx < length; idx++) { + const void *item = heap->_buckets[idx]._item; + if (value == item || (compare && kCFCompareEqualTo == compare(value, item, heap->_context.info))) { + cnt++; + } + } + return cnt; +} + +Boolean CFBinaryHeapContainsValue(CFBinaryHeapRef heap, const void *value) { + CFComparisonResult (*compare)(const void *, const void *, void *); + CFIndex idx; + CFIndex length; + __CFGenericValidateType(heap, __kCFBinaryHeapTypeID); + compare = heap->_callbacks.compare; + length = __CFBinaryHeapCount(heap); + for (idx = 0; idx < length; idx++) { + const void *item = heap->_buckets[idx]._item; + if (value == item || (compare && kCFCompareEqualTo == compare(value, item, heap->_context.info))) { + return true; + } + } + return false; +} + +const void *CFBinaryHeapGetMinimum(CFBinaryHeapRef heap) { + __CFGenericValidateType(heap, __kCFBinaryHeapTypeID); + CFAssert1(0 < __CFBinaryHeapCount(heap), __kCFLogAssertion, "%s(): binary heap is empty", __PRETTY_FUNCTION__); + return (0 < __CFBinaryHeapCount(heap)) ? heap->_buckets[0]._item : NULL; +} + +Boolean CFBinaryHeapGetMinimumIfPresent(CFBinaryHeapRef heap, const void **value) { + __CFGenericValidateType(heap, __kCFBinaryHeapTypeID); + if (0 == __CFBinaryHeapCount(heap)) return false; + if (NULL != value) __CFObjCStrongAssign(heap->_buckets[0]._item, value); + return true; +} + +void CFBinaryHeapGetValues(CFBinaryHeapRef heap, const void **values) { + CFBinaryHeapRef heapCopy; + CFIndex idx; + CFIndex cnt; + __CFGenericValidateType(heap, __kCFBinaryHeapTypeID); + CFAssert1(NULL != values, __kCFLogAssertion, "%s(): pointer to values may not be NULL", __PRETTY_FUNCTION__); + cnt = __CFBinaryHeapCount(heap); + if (0 == cnt) return; + if (CF_USING_COLLECTABLE_MEMORY) { + // GC: speculatively issue a write-barrier on the copied to buffers (3743553). + __CFObjCWriteBarrierRange(values, cnt * sizeof(void *)); + } + heapCopy = CFBinaryHeapCreateCopy(CFGetAllocator(heap), cnt, heap); + idx = 0; + while (0 < __CFBinaryHeapCount(heapCopy)) { + const void *value = CFBinaryHeapGetMinimum(heapCopy); + CFBinaryHeapRemoveMinimumValue(heapCopy); + values[idx++] = value; + } + CFRelease(heapCopy); +} + +void CFBinaryHeapApplyFunction(CFBinaryHeapRef heap, CFBinaryHeapApplierFunction applier, void *context) { + CFBinaryHeapRef heapCopy; + CFIndex cnt; + __CFGenericValidateType(heap, __kCFBinaryHeapTypeID); + CFAssert1(NULL != applier, __kCFLogAssertion, "%s(): pointer to applier function may not be NULL", __PRETTY_FUNCTION__); + cnt = __CFBinaryHeapCount(heap); + if (0 == cnt) return; + heapCopy = CFBinaryHeapCreateCopy(CFGetAllocator(heap), cnt, heap); + while (0 < __CFBinaryHeapCount(heapCopy)) { + const void *value = CFBinaryHeapGetMinimum(heapCopy); + CFBinaryHeapRemoveMinimumValue(heapCopy); + applier(value, context); + } + CFRelease(heapCopy); +} + +static void __CFBinaryHeapGrow(CFBinaryHeapRef heap, CFIndex numNewValues) { + CFIndex oldCount = __CFBinaryHeapCount(heap); + CFIndex capacity = __CFBinaryHeapRoundUpCapacity(oldCount + numNewValues); + CFAllocatorRef allocator = CFGetAllocator(heap); + __CFBinaryHeapSetCapacity(heap, capacity); + __CFBinaryHeapSetNumBuckets(heap, __CFBinaryHeapNumBucketsForCapacity(capacity)); + void *buckets = _CFAllocatorReallocateGC(allocator, heap->_buckets, __CFBinaryHeapNumBuckets(heap) * sizeof(struct __CFBinaryHeapBucket), isStrongMemory_Heap(heap) ? __kCFAllocatorGCScannedMemory : 0); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, heap, heap->_buckets, buckets); + if (__CFOASafe) __CFSetLastAllocationEventName(heap->_buckets, "CFBinaryHeap (store)"); + if (NULL == heap->_buckets) HALT; +} + +void CFBinaryHeapAddValue(CFBinaryHeapRef heap, const void *value) { + CFIndex idx, pidx; + CFIndex cnt; + CFAllocatorRef allocator = CFGetAllocator(heap); + __CFGenericValidateType(heap, __kCFBinaryHeapTypeID); + switch (__CFBinaryHeapMutableVariety(heap)) { + case kCFBinaryHeapMutable: + if (__CFBinaryHeapNumBucketsUsed(heap) == __CFBinaryHeapCapacity(heap)) + __CFBinaryHeapGrow(heap, 1); + break; + } + cnt = __CFBinaryHeapCount(heap); + idx = cnt; + __CFBinaryHeapSetNumBucketsUsed(heap, cnt + 1); + __CFBinaryHeapSetCount(heap, cnt + 1); + pidx = (idx - 1) >> 1; + while (0 < idx) { + void *item = heap->_buckets[pidx]._item; + if (kCFCompareGreaterThan != heap->_callbacks.compare(item, value, heap->_context.info)) break; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, heap->_buckets, heap->_buckets[idx]._item, item); + idx = pidx; + pidx = (idx - 1) >> 1; + } + if (heap->_callbacks.retain) { + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, heap->_buckets, heap->_buckets[idx]._item, (void *)heap->_callbacks.retain(allocator, (void *)value)); + } else { + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, heap->_buckets, heap->_buckets[idx]._item, (void *)value); + } +} + +void CFBinaryHeapRemoveMinimumValue(CFBinaryHeapRef heap) { + void *val; + CFIndex idx, cidx; + CFIndex cnt; + CFAllocatorRef allocator; + __CFGenericValidateType(heap, __kCFBinaryHeapTypeID); + cnt = __CFBinaryHeapCount(heap); + if (0 == cnt) return; + idx = 0; + __CFBinaryHeapSetNumBucketsUsed(heap, cnt - 1); + __CFBinaryHeapSetCount(heap, cnt - 1); + allocator = CFGetAllocator(heap); + if (heap->_callbacks.release) + heap->_callbacks.release(allocator, heap->_buckets[idx]._item); + val = heap->_buckets[cnt - 1]._item; + cidx = (idx << 1) + 1; + while (cidx < __CFBinaryHeapCount(heap)) { + void *item = heap->_buckets[cidx]._item; + if (cidx + 1 < __CFBinaryHeapCount(heap)) { + void *item2 = heap->_buckets[cidx + 1]._item; + if (kCFCompareGreaterThan == heap->_callbacks.compare(item, item2, heap->_context.info)) { + cidx++; + item = item2; + } + } + if (kCFCompareGreaterThan == heap->_callbacks.compare(item, val, heap->_context.info)) break; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, heap->_buckets, heap->_buckets[idx]._item, item); + idx = cidx; + cidx = (idx << 1) + 1; + } + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, heap->_buckets, heap->_buckets[idx]._item, val); +} + +void CFBinaryHeapRemoveAllValues(CFBinaryHeapRef heap) { + CFIndex idx; + CFIndex cnt; + __CFGenericValidateType(heap, __kCFBinaryHeapTypeID); + cnt = __CFBinaryHeapCount(heap); + if (heap->_callbacks.release) + for (idx = 0; idx < cnt; idx++) + heap->_callbacks.release(CFGetAllocator(heap), heap->_buckets[idx]._item); + __CFBinaryHeapSetNumBucketsUsed(heap, 0); + __CFBinaryHeapSetCount(heap, 0); +} +
diff --git a/CoreFoundation/CFBinaryHeap.h b/CoreFoundation/CFBinaryHeap.h new file mode 100644 index 0000000..09cf438 --- /dev/null +++ b/CoreFoundation/CFBinaryHeap.h
@@ -0,0 +1,319 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFBinaryHeap.h + Copyright (c) 1998-2007, Apple Inc. All rights reserved. +*/ +/*! + @header CFBinaryHeap + CFBinaryHeap implements a container which stores values sorted using + a binary search algorithm. CFBinaryHeaps can be useful as priority + queues. +*/ + +#if !defined(__COREFOUNDATION_CFBINARYHEAP__) +#define __COREFOUNDATION_CFBINARYHEAP__ 1 + +#include <CoreFoundation/CFBase.h> + +CF_EXTERN_C_BEGIN + +typedef struct { + CFIndex version; + void * info; + const void *(*retain)(const void *info); + void (*release)(const void *info); + CFStringRef (*copyDescription)(const void *info); +} CFBinaryHeapCompareContext; + +/*! + @typedef CFBinaryHeapCallBacks + Structure containing the callbacks for values of a CFBinaryHeap. + @field version The version number of the structure type being passed + in as a parameter to the CFBinaryHeap creation functions. + This structure is version 0. + @field retain The callback used to add a retain for the binary heap + on values as they are put into the binary heap. + This callback returns the value to use as the value in the + binary heap, which is usually the value parameter passed to + this callback, but may be a different value if a different + value should be added to the binary heap. The binary heap's + allocator is passed as the first argument. + @field release The callback used to remove a retain previously added + for the binary heap from values as they are removed from + the binary heap. The binary heap's allocator is passed as the + first argument. + @field copyDescription The callback used to create a descriptive + string representation of each value in the binary heap. This + is used by the CFCopyDescription() function. + @field compare The callback used to compare values in the binary heap for + equality in some operations. +*/ +typedef struct { + CFIndex version; + const void *(*retain)(CFAllocatorRef allocator, const void *ptr); + void (*release)(CFAllocatorRef allocator, const void *ptr); + CFStringRef (*copyDescription)(const void *ptr); + CFComparisonResult (*compare)(const void *ptr1, const void *ptr2, void *context); +} CFBinaryHeapCallBacks; + +/*! + @constant kCFStringBinaryHeapCallBacks + Predefined CFBinaryHeapCallBacks structure containing a set + of callbacks appropriate for use when the values in a CFBinaryHeap + are all CFString types. +*/ +CF_EXPORT const CFBinaryHeapCallBacks kCFStringBinaryHeapCallBacks; + +/*! + @typedef CFBinaryHeapApplierFunction + Type of the callback function used by the apply functions of + CFBinaryHeap. + @param value The current value from the binary heap. + @param context The user-defined context parameter given to the apply + function. +*/ +typedef void (*CFBinaryHeapApplierFunction)(const void *val, void *context); + +/*! + @typedef CFBinaryHeapRef + This is the type of a reference to CFBinaryHeaps. +*/ +typedef struct __CFBinaryHeap * CFBinaryHeapRef; + +/*! + @function CFBinaryHeapGetTypeID + Returns the type identifier of all CFBinaryHeap instances. +*/ +CF_EXPORT CFTypeID CFBinaryHeapGetTypeID(void); + +/*! + @function CFBinaryHeapCreate + Creates a new mutable binary heap with the given values. + @param allocator The CFAllocator which should be used to allocate + memory for the binary heap and its storage for values. This + parameter may be NULL in which case the current default + CFAllocator is used. If this reference is not a valid + CFAllocator, the behavior is undefined. + @param capacity A hint about the number of values that will be held + by the CFBinaryHeap. Pass 0 for no hint. The implementation may + ignore this hint, or may use it to optimize various + operations. A heap's actual capacity is only limited by + address space and available memory constraints). If this + parameter is negative, the behavior is undefined. + @param callBacks A pointer to a CFBinaryHeapCallBacks structure + initialized with the callbacks for the binary heap to use on + each value in the binary heap. A copy of the contents of the + callbacks structure is made, so that a pointer to a structure + on the stack can be passed in, or can be reused for multiple + binary heap creations. If the version field of this callbacks + structure is not one of the defined ones for CFBinaryHeap, the + behavior is undefined. The retain field may be NULL, in which + case the CFBinaryHeap will do nothing to add a retain to values + as they are put into the binary heap. The release field may be + NULL, in which case the CFBinaryHeap will do nothing to remove + the binary heap's retain (if any) on the values when the + heap is destroyed or a key-value pair is removed. If the + copyDescription field is NULL, the binary heap will create a + simple description for a value. If the equal field is NULL, the + binary heap will use pointer equality to test for equality of + values. This callbacks parameter itself may be NULL, which is + treated as if a valid structure of version 0 with all fields + NULL had been passed in. Otherwise, + if any of the fields are not valid pointers to functions + of the correct type, or this parameter is not a valid + pointer to a CFBinaryHeapCallBacks callbacks structure, + the behavior is undefined. If any of the values put into the + binary heap is not one understood by one of the callback functions + the behavior when that callback function is used is undefined. + @param compareContext A pointer to a CFBinaryHeapCompareContext structure. + @result A reference to the new CFBinaryHeap. +*/ +CF_EXPORT CFBinaryHeapRef CFBinaryHeapCreate(CFAllocatorRef allocator, CFIndex capacity, const CFBinaryHeapCallBacks *callBacks, const CFBinaryHeapCompareContext *compareContext); + +/*! + @function CFBinaryHeapCreateCopy + Creates a new mutable binary heap with the values from the given binary heap. + @param allocator The CFAllocator which should be used to allocate + memory for the binary heap and its storage for values. This + parameter may be NULL in which case the current default + CFAllocator is used. If this reference is not a valid + CFAllocator, the behavior is undefined. + @param capacity A hint about the number of values that will be held + by the CFBinaryHeap. Pass 0 for no hint. The implementation may + ignore this hint, or may use it to optimize various + operations. A heap's actual capacity is only limited by + address space and available memory constraints). + This parameter must be greater than or equal + to the count of the heap which is to be copied, or the + behavior is undefined. If this parameter is negative, the + behavior is undefined. + @param heap The binary heap which is to be copied. The values from the + binary heap are copied as pointers into the new binary heap (that is, + the values themselves are copied, not that which the values + point to, if anything). However, the values are also + retained by the new binary heap. The count of the new binary will + be the same as the given binary heap. The new binary heap uses the same + callbacks as the binary heap to be copied. If this parameter is + not a valid CFBinaryHeap, the behavior is undefined. + @result A reference to the new mutable binary heap. +*/ +CF_EXPORT CFBinaryHeapRef CFBinaryHeapCreateCopy(CFAllocatorRef allocator, CFIndex capacity, CFBinaryHeapRef heap); + +/*! + @function CFBinaryHeapGetCount + Returns the number of values currently in the binary heap. + @param heap The binary heap to be queried. If this parameter is not a valid + CFBinaryHeap, the behavior is undefined. + @result The number of values in the binary heap. +*/ +CF_EXPORT CFIndex CFBinaryHeapGetCount(CFBinaryHeapRef heap); + +/*! + @function CFBinaryHeapGetCountOfValue + Counts the number of times the given value occurs in the binary heap. + @param heap The binary heap to be searched. If this parameter is not a + valid CFBinaryHeap, the behavior is undefined. + @param value The value for which to find matches in the binary heap. The + compare() callback provided when the binary heap was created is + used to compare. If the compare() callback was NULL, pointer + equality (in C, ==) is used. If value, or any of the values + in the binary heap, are not understood by the compare() callback, + the behavior is undefined. + @result The number of times the given value occurs in the binary heap. +*/ +CF_EXPORT CFIndex CFBinaryHeapGetCountOfValue(CFBinaryHeapRef heap, const void *value); + +/*! + @function CFBinaryHeapContainsValue + Reports whether or not the value is in the binary heap. + @param heap The binary heap to be searched. If this parameter is not a + valid CFBinaryHeap, the behavior is undefined. + @param value The value for which to find matches in the binary heap. The + compare() callback provided when the binary heap was created is + used to compare. If the compare() callback was NULL, pointer + equality (in C, ==) is used. If value, or any of the values + in the binary heap, are not understood by the compare() callback, + the behavior is undefined. + @result true, if the value is in the specified binary heap, otherwise false. +*/ +CF_EXPORT Boolean CFBinaryHeapContainsValue(CFBinaryHeapRef heap, const void *value); + +/*! + @function CFBinaryHeapGetMinimum + Returns the minimum value is in the binary heap. If the heap contains several equal + minimum values, any one may be returned. + @param heap The binary heap to be searched. If this parameter is not a + valid CFBinaryHeap, the behavior is undefined. + @result A reference to the minimum value in the binary heap, or NULL if the + binary heap contains no values. +*/ +CF_EXPORT const void * CFBinaryHeapGetMinimum(CFBinaryHeapRef heap); + +/*! + @function CFBinaryHeapGetMinimumIfPresent + Returns the minimum value is in the binary heap, if present. If the heap contains several equal + minimum values, any one may be returned. + @param heap The binary heap to be searched. If this parameter is not a + valid CFBinaryHeap, the behavior is undefined. + @param value A C pointer to pointer-sized storage to be filled with the minimum value in + the binary heap. If this value is not a valid C pointer to a pointer-sized block + of storage, the result is undefined. If the result of the function is false, the value + stored at this address is undefined. + @result true, if a minimum value was found in the specified binary heap, otherwise false. +*/ +CF_EXPORT Boolean CFBinaryHeapGetMinimumIfPresent(CFBinaryHeapRef heap, const void **value); + +/*! + @function CFBinaryHeapGetValues + Fills the buffer with values from the binary heap. + @param heap The binary heap to be queried. If this parameter is not a + valid CFBinaryHeap, the behavior is undefined. + @param values A C array of pointer-sized values to be filled with + values from the binary heap. The values in the C array are ordered + from least to greatest. If this parameter is not a valid pointer to a + C array of at least CFBinaryHeapGetCount() pointers, the behavior is undefined. +*/ +CF_EXPORT void CFBinaryHeapGetValues(CFBinaryHeapRef heap, const void **values); + +/*! + @function CFBinaryHeapApplyFunction + Calls a function once for each value in the binary heap. + @param heap The binary heap to be operated upon. If this parameter is not a + valid CFBinaryHeap, the behavior is undefined. + @param applier The callback function to call once for each value in + the given binary heap. If this parameter is not a + pointer to a function of the correct prototype, the behavior + is undefined. If there are values in the binary heap which the + applier function does not expect or cannot properly apply + to, the behavior is undefined. + @param context A pointer-sized user-defined value, which is passed + as the second parameter to the applier function, but is + otherwise unused by this function. If the context is not + what is expected by the applier function, the behavior is + undefined. +*/ +CF_EXPORT void CFBinaryHeapApplyFunction(CFBinaryHeapRef heap, CFBinaryHeapApplierFunction applier, void *context); + +/*! + @function CFBinaryHeapAddValue + Adds the value to the binary heap. + @param heap The binary heap to which the value is to be added. If this parameter is not a + valid mutable CFBinaryHeap, the behavior is undefined. + @param value The value to add to the binary heap. The value is retained by + the binary heap using the retain callback provided when the binary heap + was created. If the value is not of the sort expected by the + retain callback, the behavior is undefined. +*/ +CF_EXPORT void CFBinaryHeapAddValue(CFBinaryHeapRef heap, const void *value); + +/*! + @function CFBinaryHeapRemoveMinimumValue + Removes the minimum value from the binary heap. + @param heap The binary heap from which the minimum value is to be removed. If this + parameter is not a valid mutable CFBinaryHeap, the behavior is undefined. +*/ +CF_EXPORT void CFBinaryHeapRemoveMinimumValue(CFBinaryHeapRef heap); + +/*! + @function CFBinaryHeapRemoveAllValues + Removes all the values from the binary heap, making it empty. + @param heap The binary heap from which all of the values are to be + removed. If this parameter is not a valid mutable CFBinaryHeap, + the behavior is undefined. +*/ +CF_EXPORT void CFBinaryHeapRemoveAllValues(CFBinaryHeapRef heap); + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFBINARYHEAP__ */ +
diff --git a/CoreFoundation/CFBinaryPList.c b/CoreFoundation/CFBinaryPList.c new file mode 100644 index 0000000..e284ecb --- /dev/null +++ b/CoreFoundation/CFBinaryPList.c
@@ -0,0 +1,1296 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. All rights reserved. + * Copyright (c) 2009 Stuart Crook <stuart@echus.demon.co.uk>. 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@ + */ +/* CFBinaryPList.c + Copyright 2000-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + + +#include <CoreFoundation/CFString.h> +#include <CoreFoundation/CFNumber.h> +#include <CoreFoundation/CFDate.h> +#include <CoreFoundation/CFData.h> +#include <CoreFoundation/CFArray.h> +#include <CoreFoundation/CFDictionary.h> +#include <CoreFoundation/CFSet.h> +#include <CoreFoundation/CFPropertyList.h> +#include <CoreFoundation/CFByteOrder.h> +#include <CoreFoundation/CFRuntime.h> +#include <CoreFoundation/CFStream.h> +#include <stdio.h> +#include <stdint.h> +#include <limits.h> +#include <string.h> +#include "CFInternal.h" + +typedef struct { + int64_t high; + uint64_t low; +} CFSInt128Struct; + +enum { + kCFNumberSInt128Type = 17 +}; + +extern CFNumberType _CFNumberGetType2(CFNumberRef number); + +enum { + CF_NO_ERROR = 0, + CF_OVERFLOW_ERROR = (1 << 0), +}; + +CF_INLINE uint32_t __check_uint32_add_unsigned_unsigned(uint32_t x, uint32_t y, int32_t* err) { + if((UINT_MAX - y) < x) + *err = *err | CF_OVERFLOW_ERROR; + return x + y; +}; + +CF_INLINE uint64_t __check_uint64_add_unsigned_unsigned(uint64_t x, uint64_t y, int32_t* err) { + if((ULLONG_MAX - y) < x) + *err = *err | CF_OVERFLOW_ERROR; + return x + y; +}; + +CF_INLINE uint32_t __check_uint32_mul_unsigned_unsigned(uint32_t x, uint32_t y, int32_t* err) { + uint64_t tmp = (uint64_t) x * (uint64_t) y; + /* If any of the upper 32 bits touched, overflow */ + if(tmp & 0xffffffff00000000ULL) + *err = *err | CF_OVERFLOW_ERROR; + return (uint32_t) tmp; +}; + +CF_INLINE uint64_t __check_uint64_mul_unsigned_unsigned(uint64_t x, uint64_t y, int32_t* err) { + if(x == 0) return 0; + if(ULLONG_MAX/x < y) + *err = *err | CF_OVERFLOW_ERROR; + return x * y; +}; + +#if __LP64__ +#define check_ptr_add(p, a, err) (const uint8_t *)__check_uint64_add_unsigned_unsigned((uintptr_t)p, (uintptr_t)a, err) +#define check_size_t_mul(b, a, err) (size_t)__check_uint64_mul_unsigned_unsigned((size_t)b, (size_t)a, err) +#else +#define check_ptr_add(p, a, err) (const uint8_t *)__check_uint32_add_unsigned_unsigned((uintptr_t)p, (uintptr_t)a, err) +#define check_size_t_mul(b, a, err) (size_t)__check_uint32_mul_unsigned_unsigned((size_t)b, (size_t)a, err) +#endif + + +CF_INLINE CFTypeID __CFGenericTypeID_genericobj_inline(const void *cf) { + CFTypeID typeID = (*(uint32_t *)(((CFRuntimeBase *)cf)->_cfinfo) >> 8) & 0xFFFF; + return CF_IS_OBJC(typeID, cf) ? CFGetTypeID(cf) : typeID; +} + +struct __CFKeyedArchiverUID { + CFRuntimeBase _base; + uint32_t _value; +}; + +static CFStringRef __CFKeyedArchiverUIDCopyDescription(CFTypeRef cf) { + CFKeyedArchiverUIDRef uid = (CFKeyedArchiverUIDRef)cf; + return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFKeyedArchiverUID %p [%p]>{value = %u}"), cf, CFGetAllocator(cf), uid->_value); +} + +static CFStringRef __CFKeyedArchiverUIDCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) { + CFKeyedArchiverUIDRef uid = (CFKeyedArchiverUIDRef)cf; + return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("@%u@"), uid->_value); +} + +static CFTypeID __kCFKeyedArchiverUIDTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFKeyedArchiverUIDClass = { + 0, + "CFKeyedArchiverUID", + NULL, // init + NULL, // copy + NULL, // finalize + NULL, // equal -- pointer equality only + NULL, // hash -- pointer hashing only + __CFKeyedArchiverUIDCopyFormattingDescription, + __CFKeyedArchiverUIDCopyDescription +}; + +__private_extern__ void __CFKeyedArchiverUIDInitialize(void) { + __kCFKeyedArchiverUIDTypeID = _CFRuntimeRegisterClass(&__CFKeyedArchiverUIDClass); +} + +CFTypeID _CFKeyedArchiverUIDGetTypeID(void) { + return __kCFKeyedArchiverUIDTypeID; +} + +CFKeyedArchiverUIDRef _CFKeyedArchiverUIDCreate(CFAllocatorRef allocator, uint32_t value) { + CFKeyedArchiverUIDRef uid; + uid = (CFKeyedArchiverUIDRef)_CFRuntimeCreateInstance(allocator, __kCFKeyedArchiverUIDTypeID, sizeof(struct __CFKeyedArchiverUID) - sizeof(CFRuntimeBase), NULL); + if (NULL == uid) { + return NULL; + } + ((struct __CFKeyedArchiverUID *)uid)->_value = value; + return uid; +} + + +uint32_t _CFKeyedArchiverUIDGetValue(CFKeyedArchiverUIDRef uid) { + return uid->_value; +} + + +typedef struct { + CFTypeRef stream; + CFTypeRef error; + uint64_t written; + int32_t used; + bool streamIsData; + uint8_t buffer[8192 - 32]; +} __CFBinaryPlistWriteBuffer; + +static void writeBytes(__CFBinaryPlistWriteBuffer *buf, const UInt8 *bytes, CFIndex length) { + if (0 == length) return; + if (buf->error) return; + if (buf->streamIsData) { + CFDataAppendBytes((CFMutableDataRef)buf->stream, bytes, length); + buf->written += length; + } else { + /* SystemConfiguration relies on being able to serialize a plist to a write stream. + * There seems no reason why this isn't supported. The old code read: + CFAssert(false, __kCFLogAssertion, "Streams are not supported on this platform"); + */ + CFIndex lengthWritten = CFWriteStreamWrite((CFWriteStreamRef)buf->stream, bytes, length); + buf->written += lengthWritten; + } +} + +static void bufferWrite(__CFBinaryPlistWriteBuffer *buf, const uint8_t *buffer, CFIndex count) { + if (0 == count) return; + if ((CFIndex)sizeof(buf->buffer) <= count) { + writeBytes(buf, buf->buffer, buf->used); + buf->used = 0; + writeBytes(buf, buffer, count); + return; + } + CFIndex copyLen = __CFMin(count, (CFIndex)sizeof(buf->buffer) - buf->used); + memmove(buf->buffer + buf->used, buffer, copyLen); + buf->used += copyLen; + if (sizeof(buf->buffer) == buf->used) { + writeBytes(buf, buf->buffer, sizeof(buf->buffer)); + memmove(buf->buffer, buffer + copyLen, count - copyLen); + buf->used = count - copyLen; + } +} + +static void bufferFlush(__CFBinaryPlistWriteBuffer *buf) { + writeBytes(buf, buf->buffer, buf->used); + buf->used = 0; +} + +/* +HEADER + magic number ("bplist") + file format version + +OBJECT TABLE + variable-sized objects + + Object Formats (marker byte followed by additional info in some cases) + null 0000 0000 + bool 0000 1000 // false + bool 0000 1001 // true + fill 0000 1111 // fill byte + int 0001 nnnn ... // # of bytes is 2^nnnn, big-endian bytes + real 0010 nnnn ... // # of bytes is 2^nnnn, big-endian bytes + date 0011 0011 ... // 8 byte float follows, big-endian bytes + data 0100 nnnn [int] ... // nnnn is number of bytes unless 1111 then int count follows, followed by bytes + string 0101 nnnn [int] ... // ASCII string, nnnn is # of chars, else 1111 then int count, then bytes + string 0110 nnnn [int] ... // Unicode string, nnnn is # of chars, else 1111 then int count, then big-endian 2-byte uint16_t + 0111 xxxx // unused + uid 1000 nnnn ... // nnnn+1 is # of bytes + 1001 xxxx // unused + array 1010 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows + 1011 xxxx // unused + set 1100 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows + dict 1101 nnnn [int] keyref* objref* // nnnn is count, unless '1111', then int count follows + 1110 xxxx // unused + 1111 xxxx // unused + +OFFSET TABLE + list of ints, byte size of which is given in trailer + -- these are the byte offsets into the file + -- number of these is in the trailer + +TRAILER + byte size of offset ints in offset table + byte size of object refs in arrays and dicts + number of offsets in offset table (also is number of objects) + element # in offset table which is top level object + offset table offset + +*/ + + +static CFTypeID stringtype = -1, datatype = -1, numbertype = -1, datetype = -1; +static CFTypeID booltype = -1, nulltype = -1, dicttype = -1, arraytype = -1, settype = -1; + +static void _appendInt(__CFBinaryPlistWriteBuffer *buf, uint64_t bigint) { + uint8_t marker; + uint8_t *bytes; + CFIndex nbytes; + if (bigint <= (uint64_t)0xff) { + nbytes = 1; + marker = kCFBinaryPlistMarkerInt | 0; + } else if (bigint <= (uint64_t)0xffff) { + nbytes = 2; + marker = kCFBinaryPlistMarkerInt | 1; + } else if (bigint <= (uint64_t)0xffffffff) { + nbytes = 4; + marker = kCFBinaryPlistMarkerInt | 2; + } else { + nbytes = 8; + marker = kCFBinaryPlistMarkerInt | 3; + } + bigint = CFSwapInt64HostToBig(bigint); + bytes = (uint8_t *)&bigint + sizeof(bigint) - nbytes; + bufferWrite(buf, &marker, 1); + bufferWrite(buf, bytes, nbytes); +} + +static void _appendUID(__CFBinaryPlistWriteBuffer *buf, CFKeyedArchiverUIDRef uid) { + uint8_t marker; + uint8_t *bytes; + CFIndex nbytes; + uint64_t bigint = _CFKeyedArchiverUIDGetValue(uid); + if (bigint <= (uint64_t)0xff) { + nbytes = 1; + } else if (bigint <= (uint64_t)0xffff) { + nbytes = 2; + } else if (bigint <= (uint64_t)0xffffffff) { + nbytes = 4; + } else { + nbytes = 8; + } + marker = kCFBinaryPlistMarkerUID | (uint8_t)(nbytes - 1); + bigint = CFSwapInt64HostToBig(bigint); + bytes = (uint8_t *)&bigint + sizeof(bigint) - nbytes; + bufferWrite(buf, &marker, 1); + bufferWrite(buf, bytes, nbytes); +} + +static Boolean __plistNumberEqual(CFTypeRef cf1, CFTypeRef cf2) { + // As long as this equals function is more restrictive than the + // existing one, for any given type, the hash function need not + // also be provided for the uniquing set. + if (CFNumberIsFloatType((CFNumberRef)cf1) != CFNumberIsFloatType((CFNumberRef)cf2)) return false; + return CFEqual(cf1, cf2); +} + +static CFHashCode __plistDataHash(CFTypeRef cf) { + CFDataRef data = (CFDataRef)cf; + return CFHashBytes((UInt8 *)CFDataGetBytePtr(data), __CFMin(CFDataGetLength(data), 1280)); +} + +static void _flattenPlist(CFPropertyListRef plist, CFMutableArrayRef objlist, CFMutableDictionaryRef objtable, CFMutableSetRef uniquingsets[]) { + CFPropertyListRef unique; + uint32_t refnum; + CFTypeID type = __CFGenericTypeID_genericobj_inline(plist); + CFIndex idx; + CFPropertyListRef *list, buffer[256]; + + // Do not unique dictionaries or arrays, because: they + // are slow to compare, and have poor hash codes. + // Uniquing bools is unnecessary. + int which = -1; + if (stringtype == type) { + which = 0; + } else if (numbertype == type) { + which = 1; + } else if (datetype == type) { + which = 2; + } else if (datatype == type) { + which = 3; + } + if (1 && -1 != which) { + CFMutableSetRef uniquingset = uniquingsets[which]; + CFIndex before = CFSetGetCount(uniquingset); + CFSetAddValue(uniquingset, plist); + CFIndex after = CFSetGetCount(uniquingset); + if (after == before) { // already in set + unique = CFSetGetValue(uniquingset, plist); + if (unique != plist) { + refnum = (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable, unique); + CFDictionaryAddValue(objtable, plist, (const void *)(uintptr_t)refnum); + } + return; + } + } + refnum = CFArrayGetCount(objlist); + CFArrayAppendValue(objlist, plist); + CFDictionaryAddValue(objtable, plist, (const void *)(uintptr_t)refnum); + if (dicttype == type) { + CFIndex count = CFDictionaryGetCount((CFDictionaryRef)plist); + list = (count <= 128) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, 2 * count * sizeof(CFTypeRef), 0); + CFDictionaryGetKeysAndValues((CFDictionaryRef)plist, list, list + count); + for (idx = 0; idx < 2 * count; idx++) { + _flattenPlist(list[idx], objlist, objtable, uniquingsets); + } + if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); + } else if (arraytype == type) { + CFIndex count = CFArrayGetCount((CFArrayRef)plist); + list = (count <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, count * sizeof(CFTypeRef), 0); + CFArrayGetValues((CFArrayRef)plist, CFRangeMake(0, count), list); + for (idx = 0; idx < count; idx++) { + _flattenPlist(list[idx], objlist, objtable, uniquingsets); + } + if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); + } +} + +// stream must be a CFMutableDataRef +CFIndex __CFBinaryPlistWriteToStream(CFPropertyListRef plist, CFTypeRef stream) { + CFMutableDictionaryRef objtable; + CFMutableArrayRef objlist; + CFBinaryPlistTrailer trailer; + uint64_t *offsets, length_so_far; + uint64_t mask, refnum; + int64_t idx, idx2, cnt; + __CFBinaryPlistWriteBuffer *buf; + + if ((CFTypeID)-1 == stringtype) { + stringtype = CFStringGetTypeID(); + } + if ((CFTypeID)-1 == datatype) { + datatype = CFDataGetTypeID(); + } + if ((CFTypeID)-1 == numbertype) { + numbertype = CFNumberGetTypeID(); + } + if ((CFTypeID)-1 == booltype) { + booltype = CFBooleanGetTypeID(); + } + if ((CFTypeID)-1 == datetype) { + datetype = CFDateGetTypeID(); + } + if ((CFTypeID)-1 == dicttype) { + dicttype = CFDictionaryGetTypeID(); + } + if ((CFTypeID)-1 == arraytype) { + arraytype = CFArrayGetTypeID(); + } + if ((CFTypeID)-1 == settype) { + settype = CFSetGetTypeID(); + } + if ((CFTypeID)-1 == nulltype) { + nulltype = CFNullGetTypeID(); + } + + objtable = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, NULL); + _CFDictionarySetCapacity(objtable, 640); + objlist = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, NULL); + _CFArraySetCapacity(objlist, 640); + CFSetCallBacks cb = kCFTypeSetCallBacks; + cb.retain = NULL; + cb.release = NULL; + CFMutableSetRef uniquingsets[4]; + uniquingsets[0] = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &cb); + _CFSetSetCapacity(uniquingsets[0], 1000); + cb.equal = __plistNumberEqual; + uniquingsets[1] = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &cb); + _CFSetSetCapacity(uniquingsets[1], 500); + cb.equal = kCFTypeSetCallBacks.equal; + uniquingsets[2] = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &cb); + _CFSetSetCapacity(uniquingsets[2], 500); + cb.hash = __plistDataHash; + uniquingsets[3] = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &cb); + _CFSetSetCapacity(uniquingsets[3], 500); + + _flattenPlist(plist, objlist, objtable, uniquingsets); + + CFRelease(uniquingsets[0]); + CFRelease(uniquingsets[1]); + CFRelease(uniquingsets[2]); + CFRelease(uniquingsets[3]); + + cnt = CFArrayGetCount(objlist); + offsets = (uint64_t *)CFAllocatorAllocate(kCFAllocatorSystemDefault, (CFIndex)(cnt * sizeof(*offsets)), 0); + + buf = (__CFBinaryPlistWriteBuffer *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(__CFBinaryPlistWriteBuffer), 0); + buf->stream = stream; + buf->error = NULL; + buf->streamIsData = (CFGetTypeID(stream) == CFDataGetTypeID()); + buf->written = 0; + buf->used = 0; + bufferWrite(buf, (uint8_t *)"bplist00", 8); // header + + memset(&trailer, 0, sizeof(trailer)); + trailer._numObjects = CFSwapInt64HostToBig(cnt); + trailer._topObject = 0; // true for this implementation + mask = ~(uint64_t)0; + while (cnt & mask) { + trailer._objectRefSize++; + mask = mask << 8; + } + + for (idx = 0; idx < cnt; idx++) { + CFPropertyListRef obj = CFArrayGetValueAtIndex(objlist, (CFIndex)idx); + CFTypeID type = __CFGenericTypeID_genericobj_inline(obj); + offsets[idx] = buf->written + buf->used; + if (stringtype == type) { + CFIndex ret, count = CFStringGetLength((CFStringRef)obj); + CFIndex needed; + uint8_t *bytes, buffer[1024]; + bytes = (count <= 1024) ? buffer : (uint8_t *)CFAllocatorAllocate(kCFAllocatorSystemDefault, count, 0); + // presumption, believed to be true, is that ASCII encoding may need + // less bytes, but will not need greater, than the # of unichars + ret = CFStringGetBytes((CFStringRef)obj, CFRangeMake(0, count), kCFStringEncodingASCII, 0, false, bytes, count, &needed); + if (ret == count) { + uint8_t marker = (uint8_t)(kCFBinaryPlistMarkerASCIIString | (needed < 15 ? needed : 0xf)); + bufferWrite(buf, &marker, 1); + if (15 <= needed) { + _appendInt(buf, (uint64_t)needed); + } + bufferWrite(buf, bytes, needed); + } else { + UniChar *chars; + uint8_t marker = (uint8_t)(kCFBinaryPlistMarkerUnicode16String | (count < 15 ? count : 0xf)); + bufferWrite(buf, &marker, 1); + if (15 <= count) { + _appendInt(buf, (uint64_t)count); + } + chars = (UniChar *)CFAllocatorAllocate(kCFAllocatorSystemDefault, count * sizeof(UniChar), 0); + CFStringGetCharacters((CFStringRef)obj, CFRangeMake(0, count), chars); + for (idx2 = 0; idx2 < count; idx2++) { + chars[idx2] = CFSwapInt16HostToBig(chars[idx2]); + } + bufferWrite(buf, (uint8_t *)chars, count * sizeof(UniChar)); + CFAllocatorDeallocate(kCFAllocatorSystemDefault, chars); + } + if (bytes != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, bytes); + } else if (numbertype == type) { + uint8_t marker; + uint64_t bigint; + uint8_t *bytes; + CFIndex nbytes; + if (CFNumberIsFloatType((CFNumberRef)obj)) { + CFSwappedFloat64 swapped64; + CFSwappedFloat32 swapped32; + if (CFNumberGetByteSize((CFNumberRef)obj) <= (CFIndex)sizeof(float)) { + float v; + CFNumberGetValue((CFNumberRef)obj, kCFNumberFloat32Type, &v); + swapped32 = CFConvertFloat32HostToSwapped(v); + bytes = (uint8_t *)&swapped32; + nbytes = sizeof(float); + marker = kCFBinaryPlistMarkerReal | 2; + } else { + double v; + CFNumberGetValue((CFNumberRef)obj, kCFNumberFloat64Type, &v); + swapped64 = CFConvertFloat64HostToSwapped(v); + bytes = (uint8_t *)&swapped64; + nbytes = sizeof(double); + marker = kCFBinaryPlistMarkerReal | 3; + } + bufferWrite(buf, &marker, 1); + bufferWrite(buf, bytes, nbytes); + } else { + CFNumberType type = _CFNumberGetType2((CFNumberRef)obj); + if (kCFNumberSInt128Type == type) { + CFSInt128Struct s; + CFNumberGetValue((CFNumberRef)obj, kCFNumberSInt128Type, &s); + struct { + int64_t high; + uint64_t low; + } storage; + storage.high = CFSwapInt64HostToBig(s.high); + storage.low = CFSwapInt64HostToBig(s.low); + uint8_t *bytes = (uint8_t *)&storage; + uint8_t marker = kCFBinaryPlistMarkerInt | 4; + CFIndex nbytes = 16; + bufferWrite(buf, &marker, 1); + bufferWrite(buf, bytes, nbytes); + } else { + CFNumberGetValue((CFNumberRef)obj, kCFNumberSInt64Type, &bigint); + _appendInt(buf, bigint); + } + } + } else if (_CFKeyedArchiverUIDGetTypeID() == type) { + _appendUID(buf, (CFKeyedArchiverUIDRef)obj); + } else if (booltype == type) { + uint8_t marker = CFBooleanGetValue((CFBooleanRef)obj) ? kCFBinaryPlistMarkerTrue : kCFBinaryPlistMarkerFalse; + bufferWrite(buf, &marker, 1); + } else if (datatype == type) { + CFIndex count = CFDataGetLength((CFDataRef)obj); + uint8_t marker = (uint8_t)(kCFBinaryPlistMarkerData | (count < 15 ? count : 0xf)); + bufferWrite(buf, &marker, 1); + if (15 <= count) { + _appendInt(buf, (uint64_t)count); + } + bufferWrite(buf, CFDataGetBytePtr((CFDataRef)obj), count); + } else if (datetype == type) { + CFSwappedFloat64 swapped; + uint8_t marker = kCFBinaryPlistMarkerDate; + bufferWrite(buf, &marker, 1); + swapped = CFConvertFloat64HostToSwapped(CFDateGetAbsoluteTime((CFDateRef)obj)); + bufferWrite(buf, (uint8_t *)&swapped, sizeof(swapped)); + } else if (dicttype == type) { + CFIndex count = CFDictionaryGetCount((CFDictionaryRef)obj); + CFPropertyListRef *list, buffer[512]; + uint8_t marker = (uint8_t)(kCFBinaryPlistMarkerDict | (count < 15 ? count : 0xf)); + bufferWrite(buf, &marker, 1); + if (15 <= count) { + _appendInt(buf, (uint64_t)count); + } + list = (count <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, 2 * count * sizeof(CFTypeRef), 0); + CFDictionaryGetKeysAndValues((CFDictionaryRef)obj, list, list + count); + for (idx2 = 0; idx2 < 2 * count; idx2++) { + CFPropertyListRef value = list[idx2]; + uint32_t swapped = 0; + uint8_t *source = (uint8_t *)&swapped; + refnum = (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable, value); + swapped = CFSwapInt32HostToBig((uint32_t)refnum); + bufferWrite(buf, source + sizeof(swapped) - trailer._objectRefSize, trailer._objectRefSize); + } + if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); + } else if (arraytype == type) { + CFIndex count = CFArrayGetCount((CFArrayRef)obj); + CFPropertyListRef *list, buffer[256]; + uint8_t marker = (uint8_t)(kCFBinaryPlistMarkerArray | (count < 15 ? count : 0xf)); + bufferWrite(buf, &marker, 1); + if (15 <= count) { + _appendInt(buf, (uint64_t)count); + } + list = (count <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, count * sizeof(CFTypeRef), 0); + CFArrayGetValues((CFArrayRef)obj, CFRangeMake(0, count), list); + for (idx2 = 0; idx2 < count; idx2++) { + CFPropertyListRef value = list[idx2]; + uint32_t swapped = 0; + uint8_t *source = (uint8_t *)&swapped; + refnum = (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable, value); + swapped = CFSwapInt32HostToBig((uint32_t)refnum); + bufferWrite(buf, source + sizeof(swapped) - trailer._objectRefSize, trailer._objectRefSize); + } + if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); + } else { + CFRelease(objtable); + CFRelease(objlist); + if (buf->error) CFRelease(buf->error); + CFAllocatorDeallocate(kCFAllocatorSystemDefault, buf); + CFAllocatorDeallocate(kCFAllocatorSystemDefault, offsets); + return 0; + } + } + CFRelease(objtable); + CFRelease(objlist); + + length_so_far = buf->written + buf->used; + trailer._offsetTableOffset = CFSwapInt64HostToBig(length_so_far); + trailer._offsetIntSize = 0; + mask = ~(uint64_t)0; + while (length_so_far & mask) { + trailer._offsetIntSize++; + mask = mask << 8; + } + + for (idx = 0; idx < cnt; idx++) { + uint64_t swapped = CFSwapInt64HostToBig(offsets[idx]); + uint8_t *source = (uint8_t *)&swapped; + bufferWrite(buf, source + sizeof(*offsets) - trailer._offsetIntSize, trailer._offsetIntSize); + } + length_so_far += cnt * trailer._offsetIntSize; + + bufferWrite(buf, (uint8_t *)&trailer, sizeof(trailer)); + bufferFlush(buf); + length_so_far += sizeof(trailer); + if (buf->error) { + CFRelease(buf->error); + return 0; + } + CFAllocatorDeallocate(kCFAllocatorSystemDefault, buf); + CFAllocatorDeallocate(kCFAllocatorSystemDefault, offsets); + return (CFIndex)length_so_far; +} + + +#define FAIL_FALSE do { return false; } while (0) +#define FAIL_MAXOFFSET do { return UINT64_MAX; } while (0) + +bool __CFBinaryPlistGetTopLevelInfo(const uint8_t *databytes, uint64_t datalen, uint8_t *marker, uint64_t *offset, CFBinaryPlistTrailer *trailer) { + CFBinaryPlistTrailer trail; + + if ((CFTypeID)-1 == stringtype) { + stringtype = CFStringGetTypeID(); + } + if ((CFTypeID)-1 == datatype) { + datatype = CFDataGetTypeID(); + } + if ((CFTypeID)-1 == numbertype) { + numbertype = CFNumberGetTypeID(); + } + if ((CFTypeID)-1 == booltype) { + booltype = CFBooleanGetTypeID(); + } + if ((CFTypeID)-1 == datetype) { + datetype = CFDateGetTypeID(); + } + if ((CFTypeID)-1 == dicttype) { + dicttype = CFDictionaryGetTypeID(); + } + if ((CFTypeID)-1 == arraytype) { + arraytype = CFArrayGetTypeID(); + } + if ((CFTypeID)-1 == settype) { + settype = CFSetGetTypeID(); + } + if ((CFTypeID)-1 == nulltype) { + nulltype = CFNullGetTypeID(); + } + + if (!databytes || datalen < sizeof(trail) + 8 + 1) FAIL_FALSE; + if (0 != memcmp("bplist00", databytes, 8) && 0 != memcmp("bplist01", databytes, 8)) return false; + memmove(&trail, databytes + datalen - sizeof(trail), sizeof(trail)); + if (trail._unused[0] != 0 || trail._unused[1] != 0 || trail._unused[2] != 0 || trail._unused[3] != 0 || trail._unused[4] != 0 || trail._unused[5] != 0) FAIL_FALSE; + trail._numObjects = CFSwapInt64BigToHost(trail._numObjects); + trail._topObject = CFSwapInt64BigToHost(trail._topObject); + trail._offsetTableOffset = CFSwapInt64BigToHost(trail._offsetTableOffset); + if (LONG_MAX < trail._numObjects) FAIL_FALSE; + if (LONG_MAX < trail._offsetTableOffset) FAIL_FALSE; + if (trail._numObjects < 1) FAIL_FALSE; + if (trail._numObjects <= trail._topObject) FAIL_FALSE; + if (trail._offsetTableOffset < 9) FAIL_FALSE; + if (datalen - sizeof(trail) <= trail._offsetTableOffset) FAIL_FALSE; + if (trail._offsetIntSize < 1) FAIL_FALSE; + if (trail._objectRefSize < 1) FAIL_FALSE; + int32_t err = CF_NO_ERROR; + uint64_t offsetIntSize = trail._offsetIntSize; + uint64_t offsetTableSize = __check_uint64_mul_unsigned_unsigned(trail._numObjects, offsetIntSize, &err); + if (CF_NO_ERROR!= err) FAIL_FALSE; + if (offsetTableSize < 1) FAIL_FALSE; + uint64_t objectDataSize = trail._offsetTableOffset - 8; + uint64_t tmpSum = __check_uint64_add_unsigned_unsigned(8, objectDataSize, &err); + tmpSum = __check_uint64_add_unsigned_unsigned(tmpSum, offsetTableSize, &err); + tmpSum = __check_uint64_add_unsigned_unsigned(tmpSum, sizeof(trail), &err); + if (CF_NO_ERROR != err) FAIL_FALSE; + if (datalen != tmpSum) FAIL_FALSE; + if (trail._objectRefSize < 8 && (1ULL << (8 * trail._objectRefSize)) <= trail._numObjects) FAIL_FALSE; + if (trail._offsetIntSize < 8 && (1ULL << (8 * trail._offsetIntSize)) <= trail._offsetTableOffset) FAIL_FALSE; + const uint8_t *objectsFirstByte; + objectsFirstByte = check_ptr_add(databytes, 8, &err); + if (CF_NO_ERROR != err) FAIL_FALSE; + const uint8_t *offsetsFirstByte = check_ptr_add(databytes, trail._offsetTableOffset, &err); + if (CF_NO_ERROR != err) FAIL_FALSE; + const uint8_t *offsetsLastByte; + offsetsLastByte = check_ptr_add(offsetsFirstByte, offsetTableSize - 1, &err); + if (CF_NO_ERROR != err) FAIL_FALSE; + + const uint8_t *bytesptr = databytes + trail._offsetTableOffset; + uint64_t maxOffset = trail._offsetTableOffset - 1; + for (CFIndex idx = 0; idx < trail._numObjects; idx++) { + uint64_t off = 0; + for (CFIndex idx2 = 0; idx2 < trail._offsetIntSize; idx2++) { + off = (off << 8) + bytesptr[idx2]; + } + if (maxOffset < off) FAIL_FALSE; + bytesptr += trail._offsetIntSize; + } + + bytesptr = databytes + trail._offsetTableOffset + trail._topObject * trail._offsetIntSize; + uint64_t off = 0; + for (CFIndex idx = 0; idx < trail._offsetIntSize; idx++) { + off = (off << 8) + bytesptr[idx]; + } + if (off < 8 || trail._offsetTableOffset <= off) FAIL_FALSE; + if (trailer) *trailer = trail; + if (offset) *offset = off; + if (marker) *marker = *(databytes + off); + return true; +} + +CF_INLINE Boolean _plistIsPrimitive(CFPropertyListRef pl) { + CFTypeID type = __CFGenericTypeID_genericobj_inline(pl); + if (dicttype == type || arraytype == type || settype == type) FAIL_FALSE; + return true; +} + +CF_INLINE bool _readInt(const uint8_t *ptr, const uint8_t *end_byte_ptr, uint64_t *bigint, const uint8_t **newptr) { + if (end_byte_ptr < ptr) FAIL_FALSE; + uint8_t marker = *ptr++; + if ((marker & 0xf0) != kCFBinaryPlistMarkerInt) FAIL_FALSE; + uint64_t cnt = 1 << (marker & 0x0f); + int32_t err = CF_NO_ERROR; + const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1; + if (CF_NO_ERROR != err) FAIL_FALSE; + if (end_byte_ptr < extent) FAIL_FALSE; + // integers are not required to be in the most compact possible representation, but only the last 64 bits are significant currently + *bigint = 0; + for (CFIndex idx = 0; idx < cnt; idx++) { + *bigint = (*bigint << 8) + *ptr++; + } + if (newptr) *newptr = ptr; + return true; +} + +// bytesptr points at a ref +CF_INLINE uint64_t _getOffsetOfRefAt(const uint8_t *databytes, const uint8_t *bytesptr, const CFBinaryPlistTrailer *trailer) { + // *trailer contents are trusted, even for overflows -- was checked when the trailer was parsed; + // this pointer arithmetic and the multiplication was also already done once and checked, + // and the offsetTable was already validated. + const uint8_t *objectsFirstByte = databytes + 8; + const uint8_t *offsetsFirstByte = databytes + trailer->_offsetTableOffset; + if (bytesptr < objectsFirstByte || offsetsFirstByte - trailer->_objectRefSize < bytesptr) FAIL_MAXOFFSET; + + uint64_t ref = 0; + for (CFIndex idx = 0; idx < trailer->_objectRefSize; idx++) { + ref = (ref << 8) + bytesptr[idx]; + } + if (trailer->_numObjects <= ref) FAIL_MAXOFFSET; + + bytesptr = databytes + trailer->_offsetTableOffset + ref * trailer->_offsetIntSize; + uint64_t off = 0; + for (CFIndex idx = 0; idx < trailer->_offsetIntSize; idx++) { + off = (off << 8) + bytesptr[idx]; + } + return off; +} + +static bool __CFBinaryPlistCreateObject2(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFAllocatorRef allocator, CFOptionFlags mutabilityOption, CFMutableDictionaryRef objects, CFMutableSetRef set, CFIndex curDepth, CFPropertyListRef *plist); + +bool __CFBinaryPlistGetOffsetForValueFromArray2(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFIndex idx, uint64_t *offset, CFMutableDictionaryRef objects) { + uint64_t objectsRangeStart = 8, objectsRangeEnd = trailer->_offsetTableOffset - 1; + if (startOffset < objectsRangeStart || objectsRangeEnd < startOffset) FAIL_FALSE; + const uint8_t *ptr = databytes + startOffset; + uint8_t marker = *ptr; + if ((marker & 0xf0) != kCFBinaryPlistMarkerArray) FAIL_FALSE; + int32_t err = CF_NO_ERROR; + ptr = check_ptr_add(ptr, 1, &err); + if (CF_NO_ERROR != err) FAIL_FALSE; + uint64_t cnt = (marker & 0x0f); + if (0xf == cnt) { + uint64_t bigint; + if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE; + if (LONG_MAX < bigint) FAIL_FALSE; + cnt = bigint; + } + if (cnt <= idx) FAIL_FALSE; + size_t byte_cnt = check_size_t_mul(cnt, trailer->_objectRefSize, &err); + if (CF_NO_ERROR != err) FAIL_FALSE; + const uint8_t *extent = check_ptr_add(ptr, byte_cnt, &err) - 1; + if (CF_NO_ERROR != err) FAIL_FALSE; + if (databytes + objectsRangeEnd < extent) FAIL_FALSE; + uint64_t off = _getOffsetOfRefAt(databytes, ptr + idx * trailer->_objectRefSize, trailer); + if (offset) *offset = off; + return true; +} + +bool __CFBinaryPlistGetOffsetForValueFromDictionary2(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFTypeRef key, uint64_t *koffset, uint64_t *voffset, CFMutableDictionaryRef objects) { + if (!key || !_plistIsPrimitive(key)) FAIL_FALSE; + uint64_t objectsRangeStart = 8, objectsRangeEnd = trailer->_offsetTableOffset - 1; + if (startOffset < objectsRangeStart || objectsRangeEnd < startOffset) FAIL_FALSE; + const uint8_t *ptr = databytes + startOffset; + uint8_t marker = *ptr; + if ((marker & 0xf0) != kCFBinaryPlistMarkerDict) FAIL_FALSE; + int32_t err = CF_NO_ERROR; + ptr = check_ptr_add(ptr, 1, &err); + if (CF_NO_ERROR != err) FAIL_FALSE; + uint64_t cnt = (marker & 0x0f); + if (0xf == cnt) { + uint64_t bigint = 0; + if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE; + if (LONG_MAX < bigint) FAIL_FALSE; + cnt = bigint; + } + cnt = check_size_t_mul(cnt, 2, &err); + if (CF_NO_ERROR != err) FAIL_FALSE; + size_t byte_cnt = check_size_t_mul(cnt, trailer->_objectRefSize, &err); + if (CF_NO_ERROR != err) FAIL_FALSE; + const uint8_t *extent = check_ptr_add(ptr, byte_cnt, &err) - 1; + if (CF_NO_ERROR != err) FAIL_FALSE; + if (databytes + objectsRangeEnd < extent) FAIL_FALSE; + CFIndex stringKeyLen = -1; + UniChar ubuffer[16]; + if (__CFGenericTypeID_genericobj_inline(key) == stringtype) { + stringKeyLen = CFStringGetLength((CFStringRef)key); + if (stringKeyLen < 0xf) { + CFStringGetCharacters((CFStringRef)key, CFRangeMake(0, stringKeyLen), ubuffer); + } + } + cnt = cnt / 2; + for (CFIndex idx = 0; idx < cnt; idx++) { + uint64_t off = _getOffsetOfRefAt(databytes, ptr, trailer); + uint8_t marker = *(databytes + off); + CFIndex len = marker & 0x0f; + // if it is a short ascii string in the data, and the key is a string + if ((marker & 0xf0) == kCFBinaryPlistMarkerASCIIString && len < 0xf && stringKeyLen != -1) { + if (len != stringKeyLen) goto miss; + err = CF_NO_ERROR; + const uint8_t *ptr2 = databytes + off; + extent = check_ptr_add(ptr2, len, &err); + if (CF_NO_ERROR != err) FAIL_FALSE; + if (databytes + trailer->_offsetTableOffset <= extent) FAIL_FALSE; + for (CFIndex idx2 = 0; idx2 < stringKeyLen; idx2++) { + if ((UniChar)ptr2[idx2 + 1] != ubuffer[idx2]) goto miss; + } + if (koffset) *koffset = off; + if (voffset) { + off = _getOffsetOfRefAt(databytes, ptr + cnt * trailer->_objectRefSize, trailer); + *voffset = off; + } + return true; + miss:; + } else { + CFPropertyListRef pl = NULL; + if (!__CFBinaryPlistCreateObject2(databytes, datalen, off, trailer, kCFAllocatorSystemDefault, kCFPropertyListImmutable, objects, NULL, 0, &pl) || !_plistIsPrimitive(pl)) { + if (pl) CFRelease(pl); + FAIL_FALSE; + } + if (CFEqual(key, pl)) { + CFRelease(pl); + if (koffset) *koffset = off; + if (voffset) { + off = _getOffsetOfRefAt(databytes, ptr + cnt * trailer->_objectRefSize, trailer); + *voffset = off; + } + return true; + } + CFRelease(pl); + } + ptr += trailer->_objectRefSize; + } + return false; +} + +extern CFArrayRef _CFArrayCreate_ex(CFAllocatorRef allocator, Boolean isMutable, const void **values, CFIndex numValues); +extern CFSetRef _CFSetCreate_ex(CFAllocatorRef allocator, Boolean isMutable, const void **values, CFIndex numValues); +extern CFDictionaryRef _CFDictionaryCreate_ex(CFAllocatorRef allocator, Boolean isMutable, const void **keys, const void **values, CFIndex numValues); + +static bool __CFBinaryPlistCreateObject2(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFAllocatorRef allocator, CFOptionFlags mutabilityOption, CFMutableDictionaryRef objects, CFMutableSetRef set, CFIndex curDepth, CFPropertyListRef *plist) { + + if (objects) { + *plist = CFDictionaryGetValue(objects, (const void *)(uintptr_t)startOffset); + if (*plist) { + CFRetain(*plist); + return true; + } + } + + // at any one invocation of this function, set should contain the offsets in the "path" down to this object + if (set && CFSetContainsValue(set, (const void *)(uintptr_t)startOffset)) return false; + + // databytes is trusted to be at least datalen bytes long + // *trailer contents are trusted, even for overflows -- was checked when the trailer was parsed + uint64_t objectsRangeStart = 8, objectsRangeEnd = trailer->_offsetTableOffset - 1; + if (startOffset < objectsRangeStart || objectsRangeEnd < startOffset) FAIL_FALSE; + + uint64_t off; + CFPropertyListRef *list, buffer[256]; + CFAllocatorRef listAllocator; + + uint8_t marker = *(databytes + startOffset); + switch (marker & 0xf0) { + case kCFBinaryPlistMarkerNull: + switch (marker) { + case kCFBinaryPlistMarkerNull: + *plist = kCFNull; + return true; + case kCFBinaryPlistMarkerFalse: + *plist = CFRetain(kCFBooleanFalse); + return true; + case kCFBinaryPlistMarkerTrue: + *plist = CFRetain(kCFBooleanTrue); + return true; + } + FAIL_FALSE; + case kCFBinaryPlistMarkerInt: + { + const uint8_t *ptr = (databytes + startOffset); + int32_t err = CF_NO_ERROR; + ptr = check_ptr_add(ptr, 1, &err); + if (CF_NO_ERROR != err) FAIL_FALSE; + uint64_t cnt = 1 << (marker & 0x0f); + const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1; + if (CF_NO_ERROR != err) FAIL_FALSE; + if (databytes + objectsRangeEnd < extent) FAIL_FALSE; + if (16 < cnt) FAIL_FALSE; + // in format version '00', 1, 2, and 4-byte integers have to be interpreted as unsigned, + // whereas 8-byte integers are signed (and 16-byte when available) + // negative 1, 2, 4-byte integers are always emitted as 8 bytes in format '00' + uint64_t bigint = 0; + // integers are not required to be in the most compact possible representation, but only the last 64 bits are significant currently + for (CFIndex idx = 0; idx < cnt; idx++) { + bigint = (bigint << 8) + *ptr++; + } + if (8 < cnt) { + CFSInt128Struct val; + val.high = 0; + val.low = bigint; + *plist = CFNumberCreate(allocator, kCFNumberSInt128Type, &val); + } else { + *plist = CFNumberCreate(allocator, kCFNumberSInt64Type, &bigint); + } + // these are always immutable + if (objects && *plist) { + CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist); + } + return (*plist) ? true : false; + } + case kCFBinaryPlistMarkerReal: + switch (marker & 0x0f) { + case 2: { + const uint8_t *ptr = (databytes + startOffset); + int32_t err = CF_NO_ERROR; + ptr = check_ptr_add(ptr, 1, &err); + if (CF_NO_ERROR != err) FAIL_FALSE; + const uint8_t *extent = check_ptr_add(ptr, 4, &err) - 1; + if (CF_NO_ERROR != err) FAIL_FALSE; + if (databytes + objectsRangeEnd < extent) FAIL_FALSE; + CFSwappedFloat32 swapped32; + memmove(&swapped32, ptr, 4); + float f = CFConvertFloat32SwappedToHost(swapped32); + *plist = CFNumberCreate(allocator, kCFNumberFloat32Type, &f); + // these are always immutable + if (objects && *plist) { + CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist); + } + return (*plist) ? true : false; + } + case 3: { + const uint8_t *ptr = (databytes + startOffset); + int32_t err = CF_NO_ERROR; + ptr = check_ptr_add(ptr, 1, &err); + if (CF_NO_ERROR != err) FAIL_FALSE; + const uint8_t *extent = check_ptr_add(ptr, 8, &err) - 1; + if (CF_NO_ERROR != err) FAIL_FALSE; + if (databytes + objectsRangeEnd < extent) FAIL_FALSE; + CFSwappedFloat64 swapped64; + memmove(&swapped64, ptr, 8); + double d = CFConvertFloat64SwappedToHost(swapped64); + *plist = CFNumberCreate(allocator, kCFNumberFloat64Type, &d); + // these are always immutable + if (objects && *plist) { + CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist); + } + return (*plist) ? true : false; + } + } + FAIL_FALSE; + case kCFBinaryPlistMarkerDate & 0xf0: + switch (marker) { + case kCFBinaryPlistMarkerDate: { + const uint8_t *ptr = (databytes + startOffset); + int32_t err = CF_NO_ERROR; + ptr = check_ptr_add(ptr, 1, &err); + if (CF_NO_ERROR != err) FAIL_FALSE; + const uint8_t *extent = check_ptr_add(ptr, 8, &err) - 1; + if (CF_NO_ERROR != err) FAIL_FALSE; + if (databytes + objectsRangeEnd < extent) FAIL_FALSE; + CFSwappedFloat64 swapped64; + memmove(&swapped64, ptr, 8); + double d = CFConvertFloat64SwappedToHost(swapped64); + *plist = CFDateCreate(allocator, d); + // these are always immutable + if (objects && *plist) { + CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist); + } + return (*plist) ? true : false; + } + } + FAIL_FALSE; + case kCFBinaryPlistMarkerData: { + const uint8_t *ptr = databytes + startOffset; + int32_t err = CF_NO_ERROR; + ptr = check_ptr_add(ptr, 1, &err); + if (CF_NO_ERROR != err) FAIL_FALSE; + CFIndex cnt = marker & 0x0f; + if (0xf == cnt) { + uint64_t bigint = 0; + if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE; + if (LONG_MAX < bigint) FAIL_FALSE; + cnt = (CFIndex)bigint; + } + const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1; + if (CF_NO_ERROR != err) FAIL_FALSE; + if (databytes + objectsRangeEnd < extent) FAIL_FALSE; + if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) { + *plist = CFDataCreateMutable(allocator, 0); + if (*plist) CFDataAppendBytes((CFMutableDataRef)*plist, ptr, cnt); + } else { + *plist = CFDataCreate(allocator, ptr, cnt); + } + if (objects && *plist && (mutabilityOption != kCFPropertyListMutableContainersAndLeaves)) { + CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist); + } + return (*plist) ? true : false; + } + case kCFBinaryPlistMarkerASCIIString: { + const uint8_t *ptr = databytes + startOffset; + int32_t err = CF_NO_ERROR; + ptr = check_ptr_add(ptr, 1, &err); + if (CF_NO_ERROR != err) FAIL_FALSE; + CFIndex cnt = marker & 0x0f; + if (0xf == cnt) { + uint64_t bigint = 0; + if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE; + if (LONG_MAX < bigint) FAIL_FALSE; + cnt = (CFIndex)bigint; + } + const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1; + if (CF_NO_ERROR != err) FAIL_FALSE; + if (databytes + objectsRangeEnd < extent) FAIL_FALSE; + if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) { + CFStringRef str = CFStringCreateWithBytes(allocator, ptr, cnt, kCFStringEncodingASCII, false); + *plist = str ? CFStringCreateMutableCopy(allocator, 0, str) : NULL; + if (str) CFRelease(str); + } else { + *plist = CFStringCreateWithBytes(allocator, ptr, cnt, kCFStringEncodingASCII, false); + } + if (objects && *plist && (mutabilityOption != kCFPropertyListMutableContainersAndLeaves)) { + CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist); + } + return (*plist) ? true : false; + } + case kCFBinaryPlistMarkerUnicode16String: { + const uint8_t *ptr = databytes + startOffset; + int32_t err = CF_NO_ERROR; + ptr = check_ptr_add(ptr, 1, &err); + if (CF_NO_ERROR != err) FAIL_FALSE; + CFIndex cnt = marker & 0x0f; + if (0xf == cnt) { + uint64_t bigint = 0; + if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE; + if (LONG_MAX < bigint) FAIL_FALSE; + cnt = (CFIndex)bigint; + } + const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1; + extent = check_ptr_add(extent, cnt, &err); // 2 bytes per character + if (CF_NO_ERROR != err) FAIL_FALSE; + if (databytes + objectsRangeEnd < extent) FAIL_FALSE; + size_t byte_cnt = check_size_t_mul(cnt, sizeof(UniChar), &err); + if (CF_NO_ERROR != err) FAIL_FALSE; + UniChar *chars = (UniChar *)CFAllocatorAllocate(allocator, byte_cnt, 0); + if (!chars) FAIL_FALSE; + memmove(chars, ptr, byte_cnt); + for (CFIndex idx = 0; idx < cnt; idx++) { + chars[idx] = CFSwapInt16BigToHost(chars[idx]); + } + if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) { + CFStringRef str = CFStringCreateWithCharactersNoCopy(allocator, chars, cnt, allocator); + *plist = str ? CFStringCreateMutableCopy(allocator, 0, str) : NULL; + if (str) CFRelease(str); + } else { + *plist = CFStringCreateWithCharactersNoCopy(allocator, chars, cnt, allocator); + } + if (objects && *plist && (mutabilityOption != kCFPropertyListMutableContainersAndLeaves)) { + CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist); + } + return (*plist) ? true : false; + } + case kCFBinaryPlistMarkerUID: { + const uint8_t *ptr = databytes + startOffset; + int32_t err = CF_NO_ERROR; + ptr = check_ptr_add(ptr, 1, &err); + if (CF_NO_ERROR != err) FAIL_FALSE; + CFIndex cnt = (marker & 0x0f) + 1; + const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1; + if (CF_NO_ERROR != err) FAIL_FALSE; + if (databytes + objectsRangeEnd < extent) FAIL_FALSE; + // uids are not required to be in the most compact possible representation, but only the last 64 bits are significant currently + uint64_t bigint = 0; + for (CFIndex idx = 0; idx < cnt; idx++) { + bigint = (bigint << 8) + *ptr++; + } + if (UINT32_MAX < bigint) FAIL_FALSE; + *plist = _CFKeyedArchiverUIDCreate(allocator, (uint32_t)bigint); + // these are always immutable + if (objects && *plist) { + CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist); + } + return (*plist) ? true : false; + } + case kCFBinaryPlistMarkerArray: + case kCFBinaryPlistMarkerSet: { + const uint8_t *ptr = databytes + startOffset; + int32_t err = CF_NO_ERROR; + ptr = check_ptr_add(ptr, 1, &err); + if (CF_NO_ERROR != err) FAIL_FALSE; + CFIndex cnt = marker & 0x0f; + if (0xf == cnt) { + uint64_t bigint = 0; + if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE; + if (LONG_MAX < bigint) FAIL_FALSE; + cnt = (CFIndex)bigint; + } + size_t byte_cnt = check_size_t_mul(cnt, trailer->_objectRefSize, &err); + if (CF_NO_ERROR != err) FAIL_FALSE; + const uint8_t *extent = check_ptr_add(ptr, byte_cnt, &err) - 1; + if (CF_NO_ERROR != err) FAIL_FALSE; + if (databytes + objectsRangeEnd < extent) FAIL_FALSE; + byte_cnt = check_size_t_mul(cnt, sizeof(CFPropertyListRef), &err); + if (CF_NO_ERROR != err) FAIL_FALSE; + list = (cnt <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, byte_cnt, __kCFAllocatorGCScannedMemory); + listAllocator = (list == buffer ? kCFAllocatorNull : kCFAllocatorSystemDefault); + if (!list) FAIL_FALSE; + Boolean madeSet = false; + if (!set && 15 < curDepth) { + set = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, NULL); + madeSet = set ? true : false; + } + if (set) CFSetAddValue(set, (const void *)(uintptr_t)startOffset); + for (CFIndex idx = 0; idx < cnt; idx++) { + CFPropertyListRef pl; + off = _getOffsetOfRefAt(databytes, ptr, trailer); + if (!__CFBinaryPlistCreateObject2(databytes, datalen, off, trailer, allocator, mutabilityOption, objects, set, curDepth + 1, &pl)) { + if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + while (idx--) { + CFRelease(list[idx]); + } + } + if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); + FAIL_FALSE; + } + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + CF_WRITE_BARRIER_BASE_ASSIGN(listAllocator, list, list[idx], CFMakeCollectable(pl)); + } else { + list[idx] = pl; + } + ptr += trailer->_objectRefSize; + } + if (set) CFSetRemoveValue(set, (const void *)(uintptr_t)startOffset); + if (madeSet) { + CFRelease(set); + set = NULL; + } + if ((marker & 0xf0) == kCFBinaryPlistMarkerArray) { + *plist = _CFArrayCreate_ex(allocator, (mutabilityOption != kCFPropertyListImmutable), list, cnt); + } else { + *plist = _CFSetCreate_ex(allocator, (mutabilityOption != kCFPropertyListImmutable), list, cnt); + } + if (objects && *plist && (mutabilityOption == kCFPropertyListImmutable)) { + CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist); + } + if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); + return (*plist) ? true : false; + } + case kCFBinaryPlistMarkerDict: { + const uint8_t *ptr = databytes + startOffset; + int32_t err = CF_NO_ERROR; + ptr = check_ptr_add(ptr, 1, &err); + if (CF_NO_ERROR != err) FAIL_FALSE; + CFIndex cnt = marker & 0x0f; + if (0xf == cnt) { + uint64_t bigint = 0; + if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE; + if (LONG_MAX < bigint) FAIL_FALSE; + cnt = (CFIndex)bigint; + } + cnt = (CFIndex)check_size_t_mul(cnt, 2, &err); + if (CF_NO_ERROR != err) FAIL_FALSE; + size_t byte_cnt = check_size_t_mul(cnt, trailer->_objectRefSize, &err); + if (CF_NO_ERROR != err) FAIL_FALSE; + const uint8_t *extent = check_ptr_add(ptr, byte_cnt, &err) - 1; + if (CF_NO_ERROR != err) FAIL_FALSE; + if (databytes + objectsRangeEnd < extent) FAIL_FALSE; + byte_cnt = check_size_t_mul(cnt, sizeof(CFPropertyListRef), &err); + if (CF_NO_ERROR != err) FAIL_FALSE; + list = (cnt <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, byte_cnt, __kCFAllocatorGCScannedMemory); + listAllocator = (list == buffer ? kCFAllocatorNull : kCFAllocatorSystemDefault); + if (!list) FAIL_FALSE; + Boolean madeSet = false; + if (!set && 15 < curDepth) { + set = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, NULL); + madeSet = set ? true : false; + } + if (set) CFSetAddValue(set, (const void *)(uintptr_t)startOffset); + for (CFIndex idx = 0; idx < cnt; idx++) { + CFPropertyListRef pl = NULL; + off = _getOffsetOfRefAt(databytes, ptr, trailer); + if (!__CFBinaryPlistCreateObject2(databytes, datalen, off, trailer, allocator, mutabilityOption, objects, set, curDepth + 1, &pl) || (idx < cnt / 2 && !_plistIsPrimitive(pl))) { + if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + if (pl) CFRelease(pl); + while (idx--) { + CFRelease(list[idx]); + } + } + if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); + FAIL_FALSE; + } + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + CF_WRITE_BARRIER_BASE_ASSIGN(listAllocator, list, list[idx], CFMakeCollectable(pl)); + } else { + list[idx] = pl; + } + ptr += trailer->_objectRefSize; + } + if (set) CFSetRemoveValue(set, (const void *)(uintptr_t)startOffset); + if (madeSet) { + CFRelease(set); + set = NULL; + } + *plist = _CFDictionaryCreate_ex(allocator, (mutabilityOption != kCFPropertyListImmutable), list, list + cnt / 2, cnt / 2); + if (objects && *plist && (mutabilityOption == kCFPropertyListImmutable)) { + CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist); + } + if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); + return (*plist) ? true : false; + } + } + FAIL_FALSE; +} + +bool __CFBinaryPlistCreateObject(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFAllocatorRef allocator, CFOptionFlags mutabilityOption, CFMutableDictionaryRef objects, CFPropertyListRef *plist) { + // for compatibility with Foundation's use, need to leave this here + return __CFBinaryPlistCreateObject2(databytes, datalen, startOffset, trailer, allocator, mutabilityOption, objects, NULL, 0, plist); +} + +__private_extern__ bool __CFTryParseBinaryPlist(CFAllocatorRef allocator, CFDataRef data, CFOptionFlags option, CFPropertyListRef *plist, CFStringRef *errorString) { + uint8_t marker; + CFBinaryPlistTrailer trailer; + uint64_t offset; + const uint8_t *databytes = CFDataGetBytePtr(data); + uint64_t datalen = CFDataGetLength(data); + + if (8 <= datalen && __CFBinaryPlistGetTopLevelInfo(databytes, datalen, &marker, &offset, &trailer)) { + // FALSE: We know for binary plist parsing that the result objects will be retained + // by their containing collections as the parsing proceeds, so we do not need + // to use retaining callbacks for the objects map in this case. WHY: the file might + // be malformed and contain hash-equal keys for the same dictionary (for example) + // and the later key will cause the previous one to be released when we set the second + // in the dictionary. + CFMutableDictionaryRef objects = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks); + _CFDictionarySetCapacity(objects, 4000); + CFPropertyListRef pl = NULL; + if (__CFBinaryPlistCreateObject2(databytes, datalen, offset, &trailer, allocator, option, objects, NULL, 0, &pl)) { + if (plist) *plist = pl; + } else { + if (plist) *plist = NULL; + if (errorString) *errorString = (CFStringRef)CFRetain(CFSTR("binary data is corrupt")); + } + CFRelease(objects); + return true; + } + return false; +} +
diff --git a/CoreFoundation/CFBitVector.c b/CoreFoundation/CFBitVector.c new file mode 100644 index 0000000..56a477f --- /dev/null +++ b/CoreFoundation/CFBitVector.c
@@ -0,0 +1,556 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFBitVector.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include <CoreFoundation/CFBitVector.h> +#include "CFInternal.h" +#include <string.h> + +/* The bucket type must be unsigned, at least one byte in size, and + a power of 2 in number of bits; bits are numbered from 0 from left + to right (bit 0 is the most significant) */ +typedef uint8_t __CFBitVectorBucket; + +enum { + __CF_BITS_PER_BYTE = 8 +}; + +enum { + __CF_BITS_PER_BUCKET = (__CF_BITS_PER_BYTE * sizeof(__CFBitVectorBucket)) +}; + +CF_INLINE CFIndex __CFBitVectorRoundUpCapacity(CFIndex capacity) { + return ((capacity + 63) / 64) * 64; +} + +CF_INLINE CFIndex __CFBitVectorNumBucketsForCapacity(CFIndex capacity) { + return (capacity + __CF_BITS_PER_BUCKET - 1) / __CF_BITS_PER_BUCKET; +} + +struct __CFBitVector { + CFRuntimeBase _base; + CFIndex _count; /* number of bits */ + CFIndex _capacity; /* maximum number of bits */ + __CFBitVectorBucket *_buckets; +}; + +CF_INLINE UInt32 __CFBitVectorMutableVariety(const void *cf) { + return __CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 3, 2); +} + +CF_INLINE void __CFBitVectorSetMutableVariety(void *cf, UInt32 v) { + __CFBitfieldSetValue(((CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 3, 2, v); +} + +CF_INLINE UInt32 __CFBitVectorMutableVarietyFromFlags(UInt32 flags) { + return __CFBitfieldGetValue(flags, 1, 0); +} + +// ensure that uses of these inlines are correct, bytes vs. buckets vs. bits +CF_INLINE CFIndex __CFBitVectorCount(CFBitVectorRef bv) { + return bv->_count; +} + +CF_INLINE void __CFBitVectorSetCount(CFMutableBitVectorRef bv, CFIndex v) { + bv->_count = v; +} + +CF_INLINE CFIndex __CFBitVectorCapacity(CFBitVectorRef bv) { + return bv->_capacity; +} + +CF_INLINE void __CFBitVectorSetCapacity(CFMutableBitVectorRef bv, CFIndex v) { + bv->_capacity = v; +} + +CF_INLINE CFIndex __CFBitVectorNumBucketsUsed(CFBitVectorRef bv) { + return bv->_count / __CF_BITS_PER_BUCKET + 1; +} + +CF_INLINE void __CFBitVectorSetNumBucketsUsed(CFMutableBitVectorRef bv, CFIndex v) { + /* for a CFBitVector, _bucketsUsed == _count / __CF_BITS_PER_BUCKET + 1 */ +} + +CF_INLINE CFIndex __CFBitVectorNumBuckets(CFBitVectorRef bv) { + return bv->_capacity / __CF_BITS_PER_BUCKET + 1; +} + +CF_INLINE void __CFBitVectorSetNumBuckets(CFMutableBitVectorRef bv, CFIndex v) { + /* for a CFBitVector, _bucketsNum == _capacity / __CF_BITS_PER_BUCKET + 1 */ +} + +static __CFBitVectorBucket __CFBitBucketMask(CFIndex bottomBit, CFIndex topBit) { + CFIndex shiftL = __CF_BITS_PER_BUCKET - topBit + bottomBit - 1; + __CFBitVectorBucket result = ~(__CFBitVectorBucket)0; + result = (result << shiftL); + result = (result >> bottomBit); + return result; +} + +CF_INLINE CFBit __CFBitVectorBit(__CFBitVectorBucket *buckets, CFIndex idx) { + CFIndex bucketIdx = idx / __CF_BITS_PER_BUCKET; + CFIndex bitOfBucket = idx & (__CF_BITS_PER_BUCKET - 1); + return (buckets[bucketIdx] >> (__CF_BITS_PER_BUCKET - 1 - bitOfBucket)) & 0x1; +} + +CF_INLINE void __CFSetBitVectorBit(__CFBitVectorBucket *buckets, CFIndex idx, CFBit value) { + CFIndex bucketIdx = idx / __CF_BITS_PER_BUCKET; + CFIndex bitOfBucket = idx & (__CF_BITS_PER_BUCKET - 1); + if (value) { + buckets[bucketIdx] |= (1 << (__CF_BITS_PER_BUCKET - 1 - bitOfBucket)); + } else { + buckets[bucketIdx] &= ~(1 << (__CF_BITS_PER_BUCKET - 1 - bitOfBucket)); + } +} + +CF_INLINE void __CFFlipBitVectorBit(__CFBitVectorBucket *buckets, CFIndex idx) { + CFIndex bucketIdx = idx / __CF_BITS_PER_BUCKET; + CFIndex bitOfBucket = idx & (__CF_BITS_PER_BUCKET - 1); + buckets[bucketIdx] ^= (1 << (__CF_BITS_PER_BUCKET - 1 - bitOfBucket)); +} + +#if defined(DEBUG) +CF_INLINE void __CFBitVectorValidateRange(CFBitVectorRef bv, CFRange range, const char *func) { + CFAssert2(0 <= range.location && range.location < __CFBitVectorCount(bv), __kCFLogAssertion, "%s(): range.location index (%d) out of bounds", func, range.location); + CFAssert2(0 <= range.length, __kCFLogAssertion, "%s(): range.length (%d) cannot be less than zero", func, range.length); + CFAssert2(range.location + range.length <= __CFBitVectorCount(bv), __kCFLogAssertion, "%s(): ending index (%d) out of bounds", func, range.location + range.length); +} +#else +#define __CFBitVectorValidateRange(bf,r,f) +#endif + +static Boolean __CFBitVectorEqual(CFTypeRef cf1, CFTypeRef cf2) { + CFBitVectorRef bv1 = (CFBitVectorRef)cf1; + CFBitVectorRef bv2 = (CFBitVectorRef)cf2; + CFIndex idx, cnt; + cnt = __CFBitVectorCount(bv1); + if (cnt != __CFBitVectorCount(bv2)) return false; + if (0 == cnt) return true; + for (idx = 0; idx < (cnt / __CF_BITS_PER_BUCKET) + 1; idx++) { + __CFBitVectorBucket val1 = bv1->_buckets[idx]; + __CFBitVectorBucket val2 = bv2->_buckets[idx]; + if (val1 != val2) return false; + } + return true; +} + +static CFHashCode __CFBitVectorHash(CFTypeRef cf) { + CFBitVectorRef bv = (CFBitVectorRef)cf; + return __CFBitVectorCount(bv); +} + +static CFStringRef __CFBitVectorCopyDescription(CFTypeRef cf) { + CFBitVectorRef bv = (CFBitVectorRef)cf; + CFMutableStringRef result; + CFIndex idx, cnt; + __CFBitVectorBucket *buckets; + cnt = __CFBitVectorCount(bv); + buckets = bv->_buckets; + result = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); + CFStringAppendFormat(result, NULL, CFSTR("<CFBitVector %p [%p]>{count = %u, capacity = %u, objects = (\n"), cf, CFGetAllocator(bv), cnt, __CFBitVectorCapacity(bv)); + for (idx = 0; idx < (cnt / 64); idx++) { /* Print groups of 64 */ + CFIndex idx2; + CFStringAppendFormat(result, NULL, CFSTR("\t%u : "), (idx * 64)); + for (idx2 = 0; idx2 < 64; idx2 += 4) { + CFIndex bucketIdx = (idx << 6) + idx2; + CFStringAppendFormat(result, NULL, CFSTR("%d%d%d%d"), + __CFBitVectorBit(buckets, bucketIdx + 0), + __CFBitVectorBit(buckets, bucketIdx + 1), + __CFBitVectorBit(buckets, bucketIdx + 2), + __CFBitVectorBit(buckets, bucketIdx + 3)); + } + CFStringAppend(result, CFSTR("\n")); + } + if (idx * 64 < cnt) { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : "), (idx * 64)); + for (idx = (idx * 64); idx < cnt; idx++) { /* Print remainder */ + CFStringAppendFormat(result, NULL, CFSTR("%d"), __CFBitVectorBit(buckets, idx)); + } + } + CFStringAppend(result, CFSTR("\n)}")); + return result; +} + +enum { + kCFBitVectorImmutable = 0x0, /* unchangable and fixed capacity; default */ + kCFBitVectorMutable = 0x1, /* changeable and variable capacity */ + kCFBitVectorFixedMutable = 0x3 /* changeable and fixed capacity */ +}; + +static void __CFBitVectorDeallocate(CFTypeRef cf) { + CFMutableBitVectorRef bv = (CFMutableBitVectorRef)cf; + CFAllocatorRef allocator = CFGetAllocator(bv); + if (__CFBitVectorMutableVariety(bv) == kCFBitVectorMutable) { + _CFAllocatorDeallocateGC(allocator, bv->_buckets); + } +} + +static CFTypeID __kCFBitVectorTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFBitVectorClass = { + _kCFRuntimeScannedObject, + "CFBitVector", + NULL, // init + NULL, // copy + __CFBitVectorDeallocate, + __CFBitVectorEqual, + __CFBitVectorHash, + NULL, // + __CFBitVectorCopyDescription +}; + +__private_extern__ void __CFBitVectorInitialize(void) { + __kCFBitVectorTypeID = _CFRuntimeRegisterClass(&__CFBitVectorClass); +} + +CFTypeID CFBitVectorGetTypeID(void) { + return __kCFBitVectorTypeID; +} + +static CFMutableBitVectorRef __CFBitVectorInit(CFAllocatorRef allocator, CFOptionFlags flags, CFIndex capacity, const uint8_t *bytes, CFIndex numBits) { + CFMutableBitVectorRef memory; + CFIndex size; + CFAssert2(0 <= capacity, __kCFLogAssertion, "%s(): capacity (%d) cannot be less than zero", __PRETTY_FUNCTION__, capacity); + CFAssert3(kCFBitVectorFixedMutable != __CFBitVectorMutableVarietyFromFlags(flags) || numBits <= capacity, __kCFLogAssertion, "%s(): for fixed mutable bit vectors, capacity (%d) must be greater than or equal to number of initial elements (%d)", __PRETTY_FUNCTION__, capacity, numBits); + CFAssert2(0 <= numBits, __kCFLogAssertion, "%s(): numValues (%d) cannot be less than zero", __PRETTY_FUNCTION__, numBits); + size = sizeof(struct __CFBitVector) - sizeof(CFRuntimeBase); + if (__CFBitVectorMutableVarietyFromFlags(flags) != kCFBitVectorMutable) + size += sizeof(__CFBitVectorBucket) * __CFBitVectorNumBucketsForCapacity(capacity); + memory = (CFMutableBitVectorRef)_CFRuntimeCreateInstance(allocator, __kCFBitVectorTypeID, size, NULL); + if (NULL == memory) { + return NULL; + } + switch (__CFBitVectorMutableVarietyFromFlags(flags)) { + case kCFBitVectorMutable: + __CFBitVectorSetCapacity(memory, __CFBitVectorRoundUpCapacity(1)); + __CFBitVectorSetNumBuckets(memory, __CFBitVectorNumBucketsForCapacity(__CFBitVectorRoundUpCapacity(1))); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, memory, memory->_buckets, _CFAllocatorAllocateGC(allocator, __CFBitVectorNumBuckets(memory) * sizeof(__CFBitVectorBucket), 0)); + if (__CFOASafe) __CFSetLastAllocationEventName(memory->_buckets, "CFBitVector (store)"); + if (NULL == memory->_buckets) { + CFRelease(memory); + return NULL; + } + break; + case kCFBitVectorFixedMutable: + case kCFBitVectorImmutable: + /* Don't round up capacity */ + __CFBitVectorSetCapacity(memory, capacity); + __CFBitVectorSetNumBuckets(memory, __CFBitVectorNumBucketsForCapacity(capacity)); + memory->_buckets = (__CFBitVectorBucket *)((int8_t *)memory + sizeof(struct __CFBitVector)); + break; + } + __CFBitVectorSetNumBucketsUsed(memory, numBits / __CF_BITS_PER_BUCKET + 1); + __CFBitVectorSetCount(memory, numBits); + if (bytes) { + /* This move is possible because bits are numbered from 0 on the left */ + memmove(memory->_buckets, bytes, (numBits + __CF_BITS_PER_BYTE - 1) / __CF_BITS_PER_BYTE); + } + __CFBitVectorSetMutableVariety(memory, __CFBitVectorMutableVarietyFromFlags(flags)); + return memory; +} + +CFBitVectorRef CFBitVectorCreate(CFAllocatorRef allocator, const uint8_t *bytes, CFIndex numBits) { + return __CFBitVectorInit(allocator, kCFBitVectorImmutable, numBits, bytes, numBits); +} + +CFMutableBitVectorRef CFBitVectorCreateMutable(CFAllocatorRef allocator, CFIndex capacity) { + return __CFBitVectorInit(allocator, (0 == capacity) ? kCFBitVectorMutable : kCFBitVectorFixedMutable, capacity, NULL, 0); +} + +CFBitVectorRef CFBitVectorCreateCopy(CFAllocatorRef allocator, CFBitVectorRef bv) { + __CFGenericValidateType(bv, __kCFBitVectorTypeID); + return __CFBitVectorInit(allocator, kCFBitVectorImmutable, __CFBitVectorCount(bv), (const uint8_t *)bv->_buckets, __CFBitVectorCount(bv)); +} + +CFMutableBitVectorRef CFBitVectorCreateMutableCopy(CFAllocatorRef allocator, CFIndex capacity, CFBitVectorRef bv) { + __CFGenericValidateType(bv, __kCFBitVectorTypeID); + return __CFBitVectorInit(allocator, (0 == capacity) ? kCFBitVectorMutable : kCFBitVectorFixedMutable, capacity, (const uint8_t *)bv->_buckets, __CFBitVectorCount(bv)); +} + +CFIndex CFBitVectorGetCount(CFBitVectorRef bv) { + __CFGenericValidateType(bv, __kCFBitVectorTypeID); + return __CFBitVectorCount(bv); +} + +typedef __CFBitVectorBucket (*__CFInternalMapper)(__CFBitVectorBucket bucketValue, __CFBitVectorBucket bucketValueMask, void *context); + +static void __CFBitVectorInternalMap(CFMutableBitVectorRef bv, CFRange range, __CFInternalMapper mapper, void *context) { + CFIndex bucketIdx, bitOfBucket; + CFIndex nBuckets; + __CFBitVectorBucket bucketValMask, newBucketVal; + if (0 == range.length) return; + bucketIdx = range.location / __CF_BITS_PER_BUCKET; + bitOfBucket = range.location & (__CF_BITS_PER_BUCKET - 1); + /* Follow usual pattern of ramping up to a bit bucket boundary ...*/ + if (bitOfBucket + range.length < __CF_BITS_PER_BUCKET) { + bucketValMask = __CFBitBucketMask(bitOfBucket, bitOfBucket + range.length - 1); + range.length = 0; + } else { + bucketValMask = __CFBitBucketMask(bitOfBucket, __CF_BITS_PER_BUCKET - 1); + range.length -= __CF_BITS_PER_BUCKET - bitOfBucket; + } + newBucketVal = mapper(bv->_buckets[bucketIdx], bucketValMask, context); + bv->_buckets[bucketIdx] = (bv->_buckets[bucketIdx] & ~bucketValMask) | (newBucketVal & bucketValMask); + bucketIdx++; + /* ... clipping along with entire bit buckets ... */ + nBuckets = range.length / __CF_BITS_PER_BUCKET; + range.length -= nBuckets * __CF_BITS_PER_BUCKET; + while (nBuckets--) { + newBucketVal = mapper(bv->_buckets[bucketIdx], ~0, context); + bv->_buckets[bucketIdx] = newBucketVal; + bucketIdx++; + } + /* ... and ramping down with the last fragmentary bit bucket. */ + if (0 != range.length) { + bucketValMask = __CFBitBucketMask(0, range.length - 1); + newBucketVal = mapper(bv->_buckets[bucketIdx], bucketValMask, context); + bv->_buckets[bucketIdx] = (bv->_buckets[bucketIdx] & ~bucketValMask) | (newBucketVal & bucketValMask); + } +} + +struct _occursContext { + CFBit value; + CFIndex count; +}; + +static __CFBitVectorBucket __CFBitVectorCountBits(__CFBitVectorBucket bucketValue, __CFBitVectorBucket bucketValueMask, struct _occursContext *context) { + static const __CFBitVectorBucket __CFNibbleBitCount[16] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}; + __CFBitVectorBucket val; + CFIndex idx; + val = (context->value) ? (bucketValue & bucketValueMask) : (~bucketValue & bucketValueMask); + for (idx = 0; idx < (CFIndex)sizeof(__CFBitVectorBucket) * 2; idx++) { + context->count += __CFNibbleBitCount[val & 0xF]; + val = val >> 4; + } + return bucketValue; +} + +CFIndex CFBitVectorGetCountOfBit(CFBitVectorRef bv, CFRange range, CFBit value) { + struct _occursContext context; + __CFGenericValidateType(bv, __kCFBitVectorTypeID); + __CFBitVectorValidateRange(bv, range, __PRETTY_FUNCTION__); + if (0 == range.length) return 0; + context.value = value; + context.count = 0; + __CFBitVectorInternalMap((CFMutableBitVectorRef)bv, range, (__CFInternalMapper)__CFBitVectorCountBits, &context); + return context.count; +} + +Boolean CFBitVectorContainsBit(CFBitVectorRef bv, CFRange range, CFBit value) { + __CFGenericValidateType(bv, __kCFBitVectorTypeID); + __CFBitVectorValidateRange(bv, range, __PRETTY_FUNCTION__); + return (CFBitVectorGetCountOfBit(bv, range, value) != 0) ? true : false; +} + +CFBit CFBitVectorGetBitAtIndex(CFBitVectorRef bv, CFIndex idx) { + __CFGenericValidateType(bv, __kCFBitVectorTypeID); + CFAssert2(0 <= idx && idx < __CFBitVectorCount(bv), __kCFLogAssertion, "%s(): index (%d) out of bounds", __PRETTY_FUNCTION__, idx); + return __CFBitVectorBit(bv->_buckets, idx); +} + +struct _getBitsContext { + uint8_t *curByte; + CFIndex initBits; /* Bits to extract off the front for the prev. byte */ + CFIndex totalBits; /* This is for stopping at the end */ + bool ignoreFirstInitBits; +}; + +static __CFBitVectorBucket __CFBitVectorGetBits(__CFBitVectorBucket bucketValue, __CFBitVectorBucket bucketValueMask, void *ctx) { + struct _getBitsContext *context = (struct _getBitsContext *)ctx; + __CFBitVectorBucket val; + CFIndex nBits; + val = bucketValue & bucketValueMask; + nBits = __CFMin(__CF_BITS_PER_BUCKET - context->initBits, context->totalBits); + /* First initBits bits go in *curByte ... */ + if (0 < context->initBits) { + if (!context->ignoreFirstInitBits) { + *context->curByte |= (uint8_t)(val >> (__CF_BITS_PER_BUCKET - context->initBits)); + context->curByte++; + context->totalBits -= context->initBits; + context->ignoreFirstInitBits = false; + } + val <<= context->initBits; + } + /* ... then next groups of __CF_BITS_PER_BYTE go in *curByte ... */ + while (__CF_BITS_PER_BYTE <= nBits) { + *context->curByte = (uint8_t)(val >> (__CF_BITS_PER_BUCKET - __CF_BITS_PER_BYTE)); + context->curByte++; + context->totalBits -= context->initBits; + nBits -= __CF_BITS_PER_BYTE; + val <<= __CF_BITS_PER_BYTE; + } + /* ... then remaining bits go in *curByte */ + if (0 < nBits) { + *context->curByte = (uint8_t)(val >> (__CF_BITS_PER_BUCKET - __CF_BITS_PER_BYTE)); + context->totalBits -= nBits; + } + return bucketValue; +} + +void CFBitVectorGetBits(CFBitVectorRef bv, CFRange range, uint8_t *bytes) { + struct _getBitsContext context; + __CFGenericValidateType(bv, __kCFBitVectorTypeID); + __CFBitVectorValidateRange(bv, range, __PRETTY_FUNCTION__); + if (0 == range.length) return; + context.curByte = bytes; + context.initBits = range.location & (__CF_BITS_PER_BUCKET - 1); + context.totalBits = range.length; + context.ignoreFirstInitBits = true; + __CFBitVectorInternalMap((CFMutableBitVectorRef)bv, range, __CFBitVectorGetBits, &context); +} + +CFIndex CFBitVectorGetFirstIndexOfBit(CFBitVectorRef bv, CFRange range, CFBit value) { + CFIndex idx; + __CFGenericValidateType(bv, __kCFBitVectorTypeID); + __CFBitVectorValidateRange(bv, range, __PRETTY_FUNCTION__); + for (idx = 0; idx < range.length; idx++) { + if (value == CFBitVectorGetBitAtIndex(bv, range.location + idx)) { + return range.location + idx; + } + } + return kCFNotFound; +} + +CFIndex CFBitVectorGetLastIndexOfBit(CFBitVectorRef bv, CFRange range, CFBit value) { + CFIndex idx; + __CFGenericValidateType(bv, __kCFBitVectorTypeID); + __CFBitVectorValidateRange(bv, range, __PRETTY_FUNCTION__); + for (idx = range.length; idx--;) { + if (value == CFBitVectorGetBitAtIndex(bv, range.location + idx)) { + return range.location + idx; + } + } + return kCFNotFound; +} + +static void __CFBitVectorGrow(CFMutableBitVectorRef bv, CFIndex numNewValues) { + CFIndex oldCount = __CFBitVectorCount(bv); + CFIndex capacity = __CFBitVectorRoundUpCapacity(oldCount + numNewValues); + CFAllocatorRef allocator = CFGetAllocator(bv); + __CFBitVectorSetCapacity(bv, capacity); + __CFBitVectorSetNumBuckets(bv, __CFBitVectorNumBucketsForCapacity(capacity)); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, bv, bv->_buckets, CFAllocatorReallocate(allocator, bv->_buckets, __CFBitVectorNumBuckets(bv) * sizeof(__CFBitVectorBucket), 0)); + if (__CFOASafe) __CFSetLastAllocationEventName(bv->_buckets, "CFBitVector (store)"); + if (NULL == bv->_buckets) HALT; +} + +static __CFBitVectorBucket __CFBitVectorZeroBits(__CFBitVectorBucket bucketValue, __CFBitVectorBucket bucketValueMask, void *context) { + return 0; +} + +static __CFBitVectorBucket __CFBitVectorOneBits(__CFBitVectorBucket bucketValue, __CFBitVectorBucket bucketValueMask, void *context) { + return ~(__CFBitVectorBucket)0; +} + +void CFBitVectorSetCount(CFMutableBitVectorRef bv, CFIndex count) { + CFIndex cnt; + CFAssert1(__CFBitVectorMutableVariety(bv) == kCFBitVectorMutable || __CFBitVectorMutableVariety(bv) == kCFBitVectorFixedMutable, __kCFLogAssertion, "%s(): bit vector is immutable", __PRETTY_FUNCTION__); + cnt = __CFBitVectorCount(bv); + switch (__CFBitVectorMutableVariety(bv)) { + case kCFBitVectorMutable: + if (cnt < count) { + __CFBitVectorGrow(bv, count - cnt); + } + break; + case kCFBitVectorFixedMutable: + CFAssert1(count <= __CFBitVectorCapacity(bv), __kCFLogAssertion, "%s(): fixed-capacity bit vector is full", __PRETTY_FUNCTION__); + break; + } + if (cnt < count) { + CFRange range = CFRangeMake(cnt, count - cnt); + __CFBitVectorInternalMap(bv, range, __CFBitVectorZeroBits, NULL); + } + __CFBitVectorSetNumBucketsUsed(bv, count / __CF_BITS_PER_BUCKET + 1); + __CFBitVectorSetCount(bv, count); +} + +void CFBitVectorFlipBitAtIndex(CFMutableBitVectorRef bv, CFIndex idx) { + __CFGenericValidateType(bv, __kCFBitVectorTypeID); + CFAssert2(0 <= idx && idx < __CFBitVectorCount(bv), __kCFLogAssertion, "%s(): index (%d) out of bounds", __PRETTY_FUNCTION__, idx); + CFAssert1(__CFBitVectorMutableVariety(bv) == kCFBitVectorMutable || __CFBitVectorMutableVariety(bv) == kCFBitVectorFixedMutable, __kCFLogAssertion, "%s(): bit vector is immutable", __PRETTY_FUNCTION__); + __CFFlipBitVectorBit(bv->_buckets, idx); +} + +static __CFBitVectorBucket __CFBitVectorFlipBits(__CFBitVectorBucket bucketValue, __CFBitVectorBucket bucketValueMask, void *context) { + return (~(__CFBitVectorBucket)0) ^ bucketValue; +} + +void CFBitVectorFlipBits(CFMutableBitVectorRef bv, CFRange range) { + __CFGenericValidateType(bv, __kCFBitVectorTypeID); + __CFBitVectorValidateRange(bv, range, __PRETTY_FUNCTION__); + CFAssert1(__CFBitVectorMutableVariety(bv) == kCFBitVectorMutable || __CFBitVectorMutableVariety(bv) == kCFBitVectorFixedMutable, __kCFLogAssertion, "%s(): bit vector is immutable", __PRETTY_FUNCTION__); + if (0 == range.length) return; + __CFBitVectorInternalMap(bv, range, __CFBitVectorFlipBits, NULL); +} + +void CFBitVectorSetBitAtIndex(CFMutableBitVectorRef bv, CFIndex idx, CFBit value) { + __CFGenericValidateType(bv, __kCFBitVectorTypeID); + CFAssert2(0 <= idx && idx < __CFBitVectorCount(bv), __kCFLogAssertion, "%s(): index (%d) out of bounds", __PRETTY_FUNCTION__, idx); + CFAssert1(__CFBitVectorMutableVariety(bv) == kCFBitVectorMutable || __CFBitVectorMutableVariety(bv) == kCFBitVectorFixedMutable, __kCFLogAssertion, "%s(): bit vector is immutable", __PRETTY_FUNCTION__); + __CFSetBitVectorBit(bv->_buckets, idx, value); +} + +void CFBitVectorSetBits(CFMutableBitVectorRef bv, CFRange range, CFBit value) { + __CFGenericValidateType(bv, __kCFBitVectorTypeID); + __CFBitVectorValidateRange(bv, range, __PRETTY_FUNCTION__); + CFAssert1(__CFBitVectorMutableVariety(bv) == kCFBitVectorMutable || __CFBitVectorMutableVariety(bv) == kCFBitVectorFixedMutable, __kCFLogAssertion, "%s(): bit vector is immutable", __PRETTY_FUNCTION__); + if (0 == range.length) return; + if (value) { + __CFBitVectorInternalMap(bv, range, __CFBitVectorOneBits, NULL); + } else { + __CFBitVectorInternalMap(bv, range, __CFBitVectorZeroBits, NULL); + } +} + +void CFBitVectorSetAllBits(CFMutableBitVectorRef bv, CFBit value) { + CFIndex nBuckets, leftover; + __CFGenericValidateType(bv, __kCFBitVectorTypeID); + CFAssert1(__CFBitVectorMutableVariety(bv) == kCFBitVectorMutable || __CFBitVectorMutableVariety(bv) == kCFBitVectorFixedMutable, __kCFLogAssertion, "%s(): bit vector is immutable", __PRETTY_FUNCTION__); + nBuckets = __CFBitVectorCount(bv) / __CF_BITS_PER_BUCKET; + leftover = __CFBitVectorCount(bv) - nBuckets * __CF_BITS_PER_BUCKET; + if (0 < leftover) { + CFRange range = CFRangeMake(nBuckets * __CF_BITS_PER_BUCKET, leftover); + if (value) { + __CFBitVectorInternalMap(bv, range, __CFBitVectorOneBits, NULL); + } else { + __CFBitVectorInternalMap(bv, range, __CFBitVectorZeroBits, NULL); + } + } + memset(bv->_buckets, (value ? ~0 : 0), nBuckets); +} + +#undef __CFBitVectorValidateRange +
diff --git a/CoreFoundation/CFBitVector.h b/CoreFoundation/CFBitVector.h new file mode 100644 index 0000000..0cd0a74 --- /dev/null +++ b/CoreFoundation/CFBitVector.h
@@ -0,0 +1,74 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFBitVector.h + Copyright (c) 1998-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFBITVECTOR__) +#define __COREFOUNDATION_CFBITVECTOR__ 1 + +#include <CoreFoundation/CFBase.h> + +CF_EXTERN_C_BEGIN + +typedef UInt32 CFBit; + +typedef const struct __CFBitVector * CFBitVectorRef; +typedef struct __CFBitVector * CFMutableBitVectorRef; + +CF_EXPORT CFTypeID CFBitVectorGetTypeID(void); + +CF_EXPORT CFBitVectorRef CFBitVectorCreate(CFAllocatorRef allocator, const UInt8 *bytes, CFIndex numBits); +CF_EXPORT CFBitVectorRef CFBitVectorCreateCopy(CFAllocatorRef allocator, CFBitVectorRef bv); +CF_EXPORT CFMutableBitVectorRef CFBitVectorCreateMutable(CFAllocatorRef allocator, CFIndex capacity); +CF_EXPORT CFMutableBitVectorRef CFBitVectorCreateMutableCopy(CFAllocatorRef allocator, CFIndex capacity, CFBitVectorRef bv); + +CF_EXPORT CFIndex CFBitVectorGetCount(CFBitVectorRef bv); +CF_EXPORT CFIndex CFBitVectorGetCountOfBit(CFBitVectorRef bv, CFRange range, CFBit value); +CF_EXPORT Boolean CFBitVectorContainsBit(CFBitVectorRef bv, CFRange range, CFBit value); +CF_EXPORT CFBit CFBitVectorGetBitAtIndex(CFBitVectorRef bv, CFIndex idx); +CF_EXPORT void CFBitVectorGetBits(CFBitVectorRef bv, CFRange range, UInt8 *bytes); +CF_EXPORT CFIndex CFBitVectorGetFirstIndexOfBit(CFBitVectorRef bv, CFRange range, CFBit value); +CF_EXPORT CFIndex CFBitVectorGetLastIndexOfBit(CFBitVectorRef bv, CFRange range, CFBit value); + +CF_EXPORT void CFBitVectorSetCount(CFMutableBitVectorRef bv, CFIndex count); +CF_EXPORT void CFBitVectorFlipBitAtIndex(CFMutableBitVectorRef bv, CFIndex idx); +CF_EXPORT void CFBitVectorFlipBits(CFMutableBitVectorRef bv, CFRange range); +CF_EXPORT void CFBitVectorSetBitAtIndex(CFMutableBitVectorRef bv, CFIndex idx, CFBit value); +CF_EXPORT void CFBitVectorSetBits(CFMutableBitVectorRef bv, CFRange range, CFBit value); +CF_EXPORT void CFBitVectorSetAllBits(CFMutableBitVectorRef bv, CFBit value); + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFBITVECTOR__ */ +
diff --git a/CoreFoundation/CFBuiltinConverters.c b/CoreFoundation/CFBuiltinConverters.c new file mode 100644 index 0000000..b6e55ae --- /dev/null +++ b/CoreFoundation/CFBuiltinConverters.c
@@ -0,0 +1,1206 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFBuiltinConverters.c + Copyright 1999-2002, Apple, Inc. All rights reserved. + Responsibility: Aki Inoue +*/ + +#include "CFStringEncodingConverterExt.h" +#include "CFUniChar.h" +#include "CFUnicodeDecomposition.h" +#include "CFUnicodePrecomposition.h" +#include "CFStringEncodingConverterPriv.h" +#include "CFInternal.h" + +#define ParagraphSeparator 0x2029 +#define ASCIINewLine 0x0a +static int8_t __CFMapsParagraphSeparator = -1; + +CF_INLINE bool __CFIsParagraphSeparator(UTF16Char character) { + if (-1 == __CFMapsParagraphSeparator) __CFMapsParagraphSeparator = (_CFExecutableLinkedOnOrAfter(CFSystemVersionLeopard) ? false : true); + + return ((__CFMapsParagraphSeparator && (ParagraphSeparator == character)) ? true : false); +} + +/* Precomposition */ +static const uint32_t __CFLatin1CombiningCharBitmap[] = { // 0x300 ~ 0x35FF + 0xFBB94010, 0x01800000, 0x0000000, +}; + +bool CFStringEncodingIsValidCombiningCharacterForLatin1(UniChar character) { + return ((character >= 0x300) && (character < 0x360) && (__CFLatin1CombiningCharBitmap[(character - 0x300) / 32] & (1 << (31 - ((character - 0x300) % 32)))) ? true : false); +} + +UniChar CFStringEncodingPrecomposeLatinCharacter(const UniChar *character, CFIndex numChars, CFIndex *usedChars) { + if (numChars > 0) { + UTF32Char ch = *(character++), nextCh, composedChar; + CFIndex usedCharLen = 1; + + if (CFUniCharIsSurrogateHighCharacter(ch) || CFUniCharIsSurrogateLowCharacter(ch)) { + if (usedChars) (*usedChars) = usedCharLen; + return ch; + } + + while (usedCharLen < numChars) { + nextCh = *(character++); + + if (CFUniCharIsSurrogateHighCharacter(nextCh) || CFUniCharIsSurrogateLowCharacter(nextCh)) break; + + if (CFUniCharIsMemberOf(nextCh, kCFUniCharNonBaseCharacterSet) && ((composedChar = CFUniCharPrecomposeCharacter(ch, nextCh)) != 0xFFFD)) { + if (composedChar > 0xFFFF) { // Non-base + break; + } else { + ch = composedChar; + } + } else { + break; + } + ++usedCharLen; + } + if (usedChars) (*usedChars) = usedCharLen; + return ch; + } + return 0xFFFD; +} + +/* ASCII */ +static bool __CFToASCII(uint32_t flags, UniChar character, uint8_t *byte) { + if (character < 0x80) { + *byte = (uint8_t)character; + } else if (__CFIsParagraphSeparator(character)) { + *byte = ASCIINewLine; + } else { + return false; + } + return true; +} + +static bool __CFFromASCII(uint32_t flags, uint8_t byte, UniChar *character) { + if (byte < 0x80) { + *character = (UniChar)byte; + return true; + } else { + return false; + } +} + + +__private_extern__ const CFStringEncodingConverter __CFConverterASCII = { + (void*)__CFToASCII, (void*)__CFFromASCII, 1, 1, kCFStringEncodingConverterCheapEightBit, + NULL, NULL, NULL, NULL, NULL, NULL, +}; + +/* ISO Latin 1 (8859-1) */ +static bool __CFToISOLatin1(uint32_t flags, UniChar character, uint8_t *byte) { + if (character <= 0xFF) { + *byte = (uint8_t)character; + } else if (__CFIsParagraphSeparator(character)) { + *byte = ASCIINewLine; + } else { + return false; + } + + return true; +} + +static bool __CFFromISOLatin1(uint32_t flags, uint8_t byte, UniChar *character) { + *character = (UniChar)byte; + return true; +} + +static CFIndex __CFToISOLatin1Precompose(uint32_t flags, const UniChar *character, CFIndex numChars, uint8_t *bytes, CFIndex maxByteLen, CFIndex *usedByteLen) { + uint8_t byte; + CFIndex usedCharLen; + + if (__CFToISOLatin1(flags, CFStringEncodingPrecomposeLatinCharacter(character, numChars, &usedCharLen), &byte) && byte && (usedCharLen > 1)) { + if (maxByteLen) *bytes = byte; + *usedByteLen = 1; + return usedCharLen; + } else { + return 0; + } +} + +__private_extern__ const CFStringEncodingConverter __CFConverterISOLatin1 = { + (void*)__CFToISOLatin1, (void*)__CFFromISOLatin1, 1, 1, kCFStringEncodingConverterCheapEightBit, + NULL, NULL, NULL, NULL, __CFToISOLatin1Precompose, CFStringEncodingIsValidCombiningCharacterForLatin1, +}; + +/* Mac Roman */ +#define NUM_MACROMAN_FROM_UNI 129 +static const CFStringEncodingUnicodeTo8BitCharMap macRoman_from_uni[NUM_MACROMAN_FROM_UNI] = { + { 0x00A0, 0xCA }, /* NO-BREAK SPACE */ + { 0x00A1, 0xC1 }, /* INVERTED EXCLAMATION MARK */ + { 0x00A2, 0xA2 }, /* CENT SIGN */ + { 0x00A3, 0xA3 }, /* POUND SIGN */ + { 0x00A5, 0xB4 }, /* YEN SIGN */ + { 0x00A7, 0xA4 }, /* SECTION SIGN */ + { 0x00A8, 0xAC }, /* DIAERESIS */ + { 0x00A9, 0xA9 }, /* COPYRIGHT SIGN */ + { 0x00AA, 0xBB }, /* FEMININE ORDINAL INDICATOR */ + { 0x00AB, 0xC7 }, /* LEFT-POINTING DOUBLE ANGLE QUOTATION MARK */ + { 0x00AC, 0xC2 }, /* NOT SIGN */ + { 0x00AE, 0xA8 }, /* REGISTERED SIGN */ + { 0x00AF, 0xF8 }, /* MACRON */ + { 0x00B0, 0xA1 }, /* DEGREE SIGN */ + { 0x00B1, 0xB1 }, /* PLUS-MINUS SIGN */ + { 0x00B4, 0xAB }, /* ACUTE ACCENT */ + { 0x00B5, 0xB5 }, /* MICRO SIGN */ + { 0x00B6, 0xA6 }, /* PILCROW SIGN */ + { 0x00B7, 0xE1 }, /* MIDDLE DOT */ + { 0x00B8, 0xFC }, /* CEDILLA */ + { 0x00BA, 0xBC }, /* MASCULINE ORDINAL INDICATOR */ + { 0x00BB, 0xC8 }, /* RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK */ + { 0x00BF, 0xC0 }, /* INVERTED QUESTION MARK */ + { 0x00C0, 0xCB }, /* LATIN CAPITAL LETTER A WITH GRAVE */ + { 0x00C1, 0xE7 }, /* LATIN CAPITAL LETTER A WITH ACUTE */ + { 0x00C2, 0xE5 }, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ + { 0x00C3, 0xCC }, /* LATIN CAPITAL LETTER A WITH TILDE */ + { 0x00C4, 0x80 }, /* LATIN CAPITAL LETTER A WITH DIAERESIS */ + { 0x00C5, 0x81 }, /* LATIN CAPITAL LETTER A WITH RING ABOVE */ + { 0x00C6, 0xAE }, /* LATIN CAPITAL LIGATURE AE */ + { 0x00C7, 0x82 }, /* LATIN CAPITAL LETTER C WITH CEDILLA */ + { 0x00C8, 0xE9 }, /* LATIN CAPITAL LETTER E WITH GRAVE */ + { 0x00C9, 0x83 }, /* LATIN CAPITAL LETTER E WITH ACUTE */ + { 0x00CA, 0xE6 }, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ + { 0x00CB, 0xE8 }, /* LATIN CAPITAL LETTER E WITH DIAERESIS */ + { 0x00CC, 0xED }, /* LATIN CAPITAL LETTER I WITH GRAVE */ + { 0x00CD, 0xEA }, /* LATIN CAPITAL LETTER I WITH ACUTE */ + { 0x00CE, 0xEB }, /* LATIN CAPITAL LETTER I WITH CIRCUMFLEX */ + { 0x00CF, 0xEC }, /* LATIN CAPITAL LETTER I WITH DIAERESIS */ + { 0x00D1, 0x84 }, /* LATIN CAPITAL LETTER N WITH TILDE */ + { 0x00D2, 0xF1 }, /* LATIN CAPITAL LETTER O WITH GRAVE */ + { 0x00D3, 0xEE }, /* LATIN CAPITAL LETTER O WITH ACUTE */ + { 0x00D4, 0xEF }, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ + { 0x00D5, 0xCD }, /* LATIN CAPITAL LETTER O WITH TILDE */ + { 0x00D6, 0x85 }, /* LATIN CAPITAL LETTER O WITH DIAERESIS */ + { 0x00D8, 0xAF }, /* LATIN CAPITAL LETTER O WITH STROKE */ + { 0x00D9, 0xF4 }, /* LATIN CAPITAL LETTER U WITH GRAVE */ + { 0x00DA, 0xF2 }, /* LATIN CAPITAL LETTER U WITH ACUTE */ + { 0x00DB, 0xF3 }, /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX */ + { 0x00DC, 0x86 }, /* LATIN CAPITAL LETTER U WITH DIAERESIS */ + { 0x00DF, 0xA7 }, /* LATIN SMALL LETTER SHARP S */ + { 0x00E0, 0x88 }, /* LATIN SMALL LETTER A WITH GRAVE */ + { 0x00E1, 0x87 }, /* LATIN SMALL LETTER A WITH ACUTE */ + { 0x00E2, 0x89 }, /* LATIN SMALL LETTER A WITH CIRCUMFLEX */ + { 0x00E3, 0x8B }, /* LATIN SMALL LETTER A WITH TILDE */ + { 0x00E4, 0x8A }, /* LATIN SMALL LETTER A WITH DIAERESIS */ + { 0x00E5, 0x8C }, /* LATIN SMALL LETTER A WITH RING ABOVE */ + { 0x00E6, 0xBE }, /* LATIN SMALL LIGATURE AE */ + { 0x00E7, 0x8D }, /* LATIN SMALL LETTER C WITH CEDILLA */ + { 0x00E8, 0x8F }, /* LATIN SMALL LETTER E WITH GRAVE */ + { 0x00E9, 0x8E }, /* LATIN SMALL LETTER E WITH ACUTE */ + { 0x00EA, 0x90 }, /* LATIN SMALL LETTER E WITH CIRCUMFLEX */ + { 0x00EB, 0x91 }, /* LATIN SMALL LETTER E WITH DIAERESIS */ + { 0x00EC, 0x93 }, /* LATIN SMALL LETTER I WITH GRAVE */ + { 0x00ED, 0x92 }, /* LATIN SMALL LETTER I WITH ACUTE */ + { 0x00EE, 0x94 }, /* LATIN SMALL LETTER I WITH CIRCUMFLEX */ + { 0x00EF, 0x95 }, /* LATIN SMALL LETTER I WITH DIAERESIS */ + { 0x00F1, 0x96 }, /* LATIN SMALL LETTER N WITH TILDE */ + { 0x00F2, 0x98 }, /* LATIN SMALL LETTER O WITH GRAVE */ + { 0x00F3, 0x97 }, /* LATIN SMALL LETTER O WITH ACUTE */ + { 0x00F4, 0x99 }, /* LATIN SMALL LETTER O WITH CIRCUMFLEX */ + { 0x00F5, 0x9B }, /* LATIN SMALL LETTER O WITH TILDE */ + { 0x00F6, 0x9A }, /* LATIN SMALL LETTER O WITH DIAERESIS */ + { 0x00F7, 0xD6 }, /* DIVISION SIGN */ + { 0x00F8, 0xBF }, /* LATIN SMALL LETTER O WITH STROKE */ + { 0x00F9, 0x9D }, /* LATIN SMALL LETTER U WITH GRAVE */ + { 0x00FA, 0x9C }, /* LATIN SMALL LETTER U WITH ACUTE */ + { 0x00FB, 0x9E }, /* LATIN SMALL LETTER U WITH CIRCUMFLEX */ + { 0x00FC, 0x9F }, /* LATIN SMALL LETTER U WITH DIAERESIS */ + { 0x00FF, 0xD8 }, /* LATIN SMALL LETTER Y WITH DIAERESIS */ + { 0x0131, 0xF5 }, /* LATIN SMALL LETTER DOTLESS I */ + { 0x0152, 0xCE }, /* LATIN CAPITAL LIGATURE OE */ + { 0x0153, 0xCF }, /* LATIN SMALL LIGATURE OE */ + { 0x0178, 0xD9 }, /* LATIN CAPITAL LETTER Y WITH DIAERESIS */ + { 0x0192, 0xC4 }, /* LATIN SMALL LETTER F WITH HOOK */ + { 0x02C6, 0xF6 }, /* MODIFIER LETTER CIRCUMFLEX ACCENT */ + { 0x02C7, 0xFF }, /* CARON */ + { 0x02D8, 0xF9 }, /* BREVE */ + { 0x02D9, 0xFA }, /* DOT ABOVE */ + { 0x02DA, 0xFB }, /* RING ABOVE */ + { 0x02DB, 0xFE }, /* OGONEK */ + { 0x02DC, 0xF7 }, /* SMALL TILDE */ + { 0x02DD, 0xFD }, /* DOUBLE ACUTE ACCENT */ + { 0x03A9, 0xBD }, /* OHM SIGN (Canonical ?) */ + { 0x03C0, 0xB9 }, /* GREEK SMALL LETTER PI */ + { 0x2013, 0xD0 }, /* EN DASH */ + { 0x2014, 0xD1 }, /* EM DASH */ + { 0x2018, 0xD4 }, /* LEFT SINGLE QUOTATION MARK */ + { 0x2019, 0xD5 }, /* RIGHT SINGLE QUOTATION MARK */ + { 0x201A, 0xE2 }, /* SINGLE LOW-9 QUOTATION MARK */ + { 0x201C, 0xD2 }, /* LEFT DOUBLE QUOTATION MARK */ + { 0x201D, 0xD3 }, /* RIGHT DOUBLE QUOTATION MARK */ + { 0x201E, 0xE3 }, /* DOUBLE LOW-9 QUOTATION MARK */ + { 0x2020, 0xA0 }, /* DAGGER */ + { 0x2021, 0xE0 }, /* DOUBLE DAGGER */ + { 0x2022, 0xA5 }, /* BULLET */ + { 0x2026, 0xC9 }, /* HORIZONTAL ELLIPSIS */ + { 0x2030, 0xE4 }, /* PER MILLE SIGN */ + { 0x2039, 0xDC }, /* SINGLE LEFT-POINTING ANGLE QUOTATION MARK */ + { 0x203A, 0xDD }, /* SINGLE RIGHT-POINTING ANGLE QUOTATION MARK */ + { 0x2044, 0xDA }, /* FRACTION SLASH */ + { 0x20AC, 0xDB }, /* EURO SIGN */ + { 0x2122, 0xAA }, /* TRADE MARK SIGN */ + { 0x2126, 0xBD }, /* OHM SIGN */ + { 0x2202, 0xB6 }, /* PARTIAL DIFFERENTIAL */ + { 0x2206, 0xC6 }, /* INCREMENT */ + { 0x220F, 0xB8 }, /* N-ARY PRODUCT */ + { 0x2211, 0xB7 }, /* N-ARY SUMMATION */ + { 0x221A, 0xC3 }, /* SQUARE ROOT */ + { 0x221E, 0xB0 }, /* INFINITY */ + { 0x222B, 0xBA }, /* INTEGRAL */ + { 0x2248, 0xC5 }, /* ALMOST EQUAL TO */ + { 0x2260, 0xAD }, /* NOT EQUAL TO */ + { 0x2264, 0xB2 }, /* LESS-THAN OR EQUAL TO */ + { 0x2265, 0xB3 }, /* GREATER-THAN OR EQUAL TO */ + { 0x25CA, 0xD7 }, /* LOZENGE */ + { 0xF8FF, 0xF0 }, /* Apple logo */ + { 0xFB01, 0xDE }, /* LATIN SMALL LIGATURE FI */ + { 0xFB02, 0xDF }, /* LATIN SMALL LIGATURE FL */ +}; + +static bool __CFToMacRoman(uint32_t flags, UniChar character, uint8_t *byte) { + if (character < 0x80) { + *byte = (uint8_t)character; + return true; + } else { + return CFStringEncodingUnicodeTo8BitEncoding(macRoman_from_uni, NUM_MACROMAN_FROM_UNI, character, byte); + } +} + +static const UniChar macRoman_to_uni[128] = { + 0x00C4, /* LATIN CAPITAL LETTER A WITH DIAERESIS */ + 0x00C5, /* LATIN CAPITAL LETTER A WITH RING ABOVE */ + 0x00C7, /* LATIN CAPITAL LETTER C WITH CEDILLA */ + 0x00C9, /* LATIN CAPITAL LETTER E WITH ACUTE */ + 0x00D1, /* LATIN CAPITAL LETTER N WITH TILDE */ + 0x00D6, /* LATIN CAPITAL LETTER O WITH DIAERESIS */ + 0x00DC, /* LATIN CAPITAL LETTER U WITH DIAERESIS */ + 0x00E1, /* LATIN SMALL LETTER A WITH ACUTE */ + 0x00E0, /* LATIN SMALL LETTER A WITH GRAVE */ + 0x00E2, /* LATIN SMALL LETTER A WITH CIRCUMFLEX */ + 0x00E4, /* LATIN SMALL LETTER A WITH DIAERESIS */ + 0x00E3, /* LATIN SMALL LETTER A WITH TILDE */ + 0x00E5, /* LATIN SMALL LETTER A WITH RING ABOVE */ + 0x00E7, /* LATIN SMALL LETTER C WITH CEDILLA */ + 0x00E9, /* LATIN SMALL LETTER E WITH ACUTE */ + 0x00E8, /* LATIN SMALL LETTER E WITH GRAVE */ + 0x00EA, /* LATIN SMALL LETTER E WITH CIRCUMFLEX */ + 0x00EB, /* LATIN SMALL LETTER E WITH DIAERESIS */ + 0x00ED, /* LATIN SMALL LETTER I WITH ACUTE */ + 0x00EC, /* LATIN SMALL LETTER I WITH GRAVE */ + 0x00EE, /* LATIN SMALL LETTER I WITH CIRCUMFLEX */ + 0x00EF, /* LATIN SMALL LETTER I WITH DIAERESIS */ + 0x00F1, /* LATIN SMALL LETTER N WITH TILDE */ + 0x00F3, /* LATIN SMALL LETTER O WITH ACUTE */ + 0x00F2, /* LATIN SMALL LETTER O WITH GRAVE */ + 0x00F4, /* LATIN SMALL LETTER O WITH CIRCUMFLEX */ + 0x00F6, /* LATIN SMALL LETTER O WITH DIAERESIS */ + 0x00F5, /* LATIN SMALL LETTER O WITH TILDE */ + 0x00FA, /* LATIN SMALL LETTER U WITH ACUTE */ + 0x00F9, /* LATIN SMALL LETTER U WITH GRAVE */ + 0x00FB, /* LATIN SMALL LETTER U WITH CIRCUMFLEX */ + 0x00FC, /* LATIN SMALL LETTER U WITH DIAERESIS */ + 0x2020, /* DAGGER */ + 0x00B0, /* DEGREE SIGN */ + 0x00A2, /* CENT SIGN */ + 0x00A3, /* POUND SIGN */ + 0x00A7, /* SECTION SIGN */ + 0x2022, /* BULLET */ + 0x00B6, /* PILCROW SIGN */ + 0x00DF, /* LATIN SMALL LETTER SHARP S */ + 0x00AE, /* REGISTERED SIGN */ + 0x00A9, /* COPYRIGHT SIGN */ + 0x2122, /* TRADE MARK SIGN */ + 0x00B4, /* ACUTE ACCENT */ + 0x00A8, /* DIAERESIS */ + 0x2260, /* NOT EQUAL TO */ + 0x00C6, /* LATIN CAPITAL LIGATURE AE */ + 0x00D8, /* LATIN CAPITAL LETTER O WITH STROKE */ + 0x221E, /* INFINITY */ + 0x00B1, /* PLUS-MINUS SIGN */ + 0x2264, /* LESS-THAN OR EQUAL TO */ + 0x2265, /* GREATER-THAN OR EQUAL TO */ + 0x00A5, /* YEN SIGN */ + 0x00B5, /* MICRO SIGN */ + 0x2202, /* PARTIAL DIFFERENTIAL */ + 0x2211, /* N-ARY SUMMATION */ + 0x220F, /* N-ARY PRODUCT */ + 0x03C0, /* GREEK SMALL LETTER PI */ + 0x222B, /* INTEGRAL */ + 0x00AA, /* FEMININE ORDINAL INDICATOR */ + 0x00BA, /* MASCULINE ORDINAL INDICATOR */ + 0x03A9, /* OHM SIGN (Canonical mapping) */ + 0x00E6, /* LATIN SMALL LIGATURE AE */ + 0x00F8, /* LATIN SMALL LETTER O WITH STROKE */ + 0x00BF, /* INVERTED QUESTION MARK */ + 0x00A1, /* INVERTED EXCLAMATION MARK */ + 0x00AC, /* NOT SIGN */ + 0x221A, /* SQUARE ROOT */ + 0x0192, /* LATIN SMALL LETTER F WITH HOOK */ + 0x2248, /* ALMOST EQUAL TO */ + 0x2206, /* INCREMENT */ + 0x00AB, /* LEFT-POINTING DOUBLE ANGLE QUOTATION MARK */ + 0x00BB, /* RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK */ + 0x2026, /* HORIZONTAL ELLIPSIS */ + 0x00A0, /* NO-BREAK SPACE */ + 0x00C0, /* LATIN CAPITAL LETTER A WITH GRAVE */ + 0x00C3, /* LATIN CAPITAL LETTER A WITH TILDE */ + 0x00D5, /* LATIN CAPITAL LETTER O WITH TILDE */ + 0x0152, /* LATIN CAPITAL LIGATURE OE */ + 0x0153, /* LATIN SMALL LIGATURE OE */ + 0x2013, /* EN DASH */ + 0x2014, /* EM DASH */ + 0x201C, /* LEFT DOUBLE QUOTATION MARK */ + 0x201D, /* RIGHT DOUBLE QUOTATION MARK */ + 0x2018, /* LEFT SINGLE QUOTATION MARK */ + 0x2019, /* RIGHT SINGLE QUOTATION MARK */ + 0x00F7, /* DIVISION SIGN */ + 0x25CA, /* LOZENGE */ + 0x00FF, /* LATIN SMALL LETTER Y WITH DIAERESIS */ + 0x0178, /* LATIN CAPITAL LETTER Y WITH DIAERESIS */ + 0x2044, /* FRACTION SLASH */ + 0x20AC, /* EURO SIGN */ + 0x2039, /* SINGLE LEFT-POINTING ANGLE QUOTATION MARK */ + 0x203A, /* SINGLE RIGHT-POINTING ANGLE QUOTATION MARK */ + 0xFB01, /* LATIN SMALL LIGATURE FI */ + 0xFB02, /* LATIN SMALL LIGATURE FL */ + 0x2021, /* DOUBLE DAGGER */ + 0x00B7, /* MIDDLE DOT */ + 0x201A, /* SINGLE LOW-9 QUOTATION MARK */ + 0x201E, /* DOUBLE LOW-9 QUOTATION MARK */ + 0x2030, /* PER MILLE SIGN */ + 0x00C2, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ + 0x00CA, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ + 0x00C1, /* LATIN CAPITAL LETTER A WITH ACUTE */ + 0x00CB, /* LATIN CAPITAL LETTER E WITH DIAERESIS */ + 0x00C8, /* LATIN CAPITAL LETTER E WITH GRAVE */ + 0x00CD, /* LATIN CAPITAL LETTER I WITH ACUTE */ + 0x00CE, /* LATIN CAPITAL LETTER I WITH CIRCUMFLEX */ + 0x00CF, /* LATIN CAPITAL LETTER I WITH DIAERESIS */ + 0x00CC, /* LATIN CAPITAL LETTER I WITH GRAVE */ + 0x00D3, /* LATIN CAPITAL LETTER O WITH ACUTE */ + 0x00D4, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ + 0xF8FF, /* Apple logo */ + 0x00D2, /* LATIN CAPITAL LETTER O WITH GRAVE */ + 0x00DA, /* LATIN CAPITAL LETTER U WITH ACUTE */ + 0x00DB, /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX */ + 0x00D9, /* LATIN CAPITAL LETTER U WITH GRAVE */ + 0x0131, /* LATIN SMALL LETTER DOTLESS I */ + 0x02C6, /* MODIFIER LETTER CIRCUMFLEX ACCENT */ + 0x02DC, /* SMALL TILDE */ + 0x00AF, /* MACRON */ + 0x02D8, /* BREVE */ + 0x02D9, /* DOT ABOVE */ + 0x02DA, /* RING ABOVE */ + 0x00B8, /* CEDILLA */ + 0x02DD, /* DOUBLE ACUTE ACCENT */ + 0x02DB, /* OGONEK */ + 0x02C7, /* CARON */ +}; + +static bool __CFFromMacRoman(uint32_t flags, uint8_t byte, UniChar *character) { + *character = (byte < 0x80 ? (UniChar)byte : macRoman_to_uni[byte - 0x80]); + return true; +} + +static CFIndex __CFToMacRomanPrecompose(uint32_t flags, const UniChar *character, CFIndex numChars, uint8_t *bytes, CFIndex maxByteLen, CFIndex *usedByteLen) { + uint8_t byte; + CFIndex usedCharLen; + + if (__CFToMacRoman(flags, CFStringEncodingPrecomposeLatinCharacter(character, numChars, &usedCharLen), &byte) && byte && (usedCharLen > 1)) { + if (maxByteLen) *bytes = byte; + *usedByteLen = 1; + return usedCharLen; + } else { + return 0; + } +} + +__private_extern__ const CFStringEncodingConverter __CFConverterMacRoman = { + (void*)__CFToMacRoman, (void*)__CFFromMacRoman, 1, 1, kCFStringEncodingConverterCheapEightBit, + NULL, NULL, NULL, NULL, __CFToMacRomanPrecompose, CFStringEncodingIsValidCombiningCharacterForLatin1, +}; + +/* Win Latin1 (ANSI CodePage 1252) */ +#define NUM_1252_FROM_UNI 27 +static const CFStringEncodingUnicodeTo8BitCharMap cp1252_from_uni[NUM_1252_FROM_UNI] = { + {0x0152, 0x8C}, // LATIN CAPITAL LIGATURE OE + {0x0153, 0x9C}, // LATIN SMALL LIGATURE OE + {0x0160, 0x8A}, // LATIN CAPITAL LETTER S WITH CARON + {0x0161, 0x9A}, // LATIN SMALL LETTER S WITH CARON + {0x0178, 0x9F}, // LATIN CAPITAL LETTER Y WITH DIAERESIS + {0x017D, 0x8E}, // LATIN CAPITAL LETTER Z WITH CARON + {0x017E, 0x9E}, // LATIN SMALL LETTER Z WITH CARON + {0x0192, 0x83}, // LATIN SMALL LETTER F WITH HOOK + {0x02C6, 0x88}, // MODIFIER LETTER CIRCUMFLEX ACCENT + {0x02DC, 0x98}, // SMALL TILDE + {0x2013, 0x96}, // EN DASH + {0x2014, 0x97}, // EM DASH + {0x2018, 0x91}, // LEFT SINGLE QUOTATION MARK + {0x2019, 0x92}, // RIGHT SINGLE QUOTATION MARK + {0x201A, 0x82}, // SINGLE LOW-9 QUOTATION MARK + {0x201C, 0x93}, // LEFT DOUBLE QUOTATION MARK + {0x201D, 0x94}, // RIGHT DOUBLE QUOTATION MARK + {0x201E, 0x84}, // DOUBLE LOW-9 QUOTATION MARK + {0x2020, 0x86}, // DAGGER + {0x2021, 0x87}, // DOUBLE DAGGER + {0x2022, 0x95}, // BULLET + {0x2026, 0x85}, // HORIZONTAL ELLIPSIS + {0x2030, 0x89}, // PER MILLE SIGN + {0x2039, 0x8B}, // SINGLE LEFT-POINTING ANGLE QUOTATION MARK + {0x203A, 0x9B}, // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK + {0x20AC, 0x80}, // EURO SIGN + {0x2122, 0x99}, // TRADE MARK SIGN +}; + +static bool __CFToWinLatin1(uint32_t flags, UniChar character, uint8_t *byte) { + if ((character < 0x80) || ((character > 0x9F) && (character <= 0x00FF))) { + *byte = (uint8_t)character; + return true; + } + return CFStringEncodingUnicodeTo8BitEncoding(cp1252_from_uni, NUM_1252_FROM_UNI, character, byte); +} + +static const uint16_t cp1252_to_uni[32] = { + 0x20AC, // EURO SIGN + 0xFFFD, // NOT USED + 0x201A, // SINGLE LOW-9 QUOTATION MARK + 0x0192, // LATIN SMALL LETTER F WITH HOOK + 0x201E, // DOUBLE LOW-9 QUOTATION MARK + 0x2026, // HORIZONTAL ELLIPSIS + 0x2020, // DAGGER + 0x2021, // DOUBLE DAGGER + 0x02C6, // MODIFIER LETTER CIRCUMFLEX ACCENT + 0x2030, // PER MILLE SIGN + 0x0160, // LATIN CAPITAL LETTER S WITH CARON + 0x2039, // SINGLE LEFT-POINTING ANGLE QUOTATION MARK + 0x0152, // LATIN CAPITAL LIGATURE OE + 0xFFFD, // NOT USED + 0x017D, // LATIN CAPITAL LETTER Z WITH CARON + 0xFFFD, // NOT USED + 0xFFFD, // NOT USED + 0x2018, // LEFT SINGLE QUOTATION MARK + 0x2019, // RIGHT SINGLE QUOTATION MARK + 0x201C, // LEFT DOUBLE QUOTATION MARK + 0x201D, // RIGHT DOUBLE QUOTATION MARK + 0x2022, // BULLET + 0x2013, // EN DASH + 0x2014, // EM DASH + 0x02DC, // SMALL TILDE + 0x2122, // TRADE MARK SIGN + 0x0161, // LATIN SMALL LETTER S WITH CARON + 0x203A, // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK + 0x0153, // LATIN SMALL LIGATURE OE + 0xFFFD, // NOT USED + 0x017E, // LATIN SMALL LETTER Z WITH CARON + 0x0178, // LATIN CAPITAL LETTER Y WITH DIAERESIS +}; + +static bool __CFFromWinLatin1(uint32_t flags, uint8_t byte, UniChar *character) { + *character = (byte < 0x80 || byte > 0x9F ? (UniChar)byte : cp1252_to_uni[byte - 0x80]); + return (*character != 0xFFFD); +} + +static CFIndex __CFToWinLatin1Precompose(uint32_t flags, const UniChar *character, CFIndex numChars, uint8_t *bytes, CFIndex maxByteLen, CFIndex *usedByteLen) { + uint8_t byte; + CFIndex usedCharLen; + + if (__CFToWinLatin1(flags, CFStringEncodingPrecomposeLatinCharacter(character, numChars, &usedCharLen), &byte) && byte && (usedCharLen > 1)) { + if (maxByteLen) *bytes = byte; + *usedByteLen = 1; + return usedCharLen; + } else { + return 0; + } +} + +__private_extern__ const CFStringEncodingConverter __CFConverterWinLatin1 = { + (void*)__CFToWinLatin1, (void*)__CFFromWinLatin1, 1, 1, kCFStringEncodingConverterCheapEightBit, + NULL, NULL, NULL, NULL, __CFToWinLatin1Precompose, CFStringEncodingIsValidCombiningCharacterForLatin1, +}; + +/* NEXTSTEP Encoding */ +#define NUM_NEXTSTEP_FROM_UNI 127 + +static const CFStringEncodingUnicodeTo8BitCharMap nextstep_from_tab[NUM_NEXTSTEP_FROM_UNI] = { + { 0x00a0, 0x80 }, + { 0x00a1, 0xa1 }, + { 0x00a2, 0xa2 }, + { 0x00a3, 0xa3 }, + { 0x00a4, 0xa8 }, + { 0x00a5, 0xa5 }, + { 0x00a6, 0xb5 }, + { 0x00a7, 0xa7 }, + { 0x00a8, 0xc8 }, + { 0x00a9, 0xa0 }, + { 0x00aa, 0xe3 }, + { 0x00ab, 0xab }, + { 0x00ac, 0xbe }, +/* { 0x00ad, 0x2d }, <= 96/10/25 rick removed; converts soft-hyphen to hyphen! */ + { 0x00ae, 0xb0 }, + { 0x00af, 0xc5 }, + { 0x00b1, 0xd1 }, + { 0x00b2, 0xc9 }, + { 0x00b3, 0xcc }, + { 0x00b4, 0xc2 }, + { 0x00b5, 0x9d }, + { 0x00b6, 0xb6 }, + { 0x00b7, 0xb4 }, + { 0x00b8, 0xcb }, + { 0x00b9, 0xc0 }, + { 0x00ba, 0xeb }, + { 0x00bb, 0xbb }, + { 0x00bc, 0xd2 }, + { 0x00bd, 0xd3 }, + { 0x00be, 0xd4 }, + { 0x00bf, 0xbf }, + { 0x00c0, 0x81 }, + { 0x00c1, 0x82 }, + { 0x00c2, 0x83 }, + { 0x00c3, 0x84 }, + { 0x00c4, 0x85 }, + { 0x00c5, 0x86 }, + { 0x00c6, 0xe1 }, + { 0x00c7, 0x87 }, + { 0x00c8, 0x88 }, + { 0x00c9, 0x89 }, + { 0x00ca, 0x8a }, + { 0x00cb, 0x8b }, + { 0x00cc, 0x8c }, + { 0x00cd, 0x8d }, + { 0x00ce, 0x8e }, + { 0x00cf, 0x8f }, + { 0x00d0, 0x90 }, + { 0x00d1, 0x91 }, + { 0x00d2, 0x92 }, + { 0x00d3, 0x93 }, + { 0x00d4, 0x94 }, + { 0x00d5, 0x95 }, + { 0x00d6, 0x96 }, + { 0x00d7, 0x9e }, + { 0x00d8, 0xe9 }, + { 0x00d9, 0x97 }, + { 0x00da, 0x98 }, + { 0x00db, 0x99 }, + { 0x00dc, 0x9a }, + { 0x00dd, 0x9b }, + { 0x00de, 0x9c }, + { 0x00df, 0xfb }, + { 0x00e0, 0xd5 }, + { 0x00e1, 0xd6 }, + { 0x00e2, 0xd7 }, + { 0x00e3, 0xd8 }, + { 0x00e4, 0xd9 }, + { 0x00e5, 0xda }, + { 0x00e6, 0xf1 }, + { 0x00e7, 0xdb }, + { 0x00e8, 0xdc }, + { 0x00e9, 0xdd }, + { 0x00ea, 0xde }, + { 0x00eb, 0xdf }, + { 0x00ec, 0xe0 }, + { 0x00ed, 0xe2 }, + { 0x00ee, 0xe4 }, + { 0x00ef, 0xe5 }, + { 0x00f0, 0xe6 }, + { 0x00f1, 0xe7 }, + { 0x00f2, 0xec }, + { 0x00f3, 0xed }, + { 0x00f4, 0xee }, + { 0x00f5, 0xef }, + { 0x00f6, 0xf0 }, + { 0x00f7, 0x9f }, + { 0x00f8, 0xf9 }, + { 0x00f9, 0xf2 }, + { 0x00fa, 0xf3 }, + { 0x00fb, 0xf4 }, + { 0x00fc, 0xf6 }, + { 0x00fd, 0xf7 }, + { 0x00fe, 0xfc }, + { 0x00ff, 0xfd }, + { 0x0131, 0xf5 }, + { 0x0141, 0xe8 }, + { 0x0142, 0xf8 }, + { 0x0152, 0xea }, + { 0x0153, 0xfa }, + { 0x0192, 0xa6 }, + { 0x02c6, 0xc3 }, + { 0x02c7, 0xcf }, + { 0x02cb, 0xc1 }, + { 0x02d8, 0xc6 }, + { 0x02d9, 0xc7 }, + { 0x02da, 0xca }, + { 0x02db, 0xce }, + { 0x02dc, 0xc4 }, + { 0x02dd, 0xcd }, + { 0x2013, 0xb1 }, + { 0x2014, 0xd0 }, + { 0x2019, 0xa9 }, + { 0x201a, 0xb8 }, + { 0x201c, 0xaa }, + { 0x201d, 0xba }, + { 0x201e, 0xb9 }, + { 0x2020, 0xb2 }, + { 0x2021, 0xb3 }, + { 0x2022, 0xb7 }, + { 0x2026, 0xbc }, + { 0x2030, 0xbd }, + { 0x2039, 0xac }, + { 0x203a, 0xad }, + { 0x2044, 0xa4 }, + { 0xfb01, 0xae }, + { 0xfb02, 0xaf }, + { 0xfffd, 0xff }, +}; + +static bool __CFToNextStepLatin(uint32_t flags, UniChar character, uint8_t *byte) { + if (character < 0x80) { + *byte = (uint8_t)character; + return true; + } else if (__CFIsParagraphSeparator(character)) { + *byte = ASCIINewLine; + return true; + } else { + return CFStringEncodingUnicodeTo8BitEncoding(nextstep_from_tab, NUM_NEXTSTEP_FROM_UNI, character, byte); + } +}; + +static const UniChar NSToPrecompUnicodeTable[128] = { + /* NextStep Encoding Unicode */ + /* 128 figspace */ 0x00a0, /* 0x2007 is fig space */ + /* 129 Agrave */ 0x00c0, + /* 130 Aacute */ 0x00c1, + /* 131 Acircumflex */ 0x00c2, + /* 132 Atilde */ 0x00c3, + /* 133 Adieresis */ 0x00c4, + /* 134 Aring */ 0x00c5, + /* 135 Ccedilla */ 0x00c7, + /* 136 Egrave */ 0x00c8, + /* 137 Eacute */ 0x00c9, + /* 138 Ecircumflex */ 0x00ca, + /* 139 Edieresis */ 0x00cb, + /* 140 Igrave */ 0x00cc, + /* 141 Iacute */ 0x00cd, + /* 142 Icircumflex */ 0x00ce, + /* 143 Idieresis */ 0x00cf, + /* 144 Eth */ 0x00d0, + /* 145 Ntilde */ 0x00d1, + /* 146 Ograve */ 0x00d2, + /* 147 Oacute */ 0x00d3, + /* 148 Ocircumflex */ 0x00d4, + /* 149 Otilde */ 0x00d5, + /* 150 Odieresis */ 0x00d6, + /* 151 Ugrave */ 0x00d9, + /* 152 Uacute */ 0x00da, + /* 153 Ucircumflex */ 0x00db, + /* 154 Udieresis */ 0x00dc, + /* 155 Yacute */ 0x00dd, + /* 156 Thorn */ 0x00de, + /* 157 mu */ 0x00b5, + /* 158 multiply */ 0x00d7, + /* 159 divide */ 0x00f7, + /* 160 copyright */ 0x00a9, + /* 161 exclamdown */ 0x00a1, + /* 162 cent */ 0x00a2, + /* 163 sterling */ 0x00a3, + /* 164 fraction */ 0x2044, + /* 165 yen */ 0x00a5, + /* 166 florin */ 0x0192, + /* 167 section */ 0x00a7, + /* 168 currency */ 0x00a4, + /* 169 quotesingle */ 0x2019, + /* 170 quotedblleft */ 0x201c, + /* 171 guillemotleft */ 0x00ab, + /* 172 guilsinglleft */ 0x2039, + /* 173 guilsinglright */ 0x203a, + /* 174 fi */ 0xFB01, + /* 175 fl */ 0xFB02, + /* 176 registered */ 0x00ae, + /* 177 endash */ 0x2013, + /* 178 dagger */ 0x2020, + /* 179 daggerdbl */ 0x2021, + /* 180 periodcentered */ 0x00b7, + /* 181 brokenbar */ 0x00a6, + /* 182 paragraph */ 0x00b6, + /* 183 bullet */ 0x2022, + /* 184 quotesinglbase */ 0x201a, + /* 185 quotedblbase */ 0x201e, + /* 186 quotedblright */ 0x201d, + /* 187 guillemotright */ 0x00bb, + /* 188 ellipsis */ 0x2026, + /* 189 perthousand */ 0x2030, + /* 190 logicalnot */ 0x00ac, + /* 191 questiondown */ 0x00bf, + /* 192 onesuperior */ 0x00b9, + /* 193 grave */ 0x02cb, + /* 194 acute */ 0x00b4, + /* 195 circumflex */ 0x02c6, + /* 196 tilde */ 0x02dc, + /* 197 macron */ 0x00af, + /* 198 breve */ 0x02d8, + /* 199 dotaccent */ 0x02d9, + /* 200 dieresis */ 0x00a8, + /* 201 twosuperior */ 0x00b2, + /* 202 ring */ 0x02da, + /* 203 cedilla */ 0x00b8, + /* 204 threesuperior */ 0x00b3, + /* 205 hungarumlaut */ 0x02dd, + /* 206 ogonek */ 0x02db, + /* 207 caron */ 0x02c7, + /* 208 emdash */ 0x2014, + /* 209 plusminus */ 0x00b1, + /* 210 onequarter */ 0x00bc, + /* 211 onehalf */ 0x00bd, + /* 212 threequarters */ 0x00be, + /* 213 agrave */ 0x00e0, + /* 214 aacute */ 0x00e1, + /* 215 acircumflex */ 0x00e2, + /* 216 atilde */ 0x00e3, + /* 217 adieresis */ 0x00e4, + /* 218 aring */ 0x00e5, + /* 219 ccedilla */ 0x00e7, + /* 220 egrave */ 0x00e8, + /* 221 eacute */ 0x00e9, + /* 222 ecircumflex */ 0x00ea, + /* 223 edieresis */ 0x00eb, + /* 224 igrave */ 0x00ec, + /* 225 AE */ 0x00c6, + /* 226 iacute */ 0x00ed, + /* 227 ordfeminine */ 0x00aa, + /* 228 icircumflex */ 0x00ee, + /* 229 idieresis */ 0x00ef, + /* 230 eth */ 0x00f0, + /* 231 ntilde */ 0x00f1, + /* 232 Lslash */ 0x0141, + /* 233 Oslash */ 0x00d8, + /* 234 OE */ 0x0152, + /* 235 ordmasculine */ 0x00ba, + /* 236 ograve */ 0x00f2, + /* 237 oacute */ 0x00f3, + /* 238 ocircumflex */ 0x00f4, + /* 239 otilde */ 0x00f5, + /* 240 odieresis */ 0x00f6, + /* 241 ae */ 0x00e6, + /* 242 ugrave */ 0x00f9, + /* 243 uacute */ 0x00fa, + /* 244 ucircumflex */ 0x00fb, + /* 245 dotlessi */ 0x0131, + /* 246 udieresis */ 0x00fc, + /* 247 yacute */ 0x00fd, + /* 248 lslash */ 0x0142, + /* 249 oslash */ 0x00f8, + /* 250 oe */ 0x0153, + /* 251 germandbls */ 0x00df, + /* 252 thorn */ 0x00fe, + /* 253 ydieresis */ 0x00ff, + /* 254 .notdef */ 0xFFFD, + /* 255 .notdef */ 0xFFFD +}; + +static bool __CFFromNextStepLatin(uint32_t flags, uint8_t byte, UniChar *character) { + return ((*character = (byte < 0x80 ? (UniChar)byte : NSToPrecompUnicodeTable[byte - 0x80])) != 0xFFFD); +} + +static CFIndex __CFToNextStepLatinPrecompose(uint32_t flags, const UniChar *character, CFIndex numChars, uint8_t *bytes, CFIndex maxByteLen, CFIndex *usedByteLen) { + uint8_t byte; + CFIndex usedCharLen; + + if (__CFToNextStepLatin(flags, CFStringEncodingPrecomposeLatinCharacter(character, numChars, &usedCharLen), &byte) && byte && (usedCharLen > 1)) { + if (maxByteLen) *bytes = byte; + *usedByteLen = 1; + return usedCharLen; + } else { + return 0; + } +} + +__private_extern__ const CFStringEncodingConverter __CFConverterNextStepLatin = { + (void*)__CFToNextStepLatin, (void*)__CFFromNextStepLatin, 1, 1, kCFStringEncodingConverterCheapEightBit, + NULL, NULL, NULL, NULL, __CFToNextStepLatinPrecompose, CFStringEncodingIsValidCombiningCharacterForLatin1, +}; + +/* UTF8 */ +/* + * Copyright 2001 Unicode, Inc. + * + * Disclaimer + * + * This source code is provided as is by Unicode, Inc. No claims are + * made as to fitness for any particular purpose. No warranties of any + * kind are expressed or implied. The recipient agrees to determine + * applicability of information provided. If this file has been + * purchased on magnetic or optical media from Unicode, Inc., the + * sole remedy for any claim will be exchange of defective media + * within 90 days of receipt. + * + * Limitations on Rights to Redistribute This Code + * + * Unicode, Inc. hereby grants the right to freely use the information + * supplied in this file in the creation of products supporting the + * Unicode Standard, and to make copies of this file in any form + * for internal or external distribution as long as this notice + * remains attached. + */ + +static const uint32_t kReplacementCharacter = 0x0000FFFDUL; +static const uint32_t kMaximumUCS2 = 0x0000FFFFUL; +static const uint32_t kMaximumUTF16 = 0x0010FFFFUL; +static const uint32_t kMaximumUCS4 = 0x7FFFFFFFUL; + +static const int halfShift = 10; +static const uint32_t halfBase = 0x0010000UL; +static const uint32_t halfMask = 0x3FFUL; +static const uint32_t kSurrogateHighStart = 0xD800UL; +static const uint32_t kSurrogateHighEnd = 0xDBFFUL; +static const uint32_t kSurrogateLowStart = 0xDC00UL; +static const uint32_t kSurrogateLowEnd = 0xDFFFUL; + +/* + * Index into the table below with the first byte of a UTF-8 sequence to + * get the number of trailing bytes that are supposed to follow it. + */ +static const char trailingBytesForUTF8[256] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 +}; + +/* + * Magic values subtracted from a buffer value during UTF8 conversion. + * This table contains as many values as there might be trailing bytes + * in a UTF-8 sequence. + */ +static const UTF32Char offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, + 0x03C82080UL, 0xFA082080UL, 0x82082080UL }; + +static const uint8_t firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + +/* This code is similar in effect to making successive calls on the mbtowc and wctomb routines in FSS-UTF. However, it is considerably different in code: + * it is adapted to be consistent with UTF16, + * constants have been gathered. + * loops & conditionals have been removed as much as possible for + * efficiency, in favor of drop-through switch statements. +*/ + +CF_INLINE uint16_t __CFUTF8BytesToWriteForCharacter(uint32_t ch) { + if (ch < 0x80) return 1; + else if (ch < 0x800) return 2; + else if (ch < 0x10000) return 3; + else if (ch < 0x200000) return 4; + else if (ch < 0x4000000) return 5; + else if (ch <= kMaximumUCS4) return 6; + else return 0; +} + +CF_INLINE uint16_t __CFToUTF8Core(uint32_t ch, uint8_t *bytes, uint32_t maxByteLen) { + uint16_t bytesToWrite = __CFUTF8BytesToWriteForCharacter(ch); + const uint32_t byteMask = 0xBF; + const uint32_t byteMark = 0x80; + + if (!bytesToWrite) { + bytesToWrite = 2; + ch = kReplacementCharacter; + } + + if (maxByteLen < bytesToWrite) return 0; + + switch (bytesToWrite) { /* note: code falls through cases! */ + case 6: bytes[5] = (ch | byteMark) & byteMask; ch >>= 6; + case 5: bytes[4] = (ch | byteMark) & byteMask; ch >>= 6; + case 4: bytes[3] = (ch | byteMark) & byteMask; ch >>= 6; + case 3: bytes[2] = (ch | byteMark) & byteMask; ch >>= 6; + case 2: bytes[1] = (ch | byteMark) & byteMask; ch >>= 6; + case 1: bytes[0] = ch | firstByteMark[bytesToWrite]; + } + return bytesToWrite; +} + +static CFIndex __CFToUTF8(uint32_t flags, const UniChar *characters, CFIndex numChars, uint8_t *bytes, CFIndex maxByteLen, CFIndex *usedByteLen) { + uint16_t bytesWritten; + uint32_t ch; + const UniChar *beginCharacter = characters; + const UniChar *endCharacter = characters + numChars; + const uint8_t *beginBytes = bytes; + const uint8_t *endBytes = bytes + maxByteLen; + bool isStrict = (flags & kCFStringEncodingUseHFSPlusCanonical ? false : true); + + while ((characters < endCharacter) && (!maxByteLen || (bytes < endBytes))) { + ch = *(characters++); + + if (ch < 0x80) { // ASCII + if (maxByteLen) *bytes = ch; + ++bytes; + } else { + if (ch >= kSurrogateHighStart) { + if (ch <= kSurrogateHighEnd) { + if ((characters < endCharacter) && ((*characters >= kSurrogateLowStart) && (*characters <= kSurrogateLowEnd))) { + ch = ((ch - kSurrogateHighStart) << halfShift) + (*(characters++) - kSurrogateLowStart) + halfBase; + } else if (isStrict) { + --characters; + break; + } + } else if (isStrict && (ch <= kSurrogateLowEnd)) { + --characters; + break; + } + } + + if (!(bytesWritten = (maxByteLen ? __CFToUTF8Core(ch, bytes, endBytes - bytes) : __CFUTF8BytesToWriteForCharacter(ch)))) { + characters -= (ch < 0x10000 ? 1 : 2); + break; + } + bytes += bytesWritten; + } + } + + if (usedByteLen) *usedByteLen = bytes - beginBytes; + return characters - beginCharacter; +} + +/* + * Utility routine to tell whether a sequence of bytes is legal UTF-8. + * This must be called with the length pre-determined by the first byte. + * If not calling this from ConvertUTF8to*, then the length can be set by: + * length = trailingBytesForUTF8[*source]+1; + * and the sequence is illegal right away if there aren't that many bytes + * available. + * If presented with a length > 4, this returns false. The Unicode + * definition of UTF-8 goes up to 4-byte sequences. + */ + +CF_INLINE bool __CFIsLegalUTF8(const uint8_t *source, CFIndex length) { + if (length > 4) return false; + + const uint8_t *srcptr = source+length; + uint8_t head = *source; + + while (--srcptr > source) if ((*srcptr & 0xC0) != 0x80) return false; + + if (((head >= 0x80) && (head < 0xC2)) || (head > 0xF4)) return false; + + if (((head == 0xE0) && (*(source + 1) < 0xA0)) || ((head == 0xED) && (*(source + 1) > 0x9F)) || ((head == 0xF0) && (*(source + 1) < 0x90)) || ((head == 0xF4) && (*(source + 1) > 0x8F))) return false; + return true; +} + +static CFIndex __CFFromUTF8(uint32_t flags, const uint8_t *bytes, CFIndex numBytes, UniChar *characters, CFIndex maxCharLen, CFIndex *usedCharLen) { + const uint8_t *source = bytes; + uint16_t extraBytesToRead; + CFIndex theUsedCharLen = 0; + uint32_t ch; + bool isHFSPlus = (flags & kCFStringEncodingUseHFSPlusCanonical ? true : false); + bool needsToDecompose = (flags & kCFStringEncodingUseCanonical || isHFSPlus ? true : false); + bool strictUTF8 = (flags & kCFStringEncodingLenientUTF8Conversion ? false : true); + UTF32Char decomposed[MAX_DECOMPOSED_LENGTH]; + CFIndex decompLength; + bool isStrict = !isHFSPlus; + + while (numBytes && (!maxCharLen || (theUsedCharLen < maxCharLen))) { + extraBytesToRead = trailingBytesForUTF8[*source]; + + if (extraBytesToRead > --numBytes) break; + numBytes -= extraBytesToRead; + + /* Do this check whether lenient or strict */ + // We need to allow 0xA9 (copyright in MacRoman and Unicode) not to break existing apps + // Will use a flag passed in from upper layers to switch restriction mode for this case in the next release + if ((extraBytesToRead > 3) || (strictUTF8 && !__CFIsLegalUTF8(source, extraBytesToRead + 1))) { + if ((*source == 0xA9) || (flags & kCFStringEncodingAllowLossyConversion)) { + numBytes += extraBytesToRead; + ++source; + if (maxCharLen) *(characters++) = (UTF16Char)kReplacementCharacter; + ++theUsedCharLen; + continue; + } else { + break; + } + } + + ch = 0; + /* + * The cases all fall through. See "Note A" below. + */ + switch (extraBytesToRead) { + case 3: ch += *source++; ch <<= 6; + case 2: ch += *source++; ch <<= 6; + case 1: ch += *source++; ch <<= 6; + case 0: ch += *source++; + } + ch -= offsetsFromUTF8[extraBytesToRead]; + + if (ch <= kMaximumUCS2) { + if (isStrict && (ch >= kSurrogateHighStart && ch <= kSurrogateLowEnd)) { + source -= (extraBytesToRead + 1); + break; + } + if (needsToDecompose && CFUniCharIsDecomposableCharacter(ch, isHFSPlus)) { + decompLength = CFUniCharDecomposeCharacter(ch, decomposed, MAX_DECOMPOSED_LENGTH); + + if (maxCharLen) { + if (!CFUniCharFillDestinationBuffer(decomposed, decompLength, (void **)&characters, maxCharLen, &theUsedCharLen, kCFUniCharUTF16Format)) break; + } else { + theUsedCharLen += decompLength; + } + } else { + if (maxCharLen) *(characters++) = (UTF16Char)ch; + ++theUsedCharLen; + } + } else if (ch > kMaximumUTF16) { + if (isStrict) { + source -= (extraBytesToRead + 1); + break; + } + if (maxCharLen) *(characters++) = (UTF16Char)kReplacementCharacter; + ++theUsedCharLen; + } else { + if (needsToDecompose && CFUniCharIsDecomposableCharacter(ch, isHFSPlus)) { + decompLength = CFUniCharDecomposeCharacter(ch, decomposed, MAX_DECOMPOSED_LENGTH); + + if (maxCharLen) { + if (!CFUniCharFillDestinationBuffer(decomposed, decompLength, (void **)&characters, maxCharLen, &theUsedCharLen, kCFUniCharUTF16Format)) break; + } else { + while (--decompLength >= 0) theUsedCharLen += (decomposed[decompLength] < 0x10000 ? 1 : 2); + } + } else { + if (maxCharLen) { + if ((theUsedCharLen + 2) > maxCharLen) break; + ch -= halfBase; + *(characters++) = (ch >> halfShift) + kSurrogateHighStart; + *(characters++) = (ch & halfMask) + kSurrogateLowStart; + } + theUsedCharLen += 2; + } + } + } + + if (usedCharLen) *usedCharLen = theUsedCharLen; + + return source - bytes; +} + +static CFIndex __CFToUTF8Len(uint32_t flags, const UniChar *characters, CFIndex numChars) { + uint32_t bytesToWrite = 0; + uint32_t ch; + + while (numChars) { + ch = *characters++; + numChars--; + if ((ch >= kSurrogateHighStart && ch <= kSurrogateHighEnd) && numChars && (*characters >= kSurrogateLowStart && *characters <= kSurrogateLowEnd)) { + ch = ((ch - kSurrogateHighStart) << halfShift) + (*characters++ - kSurrogateLowStart) + halfBase; + numChars--; + } + bytesToWrite += __CFUTF8BytesToWriteForCharacter(ch); + } + + return bytesToWrite; +} + +static CFIndex __CFFromUTF8Len(uint32_t flags, const uint8_t *source, CFIndex numBytes) { + uint16_t extraBytesToRead; + CFIndex theUsedCharLen = 0; + uint32_t ch; + bool isHFSPlus = (flags & kCFStringEncodingUseHFSPlusCanonical ? true : false); + bool needsToDecompose = (flags & kCFStringEncodingUseCanonical || isHFSPlus ? true : false); + bool strictUTF8 = (flags & kCFStringEncodingLenientUTF8Conversion ? false : true); + UTF32Char decomposed[MAX_DECOMPOSED_LENGTH]; + CFIndex decompLength; + bool isStrict = !isHFSPlus; + + while (numBytes) { + extraBytesToRead = trailingBytesForUTF8[*source]; + + if (extraBytesToRead > --numBytes) break; + numBytes -= extraBytesToRead; + + /* Do this check whether lenient or strict */ + // We need to allow 0xA9 (copyright in MacRoman and Unicode) not to break existing apps + // Will use a flag passed in from upper layers to switch restriction mode for this case in the next release + if ((extraBytesToRead > 3) || (strictUTF8 && !__CFIsLegalUTF8(source, extraBytesToRead + 1))) { + if ((*source == 0xA9) || (flags & kCFStringEncodingAllowLossyConversion)) { + numBytes += extraBytesToRead; + ++source; + ++theUsedCharLen; + continue; + } else { + break; + } + } + + + ch = 0; + /* + * The cases all fall through. See "Note A" below. + */ + switch (extraBytesToRead) { + case 3: ch += *source++; ch <<= 6; + case 2: ch += *source++; ch <<= 6; + case 1: ch += *source++; ch <<= 6; + case 0: ch += *source++; + } + ch -= offsetsFromUTF8[extraBytesToRead]; + + if (ch <= kMaximumUCS2) { + if (isStrict && (ch >= kSurrogateHighStart && ch <= kSurrogateLowEnd)) { + break; + } + if (needsToDecompose && CFUniCharIsDecomposableCharacter(ch, isHFSPlus)) { + decompLength = CFUniCharDecomposeCharacter(ch, decomposed, MAX_DECOMPOSED_LENGTH); + theUsedCharLen += decompLength; + } else { + ++theUsedCharLen; + } + } else if (ch > kMaximumUTF16) { + ++theUsedCharLen; + } else { + if (needsToDecompose && CFUniCharIsDecomposableCharacter(ch, isHFSPlus)) { + decompLength = CFUniCharDecomposeCharacter(ch, decomposed, MAX_DECOMPOSED_LENGTH); + while (--decompLength >= 0) theUsedCharLen += (decomposed[decompLength] < 0x10000 ? 1 : 2); + } else { + theUsedCharLen += 2; + } + } + } + + return theUsedCharLen; +} + +__private_extern__ const CFStringEncodingConverter __CFConverterUTF8 = { + (void*)__CFToUTF8, (void*)__CFFromUTF8, 3, 2, kCFStringEncodingConverterStandard, + __CFToUTF8Len, __CFFromUTF8Len, NULL, NULL, NULL, NULL, +};
diff --git a/CoreFoundation/CFBundle.c b/CoreFoundation/CFBundle.c new file mode 100644 index 0000000..e25bff0 --- /dev/null +++ b/CoreFoundation/CFBundle.c
@@ -0,0 +1,4051 @@ +/* + * 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@ + */ +/* CFBundle.c + Copyright (c) 1999-2007 Apple Inc. All rights reserved. + Responsibility: Doug Davidson +*/ + +#include "CFBundle_Internal.h" +#include <CoreFoundation/CFPropertyList.h> +#include <CoreFoundation/CFNumber.h> +#include <CoreFoundation/CFSet.h> +#include <CoreFoundation/CFURLAccess.h> +#include <CoreFoundation/CFError.h> +#include <string.h> +#include "CFPriv.h" +#include "CFInternal.h" +#include <CoreFoundation/CFByteOrder.h> +#include "CFBundle_BinaryTypes.h" +#include <ctype.h> +#include <sys/stat.h> +#include <stdlib.h> + +#if defined(BINARY_SUPPORT_DYLD) +// Import the mach-o headers that define the macho magic numbers +#include <mach-o/loader.h> +#include <mach-o/fat.h> +#include <mach-o/arch.h> +#include <mach-o/dyld.h> +#include <mach-o/getsect.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/mman.h> +#endif /* BINARY_SUPPORT_DYLD */ + +#if defined(BINARY_SUPPORT_DLFCN) +#include <dlfcn.h> +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_SOLARIS +#define CF_RTLD_FIRST RTLD_FIRST +#else +#define CF_RTLD_FIRST 0 +#endif /* DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_SOLARIS */ +#endif /* BINARY_SUPPORT_DLFCN */ + +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX +#include <fcntl.h> +#endif + +#if DEPLOYMENT_TARGET_WINDOWS +#include <io.h> +#include <stdio.h> +#define lseek _lseek +#define open _open +#define read _read +#define write _write +#define close _close +#endif + +#if DEPLOYMENT_TARGET_LINUX +#include <unistd.h> +#endif + +#define LOG_BUNDLE_LOAD 0 + +// Public CFBundle Info plist keys +CONST_STRING_DECL(kCFBundleInfoDictionaryVersionKey, "CFBundleInfoDictionaryVersion") +CONST_STRING_DECL(kCFBundleExecutableKey, "CFBundleExecutable") +CONST_STRING_DECL(kCFBundleIdentifierKey, "CFBundleIdentifier") +CONST_STRING_DECL(kCFBundleVersionKey, "CFBundleVersion") +CONST_STRING_DECL(kCFBundleDevelopmentRegionKey, "CFBundleDevelopmentRegion") +CONST_STRING_DECL(kCFBundleLocalizationsKey, "CFBundleLocalizations") + +// Finder stuff +CONST_STRING_DECL(_kCFBundlePackageTypeKey, "CFBundlePackageType") +CONST_STRING_DECL(_kCFBundleSignatureKey, "CFBundleSignature") +CONST_STRING_DECL(_kCFBundleIconFileKey, "CFBundleIconFile") +CONST_STRING_DECL(_kCFBundleDocumentTypesKey, "CFBundleDocumentTypes") +CONST_STRING_DECL(_kCFBundleURLTypesKey, "CFBundleURLTypes") + +// Keys that are usually localized in InfoPlist.strings +CONST_STRING_DECL(kCFBundleNameKey, "CFBundleName") +CONST_STRING_DECL(_kCFBundleDisplayNameKey, "CFBundleDisplayName") +CONST_STRING_DECL(_kCFBundleShortVersionStringKey, "CFBundleShortVersionString") +CONST_STRING_DECL(_kCFBundleGetInfoStringKey, "CFBundleGetInfoString") +CONST_STRING_DECL(_kCFBundleGetInfoHTMLKey, "CFBundleGetInfoHTML") + +// Sub-keys for CFBundleDocumentTypes dictionaries +CONST_STRING_DECL(_kCFBundleTypeNameKey, "CFBundleTypeName") +CONST_STRING_DECL(_kCFBundleTypeRoleKey, "CFBundleTypeRole") +CONST_STRING_DECL(_kCFBundleTypeIconFileKey, "CFBundleTypeIconFile") +CONST_STRING_DECL(_kCFBundleTypeOSTypesKey, "CFBundleTypeOSTypes") +CONST_STRING_DECL(_kCFBundleTypeExtensionsKey, "CFBundleTypeExtensions") +CONST_STRING_DECL(_kCFBundleTypeMIMETypesKey, "CFBundleTypeMIMETypes") + +// Sub-keys for CFBundleURLTypes dictionaries +CONST_STRING_DECL(_kCFBundleURLNameKey, "CFBundleURLName") +CONST_STRING_DECL(_kCFBundleURLIconFileKey, "CFBundleURLIconFile") +CONST_STRING_DECL(_kCFBundleURLSchemesKey, "CFBundleURLSchemes") + +// Compatibility key names +CONST_STRING_DECL(_kCFBundleOldExecutableKey, "NSExecutable") +CONST_STRING_DECL(_kCFBundleOldInfoDictionaryVersionKey, "NSInfoPlistVersion") +CONST_STRING_DECL(_kCFBundleOldNameKey, "NSHumanReadableName") +CONST_STRING_DECL(_kCFBundleOldIconFileKey, "NSIcon") +CONST_STRING_DECL(_kCFBundleOldDocumentTypesKey, "NSTypes") +CONST_STRING_DECL(_kCFBundleOldShortVersionStringKey, "NSAppVersion") + +// Compatibility CFBundleDocumentTypes key names +CONST_STRING_DECL(_kCFBundleOldTypeNameKey, "NSName") +CONST_STRING_DECL(_kCFBundleOldTypeRoleKey, "NSRole") +CONST_STRING_DECL(_kCFBundleOldTypeIconFileKey, "NSIcon") +CONST_STRING_DECL(_kCFBundleOldTypeExtensions1Key, "NSUnixExtensions") +CONST_STRING_DECL(_kCFBundleOldTypeExtensions2Key, "NSDOSExtensions") +CONST_STRING_DECL(_kCFBundleOldTypeOSTypesKey, "NSMacOSType") + +// Internally used keys for loaded Info plists. +CONST_STRING_DECL(_kCFBundleInfoPlistURLKey, "CFBundleInfoPlistURL") +CONST_STRING_DECL(_kCFBundleRawInfoPlistURLKey, "CFBundleRawInfoPlistURL") +CONST_STRING_DECL(_kCFBundleNumericVersionKey, "CFBundleNumericVersion") +CONST_STRING_DECL(_kCFBundleExecutablePathKey, "CFBundleExecutablePath") +CONST_STRING_DECL(_kCFBundleResourcesFileMappedKey, "CSResourcesFileMapped") +CONST_STRING_DECL(_kCFBundleCFMLoadAsBundleKey, "CFBundleCFMLoadAsBundle") +CONST_STRING_DECL(_kCFBundleAllowMixedLocalizationsKey, "CFBundleAllowMixedLocalizations") + +// Keys used by NSBundle for loaded Info plists. +CONST_STRING_DECL(_kCFBundleInitialPathKey, "NSBundleInitialPath") +CONST_STRING_DECL(_kCFBundleResolvedPathKey, "NSBundleResolvedPath") +CONST_STRING_DECL(_kCFBundlePrincipalClassKey, "NSPrincipalClass") + +static CFTypeID __kCFBundleTypeID = _kCFRuntimeNotATypeID; + +struct __CFBundle { + CFRuntimeBase _base; + + CFURLRef _url; + CFDateRef _modDate; + + CFDictionaryRef _infoDict; + CFDictionaryRef _localInfoDict; + CFArrayRef _searchLanguages; + + __CFPBinaryType _binaryType; + Boolean _isLoaded; + uint8_t _version; + Boolean _sharesStringsFiles; + char _padding[1]; + + /* CFM goop */ + void *_connectionCookie; + + /* DYLD goop */ + const void *_imageCookie; + const void *_moduleCookie; + + /* dlfcn goop */ + void *_handleCookie; + + /* CFM<->DYLD glue */ + CFMutableDictionaryRef _glueDict; + + /* Resource fork goop */ + _CFResourceData _resourceData; + + _CFPlugInData _plugInData; + +#if defined(BINARY_SUPPORT_DLL) + HMODULE _hModule; +#endif /* BINARY_SUPPORT_DLL */ + +}; + +static CFSpinLock_t CFBundleGlobalDataLock = CFSpinLockInit; + +static CFMutableDictionaryRef _bundlesByURL = NULL; +static CFMutableDictionaryRef _bundlesByIdentifier = NULL; + +// For scheduled lazy unloading. Used by CFPlugIn. +static CFMutableSetRef _bundlesToUnload = NULL; +static Boolean _scheduledBundlesAreUnloading = false; + +// Various lists of all bundles. +static CFMutableArrayRef _allBundles = NULL; + +static Boolean _initedMainBundle = false; +static CFBundleRef _mainBundle = NULL; +static CFStringRef _defaultLocalization = NULL; + +static Boolean _useDlfcn = false; + +// Forward declares functions. +static CFBundleRef _CFBundleCreate(CFAllocatorRef allocator, CFURLRef bundleURL, Boolean alreadyLocked, Boolean doFinalProcessing); +static CFStringRef _CFBundleCopyExecutableName(CFAllocatorRef alloc, CFBundleRef bundle, CFURLRef url, CFDictionaryRef infoDict); +static CFURLRef _CFBundleCopyExecutableURLIgnoringCache(CFBundleRef bundle); +static void _CFBundleEnsureBundlesUpToDateWithHintAlreadyLocked(CFStringRef hint); +static void _CFBundleEnsureAllBundlesUpToDateAlreadyLocked(void); +static void _CFBundleCheckWorkarounds(CFBundleRef bundle); +static void _CFBundleEnsureBundleExistsForImagePath(CFStringRef imagePath); +static void _CFBundleEnsureBundlesExistForImagePaths(CFArrayRef imagePaths); +#if defined(BINARY_SUPPORT_DYLD) +static CFDictionaryRef _CFBundleGrokInfoDictFromMainExecutable(void); +static Boolean _CFBundleGrokObjCImageInfoFromMainExecutable(uint32_t *objcVersion, uint32_t *objcFlags); +static CFStringRef _CFBundleDYLDCopyLoadedImagePathForPointer(void *p); +static void *_CFBundleDYLDGetSymbolByNameWithSearch(CFBundleRef bundle, CFStringRef symbolName, Boolean globalSearch); +#endif /* BINARY_SUPPORT_DYLD */ +#if defined(BINARY_SUPPORT_DLFCN) +static CFStringRef _CFBundleDlfcnCopyLoadedImagePathForPointer(void *p); +static void *_CFBundleDlfcnGetSymbolByNameWithSearch(CFBundleRef bundle, CFStringRef symbolName, Boolean globalSearch); +#endif /* BINARY_SUPPORT_DLFCN */ +#if defined(BINARY_SUPPORT_DYLD) && defined(BINARY_SUPPORT_CFM) +static void *_CFBundleFunctionPointerForTVector(CFAllocatorRef allocator, void *tvp); +static void *_CFBundleTVectorForFunctionPointer(CFAllocatorRef allocator, void *fp); +#endif /* BINARY_SUPPORT_DYLD && BINARY_SUPPORT_CFM */ + +static void _CFBundleAddToTables(CFBundleRef bundle, Boolean alreadyLocked) { + CFStringRef bundleID = CFBundleGetIdentifier(bundle); + + if (!alreadyLocked) __CFSpinLock(&CFBundleGlobalDataLock); + + // Add to the _allBundles list + if (!_allBundles) { + // Create this from the default allocator + CFArrayCallBacks nonRetainingArrayCallbacks = kCFTypeArrayCallBacks; + nonRetainingArrayCallbacks.retain = NULL; + nonRetainingArrayCallbacks.release = NULL; + _allBundles = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &nonRetainingArrayCallbacks); + } + CFArrayAppendValue(_allBundles, bundle); + + // Add to the table that maps urls to bundles + if (!_bundlesByURL) { + // Create this from the default allocator + CFDictionaryValueCallBacks nonRetainingDictionaryValueCallbacks = kCFTypeDictionaryValueCallBacks; + nonRetainingDictionaryValueCallbacks.retain = NULL; + nonRetainingDictionaryValueCallbacks.release = NULL; + _bundlesByURL = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &nonRetainingDictionaryValueCallbacks); + } + CFDictionarySetValue(_bundlesByURL, bundle->_url, bundle); + + // Add to the table that maps identifiers to bundles + if (bundleID) { + CFMutableArrayRef bundlesWithThisID = NULL; + CFBundleRef existingBundle = NULL; + if (!_bundlesByIdentifier) { + // Create this from the default allocator + _bundlesByIdentifier = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + } + bundlesWithThisID = (CFMutableArrayRef)CFDictionaryGetValue(_bundlesByIdentifier, bundleID); + if (bundlesWithThisID) { + CFIndex i, count = CFArrayGetCount(bundlesWithThisID); + UInt32 existingVersion, newVersion = CFBundleGetVersionNumber(bundle); + for (i = 0; i < count; i++) { + existingBundle = (CFBundleRef)CFArrayGetValueAtIndex(bundlesWithThisID, i); + existingVersion = CFBundleGetVersionNumber(existingBundle); + // If you load two bundles with the same identifier and the same version, the last one wins. + if (newVersion >= existingVersion) break; + } + CFArrayInsertValueAtIndex(bundlesWithThisID, i, bundle); + } else { + // Create this from the default allocator + CFArrayCallBacks nonRetainingArrayCallbacks = kCFTypeArrayCallBacks; + nonRetainingArrayCallbacks.retain = NULL; + nonRetainingArrayCallbacks.release = NULL; + bundlesWithThisID = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &nonRetainingArrayCallbacks); + CFArrayAppendValue(bundlesWithThisID, bundle); + CFDictionarySetValue(_bundlesByIdentifier, bundleID, bundlesWithThisID); + CFRelease(bundlesWithThisID); + } + } + if (!alreadyLocked) __CFSpinUnlock(&CFBundleGlobalDataLock); +} + +static void _CFBundleRemoveFromTables(CFBundleRef bundle) { + CFStringRef bundleID = CFBundleGetIdentifier(bundle); + + __CFSpinLock(&CFBundleGlobalDataLock); + + // Remove from the various lists + if (_allBundles) { + CFIndex i = CFArrayGetFirstIndexOfValue(_allBundles, CFRangeMake(0, CFArrayGetCount(_allBundles)), bundle); + if (i >= 0) CFArrayRemoveValueAtIndex(_allBundles, i); + } + + // Remove from the table that maps urls to bundles + if (_bundlesByURL) CFDictionaryRemoveValue(_bundlesByURL, bundle->_url); + + // Remove from the table that maps identifiers to bundles + if (bundleID && _bundlesByIdentifier) { + CFMutableArrayRef bundlesWithThisID = (CFMutableArrayRef)CFDictionaryGetValue(_bundlesByIdentifier, bundleID); + if (bundlesWithThisID) { + CFIndex count = CFArrayGetCount(bundlesWithThisID); + while (count-- > 0) if (bundle == (CFBundleRef)CFArrayGetValueAtIndex(bundlesWithThisID, count)) CFArrayRemoveValueAtIndex(bundlesWithThisID, count); + if (0 == CFArrayGetCount(bundlesWithThisID)) CFDictionaryRemoveValue(_bundlesByIdentifier, bundleID); + } + } + + __CFSpinUnlock(&CFBundleGlobalDataLock); +} + +__private_extern__ CFBundleRef _CFBundleFindByURL(CFURLRef url, Boolean alreadyLocked) { + CFBundleRef result = NULL; + if (!alreadyLocked) __CFSpinLock(&CFBundleGlobalDataLock); + if (_bundlesByURL) result = (CFBundleRef)CFDictionaryGetValue(_bundlesByURL, url); + if (!alreadyLocked) __CFSpinUnlock(&CFBundleGlobalDataLock); + return result; +} + +static CFURLRef _CFBundleCopyBundleURLForExecutablePath(CFStringRef str) { + //!!! need to handle frameworks, NT; need to integrate with NSBundle - drd + UniChar buff[CFMaxPathSize]; + CFIndex buffLen; + CFURLRef url = NULL; + CFStringRef outstr; + + buffLen = CFStringGetLength(str); + if (buffLen > CFMaxPathSize) buffLen = CFMaxPathSize; + CFStringGetCharacters(str, CFRangeMake(0, buffLen), buff); + + if (!url) { + buffLen = _CFLengthAfterDeletingLastPathComponent(buff, buffLen); // Remove exe name + + if (buffLen > 0) { + // See if this is a new bundle. If it is, we have to remove more path components. + CFIndex startOfLastDir = _CFStartOfLastPathComponent(buff, buffLen); + if ((startOfLastDir > 0) && (startOfLastDir < buffLen)) { + CFStringRef lastDirName = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, &(buff[startOfLastDir]), buffLen - startOfLastDir); + + if (CFEqual(lastDirName, _CFBundleGetPlatformExecutablesSubdirectoryName()) || CFEqual(lastDirName, _CFBundleGetAlternatePlatformExecutablesSubdirectoryName()) || CFEqual(lastDirName, _CFBundleGetOtherPlatformExecutablesSubdirectoryName()) || CFEqual(lastDirName, _CFBundleGetOtherAlternatePlatformExecutablesSubdirectoryName())) { + // This is a new bundle. Back off a few more levels + if (buffLen > 0) { + // Remove platform folder + buffLen = _CFLengthAfterDeletingLastPathComponent(buff, buffLen); + } + if (buffLen > 0) { + // Remove executables folder (if present) + CFIndex startOfNextDir = _CFStartOfLastPathComponent(buff, buffLen); + if ((startOfNextDir > 0) && (startOfNextDir < buffLen)) { + CFStringRef nextDirName = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, &(buff[startOfNextDir]), buffLen - startOfNextDir); + if (CFEqual(nextDirName, _CFBundleExecutablesDirectoryName)) buffLen = _CFLengthAfterDeletingLastPathComponent(buff, buffLen); + CFRelease(nextDirName); + } + } + if (buffLen > 0) { + // Remove support files folder + buffLen = _CFLengthAfterDeletingLastPathComponent(buff, buffLen); + } + } + CFRelease(lastDirName); + } + } + + if (buffLen > 0) { + outstr = CFStringCreateWithCharactersNoCopy(kCFAllocatorSystemDefault, buff, buffLen, kCFAllocatorNull); + url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, outstr, PLATFORM_PATH_STYLE, true); + CFRelease(outstr); + } + } + return url; +} + +static CFURLRef _CFBundleCopyResolvedURLForExecutableURL(CFURLRef url) { + // this is necessary so that we match any sanitization CFURL may perform on the result of _CFBundleCopyBundleURLForExecutableURL() + CFURLRef absoluteURL, url1, url2, outURL = NULL; + CFStringRef str, str1, str2; + absoluteURL = CFURLCopyAbsoluteURL(url); + str = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE); + if (str) { + UniChar buff[CFMaxPathSize]; + CFIndex buffLen = CFStringGetLength(str), len1; + if (buffLen > CFMaxPathSize) buffLen = CFMaxPathSize; + CFStringGetCharacters(str, CFRangeMake(0, buffLen), buff); + len1 = _CFLengthAfterDeletingLastPathComponent(buff, buffLen); + if (len1 > 0 && len1 + 1 < buffLen) { + str1 = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, buff, len1); + str2 = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, buff + len1 + 1, buffLen - len1 - 1); + if (str1 && str2) { + url1 = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str1, PLATFORM_PATH_STYLE, true); + if (url1) { + url2 = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, str2, PLATFORM_PATH_STYLE, false, url1); + if (url2) { + outURL = CFURLCopyAbsoluteURL(url2); + CFRelease(url2); + } + CFRelease(url1); + } + } + if (str1) CFRelease(str1); + if (str2) CFRelease(str2); + } + CFRelease(str); + } + if (!outURL) { + outURL = absoluteURL; + } else { + CFRelease(absoluteURL); + } + return outURL; +} + +CFURLRef _CFBundleCopyBundleURLForExecutableURL(CFURLRef url) { + CFURLRef resolvedURL, outurl = NULL; + CFStringRef str; + resolvedURL = _CFBundleCopyResolvedURLForExecutableURL(url); + str = CFURLCopyFileSystemPath(resolvedURL, PLATFORM_PATH_STYLE); + if (str) { + outurl = _CFBundleCopyBundleURLForExecutablePath(str); + CFRelease(str); + } + CFRelease(resolvedURL); + return outurl; +} + +CFBundleRef _CFBundleCreateIfLooksLikeBundle(CFAllocatorRef allocator, CFURLRef url) { + CFBundleRef bundle = CFBundleCreate(allocator, url); + + // exclude type 0 bundles with no binary (or CFM binary) and no Info.plist, since they give too many false positives + if (bundle && 0 == bundle->_version) { + CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); + if (!infoDict || 0 == CFDictionaryGetCount(infoDict)) { +#if defined(BINARY_SUPPORT_CFM) && defined(BINARY_SUPPORT_DYLD) + CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); + if (executableURL) { + if (bundle->_binaryType == __CFBundleUnknownBinary) bundle->_binaryType = _CFBundleGrokBinaryType(executableURL); + if (bundle->_binaryType == __CFBundleCFMBinary || bundle->_binaryType == __CFBundleUnreadableBinary) { + bundle->_version = 4; + } else { + bundle->_resourceData._executableLacksResourceFork = true; + } + CFRelease(executableURL); + } else { + bundle->_version = 4; + } +#elif defined(BINARY_SUPPORT_CFM) + bundle->_version = 4; +#else + CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); + if (executableURL) { + CFRelease(executableURL); + } else { + bundle->_version = 4; + } +#endif /* BINARY_SUPPORT_CFM && BINARY_SUPPORT_DYLD */ + } + } + if (bundle && (3 == bundle->_version || 4 == bundle->_version)) { + CFRelease(bundle); + bundle = NULL; + } + return bundle; +} + +CFBundleRef _CFBundleGetMainBundleIfLooksLikeBundle(void) { + CFBundleRef mainBundle = CFBundleGetMainBundle(); + if (mainBundle && (3 == mainBundle->_version || 4 == mainBundle->_version)) mainBundle = NULL; + return mainBundle; +} + +Boolean _CFBundleMainBundleInfoDictionaryComesFromResourceFork(void) { + CFBundleRef mainBundle = CFBundleGetMainBundle(); + return (mainBundle && mainBundle->_resourceData._infoDictionaryFromResourceFork); +} + +CFBundleRef _CFBundleCreateWithExecutableURLIfLooksLikeBundle(CFAllocatorRef allocator, CFURLRef url) { + CFBundleRef bundle = NULL; + CFURLRef bundleURL = _CFBundleCopyBundleURLForExecutableURL(url), resolvedURL = _CFBundleCopyResolvedURLForExecutableURL(url); + if (bundleURL && resolvedURL) { + bundle = _CFBundleCreateIfLooksLikeBundle(allocator, bundleURL); + if (bundle) { + CFURLRef executableURL = _CFBundleCopyExecutableURLIgnoringCache(bundle); + char buff1[CFMaxPathSize], buff2[CFMaxPathSize]; + if (!executableURL || !CFURLGetFileSystemRepresentation(resolvedURL, true, (uint8_t *)buff1, CFMaxPathSize) || !CFURLGetFileSystemRepresentation(executableURL, true, (uint8_t *)buff2, CFMaxPathSize) || 0 != strcmp(buff1, buff2)) { + CFRelease(bundle); + bundle = NULL; + } + if (executableURL) CFRelease(executableURL); + } + } + if (bundleURL) CFRelease(bundleURL); + if (resolvedURL) CFRelease(resolvedURL); + return bundle; +} + +CFURLRef _CFBundleCopyMainBundleExecutableURL(Boolean *looksLikeBundle) { + // This function is for internal use only; _mainBundle is deliberately accessed outside of the lock to get around a reentrancy issue + const char *processPath; + CFStringRef str = NULL; + CFURLRef executableURL = NULL; + processPath = _CFProcessPath(); + if (processPath) { + str = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, processPath); + if (str) { + executableURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str, PLATFORM_PATH_STYLE, false); + CFRelease(str); + } + } + if (looksLikeBundle) { + CFBundleRef mainBundle = _mainBundle; + if (mainBundle && (3 == mainBundle->_version || 4 == mainBundle->_version)) mainBundle = NULL; + *looksLikeBundle = (mainBundle ? true : false); + } + return executableURL; +} + +static void _CFBundleInitializeMainBundleInfoDictionaryAlreadyLocked(CFStringRef executablePath) { +#if defined(BINARY_SUPPORT_CFM) + Boolean versRegionOverrides = false; +#endif /* BINARY_SUPPORT_CFM */ + CFBundleGetInfoDictionary(_mainBundle); + if (!_mainBundle->_infoDict || CFDictionaryGetCount(_mainBundle->_infoDict) == 0) { + // if type 3 bundle and no Info.plist, treat as unbundled, since this gives too many false positives + if (_mainBundle->_version == 3) _mainBundle->_version = 4; + if (_mainBundle->_version == 0) { + // if type 0 bundle and no Info.plist and not main executable for bundle, treat as unbundled, since this gives too many false positives + CFStringRef executableName = _CFBundleCopyExecutableName(kCFAllocatorSystemDefault, _mainBundle, NULL, NULL); + if (!executableName || !executablePath || !CFStringHasSuffix(executablePath, executableName)) _mainBundle->_version = 4; + if (executableName) CFRelease(executableName); + } +#if defined(BINARY_SUPPORT_DYLD) + if (_mainBundle->_binaryType == __CFBundleDYLDExecutableBinary) { + if (_mainBundle->_infoDict) CFRelease(_mainBundle->_infoDict); + _mainBundle->_infoDict = _CFBundleGrokInfoDictFromMainExecutable(); + } +#endif /* BINARY_SUPPORT_DYLD */ +#if defined(BINARY_SUPPORT_CFM) + if (_mainBundle->_binaryType == __CFBundleCFMBinary || _mainBundle->_binaryType == __CFBundleUnreadableBinary) { + // if type 0 bundle and CFM binary and no Info.plist, treat as unbundled, since this also gives too many false positives + if (_mainBundle->_version == 0) _mainBundle->_version = 4; + if (_mainBundle->_version != 4) { + // if CFM binary and no Info.plist and not main executable for bundle, treat as unbundled, since this also gives too many false positives + // except for Macromedia Director MX, which is unbundled but wants to be treated as bundled + CFStringRef executableName = _CFBundleCopyExecutableName(kCFAllocatorSystemDefault, _mainBundle, NULL, NULL); + Boolean treatAsBundled = false; + if (executablePath) { + CFIndex strLength = CFStringGetLength(executablePath); + if (strLength > 10) treatAsBundled = CFStringFindWithOptions(executablePath, CFSTR(" MX"), CFRangeMake(strLength - 10, 10), 0, NULL); + } + if (!treatAsBundled && (!executableName || !executablePath || !CFStringHasSuffix(executablePath, executableName))) _mainBundle->_version = 4; + if (executableName) CFRelease(executableName); + } + if (_mainBundle->_infoDict) CFRelease(_mainBundle->_infoDict); + if (executablePath) { + CFURLRef executableURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, executablePath, PLATFORM_PATH_STYLE, false); + if (executableURL) { + _mainBundle->_infoDict = _CFBundleCopyInfoDictionaryInResourceForkWithAllocator(CFGetAllocator(_mainBundle), executableURL); + if (_mainBundle->_infoDict) _mainBundle->_resourceData._infoDictionaryFromResourceFork = true; + CFRelease(executableURL); + } + } + if (_mainBundle->_binaryType == __CFBundleUnreadableBinary && _mainBundle->_infoDict && CFDictionaryGetValue(_mainBundle->_infoDict, kCFBundleDevelopmentRegionKey)) versRegionOverrides = true; + } +#endif /* BINARY_SUPPORT_CFM */ + } + if (!_mainBundle->_infoDict) _mainBundle->_infoDict = CFDictionaryCreateMutable(CFGetAllocator(_mainBundle), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (!CFDictionaryGetValue(_mainBundle->_infoDict, _kCFBundleExecutablePathKey)) CFDictionarySetValue((CFMutableDictionaryRef)(_mainBundle->_infoDict), _kCFBundleExecutablePathKey, executablePath); +#if defined(BINARY_SUPPORT_CFM) + if (versRegionOverrides) { + // This is a hack to preserve backward compatibility for certain broken applications (2761067) + CFStringRef devLang = _CFBundleCopyBundleDevelopmentRegionFromVersResource(_mainBundle); + if (devLang) { + CFDictionarySetValue((CFMutableDictionaryRef)(_mainBundle->_infoDict), kCFBundleDevelopmentRegionKey, devLang); + CFRelease(devLang); + } + } +#endif /* BINARY_SUPPORT_CFM */ +} + +CF_EXPORT void _CFBundleFlushBundleCaches(CFBundleRef bundle) { + CFDictionaryRef oldInfoDict = bundle->_infoDict; + CFTypeRef val; + + _CFBundleFlushCachesForURL(bundle->_url); + bundle->_infoDict = NULL; + if (bundle->_localInfoDict) { + CFRelease(bundle->_localInfoDict); + bundle->_localInfoDict = NULL; + } + if (bundle->_searchLanguages) { + CFRelease(bundle->_searchLanguages); + bundle->_searchLanguages = NULL; + } + if (bundle->_resourceData._stringTableCache) { + CFRelease(bundle->_resourceData._stringTableCache); + bundle->_resourceData._stringTableCache = NULL; + } + if (bundle == _mainBundle) { + CFStringRef executablePath = oldInfoDict ? (CFStringRef)CFDictionaryGetValue(oldInfoDict, _kCFBundleExecutablePathKey) : NULL; + __CFSpinLock(&CFBundleGlobalDataLock); + _CFBundleInitializeMainBundleInfoDictionaryAlreadyLocked(executablePath); + __CFSpinUnlock(&CFBundleGlobalDataLock); + } else { + CFBundleGetInfoDictionary(bundle); + } + if (oldInfoDict) { + if (!bundle->_infoDict) bundle->_infoDict = CFDictionaryCreateMutable(CFGetAllocator(bundle), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + val = CFDictionaryGetValue(oldInfoDict, _kCFBundleInitialPathKey); + if (val) CFDictionarySetValue((CFMutableDictionaryRef)bundle->_infoDict, _kCFBundleInitialPathKey, val); + val = CFDictionaryGetValue(oldInfoDict, _kCFBundleResolvedPathKey); + if (val) CFDictionarySetValue((CFMutableDictionaryRef)bundle->_infoDict, _kCFBundleResolvedPathKey, val); + val = CFDictionaryGetValue(oldInfoDict, _kCFBundlePrincipalClassKey); + if (val) CFDictionarySetValue((CFMutableDictionaryRef)bundle->_infoDict, _kCFBundlePrincipalClassKey, val); + CFRelease(oldInfoDict); + } +} + +static CFBundleRef _CFBundleGetMainBundleAlreadyLocked(void) { + if (!_initedMainBundle) { + const char *processPath; + CFStringRef str = NULL; + CFURLRef executableURL = NULL, bundleURL = NULL; + _initedMainBundle = true; + processPath = _CFProcessPath(); + if (processPath) { + str = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, processPath); + if (!executableURL) executableURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str, PLATFORM_PATH_STYLE, false); + } + if (executableURL) bundleURL = _CFBundleCopyBundleURLForExecutableURL(executableURL); + if (bundleURL) { + // make sure that main bundle has executable path + //??? what if we are not the main executable in the bundle? + // NB doFinalProcessing must be false here, see below + _mainBundle = _CFBundleCreate(kCFAllocatorSystemDefault, bundleURL, true, false); + if (_mainBundle) { + // make sure that the main bundle is listed as loaded, and mark it as executable + _mainBundle->_isLoaded = true; +#if defined(BINARY_SUPPORT_DYLD) + if (_mainBundle->_binaryType == __CFBundleUnknownBinary) { + if (!executableURL) { + _mainBundle->_binaryType = __CFBundleNoBinary; + } else { + _mainBundle->_binaryType = _CFBundleGrokBinaryType(executableURL); +#if defined(BINARY_SUPPORT_CFM) + if (_mainBundle->_binaryType != __CFBundleCFMBinary && _mainBundle->_binaryType != __CFBundleUnreadableBinary) _mainBundle->_resourceData._executableLacksResourceFork = true; +#endif /* BINARY_SUPPORT_CFM */ + } + } +#endif /* BINARY_SUPPORT_DYLD */ +#if defined(BINARY_SUPPORT_DYLD) + // get cookie for already-loaded main bundle + if (_mainBundle->_binaryType == __CFBundleDYLDExecutableBinary && !_mainBundle->_imageCookie) { + // ??? need better way to specify main executable image + _mainBundle->_imageCookie = (void *)_dyld_get_image_header(0); +#if LOG_BUNDLE_LOAD + printf("main bundle %p getting image %p\n", _mainBundle, _mainBundle->_imageCookie); +#endif /* LOG_BUNDLE_LOAD */ + } +#endif /* BINARY_SUPPORT_DYLD */ + _CFBundleInitializeMainBundleInfoDictionaryAlreadyLocked(str); + // Perform delayed final processing steps. + // This must be done after _isLoaded has been set, for security reasons (3624341). + _CFBundleCheckWorkarounds(_mainBundle); + if (_CFBundleNeedsInitPlugIn(_mainBundle)) { + __CFSpinUnlock(&CFBundleGlobalDataLock); + _CFBundleInitPlugIn(_mainBundle); + __CFSpinLock(&CFBundleGlobalDataLock); + } + } + } + if (bundleURL) CFRelease(bundleURL); + if (str) CFRelease(str); + if (executableURL) CFRelease(executableURL); + } + return _mainBundle; +} + +CFBundleRef CFBundleGetMainBundle(void) { + CFBundleRef mainBundle; + __CFSpinLock(&CFBundleGlobalDataLock); + mainBundle = _CFBundleGetMainBundleAlreadyLocked(); + __CFSpinUnlock(&CFBundleGlobalDataLock); + return mainBundle; +} + +CFBundleRef CFBundleGetBundleWithIdentifier(CFStringRef bundleID) { + CFBundleRef result = NULL; + CFArrayRef bundlesWithThisID; + if (bundleID) { + __CFSpinLock(&CFBundleGlobalDataLock); + (void)_CFBundleGetMainBundleAlreadyLocked(); + if (_bundlesByIdentifier) { + bundlesWithThisID = (CFArrayRef)CFDictionaryGetValue(_bundlesByIdentifier, bundleID); + if (bundlesWithThisID && CFArrayGetCount(bundlesWithThisID) > 0) result = (CFBundleRef)CFArrayGetValueAtIndex(bundlesWithThisID, 0); + } +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX + if (!result) { + // Try to create the bundle for the caller and try again + void *p = __builtin_return_address(0); + if (p) { + CFStringRef imagePath = NULL; +#if defined(BINARY_SUPPORT_DLFCN) + if (!imagePath && _useDlfcn) imagePath = _CFBundleDlfcnCopyLoadedImagePathForPointer(p); +#endif /* BINARY_SUPPORT_DLFCN */ +#if defined(BINARY_SUPPORT_DYLD) + if (!imagePath) imagePath = _CFBundleDYLDCopyLoadedImagePathForPointer(p); +#endif /* BINARY_SUPPORT_DYLD */ + if (imagePath) { + _CFBundleEnsureBundleExistsForImagePath(imagePath); + CFRelease(imagePath); + } + if (_bundlesByIdentifier) { + bundlesWithThisID = (CFArrayRef)CFDictionaryGetValue(_bundlesByIdentifier, bundleID); + if (bundlesWithThisID && CFArrayGetCount(bundlesWithThisID) > 0) result = (CFBundleRef)CFArrayGetValueAtIndex(bundlesWithThisID, 0); + } + } + } +#endif + if (!result) { + // Try to guess the bundle from the identifier and try again + _CFBundleEnsureBundlesUpToDateWithHintAlreadyLocked(bundleID); + if (_bundlesByIdentifier) { + bundlesWithThisID = (CFArrayRef)CFDictionaryGetValue(_bundlesByIdentifier, bundleID); + if (bundlesWithThisID && CFArrayGetCount(bundlesWithThisID) > 0) result = (CFBundleRef)CFArrayGetValueAtIndex(bundlesWithThisID, 0); + } + } + if (!result) { + // Make sure all bundles have been created and try again. + _CFBundleEnsureAllBundlesUpToDateAlreadyLocked(); + if (_bundlesByIdentifier) { + bundlesWithThisID = (CFArrayRef)CFDictionaryGetValue(_bundlesByIdentifier, bundleID); + if (bundlesWithThisID && CFArrayGetCount(bundlesWithThisID) > 0) result = (CFBundleRef)CFArrayGetValueAtIndex(bundlesWithThisID, 0); + } + } + __CFSpinUnlock(&CFBundleGlobalDataLock); + } + return result; +} + +static CFStringRef __CFBundleCopyDescription(CFTypeRef cf) { + char buff[CFMaxPathSize]; + CFStringRef path = NULL, binaryType = NULL, retval = NULL; + if (((CFBundleRef)cf)->_url && CFURLGetFileSystemRepresentation(((CFBundleRef)cf)->_url, true, (uint8_t *)buff, CFMaxPathSize)) path = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, buff); + switch (((CFBundleRef)cf)->_binaryType) { + case __CFBundleCFMBinary: + binaryType = CFSTR(""); + break; + case __CFBundleDYLDExecutableBinary: + binaryType = CFSTR("executable, "); + break; + case __CFBundleDYLDBundleBinary: + binaryType = CFSTR("bundle, "); + break; + case __CFBundleDYLDFrameworkBinary: + binaryType = CFSTR("framework, "); + break; + case __CFBundleDLLBinary: + binaryType = CFSTR("DLL, "); + break; + case __CFBundleUnreadableBinary: + binaryType = CFSTR(""); + break; + default: + binaryType = CFSTR(""); + break; + } + if (((CFBundleRef)cf)->_plugInData._isPlugIn) { + retval = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("CFBundle/CFPlugIn %p <%@> (%@%sloaded)"), cf, path, binaryType, ((CFBundleRef)cf)->_isLoaded ? "" : "not "); + } else { + retval = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("CFBundle %p <%@> (%@%sloaded)"), cf, path, binaryType, ((CFBundleRef)cf)->_isLoaded ? "" : "not "); + } + if (path) CFRelease(path); + return retval; +} + +static void _CFBundleDeallocateGlue(const void *key, const void *value, void *context) { + CFAllocatorRef allocator = (CFAllocatorRef)context; + if (value) CFAllocatorDeallocate(allocator, (void *)value); +} + +static void __CFBundleDeallocate(CFTypeRef cf) { + CFBundleRef bundle = (CFBundleRef)cf; + CFAllocatorRef allocator; + + __CFGenericValidateType(cf, __kCFBundleTypeID); + + allocator = CFGetAllocator(bundle); + + /* Unload it */ + CFBundleUnloadExecutable(bundle); + + // Clean up plugIn stuff + _CFBundleDeallocatePlugIn(bundle); + + _CFBundleRemoveFromTables(bundle); + + if (bundle->_url) { + _CFBundleFlushCachesForURL(bundle->_url); + CFRelease(bundle->_url); + } + if (bundle->_infoDict) CFRelease(bundle->_infoDict); + if (bundle->_modDate) CFRelease(bundle->_modDate); + if (bundle->_localInfoDict) CFRelease(bundle->_localInfoDict); + if (bundle->_searchLanguages) CFRelease(bundle->_searchLanguages); + if (bundle->_glueDict) { + CFDictionaryApplyFunction(bundle->_glueDict, _CFBundleDeallocateGlue, (void *)allocator); + CFRelease(bundle->_glueDict); + } + if (bundle->_resourceData._stringTableCache) CFRelease(bundle->_resourceData._stringTableCache); +} + +static const CFRuntimeClass __CFBundleClass = { + 0, + "CFBundle", + NULL, // init + NULL, // copy + __CFBundleDeallocate, + NULL, // equal + NULL, // hash + NULL, // + __CFBundleCopyDescription +}; + +__private_extern__ void __CFBundleInitialize(void) { + __kCFBundleTypeID = _CFRuntimeRegisterClass(&__CFBundleClass); +#if defined(BINARY_SUPPORT_DLFCN) + _useDlfcn = true; +#if defined(BINARY_SUPPORT_DYLD) + if (getenv("CFBundleUseDYLD")) _useDlfcn = false; +#endif /* BINARY_SUPPORT_DYLD */ +#endif /* BINARY_SUPPORT_DLFCN */ +} + +Boolean _CFBundleUseDlfcn(void) { + return _useDlfcn; +} + +CFTypeID CFBundleGetTypeID(void) { + return __kCFBundleTypeID; +} + +CFBundleRef _CFBundleGetExistingBundleWithBundleURL(CFURLRef bundleURL) { + CFBundleRef bundle = NULL; + char buff[CFMaxPathSize]; + CFURLRef newURL = NULL; + + if (!CFURLGetFileSystemRepresentation(bundleURL, true, (uint8_t *)buff, CFMaxPathSize)) return NULL; + + newURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, (uint8_t *)buff, (CFIndex)strlen(buff), true); + if (!newURL) newURL = (CFURLRef)CFRetain(bundleURL); + bundle = _CFBundleFindByURL(newURL, false); + CFRelease(newURL); + return bundle; +} + +static CFBundleRef _CFBundleCreate(CFAllocatorRef allocator, CFURLRef bundleURL, Boolean alreadyLocked, Boolean doFinalProcessing) { + CFBundleRef bundle = NULL; + char buff[CFMaxPathSize]; + CFDateRef modDate = NULL; + Boolean exists = false; + SInt32 mode = 0; + CFURLRef newURL = NULL; + uint8_t localVersion = 0; + + if (!CFURLGetFileSystemRepresentation(bundleURL, true, (uint8_t *)buff, CFMaxPathSize)) return NULL; + + newURL = CFURLCreateFromFileSystemRepresentation(allocator, (uint8_t *)buff, (CFIndex)strlen(buff), true); + if (!newURL) newURL = (CFURLRef)CFRetain(bundleURL); + bundle = _CFBundleFindByURL(newURL, alreadyLocked); + if (bundle) { + CFRetain(bundle); + CFRelease(newURL); + return bundle; + } + + if (!_CFBundleURLLooksLikeBundleVersion(newURL, &localVersion)) { + localVersion = 3; + if (_CFGetFileProperties(allocator, newURL, &exists, &mode, NULL, &modDate, NULL, NULL) == 0) { + if (!exists || ((mode & S_IFMT) != S_IFDIR)) { + if (modDate) CFRelease(modDate); + CFRelease(newURL); + return NULL; + } + } else { + CFRelease(newURL); + return NULL; + } + } + + bundle = (CFBundleRef)_CFRuntimeCreateInstance(allocator, __kCFBundleTypeID, sizeof(struct __CFBundle) - sizeof(CFRuntimeBase), NULL); + if (!bundle) { + CFRelease(newURL); + return NULL; + } + + bundle->_url = newURL; + + bundle->_modDate = modDate; + bundle->_version = localVersion; + bundle->_infoDict = NULL; + bundle->_localInfoDict = NULL; + bundle->_searchLanguages = NULL; + +#if defined(BINARY_SUPPORT_DYLD) + /* We'll have to figure it out later */ + bundle->_binaryType = __CFBundleUnknownBinary; +#elif defined(BINARY_SUPPORT_CFM) + /* We support CFM only */ + bundle->_binaryType = __CFBundleCFMBinary; +#elif defined(BINARY_SUPPORT_DLL) + /* We support DLL only */ + bundle->_binaryType = __CFBundleDLLBinary; + bundle->_hModule = NULL; +#else + /* We'll have to figure it out later */ + bundle->_binaryType = __CFBundleUnknownBinary; +#endif /* BINARY_SUPPORT_DYLD */ + + bundle->_isLoaded = false; + bundle->_sharesStringsFiles = false; + + if (!getenv("CFBundleDisableStringsSharing") && +#if DEPLOYMENT_TARGET_MACOSX + (strncmp(buff, "/System/Library/Frameworks", 26) == 0) && +#endif + (strncmp(buff + strlen(buff) - 10, ".framework", 10) == 0)) bundle->_sharesStringsFiles = true; + + bundle->_connectionCookie = NULL; + bundle->_handleCookie = NULL; + bundle->_imageCookie = NULL; + bundle->_moduleCookie = NULL; + + bundle->_glueDict = NULL; + +#if defined(BINARY_SUPPORT_CFM) + bundle->_resourceData._executableLacksResourceFork = false; +#else /* BINARY_SUPPORT_CFM */ + bundle->_resourceData._executableLacksResourceFork = true; +#endif /* BINARY_SUPPORT_CFM */ + bundle->_resourceData._infoDictionaryFromResourceFork = false; + bundle->_resourceData._stringTableCache = NULL; + + bundle->_plugInData._isPlugIn = false; + bundle->_plugInData._loadOnDemand = false; + bundle->_plugInData._isDoingDynamicRegistration = false; + bundle->_plugInData._instanceCount = 0; + bundle->_plugInData._factories = NULL; + + CFBundleGetInfoDictionary(bundle); + + _CFBundleAddToTables(bundle, alreadyLocked); + + if (doFinalProcessing) { + _CFBundleCheckWorkarounds(bundle); + if (_CFBundleNeedsInitPlugIn(bundle)) { + if (alreadyLocked) __CFSpinUnlock(&CFBundleGlobalDataLock); + _CFBundleInitPlugIn(bundle); + if (alreadyLocked) __CFSpinLock(&CFBundleGlobalDataLock); + } + } + + return bundle; +} + +CFBundleRef CFBundleCreate(CFAllocatorRef allocator, CFURLRef bundleURL) {return _CFBundleCreate(allocator, bundleURL, false, true);} + +CFArrayRef CFBundleCreateBundlesFromDirectory(CFAllocatorRef alloc, CFURLRef directoryURL, CFStringRef bundleType) { + CFMutableArrayRef bundles = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks); + CFArrayRef URLs = _CFContentsOfDirectory(alloc, NULL, NULL, directoryURL, bundleType); + if (URLs) { + CFIndex i, c = CFArrayGetCount(URLs); + CFURLRef curURL; + CFBundleRef curBundle; + + for (i = 0; i < c; i++) { + curURL = (CFURLRef)CFArrayGetValueAtIndex(URLs, i); + curBundle = CFBundleCreate(alloc, curURL); + if (curBundle) CFArrayAppendValue(bundles, curBundle); + } + CFRelease(URLs); + } + + return bundles; +} + +CFURLRef CFBundleCopyBundleURL(CFBundleRef bundle) { + if (bundle->_url) { + CFRetain(bundle->_url); + } + return bundle->_url; +} + +void _CFBundleSetDefaultLocalization(CFStringRef localizationName) { + CFStringRef newLocalization = localizationName ? (CFStringRef)CFStringCreateCopy(kCFAllocatorSystemDefault, localizationName) : NULL; + if (_defaultLocalization) CFRelease(_defaultLocalization); + _defaultLocalization = newLocalization; +} + +CFArrayRef _CFBundleGetLanguageSearchList(CFBundleRef bundle) { + if (!bundle->_searchLanguages) { + CFMutableArrayRef langs = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); + CFStringRef devLang = CFBundleGetDevelopmentRegion(bundle); + + _CFBundleAddPreferredLprojNamesInDirectory(CFGetAllocator(bundle), bundle->_url, bundle->_version, bundle->_infoDict, langs, devLang); + + if (CFArrayGetCount(langs) == 0) { + // If the user does not prefer any of our languages, and devLang is not present, try English + _CFBundleAddPreferredLprojNamesInDirectory(CFGetAllocator(bundle), bundle->_url, bundle->_version, bundle->_infoDict, langs, CFSTR("en_US")); + } + if (CFArrayGetCount(langs) == 0) { + // if none of the preferred localizations are present, fall back on a random localization that is present + CFArrayRef localizations = CFBundleCopyBundleLocalizations(bundle); + if (localizations) { + if (CFArrayGetCount(localizations) > 0) { + _CFBundleAddPreferredLprojNamesInDirectory(CFGetAllocator(bundle), bundle->_url, bundle->_version, bundle->_infoDict, langs, (CFStringRef)CFArrayGetValueAtIndex(localizations, 0)); + } + CFRelease(localizations); + } + } + + if (devLang && !CFArrayContainsValue(langs, CFRangeMake(0, CFArrayGetCount(langs)), devLang)) { + // Make sure that devLang is on the list as a fallback for individual resources that are not present + CFArrayAppendValue(langs, devLang); + } else if (!devLang) { + // Or if there is no devLang, try some variation of English that is present + CFArrayRef localizations = CFBundleCopyBundleLocalizations(bundle); + if (localizations) { + CFStringRef en_US = CFSTR("en_US"), en = CFSTR("en"), English = CFSTR("English"); + CFRange range = CFRangeMake(0, CFArrayGetCount(localizations)); + if (CFArrayContainsValue(localizations, range, en)) { + if (!CFArrayContainsValue(langs, CFRangeMake(0, CFArrayGetCount(langs)), en)) CFArrayAppendValue(langs, en); + } else if (CFArrayContainsValue(localizations, range, English)) { + if (!CFArrayContainsValue(langs, CFRangeMake(0, CFArrayGetCount(langs)), English)) CFArrayAppendValue(langs, English); + } else if (CFArrayContainsValue(localizations, range, en_US)) { + if (!CFArrayContainsValue(langs, CFRangeMake(0, CFArrayGetCount(langs)), en_US)) CFArrayAppendValue(langs, en_US); + } + CFRelease(localizations); + } + } + if (CFArrayGetCount(langs) == 0) { + // Total backstop behavior to avoid having an empty array. + if (_defaultLocalization) { + CFArrayAppendValue(langs, _defaultLocalization); + } else { + CFArrayAppendValue(langs, CFSTR("en")); + } + } + bundle->_searchLanguages = langs; + } + return bundle->_searchLanguages; +} + +CFDictionaryRef CFBundleCopyInfoDictionaryInDirectory(CFURLRef url) {return _CFBundleCopyInfoDictionaryInDirectory(kCFAllocatorSystemDefault, url, NULL);} + +CFDictionaryRef CFBundleGetInfoDictionary(CFBundleRef bundle) { + if (!bundle->_infoDict) bundle->_infoDict = _CFBundleCopyInfoDictionaryInDirectoryWithVersion(CFGetAllocator(bundle), bundle->_url, bundle->_version); + return bundle->_infoDict; +} + +CFDictionaryRef _CFBundleGetLocalInfoDictionary(CFBundleRef bundle) {return CFBundleGetLocalInfoDictionary(bundle);} + +CFDictionaryRef CFBundleGetLocalInfoDictionary(CFBundleRef bundle) { + if (!bundle->_localInfoDict) { + CFURLRef url = CFBundleCopyResourceURL(bundle, _CFBundleLocalInfoName, _CFBundleStringTableType, NULL); + if (url) { + CFDataRef data; + SInt32 errCode; + CFStringRef errStr = NULL; + + if (CFURLCreateDataAndPropertiesFromResource(CFGetAllocator(bundle), url, &data, NULL, NULL, &errCode)) { + bundle->_localInfoDict = (CFDictionaryRef)CFPropertyListCreateFromXMLData(CFGetAllocator(bundle), data, kCFPropertyListImmutable, &errStr); + if (errStr) CFRelease(errStr); + if (bundle->_localInfoDict && CFDictionaryGetTypeID() != CFGetTypeID(bundle->_localInfoDict)) { + CFRelease(bundle->_localInfoDict); + bundle->_localInfoDict = NULL; + } + CFRelease(data); + } + CFRelease(url); + } + } + return bundle->_localInfoDict; +} + +CFPropertyListRef _CFBundleGetValueForInfoKey(CFBundleRef bundle, CFStringRef key) {return (CFPropertyListRef)CFBundleGetValueForInfoDictionaryKey(bundle, key);} + +CFTypeRef CFBundleGetValueForInfoDictionaryKey(CFBundleRef bundle, CFStringRef key) { + // Look in InfoPlist.strings first. Then look in Info.plist + CFTypeRef result = NULL; + if (bundle && key) { + CFDictionaryRef dict = CFBundleGetLocalInfoDictionary(bundle); + if (dict) result = CFDictionaryGetValue(dict, key); + if (!result) { + dict = CFBundleGetInfoDictionary(bundle); + if (dict) result = CFDictionaryGetValue(dict, key); + } + } + return result; +} + +CFStringRef CFBundleGetIdentifier(CFBundleRef bundle) { + CFStringRef bundleID = NULL; + CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); + if (infoDict) bundleID = (CFStringRef)CFDictionaryGetValue(infoDict, kCFBundleIdentifierKey); + return bundleID; +} + +#define DEVELOPMENT_STAGE 0x20 +#define ALPHA_STAGE 0x40 +#define BETA_STAGE 0x60 +#define RELEASE_STAGE 0x80 + +#define MAX_VERS_LEN 10 + +CF_INLINE Boolean _isDigit(UniChar aChar) {return (((aChar >= (UniChar)'0') && (aChar <= (UniChar)'9')) ? true : false);} + +__private_extern__ CFStringRef _CFCreateStringFromVersionNumber(CFAllocatorRef alloc, UInt32 vers) { + CFStringRef result = NULL; + uint8_t major1, major2, minor1, minor2, stage, build; + + major1 = (vers & 0xF0000000) >> 28; + major2 = (vers & 0x0F000000) >> 24; + minor1 = (vers & 0x00F00000) >> 20; + minor2 = (vers & 0x000F0000) >> 16; + stage = (vers & 0x0000FF00) >> 8; + build = (vers & 0x000000FF); + + if (stage == RELEASE_STAGE) { + if (major1 > 0) { + result = CFStringCreateWithFormat(alloc, NULL, CFSTR("%d%d.%d.%d"), major1, major2, minor1, minor2); + } else { + result = CFStringCreateWithFormat(alloc, NULL, CFSTR("%d.%d.%d"), major2, minor1, minor2); + } + } else { + if (major1 > 0) { + result = CFStringCreateWithFormat(alloc, NULL, CFSTR("%d%d.%d.%d%s%d"), major1, major2, minor1, minor2, ((stage == DEVELOPMENT_STAGE) ? "d" : ((stage == ALPHA_STAGE) ? "a" : "b")), build); + } else { + result = CFStringCreateWithFormat(alloc, NULL, CFSTR("%d.%d.%d%s%d"), major2, minor1, minor2, ((stage == DEVELOPMENT_STAGE) ? "d" : ((stage == ALPHA_STAGE) ? "a" : "b")), build); + } + } + return result; +} + +__private_extern__ UInt32 _CFVersionNumberFromString(CFStringRef versStr) { + // Parse version number from string. + // String can begin with "." for major version number 0. String can end at any point, but elements within the string cannot be skipped. + UInt32 major1 = 0, major2 = 0, minor1 = 0, minor2 = 0, stage = RELEASE_STAGE, build = 0; + UniChar versChars[MAX_VERS_LEN]; + UniChar *chars = NULL; + CFIndex len; + UInt32 theVers; + Boolean digitsDone = false; + + if (!versStr) return 0; + + len = CFStringGetLength(versStr); + + if ((len == 0) || (len > MAX_VERS_LEN)) return 0; + + CFStringGetCharacters(versStr, CFRangeMake(0, len), versChars); + chars = versChars; + + // Get major version number. + major1 = major2 = 0; + if (_isDigit(*chars)) { + major2 = *chars - (UniChar)'0'; + chars++; + len--; + if (len > 0) { + if (_isDigit(*chars)) { + major1 = major2; + major2 = *chars - (UniChar)'0'; + chars++; + len--; + if (len > 0) { + if (*chars == (UniChar)'.') { + chars++; + len--; + } else { + digitsDone = true; + } + } + } else if (*chars == (UniChar)'.') { + chars++; + len--; + } else { + digitsDone = true; + } + } + } else if (*chars == (UniChar)'.') { + chars++; + len--; + } else { + digitsDone = true; + } + + // Now major1 and major2 contain first and second digit of the major version number as ints. + // Now either len is 0 or chars points at the first char beyond the first decimal point. + + // Get the first minor version number. + if (len > 0 && !digitsDone) { + if (_isDigit(*chars)) { + minor1 = *chars - (UniChar)'0'; + chars++; + len--; + if (len > 0) { + if (*chars == (UniChar)'.') { + chars++; + len--; + } else { + digitsDone = true; + } + } + } else { + digitsDone = true; + } + } + + // Now minor1 contains the first minor version number as an int. + // Now either len is 0 or chars points at the first char beyond the second decimal point. + + // Get the second minor version number. + if (len > 0 && !digitsDone) { + if (_isDigit(*chars)) { + minor2 = *chars - (UniChar)'0'; + chars++; + len--; + } else { + digitsDone = true; + } + } + + // Now minor2 contains the second minor version number as an int. + // Now either len is 0 or chars points at the build stage letter. + + // Get the build stage letter. We must find 'd', 'a', 'b', or 'f' next, if there is anything next. + if (len > 0) { + if (*chars == (UniChar)'d') { + stage = DEVELOPMENT_STAGE; + } else if (*chars == (UniChar)'a') { + stage = ALPHA_STAGE; + } else if (*chars == (UniChar)'b') { + stage = BETA_STAGE; + } else if (*chars == (UniChar)'f') { + stage = RELEASE_STAGE; + } else { + return 0; + } + chars++; + len--; + } + + // Now stage contains the release stage. + // Now either len is 0 or chars points at the build number. + + // Get the first digit of the build number. + if (len > 0) { + if (_isDigit(*chars)) { + build = *chars - (UniChar)'0'; + chars++; + len--; + } else { + return 0; + } + } + // Get the second digit of the build number. + if (len > 0) { + if (_isDigit(*chars)) { + build *= 10; + build += *chars - (UniChar)'0'; + chars++; + len--; + } else { + return 0; + } + } + // Get the third digit of the build number. + if (len > 0) { + if (_isDigit(*chars)) { + build *= 10; + build += *chars - (UniChar)'0'; + chars++; + len--; + } else { + return 0; + } + } + + // Range check the build number and make sure we exhausted the string. + if ((build > 0xFF) || (len > 0)) return 0; + + // Build the number + theVers = major1 << 28; + theVers += major2 << 24; + theVers += minor1 << 20; + theVers += minor2 << 16; + theVers += stage << 8; + theVers += build; + + return theVers; +} + +UInt32 CFBundleGetVersionNumber(CFBundleRef bundle) { + CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); + CFTypeRef unknownVersionValue = CFDictionaryGetValue(infoDict, _kCFBundleNumericVersionKey); + CFNumberRef versNum; + UInt32 vers = 0; + + if (!unknownVersionValue) unknownVersionValue = CFDictionaryGetValue(infoDict, kCFBundleVersionKey); + if (unknownVersionValue) { + if (CFGetTypeID(unknownVersionValue) == CFStringGetTypeID()) { + // Convert a string version number into a numeric one. + vers = _CFVersionNumberFromString((CFStringRef)unknownVersionValue); + + versNum = CFNumberCreate(CFGetAllocator(bundle), kCFNumberSInt32Type, &vers); + CFDictionarySetValue((CFMutableDictionaryRef)infoDict, _kCFBundleNumericVersionKey, versNum); + CFRelease(versNum); + } else if (CFGetTypeID(unknownVersionValue) == CFNumberGetTypeID()) { + CFNumberGetValue((CFNumberRef)unknownVersionValue, kCFNumberSInt32Type, &vers); + } else { + CFDictionaryRemoveValue((CFMutableDictionaryRef)infoDict, _kCFBundleNumericVersionKey); + } + } + return vers; +} + +CFStringRef CFBundleGetDevelopmentRegion(CFBundleRef bundle) { + CFStringRef devLang = NULL; + CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); + if (infoDict) { + devLang = (CFStringRef)CFDictionaryGetValue(infoDict, kCFBundleDevelopmentRegionKey); + if (devLang && (CFGetTypeID(devLang) != CFStringGetTypeID() || CFStringGetLength(devLang) == 0)) { + devLang = NULL; + CFDictionaryRemoveValue((CFMutableDictionaryRef)infoDict, kCFBundleDevelopmentRegionKey); + } + } + + return devLang; +} + +Boolean _CFBundleGetHasChanged(CFBundleRef bundle) { + CFDateRef modDate; + Boolean result = false; + Boolean exists = false; + SInt32 mode = 0; + + if (_CFGetFileProperties(CFGetAllocator(bundle), bundle->_url, &exists, &mode, NULL, &modDate, NULL, NULL) == 0) { + // If the bundle no longer exists or is not a folder, it must have "changed" + if (!exists || ((mode & S_IFMT) != S_IFDIR)) result = true; + } else { + // Something is wrong. The stat failed. + result = true; + } + if (bundle->_modDate && !CFEqual(bundle->_modDate, modDate)) { + // mod date is different from when we created. + result = true; + } + CFRelease(modDate); + return result; +} + +void _CFBundleSetStringsFilesShared(CFBundleRef bundle, Boolean flag) { + bundle->_sharesStringsFiles = flag; +} + +Boolean _CFBundleGetStringsFilesShared(CFBundleRef bundle) { + return bundle->_sharesStringsFiles; +} + +static Boolean _urlExists(CFAllocatorRef alloc, CFURLRef url) { + Boolean exists; + return url && (0 == _CFGetFileProperties(alloc, url, &exists, NULL, NULL, NULL, NULL, NULL)) && exists; +} + +__private_extern__ CFURLRef _CFBundleCopySupportFilesDirectoryURLInDirectory(CFAllocatorRef alloc, CFURLRef bundleURL, uint8_t version) { + CFURLRef result = NULL; + if (bundleURL) { + if (1 == version) { + result = CFURLCreateWithString(alloc, _CFBundleSupportFilesURLFromBase1, bundleURL); + } else if (2 == version) { + result = CFURLCreateWithString(alloc, _CFBundleSupportFilesURLFromBase2, bundleURL); + } else { + result = (CFURLRef)CFRetain(bundleURL); + } + } + return result; +} + +CF_EXPORT CFURLRef CFBundleCopySupportFilesDirectoryURL(CFBundleRef bundle) {return _CFBundleCopySupportFilesDirectoryURLInDirectory(CFGetAllocator(bundle), bundle->_url, bundle->_version);} + +__private_extern__ CFURLRef _CFBundleCopyResourcesDirectoryURLInDirectory(CFAllocatorRef alloc, CFURLRef bundleURL, uint8_t version) { + CFURLRef result = NULL; + if (bundleURL) { + if (0 == version) { + result = CFURLCreateWithString(alloc, _CFBundleResourcesURLFromBase0, bundleURL); + } else if (1 == version) { + result = CFURLCreateWithString(alloc, _CFBundleResourcesURLFromBase1, bundleURL); + } else if (2 == version) { + result = CFURLCreateWithString(alloc, _CFBundleResourcesURLFromBase2, bundleURL); + } else { + result = (CFURLRef)CFRetain(bundleURL); + } + } + return result; +} + +CFURLRef CFBundleCopyResourcesDirectoryURL(CFBundleRef bundle) {return _CFBundleCopyResourcesDirectoryURLInDirectory(CFGetAllocator(bundle), bundle->_url, bundle->_version);} + +static CFURLRef _CFBundleCopyExecutableURLRaw(CFAllocatorRef alloc, CFURLRef urlPath, CFStringRef exeName) { + // Given an url to a folder and a name, this returns the url to the executable in that folder with that name, if it exists, and NULL otherwise. This function deals with appending the ".exe" or ".dll" on Windows. + CFURLRef executableURL = NULL; + if (!urlPath || !exeName) return NULL; + +#if DEPLOYMENT_TARGET_MACOSX + const uint8_t *image_suffix = (uint8_t *)getenv("DYLD_IMAGE_SUFFIX"); + if (image_suffix) { + CFStringRef newExeName, imageSuffix; + imageSuffix = CFStringCreateWithCString(kCFAllocatorSystemDefault, (char *)image_suffix, kCFStringEncodingUTF8); + if (CFStringHasSuffix(exeName, CFSTR(".dylib"))) { + CFStringRef bareExeName = CFStringCreateWithSubstring(alloc, exeName, CFRangeMake(0, CFStringGetLength(exeName)-6)); + newExeName = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@%@.dylib"), exeName, imageSuffix); + CFRelease(bareExeName); + } else { + newExeName = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@%@"), exeName, imageSuffix); + } + executableURL = CFURLCreateWithFileSystemPathRelativeToBase(alloc, newExeName, kCFURLPOSIXPathStyle, false, urlPath); + if (executableURL && !_urlExists(alloc, executableURL)) { + CFRelease(executableURL); + executableURL = NULL; + } + CFRelease(newExeName); + CFRelease(imageSuffix); + } +#endif + if (!executableURL) { + executableURL = CFURLCreateWithFileSystemPathRelativeToBase(alloc, exeName, kCFURLPOSIXPathStyle, false, urlPath); + if (executableURL && !_urlExists(alloc, executableURL)) { + CFRelease(executableURL); + executableURL = NULL; + } + } +#if defined(DEPLOYMENT_TARGET_WINDOWS) + if (executableURL == NULL) { + if (!CFStringHasSuffix(exeName, CFSTR(".dll"))) { + CFStringRef newExeName = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@%@"), exeName, CFSTR(".dll")); + executableURL = CFURLCreateWithString(alloc, newExeName, urlPath); + if (executableURL != NULL && !_urlExists(alloc, executableURL)) { + CFRelease(executableURL); + executableURL = NULL; + } + CFRelease(newExeName); + } + } + if (executableURL == NULL) { + if (!CFStringHasSuffix(exeName, CFSTR(".exe"))) { + CFStringRef newExeName = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@%@"), exeName, CFSTR(".exe")); + executableURL = CFURLCreateWithString(alloc, newExeName, urlPath); + if (executableURL != NULL && !_urlExists(alloc, executableURL)) { + CFRelease(executableURL); + executableURL = NULL; + } + CFRelease(newExeName); + } + } +#endif + return executableURL; +} + +static CFStringRef _CFBundleCopyExecutableName(CFAllocatorRef alloc, CFBundleRef bundle, CFURLRef url, CFDictionaryRef infoDict) { + CFStringRef executableName = NULL; + + if (!alloc && bundle) alloc = CFGetAllocator(bundle); + if (!infoDict && bundle) infoDict = CFBundleGetInfoDictionary(bundle); + if (!url && bundle) url = bundle->_url; + + if (infoDict) { + // Figure out the name of the executable. + // First try for the new key in the plist. + executableName = (CFStringRef)CFDictionaryGetValue(infoDict, kCFBundleExecutableKey); + // Second try for the old key in the plist. + if (!executableName) executableName = (CFStringRef)CFDictionaryGetValue(infoDict, _kCFBundleOldExecutableKey); + if (executableName && CFGetTypeID(executableName) == CFStringGetTypeID() && CFStringGetLength(executableName) > 0) { + CFRetain(executableName); + } else { + executableName = NULL; + } + } + if (!executableName && url) { + // Third, take the name of the bundle itself (with path extension stripped) + CFURLRef absoluteURL = CFURLCopyAbsoluteURL(url); + CFStringRef bundlePath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE); + UniChar buff[CFMaxPathSize]; + CFIndex len = CFStringGetLength(bundlePath); + CFIndex startOfBundleName, endOfBundleName; + + CFRelease(absoluteURL); + if (len > CFMaxPathSize) len = CFMaxPathSize; + CFStringGetCharacters(bundlePath, CFRangeMake(0, len), buff); + startOfBundleName = _CFStartOfLastPathComponent(buff, len); + endOfBundleName = _CFLengthAfterDeletingPathExtension(buff, len); + + if ((startOfBundleName <= len) && (endOfBundleName <= len) && (startOfBundleName < endOfBundleName)) { + executableName = CFStringCreateWithCharacters(alloc, &(buff[startOfBundleName]), (endOfBundleName - startOfBundleName)); + } + CFRelease(bundlePath); + } + + return executableName; +} + +__private_extern__ CFURLRef _CFBundleCopyResourceForkURLMayBeLocal(CFBundleRef bundle, Boolean mayBeLocal) { + CFStringRef executableName = _CFBundleCopyExecutableName(kCFAllocatorSystemDefault, bundle, NULL, NULL); + CFURLRef resourceForkURL = NULL; + if (executableName) { + if (mayBeLocal) { + resourceForkURL = CFBundleCopyResourceURL(bundle, executableName, CFSTR("rsrc"), NULL); + } else { + resourceForkURL = CFBundleCopyResourceURLForLocalization(bundle, executableName, CFSTR("rsrc"), NULL, NULL); + } + CFRelease(executableName); + } + + return resourceForkURL; +} + +CFURLRef _CFBundleCopyResourceForkURL(CFBundleRef bundle) {return _CFBundleCopyResourceForkURLMayBeLocal(bundle, true);} + +static CFURLRef _CFBundleCopyExecutableURLInDirectoryWithAllocator(CFAllocatorRef alloc, CFBundleRef bundle, CFURLRef url, CFStringRef executableName, Boolean ignoreCache, Boolean useOtherPlatform) { + uint8_t version = 0; + CFDictionaryRef infoDict = NULL; + CFStringRef executablePath = NULL; + CFURLRef executableURL = NULL; + Boolean foundIt = false; + Boolean lookupMainExe = (executableName ? false : true); + + if (bundle) { + infoDict = CFBundleGetInfoDictionary(bundle); + version = bundle->_version; + } else { + infoDict = _CFBundleCopyInfoDictionaryInDirectory(alloc, url, &version); + } + + // If we have a bundle instance and an info dict, see if we have already cached the path + if (lookupMainExe && !ignoreCache && !useOtherPlatform && bundle && infoDict) { + executablePath = (CFStringRef)CFDictionaryGetValue(infoDict, _kCFBundleExecutablePathKey); + if (executablePath) { +#if DEPLOYMENT_TARGET_MACOSX + executableURL = CFURLCreateWithFileSystemPath(alloc, executablePath, kCFURLPOSIXPathStyle, false); +#else + executableURL = CFURLCreateWithFileSystemPath(alloc, executablePath, kCFURLWindowsPathStyle, false); +#endif + if (executableURL) foundIt = true; + if (!foundIt) { + executablePath = NULL; + CFDictionaryRemoveValue((CFMutableDictionaryRef)infoDict, _kCFBundleExecutablePathKey); + } + } + } + + if (!foundIt) { + if (lookupMainExe) { + executableName = _CFBundleCopyExecutableName(alloc, bundle, url, infoDict); + } + if (executableName) { + Boolean doExecSearch = true; + // Now, look for the executable inside the bundle. + if (doExecSearch && 0 != version) { + CFURLRef exeDirURL; + CFURLRef exeSubdirURL; + + if (1 == version) { + exeDirURL = CFURLCreateWithString(alloc, _CFBundleExecutablesURLFromBase1, url); + } else if (2 == version) { + exeDirURL = CFURLCreateWithString(alloc, _CFBundleExecutablesURLFromBase2, url); + } else { + exeDirURL = (CFURLRef)CFRetain(url); + } + CFStringRef platformSubDir = useOtherPlatform ? _CFBundleGetOtherPlatformExecutablesSubdirectoryName() : _CFBundleGetPlatformExecutablesSubdirectoryName(); + exeSubdirURL = CFURLCreateWithFileSystemPathRelativeToBase(alloc, platformSubDir, kCFURLPOSIXPathStyle, true, exeDirURL); + executableURL = _CFBundleCopyExecutableURLRaw(alloc, exeSubdirURL, executableName); + if (!executableURL) { + CFRelease(exeSubdirURL); + platformSubDir = useOtherPlatform ? _CFBundleGetOtherAlternatePlatformExecutablesSubdirectoryName() : _CFBundleGetAlternatePlatformExecutablesSubdirectoryName(); + exeSubdirURL = CFURLCreateWithFileSystemPathRelativeToBase(alloc, platformSubDir, kCFURLPOSIXPathStyle, true, exeDirURL); + executableURL = _CFBundleCopyExecutableURLRaw(alloc, exeSubdirURL, executableName); + } + if (!executableURL) { + CFRelease(exeSubdirURL); + platformSubDir = useOtherPlatform ? _CFBundleGetPlatformExecutablesSubdirectoryName() : _CFBundleGetOtherPlatformExecutablesSubdirectoryName(); + exeSubdirURL = CFURLCreateWithFileSystemPathRelativeToBase(alloc, platformSubDir, kCFURLPOSIXPathStyle, true, exeDirURL); + executableURL = _CFBundleCopyExecutableURLRaw(alloc, exeSubdirURL, executableName); + } + if (!executableURL) { + CFRelease(exeSubdirURL); + platformSubDir = useOtherPlatform ? _CFBundleGetAlternatePlatformExecutablesSubdirectoryName() : _CFBundleGetOtherAlternatePlatformExecutablesSubdirectoryName(); + exeSubdirURL = CFURLCreateWithFileSystemPathRelativeToBase(alloc, platformSubDir, kCFURLPOSIXPathStyle, true, exeDirURL); + executableURL = _CFBundleCopyExecutableURLRaw(alloc, exeSubdirURL, executableName); + } + if (!executableURL) { + executableURL = _CFBundleCopyExecutableURLRaw(alloc, exeDirURL, executableName); + } + + CFRelease(exeDirURL); + CFRelease(exeSubdirURL); + } + +#if defined(DEPLOYMENT_TARGET_WINDOWS) + // Windows only: If we still haven't found the exe, look in the Executables folder. + // But only for the main bundle exe + if (lookupMainExe && (executableURL == NULL)) { + CFURLRef exeDirURL; + + exeDirURL = CFURLCreateWithString(alloc, CFSTR("../../Executables"), url); + + executableURL = _CFBundleCopyExecutableURLRaw(alloc, exeDirURL, executableName); + + CFRelease(exeDirURL); + } +#endif + + // If this was an old bundle, or we did not find the executable in the Excutables subdirectory, look directly in the bundle wrapper. + if (!executableURL) executableURL = _CFBundleCopyExecutableURLRaw(alloc, url, executableName); + if (lookupMainExe && !ignoreCache && !useOtherPlatform && bundle && infoDict && executableURL) { + // We found it. Cache the path. + CFURLRef absURL = CFURLCopyAbsoluteURL(executableURL); +#if DEPLOYMENT_TARGET_MACOSX + executablePath = CFURLCopyFileSystemPath(absURL, kCFURLPOSIXPathStyle); +#else + executablePath = CFURLCopyFileSystemPath(absURL, kCFURLWindowsPathStyle); +#endif + CFRelease(absURL); + CFDictionarySetValue((CFMutableDictionaryRef)infoDict, _kCFBundleExecutablePathKey, executablePath); + CFRelease(executablePath); + } + if (lookupMainExe && !useOtherPlatform && bundle && !executableURL) bundle->_binaryType = __CFBundleNoBinary; + if (lookupMainExe) CFRelease(executableName); + } + } + + if (!bundle && infoDict) CFRelease(infoDict); + + return executableURL; +} + +CFURLRef _CFBundleCopyExecutableURLInDirectory(CFURLRef url) {return _CFBundleCopyExecutableURLInDirectoryWithAllocator(kCFAllocatorSystemDefault, NULL, url, NULL, true, false);} + +CFURLRef _CFBundleCopyOtherExecutableURLInDirectory(CFURLRef url) {return _CFBundleCopyExecutableURLInDirectoryWithAllocator(kCFAllocatorSystemDefault, NULL, url, NULL, true, true);} + +CFURLRef CFBundleCopyExecutableURL(CFBundleRef bundle) {return _CFBundleCopyExecutableURLInDirectoryWithAllocator(CFGetAllocator(bundle), bundle, bundle->_url, NULL, false, false);} + +static CFURLRef _CFBundleCopyExecutableURLIgnoringCache(CFBundleRef bundle) {return _CFBundleCopyExecutableURLInDirectoryWithAllocator(CFGetAllocator(bundle), bundle, bundle->_url, NULL, true, false);} + +CFURLRef CFBundleCopyAuxiliaryExecutableURL(CFBundleRef bundle, CFStringRef executableName) {return _CFBundleCopyExecutableURLInDirectoryWithAllocator(CFGetAllocator(bundle), bundle, bundle->_url, executableName, true, false);} + +Boolean CFBundleIsExecutableLoaded(CFBundleRef bundle) {return bundle->_isLoaded;} + +CFBundleExecutableType CFBundleGetExecutableType(CFBundleRef bundle) { + CFBundleExecutableType result = kCFBundleOtherExecutableType; + CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); + + if (!executableURL) bundle->_binaryType = __CFBundleNoBinary; +#if defined(BINARY_SUPPORT_DYLD) + if (bundle->_binaryType == __CFBundleUnknownBinary) { + bundle->_binaryType = _CFBundleGrokBinaryType(executableURL); +#if defined(BINARY_SUPPORT_CFM) + if (bundle->_binaryType != __CFBundleCFMBinary && bundle->_binaryType != __CFBundleUnreadableBinary) bundle->_resourceData._executableLacksResourceFork = true; +#endif /* BINARY_SUPPORT_CFM */ + } +#endif /* BINARY_SUPPORT_DYLD */ + if (executableURL) CFRelease(executableURL); + + if (bundle->_binaryType == __CFBundleCFMBinary) { + result = kCFBundlePEFExecutableType; + } else if (bundle->_binaryType == __CFBundleDYLDExecutableBinary || bundle->_binaryType == __CFBundleDYLDBundleBinary || bundle->_binaryType == __CFBundleDYLDFrameworkBinary) { + result = kCFBundleMachOExecutableType; + } else if (bundle->_binaryType == __CFBundleDLLBinary) { + result = kCFBundleDLLExecutableType; + } else if (bundle->_binaryType == __CFBundleELFBinary) { + result = kCFBundleELFExecutableType; + } + return result; +} + +#define UNKNOWN_FILETYPE 0x0 +#define PEF_FILETYPE 0x1000 +#define PEF_MAGIC 0x4a6f7921 +#define PEF_CIGAM 0x21796f4a +#define TEXT_SEGMENT "__TEXT" +#define PLIST_SECTION "__info_plist" +#define OBJC_SEGMENT "__OBJC" +#define IMAGE_INFO_SECTION "__image_info" +#define LIB_X11 "/usr/X11R6/lib/libX" + +#define XLS_NAME "Book" +#define XLS_NAME2 "Workbook" +#define DOC_NAME "WordDocument" +#define PPT_NAME "PowerPoint Document" + +#define ustrncmp(x, y, z) strncmp((char *)(x), (char *)(y), (z)) +#if DEPLOYMENT_TARGET_WINDOWS +#if _MSC_VER +#define ustrncasecmp(x, y, z) _strnicmp_l((char *)(x), (char *)(y), (z), NULL) +#else +#define ustrncasecmp(x, y, z) strncasecmp((char *)(x), (char *)(y), (z)) +#endif +#elif DEPLOYMENT_TARGET_LINUX +// When compiling with -Wall the GNU C library complains about passing +// NULL to strncasecmp_l as the locale. Since a locale is never +// passed, the C locale version should be a suitable replacement. +#define ustrncasecmp(x, y, z) strncasecmp((char *)(x), (char *)(y), (z)) +#else +#define ustrncasecmp(x, y, z) strncasecmp_l((char *)(x), (char *)(y), (z), NULL) +#endif + +static const uint32_t __CFBundleMagicNumbersArray[] = { + 0xcafebabe, 0xbebafeca, 0xfeedface, 0xcefaedfe, 0xfeedfacf, 0xcffaedfe, 0x4a6f7921, 0x21796f4a, + 0x7f454c46, 0xffd8ffe0, 0x4d4d002a, 0x49492a00, 0x47494638, 0x89504e47, 0x69636e73, 0x00000100, + 0x7b5c7274, 0x25504446, 0x2e7261fd, 0x2e524d46, 0x2e736e64, 0x2e736400, 0x464f524d, 0x52494646, + 0x38425053, 0x000001b3, 0x000001ba, 0x4d546864, 0x504b0304, 0x53495421, 0x53495432, 0x53495435, + 0x53495444, 0x53747566, 0x30373037, 0x3c212d2d, 0x25215053, 0xd0cf11e0, 0x62656769, 0x3d796265, + 0x6b6f6c79, 0x3026b275, 0x0000000c, 0xfe370023, 0x09020600, 0x09040600, 0x4f676753, 0x664c6143, + 0x00010000, 0x74727565, 0x4f54544f, 0x41433130, 0xc809fe02, 0x0809fe02, 0x2356524d, 0x67696d70, + 0x3c435058, 0x28445746, 0x424f4d53, 0x49544f4c, 0x72746664 +}; + +// string, with groups of 5 characters being 1 element in the array +static const char * __CFBundleExtensionsArray = + "mach\0" "mach\0" "mach\0" "mach\0" "mach\0" "mach\0" "pef\0\0" "pef\0\0" + "elf\0\0" "jpeg\0" "tiff\0" "tiff\0" "gif\0\0" "png\0\0" "icns\0" "ico\0\0" + "rtf\0\0" "pdf\0\0" "ra\0\0\0""rm\0\0\0""au\0\0\0""au\0\0\0""iff\0\0" "riff\0" + "psd\0\0" "mpeg\0" "mpeg\0" "mid\0\0" "zip\0\0" "sit\0\0" "sit\0\0" "sit\0\0" + "sit\0\0" "sit\0\0" "cpio\0" "html\0" "ps\0\0\0""ole\0\0" "uu\0\0\0""ync\0\0" + "dmg\0\0" "wmv\0\0" "jp2\0\0" "doc\0\0" "xls\0\0" "xls\0\0" "ogg\0\0" "flac\0" + "ttf\0\0" "ttf\0\0" "otf\0\0" "dwg\0\0" "dgn\0\0" "dgn\0\0" "wrl\0\0" "xcf\0\0" + "cpx\0\0" "dwf\0\0" "bom\0\0" "lit\0\0" "rtfd\0"; + +static const char * __CFBundleOOExtensionsArray = "sxc\0\0" "sxd\0\0" "sxg\0\0" "sxi\0\0" "sxm\0\0" "sxw\0\0"; +static const char * __CFBundleODExtensionsArray = "odc\0\0" "odf\0\0" "odg\0\0" "oth\0\0" "odi\0\0" "odm\0\0" "odp\0\0" "ods\0\0" "odt\0\0"; + +#define EXTENSION_LENGTH 5 +#define NUM_EXTENSIONS 61 +#define MAGIC_BYTES_TO_READ 512 +#define DMG_BYTES_TO_READ 512 +#define ZIP_BYTES_TO_READ 1024 +#define OLE_BYTES_TO_READ 512 +#define X11_BYTES_TO_READ 4096 +#define IMAGE_INFO_BYTES_TO_READ 4096 + +#if defined(BINARY_SUPPORT_DYLD) + +CF_INLINE uint32_t _CFBundleSwapInt32Conditional(uint32_t arg, Boolean swap) {return swap ? CFSwapInt32(arg) : arg;} +CF_INLINE uint32_t _CFBundleSwapInt64Conditional(uint64_t arg, Boolean swap) {return swap ? CFSwapInt64(arg) : arg;} + +static CFDictionaryRef _CFBundleGrokInfoDictFromData(const char *bytes, uint32_t length) { + CFMutableDictionaryRef result = NULL; + CFDataRef infoData = NULL; + if (bytes && 0 < length) { + infoData = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (uint8_t *)bytes, length, kCFAllocatorNull); + if (infoData) { + result = (CFMutableDictionaryRef)CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, infoData, kCFPropertyListMutableContainers, NULL); + if (result && CFDictionaryGetTypeID() != CFGetTypeID(result)) { + CFRelease(result); + result = NULL; + } + CFRelease(infoData); + } + if (!result) result = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + } + return result; +} + +static CFDictionaryRef _CFBundleGrokInfoDictFromMainExecutable() { + unsigned long length = 0; + char *bytes = getsectdata(TEXT_SEGMENT, PLIST_SECTION, &length); + return _CFBundleGrokInfoDictFromData(bytes, length); +} + +static Boolean _CFBundleGrokObjCImageInfoFromMainExecutable(uint32_t *objcVersion, uint32_t *objcFlags) { + Boolean retval = false; + uint32_t localVersion = 0, localFlags = 0; + if (getsegbyname(OBJC_SEGMENT)) { + unsigned long length = 0; + char *bytes = getsectdata(OBJC_SEGMENT, IMAGE_INFO_SECTION, &length); + if (bytes && length >= 8) { + localVersion = *(uint32_t *)bytes; + localFlags = *(uint32_t *)(bytes + 4); + } + retval = true; + } + if (objcVersion) *objcVersion = localVersion; + if (objcFlags) *objcFlags = localFlags; + return retval; +} + +static Boolean _CFBundleGrokX11FromFile(int fd, const void *bytes, CFIndex length, uint32_t offset, Boolean swapped, Boolean sixtyFour) { + static const char libX11name[] = LIB_X11; + char *buffer = NULL; + const char *loc = NULL; + unsigned i; + Boolean result = false; + + if (fd >= 0 && lseek(fd, offset, SEEK_SET) == (off_t)offset) { + buffer = (char*)malloc(X11_BYTES_TO_READ); + if (buffer && read(fd, buffer, X11_BYTES_TO_READ) >= X11_BYTES_TO_READ) loc = buffer; + } else if (bytes && length >= offset + X11_BYTES_TO_READ) { + loc = (const char*)bytes + offset; + } + if (loc) { + if (sixtyFour) { + uint32_t ncmds = _CFBundleSwapInt32Conditional(((struct mach_header_64 *)loc)->ncmds, swapped); + uint32_t sizeofcmds = _CFBundleSwapInt32Conditional(((struct mach_header_64 *)loc)->sizeofcmds, swapped); + const char *startofcmds = loc + sizeof(struct mach_header_64); + const char *endofcmds = startofcmds + sizeofcmds; + struct dylib_command *dlp = (struct dylib_command *)startofcmds; + if (endofcmds > loc + X11_BYTES_TO_READ) endofcmds = loc + X11_BYTES_TO_READ; + for (i = 0; !result && i < ncmds && startofcmds <= (char *)dlp && (char *)dlp < endofcmds; i++) { + if (LC_LOAD_DYLIB == _CFBundleSwapInt32Conditional(dlp->cmd, swapped)) { + uint32_t nameoffset = _CFBundleSwapInt32Conditional(dlp->dylib.name.offset, swapped); + const char *name = (const char *)dlp + nameoffset; + if (startofcmds <= name && name + sizeof(libX11name) <= endofcmds && 0 == strncmp(name, libX11name, sizeof(libX11name) - 1)) result = true; + } + dlp = (struct dylib_command *)((char *)dlp + _CFBundleSwapInt32Conditional(dlp->cmdsize, swapped)); + } + } else { + uint32_t ncmds = _CFBundleSwapInt32Conditional(((struct mach_header *)loc)->ncmds, swapped); + uint32_t sizeofcmds = _CFBundleSwapInt32Conditional(((struct mach_header *)loc)->sizeofcmds, swapped); + const char *startofcmds = loc + sizeof(struct mach_header); + const char *endofcmds = startofcmds + sizeofcmds; + struct dylib_command *dlp = (struct dylib_command *)startofcmds; + if (endofcmds > loc + X11_BYTES_TO_READ) endofcmds = loc + X11_BYTES_TO_READ; + for (i = 0; !result && i < ncmds && startofcmds <= (char *)dlp && (char *)dlp < endofcmds; i++) { + if (LC_LOAD_DYLIB == _CFBundleSwapInt32Conditional(dlp->cmd, swapped)) { + uint32_t nameoffset = _CFBundleSwapInt32Conditional(dlp->dylib.name.offset, swapped); + const char *name = (const char *)dlp + nameoffset; + if (startofcmds <= name && name + sizeof(libX11name) <= endofcmds && 0 == strncmp(name, libX11name, sizeof(libX11name) - 1)) result = true; + } + dlp = (struct dylib_command *)((char *)dlp + _CFBundleSwapInt32Conditional(dlp->cmdsize, swapped)); + } + } + } + + if (buffer) free(buffer); + + return result; +} + +static CFDictionaryRef _CFBundleGrokInfoDictFromFile(int fd, const void *bytes, CFIndex length, uint32_t offset, Boolean swapped, Boolean sixtyFour) { + struct stat statBuf; + off_t fileLength = 0; + char *maploc = NULL; + const char *loc; + unsigned i, j; + CFDictionaryRef result = NULL; + Boolean foundit = false; + if (fd >= 0 && fstat(fd, &statBuf) == 0 && (maploc = (char*)mmap(0, statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) != (void *)-1) { + loc = maploc; + fileLength = statBuf.st_size; + } else { + loc = (const char*)bytes; + fileLength = length; + } + if (fileLength > offset + sizeof(struct mach_header_64)) { + if (sixtyFour) { + uint32_t ncmds = _CFBundleSwapInt32Conditional(((struct mach_header_64 *)(loc + offset))->ncmds, swapped); + uint32_t sizeofcmds = _CFBundleSwapInt32Conditional(((struct mach_header_64 *)(loc + offset))->sizeofcmds, swapped); + const char *startofcmds = loc + offset + sizeof(struct mach_header_64); + const char *endofcmds = startofcmds + sizeofcmds; + struct segment_command_64 *sgp = (struct segment_command_64 *)startofcmds; + if (endofcmds > loc + fileLength) endofcmds = loc + fileLength; + for (i = 0; !foundit && i < ncmds && startofcmds <= (char *)sgp && (char *)sgp < endofcmds; i++) { + if (LC_SEGMENT_64 == _CFBundleSwapInt32Conditional(sgp->cmd, swapped)) { + struct section_64 *sp = (struct section_64 *)((char *)sgp + sizeof(struct segment_command_64)); + uint32_t nsects = _CFBundleSwapInt32Conditional(sgp->nsects, swapped); + for (j = 0; !foundit && j < nsects && startofcmds <= (char *)sp && (char *)sp < endofcmds; j++) { + if (0 == strncmp(sp->sectname, PLIST_SECTION, sizeof(sp->sectname)) && 0 == strncmp(sp->segname, TEXT_SEGMENT, sizeof(sp->segname))) { + uint64_t sectlength64 = _CFBundleSwapInt64Conditional(sp->size, swapped); + uint32_t sectlength = (uint32_t)(sectlength64 & 0xffffffff); + uint32_t sectoffset = _CFBundleSwapInt32Conditional(sp->offset, swapped); + const char *sectbytes = loc + offset + sectoffset; + // we don't support huge-sized plists + if (sectlength64 <= 0xffffffff && loc <= sectbytes && sectbytes + sectlength <= loc + fileLength) result = _CFBundleGrokInfoDictFromData(sectbytes, sectlength); + foundit = true; + } + sp = (struct section_64 *)((char *)sp + sizeof(struct section_64)); + } + } + sgp = (struct segment_command_64 *)((char *)sgp + _CFBundleSwapInt32Conditional(sgp->cmdsize, swapped)); + } + } else { + uint32_t ncmds = _CFBundleSwapInt32Conditional(((struct mach_header *)(loc + offset))->ncmds, swapped); + uint32_t sizeofcmds = _CFBundleSwapInt32Conditional(((struct mach_header *)(loc + offset))->sizeofcmds, swapped); + const char *startofcmds = loc + offset + sizeof(struct mach_header); + const char *endofcmds = startofcmds + sizeofcmds; + struct segment_command *sgp = (struct segment_command *)startofcmds; + if (endofcmds > loc + fileLength) endofcmds = loc + fileLength; + for (i = 0; !foundit && i < ncmds && startofcmds <= (char *)sgp && (char *)sgp < endofcmds; i++) { + if (LC_SEGMENT == _CFBundleSwapInt32Conditional(sgp->cmd, swapped)) { + struct section *sp = (struct section *)((char *)sgp + sizeof(struct segment_command)); + uint32_t nsects = _CFBundleSwapInt32Conditional(sgp->nsects, swapped); + for (j = 0; !foundit && j < nsects && startofcmds <= (char *)sp && (char *)sp < endofcmds; j++) { + if (0 == strncmp(sp->sectname, PLIST_SECTION, sizeof(sp->sectname)) && 0 == strncmp(sp->segname, TEXT_SEGMENT, sizeof(sp->segname))) { + uint32_t sectlength = _CFBundleSwapInt32Conditional(sp->size, swapped); + uint32_t sectoffset = _CFBundleSwapInt32Conditional(sp->offset, swapped); + const char *sectbytes = loc + offset + sectoffset; + if (loc <= sectbytes && sectbytes + sectlength <= loc + fileLength) result = _CFBundleGrokInfoDictFromData(sectbytes, sectlength); + foundit = true; + } + sp = (struct section *)((char *)sp + sizeof(struct section)); + } + } + sgp = (struct segment_command *)((char *)sgp + _CFBundleSwapInt32Conditional(sgp->cmdsize, swapped)); + } + } + } + if (maploc) munmap(maploc, statBuf.st_size); + return result; +} + +static void _CFBundleGrokObjcImageInfoFromFile(int fd, const void *bytes, CFIndex length, uint32_t offset, Boolean swapped, Boolean sixtyFour, Boolean *hasObjc, uint32_t *objcVersion, uint32_t *objcFlags) { + uint32_t sectlength = 0, sectoffset = 0, localVersion = 0, localFlags = 0; + char *buffer = NULL; + char sectbuffer[8]; + const char *loc = NULL; + unsigned i, j; + Boolean foundit = false, localHasObjc = false; + + if (fd >= 0 && lseek(fd, offset, SEEK_SET) == (off_t)offset) { + buffer = (char*)malloc(IMAGE_INFO_BYTES_TO_READ); + if (buffer && read(fd, buffer, IMAGE_INFO_BYTES_TO_READ) >= IMAGE_INFO_BYTES_TO_READ) loc = buffer; + } else if (bytes && length >= offset + IMAGE_INFO_BYTES_TO_READ) { + loc = (const char*)bytes + offset; + } + if (loc) { + if (sixtyFour) { + uint32_t ncmds = _CFBundleSwapInt32Conditional(((struct mach_header_64 *)loc)->ncmds, swapped); + uint32_t sizeofcmds = _CFBundleSwapInt32Conditional(((struct mach_header_64 *)loc)->sizeofcmds, swapped); + const char *startofcmds = loc + sizeof(struct mach_header_64); + const char *endofcmds = startofcmds + sizeofcmds; + struct segment_command_64 *sgp = (struct segment_command_64 *)startofcmds; + if (endofcmds > loc + IMAGE_INFO_BYTES_TO_READ) endofcmds = loc + IMAGE_INFO_BYTES_TO_READ; + for (i = 0; !foundit && i < ncmds && startofcmds <= (char *)sgp && (char *)sgp < endofcmds; i++) { + if (LC_SEGMENT_64 == _CFBundleSwapInt32Conditional(sgp->cmd, swapped)) { + struct section_64 *sp = (struct section_64 *)((char *)sgp + sizeof(struct segment_command_64)); + uint32_t nsects = _CFBundleSwapInt32Conditional(sgp->nsects, swapped); + for (j = 0; !foundit && j < nsects && startofcmds <= (char *)sp && (char *)sp < endofcmds; j++) { + if (0 == strncmp(sp->segname, OBJC_SEGMENT, sizeof(sp->segname))) localHasObjc = true; + if (0 == strncmp(sp->sectname, IMAGE_INFO_SECTION, sizeof(sp->sectname)) && 0 == strncmp(sp->segname, OBJC_SEGMENT, sizeof(sp->segname))) { + uint64_t sectlength64 = _CFBundleSwapInt64Conditional(sp->size, swapped); + sectlength = (uint32_t)(sectlength64 & 0xffffffff); + sectoffset = _CFBundleSwapInt32Conditional(sp->offset, swapped); + foundit = true; + } + sp = (struct section_64 *)((char *)sp + sizeof(struct section_64)); + } + } + sgp = (struct segment_command_64 *)((char *)sgp + _CFBundleSwapInt32Conditional(sgp->cmdsize, swapped)); + } + } else { + uint32_t ncmds = _CFBundleSwapInt32Conditional(((struct mach_header *)loc)->ncmds, swapped); + uint32_t sizeofcmds = _CFBundleSwapInt32Conditional(((struct mach_header *)loc)->sizeofcmds, swapped); + const char *startofcmds = loc + sizeof(struct mach_header); + const char *endofcmds = startofcmds + sizeofcmds; + struct segment_command *sgp = (struct segment_command *)startofcmds; + if (endofcmds > loc + IMAGE_INFO_BYTES_TO_READ) endofcmds = loc + IMAGE_INFO_BYTES_TO_READ; + for (i = 0; !foundit && i < ncmds && startofcmds <= (char *)sgp && (char *)sgp < endofcmds; i++) { + if (LC_SEGMENT == _CFBundleSwapInt32Conditional(sgp->cmd, swapped)) { + struct section *sp = (struct section *)((char *)sgp + sizeof(struct segment_command)); + uint32_t nsects = _CFBundleSwapInt32Conditional(sgp->nsects, swapped); + for (j = 0; !foundit && j < nsects && startofcmds <= (char *)sp && (char *)sp < endofcmds; j++) { + if (0 == strncmp(sp->segname, OBJC_SEGMENT, sizeof(sp->segname))) localHasObjc = true; + if (0 == strncmp(sp->sectname, IMAGE_INFO_SECTION, sizeof(sp->sectname)) && 0 == strncmp(sp->segname, OBJC_SEGMENT, sizeof(sp->segname))) { + sectlength = _CFBundleSwapInt32Conditional(sp->size, swapped); + sectoffset = _CFBundleSwapInt32Conditional(sp->offset, swapped); + foundit = true; + } + sp = (struct section *)((char *)sp + sizeof(struct section)); + } + } + sgp = (struct segment_command *)((char *)sgp + _CFBundleSwapInt32Conditional(sgp->cmdsize, swapped)); + } + } + if (sectlength >= 8) { + if (fd >= 0 && lseek(fd, offset + sectoffset, SEEK_SET) == (off_t)(offset + sectoffset) && read(fd, sectbuffer, 8) >= 8) { + localVersion = _CFBundleSwapInt32Conditional(*(uint32_t *)sectbuffer, swapped); + localFlags = _CFBundleSwapInt32Conditional(*(uint32_t *)(sectbuffer + 4), swapped); + } else if (bytes && length >= offset + sectoffset + 8) { + localVersion = _CFBundleSwapInt32Conditional(*(uint32_t *)((uint32_t*)bytes + offset + sectoffset), swapped); + localFlags = _CFBundleSwapInt32Conditional(*(uint32_t *)((uint32_t*)bytes + offset + sectoffset + 4), swapped); + } + } + } + + if (buffer) free(buffer); + + if (hasObjc) *hasObjc = localHasObjc; + if (objcVersion) *objcVersion = localVersion; + if (objcFlags) *objcFlags = localFlags; +} + +static UInt32 _CFBundleGrokMachTypeForFatFile(int fd, const void *bytes, CFIndex length, Boolean *isX11, CFArrayRef *architectures, CFDictionaryRef *infodict, Boolean *hasObjc, uint32_t *objcVersion, uint32_t *objcFlags) { + UInt32 machtype = UNKNOWN_FILETYPE, magic, numFatHeaders = ((struct fat_header *)bytes)->nfat_arch, maxFatHeaders = (length - sizeof(struct fat_header)) / sizeof(struct fat_arch), i; + unsigned char buffer[sizeof(struct mach_header_64)]; + const unsigned char *moreBytes = NULL; + const NXArchInfo *archInfo = NXGetLocalArchInfo(); + struct fat_arch *fat = NULL; + + if (isX11) *isX11 = false; + if (architectures) *architectures = NULL; + if (infodict) *infodict = NULL; + if (hasObjc) *hasObjc = false; + if (objcVersion) *objcVersion = 0; + if (objcFlags) *objcFlags = 0; + if (numFatHeaders > maxFatHeaders) numFatHeaders = maxFatHeaders; + if (numFatHeaders > 0) { + fat = NXFindBestFatArch(archInfo->cputype, archInfo->cpusubtype, (struct fat_arch *)((struct fat_arch*)bytes + sizeof(struct fat_header)), numFatHeaders); + if (!fat) fat = (struct fat_arch *)((struct fat_arch *)bytes + sizeof(struct fat_header)); + if (architectures) { + CFMutableArrayRef mutableArchitectures = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); + for (i = 0; i < numFatHeaders; i++) { + CFNumberRef architecture = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberSInt32Type, (struct fat_header *)bytes + sizeof(struct fat_header) + i * sizeof(struct fat_arch)); + if (CFArrayGetFirstIndexOfValue(mutableArchitectures, CFRangeMake(0, CFArrayGetCount(mutableArchitectures)), architecture) < 0) CFArrayAppendValue(mutableArchitectures, architecture); + CFRelease(architecture); + } + *architectures = (CFArrayRef)mutableArchitectures; + } + } + if (fat) { + if (fd >= 0 && lseek(fd, fat->offset, SEEK_SET) == (off_t)fat->offset && read(fd, buffer, sizeof(struct mach_header_64)) >= (int)sizeof(struct mach_header_64)) { + moreBytes = buffer; + } else if (bytes && (uint32_t)length >= fat->offset + sizeof(struct mach_header_64)) { + moreBytes = (const unsigned char *)bytes + fat->offset; + } + if (moreBytes) { + magic = *((UInt32 *)moreBytes); + if (MH_MAGIC == magic) { + machtype = ((struct mach_header *)moreBytes)->filetype; + if (isX11 && MH_EXECUTE == machtype) *isX11 = _CFBundleGrokX11FromFile(fd, bytes, length, fat->offset, false, false); + if (infodict) *infodict = _CFBundleGrokInfoDictFromFile(fd, bytes, length, fat->offset, false, false); + if (hasObjc || objcVersion || objcFlags) _CFBundleGrokObjcImageInfoFromFile(fd, bytes, length, fat->offset, false, false, hasObjc, objcVersion, objcFlags); + } else if (MH_CIGAM == magic) { + machtype = CFSwapInt32(((struct mach_header *)moreBytes)->filetype); + if (isX11 && MH_EXECUTE == machtype) *isX11 = _CFBundleGrokX11FromFile(fd, bytes, length, fat->offset, true, false); + if (infodict) *infodict = _CFBundleGrokInfoDictFromFile(fd, bytes, length, fat->offset, true, false); + if (hasObjc || objcVersion || objcFlags) _CFBundleGrokObjcImageInfoFromFile(fd, bytes, length, fat->offset, true, false, hasObjc, objcVersion, objcFlags); + } else if (MH_MAGIC_64 == magic) { + machtype = ((struct mach_header_64 *)moreBytes)->filetype; + if (isX11 && MH_EXECUTE == machtype) *isX11 = _CFBundleGrokX11FromFile(fd, bytes, length, fat->offset, false, true); + if (infodict) *infodict = _CFBundleGrokInfoDictFromFile(fd, bytes, length, fat->offset, false, true); + if (hasObjc || objcVersion || objcFlags) _CFBundleGrokObjcImageInfoFromFile(fd, bytes, length, fat->offset, false, true, hasObjc, objcVersion, objcFlags); + } else if (MH_CIGAM_64 == magic) { + machtype = CFSwapInt32(((struct mach_header_64 *)moreBytes)->filetype); + if (isX11 && MH_EXECUTE == machtype) *isX11 = _CFBundleGrokX11FromFile(fd, bytes, length, fat->offset, true, true); + if (infodict) *infodict = _CFBundleGrokInfoDictFromFile(fd, bytes, length, fat->offset, true, true); + if (hasObjc || objcVersion || objcFlags) _CFBundleGrokObjcImageInfoFromFile(fd, bytes, length, fat->offset, true, true, hasObjc, objcVersion, objcFlags); + } + } + } + return machtype; +} + +static UInt32 _CFBundleGrokMachType(int fd, const void *bytes, CFIndex length, Boolean *isX11, CFArrayRef *architectures, CFDictionaryRef *infodict, Boolean *hasObjc, uint32_t *objcVersion, uint32_t *objcFlags) { + unsigned int magic = *((UInt32 *)bytes), machtype = UNKNOWN_FILETYPE; + CFNumberRef architecture = NULL; + CFIndex i; + + if (isX11) *isX11 = false; + if (architectures) *architectures = NULL; + if (infodict) *infodict = NULL; + if (hasObjc) *hasObjc = false; + if (objcVersion) *objcVersion = 0; + if (objcFlags) *objcFlags = 0; + if (MH_MAGIC == magic) { + machtype = ((struct mach_header *)bytes)->filetype; + if (isX11 && MH_EXECUTE == machtype) *isX11 = _CFBundleGrokX11FromFile(fd, bytes, length, 0, false, false); + if (architectures) architecture = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberSInt32Type, (char*)bytes + 4); + if (infodict) *infodict = _CFBundleGrokInfoDictFromFile(fd, bytes, length, 0, false, false); + if (hasObjc || objcVersion || objcFlags) _CFBundleGrokObjcImageInfoFromFile(fd, bytes, length, 0, false, false, hasObjc, objcVersion, objcFlags); + } else if (MH_CIGAM == magic) { + for (i = 0; i < length; i += 4) *(UInt32 *)((char*)bytes + i) = CFSwapInt32(*(UInt32 *)((char*)bytes + i)); + machtype = ((struct mach_header *)bytes)->filetype; + if (isX11 && MH_EXECUTE == machtype) *isX11 = _CFBundleGrokX11FromFile(fd, bytes, length, 0, true, false); + if (architectures) architecture = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberSInt32Type, (char*)bytes + 4); + if (infodict) *infodict = _CFBundleGrokInfoDictFromFile(fd, bytes, length, 0, true, false); + if (hasObjc || objcVersion || objcFlags) _CFBundleGrokObjcImageInfoFromFile(fd, bytes, length, 0, true, false, hasObjc, objcVersion, objcFlags); + } else if (MH_MAGIC_64 == magic) { + machtype = ((struct mach_header_64 *)bytes)->filetype; + if (isX11 && MH_EXECUTE == machtype) *isX11 = _CFBundleGrokX11FromFile(fd, bytes, length, 0, false, true); + if (architectures) architecture = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberSInt32Type, (char*)bytes + 4); + if (infodict) *infodict = _CFBundleGrokInfoDictFromFile(fd, bytes, length, 0, false, true); + if (hasObjc || objcVersion || objcFlags) _CFBundleGrokObjcImageInfoFromFile(fd, bytes, length, 0, false, true, hasObjc, objcVersion, objcFlags); + } else if (MH_CIGAM_64 == magic) { + for (i = 0; i < length; i += 4) *(UInt32 *)((char*)bytes + i) = CFSwapInt32(*(UInt32 *)((char*)bytes + i)); + machtype = ((struct mach_header_64 *)bytes)->filetype; + if (isX11 && MH_EXECUTE == machtype) *isX11 = _CFBundleGrokX11FromFile(fd, bytes, length, 0, true, true); + if (architectures) architecture = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberSInt32Type, (char*)bytes + 4); + if (infodict) *infodict = _CFBundleGrokInfoDictFromFile(fd, bytes, length, 0, true, true); + if (hasObjc || objcVersion || objcFlags) _CFBundleGrokObjcImageInfoFromFile(fd, bytes, length, 0, true, true, hasObjc, objcVersion, objcFlags); + } else if (FAT_MAGIC == magic) { + machtype = _CFBundleGrokMachTypeForFatFile(fd, bytes, length, isX11, architectures, infodict, hasObjc, objcVersion, objcFlags); + } else if (FAT_CIGAM == magic) { + for (i = 0; i < length; i += 4) *(UInt32 *)((char*)bytes + i) = CFSwapInt32(*(UInt32 *)((char*)bytes + i)); + machtype = _CFBundleGrokMachTypeForFatFile(fd, bytes, length, isX11, architectures, infodict, hasObjc, objcVersion, objcFlags); + } else if (PEF_MAGIC == magic || PEF_CIGAM == magic) { + machtype = PEF_FILETYPE; + } + if (architectures && architecture) *architectures = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&architecture, 1, &kCFTypeArrayCallBacks); + if (architecture) CFRelease(architecture); + return machtype; +} + +#endif /* BINARY_SUPPORT_DYLD */ + +static Boolean _CFBundleGrokFileTypeForZipMimeType(const unsigned char *bytes, CFIndex length, const char **ext) { + unsigned namelength = CFSwapInt16HostToLittle(*((UInt16 *)(bytes + 26))), extralength = CFSwapInt16HostToLittle(*((UInt16 *)(bytes + 28))); + const unsigned char *data = bytes + 30 + namelength + extralength; + int i = -1; + if (bytes < data && data + 56 <= bytes + length && 0 == CFSwapInt16HostToLittle(*((UInt16 *)(bytes + 8))) && (0 == ustrncasecmp(data, "application/vnd.", 16) || 0 == ustrncasecmp(data, "application/x-vnd.", 18))) { + data += ('.' == *(data + 15)) ? 16 : 18; + if (0 == ustrncasecmp(data, "sun.xml.", 8)) { + data += 8; + if (0 == ustrncasecmp(data, "calc", 4)) i = 0; + else if (0 == ustrncasecmp(data, "draw", 4)) i = 1; + else if (0 == ustrncasecmp(data, "writer.global", 13)) i = 2; + else if (0 == ustrncasecmp(data, "impress", 7)) i = 3; + else if (0 == ustrncasecmp(data, "math", 4)) i = 4; + else if (0 == ustrncasecmp(data, "writer", 6)) i = 5; + if (i >= 0 && ext) *ext = __CFBundleOOExtensionsArray + i * EXTENSION_LENGTH; + } else if (0 == ustrncasecmp(data, "oasis.opendocument.", 19)) { + data += 19; + if (0 == ustrncasecmp(data, "chart", 5)) i = 0; + else if (0 == ustrncasecmp(data, "formula", 7)) i = 1; + else if (0 == ustrncasecmp(data, "graphics", 8)) i = 2; + else if (0 == ustrncasecmp(data, "text-web", 8)) i = 3; + else if (0 == ustrncasecmp(data, "image", 5)) i = 4; + else if (0 == ustrncasecmp(data, "text-master", 11)) i = 5; + else if (0 == ustrncasecmp(data, "presentation", 12)) i = 6; + else if (0 == ustrncasecmp(data, "spreadsheet", 11)) i = 7; + else if (0 == ustrncasecmp(data, "text", 4)) i = 8; + if (i >= 0 && ext) *ext = __CFBundleODExtensionsArray + i * EXTENSION_LENGTH; + } + } else if (bytes < data && data + 41 <= bytes + length && 8 == CFSwapInt16HostToLittle(*((UInt16 *)(bytes + 8))) && 0x4b2c28c8 == CFSwapInt32HostToBig(*((UInt32 *)data)) && 0xc94c4e2c == CFSwapInt32HostToBig(*((UInt32 *)(data + 4)))) { + // AbiWord compressed mimetype odt + if (ext) *ext = "odt"; + } + return (i >= 0); +} + +static const char *_CFBundleGrokFileTypeForZipFile(int fd, const unsigned char *bytes, CFIndex length, off_t fileLength) { + const char *ext = "zip"; + const unsigned char *moreBytes = NULL; + unsigned char *buffer = NULL; + CFIndex i; + Boolean foundMimetype = false, hasMetaInf = false, hasContentXML = false, hasManifestMF = false, hasManifestXML = false, hasRels = false, hasContentTypes = false, hasWordDocument = false, hasExcelDocument = false, hasPowerPointDocument = false, hasOPF = false, hasSMIL = false; + + if (bytes) { + for (i = 0; !foundMimetype && i + 30 < length; i++) { + if (0x50 == bytes[i] && 0x4b == bytes[i + 1]) { + unsigned namelength = 0, offset = 0; + if (0x01 == bytes[i + 2] && 0x02 == bytes[i + 3]) { + namelength = (unsigned)CFSwapInt16HostToLittle(*((UInt16 *)(bytes + i + 28))); + offset = 46; + } else if (0x03 == bytes[i + 2] && 0x04 == bytes[i + 3]) { + namelength = (unsigned)CFSwapInt16HostToLittle(*((UInt16 *)(bytes + i + 26))); + offset = 30; + } + if (offset > 0 && (CFIndex)(i + offset + namelength) <= length) { + //printf("%.*s\n", namelength, bytes + i + offset); + if (8 == namelength && 30 == offset && 0 == ustrncasecmp(bytes + i + offset, "mimetype", 8)) foundMimetype = _CFBundleGrokFileTypeForZipMimeType(bytes + i, length - i, &ext); + else if (9 == namelength && 0 == ustrncasecmp(bytes + i + offset, "META-INF/", 9)) hasMetaInf = true; + else if (11 == namelength && 0 == ustrncasecmp(bytes + i + offset, "content.xml", 11)) hasContentXML = true; + else if (11 == namelength && 0 == ustrncasecmp(bytes + i + offset, "_rels/.rels", 11)) hasRels = true; + else if (19 == namelength && 0 == ustrncasecmp(bytes + i + offset, "[Content_Types].xml", 19)) hasContentTypes = true; + else if (20 == namelength && 0 == ustrncasecmp(bytes + i + offset, "META-INF/MANIFEST.MF", 20)) hasManifestMF = true; + else if (21 == namelength && 0 == ustrncasecmp(bytes + i + offset, "META-INF/manifest.xml", 21)) hasManifestXML = true; + else if (4 < namelength && 0 == ustrncasecmp(bytes + i + offset + namelength - 4, ".opf", 4)) hasOPF = true; + else if (4 < namelength && 0 == ustrncasecmp(bytes + i + offset + namelength - 4, ".sml", 4)) hasSMIL = true; + else if (5 < namelength && 0 == ustrncasecmp(bytes + i + offset + namelength - 5, ".smil", 5)) hasSMIL = true; + else if (9 < namelength && 0 == ustrncasecmp(bytes + i + offset, "word/", 5) && 0 == ustrncasecmp(bytes + i + offset + namelength - 4, ".xml", 4)) hasWordDocument = true; + else if (10 < namelength && 0 == ustrncasecmp(bytes + i + offset, "excel/", 6) && 0 == ustrncasecmp(bytes + i + offset + namelength - 4, ".xml", 4)) hasExcelDocument = true; + else if (15 < namelength && 0 == ustrncasecmp(bytes + i + offset, "powerpoint/", 11) && 0 == ustrncasecmp(bytes + i + offset + namelength - 4, ".xml", 4)) hasPowerPointDocument = true; + i += offset + namelength - 1; + } + } + } + } + if (!foundMimetype) { + if (fileLength >= ZIP_BYTES_TO_READ) { + if (fd >= 0 && lseek(fd, fileLength - ZIP_BYTES_TO_READ, SEEK_SET) == fileLength - ZIP_BYTES_TO_READ) { + buffer = (unsigned char *)malloc(ZIP_BYTES_TO_READ); + if (buffer && read(fd, buffer, ZIP_BYTES_TO_READ) >= ZIP_BYTES_TO_READ) moreBytes = buffer; + } else if (bytes && length >= ZIP_BYTES_TO_READ) { + moreBytes = bytes + length - ZIP_BYTES_TO_READ; + } + } + if (moreBytes) { + for (i = 0; i + 30 < ZIP_BYTES_TO_READ; i++) { + if (0x50 == moreBytes[i] && 0x4b == moreBytes[i + 1]) { + unsigned namelength = 0, offset = 0; + if (0x01 == moreBytes[i + 2] && 0x02 == moreBytes[i + 3]) { + namelength = CFSwapInt16HostToLittle(*((UInt16 *)(moreBytes + i + 28))); + offset = 46; + } else if (0x03 == moreBytes[i + 2] && 0x04 == moreBytes[i + 3]) { + namelength = CFSwapInt16HostToLittle(*((UInt16 *)(moreBytes + i + 26))); + offset = 30; + } + if (offset > 0 && i + offset + namelength <= ZIP_BYTES_TO_READ) { + //printf("%.*s\n", namelength, moreBytes + i + offset); + if (9 == namelength && 0 == ustrncasecmp(moreBytes + i + offset, "META-INF/", 9)) hasMetaInf = true; + else if (11 == namelength && 0 == ustrncasecmp(moreBytes + i + offset, "content.xml", 11)) hasContentXML = true; + else if (11 == namelength && 0 == ustrncasecmp(moreBytes + i + offset, "_rels/.rels", 11)) hasRels = true; + else if (19 == namelength && 0 == ustrncasecmp(moreBytes + i + offset, "[Content_Types].xml", 19)) hasContentTypes = true; + else if (20 == namelength && 0 == ustrncasecmp(moreBytes + i + offset, "META-INF/MANIFEST.MF", 20)) hasManifestMF = true; + else if (21 == namelength && 0 == ustrncasecmp(moreBytes + i + offset, "META-INF/manifest.xml", 21)) hasManifestXML = true; + else if (4 < namelength && 0 == ustrncasecmp(moreBytes + i + offset + namelength - 4, ".opf", 4)) hasOPF = true; + else if (4 < namelength && 0 == ustrncasecmp(moreBytes + i + offset + namelength - 4, ".sml", 4)) hasSMIL = true; + else if (5 < namelength && 0 == ustrncasecmp(moreBytes + i + offset + namelength - 5, ".smil", 5)) hasSMIL = true; + else if (9 < namelength && 0 == ustrncasecmp(moreBytes + i + offset, "word/", 5) && 0 == ustrncasecmp(moreBytes + i + offset + namelength - 4, ".xml", 4)) hasWordDocument = true; + else if (10 < namelength && 0 == ustrncasecmp(moreBytes + i + offset, "excel/", 6) && 0 == ustrncasecmp(moreBytes + i + offset + namelength - 4, ".xml", 4)) hasExcelDocument = true; + else if (15 < namelength && 0 == ustrncasecmp(moreBytes + i + offset, "powerpoint/", 11) && 0 == ustrncasecmp(moreBytes + i + offset + namelength - 4, ".xml", 4)) hasPowerPointDocument = true; + i += offset + namelength - 1; + } + } + } + } + //printf("hasManifestMF %d hasManifestXML %d hasContentXML %d hasRels %d hasContentTypes %d hasWordDocument %d hasExcelDocument %d hasPowerPointDocument %d hasMetaInf %d hasOPF %d hasSMIL %d\n", hasManifestMF, hasManifestXML, hasContentXML, hasRels, hasContentTypes, hasWordDocument, hasExcelDocument, hasPowerPointDocument, hasMetaInf, hasOPF, hasSMIL); + if (hasManifestMF) ext = "jar"; + else if ((hasRels || hasContentTypes) && hasWordDocument) ext = "docx"; + else if ((hasRels || hasContentTypes) && hasExcelDocument) ext = "xlsx"; + else if ((hasRels || hasContentTypes) && hasPowerPointDocument) ext = "pptx"; + else if (hasManifestXML || hasContentXML) ext = "odt"; + else if (hasMetaInf) ext = "jar"; + else if (hasOPF && hasSMIL) ext = "dtb"; + else if (hasOPF) ext = "oeb"; + + if (buffer) free(buffer); + } + return ext; +} + +static Boolean _CFBundleCheckOLEName(const char *name, const char *bytes, unsigned length) { + Boolean retval = true; + unsigned j; + for (j = 0; retval && j < length; j++) if (bytes[2 * j] != name[j]) retval = false; + return retval; +} + +static const char *_CFBundleGrokFileTypeForOLEFile(int fd, const void *bytes, CFIndex length, off_t offset) { + const char *ext = "ole", *moreBytes = NULL; + char *buffer = NULL; + + if (fd >= 0 && lseek(fd, offset, SEEK_SET) == (off_t)offset) { + buffer = (char *)malloc(OLE_BYTES_TO_READ); + if (buffer && read(fd, buffer, OLE_BYTES_TO_READ) >= OLE_BYTES_TO_READ) moreBytes = buffer; + } else if (bytes && length >= offset + OLE_BYTES_TO_READ) { + moreBytes = (char *)bytes + offset; + } + if (moreBytes) { + Boolean foundit = false; + unsigned i; + for (i = 0; !foundit && i < 4; i++) { + char namelength = moreBytes[128 * i + 64] / 2; + foundit = true; + if (sizeof(XLS_NAME) == namelength && _CFBundleCheckOLEName(XLS_NAME, moreBytes + 128 * i, namelength - 1)) ext = "xls"; + else if (sizeof(XLS_NAME2) == namelength && _CFBundleCheckOLEName(XLS_NAME2, moreBytes + 128 * i, namelength - 1)) ext = "xls"; + else if (sizeof(DOC_NAME) == namelength && _CFBundleCheckOLEName(DOC_NAME, moreBytes + 128 * i, namelength - 1)) ext = "doc"; + else if (sizeof(PPT_NAME) == namelength && _CFBundleCheckOLEName(PPT_NAME, moreBytes + 128 * i, namelength - 1)) ext = "ppt"; + else foundit = false; + } + } + + if (buffer) free(buffer); + + return ext; +} + +static Boolean _CFBundleGrokFileType(CFURLRef url, CFDataRef data, CFStringRef *extension, UInt32 *machtype, CFArrayRef *architectures, CFDictionaryRef *infodict, Boolean *hasObjc, uint32_t *objcVersion, uint32_t *objcFlags) { + struct stat statBuf; + int fd = -1; + char path[CFMaxPathSize]; + const unsigned char *bytes = NULL; + unsigned char buffer[MAGIC_BYTES_TO_READ]; + CFIndex i, length = 0; + off_t fileLength = 0; + const char *ext = NULL; + UInt32 mt = UNKNOWN_FILETYPE; +#if defined(BINARY_SUPPORT_DYLD) + Boolean isX11 = false; +#endif /* BINARY_SUPPORT_DYLD */ + Boolean isFile = false, isPlain = true, isZero = true, isHTML = false; + // extensions returned: o, tool, x11app, pef, core, dylib, bundle, elf, jpeg, jp2, tiff, gif, png, pict, icns, ico, rtf, rtfd, pdf, ra, rm, au, aiff, aifc, wav, avi, wmv, ogg, flac, psd, mpeg, mid, zip, jar, sit, cpio, html, ps, mov, qtif, ttf, otf, sfont, bmp, hqx, bin, class, tar, txt, gz, Z, uu, ync, bz, bz2, sh, pl, py, rb, dvi, sgi, tga, mp3, xml, plist, xls, doc, ppt, mp4, m4a, m4b, m4p, dmg, cwk, webarchive, dwg, dgn, pfa, pfb, afm, tfm, xcf, cpx, dwf, swf, swc, abw, bom, lit, svg, rdf, x3d, oeb, dtb, docx, xlsx, pptx, sxc, sxd, sxg, sxi, sxm, sxw, odc, odf, odg, oth, odi, odm, odp, ods + // ??? we do not distinguish between different wm types, returning wmv for any of wmv, wma, or asf + // ??? we do not distinguish between ordinary documents and template versions (often there is no difference in file contents) + // ??? the distinctions between docx, xlsx, and pptx may not be entirely reliable + if (architectures) *architectures = NULL; + if (infodict) *infodict = NULL; + if (hasObjc) *hasObjc = false; + if (objcVersion) *objcVersion = 0; + if (objcFlags) *objcFlags = 0; + if (url && CFURLGetFileSystemRepresentation(url, true, (uint8_t *)path, CFMaxPathSize) && stat(path, &statBuf) == 0 && (statBuf.st_mode & S_IFMT) == S_IFREG && (fd = open(path, O_RDONLY, 0777)) >= 0) { + length = read(fd, buffer, MAGIC_BYTES_TO_READ); + fileLength = statBuf.st_size; + bytes = buffer; + isFile = true; + } else if (data) { + length = CFDataGetLength(data); + fileLength = (off_t)length; + bytes = CFDataGetBytePtr(data); + if (length == 0) ext = "txt"; + } + if (bytes) { + if (length >= 4) { + UInt32 magic = CFSwapInt32HostToBig(*((UInt32 *)bytes)); + for (i = 0; !ext && i < NUM_EXTENSIONS; i++) { + if (__CFBundleMagicNumbersArray[i] == magic) ext = __CFBundleExtensionsArray + i * EXTENSION_LENGTH; + } + if (ext) { + if (0xcafebabe == magic && 8 <= length && 0 != *((UInt16 *)(bytes + 4))) ext = "class"; +#if defined(BINARY_SUPPORT_DYLD) + else if ((int)sizeof(struct mach_header_64) <= length) mt = _CFBundleGrokMachType(fd, bytes, length, extension ? &isX11 : NULL, architectures, infodict, hasObjc, objcVersion, objcFlags); + + if (MH_OBJECT == mt) ext = "o"; + else if (MH_EXECUTE == mt) ext = isX11 ? "x11app" : "tool"; + else if (PEF_FILETYPE == mt) ext = "pef"; + else if (MH_CORE == mt) ext = "core"; + else if (MH_DYLIB == mt) ext = "dylib"; + else if (MH_BUNDLE == mt) ext = "bundle"; +#endif /* BINARY_SUPPORT_DYLD */ + else if (0x7b5c7274 == magic && (6 > length || 'f' != bytes[4])) ext = NULL; + else if (0x00010000 == magic && (6 > length || 0 != bytes[4])) ext = NULL; + else if (0x47494638 == magic && (6 > length || (0x3761 != CFSwapInt16HostToBig(*((UInt16 *)(bytes + 4))) && 0x3961 != CFSwapInt16HostToBig(*((UInt16 *)(bytes + 4)))))) ext = NULL; + else if (0x0000000c == magic && (6 > length || 0x6a50 != CFSwapInt16HostToBig(*((UInt16 *)(bytes + 4))))) ext = NULL; + else if (0x2356524d == magic && (6 > length || 0x4c20 != CFSwapInt16HostToBig(*((UInt16 *)(bytes + 4))))) ext = NULL; + else if (0x28445746 == magic && (6 > length || 0x2056 != CFSwapInt16HostToBig(*((UInt16 *)(bytes + 4))))) ext = NULL; + else if (0x30373037 == magic && (6 > length || 0x30 != bytes[4] || !isdigit(bytes[5]))) ext = NULL; + else if (0x41433130 == magic && (6 > length || 0x31 != bytes[4] || !isdigit(bytes[5]))) ext = NULL; + else if (0x89504e47 == magic && (8 > length || 0x0d0a1a0a != CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = NULL; + else if (0x53747566 == magic && (8 > length || 0x66497420 != CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = NULL; + else if (0x3026b275 == magic && (8 > length || 0x8e66cf11 != CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = NULL; + else if (0x67696d70 == magic && (8 > length || 0x20786366 != CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = NULL; + else if (0x424f4d53 == magic && (8 > length || 0x746f7265 != CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = NULL; + else if (0x49544f4c == magic && (8 > length || 0x49544c53 != CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = NULL; + else if (0x72746664 == magic && (8 > length || 0x00000000 != CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = NULL; + else if (0x3d796265 == magic && (12 > length || 0x67696e20 != CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))) || (0x6c696e65 != CFSwapInt32HostToBig(*((UInt32 *)(bytes + 8))) && 0x70617274 != CFSwapInt32HostToBig(*((UInt32 *)(bytes + 8)))))) ext = NULL; + else if (0x25215053 == magic && 14 <= length && 0 == ustrncmp(bytes + 4, "-AdobeFont", 10)) ext = "pfa"; + else if (0x504b0304 == magic) ext = _CFBundleGrokFileTypeForZipFile(fd, bytes, length, fileLength); + else if (0x464f524d == magic) { + // IFF + ext = NULL; + if (12 <= length) { + UInt32 iffMagic = CFSwapInt32HostToBig(*((UInt32 *)(bytes + 8))); + if (0x41494646 == iffMagic) ext = "aiff"; + else if (0x414946 == iffMagic) ext = "aifc"; + } + } else if (0x52494646 == magic) { + // RIFF + ext = NULL; + if (12 <= length) { + UInt32 riffMagic = CFSwapInt32HostToBig(*((UInt32 *)(bytes + 8))); + if (0x57415645 == riffMagic) ext = "wav"; + else if (0x41564920 == riffMagic) ext = "avi"; + } + } else if (0xd0cf11e0 == magic) { + // OLE + if (52 <= length) ext = _CFBundleGrokFileTypeForOLEFile(fd, bytes, length, 512 * (1 + CFSwapInt32HostToLittle(*((UInt32 *)(bytes + 48))))); + } else if (0x62656769 == magic) { + // uu + ext = NULL; + if (76 <= length && 'n' == bytes[4] && ' ' == bytes[5] && isdigit(bytes[6]) && isdigit(bytes[7]) && isdigit(bytes[8]) && ' ' == bytes[9]) { + CFIndex endOfLine = 0; + for (i = 10; 0 == endOfLine && i < length; i++) if ('\n' == bytes[i]) endOfLine = i; + if (10 <= endOfLine && endOfLine + 62 < length && 'M' == bytes[endOfLine + 1] && '\n' == bytes[endOfLine + 62]) { + ext = "uu"; + for (i = endOfLine + 1; ext && i < endOfLine + 62; i++) if (!isprint(bytes[i])) ext = NULL; + } + } + } + } + if (extension && !ext) { + UInt16 shortMagic = CFSwapInt16HostToBig(*((UInt16 *)bytes)); + if (5 <= length && 0 == bytes[3] && 0 == bytes[4] && ((1 == bytes[1] && 1 == (0xf7 & bytes[2])) || (0 == bytes[1] && (2 == (0xf7 & bytes[2]) || (3 == (0xf7 & bytes[2])))))) ext = "tga"; + else if (8 <= length && (0x6d6f6f76 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))) || 0x6d646174 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))) || 0x77696465 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = "mov"; + else if (8 <= length && (0x69647363 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))) || 0x69646174 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = "qtif"; + else if (8 <= length && 0x424f424f == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4)))) ext = "cwk"; + else if (8 <= length && 0x62706c69 == magic && 0x7374 == CFSwapInt16HostToBig(*((UInt16 *)(bytes + 4))) && isdigit(bytes[6]) && isdigit(bytes[7])) { + for (i = 8; !ext && i < 128 && i + 16 <= length; i++) { + if (0 == ustrncmp(bytes + i, "WebMainResource", 15)) ext = "webarchive"; + } + if (!ext) ext = "plist"; + } else if (12 <= length && 0x66747970 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4)))) { + // ??? list of ftyp values needs to be checked + if (0x6d703432 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 8)))) ext = "mp4"; + else if (0x4d344120 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 8)))) ext = "m4a"; + else if (0x4d344220 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 8)))) ext = "m4b"; + else if (0x4d345020 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 8)))) ext = "m4p"; + } else if (0x424d == shortMagic && 18 <= length && 40 == CFSwapInt32HostToLittle(*((UInt32 *)(bytes + 14)))) ext = "bmp"; + else if (20 <= length && 0 == ustrncmp(bytes + 6, "%!PS-AdobeFont", 14)) ext = "pfb"; + else if (40 <= length && 0x42696e48 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 34))) && 0x6578 == CFSwapInt16HostToBig(*((UInt16 *)(bytes + 38)))) ext = "hqx"; + else if (128 <= length && 0x6d42494e == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 102)))) ext = "bin"; + else if (128 <= length && 0 == bytes[0] && 0 < bytes[1] && bytes[1] < 64 && 0 == bytes[74] && 0 == bytes[82] && 0 == (fileLength % 128)) { + unsigned df = CFSwapInt32HostToBig(*((UInt32 *)(bytes + 83))), rf = CFSwapInt32HostToBig(*((UInt32 *)(bytes + 87))), blocks = 1 + (df + 127) / 128 + (rf + 127) / 128; + if (df < 0x00800000 && rf < 0x00800000 && 1 < blocks && (off_t)(128 * blocks) == fileLength) ext = "bin"; + } else if (265 <= length && 0x75737461 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 257))) && (0x72202000 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 261))) || 0x7200 == CFSwapInt16HostToBig(*((UInt16 *)(bytes + 261))))) ext = "tar"; + else if (0xfeff == shortMagic || 0xfffe == shortMagic) ext = "txt"; + else if (0x1f9d == shortMagic) ext = "Z"; + else if (0x1f8b == shortMagic) ext = "gz"; + else if (0x71c7 == shortMagic || 0xc771 == shortMagic) ext = "cpio"; + else if (0xf702 == shortMagic) ext = "dvi"; + else if (0x01da == shortMagic && (0 == bytes[2] || 1 == bytes[2]) && (0 < bytes[3] && 16 > bytes[3])) ext = "sgi"; + else if (0x2321 == shortMagic) { + CFIndex endOfLine = 0, lastSlash = 0; + for (i = 2; 0 == endOfLine && i < length; i++) if ('\n' == bytes[i]) endOfLine = i; + if (endOfLine > 3) { + for (i = endOfLine - 1; 0 == lastSlash && i > 1; i--) if ('/' == bytes[i]) lastSlash = i; + if (lastSlash > 0) { + if (0 == ustrncmp(bytes + lastSlash + 1, "perl", 4)) ext = "pl"; + else if (0 == ustrncmp(bytes + lastSlash + 1, "python", 6)) ext = "py"; + else if (0 == ustrncmp(bytes + lastSlash + 1, "ruby", 4)) ext = "rb"; + else ext = "sh"; + } + } + } else if (0xffd8 == shortMagic && 0xff == bytes[2]) ext = "jpeg"; + else if (0x4657 == shortMagic && 0x53 == bytes[2]) ext = "swf"; + else if (0x4357 == shortMagic && 0x53 == bytes[2]) ext = "swc"; + else if (0x4944 == shortMagic && '3' == bytes[2] && 0x20 > bytes[3]) ext = "mp3"; + else if (0x425a == shortMagic && isdigit(bytes[2]) && isdigit(bytes[3])) ext = "bz"; + else if (0x425a == shortMagic && 'h' == bytes[2] && isdigit(bytes[3]) && 8 <= length && (0x31415926 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))) || 0x17724538 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = "bz2"; + else if (0x0011 == CFSwapInt16HostToBig(*((UInt16 *)(bytes + 2))) || 0x0012 == CFSwapInt16HostToBig(*((UInt16 *)(bytes + 2)))) ext = "tfm"; + else if ('<' == bytes[0] && 14 <= length) { + if (0 == ustrncasecmp(bytes + 1, "!doctype html", 13) || 0 == ustrncasecmp(bytes + 1, "head", 4) || 0 == ustrncasecmp(bytes + 1, "title", 5) || 0 == ustrncasecmp(bytes + 1, "html", 4)) { + ext = "html"; + } else if (0 == ustrncasecmp(bytes + 1, "?xml", 4)) { + for (i = 4; !ext && i < 128 && i + 20 <= length; i++) { + if ('<' == bytes[i]) { + if (0 == ustrncasecmp(bytes + i + 1, "abiword", 7)) ext = "abw"; + else if (0 == ustrncasecmp(bytes + i + 1, "!doctype svg", 12)) ext = "svg"; + else if (0 == ustrncasecmp(bytes + i + 1, "!doctype rdf", 12)) ext = "rdf"; + else if (0 == ustrncasecmp(bytes + i + 1, "!doctype x3d", 12)) ext = "x3d"; + else if (0 == ustrncasecmp(bytes + i + 1, "!doctype html", 13)) ext = "html"; + else if (0 == ustrncasecmp(bytes + i + 1, "!doctype plist", 14)) ext = "plist"; + else if (0 == ustrncasecmp(bytes + i + 1, "!doctype posingfont", 19)) ext = "sfont"; + } + } + if (!ext) ext = "xml"; + } + } + } + } + if (extension && !ext) { + //??? what about MacOSRoman? + for (i = 0; (isPlain || isZero) && !isHTML && i < length && i < 512; i++) { + char c = bytes[i]; + if (0x7f <= c || (0x20 > c && !isspace(c))) isPlain = false; + if (0 != c) isZero = false; + if (isPlain && '<' == c && i + 14 <= length && 0 == ustrncasecmp(bytes + i + 1, "!doctype html", 13)) isHTML = true; + } + if (isHTML) { + ext = "html"; + } else if (isPlain) { + if (16 <= length && 0 == ustrncmp(bytes, "StartFontMetrics", 16)) ext = "afm"; + else ext = "txt"; + } else if (isZero && length >= MAGIC_BYTES_TO_READ && fileLength >= 526) { + if (isFile) { + if (lseek(fd, 512, SEEK_SET) == 512 && read(fd, buffer, MAGIC_BYTES_TO_READ) >= 14) { + if (0x001102ff == CFSwapInt32HostToBig(*((UInt32 *)(buffer + 10)))) ext = "pict"; + } + } else { + if (526 <= length && 0x001102ff == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 522)))) ext = "pict"; + } + } + } + if (extension && (!ext || 0 == strcmp(ext, "bz2")) && length >= MAGIC_BYTES_TO_READ && fileLength >= DMG_BYTES_TO_READ) { + if (isFile) { + if (lseek(fd, fileLength - DMG_BYTES_TO_READ, SEEK_SET) == fileLength - DMG_BYTES_TO_READ && read(fd, buffer, DMG_BYTES_TO_READ) >= DMG_BYTES_TO_READ) { + if (0x6b6f6c79 == CFSwapInt32HostToBig(*((UInt32 *)buffer)) || (0x63647361 == CFSwapInt32HostToBig(*((UInt32 *)(buffer + DMG_BYTES_TO_READ - 8))) && 0x656e6372 == CFSwapInt32HostToBig(*((UInt32 *)(buffer + DMG_BYTES_TO_READ - 4))))) ext = "dmg"; + } + } else { + if (DMG_BYTES_TO_READ <= length && (0x6b6f6c79 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + length - DMG_BYTES_TO_READ))) || (0x63647361 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + length - 8))) && 0x656e6372 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + length - 4)))))) ext = "dmg"; + } + } + } + if (extension) *extension = ext ? CFStringCreateWithCStringNoCopy(kCFAllocatorSystemDefault, ext, kCFStringEncodingUTF8, kCFAllocatorNull) : NULL; + if (machtype) *machtype = mt; + if (fd >= 0) close(fd); + return (ext ? true : false); +} + +CFStringRef _CFBundleCopyFileTypeForFileURL(CFURLRef url) { + CFStringRef extension = NULL; + (void)_CFBundleGrokFileType(url, NULL, &extension, NULL, NULL, NULL, NULL, NULL, NULL); + return extension; +} + +CFStringRef _CFBundleCopyFileTypeForFileData(CFDataRef data) { + CFStringRef extension = NULL; + (void)_CFBundleGrokFileType(NULL, data, &extension, NULL, NULL, NULL, NULL, NULL, NULL); + return extension; +} + +__private_extern__ CFDictionaryRef _CFBundleCopyInfoDictionaryInExecutable(CFURLRef url) { + CFDictionaryRef result = NULL; + (void)_CFBundleGrokFileType(url, NULL, NULL, NULL, NULL, &result, NULL, NULL, NULL); + return result; +} + +__private_extern__ CFArrayRef _CFBundleCopyArchitecturesForExecutable(CFURLRef url) { + CFArrayRef result = NULL; + (void)_CFBundleGrokFileType(url, NULL, NULL, NULL, &result, NULL, NULL, NULL, NULL); + return result; +} + +static Boolean _CFBundleGetObjCImageInfoForExecutable(CFURLRef url, uint32_t *objcVersion, uint32_t *objcFlags) { + Boolean retval = false; + (void)_CFBundleGrokFileType(url, NULL, NULL, NULL, NULL, NULL, &retval, objcVersion, objcFlags); + return retval; +} + +#if defined(BINARY_SUPPORT_DYLD) + +__private_extern__ __CFPBinaryType _CFBundleGrokBinaryType(CFURLRef executableURL) { + // Attempt to grok the type of the binary by looking for DYLD magic numbers. If one of the DYLD magic numbers is found, find out what type of Mach-o file it is. Otherwise, look for the PEF magic numbers to see if it is CFM (if we understand CFM). + __CFPBinaryType result = executableURL ? __CFBundleUnreadableBinary : __CFBundleNoBinary; + UInt32 machtype = UNKNOWN_FILETYPE; + if (_CFBundleGrokFileType(executableURL, NULL, NULL, &machtype, NULL, NULL, NULL, NULL, NULL)) { + switch (machtype) { + case MH_EXECUTE: + result = __CFBundleDYLDExecutableBinary; + break; + case MH_BUNDLE: + result = __CFBundleDYLDBundleBinary; + break; + case MH_DYLIB: + result = __CFBundleDYLDFrameworkBinary; + break; +#if defined(BINARY_SUPPORT_CFM) + case PEF_FILETYPE: + result = __CFBundleCFMBinary; + break; +#endif /* BINARY_SUPPORT_CFM */ + } + } + return result; +} + +#endif /* BINARY_SUPPORT_DYLD */ + +void _CFBundleSetCFMConnectionID(CFBundleRef bundle, void *connectionID) { +#if defined(BINARY_SUPPORT_CFM) + if (bundle->_binaryType == __CFBundleUnknownBinary || bundle->_binaryType == __CFBundleUnreadableBinary) { + bundle->_binaryType = __CFBundleCFMBinary; + } +#endif /* BINARY_SUPPORT_CFM */ + bundle->_connectionCookie = connectionID; + bundle->_isLoaded = true; +} + +static CFStringRef _CFBundleCopyLastPathComponent(CFBundleRef bundle) { + CFURLRef bundleURL = CFBundleCopyBundleURL(bundle); + CFStringRef str = CFURLCopyFileSystemPath(bundleURL, kCFURLPOSIXPathStyle); + UniChar buff[CFMaxPathSize]; + CFIndex buffLen = CFStringGetLength(str), startOfLastDir = 0; + + CFRelease(bundleURL); + if (buffLen > CFMaxPathSize) buffLen = CFMaxPathSize; + CFStringGetCharacters(str, CFRangeMake(0, buffLen), buff); + CFRelease(str); + if (buffLen > 0) startOfLastDir = _CFStartOfLastPathComponent(buff, buffLen); + return CFStringCreateWithCharacters(kCFAllocatorSystemDefault, &(buff[startOfLastDir]), buffLen - startOfLastDir); +} + +static CFErrorRef _CFBundleCreateErrorDebug(CFAllocatorRef allocator, CFBundleRef bundle, CFIndex code, CFStringRef debugString) { + const void *userInfoKeys[6], *userInfoValues[6]; + CFIndex numKeys = 0; + CFURLRef bundleURL = CFBundleCopyBundleURL(bundle), absoluteURL = CFURLCopyAbsoluteURL(bundleURL), executableURL = CFBundleCopyExecutableURL(bundle); + CFBundleRef bdl = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.CoreFoundation")); + CFStringRef bundlePath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE), executablePath = executableURL ? CFURLCopyFileSystemPath(executableURL, PLATFORM_PATH_STYLE) : NULL, descFormat = NULL, desc = NULL, reason = NULL, suggestion = NULL; + CFErrorRef error; + if (bdl) { + CFStringRef name = (CFStringRef)CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleNameKey); + name = name ? (CFStringRef)CFRetain(name) : _CFBundleCopyLastPathComponent(bundle); + if (CFBundleExecutableNotFoundError == code) { + descFormat = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr4"), CFSTR("Error"), bdl, CFSTR("The bundle \\U201c%@\\U201d could not be loaded because its executable could not be located."), "NSFileNoSuchFileError"); + reason = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr4-C"), CFSTR("Error"), bdl, CFSTR("The bundle\\U2019s executable could not be located."), "NSFileNoSuchFileError"); + suggestion = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr4-R"), CFSTR("Error"), bdl, CFSTR("Try reinstalling the bundle."), "NSFileNoSuchFileError"); + } else if (CFBundleExecutableNotLoadableError == code) { + descFormat = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3584"), CFSTR("Error"), bdl, CFSTR("The bundle \\U201c%@\\U201d could not be loaded because its executable is not loadable."), "NSExecutableNotLoadableError"); + reason = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3584-C"), CFSTR("Error"), bdl, CFSTR("The bundle\\U2019s executable is not loadable."), "NSExecutableNotLoadableError"); + suggestion = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3584-R"), CFSTR("Error"), bdl, CFSTR("Try reinstalling the bundle."), "NSExecutableNotLoadableError"); + } else if (CFBundleExecutableArchitectureMismatchError == code) { + descFormat = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3585"), CFSTR("Error"), bdl, CFSTR("The bundle \\U201c%@\\U201d could not be loaded because it does not contain a version for the current architecture."), "NSExecutableArchitectureMismatchError"); + reason = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3585-C"), CFSTR("Error"), bdl, CFSTR("The bundle does not contain a version for the current architecture."), "NSExecutableArchitectureMismatchError"); + suggestion = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3585-R"), CFSTR("Error"), bdl, CFSTR("Try installing a universal version of the bundle."), "NSExecutableArchitectureMismatchError"); + } else if (CFBundleExecutableRuntimeMismatchError == code) { + descFormat = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3586"), CFSTR("Error"), bdl, CFSTR("The bundle \\U201c%@\\U201d could not be loaded because it is not compatible with the current application."), "NSExecutableRuntimeMismatchError"); + reason = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3586-C"), CFSTR("Error"), bdl, CFSTR("The bundle is not compatible with this application."), "NSExecutableRuntimeMismatchError"); + suggestion = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3586-R"), CFSTR("Error"), bdl, CFSTR("Try installing a newer version of the bundle."), "NSExecutableRuntimeMismatchError"); + } else if (CFBundleExecutableLoadError == code) { + descFormat = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3587"), CFSTR("Error"), bdl, CFSTR("The bundle \\U201c%@\\U201d could not be loaded because it is damaged or missing necessary resources."), "NSExecutableLoadError"); + reason = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3587-C"), CFSTR("Error"), bdl, CFSTR("The bundle is damaged or missing necessary resources."), "NSExecutableLoadError"); + suggestion = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3587-R"), CFSTR("Error"), bdl, CFSTR("Try reinstalling the bundle."), "NSExecutableLoadError"); + } else if (CFBundleExecutableLinkError == code) { + descFormat = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3588"), CFSTR("Error"), bdl, CFSTR("The bundle \\U201c%@\\U201d could not be loaded."), "NSExecutableLinkError"); + reason = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3588-C"), CFSTR("Error"), bdl, CFSTR("The bundle could not be loaded."), "NSExecutableLinkError"); + suggestion = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3588-R"), CFSTR("Error"), bdl, CFSTR("Try reinstalling the bundle."), "NSExecutableLinkError"); + } + if (descFormat) { + desc = CFStringCreateWithFormat(allocator, NULL, descFormat, name); + CFRelease(descFormat); + } + CFRelease(name); + } + if (bundlePath) { + userInfoKeys[numKeys] = CFSTR("NSBundlePath"); + userInfoValues[numKeys] = bundlePath; + numKeys++; + } + if (executablePath) { + userInfoKeys[numKeys] = CFSTR("NSFilePath"); + userInfoValues[numKeys] = executablePath; + numKeys++; + } + if (desc) { + userInfoKeys[numKeys] = kCFErrorLocalizedDescriptionKey; + userInfoValues[numKeys] = desc; + numKeys++; + } + if (reason) { + userInfoKeys[numKeys] = kCFErrorLocalizedFailureReasonKey; + userInfoValues[numKeys] = reason; + numKeys++; + } + if (suggestion) { + userInfoKeys[numKeys] = kCFErrorLocalizedRecoverySuggestionKey; + userInfoValues[numKeys] = suggestion; + numKeys++; + } + if (debugString) { + userInfoKeys[numKeys] = CFSTR("NSDebugDescription"); + userInfoValues[numKeys] = debugString; + numKeys++; + } + error = CFErrorCreateWithUserInfoKeysAndValues(allocator, kCFErrorDomainCocoa, code, userInfoKeys, userInfoValues, numKeys); + if (bundleURL) CFRelease(bundleURL); + if (absoluteURL) CFRelease(absoluteURL); + if (executableURL) CFRelease(executableURL); + if (bundlePath) CFRelease(bundlePath); + if (executablePath) CFRelease(executablePath); + if (desc) CFRelease(desc); + if (reason) CFRelease(reason); + if (suggestion) CFRelease(suggestion); + return error; +} + +CFErrorRef _CFBundleCreateError(CFAllocatorRef allocator, CFBundleRef bundle, CFIndex code) { + return _CFBundleCreateErrorDebug(allocator, bundle, code, NULL); +} + +Boolean _CFBundleLoadExecutableAndReturnError(CFBundleRef bundle, Boolean forceGlobal, CFErrorRef *error) { + Boolean result = false; + CFErrorRef localError = NULL, *subError = (error ? &localError : NULL); + CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); + + if (!executableURL) bundle->_binaryType = __CFBundleNoBinary; + // make sure we know whether bundle is already loaded or not +#if defined(BINARY_SUPPORT_DLFCN) + if (!bundle->_isLoaded && _useDlfcn) _CFBundleDlfcnCheckLoaded(bundle); +#endif /* BINARY_SUPPORT_DLFCN */ +#if defined(BINARY_SUPPORT_DYLD) + if (!bundle->_isLoaded) _CFBundleDYLDCheckLoaded(bundle); + // We might need to figure out what it is + if (bundle->_binaryType == __CFBundleUnknownBinary) { + bundle->_binaryType = _CFBundleGrokBinaryType(executableURL); +#if defined(BINARY_SUPPORT_CFM) + if (bundle->_binaryType != __CFBundleCFMBinary && bundle->_binaryType != __CFBundleUnreadableBinary) bundle->_resourceData._executableLacksResourceFork = true; +#endif /* BINARY_SUPPORT_CFM */ + } +#endif /* BINARY_SUPPORT_DYLD */ + if (executableURL) CFRelease(executableURL); + + if (bundle->_isLoaded) { + // Remove from the scheduled unload set if we are there. + __CFSpinLock(&CFBundleGlobalDataLock); + if (_bundlesToUnload) CFSetRemoveValue(_bundlesToUnload, bundle); + __CFSpinUnlock(&CFBundleGlobalDataLock); + return true; + } + + // Unload bundles scheduled for unloading + if (!_scheduledBundlesAreUnloading) _CFBundleUnloadScheduledBundles(); + + switch (bundle->_binaryType) { +#if defined(BINARY_SUPPORT_CFM) + case __CFBundleCFMBinary: + case __CFBundleUnreadableBinary: + result = _CFBundleCFMLoad(bundle, subError); + break; +#elif defined(BINARY_SUPPORT_DLFCN) + case __CFBundleUnreadableBinary: + result = _CFBundleDlfcnLoadBundle(bundle, forceGlobal, subError); + break; +#endif /* BINARY_SUPPORT_CFM */ +#if defined(BINARY_SUPPORT_DYLD) + case __CFBundleDYLDBundleBinary: +#if defined(BINARY_SUPPORT_DLFCN) + if (_useDlfcn) result = _CFBundleDlfcnLoadBundle(bundle, forceGlobal, subError); else +#endif /* BINARY_SUPPORT_DLFCN */ + result = _CFBundleDYLDLoadBundle(bundle, forceGlobal, subError); + break; + case __CFBundleDYLDFrameworkBinary: +#if defined(BINARY_SUPPORT_DLFCN) + if (_useDlfcn) result = _CFBundleDlfcnLoadFramework(bundle, subError); else +#endif /* BINARY_SUPPORT_DLFCN */ + result = _CFBundleDYLDLoadFramework(bundle, subError); + break; + case __CFBundleDYLDExecutableBinary: + CFLog(__kCFLogBundle, CFSTR("Attempt to load executable of a type that cannot be dynamically loaded for %@"), bundle); + if (error) localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableNotLoadableError); + break; +#endif /* BINARY_SUPPORT_DYLD */ +#if defined(BINARY_SUPPORT_DLFCN) + case __CFBundleUnknownBinary: + case __CFBundleELFBinary: + result = _CFBundleDlfcnLoadBundle(bundle, forceGlobal, subError); + break; +#endif /* BINARY_SUPPORT_DLFCN */ +#if defined(BINARY_SUPPORT_DLL) + case __CFBundleDLLBinary: + result = _CFBundleDLLLoad(bundle, subError); + break; +#endif /* BINARY_SUPPORT_DLL */ + case __CFBundleNoBinary: + CFLog(__kCFLogBundle, CFSTR("Cannot find executable for %@"), bundle); + if (error) localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableNotFoundError); + break; + default: + CFLog(__kCFLogBundle, CFSTR("Cannot recognize type of executable for %@"), bundle); + if (error) localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableNotLoadableError); + break; + } + if (result && bundle->_plugInData._isPlugIn) _CFBundlePlugInLoaded(bundle); + + if (!result && error) *error = localError; + return result; +} + +Boolean CFBundleLoadExecutableAndReturnError(CFBundleRef bundle, CFErrorRef *error) { + return _CFBundleLoadExecutableAndReturnError(bundle, false, error); +} + +Boolean CFBundleLoadExecutable(CFBundleRef bundle) { + return _CFBundleLoadExecutableAndReturnError(bundle, false, NULL); +} + +Boolean CFBundlePreflightExecutable(CFBundleRef bundle, CFErrorRef *error) { + Boolean result = false; + CFErrorRef localError = NULL, *subError = (error ? &localError : NULL); + CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); + + if (!executableURL) bundle->_binaryType = __CFBundleNoBinary; + // make sure we know whether bundle is already loaded or not +#if defined(BINARY_SUPPORT_DLFCN) + if (!bundle->_isLoaded && _useDlfcn) _CFBundleDlfcnCheckLoaded(bundle); +#endif /* BINARY_SUPPORT_DLFCN */ +#if defined(BINARY_SUPPORT_DYLD) + if (!bundle->_isLoaded) _CFBundleDYLDCheckLoaded(bundle); + // We might need to figure out what it is + if (bundle->_binaryType == __CFBundleUnknownBinary) { + bundle->_binaryType = _CFBundleGrokBinaryType(executableURL); +#if defined(BINARY_SUPPORT_CFM) + if (bundle->_binaryType != __CFBundleCFMBinary && bundle->_binaryType != __CFBundleUnreadableBinary) bundle->_resourceData._executableLacksResourceFork = true; +#endif /* BINARY_SUPPORT_CFM */ + } +#endif /* BINARY_SUPPORT_DYLD */ + if (executableURL) CFRelease(executableURL); + + if (bundle->_isLoaded) return true; + + switch (bundle->_binaryType) { +#if defined(BINARY_SUPPORT_CFM) + case __CFBundleCFMBinary: + case __CFBundleUnreadableBinary: + result = true; + break; +#elif defined(BINARY_SUPPORT_DLFCN) + case __CFBundleUnreadableBinary: + result = _CFBundleDlfcnPreflight(bundle, subError); + break; +#endif /* BINARY_SUPPORT_CFM */ +#if defined(BINARY_SUPPORT_DYLD) + case __CFBundleDYLDBundleBinary: + result = true; +#if defined(BINARY_SUPPORT_DLFCN) + result = _CFBundleDlfcnPreflight(bundle, subError); +#endif /* BINARY_SUPPORT_DLFCN */ + break; + case __CFBundleDYLDFrameworkBinary: + result = true; +#if defined(BINARY_SUPPORT_DLFCN) + result = _CFBundleDlfcnPreflight(bundle, subError); +#endif /* BINARY_SUPPORT_DLFCN */ + break; + case __CFBundleDYLDExecutableBinary: + if (error) localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableNotLoadableError); + break; +#endif /* BINARY_SUPPORT_DYLD */ +#if defined(BINARY_SUPPORT_DLFCN) + case __CFBundleUnknownBinary: + case __CFBundleELFBinary: + result = _CFBundleDlfcnPreflight(bundle, subError); + break; +#endif /* BINARY_SUPPORT_DLFCN */ +#if defined(BINARY_SUPPORT_DLL) + case __CFBundleDLLBinary: + result = true; + break; +#endif /* BINARY_SUPPORT_DLL */ + case __CFBundleNoBinary: + if (error) localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableNotFoundError); + break; + default: + if (error) localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableNotLoadableError); + break; + } + if (!result && error) *error = localError; + return result; +} + +CFArrayRef CFBundleCopyExecutableArchitectures(CFBundleRef bundle) { + CFArrayRef result = NULL; + CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); + if (executableURL) { + result = _CFBundleCopyArchitecturesForExecutable(executableURL); + CFRelease(executableURL); + } + return result; +} + +static Boolean _CFBundleGetObjCImageInfo(CFBundleRef bundle, uint32_t *objcVersion, uint32_t *objcFlags) { + Boolean retval = false; + uint32_t localVersion = 0, localFlags = 0; + CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); + if (executableURL) { + retval = _CFBundleGetObjCImageInfoForExecutable(executableURL, &localVersion, &localFlags); + CFRelease(executableURL); + } + if (objcVersion) *objcVersion = localVersion; + if (objcFlags) *objcFlags = localFlags; + return retval; +} + +void CFBundleUnloadExecutable(CFBundleRef bundle) { + // First unload bundles scheduled for unloading (if that's not what we are already doing.) + if (!_scheduledBundlesAreUnloading) _CFBundleUnloadScheduledBundles(); + + if (!bundle->_isLoaded) return; + + // Remove from the scheduled unload set if we are there. + if (!_scheduledBundlesAreUnloading) __CFSpinLock(&CFBundleGlobalDataLock); + if (_bundlesToUnload) CFSetRemoveValue(_bundlesToUnload, bundle); + if (!_scheduledBundlesAreUnloading) __CFSpinUnlock(&CFBundleGlobalDataLock); + + // Give the plugIn code a chance to realize this... + _CFPlugInWillUnload(bundle); + + switch (bundle->_binaryType) { +#if defined(BINARY_SUPPORT_CFM) + case __CFBundleCFMBinary: + _CFBundleCFMUnload(bundle); + break; +#endif /* BINARY_SUPPORT_CFM */ +#if defined(BINARY_SUPPORT_DYLD) + case __CFBundleDYLDBundleBinary: +#if defined(BINARY_SUPPORT_DLFCN) + if (bundle->_handleCookie) _CFBundleDlfcnUnload(bundle); else +#endif /* BINARY_SUPPORT_DLFCN */ + _CFBundleDYLDUnloadBundle(bundle); + break; + case __CFBundleDYLDFrameworkBinary: +#if defined(BINARY_SUPPORT_DLFCN) + if (bundle->_handleCookie && _CFExecutableLinkedOnOrAfter(CFSystemVersionLeopard)) _CFBundleDlfcnUnload(bundle); +#endif /* BINARY_SUPPORT_DLFCN */ + break; +#endif /* BINARY_SUPPORT_DYLD */ +#if defined(BINARY_SUPPORT_DLL) + case __CFBundleDLLBinary: + _CFBundleDLLUnload(bundle); + break; +#endif /* BINARY_SUPPORT_DLL */ + default: +#if defined(BINARY_SUPPORT_DLFCN) + if (bundle->_handleCookie) _CFBundleDlfcnUnload(bundle); +#endif /* BINARY_SUPPORT_DLFCN */ + break; + } + if (!bundle->_isLoaded && bundle->_glueDict) { + CFDictionaryApplyFunction(bundle->_glueDict, _CFBundleDeallocateGlue, (void *)CFGetAllocator(bundle)); + CFRelease(bundle->_glueDict); + bundle->_glueDict = NULL; + } +} + +__private_extern__ void _CFBundleScheduleForUnloading(CFBundleRef bundle) { + __CFSpinLock(&CFBundleGlobalDataLock); + if (!_bundlesToUnload) { + // Create this from the default allocator + CFSetCallBacks nonRetainingCallbacks = kCFTypeSetCallBacks; + nonRetainingCallbacks.retain = NULL; + nonRetainingCallbacks.release = NULL; + _bundlesToUnload = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &nonRetainingCallbacks); + } + CFSetAddValue(_bundlesToUnload, bundle); + __CFSpinUnlock(&CFBundleGlobalDataLock); +} + +__private_extern__ void _CFBundleUnscheduleForUnloading(CFBundleRef bundle) { + __CFSpinLock(&CFBundleGlobalDataLock); + if (_bundlesToUnload) { + CFSetRemoveValue(_bundlesToUnload, bundle); + } + __CFSpinUnlock(&CFBundleGlobalDataLock); +} + +__private_extern__ void _CFBundleUnloadScheduledBundles(void) { + __CFSpinLock(&CFBundleGlobalDataLock); + if (_bundlesToUnload) { + CFIndex c = CFSetGetCount(_bundlesToUnload); + if (c > 0) { + CFIndex i; + CFBundleRef *unloadThese = (CFBundleRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(CFBundleRef) * c, 0); + CFSetGetValues(_bundlesToUnload, (const void **)unloadThese); + _scheduledBundlesAreUnloading = true; + for (i = 0; i < c; i++) { + // This will cause them to be removed from the set. (Which is why we copied all the values out of the set up front.) + CFBundleUnloadExecutable(unloadThese[i]); + } + _scheduledBundlesAreUnloading = false; + CFAllocatorDeallocate(kCFAllocatorSystemDefault, unloadThese); + } + } + __CFSpinUnlock(&CFBundleGlobalDataLock); +} + +void *CFBundleGetFunctionPointerForName(CFBundleRef bundle, CFStringRef funcName) { + void *tvp = NULL; + // Load if necessary + if (!bundle->_isLoaded) { + if (!CFBundleLoadExecutable(bundle)) return NULL; + } + + switch (bundle->_binaryType) { +#if defined(BINARY_SUPPORT_CFM) + case __CFBundleCFMBinary: + tvp = _CFBundleCFMGetSymbolByName(bundle, funcName, kTVectorCFragSymbol); + break; +#endif /* BINARY_SUPPORT_CFM */ +#if defined(BINARY_SUPPORT_DYLD) + case __CFBundleDYLDBundleBinary: + case __CFBundleDYLDFrameworkBinary: + case __CFBundleDYLDExecutableBinary: +#if defined(BINARY_SUPPORT_DLFCN) + if (bundle->_handleCookie) return _CFBundleDlfcnGetSymbolByName(bundle, funcName); +#endif /* BINARY_SUPPORT_DLFCN */ + return _CFBundleDYLDGetSymbolByName(bundle, funcName); + break; +#endif /* BINARY_SUPPORT_DYLD */ +#if defined(BINARY_SUPPORT_DLL) + case __CFBundleDLLBinary: + tvp = _CFBundleDLLGetSymbolByName(bundle, funcName); + break; +#endif /* BINARY_SUPPORT_DLL */ + default: +#if defined(BINARY_SUPPORT_DLFCN) + if (bundle->_handleCookie) return _CFBundleDlfcnGetSymbolByName(bundle, funcName); +#endif /* BINARY_SUPPORT_DLFCN */ + break; + } +#if defined(BINARY_SUPPORT_DYLD) && defined(BINARY_SUPPORT_CFM) + if (tvp) { + if (!bundle->_glueDict) bundle->_glueDict = CFDictionaryCreateMutable(CFGetAllocator(bundle), 0, NULL, NULL); + void *fp = (void *)CFDictionaryGetValue(bundle->_glueDict, tvp); + if (!fp) { + fp = _CFBundleFunctionPointerForTVector(CFGetAllocator(bundle), tvp); + CFDictionarySetValue(bundle->_glueDict, tvp, fp); + } + return fp; + } +#endif /* BINARY_SUPPORT_DYLD && BINARY_SUPPORT_CFM */ + return tvp; +} + +void *_CFBundleGetCFMFunctionPointerForName(CFBundleRef bundle, CFStringRef funcName) { + void *fp = NULL; + // Load if necessary + if (!bundle->_isLoaded) { + if (!CFBundleLoadExecutable(bundle)) return NULL; + } +#if defined (BINARY_SUPPORT_CFM) || defined (BINARY_SUPPORT_DYLD) || defined (BINARY_SUPPORT_DLFCN) + switch (bundle->_binaryType) { +#if defined(BINARY_SUPPORT_CFM) + case __CFBundleCFMBinary: + return _CFBundleCFMGetSymbolByName(bundle, funcName, kTVectorCFragSymbol); + break; +#endif /* BINARY_SUPPORT_CFM */ +#if defined(BINARY_SUPPORT_DYLD) + case __CFBundleDYLDBundleBinary: + case __CFBundleDYLDFrameworkBinary: + case __CFBundleDYLDExecutableBinary: +#if defined(BINARY_SUPPORT_DLFCN) + if (bundle->_handleCookie) fp = _CFBundleDlfcnGetSymbolByNameWithSearch(bundle, funcName, true); else +#endif /* BINARY_SUPPORT_DLFCN */ + fp = _CFBundleDYLDGetSymbolByNameWithSearch(bundle, funcName, true); + break; +#endif /* BINARY_SUPPORT_DYLD */ + default: +#if defined(BINARY_SUPPORT_DLFCN) + if (bundle->_handleCookie) fp = _CFBundleDlfcnGetSymbolByNameWithSearch(bundle, funcName, true); +#endif /* BINARY_SUPPORT_DLFCN */ + break; + } +#endif /* BINARY_SUPPORT_CFM || BINARY_SUPPORT_DYLD || BINARY_SUPPORT_DLFCN */ +#if defined(BINARY_SUPPORT_DYLD) && defined(BINARY_SUPPORT_CFM) + if (fp) { + if (!bundle->_glueDict) bundle->_glueDict = CFDictionaryCreateMutable(CFGetAllocator(bundle), 0, NULL, NULL); + void *tvp = (void *)CFDictionaryGetValue(bundle->_glueDict, fp); + if (!tvp) { + tvp = _CFBundleTVectorForFunctionPointer(CFGetAllocator(bundle), fp); + CFDictionarySetValue(bundle->_glueDict, fp, tvp); + } + return tvp; + } +#endif /* BINARY_SUPPORT_DYLD && BINARY_SUPPORT_CFM */ + return fp; +} + +void CFBundleGetFunctionPointersForNames(CFBundleRef bundle, CFArrayRef functionNames, void *ftbl[]) { + SInt32 i, c; + + if (!ftbl) return; + + c = CFArrayGetCount(functionNames); + for (i = 0; i < c; i++) { + ftbl[i] = CFBundleGetFunctionPointerForName(bundle, (CFStringRef)CFArrayGetValueAtIndex(functionNames, i)); + } +} + +void _CFBundleGetCFMFunctionPointersForNames(CFBundleRef bundle, CFArrayRef functionNames, void *ftbl[]) { + SInt32 i, c; + + if (!ftbl) return; + + c = CFArrayGetCount(functionNames); + for (i = 0; i < c; i++) { + ftbl[i] = _CFBundleGetCFMFunctionPointerForName(bundle, (CFStringRef)CFArrayGetValueAtIndex(functionNames, i)); + } +} + +void *CFBundleGetDataPointerForName(CFBundleRef bundle, CFStringRef symbolName) { + void *dp = NULL; + // Load if necessary + if (!bundle->_isLoaded) { + if (!CFBundleLoadExecutable(bundle)) return NULL; + } + + switch (bundle->_binaryType) { +#if defined(BINARY_SUPPORT_CFM) + case __CFBundleCFMBinary: + dp = _CFBundleCFMGetSymbolByName(bundle, symbolName, kDataCFragSymbol); + break; +#endif /* BINARY_SUPPORT_CFM */ +#if defined(BINARY_SUPPORT_DYLD) + case __CFBundleDYLDBundleBinary: + case __CFBundleDYLDFrameworkBinary: + case __CFBundleDYLDExecutableBinary: +#if defined(BINARY_SUPPORT_DLFCN) + if (bundle->_handleCookie) dp = _CFBundleDlfcnGetSymbolByName(bundle, symbolName); else +#endif /* BINARY_SUPPORT_DLFCN */ + dp = _CFBundleDYLDGetSymbolByName(bundle, symbolName); + break; +#endif /* BINARY_SUPPORT_DYLD */ +#if defined(BINARY_SUPPORT_DLL) + case __CFBundleDLLBinary: + /* MF:!!! Handle this someday */ + break; +#endif /* BINARY_SUPPORT_DLL */ + default: +#if defined(BINARY_SUPPORT_DLFCN) + if (bundle->_handleCookie) dp = _CFBundleDlfcnGetSymbolByName(bundle, symbolName); +#endif /* BINARY_SUPPORT_DLFCN */ + break; + } + return dp; +} + +void CFBundleGetDataPointersForNames(CFBundleRef bundle, CFArrayRef symbolNames, void *stbl[]) { + SInt32 i, c; + + if (!stbl) return; + + c = CFArrayGetCount(symbolNames); + for (i = 0; i < c; i++) { + stbl[i] = CFBundleGetDataPointerForName(bundle, (CFStringRef)CFArrayGetValueAtIndex(symbolNames, i)); + } +} + +__private_extern__ _CFResourceData *__CFBundleGetResourceData(CFBundleRef bundle) { + return &(bundle->_resourceData); +} + +CFPlugInRef CFBundleGetPlugIn(CFBundleRef bundle) { + if (bundle->_plugInData._isPlugIn) { + return (CFPlugInRef)bundle; + } else { + return NULL; + } +} + +__private_extern__ _CFPlugInData *__CFBundleGetPlugInData(CFBundleRef bundle) { + return &(bundle->_plugInData); +} + +__private_extern__ Boolean _CFBundleCouldBeBundle(CFURLRef url) { + Boolean result = false; + Boolean exists; + SInt32 mode; + + if (_CFGetFileProperties(kCFAllocatorSystemDefault, url, &exists, &mode, NULL, NULL, NULL, NULL) == 0) { + result = (exists && ((mode & S_IFMT) == S_IFDIR) && ((mode & 0444) != 0)); + } + return result; +} + +#define LENGTH_OF(A) (sizeof(A) / sizeof(A[0])) + +__private_extern__ CFURLRef _CFBundleCopyFrameworkURLForExecutablePath(CFAllocatorRef alloc, CFStringRef executablePath) { + // MF:!!! Implement me. We need to be able to find the bundle from the exe, dealing with old vs. new as well as the Executables dir business on Windows. +#if DEPLOYMENT_TARGET_WINDOWS + UniChar executablesToFrameworksPathBuff[] = {'.', '.', '\\', 'F', 'r', 'a', 'm', 'e', 'w', 'o', 'r', 'k', 's'}; // length 16 + UniChar executablesToPrivateFrameworksPathBuff[] = {'.', '.', '\\', 'P', 'r', 'i', 'v', 'a', 't', 'e', 'F', 'r', 'a', 'm', 'e', 'w', 'o', 'r', 'k', 's'}; // length 23 + UniChar frameworksExtension[] = {'f', 'r', 'a', 'm', 'e', 'w', 'o', 'r', 'k'}; // length 9 +#endif + + UniChar pathBuff[CFMaxPathSize]; + UniChar nameBuff[CFMaxPathSize]; + CFIndex length, nameStart, nameLength, savedLength; + CFMutableStringRef cheapStr = CFStringCreateMutableWithExternalCharactersNoCopy(alloc, NULL, 0, 0, NULL); + CFURLRef bundleURL = NULL; + + length = CFStringGetLength(executablePath); + if (length > CFMaxPathSize) length = CFMaxPathSize; + CFStringGetCharacters(executablePath, CFRangeMake(0, length), pathBuff); + + // Save the name in nameBuff + length = _CFLengthAfterDeletingPathExtension(pathBuff, length); + nameStart = _CFStartOfLastPathComponent(pathBuff, length); + nameLength = length - nameStart; + memmove(nameBuff, &(pathBuff[nameStart]), nameLength * sizeof(UniChar)); + + // Strip the name from pathBuff + length = _CFLengthAfterDeletingLastPathComponent(pathBuff, length); + savedLength = length; + +#if DEPLOYMENT_TARGET_WINDOWS + // * (Windows-only) First check the "Executables" directory parallel to the "Frameworks" directory case. + if (_CFAppendPathComponent(pathBuff, &length, CFMaxPathSize, executablesToFrameworksPathBuff, 16) && _CFAppendPathComponent(pathBuff, &length, CFMaxPathSize, nameBuff, nameLength) && _CFAppendPathExtension(pathBuff, &length, CFMaxPathSize, frameworksExtension, 9)) { + CFStringSetExternalCharactersNoCopy(cheapStr, pathBuff, length, CFMaxPathSize); + bundleURL = CFURLCreateWithFileSystemPath(alloc, cheapStr, PLATFORM_PATH_STYLE, true); + if (!_CFBundleCouldBeBundle(bundleURL)) { + CFRelease(bundleURL); + bundleURL = NULL; + } + } + // * (Windows-only) Next check the "Executables" directory parallel to the "PrivateFrameworks" directory case. + if (bundleURL == NULL) { + length = savedLength; + if (_CFAppendPathComponent(pathBuff, &length, CFMaxPathSize, executablesToPrivateFrameworksPathBuff, 23) && _CFAppendPathComponent(pathBuff, &length, CFMaxPathSize, nameBuff, nameLength) && _CFAppendPathExtension(pathBuff, &length, CFMaxPathSize, frameworksExtension, 9)) { + CFStringSetExternalCharactersNoCopy(cheapStr, pathBuff, length, CFMaxPathSize); + bundleURL = CFURLCreateWithFileSystemPath(alloc, cheapStr, PLATFORM_PATH_STYLE, true); + if (!_CFBundleCouldBeBundle(bundleURL)) { + CFRelease(bundleURL); + bundleURL = NULL; + } + } + } +#endif + + // * Finally check the executable inside the framework case. + if (!bundleURL) { + // MF:!!! This should ensure the framework name is the same as the library name! + CFIndex curStart; + + length = savedLength; + // To catch all the cases, we just peel off level looking for one ending in .framework or one called "Supporting Files". + + while (length > 0) { + curStart = _CFStartOfLastPathComponent(pathBuff, length); + if (curStart >= length) { + break; + } + CFStringSetExternalCharactersNoCopy(cheapStr, &(pathBuff[curStart]), length - curStart, CFMaxPathSize - curStart); + if (CFEqual(cheapStr, _CFBundleSupportFilesDirectoryName1) || CFEqual(cheapStr, _CFBundleSupportFilesDirectoryName2)) { + length = _CFLengthAfterDeletingLastPathComponent(pathBuff, length); + CFStringSetExternalCharactersNoCopy(cheapStr, pathBuff, length, CFMaxPathSize); + bundleURL = CFURLCreateWithFileSystemPath(alloc, cheapStr, PLATFORM_PATH_STYLE, true); + if (!_CFBundleCouldBeBundle(bundleURL)) { + CFRelease(bundleURL); + bundleURL = NULL; + } + break; + } else if (CFStringHasSuffix(cheapStr, CFSTR(".framework"))) { + CFStringSetExternalCharactersNoCopy(cheapStr, pathBuff, length, CFMaxPathSize); + bundleURL = CFURLCreateWithFileSystemPath(alloc, cheapStr, PLATFORM_PATH_STYLE, true); + if (!_CFBundleCouldBeBundle(bundleURL)) { + CFRelease(bundleURL); + bundleURL = NULL; + } + break; + } + length = _CFLengthAfterDeletingLastPathComponent(pathBuff, length); + } + } + + CFStringSetExternalCharactersNoCopy(cheapStr, NULL, 0, 0); + CFRelease(cheapStr); + + return bundleURL; +} + +static void _CFBundleEnsureBundleExistsForImagePath(CFStringRef imagePath) { + // This finds the bundle for the given path. + // If an image path corresponds to a bundle, we see if there is already a bundle instance. If there is and it is NOT in the _dynamicBundles array, it is added to the staticBundles. Do not add the main bundle to the list here. + CFBundleRef bundle; + CFURLRef curURL = _CFBundleCopyFrameworkURLForExecutablePath(kCFAllocatorSystemDefault, imagePath); + Boolean doFinalProcessing = false; + + if (curURL) { + bundle = _CFBundleFindByURL(curURL, true); + if (!bundle) { + bundle = _CFBundleCreate(kCFAllocatorSystemDefault, curURL, true, false); + doFinalProcessing = true; + } + if (bundle && !bundle->_isLoaded) { + // make sure that these bundles listed as loaded, and mark them frameworks (we probably can't see anything else here, and we cannot unload them) +#if defined(BINARY_SUPPORT_DLFCN) + if (!bundle->_isLoaded && _useDlfcn) _CFBundleDlfcnCheckLoaded(bundle); +#endif /* BINARY_SUPPORT_DLFCN */ +#if defined(BINARY_SUPPORT_DYLD) + if (bundle->_binaryType == __CFBundleUnknownBinary) bundle->_binaryType = __CFBundleDYLDFrameworkBinary; + if (bundle->_binaryType != __CFBundleCFMBinary && bundle->_binaryType != __CFBundleUnreadableBinary) bundle->_resourceData._executableLacksResourceFork = true; + if (!bundle->_isLoaded) _CFBundleDYLDCheckLoaded(bundle); +#endif /* BINARY_SUPPORT_DYLD */ +#if LOG_BUNDLE_LOAD + if (!bundle->_isLoaded) printf("ensure bundle %p set loaded fallback, handle %p image %p conn %p\n", bundle, bundle->_handleCookie, bundle->_imageCookie, bundle->_connectionCookie); +#endif /* LOG_BUNDLE_LOAD */ + bundle->_isLoaded = true; + } + // Perform delayed final processing steps. + // This must be done after _isLoaded has been set. + if (bundle && doFinalProcessing) { + _CFBundleCheckWorkarounds(bundle); + if (_CFBundleNeedsInitPlugIn(bundle)) { + __CFSpinUnlock(&CFBundleGlobalDataLock); + _CFBundleInitPlugIn(bundle); + __CFSpinLock(&CFBundleGlobalDataLock); + } + } + CFRelease(curURL); + } +} + +static void _CFBundleEnsureBundlesExistForImagePaths(CFArrayRef imagePaths) { + // This finds the bundles for the given paths. + // If an image path corresponds to a bundle, we see if there is already a bundle instance. If there is and it is NOT in the _dynamicBundles array, it is added to the staticBundles. Do not add the main bundle to the list here (even if it appears in imagePaths). + CFIndex i, imagePathCount = CFArrayGetCount(imagePaths); + + for (i = 0; i < imagePathCount; i++) { + _CFBundleEnsureBundleExistsForImagePath((CFStringRef)CFArrayGetValueAtIndex(imagePaths, i)); + } +} + +static void _CFBundleEnsureBundlesUpToDateWithHintAlreadyLocked(CFStringRef hint) { + CFArrayRef imagePaths = NULL; + // Tickle the main bundle into existence + (void)_CFBundleGetMainBundleAlreadyLocked(); +#if defined(BINARY_SUPPORT_DYLD) + imagePaths = _CFBundleDYLDCopyLoadedImagePathsForHint(hint); +#endif /* BINARY_SUPPORT_DYLD */ + if (imagePaths) { + _CFBundleEnsureBundlesExistForImagePaths(imagePaths); + CFRelease(imagePaths); + } +} + +static void _CFBundleEnsureAllBundlesUpToDateAlreadyLocked(void) { + // This method returns all the statically linked bundles. This includes the main bundle as well as any frameworks that the process was linked against at launch time. It does not include frameworks or opther bundles that were loaded dynamically. + CFArrayRef imagePaths = NULL; + + // Tickle the main bundle into existence + (void)_CFBundleGetMainBundleAlreadyLocked(); + +#if defined(BINARY_SUPPORT_DLL) +// Dont know how to find static bundles for DLLs +#endif /* BINARY_SUPPORT_DLL */ + +#if defined(BINARY_SUPPORT_CFM) +// CFM bundles are supplied to us by CFM, so we do not need to figure them out ourselves +#endif /* BINARY_SUPPORT_CFM */ + +#if defined(BINARY_SUPPORT_DYLD) + imagePaths = _CFBundleDYLDCopyLoadedImagePathsIfChanged(); +#endif /* BINARY_SUPPORT_DYLD */ + if (imagePaths) { + _CFBundleEnsureBundlesExistForImagePaths(imagePaths); + CFRelease(imagePaths); + } +} + +CFArrayRef CFBundleGetAllBundles(void) { + // To answer this properly, we have to have created the static bundles! + __CFSpinLock(&CFBundleGlobalDataLock); + _CFBundleEnsureAllBundlesUpToDateAlreadyLocked(); + __CFSpinUnlock(&CFBundleGlobalDataLock); + return _allBundles; +} + +uint8_t _CFBundleLayoutVersion(CFBundleRef bundle) {return bundle->_version;} + +CF_EXPORT CFURLRef _CFBundleCopyInfoPlistURL(CFBundleRef bundle) { + CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); + CFURLRef url = (CFURLRef)CFDictionaryGetValue(infoDict, _kCFBundleInfoPlistURLKey); + if (!url) url = (CFURLRef)CFDictionaryGetValue(infoDict, _kCFBundleRawInfoPlistURLKey); + return (url ? (CFURLRef)CFRetain(url) : NULL); +} + +CF_EXPORT CFURLRef _CFBundleCopyPrivateFrameworksURL(CFBundleRef bundle) {return CFBundleCopyPrivateFrameworksURL(bundle);} + +CF_EXPORT CFURLRef CFBundleCopyPrivateFrameworksURL(CFBundleRef bundle) { + CFURLRef result = NULL; + + if (1 == bundle->_version) { + result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundlePrivateFrameworksURLFromBase1, bundle->_url); + } else if (2 == bundle->_version) { + result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundlePrivateFrameworksURLFromBase2, bundle->_url); + } else { + result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundlePrivateFrameworksURLFromBase0, bundle->_url); + } + return result; +} + +CF_EXPORT CFURLRef _CFBundleCopySharedFrameworksURL(CFBundleRef bundle) {return CFBundleCopySharedFrameworksURL(bundle);} + +CF_EXPORT CFURLRef CFBundleCopySharedFrameworksURL(CFBundleRef bundle) { + CFURLRef result = NULL; + + if (1 == bundle->_version) { + result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundleSharedFrameworksURLFromBase1, bundle->_url); + } else if (2 == bundle->_version) { + result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundleSharedFrameworksURLFromBase2, bundle->_url); + } else { + result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundleSharedFrameworksURLFromBase0, bundle->_url); + } + return result; +} + +CF_EXPORT CFURLRef _CFBundleCopySharedSupportURL(CFBundleRef bundle) {return CFBundleCopySharedSupportURL(bundle);} + +CF_EXPORT CFURLRef CFBundleCopySharedSupportURL(CFBundleRef bundle) { + CFURLRef result = NULL; + + if (1 == bundle->_version) { + result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundleSharedSupportURLFromBase1, bundle->_url); + } else if (2 == bundle->_version) { + result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundleSharedSupportURLFromBase2, bundle->_url); + } else { + result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundleSharedSupportURLFromBase0, bundle->_url); + } + return result; +} + +__private_extern__ CFURLRef _CFBundleCopyBuiltInPlugInsURL(CFBundleRef bundle) {return CFBundleCopyBuiltInPlugInsURL(bundle);} + +CF_EXPORT CFURLRef CFBundleCopyBuiltInPlugInsURL(CFBundleRef bundle) { + CFURLRef result = NULL, alternateResult = NULL; + + CFAllocatorRef alloc = CFGetAllocator(bundle); + if (1 == bundle->_version) { + result = CFURLCreateWithString(alloc, _CFBundleBuiltInPlugInsURLFromBase1, bundle->_url); + } else if (2 == bundle->_version) { + result = CFURLCreateWithString(alloc, _CFBundleBuiltInPlugInsURLFromBase2, bundle->_url); + } else { + result = CFURLCreateWithString(alloc, _CFBundleBuiltInPlugInsURLFromBase0, bundle->_url); + } + if (!result || !_urlExists(alloc, result)) { + if (1 == bundle->_version) { + alternateResult = CFURLCreateWithString(alloc, _CFBundleAlternateBuiltInPlugInsURLFromBase1, bundle->_url); + } else if (2 == bundle->_version) { + alternateResult = CFURLCreateWithString(alloc, _CFBundleAlternateBuiltInPlugInsURLFromBase2, bundle->_url); + } else { + alternateResult = CFURLCreateWithString(alloc, _CFBundleAlternateBuiltInPlugInsURLFromBase0, bundle->_url); + } + if (alternateResult && _urlExists(alloc, alternateResult)) { + if (result) CFRelease(result); + result = alternateResult; + } else { + if (alternateResult) CFRelease(alternateResult); + } + } + return result; +} + + + +#if defined(BINARY_SUPPORT_DYLD) + +static const void *__CFBundleDYLDFindImage(char *buff) { + const void *header = NULL; + uint32_t i, numImages = _dyld_image_count(), numMatches = 0; + const char *curName, *p, *q; + + for (i = 0; !header && i < numImages; i++) { + curName = _dyld_get_image_name(i); + if (curName && 0 == strncmp(curName, buff, CFMaxPathSize)) { + header = _dyld_get_image_header(i); + numMatches = 1; + } + } + if (!header) { + for (i = 0; i < numImages; i++) { + curName = _dyld_get_image_name(i); + if (curName) { + for (p = buff, q = curName; *p && *q && (q - curName < CFMaxPathSize); p++, q++) { + if (*p != *q && (q - curName > 11) && 0 == strncmp(q - 11, ".framework/Versions/", 20) && *(q + 9) && '/' == *(q + 10)) q += 11; + else if (*p != *q && (q - curName > 12) && 0 == strncmp(q - 12, ".framework/Versions/", 20) && *(q + 8) && '/' == *(q + 9)) q += 10; + if (*p != *q) break; + } + if (*p == *q) { + header = _dyld_get_image_header(i); + numMatches++; + } + } + } + } + return (numMatches == 1) ? header : NULL; +} + +__private_extern__ Boolean _CFBundleDYLDCheckLoaded(CFBundleRef bundle) { + if (!bundle->_isLoaded) { + CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); + char buff[CFMaxPathSize]; + + if (executableURL && CFURLGetFileSystemRepresentation(executableURL, true, (uint8_t *)buff, CFMaxPathSize)) { + const void *header = __CFBundleDYLDFindImage(buff); + if (header) { + if (bundle->_binaryType == __CFBundleUnknownBinary) bundle->_binaryType = __CFBundleDYLDFrameworkBinary; + if (!bundle->_imageCookie) { + bundle->_imageCookie = header; +#if LOG_BUNDLE_LOAD + printf("dyld check load bundle %p, find %s getting image %p\n", bundle, buff, bundle->_imageCookie); +#endif /* LOG_BUNDLE_LOAD */ + } + bundle->_isLoaded = true; + } else { +#if LOG_BUNDLE_LOAD + printf("dyld check load bundle %p, find %s no image\n", bundle, buff); +#endif /* LOG_BUNDLE_LOAD */ + } + } + if (executableURL) CFRelease(executableURL); + } + return bundle->_isLoaded; +} + +__private_extern__ Boolean _CFBundleDYLDLoadBundle(CFBundleRef bundle, Boolean forceGlobal, CFErrorRef *error) { + CFErrorRef localError = NULL, *subError = (error ? &localError : NULL); + NSLinkEditErrors c = NSLinkEditUndefinedError; + int errorNumber = 0; + const char *fileName = NULL; + const char *errorString = NULL; + + if (!bundle->_isLoaded) { + CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); + char buff[CFMaxPathSize]; + + if (executableURL && CFURLGetFileSystemRepresentation(executableURL, true, (uint8_t *)buff, CFMaxPathSize)) { + NSObjectFileImage image; + NSObjectFileImageReturnCode retCode = NSCreateObjectFileImageFromFile(buff, &image); +#if LOG_BUNDLE_LOAD + printf("dyld load bundle %p, create image of %s returns image %p retcode %d\n", bundle, buff, image, retCode); +#endif /* LOG_BUNDLE_LOAD */ + if (retCode == NSObjectFileImageSuccess) { + uint32_t options = forceGlobal ? NSLINKMODULE_OPTION_RETURN_ON_ERROR : (NSLINKMODULE_OPTION_BINDNOW | NSLINKMODULE_OPTION_PRIVATE | NSLINKMODULE_OPTION_RETURN_ON_ERROR); + NSModule module = NSLinkModule(image, buff, options); +#if LOG_BUNDLE_LOAD + printf("dyld load bundle %p, link module of %s options 0x%x returns module %p image %p\n", bundle, buff, options, module, image); +#endif /* LOG_BUNDLE_LOAD */ + if (module) { + bundle->_imageCookie = image; + bundle->_moduleCookie = module; + bundle->_isLoaded = true; + } else { + NSLinkEditError(&c, &errorNumber, &fileName, &errorString); + CFLog(__kCFLogBundle, CFSTR("Error loading %s: error code %d, error number %d (%s)"), fileName, c, errorNumber, errorString); + if (error) { +#if defined(BINARY_SUPPORT_DLFCN) + _CFBundleDlfcnPreflight(bundle, subError); +#endif /* BINARY_SUPPORT_DLFCN */ + if (!localError) { + CFStringRef debugString = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("error code %d, error number %d (%s)"), c, errorNumber, errorString); + localError = _CFBundleCreateErrorDebug(CFGetAllocator(bundle), bundle, CFBundleExecutableLinkError, debugString); + CFRelease(debugString); + } + } + (void)NSDestroyObjectFileImage(image); + } + } else { + CFLog(__kCFLogBundle, CFSTR("dyld returns %d when trying to load %@"), retCode, executableURL); + if (error) { + if (retCode == NSObjectFileImageArch) { + localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableArchitectureMismatchError); + } else if (retCode == NSObjectFileImageInappropriateFile) { + Boolean hasRuntimeMismatch = false; + uint32_t mainFlags = 0, bundleFlags = 0; + if (_CFBundleGrokObjCImageInfoFromMainExecutable(NULL, &mainFlags) && (mainFlags & 0x2) != 0) { + if (_CFBundleGetObjCImageInfo(bundle, NULL, &bundleFlags) && (bundleFlags & 0x2) == 0) hasRuntimeMismatch = true; + } + if (hasRuntimeMismatch) { + localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableRuntimeMismatchError); + } else { + localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableNotLoadableError); + } + } else { +#if defined(BINARY_SUPPORT_DLFCN) + _CFBundleDlfcnPreflight(bundle, subError); +#endif /* BINARY_SUPPORT_DLFCN */ + if (!localError) { + CFStringRef debugString = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("dyld returns %d"), retCode); + localError = _CFBundleCreateErrorDebug(CFGetAllocator(bundle), bundle, CFBundleExecutableLinkError, debugString); + CFRelease(debugString); + } + } + } + } + } else { + CFLog(__kCFLogBundle, CFSTR("Cannot find executable for bundle %@"), bundle); + if (error) localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableNotFoundError); + } + if (executableURL) CFRelease(executableURL); + } + if (!bundle->_isLoaded && error) *error = localError; + return bundle->_isLoaded; +} + +__private_extern__ Boolean _CFBundleDYLDLoadFramework(CFBundleRef bundle, CFErrorRef *error) { + // !!! Framework loading should be better. Can't unload frameworks. + CFErrorRef localError = NULL, *subError = (error ? &localError : NULL); + NSLinkEditErrors c = NSLinkEditUndefinedError; + int errorNumber = 0; + const char *fileName = NULL; + const char *errorString = NULL; + + if (!bundle->_isLoaded) { + CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); + char buff[CFMaxPathSize]; + + if (executableURL && CFURLGetFileSystemRepresentation(executableURL, true, (uint8_t *)buff, CFMaxPathSize)) { + void *image = (void *)NSAddImage(buff, NSADDIMAGE_OPTION_RETURN_ON_ERROR); +#if LOG_BUNDLE_LOAD + printf("dyld load framework %p, add image of %s returns image %p\n", bundle, buff, image); +#endif /* LOG_BUNDLE_LOAD */ + if (image) { + bundle->_imageCookie = image; + bundle->_isLoaded = true; + } else { + NSLinkEditError(&c, &errorNumber, &fileName, &errorString); + CFLog(__kCFLogBundle, CFSTR("Error loading %s: error code %d, error number %d (%s)"), fileName, c, errorNumber, errorString); + if (error) { +#if defined(BINARY_SUPPORT_DLFCN) + _CFBundleDlfcnPreflight(bundle, subError); +#endif /* BINARY_SUPPORT_DLFCN */ + if (!localError) { + CFStringRef debugString = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("error code %d, error number %d (%s)"), c, errorNumber, errorString); + localError = _CFBundleCreateErrorDebug(CFGetAllocator(bundle), bundle, CFBundleExecutableLinkError, debugString); + CFRelease(debugString); + } + } + } + } else { + CFLog(__kCFLogBundle, CFSTR("Cannot find executable for bundle %@"), bundle); + if (error) localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableNotFoundError); + } + if (executableURL) CFRelease(executableURL); + } + if (!bundle->_isLoaded && error) *error = localError; + return bundle->_isLoaded; +} + +__private_extern__ void _CFBundleDYLDUnloadBundle(CFBundleRef bundle) { + if (bundle->_isLoaded) { +#if LOG_BUNDLE_LOAD + printf("dyld unload bundle %p, handle %p module %p image %p\n", bundle, bundle->_handleCookie, bundle->_moduleCookie, bundle->_imageCookie); +#endif /* LOG_BUNDLE_LOAD */ + if (bundle->_moduleCookie && !NSUnLinkModule((NSModule)(bundle->_moduleCookie), NSUNLINKMODULE_OPTION_NONE)) { + CFLog(__kCFLogBundle, CFSTR("Internal error unloading bundle %@"), bundle); + } else { + if (bundle->_moduleCookie && bundle->_imageCookie) (void)NSDestroyObjectFileImage((NSObjectFileImage)(bundle->_imageCookie)); + bundle->_connectionCookie = bundle->_handleCookie = NULL; + bundle->_imageCookie = bundle->_moduleCookie = NULL; + bundle->_isLoaded = false; + } + } +} + +__private_extern__ void *_CFBundleDYLDGetSymbolByName(CFBundleRef bundle, CFStringRef symbolName) {return _CFBundleDYLDGetSymbolByNameWithSearch(bundle, symbolName, false);} + +static void *_CFBundleDYLDGetSymbolByNameWithSearch(CFBundleRef bundle, CFStringRef symbolName, Boolean globalSearch) { + void *result = NULL; + char buff[1026]; + NSSymbol symbol = NULL; + + buff[0] = '_'; + if (CFStringGetCString(symbolName, &(buff[1]), 1024, kCFStringEncodingUTF8)) { + if (bundle->_moduleCookie) { + symbol = NSLookupSymbolInModule((NSModule)(bundle->_moduleCookie), buff); + } else if (bundle->_imageCookie) { + symbol = NSLookupSymbolInImage((const struct mach_header*)bundle->_imageCookie, buff, NSLOOKUPSYMBOLINIMAGE_OPTION_BIND|NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); + } + if (!symbol && !bundle->_moduleCookie && (!bundle->_imageCookie || globalSearch)) { + char hintBuff[1026]; + CFStringRef executableName = _CFBundleCopyExecutableName(kCFAllocatorSystemDefault, bundle, NULL, NULL); + hintBuff[0] = '\0'; + if (executableName) { + if (!CFStringGetCString(executableName, hintBuff, 1024, kCFStringEncodingUTF8)) hintBuff[0] = '\0'; + CFRelease(executableName); + } + // Nowdays, NSIsSymbolNameDefinedWithHint() and NSLookupAndBindSymbolWithHint() + // are identical, except the first just returns a bool, so checking with the + // Is function first just causes a redundant lookup. + // This returns NULL on failure. + symbol = NSLookupAndBindSymbolWithHint(buff, hintBuff); + } + if (symbol) result = NSAddressOfSymbol(symbol); +#if defined(DEBUG) + if (!result) CFLog(__kCFLogBundle, CFSTR("dyld cannot find symbol %s in %@"), buff, bundle); +#endif /* DEBUG */ +#if LOG_BUNDLE_LOAD + printf("bundle %p handle %p module %p image %p dyld returns symbol %p for %s\n", bundle, bundle->_handleCookie, bundle->_moduleCookie, bundle->_imageCookie, result, buff + 1); +#endif /* LOG_BUNDLE_LOAD */ + } + return result; +} + +static CFStringRef _CFBundleDYLDCopyLoadedImagePathForPointer(void *p) { + CFStringRef result = NULL; + if (!result) { + uint32_t i, j, n = _dyld_image_count(); + Boolean foundit = false; + const char *name; +#if __LP64__ +#define MACH_HEADER_TYPE struct mach_header_64 +#define MACH_SEGMENT_CMD_TYPE struct segment_command_64 +#define MACH_SEGMENT_FLAVOR LC_SEGMENT_64 +#else +#define MACH_HEADER_TYPE struct mach_header +#define MACH_SEGMENT_CMD_TYPE struct segment_command +#define MACH_SEGMENT_FLAVOR LC_SEGMENT +#endif + for (i = 0; !foundit && i < n; i++) { + const MACH_HEADER_TYPE *mh = (const MACH_HEADER_TYPE *)_dyld_get_image_header(i); + uintptr_t addr = (uintptr_t)p - _dyld_get_image_vmaddr_slide(i); + if (mh) { + struct load_command *lc = (struct load_command *)((char *)mh + sizeof(MACH_HEADER_TYPE)); + for (j = 0; !foundit && j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize)) { + if (MACH_SEGMENT_FLAVOR == lc->cmd && ((MACH_SEGMENT_CMD_TYPE *)lc)->vmaddr <= addr && addr < ((MACH_SEGMENT_CMD_TYPE *)lc)->vmaddr + ((MACH_SEGMENT_CMD_TYPE *)lc)->vmsize) { + foundit = true; + name = _dyld_get_image_name(i); + if (name) result = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, name); + } + } + } + } +#undef MACH_HEADER_TYPE +#undef MACH_SEGMENT_CMD_TYPE +#undef MACH_SEGMENT_FLAVOR + } +#if LOG_BUNDLE_LOAD + printf("dyld image path for pointer %p is %p\n", p, result); +#endif /* LOG_BUNDLE_LOAD */ + return result; +} + +__private_extern__ CFArrayRef _CFBundleDYLDCopyLoadedImagePathsForHint(CFStringRef hint) { + uint32_t i, numImages = _dyld_image_count(); + CFMutableArrayRef result = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); + CFRange range = CFRangeMake(0, CFStringGetLength(hint)); + const char *processPath = _CFProcessPath(); + + for (i = 0; i < numImages; i++) { + const char *curName = _dyld_get_image_name(i), *lastComponent = NULL; + if (curName && (!processPath || 0 != strcmp(curName, processPath))) lastComponent = strrchr(curName, '/'); + if (lastComponent) { + CFStringRef str = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, lastComponent + 1); + if (str) { + if (CFStringFindWithOptions(hint, str, range, kCFCompareAnchored|kCFCompareBackwards|kCFCompareCaseInsensitive, NULL)) { + CFStringRef curStr = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, curName); + if (curStr) { + CFArrayAppendValue(result, curStr); + CFRelease(curStr); + } + } + CFRelease(str); + } + } + } + return result; +} + +static char *_cleanedPathForPath(const char *curName) { + char *thePath = strdup(curName); + if (thePath) { + // We are going to process the buffer replacing all "/./" and "//" with "/" + CFIndex srcIndex = 0, dstIndex = 0; + CFIndex len = strlen(thePath); + for (srcIndex=0; srcIndex<len; srcIndex++) { + thePath[dstIndex] = thePath[srcIndex]; + dstIndex++; + while (srcIndex < len-1 && thePath[srcIndex] == '/' && (thePath[srcIndex+1] == '/' || (thePath[srcIndex+1] == '.' && srcIndex < len-2 && thePath[srcIndex+2] == '/'))) srcIndex += (thePath[srcIndex+1] == '/' ? 1 : 2); + } + thePath[dstIndex] = 0; + } + return thePath; +} + +__private_extern__ CFArrayRef _CFBundleDYLDCopyLoadedImagePathsIfChanged(void) { + // This returns an array of the paths of all the dyld images in the process. These paths may not be absolute, they may point at things that are not bundles, they may be staticly linked bundles or dynamically loaded bundles, they may be NULL. + static uint32_t _cachedDYLDImageCount = -1; + + uint32_t i, numImages = _dyld_image_count(); + CFMutableArrayRef result = NULL; + + if (numImages != _cachedDYLDImageCount) { + const char *curName; + char *cleanedCurName = NULL; + CFStringRef curStr; + const char *processPath = _CFProcessPath(); + + result = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); + + for (i = 0; i < numImages; i++) { + curName = _dyld_get_image_name(i); + if (curName && i == 0) cleanedCurName = _cleanedPathForPath(curName); + if (curName && (!processPath || 0 != strcmp(curName, processPath)) && (!processPath || !cleanedCurName || 0 != strcmp(cleanedCurName, processPath))) { + curStr = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, curName); + if (curStr) { + CFArrayAppendValue(result, curStr); + CFRelease(curStr); + } + } + if (cleanedCurName) { + free(cleanedCurName); + cleanedCurName = NULL; + } + } + _cachedDYLDImageCount = numImages; + } + return result; +} + +#endif /* BINARY_SUPPORT_DYLD */ + +#if defined(BINARY_SUPPORT_DLFCN) + +__private_extern__ Boolean _CFBundleDlfcnCheckLoaded(CFBundleRef bundle) { + if (!bundle->_isLoaded) { + CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); + char buff[CFMaxPathSize]; + + if (executableURL && CFURLGetFileSystemRepresentation(executableURL, true, (uint8_t *)buff, CFMaxPathSize)) { + int mode = RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD | CF_RTLD_FIRST; + void *handle = dlopen(buff, mode); + if (handle) { + if (!bundle->_handleCookie) { + bundle->_handleCookie = handle; +#if LOG_BUNDLE_LOAD + printf("dlfcn check load bundle %p, dlopen of %s mode 0x%x getting handle %p\n", bundle, buff, mode, bundle->_handleCookie); +#endif /* LOG_BUNDLE_LOAD */ + } + bundle->_isLoaded = true; + } else { +#if LOG_BUNDLE_LOAD + printf("dlfcn check load bundle %p, dlopen of %s mode 0x%x no handle\n", bundle, buff, mode); +#endif /* LOG_BUNDLE_LOAD */ + } + } + if (executableURL) CFRelease(executableURL); + } + return bundle->_isLoaded; +} + +static SInt32 _CFBundleCurrentArchitecture(void) { + SInt32 arch = 0; +#if defined(__ppc__) || defined(__powerpc__) + arch = kCFBundleExecutableArchitecturePPC; +#elif defined(__ppc64__) + arch = kCFBundleExecutableArchitecturePPC64; +#elif defined(__i386__) + arch = kCFBundleExecutableArchitectureI386; +#elif defined(__x86_64__) + arch = kCFBundleExecutableArchitectureX86_64; +#elif defined(BINARY_SUPPORT_DYLD) + const NXArchInfo *archInfo = NXGetLocalArchInfo(); + if (archInfo) arch = archInfo->cputype; +#endif + return arch; +} + +extern Boolean _CFBundleDlfcnPreflight(CFBundleRef bundle, CFErrorRef *error) { + Boolean retval = true; + CFErrorRef localError = NULL; + if (!bundle->_isLoaded) { + CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); + char buff[CFMaxPathSize]; + + retval = false; + if (executableURL && CFURLGetFileSystemRepresentation(executableURL, true, (uint8_t *)buff, CFMaxPathSize)) { +#if DEPLOYMENT_TARGET_MACOSX && (MAC_OS_X_VERSION_10_5 <= MAC_OS_X_VERSION_MAX_ALLOWED) + retval = dlopen_preflight(buff); +#else + retval = false; +#endif /* DEPLOYMENT_TARGET_MACOSX && (MAC_OS_X_VERSION_10_5 <= MAC_OS_X_VERSION_MAX_ALLOWED) */ + if (!retval && error) { + CFArrayRef archs = CFBundleCopyExecutableArchitectures(bundle); + CFStringRef debugString = NULL; + const char *errorString = dlerror(); + if (errorString && strlen(errorString) > 0) debugString = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, errorString); + if (archs) { + Boolean hasSuitableArch = false, hasRuntimeMismatch = false; + CFIndex i, count = CFArrayGetCount(archs); + SInt32 arch, curArch = _CFBundleCurrentArchitecture(); + for (i = 0; !hasSuitableArch && i < count; i++) { + if (CFNumberGetValue((CFNumberRef)CFArrayGetValueAtIndex(archs, i), kCFNumberSInt32Type, (void *)&arch) && arch == curArch) hasSuitableArch = true; + } +#if defined(BINARY_SUPPORT_DYLD) + if (hasSuitableArch) { + uint32_t mainFlags = 0, bundleFlags = 0; + if (_CFBundleGrokObjCImageInfoFromMainExecutable(NULL, &mainFlags) && (mainFlags & 0x2) != 0) { + if (_CFBundleGetObjCImageInfo(bundle, NULL, &bundleFlags) && (bundleFlags & 0x2) == 0) hasRuntimeMismatch = true; + } + } +#endif /* BINARY_SUPPORT_DYLD */ + if (hasRuntimeMismatch) { + localError = _CFBundleCreateErrorDebug(CFGetAllocator(bundle), bundle, CFBundleExecutableRuntimeMismatchError, debugString); + } else if (!hasSuitableArch) { + localError = _CFBundleCreateErrorDebug(CFGetAllocator(bundle), bundle, CFBundleExecutableArchitectureMismatchError, debugString); + } else { + localError = _CFBundleCreateErrorDebug(CFGetAllocator(bundle), bundle, CFBundleExecutableLoadError, debugString); + } + CFRelease(archs); + } else { + localError = _CFBundleCreateErrorDebug(CFGetAllocator(bundle), bundle, CFBundleExecutableLoadError, debugString); + } + if (debugString) CFRelease(debugString); + } + } else { + if (error) localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableNotFoundError); + } + if (executableURL) CFRelease(executableURL); + } + if (!retval && error) *error = localError; + return retval; +} + +__private_extern__ Boolean _CFBundleDlfcnLoadBundle(CFBundleRef bundle, Boolean forceGlobal, CFErrorRef *error) { + CFErrorRef localError = NULL, *subError = (error ? &localError : NULL); + if (!bundle->_isLoaded) { + CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); + char buff[CFMaxPathSize]; + if (executableURL && CFURLGetFileSystemRepresentation(executableURL, true, (uint8_t *)buff, CFMaxPathSize)) { + int mode = forceGlobal ? (RTLD_LAZY | RTLD_GLOBAL | CF_RTLD_FIRST) : (RTLD_NOW | RTLD_LOCAL | CF_RTLD_FIRST); + bundle->_handleCookie = dlopen(buff, mode); +#if LOG_BUNDLE_LOAD + printf("dlfcn load bundle %p, dlopen of %s mode 0x%x returns handle %p\n", bundle, buff, mode, bundle->_handleCookie); +#endif /* LOG_BUNDLE_LOAD */ + if (bundle->_handleCookie) { + bundle->_isLoaded = true; + } else { + CFStringRef debugString = NULL; + const char *errorString = dlerror(); + if (errorString) { + CFLog(__kCFLogBundle, CFSTR("Error loading %s: %s"), buff, errorString); + debugString = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, errorString); + } else { + CFLog(__kCFLogBundle, CFSTR("Error loading %s"), buff); + } + if (error && _CFBundleDlfcnPreflight(bundle, subError)) localError = _CFBundleCreateErrorDebug(CFGetAllocator(bundle), bundle, CFBundleExecutableLinkError, debugString); + if (debugString) CFRelease(debugString); + } + } else { + CFLog(__kCFLogBundle, CFSTR("Cannot find executable for bundle %@"), bundle); + if (error) localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableNotFoundError); + } + if (executableURL) CFRelease(executableURL); + } + if (!bundle->_isLoaded && error) *error = localError; + return bundle->_isLoaded; +} + +__private_extern__ Boolean _CFBundleDlfcnLoadFramework(CFBundleRef bundle, CFErrorRef *error) { + CFErrorRef localError = NULL, *subError = (error ? &localError : NULL); + if (!bundle->_isLoaded) { + CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); + char buff[CFMaxPathSize]; + if (executableURL && CFURLGetFileSystemRepresentation(executableURL, true, (uint8_t *)buff, CFMaxPathSize)) { + int mode = RTLD_LAZY | RTLD_GLOBAL | CF_RTLD_FIRST; + bundle->_handleCookie = dlopen(buff, mode); +#if LOG_BUNDLE_LOAD + printf("dlfcn load framework %p, dlopen of %s mode 0x%x returns handle %p\n", bundle, buff, mode, bundle->_handleCookie); +#endif /* LOG_BUNDLE_LOAD */ + if (bundle->_handleCookie) { + bundle->_isLoaded = true; + } else { + CFStringRef debugString = NULL; + const char *errorString = dlerror(); + if (errorString) { + CFLog(__kCFLogBundle, CFSTR("Error loading %s: %s"), buff, errorString); + debugString = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, errorString); + } else { + CFLog(__kCFLogBundle, CFSTR("Error loading %s"), buff); + } + if (error && _CFBundleDlfcnPreflight(bundle, subError)) localError = _CFBundleCreateErrorDebug(CFGetAllocator(bundle), bundle, CFBundleExecutableLinkError, debugString); + if (debugString) CFRelease(debugString); + } + } else { + CFLog(__kCFLogBundle, CFSTR("Cannot find executable for bundle %@"), bundle); + if (error) localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableNotFoundError); + } + if (executableURL) CFRelease(executableURL); + } + if (!bundle->_isLoaded && error) *error = localError; + return bundle->_isLoaded; +} + +__private_extern__ void _CFBundleDlfcnUnload(CFBundleRef bundle) { + if (bundle->_isLoaded) { +#if LOG_BUNDLE_LOAD + printf("dlfcn unload bundle %p, handle %p module %p image %p\n", bundle, bundle->_handleCookie, bundle->_moduleCookie, bundle->_imageCookie); +#endif /* LOG_BUNDLE_LOAD */ + if (0 != dlclose(bundle->_handleCookie)) { + CFLog(__kCFLogBundle, CFSTR("Internal error unloading bundle %@"), bundle); + } else { + bundle->_connectionCookie = bundle->_handleCookie = NULL; + bundle->_imageCookie = bundle->_moduleCookie = NULL; + bundle->_isLoaded = false; + } + } +} + +__private_extern__ void *_CFBundleDlfcnGetSymbolByName(CFBundleRef bundle, CFStringRef symbolName) {return _CFBundleDlfcnGetSymbolByNameWithSearch(bundle, symbolName, false);} + +static void *_CFBundleDlfcnGetSymbolByNameWithSearch(CFBundleRef bundle, CFStringRef symbolName, Boolean globalSearch) { + void *result = NULL; + char buff[1026]; + + if (CFStringGetCString(symbolName, buff, 1024, kCFStringEncodingUTF8)) { + result = dlsym(bundle->_handleCookie, buff); + if (!result && globalSearch) result = dlsym(RTLD_DEFAULT, buff); +#if defined(DEBUG) + if (!result) CFLog(__kCFLogBundle, CFSTR("dlsym cannot find symbol %s in %@"), buff, bundle); +#endif /* DEBUG */ +#if LOG_BUNDLE_LOAD + printf("bundle %p handle %p module %p image %p dlsym returns symbol %p for %s\n", bundle, bundle->_handleCookie, bundle->_moduleCookie, bundle->_imageCookie, result, buff); +#endif /* LOG_BUNDLE_LOAD */ + } + return result; +} + +static CFStringRef _CFBundleDlfcnCopyLoadedImagePathForPointer(void *p) { + CFStringRef result = NULL; + Dl_info info; + if (0 != dladdr(p, &info) && info.dli_fname) result = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, info.dli_fname); +#if LOG_BUNDLE_LOAD + printf("dlfcn image path for pointer %p is %p\n", p, result); +#endif /* LOG_BUNDLE_LOAD */ + return result; +} + +#endif /* BINARY_SUPPORT_DLFCN */ + + +#if defined(BINARY_SUPPORT_DLL) + +__private_extern__ Boolean _CFBundleDLLLoad(CFBundleRef bundle, CFErrorRef *error) { + CFErrorRef localError = NULL; + if (!bundle->_isLoaded) { + CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); + TCHAR buff[CFMaxPathSize]; + + if (executableURL && CFURLGetFileSystemRepresentation(executableURL, true, (uint8_t *)buff, CFMaxPathSize)) { + bundle->_hModule = LoadLibrary(buff); + if (bundle->_hModule) { + bundle->_isLoaded = true; + } else { + if (error) localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableLinkError); + } + } else { + CFLog(__kCFLogBundle, CFSTR("Cannot find executable for bundle %@"), bundle); + if (error) localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableNotFoundError); + } + if (executableURL) CFRelease(executableURL); + } + if (!bundle->_isLoaded && error) *error = localError; + return bundle->_isLoaded; +} + +__private_extern__ void _CFBundleDLLUnload(CFBundleRef bundle) { + if (bundle->_isLoaded) { + FreeLibrary(bundle->_hModule); + bundle->_hModule = NULL; + bundle->_isLoaded = false; + } +} + +__private_extern__ void *_CFBundleDLLGetSymbolByName(CFBundleRef bundle, CFStringRef symbolName) { + void *result = NULL; + char buff[1024]; + if (CFStringGetCString(symbolName, buff, 1024, kCFStringEncodingWindowsLatin1)) result = GetProcAddress(bundle->_hModule, buff); + return result; +} + +#endif /* BINARY_SUPPORT_DLL */ + +/* Workarounds to be applied in the presence of certain bundles can go here. This is called on every bundle creation. +*/ +extern void _CFStringSetCompatibility(CFOptionFlags); + +static void _CFBundleCheckWorkarounds(CFBundleRef bundle) { +} +
diff --git a/CoreFoundation/CFBundle.h b/CoreFoundation/CFBundle.h new file mode 100644 index 0000000..3814fb0 --- /dev/null +++ b/CoreFoundation/CFBundle.h
@@ -0,0 +1,366 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFBundle.h + Copyright (c) 1999-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFBUNDLE__) +#define __COREFOUNDATION_CFBUNDLE__ 1 + +#include <CoreFoundation/CFBase.h> +#include <CoreFoundation/CFArray.h> +#include <CoreFoundation/CFDictionary.h> +#include <CoreFoundation/CFError.h> +#include <CoreFoundation/CFString.h> +#include <CoreFoundation/CFURL.h> + +CF_EXTERN_C_BEGIN + +typedef struct __CFBundle *CFBundleRef; +typedef struct __CFBundle *CFPlugInRef; + +/* ===================== Standard Info.plist keys ===================== */ +CF_EXPORT +const CFStringRef kCFBundleInfoDictionaryVersionKey; + /* The version of the Info.plist format */ +CF_EXPORT +const CFStringRef kCFBundleExecutableKey; + /* The name of the executable in this bundle, if any */ +CF_EXPORT +const CFStringRef kCFBundleIdentifierKey; + /* The bundle identifier (for CFBundleGetBundleWithIdentifier()) */ +CF_EXPORT +const CFStringRef kCFBundleVersionKey; + /* The version number of the bundle. For Mac OS 9 style version numbers (for example "2.5.3d5"), */ + /* clients can use CFBundleGetVersionNumber() instead of accessing this key directly since that */ + /* function will properly convert the version string into its compact integer representation. */ +CF_EXPORT +const CFStringRef kCFBundleDevelopmentRegionKey; + /* The name of the development language of the bundle. */ +CF_EXPORT +const CFStringRef kCFBundleNameKey; + /* The human-readable name of the bundle. This key is often found in the InfoPlist.strings since it is usually localized. */ +CF_EXPORT +const CFStringRef kCFBundleLocalizationsKey AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER; + /* Allows an unbundled application that handles localization itself to specify which localizations it has available. */ + +/* ===================== Finding Bundles ===================== */ + +CF_EXPORT +CFBundleRef CFBundleGetMainBundle(void); + +CF_EXPORT +CFBundleRef CFBundleGetBundleWithIdentifier(CFStringRef bundleID); + /* A bundle can name itself by providing a key in the info dictionary. */ + /* This facility is meant to allow bundle-writers to get hold of their */ + /* bundle from their code without having to know where it was on the disk. */ + /* This is meant to be a replacement mechanism for +bundleForClass: users. */ + /* Note that this does not search for bundles on the disk; it will locate */ + /* only bundles already loaded or otherwise known to the current process. */ + +CF_EXPORT +CFArrayRef CFBundleGetAllBundles(void); + /* This is potentially expensive. Use with care. */ + +/* ===================== Creating Bundles ===================== */ + +CF_EXPORT +CFTypeID CFBundleGetTypeID(void); + +CF_EXPORT +CFBundleRef CFBundleCreate(CFAllocatorRef allocator, CFURLRef bundleURL); + /* Might return an existing instance with the ref-count bumped. */ + +CF_EXPORT +CFArrayRef CFBundleCreateBundlesFromDirectory(CFAllocatorRef allocator, CFURLRef directoryURL, CFStringRef bundleType); + /* Create instances for all bundles in the given directory matching the given type */ + /* (or all of them if bundleType is NULL). Instances are created using CFBundleCreate() and are not released. */ + +/* ==================== Basic Bundle Info ==================== */ + +CF_EXPORT +CFURLRef CFBundleCopyBundleURL(CFBundleRef bundle); + +CF_EXPORT +CFTypeRef CFBundleGetValueForInfoDictionaryKey(CFBundleRef bundle, CFStringRef key); + /* Returns a localized value if available, otherwise the global value. */ + /* This is the recommended function for examining the info dictionary. */ + +CF_EXPORT +CFDictionaryRef CFBundleGetInfoDictionary(CFBundleRef bundle); + /* This is the global info dictionary. Note that CFBundle may add */ + /* extra keys to the dictionary for its own use. */ + +CF_EXPORT +CFDictionaryRef CFBundleGetLocalInfoDictionary(CFBundleRef bundle); + /* This is the localized info dictionary. */ + +CF_EXPORT +void CFBundleGetPackageInfo(CFBundleRef bundle, UInt32 *packageType, UInt32 *packageCreator); + +CF_EXPORT +CFStringRef CFBundleGetIdentifier(CFBundleRef bundle); + +CF_EXPORT +UInt32 CFBundleGetVersionNumber(CFBundleRef bundle); + +CF_EXPORT +CFStringRef CFBundleGetDevelopmentRegion(CFBundleRef bundle); + +CF_EXPORT +CFURLRef CFBundleCopySupportFilesDirectoryURL(CFBundleRef bundle); + +CF_EXPORT +CFURLRef CFBundleCopyResourcesDirectoryURL(CFBundleRef bundle); + +CF_EXPORT +CFURLRef CFBundleCopyPrivateFrameworksURL(CFBundleRef bundle); + +CF_EXPORT +CFURLRef CFBundleCopySharedFrameworksURL(CFBundleRef bundle); + +CF_EXPORT +CFURLRef CFBundleCopySharedSupportURL(CFBundleRef bundle); + +CF_EXPORT +CFURLRef CFBundleCopyBuiltInPlugInsURL(CFBundleRef bundle); + +/* ------------- Basic Bundle Info without a CFBundle instance ------------- */ +/* This API is provided to enable developers to retrieve basic information */ +/* about a bundle without having to create an instance of CFBundle. */ +/* Because of caching behavior when a CFBundle instance exists, it will be faster */ +/* to actually create a CFBundle if you need to retrieve multiple pieces of info. */ +CF_EXPORT +CFDictionaryRef CFBundleCopyInfoDictionaryInDirectory(CFURLRef bundleURL); + +CF_EXPORT +Boolean CFBundleGetPackageInfoInDirectory(CFURLRef url, UInt32 *packageType, UInt32 *packageCreator); + +/* ==================== Resource Handling API ==================== */ + +CF_EXPORT +CFURLRef CFBundleCopyResourceURL(CFBundleRef bundle, CFStringRef resourceName, CFStringRef resourceType, CFStringRef subDirName); + +CF_EXPORT +CFArrayRef CFBundleCopyResourceURLsOfType(CFBundleRef bundle, CFStringRef resourceType, CFStringRef subDirName); + +CF_EXPORT +CFStringRef CFBundleCopyLocalizedString(CFBundleRef bundle, CFStringRef key, CFStringRef value, CFStringRef tableName); + +#define CFCopyLocalizedString(key, comment) \ + CFBundleCopyLocalizedString(CFBundleGetMainBundle(), (key), (key), NULL) +#define CFCopyLocalizedStringFromTable(key, tbl, comment) \ + CFBundleCopyLocalizedString(CFBundleGetMainBundle(), (key), (key), (tbl)) +#define CFCopyLocalizedStringFromTableInBundle(key, tbl, bundle, comment) \ + CFBundleCopyLocalizedString((bundle), (key), (key), (tbl)) +#define CFCopyLocalizedStringWithDefaultValue(key, tbl, bundle, value, comment) \ + CFBundleCopyLocalizedString((bundle), (key), (value), (tbl)) + +/* ------------- Resource Handling without a CFBundle instance ------------- */ +/* This API is provided to enable developers to use the CFBundle resource */ +/* searching policy without having to create an instance of CFBundle. */ +/* Because of caching behavior when a CFBundle instance exists, it will be faster */ +/* to actually create a CFBundle if you need to access several resources. */ + +CF_EXPORT +CFURLRef CFBundleCopyResourceURLInDirectory(CFURLRef bundleURL, CFStringRef resourceName, CFStringRef resourceType, CFStringRef subDirName); + +CF_EXPORT +CFArrayRef CFBundleCopyResourceURLsOfTypeInDirectory(CFURLRef bundleURL, CFStringRef resourceType, CFStringRef subDirName); + +/* =========== Localization-specific Resource Handling API =========== */ +/* This API allows finer-grained control over specific localizations, */ +/* as distinguished from the above API, which always uses the user's */ +/* preferred localizations for the bundle in the current app context. */ + +CF_EXPORT +CFArrayRef CFBundleCopyBundleLocalizations(CFBundleRef bundle); + /* Lists the localizations that a bundle contains. */ + +CF_EXPORT +CFArrayRef CFBundleCopyPreferredLocalizationsFromArray(CFArrayRef locArray); + /* Given an array of possible localizations, returns the one or more */ + /* of them that CFBundle would use in the current application context. */ + /* To determine the localizations that would be used for a particular */ + /* bundle in the current application context, apply this function to the */ + /* result of CFBundleCopyBundleLocalizations(). */ + +CF_EXPORT +CFArrayRef CFBundleCopyLocalizationsForPreferences(CFArrayRef locArray, CFArrayRef prefArray) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER; + /* Given an array of possible localizations, returns the one or more of */ + /* them that CFBundle would use, without reference to the current application */ + /* context, if the user's preferred localizations were given by prefArray. */ + /* If prefArray is NULL, the current user's actual preferred localizations will */ + /* be used. This is not the same as CFBundleCopyPreferredLocalizationsFromArray(), */ + /* because that function takes the current application context into account. */ + /* To determine the localizations that another application would use, apply */ + /* this function to the result of CFBundleCopyBundleLocalizations(). */ + +CF_EXPORT +CFURLRef CFBundleCopyResourceURLForLocalization(CFBundleRef bundle, CFStringRef resourceName, CFStringRef resourceType, CFStringRef subDirName, CFStringRef localizationName); + +CF_EXPORT +CFArrayRef CFBundleCopyResourceURLsOfTypeForLocalization(CFBundleRef bundle, CFStringRef resourceType, CFStringRef subDirName, CFStringRef localizationName); + /* The localizationName argument to CFBundleCopyResourceURLForLocalization() or */ + /* CFBundleCopyResourceURLsOfTypeForLocalization() must be identical to one of the */ + /* localizations in the bundle, as returned by CFBundleCopyBundleLocalizations(). */ + /* It is recommended that either CFBundleCopyPreferredLocalizationsFromArray() or */ + /* CFBundleCopyLocalizationsForPreferences() be used to select the localization. */ + +/* =================== Unbundled application info ===================== */ +/* This API is provided to enable developers to retrieve bundle-related */ +/* information about an application that may be bundled or unbundled. */ +CF_EXPORT +CFDictionaryRef CFBundleCopyInfoDictionaryForURL(CFURLRef url) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER; + /* For a directory URL, this is equivalent to CFBundleCopyInfoDictionaryInDirectory(). */ + /* For a plain file URL representing an unbundled executable, this will attempt to read */ + /* an info dictionary from the (__TEXT, __info_plist) section, if it is a Mach-o file, */ + /* or from a 'plst' resource. */ + +CF_EXPORT +CFArrayRef CFBundleCopyLocalizationsForURL(CFURLRef url) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER; + /* For a directory URL, this is equivalent to calling CFBundleCopyBundleLocalizations() */ + /* on the corresponding bundle. For a plain file URL representing an unbundled executable, */ + /* this will attempt to determine its localizations using the CFBundleLocalizations and */ + /* CFBundleDevelopmentRegion keys in the dictionary returned by CFBundleCopyInfoDictionaryForURL,*/ + /* or from a 'vers' resource if those are not present. */ + +CF_EXPORT +CFArrayRef CFBundleCopyExecutableArchitecturesForURL(CFURLRef url) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + /* For a directory URL, this is equivalent to calling CFBundleCopyExecutableArchitectures() */ + /* on the corresponding bundle. For a plain file URL representing an unbundled executable, */ + /* this will return the architectures it provides, if it is a Mach-o file, or NULL otherwise. */ + +/* ==================== Primitive Code Loading API ==================== */ +/* This API abstracts the various different executable formats supported on */ +/* various platforms. It can load DYLD, CFM, or DLL shared libraries (on their */ +/* appropriate platforms) and gives a uniform API for looking up functions. */ + +CF_EXPORT +CFURLRef CFBundleCopyExecutableURL(CFBundleRef bundle); + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 +enum { + kCFBundleExecutableArchitectureI386 = 0x00000007, + kCFBundleExecutableArchitecturePPC = 0x00000012, + kCFBundleExecutableArchitectureX86_64 = 0x01000007, + kCFBundleExecutableArchitecturePPC64 = 0x01000012 +}; +#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 */ + +CF_EXPORT +CFArrayRef CFBundleCopyExecutableArchitectures(CFBundleRef bundle) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + /* If the bundle's executable exists and is a Mach-o file, this function will return an array */ + /* of CFNumbers whose values are integers representing the architectures the file provides. */ + /* The values currently in use are those listed in the enum above, but others may be added */ + /* in the future. If the executable is not a Mach-o file, this function returns NULL. */ + +CF_EXPORT +Boolean CFBundlePreflightExecutable(CFBundleRef bundle, CFErrorRef *error) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + /* This function will return true if the bundle is loaded, or if the bundle appears to be */ + /* loadable upon inspection. This does not mean that the bundle is definitively loadable, */ + /* since it may fail to load due to link errors or other problems not readily detectable. */ + /* If this function detects problems, it will return false, and return a CFError by reference. */ + /* It is the responsibility of the caller to release the CFError. */ + +CF_EXPORT +Boolean CFBundleLoadExecutableAndReturnError(CFBundleRef bundle, CFErrorRef *error) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + /* If the bundle is already loaded, this function will return true. Otherwise, it will attempt */ + /* to load the bundle, and it will return true if that attempt succeeds. If the bundle fails */ + /* to load, this function will return false, and it will return a CFError by reference. */ + /* It is the responsibility of the caller to release the CFError. */ + +CF_EXPORT +Boolean CFBundleLoadExecutable(CFBundleRef bundle); + +CF_EXPORT +Boolean CFBundleIsExecutableLoaded(CFBundleRef bundle); + +CF_EXPORT +void CFBundleUnloadExecutable(CFBundleRef bundle); + +CF_EXPORT +void *CFBundleGetFunctionPointerForName(CFBundleRef bundle, CFStringRef functionName); + +CF_EXPORT +void CFBundleGetFunctionPointersForNames(CFBundleRef bundle, CFArrayRef functionNames, void *ftbl[]); + +CF_EXPORT +void *CFBundleGetDataPointerForName(CFBundleRef bundle, CFStringRef symbolName); + +CF_EXPORT +void CFBundleGetDataPointersForNames(CFBundleRef bundle, CFArrayRef symbolNames, void *stbl[]); + +CF_EXPORT +CFURLRef CFBundleCopyAuxiliaryExecutableURL(CFBundleRef bundle, CFStringRef executableName); + /* This function can be used to find executables other than your main */ + /* executable. This is useful, for instance, for applications that have */ + /* some command line tool that is packaged with and used by the application. */ + /* The tool can be packaged in the various platform executable directories */ + /* in the bundle and can be located with this function. This allows an */ + /* app to ship versions of the tool for each platform as it does for the */ + /* main app executable. */ + +/* ==================== Getting a bundle's plugIn ==================== */ + +CF_EXPORT +CFPlugInRef CFBundleGetPlugIn(CFBundleRef bundle); + +/* ==================== Resource Manager-Related API ==================== */ + +#if __LP64__ +typedef int CFBundleRefNum; +#else +typedef SInt16 CFBundleRefNum; +#endif + +CF_EXPORT +CFBundleRefNum CFBundleOpenBundleResourceMap(CFBundleRef bundle); + /* This function opens the non-localized and the localized resource files */ + /* (if any) for the bundle, creates and makes current a single read-only */ + /* resource map combining both, and returns a reference number for it. */ + /* If it is called multiple times, it opens the files multiple times, */ + /* and returns distinct reference numbers. */ + +CF_EXPORT +SInt32 CFBundleOpenBundleResourceFiles(CFBundleRef bundle, CFBundleRefNum *refNum, CFBundleRefNum *localizedRefNum); + /* Similar to CFBundleOpenBundleResourceMap(), except that it creates two */ + /* separate resource maps and returns reference numbers for both. */ + +CF_EXPORT +void CFBundleCloseBundleResourceMap(CFBundleRef bundle, CFBundleRefNum refNum); + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFBUNDLE__ */ +
diff --git a/CoreFoundation/CFBundlePriv.h b/CoreFoundation/CFBundlePriv.h new file mode 100644 index 0000000..83e042f --- /dev/null +++ b/CoreFoundation/CFBundlePriv.h
@@ -0,0 +1,275 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFBundlePriv.h + Copyright (c) 1999-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFBUNDLEPRIV__) +#define __COREFOUNDATION_CFBUNDLEPRIV__ 1 + +#include <CoreFoundation/CFBase.h> +#include <CoreFoundation/CFArray.h> +#include <CoreFoundation/CFBundle.h> +#include <CoreFoundation/CFDictionary.h> +#include <CoreFoundation/CFString.h> +#include <CoreFoundation/CFURL.h> + +CF_EXTERN_C_BEGIN + +/* Finder stuff */ +CF_EXPORT +const CFStringRef _kCFBundlePackageTypeKey; +CF_EXPORT +const CFStringRef _kCFBundleSignatureKey; +CF_EXPORT +const CFStringRef _kCFBundleIconFileKey; +CF_EXPORT +const CFStringRef _kCFBundleDocumentTypesKey; +CF_EXPORT +const CFStringRef _kCFBundleURLTypesKey; + +/* Localizable Finder stuff */ +CF_EXPORT +const CFStringRef _kCFBundleDisplayNameKey; +CF_EXPORT +const CFStringRef _kCFBundleShortVersionStringKey; +CF_EXPORT +const CFStringRef _kCFBundleGetInfoStringKey; +CF_EXPORT +const CFStringRef _kCFBundleGetInfoHTMLKey; + +/* Sub-keys for CFBundleDocumentTypes dictionaries */ +CF_EXPORT +const CFStringRef _kCFBundleTypeNameKey; +CF_EXPORT +const CFStringRef _kCFBundleTypeRoleKey; +CF_EXPORT +const CFStringRef _kCFBundleTypeIconFileKey; +CF_EXPORT +const CFStringRef _kCFBundleTypeOSTypesKey; +CF_EXPORT +const CFStringRef _kCFBundleTypeExtensionsKey; +CF_EXPORT +const CFStringRef _kCFBundleTypeMIMETypesKey; + +/* Sub-keys for CFBundleURLTypes dictionaries */ +CF_EXPORT +const CFStringRef _kCFBundleURLNameKey; +CF_EXPORT +const CFStringRef _kCFBundleURLIconFileKey; +CF_EXPORT +const CFStringRef _kCFBundleURLSchemesKey; + +/* Compatibility key names */ +CF_EXPORT +const CFStringRef _kCFBundleOldExecutableKey; +CF_EXPORT +const CFStringRef _kCFBundleOldInfoDictionaryVersionKey; +CF_EXPORT +const CFStringRef _kCFBundleOldNameKey; +CF_EXPORT +const CFStringRef _kCFBundleOldIconFileKey; +CF_EXPORT +const CFStringRef _kCFBundleOldDocumentTypesKey; +CF_EXPORT +const CFStringRef _kCFBundleOldShortVersionStringKey; + +/* Compatibility CFBundleDocumentTypes key names */ +CF_EXPORT +const CFStringRef _kCFBundleOldTypeNameKey; +CF_EXPORT +const CFStringRef _kCFBundleOldTypeRoleKey; +CF_EXPORT +const CFStringRef _kCFBundleOldTypeIconFileKey; +CF_EXPORT +const CFStringRef _kCFBundleOldTypeExtensions1Key; +CF_EXPORT +const CFStringRef _kCFBundleOldTypeExtensions2Key; +CF_EXPORT +const CFStringRef _kCFBundleOldTypeOSTypesKey; + + +/* Functions for examining directories that may "look like" bundles */ + +CF_EXPORT +CFURLRef _CFBundleCopyBundleURLForExecutableURL(CFURLRef url); + +CF_EXPORT +Boolean _CFBundleURLLooksLikeBundle(CFURLRef url); + +CF_EXPORT +CFBundleRef _CFBundleCreateIfLooksLikeBundle(CFAllocatorRef allocator, CFURLRef url); + +CF_EXPORT +CFBundleRef _CFBundleGetMainBundleIfLooksLikeBundle(void); + +CF_EXPORT +Boolean _CFBundleMainBundleInfoDictionaryComesFromResourceFork(void); + +CF_EXPORT +CFBundleRef _CFBundleCreateWithExecutableURLIfLooksLikeBundle(CFAllocatorRef allocator, CFURLRef url); + +CF_EXPORT +CFURLRef _CFBundleCopyMainBundleExecutableURL(Boolean *looksLikeBundle); + +CF_EXPORT +CFBundleRef _CFBundleGetExistingBundleWithBundleURL(CFURLRef bundleURL); + +/* Functions for examining the structure of a bundle */ + +CF_EXPORT +CFURLRef _CFBundleCopyResourceForkURL(CFBundleRef bundle); + +CF_EXPORT +CFURLRef _CFBundleCopyInfoPlistURL(CFBundleRef bundle); + + +/* Functions for working without a bundle instance */ + +CF_EXPORT +CFURLRef _CFBundleCopyExecutableURLInDirectory(CFURLRef url); + +CF_EXPORT +CFURLRef _CFBundleCopyOtherExecutableURLInDirectory(CFURLRef url); + + +/* Functions for dealing with localizations */ + +CF_EXPORT +void _CFBundleGetLanguageAndRegionCodes(SInt32 *languageCode, SInt32 *regionCode); +// may return -1 for either one if no code can be found + +CF_EXPORT +Boolean CFBundleGetLocalizationInfoForLocalization(CFStringRef localizationName, SInt32 *languageCode, SInt32 *regionCode, SInt32 *scriptCode, CFStringEncoding *stringEncoding); + /* Gets the appropriate language and region codes, and the default */ + /* script code and encoding, for the localization specified. */ + /* Pass NULL for the localizationName to get these values for the */ + /* single most preferred localization in the current context. */ + /* May give -1 if there is no language or region code for a particular */ + /* localization. Returns false if CFBundle has no information about */ + /* the given localization. */ + +CF_EXPORT +CFStringRef CFBundleCopyLocalizationForLocalizationInfo(SInt32 languageCode, SInt32 regionCode, SInt32 scriptCode, CFStringEncoding stringEncoding); + /* Returns the default localization for the combination of codes */ + /* specified. Pass in -1 for language, region code, or script code, or */ + /* 0xFFFF for stringEncoding, if you do not wish to specify one of these. */ + +CF_EXPORT +void _CFBundleSetDefaultLocalization(CFStringRef localizationName); + + +/* Functions for dealing specifically with CFM executables */ + +CF_EXPORT +void *_CFBundleGetCFMFunctionPointerForName(CFBundleRef bundle, CFStringRef funcName); + +CF_EXPORT +void _CFBundleGetCFMFunctionPointersForNames(CFBundleRef bundle, CFArrayRef functionNames, void *ftbl[]); + +CF_EXPORT +void _CFBundleSetCFMConnectionID(CFBundleRef bundle, void *connectionID); + + +/* Miscellaneous functions */ + +CF_EXPORT +CFStringRef _CFBundleCopyFileTypeForFileURL(CFURLRef url); + +CF_EXPORT +CFStringRef _CFBundleCopyFileTypeForFileData(CFDataRef data); + +CF_EXPORT +Boolean _CFBundleGetHasChanged(CFBundleRef bundle); + +CF_EXPORT +void _CFBundleFlushCaches(void); + +CF_EXPORT +void _CFBundleFlushCachesForURL(CFURLRef url); + +CF_EXPORT +void _CFBundleFlushBundleCaches(CFBundleRef bundle); // The previous two functions flush cached resource paths; this one also flushes bundle-specific caches such as the info dictionary and strings files + +CF_EXPORT +void _CFBundleSetStringsFilesShared(CFBundleRef bundle, Boolean flag); + +CF_EXPORT +Boolean _CFBundleGetStringsFilesShared(CFBundleRef bundle); + + +/* Functions deprecated as SPI */ + +CF_EXPORT +CFDictionaryRef _CFBundleGetLocalInfoDictionary(CFBundleRef bundle); // deprecated in favor of CFBundleGetLocalInfoDictionary + +CF_EXPORT +CFPropertyListRef _CFBundleGetValueForInfoKey(CFBundleRef bundle, CFStringRef key); // deprecated in favor of CFBundleGetValueForInfoDictionaryKey + +CF_EXPORT +Boolean _CFBundleGetPackageInfoInDirectory(CFAllocatorRef alloc, CFURLRef url, UInt32 *packageType, UInt32 *packageCreator); // deprecated in favor of CFBundleGetPackageInfoInDirectory + +CF_EXPORT +CFDictionaryRef _CFBundleCopyInfoDictionaryInResourceFork(CFURLRef url); // CFBundleCopyInfoDictionaryForURL is usually preferred; for the main bundle, however, no special call is necessary, since the info dictionary will automatically be available whether the app is bundled or not + +CF_EXPORT +CFURLRef _CFBundleCopyPrivateFrameworksURL(CFBundleRef bundle); // deprecated in favor of CFBundleCopyPrivateFrameworksURL + +CF_EXPORT +CFURLRef _CFBundleCopySharedFrameworksURL(CFBundleRef bundle); // deprecated in favor of CFBundleCopySharedFrameworksURL + +CF_EXPORT +CFURLRef _CFBundleCopySharedSupportURL(CFBundleRef bundle); // deprecated in favor of CFBundleCopySharedSupportURL + +CF_EXPORT +CFURLRef _CFBundleCopyBuiltInPlugInsURL(CFBundleRef bundle); // deprecated in favor of CFBundleCopyBuiltInPlugInsURL + +CF_EXPORT +CFArrayRef _CFBundleCopyBundleRegionsArray(CFBundleRef bundle); // deprecated in favor of CFBundleCopyBundleLocalizations + +CF_EXPORT +CFURLRef _CFBundleCopyResourceURLForLanguage(CFBundleRef bundle, CFStringRef resourceName, CFStringRef resourceType, CFStringRef subDirName, CFStringRef language); // deprecated in favor of CFBundleCopyResourceURLForLocalization + +CF_EXPORT +CFArrayRef _CFBundleCopyResourceURLsOfTypeForLanguage(CFBundleRef bundle, CFStringRef resourceType, CFStringRef subDirName, CFStringRef language); // deprecated in favor of CFBundleCopyResourceURLsOfTypeForLocalization + +CF_EXPORT +CFBundleRefNum _CFBundleOpenBundleResourceFork(CFBundleRef bundle); // deprecated in favor of CFBundleOpenBundleResourceMap + +CF_EXPORT +void _CFBundleCloseBundleResourceFork(CFBundleRef bundle); // deprecated in favor of CFBundleCloseBundleResourceMap + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFBUNDLEPRIV__ */ +
diff --git a/CoreFoundation/CFBundle_BinaryTypes.h b/CoreFoundation/CFBundle_BinaryTypes.h new file mode 100644 index 0000000..67e0372 --- /dev/null +++ b/CoreFoundation/CFBundle_BinaryTypes.h
@@ -0,0 +1,83 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFBundle_BinaryTypes.h + Copyright (c) 1999-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFBUNDLE_BINARYTYPES__) +#define __COREFOUNDATION_CFBUNDLE_BINARYTYPES__ 1 + +CF_EXTERN_C_BEGIN + + +#if DEPLOYMENT_TARGET_MACOSX +#if !defined(DISABLE_DYLD_USAGE) +#define BINARY_SUPPORT_DYLD 1 +#endif +#if !defined(DISABLE_DLFCN_USAGE) +#define BINARY_SUPPORT_DLFCN 1 +#endif +#elif DEPLOYMENT_TARGET_WINDOWS +#define BINARY_SUPPORT_DLL 1 +#elif DEPLOYMENT_TARGET_LINUX +#define BINARY_SUPPORT_DLFCN 1 +#else +#error Unknown or unspecified DEPLOYMENT_TARGET +#endif + + +typedef enum { + __CFBundleUnknownBinary, + __CFBundleCFMBinary, + __CFBundleDYLDExecutableBinary, + __CFBundleDYLDBundleBinary, + __CFBundleDYLDFrameworkBinary, + __CFBundleDLLBinary, + __CFBundleUnreadableBinary, + __CFBundleNoBinary, + __CFBundleELFBinary +} __CFPBinaryType; + +/* Intended for eventual public consumption */ +typedef enum { + kCFBundleOtherExecutableType = 0, + kCFBundleMachOExecutableType, + kCFBundlePEFExecutableType, + kCFBundleELFExecutableType, + kCFBundleDLLExecutableType +} CFBundleExecutableType; + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFBUNDLE_BINARYTYPES__ */ +
diff --git a/CoreFoundation/CFBundle_Internal.h b/CoreFoundation/CFBundle_Internal.h new file mode 100644 index 0000000..c6518b0 --- /dev/null +++ b/CoreFoundation/CFBundle_Internal.h
@@ -0,0 +1,250 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFBundle_Internal.h + Copyright (c) 1999-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFBUNDLE_INTERNAL__) +#define __COREFOUNDATION_CFBUNDLE_INTERNAL__ 1 + +#include <CoreFoundation/CFDate.h> +#include <CoreFoundation/CFBundle.h> +#include <CoreFoundation/CFPlugIn.h> +#include <CoreFoundation/CFError.h> +#include "CFInternal.h" +#include "CFPlugIn_Factory.h" +#include "CFBundle_BinaryTypes.h" + +CF_EXTERN_C_BEGIN + +#define __kCFLogBundle 3 +#define __kCFLogPlugIn 3 + +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX || 0 +#define PLATFORM_PATH_STYLE kCFURLPOSIXPathStyle +#elif DEPLOYMENT_TARGET_WINDOWS +#define PLATFORM_PATH_STYLE kCFURLWindowsPathStyle +#else +#error Unknown or unspecified DEPLOYMENT_TARGET +#endif + +#define CFBundleExecutableNotFoundError 4 +#define CFBundleExecutableNotLoadableError 3584 +#define CFBundleExecutableArchitectureMismatchError 3585 +#define CFBundleExecutableRuntimeMismatchError 3586 +#define CFBundleExecutableLoadError 3587 +#define CFBundleExecutableLinkError 3588 + +typedef struct __CFResourceData { + CFMutableDictionaryRef _stringTableCache; + Boolean _executableLacksResourceFork; + Boolean _infoDictionaryFromResourceFork; + char _padding[2]; +} _CFResourceData; + +extern _CFResourceData *__CFBundleGetResourceData(CFBundleRef bundle); + +typedef struct __CFPlugInData { + Boolean _isPlugIn; + Boolean _loadOnDemand; + Boolean _isDoingDynamicRegistration; + Boolean _unused1; + UInt32 _instanceCount; + CFMutableArrayRef _factories; +} _CFPlugInData; + +extern _CFPlugInData *__CFBundleGetPlugInData(CFBundleRef bundle); + +/* Private CFBundle API */ + +extern Boolean _CFIsResourceAtURL(CFURLRef url, Boolean *isDir); +extern Boolean _CFIsResourceAtPath(CFStringRef path, Boolean *isDir); + +extern Boolean _CFBundleURLLooksLikeBundleVersion(CFURLRef url, UInt8 *version); +extern CFDictionaryRef _CFBundleCopyInfoDictionaryInDirectory(CFAllocatorRef alloc, CFURLRef url, UInt8 *version); +extern CFDictionaryRef _CFBundleCopyInfoDictionaryInDirectoryWithVersion(CFAllocatorRef alloc, CFURLRef url, UInt8 version); +extern CFURLRef _CFBundleCopySupportFilesDirectoryURLInDirectory(CFAllocatorRef alloc, CFURLRef bundleURL, UInt8 version); +extern CFURLRef _CFBundleCopyResourcesDirectoryURLInDirectory(CFAllocatorRef alloc, CFURLRef bundleURL, UInt8 version); + +extern Boolean _CFBundleCouldBeBundle(CFURLRef url); +extern CFURLRef _CFBundleCopyFrameworkURLForExecutablePath(CFAllocatorRef alloc, CFStringRef executablePath); +extern CFURLRef _CFBundleCopyResourceForkURLMayBeLocal(CFBundleRef bundle, Boolean mayBeLocal); +extern CFDictionaryRef _CFBundleCopyInfoDictionaryInResourceForkWithAllocator(CFAllocatorRef alloc, CFURLRef url); +extern CFStringRef _CFBundleCopyBundleDevelopmentRegionFromVersResource(CFBundleRef bundle); +extern CFDictionaryRef _CFBundleCopyInfoDictionaryInExecutable(CFURLRef url); +extern CFArrayRef _CFBundleCopyArchitecturesForExecutable(CFURLRef url); + +extern void _CFBundleAddPreferredLprojNamesInDirectory(CFAllocatorRef alloc, CFURLRef bundleURL, UInt8 version, CFDictionaryRef infoDict, CFMutableArrayRef lprojNames, CFStringRef devLang); + +extern CFStringRef _CFBundleGetPlatformExecutablesSubdirectoryName(void); +extern CFStringRef _CFBundleGetAlternatePlatformExecutablesSubdirectoryName(void); +extern CFStringRef _CFBundleGetOtherPlatformExecutablesSubdirectoryName(void); +extern CFStringRef _CFBundleGetOtherAlternatePlatformExecutablesSubdirectoryName(void); + +extern CFStringRef _CFCreateStringFromVersionNumber(CFAllocatorRef alloc, UInt32 vers); +extern UInt32 _CFVersionNumberFromString(CFStringRef versStr); + +extern void _CFBundleScheduleForUnloading(CFBundleRef bundle); +extern void _CFBundleUnscheduleForUnloading(CFBundleRef bundle); +extern void _CFBundleUnloadScheduledBundles(void); + + +#if defined(BINARY_SUPPORT_DYLD) +// DYLD API +extern __CFPBinaryType _CFBundleGrokBinaryType(CFURLRef executableURL); +extern Boolean _CFBundleDYLDCheckLoaded(CFBundleRef bundle); +extern Boolean _CFBundleDYLDLoadBundle(CFBundleRef bundle, Boolean forceGlobal, CFErrorRef *error); +extern Boolean _CFBundleDYLDLoadFramework(CFBundleRef bundle, CFErrorRef *error); +extern void _CFBundleDYLDUnloadBundle(CFBundleRef bundle); +extern void *_CFBundleDYLDGetSymbolByName(CFBundleRef bundle, CFStringRef symbolName); + +extern CFArrayRef _CFBundleDYLDCopyLoadedImagePathsIfChanged(void); +extern CFArrayRef _CFBundleDYLDCopyLoadedImagePathsForHint(CFStringRef hint); +#endif /* BINARY_SUPPORT_DYLD */ + +#if defined(BINARY_SUPPORT_DLFCN) +// dlfcn API +extern Boolean _CFBundleDlfcnCheckLoaded(CFBundleRef bundle); +extern Boolean _CFBundleDlfcnPreflight(CFBundleRef bundle, CFErrorRef *error); +extern Boolean _CFBundleDlfcnLoadBundle(CFBundleRef bundle, Boolean forceGlobal, CFErrorRef *error); +extern Boolean _CFBundleDlfcnLoadFramework(CFBundleRef bundle, CFErrorRef *error); +extern void _CFBundleDlfcnUnload(CFBundleRef bundle); +extern void *_CFBundleDlfcnGetSymbolByName(CFBundleRef bundle, CFStringRef symbolName); +#endif /* BINARY_SUPPORT_DLFCN */ + + +#if defined(BINARY_SUPPORT_DLL) +extern Boolean _CFBundleDLLLoad(CFBundleRef bundle, CFErrorRef *error); +extern void _CFBundleDLLUnload(CFBundleRef bundle); +extern void *_CFBundleDLLGetSymbolByName(CFBundleRef bundle, CFStringRef symbolName); +#endif /* BINARY_SUPPORT_DLL */ + + +/* Private PlugIn-related CFBundle API */ + +extern Boolean _CFBundleNeedsInitPlugIn(CFBundleRef bundle); +extern void _CFBundleInitPlugIn(CFBundleRef bundle); +extern void _CFBundlePlugInLoaded(CFBundleRef bundle); +extern void _CFBundleDeallocatePlugIn(CFBundleRef bundle); + +extern void _CFPlugInWillUnload(CFPlugInRef plugIn); + +extern void _CFPlugInAddPlugInInstance(CFPlugInRef plugIn); +extern void _CFPlugInRemovePlugInInstance(CFPlugInRef plugIn); + +extern void _CFPlugInAddFactory(CFPlugInRef plugIn, _CFPFactory *factory); +extern void _CFPlugInRemoveFactory(CFPlugInRef plugIn, _CFPFactory *factory); + + +/* Strings for parsing bundle structure */ +#define _CFBundleSupportFilesDirectoryName1 CFSTR("Support Files") +#define _CFBundleSupportFilesDirectoryName2 CFSTR("Contents") +#define _CFBundleResourcesDirectoryName CFSTR("Resources") +#define _CFBundleExecutablesDirectoryName CFSTR("Executables") +#define _CFBundleNonLocalizedResourcesDirectoryName CFSTR("Non-localized Resources") + +#define _CFBundleSupportFilesURLFromBase1 CFSTR("Support%20Files/") +#define _CFBundleSupportFilesURLFromBase2 CFSTR("Contents/") +#define _CFBundleResourcesURLFromBase0 CFSTR("Resources/") +#define _CFBundleResourcesURLFromBase1 CFSTR("Support%20Files/Resources/") +#define _CFBundleResourcesURLFromBase2 CFSTR("Contents/Resources/") +#define _CFBundleExecutablesURLFromBase1 CFSTR("Support%20Files/Executables/") +#define _CFBundleExecutablesURLFromBase2 CFSTR("Contents/") +#define _CFBundleInfoURLFromBase0 CFSTR("Resources/Info.plist") +#define _CFBundleInfoURLFromBase1 CFSTR("Support%20Files/Info.plist") +#define _CFBundleInfoURLFromBase2 CFSTR("Contents/Info.plist") +#define _CFBundleInfoURLFromBase3 CFSTR("Info.plist") +#define _CFBundleInfoFileName CFSTR("Info.plist") +#define _CFBundleInfoURLFromBaseNoExtension0 CFSTR("Resources/Info") +#define _CFBundleInfoURLFromBaseNoExtension1 CFSTR("Support%20Files/Info") +#define _CFBundleInfoURLFromBaseNoExtension2 CFSTR("Contents/Info") +#define _CFBundleInfoURLFromBaseNoExtension3 CFSTR("Info") +#define _CFBundleInfoExtension CFSTR("plist") +#define _CFBundleLocalInfoName CFSTR("InfoPlist") +#define _CFBundlePkgInfoURLFromBase1 CFSTR("Support%20Files/PkgInfo") +#define _CFBundlePkgInfoURLFromBase2 CFSTR("Contents/PkgInfo") +#define _CFBundlePseudoPkgInfoURLFromBase CFSTR("PkgInfo") +#define _CFBundlePrivateFrameworksURLFromBase0 CFSTR("Frameworks/") +#define _CFBundlePrivateFrameworksURLFromBase1 CFSTR("Support%20Files/Frameworks/") +#define _CFBundlePrivateFrameworksURLFromBase2 CFSTR("Contents/Frameworks/") +#define _CFBundleSharedFrameworksURLFromBase0 CFSTR("SharedFrameworks/") +#define _CFBundleSharedFrameworksURLFromBase1 CFSTR("Support%20Files/SharedFrameworks/") +#define _CFBundleSharedFrameworksURLFromBase2 CFSTR("Contents/SharedFrameworks/") +#define _CFBundleSharedSupportURLFromBase0 CFSTR("SharedSupport/") +#define _CFBundleSharedSupportURLFromBase1 CFSTR("Support%20Files/SharedSupport/") +#define _CFBundleSharedSupportURLFromBase2 CFSTR("Contents/SharedSupport/") +#define _CFBundleBuiltInPlugInsURLFromBase0 CFSTR("PlugIns/") +#define _CFBundleBuiltInPlugInsURLFromBase1 CFSTR("Support%20Files/PlugIns/") +#define _CFBundleBuiltInPlugInsURLFromBase2 CFSTR("Contents/PlugIns/") +#define _CFBundleAlternateBuiltInPlugInsURLFromBase0 CFSTR("Plug-ins/") +#define _CFBundleAlternateBuiltInPlugInsURLFromBase1 CFSTR("Support%20Files/Plug-ins/") +#define _CFBundleAlternateBuiltInPlugInsURLFromBase2 CFSTR("Contents/Plug-ins/") + +#define _CFBundleLprojExtension CFSTR("lproj") +#define _CFBundleLprojExtensionWithDot CFSTR(".lproj") + +#define _CFBundleMacOSXPlatformName CFSTR("macos") +#define _CFBundleAlternateMacOSXPlatformName CFSTR("macosx") +#define _CFBundleMacOS8PlatformName CFSTR("macosclassic") +#define _CFBundleAlternateMacOS8PlatformName CFSTR("macos8") +#define _CFBundleWindowsPlatformName CFSTR("windows") +#define _CFBundleHPUXPlatformName CFSTR("hpux") +#define _CFBundleSolarisPlatformName CFSTR("solaris") +#define _CFBundleLinuxPlatformName CFSTR("linux") +#define _CFBundleFreeBSDPlatformName CFSTR("freebsd") + +#define _CFBundleDefaultStringTableName CFSTR("Localizable") +#define _CFBundleStringTableType CFSTR("strings") + +#define _CFBundleUserLanguagesPreferenceName CFSTR("AppleLanguages") +#define _CFBundleOldUserLanguagesPreferenceName CFSTR("NSLanguages") + +#define _CFBundleLocalizedResourceForkFileName CFSTR("Localized") + +#if DEPLOYMENT_TARGET_WINDOWS || 0 +#define _CFBundleWindowsResourceDirectoryExtension CFSTR("resources") +#endif + +/* Old platform names (no longer used) */ +#define _CFBundleMacOSXPlatformName_OLD CFSTR("macintosh") +#define _CFBundleAlternateMacOSXPlatformName_OLD CFSTR("nextstep") +#define _CFBundleWindowsPlatformName_OLD CFSTR("windows") +#define _CFBundleAlternateWindowsPlatformName_OLD CFSTR("winnt") + +#define _CFBundleMacOSXInfoPlistPlatformName_OLD CFSTR("macos") +#define _CFBundleWindowsInfoPlistPlatformName_OLD CFSTR("win32") + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFBUNDLE_INTERNAL__ */ +
diff --git a/CoreFoundation/CFBundle_Resources.c b/CoreFoundation/CFBundle_Resources.c new file mode 100644 index 0000000..fef002f --- /dev/null +++ b/CoreFoundation/CFBundle_Resources.c
@@ -0,0 +1,2212 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. All rights reserved. + * Copyright (c) 2009 David M. Cotter <me@davevcotter.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@ + */ +/* CFBundle_Resources.c + Copyright (c) 1999-2007 Apple Inc. All rights reserved. + Responsibility: Doug Davidson +*/ + +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX +#define READ_DIRECTORIES 1 +#endif + +#define READ_DIRECTORIES_CACHE_CAPACITY 128 + +#include "CFBundle_Internal.h" +#include <CoreFoundation/CFURLAccess.h> +#include <CoreFoundation/CFPropertyList.h> +#include <CoreFoundation/CFByteOrder.h> +#include <CoreFoundation/CFNumber.h> +#include <string.h> +#include "CFInternal.h" +#include "CFPriv.h" +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.h> +#include <ctype.h> + +#if DEPLOYMENT_TARGET_MACOSX +#include <CoreFoundation/CFPreferences.h> +#include <unistd.h> +#endif + +#if READ_DIRECTORIES +#include <dirent.h> +#endif /* READ_DIRECTORIES */ + +#if DEPLOYMENT_TARGET_WINDOWS && !defined(__GNUC__) +#include "compat/dirent.h" +#define READ_DIRECTORIES 1 +#endif + + + +// All new-style bundles will have these extensions. +CF_INLINE CFStringRef _CFGetPlatformName(void) { +#if DEPLOYMENT_TARGET_MACOSX + return _CFBundleMacOSXPlatformName; +#elif DEPLOYMENT_TARGET_SOLARIS + return _CFBundleSolarisPlatformName; +#elif DEPLOYMENT_TARGET_HPUX + return _CFBundleHPUXPlatformName; +#elif DEPLOYMENT_TARGET_LINUX + return _CFBundleLinuxPlatformName; +#elif DEPLOYMENT_TARGET_FREEBSD + return _CFBundleFreeBSDPlatformName; +#elif DEPLOYMENT_TARGET_WINDOWS + return _CFBundleWindowsPlatformName; +#else +#error Unknown or unspecified DEPLOYMENT_TARGET +#endif +} + +CF_INLINE CFStringRef _CFGetAlternatePlatformName(void) { +#if DEPLOYMENT_TARGET_MACOSX + return _CFBundleAlternateMacOSXPlatformName; +#else + return CFSTR(""); +#endif +} + +static CFSpinLock_t CFBundleResourceGlobalDataLock = CFSpinLockInit; +static UniChar *_AppSupportUniChars1 = NULL; +static CFIndex _AppSupportLen1 = 0; +static UniChar *_AppSupportUniChars2 = NULL; +static CFIndex _AppSupportLen2 = 0; +static UniChar *_ResourcesUniChars = NULL; +static CFIndex _ResourcesLen = 0; +static UniChar *_PlatformUniChars = NULL; +static CFIndex _PlatformLen = 0; +static UniChar *_AlternatePlatformUniChars = NULL; +static CFIndex _AlternatePlatformLen = 0; +static UniChar *_LprojUniChars = NULL; +static CFIndex _LprojLen = 0; +static UniChar *_GlobalResourcesUniChars = NULL; +static CFIndex _GlobalResourcesLen = 0; +static UniChar *_InfoExtensionUniChars = NULL; +static CFIndex _InfoExtensionLen = 0; + +static void _CFBundleInitStaticUniCharBuffers(void) { + CFStringRef appSupportStr1 = _CFBundleSupportFilesDirectoryName1; + CFStringRef appSupportStr2 = _CFBundleSupportFilesDirectoryName2; + CFStringRef resourcesStr = _CFBundleResourcesDirectoryName; + CFStringRef platformStr = _CFGetPlatformName(); + CFStringRef alternatePlatformStr = _CFGetAlternatePlatformName(); + CFStringRef lprojStr = _CFBundleLprojExtension; + CFStringRef globalResourcesStr = _CFBundleNonLocalizedResourcesDirectoryName; + CFStringRef infoExtensionStr = _CFBundleInfoExtension; + + CFAllocatorRef alloc = __CFGetDefaultAllocator(); + + _AppSupportLen1 = CFStringGetLength(appSupportStr1); + _AppSupportLen2 = CFStringGetLength(appSupportStr2); + _ResourcesLen = CFStringGetLength(resourcesStr); + _PlatformLen = CFStringGetLength(platformStr); + _AlternatePlatformLen = CFStringGetLength(alternatePlatformStr); + _LprojLen = CFStringGetLength(lprojStr); + _GlobalResourcesLen = CFStringGetLength(globalResourcesStr); + _InfoExtensionLen = CFStringGetLength(infoExtensionStr); + + _AppSupportUniChars1 = (UniChar *)CFAllocatorAllocate(alloc, sizeof(UniChar) * (_AppSupportLen1 + _AppSupportLen2 + _ResourcesLen + _PlatformLen + _AlternatePlatformLen + _LprojLen + _GlobalResourcesLen + _InfoExtensionLen), 0); + _AppSupportUniChars2 = _AppSupportUniChars1 + _AppSupportLen1; + _ResourcesUniChars = _AppSupportUniChars2 + _AppSupportLen2; + _PlatformUniChars = _ResourcesUniChars + _ResourcesLen; + _AlternatePlatformUniChars = _PlatformUniChars + _PlatformLen; + _LprojUniChars = _AlternatePlatformUniChars + _AlternatePlatformLen; + _GlobalResourcesUniChars = _LprojUniChars + _LprojLen; + _InfoExtensionUniChars = _GlobalResourcesUniChars + _GlobalResourcesLen; + + if (_AppSupportLen1 > 0) CFStringGetCharacters(appSupportStr1, CFRangeMake(0, _AppSupportLen1), _AppSupportUniChars1); + if (_AppSupportLen2 > 0) CFStringGetCharacters(appSupportStr2, CFRangeMake(0, _AppSupportLen2), _AppSupportUniChars2); + if (_ResourcesLen > 0) CFStringGetCharacters(resourcesStr, CFRangeMake(0, _ResourcesLen), _ResourcesUniChars); + if (_PlatformLen > 0) CFStringGetCharacters(platformStr, CFRangeMake(0, _PlatformLen), _PlatformUniChars); + if (_AlternatePlatformLen > 0) CFStringGetCharacters(alternatePlatformStr, CFRangeMake(0, _AlternatePlatformLen), _AlternatePlatformUniChars); + if (_LprojLen > 0) CFStringGetCharacters(lprojStr, CFRangeMake(0, _LprojLen), _LprojUniChars); + if (_GlobalResourcesLen > 0) CFStringGetCharacters(globalResourcesStr, CFRangeMake(0, _GlobalResourcesLen), _GlobalResourcesUniChars); + if (_InfoExtensionLen > 0) CFStringGetCharacters(infoExtensionStr, CFRangeMake(0, _InfoExtensionLen), _InfoExtensionUniChars); +} + +CF_INLINE void _CFEnsureStaticBuffersInited(void) { + __CFSpinLock(&CFBundleResourceGlobalDataLock); + if (!_AppSupportUniChars1) _CFBundleInitStaticUniCharBuffers(); + __CFSpinUnlock(&CFBundleResourceGlobalDataLock); +} + +#if READ_DIRECTORIES + +static CFMutableDictionaryRef contentsCache = NULL; +static CFMutableDictionaryRef directoryContentsCache = NULL; +static CFMutableDictionaryRef unknownContentsCache = NULL; + +typedef enum { + _CFBundleAllContents = 0, + _CFBundleDirectoryContents = 1, + _CFBundleUnknownContents = 2 +} _CFBundleDirectoryContentsType; + +static CFArrayRef _CFBundleCopyDirectoryContentsAtPath(CFStringRef path, _CFBundleDirectoryContentsType contentsType) { + CFArrayRef result = NULL; + + __CFSpinLock(&CFBundleResourceGlobalDataLock); + if (contentsType == _CFBundleUnknownContents) { + if (unknownContentsCache) result = (CFMutableArrayRef)CFDictionaryGetValue(unknownContentsCache, path); + } else if (contentsType == _CFBundleDirectoryContents) { + if (directoryContentsCache) result = (CFMutableArrayRef)CFDictionaryGetValue(directoryContentsCache, path); + } else { + if (contentsCache) result = (CFMutableArrayRef)CFDictionaryGetValue(contentsCache, path); + } + if (result) CFRetain(result); + __CFSpinUnlock(&CFBundleResourceGlobalDataLock); + + if (!result) { + Boolean tryToOpen = false, allDots = true; + char cpathBuff[CFMaxPathSize]; + CFIndex cpathLen = 0, idx, lastSlashIdx = 0; + DIR *dirp = NULL; + struct dirent *dent; + CFMutableArrayRef contents = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks), directoryContents = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks), unknownContents = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); + CFStringRef dirName, name; + struct stat statBuf; + + cpathBuff[0] = '\0'; + if (CFStringGetFileSystemRepresentation(path, cpathBuff, CFMaxPathSize)) { + tryToOpen = true; + cpathLen = (CFIndex)strlen(cpathBuff); + + // First see whether we already know that the directory doesn't exist + for (idx = cpathLen; lastSlashIdx == 0 && idx-- > 0;) { + if (cpathBuff[idx] == '/') lastSlashIdx = idx; + else if (cpathBuff[idx] != '.') allDots = false; + } + if (lastSlashIdx > 0 && lastSlashIdx + 1 < cpathLen && !allDots) { + cpathBuff[lastSlashIdx] = '\0'; + dirName = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, cpathBuff); + if (dirName) { + name = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, cpathBuff + lastSlashIdx + 1); + if (name) { + // ??? we might like to use directoryContentsCache rather than contentsCache here, but we cannot unless we resolve DT_LNKs below + CFArrayRef dirDirContents = NULL; + + __CFSpinLock(&CFBundleResourceGlobalDataLock); + if (contentsCache) dirDirContents = (CFArrayRef)CFDictionaryGetValue(contentsCache, dirName); + if (dirDirContents) { + Boolean foundIt = false; + CFIndex dirDirIdx, dirDirLength = CFArrayGetCount(dirDirContents); + for (dirDirIdx = 0; !foundIt && dirDirIdx < dirDirLength; dirDirIdx++) if (kCFCompareEqualTo == CFStringCompare(name, (CFStringRef)CFArrayGetValueAtIndex(dirDirContents, dirDirIdx), kCFCompareCaseInsensitive)) foundIt = true; + if (!foundIt) tryToOpen = false; + } + __CFSpinUnlock(&CFBundleResourceGlobalDataLock); + + CFRelease(name); + } + CFRelease(dirName); + } + cpathBuff[lastSlashIdx] = '/'; + } + } + if (tryToOpen && stat(cpathBuff, &statBuf) == 0 && (statBuf.st_mode & S_IFMT) == S_IFDIR && (dirp = opendir(cpathBuff))) { + while ((dent = readdir(dirp))) { + CFIndex nameLen = (CFIndex)strlen(dent->d_name); + if (0 == nameLen || 0 == dent->d_fileno || ('.' == dent->d_name[0] && (1 == nameLen || (2 == nameLen && '.' == dent->d_name[1]) || '_' == dent->d_name[1]))) continue; + name = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, dent->d_name); + if (name) { + // ??? should we follow links for DT_LNK? unless we do, results are approximate, but for performance reasons we do not + // ??? likewise for DT_UNKNOWN + // ??? the utility of distinguishing directories from other contents is somewhat doubtful anyway + CFArrayAppendValue(contents, name); + if (dent->d_type == DT_DIR) { + CFArrayAppendValue(directoryContents, name); + } else if (dent->d_type == DT_UNKNOWN) { + CFArrayAppendValue(unknownContents, name); + } + CFRelease(name); + } + } + (void)closedir(dirp); + } + + __CFSpinLock(&CFBundleResourceGlobalDataLock); + if (!contentsCache) contentsCache = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, READ_DIRECTORIES_CACHE_CAPACITY, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (READ_DIRECTORIES_CACHE_CAPACITY <= CFDictionaryGetCount(contentsCache)) CFDictionaryRemoveAllValues(contentsCache); + CFDictionaryAddValue(contentsCache, path, contents); + + if (!directoryContentsCache) directoryContentsCache = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, READ_DIRECTORIES_CACHE_CAPACITY, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (READ_DIRECTORIES_CACHE_CAPACITY <= CFDictionaryGetCount(directoryContentsCache)) CFDictionaryRemoveAllValues(directoryContentsCache); + CFDictionaryAddValue(directoryContentsCache, path, directoryContents); + + if (!unknownContentsCache) unknownContentsCache = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, READ_DIRECTORIES_CACHE_CAPACITY, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (READ_DIRECTORIES_CACHE_CAPACITY <= CFDictionaryGetCount(unknownContentsCache)) CFDictionaryRemoveAllValues(unknownContentsCache); + CFDictionaryAddValue(unknownContentsCache, path, unknownContents); + + if (contentsType == _CFBundleUnknownContents) { + result = (CFArrayRef)CFRetain(unknownContents); + } else if (contentsType == _CFBundleDirectoryContents) { + result = (CFArrayRef)CFRetain(directoryContents); + } else { + result = (CFArrayRef)CFRetain(contents); + } + + CFRelease(contents); + CFRelease(directoryContents); + CFRelease(unknownContents); + __CFSpinUnlock(&CFBundleResourceGlobalDataLock); + } + return result; +} + +static void _CFBundleFlushContentsCaches(void) { + __CFSpinLock(&CFBundleResourceGlobalDataLock); + if (contentsCache) CFDictionaryRemoveAllValues(contentsCache); + if (directoryContentsCache) CFDictionaryRemoveAllValues(directoryContentsCache); + if (unknownContentsCache) CFDictionaryRemoveAllValues(unknownContentsCache); + __CFSpinUnlock(&CFBundleResourceGlobalDataLock); +} + +static void _CFBundleFlushContentsCacheForPath(CFMutableDictionaryRef cache, CFStringRef path) { + CFStringRef keys[READ_DIRECTORIES_CACHE_CAPACITY]; + unsigned i, count = CFDictionaryGetCount(cache); + if (count <= READ_DIRECTORIES_CACHE_CAPACITY) { + CFDictionaryGetKeysAndValues(cache, (const void **)keys, NULL); + for (i = 0; i < count; i++) { + if (CFStringFindWithOptions(keys[i], path, CFRangeMake(0, CFStringGetLength(keys[i])), kCFCompareAnchored|kCFCompareCaseInsensitive, NULL)) CFDictionaryRemoveValue(cache, keys[i]); + } + } +} + +static void _CFBundleFlushContentsCachesForPath(CFStringRef path) { + __CFSpinLock(&CFBundleResourceGlobalDataLock); + if (contentsCache) _CFBundleFlushContentsCacheForPath(contentsCache, path); + if (directoryContentsCache) _CFBundleFlushContentsCacheForPath(directoryContentsCache, path); + if (unknownContentsCache) _CFBundleFlushContentsCacheForPath(unknownContentsCache, path); + __CFSpinUnlock(&CFBundleResourceGlobalDataLock); +} + +#endif /* READ_DIRECTORIES */ + +CF_EXPORT void _CFBundleFlushCachesForURL(CFURLRef url) { +#if READ_DIRECTORIES + CFURLRef absoluteURL = CFURLCopyAbsoluteURL(url); + CFStringRef path = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE); + _CFBundleFlushContentsCachesForPath(path); + CFRelease(path); + CFRelease(absoluteURL); +#endif /* READ_DIRECTORIES */ +} + +CF_EXPORT void _CFBundleFlushCaches(void) { +#if READ_DIRECTORIES + _CFBundleFlushContentsCaches(); +#endif /* READ_DIRECTORIES */ +} + +__private_extern__ Boolean _CFIsResourceAtURL(CFURLRef url, Boolean *isDir) { + Boolean exists; + SInt32 mode; + if (_CFGetFileProperties(kCFAllocatorSystemDefault, url, &exists, &mode, NULL, NULL, NULL, NULL) == 0) { + if (isDir) *isDir = ((exists && ((mode & S_IFMT) == S_IFDIR)) ? true : false); + return (exists && (mode & 0444)); + } else { + return false; + } +} + +__private_extern__ Boolean _CFIsResourceAtPath(CFStringRef path, Boolean *isDir) { + Boolean result = false; + CFURLRef url = CFURLCreateWithFileSystemPath(CFGetAllocator(path), path, PLATFORM_PATH_STYLE, false); + if (url) { + result = _CFIsResourceAtURL(url, isDir); + CFRelease(url); + } + return result; +} + +#if READ_DIRECTORIES +static CFArrayRef _CFCopyTypesForSearchBundleDirectory(CFAllocatorRef alloc, UniChar *pathUniChars, CFIndex pathLen, UniChar *nameUniChars, CFIndex nameLen, CFArrayRef resTypes, CFMutableStringRef cheapStr, CFMutableStringRef tmpString, uint8_t version) { + CFMutableArrayRef result = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks); + CFArrayRef contents; + CFRange contentsRange, resultRange = CFRangeMake(0, 0); + CFIndex dirPathLen = pathLen, numResTypes = CFArrayGetCount(resTypes), i, j; + + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, dirPathLen, dirPathLen); + CFStringReplaceAll(cheapStr, tmpString); + //fprintf(stderr, "looking in ");CFShow(cheapStr); + contents = _CFBundleCopyDirectoryContentsAtPath(cheapStr, _CFBundleAllContents); + contentsRange = CFRangeMake(0, CFArrayGetCount(contents)); + + CFStringSetExternalCharactersNoCopy(tmpString, nameUniChars, nameLen, nameLen); + CFStringReplaceAll(cheapStr, tmpString); + for (i = 0; i < contentsRange.length; i++) { + CFStringRef content = (CFStringRef)CFArrayGetValueAtIndex(contents, i); + if (CFStringHasPrefix(content, cheapStr)) { + //fprintf(stderr, "found ");CFShow(content); + for (j = 0; j < numResTypes; j++) { + CFStringRef resType = (CFStringRef)CFArrayGetValueAtIndex(resTypes, j); + if (!CFArrayContainsValue(result, resultRange, resType) && CFStringHasSuffix(content, resType)) { + CFArrayAppendValue(result, resType); + resultRange.length = CFArrayGetCount(result); + } + } + } + } + //fprintf(stderr, "result ");CFShow(result); + CFRelease(contents); + return result; +} +#endif /* READ_DIRECTORIES */ + +static void _CFSearchBundleDirectory(CFAllocatorRef alloc, CFMutableArrayRef result, UniChar *pathUniChars, CFIndex pathLen, UniChar *nameUniChars, CFIndex nameLen, UniChar *typeUniChars, CFIndex typeLen, CFMutableStringRef cheapStr, CFMutableStringRef tmpString, uint8_t version) { + // pathUniChars is the full path to the directory we are searching. + // nameUniChars is what we are looking for. + // typeUniChars is the type we are looking for. + // platformUniChars is the platform name. + // cheapStr is available for our use for whatever we want. + // URLs for found resources get added to result. + CFIndex savedPathLen; + Boolean appendSucceeded = true, platformGenericFound = false, platformSpecificFound = false, platformGenericIsDir = false, platformSpecificIsDir = false, platformGenericIsUnknown = false, platformSpecificIsUnknown = false; + CFStringRef platformGenericStr = NULL; + +#if READ_DIRECTORIES + CFIndex dirPathLen = pathLen; + CFArrayRef contents, directoryContents, unknownContents; + CFRange contentsRange, directoryContentsRange, unknownContentsRange; + + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, dirPathLen, dirPathLen); + CFStringReplaceAll(cheapStr, tmpString); + //fprintf(stderr, "looking in ");CFShow(cheapStr); + contents = _CFBundleCopyDirectoryContentsAtPath(cheapStr, _CFBundleAllContents); + contentsRange = CFRangeMake(0, CFArrayGetCount(contents)); + directoryContents = _CFBundleCopyDirectoryContentsAtPath(cheapStr, _CFBundleDirectoryContents); + directoryContentsRange = CFRangeMake(0, CFArrayGetCount(directoryContents)); + unknownContents = _CFBundleCopyDirectoryContentsAtPath(cheapStr, _CFBundleUnknownContents); + unknownContentsRange = CFRangeMake(0, CFArrayGetCount(unknownContents)); +#endif /* READ_DIRECTORIES */ + + if (nameLen > 0) appendSucceeded = _CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, nameUniChars, nameLen); + savedPathLen = pathLen; + if (appendSucceeded && typeLen > 0) appendSucceeded = _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, typeUniChars, typeLen); + if (appendSucceeded) { +#if READ_DIRECTORIES + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + dirPathLen + 1, pathLen - dirPathLen - 1, pathLen - dirPathLen - 1); + CFStringReplaceAll(cheapStr, tmpString); + platformGenericFound = CFArrayContainsValue(contents, contentsRange, cheapStr); + platformGenericIsDir = CFArrayContainsValue(directoryContents, directoryContentsRange, cheapStr); + platformGenericIsUnknown = CFArrayContainsValue(unknownContents, unknownContentsRange, cheapStr); + //fprintf(stderr, "looking for ");CFShow(cheapStr);if (platformGenericFound) fprintf(stderr, "found it\n"); if (platformGenericIsDir) fprintf(stderr, "a directory\n"); + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); + CFStringReplaceAll(cheapStr, tmpString); + if (platformGenericFound && platformGenericIsUnknown) { + (void)_CFIsResourceAtPath(cheapStr, &platformGenericIsDir); + //if (platformGenericIsDir) fprintf(stderr, "a directory after all\n"); else fprintf(stderr, "not a directory after all\n"); + } +#else /* READ_DIRECTORIES */ + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); + CFStringReplaceAll(cheapStr, tmpString); + platformGenericFound = _CFIsResourceAtPath(cheapStr, &platformGenericIsDir); +#endif /* READ_DIRECTORIES */ + } + + // Check for platform specific. + if (platformGenericFound) { + platformGenericStr = (CFStringRef)CFStringCreateCopy(alloc, cheapStr); + if (!platformSpecificFound && (_PlatformLen > 0)) { + pathLen = savedPathLen; + pathUniChars[pathLen++] = (UniChar)'-'; + memmove(pathUniChars + pathLen, _PlatformUniChars, _PlatformLen * sizeof(UniChar)); + pathLen += _PlatformLen; + if (appendSucceeded && typeLen > 0) appendSucceeded = _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, typeUniChars, typeLen); + if (appendSucceeded) { +#if READ_DIRECTORIES + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + dirPathLen + 1, pathLen - dirPathLen - 1, pathLen - dirPathLen - 1); + CFStringReplaceAll(cheapStr, tmpString); + platformSpecificFound = CFArrayContainsValue(contents, contentsRange, cheapStr); + platformSpecificIsDir = CFArrayContainsValue(directoryContents, directoryContentsRange, cheapStr); + platformSpecificIsUnknown = CFArrayContainsValue(unknownContents, unknownContentsRange, cheapStr); + //fprintf(stderr, "looking for ");CFShow(cheapStr);if (platformSpecificFound) fprintf(stderr, "found it\n"); if (platformSpecificIsDir) fprintf(stderr, "a directory\n"); + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); + CFStringReplaceAll(cheapStr, tmpString); + if (platformSpecificFound && platformSpecificIsUnknown) { + (void)_CFIsResourceAtPath(cheapStr, &platformSpecificIsDir); + //if (platformSpecificIsDir) fprintf(stderr, "a directory after all\n"); else fprintf(stderr, "not a directory after all\n"); + } +#else /* READ_DIRECTORIES */ + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); + CFStringReplaceAll(cheapStr, tmpString); + platformSpecificFound = _CFIsResourceAtPath(cheapStr, &platformSpecificIsDir); +#endif /* READ_DIRECTORIES */ + } + } + } + if (platformSpecificFound) { + CFURLRef url = CFURLCreateWithFileSystemPath(alloc, cheapStr, PLATFORM_PATH_STYLE, platformSpecificIsDir); + CFArrayAppendValue(result, url); + CFRelease(url); + } else if (platformGenericFound) { + CFURLRef url = CFURLCreateWithFileSystemPath(alloc, platformGenericStr ? platformGenericStr : cheapStr, PLATFORM_PATH_STYLE, platformGenericIsDir); + CFArrayAppendValue(result, url); + CFRelease(url); + } + if (platformGenericStr) CFRelease(platformGenericStr); +#if READ_DIRECTORIES + CFRelease(contents); + CFRelease(directoryContents); + CFRelease(unknownContents); +#endif /* READ_DIRECTORIES */ +} + +static void _CFFindBundleResourcesInRawDir(CFAllocatorRef alloc, UniChar *workingUniChars, CFIndex workingLen, UniChar *nameUniChars, CFIndex nameLen, CFArrayRef resTypes, CFIndex limit, uint8_t version, CFMutableStringRef cheapStr, CFMutableStringRef tmpString, CFMutableArrayRef result) { + if (nameLen > 0) { + // If we have a resName, just call the search API. We may have to loop over the resTypes. + if (!resTypes) { + _CFSearchBundleDirectory(alloc, result, workingUniChars, workingLen, nameUniChars, nameLen, NULL, 0, cheapStr, tmpString, version); + } else { + CFArrayRef subResTypes = resTypes; + Boolean releaseSubResTypes = false; + CFIndex i, c = CFArrayGetCount(resTypes); +#if READ_DIRECTORIES + if (c > 2) { + // this is an optimization we employ when searching for large numbers of types, if the directory contents are available + // we scan the directory contents and restrict the list of resTypes to the types that might actually occur with the specified name + subResTypes = _CFCopyTypesForSearchBundleDirectory(alloc, workingUniChars, workingLen, nameUniChars, nameLen, resTypes, cheapStr, tmpString, version); + c = CFArrayGetCount(subResTypes); + releaseSubResTypes = true; + } +#endif /* READ_DIRECTORIES */ + for (i = 0; i < c; i++) { + CFStringRef curType = (CFStringRef)CFArrayGetValueAtIndex(subResTypes, i); + CFIndex typeLen = CFStringGetLength(curType); +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX + STACK_BUFFER_DECL(UniChar, typeChars, typeLen); +#else + UniChar *typeChars = (UniChar*)CFAllocatorAllocate(alloc, sizeof(UniChar) * typeLen, 0); + if (typeChars) { +#endif + CFStringGetCharacters(curType, CFRangeMake(0, typeLen), typeChars); + _CFSearchBundleDirectory(alloc, result, workingUniChars, workingLen, nameUniChars, nameLen, typeChars, typeLen, cheapStr, tmpString, version); + if (limit <= CFArrayGetCount(result)) break; +#if !DEPLOYMENT_TARGET_MACOSX && !DEPLOYMENT_TARGET_WINDOWS && !DEPLOYMENT_TARGET_LINUX + CFAllocatorDeallocate(alloc, typeChars); + } +#endif + } + if (releaseSubResTypes) CFRelease(subResTypes); + } + } else { + // If we have no resName, do it by hand. We may have to loop over the resTypes. + char cpathBuff[CFMaxPathSize]; + CFIndex cpathLen; + CFMutableArrayRef children; + + CFStringSetExternalCharactersNoCopy(tmpString, workingUniChars, workingLen, workingLen); + if (!CFStringGetFileSystemRepresentation(tmpString, cpathBuff, CFMaxPathSize)) return; + cpathLen = (CFIndex)strlen(cpathBuff); + + if (!resTypes) { + // ??? should this use _CFBundleCopyDirectoryContentsAtPath? + children = _CFContentsOfDirectory(alloc, cpathBuff, NULL, NULL, NULL); + if (children) { + CFIndex childIndex, childCount = CFArrayGetCount(children); + for (childIndex = 0; childIndex < childCount; childIndex++) CFArrayAppendValue(result, CFArrayGetValueAtIndex(children, childIndex)); + CFRelease(children); + } + } else { + CFIndex i, c = CFArrayGetCount(resTypes); + for (i = 0; i < c; i++) { + CFStringRef curType = (CFStringRef)CFArrayGetValueAtIndex(resTypes, i); + + // ??? should this use _CFBundleCopyDirectoryContentsAtPath? + children = _CFContentsOfDirectory(alloc, cpathBuff, NULL, NULL, curType); + if (children) { + CFIndex childIndex, childCount = CFArrayGetCount(children); + for (childIndex = 0; childIndex < childCount; childIndex++) CFArrayAppendValue(result, CFArrayGetValueAtIndex(children, childIndex)); + CFRelease(children); + } + if (limit <= CFArrayGetCount(result)) break; + } + } + } +} + +static void _CFFindBundleResourcesInResourcesDir(CFAllocatorRef alloc, UniChar *workingUniChars, CFIndex workingLen, UniChar *subDirUniChars, CFIndex subDirLen, CFArrayRef searchLanguages, UniChar *nameUniChars, CFIndex nameLen, CFArrayRef resTypes, CFIndex limit, uint8_t version, CFMutableStringRef cheapStr, CFMutableStringRef tmpString, CFMutableArrayRef result) { + CFIndex savedWorkingLen = workingLen; + + // Look directly in the directory specified in workingUniChars. as if it is a Resources directory. + if (1 == version) { + // Add the non-localized resource directory. + Boolean appendSucceeded = _CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, _GlobalResourcesUniChars, _GlobalResourcesLen); + if (appendSucceeded && subDirLen > 0) appendSucceeded = _CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, subDirUniChars, subDirLen); + if (appendSucceeded) _CFFindBundleResourcesInRawDir(alloc, workingUniChars, workingLen, nameUniChars, nameLen, resTypes, limit, version, cheapStr, tmpString, result); + // Strip the non-localized resource directory. + workingLen = savedWorkingLen; + } + if (CFArrayGetCount(result) < limit) { + Boolean appendSucceeded = true; + if (subDirLen > 0) appendSucceeded = _CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, subDirUniChars, subDirLen); + if (appendSucceeded) _CFFindBundleResourcesInRawDir(alloc, workingUniChars, workingLen, nameUniChars, nameLen, resTypes, limit, version, cheapStr, tmpString, result); + } + + // Now search the local resources. + workingLen = savedWorkingLen; + if (CFArrayGetCount(result) < limit) { + CFIndex langIndex; + CFIndex langCount = (searchLanguages ? CFArrayGetCount(searchLanguages) : 0); + CFStringRef curLangStr; + CFIndex curLangLen; + // MF:??? OK to hard-wire this length? + UniChar curLangUniChars[255]; + CFIndex numResults = CFArrayGetCount(result); + + for (langIndex = 0; langIndex < langCount; langIndex++) { + curLangStr = (CFStringRef)CFArrayGetValueAtIndex(searchLanguages, langIndex); + curLangLen = CFStringGetLength(curLangStr); + if (curLangLen > 255) curLangLen = 255; + CFStringGetCharacters(curLangStr, CFRangeMake(0, curLangLen), curLangUniChars); + savedWorkingLen = workingLen; + if (!_CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, curLangUniChars, curLangLen)) { + workingLen = savedWorkingLen; + continue; + } + if (!_CFAppendPathExtension(workingUniChars, &workingLen, CFMaxPathSize, _LprojUniChars, _LprojLen)) { + workingLen = savedWorkingLen; + continue; + } + if (subDirLen > 0) { + if (!_CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, subDirUniChars, subDirLen)) { + workingLen = savedWorkingLen; + continue; + } + } + _CFFindBundleResourcesInRawDir(alloc, workingUniChars, workingLen, nameUniChars, nameLen, resTypes, limit, version, cheapStr, tmpString, result); + + // Back off this lproj component + workingLen = savedWorkingLen; + if (CFArrayGetCount(result) != numResults) { + // We found resources in a language we already searched. Don't look any farther. + // We also don't need to check the limit, since if the count changed at all, we are bailing. + break; + } + } + } +} + +extern void _CFStrSetDesiredCapacity(CFMutableStringRef str, CFIndex len); + +CFArrayRef _CFFindBundleResources(CFBundleRef bundle, CFURLRef bundleURL, CFStringRef subDirName, CFArrayRef searchLanguages, CFStringRef resName, CFArrayRef resTypes, CFIndex limit, uint8_t version) { + CFAllocatorRef alloc = (bundle ? CFGetAllocator(bundle) : (CFAllocatorRef)CFRetain(__CFGetDefaultAllocator())); + CFMutableArrayRef result; + UniChar *workingUniChars, *nameUniChars, *subDirUniChars; + CFIndex nameLen = (resName ? CFStringGetLength(resName) : 0); + CFIndex subDirLen = (subDirName ? CFStringGetLength(subDirName) : 0); + CFIndex workingLen, savedWorkingLen; + CFURLRef absoluteURL; + CFStringRef bundlePath; + CFMutableStringRef cheapStr, tmpString; + + result = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks); + // Init the one-time-only unichar buffers. + _CFEnsureStaticBuffersInited(); + + // Build UniChar buffers for some of the string pieces we need. + // One malloc will do. + nameUniChars = (UniChar *)CFAllocatorAllocate(alloc, sizeof(UniChar) * (nameLen + subDirLen + CFMaxPathSize), 0); + subDirUniChars = nameUniChars + nameLen; + workingUniChars = subDirUniChars + subDirLen; + + if (nameLen > 0) CFStringGetCharacters(resName, CFRangeMake(0, nameLen), nameUniChars); + if (subDirLen > 0) CFStringGetCharacters(subDirName, CFRangeMake(0, subDirLen), subDirUniChars); + // Build a UniChar buffer with the absolute path to the bundle's resources directory. + // If no URL was passed, we get it from the bundle. + bundleURL = (bundleURL ? (CFURLRef)CFRetain(bundleURL) : CFBundleCopyBundleURL(bundle)); + absoluteURL = CFURLCopyAbsoluteURL(bundleURL); + bundlePath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE); + CFRelease(absoluteURL); + if ((workingLen = CFStringGetLength(bundlePath)) > 0) CFStringGetCharacters(bundlePath, CFRangeMake(0, workingLen), workingUniChars); + CFRelease(bundlePath); + CFRelease(bundleURL); + savedWorkingLen = workingLen; + if (1 == version) { + _CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, _AppSupportUniChars1, _AppSupportLen1); + } else if (2 == version) { + _CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, _AppSupportUniChars2, _AppSupportLen2); + } + if (0 == version || 1 == version || 2 == version) { + _CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, _ResourcesUniChars, _ResourcesLen); + } + + // both of these used for temp string operations, for slightly + // different purposes, where each type is appropriate + cheapStr = CFStringCreateMutable(alloc, 0); + _CFStrSetDesiredCapacity(cheapStr, CFMaxPathSize); + tmpString = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorSystemDefault, NULL, 0, 0, kCFAllocatorNull); + + _CFFindBundleResourcesInResourcesDir(alloc, workingUniChars, workingLen, subDirUniChars, subDirLen, searchLanguages, nameUniChars, nameLen, resTypes, limit, version, cheapStr, tmpString, result); + + // drd: This unfortunate hack is still necessary because of installer packages + if (0 == version && CFArrayGetCount(result) == 0) { + // Try looking directly in the bundle path + workingLen = savedWorkingLen; + _CFFindBundleResourcesInResourcesDir(alloc, workingUniChars, workingLen, subDirUniChars, subDirLen, searchLanguages, nameUniChars, nameLen, resTypes, limit, version, cheapStr, tmpString, result); + } + + CFRelease(cheapStr); + CFRelease(tmpString); + CFAllocatorDeallocate(alloc, nameUniChars); + if (!bundle) CFRelease(alloc); + + return result; +} + +CF_EXPORT CFURLRef CFBundleCopyResourceURL(CFBundleRef bundle, CFStringRef resourceName, CFStringRef resourceType, CFStringRef subDirName) { + CFURLRef result = NULL; + CFArrayRef languages = _CFBundleGetLanguageSearchList(bundle), types = NULL, array; + if (resourceType) types = CFArrayCreate(CFGetAllocator(bundle), (const void **)&resourceType, 1, &kCFTypeArrayCallBacks); + + array = _CFFindBundleResources(bundle, NULL, subDirName, languages, resourceName, types, 1, _CFBundleLayoutVersion(bundle)); + + if (types) CFRelease(types); + + if (array) { + if (CFArrayGetCount(array) > 0) result = (CFURLRef)CFRetain(CFArrayGetValueAtIndex(array, 0)); + CFRelease(array); + } + return result; +} + +CF_EXPORT CFArrayRef CFBundleCopyResourceURLsOfType(CFBundleRef bundle, CFStringRef resourceType, CFStringRef subDirName) { + CFArrayRef languages = _CFBundleGetLanguageSearchList(bundle), types = NULL, array; + if (resourceType) types = CFArrayCreate(CFGetAllocator(bundle), (const void **)&resourceType, 1, &kCFTypeArrayCallBacks); + + // MF:!!! Better "limit" than 1,000,000? + array = _CFFindBundleResources(bundle, NULL, subDirName, languages, NULL, types, 1000000, _CFBundleLayoutVersion(bundle)); + + if (types) CFRelease(types); + + return array; +} + +CF_EXPORT CFURLRef _CFBundleCopyResourceURLForLanguage(CFBundleRef bundle, CFStringRef resourceName, CFStringRef resourceType, CFStringRef subDirName, CFStringRef language) {return CFBundleCopyResourceURLForLocalization(bundle, resourceName, resourceType, subDirName, language);} + +CF_EXPORT CFURLRef CFBundleCopyResourceURLForLocalization(CFBundleRef bundle, CFStringRef resourceName, CFStringRef resourceType, CFStringRef subDirName, CFStringRef localizationName) { + CFURLRef result = NULL; + CFArrayRef languages = NULL, types = NULL, array; + + if (localizationName) languages = CFArrayCreate(CFGetAllocator(bundle), (const void **)&localizationName, 1, &kCFTypeArrayCallBacks); + if (resourceType) types = CFArrayCreate(CFGetAllocator(bundle), (const void **)&resourceType, 1, &kCFTypeArrayCallBacks); + + array = _CFFindBundleResources(bundle, NULL, subDirName, languages, resourceName, types, 1, _CFBundleLayoutVersion(bundle)); + if (array) { + if (CFArrayGetCount(array) > 0) result = (CFURLRef)CFRetain(CFArrayGetValueAtIndex(array, 0)); + CFRelease(array); + } + + if (types) CFRelease(types); + if (languages) CFRelease(languages); + + return result; +} + +CF_EXPORT CFArrayRef _CFBundleCopyResourceURLsOfTypeForLanguage(CFBundleRef bundle, CFStringRef resourceType, CFStringRef subDirName, CFStringRef language) {return CFBundleCopyResourceURLsOfTypeForLocalization(bundle, resourceType, subDirName, language);} + +CF_EXPORT CFArrayRef CFBundleCopyResourceURLsOfTypeForLocalization(CFBundleRef bundle, CFStringRef resourceType, CFStringRef subDirName, CFStringRef localizationName) { + CFArrayRef languages = NULL, types = NULL, array; + + if (localizationName) languages = CFArrayCreate(CFGetAllocator(bundle), (const void **)&localizationName, 1, &kCFTypeArrayCallBacks); + if (resourceType) types = CFArrayCreate(CFGetAllocator(bundle), (const void **)&resourceType, 1, &kCFTypeArrayCallBacks); + + // MF:!!! Better "limit" than 1,000,000? + array = _CFFindBundleResources(bundle, NULL, subDirName, languages, NULL, types, 1000000, _CFBundleLayoutVersion(bundle)); + + if (types) CFRelease(types); + if (languages) CFRelease(languages); + + return array; +} + +CF_EXPORT CFStringRef CFBundleCopyLocalizedString(CFBundleRef bundle, CFStringRef key, CFStringRef value, CFStringRef tableName) { + CFStringRef result = NULL; + CFDictionaryRef stringTable = NULL; + + if (!key) return (value ? (CFStringRef)CFRetain(value) : (CFStringRef)CFRetain(CFSTR(""))); + + if (!tableName || CFEqual(tableName, CFSTR(""))) tableName = _CFBundleDefaultStringTableName; + if (__CFBundleGetResourceData(bundle)->_stringTableCache) stringTable = (CFDictionaryRef)CFDictionaryGetValue(__CFBundleGetResourceData(bundle)->_stringTableCache, tableName); + if (!stringTable) { + // Go load the table. + CFURLRef tableURL = CFBundleCopyResourceURL(bundle, tableName, _CFBundleStringTableType, NULL); + if (tableURL) { + CFStringRef nameForSharing = NULL; + if (!stringTable) { + CFDataRef tableData = NULL; + SInt32 errCode; + CFStringRef errStr; + if (CFURLCreateDataAndPropertiesFromResource(CFGetAllocator(bundle), tableURL, &tableData, NULL, NULL, &errCode)) { + stringTable = (CFDictionaryRef)CFPropertyListCreateFromXMLData(CFGetAllocator(bundle), tableData, kCFPropertyListImmutable, &errStr); + if (errStr) { + CFRelease(errStr); + errStr = NULL; + } + if (stringTable && CFDictionaryGetTypeID() != CFGetTypeID(stringTable)) { + CFRelease(stringTable); + stringTable = NULL; + } + CFRelease(tableData); + } + } + if (nameForSharing) CFRelease(nameForSharing); + CFRelease(tableURL); + } + if (!stringTable) stringTable = CFDictionaryCreate(CFGetAllocator(bundle), NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (!__CFBundleGetResourceData(bundle)->_stringTableCache) __CFBundleGetResourceData(bundle)->_stringTableCache = CFDictionaryCreateMutable(CFGetAllocator(bundle), 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionarySetValue(__CFBundleGetResourceData(bundle)->_stringTableCache, tableName, stringTable); + CFRelease(stringTable); + } + + result = (CFStringRef)CFDictionaryGetValue(stringTable, key); + if (!result) { + static int capitalize = -1; + if (!value) { + result = (CFStringRef)CFRetain(key); + } else if (CFEqual(value, CFSTR(""))) { + result = (CFStringRef)CFRetain(key); + } else { + result = (CFStringRef)CFRetain(value); + } + if (capitalize != 0) { + if (capitalize != 0) { + CFMutableStringRef capitalizedResult = CFStringCreateMutableCopy(CFGetAllocator(bundle), 0, result); + CFLog(__kCFLogBundle, CFSTR("Localizable string \"%@\" not found in strings table \"%@\" of bundle %@."), key, tableName, bundle); + CFStringUppercase(capitalizedResult, NULL); + CFRelease(result); + result = capitalizedResult; + } + } + } else { + CFRetain(result); + } + if (CFStringHasSuffix(tableName, CFSTR(".nocache")) && __CFBundleGetResourceData(bundle)->_stringTableCache && _CFExecutableLinkedOnOrAfter(CFSystemVersionLeopard)) CFDictionaryRemoveValue(__CFBundleGetResourceData(bundle)->_stringTableCache, tableName); + + return result; +} + +CF_EXPORT CFURLRef CFBundleCopyResourceURLInDirectory(CFURLRef bundleURL, CFStringRef resourceName, CFStringRef resourceType, CFStringRef subDirName) { + CFURLRef result = NULL; + unsigned char buff[CFMaxPathSize]; + CFURLRef newURL = NULL; + + if (!CFURLGetFileSystemRepresentation(bundleURL, true, buff, CFMaxPathSize)) return NULL; + + newURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, buff, (CFIndex)strlen((char *)buff), true); + if (!newURL) newURL = (CFURLRef)CFRetain(bundleURL); + if (_CFBundleCouldBeBundle(newURL)) { + uint8_t version = 0; + CFArrayRef languages = _CFBundleCopyLanguageSearchListInDirectory(kCFAllocatorSystemDefault, newURL, &version), types = NULL, array; + if (resourceType) types = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&resourceType, 1, &kCFTypeArrayCallBacks); + + array = _CFFindBundleResources(NULL, newURL, subDirName, languages, resourceName, types, 1, version); + + if (types) CFRelease(types); + if (languages) CFRelease(languages); + + if (array) { + if (CFArrayGetCount(array) > 0) result = (CFURLRef)CFRetain(CFArrayGetValueAtIndex(array, 0)); + CFRelease(array); + } + } + if (newURL) CFRelease(newURL); + return result; +} + +CF_EXPORT CFArrayRef CFBundleCopyResourceURLsOfTypeInDirectory(CFURLRef bundleURL, CFStringRef resourceType, CFStringRef subDirName) { + CFArrayRef array = NULL; + unsigned char buff[CFMaxPathSize]; + CFURLRef newURL = NULL; + + if (!CFURLGetFileSystemRepresentation(bundleURL, true, buff, CFMaxPathSize)) return NULL; + + newURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, buff, (CFIndex)strlen((char *)buff), true); + if (!newURL) newURL = (CFURLRef)CFRetain(bundleURL); + if (_CFBundleCouldBeBundle(newURL)) { + uint8_t version = 0; + CFArrayRef languages = _CFBundleCopyLanguageSearchListInDirectory(kCFAllocatorSystemDefault, newURL, &version), types = NULL; + if (resourceType) types = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&resourceType, 1, &kCFTypeArrayCallBacks); + + // MF:!!! Better "limit" than 1,000,000? + array = _CFFindBundleResources(NULL, newURL, subDirName, languages, NULL, types, 1000000, version); + + if (types) CFRelease(types); + if (languages) CFRelease(languages); + } + if (newURL) CFRelease(newURL); + return array; +} + +// string, with groups of 6 characters being 1 element in the array of locale abbreviations +const char * __CFBundleLocaleAbbreviationsArray = + "en_US\0" "fr_FR\0" "en_GB\0" "de_DE\0" "it_IT\0" "nl_NL\0" "nl_BE\0" "sv_SE\0" + "es_ES\0" "da_DK\0" "pt_PT\0" "fr_CA\0" "nb_NO\0" "he_IL\0" "ja_JP\0" "en_AU\0" + "ar\0\0\0\0" "fi_FI\0" "fr_CH\0" "de_CH\0" "el_GR\0" "is_IS\0" "mt_MT\0" "el_CY\0" + "tr_TR\0" "hr_HR\0" "nl_NL\0" "nl_BE\0" "en_CA\0" "en_CA\0" "pt_PT\0" "nb_NO\0" + "da_DK\0" "hi_IN\0" "ur_PK\0" "tr_TR\0" "it_CH\0" "en\0\0\0\0" "\0\0\0\0\0\0" "ro_RO\0" + "grc\0\0\0" "lt_LT\0" "pl_PL\0" "hu_HU\0" "et_EE\0" "lv_LV\0" "se\0\0\0\0" "fo_FO\0" + "fa_IR\0" "ru_RU\0" "ga_IE\0" "ko_KR\0" "zh_CN\0" "zh_TW\0" "th_TH\0" "\0\0\0\0\0\0" + "cs_CZ\0" "sk_SK\0" "\0\0\0\0\0\0" "hu_HU\0" "bn\0\0\0\0" "be_BY\0" "uk_UA\0" "\0\0\0\0\0\0" + "el_GR\0" "sr_CS\0" "sl_SI\0" "mk_MK\0" "hr_HR\0" "\0\0\0\0\0\0" "de_DE\0" "pt_BR\0" + "bg_BG\0" "ca_ES\0" "\0\0\0\0\0\0" "gd\0\0\0\0" "gv\0\0\0\0" "br\0\0\0\0" "iu_CA\0" "cy\0\0\0\0" + "en_CA\0" "ga_IE\0" "en_CA\0" "dz_BT\0" "hy_AM\0" "ka_GE\0" "es_XL\0" "es_ES\0" + "to_TO\0" "pl_PL\0" "ca_ES\0" "fr\0\0\0\0" "de_AT\0" "es_XL\0" "gu_IN\0" "pa\0\0\0\0" + "ur_IN\0" "vi_VN\0" "fr_BE\0" "uz_UZ\0" "en_SG\0" "nn_NO\0" "af_ZA\0" "eo\0\0\0\0" + "mr_IN\0" "bo\0\0\0\0" "ne_NP\0" "kl\0\0\0\0" "en_IE\0"; + +#define NUM_LOCALE_ABBREVIATIONS 109 +#define LOCALE_ABBREVIATION_LENGTH 6 + +static const char * const __CFBundleLanguageNamesArray[] = { + "English", "French", "German", "Italian", "Dutch", "Swedish", "Spanish", "Danish", + "Portuguese", "Norwegian", "Hebrew", "Japanese", "Arabic", "Finnish", "Greek", "Icelandic", + "Maltese", "Turkish", "Croatian", "Chinese", "Urdu", "Hindi", "Thai", "Korean", + "Lithuanian", "Polish", "Hungarian", "Estonian", "Latvian", "Sami", "Faroese", "Farsi", + "Russian", "Chinese", "Dutch", "Irish", "Albanian", "Romanian", "Czech", "Slovak", + "Slovenian", "Yiddish", "Serbian", "Macedonian", "Bulgarian", "Ukrainian", "Byelorussian", "Uzbek", + "Kazakh", "Azerbaijani", "Azerbaijani", "Armenian", "Georgian", "Moldavian", "Kirghiz", "Tajiki", + "Turkmen", "Mongolian", "Mongolian", "Pashto", "Kurdish", "Kashmiri", "Sindhi", "Tibetan", + "Nepali", "Sanskrit", "Marathi", "Bengali", "Assamese", "Gujarati", "Punjabi", "Oriya", + "Malayalam", "Kannada", "Tamil", "Telugu", "Sinhalese", "Burmese", "Khmer", "Lao", + "Vietnamese", "Indonesian", "Tagalog", "Malay", "Malay", "Amharic", "Tigrinya", "Oromo", + "Somali", "Swahili", "Kinyarwanda", "Rundi", "Nyanja", "Malagasy", "Esperanto", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "Welsh", "Basque", "Catalan", "Latin", "Quechua", "Guarani", "Aymara", "Tatar", + "Uighur", "Dzongkha", "Javanese", "Sundanese", "Galician", "Afrikaans", "Breton", "Inuktitut", + "Scottish", "Manx", "Irish", "Tongan", "Greek", "Greenlandic", "Azerbaijani", "Nynorsk" +}; + +#define NUM_LANGUAGE_NAMES 152 +#define LANGUAGE_NAME_LENGTH 13 + +// string, with groups of 3 characters being 1 element in the array of abbreviations +const char * __CFBundleLanguageAbbreviationsArray = + "en\0" "fr\0" "de\0" "it\0" "nl\0" "sv\0" "es\0" "da\0" + "pt\0" "nb\0" "he\0" "ja\0" "ar\0" "fi\0" "el\0" "is\0" + "mt\0" "tr\0" "hr\0" "zh\0" "ur\0" "hi\0" "th\0" "ko\0" + "lt\0" "pl\0" "hu\0" "et\0" "lv\0" "se\0" "fo\0" "fa\0" + "ru\0" "zh\0" "nl\0" "ga\0" "sq\0" "ro\0" "cs\0" "sk\0" + "sl\0" "yi\0" "sr\0" "mk\0" "bg\0" "uk\0" "be\0" "uz\0" + "kk\0" "az\0" "az\0" "hy\0" "ka\0" "mo\0" "ky\0" "tg\0" + "tk\0" "mn\0" "mn\0" "ps\0" "ku\0" "ks\0" "sd\0" "bo\0" + "ne\0" "sa\0" "mr\0" "bn\0" "as\0" "gu\0" "pa\0" "or\0" + "ml\0" "kn\0" "ta\0" "te\0" "si\0" "my\0" "km\0" "lo\0" + "vi\0" "id\0" "tl\0" "ms\0" "ms\0" "am\0" "ti\0" "om\0" + "so\0" "sw\0" "rw\0" "rn\0" "\0\0\0" "mg\0" "eo\0" "\0\0\0" + "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" + "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" + "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" + "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" + "cy\0" "eu\0" "ca\0" "la\0" "qu\0" "gn\0" "ay\0" "tt\0" + "ug\0" "dz\0" "jv\0" "su\0" "gl\0" "af\0" "br\0" "iu\0" + "gd\0" "gv\0" "ga\0" "to\0" "el\0" "kl\0" "az\0" "nn\0"; + +#define NUM_LANGUAGE_ABBREVIATIONS 152 +#define LANGUAGE_ABBREVIATION_LENGTH 3 + +#if defined(__CONSTANT_CFSTRINGS__) + +// These are not necessarily common localizations per se, but localizations for which the full language name is still in common use. +// These are used to provide a fast path for it (other localizations usually use the abbreviation, which is even faster). +static CFStringRef const __CFBundleCommonLanguageNamesArray[] = {CFSTR("English"), CFSTR("French"), CFSTR("German"), CFSTR("Italian"), CFSTR("Dutch"), CFSTR("Spanish"), CFSTR("Japanese")}; +static CFStringRef const __CFBundleCommonLanguageAbbreviationsArray[] = {CFSTR("en"), CFSTR("fr"), CFSTR("de"), CFSTR("it"), CFSTR("nl"), CFSTR("es"), CFSTR("ja")}; + +#define NUM_COMMON_LANGUAGE_NAMES 7 + +#endif /* __CONSTANT_CFSTRINGS__ */ + +static const SInt32 __CFBundleScriptCodesArray[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 1, 4, 0, 6, 0, + 0, 0, 0, 2, 4, 9, 21, 3, 29, 29, 29, 29, 29, 0, 0, 4, + 7, 25, 0, 0, 0, 0, 29, 29, 0, 5, 7, 7, 7, 7, 7, 7, + 7, 7, 4, 24, 23, 7, 7, 7, 7, 27, 7, 4, 4, 4, 4, 26, + 9, 9, 9, 13, 13, 11, 10, 12, 17, 16, 14, 15, 18, 19, 20, 22, + 30, 0, 0, 0, 4, 28, 28, 28, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 7, 4, 26, 0, 0, 0, 0, 0, 28, + 0, 0, 0, 0, 6, 0, 0, 0 +}; + +static const CFStringEncoding __CFBundleStringEncodingsArray[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 1, 4, 0, 6, 37, + 0, 35, 36, 2, 4, 9, 21, 3, 29, 29, 29, 29, 29, 0, 37, 0x8C, + 7, 25, 0, 39, 0, 38, 29, 29, 36, 5, 7, 7, 7, 0x98, 7, 7, + 7, 7, 4, 24, 23, 7, 7, 7, 7, 27, 7, 4, 4, 4, 4, 26, + 9, 9, 9, 13, 13, 11, 10, 12, 17, 16, 14, 15, 18, 19, 20, 22, + 30, 0, 0, 0, 4, 28, 28, 28, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 39, 0, 0, 0, 0, 0, 0, 7, 4, 26, 0, 0, 0, 0, 39, 0xEC, + 39, 39, 40, 0, 6, 0, 0, 0 +}; + +static SInt32 _CFBundleGetLanguageCodeForLocalization(CFStringRef localizationName) { + SInt32 result = -1, i; + char buff[256]; + CFIndex length = CFStringGetLength(localizationName); + if (length >= LANGUAGE_ABBREVIATION_LENGTH - 1 && length <= 255 && CFStringGetCString(localizationName, buff, 255, kCFStringEncodingASCII)) { + buff[255] = '\0'; + for (i = 0; -1 == result && i < NUM_LANGUAGE_NAMES; i++) { + if (0 == strcmp(buff, __CFBundleLanguageNamesArray[i])) result = i; + } + if (0 == strcmp(buff, "zh_TW") || 0 == strcmp(buff, "zh-Hant")) result = 19; else if (0 == strcmp(buff, "zh_CN") || 0 == strcmp(buff, "zh-Hans")) result = 33; // hack for mixed-up Chinese language codes + if (-1 == result && (length == LANGUAGE_ABBREVIATION_LENGTH - 1 || !isalpha(buff[LANGUAGE_ABBREVIATION_LENGTH - 1]))) { + buff[LANGUAGE_ABBREVIATION_LENGTH - 1] = '\0'; + if ('n' == buff[0] && 'o' == buff[1]) result = 9; // hack for Norwegian + for (i = 0; -1 == result && i < NUM_LANGUAGE_ABBREVIATIONS * LANGUAGE_ABBREVIATION_LENGTH; i += LANGUAGE_ABBREVIATION_LENGTH) { + if (buff[0] == *(__CFBundleLanguageAbbreviationsArray + i + 0) && buff[1] == *(__CFBundleLanguageAbbreviationsArray + i + 1)) result = i / LANGUAGE_ABBREVIATION_LENGTH; + } + } + } + return result; +} + +static CFStringRef _CFBundleCopyLanguageAbbreviationForLanguageCode(SInt32 languageCode) { + CFStringRef result = NULL; + if (0 <= languageCode && languageCode < NUM_LANGUAGE_ABBREVIATIONS) { + const char *languageAbbreviation = __CFBundleLanguageAbbreviationsArray + languageCode * LANGUAGE_ABBREVIATION_LENGTH; + if (languageAbbreviation && *languageAbbreviation != '\0') result = CFStringCreateWithCStringNoCopy(kCFAllocatorSystemDefault, languageAbbreviation, kCFStringEncodingASCII, kCFAllocatorNull); + } + return result; +} + +CF_INLINE CFStringRef _CFBundleCopyLanguageNameForLanguageCode(SInt32 languageCode) { + CFStringRef result = NULL; + if (0 <= languageCode && languageCode < NUM_LANGUAGE_NAMES) { + const char *languageName = __CFBundleLanguageNamesArray[languageCode]; + if (languageName && *languageName != '\0') result = CFStringCreateWithCStringNoCopy(kCFAllocatorSystemDefault, languageName, kCFStringEncodingASCII, kCFAllocatorNull); + } + return result; +} + +CF_INLINE CFStringRef _CFBundleCopyLanguageAbbreviationForLocalization(CFStringRef localizationName) { + CFStringRef result = NULL; + SInt32 languageCode = _CFBundleGetLanguageCodeForLocalization(localizationName); + if (languageCode >= 0) { + result = _CFBundleCopyLanguageAbbreviationForLanguageCode(languageCode); + } else { + CFIndex length = CFStringGetLength(localizationName); + if (length == LANGUAGE_ABBREVIATION_LENGTH - 1 || (length > LANGUAGE_ABBREVIATION_LENGTH - 1 && CFStringGetCharacterAtIndex(localizationName, LANGUAGE_ABBREVIATION_LENGTH - 1) == '_')) { + result = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, localizationName, CFRangeMake(0, LANGUAGE_ABBREVIATION_LENGTH - 1)); + } + } + return result; +} + +CF_INLINE CFStringRef _CFBundleCopyModifiedLocalization(CFStringRef localizationName) { + CFMutableStringRef result = NULL; + CFIndex length = CFStringGetLength(localizationName); + if (length >= 4) { + UniChar c = CFStringGetCharacterAtIndex(localizationName, 2); + if ('-' == c || '_' == c) { + result = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, length, localizationName); + CFStringReplace(result, CFRangeMake(2, 1), ('-' == c) ? CFSTR("_") : CFSTR("-")); + } + } + return result; +} + +CF_INLINE CFStringRef _CFBundleCopyLanguageNameForLocalization(CFStringRef localizationName) { + CFStringRef result = NULL; + SInt32 languageCode = _CFBundleGetLanguageCodeForLocalization(localizationName); + if (languageCode >= 0) { + result = _CFBundleCopyLanguageNameForLanguageCode(languageCode); + } else { + result = (CFStringRef)CFStringCreateCopy(kCFAllocatorSystemDefault, localizationName); + } + return result; +} + +static SInt32 _CFBundleGetLanguageCodeForRegionCode(SInt32 regionCode) { + SInt32 result = -1, i; + if (52 == regionCode) { // hack for mixed-up Chinese language codes + result = 33; + } else if (0 <= regionCode && regionCode < NUM_LOCALE_ABBREVIATIONS) { + const char *localeAbbreviation = __CFBundleLocaleAbbreviationsArray + regionCode * LOCALE_ABBREVIATION_LENGTH; + if (localeAbbreviation && *localeAbbreviation != '\0') { + for (i = 0; -1 == result && i < NUM_LANGUAGE_ABBREVIATIONS * LANGUAGE_ABBREVIATION_LENGTH; i += LANGUAGE_ABBREVIATION_LENGTH) { + if (localeAbbreviation[0] == *(__CFBundleLanguageAbbreviationsArray + i + 0) && localeAbbreviation[1] == *(__CFBundleLanguageAbbreviationsArray + i + 1)) result = i / LANGUAGE_ABBREVIATION_LENGTH; + } + } + } + return result; +} + +static SInt32 _CFBundleGetRegionCodeForLanguageCode(SInt32 languageCode) { + SInt32 result = -1, i; + if (19 == languageCode) { // hack for mixed-up Chinese language codes + result = 53; + } else if (0 <= languageCode && languageCode < NUM_LANGUAGE_ABBREVIATIONS) { + const char *languageAbbreviation = __CFBundleLanguageAbbreviationsArray + languageCode * LANGUAGE_ABBREVIATION_LENGTH; + if (languageAbbreviation && *languageAbbreviation != '\0') { + for (i = 0; -1 == result && i < NUM_LOCALE_ABBREVIATIONS * LOCALE_ABBREVIATION_LENGTH; i += LOCALE_ABBREVIATION_LENGTH) { + if (*(__CFBundleLocaleAbbreviationsArray + i + 0) == languageAbbreviation[0] && *(__CFBundleLocaleAbbreviationsArray + i + 1) == languageAbbreviation[1]) result = i / LOCALE_ABBREVIATION_LENGTH; + } + } + } + if (25 == result) result = 68; + if (28 == result) result = 82; + return result; +} + +static SInt32 _CFBundleGetRegionCodeForLocalization(CFStringRef localizationName) { + SInt32 result = -1, i; + char buff[LOCALE_ABBREVIATION_LENGTH]; + CFIndex length = CFStringGetLength(localizationName); + if ((length >= LANGUAGE_ABBREVIATION_LENGTH - 1) && (length <= LOCALE_ABBREVIATION_LENGTH - 1) && CFStringGetCString(localizationName, buff, LOCALE_ABBREVIATION_LENGTH, kCFStringEncodingASCII)) { + buff[LOCALE_ABBREVIATION_LENGTH - 1] = '\0'; + for (i = 0; -1 == result && i < NUM_LOCALE_ABBREVIATIONS * LOCALE_ABBREVIATION_LENGTH; i += LOCALE_ABBREVIATION_LENGTH) { + if (0 == strcmp(buff, __CFBundleLocaleAbbreviationsArray + i)) result = i / LOCALE_ABBREVIATION_LENGTH; + } + } + if (25 == result) result = 68; + if (28 == result) result = 82; + if (37 == result) result = 0; + if (-1 == result) { + SInt32 languageCode = _CFBundleGetLanguageCodeForLocalization(localizationName); + result = _CFBundleGetRegionCodeForLanguageCode(languageCode); + } + return result; +} + +static CFStringRef _CFBundleCopyLocaleAbbreviationForRegionCode(SInt32 regionCode) { + CFStringRef result = NULL; + if (0 <= regionCode && regionCode < NUM_LOCALE_ABBREVIATIONS) { + const char *localeAbbreviation = __CFBundleLocaleAbbreviationsArray + regionCode * LOCALE_ABBREVIATION_LENGTH; + if (localeAbbreviation && *localeAbbreviation != '\0') { + result = CFStringCreateWithCStringNoCopy(kCFAllocatorSystemDefault, localeAbbreviation, kCFStringEncodingASCII, kCFAllocatorNull); + } + } + return result; +} + +Boolean CFBundleGetLocalizationInfoForLocalization(CFStringRef localizationName, SInt32 *languageCode, SInt32 *regionCode, SInt32 *scriptCode, CFStringEncoding *stringEncoding) { + Boolean retval = false; + SInt32 language = -1, region = -1, script = 0; + CFStringEncoding encoding = kCFStringEncodingMacRoman; + if (!localizationName) { + CFBundleRef mainBundle = CFBundleGetMainBundle(); + CFArrayRef languages = NULL; + if (mainBundle) { + languages = _CFBundleGetLanguageSearchList(mainBundle); + if (languages) CFRetain(languages); + } + if (!languages) languages = _CFBundleCopyUserLanguages(false); + if (languages && CFArrayGetCount(languages) > 0) localizationName = (CFStringRef)CFArrayGetValueAtIndex(languages, 0); + } + if (!retval) { + if (localizationName) { + language = _CFBundleGetLanguageCodeForLocalization(localizationName); + region = _CFBundleGetRegionCodeForLocalization(localizationName); + } else { + _CFBundleGetLanguageAndRegionCodes(&language, ®ion); + } + if ((language < 0 || language > (int)(sizeof(__CFBundleScriptCodesArray)/sizeof(SInt32))) && region != -1) language = _CFBundleGetLanguageCodeForRegionCode(region); + if (region == -1 && language != -1) region = _CFBundleGetRegionCodeForLanguageCode(language); + if (language >= 0 && language < (int)(sizeof(__CFBundleScriptCodesArray)/sizeof(SInt32))) { + script = __CFBundleScriptCodesArray[language]; + } + if (language >= 0 && language < (int)(sizeof(__CFBundleStringEncodingsArray)/sizeof(CFStringEncoding))) { + encoding = __CFBundleStringEncodingsArray[language]; + } + retval = (language != -1 || region != -1); + } + if (languageCode) *languageCode = language; + if (regionCode) *regionCode = region; + if (scriptCode) *scriptCode = script; + if (stringEncoding) *stringEncoding = encoding; + return retval; +} + +CFStringRef CFBundleCopyLocalizationForLocalizationInfo(SInt32 languageCode, SInt32 regionCode, SInt32 scriptCode, CFStringEncoding stringEncoding) { + CFStringRef localizationName = NULL; + if (!localizationName) { + localizationName = _CFBundleCopyLocaleAbbreviationForRegionCode(regionCode); + } + if (!localizationName && 0 <= languageCode && languageCode < SHRT_MAX) { + localizationName = CFLocaleCreateCanonicalLocaleIdentifierFromScriptManagerCodes(kCFAllocatorSystemDefault, (LangCode)languageCode, (RegionCode)-1); + } + if (!localizationName) { + localizationName = _CFBundleCopyLanguageAbbreviationForLanguageCode(languageCode); + } + if (!localizationName) { + SInt32 language = -1, scriptLanguage = -1, encodingLanguage = -1; + unsigned int i; + for (i = 0; language == -1 && i < (sizeof(__CFBundleScriptCodesArray)/sizeof(SInt32)); i++) { + if (__CFBundleScriptCodesArray[i] == scriptCode && __CFBundleStringEncodingsArray[i] == stringEncoding) language = i; + } + for (i = 0; scriptLanguage == -1 && i < (sizeof(__CFBundleScriptCodesArray)/sizeof(SInt32)); i++) { + if (__CFBundleScriptCodesArray[i] == scriptCode) scriptLanguage = i; + } + for (i = 0; encodingLanguage == -1 && i < (sizeof(__CFBundleStringEncodingsArray)/sizeof(CFStringEncoding)); i++) { + if (__CFBundleStringEncodingsArray[i] == stringEncoding) encodingLanguage = i; + } + localizationName = _CFBundleCopyLanguageAbbreviationForLanguageCode(language); + if (!localizationName) localizationName = _CFBundleCopyLanguageAbbreviationForLanguageCode(encodingLanguage); + if (!localizationName) localizationName = _CFBundleCopyLanguageAbbreviationForLanguageCode(scriptLanguage); + } + return localizationName; +} + +extern void *__CFAppleLanguages; + +__private_extern__ CFArrayRef _CFBundleCopyUserLanguages(Boolean useBackstops) { + CFArrayRef result = NULL; + static CFArrayRef userLanguages = NULL; + static Boolean didit = false; + CFArrayRef preferencesArray = CFLocaleCopyPreferredLanguages(); + // This is a temporary solution, until the argument domain is moved down into CFPreferences + __CFSpinLock(&CFBundleResourceGlobalDataLock); + if (!didit) { + if (__CFAppleLanguages) { + CFDataRef data; + CFIndex length = (CFIndex)strlen((const char *)__CFAppleLanguages); + if (length > 0) { + data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (const UInt8 *)__CFAppleLanguages, length, kCFAllocatorNull); + if (data) { + userLanguages = (CFArrayRef)CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, data, kCFPropertyListImmutable, NULL); + CFRelease(data); + } + } + } + if (!userLanguages && preferencesArray) userLanguages = (CFArrayRef)CFRetain(preferencesArray); + Boolean useEnglishAsBackstop = true; + // could perhaps read out of LANG environment variable + if (useEnglishAsBackstop && !userLanguages) { + CFStringRef english = CFSTR("en"); + userLanguages = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&english, 1, &kCFTypeArrayCallBacks); + } + if (userLanguages && CFGetTypeID(userLanguages) != CFArrayGetTypeID()) { + CFRelease(userLanguages); + userLanguages = NULL; + } + didit = true; + } + __CFSpinUnlock(&CFBundleResourceGlobalDataLock); + if (preferencesArray) CFRelease(preferencesArray); + if (!result && userLanguages) result = (CFArrayRef)CFRetain(userLanguages); + return result; +} + +CF_EXPORT void _CFBundleGetLanguageAndRegionCodes(SInt32 *languageCode, SInt32 *regionCode) { + // an attempt to answer the question, "what language are we running in?" + // note that the question cannot be answered fully since it may depend on the bundle + SInt32 language = -1, region = -1; + CFBundleRef mainBundle = CFBundleGetMainBundle(); + CFArrayRef languages = NULL; + if (mainBundle) { + languages = _CFBundleGetLanguageSearchList(mainBundle); + if (languages) CFRetain(languages); + } + if (!languages) languages = _CFBundleCopyUserLanguages(false); + if (languages && CFArrayGetCount(languages) > 0) { + CFStringRef localizationName = (CFStringRef)CFArrayGetValueAtIndex(languages, 0); + Boolean retval = false; + LangCode langCode = -1; + RegionCode regCode = -1; + if (!retval) { + language = _CFBundleGetLanguageCodeForLocalization(localizationName); + region = _CFBundleGetRegionCodeForLocalization(localizationName); + } + } else { + language = 0; + region = 0; + } + if (language == -1 && region != -1) language = _CFBundleGetLanguageCodeForRegionCode(region); + if (region == -1 && language != -1) region = _CFBundleGetRegionCodeForLanguageCode(language); + if (languages) CFRelease(languages); + if (languageCode) *languageCode = language; + if (regionCode) *regionCode = region; +} + + +static Boolean _CFBundleTryOnePreferredLprojNameInDirectory(CFAllocatorRef alloc, UniChar *pathUniChars, CFIndex pathLen, uint8_t version, CFDictionaryRef infoDict, CFStringRef curLangStr, CFMutableArrayRef lprojNames) { + CFIndex curLangLen = CFStringGetLength(curLangStr), savedPathLen; + UniChar curLangUniChars[255]; + CFStringRef altLangStr = NULL, modifiedLangStr = NULL, languageAbbreviation = NULL, languageName = NULL, canonicalLanguageIdentifier = NULL; + CFMutableDictionaryRef canonicalLanguageIdentifiers = NULL, predefinedCanonicalLanguageIdentifiers = NULL; + Boolean foundOne = false; + CFArrayRef predefinedLocalizations = NULL; + CFRange predefinedLocalizationsRange; + CFMutableStringRef cheapStr, tmpString; +#if READ_DIRECTORIES + CFArrayRef contents; + CFRange contentsRange; +#else /* READ_DIRECTORIES */ + Boolean isDir = false; +#endif /* READ_DIRECTORIES */ + + // both of these used for temp string operations, for slightly + // different purposes, where each type is appropriate + cheapStr = CFStringCreateMutable(alloc, 0); + _CFStrSetDesiredCapacity(cheapStr, CFMaxPathSize); + tmpString = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorSystemDefault, NULL, 0, 0, kCFAllocatorNull); + +#if READ_DIRECTORIES + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); + CFStringReplaceAll(cheapStr, tmpString); + contents = _CFBundleCopyDirectoryContentsAtPath(cheapStr, _CFBundleAllContents); + contentsRange = CFRangeMake(0, CFArrayGetCount(contents)); +#endif /* READ_DIRECTORIES */ + + if (infoDict) { + predefinedLocalizations = (CFArrayRef)CFDictionaryGetValue(infoDict, kCFBundleLocalizationsKey); + if (predefinedLocalizations && CFGetTypeID(predefinedLocalizations) != CFArrayGetTypeID()) { + predefinedLocalizations = NULL; + CFDictionaryRemoveValue((CFMutableDictionaryRef)infoDict, kCFBundleLocalizationsKey); + } + } + predefinedLocalizationsRange = CFRangeMake(0, predefinedLocalizations ? CFArrayGetCount(predefinedLocalizations) : 0); + + if (curLangLen > 255) curLangLen = 255; + CFStringGetCharacters(curLangStr, CFRangeMake(0, curLangLen), curLangUniChars); + savedPathLen = pathLen; + if (_CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, curLangUniChars, curLangLen) && _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, _LprojUniChars, _LprojLen)) { +#if READ_DIRECTORIES + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1); + CFStringReplaceAll(cheapStr, tmpString); + if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, curLangStr)) || (version != 4 && CFArrayContainsValue(contents, contentsRange, cheapStr))) { +#else /* READ_DIRECTORIES */ + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); + CFStringReplaceAll(cheapStr, tmpString); + if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, curLangStr)) || (version != 4 && _CFIsResourceAtPath(cheapStr, &isDir) && isDir)) { +#endif /* READ_DIRECTORIES */ + if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), curLangStr)) CFArrayAppendValue(lprojNames, curLangStr); + foundOne = true; + if (CFStringGetLength(curLangStr) <= 2) { + CFRelease(cheapStr); + CFRelease(tmpString); +#if READ_DIRECTORIES + CFRelease(contents); +#endif /* READ_DIRECTORIES */ + return foundOne; + } + } + } +#if defined(__CONSTANT_CFSTRINGS__) + if (!altLangStr) { + CFIndex idx; + for (idx = 0; !altLangStr && idx < NUM_COMMON_LANGUAGE_NAMES; idx++) { + if (CFEqual(curLangStr, __CFBundleCommonLanguageAbbreviationsArray[idx])) altLangStr = __CFBundleCommonLanguageNamesArray[idx]; + else if (CFEqual(curLangStr, __CFBundleCommonLanguageNamesArray[idx])) altLangStr = __CFBundleCommonLanguageAbbreviationsArray[idx]; + } + } +#endif /* __CONSTANT_CFSTRINGS__ */ + if (foundOne && altLangStr) { + CFRelease(cheapStr); + CFRelease(tmpString); +#if READ_DIRECTORIES + CFRelease(contents); +#endif /* READ_DIRECTORIES */ + return foundOne; + } + if (altLangStr) { + curLangLen = CFStringGetLength(altLangStr); + if (curLangLen > 255) curLangLen = 255; + CFStringGetCharacters(altLangStr, CFRangeMake(0, curLangLen), curLangUniChars); + pathLen = savedPathLen; + if (_CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, curLangUniChars, curLangLen) && _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, _LprojUniChars, _LprojLen)) { +#if READ_DIRECTORIES + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1); + CFStringReplaceAll(cheapStr, tmpString); + if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, altLangStr)) || (version != 4 && CFArrayContainsValue(contents, contentsRange, cheapStr))) { +#else /* READ_DIRECTORIES */ + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); + CFStringReplaceAll(cheapStr, tmpString); + if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, altLangStr)) || (version != 4 && _CFIsResourceAtPath(cheapStr, &isDir) && isDir)) { +#endif /* READ_DIRECTORIES */ + if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), altLangStr)) CFArrayAppendValue(lprojNames, altLangStr); + foundOne = true; + CFRelease(cheapStr); + CFRelease(tmpString); +#if READ_DIRECTORIES + CFRelease(contents); +#endif /* READ_DIRECTORIES */ + return foundOne; + } + } + } +#if READ_DIRECTORIES + if (!foundOne && (!predefinedLocalizations || CFArrayGetCount(predefinedLocalizations) == 0)) { + Boolean hasLocalizations = false; + CFIndex idx; + for (idx = 0; !hasLocalizations && idx < contentsRange.length; idx++) { + CFStringRef name = (CFStringRef)CFArrayGetValueAtIndex(contents, idx); + if (CFStringHasSuffix(name, _CFBundleLprojExtensionWithDot)) hasLocalizations = true; + } + if (!hasLocalizations) { + CFRelease(cheapStr); + CFRelease(tmpString); + CFRelease(contents); + return foundOne; + } + } +#endif /* READ_DIRECTORIES */ + if (!altLangStr && (modifiedLangStr = _CFBundleCopyModifiedLocalization(curLangStr))) { + curLangLen = CFStringGetLength(modifiedLangStr); + if (curLangLen > 255) curLangLen = 255; + CFStringGetCharacters(modifiedLangStr, CFRangeMake(0, curLangLen), curLangUniChars); + pathLen = savedPathLen; + if (_CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, curLangUniChars, curLangLen) && _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, _LprojUniChars, _LprojLen)) { +#if READ_DIRECTORIES + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1); + CFStringReplaceAll(cheapStr, tmpString); + if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, modifiedLangStr)) || (version != 4 && CFArrayContainsValue(contents, contentsRange, cheapStr))) { +#else /* READ_DIRECTORIES */ + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); + CFStringReplaceAll(cheapStr, tmpString); + if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, modifiedLangStr)) || (version != 4 && _CFIsResourceAtPath(cheapStr, &isDir) && isDir)) { +#endif /* READ_DIRECTORIES */ + if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), modifiedLangStr)) CFArrayAppendValue(lprojNames, modifiedLangStr); + foundOne = true; + } + } + } + if (!altLangStr && (languageAbbreviation = _CFBundleCopyLanguageAbbreviationForLocalization(curLangStr)) && !CFEqual(curLangStr, languageAbbreviation)) { + curLangLen = CFStringGetLength(languageAbbreviation); + if (curLangLen > 255) curLangLen = 255; + CFStringGetCharacters(languageAbbreviation, CFRangeMake(0, curLangLen), curLangUniChars); + pathLen = savedPathLen; + if (_CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, curLangUniChars, curLangLen) && _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, _LprojUniChars, _LprojLen)) { +#if READ_DIRECTORIES + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1); + CFStringReplaceAll(cheapStr, tmpString); + if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, languageAbbreviation)) || (version != 4 && CFArrayContainsValue(contents, contentsRange, cheapStr))) { +#else /* READ_DIRECTORIES */ + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); + CFStringReplaceAll(cheapStr, tmpString); + if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, languageAbbreviation)) || (version != 4 && _CFIsResourceAtPath(cheapStr, &isDir) && isDir)) { +#endif /* READ_DIRECTORIES */ + if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), languageAbbreviation)) CFArrayAppendValue(lprojNames, languageAbbreviation); + foundOne = true; + } + } + } + if (!altLangStr && (languageName = _CFBundleCopyLanguageNameForLocalization(curLangStr)) && !CFEqual(curLangStr, languageName)) { + curLangLen = CFStringGetLength(languageName); + if (curLangLen > 255) curLangLen = 255; + CFStringGetCharacters(languageName, CFRangeMake(0, curLangLen), curLangUniChars); + pathLen = savedPathLen; + if (_CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, curLangUniChars, curLangLen) && _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, _LprojUniChars, _LprojLen)) { +#if READ_DIRECTORIES + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1); + CFStringReplaceAll(cheapStr, tmpString); + if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, languageName)) || (version != 4 && CFArrayContainsValue(contents, contentsRange, cheapStr))) { +#else /* READ_DIRECTORIES */ + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); + CFStringReplaceAll(cheapStr, tmpString); + if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, languageName)) || (version != 4 && _CFIsResourceAtPath(cheapStr, &isDir) && isDir)) { +#endif /* READ_DIRECTORIES */ + if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), languageName)) CFArrayAppendValue(lprojNames, languageName); + foundOne = true; + } + } + } + if (modifiedLangStr) CFRelease(modifiedLangStr); + if (languageAbbreviation) CFRelease(languageAbbreviation); + if (languageName) CFRelease(languageName); + if (canonicalLanguageIdentifier) CFRelease(canonicalLanguageIdentifier); + if (canonicalLanguageIdentifiers) CFRelease(canonicalLanguageIdentifiers); + if (predefinedCanonicalLanguageIdentifiers) CFRelease(predefinedCanonicalLanguageIdentifiers); + CFRelease(cheapStr); + CFRelease(tmpString); +#if READ_DIRECTORIES + CFRelease(contents); +#endif /* READ_DIRECTORIES */ + + return foundOne; +} + +static Boolean CFBundleAllowMixedLocalizations(void) { + static Boolean allowMixed = false, examinedMain = false; + if (!examinedMain) { + CFBundleRef mainBundle = CFBundleGetMainBundle(); + CFDictionaryRef infoDict = mainBundle ? CFBundleGetInfoDictionary(mainBundle) : NULL; + CFTypeRef allowMixedValue = infoDict ? CFDictionaryGetValue(infoDict, _kCFBundleAllowMixedLocalizationsKey) : NULL; + if (allowMixedValue) { + CFTypeID typeID = CFGetTypeID(allowMixedValue); + if (typeID == CFBooleanGetTypeID()) { + allowMixed = CFBooleanGetValue((CFBooleanRef)allowMixedValue); + } else if (typeID == CFStringGetTypeID()) { + allowMixed = (CFStringCompare((CFStringRef)allowMixedValue, CFSTR("true"), kCFCompareCaseInsensitive) == kCFCompareEqualTo || CFStringCompare((CFStringRef)allowMixedValue, CFSTR("YES"), kCFCompareCaseInsensitive) == kCFCompareEqualTo); + } else if (typeID == CFNumberGetTypeID()) { + SInt32 val = 0; + if (CFNumberGetValue((CFNumberRef)allowMixedValue, kCFNumberSInt32Type, &val)) allowMixed = (val != 0); + } + } + examinedMain = true; + } + return allowMixed; +} + +__private_extern__ void _CFBundleAddPreferredLprojNamesInDirectory(CFAllocatorRef alloc, CFURLRef bundleURL, uint8_t version, CFDictionaryRef infoDict, CFMutableArrayRef lprojNames, CFStringRef devLang) { + // This function will add zero, one or two elements to the lprojNames array. + // It examines the users preferred language list and the lproj directories inside the bundle directory. It picks the lproj directory that is highest on the users list. + // The users list can contain region names (like "en_US" for US English). In this case, if the region lproj exists, it will be added, and, if the region's associated language lproj exists that will be added. + CFURLRef resourcesURL = _CFBundleCopyResourcesDirectoryURLInDirectory(alloc, bundleURL, version); + CFURLRef absoluteURL; + CFIndex idx; + CFIndex count; + CFStringRef resourcesPath; + UniChar pathUniChars[CFMaxPathSize]; + CFIndex pathLen; + CFStringRef curLangStr; + Boolean foundOne = false; + + CFArrayRef userLanguages; + + // Init the one-time-only unichar buffers. + _CFEnsureStaticBuffersInited(); + + // Get the path to the resources and extract into a buffer. + absoluteURL = CFURLCopyAbsoluteURL(resourcesURL); + resourcesPath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE); + CFRelease(absoluteURL); + pathLen = CFStringGetLength(resourcesPath); + if (pathLen > CFMaxPathSize) pathLen = CFMaxPathSize; + CFStringGetCharacters(resourcesPath, CFRangeMake(0, pathLen), pathUniChars); + CFRelease(resourcesURL); + CFRelease(resourcesPath); + + // First check the main bundle. + if (!CFBundleAllowMixedLocalizations()) { + CFBundleRef mainBundle = CFBundleGetMainBundle(); + if (mainBundle) { + CFURLRef mainBundleURL = CFBundleCopyBundleURL(mainBundle); + if (!CFEqual(bundleURL, mainBundleURL)) { + // If there is a main bundle, and it isn't this one, try to use the language it prefers. + CFArrayRef mainBundleLangs = _CFBundleGetLanguageSearchList(mainBundle); + if (mainBundleLangs && (CFArrayGetCount(mainBundleLangs) > 0)) { + curLangStr = (CFStringRef)CFArrayGetValueAtIndex(mainBundleLangs, 0); + foundOne = _CFBundleTryOnePreferredLprojNameInDirectory(alloc, pathUniChars, pathLen, version, infoDict, curLangStr, lprojNames); + } + } + CFRelease(mainBundleURL); + } + } + + if (!foundOne) { + // If we didn't find the main bundle's preferred language, look at the users' prefs again and find the best one. + userLanguages = _CFBundleCopyUserLanguages(true); + count = (userLanguages ? CFArrayGetCount(userLanguages) : 0); + for (idx = 0; !foundOne && idx < count; idx++) { + curLangStr = (CFStringRef)CFArrayGetValueAtIndex(userLanguages, idx); + foundOne = _CFBundleTryOnePreferredLprojNameInDirectory(alloc, pathUniChars, pathLen, version, infoDict, curLangStr, lprojNames); + } + // use development region and U.S. English as backstops + if (!foundOne && devLang) foundOne = _CFBundleTryOnePreferredLprojNameInDirectory(alloc, pathUniChars, pathLen, version, infoDict, devLang, lprojNames); + if (!foundOne) foundOne = _CFBundleTryOnePreferredLprojNameInDirectory(alloc, pathUniChars, pathLen, version, infoDict, CFSTR("en_US"), lprojNames); + if (userLanguages) CFRelease(userLanguages); + } +} + +static Boolean _CFBundleTryOnePreferredLprojNameInArray(CFArrayRef array, CFStringRef curLangStr, CFMutableArrayRef lprojNames) { + Boolean foundOne = false; + CFRange range = CFRangeMake(0, CFArrayGetCount(array)); + CFStringRef altLangStr = NULL, modifiedLangStr = NULL, languageAbbreviation = NULL, languageName = NULL, canonicalLanguageIdentifier = NULL; + CFMutableDictionaryRef canonicalLanguageIdentifiers = NULL; + + if (range.length == 0) return foundOne; + if (CFArrayContainsValue(array, range, curLangStr)) { + if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), curLangStr)) CFArrayAppendValue(lprojNames, curLangStr); + foundOne = true; + if (range.length == 1 || CFStringGetLength(curLangStr) <= 2) return foundOne; + } + if (range.length == 1 && CFArrayContainsValue(array, range, CFSTR("default"))) return foundOne; +#if defined(__CONSTANT_CFSTRINGS__) + if (!altLangStr) { + CFIndex idx; + for (idx = 0; !altLangStr && idx < NUM_COMMON_LANGUAGE_NAMES; idx++) { + if (CFEqual(curLangStr, __CFBundleCommonLanguageAbbreviationsArray[idx])) altLangStr = __CFBundleCommonLanguageNamesArray[idx]; + else if (CFEqual(curLangStr, __CFBundleCommonLanguageNamesArray[idx])) altLangStr = __CFBundleCommonLanguageAbbreviationsArray[idx]; + } + } +#endif /* __CONSTANT_CFSTRINGS__ */ + if (foundOne && altLangStr) return foundOne; + if (altLangStr && CFArrayContainsValue(array, range, altLangStr)) { + if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), altLangStr)) CFArrayAppendValue(lprojNames, altLangStr); + foundOne = true; + return foundOne; + } + if (!altLangStr && (modifiedLangStr = _CFBundleCopyModifiedLocalization(curLangStr))) { + if (CFArrayContainsValue(array, range, modifiedLangStr)) { + if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), modifiedLangStr)) CFArrayAppendValue(lprojNames, modifiedLangStr); + foundOne = true; + } + } + if (!altLangStr && (languageAbbreviation = _CFBundleCopyLanguageAbbreviationForLocalization(curLangStr)) && !CFEqual(curLangStr, languageAbbreviation)) { + if (CFArrayContainsValue(array, range, languageAbbreviation)) { + if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), languageAbbreviation)) CFArrayAppendValue(lprojNames, languageAbbreviation); + foundOne = true; + } + } + if (!altLangStr && (languageName = _CFBundleCopyLanguageNameForLocalization(curLangStr)) && !CFEqual(curLangStr, languageName)) { + if (CFArrayContainsValue(array, range, languageName)) { + if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), languageName)) CFArrayAppendValue(lprojNames, languageName); + foundOne = true; + } + } + if (modifiedLangStr) CFRelease(modifiedLangStr); + if (languageAbbreviation) CFRelease(languageAbbreviation); + if (languageName) CFRelease(languageName); + if (canonicalLanguageIdentifier) CFRelease(canonicalLanguageIdentifier); + if (canonicalLanguageIdentifiers) CFRelease(canonicalLanguageIdentifiers); + + return foundOne; +} + +static CFArrayRef _CFBundleCopyLocalizationsForPreferences(CFArrayRef locArray, CFArrayRef prefArray, Boolean considerMain) { + CFMutableArrayRef lprojNames = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); + Boolean foundOne = false, releasePrefArray = false; + CFIndex idx, count; + + if (considerMain && !CFBundleAllowMixedLocalizations()) { + CFBundleRef mainBundle = CFBundleGetMainBundle(); + if (mainBundle) { + // If there is a main bundle, try to use the language it prefers. + CFArrayRef mainBundleLangs = _CFBundleGetLanguageSearchList(mainBundle); + if (mainBundleLangs && (CFArrayGetCount(mainBundleLangs) > 0)) { + foundOne = _CFBundleTryOnePreferredLprojNameInArray(locArray, (CFStringRef)CFArrayGetValueAtIndex(mainBundleLangs, 0), lprojNames); + } + } + } + if (!foundOne) { + if (!prefArray) { + prefArray = _CFBundleCopyUserLanguages(true); + if (prefArray) releasePrefArray = true; + } + count = (prefArray ? CFArrayGetCount(prefArray) : 0); + for (idx = 0; !foundOne && idx < count; idx++) { + foundOne = _CFBundleTryOnePreferredLprojNameInArray(locArray, (CFStringRef)CFArrayGetValueAtIndex(prefArray, idx), lprojNames); + } + // use U.S. English as backstop + if (!foundOne) { + foundOne = _CFBundleTryOnePreferredLprojNameInArray(locArray, CFSTR("en_US"), lprojNames); + } + // use random entry as backstop + if (!foundOne && CFArrayGetCount(lprojNames) > 0) { + foundOne = _CFBundleTryOnePreferredLprojNameInArray(locArray, (CFStringRef)CFArrayGetValueAtIndex(locArray, 0), lprojNames); + } + } + if (CFArrayGetCount(lprojNames) == 0) { + // Total backstop behavior to avoid having an empty array. + CFArrayAppendValue(lprojNames, CFSTR("en")); + } + if (releasePrefArray) { + CFRelease(prefArray); + } + return lprojNames; +} + +CF_EXPORT CFArrayRef CFBundleCopyLocalizationsForPreferences(CFArrayRef locArray, CFArrayRef prefArray) {return _CFBundleCopyLocalizationsForPreferences(locArray, prefArray, false);} + +CF_EXPORT CFArrayRef CFBundleCopyPreferredLocalizationsFromArray(CFArrayRef locArray) {return _CFBundleCopyLocalizationsForPreferences(locArray, NULL, true);} + +__private_extern__ CFArrayRef _CFBundleCopyLanguageSearchListInDirectory(CFAllocatorRef alloc, CFURLRef url, uint8_t *version) { + CFMutableArrayRef langs = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks); + uint8_t localVersion = 0; + CFDictionaryRef infoDict = _CFBundleCopyInfoDictionaryInDirectory(alloc, url, &localVersion); + CFStringRef devLang = NULL; + if (infoDict) devLang = (CFStringRef)CFDictionaryGetValue(infoDict, kCFBundleDevelopmentRegionKey); + if (devLang && (CFGetTypeID(devLang) != CFStringGetTypeID() || CFStringGetLength(devLang) == 0)) devLang = NULL; + + _CFBundleAddPreferredLprojNamesInDirectory(alloc, url, localVersion, infoDict, langs, devLang); + + if (devLang && CFArrayGetFirstIndexOfValue(langs, CFRangeMake(0, CFArrayGetCount(langs)), devLang) < 0) CFArrayAppendValue(langs, devLang); + + // Total backstop behavior to avoid having an empty array. + if (CFArrayGetCount(langs) == 0) CFArrayAppendValue(langs, CFSTR("en")); + + if (infoDict) CFRelease(infoDict); + if (version) *version = localVersion; + return langs; +} + +CF_EXPORT Boolean _CFBundleURLLooksLikeBundle(CFURLRef url) { + Boolean result = false; + CFBundleRef bundle = _CFBundleCreateIfLooksLikeBundle(kCFAllocatorSystemDefault, url); + if (bundle) { + result = true; + CFRelease(bundle); + } + return result; +} + +// Note that subDirName is expected to be the string for a URL +CF_INLINE Boolean _CFBundleURLHasSubDir(CFURLRef url, CFStringRef subDirName) { + CFURLRef dirURL; + Boolean isDir = false, result = false; + + dirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, subDirName, url); + if (dirURL) { + if (_CFIsResourceAtURL(dirURL, &isDir) && isDir) result = true; + CFRelease(dirURL); + } + return result; +} + +__private_extern__ Boolean _CFBundleURLLooksLikeBundleVersion(CFURLRef url, uint8_t *version) { + // check for existence of "Resources" or "Contents" or "Support Files" + // but check for the most likely one first + // version 0: old-style "Resources" bundles + // version 1: obsolete "Support Files" bundles + // version 2: modern "Contents" bundles + // version 3: none of the above (see below) + // version 4: not a bundle (for main bundle only) + uint8_t localVersion = 3; +#if READ_DIRECTORIES + CFURLRef absoluteURL = CFURLCopyAbsoluteURL(url); + CFStringRef directoryPath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE); + CFArrayRef contents = _CFBundleCopyDirectoryContentsAtPath(directoryPath, _CFBundleAllContents); + CFRange contentsRange = CFRangeMake(0, CFArrayGetCount(contents)); + if (CFStringHasSuffix(CFURLGetString(url), CFSTR(".framework/"))) { + if (CFArrayContainsValue(contents, contentsRange, _CFBundleResourcesDirectoryName)) localVersion = 0; + else if (CFArrayContainsValue(contents, contentsRange, _CFBundleSupportFilesDirectoryName2)) localVersion = 2; + else if (CFArrayContainsValue(contents, contentsRange, _CFBundleSupportFilesDirectoryName1)) localVersion = 1; + } else { + if (CFArrayContainsValue(contents, contentsRange, _CFBundleSupportFilesDirectoryName2)) localVersion = 2; + else if (CFArrayContainsValue(contents, contentsRange, _CFBundleResourcesDirectoryName)) localVersion = 0; + else if (CFArrayContainsValue(contents, contentsRange, _CFBundleSupportFilesDirectoryName1)) localVersion = 1; + } + CFRelease(contents); + CFRelease(directoryPath); + CFRelease(absoluteURL); +#endif /* READ_DIRECTORIES */ + if (localVersion == 3) { +#if DEPLOYMENT_TARGET_MACOSX + if (CFStringHasSuffix(CFURLGetString(url), CFSTR(".framework/"))) { + if (_CFBundleURLHasSubDir(url, _CFBundleResourcesURLFromBase0)) localVersion = 0; + else if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase2)) localVersion = 2; + else if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase1)) localVersion = 1; + } else { + if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase2)) localVersion = 2; + else if (_CFBundleURLHasSubDir(url, _CFBundleResourcesURLFromBase0)) localVersion = 0; + else if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase1)) localVersion = 1; + } +#endif + } + if (version) *version = localVersion; + return !(localVersion == 3); +} + +__private_extern__ CFDictionaryRef _CFBundleCopyInfoDictionaryInDirectory(CFAllocatorRef alloc, CFURLRef url, uint8_t *version) { + CFDictionaryRef dict = NULL; + unsigned char buff[CFMaxPathSize]; + uint8_t localVersion = 0; + + if (CFURLGetFileSystemRepresentation(url, true, buff, CFMaxPathSize)) { + CFURLRef newURL = CFURLCreateFromFileSystemRepresentation(alloc, buff, (CFIndex)strlen((char *)buff), true); + if (!newURL) newURL = (CFURLRef)CFRetain(url); + + // version 3 is for flattened pseudo-bundles with no Contents, Support Files, or Resources directories + if (!_CFBundleURLLooksLikeBundleVersion(newURL, &localVersion)) localVersion = 3; + + dict = _CFBundleCopyInfoDictionaryInDirectoryWithVersion(alloc, newURL, localVersion); + CFRelease(newURL); + } + if (version) *version = localVersion; + return dict; +} + +__private_extern__ CFDictionaryRef _CFBundleCopyInfoDictionaryInDirectoryWithVersion(CFAllocatorRef alloc, CFURLRef url, uint8_t version) { + CFDictionaryRef result = NULL; + if (url) { + CFURLRef infoURL = NULL, rawInfoURL = NULL; + CFDataRef infoData = NULL; + UniChar buff[CFMaxPathSize]; + CFIndex len; + CFMutableStringRef cheapStr; + CFStringRef infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension0, infoURLFromBase = _CFBundleInfoURLFromBase0; + Boolean tryPlatformSpecific = true, tryGlobal = true; +#if READ_DIRECTORIES + CFURLRef directoryURL = NULL, absoluteURL; + CFStringRef directoryPath; + CFArrayRef contents = NULL; + CFRange contentsRange = CFRangeMake(0, 0); +#endif /* READ_DIRECTORIES */ + + _CFEnsureStaticBuffersInited(); + + if (0 == version) { +#if READ_DIRECTORIES + directoryURL = CFURLCreateWithString(alloc, _CFBundleResourcesURLFromBase0, url); +#endif /* READ_DIRECTORIES */ + infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension0; + infoURLFromBase = _CFBundleInfoURLFromBase0; + } else if (1 == version) { +#if READ_DIRECTORIES + directoryURL = CFURLCreateWithString(alloc, _CFBundleSupportFilesURLFromBase1, url); +#endif /* READ_DIRECTORIES */ + infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension1; + infoURLFromBase = _CFBundleInfoURLFromBase1; + } else if (2 == version) { +#if READ_DIRECTORIES + directoryURL = CFURLCreateWithString(alloc, _CFBundleSupportFilesURLFromBase2, url); +#endif /* READ_DIRECTORIES */ + infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension2; + infoURLFromBase = _CFBundleInfoURLFromBase2; + } else if (3 == version) { +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX + CFStringRef posixPath = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); + // this test is necessary to exclude the case where a bundle is spuriously created from the innards of another bundle + if (posixPath) { + if (!(CFStringHasSuffix(posixPath, _CFBundleSupportFilesDirectoryName1) || CFStringHasSuffix(posixPath, _CFBundleSupportFilesDirectoryName2) || CFStringHasSuffix(posixPath, _CFBundleResourcesDirectoryName))) { +#if READ_DIRECTORIES + directoryURL = (CFURLRef)CFRetain(url); +#endif /* READ_DIRECTORIES */ + infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension3; + infoURLFromBase = _CFBundleInfoURLFromBase3; + } + CFRelease(posixPath); + } +#elif DEPLOYMENT_TARGET_WINDOWS + CFStringRef windowsPath = CFURLCopyFileSystemPath(url, kCFURLWindowsPathStyle); + // this test is necessary to exclude the case where a bundle is spuriously created from the innards of another bundle + if (windowsPath) { + if (!(CFStringHasSuffix(windowsPath, _CFBundleSupportFilesDirectoryName1) || CFStringHasSuffix(windowsPath, _CFBundleSupportFilesDirectoryName2) || CFStringHasSuffix(windowsPath, _CFBundleResourcesDirectoryName))) { +#if READ_DIRECTORIES + directoryURL = (CFURLRef)CFRetain(url); +#endif /* READ_DIRECTORIES */ + infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension3; + infoURLFromBase = _CFBundleInfoURLFromBase3; + } + CFRelease(windowsPath); + } +#else +#error Unknown or unspecified DEPLOYMENT_TARGET +#endif + } +#if READ_DIRECTORIES + if (directoryURL) { + absoluteURL = CFURLCopyAbsoluteURL(directoryURL); + directoryPath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE); + contents = _CFBundleCopyDirectoryContentsAtPath(directoryPath, _CFBundleAllContents); + contentsRange = CFRangeMake(0, CFArrayGetCount(contents)); + CFRelease(directoryPath); + CFRelease(absoluteURL); + CFRelease(directoryURL); + } +#endif /* READ_DIRECTORIES */ + + len = CFStringGetLength(infoURLFromBaseNoExtension); + CFStringGetCharacters(infoURLFromBaseNoExtension, CFRangeMake(0, len), buff); + buff[len++] = (UniChar)'-'; + memmove(buff + len, _PlatformUniChars, _PlatformLen * sizeof(UniChar)); + len += _PlatformLen; + _CFAppendPathExtension(buff, &len, CFMaxPathSize, _InfoExtensionUniChars, _InfoExtensionLen); + cheapStr = CFStringCreateMutable(alloc, 0); + CFStringAppendCharacters(cheapStr, buff, len); + infoURL = CFURLCreateWithString(alloc, cheapStr, url); +#if READ_DIRECTORIES + if (contents) { + CFIndex resourcesLen, idx; + for (resourcesLen = len; resourcesLen > 0; resourcesLen--) if (buff[resourcesLen - 1] == '/') break; + CFStringDelete(cheapStr, CFRangeMake(0, CFStringGetLength(cheapStr))); + CFStringAppendCharacters(cheapStr, buff + resourcesLen, len - resourcesLen); + for (tryPlatformSpecific = false, idx = 0; !tryPlatformSpecific && idx < contentsRange.length; idx++) { + // Need to do this case-insensitive to accommodate Palm + if (kCFCompareEqualTo == CFStringCompare(cheapStr, (CFStringRef)CFArrayGetValueAtIndex(contents, idx), kCFCompareCaseInsensitive)) tryPlatformSpecific = true; + } + } +#endif /* READ_DIRECTORIES */ + if (tryPlatformSpecific) CFURLCreateDataAndPropertiesFromResource(alloc, infoURL, &infoData, NULL, NULL, NULL); + //fprintf(stderr, "looking for ");CFShow(infoURL);fprintf(stderr, infoData ? "found it\n" : (tryPlatformSpecific ? "missed it\n" : "skipped it\n")); + CFRelease(cheapStr); + if (!infoData) { + // Check for global Info.plist + CFRelease(infoURL); + infoURL = CFURLCreateWithString(alloc, infoURLFromBase, url); +#if READ_DIRECTORIES + if (contents) { + CFIndex idx; + for (tryGlobal = false, idx = 0; !tryGlobal && idx < contentsRange.length; idx++) { + // Need to do this case-insensitive to accommodate Palm + if (kCFCompareEqualTo == CFStringCompare(_CFBundleInfoFileName, (CFStringRef)CFArrayGetValueAtIndex(contents, idx), kCFCompareCaseInsensitive)) tryGlobal = true; + } + } +#endif /* READ_DIRECTORIES */ + if (tryGlobal) CFURLCreateDataAndPropertiesFromResource(alloc, infoURL, &infoData, NULL, NULL, NULL); + //fprintf(stderr, "looking for ");CFShow(infoURL);fprintf(stderr, infoData ? "found it\n" : (tryGlobal ? "missed it\n" : "skipped it\n")); + } + + if (infoData) { + result = (CFDictionaryRef)CFPropertyListCreateFromXMLData(alloc, infoData, kCFPropertyListMutableContainers, NULL); + if (result) { + if (CFDictionaryGetTypeID() == CFGetTypeID(result)) { + CFDictionarySetValue((CFMutableDictionaryRef)result, _kCFBundleInfoPlistURLKey, infoURL); + } else { + CFRelease(result); + result = NULL; + } + } + if (!result) rawInfoURL = infoURL; + CFRelease(infoData); + } + if (!result) { + result = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (rawInfoURL) CFDictionarySetValue((CFMutableDictionaryRef)result, _kCFBundleRawInfoPlistURLKey, rawInfoURL); + } + + CFRelease(infoURL); +#if READ_DIRECTORIES + if (contents) CFRelease(contents); +#endif /* READ_DIRECTORIES */ + } + return result; +} + +static Boolean _CFBundleGetPackageInfoInDirectoryWithInfoDictionary(CFAllocatorRef alloc, CFURLRef url, CFDictionaryRef infoDict, UInt32 *packageType, UInt32 *packageCreator) { + Boolean retVal = false, hasType = false, hasCreator = false, releaseInfoDict = false; + CFURLRef tempURL; + CFDataRef pkgInfoData = NULL; + + // Check for a "real" new bundle + tempURL = CFURLCreateWithString(alloc, _CFBundlePkgInfoURLFromBase2, url); + CFURLCreateDataAndPropertiesFromResource(alloc, tempURL, &pkgInfoData, NULL, NULL, NULL); + CFRelease(tempURL); + if (!pkgInfoData) { + tempURL = CFURLCreateWithString(alloc, _CFBundlePkgInfoURLFromBase1, url); + CFURLCreateDataAndPropertiesFromResource(alloc, tempURL, &pkgInfoData, NULL, NULL, NULL); + CFRelease(tempURL); + } + if (!pkgInfoData) { + // Check for a "pseudo" new bundle + tempURL = CFURLCreateWithString(alloc, _CFBundlePseudoPkgInfoURLFromBase, url); + CFURLCreateDataAndPropertiesFromResource(alloc, tempURL, &pkgInfoData, NULL, NULL, NULL); + CFRelease(tempURL); + } + + // Now, either we have a pkgInfoData or not. If not, then is it because this is a new bundle without one (do we allow this?), or is it dbecause it is an old bundle. + // If we allow new bundles to not have a PkgInfo (because they already have the same data in the Info.plist), then we have to go read the info plist which makes failure expensive. + // drd: So we assume that a new bundle _must_ have a PkgInfo if they have this data at all, otherwise we manufacture it from the extension. + + if (pkgInfoData && CFDataGetLength(pkgInfoData) >= (int)(sizeof(UInt32) * 2)) { + UInt32 *pkgInfo = (UInt32 *)CFDataGetBytePtr(pkgInfoData); + if (packageType) *packageType = CFSwapInt32BigToHost(pkgInfo[0]); + if (packageCreator) *packageCreator = CFSwapInt32BigToHost(pkgInfo[1]); + retVal = hasType = hasCreator = true; + } + if (pkgInfoData) CFRelease(pkgInfoData); + if (!retVal) { + if (!infoDict) { + infoDict = _CFBundleCopyInfoDictionaryInDirectory(alloc, url, NULL); + releaseInfoDict = true; + } + if (infoDict) { + CFStringRef typeString = (CFStringRef)CFDictionaryGetValue(infoDict, _kCFBundlePackageTypeKey), creatorString = (CFStringRef)CFDictionaryGetValue(infoDict, _kCFBundleSignatureKey); + UInt32 tmp; + CFIndex usedBufLen = 0; + if (typeString && CFGetTypeID(typeString) == CFStringGetTypeID() && CFStringGetLength(typeString) == 4 && 4 == CFStringGetBytes(typeString, CFRangeMake(0, 4), kCFStringEncodingMacRoman, 0, false, (UInt8 *)&tmp, 4, &usedBufLen) && 4 == usedBufLen) { + if (packageType) *packageType = CFSwapInt32BigToHost(tmp); + retVal = hasType = true; + } + if (creatorString && CFGetTypeID(creatorString) == CFStringGetTypeID() && CFStringGetLength(creatorString) == 4 && 4 == CFStringGetBytes(creatorString, CFRangeMake(0, 4), kCFStringEncodingMacRoman, 0, false, (UInt8 *)&tmp, 4, &usedBufLen) && 4 == usedBufLen) { + if (packageCreator) *packageCreator = CFSwapInt32BigToHost(tmp); + retVal = hasCreator = true; + } + if (releaseInfoDict) CFRelease(infoDict); + } + } + if (!hasType || !hasCreator) { + // If this looks like a bundle then manufacture the type and creator. + if (retVal || _CFBundleURLLooksLikeBundle(url)) { + if (packageCreator && !hasCreator) *packageCreator = 0x3f3f3f3f; // '????' + if (packageType && !hasType) { + CFStringRef urlStr; + UniChar buff[CFMaxPathSize]; + CFIndex strLen, startOfExtension; + CFURLRef absoluteURL; + + // Detect "app", "debug", "profile", or "framework" extensions + absoluteURL = CFURLCopyAbsoluteURL(url); + urlStr = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE); + CFRelease(absoluteURL); + strLen = CFStringGetLength(urlStr); + if (strLen > CFMaxPathSize) strLen = CFMaxPathSize; + CFStringGetCharacters(urlStr, CFRangeMake(0, strLen), buff); + CFRelease(urlStr); + startOfExtension = _CFStartOfPathExtension(buff, strLen); + if (((strLen - startOfExtension == 4) || (strLen - startOfExtension == 5)) && (buff[startOfExtension] == (UniChar)'.') && (buff[startOfExtension+1] == (UniChar)'a') && (buff[startOfExtension+2] == (UniChar)'p') && (buff[startOfExtension+3] == (UniChar)'p') && ((strLen - startOfExtension == 4) || (buff[startOfExtension+4] == (UniChar)'/'))) { + // This is an app + *packageType = 0x4150504c; // 'APPL' + } else if (((strLen - startOfExtension == 6) || (strLen - startOfExtension == 7)) && (buff[startOfExtension] == (UniChar)'.') && (buff[startOfExtension+1] == (UniChar)'d') && (buff[startOfExtension+2] == (UniChar)'e') && (buff[startOfExtension+3] == (UniChar)'b') && (buff[startOfExtension+4] == (UniChar)'u') && (buff[startOfExtension+5] == (UniChar)'g') && ((strLen - startOfExtension == 6) || (buff[startOfExtension+6] == (UniChar)'/'))) { + // This is an app (debug version) + *packageType = 0x4150504c; // 'APPL' + } else if (((strLen - startOfExtension == 8) || (strLen - startOfExtension == 9)) && (buff[startOfExtension] == (UniChar)'.') && (buff[startOfExtension+1] == (UniChar)'p') && (buff[startOfExtension+2] == (UniChar)'r') && (buff[startOfExtension+3] == (UniChar)'o') && (buff[startOfExtension+4] == (UniChar)'f') && (buff[startOfExtension+5] == (UniChar)'i') && (buff[startOfExtension+6] == (UniChar)'l') && (buff[startOfExtension+7] == (UniChar)'e') && ((strLen - startOfExtension == 8) || (buff[startOfExtension+8] == (UniChar)'/'))) { + // This is an app (profile version) + *packageType = 0x4150504c; // 'APPL' + } else if (((strLen - startOfExtension == 8) || (strLen - startOfExtension == 9)) && (buff[startOfExtension] == (UniChar)'.') && (buff[startOfExtension+1] == (UniChar)'s') && (buff[startOfExtension+2] == (UniChar)'e') && (buff[startOfExtension+3] == (UniChar)'r') && (buff[startOfExtension+4] == (UniChar)'v') && (buff[startOfExtension+5] == (UniChar)'i') && (buff[startOfExtension+6] == (UniChar)'c') && (buff[startOfExtension+7] == (UniChar)'e') && ((strLen - startOfExtension == 8) || (buff[startOfExtension+8] == (UniChar)'/'))) { + // This is a service + *packageType = 0x4150504c; // 'APPL' + } else if (((strLen - startOfExtension == 10) || (strLen - startOfExtension == 11)) && (buff[startOfExtension] == (UniChar)'.') && (buff[startOfExtension+1] == (UniChar)'f') && (buff[startOfExtension+2] == (UniChar)'r') && (buff[startOfExtension+3] == (UniChar)'a') && (buff[startOfExtension+4] == (UniChar)'m') && (buff[startOfExtension+5] == (UniChar)'e') && (buff[startOfExtension+6] == (UniChar)'w') && (buff[startOfExtension+7] == (UniChar)'o') && (buff[startOfExtension+8] == (UniChar)'r') && (buff[startOfExtension+9] == (UniChar)'k') && ((strLen - startOfExtension == 10) || (buff[startOfExtension+10] == (UniChar)'/'))) { + // This is a framework + *packageType = 0x464d574b; // 'FMWK' + } else { + // Default to BNDL for generic bundle + *packageType = 0x424e444c; // 'BNDL' + } + } + retVal = true; + } + } + return retVal; +} + +CF_EXPORT Boolean _CFBundleGetPackageInfoInDirectory(CFAllocatorRef alloc, CFURLRef url, UInt32 *packageType, UInt32 *packageCreator) {return _CFBundleGetPackageInfoInDirectoryWithInfoDictionary(alloc, url, NULL, packageType, packageCreator);} + +CF_EXPORT void CFBundleGetPackageInfo(CFBundleRef bundle, UInt32 *packageType, UInt32 *packageCreator) { + CFURLRef bundleURL = CFBundleCopyBundleURL(bundle); + if (!_CFBundleGetPackageInfoInDirectoryWithInfoDictionary(CFGetAllocator(bundle), bundleURL, CFBundleGetInfoDictionary(bundle), packageType, packageCreator)) { + if (packageType) *packageType = 0x424e444c; // 'BNDL' + if (packageCreator) *packageCreator = 0x3f3f3f3f; // '????' + } + if (bundleURL) CFRelease(bundleURL); +} + +CF_EXPORT Boolean CFBundleGetPackageInfoInDirectory(CFURLRef url, UInt32 *packageType, UInt32 *packageCreator) {return _CFBundleGetPackageInfoInDirectory(kCFAllocatorSystemDefault, url, packageType, packageCreator);} + +__private_extern__ CFStringRef _CFBundleGetPlatformExecutablesSubdirectoryName(void) { +#if DEPLOYMENT_TARGET_MACOSX + return CFSTR("MacOS"); +#elif DEPLOYMENT_TARGET_SOLARIS + return CFSTR("Solaris"); +#elif DEPLOYMENT_TARGET_HPUX + return CFSTR("HPUX"); +#elif DEPLOYMENT_TARGET_LINUX + return CFSTR("Linux"); +#elif DEPLOYMENT_TARGET_FREEBSD + return CFSTR("FreeBSD"); +#elif DEPLOYMENT_TARGET_WINDOWS + return CFSTR("Windows"); +#else +#error Unknown or unspecified DEPLOYMENT_TARGET +#endif +} + +__private_extern__ CFStringRef _CFBundleGetAlternatePlatformExecutablesSubdirectoryName(void) { +#if DEPLOYMENT_TARGET_MACOSX + return CFSTR("Mac OS X"); +#elif DEPLOYMENT_TARGET_SOLARIS + return CFSTR("Solaris"); +#elif DEPLOYMENT_TARGET_HPUX + return CFSTR("HP-UX"); +#elif DEPLOYMENT_TARGET_LINUX + return CFSTR("Linux"); +#elif DEPLOYMENT_TARGET_FREEBSD + return CFSTR("FreeBSD"); +#elif DEPLOYMENT_TARGET_WINDOWS + return CFSTR("Windows"); +#else +#error Unknown or unspecified DEPLOYMENT_TARGET +#endif +} + +__private_extern__ CFStringRef _CFBundleGetOtherPlatformExecutablesSubdirectoryName(void) { +#if DEPLOYMENT_TARGET_MACOSX + return CFSTR("MacOSClassic"); +#elif DEPLOYMENT_TARGET_HPUX + return CFSTR("Other"); +#elif DEPLOYMENT_TARGET_SOLARIS + return CFSTR("Other"); +#elif DEPLOYMENT_TARGET_LINUX + return CFSTR("Other"); +#elif DEPLOYMENT_TARGET_FREEBSD + return CFSTR("Other"); +#elif DEPLOYMENT_TARGET_WINDOWS + return CFSTR("Other"); +#else +#error Unknown or unspecified DEPLOYMENT_TARGET +#endif +} + +__private_extern__ CFStringRef _CFBundleGetOtherAlternatePlatformExecutablesSubdirectoryName(void) { +#if DEPLOYMENT_TARGET_MACOSX + return CFSTR("Mac OS 8"); +#elif DEPLOYMENT_TARGET_HPUX + return CFSTR("Other"); +#elif DEPLOYMENT_TARGET_SOLARIS + return CFSTR("Other"); +#elif DEPLOYMENT_TARGET_LINUX + return CFSTR("Other"); +#elif DEPLOYMENT_TARGET_FREEBSD + return CFSTR("Other"); +#elif DEPLOYMENT_TARGET_WINDOWS + return CFSTR("Other"); +#else +#error Unknown or unspecified DEPLOYMENT_TARGET +#endif +} + +__private_extern__ CFArrayRef _CFBundleCopyBundleRegionsArray(CFBundleRef bundle) {return CFBundleCopyBundleLocalizations(bundle);} + +CF_EXPORT CFArrayRef CFBundleCopyBundleLocalizations(CFBundleRef bundle) { + CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); + CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle); +#if READ_DIRECTORIES + CFURLRef absoluteURL; + CFStringRef directoryPath; + CFArrayRef contents; + CFRange contentsRange; + CFIndex idx; +#else /* READ_DIRECTORIES */ + CFArrayRef urls = ((_CFBundleLayoutVersion(bundle) != 4) ? _CFContentsOfDirectory(CFGetAllocator(bundle), NULL, NULL, resourcesURL, _CFBundleLprojExtension) : NULL); +#endif /* READ_DIRECTORIES */ + CFArrayRef predefinedLocalizations = NULL; + CFMutableArrayRef result = NULL; + + if (infoDict) { + predefinedLocalizations = (CFArrayRef)CFDictionaryGetValue(infoDict, kCFBundleLocalizationsKey); + if (predefinedLocalizations && CFGetTypeID(predefinedLocalizations) != CFArrayGetTypeID()) { + predefinedLocalizations = NULL; + CFDictionaryRemoveValue((CFMutableDictionaryRef)infoDict, kCFBundleLocalizationsKey); + } + if (predefinedLocalizations) { + CFIndex i, c = CFArrayGetCount(predefinedLocalizations); + if (c > 0 && !result) result = CFArrayCreateMutable(CFGetAllocator(bundle), 0, &kCFTypeArrayCallBacks); + for (i = 0; i < c; i++) CFArrayAppendValue(result, CFArrayGetValueAtIndex(predefinedLocalizations, i)); + } + } + +#if READ_DIRECTORIES + if (resourcesURL) { + absoluteURL = CFURLCopyAbsoluteURL(resourcesURL); + directoryPath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE); + contents = _CFBundleCopyDirectoryContentsAtPath(directoryPath, _CFBundleAllContents); + contentsRange = CFRangeMake(0, CFArrayGetCount(contents)); + for (idx = 0; idx < contentsRange.length; idx++) { + CFStringRef name = (CFStringRef)CFArrayGetValueAtIndex(contents, idx); + if (CFStringHasSuffix(name, _CFBundleLprojExtensionWithDot)) { + CFStringRef localization = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, name, CFRangeMake(0, CFStringGetLength(name) - 6)); + if (!result) result = CFArrayCreateMutable(CFGetAllocator(bundle), 0, &kCFTypeArrayCallBacks); + CFArrayAppendValue(result, localization); + CFRelease(localization); + } + } + CFRelease(contents); + CFRelease(directoryPath); + CFRelease(absoluteURL); + } +#else /* READ_DIRECTORIES */ + if (urls) { + CFIndex i, c = CFArrayGetCount(urls); + CFURLRef curURL, curAbsoluteURL; + CFStringRef curStr, regionStr; + UniChar buff[CFMaxPathSize]; + CFIndex strLen, startOfLastPathComponent, regionLen; + + if (c > 0 && !result) result = CFArrayCreateMutable(CFGetAllocator(bundle), 0, &kCFTypeArrayCallBacks); + for (i = 0; i < c; i++) { + curURL = (CFURLRef)CFArrayGetValueAtIndex(urls, i); + curAbsoluteURL = CFURLCopyAbsoluteURL(curURL); + curStr = CFURLCopyFileSystemPath(curAbsoluteURL, PLATFORM_PATH_STYLE); + CFRelease(curAbsoluteURL); + strLen = CFStringGetLength(curStr); + if (strLen > CFMaxPathSize) strLen = CFMaxPathSize; + CFStringGetCharacters(curStr, CFRangeMake(0, strLen), buff); + + startOfLastPathComponent = _CFStartOfLastPathComponent(buff, strLen); + regionLen = _CFLengthAfterDeletingPathExtension(&(buff[startOfLastPathComponent]), strLen - startOfLastPathComponent); + regionStr = CFStringCreateWithCharacters(CFGetAllocator(bundle), &(buff[startOfLastPathComponent]), regionLen); + CFArrayAppendValue(result, regionStr); + CFRelease(regionStr); + CFRelease(curStr); + } + CFRelease(urls); + } +#endif /* READ_DIRECTORIES */ + + if (!result) { + CFStringRef developmentLocalization = CFBundleGetDevelopmentRegion(bundle); + if (developmentLocalization) { + result = CFArrayCreateMutable(CFGetAllocator(bundle), 0, &kCFTypeArrayCallBacks); + CFArrayAppendValue(result, developmentLocalization); + } + } + if (resourcesURL) CFRelease(resourcesURL); + + return result; +} + + +CF_EXPORT CFDictionaryRef CFBundleCopyInfoDictionaryForURL(CFURLRef url) { + CFDictionaryRef result = NULL; + Boolean isDir = false; + if (_CFIsResourceAtURL(url, &isDir)) { + if (isDir) { + result = _CFBundleCopyInfoDictionaryInDirectory(kCFAllocatorSystemDefault, url, NULL); + } else { + result = _CFBundleCopyInfoDictionaryInExecutable(url); + } + } + return result; +} + +CFArrayRef CFBundleCopyExecutableArchitecturesForURL(CFURLRef url) { + CFArrayRef result = NULL; + CFBundleRef bundle = CFBundleCreate(kCFAllocatorSystemDefault, url); + if (bundle) { + result = CFBundleCopyExecutableArchitectures(bundle); + CFRelease(bundle); + } else { + result = _CFBundleCopyArchitecturesForExecutable(url); + } + return result; +} + +CFArrayRef CFBundleCopyLocalizationsForURL(CFURLRef url) { + CFArrayRef result = NULL; + CFBundleRef bundle = CFBundleCreate(kCFAllocatorSystemDefault, url); + CFStringRef devLang = NULL; + if (bundle) { + result = CFBundleCopyBundleLocalizations(bundle); + CFRelease(bundle); + } else { + CFDictionaryRef infoDict = _CFBundleCopyInfoDictionaryInExecutable(url); + if (infoDict) { + CFArrayRef predefinedLocalizations = (CFArrayRef)CFDictionaryGetValue(infoDict, kCFBundleLocalizationsKey); + if (predefinedLocalizations && CFGetTypeID(predefinedLocalizations) == CFArrayGetTypeID()) result = (CFArrayRef)CFRetain(predefinedLocalizations); + if (!result) { + devLang = (CFStringRef)CFDictionaryGetValue(infoDict, kCFBundleDevelopmentRegionKey); + if (devLang && (CFGetTypeID(devLang) == CFStringGetTypeID() && CFStringGetLength(devLang) > 0)) result = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&devLang, 1, &kCFTypeArrayCallBacks); + } + CFRelease(infoDict); + } + } + return result; +}
diff --git a/CoreFoundation/CFByteOrder.h b/CoreFoundation/CFByteOrder.h new file mode 100644 index 0000000..7172cbb --- /dev/null +++ b/CoreFoundation/CFByteOrder.h
@@ -0,0 +1,332 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFByteOrder.h + Copyright (c) 1995-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFBYTEORDER__) +#define __COREFOUNDATION_CFBYTEORDER__ 1 + +#include <CoreFoundation/CFBase.h> +#if defined(__MACH__) && !defined(CF_USE_OSBYTEORDER_H) +#include <libkern/OSByteOrder.h> +#define CF_USE_OSBYTEORDER_H 1 +#endif + +CF_EXTERN_C_BEGIN + +enum __CFByteOrder { + CFByteOrderUnknown, + CFByteOrderLittleEndian, + CFByteOrderBigEndian +}; +typedef CFIndex CFByteOrder; + +CF_INLINE CFByteOrder CFByteOrderGetCurrent(void) { +#if CF_USE_OSBYTEORDER_H + int32_t byteOrder = OSHostByteOrder(); + switch (byteOrder) { + case OSLittleEndian: return CFByteOrderLittleEndian; + case OSBigEndian: return CFByteOrderBigEndian; + default: break; + } + return CFByteOrderUnknown; +#else +#if __LITTLE_ENDIAN__ + return CFByteOrderLittleEndian; +#elif __BIG_ENDIAN__ + return CFByteOrderBigEndian; +#else + return CFByteOrderUnknown; +#endif +#endif +} + +CF_INLINE uint16_t CFSwapInt16(uint16_t arg) { +#if CF_USE_OSBYTEORDER_H + return OSSwapInt16(arg); +#else + uint16_t result; + result = (uint16_t)(((arg << 8) & 0xFF00) | ((arg >> 8) & 0xFF)); + return result; +#endif +} + +CF_INLINE uint32_t CFSwapInt32(uint32_t arg) { +#if CF_USE_OSBYTEORDER_H + return OSSwapInt32(arg); +#else + uint32_t result; + result = ((arg & 0xFF) << 24) | ((arg & 0xFF00) << 8) | ((arg >> 8) & 0xFF00) | ((arg >> 24) & 0xFF); + return result; +#endif +} + +CF_INLINE uint64_t CFSwapInt64(uint64_t arg) { +#if CF_USE_OSBYTEORDER_H + return OSSwapInt64(arg); +#else + union CFSwap { + uint64_t sv; + uint32_t ul[2]; + } tmp, result; + tmp.sv = arg; + result.ul[0] = CFSwapInt32(tmp.ul[1]); + result.ul[1] = CFSwapInt32(tmp.ul[0]); + return result.sv; +#endif +} + +CF_INLINE uint16_t CFSwapInt16BigToHost(uint16_t arg) { +#if CF_USE_OSBYTEORDER_H + return OSSwapBigToHostInt16(arg); +#elif __BIG_ENDIAN__ + return arg; +#else + return CFSwapInt16(arg); +#endif +} + +CF_INLINE uint32_t CFSwapInt32BigToHost(uint32_t arg) { +#if CF_USE_OSBYTEORDER_H + return OSSwapBigToHostInt32(arg); +#elif __BIG_ENDIAN__ + return arg; +#else + return CFSwapInt32(arg); +#endif +} + +CF_INLINE uint64_t CFSwapInt64BigToHost(uint64_t arg) { +#if CF_USE_OSBYTEORDER_H + return OSSwapBigToHostInt64(arg); +#elif __BIG_ENDIAN__ + return arg; +#else + return CFSwapInt64(arg); +#endif +} + +CF_INLINE uint16_t CFSwapInt16HostToBig(uint16_t arg) { +#if CF_USE_OSBYTEORDER_H + return OSSwapHostToBigInt16(arg); +#elif __BIG_ENDIAN__ + return arg; +#else + return CFSwapInt16(arg); +#endif +} + +CF_INLINE uint32_t CFSwapInt32HostToBig(uint32_t arg) { +#if CF_USE_OSBYTEORDER_H + return OSSwapHostToBigInt32(arg); +#elif __BIG_ENDIAN__ + return arg; +#else + return CFSwapInt32(arg); +#endif +} + +CF_INLINE uint64_t CFSwapInt64HostToBig(uint64_t arg) { +#if CF_USE_OSBYTEORDER_H + return OSSwapHostToBigInt64(arg); +#elif __BIG_ENDIAN__ + return arg; +#else + return CFSwapInt64(arg); +#endif +} + +CF_INLINE uint16_t CFSwapInt16LittleToHost(uint16_t arg) { +#if CF_USE_OSBYTEORDER_H + return OSSwapLittleToHostInt16(arg); +#elif __LITTLE_ENDIAN__ + return arg; +#else + return CFSwapInt16(arg); +#endif +} + +CF_INLINE uint32_t CFSwapInt32LittleToHost(uint32_t arg) { +#if CF_USE_OSBYTEORDER_H + return OSSwapLittleToHostInt32(arg); +#elif __LITTLE_ENDIAN__ + return arg; +#else + return CFSwapInt32(arg); +#endif +} + +CF_INLINE uint64_t CFSwapInt64LittleToHost(uint64_t arg) { +#if CF_USE_OSBYTEORDER_H + return OSSwapLittleToHostInt64(arg); +#elif __LITTLE_ENDIAN__ + return arg; +#else + return CFSwapInt64(arg); +#endif +} + +CF_INLINE uint16_t CFSwapInt16HostToLittle(uint16_t arg) { +#if CF_USE_OSBYTEORDER_H + return OSSwapHostToLittleInt16(arg); +#elif __LITTLE_ENDIAN__ + return arg; +#else + return CFSwapInt16(arg); +#endif +} + +CF_INLINE uint32_t CFSwapInt32HostToLittle(uint32_t arg) { +#if CF_USE_OSBYTEORDER_H + return OSSwapHostToLittleInt32(arg); +#elif __LITTLE_ENDIAN__ + return arg; +#else + return CFSwapInt32(arg); +#endif +} + +CF_INLINE uint64_t CFSwapInt64HostToLittle(uint64_t arg) { +#if CF_USE_OSBYTEORDER_H + return OSSwapHostToLittleInt64(arg); +#elif __LITTLE_ENDIAN__ + return arg; +#else + return CFSwapInt64(arg); +#endif +} + +typedef struct {uint32_t v;} CFSwappedFloat32; +typedef struct {uint64_t v;} CFSwappedFloat64; + +CF_INLINE CFSwappedFloat32 CFConvertFloat32HostToSwapped(Float32 arg) { + union CFSwap { + Float32 v; + CFSwappedFloat32 sv; + } result; + result.v = arg; +#if __LITTLE_ENDIAN__ + result.sv.v = CFSwapInt32(result.sv.v); +#endif + return result.sv; +} + +CF_INLINE Float32 CFConvertFloat32SwappedToHost(CFSwappedFloat32 arg) { + union CFSwap { + Float32 v; + CFSwappedFloat32 sv; + } result; + result.sv = arg; +#if __LITTLE_ENDIAN__ + result.sv.v = CFSwapInt32(result.sv.v); +#endif + return result.v; +} + +CF_INLINE CFSwappedFloat64 CFConvertFloat64HostToSwapped(Float64 arg) { + union CFSwap { + Float64 v; + CFSwappedFloat64 sv; + } result; + result.v = arg; +#if __LITTLE_ENDIAN__ + result.sv.v = CFSwapInt64(result.sv.v); +#endif + return result.sv; +} + +CF_INLINE Float64 CFConvertFloat64SwappedToHost(CFSwappedFloat64 arg) { + union CFSwap { + Float64 v; + CFSwappedFloat64 sv; + } result; + result.sv = arg; +#if __LITTLE_ENDIAN__ + result.sv.v = CFSwapInt64(result.sv.v); +#endif + return result.v; +} + +CF_INLINE CFSwappedFloat32 CFConvertFloatHostToSwapped(float arg) { + union CFSwap { + float v; + CFSwappedFloat32 sv; + } result; + result.v = arg; +#if __LITTLE_ENDIAN__ + result.sv.v = CFSwapInt32(result.sv.v); +#endif + return result.sv; +} + +CF_INLINE float CFConvertFloatSwappedToHost(CFSwappedFloat32 arg) { + union CFSwap { + float v; + CFSwappedFloat32 sv; + } result; + result.sv = arg; +#if __LITTLE_ENDIAN__ + result.sv.v = CFSwapInt32(result.sv.v); +#endif + return result.v; +} + +CF_INLINE CFSwappedFloat64 CFConvertDoubleHostToSwapped(double arg) { + union CFSwap { + double v; + CFSwappedFloat64 sv; + } result; + result.v = arg; +#if __LITTLE_ENDIAN__ + result.sv.v = CFSwapInt64(result.sv.v); +#endif + return result.sv; +} + +CF_INLINE double CFConvertDoubleSwappedToHost(CFSwappedFloat64 arg) { + union CFSwap { + double v; + CFSwappedFloat64 sv; + } result; + result.sv = arg; +#if __LITTLE_ENDIAN__ + result.sv.v = CFSwapInt64(result.sv.v); +#endif + return result.v; +} + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFBYTEORDER__ */ +
diff --git a/CoreFoundation/CFCalendar.c b/CoreFoundation/CFCalendar.c new file mode 100644 index 0000000..796200b --- /dev/null +++ b/CoreFoundation/CFCalendar.c
@@ -0,0 +1,1079 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFCalendar.c + Copyright 2004-2004, Apple Computer, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include <CoreFoundation/CFCalendar.h> +#include <CoreFoundation/CFRuntime.h> +#include "CFInternal.h" +#include "CFPriv.h" +#include <unicode/ucal.h> + +#define BUFFER_SIZE 512 + +struct __CFCalendar { + CFRuntimeBase _base; + CFStringRef _identifier; // canonical identifier, never NULL + CFLocaleRef _locale; + CFStringRef _localeID; + CFTimeZoneRef _tz; + UCalendar *_cal; +}; + +static Boolean __CFCalendarEqual(CFTypeRef cf1, CFTypeRef cf2) { + CFCalendarRef calendar1 = (CFCalendarRef)cf1; + CFCalendarRef calendar2 = (CFCalendarRef)cf2; + return CFEqual(calendar1->_identifier, calendar2->_identifier); +} + +static CFHashCode __CFCalendarHash(CFTypeRef cf) { + CFCalendarRef calendar = (CFCalendarRef)cf; + return CFHash(calendar->_identifier); +} + +static CFStringRef __CFCalendarCopyDescription(CFTypeRef cf) { + CFCalendarRef calendar = (CFCalendarRef)cf; + return CFStringCreateWithFormat(CFGetAllocator(calendar), NULL, CFSTR("<CFCalendar %p [%p]>{identifier = '%@'}"), cf, CFGetAllocator(calendar), calendar->_identifier); +} + +static void __CFCalendarDeallocate(CFTypeRef cf) { + CFCalendarRef calendar = (CFCalendarRef)cf; + CFRelease(calendar->_identifier); + if (calendar->_locale) CFRelease(calendar->_locale); + if (calendar->_localeID) CFRelease(calendar->_localeID); + CFRelease(calendar->_tz); + if (calendar->_cal) ucal_close(calendar->_cal); +} + +static CFTypeID __kCFCalendarTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFCalendarClass = { + 0, + "CFCalendar", + NULL, // init + NULL, // copy + __CFCalendarDeallocate, + __CFCalendarEqual, + __CFCalendarHash, + NULL, // + __CFCalendarCopyDescription +}; + +static void __CFCalendarInitialize(void) { + __kCFCalendarTypeID = _CFRuntimeRegisterClass(&__CFCalendarClass); +} + +CFTypeID CFCalendarGetTypeID(void) { + if (_kCFRuntimeNotATypeID == __kCFCalendarTypeID) __CFCalendarInitialize(); + return __kCFCalendarTypeID; +} + +__private_extern__ UCalendar *__CFCalendarCreateUCalendar(CFStringRef calendarID, CFStringRef localeID, CFTimeZoneRef tz) { + if (calendarID) { + CFDictionaryRef components = CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault, localeID); + CFMutableDictionaryRef mcomponents = CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault, 0, components); + CFDictionarySetValue(mcomponents, kCFLocaleCalendarIdentifier, calendarID); + localeID = CFLocaleCreateLocaleIdentifierFromComponents(kCFAllocatorSystemDefault, mcomponents); + CFRelease(mcomponents); + CFRelease(components); + } + + char buffer[BUFFER_SIZE]; + const char *cstr = CFStringGetCStringPtr(localeID, kCFStringEncodingASCII); + if (NULL == cstr) { + if (CFStringGetCString(localeID, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer; + } + if (NULL == cstr) { + if (calendarID) CFRelease(localeID); + return NULL; + } + + UChar ubuffer[BUFFER_SIZE]; + CFStringRef tznam = CFTimeZoneGetName(tz); + CFIndex cnt = CFStringGetLength(tznam); + if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; + CFStringGetCharacters(tznam, CFRangeMake(0, cnt), (UniChar *)ubuffer); + + UErrorCode status = U_ZERO_ERROR; + UCalendar *cal = ucal_open(ubuffer, cnt, cstr, UCAL_TRADITIONAL, &status); + if (calendarID) CFRelease(localeID); + return cal; +} + +static void __CFCalendarSetupCal(CFCalendarRef calendar) { + calendar->_cal = __CFCalendarCreateUCalendar(calendar->_identifier, calendar->_localeID, calendar->_tz); +} + +static void __CFCalendarZapCal(CFCalendarRef calendar) { + ucal_close(calendar->_cal); + calendar->_cal = NULL; +} + +CFCalendarRef CFCalendarCopyCurrent(void) { + CFLocaleRef locale = CFLocaleCopyCurrent(); + CFCalendarRef calID = (CFCalendarRef)CFLocaleGetValue(locale, kCFLocaleCalendarIdentifier); + if (calID) { + CFCalendarRef calendar = CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault, (CFStringRef)calID); + CFCalendarSetLocale(calendar, locale); + CFRelease(locale); + return calendar; + } + return NULL; +} + +CFCalendarRef CFCalendarCreateWithIdentifier(CFAllocatorRef allocator, CFStringRef identifier) { + if (allocator == NULL) allocator = __CFGetDefaultAllocator(); + __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); + __CFGenericValidateType(identifier, CFStringGetTypeID()); + // return NULL until Chinese calendar is available + if (identifier != kCFGregorianCalendar && identifier != kCFBuddhistCalendar && identifier != kCFJapaneseCalendar && identifier != kCFIslamicCalendar && identifier != kCFIslamicCivilCalendar && identifier != kCFHebrewCalendar) { +// if (identifier != kCFGregorianCalendar && identifier != kCFBuddhistCalendar && identifier != kCFJapaneseCalendar && identifier != kCFIslamicCalendar && identifier != kCFIslamicCivilCalendar && identifier != kCFHebrewCalendar && identifier != kCFChineseCalendar) { + if (CFEqual(kCFGregorianCalendar, identifier)) identifier = kCFGregorianCalendar; + else if (CFEqual(kCFBuddhistCalendar, identifier)) identifier = kCFBuddhistCalendar; + else if (CFEqual(kCFJapaneseCalendar, identifier)) identifier = kCFJapaneseCalendar; + else if (CFEqual(kCFIslamicCalendar, identifier)) identifier = kCFIslamicCalendar; + else if (CFEqual(kCFIslamicCivilCalendar, identifier)) identifier = kCFIslamicCivilCalendar; + else if (CFEqual(kCFHebrewCalendar, identifier)) identifier = kCFHebrewCalendar; +// else if (CFEqual(kCFChineseCalendar, identifier)) identifier = kCFChineseCalendar; + else return NULL; + } + struct __CFCalendar *calendar = NULL; + uint32_t size = sizeof(struct __CFCalendar) - sizeof(CFRuntimeBase); + calendar = (struct __CFCalendar *)_CFRuntimeCreateInstance(allocator, CFCalendarGetTypeID(), size, NULL); + if (NULL == calendar) { + return NULL; + } + calendar->_identifier = (CFStringRef)CFRetain(identifier); + calendar->_locale = NULL; + calendar->_localeID = CFLocaleGetIdentifier(CFLocaleGetSystem()); + calendar->_tz = CFTimeZoneCopyDefault(); + calendar->_cal = NULL; + return (CFCalendarRef)calendar; +} + +CFStringRef CFCalendarGetIdentifier(CFCalendarRef calendar) { + CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFStringRef, calendar, "calendarIdentifier"); + __CFGenericValidateType(calendar, CFCalendarGetTypeID()); + return calendar->_identifier; +} + +CFLocaleRef CFCalendarCopyLocale(CFCalendarRef calendar) { + CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFLocaleRef, calendar, "_copyLocale"); + __CFGenericValidateType(calendar, CFCalendarGetTypeID()); + return (CFLocaleRef)CFLocaleCreate(kCFAllocatorSystemDefault, calendar->_localeID); +} + +void CFCalendarSetLocale(CFCalendarRef calendar, CFLocaleRef locale) { + CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar, "setLocale:", locale); + __CFGenericValidateType(calendar, CFCalendarGetTypeID()); + __CFGenericValidateType(locale, CFLocaleGetTypeID()); + CFStringRef localeID = CFLocaleGetIdentifier(locale); + if (localeID != calendar->_localeID) { + CFRelease(calendar->_localeID); + CFRetain(localeID); + calendar->_localeID = localeID; + if (calendar->_cal) __CFCalendarZapCal(calendar); + } +} + +CFTimeZoneRef CFCalendarCopyTimeZone(CFCalendarRef calendar) { + CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFTimeZoneRef, calendar, "_copyTimeZone"); + __CFGenericValidateType(calendar, CFCalendarGetTypeID()); + return (CFTimeZoneRef)CFRetain(calendar->_tz); +} + +void CFCalendarSetTimeZone(CFCalendarRef calendar, CFTimeZoneRef tz) { + CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar, "setTimeZone:", tz); + __CFGenericValidateType(calendar, CFCalendarGetTypeID()); + if (tz) __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); + if (tz != calendar->_tz) { + CFRelease(calendar->_tz); + calendar->_tz = tz ? (CFTimeZoneRef)CFRetain(tz) : CFTimeZoneCopyDefault(); + if (calendar->_cal) __CFCalendarZapCal(calendar); + } +} + +CFIndex CFCalendarGetFirstWeekday(CFCalendarRef calendar) { + CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFIndex, calendar, "firstWeekday"); + __CFGenericValidateType(calendar, CFCalendarGetTypeID()); + if (!calendar->_cal) __CFCalendarSetupCal(calendar); + if (calendar->_cal) { + return ucal_getAttribute(calendar->_cal, UCAL_FIRST_DAY_OF_WEEK); + } + return -1; +} + +void CFCalendarSetFirstWeekday(CFCalendarRef calendar, CFIndex wkdy) { + CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar, "setFirstWeekday:", wkdy); + __CFGenericValidateType(calendar, CFCalendarGetTypeID()); + if (!calendar->_cal) __CFCalendarSetupCal(calendar); + if (calendar->_cal) { + ucal_setAttribute(calendar->_cal, UCAL_FIRST_DAY_OF_WEEK, wkdy); + } +} + +CFIndex CFCalendarGetMinimumDaysInFirstWeek(CFCalendarRef calendar) { + CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFIndex, calendar, "minimumDaysInFirstWeek"); + __CFGenericValidateType(calendar, CFCalendarGetTypeID()); + if (!calendar->_cal) __CFCalendarSetupCal(calendar); + return calendar->_cal ? ucal_getAttribute(calendar->_cal, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK) : -1; +} + +void CFCalendarSetMinimumDaysInFirstWeek(CFCalendarRef calendar, CFIndex mwd) { + CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar, "setMinimumDaysInFirstWeek:", mwd); + __CFGenericValidateType(calendar, CFCalendarGetTypeID()); + if (!calendar->_cal) __CFCalendarSetupCal(calendar); + if (calendar->_cal) ucal_setAttribute(calendar->_cal, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK, mwd); +} + +CFDateRef CFCalendarCopyGregorianStartDate(CFCalendarRef calendar) { + CF_OBJC_FUNCDISPATCH0(CFCalendarGetTypeID(), CFDateRef, calendar, "_gregorianStartDate"); + __CFGenericValidateType(calendar, CFCalendarGetTypeID()); + if (!calendar->_cal) __CFCalendarSetupCal(calendar); + UErrorCode status = U_ZERO_ERROR; + UDate udate = calendar->_cal ? ucal_getGregorianChange(calendar->_cal, &status) : 0; + if (calendar->_cal && U_SUCCESS(status)) { + CFAbsoluteTime at = (double)udate / 1000.0 - kCFAbsoluteTimeIntervalSince1970; + return CFDateCreate(CFGetAllocator(calendar), at); + } + return NULL; +} + +void CFCalendarSetGregorianStartDate(CFCalendarRef calendar, CFDateRef date) { + CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), void, calendar, "_setGregorianStartDate:", date); + __CFGenericValidateType(calendar, CFCalendarGetTypeID()); + if (date) __CFGenericValidateType(date, CFDateGetTypeID()); + if (!calendar->_cal) __CFCalendarSetupCal(calendar); + if (!calendar->_cal) return; + if (!date) { + UErrorCode status = U_ZERO_ERROR; + UCalendar *cal = __CFCalendarCreateUCalendar(calendar->_identifier, calendar->_localeID, calendar->_tz); + UDate udate = cal ? ucal_getGregorianChange(cal, &status) : 0; + if (cal && U_SUCCESS(status)) { + status = U_ZERO_ERROR; + if (calendar->_cal) ucal_setGregorianChange(calendar->_cal, udate, &status); + } + if (cal) ucal_close(cal); + } else { + CFAbsoluteTime at = CFDateGetAbsoluteTime(date); + UDate udate = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0; + UErrorCode status = U_ZERO_ERROR; + if (calendar->_cal) ucal_setGregorianChange(calendar->_cal, udate, &status); + } +} + + +static UCalendarDateFields __CFCalendarGetICUFieldCode(CFCalendarUnit unit) { + switch (unit) { + case kCFCalendarUnitEra: return UCAL_ERA; + case kCFCalendarUnitYear: return UCAL_YEAR; + case kCFCalendarUnitMonth: return UCAL_MONTH; + case kCFCalendarUnitDay: return UCAL_DAY_OF_MONTH; + case kCFCalendarUnitHour: return UCAL_HOUR_OF_DAY; + case kCFCalendarUnitMinute: return UCAL_MINUTE; + case kCFCalendarUnitSecond: return UCAL_SECOND; + case kCFCalendarUnitWeek: return UCAL_WEEK_OF_YEAR; + case kCFCalendarUnitWeekday: return UCAL_DAY_OF_WEEK; + case kCFCalendarUnitWeekdayOrdinal: return UCAL_DAY_OF_WEEK_IN_MONTH; + } + return (UCalendarDateFields)-1; +} + +static UCalendarDateFields __CFCalendarGetICUFieldCodeFromChar(char ch) { + switch (ch) { + case 'G': return UCAL_ERA; + case 'y': return UCAL_YEAR; + case 'M': return UCAL_MONTH; + case 'd': return UCAL_DAY_OF_MONTH; + case 'h': return UCAL_HOUR; + case 'H': return UCAL_HOUR_OF_DAY; + case 'm': return UCAL_MINUTE; + case 's': return UCAL_SECOND; + case 'S': return UCAL_MILLISECOND; + case 'w': return UCAL_WEEK_OF_YEAR; + case 'W': return UCAL_WEEK_OF_MONTH; + case 'E': return UCAL_DAY_OF_WEEK; + case 'D': return UCAL_DAY_OF_YEAR; + case 'F': return UCAL_DAY_OF_WEEK_IN_MONTH; + case 'a': return UCAL_AM_PM; + case 'g': return UCAL_JULIAN_DAY; + } + return (UCalendarDateFields)-1; +} + +static UCalendarDateFields __CFCalendarGetCalendarUnitFromChar(char ch) { + switch (ch) { + case 'G': return (UCalendarDateFields)kCFCalendarUnitEra; + case 'y': return (UCalendarDateFields)kCFCalendarUnitYear; + case 'M': return (UCalendarDateFields)kCFCalendarUnitMonth; + case 'd': return (UCalendarDateFields)kCFCalendarUnitDay; + case 'H': return (UCalendarDateFields)kCFCalendarUnitHour; + case 'm': return (UCalendarDateFields)kCFCalendarUnitMinute; + case 's': return (UCalendarDateFields)kCFCalendarUnitSecond; + case 'w': return (UCalendarDateFields)kCFCalendarUnitWeek; + case 'E': return (UCalendarDateFields)kCFCalendarUnitWeekday; + case 'F': return (UCalendarDateFields)kCFCalendarUnitWeekdayOrdinal; + } + return (UCalendarDateFields)-1; +} + +CFRange CFCalendarGetMinimumRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit) { + CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), CFRange, calendar, "_minimumRangeOfUnit:", unit); + CFRange range = {kCFNotFound, kCFNotFound}; + __CFGenericValidateType(calendar, CFCalendarGetTypeID()); + if (!calendar->_cal) __CFCalendarSetupCal(calendar); + if (calendar->_cal) { + ucal_clear(calendar->_cal); + UCalendarDateFields field = __CFCalendarGetICUFieldCode(unit); + UErrorCode status = U_ZERO_ERROR; + range.location = ucal_getLimit(calendar->_cal, field, UCAL_GREATEST_MINIMUM, &status); + range.length = ucal_getLimit(calendar->_cal, field, UCAL_LEAST_MAXIMUM, &status) - range.location + 1; + if (UCAL_MONTH == field) range.location++; + if (100000 < range.length) range.length = 100000; + } + return range; +} + +CFRange CFCalendarGetMaximumRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit) { + CF_OBJC_FUNCDISPATCH1(CFCalendarGetTypeID(), CFRange, calendar, "_maximumRangeOfUnit:", unit); + CFRange range = {kCFNotFound, kCFNotFound}; + __CFGenericValidateType(calendar, CFCalendarGetTypeID()); + if (!calendar->_cal) __CFCalendarSetupCal(calendar); + if (calendar->_cal) { + ucal_clear(calendar->_cal); + UCalendarDateFields field = __CFCalendarGetICUFieldCode(unit); + UErrorCode status = U_ZERO_ERROR; + range.location = ucal_getLimit(calendar->_cal, field, UCAL_MINIMUM, &status); + range.length = ucal_getLimit(calendar->_cal, field, UCAL_MAXIMUM, &status) - range.location + 1; + if (UCAL_MONTH == field) range.location++; + if (100000 < range.length) range.length = 100000; + } + return range; +} + +static void __CFCalendarSetToFirstInstant(CFCalendarRef calendar, CFCalendarUnit unit, CFAbsoluteTime at) { + // Set UCalendar to first instant of unit prior to 'at' + UErrorCode status = U_ZERO_ERROR; + UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0); + ucal_setMillis(calendar->_cal, udate, &status); + int target_era = INT_MIN; + switch (unit) { // largest to smallest, we set the fields to their minimum value + case kCFCalendarUnitWeek: + { + // reduce to first day of week, then reduce the rest of the day + int32_t goal = ucal_getAttribute(calendar->_cal, UCAL_FIRST_DAY_OF_WEEK); + int32_t dow = ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status); + while (dow != goal) { + ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, -1, &status); + dow = ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status); + } + goto day; + } + case kCFCalendarUnitEra: + { + target_era = ucal_get(calendar->_cal, UCAL_ERA, &status); + ucal_set(calendar->_cal, UCAL_YEAR, ucal_getLimit(calendar->_cal, UCAL_YEAR, UCAL_ACTUAL_MINIMUM, &status)); + } + case kCFCalendarUnitYear: + ucal_set(calendar->_cal, UCAL_MONTH, ucal_getLimit(calendar->_cal, UCAL_MONTH, UCAL_ACTUAL_MINIMUM, &status)); + case kCFCalendarUnitMonth: + ucal_set(calendar->_cal, UCAL_DAY_OF_MONTH, ucal_getLimit(calendar->_cal, UCAL_DAY_OF_MONTH, UCAL_ACTUAL_MINIMUM, &status)); + case kCFCalendarUnitWeekday: + case kCFCalendarUnitDay: + day:; + ucal_set(calendar->_cal, UCAL_HOUR_OF_DAY, ucal_getLimit(calendar->_cal, UCAL_HOUR_OF_DAY, UCAL_ACTUAL_MINIMUM, &status)); + case kCFCalendarUnitHour: + ucal_set(calendar->_cal, UCAL_MINUTE, ucal_getLimit(calendar->_cal, UCAL_MINUTE, UCAL_ACTUAL_MINIMUM, &status)); + case kCFCalendarUnitMinute: + ucal_set(calendar->_cal, UCAL_SECOND, ucal_getLimit(calendar->_cal, UCAL_SECOND, UCAL_ACTUAL_MINIMUM, &status)); + case kCFCalendarUnitSecond: + ucal_set(calendar->_cal, UCAL_MILLISECOND, 0); + } + if (INT_MIN != target_era && ucal_get(calendar->_cal, UCAL_ERA, &status) < target_era) { + // In the Japanese calendar, and possibly others, eras don't necessarily + // start on the first day of a year, so the previous code may have backed + // up into the previous era, and we have to correct forward. + UDate bad_udate = ucal_getMillis(calendar->_cal, &status); + ucal_add(calendar->_cal, UCAL_MONTH, 1, &status); + while (ucal_get(calendar->_cal, UCAL_ERA, &status) < target_era) { + bad_udate = ucal_getMillis(calendar->_cal, &status); + ucal_add(calendar->_cal, UCAL_MONTH, 1, &status); + } + udate = ucal_getMillis(calendar->_cal, &status); + // target date is between bad_udate and udate + for (;;) { + UDate test_udate = (udate + bad_udate) / 2; + ucal_setMillis(calendar->_cal, test_udate, &status); + if (ucal_get(calendar->_cal, UCAL_ERA, &status) < target_era) { + bad_udate = test_udate; + } else { + udate = test_udate; + } + if (fabs(udate - bad_udate) < 1000) break; + } + do { + bad_udate = floor((bad_udate + 1000) / 1000) * 1000; + ucal_setMillis(calendar->_cal, bad_udate, &status); + } while (ucal_get(calendar->_cal, UCAL_ERA, &status) < target_era); + } +} + +static Boolean __validUnits(CFCalendarUnit smaller, CFCalendarUnit bigger) { + switch (bigger) { + case kCFCalendarUnitEra: + if (kCFCalendarUnitEra == smaller) return false; + if (kCFCalendarUnitWeekday == smaller) return false; + if (kCFCalendarUnitMinute == smaller) return false; // this causes CFIndex overflow in range.length + if (kCFCalendarUnitSecond == smaller) return false; // this causes CFIndex overflow in range.length + return true; + case kCFCalendarUnitYear: + if (kCFCalendarUnitEra == smaller) return false; + if (kCFCalendarUnitYear == smaller) return false; + if (kCFCalendarUnitWeekday == smaller) return false; + return true; + case kCFCalendarUnitMonth: + if (kCFCalendarUnitEra == smaller) return false; + if (kCFCalendarUnitYear == smaller) return false; + if (kCFCalendarUnitMonth == smaller) return false; + if (kCFCalendarUnitWeekday == smaller) return false; + return true; + case kCFCalendarUnitDay: + if (kCFCalendarUnitHour == smaller) return true; + if (kCFCalendarUnitMinute == smaller) return true; + if (kCFCalendarUnitSecond == smaller) return true; + return false; + case kCFCalendarUnitHour: + if (kCFCalendarUnitMinute == smaller) return true; + if (kCFCalendarUnitSecond == smaller) return true; + return false; + case kCFCalendarUnitMinute: + if (kCFCalendarUnitSecond == smaller) return true; + return false; + case kCFCalendarUnitWeek: + if (kCFCalendarUnitWeekday == smaller) return true; + if (kCFCalendarUnitDay == smaller) return true; + if (kCFCalendarUnitHour == smaller) return true; + if (kCFCalendarUnitMinute == smaller) return true; + if (kCFCalendarUnitSecond == smaller) return true; + return false; + case kCFCalendarUnitSecond: + case kCFCalendarUnitWeekday: + case kCFCalendarUnitWeekdayOrdinal: + return false; + } + return false; +}; + +static CFRange __CFCalendarGetRangeOfUnit1(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) { + CFRange range = {kCFNotFound, kCFNotFound}; + if (!__validUnits(smallerUnit, biggerUnit)) return range; + CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), CFRange, calendar, "_rangeOfUnit:inUnit:forAT:", smallerUnit, biggerUnit, at); + __CFGenericValidateType(calendar, CFCalendarGetTypeID()); + if (!calendar->_cal) __CFCalendarSetupCal(calendar); + if (calendar->_cal) { + int32_t dow = -1; + ucal_clear(calendar->_cal); + UCalendarDateFields smallField = __CFCalendarGetICUFieldCode(smallerUnit); + UCalendarDateFields bigField = __CFCalendarGetICUFieldCode(biggerUnit); + if (kCFCalendarUnitWeekdayOrdinal == smallerUnit) { + UErrorCode status = U_ZERO_ERROR; + UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0); + ucal_setMillis(calendar->_cal, udate, &status); + dow = ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status); + } + // Set calendar to first instant of big unit + __CFCalendarSetToFirstInstant(calendar, biggerUnit, at); + UErrorCode status = U_ZERO_ERROR; + UDate start = ucal_getMillis(calendar->_cal, &status); + if (kCFCalendarUnitWeek == biggerUnit) { + range.location = ucal_get(calendar->_cal, smallField, &status); + if (kCFCalendarUnitMonth == smallerUnit) range.location++; + } else { + range.location = (kCFCalendarUnitHour == smallerUnit || kCFCalendarUnitMinute == smallerUnit || kCFCalendarUnitSecond == smallerUnit) ? 0 : 1; + } + // Set calendar to first instant of next value of big unit + if (UCAL_ERA == bigField) { + // ICU refuses to do the addition, probably because we are + // at the limit of UCAL_ERA. Use alternate strategy. + CFIndex limit = ucal_getLimit(calendar->_cal, UCAL_YEAR, UCAL_MAXIMUM, &status); + if (100000 < limit) limit = 100000; + ucal_add(calendar->_cal, UCAL_YEAR, limit, &status); + } else { + ucal_add(calendar->_cal, bigField, 1, &status); + } + if (kCFCalendarUnitWeek == smallerUnit && kCFCalendarUnitYear == biggerUnit) { + ucal_add(calendar->_cal, UCAL_SECOND, -1, &status); + range.length = ucal_get(calendar->_cal, UCAL_WEEK_OF_YEAR, &status); + while (1 == range.length) { + ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, -1, &status); + range.length = ucal_get(calendar->_cal, UCAL_WEEK_OF_YEAR, &status); + } + range.location = 1; + return range; + } else if (kCFCalendarUnitWeek == smallerUnit && kCFCalendarUnitMonth == biggerUnit) { + ucal_add(calendar->_cal, UCAL_SECOND, -1, &status); + range.length = ucal_get(calendar->_cal, UCAL_WEEK_OF_YEAR, &status); + range.location = 1; + return range; + } + UDate goal = ucal_getMillis(calendar->_cal, &status); + // Set calendar back to first instant of big unit + ucal_setMillis(calendar->_cal, start, &status); + if (kCFCalendarUnitWeekdayOrdinal == smallerUnit) { + // roll day forward to first 'dow' + while (ucal_get(calendar->_cal, (kCFCalendarUnitMonth == biggerUnit) ? UCAL_WEEK_OF_MONTH : UCAL_WEEK_OF_YEAR, &status) != 1) { + ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, 1, &status); + } + while (ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status) != dow) { + ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, 1, &status); + } + start = ucal_getMillis(calendar->_cal, &status); + goal -= 1000; + range.location = 1; // constant here works around ICU -- see 3948293 + } + UDate curr = start; + range.length = (kCFCalendarUnitWeekdayOrdinal == smallerUnit) ? 1 : 0; + const int multiple_table[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14}; + int multiple = (1 << multiple_table[flsl(smallerUnit) - 1]); + Boolean divide = false, alwaysDivide = false; + while (curr < goal) { + ucal_add(calendar->_cal, smallField, multiple, &status); + UDate newcurr = ucal_getMillis(calendar->_cal, &status); + if (curr < newcurr && newcurr <= goal) { + range.length += multiple; + curr = newcurr; + } else { + // Either newcurr is going backwards, or not making + // progress, or has overshot the goal; reset date + // and try smaller multiples. + ucal_setMillis(calendar->_cal, curr, &status); + divide = true; + // once we start overshooting the goal, the add at + // smaller multiples will succeed at most once for + // each multiple, so we reduce it every time through + // the loop. + if (goal < newcurr) alwaysDivide = true; + } + if (divide) { + multiple = multiple / 2; + if (0 == multiple) break; + divide = alwaysDivide; + } + } + } + return range; +} + +#if __GNUC__ +static CFRange __CFCalendarGetRangeOfUnit2(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) __attribute__((noinline)); +#endif +static CFRange __CFCalendarGetRangeOfUnit2(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) { + CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), CFRange, calendar, "_rangeOfUnit:inUnit:forAT:", smallerUnit, biggerUnit, at); + __CFGenericValidateType(calendar, CFCalendarGetTypeID()); + CFRange range = {kCFNotFound, kCFNotFound}; + if (!calendar->_cal) __CFCalendarSetupCal(calendar); + if (calendar->_cal) { + switch (smallerUnit) { + case kCFCalendarUnitSecond: + switch (biggerUnit) { + case kCFCalendarUnitMinute: + case kCFCalendarUnitHour: + case kCFCalendarUnitDay: + case kCFCalendarUnitWeekday: + case kCFCalendarUnitWeek: + case kCFCalendarUnitMonth: + case kCFCalendarUnitYear: + case kCFCalendarUnitEra: + // goto calculate; + range.location = 0; + range.length = 60; + break; + } + break; + case kCFCalendarUnitMinute: + switch (biggerUnit) { + case kCFCalendarUnitHour: + case kCFCalendarUnitDay: + case kCFCalendarUnitWeekday: + case kCFCalendarUnitWeek: + case kCFCalendarUnitMonth: + case kCFCalendarUnitYear: + case kCFCalendarUnitEra: + // goto calculate; + range.location = 0; + range.length = 60; + break; + } + break; + case kCFCalendarUnitHour: + switch (biggerUnit) { + case kCFCalendarUnitDay: + case kCFCalendarUnitWeekday: + case kCFCalendarUnitWeek: + case kCFCalendarUnitMonth: + case kCFCalendarUnitYear: + case kCFCalendarUnitEra: + // goto calculate; + range.location = 0; + range.length = 24; + break; + } + break; + case kCFCalendarUnitDay: + switch (biggerUnit) { + case kCFCalendarUnitWeek: + case kCFCalendarUnitMonth: + case kCFCalendarUnitYear: + case kCFCalendarUnitEra: + goto calculate; + break; + } + break; + case kCFCalendarUnitWeekday: + switch (biggerUnit) { + case kCFCalendarUnitWeek: + case kCFCalendarUnitMonth: + case kCFCalendarUnitYear: + case kCFCalendarUnitEra: + goto calculate; + break; + } + break; + case kCFCalendarUnitWeekdayOrdinal: + switch (biggerUnit) { + case kCFCalendarUnitMonth: + case kCFCalendarUnitYear: + case kCFCalendarUnitEra: + goto calculate; + break; + } + break; + case kCFCalendarUnitWeek: + switch (biggerUnit) { + case kCFCalendarUnitMonth: + case kCFCalendarUnitYear: + case kCFCalendarUnitEra: + goto calculate; + break; + } + break; + case kCFCalendarUnitMonth: + switch (biggerUnit) { + case kCFCalendarUnitYear: + case kCFCalendarUnitEra: + goto calculate; + break; + } + break; + case kCFCalendarUnitYear: + switch (biggerUnit) { + case kCFCalendarUnitEra: + goto calculate; + break; + } + break; + case kCFCalendarUnitEra: + break; + } + } + return range; + + calculate:; + ucal_clear(calendar->_cal); + UCalendarDateFields smallField = __CFCalendarGetICUFieldCode(smallerUnit); + UCalendarDateFields bigField = __CFCalendarGetICUFieldCode(biggerUnit); + UCalendarDateFields yearField = __CFCalendarGetICUFieldCode(kCFCalendarUnitYear); + UCalendarDateFields fieldToAdd = smallField; + if (kCFCalendarUnitWeekday == smallerUnit) { + fieldToAdd = __CFCalendarGetICUFieldCode(kCFCalendarUnitDay); + } + int32_t dow = -1; + if (kCFCalendarUnitWeekdayOrdinal == smallerUnit) { + UErrorCode status = U_ZERO_ERROR; + UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0); + ucal_setMillis(calendar->_cal, udate, &status); + dow = ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status); + fieldToAdd = __CFCalendarGetICUFieldCode(kCFCalendarUnitWeek); + } + // Set calendar to first instant of big unit + __CFCalendarSetToFirstInstant(calendar, biggerUnit, at); + if (kCFCalendarUnitWeekdayOrdinal == smallerUnit) { + UErrorCode status = U_ZERO_ERROR; + // roll day forward to first 'dow' + while (ucal_get(calendar->_cal, (kCFCalendarUnitMonth == biggerUnit) ? UCAL_WEEK_OF_MONTH : UCAL_WEEK_OF_YEAR, &status) != 1) { + ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, 1, &status); + } + while (ucal_get(calendar->_cal, UCAL_DAY_OF_WEEK, &status) != dow) { + ucal_add(calendar->_cal, UCAL_DAY_OF_MONTH, 1, &status); + } + } + int32_t minSmallValue = INT32_MAX; + int32_t maxSmallValue = INT32_MIN; + UErrorCode status = U_ZERO_ERROR; + int32_t bigValue = ucal_get(calendar->_cal, bigField, &status); + for (;;) { + int32_t smallValue = ucal_get(calendar->_cal, smallField, &status); + if (smallValue < minSmallValue) minSmallValue = smallValue; + if (smallValue > maxSmallValue) maxSmallValue = smallValue; + ucal_add(calendar->_cal, fieldToAdd, 1, &status); + if (bigValue != ucal_get(calendar->_cal, bigField, &status)) break; + if (biggerUnit == kCFCalendarUnitEra && ucal_get(calendar->_cal, yearField, &status) > 10000) break; + // we assume an answer for 10000 years can be extrapolated to 100000 years, to save time + } + status = U_ZERO_ERROR; + range.location = minSmallValue; + if (smallerUnit == kCFCalendarUnitMonth) range.location = 1; + range.length = maxSmallValue - minSmallValue + 1; + if (biggerUnit == kCFCalendarUnitEra && ucal_get(calendar->_cal, yearField, &status) > 10000) range.length = 100000; + + return range; +} + +CFRange CFCalendarGetRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) { + if (_CFExecutableLinkedOnOrAfter(CFSystemVersionLeopard)) { + return __CFCalendarGetRangeOfUnit2(calendar, smallerUnit, biggerUnit, at); + } else { + return __CFCalendarGetRangeOfUnit1(calendar, smallerUnit, biggerUnit, at); + } +} + +CFIndex CFCalendarGetOrdinalityOfUnit(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) { + CFIndex result = kCFNotFound; + if (!__validUnits(smallerUnit, biggerUnit)) return result; + CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), CFIndex, calendar, "_ordinalityOfUnit:inUnit:forAT:", smallerUnit, biggerUnit, at); + __CFGenericValidateType(calendar, CFCalendarGetTypeID()); + if (!calendar->_cal) __CFCalendarSetupCal(calendar); + if (calendar->_cal) { + UErrorCode status = U_ZERO_ERROR; + ucal_clear(calendar->_cal); + if (kCFCalendarUnitWeek == smallerUnit && kCFCalendarUnitYear == biggerUnit) { + UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0); + ucal_setMillis(calendar->_cal, udate, &status); + int32_t val = ucal_get(calendar->_cal, UCAL_WEEK_OF_YEAR, &status); + return val; + } else if (kCFCalendarUnitWeek == smallerUnit && kCFCalendarUnitMonth == biggerUnit) { + UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0); + ucal_setMillis(calendar->_cal, udate, &status); + int32_t val = ucal_get(calendar->_cal, UCAL_WEEK_OF_MONTH, &status); + return val; + } + UCalendarDateFields smallField = __CFCalendarGetICUFieldCode(smallerUnit); + // Set calendar to first instant of big unit + __CFCalendarSetToFirstInstant(calendar, biggerUnit, at); + UDate curr = ucal_getMillis(calendar->_cal, &status); + UDate goal = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0); + result = 1; + const int multiple_table[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14}; + int multiple = (1 << multiple_table[flsl(smallerUnit) - 1]); + Boolean divide = false, alwaysDivide = false; + while (curr < goal) { + ucal_add(calendar->_cal, smallField, multiple, &status); + UDate newcurr = ucal_getMillis(calendar->_cal, &status); + if (curr < newcurr && newcurr <= goal) { + result += multiple; + curr = newcurr; + } else { + // Either newcurr is going backwards, or not making + // progress, or has overshot the goal; reset date + // and try smaller multiples. + ucal_setMillis(calendar->_cal, curr, &status); + divide = true; + // once we start overshooting the goal, the add at + // smaller multiples will succeed at most once for + // each multiple, so we reduce it every time through + // the loop. + if (goal < newcurr) alwaysDivide = true; + } + if (divide) { + multiple = multiple / 2; + if (0 == multiple) break; + divide = alwaysDivide; + } + } + } + return result; +} + +Boolean _CFCalendarComposeAbsoluteTimeV(CFCalendarRef calendar, /* out */ CFAbsoluteTime *atp, const char *componentDesc, int *vector, int count) { + if (!calendar->_cal) __CFCalendarSetupCal(calendar); + if (calendar->_cal) { + UErrorCode status = U_ZERO_ERROR; + ucal_clear(calendar->_cal); + ucal_set(calendar->_cal, UCAL_YEAR, 1); + ucal_set(calendar->_cal, UCAL_MONTH, 0); + ucal_set(calendar->_cal, UCAL_DAY_OF_MONTH, 1); + ucal_set(calendar->_cal, UCAL_HOUR_OF_DAY, 0); + ucal_set(calendar->_cal, UCAL_MINUTE, 0); + ucal_set(calendar->_cal, UCAL_SECOND, 0); + const char *desc = componentDesc; + Boolean doWOY = false; + char ch = *desc; + while (ch) { + UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch); + if (UCAL_WEEK_OF_YEAR == field) { + doWOY = true; + } + desc++; + ch = *desc; + } + desc = componentDesc; + ch = *desc; + while (ch) { + UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch); + int value = *vector; + if (UCAL_YEAR == field && doWOY) field = UCAL_YEAR_WOY; + if (UCAL_MONTH == field) value--; + ucal_set(calendar->_cal, field, value); + vector++; + desc++; + ch = *desc; + } + UDate udate = ucal_getMillis(calendar->_cal, &status); + CFAbsoluteTime at = (udate / 1000.0) - kCFAbsoluteTimeIntervalSince1970; + if (atp) *atp = at; + return U_SUCCESS(status) ? true : false; + } + return false; +} + +Boolean _CFCalendarDecomposeAbsoluteTimeV(CFCalendarRef calendar, CFAbsoluteTime at, const char *componentDesc, int **vector, int count) { + if (!calendar->_cal) __CFCalendarSetupCal(calendar); + if (calendar->_cal) { + UErrorCode status = U_ZERO_ERROR; + ucal_clear(calendar->_cal); + UDate udate = floor((at + kCFAbsoluteTimeIntervalSince1970) * 1000.0); + ucal_setMillis(calendar->_cal, udate, &status); + char ch = *componentDesc; + while (ch) { + UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch); + int value = ucal_get(calendar->_cal, field, &status); + if (UCAL_MONTH == field) value++; + *(*vector) = value; + vector++; + componentDesc++; + ch = *componentDesc; + } + return U_SUCCESS(status) ? true : false; + } + return false; +} + +Boolean _CFCalendarAddComponentsV(CFCalendarRef calendar, /* inout */ CFAbsoluteTime *atp, CFOptionFlags options, const char *componentDesc, int *vector, int count) { + if (!calendar->_cal) __CFCalendarSetupCal(calendar); + if (calendar->_cal) { + UErrorCode status = U_ZERO_ERROR; + ucal_clear(calendar->_cal); + UDate udate = floor((*atp + kCFAbsoluteTimeIntervalSince1970) * 1000.0); + ucal_setMillis(calendar->_cal, udate, &status); + char ch = *componentDesc; + while (ch) { + UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch); + int amount = *vector; + if (options & kCFCalendarComponentsWrap) { + ucal_roll(calendar->_cal, field, amount, &status); + } else { + ucal_add(calendar->_cal, field, amount, &status); + } + vector++; + componentDesc++; + ch = *componentDesc; + } + udate = ucal_getMillis(calendar->_cal, &status); + *atp = (udate / 1000.0) - kCFAbsoluteTimeIntervalSince1970; + return U_SUCCESS(status) ? true : false; + } + return false; +} + +Boolean _CFCalendarGetComponentDifferenceV(CFCalendarRef calendar, CFAbsoluteTime startingAT, CFAbsoluteTime resultAT, CFOptionFlags options, const char *componentDesc, int **vector, int count) { + if (!calendar->_cal) __CFCalendarSetupCal(calendar); + if (calendar->_cal) { + UErrorCode status = U_ZERO_ERROR; + ucal_clear(calendar->_cal); + UDate curr = floor((startingAT + kCFAbsoluteTimeIntervalSince1970) * 1000.0); + UDate goal = floor((resultAT + kCFAbsoluteTimeIntervalSince1970) * 1000.0); + ucal_setMillis(calendar->_cal, curr, &status); + int direction = (startingAT <= resultAT) ? 1 : -1; + char ch = *componentDesc; + while (ch) { + UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch); + const int multiple_table[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14}; + int multiple = direction * (1 << multiple_table[flsl(__CFCalendarGetCalendarUnitFromChar(ch)) - 1]); + Boolean divide = false, alwaysDivide = false; + int result = 0; + while ((direction > 0 && curr < goal) || (direction < 0 && goal < curr)) { + ucal_add(calendar->_cal, field, multiple, &status); + UDate newcurr = ucal_getMillis(calendar->_cal, &status); + if ((direction > 0 && curr < newcurr && newcurr <= goal) || (direction < 0 && newcurr < curr && goal <= newcurr)) { + result += multiple; + curr = newcurr; + } else { + // Either newcurr is going backwards, or not making + // progress, or has overshot the goal; reset date + // and try smaller multiples. + ucal_setMillis(calendar->_cal, curr, &status); + divide = true; + // once we start overshooting the goal, the add at + // smaller multiples will succeed at most once for + // each multiple, so we reduce it every time through + // the loop. + if ((direction > 0 && goal < newcurr) || (direction < 0 && newcurr < goal)) alwaysDivide = true; + } + if (divide) { + multiple = multiple / 2; + if (0 == multiple) break; + divide = alwaysDivide; + } + } + *(*vector) = result; + vector++; + componentDesc++; + ch = *componentDesc; + } + return U_SUCCESS(status) ? true : false; + } + return false; +} + +Boolean CFCalendarComposeAbsoluteTime(CFCalendarRef calendar, /* out */ CFAbsoluteTime *atp, const char *componentDesc, ...) { + va_list args; + va_start(args, componentDesc); + CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), Boolean, calendar, "_composeAbsoluteTime:::", atp, componentDesc, args); + __CFGenericValidateType(calendar, CFCalendarGetTypeID()); + int idx, cnt = (int)strlen((char *)componentDesc); +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX + STACK_BUFFER_DECL(int, vector, cnt); +#else + int vector[256]; // Dynamic stack allocation is GNU specific +#endif + for (idx = 0; idx < cnt; idx++) { + int arg = va_arg(args, int); + vector[idx] = arg; + } + va_end(args); + return _CFCalendarComposeAbsoluteTimeV(calendar, atp, componentDesc, vector, cnt); +} + +Boolean CFCalendarDecomposeAbsoluteTime(CFCalendarRef calendar, CFAbsoluteTime at, const char *componentDesc, ...) { + va_list args; + va_start(args, componentDesc); + CF_OBJC_FUNCDISPATCH3(CFCalendarGetTypeID(), Boolean, calendar, "_decomposeAbsoluteTime:::", at, componentDesc, args); + __CFGenericValidateType(calendar, CFCalendarGetTypeID()); + int idx, cnt = (int)strlen((char *)componentDesc); +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX + STACK_BUFFER_DECL(int *, vector, cnt); +#else + int* vector[256]; // Dynamic stack allocation is GNU specific +#endif + for (idx = 0; idx < cnt; idx++) { + int *arg = va_arg(args, int *); + vector[idx] = arg; + } + va_end(args); + return _CFCalendarDecomposeAbsoluteTimeV(calendar, at, componentDesc, vector, cnt); +} + +Boolean CFCalendarAddComponents(CFCalendarRef calendar, /* inout */ CFAbsoluteTime *atp, CFOptionFlags options, const char *componentDesc, ...) { + va_list args; + va_start(args, componentDesc); + CF_OBJC_FUNCDISPATCH4(CFCalendarGetTypeID(), Boolean, calendar, "_addComponents::::", atp, options, componentDesc, args); + __CFGenericValidateType(calendar, CFCalendarGetTypeID()); + int idx, cnt = (int)strlen((char *)componentDesc); +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX + STACK_BUFFER_DECL(int, vector, cnt); +#else + int vector[256]; // Dynamic stack allocation is GNU specific +#endif + for (idx = 0; idx < cnt; idx++) { + int arg = va_arg(args, int); + vector[idx] = arg; + } + va_end(args); + return _CFCalendarAddComponentsV(calendar, atp, options, componentDesc, vector, cnt); +} + +Boolean CFCalendarGetComponentDifference(CFCalendarRef calendar, CFAbsoluteTime startingAT, CFAbsoluteTime resultAT, CFOptionFlags options, const char *componentDesc, ...) { + va_list args; + va_start(args, componentDesc); + CF_OBJC_FUNCDISPATCH5(CFCalendarGetTypeID(), Boolean, calendar, "_diffComponents:::::", startingAT, resultAT, options, componentDesc, args); + __CFGenericValidateType(calendar, CFCalendarGetTypeID()); + int idx, cnt = (int)strlen((char *)componentDesc); +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX + STACK_BUFFER_DECL(int *, vector, cnt); +#else + int* vector[256]; // Dynamic stack allocation is GNU specific +#endif + for (idx = 0; idx < cnt; idx++) { + int *arg = va_arg(args, int *); + vector[idx] = arg; + } + va_end(args); + Boolean ret = _CFCalendarGetComponentDifferenceV(calendar, startingAT, resultAT, options, componentDesc, vector, cnt); + return ret; +} + +Boolean CFCalendarGetTimeRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit, CFAbsoluteTime at, CFAbsoluteTime *startp, CFTimeInterval *tip) { + CF_OBJC_FUNCDISPATCH4(CFCalendarGetTypeID(), Boolean, calendar, "_rangeOfUnit:startTime:interval:forAT:", unit, startp, tip, at); + __CFGenericValidateType(calendar, CFCalendarGetTypeID()); + if (kCFCalendarUnitWeekdayOrdinal == unit) return false; + if (kCFCalendarUnitWeekday == unit) unit = kCFCalendarUnitDay; + if (!calendar->_cal) __CFCalendarSetupCal(calendar); + if (calendar->_cal) { + ucal_clear(calendar->_cal); + __CFCalendarSetToFirstInstant(calendar, unit, at); + UErrorCode status = U_ZERO_ERROR; + UDate start = ucal_getMillis(calendar->_cal, &status); + UCalendarDateFields field = __CFCalendarGetICUFieldCode(unit); + ucal_add(calendar->_cal, field, 1, &status); + UDate end = ucal_getMillis(calendar->_cal, &status); + if (end == start && kCFCalendarUnitEra == unit) { + // ICU refuses to do the addition, probably because we are + // at the limit of UCAL_ERA. Use alternate strategy. + CFIndex limit = ucal_getLimit(calendar->_cal, UCAL_YEAR, UCAL_MAXIMUM, &status); + if (100000 < limit) limit = 100000; + ucal_add(calendar->_cal, UCAL_YEAR, limit, &status); + end = ucal_getMillis(calendar->_cal, &status); + } + if (U_SUCCESS(status)) { + if (startp) *startp = (double)start / 1000.0 - kCFAbsoluteTimeIntervalSince1970; + if (tip) *tip = (double)(end - start) / 1000.0; + return true; + } + } + return false; +} + +#undef BUFFER_SIZE +
diff --git a/CoreFoundation/CFCalendar.h b/CoreFoundation/CFCalendar.h new file mode 100644 index 0000000..31b28c8 --- /dev/null +++ b/CoreFoundation/CFCalendar.h
@@ -0,0 +1,143 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFCalendar.h + Copyright (c) 2004-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFCALENDAR__) +#define __COREFOUNDATION_CFCALENDAR__ 1 + +#include <CoreFoundation/CFBase.h> +#include <CoreFoundation/CFLocale.h> +#include <CoreFoundation/CFDate.h> +#include <CoreFoundation/CFTimeZone.h> + +#if MAC_OS_X_VERSION_10_4 <= MAC_OS_X_VERSION_MAX_ALLOWED + +CF_EXTERN_C_BEGIN + +typedef struct __CFCalendar * CFCalendarRef; + +CF_EXPORT +CFTypeID CFCalendarGetTypeID(void) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +CF_EXPORT +CFCalendarRef CFCalendarCopyCurrent(void) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +CF_EXPORT +CFCalendarRef CFCalendarCreateWithIdentifier(CFAllocatorRef allocator, CFStringRef identifier) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + // Create a calendar. The identifiers are the kCF*Calendar + // constants in CFLocale.h. + +CF_EXPORT +CFStringRef CFCalendarGetIdentifier(CFCalendarRef calendar) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + // Returns the calendar's identifier. + +CF_EXPORT +CFLocaleRef CFCalendarCopyLocale(CFCalendarRef calendar) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +CF_EXPORT +void CFCalendarSetLocale(CFCalendarRef calendar, CFLocaleRef locale) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +CF_EXPORT +CFTimeZoneRef CFCalendarCopyTimeZone(CFCalendarRef calendar) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +CF_EXPORT +void CFCalendarSetTimeZone(CFCalendarRef calendar, CFTimeZoneRef tz) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +CF_EXPORT +CFIndex CFCalendarGetFirstWeekday(CFCalendarRef calendar) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +CF_EXPORT +void CFCalendarSetFirstWeekday(CFCalendarRef calendar, CFIndex wkdy) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +CF_EXPORT +CFIndex CFCalendarGetMinimumDaysInFirstWeek(CFCalendarRef calendar) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +CF_EXPORT +void CFCalendarSetMinimumDaysInFirstWeek(CFCalendarRef calendar, CFIndex mwd) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + + +enum { + kCFCalendarUnitEra = (1 << 1), + kCFCalendarUnitYear = (1 << 2), + kCFCalendarUnitMonth = (1 << 3), + kCFCalendarUnitDay = (1 << 4), + kCFCalendarUnitHour = (1 << 5), + kCFCalendarUnitMinute = (1 << 6), + kCFCalendarUnitSecond = (1 << 7), + kCFCalendarUnitWeek = (1 << 8), + kCFCalendarUnitWeekday = (1 << 9), + kCFCalendarUnitWeekdayOrdinal = (1 << 10) +}; +typedef CFOptionFlags CFCalendarUnit; + +CF_EXPORT +CFRange CFCalendarGetMinimumRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +CF_EXPORT +CFRange CFCalendarGetMaximumRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +CF_EXPORT +CFRange CFCalendarGetRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +CF_EXPORT +CFIndex CFCalendarGetOrdinalityOfUnit(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +CF_EXPORT +Boolean CFCalendarGetTimeRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit, CFAbsoluteTime at, CFAbsoluteTime *startp, CFTimeInterval *tip) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +CF_EXPORT +Boolean CFCalendarComposeAbsoluteTime(CFCalendarRef calendar, /* out */ CFAbsoluteTime *at, const char *componentDesc, ...) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +CF_EXPORT +Boolean CFCalendarDecomposeAbsoluteTime(CFCalendarRef calendar, CFAbsoluteTime at, const char *componentDesc, ...) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + + +enum { + kCFCalendarComponentsWrap = (1 << 0) // option for adding +}; + +CF_EXPORT +Boolean CFCalendarAddComponents(CFCalendarRef calendar, /* inout */ CFAbsoluteTime *at, CFOptionFlags options, const char *componentDesc, ...) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +CF_EXPORT +Boolean CFCalendarGetComponentDifference(CFCalendarRef calendar, CFAbsoluteTime startingAT, CFAbsoluteTime resultAT, CFOptionFlags options, const char *componentDesc, ...) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + + +CF_EXTERN_C_END + +#endif + +#endif /* ! __COREFOUNDATION_CFCALENDAR__ */ +
diff --git a/CoreFoundation/CFCharacterSet.c b/CoreFoundation/CFCharacterSet.c new file mode 100644 index 0000000..67b3f73 --- /dev/null +++ b/CoreFoundation/CFCharacterSet.c
@@ -0,0 +1,2742 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFCharacterSet.c + Copyright 1999-2002, Apple, Inc. All rights reserved. + Responsibility: Aki Inoue +*/ + +#include <CoreFoundation/CFCharacterSet.h> +#include <CoreFoundation/CFByteOrder.h> +#include "CFCharacterSetPriv.h" +#include <CoreFoundation/CFData.h> +#include <CoreFoundation/CFString.h> +#include "CFInternal.h" +#include "CFUniChar.h" +#include "CFUniCharPriv.h" +#include <stdlib.h> +#include <string.h> + + +#define BITSPERBYTE 8 /* (CHAR_BIT * sizeof(unsigned char)) */ +#define LOG_BPB 3 +#define LOG_BPLW 5 +#define NUMCHARACTERS 65536 + +#define MAX_ANNEX_PLANE (16) + +/* Number of things in the array keeping the bits. +*/ +#define __kCFBitmapSize (NUMCHARACTERS / BITSPERBYTE) + +/* How many elements max can be in an __kCFCharSetClassString CFCharacterSet +*/ +#define __kCFStringCharSetMax 64 + +/* The last builtin set ID number +*/ +#define __kCFLastBuiltinSetID kCFCharacterSetNewline + +/* How many elements in the "singles" array before we use binary search. +*/ +#define __kCFSetBreakeven 10 + +/* This tells us, within 1k or so, whether a thing is POTENTIALLY in the set (in the bitmap blob of the private structure) before we bother to do specific checking. +*/ +#define __CFCSetBitsInRange(n, i) (i[n>>15] & (1L << ((n>>10) % 32))) + +/* Compact bitmap params +*/ +#define __kCFCompactBitmapNumPages (256) + +#define __kCFCompactBitmapMaxPages (128) // the max pages allocated + +#define __kCFCompactBitmapPageSize (__kCFBitmapSize / __kCFCompactBitmapNumPages) + +typedef struct { + CFCharacterSetRef *_nonBMPPlanes; + unsigned int _validEntriesBitmap; + unsigned char _numOfAllocEntries; + unsigned char _isAnnexInverted; + uint16_t _padding; +} CFCharSetAnnexStruct; + +struct __CFCharacterSet { + CFRuntimeBase _base; + CFHashCode _hashValue; + union { + struct { + CFIndex _type; + } _builtin; + struct { + UInt32 _firstChar; + CFIndex _length; + } _range; + struct { + UniChar *_buffer; + CFIndex _length; + } _string; + struct { + uint8_t *_bits; + } _bitmap; + struct { + uint8_t *_cBits; + } _compactBitmap; + } _variants; + CFCharSetAnnexStruct *_annex; +}; + +/* _base._info values interesting for CFCharacterSet +*/ +enum { + __kCFCharSetClassTypeMask = 0x0070, + __kCFCharSetClassBuiltin = 0x0000, + __kCFCharSetClassRange = 0x0010, + __kCFCharSetClassString = 0x0020, + __kCFCharSetClassBitmap = 0x0030, + __kCFCharSetClassSet = 0x0040, + __kCFCharSetClassCompactBitmap = 0x0040, + + __kCFCharSetIsInvertedMask = 0x0008, + __kCFCharSetIsInverted = 0x0008, + + __kCFCharSetHasHashValueMask = 0x00004, + __kCFCharSetHasHashValue = 0x0004, + + /* Generic CFBase values */ + __kCFCharSetIsMutableMask = 0x0001, + __kCFCharSetIsMutable = 0x0001, +}; + +/* Inline accessor macros for _base._info +*/ +CF_INLINE Boolean __CFCSetIsMutable(CFCharacterSetRef cset) {return (cset->_base._cfinfo[CF_INFO_BITS] & __kCFCharSetIsMutableMask) == __kCFCharSetIsMutable;} +CF_INLINE Boolean __CFCSetIsBuiltin(CFCharacterSetRef cset) {return (cset->_base._cfinfo[CF_INFO_BITS] & __kCFCharSetClassTypeMask) == __kCFCharSetClassBuiltin;} +CF_INLINE Boolean __CFCSetIsRange(CFCharacterSetRef cset) {return (cset->_base._cfinfo[CF_INFO_BITS] & __kCFCharSetClassTypeMask) == __kCFCharSetClassRange;} +CF_INLINE Boolean __CFCSetIsString(CFCharacterSetRef cset) {return (cset->_base._cfinfo[CF_INFO_BITS] & __kCFCharSetClassTypeMask) == __kCFCharSetClassString;} +CF_INLINE Boolean __CFCSetIsBitmap(CFCharacterSetRef cset) {return (cset->_base._cfinfo[CF_INFO_BITS] & __kCFCharSetClassTypeMask) == __kCFCharSetClassBitmap;} +CF_INLINE Boolean __CFCSetIsCompactBitmap(CFCharacterSetRef cset) {return (cset->_base._cfinfo[CF_INFO_BITS] & __kCFCharSetClassTypeMask) == __kCFCharSetClassCompactBitmap;} +CF_INLINE Boolean __CFCSetIsInverted(CFCharacterSetRef cset) {return (cset->_base._cfinfo[CF_INFO_BITS] & __kCFCharSetIsInvertedMask) == __kCFCharSetIsInverted;} +CF_INLINE Boolean __CFCSetHasHashValue(CFCharacterSetRef cset) {return (cset->_base._cfinfo[CF_INFO_BITS] & __kCFCharSetHasHashValueMask) == __kCFCharSetHasHashValue;} +CF_INLINE UInt32 __CFCSetClassType(CFCharacterSetRef cset) {return (cset->_base._cfinfo[CF_INFO_BITS] & __kCFCharSetClassTypeMask);} + +CF_INLINE void __CFCSetPutIsMutable(CFMutableCharacterSetRef cset, Boolean isMutable) {(isMutable ? (cset->_base._cfinfo[CF_INFO_BITS] |= __kCFCharSetIsMutable) : (cset->_base._cfinfo[CF_INFO_BITS] &= ~ __kCFCharSetIsMutable));} +CF_INLINE void __CFCSetPutIsInverted(CFMutableCharacterSetRef cset, Boolean isInverted) {(isInverted ? (cset->_base._cfinfo[CF_INFO_BITS] |= __kCFCharSetIsInverted) : (cset->_base._cfinfo[CF_INFO_BITS] &= ~__kCFCharSetIsInverted));} +CF_INLINE void __CFCSetPutHasHashValue(CFMutableCharacterSetRef cset, Boolean hasHash) {(hasHash ? (cset->_base._cfinfo[CF_INFO_BITS] |= __kCFCharSetHasHashValue) : (cset->_base._cfinfo[CF_INFO_BITS] &= ~__kCFCharSetHasHashValue));} +CF_INLINE void __CFCSetPutClassType(CFMutableCharacterSetRef cset, UInt32 classType) {cset->_base._cfinfo[CF_INFO_BITS] &= ~__kCFCharSetClassTypeMask; cset->_base._cfinfo[CF_INFO_BITS] |= classType;} + + +/* Inline contents accessor macros +*/ +CF_INLINE CFCharacterSetPredefinedSet __CFCSetBuiltinType(CFCharacterSetRef cset) {return cset->_variants._builtin._type;} +CF_INLINE UInt32 __CFCSetRangeFirstChar(CFCharacterSetRef cset) {return cset->_variants._range._firstChar;} +CF_INLINE CFIndex __CFCSetRangeLength(CFCharacterSetRef cset) {return cset->_variants._range._length;} +CF_INLINE UniChar *__CFCSetStringBuffer(CFCharacterSetRef cset) {return (UniChar*)(cset->_variants._string._buffer);} +CF_INLINE CFIndex __CFCSetStringLength(CFCharacterSetRef cset) {return cset->_variants._string._length;} +CF_INLINE uint8_t *__CFCSetBitmapBits(CFCharacterSetRef cset) {return cset->_variants._bitmap._bits;} +CF_INLINE uint8_t *__CFCSetCompactBitmapBits(CFCharacterSetRef cset) {return cset->_variants._compactBitmap._cBits;} + +CF_INLINE void __CFCSetPutBuiltinType(CFMutableCharacterSetRef cset, CFCharacterSetPredefinedSet type) {cset->_variants._builtin._type = type;} +CF_INLINE void __CFCSetPutRangeFirstChar(CFMutableCharacterSetRef cset, UInt32 first) {cset->_variants._range._firstChar = first;} +CF_INLINE void __CFCSetPutRangeLength(CFMutableCharacterSetRef cset, CFIndex length) {cset->_variants._range._length = length;} +CF_INLINE void __CFCSetPutStringBuffer(CFMutableCharacterSetRef cset, UniChar *theBuffer) {cset->_variants._string._buffer = theBuffer;} +CF_INLINE void __CFCSetPutStringLength(CFMutableCharacterSetRef cset, CFIndex length) {cset->_variants._string._length = length;} +CF_INLINE void __CFCSetPutBitmapBits(CFMutableCharacterSetRef cset, uint8_t *bits) {cset->_variants._bitmap._bits = bits;} +CF_INLINE void __CFCSetPutCompactBitmapBits(CFMutableCharacterSetRef cset, uint8_t *bits) {cset->_variants._compactBitmap._cBits = bits;} + +/* Validation funcs +*/ +#if defined(CF_ENABLE_ASSERTIONS) +CF_INLINE void __CFCSetValidateBuiltinType(CFCharacterSetPredefinedSet type, const char *func) { + CFAssert2(type > 0 && type <= __kCFLastBuiltinSetID, __kCFLogAssertion, "%s: Unknowen builtin type %d", func, type); +} +CF_INLINE void __CFCSetValidateRange(CFRange theRange, const char *func) { + CFAssert3(theRange.location >= 0 && theRange.location + theRange.length <= 0x1FFFFF, __kCFLogAssertion, "%s: Range out of Unicode range (location -> %d length -> %d)", func, theRange.location, theRange.length); +} +CF_INLINE void __CFCSetValidateTypeAndMutability(CFCharacterSetRef cset, const char *func) { + __CFGenericValidateType(cset, __kCFCharacterSetTypeID); + CFAssert1(__CFCSetIsMutable(cset), __kCFLogAssertion, "%s: Immutable character set passed to mutable function", func); +} +#else +#define __CFCSetValidateBuiltinType(t,f) +#define __CFCSetValidateRange(r,f) +#define __CFCSetValidateTypeAndMutability(r,f) +#endif + +/* Inline utility funcs +*/ +static Boolean __CFCSetIsEqualBitmap(const UInt32 *bits1, const UInt32 *bits2) { + CFIndex length = __kCFBitmapSize / sizeof(UInt32); + + if (bits1 == bits2) { + return true; + } else if (bits1 && bits2) { + if (bits1 == (const UInt32 *)-1) { + while (length--) if ((UInt32)-1 != *bits2++) return false; + } else if (bits2 == (const UInt32 *)-1) { + while (length--) if ((UInt32)-1 != *bits1++) return false; + } else { + while (length--) if (*bits1++ != *bits2++) return false; + } + return true; + } else if (!bits1 && !bits2) { // empty set + return true; + } else { + if (bits2) bits1 = bits2; + if (bits1 == (const UInt32 *)-1) return false; + while (length--) if (*bits1++) return false; + return true; + } +} + +CF_INLINE Boolean __CFCSetIsEqualBitmapInverted(const UInt32 *bits1, const UInt32 *bits2) { + CFIndex length = __kCFBitmapSize / sizeof(UInt32); + + while (length--) if (*bits1++ != ~(*(bits2++))) return false; + return true; +} + +static Boolean __CFCSetIsBitmapEqualToRange(const UInt32 *bits, UniChar firstChar, UniChar lastChar, Boolean isInverted) { + CFIndex firstCharIndex = firstChar >> LOG_BPB; + CFIndex lastCharIndex = lastChar >> LOG_BPB; + CFIndex length; + UInt32 value; + + if (firstCharIndex == lastCharIndex) { + value = ((((UInt32)0xFF) << (firstChar & (BITSPERBYTE - 1))) & (((UInt32)0xFF) >> ((BITSPERBYTE - 1) - (lastChar & (BITSPERBYTE - 1))))) << (((sizeof(UInt32) - 1) - (firstCharIndex % sizeof(UInt32))) * BITSPERBYTE); + value = CFSwapInt32HostToBig(value); + firstCharIndex = lastCharIndex = firstChar >> LOG_BPLW; + if (*(bits + firstCharIndex) != (isInverted ? ~value : value)) return FALSE; + } else { + UInt32 firstCharMask; + UInt32 lastCharMask; + + length = firstCharIndex % sizeof(UInt32); + firstCharMask = (((((UInt32)0xFF) << (firstChar & (BITSPERBYTE - 1))) & 0xFF) << (((sizeof(UInt32) - 1) - length) * BITSPERBYTE)) | (((UInt32)0xFFFFFFFF) >> ((length + 1) * BITSPERBYTE)); + + length = lastCharIndex % sizeof(UInt32); + lastCharMask = ((((UInt32)0xFF) >> ((BITSPERBYTE - 1) - (lastChar & (BITSPERBYTE - 1)))) << (((sizeof(UInt32) - 1) - length) * BITSPERBYTE)) | (((UInt32)0xFFFFFFFF) << ((sizeof(UInt32) - length) * BITSPERBYTE)); + + firstCharIndex = firstChar >> LOG_BPLW; + lastCharIndex = lastChar >> LOG_BPLW; + + if (firstCharIndex == lastCharIndex) { + firstCharMask &= lastCharMask; + value = CFSwapInt32HostToBig(firstCharMask & lastCharMask); + if (*(bits + firstCharIndex) != (isInverted ? ~value : value)) return FALSE; + } else { + value = CFSwapInt32HostToBig(firstCharMask); + if (*(bits + firstCharIndex) != (isInverted ? ~value : value)) return FALSE; + + value = CFSwapInt32HostToBig(lastCharMask); + if (*(bits + lastCharIndex) != (isInverted ? ~value : value)) return FALSE; + } + } + + length = firstCharIndex; + value = (isInverted ? ((UInt32)0xFFFFFFFF) : 0); + while (length--) { + if (*(bits++) != value) return FALSE; + } + + ++bits; // Skip firstCharIndex + length = (lastCharIndex - (firstCharIndex + 1)); + value = (isInverted ? 0 : ((UInt32)0xFFFFFFFF)); + while (length-- > 0) { + if (*(bits++) != value) return FALSE; + } + if (firstCharIndex != lastCharIndex) ++bits; + + length = (0xFFFF >> LOG_BPLW) - lastCharIndex; + value = (isInverted ? ((UInt32)0xFFFFFFFF) : 0); + while (length--) { + if (*(bits++) != value) return FALSE; + } + + return TRUE; +} + +CF_INLINE Boolean __CFCSetIsBitmapSupersetOfBitmap(const UInt32 *bits1, const UInt32 *bits2, Boolean isInverted1, Boolean isInverted2) { + CFIndex length = __kCFBitmapSize / sizeof(UInt32); + UInt32 val1, val2; + + while (length--) { + val2 = (isInverted2 ? ~(*(bits2++)) : *(bits2++)); + val1 = (isInverted1 ? ~(*(bits1++)) : *(bits1++)) & val2; + if (val1 != val2) return false; + } + + return true; +} + +CF_INLINE Boolean __CFCSetHasNonBMPPlane(CFCharacterSetRef cset) { return ((cset)->_annex && (cset)->_annex->_validEntriesBitmap ? true : false); } +CF_INLINE Boolean __CFCSetAnnexIsInverted (CFCharacterSetRef cset) { return ((cset)->_annex && (cset)->_annex->_isAnnexInverted ? true : false); } +CF_INLINE UInt32 __CFCSetAnnexValidEntriesBitmap(CFCharacterSetRef cset) { return ((cset)->_annex ? (cset)->_annex->_validEntriesBitmap : 0); } + +CF_INLINE Boolean __CFCSetIsEmpty(CFCharacterSetRef cset) { + if (__CFCSetHasNonBMPPlane(cset) || __CFCSetAnnexIsInverted(cset)) return false; + + switch (__CFCSetClassType(cset)) { + case __kCFCharSetClassRange: if (!__CFCSetRangeLength(cset)) return true; break; + case __kCFCharSetClassString: if (!__CFCSetStringLength(cset)) return true; break; + case __kCFCharSetClassBitmap: if (!__CFCSetBitmapBits(cset)) return true; break; + case __kCFCharSetClassCompactBitmap: if (!__CFCSetCompactBitmapBits(cset)) return true; break; + } + return false; +} + +CF_INLINE void __CFCSetBitmapAddCharacter(uint8_t *bitmap, UniChar theChar) { + bitmap[(theChar) >> LOG_BPB] |= (((unsigned)1) << (theChar & (BITSPERBYTE - 1))); +} + +CF_INLINE void __CFCSetBitmapRemoveCharacter(uint8_t *bitmap, UniChar theChar) { + bitmap[(theChar) >> LOG_BPB] &= ~(((unsigned)1) << (theChar & (BITSPERBYTE - 1))); +} + +CF_INLINE Boolean __CFCSetIsMemberBitmap(const uint8_t *bitmap, UniChar theChar) { + return ((bitmap[(theChar) >> LOG_BPB] & (((unsigned)1) << (theChar & (BITSPERBYTE - 1)))) ? true : false); +} + +#define NUM_32BIT_SLOTS (NUMCHARACTERS / 32) + +CF_INLINE void __CFCSetBitmapFastFillWithValue(UInt32 *bitmap, uint8_t value) { + UInt32 mask = (value << 24) | (value << 16) | (value << 8) | value; + UInt32 numSlots = NUMCHARACTERS / 32; + + while (numSlots--) *(bitmap++) = mask; +} + +CF_INLINE void __CFCSetBitmapAddCharactersInRange(uint8_t *bitmap, UniChar firstChar, UniChar lastChar) { + if (firstChar == lastChar) { + bitmap[firstChar >> LOG_BPB] |= (((unsigned)1) << (firstChar & (BITSPERBYTE - 1))); + } else { + UInt32 idx = firstChar >> LOG_BPB; + UInt32 max = lastChar >> LOG_BPB; + + if (idx == max) { + bitmap[idx] |= (((unsigned)0xFF) << (firstChar & (BITSPERBYTE - 1))) & (((unsigned)0xFF) >> ((BITSPERBYTE - 1) - (lastChar & (BITSPERBYTE - 1)))); + } else { + bitmap[idx] |= (((unsigned)0xFF) << (firstChar & (BITSPERBYTE - 1))); + bitmap[max] |= (((unsigned)0xFF) >> ((BITSPERBYTE - 1) - (lastChar & (BITSPERBYTE - 1)))); + + ++idx; + while (idx < max) bitmap[idx++] = 0xFF; + } + } +} + +CF_INLINE void __CFCSetBitmapRemoveCharactersInRange(uint8_t *bitmap, UniChar firstChar, UniChar lastChar) { + UInt32 idx = firstChar >> LOG_BPB; + UInt32 max = lastChar >> LOG_BPB; + + if (idx == max) { + bitmap[idx] &= ~((((unsigned)0xFF) << (firstChar & (BITSPERBYTE - 1))) & (((unsigned)0xFF) >> ((BITSPERBYTE - 1) - (lastChar & (BITSPERBYTE - 1))))); + } else { + bitmap[idx] &= ~(((unsigned)0xFF) << (firstChar & (BITSPERBYTE - 1))); + bitmap[max] &= ~(((unsigned)0xFF) >> ((BITSPERBYTE - 1) - (lastChar & (BITSPERBYTE - 1)))); + + ++idx; + while (idx < max) bitmap[idx++] = 0; + } +} + +#define __CFCSetAnnexBitmapSetPlane(bitmap,plane) ((bitmap) |= (1 << (plane))) +#define __CFCSetAnnexBitmapClearPlane(bitmap,plane) ((bitmap) &= (~(1 << (plane)))) +#define __CFCSetAnnexBitmapGetPlane(bitmap,plane) ((bitmap) & (1 << (plane))) + +CF_INLINE void __CFCSetAllocateAnnexForPlane(CFCharacterSetRef cset, int plane) { + if (cset->_annex == NULL) { + ((CFMutableCharacterSetRef)cset)->_annex = (CFCharSetAnnexStruct *)CFAllocatorAllocate(CFGetAllocator(cset), sizeof(CFCharSetAnnexStruct), 0); + cset->_annex->_numOfAllocEntries = plane; + cset->_annex->_isAnnexInverted = false; + cset->_annex->_validEntriesBitmap = 0; + cset->_annex->_nonBMPPlanes = ((plane > 0) ? (CFCharacterSetRef*)CFAllocatorAllocate(CFGetAllocator(cset), sizeof(CFCharacterSetRef) * plane, 0) : NULL); + } else if (cset->_annex->_numOfAllocEntries < plane) { + cset->_annex->_numOfAllocEntries = plane; + if (NULL == cset->_annex->_nonBMPPlanes) { + cset->_annex->_nonBMPPlanes = (CFCharacterSetRef*)CFAllocatorAllocate(CFGetAllocator(cset), sizeof(CFCharacterSetRef) * plane, 0); + } else { + cset->_annex->_nonBMPPlanes = (CFCharacterSetRef*)CFAllocatorReallocate(CFGetAllocator(cset), (void *)cset->_annex->_nonBMPPlanes, sizeof(CFCharacterSetRef) * plane, 0); + } + } +} + +CF_INLINE void __CFCSetAnnexSetIsInverted(CFCharacterSetRef cset, Boolean flag) { + if (flag) __CFCSetAllocateAnnexForPlane(cset, 0); + if (cset->_annex) ((CFMutableCharacterSetRef)cset)->_annex->_isAnnexInverted = flag; +} + +CF_INLINE void __CFCSetPutCharacterSetToAnnexPlane(CFCharacterSetRef cset, CFCharacterSetRef annexCSet, int plane) { + __CFCSetAllocateAnnexForPlane(cset, plane); + if (__CFCSetAnnexBitmapGetPlane(cset->_annex->_validEntriesBitmap, plane)) CFRelease(cset->_annex->_nonBMPPlanes[plane - 1]); + if (annexCSet) { + cset->_annex->_nonBMPPlanes[plane - 1] = (CFCharacterSetRef)CFRetain(annexCSet); + __CFCSetAnnexBitmapSetPlane(cset->_annex->_validEntriesBitmap, plane); + } else { + __CFCSetAnnexBitmapClearPlane(cset->_annex->_validEntriesBitmap, plane); + } +} + +CF_INLINE CFCharacterSetRef __CFCSetGetAnnexPlaneCharacterSet(CFCharacterSetRef cset, int plane) { + __CFCSetAllocateAnnexForPlane(cset, plane); + if (!__CFCSetAnnexBitmapGetPlane(cset->_annex->_validEntriesBitmap, plane)) { + cset->_annex->_nonBMPPlanes[plane - 1] = (CFCharacterSetRef)CFCharacterSetCreateMutable(CFGetAllocator(cset)); + __CFCSetAnnexBitmapSetPlane(cset->_annex->_validEntriesBitmap, plane); + } + return cset->_annex->_nonBMPPlanes[plane - 1]; +} + +CF_INLINE CFCharacterSetRef __CFCSetGetAnnexPlaneCharacterSetNoAlloc(CFCharacterSetRef cset, int plane) { + return (cset->_annex && __CFCSetAnnexBitmapGetPlane(cset->_annex->_validEntriesBitmap, plane) ? cset->_annex->_nonBMPPlanes[plane - 1] : NULL); +} + +CF_INLINE void __CFCSetDeallocateAnnexPlane(CFCharacterSetRef cset) { + if (cset->_annex) { + int idx; + + for (idx = 0;idx < MAX_ANNEX_PLANE;idx++) { + if (__CFCSetAnnexBitmapGetPlane(cset->_annex->_validEntriesBitmap, idx + 1)) { + CFRelease(cset->_annex->_nonBMPPlanes[idx]); + } + } + CFAllocatorDeallocate(CFGetAllocator(cset), cset->_annex->_nonBMPPlanes); + CFAllocatorDeallocate(CFGetAllocator(cset), cset->_annex); + ((CFMutableCharacterSetRef)cset)->_annex = NULL; + } +} + +CF_INLINE uint8_t __CFCSetGetHeaderValue(const uint8_t *bitmap, int *numPages) { + uint8_t value = *bitmap; + + if ((value == 0) || (value == UINT8_MAX)) { + int numBytes = __kCFCompactBitmapPageSize - 1; + + while (numBytes > 0) { + if (*(++bitmap) != value) break; + --numBytes; + } + if (numBytes == 0) return value; + } + return (uint8_t)(++(*numPages)); +} + +CF_INLINE bool __CFCSetIsMemberInCompactBitmap(const uint8_t *compactBitmap, UTF16Char character) { + uint8_t value = compactBitmap[(character >> 8)]; // Assuming __kCFCompactBitmapNumPages == 256 + + if (value == 0) { + return false; + } else if (value == UINT8_MAX) { + return true; + } else { + compactBitmap += (__kCFCompactBitmapNumPages + (__kCFCompactBitmapPageSize * (value - 1))); + character &= 0xFF; // Assuming __kCFCompactBitmapNumPages == 256 + return ((compactBitmap[(character / BITSPERBYTE)] & (1 << (character % BITSPERBYTE))) ? true : false); + } +} + +CF_INLINE uint32_t __CFCSetGetCompactBitmapSize(const uint8_t *compactBitmap) { + uint32_t length = __kCFCompactBitmapNumPages; + uint32_t size = __kCFCompactBitmapNumPages; + uint8_t value; + + while (length-- > 0) { + value = *(compactBitmap++); + if ((value != 0) && (value != UINT8_MAX)) size += __kCFCompactBitmapPageSize; + } + return size; +} + +/* Take a private "set" structure and make a bitmap from it. Return the bitmap. THE CALLER MUST RELEASE THE RETURNED MEMORY as necessary. +*/ + +CF_INLINE void __CFCSetBitmapProcessManyCharacters(unsigned char *map, unsigned n, unsigned m, Boolean isInverted) { + if (isInverted) { + __CFCSetBitmapRemoveCharactersInRange(map, n, m); + } else { + __CFCSetBitmapAddCharactersInRange(map, n, m); + } +} + +CF_INLINE void __CFExpandCompactBitmap(const uint8_t *src, uint8_t *dst) { + const uint8_t *srcBody = src + __kCFCompactBitmapNumPages; + int i; + uint8_t value; + + for (i = 0;i < __kCFCompactBitmapNumPages;i++) { + value = *(src++); + if ((value == 0) || (value == UINT8_MAX)) { + memset(dst, value, __kCFCompactBitmapPageSize); + } else { + memmove(dst, srcBody, __kCFCompactBitmapPageSize); + srcBody += __kCFCompactBitmapPageSize; + } + dst += __kCFCompactBitmapPageSize; + } +} + + +static void __CFCheckForExpandedSet(CFCharacterSetRef cset) { + static int8_t __CFNumberOfPlanesForLogging = -1; + static bool warnedOnce = false; + + if (0 > __CFNumberOfPlanesForLogging) { + const char *envVar = getenv("CFCharacterSetCheckForExpandedSet"); +#if DEPLOYMENT_TARGET_MACOSX + long value = (envVar ? strtol_l(envVar, NULL, 0, NULL) : 0); +#else + long value = (envVar ? strtol(envVar, NULL, 0) : 0); +#endif + __CFNumberOfPlanesForLogging = (int8_t)(((value > 0) && (value <= 16)) ? value : 0); + } + + if (__CFNumberOfPlanesForLogging) { + uint32_t entries = __CFCSetAnnexValidEntriesBitmap(cset); + int count = 0; + + while (entries) { + if ((entries & 1) && (++count >= __CFNumberOfPlanesForLogging)) { + if (!warnedOnce) { + CFLog(kCFLogLevelWarning, CFSTR("An expanded CFMutableCharacter has been detected. Recommend to compact with CFCharacterSetCreateCopy")); + warnedOnce = true; + } + break; + } + entries >>= 1; + } + } +} + +static void __CFCSetGetBitmap(CFCharacterSetRef cset, uint8_t *bits) { + uint8_t *bitmap; + CFIndex length = __kCFBitmapSize; + + if (__CFCSetIsBitmap(cset) && (bitmap = __CFCSetBitmapBits(cset))) { + memmove(bits, bitmap, __kCFBitmapSize); + } else { + Boolean isInverted = __CFCSetIsInverted(cset); + uint8_t value = (isInverted ? (uint8_t)-1 : 0); + + bitmap = bits; + while (length--) *bitmap++ = value; // Initialize the buffer + + if (!__CFCSetIsEmpty(cset)) { + switch (__CFCSetClassType(cset)) { + case __kCFCharSetClassBuiltin: { + UInt8 result = CFUniCharGetBitmapForPlane(__CFCSetBuiltinType(cset), 0, bits, (isInverted != 0)); + if (result == kCFUniCharBitmapEmpty && isInverted) { + length = __kCFBitmapSize; + bitmap = bits; + while (length--) *bitmap++ = 0; + } else if (result == kCFUniCharBitmapAll && !isInverted) { + length = __kCFBitmapSize; + bitmap = bits; + while (length--) *bitmap++ = (UInt8)0xFF; + } + } + break; + + case __kCFCharSetClassRange: { + UInt32 theChar = __CFCSetRangeFirstChar(cset); + if (theChar < NUMCHARACTERS) { // the range starts in BMP + length = __CFCSetRangeLength(cset); + if (theChar + length >= NUMCHARACTERS) length = NUMCHARACTERS - theChar; + if (isInverted) { + __CFCSetBitmapRemoveCharactersInRange(bits, theChar, (UniChar)(theChar + length) - 1); + } else { + __CFCSetBitmapAddCharactersInRange(bits, theChar, (UniChar)(theChar + length) - 1); + } + } + } + break; + + case __kCFCharSetClassString: { + const UniChar *buffer = __CFCSetStringBuffer(cset); + length = __CFCSetStringLength(cset); + while (length--) (isInverted ? __CFCSetBitmapRemoveCharacter(bits, *buffer++) : __CFCSetBitmapAddCharacter(bits, *buffer++)); + } + break; + + case __kCFCharSetClassCompactBitmap: + __CFExpandCompactBitmap(__CFCSetCompactBitmapBits(cset), bits); + break; + } + } + } +} + +static Boolean __CFCharacterSetEqual(CFTypeRef cf1, CFTypeRef cf2); + +static Boolean __CFCSetIsEqualAnnex(CFCharacterSetRef cf1, CFCharacterSetRef cf2) { + CFCharacterSetRef subSet1; + CFCharacterSetRef subSet2; + Boolean isAnnexInvertStateIdentical = (__CFCSetAnnexIsInverted(cf1) == __CFCSetAnnexIsInverted(cf2) ? true: false); + int idx; + + if (isAnnexInvertStateIdentical) { + if (__CFCSetAnnexValidEntriesBitmap(cf1) != __CFCSetAnnexValidEntriesBitmap(cf2)) return false; + for (idx = 1;idx <= MAX_ANNEX_PLANE;idx++) { + subSet1 = __CFCSetGetAnnexPlaneCharacterSetNoAlloc(cf1, idx); + subSet2 = __CFCSetGetAnnexPlaneCharacterSetNoAlloc(cf2, idx); + + if (subSet1 && !__CFCharacterSetEqual(subSet1, subSet2)) return false; + } + } else { + uint8_t bitsBuf[__kCFBitmapSize]; + uint8_t bitsBuf2[__kCFBitmapSize]; + + for (idx = 1;idx <= MAX_ANNEX_PLANE;idx++) { + subSet1 = __CFCSetGetAnnexPlaneCharacterSetNoAlloc(cf1, idx); + subSet2 = __CFCSetGetAnnexPlaneCharacterSetNoAlloc(cf2, idx); + + if (subSet1 == NULL && subSet2 == NULL) { + return false; + } else if (subSet1 == NULL) { + if (__CFCSetIsBitmap(subSet2)) { + if (!__CFCSetIsEqualBitmap((const UInt32 *)__CFCSetBitmapBits(subSet2), (const UInt32 *)-1)) { + return false; + } + } else { + __CFCSetGetBitmap(subSet2, bitsBuf); + if (!__CFCSetIsEqualBitmap((const UInt32 *)bitsBuf, (const UInt32 *)-1)) { + return false; + } + } + } else if (subSet2 == NULL) { + if (__CFCSetIsBitmap(subSet1)) { + if (!__CFCSetIsEqualBitmap((const UInt32 *)__CFCSetBitmapBits(subSet1), (const UInt32 *)-1)) { + return false; + } + } else { + __CFCSetGetBitmap(subSet1, bitsBuf); + if (!__CFCSetIsEqualBitmap((const UInt32 *)bitsBuf, (const UInt32 *)-1)) { + return false; + } + } + } else { + Boolean isBitmap1 = __CFCSetIsBitmap(subSet1); + Boolean isBitmap2 = __CFCSetIsBitmap(subSet2); + + if (isBitmap1 && isBitmap2) { + if (!__CFCSetIsEqualBitmapInverted((const UInt32 *)__CFCSetBitmapBits(subSet1), (const UInt32 *)__CFCSetBitmapBits(subSet2))) { + return false; + } + } else if (!isBitmap1 && !isBitmap2) { + __CFCSetGetBitmap(subSet1, bitsBuf); + __CFCSetGetBitmap(subSet2, bitsBuf2); + if (!__CFCSetIsEqualBitmapInverted((const UInt32 *)bitsBuf, (const UInt32 *)bitsBuf2)) { + return false; + } + } else { + if (isBitmap2) { + CFCharacterSetRef tmp = subSet2; + subSet2 = subSet1; + subSet1 = tmp; + } + __CFCSetGetBitmap(subSet2, bitsBuf); + if (!__CFCSetIsEqualBitmapInverted((const UInt32 *)__CFCSetBitmapBits(subSet1), (const UInt32 *)bitsBuf)) { + return false; + } + } + } + } + } + return true; +} + +/* Compact bitmap +*/ +static uint8_t *__CFCreateCompactBitmap(CFAllocatorRef allocator, const uint8_t *bitmap) { + const uint8_t *src; + uint8_t *dst; + int i; + int numPages = 0; + uint8_t header[__kCFCompactBitmapNumPages]; + + src = bitmap; + for (i = 0;i < __kCFCompactBitmapNumPages;i++) { + header[i] = __CFCSetGetHeaderValue(src, &numPages); + + // Allocating more pages is probably not interesting enough to be compact + if (numPages > __kCFCompactBitmapMaxPages) return NULL; + src += __kCFCompactBitmapPageSize; + } + + dst = (uint8_t *)CFAllocatorAllocate(allocator, __kCFCompactBitmapNumPages + (__kCFCompactBitmapPageSize * numPages), 0); + + if (numPages > 0) { + uint8_t *dstBody = dst + __kCFCompactBitmapNumPages; + + src = bitmap; + for (i = 0;i < __kCFCompactBitmapNumPages;i++) { + dst[i] = header[i]; + + if ((dst[i] != 0) && (dst[i] != UINT8_MAX)) { + memmove(dstBody, src, __kCFCompactBitmapPageSize); + dstBody += __kCFCompactBitmapPageSize; + } + src += __kCFCompactBitmapPageSize; + } + } else { + memmove(dst, header, __kCFCompactBitmapNumPages); + } + + return dst; +} + +static void __CFCSetMakeCompact(CFMutableCharacterSetRef cset) { + if (__CFCSetIsBitmap(cset) && __CFCSetBitmapBits(cset)) { + uint8_t *bitmap = __CFCSetBitmapBits(cset); + uint8_t *cBitmap = __CFCreateCompactBitmap(CFGetAllocator(cset), bitmap); + + if (cBitmap) { + CFAllocatorDeallocate(CFGetAllocator(cset), bitmap); + __CFCSetPutClassType(cset, __kCFCharSetClassCompactBitmap); + __CFCSetPutCompactBitmapBits(cset, cBitmap); + } + } +} + +static void __CFCSetAddNonBMPPlanesInRange(CFMutableCharacterSetRef cset, CFRange range) { + int firstChar = (range.location & 0xFFFF); + int maxChar = range.location + range.length; + int idx = range.location >> 16; // first plane + int maxPlane = (maxChar - 1) >> 16; // last plane + CFRange planeRange; + CFMutableCharacterSetRef annexPlane; + + maxChar &= 0xFFFF; + + for (idx = (idx ? idx : 1);idx <= maxPlane;idx++) { + planeRange.location = __CFMax(firstChar, 0); + planeRange.length = (idx == maxPlane && maxChar ? maxChar : 0x10000) - planeRange.location; + if (__CFCSetAnnexIsInverted(cset)) { + if ((annexPlane = (CFMutableCharacterSetRef)__CFCSetGetAnnexPlaneCharacterSetNoAlloc(cset, idx))) { + CFCharacterSetRemoveCharactersInRange(annexPlane, planeRange); + if (__CFCSetIsEmpty(annexPlane) && !__CFCSetIsInverted(annexPlane)) { + CFRelease(annexPlane); + __CFCSetAnnexBitmapClearPlane(cset->_annex->_validEntriesBitmap, idx); + } + } + } else { + CFCharacterSetAddCharactersInRange((CFMutableCharacterSetRef)__CFCSetGetAnnexPlaneCharacterSet(cset, idx), planeRange); + } + } + if (!__CFCSetHasNonBMPPlane(cset) && !__CFCSetAnnexIsInverted(cset)) __CFCSetDeallocateAnnexPlane(cset); +} + +static void __CFCSetRemoveNonBMPPlanesInRange(CFMutableCharacterSetRef cset, CFRange range) { + int firstChar = (range.location & 0xFFFF); + int maxChar = range.location + range.length; + int idx = range.location >> 16; // first plane + int maxPlane = (maxChar - 1) >> 16; // last plane + CFRange planeRange; + CFMutableCharacterSetRef annexPlane; + + maxChar &= 0xFFFF; + + for (idx = (idx ? idx : 1);idx <= maxPlane;idx++) { + planeRange.location = __CFMax(firstChar, 0); + planeRange.length = (idx == maxPlane && maxChar ? maxChar : 0x10000) - planeRange.location; + if (__CFCSetAnnexIsInverted(cset)) { + CFCharacterSetAddCharactersInRange((CFMutableCharacterSetRef)__CFCSetGetAnnexPlaneCharacterSet(cset, idx), planeRange); + } else { + if ((annexPlane = (CFMutableCharacterSetRef)__CFCSetGetAnnexPlaneCharacterSetNoAlloc(cset, idx))) { + CFCharacterSetRemoveCharactersInRange(annexPlane, planeRange); + if(__CFCSetIsEmpty(annexPlane) && !__CFCSetIsInverted(annexPlane)) { + CFRelease(annexPlane); + __CFCSetAnnexBitmapClearPlane(cset->_annex->_validEntriesBitmap, idx); + } + } + } + } + if (!__CFCSetHasNonBMPPlane(cset) && !__CFCSetAnnexIsInverted(cset)) __CFCSetDeallocateAnnexPlane(cset); +} + +static void __CFCSetMakeBitmap(CFMutableCharacterSetRef cset) { + if (!__CFCSetIsBitmap(cset) || !__CFCSetBitmapBits(cset)) { + CFAllocatorRef allocator = CFGetAllocator(cset); + uint8_t *bitmap = (uint8_t *)CFAllocatorAllocate(allocator, __kCFBitmapSize, 0); + __CFCSetGetBitmap(cset, bitmap); + + if (__CFCSetIsBuiltin(cset)) { + CFIndex numPlanes = CFUniCharGetNumberOfPlanes(__CFCSetBuiltinType(cset)); + + if (numPlanes > 1) { + CFMutableCharacterSetRef annexSet; + uint8_t *annexBitmap = NULL; + int idx; + UInt8 result; + + __CFCSetAllocateAnnexForPlane(cset, numPlanes - 1); + for (idx = 1;idx < numPlanes;idx++) { + if (NULL == annexBitmap) { + annexBitmap = (uint8_t *)CFAllocatorAllocate(allocator, __kCFBitmapSize, 0); + } + result = CFUniCharGetBitmapForPlane(__CFCSetBuiltinType(cset), idx, annexBitmap, false); + if (result == kCFUniCharBitmapEmpty) continue; + if (result == kCFUniCharBitmapAll) { + CFIndex bitmapLength = __kCFBitmapSize; + uint8_t *bytes = annexBitmap; + while (bitmapLength-- > 0) *(bytes++) = (uint8_t)0xFF; + } + annexSet = (CFMutableCharacterSetRef)__CFCSetGetAnnexPlaneCharacterSet(cset, idx); + __CFCSetPutClassType(annexSet, __kCFCharSetClassBitmap); + __CFCSetPutBitmapBits(annexSet, annexBitmap); + __CFCSetPutIsInverted(annexSet, false); + __CFCSetPutHasHashValue(annexSet, false); + annexBitmap = NULL; + } + if (annexBitmap) CFAllocatorDeallocate(allocator, annexBitmap); + } + } else if (__CFCSetIsCompactBitmap(cset) && __CFCSetCompactBitmapBits(cset)) { + CFAllocatorDeallocate(allocator, __CFCSetCompactBitmapBits(cset)); + __CFCSetPutCompactBitmapBits(cset, NULL); + } else if (__CFCSetIsString(cset) && __CFCSetStringBuffer(cset)) { + CFAllocatorDeallocate(allocator, __CFCSetStringBuffer(cset)); + __CFCSetPutStringBuffer(cset, NULL); + } else if (__CFCSetIsRange(cset)) { // We may have to allocate annex here + Boolean needsToInvert = (!__CFCSetHasNonBMPPlane(cset) && __CFCSetIsInverted(cset) ? true : false); + __CFCSetAddNonBMPPlanesInRange(cset, CFRangeMake(__CFCSetRangeFirstChar(cset), __CFCSetRangeLength(cset))); + if (needsToInvert) __CFCSetAnnexSetIsInverted(cset, true); + } + __CFCSetPutClassType(cset, __kCFCharSetClassBitmap); + __CFCSetPutBitmapBits(cset, bitmap); + __CFCSetPutIsInverted(cset, false); + } +} + +CF_INLINE CFMutableCharacterSetRef __CFCSetGenericCreate(CFAllocatorRef allocator, UInt32 flags) { + CFMutableCharacterSetRef cset; + CFIndex size = sizeof(struct __CFCharacterSet) - sizeof(CFRuntimeBase); + + cset = (CFMutableCharacterSetRef)_CFRuntimeCreateInstance(allocator, CFCharacterSetGetTypeID(), size, NULL); + if (NULL == cset) return NULL; + + cset->_base._cfinfo[CF_INFO_BITS] |= flags; + cset->_hashValue = 0; + cset->_annex = NULL; + + return cset; +} + +/* Bsearch theChar for __kCFCharSetClassString +*/ +CF_INLINE Boolean __CFCSetBsearchUniChar(const UniChar *theTable, CFIndex length, UniChar theChar) { + const UniChar *p, *q, *divider; + + if ((theChar < theTable[0]) || (theChar > theTable[length - 1])) return false; + + p = theTable; + q = p + (length - 1); + while (p <= q) { + divider = p + ((q - p) >> 1); /* divide by 2 */ + if (theChar < *divider) q = divider - 1; + else if (theChar > *divider) p = divider + 1; + else return true; + } + return false; +} + +/* Predefined cset names + Need to add entry here for new builtin types +*/ +CONST_STRING_DECL(__kCFCSetNameControl, "<CFCharacterSet Predefined Control Set>") +CONST_STRING_DECL(__kCFCSetNameWhitespace, "<CFCharacterSet Predefined Whitespace Set>") +CONST_STRING_DECL(__kCFCSetNameWhitespaceAndNewline, "<CFCharacterSet Predefined WhitespaceAndNewline Set>") +CONST_STRING_DECL(__kCFCSetNameDecimalDigit, "<CFCharacterSet Predefined DecimalDigit Set>") +CONST_STRING_DECL(__kCFCSetNameLetter, "<CFCharacterSet Predefined Letter Set>") +CONST_STRING_DECL(__kCFCSetNameLowercaseLetter, "<CFCharacterSet Predefined LowercaseLetter Set>") +CONST_STRING_DECL(__kCFCSetNameUppercaseLetter, "<CFCharacterSet Predefined UppercaseLetter Set>") +CONST_STRING_DECL(__kCFCSetNameNonBase, "<CFCharacterSet Predefined NonBase Set>") +CONST_STRING_DECL(__kCFCSetNameDecomposable, "<CFCharacterSet Predefined Decomposable Set>") +CONST_STRING_DECL(__kCFCSetNameAlphaNumeric, "<CFCharacterSet Predefined AlphaNumeric Set>") +CONST_STRING_DECL(__kCFCSetNamePunctuation, "<CFCharacterSet Predefined Punctuation Set>") +CONST_STRING_DECL(__kCFCSetNameIllegal, "<CFCharacterSet Predefined Illegal Set>") +CONST_STRING_DECL(__kCFCSetNameCapitalizedLetter, "<CFCharacterSet Predefined CapitalizedLetter Set>") +CONST_STRING_DECL(__kCFCSetNameSymbol, "<CFCharacterSet Predefined Symbol Set>") +CONST_STRING_DECL(__kCFCSetNameNewline, "<CFCharacterSet Predefined Newline Set>") + +CONST_STRING_DECL(__kCFCSetNameStringTypeFormat, "<CFCharacterSet Items(") + +/* Array of instantiated builtin set. Note builtin set ID starts with 1 so the array index is ID - 1 +*/ +static CFCharacterSetRef *__CFBuiltinSets = NULL; + +/* Global lock for character set +*/ +static CFSpinLock_t __CFCharacterSetLock = CFSpinLockInit; + +/* CFBase API functions +*/ +static Boolean __CFCharacterSetEqual(CFTypeRef cf1, CFTypeRef cf2) { + Boolean isInvertStateIdentical = (__CFCSetIsInverted((CFCharacterSetRef)cf1) == __CFCSetIsInverted((CFCharacterSetRef)cf2) ? true: false); + Boolean isAnnexInvertStateIdentical = (__CFCSetAnnexIsInverted((CFCharacterSetRef)cf1) == __CFCSetAnnexIsInverted((CFCharacterSetRef)cf2) ? true: false); + CFIndex idx; + CFCharacterSetRef subSet1; + uint8_t bitsBuf[__kCFBitmapSize]; + uint8_t *bits; + Boolean isBitmap1; + Boolean isBitmap2; + + if (__CFCSetHasHashValue((CFCharacterSetRef)cf1) && __CFCSetHasHashValue((CFCharacterSetRef)cf2) && ((CFCharacterSetRef)cf1)->_hashValue != ((CFCharacterSetRef)cf2)->_hashValue) return false; + if (__CFCSetIsEmpty((CFCharacterSetRef)cf1) && __CFCSetIsEmpty((CFCharacterSetRef)cf2) && !isInvertStateIdentical) return false; + + if (__CFCSetClassType((CFCharacterSetRef)cf1) == __CFCSetClassType((CFCharacterSetRef)cf2)) { // Types are identical, we can do it fast + switch (__CFCSetClassType((CFCharacterSetRef)cf1)) { + case __kCFCharSetClassBuiltin: + return (__CFCSetBuiltinType((CFCharacterSetRef)cf1) == __CFCSetBuiltinType((CFCharacterSetRef)cf2) && isInvertStateIdentical ? true : false); + + case __kCFCharSetClassRange: + return (__CFCSetRangeFirstChar((CFCharacterSetRef)cf1) == __CFCSetRangeFirstChar((CFCharacterSetRef)cf2) && __CFCSetRangeLength((CFCharacterSetRef)cf1) && __CFCSetRangeLength((CFCharacterSetRef)cf2) && isInvertStateIdentical ? true : false); + + case __kCFCharSetClassString: + if (__CFCSetStringLength((CFCharacterSetRef)cf1) == __CFCSetStringLength((CFCharacterSetRef)cf2) && isInvertStateIdentical) { + const UniChar *buf1 = __CFCSetStringBuffer((CFCharacterSetRef)cf1); + const UniChar *buf2 = __CFCSetStringBuffer((CFCharacterSetRef)cf2); + CFIndex length = __CFCSetStringLength((CFCharacterSetRef)cf1); + + while (length--) if (*buf1++ != *buf2++) return false; + } else { + return false; + } + break; + + case __kCFCharSetClassBitmap: + if (!__CFCSetIsEqualBitmap((const UInt32 *)__CFCSetBitmapBits((CFCharacterSetRef)cf1), (const UInt32 *)__CFCSetBitmapBits((CFCharacterSetRef)cf2))) return false; + break; + } + return __CFCSetIsEqualAnnex((CFCharacterSetRef)cf1, (CFCharacterSetRef)cf2); + } + + // Check for easy empty cases + if (__CFCSetIsEmpty((CFCharacterSetRef)cf1) || __CFCSetIsEmpty((CFCharacterSetRef)cf2)) { + CFCharacterSetRef emptySet = (__CFCSetIsEmpty((CFCharacterSetRef)cf1) ? (CFCharacterSetRef)cf1 : (CFCharacterSetRef)cf2); + CFCharacterSetRef nonEmptySet = (emptySet == cf1 ? (CFCharacterSetRef)cf2 : (CFCharacterSetRef)cf1); + + if (__CFCSetIsBuiltin(nonEmptySet)) { + return false; + } else if (__CFCSetIsRange(nonEmptySet)) { + if (isInvertStateIdentical) { + return (__CFCSetRangeLength(nonEmptySet) ? false : true); + } else { + return (__CFCSetRangeLength(nonEmptySet) == 0x110000 ? true : false); + } + } else { + if (__CFCSetAnnexIsInverted(nonEmptySet)) { + if (__CFCSetAnnexValidEntriesBitmap(nonEmptySet) != 0x1FFFE) return false; + } else { + if (__CFCSetAnnexValidEntriesBitmap(nonEmptySet)) return false; + } + + if (__CFCSetIsBitmap(nonEmptySet)) { + bits = __CFCSetBitmapBits(nonEmptySet); + } else { + bits = bitsBuf; + __CFCSetGetBitmap(nonEmptySet, bitsBuf); + } + + if (__CFCSetIsEqualBitmap(NULL, (const UInt32 *)bits)) { + if (!__CFCSetAnnexIsInverted(nonEmptySet)) return true; + } else { + return false; + } + + // Annex set has to be CFRangeMake(0x10000, 0xfffff) + for (idx = 1;idx < MAX_ANNEX_PLANE;idx++) { + if (__CFCSetIsBitmap(nonEmptySet)) { + if (!__CFCSetIsEqualBitmap((__CFCSetAnnexIsInverted(nonEmptySet) ? NULL : (const UInt32 *)-1), (const UInt32 *)bitsBuf)) return false; + } else { + __CFCSetGetBitmap(__CFCSetGetAnnexPlaneCharacterSetNoAlloc(nonEmptySet, idx), bitsBuf); + if (!__CFCSetIsEqualBitmap((const UInt32 *)-1, (const UInt32 *)bitsBuf)) return false; + } + } + return true; + } + } + + if (__CFCSetIsBuiltin((CFCharacterSetRef)cf1) || __CFCSetIsBuiltin((CFCharacterSetRef)cf2)) { + CFCharacterSetRef builtinSet = (__CFCSetIsBuiltin((CFCharacterSetRef)cf1) ? (CFCharacterSetRef)cf1 : (CFCharacterSetRef)cf2); + CFCharacterSetRef nonBuiltinSet = (builtinSet == cf1 ? (CFCharacterSetRef)cf2 : (CFCharacterSetRef)cf1); + + + if (__CFCSetIsRange(nonBuiltinSet)) { + UTF32Char firstChar = __CFCSetRangeFirstChar(nonBuiltinSet); + UTF32Char lastChar = (firstChar + __CFCSetRangeLength(nonBuiltinSet) - 1); + uint8_t firstPlane = (firstChar >> 16) & 0xFF; + uint8_t lastPlane = (lastChar >> 16) & 0xFF; + uint8_t result; + + for (idx = 0;idx < MAX_ANNEX_PLANE;idx++) { + result = CFUniCharGetBitmapForPlane(__CFCSetBuiltinType(builtinSet), idx, bitsBuf, (isInvertStateIdentical != 0)); + + if (idx < firstPlane || idx > lastPlane) { + if (result == kCFUniCharBitmapAll) { + return false; + } else if (result == kCFUniCharBitmapFilled) { + if (!__CFCSetIsEqualBitmap(NULL, (const UInt32 *)bitsBuf)) return false; + } + } else if (idx > firstPlane && idx < lastPlane) { + if (result == kCFUniCharBitmapEmpty) { + return false; + } else if (result == kCFUniCharBitmapFilled) { + if (!__CFCSetIsEqualBitmap((const UInt32 *)-1, (const UInt32 *)bitsBuf)) return false; + } + } else { + if (result == kCFUniCharBitmapEmpty) { + return false; + } else if (result == kCFUniCharBitmapAll) { + if (idx == firstPlane) { + if (((firstChar & 0xFFFF) != 0) || (firstPlane == lastPlane && ((lastChar & 0xFFFF) != 0xFFFF))) return false; + } else { + if (((lastChar & 0xFFFF) != 0xFFFF) || (firstPlane == lastPlane && ((firstChar & 0xFFFF) != 0))) return false; + } + } else { + if (idx == firstPlane) { + if (!__CFCSetIsBitmapEqualToRange((const UInt32 *)bitsBuf, firstChar & 0xFFFF, (firstPlane == lastPlane ? lastChar & 0xFFFF : 0xFFFF), false)) return false; + } else { + if (!__CFCSetIsBitmapEqualToRange((const UInt32 *)bitsBuf, (firstPlane == lastPlane ? firstChar & 0xFFFF : 0), lastChar & 0xFFFF, false)) return false; + } + } + } + } + return true; + } else { + uint8_t bitsBuf2[__kCFBitmapSize]; + uint8_t result; + + result = CFUniCharGetBitmapForPlane(__CFCSetBuiltinType(builtinSet), 0, bitsBuf, (__CFCSetIsInverted(builtinSet) != 0)); + if (result == kCFUniCharBitmapFilled) { + if (__CFCSetIsBitmap(nonBuiltinSet)) { + if (!__CFCSetIsEqualBitmap((const UInt32 *)bitsBuf, (const UInt32 *)__CFCSetBitmapBits(nonBuiltinSet))) return false; + } else { + + __CFCSetGetBitmap(nonBuiltinSet, bitsBuf2); + if (!__CFCSetIsEqualBitmap((const UInt32 *)bitsBuf, (const UInt32 *)bitsBuf2)) { + return false; + } + } + } else { + if (__CFCSetIsBitmap(nonBuiltinSet)) { + if (!__CFCSetIsEqualBitmap((result == kCFUniCharBitmapAll ? (const UInt32*)-1 : NULL), (const UInt32 *)__CFCSetBitmapBits(nonBuiltinSet))) return false; + } else { + __CFCSetGetBitmap(nonBuiltinSet, bitsBuf); + if (!__CFCSetIsEqualBitmap((result == kCFUniCharBitmapAll ? (const UInt32*)-1: NULL), (const UInt32 *)bitsBuf)) return false; + } + } + + isInvertStateIdentical = (__CFCSetIsInverted(builtinSet) == __CFCSetAnnexIsInverted(nonBuiltinSet) ? true : false); + + for (idx = 1;idx < MAX_ANNEX_PLANE;idx++) { + result = CFUniCharGetBitmapForPlane(__CFCSetBuiltinType(builtinSet), idx, bitsBuf, !isInvertStateIdentical); + subSet1 = __CFCSetGetAnnexPlaneCharacterSetNoAlloc(nonBuiltinSet, idx); + + if (result == kCFUniCharBitmapFilled) { + if (NULL == subSet1) { + return false; + } else if (__CFCSetIsBitmap(subSet1)) { + if (!__CFCSetIsEqualBitmap((const UInt32*)bitsBuf, (const UInt32*)__CFCSetBitmapBits(subSet1))) { + return false; + } + } else { + + __CFCSetGetBitmap(subSet1, bitsBuf2); + if (!__CFCSetIsEqualBitmap((const UInt32*)bitsBuf, (const UInt32*)bitsBuf2)) { + return false; + } + } + } else { + if (NULL == subSet1) { + if (result == kCFUniCharBitmapAll) { + return false; + } + } else if (__CFCSetIsBitmap(subSet1)) { + if (!__CFCSetIsEqualBitmap((result == kCFUniCharBitmapAll ? (const UInt32*)-1: NULL), (const UInt32*)__CFCSetBitmapBits(subSet1))) { + return false; + } + } else { + __CFCSetGetBitmap(subSet1, bitsBuf); + if (!__CFCSetIsEqualBitmap((result == kCFUniCharBitmapAll ? (const UInt32*)-1: NULL), (const UInt32*)bitsBuf)) { + return false; + } + } + } + } + return true; + } + } + + if (__CFCSetIsRange((CFCharacterSetRef)cf1) || __CFCSetIsRange((CFCharacterSetRef)cf2)) { + CFCharacterSetRef rangeSet = (__CFCSetIsRange((CFCharacterSetRef)cf1) ? (CFCharacterSetRef)cf1 : (CFCharacterSetRef)cf2); + CFCharacterSetRef nonRangeSet = (rangeSet == cf1 ? (CFCharacterSetRef)cf2 : (CFCharacterSetRef)cf1); + UTF32Char firstChar = __CFCSetRangeFirstChar(rangeSet); + UTF32Char lastChar = (firstChar + __CFCSetRangeLength(rangeSet) - 1); + uint8_t firstPlane = (firstChar >> 16) & 0xFF; + uint8_t lastPlane = (lastChar >> 16) & 0xFF; + Boolean isRangeSetInverted = __CFCSetIsInverted(rangeSet); + + if (__CFCSetIsBitmap(nonRangeSet)) { + bits = __CFCSetBitmapBits(nonRangeSet); + } else { + bits = bitsBuf; + __CFCSetGetBitmap(nonRangeSet, bitsBuf); + } + if (firstPlane == 0) { + if (!__CFCSetIsBitmapEqualToRange((const UInt32*)bits, firstChar, (lastPlane == 0 ? lastChar : 0xFFFF), isRangeSetInverted)) return false; + firstPlane = 1; + firstChar = 0; + } else { + if (!__CFCSetIsEqualBitmap((const UInt32*)bits, (isRangeSetInverted ? (const UInt32 *)-1 : NULL))) return false; + firstChar &= 0xFFFF; + } + + lastChar &= 0xFFFF; + + isAnnexInvertStateIdentical = (isRangeSetInverted == __CFCSetAnnexIsInverted(nonRangeSet) ? true : false); + + for (idx = 1;idx < MAX_ANNEX_PLANE;idx++) { + subSet1 = __CFCSetGetAnnexPlaneCharacterSetNoAlloc(nonRangeSet, idx); + if (NULL == subSet1) { + if (idx < firstPlane || idx > lastPlane) { + if (!isAnnexInvertStateIdentical) return false; + } else if (idx > firstPlane && idx < lastPlane) { + if (isAnnexInvertStateIdentical) return false; + } else if (idx == firstPlane) { + if (isAnnexInvertStateIdentical || firstChar || (idx == lastPlane && lastChar != 0xFFFF)) return false; + } else if (idx == lastPlane) { + if (isAnnexInvertStateIdentical || (idx == firstPlane && firstChar) || (lastChar != 0xFFFF)) return false; + } + } else { + if (__CFCSetIsBitmap(subSet1)) { + bits = __CFCSetBitmapBits(subSet1); + } else { + __CFCSetGetBitmap(subSet1, bitsBuf); + bits = bitsBuf; + } + + if (idx < firstPlane || idx > lastPlane) { + if (!__CFCSetIsEqualBitmap((const UInt32*)bits, (isAnnexInvertStateIdentical ? NULL : (const UInt32 *)-1))) return false; + } else if (idx > firstPlane && idx < lastPlane) { + if (!__CFCSetIsEqualBitmap((const UInt32*)bits, (isAnnexInvertStateIdentical ? (const UInt32 *)-1 : NULL))) return false; + } else if (idx == firstPlane) { + if (!__CFCSetIsBitmapEqualToRange((const UInt32*)bits, firstChar, (idx == lastPlane ? lastChar : 0xFFFF), !isAnnexInvertStateIdentical)) return false; + } else if (idx == lastPlane) { + if (!__CFCSetIsBitmapEqualToRange((const UInt32*)bits, (idx == firstPlane ? firstChar : 0), lastChar, !isAnnexInvertStateIdentical)) return false; + } + } + } + return true; + } + + isBitmap1 = __CFCSetIsBitmap((CFCharacterSetRef)cf1); + isBitmap2 = __CFCSetIsBitmap((CFCharacterSetRef)cf2); + + if (isBitmap1 && isBitmap2) { + if (!__CFCSetIsEqualBitmap((const UInt32 *)__CFCSetBitmapBits((CFCharacterSetRef)cf1), (const UInt32 *)__CFCSetBitmapBits((CFCharacterSetRef)cf2))) return false; + } else if (!isBitmap1 && !isBitmap2) { + uint8_t bitsBuf2[__kCFBitmapSize]; + + __CFCSetGetBitmap((CFCharacterSetRef)cf1, bitsBuf); + __CFCSetGetBitmap((CFCharacterSetRef)cf2, bitsBuf2); + + if (!__CFCSetIsEqualBitmap((const UInt32*)bitsBuf, (const UInt32*)bitsBuf2)) { + return false; + } + } else { + if (isBitmap2) { + CFCharacterSetRef tmp = (CFCharacterSetRef)cf2; + cf2 = cf1; + cf1 = tmp; + } + + __CFCSetGetBitmap((CFCharacterSetRef)cf2, bitsBuf); + + if (!__CFCSetIsEqualBitmap((const UInt32 *)__CFCSetBitmapBits((CFCharacterSetRef)cf1), (const UInt32 *)bitsBuf)) return false; + } + return __CFCSetIsEqualAnnex((CFCharacterSetRef)cf1, (CFCharacterSetRef)cf2); +} + +static CFHashCode __CFCharacterSetHash(CFTypeRef cf) { + if (!__CFCSetHasHashValue((CFCharacterSetRef)cf)) { + if (__CFCSetIsEmpty((CFCharacterSetRef)cf)) { + ((CFMutableCharacterSetRef)cf)->_hashValue = (__CFCSetIsInverted((CFCharacterSetRef)cf) ? ((UInt32)0xFFFFFFFF) : 0); + } else if (__CFCSetIsBitmap( (CFCharacterSetRef) cf )) { + ((CFMutableCharacterSetRef)cf)->_hashValue = CFHashBytes(__CFCSetBitmapBits((CFCharacterSetRef)cf), __kCFBitmapSize); + } else { + uint8_t bitsBuf[__kCFBitmapSize]; + __CFCSetGetBitmap((CFCharacterSetRef)cf, bitsBuf); + ((CFMutableCharacterSetRef)cf)->_hashValue = CFHashBytes(bitsBuf, __kCFBitmapSize); + } + __CFCSetPutHasHashValue((CFMutableCharacterSetRef)cf, true); + } + return ((CFCharacterSetRef)cf)->_hashValue; +} + +static CFStringRef __CFCharacterSetCopyDescription(CFTypeRef cf) { + CFMutableStringRef string; + CFIndex idx; + CFIndex length; + + if (__CFCSetIsEmpty((CFCharacterSetRef)cf)) { + return (CFStringRef)(__CFCSetIsInverted((CFCharacterSetRef)cf) ? CFRetain(CFSTR("<CFCharacterSet All>")) : CFRetain(CFSTR("<CFCharacterSet Empty>"))); + } + + switch (__CFCSetClassType((CFCharacterSetRef)cf)) { + case __kCFCharSetClassBuiltin: + switch (__CFCSetBuiltinType((CFCharacterSetRef)cf)) { + case kCFCharacterSetControl: return (CFStringRef)CFRetain(__kCFCSetNameControl); + case kCFCharacterSetWhitespace : return (CFStringRef)CFRetain(__kCFCSetNameWhitespace); + case kCFCharacterSetWhitespaceAndNewline: return (CFStringRef)CFRetain(__kCFCSetNameWhitespaceAndNewline); + case kCFCharacterSetDecimalDigit: return (CFStringRef)CFRetain(__kCFCSetNameDecimalDigit); + case kCFCharacterSetLetter: return (CFStringRef)CFRetain(__kCFCSetNameLetter); + case kCFCharacterSetLowercaseLetter: return (CFStringRef)CFRetain(__kCFCSetNameLowercaseLetter); + case kCFCharacterSetUppercaseLetter: return (CFStringRef)CFRetain(__kCFCSetNameUppercaseLetter); + case kCFCharacterSetNonBase: return (CFStringRef)CFRetain(__kCFCSetNameNonBase); + case kCFCharacterSetDecomposable: return (CFStringRef)CFRetain(__kCFCSetNameDecomposable); + case kCFCharacterSetAlphaNumeric: return (CFStringRef)CFRetain(__kCFCSetNameAlphaNumeric); + case kCFCharacterSetPunctuation: return (CFStringRef)CFRetain(__kCFCSetNamePunctuation); + case kCFCharacterSetIllegal: return (CFStringRef)CFRetain(__kCFCSetNameIllegal); + case kCFCharacterSetCapitalizedLetter: return (CFStringRef)CFRetain(__kCFCSetNameCapitalizedLetter); + case kCFCharacterSetSymbol: return (CFStringRef)CFRetain(__kCFCSetNameSymbol); + case kCFCharacterSetNewline: return (CFStringRef)CFRetain(__kCFCSetNameNewline); + } + break; + + case __kCFCharSetClassRange: + return CFStringCreateWithFormat(CFGetAllocator((CFCharacterSetRef)cf), NULL, CFSTR("<CFCharacterSet Range(%d, %d)>"), __CFCSetRangeFirstChar((CFCharacterSetRef)cf), __CFCSetRangeLength((CFCharacterSetRef)cf)); + + case __kCFCharSetClassString: + length = __CFCSetStringLength((CFCharacterSetRef)cf); + string = CFStringCreateMutable(CFGetAllocator(cf), CFStringGetLength(__kCFCSetNameStringTypeFormat) + 7 * length + 2); // length of__kCFCSetNameStringTypeFormat + "U+XXXX "(7) * length + ")>"(2) + CFStringAppend(string, __kCFCSetNameStringTypeFormat); + for (idx = 0;idx < length;idx++) { + CFStringAppendFormat(string, NULL, CFSTR("%sU+%04X"), (idx > 0 ? " " : ""), (UInt32)((__CFCSetStringBuffer((CFCharacterSetRef)cf))[idx])); + } + CFStringAppend(string, CFSTR(")>")); + return string; + + case __kCFCharSetClassBitmap: + case __kCFCharSetClassCompactBitmap: + return (CFStringRef)CFRetain(CFSTR("<CFCharacterSet Bitmap>")); // ??? Should generate description for 8k bitmap ? + } + CFAssert1(0, __kCFLogAssertion, "%s: Internal inconsistency error: unknown character set type", __PRETTY_FUNCTION__); // We should never come here + return NULL; +} + +static void __CFCharacterSetDeallocate(CFTypeRef cf) { + CFAllocatorRef allocator = CFGetAllocator(cf); + + if (__CFCSetIsBuiltin((CFCharacterSetRef)cf) && !__CFCSetIsMutable((CFCharacterSetRef)cf) && !__CFCSetIsInverted((CFCharacterSetRef)cf)) { + CFCharacterSetRef sharedSet = CFCharacterSetGetPredefined(__CFCSetBuiltinType((CFCharacterSetRef)cf)); + if (sharedSet == cf) { // We're trying to dealloc the builtin set + CFAssert1(0, __kCFLogAssertion, "%s: Trying to deallocate predefined set. The process is likely to crash.", __PRETTY_FUNCTION__); + return; // We never deallocate builtin set + } + } + + if (__CFCSetIsString((CFCharacterSetRef)cf) && __CFCSetStringBuffer((CFCharacterSetRef)cf)) CFAllocatorDeallocate(allocator, __CFCSetStringBuffer((CFCharacterSetRef)cf)); + else if (__CFCSetIsBitmap((CFCharacterSetRef)cf) && __CFCSetBitmapBits((CFCharacterSetRef)cf)) CFAllocatorDeallocate(allocator, __CFCSetBitmapBits((CFCharacterSetRef)cf)); + else if (__CFCSetIsCompactBitmap((CFCharacterSetRef)cf) && __CFCSetCompactBitmapBits((CFCharacterSetRef)cf)) CFAllocatorDeallocate(allocator, __CFCSetCompactBitmapBits((CFCharacterSetRef)cf)); + __CFCSetDeallocateAnnexPlane((CFCharacterSetRef)cf); +} + +static CFTypeID __kCFCharacterSetTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFCharacterSetClass = { + 0, + "CFCharacterSet", + NULL, // init + NULL, // copy + __CFCharacterSetDeallocate, + __CFCharacterSetEqual, + __CFCharacterSetHash, + NULL, // + __CFCharacterSetCopyDescription +}; + +static bool __CFCheckForExapendedSet = false; + +__private_extern__ void __CFCharacterSetInitialize(void) { + const char *checkForExpandedSet = getenv("__CF_DEBUG_EXPANDED_SET"); + + __kCFCharacterSetTypeID = _CFRuntimeRegisterClass(&__CFCharacterSetClass); + + if (checkForExpandedSet && (*checkForExpandedSet == 'Y')) __CFCheckForExapendedSet = true; +} + +/* Public functions +*/ + +CFTypeID CFCharacterSetGetTypeID(void) { + return __kCFCharacterSetTypeID; +} + +/*** CharacterSet creation ***/ +/* Functions to create basic immutable characterset. +*/ +CFCharacterSetRef CFCharacterSetGetPredefined(CFCharacterSetPredefinedSet theSetIdentifier) { + CFCharacterSetRef cset; + + __CFCSetValidateBuiltinType(theSetIdentifier, __PRETTY_FUNCTION__); + + __CFSpinLock(&__CFCharacterSetLock); + cset = ((NULL != __CFBuiltinSets) ? __CFBuiltinSets[theSetIdentifier - 1] : NULL); + __CFSpinUnlock(&__CFCharacterSetLock); + + if (NULL != cset) return cset; + + if (!(cset = __CFCSetGenericCreate(kCFAllocatorSystemDefault, __kCFCharSetClassBuiltin))) return NULL; + __CFCSetPutBuiltinType((CFMutableCharacterSetRef)cset, theSetIdentifier); + + __CFSpinLock(&__CFCharacterSetLock); + if (!__CFBuiltinSets) { + __CFBuiltinSets = (CFCharacterSetRef *)CFAllocatorAllocate((CFAllocatorRef)CFRetain(__CFGetDefaultAllocator()), sizeof(CFCharacterSetRef) * __kCFLastBuiltinSetID, 0); + memset(__CFBuiltinSets, 0, sizeof(CFCharacterSetRef) * __kCFLastBuiltinSetID); + } + + __CFBuiltinSets[theSetIdentifier - 1] = cset; + __CFSpinUnlock(&__CFCharacterSetLock); + + return cset; +} + +CFCharacterSetRef CFCharacterSetCreateWithCharactersInRange(CFAllocatorRef allocator, CFRange theRange) { + CFMutableCharacterSetRef cset; + + __CFCSetValidateRange(theRange, __PRETTY_FUNCTION__); + + if (theRange.length) { + if (!(cset = __CFCSetGenericCreate(allocator, __kCFCharSetClassRange))) return NULL; + __CFCSetPutRangeFirstChar(cset, theRange.location); + __CFCSetPutRangeLength(cset, theRange.length); + } else { + if (!(cset = __CFCSetGenericCreate(allocator, __kCFCharSetClassBitmap))) return NULL; + __CFCSetPutBitmapBits(cset, NULL); + __CFCSetPutHasHashValue(cset, true); // _hashValue is 0 + } + + return cset; +} + +static int chcompar(const void *a, const void *b) { + return -(int)(*(UniChar *)b - *(UniChar *)a); +} + +CFCharacterSetRef CFCharacterSetCreateWithCharactersInString(CFAllocatorRef allocator, CFStringRef theString) { + CFIndex length; + + length = CFStringGetLength(theString); + if (length < __kCFStringCharSetMax) { + CFMutableCharacterSetRef cset; + + if (!(cset = __CFCSetGenericCreate(allocator, __kCFCharSetClassString))) return NULL; + __CFCSetPutStringBuffer(cset, (UniChar *)CFAllocatorAllocate(CFGetAllocator(cset), __kCFStringCharSetMax * sizeof(UniChar), 0)); + __CFCSetPutStringLength(cset, length); + CFStringGetCharacters(theString, CFRangeMake(0, length), __CFCSetStringBuffer(cset)); + qsort(__CFCSetStringBuffer(cset), length, sizeof(UniChar), chcompar); + if (!length) __CFCSetPutHasHashValue(cset, true); // _hashValue is 0 + return cset; + } else { + CFMutableCharacterSetRef mcset = CFCharacterSetCreateMutable(allocator); + CFCharacterSetAddCharactersInString(mcset, theString); + __CFCSetMakeCompact(mcset); + __CFCSetPutIsMutable(mcset, false); + return mcset; + } +} + +CFCharacterSetRef CFCharacterSetCreateWithBitmapRepresentation(CFAllocatorRef allocator, CFDataRef theData) { + CFMutableCharacterSetRef cset; + CFIndex length; + + if (!(cset = __CFCSetGenericCreate(allocator, __kCFCharSetClassBitmap))) return NULL; + + if (theData && (length = CFDataGetLength(theData)) > 0) { + uint8_t *bitmap; + uint8_t *cBitmap; + + if (length < __kCFBitmapSize) { + bitmap = (uint8_t *)CFAllocatorAllocate(allocator, __kCFBitmapSize, 0); + memmove(bitmap, CFDataGetBytePtr(theData), length); + memset(bitmap + length, 0, __kCFBitmapSize - length); + + cBitmap = __CFCreateCompactBitmap(allocator, bitmap); + + if (cBitmap == NULL) { + __CFCSetPutBitmapBits(cset, bitmap); + } else { + CFAllocatorDeallocate(allocator, bitmap); + __CFCSetPutCompactBitmapBits(cset, cBitmap); + __CFCSetPutClassType(cset, __kCFCharSetClassCompactBitmap); + } + } else { + cBitmap = __CFCreateCompactBitmap(allocator, CFDataGetBytePtr(theData)); + + if (cBitmap == NULL) { + bitmap = (uint8_t *)CFAllocatorAllocate(allocator, __kCFBitmapSize, 0); + memmove(bitmap, CFDataGetBytePtr(theData), __kCFBitmapSize); + + __CFCSetPutBitmapBits(cset, bitmap); + } else { + __CFCSetPutCompactBitmapBits(cset, cBitmap); + __CFCSetPutClassType(cset, __kCFCharSetClassCompactBitmap); + } + + if (length > __kCFBitmapSize) { + CFMutableCharacterSetRef annexSet; + const uint8_t *bytes = CFDataGetBytePtr(theData) + __kCFBitmapSize; + + length -= __kCFBitmapSize; + + while (length > 1) { + annexSet = (CFMutableCharacterSetRef)__CFCSetGetAnnexPlaneCharacterSet(cset, *(bytes++)); + --length; // Decrement the plane no byte + + if (length < __kCFBitmapSize) { + bitmap = (uint8_t *)CFAllocatorAllocate(allocator, __kCFBitmapSize, 0); + memmove(bitmap, bytes, length); + memset(bitmap + length, 0, __kCFBitmapSize - length); + + cBitmap = __CFCreateCompactBitmap(allocator, bitmap); + + if (cBitmap == NULL) { + __CFCSetPutBitmapBits(annexSet, bitmap); + } else { + CFAllocatorDeallocate(allocator, bitmap); + __CFCSetPutCompactBitmapBits(annexSet, cBitmap); + __CFCSetPutClassType(annexSet, __kCFCharSetClassCompactBitmap); + } + } else { + cBitmap = __CFCreateCompactBitmap(allocator, bytes); + + if (cBitmap == NULL) { + bitmap = (uint8_t *)CFAllocatorAllocate(allocator, __kCFBitmapSize, 0); + memmove(bitmap, bytes, __kCFBitmapSize); + + __CFCSetPutBitmapBits(annexSet, bitmap); + } else { + __CFCSetPutCompactBitmapBits(annexSet, cBitmap); + __CFCSetPutClassType(annexSet, __kCFCharSetClassCompactBitmap); + } + } + length -= __kCFBitmapSize; + bytes += __kCFBitmapSize; + } + } + } + } else { + __CFCSetPutBitmapBits(cset, NULL); + __CFCSetPutHasHashValue(cset, true); // Hash value is 0 + } + + return cset; +} + +CFCharacterSetRef CFCharacterSetCreateInvertedSet(CFAllocatorRef alloc, CFCharacterSetRef theSet) { + CFMutableCharacterSetRef cset; + + CF_OBJC_FUNCDISPATCH0(__kCFCharacterSetTypeID, CFCharacterSetRef , theSet, "invertedSet"); + + cset = CFCharacterSetCreateMutableCopy(alloc, theSet); + CFCharacterSetInvert(cset); + __CFCSetPutIsMutable(cset, false); + + return cset; +} + +/* Functions to create mutable characterset. +*/ +CFMutableCharacterSetRef CFCharacterSetCreateMutable(CFAllocatorRef allocator) { + CFMutableCharacterSetRef cset; + + if (!(cset = __CFCSetGenericCreate(allocator, __kCFCharSetClassBitmap| __kCFCharSetIsMutable))) return NULL; + __CFCSetPutBitmapBits(cset, NULL); + __CFCSetPutHasHashValue(cset, true); // Hash value is 0 + + return cset; +} + +CFMutableCharacterSetRef __CFCharacterSetCreateCopy(CFAllocatorRef alloc, CFCharacterSetRef theSet, bool isMutable) { + CFMutableCharacterSetRef cset; + + CF_OBJC_FUNCDISPATCH0(__kCFCharacterSetTypeID, CFMutableCharacterSetRef , theSet, "mutableCopy"); + + __CFGenericValidateType(theSet, __kCFCharacterSetTypeID); + + if (!isMutable && !__CFCSetIsMutable(theSet)) { + return (CFMutableCharacterSetRef)CFRetain(theSet); + } + + cset = CFCharacterSetCreateMutable(alloc); + + __CFCSetPutClassType(cset, __CFCSetClassType(theSet)); + __CFCSetPutHasHashValue(cset, __CFCSetHasHashValue(theSet)); + __CFCSetPutIsInverted(cset, __CFCSetIsInverted(theSet)); + cset->_hashValue = theSet->_hashValue; + + switch (__CFCSetClassType(theSet)) { + case __kCFCharSetClassBuiltin: + __CFCSetPutBuiltinType(cset, __CFCSetBuiltinType(theSet)); + break; + + case __kCFCharSetClassRange: + __CFCSetPutRangeFirstChar(cset, __CFCSetRangeFirstChar(theSet)); + __CFCSetPutRangeLength(cset, __CFCSetRangeLength(theSet)); + break; + + case __kCFCharSetClassString: + __CFCSetPutStringBuffer(cset, (UniChar *)CFAllocatorAllocate(alloc, __kCFStringCharSetMax * sizeof(UniChar), 0)); + + __CFCSetPutStringLength(cset, __CFCSetStringLength(theSet)); + memmove(__CFCSetStringBuffer(cset), __CFCSetStringBuffer(theSet), __CFCSetStringLength(theSet) * sizeof(UniChar)); + break; + + case __kCFCharSetClassBitmap: + if (__CFCSetBitmapBits(theSet)) { + uint8_t * bitmap = (isMutable ? NULL : __CFCreateCompactBitmap(alloc, __CFCSetBitmapBits(theSet))); + + if (bitmap == NULL) { + bitmap = (uint8_t *)CFAllocatorAllocate(alloc, sizeof(uint8_t) * __kCFBitmapSize, 0); + memmove(bitmap, __CFCSetBitmapBits(theSet), __kCFBitmapSize); + __CFCSetPutBitmapBits(cset, bitmap); + } else { + __CFCSetPutCompactBitmapBits(cset, bitmap); + __CFCSetPutClassType(cset, __kCFCharSetClassCompactBitmap); + } + } else { + __CFCSetPutBitmapBits(cset, NULL); + } + break; + + case __kCFCharSetClassCompactBitmap: { + const uint8_t *compactBitmap = __CFCSetCompactBitmapBits(theSet); + + if (compactBitmap) { + uint32_t size = __CFCSetGetCompactBitmapSize(compactBitmap); + uint8_t *newBitmap = (uint8_t *)CFAllocatorAllocate(alloc, size, 0); + + memmove(newBitmap, compactBitmap, size); + __CFCSetPutCompactBitmapBits(cset, newBitmap); + } + } + break; + + default: + CFAssert1(0, __kCFLogAssertion, "%s: Internal inconsistency error: unknown character set type", __PRETTY_FUNCTION__); // We should never come here + } + if (__CFCSetHasNonBMPPlane(theSet)) { + CFMutableCharacterSetRef annexPlane; + int idx; + + for (idx = 1;idx <= MAX_ANNEX_PLANE;idx++) { + if ((annexPlane = (CFMutableCharacterSetRef)__CFCSetGetAnnexPlaneCharacterSetNoAlloc(theSet, idx))) { + annexPlane = __CFCharacterSetCreateCopy(alloc, annexPlane, isMutable); + __CFCSetPutCharacterSetToAnnexPlane(cset, annexPlane, idx); + CFRelease(annexPlane); + } + } + __CFCSetAnnexSetIsInverted(cset, __CFCSetAnnexIsInverted(theSet)); + } else if (__CFCSetAnnexIsInverted(theSet)) { + __CFCSetAllocateAnnexForPlane(cset, 0); // We need to alloc annex to invert + __CFCSetAnnexSetIsInverted(cset, true); + } + + return cset; +} + +CFCharacterSetRef CFCharacterSetCreateCopy(CFAllocatorRef alloc, CFCharacterSetRef theSet) { + return __CFCharacterSetCreateCopy(alloc, theSet, false); +} + +CFMutableCharacterSetRef CFCharacterSetCreateMutableCopy(CFAllocatorRef alloc, CFCharacterSetRef theSet) { + return __CFCharacterSetCreateCopy(alloc, theSet, true); +} + +/*** Basic accessors ***/ +Boolean CFCharacterSetIsCharacterMember(CFCharacterSetRef theSet, UniChar theChar) { + CFIndex length; + Boolean isInverted; + Boolean result = false; + + CF_OBJC_FUNCDISPATCH1(__kCFCharacterSetTypeID, Boolean, theSet, "longCharacterIsMember:", theChar); + + __CFGenericValidateType(theSet, __kCFCharacterSetTypeID); + + isInverted = __CFCSetIsInverted(theSet); + + switch (__CFCSetClassType(theSet)) { + case __kCFCharSetClassBuiltin: + result = (CFUniCharIsMemberOf(theChar, __CFCSetBuiltinType(theSet)) ? !isInverted : isInverted); + break; + + case __kCFCharSetClassRange: + length = __CFCSetRangeLength(theSet); + result = (length && __CFCSetRangeFirstChar(theSet) <= theChar && theChar < __CFCSetRangeFirstChar(theSet) + length ? !isInverted : isInverted); + break; + + case __kCFCharSetClassString: + result = ((length = __CFCSetStringLength(theSet)) ? (__CFCSetBsearchUniChar(__CFCSetStringBuffer(theSet), length, theChar) ? !isInverted : isInverted) : isInverted); + break; + + case __kCFCharSetClassBitmap: + result = (__CFCSetCompactBitmapBits(theSet) ? (__CFCSetIsMemberBitmap(__CFCSetBitmapBits(theSet), theChar) ? true : false) : isInverted); + break; + + case __kCFCharSetClassCompactBitmap: + result = (__CFCSetCompactBitmapBits(theSet) ? (__CFCSetIsMemberInCompactBitmap(__CFCSetCompactBitmapBits(theSet), theChar) ? true : false) : isInverted); + break; + + default: + CFAssert1(0, __kCFLogAssertion, "%s: Internal inconsistency error: unknown character set type", __PRETTY_FUNCTION__); // We should never come here + break; + } + + return result; +} + +Boolean CFCharacterSetIsLongCharacterMember(CFCharacterSetRef theSet, UTF32Char theChar) { + CFIndex length; + UInt32 plane = (theChar >> 16); + Boolean isAnnexInverted = false; + Boolean isInverted; + Boolean result = false; + + CF_OBJC_FUNCDISPATCH1(__kCFCharacterSetTypeID, Boolean, theSet, "longCharacterIsMember:", theChar); + + __CFGenericValidateType(theSet, __kCFCharacterSetTypeID); + + if (plane) { + CFCharacterSetRef annexPlane; + + if (__CFCSetIsBuiltin(theSet)) { + isInverted = __CFCSetIsInverted(theSet); + return (CFUniCharIsMemberOf(theChar, __CFCSetBuiltinType(theSet)) ? !isInverted : isInverted); + } + + isAnnexInverted = __CFCSetAnnexIsInverted(theSet); + + if ((annexPlane = __CFCSetGetAnnexPlaneCharacterSetNoAlloc(theSet, plane)) == NULL) { + if (!__CFCSetHasNonBMPPlane(theSet) && __CFCSetIsRange(theSet)) { + isInverted = __CFCSetIsInverted(theSet); + length = __CFCSetRangeLength(theSet); + return (length && __CFCSetRangeFirstChar(theSet) <= theChar && theChar < __CFCSetRangeFirstChar(theSet) + length ? !isInverted : isInverted); + } else { + return (isAnnexInverted ? true : false); + } + } else { + theSet = annexPlane; + theChar &= 0xFFFF; + } + } + + isInverted = __CFCSetIsInverted(theSet); + + switch (__CFCSetClassType(theSet)) { + case __kCFCharSetClassBuiltin: + result = (CFUniCharIsMemberOf(theChar, __CFCSetBuiltinType(theSet)) ? !isInverted : isInverted); + break; + + case __kCFCharSetClassRange: + length = __CFCSetRangeLength(theSet); + result = (length && __CFCSetRangeFirstChar(theSet) <= theChar && theChar < __CFCSetRangeFirstChar(theSet) + length ? !isInverted : isInverted); + break; + + case __kCFCharSetClassString: + result = ((length = __CFCSetStringLength(theSet)) ? (__CFCSetBsearchUniChar(__CFCSetStringBuffer(theSet), length, theChar) ? !isInverted : isInverted) : isInverted); + break; + + case __kCFCharSetClassBitmap: + result = (__CFCSetCompactBitmapBits(theSet) ? (__CFCSetIsMemberBitmap(__CFCSetBitmapBits(theSet), theChar) ? true : false) : isInverted); + break; + + case __kCFCharSetClassCompactBitmap: + result = (__CFCSetCompactBitmapBits(theSet) ? (__CFCSetIsMemberInCompactBitmap(__CFCSetCompactBitmapBits(theSet), theChar) ? true : false) : isInverted); + break; + + default: + CFAssert1(0, __kCFLogAssertion, "%s: Internal inconsistency error: unknown character set type", __PRETTY_FUNCTION__); // We should never come here + return false; // To make compiler happy + } + + return (result ? !isAnnexInverted : isAnnexInverted); +} + +Boolean CFCharacterSetIsSurrogatePairMember(CFCharacterSetRef theSet, UniChar surrogateHigh, UniChar surrogateLow) { + return CFCharacterSetIsLongCharacterMember(theSet, CFCharacterSetGetLongCharacterForSurrogatePair(surrogateHigh, surrogateLow)); +} + + +static inline CFCharacterSetRef __CFCharacterSetGetExpandedSetForNSCharacterSet(const void *characterSet) { + CF_OBJC_FUNCDISPATCH0(__kCFCharacterSetTypeID, CFCharacterSetRef , characterSet, "_expandedCFCharacterSet"); + return NULL; +} + +Boolean CFCharacterSetIsSupersetOfSet(CFCharacterSetRef theSet, CFCharacterSetRef theOtherSet) { + CFMutableCharacterSetRef copy; + CFCharacterSetRef expandedSet = NULL; + CFCharacterSetRef expandedOtherSet = NULL; + Boolean result; + + if ((!CF_IS_OBJC(__kCFCharacterSetTypeID, theSet) || (expandedSet = __CFCharacterSetGetExpandedSetForNSCharacterSet(theSet))) && (!CF_IS_OBJC(__kCFCharacterSetTypeID, theOtherSet) || (expandedOtherSet = __CFCharacterSetGetExpandedSetForNSCharacterSet(theOtherSet)))) { // Really CF, we can do some trick here + if (expandedSet) theSet = expandedSet; + if (expandedOtherSet) theOtherSet = expandedOtherSet; + + __CFGenericValidateType(theSet, __kCFCharacterSetTypeID); + __CFGenericValidateType(theOtherSet, __kCFCharacterSetTypeID); + + if (__CFCSetIsEmpty(theSet)) { + if (__CFCSetIsInverted(theSet)) { + return TRUE; // Inverted empty set covers all range + } else if (!__CFCSetIsEmpty(theOtherSet) || __CFCSetIsInverted(theOtherSet)) { + return FALSE; + } + } else if (__CFCSetIsEmpty(theOtherSet) && !__CFCSetIsInverted(theOtherSet)) { + return TRUE; + } else { + if (__CFCSetIsBuiltin(theSet) || __CFCSetIsBuiltin(theOtherSet)) { + if (__CFCSetClassType(theSet) == __CFCSetClassType(theOtherSet) && __CFCSetBuiltinType(theSet) == __CFCSetBuiltinType(theOtherSet) && !__CFCSetIsInverted(theSet) && !__CFCSetIsInverted(theOtherSet)) return TRUE; + } else if (__CFCSetIsRange(theSet) || __CFCSetIsRange(theOtherSet)) { + if (__CFCSetClassType(theSet) == __CFCSetClassType(theOtherSet)) { + if (__CFCSetIsInverted(theSet)) { + if (__CFCSetIsInverted(theOtherSet)) { + return (__CFCSetRangeFirstChar(theOtherSet) > __CFCSetRangeFirstChar(theSet) || (__CFCSetRangeFirstChar(theSet) + __CFCSetRangeLength(theSet)) > (__CFCSetRangeFirstChar(theOtherSet) + __CFCSetRangeLength(theOtherSet)) ? FALSE : TRUE); + } else { + return ((__CFCSetRangeFirstChar(theOtherSet) + __CFCSetRangeLength(theOtherSet)) <= __CFCSetRangeFirstChar(theSet) || (__CFCSetRangeFirstChar(theSet) + __CFCSetRangeLength(theSet)) <= __CFCSetRangeFirstChar(theOtherSet) ? TRUE : FALSE); + } + } else { + if (__CFCSetIsInverted(theOtherSet)) { + return ((__CFCSetRangeFirstChar(theSet) == 0 && __CFCSetRangeLength(theSet) == 0x110000) || (__CFCSetRangeFirstChar(theOtherSet) == 0 && (UInt32)__CFCSetRangeLength(theOtherSet) <= __CFCSetRangeFirstChar(theSet)) || ((__CFCSetRangeFirstChar(theSet) + __CFCSetRangeLength(theSet)) <= __CFCSetRangeFirstChar(theOtherSet) && (__CFCSetRangeFirstChar(theOtherSet) + __CFCSetRangeLength(theOtherSet)) == 0x110000) ? TRUE : FALSE); + } else { + return (__CFCSetRangeFirstChar(theOtherSet) < __CFCSetRangeFirstChar(theSet) || (__CFCSetRangeFirstChar(theSet) + __CFCSetRangeLength(theSet)) < (__CFCSetRangeFirstChar(theOtherSet) + __CFCSetRangeLength(theOtherSet)) ? FALSE : TRUE); + } + } + } + } else { + UInt32 theSetAnnexMask = __CFCSetAnnexValidEntriesBitmap(theSet); + UInt32 theOtherSetAnnexMask = __CFCSetAnnexValidEntriesBitmap(theOtherSet); + Boolean isTheSetAnnexInverted = __CFCSetAnnexIsInverted(theSet); + Boolean isTheOtherSetAnnexInverted = __CFCSetAnnexIsInverted(theOtherSet); + uint8_t theSetBuffer[__kCFBitmapSize]; + uint8_t theOtherSetBuffer[__kCFBitmapSize]; + + // We mask plane 1 to plane 16 + if (isTheSetAnnexInverted) theSetAnnexMask = (~theSetAnnexMask) & (0xFFFF < 1); + if (isTheOtherSetAnnexInverted) theOtherSetAnnexMask = (~theOtherSetAnnexMask) & (0xFFFF < 1); + + __CFCSetGetBitmap(theSet, theSetBuffer); + __CFCSetGetBitmap(theOtherSet, theOtherSetBuffer); + + if (!__CFCSetIsBitmapSupersetOfBitmap((const UInt32 *)theSetBuffer, (const UInt32 *)theOtherSetBuffer, FALSE, FALSE)) return FALSE; + + if (theOtherSetAnnexMask) { + CFCharacterSetRef theSetAnnex; + CFCharacterSetRef theOtherSetAnnex; + uint32_t idx; + + if ((theSetAnnexMask & theOtherSetAnnexMask) != theOtherSetAnnexMask) return FALSE; + + for (idx = 1;idx <= 16;idx++) { + theSetAnnex = __CFCSetGetAnnexPlaneCharacterSetNoAlloc(theSet, idx); + if (NULL == theSetAnnex) continue; // This case is already handled by the mask above + + theOtherSetAnnex = __CFCSetGetAnnexPlaneCharacterSetNoAlloc(theOtherSet, idx); + + if (NULL == theOtherSetAnnex) { + if (isTheOtherSetAnnexInverted) { + __CFCSetGetBitmap(theSetAnnex, theSetBuffer); + if (!__CFCSetIsEqualBitmap((const UInt32 *)theSetBuffer, (isTheSetAnnexInverted ? NULL : (const UInt32 *)-1))) return FALSE; + } + } else { + __CFCSetGetBitmap(theSetAnnex, theSetBuffer); + __CFCSetGetBitmap(theOtherSetAnnex, theOtherSetBuffer); + if (!__CFCSetIsBitmapSupersetOfBitmap((const UInt32 *)theSetBuffer, (const UInt32 *)theOtherSetBuffer, isTheSetAnnexInverted, isTheOtherSetAnnexInverted)) return FALSE; + } + } + } + + return TRUE; + } + } + } + + copy = CFCharacterSetCreateMutableCopy(kCFAllocatorSystemDefault, theSet); + CFCharacterSetIntersect(copy, theOtherSet); + result = __CFCharacterSetEqual(copy, theOtherSet); + CFRelease(copy); + + return result; +} + +Boolean CFCharacterSetHasMemberInPlane(CFCharacterSetRef theSet, CFIndex thePlane) { + Boolean isInverted = __CFCSetIsInverted(theSet); + + CF_OBJC_FUNCDISPATCH1(__kCFCharacterSetTypeID, Boolean, theSet, "hasMemberInPlane:", thePlane); + + if (__CFCSetIsEmpty(theSet)) { + return (isInverted ? TRUE : FALSE); + } else if (__CFCSetIsBuiltin(theSet)) { + CFCharacterSetPredefinedSet type = __CFCSetBuiltinType(theSet); + + if (type == kCFCharacterSetControl) { + if (isInverted || (thePlane == 14)) { + return TRUE; // There is no plane that covers all values || Plane 14 has language tags + } else { + return (CFUniCharGetBitmapPtrForPlane(type, thePlane) ? TRUE : FALSE); + } + } else if ((type < kCFCharacterSetDecimalDigit) || (type == kCFCharacterSetNewline)) { + return (thePlane && !isInverted ? FALSE : TRUE); + } else if (__CFCSetBuiltinType(theSet) == kCFCharacterSetIllegal) { + return (isInverted ? (thePlane < 3 || thePlane > 13 ? TRUE : FALSE) : TRUE); // This is according to Unicode 3.1 + } else { + if (isInverted) { + return TRUE; // There is no plane that covers all values + } else { + return (CFUniCharGetBitmapPtrForPlane(type, thePlane) ? TRUE : FALSE); + } + } + } else if (__CFCSetIsRange(theSet)) { + UTF32Char firstChar = __CFCSetRangeFirstChar(theSet); + UTF32Char lastChar = (firstChar + __CFCSetRangeLength(theSet) - 1); + CFIndex firstPlane = firstChar >> 16; + CFIndex lastPlane = lastChar >> 16; + + if (isInverted) { + if (thePlane < firstPlane || thePlane > lastPlane) { + return TRUE; + } else if (thePlane > firstPlane && thePlane < lastPlane) { + return FALSE; + } else { + firstChar &= 0xFFFF; + lastChar &= 0xFFFF; + if (thePlane == firstPlane) { + return (firstChar || (firstPlane == lastPlane && lastChar != 0xFFFF) ? TRUE : FALSE); + } else { + return (lastChar != 0xFFFF || (firstPlane == lastPlane && firstChar) ? TRUE : FALSE); + } + } + } else { + return (thePlane < firstPlane || thePlane > lastPlane ? FALSE : TRUE); + } + } else { + if (thePlane == 0) { + switch (__CFCSetClassType(theSet)) { + case __kCFCharSetClassString: if (!__CFCSetStringLength(theSet)) return isInverted; break; + case __kCFCharSetClassCompactBitmap: return (__CFCSetCompactBitmapBits(theSet) ? TRUE : FALSE); break; + case __kCFCharSetClassBitmap: return (__CFCSetBitmapBits(theSet) ? TRUE : FALSE); break; + } + return TRUE; + } else { + CFCharacterSetRef annex = __CFCSetGetAnnexPlaneCharacterSetNoAlloc(theSet, thePlane); + if (annex) { + if (__CFCSetIsRange(annex)) { + return (__CFCSetAnnexIsInverted(theSet) && (__CFCSetRangeFirstChar(annex) == 0) && (__CFCSetRangeLength(annex) == 0x10000) ? FALSE : TRUE); + } else if (__CFCSetIsBitmap(annex)) { + return (__CFCSetAnnexIsInverted(theSet) && __CFCSetIsEqualBitmap((const UInt32 *)__CFCSetBitmapBits(annex), (const UInt32 *)-1) ? FALSE : TRUE); + } else { + uint8_t bitsBuf[__kCFBitmapSize]; + __CFCSetGetBitmap(annex, bitsBuf); + return (__CFCSetAnnexIsInverted(theSet) && __CFCSetIsEqualBitmap((const UInt32 *)bitsBuf, (const UInt32 *)-1) ? FALSE : TRUE); + } + } else { + return __CFCSetAnnexIsInverted(theSet); + } + } + } + + return FALSE; +} + + +CFDataRef CFCharacterSetCreateBitmapRepresentation(CFAllocatorRef alloc, CFCharacterSetRef theSet) { + CFMutableDataRef data; + int numNonBMPPlanes = 0; + int planeIndices[MAX_ANNEX_PLANE]; + int idx; + int length; + bool isAnnexInverted; + + CF_OBJC_FUNCDISPATCH0(__kCFCharacterSetTypeID, CFDataRef , theSet, "_retainedBitmapRepresentation"); + + __CFGenericValidateType(theSet, __kCFCharacterSetTypeID); + + isAnnexInverted = (__CFCSetAnnexIsInverted(theSet) != 0); + + if (__CFCSetHasNonBMPPlane(theSet)) { + for (idx = 1;idx <= MAX_ANNEX_PLANE;idx++) { + if (isAnnexInverted || __CFCSetGetAnnexPlaneCharacterSetNoAlloc(theSet, idx)) { + planeIndices[numNonBMPPlanes++] = idx; + } + } + } else if (__CFCSetIsBuiltin(theSet)) { + numNonBMPPlanes = (__CFCSetIsInverted(theSet) ? MAX_ANNEX_PLANE : CFUniCharGetNumberOfPlanes(__CFCSetBuiltinType(theSet)) - 1); + } else if (__CFCSetIsRange(theSet)) { + UInt32 firstChar = __CFCSetRangeFirstChar(theSet); + UInt32 lastChar = __CFCSetRangeFirstChar(theSet) + __CFCSetRangeLength(theSet) - 1; + int firstPlane = (firstChar >> 16); + int lastPlane = (lastChar >> 16); + bool isInverted = (__CFCSetIsInverted(theSet) != 0); + + if (lastPlane > 0) { + if (firstPlane == 0) { + firstPlane = 1; + firstChar = 0x10000; + } + numNonBMPPlanes = (lastPlane - firstPlane) + 1; + if (isInverted) { + numNonBMPPlanes = MAX_ANNEX_PLANE - numNonBMPPlanes; + if (firstPlane == lastPlane) { + if (((firstChar & 0xFFFF) > 0) || ((lastChar & 0xFFFF) < 0xFFFF)) ++numNonBMPPlanes; + } else { + if ((firstChar & 0xFFFF) > 0) ++numNonBMPPlanes; + if ((lastChar & 0xFFFF) < 0xFFFF) ++numNonBMPPlanes; + } + } + } else if (isInverted) { + numNonBMPPlanes = MAX_ANNEX_PLANE; + } + } else if (isAnnexInverted) { + numNonBMPPlanes = MAX_ANNEX_PLANE; + } + + length = __kCFBitmapSize + ((__kCFBitmapSize + 1) * numNonBMPPlanes); + data = CFDataCreateMutable(alloc, length); + CFDataSetLength(data, length); + __CFCSetGetBitmap(theSet, CFDataGetMutableBytePtr(data)); + + if (numNonBMPPlanes > 0) { + uint8_t *bytes = CFDataGetMutableBytePtr(data) + __kCFBitmapSize; + + if (__CFCSetHasNonBMPPlane(theSet)) { + CFCharacterSetRef subset; + + for (idx = 0;idx < numNonBMPPlanes;idx++) { + *(bytes++) = planeIndices[idx]; + if ((subset = __CFCSetGetAnnexPlaneCharacterSetNoAlloc(theSet, planeIndices[idx])) == NULL) { + __CFCSetBitmapFastFillWithValue((UInt32 *)bytes, (isAnnexInverted ? 0xFF : 0)); + } else { + __CFCSetGetBitmap(subset, bytes); + if (isAnnexInverted) { + uint32_t count = __kCFBitmapSize / sizeof(uint32_t); + uint32_t *bits = (uint32_t *)bytes; + + while (count-- > 0) { + *bits = ~(*bits); + ++bits; + } + } + } + bytes += __kCFBitmapSize; + } + } else if (__CFCSetIsBuiltin(theSet)) { + UInt8 result; + CFIndex delta; + Boolean isInverted = __CFCSetIsInverted(theSet); + + for (idx = 0;idx < numNonBMPPlanes;idx++) { + if ((result = CFUniCharGetBitmapForPlane(__CFCSetBuiltinType(theSet), idx + 1, bytes + 1, (isInverted != 0))) == kCFUniCharBitmapEmpty) continue; + *(bytes++) = idx + 1; + if (result == kCFUniCharBitmapAll) { + CFIndex bitmapLength = __kCFBitmapSize; + while (bitmapLength-- > 0) *(bytes++) = (uint8_t)0xFF; + } else { + bytes += __kCFBitmapSize; + } + } + delta = bytes - (const uint8_t *)CFDataGetBytePtr(data); + if (delta < length) CFDataSetLength(data, delta); + } else if (__CFCSetIsRange(theSet)) { + UInt32 firstChar = __CFCSetRangeFirstChar(theSet); + UInt32 lastChar = __CFCSetRangeFirstChar(theSet) + __CFCSetRangeLength(theSet) - 1; + int firstPlane = (firstChar >> 16); + int lastPlane = (lastChar >> 16); + + if (firstPlane == 0) { + firstPlane = 1; + firstChar = 0x10000; + } + if (__CFCSetIsInverted(theSet)) { + // Mask out the plane byte + firstChar &= 0xFFFF; + lastChar &= 0xFFFF; + + for (idx = 1;idx < firstPlane;idx++) { // Fill up until the first plane + *(bytes++) = idx; + __CFCSetBitmapFastFillWithValue((UInt32 *)bytes, 0xFF); + bytes += __kCFBitmapSize; + } + if (firstPlane == lastPlane) { + if ((firstChar > 0) || (lastChar < 0xFFFF)) { + *(bytes++) = idx; + __CFCSetBitmapFastFillWithValue((UInt32 *)bytes, 0xFF); + __CFCSetBitmapRemoveCharactersInRange(bytes, firstChar, lastChar); + bytes += __kCFBitmapSize; + } + } else if (firstPlane < lastPlane) { + if (firstChar > 0) { + *(bytes++) = idx; + __CFCSetBitmapFastFillWithValue((UInt32 *)bytes, 0); + __CFCSetBitmapAddCharactersInRange(bytes, 0, firstChar - 1); + bytes += __kCFBitmapSize; + } + if (lastChar < 0xFFFF) { + *(bytes++) = idx; + __CFCSetBitmapFastFillWithValue((UInt32 *)bytes, 0); + __CFCSetBitmapAddCharactersInRange(bytes, lastChar, 0xFFFF); + bytes += __kCFBitmapSize; + } + } + for (idx = lastPlane + 1;idx <= MAX_ANNEX_PLANE;idx++) { + *(bytes++) = idx; + __CFCSetBitmapFastFillWithValue((UInt32 *)bytes, 0xFF); + bytes += __kCFBitmapSize; + } + } else { + for (idx = firstPlane;idx <= lastPlane;idx++) { + *(bytes++) = idx; + __CFCSetBitmapAddCharactersInRange(bytes, (idx == firstPlane ? firstChar : 0), (idx == lastPlane ? lastChar : 0xFFFF)); + bytes += __kCFBitmapSize; + } + } + } else if (isAnnexInverted) { + for (idx = 1;idx <= MAX_ANNEX_PLANE;idx++) { + *(bytes++) = idx; + __CFCSetBitmapFastFillWithValue((UInt32 *)bytes, 0xFF); + bytes += __kCFBitmapSize; + } + } + } + + return data; +} + +/*** MutableCharacterSet functions ***/ +void CFCharacterSetAddCharactersInRange(CFMutableCharacterSetRef theSet, CFRange theRange) { + CF_OBJC_FUNCDISPATCH1(__kCFCharacterSetTypeID, void, theSet, "addCharactersInRange:", theRange); + + __CFCSetValidateTypeAndMutability(theSet, __PRETTY_FUNCTION__); + __CFCSetValidateRange(theRange, __PRETTY_FUNCTION__); + + if (!theRange.length || (__CFCSetIsInverted(theSet) && __CFCSetIsEmpty(theSet))) return; // Inverted && empty set contains all char + + if (!__CFCSetIsInverted(theSet)) { + if (__CFCSetIsEmpty(theSet)) { + __CFCSetPutClassType(theSet, __kCFCharSetClassRange); + __CFCSetPutRangeFirstChar(theSet, theRange.location); + __CFCSetPutRangeLength(theSet, theRange.length); + __CFCSetPutHasHashValue(theSet, false); + return; + } else if (__CFCSetIsRange(theSet)) { + CFIndex firstChar = __CFCSetRangeFirstChar(theSet); + CFIndex length = __CFCSetRangeLength(theSet); + + if (firstChar == theRange.location) { + __CFCSetPutRangeLength(theSet, __CFMin(length, theRange.length)); + __CFCSetPutHasHashValue(theSet, false); + return; + } else if (firstChar < theRange.location && theRange.location <= firstChar + length) { + if (firstChar + length < theRange.location + theRange.length) __CFCSetPutRangeLength(theSet, theRange.length + (theRange.location - firstChar)); + __CFCSetPutHasHashValue(theSet, false); + return; + } else if (theRange.location < firstChar && firstChar <= theRange.location + theRange.length) { + __CFCSetPutRangeFirstChar(theSet, theRange.location); + __CFCSetPutRangeLength(theSet, length + (firstChar - theRange.location)); + __CFCSetPutHasHashValue(theSet, false); + return; + } + } else if (__CFCSetIsString(theSet) && __CFCSetStringLength(theSet) + theRange.length < __kCFStringCharSetMax) { + UniChar *buffer; + if (!__CFCSetStringBuffer(theSet)) + __CFCSetPutStringBuffer(theSet, (UniChar *)CFAllocatorAllocate(CFGetAllocator(theSet), __kCFStringCharSetMax * sizeof(UniChar), 0)); + buffer = __CFCSetStringBuffer(theSet) + __CFCSetStringLength(theSet); + __CFCSetPutStringLength(theSet, __CFCSetStringLength(theSet) + theRange.length); + while (theRange.length--) *buffer++ = (UniChar)theRange.location++; + qsort(__CFCSetStringBuffer(theSet), __CFCSetStringLength(theSet), sizeof(UniChar), chcompar); + __CFCSetPutHasHashValue(theSet, false); + return; + } + } + + // OK, I have to be a bitmap + __CFCSetMakeBitmap(theSet); + __CFCSetAddNonBMPPlanesInRange(theSet, theRange); + if (theRange.location < 0x10000) { // theRange is in BMP + if (theRange.location + theRange.length >= NUMCHARACTERS) theRange.length = NUMCHARACTERS - theRange.location; + __CFCSetBitmapAddCharactersInRange(__CFCSetBitmapBits(theSet), (UniChar)theRange.location, (UniChar)(theRange.location + theRange.length - 1)); + } + __CFCSetPutHasHashValue(theSet, false); + + if (__CFCheckForExapendedSet) __CFCheckForExpandedSet(theSet); +} + +void CFCharacterSetRemoveCharactersInRange(CFMutableCharacterSetRef theSet, CFRange theRange) { + CF_OBJC_FUNCDISPATCH1(__kCFCharacterSetTypeID, void, theSet, "removeCharactersInRange:", theRange); + + __CFCSetValidateTypeAndMutability(theSet, __PRETTY_FUNCTION__); + __CFCSetValidateRange(theRange, __PRETTY_FUNCTION__); + + if (!theRange.length || (!__CFCSetIsInverted(theSet) && __CFCSetIsEmpty(theSet))) return; // empty set + + if (__CFCSetIsInverted(theSet)) { + if (__CFCSetIsEmpty(theSet)) { + __CFCSetPutClassType(theSet, __kCFCharSetClassRange); + __CFCSetPutRangeFirstChar(theSet, theRange.location); + __CFCSetPutRangeLength(theSet, theRange.length); + __CFCSetPutHasHashValue(theSet, false); + return; + } else if (__CFCSetIsRange(theSet)) { + CFIndex firstChar = __CFCSetRangeFirstChar(theSet); + CFIndex length = __CFCSetRangeLength(theSet); + + if (firstChar == theRange.location) { + __CFCSetPutRangeLength(theSet, __CFMin(length, theRange.length)); + __CFCSetPutHasHashValue(theSet, false); + return; + } else if (firstChar < theRange.location && theRange.location <= firstChar + length) { + if (firstChar + length < theRange.location + theRange.length) __CFCSetPutRangeLength(theSet, theRange.length + (theRange.location - firstChar)); + __CFCSetPutHasHashValue(theSet, false); + return; + } else if (theRange.location < firstChar && firstChar <= theRange.location + theRange.length) { + __CFCSetPutRangeFirstChar(theSet, theRange.location); + __CFCSetPutRangeLength(theSet, length + (firstChar - theRange.location)); + __CFCSetPutHasHashValue(theSet, false); + return; + } + } else if (__CFCSetIsString(theSet) && __CFCSetStringLength(theSet) + theRange.length < __kCFStringCharSetMax) { + UniChar *buffer; + if (!__CFCSetStringBuffer(theSet)) + __CFCSetPutStringBuffer(theSet, (UniChar *)CFAllocatorAllocate(CFGetAllocator(theSet), __kCFStringCharSetMax * sizeof(UniChar), 0)); + buffer = __CFCSetStringBuffer(theSet) + __CFCSetStringLength(theSet); + __CFCSetPutStringLength(theSet, __CFCSetStringLength(theSet) + theRange.length); + while (theRange.length--) *buffer++ = (UniChar)theRange.location++; + qsort(__CFCSetStringBuffer(theSet), __CFCSetStringLength(theSet), sizeof(UniChar), chcompar); + __CFCSetPutHasHashValue(theSet, false); + return; + } + } + + // OK, I have to be a bitmap + __CFCSetMakeBitmap(theSet); + __CFCSetRemoveNonBMPPlanesInRange(theSet, theRange); + if (theRange.location < 0x10000) { // theRange is in BMP + if (theRange.location + theRange.length > NUMCHARACTERS) theRange.length = NUMCHARACTERS - theRange.location; + if (theRange.location == 0 && theRange.length == NUMCHARACTERS) { // Remove all + CFAllocatorDeallocate(CFGetAllocator(theSet), __CFCSetBitmapBits(theSet)); + __CFCSetPutBitmapBits(theSet, NULL); + } else { + __CFCSetBitmapRemoveCharactersInRange(__CFCSetBitmapBits(theSet), (UniChar)theRange.location, (UniChar)(theRange.location + theRange.length - 1)); + } + } + + __CFCSetPutHasHashValue(theSet, false); + if (__CFCheckForExapendedSet) __CFCheckForExpandedSet(theSet); +} + +void CFCharacterSetAddCharactersInString(CFMutableCharacterSetRef theSet, CFStringRef theString) { + const UniChar *buffer; + CFIndex length; + + CF_OBJC_FUNCDISPATCH1(__kCFCharacterSetTypeID, void, theSet, "addCharactersInString:", theString); + + __CFCSetValidateTypeAndMutability(theSet, __PRETTY_FUNCTION__); + + if ((__CFCSetIsEmpty(theSet) && __CFCSetIsInverted(theSet)) || !(length = CFStringGetLength(theString))) return; + + if (!__CFCSetIsInverted(theSet)) { + CFIndex newLength = length + (__CFCSetIsEmpty(theSet) ? 0 : (__CFCSetIsString(theSet) ? __CFCSetStringLength(theSet) : __kCFStringCharSetMax)); + + if (newLength < __kCFStringCharSetMax) { + if (__CFCSetIsEmpty(theSet)) { + __CFCSetPutClassType(theSet, __kCFCharSetClassString); + __CFCSetPutStringLength(theSet, 0); // Make sure to reset this + } + + if (!__CFCSetStringBuffer(theSet)) + __CFCSetPutStringBuffer(theSet, (UniChar *)CFAllocatorAllocate(CFGetAllocator(theSet), __kCFStringCharSetMax * sizeof(UniChar), 0)); + buffer = __CFCSetStringBuffer(theSet) + __CFCSetStringLength(theSet); + + __CFCSetPutClassType(theSet, __kCFCharSetClassString); + __CFCSetPutStringLength(theSet, newLength); + CFStringGetCharacters(theString, CFRangeMake(0, length), (UniChar*)buffer); + qsort(__CFCSetStringBuffer(theSet), newLength, sizeof(UniChar), chcompar); + __CFCSetPutHasHashValue(theSet, false); + return; + } + } + + // OK, I have to be a bitmap + __CFCSetMakeBitmap(theSet); + if ((buffer = CFStringGetCharactersPtr(theString))) { + while (length--) __CFCSetBitmapAddCharacter(__CFCSetBitmapBits(theSet), *buffer++); + } else { + CFStringInlineBuffer inlineBuffer; + CFIndex idx; + + CFStringInitInlineBuffer(theString, &inlineBuffer, CFRangeMake(0, length)); + for (idx = 0;idx < length;idx++) __CFCSetBitmapAddCharacter(__CFCSetBitmapBits(theSet), __CFStringGetCharacterFromInlineBufferQuick(&inlineBuffer, idx)); + } + __CFCSetPutHasHashValue(theSet, false); + if (__CFCheckForExapendedSet) __CFCheckForExpandedSet(theSet); +} + +void CFCharacterSetRemoveCharactersInString(CFMutableCharacterSetRef theSet, CFStringRef theString) { + const UniChar *buffer; + CFIndex length; + + CF_OBJC_FUNCDISPATCH1(__kCFCharacterSetTypeID, void, theSet, "removeCharactersInString:", theString); + + __CFCSetValidateTypeAndMutability(theSet, __PRETTY_FUNCTION__); + + if ((__CFCSetIsEmpty(theSet) && !__CFCSetIsInverted(theSet)) || !(length = CFStringGetLength(theString))) return; + + if (__CFCSetIsInverted(theSet)) { + CFIndex newLength = length + (__CFCSetIsEmpty(theSet) ? 0 : (__CFCSetIsString(theSet) ? __CFCSetStringLength(theSet) : __kCFStringCharSetMax)); + + if (newLength < __kCFStringCharSetMax) { + if (__CFCSetIsEmpty(theSet)) { + __CFCSetPutClassType(theSet, __kCFCharSetClassString); + __CFCSetPutStringLength(theSet, 0); // Make sure to reset this + } + + if (!__CFCSetStringBuffer(theSet)) + __CFCSetPutStringBuffer(theSet, (UniChar *)CFAllocatorAllocate(CFGetAllocator(theSet), __kCFStringCharSetMax * sizeof(UniChar), 0)); + buffer = __CFCSetStringBuffer(theSet) + __CFCSetStringLength(theSet); + + __CFCSetPutClassType(theSet, __kCFCharSetClassString); + __CFCSetPutStringLength(theSet, newLength); + CFStringGetCharacters(theString, CFRangeMake(0, length), (UniChar *)buffer); + qsort(__CFCSetStringBuffer(theSet), newLength, sizeof(UniChar), chcompar); + __CFCSetPutHasHashValue(theSet, false); + return; + } + } + + // OK, I have to be a bitmap + __CFCSetMakeBitmap(theSet); + if ((buffer = CFStringGetCharactersPtr(theString))) { + while (length--) __CFCSetBitmapRemoveCharacter(__CFCSetBitmapBits(theSet), *buffer++); + } else { + CFStringInlineBuffer inlineBuffer; + CFIndex idx; + + CFStringInitInlineBuffer(theString, &inlineBuffer, CFRangeMake(0, length)); + for (idx = 0;idx < length;idx++) __CFCSetBitmapRemoveCharacter(__CFCSetBitmapBits(theSet), __CFStringGetCharacterFromInlineBufferQuick(&inlineBuffer, idx)); + } + __CFCSetPutHasHashValue(theSet, false); + if (__CFCheckForExapendedSet) __CFCheckForExpandedSet(theSet); +} + +void CFCharacterSetUnion(CFMutableCharacterSetRef theSet, CFCharacterSetRef theOtherSet) { + CFCharacterSetRef expandedSet = NULL; + + CF_OBJC_FUNCDISPATCH1(__kCFCharacterSetTypeID, void, theSet, "formUnionWithCharacterSet:", theOtherSet); + + __CFCSetValidateTypeAndMutability(theSet, __PRETTY_FUNCTION__); + + if (__CFCSetIsEmpty(theSet) && __CFCSetIsInverted(theSet)) return; // Inverted empty set contains all char + + if (!CF_IS_OBJC(__kCFCharacterSetTypeID, theOtherSet) || (expandedSet = __CFCharacterSetGetExpandedSetForNSCharacterSet(theOtherSet))) { // Really CF, we can do some trick here + if (expandedSet) theOtherSet = expandedSet; + + if (__CFCSetIsEmpty(theOtherSet)) { + if (__CFCSetIsInverted(theOtherSet)) { + if (__CFCSetIsString(theSet) && __CFCSetStringBuffer(theSet)) { + CFAllocatorDeallocate(CFGetAllocator(theSet), __CFCSetStringBuffer(theSet)); + } else if (__CFCSetIsBitmap(theSet) && __CFCSetBitmapBits(theSet)) { + CFAllocatorDeallocate(CFGetAllocator(theSet), __CFCSetBitmapBits(theSet)); + } else if (__CFCSetIsCompactBitmap(theSet) && __CFCSetCompactBitmapBits(theSet)) { + CFAllocatorDeallocate(CFGetAllocator(theSet), __CFCSetCompactBitmapBits(theSet)); + } + __CFCSetPutClassType(theSet, __kCFCharSetClassRange); + __CFCSetPutRangeLength(theSet, 0); + __CFCSetPutIsInverted(theSet, true); + __CFCSetPutHasHashValue(theSet, false); + __CFCSetDeallocateAnnexPlane(theSet); + } + } else if (__CFCSetIsBuiltin(theOtherSet) && __CFCSetIsEmpty(theSet)) { // theSet can be builtin set + __CFCSetPutClassType(theSet, __kCFCharSetClassBuiltin); + __CFCSetPutBuiltinType(theSet, __CFCSetBuiltinType(theOtherSet)); + __CFCSetPutHasHashValue(theSet, false); + } else { + if (__CFCSetIsRange(theOtherSet)) { + if (__CFCSetIsInverted(theOtherSet)) { + UTF32Char firstChar = __CFCSetRangeFirstChar(theOtherSet); + CFIndex length = __CFCSetRangeLength(theOtherSet); + + if (firstChar > 0) CFCharacterSetAddCharactersInRange(theSet, CFRangeMake(0, firstChar)); + firstChar += length; + length = 0x110000 - firstChar; + CFCharacterSetAddCharactersInRange(theSet, CFRangeMake(firstChar, length)); + } else { + CFCharacterSetAddCharactersInRange(theSet, CFRangeMake(__CFCSetRangeFirstChar(theOtherSet), __CFCSetRangeLength(theOtherSet))); + } + } else if (__CFCSetIsString(theOtherSet)) { + CFStringRef string = CFStringCreateWithCharactersNoCopy(CFGetAllocator(theSet), __CFCSetStringBuffer(theOtherSet), __CFCSetStringLength(theOtherSet), kCFAllocatorNull); + CFCharacterSetAddCharactersInString(theSet, string); + CFRelease(string); + } else { + __CFCSetMakeBitmap(theSet); + if (__CFCSetIsBitmap(theOtherSet)) { + UInt32 *bitmap1 = (UInt32*)__CFCSetBitmapBits(theSet); + UInt32 *bitmap2 = (UInt32*)__CFCSetBitmapBits(theOtherSet); + CFIndex length = __kCFBitmapSize / sizeof(UInt32); + while (length--) *bitmap1++ |= *bitmap2++; + } else { + UInt32 *bitmap1 = (UInt32*)__CFCSetBitmapBits(theSet); + UInt32 *bitmap2; + CFIndex length = __kCFBitmapSize / sizeof(UInt32); + uint8_t bitmapBuffer[__kCFBitmapSize]; + __CFCSetGetBitmap(theOtherSet, bitmapBuffer); + bitmap2 = (UInt32*)bitmapBuffer; + while (length--) *bitmap1++ |= *bitmap2++; + } + __CFCSetPutHasHashValue(theSet, false); + } + if (__CFCSetHasNonBMPPlane(theOtherSet)) { + CFMutableCharacterSetRef otherSetPlane; + int idx; + + for (idx = 1;idx <= MAX_ANNEX_PLANE;idx++) { + if ((otherSetPlane = (CFMutableCharacterSetRef)__CFCSetGetAnnexPlaneCharacterSetNoAlloc(theOtherSet, idx))) { + CFCharacterSetUnion((CFMutableCharacterSetRef)__CFCSetGetAnnexPlaneCharacterSet(theSet, idx), otherSetPlane); + } + } + } else if (__CFCSetIsBuiltin(theOtherSet)) { + CFMutableCharacterSetRef annexPlane; + uint8_t bitmapBuffer[__kCFBitmapSize]; + uint8_t result; + int planeIndex; + Boolean isOtherAnnexPlaneInverted = __CFCSetAnnexIsInverted(theOtherSet); + UInt32 *bitmap1; + UInt32 *bitmap2; + CFIndex length; + + for (planeIndex = 1;planeIndex <= MAX_ANNEX_PLANE;planeIndex++) { + result = CFUniCharGetBitmapForPlane(__CFCSetBuiltinType(theOtherSet), planeIndex, bitmapBuffer, (isOtherAnnexPlaneInverted != 0)); + if (result != kCFUniCharBitmapEmpty) { + annexPlane = (CFMutableCharacterSetRef)__CFCSetGetAnnexPlaneCharacterSet(theSet, planeIndex); + if (result == kCFUniCharBitmapAll) { + CFCharacterSetAddCharactersInRange(annexPlane, CFRangeMake(0x0000, 0x10000)); + } else { + __CFCSetMakeBitmap(annexPlane); + bitmap1 = (UInt32 *)__CFCSetBitmapBits(annexPlane); + length = __kCFBitmapSize / sizeof(UInt32); + bitmap2 = (UInt32*)bitmapBuffer; + while (length--) *bitmap1++ |= *bitmap2++; + } + } + } + } + } + if (__CFCheckForExapendedSet) __CFCheckForExpandedSet(theSet); + } else { // It's NSCharacterSet + CFDataRef bitmapRep = CFCharacterSetCreateBitmapRepresentation(kCFAllocatorSystemDefault, theOtherSet); + const UInt32 *bitmap2 = (bitmapRep && CFDataGetLength(bitmapRep) ? (const UInt32 *)CFDataGetBytePtr(bitmapRep) : NULL); + if (bitmap2) { + UInt32 *bitmap1; + CFIndex length = __kCFBitmapSize / sizeof(UInt32); + __CFCSetMakeBitmap(theSet); + bitmap1 = (UInt32*)__CFCSetBitmapBits(theSet); + while (length--) *bitmap1++ |= *bitmap2++; + __CFCSetPutHasHashValue(theSet, false); + } + CFRelease(bitmapRep); + } +} + +void CFCharacterSetIntersect(CFMutableCharacterSetRef theSet, CFCharacterSetRef theOtherSet) { + CFCharacterSetRef expandedSet = NULL; + + CF_OBJC_FUNCDISPATCH1(__kCFCharacterSetTypeID, void, theSet, "formIntersectionWithCharacterSet:", theOtherSet); + + __CFCSetValidateTypeAndMutability(theSet, __PRETTY_FUNCTION__); + + if (__CFCSetIsEmpty(theSet) && !__CFCSetIsInverted(theSet)) return; // empty set + + if (!CF_IS_OBJC(__kCFCharacterSetTypeID, theOtherSet) || (expandedSet = __CFCharacterSetGetExpandedSetForNSCharacterSet(theOtherSet))) { // Really CF, we can do some trick here + if (expandedSet) theOtherSet = expandedSet; + + if (__CFCSetIsEmpty(theOtherSet)) { + if (!__CFCSetIsInverted(theOtherSet)) { + if (__CFCSetIsString(theSet) && __CFCSetStringBuffer(theSet)) { + CFAllocatorDeallocate(CFGetAllocator(theSet), __CFCSetStringBuffer(theSet)); + } else if (__CFCSetIsBitmap(theSet) && __CFCSetBitmapBits(theSet)) { + CFAllocatorDeallocate(CFGetAllocator(theSet), __CFCSetBitmapBits(theSet)); + } else if (__CFCSetIsCompactBitmap(theSet) && __CFCSetCompactBitmapBits(theSet)) { + CFAllocatorDeallocate(CFGetAllocator(theSet), __CFCSetCompactBitmapBits(theSet)); + } + __CFCSetPutClassType(theSet, __kCFCharSetClassBitmap); + __CFCSetPutBitmapBits(theSet, NULL); + __CFCSetPutIsInverted(theSet, false); + theSet->_hashValue = 0; + __CFCSetPutHasHashValue(theSet, true); + __CFCSetDeallocateAnnexPlane(theSet); + } + } else if (__CFCSetIsEmpty(theSet)) { // non inverted empty set contains all character + __CFCSetPutClassType(theSet, __CFCSetClassType(theOtherSet)); + __CFCSetPutHasHashValue(theSet, __CFCSetHasHashValue(theOtherSet)); + __CFCSetPutIsInverted(theSet, __CFCSetIsInverted(theOtherSet)); + theSet->_hashValue = theOtherSet->_hashValue; + if (__CFCSetHasNonBMPPlane(theOtherSet)) { + CFMutableCharacterSetRef otherSetPlane; + int idx; + for (idx = 1;idx <= MAX_ANNEX_PLANE;idx++) { + if ((otherSetPlane = (CFMutableCharacterSetRef)__CFCSetGetAnnexPlaneCharacterSetNoAlloc(theOtherSet, idx))) { + otherSetPlane = (CFMutableCharacterSetRef)CFCharacterSetCreateMutableCopy(CFGetAllocator(theSet), otherSetPlane); + __CFCSetPutCharacterSetToAnnexPlane(theSet, otherSetPlane, idx); + CFRelease(otherSetPlane); + } + } + __CFCSetAnnexSetIsInverted(theSet, __CFCSetAnnexIsInverted(theOtherSet)); + } + + switch (__CFCSetClassType(theOtherSet)) { + case __kCFCharSetClassBuiltin: + __CFCSetPutBuiltinType(theSet, __CFCSetBuiltinType(theOtherSet)); + break; + + case __kCFCharSetClassRange: + __CFCSetPutRangeFirstChar(theSet, __CFCSetRangeFirstChar(theOtherSet)); + __CFCSetPutRangeLength(theSet, __CFCSetRangeLength(theOtherSet)); + break; + + case __kCFCharSetClassString: + __CFCSetPutStringLength(theSet, __CFCSetStringLength(theOtherSet)); + if (!__CFCSetStringBuffer(theSet)) + __CFCSetPutStringBuffer(theSet, (UniChar *)CFAllocatorAllocate(CFGetAllocator(theSet), __kCFStringCharSetMax * sizeof(UniChar), 0)); + memmove(__CFCSetStringBuffer(theSet), __CFCSetStringBuffer(theOtherSet), __CFCSetStringLength(theSet) * sizeof(UniChar)); + break; + + case __kCFCharSetClassBitmap: + __CFCSetPutBitmapBits(theSet, (uint8_t *)CFAllocatorAllocate(CFGetAllocator(theSet), sizeof(uint8_t) * __kCFBitmapSize, 0)); + memmove(__CFCSetBitmapBits(theSet), __CFCSetBitmapBits(theOtherSet), __kCFBitmapSize); + break; + + case __kCFCharSetClassCompactBitmap: { + const uint8_t *cBitmap = __CFCSetCompactBitmapBits(theOtherSet); + uint8_t *newBitmap; + uint32_t size = __CFCSetGetCompactBitmapSize(cBitmap); + newBitmap = (uint8_t *)CFAllocatorAllocate(CFGetAllocator(theSet), sizeof(uint8_t) * size, 0); + __CFCSetPutBitmapBits(theSet, newBitmap); + memmove(newBitmap, cBitmap, size); + } + break; + + default: + CFAssert1(0, __kCFLogAssertion, "%s: Internal inconsistency error: unknown character set type", __PRETTY_FUNCTION__); // We should never come here + } + } else { + __CFCSetMakeBitmap(theSet); + if (__CFCSetIsBitmap(theOtherSet)) { + UInt32 *bitmap1 = (UInt32*)__CFCSetBitmapBits(theSet); + UInt32 *bitmap2 = (UInt32*)__CFCSetBitmapBits(theOtherSet); + CFIndex length = __kCFBitmapSize / sizeof(UInt32); + while (length--) *bitmap1++ &= *bitmap2++; + } else { + UInt32 *bitmap1 = (UInt32*)__CFCSetBitmapBits(theSet); + UInt32 *bitmap2; + CFIndex length = __kCFBitmapSize / sizeof(UInt32); + uint8_t bitmapBuffer[__kCFBitmapSize]; + __CFCSetGetBitmap(theOtherSet, bitmapBuffer); + bitmap2 = (UInt32*)bitmapBuffer; + while (length--) *bitmap1++ &= *bitmap2++; + } + __CFCSetPutHasHashValue(theSet, false); + if (__CFCSetHasNonBMPPlane(theOtherSet)) { + CFMutableCharacterSetRef annexPlane; + CFMutableCharacterSetRef otherSetPlane; + int idx; + for (idx = 1;idx <= MAX_ANNEX_PLANE;idx++) { + if ((otherSetPlane = (CFMutableCharacterSetRef)__CFCSetGetAnnexPlaneCharacterSetNoAlloc(theOtherSet, idx))) { + annexPlane = (CFMutableCharacterSetRef)__CFCSetGetAnnexPlaneCharacterSet(theSet, idx); + CFCharacterSetIntersect(annexPlane, otherSetPlane); + if (__CFCSetIsEmpty(annexPlane) && !__CFCSetIsInverted(annexPlane)) __CFCSetPutCharacterSetToAnnexPlane(theSet, NULL, idx); + } else if (__CFCSetGetAnnexPlaneCharacterSetNoAlloc(theSet, idx)) { + __CFCSetPutCharacterSetToAnnexPlane(theSet, NULL, idx); + } + } + if (!__CFCSetHasNonBMPPlane(theSet)) __CFCSetDeallocateAnnexPlane(theSet); + } else if (__CFCSetIsBuiltin(theOtherSet)) { + CFMutableCharacterSetRef annexPlane; + uint8_t bitmapBuffer[__kCFBitmapSize]; + uint8_t result; + int planeIndex; + Boolean isOtherAnnexPlaneInverted = __CFCSetAnnexIsInverted(theOtherSet); + UInt32 *bitmap1; + UInt32 *bitmap2; + CFIndex length; + + for (planeIndex = 1;planeIndex <= MAX_ANNEX_PLANE;planeIndex++) { + annexPlane = (CFMutableCharacterSetRef)__CFCSetGetAnnexPlaneCharacterSetNoAlloc(theSet, planeIndex); + if (annexPlane) { + result = CFUniCharGetBitmapForPlane(__CFCSetBuiltinType(theOtherSet), planeIndex, bitmapBuffer, (isOtherAnnexPlaneInverted != 0)); + if (result == kCFUniCharBitmapEmpty) { + __CFCSetPutCharacterSetToAnnexPlane(theSet, NULL, planeIndex); + } else if (result == kCFUniCharBitmapFilled) { + Boolean isEmpty = true; + + __CFCSetMakeBitmap(annexPlane); + bitmap1 = (UInt32 *)__CFCSetBitmapBits(annexPlane); + length = __kCFBitmapSize / sizeof(UInt32); + bitmap2 = (UInt32*)bitmapBuffer; + + while (length--) { + if ((*bitmap1++ &= *bitmap2++)) isEmpty = false; + } + if (isEmpty) __CFCSetPutCharacterSetToAnnexPlane(theSet, NULL, planeIndex); + } + } + } + if (!__CFCSetHasNonBMPPlane(theSet)) __CFCSetDeallocateAnnexPlane(theSet); + } else if (__CFCSetIsRange(theOtherSet)) { + CFMutableCharacterSetRef tempOtherSet = CFCharacterSetCreateMutable(CFGetAllocator(theSet)); + CFMutableCharacterSetRef annexPlane; + CFMutableCharacterSetRef otherSetPlane; + int idx; + + __CFCSetAddNonBMPPlanesInRange(tempOtherSet, CFRangeMake(__CFCSetRangeFirstChar(theOtherSet), __CFCSetRangeLength(theOtherSet))); + + for (idx = 1;idx <= MAX_ANNEX_PLANE;idx++) { + if ((otherSetPlane = (CFMutableCharacterSetRef)__CFCSetGetAnnexPlaneCharacterSetNoAlloc(tempOtherSet, idx))) { + annexPlane = (CFMutableCharacterSetRef)__CFCSetGetAnnexPlaneCharacterSet(theSet, idx); + CFCharacterSetIntersect(annexPlane, otherSetPlane); + if (__CFCSetIsEmpty(annexPlane) && !__CFCSetIsInverted(annexPlane)) __CFCSetPutCharacterSetToAnnexPlane(theSet, NULL, idx); + } else if (__CFCSetGetAnnexPlaneCharacterSetNoAlloc(theSet, idx)) { + __CFCSetPutCharacterSetToAnnexPlane(theSet, NULL, idx); + } + } + if (!__CFCSetHasNonBMPPlane(theSet)) __CFCSetDeallocateAnnexPlane(theSet); + CFRelease(tempOtherSet); + } else if (__CFCSetHasNonBMPPlane(theSet)) { + __CFCSetDeallocateAnnexPlane(theSet); + } + } + if (__CFCheckForExapendedSet) __CFCheckForExpandedSet(theSet); + } else { // It's NSCharacterSet + CFDataRef bitmapRep = CFCharacterSetCreateBitmapRepresentation(kCFAllocatorSystemDefault, theOtherSet); + const UInt32 *bitmap2 = (bitmapRep && CFDataGetLength(bitmapRep) ? (const UInt32 *)CFDataGetBytePtr(bitmapRep) : NULL); + if (bitmap2) { + UInt32 *bitmap1; + CFIndex length = __kCFBitmapSize / sizeof(UInt32); + __CFCSetMakeBitmap(theSet); + bitmap1 = (UInt32*)__CFCSetBitmapBits(theSet); + while (length--) *bitmap1++ &= *bitmap2++; + __CFCSetPutHasHashValue(theSet, false); + } + CFRelease(bitmapRep); + } +} + +void CFCharacterSetInvert(CFMutableCharacterSetRef theSet) { + + CF_OBJC_FUNCDISPATCH0(__kCFCharacterSetTypeID, void, theSet, "invert"); + + __CFCSetValidateTypeAndMutability(theSet, __PRETTY_FUNCTION__); + + __CFCSetPutHasHashValue(theSet, false); + + if (__CFCSetClassType(theSet) == __kCFCharSetClassBitmap) { + CFIndex idx; + CFIndex count = __kCFBitmapSize / sizeof(UInt32); + UInt32 *bitmap = (UInt32*) __CFCSetBitmapBits(theSet); + + if (NULL == bitmap) { + bitmap = (UInt32 *)CFAllocatorAllocate(CFGetAllocator(theSet), __kCFBitmapSize, 0); + __CFCSetPutBitmapBits(theSet, (uint8_t *)bitmap); + for (idx = 0;idx < count;idx++) bitmap[idx] = ((UInt32)0xFFFFFFFF); + } else { + for (idx = 0;idx < count;idx++) bitmap[idx] = ~(bitmap[idx]); + } + __CFCSetAllocateAnnexForPlane(theSet, 0); // We need to alloc annex to invert + } else if (__CFCSetClassType(theSet) == __kCFCharSetClassCompactBitmap) { + uint8_t *bitmap = __CFCSetCompactBitmapBits(theSet); + int idx; + int length = 0; + uint8_t value; + + for (idx = 0;idx < __kCFCompactBitmapNumPages;idx++) { + value = bitmap[idx]; + + if (value == 0) { + bitmap[idx] = UINT8_MAX; + } else if (value == UINT8_MAX) { + bitmap[idx] = 0; + } else { + length += __kCFCompactBitmapPageSize; + } + } + bitmap += __kCFCompactBitmapNumPages; + for (idx = 0;idx < length;idx++) bitmap[idx] = ~(bitmap[idx]); + __CFCSetAllocateAnnexForPlane(theSet, 0); // We need to alloc annex to invert + } else { + __CFCSetPutIsInverted(theSet, !__CFCSetIsInverted(theSet)); + } + __CFCSetAnnexSetIsInverted(theSet, !__CFCSetAnnexIsInverted(theSet)); +} + +void CFCharacterSetCompact(CFMutableCharacterSetRef theSet) { + if (__CFCSetIsBitmap(theSet) && __CFCSetBitmapBits(theSet)) __CFCSetMakeCompact(theSet); + if (__CFCSetHasNonBMPPlane(theSet)) { + CFMutableCharacterSetRef annex; + int idx; + + for (idx = 1;idx <= MAX_ANNEX_PLANE;idx++) { + if ((annex = (CFMutableCharacterSetRef)__CFCSetGetAnnexPlaneCharacterSetNoAlloc(theSet, idx)) && __CFCSetIsBitmap(annex) && __CFCSetBitmapBits(annex)) { + __CFCSetMakeCompact(annex); + } + } + } +} + +void CFCharacterSetFast(CFMutableCharacterSetRef theSet) { + if (__CFCSetIsCompactBitmap(theSet) && __CFCSetCompactBitmapBits(theSet)) __CFCSetMakeBitmap(theSet); + if (__CFCSetHasNonBMPPlane(theSet)) { + CFMutableCharacterSetRef annex; + int idx; + + for (idx = 1;idx <= MAX_ANNEX_PLANE;idx++) { + if ((annex = (CFMutableCharacterSetRef)__CFCSetGetAnnexPlaneCharacterSetNoAlloc(theSet, idx)) && __CFCSetIsCompactBitmap(annex) && __CFCSetCompactBitmapBits(annex)) { + __CFCSetMakeBitmap(annex); + } + } + } +} + +/* Keyed-coding support +*/ +CFCharacterSetKeyedCodingType _CFCharacterSetGetKeyedCodingType(CFCharacterSetRef cset) { + switch (__CFCSetClassType(cset)) { + case __kCFCharSetClassBuiltin: return ((__CFCSetBuiltinType(cset) < kCFCharacterSetSymbol) ? kCFCharacterSetKeyedCodingTypeBuiltin : kCFCharacterSetKeyedCodingTypeBuiltinAndBitmap); + case __kCFCharSetClassRange: return kCFCharacterSetKeyedCodingTypeRange; + + case __kCFCharSetClassString: // We have to check if we have non-BMP here + if (!__CFCSetHasNonBMPPlane(cset) && !__CFCSetAnnexIsInverted(cset)) return kCFCharacterSetKeyedCodingTypeString; // BMP only. we can archive the string + /* fallthrough */ + + default: + return kCFCharacterSetKeyedCodingTypeBitmap; + } +} + +bool _CFCharacterSetIsMutable(CFCharacterSetRef cset) { return __CFCSetIsMutable(cset); } +CFCharacterSetPredefinedSet _CFCharacterSetGetKeyedCodingBuiltinType(CFCharacterSetRef cset) { return __CFCSetBuiltinType(cset); } +CFRange _CFCharacterSetGetKeyedCodingRange(CFCharacterSetRef cset) { return CFRangeMake(__CFCSetRangeFirstChar(cset), __CFCSetRangeLength(cset)); } +CFStringRef _CFCharacterSetCreateKeyedCodingString(CFCharacterSetRef cset) { return CFStringCreateWithCharacters(kCFAllocatorSystemDefault, __CFCSetStringBuffer(cset), __CFCSetStringLength(cset)); } + +bool _CFCharacterSetIsInverted(CFCharacterSetRef cset) { return (__CFCSetIsInverted(cset) != 0); } +void _CFCharacterSetSetIsInverted(CFCharacterSetRef cset, bool flag) { __CFCSetPutIsInverted((CFMutableCharacterSetRef)cset, flag); } + +/* Inline buffer support +*/ +void CFCharacterSetInitInlineBuffer(CFCharacterSetRef cset, CFCharacterSetInlineBuffer *buffer) { + memset(buffer, 0, sizeof(CFCharacterSetInlineBuffer)); + buffer->cset = cset; + buffer->rangeLimit = 0x10000; + + if (CF_IS_OBJC(__kCFCharacterSetTypeID, cset)) { + CFCharacterSetRef expandedSet = __CFCharacterSetGetExpandedSetForNSCharacterSet(cset); + + if (NULL == expandedSet) { + buffer->flags = kCFCharacterSetNoBitmapAvailable; + buffer->rangeLimit = 0x110000; + + return; + } else { + cset = expandedSet; + } + } + + switch (__CFCSetClassType(cset)) { + case __kCFCharSetClassBuiltin: + buffer->bitmap = CFUniCharGetBitmapPtrForPlane(__CFCSetBuiltinType(cset), 0); + buffer->rangeLimit = 0x110000; + if (NULL == buffer->bitmap) { + buffer->flags = kCFCharacterSetNoBitmapAvailable; + } else { + if (__CFCSetIsInverted(cset)) buffer->flags = kCFCharacterSetIsInverted; + } + break; + + case __kCFCharSetClassRange: + buffer->rangeStart = __CFCSetRangeFirstChar(cset); + buffer->rangeLimit = __CFCSetRangeFirstChar(cset) + __CFCSetRangeLength(cset); + if (__CFCSetIsInverted(cset)) buffer->flags = kCFCharacterSetIsInverted; + return; + + case __kCFCharSetClassString: + buffer->flags = kCFCharacterSetNoBitmapAvailable; + if (__CFCSetStringLength(cset) > 0) { + buffer->rangeStart = *__CFCSetStringBuffer(cset); + buffer->rangeLimit = *(__CFCSetStringBuffer(cset) + __CFCSetStringLength(cset) - 1) + 1; + + if (__CFCSetIsInverted(cset)) { + if (0 == buffer->rangeStart) { + buffer->rangeStart = buffer->rangeLimit; + buffer->rangeLimit = 0x10000; + } else if (0x10000 == buffer->rangeLimit) { + buffer->rangeLimit = buffer->rangeStart; + buffer->rangeStart = 0; + } else { + buffer->rangeStart = 0; + buffer->rangeLimit = 0x10000; + } + } + } + break; + + case __kCFCharSetClassBitmap: + case __kCFCharSetClassCompactBitmap: + buffer->bitmap = __CFCSetCompactBitmapBits(cset); + if (NULL == buffer->bitmap) { + buffer->flags = kCFCharacterSetIsCompactBitmap; + if (__CFCSetIsInverted(cset)) buffer->flags |= kCFCharacterSetIsInverted; + } else { + if (__kCFCharSetClassCompactBitmap == __CFCSetClassType(cset)) buffer->flags = kCFCharacterSetIsCompactBitmap; + } + break; + + default: + CFAssert1(0, __kCFLogAssertion, "%s: Internal inconsistency error: unknown character set type", __PRETTY_FUNCTION__); // We should never come here + return; + } + + if (__CFCSetAnnexIsInverted(cset)) { + buffer->rangeLimit = 0x110000; + } else if (__CFCSetHasNonBMPPlane(cset)) { + CFIndex index; + + for (index = MAX_ANNEX_PLANE;index > 0;index--) { + if (NULL != __CFCSetGetAnnexPlaneCharacterSetNoAlloc(cset, index)) { + buffer->rangeLimit = (index + 1) << 16; + break; + } + } + } +}
diff --git a/CoreFoundation/CFCharacterSet.h b/CoreFoundation/CFCharacterSet.h new file mode 100644 index 0000000..9360965 --- /dev/null +++ b/CoreFoundation/CFCharacterSet.h
@@ -0,0 +1,425 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFCharacterSet.h + Copyright (c) 1999-2007, Apple Inc. All rights reserved. +*/ + +/*! + @header CFCharacterSet + CFCharacterSet represents a set, or a bag, of Unicode characters. + The API consists of 3 groups: + 1) creation/manipulation of CFCharacterSet instances, + 2) query of a single Unicode character membership, + and 3) bitmap representation related (reading/writing). + Conceptually, CFCharacterSet is a 136K byte bitmap array of + which each bit represents a Unicode code point. It could + contain the Unicode characters in ISO 10646 Basic Multilingual + Plane (BMP) and characters in Plane 1 through Plane 16 + accessible via surrogate paris in the Unicode Transformation + Format, 16-bit encoding form (UTF-16). In other words, it can + store values from 0x00000 to 0x10FFFF in the Unicode + Transformation Format, 32-bit encoding form (UTF-32). However, + in general, how CFCharacterSet stores the information is an + implementation detail. Note even CFData used for the external + bitmap representation rarely has 136K byte. For detailed + discussion of the external bitmap representation, refer to the + comments for CFCharacterSetCreateWithBitmapRepresentation below. + Note that the existance of non-BMP characters in a character set + does not imply the membership of the corresponding surrogate + characters. For example, a character set with U+10000 does not + match with U+D800. +*/ + +#if !defined(__COREFOUNDATION_CFCHARACTERSET__) +#define __COREFOUNDATION_CFCHARACTERSET__ 1 + +#include <CoreFoundation/CFBase.h> +#include <CoreFoundation/CFData.h> + +CF_EXTERN_C_BEGIN + +/*! + @typedef CFCharacterSetRef + This is the type of a reference to immutable CFCharacterSets. +*/ +typedef const struct __CFCharacterSet * CFCharacterSetRef; + +/*! + @typedef CFMutableCharacterSetRef + This is the type of a reference to mutable CFMutableCharacterSets. +*/ +typedef struct __CFCharacterSet * CFMutableCharacterSetRef; + +/*! + @typedef CFCharacterSetPredefinedSet + Type of the predefined CFCharacterSet selector values. +*/ + +enum { + kCFCharacterSetControl = 1, /* Control character set (Unicode General Category Cc and Cf) */ + kCFCharacterSetWhitespace, /* Whitespace character set (Unicode General Category Zs and U0009 CHARACTER TABULATION) */ + kCFCharacterSetWhitespaceAndNewline, /* Whitespace and Newline character set (Unicode General Category Z*, U000A ~ U000D, and U0085) */ + kCFCharacterSetDecimalDigit, /* Decimal digit character set */ + kCFCharacterSetLetter, /* Letter character set (Unicode General Category L* & M*) */ + kCFCharacterSetLowercaseLetter, /* Lowercase character set (Unicode General Category Ll) */ + kCFCharacterSetUppercaseLetter, /* Uppercase character set (Unicode General Category Lu and Lt) */ + kCFCharacterSetNonBase, /* Non-base character set (Unicode General Category M*) */ + kCFCharacterSetDecomposable, /* Canonically decomposable character set */ + kCFCharacterSetAlphaNumeric, /* Alpha Numeric character set (Unicode General Category L*, M*, & N*) */ + kCFCharacterSetPunctuation, /* Punctuation character set (Unicode General Category P*) */ +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED + kCFCharacterSetCapitalizedLetter = 13, /* Titlecase character set (Unicode General Category Lt) */ +#endif +#if MAC_OS_X_VERSION_10_3 <= MAC_OS_X_VERSION_MAX_ALLOWED + kCFCharacterSetSymbol = 14, /* Symbol character set (Unicode General Category S*) */ +#endif +#if MAC_OS_X_VERSION_10_5 <= MAC_OS_X_VERSION_MAX_ALLOWED + kCFCharacterSetNewline = 15, /* Newline character set (U000A ~ U000D, U0085, U2028, and U2029) */ +#endif + kCFCharacterSetIllegal = 12/* Illegal character set */ +}; +typedef CFIndex CFCharacterSetPredefinedSet; + +/*! + @function CFCharacterSetGetTypeID + Returns the type identifier of all CFCharacterSet instances. +*/ +CF_EXPORT +CFTypeID CFCharacterSetGetTypeID(void); + +/*! + @function CFCharacterSetGetPredefined + Returns a predefined CFCharacterSet instance. + @param theSetIdentifier The CFCharacterSetPredefinedSet selector + which specifies the predefined character set. If the + value is not in CFCharacterSetPredefinedSet, the behavior + is undefined. + @result A reference to the predefined immutable CFCharacterSet. + This instance is owned by CF. +*/ +CF_EXPORT +CFCharacterSetRef CFCharacterSetGetPredefined(CFCharacterSetPredefinedSet theSetIdentifier); + +/*! + @function CFCharacterSetCreateWithCharactersInRange + Creates a new immutable character set with the values from the given range. + @param alloc The CFAllocator which should be used to allocate + memory for the array and its storage for values. This + parameter may be NULL in which case the current default + CFAllocator is used. If this reference is not a valid + CFAllocator, the behavior is undefined. + @param theRange The CFRange which should be used to specify the + Unicode range the character set is filled with. It + accepts the range in 32-bit in the UTF-32 format. The + valid character point range is from 0x00000 to 0x10FFFF. + If the range is outside of the valid Unicode character + point, the behavior is undefined. + @result A reference to the new immutable CFCharacterSet. +*/ +CF_EXPORT +CFCharacterSetRef CFCharacterSetCreateWithCharactersInRange(CFAllocatorRef alloc, CFRange theRange); + +/*! + @function CFCharacterSetCreateWithCharactersInString + Creates a new immutable character set with the values in the given string. + @param alloc The CFAllocator which should be used to allocate + memory for the array and its storage for values. This + parameter may be NULL in which case the current default + CFAllocator is used. If this reference is not a valid + CFAllocator, the behavior is undefined. + @param theString The CFString which should be used to specify + the Unicode characters the character set is filled with. + If this parameter is not a valid CFString, the behavior + is undefined. + @result A reference to the new immutable CFCharacterSet. +*/ +CF_EXPORT +CFCharacterSetRef CFCharacterSetCreateWithCharactersInString(CFAllocatorRef alloc, CFStringRef theString); + +/*! + @function CFCharacterSetCreateWithBitmapRepresentation + Creates a new immutable character set with the bitmap representtion in the given data. + @param alloc The CFAllocator which should be used to allocate + memory for the array and its storage for values. This + parameter may be NULL in which case the current default + CFAllocator is used. If this reference is not a valid + CFAllocator, the behavior is undefined. + @param theData The CFData which should be used to specify the + bitmap representation of the Unicode character points + the character set is filled with. The bitmap + representation could contain all the Unicode character + range starting from BMP to Plane 16. The first 8192 bytes + of the data represent the BMP range. The BMP range 8192 + bytes can be followed by zero to sixteen 8192 byte + bitmaps, each one with the plane index byte prepended. + For example, the bitmap representing the BMP and Plane 2 + has the size of 16385 bytes (8192 bytes for BMP, 1 byte + index + 8192 bytes bitmap for Plane 2). The plane index + byte, in this case, contains the integer value two. If + this parameter is not a valid CFData or it contains a + Plane index byte outside of the valid Plane range + (1 to 16), the behavior is undefined. + @result A reference to the new immutable CFCharacterSet. +*/ +CF_EXPORT +CFCharacterSetRef CFCharacterSetCreateWithBitmapRepresentation(CFAllocatorRef alloc, CFDataRef theData); + +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED +/*! + @function CFCharacterSetCreateInvertedSet + Creates a new immutable character set that is the invert of the specified character set. + @param alloc The CFAllocator which should be used to allocate + memory for the array and its storage for values. This + parameter may be NULL in which case the current default + CFAllocator is used. If this reference is not a valid + CFAllocator, the behavior is undefined. + @param theSet The CFCharacterSet which is to be inverted. If this + parameter is not a valid CFCharacterSet, the behavior is + undefined. + @result A reference to the new immutable CFCharacterSet. +*/ +CF_EXPORT CFCharacterSetRef CFCharacterSetCreateInvertedSet(CFAllocatorRef alloc, CFCharacterSetRef theSet); + +/*! + @function CFCharacterSetIsSupersetOfSet + Reports whether or not the character set is a superset of the character set specified as the second parameter. + @param theSet The character set to be checked for the membership of theOtherSet. + If this parameter is not a valid CFCharacterSet, the behavior is undefined. + @param theOtherset The character set to be checked whether or not it is a subset of theSet. + If this parameter is not a valid CFCharacterSet, the behavior is undefined. +*/ +CF_EXPORT Boolean CFCharacterSetIsSupersetOfSet(CFCharacterSetRef theSet, CFCharacterSetRef theOtherset); + +/*! + @function CFCharacterSetHasMemberInPlane + Reports whether or not the character set contains at least one member character in the specified plane. + @param theSet The character set to be checked for the membership. If this + parameter is not a valid CFCharacterSet, the behavior is undefined. + @param thePlane The plane number to be checked for the membership. + The valid value range is from 0 to 16. If the value is outside of the valid + plane number range, the behavior is undefined. +*/ +CF_EXPORT Boolean CFCharacterSetHasMemberInPlane(CFCharacterSetRef theSet, CFIndex thePlane); +#endif + +/*! + @function CFCharacterSetCreateMutable + Creates a new empty mutable character set. + @param allocator The CFAllocator which should be used to allocate + memory for the array and its storage for values. This + parameter may be NULL in which case the current default + CFAllocator is used. If this reference is not a valid + CFAllocator, the behavior is undefined. + @result A reference to the new mutable CFCharacterSet. +*/ +CF_EXPORT +CFMutableCharacterSetRef CFCharacterSetCreateMutable(CFAllocatorRef alloc); + +#if MAC_OS_X_VERSION_10_3 <= MAC_OS_X_VERSION_MAX_ALLOWED +/*! + @function CFCharacterSetCreateCopy + Creates a new character set with the values from the given character set. This function tries to compact the backing store where applicable. + @param allocator The CFAllocator which should be used to allocate + memory for the array and its storage for values. This + parameter may be NULL in which case the current default + CFAllocator is used. If this reference is not a valid + CFAllocator, the behavior is undefined. + @param theSet The CFCharacterSet which is to be copied. If this + parameter is not a valid CFCharacterSet, the behavior is + undefined. + @result A reference to the new CFCharacterSet. +*/ +CF_EXPORT +CFCharacterSetRef CFCharacterSetCreateCopy(CFAllocatorRef alloc, CFCharacterSetRef theSet) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; +#endif + +/*! + @function CFCharacterSetCreateMutableCopy + Creates a new mutable character set with the values from the given character set. + @param allocator The CFAllocator which should be used to allocate + memory for the array and its storage for values. This + parameter may be NULL in which case the current default + CFAllocator is used. If this reference is not a valid + CFAllocator, the behavior is undefined. + @param theSet The CFCharacterSet which is to be copied. If this + parameter is not a valid CFCharacterSet, the behavior is + undefined. + @result A reference to the new mutable CFCharacterSet. +*/ +CF_EXPORT +CFMutableCharacterSetRef CFCharacterSetCreateMutableCopy(CFAllocatorRef alloc, CFCharacterSetRef theSet); + +/*! + @function CFCharacterSetIsCharacterMember + Reports whether or not the Unicode character is in the character set. + @param theSet The character set to be searched. If this parameter + is not a valid CFCharacterSet, the behavior is undefined. + @param theChar The Unicode character for which to test against the + character set. Note that this function takes 16-bit Unicode + character value; hence, it does not support access to the + non-BMP planes. + @result true, if the value is in the character set, otherwise false. +*/ +CF_EXPORT +Boolean CFCharacterSetIsCharacterMember(CFCharacterSetRef theSet, UniChar theChar); + +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED +/*! + @function CFCharacterSetIsLongCharacterMember + Reports whether or not the UTF-32 character is in the character set. + @param theSet The character set to be searched. If this parameter + is not a valid CFCharacterSet, the behavior is undefined. + @param theChar The UTF-32 character for which to test against the + character set. + @result true, if the value is in the character set, otherwise false. +*/ +CF_EXPORT Boolean CFCharacterSetIsLongCharacterMember(CFCharacterSetRef theSet, UTF32Char theChar); +#endif + +/*! + @function CFCharacterSetCreateBitmapRepresentation + Creates a new immutable data with the bitmap representation from the given character set. + @param allocator The CFAllocator which should be used to allocate + memory for the array and its storage for values. This + parameter may be NULL in which case the current default + CFAllocator is used. If this reference is not a valid + CFAllocator, the behavior is undefined. + @param theSet The CFCharacterSet which is to be used create the + bitmap representation from. Refer to the comments for + CFCharacterSetCreateWithBitmapRepresentation for the + detailed discussion of the bitmap representation format. + If this parameter is not a valid CFCharacterSet, the + behavior is undefined. + @result A reference to the new immutable CFData. +*/ +CF_EXPORT +CFDataRef CFCharacterSetCreateBitmapRepresentation(CFAllocatorRef alloc, CFCharacterSetRef theSet); + +/*! + @function CFCharacterSetAddCharactersInRange + Adds the given range to the charaacter set. + @param theSet The character set to which the range is to be added. + If this parameter is not a valid mutable CFCharacterSet, + the behavior is undefined. + @param theRange The range to add to the character set. It accepts + the range in 32-bit in the UTF-32 format. The valid + character point range is from 0x00000 to 0x10FFFF. If the + range is outside of the valid Unicode character point, + the behavior is undefined. +*/ +CF_EXPORT +void CFCharacterSetAddCharactersInRange(CFMutableCharacterSetRef theSet, CFRange theRange); + +/*! + @function CFCharacterSetRemoveCharactersInRange + Removes the given range from the charaacter set. + @param theSet The character set from which the range is to be + removed. If this parameter is not a valid mutable + CFCharacterSet, the behavior is undefined. + @param theRange The range to remove from the character set. + It accepts the range in 32-bit in the UTF-32 format. + The valid character point range is from 0x00000 to 0x10FFFF. + If the range is outside of the valid Unicode character point, + the behavior is undefined. +*/ +CF_EXPORT +void CFCharacterSetRemoveCharactersInRange(CFMutableCharacterSetRef theSet, CFRange theRange); + +/*! + @function CFCharacterSetAddCharactersInString + Adds the characters in the given string to the charaacter set. + @param theSet The character set to which the characters in the + string are to be added. If this parameter is not a + valid mutable CFCharacterSet, the behavior is undefined. + @param theString The string to add to the character set. + If this parameter is not a valid CFString, the behavior + is undefined. +*/ +CF_EXPORT +void CFCharacterSetAddCharactersInString(CFMutableCharacterSetRef theSet, CFStringRef theString); + +/*! + @function CFCharacterSetRemoveCharactersInString + Removes the characters in the given string from the charaacter set. + @param theSet The character set from which the characters in the + string are to be remove. If this parameter is not a + valid mutable CFCharacterSet, the behavior is undefined. + @param theString The string to remove from the character set. + If this parameter is not a valid CFString, the behavior + is undefined. +*/ +CF_EXPORT +void CFCharacterSetRemoveCharactersInString(CFMutableCharacterSetRef theSet, CFStringRef theString); + +/*! + @function CFCharacterSetUnion + Forms the union with the given character set. + @param theSet The destination character set into which the + union of the two character sets is stored. If this + parameter is not a valid mutable CFCharacterSet, the + behavior is undefined. + @param theOtherSet The character set with which the union is + formed. If this parameter is not a valid CFCharacterSet, + the behavior is undefined. +*/ +CF_EXPORT +void CFCharacterSetUnion(CFMutableCharacterSetRef theSet, CFCharacterSetRef theOtherSet); + +/*! + @function CFCharacterSetIntersect + Forms the intersection with the given character set. + @param theSet The destination character set into which the + intersection of the two character sets is stored. + If this parameter is not a valid mutable CFCharacterSet, + the behavior is undefined. + @param theOtherSet The character set with which the intersection + is formed. If this parameter is not a valid CFCharacterSet, + the behavior is undefined. +*/ +CF_EXPORT +void CFCharacterSetIntersect(CFMutableCharacterSetRef theSet, CFCharacterSetRef theOtherSet); + +/*! + @function CFCharacterSetInvert + Inverts the content of the given character set. + @param theSet The character set to be inverted. + If this parameter is not a valid mutable CFCharacterSet, + the behavior is undefined. +*/ +CF_EXPORT +void CFCharacterSetInvert(CFMutableCharacterSetRef theSet); + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFCHARACTERSET__ */ +
diff --git a/CoreFoundation/CFCharacterSetBitmaps.bitmap b/CoreFoundation/CFCharacterSetBitmaps.bitmap new file mode 100644 index 0000000..b0874f9 --- /dev/null +++ b/CoreFoundation/CFCharacterSetBitmaps.bitmap Binary files differ
diff --git a/CoreFoundation/CFCharacterSetPriv.h b/CoreFoundation/CFCharacterSetPriv.h new file mode 100644 index 0000000..6d65653 --- /dev/null +++ b/CoreFoundation/CFCharacterSetPriv.h
@@ -0,0 +1,103 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFCharacterSetPriv.h + Copyright (c) 1998-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFCHARACTERSETPRIV__) +#define __COREFOUNDATION_CFCHARACTERSETPRIV__ 1 + +#include <CoreFoundation/CFCharacterSet.h> + +CF_EXTERN_C_BEGIN + +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED +/*! + @function CFCharacterSetIsSurrogateHighCharacter + Reports whether or not the character is a high surrogate. + @param character The character to be checked. + @result true, if character is a high surrogate, otherwise false. +*/ +CF_INLINE Boolean CFCharacterSetIsSurrogateHighCharacter(UniChar character) { + return ((character >= 0xD800UL) && (character <= 0xDBFFUL) ? true : false); +} + +/*! + @function CFCharacterSetIsSurrogateLowCharacter + Reports whether or not the character is a low surrogate. + @param character The character to be checked. + @result true, if character is a low surrogate, otherwise false. +*/ +CF_INLINE Boolean CFCharacterSetIsSurrogateLowCharacter(UniChar character) { + return ((character >= 0xDC00UL) && (character <= 0xDFFFUL) ? true : false); +} + +/*! + @function CFCharacterSetGetLongCharacterForSurrogatePair + Returns the UTF-32 value corresponding to the surrogate pair passed in. + @param surrogateHigh The high surrogate character. If this parameter + is not a valid high surrogate character, the behavior is undefined. + @param surrogateLow The low surrogate character. If this parameter + is not a valid low surrogate character, the behavior is undefined. + @result The UTF-32 value for the surrogate pair. +*/ +CF_INLINE UTF32Char CFCharacterSetGetLongCharacterForSurrogatePair(UniChar surrogateHigh, UniChar surrogateLow) { + return ((surrogateHigh - 0xD800UL) << 10) + (surrogateLow - 0xDC00UL) + 0x0010000UL; +} +#endif + +/* Check to see if the character represented by the surrogate pair surrogateHigh & surrogateLow is in the chraracter set */ +CF_EXPORT Boolean CFCharacterSetIsSurrogatePairMember(CFCharacterSetRef theSet, UniChar surrogateHigh, UniChar surrogateLow) ; + +/* Keyed-coding support +*/ +enum { + kCFCharacterSetKeyedCodingTypeBitmap = 1, + kCFCharacterSetKeyedCodingTypeBuiltin = 2, + kCFCharacterSetKeyedCodingTypeRange = 3, + kCFCharacterSetKeyedCodingTypeString = 4, + kCFCharacterSetKeyedCodingTypeBuiltinAndBitmap = 5 +}; +typedef CFIndex CFCharacterSetKeyedCodingType; + +CF_EXPORT CFCharacterSetKeyedCodingType _CFCharacterSetGetKeyedCodingType(CFCharacterSetRef cset); +CF_EXPORT CFCharacterSetPredefinedSet _CFCharacterSetGetKeyedCodingBuiltinType(CFCharacterSetRef cset); +CF_EXPORT CFRange _CFCharacterSetGetKeyedCodingRange(CFCharacterSetRef cset); +CF_EXPORT CFStringRef _CFCharacterSetCreateKeyedCodingString(CFCharacterSetRef cset); +CF_EXPORT bool _CFCharacterSetIsInverted(CFCharacterSetRef cset); +CF_EXPORT void _CFCharacterSetSetIsInverted(CFCharacterSetRef cset, bool flag); + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFCHARACTERSETPRIV__ */ +
diff --git a/CoreFoundation/CFConcreteStreams.c b/CoreFoundation/CFConcreteStreams.c new file mode 100644 index 0000000..6218446 --- /dev/null +++ b/CoreFoundation/CFConcreteStreams.c
@@ -0,0 +1,870 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFConcreteStreams.c + Copyright 2000-2002, Apple, Inc. All rights reserved. + Responsibility: Becky Willrich +*/ + +#define _DARWIN_UNLIMITED_SELECT 1 + +#include "CFStreamInternal.h" +#include "CFInternal.h" +#include "CFPriv.h" +#include <CoreFoundation/CFNumber.h> +#include <sys/types.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <stdio.h> +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX +#include <sys/time.h> +#include <unistd.h> +#elif DEPLOYMENT_TARGET_WINDOWS +#include <io.h> +#define lseek _lseek +#define open _open +#define read _read +#define write _write +#define close _close +#endif + +// On Unix, you can schedule an fd with the RunLoop by creating a CFSocket around it. On Win32 +// files and sockets are not interchangeable, and we do cheapo scheduling, where the file is +// always readable and writable until we hit EOF (similar to the way CFData streams are scheduled). +#if DEPLOYMENT_TARGET_MACOSX +#define REAL_FILE_SCHEDULING (1) +#endif + +#define SCHEDULE_AFTER_WRITE (0) +#define SCHEDULE_AFTER_READ (1) +#define APPEND (3) +#define AT_EOF (4) +#define USE_RUNLOOP_ARRAY (5) + + +/* File callbacks */ +typedef struct { + CFURLRef url; + int fd; +#ifdef REAL_FILE_SCHEDULING + union { + CFSocketRef sock; // socket created once we open and have an fd + CFMutableArrayRef rlArray; // scheduling information prior to open + } rlInfo; // If fd > 0, sock exists. Otherwise, rlArray. +#else + uint16_t scheduled; // ref count of how many times we've been scheduled +#endif + CFOptionFlags flags; + off_t offset; +} _CFFileStreamContext; + + +CONST_STRING_DECL(kCFStreamPropertyFileCurrentOffset, "kCFStreamPropertyFileCurrentOffset"); + + +#ifdef REAL_FILE_SCHEDULING +static void fileCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info); + +static void constructCFSocket(_CFFileStreamContext *fileStream, Boolean forRead, struct _CFStream *stream) { + CFSocketContext context = {0, stream, NULL, NULL, CFCopyDescription}; + CFSocketRef sock = CFSocketCreateWithNative(CFGetAllocator(stream), fileStream->fd, forRead ? kCFSocketReadCallBack : kCFSocketWriteCallBack, fileCallBack, &context); + CFSocketSetSocketFlags(sock, 0); + if (fileStream->rlInfo.rlArray) { + CFIndex i, c = CFArrayGetCount(fileStream->rlInfo.rlArray); + CFRunLoopSourceRef src = CFSocketCreateRunLoopSource(CFGetAllocator(stream), sock, 0); + for (i = 0; i+1 < c; i += 2) { + CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(fileStream->rlInfo.rlArray, i); + CFStringRef mode = (CFStringRef)CFArrayGetValueAtIndex(fileStream->rlInfo.rlArray, i+1); + CFRunLoopAddSource(rl, src, mode); + } + CFRelease(fileStream->rlInfo.rlArray); + CFRelease(src); + } + fileStream->rlInfo.sock = sock; +} +#endif + +static Boolean constructFD(_CFFileStreamContext *fileStream, CFStreamError *error, Boolean forRead, struct _CFStream *stream) { + UInt8 path[1024]; + int flags = forRead ? O_RDONLY : (O_CREAT | O_TRUNC | O_WRONLY); + + if (CFURLGetFileSystemRepresentation(fileStream->url, TRUE, path, 1024) == FALSE) { + error->error = ENOENT; + error->domain = kCFStreamErrorDomainPOSIX; + return FALSE; + } + if (__CFBitIsSet(fileStream->flags, APPEND)) { + flags |= O_APPEND; + if(_CFExecutableLinkedOnOrAfter(CFSystemVersionPanther)) flags &= ~O_TRUNC; + } + + do { + fileStream->fd = open((const char *)path, flags, 0666); + + if (fileStream->fd < 0) + break; + + if ((fileStream->offset != -1) && (lseek(fileStream->fd, fileStream->offset, SEEK_SET) == -1)) + break; + +#ifdef REAL_FILE_SCHEDULING + if (fileStream->rlInfo.rlArray != NULL) { + constructCFSocket(fileStream, forRead, stream); + } +#endif + + return TRUE; + } while (1); + + __CFBitSet(fileStream->flags, USE_RUNLOOP_ARRAY); + error->error = errno; + error->domain = kCFStreamErrorDomainPOSIX; + + return FALSE; +} + +static Boolean fileOpen(struct _CFStream *stream, CFStreamError *errorCode, Boolean *openComplete, void *info) { + _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; + Boolean forRead = (CFGetTypeID(stream) == CFReadStreamGetTypeID()); + *openComplete = TRUE; + if (ctxt->url) { + if (constructFD(ctxt, errorCode, forRead, stream)) { +#ifndef REAL_FILE_SCHEDULING + if (ctxt->scheduled > 0) { + if (forRead) + CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL); + else + CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL); + } +#endif + return TRUE; + } else { + return FALSE; + } +#ifdef REAL_FILE_SCHEDULING + } else if (ctxt->rlInfo.rlArray != NULL) { + constructCFSocket(ctxt, forRead, stream); +#endif + } + return TRUE; +} + +__private_extern__ CFIndex fdRead(int fd, UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, Boolean *atEOF) { + CFIndex bytesRead = read(fd, buffer, bufferLength); + if (bytesRead < 0) { + errorCode->error = errno; + errorCode->domain = kCFStreamErrorDomainPOSIX; + return -1; + } else { + *atEOF = (bytesRead == 0) ? TRUE : FALSE; + errorCode->error = 0; + return bytesRead; + } +} + +static CFIndex fileRead(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, Boolean *atEOF, void *info) { + _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; + CFIndex result; + result = fdRead(ctxt->fd, buffer, bufferLength, errorCode, atEOF); +#ifdef REAL_FILE_SCHEDULING + if (__CFBitIsSet(ctxt->flags, SCHEDULE_AFTER_READ)) { + __CFBitClear(ctxt->flags, SCHEDULE_AFTER_READ); + if (ctxt->rlInfo.sock) { + CFSocketEnableCallBacks(ctxt->rlInfo.sock, kCFSocketReadCallBack); + } + } +#else + if (*atEOF) + __CFBitSet(ctxt->flags, AT_EOF); + if (ctxt->scheduled > 0 && !*atEOF) { + CFReadStreamSignalEvent(stream, kCFStreamEventHasBytesAvailable, NULL); + } +#endif + return result; +} + +#ifdef REAL_FILE_SCHEDULING +__private_extern__ Boolean fdCanRead(int fd) { + struct timeval timeout = {0, 0}; + fd_set *readSetPtr; + fd_set readSet; + Boolean result; +// fd_set is not a mask in Win32, so checking for an fd that's too big is not relevant + if (fd < FD_SETSIZE) { + FD_ZERO(&readSet); + readSetPtr = &readSet; + } else { + int size = howmany(fd+1, NFDBITS) * sizeof(uint32_t); + uint32_t *fds_bits = (uint32_t *)malloc(size); + memset(fds_bits, 0, size); + readSetPtr = (fd_set *)fds_bits; + } + FD_SET(fd, readSetPtr); + result = (select(fd + 1, readSetPtr, NULL, NULL, &timeout) == 1) ? TRUE : FALSE; + if (readSetPtr != &readSet) { + free(readSetPtr); + } + return result; +} +#endif + +static Boolean fileCanRead(CFReadStreamRef stream, void *info) { + _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; +#ifdef REAL_FILE_SCHEDULING + return fdCanRead(ctxt->fd); +#else + return !__CFBitIsSet(ctxt->flags, AT_EOF); +#endif +} + +__private_extern__ CFIndex fdWrite(int fd, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode) { + CFIndex bytesWritten = write(fd, buffer, bufferLength); + if (bytesWritten < 0) { + errorCode->error = errno; + errorCode->domain = kCFStreamErrorDomainPOSIX; + return -1; + } else { + errorCode->error = 0; + return bytesWritten; + } +} + +static CFIndex fileWrite(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, void *info) { + _CFFileStreamContext *fileStream = ((_CFFileStreamContext *)info); + CFIndex result = fdWrite(fileStream->fd, buffer, bufferLength, errorCode); +#ifdef REAL_FILE_SCHEDULING + if (__CFBitIsSet(fileStream->flags, SCHEDULE_AFTER_WRITE)) { + __CFBitClear(fileStream->flags, SCHEDULE_AFTER_WRITE); + if (fileStream->rlInfo.sock) { + CFSocketEnableCallBacks(fileStream->rlInfo.sock, kCFSocketWriteCallBack); + } + } +#else + if (fileStream->scheduled > 0) { + CFWriteStreamSignalEvent(stream, kCFStreamEventCanAcceptBytes, NULL); + } +#endif + return result; +} + +#ifdef REAL_FILE_SCHEDULING +__private_extern__ Boolean fdCanWrite(int fd) { + struct timeval timeout = {0, 0}; + fd_set *writeSetPtr; + fd_set writeSet; + Boolean result; + if (fd < FD_SETSIZE) { + FD_ZERO(&writeSet); + writeSetPtr = &writeSet; + } else { + int size = howmany(fd+1, NFDBITS) * sizeof(uint32_t); + uint32_t *fds_bits = (uint32_t *)malloc(size); + memset(fds_bits, 0, size); + writeSetPtr = (fd_set *)fds_bits; + } + FD_SET(fd, writeSetPtr); + result = (select(fd + 1, NULL, writeSetPtr, NULL, &timeout) == 1) ? TRUE : FALSE; + if (writeSetPtr != &writeSet) { + free(writeSetPtr); + } + return result; +} +#endif + +static Boolean fileCanWrite(CFWriteStreamRef stream, void *info) { +#ifdef REAL_FILE_SCHEDULING + return fdCanWrite(((_CFFileStreamContext *)info)->fd); +#else + return TRUE; +#endif +} + +static void fileClose(struct _CFStream *stream, void *info) { + _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; + if (ctxt->fd >= 0) { + close(ctxt->fd); + ctxt->fd = -1; +#ifdef REAL_FILE_SCHEDULING + if (ctxt->rlInfo.sock) { + CFSocketInvalidate(ctxt->rlInfo.sock); + CFRelease(ctxt->rlInfo.sock); + ctxt->rlInfo.sock = NULL; + } + } else if (ctxt->rlInfo.rlArray) { + CFRelease(ctxt->rlInfo.rlArray); + ctxt->rlInfo.rlArray = NULL; +#endif + } +} + +#ifdef REAL_FILE_SCHEDULING +static void fileCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) { + struct _CFStream *stream = (struct _CFStream *)info; + Boolean isReadStream = (CFGetTypeID(stream) == CFReadStreamGetTypeID()); + _CFFileStreamContext *fileStream = (_CFFileStreamContext *)((isReadStream) ? CFReadStreamGetInfoPointer((CFReadStreamRef)stream) : CFWriteStreamGetInfoPointer((CFWriteStreamRef)stream)); + if (type == kCFSocketWriteCallBack) { + __CFBitSet(fileStream->flags, SCHEDULE_AFTER_WRITE); + CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL); + } else { + // type == kCFSocketReadCallBack + __CFBitSet(fileStream->flags, SCHEDULE_AFTER_READ); + CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL); + } +} +#endif + +static void fileSchedule(struct _CFStream *stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info) { + _CFFileStreamContext *fileStream = (_CFFileStreamContext *)info; + Boolean isReadStream = (CFGetTypeID(stream) == CFReadStreamGetTypeID()); + CFStreamStatus status = isReadStream ? CFReadStreamGetStatus((CFReadStreamRef)stream) : CFWriteStreamGetStatus((CFWriteStreamRef)stream); + if (fileStream->fd < 0 && status != kCFStreamStatusNotOpen) { + // Stream's already closed or error-ed out + return; + } +#ifdef REAL_FILE_SCHEDULING + if (status == kCFStreamStatusNotOpen) { + if (!fileStream->rlInfo.rlArray) { + fileStream->rlInfo.rlArray = CFArrayCreateMutable(CFGetAllocator(stream), 0, &kCFTypeArrayCallBacks); + } + CFArrayAppendValue(fileStream->rlInfo.rlArray, runLoop); + CFArrayAppendValue(fileStream->rlInfo.rlArray, runLoopMode); + } else { + CFRunLoopSourceRef rlSrc; + if (!fileStream->rlInfo.sock) { + constructCFSocket(fileStream, isReadStream, stream); + } + rlSrc = CFSocketCreateRunLoopSource(CFGetAllocator(stream), fileStream->rlInfo.sock, 0); + CFRunLoopAddSource(runLoop, rlSrc, runLoopMode); + CFRelease(rlSrc); + } +#else + fileStream->scheduled++; + if (fileStream->scheduled == 1 && fileStream->fd > 0 && status == kCFStreamStatusOpen) { + if (isReadStream) + CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL); + else + CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL); + } +#endif +} + +static void fileUnschedule(struct _CFStream *stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info) { + _CFFileStreamContext *fileStream = (_CFFileStreamContext *)info; +#ifdef REAL_FILE_SCHEDULING + Boolean isReadStream = (CFGetTypeID(stream) == CFReadStreamGetTypeID()); + CFStreamStatus status = isReadStream ? CFReadStreamGetStatus((CFReadStreamRef)stream) : CFWriteStreamGetStatus((CFWriteStreamRef)stream); + if (status == kCFStreamStatusNotOpen) { + // Not opened yet + if (fileStream->rlInfo.rlArray) { + CFMutableArrayRef runloops = fileStream->rlInfo.rlArray; + CFIndex i, c; + for (i = 0, c = CFArrayGetCount(runloops); i+1 < c; i += 2) { + if (CFEqual(CFArrayGetValueAtIndex(runloops, i), runLoop) && CFEqual(CFArrayGetValueAtIndex(runloops, i+1), runLoopMode)) { + CFArrayRemoveValueAtIndex(runloops, i); + CFArrayRemoveValueAtIndex(runloops, i); + break; + } + } + } + } else if (fileStream->rlInfo.sock) { + if (__CFBitIsSet(fileStream->flags, USE_RUNLOOP_ARRAY)) { + // we know that fileStream->rlInfo.rlArray is non-NULL because it is in a union with fileStream->rlInfo.sock + CFMutableArrayRef runloops = fileStream->rlInfo.rlArray; + CFIndex i, c; + for (i = 0, c = CFArrayGetCount(runloops); i+1 < c; i += 2) { + if (CFEqual(CFArrayGetValueAtIndex(runloops, i), runLoop) && CFEqual(CFArrayGetValueAtIndex(runloops, i+1), runLoopMode)) { + CFArrayRemoveValueAtIndex(runloops, i); + CFArrayRemoveValueAtIndex(runloops, i); + break; + } + } + } else { + CFRunLoopSourceRef sockSource = CFSocketCreateRunLoopSource(CFGetAllocator(stream), fileStream->rlInfo.sock, 0); + CFRunLoopRemoveSource(runLoop, sockSource, runLoopMode); + CFRelease(sockSource); + } + } +#else + if (fileStream->scheduled > 0) + fileStream->scheduled--; +#endif +} + +static CFTypeRef fileCopyProperty(struct _CFStream *stream, CFStringRef propertyName, void *info) { + + CFTypeRef result = NULL; + _CFFileStreamContext *fileStream = (_CFFileStreamContext *)info; + + if (CFEqual(propertyName, kCFStreamPropertyFileCurrentOffset)) { + + // NOTE that this does a lseek of 0 from the current location in + // order to populate the offset field which will then be used to + // create the resulting value. + if (!__CFBitIsSet(fileStream->flags, APPEND) && fileStream->fd != -1) { + fileStream->offset = lseek(fileStream->fd, 0, SEEK_CUR); + } + + if (fileStream->offset != -1) { + result = CFNumberCreate(CFGetAllocator((CFTypeRef)stream), kCFNumberSInt64Type, &(fileStream->offset)); + } + } + + return result; +} + +static Boolean fileSetProperty(struct _CFStream *stream, CFStringRef prop, CFTypeRef val, void *info) { + + Boolean result = FALSE; + _CFFileStreamContext *fileStream = (_CFFileStreamContext *)info; + + if (CFEqual(prop, kCFStreamPropertyAppendToFile) && CFGetTypeID(stream) == CFWriteStreamGetTypeID() && + CFWriteStreamGetStatus((CFWriteStreamRef)stream) == kCFStreamStatusNotOpen) + { + if (val == kCFBooleanTrue) { + __CFBitSet(fileStream->flags, APPEND); + fileStream->offset = -1; // Can't offset and append on the stream + } else { + __CFBitClear(fileStream->flags, APPEND); + } + result = TRUE; + } + + else if (CFEqual(prop, kCFStreamPropertyFileCurrentOffset)) { + + if (!__CFBitIsSet(fileStream->flags, APPEND)) + { + result = CFNumberGetValue((CFNumberRef)val, kCFNumberSInt64Type, &(fileStream->offset)); + } + + if ((fileStream->fd != -1) && (lseek(fileStream->fd, fileStream->offset, SEEK_SET) == -1)) { + result = FALSE; + } + } + + return result; +} + +static void *fileCreate(struct _CFStream *stream, void *info) { + _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; + _CFFileStreamContext *newCtxt = (_CFFileStreamContext *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFFileStreamContext), 0); + if (!newCtxt) return NULL; + newCtxt->url = ctxt->url; + if (newCtxt->url) { + CFRetain(newCtxt->url); + } + newCtxt->fd = ctxt->fd; +#ifdef REAL_FILE_SCHEDULING + newCtxt->rlInfo.sock = NULL; +#else + newCtxt->scheduled = 0; +#endif + newCtxt->flags = 0; + newCtxt->offset = -1; + return newCtxt; +} + +static void fileFinalize(struct _CFStream *stream, void *info) { + _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; + if (ctxt->fd > 0) { +#ifdef REAL_FILE_SCHEDULING + if (ctxt->rlInfo.sock) { + CFSocketInvalidate(ctxt->rlInfo.sock); + CFRelease(ctxt->rlInfo.sock); + } +#endif + close(ctxt->fd); +#ifdef REAL_FILE_SCHEDULING + } else if (ctxt->rlInfo.rlArray) { + CFRelease(ctxt->rlInfo.rlArray); +#endif + } + if (ctxt->url) { + CFRelease(ctxt->url); + } + CFAllocatorDeallocate(CFGetAllocator(stream), ctxt); +} + +static CFStringRef fileCopyDescription(struct _CFStream *stream, void *info) { + // This needs work + _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; + if (ctxt->url) { + return CFCopyDescription(ctxt->url); + } else { + return CFStringCreateWithFormat(CFGetAllocator(stream), NULL, CFSTR("fd = %d"), ctxt->fd); + } +} + +/* CFData stream callbacks */ +typedef struct { + CFDataRef data; // Mutable if the stream was constructed writable + const UInt8 *loc; // Current location in the file + Boolean scheduled; + char _padding[3]; +} _CFReadDataStreamContext; + +#define BUF_SIZE 1024 +typedef struct _CFStreamByteBuffer { + UInt8 *bytes; + CFIndex capacity, length; + struct _CFStreamByteBuffer *next; +} _CFStreamByteBuffer; + +typedef struct { + _CFStreamByteBuffer *firstBuf, *currentBuf; + CFAllocatorRef bufferAllocator; + Boolean scheduled; + char _padding[3]; +} _CFWriteDataStreamContext; + +static Boolean readDataOpen(struct _CFStream *stream, CFStreamError *errorCode, Boolean *openComplete, void *info) { + _CFReadDataStreamContext *dataStream = (_CFReadDataStreamContext *)info; + if (dataStream->scheduled) { + if (CFDataGetLength(dataStream->data) != 0) { + CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL); + } else { + CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventEndEncountered, NULL); + } + } + errorCode->error = 0; + *openComplete = TRUE; + return TRUE; +} + +static void readDataSchedule(struct _CFStream *stream, CFRunLoopRef rl, CFStringRef rlMode, void *info) { + _CFReadDataStreamContext *dataStream = (_CFReadDataStreamContext *)info; + if (dataStream->scheduled == FALSE) { + dataStream->scheduled = TRUE; + if (CFReadStreamGetStatus((CFReadStreamRef)stream) != kCFStreamStatusOpen) + return; + if (CFDataGetBytePtr(dataStream->data) + CFDataGetLength(dataStream->data) > dataStream->loc) { + CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL); + } else { + CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventEndEncountered, NULL); + } + } +} + +static CFIndex dataRead(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, Boolean *atEOF, void *info) { + _CFReadDataStreamContext *dataCtxt = (_CFReadDataStreamContext *)info; + const UInt8 *bytePtr = CFDataGetBytePtr(dataCtxt->data); + CFIndex length = CFDataGetLength(dataCtxt->data); + CFIndex bytesToCopy = bytePtr + length - dataCtxt->loc; + if (bytesToCopy > bufferLength) { + bytesToCopy = bufferLength; + } + if (bytesToCopy < 0) { + bytesToCopy = 0; + } + if (bytesToCopy != 0) { + memmove(buffer, dataCtxt->loc, bytesToCopy); + dataCtxt->loc += bytesToCopy; + } + error->error = 0; + *atEOF = (dataCtxt->loc < bytePtr + length) ? FALSE : TRUE; + if (dataCtxt->scheduled && !*atEOF) { + CFReadStreamSignalEvent(stream, kCFStreamEventHasBytesAvailable, NULL); + } + return bytesToCopy; +} + +static const UInt8 *dataGetBuffer(CFReadStreamRef stream, CFIndex maxBytesToRead, CFIndex *numBytesRead, CFStreamError *error, Boolean *atEOF, void *info) { + _CFReadDataStreamContext *dataCtxt = (_CFReadDataStreamContext *)info; + const UInt8 *bytes = CFDataGetBytePtr(dataCtxt->data); + if (dataCtxt->loc - bytes > maxBytesToRead) { + *numBytesRead = maxBytesToRead; + *atEOF = FALSE; + } else { + *numBytesRead = dataCtxt->loc - bytes; + *atEOF = TRUE; + } + error->error = 0; + bytes = dataCtxt->loc; + dataCtxt->loc += *numBytesRead; + if (dataCtxt->scheduled && !*atEOF) { + CFReadStreamSignalEvent(stream, kCFStreamEventHasBytesAvailable, NULL); + } + return bytes; +} + +static Boolean dataCanRead(CFReadStreamRef stream, void *info) { + _CFReadDataStreamContext *dataCtxt = (_CFReadDataStreamContext *)info; + return (CFDataGetBytePtr(dataCtxt->data) + CFDataGetLength(dataCtxt->data) > dataCtxt->loc) ? TRUE : FALSE; +} + +static Boolean writeDataOpen(struct _CFStream *stream, CFStreamError *errorCode, Boolean *openComplete, void *info) { + _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info; + if (dataStream->scheduled) { + if (dataStream->bufferAllocator != kCFAllocatorNull || dataStream->currentBuf->capacity > dataStream->currentBuf->length) { + CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL); + } else { + CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventEndEncountered, NULL); + } + } + errorCode->error = 0; + *openComplete = TRUE; + return TRUE; +} + +static void writeDataSchedule(struct _CFStream *stream, CFRunLoopRef rl, CFStringRef rlMode, void *info) { + _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info; + if (dataStream->scheduled == FALSE) { + dataStream->scheduled = TRUE; + if (CFWriteStreamGetStatus((CFWriteStreamRef)stream) != kCFStreamStatusOpen) + return; + if (dataStream->bufferAllocator != kCFAllocatorNull || dataStream->currentBuf->capacity > dataStream->currentBuf->length) { + CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL); + } else { + CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventEndEncountered, NULL); + } + } +} + +static CFIndex dataWrite(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, void *info) { + _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info; + CFIndex result; + CFIndex freeSpace = dataStream->currentBuf->capacity - dataStream->currentBuf->length; + if (dataStream->bufferAllocator == kCFAllocatorNull && bufferLength > freeSpace) { + errorCode->error = ENOMEM; + errorCode->domain = kCFStreamErrorDomainPOSIX; + return -1; + } else { + result = bufferLength; + while (bufferLength > 0) { + CFIndex amountToCopy = (bufferLength > freeSpace) ? freeSpace : bufferLength; + if (freeSpace > 0) { + memmove(dataStream->currentBuf->bytes + dataStream->currentBuf->length, buffer, amountToCopy); + buffer += amountToCopy; + bufferLength -= amountToCopy; + dataStream->currentBuf->length += amountToCopy; + } + if (bufferLength > 0) { + CFIndex bufSize = BUF_SIZE > bufferLength ? BUF_SIZE : bufferLength; + _CFStreamByteBuffer *newBuf = (_CFStreamByteBuffer *)CFAllocatorAllocate(dataStream->bufferAllocator, sizeof(_CFStreamByteBuffer) + bufSize, 0); + newBuf->bytes = (UInt8 *)(newBuf + 1); + newBuf->capacity = bufSize; + newBuf->length = 0; + newBuf->next = NULL; + dataStream->currentBuf->next = newBuf; + dataStream->currentBuf = newBuf; + freeSpace = bufSize; + } + } + errorCode->error = 0; + } + if (dataStream->scheduled && (dataStream->bufferAllocator != kCFAllocatorNull || dataStream->currentBuf->capacity > dataStream->currentBuf->length)) { + CFWriteStreamSignalEvent(stream, kCFStreamEventCanAcceptBytes, NULL); + } + return result; +} + +static Boolean dataCanWrite(CFWriteStreamRef stream, void *info) { + _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info; + if (dataStream->bufferAllocator != kCFAllocatorNull) return TRUE; + if (dataStream->currentBuf->capacity > dataStream->currentBuf->length) return TRUE; + return FALSE; +} + +static CFPropertyListRef dataCopyProperty(struct _CFStream *stream, CFStringRef propertyName, void *info) { + _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info; + CFIndex size = 0; + _CFStreamByteBuffer *buf; + CFAllocatorRef alloc; + UInt8 *bytes, *currByte; + if (!CFEqual(propertyName, kCFStreamPropertyDataWritten)) return NULL; + if (dataStream->bufferAllocator == kCFAllocatorNull) return NULL; + alloc = dataStream->bufferAllocator; + for (buf = dataStream->firstBuf; buf != NULL; buf = buf->next) { + size += buf->length; + } + if (size == 0) return NULL; + bytes = (UInt8 *)CFAllocatorAllocate(alloc, size, 0); + currByte = bytes; + for (buf = dataStream->firstBuf; buf != NULL; buf = buf->next) { + memmove(currByte, buf->bytes, buf->length); + currByte += buf->length; + } + return CFDataCreateWithBytesNoCopy(alloc, bytes, size, alloc); +} + +static void *readDataCreate(struct _CFStream *stream, void *info) { + _CFReadDataStreamContext *ctxt = (_CFReadDataStreamContext *)info; + _CFReadDataStreamContext *newCtxt = (_CFReadDataStreamContext *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFReadDataStreamContext), 0); + if (!newCtxt) return NULL; + newCtxt->data = (CFDataRef)CFRetain(ctxt->data); + newCtxt->loc = CFDataGetBytePtr(newCtxt->data); + newCtxt->scheduled = FALSE; + return (void *)newCtxt; +} + +static void readDataFinalize(struct _CFStream *stream, void *info) { + _CFReadDataStreamContext *ctxt = (_CFReadDataStreamContext *)info; + CFRelease(ctxt->data); + CFAllocatorDeallocate(CFGetAllocator(stream), ctxt); +} + +static CFStringRef readDataCopyDescription(struct _CFStream *stream, void *info) { + return CFCopyDescription(((_CFReadDataStreamContext *)info)->data); +} + +static void *writeDataCreate(struct _CFStream *stream, void *info) { + _CFWriteDataStreamContext *ctxt = (_CFWriteDataStreamContext *)info; + _CFWriteDataStreamContext *newCtxt; + if (ctxt->bufferAllocator != kCFAllocatorNull) { + if (ctxt->bufferAllocator == NULL) ctxt->bufferAllocator = CFAllocatorGetDefault(); + CFRetain(ctxt->bufferAllocator); + newCtxt = (_CFWriteDataStreamContext *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFWriteDataStreamContext) + sizeof(_CFStreamByteBuffer) + BUF_SIZE, 0); + newCtxt->firstBuf = (_CFStreamByteBuffer *)(newCtxt + 1); + newCtxt->firstBuf->bytes = (UInt8 *)(newCtxt->firstBuf + 1); + newCtxt->firstBuf->capacity = BUF_SIZE; + newCtxt->firstBuf->length = 0; + newCtxt->firstBuf->next = NULL; + newCtxt->currentBuf = newCtxt->firstBuf; + newCtxt->bufferAllocator = ctxt->bufferAllocator; + newCtxt->scheduled = FALSE; + } else { + newCtxt = (_CFWriteDataStreamContext *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFWriteDataStreamContext) + sizeof(_CFStreamByteBuffer), 0); + newCtxt->firstBuf = (_CFStreamByteBuffer *)(newCtxt+1); + newCtxt->firstBuf->bytes = ctxt->firstBuf->bytes; + newCtxt->firstBuf->capacity = ctxt->firstBuf->capacity; + newCtxt->firstBuf->length = 0; + newCtxt->firstBuf->next = NULL; + newCtxt->currentBuf = newCtxt->firstBuf; + newCtxt->bufferAllocator = kCFAllocatorNull; + newCtxt->scheduled = FALSE; + } + return (void *)newCtxt; +} + +static void writeDataFinalize(struct _CFStream *stream, void *info) { + _CFWriteDataStreamContext *ctxt = (_CFWriteDataStreamContext *)info; + if (ctxt->bufferAllocator != kCFAllocatorNull) { + _CFStreamByteBuffer *buf = ctxt->firstBuf->next, *next; + while (buf != NULL) { + next = buf->next; + CFAllocatorDeallocate(ctxt->bufferAllocator, buf); + buf = next; + } + CFRelease(ctxt->bufferAllocator); + } + CFAllocatorDeallocate(CFGetAllocator(stream), ctxt); +} + +static CFStringRef writeDataCopyDescription(struct _CFStream *stream, void *info) { + return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFWriteDataContext %p>"), info); +} + +static const struct _CFStreamCallBacksV1 fileCallBacks = {1, fileCreate, fileFinalize, fileCopyDescription, fileOpen, NULL, fileRead, NULL, fileCanRead, fileWrite, fileCanWrite, fileClose, fileCopyProperty, fileSetProperty, NULL, fileSchedule, fileUnschedule}; + +static struct _CFStream *_CFStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL, Boolean forReading) { + _CFFileStreamContext fileContext; + CFStringRef scheme = fileURL ? CFURLCopyScheme(fileURL) : NULL; + if (!scheme || !CFEqual(scheme, CFSTR("file"))) { + if (scheme) CFRelease(scheme); + return NULL; + } + CFRelease(scheme); + fileContext.url = fileURL; + fileContext.fd = -1; + return _CFStreamCreateWithConstantCallbacks(alloc, &fileContext, (struct _CFStreamCallBacks *)(&fileCallBacks), forReading); +} + +CF_EXPORT CFReadStreamRef CFReadStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL) { + return (CFReadStreamRef)_CFStreamCreateWithFile(alloc, fileURL, TRUE); +} + +CF_EXPORT CFWriteStreamRef CFWriteStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL) { + return (CFWriteStreamRef)_CFStreamCreateWithFile(alloc, fileURL, FALSE); +} + +CFReadStreamRef _CFReadStreamCreateFromFileDescriptor(CFAllocatorRef alloc, int fd) { + _CFFileStreamContext fileContext; + fileContext.url = NULL; + fileContext.fd = fd; + return (CFReadStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &fileContext, (struct _CFStreamCallBacks *)(&fileCallBacks), TRUE); +} + +CFWriteStreamRef _CFWriteStreamCreateFromFileDescriptor(CFAllocatorRef alloc, int fd) { + _CFFileStreamContext fileContext; + fileContext.url = NULL; + fileContext.fd = fd; + return (CFWriteStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &fileContext, (struct _CFStreamCallBacks *)(&fileCallBacks), FALSE); +} + + + +static const struct _CFStreamCallBacksV1 readDataCallBacks = {1, readDataCreate, readDataFinalize, readDataCopyDescription, readDataOpen, NULL, dataRead, dataGetBuffer, dataCanRead, NULL, NULL, NULL, NULL, NULL, NULL, readDataSchedule, NULL}; +static const struct _CFStreamCallBacksV1 writeDataCallBacks = {1, writeDataCreate, writeDataFinalize, writeDataCopyDescription, writeDataOpen, NULL, NULL, NULL, NULL, dataWrite, dataCanWrite, NULL, dataCopyProperty, NULL, NULL, writeDataSchedule, NULL}; + +CF_EXPORT CFReadStreamRef CFReadStreamCreateWithBytesNoCopy(CFAllocatorRef alloc, const UInt8 *bytes, CFIndex length, CFAllocatorRef bytesDeallocator) { + _CFReadDataStreamContext ctxt; + CFReadStreamRef result; + ctxt.data = CFDataCreateWithBytesNoCopy(alloc, bytes, length, bytesDeallocator); + result = (CFReadStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, (struct _CFStreamCallBacks *)(&readDataCallBacks), TRUE); + CFRelease(ctxt.data); + return result; +} + +/* This needs to be exported to make it callable from Foundation. */ +CF_EXPORT CFReadStreamRef CFReadStreamCreateWithData(CFAllocatorRef alloc, CFDataRef data) { + _CFReadDataStreamContext ctxt; + CFReadStreamRef result = NULL; + + ctxt.data = (CFDataRef)CFRetain(data); + result = (CFReadStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, (struct _CFStreamCallBacks *)(&readDataCallBacks), TRUE); + CFRelease(data); + return result; +} + +CFWriteStreamRef CFWriteStreamCreateWithBuffer(CFAllocatorRef alloc, UInt8 *buffer, CFIndex bufferCapacity) { + _CFStreamByteBuffer buf; + _CFWriteDataStreamContext ctxt; + buf.bytes = buffer; + buf.capacity = bufferCapacity; + buf.length = 0; + buf.next = NULL; + ctxt.firstBuf = &buf; + ctxt.currentBuf = ctxt.firstBuf; + ctxt.bufferAllocator = kCFAllocatorNull; + return (CFWriteStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, (struct _CFStreamCallBacks *)(&writeDataCallBacks), FALSE); +} + +CF_EXPORT CFWriteStreamRef CFWriteStreamCreateWithAllocatedBuffers(CFAllocatorRef alloc, CFAllocatorRef bufferAllocator) { + _CFWriteDataStreamContext ctxt; + ctxt.firstBuf = NULL; + ctxt.currentBuf = NULL; + ctxt.bufferAllocator = bufferAllocator; + return (CFWriteStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, (struct _CFStreamCallBacks *)(&writeDataCallBacks), FALSE); +} + +#undef BUF_SIZE +
diff --git a/CoreFoundation/CFData.c b/CoreFoundation/CFData.c new file mode 100644 index 0000000..d74655f --- /dev/null +++ b/CoreFoundation/CFData.c
@@ -0,0 +1,426 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFData.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include <CoreFoundation/CFData.h> +#include "CFPriv.h" +#include "CFInternal.h" +#include <string.h> + +struct __CFData { + CFRuntimeBase _base; + CFIndex _length; /* number of bytes */ + CFIndex _capacity; /* maximum number of bytes */ + CFAllocatorRef _bytesDeallocator; /* used only for immutable; if NULL, no deallocation */ + uint8_t *_bytes; +}; + +/* Bits 3-2 are used for mutability variation */ + +CF_INLINE UInt32 __CFMutableVariety(const void *cf) { + return __CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 3, 2); +} + +CF_INLINE void __CFSetMutableVariety(void *cf, UInt32 v) { + __CFBitfieldSetValue(((CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 3, 2, v); +} + +CF_INLINE UInt32 __CFMutableVarietyFromFlags(UInt32 flags) { + return __CFBitfieldGetValue(flags, 1, 0); +} + +#define __CFGenericValidateMutabilityFlags(flags) \ + CFAssert2(__CFMutableVarietyFromFlags(flags) != 0x2, __kCFLogAssertion, "%s(): flags 0x%x do not correctly specify the mutable variety", __PRETTY_FUNCTION__, flags); + +CF_INLINE CFIndex __CFDataLength(CFDataRef data) { + return data->_length; +} + +CF_INLINE void __CFDataSetLength(CFMutableDataRef data, CFIndex v) { + /* for a CFData, _bytesUsed == _length */ +} + +CF_INLINE CFIndex __CFDataCapacity(CFDataRef data) { + return data->_capacity; +} + +CF_INLINE void __CFDataSetCapacity(CFMutableDataRef data, CFIndex v) { + /* for a CFData, _bytesNum == _capacity */ +} + +CF_INLINE CFIndex __CFDataNumBytesUsed(CFDataRef data) { + return data->_length; +} + +CF_INLINE void __CFDataSetNumBytesUsed(CFMutableDataRef data, CFIndex v) { + data->_length = v; +} + +CF_INLINE CFIndex __CFDataNumBytes(CFDataRef data) { + return data->_capacity; +} + +CF_INLINE void __CFDataSetNumBytes(CFMutableDataRef data, CFIndex v) { + data->_capacity = v; +} + +CF_INLINE CFIndex __CFDataRoundUpCapacity(CFIndex capacity) { + if (capacity < 16) return 16; +// CF: quite probably, this doubling should slow as the data gets larger and larger; should not use strict doubling + return (1 << flsl(capacity)); +} + +CF_INLINE CFIndex __CFDataNumBytesForCapacity(CFIndex capacity) { + return capacity; +} + +static void __CFDataHandleOutOfMemory(CFTypeRef obj, CFIndex numBytes) { + CFStringRef msg = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("Attempt to allocate %ld bytes for NS/CFData failed"), numBytes); + CFBadErrorCallBack cb = _CFGetOutOfMemoryErrorCallBack(); + if (NULL == cb || !cb(obj, CFSTR("NS/CFData"), msg)) { + CFLog(kCFLogLevelCritical, CFSTR("%@"), msg); + HALT; + } + CFRelease(msg); +} + +#if defined(DEBUG) +CF_INLINE void __CFDataValidateRange(CFDataRef data, CFRange range, const char *func) { + CFAssert2(0 <= range.location && range.location <= __CFDataLength(data), __kCFLogAssertion, "%s(): range.location index (%d) out of bounds", func, range.location); + CFAssert2(0 <= range.length, __kCFLogAssertion, "%s(): length (%d) cannot be less than zero", func, range.length); + CFAssert2(range.location + range.length <= __CFDataLength(data), __kCFLogAssertion, "%s(): ending index (%d) out of bounds", func, range.location + range.length); +} +#else +#define __CFDataValidateRange(a,r,f) +#endif + +static Boolean __CFDataEqual(CFTypeRef cf1, CFTypeRef cf2) { + CFDataRef data1 = (CFDataRef)cf1; + CFDataRef data2 = (CFDataRef)cf2; + CFIndex length; + length = __CFDataLength(data1); + if (length != __CFDataLength(data2)) return false; + return 0 == memcmp(data1->_bytes, data2->_bytes, length); +} + +static CFHashCode __CFDataHash(CFTypeRef cf) { + CFDataRef data = (CFDataRef)cf; + return CFHashBytes(data->_bytes, __CFMin(__CFDataLength(data), 80)); +} + +static CFStringRef __CFDataCopyDescription(CFTypeRef cf) { + CFDataRef data = (CFDataRef)cf; + CFMutableStringRef result; + CFIndex idx; + CFIndex len; + const uint8_t *bytes; + len = __CFDataLength(data); + bytes = data->_bytes; + result = CFStringCreateMutable(CFGetAllocator(data), 0); + CFStringAppendFormat(result, NULL, CFSTR("<CFData %p [%p]>{length = %u, capacity = %u, bytes = 0x"), cf, CFGetAllocator(data), len, __CFDataCapacity(data)); + if (24 < len) { + for (idx = 0; idx < 16; idx += 4) { + CFStringAppendFormat(result, NULL, CFSTR("%02x%02x%02x%02x"), bytes[idx], bytes[idx + 1], bytes[idx + 2], bytes[idx + 3]); + } + CFStringAppend(result, CFSTR(" ... ")); + for (idx = len - 8; idx < len; idx += 4) { + CFStringAppendFormat(result, NULL, CFSTR("%02x%02x%02x%02x"), bytes[idx], bytes[idx + 1], bytes[idx + 2], bytes[idx + 3]); + } + } else { + for (idx = 0; idx < len; idx++) { + CFStringAppendFormat(result, NULL, CFSTR("%02x"), bytes[idx]); + } + } + CFStringAppend(result, CFSTR("}")); + return result; +} + +enum { + kCFImmutable = 0x0, /* unchangable and fixed capacity; default */ + kCFMutable = 0x1, /* changeable and variable capacity */ + kCFFixedMutable = 0x3 /* changeable and fixed capacity */ +}; + +static void __CFDataDeallocate(CFTypeRef cf) { + CFMutableDataRef data = (CFMutableDataRef)cf; + CFAllocatorRef allocator = __CFGetAllocator(data); + switch (__CFMutableVariety(data)) { + case kCFMutable: + _CFAllocatorDeallocateGC(allocator, data->_bytes); + data->_bytes = NULL; + break; + case kCFFixedMutable: + break; + case kCFImmutable: + if (NULL != data->_bytesDeallocator) { + if (CF_IS_COLLECTABLE_ALLOCATOR(data->_bytesDeallocator)) { + // GC: for finalization safety, let collector reclaim the buffer in the next GC cycle. + auto_zone_release(__CFCollectableZone, data->_bytes); + } else { + CFAllocatorDeallocate(data->_bytesDeallocator, data->_bytes); + CFRelease(data->_bytesDeallocator); + data->_bytes = NULL; + } + } + break; + } +} + +static CFTypeID __kCFDataTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFDataClass = { + 0, + "CFData", + NULL, // init + NULL, // copy + __CFDataDeallocate, + __CFDataEqual, + __CFDataHash, + NULL, // + __CFDataCopyDescription +}; + +__private_extern__ void __CFDataInitialize(void) { + __kCFDataTypeID = _CFRuntimeRegisterClass(&__CFDataClass); +} + +CFTypeID CFDataGetTypeID(void) { + return __kCFDataTypeID; +} + +// NULL bytesDeallocator to this function does not mean the default allocator, it means +// that there should be no deallocator, and the bytes should be copied. +static CFMutableDataRef __CFDataInit(CFAllocatorRef allocator, CFOptionFlags flags, CFIndex capacity, const uint8_t *bytes, CFIndex length, CFAllocatorRef bytesDeallocator) { + CFMutableDataRef memory; + CFIndex size; + __CFGenericValidateMutabilityFlags(flags); + CFAssert2(0 <= capacity, __kCFLogAssertion, "%s(): capacity (%d) cannot be less than zero", __PRETTY_FUNCTION__, capacity); + CFAssert3(kCFFixedMutable != __CFMutableVarietyFromFlags(flags) || length <= capacity, __kCFLogAssertion, "%s(): for kCFFixedMutable type, capacity (%d) must be greater than or equal to number of initial elements (%d)", __PRETTY_FUNCTION__, capacity, length); + CFAssert2(0 <= length, __kCFLogAssertion, "%s(): length (%d) cannot be less than zero", __PRETTY_FUNCTION__, length); + size = sizeof(struct __CFData) - sizeof(CFRuntimeBase); + if (__CFMutableVarietyFromFlags(flags) != kCFMutable && (bytesDeallocator == NULL)) { + size += sizeof(uint8_t) * __CFDataNumBytesForCapacity(capacity); + } + if (__CFMutableVarietyFromFlags(flags) != kCFMutable) { + size += sizeof(uint8_t) * 15; // for 16-byte alignment fixup + } + memory = (CFMutableDataRef)_CFRuntimeCreateInstance(allocator, __kCFDataTypeID, size, NULL); + if (NULL == memory) { + return NULL; + } + __CFDataSetNumBytesUsed(memory, 0); + __CFDataSetLength(memory, 0); + switch (__CFMutableVarietyFromFlags(flags)) { + case kCFMutable: + __CFDataSetCapacity(memory, __CFDataRoundUpCapacity(1)); + __CFDataSetNumBytes(memory, __CFDataNumBytesForCapacity(__CFDataRoundUpCapacity(1))); + // GC: if allocated in the collectable zone, mark the object as needing to be scanned. + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) auto_zone_set_layout_type(__CFCollectableZone, memory, AUTO_MEMORY_SCANNED); + // assume that allocators give 16-byte aligned memory back -- it is their responsibility + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, memory, memory->_bytes, _CFAllocatorAllocateGC(allocator, __CFDataNumBytes(memory) * sizeof(uint8_t), 0)); + if (__CFOASafe) __CFSetLastAllocationEventName(memory->_bytes, "CFData (store)"); + if (NULL == memory->_bytes) { + CFRelease(memory); + return NULL; + } + memory->_bytesDeallocator = NULL; + __CFSetMutableVariety(memory, kCFMutable); + CFDataReplaceBytes(memory, CFRangeMake(0, 0), bytes, length); + break; + case kCFFixedMutable: + /* Don't round up capacity */ + __CFDataSetCapacity(memory, capacity); + __CFDataSetNumBytes(memory, __CFDataNumBytesForCapacity(capacity)); + memory->_bytes = (uint8_t *)((uintptr_t)((int8_t *)memory + sizeof(struct __CFData) + 15) & ~0xF); // 16-byte align + memory->_bytesDeallocator = NULL; + __CFSetMutableVariety(memory, kCFFixedMutable); + CFDataReplaceBytes(memory, CFRangeMake(0, 0), bytes, length); + break; + case kCFImmutable: + /* Don't round up capacity */ + __CFDataSetCapacity(memory, capacity); + __CFDataSetNumBytes(memory, __CFDataNumBytesForCapacity(capacity)); + if (bytesDeallocator != NULL) { + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, memory, memory->_bytes, (uint8_t *)bytes); + memory->_bytesDeallocator = (CFAllocatorRef)CFRetain(bytesDeallocator); + __CFDataSetNumBytesUsed(memory, length); + __CFDataSetLength(memory, length); + } else { + memory->_bytes = (uint8_t *)((uintptr_t)((int8_t *)memory + sizeof(struct __CFData) + 15) & ~0xF); // 16-byte align + memory->_bytesDeallocator = NULL; + __CFSetMutableVariety(memory, kCFFixedMutable); + CFDataReplaceBytes(memory, CFRangeMake(0, 0), bytes, length); + } + break; + } + __CFSetMutableVariety(memory, __CFMutableVarietyFromFlags(flags)); + return memory; +} + +CFDataRef CFDataCreate(CFAllocatorRef allocator, const uint8_t *bytes, CFIndex length) { + return __CFDataInit(allocator, kCFImmutable, length, bytes, length, NULL); +} + +CFDataRef CFDataCreateWithBytesNoCopy(CFAllocatorRef allocator, const uint8_t *bytes, CFIndex length, CFAllocatorRef bytesDeallocator) { + CFAssert1((0 == length || bytes != NULL), __kCFLogAssertion, "%s(): bytes pointer cannot be NULL if length is non-zero", __PRETTY_FUNCTION__); + if (NULL == bytesDeallocator) bytesDeallocator = __CFGetDefaultAllocator(); + return __CFDataInit(allocator, kCFImmutable, length, bytes, length, bytesDeallocator); +} + +CFDataRef CFDataCreateCopy(CFAllocatorRef allocator, CFDataRef data) { + CFIndex length = CFDataGetLength(data); + return __CFDataInit(allocator, kCFImmutable, length, CFDataGetBytePtr(data), length, NULL); +} + +CFMutableDataRef CFDataCreateMutable(CFAllocatorRef allocator, CFIndex capacity) { + return __CFDataInit(allocator, (0 == capacity) ? kCFMutable : kCFFixedMutable, capacity, NULL, 0, NULL); +} + +CFMutableDataRef CFDataCreateMutableCopy(CFAllocatorRef allocator, CFIndex capacity, CFDataRef data) { + return __CFDataInit(allocator, (0 == capacity) ? kCFMutable : kCFFixedMutable, capacity, CFDataGetBytePtr(data), CFDataGetLength(data), NULL); +} + +CFIndex CFDataGetLength(CFDataRef data) { + CF_OBJC_FUNCDISPATCH0(__kCFDataTypeID, CFIndex, data, "length"); + __CFGenericValidateType(data, __kCFDataTypeID); + return __CFDataLength(data); +} + +const uint8_t *CFDataGetBytePtr(CFDataRef data) { + CF_OBJC_FUNCDISPATCH0(__kCFDataTypeID, const uint8_t *, data, "bytes"); + __CFGenericValidateType(data, __kCFDataTypeID); + return data->_bytes; +} + +uint8_t *CFDataGetMutableBytePtr(CFMutableDataRef data) { + CF_OBJC_FUNCDISPATCH0(__kCFDataTypeID, uint8_t *, data, "mutableBytes"); + CFAssert1(__CFMutableVariety(data) == kCFMutable || __CFMutableVariety(data) == kCFFixedMutable, __kCFLogAssertion, "%s(): data is immutable", __PRETTY_FUNCTION__); + return data->_bytes; +} + +void CFDataGetBytes(CFDataRef data, CFRange range, uint8_t *buffer) { + CF_OBJC_FUNCDISPATCH2(__kCFDataTypeID, void, data, "getBytes:range:", buffer, range); + memmove(buffer, data->_bytes + range.location, range.length); +} + +static void __CFDataGrow(CFMutableDataRef data, CFIndex numNewValues) { + CFIndex oldLength = __CFDataLength(data); + CFIndex capacity = __CFDataRoundUpCapacity(oldLength + numNewValues); + CFAllocatorRef allocator = CFGetAllocator(data); + __CFDataSetCapacity(data, capacity); + __CFDataSetNumBytes(data, __CFDataNumBytesForCapacity(capacity)); + void *bytes = _CFAllocatorReallocateGC(allocator, data->_bytes, __CFDataNumBytes(data) * sizeof(uint8_t), 0); + if (NULL == bytes) __CFDataHandleOutOfMemory(data, __CFDataNumBytes(data) * sizeof(uint8_t)); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, data, data->_bytes, bytes); + if (__CFOASafe) __CFSetLastAllocationEventName(data->_bytes, "CFData (store)"); +} + +void CFDataSetLength(CFMutableDataRef data, CFIndex length) { + CFIndex len; + CF_OBJC_FUNCDISPATCH1(__kCFDataTypeID, void, data, "setLength:", length); + CFAssert1(__CFMutableVariety(data) == kCFMutable || __CFMutableVariety(data) == kCFFixedMutable, __kCFLogAssertion, "%s(): data is immutable", __PRETTY_FUNCTION__); + len = __CFDataLength(data); + switch (__CFMutableVariety(data)) { + case kCFMutable: + if (len < length) { +// CF: should only grow when new length exceeds current capacity, not whenever it exceeds the current length + __CFDataGrow(data, length - len); + } + break; + case kCFFixedMutable: + CFAssert1(length <= __CFDataCapacity(data), __kCFLogAssertion, "%s(): fixed-capacity data is full", __PRETTY_FUNCTION__); + break; + } + if (len < length) { + memset(data->_bytes + len, 0, length - len); + } + __CFDataSetLength(data, length); + __CFDataSetNumBytesUsed(data, length); +} + +void CFDataIncreaseLength(CFMutableDataRef data, CFIndex extraLength) { + CF_OBJC_FUNCDISPATCH1(__kCFDataTypeID, void, data, "increaseLengthBy:", extraLength); + CFAssert1(__CFMutableVariety(data) == kCFMutable || __CFMutableVariety(data) == kCFFixedMutable, __kCFLogAssertion, "%s(): data is immutable", __PRETTY_FUNCTION__); + CFDataSetLength(data, __CFDataLength(data) + extraLength); +} + +void CFDataAppendBytes(CFMutableDataRef data, const uint8_t *bytes, CFIndex length) { + CF_OBJC_FUNCDISPATCH2(__kCFDataTypeID, void, data, "appendBytes:length:", bytes, length); + CFAssert1(__CFMutableVariety(data) == kCFMutable || __CFMutableVariety(data) == kCFFixedMutable, __kCFLogAssertion, "%s(): data is immutable", __PRETTY_FUNCTION__); + CFDataReplaceBytes(data, CFRangeMake(__CFDataLength(data), 0), bytes, length); +} + +void CFDataDeleteBytes(CFMutableDataRef data, CFRange range) { + CF_OBJC_FUNCDISPATCH3(__kCFDataTypeID, void, data, "replaceBytesInRange:withBytes:length:", range, NULL, 0); + CFAssert1(__CFMutableVariety(data) == kCFMutable || __CFMutableVariety(data) == kCFFixedMutable, __kCFLogAssertion, "%s(): data is immutable", __PRETTY_FUNCTION__); + CFDataReplaceBytes(data, range, NULL, 0); +} + +void CFDataReplaceBytes(CFMutableDataRef data, CFRange range, const uint8_t *newBytes, CFIndex newLength) { + CF_OBJC_FUNCDISPATCH3(__kCFDataTypeID, void, data, "replaceBytesInRange:withBytes:length:", range, newBytes, newLength); + __CFGenericValidateType(data, __kCFDataTypeID); + __CFDataValidateRange(data, range, __PRETTY_FUNCTION__); + CFAssert1(__CFMutableVariety(data) == kCFMutable || __CFMutableVariety(data) == kCFFixedMutable, __kCFLogAssertion, "%s(): data is immutable", __PRETTY_FUNCTION__); + CFAssert2(0 <= newLength, __kCFLogAssertion, "%s(): newLength (%d) cannot be less than zero", __PRETTY_FUNCTION__, newLength); + + CFIndex len = __CFDataLength(data); + if (len < 0 || range.length < 0 || newLength < 0) HALT; + CFIndex newCount = len - range.length + newLength; + if (newCount < 0) HALT; + + switch (__CFMutableVariety(data)) { + case kCFMutable: + if (__CFDataNumBytes(data) < newCount) { + __CFDataGrow(data, newLength - range.length); + } + break; + case kCFFixedMutable: + CFAssert1(newCount <= __CFDataCapacity(data), __kCFLogAssertion, "%s(): fixed-capacity data is full", __PRETTY_FUNCTION__); + break; + } + if (newLength != range.length && range.location + range.length < len) { + memmove(data->_bytes + range.location + newLength, data->_bytes + range.location + range.length, (len - range.location - range.length) * sizeof(uint8_t)); + } + if (0 < newLength) { + memmove(data->_bytes + range.location, newBytes, newLength * sizeof(uint8_t)); + } + __CFDataSetNumBytesUsed(data, newCount); + __CFDataSetLength(data, newCount); +} + +#undef __CFDataValidateRange +#undef __CFGenericValidateMutabilityFlags +
diff --git a/CoreFoundation/CFData.h b/CoreFoundation/CFData.h new file mode 100644 index 0000000..379af56 --- /dev/null +++ b/CoreFoundation/CFData.h
@@ -0,0 +1,96 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFData.h + Copyright (c) 1998-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFDATA__) +#define __COREFOUNDATION_CFDATA__ 1 + +#include <CoreFoundation/CFBase.h> + +CF_EXTERN_C_BEGIN + +typedef const struct __CFData * CFDataRef; +typedef struct __CFData * CFMutableDataRef; + +CF_EXPORT +CFTypeID CFDataGetTypeID(void); + +CF_EXPORT +CFDataRef CFDataCreate(CFAllocatorRef allocator, const UInt8 *bytes, CFIndex length); + +CF_EXPORT +CFDataRef CFDataCreateWithBytesNoCopy(CFAllocatorRef allocator, const UInt8 *bytes, CFIndex length, CFAllocatorRef bytesDeallocator); + /* Pass kCFAllocatorNull as bytesDeallocator to assure the bytes aren't freed */ + +CF_EXPORT +CFDataRef CFDataCreateCopy(CFAllocatorRef allocator, CFDataRef theData); + +CF_EXPORT +CFMutableDataRef CFDataCreateMutable(CFAllocatorRef allocator, CFIndex capacity); + +CF_EXPORT +CFMutableDataRef CFDataCreateMutableCopy(CFAllocatorRef allocator, CFIndex capacity, CFDataRef theData); + +CF_EXPORT +CFIndex CFDataGetLength(CFDataRef theData); + +CF_EXPORT +const UInt8 *CFDataGetBytePtr(CFDataRef theData); + +CF_EXPORT +UInt8 *CFDataGetMutableBytePtr(CFMutableDataRef theData); + +CF_EXPORT +void CFDataGetBytes(CFDataRef theData, CFRange range, UInt8 *buffer); + +CF_EXPORT +void CFDataSetLength(CFMutableDataRef theData, CFIndex length); + +CF_EXPORT +void CFDataIncreaseLength(CFMutableDataRef theData, CFIndex extraLength); + +CF_EXPORT +void CFDataAppendBytes(CFMutableDataRef theData, const UInt8 *bytes, CFIndex length); + +CF_EXPORT +void CFDataReplaceBytes(CFMutableDataRef theData, CFRange range, const UInt8 *newBytes, CFIndex newLength); + +CF_EXPORT +void CFDataDeleteBytes(CFMutableDataRef theData, CFRange range); + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFDATA__ */ +
diff --git a/CoreFoundation/CFDate.c b/CoreFoundation/CFDate.c new file mode 100644 index 0000000..0ce697b --- /dev/null +++ b/CoreFoundation/CFDate.c
@@ -0,0 +1,538 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFDate.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include <CoreFoundation/CFDate.h> +#include <CoreFoundation/CFTimeZone.h> +#include <CoreFoundation/CFDictionary.h> +#include <CoreFoundation/CFArray.h> +#include <CoreFoundation/CFString.h> +#include <CoreFoundation/CFNumber.h> +#include "CFInternal.h" +#include <math.h> +#if DEPLOYMENT_TARGET_LINUX +#include <time.h> +#endif +#if DEPLOYMENT_TARGET_MACOSX +#include <sys/time.h> +#endif + +#if defined(_MSC_VER) +extern void gettimeofday(struct timeval *tv, void *dummy); +#endif + +const CFTimeInterval kCFAbsoluteTimeIntervalSince1970 = 978307200.0L; +const CFTimeInterval kCFAbsoluteTimeIntervalSince1904 = 3061152000.0L; + +/* cjk: The Julian Date for the reference date is 2451910.5, + I think, in case that's ever useful. */ + +__private_extern__ double __CFTSRRate = 0.0; +static double __CF1_TSRRate = 0.0; + +#if DEPLOYMENT_TARGET_WINDOWS +// We should export this as SPI or API to clients - 3514284 +CFAbsoluteTime _CFAbsoluteTimeFromFileTime(const FILETIME *ft) { + CFAbsoluteTime ret = (CFTimeInterval)ft->dwHighDateTime * 429.49672960; + ret += (CFTimeInterval)ft->dwLowDateTime / 10000000.0; + ret -= (11644473600.0 + kCFAbsoluteTimeIntervalSince1970); + /* seconds between 1601 and 1970, 1970 and 2001 */ + return ret; +} +#endif + +__private_extern__ int64_t __CFTimeIntervalToTSR(CFTimeInterval ti) { + if ((ti * __CFTSRRate) > INT64_MAX / 2) return (INT64_MAX / 2); + return (int64_t)(ti * __CFTSRRate); +} + +__private_extern__ CFTimeInterval __CFTSRToTimeInterval(int64_t tsr) { + return (CFTimeInterval)((double)tsr * __CF1_TSRRate); +} + +#ifdef DEPLOYMENT_TARGET_WINDOWS +__private_extern__ CFAbsoluteTime __CFDateWindowsSystemTimeToAbsoluteTime(SYSTEMTIME *the_time) { + // this song and dance seems to be required to get enough precision + // and date offset that the OSX version returns - steps were culled + // from the remarks section of RtlTimeToSecondsSince1970 in the + // msdn documentation + + SYSTEMTIME darwin_epoch; + FILETIME also_the_time, also_epoch; + ULARGE_INTEGER also_also_the_time, also_also_epoch; + + memset(&darwin_epoch, 0, sizeof(SYSTEMTIME)); + SystemTimeToFileTime(the_time, &also_the_time); + + // MSDN says casting a pointer from FILETIME to ULARGE_INTEGER* or __int64* can cause alignment faults on WIN64 + also_also_the_time.LowPart = also_the_time.dwLowDateTime; + also_also_the_time.HighPart = also_the_time.dwHighDateTime; + + darwin_epoch.wYear = 2001; + darwin_epoch.wMonth = 1; + darwin_epoch.wDay = 1; + SystemTimeToFileTime(&darwin_epoch, &also_epoch); + also_also_epoch.LowPart = also_epoch.dwLowDateTime; + also_also_epoch.HighPart = also_epoch.dwHighDateTime; + + return((double)(also_also_the_time.QuadPart - also_also_epoch.QuadPart) / 10000000.0); +} +#endif // DEPLOYMENT_TARGET_WINDOWS + +CFAbsoluteTime CFAbsoluteTimeGetCurrent(void) { + CFAbsoluteTime ret; +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD + struct timeval tv; + gettimeofday(&tv, NULL); + ret = (CFTimeInterval)tv.tv_sec - kCFAbsoluteTimeIntervalSince1970; + ret += (1.0E-6 * (CFTimeInterval)tv.tv_usec); +#elif DEPLOYMENT_TARGET_WINDOWS + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + ret = _CFAbsoluteTimeFromFileTime(&ft); +#else +#error CFAbsoluteTimeGetCurrent unimplemented for this platform +#endif + return ret; +} + +__private_extern__ void __CFDateInitialize(void) { +#if DEPLOYMENT_TARGET_MACOSX + struct mach_timebase_info info; + mach_timebase_info(&info); + __CFTSRRate = (1.0E9 / (double)info.numer) * (double)info.denom; +#elif DEPLOYMENT_TARGET_WINDOWS + LARGE_INTEGER freq; + if (!QueryPerformanceFrequency(&freq)) { + HALT; + } + __CFTSRRate = (double)freq.QuadPart; +#elif DEPLOYMENT_TARGET_LINUX + // On Linux, __CFReadTSR (see ForFoundation.h) is implemented on + // the POSIX RT clock_* APIs which work at nanosecond granularity + // (though not necessarily nanosecond resolution). Simply set the + // TSRRate to 1 ns. + __CFTSRRate = 1.0E9; +#else +#warning "__CFTSRRate not initialized for this platform!" +#endif + __CF1_TSRRate = 1.0 / __CFTSRRate; + CFDateGetTypeID(); // cause side-effects +} + +#if 1 +struct __CFDate { + CFRuntimeBase _base; + CFAbsoluteTime _time; /* immutable */ +}; + +static Boolean __CFDateEqual(CFTypeRef cf1, CFTypeRef cf2) { + CFDateRef date1 = (CFDateRef)cf1; + CFDateRef date2 = (CFDateRef)cf2; + if (date1->_time != date2->_time) return false; + return true; +} + +static CFHashCode __CFDateHash(CFTypeRef cf) { + CFDateRef date = (CFDateRef)cf; + return (CFHashCode)(float)floor(date->_time); +} + +static CFStringRef __CFDateCopyDescription(CFTypeRef cf) { + CFDateRef date = (CFDateRef)cf; + return CFStringCreateWithFormat(CFGetAllocator(date), NULL, CFSTR("<CFDate %p [%p]>{time = %0.09g}"), cf, CFGetAllocator(date), date->_time); +} + +static CFTypeID __kCFDateTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFDateClass = { + 0, + "CFDate", + NULL, // init + NULL, // copy + NULL, // dealloc + __CFDateEqual, + __CFDateHash, + NULL, // + __CFDateCopyDescription +}; + +CFTypeID CFDateGetTypeID(void) { + if (_kCFRuntimeNotATypeID == __kCFDateTypeID) __kCFDateTypeID = _CFRuntimeRegisterClass(&__CFDateClass); + return __kCFDateTypeID; +} + +CFDateRef CFDateCreate(CFAllocatorRef allocator, CFAbsoluteTime at) { + CFDateRef memory; + uint32_t size; + size = sizeof(struct __CFDate) - sizeof(CFRuntimeBase); + memory = (CFDateRef)_CFRuntimeCreateInstance(allocator, CFDateGetTypeID(), size, NULL); + if (NULL == memory) { + return NULL; + } + ((struct __CFDate *)memory)->_time = at; + return memory; +} + +CFTimeInterval CFDateGetAbsoluteTime(CFDateRef date) { + CF_OBJC_FUNCDISPATCH0(CFDateGetTypeID(), CFTimeInterval, date, "timeIntervalSinceReferenceDate"); + __CFGenericValidateType(date, CFDateGetTypeID()); + return date->_time; +} + +CFTimeInterval CFDateGetTimeIntervalSinceDate(CFDateRef date, CFDateRef otherDate) { + CF_OBJC_FUNCDISPATCH1(CFDateGetTypeID(), CFTimeInterval, date, "timeIntervalSinceDate:", otherDate); + __CFGenericValidateType(date, CFDateGetTypeID()); + __CFGenericValidateType(otherDate, CFDateGetTypeID()); + return date->_time - otherDate->_time; +} + +CFComparisonResult CFDateCompare(CFDateRef date, CFDateRef otherDate, void *context) { + CF_OBJC_FUNCDISPATCH1(CFDateGetTypeID(), CFComparisonResult, date, "compare:", otherDate); + __CFGenericValidateType(date, CFDateGetTypeID()); + __CFGenericValidateType(otherDate, CFDateGetTypeID()); + if (date->_time < otherDate->_time) return kCFCompareLessThan; + if (date->_time > otherDate->_time) return kCFCompareGreaterThan; + return kCFCompareEqualTo; +} +#endif + +CF_INLINE int32_t __CFDoubleModToInt(double d, int32_t modulus) { + int32_t result = (int32_t)(float)floor(d - floor(d / modulus) * modulus); + if (result < 0) result += modulus; + return result; +} + +CF_INLINE double __CFDoubleMod(double d, int32_t modulus) { + double result = d - floor(d / modulus) * modulus; + if (result < 0.0) result += (double)modulus; + return result; +} + +static const uint8_t daysInMonth[16] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0, 0, 0}; +static const uint16_t daysBeforeMonth[16] = {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365, 0, 0}; +static const uint16_t daysAfterMonth[16] = {365, 334, 306, 275, 245, 214, 184, 153, 122, 92, 61, 31, 0, 0, 0, 0}; + +CF_INLINE bool isleap(int64_t year) { + int64_t y = (year + 1) % 400; /* correct to nearest multiple-of-400 year, then find the remainder */ + if (y < 0) y = -y; + return (0 == (y & 3) && 100 != y && 200 != y && 300 != y); +} + +/* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */ +CF_INLINE uint8_t __CFDaysInMonth(int8_t month, int64_t year, bool leap) { + return daysInMonth[month] + (2 == month && leap); +} + +/* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */ +CF_INLINE uint16_t __CFDaysBeforeMonth(int8_t month, int64_t year, bool leap) { + return daysBeforeMonth[month] + (2 < month && leap); +} + +/* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */ +CF_INLINE uint16_t __CFDaysAfterMonth(int8_t month, int64_t year, bool leap) { + return daysAfterMonth[month] + (month < 2 && leap); +} + +/* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */ +static void __CFYMDFromAbsolute(int64_t absolute, int64_t *year, int8_t *month, int8_t *day) { + int64_t b = absolute / 146097; // take care of as many multiples of 400 years as possible + int64_t y = b * 400; + uint16_t ydays; + absolute -= b * 146097; + while (absolute < 0) { + y -= 1; + absolute += __CFDaysAfterMonth(0, y, isleap(y)); + } + /* Now absolute is non-negative days to add to year */ + ydays = __CFDaysAfterMonth(0, y, isleap(y)); + while (ydays <= absolute) { + y += 1; + absolute -= ydays; + ydays = __CFDaysAfterMonth(0, y, isleap(y)); + } + /* Now we have year and days-into-year */ + if (year) *year = y; + if (month || day) { + int8_t m = absolute / 33 + 1; /* search from the approximation */ + bool leap = isleap(y); + while (__CFDaysBeforeMonth(m + 1, y, leap) <= absolute) m++; + if (month) *month = m; + if (day) *day = absolute - __CFDaysBeforeMonth(m, y, leap) + 1; + } +} + +/* year arg is absolute year; Gregorian 2001 == year 0; 2001/1/1 = absolute date 0 */ +static double __CFAbsoluteFromYMD(int64_t year, int8_t month, int8_t day) { + double absolute = 0.0; + int64_t idx; + int64_t b = year / 400; // take care of as many multiples of 400 years as possible + absolute += b * 146097.0; + year -= b * 400; + if (year < 0) { + for (idx = year; idx < 0; idx++) + absolute -= __CFDaysAfterMonth(0, idx, isleap(idx)); + } else { + for (idx = 0; idx < year; idx++) + absolute += __CFDaysAfterMonth(0, idx, isleap(idx)); + } + /* Now add the days into the original year */ + absolute += __CFDaysBeforeMonth(month, year, isleap(year)) + day - 1; + return absolute; +} + +Boolean CFGregorianDateIsValid(CFGregorianDate gdate, CFOptionFlags unitFlags) { + if ((unitFlags & kCFGregorianUnitsYears) && (gdate.year <= 0)) return false; + if ((unitFlags & kCFGregorianUnitsMonths) && (gdate.month < 1 || 12 < gdate.month)) return false; + if ((unitFlags & kCFGregorianUnitsDays) && (gdate.day < 1 || 31 < gdate.day)) return false; + if ((unitFlags & kCFGregorianUnitsHours) && (gdate.hour < 0 || 23 < gdate.hour)) return false; + if ((unitFlags & kCFGregorianUnitsMinutes) && (gdate.minute < 0 || 59 < gdate.minute)) return false; + if ((unitFlags & kCFGregorianUnitsSeconds) && (gdate.second < 0.0 || 60.0 <= gdate.second)) return false; + if ((unitFlags & kCFGregorianUnitsDays) && (unitFlags & kCFGregorianUnitsMonths) && (unitFlags & kCFGregorianUnitsYears) && (__CFDaysInMonth(gdate.month, gdate.year - 2001, isleap(gdate.year - 2001)) < gdate.day)) return false; + return true; +} + +CFAbsoluteTime CFGregorianDateGetAbsoluteTime(CFGregorianDate gdate, CFTimeZoneRef tz) { + CFAbsoluteTime at; + CFTimeInterval offset0, offset1; + if (NULL != tz) { + __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); + } + at = 86400.0 * __CFAbsoluteFromYMD(gdate.year - 2001, gdate.month, gdate.day); + at += 3600.0 * gdate.hour + 60.0 * gdate.minute + gdate.second; + if (NULL != tz) { + offset0 = CFTimeZoneGetSecondsFromGMT(tz, at); + offset1 = CFTimeZoneGetSecondsFromGMT(tz, at - offset0); + at -= offset1; + } + return at; +} + +CFGregorianDate CFAbsoluteTimeGetGregorianDate(CFAbsoluteTime at, CFTimeZoneRef tz) { + CFGregorianDate gdate; + int64_t absolute, year; + int8_t month, day; + CFAbsoluteTime fixedat; + if (NULL != tz) { + __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); + } + fixedat = at + (NULL != tz ? CFTimeZoneGetSecondsFromGMT(tz, at) : 0.0); + absolute = (int64_t)floor(fixedat / 86400.0); + __CFYMDFromAbsolute(absolute, &year, &month, &day); + if (INT32_MAX - 2001 < year) year = INT32_MAX - 2001; + gdate.year = year + 2001; + gdate.month = month; + gdate.day = day; + gdate.hour = __CFDoubleModToInt(floor(fixedat / 3600.0), 24); + gdate.minute = __CFDoubleModToInt(floor(fixedat / 60.0), 60); + gdate.second = __CFDoubleMod(fixedat, 60); + if (0.0 == gdate.second) gdate.second = 0.0; // stomp out possible -0.0 + return gdate; +} + +/* Note that the units of years and months are not equal length, but are treated as such. */ +CFAbsoluteTime CFAbsoluteTimeAddGregorianUnits(CFAbsoluteTime at, CFTimeZoneRef tz, CFGregorianUnits units) { + CFGregorianDate gdate; + CFGregorianUnits working; + CFAbsoluteTime candidate_at0, candidate_at1; + uint8_t monthdays; + + if (NULL != tz) { + __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); + } + + /* Most people seem to expect years, then months, then days, etc. + to be added in that order. Thus, 27 April + (4 days, 1 month) + = 31 May, and not 1 June. This is also relatively predictable. + + On another issue, months not being equal length, people also + seem to expect late day-of-month clamping (don't clamp as you + go through months), but clamp before adding in the days. Late + clamping is also more predictable given random starting points + and random numbers of months added (ie Jan 31 + 2 months could + be March 28 or March 29 in different years with aggressive + clamping). Proportionality (28 Feb + 1 month = 31 March) is + also not expected. + + Also, people don't expect time zone transitions to have any + effect when adding years and/or months and/or days, only. + Hours, minutes, and seconds, though, are added in as humans + would experience the passing of that time. What this means + is that if the date, after adding years, months, and days + lands on some date, and then adding hours, minutes, and + seconds crosses a time zone transition, the time zone + transition is accounted for. If adding years, months, and + days gets the date into a different time zone offset period, + that transition is not taken into account. + */ + gdate = CFAbsoluteTimeGetGregorianDate(at, tz); + /* We must work in a CFGregorianUnits, because the fields in the CFGregorianDate can easily overflow */ + working.years = gdate.year; + working.months = gdate.month; + working.days = gdate.day; + working.years += units.years; + working.months += units.months; + while (12 < working.months) { + working.months -= 12; + working.years += 1; + } + while (working.months < 1) { + working.months += 12; + working.years -= 1; + } + monthdays = __CFDaysInMonth(working.months, working.years - 2001, isleap(working.years - 2001)); + if (monthdays < working.days) { /* Clamp day to new month */ + working.days = monthdays; + } + working.days += units.days; + while (monthdays < working.days) { + working.months += 1; + if (12 < working.months) { + working.months -= 12; + working.years += 1; + } + working.days -= monthdays; + monthdays = __CFDaysInMonth(working.months, working.years - 2001, isleap(working.years - 2001)); + } + while (working.days < 1) { + working.months -= 1; + if (working.months < 1) { + working.months += 12; + working.years -= 1; + } + monthdays = __CFDaysInMonth(working.months, working.years - 2001, isleap(working.years - 2001)); + working.days += monthdays; + } + gdate.year = working.years; + gdate.month = working.months; + gdate.day = working.days; + /* Roll in hours, minutes, and seconds */ + candidate_at0 = CFGregorianDateGetAbsoluteTime(gdate, tz); + candidate_at1 = candidate_at0 + 3600.0 * units.hours + 60.0 * units.minutes + units.seconds; + /* If summing in the hours, minutes, and seconds delta pushes us + * into a new time zone offset, that will automatically be taken + * care of by the fact that we just add the raw time above. To + * undo that effect, we'd have to get the time zone offsets for + * candidate_at0 and candidate_at1 here, and subtract the + * difference (offset1 - offset0) from candidate_at1. */ + return candidate_at1; +} + +/* at1 - at2. The only constraint here is that this needs to be the inverse +of CFAbsoluteTimeByAddingGregorianUnits(), but that's a very rigid constraint. +Unfortunately, due to the nonuniformity of the year and month units, this +inversion essentially has to approximate until it finds the answer. */ +CFGregorianUnits CFAbsoluteTimeGetDifferenceAsGregorianUnits(CFAbsoluteTime at1, CFAbsoluteTime at2, CFTimeZoneRef tz, CFOptionFlags unitFlags) { + const int32_t seconds[5] = {366 * 24 * 3600, 31 * 24 * 3600, 24 * 3600, 3600, 60}; + CFGregorianUnits units = {0, 0, 0, 0, 0, 0.0}; + CFAbsoluteTime atold, atnew = at2; + int32_t idx, incr; + incr = (at2 < at1) ? 1 : -1; + /* Successive approximation: years, then months, then days, then hours, then minutes. */ + for (idx = 0; idx < 5; idx++) { + if (unitFlags & (1 << idx)) { + ((int32_t *)&units)[idx] = -3 * incr + (int32_t)((at1 - atnew) / seconds[idx]); + do { + atold = atnew; + ((int32_t *)&units)[idx] += incr; + atnew = CFAbsoluteTimeAddGregorianUnits(at2, tz, units); + } while ((1 == incr && atnew <= at1) || (-1 == incr && at1 <= atnew)); + ((int32_t *)&units)[idx] -= incr; + atnew = atold; + } + } + if (unitFlags & kCFGregorianUnitsSeconds) { + units.seconds = at1 - atnew; + } + if (0.0 == units.seconds) units.seconds = 0.0; // stomp out possible -0.0 + return units; +} + +SInt32 CFAbsoluteTimeGetDayOfWeek(CFAbsoluteTime at, CFTimeZoneRef tz) { + int64_t absolute; + CFAbsoluteTime fixedat; + if (NULL != tz) { + __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); + } + fixedat = at + (NULL != tz ? CFTimeZoneGetSecondsFromGMT(tz, at) : 0.0); + absolute = (int64_t)floor(fixedat / 86400.0); + return (absolute < 0) ? ((absolute + 1) % 7 + 7) : (absolute % 7 + 1); /* Monday = 1, etc. */ +} + +SInt32 CFAbsoluteTimeGetDayOfYear(CFAbsoluteTime at, CFTimeZoneRef tz) { + CFAbsoluteTime fixedat; + int64_t absolute, year; + int8_t month, day; + if (NULL != tz) { + __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); + } + fixedat = at + (NULL != tz ? CFTimeZoneGetSecondsFromGMT(tz, at) : 0.0); + absolute = (int64_t)floor(fixedat / 86400.0); + __CFYMDFromAbsolute(absolute, &year, &month, &day); + return __CFDaysBeforeMonth(month, year, isleap(year)) + day; +} + +/* "the first week of a year is the one which includes the first Thursday" (ISO 8601) */ +SInt32 CFAbsoluteTimeGetWeekOfYear(CFAbsoluteTime at, CFTimeZoneRef tz) { + int64_t absolute, year; + int8_t month, day; + CFAbsoluteTime fixedat; + if (NULL != tz) { + __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); + } + fixedat = at + (NULL != tz ? CFTimeZoneGetSecondsFromGMT(tz, at) : 0.0); + absolute = (int64_t)floor(fixedat / 86400.0); + __CFYMDFromAbsolute(absolute, &year, &month, &day); + double absolute0101 = __CFAbsoluteFromYMD(year, 1, 1); + int64_t dow0101 = __CFDoubleModToInt(absolute0101, 7) + 1; + /* First three and last three days of a year can end up in a week of a different year */ + if (1 == month && day < 4) { + if ((day < 4 && 5 == dow0101) || (day < 3 && 6 == dow0101) || (day < 2 && 7 == dow0101)) { + return 53; + } + } + if (12 == month && 28 < day) { + double absolute20101 = __CFAbsoluteFromYMD(year + 1, 1, 1); + int64_t dow20101 = __CFDoubleModToInt(absolute20101, 7) + 1; + if ((28 < day && 4 == dow20101) || (29 < day && 3 == dow20101) || (30 < day && 2 == dow20101)) { + return 1; + } + } + /* Days into year, plus a week-shifting correction, divided by 7. First week is 1. */ + return (__CFDaysBeforeMonth(month, year, isleap(year)) + day + (dow0101 - 11) % 7 + 2) / 7 + 1; +} + +
diff --git a/CoreFoundation/CFDate.h b/CoreFoundation/CFDate.h new file mode 100644 index 0000000..9503e58 --- /dev/null +++ b/CoreFoundation/CFDate.h
@@ -0,0 +1,136 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFDate.h + Copyright (c) 1998-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFDATE__) +#define __COREFOUNDATION_CFDATE__ 1 + +#include <CoreFoundation/CFBase.h> + +CF_EXTERN_C_BEGIN + +typedef double CFTimeInterval; +typedef CFTimeInterval CFAbsoluteTime; +/* absolute time is the time interval since the reference date */ +/* the reference date (epoch) is 00:00:00 1 January 2001. */ + +CF_EXPORT +CFAbsoluteTime CFAbsoluteTimeGetCurrent(void); + +CF_EXPORT +const CFTimeInterval kCFAbsoluteTimeIntervalSince1970; +CF_EXPORT +const CFTimeInterval kCFAbsoluteTimeIntervalSince1904; + +typedef const struct __CFDate * CFDateRef; + +CF_EXPORT +CFTypeID CFDateGetTypeID(void); + +CF_EXPORT +CFDateRef CFDateCreate(CFAllocatorRef allocator, CFAbsoluteTime at); + +CF_EXPORT +CFAbsoluteTime CFDateGetAbsoluteTime(CFDateRef theDate); + +CF_EXPORT +CFTimeInterval CFDateGetTimeIntervalSinceDate(CFDateRef theDate, CFDateRef otherDate); + +CF_EXPORT +CFComparisonResult CFDateCompare(CFDateRef theDate, CFDateRef otherDate, void *context); + +typedef const struct __CFTimeZone * CFTimeZoneRef; + +typedef struct _CFGregorianDate { + SInt32 year; + SInt8 month; + SInt8 day; + SInt8 hour; + SInt8 minute; + double second; +} CFGregorianDate; + +typedef struct { + SInt32 years; + SInt32 months; + SInt32 days; + SInt32 hours; + SInt32 minutes; + double seconds; +} CFGregorianUnits; + +enum { + kCFGregorianUnitsYears = (1 << 0), + kCFGregorianUnitsMonths = (1 << 1), + kCFGregorianUnitsDays = (1 << 2), + kCFGregorianUnitsHours = (1 << 3), + kCFGregorianUnitsMinutes = (1 << 4), + kCFGregorianUnitsSeconds = (1 << 5), +#if 0 + kCFGregorianUnitsTimeZone = (1 << 8), + kCFGregorianUnitsDayOfWeek = (1 << 9), +#endif + kCFGregorianAllUnits = 0x00FFFFFF +}; +typedef CFOptionFlags CFGregorianUnitFlags; + +CF_EXPORT +Boolean CFGregorianDateIsValid(CFGregorianDate gdate, CFOptionFlags unitFlags); + +CF_EXPORT +CFAbsoluteTime CFGregorianDateGetAbsoluteTime(CFGregorianDate gdate, CFTimeZoneRef tz); + +CF_EXPORT +CFGregorianDate CFAbsoluteTimeGetGregorianDate(CFAbsoluteTime at, CFTimeZoneRef tz); + +CF_EXPORT +CFAbsoluteTime CFAbsoluteTimeAddGregorianUnits(CFAbsoluteTime at, CFTimeZoneRef tz, CFGregorianUnits units); + +CF_EXPORT +CFGregorianUnits CFAbsoluteTimeGetDifferenceAsGregorianUnits(CFAbsoluteTime at1, CFAbsoluteTime at2, CFTimeZoneRef tz, CFOptionFlags unitFlags); + +CF_EXPORT +SInt32 CFAbsoluteTimeGetDayOfWeek(CFAbsoluteTime at, CFTimeZoneRef tz); + +CF_EXPORT +SInt32 CFAbsoluteTimeGetDayOfYear(CFAbsoluteTime at, CFTimeZoneRef tz); + +CF_EXPORT +SInt32 CFAbsoluteTimeGetWeekOfYear(CFAbsoluteTime at, CFTimeZoneRef tz); + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFDATE__ */ +
diff --git a/CoreFoundation/CFDateFormatter.c b/CoreFoundation/CFDateFormatter.c new file mode 100644 index 0000000..64871de --- /dev/null +++ b/CoreFoundation/CFDateFormatter.c
@@ -0,0 +1,802 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFDateFormatter.c + Copyright 2002-2003, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include <CoreFoundation/CFDateFormatter.h> +#include <CoreFoundation/CFDate.h> +#include <CoreFoundation/CFTimeZone.h> +#include <CoreFoundation/CFCalendar.h> +#include <CoreFoundation/CFNumber.h> +#include "CFInternal.h" +#include <unicode/udat.h> +#include <math.h> +#include <float.h> + +extern UCalendar *__CFCalendarCreateUCalendar(CFStringRef calendarID, CFStringRef localeID, CFTimeZoneRef tz); +#ifdef DEPLOYMENT_TARGET_WINDOWS +extern CFAbsoluteTime __CFDateWindowsSystemTimeToAbsoluteTime(SYSTEMTIME *time); +#endif +static void __CFDateFormatterCustomize(CFDateFormatterRef formatter); + +CF_EXPORT const CFStringRef kCFDateFormatterCalendarIdentifier; + +#define BUFFER_SIZE 768 + +struct __CFDateFormatter { + CFRuntimeBase _base; + UDateFormat *_df; + CFLocaleRef _locale; + CFDateFormatterStyle _timeStyle; + CFDateFormatterStyle _dateStyle; + CFStringRef _format; + CFStringRef _defformat; + CFStringRef _calendarName; + CFTimeZoneRef _tz; + CFDateRef _defaultDate; +}; + +static CFStringRef __CFDateFormatterCopyDescription(CFTypeRef cf) { + CFDateFormatterRef formatter = (CFDateFormatterRef)cf; + return CFStringCreateWithFormat(CFGetAllocator(formatter), NULL, CFSTR("<CFDateFormatter %p [%p]>"), cf, CFGetAllocator(formatter)); +} + +static void __CFDateFormatterDeallocate(CFTypeRef cf) { + CFDateFormatterRef formatter = (CFDateFormatterRef)cf; + if (formatter->_df) udat_close(formatter->_df); + if (formatter->_locale) CFRelease(formatter->_locale); + if (formatter->_format) CFRelease(formatter->_format); + if (formatter->_defformat) CFRelease(formatter->_defformat); + if (formatter->_calendarName) CFRelease(formatter->_calendarName); + if (formatter->_tz) CFRelease(formatter->_tz); + if (formatter->_defaultDate) CFRelease(formatter->_defaultDate); +} + +static CFTypeID __kCFDateFormatterTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFDateFormatterClass = { + 0, + "CFDateFormatter", + NULL, // init + NULL, // copy + __CFDateFormatterDeallocate, + NULL, + NULL, + NULL, // + __CFDateFormatterCopyDescription +}; + +static void __CFDateFormatterInitialize(void) { + __kCFDateFormatterTypeID = _CFRuntimeRegisterClass(&__CFDateFormatterClass); +} + +CFTypeID CFDateFormatterGetTypeID(void) { + if (_kCFRuntimeNotATypeID == __kCFDateFormatterTypeID) __CFDateFormatterInitialize(); + return __kCFDateFormatterTypeID; +} + +CFDateFormatterRef CFDateFormatterCreate(CFAllocatorRef allocator, CFLocaleRef locale, CFDateFormatterStyle dateStyle, CFDateFormatterStyle timeStyle) { + struct __CFDateFormatter *memory; + uint32_t size = sizeof(struct __CFDateFormatter) - sizeof(CFRuntimeBase); + if (allocator == NULL) allocator = __CFGetDefaultAllocator(); + __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); + if (locale) __CFGenericValidateType(locale, CFLocaleGetTypeID()); + memory = (struct __CFDateFormatter *)_CFRuntimeCreateInstance(allocator, CFDateFormatterGetTypeID(), size, NULL); + if (NULL == memory) { + return NULL; + } + memory->_df = NULL; + memory->_locale = NULL; + memory->_format = NULL; + memory->_defformat = NULL; + memory->_calendarName = NULL; + memory->_tz = NULL; + memory->_defaultDate = NULL; + if (NULL == locale) locale = CFLocaleGetSystem(); + memory->_dateStyle = dateStyle; + memory->_timeStyle = timeStyle; + int32_t udstyle, utstyle; + switch (dateStyle) { + case kCFDateFormatterNoStyle: udstyle = UDAT_NONE; break; + case kCFDateFormatterShortStyle: udstyle = UDAT_SHORT; break; + case kCFDateFormatterMediumStyle: udstyle = UDAT_MEDIUM; break; + case kCFDateFormatterLongStyle: udstyle = UDAT_LONG; break; + case kCFDateFormatterFullStyle: udstyle = UDAT_FULL; break; + default: + CFAssert2(0, __kCFLogAssertion, "%s(): unknown date style %d", __PRETTY_FUNCTION__, dateStyle); + udstyle = UDAT_MEDIUM; + memory->_dateStyle = kCFDateFormatterMediumStyle; + break; + } + switch (timeStyle) { + case kCFDateFormatterNoStyle: utstyle = UDAT_NONE; break; + case kCFDateFormatterShortStyle: utstyle = UDAT_SHORT; break; + case kCFDateFormatterMediumStyle: utstyle = UDAT_MEDIUM; break; + case kCFDateFormatterLongStyle: utstyle = UDAT_LONG; break; + case kCFDateFormatterFullStyle: utstyle = UDAT_FULL; break; + default: + CFAssert2(0, __kCFLogAssertion, "%s(): unknown time style %d", __PRETTY_FUNCTION__, timeStyle); + utstyle = UDAT_MEDIUM; + memory->_timeStyle = kCFDateFormatterMediumStyle; + break; + } + CFStringRef localeName = locale ? CFLocaleGetIdentifier(locale) : CFSTR(""); + char buffer[BUFFER_SIZE]; + const char *cstr = CFStringGetCStringPtr(localeName, kCFStringEncodingASCII); + if (NULL == cstr) { + if (CFStringGetCString(localeName, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer; + } + if (NULL == cstr) { + CFRelease(memory); + return NULL; + } + UChar ubuffer[BUFFER_SIZE]; + memset(ubuffer, 0x00, sizeof(UChar) * BUFFER_SIZE); + memory->_tz = CFTimeZoneCopyDefault(); +#ifdef DEPLOYMENT_TARGET_WINDOWS + // ICU doesn't know about Windows Time Zone names. It'll understand the abbreviation for the non-daylight saving time, though + TIME_ZONE_INFORMATION *tzi = (TIME_ZONE_INFORMATION *)CFDataGetBytePtr(CFTimeZoneGetData(memory->_tz)); + CFAbsoluteTime at = __CFDateWindowsSystemTimeToAbsoluteTime(&tzi->DaylightDate); + // subtract a day to get us off the time change boundary + at -= (60 * 60 * 24); + CFStringRef tznam = CFTimeZoneCopyAbbreviation(memory->_tz, at); +#else + CFStringRef tznam = CFTimeZoneGetName(memory->_tz); +#endif + CFIndex cnt = CFStringGetLength(tznam); + if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; + CFStringGetCharacters(tznam, CFRangeMake(0, cnt), (UniChar *)ubuffer); + UErrorCode status = U_ZERO_ERROR; + memory->_df = udat_open((UDateFormatStyle)utstyle, (UDateFormatStyle)udstyle, cstr, ubuffer, cnt, NULL, 0, &status); + CFAssert2(memory->_df, __kCFLogAssertion, "%s(): error (%d) creating date formatter", __PRETTY_FUNCTION__, status); + if (NULL == memory->_df) { + CFRelease(memory->_tz); + CFRelease(memory); + return NULL; + } + udat_setLenient(memory->_df, 0); + if (kCFDateFormatterNoStyle == dateStyle && kCFDateFormatterNoStyle == timeStyle) { + udat_applyPattern(memory->_df, false, NULL, 0); + } + CFTypeRef calident = CFLocaleGetValue(locale, kCFLocaleCalendarIdentifier); + if (calident && CFEqual(calident, kCFGregorianCalendar)) { + status = U_ZERO_ERROR; + udat_set2DigitYearStart(memory->_df, -631152000000.0, &status); // 1950-01-01 00:00:00 GMT + } + memory->_locale = locale ? CFLocaleCreateCopy(allocator, locale) : CFLocaleGetSystem(); + __CFDateFormatterCustomize(memory); + status = U_ZERO_ERROR; + int32_t ret = udat_toPattern(memory->_df, false, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && ret <= BUFFER_SIZE) { + memory->_format = CFStringCreateWithCharacters(allocator, (const UniChar *)ubuffer, ret); + } + memory->_defformat = memory->_format ? (CFStringRef)CFRetain(memory->_format) : NULL; + return (CFDateFormatterRef)memory; +} + +extern CFDictionaryRef __CFLocaleGetPrefs(CFLocaleRef locale); + +static void __substituteFormatStringFromPrefsDF(CFDateFormatterRef formatter, bool doTime) { + CFIndex formatStyle = doTime ? formatter->_timeStyle : formatter->_dateStyle; + CFStringRef prefName = doTime ? CFSTR("AppleICUTimeFormatStrings") : CFSTR("AppleICUDateFormatStrings"); + if (kCFDateFormatterNoStyle != formatStyle) { + CFStringRef pref = NULL; + CFDictionaryRef prefs = __CFLocaleGetPrefs(formatter->_locale); + CFPropertyListRef metapref = prefs ? CFDictionaryGetValue(prefs, prefName) : NULL; + if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) { + CFStringRef key; + switch (formatStyle) { + case kCFDateFormatterShortStyle: key = CFSTR("1"); break; + case kCFDateFormatterMediumStyle: key = CFSTR("2"); break; + case kCFDateFormatterLongStyle: key = CFSTR("3"); break; + case kCFDateFormatterFullStyle: key = CFSTR("4"); break; + default: key = CFSTR("0"); break; + } + pref = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)metapref, key); + } + if (NULL != pref && CFGetTypeID(pref) == CFStringGetTypeID()) { + int32_t icustyle = UDAT_NONE; + switch (formatStyle) { + case kCFDateFormatterShortStyle: icustyle = UDAT_SHORT; break; + case kCFDateFormatterMediumStyle: icustyle = UDAT_MEDIUM; break; + case kCFDateFormatterLongStyle: icustyle = UDAT_LONG; break; + case kCFDateFormatterFullStyle: icustyle = UDAT_FULL; break; + } + CFStringRef localeName = CFLocaleGetIdentifier(formatter->_locale); + char buffer[BUFFER_SIZE]; + const char *cstr = CFStringGetCStringPtr(localeName, kCFStringEncodingASCII); + if (NULL == cstr) { + if (CFStringGetCString(localeName, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer; + } + UErrorCode status = U_ZERO_ERROR; + UDateFormat *df = udat_open((UDateFormatStyle)(doTime ? icustyle : UDAT_NONE), (UDateFormatStyle)(doTime ? UDAT_NONE : icustyle), cstr, NULL, 0, NULL, 0, &status); + if (NULL != df) { + UChar ubuffer[BUFFER_SIZE]; + status = U_ZERO_ERROR; + int32_t date_len = udat_toPattern(df, false, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && date_len <= BUFFER_SIZE) { + CFStringRef dateString = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (UniChar *)ubuffer, date_len); + status = U_ZERO_ERROR; + int32_t formatter_len = udat_toPattern(formatter->_df, false, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && formatter_len <= BUFFER_SIZE) { + CFMutableStringRef formatString = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); + CFStringAppendCharacters(formatString, (UniChar *)ubuffer, formatter_len); + // find dateString inside formatString, substitute the pref in that range + CFRange result; + if (CFStringFindWithOptions(formatString, dateString, CFRangeMake(0, formatter_len), 0, &result)) { + CFStringReplace(formatString, result, pref); + int32_t new_len = CFStringGetLength(formatString); +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX + STACK_BUFFER_DECL(UChar, new_buffer, new_len); +#else + UChar new_buffer[BUFFER_SIZE]; // Dynamic stack allocation is GNU specific +#endif + const UChar *new_ustr = (UChar *)CFStringGetCharactersPtr(formatString); + if (NULL == new_ustr) { + CFStringGetCharacters(formatString, CFRangeMake(0, new_len), (UniChar *)new_buffer); + new_ustr = new_buffer; + } + status = U_ZERO_ERROR; +// udat_applyPattern(formatter->_df, false, new_ustr, new_len, &status); + udat_applyPattern(formatter->_df, false, new_ustr, new_len); + } + CFRelease(formatString); + } + CFRelease(dateString); + } + udat_close(df); + } + } + } +} + +static void __CFDateFormatterApplySymbolPrefs(const void *key, const void *value, void *context) { + if (CFGetTypeID(key) == CFStringGetTypeID() && CFGetTypeID(value) == CFArrayGetTypeID()) { + CFDateFormatterRef formatter = (CFDateFormatterRef)context; + UDateFormatSymbolType sym = (UDateFormatSymbolType)CFStringGetIntValue((CFStringRef)key); + CFArrayRef array = (CFArrayRef)value; + CFIndex idx, cnt = CFArrayGetCount(array); + for (idx = 0; idx < cnt; idx++) { + CFStringRef item = (CFStringRef)CFArrayGetValueAtIndex(array, idx); + if (CFGetTypeID(item) != CFStringGetTypeID()) continue; + CFIndex item_cnt = CFStringGetLength(item); +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX + STACK_BUFFER_DECL(UChar, item_buffer, __CFMin(BUFFER_SIZE, item_cnt)); +#else + UChar item_buffer[BUFFER_SIZE]; // Dynamic stack allocation is GNU specific +#endif + UChar *item_ustr = (UChar *)CFStringGetCharactersPtr(item); + if (NULL == item_ustr) { + item_cnt = __CFMin(BUFFER_SIZE, item_cnt); + CFStringGetCharacters(item, CFRangeMake(0, item_cnt), (UniChar *)item_buffer); + item_ustr = item_buffer; + } + UErrorCode status = U_ZERO_ERROR; + udat_setSymbols(formatter->_df, sym, idx, item_ustr, item_cnt, &status); + } + } +} + +static void __CFDateFormatterCustomize(CFDateFormatterRef formatter) { + __substituteFormatStringFromPrefsDF(formatter, false); + __substituteFormatStringFromPrefsDF(formatter, true); + CFDictionaryRef prefs = __CFLocaleGetPrefs(formatter->_locale); + CFPropertyListRef metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUDateTimeSymbols")) : NULL; + if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) { + CFDictionaryApplyFunction((CFDictionaryRef)metapref, __CFDateFormatterApplySymbolPrefs, formatter); + } +} + +CFLocaleRef CFDateFormatterGetLocale(CFDateFormatterRef formatter) { + __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); + return formatter->_locale; +} + +CFDateFormatterStyle CFDateFormatterGetDateStyle(CFDateFormatterRef formatter) { + __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); + return formatter->_dateStyle; +} + +CFDateFormatterStyle CFDateFormatterGetTimeStyle(CFDateFormatterRef formatter) { + __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); + return formatter->_timeStyle; +} + +CFStringRef CFDateFormatterGetFormat(CFDateFormatterRef formatter) { + __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); + return formatter->_format; +} + +void CFDateFormatterSetFormat(CFDateFormatterRef formatter, CFStringRef formatString) { + __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); + __CFGenericValidateType(formatString, CFStringGetTypeID()); + CFIndex cnt = CFStringGetLength(formatString); + CFAssert1(cnt <= 1024, __kCFLogAssertion, "%s(): format string too long", __PRETTY_FUNCTION__); + if (formatter->_format != formatString && cnt <= 1024) { +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX + STACK_BUFFER_DECL(UChar, ubuffer, cnt); +#else + UChar ubuffer[BUFFER_SIZE]; // Dynamic stack allocation is GNU specific +#endif + const UChar *ustr = (UChar *)CFStringGetCharactersPtr((CFStringRef)formatString); + if (NULL == ustr) { + CFStringGetCharacters(formatString, CFRangeMake(0, cnt), (UniChar *)ubuffer); + ustr = ubuffer; + } + UErrorCode status = U_ZERO_ERROR; +// udat_applyPattern(formatter->_df, false, ustr, cnt, &status); + udat_applyPattern(formatter->_df, false, ustr, cnt); + if (U_SUCCESS(status)) { + if (formatter->_format) CFRelease(formatter->_format); + formatter->_format = (CFStringRef)CFStringCreateCopy(CFGetAllocator(formatter), formatString); + } + } +} + +CFStringRef CFDateFormatterCreateStringWithDate(CFAllocatorRef allocator, CFDateFormatterRef formatter, CFDateRef date) { + if (allocator == NULL) allocator = __CFGetDefaultAllocator(); + __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); + __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); + __CFGenericValidateType(date, CFDateGetTypeID()); + return CFDateFormatterCreateStringWithAbsoluteTime(allocator, formatter, CFDateGetAbsoluteTime(date)); +} + +CFStringRef CFDateFormatterCreateStringWithAbsoluteTime(CFAllocatorRef allocator, CFDateFormatterRef formatter, CFAbsoluteTime at) { + if (allocator == NULL) allocator = __CFGetDefaultAllocator(); + __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); + __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); + UChar *ustr = NULL, ubuffer[BUFFER_SIZE]; + UErrorCode status = U_ZERO_ERROR; + CFIndex used, cnt = BUFFER_SIZE; + UDate ud = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0 + 0.5; + used = udat_format(formatter->_df, ud, ubuffer, cnt, NULL, &status); + if (status == U_BUFFER_OVERFLOW_ERROR || cnt < used) { + cnt = used + 1; + ustr = (UChar *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(UChar) * cnt, 0); + status = U_ZERO_ERROR; + used = udat_format(formatter->_df, ud, ustr, cnt, NULL, &status); + } + CFStringRef string = NULL; + if (U_SUCCESS(status)) { + string = CFStringCreateWithCharacters(allocator, (const UniChar *)(ustr ? ustr : ubuffer), used); + } + if (ustr) CFAllocatorDeallocate(kCFAllocatorSystemDefault, ustr); + return string; +} + +CFDateRef CFDateFormatterCreateDateFromString(CFAllocatorRef allocator, CFDateFormatterRef formatter, CFStringRef string, CFRange *rangep) { + if (allocator == NULL) allocator = __CFGetDefaultAllocator(); + __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); + __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); + __CFGenericValidateType(string, CFStringGetTypeID()); + CFAbsoluteTime at; + if (CFDateFormatterGetAbsoluteTimeFromString(formatter, string, rangep, &at)) { + return CFDateCreate(allocator, at); + } + return NULL; +} + +Boolean CFDateFormatterGetAbsoluteTimeFromString(CFDateFormatterRef formatter, CFStringRef string, CFRange *rangep, CFAbsoluteTime *atp) { + __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); + __CFGenericValidateType(string, CFStringGetTypeID()); + CFRange range = {0, 0}; + if (rangep) { + range = *rangep; + } else { + range.length = CFStringGetLength(string); + } + if (1024 < range.length) range.length = 1024; + const UChar *ustr = (UChar *)CFStringGetCharactersPtr(string); +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX + STACK_BUFFER_DECL(UChar, ubuffer, (NULL == ustr) ? range.length : 1); +#else + UChar ubuffer[BUFFER_SIZE]; // Dynamic stack allocation is GNU specific +#endif + if (NULL == ustr) { + CFStringGetCharacters(string, range, (UniChar *)ubuffer); + ustr = ubuffer; + } else { + ustr += range.location; + } + UDate udate; + int32_t dpos = 0; + UErrorCode status = U_ZERO_ERROR; + if (formatter->_defaultDate) { + CFAbsoluteTime at = CFDateGetAbsoluteTime(formatter->_defaultDate); + udate = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0; + UDateFormat *df2 = udat_clone(formatter->_df, &status); + UCalendar *cal2 = (UCalendar *)udat_getCalendar(df2); + ucal_setMillis(cal2, udate, &status); + udat_parseCalendar(formatter->_df, cal2, ustr, range.length, &dpos, &status); + udate = ucal_getMillis(cal2, &status); + udat_close(df2); + } else { + udate = udat_parse(formatter->_df, ustr, range.length, &dpos, &status); + } + if (rangep) rangep->length = dpos; + if (U_FAILURE(status)) { + return false; + } + if (atp) { + *atp = (double)udate / 1000.0 - kCFAbsoluteTimeIntervalSince1970; + } + return true; +} + +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX +#define SET_SYMBOLS_ARRAY(ICU_CODE, INDEX_BASE) \ + __CFGenericValidateType(value, CFArrayGetTypeID()); \ + CFArrayRef array = (CFArrayRef)value; \ + CFIndex idx, cnt = CFArrayGetCount(array); \ + for (idx = 0; idx < cnt; idx++) { \ + CFStringRef item = (CFStringRef)CFArrayGetValueAtIndex(array, idx); \ + __CFGenericValidateType(item, CFStringGetTypeID()); \ + CFIndex item_cnt = CFStringGetLength(item); \ + STACK_BUFFER_DECL(UChar, item_buffer, __CFMin(BUFFER_SIZE, item_cnt)); \ + UChar *item_ustr = (UChar *)CFStringGetCharactersPtr(item); \ + if (NULL == item_ustr) { \ + item_cnt = __CFMin(BUFFER_SIZE, item_cnt); \ + CFStringGetCharacters(item, CFRangeMake(0, item_cnt), (UniChar *)item_buffer); \ + item_ustr = item_buffer; \ + } \ + status = U_ZERO_ERROR; \ + udat_setSymbols(formatter->_df, ICU_CODE, idx + INDEX_BASE, item_ustr, item_cnt, &status); \ + } +#else +#define SET_SYMBOLS_ARRAY(ICU_CODE, INDEX_BASE) \ + __CFGenericValidateType(value, CFArrayGetTypeID()); \ + CFArrayRef array = (CFArrayRef)value; \ + CFIndex idx, cnt = CFArrayGetCount(array); \ + for (idx = 0; idx < cnt; idx++) { \ + CFStringRef item = (CFStringRef)CFArrayGetValueAtIndex(array, idx); \ + __CFGenericValidateType(item, CFStringGetTypeID()); \ + CFIndex item_cnt = CFStringGetLength(item); \ + UChar item_buffer[BUFFER_SIZE]; \ + UChar *item_ustr = (UChar *)CFStringGetCharactersPtr(item); \ + if (NULL == item_ustr) { \ + item_cnt = __CFMin(BUFFER_SIZE, item_cnt); \ + CFStringGetCharacters(item, CFRangeMake(0, item_cnt), (UniChar *)item_buffer); \ + item_ustr = item_buffer; \ + } \ + status = U_ZERO_ERROR; \ + udat_setSymbols(formatter->_df, ICU_CODE, idx + INDEX_BASE, item_ustr, item_cnt, &status); \ + } +#endif + + +void CFDateFormatterSetProperty(CFDateFormatterRef formatter, CFStringRef key, CFTypeRef value) { + __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); + __CFGenericValidateType(key, CFStringGetTypeID()); + UErrorCode status = U_ZERO_ERROR; + UChar ubuffer[BUFFER_SIZE]; + + if (kCFDateFormatterIsLenient == key) { + __CFGenericValidateType(value, CFBooleanGetTypeID()); + udat_setLenient(formatter->_df, (kCFBooleanTrue == value)); + } else if (kCFDateFormatterCalendar == key) { + __CFGenericValidateType(value, CFCalendarGetTypeID()); + CFStringRef localeName = CFLocaleGetIdentifier(formatter->_locale); + CFDictionaryRef components = CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault, localeName); + CFMutableDictionaryRef mcomponents = CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault, 0, components); + CFDictionarySetValue(mcomponents, kCFLocaleCalendarIdentifier, CFCalendarGetIdentifier((CFCalendarRef)value)); + localeName = CFLocaleCreateLocaleIdentifierFromComponents(kCFAllocatorSystemDefault, mcomponents); + CFRelease(mcomponents); + CFRelease(components); + CFLocaleRef newLocale = CFLocaleCreate(CFGetAllocator(formatter->_locale), localeName); + CFRelease(localeName); + CFRelease(formatter->_locale); + formatter->_locale = newLocale; + UCalendar *cal = __CFCalendarCreateUCalendar(NULL, CFLocaleGetIdentifier(formatter->_locale), formatter->_tz); + if (cal) udat_setCalendar(formatter->_df, cal); + if (cal) ucal_close(cal); + } else if (kCFDateFormatterCalendarIdentifier == key || kCFDateFormatterCalendarName == key) { + __CFGenericValidateType(value, CFStringGetTypeID()); + CFStringRef localeName = CFLocaleGetIdentifier(formatter->_locale); + CFDictionaryRef components = CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault, localeName); + CFMutableDictionaryRef mcomponents = CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault, 0, components); + CFDictionarySetValue(mcomponents, kCFLocaleCalendarIdentifier, value); + localeName = CFLocaleCreateLocaleIdentifierFromComponents(kCFAllocatorSystemDefault, mcomponents); + CFRelease(mcomponents); + CFRelease(components); + CFLocaleRef newLocale = CFLocaleCreate(CFGetAllocator(formatter->_locale), localeName); + CFRelease(localeName); + CFRelease(formatter->_locale); + formatter->_locale = newLocale; + UCalendar *cal = __CFCalendarCreateUCalendar(NULL, CFLocaleGetIdentifier(formatter->_locale), formatter->_tz); + if (cal) udat_setCalendar(formatter->_df, cal); + if (cal) ucal_close(cal); + } else if (kCFDateFormatterTimeZone == key) { + __CFGenericValidateType(value, CFTimeZoneGetTypeID()); + CFTimeZoneRef old = formatter->_tz; + formatter->_tz = value ? (CFTimeZoneRef)CFRetain(value) : CFTimeZoneCopyDefault(); + if (old) CFRelease(old); + CFStringRef tznam = CFTimeZoneGetName(formatter->_tz); + UCalendar *cal = (UCalendar *)udat_getCalendar(formatter->_df); + CFIndex ucnt = CFStringGetLength(tznam); + if (BUFFER_SIZE < ucnt) ucnt = BUFFER_SIZE; + CFStringGetCharacters(tznam, CFRangeMake(0, ucnt), (UniChar *)ubuffer); + ucal_setTimeZone(cal, ubuffer, ucnt, &status); + } else if (kCFDateFormatterDefaultFormat == key) { + // read-only attribute + } else if (kCFDateFormatterTwoDigitStartDate == key) { + __CFGenericValidateType(value, CFDateGetTypeID()); + CFAbsoluteTime at = CFDateGetAbsoluteTime((CFDateRef)value); + UDate udate = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0; + udat_set2DigitYearStart(formatter->_df, udate, &status); + } else if (kCFDateFormatterDefaultDate == key) { + __CFGenericValidateType(value, CFDateGetTypeID()); + CFDateRef old = formatter->_defaultDate; + formatter->_defaultDate = value ? (CFDateRef)CFRetain(value) : NULL; + if (old) CFRelease(old); + } else if (kCFDateFormatterEraSymbols == key) { + SET_SYMBOLS_ARRAY(UDAT_ERAS, 0) + } else if (kCFDateFormatterMonthSymbols == key) { + SET_SYMBOLS_ARRAY(UDAT_MONTHS, 0) + } else if (kCFDateFormatterShortMonthSymbols == key) { + SET_SYMBOLS_ARRAY(UDAT_SHORT_MONTHS, 0) + } else if (kCFDateFormatterWeekdaySymbols == key) { + SET_SYMBOLS_ARRAY(UDAT_WEEKDAYS, 1) + } else if (kCFDateFormatterShortWeekdaySymbols == key) { + SET_SYMBOLS_ARRAY(UDAT_SHORT_WEEKDAYS, 1) + } else if (kCFDateFormatterAMSymbol == key) { + __CFGenericValidateType(value, CFStringGetTypeID()); + CFIndex item_cnt = CFStringGetLength((CFStringRef)value); +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX + STACK_BUFFER_DECL(UChar, item_buffer, __CFMin(BUFFER_SIZE, item_cnt)); +#else + UChar item_buffer[BUFFER_SIZE]; +#endif + UChar *item_ustr = (UChar *)CFStringGetCharactersPtr((CFStringRef)value); + if (NULL == item_ustr) { + item_cnt = __CFMin(BUFFER_SIZE, item_cnt); + CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, item_cnt), (UniChar *)item_buffer); + item_ustr = item_buffer; + } + udat_setSymbols(formatter->_df, UDAT_AM_PMS, 0, item_ustr, item_cnt, &status); + } else if (kCFDateFormatterPMSymbol == key) { + __CFGenericValidateType(value, CFStringGetTypeID()); + CFIndex item_cnt = CFStringGetLength((CFStringRef)value); +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX + STACK_BUFFER_DECL(UChar, item_buffer, __CFMin(BUFFER_SIZE, item_cnt)); +#else + UChar item_buffer[BUFFER_SIZE]; +#endif + UChar *item_ustr = (UChar *)CFStringGetCharactersPtr((CFStringRef)value); + if (NULL == item_ustr) { + item_cnt = __CFMin(BUFFER_SIZE, item_cnt); + CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, item_cnt), (UniChar *)item_buffer); + item_ustr = item_buffer; + } + udat_setSymbols(formatter->_df, UDAT_AM_PMS, 1, item_ustr, item_cnt, &status); + } else if (kCFDateFormatterGregorianStartDate == key) { + __CFGenericValidateType(value, CFDateGetTypeID()); + CFAbsoluteTime at = CFDateGetAbsoluteTime((CFDateRef)value); + UDate udate = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0; + UCalendar *cal = (UCalendar *)udat_getCalendar(formatter->_df); + ucal_setGregorianChange(cal, udate, &status); + } else if (kCFDateFormatterLongEraSymbols == key) { + SET_SYMBOLS_ARRAY(UDAT_ERA_NAMES, 0) + } else if (kCFDateFormatterVeryShortMonthSymbols == key) { + SET_SYMBOLS_ARRAY(UDAT_NARROW_MONTHS, 0) + } else if (kCFDateFormatterStandaloneMonthSymbols == key) { + SET_SYMBOLS_ARRAY(UDAT_STANDALONE_MONTHS, 0) + } else if (kCFDateFormatterShortStandaloneMonthSymbols == key) { + SET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_MONTHS, 0) + } else if (kCFDateFormatterVeryShortStandaloneMonthSymbols == key) { + SET_SYMBOLS_ARRAY(UDAT_STANDALONE_NARROW_MONTHS, 0) + } else if (kCFDateFormatterVeryShortWeekdaySymbols == key) { + SET_SYMBOLS_ARRAY(UDAT_NARROW_WEEKDAYS, 1) + } else if (kCFDateFormatterStandaloneWeekdaySymbols == key) { + SET_SYMBOLS_ARRAY(UDAT_STANDALONE_WEEKDAYS, 1) + } else if (kCFDateFormatterShortStandaloneWeekdaySymbols == key) { + SET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_WEEKDAYS, 1) + } else if (kCFDateFormatterVeryShortStandaloneWeekdaySymbols == key) { + SET_SYMBOLS_ARRAY(UDAT_STANDALONE_NARROW_WEEKDAYS, 1) + } else if (kCFDateFormatterQuarterSymbols == key) { + SET_SYMBOLS_ARRAY(UDAT_QUARTERS, 1) + } else if (kCFDateFormatterShortQuarterSymbols == key) { + SET_SYMBOLS_ARRAY(UDAT_SHORT_QUARTERS, 1) + } else if (kCFDateFormatterStandaloneQuarterSymbols == key) { + SET_SYMBOLS_ARRAY(UDAT_STANDALONE_QUARTERS, 1) + } else if (kCFDateFormatterShortStandaloneQuarterSymbols == key) { + SET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_QUARTERS, 1) + } else { + CFAssert3(0, __kCFLogAssertion, "%s(): unknown key %p (%@)", __PRETTY_FUNCTION__, key, key); + } +} + +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX +#define GET_SYMBOLS_ARRAY(ICU_CODE, INDEX_BASE) \ + CFIndex idx, cnt = udat_countSymbols(formatter->_df, ICU_CODE) - INDEX_BASE; \ + STACK_BUFFER_DECL(CFStringRef, strings, cnt); \ + for (idx = 0; idx < cnt; idx++) { \ + CFStringRef str = NULL; \ + status = U_ZERO_ERROR; \ + CFIndex ucnt = udat_getSymbols(formatter->_df, ICU_CODE, idx + INDEX_BASE, ubuffer, BUFFER_SIZE, &status); \ + if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { \ + str = CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, ucnt); \ + } \ + strings[idx] = !str ? (CFStringRef)CFRetain(CFSTR("<error>")) : str; \ + } \ + CFArrayRef array = CFArrayCreate(CFGetAllocator(formatter), (const void **)strings, cnt, &kCFTypeArrayCallBacks); \ + while (cnt--) { \ + CFRelease(strings[cnt]); \ + } \ + return array; +#else +#define GET_SYMBOLS_ARRAY(ICU_CODE, INDEX_BASE) \ + CFIndex idx, cnt = udat_countSymbols(formatter->_df, ICU_CODE) - INDEX_BASE; \ + CFStringRef strings[BUFFER_SIZE]; \ + for (idx = 0; idx < cnt; idx++) { \ + CFStringRef str = NULL; \ + status = U_ZERO_ERROR; \ + CFIndex ucnt = udat_getSymbols(formatter->_df, ICU_CODE, idx + INDEX_BASE, ubuffer, BUFFER_SIZE, &status); \ + if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { \ + str = CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, ucnt); \ + } \ + strings[idx] = !str ? (CFStringRef)CFRetain(CFSTR("<error>")) : str; \ + } \ + CFArrayRef array = CFArrayCreate(CFGetAllocator(formatter), (const void **)strings, cnt, &kCFTypeArrayCallBacks); \ + while (cnt--) { \ + CFRelease(strings[cnt]); \ + } \ + return array; +#endif + +CFTypeRef CFDateFormatterCopyProperty(CFDateFormatterRef formatter, CFStringRef key) { + __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); + __CFGenericValidateType(key, CFStringGetTypeID()); + UErrorCode status = U_ZERO_ERROR; + UChar ubuffer[BUFFER_SIZE]; + + if (kCFDateFormatterIsLenient == key) { + return CFRetain(udat_isLenient(formatter->_df) ? kCFBooleanTrue : kCFBooleanFalse); + } else if (kCFDateFormatterCalendar == key) { + CFCalendarRef calendar = (CFCalendarRef)CFLocaleGetValue(formatter->_locale, kCFLocaleCalendar); + return calendar ? CFRetain(calendar) : NULL; + } else if (kCFDateFormatterCalendarIdentifier == key || kCFDateFormatterCalendarName == key) { + CFStringRef ident = (CFStringRef)CFLocaleGetValue(formatter->_locale, kCFLocaleCalendarIdentifier); + return ident ? CFRetain(ident) : NULL; + } else if (kCFDateFormatterTimeZone == key) { + return CFRetain(formatter->_tz); + } else if (kCFDateFormatterDefaultFormat == key) { + return formatter->_defformat ? CFRetain(formatter->_defformat) : NULL; + } else if (kCFDateFormatterTwoDigitStartDate == key) { + UDate udate = udat_get2DigitYearStart(formatter->_df, &status); + if (U_SUCCESS(status)) { + CFAbsoluteTime at = (double)udate / 1000.0 - kCFAbsoluteTimeIntervalSince1970; + return CFDateCreate(CFGetAllocator(formatter), at); + } + } else if (kCFDateFormatterDefaultDate == key) { + return formatter->_defaultDate ? CFRetain(formatter->_defaultDate) : NULL; + } else if (kCFDateFormatterEraSymbols == key) { + GET_SYMBOLS_ARRAY(UDAT_ERAS, 0) + } else if (kCFDateFormatterMonthSymbols == key) { + GET_SYMBOLS_ARRAY(UDAT_MONTHS, 0) + } else if (kCFDateFormatterShortMonthSymbols == key) { + GET_SYMBOLS_ARRAY(UDAT_SHORT_MONTHS, 0) + } else if (kCFDateFormatterWeekdaySymbols == key) { + GET_SYMBOLS_ARRAY(UDAT_WEEKDAYS, 1) + } else if (kCFDateFormatterShortWeekdaySymbols == key) { + GET_SYMBOLS_ARRAY(UDAT_SHORT_WEEKDAYS, 1) + } else if (kCFDateFormatterAMSymbol == key) { + CFIndex cnt = udat_countSymbols(formatter->_df, UDAT_AM_PMS); + if (2 <= cnt) { + CFIndex ucnt = udat_getSymbols(formatter->_df, UDAT_AM_PMS, 0, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { + return CFStringCreateWithCharacters(CFGetAllocator(formatter), (UniChar *)ubuffer, ucnt); + } + } + } else if (kCFDateFormatterPMSymbol == key) { + CFIndex cnt = udat_countSymbols(formatter->_df, UDAT_AM_PMS); + if (2 <= cnt) { + CFIndex ucnt = udat_getSymbols(formatter->_df, UDAT_AM_PMS, 1, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { + return CFStringCreateWithCharacters(CFGetAllocator(formatter), (UniChar *)ubuffer, ucnt); + } + } + } else if (kCFDateFormatterGregorianStartDate == key) { + UCalendar *cal = (UCalendar *)udat_getCalendar(formatter->_df); + UDate udate = ucal_getGregorianChange(cal, &status); + if (U_SUCCESS(status)) { + CFAbsoluteTime at = (double)udate / 1000.0 - kCFAbsoluteTimeIntervalSince1970; + return CFDateCreate(CFGetAllocator(formatter), at); + } + } else if (kCFDateFormatterLongEraSymbols == key) { + GET_SYMBOLS_ARRAY(UDAT_ERA_NAMES, 0) + } else if (kCFDateFormatterVeryShortMonthSymbols == key) { + GET_SYMBOLS_ARRAY(UDAT_NARROW_MONTHS, 0) + } else if (kCFDateFormatterStandaloneMonthSymbols == key) { + GET_SYMBOLS_ARRAY(UDAT_STANDALONE_MONTHS, 0) + } else if (kCFDateFormatterShortStandaloneMonthSymbols == key) { + GET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_MONTHS, 0) + } else if (kCFDateFormatterVeryShortStandaloneMonthSymbols == key) { + GET_SYMBOLS_ARRAY(UDAT_STANDALONE_NARROW_MONTHS, 0) + } else if (kCFDateFormatterVeryShortWeekdaySymbols == key) { + GET_SYMBOLS_ARRAY(UDAT_NARROW_WEEKDAYS, 1) + } else if (kCFDateFormatterStandaloneWeekdaySymbols == key) { + GET_SYMBOLS_ARRAY(UDAT_STANDALONE_WEEKDAYS, 1) + } else if (kCFDateFormatterShortStandaloneWeekdaySymbols == key) { + GET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_WEEKDAYS, 1) + } else if (kCFDateFormatterVeryShortStandaloneWeekdaySymbols == key) { + GET_SYMBOLS_ARRAY(UDAT_STANDALONE_NARROW_WEEKDAYS, 1) + } else if (kCFDateFormatterQuarterSymbols == key) { + GET_SYMBOLS_ARRAY(UDAT_QUARTERS, 1) + } else if (kCFDateFormatterShortQuarterSymbols == key) { + GET_SYMBOLS_ARRAY(UDAT_SHORT_QUARTERS, 1) + } else if (kCFDateFormatterStandaloneQuarterSymbols == key) { + GET_SYMBOLS_ARRAY(UDAT_STANDALONE_QUARTERS, 1) + } else if (kCFDateFormatterShortStandaloneQuarterSymbols == key) { + GET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_QUARTERS, 1) + } else { + CFAssert3(0, __kCFLogAssertion, "%s(): unknown key %p (%@)", __PRETTY_FUNCTION__, key, key); + } + return NULL; +} + +CONST_STRING_DECL(kCFDateFormatterIsLenient, "kCFDateFormatterIsLenient") +CONST_STRING_DECL(kCFDateFormatterTimeZone, "kCFDateFormatterTimeZone") +CONST_STRING_DECL(kCFDateFormatterCalendarName, "kCFDateFormatterCalendarName") +CONST_STRING_DECL(kCFDateFormatterCalendarIdentifier, "kCFDateFormatterCalendarIdentifier") +CONST_STRING_DECL(kCFDateFormatterCalendar, "kCFDateFormatterCalendar") +CONST_STRING_DECL(kCFDateFormatterDefaultFormat, "kCFDateFormatterDefaultFormat") + +CONST_STRING_DECL(kCFDateFormatterTwoDigitStartDate, "kCFDateFormatterTwoDigitStartDate") +CONST_STRING_DECL(kCFDateFormatterDefaultDate, "kCFDateFormatterDefaultDate") +CONST_STRING_DECL(kCFDateFormatterEraSymbols, "kCFDateFormatterEraSymbols") +CONST_STRING_DECL(kCFDateFormatterMonthSymbols, "kCFDateFormatterMonthSymbols") +CONST_STRING_DECL(kCFDateFormatterShortMonthSymbols, "kCFDateFormatterShortMonthSymbols") +CONST_STRING_DECL(kCFDateFormatterWeekdaySymbols, "kCFDateFormatterWeekdaySymbols") +CONST_STRING_DECL(kCFDateFormatterShortWeekdaySymbols, "kCFDateFormatterShortWeekdaySymbols") +CONST_STRING_DECL(kCFDateFormatterAMSymbol, "kCFDateFormatterAMSymbol") +CONST_STRING_DECL(kCFDateFormatterPMSymbol, "kCFDateFormatterPMSymbol") + +CONST_STRING_DECL(kCFDateFormatterLongEraSymbols, "kCFDateFormatterLongEraSymbols") +CONST_STRING_DECL(kCFDateFormatterVeryShortMonthSymbols, "kCFDateFormatterVeryShortMonthSymbols") +CONST_STRING_DECL(kCFDateFormatterStandaloneMonthSymbols, "kCFDateFormatterStandaloneMonthSymbols") +CONST_STRING_DECL(kCFDateFormatterShortStandaloneMonthSymbols, "kCFDateFormatterShortStandaloneMonthSymbols") +CONST_STRING_DECL(kCFDateFormatterVeryShortStandaloneMonthSymbols, "kCFDateFormatterVeryShortStandaloneMonthSymbols") +CONST_STRING_DECL(kCFDateFormatterVeryShortWeekdaySymbols, "kCFDateFormatterVeryShortWeekdaySymbols") +CONST_STRING_DECL(kCFDateFormatterStandaloneWeekdaySymbols, "kCFDateFormatterStandaloneWeekdaySymbols") +CONST_STRING_DECL(kCFDateFormatterShortStandaloneWeekdaySymbols, "kCFDateFormatterShortStandaloneWeekdaySymbols") +CONST_STRING_DECL(kCFDateFormatterVeryShortStandaloneWeekdaySymbols, "kCFDateFormatterVeryShortStandaloneWeekdaySymbols") +CONST_STRING_DECL(kCFDateFormatterQuarterSymbols, "kCFDateFormatterQuarterSymbols") +CONST_STRING_DECL(kCFDateFormatterShortQuarterSymbols, "kCFDateFormatterShortQuarterSymbols") +CONST_STRING_DECL(kCFDateFormatterStandaloneQuarterSymbols, "kCFDateFormatterStandaloneQuarterSymbols") +CONST_STRING_DECL(kCFDateFormatterShortStandaloneQuarterSymbols, "kCFDateFormatterShortStandaloneQuarterSymbols") +CONST_STRING_DECL(kCFDateFormatterGregorianStartDate, "kCFDateFormatterGregorianStartDate") + +#undef BUFFER_SIZE +
diff --git a/CoreFoundation/CFDateFormatter.h b/CoreFoundation/CFDateFormatter.h new file mode 100644 index 0000000..d9ae72c --- /dev/null +++ b/CoreFoundation/CFDateFormatter.h
@@ -0,0 +1,177 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFDateFormatter.h + Copyright (c) 2003-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFDATEFORMATTER__) +#define __COREFOUNDATION_CFDATEFORMATTER__ 1 + +#include <CoreFoundation/CFBase.h> +#include <CoreFoundation/CFDate.h> +#include <CoreFoundation/CFLocale.h> + +#if MAC_OS_X_VERSION_10_3 <= MAC_OS_X_VERSION_MAX_ALLOWED + +CF_EXTERN_C_BEGIN + +typedef struct __CFDateFormatter *CFDateFormatterRef; + +// CFDateFormatters are not thread-safe. Do not use one from multiple threads! + +CF_EXPORT +CFTypeID CFDateFormatterGetTypeID(void) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + +enum { // date and time format styles + kCFDateFormatterNoStyle = 0, + kCFDateFormatterShortStyle = 1, + kCFDateFormatterMediumStyle = 2, + kCFDateFormatterLongStyle = 3, + kCFDateFormatterFullStyle = 4 +}; +typedef CFIndex CFDateFormatterStyle; + +// The exact formatted result for these date and time styles depends on the +// locale, but generally: +// Short is completely numeric, such as "12/13/52" or "3:30pm" +// Medium is longer, such as "Jan 12, 1952" +// Long is longer, such as "January 12, 1952" or "3:30:32pm" +// Full is pretty complete; e.g. "Tuesday, April 12, 1952 AD" or "3:30:42pm PST" +// The specifications though are left fuzzy, in part simply because a user's +// preference choices may affect the output, and also the results may change +// from one OS release to another. To produce an exactly formatted date you +// should not rely on styles and localization, but set the format string and +// use nothing but numbers. + +CF_EXPORT +CFDateFormatterRef CFDateFormatterCreate(CFAllocatorRef allocator, CFLocaleRef locale, CFDateFormatterStyle dateStyle, CFDateFormatterStyle timeStyle) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + // Returns a CFDateFormatter, localized to the given locale, which + // will format dates to the given date and time styles. + +CF_EXPORT +CFLocaleRef CFDateFormatterGetLocale(CFDateFormatterRef formatter) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + +CF_EXPORT +CFDateFormatterStyle CFDateFormatterGetDateStyle(CFDateFormatterRef formatter) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + +CF_EXPORT +CFDateFormatterStyle CFDateFormatterGetTimeStyle(CFDateFormatterRef formatter) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + // Get the properties with which the date formatter was created. + +CF_EXPORT +CFStringRef CFDateFormatterGetFormat(CFDateFormatterRef formatter) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + +CF_EXPORT +void CFDateFormatterSetFormat(CFDateFormatterRef formatter, CFStringRef formatString) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + // Set the format description string of the date formatter. This + // overrides the style settings. The format of the format string + // is as defined by the ICU library. The date formatter starts with a + // default format string defined by the style arguments with + // which it was created. + + +CF_EXPORT +CFStringRef CFDateFormatterCreateStringWithDate(CFAllocatorRef allocator, CFDateFormatterRef formatter, CFDateRef date) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + +CF_EXPORT +CFStringRef CFDateFormatterCreateStringWithAbsoluteTime(CFAllocatorRef allocator, CFDateFormatterRef formatter, CFAbsoluteTime at) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + // Create a string representation of the given date or CFAbsoluteTime + // using the current state of the date formatter. + + +CF_EXPORT +CFDateRef CFDateFormatterCreateDateFromString(CFAllocatorRef allocator, CFDateFormatterRef formatter, CFStringRef string, CFRange *rangep) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + +CF_EXPORT +Boolean CFDateFormatterGetAbsoluteTimeFromString(CFDateFormatterRef formatter, CFStringRef string, CFRange *rangep, CFAbsoluteTime *atp) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + // Parse a string representation of a date using the current state + // of the date formatter. The range parameter specifies the range + // of the string in which the parsing should occur in input, and on + // output indicates the extent that was used; this parameter can + // be NULL, in which case the whole string may be used. The + // return value indicates whether some date was computed and + // (if atp is not NULL) stored at the location specified by atp. + + +CF_EXPORT +void CFDateFormatterSetProperty(CFDateFormatterRef formatter, CFStringRef key, CFTypeRef value) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + +CF_EXPORT +CFTypeRef CFDateFormatterCopyProperty(CFDateFormatterRef formatter, CFStringRef key) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + // Set and get various properties of the date formatter, the set of + // which may be expanded in the future. + +CF_EXPORT const CFStringRef kCFDateFormatterIsLenient AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFBoolean +CF_EXPORT const CFStringRef kCFDateFormatterTimeZone AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFTimeZone +CF_EXPORT const CFStringRef kCFDateFormatterCalendarName AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFString +CF_EXPORT const CFStringRef kCFDateFormatterDefaultFormat AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFString +CF_EXPORT const CFStringRef kCFDateFormatterTwoDigitStartDate AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; // CFDate +CF_EXPORT const CFStringRef kCFDateFormatterDefaultDate AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; // CFDate +CF_EXPORT const CFStringRef kCFDateFormatterCalendar AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; // CFCalendar +CF_EXPORT const CFStringRef kCFDateFormatterEraSymbols AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; // CFArray of CFString +CF_EXPORT const CFStringRef kCFDateFormatterMonthSymbols AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; // CFArray of CFString +CF_EXPORT const CFStringRef kCFDateFormatterShortMonthSymbols AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; // CFArray of CFString +CF_EXPORT const CFStringRef kCFDateFormatterWeekdaySymbols AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; // CFArray of CFString +CF_EXPORT const CFStringRef kCFDateFormatterShortWeekdaySymbols AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; // CFArray of CFString +CF_EXPORT const CFStringRef kCFDateFormatterAMSymbol AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; // CFString +CF_EXPORT const CFStringRef kCFDateFormatterPMSymbol AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; // CFString +CF_EXPORT const CFStringRef kCFDateFormatterLongEraSymbols AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; // CFArray of CFString +CF_EXPORT const CFStringRef kCFDateFormatterVeryShortMonthSymbols AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; // CFArray of CFString +CF_EXPORT const CFStringRef kCFDateFormatterStandaloneMonthSymbols AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; // CFArray of CFString +CF_EXPORT const CFStringRef kCFDateFormatterShortStandaloneMonthSymbols AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; // CFArray of CFString +CF_EXPORT const CFStringRef kCFDateFormatterVeryShortStandaloneMonthSymbols AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; // CFArray of CFString +CF_EXPORT const CFStringRef kCFDateFormatterVeryShortWeekdaySymbols AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; // CFArray of CFString +CF_EXPORT const CFStringRef kCFDateFormatterStandaloneWeekdaySymbols AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; // CFArray of CFString +CF_EXPORT const CFStringRef kCFDateFormatterShortStandaloneWeekdaySymbols AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; // CFArray of CFString +CF_EXPORT const CFStringRef kCFDateFormatterVeryShortStandaloneWeekdaySymbols AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; // CFArray of CFString +CF_EXPORT const CFStringRef kCFDateFormatterQuarterSymbols AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; // CFArray of CFString +CF_EXPORT const CFStringRef kCFDateFormatterShortQuarterSymbols AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; // CFArray of CFString +CF_EXPORT const CFStringRef kCFDateFormatterStandaloneQuarterSymbols AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; // CFArray of CFString +CF_EXPORT const CFStringRef kCFDateFormatterShortStandaloneQuarterSymbols AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; // CFArray of CFString +CF_EXPORT const CFStringRef kCFDateFormatterGregorianStartDate AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; // CFDate + +// See CFLocale.h for these calendar constants: +// const CFStringRef kCFGregorianCalendar; +// const CFStringRef kCFBuddhistCalendar; +// const CFStringRef kCFJapaneseCalendar; +// const CFStringRef kCFIslamicCalendar; +// const CFStringRef kCFIslamicCivilCalendar; +// const CFStringRef kCFHebrewCalendar; +// const CFStringRef kCFChineseCalendar; + +CF_EXTERN_C_END + +#endif + +#endif /* ! __COREFOUNDATION_CFDATEFORMATTER__ */ +
diff --git a/CoreFoundation/CFDictionary.c b/CoreFoundation/CFDictionary.c new file mode 100644 index 0000000..daaf6d7 --- /dev/null +++ b/CoreFoundation/CFDictionary.c
@@ -0,0 +1,1483 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFDictionary.c + Copyright 1998-2006, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane + Machine generated from Notes/HashingCode.template +*/ + + + + +#include <CoreFoundation/CFDictionary.h> +#include "CFInternal.h" +#if DEPLOYMENT_TARGET_MACOSX +#include <mach-o/dyld.h> +#endif + +#define CFDictionary 0 +#define CFSet 0 +#define CFBag 0 +#undef CFDictionary +#define CFDictionary 1 + +#if CFDictionary +const CFDictionaryKeyCallBacks kCFTypeDictionaryKeyCallBacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; +const CFDictionaryKeyCallBacks kCFCopyStringDictionaryKeyCallBacks = {0, __CFStringCollectionCopy, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; +const CFDictionaryValueCallBacks kCFTypeDictionaryValueCallBacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, CFCopyDescription, CFEqual}; +static const CFDictionaryKeyCallBacks __kCFNullDictionaryKeyCallBacks = {0, NULL, NULL, NULL, NULL, NULL}; +static const CFDictionaryValueCallBacks __kCFNullDictionaryValueCallBacks = {0, NULL, NULL, NULL, NULL}; + +#define CFHashRef CFDictionaryRef +#define CFMutableHashRef CFMutableDictionaryRef +#define __kCFHashTypeID __kCFDictionaryTypeID +#endif + +#if CFSet +const CFDictionaryCallBacks kCFTypeDictionaryCallBacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; +const CFDictionaryCallBacks kCFCopyStringDictionaryCallBacks = {0, __CFStringCollectionCopy, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; +static const CFDictionaryCallBacks __kCFNullDictionaryCallBacks = {0, NULL, NULL, NULL, NULL, NULL}; + +#define CFDictionaryKeyCallBacks CFDictionaryCallBacks +#define CFDictionaryValueCallBacks CFDictionaryCallBacks +#define kCFTypeDictionaryKeyCallBacks kCFTypeDictionaryCallBacks +#define kCFTypeDictionaryValueCallBacks kCFTypeDictionaryCallBacks +#define __kCFNullDictionaryKeyCallBacks __kCFNullDictionaryCallBacks +#define __kCFNullDictionaryValueCallBacks __kCFNullDictionaryCallBacks + +#define CFHashRef CFSetRef +#define CFMutableHashRef CFMutableSetRef +#define __kCFHashTypeID __kCFSetTypeID +#endif + +#if CFBag +const CFDictionaryCallBacks kCFTypeDictionaryCallBacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; +const CFDictionaryCallBacks kCFCopyStringDictionaryCallBacks = {0, __CFStringCollectionCopy, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; +static const CFDictionaryCallBacks __kCFNullDictionaryCallBacks = {0, NULL, NULL, NULL, NULL, NULL}; + +#define CFDictionaryKeyCallBacks CFDictionaryCallBacks +#define CFDictionaryValueCallBacks CFDictionaryCallBacks +#define kCFTypeDictionaryKeyCallBacks kCFTypeDictionaryCallBacks +#define kCFTypeDictionaryValueCallBacks kCFTypeDictionaryCallBacks +#define __kCFNullDictionaryKeyCallBacks __kCFNullDictionaryCallBacks +#define __kCFNullDictionaryValueCallBacks __kCFNullDictionaryCallBacks + +#define CFHashRef CFBagRef +#define CFMutableHashRef CFMutableBagRef +#define __kCFHashTypeID __kCFBagTypeID +#endif + +#define GETNEWKEY(newKey, oldKey) \ + any_t (*kretain)(CFAllocatorRef, any_t, any_pointer_t) = \ + !hasBeenFinalized(hc) \ + ? (any_t (*)(CFAllocatorRef,any_t,any_pointer_t))__CFDictionaryGetKeyCallBacks(hc)->retain \ + : (any_t (*)(CFAllocatorRef,any_t,any_pointer_t))0; \ + any_t newKey = kretain ? (any_t)INVOKE_CALLBACK3(kretain, allocator, (any_t)key, hc->_context) : (any_t)oldKey + +#define RELEASEKEY(oldKey) \ + void (*krelease)(CFAllocatorRef, any_t, any_pointer_t) = \ + !hasBeenFinalized(hc) \ + ? (void (*)(CFAllocatorRef,any_t,any_pointer_t))__CFDictionaryGetKeyCallBacks(hc)->release \ + : (void (*)(CFAllocatorRef,any_t,any_pointer_t))0; \ + if (krelease) INVOKE_CALLBACK3(krelease, allocator, oldKey, hc->_context) + +#if CFDictionary +#define GETNEWVALUE(newValue) \ + any_t (*vretain)(CFAllocatorRef, any_t, any_pointer_t) = \ + !hasBeenFinalized(hc) \ + ? (any_t (*)(CFAllocatorRef,any_t,any_pointer_t))__CFDictionaryGetValueCallBacks(hc)->retain \ + : (any_t (*)(CFAllocatorRef,any_t,any_pointer_t))0; \ + any_t newValue = vretain ? (any_t)INVOKE_CALLBACK3(vretain, allocator, (any_t)value, hc->_context) : (any_t)value + +#define RELEASEVALUE(oldValue) \ + void (*vrelease)(CFAllocatorRef, any_t, any_pointer_t) = \ + !hasBeenFinalized(hc) \ + ? (void (*)(CFAllocatorRef,any_t,any_pointer_t))__CFDictionaryGetValueCallBacks(hc)->release \ + : (void (*)(CFAllocatorRef,any_t,any_pointer_t))0; \ + if (vrelease) INVOKE_CALLBACK3(vrelease, allocator, oldValue, hc->_context) + +#endif + +static void __CFDictionaryHandleOutOfMemory(CFTypeRef obj, CFIndex numBytes) { + CFStringRef msg = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("Attempt to allocate %ld bytes for NS/CFDictionary failed"), numBytes); + CFBadErrorCallBack cb = _CFGetOutOfMemoryErrorCallBack(); + if (NULL == cb || !cb(obj, CFSTR("NS/CFDictionary"), msg)) { + CFLog(kCFLogLevelCritical, CFSTR("%@"), msg); + HALT; + } + CFRelease(msg); +} + + +// Max load is 3/4 number of buckets +CF_INLINE CFIndex __CFHashRoundUpCapacity(CFIndex capacity) { + return 3 * ((CFIndex)1 << (flsl((capacity - 1) / 3))); +} + +// Returns next power of two higher than the capacity +// threshold for the given input capacity. +CF_INLINE CFIndex __CFHashNumBucketsForCapacity(CFIndex capacity) { + return 4 * ((CFIndex)1 << (flsl((capacity - 1) / 3))); +} + +enum { /* Bits 1-0 */ + __kCFHashImmutable = 0, /* unchangable and fixed capacity */ + __kCFHashMutable = 1, /* changeable and variable capacity */ +}; + +enum { /* Bits 5-4 (value), 3-2 (key) */ + __kCFHashHasNullCallBacks = 0, + __kCFHashHasCFTypeCallBacks = 1, + __kCFHashHasCustomCallBacks = 3 /* callbacks are at end of header */ +}; + +// Under GC, we fudge the key/value memory in two ways +// First, if we had null callbacks or null for both retain/release, we use unscanned memory and get +// standard 'dangling' references. +// This means that if people were doing addValue:[xxx new] and never removing, well, that doesn't work +// +// Second, if we notice standard retain/release implementations we use scanned memory, and fudge the +// standard callbacks to generally do nothing if the collection was allocated in GC memory. On special +// CF objects, however, like those used for precious resources like video-card buffers, we do indeed +// do CFRetain on input and CFRelease on output. The tricky case is GC finalization; we need to remember +// that we did the CFReleases so that subsequent collection operations, like removal, don't double CFRelease. +// (In fact we don't really use CFRetain/CFRelease but go directly to the collector) +// + +enum { + __kCFHashFinalized = (1 << 7), + __kCFHashWeakKeys = (1 << 8), + __kCFHashWeakValues = (1 << 9) +}; + +typedef uintptr_t any_t; +typedef const void * const_any_pointer_t; +typedef void * any_pointer_t; + +struct __CFDictionary { + CFRuntimeBase _base; + CFIndex _count; /* number of values */ + CFIndex _bucketsNum; /* number of buckets */ + CFIndex _bucketsUsed; /* number of used buckets */ + CFIndex _bucketsCap; /* maximum number of used buckets */ + CFIndex _mutations; + CFIndex _deletes; + any_pointer_t _context; /* private */ + CFOptionFlags _xflags; + any_t _marker; + any_t *_keys; /* can be NULL if not allocated yet */ + any_t *_values; /* can be NULL if not allocated yet */ +}; + +/* Bits 1-0 of the _xflags are used for mutability variety */ +/* Bits 3-2 of the _xflags are used for key callback indicator bits */ +/* Bits 5-4 of the _xflags are used for value callback indicator bits */ +/* Bit 6 of the _xflags is special KVO actions bit */ +/* Bits 7,8,9 are GC use */ + +CF_INLINE bool hasBeenFinalized(CFTypeRef collection) { + return __CFBitfieldGetValue(((const struct __CFDictionary *)collection)->_xflags, 7, 7) != 0; +} + +CF_INLINE void markFinalized(CFTypeRef collection) { + __CFBitfieldSetValue(((struct __CFDictionary *)collection)->_xflags, 7, 7, 1); +} + + +CF_INLINE CFIndex __CFHashGetType(CFHashRef hc) { + return __CFBitfieldGetValue(hc->_xflags, 1, 0); +} + +CF_INLINE CFIndex __CFDictionaryGetSizeOfType(CFIndex t) { + CFIndex size = sizeof(struct __CFDictionary); + if (__CFBitfieldGetValue(t, 3, 2) == __kCFHashHasCustomCallBacks) { + size += sizeof(CFDictionaryKeyCallBacks); + } + if (__CFBitfieldGetValue(t, 5, 4) == __kCFHashHasCustomCallBacks) { + size += sizeof(CFDictionaryValueCallBacks); + } + return size; +} + +CF_INLINE const CFDictionaryKeyCallBacks *__CFDictionaryGetKeyCallBacks(CFHashRef hc) { + CFDictionaryKeyCallBacks *result = NULL; + switch (__CFBitfieldGetValue(hc->_xflags, 3, 2)) { + case __kCFHashHasNullCallBacks: + return &__kCFNullDictionaryKeyCallBacks; + case __kCFHashHasCFTypeCallBacks: + return &kCFTypeDictionaryKeyCallBacks; + case __kCFHashHasCustomCallBacks: + break; + } + result = (CFDictionaryKeyCallBacks *)((uint8_t *)hc + sizeof(struct __CFDictionary)); + return result; +} + +CF_INLINE Boolean __CFDictionaryKeyCallBacksMatchNull(const CFDictionaryKeyCallBacks *c) { + return (NULL == c || + (c->retain == __kCFNullDictionaryKeyCallBacks.retain && + c->release == __kCFNullDictionaryKeyCallBacks.release && + c->copyDescription == __kCFNullDictionaryKeyCallBacks.copyDescription && + c->equal == __kCFNullDictionaryKeyCallBacks.equal && + c->hash == __kCFNullDictionaryKeyCallBacks.hash)); +} + +CF_INLINE Boolean __CFDictionaryKeyCallBacksMatchCFType(const CFDictionaryKeyCallBacks *c) { + return (&kCFTypeDictionaryKeyCallBacks == c || + (c->retain == kCFTypeDictionaryKeyCallBacks.retain && + c->release == kCFTypeDictionaryKeyCallBacks.release && + c->copyDescription == kCFTypeDictionaryKeyCallBacks.copyDescription && + c->equal == kCFTypeDictionaryKeyCallBacks.equal && + c->hash == kCFTypeDictionaryKeyCallBacks.hash)); +} + +CF_INLINE const CFDictionaryValueCallBacks *__CFDictionaryGetValueCallBacks(CFHashRef hc) { + CFDictionaryValueCallBacks *result = NULL; + switch (__CFBitfieldGetValue(hc->_xflags, 5, 4)) { + case __kCFHashHasNullCallBacks: + return &__kCFNullDictionaryValueCallBacks; + case __kCFHashHasCFTypeCallBacks: + return &kCFTypeDictionaryValueCallBacks; + case __kCFHashHasCustomCallBacks: + break; + } + if (__CFBitfieldGetValue(hc->_xflags, 3, 2) == __kCFHashHasCustomCallBacks) { + result = (CFDictionaryValueCallBacks *)((uint8_t *)hc + sizeof(struct __CFDictionary) + sizeof(CFDictionaryKeyCallBacks)); + } else { + result = (CFDictionaryValueCallBacks *)((uint8_t *)hc + sizeof(struct __CFDictionary)); + } + return result; +} + +CF_INLINE Boolean __CFDictionaryValueCallBacksMatchNull(const CFDictionaryValueCallBacks *c) { + return (NULL == c || + (c->retain == __kCFNullDictionaryValueCallBacks.retain && + c->release == __kCFNullDictionaryValueCallBacks.release && + c->copyDescription == __kCFNullDictionaryValueCallBacks.copyDescription && + c->equal == __kCFNullDictionaryValueCallBacks.equal)); +} + +CF_INLINE Boolean __CFDictionaryValueCallBacksMatchCFType(const CFDictionaryValueCallBacks *c) { + return (&kCFTypeDictionaryValueCallBacks == c || + (c->retain == kCFTypeDictionaryValueCallBacks.retain && + c->release == kCFTypeDictionaryValueCallBacks.release && + c->copyDescription == kCFTypeDictionaryValueCallBacks.copyDescription && + c->equal == kCFTypeDictionaryValueCallBacks.equal)); +} + +CFIndex _CFDictionaryGetKVOBit(CFHashRef hc) { + return __CFBitfieldGetValue(hc->_xflags, 6, 6); +} + +void _CFDictionarySetKVOBit(CFHashRef hc, CFIndex bit) { + __CFBitfieldSetValue(((CFMutableHashRef)hc)->_xflags, 6, 6, ((uintptr_t)bit & 0x1)); +} + +CF_INLINE Boolean __CFDictionaryShouldShrink(CFHashRef hc) { + return (__kCFHashMutable == __CFHashGetType(hc)) && + !(CF_USING_COLLECTABLE_MEMORY && auto_zone_is_finalized(__CFCollectableZone, hc)) && /* GC: don't shrink finalizing hcs! */ + (hc->_bucketsNum < 4 * hc->_deletes || (256 <= hc->_bucketsCap && hc-> _bucketsUsed < 3 * hc->_bucketsCap / 16)); +} + +CF_INLINE CFIndex __CFHashGetOccurrenceCount(CFHashRef hc, CFIndex idx) { +#if CFBag + return hc->_values[idx]; +#endif + return 1; +} + +CF_INLINE Boolean __CFHashKeyIsValue(CFHashRef hc, any_t key) { + return (hc->_marker != key && ~hc->_marker != key) ? true : false; +} + +CF_INLINE Boolean __CFHashKeyIsMagic(CFHashRef hc, any_t key) { + return (hc->_marker == key || ~hc->_marker == key) ? true : false; +} + + +#if !defined(CF_OBJC_KVO_WILLCHANGE) +#define CF_OBJC_KVO_WILLCHANGE(obj, key) +#define CF_OBJC_KVO_DIDCHANGE(obj, key) +#endif + +CF_INLINE uintptr_t __CFDictionaryScrambleHash(uintptr_t k) { +#if 0 + return k; +#else +#if __LP64__ + uintptr_t a = 0x4368726973746F70ULL; + uintptr_t b = 0x686572204B616E65ULL; +#else + uintptr_t a = 0x4B616E65UL; + uintptr_t b = 0x4B616E65UL; +#endif + uintptr_t c = 1; + a += k; +#if __LP64__ + a -= b; a -= c; a ^= (c >> 43); + b -= c; b -= a; b ^= (a << 9); + c -= a; c -= b; c ^= (b >> 8); + a -= b; a -= c; a ^= (c >> 38); + b -= c; b -= a; b ^= (a << 23); + c -= a; c -= b; c ^= (b >> 5); + a -= b; a -= c; a ^= (c >> 35); + b -= c; b -= a; b ^= (a << 49); + c -= a; c -= b; c ^= (b >> 11); + a -= b; a -= c; a ^= (c >> 12); + b -= c; b -= a; b ^= (a << 18); + c -= a; c -= b; c ^= (b >> 22); +#else + a -= b; a -= c; a ^= (c >> 13); + b -= c; b -= a; b ^= (a << 8); + c -= a; c -= b; c ^= (b >> 13); + a -= b; a -= c; a ^= (c >> 12); + b -= c; b -= a; b ^= (a << 16); + c -= a; c -= b; c ^= (b >> 5); + a -= b; a -= c; a ^= (c >> 3); + b -= c; b -= a; b ^= (a << 10); + c -= a; c -= b; c ^= (b >> 15); +#endif + return c; +#endif +} + +static CFIndex __CFDictionaryFindBuckets1a(CFHashRef hc, any_t key) { + CFHashCode keyHash = (CFHashCode)key; + keyHash = (CFHashCode)__CFDictionaryScrambleHash(keyHash); + any_t *keys = hc->_keys; + any_t marker = hc->_marker; + CFIndex probe = keyHash & (hc->_bucketsNum - 1); + CFIndex probeskip = 1; // See RemoveValue() for notes before changing this value + CFIndex start = probe; + for (;;) { + any_t currKey = keys[probe]; + if (marker == currKey) { /* empty */ + return kCFNotFound; + } else if (~marker == currKey) { /* deleted */ + /* do nothing */ + } else if (currKey == key) { + return probe; + } + probe = probe + probeskip; + // This alternative to probe % buckets assumes that + // probeskip is always positive and less than the + // number of buckets. + if (hc->_bucketsNum <= probe) { + probe -= hc->_bucketsNum; + } + if (start == probe) { + return kCFNotFound; + } + } +} + +static CFIndex __CFDictionaryFindBuckets1b(CFHashRef hc, any_t key) { + const CFDictionaryKeyCallBacks *cb = __CFDictionaryGetKeyCallBacks(hc); + CFHashCode keyHash = cb->hash ? (CFHashCode)INVOKE_CALLBACK2(((CFHashCode (*)(any_t, any_pointer_t))cb->hash), key, hc->_context) : (CFHashCode)key; + keyHash = (CFHashCode)__CFDictionaryScrambleHash(keyHash); + any_t *keys = hc->_keys; + any_t marker = hc->_marker; + CFIndex probe = keyHash & (hc->_bucketsNum - 1); + CFIndex probeskip = 1; // See RemoveValue() for notes before changing this value + CFIndex start = probe; + for (;;) { + any_t currKey = keys[probe]; + if (marker == currKey) { /* empty */ + return kCFNotFound; + } else if (~marker == currKey) { /* deleted */ + /* do nothing */ + } else if (currKey == key || (cb->equal && INVOKE_CALLBACK3((Boolean (*)(any_t, any_t, any_pointer_t))cb->equal, currKey, key, hc->_context))) { + return probe; + } + probe = probe + probeskip; + // This alternative to probe % buckets assumes that + // probeskip is always positive and less than the + // number of buckets. + if (hc->_bucketsNum <= probe) { + probe -= hc->_bucketsNum; + } + if (start == probe) { + return kCFNotFound; + } + } +} + +CF_INLINE CFIndex __CFDictionaryFindBuckets1(CFHashRef hc, any_t key) { + if (__kCFHashHasNullCallBacks == __CFBitfieldGetValue(hc->_xflags, 3, 2)) { + return __CFDictionaryFindBuckets1a(hc, key); + } + return __CFDictionaryFindBuckets1b(hc, key); +} + +static void __CFDictionaryFindBuckets2(CFHashRef hc, any_t key, CFIndex *match, CFIndex *nomatch) { + const CFDictionaryKeyCallBacks *cb = __CFDictionaryGetKeyCallBacks(hc); + CFHashCode keyHash = cb->hash ? (CFHashCode)INVOKE_CALLBACK2(((CFHashCode (*)(any_t, any_pointer_t))cb->hash), key, hc->_context) : (CFHashCode)key; + keyHash = (CFHashCode)__CFDictionaryScrambleHash(keyHash); + any_t *keys = hc->_keys; + any_t marker = hc->_marker; + CFIndex probe = keyHash & (hc->_bucketsNum - 1); + CFIndex probeskip = 1; // See RemoveValue() for notes before changing this value + CFIndex start = probe; + *match = kCFNotFound; + *nomatch = kCFNotFound; + for (;;) { + any_t currKey = keys[probe]; + if (marker == currKey) { /* empty */ + if (nomatch) *nomatch = probe; + return; + } else if (~marker == currKey) { /* deleted */ + if (nomatch) { + *nomatch = probe; + nomatch = NULL; + } + } else if (currKey == key || (cb->equal && INVOKE_CALLBACK3((Boolean (*)(any_t, any_t, any_pointer_t))cb->equal, currKey, key, hc->_context))) { + *match = probe; + return; + } + probe = probe + probeskip; + // This alternative to probe % buckets assumes that + // probeskip is always positive and less than the + // number of buckets. + if (hc->_bucketsNum <= probe) { + probe -= hc->_bucketsNum; + } + if (start == probe) { + return; + } + } +} + +static void __CFDictionaryFindNewMarker(CFHashRef hc) { + any_t *keys = hc->_keys; + any_t newMarker; + CFIndex idx, nbuckets; + Boolean hit; + + nbuckets = hc->_bucketsNum; + newMarker = hc->_marker; + do { + newMarker--; + hit = false; + for (idx = 0; idx < nbuckets; idx++) { + if (newMarker == keys[idx] || ~newMarker == keys[idx]) { + hit = true; + break; + } + } + } while (hit); + for (idx = 0; idx < nbuckets; idx++) { + if (hc->_marker == keys[idx]) { + keys[idx] = newMarker; + } else if (~hc->_marker == keys[idx]) { + keys[idx] = ~newMarker; + } + } + ((struct __CFDictionary *)hc)->_marker = newMarker; +} + +static Boolean __CFDictionaryEqual(CFTypeRef cf1, CFTypeRef cf2) { + CFHashRef hc1 = (CFHashRef)cf1; + CFHashRef hc2 = (CFHashRef)cf2; + const CFDictionaryKeyCallBacks *cb1, *cb2; + const CFDictionaryValueCallBacks *vcb1, *vcb2; + any_t *keys; + CFIndex idx, nbuckets; + if (hc1 == hc2) return true; + if (hc1->_count != hc2->_count) return false; + cb1 = __CFDictionaryGetKeyCallBacks(hc1); + cb2 = __CFDictionaryGetKeyCallBacks(hc2); + if (cb1->equal != cb2->equal) return false; + vcb1 = __CFDictionaryGetValueCallBacks(hc1); + vcb2 = __CFDictionaryGetValueCallBacks(hc2); + if (vcb1->equal != vcb2->equal) return false; + if (0 == hc1->_bucketsUsed) return true; /* after function comparison! */ + keys = hc1->_keys; + nbuckets = hc1->_bucketsNum; + for (idx = 0; idx < nbuckets; idx++) { + if (hc1->_marker != keys[idx] && ~hc1->_marker != keys[idx]) { +#if CFDictionary + const_any_pointer_t value; + if (!CFDictionaryGetValueIfPresent(hc2, (any_pointer_t)keys[idx], &value)) return false; + if (hc1->_values[idx] != (any_t)value) { + if (NULL == vcb1->equal) return false; + if (!INVOKE_CALLBACK3((Boolean (*)(any_t, any_t, any_pointer_t))vcb1->equal, hc1->_values[idx], (any_t)value, hc1->_context)) return false; + } +#endif +#if CFSet + const_any_pointer_t value; + if (!CFDictionaryGetValueIfPresent(hc2, (any_pointer_t)keys[idx], &value)) return false; +#endif +#if CFBag + if (hc1->_values[idx] != CFDictionaryGetCountOfValue(hc2, (any_pointer_t)keys[idx])) return false; +#endif + } + } + return true; +} + +static CFHashCode __CFDictionaryHash(CFTypeRef cf) { + CFHashRef hc = (CFHashRef)cf; + return hc->_count; +} + +static CFStringRef __CFDictionaryCopyDescription(CFTypeRef cf) { + CFHashRef hc = (CFHashRef)cf; + CFAllocatorRef allocator; + const CFDictionaryKeyCallBacks *cb; + const CFDictionaryValueCallBacks *vcb; + any_t *keys; + CFIndex idx, nbuckets; + CFMutableStringRef result; + cb = __CFDictionaryGetKeyCallBacks(hc); + vcb = __CFDictionaryGetValueCallBacks(hc); + keys = hc->_keys; + nbuckets = hc->_bucketsNum; + allocator = CFGetAllocator(hc); + result = CFStringCreateMutable(allocator, 0); + const char *type = "?"; + switch (__CFHashGetType(hc)) { + case __kCFHashImmutable: type = "immutable"; break; + case __kCFHashMutable: type = "mutable"; break; + } + CFStringAppendFormat(result, NULL, CFSTR("<CFDictionary %p [%p]>{type = %s, count = %u, capacity = %u, pairs = (\n"), cf, allocator, type, hc->_count, hc->_bucketsCap); + for (idx = 0; idx < nbuckets; idx++) { + if (__CFHashKeyIsValue(hc, keys[idx])) { + CFStringRef kDesc = NULL, vDesc = NULL; + if (NULL != cb->copyDescription) { + kDesc = (CFStringRef)INVOKE_CALLBACK2(((CFStringRef (*)(any_t, any_pointer_t))cb->copyDescription), keys[idx], hc->_context); + } + if (NULL != vcb->copyDescription) { + vDesc = (CFStringRef)INVOKE_CALLBACK2(((CFStringRef (*)(any_t, any_pointer_t))vcb->copyDescription), hc->_values[idx], hc->_context); + } +#if CFDictionary + if (NULL != kDesc && NULL != vDesc) { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : %@ = %@\n"), idx, kDesc, vDesc); + CFRelease(kDesc); + CFRelease(vDesc); + } else if (NULL != kDesc) { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : %@ = <%p>\n"), idx, kDesc, hc->_values[idx]); + CFRelease(kDesc); + } else if (NULL != vDesc) { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : <%p> = %@\n"), idx, keys[idx], vDesc); + CFRelease(vDesc); + } else { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : <%p> = <%p>\n"), idx, keys[idx], hc->_values[idx]); + } +#endif +#if CFSet + if (NULL != kDesc) { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : %@\n"), idx, kDesc); + CFRelease(kDesc); + } else { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : <%p>\n"), idx, keys[idx]); + } +#endif +#if CFBag + if (NULL != kDesc) { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : %@ (%ld)\n"), idx, kDesc, hc->_values[idx]); + CFRelease(kDesc); + } else { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : <%p> (%ld)\n"), idx, keys[idx], hc->_values[idx]); + } +#endif + } + } + CFStringAppend(result, CFSTR(")}")); + return result; +} + +static void __CFDictionaryDeallocate(CFTypeRef cf) { + CFMutableHashRef hc = (CFMutableHashRef)cf; + CFAllocatorRef allocator = __CFGetAllocator(hc); + const CFDictionaryKeyCallBacks *cb = __CFDictionaryGetKeyCallBacks(hc); + const CFDictionaryValueCallBacks *vcb = __CFDictionaryGetValueCallBacks(hc); + + // mark now in case any callout somehow tries to add an entry back in + markFinalized(cf); + if (vcb->release || cb->release) { + any_t *keys = hc->_keys; + CFIndex idx, nbuckets = hc->_bucketsNum; + for (idx = 0; idx < nbuckets; idx++) { + any_t oldkey = keys[idx]; + if (hc->_marker != oldkey && ~hc->_marker != oldkey) { + if (vcb->release) { + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, any_t, any_pointer_t))vcb->release), allocator, hc->_values[idx], hc->_context); + } + if (cb->release) { + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, any_t, any_pointer_t))cb->release), allocator, oldkey, hc->_context); + } + } + } + } + + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + // return early so that contents are preserved after finalization + return; + } + + _CFAllocatorDeallocateGC(allocator, hc->_keys); +#if CFDictionary || CFBag + _CFAllocatorDeallocateGC(allocator, hc->_values); +#endif + hc->_keys = NULL; + hc->_values = NULL; + hc->_count = 0; // GC: also zero count, so the hc will appear empty. + hc->_bucketsUsed = 0; + hc->_bucketsNum = 0; +} + +static CFTypeID __kCFDictionaryTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFDictionaryClass = { + _kCFRuntimeScannedObject, + "CFDictionary", + NULL, // init + NULL, // copy + __CFDictionaryDeallocate, + __CFDictionaryEqual, + __CFDictionaryHash, + NULL, // + __CFDictionaryCopyDescription +}; + +__private_extern__ void __CFDictionaryInitialize(void) { + __kCFHashTypeID = _CFRuntimeRegisterClass(&__CFDictionaryClass); +} + +CFTypeID CFDictionaryGetTypeID(void) { + return __kCFHashTypeID; +} + +static CFMutableHashRef __CFDictionaryInit(CFAllocatorRef allocator, CFOptionFlags flags, CFIndex capacity, const CFDictionaryKeyCallBacks *keyCallBacks +#if CFDictionary +, const CFDictionaryValueCallBacks *valueCallBacks +#endif +) { + struct __CFDictionary *hc; + CFIndex size; + __CFBitfieldSetValue(flags, 31, 2, 0); + CFOptionFlags xflags = 0; + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + // preserve NULL for key or value CB, otherwise fix up. + if (!keyCallBacks || (keyCallBacks->retain == NULL && keyCallBacks->release == NULL)) { + xflags = __kCFHashWeakKeys; + } +#if CFDictionary + if (!valueCallBacks || (valueCallBacks->retain == NULL && valueCallBacks->release == NULL)) { + xflags |= __kCFHashWeakValues; + } +#endif +#if CFBag + xflags |= __kCFHashWeakValues; +#endif + } + if (__CFDictionaryKeyCallBacksMatchNull(keyCallBacks)) { + __CFBitfieldSetValue(flags, 3, 2, __kCFHashHasNullCallBacks); + } else if (__CFDictionaryKeyCallBacksMatchCFType(keyCallBacks)) { + __CFBitfieldSetValue(flags, 3, 2, __kCFHashHasCFTypeCallBacks); + } else { + __CFBitfieldSetValue(flags, 3, 2, __kCFHashHasCustomCallBacks); + } +#if CFDictionary + if (__CFDictionaryValueCallBacksMatchNull(valueCallBacks)) { + __CFBitfieldSetValue(flags, 5, 4, __kCFHashHasNullCallBacks); + } else if (__CFDictionaryValueCallBacksMatchCFType(valueCallBacks)) { + __CFBitfieldSetValue(flags, 5, 4, __kCFHashHasCFTypeCallBacks); + } else { + __CFBitfieldSetValue(flags, 5, 4, __kCFHashHasCustomCallBacks); + } +#endif + size = __CFDictionaryGetSizeOfType(flags) - sizeof(CFRuntimeBase); + hc = (struct __CFDictionary *)_CFRuntimeCreateInstance(allocator, __kCFHashTypeID, size, NULL); + if (NULL == hc) { + return NULL; + } + hc->_count = 0; + hc->_bucketsUsed = 0; + hc->_marker = (any_t)0xa1b1c1d3; + hc->_context = NULL; + hc->_deletes = 0; + hc->_mutations = 1; + hc->_xflags = xflags | flags; + switch (__CFBitfieldGetValue(flags, 1, 0)) { + case __kCFHashImmutable: + if (__CFOASafe) __CFSetLastAllocationEventName(hc, "CFDictionary (immutable)"); + break; + case __kCFHashMutable: + if (__CFOASafe) __CFSetLastAllocationEventName(hc, "CFDictionary (mutable-variable)"); + break; + } + hc->_bucketsCap = __CFHashRoundUpCapacity(1); + hc->_bucketsNum = 0; + hc->_keys = NULL; + hc->_values = NULL; + if (__kCFHashHasCustomCallBacks == __CFBitfieldGetValue(flags, 3, 2)) { + CFDictionaryKeyCallBacks *cb = (CFDictionaryKeyCallBacks *)__CFDictionaryGetKeyCallBacks((CFHashRef)hc); + *cb = *keyCallBacks; + FAULT_CALLBACK((void **)&(cb->retain)); + FAULT_CALLBACK((void **)&(cb->release)); + FAULT_CALLBACK((void **)&(cb->copyDescription)); + FAULT_CALLBACK((void **)&(cb->equal)); + FAULT_CALLBACK((void **)&(cb->hash)); + } +#if CFDictionary + if (__kCFHashHasCustomCallBacks == __CFBitfieldGetValue(flags, 5, 4)) { + CFDictionaryValueCallBacks *vcb = (CFDictionaryValueCallBacks *)__CFDictionaryGetValueCallBacks((CFHashRef)hc); + *vcb = *valueCallBacks; + FAULT_CALLBACK((void **)&(vcb->retain)); + FAULT_CALLBACK((void **)&(vcb->release)); + FAULT_CALLBACK((void **)&(vcb->copyDescription)); + FAULT_CALLBACK((void **)&(vcb->equal)); + } +#endif + return hc; +} + +#if CFDictionary +CFHashRef CFDictionaryCreate(CFAllocatorRef allocator, const_any_pointer_t *keys, const_any_pointer_t *values, CFIndex numValues, const CFDictionaryKeyCallBacks *keyCallBacks, const CFDictionaryValueCallBacks *valueCallBacks) { +#endif +#if CFSet || CFBag +CFHashRef CFDictionaryCreate(CFAllocatorRef allocator, const_any_pointer_t *keys, CFIndex numValues, const CFDictionaryKeyCallBacks *keyCallBacks) { +#endif + CFAssert2(0 <= numValues, __kCFLogAssertion, "%s(): numValues (%ld) cannot be less than zero", __PRETTY_FUNCTION__, numValues); +#if CFDictionary + CFMutableHashRef hc = __CFDictionaryInit(allocator, __kCFHashImmutable, numValues, keyCallBacks, valueCallBacks); +#endif +#if CFSet || CFBag + CFMutableHashRef hc = __CFDictionaryInit(allocator, __kCFHashImmutable, numValues, keyCallBacks); +#endif + __CFBitfieldSetValue(hc->_xflags, 1, 0, __kCFHashMutable); + for (CFIndex idx = 0; idx < numValues; idx++) { +#if CFDictionary + CFDictionaryAddValue(hc, keys[idx], values[idx]); +#endif +#if CFSet || CFBag + CFDictionaryAddValue(hc, keys[idx]); +#endif + } + __CFBitfieldSetValue(hc->_xflags, 1, 0, __kCFHashImmutable); + return (CFHashRef)hc; +} + +#if CFDictionary +CFMutableHashRef CFDictionaryCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFDictionaryKeyCallBacks *keyCallBacks, const CFDictionaryValueCallBacks *valueCallBacks) { +#endif +#if CFSet || CFBag +CFMutableHashRef CFDictionaryCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFDictionaryKeyCallBacks *keyCallBacks) { +#endif + CFAssert2(0 <= capacity, __kCFLogAssertion, "%s(): capacity (%ld) cannot be less than zero", __PRETTY_FUNCTION__, capacity); +#if CFDictionary + CFMutableHashRef hc = __CFDictionaryInit(allocator, __kCFHashMutable, capacity, keyCallBacks, valueCallBacks); +#endif +#if CFSet || CFBag + CFMutableHashRef hc = __CFDictionaryInit(allocator, __kCFHashMutable, capacity, keyCallBacks); +#endif + return hc; +} + +#if CFDictionary || CFSet +// does not have Add semantics for Bag; it has Set semantics ... is that best? +static void __CFDictionaryGrow(CFMutableHashRef hc, CFIndex numNewValues); + +// This creates a hc which is for CFTypes or NSObjects, with a CFRetain style ownership transfer; +// the hc does not take a retain (since it claims 1), and the caller does not need to release the inserted objects (since we do it). +// The incoming objects must also be collectable if allocated out of a collectable allocator - and are neither released nor retained. +#if CFDictionary +CFHashRef _CFDictionaryCreate_ex(CFAllocatorRef allocator, Boolean isMutable, const_any_pointer_t *keys, const_any_pointer_t *values, CFIndex numValues) { +#endif +#if CFSet || CFBag +CFHashRef _CFDictionaryCreate_ex(CFAllocatorRef allocator, Boolean isMutable, const_any_pointer_t *keys, CFIndex numValues) { +#endif + CFAssert2(0 <= numValues, __kCFLogAssertion, "%s(): numValues (%ld) cannot be less than zero", __PRETTY_FUNCTION__, numValues); +#if CFDictionary + CFMutableHashRef hc = __CFDictionaryInit(allocator, __kCFHashMutable, numValues, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); +#endif +#if CFSet || CFBag + CFMutableHashRef hc = __CFDictionaryInit(allocator, __kCFHashMutable, numValues, &kCFTypeDictionaryKeyCallBacks); +#endif + __CFDictionaryGrow(hc, numValues); + for (CFIndex idx = 0; idx < numValues; idx++) { + CFIndex match, nomatch; + __CFDictionaryFindBuckets2(hc, (any_t)keys[idx], &match, &nomatch); + if (kCFNotFound == match) { + CFAllocatorRef allocator = __CFGetAllocator(hc); + any_t newKey = (any_t)keys[idx]; + if (__CFHashKeyIsMagic(hc, newKey)) { + __CFDictionaryFindNewMarker(hc); + } + if (hc->_keys[nomatch] == ~hc->_marker) { + hc->_deletes--; + } + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[nomatch], newKey); +#if CFDictionary + any_t newValue = (any_t)values[idx]; + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, hc->_values[nomatch], newValue); +#endif +#if CFBag + hc->_values[nomatch] = 1; +#endif + hc->_bucketsUsed++; + hc->_count++; + } else { + CFAllocatorRef allocator = __CFGetAllocator(hc); +#if CFSet || CFBag + any_t oldKey = hc->_keys[match]; + any_t newKey = (any_t)keys[idx]; + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[match], ~hc->_marker); + if (__CFHashKeyIsMagic(hc, newKey)) { + __CFDictionaryFindNewMarker(hc); + } + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[match], newKey); + RELEASEKEY(oldKey); +#endif +#if CFDictionary + any_t oldValue = hc->_values[match]; + any_t newValue = (any_t)values[idx]; + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, hc->_values[match], newValue); + RELEASEVALUE(oldValue); +#endif + } + } + if (!isMutable) __CFBitfieldSetValue(hc->_xflags, 1, 0, __kCFHashImmutable); + return (CFHashRef)hc; +} +#endif + +CFHashRef CFDictionaryCreateCopy(CFAllocatorRef allocator, CFHashRef other) { + CFMutableHashRef hc = CFDictionaryCreateMutableCopy(allocator, CFDictionaryGetCount(other), other); + __CFBitfieldSetValue(hc->_xflags, 1, 0, __kCFHashImmutable); + if (__CFOASafe) __CFSetLastAllocationEventName(hc, "CFDictionary (immutable)"); + return hc; +} + +CFMutableHashRef CFDictionaryCreateMutableCopy(CFAllocatorRef allocator, CFIndex capacity, CFHashRef other) { + CFIndex numValues = CFDictionaryGetCount(other); + const_any_pointer_t *list, buffer[256]; + list = (numValues <= 256) ? buffer : (const_any_pointer_t *)CFAllocatorAllocate(allocator, numValues * sizeof(const_any_pointer_t), 0); + if (list != buffer && __CFOASafe) __CFSetLastAllocationEventName(list, "CFDictionary (temp)"); +#if CFDictionary + const_any_pointer_t *vlist, vbuffer[256]; + vlist = (numValues <= 256) ? vbuffer : (const_any_pointer_t *)CFAllocatorAllocate(allocator, numValues * sizeof(const_any_pointer_t), 0); + if (vlist != vbuffer && __CFOASafe) __CFSetLastAllocationEventName(vlist, "CFDictionary (temp)"); +#endif +#if CFSet || CFBag + CFDictionaryGetValues(other, list); +#endif +#if CFDictionary + CFDictionaryGetKeysAndValues(other, list, vlist); +#endif + const CFDictionaryKeyCallBacks *kcb; + const CFDictionaryValueCallBacks *vcb; + if (CF_IS_OBJC(__kCFHashTypeID, other)) { + kcb = &kCFTypeDictionaryKeyCallBacks; + vcb = &kCFTypeDictionaryValueCallBacks; + } else { + kcb = __CFDictionaryGetKeyCallBacks(other); + vcb = __CFDictionaryGetValueCallBacks(other); + } +#if CFDictionary + CFMutableHashRef hc = __CFDictionaryInit(allocator, __kCFHashMutable, capacity, kcb, vcb); +#endif +#if CFSet || CFBag + CFMutableHashRef hc = __CFDictionaryInit(allocator, __kCFHashMutable, capacity, kcb); +#endif + if (0 == capacity) _CFDictionarySetCapacity(hc, numValues); + for (CFIndex idx = 0; idx < numValues; idx++) { +#if CFDictionary + CFDictionaryAddValue(hc, list[idx], vlist[idx]); +#endif +#if CFSet || CFBag + CFDictionaryAddValue(hc, list[idx]); +#endif + } + if (list != buffer) CFAllocatorDeallocate(allocator, list); +#if CFDictionary + if (vlist != vbuffer) CFAllocatorDeallocate(allocator, vlist); +#endif + return hc; +} + +// Used by NSHashTables/NSMapTables and KVO +void _CFDictionarySetContext(CFHashRef hc, any_pointer_t context) { + __CFGenericValidateType(hc, __kCFHashTypeID); + CF_WRITE_BARRIER_BASE_ASSIGN(__CFGetAllocator(hc), hc, hc->_context, context); +} + +any_pointer_t _CFDictionaryGetContext(CFHashRef hc) { + __CFGenericValidateType(hc, __kCFHashTypeID); + return hc->_context; +} + +CFIndex CFDictionaryGetCount(CFHashRef hc) { + if (CFDictionary || CFSet) CF_OBJC_FUNCDISPATCH0(__kCFHashTypeID, CFIndex, hc, "count"); + __CFGenericValidateType(hc, __kCFHashTypeID); + return hc->_count; +} + +#if CFDictionary +CFIndex CFDictionaryGetCountOfKey(CFHashRef hc, const_any_pointer_t key) { +#endif +#if CFSet || CFBag +CFIndex CFDictionaryGetCountOfValue(CFHashRef hc, const_any_pointer_t key) { +#endif + if (CFDictionary) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, CFIndex, hc, "countForKey:", key); + if (CFSet) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, CFIndex, hc, "countForObject:", key); + __CFGenericValidateType(hc, __kCFHashTypeID); + if (0 == hc->_bucketsUsed) return 0; + CFIndex match = __CFDictionaryFindBuckets1(hc, (any_t)key); + return (kCFNotFound != match ? __CFHashGetOccurrenceCount(hc, match) : 0); +} + +#if CFDictionary +Boolean CFDictionaryContainsKey(CFHashRef hc, const_any_pointer_t key) { +#endif +#if CFSet || CFBag +Boolean CFDictionaryContainsValue(CFHashRef hc, const_any_pointer_t key) { +#endif + if (CFDictionary) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, char, hc, "containsKey:", key); + if (CFSet) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, char, hc, "containsObject:", key); + __CFGenericValidateType(hc, __kCFHashTypeID); + if (0 == hc->_bucketsUsed) return false; + CFIndex match = __CFDictionaryFindBuckets1(hc, (any_t)key); + return (kCFNotFound != match ? true : false); +} + +#if CFDictionary +CFIndex CFDictionaryGetCountOfValue(CFHashRef hc, const_any_pointer_t value) { + CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, CFIndex, hc, "countForObject:", value); + __CFGenericValidateType(hc, __kCFHashTypeID); + if (0 == hc->_bucketsUsed) return 0; + any_t *keys = hc->_keys; + Boolean (*equal)(any_t, any_t, any_pointer_t) = (Boolean (*)(any_t, any_t, any_pointer_t))__CFDictionaryGetValueCallBacks(hc)->equal; + CFIndex cnt = 0; + for (CFIndex idx = 0, nbuckets = hc->_bucketsNum; idx < nbuckets; idx++) { + if (__CFHashKeyIsValue(hc, keys[idx])) { + if ((hc->_values[idx] == (any_t)value) || (equal && INVOKE_CALLBACK3(equal, hc->_values[idx], (any_t)value, hc->_context))) { + cnt++; + } + } + } + return cnt; +} + +Boolean CFDictionaryContainsValue(CFHashRef hc, const_any_pointer_t value) { + CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, char, hc, "containsObject:", value); + __CFGenericValidateType(hc, __kCFHashTypeID); + if (0 == hc->_bucketsUsed) return false; + any_t *keys = hc->_keys; + Boolean (*equal)(any_t, any_t, any_pointer_t) = (Boolean (*)(any_t, any_t, any_pointer_t))__CFDictionaryGetValueCallBacks(hc)->equal; + for (CFIndex idx = 0, nbuckets = hc->_bucketsNum; idx < nbuckets; idx++) { + if (__CFHashKeyIsValue(hc, keys[idx])) { + if ((hc->_values[idx] == (any_t)value) || (equal && INVOKE_CALLBACK3(equal, hc->_values[idx], (any_t)value, hc->_context))) { + return true; + } + } + } + return false; +} +#endif + +const_any_pointer_t CFDictionaryGetValue(CFHashRef hc, const_any_pointer_t key) { + if (CFDictionary) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, const_any_pointer_t, hc, "objectForKey:", key); + if (CFSet) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, const_any_pointer_t, hc, "member:", key); + __CFGenericValidateType(hc, __kCFHashTypeID); + if (0 == hc->_bucketsUsed) return 0; + CFIndex match = __CFDictionaryFindBuckets1(hc, (any_t)key); + return (kCFNotFound != match ? (const_any_pointer_t)(CFDictionary ? hc->_values[match] : hc->_keys[match]) : 0); +} + +Boolean CFDictionaryGetValueIfPresent(CFHashRef hc, const_any_pointer_t key, const_any_pointer_t *value) { + if (CFDictionary) CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, Boolean, hc, "_getValue:forKey:", (any_t *)value, key); + if (CFSet) CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, Boolean, hc, "_getValue:forObj:", (any_t *)value, key); + __CFGenericValidateType(hc, __kCFHashTypeID); + if (0 == hc->_bucketsUsed) return false; + CFIndex match = __CFDictionaryFindBuckets1(hc, (any_t)key); + return (kCFNotFound != match ? ((value ? __CFObjCStrongAssign((const_any_pointer_t)(CFDictionary ? hc->_values[match] : hc->_keys[match]), value) : 0), true) : false); +} + +#if CFDictionary +Boolean CFDictionaryGetKeyIfPresent(CFHashRef hc, const_any_pointer_t key, const_any_pointer_t *actualkey) { + CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, Boolean, hc, "getActualKey:forKey:", actualkey, key); + __CFGenericValidateType(hc, __kCFHashTypeID); + if (0 == hc->_bucketsUsed) return false; + CFIndex match = __CFDictionaryFindBuckets1(hc, (any_t)key); + return (kCFNotFound != match ? ((actualkey ? __CFObjCStrongAssign((const_any_pointer_t)hc->_keys[match], actualkey) : NULL), true) : false); +} +#endif + +#if CFDictionary +void CFDictionaryGetKeysAndValues(CFHashRef hc, const_any_pointer_t *keybuf, const_any_pointer_t *valuebuf) { +#endif +#if CFSet || CFBag +void CFDictionaryGetValues(CFHashRef hc, const_any_pointer_t *keybuf) { + const_any_pointer_t *valuebuf = 0; +#endif + if (CFDictionary) CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, void, hc, "getObjects:andKeys:", (any_t *)valuebuf, (any_t *)keybuf); + if (CFSet) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, void, hc, "getObjects:", (any_t *)keybuf); + __CFGenericValidateType(hc, __kCFHashTypeID); + if (CF_USING_COLLECTABLE_MEMORY) { + // GC: speculatively issue a write-barrier on the copied to buffers + __CFObjCWriteBarrierRange(keybuf, hc->_count * sizeof(any_t)); + __CFObjCWriteBarrierRange(valuebuf, hc->_count * sizeof(any_t)); + } + any_t *keys = hc->_keys; + for (CFIndex idx = 0, nbuckets = hc->_bucketsNum; idx < nbuckets; idx++) { + if (__CFHashKeyIsValue(hc, keys[idx])) { + for (CFIndex cnt = __CFHashGetOccurrenceCount(hc, idx); cnt--;) { + if (keybuf) *keybuf++ = (const_any_pointer_t)keys[idx]; + if (valuebuf) *valuebuf++ = (const_any_pointer_t)hc->_values[idx]; + } + } + } +} + +#if CFDictionary || CFSet +unsigned long _CFDictionaryFastEnumeration(CFHashRef hc, struct __objcFastEnumerationStateEquivalent *state, void *stackbuffer, unsigned long count) { + /* copy as many as count items over */ + if (0 == state->state) { /* first time */ + state->mutationsPtr = (unsigned long *)&hc->_mutations; + } + state->itemsPtr = (unsigned long *)stackbuffer; + CFIndex cnt = 0; + any_t *keys = hc->_keys; + for (CFIndex idx = (CFIndex)state->state, nbuckets = hc->_bucketsNum; idx < nbuckets && cnt < (CFIndex)count; idx++) { + if (__CFHashKeyIsValue(hc, keys[idx])) { + state->itemsPtr[cnt++] = (unsigned long)keys[idx]; + } + state->state++; + } + return cnt; +} +#endif + +void CFDictionaryApplyFunction(CFHashRef hc, CFDictionaryApplierFunction applier, any_pointer_t context) { + FAULT_CALLBACK((void **)&(applier)); + if (CFDictionary) CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, void, hc, "_apply:context:", applier, context); + if (CFSet) CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, void, hc, "_applyValues:context:", applier, context); + __CFGenericValidateType(hc, __kCFHashTypeID); + any_t *keys = hc->_keys; + for (CFIndex idx = 0, nbuckets = hc->_bucketsNum; idx < nbuckets; idx++) { + if (__CFHashKeyIsValue(hc, keys[idx])) { + for (CFIndex cnt = __CFHashGetOccurrenceCount(hc, idx); cnt--;) { +#if CFDictionary + INVOKE_CALLBACK3(applier, (const_any_pointer_t)keys[idx], (const_any_pointer_t)hc->_values[idx], context); +#endif +#if CFSet || CFBag + INVOKE_CALLBACK2(applier, (const_any_pointer_t)keys[idx], context); +#endif + } + } + } +} + +static void __CFDictionaryGrow(CFMutableHashRef hc, CFIndex numNewValues) { + any_t *oldkeys = hc->_keys; + any_t *oldvalues = hc->_values; + CFIndex nbuckets = hc->_bucketsNum; + hc->_bucketsCap = __CFHashRoundUpCapacity(hc->_bucketsUsed + numNewValues); + hc->_bucketsNum = __CFHashNumBucketsForCapacity(hc->_bucketsCap); + hc->_deletes = 0; + CFAllocatorRef allocator = __CFGetAllocator(hc); + CFOptionFlags weakOrStrong = (hc->_xflags & __kCFHashWeakKeys) ? 0 : __kCFAllocatorGCScannedMemory; + any_t *mem = (any_t *)_CFAllocatorAllocateGC(allocator, hc->_bucketsNum * sizeof(any_t), weakOrStrong); + if (NULL == mem) __CFDictionaryHandleOutOfMemory(hc, hc->_bucketsNum * sizeof(any_t)); + if (__CFOASafe) __CFSetLastAllocationEventName(mem, "CFDictionary (key-store)"); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, hc, hc->_keys, mem); + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; // GC: avoids write-barrier in weak case. + any_t *keysBase = mem; +#if CFDictionary || CFBag + weakOrStrong = (hc->_xflags & __kCFHashWeakValues) ? 0 : __kCFAllocatorGCScannedMemory; + mem = (any_t *)_CFAllocatorAllocateGC(allocator, hc->_bucketsNum * sizeof(any_t), weakOrStrong); + if (NULL == mem) __CFDictionaryHandleOutOfMemory(hc, hc->_bucketsNum * sizeof(any_t)); + if (__CFOASafe) __CFSetLastAllocationEventName(mem, "CFDictionary (value-store)"); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, hc, hc->_values, mem); +#endif +#if CFDictionary + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; // GC: avoids write-barrier in weak case. + any_t *valuesBase = mem; +#endif + for (CFIndex idx = 0, nbuckets = hc->_bucketsNum; idx < nbuckets; idx++) { + hc->_keys[idx] = hc->_marker; +#if CFDictionary || CFBag + hc->_values[idx] = 0; +#endif + } + if (NULL == oldkeys) return; + for (CFIndex idx = 0; idx < nbuckets; idx++) { + if (__CFHashKeyIsValue(hc, oldkeys[idx])) { + CFIndex match, nomatch; + __CFDictionaryFindBuckets2(hc, oldkeys[idx], &match, &nomatch); + CFAssert3(kCFNotFound == match, __kCFLogAssertion, "%s(): two values (%p, %p) now hash to the same slot; mutable value changed while in table or hash value is not immutable", __PRETTY_FUNCTION__, oldkeys[idx], hc->_keys[match]); + if (kCFNotFound != nomatch) { + CF_WRITE_BARRIER_BASE_ASSIGN(keysAllocator, keysBase, hc->_keys[nomatch], oldkeys[idx]); +#if CFDictionary + CF_WRITE_BARRIER_BASE_ASSIGN(valuesAllocator, valuesBase, hc->_values[nomatch], oldvalues[idx]); +#endif +#if CFBag + hc->_values[nomatch] = oldvalues[idx]; +#endif + } + } + } + _CFAllocatorDeallocateGC(allocator, oldkeys); + _CFAllocatorDeallocateGC(allocator, oldvalues); +} + +// This function is for Foundation's benefit; no one else should use it. +void _CFDictionarySetCapacity(CFMutableHashRef hc, CFIndex cap) { + if (CF_IS_OBJC(__kCFHashTypeID, hc)) return; + __CFGenericValidateType(hc, __kCFHashTypeID); + CFAssert1(__CFHashGetType(hc) != __kCFHashImmutable, __kCFLogAssertion, "%s(): collection is immutable", __PRETTY_FUNCTION__); + CFAssert3(hc->_bucketsUsed <= cap, __kCFLogAssertion, "%s(): desired capacity (%ld) is less than bucket count (%ld)", __PRETTY_FUNCTION__, cap, hc->_bucketsUsed); + __CFDictionaryGrow(hc, cap - hc->_bucketsUsed); +} + +// This function is for Foundation's benefit; no one else should use it. +bool _CFDictionaryIsMutable(CFDictionaryRef dict) { + return (__CFHashGetType(dict) != __kCFHashImmutable); +} + +#if CFDictionary +void CFDictionaryAddValue(CFMutableHashRef hc, const_any_pointer_t key, const_any_pointer_t value) { +#endif +#if CFSet || CFBag +void CFDictionaryAddValue(CFMutableHashRef hc, const_any_pointer_t key) { + #define value 0 +#endif + if (CFDictionary) CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, void, hc, "_addObject:forKey:", value, key); + if (CFSet) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, void, hc, "addObject:", key); + __CFGenericValidateType(hc, __kCFHashTypeID); + switch (__CFHashGetType(hc)) { + case __kCFHashMutable: + if (hc->_bucketsUsed == hc->_bucketsCap || NULL == hc->_keys) { + __CFDictionaryGrow(hc, 1); + } + break; + default: + CFAssert2(__CFHashGetType(hc) != __kCFHashImmutable, __kCFLogAssertion, "%s(): immutable collection %p passed to mutating operation", __PRETTY_FUNCTION__, hc); + break; + } + hc->_mutations++; + CFIndex match, nomatch; + __CFDictionaryFindBuckets2(hc, (any_t)key, &match, &nomatch); + if (kCFNotFound != match) { +#if CFBag + CF_OBJC_KVO_WILLCHANGE(hc, hc->_keys[match]); + hc->_values[match]++; + hc->_count++; + CF_OBJC_KVO_DIDCHANGE(hc, hc->_keys[match]); +#endif + } else { + CFAllocatorRef allocator = __CFGetAllocator(hc); + GETNEWKEY(newKey, key); +#if CFDictionary + GETNEWVALUE(newValue); +#endif + if (__CFHashKeyIsMagic(hc, newKey)) { + __CFDictionaryFindNewMarker(hc); + } + if (hc->_keys[nomatch] == ~hc->_marker) { + hc->_deletes--; + } + CF_OBJC_KVO_WILLCHANGE(hc, key); + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[nomatch], newKey); +#if CFDictionary + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, hc->_values[nomatch], newValue); +#endif +#if CFBag + hc->_values[nomatch] = 1; +#endif + hc->_bucketsUsed++; + hc->_count++; + CF_OBJC_KVO_DIDCHANGE(hc, key); + } +} + +#if CFDictionary +void CFDictionaryReplaceValue(CFMutableHashRef hc, const_any_pointer_t key, const_any_pointer_t value) { +#endif +#if CFSet || CFBag +void CFDictionaryReplaceValue(CFMutableHashRef hc, const_any_pointer_t key) { + #define value 0 +#endif + if (CFDictionary) CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, void, hc, "_replaceObject:forKey:", value, key); + if (CFSet) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, void, hc, "_replaceObject:", key); + __CFGenericValidateType(hc, __kCFHashTypeID); + switch (__CFHashGetType(hc)) { + case __kCFHashMutable: + break; + default: + CFAssert2(__CFHashGetType(hc) != __kCFHashImmutable, __kCFLogAssertion, "%s(): immutable collection %p passed to mutating operation", __PRETTY_FUNCTION__, hc); + break; + } + hc->_mutations++; + if (0 == hc->_bucketsUsed) return; + CFIndex match = __CFDictionaryFindBuckets1(hc, (any_t)key); + if (kCFNotFound == match) return; + CFAllocatorRef allocator = __CFGetAllocator(hc); +#if CFSet || CFBag + GETNEWKEY(newKey, key); +#endif +#if CFDictionary + GETNEWVALUE(newValue); +#endif + any_t oldKey = hc->_keys[match]; + CF_OBJC_KVO_WILLCHANGE(hc, oldKey); +#if CFSet || CFBag + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[match], ~hc->_marker); + if (__CFHashKeyIsMagic(hc, newKey)) { + __CFDictionaryFindNewMarker(hc); + } + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[match], newKey); +#endif +#if CFDictionary + any_t oldValue = hc->_values[match]; + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, hc->_values[match], newValue); +#endif + CF_OBJC_KVO_DIDCHANGE(hc, oldKey); +#if CFSet || CFBag + RELEASEKEY(oldKey); +#endif +#if CFDictionary + RELEASEVALUE(oldValue); +#endif +} + +#if CFDictionary +void CFDictionarySetValue(CFMutableHashRef hc, const_any_pointer_t key, const_any_pointer_t value) { +#endif +#if CFSet || CFBag +void CFDictionarySetValue(CFMutableHashRef hc, const_any_pointer_t key) { + #define value 0 +#endif + if (CFDictionary) CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, void, hc, "setObject:forKey:", value, key); + if (CFSet) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, void, hc, "_setObject:", key); + __CFGenericValidateType(hc, __kCFHashTypeID); + switch (__CFHashGetType(hc)) { + case __kCFHashMutable: + if (hc->_bucketsUsed == hc->_bucketsCap || NULL == hc->_keys) { + __CFDictionaryGrow(hc, 1); + } + break; + default: + CFAssert2(__CFHashGetType(hc) != __kCFHashImmutable, __kCFLogAssertion, "%s(): immutable collection %p passed to mutating operation", __PRETTY_FUNCTION__, hc); + break; + } + hc->_mutations++; + CFIndex match, nomatch; + __CFDictionaryFindBuckets2(hc, (any_t)key, &match, &nomatch); + if (kCFNotFound == match) { + CFAllocatorRef allocator = __CFGetAllocator(hc); + GETNEWKEY(newKey, key); +#if CFDictionary + GETNEWVALUE(newValue); +#endif + if (__CFHashKeyIsMagic(hc, newKey)) { + __CFDictionaryFindNewMarker(hc); + } + if (hc->_keys[nomatch] == ~hc->_marker) { + hc->_deletes--; + } + CF_OBJC_KVO_WILLCHANGE(hc, key); + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[nomatch], newKey); +#if CFDictionary + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, hc->_values[nomatch], newValue); +#endif +#if CFBag + hc->_values[nomatch] = 1; +#endif + hc->_bucketsUsed++; + hc->_count++; + CF_OBJC_KVO_DIDCHANGE(hc, key); + } else { + CFAllocatorRef allocator = __CFGetAllocator(hc); +#if CFSet || CFBag + GETNEWKEY(newKey, key); +#endif +#if CFDictionary + GETNEWVALUE(newValue); +#endif + any_t oldKey = hc->_keys[match]; + CF_OBJC_KVO_WILLCHANGE(hc, oldKey); +#if CFSet || CFBag + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[match], ~hc->_marker); + if (__CFHashKeyIsMagic(hc, newKey)) { + __CFDictionaryFindNewMarker(hc); + } + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[match], newKey); +#endif +#if CFDictionary + any_t oldValue = hc->_values[match]; + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, hc->_values[match], newValue); +#endif + CF_OBJC_KVO_DIDCHANGE(hc, oldKey); +#if CFSet || CFBag + RELEASEKEY(oldKey); +#endif +#if CFDictionary + RELEASEVALUE(oldValue); +#endif + } +} + +void CFDictionaryRemoveValue(CFMutableHashRef hc, const_any_pointer_t key) { + if (CFDictionary) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, void, hc, "removeObjectForKey:", key); + if (CFSet) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, void, hc, "removeObject:", key); + __CFGenericValidateType(hc, __kCFHashTypeID); + switch (__CFHashGetType(hc)) { + case __kCFHashMutable: + break; + default: + CFAssert2(__CFHashGetType(hc) != __kCFHashImmutable, __kCFLogAssertion, "%s(): immutable collection %p passed to mutating operation", __PRETTY_FUNCTION__, hc); + break; + } + hc->_mutations++; + if (0 == hc->_bucketsUsed) return; + CFIndex match = __CFDictionaryFindBuckets1(hc, (any_t)key); + if (kCFNotFound == match) return; + if (1 < __CFHashGetOccurrenceCount(hc, match)) { +#if CFBag + CF_OBJC_KVO_WILLCHANGE(hc, hc->_keys[match]); + hc->_values[match]--; + hc->_count--; + CF_OBJC_KVO_DIDCHANGE(hc, hc->_keys[match]); +#endif + } else { + CFAllocatorRef allocator = __CFGetAllocator(hc); + any_t oldKey = hc->_keys[match]; + CF_OBJC_KVO_WILLCHANGE(hc, oldKey); + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[match], ~hc->_marker); +#if CFDictionary + any_t oldValue = hc->_values[match]; + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, hc->_values[match], 0); +#endif +#if CFBag + hc->_values[match] = 0; +#endif + hc->_count--; + hc->_bucketsUsed--; + hc->_deletes++; + CF_OBJC_KVO_DIDCHANGE(hc, oldKey); + RELEASEKEY(oldKey); +#if CFDictionary + RELEASEVALUE(oldValue); +#endif + if (__CFDictionaryShouldShrink(hc)) { + __CFDictionaryGrow(hc, 0); + } else { + // When the probeskip == 1 always and only, a DELETED slot followed by an EMPTY slot + // can be converted to an EMPTY slot. By extension, a chain of DELETED slots followed + // by an EMPTY slot can be converted to EMPTY slots, which is what we do here. + if (match < hc->_bucketsNum - 1 && hc->_keys[match + 1] == hc->_marker) { + while (0 <= match && hc->_keys[match] == ~hc->_marker) { + hc->_keys[match] = hc->_marker; + hc->_deletes--; + match--; + } + } + } + } +} + +void CFDictionaryRemoveAllValues(CFMutableHashRef hc) { + if (CFDictionary) CF_OBJC_FUNCDISPATCH0(__kCFHashTypeID, void, hc, "removeAllObjects"); + if (CFSet) CF_OBJC_FUNCDISPATCH0(__kCFHashTypeID, void, hc, "removeAllObjects"); + __CFGenericValidateType(hc, __kCFHashTypeID); + switch (__CFHashGetType(hc)) { + case __kCFHashMutable: + break; + default: + CFAssert2(__CFHashGetType(hc) != __kCFHashImmutable, __kCFLogAssertion, "%s(): immutable collection %p passed to mutating operation", __PRETTY_FUNCTION__, hc); + break; + } + hc->_mutations++; + if (0 == hc->_bucketsUsed) return; + CFAllocatorRef allocator = __CFGetAllocator(hc); + any_t *keys = hc->_keys; + for (CFIndex idx = 0, nbuckets = hc->_bucketsNum; idx < nbuckets; idx++) { + if (__CFHashKeyIsValue(hc, keys[idx])) { + any_t oldKey = keys[idx]; + CF_OBJC_KVO_WILLCHANGE(hc, oldKey); +#if CFDictionary || CFSet + hc->_count--; +#endif +#if CFBag + hc->_count -= hc->_values[idx]; +#endif + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[idx], ~hc->_marker); +#if CFDictionary + any_t oldValue = hc->_values[idx]; + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, hc->_values[idx], 0); +#endif +#if CFBag + hc->_values[idx] = 0; +#endif + hc->_bucketsUsed--; + hc->_deletes++; + CF_OBJC_KVO_DIDCHANGE(hc, oldKey); + RELEASEKEY(oldKey); +#if CFDictionary + RELEASEVALUE(oldValue); +#endif + } + } + for (CFIndex idx = 0, nbuckets = hc->_bucketsNum; idx < nbuckets; idx++) { + keys[idx] = hc->_marker; + } + hc->_deletes = 0; + hc->_bucketsUsed = 0; + hc->_count = 0; + if (__CFDictionaryShouldShrink(hc) && (256 <= hc->_bucketsCap)) { + __CFDictionaryGrow(hc, 128); + } +} + +#undef CF_OBJC_KVO_WILLCHANGE +#undef CF_OBJC_KVO_DIDCHANGE +
diff --git a/CoreFoundation/CFDictionary.h b/CoreFoundation/CFDictionary.h new file mode 100644 index 0000000..071871b --- /dev/null +++ b/CoreFoundation/CFDictionary.h
@@ -0,0 +1,699 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFDictionary.h + Copyright (c) 1998-2007, Apple Inc. All rights reserved. +*/ + +/*! + @header CFDictionary + CFDictionary implements a container which pairs pointer-sized keys + with pointer-sized values. Values are accessed via arbitrary + user-defined keys. A CFDictionary differs from a CFArray in that + the key used to access a particular value in the dictionary remains + the same as values are added to or removed from the dictionary, + unless a value associated with its particular key is replaced or + removed. In a CFArray, the key (or index) used to retrieve a + particular value can change over time as values are added to or + deleted from the array. Also unlike an array, there is no ordering + among values in a dictionary. To enable later retrieval of a value, + the key of the key-value pair should be constant (or treated as + constant); if the key changes after being used to put a value in + the dictionary, the value may not be retrievable. The keys of a + dictionary form a set; that is, no two keys which are equal to + one another are present in the dictionary at any time. + + Dictionaries come in two flavors, immutable, which cannot have + values added to them or removed from them after the dictionary is + created, and mutable, to which you can add values or from which + remove values. Mutable dictionaries can have an unlimited number + of values (or rather, limited only by constraints external to + CFDictionary, like the amount of available memory). + + As with all CoreFoundation collection types, dictionaries maintain + hard references on the values you put in them, but the retaining and + releasing functions are user-defined callbacks that can actually do + whatever the user wants (for example, nothing). + + Although a particular implementation of CFDictionary may not use + hashing and a hash table for storage of the values, the keys have + a hash-code generating function defined for them, and a function + to test for equality of two keys. These two functions together + must maintain the invariant that if equal(X, Y), then hash(X) == + hash(Y). Note that the converse will not generally be true (but + the contrapositive, if hash(X) != hash(Y), then !equal(X, Y), + will be as required by Boolean logic). If the hash() and equal() + key callbacks are NULL, the key is used as a pointer-sized integer, + and pointer equality is used. Care should be taken to provide a + hash() callback which will compute sufficiently dispersed hash + codes for the key set for best performance. + + Computational Complexity + The access time for a value in the dictionary is guaranteed to be at + worst O(lg N) for any implementation, current and future, but will + often be O(1) (constant time). Insertion or deletion operations + will typically be constant time as well, but are O(N*lg N) in the + worst case in some implementations. Access of values through a key + is faster than accessing values directly (if there are any such + operations). Dictionaries will tend to use significantly more memory + than a array with the same number of values. +*/ + +#if !defined(__COREFOUNDATION_CFDICTIONARY__) +#define __COREFOUNDATION_CFDICTIONARY__ 1 + +#include <CoreFoundation/CFBase.h> + +CF_EXTERN_C_BEGIN + +/*! + @typedef CFDictionaryKeyCallBacks + Structure containing the callbacks for keys of a CFDictionary. + @field version The version number of the structure type being passed + in as a parameter to the CFDictionary creation functions. + This structure is version 0. + @field retain The callback used to add a retain for the dictionary + on keys as they are used to put values into the dictionary. + This callback returns the value to use as the key in the + dictionary, which is usually the value parameter passed to + this callback, but may be a different value if a different + value should be used as the key. The dictionary's allocator + is passed as the first argument. + @field release The callback used to remove a retain previously added + for the dictionary from keys as their values are removed from + the dictionary. The dictionary's allocator is passed as the + first argument. + @field copyDescription The callback used to create a descriptive + string representation of each key in the dictionary. This + is used by the CFCopyDescription() function. + @field equal The callback used to compare keys in the dictionary for + equality. + @field hash The callback used to compute a hash code for keys as they + are used to access, add, or remove values in the dictionary. +*/ +typedef const void * (*CFDictionaryRetainCallBack)(CFAllocatorRef allocator, const void *value); +typedef void (*CFDictionaryReleaseCallBack)(CFAllocatorRef allocator, const void *value); +typedef CFStringRef (*CFDictionaryCopyDescriptionCallBack)(const void *value); +typedef Boolean (*CFDictionaryEqualCallBack)(const void *value1, const void *value2); +typedef CFHashCode (*CFDictionaryHashCallBack)(const void *value); +typedef struct { + CFIndex version; + CFDictionaryRetainCallBack retain; + CFDictionaryReleaseCallBack release; + CFDictionaryCopyDescriptionCallBack copyDescription; + CFDictionaryEqualCallBack equal; + CFDictionaryHashCallBack hash; +} CFDictionaryKeyCallBacks; + +/*! + @constant kCFTypeDictionaryKeyCallBacks + Predefined CFDictionaryKeyCallBacks structure containing a + set of callbacks appropriate for use when the keys of a + CFDictionary are all CFTypes. +*/ +CF_EXPORT +const CFDictionaryKeyCallBacks kCFTypeDictionaryKeyCallBacks; + +/*! + @constant kCFCopyStringDictionaryKeyCallBacks + Predefined CFDictionaryKeyCallBacks structure containing a + set of callbacks appropriate for use when the keys of a + CFDictionary are all CFStrings, which may be mutable and + need to be copied in order to serve as constant keys for + the values in the dictionary. +*/ +CF_EXPORT +const CFDictionaryKeyCallBacks kCFCopyStringDictionaryKeyCallBacks; + +/*! + @typedef CFDictionaryValueCallBacks + Structure containing the callbacks for values of a CFDictionary. + @field version The version number of the structure type being passed + in as a parameter to the CFDictionary creation functions. + This structure is version 0. + @field retain The callback used to add a retain for the dictionary + on values as they are put into the dictionary. + This callback returns the value to use as the value in the + dictionary, which is usually the value parameter passed to + this callback, but may be a different value if a different + value should be added to the dictionary. The dictionary's + allocator is passed as the first argument. + @field release The callback used to remove a retain previously added + for the dictionary from values as they are removed from + the dictionary. The dictionary's allocator is passed as the + first argument. + @field copyDescription The callback used to create a descriptive + string representation of each value in the dictionary. This + is used by the CFCopyDescription() function. + @field equal The callback used to compare values in the dictionary for + equality in some operations. +*/ +typedef struct { + CFIndex version; + CFDictionaryRetainCallBack retain; + CFDictionaryReleaseCallBack release; + CFDictionaryCopyDescriptionCallBack copyDescription; + CFDictionaryEqualCallBack equal; +} CFDictionaryValueCallBacks; + +/*! + @constant kCFTypeDictionaryValueCallBacks + Predefined CFDictionaryValueCallBacks structure containing a set + of callbacks appropriate for use when the values in a CFDictionary + are all CFTypes. +*/ +CF_EXPORT +const CFDictionaryValueCallBacks kCFTypeDictionaryValueCallBacks; + +/*! + @typedef CFDictionaryApplierFunction + Type of the callback function used by the apply functions of + CFDictionarys. + @param key The current key for the value. + @param value The current value from the dictionary. + @param context The user-defined context parameter given to the apply + function. +*/ +typedef void (*CFDictionaryApplierFunction)(const void *key, const void *value, void *context); + +/*! + @typedef CFDictionaryRef + This is the type of a reference to immutable CFDictionarys. +*/ +typedef const struct __CFDictionary * CFDictionaryRef; + +/*! + @typedef CFMutableDictionaryRef + This is the type of a reference to mutable CFDictionarys. +*/ +typedef struct __CFDictionary * CFMutableDictionaryRef; + +/*! + @function CFDictionaryGetTypeID + Returns the type identifier of all CFDictionary instances. +*/ +CF_EXPORT +CFTypeID CFDictionaryGetTypeID(void); + +/*! + @function CFDictionaryCreate + Creates a new immutable dictionary with the given values. + @param allocator The CFAllocator which should be used to allocate + memory for the dictionary and its storage for values. This + parameter may be NULL in which case the current default + CFAllocator is used. If this reference is not a valid + CFAllocator, the behavior is undefined. + @param keys A C array of the pointer-sized keys to be used for + the parallel C array of values to be put into the dictionary. + This parameter may be NULL if the numValues parameter is 0. + This C array is not changed or freed by this function. If + this parameter is not a valid pointer to a C array of at + least numValues pointers, the behavior is undefined. + @param values A C array of the pointer-sized values to be in the + dictionary. This parameter may be NULL if the numValues + parameter is 0. This C array is not changed or freed by + this function. If this parameter is not a valid pointer to + a C array of at least numValues pointers, the behavior is + undefined. + @param numValues The number of values to copy from the keys and + values C arrays into the CFDictionary. This number will be + the count of the dictionary. If this parameter is + negative, or greater than the number of values actually + in the keys or values C arrays, the behavior is undefined. + @param keyCallBacks A pointer to a CFDictionaryKeyCallBacks structure + initialized with the callbacks for the dictionary to use on + each key in the dictionary. The retain callback will be used + within this function, for example, to retain all of the new + keys from the keys C array. A copy of the contents of the + callbacks structure is made, so that a pointer to a structure + on the stack can be passed in, or can be reused for multiple + dictionary creations. If the version field of this + callbacks structure is not one of the defined ones for + CFDictionary, the behavior is undefined. The retain field may + be NULL, in which case the CFDictionary will do nothing to add + a retain to the keys of the contained values. The release field + may be NULL, in which case the CFDictionary will do nothing + to remove the dictionary's retain (if any) on the keys when the + dictionary is destroyed or a key-value pair is removed. If the + copyDescription field is NULL, the dictionary will create a + simple description for a key. If the equal field is NULL, the + dictionary will use pointer equality to test for equality of + keys. If the hash field is NULL, a key will be converted from + a pointer to an integer to compute the hash code. This callbacks + parameter itself may be NULL, which is treated as if a valid + structure of version 0 with all fields NULL had been passed in. + Otherwise, if any of the fields are not valid pointers to + functions of the correct type, or this parameter is not a + valid pointer to a CFDictionaryKeyCallBacks callbacks structure, + the behavior is undefined. If any of the keys put into the + dictionary is not one understood by one of the callback functions + the behavior when that callback function is used is undefined. + @param valueCallBacks A pointer to a CFDictionaryValueCallBacks structure + initialized with the callbacks for the dictionary to use on + each value in the dictionary. The retain callback will be used + within this function, for example, to retain all of the new + values from the values C array. A copy of the contents of the + callbacks structure is made, so that a pointer to a structure + on the stack can be passed in, or can be reused for multiple + dictionary creations. If the version field of this callbacks + structure is not one of the defined ones for CFDictionary, the + behavior is undefined. The retain field may be NULL, in which + case the CFDictionary will do nothing to add a retain to values + as they are put into the dictionary. The release field may be + NULL, in which case the CFDictionary will do nothing to remove + the dictionary's retain (if any) on the values when the + dictionary is destroyed or a key-value pair is removed. If the + copyDescription field is NULL, the dictionary will create a + simple description for a value. If the equal field is NULL, the + dictionary will use pointer equality to test for equality of + values. This callbacks parameter itself may be NULL, which is + treated as if a valid structure of version 0 with all fields + NULL had been passed in. Otherwise, + if any of the fields are not valid pointers to functions + of the correct type, or this parameter is not a valid + pointer to a CFDictionaryValueCallBacks callbacks structure, + the behavior is undefined. If any of the values put into the + dictionary is not one understood by one of the callback functions + the behavior when that callback function is used is undefined. + @result A reference to the new immutable CFDictionary. +*/ +CF_EXPORT +CFDictionaryRef CFDictionaryCreate(CFAllocatorRef allocator, const void **keys, const void **values, CFIndex numValues, const CFDictionaryKeyCallBacks *keyCallBacks, const CFDictionaryValueCallBacks *valueCallBacks); + +/*! + @function CFDictionaryCreateCopy + Creates a new immutable dictionary with the key-value pairs from + the given dictionary. + @param allocator The CFAllocator which should be used to allocate + memory for the dictionary and its storage for values. This + parameter may be NULL in which case the current default + CFAllocator is used. If this reference is not a valid + CFAllocator, the behavior is undefined. + @param theDict The dictionary which is to be copied. The keys and values + from the dictionary are copied as pointers into the new + dictionary (that is, the values themselves are copied, not + that which the values point to, if anything). However, the + keys and values are also retained by the new dictionary using + the retain function of the original dictionary. + The count of the new dictionary will be the same as the + given dictionary. The new dictionary uses the same callbacks + as the dictionary to be copied. If this parameter is + not a valid CFDictionary, the behavior is undefined. + @result A reference to the new immutable CFDictionary. +*/ +CF_EXPORT +CFDictionaryRef CFDictionaryCreateCopy(CFAllocatorRef allocator, CFDictionaryRef theDict); + +/*! + @function CFDictionaryCreateMutable + Creates a new mutable dictionary. + @param allocator The CFAllocator which should be used to allocate + memory for the dictionary and its storage for values. This + parameter may be NULL in which case the current default + CFAllocator is used. If this reference is not a valid + CFAllocator, the behavior is undefined. + @param capacity A hint about the number of values that will be held + by the CFDictionary. Pass 0 for no hint. The implementation may + ignore this hint, or may use it to optimize various + operations. A dictionary's actual capacity is only limited by + address space and available memory constraints). If this + parameter is negative, the behavior is undefined. + @param keyCallBacks A pointer to a CFDictionaryKeyCallBacks structure + initialized with the callbacks for the dictionary to use on + each key in the dictionary. A copy of the contents of the + callbacks structure is made, so that a pointer to a structure + on the stack can be passed in, or can be reused for multiple + dictionary creations. If the version field of this + callbacks structure is not one of the defined ones for + CFDictionary, the behavior is undefined. The retain field may + be NULL, in which case the CFDictionary will do nothing to add + a retain to the keys of the contained values. The release field + may be NULL, in which case the CFDictionary will do nothing + to remove the dictionary's retain (if any) on the keys when the + dictionary is destroyed or a key-value pair is removed. If the + copyDescription field is NULL, the dictionary will create a + simple description for a key. If the equal field is NULL, the + dictionary will use pointer equality to test for equality of + keys. If the hash field is NULL, a key will be converted from + a pointer to an integer to compute the hash code. This callbacks + parameter itself may be NULL, which is treated as if a valid + structure of version 0 with all fields NULL had been passed in. + Otherwise, if any of the fields are not valid pointers to + functions of the correct type, or this parameter is not a + valid pointer to a CFDictionaryKeyCallBacks callbacks structure, + the behavior is undefined. If any of the keys put into the + dictionary is not one understood by one of the callback functions + the behavior when that callback function is used is undefined. + @param valueCallBacks A pointer to a CFDictionaryValueCallBacks structure + initialized with the callbacks for the dictionary to use on + each value in the dictionary. The retain callback will be used + within this function, for example, to retain all of the new + values from the values C array. A copy of the contents of the + callbacks structure is made, so that a pointer to a structure + on the stack can be passed in, or can be reused for multiple + dictionary creations. If the version field of this callbacks + structure is not one of the defined ones for CFDictionary, the + behavior is undefined. The retain field may be NULL, in which + case the CFDictionary will do nothing to add a retain to values + as they are put into the dictionary. The release field may be + NULL, in which case the CFDictionary will do nothing to remove + the dictionary's retain (if any) on the values when the + dictionary is destroyed or a key-value pair is removed. If the + copyDescription field is NULL, the dictionary will create a + simple description for a value. If the equal field is NULL, the + dictionary will use pointer equality to test for equality of + values. This callbacks parameter itself may be NULL, which is + treated as if a valid structure of version 0 with all fields + NULL had been passed in. Otherwise, + if any of the fields are not valid pointers to functions + of the correct type, or this parameter is not a valid + pointer to a CFDictionaryValueCallBacks callbacks structure, + the behavior is undefined. If any of the values put into the + dictionary is not one understood by one of the callback functions + the behavior when that callback function is used is undefined. + @result A reference to the new mutable CFDictionary. +*/ +CF_EXPORT +CFMutableDictionaryRef CFDictionaryCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFDictionaryKeyCallBacks *keyCallBacks, const CFDictionaryValueCallBacks *valueCallBacks); + +/*! + @function CFDictionaryCreateMutableCopy + Creates a new mutable dictionary with the key-value pairs from + the given dictionary. + @param allocator The CFAllocator which should be used to allocate + memory for the dictionary and its storage for values. This + parameter may be NULL in which case the current default + CFAllocator is used. If this reference is not a valid + CFAllocator, the behavior is undefined. + @param capacity A hint about the number of values that will be held + by the CFDictionary. Pass 0 for no hint. The implementation may + ignore this hint, or may use it to optimize various + operations. A dictionary's actual capacity is only limited by + address space and available memory constraints). + This parameter must be greater than or equal + to the count of the dictionary which is to be copied, or the + behavior is undefined. If this parameter is negative, the + behavior is undefined. + @param theDict The dictionary which is to be copied. The keys and values + from the dictionary are copied as pointers into the new + dictionary (that is, the values themselves are copied, not + that which the values point to, if anything). However, the + keys and values are also retained by the new dictionary using + the retain function of the original dictionary. + The count of the new dictionary will be the same as the + given dictionary. The new dictionary uses the same callbacks + as the dictionary to be copied. If this parameter is + not a valid CFDictionary, the behavior is undefined. + @result A reference to the new mutable CFDictionary. +*/ +CF_EXPORT +CFMutableDictionaryRef CFDictionaryCreateMutableCopy(CFAllocatorRef allocator, CFIndex capacity, CFDictionaryRef theDict); + +/*! + @function CFDictionaryGetCount + Returns the number of values currently in the dictionary. + @param theDict The dictionary to be queried. If this parameter is + not a valid CFDictionary, the behavior is undefined. + @result The number of values in the dictionary. +*/ +CF_EXPORT +CFIndex CFDictionaryGetCount(CFDictionaryRef theDict); + +/*! + @function CFDictionaryGetCountOfKey + Counts the number of times the given key occurs in the dictionary. + @param theDict The dictionary to be searched. If this parameter is + not a valid CFDictionary, the behavior is undefined. + @param key The key for which to find matches in the dictionary. The + hash() and equal() key callbacks provided when the dictionary + was created are used to compare. If the hash() key callback + was NULL, the key is treated as a pointer and converted to + an integer. If the equal() key callback was NULL, pointer + equality (in C, ==) is used. If key, or any of the keys in + the dictionary, are not understood by the equal() callback, + the behavior is undefined. + @result Returns 1 if a matching key is used by the dictionary, + 0 otherwise. +*/ +CF_EXPORT +CFIndex CFDictionaryGetCountOfKey(CFDictionaryRef theDict, const void *key); + +/*! + @function CFDictionaryGetCountOfValue + Counts the number of times the given value occurs in the dictionary. + @param theDict The dictionary to be searched. If this parameter is + not a valid CFDictionary, the behavior is undefined. + @param value The value for which to find matches in the dictionary. The + equal() callback provided when the dictionary was created is + used to compare. If the equal() value callback was NULL, pointer + equality (in C, ==) is used. If value, or any of the values in + the dictionary, are not understood by the equal() callback, + the behavior is undefined. + @result The number of times the given value occurs in the dictionary. +*/ +CF_EXPORT +CFIndex CFDictionaryGetCountOfValue(CFDictionaryRef theDict, const void *value); + +/*! + @function CFDictionaryContainsKey + Reports whether or not the key is in the dictionary. + @param theDict The dictionary to be searched. If this parameter is + not a valid CFDictionary, the behavior is undefined. + @param key The key for which to find matches in the dictionary. The + hash() and equal() key callbacks provided when the dictionary + was created are used to compare. If the hash() key callback + was NULL, the key is treated as a pointer and converted to + an integer. If the equal() key callback was NULL, pointer + equality (in C, ==) is used. If key, or any of the keys in + the dictionary, are not understood by the equal() callback, + the behavior is undefined. + @result true, if the key is in the dictionary, otherwise false. +*/ +CF_EXPORT +Boolean CFDictionaryContainsKey(CFDictionaryRef theDict, const void *key); + +/*! + @function CFDictionaryContainsValue + Reports whether or not the value is in the dictionary. + @param theDict The dictionary to be searched. If this parameter is + not a valid CFDictionary, the behavior is undefined. + @param value The value for which to find matches in the dictionary. The + equal() callback provided when the dictionary was created is + used to compare. If the equal() callback was NULL, pointer + equality (in C, ==) is used. If value, or any of the values + in the dictionary, are not understood by the equal() callback, + the behavior is undefined. + @result true, if the value is in the dictionary, otherwise false. +*/ +CF_EXPORT +Boolean CFDictionaryContainsValue(CFDictionaryRef theDict, const void *value); + +/*! + @function CFDictionaryGetValue + Retrieves the value associated with the given key. + @param theDict The dictionary to be queried. If this parameter is + not a valid CFDictionary, the behavior is undefined. + @param key The key for which to find a match in the dictionary. The + hash() and equal() key callbacks provided when the dictionary + was created are used to compare. If the hash() key callback + was NULL, the key is treated as a pointer and converted to + an integer. If the equal() key callback was NULL, pointer + equality (in C, ==) is used. If key, or any of the keys in + the dictionary, are not understood by the equal() callback, + the behavior is undefined. + @result The value with the given key in the dictionary, or NULL if + no key-value pair with a matching key exists. Since NULL + can be a valid value in some dictionaries, the function + CFDictionaryGetValueIfPresent() must be used to distinguish + NULL-no-found from NULL-is-the-value. +*/ +CF_EXPORT +const void *CFDictionaryGetValue(CFDictionaryRef theDict, const void *key); + +/*! + @function CFDictionaryGetValueIfPresent + Retrieves the value associated with the given key. + @param theDict The dictionary to be queried. If this parameter is + not a valid CFDictionary, the behavior is undefined. + @param key The key for which to find a match in the dictionary. The + hash() and equal() key callbacks provided when the dictionary + was created are used to compare. If the hash() key callback + was NULL, the key is treated as a pointer and converted to + an integer. If the equal() key callback was NULL, pointer + equality (in C, ==) is used. If key, or any of the keys in + the dictionary, are not understood by the equal() callback, + the behavior is undefined. + @param value A pointer to memory which should be filled with the + pointer-sized value if a matching key is found. If no key + match is found, the contents of the storage pointed to by + this parameter are undefined. This parameter may be NULL, + in which case the value from the dictionary is not returned + (but the return value of this function still indicates + whether or not the key-value pair was present). + @result true, if a matching key was found, false otherwise. +*/ +CF_EXPORT +Boolean CFDictionaryGetValueIfPresent(CFDictionaryRef theDict, const void *key, const void **value); + +/*! + @function CFDictionaryGetKeysAndValues + Fills the two buffers with the keys and values from the dictionary. + @param theDict The dictionary to be queried. If this parameter is + not a valid CFDictionary, the behavior is undefined. + @param keys A C array of pointer-sized values to be filled with keys + from the dictionary. The keys and values C arrays are parallel + to each other (that is, the items at the same indices form a + key-value pair from the dictionary). This parameter may be NULL + if the keys are not desired. If this parameter is not a valid + pointer to a C array of at least CFDictionaryGetCount() pointers, + or NULL, the behavior is undefined. + @param values A C array of pointer-sized values to be filled with values + from the dictionary. The keys and values C arrays are parallel + to each other (that is, the items at the same indices form a + key-value pair from the dictionary). This parameter may be NULL + if the values are not desired. If this parameter is not a valid + pointer to a C array of at least CFDictionaryGetCount() pointers, + or NULL, the behavior is undefined. +*/ +CF_EXPORT +void CFDictionaryGetKeysAndValues(CFDictionaryRef theDict, const void **keys, const void **values); + +/*! + @function CFDictionaryApplyFunction + Calls a function once for each value in the dictionary. + @param theDict The dictionary to be queried. If this parameter is + not a valid CFDictionary, the behavior is undefined. + @param applier The callback function to call once for each value in + the dictionary. If this parameter is not a + pointer to a function of the correct prototype, the behavior + is undefined. If there are keys or values which the + applier function does not expect or cannot properly apply + to, the behavior is undefined. + @param context A pointer-sized user-defined value, which is passed + as the third parameter to the applier function, but is + otherwise unused by this function. If the context is not + what is expected by the applier function, the behavior is + undefined. +*/ +CF_EXPORT +void CFDictionaryApplyFunction(CFDictionaryRef theDict, CFDictionaryApplierFunction applier, void *context); + +/*! + @function CFDictionaryAddValue + Adds the key-value pair to the dictionary if no such key already exists. + @param theDict The dictionary to which the value is to be added. If this + parameter is not a valid mutable CFDictionary, the behavior is + undefined. + @param key The key of the value to add to the dictionary. The key is + retained by the dictionary using the retain callback provided + when the dictionary was created. If the key is not of the sort + expected by the retain callback, the behavior is undefined. If + a key which matches this key is already present in the dictionary, + this function does nothing ("add if absent"). + @param value The value to add to the dictionary. The value is retained + by the dictionary using the retain callback provided when the + dictionary was created. If the value is not of the sort expected + by the retain callback, the behavior is undefined. +*/ +CF_EXPORT +void CFDictionaryAddValue(CFMutableDictionaryRef theDict, const void *key, const void *value); + +/*! + @function CFDictionarySetValue + Sets the value of the key in the dictionary. + @param theDict The dictionary to which the value is to be set. If this + parameter is not a valid mutable CFDictionary, the behavior is + undefined. + @param key The key of the value to set into the dictionary. If a key + which matches this key is already present in the dictionary, only + the value is changed ("add if absent, replace if present"). If + no key matches the given key, the key-value pair is added to the + dictionary. If added, the key is retained by the dictionary, + using the retain callback provided + when the dictionary was created. If the key is not of the sort + expected by the key retain callback, the behavior is undefined. + @param value The value to add to or replace into the dictionary. The value + is retained by the dictionary using the retain callback provided + when the dictionary was created, and the previous value if any is + released. If the value is not of the sort expected by the + retain or release callbacks, the behavior is undefined. +*/ +CF_EXPORT +void CFDictionarySetValue(CFMutableDictionaryRef theDict, const void *key, const void *value); + +/*! + @function CFDictionaryReplaceValue + Replaces the value of the key in the dictionary. + @param theDict The dictionary to which the value is to be replaced. If this + parameter is not a valid mutable CFDictionary, the behavior is + undefined. + @param key The key of the value to replace in the dictionary. If a key + which matches this key is present in the dictionary, the value + is changed to the given value, otherwise this function does + nothing ("replace if present"). + @param value The value to replace into the dictionary. The value + is retained by the dictionary using the retain callback provided + when the dictionary was created, and the previous value is + released. If the value is not of the sort expected by the + retain or release callbacks, the behavior is undefined. +*/ +CF_EXPORT +void CFDictionaryReplaceValue(CFMutableDictionaryRef theDict, const void *key, const void *value); + +/*! + @function CFDictionaryRemoveValue + Removes the value of the key from the dictionary. + @param theDict The dictionary from which the value is to be removed. If this + parameter is not a valid mutable CFDictionary, the behavior is + undefined. + @param key The key of the value to remove from the dictionary. If a key + which matches this key is present in the dictionary, the key-value + pair is removed from the dictionary, otherwise this function does + nothing ("remove if present"). +*/ +CF_EXPORT +void CFDictionaryRemoveValue(CFMutableDictionaryRef theDict, const void *key); + +/*! + @function CFDictionaryRemoveAllValues + Removes all the values from the dictionary, making it empty. + @param theDict The dictionary from which all of the values are to be + removed. If this parameter is not a valid mutable + CFDictionary, the behavior is undefined. +*/ +CF_EXPORT +void CFDictionaryRemoveAllValues(CFMutableDictionaryRef theDict); + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFDICTIONARY__ */ +
diff --git a/CoreFoundation/CFError.c b/CoreFoundation/CFError.c new file mode 100644 index 0000000..fec7c16 --- /dev/null +++ b/CoreFoundation/CFError.c
@@ -0,0 +1,488 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFError.c + Copyright 2006, Apple, Inc. All rights reserved. + Responsibility: Ali Ozer +*/ + +#include <CoreFoundation/CFError.h> +#include <CoreFoundation/CFError_Private.h> +#include "CFInternal.h" +#include "CFPriv.h" +#if DEPLOYMENT_TARGET_MACOSX +#include <objc/runtime.h> +#include <mach/mach_error.h> +#endif + +/* Pre-defined userInfo keys +*/ +CONST_STRING_DECL(kCFErrorLocalizedDescriptionKey, "NSLocalizedDescription"); +CONST_STRING_DECL(kCFErrorLocalizedFailureReasonKey, "NSLocalizedFailureReason"); +CONST_STRING_DECL(kCFErrorLocalizedRecoverySuggestionKey, "NSLocalizedRecoverySuggestion"); +CONST_STRING_DECL(kCFErrorDescriptionKey, "NSDescription"); +CONST_STRING_DECL(kCFErrorDebugDescriptionKey, "NSDebugDescription"); +CONST_STRING_DECL(kCFErrorUnderlyingErrorKey, "NSUnderlyingError"); + +/* Pre-defined error domains +*/ +CONST_STRING_DECL(kCFErrorDomainPOSIX, "NSPOSIXErrorDomain"); +CONST_STRING_DECL(kCFErrorDomainOSStatus, "NSOSStatusErrorDomain"); +CONST_STRING_DECL(kCFErrorDomainMach, "NSMachErrorDomain"); +CONST_STRING_DECL(kCFErrorDomainCocoa, "NSCocoaErrorDomain"); + +/* We put the localized names of domain names here so genstrings can pick them out. Any additional domains that are added should be listed here if we'd like them localized. + +CFCopyLocalizedStringWithDefaultValue(CFSTR("NSMachErrorDomain"), CFSTR("Error"), NULL, CFSTR("Mach"), "Name of the 'Mach' error domain when showing to user. This probably will not get localized, unless there is a generally recognized phrase for 'Mach' in the language.") +CFCopyLocalizedStringWithDefaultValue(CFSTR("NSCoreFoundationErrorDomain"), CFSTR("Error"), NULL, CFSTR("Core Foundation"), "Name of the 'Core Foundation' error domain when showing to user. Very likely this will not get localized differently in other languages.") +CFCopyLocalizedStringWithDefaultValue(CFSTR("NSPOSIXErrorDomain"), CFSTR("Error"), NULL, CFSTR("POSIX"), "Name of the 'POSIX' error domain when showing to user. This probably will not get localized, unless there is a generally recognized phrase for 'POSIX' in the language.") +CFCopyLocalizedStringWithDefaultValue(CFSTR("NSOSStatusErrorDomain"), CFSTR("Error"), NULL, CFSTR("OSStatus"), "Name of the 'OSStatus' error domain when showing to user. Very likely this will not get localized.") +CFCopyLocalizedStringWithDefaultValue(CFSTR("NSCocoaErrorDomain"), CFSTR("Error"), NULL, CFSTR("Cocoa"), "Name of the 'Cocoa' error domain when showing to user. Very likely this will not get localized.") +*/ + + + +/* Forward declarations +*/ +static CFDictionaryRef _CFErrorGetUserInfo(CFErrorRef err); +static CFStringRef _CFErrorCopyUserInfoKey(CFErrorRef err, CFStringRef key); +static CFDictionaryRef _CFErrorCreateEmptyDictionary(CFAllocatorRef allocator); + +/* Assertions and other macros/inlines +*/ +#define __CFAssertIsError(cf) __CFGenericValidateType(cf, __kCFErrorTypeID) + +/* This lock is used in the few places in CFError where we create and access shared static objects. Should only be around tiny snippets of code; no recursion +*/ +static CFSpinLock_t _CFErrorSpinlock = CFSpinLockInit; + + + + +/**** CFError CF runtime stuff ****/ + +struct __CFError { // Potentially interesting to keep layout same as NSError (but currently not a requirement) + CFRuntimeBase _base; + CFIndex code; + CFStringRef domain; // !!! Could compress well-known domains down to few bits, but probably not worth its weight in code since CFErrors are rare + CFDictionaryRef userInfo; // !!! Could avoid allocating this slot if userInfo is NULL, but probably not worth its weight in code since CFErrors are rare +}; + +/* CFError equal checks for equality of domain, code, and userInfo. +*/ +static Boolean __CFErrorEqual(CFTypeRef cf1, CFTypeRef cf2) { + CFErrorRef err1 = (CFErrorRef)cf1; + CFErrorRef err2 = (CFErrorRef)cf2; + + // First do quick checks of code and domain (in that order for performance) + if (CFErrorGetCode(err1) != CFErrorGetCode(err2)) return false; + if (!CFEqual(CFErrorGetDomain(err1), CFErrorGetDomain(err2))) return false; + + // If those are equal, then check the dictionaries + CFDictionaryRef dict1 = CFErrorCopyUserInfo(err1); + CFDictionaryRef dict2 = CFErrorCopyUserInfo(err2); + + Boolean result = false; + + if (dict1 == dict2) { + result = true; + } else if (dict1 && dict2 && CFEqual(dict1, dict2)) { + result = true; + } + + if (dict1) CFRelease(dict1); + if (dict2) CFRelease(dict2); + + return result; +} + +/* CFError hash code is hash(domain) + code +*/ +static CFHashCode __CFErrorHash(CFTypeRef cf) { + CFErrorRef err = (CFErrorRef)cf; + /* !!! We do not need an assertion here, as this is called by the CFBase runtime only */ + return CFHash(err->domain) + err->code; +} + +/* This is the full debug description. Shows the description (possibly localized), plus the domain, code, and userInfo explicitly. If there is a debug description, shows that as well. +*/ +static CFStringRef __CFErrorCopyDescription(CFTypeRef cf) { + return _CFErrorCreateDebugDescription((CFErrorRef)cf); +} + +/* This is the description you get for %@; we tone it down a bit from what you get in __CFErrorCopyDescription(). +*/ +static CFStringRef __CFErrorCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) { + CFErrorRef err = (CFErrorRef)cf; + return CFErrorCopyDescription(err); // No need to release, since we are returning from a Copy function +} + +static void __CFErrorDeallocate(CFTypeRef cf) { + CFErrorRef err = (CFErrorRef)cf; + CFRelease(err->domain); + CFRelease(err->userInfo); +} + + +static CFTypeID __kCFErrorTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFErrorClass = { + 0, + "CFError", + NULL, // init + NULL, // copy + __CFErrorDeallocate, + __CFErrorEqual, + __CFErrorHash, + __CFErrorCopyFormattingDescription, + __CFErrorCopyDescription +}; + +__private_extern__ void __CFErrorInitialize(void) { + __kCFErrorTypeID = _CFRuntimeRegisterClass(&__CFErrorClass); +} + +CFTypeID CFErrorGetTypeID(void) { + return __kCFErrorTypeID; +} + + + + +/**** CFError support functions ****/ + +/* Returns a shared empty dictionary (unless the allocator is not kCFAllocatorSystemDefault, in which case returns a newly allocated one). +*/ +static CFDictionaryRef _CFErrorCreateEmptyDictionary(CFAllocatorRef allocator) { + if (allocator == NULL) allocator = __CFGetDefaultAllocator(); + if (allocator == kCFAllocatorSystemDefault) { + static CFDictionaryRef emptyErrorDictionary = NULL; + if (emptyErrorDictionary == NULL) { + CFDictionaryRef tmp = CFDictionaryCreate(allocator, NULL, NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + __CFSpinLock(&_CFErrorSpinlock); + if (emptyErrorDictionary == NULL) { + emptyErrorDictionary = tmp; + __CFSpinUnlock(&_CFErrorSpinlock); + } else { + __CFSpinUnlock(&_CFErrorSpinlock); + CFRelease(tmp); + } + } + return (CFDictionaryRef)CFRetain(emptyErrorDictionary); + } else { + return CFDictionaryCreate(allocator, NULL, NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + } +} + +/* A non-retained accessor for the userInfo. Might return NULL in some cases, if the subclass of NSError returned nil for some reason. It works with a CF or NSError. +*/ +static CFDictionaryRef _CFErrorGetUserInfo(CFErrorRef err) { + CF_OBJC_FUNCDISPATCH0(__kCFErrorTypeID, CFDictionaryRef, err, "userInfo"); + __CFAssertIsError(err); + return err->userInfo; +} + +/* This function retrieves the value of the specified key from the userInfo, or from the callback. It works with a CF or NSError. +*/ +static CFStringRef _CFErrorCopyUserInfoKey(CFErrorRef err, CFStringRef key) { + CFStringRef result = NULL; + // First consult the userInfo dictionary + CFDictionaryRef userInfo = _CFErrorGetUserInfo(err); + if (userInfo) result = (CFStringRef)CFDictionaryGetValue(userInfo, key); + // If that doesn't work, consult the callback + if (result) { + CFRetain(result); + } else { + CFErrorUserInfoKeyCallBack callBack = CFErrorGetCallBackForDomain(CFErrorGetDomain(err)); + if (callBack) result = (CFStringRef)callBack(err, key); + } + return result; +} + +/* The real guts of the description creation functionality. See the header file for the steps this function goes through to compute the description. This function can take a CF or NSError. It's called by NSError for the localizedDescription computation. +*/ +CFStringRef _CFErrorCreateLocalizedDescription(CFErrorRef err) { + // First look for kCFErrorLocalizedDescriptionKey; if non-NULL, return that as-is. + CFStringRef localizedDesc = _CFErrorCopyUserInfoKey(err, kCFErrorLocalizedDescriptionKey); + if (localizedDesc) return localizedDesc; + + // Cache the CF bundle since we will be using it for localized strings. !!! Might be good to check for NULL, although that indicates some serious problem. + CFBundleRef cfBundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.CoreFoundation")); + + // Then look for kCFErrorLocalizedFailureReasonKey; if there, create a full sentence from that. + CFStringRef reason = _CFErrorCopyUserInfoKey(err, kCFErrorLocalizedFailureReasonKey); + if (reason) { + CFStringRef operationFailedStr = CFCopyLocalizedStringFromTableInBundle(CFSTR("Operation could not be completed. %@"), CFSTR("Error"), cfBundle, "A generic error string indicating there was a problem. The %@ will be replaced by a second sentence which indicates why the operation failed."); + CFStringRef result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, operationFailedStr, reason); + CFRelease(operationFailedStr); + CFRelease(reason); + return result; + } + + // Otherwise, generate a semi-user presentable string from the domain, code, and if available, the presumably non-localized kCFErrorDescriptionKey. + CFStringRef result; + CFStringRef desc = _CFErrorCopyUserInfoKey(err, kCFErrorDescriptionKey); + CFStringRef localizedDomain = CFCopyLocalizedStringFromTableInBundle(CFErrorGetDomain(err), CFSTR("Error"), cfBundle, "These are localized in the comment above"); + if (desc) { // We have kCFErrorDescriptionKey, so include that with the error domain and code + CFStringRef operationFailedStr = CFCopyLocalizedStringFromTableInBundle(CFSTR("Operation could not be completed. (%@ error %ld - %@)"), CFSTR("Error"), cfBundle, "A generic error string indicating there was a problem, followed by a parenthetical sentence which indicates error domain, code, and a description when there is no other way to present an error to the user. The first %@ indicates the error domain, %ld indicates the error code, and the second %@ indicates the description; so this might become '(Mach error 42 - Server error.)' for instance."); + result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, operationFailedStr, localizedDomain, (long)CFErrorGetCode(err), desc); + CFRelease(operationFailedStr); + CFRelease(desc); + } else { // We don't have kCFErrorDescriptionKey, so just use error domain and code + CFStringRef operationFailedStr = CFCopyLocalizedStringFromTableInBundle(CFSTR("Operation could not be completed. (%@ error %ld.)"), CFSTR("Error"), cfBundle, "A generic error string indicating there was a problem, followed by a parenthetical sentence which indicates error domain and code when there is no other way to present an error to the user. The %@ indicates the error domain while %ld indicates the error code; so this might become '(Mach error 42.)' for instance."); + result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, operationFailedStr, localizedDomain, (long)CFErrorGetCode(err)); + CFRelease(operationFailedStr); + } + CFRelease(localizedDomain); + return result; +} + +/* The real guts of the failure reason creation functionality. This function can take a CF or NSError. It's called by NSError for the localizedFailureReason computation. +*/ +CFStringRef _CFErrorCreateLocalizedFailureReason(CFErrorRef err) { + // We simply return the value of kCFErrorLocalizedFailureReasonKey; no other searching takes place + return _CFErrorCopyUserInfoKey(err, kCFErrorLocalizedFailureReasonKey); +} + +/* The real guts of the recovery suggestion functionality. This function can take a CF or NSError. It's called by NSError for the localizedRecoverySuggestion computation. +*/ +CFStringRef _CFErrorCreateLocalizedRecoverySuggestion(CFErrorRef err) { + // We simply return the value of kCFErrorLocalizedRecoverySuggestionKey; no other searching takes place + return _CFErrorCopyUserInfoKey(err, kCFErrorLocalizedRecoverySuggestionKey); +} + +/* The "debug" description, used by CFCopyDescription and -[NSObject description]. +*/ +CFStringRef _CFErrorCreateDebugDescription(CFErrorRef err) { + CFStringRef desc = CFErrorCopyDescription(err); + CFStringRef debugDesc = _CFErrorCopyUserInfoKey(err, kCFErrorDebugDescriptionKey); + CFDictionaryRef userInfo = _CFErrorGetUserInfo(err); + CFMutableStringRef result = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); + CFStringAppendFormat(result, NULL, CFSTR("Error Domain=%@ Code=%d"), CFErrorGetDomain(err), (long)CFErrorGetCode(err)); + if (userInfo) CFStringAppendFormat(result, NULL, CFSTR(" UserInfo=%p"), userInfo); + CFStringAppendFormat(result, NULL, CFSTR(" \"%@\""), desc); + if (debugDesc && CFStringGetLength(debugDesc) > 0) CFStringAppendFormat(result, NULL, CFSTR(" (%@)"), debugDesc); + if (debugDesc) CFRelease(debugDesc); + if (desc) CFRelease(desc); + return result; +} + + + + +/**** CFError API/SPI ****/ + +/* Note that there are two entry points for creating CFErrors. This one does it with a presupplied userInfo dictionary. +*/ +CFErrorRef CFErrorCreate(CFAllocatorRef allocator, CFStringRef domain, CFIndex code, CFDictionaryRef userInfo) { + __CFGenericValidateType(domain, CFStringGetTypeID()); + if (userInfo) __CFGenericValidateType(userInfo, CFDictionaryGetTypeID()); + + CFErrorRef err = (CFErrorRef)_CFRuntimeCreateInstance(allocator, __kCFErrorTypeID, sizeof(struct __CFError) - sizeof(CFRuntimeBase), NULL); + if (NULL == err) return NULL; + + err->domain = CFStringCreateCopy(allocator, domain); + err->code = code; + err->userInfo = userInfo ? CFDictionaryCreateCopy(allocator, userInfo) : _CFErrorCreateEmptyDictionary(allocator); + + return err; +} + +/* Note that there are two entry points for creating CFErrors. This one does it with individual keys and values which are used to create the userInfo dictionary. +*/ +CFErrorRef CFErrorCreateWithUserInfoKeysAndValues(CFAllocatorRef allocator, CFStringRef domain, CFIndex code, const void *const *userInfoKeys, const void *const *userInfoValues, CFIndex numUserInfoValues) { + __CFGenericValidateType(domain, CFStringGetTypeID()); + + CFErrorRef err = (CFErrorRef)_CFRuntimeCreateInstance(allocator, __kCFErrorTypeID, sizeof(struct __CFError) - sizeof(CFRuntimeBase), NULL); + if (NULL == err) return NULL; + + err->domain = CFStringCreateCopy(allocator, domain); + err->code = code; + err->userInfo = CFDictionaryCreate(allocator, (const void **)userInfoKeys, (const void **)userInfoValues, numUserInfoValues, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + return err; +} + +CFStringRef CFErrorGetDomain(CFErrorRef err) { + CF_OBJC_FUNCDISPATCH0(__kCFErrorTypeID, CFStringRef, err, "domain"); + __CFAssertIsError(err); + return err->domain; +} + +CFIndex CFErrorGetCode(CFErrorRef err) { + CF_OBJC_FUNCDISPATCH0(__kCFErrorTypeID, CFIndex, err, "code"); + __CFAssertIsError(err); + return err->code; +} + +/* This accessor never returns NULL. For usage inside this file, consider __CFErrorGetUserInfo(). +*/ +CFDictionaryRef CFErrorCopyUserInfo(CFErrorRef err) { + CFDictionaryRef userInfo = _CFErrorGetUserInfo(err); + return userInfo ? (CFDictionaryRef)CFRetain(userInfo) : _CFErrorCreateEmptyDictionary(CFGetAllocator(err)); +} + +CFStringRef CFErrorCopyDescription(CFErrorRef err) { + if (CF_IS_OBJC(__kCFErrorTypeID, err)) { // Since we have to return a retained result, we need to treat the toll-free bridging specially + CFStringRef desc; + CF_OBJC_CALL0(CFStringRef, desc, err, "localizedDescription"); + return desc ? (CFStringRef)CFRetain(desc) : NULL; // !!! It really should never return nil. + } + __CFAssertIsError(err); + return _CFErrorCreateLocalizedDescription(err); +} + +CFStringRef CFErrorCopyFailureReason(CFErrorRef err) { + if (CF_IS_OBJC(__kCFErrorTypeID, err)) { // Since we have to return a retained result, we need to treat the toll-free bridging specially + CFStringRef str; + CF_OBJC_CALL0(CFStringRef, str, err, "localizedFailureReason"); + return str ? (CFStringRef)CFRetain(str) : NULL; // It's possible for localizedFailureReason to return nil + } + __CFAssertIsError(err); + return _CFErrorCreateLocalizedFailureReason(err); +} + +CFStringRef CFErrorCopyRecoverySuggestion(CFErrorRef err) { + if (CF_IS_OBJC(__kCFErrorTypeID, err)) { // Since we have to return a retained result, we need to treat the toll-free bridging specially + CFStringRef str; + CF_OBJC_CALL0(CFStringRef, str, err, "localizedRecoverySuggestion"); + return str ? (CFStringRef)CFRetain(str) : NULL; // It's possible for localizedRecoverySuggestion to return nil + } + __CFAssertIsError(err); + return _CFErrorCreateLocalizedRecoverySuggestion(err); +} + + +/**** CFError CallBack management ****/ + +/* Domain-to-callback mapping dictionary +*/ +static CFMutableDictionaryRef _CFErrorCallBackTable = NULL; + + +/* Built-in callback for POSIX domain. Note that we will pick up localizations from ErrnoErrors.strings in /System/Library/CoreServices/CoreTypes.bundle, if the file happens to be there. +*/ +static CFTypeRef _CFErrorPOSIXCallBack(CFErrorRef err, CFStringRef key) { + if (!CFEqual(key, kCFErrorDescriptionKey) && !CFEqual(key, kCFErrorLocalizedFailureReasonKey)) return NULL; + + const char *errCStr = strerror(CFErrorGetCode(err)); + CFStringRef errStr = (errCStr && strlen(errCStr)) ? CFStringCreateWithCString(kCFAllocatorSystemDefault, errCStr, kCFStringEncodingUTF8) : NULL; + + if (!errStr) return NULL; + if (CFEqual(key, kCFErrorDescriptionKey)) return errStr; // If all we wanted was the non-localized description, we're done + + // We need a kCFErrorLocalizedFailureReasonKey, so look up a possible localization for the error message + // Look for the bundle in /System/Library/CoreServices/CoreTypes.bundle + CFArrayRef paths = CFCopySearchPathForDirectoriesInDomains(kCFLibraryDirectory, kCFSystemDomainMask, false); + if (paths) { + if (CFArrayGetCount(paths) > 0) { + CFStringRef path = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@/CoreServices/CoreTypes.bundle"), CFArrayGetValueAtIndex(paths, 0)); + CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, path, kCFURLPOSIXPathStyle, false /* not a directory */); + if (url) { + CFBundleRef bundle = CFBundleCreate(kCFAllocatorSystemDefault, url); + if (bundle) { + // We only want to return a result if there was a localization + CFStringRef localizedErrStr = CFBundleCopyLocalizedString(bundle, errStr, errStr, CFSTR("ErrnoErrors")); + if (localizedErrStr == errStr) { + CFRelease(localizedErrStr); + CFRelease(errStr); + errStr = NULL; + } else { + CFRelease(errStr); + errStr = localizedErrStr; + } + CFRelease(bundle); + } + CFRelease(url); + } + CFRelease(path); + } + CFRelease(paths); + } + + return errStr; +} + +#if DEPLOYMENT_TARGET_MACOSX +/* Built-in callback for Mach domain. +*/ +static CFTypeRef _CFErrorMachCallBack(CFErrorRef err, CFStringRef key) { + if (CFEqual(key, kCFErrorDescriptionKey)) { + const char *errStr = mach_error_string(CFErrorGetCode(err)); + if (errStr && strlen(errStr)) return CFStringCreateWithCString(kCFAllocatorSystemDefault, errStr, kCFStringEncodingUTF8); + } + return NULL; +} +#endif + + +/* This initialize function is meant to be called lazily, the first time a callback is registered or requested. It creates the table and registers the built-in callbacks. Clearly doing this non-lazily in _CFErrorInitialize() would be simpler, but this is a fine example of something that should not have to happen at launch time. +*/ +static void _CFErrorInitializeCallBackTable(void) { + // Create the table outside the lock + CFMutableDictionaryRef table = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, NULL); + __CFSpinLock(&_CFErrorSpinlock); + if (!_CFErrorCallBackTable) { + _CFErrorCallBackTable = table; + __CFSpinUnlock(&_CFErrorSpinlock); + } else { + __CFSpinUnlock(&_CFErrorSpinlock); + CFRelease(table); + // Note, even though the table looks like it was initialized, we go on to register the items on this thread as well, since otherwise we might consult the table before the items are actually registered. + } + CFErrorSetCallBackForDomain(kCFErrorDomainPOSIX, _CFErrorPOSIXCallBack); +#if DEPLOYMENT_TARGET_MACOSX + CFErrorSetCallBackForDomain(kCFErrorDomainMach, _CFErrorMachCallBack); +#endif +} + +void CFErrorSetCallBackForDomain(CFStringRef domainName, CFErrorUserInfoKeyCallBack callBack) { + if (!_CFErrorCallBackTable) _CFErrorInitializeCallBackTable(); + __CFSpinLock(&_CFErrorSpinlock); + if (callBack) { + CFDictionarySetValue(_CFErrorCallBackTable, domainName, (void*)callBack); + } else { + CFDictionaryRemoveValue(_CFErrorCallBackTable, domainName); + } + __CFSpinUnlock(&_CFErrorSpinlock); +} + +CFErrorUserInfoKeyCallBack CFErrorGetCallBackForDomain(CFStringRef domainName) { + if (!_CFErrorCallBackTable) _CFErrorInitializeCallBackTable(); + __CFSpinLock(&_CFErrorSpinlock); + CFErrorUserInfoKeyCallBack callBack = (CFErrorUserInfoKeyCallBack)CFDictionaryGetValue(_CFErrorCallBackTable, domainName); + __CFSpinUnlock(&_CFErrorSpinlock); + return callBack; +} + + +
diff --git a/CoreFoundation/CFError.h b/CoreFoundation/CFError.h new file mode 100644 index 0000000..c494d91 --- /dev/null +++ b/CoreFoundation/CFError.h
@@ -0,0 +1,202 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFError.h + Copyright (c) 2006-2007, Apple Inc. All rights reserved. +*/ + +/*! + @header CFError + @discussion + CFErrors are used to encompass information about errors. At minimum, errors are identified by their domain (a string) and an error code within that domain. In addition a "userInfo" dictionary supplied at creation time enables providing additional info that might be useful for the interpretation and reporting of the error. This dictionary can even contain an "underlying" error, which is wrapped as an error bubbles up through various layers. + + CFErrors have the ability to provide human-readable descriptions for the errors; in fact, they are designed to provide localizable, end-user presentable errors that can appear in the UI. CFError has a number of predefined userInfo keys to enable developers to supply the info. + + Usage recommendation for CFErrors is to return them as by-ref parameters in functions. This enables the caller to pass NULL in when they don't actually want information about the error. The presence of an error should be reported by other means, for instance a NULL or false return value from the function call proper: + + CFError *error; + if (!ReadFromFile(fd, &error)) { + ... process error ... + CFRelease(error); // If an error occurs, the returned CFError must be released. + } + + It is the responsibility of anyone returning CFErrors this way to: + - Not touch the error argument if no error occurs + - Create and assign the error for return only if the error argument is non-NULL + + In addition, it's recommended that CFErrors be used in error situations only (not status), and where there are multiple possible errors to distinguish between. For instance there is no plan to add CFErrors to existing APIs in CF which currently don't return errors; in many cases, there is one possible reason for failure, and a false or NULL return is enough to indicate it. + + CFError is toll-free bridged to NSError in Foundation. NSError in Foundation has some additional guidelines which makes it easy to automatically report errors to users and even try to recover from them. See http://developer.apple.com/documentation/Cocoa/Conceptual/ErrorHandlingCocoa/ErrorHandling/chapter_1_section_1.html for more info on NSError programming guidelines. +*/ + +#if !defined(__COREFOUNDATION_CFERROR__) +#define __COREFOUNDATION_CFERROR__ 1 + +#include <CoreFoundation/CFBase.h> +#include <CoreFoundation/CFString.h> +#include <CoreFoundation/CFDictionary.h> + +CF_EXTERN_C_BEGIN + +/*! + @typedef CFErrorRef + This is the type of a reference to CFErrors. CFErrorRef is toll-free bridged with NSError. +*/ +typedef struct __CFError * CFErrorRef; + +/*! + @function CFErrorGetTypeID + Returns the type identifier of all CFError instances. +*/ +CF_EXPORT +CFTypeID CFErrorGetTypeID(void) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + + +// Predefined domains; value of "code" will correspond to preexisting values in these domains. +CF_EXPORT const CFStringRef kCFErrorDomainPOSIX AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; +CF_EXPORT const CFStringRef kCFErrorDomainOSStatus AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; +CF_EXPORT const CFStringRef kCFErrorDomainMach AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; +CF_EXPORT const CFStringRef kCFErrorDomainCocoa AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +// Keys in userInfo for localizable, end-user presentable error messages. At minimum provide one of first two; ideally provide all three. +CF_EXPORT const CFStringRef kCFErrorLocalizedDescriptionKey AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; // Key to identify the end user-presentable description in userInfo. +CF_EXPORT const CFStringRef kCFErrorLocalizedFailureReasonKey AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; // Key to identify the end user-presentable failure reason in userInfo. +CF_EXPORT const CFStringRef kCFErrorLocalizedRecoverySuggestionKey AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; // Key to identify the end user-presentable recovery suggestion in userInfo. + +// If you do not have localizable error strings, you can provide a value for this key instead. +CF_EXPORT const CFStringRef kCFErrorDescriptionKey AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; // Key to identify the description in the userInfo dictionary. Should be a complete sentence if possible. Should not contain domain name or error code. + +// Other keys in userInfo. +CF_EXPORT const CFStringRef kCFErrorUnderlyingErrorKey AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; // Key to identify the underlying error in userInfo. + + +/*! + @function CFErrorCreate + @abstract Creates a new CFError. + @param allocator The CFAllocator which should be used to allocate memory for the error. This parameter may be NULL in which case the + current default CFAllocator is used. If this reference is not a valid CFAllocator, the behavior is undefined. + @param domain A CFString identifying the error domain. If this reference is NULL or is otherwise not a valid CFString, the behavior is undefined. + @param code A CFIndex identifying the error code. The code is interpreted within the context of the error domain. + @param userInfo A CFDictionary created with kCFCopyStringDictionaryKeyCallBacks and kCFTypeDictionaryValueCallBacks. It will be copied with CFDictionaryCreateCopy(). + If no userInfo dictionary is desired, NULL may be passed in as a convenience, in which case an empty userInfo dictionary will be assigned. + @result A reference to the new CFError. +*/ +CF_EXPORT +CFErrorRef CFErrorCreate(CFAllocatorRef allocator, CFStringRef domain, CFIndex code, CFDictionaryRef userInfo) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +/*! + @function CFErrorCreateWithUserInfoKeysAndValues + @abstract Creates a new CFError without having to create an intermediate userInfo dictionary. + @param allocator The CFAllocator which should be used to allocate memory for the error. This parameter may be NULL in which case the + current default CFAllocator is used. If this reference is not a valid CFAllocator, the behavior is undefined. + @param domain A CFString identifying the error domain. If this reference is NULL or is otherwise not a valid CFString, the behavior is undefined. + @param code A CFIndex identifying the error code. The code is interpreted within the context of the error domain. + @param userInfoKeys An array of numUserInfoValues CFStrings used as keys in creating the userInfo dictionary. NULL is valid only if numUserInfoValues is 0. + @param userInfoValues An array of numUserInfoValues CF types used as values in creating the userInfo dictionary. NULL is valid only if numUserInfoValues is 0. + @param numUserInfoValues CFIndex representing the number of keys and values in the userInfoKeys and userInfoValues arrays. + @result A reference to the new CFError. numUserInfoValues CF types are gathered from each of userInfoKeys and userInfoValues to create the userInfo dictionary. +*/ +CF_EXPORT +CFErrorRef CFErrorCreateWithUserInfoKeysAndValues(CFAllocatorRef allocator, CFStringRef domain, CFIndex code, const void *const *userInfoKeys, const void *const *userInfoValues, CFIndex numUserInfoValues) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +/*! + @function CFErrorGetDomain + @abstract Returns the error domain the CFError was created with. + @param err The CFError whose error domain is to be returned. If this reference is not a valid CFError, the behavior is undefined. + @result The error domain of the CFError. Since this is a "Get" function, the caller shouldn't CFRelease the return value. +*/ +CF_EXPORT +CFStringRef CFErrorGetDomain(CFErrorRef err) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +/*! + @function CFErrorGetCode + @abstract Returns the error code the CFError was created with. + @param err The CFError whose error code is to be returned. If this reference is not a valid CFError, the behavior is undefined. + @result The error code of the CFError (not an error return for the current call). +*/ +CF_EXPORT +CFIndex CFErrorGetCode(CFErrorRef err) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +/*! + @function CFErrorCopyUserInfo + @abstract Returns CFError userInfo dictionary. + @discussion Returns a dictionary containing the same keys and values as in the userInfo dictionary the CFError was created with. Returns an empty dictionary if NULL was supplied to CFErrorCreate(). + @param err The CFError whose error user info is to be returned. If this reference is not a valid CFError, the behavior is undefined. + @result The user info of the CFError. +*/ +CF_EXPORT +CFDictionaryRef CFErrorCopyUserInfo(CFErrorRef err) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +/*! + @function CFErrorCopyDescription + @abstract Returns a human-presentable description for the error. CFError creators should strive to make sure the return value is human-presentable and localized by providing a value for kCFErrorLocalizedDescriptionKey at the time of CFError creation. + @discussion This is a complete sentence or two which says what failed and why it failed. Rules for computing the return value: + - Look for kCFErrorLocalizedDescriptionKey in the user info and if not NULL, returns that as-is. + - Otherwise, if there is a kCFErrorLocalizedFailureReasonKey in the user info, generate an error from that. Something like: "Operation code not be completed. " + kCFErrorLocalizedFailureReasonKey + - Otherwise, generate a semi-user presentable string from kCFErrorDescriptionKey, the domain, and code. Something like: "Operation could not be completed. Error domain/code occurred. " or "Operation could not be completed. " + kCFErrorDescriptionKey + " (Error domain/code)" + Toll-free bridged NSError instances might provide additional behaviors for manufacturing a description string. Do not count on the exact contents or format of the returned string, it might change. + @param err The CFError whose description is to be returned. If this reference is not a valid CFError, the behavior is undefined. + @result A CFString with human-presentable description of the CFError. Never NULL. +*/ +CF_EXPORT +CFStringRef CFErrorCopyDescription(CFErrorRef err) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +/*! + @function CFErrorCopyFailureReason + @abstract Returns a human-presentable failure reason for the error. May return NULL. CFError creators should strive to make sure the return value is human-presentable and localized by providing a value for kCFErrorLocalizedFailureReasonKey at the time of CFError creation. + @discussion This is a complete sentence which describes why the operation failed. In many cases this will be just the "because" part of the description (but as a complete sentence, which makes localization easier). By default this looks for kCFErrorLocalizedFailureReasonKey in the user info. Toll-free bridged NSError instances might provide additional behaviors for manufacturing this value. If no user-presentable string is available, returns NULL. + Example Description: "Could not save file 'Letter' in folder 'Documents' because the volume 'MyDisk' doesn't have enough space." + Corresponding FailureReason: "The volume 'MyDisk' doesn't have enough space." + @param err The CFError whose failure reason is to be returned. If this reference is not a valid CFError, the behavior is undefined. + @result A CFString with the localized, end-user presentable failure reason of the CFError, or NULL. +*/ +CF_EXPORT +CFStringRef CFErrorCopyFailureReason(CFErrorRef err) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +/*! + @function CFErrorCopyRecoverySuggestion + @abstract Returns a human presentable recovery suggestion for the error. May return NULL. CFError creators should strive to make sure the return value is human-presentable and localized by providing a value for kCFErrorLocalizedRecoverySuggestionKey at the time of CFError creation. + @discussion This is the string that can be displayed as the "informative" (aka "secondary") message on an alert panel. By default this looks for kCFErrorLocalizedRecoverySuggestionKey in the user info. Toll-free bridged NSError instances might provide additional behaviors for manufacturing this value. If no user-presentable string is available, returns NULL. + Example Description: "Could not save file 'Letter' in folder 'Documents' because the volume 'MyDisk' doesn't have enough space." + Corresponding RecoverySuggestion: "Remove some files from the volume and try again." + @param err The CFError whose recovery suggestion is to be returned. If this reference is not a valid CFError, the behavior is undefined. + @result A CFString with the localized, end-user presentable recovery suggestion of the CFError, or NULL. +*/ +CF_EXPORT +CFStringRef CFErrorCopyRecoverySuggestion(CFErrorRef err) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + + + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFERROR__ */ +
diff --git a/CoreFoundation/CFError_Private.h b/CoreFoundation/CFError_Private.h new file mode 100644 index 0000000..0454ad5 --- /dev/null +++ b/CoreFoundation/CFError_Private.h
@@ -0,0 +1,60 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFError_Private.h + Copyright (c) 2006-2007, Apple Inc. All rights reserved. + + This is Apple-internal SPI for CFError. +*/ + +#if !defined(__COREFOUNDATION_CFERRORPRIVATE__) +#define __COREFOUNDATION_CFERRORPRIVATE__ 1 + +#include <CoreFoundation/CFError.h> + +CF_EXTERN_C_BEGIN + +/* This callback function is consulted if a key is not present in the userInfo dictionary. Note that setting a callback for the same domain again simply replaces the previous callback. Set NULL as the callback to remove it. +*/ +typedef CFTypeRef (*CFErrorUserInfoKeyCallBack)(CFErrorRef err, CFStringRef key); +CF_EXPORT void CFErrorSetCallBackForDomain(CFStringRef domainName, CFErrorUserInfoKeyCallBack callBack) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; +CF_EXPORT CFErrorUserInfoKeyCallBack CFErrorGetCallBackForDomain(CFStringRef domainName) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +/* A key for "true" debugging descriptions which should never be shown to the user. It's only used when the CFError is shown to the console, and nothing else is available. For instance the rather terse and techie OSStatus descriptions are in this boat. +*/ +CF_EXPORT const CFStringRef kCFErrorDebugDescription AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFERRORPRIVATE__ */ +
diff --git a/CoreFoundation/CFFileUtilities.c b/CoreFoundation/CFFileUtilities.c new file mode 100644 index 0000000..ae4bb62 --- /dev/null +++ b/CoreFoundation/CFFileUtilities.c
@@ -0,0 +1,783 @@ +/* + * 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@ + */ +/* CFFileUtilities.c + Copyright 1999-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include "CFInternal.h" +#include "CFPriv.h" +#include <string.h> +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX +#include <unistd.h> +#include <dirent.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <pwd.h> +#include <fcntl.h> +#elif DEPLOYMENT_TARGET_WINDOWS +/* In typical fragile fashion, order of include on Windows is important */ +#include <io.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <windows.h> +#define lseek _lseek +#define open _open +#define read _read +#define write _write +#define close _close +#endif +#include <errno.h> +#include <stdio.h> + +#if DEPLOYMENT_TARGET_WINDOWS + #define CF_OPENFLGS (_O_BINARY|_O_NOINHERIT) +#else + #define CF_OPENFLGS (0) +#endif + + +__private_extern__ CFStringRef _CFCopyExtensionForAbstractType(CFStringRef abstractType) { + return (abstractType ? (CFStringRef)CFRetain(abstractType) : NULL); +} + + +__private_extern__ Boolean _CFCreateDirectory(const char *path) { +#if DEPLOYMENT_TARGET_WINDOWS || 0 + return CreateDirectoryA(path, (LPSECURITY_ATTRIBUTES)NULL); +#else + int no_hang_fd = open("/dev/autofs_nowait", 0); + int ret = ((mkdir(path, 0777) == 0) ? true : false); + close(no_hang_fd); + return ret; +#endif +} + +__private_extern__ Boolean _CFRemoveDirectory(const char *path) { +#if DEPLOYMENT_TARGET_WINDOWS || 0 + return RemoveDirectoryA(path); +#else + int no_hang_fd = open("/dev/autofs_nowait", 0); + int ret = ((rmdir(path) == 0) ? true : false); + close(no_hang_fd); + return ret; +#endif +} + +__private_extern__ Boolean _CFDeleteFile(const char *path) { +#if DEPLOYMENT_TARGET_WINDOWS || 0 + return DeleteFileA(path); +#else + int no_hang_fd = open("/dev/autofs_nowait", 0); + int ret = unlink(path) == 0; + close(no_hang_fd); + return ret; +#endif +} + +__private_extern__ Boolean _CFReadBytesFromFile(CFAllocatorRef alloc, CFURLRef url, void **bytes, CFIndex *length, CFIndex maxLength) { + // maxLength is the number of bytes desired, or 0 if the whole file is desired regardless of length. + struct stat statBuf; + int fd = -1; + char path[CFMaxPathSize]; + if (!CFURLGetFileSystemRepresentation(url, true, (uint8_t *)path, CFMaxPathSize)) { + return false; + } + + *bytes = NULL; + +#if DEPLOYMENT_TARGET_WINDOWS || 0 + fd = open(path, O_RDONLY|CF_OPENFLGS, 0666|_S_IREAD); +#else + int no_hang_fd = open("/dev/autofs_nowait", 0); + fd = open(path, O_RDONLY|CF_OPENFLGS, 0666); +#endif + if (fd < 0) { +#if !DEPLOYMENT_TARGET_WINDOWS + close(no_hang_fd); +#endif + return false; + } + if (fstat(fd, &statBuf) < 0) { + int saveerr = thread_errno(); + close(fd); +#if !DEPLOYMENT_TARGET_WINDOWS + close(no_hang_fd); +#endif + thread_set_errno(saveerr); + return false; + } + if ((statBuf.st_mode & S_IFMT) != S_IFREG) { + close(fd); +#if !DEPLOYMENT_TARGET_WINDOWS + close(no_hang_fd); +#endif + thread_set_errno(EACCES); + return false; + } + if (statBuf.st_size == 0) { + *bytes = CFAllocatorAllocate(alloc, 4, 0); // don't return constant string -- it's freed! + if (__CFOASafe) __CFSetLastAllocationEventName(*bytes, "CFUtilities (file-bytes)"); + *length = 0; + } else { + CFIndex desiredLength; + if ((maxLength >= statBuf.st_size) || (maxLength == 0)) { + desiredLength = statBuf.st_size; + } else { + desiredLength = maxLength; + } + *bytes = CFAllocatorAllocate(alloc, desiredLength, 0); + if (__CFOASafe) __CFSetLastAllocationEventName(*bytes, "CFUtilities (file-bytes)"); +// fcntl(fd, F_NOCACHE, 1); + if (read(fd, *bytes, desiredLength) < 0) { + CFAllocatorDeallocate(alloc, *bytes); + close(fd); +#if !DEPLOYMENT_TARGET_WINDOWS + close(no_hang_fd); +#endif + return false; + } + *length = desiredLength; + } + close(fd); +#if !DEPLOYMENT_TARGET_WINDOWS + close(no_hang_fd); +#endif + return true; +} + +__private_extern__ Boolean _CFWriteBytesToFile(CFURLRef url, const void *bytes, CFIndex length) { + struct stat statBuf; + int fd = -1; + int mode; + char path[CFMaxPathSize]; + if (!CFURLGetFileSystemRepresentation(url, true, (uint8_t *)path, CFMaxPathSize)) { + return false; + } + +#if DEPLOYMENT_TARGET_WINDOWS || 0 + mode = 0666; + if (0 == stat(path, &statBuf)) { + mode = statBuf.st_mode; + } else if (thread_errno() != ENOENT) { + return false; + } + fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|CF_OPENFLGS, 0666|_S_IWRITE); + if (fd < 0) { + return false; + } + if (length && write(fd, bytes, length) != length) { + int saveerr = thread_errno(); + close(fd); + thread_set_errno(saveerr); + return false; + } + FlushFileBuffers((HANDLE)_get_osfhandle(fd)); + close(fd); +#else + int no_hang_fd = open("/dev/autofs_nowait", 0); + mode = 0666; + if (0 == stat(path, &statBuf)) { + mode = statBuf.st_mode; + } else if (thread_errno() != ENOENT) { + close(no_hang_fd); + return false; + } + fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|CF_OPENFLGS, 0666); + if (fd < 0) { + close(no_hang_fd); + return false; + } + if (length && write(fd, bytes, length) != length) { + int saveerr = thread_errno(); + close(fd); + close(no_hang_fd); + thread_set_errno(saveerr); + return false; + } + fsync(fd); + close(fd); + close(no_hang_fd); +#endif + return true; +} + + +/* On Mac OS 8/9, one of dirSpec and dirURL must be non-NULL. On all other platforms, one of path and dirURL must be non-NULL +If both are present, they are assumed to be in-synch; that is, they both refer to the same directory. */ +/* Lately, dirSpec appears to be (rightfully) unused. */ +__private_extern__ CFMutableArrayRef _CFContentsOfDirectory(CFAllocatorRef alloc, char *dirPath, void *dirSpec, CFURLRef dirURL, CFStringRef matchingAbstractType) { + CFMutableArrayRef files = NULL; + Boolean releaseBase = false; + CFIndex pathLength = dirPath ? (CFIndex)strlen(dirPath) : 0; + // MF:!!! Need to use four-letter type codes where appropriate. + CFStringRef extension = (matchingAbstractType ? _CFCopyExtensionForAbstractType(matchingAbstractType) : NULL); + CFIndex extLen = (extension ? CFStringGetLength(extension) : 0); + uint8_t extBuff[CFMaxPathSize]; + + if (extLen > 0) { + CFStringGetBytes(extension, CFRangeMake(0, extLen), CFStringFileSystemEncoding(), 0, false, extBuff, CFMaxPathLength, &extLen); + extBuff[extLen] = '\0'; + } + + uint8_t pathBuf[CFMaxPathSize]; + + if (!dirPath) { + if (!CFURLGetFileSystemRepresentation(dirURL, true, pathBuf, CFMaxPathLength)) { + if (extension) CFRelease(extension); + return NULL; + } else { + dirPath = (char *)pathBuf; + pathLength = (CFIndex)strlen(dirPath); + } + } + +#if (DEPLOYMENT_TARGET_MACOSX) || defined(__svr4__) || defined(__hpux__) || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD + struct dirent buffer; + struct dirent *dp; + int err; + + int no_hang_fd = open("/dev/autofs_nowait", 0); + + DIR *dirp = opendir(dirPath); + if (!dirp) { + if (extension) { + CFRelease(extension); + } + close(no_hang_fd); + return NULL; + // raiseErrno("opendir", path); + } + files = CFArrayCreateMutable(alloc, 0, & kCFTypeArrayCallBacks); + + while((0 == readdir_r(dirp, &buffer, &dp)) && dp) { + CFURLRef fileURL; + unsigned namelen = strlen(dp->d_name); + + // skip . & ..; they cause descenders to go berserk + if (dp->d_name[0] == '.' && (namelen == 1 || (namelen == 2 && dp->d_name[1] == '.'))) { + continue; + } + + if (extLen > namelen) continue; // if the extension is the same length or longer than the name, it can't possibly match. + + if (extLen > 0) { + // Check to see if it matches the extension we're looking for. + if (strncmp(&(dp->d_name[namelen - extLen]), (char *)extBuff, extLen) != 0) { + continue; + } + } + if (dirURL == NULL) { + dirURL = CFURLCreateFromFileSystemRepresentation(alloc, (uint8_t *)dirPath, pathLength, true); + releaseBase = true; + } + if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN) { + Boolean isDir = (dp->d_type == DT_DIR); + if (!isDir) { + // Ugh; must stat. + char subdirPath[CFMaxPathLength]; + struct stat statBuf; + strlcpy(subdirPath, dirPath, sizeof(subdirPath)); + strlcat(subdirPath, "/", sizeof(subdirPath)); + strlcat(subdirPath, dp->d_name, sizeof(subdirPath)); + if (stat(subdirPath, &statBuf) == 0) { + isDir = ((statBuf.st_mode & S_IFMT) == S_IFDIR); + } + } + fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase(alloc, (uint8_t *)dp->d_name, namelen, isDir, dirURL); + } else { + fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase (alloc, (uint8_t *)dp->d_name, namelen, false, dirURL); + } + CFArrayAppendValue(files, fileURL); + CFRelease(fileURL); + } + err = closedir(dirp); + close(no_hang_fd); + if (err != 0) { + CFRelease(files); + if (releaseBase) { + CFRelease(dirURL); + } + if (extension) { + CFRelease(extension); + } + return NULL; + } + +#elif DEPLOYMENT_TARGET_WINDOWS + + WIN32_FIND_DATAA file; + HANDLE handle; + + if (pathLength + 2 >= CFMaxPathLength) { + if (extension) { + CFRelease(extension); + } + return NULL; + } + if (NULL != dirPath) { + dirPath[pathLength] = '\''; + dirPath[pathLength + 1] = '*'; + dirPath[pathLength + 2] = '\0'; + handle = FindFirstFileA(dirPath, &file); + if (INVALID_HANDLE_VALUE == handle) { + dirPath[pathLength] = '\0'; + if (extension) { + CFRelease(extension); + } + return NULL; + } + } else { + pathLength = 0; + } + files = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks); + + do { + CFURLRef fileURL; + CFIndex namelen = (CFIndex)strlen(file.cFileName); + if (file.cFileName[0] == '.' && (namelen == 1 || (namelen == 2 && file.cFileName[1] == '.'))) { + continue; + } + if (extLen > 0) { + // Check to see if it matches the extension we're looking for. + if (_stricmp((char*)&(file.cFileName[namelen - extLen]), (char*)extBuff) != 0) { + continue; + } + } + if (dirURL == NULL) { + dirURL = CFURLCreateFromFileSystemRepresentation(alloc, (UInt8*)dirPath, pathLength, true); + releaseBase = true; + } + // MF:!!! What about the trailing slash? + fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase(alloc, (UInt8*)file.cFileName, namelen, false, dirURL); + CFArrayAppendValue(files, fileURL); + CFRelease(fileURL); + } while (FindNextFileA(handle, &file)); + FindClose(handle); + dirPath[pathLength] = '\0'; + +#else + +#error _CFContentsOfDirectory() unknown architechture, not implemented + +#endif + + if (extension) { + CFRelease(extension); + } + if (releaseBase) { + CFRelease(dirURL); + } + return files; +} + +__private_extern__ SInt32 _CFGetFileProperties(CFAllocatorRef alloc, CFURLRef pathURL, Boolean *exists, SInt32 *posixMode, int64_t *size, CFDateRef *modTime, SInt32 *ownerID, CFArrayRef *dirContents) { + Boolean fileExists; + Boolean isDirectory = false; + +#if DEPLOYMENT_TARGET_WINDOWS + struct _stati64 statBuf; + int fd = -1; +#else + struct stat64 statBuf; +#endif + char path[CFMaxPathSize]; + + if ((exists == NULL) && (posixMode == NULL) && (size == NULL) && (modTime == NULL) && (ownerID == NULL) && (dirContents == NULL)) { + // Nothing to do. + return 0; + } + + if (!CFURLGetFileSystemRepresentation(pathURL, true, (uint8_t *)path, CFMaxPathLength)) { + return -1; + } + +#if DEPLOYMENT_TARGET_WINDOWS + HANDLE hFile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + if (INVALID_HANDLE_VALUE == hFile) + return -1; + + fd = _open_osfhandle((intptr_t)hFile, _O_RDONLY|CF_OPENFLGS); + if (fd < 0) { + if (thread_errno() == ENOENT) { + fileExists = false; + } else { + CloseHandle (hFile); + return thread_errno(); + } + } + else + { + if (_fstati64(fd, &statBuf) != 0) { + close(fd); + CloseHandle (hFile); + // stat failed, but why? + if (thread_errno() == ENOENT) { + fileExists = false; + } else { + return thread_errno(); + } + } else { + close(fd); + CloseHandle (hFile); + fileExists = true; + isDirectory = ((statBuf.st_mode & S_IFMT) == S_IFDIR); + } + } +#else + if (stat64(path, &statBuf) != 0) { + // stat failed, but why? + if (thread_errno() == ENOENT) { + fileExists = false; + } else { + return thread_errno(); + } + } else { + fileExists = true; + isDirectory = ((statBuf.st_mode & S_IFMT) == S_IFDIR); + } +#endif + + if (exists != NULL) { + *exists = fileExists; + } + + if (posixMode != NULL) { + if (fileExists) { + + *posixMode = statBuf.st_mode; + + } else { + *posixMode = 0; + } + } + + if (size != NULL) { + if (fileExists) { + + *size = statBuf.st_size; + + } else { + *size = 0; + } + } + + if (modTime != NULL) { + if (fileExists) { +#if DEPLOYMENT_TARGET_WINDOWS + CFAbsoluteTime theTime = (CFAbsoluteTime)statBuf.st_mtime; +#elif DEPLOYMENT_TARGET_LINUX +#if defined _BSD_SOURCE || defined _SVID_SOURCE + CFAbsoluteTime theTime = (CFAbsoluteTime)statBuf.st_mtim.tv_sec - kCFAbsoluteTimeIntervalSince1970; + theTime += (CFAbsoluteTime)statBuf.st_mtim.tv_nsec / 1000000000.0; +#else + CFAbsoluteTime theTime = (CFAbsoluteTime)statBuf.st_mtime - kCFAbsoluteTimeIntervalSince1970; + theTime += (CFAbsoluteTime)statBuf.st_mtimensec / 1000000000.0; +#endif /* defined _BSD_SOURCE || defined _SVID_SOURCE */ +#else + CFAbsoluteTime theTime = (CFAbsoluteTime)statBuf.st_mtimespec.tv_sec - kCFAbsoluteTimeIntervalSince1970; + theTime += (CFAbsoluteTime)statBuf.st_mtimespec.tv_nsec / 1000000000.0; +#endif + *modTime = CFDateCreate(alloc, theTime); + } else { + *modTime = NULL; + } + } + + if (ownerID != NULL) { + if (fileExists) { + + *ownerID = statBuf.st_uid; + + } else { + *ownerID = -1; + } + } + + if (dirContents != NULL) { + if (fileExists && isDirectory) { + + CFMutableArrayRef contents = _CFContentsOfDirectory(alloc, path, NULL, pathURL, NULL); + + if (contents) { + *dirContents = contents; + } else { + *dirContents = NULL; + } + } else { + *dirContents = NULL; + } + } + return 0; +} + + +// MF:!!! Should pull in the rest of the UniChar based path utils from Foundation. +#if (DEPLOYMENT_TARGET_MACOSX) || defined(__svr4__) || defined(__hpux__) || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD + #define UNIX_PATH_SEMANTICS +#elif DEPLOYMENT_TARGET_WINDOWS + #define WINDOWS_PATH_SEMANTICS +#else +#error Unknown platform +#endif + +#if defined(WINDOWS_PATH_SEMANTICS) + #define CFPreferredSlash ((UniChar)'\\') +#elif defined(UNIX_PATH_SEMANTICS) + #define CFPreferredSlash ((UniChar)'/') +#elif defined(HFS_PATH_SEMANTICS) + #define CFPreferredSlash ((UniChar)':') +#else + #error Cannot define NSPreferredSlash on this platform +#endif + +#if defined(HFS_PATH_SEMANTICS) +#define HAS_DRIVE(S) (false) +#define HAS_NET(S) (false) +#else +#define HAS_DRIVE(S) ((S)[1] == ':' && (('A' <= (S)[0] && (S)[0] <= 'Z') || ('a' <= (S)[0] && (S)[0] <= 'z'))) +#define HAS_NET(S) ((S)[0] == '\\' && (S)[1] == '\\') +#endif + +#if defined(WINDOWS_PATH_SEMANTICS) + #define IS_SLASH(C) ((C) == '\\' || (C) == '/') +#elif defined(UNIX_PATH_SEMANTICS) + #define IS_SLASH(C) ((C) == '/') +#elif defined(HFS_PATH_SEMANTICS) + #define IS_SLASH(C) ((C) == ':') +#endif + +__private_extern__ Boolean _CFIsAbsolutePath(UniChar *unichars, CFIndex length) { + if (length < 1) { + return false; + } +#if defined(WINDOWS_PATH_SEMANTICS) + if (unichars[0] == '~') { + return true; + } + if (length < 2) { + return false; + } + if (HAS_NET(unichars)) { + return true; + } + if (length < 3) { + return false; + } + if (IS_SLASH(unichars[2]) && HAS_DRIVE(unichars)) { + return true; + } +#elif defined(HFS_PATH_SEMANTICS) + return !IS_SLASH(unichars[0]); +#else + if (unichars[0] == '~') { + return true; + } + if (IS_SLASH(unichars[0])) { + return true; + } +#endif + return false; +} + +__private_extern__ Boolean _CFStripTrailingPathSlashes(UniChar *unichars, CFIndex *length) { + Boolean destHasDrive = (1 < *length) && HAS_DRIVE(unichars); + CFIndex oldLength = *length; + while (((destHasDrive && 3 < *length) || (!destHasDrive && 1 < *length)) && IS_SLASH(unichars[*length - 1])) { + (*length)--; + } + return (oldLength != *length); +} + +__private_extern__ Boolean _CFAppendPathComponent(UniChar *unichars, CFIndex *length, CFIndex maxLength, UniChar *component, CFIndex componentLength) { + if (0 == componentLength) { + return true; + } + if (maxLength < *length + 1 + componentLength) { + return false; + } + switch (*length) { + case 0: + break; + case 1: + if (!IS_SLASH(unichars[0])) { + unichars[(*length)++] = CFPreferredSlash; + } + break; + case 2: + if (!HAS_DRIVE(unichars) && !HAS_NET(unichars)) { + unichars[(*length)++] = CFPreferredSlash; + } + break; + default: + unichars[(*length)++] = CFPreferredSlash; + break; + } + memmove(unichars + *length, component, componentLength * sizeof(UniChar)); + *length += componentLength; + return true; +} + +__private_extern__ Boolean _CFAppendPathExtension(UniChar *unichars, CFIndex *length, CFIndex maxLength, UniChar *extension, CFIndex extensionLength) { + if (maxLength < *length + 1 + extensionLength) { + return false; + } + if ((0 < extensionLength && IS_SLASH(extension[0])) || (1 < extensionLength && HAS_DRIVE(extension))) { + return false; + } + _CFStripTrailingPathSlashes(unichars, length); + switch (*length) { + case 0: + return false; + case 1: + if (IS_SLASH(unichars[0]) || unichars[0] == '~') { + return false; + } + break; + case 2: + if (HAS_DRIVE(unichars) || HAS_NET(unichars)) { + return false; + } + break; + case 3: + if (IS_SLASH(unichars[2]) && HAS_DRIVE(unichars)) { + return false; + } + break; + } + if (0 < *length && unichars[0] == '~') { + CFIndex idx; + Boolean hasSlash = false; + for (idx = 1; idx < *length; idx++) { + if (IS_SLASH(unichars[idx])) { + hasSlash = true; + break; + } + } + if (!hasSlash) { + return false; + } + } + unichars[(*length)++] = '.'; + memmove(unichars + *length, extension, extensionLength * sizeof(UniChar)); + *length += extensionLength; + return true; +} + +__private_extern__ Boolean _CFTransmutePathSlashes(UniChar *unichars, CFIndex *length, UniChar replSlash) { + CFIndex didx, sidx, scnt = *length; + sidx = (1 < *length && HAS_NET(unichars)) ? 2 : 0; + didx = sidx; + while (sidx < scnt) { + if (IS_SLASH(unichars[sidx])) { + unichars[didx++] = replSlash; + for (sidx++; sidx < scnt && IS_SLASH(unichars[sidx]); sidx++); + } else { + unichars[didx++] = unichars[sidx++]; + } + } + *length = didx; + return (scnt != didx); +} + +__private_extern__ CFIndex _CFStartOfLastPathComponent(UniChar *unichars, CFIndex length) { + CFIndex idx; + if (length < 2) { + return 0; + } + for (idx = length - 1; idx; idx--) { + if (IS_SLASH(unichars[idx - 1])) { + return idx; + } + } + if ((2 < length) && HAS_DRIVE(unichars)) { + return 2; + } + return 0; +} + +__private_extern__ CFIndex _CFLengthAfterDeletingLastPathComponent(UniChar *unichars, CFIndex length) { + CFIndex idx; + if (length < 2) { + return 0; + } + for (idx = length - 1; idx; idx--) { + if (IS_SLASH(unichars[idx - 1])) { + if ((idx != 1) && (!HAS_DRIVE(unichars) || idx != 3)) { + return idx - 1; + } + return idx; + } + } + if ((2 < length) && HAS_DRIVE(unichars)) { + return 2; + } + return 0; +} + +__private_extern__ CFIndex _CFStartOfPathExtension(UniChar *unichars, CFIndex length) { + CFIndex idx; + if (length < 2) { + return 0; + } + for (idx = length - 1; idx; idx--) { + if (IS_SLASH(unichars[idx - 1])) { + return 0; + } + if (unichars[idx] != '.') { + continue; + } + if (idx == 2 && HAS_DRIVE(unichars)) { + return 0; + } + return idx; + } + return 0; +} + +__private_extern__ CFIndex _CFLengthAfterDeletingPathExtension(UniChar *unichars, CFIndex length) { + CFIndex start = _CFStartOfPathExtension(unichars, length); + return ((0 < start) ? start : length); +} + +#undef CF_OPENFLGS +#undef UNIX_PATH_SEMANTICS +#undef WINDOWS_PATH_SEMANTICS +#undef HFS_PATH_SEMANTICS +#undef CFPreferredSlash +#undef HAS_DRIVE +#undef HAS_NET +#undef IS_SLASH +
diff --git a/CoreFoundation/CFInternal.h b/CoreFoundation/CFInternal.h new file mode 100644 index 0000000..0c9f709 --- /dev/null +++ b/CoreFoundation/CFInternal.h
@@ -0,0 +1,780 @@ +/* + * 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@ + */ +/* CFInternal.h + Copyright (c) 1998-2007, Apple Inc. All rights reserved. +*/ + +/* + NOT TO BE USED OUTSIDE CF! +*/ + +#if !CF_BUILDING_CF + #error The header file CFInternal.h is for the exclusive use of CoreFoundation. No other project should include it. +#endif + +#if !defined(__COREFOUNDATION_CFINTERNAL__) +#define __COREFOUNDATION_CFINTERNAL__ 1 + +#include <CoreFoundation/CFBase.h> +#include <CoreFoundation/CFURL.h> +#include <CoreFoundation/CFString.h> +#include <CoreFoundation/CFDate.h> +#include <CoreFoundation/CFArray.h> +#include <CoreFoundation/CFRunLoop.h> +#include <CoreFoundation/CFStorage.h> +#include "CFLogUtilities.h" +#include "CFRuntime.h" +#if defined(__MACH__) +#include <xlocale.h> +#include <mach/mach_time.h> +#elif DEPLOYMENT_TARGET_WINDOWS +#include <windows.h> +#include <malloc.h> +#endif +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD +#include <sys/time.h> +#include <pthread.h> +#endif +#include <limits.h> +#include "auto_stubs.h" +#ifdef DEPLOYMENT_TARGET_MACOSX +#include <libkern/OSAtomic.h> +#endif //DEPLOYMENT_TARGET_MACOSX + +#if !(DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_FREEBSD) +extern int flsl(long mask); +#endif + +#if DEPLOYMENT_TARGET_WINDOWS +#define __PRETTY_FUNCTION__ __FUNCTION__ +#define __builtin_expect(expr, val) (expr) +#endif + +#if defined(__BIG_ENDIAN__) +#define __CF_BIG_ENDIAN__ 1 +#define __CF_LITTLE_ENDIAN__ 0 +#endif + +#if defined(__LITTLE_ENDIAN__) +#define __CF_LITTLE_ENDIAN__ 1 +#define __CF_BIG_ENDIAN__ 0 +#endif + + +#include "ForFoundationOnly.h" + +CF_EXPORT const char *_CFProcessName(void); +CF_EXPORT CFStringRef _CFProcessNameString(void); + +CF_EXPORT Boolean _CFIsCFM(void); + +CF_EXPORT Boolean _CFGetCurrentDirectory(char *path, int maxlen); + +CF_EXPORT CFStringRef _CFGetUserName(void); + +CF_EXPORT CFArrayRef _CFGetWindowsBinaryDirectories(void); + +CF_EXPORT CFStringRef _CFStringCreateHostName(void); + +#if DEPLOYMENT_TARGET_MACOSX +CF_EXPORT void _CFMachPortInstallNotifyPort(CFRunLoopRef rl, CFStringRef mode); +#endif + +#if defined(__ppc__) || defined(__ppc64__) || defined(__powerpc__) + #define HALT asm __volatile__("trap") +#elif defined(__i386__) || defined(__x86_64__) + #if defined(__GNUC__) + #define HALT __asm__ __volatile__("int3") + #elif defined(_MSC_VER) + #define HALT __asm int 3; + #else + #error Compiler not supported + #endif +#elif defined(__arm__) + #define HALT asm __volatile__("bkpt") +#elif defined(__aarch64__) + #define HALT asm __volatile__("brk #0000") +#endif + +#if defined(DEBUG) + #define __CFAssert(cond, prio, desc, a1, a2, a3, a4, a5) \ + do { \ + if (!(cond)) { \ + CFLog(prio, CFSTR(desc), a1, a2, a3, a4, a5); \ + /* HALT; */ \ + } \ + } while (0) +#else + #define __CFAssert(cond, prio, desc, a1, a2, a3, a4, a5) \ + do {} while (0) +#endif + +#define CFAssert(condition, priority, description) \ + __CFAssert((condition), (priority), description, 0, 0, 0, 0, 0) +#define CFAssert1(condition, priority, description, a1) \ + __CFAssert((condition), (priority), description, (a1), 0, 0, 0, 0) +#define CFAssert2(condition, priority, description, a1, a2) \ + __CFAssert((condition), (priority), description, (a1), (a2), 0, 0, 0) +#define CFAssert3(condition, priority, description, a1, a2, a3) \ + __CFAssert((condition), (priority), description, (a1), (a2), (a3), 0, 0) +#define CFAssert4(condition, priority, description, a1, a2, a3, a4) \ + __CFAssert((condition), (priority), description, (a1), (a2), (a3), (a4), 0) + +#define __kCFLogAssertion 3 + +#if defined(DEBUG) +extern void __CFGenericValidateType_(CFTypeRef cf, CFTypeID type, const char *func); +#define __CFGenericValidateType(cf, type) __CFGenericValidateType_(cf, type, __PRETTY_FUNCTION__) +#else +#define __CFGenericValidateType(cf, type) ((void)0) +#endif + +#define CF_INFO_BITS (!!(__CF_BIG_ENDIAN__) * 3) +#define CF_RC_BITS (!!(__CF_LITTLE_ENDIAN__) * 3) + +/* Bit manipulation macros */ +/* Bits are numbered from 31 on left to 0 on right */ +/* May or may not work if you use them on bitfields in types other than UInt32, bitfields the full width of a UInt32, or anything else for which they were not designed. */ +/* In the following, N1 and N2 specify an inclusive range N2..N1 with N1 >= N2 */ +#define __CFBitfieldMask(N1, N2) ((((UInt32)~0UL) << (31UL - (N1) + (N2))) >> (31UL - N1)) +#define __CFBitfieldGetValue(V, N1, N2) (((V) & __CFBitfieldMask(N1, N2)) >> (N2)) +#define __CFBitfieldSetValue(V, N1, N2, X) ((V) = ((V) & ~__CFBitfieldMask(N1, N2)) | (((X) << (N2)) & __CFBitfieldMask(N1, N2))) +#define __CFBitfieldMaxValue(N1, N2) __CFBitfieldGetValue(0xFFFFFFFFUL, (N1), (N2)) + +#define __CFBitIsSet(V, N) (((V) & (1UL << (N))) != 0) +#define __CFBitSet(V, N) ((V) |= (1UL << (N))) +#define __CFBitClear(V, N) ((V) &= ~(1UL << (N))) + +#if DEPLOYMENT_TARGET_WINDOWS +CF_INLINE bool _CFAtomicCompareAndSwap32Barrier(int32_t oldValue, int32_t newValue, volatile int32_t *theValue) { + int32_t actualOldValue = InterlockedCompareExchange((volatile LONG *)theValue, newValue, oldValue); + return actualOldValue == oldValue ? true : false; +} +CF_INLINE bool _CFAtomicCompareAndSwapPtrBarrier(void* oldValue, void* newValue, void* volatile *theValue) { + void *actualOldValue = InterlockedCompareExchangePointer((volatile PVOID*)theValue, newValue, (PVOID)oldValue); + return actualOldValue == oldValue ? true : false; +} +CF_INLINE int32_t _CFAtomicIncrement32(volatile int32_t *theValue) { + return (unsigned int)InterlockedIncrement((volatile LONG*)theValue); +} +CF_INLINE void _CFMemoryBarrier(void) { + MemoryBarrier(); +} +#elif DEPLOYMENT_TARGET_MACOSX +CF_INLINE bool _CFAtomicCompareAndSwap32Barrier(int32_t __oldValue, int32_t __newValue, volatile int32_t *__theValue) { + return OSAtomicCompareAndSwap32Barrier(__oldValue, __newValue, __theValue); +} +CF_INLINE bool _CFAtomicCompareAndSwapPtrBarrier(void* __oldValue, void* __newValue, void* volatile *__theValue) { + return OSAtomicCompareAndSwapPtrBarrier(__oldValue, __newValue, __theValue); +} +CF_INLINE int32_t _CFAtomicIncrement32(volatile int32_t *theValue) { + return OSAtomicIncrement32(theValue); +} +CF_INLINE void _CFMemoryBarrier(void) { + OSMemoryBarrier(); +} +#elif DEPLOYMENT_TARGET_LINUX +// Simply leverage GCC's atomic built-ins (see http://gcc.gnu.org/onlinedocs/gcc-4.1.0/gcc/Atomic-Builtins.html) +CF_INLINE bool _CFAtomicCompareAndSwap32Barrier(int32_t __oldValue, int32_t __newValue, volatile int32_t *__theValue) { + return __sync_bool_compare_and_swap(__theValue, __oldValue, __newValue); +} +CF_INLINE bool _CFAtomicCompareAndSwapPtrBarrier(void* __oldValue, void* __newValue, void* volatile *__theValue) { + return __sync_bool_compare_and_swap(__theValue, __oldValue, __newValue); +} +CF_INLINE int32_t _CFAtomicIncrement32(volatile int32_t *theValue) { + return __sync_fetch_and_add(theValue, 1); +} +CF_INLINE void _CFMemoryBarrier(void) { + __sync_synchronize(); +} +#else +#error "Don't know how to perform atomic operations." +#endif + +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_FREEBSD +#define _CFIsSetUgid() issetugid() +#elif DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_SOLARIS +#include <unistd.h> +CF_INLINE bool _CFIsSetUgid(void) { + return (getuid() != geteuid() || getgid() != getegid()); +} +#elif DEPLOYMENT_TARGET_WINDOWS +#define _CFIsSetUgid() 0 +#else +#error "Don't know how to determine a setuid or setgid executable." +#endif /* DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_FREEBSD */ + + +typedef struct ___CFThreadSpecificData { + void *_unused1; + void *_allocator; +#if DEPLOYMENT_TARGET_WINDOWS + HHOOK _messageHook; +#endif +// If you add things to this struct, add cleanup to __CFFinalizeThreadData() +} __CFThreadSpecificData; + +extern __CFThreadSpecificData *__CFGetThreadSpecificData(void); +__private_extern__ void __CFFinalizeThreadData(void *arg); + +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD +extern pthread_key_t __CFTSDKey; +#elif DEPLOYMENT_TARGET_WINDOWS +extern DWORD __CFTSDKey; +#endif + +//extern void *pthread_getspecific(pthread_key_t key); + +CF_INLINE __CFThreadSpecificData *__CFGetThreadSpecificData_inline(void) { +#if DEPLOYMENT_TARGET_MACOSX|| DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD + __CFThreadSpecificData *data = (__CFThreadSpecificData *)pthread_getspecific(__CFTSDKey); + return data ? data : __CFGetThreadSpecificData(); +#elif DEPLOYMENT_TARGET_WINDOWS + __CFThreadSpecificData *data = (__CFThreadSpecificData *)TlsGetValue(__CFTSDKey); + return data ? data : __CFGetThreadSpecificData(); +#endif +} + +#define __kCFAllocatorTypeID_CONST 2 + +CF_INLINE CFAllocatorRef __CFGetDefaultAllocator(void) { + CFAllocatorRef allocator = (CFAllocatorRef)__CFGetThreadSpecificData_inline()->_allocator; + if (NULL == allocator) { + allocator = kCFAllocatorSystemDefault; + } + return allocator; +} + +extern CFTypeID __CFGenericTypeID(const void *cf); + +// This should only be used in CF types, not toll-free bridged objects! +// It should not be used with CFAllocator arguments! +// Use CFGetAllocator() in the general case, and this inline function in a few limited (but often called) situations. +CF_INLINE CFAllocatorRef __CFGetAllocator(CFTypeRef cf) { // !!! Use with CF types only, and NOT WITH CFAllocator! + CFAssert1(__kCFAllocatorTypeID_CONST != __CFGenericTypeID(cf), __kCFLogAssertion, "__CFGetAllocator(): CFAllocator argument", cf); + if (__builtin_expect(__CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 7, 7), 1)) { + return kCFAllocatorSystemDefault; + } + return *(CFAllocatorRef *)((char *)cf - sizeof(CFAllocatorRef)); +} + +// Don't define a __CFGetCurrentRunLoop(), because even internal clients should go through the real one + + +#if !defined(LLONG_MAX) + #if defined(_I64_MAX) + #define LLONG_MAX _I64_MAX + #else + #warning Arbitrarily defining LLONG_MAX + #define LLONG_MAX (int64_t)9223372036854775807 + #endif +#endif /* !defined(LLONG_MAX) */ + +#if !defined(LLONG_MIN) + #if defined(_I64_MIN) + #define LLONG_MIN _I64_MIN + #else + #warning Arbitrarily defining LLONG_MIN + #define LLONG_MIN (-LLONG_MAX - (int64_t)1) + #endif +#endif /* !defined(LLONG_MIN) */ + +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) + #define __CFMin(A,B) ({__typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __a : __b; }) + #define __CFMax(A,B) ({__typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __b : __a; }) +#else /* __GNUC__ */ + #define __CFMin(A,B) ((A) < (B) ? (A) : (B)) + #define __CFMax(A,B) ((A) > (B) ? (A) : (B)) +#endif /* __GNUC__ */ + +/* Secret CFAllocator hint bits */ +#define __kCFAllocatorTempMemory 0x2 +#define __kCFAllocatorNoPointers 0x10 +#define __kCFAllocatorDoNotRecordEvent 0x100 +#define __kCFAllocatorGCScannedMemory 0x200 /* GC: memory should be scanned. */ +#define __kCFAllocatorGCObjectMemory 0x400 /* GC: memory needs to be finalized. */ + +CF_INLINE auto_memory_type_t CF_GET_GC_MEMORY_TYPE(CFOptionFlags flags) { + auto_memory_type_t type = (flags & __kCFAllocatorGCScannedMemory ? 0 : AUTO_UNSCANNED) | (flags & __kCFAllocatorGCObjectMemory ? AUTO_OBJECT : 0); + return type; +} + +CF_EXPORT CFAllocatorRef _CFTemporaryMemoryAllocator(void); + +extern SInt64 __CFTimeIntervalToTSR(CFTimeInterval ti); +extern CFTimeInterval __CFTSRToTimeInterval(SInt64 tsr); + +extern CFStringRef __CFCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions); + +/* result is long long or int, depending on doLonglong +*/ +extern Boolean __CFStringScanInteger(CFStringInlineBuffer *buf, CFTypeRef locale, SInt32 *indexPtr, Boolean doLonglong, void *result); +extern Boolean __CFStringScanDouble(CFStringInlineBuffer *buf, CFTypeRef locale, SInt32 *indexPtr, double *resultPtr); +extern Boolean __CFStringScanHex(CFStringInlineBuffer *buf, SInt32 *indexPtr, unsigned *result); + + +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX || (DEPLOYMENT_TARGET_WINDOWS && __GNUC__) + +#define STACK_BUFFER_DECL(T, N, C) T N[C]; + +#elif DEPLOYMENT_TARGET_WINDOWS && _MSC_VER + +#define STACK_BUFFER_DECL(T, N, C) T* N = (T*)_alloca((C + 1) * sizeof(T)); + +#endif + + +#ifdef __CONSTANT_CFSTRINGS__ +#define CONST_STRING_DECL(S, V) const CFStringRef S = (const CFStringRef)__builtin___CFStringMakeConstantString("" V ""); +#else + +struct CF_CONST_STRING { + CFRuntimeBase _base; + uint8_t *_ptr; + uint32_t _length; +}; + +extern int __CFConstantStringClassReference[]; + +/* CFNetwork also has a copy of the CONST_STRING_DECL macro (for use on platforms without constant string support in cc); please warn cfnetwork-core@group.apple.com of any necessary changes to this macro. -- REW, 1/28/2002 */ +#if DEPLOYMENT_TARGET_WINDOWS +#define ___WindowsConstantStringClassReference (uintptr_t)&__CFConstantStringClassReference +#else +#define ___WindowsConstantStringClassReference NULL +#endif + +#if __CF_BIG_ENDIAN__ +#define CONST_STRING_DECL(S, V) \ + static struct CF_CONST_STRING __ ## S ## __ = {{(uintptr_t)&__CFConstantStringClassReference, {0x00, 0x00, 0x07, 0xc8}}, (uint8_t *)V, sizeof(V) - 1}; \ +const CFStringRef S = (CFStringRef) & __ ## S ## __; +#elif !DEPLOYMENT_TARGET_WINDOWS || (DEPLOYMENT_TARGET_WINDOWS && defined(__GNUC__)) +#define CONST_STRING_DECL(S, V) \ + static struct CF_CONST_STRING __ ## S ## __ = {{(uintptr_t)&__CFConstantStringClassReference, {0xc8, 0x07, 0x00, 0x00}}, (uint8_t *)V, sizeof(V) - 1}; \ +const CFStringRef S = (CFStringRef) & __ ## S ## __; +#elif DEPLOYMENT_TARGET_WINDOWS +#define CONST_STRING_DECL(S, V) \ +static struct CF_CONST_STRING __ ## S ## __ = {{(uintptr_t)&__CFConstantStringClassReference, {0xc8, 0x07, 0x00, 0x00}},(uint8_t *) V, sizeof(V) - 1}; \ +CF_EXPORT const CFStringRef S = (CFStringRef) & __ ## S ## __; + +#define CONST_STRING_DECL_EXPORT(S, V) \ +struct CF_CONST_STRING __ ## S ## __ = {{___WindowsConstantStringClassReference, {0xc8, 0x07, 0x00, 0x00}}, (uint8_t *)V, sizeof(V) - 1}; \ +CF_EXPORT const CFStringRef S = (CFStringRef) & __ ## S ## __; + +#else +#define CONST_STRING_DECL(S, V) \ +static struct CF_CONST_STRING __ ## S ## __ = {{(uintptr_t)NULL, {0xc8, 0x07, 0x00, 0x00}},(uint8_t *) V, sizeof(V) - 1}; \ +const CFStringRef S = (CFStringRef) & __ ## S ## __; + +#define CONST_STRING_DECL_EXPORT(S, V) \ +struct CF_CONST_STRING __ ## S ## __ = {{(uintptr_t)NULL, {0xc8, 0x07, 0x00, 0x00}}, (uint8_t *)V, sizeof(V) - 1}; \ +CF_EXPORT const CFStringRef S = (CFStringRef) & __ ## S ## __; + +#endif // __WIN32__ +#endif // __BIG_ENDIAN__ + +#undef ___WindowsConstantStringClassReference + +/* Buffer size for file pathname */ +#if DEPLOYMENT_TARGET_WINDOWS + #define CFMaxPathSize ((CFIndex)262) + #define CFMaxPathLength ((CFIndex)260) +#else + #define CFMaxPathSize ((CFIndex)1026) + #define CFMaxPathLength ((CFIndex)1024) +#endif + +#define __CFOASafe 0 +#define __CFSetLastAllocationEventName(a, b) ((void) 0) + +CF_EXPORT CFStringRef _CFCreateLimitedUniqueString(void); + +/* Comparators are passed the address of the values; this is somewhat different than CFComparatorFunction is used in public API usually. */ +CF_EXPORT CFIndex CFBSearch(const void *element, CFIndex elementSize, const void *list, CFIndex count, CFComparatorFunction comparator, void *context); + +CF_EXPORT CFHashCode CFHashBytes(UInt8 *bytes, CFIndex length); + +CF_EXPORT CFStringEncoding CFStringFileSystemEncoding(void); + +__private_extern__ CFStringRef __CFStringCreateImmutableFunnel3(CFAllocatorRef alloc, const void *bytes, CFIndex numBytes, CFStringEncoding encoding, Boolean possiblyExternalFormat, Boolean tryToReduceUnicode, Boolean hasLengthByte, Boolean hasNullByte, Boolean noCopy, CFAllocatorRef contentsDeallocator, UInt32 converterFlags); + +extern const void *__CFStringCollectionCopy(CFAllocatorRef allocator, const void *ptr); +extern const void *__CFTypeCollectionRetain(CFAllocatorRef allocator, const void *ptr); +extern void __CFTypeCollectionRelease(CFAllocatorRef allocator, const void *ptr); + + +#if DEPLOYMENT_TARGET_MACOSX + +typedef OSSpinLock CFSpinLock_t; + +#define CFSpinLockInit OS_SPINLOCK_INIT +#define CF_SPINLOCK_INIT_FOR_STRUCTS(X) (X = CFSpinLockInit) + +CF_INLINE void __CFSpinLock(CFSpinLock_t *lockp) { + OSSpinLockLock(lockp); +} + +CF_INLINE void __CFSpinUnlock(CFSpinLock_t *lockp) { + OSSpinLockUnlock(lockp); +} + +#elif DEPLOYMENT_TARGET_WINDOWS + +typedef CRITICAL_SECTION CFSpinLock_t; + +#define CFSpinLockInit {0} + +// For some reason, the {0} initializer does not work when the spinlock is a member of a structure; hence this macro +#define CF_SPINLOCK_INIT_FOR_STRUCTS(X) InitializeCriticalSection(&X) +extern CFSpinLock_t *theLock; +CF_INLINE void __CFSpinLock(CFSpinLock_t *slock) { + if (NULL == slock->DebugInfo) { + InitializeCriticalSection(slock); + } + EnterCriticalSection(slock); +} + +CF_INLINE void __CFSpinUnlock(CFSpinLock_t *lock) { + LeaveCriticalSection(lock); +} + +#elif DEPLOYMENT_TARGET_LINUX + +typedef struct __CFSpinLock { + int init; + pthread_spinlock_t lock; +} CFSpinLock_t; + +#define CFSpinLockInit {0} +#define CF_SPINLOCK_INIT_FOR_STRUCTS(X) do { pthread_spin_init(&X.lock, PTHREAD_PROCESS_PRIVATE); X.init = 1; } while (0) + +CF_INLINE void __CFSpinLock(CFSpinLock_t *lockp) { + if (lockp->init == 0) { + !pthread_spin_init(&lockp->lock, PTHREAD_PROCESS_PRIVATE) && + (lockp->init = 1); + } + pthread_spin_lock(&lockp->lock); +} + +CF_INLINE void __CFSpinUnlock(CFSpinLock_t *lockp) { + pthread_spin_unlock(&lockp->lock); +} + +#else + +#warning CF spin locks not defined for this platform -- CF is not thread-safe +#define __CFSpinLock(A) do {} while (0) +#define __CFSpinUnlock(A) do {} while (0) + +#endif + +#if !defined(CHECK_FOR_FORK) +#define CHECK_FOR_FORK() do { } while (0) +#endif + +#if !defined(HAS_FORKED) +#define HAS_FORKED() 0 +#endif + +#if defined(__svr4__) || defined(__hpux__) || DEPLOYMENT_TARGET_WINDOWS +#include <errno.h> +#elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD +#include <sys/errno.h> +#endif + +#define thread_errno() errno +#define thread_set_errno(V) do {errno = (V);} while (0) + +extern void *__CFStartSimpleThread(void *func, void *arg); + +/* ==================== Simple file access ==================== */ +/* For dealing with abstract types. MF:!!! These ought to be somewhere else and public. */ + +CF_EXPORT CFStringRef _CFCopyExtensionForAbstractType(CFStringRef abstractType); + +/* ==================== Simple file access ==================== */ +/* These functions all act on a c-strings which must be in the file system encoding. */ + +CF_EXPORT Boolean _CFCreateDirectory(const char *path); +CF_EXPORT Boolean _CFRemoveDirectory(const char *path); +CF_EXPORT Boolean _CFDeleteFile(const char *path); + +CF_EXPORT Boolean _CFReadBytesFromFile(CFAllocatorRef alloc, CFURLRef url, void **bytes, CFIndex *length, CFIndex maxLength); + /* resulting bytes are allocated from alloc which MUST be non-NULL. */ + /* maxLength of zero means the whole file. Otherwise it sets a limit on the number of bytes read. */ + +CF_EXPORT Boolean _CFWriteBytesToFile(CFURLRef url, const void *bytes, CFIndex length); +#if DEPLOYMENT_TARGET_MACOSX +CF_EXPORT Boolean _CFWriteBytesToFileWithAtomicity(CFURLRef url, const void *bytes, unsigned int length, SInt32 mode, Boolean atomic); +#endif + +CF_EXPORT CFMutableArrayRef _CFContentsOfDirectory(CFAllocatorRef alloc, char *dirPath, void *dirSpec, CFURLRef dirURL, CFStringRef matchingAbstractType); + /* On Mac OS 8/9, one of dirSpec, dirPath and dirURL must be non-NULL */ + /* On all other platforms, one of path and dirURL must be non-NULL */ + /* If both are present, they are assumed to be in-synch; that is, they both refer to the same directory. */ + /* alloc may be NULL */ + /* return value is CFArray of CFURLs */ + +CF_EXPORT SInt32 _CFGetFileProperties(CFAllocatorRef alloc, CFURLRef pathURL, Boolean *exists, SInt32 *posixMode, SInt64 *size, CFDateRef *modTime, SInt32 *ownerID, CFArrayRef *dirContents); + /* alloc may be NULL */ + /* any of exists, posixMode, size, modTime, and dirContents can be NULL. Usually it is not a good idea to pass NULL for exists, since interpretting the other values sometimes requires that you know whether the file existed or not. Except for dirContents, it is pretty cheap to compute any of these things as loing as one of them must be computed. */ + + +/* ==================== Simple path manipulation ==================== */ +/* These functions all act on a UniChar buffers. */ + +CF_EXPORT Boolean _CFIsAbsolutePath(UniChar *unichars, CFIndex length); +CF_EXPORT Boolean _CFStripTrailingPathSlashes(UniChar *unichars, CFIndex *length); +CF_EXPORT Boolean _CFAppendPathComponent(UniChar *unichars, CFIndex *length, CFIndex maxLength, UniChar *component, CFIndex componentLength); +CF_EXPORT Boolean _CFAppendPathExtension(UniChar *unichars, CFIndex *length, CFIndex maxLength, UniChar *extension, CFIndex extensionLength); +CF_EXPORT Boolean _CFTransmutePathSlashes(UniChar *unichars, CFIndex *length, UniChar replSlash); +CF_EXPORT CFIndex _CFStartOfLastPathComponent(UniChar *unichars, CFIndex length); +CF_EXPORT CFIndex _CFLengthAfterDeletingLastPathComponent(UniChar *unichars, CFIndex length); +CF_EXPORT CFIndex _CFStartOfPathExtension(UniChar *unichars, CFIndex length); +CF_EXPORT CFIndex _CFLengthAfterDeletingPathExtension(UniChar *unichars, CFIndex length); + +#ifdef COCOTRON + +extern SEL (*__CFGetObjCSelector)(const char *); +extern void* (*__CFSendObjCMsg)(const void*, SEL, ...); +extern void* __CFISAForTypeID(CFTypeID typeid); + +#define CF_IS_OBJC(typeID, obj) (((CFRuntimeBase *)(obj))->_cfisa != NULL && ((CFRuntimeBase *)(obj))->_cfisa != __CFISAForTypeID(typeID)) + +#define CF_OBJC_VOIDCALL0(obj, sel) \ + {void (*func)(const void *, SEL) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = __CFGetObjCSelector(sel); \ + func((const void *)obj, s);} +#define CF_OBJC_VOIDCALL1(obj, sel, a1) \ + {void (*func)(const void *, SEL, ...) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = __CFGetObjCSelector(sel); \ + func((const void *)obj, s, (a1));} +#define CF_OBJC_VOIDCALL2(obj, sel, a1, a2) \ + {void (*func)(const void *, SEL, ...) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = __CFGetObjCSelector(sel); \ + func((const void *)obj, s, (a1), (a2));} + +// Invoke an ObjC method, leaving the result in "retvar". +// Assumes CF_IS_OBJC has already been checked. +#define CF_OBJC_CALL0(rettype, retvar, obj, sel) \ + {rettype (*func)(const void *, SEL) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = __CFGetObjCSelector(sel); \ + retvar = func((const void *)obj, s);} +#define CF_OBJC_CALL1(rettype, retvar, obj, sel, a1) \ + {rettype (*func)(const void *, SEL, ...) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = __CFGetObjCSelector(sel); \ + retvar = func((const void *)obj, s, (a1));} +#define CF_OBJC_CALL2(rettype, retvar, obj, sel, a1, a2) \ + {rettype (*func)(const void *, SEL, ...) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = __CFGetObjCSelector(sel); \ + retvar = func((const void *)obj, s, (a1), (a2));} + +#define CF_OBJC_FUNCDISPATCH0(typeID, rettype, obj, sel) \ + if (CF_IS_OBJC(typeID, obj)) \ + {rettype (*func)(id, SEL) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = __CFGetObjCSelector(sel); \ + return func((id)obj, s);} +#define CF_OBJC_FUNCDISPATCH1(typeID, rettype, obj, sel, a1) \ + if (CF_IS_OBJC(typeID, obj)) \ + {rettype (*func)(id, SEL, ...) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = __CFGetObjCSelector(sel); \ + return func((id)obj, s, (a1));} +#define CF_OBJC_FUNCDISPATCH2(typeID, rettype, obj, sel, a1, a2) \ + if (CF_IS_OBJC(typeID, obj)) \ + {rettype (*func)(id, SEL, ...) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = __CFGetObjCSelector(sel); \ + return func((id)obj, s, (a1), (a2));} +#define CF_OBJC_FUNCDISPATCH3(typeID, rettype, obj, sel, a1, a2, a3) \ + if (CF_IS_OBJC(typeID, obj)) \ + {rettype (*func)(id, SEL, ...) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = __CFGetObjCSelector(sel); \ + return func((id)obj, s, (a1), (a2), (a3));} +#define CF_OBJC_FUNCDISPATCH4(typeID, rettype, obj, sel, a1, a2, a3, a4) \ + if (CF_IS_OBJC(typeID, obj)) \ + {rettype (*func)(id, SEL, ...) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = __CFGetObjCSelector(sel); \ + return func((id)obj, s, (a1), (a2), (a3), (a4));} +#define CF_OBJC_FUNCDISPATCH5(typeID, rettype, obj, sel, a1, a2, a3, a4, a5) \ + if (CF_IS_OBJC(typeID, obj)) \ + {rettype (*func)(id, SEL, ...) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = __CFGetObjCSelector(sel); \ + return func((id)obj, s, (a1), (a2), (a3), (a4), (a5));} + +#else + +#define CF_IS_OBJC(typeID, obj) (false) + +#define CF_OBJC_VOIDCALL0(obj, sel) +#define CF_OBJC_VOIDCALL1(obj, sel, a1) +#define CF_OBJC_VOIDCALL2(obj, sel, a1, a2) + +#define CF_OBJC_CALL0(rettype, retvar, obj, sel) +#define CF_OBJC_CALL1(rettype, retvar, obj, sel, a1) +#define CF_OBJC_CALL2(rettype, retvar, obj, sel, a1, a2) + +#if defined DEPLOYMENT_TARGET_WINDOWS +#define CF_OBJC_FUNCDISPATCH0(typeID, rettype, obj, sel) ((void)0) +#define CF_OBJC_FUNCDISPATCH1(typeID, rettype, obj, sel, a1) ((void)0) +#define CF_OBJC_FUNCDISPATCH2(typeID, rettype, obj, sel, a1, a2) ((void)0) +#define CF_OBJC_FUNCDISPATCH3(typeID, rettype, obj, sel, a1, a2, a3) ((void)0) +#define CF_OBJC_FUNCDISPATCH4(typeID, rettype, obj, sel, a1, a2, a3, a4) ((void)0) +#define CF_OBJC_FUNCDISPATCH5(typeID, rettype, obj, sel, a1, a2, a3, a4, a5) ((void)0) +#else +#define CF_OBJC_FUNCDISPATCH0(typeID, rettype, obj, sel) +#define CF_OBJC_FUNCDISPATCH1(typeID, rettype, obj, sel, a1) +#define CF_OBJC_FUNCDISPATCH2(typeID, rettype, obj, sel, a1, a2) +#define CF_OBJC_FUNCDISPATCH3(typeID, rettype, obj, sel, a1, a2, a3) +#define CF_OBJC_FUNCDISPATCH4(typeID, rettype, obj, sel, a1, a2, a3, a4) +#define CF_OBJC_FUNCDISPATCH5(typeID, rettype, obj, sel, a1, a2, a3, a4, a5) +#endif + +#define __CFISAForTypeID(x) (0) + +#endif + +#define __CFMaxRuntimeTypes 65535 + +/* See comments in CFBase.c +*/ +#if DEPLOYMENT_TARGET_MACOSX && defined(__ppc__) +extern void __CF_FAULT_CALLBACK(void **ptr); +extern void *__CF_INVOKE_CALLBACK(void *, ...); +#define FAULT_CALLBACK(V) __CF_FAULT_CALLBACK(V) +#define INVOKE_CALLBACK1(P, A) (__CF_INVOKE_CALLBACK(P, A)) +#define INVOKE_CALLBACK2(P, A, B) (__CF_INVOKE_CALLBACK(P, A, B)) +#define INVOKE_CALLBACK3(P, A, B, C) (__CF_INVOKE_CALLBACK(P, A, B, C)) +#define INVOKE_CALLBACK4(P, A, B, C, D) (__CF_INVOKE_CALLBACK(P, A, B, C, D)) +#define INVOKE_CALLBACK5(P, A, B, C, D, E) (__CF_INVOKE_CALLBACK(P, A, B, C, D, E)) +#define UNFAULT_CALLBACK(V) do { V = (void *)((uintptr_t)V & ~0x3); } while (0) +#else +#define FAULT_CALLBACK(V) +#define INVOKE_CALLBACK1(P, A) (P)(A) +#define INVOKE_CALLBACK2(P, A, B) (P)(A, B) +#define INVOKE_CALLBACK3(P, A, B, C) (P)(A, B, C) +#define INVOKE_CALLBACK4(P, A, B, C, D) (P)(A, B, C, D) +#define INVOKE_CALLBACK5(P, A, B, C, D, E) (P)(A, B, C, D, E) +#define UNFAULT_CALLBACK(V) do { } while (0) +#endif + +/* For the support of functionality which needs CarbonCore or other frameworks */ +// These macros define an upcall or weak "symbol-lookup" wrapper function. +// The parameters are: +// R : the return type of the function +// N : the name of the function (in the other library) +// P : the parenthesized parameter list of the function +// A : the parenthesized actual argument list to be passed +// opt: a fifth optional argument can be passed in which is the +// return value of the wrapper when the function cannot be +// found; should be of type R, & can be a function call +// The name of the resulting wrapper function is: +// __CFCarbonCore_N (where N is the second parameter) +// __CFNetwork_N (where N is the second parameter) +// +// Example: +// DEFINE_WEAK_CARBONCORE_FUNC(void, DisposeHandle, (Handle h), (h)) +// + +#if DEPLOYMENT_TARGET_MACOSX + +extern void *__CFLookupCFNetworkFunction(const char *name); + +#define DEFINE_WEAK_CFNETWORK_FUNC(R, N, P, A, ...) \ + static R __CFNetwork_ ## N P { \ + typedef R (*CALL_FUN_TYPE ## N) P; \ + static CALL_FUN_TYPE ## N dyfunc = (CALL_FUN_TYPE ## N)(~(uintptr_t)0); \ + if (((CALL_FUN_TYPE ## N)(~(uintptr_t)0)) == dyfunc) { \ + dyfunc = (CALL_FUN_TYPE ## N)__CFLookupCFNetworkFunction(#N); } \ + if (dyfunc) { return dyfunc A ; } \ + return __VA_ARGS__ ; \ + } + +#else + +#define DEFINE_WEAK_CFNETWORK_FUNC(R, N, P, A, ...) + +#endif + + +#if !defined(DEFINE_WEAK_CARBONCORE_FUNC) +#define DEFINE_WEAK_CARBONCORE_FUNC(R, N, P, A, ...) +#endif + + +__private_extern__ CFArrayRef _CFBundleCopyUserLanguages(Boolean useBackstops); + +/* GC related internal SPIs. */ +extern malloc_zone_t *__CFCollectableZone; + +/* !!! Avoid #importing objc.h; e.g. converting this to a .m file */ +struct __objcFastEnumerationStateEquivalent { + unsigned long state; + unsigned long *itemsPtr; + unsigned long *mutationsPtr; + unsigned long extra[5]; +}; + +unsigned long _CFStorageFastEnumeration(CFStorageRef storage, struct __objcFastEnumerationStateEquivalent *state, void *stackbuffer, unsigned long count); + +#if DEPLOYMENT_TARGET_MACOSX +// Allocate an id[count], new slots are nil +extern void *__CFAllocateObjectArray(unsigned long count); +extern void *__CFReallocateObjectArray(id *array, unsigned long count); +extern void __CFFreeObjectArray(id *array); + +// check against LONG_MAX to catch negative numbers +#define new_id_array(N, C) \ + size_t N ## _count__ = (C); \ + if (N ## _count__ > LONG_MAX) { \ + id rr = [objc_lookUpClass("NSString") stringWithFormat:@"*** attempt to create a temporary id buffer which is too large or with a negative count (%lu) -- possibly data is corrupt", N ## _count__]; \ + @throw [NSException exceptionWithName:NSGenericException reason:rr userInfo:nil]; \ + } \ + NSInteger N ## _is_stack__ = (N ## _count__ <= 256); \ + id N ## _buffer__[N ## _is_stack__ ? N ## _count__ : 0]; \ + if (N ## _is_stack__) memset(N ## _buffer__, 0, sizeof(N ## _buffer__)); \ + id * N = N ## _is_stack__ ? N ## _buffer__ : __CFAllocateObjectArray(N ## _count__); \ + if (! N) { \ + id rr = [objc_lookUpClass("NSString") stringWithFormat:@"*** attempt to create a temporary id buffer of length (%lu) failed", N ## _count__]; \ + @throw [NSException exceptionWithName:NSMallocException reason:rr userInfo:nil]; \ + } \ + do {} while (0) + +#define free_id_array(N) \ + if (! N ## _is_stack__) __CFFreeObjectArray(N) + +extern void *__CFFullMethodName(Class cls, id obj, SEL sel); +extern void *__CFExceptionProem(id obj, SEL sel); +extern void __CFRequireConcreteImplementation(Class absClass, id obj, SEL sel); +#endif + +#endif /* ! __COREFOUNDATION_CFINTERNAL__ */ +
diff --git a/CoreFoundation/CFLite.sln b/CoreFoundation/CFLite.sln new file mode 100644 index 0000000..e687de1 --- /dev/null +++ b/CoreFoundation/CFLite.sln
@@ -0,0 +1,130 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WritePListExample", "examples\WritePListExample\WritePListExample.vcproj", "{AD525151-D756-44E6-AEA7-12052F4F43E2}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {EC99198F-4CDD-4D85-AE6F-F837FF6CC372} = {EC99198F-4CDD-4D85-AE6F-F837FF6CC372} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReadPListExample", "examples\ReadPListExample\ReadPListExample.vcproj", "{CDF7218C-AE33-4367-B6F9-5728AE3C7F6E}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {EC99198F-4CDD-4D85-AE6F-F837FF6CC372} = {EC99198F-4CDD-4D85-AE6F-F837FF6CC372} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StringExample", "examples\StringExample\StringExample.vcproj", "{4E98FDA7-54AE-4A8E-847A-8665DEF6BBE1}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {EC99198F-4CDD-4D85-AE6F-F837FF6CC372} = {EC99198F-4CDD-4D85-AE6F-F837FF6CC372} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AllocatorExample", "examples\Allocator\Allocator.vcproj", "{2A4D3906-539D-4D1E-9C97-47ABD83FD999}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {EC99198F-4CDD-4D85-AE6F-F837FF6CC372} = {EC99198F-4CDD-4D85-AE6F-F837FF6CC372} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CFLite", "windows\CFLiteLib.vcproj", "{EC99198F-4CDD-4D85-AE6F-F837FF6CC372}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DisplayURL", "examples\DisplayURL\DisplayURL.vcproj", "{6816B34F-DE8C-48EC-8F31-11176DA5FE11}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {EC99198F-4CDD-4D85-AE6F-F837FF6CC372} = {EC99198F-4CDD-4D85-AE6F-F837FF6CC372} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DNSServiceMetaQuery", "examples\DNSServiceMetaQuery\DNSServiceMetaQuery.vcproj", "{B5F7F4D9-DCA9-4CCD-8776-D2EAF45E4BE9}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {EC99198F-4CDD-4D85-AE6F-F837FF6CC372} = {EC99198F-4CDD-4D85-AE6F-F837FF6CC372} + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{60C062B2-DAC0-4361-9D92-9654183AFD72}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CFRunLoopTimerExample", "examples\CFRunLoopTimerExample\CFRunLoopTimerExample.vcproj", "{6EC2C74D-B79D-4B8D-B14B-94ED3F08EB28}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {EC99198F-4CDD-4D85-AE6F-F837FF6CC372} = {EC99198F-4CDD-4D85-AE6F-F837FF6CC372} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AD525151-D756-44E6-AEA7-12052F4F43E2}.Debug|Win32.ActiveCfg = Debug|Win32 + {AD525151-D756-44E6-AEA7-12052F4F43E2}.Debug|Win32.Build.0 = Debug|Win32 + {AD525151-D756-44E6-AEA7-12052F4F43E2}.Release|Win32.ActiveCfg = Release|Win32 + {AD525151-D756-44E6-AEA7-12052F4F43E2}.Release|Win32.Build.0 = Release|Win32 + {CDF7218C-AE33-4367-B6F9-5728AE3C7F6E}.Debug|Win32.ActiveCfg = Debug|Win32 + {CDF7218C-AE33-4367-B6F9-5728AE3C7F6E}.Debug|Win32.Build.0 = Debug|Win32 + {CDF7218C-AE33-4367-B6F9-5728AE3C7F6E}.Release|Win32.ActiveCfg = Release|Win32 + {CDF7218C-AE33-4367-B6F9-5728AE3C7F6E}.Release|Win32.Build.0 = Release|Win32 + {4E98FDA7-54AE-4A8E-847A-8665DEF6BBE1}.Debug|Win32.ActiveCfg = Debug|Win32 + {4E98FDA7-54AE-4A8E-847A-8665DEF6BBE1}.Debug|Win32.Build.0 = Debug|Win32 + {4E98FDA7-54AE-4A8E-847A-8665DEF6BBE1}.Release|Win32.ActiveCfg = Release|Win32 + {4E98FDA7-54AE-4A8E-847A-8665DEF6BBE1}.Release|Win32.Build.0 = Release|Win32 + {2A4D3906-539D-4D1E-9C97-47ABD83FD999}.Debug|Win32.ActiveCfg = Debug|Win32 + {2A4D3906-539D-4D1E-9C97-47ABD83FD999}.Debug|Win32.Build.0 = Debug|Win32 + {2A4D3906-539D-4D1E-9C97-47ABD83FD999}.Release|Win32.ActiveCfg = Release|Win32 + {2A4D3906-539D-4D1E-9C97-47ABD83FD999}.Release|Win32.Build.0 = Release|Win32 + {EC99198F-4CDD-4D85-AE6F-F837FF6CC372}.Debug|Win32.ActiveCfg = Debug|Win32 + {EC99198F-4CDD-4D85-AE6F-F837FF6CC372}.Debug|Win32.Build.0 = Debug|Win32 + {EC99198F-4CDD-4D85-AE6F-F837FF6CC372}.Release|Win32.ActiveCfg = Release|Win32 + {EC99198F-4CDD-4D85-AE6F-F837FF6CC372}.Release|Win32.Build.0 = Release|Win32 + {6816B34F-DE8C-48EC-8F31-11176DA5FE11}.Debug|Win32.ActiveCfg = Debug|Win32 + {6816B34F-DE8C-48EC-8F31-11176DA5FE11}.Debug|Win32.Build.0 = Debug|Win32 + {6816B34F-DE8C-48EC-8F31-11176DA5FE11}.Release|Win32.ActiveCfg = Release|Win32 + {6816B34F-DE8C-48EC-8F31-11176DA5FE11}.Release|Win32.Build.0 = Release|Win32 + {B5F7F4D9-DCA9-4CCD-8776-D2EAF45E4BE9}.Debug|Win32.ActiveCfg = Debug|Win32 + {B5F7F4D9-DCA9-4CCD-8776-D2EAF45E4BE9}.Debug|Win32.Build.0 = Debug|Win32 + {B5F7F4D9-DCA9-4CCD-8776-D2EAF45E4BE9}.Release|Win32.ActiveCfg = Release|Win32 + {B5F7F4D9-DCA9-4CCD-8776-D2EAF45E4BE9}.Release|Win32.Build.0 = Release|Win32 + {6EC2C74D-B79D-4B8D-B14B-94ED3F08EB28}.Debug|Win32.ActiveCfg = Debug|Win32 + {6EC2C74D-B79D-4B8D-B14B-94ED3F08EB28}.Debug|Win32.Build.0 = Debug|Win32 + {6EC2C74D-B79D-4B8D-B14B-94ED3F08EB28}.Release|Win32.ActiveCfg = Release|Win32 + {6EC2C74D-B79D-4B8D-B14B-94ED3F08EB28}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {CDF7218C-AE33-4367-B6F9-5728AE3C7F6E} = {60C062B2-DAC0-4361-9D92-9654183AFD72} + {4E98FDA7-54AE-4A8E-847A-8665DEF6BBE1} = {60C062B2-DAC0-4361-9D92-9654183AFD72} + {2A4D3906-539D-4D1E-9C97-47ABD83FD999} = {60C062B2-DAC0-4361-9D92-9654183AFD72} + {6816B34F-DE8C-48EC-8F31-11176DA5FE11} = {60C062B2-DAC0-4361-9D92-9654183AFD72} + {B5F7F4D9-DCA9-4CCD-8776-D2EAF45E4BE9} = {60C062B2-DAC0-4361-9D92-9654183AFD72} + {AD525151-D756-44E6-AEA7-12052F4F43E2} = {60C062B2-DAC0-4361-9D92-9654183AFD72} + {6EC2C74D-B79D-4B8D-B14B-94ED3F08EB28} = {60C062B2-DAC0-4361-9D92-9654183AFD72} + EndGlobalSection +EndGlobal
diff --git a/CoreFoundation/CFLocale.c b/CoreFoundation/CFLocale.c new file mode 100644 index 0000000..872e2dd --- /dev/null +++ b/CoreFoundation/CFLocale.c
@@ -0,0 +1,1073 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFLocale.c + Copyright 2002-2003, Apple Computer, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +// Note the header file is in the OpenSource set (stripped to almost nothing), but not the .c file + +#include <CoreFoundation/CFLocale.h> +#include <CoreFoundation/CFString.h> +#include <CoreFoundation/CFArray.h> +#include <CoreFoundation/CFDictionary.h> +#include <CoreFoundation/CFPreferences.h> +#include <CoreFoundation/CFCalendar.h> +#include <CoreFoundation/CFNumber.h> +#include "CFInternal.h" +#include <unicode/uloc.h> // ICU locales +#include <unicode/ulocdata.h> // ICU locale data +#include <unicode/ucurr.h> // ICU currency functions +#include <unicode/uset.h> // ICU Unicode sets +#include <unicode/putil.h> // ICU low-level utilities +#include <unicode/umsg.h> // ICU message formatting +#include <CoreFoundation/CFNumberFormatter.h> +#include <stdlib.h> +#include <stdio.h> +#include <unicode/ucol.h> + +#if DEPLOYMENT_TARGET_WINDOWS +#include <windows.h> // for GetLocaleInfo +#include <winnls.h> +#include <locale.h> +#endif + +#if _MSC_VER +#define snprintf _snprintf +#define BUFFER_SIZE 512 +#endif + +CONST_STRING_DECL(kCFLocaleCurrentLocaleDidChangeNotification, "kCFLocaleCurrentLocaleDidChangeNotification") + +static const char *kCalendarKeyword = "calendar"; +static const char *kCollationKeyword = "collation"; +#define kMaxICUNameSize 1024 + +typedef struct __CFLocale *CFMutableLocaleRef; + +__private_extern__ CONST_STRING_DECL(__kCFLocaleCollatorID, "locale:collator id") + +enum { + __kCFLocaleKeyTableCount = 16 +}; + +struct key_table { + CFStringRef key; + bool (*get)(CFLocaleRef, bool user, CFTypeRef *, CFStringRef context); // returns an immutable copy & reference + bool (*set)(CFMutableLocaleRef, CFTypeRef, CFStringRef context); + bool (*name)(const char *, const char *, CFStringRef *); + CFStringRef context; +}; + + +// Must forward decl. these functions: +static bool __CFLocaleCopyLocaleID(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context); +static bool __CFLocaleSetNOP(CFMutableLocaleRef locale, CFTypeRef cf, CFStringRef context); +static bool __CFLocaleFullName(const char *locale, const char *value, CFStringRef *out); +static bool __CFLocaleCopyCodes(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context); +static bool __CFLocaleCountryName(const char *locale, const char *value, CFStringRef *out); +static bool __CFLocaleScriptName(const char *locale, const char *value, CFStringRef *out); +static bool __CFLocaleLanguageName(const char *locale, const char *value, CFStringRef *out); +static bool __CFLocaleCurrencyShortName(const char *locale, const char *value, CFStringRef *out); +static bool __CFLocaleCopyExemplarCharSet(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context); +static bool __CFLocaleVariantName(const char *locale, const char *value, CFStringRef *out); +static bool __CFLocaleNoName(const char *locale, const char *value, CFStringRef *out); +static bool __CFLocaleCopyCalendarID(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context); +static bool __CFLocaleCalendarName(const char *locale, const char *value, CFStringRef *out); +static bool __CFLocaleCollationName(const char *locale, const char *value, CFStringRef *out); +static bool __CFLocaleCopyUsesMetric(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context); +static bool __CFLocaleCopyCalendar(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context); +static bool __CFLocaleCopyCollationID(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context); +static bool __CFLocaleCopyMeasurementSystem(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context); +static bool __CFLocaleCopyNumberFormat(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context); +static bool __CFLocaleCopyNumberFormat2(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context); +static bool __CFLocaleCurrencyFullName(const char *locale, const char *value, CFStringRef *out); +static bool __CFLocaleCopyCollatorID(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context); + +// Note string members start with an extra &, and are fixed up at init time +static struct key_table __CFLocaleKeyTable[__kCFLocaleKeyTableCount] = { + {(CFStringRef)&kCFLocaleIdentifier, __CFLocaleCopyLocaleID, __CFLocaleSetNOP, __CFLocaleFullName, NULL}, + {(CFStringRef)&kCFLocaleLanguageCode, __CFLocaleCopyCodes, __CFLocaleSetNOP, __CFLocaleLanguageName, (CFStringRef)&kCFLocaleLanguageCode}, + {(CFStringRef)&kCFLocaleCountryCode, __CFLocaleCopyCodes, __CFLocaleSetNOP, __CFLocaleCountryName, (CFStringRef)&kCFLocaleCountryCode}, + {(CFStringRef)&kCFLocaleScriptCode, __CFLocaleCopyCodes, __CFLocaleSetNOP, __CFLocaleScriptName, (CFStringRef)&kCFLocaleScriptCode}, + {(CFStringRef)&kCFLocaleVariantCode, __CFLocaleCopyCodes, __CFLocaleSetNOP, __CFLocaleVariantName, (CFStringRef)&kCFLocaleVariantCode}, + {(CFStringRef)&kCFLocaleExemplarCharacterSet, __CFLocaleCopyExemplarCharSet, __CFLocaleSetNOP, __CFLocaleNoName, NULL}, + {(CFStringRef)&kCFLocaleCalendarIdentifier, __CFLocaleCopyCalendarID, __CFLocaleSetNOP, __CFLocaleCalendarName, NULL}, + {(CFStringRef)&kCFLocaleCalendar, __CFLocaleCopyCalendar, __CFLocaleSetNOP, __CFLocaleNoName, NULL}, + {(CFStringRef)&kCFLocaleCollationIdentifier, __CFLocaleCopyCollationID, __CFLocaleSetNOP, __CFLocaleCollationName, NULL}, + {(CFStringRef)&kCFLocaleUsesMetricSystem, __CFLocaleCopyUsesMetric, __CFLocaleSetNOP, __CFLocaleNoName, NULL}, + {(CFStringRef)&kCFLocaleMeasurementSystem, __CFLocaleCopyMeasurementSystem, __CFLocaleSetNOP, __CFLocaleNoName, NULL}, + {(CFStringRef)&kCFLocaleDecimalSeparator, __CFLocaleCopyNumberFormat, __CFLocaleSetNOP, __CFLocaleNoName, (CFStringRef)&kCFNumberFormatterDecimalSeparator}, + {(CFStringRef)&kCFLocaleGroupingSeparator, __CFLocaleCopyNumberFormat, __CFLocaleSetNOP, __CFLocaleNoName, (CFStringRef)&kCFNumberFormatterGroupingSeparator}, + {(CFStringRef)&kCFLocaleCurrencySymbol, __CFLocaleCopyNumberFormat2, __CFLocaleSetNOP, __CFLocaleCurrencyShortName, (CFStringRef)&kCFNumberFormatterCurrencySymbol}, + {(CFStringRef)&kCFLocaleCurrencyCode, __CFLocaleCopyNumberFormat2, __CFLocaleSetNOP, __CFLocaleCurrencyFullName, (CFStringRef)&kCFNumberFormatterCurrencyCode}, + {(CFStringRef)&__kCFLocaleCollatorID, __CFLocaleCopyCollatorID, __CFLocaleSetNOP, __CFLocaleNoName, NULL}, +}; + + +static CFLocaleRef __CFLocaleSystem = NULL; +static CFMutableDictionaryRef __CFLocaleCache = NULL; +static CFSpinLock_t __CFLocaleGlobalLock = CFSpinLockInit; + +struct __CFLocale { + CFRuntimeBase _base; + CFStringRef _identifier; // canonical identifier, never NULL + CFMutableDictionaryRef _cache; + CFMutableDictionaryRef _overrides; + CFDictionaryRef _prefs; + CFSpinLock_t _lock; +}; + +/* Flag bits */ +enum { /* Bits 0-1 */ + __kCFLocaleOrdinary = 0, + __kCFLocaleSystem = 1, + __kCFLocaleUser = 2, + __kCFLocaleCustom = 3 +}; + +CF_INLINE CFIndex __CFLocaleGetType(CFLocaleRef locale) { + return __CFBitfieldGetValue(((const CFRuntimeBase *)locale)->_cfinfo[CF_INFO_BITS], 1, 0); +} + +CF_INLINE void __CFLocaleSetType(CFLocaleRef locale, CFIndex type) { + __CFBitfieldSetValue(((CFRuntimeBase *)locale)->_cfinfo[CF_INFO_BITS], 1, 0, (uint8_t)type); +} + +CF_INLINE void __CFLocaleLockGlobal(void) { + __CFSpinLock(&__CFLocaleGlobalLock); +} + +CF_INLINE void __CFLocaleUnlockGlobal(void) { + __CFSpinUnlock(&__CFLocaleGlobalLock); +} + +CF_INLINE void __CFLocaleLock(CFLocaleRef locale) { + __CFSpinLock((CFSpinLock_t *)(&locale->_lock)); +} + +CF_INLINE void __CFLocaleUnlock(CFLocaleRef locale) { + __CFSpinUnlock((CFSpinLock_t *)(&locale->_lock)); +} + +#ifdef DEPLOYMENT_TARGET_WINDOWS +__private_extern__ CFStringRef __CFLocaleWindowsLCIDToISOLocaleName(LCID id) +{ + //XXXBS Windows Vista should use LCIDToLocaleName instead of this + char iso_localename[18], iso_lang_name[9], iso_region_name[9]; + GetLocaleInfoA(id, LOCALE_SISO639LANGNAME, iso_lang_name, 9); + GetLocaleInfoA(id, LOCALE_SISO3166CTRYNAME,iso_region_name, 9); + // implicitly protected from overflow because of guaranteed maximum size returned by GetLocaleInfoA/W + strcpy(iso_localename, iso_lang_name); + strcat(iso_localename, "_"); + strcat(iso_localename, iso_region_name); + return(CFStringCreateWithCString(kCFAllocatorSystemDefault, iso_localename, kCFStringEncodingASCII)); +} + +__private_extern__ CFStringRef __CFLocaleWindowsLCIDToISOLocaleNameUsingHyphen(LCID id) +{ + char iso_localename[18], iso_lang_name[9], iso_region_name[9]; + GetLocaleInfoA(id, LOCALE_SISO639LANGNAME, iso_lang_name, 9); + GetLocaleInfoA(id, LOCALE_SISO3166CTRYNAME,iso_region_name, 9); + // implicitly protected from overflow because of guaranteed maximum size returned by GetLocaleInfoA/W + strcpy(iso_localename, iso_lang_name); + strcat(iso_localename, "-"); + strcat(iso_localename, iso_region_name); + return(CFStringCreateWithCString(kCFAllocatorSystemDefault, iso_localename, kCFStringEncodingASCII)); +} + +CFStringRef s_WindowsInstalledLocales[255]; +int s_num_installed_localesI = 0; + +static BOOL CALLBACK WindowsLocaleEnumProc(char *localeName) +{ + s_WindowsInstalledLocales[s_num_installed_localesI] = CFStringCreateWithCString(kCFAllocatorDefault, localeName, kCFStringEncodingASCII); + s_num_installed_localesI++; + return (TRUE); +} + +__private_extern__ CFArrayRef CFLocaleWindowsGetInstalledLocales(void) +{ + CFArrayRef WindowsLocales; + EnumSystemLocales((LOCALE_ENUMPROC)WindowsLocaleEnumProc, LCID_INSTALLED); + WindowsLocales = CFArrayCreate(kCFAllocatorSystemDefault, (const void**)s_WindowsInstalledLocales, s_num_installed_localesI, &kCFTypeArrayCallBacks); + return (WindowsLocales); +} +#elif DEPLOYMENT_TARGET_LINUX +static CFStringRef __CFLocaleCopyPOSIXLocaleName(void) +{ + const char *p = NULL; + CFStringRef identifier = NULL; + + p = getenv("LC_ALL"); + + if (NULL == p) p = getenv("LANG"); + if (NULL != p) identifier = CFStringCreateWithCString(kCFAllocatorDefault, p, kCFStringEncodingASCII); + + return (identifier); +} +#endif // DEPLOYMENT_TARGET_WINDOWS + +static Boolean __CFLocaleEqual(CFTypeRef cf1, CFTypeRef cf2) { + CFLocaleRef locale1 = (CFLocaleRef)cf1; + CFLocaleRef locale2 = (CFLocaleRef)cf2; + // a user locale and a locale created with an ident are not the same even if their contents are + if (__CFLocaleGetType(locale1) != __CFLocaleGetType(locale2)) return false; + if (!CFEqual(locale1->_identifier, locale2->_identifier)) return false; + if (NULL == locale1->_overrides && NULL != locale2->_overrides) return false; + if (NULL != locale1->_overrides && NULL == locale2->_overrides) return false; + if (NULL != locale1->_overrides && !CFEqual(locale1->_overrides, locale2->_overrides)) return false; + if (__kCFLocaleUser == __CFLocaleGetType(locale1)) { + return CFEqual(locale1->_prefs, locale2->_prefs); + } + return true; +} + +static CFHashCode __CFLocaleHash(CFTypeRef cf) { + CFLocaleRef locale = (CFLocaleRef)cf; + return CFHash(locale->_identifier); +} + +static CFStringRef __CFLocaleCopyDescription(CFTypeRef cf) { + CFLocaleRef locale = (CFLocaleRef)cf; + const char *type = NULL; + switch (__CFLocaleGetType(locale)) { + case __kCFLocaleOrdinary: type = "ordinary"; break; + case __kCFLocaleSystem: type = "system"; break; + case __kCFLocaleUser: type = "user"; break; + case __kCFLocaleCustom: type = "custom"; break; + } + return CFStringCreateWithFormat(CFGetAllocator(locale), NULL, CFSTR("<CFLocale %p [%p]>{type = %s, identifier = '%@'}"), cf, CFGetAllocator(locale), type, locale->_identifier); +} + +static void __CFLocaleDeallocate(CFTypeRef cf) { + CFLocaleRef locale = (CFLocaleRef)cf; + CFRelease(locale->_identifier); + if (NULL != locale->_cache) CFRelease(locale->_cache); + if (NULL != locale->_overrides) CFRelease(locale->_overrides); + if (NULL != locale->_prefs) CFRelease(locale->_prefs); +} + +static CFTypeID __kCFLocaleTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFLocaleClass = { + 0, + "CFLocale", + NULL, // init + NULL, // copy + __CFLocaleDeallocate, + __CFLocaleEqual, + __CFLocaleHash, + NULL, // + __CFLocaleCopyDescription +}; + +__private_extern__ void __CFLocaleInitialize(void) { + CFIndex idx; + __kCFLocaleTypeID = _CFRuntimeRegisterClass(&__CFLocaleClass); + for (idx = 0; idx < __kCFLocaleKeyTableCount; idx++) { + // table fixup to workaround compiler/language limitations + __CFLocaleKeyTable[idx].key = *((CFStringRef *)__CFLocaleKeyTable[idx].key); + if (NULL != __CFLocaleKeyTable[idx].context) { + __CFLocaleKeyTable[idx].context = *((CFStringRef *)__CFLocaleKeyTable[idx].context); + } + } +} + +CFTypeID CFLocaleGetTypeID(void) { + if (_kCFRuntimeNotATypeID == __kCFLocaleTypeID) __CFLocaleInitialize(); + return __kCFLocaleTypeID; +} + +CFLocaleRef CFLocaleGetSystem(void) { + CFLocaleRef locale; + __CFLocaleLockGlobal(); + if (NULL == __CFLocaleSystem) { + __CFLocaleUnlockGlobal(); + locale = CFLocaleCreate(kCFAllocatorSystemDefault, CFSTR("")); + if (!locale) return NULL; + __CFLocaleSetType(locale, __kCFLocaleSystem); + __CFLocaleLockGlobal(); + if (NULL == __CFLocaleSystem) { + __CFLocaleSystem = locale; + } else { + if (locale) CFRelease(locale); + } + } + locale = __CFLocaleSystem ? (CFLocaleRef)CFRetain(__CFLocaleSystem) : NULL; + __CFLocaleUnlockGlobal(); + return locale; +} + +static CFLocaleRef __CFLocaleCurrent = NULL; + +#define FALLBACK_LOCALE_NAME CFSTR("") + +CFLocaleRef CFLocaleCopyCurrent(void) { + + __CFLocaleLockGlobal(); + if (__CFLocaleCurrent) { + CFRetain(__CFLocaleCurrent); + __CFLocaleUnlockGlobal(); + return __CFLocaleCurrent; + } + __CFLocaleUnlockGlobal(); + + CFDictionaryRef prefs = NULL; + CFStringRef identifier = NULL; + +#ifdef DEPLOYMENT_TARGET_WINDOWS + identifier = __CFLocaleWindowsLCIDToISOLocaleName(LOCALE_USER_DEFAULT); +#elif DEPLOYMENT_TARGET_LINUX + identifier = __CFLocaleCopyPOSIXLocaleName(); +#endif + + struct __CFLocale *locale; + uint32_t size = sizeof(struct __CFLocale) - sizeof(CFRuntimeBase); + locale = (struct __CFLocale *)_CFRuntimeCreateInstance(kCFAllocatorSystemDefault, CFLocaleGetTypeID(), size, NULL); + if (NULL == locale) { + return NULL; + } + __CFLocaleSetType(locale, __kCFLocaleUser); + if (NULL == identifier) identifier = (CFStringRef)CFRetain(FALLBACK_LOCALE_NAME); + locale->_identifier = identifier; + locale->_cache = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks); + locale->_overrides = NULL; + locale->_prefs = prefs; + + CF_SPINLOCK_INIT_FOR_STRUCTS(locale->_lock); + + __CFLocaleLockGlobal(); + if (NULL == __CFLocaleCurrent) { + __CFLocaleCurrent = locale; + } else { + CFRelease(locale); + } + locale = (struct __CFLocale *)CFRetain(__CFLocaleCurrent); + __CFLocaleUnlockGlobal(); + return locale; +} + +__private_extern__ CFDictionaryRef __CFLocaleGetPrefs(CFLocaleRef locale) { + return locale->_prefs; +} + +CFLocaleRef CFLocaleCreate(CFAllocatorRef allocator, CFStringRef identifier) { + if (allocator == NULL) allocator = __CFGetDefaultAllocator(); + __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); + __CFGenericValidateType(identifier, CFStringGetTypeID()); + CFStringRef localeIdentifier = NULL; + if (identifier) { + localeIdentifier = CFLocaleCreateCanonicalLocaleIdentifierFromString(allocator, identifier); + } + if (NULL == localeIdentifier) return NULL; + CFStringRef old = localeIdentifier; + localeIdentifier = (CFStringRef)CFStringCreateCopy(allocator, localeIdentifier); + CFRelease(old); + __CFLocaleLockGlobal(); + // Look for cases where we can return a cached instance. + // We only use cached objects if the allocator is the system + // default allocator. + if (!allocator) allocator = __CFGetDefaultAllocator(); + Boolean canCache = (kCFAllocatorSystemDefault == allocator); + if (canCache && __CFLocaleCache) { + CFLocaleRef locale = (CFLocaleRef)CFDictionaryGetValue(__CFLocaleCache, localeIdentifier); + if (locale) { + CFRetain(locale); + __CFLocaleUnlockGlobal(); + CFRelease(localeIdentifier); + return locale; + } + } + struct __CFLocale *locale = NULL; + uint32_t size = sizeof(struct __CFLocale) - sizeof(CFRuntimeBase); + locale = (struct __CFLocale *)_CFRuntimeCreateInstance(allocator, CFLocaleGetTypeID(), size, NULL); + if (NULL == locale) { + return NULL; + } + __CFLocaleSetType(locale, __kCFLocaleOrdinary); + locale->_identifier = localeIdentifier; + locale->_cache = CFDictionaryCreateMutable(allocator, 0, NULL, &kCFTypeDictionaryValueCallBacks); + locale->_overrides = NULL; + locale->_prefs = NULL; + + CF_SPINLOCK_INIT_FOR_STRUCTS(locale->_lock); + + if (canCache) { + if (NULL == __CFLocaleCache) { + __CFLocaleCache = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + } + CFDictionarySetValue(__CFLocaleCache, localeIdentifier, locale); + } + __CFLocaleUnlockGlobal(); + return (CFLocaleRef)locale; +} + +CFLocaleRef CFLocaleCreateCopy(CFAllocatorRef allocator, CFLocaleRef locale) { + return (CFLocaleRef)CFRetain(locale); +} + +CFStringRef CFLocaleGetIdentifier(CFLocaleRef locale) { + CF_OBJC_FUNCDISPATCH0(CFLocaleGetTypeID(), CFStringRef, locale, "localeIdentifier"); + return locale->_identifier; +} + +CFTypeRef CFLocaleGetValue(CFLocaleRef locale, CFStringRef key) { + CF_OBJC_FUNCDISPATCH1(CFLocaleGetTypeID(), CFTypeRef, locale, "objectForKey:", key); + CFIndex idx, slot = -1; + for (idx = 0; idx < __kCFLocaleKeyTableCount; idx++) { + if (__CFLocaleKeyTable[idx].key == key) { + slot = idx; + break; + } + } + if (-1 == slot && NULL != key) { + for (idx = 0; idx < __kCFLocaleKeyTableCount; idx++) { + if (CFEqual(__CFLocaleKeyTable[idx].key, key)) { + slot = idx; + break; + } + } + } + if (-1 == slot) { + return NULL; + } + CFTypeRef value; + if (NULL != locale->_overrides && CFDictionaryGetValueIfPresent(locale->_overrides, __CFLocaleKeyTable[slot].key, &value)) { + return value; + } + __CFLocaleLock(locale); + if (CFDictionaryGetValueIfPresent(locale->_cache, __CFLocaleKeyTable[slot].key, &value)) { + __CFLocaleUnlock(locale); + return value; + } + if (__kCFLocaleUser == __CFLocaleGetType(locale) && __CFLocaleKeyTable[slot].get(locale, true, &value, __CFLocaleKeyTable[slot].context)) { + if (value) CFDictionarySetValue(locale->_cache, __CFLocaleKeyTable[idx].key, value); + if (value) CFRelease(value); + __CFLocaleUnlock(locale); + return value; + } + if (__CFLocaleKeyTable[slot].get(locale, false, &value, __CFLocaleKeyTable[slot].context)) { + if (value) CFDictionarySetValue(locale->_cache, __CFLocaleKeyTable[idx].key, value); + if (value) CFRelease(value); + __CFLocaleUnlock(locale); + return value; + } + __CFLocaleUnlock(locale); + return NULL; +} + +CFStringRef CFLocaleCopyDisplayNameForPropertyValue(CFLocaleRef displayLocale, CFStringRef key, CFStringRef value) { + CF_OBJC_FUNCDISPATCH2(CFLocaleGetTypeID(), CFStringRef, displayLocale, "_copyDisplayNameForKey:value:", key, value); + CFIndex idx, slot = -1; + for (idx = 0; idx < __kCFLocaleKeyTableCount; idx++) { + if (__CFLocaleKeyTable[idx].key == key) { + slot = idx; + break; + } + } + if (-1 == slot && NULL != key) { + for (idx = 0; idx < __kCFLocaleKeyTableCount; idx++) { + if (CFEqual(__CFLocaleKeyTable[idx].key, key)) { + slot = idx; + break; + } + } + } + if (-1 == slot || !value) { + return NULL; + } + // Get the locale ID as a C string + char localeID[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY]; + char cValue[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY]; + if (CFStringGetCString(displayLocale->_identifier, localeID, sizeof(localeID)/sizeof(localeID[0]), kCFStringEncodingASCII) && CFStringGetCString(value, cValue, sizeof(cValue)/sizeof(char), kCFStringEncodingASCII)) { + CFStringRef result; + if ((NULL == displayLocale->_prefs) && __CFLocaleKeyTable[slot].name(localeID, cValue, &result)) { + return result; + } + + // We could not find a result using the requested language. Fall back through all preferred languages. + CFArrayRef langPref; + if (displayLocale->_prefs) { + langPref = (CFArrayRef)CFDictionaryGetValue(displayLocale->_prefs, CFSTR("AppleLanguages")); + if (langPref) CFRetain(langPref); + } else { + langPref = (CFArrayRef)CFPreferencesCopyAppValue(CFSTR("AppleLanguages"), kCFPreferencesCurrentApplication); + } + if (langPref != NULL) { + CFIndex count = CFArrayGetCount(langPref); + CFIndex i; + bool success = false; + for (i = 0; i < count && !success; ++i) { + CFStringRef language = (CFStringRef)CFArrayGetValueAtIndex(langPref, i); + CFStringRef cleanLanguage = CFLocaleCreateCanonicalLanguageIdentifierFromString(kCFAllocatorSystemDefault, language); + if (CFStringGetCString(cleanLanguage, localeID, sizeof(localeID)/sizeof(localeID[0]), kCFStringEncodingASCII)) { + success = __CFLocaleKeyTable[slot].name(localeID, cValue, &result); + } + CFRelease(cleanLanguage); + } + CFRelease(langPref); + if (success) + return result; + } + } + return NULL; +} + +CFArrayRef CFLocaleCopyAvailableLocaleIdentifiers(void) { + int32_t locale, localeCount = uloc_countAvailable(); + CFMutableSetRef working = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks); + for (locale = 0; locale < localeCount; ++locale) { + const char *localeID = uloc_getAvailable(locale); + CFStringRef string1 = CFStringCreateWithCString(kCFAllocatorSystemDefault, localeID, kCFStringEncodingASCII); + CFStringRef string2 = CFLocaleCreateCanonicalLocaleIdentifierFromString(kCFAllocatorSystemDefault, string1); + CFSetAddValue(working, string1); + // do not include canonicalized version as IntlFormats cannot cope with that in its popup + CFRelease(string1); + CFRelease(string2); + } + CFIndex cnt = CFSetGetCount(working); +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX || (DEPLOYMENT_TARGET_WINDOWS && __GNUC__) + STACK_BUFFER_DECL(const void *, buffer, cnt); +#else + const void* buffer[BUFFER_SIZE]; +#endif + CFSetGetValues(working, buffer); + CFArrayRef result = CFArrayCreate(kCFAllocatorSystemDefault, buffer, cnt, &kCFTypeArrayCallBacks); + CFRelease(working); + return result; +} + +static CFArrayRef __CFLocaleCopyCStringsAsArray(const char* const* p) { + CFMutableArrayRef working = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); + for (; *p; ++p) { + CFStringRef string = CFStringCreateWithCString(kCFAllocatorSystemDefault, *p, kCFStringEncodingASCII); + CFArrayAppendValue(working, string); + CFRelease(string); + } + CFArrayRef result = CFArrayCreateCopy(kCFAllocatorSystemDefault, working); + CFRelease(working); + return result; +} + +static CFArrayRef __CFLocaleCopyUEnumerationAsArray(UEnumeration *enumer, UErrorCode *icuErr) { + const UChar *next = NULL; + int32_t len = 0; + CFMutableArrayRef working = NULL; + if (U_SUCCESS(*icuErr)) { + working = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); + } + while ((next = uenum_unext(enumer, &len, icuErr)) && U_SUCCESS(*icuErr)) { + CFStringRef string = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)next, (CFIndex) len); + CFArrayAppendValue(working, string); + CFRelease(string); + } + if (*icuErr == U_INDEX_OUTOFBOUNDS_ERROR) { + *icuErr = U_ZERO_ERROR; // Temp: Work around bug (ICU 5220) in ucurr enumerator + } + CFArrayRef result = NULL; + if (U_SUCCESS(*icuErr)) { + result = CFArrayCreateCopy(kCFAllocatorSystemDefault, working); + } + if (working != NULL) { + CFRelease(working); + } + return result; +} + +CFArrayRef CFLocaleCopyISOLanguageCodes(void) { + const char* const* p = uloc_getISOLanguages(); + return __CFLocaleCopyCStringsAsArray(p); +} + +CFArrayRef CFLocaleCopyISOCountryCodes(void) { + const char* const* p = uloc_getISOCountries(); + return __CFLocaleCopyCStringsAsArray(p); +} + +CFArrayRef CFLocaleCopyISOCurrencyCodes(void) { + UErrorCode icuStatus = U_ZERO_ERROR; + UEnumeration *enumer = ucurr_openISOCurrencies(UCURR_ALL, &icuStatus); + CFArrayRef result = __CFLocaleCopyUEnumerationAsArray(enumer, &icuStatus); + uenum_close(enumer); + return result; +} + +CFArrayRef CFLocaleCopyCommonISOCurrencyCodes(void) { + UErrorCode icuStatus = U_ZERO_ERROR; + UEnumeration *enumer = ucurr_openISOCurrencies(UCURR_COMMON|UCURR_NON_DEPRECATED, &icuStatus); + CFArrayRef result = __CFLocaleCopyUEnumerationAsArray(enumer, &icuStatus); + uenum_close(enumer); + return result; +} + +CFArrayRef CFLocaleCopyPreferredLanguages(void) { + CFMutableArrayRef newArray = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); + +#if DEPLOYMENT_TARGET_WINDOWS + LANGID langId = GetUserDefaultLangID(); + CFStringRef lang_name = __CFLocaleWindowsLCIDToISOLocaleName(langId); + CFArrayAppendValue(newArray, lang_name); +#else + CFArrayRef languagesArray = (CFArrayRef)CFPreferencesCopyAppValue(CFSTR("AppleLanguages"), kCFPreferencesCurrentApplication); + if (languagesArray && (CFArrayGetTypeID() == CFGetTypeID(languagesArray))) { + for (CFIndex idx = 0, cnt = CFArrayGetCount(languagesArray); idx < cnt; idx++) { + CFStringRef str = (CFStringRef)CFArrayGetValueAtIndex(languagesArray, idx); + if (str && (CFStringGetTypeID() == CFGetTypeID(str))) { + CFStringRef ident = CFLocaleCreateCanonicalLanguageIdentifierFromString(kCFAllocatorSystemDefault, str); + CFArrayAppendValue(newArray, ident); + CFRelease(ident); + } + } + } + if (languagesArray) CFRelease(languagesArray); +#endif + return newArray; +} + +// -------- -------- -------- -------- -------- -------- + +// These functions return true or false depending on the success or failure of the function. +// In the Copy case, this is failure to fill the *cf out parameter, and that out parameter is +// returned by reference WITH a retain on it. +static bool __CFLocaleSetNOP(CFMutableLocaleRef locale, CFTypeRef cf, CFStringRef context) { + return false; +} + +static bool __CFLocaleCopyLocaleID(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) { + *cf = CFRetain(locale->_identifier); + return true; +} + + +static bool __CFLocaleCopyCodes(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) { + CFDictionaryRef codes = NULL; + // this access of _cache is protected by the lock in CFLocaleGetValue() + if (!CFDictionaryGetValueIfPresent(locale->_cache, CFSTR("__kCFLocaleCodes"), (const void **)&codes)) { + codes = CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault, locale->_identifier); + if (codes) CFDictionarySetValue(locale->_cache, CFSTR("__kCFLocaleCodes"), codes); + if (codes) CFRelease(codes); + } + if (codes) { + CFStringRef value = (CFStringRef)CFDictionaryGetValue(codes, context); // context is one of kCFLocale*Code constants + if (value) CFRetain(value); + *cf = value; + return true; + } + return false; +} + +CFCharacterSetRef _CFCreateCharacterSetFromUSet(USet *set) { + UErrorCode icuErr = U_ZERO_ERROR; + CFMutableCharacterSetRef working = CFCharacterSetCreateMutable(NULL); + UChar buffer[2048]; // Suitable for most small sets + int32_t stringLen; + + if (working == NULL) + return NULL; + + int32_t itemCount = uset_getItemCount(set); + int32_t i; + for (i = 0; i < itemCount; ++i) + { + UChar32 start, end; + UChar * string; + + string = buffer; + stringLen = uset_getItem(set, i, &start, &end, buffer, sizeof(buffer)/sizeof(UChar), &icuErr); + if (icuErr == U_BUFFER_OVERFLOW_ERROR) + { + string = (UChar *) malloc(sizeof(UChar)*(stringLen+1)); + if (!string) + { + CFRelease(working); + return NULL; + } + icuErr = U_ZERO_ERROR; + (void) uset_getItem(set, i, &start, &end, string, stringLen+1, &icuErr); + } + if (U_FAILURE(icuErr)) + { + if (string != buffer) + free(string); + CFRelease(working); + return NULL; + } + if (stringLen <= 0) + CFCharacterSetAddCharactersInRange(working, CFRangeMake(start, end-start+1)); + else + { + CFStringRef cfString = CFStringCreateWithCharactersNoCopy(kCFAllocatorSystemDefault, (UniChar *)string, stringLen, kCFAllocatorNull); + CFCharacterSetAddCharactersInString(working, cfString); + CFRelease(cfString); + } + if (string != buffer) + free(string); + } + + CFCharacterSetRef result = CFCharacterSetCreateCopy(kCFAllocatorSystemDefault, working); + CFRelease(working); + return result; +} + + +static bool __CFLocaleCopyExemplarCharSet(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) { + char localeID[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY]; + if (CFStringGetCString(locale->_identifier, localeID, sizeof(localeID)/sizeof(char), kCFStringEncodingASCII)) { + UErrorCode icuStatus = U_ZERO_ERROR; + ULocaleData* uld = ulocdata_open(localeID, &icuStatus); + USet *set = ulocdata_getExemplarSet(uld, NULL, USET_ADD_CASE_MAPPINGS, ULOCDATA_ES_STANDARD, &icuStatus); + ulocdata_close(uld); + if (U_FAILURE(icuStatus)) + return false; + if (icuStatus == U_USING_DEFAULT_WARNING) // If default locale used, force to empty set + uset_clear(set); + *cf = (CFTypeRef) _CFCreateCharacterSetFromUSet(set); + uset_close(set); + return (*cf != NULL); + } + return false; +} + +static bool __CFLocaleCopyICUKeyword(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context, const char *keyword) +{ + char localeID[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY]; + if (CFStringGetCString(locale->_identifier, localeID, sizeof(localeID)/sizeof(char), kCFStringEncodingASCII)) + { + char value[ULOC_KEYWORD_AND_VALUES_CAPACITY]; + UErrorCode icuStatus = U_ZERO_ERROR; + if (uloc_getKeywordValue(localeID, keyword, value, sizeof(value)/sizeof(char), &icuStatus) > 0 && U_SUCCESS(icuStatus)) + { + *cf = (CFTypeRef) CFStringCreateWithCString(kCFAllocatorSystemDefault, value, kCFStringEncodingASCII); + return true; + } + } + *cf = NULL; + return false; +} + +static bool __CFLocaleCopyCalendarID(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) { + bool succeeded = __CFLocaleCopyICUKeyword(locale, user, cf, context, kCalendarKeyword); + if (succeeded) { + if (CFEqual(*cf, kCFGregorianCalendar)) { + CFRelease(*cf); + *cf = CFRetain(kCFGregorianCalendar); + } else if (CFEqual(*cf, kCFBuddhistCalendar)) { + CFRelease(*cf); + *cf = CFRetain(kCFBuddhistCalendar); + } else if (CFEqual(*cf, kCFJapaneseCalendar)) { + CFRelease(*cf); + *cf = CFRetain(kCFJapaneseCalendar); + } else if (CFEqual(*cf, kCFIslamicCalendar)) { + CFRelease(*cf); + *cf = CFRetain(kCFIslamicCalendar); + } else if (CFEqual(*cf, kCFIslamicCivilCalendar)) { + CFRelease(*cf); + *cf = CFRetain(kCFIslamicCivilCalendar); + } else if (CFEqual(*cf, kCFHebrewCalendar)) { + CFRelease(*cf); + *cf = CFRetain(kCFHebrewCalendar); + } else if (CFEqual(*cf, kCFChineseCalendar)) { + CFRelease(*cf); + *cf = CFRetain(kCFChineseCalendar); + } + } else { + *cf = CFRetain(kCFGregorianCalendar); + } + return true; +} + +static bool __CFLocaleCopyCalendar(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) { + if (__CFLocaleCopyCalendarID(locale, user, cf, context)) { + CFCalendarRef calendar = CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault, (CFStringRef)*cf); + CFCalendarSetLocale(calendar, locale); + CFRelease(*cf); + *cf = calendar; + return true; + } + return false; +} + +static bool __CFLocaleCopyCollationID(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) { + return __CFLocaleCopyICUKeyword(locale, user, cf, context, kCollationKeyword); +} + +static bool __CFLocaleCopyCollatorID(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) { + CFStringRef canonLocaleCFStr = NULL; + if (!canonLocaleCFStr) { + canonLocaleCFStr = CFLocaleGetIdentifier(locale); + CFRetain(canonLocaleCFStr); + } + *cf = canonLocaleCFStr; + return canonLocaleCFStr ? true : false; +} + +static bool __CFLocaleCopyUsesMetric(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) { + bool us = false; // Default is Metric + bool done = false; + + if (!done) { + char localeID[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY]; + if (CFStringGetCString(locale->_identifier, localeID, sizeof(localeID)/sizeof(char), kCFStringEncodingASCII)) { + UErrorCode icuStatus = U_ZERO_ERROR; + UMeasurementSystem ms = UMS_SI; + ms = ulocdata_getMeasurementSystem(localeID, &icuStatus); + if (U_SUCCESS(icuStatus)) { + us = (ms == UMS_US); + done = true; + } + } + } + if (!done) + us = false; + *cf = us ? CFRetain(kCFBooleanFalse) : CFRetain(kCFBooleanTrue); + return true; +} + +static bool __CFLocaleCopyMeasurementSystem(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) { + if (__CFLocaleCopyUsesMetric(locale, user, cf, context)) { + bool us = (*cf == kCFBooleanFalse); + CFRelease(*cf); + *cf = us ? CFRetain(CFSTR("U.S.")) : CFRetain(CFSTR("Metric")); + return true; + } + return false; +} + +static bool __CFLocaleCopyNumberFormat(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) { + CFStringRef str = NULL; + CFNumberFormatterRef nf = CFNumberFormatterCreate(kCFAllocatorSystemDefault, locale, kCFNumberFormatterDecimalStyle); + str = nf ? (CFStringRef)CFNumberFormatterCopyProperty(nf, context) : NULL; + if (nf) CFRelease(nf); + if (str) { + *cf = str; + return true; + } + return false; +} + +// ICU does not reliably set up currency info for other than Currency-type formatters, +// so we have to have another routine here which creates a Currency number formatter. +static bool __CFLocaleCopyNumberFormat2(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) { + CFStringRef str = NULL; + CFNumberFormatterRef nf = CFNumberFormatterCreate(kCFAllocatorSystemDefault, locale, kCFNumberFormatterCurrencyStyle); + str = nf ? (CFStringRef)CFNumberFormatterCopyProperty(nf, context) : NULL; + if (nf) CFRelease(nf); + if (str) { + *cf = str; + return true; + } + return false; +} + +typedef int32_t (*__CFICUFunction)(const char *, const char *, UChar *, int32_t, UErrorCode *); + +static bool __CFLocaleICUName(const char *locale, const char *valLocale, CFStringRef *out, __CFICUFunction icu) { + UErrorCode icuStatus = U_ZERO_ERROR; + int32_t size; + UChar name[kMaxICUNameSize]; + + size = (*icu)(valLocale, locale, name, kMaxICUNameSize, &icuStatus); + if (U_SUCCESS(icuStatus) && size > 0 && icuStatus != U_USING_DEFAULT_WARNING) { + *out = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (UniChar *)name, size); + return (*out != NULL); + } + return false; +} + +static bool __CFLocaleICUKeywordValueName(const char *locale, const char *value, const char *keyword, CFStringRef *out) { + UErrorCode icuStatus = U_ZERO_ERROR; + int32_t size = 0; + UChar name[kMaxICUNameSize]; + // Need to make a fake locale ID + char lid[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY]; + if (strlen(value) < ULOC_KEYWORD_AND_VALUES_CAPACITY) { + snprintf(lid, sizeof(lid), "en_US@%s=%s", keyword, value); + size = uloc_getDisplayKeywordValue(lid, keyword, locale, name, kMaxICUNameSize, &icuStatus); + if (U_SUCCESS(icuStatus) && size > 0 && icuStatus != U_USING_DEFAULT_WARNING) { + *out = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (UniChar *)name, size); + return (*out != NULL); + } + } + return false; +} + +static bool __CFLocaleICUCurrencyName(const char *locale, const char *value, UCurrNameStyle style, CFStringRef *out) { + size_t valLen = strlen(value); + if (valLen != 3) // not a valid ISO code + return false; + UChar curr[4]; + UBool isChoice = FALSE; + int32_t size = 0; + UErrorCode icuStatus = U_ZERO_ERROR; + u_charsToUChars(value, curr, (int32_t)valLen); + curr[valLen] = '\0'; + const UChar *name; + name = ucurr_getName(curr, locale, style, &isChoice, &size, &icuStatus); + if (U_FAILURE(icuStatus) || icuStatus == U_USING_DEFAULT_WARNING) + return false; + UChar result[kMaxICUNameSize]; + if (isChoice) + { + UChar pattern[kMaxICUNameSize]; + CFStringRef patternRef = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("{0,choice,%S}"), name); + CFIndex pattlen = CFStringGetLength(patternRef); + CFStringGetCharacters(patternRef, CFRangeMake(0, pattlen), (UniChar *)pattern); + CFRelease(patternRef); + pattern[pattlen] = '\0'; // null terminate the pattern + // Format the message assuming a large amount of the currency + size = u_formatMessage("en_US", pattern, pattlen, result, kMaxICUNameSize, &icuStatus, 10.0); + if (U_FAILURE(icuStatus)) + return false; + name = result; + + } + *out = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (UniChar *)name, size); + return (*out != NULL); +} + +static bool __CFLocaleFullName(const char *locale, const char *value, CFStringRef *out) { + UErrorCode icuStatus = U_ZERO_ERROR; + int32_t size; + UChar name[kMaxICUNameSize]; + + // First, try to get the full locale. + size = uloc_getDisplayName(value, locale, name, kMaxICUNameSize, &icuStatus); + if (U_FAILURE(icuStatus) || size <= 0) + return false; + + // Did we wind up using a default somewhere? + if (icuStatus == U_USING_DEFAULT_WARNING) { + // For some locale IDs, there may be no language which has a translation for every + // piece. Rather than return nothing, see if we can at least handle + // the language part of the locale. + UErrorCode localStatus = U_ZERO_ERROR; + int32_t localSize; + UChar localName[kMaxICUNameSize]; + localSize = uloc_getDisplayLanguage(value, locale, localName, kMaxICUNameSize, &localStatus); + if (U_FAILURE(localStatus) || size <= 0 || localStatus == U_USING_DEFAULT_WARNING) + return false; + } + + // This locale is OK, so use the result. + *out = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (UniChar *)name, size); + return (*out != NULL); +} + +static bool __CFLocaleLanguageName(const char *locale, const char *value, CFStringRef *out) { + size_t len = strlen(value); + if (len >= 2 && len <= 3) + return __CFLocaleICUName(locale, value, out, uloc_getDisplayLanguage); + return false; +} + +static bool __CFLocaleCountryName(const char *locale, const char *value, CFStringRef *out) { + // Need to make a fake locale ID + char lid[ULOC_FULLNAME_CAPACITY]; + if (strlen(value) == 2) { + snprintf(lid, sizeof(lid), "en_%s", value); + return __CFLocaleICUName(locale, lid, out, uloc_getDisplayCountry); + } + return false; +} + +static bool __CFLocaleScriptName(const char *locale, const char *value, CFStringRef *out) { + // Need to make a fake locale ID + char lid[ULOC_FULLNAME_CAPACITY]; + if (strlen(value) == 4) { + snprintf(lid, sizeof(lid), "en_%s_US", value); + return __CFLocaleICUName(locale, lid, out, uloc_getDisplayScript); + } + return false; +} + +static bool __CFLocaleVariantName(const char *locale, const char *value, CFStringRef *out) { + // Need to make a fake locale ID + char lid[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY]; + if (strlen(value) < ULOC_FULLNAME_CAPACITY) { + snprintf(lid, sizeof(lid), "en_US_%s", value); + return __CFLocaleICUName(locale, lid, out, uloc_getDisplayVariant); + } + return false; +} + +static bool __CFLocaleCalendarName(const char *locale, const char *value, CFStringRef *out) { + return __CFLocaleICUKeywordValueName(locale, value, kCalendarKeyword, out); +} + +static bool __CFLocaleCollationName(const char *locale, const char *value, CFStringRef *out) { + return __CFLocaleICUKeywordValueName(locale, value, kCollationKeyword, out); +} + +static bool __CFLocaleCurrencyShortName(const char *locale, const char *value, CFStringRef *out) { + return __CFLocaleICUCurrencyName(locale, value, UCURR_SYMBOL_NAME, out); +} + +static bool __CFLocaleCurrencyFullName(const char *locale, const char *value, CFStringRef *out) { + return __CFLocaleICUCurrencyName(locale, value, UCURR_LONG_NAME, out); +} + +static bool __CFLocaleNoName(const char *locale, const char *value, CFStringRef *out) { + return false; +} + +// Remember to keep the names such that they would make sense for the user locale, +// in addition to the others; for example, it is "Currency", not "DefaultCurrency". +// (And besides, "Default" is almost always implied.) Words like "Default" and +// "Preferred" and so on should be left out of the names. +CONST_STRING_DECL(kCFLocaleIdentifier, "locale:id") +CONST_STRING_DECL(kCFLocaleLanguageCode, "locale:language code") +CONST_STRING_DECL(kCFLocaleCountryCode, "locale:country code") +CONST_STRING_DECL(kCFLocaleScriptCode, "locale:script code") +CONST_STRING_DECL(kCFLocaleVariantCode, "locale:variant code") +CONST_STRING_DECL(kCFLocaleExemplarCharacterSet, "locale:exemplar characters") +CONST_STRING_DECL(kCFLocaleCalendarIdentifier, "calendar") +CONST_STRING_DECL(kCFLocaleCalendar, "locale:calendarref") +CONST_STRING_DECL(kCFLocaleCollationIdentifier, "collation") +CONST_STRING_DECL(kCFLocaleUsesMetricSystem, "locale:uses metric") +CONST_STRING_DECL(kCFLocaleMeasurementSystem, "locale:measurement system") +CONST_STRING_DECL(kCFLocaleDecimalSeparator, "locale:decimal separator") +CONST_STRING_DECL(kCFLocaleGroupingSeparator, "locale:grouping separator") +CONST_STRING_DECL(kCFLocaleCurrencySymbol, "locale:currency symbol") +CONST_STRING_DECL(kCFLocaleCurrencyCode, "currency") + +CONST_STRING_DECL(kCFGregorianCalendar, "gregorian") +CONST_STRING_DECL(kCFBuddhistCalendar, "buddhist") +CONST_STRING_DECL(kCFJapaneseCalendar, "japanese") +CONST_STRING_DECL(kCFIslamicCalendar, "islamic") +CONST_STRING_DECL(kCFIslamicCivilCalendar, "islamic-civil") +CONST_STRING_DECL(kCFHebrewCalendar, "hebrew") +CONST_STRING_DECL(kCFChineseCalendar, "chinese") + +#undef kMaxICUNameSize +
diff --git a/CoreFoundation/CFLocale.h b/CoreFoundation/CFLocale.h new file mode 100644 index 0000000..7302a55 --- /dev/null +++ b/CoreFoundation/CFLocale.h
@@ -0,0 +1,195 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFLocale.h + Copyright (c) 2002-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFLOCALE__) +#define __COREFOUNDATION_CFLOCALE__ 1 + +#include <CoreFoundation/CFBase.h> +#include <CoreFoundation/CFArray.h> +#include <CoreFoundation/CFDictionary.h> + +#if MAC_OS_X_VERSION_10_3 <= MAC_OS_X_VERSION_MAX_ALLOWED + +CF_EXTERN_C_BEGIN + +typedef const struct __CFLocale *CFLocaleRef; + +CF_EXPORT +CFTypeID CFLocaleGetTypeID(void) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + +CF_EXPORT +CFLocaleRef CFLocaleGetSystem(void) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + // Returns the "root", canonical locale. Contains fixed "backstop" settings. + +CF_EXPORT +CFLocaleRef CFLocaleCopyCurrent(void) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + // Returns the logical "user" locale for the current user. + // [This is Copy in the sense that you get a retain you have to release, + // but we may return the same cached object over and over.] Settings + // you get from this locale do not change under you as CFPreferences + // are changed (for safety and correctness). Generally you would not + // grab this and hold onto it forever, but use it to do the operations + // you need to do at the moment, then throw it away. (The non-changing + // ensures that all the results of your operations are consistent.) + +CF_EXPORT +CFArrayRef CFLocaleCopyAvailableLocaleIdentifiers(void) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + // Returns an array of CFStrings that represents all locales for + // which locale data is available. + +CF_EXPORT +CFArrayRef CFLocaleCopyISOLanguageCodes(void) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + // Returns an array of CFStrings that represents all known legal ISO + // language codes. Note: many of these will not have any supporting + // locale data in Mac OS X. + +CF_EXPORT +CFArrayRef CFLocaleCopyISOCountryCodes(void) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + // Returns an array of CFStrings that represents all known legal ISO + // country codes. Note: many of these will not have any supporting + // locale data in Mac OS X. + +CF_EXPORT +CFArrayRef CFLocaleCopyISOCurrencyCodes(void) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + // Returns an array of CFStrings that represents all known legal ISO + // currency codes. Note: some of these currencies may be obsolete, or + // represent other financial instruments. + +CF_EXPORT +CFArrayRef CFLocaleCopyCommonISOCurrencyCodes(void) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + // Returns an array of CFStrings that represents ISO currency codes for + // currencies in common use. + +CF_EXPORT +CFArrayRef CFLocaleCopyPreferredLanguages(void) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + // Returns the array of canonicalized CFString locale IDs that the user prefers. + +CF_EXPORT +CFStringRef CFLocaleCreateCanonicalLanguageIdentifierFromString(CFAllocatorRef allocator, CFStringRef localeIdentifier) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + // Map an arbitrary language identification string (something close at + // least) to a canonical language identifier. + +CF_EXPORT +CFStringRef CFLocaleCreateCanonicalLocaleIdentifierFromString(CFAllocatorRef allocator, CFStringRef localeIdentifier) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + // Map an arbitrary locale identification string (something close at + // least) to the canonical identifier. + +CF_EXPORT +CFStringRef CFLocaleCreateCanonicalLocaleIdentifierFromScriptManagerCodes(CFAllocatorRef allocator, LangCode lcode, RegionCode rcode) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + // Map a Mac OS LangCode and RegionCode to the canonical locale identifier. + +CF_EXPORT +CFDictionaryRef CFLocaleCreateComponentsFromLocaleIdentifier(CFAllocatorRef allocator, CFStringRef localeID) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + // Parses a locale ID consisting of language, script, country, variant, + // and keyword/value pairs into a dictionary. The keys are the constant + // CFStrings corresponding to the locale ID components, and the values + // will correspond to constants where available. + // Example: "en_US@calendar=japanese" yields a dictionary with three + // entries: kCFLocaleLanguageCode=en, kCFLocaleCountryCode=US, and + // kCFLocaleCalendarIdentifier=kCFJapaneseCalendar. + +CF_EXPORT +CFStringRef CFLocaleCreateLocaleIdentifierFromComponents(CFAllocatorRef allocator, CFDictionaryRef dictionary) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + // Reverses the actions of CFLocaleCreateDictionaryFromLocaleIdentifier, + // creating a single string from the data in the dictionary. The + // dictionary {kCFLocaleLanguageCode=en, kCFLocaleCountryCode=US, + // kCFLocaleCalendarIdentifier=kCFJapaneseCalendar} becomes + // "en_US@calendar=japanese". + +CF_EXPORT +CFLocaleRef CFLocaleCreate(CFAllocatorRef allocator, CFStringRef localeIdentifier) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + // Returns a CFLocaleRef for the locale named by the "arbitrary" locale identifier. + +CF_EXPORT +CFLocaleRef CFLocaleCreateCopy(CFAllocatorRef allocator, CFLocaleRef locale) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + // Having gotten a CFLocale from somebody, code should make a copy + // if it is going to use it for several operations + // or hold onto it. In the future, there may be mutable locales. + +CF_EXPORT +CFStringRef CFLocaleGetIdentifier(CFLocaleRef locale) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + // Returns the locale's identifier. This may not be the same string + // that the locale was created with (CFLocale may canonicalize it). + +CF_EXPORT +CFTypeRef CFLocaleGetValue(CFLocaleRef locale, CFStringRef key) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + // Returns the value for the given key. This is how settings and state + // are accessed via a CFLocale. Values might be of any CF type. + +CF_EXPORT +CFStringRef CFLocaleCopyDisplayNameForPropertyValue(CFLocaleRef displayLocale, CFStringRef key, CFStringRef value) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + // Returns the display name for the given value. The key tells what + // the value is, and is one of the usual locale property keys, though + // not all locale property keys have values with display name values. + + +CF_EXPORT const CFStringRef kCFLocaleCurrentLocaleDidChangeNotification AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + + +// Locale Keys +CF_EXPORT const CFStringRef kCFLocaleIdentifier AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFLocaleLanguageCode AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFLocaleCountryCode AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFLocaleScriptCode AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFLocaleVariantCode AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +CF_EXPORT const CFStringRef kCFLocaleExemplarCharacterSet AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFLocaleCalendarIdentifier AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFLocaleCalendar AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFLocaleCollationIdentifier AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFLocaleUsesMetricSystem AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFLocaleMeasurementSystem AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // "Metric" or "U.S." +CF_EXPORT const CFStringRef kCFLocaleDecimalSeparator AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; +CF_EXPORT const CFStringRef kCFLocaleGroupingSeparator AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; +CF_EXPORT const CFStringRef kCFLocaleCurrencySymbol AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; +CF_EXPORT const CFStringRef kCFLocaleCurrencyCode AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // ISO 3-letter currency code + +// Values for kCFLocaleCalendarIdentifier +CF_EXPORT const CFStringRef kCFGregorianCalendar AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; +CF_EXPORT const CFStringRef kCFBuddhistCalendar AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFChineseCalendar AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFHebrewCalendar AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFIslamicCalendar AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFIslamicCivilCalendar AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFJapaneseCalendar AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + + +CF_EXTERN_C_END + +#endif + +#endif /* ! __COREFOUNDATION_CFLOCALE__ */ +
diff --git a/CoreFoundation/CFLocaleIdentifier.c b/CoreFoundation/CFLocaleIdentifier.c new file mode 100644 index 0000000..b380a89 --- /dev/null +++ b/CoreFoundation/CFLocaleIdentifier.c
@@ -0,0 +1,1911 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* + CFLocaleIdentifier.c + Copyright (c) 2002-2007, Apple Inc. All rights reserved. + Responsibility: Christopher Kane + + CFLocaleIdentifier.c defines + - enum value kLocaleIdentifierCStringMax + - structs KeyStringToResultString, SpecialCaseUpdates + and provides the following data for the functions + CFLocaleCreateCanonicalLocaleIdentifierFromScriptManagerCodes, + CFLocaleCreateCanonicalLocaleIdentifierFromString + CFLocaleCreateCanonicalLanguageIdentifierFromString + + 1. static const char * regionCodeToLocaleString[]; enum kNumRegionCodeToLocaleString; + map RegionCode 0..kNumRegionCodeToLocaleString-1 to canonical locale string + + 2. static const char * langCodeToLocaleString[]; enum kNumLangCodeToLocaleString; + map LangCode 0..kNumLangCodeToLocaleString-1 to canonical locale string + + 3. static const KeyStringToResultString oldAppleLocaleToCanonical[]; enum kNumOldAppleLocaleToCanonical; + map old Apple string oldAppleLocaleToCanonical[n].key + to canonical locale string oldAppleLocaleToCanonical[n].result + for n = 0..kNumOldAppleLocaleToCanonical-1 + + 4. static const KeyStringToResultString localeStringPrefixToCanonical[]; enum kNumLocaleStringPrefixToCanonical; + map non-canonical language prefix (3-letter, obsolete) localeStringPrefixToCanonical[].key + to updated replacement localeStringPrefixToCanonical[].result + for n = 0..kNumLocaleStringPrefixToCanonical-1 + + 5. static const SpecialCaseUpdates specialCases[]; + various special cases for updating region codes, or for updating language codes based on region codes + + 6. static const KeyStringToResultString localeStringRegionToDefaults[]; enum kNumLocaleStringRegionToDefaults; + map locale string region tag localeStringRegionToDefaults[n].key + to default substrings to delete localeStringRegionToDefaults[n].result + for n = 0..kNumLocaleStringRegionToDefaults-1 + + 7. static const KeyStringToResultString localeStringPrefixToDefaults[]; enum kNumLocaleStringPrefixToDefaults; + map locale string initial part localeStringPrefixToDefaults[n].key + to default substrings to delete localeStringPrefixToDefaults[n].result + for n = 0..kNumLocaleStringPrefixToDefaults-1 + + 8. static const KeyStringToResultString appleLocaleToLanguageString[]; enum kNumAppleLocaleToLanguageString; + map Apple locale string appleLocaleToLanguageString[].key + to equivalent language string appleLocaleToLanguageString[].result + for n = 0..kNumAppleLocaleToLanguageString-1 + +*/ + +#include <CoreFoundation/CFString.h> +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#include <unicode/uloc.h> + +#if DEPLOYMENT_TARGET_WINDOWS +extern size_t strlcpy(char *dst, const char *src, size_t siz); +extern size_t strlcat(char *dst, const char *src, size_t siz); +#endif + +// Max byte length of locale identifier (ASCII) as C string, including terminating null byte +enum { + kLocaleIdentifierCStringMax = ULOC_FULLNAME_CAPACITY + ULOC_KEYWORD_AND_VALUES_CAPACITY // currently 56 + 100 +}; + +// KeyStringToResultString struct used in data tables for CFLocaleCreateCanonicalLocaleIdentifierFromString +struct KeyStringToResultString { + const char * key; + const char * result; +}; +typedef struct KeyStringToResultString KeyStringToResultString; + +// SpecialCaseUpdates struct used in data tables for CFLocaleCreateCanonicalLocaleIdentifierFromString +struct SpecialCaseUpdates { + const char * lang; + const char * reg1; + const char * update1; + const char * reg2; + const char * update2; +}; +typedef struct SpecialCaseUpdates SpecialCaseUpdates; + + +static const char * const regionCodeToLocaleString[] = { +// map RegionCode (array index) to canonical locale string +// +// canon. string region code; language code; [comment] [ # __CFBundleLocaleAbbreviationsArray +// -------- ------------ ------------------ ------------ -------- string, if different ] + "en_US", // 0 verUS; 0 langEnglish; + "fr_FR", // 1 verFrance; 1 langFrench; + "en_GB", // 2 verBritain; 0 langEnglish; + "de_DE", // 3 verGermany; 2 langGerman; + "it_IT", // 4 verItaly; 3 langItalian; + "nl_NL", // 5 verNetherlands; 4 langDutch; + "nl_BE", // 6 verFlemish; 34 langFlemish (redundant, =Dutch); + "sv_SE", // 7 verSweden; 5 langSwedish; + "es_ES", // 8 verSpain; 6 langSpanish; + "da_DK", // 9 verDenmark; 7 langDanish; + "pt_PT", // 10 verPortugal; 8 langPortuguese; + "fr_CA", // 11 verFrCanada; 1 langFrench; + "nb_NO", // 12 verNorway; 9 langNorwegian (Bokmal); # "no_NO" + "he_IL", // 13 verIsrael; 10 langHebrew; + "ja_JP", // 14 verJapan; 11 langJapanese; + "en_AU", // 15 verAustralia; 0 langEnglish; + "ar", // 16 verArabic; 12 langArabic; + "fi_FI", // 17 verFinland; 13 langFinnish; + "fr_CH", // 18 verFrSwiss; 1 langFrench; + "de_CH", // 19 verGrSwiss; 2 langGerman; + "el_GR", // 20 verGreece; 14 langGreek (modern)-Grek-mono; + "is_IS", // 21 verIceland; 15 langIcelandic; + "mt_MT", // 22 verMalta; 16 langMaltese; + "el_CY", // 23 verCyprus; 14 langGreek?; el or tr? guess el # "" + "tr_TR", // 24 verTurkey; 17 langTurkish; + "hr_HR", // 25 verYugoCroatian; 18 langCroatian; * one-way mapping -> verCroatia + "nl_NL", // 26 KCHR, Netherlands; 4 langDutch; * one-way mapping + "nl_BE", // 27 KCHR, verFlemish; 34 langFlemish; * one-way mapping + "_CA", // 28 KCHR, Canada-en/fr?; -1 none; * one-way mapping # "en_CA" + "_CA", // 29 KCHR, Canada-en/fr?; -1 none; * one-way mapping # "en_CA" + "pt_PT", // 30 KCHR, Portugal; 8 langPortuguese; * one-way mapping + "nb_NO", // 31 KCHR, Norway; 9 langNorwegian (Bokmal); * one-way mapping # "no_NO" + "da_DK", // 32 KCHR, Denmark; 7 langDanish; * one-way mapping + "hi_IN", // 33 verIndiaHindi; 21 langHindi; + "ur_PK", // 34 verPakistanUrdu; 20 langUrdu; + "tr_TR", // 35 verTurkishModified; 17 langTurkish; * one-way mapping + "it_CH", // 36 verItalianSwiss; 3 langItalian; + "en_001", // 37 verInternational; 0 langEnglish; ASCII only # "en" + NULL, // 38 *unassigned; -1 none; * one-way mapping # "" + "ro_RO", // 39 verRomania; 37 langRomanian; + "grc", // 40 verGreekAncient; 148 langGreekAncient -Grek-poly; # "el_GR" + "lt_LT", // 41 verLithuania; 24 langLithuanian; + "pl_PL", // 42 verPoland; 25 langPolish; + "hu_HU", // 43 verHungary; 26 langHungarian; + "et_EE", // 44 verEstonia; 27 langEstonian; + "lv_LV", // 45 verLatvia; 28 langLatvian; + "se", // 46 verSami; 29 langSami; + "fo_FO", // 47 verFaroeIsl; 30 langFaroese; + "fa_IR", // 48 verIran; 31 langFarsi/Persian; + "ru_RU", // 49 verRussia; 32 langRussian; + "ga_IE", // 50 verIreland; 35 langIrishGaelic (no dots); + "ko_KR", // 51 verKorea; 23 langKorean; + "zh_CN", // 52 verChina; 33 langSimpChinese; + "zh_TW", // 53 verTaiwan; 19 langTradChinese; + "th_TH", // 54 verThailand; 22 langThai; + "und", // 55 verScriptGeneric; -1 none; # "" // <1.9> + "cs_CZ", // 56 verCzech; 38 langCzech; + "sk_SK", // 57 verSlovak; 39 langSlovak; + "und", // 58 verEastAsiaGeneric; -1 none; * one-way mapping # "" // <1.9> + "hu_HU", // 59 verMagyar; 26 langHungarian; * one-way mapping -> verHungary + "bn", // 60 verBengali; 67 langBengali; _IN or _BD? guess generic + "be_BY", // 61 verBelarus; 46 langBelorussian; + "uk_UA", // 62 verUkraine; 45 langUkrainian; + NULL, // 63 *unused; -1 none; * one-way mapping # "" + "el_GR", // 64 verGreeceAlt; 14 langGreek (modern)-Grek-mono; * one-way mapping + "sr_CS", // 65 verSerbian; 42 langSerbian -Cyrl; // <1.18> + "sl_SI", // 66 verSlovenian; 40 langSlovenian; + "mk_MK", // 67 verMacedonian; 43 langMacedonian; + "hr_HR", // 68 verCroatia; 18 langCroatian; + NULL, // 69 *unused; -1 none; * one-way mapping # "" + "de-1996", // 70 verGermanReformed; 2 langGerman; 1996 orthogr. # "de_DE" + "pt_BR", // 71 verBrazil; 8 langPortuguese; + "bg_BG", // 72 verBulgaria; 44 langBulgarian; + "ca_ES", // 73 verCatalonia; 130 langCatalan; + "mul", // 74 verMultilingual; -1 none; # "" + "gd", // 75 verScottishGaelic; 144 langScottishGaelic; + "gv", // 76 verManxGaelic; 145 langManxGaelic; + "br", // 77 verBreton; 142 langBreton; + "iu_CA", // 78 verNunavut; 143 langInuktitut -Cans; + "cy", // 79 verWelsh; 128 langWelsh; + "_CA", // 80 KCHR, Canada-en/fr?; -1 none; * one-way mapping # "en_CA" + "ga-Latg_IE", // 81 verIrishGaelicScrip; 146 langIrishGaelicScript -dots; # "ga_IE" // <xx> + "en_CA", // 82 verEngCanada; 0 langEnglish; + "dz_BT", // 83 verBhutan; 137 langDzongkha; + "hy_AM", // 84 verArmenian; 51 langArmenian; + "ka_GE", // 85 verGeorgian; 52 langGeorgian; + "es_419", // 86 verSpLatinAmerica; 6 langSpanish; # "es" + "es_ES", // 87 KCHR, Spain; 6 langSpanish; * one-way mapping + "to_TO", // 88 verTonga; 147 langTongan; + "pl_PL", // 89 KCHR, Poland; 25 langPolish; * one-way mapping + "ca_ES", // 90 KCHR, Catalonia; 130 langCatalan; * one-way mapping + "fr_001", // 91 verFrenchUniversal; 1 langFrench; + "de_AT", // 92 verAustria; 2 langGerman; + "es_419", // 93 > verSpLatinAmerica; 6 langSpanish; * one-way mapping # "es" + "gu_IN", // 94 verGujarati; 69 langGujarati; + "pa", // 95 verPunjabi; 70 langPunjabi; _IN or _PK? guess generic + "ur_IN", // 96 verIndiaUrdu; 20 langUrdu; + "vi_VN", // 97 verVietnam; 80 langVietnamese; + "fr_BE", // 98 verFrBelgium; 1 langFrench; + "uz_UZ", // 99 verUzbek; 47 langUzbek; + "en_SG", // 100 verSingapore; 0 langEnglish?; en, zh, or ms? guess en # "" + "nn_NO", // 101 verNynorsk; 151 langNynorsk; # "" + "af_ZA", // 102 verAfrikaans; 141 langAfrikaans; + "eo", // 103 verEsperanto; 94 langEsperanto; + "mr_IN", // 104 verMarathi; 66 langMarathi; + "bo", // 105 verTibetan; 63 langTibetan; + "ne_NP", // 106 verNepal; 64 langNepali; + "kl", // 107 verGreenland; 149 langGreenlandic; + "en_IE", // 108 verIrelandEnglish; 0 langEnglish; # (no entry) +}; +enum { + kNumRegionCodeToLocaleString = sizeof(regionCodeToLocaleString)/sizeof(char *) +}; + +static const char * const langCodeToLocaleString[] = { +// map LangCode (array index) to canonical locale string +// +// canon. string language code; [ comment] [ # __CFBundleLanguageAbbreviationsArray +// -------- -------------- ---------- -------- string, if different ] + "en", // 0 langEnglish; + "fr", // 1 langFrench; + "de", // 2 langGerman; + "it", // 3 langItalian; + "nl", // 4 langDutch; + "sv", // 5 langSwedish; + "es", // 6 langSpanish; + "da", // 7 langDanish; + "pt", // 8 langPortuguese; + "nb", // 9 langNorwegian (Bokmal); # "no" + "he", // 10 langHebrew -Hebr; + "ja", // 11 langJapanese -Jpan; + "ar", // 12 langArabic -Arab; + "fi", // 13 langFinnish; + "el", // 14 langGreek (modern)-Grek-mono; + "is", // 15 langIcelandic; + "mt", // 16 langMaltese -Latn; + "tr", // 17 langTurkish -Latn; + "hr", // 18 langCroatian; + "zh-Hant", // 19 langTradChinese; # "zh" + "ur", // 20 langUrdu -Arab; + "hi", // 21 langHindi -Deva; + "th", // 22 langThai -Thai; + "ko", // 23 langKorean -Hang; + "lt", // 24 langLithuanian; + "pl", // 25 langPolish; + "hu", // 26 langHungarian; + "et", // 27 langEstonian; + "lv", // 28 langLatvian; + "se", // 29 langSami; + "fo", // 30 langFaroese; + "fa", // 31 langFarsi/Persian -Arab; + "ru", // 32 langRussian -Cyrl; + "zh-Hans", // 33 langSimpChinese; # "zh" + "nl-BE", // 34 langFlemish (redundant, =Dutch); # "nl" + "ga", // 35 langIrishGaelic (no dots); + "sq", // 36 langAlbanian; no region codes + "ro", // 37 langRomanian; + "cs", // 38 langCzech; + "sk", // 39 langSlovak; + "sl", // 40 langSlovenian; + "yi", // 41 langYiddish -Hebr; no region codes + "sr", // 42 langSerbian -Cyrl; + "mk", // 43 langMacedonian -Cyrl; + "bg", // 44 langBulgarian -Cyrl; + "uk", // 45 langUkrainian -Cyrl; + "be", // 46 langBelorussian -Cyrl; + "uz-Cyrl", // 47 langUzbek -Cyrl; also -Latn, -Arab + "kk", // 48 langKazakh -Cyrl; no region codes; also -Latn, -Arab + "az-Cyrl", // 49 langAzerbaijani -Cyrl; no region codes # "az" + "az-Arab", // 50 langAzerbaijanAr -Arab; no region codes # "az" + "hy", // 51 langArmenian -Armn; + "ka", // 52 langGeorgian -Geor; + "mo", // 53 langMoldavian -Cyrl; no region codes + "ky", // 54 langKirghiz -Cyrl; no region codes; also -Latn, -Arab + "tg-Cyrl", // 55 langTajiki -Cyrl; no region codes; also -Latn, -Arab + "tk-Cyrl", // 56 langTurkmen -Cyrl; no region codes; also -Latn, -Arab + "mn-Mong", // 57 langMongolian -Mong; no region codes # "mn" + "mn-Cyrl", // 58 langMongolianCyr -Cyrl; no region codes # "mn" + "ps", // 59 langPashto -Arab; no region codes + "ku", // 60 langKurdish -Arab; no region codes + "ks", // 61 langKashmiri -Arab; no region codes + "sd", // 62 langSindhi -Arab; no region codes + "bo", // 63 langTibetan -Tibt; + "ne", // 64 langNepali -Deva; + "sa", // 65 langSanskrit -Deva; no region codes + "mr", // 66 langMarathi -Deva; + "bn", // 67 langBengali -Beng; + "as", // 68 langAssamese -Beng; no region codes + "gu", // 69 langGujarati -Gujr; + "pa", // 70 langPunjabi -Guru; + "or", // 71 langOriya -Orya; no region codes + "ml", // 72 langMalayalam -Mlym; no region codes + "kn", // 73 langKannada -Knda; no region codes + "ta", // 74 langTamil -Taml; no region codes + "te", // 75 langTelugu -Telu; no region codes + "si", // 76 langSinhalese -Sinh; no region codes + "my", // 77 langBurmese -Mymr; no region codes + "km", // 78 langKhmer -Khmr; no region codes + "lo", // 79 langLao -Laoo; no region codes + "vi", // 80 langVietnamese -Latn; + "id", // 81 langIndonesian -Latn; no region codes + "tl", // 82 langTagalog -Latn; no region codes + "ms", // 83 langMalayRoman -Latn; no region codes # "ms" + "ms-Arab", // 84 langMalayArabic -Arab; no region codes # "ms" + "am", // 85 langAmharic -Ethi; no region codes + "ti", // 86 langTigrinya -Ethi; no region codes + "om", // 87 langOromo -Ethi; no region codes + "so", // 88 langSomali -Latn; no region codes + "sw", // 89 langSwahili -Latn; no region codes + "rw", // 90 langKinyarwanda -Latn; no region codes + "rn", // 91 langRundi -Latn; no region codes + "ny", // 92 langNyanja/Chewa -Latn; no region codes # "" + "mg", // 93 langMalagasy -Latn; no region codes + "eo", // 94 langEsperanto -Latn; + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, // 95 to 105 (gap) + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, // 106 to 116 (gap) + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, // 107 to 117 (gap) + "cy", // 128 langWelsh -Latn; + "eu", // 129 langBasque -Latn; no region codes + "ca", // 130 langCatalan -Latn; + "la", // 131 langLatin -Latn; no region codes + "qu", // 132 langQuechua -Latn; no region codes + "gn", // 133 langGuarani -Latn; no region codes + "ay", // 134 langAymara -Latn; no region codes + "tt-Cyrl", // 135 langTatar -Cyrl; no region codes + "ug", // 136 langUighur -Arab; no region codes + "dz", // 137 langDzongkha -Tibt; + "jv", // 138 langJavaneseRom -Latn; no region codes + "su", // 139 langSundaneseRom -Latn; no region codes + "gl", // 140 langGalician -Latn; no region codes + "af", // 141 langAfrikaans -Latn; + "br", // 142 langBreton -Latn; + "iu", // 143 langInuktitut -Cans; + "gd", // 144 langScottishGaelic; + "gv", // 145 langManxGaelic -Latn; + "ga-Latg", // 146 langIrishGaelicScript -Latn-dots; # "ga" // <xx> + "to", // 147 langTongan -Latn; + "grc", // 148 langGreekAncient -Grek-poly; # "el" + "kl", // 149 langGreenlandic -Latn; + "az-Latn", // 150 langAzerbaijanRoman -Latn; no region codes # "az" + "nn", // 151 langNynorsk -Latn; # (no entry) +}; +enum { + kNumLangCodeToLocaleString = sizeof(langCodeToLocaleString)/sizeof(char *) +}; + +static const KeyStringToResultString oldAppleLocaleToCanonical[] = { +// Map obsolete/old-style Apple strings to canonical +// Must be sorted according to how strcmp compares the strings in the first column +// +// non-canonical canonical [ comment ] # source/reason for non-canonical string +// string string +// ------------- --------- + { "Afrikaans", "af" }, // # __CFBundleLanguageNamesArray + { "Albanian", "sq" }, // # __CFBundleLanguageNamesArray + { "Amharic", "am" }, // # __CFBundleLanguageNamesArray + { "Arabic", "ar" }, // # __CFBundleLanguageNamesArray + { "Armenian", "hy" }, // # __CFBundleLanguageNamesArray + { "Assamese", "as" }, // # __CFBundleLanguageNamesArray + { "Aymara", "ay" }, // # __CFBundleLanguageNamesArray + { "Azerbaijani", "az" }, // -Arab,-Cyrl,-Latn? # __CFBundleLanguageNamesArray (had 3 entries "Azerbaijani" for "az-Arab", "az-Cyrl", "az-Latn") + { "Basque", "eu" }, // # __CFBundleLanguageNamesArray + { "Belarusian", "be" }, // # handle other names + { "Belorussian", "be" }, // # handle other names + { "Bengali", "bn" }, // # __CFBundleLanguageNamesArray + { "Brazilian Portugese", "pt-BR" }, // # from Installer.app Info.plist IFLanguages key, misspelled + { "Brazilian Portuguese", "pt-BR" }, // # correct spelling for above + { "Breton", "br" }, // # __CFBundleLanguageNamesArray + { "Bulgarian", "bg" }, // # __CFBundleLanguageNamesArray + { "Burmese", "my" }, // # __CFBundleLanguageNamesArray + { "Byelorussian", "be" }, // # __CFBundleLanguageNamesArray + { "Catalan", "ca" }, // # __CFBundleLanguageNamesArray + { "Chewa", "ny" }, // # handle other names + { "Chichewa", "ny" }, // # handle other names + { "Chinese", "zh" }, // -Hans,-Hant? # __CFBundleLanguageNamesArray (had 2 entries "Chinese" for "zh-Hant", "zh-Hans") + { "Chinese, Simplified", "zh-Hans" }, // # from Installer.app Info.plist IFLanguages key + { "Chinese, Traditional", "zh-Hant" }, // # correct spelling for below + { "Chinese, Tradtional", "zh-Hant" }, // # from Installer.app Info.plist IFLanguages key, misspelled + { "Croatian", "hr" }, // # __CFBundleLanguageNamesArray + { "Czech", "cs" }, // # __CFBundleLanguageNamesArray + { "Danish", "da" }, // # __CFBundleLanguageNamesArray + { "Dutch", "nl" }, // # __CFBundleLanguageNamesArray (had 2 entries "Dutch" for "nl", "nl-BE") + { "Dzongkha", "dz" }, // # __CFBundleLanguageNamesArray + { "English", "en" }, // # __CFBundleLanguageNamesArray + { "Esperanto", "eo" }, // # __CFBundleLanguageNamesArray + { "Estonian", "et" }, // # __CFBundleLanguageNamesArray + { "Faroese", "fo" }, // # __CFBundleLanguageNamesArray + { "Farsi", "fa" }, // # __CFBundleLanguageNamesArray + { "Finnish", "fi" }, // # __CFBundleLanguageNamesArray + { "Flemish", "nl-BE" }, // # handle other names + { "French", "fr" }, // # __CFBundleLanguageNamesArray + { "Galician", "gl" }, // # __CFBundleLanguageNamesArray + { "Gallegan", "gl" }, // # handle other names + { "Georgian", "ka" }, // # __CFBundleLanguageNamesArray + { "German", "de" }, // # __CFBundleLanguageNamesArray + { "Greek", "el" }, // # __CFBundleLanguageNamesArray (had 2 entries "Greek" for "el", "grc") + { "Greenlandic", "kl" }, // # __CFBundleLanguageNamesArray + { "Guarani", "gn" }, // # __CFBundleLanguageNamesArray + { "Gujarati", "gu" }, // # __CFBundleLanguageNamesArray + { "Hawaiian", "haw" }, // # handle new languages + { "Hebrew", "he" }, // # __CFBundleLanguageNamesArray + { "Hindi", "hi" }, // # __CFBundleLanguageNamesArray + { "Hungarian", "hu" }, // # __CFBundleLanguageNamesArray + { "Icelandic", "is" }, // # __CFBundleLanguageNamesArray + { "Indonesian", "id" }, // # __CFBundleLanguageNamesArray + { "Inuktitut", "iu" }, // # __CFBundleLanguageNamesArray + { "Irish", "ga" }, // # __CFBundleLanguageNamesArray (had 2 entries "Irish" for "ga", "ga-dots") + { "Italian", "it" }, // # __CFBundleLanguageNamesArray + { "Japanese", "ja" }, // # __CFBundleLanguageNamesArray + { "Javanese", "jv" }, // # __CFBundleLanguageNamesArray + { "Kalaallisut", "kl" }, // # handle other names + { "Kannada", "kn" }, // # __CFBundleLanguageNamesArray + { "Kashmiri", "ks" }, // # __CFBundleLanguageNamesArray + { "Kazakh", "kk" }, // # __CFBundleLanguageNamesArray + { "Khmer", "km" }, // # __CFBundleLanguageNamesArray + { "Kinyarwanda", "rw" }, // # __CFBundleLanguageNamesArray + { "Kirghiz", "ky" }, // # __CFBundleLanguageNamesArray + { "Korean", "ko" }, // # __CFBundleLanguageNamesArray + { "Kurdish", "ku" }, // # __CFBundleLanguageNamesArray + { "Lao", "lo" }, // # __CFBundleLanguageNamesArray + { "Latin", "la" }, // # __CFBundleLanguageNamesArray + { "Latvian", "lv" }, // # __CFBundleLanguageNamesArray + { "Lithuanian", "lt" }, // # __CFBundleLanguageNamesArray + { "Macedonian", "mk" }, // # __CFBundleLanguageNamesArray + { "Malagasy", "mg" }, // # __CFBundleLanguageNamesArray + { "Malay", "ms" }, // -Latn,-Arab? # __CFBundleLanguageNamesArray (had 2 entries "Malay" for "ms-Latn", "ms-Arab") + { "Malayalam", "ml" }, // # __CFBundleLanguageNamesArray + { "Maltese", "mt" }, // # __CFBundleLanguageNamesArray + { "Manx", "gv" }, // # __CFBundleLanguageNamesArray + { "Marathi", "mr" }, // # __CFBundleLanguageNamesArray + { "Moldavian", "mo" }, // # __CFBundleLanguageNamesArray + { "Mongolian", "mn" }, // -Mong,-Cyrl? # __CFBundleLanguageNamesArray (had 2 entries "Mongolian" for "mn-Mong", "mn-Cyrl") + { "Nepali", "ne" }, // # __CFBundleLanguageNamesArray + { "Norwegian", "nb" }, // # __CFBundleLanguageNamesArray (had "Norwegian" mapping to "no") + { "Nyanja", "ny" }, // # __CFBundleLanguageNamesArray + { "Nynorsk", "nn" }, // # handle other names (no entry in __CFBundleLanguageNamesArray) + { "Oriya", "or" }, // # __CFBundleLanguageNamesArray + { "Oromo", "om" }, // # __CFBundleLanguageNamesArray + { "Panjabi", "pa" }, // # handle other names + { "Pashto", "ps" }, // # __CFBundleLanguageNamesArray + { "Persian", "fa" }, // # handle other names + { "Polish", "pl" }, // # __CFBundleLanguageNamesArray + { "Portuguese", "pt" }, // # __CFBundleLanguageNamesArray + { "Portuguese, Brazilian", "pt-BR" }, // # handle other names + { "Punjabi", "pa" }, // # __CFBundleLanguageNamesArray + { "Pushto", "ps" }, // # handle other names + { "Quechua", "qu" }, // # __CFBundleLanguageNamesArray + { "Romanian", "ro" }, // # __CFBundleLanguageNamesArray + { "Ruanda", "rw" }, // # handle other names + { "Rundi", "rn" }, // # __CFBundleLanguageNamesArray + { "Russian", "ru" }, // # __CFBundleLanguageNamesArray + { "Sami", "se" }, // # __CFBundleLanguageNamesArray + { "Sanskrit", "sa" }, // # __CFBundleLanguageNamesArray + { "Scottish", "gd" }, // # __CFBundleLanguageNamesArray + { "Serbian", "sr" }, // # __CFBundleLanguageNamesArray + { "Simplified Chinese", "zh-Hans" }, // # handle other names + { "Sindhi", "sd" }, // # __CFBundleLanguageNamesArray + { "Sinhalese", "si" }, // # __CFBundleLanguageNamesArray + { "Slovak", "sk" }, // # __CFBundleLanguageNamesArray + { "Slovenian", "sl" }, // # __CFBundleLanguageNamesArray + { "Somali", "so" }, // # __CFBundleLanguageNamesArray + { "Spanish", "es" }, // # __CFBundleLanguageNamesArray + { "Sundanese", "su" }, // # __CFBundleLanguageNamesArray + { "Swahili", "sw" }, // # __CFBundleLanguageNamesArray + { "Swedish", "sv" }, // # __CFBundleLanguageNamesArray + { "Tagalog", "tl" }, // # __CFBundleLanguageNamesArray + { "Tajik", "tg" }, // # handle other names + { "Tajiki", "tg" }, // # __CFBundleLanguageNamesArray + { "Tamil", "ta" }, // # __CFBundleLanguageNamesArray + { "Tatar", "tt" }, // # __CFBundleLanguageNamesArray + { "Telugu", "te" }, // # __CFBundleLanguageNamesArray + { "Thai", "th" }, // # __CFBundleLanguageNamesArray + { "Tibetan", "bo" }, // # __CFBundleLanguageNamesArray + { "Tigrinya", "ti" }, // # __CFBundleLanguageNamesArray + { "Tongan", "to" }, // # __CFBundleLanguageNamesArray + { "Traditional Chinese", "zh-Hant" }, // # handle other names + { "Turkish", "tr" }, // # __CFBundleLanguageNamesArray + { "Turkmen", "tk" }, // # __CFBundleLanguageNamesArray + { "Uighur", "ug" }, // # __CFBundleLanguageNamesArray + { "Ukrainian", "uk" }, // # __CFBundleLanguageNamesArray + { "Urdu", "ur" }, // # __CFBundleLanguageNamesArray + { "Uzbek", "uz" }, // # __CFBundleLanguageNamesArray + { "Vietnamese", "vi" }, // # __CFBundleLanguageNamesArray + { "Welsh", "cy" }, // # __CFBundleLanguageNamesArray + { "Yiddish", "yi" }, // # __CFBundleLanguageNamesArray + { "ar_??", "ar" }, // # from old MapScriptInfoAndISOCodes + { "az.Ar", "az-Arab" }, // # from old LocaleRefGetPartString + { "az.Cy", "az-Cyrl" }, // # from old LocaleRefGetPartString + { "az.La", "az-Latn" }, // # from old LocaleRefGetPartString + { "be_??", "be_BY" }, // # from old MapScriptInfoAndISOCodes + { "bn_??", "bn" }, // # from old LocaleRefGetPartString + { "bo_??", "bo" }, // # from old MapScriptInfoAndISOCodes + { "br_??", "br" }, // # from old MapScriptInfoAndISOCodes + { "cy_??", "cy" }, // # from old MapScriptInfoAndISOCodes + { "de-96", "de-1996" }, // # from old MapScriptInfoAndISOCodes // <1.9> + { "de_96", "de-1996" }, // # from old MapScriptInfoAndISOCodes // <1.9> + { "de_??", "de-1996" }, // # from old MapScriptInfoAndISOCodes + { "el.El-P", "grc" }, // # from old LocaleRefGetPartString + { "en-ascii", "en_001" }, // # from earlier version of tables in this file! + { "en_??", "en_001" }, // # from old MapScriptInfoAndISOCodes + { "eo_??", "eo" }, // # from old MapScriptInfoAndISOCodes + { "es_??", "es_419" }, // # from old MapScriptInfoAndISOCodes + { "es_XL", "es_419" }, // # from earlier version of tables in this file! + { "fr_??", "fr_001" }, // # from old MapScriptInfoAndISOCodes + { "ga-dots", "ga-Latg" }, // # from earlier version of tables in this file! // <1.8> + { "ga-dots_IE", "ga-Latg_IE" }, // # from earlier version of tables in this file! // <1.8> + { "ga.Lg", "ga-Latg" }, // # from old LocaleRefGetPartString // <1.8> + { "ga.Lg_IE", "ga-Latg_IE" }, // # from old LocaleRefGetPartString // <1.8> + { "gd_??", "gd" }, // # from old MapScriptInfoAndISOCodes + { "gv_??", "gv" }, // # from old MapScriptInfoAndISOCodes + { "jv.La", "jv" }, // # logical extension // <1.9> + { "jw.La", "jv" }, // # from old LocaleRefGetPartString + { "kk.Cy", "kk" }, // # from old LocaleRefGetPartString + { "kl.La", "kl" }, // # from old LocaleRefGetPartString + { "kl.La_GL", "kl_GL" }, // # from old LocaleRefGetPartString // <1.9> + { "lp_??", "se" }, // # from old MapScriptInfoAndISOCodes + { "mk_??", "mk_MK" }, // # from old MapScriptInfoAndISOCodes + { "mn.Cy", "mn-Cyrl" }, // # from old LocaleRefGetPartString + { "mn.Mn", "mn-Mong" }, // # from old LocaleRefGetPartString + { "ms.Ar", "ms-Arab" }, // # from old LocaleRefGetPartString + { "ms.La", "ms" }, // # from old LocaleRefGetPartString + { "nl-be", "nl-BE" }, // # from old LocaleRefGetPartString + { "nl-be_BE", "nl_BE" }, // # from old LocaleRefGetPartString +// { "no-bok_NO", "nb_NO" }, // # from old LocaleRefGetPartString - handled by localeStringPrefixToCanonical +// { "no-nyn_NO", "nn_NO" }, // # from old LocaleRefGetPartString - handled by localeStringPrefixToCanonical +// { "nya", "ny" }, // # from old LocaleRefGetPartString - handled by localeStringPrefixToCanonical + { "pa_??", "pa" }, // # from old LocaleRefGetPartString + { "sa.Dv", "sa" }, // # from old LocaleRefGetPartString + { "sl_??", "sl_SI" }, // # from old MapScriptInfoAndISOCodes + { "sr_??", "sr_CS" }, // # from old MapScriptInfoAndISOCodes // <1.18> + { "su.La", "su" }, // # from old LocaleRefGetPartString + { "yi.He", "yi" }, // # from old LocaleRefGetPartString + { "zh-simp", "zh-Hans" }, // # from earlier version of tables in this file! + { "zh-trad", "zh-Hant" }, // # from earlier version of tables in this file! + { "zh.Ha-S", "zh-Hans" }, // # from old LocaleRefGetPartString + { "zh.Ha-S_CN", "zh_CN" }, // # from old LocaleRefGetPartString + { "zh.Ha-T", "zh-Hant" }, // # from old LocaleRefGetPartString + { "zh.Ha-T_TW", "zh_TW" }, // # from old LocaleRefGetPartString +}; +enum { + kNumOldAppleLocaleToCanonical = sizeof(oldAppleLocaleToCanonical)/sizeof(KeyStringToResultString) +}; + +static const KeyStringToResultString localeStringPrefixToCanonical[] = { +// Map 3-letter & obsolete ISO 639 codes, plus obsolete RFC 3066 codes, to 2-letter ISO 639 code. +// (special cases for 'sh' handled separately) +// First column must be all lowercase; must be sorted according to how strcmp compares the strings in the first column. +// +// non-canonical canonical [ comment ] # source/reason for non-canonical string +// prefix prefix +// ------------- --------- + + { "afr", "af" }, // Afrikaans + { "alb", "sq" }, // Albanian + { "amh", "am" }, // Amharic + { "ara", "ar" }, // Arabic + { "arm", "hy" }, // Armenian + { "asm", "as" }, // Assamese + { "aym", "ay" }, // Aymara + { "aze", "az" }, // Azerbaijani + { "baq", "eu" }, // Basque + { "bel", "be" }, // Belarusian + { "ben", "bn" }, // Bengali + { "bih", "bh" }, // Bihari + { "bod", "bo" }, // Tibetan + { "bos", "bs" }, // Bosnian + { "bre", "br" }, // Breton + { "bul", "bg" }, // Bulgarian + { "bur", "my" }, // Burmese + { "cat", "ca" }, // Catalan + { "ces", "cs" }, // Czech + { "che", "ce" }, // Chechen + { "chi", "zh" }, // Chinese + { "cor", "kw" }, // Cornish + { "cos", "co" }, // Corsican + { "cym", "cy" }, // Welsh + { "cze", "cs" }, // Czech + { "dan", "da" }, // Danish + { "deu", "de" }, // German + { "dut", "nl" }, // Dutch + { "dzo", "dz" }, // Dzongkha + { "ell", "el" }, // Greek, Modern (1453-) + { "eng", "en" }, // English + { "epo", "eo" }, // Esperanto + { "est", "et" }, // Estonian + { "eus", "eu" }, // Basque + { "fao", "fo" }, // Faroese + { "fas", "fa" }, // Persian + { "fin", "fi" }, // Finnish + { "fra", "fr" }, // French + { "fre", "fr" }, // French + { "geo", "ka" }, // Georgian + { "ger", "de" }, // German + { "gla", "gd" }, // Gaelic,Scottish + { "gle", "ga" }, // Irish + { "glg", "gl" }, // Gallegan + { "glv", "gv" }, // Manx + { "gre", "el" }, // Greek, Modern (1453-) + { "grn", "gn" }, // Guarani + { "guj", "gu" }, // Gujarati + { "heb", "he" }, // Hebrew + { "hin", "hi" }, // Hindi + { "hrv", "hr" }, // Croatian + { "hun", "hu" }, // Hungarian + { "hye", "hy" }, // Armenian + { "i-hak", "zh-hakka" }, // Hakka # deprecated RFC 3066 + { "i-lux", "lb" }, // Luxembourgish # deprecated RFC 3066 + { "i-navajo", "nv" }, // Navajo # deprecated RFC 3066 + { "ice", "is" }, // Icelandic + { "iku", "iu" }, // Inuktitut + { "ile", "ie" }, // Interlingue + { "in", "id" }, // Indonesian # deprecated 639 code in -> id (1989) + { "ina", "ia" }, // Interlingua + { "ind", "id" }, // Indonesian + { "isl", "is" }, // Icelandic + { "ita", "it" }, // Italian + { "iw", "he" }, // Hebrew # deprecated 639 code iw -> he (1989) + { "jav", "jv" }, // Javanese + { "jaw", "jv" }, // Javanese # deprecated 639 code jaw -> jv (2001) + { "ji", "yi" }, // Yiddish # deprecated 639 code ji -> yi (1989) + { "jpn", "ja" }, // Japanese + { "kal", "kl" }, // Kalaallisut + { "kan", "kn" }, // Kannada + { "kas", "ks" }, // Kashmiri + { "kat", "ka" }, // Georgian + { "kaz", "kk" }, // Kazakh + { "khm", "km" }, // Khmer + { "kin", "rw" }, // Kinyarwanda + { "kir", "ky" }, // Kirghiz + { "kor", "ko" }, // Korean + { "kur", "ku" }, // Kurdish + { "lao", "lo" }, // Lao + { "lat", "la" }, // Latin + { "lav", "lv" }, // Latvian + { "lit", "lt" }, // Lithuanian + { "ltz", "lb" }, // Letzeburgesch + { "mac", "mk" }, // Macedonian + { "mal", "ml" }, // Malayalam + { "mar", "mr" }, // Marathi + { "may", "ms" }, // Malay + { "mkd", "mk" }, // Macedonian + { "mlg", "mg" }, // Malagasy + { "mlt", "mt" }, // Maltese + { "mol", "mo" }, // Moldavian + { "mon", "mn" }, // Mongolian + { "msa", "ms" }, // Malay + { "mya", "my" }, // Burmese + { "nep", "ne" }, // Nepali + { "nld", "nl" }, // Dutch + { "nno", "nn" }, // Norwegian Nynorsk + { "no", "nb" }, // Norwegian generic # ambiguous 639 code no -> nb + { "no-bok", "nb" }, // Norwegian Bokmal # deprecated RFC 3066 tag - used in old LocaleRefGetPartString + { "no-nyn", "nn" }, // Norwegian Nynorsk # deprecated RFC 3066 tag - used in old LocaleRefGetPartString + { "nob", "nb" }, // Norwegian Bokmal + { "nor", "nb" }, // Norwegian generic # ambiguous 639 code nor -> nb + { "nya", "ny" }, // Nyanja/Chewa/Chichewa # 3-letter code used in old LocaleRefGetPartString + { "oci", "oc" }, // Occitan/Provencal + { "ori", "or" }, // Oriya + { "orm", "om" }, // Oromo,Galla + { "pan", "pa" }, // Panjabi + { "per", "fa" }, // Persian + { "pol", "pl" }, // Polish + { "por", "pt" }, // Portuguese + { "pus", "ps" }, // Pushto + { "que", "qu" }, // Quechua + { "roh", "rm" }, // Raeto-Romance + { "ron", "ro" }, // Romanian + { "rum", "ro" }, // Romanian + { "run", "rn" }, // Rundi + { "rus", "ru" }, // Russian + { "san", "sa" }, // Sanskrit + { "scc", "sr" }, // Serbian + { "scr", "hr" }, // Croatian + { "sin", "si" }, // Sinhalese + { "slk", "sk" }, // Slovak + { "slo", "sk" }, // Slovak + { "slv", "sl" }, // Slovenian + { "sme", "se" }, // Sami,Northern + { "snd", "sd" }, // Sindhi + { "som", "so" }, // Somali + { "spa", "es" }, // Spanish + { "sqi", "sq" }, // Albanian + { "srp", "sr" }, // Serbian + { "sun", "su" }, // Sundanese + { "swa", "sw" }, // Swahili + { "swe", "sv" }, // Swedish + { "tam", "ta" }, // Tamil + { "tat", "tt" }, // Tatar + { "tel", "te" }, // Telugu + { "tgk", "tg" }, // Tajik + { "tgl", "tl" }, // Tagalog + { "tha", "th" }, // Thai + { "tib", "bo" }, // Tibetan + { "tir", "ti" }, // Tigrinya + { "ton", "to" }, // Tongan + { "tuk", "tk" }, // Turkmen + { "tur", "tr" }, // Turkish + { "uig", "ug" }, // Uighur + { "ukr", "uk" }, // Ukrainian + { "urd", "ur" }, // Urdu + { "uzb", "uz" }, // Uzbek + { "vie", "vi" }, // Vietnamese + { "wel", "cy" }, // Welsh + { "yid", "yi" }, // Yiddish + { "zho", "zh" }, // Chinese +}; +enum { + kNumLocaleStringPrefixToCanonical = sizeof(localeStringPrefixToCanonical)/sizeof(KeyStringToResultString) +}; + + +static const SpecialCaseUpdates specialCases[] = { +// Data for special cases +// a) The 3166 code CS was used for Czechoslovakia until 1993, when that country split and the code was +// replaced by CZ and SK. Then in 2003-07, the code YU (formerly designating all of Yugoslavia, then after +// the 1990s breakup just designating what is now Serbia and Montenegro) was changed to CS! However, ICU +// and RFC 3066bis will continue to use YU for this. So now CS is ambiguous. We guess as follows: If we +// see CS but a language of cs or sk, we change CS to CZ or SK. Otherwise, we change CS to YU. +// b) The 639 code sh for Serbo-Croatian was also replaced in the 1990s by separate codes hr and sr, and +// deprecated in 2000. We guess which one to map it to as follows: If there is a region tag of HR we use +// hr; if there is a region tag of (now) YU we use sr; else we do not change it (not enough info). +// c) There are other codes that have been updated without these issues (eg. TP to TL), plus among the +// "exceptionally reserved" codes some are just alternates for standard codes (eg. UK for GB). + { NULL, "-UK", "GB", NULL, NULL }, // always change UK to GB (UK is "exceptionally reserved" to mean GB) + { NULL, "-TP", "TL", NULL, NULL }, // always change TP to TL (East Timor, code changed 2002-05) + { "cs", "-CS", "CZ", NULL, NULL }, // if language is cs, change CS (pre-1993 Czechoslovakia) to CZ (Czech Republic) + { "sk", "-CS", "SK", NULL, NULL }, // if language is sk, change CS (pre-1993 Czechoslovakia) to SK (Slovakia) + { NULL, "-YU", "CS", NULL, NULL }, // then always change YU to CS (map old Yugoslavia code to new 2003-07 ISO code + // for Serbia & Montenegro per RFC3066bis & ICU) // <1.18> + // Note: do this after fixing CS for cs/sk as above. + { "sh", "-HR", "hr", "-CS", "sr" }, // if language is old 'sh' (SerboCroatian), change it to 'hr' (Croatian) if we find + // HR (Croatia) or to 'sr' (Serbian) if we find CS (Serbia & Montenegro, Yugoslavia). // <1.18> + // Note: Do this after changing YU to CS as above. + { NULL, NULL, NULL, NULL, NULL } // terminator +}; + + +static const KeyStringToResultString localeStringRegionToDefaults[] = { +// For some region-code suffixes, there are default substrings to strip off for canonical string. +// Must be sorted according to how strcmp compares the strings in the first column +// +// region default writing +// suffix system tags, strip comment +// -------- ------------- --------- + { "_CN", "-Hans" }, // mainland China, default is simplified + { "_HK", "-Hant" }, // Hong Kong, default is traditional + { "_MO", "-Hant" }, // Macao, default is traditional + { "_SG", "-Hans" }, // Singapore, default is simplified + { "_TW", "-Hant" }, // Taiwan, default is traditional +}; +enum { + kNumLocaleStringRegionToDefaults = sizeof(localeStringRegionToDefaults)/sizeof(KeyStringToResultString) +}; + +static const KeyStringToResultString localeStringPrefixToDefaults[] = { +// For some initial portions of language tag, there are default substrings to strip off for canonical string. +// Must be sorted according to how strcmp compares the strings in the first column +// +// language default writing +// tag prefix system tags, strip comment +// -------- ------------- --------- + { "ab-", "-Cyrl" }, // Abkhazian + { "af-", "-Latn" }, // Afrikaans + { "am-", "-Ethi" }, // Amharic + { "ar-", "-Arab" }, // Arabic + { "as-", "-Beng" }, // Assamese + { "ay-", "-Latn" }, // Aymara + { "be-", "-Cyrl" }, // Belarusian + { "bg-", "-Cyrl" }, // Bulgarian + { "bn-", "-Beng" }, // Bengali + { "bo-", "-Tibt" }, // Tibetan (? not Suppress-Script) + { "br-", "-Latn" }, // Breton (? not Suppress-Script) + { "bs-", "-Latn" }, // Bosnian + { "ca-", "-Latn" }, // Catalan + { "cs-", "-Latn" }, // Czech + { "cy-", "-Latn" }, // Welsh + { "da-", "-Latn" }, // Danish + { "de-", "-Latn -1901" }, // German, traditional orthography + { "dv-", "-Thaa" }, // Divehi/Maldivian + { "dz-", "-Tibt" }, // Dzongkha + { "el-", "-Grek" }, // Greek (modern, monotonic) + { "en-", "-Latn" }, // English + { "eo-", "-Latn" }, // Esperanto + { "es-", "-Latn" }, // Spanish + { "et-", "-Latn" }, // Estonian + { "eu-", "-Latn" }, // Basque + { "fa-", "-Arab" }, // Farsi + { "fi-", "-Latn" }, // Finnish + { "fo-", "-Latn" }, // Faroese + { "fr-", "-Latn" }, // French + { "ga-", "-Latn" }, // Irish + { "gd-", "-Latn" }, // Scottish Gaelic (? not Suppress-Script) + { "gl-", "-Latn" }, // Galician + { "gn-", "-Latn" }, // Guarani + { "gu-", "-Gujr" }, // Gujarati + { "gv-", "-Latn" }, // Manx + { "haw-", "-Latn" }, // Hawaiian (? not Suppress-Script) + { "he-", "-Hebr" }, // Hebrew + { "hi-", "-Deva" }, // Hindi + { "hr-", "-Latn" }, // Croatian + { "hu-", "-Latn" }, // Hungarian + { "hy-", "-Armn" }, // Armenian + { "id-", "-Latn" }, // Indonesian + { "is-", "-Latn" }, // Icelandic + { "it-", "-Latn" }, // Italian + { "ja-", "-Jpan" }, // Japanese + { "ka-", "-Geor" }, // Georgian + { "kk-", "-Cyrl" }, // Kazakh + { "kl-", "-Latn" }, // Kalaallisut/Greenlandic + { "km-", "-Khmr" }, // Central Khmer + { "kn-", "-Knda" }, // Kannada + { "ko-", "-Hang" }, // Korean (? not Suppress-Script) + { "kok-", "-Deva" }, // Konkani + { "la-", "-Latn" }, // Latin + { "lb-", "-Latn" }, // Luxembourgish + { "lo-", "-Laoo" }, // Lao + { "lt-", "-Latn" }, // Lithuanian + { "lv-", "-Latn" }, // Latvian + { "mg-", "-Latn" }, // Malagasy + { "mk-", "-Cyrl" }, // Macedonian + { "ml-", "-Mlym" }, // Malayalam + { "mo-", "-Latn" }, // Moldavian + { "mr-", "-Deva" }, // Marathi + { "ms-", "-Latn" }, // Malay + { "mt-", "-Latn" }, // Maltese + { "my-", "-Mymr" }, // Burmese/Myanmar + { "nb-", "-Latn" }, // Norwegian Bokmal + { "ne-", "-Deva" }, // Nepali + { "nl-", "-Latn" }, // Dutch + { "nn-", "-Latn" }, // Norwegian Nynorsk + { "ny-", "-Latn" }, // Chichewa/Nyanja + { "om-", "-Latn" }, // Oromo + { "or-", "-Orya" }, // Oriya + { "pa-", "-Guru" }, // Punjabi + { "pl-", "-Latn" }, // Polish + { "ps-", "-Arab" }, // Pushto + { "pt-", "-Latn" }, // Portuguese + { "qu-", "-Latn" }, // Quechua + { "rn-", "-Latn" }, // Rundi + { "ro-", "-Latn" }, // Romanian + { "ru-", "-Cyrl" }, // Russian + { "rw-", "-Latn" }, // Kinyarwanda + { "sa-", "-Deva" }, // Sanskrit (? not Suppress-Script) + { "se-", "-Latn" }, // Sami (? not Suppress-Script) + { "si-", "-Sinh" }, // Sinhala + { "sk-", "-Latn" }, // Slovak + { "sl-", "-Latn" }, // Slovenian + { "so-", "-Latn" }, // Somali + { "sq-", "-Latn" }, // Albanian + { "sv-", "-Latn" }, // Swedish + { "sw-", "-Latn" }, // Swahili + { "ta-", "-Taml" }, // Tamil + { "te-", "-Telu" }, // Telugu + { "th-", "-Thai" }, // Thai + { "ti-", "-Ethi" }, // Tigrinya + { "tl-", "-Latn" }, // Tagalog + { "tn-", "-Latn" }, // Tswana + { "to-", "-Latn" }, // Tonga of Tonga Islands + { "tr-", "-Latn" }, // Turkish + { "uk-", "-Cyrl" }, // Ukrainian + { "ur-", "-Arab" }, // Urdu + { "vi-", "-Latn" }, // Vietnamese + { "wo-", "-Latn" }, // Wolof + { "xh-", "-Latn" }, // Xhosa + { "yi-", "-Hebr" }, // Yiddish + { "zh-", "-Hani" }, // Chinese (? not Suppress-Script) + { "zu-", "-Latn" }, // Zulu +}; +enum { + kNumLocaleStringPrefixToDefaults = sizeof(localeStringPrefixToDefaults)/sizeof(KeyStringToResultString) +}; + +static const KeyStringToResultString appleLocaleToLanguageString[] = { +// Map locale strings that Apple uses as language IDs to real language strings. +// Must be sorted according to how strcmp compares the strings in the first column. +// Note: Now we remove all transforms of the form ll_RR -> ll-RR, they are now +// handled in the code. <1.19> +// +// locale lang [ comment ] +// string string +// ------- ------- + { "en_US_POSIX", "en-US-POSIX" }, // POSIX locale, need as language string // <1.17> [3840752] + { "zh_CN", "zh-Hans" }, // mainland China => simplified + { "zh_HK", "zh-Hant" }, // Hong Kong => traditional, not currently used + { "zh_MO", "zh-Hant" }, // Macao => traditional, not currently used + { "zh_SG", "zh-Hans" }, // Singapore => simplified, not currently used + { "zh_TW", "zh-Hant" }, // Taiwan => traditional +}; +enum { + kNumAppleLocaleToLanguageString = sizeof(appleLocaleToLanguageString)/sizeof(KeyStringToResultString) +}; + +static const KeyStringToResultString appleLocaleToLanguageStringForCFBundle[] = { +// Map locale strings that Apple uses as language IDs to real language strings. +// Must be sorted according to how strcmp compares the strings in the first column. +// +// locale lang [ comment ] +// string string +// ------- ------- + { "de_AT", "de-AT" }, // Austrian German + { "de_CH", "de-CH" }, // Swiss German +// { "de_DE", "de-DE" }, // German for Germany (default), not currently used + { "en_AU", "en-AU" }, // Australian English + { "en_CA", "en-CA" }, // Canadian English + { "en_GB", "en-GB" }, // British English +// { "en_IE", "en-IE" }, // Irish English, not currently used + { "en_US", "en-US" }, // U.S. English + { "en_US_POSIX", "en-US-POSIX" }, // POSIX locale, need as language string // <1.17> [3840752] +// { "fr_BE", "fr-BE" }, // Belgian French, not currently used + { "fr_CA", "fr-CA" }, // Canadian French + { "fr_CH", "fr-CH" }, // Swiss French +// { "fr_FR", "fr-FR" }, // French for France (default), not currently used + { "nl_BE", "nl-BE" }, // Flemish = Vlaams, Dutch for Belgium +// { "nl_NL", "nl-NL" }, // Dutch for Netherlands (default), not currently used + { "pt_BR", "pt-BR" }, // Brazilian Portuguese + { "pt_PT", "pt-PT" }, // Portuguese for Portugal + { "zh_CN", "zh-Hans" }, // mainland China => simplified + { "zh_HK", "zh-Hant" }, // Hong Kong => traditional, not currently used + { "zh_MO", "zh-Hant" }, // Macao => traditional, not currently used + { "zh_SG", "zh-Hans" }, // Singapore => simplified, not currently used + { "zh_TW", "zh-Hant" }, // Taiwan => traditional +}; +enum { + kNumAppleLocaleToLanguageStringForCFBundle = sizeof(appleLocaleToLanguageStringForCFBundle)/sizeof(KeyStringToResultString) +}; + + +struct LocaleToLegacyCodes { + const char * locale; // reduced to language plus one other component (script, region, variant), separators normalized to'_' + RegionCode regCode; + LangCode langCode; + CFStringEncoding encoding; +}; +typedef struct LocaleToLegacyCodes LocaleToLegacyCodes; + +static const LocaleToLegacyCodes localeToLegacyCodes[] = { + // locale RegionCode LangCode CFStringEncoding + { "af"/*ZA*/, 102/*verAfrikaans*/, 141/*langAfrikaans*/, 0/*Roman*/ }, // Latn + { "am", -1, 85/*langAmharic*/, 28/*Ethiopic*/ }, // Ethi + { "ar", 16/*verArabic*/, 12/*langArabic*/, 4/*Arabic*/ }, // Arab; + { "as", -1, 68/*langAssamese*/, 13/*Bengali*/ }, // Beng; + { "ay", -1, 134/*langAymara*/, 0/*Roman*/ }, // Latn; + { "az", -1, 49/*langAzerbaijani*/, 7/*Cyrillic*/ }, // assume "az" defaults to -Cyrl + { "az_Arab", -1, 50/*langAzerbaijanAr*/, 4/*Arabic*/ }, // Arab; + { "az_Cyrl", -1, 49/*langAzerbaijani*/, 7/*Cyrillic*/ }, // Cyrl; + { "az_Latn", -1, 150/*langAzerbaijanRoman*/, 0/*Roman*/ }, // Latn; + { "be"/*BY*/, 61/*verBelarus*/, 46/*langBelorussian*/, 7/*Cyrillic*/ }, // Cyrl; + { "bg"/*BG*/, 72/*verBulgaria*/, 44/*langBulgarian*/, 7/*Cyrillic*/ }, // Cyrl; + { "bn", 60/*verBengali*/, 67/*langBengali*/, 13/*Bengali*/ }, // Beng; + { "bo", 105/*verTibetan*/, 63/*langTibetan*/, 26/*Tibetan*/ }, // Tibt; + { "br", 77/*verBreton*/, 142/*langBreton*/, 39/*Celtic*/ }, // Latn; + { "ca"/*ES*/, 73/*verCatalonia*/, 130/*langCatalan*/, 0/*Roman*/ }, // Latn; + { "cs"/*CZ*/, 56/*verCzech*/, 38/*langCzech*/, 29/*CentralEurRoman*/ }, // Latn; + { "cy", 79/*verWelsh*/, 128/*langWelsh*/, 39/*Celtic*/ }, // Latn; + { "da"/*DK*/, 9/*verDenmark*/, 7/*langDanish*/, 0/*Roman*/ }, // Latn; + { "de", 3/*verGermany*/, 2/*langGerman*/, 0/*Roman*/ }, // assume "de" defaults to verGermany + { "de_1996", 70/*verGermanReformed*/, 2/*langGerman*/, 0/*Roman*/ }, + { "de_AT", 92/*verAustria*/, 2/*langGerman*/, 0/*Roman*/ }, + { "de_CH", 19/*verGrSwiss*/, 2/*langGerman*/, 0/*Roman*/ }, + { "de_DE", 3/*verGermany*/, 2/*langGerman*/, 0/*Roman*/ }, + { "dz"/*BT*/, 83/*verBhutan*/, 137/*langDzongkha*/, 26/*Tibetan*/ }, // Tibt; + { "el", 20/*verGreece*/, 14/*langGreek*/, 6/*Greek*/ }, // assume "el" defaults to verGreece + { "el_CY", 23/*verCyprus*/, 14/*langGreek*/, 6/*Greek*/ }, + { "el_GR", 20/*verGreece*/, 14/*langGreek*/, 6/*Greek*/ }, // modern monotonic + { "en", 0/*verUS*/, 0/*langEnglish*/, 0/*Roman*/ }, // "en" defaults to verUS (per Chris Hansten) + { "en_001", 37/*verInternational*/, 0/*langEnglish*/, 0/*Roman*/ }, + { "en_AU", 15/*verAustralia*/, 0/*langEnglish*/, 0/*Roman*/ }, + { "en_CA", 82/*verEngCanada*/, 0/*langEnglish*/, 0/*Roman*/ }, + { "en_GB", 2/*verBritain*/, 0/*langEnglish*/, 0/*Roman*/ }, + { "en_IE", 108/*verIrelandEnglish*/, 0/*langEnglish*/, 0/*Roman*/ }, + { "en_SG", 100/*verSingapore*/, 0/*langEnglish*/, 0/*Roman*/ }, + { "en_US", 0/*verUS*/, 0/*langEnglish*/, 0/*Roman*/ }, + { "eo", 103/*verEsperanto*/, 94/*langEsperanto*/, 0/*Roman*/ }, // Latn; + { "es", 8/*verSpain*/, 6/*langSpanish*/, 0/*Roman*/ }, // "es" defaults to verSpain (per Chris Hansten) + { "es_419", 86/*verSpLatinAmerica*/, 6/*langSpanish*/, 0/*Roman*/ }, // new BCP 47 tag + { "es_ES", 8/*verSpain*/, 6/*langSpanish*/, 0/*Roman*/ }, + { "es_MX", 86/*verSpLatinAmerica*/, 6/*langSpanish*/, 0/*Roman*/ }, + { "es_US", 86/*verSpLatinAmerica*/, 6/*langSpanish*/, 0/*Roman*/ }, + { "et"/*EE*/, 44/*verEstonia*/, 27/*langEstonian*/, 29/*CentralEurRoman*/ }, + { "eu", -1, 129/*langBasque*/, 0/*Roman*/ }, // Latn; + { "fa"/*IR*/, 48/*verIran*/, 31/*langFarsi/Persian*/, 0x8C/*Farsi*/ }, // Arab; + { "fi"/*FI*/, 17/*verFinland*/, 13/*langFinnish*/, 0/*Roman*/ }, + { "fo"/*FO*/, 47/*verFaroeIsl*/, 30/*langFaroese*/, 37/*Icelandic*/ }, + { "fr", 1/*verFrance*/, 1/*langFrench*/, 0/*Roman*/ }, // "fr" defaults to verFrance (per Chris Hansten) + { "fr_001", 91/*verFrenchUniversal*/, 1/*langFrench*/, 0/*Roman*/ }, + { "fr_BE", 98/*verFrBelgium*/, 1/*langFrench*/, 0/*Roman*/ }, + { "fr_CA", 11/*verFrCanada*/, 1/*langFrench*/, 0/*Roman*/ }, + { "fr_CH", 18/*verFrSwiss*/, 1/*langFrench*/, 0/*Roman*/ }, + { "fr_FR", 1/*verFrance*/, 1/*langFrench*/, 0/*Roman*/ }, + { "ga"/*IE*/, 50/*verIreland*/, 35/*langIrishGaelic*/, 0/*Roman*/ }, // no dots (h after) + { "ga_Latg"/*IE*/, 81/*verIrishGaelicScrip*/, 146/*langIrishGaelicScript*/, 40/*Gaelic*/ }, // using dots + { "gd", 75/*verScottishGaelic*/, 144/*langScottishGaelic*/, 39/*Celtic*/ }, + { "gl", -1, 140/*langGalician*/, 0/*Roman*/ }, // Latn; + { "gn", -1, 133/*langGuarani*/, 0/*Roman*/ }, // Latn; + { "grc", 40/*verGreekAncient*/, 148/*langGreekAncient*/, 6/*Greek*/ }, // polytonic (MacGreek doesn't actually support it) + { "gu"/*IN*/, 94/*verGujarati*/, 69/*langGujarati*/, 11/*Gujarati*/ }, // Gujr; + { "gv", 76/*verManxGaelic*/, 145/*langManxGaelic*/, 39/*Celtic*/ }, // Latn; + { "he"/*IL*/, 13/*verIsrael*/, 10/*langHebrew*/, 5/*Hebrew*/ }, // Hebr; + { "hi"/*IN*/, 33/*verIndiaHindi*/, 21/*langHindi*/, 9/*Devanagari*/ }, // Deva; + { "hr"/*HR*/, 68/*verCroatia*/, 18/*langCroatian*/, 36/*Croatian*/ }, + { "hu"/*HU*/, 43/*verHungary*/, 26/*langHungarian*/, 29/*CentralEurRoman*/ }, + { "hy"/*AM*/, 84/*verArmenian*/, 51/*langArmenian*/, 24/*Armenian*/ }, // Armn; + { "id", -1, 81/*langIndonesian*/, 0/*Roman*/ }, // Latn; + { "is"/*IS*/, 21/*verIceland*/, 15/*langIcelandic*/, 37/*Icelandic*/ }, + { "it", 4/*verItaly*/, 3/*langItalian*/, 0/*Roman*/ }, // "it" defaults to verItaly + { "it_CH", 36/*verItalianSwiss*/, 3/*langItalian*/, 0/*Roman*/ }, + { "it_IT", 4/*verItaly*/, 3/*langItalian*/, 0/*Roman*/ }, + { "iu"/*CA*/, 78/*verNunavut*/, 143/*langInuktitut*/, 0xEC/*Inuit*/ }, // Cans; + { "ja"/*JP*/, 14/*verJapan*/, 11/*langJapanese*/, 1/*Japanese*/ }, // Jpan; + { "jv", -1, 138/*langJavaneseRom*/, 0/*Roman*/ }, // Latn; + { "ka"/*GE*/, 85/*verGeorgian*/, 52/*langGeorgian*/, 23/*Georgian*/ }, // Geor; + { "kk", -1, 48/*langKazakh*/, 7/*Cyrillic*/ }, // "kk" defaults to -Cyrl; also have -Latn, -Arab + { "kl", 107/*verGreenland*/, 149/*langGreenlandic*/, 0/*Roman*/ }, // Latn; + { "km", -1, 78/*langKhmer*/, 20/*Khmer*/ }, // Khmr; + { "kn", -1, 73/*langKannada*/, 16/*Kannada*/ }, // Knda; + { "ko"/*KR*/, 51/*verKorea*/, 23/*langKorean*/, 3/*Korean*/ }, // Hang; + { "ks", -1, 61/*langKashmiri*/, 4/*Arabic*/ }, // Arab; + { "ku", -1, 60/*langKurdish*/, 4/*Arabic*/ }, // Arab; + { "ky", -1, 54/*langKirghiz*/, 7/*Cyrillic*/ }, // Cyrl; also -Latn, -Arab + { "la", -1, 131/*langLatin*/, 0/*Roman*/ }, // Latn; + { "lo", -1, 79/*langLao*/, 22/*Laotian*/ }, // Laoo; + { "lt"/*LT*/, 41/*verLithuania*/, 24/*langLithuanian*/, 29/*CentralEurRoman*/ }, + { "lv"/*LV*/, 45/*verLatvia*/, 28/*langLatvian*/, 29/*CentralEurRoman*/ }, + { "mg", -1, 93/*langMalagasy*/, 0/*Roman*/ }, // Latn; + { "mk"/*MK*/, 67/*verMacedonian*/, 43/*langMacedonian*/, 7/*Cyrillic*/ }, // Cyrl; + { "ml", -1, 72/*langMalayalam*/, 17/*Malayalam*/ }, // Mlym; + { "mn", -1, 57/*langMongolian*/, 27/*Mongolian*/ }, // "mn" defaults to -Mong + { "mn_Cyrl", -1, 58/*langMongolianCyr*/, 7/*Cyrillic*/ }, // Cyrl; + { "mn_Mong", -1, 57/*langMongolian*/, 27/*Mongolian*/ }, // Mong; + { "mo", -1, 53/*langMoldavian*/, 7/*Cyrillic*/ }, // Cyrl; + { "mr"/*IN*/, 104/*verMarathi*/, 66/*langMarathi*/, 9/*Devanagari*/ }, // Deva; + { "ms", -1, 83/*langMalayRoman*/, 0/*Roman*/ }, // "ms" defaults to -Latn; + { "ms_Arab", -1, 84/*langMalayArabic*/, 4/*Arabic*/ }, // Arab; + { "mt"/*MT*/, 22/*verMalta*/, 16/*langMaltese*/, 0/*Roman*/ }, // Latn; + { "mul", 74/*verMultilingual*/, -1, 0 }, + { "my", -1, 77/*langBurmese*/, 19/*Burmese*/ }, // Mymr; + { "nb"/*NO*/, 12/*verNorway*/, 9/*langNorwegian*/, 0/*Roman*/ }, + { "ne"/*NP*/, 106/*verNepal*/, 64/*langNepali*/, 9/*Devanagari*/ }, // Deva; + { "nl", 5/*verNetherlands*/, 4/*langDutch*/, 0/*Roman*/ }, // "nl" defaults to verNetherlands + { "nl_BE", 6/*verFlemish*/, 34/*langFlemish*/, 0/*Roman*/ }, + { "nl_NL", 5/*verNetherlands*/, 4/*langDutch*/, 0/*Roman*/ }, + { "nn"/*NO*/, 101/*verNynorsk*/, 151/*langNynorsk*/, 0/*Roman*/ }, + { "ny", -1, 92/*langNyanja/Chewa*/, 0/*Roman*/ }, // Latn; + { "om", -1, 87/*langOromo*/, 28/*Ethiopic*/ }, // Ethi; + { "or", -1, 71/*langOriya*/, 12/*Oriya*/ }, // Orya; + { "pa", 95/*verPunjabi*/, 70/*langPunjabi*/, 10/*Gurmukhi*/ }, // Guru; + { "pl"/*PL*/, 42/*verPoland*/, 25/*langPolish*/, 29/*CentralEurRoman*/ }, + { "ps", -1, 59/*langPashto*/, 0x8C/*Farsi*/ }, // Arab; + { "pt", 71/*verBrazil*/, 8/*langPortuguese*/, 0/*Roman*/ }, // "pt" defaults to verBrazil (per Chris Hansten) + { "pt_BR", 71/*verBrazil*/, 8/*langPortuguese*/, 0/*Roman*/ }, + { "pt_PT", 10/*verPortugal*/, 8/*langPortuguese*/, 0/*Roman*/ }, + { "qu", -1, 132/*langQuechua*/, 0/*Roman*/ }, // Latn; + { "rn", -1, 91/*langRundi*/, 0/*Roman*/ }, // Latn; + { "ro"/*RO*/, 39/*verRomania*/, 37/*langRomanian*/, 38/*Romanian*/ }, + { "ru"/*RU*/, 49/*verRussia*/, 32/*langRussian*/, 7/*Cyrillic*/ }, // Cyrl; + { "rw", -1, 90/*langKinyarwanda*/, 0/*Roman*/ }, // Latn; + { "sa", -1, 65/*langSanskrit*/, 9/*Devanagari*/ }, // Deva; + { "sd", -1, 62/*langSindhi*/, 0x8C/*Farsi*/ }, // Arab; + { "se", 46/*verSami*/, 29/*langSami*/, 0/*Roman*/ }, + { "si", -1, 76/*langSinhalese*/, 18/*Sinhalese*/ }, // Sinh; + { "sk"/*SK*/, 57/*verSlovak*/, 39/*langSlovak*/, 29/*CentralEurRoman*/ }, + { "sl"/*SI*/, 66/*verSlovenian*/, 40/*langSlovenian*/, 36/*Croatian*/ }, + { "so", -1, 88/*langSomali*/, 0/*Roman*/ }, // Latn; + { "sq", -1, 36/*langAlbanian*/, 0/*Roman*/ }, + { "sr"/*CS,RS*/, 65/*verSerbian*/, 42/*langSerbian*/, 7/*Cyrillic*/ }, // Cyrl; + { "su", -1, 139/*langSundaneseRom*/, 0/*Roman*/ }, // Latn; + { "sv"/*SE*/, 7/*verSweden*/, 5/*langSwedish*/, 0/*Roman*/ }, + { "sw", -1, 89/*langSwahili*/, 0/*Roman*/ }, // Latn; + { "ta", -1, 74/*langTamil*/, 14/*Tamil*/ }, // Taml; + { "te", -1, 75/*langTelugu*/, 15/*Telugu*/ }, // Telu + { "tg", -1, 55/*langTajiki*/, 7/*Cyrillic*/ }, // "tg" defaults to "Cyrl" + { "tg_Cyrl", -1, 55/*langTajiki*/, 7/*Cyrillic*/ }, // Cyrl; also -Latn, -Arab + { "th"/*TH*/, 54/*verThailand*/, 22/*langThai*/, 21/*Thai*/ }, // Thai; + { "ti", -1, 86/*langTigrinya*/, 28/*Ethiopic*/ }, // Ethi; + { "tk", -1, 56/*langTurkmen*/, 7/*Cyrillic*/ }, // "tk" defaults to Cyrl + { "tk_Cyrl", -1, 56/*langTurkmen*/, 7/*Cyrillic*/ }, // Cyrl; also -Latn, -Arab + { "tl", -1, 82/*langTagalog*/, 0/*Roman*/ }, // Latn; + { "to"/*TO*/, 88/*verTonga*/, 147/*langTongan*/, 0/*Roman*/ }, // Latn; + { "tr"/*TR*/, 24/*verTurkey*/, 17/*langTurkish*/, 35/*Turkish*/ }, // Latn; + { "tt", -1, 135/*langTatar*/, 7/*Cyrillic*/ }, // Cyrl; + { "tt_Cyrl", -1, 135/*langTatar*/, 7/*Cyrillic*/ }, // Cyrl; + { "ug", -1, 136/*langUighur*/, 4/*Arabic*/ }, // Arab; + { "uk"/*UA*/, 62/*verUkraine*/, 45/*langUkrainian*/, 7/*Cyrillic*/ }, // Cyrl; + { "und", 55/*verScriptGeneric*/, -1, 0 }, + { "ur", 34/*verPakistanUrdu*/, 20/*langUrdu*/, 0x8C/*Farsi*/ }, // "ur" defaults to verPakistanUrdu + { "ur_IN", 96/*verIndiaUrdu*/, 20/*langUrdu*/, 0x8C/*Farsi*/ }, // Arab + { "ur_PK", 34/*verPakistanUrdu*/, 20/*langUrdu*/, 0x8C/*Farsi*/ }, // Arab + { "uz"/*UZ*/, 99/*verUzbek*/, 47/*langUzbek*/, 7/*Cyrillic*/ }, // Cyrl; also -Latn, -Arab + { "uz_Cyrl", 99/*verUzbek*/, 47/*langUzbek*/, 7/*Cyrillic*/ }, + { "vi"/*VN*/, 97/*verVietnam*/, 80/*langVietnamese*/, 30/*Vietnamese*/ }, // Latn + { "yi", -1, 41/*langYiddish*/, 5/*Hebrew*/ }, // Hebr; + { "zh", 52/*verChina*/, 33/*langSimpChinese*/, 25/*ChineseSimp*/ }, // "zh" defaults to verChina, langSimpChinese + { "zh_CN", 52/*verChina*/, 33/*langSimpChinese*/, 25/*ChineseSimp*/ }, + { "zh_HK", 53/*verTaiwan*/, 19/*langTradChinese*/, 2/*ChineseTrad*/ }, + { "zh_Hans", 52/*verChina*/, 33/*langSimpChinese*/, 25/*ChineseSimp*/ }, + { "zh_Hant", 53/*verTaiwan*/, 19/*langTradChinese*/, 2/*ChineseTrad*/ }, + { "zh_MO", 53/*verTaiwan*/, 19/*langTradChinese*/, 2/*ChineseTrad*/ }, + { "zh_SG", 52/*verChina*/, 33/*langSimpChinese*/, 25/*ChineseSimp*/ }, + { "zh_TW", 53/*verTaiwan*/, 19/*langTradChinese*/, 2/*ChineseTrad*/ }, +}; +enum { + kNumLocaleToLegacyCodes = sizeof(localeToLegacyCodes)/sizeof(localeToLegacyCodes[0]) +}; + +/* + For reference here is a list of ICU locales with variants and how some + of them are canonicalized with the ICU function uloc_canonicalize: + + ICU 3.0 has: + en_US_POSIX x no change + hy_AM_REVISED x no change + ja_JP_TRADITIONAL -> ja_JP@calendar=japanese + th_TH_TRADITIONAL -> th_TH@calendar=buddhist + + ICU 2.8 also had the following (now obsolete): + ca_ES_PREEURO + de__PHONEBOOK -> de@collation=phonebook + de_AT_PREEURO + de_DE_PREEURO + de_LU_PREEURO + el_GR_PREEURO + en_BE_PREEURO + en_GB_EURO -> en_GB@currency=EUR + en_IE_PREEURO -> en_IE@currency=IEP + es__TRADITIONAL -> es@collation=traditional + es_ES_PREEURO + eu_ES_PREEURO + fi_FI_PREEURO + fr_BE_PREEURO + fr_FR_PREEURO -> fr_FR@currency=FRF + fr_LU_PREEURO + ga_IE_PREEURO + gl_ES_PREEURO + hi__DIRECT -> hi@collation=direct + it_IT_PREEURO + nl_BE_PREEURO + nl_NL_PREEURO + pt_PT_PREEURO + zh__PINYIN -> zh@collation=pinyin + zh_TW_STROKE -> zh_TW@collation=stroke + +*/ + +// _CompareTestEntryToTableEntryKey +// (Local function for CFLocaleCreateCanonicalLocaleIdentifierFromString) +// comparison function for bsearch +static int _CompareTestEntryToTableEntryKey(const void *testEntryPtr, const void *tableEntryKeyPtr) { + return strcmp( ((const KeyStringToResultString *)testEntryPtr)->key, ((const KeyStringToResultString *)tableEntryKeyPtr)->key ); +} + +// _CompareTestEntryPrefixToTableEntryKey +// (Local function for CFLocaleCreateCanonicalLocaleIdentifierFromString) +// Comparison function for bsearch. Assumes prefix IS terminated with '-' or '_'. +// Do the following instead of strlen & strncmp so we don't walk tableEntry key twice. +static int _CompareTestEntryPrefixToTableEntryKey(const void *testEntryPtr, const void *tableEntryKeyPtr) { + const char * testPtr = ((const KeyStringToResultString *)testEntryPtr)->key; + const char * tablePtr = ((const KeyStringToResultString *)tableEntryKeyPtr)->key; + + while ( *testPtr == *tablePtr && *tablePtr != 0 ) { + testPtr++; tablePtr++; + } + if ( *tablePtr != 0 ) { + // strings are different, and the string in the table has not run out; + // i.e. the table entry is not a prefix of the text string. + return ( *testPtr < *tablePtr )? -1: 1; + } + return 0; +} + +// _CompareLowerTestEntryPrefixToTableEntryKey +// (Local function for CFLocaleCreateCanonicalLocaleIdentifierFromString) +// Comparison function for bsearch. Assumes prefix NOT terminated with '-' or '_'. +// Lowercases the test string before comparison (the table should already have lowercased entries). +static int _CompareLowerTestEntryPrefixToTableEntryKey(const void *testEntryPtr, const void *tableEntryKeyPtr) { + const char * testPtr = ((const KeyStringToResultString *)testEntryPtr)->key; + const char * tablePtr = ((const KeyStringToResultString *)tableEntryKeyPtr)->key; + char lowerTestChar; + + while ( (lowerTestChar = tolower(*testPtr)) == *tablePtr && *tablePtr != 0 && lowerTestChar != '_' ) { // <1.9> + testPtr++; tablePtr++; + } + if ( *tablePtr != 0 ) { + // strings are different, and the string in the table has not run out; + // i.e. the table entry is not a prefix of the text string. + if (lowerTestChar == '_') // <1.9> + return -1; // <1.9> + return ( lowerTestChar < *tablePtr )? -1: 1; + } + // The string in the table has run out. If the test string char is not alnum, + // then the string matches, else the test string sorts after. + return ( !isalnum(lowerTestChar) )? 0: 1; +} + +// _DeleteCharsAtPointer +// (Local function for CFLocaleCreateCanonicalLocaleIdentifierFromString) +// remove _length_ characters from the beginning of the string indicated by _stringPtr_ +// (we know that the string has at least _length_ characters in it) +static void _DeleteCharsAtPointer(char *stringPtr, int length) { + do { + *stringPtr = stringPtr[length]; + } while (*stringPtr++ != 0); +} + +// _CopyReplacementAtPointer +// (Local function for CFLocaleCreateCanonicalLocaleIdentifierFromString) +// Copy replacement string (*excluding* terminating NULL byte) to the place indicated by stringPtr +static void _CopyReplacementAtPointer(char *stringPtr, const char *replacementPtr) { + while (*replacementPtr != 0) { + *stringPtr++ = *replacementPtr++; + } +} + +// _CheckForTag +// (Local function for CFLocaleCreateCanonicalLocaleIdentifierFromString) +static Boolean _CheckForTag(const char *localeStringPtr, const char *tagPtr, int tagLen) { + return ( strncmp(localeStringPtr, tagPtr, tagLen) == 0 && !isalnum(localeStringPtr[tagLen]) ); +} + +// _ReplacePrefix +// Move this code from _UpdateFullLocaleString into separate function // <1.10> +static void _ReplacePrefix(char locString[], int locStringMaxLen, int oldPrefixLen, const char *newPrefix) { + int newPrefixLen = (int)strlen(newPrefix); + int lengthDelta = newPrefixLen - oldPrefixLen; + + if (lengthDelta < 0) { + // replacement is shorter, delete chars by shifting tail of string + _DeleteCharsAtPointer(locString + newPrefixLen, -lengthDelta); + } else if (lengthDelta > 0) { + // replacement is longer... + int stringLen = (int)strlen(locString); + + if (stringLen + lengthDelta < locStringMaxLen) { + // make room by shifting tail of string + char * tailShiftPtr = locString + stringLen; + char * tailStartPtr = locString + oldPrefixLen; // pointer to tail of string to shift + + while (tailShiftPtr >= tailStartPtr) { + tailShiftPtr[lengthDelta] = *tailShiftPtr; + tailShiftPtr--; + } + } else { + // no room, can't do substitution + newPrefix = NULL; + } + } + + if (newPrefix) { + // do the substitution + _CopyReplacementAtPointer(locString, newPrefix); + } +} + +// _UpdateFullLocaleString +// Given a locale string that uses standard codes (not a special old-style Apple string), +// update all the language codes and region codes to latest versions, map 3-letter +// language codes to 2-letter codes if possible, and normalize casing. If requested, return +// pointers to a language-region variant subtag (if present) and a region tag (if present). +// (add locStringMaxLen parameter) // <1.10> +static void _UpdateFullLocaleString(char inLocaleString[], int locStringMaxLen, + char **langRegSubtagRef, char **regionTagRef, + char varKeyValueString[]) // <1.17> +{ + KeyStringToResultString testEntry; + KeyStringToResultString * foundEntry; + const SpecialCaseUpdates * specialCasePtr; + char * inLocalePtr; + char * subtagPtr; + char * langRegSubtag = NULL; + char * regionTag = NULL; + char * variantTag = NULL; + Boolean subtagHasDigits, pastPrimarySubtag, hadRegion; + + // 1. First replace any non-canonical prefix (case insensitive) with canonical + // (change 3-letter ISO 639 code to 2-letter, update obsolete ISO 639 codes & RFC 3066 tags, etc.) + + testEntry.key = inLocaleString; + foundEntry = (KeyStringToResultString *)bsearch( &testEntry, localeStringPrefixToCanonical, kNumLocaleStringPrefixToCanonical, + sizeof(KeyStringToResultString), _CompareLowerTestEntryPrefixToTableEntryKey ); + if (foundEntry) { + // replace key (at beginning of string) with result + _ReplacePrefix(inLocaleString, locStringMaxLen, (int)strlen(foundEntry->key), foundEntry->result); // <1.10> + } + + // 2. Walk through input string, normalizing case & marking use of ISO 3166 codes + + inLocalePtr = inLocaleString; + subtagPtr = inLocaleString; + subtagHasDigits = false; + pastPrimarySubtag = false; + hadRegion = false; + + while ( true ) { + if ( isalpha(*inLocalePtr) ) { + // if not past a region tag, then lowercase, else uppercase + *inLocalePtr = (!hadRegion)? tolower(*inLocalePtr): toupper(*inLocalePtr); + } else if ( isdigit(*inLocalePtr) ) { + subtagHasDigits = true; + } else { + + if (!pastPrimarySubtag) { + // may have a NULL primary subtag + if (subtagHasDigits) { + break; + } + pastPrimarySubtag = true; + } else if (!hadRegion) { + // We are after any primary language subtag, but not past any region tag. + // This subtag is preceded by '-' or '_'. + int subtagLength = inLocalePtr - subtagPtr; // includes leading '-' or '_' + + if (subtagLength == 3 && !subtagHasDigits) { + // potential ISO 3166 code for region or language variant; if so, needs uppercasing + if (*subtagPtr == '_') { + regionTag = subtagPtr; + hadRegion = true; + subtagPtr[1] = toupper(subtagPtr[1]); + subtagPtr[2] = toupper(subtagPtr[2]); + } else if (langRegSubtag == NULL) { + langRegSubtag = subtagPtr; + subtagPtr[1] = toupper(subtagPtr[1]); + subtagPtr[2] = toupper(subtagPtr[2]); + } + } else if (subtagLength == 4 && subtagHasDigits) { + // potential UN M.49 region code + if (*subtagPtr == '_') { + regionTag = subtagPtr; + hadRegion = true; + } else if (langRegSubtag == NULL) { + langRegSubtag = subtagPtr; + } + } else if (subtagLength == 5 && !subtagHasDigits) { + // ISO 15924 script code, uppercase just the first letter + subtagPtr[1] = toupper(subtagPtr[1]); + } else if (subtagLength == 1 && *subtagPtr == '_') { // <1.17> + hadRegion = true; + } + + if (!hadRegion) { + // convert improper '_' to '-' + *subtagPtr = '-'; + } + } else { + variantTag = subtagPtr; // <1.17> + } + + if (*inLocalePtr == '-' || *inLocalePtr == '_') { + subtagPtr = inLocalePtr; + subtagHasDigits = false; + } else { + break; + } + } + + inLocalePtr++; + } + + // 3 If there is a variant tag, see if ICU canonicalizes it to keywords. // <1.17> [3577669] + // If so, copy the keywords to varKeyValueString and delete the variant tag + // from the original string (but don't otherwise use the ICU canonicalization). + varKeyValueString[0] = 0; + if (variantTag) { + UErrorCode icuStatus; + int icuCanonStringLen; + char * varKeyValueStringPtr = varKeyValueString; + + icuStatus = U_ZERO_ERROR; + icuCanonStringLen = uloc_canonicalize( inLocaleString, varKeyValueString, locStringMaxLen, &icuStatus ); + if ( U_SUCCESS(icuStatus) ) { + char * icuCanonStringPtr = varKeyValueString; + + if (icuCanonStringLen >= locStringMaxLen) + icuCanonStringLen = locStringMaxLen - 1; + varKeyValueString[icuCanonStringLen] = 0; + while (*icuCanonStringPtr != 0 && *icuCanonStringPtr != ULOC_KEYWORD_SEPARATOR) + ++icuCanonStringPtr; + if (*icuCanonStringPtr != 0) { + // the canonicalized string has keywords + // delete the variant tag in the original string (and other trailing '_' or '-') + *variantTag-- = 0; + while (*variantTag == '_') + *variantTag-- = 0; + // delete all of the canonicalized string except the keywords + while (*icuCanonStringPtr != 0) + *varKeyValueStringPtr++ = *icuCanonStringPtr++; + } + *varKeyValueStringPtr = 0; + } + } + + // 4. Handle special cases of updating region codes, or updating language codes based on + // region code. + for (specialCasePtr = specialCases; specialCasePtr->reg1 != NULL; specialCasePtr++) { + if ( specialCasePtr->lang == NULL || _CheckForTag(inLocaleString, specialCasePtr->lang, 2) ) { + // OK, we matched any language specified. Now what needs updating? + char * foundTag; + + if ( isupper(specialCasePtr->update1[0]) ) { + // updating a region code + if ( ( foundTag = strstr(inLocaleString, specialCasePtr->reg1) ) && !isalnum(foundTag[3]) ) { + _CopyReplacementAtPointer(foundTag+1, specialCasePtr->update1); + } + if ( regionTag && _CheckForTag(regionTag+1, specialCasePtr->reg1 + 1, 2) ) { + _CopyReplacementAtPointer(regionTag+1, specialCasePtr->update1); + } + + } else { + // updating the language, there will be two choices based on region + if ( ( regionTag && _CheckForTag(regionTag+1, specialCasePtr->reg1 + 1, 2) ) || + ( ( foundTag = strstr(inLocaleString, specialCasePtr->reg1) ) && !isalnum(foundTag[3]) ) ) { + _CopyReplacementAtPointer(inLocaleString, specialCasePtr->update1); + } else if ( ( regionTag && _CheckForTag(regionTag+1, specialCasePtr->reg2 + 1, 2) ) || + ( ( foundTag = strstr(inLocaleString, specialCasePtr->reg2) ) && !isalnum(foundTag[3]) ) ) { + _CopyReplacementAtPointer(inLocaleString, specialCasePtr->update2); + } + } + } + } + + // 5. return pointers if requested. + if (langRegSubtagRef != NULL) { + *langRegSubtagRef = langRegSubtag; + } + if (regionTagRef != NULL) { + *regionTagRef = regionTag; + } +} + + +// _RemoveSubstringsIfPresent +// (Local function for CFLocaleCreateCanonicalLocaleIdentifierFromString) +// substringList is a list of space-separated substrings to strip if found in localeString +static void _RemoveSubstringsIfPresent(char *localeString, const char *substringList) { + while (*substringList != 0) { + char currentSubstring[kLocaleIdentifierCStringMax]; + int substringLength = 0; + char * foundSubstring; + + // copy current substring & get its length + while ( isgraph(*substringList) ) { + currentSubstring[substringLength++] = *substringList++; + } + // move to next substring + while ( isspace(*substringList) ) { + substringList++; + } + + // search for current substring in locale string + if (substringLength == 0) + continue; + currentSubstring[substringLength] = 0; + foundSubstring = strstr(localeString, currentSubstring); + + // if substring is found, delete it + if (foundSubstring) { + _DeleteCharsAtPointer(foundSubstring, substringLength); + } + } +} + + +// _GetKeyValueString // <1.10> +// Removes any key-value string from inLocaleString, puts canonized version in keyValueString + +static void _GetKeyValueString(char inLocaleString[], char keyValueString[]) { + char * inLocalePtr = inLocaleString; + + while (*inLocalePtr != 0 && *inLocalePtr != ULOC_KEYWORD_SEPARATOR) { + inLocalePtr++; + } + if (*inLocalePtr != 0) { // we found a key-value section + char * keyValuePtr = keyValueString; + + *keyValuePtr = *inLocalePtr; + *inLocalePtr = 0; + do { + if ( *(++inLocalePtr) != ' ' ) { + *(++keyValuePtr) = *inLocalePtr; // remove "tolower() for *inLocalePtr" // <1.11> + } + } while (*inLocalePtr != 0); + } else { + keyValueString[0] = 0; + } +} + +static void _AppendKeyValueString(char inLocaleString[], int locStringMaxLen, char keyValueString[]) { + if (keyValueString[0] != 0) { + UErrorCode uerr = U_ZERO_ERROR; + UEnumeration * uenum = uloc_openKeywords(keyValueString, &uerr); + if ( uenum != NULL ) { + const char * keyword; + int32_t length; + char value[ULOC_KEYWORDS_CAPACITY]; // use as max for keyword value + while ( U_SUCCESS(uerr) ) { + keyword = uenum_next(uenum, &length, &uerr); + if ( keyword == NULL ) { + break; + } + length = uloc_getKeywordValue( keyValueString, keyword, value, sizeof(value), &uerr ); + length = uloc_setKeywordValue( keyword, value, inLocaleString, locStringMaxLen, &uerr ); + } + uenum_close(uenum); + } + } +} + +__private_extern__ CFStringRef _CFLocaleCreateCanonicalLanguageIdentifierForCFBundle(CFAllocatorRef allocator, CFStringRef localeIdentifier) { + char inLocaleString[kLocaleIdentifierCStringMax]; + CFStringRef outStringRef = NULL; + + if ( localeIdentifier && CFStringGetCString(localeIdentifier, inLocaleString, sizeof(inLocaleString), kCFStringEncodingASCII) ) { + KeyStringToResultString testEntry; + KeyStringToResultString * foundEntry; + char keyValueString[sizeof(inLocaleString)]; // <1.10> + char varKeyValueString[sizeof(inLocaleString)]; // <1.17> + + _GetKeyValueString(inLocaleString, keyValueString); // <1.10> + testEntry.result = NULL; + + // A. First check if input string matches an old-style string that has a replacement + // (do this before case normalization) + testEntry.key = inLocaleString; + foundEntry = (KeyStringToResultString *)bsearch( &testEntry, oldAppleLocaleToCanonical, kNumOldAppleLocaleToCanonical, + sizeof(KeyStringToResultString), _CompareTestEntryToTableEntryKey ); + if (foundEntry) { + // It does match, so replace old string with new + strlcpy(inLocaleString, foundEntry->result, sizeof(inLocaleString)); + varKeyValueString[0] = 0; + } else { + // B. No match with an old-style string, use input string but update codes, normalize case, etc. + _UpdateFullLocaleString(inLocaleString, sizeof(inLocaleString), NULL, NULL, varKeyValueString); // <1.10><1.17> + } + + // C. Now we have an up-to-date locale string, but we need to strip defaults and turn it into a language string + + // 1. Strip defaults in input string based on initial part of locale string + // (mainly to strip default script tag for a language) + testEntry.key = inLocaleString; + foundEntry = (KeyStringToResultString *)bsearch( &testEntry, localeStringPrefixToDefaults, kNumLocaleStringPrefixToDefaults, + sizeof(KeyStringToResultString), _CompareTestEntryPrefixToTableEntryKey ); + if (foundEntry) { + // The input string begins with a character sequence for which + // there are default substrings which should be stripped if present + _RemoveSubstringsIfPresent(inLocaleString, foundEntry->result); + } + + // 2. If the string matches a locale string used by Apple as a language string, turn it into a language string + testEntry.key = inLocaleString; + foundEntry = (KeyStringToResultString *)bsearch( &testEntry, appleLocaleToLanguageStringForCFBundle, kNumAppleLocaleToLanguageStringForCFBundle, + sizeof(KeyStringToResultString), _CompareTestEntryToTableEntryKey ); + if (foundEntry) { + // it does match + strlcpy(inLocaleString, foundEntry->result, sizeof(inLocaleString)); + } else { + // just delete the region tag and anything after + char * inLocalePtr = inLocaleString; + while (*inLocalePtr != 0 && *inLocalePtr != '_') { + inLocalePtr++; + } + *inLocalePtr = 0; + } + + // D. Re-append any key-value strings, now canonical // <1.10><1.17> + _AppendKeyValueString( inLocaleString, sizeof(inLocaleString), varKeyValueString ); + _AppendKeyValueString( inLocaleString, sizeof(inLocaleString), keyValueString ); + + // All done, return what we came up with. + outStringRef = CFStringCreateWithCString(allocator, inLocaleString, kCFStringEncodingASCII); + } + + return outStringRef; +} + +CFStringRef CFLocaleCreateCanonicalLanguageIdentifierFromString(CFAllocatorRef allocator, CFStringRef localeIdentifier) { + char inLocaleString[kLocaleIdentifierCStringMax]; + CFStringRef outStringRef = NULL; + + if ( localeIdentifier && CFStringGetCString(localeIdentifier, inLocaleString, sizeof(inLocaleString), kCFStringEncodingASCII) ) { + KeyStringToResultString testEntry; + KeyStringToResultString * foundEntry; + char keyValueString[sizeof(inLocaleString)]; // <1.10> + char varKeyValueString[sizeof(inLocaleString)]; // <1.17> + + _GetKeyValueString(inLocaleString, keyValueString); // <1.10> + testEntry.result = NULL; + + // A. First check if input string matches an old-style string that has a replacement + // (do this before case normalization) + testEntry.key = inLocaleString; + foundEntry = (KeyStringToResultString *)bsearch( &testEntry, oldAppleLocaleToCanonical, kNumOldAppleLocaleToCanonical, + sizeof(KeyStringToResultString), _CompareTestEntryToTableEntryKey ); + if (foundEntry) { + // It does match, so replace old string with new + strlcpy(inLocaleString, foundEntry->result, sizeof(inLocaleString)); + varKeyValueString[0] = 0; + } else { + char * langRegSubtag = NULL; + char * regionTag = NULL; + + // B. No match with an old-style string, use input string but update codes, normalize case, etc. + _UpdateFullLocaleString(inLocaleString, sizeof(inLocaleString), &langRegSubtag, ®ionTag, varKeyValueString); // <1.10><1.17><1.19> + + // if the language part already includes a regional variant, then delete any region tag. <1.19> + if (langRegSubtag && regionTag) + *regionTag = 0; + } + + // C. Now we have an up-to-date locale string, but we need to strip defaults and turn it into a language string + + // 1. Strip defaults in input string based on initial part of locale string + // (mainly to strip default script tag for a language) + testEntry.key = inLocaleString; + foundEntry = (KeyStringToResultString *)bsearch( &testEntry, localeStringPrefixToDefaults, kNumLocaleStringPrefixToDefaults, + sizeof(KeyStringToResultString), _CompareTestEntryPrefixToTableEntryKey ); + if (foundEntry) { + // The input string begins with a character sequence for which + // there are default substrings which should be stripped if present + _RemoveSubstringsIfPresent(inLocaleString, foundEntry->result); + } + + // 2. If the string matches a locale string used by Apple as a language string, turn it into a language string + testEntry.key = inLocaleString; + foundEntry = (KeyStringToResultString *)bsearch( &testEntry, appleLocaleToLanguageString, kNumAppleLocaleToLanguageString, + sizeof(KeyStringToResultString), _CompareTestEntryToTableEntryKey ); + if (foundEntry) { + // it does match + strlcpy(inLocaleString, foundEntry->result, sizeof(inLocaleString)); + } else { + // skip to any region tag or java-type variant + char * inLocalePtr = inLocaleString; + while (*inLocalePtr != 0 && *inLocalePtr != '_') { + inLocalePtr++; + } + // if there is still a region tag, turn it into a language variant <1.19> + if (*inLocalePtr == '_') { + // handle 3-digit regions in addition to 2-letter ones + char * regionTag = inLocalePtr++; + long expectedLength = 0; + if ( isalpha(*inLocalePtr) ) { + while ( isalpha(*(++inLocalePtr)) ) + ; + expectedLength = 3; + } else if ( isdigit(*inLocalePtr) ) { + while ( isdigit(*(++inLocalePtr)) ) + ; + expectedLength = 4; + } + *regionTag = (inLocalePtr - regionTag == expectedLength)? '-': 0; + } + // anything else at/after '_' just gets deleted + *inLocalePtr = 0; + } + + // D. Re-append any key-value strings, now canonical // <1.10><1.17> + _AppendKeyValueString( inLocaleString, sizeof(inLocaleString), varKeyValueString ); + _AppendKeyValueString( inLocaleString, sizeof(inLocaleString), keyValueString ); + + // All done, return what we came up with. + outStringRef = CFStringCreateWithCString(allocator, inLocaleString, kCFStringEncodingASCII); + } + + return outStringRef; +} + + +CFStringRef CFLocaleCreateCanonicalLocaleIdentifierFromString(CFAllocatorRef allocator, CFStringRef localeIdentifier) { + char inLocaleString[kLocaleIdentifierCStringMax]; + CFStringRef outStringRef = NULL; + + if ( localeIdentifier && CFStringGetCString(localeIdentifier, inLocaleString, sizeof(inLocaleString), kCFStringEncodingASCII) ) { + KeyStringToResultString testEntry; + KeyStringToResultString * foundEntry; + char keyValueString[sizeof(inLocaleString)]; // <1.10> + char varKeyValueString[sizeof(inLocaleString)]; // <1.17> + + _GetKeyValueString(inLocaleString, keyValueString); // <1.10> + testEntry.result = NULL; + + // A. First check if input string matches an old-style Apple string that has a replacement + // (do this before case normalization) + testEntry.key = inLocaleString; + foundEntry = (KeyStringToResultString *)bsearch( &testEntry, oldAppleLocaleToCanonical, kNumOldAppleLocaleToCanonical, + sizeof(KeyStringToResultString), _CompareTestEntryToTableEntryKey ); + if (foundEntry) { + // It does match, so replace old string with new // <1.10> + strlcpy(inLocaleString, foundEntry->result, sizeof(inLocaleString)); + varKeyValueString[0] = 0; + } else { + char * langRegSubtag = NULL; + char * regionTag = NULL; + + // B. No match with an old-style string, use input string but update codes, normalize case, etc. + _UpdateFullLocaleString(inLocaleString, sizeof(inLocaleString), &langRegSubtag, ®ionTag, varKeyValueString); // <1.10><1.17> + + + // C. Now strip defaults that are implied by other fields. + + // 1. If an ISO 3166 region tag matches an ISO 3166 regional language variant subtag, strip the latter. + if ( langRegSubtag && regionTag && strncmp(langRegSubtag+1, regionTag+1, 2) == 0 ) { + _DeleteCharsAtPointer(langRegSubtag, 3); + } + + // 2. Strip defaults in input string based on final region tag in locale string + // (mainly for Chinese, to strip -Hans for _CN/_SG, -Hant for _TW/_HK/_MO) + if ( regionTag ) { + testEntry.key = regionTag; + foundEntry = (KeyStringToResultString *)bsearch( &testEntry, localeStringRegionToDefaults, kNumLocaleStringRegionToDefaults, + sizeof(KeyStringToResultString), _CompareTestEntryToTableEntryKey ); + if (foundEntry) { + _RemoveSubstringsIfPresent(inLocaleString, foundEntry->result); + } + } + + // 3. Strip defaults in input string based on initial part of locale string + // (mainly to strip default script tag for a language) + testEntry.key = inLocaleString; + foundEntry = (KeyStringToResultString *)bsearch( &testEntry, localeStringPrefixToDefaults, kNumLocaleStringPrefixToDefaults, + sizeof(KeyStringToResultString), _CompareTestEntryPrefixToTableEntryKey ); + if (foundEntry) { + // The input string begins with a character sequence for which + // there are default substrings which should be stripped if present + _RemoveSubstringsIfPresent(inLocaleString, foundEntry->result); + } + } + + // D. Re-append any key-value strings, now canonical // <1.10><1.17> + _AppendKeyValueString( inLocaleString, sizeof(inLocaleString), varKeyValueString ); + _AppendKeyValueString( inLocaleString, sizeof(inLocaleString), keyValueString ); + + // Now create the CFString (even if empty!) + outStringRef = CFStringCreateWithCString(allocator, inLocaleString, kCFStringEncodingASCII); + } + + return outStringRef; +} + +// CFLocaleCreateCanonicalLocaleIdentifierFromScriptManagerCodes, based on +// the first part of the SPI CFBundleCopyLocalizationForLocalizationInfo in CFBundle_Resources.c +CFStringRef CFLocaleCreateCanonicalLocaleIdentifierFromScriptManagerCodes(CFAllocatorRef allocator, LangCode lcode, RegionCode rcode) { + CFStringRef result = NULL; + if (0 <= rcode && rcode < kNumRegionCodeToLocaleString) { + const char *localeString = regionCodeToLocaleString[rcode]; + if (localeString != NULL && *localeString != '\0') { + result = CFStringCreateWithCStringNoCopy(allocator, localeString, kCFStringEncodingASCII, kCFAllocatorNull); + } + } + if (result) return result; + if (0 <= lcode && lcode < kNumLangCodeToLocaleString) { + const char *localeString = langCodeToLocaleString[lcode]; + if (localeString != NULL && *localeString != '\0') { + result = CFStringCreateWithCStringNoCopy(allocator, localeString, kCFStringEncodingASCII, kCFAllocatorNull); + } + } + return result; +} + + +CFDictionaryRef CFLocaleCreateComponentsFromLocaleIdentifier(CFAllocatorRef allocator, CFStringRef localeID) { + char cLocaleID[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY]; + char buffer[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY]; + CFMutableDictionaryRef working = CFDictionaryCreateMutable(allocator, 10, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + UErrorCode icuStatus = U_ZERO_ERROR; + int32_t length = 0; + + // Extract the C string locale ID, for ICU + CFIndex outBytes = 0; + CFStringGetBytes(localeID, CFRangeMake(0, CFStringGetLength(localeID)), kCFStringEncodingASCII, (UInt8) '?', true, (unsigned char *)cLocaleID, sizeof(cLocaleID)/sizeof(char) - 1, &outBytes); + cLocaleID[outBytes] = '\0'; + + // Get the components + length = uloc_getLanguage(cLocaleID, buffer, sizeof(buffer)/sizeof(char), &icuStatus); + if (U_SUCCESS(icuStatus) && length > 0) + { + CFStringRef string = CFStringCreateWithBytes(allocator, (UInt8 *)buffer, length, kCFStringEncodingASCII, true); + CFDictionaryAddValue(working, kCFLocaleLanguageCode, string); + CFRelease(string); + } + icuStatus = U_ZERO_ERROR; + + length = uloc_getScript(cLocaleID, buffer, sizeof(buffer)/sizeof(char), &icuStatus); + if (U_SUCCESS(icuStatus) && length > 0) + { + CFStringRef string = CFStringCreateWithBytes(allocator, (UInt8 *)buffer, length, kCFStringEncodingASCII, true); + CFDictionaryAddValue(working, kCFLocaleScriptCode, string); + CFRelease(string); + } + icuStatus = U_ZERO_ERROR; + + length = uloc_getCountry(cLocaleID, buffer, sizeof(buffer)/sizeof(char), &icuStatus); + if (U_SUCCESS(icuStatus) && length > 0) + { + CFStringRef string = CFStringCreateWithBytes(allocator, (UInt8 *)buffer, length, kCFStringEncodingASCII, true); + CFDictionaryAddValue(working, kCFLocaleCountryCode, string); + CFRelease(string); + } + icuStatus = U_ZERO_ERROR; + + length = uloc_getVariant(cLocaleID, buffer, sizeof(buffer)/sizeof(char), &icuStatus); + if (U_SUCCESS(icuStatus) && length > 0) + { + CFStringRef string = CFStringCreateWithBytes(allocator, (UInt8 *)buffer, length, kCFStringEncodingASCII, true); + CFDictionaryAddValue(working, kCFLocaleVariantCode, string); + CFRelease(string); + } + icuStatus = U_ZERO_ERROR; + + // Now get the keywords; open an enumerator on them + UEnumeration *iter = uloc_openKeywords(cLocaleID, &icuStatus); + const char *locKey = NULL; + int32_t locKeyLen = 0; + while ((locKey = uenum_next(iter, &locKeyLen, &icuStatus)) && U_SUCCESS(icuStatus)) + { + char locValue[ULOC_KEYWORD_AND_VALUES_CAPACITY]; + + // Get the value for this keyword + if (uloc_getKeywordValue(cLocaleID, locKey, locValue, sizeof(locValue)/sizeof(char), &icuStatus) > 0 + && U_SUCCESS(icuStatus)) + { + CFStringRef key = CFStringCreateWithBytes(allocator, (UInt8 *)locKey, (CFIndex)strlen(locKey), kCFStringEncodingASCII, true); + CFStringRef value = CFStringCreateWithBytes(allocator, (UInt8 *)locValue, (CFIndex)strlen(locValue), kCFStringEncodingASCII, true); + if (key && value) + CFDictionaryAddValue(working, key, value); + if (key) + CFRelease(key); + if (value) + CFRelease(value); + } + } + uenum_close(iter); + + // Convert to an immutable dictionary and return + CFDictionaryRef result = CFDictionaryCreateCopy(allocator, working); + CFRelease(working); + return result; +} + +typedef struct __AppendContext +{ + char separator; + CFMutableStringRef working; +} __AppendContext; + +static void __AppendKeywords(const void *k, const void *v, void *c) +{ + __AppendContext *context = (__AppendContext *) c; + CFStringRef key = (CFStringRef) k; + CFStringRef value = (CFStringRef) v; + if (CFEqual(key, kCFLocaleLanguageCode) || CFEqual(key, kCFLocaleScriptCode) || CFEqual(key, kCFLocaleCountryCode) || CFEqual(key, kCFLocaleVariantCode)) + return; + CFStringAppendFormat(context->working, NULL, CFSTR("%c%@%c%@"), context->separator, key, ULOC_KEYWORD_ASSIGN, value); + context->separator = ULOC_KEYWORD_ITEM_SEPARATOR; +} + +CFStringRef CFLocaleCreateLocaleIdentifierFromComponents(CFAllocatorRef allocator, CFDictionaryRef dictionary) { + CFMutableStringRef working = CFStringCreateMutable(allocator, 0); + CFStringRef value = NULL; + bool country = false; + __AppendContext context = {ULOC_KEYWORD_SEPARATOR, working}; + + if ((value = (CFStringRef) CFDictionaryGetValue(dictionary, kCFLocaleLanguageCode))) + { + CFStringAppend(working, value); + } + + if ((value = (CFStringRef) CFDictionaryGetValue(dictionary, kCFLocaleScriptCode))) + { + CFStringAppendFormat(working, NULL, CFSTR("_%@"), value); + } + + if ((value = (CFStringRef) CFDictionaryGetValue(dictionary, kCFLocaleCountryCode))) + { + CFStringAppendFormat(working, NULL, CFSTR("_%@"), value); + country = true; + } + + if ((value = (CFStringRef) CFDictionaryGetValue(dictionary, kCFLocaleVariantCode))) + { + if (!country) + CFStringAppend(working, CFSTR("_")); + CFStringAppendFormat(working, NULL, CFSTR("_%@"), value); + } + + // Now iterate through any remaining entries and append as keywords + CFDictionaryApplyFunction(dictionary, __AppendKeywords, &context); + + // Convert to immutable string and return + CFStringRef result = (CFStringRef)CFStringCreateCopy(allocator, working); + CFRelease(working); + return result; +} +
diff --git a/CoreFoundation/CFLogUtilities.h b/CoreFoundation/CFLogUtilities.h new file mode 100644 index 0000000..f035265 --- /dev/null +++ b/CoreFoundation/CFLogUtilities.h
@@ -0,0 +1,73 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFLogUtilities.h + Copyright (c) 2004-2007, Apple Inc. All rights reserved. +*/ + +/* + APPLE SPI: NOT TO BE USED OUTSIDE APPLE! +*/ + +#if !defined(__COREFOUNDATION_CFLOGUTILITIES__) +#define __COREFOUNDATION_CFLOGUTILITIES__ 1 + +#include <CoreFoundation/CFBase.h> +#include <CoreFoundation/CFString.h> + +CF_EXTERN_C_BEGIN + + +enum { // Legal level values for CFLog() + kCFLogLevelEmergency = 0, + kCFLogLevelAlert = 1, + kCFLogLevelCritical = 2, + kCFLogLevelError = 3, + kCFLogLevelWarning = 4, + kCFLogLevelNotice = 5, + kCFLogLevelInfo = 6, + kCFLogLevelDebug = 7, +}; + +CF_EXPORT void CFLog(int32_t level, CFStringRef format, ...); +/* Passing in a level value which is outside the range of 0-7 will cause the the call to do nothing. + CFLog() logs the message using the asl.h API, and uses the level parameter as the log level. + Note that the asl subsystem ignores some log levels by default. + CFLog() is not fast, and is not going to be guaranteed to be fast. + Even "no-op" CFLogs are not necessarily fast. + If you care about performance, you shouldn't be logging. +*/ + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFLOGUTILITIES__ */ +
diff --git a/CoreFoundation/CFMachPort.c b/CoreFoundation/CFMachPort.c new file mode 100644 index 0000000..10b3d1c --- /dev/null +++ b/CoreFoundation/CFMachPort.c
@@ -0,0 +1,595 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFMachPort.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +/* + [The following dissertation was written mostly for the + benefit of open source developers.] + + Writing a run loop source can be a tricky business, but + for CFMachPort that part is relatively straightforward. + Thus, it makes a good example for study. Particularly + interesting for examination is the process of caching + the objects in a non-retaining cache, the invalidation + and deallocation sequences, locking for thread-safety, + and how the invalidation callback is used. + + CFMachPort is a version 1 CFRunLoopSource, implemented + by a few functions. See CFMachPortCreateRunLoopSource() + for details on how the run loop source is setup. Note + how the source is kept track of by the CFMachPort, so + that it can be returned again and again from that function. + This helps not only reduce the amount of memory expended + in run loop source objects, but eliminates redundant + registrations with the run loop and the excess time and + memory that would consume. It also allows the CFMachPort + to propogate its own invalidation to the run loop source + representing it. + + CFMachPortCreateWithPort() is the funnel point for the + creation of CFMachPort instances. The cache is first + probed to see if an instance with that port is already + available, and return that. The object is next allocated + and mostly initialized, before it is registered for death + notification. This is because cleaning up the memory is + simpler than trying to get rid of the registration if + memory allocation later fails. The new object must be at + least partially initialized (into a harmless state) so + that it can be safely invalidated/deallocated if something + fails later in creation. Any object allocated with + _CFRuntimeCreateInstance() may only be disposed by using + CFRelease() (never CFAllocatorDeallocate!) so the class + deallocation function __CFMachPortDeallocate() must be + able to handle that, and initializing the object to have + NULL fields and whatnot makes that possible. The creation + eventually inserts the new object in the cache. + + A CFMachPort may be explicitly invalidated, autoinvalidated + due to the death of the port (that process is not discussed + further here), or invalidated as part of the deallocation + process when the last reference is released. For + convenience, in all cases this is done through + CFMachPortInvalidate(). To prevent the CFMachPort from + being freed in mid-function due to the callouts, the object + is retained at the beginning of the function. But if this + invalidation is due to the object being deallocated, a + retain and then release at the end of the function would + cause a recursive call to __CFMachPortDeallocate(). The + retain protection should be immaterial though at that stage. + Invalidation also removes the object from the cache; though + the object itself is not yet destroyed, invalidation makes + it "useless". + + The best way to learn about the locking is to look through + the code -- it's fairly straightforward. The one thing + worth calling attention to is how locks must be unlocked + before invoking any user-defined callout function, and + usually retaken after it returns. This supports reentrancy + (which is distinct from thread-safety). + + The invalidation callback, if one has been set, is called + at invalidation time, but before the object has been torn + down so that the port and other properties may be retrieved + from the object in the callback. Note that if the callback + is attempted to be set after the CFMachPort is invalid, + the function is simply called. This helps with certain + race conditions where the invalidation notification might + be lost. Only the owner/creator of a CFMachPort should + really be setting the invalidation callback. + + Now, the CFMachPort is not retained/released around all + callouts, but the callout may release the last reference. + Also, sometimes it is friendly to retain/release the + user-defined "info" around callouts, so that clients + don't have to worry about that. These may be some things + to think about in the future, but is usually overkill. + + In general, with higher level functionalities in the system, + it isn't even possible for a process to fork() and the child + not exec(), but continue running, since the higher levels + have done one-time initializations that aren't going to + happen again. + + - Chris Kane + +*/ + +#if DEPLOYMENT_TARGET_MACOSX + +#include <CoreFoundation/CFMachPort.h> +#include <CoreFoundation/CFRunLoop.h> +#include <CoreFoundation/CFDictionary.h> +#include <CoreFoundation/CFByteOrder.h> +#include <mach/mach.h> +#include <mach/mach_error.h> +#include <mach/notify.h> +#include <unistd.h> +#include "CFInternal.h" +#include <dlfcn.h> + +static CFSpinLock_t __CFAllMachPortsLock = CFSpinLockInit; +static CFMutableDictionaryRef __CFAllMachPorts = NULL; +static mach_port_t __CFNotifyRawMachPort = MACH_PORT_NULL; +static CFMachPortRef __CFNotifyMachPort = NULL; + +struct __CFMachPort { + CFRuntimeBase _base; + CFSpinLock_t _lock; + mach_port_t _port; /* immutable; invalidated */ + mach_port_t _oldnotify; /* immutable; invalidated */ + CFRunLoopSourceRef _source; /* immutable, once created; invalidated */ + CFMachPortInvalidationCallBack _icallout; + CFMachPortCallBack _callout; /* immutable */ + CFMachPortContext _context; /* immutable; invalidated */ +}; + +/* Bit 0 in the base reserved bits is used for invalid state */ +/* Bit 1 in the base reserved bits is used for has-receive-ref state */ +/* Bit 2 in the base reserved bits is used for has-send-ref state */ +/* Bit 3 in the base reserved bits is used for is-deallocing state */ + +CF_INLINE Boolean __CFMachPortIsValid(CFMachPortRef mp) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 0, 0); +} + +CF_INLINE void __CFMachPortSetValid(CFMachPortRef mp) { + __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 0, 0, 1); +} + +CF_INLINE void __CFMachPortUnsetValid(CFMachPortRef mp) { + __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 0, 0, 0); +} + +CF_INLINE Boolean __CFMachPortHasReceive(CFMachPortRef mp) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 1, 1); +} + +CF_INLINE void __CFMachPortSetHasReceive(CFMachPortRef mp) { + __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 1, 1, 1); +} + +CF_INLINE Boolean __CFMachPortHasSend(CFMachPortRef mp) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 2, 2); +} + +CF_INLINE void __CFMachPortSetHasSend(CFMachPortRef mp) { + __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 2, 2, 1); +} + +CF_INLINE Boolean __CFMachPortIsDeallocing(CFMachPortRef mp) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 3, 3); +} + +CF_INLINE void __CFMachPortSetIsDeallocing(CFMachPortRef mp) { + __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 3, 3, 1); +} + +CF_INLINE void __CFMachPortLock(CFMachPortRef mp) { + __CFSpinLock(&(mp->_lock)); +} + +CF_INLINE void __CFMachPortUnlock(CFMachPortRef mp) { + __CFSpinUnlock(&(mp->_lock)); +} + +void _CFMachPortInstallNotifyPort(CFRunLoopRef rl, CFStringRef mode) { + CFRunLoopSourceRef source; + if (NULL == __CFNotifyMachPort) return; + source = CFMachPortCreateRunLoopSource(kCFAllocatorSystemDefault, __CFNotifyMachPort, -1000); + CFRunLoopAddSource(rl, source, mode); + CFRelease(source); +} + +static void __CFNotifyDeadMachPort(CFMachPortRef port, void *msg, CFIndex size, void *info) { + mach_msg_header_t *header = (mach_msg_header_t *)msg; + mach_port_t dead_port = MACH_PORT_NULL; + if (header && header->msgh_id == MACH_NOTIFY_DEAD_NAME) { + dead_port = ((mach_dead_name_notification_t *)msg)->not_port; + if (((mach_dead_name_notification_t *)msg)->NDR.int_rep != NDR_record.int_rep) { + dead_port = CFSwapInt32(dead_port); + } + } else if (header && header->msgh_id == MACH_NOTIFY_PORT_DELETED) { + dead_port = ((mach_port_deleted_notification_t *)msg)->not_port; + if (((mach_port_deleted_notification_t *)msg)->NDR.int_rep != NDR_record.int_rep) { + dead_port = CFSwapInt32(dead_port); + } + } else { + return; + } + + CFMachPortRef existing; + /* If the CFMachPort has already been invalidated, it won't be found here. */ + __CFSpinLock(&__CFAllMachPortsLock); + if (NULL != __CFAllMachPorts && CFDictionaryGetValueIfPresent(__CFAllMachPorts, (void *)(uintptr_t)dead_port, (const void **)&existing)) { + CFDictionaryRemoveValue(__CFAllMachPorts, (void *)(uintptr_t)dead_port); + CFRetain(existing); + __CFSpinUnlock(&__CFAllMachPortsLock); + __CFMachPortLock(existing); + mach_port_t old_port = existing->_oldnotify; + existing->_oldnotify = MACH_PORT_NULL; + __CFMachPortUnlock(existing); + if (MACH_PORT_NULL != old_port) { + header->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0) | MACH_MSGH_BITS_COMPLEX; + header->msgh_local_port = MACH_PORT_NULL; + header->msgh_remote_port = old_port; + mach_msg(header, MACH_SEND_MSG, header->msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + } + CFMachPortInvalidate(existing); + CFRelease(existing); + } else { + __CFSpinUnlock(&__CFAllMachPortsLock); + } + + if (header && header->msgh_id == MACH_NOTIFY_DEAD_NAME) { + /* Delete port reference we got for this notification */ + mach_port_deallocate(mach_task_self(), dead_port); + } +} + +static Boolean __CFMachPortEqual(CFTypeRef cf1, CFTypeRef cf2) { + CFMachPortRef mp1 = (CFMachPortRef)cf1; + CFMachPortRef mp2 = (CFMachPortRef)cf2; + return (mp1->_port == mp2->_port); +} + +static CFHashCode __CFMachPortHash(CFTypeRef cf) { + CHECK_FOR_FORK(); + CFMachPortRef mp = (CFMachPortRef)cf; + return (CFHashCode)mp->_port; +} + +static CFStringRef __CFMachPortCopyDescription(CFTypeRef cf) { + CFMachPortRef mp = (CFMachPortRef)cf; + CFStringRef result; + const char *locked; + CFStringRef contextDesc = NULL; + locked = mp->_lock ? "Yes" : "No"; + if (NULL != mp->_context.info && NULL != mp->_context.copyDescription) { + contextDesc = mp->_context.copyDescription(mp->_context.info); + } + if (NULL == contextDesc) { + contextDesc = CFStringCreateWithFormat(CFGetAllocator(mp), NULL, CFSTR("<CFMachPort context %p>"), mp->_context.info); + } + void *addr = (void*)mp->_callout; + Dl_info info; + const char *name = (dladdr(addr, &info) && info.dli_saddr == addr && info.dli_sname) ? info.dli_sname : "???"; + result = CFStringCreateWithFormat(CFGetAllocator(mp), NULL, CFSTR("<CFMachPort %p [%p]>{locked = %s, valid = %s, port = %p, source = %p, callout = %s (%p), context = %@}"), cf, CFGetAllocator(mp), locked, (__CFMachPortIsValid(mp) ? "Yes" : "No"), mp->_port, mp->_source, name, addr, (NULL != contextDesc ? contextDesc : CFSTR("<no description>"))); + if (NULL != contextDesc) { + CFRelease(contextDesc); + } + return result; +} + +static void __CFMachPortDeallocate(CFTypeRef cf) { + CHECK_FOR_FORK(); + CFMachPortRef mp = (CFMachPortRef)cf; + __CFMachPortSetIsDeallocing(mp); + CFMachPortInvalidate(mp); + // MUST deallocate the send right FIRST if necessary, + // then the receive right if necessary. Don't ask me why; + // if it's done in the other order the port will leak. + if (__CFMachPortHasSend(mp)) { + mach_port_mod_refs(mach_task_self(), mp->_port, MACH_PORT_RIGHT_SEND, -1); + } + if (__CFMachPortHasReceive(mp)) { + mach_port_mod_refs(mach_task_self(), mp->_port, MACH_PORT_RIGHT_RECEIVE, -1); + } +} + +static CFTypeID __kCFMachPortTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFMachPortClass = { + 0, + "CFMachPort", + NULL, // init + NULL, // copy + __CFMachPortDeallocate, + __CFMachPortEqual, + __CFMachPortHash, + NULL, // + __CFMachPortCopyDescription +}; + +__private_extern__ void __CFMachPortInitialize(void) { + __kCFMachPortTypeID = _CFRuntimeRegisterClass(&__CFMachPortClass); +} + +CFTypeID CFMachPortGetTypeID(void) { + return __kCFMachPortTypeID; +} + +CFMachPortRef CFMachPortCreate(CFAllocatorRef allocator, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo) { + CFMachPortRef result; + mach_port_t port; + kern_return_t ret; + if (shouldFreeInfo) *shouldFreeInfo = true; + ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port); + if (KERN_SUCCESS != ret) { + return NULL; + } + ret = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND); + if (KERN_SUCCESS != ret) { + mach_port_destroy(mach_task_self(), port); + return NULL; + } + result = CFMachPortCreateWithPort(allocator, port, callout, context, shouldFreeInfo); + if (NULL != result) { + __CFMachPortSetHasReceive(result); + __CFMachPortSetHasSend(result); + } + return result; +} + +/* Note: any receive or send rights that the port contains coming in will + * not be cleaned up by CFMachPort; it will increment and decrement + * references on the port if the kernel ever allows that in the future, + * but will not cleanup any references you got when you got the port. */ +CFMachPortRef CFMachPortCreateWithPort(CFAllocatorRef allocator, mach_port_t port, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo) { + CHECK_FOR_FORK(); + CFMachPortRef memory; + SInt32 size; + Boolean didCreateNotifyPort = false; + CFRunLoopSourceRef source; + if (shouldFreeInfo) *shouldFreeInfo = true; + __CFSpinLock(&__CFAllMachPortsLock); + if (NULL != __CFAllMachPorts && CFDictionaryGetValueIfPresent(__CFAllMachPorts, (void *)(uintptr_t)port, (const void **)&memory)) { + CFRetain(memory); + __CFSpinUnlock(&__CFAllMachPortsLock); + return (CFMachPortRef)(memory); + } + size = sizeof(struct __CFMachPort) - sizeof(CFRuntimeBase); + memory = (CFMachPortRef)_CFRuntimeCreateInstance(allocator, __kCFMachPortTypeID, size, NULL); + if (NULL == memory) { + __CFSpinUnlock(&__CFAllMachPortsLock); + return NULL; + } + __CFMachPortUnsetValid(memory); + memory->_lock = CFSpinLockInit; + memory->_port = port; + memory->_source = NULL; + memory->_icallout = NULL; + memory->_context.info = NULL; + memory->_context.retain = NULL; + memory->_context.release = NULL; + memory->_context.copyDescription = NULL; + if (MACH_PORT_NULL == __CFNotifyRawMachPort) { + kern_return_t ret; + ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &__CFNotifyRawMachPort); + if (KERN_SUCCESS != ret) { + __CFSpinUnlock(&__CFAllMachPortsLock); + CFRelease(memory); + return NULL; + } + didCreateNotifyPort = true; + } + // Do not register for notifications on the notify port + if (MACH_PORT_NULL != __CFNotifyRawMachPort && port != __CFNotifyRawMachPort) { + mach_port_t old_port; + kern_return_t ret; + old_port = MACH_PORT_NULL; + ret = mach_port_request_notification(mach_task_self(), port, MACH_NOTIFY_DEAD_NAME, 0, __CFNotifyRawMachPort, MACH_MSG_TYPE_MAKE_SEND_ONCE, &old_port); + if (ret != KERN_SUCCESS) { + __CFSpinUnlock(&__CFAllMachPortsLock); + CFRelease(memory); + return NULL; + } + memory->_oldnotify = old_port; + } + __CFMachPortSetValid(memory); + memory->_callout = callout; + if (NULL != context) { + CF_WRITE_BARRIER_MEMMOVE(&memory->_context, context, sizeof(CFMachPortContext)); + memory->_context.info = context->retain ? (void *)context->retain(context->info) : context->info; + } + if (NULL == __CFAllMachPorts) { + __CFAllMachPorts = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, NULL); + _CFDictionarySetCapacity(__CFAllMachPorts, 20); + } + CFDictionaryAddValue(__CFAllMachPorts, (void *)(uintptr_t)port, memory); + __CFSpinUnlock(&__CFAllMachPortsLock); + if (didCreateNotifyPort) { + // __CFNotifyMachPort ends up in cache + CFMachPortRef mp = CFMachPortCreateWithPort(kCFAllocatorSystemDefault, __CFNotifyRawMachPort, __CFNotifyDeadMachPort, NULL, NULL); + __CFMachPortSetHasReceive(mp); + __CFNotifyMachPort = mp; + } + if (NULL != __CFNotifyMachPort) { + // We do this so that it gets into each thread's run loop, since + // we don't know which run loop is the main thread's, and that's + // not necessarily the "right" one anyway. This won't happen for + // the call which creates the __CFNotifyMachPort itself, but that's + // OK since it will happen in the invocation of this function + // from which that call was triggered. + source = CFMachPortCreateRunLoopSource(kCFAllocatorSystemDefault, __CFNotifyMachPort, -1000); + CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes); + CFRelease(source); + } + if (shouldFreeInfo) *shouldFreeInfo = false; + return memory; +} + +mach_port_t CFMachPortGetPort(CFMachPortRef mp) { + CHECK_FOR_FORK(); + CF_OBJC_FUNCDISPATCH0(__kCFMachPortTypeID, mach_port_t, mp, "machPort"); + __CFGenericValidateType(mp, __kCFMachPortTypeID); + return mp->_port; +} + +void CFMachPortGetContext(CFMachPortRef mp, CFMachPortContext *context) { + __CFGenericValidateType(mp, __kCFMachPortTypeID); + CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__); + CF_WRITE_BARRIER_MEMMOVE(context, &mp->_context, sizeof(CFMachPortContext)); +} + +void CFMachPortInvalidate(CFMachPortRef mp) { + CHECK_FOR_FORK(); + CF_OBJC_FUNCDISPATCH0(__kCFMachPortTypeID, void, mp, "invalidate"); + __CFGenericValidateType(mp, __kCFMachPortTypeID); + if (!__CFMachPortIsDeallocing(mp)) { + CFRetain(mp); + } + __CFSpinLock(&__CFAllMachPortsLock); + if (NULL != __CFAllMachPorts) { + CFDictionaryRemoveValue(__CFAllMachPorts, (void *)(uintptr_t)(mp->_port)); + } + __CFSpinUnlock(&__CFAllMachPortsLock); + __CFMachPortLock(mp); + if (__CFMachPortIsValid(mp)) { + CFRunLoopSourceRef source; + void *info; + mach_port_t old_port = mp->_oldnotify; + CFMachPortInvalidationCallBack callout = mp->_icallout; + __CFMachPortUnsetValid(mp); + __CFMachPortUnlock(mp); + if (NULL != callout) { + callout(mp, mp->_context.info); + } + __CFMachPortLock(mp); + // For hashing and equality purposes, cannot get rid of _port here + source = mp->_source; + mp->_source = NULL; + info = mp->_context.info; + mp->_context.info = NULL; + __CFMachPortUnlock(mp); + if (NULL != mp->_context.release) { + mp->_context.release(info); + } + if (NULL != source) { + CFRunLoopSourceInvalidate(source); + CFRelease(source); + } + if (MACH_PORT_NULL != old_port) { + mach_port_deallocate(mach_task_self(), old_port); + } + } else { + __CFMachPortUnlock(mp); + } + if (!__CFMachPortIsDeallocing(mp)) { + CFRelease(mp); + } +} + +Boolean CFMachPortIsValid(CFMachPortRef mp) { + CF_OBJC_FUNCDISPATCH0(__kCFMachPortTypeID, Boolean, mp, "isValid"); + __CFGenericValidateType(mp, __kCFMachPortTypeID); + return __CFMachPortIsValid(mp); +} + +CFMachPortInvalidationCallBack CFMachPortGetInvalidationCallBack(CFMachPortRef mp) { + __CFGenericValidateType(mp, __kCFMachPortTypeID); + return mp->_icallout; +} + +void CFMachPortSetInvalidationCallBack(CFMachPortRef mp, CFMachPortInvalidationCallBack callout) { + __CFGenericValidateType(mp, __kCFMachPortTypeID); + if (!__CFMachPortIsValid(mp) && NULL != callout) { + callout(mp, mp->_context.info); + } else { + mp->_icallout = callout; + } +} + +/* Returns the number of messages queued for a receive port. */ +CFIndex CFMachPortGetQueuedMessageCount(CFMachPortRef mp) { + CHECK_FOR_FORK(); + mach_port_status_t status; + mach_msg_type_number_t num = MACH_PORT_RECEIVE_STATUS_COUNT; + kern_return_t ret; + ret = mach_port_get_attributes(mach_task_self(), mp->_port, MACH_PORT_RECEIVE_STATUS, (mach_port_info_t)&status, &num); + return (KERN_SUCCESS != ret) ? 0 : status.mps_msgcount; +} + +static mach_port_t __CFMachPortGetPort(void *info) { + CFMachPortRef mp = (CFMachPortRef)info; + return mp->_port; +} + +static void *__CFMachPortPerform(void *msg, CFIndex size, CFAllocatorRef allocator, void *info) { + CHECK_FOR_FORK(); + CFMachPortRef mp = (CFMachPortRef)info; + void *context_info; + void (*context_release)(const void *); + __CFMachPortLock(mp); + if (!__CFMachPortIsValid(mp)) { + __CFMachPortUnlock(mp); + return NULL; + } + if (NULL != mp->_context.retain) { + context_info = (void *)mp->_context.retain(mp->_context.info); + context_release = mp->_context.release; + } else { + context_info = mp->_context.info; + context_release = NULL; + } + __CFMachPortUnlock(mp); + mp->_callout(mp, msg, size, mp->_context.info); + CHECK_FOR_FORK(); + if (context_release) { + context_release(context_info); + } + return NULL; +} + +CFRunLoopSourceRef CFMachPortCreateRunLoopSource(CFAllocatorRef allocator, CFMachPortRef mp, CFIndex order) { + CHECK_FOR_FORK(); + CFRunLoopSourceRef result = NULL; + __CFGenericValidateType(mp, __kCFMachPortTypeID); + __CFMachPortLock(mp); + if (!__CFMachPortIsValid(mp)) { + __CFMachPortUnlock(mp); + return NULL; + } + if (NULL == mp->_source) { + CFRunLoopSourceContext1 context; + context.version = 1; + context.info = (void *)mp; + context.retain = (const void *(*)(const void *))CFRetain; + context.release = (void (*)(const void *))CFRelease; + context.copyDescription = (CFStringRef (*)(const void *))__CFMachPortCopyDescription; + context.equal = (Boolean (*)(const void *, const void *))__CFMachPortEqual; + context.hash = (CFHashCode (*)(const void *))__CFMachPortHash; + context.getPort = __CFMachPortGetPort; + context.perform = __CFMachPortPerform; + mp->_source = CFRunLoopSourceCreate(allocator, order, (CFRunLoopSourceContext *)&context); + } + if (NULL != mp->_source) { + result = (CFRunLoopSourceRef)CFRetain(mp->_source); + } + __CFMachPortUnlock(mp); + return result; +} + +#endif
diff --git a/CoreFoundation/CFMachPort.h b/CoreFoundation/CFMachPort.h new file mode 100644 index 0000000..e977de3 --- /dev/null +++ b/CoreFoundation/CFMachPort.h
@@ -0,0 +1,78 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFMachPort.h + Copyright (c) 1998-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFMACHPORT__) +#define __COREFOUNDATION_CFMACHPORT__ 1 + +#if DEPLOYMENT_TARGET_MACOSX + +#include <CoreFoundation/CFRunLoop.h> +#include <mach/port.h> + +CF_EXTERN_C_BEGIN + +typedef struct __CFMachPort * CFMachPortRef; + +typedef struct { + CFIndex version; + void * info; + const void *(*retain)(const void *info); + void (*release)(const void *info); + CFStringRef (*copyDescription)(const void *info); +} CFMachPortContext; + +typedef void (*CFMachPortCallBack)(CFMachPortRef port, void *msg, CFIndex size, void *info); +typedef void (*CFMachPortInvalidationCallBack)(CFMachPortRef port, void *info); + +CF_EXPORT CFTypeID CFMachPortGetTypeID(void); + +CF_EXPORT CFMachPortRef CFMachPortCreate(CFAllocatorRef allocator, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo); +CF_EXPORT CFMachPortRef CFMachPortCreateWithPort(CFAllocatorRef allocator, mach_port_t portNum, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo); + +CF_EXPORT mach_port_t CFMachPortGetPort(CFMachPortRef port); +CF_EXPORT void CFMachPortGetContext(CFMachPortRef port, CFMachPortContext *context); +CF_EXPORT void CFMachPortInvalidate(CFMachPortRef port); +CF_EXPORT Boolean CFMachPortIsValid(CFMachPortRef port); +CF_EXPORT CFMachPortInvalidationCallBack CFMachPortGetInvalidationCallBack(CFMachPortRef port); +CF_EXPORT void CFMachPortSetInvalidationCallBack(CFMachPortRef port, CFMachPortInvalidationCallBack callout); + +CF_EXPORT CFRunLoopSourceRef CFMachPortCreateRunLoopSource(CFAllocatorRef allocator, CFMachPortRef port, CFIndex order); + +CF_EXTERN_C_END + +#endif /* DEPLOYMENT_TARGET_MACOSX */ +#endif /* ! __COREFOUNDATION_CFMACHPORT__ */ +
diff --git a/CoreFoundation/CFMessagePort.c b/CoreFoundation/CFMessagePort.c new file mode 100644 index 0000000..0e74abd --- /dev/null +++ b/CoreFoundation/CFMessagePort.c
@@ -0,0 +1,982 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFMessagePort.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#if DEPLOYMENT_TARGET_MACOSX + +#include <CoreFoundation/CFMessagePort.h> +#include <CoreFoundation/CFRunLoop.h> +#include <CoreFoundation/CFMachPort.h> +#include <CoreFoundation/CFDictionary.h> +#include <CoreFoundation/CFByteOrder.h> +#include <limits.h> +#include <unistd.h> +#include "CFInternal.h" +#include <mach/mach.h> +#include <mach/message.h> +#include <mach/mach_error.h> +#include <bootstrap_priv.h> +#include <math.h> +#include <mach/mach_time.h> +#include <dlfcn.h> + + +#define __kCFMessagePortMaxNameLengthMax 255 + +#if defined(BOOTSTRAP_MAX_NAME_LEN) + #define __kCFMessagePortMaxNameLength BOOTSTRAP_MAX_NAME_LEN +#else + #define __kCFMessagePortMaxNameLength 128 +#endif + +#if __kCFMessagePortMaxNameLengthMax < __kCFMessagePortMaxNameLength + #undef __kCFMessagePortMaxNameLength + #define __kCFMessagePortMaxNameLength __kCFMessagePortMaxNameLengthMax +#endif + +static CFSpinLock_t __CFAllMessagePortsLock = CFSpinLockInit; +static CFMutableDictionaryRef __CFAllLocalMessagePorts = NULL; +static CFMutableDictionaryRef __CFAllRemoteMessagePorts = NULL; + +struct __CFMessagePort { + CFRuntimeBase _base; + CFSpinLock_t _lock; + CFStringRef _name; + CFMachPortRef _port; /* immutable; invalidated */ + CFMutableDictionaryRef _replies; + int32_t _convCounter; + int32_t _perPID; /* zero if not per-pid, else pid */ + CFMachPortRef _replyPort; /* only used by remote port; immutable once created; invalidated */ + CFRunLoopSourceRef _source; /* only used by local port; immutable once created; invalidated */ + CFMessagePortInvalidationCallBack _icallout; + CFMessagePortCallBack _callout; /* only used by local port; immutable */ + CFMessagePortContext _context; /* not part of remote port; immutable; invalidated */ +}; + +/* Bit 0 in the base reserved bits is used for invalid state */ +/* Bit 1 of the base reserved bits is used for has-extra-port-refs state */ +/* Bit 2 of the base reserved bits is used for is-remote state */ +/* Bit 3 in the base reserved bits is used for is-deallocing state */ + +CF_INLINE Boolean __CFMessagePortIsValid(CFMessagePortRef ms) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 0, 0); +} + +CF_INLINE void __CFMessagePortSetValid(CFMessagePortRef ms) { + __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 0, 0, 1); +} + +CF_INLINE void __CFMessagePortUnsetValid(CFMessagePortRef ms) { + __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 0, 0, 0); +} + +CF_INLINE Boolean __CFMessagePortExtraMachRef(CFMessagePortRef ms) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 1, 1); +} + +CF_INLINE void __CFMessagePortSetExtraMachRef(CFMessagePortRef ms) { + __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 1, 1, 1); +} + +CF_INLINE void __CFMessagePortUnsetExtraMachRef(CFMessagePortRef ms) { + __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 1, 1, 0); +} + +CF_INLINE Boolean __CFMessagePortIsRemote(CFMessagePortRef ms) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 2, 2); +} + +CF_INLINE void __CFMessagePortSetRemote(CFMessagePortRef ms) { + __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 2, 2, 1); +} + +CF_INLINE void __CFMessagePortUnsetRemote(CFMessagePortRef ms) { + __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 2, 2, 0); +} + +CF_INLINE Boolean __CFMessagePortIsDeallocing(CFMessagePortRef ms) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 3, 3); +} + +CF_INLINE void __CFMessagePortSetIsDeallocing(CFMessagePortRef ms) { + __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 3, 3, 1); +} + +CF_INLINE void __CFMessagePortLock(CFMessagePortRef ms) { + __CFSpinLock(&(ms->_lock)); +} + +CF_INLINE void __CFMessagePortUnlock(CFMessagePortRef ms) { + __CFSpinUnlock(&(ms->_lock)); +} + +// Just a heuristic +#define __CFMessagePortMaxInlineBytes 4096*10 + +struct __CFMessagePortMachMsg0 { + int32_t msgid; + int32_t byteslen; + uint8_t bytes[__CFMessagePortMaxInlineBytes]; +}; + +struct __CFMessagePortMachMsg1 { + mach_msg_descriptor_t desc; + int32_t msgid; +}; + +struct __CFMessagePortMachMessage { + mach_msg_header_t head; + mach_msg_body_t body; + union { + struct __CFMessagePortMachMsg0 msg0; + struct __CFMessagePortMachMsg1 msg1; + } contents; +}; + +static struct __CFMessagePortMachMessage *__CFMessagePortCreateMessage(CFAllocatorRef allocator, bool reply, mach_port_t port, mach_port_t replyPort, int32_t convid, int32_t msgid, const uint8_t *bytes, int32_t byteslen) { + struct __CFMessagePortMachMessage *msg; + int32_t size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t); + if (byteslen < __CFMessagePortMaxInlineBytes) { + size += 2 * sizeof(int32_t) + ((byteslen + 3) & ~0x3); + } else { + size += sizeof(struct __CFMessagePortMachMsg1); + } + msg = (struct __CFMessagePortMachMessage*)CFAllocatorAllocate(allocator, size, 0); + msg->head.msgh_id = convid; + msg->head.msgh_size = size; + msg->head.msgh_remote_port = port; + msg->head.msgh_local_port = replyPort; + msg->head.msgh_reserved = 0; +// msg->head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, (replyPort ? MACH_MSG_TYPE_MAKE_SEND : 0)); + msg->head.msgh_bits = MACH_MSGH_BITS((reply ? MACH_MSG_TYPE_MOVE_SEND_ONCE : MACH_MSG_TYPE_COPY_SEND), (MACH_PORT_NULL != replyPort ? MACH_MSG_TYPE_MAKE_SEND_ONCE : 0)); + if (byteslen < __CFMessagePortMaxInlineBytes) { + msg->body.msgh_descriptor_count = 0; + msg->contents.msg0.msgid = CFSwapInt32HostToLittle(msgid); + msg->contents.msg0.byteslen = CFSwapInt32HostToLittle(byteslen); + if (NULL != bytes && 0 < byteslen) { + memmove(msg->contents.msg0.bytes, bytes, byteslen); + } + memset(msg->contents.msg0.bytes + byteslen, 0, ((byteslen + 3) & ~0x3) - byteslen); + } else { + msg->head.msgh_bits |= MACH_MSGH_BITS_COMPLEX; + msg->body.msgh_descriptor_count = 1; + msg->contents.msg1.desc.out_of_line.deallocate = false; + msg->contents.msg1.desc.out_of_line.copy = MACH_MSG_VIRTUAL_COPY; + msg->contents.msg1.desc.out_of_line.address = (void *)bytes; + msg->contents.msg1.desc.out_of_line.size = byteslen; + msg->contents.msg1.desc.out_of_line.type = MACH_MSG_OOL_DESCRIPTOR; + msg->contents.msg1.msgid = CFSwapInt32HostToLittle(msgid); + } + return msg; +} + +static CFStringRef __CFMessagePortCopyDescription(CFTypeRef cf) { + CFMessagePortRef ms = (CFMessagePortRef)cf; + CFStringRef result; + const char *locked; + CFStringRef contextDesc = NULL; + locked = ms->_lock ? "Yes" : "No"; + if (!__CFMessagePortIsRemote(ms)) { + if (NULL != ms->_context.info && NULL != ms->_context.copyDescription) { + contextDesc = ms->_context.copyDescription(ms->_context.info); + } + if (NULL == contextDesc) { + contextDesc = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFMessagePort context %p>"), ms->_context.info); + } + result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFMessagePort %p [%p]>{locked = %s, valid = %s, remote = %s, name = %@}"), cf, CFGetAllocator(ms), locked, (__CFMessagePortIsValid(ms) ? "Yes" : "No"), (__CFMessagePortIsRemote(ms) ? "Yes" : "No"), ms->_name); + } else { + void *addr = (void*)ms->_callout; + Dl_info info; + const char *name = (dladdr(addr, &info) && info.dli_saddr == addr && info.dli_sname) ? info.dli_sname : "???"; + result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFMessagePort %p [%p]>{locked = %s, valid = %s, remote = %s, name = %@, source = %p, callout = %s (%p), context = %@}"), cf, CFGetAllocator(ms), locked, (__CFMessagePortIsValid(ms) ? "Yes" : "No"), (__CFMessagePortIsRemote(ms) ? "Yes" : "No"), ms->_name, ms->_source, name, addr, (NULL != contextDesc ? contextDesc : CFSTR("<no description>"))); + } + if (NULL != contextDesc) { + CFRelease(contextDesc); + } + return result; +} + +static void __CFMessagePortDeallocate(CFTypeRef cf) { + CFMessagePortRef ms = (CFMessagePortRef)cf; + __CFMessagePortSetIsDeallocing(ms); + CFMessagePortInvalidate(ms); + // Delay cleanup of _replies until here so that invalidation during + // SendRequest does not cause _replies to disappear out from under that function. + if (NULL != ms->_replies) { + CFRelease(ms->_replies); + } + if (NULL != ms->_name) { + CFRelease(ms->_name); + } + if (NULL != ms->_port) { + if (__CFMessagePortExtraMachRef(ms)) { + mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(ms->_port), MACH_PORT_RIGHT_SEND, -1); + mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(ms->_port), MACH_PORT_RIGHT_RECEIVE, -1); + } + CFMachPortInvalidate(ms->_port); + CFRelease(ms->_port); + } + + // A remote message port for a local message port in the same process will get the + // same mach port, and the remote port will keep the mach port from being torn down, + // thus keeping the remote port from getting any sort of death notification and + // auto-invalidating; so we manually implement the 'auto-invalidation' here by + // tickling each remote port to check its state after any message port is destroyed, + // but most importantly after local message ports are destroyed. + __CFSpinLock(&__CFAllMessagePortsLock); + CFMessagePortRef *remotePorts = NULL; + CFIndex cnt = 0; + if (NULL != __CFAllRemoteMessagePorts) { + cnt = CFDictionaryGetCount(__CFAllRemoteMessagePorts); + remotePorts = (CFMessagePortRef*)CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(CFMessagePortRef), __kCFAllocatorGCScannedMemory); + CFDictionaryGetKeysAndValues(__CFAllRemoteMessagePorts, NULL, (const void **)remotePorts); + for (CFIndex idx = 0; idx < cnt; idx++) { + CFRetain(remotePorts[idx]); + } + } + __CFSpinUnlock(&__CFAllMessagePortsLock); + if (remotePorts) { + for (CFIndex idx = 0; idx < cnt; idx++) { + // as a side-effect, this will auto-invalidate the CFMessagePort if the CFMachPort is invalid + CFMessagePortIsValid(remotePorts[idx]); + CFRelease(remotePorts[idx]); + } + CFAllocatorDeallocate(kCFAllocatorSystemDefault, remotePorts); + } +} + +static CFTypeID __kCFMessagePortTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFMessagePortClass = { + 0, + "CFMessagePort", + NULL, // init + NULL, // copy + __CFMessagePortDeallocate, + NULL, + NULL, + NULL, // + __CFMessagePortCopyDescription +}; + +__private_extern__ void __CFMessagePortInitialize(void) { + __kCFMessagePortTypeID = _CFRuntimeRegisterClass(&__CFMessagePortClass); +} + +CFTypeID CFMessagePortGetTypeID(void) { + return __kCFMessagePortTypeID; +} + +static CFStringRef __CFMessagePortSanitizeStringName(CFAllocatorRef allocator, CFStringRef name, uint8_t **utfnamep, CFIndex *utfnamelenp) { + uint8_t *utfname; + CFIndex utflen; + CFStringRef result; + utfname = (uint8_t*)CFAllocatorAllocate(allocator, __kCFMessagePortMaxNameLength + 1, 0); + CFStringGetBytes(name, CFRangeMake(0, CFStringGetLength(name)), kCFStringEncodingUTF8, 0, false, utfname, __kCFMessagePortMaxNameLength, &utflen); + utfname[utflen] = '\0'; + /* A new string is created, because the original string may have been + truncated to the max length, and we want the string name to definitely + match the raw UTF-8 chunk that has been created. Also, this is useful + to get a constant string in case the original name string was mutable. */ + result = CFStringCreateWithBytes(allocator, utfname, utflen, kCFStringEncodingUTF8, false); + if (NULL != utfnamep) { + *utfnamep = utfname; + } else { + CFAllocatorDeallocate(allocator, utfname); + } + if (NULL != utfnamelenp) { + *utfnamelenp = utflen; + } + return result; +} + +static void __CFMessagePortDummyCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) { + // not supposed to be implemented +} + +static void __CFMessagePortInvalidationCallBack(CFMachPortRef port, void *info) { + // info has been setup as the CFMessagePort owning the CFMachPort + CFMessagePortInvalidate((CFMessagePortRef)info); +} + +static CFMessagePortRef __CFMessagePortCreateLocal(CFAllocatorRef allocator, CFStringRef name, CFMessagePortCallBack callout, CFMessagePortContext *context, Boolean *shouldFreeInfo, Boolean perPID) { + CFMessagePortRef memory; + uint8_t *utfname = NULL; + + if (shouldFreeInfo) *shouldFreeInfo = true; + if (NULL != name) { + name = __CFMessagePortSanitizeStringName(allocator, name, &utfname, NULL); + } + __CFSpinLock(&__CFAllMessagePortsLock); + if (!perPID && NULL != name) { + CFMessagePortRef existing; + if (NULL != __CFAllLocalMessagePorts && CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts, name, (const void **)&existing)) { + CFRetain(existing); + __CFSpinUnlock(&__CFAllMessagePortsLock); + CFRelease(name); + CFAllocatorDeallocate(allocator, utfname); + return (CFMessagePortRef)(existing); + } + } + __CFSpinUnlock(&__CFAllMessagePortsLock); + CFIndex size = sizeof(struct __CFMessagePort) - sizeof(CFRuntimeBase); + memory = (CFMessagePortRef)_CFRuntimeCreateInstance(allocator, __kCFMessagePortTypeID, size, NULL); + if (NULL == memory) { + if (NULL != name) { + CFRelease(name); + } + CFAllocatorDeallocate(allocator, utfname); + return NULL; + } + __CFMessagePortUnsetValid(memory); + __CFMessagePortUnsetExtraMachRef(memory); + __CFMessagePortUnsetRemote(memory); + memory->_lock = CFSpinLockInit; + memory->_name = name; + memory->_port = NULL; + memory->_replies = NULL; + memory->_convCounter = 0; + memory->_perPID = perPID ? getpid() : 0; // actual value not terribly useful for local ports + memory->_replyPort = NULL; + memory->_source = NULL; + memory->_icallout = NULL; + memory->_callout = callout; + memory->_context.info = NULL; + memory->_context.retain = NULL; + memory->_context.release = NULL; + memory->_context.copyDescription = NULL; + + if (NULL != name) { + CFMachPortRef native = NULL; + kern_return_t ret; + mach_port_t bs, mp; + task_get_bootstrap_port(mach_task_self(), &bs); + if (!perPID) { + ret = bootstrap_check_in(bs, (char *)utfname, &mp); /* If we're started by launchd or the old mach_init */ + if (ret == KERN_SUCCESS) { + ret = mach_port_insert_right(mach_task_self(), mp, mp, MACH_MSG_TYPE_MAKE_SEND); + if (KERN_SUCCESS == ret) { + CFMachPortContext ctx = {0, memory, NULL, NULL, NULL}; + native = CFMachPortCreateWithPort(allocator, mp, __CFMessagePortDummyCallback, &ctx, NULL); + __CFMessagePortSetExtraMachRef(memory); + } else { + CFLog(kCFLogLevelWarning, CFSTR("*** CFMessagePort: mach_port_insert_member() after bootstrap_check_in(): failed %d (0x%x) '%s', port = 0x%x, name = '%s'"), ret, ret, bootstrap_strerror(ret), mp, utfname); + mach_port_destroy(mach_task_self(), mp); + CFAllocatorDeallocate(allocator, utfname); + // name is released by deallocation + CFRelease(memory); + return NULL; + } + } + } + if (!native) { + CFMachPortContext ctx = {0, memory, NULL, NULL, NULL}; + native = CFMachPortCreate(allocator, __CFMessagePortDummyCallback, &ctx, NULL); + mp = CFMachPortGetPort(native); + ret = bootstrap_register2(bs, (char *)utfname, mp, perPID ? BOOTSTRAP_PER_PID_SERVICE : 0); + if (ret != KERN_SUCCESS) { + CFLog(kCFLogLevelWarning, CFSTR("*** CFMessagePort: bootstrap_register(): failed %d (0x%x) '%s', port = 0x%x, name = '%s'\nSee /usr/include/servers/bootstrap_defs.h for the error codes."), ret, ret, bootstrap_strerror(ret), mp, utfname); + CFMachPortInvalidate(native); + CFRelease(native); + CFAllocatorDeallocate(allocator, utfname); + // name is released by deallocation + CFRelease(memory); + return NULL; + } + } + CFMachPortSetInvalidationCallBack(native, __CFMessagePortInvalidationCallBack); + memory->_port = native; + } + + CFAllocatorDeallocate(allocator, utfname); + __CFMessagePortSetValid(memory); + if (NULL != context) { + memmove(&memory->_context, context, sizeof(CFMessagePortContext)); + memory->_context.info = context->retain ? (void *)context->retain(context->info) : context->info; + } + __CFSpinLock(&__CFAllMessagePortsLock); + if (!perPID && NULL != name) { + CFMessagePortRef existing; + if (NULL != __CFAllLocalMessagePorts && CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts, name, (const void **)&existing)) { + CFRetain(existing); + __CFSpinUnlock(&__CFAllMessagePortsLock); + CFRelease(memory); + return (CFMessagePortRef)(existing); + } + if (NULL == __CFAllLocalMessagePorts) { + __CFAllLocalMessagePorts = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL); + } + CFDictionaryAddValue(__CFAllLocalMessagePorts, name, memory); + } + __CFSpinUnlock(&__CFAllMessagePortsLock); + if (shouldFreeInfo) *shouldFreeInfo = false; + return memory; +} + +CFMessagePortRef CFMessagePortCreateLocal(CFAllocatorRef allocator, CFStringRef name, CFMessagePortCallBack callout, CFMessagePortContext *context, Boolean *shouldFreeInfo) { + return __CFMessagePortCreateLocal(allocator, name, callout, context, shouldFreeInfo, false); +} + +CFMessagePortRef CFMessagePortCreatePerProcessLocal(CFAllocatorRef allocator, CFStringRef name, CFMessagePortCallBack callout, CFMessagePortContext *context, Boolean *shouldFreeInfo) { + return __CFMessagePortCreateLocal(allocator, name, callout, context, shouldFreeInfo, true); +} + +static CFMessagePortRef __CFMessagePortCreateRemote(CFAllocatorRef allocator, CFStringRef name, Boolean perPID, CFIndex pid) { + CFMessagePortRef memory; + CFMachPortRef native; + CFMachPortContext ctx; + uint8_t *utfname = NULL; + CFIndex size; + mach_port_t bp, port; + kern_return_t ret; + + name = __CFMessagePortSanitizeStringName(allocator, name, &utfname, NULL); + if (NULL == name) { + return NULL; + } + __CFSpinLock(&__CFAllMessagePortsLock); + if (!perPID && NULL != name) { + CFMessagePortRef existing; + if (NULL != __CFAllRemoteMessagePorts && CFDictionaryGetValueIfPresent(__CFAllRemoteMessagePorts, name, (const void **)&existing)) { + CFRetain(existing); + __CFSpinUnlock(&__CFAllMessagePortsLock); + CFRelease(name); + CFAllocatorDeallocate(allocator, utfname); + return (CFMessagePortRef)(existing); + } + } + __CFSpinUnlock(&__CFAllMessagePortsLock); + size = sizeof(struct __CFMessagePort) - sizeof(CFMessagePortContext) - sizeof(CFRuntimeBase); + memory = (CFMessagePortRef)_CFRuntimeCreateInstance(allocator, __kCFMessagePortTypeID, size, NULL); + if (NULL == memory) { + if (NULL != name) { + CFRelease(name); + } + CFAllocatorDeallocate(allocator, utfname); + return NULL; + } + __CFMessagePortUnsetValid(memory); + __CFMessagePortUnsetExtraMachRef(memory); + __CFMessagePortSetRemote(memory); + memory->_lock = CFSpinLockInit; + memory->_name = name; + memory->_port = NULL; + memory->_replies = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, NULL); + memory->_convCounter = 0; + memory->_perPID = perPID ? pid : 0; + memory->_replyPort = NULL; + memory->_source = NULL; + memory->_icallout = NULL; + memory->_callout = NULL; + ctx.version = 0; + ctx.info = memory; + ctx.retain = NULL; + ctx.release = NULL; + ctx.copyDescription = NULL; + task_get_bootstrap_port(mach_task_self(), &bp); + ret = bootstrap_look_up2(bp, (char *)utfname, &port, perPID ? (pid_t)pid : 0, perPID ? BOOTSTRAP_PER_PID_SERVICE : 0); + native = (KERN_SUCCESS == ret) ? CFMachPortCreateWithPort(allocator, port, __CFMessagePortDummyCallback, &ctx, NULL) : NULL; + CFAllocatorDeallocate(allocator, utfname); + if (NULL == native) { + // name is released by deallocation + CFRelease(memory); + return NULL; + } + memory->_port = native; + CFMachPortSetInvalidationCallBack(native, __CFMessagePortInvalidationCallBack); + __CFMessagePortSetValid(memory); + __CFSpinLock(&__CFAllMessagePortsLock); + if (!perPID && NULL != name) { + CFMessagePortRef existing; + if (NULL != __CFAllRemoteMessagePorts && CFDictionaryGetValueIfPresent(__CFAllRemoteMessagePorts, name, (const void **)&existing)) { + CFRetain(existing); + __CFSpinUnlock(&__CFAllMessagePortsLock); + CFRelease(memory); + return (CFMessagePortRef)(existing); + } + if (NULL == __CFAllRemoteMessagePorts) { + __CFAllRemoteMessagePorts = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL); + } + CFDictionaryAddValue(__CFAllRemoteMessagePorts, name, memory); + } + __CFSpinUnlock(&__CFAllMessagePortsLock); + return (CFMessagePortRef)memory; +} + +CFMessagePortRef CFMessagePortCreateRemote(CFAllocatorRef allocator, CFStringRef name) { + return __CFMessagePortCreateRemote(allocator, name, false, 0); +} + +CFMessagePortRef CFMessagePortCreatePerProcessRemote(CFAllocatorRef allocator, CFStringRef name, CFIndex pid) { + return __CFMessagePortCreateRemote(allocator, name, true, pid); +} + +Boolean CFMessagePortIsRemote(CFMessagePortRef ms) { + __CFGenericValidateType(ms, __kCFMessagePortTypeID); + return __CFMessagePortIsRemote(ms); +} + +CFStringRef CFMessagePortGetName(CFMessagePortRef ms) { + __CFGenericValidateType(ms, __kCFMessagePortTypeID); + return ms->_name; +} + +Boolean CFMessagePortSetName(CFMessagePortRef ms, CFStringRef name) { + CFAllocatorRef allocator = CFGetAllocator(ms); + uint8_t *utfname = NULL; + + __CFGenericValidateType(ms, __kCFMessagePortTypeID); + if (ms->_perPID || __CFMessagePortIsRemote(ms)) return false; + name = __CFMessagePortSanitizeStringName(allocator, name, &utfname, NULL); + if (NULL == name) { + return false; + } + __CFSpinLock(&__CFAllMessagePortsLock); + if (NULL != name) { + CFMessagePortRef existing; + if (NULL != __CFAllLocalMessagePorts && CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts, name, (const void **)&existing)) { + __CFSpinUnlock(&__CFAllMessagePortsLock); + CFRelease(name); + CFAllocatorDeallocate(allocator, utfname); + return false; + } + } + __CFSpinUnlock(&__CFAllMessagePortsLock); + + if (NULL != name && (NULL == ms->_name || !CFEqual(ms->_name, name))) { + CFMachPortRef oldPort = ms->_port; + CFMachPortRef native = NULL; + kern_return_t ret; + mach_port_t bs, mp; + task_get_bootstrap_port(mach_task_self(), &bs); + ret = bootstrap_check_in(bs, (char *)utfname, &mp); /* If we're started by launchd or the old mach_init */ + if (ret == KERN_SUCCESS) { + ret = mach_port_insert_right(mach_task_self(), mp, mp, MACH_MSG_TYPE_MAKE_SEND); + if (KERN_SUCCESS == ret) { + CFMachPortContext ctx = {0, ms, NULL, NULL, NULL}; + native = CFMachPortCreateWithPort(allocator, mp, __CFMessagePortDummyCallback, &ctx, NULL); + __CFMessagePortSetExtraMachRef(ms); + } else { + mach_port_destroy(mach_task_self(), mp); + CFAllocatorDeallocate(allocator, utfname); + CFRelease(name); + return false; + } + } + if (!native) { + CFMachPortContext ctx = {0, ms, NULL, NULL, NULL}; + native = CFMachPortCreate(allocator, __CFMessagePortDummyCallback, &ctx, NULL); + mp = CFMachPortGetPort(native); + ret = bootstrap_register2(bs, (char *)utfname, mp, 0); + if (ret != KERN_SUCCESS) { + CFLog(kCFLogLevelWarning, CFSTR("*** CFMessagePort: bootstrap_register(): failed %d (0x%x) '%s', port = 0x%x, name = '%s'\nSee /usr/include/servers/bootstrap_defs.h for the error codes."), ret, ret, bootstrap_strerror(ret), mp, utfname); + CFMachPortInvalidate(native); + CFRelease(native); + CFAllocatorDeallocate(allocator, utfname); + CFRelease(name); + return false; + } + } + CFMachPortSetInvalidationCallBack(native, __CFMessagePortInvalidationCallBack); + ms->_port = native; + if (NULL != oldPort && oldPort != native) { + if (__CFMessagePortExtraMachRef(ms)) { + mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(oldPort), MACH_PORT_RIGHT_SEND, -1); + mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(oldPort), MACH_PORT_RIGHT_RECEIVE, -1); + } + CFMachPortInvalidate(oldPort); + CFRelease(oldPort); + } + __CFSpinLock(&__CFAllMessagePortsLock); + // This relocking without checking to see if something else has grabbed + // that name in the cache is rather suspect, but what would that even + // mean has happened? We'd expect the bootstrap_* calls above to have + // failed for this one and not gotten this far, or failed for all of the + // other simultaneous attempts to get the name (and having succeeded for + // this one, gotten here). So we're not going to try very hard here + // with the thread-safety. + if (NULL == __CFAllLocalMessagePorts) { + __CFAllLocalMessagePorts = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL); + } + if (NULL != ms->_name) { + CFDictionaryRemoveValue(__CFAllLocalMessagePorts, ms->_name); + CFRelease(ms->_name); + } + ms->_name = name; + CFDictionaryAddValue(__CFAllLocalMessagePorts, name, ms); + __CFSpinUnlock(&__CFAllMessagePortsLock); + } + + CFAllocatorDeallocate(allocator, utfname); + return true; +} + +void CFMessagePortGetContext(CFMessagePortRef ms, CFMessagePortContext *context) { + __CFGenericValidateType(ms, __kCFMessagePortTypeID); +//#warning CF: assert that this is a local port + CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__); + memmove(context, &ms->_context, sizeof(CFMessagePortContext)); +} + +void CFMessagePortInvalidate(CFMessagePortRef ms) { + __CFGenericValidateType(ms, __kCFMessagePortTypeID); + if (!__CFMessagePortIsDeallocing(ms)) { + CFRetain(ms); + } + __CFMessagePortLock(ms); + if (__CFMessagePortIsValid(ms)) { + CFMessagePortInvalidationCallBack callout = ms->_icallout; + CFRunLoopSourceRef source = ms->_source; + CFMachPortRef replyPort = ms->_replyPort; + CFMachPortRef port = ms->_port; + CFStringRef name = ms->_name; + void *info = NULL; + + __CFMessagePortUnsetValid(ms); + if (!__CFMessagePortIsRemote(ms)) { + info = ms->_context.info; + ms->_context.info = NULL; + } + ms->_source = NULL; + ms->_replyPort = NULL; + __CFMessagePortUnlock(ms); + + __CFSpinLock(&__CFAllMessagePortsLock); + if (0 == ms->_perPID && NULL != (__CFMessagePortIsRemote(ms) ? __CFAllRemoteMessagePorts : __CFAllLocalMessagePorts)) { + CFDictionaryRemoveValue(__CFMessagePortIsRemote(ms) ? __CFAllRemoteMessagePorts : __CFAllLocalMessagePorts, name); + } + __CFSpinUnlock(&__CFAllMessagePortsLock); + if (NULL != callout) { + callout(ms, info); + } + // We already know we're going invalid, don't need this callback + // anymore; plus, this solves a reentrancy deadlock; also, this + // must be done before the deallocate of the Mach port, to + // avoid a race between the notification message which could be + // handled in another thread, and this NULL'ing out. + CFMachPortSetInvalidationCallBack(port, NULL); + // For hashing and equality purposes, cannot get rid of _port here + if (!__CFMessagePortIsRemote(ms) && NULL != ms->_context.release) { + ms->_context.release(info); + } + if (NULL != source) { + CFRunLoopSourceInvalidate(source); + CFRelease(source); + } + if (NULL != replyPort) { + CFMachPortInvalidate(replyPort); + CFRelease(replyPort); + } + if (__CFMessagePortIsRemote(ms)) { + // Get rid of our extra ref on the Mach port gotten from bs server + mach_port_deallocate(mach_task_self(), CFMachPortGetPort(port)); + } + } else { + __CFMessagePortUnlock(ms); + } + if (!__CFMessagePortIsDeallocing(ms)) { + CFRelease(ms); + } +} + +Boolean CFMessagePortIsValid(CFMessagePortRef ms) { + __CFGenericValidateType(ms, __kCFMessagePortTypeID); + if (!__CFMessagePortIsValid(ms)) return false; + if (NULL != ms->_port && !CFMachPortIsValid(ms->_port)) { + CFMessagePortInvalidate(ms); + return false; + } + if (NULL != ms->_replyPort && !CFMachPortIsValid(ms->_replyPort)) { + CFMessagePortInvalidate(ms); + return false; + } + if (NULL != ms->_source && !CFRunLoopSourceIsValid(ms->_source)) { + CFMessagePortInvalidate(ms); + return false; + } + return true; +} + +CFMessagePortInvalidationCallBack CFMessagePortGetInvalidationCallBack(CFMessagePortRef ms) { + __CFGenericValidateType(ms, __kCFMessagePortTypeID); + return ms->_icallout; +} + +void CFMessagePortSetInvalidationCallBack(CFMessagePortRef ms, CFMessagePortInvalidationCallBack callout) { + __CFGenericValidateType(ms, __kCFMessagePortTypeID); + if (!__CFMessagePortIsValid(ms) && NULL != callout) { + callout(ms, ms->_context.info); + } else { + ms->_icallout = callout; + } +} + +static void __CFMessagePortReplyCallBack(CFMachPortRef port, void *msg, CFIndex size, void *info) { + CFMessagePortRef ms = (CFMessagePortRef)info; + struct __CFMessagePortMachMessage *msgp = (struct __CFMessagePortMachMessage *)msg; + struct __CFMessagePortMachMessage *replymsg; + __CFMessagePortLock(ms); + if (!__CFMessagePortIsValid(ms)) { + __CFMessagePortUnlock(ms); + return; + } +// assert: (int32_t)msgp->head.msgh_id < 0 + if (CFDictionaryContainsKey(ms->_replies, (void *)(uintptr_t)msgp->head.msgh_id)) { + CFDataRef reply = NULL; + replymsg = (struct __CFMessagePortMachMessage *)msg; + if (0 == replymsg->body.msgh_descriptor_count) { + int32_t byteslen = CFSwapInt32LittleToHost(replymsg->contents.msg0.byteslen); + if (0 <= byteslen) { + reply = CFDataCreate(kCFAllocatorSystemDefault, replymsg->contents.msg0.bytes, byteslen); + } else { + reply = (CFDataRef)((void *)~0); // means NULL data + } + } else { +//#warning CF: should create a no-copy data here that has a custom VM-freeing allocator, and not vm_dealloc here + reply = CFDataCreate(kCFAllocatorSystemDefault, (const UInt8*)replymsg->contents.msg1.desc.out_of_line.address, replymsg->contents.msg1.desc.out_of_line.size); + vm_deallocate(mach_task_self(), (vm_address_t)replymsg->contents.msg1.desc.out_of_line.address, replymsg->contents.msg1.desc.out_of_line.size); + } + CFDictionarySetValue(ms->_replies, (void *)(uintptr_t)msgp->head.msgh_id, (void *)reply); + } else { /* discard message */ + if (1 == msgp->body.msgh_descriptor_count) { + vm_deallocate(mach_task_self(), (vm_address_t)msgp->contents.msg1.desc.out_of_line.address, msgp->contents.msg1.desc.out_of_line.size); + } + } + __CFMessagePortUnlock(ms); +} + +SInt32 CFMessagePortSendRequest(CFMessagePortRef remote, SInt32 msgid, CFDataRef data, CFTimeInterval sendTimeout, CFTimeInterval rcvTimeout, CFStringRef replyMode, CFDataRef *returnDatap) { + struct __CFMessagePortMachMessage *sendmsg; + CFRunLoopRef currentRL = CFRunLoopGetCurrent(); + CFRunLoopSourceRef source = NULL; + CFDataRef reply = NULL; + int64_t termTSR; + uint32_t sendOpts = 0, sendTimeOut = 0; + int32_t desiredReply; + Boolean didRegister = false; + kern_return_t ret; + + //#warning CF: This should be an assert + // if (!__CFMessagePortIsRemote(remote)) return -999; + if (!__CFMessagePortIsValid(remote)) return kCFMessagePortIsInvalid; + __CFMessagePortLock(remote); + if (NULL == remote->_replyPort) { + CFMachPortContext context; + context.version = 0; + context.info = remote; + context.retain = (const void *(*)(const void *))CFRetain; + context.release = (void (*)(const void *))CFRelease; + context.copyDescription = (CFStringRef (*)(const void *))__CFMessagePortCopyDescription; + remote->_replyPort = CFMachPortCreate(CFGetAllocator(remote), __CFMessagePortReplyCallBack, &context, NULL); + } + remote->_convCounter++; + desiredReply = -remote->_convCounter; + sendmsg = __CFMessagePortCreateMessage(kCFAllocatorSystemDefault, false, CFMachPortGetPort(remote->_port), (replyMode != NULL ? CFMachPortGetPort(remote->_replyPort) : MACH_PORT_NULL), -desiredReply, msgid, (data ? CFDataGetBytePtr(data) : NULL), (data ? CFDataGetLength(data) : 0)); + __CFMessagePortUnlock(remote); + if (replyMode != NULL) { + CFDictionarySetValue(remote->_replies, (void *)(uintptr_t)desiredReply, NULL); + source = CFMachPortCreateRunLoopSource(CFGetAllocator(remote), remote->_replyPort, -100); + didRegister = !CFRunLoopContainsSource(currentRL, source, replyMode); + if (didRegister) { + CFRunLoopAddSource(currentRL, source, replyMode); + } + } + if (sendTimeout < 10.0*86400) { + // anything more than 10 days is no timeout! + sendOpts = MACH_SEND_TIMEOUT; + sendTimeout *= 1000.0; + if (sendTimeout < 1.0) sendTimeout = 0.0; + sendTimeOut = floor(sendTimeout); + } + ret = mach_msg((mach_msg_header_t *)sendmsg, MACH_SEND_MSG|sendOpts, sendmsg->head.msgh_size, 0, MACH_PORT_NULL, sendTimeOut, MACH_PORT_NULL); + if (KERN_SUCCESS != ret) { + // need to deallocate the send-once right that might have been created + if (replyMode != NULL) mach_port_deallocate(mach_task_self(), ((mach_msg_header_t *)sendmsg)->msgh_local_port); + if (didRegister) { + CFRunLoopRemoveSource(currentRL, source, replyMode); + } + if (source) CFRelease(source); + CFAllocatorDeallocate(kCFAllocatorSystemDefault, sendmsg); + return (MACH_SEND_TIMED_OUT == ret) ? kCFMessagePortSendTimeout : kCFMessagePortTransportError; + } + CFAllocatorDeallocate(kCFAllocatorSystemDefault, sendmsg); + if (replyMode == NULL) { + return kCFMessagePortSuccess; + } + CFRetain(remote); // retain during run loop to avoid invalidation causing freeing + _CFMachPortInstallNotifyPort(currentRL, replyMode); + termTSR = mach_absolute_time() + __CFTimeIntervalToTSR(rcvTimeout); + for (;;) { + CFRunLoopRunInMode(replyMode, __CFTSRToTimeInterval(termTSR - mach_absolute_time()), true); + // warning: what, if anything, should be done if remote is now invalid? + reply = (CFDataRef)CFDictionaryGetValue(remote->_replies, (void *)(uintptr_t)desiredReply); + if (NULL != reply || termTSR < (int64_t)mach_absolute_time()) { + break; + } + if (!CFMessagePortIsValid(remote)) { + // no reason that reply port alone should go invalid so we don't check for that + break; + } + } + // Should we uninstall the notify port? A complex question... + if (didRegister) { + CFRunLoopRemoveSource(currentRL, source, replyMode); + } + if (source) CFRelease(source); + if (NULL == reply) { + CFDictionaryRemoveValue(remote->_replies, (void *)(uintptr_t)desiredReply); + CFRelease(remote); + return CFMessagePortIsValid(remote) ? kCFMessagePortReceiveTimeout : -5; + } + if (NULL != returnDatap) { + *returnDatap = ((void *)~0 == reply) ? NULL : reply; + } else if ((void *)~0 != reply) { + CFRelease(reply); + } + CFDictionaryRemoveValue(remote->_replies, (void *)(uintptr_t)desiredReply); + CFRelease(remote); + return kCFMessagePortSuccess; +} + +static mach_port_t __CFMessagePortGetPort(void *info) { + CFMessagePortRef ms = (CFMessagePortRef)info; + if (!ms->_port) CFLog(kCFLogLevelWarning, CFSTR("*** Warning: A local CFMessagePort (%p) is being put in a run loop, but it has not been named yet, so this will be a no-op and no messages are going to be received, even if named later."), info); + return ms->_port ? CFMachPortGetPort(ms->_port) : MACH_PORT_NULL; +} + +static void *__CFMessagePortPerform(void *msg, CFIndex size, CFAllocatorRef allocator, void *info) { + CFMessagePortRef ms = (CFMessagePortRef)info; + struct __CFMessagePortMachMessage *msgp = (struct __CFMessagePortMachMessage *)msg; + struct __CFMessagePortMachMessage *replymsg; + void *context_info; + void (*context_release)(const void *); + CFDataRef returnData, data = NULL; + void *return_bytes = NULL; + CFIndex return_len = 0; + int32_t msgid; + + __CFMessagePortLock(ms); + if (!__CFMessagePortIsValid(ms)) { + __CFMessagePortUnlock(ms); + return NULL; + } +// assert: 0 < (int32_t)msgp->head.msgh_id + if (NULL != ms->_context.retain) { + context_info = (void *)ms->_context.retain(ms->_context.info); + context_release = ms->_context.release; + } else { + context_info = ms->_context.info; + context_release = NULL; + } + __CFMessagePortUnlock(ms); + /* Create no-copy, no-free-bytes wrapper CFData */ + if (0 == msgp->body.msgh_descriptor_count) { + int32_t byteslen = CFSwapInt32LittleToHost(msgp->contents.msg0.byteslen); + msgid = CFSwapInt32LittleToHost(msgp->contents.msg0.msgid); + if (0 <= byteslen) { + data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, msgp->contents.msg0.bytes, byteslen, kCFAllocatorNull); + } + } else { + msgid = CFSwapInt32LittleToHost(msgp->contents.msg1.msgid); + data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (const UInt8*)msgp->contents.msg1.desc.out_of_line.address, msgp->contents.msg1.desc.out_of_line.size, kCFAllocatorNull); + } + returnData = ms->_callout(ms, msgid, data, context_info); + /* Now, returnData could be (1) NULL, (2) an ordinary data < MAX_INLINE, + (3) ordinary data >= MAX_INLINE, (4) a no-copy data < MAX_INLINE, + (5) a no-copy data >= MAX_INLINE. In cases (2) and (4), we send the return + bytes inline in the Mach message, so can release the returnData object + here. In cases (3) and (5), we'll send the data out-of-line, we need to + create a copy of the memory, which we'll have the kernel autodeallocate + for us on send. In case (4) also, the bytes in the return data may be part + of the bytes in "data" that we sent into the callout, so if the incoming + data was received out of line, we wouldn't be able to clean up the out-of-line + wad until the message was sent either, if we didn't make the copy. */ + if (NULL != returnData) { + return_len = CFDataGetLength(returnData); + if (return_len < __CFMessagePortMaxInlineBytes) { + return_bytes = (void *)CFDataGetBytePtr(returnData); + } else { + return_bytes = NULL; + vm_allocate(mach_task_self(), (vm_address_t *)&return_bytes, return_len, VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG)); + /* vm_copy would only be a win here if the source address + is page aligned; it is a lose in all other cases, since + the kernel will just do the memmove for us (but not in + as simple a way). */ + memmove(return_bytes, CFDataGetBytePtr(returnData), return_len); + } + } + replymsg = __CFMessagePortCreateMessage(allocator, true, msgp->head.msgh_remote_port, MACH_PORT_NULL, -1 * (int32_t)msgp->head.msgh_id, msgid, (const uint8_t*)return_bytes, return_len); + if (1 == replymsg->body.msgh_descriptor_count) { + replymsg->contents.msg1.desc.out_of_line.deallocate = true; + } + if (data) CFRelease(data); + if (1 == msgp->body.msgh_descriptor_count) { + vm_deallocate(mach_task_self(), (vm_address_t)msgp->contents.msg1.desc.out_of_line.address, msgp->contents.msg1.desc.out_of_line.size); + } + if (returnData) CFRelease(returnData); + if (context_release) { + context_release(context_info); + } + return replymsg; +} + +CFRunLoopSourceRef CFMessagePortCreateRunLoopSource(CFAllocatorRef allocator, CFMessagePortRef ms, CFIndex order) { + CFRunLoopSourceRef result = NULL; + __CFGenericValidateType(ms, __kCFMessagePortTypeID); +//#warning CF: This should be an assert + // if (__CFMessagePortIsRemote(ms)) return NULL; + __CFMessagePortLock(ms); + if (NULL == ms->_source && __CFMessagePortIsValid(ms)) { + CFRunLoopSourceContext1 context; + context.version = 1; + context.info = (void *)ms; + context.retain = (const void *(*)(const void *))CFRetain; + context.release = (void (*)(const void *))CFRelease; + context.copyDescription = (CFStringRef (*)(const void *))__CFMessagePortCopyDescription; + context.equal = NULL; + context.hash = NULL; + context.getPort = __CFMessagePortGetPort; + context.perform = __CFMessagePortPerform; + ms->_source = CFRunLoopSourceCreate(allocator, order, (CFRunLoopSourceContext *)&context); + } + if (NULL != ms->_source) { + result = (CFRunLoopSourceRef)CFRetain(ms->_source); + } + __CFMessagePortUnlock(ms); + return result; +} + +#endif
diff --git a/CoreFoundation/CFMessagePort.h b/CoreFoundation/CFMessagePort.h new file mode 100644 index 0000000..3061291 --- /dev/null +++ b/CoreFoundation/CFMessagePort.h
@@ -0,0 +1,90 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFMessagePort.h + Copyright (c) 1998-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFMESSAGEPORT__) +#define __COREFOUNDATION_CFMESSAGEPORT__ 1 + +#include <CoreFoundation/CFString.h> +#include <CoreFoundation/CFRunLoop.h> +#include <CoreFoundation/CFData.h> + +CF_EXTERN_C_BEGIN + +typedef struct __CFMessagePort * CFMessagePortRef; + +enum { + kCFMessagePortSuccess = 0, + kCFMessagePortSendTimeout = -1, + kCFMessagePortReceiveTimeout = -2, + kCFMessagePortIsInvalid = -3, + kCFMessagePortTransportError = -4 +}; + +typedef struct { + CFIndex version; + void * info; + const void *(*retain)(const void *info); + void (*release)(const void *info); + CFStringRef (*copyDescription)(const void *info); +} CFMessagePortContext; + +typedef CFDataRef (*CFMessagePortCallBack)(CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info); +/* If callout wants to keep a hold of the data past the return of the callout, it must COPY the data. This includes the case where the data is given to some routine which _might_ keep a hold of it; System will release returned CFData. */ +typedef void (*CFMessagePortInvalidationCallBack)(CFMessagePortRef ms, void *info); + +CF_EXPORT CFTypeID CFMessagePortGetTypeID(void); + +CF_EXPORT CFMessagePortRef CFMessagePortCreateLocal(CFAllocatorRef allocator, CFStringRef name, CFMessagePortCallBack callout, CFMessagePortContext *context, Boolean *shouldFreeInfo); +CF_EXPORT CFMessagePortRef CFMessagePortCreateRemote(CFAllocatorRef allocator, CFStringRef name); + +CF_EXPORT Boolean CFMessagePortIsRemote(CFMessagePortRef ms); +CF_EXPORT CFStringRef CFMessagePortGetName(CFMessagePortRef ms); +CF_EXPORT Boolean CFMessagePortSetName(CFMessagePortRef ms, CFStringRef newName); +CF_EXPORT void CFMessagePortGetContext(CFMessagePortRef ms, CFMessagePortContext *context); +CF_EXPORT void CFMessagePortInvalidate(CFMessagePortRef ms); +CF_EXPORT Boolean CFMessagePortIsValid(CFMessagePortRef ms); +CF_EXPORT CFMessagePortInvalidationCallBack CFMessagePortGetInvalidationCallBack(CFMessagePortRef ms); +CF_EXPORT void CFMessagePortSetInvalidationCallBack(CFMessagePortRef ms, CFMessagePortInvalidationCallBack callout); + +/* NULL replyMode argument means no return value expected, dont wait for it */ +CF_EXPORT SInt32 CFMessagePortSendRequest(CFMessagePortRef remote, SInt32 msgid, CFDataRef data, CFTimeInterval sendTimeout, CFTimeInterval rcvTimeout, CFStringRef replyMode, CFDataRef *returnData); + +CF_EXPORT CFRunLoopSourceRef CFMessagePortCreateRunLoopSource(CFAllocatorRef allocator, CFMessagePortRef local, CFIndex order); + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFMESSAGEPORT__ */ +
diff --git a/CoreFoundation/CFNumber.c b/CoreFoundation/CFNumber.c new file mode 100644 index 0000000..b73f338 --- /dev/null +++ b/CoreFoundation/CFNumber.c
@@ -0,0 +1,1136 @@ +/* + * 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 +
diff --git a/CoreFoundation/CFNumber.h b/CoreFoundation/CFNumber.h new file mode 100644 index 0000000..c8152dc --- /dev/null +++ b/CoreFoundation/CFNumber.h
@@ -0,0 +1,159 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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.h + Copyright (c) 1999-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFNUMBER__) +#define __COREFOUNDATION_CFNUMBER__ 1 + +#include <CoreFoundation/CFBase.h> + +CF_EXTERN_C_BEGIN + +typedef const struct __CFBoolean * CFBooleanRef; + +CF_EXPORT +const CFBooleanRef kCFBooleanTrue; +CF_EXPORT +const CFBooleanRef kCFBooleanFalse; + +CF_EXPORT +CFTypeID CFBooleanGetTypeID(void); + +CF_EXPORT +Boolean CFBooleanGetValue(CFBooleanRef boolean); + +enum { + /* Fixed-width types */ + kCFNumberSInt8Type = 1, + kCFNumberSInt16Type = 2, + kCFNumberSInt32Type = 3, + kCFNumberSInt64Type = 4, + kCFNumberFloat32Type = 5, + kCFNumberFloat64Type = 6, /* 64-bit IEEE 754 */ + /* Basic C types */ + kCFNumberCharType = 7, + kCFNumberShortType = 8, + kCFNumberIntType = 9, + kCFNumberLongType = 10, + kCFNumberLongLongType = 11, + kCFNumberFloatType = 12, + kCFNumberDoubleType = 13, + /* Other */ + kCFNumberCFIndexType = 14, +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + kCFNumberNSIntegerType = 15, + kCFNumberCGFloatType = 16, + kCFNumberMaxType = 16 +#else + kCFNumberMaxType = 14 +#endif +}; +typedef CFIndex CFNumberType; + +typedef const struct __CFNumber * CFNumberRef; + +CF_EXPORT +const CFNumberRef kCFNumberPositiveInfinity; +CF_EXPORT +const CFNumberRef kCFNumberNegativeInfinity; +CF_EXPORT +const CFNumberRef kCFNumberNaN; + +CF_EXPORT +CFTypeID CFNumberGetTypeID(void); + +/* + Creates a CFNumber with the given value. The type of number pointed + to by the valuePtr is specified by type. If type is a floating point + type and the value represents one of the infinities or NaN, the + well-defined CFNumber for that value is returned. If either of + valuePtr or type is an invalid value, the result is undefined. +*/ +CF_EXPORT +CFNumberRef CFNumberCreate(CFAllocatorRef allocator, CFNumberType theType, const void *valuePtr); + +/* + Returns the storage format of the CFNumber's value. Note that + this is not necessarily the type provided in CFNumberCreate(). +*/ +CF_EXPORT +CFNumberType CFNumberGetType(CFNumberRef number); + +/* + Returns the size in bytes of the type of the number. +*/ +CF_EXPORT +CFIndex CFNumberGetByteSize(CFNumberRef number); + +/* + Returns true if the type of the CFNumber's value is one of + the defined floating point types. +*/ +CF_EXPORT +Boolean CFNumberIsFloatType(CFNumberRef number); + +/* + Copies the CFNumber's value into the space pointed to by + valuePtr, as the specified type. If conversion needs to take + place, the conversion rules follow human expectation and not + C's promotion and truncation rules. If the conversion is + lossy, or the value is out of range, false is returned. Best + attempt at conversion will still be in *valuePtr. +*/ +CF_EXPORT +Boolean CFNumberGetValue(CFNumberRef number, CFNumberType theType, void *valuePtr); + +/* + Compares the two CFNumber instances. If conversion of the + types of the values is needed, the conversion and comparison + follow human expectations and not C's promotion and comparison + rules. Negative zero compares less than positive zero. + Positive infinity compares greater than everything except + itself, to which it compares equal. Negative infinity compares + less than everything except itself, to which it compares equal. + Unlike standard practice, if both numbers are NaN, then they + compare equal; if only one of the numbers is NaN, then the NaN + compares greater than the other number if it is negative, and + smaller than the other number if it is positive. (Note that in + CFEqual() with two CFNumbers, if either or both of the numbers + is NaN, true is returned.) +*/ +CF_EXPORT +CFComparisonResult CFNumberCompare(CFNumberRef number, CFNumberRef otherNumber, void *context); + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFNUMBER__ */ +
diff --git a/CoreFoundation/CFNumberFormatter.c b/CoreFoundation/CFNumberFormatter.c new file mode 100644 index 0000000..2758871 --- /dev/null +++ b/CoreFoundation/CFNumberFormatter.c
@@ -0,0 +1,1007 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFNumberFormatter.c + Copyright 2002-2003, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include <CoreFoundation/CFNumberFormatter.h> +#include "CFInternal.h" +#include <unicode/unum.h> +#include <unicode/ucurr.h> +#include <math.h> +#include <float.h> + +static void __CFNumberFormatterCustomize(CFNumberFormatterRef formatter); + +#define BUFFER_SIZE 768 + +struct __CFNumberFormatter { + CFRuntimeBase _base; + UNumberFormat *_nf; + CFLocaleRef _locale; + CFNumberFormatterStyle _style; + CFStringRef _format; // NULL for RBNFs + CFStringRef _defformat; + CFNumberRef _multiplier; + CFStringRef _zeroSym; +}; + +static CFStringRef __CFNumberFormatterCopyDescription(CFTypeRef cf) { + CFNumberFormatterRef formatter = (CFNumberFormatterRef)cf; + return CFStringCreateWithFormat(CFGetAllocator(formatter), NULL, CFSTR("<CFNumberFormatter %p [%p]>"), cf, CFGetAllocator(formatter)); +} + +static void __CFNumberFormatterDeallocate(CFTypeRef cf) { + CFNumberFormatterRef formatter = (CFNumberFormatterRef)cf; + if (formatter->_nf) unum_close(formatter->_nf); + if (formatter->_locale) CFRelease(formatter->_locale); + if (formatter->_format) CFRelease(formatter->_format); + if (formatter->_defformat) CFRelease(formatter->_defformat); + if (formatter->_multiplier) CFRelease(formatter->_multiplier); + if (formatter->_zeroSym) CFRelease(formatter->_zeroSym); +} + +static CFTypeID __kCFNumberFormatterTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFNumberFormatterClass = { + 0, + "CFNumberFormatter", + NULL, // init + NULL, // copy + __CFNumberFormatterDeallocate, + NULL, + NULL, + NULL, // + __CFNumberFormatterCopyDescription +}; + +static void __CFNumberFormatterInitialize(void) { + __kCFNumberFormatterTypeID = _CFRuntimeRegisterClass(&__CFNumberFormatterClass); +} + +CFTypeID CFNumberFormatterGetTypeID(void) { + if (_kCFRuntimeNotATypeID == __kCFNumberFormatterTypeID) __CFNumberFormatterInitialize(); + return __kCFNumberFormatterTypeID; +} + +CFNumberFormatterRef CFNumberFormatterCreate(CFAllocatorRef allocator, CFLocaleRef locale, CFNumberFormatterStyle style) { + struct __CFNumberFormatter *memory; + uint32_t size = sizeof(struct __CFNumberFormatter) - sizeof(CFRuntimeBase); + if (allocator == NULL) allocator = __CFGetDefaultAllocator(); + __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); + __CFGenericValidateType(locale, CFLocaleGetTypeID()); + memory = (struct __CFNumberFormatter *)_CFRuntimeCreateInstance(allocator, CFNumberFormatterGetTypeID(), size, NULL); + if (NULL == memory) { + return NULL; + } + memory->_nf = NULL; + memory->_locale = NULL; + memory->_format = NULL; + memory->_defformat = NULL; + memory->_multiplier = NULL; + memory->_zeroSym = NULL; + if (NULL == locale) locale = CFLocaleGetSystem(); + memory->_style = style; + uint32_t ustyle; + switch (style) { + case kCFNumberFormatterNoStyle: ustyle = UNUM_IGNORE; break; + case kCFNumberFormatterDecimalStyle: ustyle = UNUM_DECIMAL; break; + case kCFNumberFormatterCurrencyStyle: ustyle = UNUM_CURRENCY; break; + case kCFNumberFormatterPercentStyle: ustyle = UNUM_PERCENT; break; + case kCFNumberFormatterScientificStyle: ustyle = UNUM_SCIENTIFIC; break; + case kCFNumberFormatterSpellOutStyle: ustyle = UNUM_SPELLOUT; break; + default: + CFAssert2(0, __kCFLogAssertion, "%s(): unknown style %d", __PRETTY_FUNCTION__, style); + ustyle = UNUM_DECIMAL; + memory->_style = kCFNumberFormatterDecimalStyle; + break; + } + CFStringRef localeName = locale ? CFLocaleGetIdentifier(locale) : CFSTR(""); + char buffer[BUFFER_SIZE]; + const char *cstr = CFStringGetCStringPtr(localeName, kCFStringEncodingASCII); + if (NULL == cstr) { + if (CFStringGetCString(localeName, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer; + } + if (NULL == cstr) { + CFRelease(memory); + return NULL; + } + UErrorCode status = U_ZERO_ERROR; + memory->_nf = unum_open((UNumberFormatStyle)ustyle, NULL, 0, cstr, NULL, &status); + CFAssert2(memory->_nf, __kCFLogAssertion, "%s(): error (%d) creating number formatter", __PRETTY_FUNCTION__, status); + if (NULL == memory->_nf) { + CFRelease(memory); + return NULL; + } + UChar ubuff[4]; + if (kCFNumberFormatterNoStyle == style) { + status = U_ZERO_ERROR; + ubuff[0] = '#'; ubuff[1] = ';'; ubuff[2] = '#'; + unum_applyPattern(memory->_nf, false, ubuff, 3, NULL, &status); + unum_setAttribute(memory->_nf, UNUM_MAX_INTEGER_DIGITS, 42); + unum_setAttribute(memory->_nf, UNUM_MAX_FRACTION_DIGITS, 0); + } + memory->_locale = locale ? CFLocaleCreateCopy(allocator, locale) : CFLocaleGetSystem(); + __CFNumberFormatterCustomize(memory); + if (kCFNumberFormatterSpellOutStyle != memory->_style) { + UChar ubuffer[BUFFER_SIZE]; + status = U_ZERO_ERROR; + int32_t ret = unum_toPattern(memory->_nf, false, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && ret <= BUFFER_SIZE) { + memory->_format = CFStringCreateWithCharacters(allocator, (const UniChar *)ubuffer, ret); + } + } + memory->_defformat = memory->_format ? (CFStringRef)CFRetain(memory->_format) : NULL; + if (kCFNumberFormatterSpellOutStyle != memory->_style) { + int32_t n = unum_getAttribute(memory->_nf, UNUM_MULTIPLIER); + if (1 != n) { + memory->_multiplier = CFNumberCreate(allocator, kCFNumberSInt32Type, &n); + unum_setAttribute(memory->_nf, UNUM_MULTIPLIER, 1); + } + } + return (CFNumberFormatterRef)memory; +} + +extern CFDictionaryRef __CFLocaleGetPrefs(CFLocaleRef locale); + +static void __substituteFormatStringFromPrefsNF(CFNumberFormatterRef formatter) { + CFIndex formatStyle = formatter->_style; + if (kCFNumberFormatterSpellOutStyle == formatStyle) return; + CFStringRef prefName = CFSTR("AppleICUNumberFormatStrings"); + if (kCFNumberFormatterNoStyle != formatStyle) { + CFStringRef pref = NULL; + CFDictionaryRef prefs = __CFLocaleGetPrefs(formatter->_locale); + CFPropertyListRef metapref = prefs ? CFDictionaryGetValue(prefs, prefName) : NULL; + if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) { + CFStringRef key; + switch (formatStyle) { + case kCFNumberFormatterDecimalStyle: key = CFSTR("1"); break; + case kCFNumberFormatterCurrencyStyle: key = CFSTR("2"); break; + case kCFNumberFormatterPercentStyle: key = CFSTR("3"); break; + case kCFNumberFormatterScientificStyle: key = CFSTR("4"); break; + case kCFNumberFormatterSpellOutStyle: key = CFSTR("5"); break; + default: key = CFSTR("0"); break; + } + pref = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)metapref, key); + } + if (NULL != pref && CFGetTypeID(pref) == CFStringGetTypeID()) { + int32_t icustyle = UNUM_IGNORE; + switch (formatStyle) { + case kCFNumberFormatterDecimalStyle: icustyle = UNUM_DECIMAL; break; + case kCFNumberFormatterCurrencyStyle: icustyle = UNUM_CURRENCY; break; + case kCFNumberFormatterPercentStyle: icustyle = UNUM_PERCENT; break; + case kCFNumberFormatterScientificStyle: icustyle = UNUM_SCIENTIFIC; break; + case kCFNumberFormatterSpellOutStyle: icustyle = UNUM_SPELLOUT; break; + } + CFStringRef localeName = CFLocaleGetIdentifier(formatter->_locale); + char buffer[BUFFER_SIZE]; + const char *cstr = CFStringGetCStringPtr(localeName, kCFStringEncodingASCII); + if (NULL == cstr) { + if (CFStringGetCString(localeName, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer; + } + UErrorCode status = U_ZERO_ERROR; + UNumberFormat *nf = unum_open((UNumberFormatStyle)icustyle, NULL, 0, cstr, NULL, &status); + if (NULL != nf) { + UChar ubuffer[BUFFER_SIZE]; + status = U_ZERO_ERROR; + int32_t number_len = unum_toPattern(nf, false, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && number_len <= BUFFER_SIZE) { + CFStringRef numberString = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)ubuffer, number_len); + status = U_ZERO_ERROR; + int32_t formatter_len = unum_toPattern(formatter->_nf, false, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && formatter_len <= BUFFER_SIZE) { + CFMutableStringRef formatString = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); + CFStringAppendCharacters(formatString, (const UniChar *)ubuffer, formatter_len); + // find numberString inside formatString, substitute the pref in that range + CFRange result; + if (CFStringFindWithOptions(formatString, numberString, CFRangeMake(0, formatter_len), 0, &result)) { + CFStringReplace(formatString, result, pref); + int32_t new_len = CFStringGetLength(formatString); +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX + STACK_BUFFER_DECL(UChar, new_buffer, new_len); +#else + UChar new_buffer[BUFFER_SIZE]; +#endif + const UChar *new_ustr = (const UChar *)CFStringGetCharactersPtr(formatString); + if (NULL == new_ustr) { + CFStringGetCharacters(formatString, CFRangeMake(0, new_len), (UniChar *)new_buffer); + new_ustr = new_buffer; + } + status = U_ZERO_ERROR; + unum_applyPattern(formatter->_nf, false, new_ustr, new_len, NULL, &status); + } + CFRelease(formatString); + } + CFRelease(numberString); + } + unum_close(nf); + } + } + } +} + +static void __CFNumberFormatterApplySymbolPrefs(const void *key, const void *value, void *context) { + if (CFGetTypeID(key) == CFStringGetTypeID() && CFGetTypeID(value) == CFStringGetTypeID()) { + CFNumberFormatterRef formatter = (CFNumberFormatterRef)context; + UNumberFormatSymbol sym = (UNumberFormatSymbol)CFStringGetIntValue((CFStringRef)key); + CFStringRef item = (CFStringRef)value; + CFIndex item_cnt = CFStringGetLength(item); +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX + STACK_BUFFER_DECL(UChar, item_buffer, item_cnt); +#else + UChar item_buffer[BUFFER_SIZE]; +#endif + UChar *item_ustr = (UChar *)CFStringGetCharactersPtr(item); + if (NULL == item_ustr) { + CFStringGetCharacters(item, CFRangeMake(0, __CFMin(BUFFER_SIZE, item_cnt)), (UniChar *)item_buffer); + item_ustr = item_buffer; + } + UErrorCode status = U_ZERO_ERROR; + unum_setSymbol(formatter->_nf, sym, item_ustr, item_cnt, &status); + } +} + +static void __CFNumberFormatterCustomize(CFNumberFormatterRef formatter) { + __substituteFormatStringFromPrefsNF(formatter); + CFDictionaryRef prefs = __CFLocaleGetPrefs(formatter->_locale); + CFPropertyListRef metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUNumberSymbols")) : NULL; + if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) { + CFDictionaryApplyFunction((CFDictionaryRef)metapref, __CFNumberFormatterApplySymbolPrefs, formatter); + } +} + +CFLocaleRef CFNumberFormatterGetLocale(CFNumberFormatterRef formatter) { + __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); + return formatter->_locale; +} + +CFNumberFormatterStyle CFNumberFormatterGetStyle(CFNumberFormatterRef formatter) { + __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); + return formatter->_style; +} + +CFStringRef CFNumberFormatterGetFormat(CFNumberFormatterRef formatter) { + __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); + if (kCFNumberFormatterSpellOutStyle == formatter->_style) return NULL; + UChar ubuffer[BUFFER_SIZE]; + CFStringRef newString = NULL; + UErrorCode status = U_ZERO_ERROR; + int32_t ret = unum_toPattern(formatter->_nf, false, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && ret <= BUFFER_SIZE) { + newString = CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, ret); + } + if (newString && !formatter->_format) { + formatter->_format = newString; + } else if (newString && !CFEqual(newString, formatter->_format)) { + CFRelease(formatter->_format); + formatter->_format = newString; + } else if (newString) { + CFRelease(newString); + } + return formatter->_format; +} + +void CFNumberFormatterSetFormat(CFNumberFormatterRef formatter, CFStringRef formatString) { + __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); + __CFGenericValidateType(formatString, CFStringGetTypeID()); + if (kCFNumberFormatterSpellOutStyle == formatter->_style) return; + CFIndex cnt = CFStringGetLength(formatString); + CFAssert1(cnt <= 1024, __kCFLogAssertion, "%s(): format string too long", __PRETTY_FUNCTION__); + if ((!formatter->_format || !CFEqual(formatter->_format, formatString)) && cnt <= 1024) { +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX + STACK_BUFFER_DECL(UChar, ubuffer, cnt); +#else + UChar ubuffer[BUFFER_SIZE]; +#endif + const UChar *ustr = (const UChar *)CFStringGetCharactersPtr(formatString); + if (NULL == ustr) { + CFStringGetCharacters(formatString, CFRangeMake(0, cnt), (UniChar *)ubuffer); + ustr = ubuffer; + } + UErrorCode status = U_ZERO_ERROR; + unum_applyPattern(formatter->_nf, false, ustr, cnt, NULL, &status); + if (U_SUCCESS(status)) { + if (formatter->_format) CFRelease(formatter->_format); + UChar ubuffer2[BUFFER_SIZE]; + status = U_ZERO_ERROR; + int32_t ret = unum_toPattern(formatter->_nf, false, ubuffer2, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && ret <= BUFFER_SIZE) { + formatter->_format = CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer2, ret); + } + } + } +} + +CFStringRef CFNumberFormatterCreateStringWithNumber(CFAllocatorRef allocator, CFNumberFormatterRef formatter, CFNumberRef number) { + if (allocator == NULL) allocator = __CFGetDefaultAllocator(); + __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); + __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); + __CFGenericValidateType(number, CFNumberGetTypeID()); + CFNumberType type = CFNumberGetType(number); + char buffer[64]; + CFNumberGetValue(number, type, buffer); + return CFNumberFormatterCreateStringWithValue(allocator, formatter, type, buffer); +} + +#define FORMAT(T, FUNC) \ + T value = *(T *)valuePtr; \ + if (0 == value && formatter->_zeroSym) { return (CFStringRef)CFRetain(formatter->_zeroSym); } \ + if (1.0 != multiplier) value = (T)(value * multiplier); \ + status = U_ZERO_ERROR; \ + used = FUNC(formatter->_nf, value, ubuffer, cnt, NULL, &status); \ + if (status == U_BUFFER_OVERFLOW_ERROR || cnt < used) { \ + cnt = used + 1; \ + ustr = (UChar *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(UChar) * cnt, 0); \ + status = U_ZERO_ERROR; \ + used = FUNC(formatter->_nf, value, ustr, cnt, NULL, &status); \ + } + +CFStringRef CFNumberFormatterCreateStringWithValue(CFAllocatorRef allocator, CFNumberFormatterRef formatter, CFNumberType numberType, const void *valuePtr) { + if (allocator == NULL) allocator = __CFGetDefaultAllocator(); + __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); + __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); + double multiplier = 1.0; + if (formatter->_multiplier) { + if (!CFNumberGetValue(formatter->_multiplier, kCFNumberFloat64Type, &multiplier)) { + multiplier = 1.0; + } + } + UChar *ustr = NULL, ubuffer[BUFFER_SIZE]; + UErrorCode status = U_ZERO_ERROR; + CFIndex used, cnt = BUFFER_SIZE; + if (numberType == kCFNumberFloat64Type || numberType == kCFNumberDoubleType) { + FORMAT(double, unum_formatDouble) + } else if (numberType == kCFNumberFloat32Type || numberType == kCFNumberFloatType) { + FORMAT(float, unum_formatDouble) + } else if (numberType == kCFNumberSInt64Type || numberType == kCFNumberLongLongType) { + FORMAT(int64_t, unum_formatInt64) + } else if (numberType == kCFNumberLongType || numberType == kCFNumberCFIndexType) { +#if __LP64__ + FORMAT(int64_t, unum_formatInt64) +#else + FORMAT(int32_t, unum_formatInt64) +#endif + } else if (numberType == kCFNumberSInt32Type || numberType == kCFNumberIntType) { + FORMAT(int32_t, unum_formatInt64) + } else if (numberType == kCFNumberSInt16Type || numberType == kCFNumberShortType) { + FORMAT(int16_t, unum_formatInt64) + } else if (numberType == kCFNumberSInt8Type || numberType == kCFNumberCharType) { + FORMAT(int8_t, unum_formatInt64) + } else { + CFAssert2(0, __kCFLogAssertion, "%s(): unknown CFNumberType (%d)", __PRETTY_FUNCTION__, numberType); + return NULL; + } + CFStringRef string = NULL; + if (U_SUCCESS(status)) { + string = CFStringCreateWithCharacters(allocator, ustr ? (const UniChar *)ustr : (const UniChar *)ubuffer, used); + } + if (ustr) CFAllocatorDeallocate(kCFAllocatorSystemDefault, ustr); + return string; +} + +#undef FORMAT + +CFNumberRef CFNumberFormatterCreateNumberFromString(CFAllocatorRef allocator, CFNumberFormatterRef formatter, CFStringRef string, CFRange *rangep, CFOptionFlags options) { + if (allocator == NULL) allocator = __CFGetDefaultAllocator(); + __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); + __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); + __CFGenericValidateType(string, CFStringGetTypeID()); + CFNumberType type = (options & kCFNumberFormatterParseIntegersOnly) ? kCFNumberSInt64Type : kCFNumberFloat64Type; + char buffer[16]; + if (CFNumberFormatterGetValueFromString(formatter, string, rangep, type, buffer)) { + return CFNumberCreate(allocator, type, buffer); + } + return NULL; +} + +Boolean CFNumberFormatterGetValueFromString(CFNumberFormatterRef formatter, CFStringRef string, CFRange *rangep, CFNumberType numberType, void *valuePtr) { + __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); + __CFGenericValidateType(string, CFStringGetTypeID()); + Boolean isZero = false; + CFRange range = {0, 0}; + if (rangep) { + range = *rangep; + } else { + range.length = CFStringGetLength(string); + } + if (formatter->_zeroSym && kCFCompareEqualTo == CFStringCompareWithOptions(string, formatter->_zeroSym, range, 0)) { + isZero = true; + } + if (1024 < range.length) range.length = 1024; + const UChar *ustr = (const UChar *)CFStringGetCharactersPtr(string); +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX + STACK_BUFFER_DECL(UChar, ubuffer, (NULL == ustr) ? range.length : 1); +#else + UChar ubuffer[1024]; +#endif + if (NULL == ustr) { + CFStringGetCharacters(string, range, (UniChar *)ubuffer); + ustr = ubuffer; + } else { + ustr += range.location; + } + Boolean integerOnly = 1; + switch (numberType) { + case kCFNumberSInt8Type: case kCFNumberCharType: + case kCFNumberSInt16Type: case kCFNumberShortType: + case kCFNumberSInt32Type: case kCFNumberIntType: + case kCFNumberLongType: case kCFNumberCFIndexType: + case kCFNumberSInt64Type: case kCFNumberLongLongType: + unum_setAttribute(formatter->_nf, UNUM_PARSE_INT_ONLY, 1); + break; + default: + unum_setAttribute(formatter->_nf, UNUM_PARSE_INT_ONLY, 0); + integerOnly = 0; + break; + } + int32_t dpos = 0; + UErrorCode status = U_ZERO_ERROR; + int64_t dreti = 0; + double dretd = 0.0; + if (isZero) { + dpos = rangep ? rangep->length : 0; + } else { + if (integerOnly) { + dreti = unum_parseInt64(formatter->_nf, ustr, range.length, &dpos, &status); + } else { + dretd = unum_parseDouble(formatter->_nf, ustr, range.length, &dpos, &status); + } + } + if (rangep) rangep->length = dpos; + if (U_FAILURE(status)) { + return false; + } + if (formatter->_multiplier) { + double multiplier = 1.0; + if (!CFNumberGetValue(formatter->_multiplier, kCFNumberFloat64Type, &multiplier)) { + multiplier = 1.0; + } + dreti = (int64_t)((double)dreti / multiplier); // integer truncation + dretd = dretd / multiplier; + } + switch (numberType) { + case kCFNumberSInt8Type: case kCFNumberCharType: + if (INT8_MIN <= dreti && dreti <= INT8_MAX) { + *(int8_t *)valuePtr = (int8_t)dreti; + return true; + } + break; + case kCFNumberSInt16Type: case kCFNumberShortType: + if (INT16_MIN <= dreti && dreti <= INT16_MAX) { + *(int16_t *)valuePtr = (int16_t)dreti; + return true; + } + break; + case kCFNumberSInt32Type: case kCFNumberIntType: +#if !__LP64__ + case kCFNumberLongType: case kCFNumberCFIndexType: +#endif + if (INT32_MIN <= dreti && dreti <= INT32_MAX) { + *(int32_t *)valuePtr = (int32_t)dreti; + return true; + } + break; + case kCFNumberSInt64Type: case kCFNumberLongLongType: +#if __LP64__ + case kCFNumberLongType: case kCFNumberCFIndexType: +#endif + if (INT64_MIN <= dreti && dreti <= INT64_MAX) { + *(int64_t *)valuePtr = (int64_t)dreti; + return true; + } + break; + case kCFNumberFloat32Type: case kCFNumberFloatType: + if (-FLT_MAX <= dretd && dretd <= FLT_MAX) { + *(float *)valuePtr = (float)dretd; + return true; + } + break; + case kCFNumberFloat64Type: case kCFNumberDoubleType: + if (-DBL_MAX <= dretd && dretd <= DBL_MAX) { + *(double *)valuePtr = (double)dretd; + return true; + } + break; + } + return false; +} + +void CFNumberFormatterSetProperty(CFNumberFormatterRef formatter, CFStringRef key, CFTypeRef value) { + int32_t n; + double d; + UErrorCode status = U_ZERO_ERROR; + UChar ubuffer[BUFFER_SIZE]; + CFIndex cnt; + __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); + __CFGenericValidateType(key, CFStringGetTypeID()); + if (kCFNumberFormatterCurrencyCode == key) { + __CFGenericValidateType(value, CFStringGetTypeID()); + cnt = CFStringGetLength((CFStringRef)value); + if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; + CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); + unum_setTextAttribute(formatter->_nf, UNUM_CURRENCY_CODE, ubuffer, cnt, &status); + } else if (kCFNumberFormatterDecimalSeparator == key) { + __CFGenericValidateType(value, CFStringGetTypeID()); + cnt = CFStringGetLength((CFStringRef)value); + if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; + CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); + unum_setSymbol(formatter->_nf, UNUM_DECIMAL_SEPARATOR_SYMBOL, ubuffer, cnt, &status); + } else if (kCFNumberFormatterCurrencyDecimalSeparator == key) { + __CFGenericValidateType(value, CFStringGetTypeID()); + cnt = CFStringGetLength((CFStringRef)value); + if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; + CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); + unum_setSymbol(formatter->_nf, UNUM_MONETARY_SEPARATOR_SYMBOL, ubuffer, cnt, &status); + } else if (kCFNumberFormatterAlwaysShowDecimalSeparator == key) { + __CFGenericValidateType(value, CFBooleanGetTypeID()); + unum_setAttribute(formatter->_nf, UNUM_DECIMAL_ALWAYS_SHOWN, (kCFBooleanTrue == value)); + } else if (kCFNumberFormatterGroupingSeparator == key) { + __CFGenericValidateType(value, CFStringGetTypeID()); + cnt = CFStringGetLength((CFStringRef)value); + if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; + CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); + unum_setSymbol(formatter->_nf, UNUM_GROUPING_SEPARATOR_SYMBOL, (const UChar *)ubuffer, cnt, &status); + } else if (kCFNumberFormatterUseGroupingSeparator == key) { + __CFGenericValidateType(value, CFBooleanGetTypeID()); + unum_setAttribute(formatter->_nf, UNUM_GROUPING_USED, (kCFBooleanTrue == value)); + } else if (kCFNumberFormatterPercentSymbol == key) { + __CFGenericValidateType(value, CFStringGetTypeID()); + cnt = CFStringGetLength((CFStringRef)value); + if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; + CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); + unum_setSymbol(formatter->_nf, UNUM_PERCENT_SYMBOL, ubuffer, cnt, &status); + } else if (kCFNumberFormatterZeroSymbol == key) { + __CFGenericValidateType(value, CFStringGetTypeID()); + CFStringRef old = formatter->_zeroSym; + formatter->_zeroSym = value ? (CFStringRef)CFRetain(value) : NULL; + if (old) CFRelease(old); + } else if (kCFNumberFormatterNaNSymbol == key) { + __CFGenericValidateType(value, CFStringGetTypeID()); + cnt = CFStringGetLength((CFStringRef)value); + if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; + CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); + unum_setSymbol(formatter->_nf, UNUM_NAN_SYMBOL, ubuffer, cnt, &status); + } else if (kCFNumberFormatterInfinitySymbol == key) { + __CFGenericValidateType(value, CFStringGetTypeID()); + cnt = CFStringGetLength((CFStringRef)value); + if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; + CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); + unum_setSymbol(formatter->_nf, UNUM_INFINITY_SYMBOL, ubuffer, cnt, &status); + } else if (kCFNumberFormatterMinusSign == key) { + __CFGenericValidateType(value, CFStringGetTypeID()); + cnt = CFStringGetLength((CFStringRef)value); + if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; + CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); + unum_setSymbol(formatter->_nf, UNUM_MINUS_SIGN_SYMBOL, ubuffer, cnt, &status); + } else if (kCFNumberFormatterPlusSign == key) { + __CFGenericValidateType(value, CFStringGetTypeID()); + cnt = CFStringGetLength((CFStringRef)value); + if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; + CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); + unum_setSymbol(formatter->_nf, UNUM_PLUS_SIGN_SYMBOL, ubuffer, cnt, &status); + } else if (kCFNumberFormatterCurrencySymbol == key) { + __CFGenericValidateType(value, CFStringGetTypeID()); + cnt = CFStringGetLength((CFStringRef)value); + if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; + CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); + unum_setSymbol(formatter->_nf, UNUM_CURRENCY_SYMBOL, (const UChar *)ubuffer, cnt, &status); + } else if (kCFNumberFormatterExponentSymbol == key) { + __CFGenericValidateType(value, CFStringGetTypeID()); + cnt = CFStringGetLength((CFStringRef)value); + if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; + CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); + unum_setSymbol(formatter->_nf, UNUM_EXPONENTIAL_SYMBOL, ubuffer, cnt, &status); + } else if (kCFNumberFormatterMinIntegerDigits == key) { + __CFGenericValidateType(value, CFNumberGetTypeID()); + CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); + unum_setAttribute(formatter->_nf, UNUM_MIN_INTEGER_DIGITS, n); + } else if (kCFNumberFormatterMaxIntegerDigits == key) { + __CFGenericValidateType(value, CFNumberGetTypeID()); + CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); + unum_setAttribute(formatter->_nf, UNUM_MAX_INTEGER_DIGITS, n); + } else if (kCFNumberFormatterMinFractionDigits == key) { + __CFGenericValidateType(value, CFNumberGetTypeID()); + CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); + unum_setAttribute(formatter->_nf, UNUM_MIN_FRACTION_DIGITS, n); + } else if (kCFNumberFormatterMaxFractionDigits == key) { + __CFGenericValidateType(value, CFNumberGetTypeID()); + CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); + unum_setAttribute(formatter->_nf, UNUM_MAX_FRACTION_DIGITS, n); + } else if (kCFNumberFormatterGroupingSize == key) { + __CFGenericValidateType(value, CFNumberGetTypeID()); + CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); + unum_setAttribute(formatter->_nf, UNUM_GROUPING_SIZE, n); + } else if (kCFNumberFormatterSecondaryGroupingSize == key) { + __CFGenericValidateType(value, CFNumberGetTypeID()); + CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); + unum_setAttribute(formatter->_nf, UNUM_SECONDARY_GROUPING_SIZE, n); + } else if (kCFNumberFormatterRoundingMode == key) { + __CFGenericValidateType(value, CFNumberGetTypeID()); + CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); + unum_setAttribute(formatter->_nf, UNUM_ROUNDING_MODE, n); + } else if (kCFNumberFormatterRoundingIncrement == key) { + __CFGenericValidateType(value, CFNumberGetTypeID()); + CFNumberGetValue((CFNumberRef)value, kCFNumberDoubleType, &d); + unum_setDoubleAttribute(formatter->_nf, UNUM_ROUNDING_INCREMENT, d); + } else if (kCFNumberFormatterFormatWidth == key) { + __CFGenericValidateType(value, CFNumberGetTypeID()); + CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); + unum_setAttribute(formatter->_nf, UNUM_FORMAT_WIDTH, n); + } else if (kCFNumberFormatterPaddingPosition == key) { + __CFGenericValidateType(value, CFNumberGetTypeID()); + CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); + unum_setAttribute(formatter->_nf, UNUM_PADDING_POSITION, n); + } else if (kCFNumberFormatterPaddingCharacter == key) { + __CFGenericValidateType(value, CFStringGetTypeID()); + cnt = CFStringGetLength((CFStringRef)value); + if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; + CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); + unum_setTextAttribute(formatter->_nf, UNUM_PADDING_CHARACTER, ubuffer, cnt, &status); + } else if (kCFNumberFormatterDefaultFormat == key) { + // read-only attribute + } else if (kCFNumberFormatterMultiplier == key) { + __CFGenericValidateType(value, CFNumberGetTypeID()); + CFNumberRef old = formatter->_multiplier; + formatter->_multiplier = value ? (CFNumberRef)CFRetain(value) : NULL; + if (old) CFRelease(old); + } else if (kCFNumberFormatterPositivePrefix == key) { + __CFGenericValidateType(value, CFStringGetTypeID()); + cnt = CFStringGetLength((CFStringRef)value); + if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; + CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); + unum_setTextAttribute(formatter->_nf, UNUM_POSITIVE_PREFIX, ubuffer, cnt, &status); + } else if (kCFNumberFormatterPositiveSuffix == key) { + __CFGenericValidateType(value, CFStringGetTypeID()); + cnt = CFStringGetLength((CFStringRef)value); + if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; + CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); + unum_setTextAttribute(formatter->_nf, UNUM_POSITIVE_SUFFIX, (const UChar *)ubuffer, cnt, &status); + } else if (kCFNumberFormatterNegativePrefix == key) { + __CFGenericValidateType(value, CFStringGetTypeID()); + cnt = CFStringGetLength((CFStringRef)value); + if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; + CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); + unum_setTextAttribute(formatter->_nf, UNUM_NEGATIVE_PREFIX, ubuffer, cnt, &status); + } else if (kCFNumberFormatterNegativeSuffix == key) { + __CFGenericValidateType(value, CFStringGetTypeID()); + cnt = CFStringGetLength((CFStringRef)value); + if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; + CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); + unum_setTextAttribute(formatter->_nf, UNUM_NEGATIVE_SUFFIX, (const UChar *)ubuffer, cnt, &status); + } else if (kCFNumberFormatterPerMillSymbol == key) { + __CFGenericValidateType(value, CFStringGetTypeID()); + cnt = CFStringGetLength((CFStringRef)value); + if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; + CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); + unum_setSymbol(formatter->_nf, UNUM_PERMILL_SYMBOL, ubuffer, cnt, &status); + } else if (kCFNumberFormatterInternationalCurrencySymbol == key) { + __CFGenericValidateType(value, CFStringGetTypeID()); + cnt = CFStringGetLength((CFStringRef)value); + if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; + CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); + unum_setSymbol(formatter->_nf, UNUM_INTL_CURRENCY_SYMBOL, ubuffer, cnt, &status); + } else if (kCFNumberFormatterCurrencyGroupingSeparator == key) { + __CFGenericValidateType(value, CFStringGetTypeID()); + cnt = CFStringGetLength((CFStringRef)value); + if (BUFFER_SIZE < cnt) cnt = BUFFER_SIZE; + CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, cnt), (UniChar *)ubuffer); + unum_setSymbol(formatter->_nf, UNUM_MONETARY_GROUPING_SEPARATOR_SYMBOL, ubuffer, cnt, &status); + } else if (kCFNumberFormatterIsLenient == key) { + __CFGenericValidateType(value, CFBooleanGetTypeID()); + unum_setAttribute(formatter->_nf, UNUM_LENIENT_PARSE, (kCFBooleanTrue == value)); + } else if (kCFNumberFormatterUseSignificantDigits == key) { + __CFGenericValidateType(value, CFBooleanGetTypeID()); + unum_setAttribute(formatter->_nf, UNUM_SIGNIFICANT_DIGITS_USED, (kCFBooleanTrue == value)); + } else if (kCFNumberFormatterMinSignificantDigits == key) { + __CFGenericValidateType(value, CFNumberGetTypeID()); + CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); + unum_setAttribute(formatter->_nf, UNUM_MIN_SIGNIFICANT_DIGITS, n); + } else if (kCFNumberFormatterMaxSignificantDigits == key) { + __CFGenericValidateType(value, CFNumberGetTypeID()); + CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &n); + unum_setAttribute(formatter->_nf, UNUM_MAX_SIGNIFICANT_DIGITS, n); + } else { + CFAssert3(0, __kCFLogAssertion, "%s(): unknown key %p (%@)", __PRETTY_FUNCTION__, key, key); + } +} + +CFTypeRef CFNumberFormatterCopyProperty(CFNumberFormatterRef formatter, CFStringRef key) { + int32_t n; + double d; + UErrorCode status = U_ZERO_ERROR; + UChar ubuffer[BUFFER_SIZE]; + CFIndex cnt; + __CFGenericValidateType(formatter, CFNumberFormatterGetTypeID()); + __CFGenericValidateType(key, CFStringGetTypeID()); + if (kCFNumberFormatterCurrencyCode == key) { + cnt = unum_getTextAttribute(formatter->_nf, UNUM_CURRENCY_CODE, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && cnt == 0) { + CFStringRef localeName = CFLocaleGetIdentifier(formatter->_locale); + char buffer[BUFFER_SIZE]; + const char *cstr = CFStringGetCStringPtr(localeName, kCFStringEncodingASCII); + if (NULL == cstr) { + if (CFStringGetCString(localeName, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer; + } + if (NULL == cstr) { + return NULL; + } + UErrorCode status = U_ZERO_ERROR; + UNumberFormat *nf = unum_open(UNUM_CURRENCY, NULL, 0, cstr, NULL, &status); + if (NULL != nf) { + cnt = unum_getTextAttribute(nf, UNUM_CURRENCY_CODE, ubuffer, BUFFER_SIZE, &status); + unum_close(nf); + } + } + if (U_SUCCESS(status) && 0 < cnt && cnt <= BUFFER_SIZE) { + return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); + } + } else if (kCFNumberFormatterDecimalSeparator == key) { + cnt = unum_getSymbol(formatter->_nf, UNUM_DECIMAL_SEPARATOR_SYMBOL, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { + return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); + } + } else if (kCFNumberFormatterCurrencyDecimalSeparator == key) { + cnt = unum_getSymbol(formatter->_nf, UNUM_MONETARY_SEPARATOR_SYMBOL, (UChar *)ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { + return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); + } + } else if (kCFNumberFormatterAlwaysShowDecimalSeparator == key) { + n = unum_getAttribute(formatter->_nf, UNUM_DECIMAL_ALWAYS_SHOWN); + if (1) { + return CFRetain(n ? kCFBooleanTrue : kCFBooleanFalse); + } + } else if (kCFNumberFormatterGroupingSeparator == key) { + cnt = unum_getSymbol(formatter->_nf, UNUM_GROUPING_SEPARATOR_SYMBOL, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { + return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); + } + } else if (kCFNumberFormatterUseGroupingSeparator == key) { + n = unum_getAttribute(formatter->_nf, UNUM_GROUPING_USED); + if (1) { + return CFRetain(n ? kCFBooleanTrue : kCFBooleanFalse); + } + } else if (kCFNumberFormatterPercentSymbol == key) { + cnt = unum_getSymbol(formatter->_nf, UNUM_PERCENT_SYMBOL, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { + return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); + } + } else if (kCFNumberFormatterZeroSymbol == key) { + return formatter->_zeroSym ? CFRetain(formatter->_zeroSym) : NULL; + } else if (kCFNumberFormatterNaNSymbol == key) { + cnt = unum_getSymbol(formatter->_nf, UNUM_NAN_SYMBOL, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { + return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); + } + } else if (kCFNumberFormatterInfinitySymbol == key) { + cnt = unum_getSymbol(formatter->_nf, UNUM_INFINITY_SYMBOL, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { + return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); + } + } else if (kCFNumberFormatterMinusSign == key) { + cnt = unum_getSymbol(formatter->_nf, UNUM_MINUS_SIGN_SYMBOL, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { + return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); + } + } else if (kCFNumberFormatterPlusSign == key) { + cnt = unum_getSymbol(formatter->_nf, UNUM_PLUS_SIGN_SYMBOL, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { + return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); + } + } else if (kCFNumberFormatterCurrencySymbol == key) { + cnt = unum_getSymbol(formatter->_nf, UNUM_CURRENCY_SYMBOL, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { + return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); + } + } else if (kCFNumberFormatterExponentSymbol == key) { + cnt = unum_getSymbol(formatter->_nf, UNUM_EXPONENTIAL_SYMBOL, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { + return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); + } + } else if (kCFNumberFormatterMinIntegerDigits == key) { + n = unum_getAttribute(formatter->_nf, UNUM_MIN_INTEGER_DIGITS); + if (1) { + return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); + } + } else if (kCFNumberFormatterMaxIntegerDigits == key) { + n = unum_getAttribute(formatter->_nf, UNUM_MAX_INTEGER_DIGITS); + if (1) { + return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); + } + } else if (kCFNumberFormatterMinFractionDigits == key) { + n = unum_getAttribute(formatter->_nf, UNUM_MIN_FRACTION_DIGITS); + if (1) { + return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); + } + } else if (kCFNumberFormatterMaxFractionDigits == key) { + n = unum_getAttribute(formatter->_nf, UNUM_MAX_FRACTION_DIGITS); + if (1) { + return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); + } + } else if (kCFNumberFormatterGroupingSize == key) { + n = unum_getAttribute(formatter->_nf, UNUM_GROUPING_SIZE); + if (1) { + return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); + } + } else if (kCFNumberFormatterSecondaryGroupingSize == key) { + n = unum_getAttribute(formatter->_nf, UNUM_SECONDARY_GROUPING_SIZE); + if (1) { + return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); + } + } else if (kCFNumberFormatterRoundingMode == key) { + n = unum_getAttribute(formatter->_nf, UNUM_ROUNDING_MODE); + if (1) { + return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); + } + } else if (kCFNumberFormatterRoundingIncrement == key) { + d = unum_getDoubleAttribute(formatter->_nf, UNUM_ROUNDING_INCREMENT); + if (1) { + return CFNumberCreate(CFGetAllocator(formatter), kCFNumberDoubleType, &d); + } + } else if (kCFNumberFormatterFormatWidth == key) { + n = unum_getAttribute(formatter->_nf, UNUM_FORMAT_WIDTH); + if (1) { + return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); + } + } else if (kCFNumberFormatterPaddingPosition == key) { + n = unum_getAttribute(formatter->_nf, UNUM_PADDING_POSITION); + if (1) { + return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); + } + } else if (kCFNumberFormatterPaddingCharacter == key) { + cnt = unum_getTextAttribute(formatter->_nf, UNUM_PADDING_CHARACTER, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { + return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); + } + } else if (kCFNumberFormatterDefaultFormat == key) { + return formatter->_defformat ? CFRetain(formatter->_defformat) : NULL; + } else if (kCFNumberFormatterMultiplier == key) { + return formatter->_multiplier ? CFRetain(formatter->_multiplier) : NULL; + } else if (kCFNumberFormatterPositivePrefix == key) { + cnt = unum_getTextAttribute(formatter->_nf, UNUM_POSITIVE_PREFIX, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { + return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); + } + } else if (kCFNumberFormatterPositiveSuffix == key) { + cnt = unum_getTextAttribute(formatter->_nf, UNUM_POSITIVE_SUFFIX, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { + return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); + } + } else if (kCFNumberFormatterNegativePrefix == key) { + cnt = unum_getTextAttribute(formatter->_nf, UNUM_NEGATIVE_PREFIX, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { + return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); + } + } else if (kCFNumberFormatterNegativeSuffix == key) { + cnt = unum_getTextAttribute(formatter->_nf, UNUM_NEGATIVE_SUFFIX, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { + return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); + } + } else if (kCFNumberFormatterPerMillSymbol == key) { + cnt = unum_getSymbol(formatter->_nf, UNUM_PERMILL_SYMBOL, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { + return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); + } + } else if (kCFNumberFormatterInternationalCurrencySymbol == key) { + cnt = unum_getSymbol(formatter->_nf, UNUM_INTL_CURRENCY_SYMBOL, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { + return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); + } + } else if (kCFNumberFormatterCurrencyGroupingSeparator == key) { + cnt = unum_getSymbol(formatter->_nf, UNUM_MONETARY_GROUPING_SEPARATOR_SYMBOL, ubuffer, BUFFER_SIZE, &status); + if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { + return CFStringCreateWithCharacters(CFGetAllocator(formatter), (const UniChar *)ubuffer, cnt); + } + } else if (kCFNumberFormatterIsLenient == key) { + n = unum_getAttribute(formatter->_nf, UNUM_LENIENT_PARSE); + if (1) { + return CFRetain(n ? kCFBooleanTrue : kCFBooleanFalse); + } + } else if (kCFNumberFormatterUseSignificantDigits == key) { + n = unum_getAttribute(formatter->_nf, UNUM_SIGNIFICANT_DIGITS_USED); + if (1) { + return CFRetain(n ? kCFBooleanTrue : kCFBooleanFalse); + } + } else if (kCFNumberFormatterMinSignificantDigits == key) { + n = unum_getAttribute(formatter->_nf, UNUM_MIN_SIGNIFICANT_DIGITS); + if (1) { + return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); + } + } else if (kCFNumberFormatterMaxSignificantDigits == key) { + n = unum_getAttribute(formatter->_nf, UNUM_MAX_SIGNIFICANT_DIGITS); + if (1) { + return CFNumberCreate(CFGetAllocator(formatter), kCFNumberSInt32Type, &n); + } + } else { + CFAssert3(0, __kCFLogAssertion, "%s(): unknown key %p (%@)", __PRETTY_FUNCTION__, key, key); + } + return NULL; +} + +CONST_STRING_DECL(kCFNumberFormatterCurrencyCode, "kCFNumberFormatterCurrencyCode") +CONST_STRING_DECL(kCFNumberFormatterDecimalSeparator, "kCFNumberFormatterDecimalSeparator") +CONST_STRING_DECL(kCFNumberFormatterCurrencyDecimalSeparator, "kCFNumberFormatterCurrencyDecimalSeparator") +CONST_STRING_DECL(kCFNumberFormatterAlwaysShowDecimalSeparator, "kCFNumberFormatterAlwaysShowDecimalSeparator") +CONST_STRING_DECL(kCFNumberFormatterGroupingSeparator, "kCFNumberFormatterGroupingSeparator") +CONST_STRING_DECL(kCFNumberFormatterUseGroupingSeparator, "kCFNumberFormatterUseGroupingSeparator") +CONST_STRING_DECL(kCFNumberFormatterPercentSymbol, "kCFNumberFormatterPercentSymbol") +CONST_STRING_DECL(kCFNumberFormatterZeroSymbol, "kCFNumberFormatterZeroSymbol") +CONST_STRING_DECL(kCFNumberFormatterNaNSymbol, "kCFNumberFormatterNaNSymbol") +CONST_STRING_DECL(kCFNumberFormatterInfinitySymbol, "kCFNumberFormatterInfinitySymbol") +CONST_STRING_DECL(kCFNumberFormatterMinusSign, "kCFNumberFormatterMinusSignSymbol") +CONST_STRING_DECL(kCFNumberFormatterPlusSign, "kCFNumberFormatterPlusSignSymbol") +CONST_STRING_DECL(kCFNumberFormatterCurrencySymbol, "kCFNumberFormatterCurrencySymbol") +CONST_STRING_DECL(kCFNumberFormatterExponentSymbol, "kCFNumberFormatterExponentSymbol") +CONST_STRING_DECL(kCFNumberFormatterMinIntegerDigits, "kCFNumberFormatterMinIntegerDigits") +CONST_STRING_DECL(kCFNumberFormatterMaxIntegerDigits, "kCFNumberFormatterMaxIntegerDigits") +CONST_STRING_DECL(kCFNumberFormatterMinFractionDigits, "kCFNumberFormatterMinFractionDigits") +CONST_STRING_DECL(kCFNumberFormatterMaxFractionDigits, "kCFNumberFormatterMaxFractionDigits") +CONST_STRING_DECL(kCFNumberFormatterGroupingSize, "kCFNumberFormatterGroupingSize") +CONST_STRING_DECL(kCFNumberFormatterSecondaryGroupingSize, "kCFNumberFormatterSecondaryGroupingSize") +CONST_STRING_DECL(kCFNumberFormatterRoundingMode, "kCFNumberFormatterRoundingMode") +CONST_STRING_DECL(kCFNumberFormatterRoundingIncrement, "kCFNumberFormatterRoundingIncrement") +CONST_STRING_DECL(kCFNumberFormatterFormatWidth, "kCFNumberFormatterFormatWidth") +CONST_STRING_DECL(kCFNumberFormatterPaddingPosition, "kCFNumberFormatterPaddingPosition") +CONST_STRING_DECL(kCFNumberFormatterPaddingCharacter, "kCFNumberFormatterPaddingCharacter") +CONST_STRING_DECL(kCFNumberFormatterDefaultFormat, "kCFNumberFormatterDefaultFormat") + +CONST_STRING_DECL(kCFNumberFormatterMultiplier, "kCFNumberFormatterMultiplier") +CONST_STRING_DECL(kCFNumberFormatterPositivePrefix, "kCFNumberFormatterPositivePrefix") +CONST_STRING_DECL(kCFNumberFormatterPositiveSuffix, "kCFNumberFormatterPositiveSuffix") +CONST_STRING_DECL(kCFNumberFormatterNegativePrefix, "kCFNumberFormatterNegativePrefix") +CONST_STRING_DECL(kCFNumberFormatterNegativeSuffix, "kCFNumberFormatterNegativeSuffix") +CONST_STRING_DECL(kCFNumberFormatterPerMillSymbol, "kCFNumberFormatterPerMillSymbol") +CONST_STRING_DECL(kCFNumberFormatterInternationalCurrencySymbol, "kCFNumberFormatterInternationalCurrencySymbol") + +CONST_STRING_DECL(kCFNumberFormatterCurrencyGroupingSeparator, "kCFNumberFormatterCurrencyGroupingSeparator") +CONST_STRING_DECL(kCFNumberFormatterIsLenient, "kCFNumberFormatterIsLenient") +CONST_STRING_DECL(kCFNumberFormatterUseSignificantDigits, "kCFNumberFormatterUseSignificantDigits") +CONST_STRING_DECL(kCFNumberFormatterMinSignificantDigits, "kCFNumberFormatterMinSignificantDigits") +CONST_STRING_DECL(kCFNumberFormatterMaxSignificantDigits, "kCFNumberFormatterMaxSignificantDigits") + +Boolean CFNumberFormatterGetDecimalInfoForCurrencyCode(CFStringRef currencyCode, int32_t *defaultFractionDigits, double *roundingIncrement) { + UChar ubuffer[4]; + __CFGenericValidateType(currencyCode, CFStringGetTypeID()); + CFAssert1(3 == CFStringGetLength(currencyCode), __kCFLogAssertion, "%s(): currencyCode is not 3 characters", __PRETTY_FUNCTION__); + CFStringGetCharacters(currencyCode, CFRangeMake(0, 3), (UniChar *)ubuffer); + ubuffer[3] = 0; + UErrorCode icuStatus = U_ZERO_ERROR; + if (defaultFractionDigits) *defaultFractionDigits = ucurr_getDefaultFractionDigits(ubuffer, &icuStatus); + if (roundingIncrement) *roundingIncrement = ucurr_getRoundingIncrement(ubuffer, &icuStatus); + if (U_FAILURE(icuStatus)) + return false; + return (!defaultFractionDigits || 0 <= *defaultFractionDigits) && (!roundingIncrement || 0.0 <= *roundingIncrement); +} + +#undef BUFFER_SIZE +
diff --git a/CoreFoundation/CFNumberFormatter.h b/CoreFoundation/CFNumberFormatter.h new file mode 100644 index 0000000..e453cb9 --- /dev/null +++ b/CoreFoundation/CFNumberFormatter.h
@@ -0,0 +1,201 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFNumberFormatter.h + Copyright (c) 2003-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFNUMBERFORMATTER__) +#define __COREFOUNDATION_CFNUMBERFORMATTER__ 1 + +#include <CoreFoundation/CFBase.h> +#include <CoreFoundation/CFNumber.h> +#include <CoreFoundation/CFLocale.h> + +#if MAC_OS_X_VERSION_10_3 <= MAC_OS_X_VERSION_MAX_ALLOWED + +CF_EXTERN_C_BEGIN + +typedef struct __CFNumberFormatter *CFNumberFormatterRef; + +// CFNumberFormatters are not thread-safe. Do not use one from multiple threads! + +CF_EXPORT +CFTypeID CFNumberFormatterGetTypeID(void) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + +enum { // number format styles + kCFNumberFormatterNoStyle = 0, + kCFNumberFormatterDecimalStyle = 1, + kCFNumberFormatterCurrencyStyle = 2, + kCFNumberFormatterPercentStyle = 3, + kCFNumberFormatterScientificStyle = 4, + kCFNumberFormatterSpellOutStyle = 5 +}; +typedef CFIndex CFNumberFormatterStyle; + + +CF_EXPORT +CFNumberFormatterRef CFNumberFormatterCreate(CFAllocatorRef allocator, CFLocaleRef locale, CFNumberFormatterStyle style) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + // Returns a CFNumberFormatter, localized to the given locale, which + // will format numbers to the given style. + +CF_EXPORT +CFLocaleRef CFNumberFormatterGetLocale(CFNumberFormatterRef formatter) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + +CF_EXPORT +CFNumberFormatterStyle CFNumberFormatterGetStyle(CFNumberFormatterRef formatter) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + // Get the properties with which the number formatter was created. + +CF_EXPORT +CFStringRef CFNumberFormatterGetFormat(CFNumberFormatterRef formatter) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + +CF_EXPORT +void CFNumberFormatterSetFormat(CFNumberFormatterRef formatter, CFStringRef formatString) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + // Set the format description string of the number formatter. This + // overrides the style settings. The format of the format string + // is as defined by the ICU library, and is similar to that found + // in Microsoft Excel and NSNumberFormatter (and Java I believe). + // The number formatter starts with a default format string defined + // by the style argument with which it was created. + + +CF_EXPORT +CFStringRef CFNumberFormatterCreateStringWithNumber(CFAllocatorRef allocator, CFNumberFormatterRef formatter, CFNumberRef number) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + +CF_EXPORT +CFStringRef CFNumberFormatterCreateStringWithValue(CFAllocatorRef allocator, CFNumberFormatterRef formatter, CFNumberType numberType, const void *valuePtr) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + // Create a string representation of the given number or value + // using the current state of the number formatter. + + +enum { + kCFNumberFormatterParseIntegersOnly = 1 /* only parse integers */ +}; +typedef CFOptionFlags CFNumberFormatterOptionFlags; + +CF_EXPORT +CFNumberRef CFNumberFormatterCreateNumberFromString(CFAllocatorRef allocator, CFNumberFormatterRef formatter, CFStringRef string, CFRange *rangep, CFOptionFlags options) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + +CF_EXPORT +Boolean CFNumberFormatterGetValueFromString(CFNumberFormatterRef formatter, CFStringRef string, CFRange *rangep, CFNumberType numberType, void *valuePtr) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + // Parse a string representation of a number using the current state + // of the number formatter. The range parameter specifies the range + // of the string in which the parsing should occur in input, and on + // output indicates the extent that was used; this parameter can + // be NULL, in which case the whole string may be used. The + // return value indicates whether some number was computed and + // (if valuePtr is not NULL) stored at the location specified by + // valuePtr. The numberType indicates the type of value pointed + // to by valuePtr. + + +CF_EXPORT +void CFNumberFormatterSetProperty(CFNumberFormatterRef formatter, CFStringRef key, CFTypeRef value) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + +CF_EXPORT +CFTypeRef CFNumberFormatterCopyProperty(CFNumberFormatterRef formatter, CFStringRef key) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + // Set and get various properties of the number formatter, the set of + // which may be expanded in the future. + +CF_EXPORT const CFStringRef kCFNumberFormatterCurrencyCode AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFString +CF_EXPORT const CFStringRef kCFNumberFormatterDecimalSeparator AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFString +CF_EXPORT const CFStringRef kCFNumberFormatterCurrencyDecimalSeparator AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFString +CF_EXPORT const CFStringRef kCFNumberFormatterAlwaysShowDecimalSeparator AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFBoolean +CF_EXPORT const CFStringRef kCFNumberFormatterGroupingSeparator AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFString +CF_EXPORT const CFStringRef kCFNumberFormatterUseGroupingSeparator AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFBoolean +CF_EXPORT const CFStringRef kCFNumberFormatterPercentSymbol AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFString +CF_EXPORT const CFStringRef kCFNumberFormatterZeroSymbol AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFString +CF_EXPORT const CFStringRef kCFNumberFormatterNaNSymbol AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFString +CF_EXPORT const CFStringRef kCFNumberFormatterInfinitySymbol AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFString +CF_EXPORT const CFStringRef kCFNumberFormatterMinusSign AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFString +CF_EXPORT const CFStringRef kCFNumberFormatterPlusSign AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFString +CF_EXPORT const CFStringRef kCFNumberFormatterCurrencySymbol AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFString +CF_EXPORT const CFStringRef kCFNumberFormatterExponentSymbol AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFString +CF_EXPORT const CFStringRef kCFNumberFormatterMinIntegerDigits AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFNumber +CF_EXPORT const CFStringRef kCFNumberFormatterMaxIntegerDigits AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFNumber +CF_EXPORT const CFStringRef kCFNumberFormatterMinFractionDigits AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFNumber +CF_EXPORT const CFStringRef kCFNumberFormatterMaxFractionDigits AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFNumber +CF_EXPORT const CFStringRef kCFNumberFormatterGroupingSize AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFNumber +CF_EXPORT const CFStringRef kCFNumberFormatterSecondaryGroupingSize AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFNumber +CF_EXPORT const CFStringRef kCFNumberFormatterRoundingMode AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFNumber +CF_EXPORT const CFStringRef kCFNumberFormatterRoundingIncrement AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFNumber +CF_EXPORT const CFStringRef kCFNumberFormatterFormatWidth AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFNumber +CF_EXPORT const CFStringRef kCFNumberFormatterPaddingPosition AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFNumber +CF_EXPORT const CFStringRef kCFNumberFormatterPaddingCharacter AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFString +CF_EXPORT const CFStringRef kCFNumberFormatterDefaultFormat AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // CFString +CF_EXPORT const CFStringRef kCFNumberFormatterMultiplier AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; // CFNumber +CF_EXPORT const CFStringRef kCFNumberFormatterPositivePrefix AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; // CFString +CF_EXPORT const CFStringRef kCFNumberFormatterPositiveSuffix AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; // CFString +CF_EXPORT const CFStringRef kCFNumberFormatterNegativePrefix AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; // CFString +CF_EXPORT const CFStringRef kCFNumberFormatterNegativeSuffix AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; // CFString +CF_EXPORT const CFStringRef kCFNumberFormatterPerMillSymbol AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; // CFString +CF_EXPORT const CFStringRef kCFNumberFormatterInternationalCurrencySymbol AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; // CFString +CF_EXPORT const CFStringRef kCFNumberFormatterCurrencyGroupingSeparator AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; // CFString +CF_EXPORT const CFStringRef kCFNumberFormatterIsLenient AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; // CFBoolean +CF_EXPORT const CFStringRef kCFNumberFormatterUseSignificantDigits AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; // CFBoolean +CF_EXPORT const CFStringRef kCFNumberFormatterMinSignificantDigits AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; // CFNumber +CF_EXPORT const CFStringRef kCFNumberFormatterMaxSignificantDigits AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; // CFNumber + +enum { + kCFNumberFormatterRoundCeiling = 0, + kCFNumberFormatterRoundFloor = 1, + kCFNumberFormatterRoundDown = 2, + kCFNumberFormatterRoundUp = 3, + kCFNumberFormatterRoundHalfEven = 4, + kCFNumberFormatterRoundHalfDown = 5, + kCFNumberFormatterRoundHalfUp = 6 +}; +typedef CFIndex CFNumberFormatterRoundingMode; + +enum { + kCFNumberFormatterPadBeforePrefix = 0, + kCFNumberFormatterPadAfterPrefix = 1, + kCFNumberFormatterPadBeforeSuffix = 2, + kCFNumberFormatterPadAfterSuffix = 3 +}; +typedef CFIndex CFNumberFormatterPadPosition; + + +CF_EXPORT +Boolean CFNumberFormatterGetDecimalInfoForCurrencyCode(CFStringRef currencyCode, int32_t *defaultFractionDigits, double *roundingIncrement) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + // Returns the number of fraction digits that should be displayed, and + // the rounding increment (or 0.0 if no rounding is done by the currency) + // for the given currency. Returns false if the currency code is unknown + // or the information is not available. + // Not localized because these are properties of the currency. + +CF_EXTERN_C_END + +#endif + +#endif /* ! __COREFOUNDATION_CFNUMBERFORMATTER__ */ +
diff --git a/CoreFoundation/CFPlatform.c b/CoreFoundation/CFPlatform.c new file mode 100644 index 0000000..23fefd6 --- /dev/null +++ b/CoreFoundation/CFPlatform.c
@@ -0,0 +1,558 @@ +/* + * 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@ + */ +/* CFPlatform.c + Copyright 1999-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include "CFInternal.h" +#include "CFPriv.h" +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#if DEPLOYMENT_TARGET_WINDOWS +/* In typical fragile fashion, order of include on Windows is important */ +#include <io.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <windows.h> +#include <tchar.h> +#ifndef _WIN32_IE +#define _WIN32_IE 0x0600 +#endif +#define strdup _strdup +#include <shlobj.h> +extern size_t strlcpy(char *dst, const char *src, size_t siz); +extern size_t strlcat(char *dst, const char *src, size_t siz); +#ifndef SHGFP_TYPE_CURRENT +#define SHGFP_TYPE_CURRENT 0 +#endif +#else +#include <sys/stat.h> +#include <unistd.h> +#include <pwd.h> +#include <stdio.h> +#endif +#if DEPLOYMENT_TARGET_MACOSX +#include <mach-o/dyld.h> +#include <crt_externs.h> +#endif + +#if DEPLOYMENT_TARGET_MACOSX +#define kCFPlatformInterfaceStringEncoding kCFStringEncodingUTF8 +#else +#define kCFPlatformInterfaceStringEncoding CFStringGetSystemEncoding() +#endif + +static CFStringRef _CFUserName(void); + +#if DEPLOYMENT_TARGET_MACOSX +// CoreGraphics and LaunchServices are only projects (1 Dec 2006) that use these +char **_CFArgv(void) { return *_NSGetArgv(); } +int _CFArgc(void) { return *_NSGetArgc(); } +#endif + + +__private_extern__ Boolean _CFGetCurrentDirectory(char *path, int maxlen) { +#if DEPLOYMENT_TARGET_WINDOWS || 0 + DWORD len = GetCurrentDirectoryA(maxlen, path); + return ((0 != len) && (maxlen > 0) && (len + 1 <= (DWORD)maxlen)); +#else + return getcwd(path, maxlen) != NULL; +#endif +} + +static Boolean __CFIsCFM = false; + +// If called super early, we just return false +__private_extern__ Boolean _CFIsCFM(void) { + return __CFIsCFM; +} + +#if DEPLOYMENT_TARGET_WINDOWS || 0 +#define PATH_SEP '\\' +#else +#define PATH_SEP '/' +#endif + +#if DEPLOYMENT_TARGET_WINDOWS +#define PATH_LIST_SEP ';' +#else +#define PATH_LIST_SEP ':' +#endif + +static char *_CFSearchForNameInPath(const char *name, char *path) { + struct stat statbuf; +#if DEPLOYMENT_TARGET_WINDOWS && !defined(__GNUC__) + char nname[MAX_PATH + 1]; +#else + char nname[strlen(name) + strlen(path) + 2]; +#endif +#if !DEPLOYMENT_TARGET_WINDOWS + int no_hang_fd = open("/dev/autofs_nowait", 0); +#endif + for (;;) { + char *p = (char *)strchr(path, PATH_LIST_SEP); + if (NULL != p) { + *p = '\0'; + } + nname[0] = '\0'; + strlcat(nname, path, sizeof(nname)); + strlcat(nname, "/", sizeof(nname)); + strlcat(nname, name, sizeof(nname)); + // Could also do access(us, X_OK) == 0 in next condition, + // for executable-only searching + if (0 == stat(nname, &statbuf) && (statbuf.st_mode & S_IFMT) == S_IFREG) { + if (p != NULL) { + *p = PATH_LIST_SEP; + } +#if !DEPLOYMENT_TARGET_WINDOWS + close(no_hang_fd); +#endif + return strdup(nname); + } + if (NULL == p) { + break; + } + *p = PATH_LIST_SEP; + path = p + 1; + } +#if !DEPLOYMENT_TARGET_WINDOWS + close(no_hang_fd); +#endif + return NULL; +} + +#if DEPLOYMENT_TARGET_WINDOWS +// Returns the path to the CF DLL, which we can then use to find resources like char sets + +__private_extern__ const char* _CFDLLPath(void) { + static char cachedPath[MAX_PATH+1] = ""; + + if ('\0' == cachedPath[0]) { +#if defined(DEBUG) + char* DLLFileName = "CoreFoundation_debug"; +#elif defined(PROFILE) + char* DLLFileName = "CoreFoundation_profile"; +#else + char* DLLFileName = "CoreFoundation"; +#endif + HMODULE ourModule = GetModuleHandleA(DLLFileName); + CFAssert(ourModule, __kCFLogAssertion, "GetModuleHandle failed"); + + DWORD wResult = GetModuleFileNameA(ourModule, cachedPath, MAX_PATH+1); + CFAssert1(wResult > 0, __kCFLogAssertion, "GetModuleFileName failed: %d", GetLastError()); + CFAssert1(wResult < MAX_PATH+1, __kCFLogAssertion, "GetModuleFileName result truncated: %s", cachedPath); + + // strip off last component, the DLL name + CFIndex idx; + for (idx = wResult - 1; idx; idx--) { + if ('\\' == cachedPath[idx]) { + cachedPath[idx] = '\0'; + break; + } + } + } + return cachedPath; +} +#endif + +static const char *__CFProcessPath = NULL; +static const char *__CFprogname = NULL; + +const char **_CFGetProgname(void) { + if (!__CFprogname) + _CFProcessPath(); // sets up __CFprogname as a side-effect + return &__CFprogname; +} + +const char **_CFGetProcessPath(void) { + if (!__CFProcessPath) + _CFProcessPath(); // sets up __CFProcessPath as a side-effect + return &__CFProcessPath; +} + +const char *_CFProcessPath(void) { + if (__CFProcessPath) return __CFProcessPath; + + char *thePath = NULL; +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX + if (!_CFIsSetUgid()) { +#else + if (!__CFProcessPath) { +#endif + thePath = getenv("CFProcessPath"); + if (thePath) { + int len = (int)strlen(thePath); + __CFProcessPath = (const char *)CFAllocatorAllocate(kCFAllocatorSystemDefault, len+1, 0); + if (__CFOASafe) __CFSetLastAllocationEventName((void *)__CFProcessPath, "CFUtilities (process-path)"); + memmove((char *)__CFProcessPath, thePath, len + 1); + } + } +#if DEPLOYMENT_TARGET_WINDOWS + if (!__CFProcessPath) { + char buf[CFMaxPathSize] = {0}; + HINSTANCE hinst = GetModuleHandle(NULL); + DWORD rlen = hinst ? GetModuleFileNameA(hinst, buf, CFMaxPathSize) : 0; + thePath = rlen ? buf : NULL; +#elif DEPLOYMENT_TARGET_MACOSX + int execIndex = 0; + + if (!__CFProcessPath && NULL != (*_NSGetArgv())[execIndex]) { + int no_hang_fd = open("/dev/autofs_nowait", 0); + char buf[CFMaxPathSize] = {0}; + struct stat statbuf; + const char *arg0 = (*_NSGetArgv())[execIndex]; + if (arg0[0] == '/') { + // We've got an absolute path; look no further; + thePath = (char *)arg0; + } else { + char *theList = getenv("PATH"); + if (NULL != theList && NULL == strrchr(arg0, '/')) { + thePath = _CFSearchForNameInPath(arg0, theList); + if (thePath) { + // User could have "." or "../bin" or other relative path in $PATH + if (('/' != thePath[0]) && _CFGetCurrentDirectory(buf, CFMaxPathSize)) { + strlcat(buf, "/", sizeof(buf)); + strlcat(buf, thePath, sizeof(buf)); + if (0 == stat(buf, &statbuf)) { + free(thePath); + thePath = buf; + } + } + if (thePath != buf) { + strlcpy(buf, thePath, sizeof(buf)); + free((void *)thePath); + thePath = buf; + } + } + } + } + + // After attempting a search through $PATH, if existant, + // try prepending the current directory to argv[0]. + if (!thePath && _CFGetCurrentDirectory(buf, CFMaxPathSize)) { + if (buf[strlen(buf)-1] != '/') { + strlcat(buf, "/", sizeof(buf)); + } + strlcat(buf, arg0, CFMaxPathSize); + if (0 == stat(buf, &statbuf)) { + thePath = buf; + } + } + + if (thePath) { + // We are going to process the buffer replacing all "/./" and "//" with "/" + CFIndex srcIndex = 0, dstIndex = 0; + CFIndex len = strlen(thePath); + for (srcIndex=0; srcIndex<len; srcIndex++) { + thePath[dstIndex] = thePath[srcIndex]; + dstIndex++; + while (srcIndex < len-1 && thePath[srcIndex] == '/' && (thePath[srcIndex+1] == '/' || (thePath[srcIndex+1] == '.' && srcIndex < len-2 && thePath[srcIndex+2] == '/'))) srcIndex += (thePath[srcIndex+1] == '/' ? 1 : 2); + } + thePath[dstIndex] = 0; + } + if (!thePath) { + thePath = (*_NSGetArgv())[execIndex]; + } +#elif DEPLOYMENT_TARGET_LINUX + if (!__CFProcessPath) { + pid_t pid = getpid(); + char buf[2][CFMaxPathSize] = {{0}, {0}}; + int n; + + n = snprintf(buf[0], CFMaxPathSize, "/proc/%u/exe", pid); + + if (n > 0) { + n = readlink(buf[0], buf[1], CFMaxPathLength); + if (n > 0 && n <= CFMaxPathLength) { + buf[1][n] = '\0'; + thePath = buf[1]; + } + } +#else +#error "Don't know how to compute the process path for this platform." +#endif + + if (thePath) { + int len = (int)strlen(thePath); + __CFProcessPath = (const char *)CFAllocatorAllocate(kCFAllocatorSystemDefault, len + 1, 0); + if (__CFOASafe) __CFSetLastAllocationEventName((void *)__CFProcessPath, "CFUtilities (process-path)"); + memmove((char *)__CFProcessPath, thePath, len + 1); + } + if (__CFProcessPath) { + const char *p = 0; + int i; + for (i = 0; __CFProcessPath[i] != 0; i++){ + if (__CFProcessPath[i] == PATH_SEP) + p = __CFProcessPath + i; + } + if (p != 0) + __CFprogname = p + 1; + else + __CFprogname = __CFProcessPath; + } + +#if DEPLOYMENT_TARGET_MACOSX + close(no_hang_fd); +#endif + + } + + if (!__CFProcessPath) { + __CFProcessPath = ""; + __CFprogname = __CFProcessPath; + } else { + const char *p = 0; + int i; + for (i = 0; __CFProcessPath[i] != 0; i++){ + if (__CFProcessPath[i] == PATH_SEP) + p = __CFProcessPath + i; + } + if (p != 0) + __CFprogname = p + 1; + else + __CFprogname = __CFProcessPath; + } + return __CFProcessPath; +} + +__private_extern__ CFStringRef _CFProcessNameString(void) { + static CFStringRef __CFProcessNameString = NULL; + if (!__CFProcessNameString) { + const char *processName = *_CFGetProgname(); + if (!processName) processName = ""; + __CFProcessNameString = CFStringCreateWithCString(__CFGetDefaultAllocator(), processName, kCFPlatformInterfaceStringEncoding); + } + return __CFProcessNameString; +} + +static CFStringRef __CFUserName = NULL; + +#if (DEPLOYMENT_TARGET_MACOSX) || defined(__svr4__) || defined(__hpux__) || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD +static CFURLRef __CFHomeDirectory = NULL; +static uint32_t __CFEUID = -1; +static uint32_t __CFUID = -1; + +static CFURLRef _CFCopyHomeDirURLForUser(struct passwd *upwd) { + CFURLRef home = NULL; + if (!_CFIsSetUgid()) { + const char *path = getenv("CFFIXED_USER_HOME"); + if (path) { + home = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, (uint8_t *)path, strlen(path), true); + } + } + if (!home) { + if (upwd && upwd->pw_dir) { + home = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, (uint8_t *)upwd->pw_dir, strlen(upwd->pw_dir), true); + } + } + return home; +} + +static void _CFUpdateUserInfo(void) { + struct passwd *upwd; + + __CFEUID = geteuid(); + __CFUID = getuid(); + if (__CFHomeDirectory) CFRelease(__CFHomeDirectory); + __CFHomeDirectory = NULL; + if (__CFUserName) CFRelease(__CFUserName); + __CFUserName = NULL; + + upwd = getpwuid(__CFEUID ? __CFEUID : __CFUID); + __CFHomeDirectory = _CFCopyHomeDirURLForUser(upwd); + if (!__CFHomeDirectory) { + const char *cpath = getenv("HOME"); + if (cpath) { + __CFHomeDirectory = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, (uint8_t *)cpath, strlen(cpath), true); + } + } + + // This implies that UserManager stores directory info in CString + // rather than FileSystemRep. Perhaps this is wrong & we should + // expect NeXTSTEP encodings. A great test of our localized system would + // be to have a user "O-umlat z e r". XXX + if (upwd && upwd->pw_name) { + __CFUserName = CFStringCreateWithCString(kCFAllocatorSystemDefault, upwd->pw_name, kCFPlatformInterfaceStringEncoding); + } else { + const char *cuser = getenv("USER"); + if (cuser) + __CFUserName = CFStringCreateWithCString(kCFAllocatorSystemDefault, cuser, kCFPlatformInterfaceStringEncoding); + } +} +#endif + +static CFURLRef _CFCreateHomeDirectoryURLForUser(CFStringRef uName) { +#if (DEPLOYMENT_TARGET_MACOSX) || defined(__svr4__) || defined(__hpux__) || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD + if (!uName) { + if (geteuid() != __CFEUID || getuid() != __CFUID || !__CFHomeDirectory) + _CFUpdateUserInfo(); + if (__CFHomeDirectory) CFRetain(__CFHomeDirectory); + return __CFHomeDirectory; + } else { + struct passwd *upwd = NULL; + char buf[128], *user; + SInt32 len = CFStringGetLength(uName), size = CFStringGetMaximumSizeForEncoding(len, kCFPlatformInterfaceStringEncoding); + CFIndex usedSize; + if (size < 127) { + user = buf; + } else { + user = (char*)CFAllocatorAllocate(kCFAllocatorSystemDefault, size+1, 0); + if (__CFOASafe) __CFSetLastAllocationEventName(user, "CFUtilities (temp)"); + } + if (CFStringGetBytes(uName, CFRangeMake(0, len), kCFPlatformInterfaceStringEncoding, 0, true, (uint8_t *)user, size, &usedSize) == len) { + user[usedSize] = '\0'; + upwd = getpwnam(user); + } + if (buf != user) { + CFAllocatorDeallocate(kCFAllocatorSystemDefault, user); + } + return _CFCopyHomeDirURLForUser(upwd); + } +#elif DEPLOYMENT_TARGET_WINDOWS + CFStringRef user = !uName ? _CFUserName() : uName; + CFURLRef home = NULL; + + if (!uName || CFEqual(user, _CFUserName())) { + const char *cpath = getenv("HOMEPATH"); + const char *cdrive = getenv("HOMEDRIVE"); + if (cdrive && cpath) { + char fullPath[CFMaxPathSize]; + CFStringRef str; + strlcpy(fullPath, cdrive, sizeof(fullPath)); + strlcat(fullPath, cpath, sizeof(fullPath)); + str = CFStringCreateWithCString(kCFAllocatorSystemDefault, fullPath, kCFPlatformInterfaceStringEncoding); + home = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str, kCFURLWindowsPathStyle, true); + CFRelease(str); + } + } + if (home == NULL) { + UniChar pathChars[MAX_PATH]; + if (S_OK == SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, (LPWSTR) pathChars)) { + UniChar* p = pathChars; + CFIndex len = 0; + CFStringRef str; + while (*p++ != 0) + ++len; + str = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, pathChars, len); + home = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str, kCFURLWindowsPathStyle, true); + CFRelease(str); + } else { + // We have to get "some" directory location, so fall-back to the + // processes current directory. + UniChar currDir[MAX_PATH]; + DWORD dwChars = GetCurrentDirectory(MAX_PATH + 1, (LPWSTR)currDir); + if (dwChars > 0) { + UniChar* p = currDir; + CFIndex len = 0; + CFStringRef str; + while (*p++ != 0) + ++len; + str = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, currDir, len); + home = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str, kCFURLWindowsPathStyle, true); + } + } + } + // We could do more here (as in KB Article Q101507). If that article is to + // be believed, we should only run into this case on Win95, or through + // user error. + if (home) { + CFStringRef str = CFURLCopyFileSystemPath(home, kCFURLWindowsPathStyle); + if (str && CFStringGetLength(str) == 0) { + CFRelease(home); + home=NULL; + } + if (str) CFRelease(str); + } + return home; +#else +#error Dont know how to compute users home directories on this platform +#endif +} + +static CFStringRef _CFUserName(void) { +#if (DEPLOYMENT_TARGET_MACOSX) || defined(__svr4__) || defined(__hpux__) || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD + if (geteuid() != __CFEUID || getuid() != __CFUID) + _CFUpdateUserInfo(); +#elif DEPLOYMENT_TARGET_WINDOWS + if (!__CFUserName) { + char username[1040]; + DWORD size = 1040; + username[0] = 0; + if (GetUserNameA(username, &size)) { + __CFUserName = CFStringCreateWithCString(kCFAllocatorSystemDefault, username, kCFPlatformInterfaceStringEncoding); + } else { + const char *cname = getenv("USERNAME"); + if (cname) + __CFUserName = CFStringCreateWithCString(kCFAllocatorSystemDefault, cname, kCFPlatformInterfaceStringEncoding); + } + } +#else +#error Dont know how to compute user name on this platform +#endif + if (!__CFUserName) + __CFUserName = (CFStringRef)CFRetain(CFSTR("")); + return __CFUserName; +} + +__private_extern__ CFStringRef _CFGetUserName(void) { + return CFStringCreateCopy(kCFAllocatorSystemDefault, _CFUserName()); +} + +#define CFMaxHostNameLength 256 +#define CFMaxHostNameSize (CFMaxHostNameLength+1) + +__private_extern__ CFStringRef _CFStringCreateHostName(void) { + char myName[CFMaxHostNameSize]; + + // return @"" instead of nil a la CFUserName() and Ali Ozer + if (0 != gethostname(myName, CFMaxHostNameSize)) myName[0] = '\0'; + return CFStringCreateWithCString(kCFAllocatorSystemDefault, myName, kCFPlatformInterfaceStringEncoding); +} + +/* These are sanitized versions of the above functions. We might want to eliminate the above ones someday. + These can return NULL. +*/ +CF_EXPORT CFStringRef CFGetUserName(void) { + return _CFUserName(); +} + +CF_EXPORT CFURLRef CFCopyHomeDirectoryURLForUser(CFStringRef uName) { + return _CFCreateHomeDirectoryURLForUser(uName); +} + +#undef CFMaxHostNameLength +#undef CFMaxHostNameSize +
diff --git a/CoreFoundation/CFPlugIn.c b/CoreFoundation/CFPlugIn.c new file mode 100644 index 0000000..80eb2f4 --- /dev/null +++ b/CoreFoundation/CFPlugIn.c
@@ -0,0 +1,185 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFPlugIn.c + Copyright (c) 1999-2007 Apple Inc. All rights reserved. + Responsibility: Doug Davidson +*/ + +#include "CFBundle_Internal.h" +#include "CFInternal.h" + +CONST_STRING_DECL(kCFPlugInDynamicRegistrationKey, "CFPlugInDynamicRegistration") +CONST_STRING_DECL(kCFPlugInDynamicRegisterFunctionKey, "CFPlugInDynamicRegisterFunction") +CONST_STRING_DECL(kCFPlugInUnloadFunctionKey, "CFPlugInUnloadFunction") +CONST_STRING_DECL(kCFPlugInFactoriesKey, "CFPlugInFactories") +CONST_STRING_DECL(kCFPlugInTypesKey, "CFPlugInTypes") + +__private_extern__ void __CFPlugInInitialize(void) { +} + +/* ===================== Finding factories and creating instances ===================== */ +/* For plugIn hosts. */ +/* Functions for finding factories to create specific types and actually creating instances of a type. */ + +CF_EXPORT CFArrayRef CFPlugInFindFactoriesForPlugInType(CFUUIDRef typeID) { + CFArrayRef array = _CFPFactoryFindForType(typeID); + CFMutableArrayRef result = NULL; + + if (array) { + SInt32 i, c = CFArrayGetCount(array); + + // Use default allocator + result = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); + + for (i=0; i<c; i++) { + CFArrayAppendValue(result, _CFPFactoryGetFactoryID((_CFPFactory *)CFArrayGetValueAtIndex(array, i))); + } + } + return result; +} + +CF_EXPORT CFArrayRef CFPlugInFindFactoriesForPlugInTypeInPlugIn(CFUUIDRef typeID, CFPlugInRef plugIn) { + CFArrayRef array = _CFPFactoryFindForType(typeID); + CFMutableArrayRef result = NULL; + + if (array) { + SInt32 i, c = CFArrayGetCount(array); + _CFPFactory *factory; + + // Use default allocator + result = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); + + for (i=0; i<c; i++) { + factory = (_CFPFactory *)CFArrayGetValueAtIndex(array, i); + if (_CFPFactoryGetPlugIn(factory) == plugIn) { + CFArrayAppendValue(result, _CFPFactoryGetFactoryID(factory)); + } + } + } + return result; +} + +CF_EXPORT void *CFPlugInInstanceCreate(CFAllocatorRef allocator, CFUUIDRef factoryID, CFUUIDRef typeID) { + _CFPFactory *factory = _CFPFactoryFind(factoryID, true); + void *result = NULL; + if (!factory) { + /* MF:!!! No such factory. */ + CFLog(__kCFLogPlugIn, CFSTR("Cannot find factory %@"), factoryID); + } else { + if (!_CFPFactorySupportsType(factory, typeID)) { + /* MF:!!! Factory does not support type. */ + CFLog(__kCFLogPlugIn, CFSTR("Factory %@ does not support type %@"), factoryID, typeID); + } else { + result = _CFPFactoryCreateInstance(allocator, factory, typeID); + } + } + return result; +} + +/* ===================== Registering factories and types ===================== */ +/* For plugIn writers who must dynamically register things. */ +/* Functions to register factory functions and to associate factories with types. */ + +CF_EXPORT Boolean CFPlugInRegisterFactoryFunction(CFUUIDRef factoryID, CFPlugInFactoryFunction func) { + // Create factories without plugIns from default allocator + // MF:!!! Should probably check that this worked, and maybe do some pre-checking to see if it already exists + // _CFPFactory *factory = + (void)_CFPFactoryCreate(kCFAllocatorSystemDefault, factoryID, func); + return true; +} + +CF_EXPORT Boolean CFPlugInRegisterFactoryFunctionByName(CFUUIDRef factoryID, CFPlugInRef plugIn, CFStringRef functionName) { + // Create factories with plugIns from plugIn's allocator + // MF:!!! Should probably check that this worked, and maybe do some pre-checking to see if it already exists + // _CFPFactory *factory = + (void)_CFPFactoryCreateByName(CFGetAllocator(plugIn), factoryID, plugIn, functionName); + return true; +} + +CF_EXPORT Boolean CFPlugInUnregisterFactory(CFUUIDRef factoryID) { + _CFPFactory *factory = _CFPFactoryFind(factoryID, true); + + if (!factory) { + /* MF:!!! Error. No factory registered for this ID. */ + } else { + _CFPFactoryDisable(factory); + } + return true; +} + +CF_EXPORT Boolean CFPlugInRegisterPlugInType(CFUUIDRef factoryID, CFUUIDRef typeID) { + _CFPFactory *factory = _CFPFactoryFind(factoryID, true); + + if (!factory) { + /* MF:!!! Error. Factory must be registered (and not disabled) before types can be associated with it. */ + } else { + _CFPFactoryAddType(factory, typeID); + } + return true; +} + +CF_EXPORT Boolean CFPlugInUnregisterPlugInType(CFUUIDRef factoryID, CFUUIDRef typeID) { + _CFPFactory *factory = _CFPFactoryFind(factoryID, true); + + if (!factory) { + /* MF:!!! Error. Could not find factory. */ + } else { + _CFPFactoryRemoveType(factory, typeID); + } + return true; +} + + +/* ================= Registering instances ================= */ +/* When a new instance of a type is created, the instance is responsible for registering itself with the factory that created it and unregistering when it deallocates. */ +/* This means that an instance must keep track of the CFUUIDRef of the factory that created it so it can unregister when it goes away. */ + +CF_EXPORT void CFPlugInAddInstanceForFactory(CFUUIDRef factoryID) { + _CFPFactory *factory = _CFPFactoryFind(factoryID, true); + + if (!factory) { + /* MF:!!! Error. Could not find factory. */ + } else { + _CFPFactoryAddInstance(factory); + } +} + +CF_EXPORT void CFPlugInRemoveInstanceForFactory(CFUUIDRef factoryID) { + _CFPFactory *factory = _CFPFactoryFind(factoryID, true); + + if (!factory) { + /* MF:!!! Error. Could not find factory. */ + } else { + _CFPFactoryRemoveInstance(factory); + } +}
diff --git a/CoreFoundation/CFPlugIn.h b/CoreFoundation/CFPlugIn.h new file mode 100644 index 0000000..6b4be37 --- /dev/null +++ b/CoreFoundation/CFPlugIn.h
@@ -0,0 +1,168 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFPlugIn.h + Copyright (c) 1999-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFPLUGIN__) +#define __COREFOUNDATION_CFPLUGIN__ 1 + +#if !defined(COREFOUNDATION_CFPLUGINCOM_SEPARATE) +#define COREFOUNDATION_CFPLUGINCOM_SEPARATE 1 +#endif + +#include <CoreFoundation/CFBase.h> +#include <CoreFoundation/CFArray.h> +#include <CoreFoundation/CFBundle.h> +#include <CoreFoundation/CFString.h> +#include <CoreFoundation/CFURL.h> +#include <CoreFoundation/CFUUID.h> + +CF_EXTERN_C_BEGIN + +/* ================ Standard Info.plist keys for plugIns ================ */ + +CF_EXPORT +const CFStringRef kCFPlugInDynamicRegistrationKey; +CF_EXPORT +const CFStringRef kCFPlugInDynamicRegisterFunctionKey; +CF_EXPORT +const CFStringRef kCFPlugInUnloadFunctionKey; +CF_EXPORT +const CFStringRef kCFPlugInFactoriesKey; +CF_EXPORT +const CFStringRef kCFPlugInTypesKey; + +/* ================= Function prototypes for various callbacks ================= */ +/* Function types that plugIn authors can implement for various purposes. */ + +typedef void (*CFPlugInDynamicRegisterFunction)(CFPlugInRef plugIn); +typedef void (*CFPlugInUnloadFunction)(CFPlugInRef plugIn); +typedef void *(*CFPlugInFactoryFunction)(CFAllocatorRef allocator, CFUUIDRef typeUUID); + +/* ================= Creating PlugIns ================= */ + +CF_EXPORT +CFTypeID CFPlugInGetTypeID(void); + +CF_EXPORT +CFPlugInRef CFPlugInCreate(CFAllocatorRef allocator, CFURLRef plugInURL); + /* Might return an existing instance with the ref-count bumped. */ + +CF_EXPORT +CFBundleRef CFPlugInGetBundle(CFPlugInRef plugIn); + +/* ================= Controlling load on demand ================= */ +/* For plugIns. */ +/* PlugIns that do static registration are load on demand by default. */ +/* PlugIns that do dynamic registration are not load on demand by default. */ +/* A dynamic registration function can call CFPlugInSetLoadOnDemand(). */ + +CF_EXPORT +void CFPlugInSetLoadOnDemand(CFPlugInRef plugIn, Boolean flag); + +CF_EXPORT +Boolean CFPlugInIsLoadOnDemand(CFPlugInRef plugIn); + +/* ================= Finding factories and creating instances ================= */ +/* For plugIn hosts. */ +/* Functions for finding factories to create specific types and actually creating instances of a type. */ + +CF_EXPORT +CFArrayRef CFPlugInFindFactoriesForPlugInType(CFUUIDRef typeUUID); + /* This function finds all the factories from any plugin for the given type. Returns an array that the caller must release. */ + +CF_EXPORT +CFArrayRef CFPlugInFindFactoriesForPlugInTypeInPlugIn(CFUUIDRef typeUUID, CFPlugInRef plugIn); + /* This function restricts the result to factories from the given plug-in that can create the given type. Returns an array that the caller must release. */ + +CF_EXPORT +void *CFPlugInInstanceCreate(CFAllocatorRef allocator, CFUUIDRef factoryUUID, CFUUIDRef typeUUID); + /* This function returns the IUnknown interface for the new instance. */ + +/* ================= Registering factories and types ================= */ +/* For plugIn writers who must dynamically register things. */ +/* Functions to register factory functions and to associate factories with types. */ + +CF_EXPORT +Boolean CFPlugInRegisterFactoryFunction(CFUUIDRef factoryUUID, CFPlugInFactoryFunction func); + +CF_EXPORT +Boolean CFPlugInRegisterFactoryFunctionByName(CFUUIDRef factoryUUID, CFPlugInRef plugIn, CFStringRef functionName); + +CF_EXPORT +Boolean CFPlugInUnregisterFactory(CFUUIDRef factoryUUID); + +CF_EXPORT +Boolean CFPlugInRegisterPlugInType(CFUUIDRef factoryUUID, CFUUIDRef typeUUID); + +CF_EXPORT +Boolean CFPlugInUnregisterPlugInType(CFUUIDRef factoryUUID, CFUUIDRef typeUUID); + +/* ================= Registering instances ================= */ +/* When a new instance of a type is created, the instance is responsible for registering itself with the factory that created it and unregistering when it deallocates. */ +/* This means that an instance must keep track of the CFUUIDRef of the factory that created it so it can unregister when it goes away. */ + +CF_EXPORT +void CFPlugInAddInstanceForFactory(CFUUIDRef factoryID); + +CF_EXPORT +void CFPlugInRemoveInstanceForFactory(CFUUIDRef factoryID); + + +/* Obsolete API */ + +typedef struct __CFPlugInInstance *CFPlugInInstanceRef; + +typedef Boolean (*CFPlugInInstanceGetInterfaceFunction)(CFPlugInInstanceRef instance, CFStringRef interfaceName, void **ftbl); +typedef void (*CFPlugInInstanceDeallocateInstanceDataFunction)(void *instanceData); + +CF_EXPORT +Boolean CFPlugInInstanceGetInterfaceFunctionTable(CFPlugInInstanceRef instance, CFStringRef interfaceName, void **ftbl); +CF_EXPORT +CFStringRef CFPlugInInstanceGetFactoryName(CFPlugInInstanceRef instance); +CF_EXPORT +void *CFPlugInInstanceGetInstanceData(CFPlugInInstanceRef instance); +CF_EXPORT +CFTypeID CFPlugInInstanceGetTypeID(void); +CF_EXPORT +CFPlugInInstanceRef CFPlugInInstanceCreateWithInstanceDataSize(CFAllocatorRef allocator, CFIndex instanceDataSize, CFPlugInInstanceDeallocateInstanceDataFunction deallocateInstanceFunction, CFStringRef factoryName, CFPlugInInstanceGetInterfaceFunction getInterfaceFunction); + +CF_EXTERN_C_END + +#if !COREFOUNDATION_CFPLUGINCOM_SEPARATE +#include <CoreFoundation/CFPlugInCOM.h> +#endif /* !COREFOUNDATION_CFPLUGINCOM_SEPARATE */ + +#endif /* ! __COREFOUNDATION_CFPLUGIN__ */ +
diff --git a/CoreFoundation/CFPlugInCOM.h b/CoreFoundation/CFPlugInCOM.h new file mode 100644 index 0000000..6628e7c --- /dev/null +++ b/CoreFoundation/CFPlugInCOM.h
@@ -0,0 +1,127 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFPlugInCOM.h + Copyright (c) 1999-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFPLUGINCOM__) +#define __COREFOUNDATION_CFPLUGINCOM__ 1 + +#include <CoreFoundation/CFPlugIn.h> + +CF_EXTERN_C_BEGIN + +/* ================= IUnknown definition (C struct) ================= */ + +/* All interface structs must have an IUnknownStruct at the beginning. */ +/* The _reserved field is part of the Microsoft COM binary standard on Macintosh. */ +/* You can declare new C struct interfaces by defining a new struct that includes "IUNKNOWN_C_GUTS;" before the first field of the struct. */ + +typedef SInt32 HRESULT; +typedef UInt32 ULONG; +typedef void *LPVOID; +typedef CFUUIDBytes REFIID; + +#define SUCCEEDED(Status) ((HRESULT)(Status) >= 0) +#define FAILED(Status) ((HRESULT)(Status)<0) + +/* Macros for more detailed HRESULT analysis */ +#define IS_ERROR(Status) ((unsigned long)(Status) >> 31 == SEVERITY_ERROR) +#define HRESULT_CODE(hr) ((hr) & 0xFFFF) +#define HRESULT_FACILITY(hr) (((hr) >> 16) & 0x1fff) +#define HRESULT_SEVERITY(hr) (((hr) >> 31) & 0x1) +#define SEVERITY_SUCCESS 0 +#define SEVERITY_ERROR 1 + +/* Creating an HRESULT from its component pieces */ +#define MAKE_HRESULT(sev,fac,code) ((HRESULT) (((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | ((unsigned long)(code))) ) + +/* Pre-defined success HRESULTS */ +#define S_OK ((HRESULT)0x00000000L) +#define S_FALSE ((HRESULT)0x00000001L) + +/* Common error HRESULTS */ +#define E_UNEXPECTED ((HRESULT)0x8000FFFFL) +#define E_NOTIMPL ((HRESULT)0x80000001L) +#define E_OUTOFMEMORY ((HRESULT)0x80000002L) +#define E_INVALIDARG ((HRESULT)0x80000003L) +#define E_NOINTERFACE ((HRESULT)0x80000004L) +#define E_POINTER ((HRESULT)0x80000005L) +#define E_HANDLE ((HRESULT)0x80000006L) +#define E_ABORT ((HRESULT)0x80000007L) +#define E_FAIL ((HRESULT)0x80000008L) +#define E_ACCESSDENIED ((HRESULT)0x80000009L) + +/* This macro should be used when defining all interface functions (as it is for the IUnknown functions below). */ +#define STDMETHODCALLTYPE + +/* The __RPC_FAR macro is for COM source compatibility only. This macro is used a lot in COM interface definitions. If your CFPlugIn interfaces need to be COM interfaces as well, you can use this macro to get better source compatibility. It is not used in the IUnknown definition below, because when doing COM, you will be using the Microsoft supplied IUnknown interface anyway. */ +#define __RPC_FAR + +/* The IUnknown interface */ +#define IUnknownUUID CFUUIDGetConstantUUIDWithBytes(kCFAllocatorSystemDefault, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46) + +#define IUNKNOWN_C_GUTS \ + void *_reserved; \ + HRESULT (STDMETHODCALLTYPE *QueryInterface)(void *thisPointer, REFIID iid, LPVOID *ppv); \ + ULONG (STDMETHODCALLTYPE *AddRef)(void *thisPointer); \ + ULONG (STDMETHODCALLTYPE *Release)(void *thisPointer) + +typedef struct IUnknownVTbl { + IUNKNOWN_C_GUTS; +} IUnknownVTbl; + +CF_EXTERN_C_END + + +/* C++ specific stuff */ +#if defined(__cplusplus) +/* ================= IUnknown definition (C++ class) ================= */ + +/* This is a definition of IUnknown as a pure abstract virtual C++ class. This class will work only with compilers that can produce COM-compatible object layouts for C++ classes. egcs can not do this. MetroWerks can do this (if you subclass from __comobject) */ + +class IUnknown +#if defined(__MWERKS__) && !defined(__MACH__) + : __comobject +#endif /* __MWERKS__ && !__MACH__ */ +{ + public: + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) = 0; + virtual ULONG STDMETHODCALLTYPE AddRef(void) = 0; + virtual ULONG STDMETHODCALLTYPE Release(void) = 0; +}; + +#endif /* __cplusplus */ + +#endif /* ! __COREFOUNDATION_CFPLUGINCOM__ */ +
diff --git a/CoreFoundation/CFPlugIn_Factory.c b/CoreFoundation/CFPlugIn_Factory.c new file mode 100644 index 0000000..1801707 --- /dev/null +++ b/CoreFoundation/CFPlugIn_Factory.c
@@ -0,0 +1,293 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFPlugIn_Factory.c + Copyright (c) 1999-2007 Apple Inc. All rights reserved. + Responsibility: Doug Davidson +*/ + +#include "CFBundle_Internal.h" +#include "CFInternal.h" + +static CFSpinLock_t CFPlugInGlobalDataLock = CFSpinLockInit; +static CFMutableDictionaryRef _factoriesByFactoryID = NULL; /* Value is _CFPFactory */ +static CFMutableDictionaryRef _factoriesByTypeID = NULL; /* Value is array of _CFPFactory */ + +static void _CFPFactoryAddToTable(_CFPFactory *factory) { + __CFSpinLock(&CFPlugInGlobalDataLock); + if (_factoriesByFactoryID == NULL) { + CFDictionaryValueCallBacks _factoryDictValueCallbacks = {0, NULL, NULL, NULL, NULL}; + // Use default allocator + _factoriesByFactoryID = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &_factoryDictValueCallbacks); + } + CFDictionarySetValue(_factoriesByFactoryID, factory->_uuid, factory); + __CFSpinUnlock(&CFPlugInGlobalDataLock); +} + +static void _CFPFactoryRemoveFromTable(_CFPFactory *factory) { + __CFSpinLock(&CFPlugInGlobalDataLock); + if (_factoriesByFactoryID != NULL) { + CFDictionaryRemoveValue(_factoriesByFactoryID, factory->_uuid); + } + __CFSpinUnlock(&CFPlugInGlobalDataLock); +} + +__private_extern__ _CFPFactory *_CFPFactoryFind(CFUUIDRef factoryID, Boolean enabled) { + _CFPFactory *result = NULL; + + __CFSpinLock(&CFPlugInGlobalDataLock); + if (_factoriesByFactoryID != NULL) { + result = (_CFPFactory *)CFDictionaryGetValue(_factoriesByFactoryID, factoryID); + if (result && result->_enabled != enabled) { + result = NULL; + } + } + __CFSpinUnlock(&CFPlugInGlobalDataLock); + return result; +} + +static void _CFPFactoryDeallocate(_CFPFactory *factory) { + CFAllocatorRef allocator = factory->_allocator; + SInt32 c; + + _CFPFactoryRemoveFromTable(factory); + + if (factory->_plugIn) { + _CFPlugInRemoveFactory(factory->_plugIn, factory); + } + + /* Remove all types for this factory. */ + c = CFArrayGetCount(factory->_types); + while (c--) { + _CFPFactoryRemoveType(factory, (CFUUIDRef)CFArrayGetValueAtIndex(factory->_types, c)); + } + CFRelease(factory->_types); + + if (factory->_funcName) { + CFRelease(factory->_funcName); + } + + if (factory->_uuid) { + CFRelease(factory->_uuid); + } + + CFAllocatorDeallocate(allocator, factory); + CFRelease(allocator); +} + +static _CFPFactory *_CFPFactoryCommonCreate(CFAllocatorRef allocator, CFUUIDRef factoryID) { + _CFPFactory *factory; + UInt32 size; + size = sizeof(_CFPFactory); + allocator = ((NULL == allocator) ? (CFAllocatorRef)CFRetain(__CFGetDefaultAllocator()) : (CFAllocatorRef)CFRetain(allocator)); + factory = (_CFPFactory *)CFAllocatorAllocate(allocator, size, 0); + if (NULL == factory) { + CFRelease(allocator); + return NULL; + } + + factory->_allocator = allocator; + + factory->_uuid = (CFUUIDRef)CFRetain(factoryID); + factory->_enabled = true; + factory->_instanceCount = 0; + + _CFPFactoryAddToTable(factory); + + factory->_types = CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks); + + return factory; +} + +__private_extern__ _CFPFactory *_CFPFactoryCreate(CFAllocatorRef allocator, CFUUIDRef factoryID, CFPlugInFactoryFunction func) { + _CFPFactory *factory = _CFPFactoryCommonCreate(allocator, factoryID); + + factory->_func = func; + factory->_plugIn = NULL; + factory->_funcName = NULL; + + return factory; +} + +__private_extern__ _CFPFactory *_CFPFactoryCreateByName(CFAllocatorRef allocator, CFUUIDRef factoryID, CFPlugInRef plugIn, CFStringRef funcName) { + _CFPFactory *factory = _CFPFactoryCommonCreate(allocator, factoryID); + + factory->_func = NULL; + factory->_plugIn = plugIn; + if (plugIn) { + _CFPlugInAddFactory(plugIn, factory); + } + factory->_funcName = (funcName ? (CFStringRef)CFStringCreateCopy(allocator, funcName) : NULL); + + return factory; +} + +__private_extern__ CFUUIDRef _CFPFactoryGetFactoryID(_CFPFactory *factory) { + return factory->_uuid; +} + +__private_extern__ CFPlugInRef _CFPFactoryGetPlugIn(_CFPFactory *factory) { + return factory->_plugIn; +} + +__private_extern__ void *_CFPFactoryCreateInstance(CFAllocatorRef allocator, _CFPFactory *factory, CFUUIDRef typeID) { + void *result = NULL; + if (factory->_enabled) { + if (factory->_func == NULL) { + factory->_func = (CFPlugInFactoryFunction)CFBundleGetFunctionPointerForName(factory->_plugIn, factory->_funcName); + if (factory->_func == NULL) { + CFLog(__kCFLogPlugIn, CFSTR("Cannot find function pointer %@ for factory %@ in %@"), factory->_funcName, factory->_uuid, factory->_plugIn); + } +#if BINARY_SUPPORT_CFM + else { + // return values from CFBundleGetFunctionPointerForName will always be dyld, but + // we must force-fault them because pointers to glue code do not fault correctly + factory->_func = (void *)((uint32_t)(factory->_func) | 0x1); + } +#endif /* BINARY_SUPPORT_CFM */ + } + if (factory->_func) { + // UPPGOOP + FAULT_CALLBACK((void **)&(factory->_func)); + result = (void *)INVOKE_CALLBACK2(factory->_func, allocator, typeID); + } + } else { + CFLog(__kCFLogPlugIn, CFSTR("Factory %@ is disabled"), factory->_uuid); + } + return result; +} + +__private_extern__ void _CFPFactoryDisable(_CFPFactory *factory) { + factory->_enabled = false; + if (factory->_instanceCount == 0) { + _CFPFactoryDeallocate(factory); + } +} + +__private_extern__ Boolean _CFPFactoryIsEnabled(_CFPFactory *factory) { + return factory->_enabled; +} + +__private_extern__ void _CFPFactoryFlushFunctionCache(_CFPFactory *factory) { + /* MF:!!! Assert that this factory belongs to a plugIn. */ + /* This is called by the factory's plugIn when the plugIn unloads its code. */ + factory->_func = NULL; +} + +__private_extern__ void _CFPFactoryAddType(_CFPFactory *factory, CFUUIDRef typeID) { + CFMutableArrayRef array; + + /* Add the type to the factory's type list */ + CFArrayAppendValue(factory->_types, typeID); + + /* Add the factory to the type's array of factories */ + __CFSpinLock(&CFPlugInGlobalDataLock); + if (_factoriesByTypeID == NULL) { + // Create this from default allocator + _factoriesByTypeID = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + } + array = (CFMutableArrayRef)CFDictionaryGetValue(_factoriesByTypeID, typeID); + if (array == NULL) { + CFArrayCallBacks _factoryArrayCallbacks = {0, NULL, NULL, NULL, NULL}; + // Create this from default allocator + array = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &_factoryArrayCallbacks); + CFDictionarySetValue(_factoriesByTypeID, typeID, array); + CFRelease(array); + } + CFArrayAppendValue(array, factory); + __CFSpinUnlock(&CFPlugInGlobalDataLock); +} + +__private_extern__ void _CFPFactoryRemoveType(_CFPFactory *factory, CFUUIDRef typeID) { + /* Remove it from the factory's type list */ + SInt32 idx; + + idx = CFArrayGetFirstIndexOfValue(factory->_types, CFRangeMake(0, CFArrayGetCount(factory->_types)), typeID); + if (idx >=0) { + CFArrayRemoveValueAtIndex(factory->_types, idx); + } + + /* Remove the factory from the type's list of factories */ + __CFSpinLock(&CFPlugInGlobalDataLock); + if (_factoriesByTypeID != NULL) { + CFMutableArrayRef array = (CFMutableArrayRef)CFDictionaryGetValue(_factoriesByTypeID, typeID); + if (array != NULL) { + idx = CFArrayGetFirstIndexOfValue(array, CFRangeMake(0, CFArrayGetCount(array)), factory); + if (idx >=0) { + CFArrayRemoveValueAtIndex(array, idx); + if (CFArrayGetCount(array) == 0) { + CFDictionaryRemoveValue(_factoriesByTypeID, typeID); + } + } + } + } + __CFSpinUnlock(&CFPlugInGlobalDataLock); +} + +__private_extern__ Boolean _CFPFactorySupportsType(_CFPFactory *factory, CFUUIDRef typeID) { + SInt32 idx; + + idx = CFArrayGetFirstIndexOfValue(factory->_types, CFRangeMake(0, CFArrayGetCount(factory->_types)), typeID); + return ((idx >= 0) ? true : false); +} + +__private_extern__ CFArrayRef _CFPFactoryFindForType(CFUUIDRef typeID) { + CFArrayRef result = NULL; + + __CFSpinLock(&CFPlugInGlobalDataLock); + if (_factoriesByTypeID != NULL) { + result = (CFArrayRef)CFDictionaryGetValue(_factoriesByTypeID, typeID); + } + __CFSpinUnlock(&CFPlugInGlobalDataLock); + + return result; +} + +/* These methods are called by CFPlugInInstance when an instance is created or destroyed. If a factory's instance count goes to 0 and the factory has been disabled, the factory is destroyed. */ +__private_extern__ void _CFPFactoryAddInstance(_CFPFactory *factory) { + /* MF:!!! Assert that factory is enabled. */ + factory->_instanceCount++; + if (factory->_plugIn) { + _CFPlugInAddPlugInInstance(factory->_plugIn); + } +} + +__private_extern__ void _CFPFactoryRemoveInstance(_CFPFactory *factory) { + /* MF:!!! Assert that _instanceCount > 0. */ + factory->_instanceCount--; + if (factory->_plugIn) { + _CFPlugInRemovePlugInInstance(factory->_plugIn); + } + if ((factory->_instanceCount == 0) && (!factory->_enabled)) { + _CFPFactoryDeallocate(factory); + } +}
diff --git a/CoreFoundation/CFPlugIn_Factory.h b/CoreFoundation/CFPlugIn_Factory.h new file mode 100644 index 0000000..2714320 --- /dev/null +++ b/CoreFoundation/CFPlugIn_Factory.h
@@ -0,0 +1,87 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFPlugIn_Factory.h + Copyright (c) 1999-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFPLUGIN_FACTORY__) +#define __COREFOUNDATION_CFPLUGIN_FACTORY__ 1 + +#include "CFBundle_Internal.h" + +CF_EXTERN_C_BEGIN + +typedef struct __CFPFactory { + CFAllocatorRef _allocator; + + CFUUIDRef _uuid; + Boolean _enabled; + char _padding[3]; + SInt32 _instanceCount; + + CFPlugInFactoryFunction _func; + + CFPlugInRef _plugIn; + CFStringRef _funcName; + + CFMutableArrayRef _types; +} _CFPFactory; + +extern _CFPFactory *_CFPFactoryCreate(CFAllocatorRef allocator, CFUUIDRef factoryID, CFPlugInFactoryFunction func); +extern _CFPFactory *_CFPFactoryCreateByName(CFAllocatorRef allocator, CFUUIDRef factoryID, CFPlugInRef plugIn, CFStringRef funcName); + +extern _CFPFactory *_CFPFactoryFind(CFUUIDRef factoryID, Boolean enabled); + +extern CFUUIDRef _CFPFactoryGetFactoryID(_CFPFactory *factory); +extern CFPlugInRef _CFPFactoryGetPlugIn(_CFPFactory *factory); + +extern void *_CFPFactoryCreateInstance(CFAllocatorRef allocator, _CFPFactory *factory, CFUUIDRef typeID); +extern void _CFPFactoryDisable(_CFPFactory *factory); +extern Boolean _CFPFactoryIsEnabled(_CFPFactory *factory); + +extern void _CFPFactoryFlushFunctionCache(_CFPFactory *factory); + +extern void _CFPFactoryAddType(_CFPFactory *factory, CFUUIDRef typeID); +extern void _CFPFactoryRemoveType(_CFPFactory *factory, CFUUIDRef typeID); + +extern Boolean _CFPFactorySupportsType(_CFPFactory *factory, CFUUIDRef typeID); +extern CFArrayRef _CFPFactoryFindForType(CFUUIDRef typeID); + +/* These methods are called by CFPlugInInstance when an instance is created or destroyed. If a factory's instance count goes to 0 and the factory has been disabled, the factory is destroyed. */ +extern void _CFPFactoryAddInstance(_CFPFactory *factory); +extern void _CFPFactoryRemoveInstance(_CFPFactory *factory); + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFPLUGIN_FACTORY__ */ +
diff --git a/CoreFoundation/CFPlugIn_Instance.c b/CoreFoundation/CFPlugIn_Instance.c new file mode 100644 index 0000000..783c745 --- /dev/null +++ b/CoreFoundation/CFPlugIn_Instance.c
@@ -0,0 +1,140 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFPlugIn_Instance.c + Copyright (c) 1999-2007 Apple Inc. All rights reserved. + Responsibility: Doug Davidson +*/ + +#include "CFBundle_Internal.h" +#include "CFInternal.h" + +static CFTypeID __kCFPlugInInstanceTypeID = _kCFRuntimeNotATypeID; + +struct __CFPlugInInstance { + CFRuntimeBase _base; + + _CFPFactory *factory; + + CFPlugInInstanceGetInterfaceFunction getInterfaceFunction; + CFPlugInInstanceDeallocateInstanceDataFunction deallocateInstanceDataFunction; + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4200) +#endif //_MSC_VER + uint8_t _instanceData[0]; +#ifdef _MSC_VER +#pragma warning(pop) +#endif //_MSC_VER +}; + +static CFStringRef __CFPlugInInstanceCopyDescription(CFTypeRef cf) { + /* MF:!!! Implement me */ + return CFSTR("Some CFPlugInInstance"); +} + +static void __CFPlugInInstanceDeallocate(CFTypeRef cf) { + CFPlugInInstanceRef instance = (CFPlugInInstanceRef)cf; + + __CFGenericValidateType(cf, __kCFPlugInInstanceTypeID); + + if (instance->deallocateInstanceDataFunction) { + FAULT_CALLBACK((void **)&(instance->deallocateInstanceDataFunction)); + (void)INVOKE_CALLBACK1(instance->deallocateInstanceDataFunction, (void *)(&instance->_instanceData[0])); + } + + if (instance->factory) { + _CFPFactoryRemoveInstance(instance->factory); + } +} + +static const CFRuntimeClass __CFPlugInInstanceClass = { + 0, + "CFPlugInInstance", + NULL, // init + NULL, // copy + __CFPlugInInstanceDeallocate, + NULL, // equal + NULL, // hash + NULL, // + __CFPlugInInstanceCopyDescription +}; + +__private_extern__ void __CFPlugInInstanceInitialize(void) { + __kCFPlugInInstanceTypeID = _CFRuntimeRegisterClass(&__CFPlugInInstanceClass); +} + +CFTypeID CFPlugInInstanceGetTypeID(void) { + return __kCFPlugInInstanceTypeID; +} +CF_EXPORT CFPlugInInstanceRef CFPlugInInstanceCreateWithInstanceDataSize(CFAllocatorRef allocator, CFIndex instanceDataSize, CFPlugInInstanceDeallocateInstanceDataFunction deallocateInstanceFunction, CFStringRef factoryName, CFPlugInInstanceGetInterfaceFunction getInterfaceFunction) { + CFPlugInInstanceRef instance; + UInt32 size; + size = sizeof(struct __CFPlugInInstance) + instanceDataSize - sizeof(CFRuntimeBase); + instance = (CFPlugInInstanceRef)_CFRuntimeCreateInstance(allocator, __kCFPlugInInstanceTypeID, size, NULL); + if (NULL == instance) { + return NULL; + } + + instance->factory = _CFPFactoryFind((CFUUIDRef)factoryName, true); + if (instance->factory) { + _CFPFactoryAddInstance(instance->factory); + } + instance->getInterfaceFunction = getInterfaceFunction; + instance->deallocateInstanceDataFunction = deallocateInstanceFunction; + + return instance; +} + +CF_EXPORT Boolean CFPlugInInstanceGetInterfaceFunctionTable(CFPlugInInstanceRef instance, CFStringRef interfaceName, void **ftbl) { + void *myFtbl; + Boolean result = false; + + if (instance->getInterfaceFunction) { + FAULT_CALLBACK((void **)&(instance->getInterfaceFunction)); + result = INVOKE_CALLBACK3(instance->getInterfaceFunction, instance, interfaceName, &myFtbl) ? true : false; + } + if (ftbl) { + *ftbl = (result ? myFtbl : NULL); + } + return result; +} + +CF_EXPORT CFStringRef CFPlugInInstanceGetFactoryName(CFPlugInInstanceRef instance) { + return (CFStringRef)_CFPFactoryGetFactoryID(instance->factory); +} + +CF_EXPORT void *CFPlugInInstanceGetInstanceData(CFPlugInInstanceRef instance) { + return (void *)(&instance->_instanceData[0]); +} +
diff --git a/CoreFoundation/CFPlugIn_PlugIn.c b/CoreFoundation/CFPlugIn_PlugIn.c new file mode 100644 index 0000000..30279a5 --- /dev/null +++ b/CoreFoundation/CFPlugIn_PlugIn.c
@@ -0,0 +1,275 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFPlugIn_PlugIn.c + Copyright (c) 1999-2007 Apple Inc. All rights reserved. + Responsibility: Doug Davidson +*/ + +#include "CFBundle_Internal.h" +#include "CFInternal.h" + + +static void _registerFactory(const void *key, const void *val, void *context) { + CFStringRef factoryIDStr = (CFStringRef)key; + CFStringRef factoryFuncStr = (CFStringRef)val; + CFBundleRef bundle = (CFBundleRef)context; + CFUUIDRef factoryID = (CFGetTypeID(factoryIDStr) == CFStringGetTypeID()) ? CFUUIDCreateFromString(kCFAllocatorSystemDefault, factoryIDStr) : NULL; + if (NULL == factoryID) factoryID = (CFUUIDRef)CFRetain(factoryIDStr); + if (CFGetTypeID(factoryFuncStr) != CFStringGetTypeID() || CFStringGetLength(factoryFuncStr) <= 0) factoryFuncStr = NULL; + CFPlugInRegisterFactoryFunctionByName(factoryID, bundle, factoryFuncStr); + if (NULL != factoryID) CFRelease(factoryID); +} + +static void _registerType(const void *key, const void *val, void *context) { + CFStringRef typeIDStr = (CFStringRef)key; + CFArrayRef factoryIDStrArray = (CFArrayRef)val; + CFBundleRef bundle = (CFBundleRef)context; + SInt32 i, c = (CFGetTypeID(factoryIDStrArray) == CFArrayGetTypeID()) ? CFArrayGetCount(factoryIDStrArray) : 0; + CFStringRef curFactoryIDStr; + CFUUIDRef typeID = (CFGetTypeID(typeIDStr) == CFStringGetTypeID()) ? CFUUIDCreateFromString(kCFAllocatorSystemDefault, typeIDStr) : NULL; + CFUUIDRef curFactoryID; + if (NULL == typeID) typeID = (CFUUIDRef)CFRetain(typeIDStr); + if (0 == c && (CFGetTypeID(factoryIDStrArray) != CFArrayGetTypeID())) { + curFactoryIDStr = (CFStringRef)val; + curFactoryID = (CFGetTypeID(curFactoryIDStr) == CFStringGetTypeID()) ? CFUUIDCreateFromString(CFGetAllocator(bundle), curFactoryIDStr) : NULL; + if (NULL == curFactoryID) curFactoryID = (CFUUIDRef)CFRetain(curFactoryIDStr); + CFPlugInRegisterPlugInType(curFactoryID, typeID); + if (NULL != curFactoryID) CFRelease(curFactoryID); + } else for (i=0; i<c; i++) { + curFactoryIDStr = (CFStringRef)CFArrayGetValueAtIndex(factoryIDStrArray, i); + curFactoryID = (CFGetTypeID(curFactoryIDStr) == CFStringGetTypeID()) ? CFUUIDCreateFromString(CFGetAllocator(bundle), curFactoryIDStr) : NULL; + if (NULL == curFactoryID) curFactoryID = (CFUUIDRef)CFRetain(curFactoryIDStr); + CFPlugInRegisterPlugInType(curFactoryID, typeID); + if (NULL != curFactoryID) CFRelease(curFactoryID); + } + if (NULL != typeID) CFRelease(typeID); +} + +__private_extern__ Boolean _CFBundleNeedsInitPlugIn(CFBundleRef bundle) { + Boolean result = false; + CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle), factoryDict; + CFStringRef tempStr; + if (infoDict) { + factoryDict = (CFDictionaryRef)CFDictionaryGetValue(infoDict, kCFPlugInFactoriesKey); + if (factoryDict != NULL && CFGetTypeID(factoryDict) == CFDictionaryGetTypeID()) result = true; + tempStr = (CFStringRef)CFDictionaryGetValue(infoDict, kCFPlugInDynamicRegistrationKey); + if (tempStr != NULL && CFGetTypeID(tempStr) == CFStringGetTypeID() && CFStringCompare(tempStr, CFSTR("YES"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) result = true; + } + return result; +} + +__private_extern__ void _CFBundleInitPlugIn(CFBundleRef bundle) { + CFArrayCallBacks _pluginFactoryArrayCallbacks = {0, NULL, NULL, NULL, NULL}; + Boolean doDynamicReg = false; + CFDictionaryRef infoDict; + CFDictionaryRef factoryDict; + CFDictionaryRef typeDict; + CFStringRef tempStr; + + infoDict = CFBundleGetInfoDictionary(bundle); + if (infoDict == NULL) { + return; + } + factoryDict = (CFDictionaryRef)CFDictionaryGetValue(infoDict, kCFPlugInFactoriesKey); + if (factoryDict != NULL && CFGetTypeID(factoryDict) != CFDictionaryGetTypeID()) factoryDict = NULL; + tempStr = (CFStringRef)CFDictionaryGetValue(infoDict, kCFPlugInDynamicRegistrationKey); + if (tempStr != NULL && CFGetTypeID(tempStr) == CFStringGetTypeID() && CFStringCompare(tempStr, CFSTR("YES"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { + doDynamicReg = true; + } + if (!factoryDict && !doDynamicReg) { + // This is not a plugIn. + return; + } + + /* loadOnDemand is true by default if the plugIn does not do dynamic registration. It is false, by default if it does do dynamic registration. The dynamic register function can set this. */ + __CFBundleGetPlugInData(bundle)->_isPlugIn = true; + __CFBundleGetPlugInData(bundle)->_loadOnDemand = true; + __CFBundleGetPlugInData(bundle)->_isDoingDynamicRegistration = false; + __CFBundleGetPlugInData(bundle)->_instanceCount = 0; + + __CFBundleGetPlugInData(bundle)->_factories = CFArrayCreateMutable(CFGetAllocator(bundle), 0, &_pluginFactoryArrayCallbacks); + + /* Now do the registration */ + + /* First do static registrations, if any. */ + if (factoryDict != NULL) { + CFDictionaryApplyFunction(factoryDict, _registerFactory, bundle); + } + typeDict = (CFDictionaryRef)CFDictionaryGetValue(infoDict, kCFPlugInTypesKey); + if (typeDict != NULL && CFGetTypeID(typeDict) != CFDictionaryGetTypeID()) typeDict = NULL; + if (typeDict != NULL) { + CFDictionaryApplyFunction(typeDict, _registerType, bundle); + } + + /* Now set key for dynamic registration if necessary */ + if (doDynamicReg) { + CFDictionarySetValue((CFMutableDictionaryRef)infoDict, CFSTR("CFPlugInNeedsDynamicRegistration"), CFSTR("YES")); + if (CFBundleIsExecutableLoaded(bundle)) _CFBundlePlugInLoaded(bundle); + } +} + +__private_extern__ void _CFBundlePlugInLoaded(CFBundleRef bundle) { + CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); + CFStringRef tempStr; + CFPlugInDynamicRegisterFunction func = NULL; + + if (!__CFBundleGetPlugInData(bundle)->_isPlugIn || __CFBundleGetPlugInData(bundle)->_isDoingDynamicRegistration || !infoDict || !CFBundleIsExecutableLoaded(bundle)) return; + + tempStr = (CFStringRef)CFDictionaryGetValue(infoDict, CFSTR("CFPlugInNeedsDynamicRegistration")); + if (tempStr != NULL && CFGetTypeID(tempStr) == CFStringGetTypeID() && CFStringCompare(tempStr, CFSTR("YES"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { + CFDictionaryRemoveValue((CFMutableDictionaryRef)infoDict, CFSTR("CFPlugInNeedsDynamicRegistration")); + tempStr = (CFStringRef)CFDictionaryGetValue(infoDict, kCFPlugInDynamicRegisterFunctionKey); + if (tempStr == NULL || CFGetTypeID(tempStr) != CFStringGetTypeID() || CFStringGetLength(tempStr) <= 0) { + tempStr = CFSTR("CFPlugInDynamicRegister"); + } + __CFBundleGetPlugInData(bundle)->_loadOnDemand = false; + __CFBundleGetPlugInData(bundle)->_isDoingDynamicRegistration = true; + + /* Find the symbol and call it. */ + func = (CFPlugInDynamicRegisterFunction)CFBundleGetFunctionPointerForName(bundle, tempStr); + if (func) { + func(bundle); + // MF:!!! Unload function is never called. Need to deal with this! + } + + __CFBundleGetPlugInData(bundle)->_isDoingDynamicRegistration = false; + if (__CFBundleGetPlugInData(bundle)->_loadOnDemand && (__CFBundleGetPlugInData(bundle)->_instanceCount == 0)) { + /* Unload now if we can/should. */ + CFBundleUnloadExecutable(bundle); + } + } else { + CFDictionaryRemoveValue((CFMutableDictionaryRef)infoDict, CFSTR("CFPlugInNeedsDynamicRegistration")); + } +} + +__private_extern__ void _CFBundleDeallocatePlugIn(CFBundleRef bundle) { + if (__CFBundleGetPlugInData(bundle)->_isPlugIn) { + SInt32 c; + + /* Go through factories disabling them. Disabling these factories should cause them to dealloc since we wouldn't be deallocating if any of the factories had outstanding instances. So go backwards. */ + c = CFArrayGetCount(__CFBundleGetPlugInData(bundle)->_factories); + while (c--) { + _CFPFactoryDisable((_CFPFactory *)CFArrayGetValueAtIndex(__CFBundleGetPlugInData(bundle)->_factories, c)); + } + CFRelease(__CFBundleGetPlugInData(bundle)->_factories); + + __CFBundleGetPlugInData(bundle)->_isPlugIn = false; + } +} + +CFTypeID CFPlugInGetTypeID(void) { + return CFBundleGetTypeID(); +} + +CFPlugInRef CFPlugInCreate(CFAllocatorRef allocator, CFURLRef plugInURL) { + CFBundleRef bundle = CFBundleCreate(allocator, plugInURL); + return (CFPlugInRef)bundle; +} + +CFBundleRef CFPlugInGetBundle(CFPlugInRef plugIn) { + return (CFBundleRef)plugIn; +} + +void CFPlugInSetLoadOnDemand(CFPlugInRef plugIn, Boolean flag) { + if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) { + __CFBundleGetPlugInData(plugIn)->_loadOnDemand = flag; + if (__CFBundleGetPlugInData(plugIn)->_loadOnDemand && !__CFBundleGetPlugInData(plugIn)->_isDoingDynamicRegistration && (__CFBundleGetPlugInData(plugIn)->_instanceCount == 0)) { + /* Unload now if we can/should. */ + /* If we are doing dynamic registration currently, do not unload. The unloading will happen when dynamic registration is done, if necessary. */ + CFBundleUnloadExecutable(plugIn); + } else if (!__CFBundleGetPlugInData(plugIn)->_loadOnDemand) { + /* Make sure we're loaded now. */ + CFBundleLoadExecutable(plugIn); + } + } +} + +Boolean CFPlugInIsLoadOnDemand(CFPlugInRef plugIn) { + if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) { + return __CFBundleGetPlugInData(plugIn)->_loadOnDemand; + } else { + return false; + } +} + +__private_extern__ void _CFPlugInWillUnload(CFPlugInRef plugIn) { + if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) { + SInt32 c = CFArrayGetCount(__CFBundleGetPlugInData(plugIn)->_factories); + /* First, flush all the function pointers that may be cached by our factories. */ + while (c--) { + _CFPFactoryFlushFunctionCache((_CFPFactory *)CFArrayGetValueAtIndex(__CFBundleGetPlugInData(plugIn)->_factories, c)); + } + } +} + +__private_extern__ void _CFPlugInAddPlugInInstance(CFPlugInRef plugIn) { + if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) { + if ((__CFBundleGetPlugInData(plugIn)->_instanceCount == 0) && (__CFBundleGetPlugInData(plugIn)->_loadOnDemand)) { + /* Make sure we are not scheduled for unloading */ + _CFBundleUnscheduleForUnloading(CFPlugInGetBundle(plugIn)); + } + __CFBundleGetPlugInData(plugIn)->_instanceCount++; + /* Instances also retain the CFBundle */ + CFRetain(plugIn); + } +} + +__private_extern__ void _CFPlugInRemovePlugInInstance(CFPlugInRef plugIn) { + if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) { + /* MF:!!! Assert that instanceCount > 0. */ + __CFBundleGetPlugInData(plugIn)->_instanceCount--; + if ((__CFBundleGetPlugInData(plugIn)->_instanceCount == 0) && (__CFBundleGetPlugInData(plugIn)->_loadOnDemand)) { + // We unload the code lazily because the code that caused this function to be called is probably code from the plugin itself. If we unload now, we will hose things. + //CFBundleUnloadExecutable(plugIn); + _CFBundleScheduleForUnloading(CFPlugInGetBundle(plugIn)); + } + /* Instances also retain the CFPlugIn */ + /* MF:!!! This will cause immediate unloading if it was the last ref on the plugin. */ + CFRelease(plugIn); + } +} + +__private_extern__ void _CFPlugInAddFactory(CFPlugInRef plugIn, _CFPFactory *factory) { + if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) { + CFArrayAppendValue(__CFBundleGetPlugInData(plugIn)->_factories, factory); + } +} + +__private_extern__ void _CFPlugInRemoveFactory(CFPlugInRef plugIn, _CFPFactory *factory) { + if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) { + SInt32 idx = CFArrayGetFirstIndexOfValue(__CFBundleGetPlugInData(plugIn)->_factories, CFRangeMake(0, CFArrayGetCount(__CFBundleGetPlugInData(plugIn)->_factories)), factory); + if (idx >= 0) { + CFArrayRemoveValueAtIndex(__CFBundleGetPlugInData(plugIn)->_factories, idx); + } + } +}
diff --git a/CoreFoundation/CFPreferences.c b/CoreFoundation/CFPreferences.c new file mode 100644 index 0000000..5369d0f --- /dev/null +++ b/CoreFoundation/CFPreferences.c
@@ -0,0 +1,781 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFPreferences.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Chris Parker +*/ + +#include <CoreFoundation/CFPreferences.h> +#include <CoreFoundation/CFURLAccess.h> +#ifndef DEPLOYMENT_TARGET_WINDOWS +#include <CoreFoundation/CFUserNotification.h> +#endif +#include <CoreFoundation/CFPropertyList.h> +#include <CoreFoundation/CFBundle.h> +#include <CoreFoundation/CFNumber.h> +#include <CoreFoundation/CFPriv.h> +#include "CFPriv.h" +#include "CFInternal.h" +#include <sys/stat.h> +#if !DEPLOYMENT_TARGET_WINDOWS +#include <unistd.h> +#endif + +#if DEBUG_PREFERENCES_MEMORY +#include "../Tests/CFCountingAllocator.c" +#endif + +static CFURLRef _CFPreferencesURLForStandardDomainWithSafetyLevel(CFStringRef domainName, CFStringRef userName, CFStringRef hostName, unsigned long safeLevel); + +struct __CFPreferencesDomain { + CFRuntimeBase _base; + /* WARNING - not copying the callbacks; we know they are always static structs */ + const _CFPreferencesDomainCallBacks *_callBacks; + CFTypeRef _context; + void *_domain; +}; + +CONST_STRING_DECL(kCFPreferencesAnyApplication, "kCFPreferencesAnyApplication") +CONST_STRING_DECL(kCFPreferencesAnyHost, "kCFPreferencesAnyHost") +CONST_STRING_DECL(kCFPreferencesAnyUser, "kCFPreferencesAnyUser") +CONST_STRING_DECL(kCFPreferencesCurrentApplication, "kCFPreferencesCurrentApplication") +CONST_STRING_DECL(kCFPreferencesCurrentHost, "kCFPreferencesCurrentHost") +CONST_STRING_DECL(kCFPreferencesCurrentUser, "kCFPreferencesCurrentUser") + + +static CFAllocatorRef _preferencesAllocator = NULL; +__private_extern__ CFAllocatorRef __CFPreferencesAllocator(void) { + if (!_preferencesAllocator) { +#if DEBUG_PREFERENCES_MEMORY + _preferencesAllocator = CFCountingAllocatorCreate(NULL); +#else + _preferencesAllocator = __CFGetDefaultAllocator(); + CFRetain(_preferencesAllocator); +#endif + } + return _preferencesAllocator; +} + +// declaration for telling the +void _CFApplicationPreferencesDomainHasChanged(CFPreferencesDomainRef); + +#if DEBUG_PREFERENCES_MEMORY +#warning Preferences debugging on +CF_EXPORT void CFPreferencesDumpMem(void) { + if (_preferencesAllocator) { +// CFCountingAllocatorPrintSummary(_preferencesAllocator); + CFCountingAllocatorPrintPointers(_preferencesAllocator); + } +// CFCountingAllocatorReset(_preferencesAllocator); +} +#endif + +#if DEPLOYMENT_TARGET_MACOSX +#pragma mark - +#pragma mark Determining host UUID +#endif + + +__private_extern__ CFStringRef _CFPreferencesGetByHostIdentifierString(void) { + return CFSTR(""); +} + + + +static unsigned long __CFSafeLaunchLevel = 0; + +#if 0 +#include <shfolder.h> + +CF_INLINE CFIndex strlen_UniChar(const UniChar* p) { + CFIndex result = 0; + while ((*p++) != 0) + ++result; + return result; +} + +#endif + +static CFURLRef _preferencesDirectoryForUserHostSafetyLevel(CFStringRef userName, CFStringRef hostName, unsigned long safeLevel) { + CFAllocatorRef alloc = __CFPreferencesAllocator(); +#if 0 + + CFURLRef url = NULL; + + UniChar szPath[MAX_PATH]; + if (S_OK == SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, (LPWSTR) szPath)) { + CFStringRef directoryPath = CFStringCreateWithCharacters(alloc, szPath, strlen_UniChar(szPath)); + if (directoryPath) { + CFStringRef completePath = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@\\Apple\\"), directoryPath); + if (completePath) { + url = CFURLCreateWithFileSystemPath(alloc, completePath, kCFURLWindowsPathStyle, true); + CFRelease(completePath); + } + CFRelease(directoryPath); + } + } + + // Can't find a better place? Home directory then? + if (url == NULL) + url = CFCopyHomeDirectoryURLForUser((userName == kCFPreferencesCurrentUser) ? NULL : userName); + + return url; + +#else + CFURLRef home = NULL; + CFURLRef url; + int levels = 0; + // if (hostName != kCFPreferencesCurrentHost && hostName != kCFPreferencesAnyHost) return NULL; // Arbitrary host access not permitted + if (userName == kCFPreferencesAnyUser) { + if (!home) home = CFURLCreateWithFileSystemPath(alloc, CFSTR("/Library/Preferences/"), kCFURLPOSIXPathStyle, true); + levels = 1; + if (hostName == kCFPreferencesCurrentHost) url = home; + else { + url = CFURLCreateWithFileSystemPathRelativeToBase(alloc, CFSTR("Network/"), kCFURLPOSIXPathStyle, true, home); + levels ++; + CFRelease(home); + } + } else { + home = CFCopyHomeDirectoryURLForUser((userName == kCFPreferencesCurrentUser) ? NULL : userName); + if (home) { + url = (safeLevel > 0) ? CFURLCreateWithFileSystemPathRelativeToBase(alloc, CFSTR("Library/Safe Preferences/"), kCFURLPOSIXPathStyle, true, home) : + CFURLCreateWithFileSystemPathRelativeToBase(alloc, CFSTR("Library/Preferences/"), kCFURLPOSIXPathStyle, true, home); + levels = 2; + CFRelease(home); + if (hostName != kCFPreferencesAnyHost) { + home = url; + url = CFURLCreateWithFileSystemPathRelativeToBase(alloc, CFSTR("ByHost/"), kCFURLPOSIXPathStyle, true, home); + levels ++; + CFRelease(home); + } + } else { + url = NULL; + } + } + return url; +#endif +} + +static CFURLRef _preferencesDirectoryForUserHost(CFStringRef userName, CFStringRef hostName) { + return _preferencesDirectoryForUserHostSafetyLevel(userName, hostName, __CFSafeLaunchLevel); +} + +static Boolean __CFPreferencesWritesXML = true; + +Boolean __CFPreferencesShouldWriteXML(void) { + return __CFPreferencesWritesXML; +} + +static CFSpinLock_t domainCacheLock = CFSpinLockInit; +static CFMutableDictionaryRef domainCache = NULL; // mutable + +// Public API + +CFTypeRef CFPreferencesCopyValue(CFStringRef key, CFStringRef appName, CFStringRef user, CFStringRef host) { + CFPreferencesDomainRef domain; + CFAssert1(appName != NULL && user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__); + CFAssert1(key != NULL, __kCFLogAssertion, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__); + + domain = _CFPreferencesStandardDomain(appName, user, host); + if (domain) { + return _CFPreferencesDomainCreateValueForKey(domain, key); + } else { + return NULL; + } +} + +CFDictionaryRef CFPreferencesCopyMultiple(CFArrayRef keysToFetch, CFStringRef appName, CFStringRef user, CFStringRef host) { + CFPreferencesDomainRef domain; + CFMutableDictionaryRef result; + CFIndex idx, count; + + CFAssert1(appName != NULL && user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__); + __CFGenericValidateType(appName, CFStringGetTypeID()); + __CFGenericValidateType(user, CFStringGetTypeID()); + __CFGenericValidateType(host, CFStringGetTypeID()); + + domain = _CFPreferencesStandardDomain(appName, user, host); + if (!domain) return NULL; + if (!keysToFetch) { + return _CFPreferencesDomainDeepCopyDictionary(domain); + } else { + __CFGenericValidateType(keysToFetch, CFArrayGetTypeID()); + count = CFArrayGetCount(keysToFetch); + result = CFDictionaryCreateMutable(CFGetAllocator(domain), count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (!result) return NULL; + for (idx = 0; idx < count; idx ++) { + CFStringRef key = (CFStringRef)CFArrayGetValueAtIndex(keysToFetch, idx); + CFPropertyListRef value; + __CFGenericValidateType(key, CFStringGetTypeID()); + value = _CFPreferencesDomainCreateValueForKey(domain, key); + if (value) { + CFDictionarySetValue(result, key, value); + CFRelease(value); + } + } + } + return result; +} + +void CFPreferencesSetValue(CFStringRef key, CFTypeRef value, CFStringRef appName, CFStringRef user, CFStringRef host) { + CFPreferencesDomainRef domain; + CFAssert1(appName != NULL && user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__); + CFAssert1(key != NULL, __kCFLogAssertion, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__); + + domain = _CFPreferencesStandardDomain(appName, user, host); + if (domain) { + _CFPreferencesDomainSet(domain, key, value); + _CFApplicationPreferencesDomainHasChanged(domain); + } +} + + +void CFPreferencesSetMultiple(CFDictionaryRef keysToSet, CFArrayRef keysToRemove, CFStringRef appName, CFStringRef user, CFStringRef host) { + CFPreferencesDomainRef domain; + CFIndex idx, count; + CFAssert1(appName != NULL && user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__); + if (keysToSet) __CFGenericValidateType(keysToSet, CFDictionaryGetTypeID()); + if (keysToRemove) __CFGenericValidateType(keysToRemove, CFArrayGetTypeID()); + __CFGenericValidateType(appName, CFStringGetTypeID()); + __CFGenericValidateType(user, CFStringGetTypeID()); + __CFGenericValidateType(host, CFStringGetTypeID()); + + CFTypeRef *keys = NULL; + CFTypeRef *values; + CFIndex numOfKeysToSet = 0; + + domain = _CFPreferencesStandardDomain(appName, user, host); + if (!domain) return; + + CFAllocatorRef alloc = CFGetAllocator(domain); + + if (keysToSet && (count = CFDictionaryGetCount(keysToSet))) { + numOfKeysToSet = count; + keys = (CFTypeRef *)CFAllocatorAllocate(alloc, 2*count*sizeof(CFTypeRef), 0); + if (keys) { + values = &(keys[count]); + CFDictionaryGetKeysAndValues(keysToSet, keys, values); + for (idx = 0; idx < count; idx ++) { + _CFPreferencesDomainSet(domain, (CFStringRef)keys[idx], values[idx]); + } + } + } + if (keysToRemove && (count = CFArrayGetCount(keysToRemove))) { + for (idx = 0; idx < count; idx ++) { + CFStringRef removedKey = (CFStringRef)CFArrayGetValueAtIndex(keysToRemove, idx); + _CFPreferencesDomainSet(domain, removedKey, NULL); + } + } + + + _CFApplicationPreferencesDomainHasChanged(domain); + + if(keys) CFAllocatorDeallocate(alloc, keys); +} + +Boolean CFPreferencesSynchronize(CFStringRef appName, CFStringRef user, CFStringRef host) { + CFPreferencesDomainRef domain; + CFAssert1(appName != NULL && user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__); + + domain = _CFPreferencesStandardDomain(appName, user, host); + if(domain) _CFApplicationPreferencesDomainHasChanged(domain); + + return domain ? _CFPreferencesDomainSynchronize(domain) : false; +} + +CFArrayRef CFPreferencesCopyApplicationList(CFStringRef user, CFStringRef host) { + CFArrayRef array; + CFAssert1(user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL user or host", __PRETTY_FUNCTION__); + array = _CFPreferencesCreateDomainList(user, host); + return array; +} + +CFArrayRef CFPreferencesCopyKeyList(CFStringRef appName, CFStringRef user, CFStringRef host) { + CFPreferencesDomainRef domain; + CFAssert1(appName != NULL && user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__); + + domain = _CFPreferencesStandardDomain(appName, user, host); + if (!domain) { + return NULL; + } else { + CFArrayRef result; + + CFAllocatorRef alloc = __CFPreferencesAllocator(); + CFDictionaryRef d = _CFPreferencesDomainDeepCopyDictionary(domain); + CFIndex count = d ? CFDictionaryGetCount(d) : 0; + CFTypeRef *keys = (CFTypeRef *)CFAllocatorAllocate(alloc, count * sizeof(CFTypeRef), 0); + if (d) CFDictionaryGetKeysAndValues(d, keys, NULL); + if (count == 0) { + result = NULL; + } else { + result = CFArrayCreate(alloc, keys, count, &kCFTypeArrayCallBacks); + } + CFAllocatorDeallocate(alloc, keys); + if (d) CFRelease(d); + return result; + } +} + + +/****************************/ +/* CFPreferencesDomain */ +/****************************/ + +static CFStringRef __CFPreferencesDomainCopyDescription(CFTypeRef cf) { + return CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL, CFSTR("<Private CFType %p>\n"), cf); +} + +static void __CFPreferencesDomainDeallocate(CFTypeRef cf) { + const struct __CFPreferencesDomain *domain = (struct __CFPreferencesDomain *)cf; + CFAllocatorRef alloc = __CFPreferencesAllocator(); + domain->_callBacks->freeDomain(alloc, domain->_context, domain->_domain); + if (domain->_context) CFRelease(domain->_context); +} + +static CFTypeID __kCFPreferencesDomainTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFPreferencesDomainClass = { + 0, + "CFPreferencesDomain", + NULL, // init + NULL, // copy + __CFPreferencesDomainDeallocate, + NULL, + NULL, + NULL, // + __CFPreferencesDomainCopyDescription +}; + +/* This is called once at CFInitialize() time. */ +__private_extern__ void __CFPreferencesDomainInitialize(void) { + __kCFPreferencesDomainTypeID = _CFRuntimeRegisterClass(&__CFPreferencesDomainClass); +} + +/* We spend a lot of time constructing these prefixes; we should cache. REW, 7/19/99 */ +static CFStringRef _CFPreferencesCachePrefixForUserHost(CFStringRef userName, CFStringRef hostName) { + if (userName == kCFPreferencesAnyUser && hostName == kCFPreferencesAnyHost) { + return (CFStringRef)CFRetain(CFSTR("*/*/")); + } + CFMutableStringRef result = CFStringCreateMutable(__CFPreferencesAllocator(), 0); + if (userName == kCFPreferencesCurrentUser) { + userName = CFGetUserName(); + CFStringAppend(result, userName); + CFStringAppend(result, CFSTR("/")); + } else if (userName == kCFPreferencesAnyUser) { + CFStringAppend(result, CFSTR("*/")); + } + if (hostName == kCFPreferencesCurrentHost) { + CFStringRef hostID = _CFPreferencesGetByHostIdentifierString(); + CFStringAppend(result, hostID); + CFStringAppend(result, CFSTR("/")); + } else if (hostName == kCFPreferencesAnyHost) { + CFStringAppend(result, CFSTR("*/")); + } + return result; +} + +// It would be nice if we could remember the key for "well-known" combinations, so we're not constantly allocing more strings.... - REW 2/3/99 +static CFStringRef _CFPreferencesStandardDomainCacheKey(CFStringRef domainName, CFStringRef userName, CFStringRef hostName) { + CFStringRef prefix = _CFPreferencesCachePrefixForUserHost(userName, hostName); + CFStringRef result = NULL; + + if (prefix) { + result = CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL, CFSTR("%@%@"), prefix, domainName); + CFRelease(prefix); + } + return result; +} + +static CFURLRef _CFPreferencesURLForStandardDomainWithSafetyLevel(CFStringRef domainName, CFStringRef userName, CFStringRef hostName, unsigned long safeLevel) { + CFURLRef theURL = NULL; + CFAllocatorRef prefAlloc = __CFPreferencesAllocator(); +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX + CFURLRef prefDir = _preferencesDirectoryForUserHostSafetyLevel(userName, hostName, safeLevel); + CFStringRef appName; + CFStringRef fileName; + Boolean mustFreeAppName = false; + + if (!prefDir) return NULL; + if (domainName == kCFPreferencesAnyApplication) { + appName = CFSTR(".GlobalPreferences"); + } else if (domainName == kCFPreferencesCurrentApplication) { + CFBundleRef mainBundle = CFBundleGetMainBundle(); + appName = mainBundle ? CFBundleGetIdentifier(mainBundle) : NULL; + if (!appName || CFStringGetLength(appName) == 0) { + appName = _CFProcessNameString(); + } + } else { + appName = domainName; + } + if (userName != kCFPreferencesAnyUser) { + if (hostName == kCFPreferencesAnyHost) { + fileName = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR("%@.plist"), appName); + } else if (hostName == kCFPreferencesCurrentHost) { + CFStringRef hostID = _CFPreferencesGetByHostIdentifierString(); + fileName = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR("%@.%@.plist"), appName, hostID); + } else { + fileName = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR("%@.%@.plist"), appName, hostName); // sketchy - this allows someone to set an arbitrary hostname. + } + } else { + fileName = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR("%@.plist"), appName); + } + if (mustFreeAppName) { + CFRelease(appName); + } + if (fileName) { + theURL = CFURLCreateWithFileSystemPathRelativeToBase(prefAlloc, fileName, kCFURLPOSIXPathStyle, false, prefDir); + if (prefDir) CFRelease(prefDir); + CFRelease(fileName); + } +#else +//#error Do not know where to store NSUserDefaults on this platform +#endif + return theURL; +} + +static CFURLRef _CFPreferencesURLForStandardDomain(CFStringRef domainName, CFStringRef userName, CFStringRef hostName) { + return _CFPreferencesURLForStandardDomainWithSafetyLevel(domainName, userName, hostName, __CFSafeLaunchLevel); +} + +CFPreferencesDomainRef _CFPreferencesStandardDomain(CFStringRef domainName, CFStringRef userName, CFStringRef hostName) { + CFPreferencesDomainRef domain; + CFStringRef domainKey; + Boolean shouldReleaseDomain = true; + domainKey = _CFPreferencesStandardDomainCacheKey(domainName, userName, hostName); + __CFSpinLock(&domainCacheLock); + if (!domainCache) { + CFAllocatorRef alloc = __CFPreferencesAllocator(); + domainCache = CFDictionaryCreateMutable(alloc, 0, & kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + } + domain = (CFPreferencesDomainRef)CFDictionaryGetValue(domainCache, domainKey); + __CFSpinUnlock(&domainCacheLock); + if (!domain) { + // Domain's not in the cache; load from permanent storage + CFURLRef theURL = _CFPreferencesURLForStandardDomain(domainName, userName, hostName); + if (theURL) { + domain = _CFPreferencesDomainCreate(theURL, &__kCFXMLPropertyListDomainCallBacks); + + if (userName == kCFPreferencesAnyUser) { + _CFPreferencesDomainSetIsWorldReadable(domain, true); + } + CFRelease(theURL); + } + __CFSpinLock(&domainCacheLock); + if (domain && domainCache) { + // We've just synthesized a domain & we're about to throw it in the domain cache. The problem is that someone else might have gotten in here behind our backs, so we can't just blindly set the domain (3021920). We'll need to check to see if this happened, and compensate. + CFPreferencesDomainRef checkDomain = (CFPreferencesDomainRef)CFDictionaryGetValue(domainCache, domainKey); + if(checkDomain) { + // Someone got in here ahead of us, so we shouldn't smash the domain we're given. checkDomain is the current version, we should use that. + // checkDomain was retrieved with a Get, so we don't want to over-release. + shouldReleaseDomain = false; + CFRelease(domain); // release the domain we synthesized earlier. + domain = checkDomain; // repoint it at the domain picked up out of the cache. + } else { + // We must not have found the domain in the cache, so it's ok for us to put this in. + CFDictionarySetValue(domainCache, domainKey, domain); + } + if(shouldReleaseDomain) CFRelease(domain); + } + __CFSpinUnlock(&domainCacheLock); + } + CFRelease(domainKey); + return domain; +} + +static void __CFPreferencesPerformSynchronize(const void *key, const void *value, void *context) { + CFPreferencesDomainRef domain = (CFPreferencesDomainRef)value; + Boolean *cumulativeResult = (Boolean *)context; + if (!_CFPreferencesDomainSynchronize(domain)) *cumulativeResult = false; +} + +__private_extern__ Boolean _CFSynchronizeDomainCache(void) { + Boolean result = true; + __CFSpinLock(&domainCacheLock); + if (domainCache) { + CFDictionaryApplyFunction(domainCache, __CFPreferencesPerformSynchronize, &result); + } + __CFSpinUnlock(&domainCacheLock); + return result; +} + +__private_extern__ void _CFPreferencesPurgeDomainCache(void) { + _CFSynchronizeDomainCache(); + __CFSpinLock(&domainCacheLock); + if (domainCache) { + CFRelease(domainCache); + domainCache = NULL; + } + __CFSpinUnlock(&domainCacheLock); +} + +__private_extern__ CFArrayRef _CFPreferencesCreateDomainList(CFStringRef userName, CFStringRef hostName) { + CFAllocatorRef prefAlloc = __CFPreferencesAllocator(); + CFArrayRef domains; + CFMutableArrayRef marray; + CFStringRef *cachedDomainKeys; + CFPreferencesDomainRef *cachedDomains; + SInt32 idx, cnt; + CFStringRef suffix; + UInt32 suffixLen; + CFURLRef prefDir = _preferencesDirectoryForUserHost(userName, hostName); + + if (!prefDir) { + return NULL; + } + if (hostName == kCFPreferencesAnyHost) { + suffix = CFStringCreateWithCString(prefAlloc, ".plist", kCFStringEncodingASCII); + } else if (hostName == kCFPreferencesCurrentHost) { + CFStringRef hostID = _CFPreferencesGetByHostIdentifierString(); + suffix = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR(".%@.plist"), hostID); + } else { + suffix = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR(".%@.plist"), hostName); // sketchy - this allows someone to create a domain list for an arbitrary hostname. + } + suffixLen = CFStringGetLength(suffix); + + domains = (CFArrayRef)CFURLCreatePropertyFromResource(prefAlloc, prefDir, kCFURLFileDirectoryContents, NULL); + CFRelease(prefDir); + if (domains){ + marray = CFArrayCreateMutableCopy(prefAlloc, 0, domains); + CFRelease(domains); + } else { + marray = CFArrayCreateMutable(prefAlloc, 0, & kCFTypeArrayCallBacks); + } + for (idx = CFArrayGetCount(marray)-1; idx >= 0; idx --) { + CFURLRef url = (CFURLRef)CFArrayGetValueAtIndex(marray, idx); + CFStringRef string = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); + if (!CFStringHasSuffix(string, suffix)) { + CFArrayRemoveValueAtIndex(marray, idx); + } else { + CFStringRef dom = CFStringCreateWithSubstring(prefAlloc, string, CFRangeMake(0, CFStringGetLength(string) - suffixLen)); + if (CFEqual(dom, CFSTR(".GlobalPreferences"))) { + CFArraySetValueAtIndex(marray, idx, kCFPreferencesAnyApplication); + } else { + CFArraySetValueAtIndex(marray, idx, dom); + } + CFRelease(dom); + } + CFRelease(string); + } + CFRelease(suffix); + + // Now add any domains added in the cache; delete any that have been deleted in the cache + __CFSpinLock(&domainCacheLock); + if (!domainCache) { + __CFSpinUnlock(&domainCacheLock); + return marray; + } + cnt = CFDictionaryGetCount(domainCache); + cachedDomainKeys = (CFStringRef *)CFAllocatorAllocate(prefAlloc, 2 * cnt * sizeof(CFStringRef), 0); + cachedDomains = (CFPreferencesDomainRef *)(cachedDomainKeys + cnt); + CFDictionaryGetKeysAndValues(domainCache, (const void **)cachedDomainKeys, (const void **)cachedDomains); + __CFSpinUnlock(&domainCacheLock); + suffix = _CFPreferencesCachePrefixForUserHost(userName, hostName); + suffixLen = CFStringGetLength(suffix); + + for (idx = 0; idx < cnt; idx ++) { + CFStringRef domainKey = cachedDomainKeys[idx]; + CFPreferencesDomainRef domain = cachedDomains[idx]; + CFStringRef domainName; + CFIndex keyCount = 0; + + if (!CFStringHasPrefix(domainKey, suffix)) continue; + domainName = CFStringCreateWithSubstring(prefAlloc, domainKey, CFRangeMake(suffixLen, CFStringGetLength(domainKey) - suffixLen)); + if (CFEqual(domainName, CFSTR("*"))) { + CFRelease(domainName); + domainName = (CFStringRef)CFRetain(kCFPreferencesAnyApplication); + } else if (CFEqual(domainName, kCFPreferencesCurrentApplication)) { + CFRelease(domainName); + domainName = (CFStringRef)CFRetain(_CFProcessNameString()); + } + CFDictionaryRef d = _CFPreferencesDomainDeepCopyDictionary(domain); + keyCount = d ? CFDictionaryGetCount(d) : 0; + if (keyCount) CFRelease(d); + if (keyCount == 0) { + // Domain was deleted + SInt32 firstIndexOfValue = CFArrayGetFirstIndexOfValue(marray, CFRangeMake(0, CFArrayGetCount(marray)), domainName); + if (0 <= firstIndexOfValue) { + CFArrayRemoveValueAtIndex(marray, firstIndexOfValue); + } + } else if (!CFArrayContainsValue(marray, CFRangeMake(0, CFArrayGetCount(marray)), domainName)) { + CFArrayAppendValue(marray, domainName); + } + CFRelease(domainName); + } + CFRelease(suffix); + CFAllocatorDeallocate(prefAlloc, cachedDomainKeys); + return marray; +} + +// +// CFPreferencesDomain functions +// + +CFPreferencesDomainRef _CFPreferencesDomainCreate(CFTypeRef context, const _CFPreferencesDomainCallBacks *callBacks) { + CFAllocatorRef alloc = __CFPreferencesAllocator(); + CFPreferencesDomainRef newDomain; + CFAssert(callBacks != NULL && callBacks->createDomain != NULL && callBacks->freeDomain != NULL && callBacks->fetchValue != NULL && callBacks->writeValue != NULL, __kCFLogAssertion, "Cannot create a domain with NULL callbacks"); + newDomain = (CFPreferencesDomainRef)_CFRuntimeCreateInstance(alloc, __kCFPreferencesDomainTypeID, sizeof(struct __CFPreferencesDomain) - sizeof(CFRuntimeBase), NULL); + if (newDomain) { + newDomain->_callBacks = callBacks; + if (context) CFRetain(context); + newDomain->_context = context; + newDomain->_domain = callBacks->createDomain(alloc, context); + } + return newDomain; +} + +CFTypeRef _CFPreferencesDomainCreateValueForKey(CFPreferencesDomainRef domain, CFStringRef key) { + return domain->_callBacks->fetchValue(domain->_context, domain->_domain, key); +} + +void _CFPreferencesDomainSet(CFPreferencesDomainRef domain, CFStringRef key, CFTypeRef value) { + domain->_callBacks->writeValue(domain->_context, domain->_domain, key, value); +} + +__private_extern__ Boolean _CFPreferencesDomainSynchronize(CFPreferencesDomainRef domain) { + return domain->_callBacks->synchronize(domain->_context, domain->_domain); +} + +__private_extern__ void _CFPreferencesDomainSetIsWorldReadable(CFPreferencesDomainRef domain, Boolean isWorldReadable) { + if (domain->_callBacks->setIsWorldReadable) { + domain->_callBacks->setIsWorldReadable(domain->_context, domain->_domain, isWorldReadable); + } +} + +__private_extern__ void *_CFPreferencesDomainCopyDictFunc(CFPreferencesDomainRef domain) { + return (void*)domain->_callBacks->copyDomainDictionary; +} + +void _CFPreferencesDomainSetDictionary(CFPreferencesDomainRef domain, CFDictionaryRef dict) { + CFAllocatorRef alloc = __CFPreferencesAllocator(); + CFDictionaryRef d = _CFPreferencesDomainDeepCopyDictionary(domain); + CFIndex idx, count = d ? CFDictionaryGetCount(d) : 0; + + CFTypeRef *keys = (CFTypeRef *)CFAllocatorAllocate(alloc, count * sizeof(CFTypeRef), 0); + if (d) CFDictionaryGetKeysAndValues(d, keys, NULL); + for (idx = 0; idx < count; idx ++) { + _CFPreferencesDomainSet(domain, (CFStringRef)keys[idx], NULL); + } + CFAllocatorDeallocate(alloc, keys); + if (d) CFRelease(d); + + if (dict && (count = CFDictionaryGetCount(dict)) != 0) { + CFStringRef *newKeys = (CFStringRef *)CFAllocatorAllocate(alloc, count * sizeof(CFStringRef), 0); + CFDictionaryGetKeysAndValues(dict, (const void **)newKeys, NULL); + for (idx = 0; idx < count; idx ++) { + CFStringRef key = newKeys[idx]; + _CFPreferencesDomainSet(domain, key, (CFTypeRef)CFDictionaryGetValue(dict, key)); + } + CFAllocatorDeallocate(alloc, newKeys); + } +} + +CFDictionaryRef _CFPreferencesDomainDeepCopyDictionary(CFPreferencesDomainRef domain) { + CFDictionaryRef result = domain->_callBacks->copyDomainDictionary(domain->_context, domain->_domain); + if(result && CFDictionaryGetCount(result) == 0) { + CFRelease(result); + result = NULL; + } + return result; +} + +Boolean _CFPreferencesDomainExists(CFStringRef domainName, CFStringRef userName, CFStringRef hostName) { + CFPreferencesDomainRef domain; + domain = _CFPreferencesStandardDomain(domainName, userName, hostName); + if (domain) { + CFDictionaryRef d = _CFPreferencesDomainDeepCopyDictionary(domain); + if (d) CFRelease(d); + return d != NULL; + } else { + return false; + } +} + +/* Volatile domains - context is ignored; domain is a CFDictionary (mutable) */ +static void *createVolatileDomain(CFAllocatorRef allocator, CFTypeRef context) { + return CFDictionaryCreateMutable(allocator, 0, & kCFTypeDictionaryKeyCallBacks, & kCFTypeDictionaryValueCallBacks); +} + +static void freeVolatileDomain(CFAllocatorRef allocator, CFTypeRef context, void *domain) { + CFRelease((CFTypeRef)domain); +} + +static CFTypeRef fetchVolatileValue(CFTypeRef context, void *domain, CFStringRef key) { + CFTypeRef result = CFDictionaryGetValue((CFMutableDictionaryRef )domain, key); + if (result) CFRetain(result); + return result; +} + +static void writeVolatileValue(CFTypeRef context, void *domain, CFStringRef key, CFTypeRef value) { + if (value) + CFDictionarySetValue((CFMutableDictionaryRef )domain, key, value); + else + CFDictionaryRemoveValue((CFMutableDictionaryRef )domain, key); +} + +static Boolean synchronizeVolatileDomain(CFTypeRef context, void *domain) { + return true; +} + +static void getVolatileKeysAndValues(CFAllocatorRef alloc, CFTypeRef context, void *domain, void **buf[], CFIndex *numKeyValuePairs) { + CFMutableDictionaryRef dict = (CFMutableDictionaryRef)domain; + CFIndex count = CFDictionaryGetCount(dict); + + if (buf) { + void **values; + if ( count < *numKeyValuePairs ) { + values = *buf + count; + CFDictionaryGetKeysAndValues(dict, (const void **)*buf, (const void **)values); + } else if (alloc != kCFAllocatorNull) { + if (*buf) { + *buf = (void **)CFAllocatorReallocate(alloc, *buf, count * 2 * sizeof(void *), 0); + } else { + *buf = (void **)CFAllocatorAllocate(alloc, count*2*sizeof(void *), 0); + } + if (*buf) { + values = *buf + count; + CFDictionaryGetKeysAndValues(dict, (const void **)*buf, (const void **)values); + } + } + } + *numKeyValuePairs = count; +} + +static CFDictionaryRef copyVolatileDomainDictionary(CFTypeRef context, void *volatileDomain) { + CFMutableDictionaryRef dict = (CFMutableDictionaryRef)volatileDomain; + + CFDictionaryRef result = (CFDictionaryRef)CFPropertyListCreateDeepCopy(__CFPreferencesAllocator(), dict, kCFPropertyListImmutable); + return result; +} + +const _CFPreferencesDomainCallBacks __kCFVolatileDomainCallBacks = {createVolatileDomain, freeVolatileDomain, fetchVolatileValue, writeVolatileValue, synchronizeVolatileDomain, getVolatileKeysAndValues, copyVolatileDomainDictionary, NULL};
diff --git a/CoreFoundation/CFPreferences.h b/CoreFoundation/CFPreferences.h new file mode 100644 index 0000000..44d1cef --- /dev/null +++ b/CoreFoundation/CFPreferences.h
@@ -0,0 +1,153 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFPreferences.h + Copyright (c) 1998-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFPREFERENCES__) +#define __COREFOUNDATION_CFPREFERENCES__ 1 + +#include <CoreFoundation/CFBase.h> +#include <CoreFoundation/CFArray.h> +#include <CoreFoundation/CFString.h> + +CF_EXTERN_C_BEGIN + +CF_EXPORT +const CFStringRef kCFPreferencesAnyApplication; +CF_EXPORT +const CFStringRef kCFPreferencesCurrentApplication; +CF_EXPORT +const CFStringRef kCFPreferencesAnyHost; +CF_EXPORT +const CFStringRef kCFPreferencesCurrentHost; +CF_EXPORT +const CFStringRef kCFPreferencesAnyUser; +CF_EXPORT +const CFStringRef kCFPreferencesCurrentUser; + +/* NOTE: All CFPropertyListRef values returned from + CFPreferences API should be assumed to be immutable. +*/ + +/* The "App" functions search the various sources of defaults that + apply to the given application, and should never be called with + kCFPreferencesAnyApplication - only kCFPreferencesCurrentApplication + or an application's ID (its bundle identifier). +*/ + +/* Searches the various sources of application defaults to find the +value for the given key. key must not be NULL. If a value is found, +it returns it; otherwise returns NULL. Caller must release the +returned value */ +CF_EXPORT +CFPropertyListRef CFPreferencesCopyAppValue(CFStringRef key, CFStringRef applicationID); + +/* Convenience to interpret a preferences value as a boolean directly. +Returns false if the key doesn't exist, or has an improper format; under +those conditions, keyExistsAndHasValidFormat (if non-NULL) is set to false */ +CF_EXPORT +Boolean CFPreferencesGetAppBooleanValue(CFStringRef key, CFStringRef applicationID, Boolean *keyExistsAndHasValidFormat); + +/* Convenience to interpret a preferences value as an integer directly. +Returns 0 if the key doesn't exist, or has an improper format; under +those conditions, keyExistsAndHasValidFormat (if non-NULL) is set to false */ +CF_EXPORT +CFIndex CFPreferencesGetAppIntegerValue(CFStringRef key, CFStringRef applicationID, Boolean *keyExistsAndHasValidFormat); + +/* Sets the given value for the given key in the "normal" place for +application preferences. key must not be NULL. If value is NULL, +key is removed instead. */ +CF_EXPORT +void CFPreferencesSetAppValue(CFStringRef key, CFPropertyListRef value, CFStringRef applicationID); + +/* Adds the preferences for the given suite to the app preferences for + the specified application. To write to the suite domain, use + CFPreferencesSetValue(), below, using the suiteName in place + of the appName */ +CF_EXPORT +void CFPreferencesAddSuitePreferencesToApp(CFStringRef applicationID, CFStringRef suiteID); + +CF_EXPORT +void CFPreferencesRemoveSuitePreferencesFromApp(CFStringRef applicationID, CFStringRef suiteID); + +/* Writes all changes in all sources of application defaults. +Returns success or failure. */ +CF_EXPORT +Boolean CFPreferencesAppSynchronize(CFStringRef applicationID); + +/* The primitive get mechanism; all arguments must be non-NULL +(use the constants above for common values). Only the exact +location specified by app-user-host is searched. The returned +CFType must be released by the caller when it is finished with it. */ +CF_EXPORT +CFPropertyListRef CFPreferencesCopyValue(CFStringRef key, CFStringRef applicationID, CFStringRef userName, CFStringRef hostName); + +/* Convenience to fetch multiple keys at once. Keys in +keysToFetch that are not present in the returned dictionary +are not present in the domain. If keysToFetch is NULL, all +keys are fetched. */ +CF_EXPORT +CFDictionaryRef CFPreferencesCopyMultiple(CFArrayRef keysToFetch, CFStringRef applicationID, CFStringRef userName, CFStringRef hostName); + +/* The primitive set function; all arguments except value must be +non-NULL. If value is NULL, the given key is removed */ +CF_EXPORT +void CFPreferencesSetValue(CFStringRef key, CFPropertyListRef value, CFStringRef applicationID, CFStringRef userName, CFStringRef hostName); + +/* Convenience to set multiple values at once. Behavior is undefined +if a key is in both keysToSet and keysToRemove */ +CF_EXPORT +void CFPreferencesSetMultiple(CFDictionaryRef keysToSet, CFArrayRef keysToRemove, CFStringRef applicationID, CFStringRef userName, CFStringRef hostName); + +CF_EXPORT +Boolean CFPreferencesSynchronize(CFStringRef applicationID, CFStringRef userName, CFStringRef hostName); + +/* Constructs and returns the list of the name of all applications +which have preferences in the scope of the given user and host. +The returned value must be released by the caller; neither argument +may be NULL. */ +CF_EXPORT +CFArrayRef CFPreferencesCopyApplicationList(CFStringRef userName, CFStringRef hostName); + +/* Constructs and returns the list of all keys set in the given +location. The returned value must be released by the caller; +all arguments must be non-NULL */ +CF_EXPORT +CFArrayRef CFPreferencesCopyKeyList(CFStringRef applicationID, CFStringRef userName, CFStringRef hostName); + + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFPREFERENCES__ */ +
diff --git a/CoreFoundation/CFPriv.h b/CoreFoundation/CFPriv.h new file mode 100644 index 0000000..b27c59a --- /dev/null +++ b/CoreFoundation/CFPriv.h
@@ -0,0 +1,436 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFPriv.h + Copyright (c) 1998-2007, Apple Inc. All rights reserved. +*/ + +/* + APPLE SPI: NOT TO BE USED OUTSIDE APPLE! +*/ + +#if !defined(__COREFOUNDATION_CFPRIV__) +#define __COREFOUNDATION_CFPRIV__ 1 + +#include <string.h> +#include <CoreFoundation/CFBase.h> +#include <CoreFoundation/CFArray.h> +#include <CoreFoundation/CFString.h> +#include <CoreFoundation/CFURL.h> +#include <CoreFoundation/CFBundlePriv.h> + + +#if defined(__MACH__) +#include <CoreFoundation/CFMachPort.h> +#endif + +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS +#include <CoreFoundation/CFRunLoop.h> +#include <CoreFoundation/CFSocket.h> +#endif + +#if defined(COCOTRON) || (defined(__GNUC__) && HAVE_OBJC_OBJC_H) || DEPLOYMENT_TARGET_MACOSX +#import <objc/objc.h> +#endif + +#if DEPLOYMENT_TARGET_WINDOWS +#include <shlobj.h> +#endif + + +CF_EXTERN_C_BEGIN + +CF_EXPORT intptr_t _CFDoOperation(intptr_t code, intptr_t subcode1, intptr_t subcode2); + +CF_EXPORT void _CFRuntimeSetCFMPresent(void *a); + +CF_EXPORT const char *_CFProcessPath(void); +CF_EXPORT const char **_CFGetProcessPath(void); +CF_EXPORT const char **_CFGetProgname(void); + + +#if defined(__MACH__) +CF_EXPORT CFRunLoopRef CFRunLoopGetMain(void); +CF_EXPORT SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled); + +CF_EXPORT void _CFRunLoopSetCurrent(CFRunLoopRef rl); + +CF_EXPORT Boolean _CFRunLoopModeContainsMode(CFRunLoopRef rl, CFStringRef modeName, CFStringRef candidateContainedName); +CF_EXPORT void _CFRunLoopAddModeToMode(CFRunLoopRef rl, CFStringRef modeName, CFStringRef toModeName); +CF_EXPORT void _CFRunLoopRemoveModeFromMode(CFRunLoopRef rl, CFStringRef modeName, CFStringRef fromModeName); +CF_EXPORT void _CFRunLoopStopMode(CFRunLoopRef rl, CFStringRef modeName); + +#if defined(__MACH__) +CF_EXPORT CFIndex CFMachPortGetQueuedMessageCount(CFMachPortRef mp); +#endif + +CF_EXPORT CFPropertyListRef _CFURLCopyPropertyListRepresentation(CFURLRef url); +CF_EXPORT CFURLRef _CFURLCreateFromPropertyListRepresentation(CFAllocatorRef alloc, CFPropertyListRef pListRepresentation); +#endif +CF_EXPORT CFPropertyListRef _CFURLCopyPropertyListRepresentation(CFURLRef url); +CF_EXPORT CFURLRef _CFURLCreateFromPropertyListRepresentation(CFAllocatorRef alloc, CFPropertyListRef pListRepresentation); + +CF_EXPORT void CFPreferencesFlushCaches(void); + +#if !__LP64__ +#if !defined(__WIN32__) +struct FSSpec; +CF_EXPORT +Boolean _CFGetFSSpecFromURL(CFAllocatorRef alloc, CFURLRef url, struct FSSpec *spec); + +CF_EXPORT +CFURLRef _CFCreateURLFromFSSpec(CFAllocatorRef alloc, const struct FSSpec *voidspec, Boolean isDirectory); +#endif +#endif + +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED +enum { + kCFURLComponentDecompositionNonHierarchical, + kCFURLComponentDecompositionRFC1808, /* use this for RFC 1738 decompositions as well */ + kCFURLComponentDecompositionRFC2396 +}; +typedef CFIndex CFURLComponentDecomposition; + +typedef struct { + CFStringRef scheme; + CFStringRef schemeSpecific; +} CFURLComponentsNonHierarchical; + +typedef struct { + CFStringRef scheme; + CFStringRef user; + CFStringRef password; + CFStringRef host; + CFIndex port; /* kCFNotFound means ignore/omit */ + CFArrayRef pathComponents; + CFStringRef parameterString; + CFStringRef query; + CFStringRef fragment; + CFURLRef baseURL; +} CFURLComponentsRFC1808; + +typedef struct { + CFStringRef scheme; + + /* if the registered name form of the net location is used, userinfo is NULL, port is kCFNotFound, and host is the entire registered name. */ + CFStringRef userinfo; + CFStringRef host; + CFIndex port; + + CFArrayRef pathComponents; + CFStringRef query; + CFStringRef fragment; + CFURLRef baseURL; +} CFURLComponentsRFC2396; + +/* Fills components and returns TRUE if the URL can be decomposed according to decompositionType; FALSE (leaving components unchanged) otherwise. components should be a pointer to the CFURLComponents struct defined above that matches decompositionStyle */ +CF_EXPORT +Boolean _CFURLCopyComponents(CFURLRef url, CFURLComponentDecomposition decompositionType, void *components); + +/* Creates and returns the URL described by components; components should point to the CFURLComponents struct defined above that matches decompositionType. */ +CF_EXPORT +CFURLRef _CFURLCreateFromComponents(CFAllocatorRef alloc, CFURLComponentDecomposition decompositionType, const void *components); +#define CFURLCopyComponents _CFURLCopyComponents +#define CFURLCreateFromComponents _CFURLCreateFromComponents +#endif + + +CF_EXPORT Boolean _CFStringGetFileSystemRepresentation(CFStringRef string, UInt8 *buffer, CFIndex maxBufLen); + +/* If this is publicized, we might need to create a GetBytesPtr type function as well. */ +CF_EXPORT CFStringRef _CFStringCreateWithBytesNoCopy(CFAllocatorRef alloc, const UInt8 *bytes, CFIndex numBytes, CFStringEncoding encoding, Boolean externalFormat, CFAllocatorRef contentsDeallocator); + +/* These return NULL on MacOS 8 */ +CF_EXPORT +CFStringRef CFGetUserName(void); + +CF_EXPORT +CFURLRef CFCopyHomeDirectoryURLForUser(CFStringRef uName); /* Pass NULL for the current user's home directory */ + + +/* Extra user notification key for iPhone */ +CF_EXPORT +const CFStringRef kCFUserNotificationKeyboardTypesKey AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + + +/* + CFCopySearchPathForDirectoriesInDomains returns the various + standard system directories where apps, resources, etc get + installed. Because queries can return multiple directories, + you get back a CFArray (which you should free when done) of + CFStrings. The directories are returned in search path order; + that is, the first place to look is returned first. This API + may return directories that do not exist yet. If NSUserDomain + is included in a query, then the results will contain "~" to + refer to the user's directory. Specify expandTilde to expand + this to the current user's home. Some calls might return no + directories! + ??? On MacOS 8 this function currently returns an empty array. +*/ +enum { + kCFApplicationDirectory = 1, /* supported applications (Applications) */ + kCFDemoApplicationDirectory, /* unsupported applications, demonstration versions (Demos) */ + kCFDeveloperApplicationDirectory, /* developer applications (Developer/Applications) */ + kCFAdminApplicationDirectory, /* system and network administration applications (Administration) */ + kCFLibraryDirectory, /* various user-visible documentation, support, and configuration files, resources (Library) */ + kCFDeveloperDirectory, /* developer resources (Developer) */ + kCFUserDirectory, /* user home directories (Users) */ + kCFDocumentationDirectory, /* documentation (Documentation) */ + kCFDocumentDirectory, /* documents (Library/Documents) */ + kCFAllApplicationsDirectory = 100, /* all directories where applications can occur (ie Applications, Demos, Administration, Developer/Applications) */ + kCFAllLibrariesDirectory = 101 /* all directories where resources can occur (Library, Developer) */ +}; +typedef CFIndex CFSearchPathDirectory; + +enum { + kCFUserDomainMask = 1, /* user's home directory --- place to install user's personal items (~) */ + kCFLocalDomainMask = 2, /* local to the current machine --- place to install items available to everyone on this machine (/Local) */ + kCFNetworkDomainMask = 4, /* publically available location in the local area network --- place to install items available on the network (/Network) */ + kCFSystemDomainMask = 8, /* provided by Apple, unmodifiable (/System) */ + kCFAllDomainsMask = 0x0ffff /* all domains: all of the above and more, future items */ +}; +typedef CFOptionFlags CFSearchPathDomainMask; + +CF_EXPORT +CFArrayRef CFCopySearchPathForDirectoriesInDomains(CFSearchPathDirectory directory, CFSearchPathDomainMask domainMask, Boolean expandTilde); + +/* Obsolete keys */ +CF_EXPORT const CFStringRef kCFFileURLExists; +CF_EXPORT const CFStringRef kCFFileURLPOSIXMode; +CF_EXPORT const CFStringRef kCFFileURLSize; +CF_EXPORT const CFStringRef kCFFileURLDirectoryContents; +CF_EXPORT const CFStringRef kCFFileURLLastModificationTime; +CF_EXPORT const CFStringRef kCFHTTPURLStatusCode; +CF_EXPORT const CFStringRef kCFHTTPURLStatusLine; + + +/* System Version file access */ +CF_EXPORT CFStringRef CFCopySystemVersionString(void); // Human-readable string containing both marketing and build version, should be API'd +CF_EXPORT CFDictionaryRef _CFCopySystemVersionDictionary(void); +CF_EXPORT CFDictionaryRef _CFCopyServerVersionDictionary(void); +CF_EXPORT const CFStringRef _kCFSystemVersionProductNameKey; +CF_EXPORT const CFStringRef _kCFSystemVersionProductCopyrightKey; +CF_EXPORT const CFStringRef _kCFSystemVersionProductVersionKey; +CF_EXPORT const CFStringRef _kCFSystemVersionProductVersionExtraKey; +CF_EXPORT const CFStringRef _kCFSystemVersionProductUserVisibleVersionKey; // For loginwindow; see 2987512 +CF_EXPORT const CFStringRef _kCFSystemVersionBuildVersionKey; +CF_EXPORT const CFStringRef _kCFSystemVersionProductVersionStringKey; // Localized string for the string "Version" +CF_EXPORT const CFStringRef _kCFSystemVersionBuildStringKey; // Localized string for the string "Build" + + +CF_EXPORT void CFMergeSortArray(void *list, CFIndex count, CFIndex elementSize, CFComparatorFunction comparator, void *context); +CF_EXPORT void CFQSortArray(void *list, CFIndex count, CFIndex elementSize, CFComparatorFunction comparator, void *context); + +/* _CFExecutableLinkedOnOrAfter(releaseVersionName) will return YES if the current executable seems to be linked on or after the specified release. Example: If you specify CFSystemVersionPuma (10.1), you will get back true for executables linked on Puma or Jaguar(10.2), but false for those linked on Cheetah (10.0) or any of its software updates (10.0.x). You will also get back false for any app whose version info could not be figured out. + This function caches its results, so no need to cache at call sites. + + Note that for non-MACH this function always returns true. +*/ +enum { + CFSystemVersionCheetah = 0, /* 10.0 */ + CFSystemVersionPuma = 1, /* 10.1 */ + CFSystemVersionJaguar = 2, /* 10.2 */ + CFSystemVersionPanther = 3, /* 10.3 */ + CFSystemVersionPinot = 3, /* Deprecated name for Panther */ + CFSystemVersionTiger = 4, /* 10.4 */ + CFSystemVersionMerlot = 4, /* Deprecated name for Tiger */ + CFSystemVersionLeopard = 5, /* Post-Tiger */ + CFSystemVersionChablis = 5, /* Deprecated name for Leopard */ + CFSystemVersionMax /* This should bump up when new entries are added */ +}; +typedef CFIndex CFSystemVersion; + +CF_EXPORT Boolean _CFExecutableLinkedOnOrAfter(CFSystemVersion version); + + +enum { + kCFStringGraphemeCluster = 1, /* Unicode Grapheme Cluster */ + kCFStringComposedCharacterCluster = 2, /* Compose all non-base (including spacing marks) */ + kCFStringCursorMovementCluster = 3, /* Cluster suitable for cursor movements */ + kCFStringBackwardDeletionCluster = 4 /* Cluster suitable for backward deletion */ +}; +typedef CFIndex CFStringCharacterClusterType; + +CF_EXPORT CFRange CFStringGetRangeOfCharacterClusterAtIndex(CFStringRef string, CFIndex charIndex, CFStringCharacterClusterType type); + +// Compatibility kCFCompare flags. Use the new public kCFCompareDiacriticInsensitive +enum { + kCFCompareDiacriticsInsensitive = 128, /* kCFCompareDiacriticInsensitive */ + kCFCompareDiacriticsInsensitiveCompatibilityMask = ((1 << 28)|kCFCompareDiacriticsInsensitive), +}; + +/* CFStringEncoding SPI */ +/* When set, CF encoding conversion engine keeps ASCII compatibility. (i.e. ASCII backslash <-> Unicode backslash in MacJapanese */ +CF_EXPORT void _CFStringEncodingSetForceASCIICompatibility(Boolean flag); + +#if defined(CF_INLINE) +CF_INLINE const UniChar *CFStringGetCharactersPtrFromInlineBuffer(CFStringInlineBuffer *buf, CFRange desiredRange) { + if ((desiredRange.location < 0) || ((desiredRange.location + desiredRange.length) > buf->rangeToBuffer.length)) return NULL; + + if (buf->directBuffer) { + return buf->directBuffer + buf->rangeToBuffer.location + desiredRange.location; + } else { + if (desiredRange.length > __kCFStringInlineBufferLength) return NULL; + + if (((desiredRange.location + desiredRange.length) > buf->bufferedRangeEnd) || (desiredRange.location < buf->bufferedRangeStart)) { + buf->bufferedRangeStart = desiredRange.location; + buf->bufferedRangeEnd = buf->bufferedRangeStart + __kCFStringInlineBufferLength; + if (buf->bufferedRangeEnd > buf->rangeToBuffer.length) buf->bufferedRangeEnd = buf->rangeToBuffer.length; + CFStringGetCharacters(buf->theString, CFRangeMake(buf->rangeToBuffer.location + buf->bufferedRangeStart, buf->bufferedRangeEnd - buf->bufferedRangeStart), buf->buffer); + } + + return buf->buffer + (desiredRange.location - buf->bufferedRangeStart); + } +} + +CF_INLINE void CFStringGetCharactersFromInlineBuffer(CFStringInlineBuffer *buf, CFRange desiredRange, UniChar *outBuf) { + if (buf->directBuffer) { + memmove(outBuf, buf->directBuffer + buf->rangeToBuffer.location + desiredRange.location, desiredRange.length * sizeof(UniChar)); + } else { + if ((desiredRange.location >= buf->bufferedRangeStart) && (desiredRange.location < buf->bufferedRangeEnd)) { + CFIndex bufLen = desiredRange.length; + + if (bufLen > (buf->bufferedRangeEnd - desiredRange.location)) bufLen = (buf->bufferedRangeEnd - desiredRange.location); + + memmove(outBuf, buf->buffer + (desiredRange.location - buf->bufferedRangeStart), bufLen * sizeof(UniChar)); + outBuf += bufLen; desiredRange.location += bufLen; desiredRange.length -= bufLen; + } else { + CFIndex desiredRangeMax = (desiredRange.location + desiredRange.length); + + if ((desiredRangeMax > buf->bufferedRangeStart) && (desiredRangeMax < buf->bufferedRangeEnd)) { + desiredRange.length = (buf->bufferedRangeStart - desiredRange.location); + memmove(outBuf + desiredRange.length, buf->buffer, (desiredRangeMax - buf->bufferedRangeStart) * sizeof(UniChar)); + } + } + + if (desiredRange.length > 0) CFStringGetCharacters(buf->theString, CFRangeMake(buf->rangeToBuffer.location + desiredRange.location, desiredRange.length), outBuf); + } +} + +#else +#define CFStringGetCharactersPtrFromInlineBuffer(buf, desiredRange) ((buf)->directBuffer ? (buf)->directBuffer + (buf)->rangeToBuffer.location + desiredRange.location : NULL) + +#define CFStringGetCharactersFromInlineBuffer(buf, desiredRange, outBuf) \ + if (buf->directBuffer) memmove(outBuf, (buf)->directBuffer + (buf)->rangeToBuffer.location + desiredRange.location, desiredRange.length * sizeof(UniChar)); \ + else CFStringGetCharacters((buf)->theString, CFRangeMake((buf)->rangeToBuffer.location + desiredRange.location, desiredRange.length), outBuf); + +#endif /* CF_INLINE */ + +/* + CFCharacterSetInlineBuffer related declarations + */ +/*! +@typedef CFCharacterSetInlineBuffer + @field cset The character set this inline buffer is initialized with. + The object is not retained by the structure. + @field flags The field is a bit mask that carries various settings. + @field rangeStart The beginning of the character range that contains all members. + It is guaranteed that there is no member below this value. + @field rangeLimit The end of the character range that contains all members. + It is guaranteed that there is no member above and equal to this value. + @field bitmap The bitmap data representing the membership of the Basic Multilingual Plane characters. + If NULL, all BMP characters inside the range are members of the character set. + */ +typedef struct { + CFCharacterSetRef cset; + uint32_t flags; + uint32_t rangeStart; + uint32_t rangeLimit; + const uint8_t *bitmap; +} CFCharacterSetInlineBuffer; + +// Bits for flags field +enum { + kCFCharacterSetIsCompactBitmap = (1 << 0), + kCFCharacterSetNoBitmapAvailable = (1 << 1), + kCFCharacterSetIsInverted = (1 << 2) +}; + +/*! +@function CFCharacterSetInitInlineBuffer + Initializes buffer with cset. + @param cset The character set used to initialized the buffer. + If this parameter is not a valid CFCharacterSet, the behavior is undefined. + @param buffer The reference to the inline buffer to be initialized. + */ +CF_EXPORT +void CFCharacterSetInitInlineBuffer(CFCharacterSetRef cset, CFCharacterSetInlineBuffer *buffer); + +/*! +@function CFCharacterSetInlineBufferIsLongCharacterMember + Reports whether or not the UTF-32 character is in the character set. + @param buffer The reference to the inline buffer to be searched. + @param character The UTF-32 character for which to test against the + character set. + @result true, if the value is in the character set, otherwise false. + */ +#if defined(CF_INLINE) +CF_INLINE bool CFCharacterSetInlineBufferIsLongCharacterMember(CFCharacterSetInlineBuffer *buffer, UTF32Char character) { + bool isInverted = ((0 == (buffer->flags & kCFCharacterSetIsInverted)) ? false : true); + + if ((character >= buffer->rangeStart) && (character < buffer->rangeLimit)) { + if ((character > 0xFFFF) || (0 != (buffer->flags & kCFCharacterSetNoBitmapAvailable))) return (CFCharacterSetIsLongCharacterMember(buffer->cset, character) != 0); + if (NULL == buffer->bitmap) { + if (0 == (buffer->flags & kCFCharacterSetIsCompactBitmap)) isInverted = !isInverted; + } else if (0 == (buffer->flags & kCFCharacterSetIsCompactBitmap)) { + if (buffer->bitmap[character >> 3] & (1 << (character & 7))) isInverted = !isInverted; + } else { + uint8_t value = buffer->bitmap[character >> 8]; + + if (value == 0xFF) { + isInverted = !isInverted; + } else if (value > 0) { + const uint8_t *segment = buffer->bitmap + (256 + (32 * (value - 1))); + character &= 0xFF; + if (segment[character >> 3] & (1 << (character % 8))) isInverted = !isInverted; + } + } + } + return isInverted; +} +#else /* CF_INLINE */ +#define CFCharacterSetInlineBufferIsLongCharacterMember(buffer, character) (CFCharacterSetIsLongCharacterMember(buffer->cset, character)) +#endif /* CF_INLINE */ + + +#if defined(__MACH__) +#include <CoreFoundation/CFMessagePort.h> + +CFMessagePortRef CFMessagePortCreatePerProcessLocal(CFAllocatorRef allocator, CFStringRef name, CFMessagePortCallBack callout, CFMessagePortContext *context, Boolean *shouldFreeInfo); +CFMessagePortRef CFMessagePortCreatePerProcessRemote(CFAllocatorRef allocator, CFStringRef name, CFIndex pid); +#endif + + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFPRIV__ */ +
diff --git a/CoreFoundation/CFPropertyList.c b/CoreFoundation/CFPropertyList.c new file mode 100644 index 0000000..a1c99c7 --- /dev/null +++ b/CoreFoundation/CFPropertyList.c
@@ -0,0 +1,2724 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFPropertyList.c + Copyright 1999-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include <CoreFoundation/CFPropertyList.h> +#include <CoreFoundation/CFDate.h> +#include <CoreFoundation/CFNumber.h> +#include <CoreFoundation/CFSet.h> +#include "CFPriv.h" +#include "CFStringEncodingConverter.h" +#include "CFInternal.h" +#include <CoreFoundation/CFStream.h> +#include <CoreFoundation/CFCalendar.h> +#include <limits.h> +#include <float.h> +#include <string.h> +#include <stdlib.h> +#include <math.h> +#include <ctype.h> +#include <CoreFoundation/CFPreferences.h> +#if DEPLOYMENT_TARGET_MACOSX +#include <mach-o/dyld.h> +#endif + + +__private_extern__ bool allowMissingSemi = false; + +// Should move this somewhere else +intptr_t _CFDoOperation(intptr_t code, intptr_t subcode1, intptr_t subcode2) { + switch (code) { + case 15317: allowMissingSemi = subcode1 ? true : false; break; + } + return code; +} + +#define PLIST_IX 0 +#define ARRAY_IX 1 +#define DICT_IX 2 +#define KEY_IX 3 +#define STRING_IX 4 +#define DATA_IX 5 +#define DATE_IX 6 +#define REAL_IX 7 +#define INTEGER_IX 8 +#define TRUE_IX 9 +#define FALSE_IX 10 +#define DOCTYPE_IX 11 +#define CDSECT_IX 12 + +#define PLIST_TAG_LENGTH 5 +#define ARRAY_TAG_LENGTH 5 +#define DICT_TAG_LENGTH 4 +#define KEY_TAG_LENGTH 3 +#define STRING_TAG_LENGTH 6 +#define DATA_TAG_LENGTH 4 +#define DATE_TAG_LENGTH 4 +#define REAL_TAG_LENGTH 4 +#define INTEGER_TAG_LENGTH 7 +#define TRUE_TAG_LENGTH 4 +#define FALSE_TAG_LENGTH 5 +#define DOCTYPE_TAG_LENGTH 7 +#define CDSECT_TAG_LENGTH 9 + +// don't allow _CFKeyedArchiverUID here +#define __CFAssertIsPList(cf) CFAssert2(CFGetTypeID(cf) == CFStringGetTypeID() || CFGetTypeID(cf) == CFArrayGetTypeID() || CFGetTypeID(cf) == CFBooleanGetTypeID() || CFGetTypeID(cf) == CFNumberGetTypeID() || CFGetTypeID(cf) == CFDictionaryGetTypeID() || CFGetTypeID(cf) == CFDateGetTypeID() || CFGetTypeID(cf) == CFDataGetTypeID(), __kCFLogAssertion, "%s(): %p not of a property list type", __PRETTY_FUNCTION__, cf); + +static bool __CFPropertyListIsValidAux(CFPropertyListRef plist, bool recursive, CFMutableSetRef set, CFPropertyListFormat format); + +static CFTypeID stringtype = -1, datatype = -1, numbertype = -1, datetype = -1; +static CFTypeID booltype = -1, nulltype = -1, dicttype = -1, arraytype = -1, settype = -1; + +static void initStatics() { + if ((CFTypeID)-1 == stringtype) { + stringtype = CFStringGetTypeID(); + } + if ((CFTypeID)-1 == datatype) { + datatype = CFDataGetTypeID(); + } + if ((CFTypeID)-1 == numbertype) { + numbertype = CFNumberGetTypeID(); + } + if ((CFTypeID)-1 == booltype) { + booltype = CFBooleanGetTypeID(); + } + if ((CFTypeID)-1 == datetype) { + datetype = CFDateGetTypeID(); + } + if ((CFTypeID)-1 == dicttype) { + dicttype = CFDictionaryGetTypeID(); + } + if ((CFTypeID)-1 == arraytype) { + arraytype = CFArrayGetTypeID(); + } + if ((CFTypeID)-1 == settype) { + settype = CFSetGetTypeID(); + } + if ((CFTypeID)-1 == nulltype) { + nulltype = CFNullGetTypeID(); + } +} + +struct context { + bool answer; + CFMutableSetRef set; + CFPropertyListFormat format; +}; + +static void __CFPropertyListIsArrayPlistAux(const void *value, void *context) { + struct context *ctx = (struct context *)context; + if (!ctx->answer) return; +#if defined(DEBUG) + if (!value) CFLog(kCFLogLevelWarning, CFSTR("CFPropertyListIsValid(): property list arrays cannot contain NULL")); +#endif + ctx->answer = value && __CFPropertyListIsValidAux(value, true, ctx->set, ctx->format); +} + +static void __CFPropertyListIsDictPlistAux(const void *key, const void *value, void *context) { + struct context *ctx = (struct context *)context; + if (!ctx->answer) return; +#if defined(DEBUG) + if (!key) CFLog(kCFLogLevelWarning, CFSTR("CFPropertyListIsValid(): property list dictionaries cannot contain NULL keys")); + if (!value) CFLog(kCFLogLevelWarning, CFSTR("CFPropertyListIsValid(): property list dictionaries cannot contain NULL values")); + if (stringtype != CFGetTypeID(key)) { + CFStringRef desc = CFCopyTypeIDDescription(CFGetTypeID(key)); + CFLog(kCFLogLevelWarning, CFSTR("CFPropertyListIsValid(): property list dictionaries may only have keys which are CFStrings, not '%@'"), desc); + CFRelease(desc); + } +#endif + ctx->answer = key && value && (stringtype == CFGetTypeID(key)) && __CFPropertyListIsValidAux(value, true, ctx->set, ctx->format); +} + +static bool __CFPropertyListIsValidAux(CFPropertyListRef plist, bool recursive, CFMutableSetRef set, CFPropertyListFormat format) { + CFTypeID type; +#if defined(DEBUG) + if (!plist) CFLog(kCFLogLevelWarning, CFSTR("CFPropertyListIsValid(): property lists cannot contain NULL")); +#endif + if (!plist) return false; + type = CFGetTypeID(plist); + if (stringtype == type) return true; + if (datatype == type) return true; + if (kCFPropertyListOpenStepFormat != format) { + if (booltype == type) return true; + if (numbertype == type) return true; + if (datetype == type) return true; +#if DEPLOYMENT_TARGET_MACOSX + if (_CFKeyedArchiverUIDGetTypeID() == type) return true; +#endif + } + if (!recursive && arraytype == type) return true; + if (!recursive && dicttype == type) return true; + // at any one invocation of this function, set should contain the objects in the "path" down to this object +#if defined(DEBUG) + if (CFSetContainsValue(set, plist)) CFLog(kCFLogLevelWarning, CFSTR("CFPropertyListIsValid(): property lists cannot contain recursive container references")); +#endif + if (CFSetContainsValue(set, plist)) return false; + if (arraytype == type) { + struct context ctx = {true, set, format}; + CFSetAddValue(set, plist); + CFArrayApplyFunction((CFArrayRef)plist, CFRangeMake(0, CFArrayGetCount((CFArrayRef)plist)), __CFPropertyListIsArrayPlistAux, &ctx); + CFSetRemoveValue(set, plist); + return ctx.answer; + } + if (dicttype == type) { + struct context ctx = {true, set, format}; + CFSetAddValue(set, plist); + CFDictionaryApplyFunction((CFDictionaryRef)plist, __CFPropertyListIsDictPlistAux, &ctx); + CFSetRemoveValue(set, plist); + return ctx.answer; + } +#if defined(DEBUG) + { + CFStringRef desc = CFCopyTypeIDDescription(type); + CFLog(kCFLogLevelWarning, CFSTR("CFPropertyListIsValid(): property lists cannot contain objects of type '%@'"), desc); + CFRelease(desc); + } +#endif + return false; +} + +Boolean CFPropertyListIsValid(CFPropertyListRef plist, CFPropertyListFormat format) { + initStatics(); + CFAssert1(plist != NULL, __kCFLogAssertion, "%s(): NULL is not a property list", __PRETTY_FUNCTION__); + CFMutableSetRef set = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, NULL); + bool result = __CFPropertyListIsValidAux(plist, true, set, format); + CFRelease(set); + return result; +} + +static const UniChar CFXMLPlistTags[13][10]= { +{'p', 'l', 'i', 's', 't', '\0', '\0', '\0', '\0', '\0'}, +{'a', 'r', 'r', 'a', 'y', '\0', '\0', '\0', '\0', '\0'}, +{'d', 'i', 'c', 't', '\0', '\0', '\0', '\0', '\0', '\0'}, +{'k', 'e', 'y', '\0', '\0', '\0', '\0', '\0', '\0', '\0'}, +{'s', 't', 'r', 'i', 'n', 'g', '\0', '\0', '\0', '\0'}, +{'d', 'a', 't', 'a', '\0', '\0', '\0', '\0', '\0', '\0'}, +{'d', 'a', 't', 'e', '\0', '\0', '\0', '\0', '\0', '\0'}, +{'r', 'e', 'a', 'l', '\0', '\0', '\0', '\0', '\0', '\0'}, +{'i', 'n', 't', 'e', 'g', 'e', 'r', '\0', '\0', '\0'}, +{'t', 'r', 'u', 'e', '\0', '\0', '\0', '\0', '\0', '\0'}, +{'f', 'a', 'l', 's', 'e', '\0', '\0', '\0', '\0', '\0'}, +{'D', 'O', 'C', 'T', 'Y', 'P', 'E', '\0', '\0', '\0'}, +{'<', '!', '[', 'C', 'D', 'A', 'T', 'A', '[', '\0'} +}; + +typedef struct { + const UniChar *begin; // first character of the XML to be parsed + const UniChar *curr; // current parse location + const UniChar *end; // the first character _after_ the end of the XML + CFStringRef errorString; + CFAllocatorRef allocator; + UInt32 mutabilityOption; + CFMutableSetRef stringSet; // set of all strings involved in this parse; allows us to share non-mutable strings in the returned plist + CFMutableStringRef tmpString; // Mutable string with external characters that functions can feel free to use as temporary storage as the parse progresses + Boolean allowNewTypes; // Whether to allow the new types supported by XML property lists, but not by the old, OPENSTEP ASCII property lists (CFNumber, CFBoolean, CFDate) + char _padding[3]; +} _CFXMLPlistParseInfo; + +static CFTypeRef parseOldStylePropertyListOrStringsFile(_CFXMLPlistParseInfo *pInfo); + + + +// The following set of _plist... functions append various things to a mutable data which is in UTF8 encoding. These are pretty general. Assumption is call characters and CFStrings can be converted to UTF8 and appeneded. + +// Null-terminated, ASCII or UTF8 string +// +static void _plistAppendUTF8CString(CFMutableDataRef mData, const char *cString) { + CFDataAppendBytes (mData, (const UInt8 *)cString, (CFIndex)strlen(cString)); +} + +// UniChars +// +static void _plistAppendCharacters(CFMutableDataRef mData, const UniChar *chars, CFIndex length) { + CFIndex curLoc = 0; + + do { // Flush out ASCII chars, BUFLEN at a time + #define BUFLEN 400 + UInt8 buf[BUFLEN], *bufPtr = buf; + CFIndex cnt = 0; + while (cnt < length && (cnt - curLoc < BUFLEN) && (chars[cnt] < 128)) *bufPtr++ = (UInt8)(chars[cnt++]); + if (cnt > curLoc) { // Flush any ASCII bytes + CFDataAppendBytes(mData, buf, cnt - curLoc); + curLoc = cnt; + } + } while (curLoc < length && (chars[curLoc] < 128)); // We will exit out of here when we run out of chars or hit a non-ASCII char + + if (curLoc < length) { // Now deal with non-ASCII chars + CFDataRef data = NULL; + CFStringRef str = NULL; + if ((str = CFStringCreateWithCharactersNoCopy(kCFAllocatorSystemDefault, chars + curLoc, length - curLoc, kCFAllocatorNull))) { + if ((data = CFStringCreateExternalRepresentation(kCFAllocatorSystemDefault, str, kCFStringEncodingUTF8, 0))) { + CFDataAppendBytes (mData, CFDataGetBytePtr(data), CFDataGetLength(data)); + CFRelease(data); + } + CFRelease(str); + } + CFAssert1(str && data, __kCFLogAssertion, "%s(): Error writing plist", __PRETTY_FUNCTION__); + } +} + +// Append CFString +// +static void _plistAppendString(CFMutableDataRef mData, CFStringRef str) { + const UniChar *chars; + const char *cStr; + CFDataRef data; + if ((chars = CFStringGetCharactersPtr(str))) { + _plistAppendCharacters(mData, chars, CFStringGetLength(str)); + } else if ((cStr = CFStringGetCStringPtr(str, kCFStringEncodingASCII)) || (cStr = CFStringGetCStringPtr(str, kCFStringEncodingUTF8))) { + _plistAppendUTF8CString(mData, cStr); + } else if ((data = CFStringCreateExternalRepresentation(kCFAllocatorSystemDefault, str, kCFStringEncodingUTF8, 0))) { + CFDataAppendBytes (mData, CFDataGetBytePtr(data), CFDataGetLength(data)); + CFRelease(data); + } else { + CFAssert1(TRUE, __kCFLogAssertion, "%s(): Error in plist writing", __PRETTY_FUNCTION__); + } +} + + +// Append CFString-style format + arguments +// +static void _plistAppendFormat(CFMutableDataRef mData, CFStringRef format, ...) { + CFStringRef fStr; + va_list argList; + + va_start(argList, format); + fStr = CFStringCreateWithFormatAndArguments(kCFAllocatorSystemDefault, NULL, format, argList); + va_end(argList); + + CFAssert1(fStr, __kCFLogAssertion, "%s(): Error writing plist", __PRETTY_FUNCTION__); + _plistAppendString(mData, fStr); + CFRelease(fStr); +} + + + +static void _appendIndents(CFIndex numIndents, CFMutableDataRef str) { +#define NUMTABS 4 + static const UniChar tabs[NUMTABS] = {'\t','\t','\t','\t'}; + for (; numIndents > 0; numIndents -= NUMTABS) _plistAppendCharacters(str, tabs, (numIndents >= NUMTABS) ? NUMTABS : numIndents); +} + +/* Append the escaped version of origStr to mStr. +*/ +static void _appendEscapedString(CFStringRef origStr, CFMutableDataRef mStr) { +#define BUFSIZE 64 + CFIndex i, length = CFStringGetLength(origStr); + CFIndex bufCnt = 0; + UniChar buf[BUFSIZE]; + CFStringInlineBuffer inlineBuffer; + + CFStringInitInlineBuffer(origStr, &inlineBuffer, CFRangeMake(0, length)); + + for (i = 0; i < length; i ++) { + UniChar ch = __CFStringGetCharacterFromInlineBufferQuick(&inlineBuffer, i); + switch(ch) { + case '<': + if (bufCnt) _plistAppendCharacters(mStr, buf, bufCnt); + bufCnt = 0; + _plistAppendUTF8CString(mStr, "<"); + break; + case '>': + if (bufCnt) _plistAppendCharacters(mStr, buf, bufCnt); + bufCnt = 0; + _plistAppendUTF8CString(mStr, ">"); + break; + case '&': + if (bufCnt) _plistAppendCharacters(mStr, buf, bufCnt); + bufCnt = 0; + _plistAppendUTF8CString(mStr, "&"); + break; + default: + buf[bufCnt++] = ch; + if (bufCnt == BUFSIZE) { + _plistAppendCharacters(mStr, buf, bufCnt); + bufCnt = 0; + } + break; + } + } + if (bufCnt) _plistAppendCharacters(mStr, buf, bufCnt); +} + + + +/* Base-64 encoding/decoding */ + +/* The base-64 encoding packs three 8-bit bytes into four 7-bit ASCII + * characters. If the number of bytes in the original data isn't divisable + * by three, "=" characters are used to pad the encoded data. The complete + * set of characters used in base-64 are: + * + * 'A'..'Z' => 00..25 + * 'a'..'z' => 26..51 + * '0'..'9' => 52..61 + * '+' => 62 + * '/' => 63 + * '=' => pad + */ + +// Write the inputData to the mData using Base 64 encoding + +static void _XMLPlistAppendDataUsingBase64(CFMutableDataRef mData, CFDataRef inputData, CFIndex indent) { + static const char __CFPLDataEncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + #define MAXLINELEN 76 + char buf[MAXLINELEN + 4 + 2]; // For the slop and carriage return and terminating NULL + + const uint8_t *bytes = CFDataGetBytePtr(inputData); + CFIndex length = CFDataGetLength(inputData); + CFIndex i, pos; + const uint8_t *p; + + if (indent > 8) indent = 8; // refuse to indent more than 64 characters + + pos = 0; // position within buf + + for (i = 0, p = bytes; i < length; i++, p++) { + /* 3 bytes are encoded as 4 */ + switch (i % 3) { + case 0: + buf[pos++] = __CFPLDataEncodeTable [ ((p[0] >> 2) & 0x3f)]; + break; + case 1: + buf[pos++] = __CFPLDataEncodeTable [ ((((p[-1] << 8) | p[0]) >> 4) & 0x3f)]; + break; + case 2: + buf[pos++] = __CFPLDataEncodeTable [ ((((p[-1] << 8) | p[0]) >> 6) & 0x3f)]; + buf[pos++] = __CFPLDataEncodeTable [ (p[0] & 0x3f)]; + break; + } + /* Flush the line out every 76 (or fewer) chars --- indents count against the line length*/ + if (pos >= MAXLINELEN - 8 * indent) { + buf[pos++] = '\n'; + buf[pos++] = 0; + _appendIndents(indent, mData); + _plistAppendUTF8CString(mData, buf); + pos = 0; + } + } + + switch (i % 3) { + case 0: + break; + case 1: + buf[pos++] = __CFPLDataEncodeTable [ ((p[-1] << 4) & 0x30)]; + buf[pos++] = '='; + buf[pos++] = '='; + break; + case 2: + buf[pos++] = __CFPLDataEncodeTable [ ((p[-1] << 2) & 0x3c)]; + buf[pos++] = '='; + break; + } + + if (pos > 0) { + buf[pos++] = '\n'; + buf[pos++] = 0; + _appendIndents(indent, mData); + _plistAppendUTF8CString(mData, buf); + } +} + +extern CFStringRef __CFNumberCopyFormattingDescriptionAsFloat64(CFTypeRef cf); + +static void _CFAppendXML0(CFTypeRef object, UInt32 indentation, CFMutableDataRef xmlString) { + UInt32 typeID = CFGetTypeID(object); + _appendIndents(indentation, xmlString); + if (typeID == stringtype) { + _plistAppendUTF8CString(xmlString, "<"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[STRING_IX], STRING_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">"); + _appendEscapedString((CFStringRef)object, xmlString); + _plistAppendUTF8CString(xmlString, "</"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[STRING_IX], STRING_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">\n"); +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX + } else if (typeID == _CFKeyedArchiverUIDGetTypeID()) { + _plistAppendUTF8CString(xmlString, "<"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[DICT_IX], DICT_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">\n"); + _appendIndents(indentation+1, xmlString); + _plistAppendUTF8CString(xmlString, "<"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[KEY_IX], KEY_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">"); + _appendEscapedString(CFSTR("CF$UID"), xmlString); + _plistAppendUTF8CString(xmlString, "</"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[KEY_IX], KEY_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">\n"); + _appendIndents(indentation + 1, xmlString); + _plistAppendUTF8CString(xmlString, "<"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[INTEGER_IX], INTEGER_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">"); + + uint64_t v = _CFKeyedArchiverUIDGetValue((CFKeyedArchiverUIDRef)object); + CFNumberRef num = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberSInt64Type, &v); + _plistAppendFormat(xmlString, CFSTR("%@"), num); + CFRelease(num); + + _plistAppendUTF8CString(xmlString, "</"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[INTEGER_IX], INTEGER_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">\n"); + _appendIndents(indentation, xmlString); + _plistAppendUTF8CString(xmlString, "</"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[DICT_IX], DICT_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">\n"); +#elif 0 +#else +#error Unknown or unspecified DEPLOYMENT_TARGET +#endif + } else if (typeID == arraytype) { + UInt32 i, count = CFArrayGetCount((CFArrayRef)object); + if (count == 0) { + _plistAppendUTF8CString(xmlString, "<"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[ARRAY_IX], ARRAY_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, "/>\n"); + return; + } + _plistAppendUTF8CString(xmlString, "<"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[ARRAY_IX], ARRAY_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">\n"); + for (i = 0; i < count; i ++) { + _CFAppendXML0(CFArrayGetValueAtIndex((CFArrayRef)object, i), indentation+1, xmlString); + } + _appendIndents(indentation, xmlString); + _plistAppendUTF8CString(xmlString, "</"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[ARRAY_IX], ARRAY_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">\n"); + } else if (typeID == dicttype) { + UInt32 i, count = CFDictionaryGetCount((CFDictionaryRef)object); + CFMutableArrayRef keyArray; + CFTypeRef *keys; + if (count == 0) { + _plistAppendUTF8CString(xmlString, "<"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[DICT_IX], DICT_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, "/>\n"); + return; + } + _plistAppendUTF8CString(xmlString, "<"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[DICT_IX], DICT_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">\n"); + keys = (CFTypeRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, count * sizeof(CFTypeRef), 0); + CFDictionaryGetKeysAndValues((CFDictionaryRef)object, keys, NULL); + keyArray = CFArrayCreateMutable(kCFAllocatorSystemDefault, count, &kCFTypeArrayCallBacks); + CFArrayReplaceValues(keyArray, CFRangeMake(0, 0), keys, count); + CFArraySortValues(keyArray, CFRangeMake(0, count), (CFComparatorFunction)CFStringCompare, NULL); + CFArrayGetValues(keyArray, CFRangeMake(0, count), keys); + CFRelease(keyArray); + for (i = 0; i < count; i ++) { + CFTypeRef key = keys[i]; + _appendIndents(indentation+1, xmlString); + _plistAppendUTF8CString(xmlString, "<"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[KEY_IX], KEY_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">"); + _appendEscapedString((CFStringRef)key, xmlString); + _plistAppendUTF8CString(xmlString, "</"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[KEY_IX], KEY_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">\n"); + _CFAppendXML0(CFDictionaryGetValue((CFDictionaryRef)object, key), indentation+1, xmlString); + } + CFAllocatorDeallocate(kCFAllocatorSystemDefault, keys); + _appendIndents(indentation, xmlString); + _plistAppendUTF8CString(xmlString, "</"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[DICT_IX], DICT_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">\n"); + } else if (typeID == datatype) { + _plistAppendUTF8CString(xmlString, "<"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[DATA_IX], DATA_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">\n"); + _XMLPlistAppendDataUsingBase64(xmlString, (CFDataRef)object, indentation); + _appendIndents(indentation, xmlString); + _plistAppendUTF8CString(xmlString, "</"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[DATA_IX], DATA_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">\n"); + } else if (typeID == datetype) { + // YYYY '-' MM '-' DD 'T' hh ':' mm ':' ss 'Z' + int32_t y = 0, M = 0, d = 0, H = 0, m = 0, s = 0; +#if 1 + CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(CFDateGetAbsoluteTime((CFDateRef)object), NULL); + y = date.year; + M = date.month; + d = date.day; + H = date.hour; + m = date.minute; + s = (int32_t)date.second; +#else + CFCalendarRef calendar = CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault, kCFGregorianCalendar); + CFTimeZoneRef tz = CFTimeZoneCreateWithName(kCFAllocatorSystemDefault, CFSTR("GMT"), true); + CFCalendarSetTimeZone(calendar, tz); + CFCalendarDecomposeAbsoluteTime(calendar, CFDateGetAbsoluteTime((CFDateRef)object), (const uint8_t *)"yMdHms", &y, &M, &d, &H, &m, &s); + CFRelease(calendar); + CFRelease(tz); +#endif + _plistAppendUTF8CString(xmlString, "<"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[DATE_IX], DATE_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">"); + _plistAppendFormat(xmlString, CFSTR("%04d-%02d-%02dT%02d:%02d:%02dZ"), y, M, d, H, m, s); + _plistAppendUTF8CString(xmlString, "</"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[DATE_IX], DATE_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">\n"); + } else if (typeID == numbertype) { + if (CFNumberIsFloatType((CFNumberRef)object)) { + _plistAppendUTF8CString(xmlString, "<"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">"); + CFStringRef s = __CFNumberCopyFormattingDescriptionAsFloat64(object); + _plistAppendString(xmlString, s); + CFRelease(s); + _plistAppendUTF8CString(xmlString, "</"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">\n"); + } else { + _plistAppendUTF8CString(xmlString, "<"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[INTEGER_IX], INTEGER_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">"); + + _plistAppendFormat(xmlString, CFSTR("%@"), object); + + _plistAppendUTF8CString(xmlString, "</"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[INTEGER_IX], INTEGER_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">\n"); + } + } else if (typeID == booltype) { + if (CFBooleanGetValue((CFBooleanRef)object)) { + _plistAppendUTF8CString(xmlString, "<"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[TRUE_IX], TRUE_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, "/>\n"); + } else { + _plistAppendUTF8CString(xmlString, "<"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[FALSE_IX], FALSE_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, "/>\n"); + } + } +} + +static void _CFGenerateXMLPropertyListToData(CFMutableDataRef xml, CFTypeRef propertyList) { + _plistAppendUTF8CString(xml, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE "); + _plistAppendCharacters(xml, CFXMLPlistTags[PLIST_IX], PLIST_TAG_LENGTH); + _plistAppendUTF8CString(xml, " PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<"); + _plistAppendCharacters(xml, CFXMLPlistTags[PLIST_IX], PLIST_TAG_LENGTH); + _plistAppendUTF8CString(xml, " version=\"1.0\">\n"); + + _CFAppendXML0(propertyList, 0, xml); + + _plistAppendUTF8CString(xml, "</"); + _plistAppendCharacters(xml, CFXMLPlistTags[PLIST_IX], PLIST_TAG_LENGTH); + _plistAppendUTF8CString(xml, ">\n"); +} + +CFDataRef CFPropertyListCreateXMLData(CFAllocatorRef allocator, CFPropertyListRef propertyList) { + initStatics(); + CFMutableDataRef xml; + CFAssert1(propertyList != NULL, __kCFLogAssertion, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__); + __CFAssertIsPList(propertyList); + if (!CFPropertyListIsValid(propertyList, kCFPropertyListXMLFormat_v1_0)) return NULL; + xml = CFDataCreateMutable(allocator, 0); + _CFGenerateXMLPropertyListToData(xml, propertyList); + return xml; +} + +CFDataRef _CFPropertyListCreateXMLDataWithExtras(CFAllocatorRef allocator, CFPropertyListRef propertyList) { + initStatics(); + CFMutableDataRef xml; + CFAssert1(propertyList != NULL, __kCFLogAssertion, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__); + xml = CFDataCreateMutable(allocator, 0); + _CFGenerateXMLPropertyListToData(xml, propertyList); + return xml; +} + +// ======================================================================== + +// +// ------------------------- Reading plists ------------------ +// + +static void skipInlineDTD(_CFXMLPlistParseInfo *pInfo); +static CFTypeRef parseXMLElement(_CFXMLPlistParseInfo *pInfo, Boolean *isKey); + +// warning: doesn't have a good idea of Unicode line separators +static UInt32 lineNumber(_CFXMLPlistParseInfo *pInfo) { + const UniChar *p = pInfo->begin; + UInt32 count = 1; + while (p < pInfo->curr) { + if (*p == '\r') { + count ++; + if (*(p + 1) == '\n') + p ++; + } else if (*p == '\n') { + count ++; + } + p ++; + } + return count; +} + +// warning: doesn't have a good idea of Unicode white space +CF_INLINE void skipWhitespace(_CFXMLPlistParseInfo *pInfo) { + while (pInfo->curr < pInfo->end) { + switch (*(pInfo->curr)) { + case ' ': + case '\t': + case '\n': + case '\r': + pInfo->curr ++; + continue; + default: + return; + } + } +} + +/* All of these advance to the end of the given construct and return a pointer to the first character beyond the construct. If the construct doesn't parse properly, NULL is returned. */ + +// pInfo should be just past "<!--" +static void skipXMLComment(_CFXMLPlistParseInfo *pInfo) { + const UniChar *p = pInfo->curr; + const UniChar *end = pInfo->end - 3; // Need at least 3 characters to compare against + while (p < end) { + if (*p == '-' && *(p+1) == '-' && *(p+2) == '>') { + pInfo->curr = p+3; + return; + } + p ++; + } + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Unterminated comment started on line %d"), lineNumber(pInfo)); +} + +// stringToMatch and buf must both be of at least len +static Boolean matchString(const UniChar *buf, const UniChar *stringToMatch, UInt32 len) { + switch (len) { + case 10: if (buf[9] != stringToMatch[9]) return false; + case 9: if (buf[8] != stringToMatch[8]) return false; + case 8: if (buf[7] != stringToMatch[7]) return false; + case 7: if (buf[6] != stringToMatch[6]) return false; + case 6: if (buf[5] != stringToMatch[5]) return false; + case 5: if (buf[4] != stringToMatch[4]) return false; + case 4: if (buf[3] != stringToMatch[3]) return false; + case 3: if (buf[2] != stringToMatch[2]) return false; + case 2: if (buf[1] != stringToMatch[1]) return false; + case 1: if (buf[0] != stringToMatch[0]) return false; + case 0: return true; + } + return false; // internal error +} + +// pInfo should be set to the first character after "<?" +static void skipXMLProcessingInstruction(_CFXMLPlistParseInfo *pInfo) { + const UniChar *begin = pInfo->curr, *end = pInfo->end - 2; // Looking for "?>" so we need at least 2 characters + while (pInfo->curr < end) { + if (*(pInfo->curr) == '?' && *(pInfo->curr+1) == '>') { + pInfo->curr += 2; + return; + } + pInfo->curr ++; + } + pInfo->curr = begin; + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unexpected EOF while parsing the processing instruction begun on line %d"), lineNumber(pInfo)); +} + +// first character should be immediately after the "<!" +static void skipDTD(_CFXMLPlistParseInfo *pInfo) { + // First pass "DOCTYPE" + if (pInfo->end - pInfo->curr < DOCTYPE_TAG_LENGTH || !matchString(pInfo->curr, CFXMLPlistTags[DOCTYPE_IX], DOCTYPE_TAG_LENGTH)) { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Malformed DTD on line %d"), lineNumber(pInfo)); + return; + } + pInfo->curr += DOCTYPE_TAG_LENGTH; + skipWhitespace(pInfo); + + // Look for either the beginning of a complex DTD or the end of the DOCTYPE structure + while (pInfo->curr < pInfo->end) { + UniChar ch = *(pInfo->curr); + if (ch == '[') break; // inline DTD + if (ch == '>') { // End of the DTD + pInfo->curr ++; + return; + } + pInfo->curr ++; + } + if (pInfo->curr == pInfo->end) { + pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF while parsing DTD", CFStringGetSystemEncoding()); + return; + } + + // *Sigh* Must parse in-line DTD + skipInlineDTD(pInfo); + if (pInfo->errorString) return; + skipWhitespace(pInfo); + if (pInfo->errorString) return; + if (pInfo->curr < pInfo->end) { + if (*(pInfo->curr) == '>') { + pInfo->curr ++; + } else { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unexpected character %c on line %d while parsing DTD"), *(pInfo->curr), lineNumber(pInfo)); + } + } else { + pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF while parsing DTD", CFStringGetSystemEncoding()); + } +} + +static void skipPERef(_CFXMLPlistParseInfo *pInfo) { + const UniChar *p = pInfo->curr; + while (p < pInfo->end) { + if (*p == ';') { + pInfo->curr = p+1; + return; + } + p ++; + } + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unexpected EOF while parsing percent-escape sequence begun on line %d"), lineNumber(pInfo)); +} + +// First character should be just past '[' +static void skipInlineDTD(_CFXMLPlistParseInfo *pInfo) { + while (!pInfo->errorString && pInfo->curr < pInfo->end) { + UniChar ch; + skipWhitespace(pInfo); + ch = *pInfo->curr; + if (ch == '%') { + pInfo->curr ++; + skipPERef(pInfo); + } else if (ch == '<') { + pInfo->curr ++; + if (pInfo->curr >= pInfo->end) { + pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF while parsing inline DTD", CFStringGetSystemEncoding()); + return; + } + ch = *(pInfo->curr); + if (ch == '?') { + pInfo->curr ++; + skipXMLProcessingInstruction(pInfo); + } else if (ch == '!') { + if (pInfo->curr + 2 < pInfo->end && (*(pInfo->curr+1) == '-' && *(pInfo->curr+2) == '-')) { + pInfo->curr += 3; + skipXMLComment(pInfo); + } else { + // Skip the myriad of DTD declarations of the form "<!string" ... ">" + pInfo->curr ++; // Past both '<' and '!' + while (pInfo->curr < pInfo->end) { + if (*(pInfo->curr) == '>') break; + pInfo->curr ++; + } + if (*(pInfo->curr) != '>') { + pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF while parsing inline DTD", CFStringGetSystemEncoding()); + return; + + } + pInfo->curr ++; + } + } else { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unexpected character %c on line %d while parsing inline DTD"), ch, lineNumber(pInfo)); + return; + } + } else if (ch == ']') { + pInfo->curr ++; + return; + } else { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unexpected character %c on line %d while parsing inline DTD"), ch, lineNumber(pInfo)); + return; + } + } + if (!pInfo->errorString) + pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF while parsing inline DTD", CFStringGetSystemEncoding()); +} + +/* A bit wasteful to do everything with unichars (since we know all the characters we're going to see are 7-bit ASCII), but since our data is coming from or going to a CFString, this prevents the extra cost of converting formats. */ + +static const signed char __CFPLDataDecodeTable[128] = { + /* 000 */ -1, -1, -1, -1, -1, -1, -1, -1, + /* 010 */ -1, -1, -1, -1, -1, -1, -1, -1, + /* 020 */ -1, -1, -1, -1, -1, -1, -1, -1, + /* 030 */ -1, -1, -1, -1, -1, -1, -1, -1, + /* ' ' */ -1, -1, -1, -1, -1, -1, -1, -1, + /* '(' */ -1, -1, -1, 62, -1, -1, -1, 63, + /* '0' */ 52, 53, 54, 55, 56, 57, 58, 59, + /* '8' */ 60, 61, -1, -1, -1, 0, -1, -1, + /* '@' */ -1, 0, 1, 2, 3, 4, 5, 6, + /* 'H' */ 7, 8, 9, 10, 11, 12, 13, 14, + /* 'P' */ 15, 16, 17, 18, 19, 20, 21, 22, + /* 'X' */ 23, 24, 25, -1, -1, -1, -1, -1, + /* '`' */ -1, 26, 27, 28, 29, 30, 31, 32, + /* 'h' */ 33, 34, 35, 36, 37, 38, 39, 40, + /* 'p' */ 41, 42, 43, 44, 45, 46, 47, 48, + /* 'x' */ 49, 50, 51, -1, -1, -1, -1, -1 +}; + +static CFDataRef __CFPLDataDecode(_CFXMLPlistParseInfo *pInfo, Boolean isMutable) { + int tmpbufpos = 0; + int tmpbuflen = 256; + uint8_t *tmpbuf; + int numeq = 0; + int acc = 0; + int cntr = 0; + + tmpbuf = (uint8_t *)CFAllocatorAllocate(pInfo->allocator, tmpbuflen, 0); + for (; pInfo->curr < pInfo->end; pInfo->curr++) { + UniChar c = *(pInfo->curr); + if (c == '<') { + break; + } + if ('=' == c) { + numeq++; + } else if (!isspace(c)) { + numeq = 0; + } + if (__CFPLDataDecodeTable[c] < 0) + continue; + cntr++; + acc <<= 6; + acc += __CFPLDataDecodeTable[c]; + if (0 == (cntr & 0x3)) { + if (tmpbuflen <= tmpbufpos + 2) { + if (tmpbuflen < 256 * 1024) { + tmpbuflen *= 4; + } else if (tmpbuflen < 16 * 1024 * 1024) { + tmpbuflen *= 2; + } else { + // once in this stage, this will be really slow + // and really potentially fragment memory + tmpbuflen += 256 * 1024; + } + tmpbuf = (uint8_t *)CFAllocatorReallocate(pInfo->allocator, tmpbuf, tmpbuflen, 0); + if (!tmpbuf) HALT; + } + tmpbuf[tmpbufpos++] = (acc >> 16) & 0xff; + if (numeq < 2) + tmpbuf[tmpbufpos++] = (acc >> 8) & 0xff; + if (numeq < 1) + tmpbuf[tmpbufpos++] = acc & 0xff; + } + } + if (isMutable) { + CFMutableDataRef result = CFDataCreateMutable(pInfo->allocator, 0); + CFDataAppendBytes(result, tmpbuf, tmpbufpos); + CFAllocatorDeallocate(pInfo->allocator, tmpbuf); + return result; + } else { + return CFDataCreateWithBytesNoCopy(pInfo->allocator, tmpbuf, tmpbufpos, pInfo->allocator); + } +} + +// content ::== (element | CharData | Reference | CDSect | PI | Comment)* +// In the context of a plist, CharData, Reference and CDSect are not legal (they all resolve to strings). Skipping whitespace, then, the next character should be '<'. From there, we figure out which of the three remaining cases we have (element, PI, or Comment). +static CFTypeRef getContentObject(_CFXMLPlistParseInfo *pInfo, Boolean *isKey) { + if (isKey) *isKey = false; + while (!pInfo->errorString && pInfo->curr < pInfo->end) { + skipWhitespace(pInfo); + if (pInfo->curr >= pInfo->end) { + pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF", CFStringGetSystemEncoding()); + return NULL; + } + if (*(pInfo->curr) != '<') { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unexpected character %c on line %d"), *(pInfo->curr), lineNumber(pInfo)); + return NULL; + } + pInfo->curr ++; + if (pInfo->curr >= pInfo->end) { + pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF", CFStringGetSystemEncoding()); + return NULL; + } + switch (*(pInfo->curr)) { + case '?': + // Processing instruction + skipXMLProcessingInstruction(pInfo); + break; + case '!': + // Could be a comment + if (pInfo->curr+2 >= pInfo->end) { + pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF", CFStringGetSystemEncoding()); + return NULL; + } + if (*(pInfo->curr+1) == '-' && *(pInfo->curr+2) == '-') { + pInfo->curr += 2; + skipXMLComment(pInfo); + } else { + pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF", CFStringGetSystemEncoding()); + return NULL; + } + break; + case '/': + // Whoops! Looks like we got to the end tag for the element whose content we're parsing + pInfo->curr --; // Back off to the '<' + return NULL; + default: + // Should be an element + return parseXMLElement(pInfo, isKey); + } + } + // Do not set the error string here; if it wasn't already set by one of the recursive parsing calls, the caller will quickly detect the failure (b/c pInfo->curr >= pInfo->end) and provide a more useful one of the form "end tag for <blah> not found" + return NULL; +} + +static void _catFromMarkToBuf(const UniChar *mark, const UniChar *buf, CFMutableStringRef *string, CFAllocatorRef allocator ) { + if (!(*string)) { + *string = CFStringCreateMutable(allocator, 0); + } + CFStringAppendCharacters(*string, mark, buf-mark); +} + +static void parseCDSect_pl(_CFXMLPlistParseInfo *pInfo, CFMutableStringRef string) { + const UniChar *end, *begin; + if (pInfo->end - pInfo->curr < CDSECT_TAG_LENGTH) { + pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF", CFStringGetSystemEncoding()); + return; + } + if (!matchString(pInfo->curr, CFXMLPlistTags[CDSECT_IX], CDSECT_TAG_LENGTH)) { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered improper CDATA opening at line %d"), lineNumber(pInfo)); + return; + } + pInfo->curr += CDSECT_TAG_LENGTH; + begin = pInfo->curr; // Marks the first character of the CDATA content + end = pInfo->end-2; // So we can safely look 2 characters beyond p + while (pInfo->curr < end) { + if (*(pInfo->curr) == ']' && *(pInfo->curr+1) == ']' && *(pInfo->curr+2) == '>') { + // Found the end! + CFStringAppendCharacters(string, begin, pInfo->curr-begin); + pInfo->curr += 3; + return; + } + pInfo->curr ++; + } + // Never found the end mark + pInfo->curr = begin; + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Could not find end of CDATA started on line %d"), lineNumber(pInfo)); +} + +// Only legal references are {lt, gt, amp, apos, quote, #ddd, #xAAA} +static void parseEntityReference_pl(_CFXMLPlistParseInfo *pInfo, CFMutableStringRef string) { + int len; + UniChar ch; + pInfo->curr ++; // move past the '&'; + len = pInfo->end - pInfo->curr; // how many characters we can safely scan + if (len < 1) { + pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF", CFStringGetSystemEncoding()); + return; + } + switch (*(pInfo->curr)) { + case 'l': // "lt" + if (len >= 3 && *(pInfo->curr+1) == 't' && *(pInfo->curr+2) == ';') { + ch = '<'; + pInfo->curr += 3; + break; + } + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo)); + return; + case 'g': // "gt" + if (len >= 3 && *(pInfo->curr+1) == 't' && *(pInfo->curr+2) == ';') { + ch = '>'; + pInfo->curr += 3; + break; + } + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo)); + return; + case 'a': // "apos" or "amp" + if (len < 4) { // Not enough characters for either conversion + pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF", CFStringGetSystemEncoding()); + return; + } + if (*(pInfo->curr+1) == 'm') { + // "amp" + if (*(pInfo->curr+2) == 'p' && *(pInfo->curr+3) == ';') { + ch = '&'; + pInfo->curr += 4; + break; + } + } else if (*(pInfo->curr+1) == 'p') { + // "apos" + if (len > 4 && *(pInfo->curr+2) == 'o' && *(pInfo->curr+3) == 's' && *(pInfo->curr+4) == ';') { + ch = '\''; + pInfo->curr += 5; + break; + } + } + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo)); + return; + case 'q': // "quote" + if (len >= 5 && *(pInfo->curr+1) == 'u' && *(pInfo->curr+2) == 'o' && *(pInfo->curr+3) == 't' && *(pInfo->curr+4) == ';') { + ch = '\"'; + pInfo->curr += 5; + break; + } + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo)); + return; + case '#': + { + uint16_t num = 0; + Boolean isHex = false; + if ( len < 4) { // Not enough characters to make it all fit! Need at least "&#d;" + pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF", CFStringGetSystemEncoding()); + return; + } + pInfo->curr ++; + if (*(pInfo->curr) == 'x') { + isHex = true; + pInfo->curr ++; + } + while (pInfo->curr < pInfo->end) { + ch = *(pInfo->curr); + pInfo->curr ++; + if (ch == ';') { + CFStringAppendCharacters(string, &num, 1); + return; + } + if (!isHex) num = num*10; + else num = num << 4; + if (ch <= '9' && ch >= '0') { + num += (ch - '0'); + } else if (!isHex) { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unexpected character %c at line %d"), ch, lineNumber(pInfo)); + return; + } else if (ch >= 'a' && ch <= 'f') { + num += 10 + (ch - 'a'); + } else if (ch >= 'A' && ch <= 'F') { + num += 10 + (ch - 'A'); + } else { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unexpected character %c at line %d"), ch, lineNumber(pInfo)); + return; + } + } + pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF", CFStringGetSystemEncoding()); + return; + } + default: + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo)); + return; + } + CFStringAppendCharacters(string, &ch, 1); +} + +static CFStringRef _uniqueStringForString(_CFXMLPlistParseInfo *pInfo, CFStringRef stringToUnique) { + if (!pInfo->stringSet) { + pInfo->stringSet = CFSetCreateMutable(pInfo->allocator, 0, &kCFCopyStringSetCallBacks); + _CFSetSetCapacity(pInfo->stringSet, 160); // set capacity high to avoid lots of rehashes, though waste some memory + } + CFSetAddValue(pInfo->stringSet, stringToUnique); + return (CFStringRef)CFSetGetValue(pInfo->stringSet, stringToUnique); +} + +extern void _CFStrSetDesiredCapacity(CFMutableStringRef str, CFIndex len); + +static CFStringRef _uniqueStringForCharacters(_CFXMLPlistParseInfo *pInfo, const UniChar *base, CFIndex length) { + CFIndex idx; + uint8_t *ascii, buffer[1024]; + bool isASCII; + if (!pInfo->stringSet) { + pInfo->stringSet = CFSetCreateMutable(pInfo->allocator, 0, &kCFCopyStringSetCallBacks); + _CFSetSetCapacity(pInfo->stringSet, 160); // set capacity high to avoid lots of rehashes, though waste some memory + } + if (pInfo->tmpString) { + CFStringDelete(pInfo->tmpString, CFRangeMake(0, CFStringGetLength(pInfo->tmpString))); + } else { + pInfo->tmpString = CFStringCreateMutable(pInfo->allocator, 0); + _CFStrSetDesiredCapacity(pInfo->tmpString, 512); + } + // This is to avoid having to promote the buffers of all the strings compared against + // during the set probe; if a Unicode string is passed in, that's what happens. + isASCII = true; + for (idx = 0; isASCII && idx < length; idx++) isASCII = isASCII && (base[idx] < 0x80); + if (isASCII) { + ascii = (length < (CFIndex)sizeof(buffer)) ? buffer : (uint8_t *)CFAllocatorAllocate(kCFAllocatorSystemDefault, length + 1, 0); + for (idx = 0; idx < length; idx++) ascii[idx] = (uint8_t)base[idx]; + ascii[length] = '\0'; + CFStringAppendCString(pInfo->tmpString, (char *)ascii, kCFStringEncodingASCII); + if (ascii != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, ascii); + } else { + CFStringAppendCharacters(pInfo->tmpString, base, length); + } + CFSetAddValue(pInfo->stringSet, pInfo->tmpString); + return (CFStringRef)CFSetGetValue(pInfo->stringSet, pInfo->tmpString); +} + + +// String could be comprised of characters, CDSects, or references to one of the "well-known" entities ('<', '>', '&', ''', '"') +// returns a retained object in *string. +static CFStringRef getString(_CFXMLPlistParseInfo *pInfo) { + const UniChar *mark = pInfo->curr; // At any time in the while loop below, the characters between mark and p have not yet been added to *string + CFMutableStringRef string = NULL; + while (!pInfo->errorString && pInfo->curr < pInfo->end) { + UniChar ch = *(pInfo->curr); + if (ch == '<') { + if (pInfo->curr + 1 >= pInfo->end) break; + // Could be a CDSect; could be the end of the string + if (*(pInfo->curr+1) != '!') break; // End of the string + _catFromMarkToBuf(mark, pInfo->curr, &string, pInfo->allocator); + parseCDSect_pl(pInfo, string); + mark = pInfo->curr; + } else if (ch == '&') { + _catFromMarkToBuf(mark, pInfo->curr, &string, pInfo->allocator); + parseEntityReference_pl(pInfo, string); + mark = pInfo->curr; + } else { + pInfo->curr ++; + } + } + + if (pInfo->errorString) { + if (string) CFRelease(string); + return NULL; + } + if (!string) { + if (pInfo->mutabilityOption != kCFPropertyListMutableContainersAndLeaves) { + CFStringRef uniqueString = _uniqueStringForCharacters(pInfo, mark, pInfo->curr-mark); + if (uniqueString) CFRetain(uniqueString); + return uniqueString; + } else { + string = CFStringCreateMutable(pInfo->allocator, 0); + CFStringAppendCharacters(string, mark, pInfo->curr - mark); + return string; + } + } + _catFromMarkToBuf(mark, pInfo->curr, &string, pInfo->allocator); + if (pInfo->mutabilityOption != kCFPropertyListMutableContainersAndLeaves) { + CFStringRef uniqueString = _uniqueStringForString(pInfo, string); + if (uniqueString) CFRetain(uniqueString); + CFRelease(string); + return uniqueString; + } + return string; +} + +static Boolean checkForCloseTag(_CFXMLPlistParseInfo *pInfo, const UniChar *tag, CFIndex tagLen) { + if (pInfo->end - pInfo->curr < tagLen + 3) { + pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF", CFStringGetSystemEncoding()); + return false; + } + if (*(pInfo->curr) != '<' || *(++pInfo->curr) != '/') { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unexpected character %c on line %d"), *(pInfo->curr), lineNumber(pInfo)); + return false; + } + pInfo->curr ++; + if (!matchString(pInfo->curr, tag, tagLen)) { + CFStringRef str = CFStringCreateWithCharactersNoCopy(pInfo->allocator, tag, tagLen, kCFAllocatorNull); + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Close tag on line %d does not match open tag %@"), lineNumber(pInfo), str); + CFRelease(str); + return false; + } + pInfo->curr += tagLen; + skipWhitespace(pInfo); + if (pInfo->curr == pInfo->end) { + pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF", CFStringGetSystemEncoding()); + return false; + } + if (*(pInfo->curr) != '>') { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unexpected character %c on line %d"), *(pInfo->curr), lineNumber(pInfo)); + return false; + } + pInfo->curr ++; + return true; +} + +// pInfo should be set to the first content character of the <plist> +static CFTypeRef parsePListTag(_CFXMLPlistParseInfo *pInfo) { + CFTypeRef result, tmp = NULL; + const UniChar *save; + result = getContentObject(pInfo, NULL); + if (!result) { + if (!pInfo->errorString) pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered empty plist tag", CFStringGetSystemEncoding()); + return NULL; + } + save = pInfo->curr; // Save this in case the next step fails + tmp = getContentObject(pInfo, NULL); + if (tmp) { + // Got an extra object + CFRelease(tmp); + CFRelease(result); + pInfo->curr = save; + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unexpected element at line %d (plist can only include one object)"), lineNumber(pInfo)); + return NULL; + } + if (pInfo->errorString) { + // Parse failed catastrophically + CFRelease(result); + return NULL; + } + if (checkForCloseTag(pInfo, CFXMLPlistTags[PLIST_IX], PLIST_TAG_LENGTH)) { + return result; + } + CFRelease(result); + return NULL; +} + +static int allowImmutableCollections = -1; + +static void checkImmutableCollections(void) { + allowImmutableCollections = (NULL == getenv("CFPropertyListAllowImmutableCollections")) ? 0 : 1; +} + +static CFTypeRef parseArrayTag(_CFXMLPlistParseInfo *pInfo) { + CFMutableArrayRef array = CFArrayCreateMutable(pInfo->allocator, 0, &kCFTypeArrayCallBacks); + CFTypeRef tmp = getContentObject(pInfo, NULL); + while (tmp) { + CFArrayAppendValue(array, tmp); + CFRelease(tmp); + tmp = getContentObject(pInfo, NULL); + } + if (pInfo->errorString) { // getContentObject encountered a parse error + CFRelease(array); + return NULL; + } + if (checkForCloseTag(pInfo, CFXMLPlistTags[ARRAY_IX], ARRAY_TAG_LENGTH)) { + if (-1 == allowImmutableCollections) checkImmutableCollections(); + if (1 == allowImmutableCollections) { + if (pInfo->mutabilityOption == kCFPropertyListImmutable) { + CFArrayRef newArray = CFArrayCreateCopy(pInfo->allocator, array); + CFRelease(array); + array = (CFMutableArrayRef)newArray; + } + } + return array; + } + CFRelease(array); + return NULL; +} + +static CFTypeRef parseDictTag(_CFXMLPlistParseInfo *pInfo) { + CFMutableDictionaryRef dict = NULL; + CFTypeRef key=NULL, value=NULL; + Boolean gotKey; + const UniChar *base = pInfo->curr; + key = getContentObject(pInfo, &gotKey); + while (key) { + if (!gotKey) { + if (key) CFRelease(key); + if (dict) CFRelease(dict); + pInfo->curr = base; + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Found non-key inside <dict> at line %d"), lineNumber(pInfo)); + return NULL; + } + value = getContentObject(pInfo, NULL); + if (!value) { + if (key) CFRelease(key); + if (dict) CFRelease(dict); + if (!pInfo->errorString) + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Value missing for key inside <dict> at line %d"), lineNumber(pInfo)); + return NULL; + } + if (NULL == dict) { + dict = CFDictionaryCreateMutable(pInfo->allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + _CFDictionarySetCapacity(dict, 10); + } + CFDictionarySetValue(dict, key, value); + CFRelease(key); + key = NULL; + CFRelease(value); + value = NULL; + base = pInfo->curr; + key = getContentObject(pInfo, &gotKey); + } + if (checkForCloseTag(pInfo, CFXMLPlistTags[DICT_IX], DICT_TAG_LENGTH)) { + if (NULL == dict) { + if (pInfo->mutabilityOption == kCFPropertyListImmutable) { + dict = (CFMutableDictionaryRef)CFDictionaryCreate(pInfo->allocator, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + } else { + dict = CFDictionaryCreateMutable(pInfo->allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + } + } else { + CFIndex cnt = CFDictionaryGetCount(dict); +#if DEPLOYMENT_TARGET_MACOSX + if (1 == cnt) { + CFTypeRef val = CFDictionaryGetValue(dict, CFSTR("CF$UID")); + if (val && CFGetTypeID(val) == numbertype) { + CFTypeRef uid; + uint32_t v; + CFNumberGetValue((CFNumberRef)val, kCFNumberSInt32Type, &v); + uid = (CFTypeRef)_CFKeyedArchiverUIDCreate(pInfo->allocator, v); + CFRelease(dict); + return uid; + } + } +#endif + if (-1 == allowImmutableCollections) checkImmutableCollections(); + if (1 == allowImmutableCollections) { + if (pInfo->mutabilityOption == kCFPropertyListImmutable) { + CFDictionaryRef newDict = CFDictionaryCreateCopy(pInfo->allocator, dict); + CFRelease(dict); + dict = (CFMutableDictionaryRef)newDict; + } + } + } + return dict; + } + if (dict) CFRelease(dict); + return NULL; +} + +static CFTypeRef parseDataTag(_CFXMLPlistParseInfo *pInfo) { + CFDataRef result; + const UniChar *base = pInfo->curr; + result = __CFPLDataDecode(pInfo, pInfo->mutabilityOption == kCFPropertyListMutableContainersAndLeaves); + if (!result) { + pInfo->curr = base; + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Could not interpret <data> at line %d (should be base64-encoded)"), lineNumber(pInfo)); + return NULL; + } + if (checkForCloseTag(pInfo, CFXMLPlistTags[DATA_IX], DATA_TAG_LENGTH)) return result; + CFRelease(result); + return NULL; +} + +CF_INLINE Boolean read2DigitNumber(_CFXMLPlistParseInfo *pInfo, int32_t *result) { + UniChar ch1, ch2; + if (pInfo->curr + 2 >= pInfo->end) return false; + ch1 = *pInfo->curr; + ch2 = *(pInfo->curr + 1); + pInfo->curr += 2; + if (!isdigit(ch1) || !isdigit(ch2)) return false; + *result = (ch1 - '0')*10 + (ch2 - '0'); + return true; +} + +// YYYY '-' MM '-' DD 'T' hh ':' mm ':' ss 'Z' +static CFTypeRef parseDateTag(_CFXMLPlistParseInfo *pInfo) { + int32_t year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0; + int32_t num = 0; + Boolean badForm = false; + + while (pInfo->curr < pInfo->end && isdigit(*pInfo->curr)) { + year = 10*year + (*pInfo->curr) - '0'; + pInfo->curr ++; + } + if (pInfo->curr >= pInfo->end || *pInfo->curr != '-') { + badForm = true; + } else { + pInfo->curr ++; + } + + if (!badForm && read2DigitNumber(pInfo, &month) && pInfo->curr < pInfo->end && *pInfo->curr == '-') { + pInfo->curr ++; + } else { + badForm = true; + } + + if (!badForm && read2DigitNumber(pInfo, &day) && pInfo->curr < pInfo->end && *pInfo->curr == 'T') { + pInfo->curr ++; + } else { + badForm = true; + } + + if (!badForm && read2DigitNumber(pInfo, &hour) && pInfo->curr < pInfo->end && *pInfo->curr == ':') { + pInfo->curr ++; + } else { + badForm = true; + } + + if (!badForm && read2DigitNumber(pInfo, &minute) && pInfo->curr < pInfo->end && *pInfo->curr == ':') { + pInfo->curr ++; + } else { + badForm = true; + } + + if (!badForm && read2DigitNumber(pInfo, &num) && pInfo->curr < pInfo->end && *pInfo->curr == 'Z') { + second = num; + pInfo->curr ++; + } else { + badForm = true; + } + + if (badForm) { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Could not interpret <date> at line %d"), lineNumber(pInfo)); + return NULL; + } + if (!checkForCloseTag(pInfo, CFXMLPlistTags[DATE_IX], DATE_TAG_LENGTH)) return NULL; + + CFAbsoluteTime at = 0.0; +#if 1 + CFGregorianDate date = {year, month, day, hour, minute, second}; + at = CFGregorianDateGetAbsoluteTime(date, NULL); +#else + CFCalendarRef calendar = CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault, kCFGregorianCalendar); + CFTimeZoneRef tz = CFTimeZoneCreateWithName(kCFAllocatorSystemDefault, CFSTR("GMT"), true); + CFCalendarSetTimeZone(calendar, tz); + CFCalendarComposeAbsoluteTime(calendar, &at, (const uint8_t *)"yMdHms", year, month, day, hour, minute, second); + CFRelease(calendar); + CFRelease(tz); +#endif + return CFDateCreate(pInfo->allocator, at); +} + +static CFTypeRef parseRealTag(_CFXMLPlistParseInfo *pInfo) { + CFStringRef str = getString(pInfo); + SInt32 idx, len; + double val; + CFNumberRef result; + CFStringInlineBuffer buf; + if (!str) { + if (!pInfo->errorString) + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered empty <real> on line %d"), lineNumber(pInfo)); + return NULL; + } + + if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("nan"), kCFCompareCaseInsensitive)) { + CFRelease(str); + return (checkForCloseTag(pInfo, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH)) ? CFRetain(kCFNumberNaN) : NULL; + } + if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("+infinity"), kCFCompareCaseInsensitive)) { + CFRelease(str); + return (checkForCloseTag(pInfo, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH)) ? CFRetain(kCFNumberPositiveInfinity) : NULL; + } + if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("-infinity"), kCFCompareCaseInsensitive)) { + CFRelease(str); + return (checkForCloseTag(pInfo, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH)) ? CFRetain(kCFNumberNegativeInfinity) : NULL; + } + if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("infinity"), kCFCompareCaseInsensitive)) { + CFRelease(str); + return (checkForCloseTag(pInfo, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH)) ? CFRetain(kCFNumberPositiveInfinity) : NULL; + } + if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("-inf"), kCFCompareCaseInsensitive)) { + CFRelease(str); + return (checkForCloseTag(pInfo, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH)) ? CFRetain(kCFNumberNegativeInfinity) : NULL; + } + if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("inf"), kCFCompareCaseInsensitive)) { + CFRelease(str); + return (checkForCloseTag(pInfo, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH)) ? CFRetain(kCFNumberPositiveInfinity) : NULL; + } + if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("+inf"), kCFCompareCaseInsensitive)) { + CFRelease(str); + return (checkForCloseTag(pInfo, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH)) ? CFRetain(kCFNumberPositiveInfinity) : NULL; + } + + len = CFStringGetLength(str); + CFStringInitInlineBuffer(str, &buf, CFRangeMake(0, len)); + idx = 0; + if (!__CFStringScanDouble(&buf, NULL, &idx, &val) || idx != len) { + CFRelease(str); + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered misformatted real on line %d"), lineNumber(pInfo)); + return NULL; + } + CFRelease(str); + result = CFNumberCreate(pInfo->allocator, kCFNumberDoubleType, &val); + if (checkForCloseTag(pInfo, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH)) return result; + CFRelease(result); + return NULL; +} + +#define GET_CH if (pInfo->curr == pInfo->end) { \ + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Premature end of file after <integer> on line %d"), lineNumber(pInfo)); \ + return NULL; \ + } \ + ch = *(pInfo->curr) + +typedef struct { + int64_t high; + uint64_t low; +} CFSInt128Struct; + +enum { + kCFNumberSInt128Type = 17 +}; + +static CFTypeRef parseIntegerTag(_CFXMLPlistParseInfo *pInfo) { + bool isHex = false, isNeg = false, hadLeadingZero = false; + UniChar ch = 0; + + // decimal_constant S*(-|+)?S*[0-9]+ (S == space) + // hex_constant S*(-|+)?S*0[xX][0-9a-fA-F]+ (S == space) + + while (pInfo->curr < pInfo->end && __CFIsWhitespace(*(pInfo->curr))) pInfo->curr++; + GET_CH; + if ('<' == ch) { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered empty <integer> on line %d"), lineNumber(pInfo)); + return NULL; + } + if ('-' == ch || '+' == ch) { + isNeg = ('-' == ch); + pInfo->curr++; + while (pInfo->curr < pInfo->end && __CFIsWhitespace(*(pInfo->curr))) pInfo->curr++; + } + GET_CH; + if ('0' == ch) { + if (pInfo->curr + 1 < pInfo->end && ('x' == *(pInfo->curr + 1) || 'X' == *(pInfo->curr + 1))) { + pInfo->curr++; + isHex = true; + } else { + hadLeadingZero = true; + } + pInfo->curr++; + } + GET_CH; + while ('0' == ch) { + hadLeadingZero = true; + pInfo->curr++; + GET_CH; + } + if ('<' == ch && hadLeadingZero) { // nothing but zeros + int32_t val = 0; + if (!checkForCloseTag(pInfo, CFXMLPlistTags[INTEGER_IX], INTEGER_TAG_LENGTH)) { + // checkForCloseTag() sets error string + return NULL; + } + return CFNumberCreate(pInfo->allocator, kCFNumberSInt32Type, &val); + } + if ('<' == ch) { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Incomplete <integer> on line %d"), lineNumber(pInfo)); + return NULL; + } + uint64_t value = 0; + uint32_t multiplier = (isHex ? 16 : 10); + while ('<' != ch) { + uint32_t new_digit = 0; + switch (ch) { + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + new_digit = (ch - '0'); + break; + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + new_digit = (ch - 'a' + 10); + break; + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + new_digit = (ch - 'A' + 10); + break; + default: // other character + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Unknown character '%c' (0x%x) in <integer> on line %d"), ch, ch, lineNumber(pInfo)); + return NULL; + } + if (!isHex && new_digit > 9) { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Hex digit in non-hex <integer> on line %d"), lineNumber(pInfo)); + return NULL; + } + if (UINT64_MAX / multiplier < value) { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Integer overflow in <integer> on line %d"), lineNumber(pInfo)); + return NULL; + } + value = multiplier * value; + if (UINT64_MAX - new_digit < value) { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Integer overflow in <integer> on line %d"), lineNumber(pInfo)); + return NULL; + } + value = value + new_digit; + if (isNeg && (uint64_t)INT64_MAX + 1 < value) { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Integer underflow in <integer> on line %d"), lineNumber(pInfo)); + return NULL; + } + pInfo->curr++; + GET_CH; + } + if (!checkForCloseTag(pInfo, CFXMLPlistTags[INTEGER_IX], INTEGER_TAG_LENGTH)) { + // checkForCloseTag() sets error string + return NULL; + } + if (isNeg || value <= INT64_MAX) { + int64_t v = value; + if (isNeg) v = -v; // no-op if INT64_MIN + return CFNumberCreate(pInfo->allocator, kCFNumberSInt64Type, &v); + } + CFSInt128Struct val; + val.high = 0; + val.low = value; + return CFNumberCreate(pInfo->allocator, kCFNumberSInt128Type, &val); +} + +#undef GET_CH + +// Returned object is retained; caller must free. pInfo->curr expected to point to the first character after the '<' +static CFTypeRef parseXMLElement(_CFXMLPlistParseInfo *pInfo, Boolean *isKey) { + const UniChar *marker = pInfo->curr; + int markerLength = -1; + Boolean isEmpty; + int markerIx = -1; + + if (isKey) *isKey = false; + while (pInfo->curr < pInfo->end) { + UniChar ch = *(pInfo->curr); + if (ch == ' ' || ch == '\t' || ch == '\n' || ch =='\r') { + if (markerLength == -1) markerLength = pInfo->curr - marker; + } else if (ch == '>') { + break; + } + pInfo->curr ++; + } + if (pInfo->curr >= pInfo->end) return NULL; + isEmpty = (*(pInfo->curr-1) == '/'); + if (markerLength == -1) + markerLength = pInfo->curr - (isEmpty ? 1 : 0) - marker; + pInfo->curr ++; // Advance past '>' + if (markerLength == 0) { + // Back up to the beginning of the marker + pInfo->curr = marker; + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Malformed tag on line %d"), lineNumber(pInfo)); + return NULL; + } + switch (*marker) { + case 'a': // Array + if (markerLength == ARRAY_TAG_LENGTH && matchString(marker, CFXMLPlistTags[ARRAY_IX], ARRAY_TAG_LENGTH)) + markerIx = ARRAY_IX; + break; + case 'd': // Dictionary, data, or date; Fortunately, they all have the same marker length.... + if (markerLength != DICT_TAG_LENGTH) + break; + if (matchString(marker, CFXMLPlistTags[DICT_IX], DICT_TAG_LENGTH)) + markerIx = DICT_IX; + else if (matchString(marker, CFXMLPlistTags[DATA_IX], DATA_TAG_LENGTH)) + markerIx = DATA_IX; + else if (matchString(marker, CFXMLPlistTags[DATE_IX], DATE_TAG_LENGTH)) + markerIx = DATE_IX; + break; + case 'f': // false (boolean) + if (markerLength == FALSE_TAG_LENGTH && matchString(marker, CFXMLPlistTags[FALSE_IX], FALSE_TAG_LENGTH)) { + markerIx = FALSE_IX; + } + break; + case 'i': // integer + if (markerLength == INTEGER_TAG_LENGTH && matchString(marker, CFXMLPlistTags[INTEGER_IX], INTEGER_TAG_LENGTH)) + markerIx = INTEGER_IX; + break; + case 'k': // Key of a dictionary + if (markerLength == KEY_TAG_LENGTH && matchString(marker, CFXMLPlistTags[KEY_IX], KEY_TAG_LENGTH)) { + markerIx = KEY_IX; + if (isKey) *isKey = true; + } + break; + case 'p': // Plist + if (markerLength == PLIST_TAG_LENGTH && matchString(marker, CFXMLPlistTags[PLIST_IX], PLIST_TAG_LENGTH)) + markerIx = PLIST_IX; + break; + case 'r': // real + if (markerLength == REAL_TAG_LENGTH && matchString(marker, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH)) + markerIx = REAL_IX; + break; + case 's': // String + if (markerLength == STRING_TAG_LENGTH && matchString(marker, CFXMLPlistTags[STRING_IX], STRING_TAG_LENGTH)) + markerIx = STRING_IX; + break; + case 't': // true (boolean) + if (markerLength == TRUE_TAG_LENGTH && matchString(marker, CFXMLPlistTags[TRUE_IX], TRUE_TAG_LENGTH)) + markerIx = TRUE_IX; + break; + } + + if (!pInfo->allowNewTypes && markerIx != PLIST_IX && markerIx != ARRAY_IX && markerIx != DICT_IX && markerIx != STRING_IX && markerIx != KEY_IX && markerIx != DATA_IX) { + pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered new tag when expecting only old-style property list objects", CFStringGetSystemEncoding()); + return NULL; + } + + switch (markerIx) { + case PLIST_IX: + if (isEmpty) { + pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered empty plist tag", CFStringGetSystemEncoding()); + return NULL; + } + return parsePListTag(pInfo); + case ARRAY_IX: + if (isEmpty) { + return pInfo->mutabilityOption == kCFPropertyListImmutable ? CFArrayCreate(pInfo->allocator, NULL, 0, &kCFTypeArrayCallBacks) : CFArrayCreateMutable(pInfo->allocator, 0, &kCFTypeArrayCallBacks); + } else { + return parseArrayTag(pInfo); + } + case DICT_IX: + if (isEmpty) { + if (pInfo->mutabilityOption == kCFPropertyListImmutable) { + return CFDictionaryCreate(pInfo->allocator, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + } else { + return CFDictionaryCreateMutable(pInfo->allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + } + } else { + return parseDictTag(pInfo); + } + case KEY_IX: + case STRING_IX: + { + CFStringRef str; + int tagLen = (markerIx == KEY_IX) ? KEY_TAG_LENGTH : STRING_TAG_LENGTH; + if (isEmpty) { + return pInfo->mutabilityOption == kCFPropertyListMutableContainersAndLeaves ? CFStringCreateMutable(pInfo->allocator, 0) : CFStringCreateWithCharacters(pInfo->allocator, NULL, 0); + } + str = getString(pInfo); + if (!str) return NULL; // getString will already have set the error string + if (!checkForCloseTag(pInfo, CFXMLPlistTags[markerIx], tagLen)) { + CFRelease(str); + return NULL; + } + return str; + } + case DATA_IX: + if (isEmpty) { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered empty <data> on line %d"), lineNumber(pInfo)); + return NULL; + } else { + return parseDataTag(pInfo); + } + case DATE_IX: + if (isEmpty) { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered empty <date> on line %d"), lineNumber(pInfo)); + return NULL; + } else { + return parseDateTag(pInfo); + } + case TRUE_IX: + if (!isEmpty) { + if (!checkForCloseTag(pInfo, CFXMLPlistTags[TRUE_IX], TRUE_TAG_LENGTH)) return NULL; + } + return CFRetain(kCFBooleanTrue); + case FALSE_IX: + if (!isEmpty) { + if (!checkForCloseTag(pInfo, CFXMLPlistTags[FALSE_IX], FALSE_TAG_LENGTH)) return NULL; + } + return CFRetain(kCFBooleanFalse); + case REAL_IX: + if (isEmpty) { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered empty <real> on line %d"), lineNumber(pInfo)); + return NULL; + } else { + return parseRealTag(pInfo); + } + case INTEGER_IX: + if (isEmpty) { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered empty <integer> on line %d"), lineNumber(pInfo)); + return NULL; + } else { + return parseIntegerTag(pInfo); + } + default: { + CFStringRef markerStr = CFStringCreateWithCharacters(pInfo->allocator, marker, markerLength); + pInfo->curr = marker; + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unknown tag %@ on line %d"), markerStr, lineNumber(pInfo)); + CFRelease(markerStr); + return NULL; + } + } +} + +static CFTypeRef parseXMLPropertyList(_CFXMLPlistParseInfo *pInfo) { + while (!pInfo->errorString && pInfo->curr < pInfo->end) { + UniChar ch; + skipWhitespace(pInfo); + if (pInfo->curr+1 >= pInfo->end) { + pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "No XML content found", CFStringGetSystemEncoding()); + return NULL; + } + if (*(pInfo->curr) != '<') { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Unexpected character %c at line %d"), *(pInfo->curr), lineNumber(pInfo)); + return NULL; + } + ch = *(++ pInfo->curr); + if (ch == '!') { + // Comment or DTD + ++ pInfo->curr; + if (pInfo->curr+1 < pInfo->end && *pInfo->curr == '-' && *(pInfo->curr+1) == '-') { + // Comment + pInfo->curr += 2; + skipXMLComment(pInfo); + } else { + skipDTD(pInfo); + } + } else if (ch == '?') { + // Processing instruction + pInfo->curr++; + skipXMLProcessingInstruction(pInfo); + } else { + // Tag or malformed + return parseXMLElement(pInfo, NULL); + // Note we do not verify that there was only one element, so a file that has garbage after the first element will nonetheless successfully parse + } + } + // Should never get here + if (!(pInfo->errorString)) + pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF", CFStringGetSystemEncoding()); + return NULL; +} + +static CFStringEncoding encodingForXMLData(CFDataRef data, CFStringRef *error) { + const uint8_t *bytes = (uint8_t *)CFDataGetBytePtr(data); + UInt32 length = CFDataGetLength(data); + const uint8_t *idx, *end; + char quote; + + // Check for the byte order mark first + if (length > 2 && + ((*bytes == 0xFF && *(bytes+1) == 0xFE) || + (*bytes == 0xFE && *(bytes+1) == 0xFF) || + *bytes == 0x00 || *(bytes+1) == 0x00)) // This clause checks for a Unicode sequence lacking the byte order mark; technically an error, but this check is recommended by the XML spec + return kCFStringEncodingUnicode; + + // Scan for the <?xml.... ?> opening + if (length < 5 || strncmp((char const *) bytes, "<?xml", 5) != 0) return kCFStringEncodingUTF8; + idx = bytes + 5; + end = bytes + length; + // Found "<?xml"; now we scan for "encoding" + while (idx < end) { + uint8_t ch = *idx; + const uint8_t *scan; + if ( ch == '?' || ch == '>') return kCFStringEncodingUTF8; + idx ++; + scan = idx; + if (ch == 'e' && *scan++ == 'n' && *scan++ == 'c' && *scan++ == 'o' && *scan++ == 'd' && *scan++ == 'i' + && *scan++ == 'n' && *scan++ == 'g' && *scan++ == '=') { + idx = scan; + break; + } + } + if (idx >= end) return kCFStringEncodingUTF8; + quote = *idx; + if (quote != '\'' && quote != '\"') return kCFStringEncodingUTF8; + else { + CFStringRef encodingName; + const uint8_t *base = idx+1; // Move past the quote character + CFStringEncoding enc; + UInt32 len; + idx ++; + while (idx < end && *idx != quote) idx ++; + if (idx >= end) return kCFStringEncodingUTF8; + len = idx - base; + if (len == 5 && (*base == 'u' || *base == 'U') && (base[1] == 't' || base[1] == 'T') && (base[2] == 'f' || base[2] == 'F') && (base[3] == '-') && (base[4] == '8')) + return kCFStringEncodingUTF8; + encodingName = CFStringCreateWithBytes(kCFAllocatorSystemDefault, base, len, kCFStringEncodingISOLatin1, false); + enc = CFStringConvertIANACharSetNameToEncoding(encodingName); + if (enc != kCFStringEncodingInvalidId) { + CFRelease(encodingName); + return enc; + } + + if (error) { + *error = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("Encountered unknown encoding (%@)"), encodingName); + CFRelease(encodingName); + } + return 0; + } +} + +extern bool __CFTryParseBinaryPlist(CFAllocatorRef allocator, CFDataRef data, CFOptionFlags option, CFPropertyListRef *plist, CFStringRef *errorString); +int32_t _CFPropertyListAllowNonUTF8 = 0; + +#define SAVE_PLISTS 0 + +#if SAVE_PLISTS +static Boolean __savePlistData(CFDataRef data, CFOptionFlags opt) { + uint8_t pn[2048]; + uint8_t fn[2048]; + uint32_t pnlen = sizeof(pn); + uint8_t *pnp = NULL; + if (0 == _NSGetExecutablePath((char *)pn, &pnlen)) { + pnp = strrchr((char *)pn, '/'); + } + if (!pnp) { + pnp = pn; + } else { + pnp++; + } + if (0 == strcmp((char *)pnp, "parse_plists")) return true; + CFUUIDRef r = CFUUIDCreate(kCFAllocatorSystemDefault); + CFStringRef s = CFUUIDCreateString(kCFAllocatorSystemDefault, r); + CFStringRef p = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("/tmp/plists/%s#%@#0x%x"), pnp, s, opt); + _CFStringGetFileSystemRepresentation(p, fn, sizeof(fn)); + CFRelease(r); + CFRelease(s); + CFRelease(p); + int fd = open((const char *)fn, O_WRONLY|O_CREAT|O_TRUNC, 0666); + if (fd < 0) return false; + int len = CFDataGetLength(data); + int w = write(fd, CFDataGetBytePtr(data), len); + fsync(fd); + close(fd); + if (w != len) return false; + return true; +} +#endif + +CFTypeRef _CFPropertyListCreateFromXMLData(CFAllocatorRef allocator, CFDataRef xmlData, CFOptionFlags option, CFStringRef *errorString, Boolean allowNewTypes, CFPropertyListFormat *format) { + initStatics(); + CFStringEncoding encoding; + CFStringRef xmlString; + UInt32 length; + CFPropertyListRef plist; + + if (errorString) *errorString = NULL; + if (!xmlData || CFDataGetLength(xmlData) == 0) { + if (errorString) { + *errorString = CFSTR("Cannot parse a NULL or zero-length data"); + CFRetain(*errorString); // Caller expects to release + } + return NULL; + } + +#if SAVE_PLISTS + __savePlistData(xmlData, option); +#endif + + if (__CFTryParseBinaryPlist(allocator, xmlData, option, &plist, errorString)) { + if (format) *format = kCFPropertyListBinaryFormat_v1_0; + return plist; + } + + allocator = allocator ? allocator : __CFGetDefaultAllocator(); + CFRetain(allocator); + + encoding = encodingForXMLData(xmlData, errorString); // 0 is an error return, NOT MacRoman. + + if (encoding == 0) { + // Couldn't find an encoding; encodingForXMLData already set *errorString if necessary + // Note that encodingForXMLData() will give us the right values for a standard plist, too. + if (errorString) *errorString = CFStringCreateWithFormat(allocator, NULL, CFSTR("Could not determine the encoding of the XML data")); + return NULL; + } + + xmlString = CFStringCreateWithBytes(allocator, CFDataGetBytePtr(xmlData), CFDataGetLength(xmlData), encoding, true); + if (NULL == xmlString && (!_CFExecutableLinkedOnOrAfter(CFSystemVersionLeopard) || _CFPropertyListAllowNonUTF8)) { // conversion failed, probably because not in proper encoding + // Call __CFStringCreateImmutableFunnel3() the same way CFStringCreateWithBytes() does, except with the addt'l flag + if (encoding == kCFStringEncodingUTF8) xmlString = __CFStringCreateImmutableFunnel3(allocator, CFDataGetBytePtr(xmlData), CFDataGetLength(xmlData), kCFStringEncodingUTF8, true, true, false, false, false, (CFAllocatorRef)-1 /* ALLOCATORSFREEFUNC */, kCFStringEncodingLenientUTF8Conversion); + } + length = xmlString ? CFStringGetLength(xmlString) : 0; + + if (length) { + _CFXMLPlistParseInfo pInfoBuf; + _CFXMLPlistParseInfo *pInfo = &pInfoBuf; + CFTypeRef result; + UniChar *buf = (UniChar *)CFStringGetCharactersPtr(xmlString); + + if (errorString) *errorString = NULL; + if (!buf) { + buf = (UniChar *)CFAllocatorAllocate(allocator, length * sizeof(UniChar), 0); + CFStringGetCharacters(xmlString, CFRangeMake(0, length), buf); + CFRelease(xmlString); + xmlString = NULL; + } + pInfo->begin = buf; + pInfo->end = buf+length; + pInfo->curr = buf; + pInfo->allocator = allocator; + pInfo->errorString = NULL; + pInfo->stringSet = NULL; + pInfo->tmpString = NULL; + pInfo->mutabilityOption = option; + pInfo->allowNewTypes = allowNewTypes; + + // Haven't done anything XML-specific to this point. However, the encoding we used to translate the bytes should be kept in mind; we used Unicode if the byte-order mark was present; UTF-8 otherwise. If the system encoding is not UTF-8 or some variant of 7-bit ASCII, we'll be in trouble..... + result = parseXMLPropertyList(pInfo); + if (result && format) *format = kCFPropertyListXMLFormat_v1_0; + if (!result) { + CFStringRef err = pInfo->errorString; + // Reset pInfo so we can try again + pInfo->curr = pInfo->begin; + pInfo->errorString = NULL; + // Try pList + result = parseOldStylePropertyListOrStringsFile(pInfo); + if (result && format) *format = kCFPropertyListOpenStepFormat; + if (!result) { + if (errorString) *errorString = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("XML parser error:\n\t%@\nOld-style plist parser error:\n\t%@\n"), err, pInfo->errorString); + } + if (err) CFRelease(err); + if (pInfo->errorString) CFRelease(pInfo->errorString); + } + if (xmlString) { + CFRelease(xmlString); + } else { + CFAllocatorDeallocate(allocator, (void *)pInfo->begin); + } + if (pInfo->stringSet) CFRelease(pInfo->stringSet); + if (pInfo->tmpString) CFRelease(pInfo->tmpString); + CFRelease(allocator); + return result; + } else { + if (errorString) + *errorString = (CFStringRef)CFRetain(CFSTR("Conversion of data failed. The file is not UTF-8, or in the encoding specified in XML header if XML.")); + return NULL; + } +} + +CFTypeRef CFPropertyListCreateFromXMLData(CFAllocatorRef allocator, CFDataRef xmlData, CFOptionFlags option, CFStringRef *errorString) { + initStatics(); + CFAssert1(xmlData != NULL, __kCFLogAssertion, "%s(): NULL data not allowed", __PRETTY_FUNCTION__); + CFAssert2(option == kCFPropertyListImmutable || option == kCFPropertyListMutableContainers || option == kCFPropertyListMutableContainersAndLeaves, __kCFLogAssertion, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__, option); + return _CFPropertyListCreateFromXMLData(allocator, xmlData, option, errorString, true, NULL); +} + +CFIndex CFPropertyListWriteToStream(CFPropertyListRef propertyList, CFWriteStreamRef stream, CFPropertyListFormat format, CFStringRef *errorString) { + initStatics(); + CFAssert1(stream != NULL, __kCFLogAssertion, "%s(): NULL stream not allowed", __PRETTY_FUNCTION__); + CFAssert2(format == kCFPropertyListOpenStepFormat || format == kCFPropertyListXMLFormat_v1_0 || format == kCFPropertyListBinaryFormat_v1_0, __kCFLogAssertion, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__, format); + CFAssert1(propertyList != NULL, __kCFLogAssertion, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__); + __CFAssertIsPList(propertyList); + CFAssert1(CFWriteStreamGetTypeID() == CFGetTypeID(stream), __kCFLogAssertion, "%s(): stream argument is not a write stream", __PRETTY_FUNCTION__); + CFAssert1(kCFStreamStatusOpen == CFWriteStreamGetStatus(stream) || kCFStreamStatusWriting == CFWriteStreamGetStatus(stream), __kCFLogAssertion, "%s(): stream is not open", __PRETTY_FUNCTION__); + + if (errorString) *errorString = NULL; + if (!CFPropertyListIsValid(propertyList, format)) { + if (errorString) *errorString = (CFStringRef)CFRetain(CFSTR("Property list invalid for format")); + return 0; + } + if (format == kCFPropertyListOpenStepFormat) { + if (errorString) *errorString = (CFStringRef)CFRetain(CFSTR("Property list format kCFPropertyListOpenStepFormat not supported for writing")); + return 0; + } + if (format == kCFPropertyListXMLFormat_v1_0) { + CFDataRef data = CFPropertyListCreateXMLData(kCFAllocatorSystemDefault, propertyList); + if (!data) { + if (errorString) *errorString = (CFStringRef)CFRetain(CFSTR("Property list in XML format could not be created for unknown reasons.")); + return 0; + } + CFIndex len = CFDataGetLength(data); + const uint8_t *ptr = CFDataGetBytePtr(data); + while (0 < len) { + CFIndex ret = CFWriteStreamWrite(stream, ptr, len); + if (ret == 0) { + if (errorString) *errorString = (CFStringRef)CFRetain(CFSTR("Property list writing could not be completed because stream is full.")); + return 0; + } + if (ret < 0) { + CFErrorRef err = CFWriteStreamCopyError(stream); + CFStringRef str = err ? CFErrorCopyDescription(err) : NULL; + if (errorString) *errorString = str ? str : (CFStringRef)CFRetain(CFSTR("Property list writing could not be completed because the stream had an unknown error.")); + if (err) CFRelease(err); + return 0; + } + ptr += ret; + len -= ret; + } + len = CFDataGetLength(data); + CFRelease(data); + return len; + } + if (format == kCFPropertyListBinaryFormat_v1_0) { + CFIndex len = __CFBinaryPlistWriteToStream(propertyList, stream); + return len; + } + if (errorString) *errorString = (CFStringRef)CFRetain(CFSTR("Unknown format option")); + return 0; +} + +static void __CFConvertReadStreamToBytes(CFReadStreamRef stream, CFIndex max, uint8_t **buffer, CFIndex *length) { + int32_t buflen = 0, bufsize = 0, retlen; + uint8_t *buf = NULL, sbuf[8192]; + for (;;) { + retlen = CFReadStreamRead(stream, sbuf, __CFMin(8192, max)); + if (retlen <= 0) { + *buffer = buf; + *length = buflen; + return; + } + if (bufsize < buflen + retlen) { + if (bufsize < 256 * 1024) { + bufsize *= 4; + } else if (bufsize < 16 * 1024 * 1024) { + bufsize *= 2; + } else { + // once in this stage, this will be really slow + // and really potentially fragment memory + bufsize += 256 * 1024; + } + if (bufsize < buflen + retlen) bufsize = buflen + retlen; + buf = (uint8_t *)CFAllocatorReallocate(kCFAllocatorSystemDefault, buf, bufsize, 0); + if (!buf) HALT; + } + memmove(buf + buflen, sbuf, retlen); + buflen += retlen; + max -= retlen; + if (max <= 0) { + *buffer = buf; + *length = buflen; + return; + } + } +} + +CFPropertyListRef CFPropertyListCreateFromStream(CFAllocatorRef allocator, CFReadStreamRef stream, CFIndex length, CFOptionFlags mutabilityOption, CFPropertyListFormat *format, CFStringRef *errorString) { + initStatics(); + CFPropertyListRef pl; + CFDataRef data; + CFIndex buflen = 0; + uint8_t *buffer = NULL; + CFAssert1(stream != NULL, __kCFLogAssertion, "%s(): NULL stream not allowed", __PRETTY_FUNCTION__); + CFAssert1(CFReadStreamGetTypeID() == CFGetTypeID(stream), __kCFLogAssertion, "%s(): stream argument is not a read stream", __PRETTY_FUNCTION__); + CFAssert1(kCFStreamStatusOpen == CFReadStreamGetStatus(stream) || kCFStreamStatusReading == CFReadStreamGetStatus(stream), __kCFLogAssertion, "%s(): stream is not open", __PRETTY_FUNCTION__); + CFAssert2(mutabilityOption == kCFPropertyListImmutable || mutabilityOption == kCFPropertyListMutableContainers || mutabilityOption == kCFPropertyListMutableContainersAndLeaves, __kCFLogAssertion, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__, mutabilityOption); + + if (errorString) *errorString = NULL; + if (0 == length) length = INT_MAX; + __CFConvertReadStreamToBytes(stream, length, &buffer, &buflen); + if (!buffer || buflen < 6) { + if (buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, buffer); + if (errorString) *errorString = (CFStringRef)CFRetain(CFSTR("stream had too few bytes")); + return NULL; + } + data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, buffer, buflen, kCFAllocatorSystemDefault); + pl = _CFPropertyListCreateFromXMLData(allocator, data, mutabilityOption, errorString, true, format); + CFRelease(data); + return pl; +} + +// ======================================================================== + +// +// Old NeXT-style property lists +// + +static CFTypeRef parsePlistObject(_CFXMLPlistParseInfo *pInfo, bool requireObject); + +#define isValidUnquotedStringCharacter(x) (((x) >= 'a' && (x) <= 'z') || ((x) >= 'A' && (x) <= 'Z') || ((x) >= '0' && (x) <= '9') || (x) == '_' || (x) == '$' || (x) == '/' || (x) == ':' || (x) == '.' || (x) == '-') + +static void advanceToNonSpace(_CFXMLPlistParseInfo *pInfo) { + UniChar ch2; + while (pInfo->curr < pInfo->end) { + ch2 = *(pInfo->curr); + pInfo->curr ++; + if (ch2 >= 9 && ch2 <= 0x0d) continue; // tab, newline, vt, form feed, carriage return + if (ch2 == ' ' || ch2 == 0x2028 || ch2 == 0x2029) continue; // space and Unicode line sep, para sep + if (ch2 == '/') { + if (pInfo->curr >= pInfo->end) { + // whoops; back up and return + pInfo->curr --; + return; + } else if (*(pInfo->curr) == '/') { + pInfo->curr ++; + while (pInfo->curr < pInfo->end) { // go to end of comment line + UniChar ch3 = *(pInfo->curr); + if (ch3 == '\n' || ch3 == '\r' || ch3 == 0x2028 || ch3 == 0x2029) break; + pInfo->curr ++; + } + } else if (*(pInfo->curr) == '*') { // handle /* ... */ + pInfo->curr ++; + while (pInfo->curr < pInfo->end) { + ch2 = *(pInfo->curr); + pInfo->curr ++; + if (ch2 == '*' && pInfo->curr < pInfo->end && *(pInfo->curr) == '/') { + pInfo->curr ++; // advance past the '/' + break; + } + } + } else { + pInfo->curr --; + return; + } + } else { + pInfo->curr --; + return; + } + } +} + +static UniChar getSlashedChar(_CFXMLPlistParseInfo *pInfo) { + UniChar ch = *(pInfo->curr); + pInfo->curr ++; + switch (ch) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': { + uint8_t num = ch - '0'; + UniChar result; + CFIndex usedCharLen; + /* three digits maximum to avoid reading \000 followed by 5 as \5 ! */ + if ((ch = *(pInfo->curr)) >= '0' && ch <= '7') { // we use in this test the fact that the buffer is zero-terminated + pInfo->curr ++; + num = (num << 3) + ch - '0'; + if ((pInfo->curr < pInfo->end) && (ch = *(pInfo->curr)) >= '0' && ch <= '7') { + pInfo->curr ++; + num = (num << 3) + ch - '0'; + } + } + CFStringEncodingBytesToUnicode(kCFStringEncodingNextStepLatin, 0, &num, sizeof(uint8_t), NULL, &result, 1, &usedCharLen); + return (usedCharLen == 1) ? result : 0; + } + case 'U': { + unsigned num = 0, numDigits = 4; /* Parse four digits */ + while (pInfo->curr < pInfo->end && numDigits--) { + if (((ch = *(pInfo->curr)) < 128) && isxdigit(ch)) { + pInfo->curr ++; + num = (num << 4) + ((ch <= '9') ? (ch - '0') : ((ch <= 'F') ? (ch - 'A' + 10) : (ch - 'a' + 10))); + } + } + return num; + } + case 'a': return '\a'; // Note: the meaning of '\a' varies with -traditional to gcc + case 'b': return '\b'; + case 'f': return '\f'; + case 'n': return '\n'; + case 'r': return '\r'; + case 't': return '\t'; + case 'v': return '\v'; + case '"': return '\"'; + case '\n': return '\n'; + } + return ch; +} + +static CFStringRef parseQuotedPlistString(_CFXMLPlistParseInfo *pInfo, UniChar quote) { + CFMutableStringRef str = NULL; + const UniChar *startMark = pInfo->curr; + const UniChar *mark = pInfo->curr; + while (pInfo->curr < pInfo->end) { + UniChar ch = *(pInfo->curr); + if (ch == quote) break; + if (ch == '\\') { + _catFromMarkToBuf(mark, pInfo->curr, &str, pInfo->allocator); + pInfo->curr ++; + ch = getSlashedChar(pInfo); + CFStringAppendCharacters(str, &ch, 1); + mark = pInfo->curr; + } else { + // Note that the original NSParser code was much more complex at this point, but it had to deal with 8-bit characters in a non-UniChar stream. We always have UniChar (we translated the data by the system encoding at the very beginning, hopefully), so this is safe. + pInfo->curr ++; + } + } + if (pInfo->end <= pInfo->curr) { + if (str) CFRelease(str); + pInfo->curr = startMark; + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Unterminated quoted string starting on line %d"), lineNumber(pInfo)); + return NULL; + } + if (!str) { + if (pInfo->mutabilityOption == kCFPropertyListMutableContainersAndLeaves) { + _catFromMarkToBuf(mark, pInfo->curr, &str, pInfo->allocator); + } else { + str = (CFMutableStringRef)_uniqueStringForCharacters(pInfo, mark, pInfo->curr-mark); + CFRetain(str); + } + } else { + if (mark != pInfo->curr) { + _catFromMarkToBuf(mark, pInfo->curr, &str, pInfo->allocator); + } + if (pInfo->mutabilityOption != kCFPropertyListMutableContainersAndLeaves) { + CFStringRef uniqueString = _uniqueStringForString(pInfo, str); + CFRetain(uniqueString); + CFRelease(str); + str = (CFMutableStringRef)uniqueString; + } + } + pInfo->curr ++; // Advance past the quote character before returning. + if (pInfo->errorString) { + CFRelease(pInfo->errorString); + pInfo->errorString = NULL; + } + return str; +} + +static CFStringRef parseUnquotedPlistString(_CFXMLPlistParseInfo *pInfo) { + const UniChar *mark = pInfo->curr; + while (pInfo->curr < pInfo->end) { + UniChar ch = *pInfo->curr; + if (isValidUnquotedStringCharacter(ch)) + pInfo->curr ++; + else break; + } + if (pInfo->curr != mark) { + if (pInfo->mutabilityOption != kCFPropertyListMutableContainersAndLeaves) { + CFStringRef str = _uniqueStringForCharacters(pInfo, mark, pInfo->curr-mark); + CFRetain(str); + return str; + } else { + CFMutableStringRef str = CFStringCreateMutable(pInfo->allocator, 0); + CFStringAppendCharacters(str, mark, pInfo->curr - mark); + return str; + } + } + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Unexpected EOF")); + return NULL; +} + +static CFStringRef parsePlistString(_CFXMLPlistParseInfo *pInfo, bool requireObject) { + UniChar ch; + advanceToNonSpace(pInfo); + if (pInfo->curr >= pInfo->end) { + if (requireObject) { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Unexpected EOF while parsing string")); + } + return NULL; + } + ch = *(pInfo->curr); + if (ch == '\'' || ch == '\"') { + pInfo->curr ++; + return parseQuotedPlistString(pInfo, ch); + } else if (isValidUnquotedStringCharacter(ch)) { + return parseUnquotedPlistString(pInfo); + } else { + if (requireObject) { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Invalid string character at line %d"), lineNumber(pInfo)); + } + return NULL; + } +} + +static CFTypeRef parsePlistArray(_CFXMLPlistParseInfo *pInfo) { + CFMutableArrayRef array = CFArrayCreateMutable(pInfo->allocator, 0, &kCFTypeArrayCallBacks); + CFTypeRef tmp = parsePlistObject(pInfo, false); + while (tmp) { + CFArrayAppendValue(array, tmp); + CFRelease(tmp); + advanceToNonSpace(pInfo); + if (*pInfo->curr != ',') { + tmp = NULL; + } else { + pInfo->curr ++; + tmp = parsePlistObject(pInfo, false); + } + } + advanceToNonSpace(pInfo); + if (*pInfo->curr != ')') { + CFRelease(array); + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Expected terminating ')' for array at line %d"), lineNumber(pInfo)); + return NULL; + } + if (pInfo->errorString) { + CFRelease(pInfo->errorString); + pInfo->errorString = NULL; + } + pInfo->curr ++; + return array; +} + +static CFDictionaryRef parsePlistDictContent(_CFXMLPlistParseInfo *pInfo) { + CFMutableDictionaryRef dict = CFDictionaryCreateMutable(pInfo->allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFStringRef key = NULL; + Boolean failedParse = false; + key = parsePlistString(pInfo, false); + while (key) { + CFTypeRef value; + advanceToNonSpace(pInfo); + if (*pInfo->curr == ';') { + /* This is a strings file using the shortcut format */ + /* although this check here really applies to all plists. */ + value = CFRetain(key); + } else if (*pInfo->curr == '=') { + pInfo->curr ++; + value = parsePlistObject(pInfo, true); + if (!value) { + failedParse = true; + break; + } + } else { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Unexpected ';' or '=' after key at line %d"), lineNumber(pInfo)); + failedParse = true; + break; + } + CFDictionarySetValue(dict, key, value); + CFRelease(key); + key = NULL; + CFRelease(value); + value = NULL; + advanceToNonSpace(pInfo); + if (*pInfo->curr == ';') { + pInfo->curr ++; + key = parsePlistString(pInfo, false); + } else if (!allowMissingSemi && _CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar)) { + CFLog(kCFLogLevelWarning, CFSTR("CFPropertyListCreateFromXMLData(): Old-style plist parser: missing semicolon in dictionary.")); + failedParse = true; + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Missing ';' on line %d"), lineNumber(pInfo)); + } else { + // on pre-Jaguar systems, do nothing except silently ignore the rest + // of the dictionary, which is what happened on those systems. + } + } + + if (failedParse) { + if (key) CFRelease(key); + CFRelease(dict); + return NULL; + } + if (pInfo->errorString) { + CFRelease(pInfo->errorString); + pInfo->errorString = NULL; + } + return dict; +} + +static CFTypeRef parsePlistDict(_CFXMLPlistParseInfo *pInfo) { + CFDictionaryRef dict = parsePlistDictContent(pInfo); + if (!dict) return NULL; + advanceToNonSpace(pInfo); + if (*pInfo->curr != '}') { + CFRelease(dict); + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Expected terminating '}' for dictionary at line %d"), lineNumber(pInfo)); + return NULL; + } + pInfo->curr ++; + return dict; +} + +CF_INLINE unsigned char fromHexDigit(unsigned char ch) { + if (isdigit(ch)) return ch - '0'; + if ((ch >= 'a') && (ch <= 'f')) return ch - 'a' + 10; + if ((ch >= 'A') && (ch <= 'F')) return ch - 'A' + 10; + return 0xff; // Just choose a large number for the error code +} + +/* Gets up to bytesSize bytes from a plist data. Returns number of bytes actually read. Leaves cursor at first non-space, non-hex character. + -1 is returned for unexpected char, -2 for uneven number of hex digits +*/ +static int getDataBytes(_CFXMLPlistParseInfo *pInfo, unsigned char *bytes, int bytesSize) { + int numBytesRead = 0; + while ((pInfo->curr < pInfo->end) && (numBytesRead < bytesSize)) { + int first, second; + UniChar ch1 = *pInfo->curr; + if (ch1 == '>') return numBytesRead; // Meaning we're done + first = fromHexDigit((unsigned char)ch1); + if (first != 0xff) { // If the first char is a hex, then try to read a second hex + pInfo->curr++; + if (pInfo->curr >= pInfo->end) return -2; // Error: uneven number of hex digits + UniChar ch2 = *pInfo->curr; + second = fromHexDigit((unsigned char)ch2); + if (second == 0xff) return -2; // Error: uneven number of hex digits + bytes[numBytesRead++] = (first << 4) + second; + pInfo->curr++; + } else if (ch1 == ' ' || ch1 == '\n' || ch1 == '\t' || ch1 == '\r' || ch1 == 0x2028 || ch1 == 0x2029) { + pInfo->curr++; + } else { + return -1; // Error: unexpected character + } + } + return numBytesRead; // This does likely mean we didn't encounter a '>', but we'll let the caller deal with that +} + +#define numBytes 400 +static CFTypeRef parsePlistData(_CFXMLPlistParseInfo *pInfo) { + CFMutableDataRef result = CFDataCreateMutable(pInfo->allocator, 0); + + // Read hex bytes and append them to result + while (1) { + unsigned char bytes[numBytes]; + int numBytesRead = getDataBytes(pInfo, bytes, numBytes); + if (numBytesRead < 0) { + CFRelease(result); + switch (numBytesRead) { + case -2: pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Malformed data byte group at line %d; uneven length"), lineNumber(pInfo)); break; + default: pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Malformed data byte group at line %d; invalid hex"), lineNumber(pInfo)); break; + } + return NULL; + } + if (numBytesRead == 0) break; + CFDataAppendBytes(result, bytes, numBytesRead); + } + + if (pInfo->errorString) { + CFRelease(pInfo->errorString); + pInfo->errorString = NULL; + } + + if (*(pInfo->curr) == '>') { + pInfo->curr ++; // Move past '>' + return result; + } else { + CFRelease(result); + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Expected terminating '>' for data at line %d"), lineNumber(pInfo)); + return NULL; + } +} +#undef numBytes + +// Returned object is retained; caller must free. +static CFTypeRef parsePlistObject(_CFXMLPlistParseInfo *pInfo, bool requireObject) { + UniChar ch; + advanceToNonSpace(pInfo); + if (pInfo->curr + 1 >= pInfo->end) { + if (requireObject) { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Unexpected EOF while parsing plist")); + } + return NULL; + } + ch = *(pInfo->curr); + pInfo->curr ++; + if (ch == '{') { + return parsePlistDict(pInfo); + } else if (ch == '(') { + return parsePlistArray(pInfo); + } else if (ch == '<') { + return parsePlistData(pInfo); + } else if (ch == '\'' || ch == '\"') { + return parseQuotedPlistString(pInfo, ch); + } else if (isValidUnquotedStringCharacter(ch)) { + pInfo->curr --; + return parseUnquotedPlistString(pInfo); + } else { + pInfo->curr --; // Must back off the charcter we just read + if (requireObject) { + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Unexpected character '0x%x' at line %d"), ch, lineNumber(pInfo)); + } + return NULL; + } +} + +static CFTypeRef parseOldStylePropertyListOrStringsFile(_CFXMLPlistParseInfo *pInfo) { + const UniChar *begin = pInfo->curr; + CFTypeRef result; + advanceToNonSpace(pInfo); + // A file consisting only of whitespace (or empty) is now defined to be an empty dictionary + if (pInfo->curr >= pInfo->end) return CFDictionaryCreateMutable(pInfo->allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + result = parsePlistObject(pInfo, true); + advanceToNonSpace(pInfo); + if (pInfo->curr >= pInfo->end) return result; + if (!result) return NULL; + if (CFGetTypeID(result) != stringtype) { + CFRelease(result); + pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Junk after plist at line %d"), lineNumber(pInfo)); + return NULL; + } + CFRelease(result); + // Check for a strings file (looks like a dictionary without the opening/closing curly braces) + pInfo->curr = begin; + return parsePlistDictContent(pInfo); +} + +#undef isValidUnquotedStringCharacter + +static CFArrayRef _arrayDeepImmutableCopy(CFAllocatorRef allocator, CFArrayRef array, CFOptionFlags mutabilityOption) { + CFArrayRef result = NULL; + CFIndex i, c = CFArrayGetCount(array); + CFTypeRef *values; + if (c == 0) { + result = CFArrayCreate(allocator, NULL, 0, &kCFTypeArrayCallBacks); + } else if ((values = (CFTypeRef *)CFAllocatorAllocate(allocator, c*sizeof(CFTypeRef), 0)) != NULL) { + CFArrayGetValues(array, CFRangeMake(0, c), values); + for (i = 0; i < c; i ++) { + values[i] = CFPropertyListCreateDeepCopy(allocator, values[i], mutabilityOption); + if (values[i] == NULL) { + break; + } + } + result = (i == c) ? CFArrayCreate(allocator, values, c, &kCFTypeArrayCallBacks) : NULL; + c = i; + for (i = 0; i < c; i ++) { + CFRelease(values[i]); + } + CFAllocatorDeallocate(allocator, values); + } + return result; +} + +static CFMutableArrayRef _arrayDeepMutableCopy(CFAllocatorRef allocator, CFArrayRef array, CFOptionFlags mutabilityOption) { + CFIndex i, c = CFArrayGetCount(array); + CFMutableArrayRef result = CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks); + if (result) { + for (i = 0; i < c; i ++) { + CFTypeRef newValue = CFPropertyListCreateDeepCopy(allocator, CFArrayGetValueAtIndex(array, i), mutabilityOption); + if (!newValue) break; + CFArrayAppendValue(result, newValue); + CFRelease(newValue); + } + if (i != c) { + CFRelease(result); + result = NULL; + } + } + return result; +} + +CFPropertyListRef CFPropertyListCreateDeepCopy(CFAllocatorRef allocator, CFPropertyListRef propertyList, CFOptionFlags mutabilityOption) { + initStatics(); + CFTypeID typeID; + CFPropertyListRef result = NULL; + CFAssert1(propertyList != NULL, __kCFLogAssertion, "%s(): cannot copy a NULL property list", __PRETTY_FUNCTION__); + __CFAssertIsPList(propertyList); + CFAssert2(mutabilityOption == kCFPropertyListImmutable || mutabilityOption == kCFPropertyListMutableContainers || mutabilityOption == kCFPropertyListMutableContainersAndLeaves, __kCFLogAssertion, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__, mutabilityOption); + if (!CFPropertyListIsValid(propertyList, kCFPropertyListBinaryFormat_v1_0)) return NULL; + + if (allocator == NULL) { + allocator = (CFAllocatorRef)CFRetain(__CFGetDefaultAllocator()); + } else { + CFRetain(allocator); + } + + typeID = CFGetTypeID(propertyList); + if (typeID == dicttype) { + CFDictionaryRef dict = (CFDictionaryRef)propertyList; + Boolean isMutable = (mutabilityOption != kCFPropertyListImmutable); + CFIndex count = CFDictionaryGetCount(dict); + CFTypeRef *keys, *values; + if (count == 0) { + result = isMutable ? CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks): CFDictionaryCreate(allocator, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + } else if ((keys = (CFTypeRef *)CFAllocatorAllocate(allocator, 2 * count * sizeof(CFTypeRef), 0)) != NULL) { + CFIndex i; + values = keys+count; + CFDictionaryGetKeysAndValues(dict, keys, values); + for (i = 0; i < count; i ++) { + keys[i] = CFStringCreateCopy(allocator, (CFStringRef)keys[i]); + if (keys[i] == NULL) { + break; + } + values[i] = CFPropertyListCreateDeepCopy(allocator, values[i], mutabilityOption); + if (values[i] == NULL) { + CFRelease(keys[i]); + break; + } + } + if (i == count) { + result = isMutable ? CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) : CFDictionaryCreate(allocator, keys, values, count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + for (i = 0; i < count; i ++) { + if (isMutable) { + CFDictionarySetValue((CFMutableDictionaryRef)result, keys[i], values[i]); + } + CFRelease(keys[i]); + CFRelease(values[i]); + } + } else { + result = NULL; + count = i; + for (i = 0; i < count; i ++) { + CFRelease(keys[i]); + CFRelease(values[i]); + } + } + CFAllocatorDeallocate(allocator, keys); + } else { + result = NULL; + } + } else if (typeID == arraytype) { + if (mutabilityOption == kCFPropertyListImmutable) { + result = _arrayDeepImmutableCopy(allocator, (CFArrayRef)propertyList, mutabilityOption); + } else { + result = _arrayDeepMutableCopy(allocator, (CFArrayRef)propertyList, mutabilityOption); + } + } else if (typeID == datatype) { + if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) { + result = CFDataCreateMutableCopy(allocator, 0, (CFDataRef)propertyList); + } else { + result = CFDataCreateCopy(allocator, (CFDataRef)propertyList); + } + } else if (typeID == numbertype) { + // Warning - this will break if byteSize is ever greater than 32 + uint8_t bytes[32]; + CFNumberType numType = CFNumberGetType((CFNumberRef)propertyList); + CFNumberGetValue((CFNumberRef)propertyList, numType, (void *)bytes); + result = CFNumberCreate(allocator, numType, (void *)bytes); + } else if (typeID == booltype) { + // Booleans are immutable & shared instances + CFRetain(propertyList); + result = propertyList; + } else if (typeID == datetype) { + // Dates are immutable + result = CFDateCreate(allocator, CFDateGetAbsoluteTime((CFDateRef)propertyList)); + } else if (typeID == stringtype) { + if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) { + result = CFStringCreateMutableCopy(allocator, 0, (CFStringRef)propertyList); + } else { + result = CFStringCreateCopy(allocator, (CFStringRef)propertyList); + } + } else { + CFAssert2(false, __kCFLogAssertion, "%s(): %p is not a property list type", __PRETTY_FUNCTION__, propertyList); + result = NULL; + } + CFRelease(allocator); + return result; +} +
diff --git a/CoreFoundation/CFPropertyList.h b/CoreFoundation/CFPropertyList.h new file mode 100644 index 0000000..9ac413b --- /dev/null +++ b/CoreFoundation/CFPropertyList.h
@@ -0,0 +1,135 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFPropertyList.h + Copyright (c) 1998-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFPROPERTYLIST__) +#define __COREFOUNDATION_CFPROPERTYLIST__ 1 + +#include <CoreFoundation/CFBase.h> +#include <CoreFoundation/CFData.h> +#include <CoreFoundation/CFString.h> +#include <CoreFoundation/CFStream.h> + +CF_EXTERN_C_BEGIN + +enum { + kCFPropertyListImmutable = 0, + kCFPropertyListMutableContainers, + kCFPropertyListMutableContainersAndLeaves +}; +typedef CFOptionFlags CFPropertyListMutabilityOptions; + +/* + Creates a property list object from its XML description; xmlData should + be the raw bytes of that description, possibly the contents of an XML + file. Returns NULL if the data cannot be parsed; if the parse fails + and errorString is non-NULL, a human-readable description of the failure + is returned in errorString. It is the caller's responsibility to release + either the returned object or the error string, whichever is applicable. +*/ +CF_EXPORT +CFPropertyListRef CFPropertyListCreateFromXMLData(CFAllocatorRef allocator, CFDataRef xmlData, CFOptionFlags mutabilityOption, CFStringRef *errorString); + +/* + Returns the XML description of the given object; propertyList must + be one of the supported property list types, and (for composite types + like CFArray and CFDictionary) must not contain any elements that + are not themselves of a property list type. If a non-property list + type is encountered, NULL is returned. The returned data is + appropriate for writing out to an XML file. Note that a data, not a + string, is returned because the bytes contain in them a description + of the string encoding used. +*/ +CF_EXPORT +CFDataRef CFPropertyListCreateXMLData(CFAllocatorRef allocator, CFPropertyListRef propertyList); + +/* + Recursively creates a copy of the given property list (so nested arrays + and dictionaries are copied as well as the top-most container). The + resulting property list has the mutability characteristics determined + by mutabilityOption. +*/ +CF_EXPORT +CFPropertyListRef CFPropertyListCreateDeepCopy(CFAllocatorRef allocator, CFPropertyListRef propertyList, CFOptionFlags mutabilityOption); + +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED + +enum { + kCFPropertyListOpenStepFormat = 1, + kCFPropertyListXMLFormat_v1_0 = 100, + kCFPropertyListBinaryFormat_v1_0 = 200 +}; +typedef CFIndex CFPropertyListFormat; + +CF_EXPORT +Boolean CFPropertyListIsValid(CFPropertyListRef plist, CFPropertyListFormat format); + +/* Returns true if the object graph rooted at plist is a valid property list + * graph -- that is, no cycles, containing only plist objects, and dictionary + * keys are strings. The debugging library version spits out some messages + * to be helpful. The plist structure which is to be allowed is given by + * the format parameter. */ + +CF_EXPORT +CFIndex CFPropertyListWriteToStream(CFPropertyListRef propertyList, CFWriteStreamRef stream, CFPropertyListFormat format, CFStringRef *errorString); + +/* Writes the bytes of a plist serialization out to the stream. The + * stream must be opened and configured -- the function simply writes + * a bunch of bytes to the stream. The output plist format can be chosen. + * Leaves the stream open, but note that reading a plist expects the + * reading stream to end wherever the writing ended, so that the + * end of the plist data can be identified. Returns the number of bytes + * written, or 0 on error. Error messages are not currently localized, but + * may be in the future, so they are not suitable for comparison. */ + +CF_EXPORT +CFPropertyListRef CFPropertyListCreateFromStream(CFAllocatorRef allocator, CFReadStreamRef stream, CFIndex streamLength, CFOptionFlags mutabilityOption, CFPropertyListFormat *format, CFStringRef *errorString); + +/* Same as current function CFPropertyListCreateFromXMLData() + * but takes a stream instead of data, and works on any plist file format. + * CFPropertyListCreateFromXMLData() also works on any plist file format. + * The stream must be open and configured -- the function simply reads a bunch + * of bytes from it starting at the current location in the stream, to the END + * of the stream, which is expected to be the end of the plist, or up to the + * number of bytes given by the length parameter if it is not 0. Error messages + * are not currently localized, but may be in the future, so they are not + * suitable for comparison. */ + +#endif + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFPROPERTYLIST__ */ +
diff --git a/CoreFoundation/CFRunLoop.c b/CoreFoundation/CFRunLoop.c new file mode 100644 index 0000000..d190406 --- /dev/null +++ b/CoreFoundation/CFRunLoop.c
@@ -0,0 +1,3215 @@ +/* + * 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@ + */ +/* CFRunLoop.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX + +#include <CoreFoundation/CFRunLoop.h> +#include <CoreFoundation/CFSet.h> +#include <CoreFoundation/CFBag.h> +#include "CFInternal.h" +#include <math.h> +#include <stdio.h> +#include <limits.h> +#if DEPLOYMENT_TARGET_MACOSX +#include <mach/mach.h> +#include <mach/clock_types.h> +#include <mach/clock.h> +#include <unistd.h> +#include <dlfcn.h> +#elif DEPLOYMENT_TARGET_WINDOWS +#if !defined(__MINGW32__) && !defined(__CYGWIN__) +// With the MS headers, turning off Standard-C gets you macros for stat vs _stat. +// Strictly speaking, this is supposed to control traditional vs ANSI C features. +#undef __STDC__ +#endif +#include <windows.h> +#include <process.h> +#if !defined(__MINGW32__) && !defined(__CYGWIN__) +#define __STDC__ +#endif +#elif DEPLOYMENT_TARGET_LINUX +#include <dlfcn.h> +#include <pthread.h> +#include <semaphore.h> +#endif + +static int _LogCFRunLoop = 0; + +#if 0 || 0 +static pthread_t kNilThreadT = { nil, nil }; +#define pthreadPointer(a) a.p +#define lockCount(a) a.LockCount +#define NativeThread pthread_t +#elif DEPLOYMENT_TARGET_WINDOWS +DWORD GetInternalThreadId(HANDLE t); + +static HANDLE kNilThreadT = 0; +static DWORD __kCFMainThread = 0; +#define pthreadPointer(a) ((void*)GetInternalThreadId(a)) +// Note: Windows RTL_CRITICAL_SECTION LockCount is initialized to -1 +// indicating no lock. So add one so lock count shows a valid 'count'. +#define lockCount(a) (a.LockCount + 1) +#define NativeThread HANDLE +#elif DEPLOYMENT_TARGET_LINUX +static pthread_t kNilThreadT = (pthread_t)0; +static pthread_t __kCFMainThread = (pthread_t)0; +#define pthreadPointer(a) (void *)a +#define lockCount(a) a.lock +#define NativeThread pthread_t +#else +static pthread_t kNilThreadT = (pthread_t)0; +#define pthreadPointer(a) a +#define lockCount(a) a +#define NativeThread pthread_t +#endif + +#if DEPLOYMENT_TARGET_MACOSX +#include <sys/types.h> +#include <sys/event.h> + +typedef struct { + CFIndex version; + void * info; + const void *(*retain)(const void *info); + void (*release)(const void *info); + CFStringRef (*copyDescription)(const void *info); + Boolean (*equal)(const void *info1, const void *info2); + CFHashCode (*hash)(const void *info); + void (*perform)(const struct kevent *kev, void *info); + struct kevent event; +} CFRunLoopSourceContext2; + +// The bits in the flags field in the kevent structure are cleared except for EV_ONESHOT and EV_CLEAR. +// Do not use the udata field of the kevent structure -- that field is smashed by CFRunLoop. +// There is no way to EV_ENABLE or EV_DISABLE a kevent. +// The "autoinvalidation" of EV_ONESHOT is not handled properly by CFRunLoop yet. +// The "autoinvalidation" of EV_DELETE on the last close of a file descriptor is not handled properly by CFRunLoop yet. +// There is no way to reset the state in a kevent (such as clearing the EV_EOF state for fifos). +#endif + +extern bool CFDictionaryGetKeyIfPresent(CFDictionaryRef dict, const void *key, const void **actualkey); + +// In order to reuse most of the code across Mach and Windows v1 RunLoopSources, we define a +// simple abstraction layer spanning Mach ports and Windows HANDLES +#if DEPLOYMENT_TARGET_MACOSX + +typedef mach_port_t __CFPort; +#define CFPORT_NULL MACH_PORT_NULL +#define CFPORTSET_NULL MACH_PORT_NULL +typedef mach_port_t __CFPortSet; + +static __CFPort __CFPortAllocate(void) { + __CFPort result; + kern_return_t ret; + ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &result); + if (KERN_SUCCESS == ret) { + ret = mach_port_insert_right(mach_task_self(), result, result, MACH_MSG_TYPE_MAKE_SEND); + } + if (KERN_SUCCESS == ret) { + mach_port_limits_t limits; + limits.mpl_qlimit = 1; + ret = mach_port_set_attributes(mach_task_self(), result, MACH_PORT_LIMITS_INFO, (mach_port_info_t)&limits, MACH_PORT_LIMITS_INFO_COUNT); + } + return (KERN_SUCCESS == ret) ? result : CFPORT_NULL; +} + +CF_INLINE void __CFPortFree(__CFPort port) { + mach_port_destroy(mach_task_self(), port); +} + +CF_INLINE __CFPortSet __CFPortSetAllocate(void) { + __CFPortSet result; + kern_return_t ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &result); + return (KERN_SUCCESS == ret) ? result : CFPORT_NULL; +} + +CF_INLINE Boolean __CFPortSetInsert(__CFPort port, __CFPortSet portSet) { + kern_return_t ret = mach_port_insert_member(mach_task_self(), port, portSet); + return (KERN_SUCCESS == ret); +} + +CF_INLINE Boolean __CFPortSetRemove(__CFPort port, __CFPortSet portSet) { + kern_return_t ret = mach_port_extract_member(mach_task_self(), port, portSet); + return (KERN_SUCCESS == ret); +} + +CF_INLINE void __CFPortSetFree(__CFPortSet portSet) { + kern_return_t ret; + mach_port_name_array_t array; + mach_msg_type_number_t idx, number; + + ret = mach_port_get_set_status(mach_task_self(), portSet, &array, &number); + if (KERN_SUCCESS == ret) { + for (idx = 0; idx < number; idx++) { + mach_port_extract_member(mach_task_self(), array[idx], portSet); + } + vm_deallocate(mach_task_self(), (vm_address_t)array, number * sizeof(mach_port_name_t)); + } + mach_port_destroy(mach_task_self(), portSet); +} + +#elif DEPLOYMENT_TARGET_WINDOWS + +typedef HANDLE __CFPort; +#define CFPORT_NULL NULL +#define CFPORTSET_NULL NULL +#define MAX_PORTS MAXIMUM_WAIT_OBJECTS + +CF_INLINE __CFPort __CFPortAllocate(void) { + return CreateEvent(NULL, true, false, NULL); +} + +CF_INLINE void __CFPortFree(__CFPort port) { + CloseHandle(port); +} + +#elif DEPLOYMENT_TARGET_LINUX + +typedef sem_t * __CFPort; +#define CFPORT_NULL NULL +#define CFPORTSET_NULL NULL +#define MAX_PORTS 16 + +CF_INLINE __CFPort __CFPortAllocate(void) { + __CFPort port = CFPORT_NULL; + int status; + + port = (__CFPort)CFAllocatorAllocate(kCFAllocatorSystemDefault, + sizeof(sem_t), 0); + CFAssert1(port != CFPORT_NULL, __kCFLogAssertion, + "%s(): Could not allocate port memory.", __PRETTY_FUNCTION__); + if (port != CFPORT_NULL) { + status = sem_init(port, false, 0); + CFAssert1(status == 0, __kCFLogAssertion, + "%s(): Could not initialize port.", __PRETTY_FUNCTION__); + } + + return port; +} + +CF_INLINE void __CFPortFree(__CFPort port) { + int status; + CFAssert1(port != CFPORT_NULL, __kCFLogAssertion, + "%s(): Attemping to free an invalid port.", __PRETTY_FUNCTION__); + status = sem_destroy(port); + CFAllocatorDeallocate(kCFAllocatorSystemDefault, port); +} + +#endif /* DEPLOYMENT_TARGET_MACOSX */ + +#if !DEPLOYMENT_TARGET_MACOSX + +// A simple dynamic array of __CFPorts, which grows to a high-water mark +typedef struct ___CFPortSet { + uint16_t used; + uint16_t size; + __CFPort *ports; + CFSpinLock_t lock; // insert and remove must be thread safe, like the Mach calls +} *__CFPortSet; + +static __CFPortSet __CFPortSetAllocate(void) { + __CFPortSet result = (__CFPortSet)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(struct ___CFPortSet), 0); + result->used = 0; + result->size = 4; + result->ports = (__CFPort *)CFAllocatorAllocate(kCFAllocatorSystemDefault, result->size * sizeof(__CFPort), 0); + CF_SPINLOCK_INIT_FOR_STRUCTS(result->lock); + return result; +} + +static void __CFPortSetFree(__CFPortSet portSet) { + CFAllocatorDeallocate(kCFAllocatorSystemDefault, portSet->ports); + CFAllocatorDeallocate(kCFAllocatorSystemDefault, portSet); +} + +// Returns portBuf if ports fit in that space, else returns another ptr that must be freed +static __CFPort *__CFPortSetGetPorts(__CFPortSet portSet, __CFPort *portBuf, uint32_t bufSize, uint32_t *portsUsed) { + __CFSpinLock(&(portSet->lock)); + __CFPort *result = portBuf; + if (bufSize > portSet->used) + result = (__CFPort *)CFAllocatorAllocate(kCFAllocatorSystemDefault, portSet->used * sizeof(__CFPort), 0); + memmove(result, portSet->ports, portSet->used * sizeof(__CFPort)); + *portsUsed = portSet->used; + __CFSpinUnlock(&(portSet->lock)); + return result; +} + +static Boolean __CFPortSetInsert(__CFPort port, __CFPortSet portSet) { + __CFSpinLock(&(portSet->lock)); + if (portSet->used >= portSet->size) { + portSet->size += 4; + portSet->ports = (__CFPort*)CFAllocatorReallocate(kCFAllocatorSystemDefault, portSet->ports, portSet->size * sizeof(__CFPort), 0); + } + if (portSet->used >= MAX_PORTS) + CFLog(kCFLogLevelWarning, CFSTR("*** More than %d ports added to a port set. The last ones will be ignored."), MAX_PORTS); + portSet->ports[portSet->used++] = port; + __CFSpinUnlock(&(portSet->lock)); + return true; +} + +static Boolean __CFPortSetRemove(__CFPort port, __CFPortSet portSet) { + int i, j; + __CFSpinLock(&(portSet->lock)); + for (i = 0; i < portSet->used; i++) { + if (portSet->ports[i] == port) { + for (j = i+1; j < portSet->used; j++) { + portSet->ports[j-1] = portSet->ports[j]; + } + portSet->used--; + __CFSpinUnlock(&(portSet->lock)); + return true; + } + } + __CFSpinUnlock(&(portSet->lock)); + return false; +} + +#endif + +#if DEPLOYMENT_TARGET_MACOSX +extern mach_port_name_t mk_timer_create(void); +extern kern_return_t mk_timer_destroy(mach_port_name_t name); +extern kern_return_t mk_timer_arm(mach_port_name_t name, AbsoluteTime expire_time); +extern kern_return_t mk_timer_cancel(mach_port_name_t name, AbsoluteTime *result_time); + +CF_INLINE AbsoluteTime __CFUInt64ToAbsoluteTime(int64_t x) { + AbsoluteTime a; + a.hi = x >> 32; + a.lo = x & (int64_t)0xFFFFFFFF; + return a; +} + +static uint32_t __CFSendTrivialMachMessage(mach_port_t port, uint32_t msg_id, CFOptionFlags options, uint32_t timeout) { + kern_return_t result; + mach_msg_header_t header; + header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); + header.msgh_size = sizeof(mach_msg_header_t); + header.msgh_remote_port = port; + header.msgh_local_port = MACH_PORT_NULL; + header.msgh_id = msg_id; + result = mach_msg(&header, MACH_SEND_MSG|options, header.msgh_size, 0, MACH_PORT_NULL, timeout, MACH_PORT_NULL); + if (result == MACH_SEND_TIMED_OUT) mach_msg_destroy(&header); + return result; +} +#endif + +/* unlock a run loop and modes before doing callouts/sleeping */ +/* never try to take the run loop lock with a mode locked */ +/* be very careful of common subexpression elimination and compacting code, particular across locks and unlocks! */ +/* run loop mode structures should never be deallocated, even if they become empty */ + +static CFTypeID __kCFRunLoopModeTypeID = _kCFRuntimeNotATypeID; +static CFTypeID __kCFRunLoopTypeID = _kCFRuntimeNotATypeID; +static CFTypeID __kCFRunLoopSourceTypeID = _kCFRuntimeNotATypeID; +static CFTypeID __kCFRunLoopObserverTypeID = _kCFRuntimeNotATypeID; +static CFTypeID __kCFRunLoopTimerTypeID = _kCFRuntimeNotATypeID; + +typedef struct __CFRunLoopMode *CFRunLoopModeRef; + +struct __CFRunLoopMode { + CFRuntimeBase _base; + CFSpinLock_t _lock; /* must have the run loop locked before locking this */ + CFStringRef _name; + Boolean _stopped; + char _padding[3]; + CFMutableSetRef _sources; + CFMutableSetRef _observers; + CFMutableSetRef _timers; + CFMutableArrayRef _submodes; // names of the submodes + __CFPortSet _portSet; +#if DEPLOYMENT_TARGET_MACOSX + int _kq; +#elif DEPLOYMENT_TARGET_WINDOWS + DWORD _msgQMask; +#endif +}; + +static int64_t __CFRunLoopGetNextTimerFireTSR(CFRunLoopRef rl, CFRunLoopModeRef rlm); + +CF_INLINE void __CFRunLoopModeLock(CFRunLoopModeRef rlm) { + __CFSpinLock(&(rlm->_lock)); +} + +CF_INLINE void __CFRunLoopModeUnlock(CFRunLoopModeRef rlm) { + __CFSpinUnlock(&(rlm->_lock)); +} + +static Boolean __CFRunLoopModeEqual(CFTypeRef cf1, CFTypeRef cf2) { + CFRunLoopModeRef rlm1 = (CFRunLoopModeRef)cf1; + CFRunLoopModeRef rlm2 = (CFRunLoopModeRef)cf2; + return CFEqual(rlm1->_name, rlm2->_name); +} + +static CFHashCode __CFRunLoopModeHash(CFTypeRef cf) { + CFRunLoopModeRef rlm = (CFRunLoopModeRef)cf; + return CFHash(rlm->_name); +} + +static CFStringRef __CFRunLoopModeCopyDescription(CFTypeRef cf) { + CFRunLoopModeRef rlm = (CFRunLoopModeRef)cf; + CFMutableStringRef result; + result = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); + CFStringAppendFormat(result, NULL, CFSTR("<CFRunLoopMode %p [%p]>{name = %@, "), rlm, CFGetAllocator(rlm), rlm->_name); +#if DEPLOYMENT_TARGET_MACOSX + CFStringAppendFormat(result, NULL, CFSTR("port set = %p,"), rlm->_portSet); +#elif DEPLOYMENT_TARGET_WINDOWS + CFStringAppendFormat(result, NULL, CFSTR("MSGQ mask = %p,"), rlm->_msgQMask); +#endif + CFStringAppendFormat(result, NULL, CFSTR("\n\tsources = %@,\n\tobservers == %@,\n\ttimers = %@\n},\n"), rlm->_sources, rlm->_observers, rlm->_timers); + return result; +} + +static void __CFRunLoopModeDeallocate(CFTypeRef cf) { + CFRunLoopModeRef rlm = (CFRunLoopModeRef)cf; + if (NULL != rlm->_sources) CFRelease(rlm->_sources); + if (NULL != rlm->_observers) CFRelease(rlm->_observers); + if (NULL != rlm->_timers) CFRelease(rlm->_timers); + if (NULL != rlm->_submodes) CFRelease(rlm->_submodes); + CFRelease(rlm->_name); + __CFPortSetFree(rlm->_portSet); +#if DEPLOYMENT_TARGET_MACOSX + if (-1 != rlm->_kq) close(rlm->_kq); +#endif +} + +struct __CFRunLoop { + CFRuntimeBase _base; + CFSpinLock_t _lock; /* locked for accessing mode list */ + __CFPort _wakeUpPort; // used for CFRunLoopWakeUp + volatile uint32_t *_stopped; + CFMutableSetRef _commonModes; + CFMutableSetRef _commonModeItems; + CFRunLoopModeRef _currentMode; + CFMutableSetRef _modes; + void *_counterpart; +}; + +/* Bit 0 of the base reserved bits is used for stopped state */ +/* Bit 1 of the base reserved bits is used for sleeping state */ +/* Bit 2 of the base reserved bits is used for deallocating state */ + +CF_INLINE Boolean __CFRunLoopIsStopped(CFRunLoopRef rl) { + return (rl->_stopped && rl->_stopped[2]) ? true : false; +} + +CF_INLINE void __CFRunLoopSetStopped(CFRunLoopRef rl) { + if (rl->_stopped) rl->_stopped[2] = 0x53544F50; // 'STOP' +} + +CF_INLINE void __CFRunLoopUnsetStopped(CFRunLoopRef rl) { + if (rl->_stopped) rl->_stopped[2] = 0x0; +} + +CF_INLINE Boolean __CFRunLoopIsSleeping(CFRunLoopRef rl) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)rl)->_cfinfo[CF_INFO_BITS], 1, 1); +} + +CF_INLINE void __CFRunLoopSetSleeping(CFRunLoopRef rl) { + __CFBitfieldSetValue(((CFRuntimeBase *)rl)->_cfinfo[CF_INFO_BITS], 1, 1, 1); +} + +CF_INLINE void __CFRunLoopUnsetSleeping(CFRunLoopRef rl) { + __CFBitfieldSetValue(((CFRuntimeBase *)rl)->_cfinfo[CF_INFO_BITS], 1, 1, 0); +} + +CF_INLINE Boolean __CFRunLoopIsDeallocating(CFRunLoopRef rl) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)rl)->_cfinfo[CF_INFO_BITS], 2, 2); +} + +CF_INLINE void __CFRunLoopSetDeallocating(CFRunLoopRef rl) { + __CFBitfieldSetValue(((CFRuntimeBase *)rl)->_cfinfo[CF_INFO_BITS], 2, 2, 1); +} + +CF_INLINE void __CFRunLoopLock(CFRunLoopRef rl) { + __CFSpinLock(&(((CFRunLoopRef)rl)->_lock)); +} + +CF_INLINE void __CFRunLoopUnlock(CFRunLoopRef rl) { + __CFSpinUnlock(&(((CFRunLoopRef)rl)->_lock)); +} + +static CFStringRef __CFRunLoopCopyDescription(CFTypeRef cf) { + CFRunLoopRef rl = (CFRunLoopRef)cf; + CFMutableStringRef result; + result = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); + CFStringAppendFormat(result, NULL, CFSTR("<CFRunLoop %p [%p]>{wait port = 0x%x, stopped = %s,\ncurrent mode = %@,\n"), cf, CFGetAllocator(cf), rl->_wakeUpPort, (rl->_stopped && (rl->_stopped[2] == 0x53544F50)) ? "true" : "false", rl->_currentMode ? rl->_currentMode->_name : CFSTR("(none)")); + CFStringAppendFormat(result, NULL, CFSTR("common modes = %@,\ncommon mode items = %@,\nmodes = %@}\n"), rl->_commonModes, rl->_commonModeItems, rl->_modes); + return result; +} + +/* call with rl locked */ +static CFRunLoopModeRef __CFRunLoopFindMode(CFRunLoopRef rl, CFStringRef modeName, Boolean create) { + CHECK_FOR_FORK(); + CFRunLoopModeRef rlm; + struct __CFRunLoopMode srlm; + srlm._base._cfisa = __CFISAForTypeID(__kCFRunLoopModeTypeID); + srlm._base._cfinfo[CF_INFO_BITS] = 0; + _CFRuntimeSetInstanceTypeID(&srlm, __kCFRunLoopModeTypeID); + srlm._name = modeName; + rlm = (CFRunLoopModeRef)CFSetGetValue(rl->_modes, &srlm); + if (NULL != rlm) { + __CFRunLoopModeLock(rlm); + return rlm; + } + if (!create) { + return NULL; + } + rlm = (CFRunLoopModeRef)_CFRuntimeCreateInstance(CFGetAllocator(rl), __kCFRunLoopModeTypeID, sizeof(struct __CFRunLoopMode) - sizeof(CFRuntimeBase), NULL); + if (NULL == rlm) { + return NULL; + } + CF_SPINLOCK_INIT_FOR_STRUCTS(rlm->_lock); + rlm->_name = CFStringCreateCopy(CFGetAllocator(rlm), modeName); + rlm->_stopped = false; + rlm->_sources = NULL; + rlm->_observers = NULL; + rlm->_timers = NULL; + rlm->_submodes = NULL; + rlm->_portSet = __CFPortSetAllocate(); + if (CFPORTSET_NULL == rlm->_portSet) HALT; + if (!__CFPortSetInsert(rl->_wakeUpPort, rlm->_portSet)) HALT; +#if DEPLOYMENT_TARGET_MACOSX + rlm->_kq = -1; +#elif DELPOYMENT_TARGET_WIN32 + rlm->_msgQMask = 0; +#endif + CFSetAddValue(rl->_modes, rlm); + CFRelease(rlm); + __CFRunLoopModeLock(rlm); /* return mode locked */ + return rlm; +} + + +// expects rl and rlm locked +static Boolean __CFRunLoopModeIsEmpty(CFRunLoopRef rl, CFRunLoopModeRef rlm) { + CHECK_FOR_FORK(); + if (NULL == rlm) return true; +#if DEPLOYMENT_TARGET_WINDOWS + if (0 != rlm->_msgQMask) return false; +#endif + if (NULL != rlm->_sources && 0 < CFSetGetCount(rlm->_sources)) return false; + if (NULL != rlm->_timers && 0 < CFSetGetCount(rlm->_timers)) return false; + if (NULL != rlm->_submodes) { + CFIndex idx, cnt; + for (idx = 0, cnt = CFArrayGetCount(rlm->_submodes); idx < cnt; idx++) { + CFStringRef modeName = (CFStringRef)CFArrayGetValueAtIndex(rlm->_submodes, idx); + CFRunLoopModeRef subrlm; + Boolean subIsEmpty; + subrlm = __CFRunLoopFindMode(rl, modeName, false); + subIsEmpty = (NULL != subrlm) ? __CFRunLoopModeIsEmpty(rl, subrlm) : true; + if (NULL != subrlm) __CFRunLoopModeUnlock(subrlm); + if (!subIsEmpty) return false; + } + } + return true; +} + +#if DEPLOYMENT_TARGET_WINDOWS +DWORD __CFRunLoopGetWindowsMessageQueueMask(CFRunLoopRef rl, CFStringRef modeName) { + CFRunLoopModeRef rlm; + DWORD result = 0; + __CFRunLoopLock(rl); + rlm = __CFRunLoopFindMode(rl, modeName, false); + if (rlm) { + result = rlm->_msgQMask; + __CFRunLoopModeUnlock(rlm); + } + __CFRunLoopUnlock(rl); + return result; +} + +void __CFRunLoopSetWindowsMessageQueueMask(CFRunLoopRef rl, DWORD mask, CFStringRef modeName) { + CFRunLoopModeRef rlm; + __CFRunLoopLock(rl); + rlm = __CFRunLoopFindMode(rl, modeName, true); + rlm->_msgQMask = mask; + __CFRunLoopModeUnlock(rlm); + __CFRunLoopUnlock(rl); +} +#endif + +/* Bit 3 in the base reserved bits is used for invalid state in run loop objects */ + +CF_INLINE Boolean __CFIsValid(const void *cf) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 3, 3); +} + +CF_INLINE void __CFSetValid(void *cf) { + __CFBitfieldSetValue(((CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 3, 3, 1); +} + +CF_INLINE void __CFUnsetValid(void *cf) { + __CFBitfieldSetValue(((CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 3, 3, 0); +} + +struct __CFRunLoopSource { + CFRuntimeBase _base; + uint32_t _bits; + CFSpinLock_t _lock; + CFIndex _order; /* immutable */ + CFMutableBagRef _runLoops; + union { + CFRunLoopSourceContext version0; /* immutable, except invalidation */ + CFRunLoopSourceContext1 version1; /* immutable, except invalidation */ + } _context; +}; + +/* Bit 1 of the base reserved bits is used for signalled state */ + +CF_INLINE Boolean __CFRunLoopSourceIsSignaled(CFRunLoopSourceRef rls) { + return (Boolean)__CFBitfieldGetValue(rls->_bits, 1, 1); +} + +CF_INLINE void __CFRunLoopSourceSetSignaled(CFRunLoopSourceRef rls) { + __CFBitfieldSetValue(rls->_bits, 1, 1, 1); +} + +CF_INLINE void __CFRunLoopSourceUnsetSignaled(CFRunLoopSourceRef rls) { + __CFBitfieldSetValue(rls->_bits, 1, 1, 0); +} + +CF_INLINE void __CFRunLoopSourceLock(CFRunLoopSourceRef rls) { + __CFSpinLock(&(rls->_lock)); +} + +CF_INLINE void __CFRunLoopSourceUnlock(CFRunLoopSourceRef rls) { + __CFSpinUnlock(&(rls->_lock)); +} + +/* rlm is not locked */ +static void __CFRunLoopSourceSchedule(CFRunLoopSourceRef rls, CFRunLoopRef rl, CFRunLoopModeRef rlm) { /* DOES CALLOUT */ + __CFRunLoopSourceLock(rls); + if (NULL == rls->_runLoops) { + rls->_runLoops = CFBagCreateMutable(CFGetAllocator(rls), 0, NULL); + } + CFBagAddValue(rls->_runLoops, rl); + __CFRunLoopSourceUnlock(rls); // have to unlock before the callout -- cannot help clients with safety + if (0 == rls->_context.version0.version) { + if (NULL != rls->_context.version0.schedule) { + rls->_context.version0.schedule(rls->_context.version0.info, rl, rlm->_name); + } + } else if (1 == rls->_context.version0.version) { + __CFPort port = rls->_context.version1.getPort(rls->_context.version1.info); /* CALLOUT */ + if (CFPORT_NULL != port) { + __CFPortSetInsert(port, rlm->_portSet); + } + } +} + +/* rlm is not locked */ +static void __CFRunLoopSourceCancel(CFRunLoopSourceRef rls, CFRunLoopRef rl, CFRunLoopModeRef rlm) { /* DOES CALLOUT */ + if (0 == rls->_context.version0.version) { + if (NULL != rls->_context.version0.cancel) { + rls->_context.version0.cancel(rls->_context.version0.info, rl, rlm->_name); /* CALLOUT */ + } + } else if (1 == rls->_context.version0.version) { + __CFPort port = rls->_context.version1.getPort(rls->_context.version1.info); /* CALLOUT */ + if (CFPORT_NULL != port) { + __CFPortSetRemove(port, rlm->_portSet); + } + } + __CFRunLoopSourceLock(rls); + if (NULL != rls->_runLoops) { + CFBagRemoveValue(rls->_runLoops, rl); + } + __CFRunLoopSourceUnlock(rls); +} + +struct __CFRunLoopObserver { + CFRuntimeBase _base; + CFSpinLock_t _lock; + CFRunLoopRef _runLoop; + CFIndex _rlCount; + CFOptionFlags _activities; /* immutable */ + CFIndex _order; /* immutable */ + CFRunLoopObserverCallBack _callout; /* immutable */ + CFRunLoopObserverContext _context; /* immutable, except invalidation */ +}; + +/* Bit 0 of the base reserved bits is used for firing state */ +/* Bit 1 of the base reserved bits is used for repeats state */ + +CF_INLINE Boolean __CFRunLoopObserverIsFiring(CFRunLoopObserverRef rlo) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 0, 0); +} + +CF_INLINE void __CFRunLoopObserverSetFiring(CFRunLoopObserverRef rlo) { + __CFBitfieldSetValue(((CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 0, 0, 1); +} + +CF_INLINE void __CFRunLoopObserverUnsetFiring(CFRunLoopObserverRef rlo) { + __CFBitfieldSetValue(((CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 0, 0, 0); +} + +CF_INLINE Boolean __CFRunLoopObserverRepeats(CFRunLoopObserverRef rlo) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 1, 1); +} + +CF_INLINE void __CFRunLoopObserverSetRepeats(CFRunLoopObserverRef rlo) { + __CFBitfieldSetValue(((CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 1, 1, 1); +} + +CF_INLINE void __CFRunLoopObserverUnsetRepeats(CFRunLoopObserverRef rlo) { + __CFBitfieldSetValue(((CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 1, 1, 0); +} + +CF_INLINE void __CFRunLoopObserverLock(CFRunLoopObserverRef rlo) { + __CFSpinLock(&(rlo->_lock)); +} + +CF_INLINE void __CFRunLoopObserverUnlock(CFRunLoopObserverRef rlo) { + __CFSpinUnlock(&(rlo->_lock)); +} + +static void __CFRunLoopObserverSchedule(CFRunLoopObserverRef rlo, CFRunLoopRef rl, CFRunLoopModeRef rlm) { + __CFRunLoopObserverLock(rlo); + if (0 == rlo->_rlCount) { + rlo->_runLoop = rl; + } + rlo->_rlCount++; + __CFRunLoopObserverUnlock(rlo); +} + +static void __CFRunLoopObserverCancel(CFRunLoopObserverRef rlo, CFRunLoopRef rl, CFRunLoopModeRef rlm) { + __CFRunLoopObserverLock(rlo); + rlo->_rlCount--; + if (0 == rlo->_rlCount) { + rlo->_runLoop = NULL; + } + __CFRunLoopObserverUnlock(rlo); +} + +struct __CFRunLoopTimer { + CFRuntimeBase _base; + CFSpinLock_t _lock; + CFRunLoopRef _runLoop; + CFIndex _rlCount; +#if DEPLOYMENT_TARGET_MACOSX + mach_port_name_t _port; +#endif + CFIndex _order; /* immutable */ + int64_t _fireTSR; /* TSR units */ + int64_t _intervalTSR; /* immutable; 0 means non-repeating; TSR units */ + CFRunLoopTimerCallBack _callout; /* immutable */ + CFRunLoopTimerContext _context; /* immutable, except invalidation */ +}; + +/* Bit 0 of the base reserved bits is used for firing state */ +/* Bit 1 of the base reserved bits is used for fired-during-callout state */ + +CF_INLINE Boolean __CFRunLoopTimerIsFiring(CFRunLoopTimerRef rlt) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)rlt)->_cfinfo[CF_INFO_BITS], 0, 0); +} + +CF_INLINE void __CFRunLoopTimerSetFiring(CFRunLoopTimerRef rlt) { + __CFBitfieldSetValue(((CFRuntimeBase *)rlt)->_cfinfo[CF_INFO_BITS], 0, 0, 1); +} + +CF_INLINE void __CFRunLoopTimerUnsetFiring(CFRunLoopTimerRef rlt) { + __CFBitfieldSetValue(((CFRuntimeBase *)rlt)->_cfinfo[CF_INFO_BITS], 0, 0, 0); +} + +CF_INLINE Boolean __CFRunLoopTimerDidFire(CFRunLoopTimerRef rlt) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)rlt)->_cfinfo[CF_INFO_BITS], 1, 1); +} + +CF_INLINE void __CFRunLoopTimerSetDidFire(CFRunLoopTimerRef rlt) { + __CFBitfieldSetValue(((CFRuntimeBase *)rlt)->_cfinfo[CF_INFO_BITS], 1, 1, 1); +} + +CF_INLINE void __CFRunLoopTimerUnsetDidFire(CFRunLoopTimerRef rlt) { + __CFBitfieldSetValue(((CFRuntimeBase *)rlt)->_cfinfo[CF_INFO_BITS], 1, 1, 0); +} + +CF_INLINE void __CFRunLoopTimerLock(CFRunLoopTimerRef rlt) { + __CFSpinLock(&(rlt->_lock)); +} + +CF_INLINE void __CFRunLoopTimerUnlock(CFRunLoopTimerRef rlt) { + __CFSpinUnlock(&(rlt->_lock)); +} + +static CFSpinLock_t __CFRLTFireTSRLock = CFSpinLockInit; + +CF_INLINE void __CFRunLoopTimerFireTSRLock(void) { + __CFSpinLock(&__CFRLTFireTSRLock); +} + +CF_INLINE void __CFRunLoopTimerFireTSRUnlock(void) { + __CFSpinUnlock(&__CFRLTFireTSRLock); +} + +#if DEPLOYMENT_TARGET_MACOSX +static CFMutableDictionaryRef __CFRLTPortMap = NULL; +static CFSpinLock_t __CFRLTPortMapLock = CFSpinLockInit; + +CF_INLINE void __CFRunLoopTimerPortMapLock(void) { + __CFSpinLock(&__CFRLTPortMapLock); +} + +CF_INLINE void __CFRunLoopTimerPortMapUnlock(void) { + __CFSpinUnlock(&__CFRLTPortMapLock); +} +#endif + +static void __CFRunLoopTimerSchedule(CFRunLoopTimerRef rlt, CFRunLoopRef rl, CFRunLoopModeRef rlm) { +#if DEPLOYMENT_TARGET_MACOSX + __CFRunLoopTimerLock(rlt); + if (0 == rlt->_rlCount) { + rlt->_runLoop = rl; + if (MACH_PORT_NULL == rlt->_port) { + rlt->_port = mk_timer_create(); + } + __CFRunLoopTimerPortMapLock(); + if (NULL == __CFRLTPortMap) { + __CFRLTPortMap = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, NULL); + } + CFDictionarySetValue(__CFRLTPortMap, (void *)(uintptr_t)rlt->_port, rlt); + __CFRunLoopTimerPortMapUnlock(); + } + rlt->_rlCount++; + mach_port_insert_member(mach_task_self(), rlt->_port, rlm->_portSet); + mk_timer_arm(rlt->_port, __CFUInt64ToAbsoluteTime(rlt->_fireTSR)); + __CFRunLoopTimerUnlock(rlt); +#endif +} + +static void __CFRunLoopTimerCancel(CFRunLoopTimerRef rlt, CFRunLoopRef rl, CFRunLoopModeRef rlm) { +#if DEPLOYMENT_TARGET_MACOSX + __CFRunLoopTimerLock(rlt); + __CFPortSetRemove(rlt->_port, rlm->_portSet); + rlt->_rlCount--; + if (0 == rlt->_rlCount) { + __CFRunLoopTimerPortMapLock(); + if (NULL != __CFRLTPortMap) { + CFDictionaryRemoveValue(__CFRLTPortMap, (void *)(uintptr_t)rlt->_port); + } + __CFRunLoopTimerPortMapUnlock(); + rlt->_runLoop = NULL; + mk_timer_cancel(rlt->_port, NULL); + } + __CFRunLoopTimerUnlock(rlt); +#endif +} + +// Caller must hold the Timer lock for safety +static void __CFRunLoopTimerRescheduleWithAllModes(CFRunLoopTimerRef rlt, CFRunLoopRef rl) { +#if DEPLOYMENT_TARGET_MACOSX + mk_timer_arm(rlt->_port, __CFUInt64ToAbsoluteTime(rlt->_fireTSR)); +#endif +} + +#if DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX +typedef struct _collectTimersContext { + CFMutableArrayRef results; + int64_t cutoffTSR; +}; + +static void __CFRunLoopCollectTimers(const void *value, void *ctx) { + CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)value; + struct _collectTimersContext *context = (struct _collectTimersContext*)ctx; + if (rlt->_fireTSR <= context->cutoffTSR) { + if (NULL == context->results) + context->results = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); + CFArrayAppendValue(context->results, rlt); + } +} + +// RunLoop and RunLoopMode must be locked +static void __CFRunLoopTimersToFireRecursive(CFRunLoopRef rl, CFRunLoopModeRef rlm, struct _collectTimersContext *ctxt) { + if (NULL != rlm->_timers && 0 < CFSetGetCount(rlm->_timers)) { + __CFRunLoopTimerFireTSRLock(); + CFSetApplyFunction(rlm->_timers, __CFRunLoopCollectTimers, ctxt); + __CFRunLoopTimerFireTSRUnlock(); + } + if (NULL != rlm->_submodes) { + CFIndex idx, cnt; + for (idx = 0, cnt = CFArrayGetCount(rlm->_submodes); idx < cnt; idx++) { + CFStringRef modeName = (CFStringRef)CFArrayGetValueAtIndex(rlm->_submodes, idx); + CFRunLoopModeRef subrlm; + subrlm = __CFRunLoopFindMode(rl, modeName, false); + if (NULL != subrlm) { + __CFRunLoopTimersToFireRecursive(rl, subrlm, ctxt); + __CFRunLoopModeUnlock(subrlm); + } + } + } +} + +// RunLoop and RunLoopMode must be locked +static CFArrayRef __CFRunLoopTimersToFire(CFRunLoopRef rl, CFRunLoopModeRef rlm) { + struct _collectTimersContext ctxt = {NULL, __CFReadTSR()}; + __CFRunLoopTimersToFireRecursive(rl, rlm, &ctxt); + return ctxt.results; +} +#endif // DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX + +/* CFRunLoop */ + +CONST_STRING_DECL(kCFRunLoopDefaultMode, "kCFRunLoopDefaultMode") +CONST_STRING_DECL(kCFRunLoopCommonModes, "kCFRunLoopCommonModes") + +struct _findsource { + __CFPort port; + CFRunLoopSourceRef result; +}; + +static void __CFRunLoopFindSource(const void *value, void *ctx) { + CFRunLoopSourceRef rls = (CFRunLoopSourceRef)value; + struct _findsource *context = (struct _findsource *)ctx; + __CFPort port; + if (NULL != context->result) return; + if (1 != rls->_context.version0.version) return; + __CFRunLoopSourceLock(rls); + port = rls->_context.version1.getPort(rls->_context.version1.info); + if (port == context->port) { + context->result = rls; + } + __CFRunLoopSourceUnlock(rls); +} + +// call with rl and rlm locked +static CFRunLoopSourceRef __CFRunLoopModeFindSourceForMachPort(CFRunLoopRef rl, CFRunLoopModeRef rlm, __CFPort port) { /* DOES CALLOUT */ + CHECK_FOR_FORK(); + struct _findsource context = {port, NULL}; + if (NULL != rlm->_sources) { + CFSetApplyFunction(rlm->_sources, (__CFRunLoopFindSource), &context); + } + if (NULL == context.result && NULL != rlm->_submodes) { + CFIndex idx, cnt; + for (idx = 0, cnt = CFArrayGetCount(rlm->_submodes); idx < cnt; idx++) { + CFRunLoopSourceRef source = NULL; + CFStringRef modeName = (CFStringRef)CFArrayGetValueAtIndex(rlm->_submodes, idx); + CFRunLoopModeRef subrlm; + subrlm = __CFRunLoopFindMode(rl, modeName, false); + if (NULL != subrlm) { + source = __CFRunLoopModeFindSourceForMachPort(rl, subrlm, port); + __CFRunLoopModeUnlock(subrlm); + } + if (NULL != source) { + context.result = source; + break; + } + } + } + return context.result; +} + +#if DEPLOYMENT_TARGET_MACOSX +// call with rl and rlm locked +static CFRunLoopTimerRef __CFRunLoopModeFindTimerForMachPort(CFRunLoopModeRef rlm, __CFPort port) { + CHECK_FOR_FORK(); + CFRunLoopTimerRef result = NULL; + __CFRunLoopTimerPortMapLock(); + if (NULL != __CFRLTPortMap) { + result = (CFRunLoopTimerRef)CFDictionaryGetValue(__CFRLTPortMap, (void *)(uintptr_t)port); + } + __CFRunLoopTimerPortMapUnlock(); + return result; +} +#endif + +static void __CFRunLoopDeallocateSources(const void *value, void *context) { + CFRunLoopModeRef rlm = (CFRunLoopModeRef)value; + CFRunLoopRef rl = (CFRunLoopRef)context; + CFIndex idx, cnt; + const void **list, *buffer[256]; + if (NULL == rlm->_sources) return; + cnt = CFSetGetCount(rlm->_sources); + list = (cnt <= 256) ? buffer : (const void**)CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(void *), 0); + CFSetGetValues(rlm->_sources, list); + for (idx = 0; idx < cnt; idx++) { + CFRetain(list[idx]); + } + CFSetRemoveAllValues(rlm->_sources); + for (idx = 0; idx < cnt; idx++) { + __CFRunLoopSourceCancel((CFRunLoopSourceRef)list[idx], rl, rlm); + CFRelease(list[idx]); + } + if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); +} + +static void __CFRunLoopDeallocateObservers(const void *value, void *context) { + CFRunLoopModeRef rlm = (CFRunLoopModeRef)value; + CFRunLoopRef rl = (CFRunLoopRef)context; + CFIndex idx, cnt; + const void **list, *buffer[256]; + if (NULL == rlm->_observers) return; + cnt = CFSetGetCount(rlm->_observers); + list = (cnt <= 256) ? buffer : (const void**)CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(void *), 0); + CFSetGetValues(rlm->_observers, list); + for (idx = 0; idx < cnt; idx++) { + CFRetain(list[idx]); + } + CFSetRemoveAllValues(rlm->_observers); + for (idx = 0; idx < cnt; idx++) { + __CFRunLoopObserverCancel((CFRunLoopObserverRef)list[idx], rl, rlm); + CFRelease(list[idx]); + } + if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); +} + +static void __CFRunLoopDeallocateTimers(const void *value, void *context) { + CFRunLoopModeRef rlm = (CFRunLoopModeRef)value; + CFRunLoopRef rl = (CFRunLoopRef)context; + CFIndex idx, cnt; + const void **list, *buffer[256]; + if (NULL == rlm->_timers) return; + cnt = CFSetGetCount(rlm->_timers); + list = (cnt <= 256) ? buffer : (const void**)CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(void *), 0); + CFSetGetValues(rlm->_timers, list); + for (idx = 0; idx < cnt; idx++) { + CFRetain(list[idx]); + } + CFSetRemoveAllValues(rlm->_timers); + for (idx = 0; idx < cnt; idx++) { + __CFRunLoopTimerCancel((CFRunLoopTimerRef)list[idx], rl, rlm); + CFRelease(list[idx]); + } + if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); +} + +static void __CFRunLoopDeallocate(CFTypeRef cf) { + CFRunLoopRef rl = (CFRunLoopRef)cf; + /* We try to keep the run loop in a valid state as long as possible, + since sources may have non-retained references to the run loop. + Another reason is that we don't want to lock the run loop for + callback reasons, if we can get away without that. We start by + eliminating the sources, since they are the most likely to call + back into the run loop during their "cancellation". Common mode + items will be removed from the mode indirectly by the following + three lines. */ + __CFRunLoopSetDeallocating(rl); + if (NULL != rl->_modes) { + CFSetApplyFunction(rl->_modes, (__CFRunLoopDeallocateSources), rl); + CFSetApplyFunction(rl->_modes, (__CFRunLoopDeallocateObservers), rl); + CFSetApplyFunction(rl->_modes, (__CFRunLoopDeallocateTimers), rl); + } + __CFRunLoopLock(rl); + if (NULL != rl->_commonModeItems) { + CFRelease(rl->_commonModeItems); + } + if (NULL != rl->_commonModes) { + CFRelease(rl->_commonModes); + } + if (NULL != rl->_modes) { + CFRelease(rl->_modes); + } + __CFPortFree(rl->_wakeUpPort); + rl->_wakeUpPort = CFPORT_NULL; + __CFRunLoopUnlock(rl); +} + +static const CFRuntimeClass __CFRunLoopModeClass = { + 0, + "CFRunLoopMode", + NULL, // init + NULL, // copy + __CFRunLoopModeDeallocate, + __CFRunLoopModeEqual, + __CFRunLoopModeHash, + NULL, // + __CFRunLoopModeCopyDescription +}; + +static const CFRuntimeClass __CFRunLoopClass = { + 0, + "CFRunLoop", + NULL, // init + NULL, // copy + __CFRunLoopDeallocate, + NULL, + NULL, + NULL, // + __CFRunLoopCopyDescription +}; + +CF_INLINE NativeThread _CFThreadSelf(void) { +#if DEPLOYMENT_TARGET_MACOS || DEPLOYMENT_TARGET_FREEBSD || DEPLOYMENT_TARGET_LINUX + return pthread_self(); +#elif DEPLOYMENT_TARGET_WINDOWS + return GetCurrentThread(); +#else + HALT; + return kNilThreadT; +#endif +} + +CF_INLINE Boolean _CFThreadIsMain(void) { + Boolean result = false; +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_FREEBSD + result = pthread_main_np(); +#elif DEPLOYMENT_TARGET_LINUX + result = (__kCFMainThread == _CFThreadSelf()); +#elif DEPLOYMENT_TARGET_WINDOWS + result = (__kCFMainThread == GetCurrentThreadId()); +#else + HALT; +#endif + return result; +} + +CF_INLINE Boolean _CFThreadsAreEqual(NativeThread a, NativeThread b) { + Boolean result = false; +#if DEPLOYMENT_TARGET_MACOS || DEPLOYMENT_TARGET_FREEBSD || DEPLOYMENT_TARGET_LINUX + result = pthread_equal(a, b); +#elif DEPLOYMENT_TARGET_WINDOWS + result = (pthreadPointer(a) == pthreadPointer(b)); +#else + HALT; +#endif + return result; +} + +__private_extern__ void __CFRunLoopInitialize(void) { + __kCFRunLoopTypeID = _CFRuntimeRegisterClass(&__CFRunLoopClass); + __kCFRunLoopModeTypeID = _CFRuntimeRegisterClass(&__CFRunLoopModeClass); +#if DEPLOYMENT_TARGET_LINUX + __kCFMainThread = _CFThreadSelf(); +#elif DEPLOYMENT_TARGET_WINDOWS + __kCFMainThread = GetInternalThreadId (_CFThreadSelf()); +#endif +} + +CFTypeID CFRunLoopGetTypeID(void) { + return __kCFRunLoopTypeID; +} + +static CFRunLoopRef __CFRunLoopCreate(void) { + CFRunLoopRef loop = NULL; + CFRunLoopModeRef rlm; + uint32_t size = sizeof(struct __CFRunLoop) - sizeof(CFRuntimeBase); + loop = (CFRunLoopRef)_CFRuntimeCreateInstance(kCFAllocatorSystemDefault, __kCFRunLoopTypeID, size, NULL); + if (NULL == loop) { + return NULL; + } + loop->_stopped = NULL; + CF_SPINLOCK_INIT_FOR_STRUCTS(loop->_lock); + loop->_wakeUpPort = __CFPortAllocate(); + if (CFPORT_NULL == loop->_wakeUpPort) HALT; + loop->_commonModes = CFSetCreateMutable(CFGetAllocator(loop), 0, &kCFTypeSetCallBacks); + CFSetAddValue(loop->_commonModes, kCFRunLoopDefaultMode); + loop->_commonModeItems = NULL; + loop->_currentMode = NULL; + loop->_modes = CFSetCreateMutable(CFGetAllocator(loop), 0, &kCFTypeSetCallBacks); + _CFSetSetCapacity(loop->_modes, 10); + loop->_counterpart = NULL; + rlm = __CFRunLoopFindMode(loop, kCFRunLoopDefaultMode, true); + if (NULL != rlm) __CFRunLoopModeUnlock(rlm); + return loop; +} + +static CFMutableDictionaryRef runLoops = NULL; +static char setMainLoop = 0; +static CFSpinLock_t loopsLock = CFSpinLockInit; + +// If this is called on a non-main thread, and the main thread pthread_t is passed in, +// and this has not yet beed called on the main thread (since the last fork(), this will +// produce a different run loop that will probably be tossed away eventually, than the +// main thread run loop. There's nothing much we can do about that, without a call to +// fetch the main thread's pthread_t from the pthreads subsystem. + +// t==0 is a synonym for "main thread" that always works +__private_extern__ CFRunLoopRef _CFRunLoop0(NativeThread t) { + CFRunLoopRef loop = NULL; + __CFSpinLock(&loopsLock); + if (!runLoops) { + __CFSpinUnlock(&loopsLock); + CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, NULL); + CFRunLoopRef mainLoop = __CFRunLoopCreate(); + CFDictionarySetValue(dict, 0, mainLoop); + if (!_CFAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&runLoops)) { + CFRelease(dict); + CFRelease(mainLoop); + } + __CFSpinLock(&loopsLock); + } + if (_CFThreadIsMain() && _CFThreadsAreEqual(t, _CFThreadSelf())) { + t = kNilThreadT; + } + loop = (CFRunLoopRef)CFDictionaryGetValue(runLoops, pthreadPointer(t)); + if (!loop) { + __CFSpinUnlock(&loopsLock); + CFRunLoopRef newLoop = __CFRunLoopCreate(); + __CFGetThreadSpecificData(); // just cause the thread finalizer to be called as a side effect + __CFSpinLock(&loopsLock); + loop = (CFRunLoopRef)CFDictionaryGetValue(runLoops, pthreadPointer(t)); + if (loop) { + CFRelease(newLoop); + } else { + CFDictionarySetValue(runLoops, pthreadPointer(t), newLoop); + loop = newLoop; + } + } + if (!setMainLoop && _CFThreadIsMain()) { + if (_CFThreadsAreEqual(t, kNilThreadT)) { + CFDictionarySetValue(runLoops, pthreadPointer(_CFThreadSelf()), loop); + } else { + CFRunLoopRef mainLoop = (CFRunLoopRef)CFDictionaryGetValue(runLoops, pthreadPointer(kNilThreadT)); + CFDictionarySetValue(runLoops, pthreadPointer(_CFThreadSelf()), mainLoop); + } + setMainLoop = 1; + } + __CFSpinUnlock(&loopsLock); + return loop; +} + +__private_extern__ void _CFRunLoop1(void) { + __CFSpinLock(&loopsLock); + if (runLoops) { + NativeThread t = _CFThreadSelf(); + CFRunLoopRef currentLoop = (CFRunLoopRef)CFDictionaryGetValue(runLoops, pthreadPointer(t)); + CFRunLoopRef mainLoop = (CFRunLoopRef)CFDictionaryGetValue(runLoops, pthreadPointer(kNilThreadT)); + if (currentLoop && mainLoop != currentLoop) { + CFDictionaryRemoveValue(runLoops, pthreadPointer(t)); + CFRelease(currentLoop); + } + } + __CFSpinUnlock(&loopsLock); +} + +CFRunLoopRef CFRunLoopGetMain(void) { + CHECK_FOR_FORK(); + return _CFRunLoop0(kNilThreadT); +} + +CFRunLoopRef CFRunLoopGetCurrent(void) { + CHECK_FOR_FORK(); + return _CFRunLoop0(_CFThreadSelf()); +} + +void _CFRunLoopSetCurrent(CFRunLoopRef rl) { + __CFSpinLock(&loopsLock); + CFRunLoopRef currentLoop = runLoops ? (CFRunLoopRef)CFDictionaryGetValue(runLoops, pthreadPointer(_CFThreadSelf())) : NULL; + if (rl != currentLoop) { + // intentionally leak currentLoop so we don't kill any ports in the child + // if (currentLoop) CFRelease(currentLoop); + if (rl) { + if (!runLoops) { + runLoops = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, NULL); + CFRunLoopRef mainLoop = __CFRunLoopCreate(); + CFDictionarySetValue(runLoops, pthreadPointer(kNilThreadT), mainLoop); + } + CFRetain(rl); + CFDictionarySetValue(runLoops, pthreadPointer(_CFThreadSelf()), rl); + } else { + CFDictionaryRemoveValue(runLoops, pthreadPointer(_CFThreadSelf())); + } + } + __CFSpinUnlock(&loopsLock); +} + +CFStringRef CFRunLoopCopyCurrentMode(CFRunLoopRef rl) { + CHECK_FOR_FORK(); + CFStringRef result = NULL; + __CFRunLoopLock(rl); + if (NULL != rl->_currentMode) { + result = (CFStringRef)CFRetain(rl->_currentMode->_name); + } + __CFRunLoopUnlock(rl); + return result; +} + +static void __CFRunLoopGetModeName(const void *value, void *context) { + CFRunLoopModeRef rlm = (CFRunLoopModeRef)value; + CFMutableArrayRef array = (CFMutableArrayRef)context; + CFArrayAppendValue(array, rlm->_name); +} + +CFArrayRef CFRunLoopCopyAllModes(CFRunLoopRef rl) { + CHECK_FOR_FORK(); + CFMutableArrayRef array; + __CFRunLoopLock(rl); + array = CFArrayCreateMutable(kCFAllocatorSystemDefault, CFSetGetCount(rl->_modes), &kCFTypeArrayCallBacks); + CFSetApplyFunction(rl->_modes, (__CFRunLoopGetModeName), array); + __CFRunLoopUnlock(rl); + return array; +} + +static void __CFRunLoopAddItemsToCommonMode(const void *value, void *ctx) { + CFTypeRef item = (CFTypeRef)value; + CFRunLoopRef rl = (CFRunLoopRef)(((CFTypeRef *)ctx)[0]); + CFStringRef modeName = (CFStringRef)(((CFTypeRef *)ctx)[1]); + if (CFGetTypeID(item) == __kCFRunLoopSourceTypeID) { + CFRunLoopAddSource(rl, (CFRunLoopSourceRef)item, modeName); + } else if (CFGetTypeID(item) == __kCFRunLoopObserverTypeID) { + CFRunLoopAddObserver(rl, (CFRunLoopObserverRef)item, modeName); + } else if (CFGetTypeID(item) == __kCFRunLoopTimerTypeID) { + CFRunLoopAddTimer(rl, (CFRunLoopTimerRef)item, modeName); + } +} + +static void __CFRunLoopAddItemToCommonModes(const void *value, void *ctx) { + CFStringRef modeName = (CFStringRef)value; + CFRunLoopRef rl = (CFRunLoopRef)(((CFTypeRef *)ctx)[0]); + CFTypeRef item = (CFTypeRef)(((CFTypeRef *)ctx)[1]); + if (CFGetTypeID(item) == __kCFRunLoopSourceTypeID) { + CFRunLoopAddSource(rl, (CFRunLoopSourceRef)item, modeName); + } else if (CFGetTypeID(item) == __kCFRunLoopObserverTypeID) { + CFRunLoopAddObserver(rl, (CFRunLoopObserverRef)item, modeName); + } else if (CFGetTypeID(item) == __kCFRunLoopTimerTypeID) { + CFRunLoopAddTimer(rl, (CFRunLoopTimerRef)item, modeName); + } +} + +static void __CFRunLoopRemoveItemFromCommonModes(const void *value, void *ctx) { + CFStringRef modeName = (CFStringRef)value; + CFRunLoopRef rl = (CFRunLoopRef)(((CFTypeRef *)ctx)[0]); + CFTypeRef item = (CFTypeRef)(((CFTypeRef *)ctx)[1]); + if (CFGetTypeID(item) == __kCFRunLoopSourceTypeID) { + CFRunLoopRemoveSource(rl, (CFRunLoopSourceRef)item, modeName); + } else if (CFGetTypeID(item) == __kCFRunLoopObserverTypeID) { + CFRunLoopRemoveObserver(rl, (CFRunLoopObserverRef)item, modeName); + } else if (CFGetTypeID(item) == __kCFRunLoopTimerTypeID) { + CFRunLoopRemoveTimer(rl, (CFRunLoopTimerRef)item, modeName); + } +} + +Boolean _CFRunLoop01(CFRunLoopRef rl, CFStringRef modeName) { + __CFRunLoopLock(rl); + Boolean present = CFSetContainsValue(rl->_commonModes, modeName); + __CFRunLoopUnlock(rl); + return present; +} + +void *_CFRunLoop02(CFRunLoopRef rl) { + return rl->_counterpart; +} + +void _CFRunLoop03(CFRunLoopRef rl, void *ns) { + rl->_counterpart = ns; +} + +void CFRunLoopAddCommonMode(CFRunLoopRef rl, CFStringRef modeName) { + CHECK_FOR_FORK(); + if (__CFRunLoopIsDeallocating(rl)) return; + __CFRunLoopLock(rl); + if (!CFSetContainsValue(rl->_commonModes, modeName)) { + CFSetRef set = rl->_commonModeItems ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModeItems) : NULL; + CFSetAddValue(rl->_commonModes, modeName); + __CFRunLoopUnlock(rl); + if (NULL != set) { + CFTypeRef context[2] = {rl, modeName}; + /* add all common-modes items to new mode */ + CFSetApplyFunction(set, (__CFRunLoopAddItemsToCommonMode), (void *)context); + CFRelease(set); + } + } else { + __CFRunLoopUnlock(rl); + } +} + +static CFComparisonResult __CFRunLoopObserverQSortComparator(const void *val1, const void *val2, void *context) { + CFRunLoopObserverRef o1 = *((CFRunLoopObserverRef *)val1); + CFRunLoopObserverRef o2 = *((CFRunLoopObserverRef *)val2); + if (!o1) { + return (!o2) ? kCFCompareEqualTo : kCFCompareLessThan; + } + if (!o2) { + return kCFCompareGreaterThan; + } + if (o1->_order < o2->_order) return kCFCompareLessThan; + if (o2->_order < o1->_order) return kCFCompareGreaterThan; + return kCFCompareEqualTo; +} + + +/* rl is unlocked, rlm is locked on entrance and exit */ +/* ALERT: this should collect all the candidate observers from the top level + * and all submodes, recursively, THEN start calling them, in order to obey + * the ordering parameter. */ +static void __CFRunLoopDoObservers(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopActivity activity) { /* DOES CALLOUT */ + CHECK_FOR_FORK(); + CFIndex idx, cnt; + CFArrayRef submodes; + + /* Fire the observers */ + submodes = (NULL != rlm->_submodes && 0 < CFArrayGetCount(rlm->_submodes)) ? CFArrayCreateCopy(kCFAllocatorSystemDefault, rlm->_submodes) : NULL; + if (NULL != rlm->_observers) { + cnt = CFSetGetCount(rlm->_observers); + if (0 < cnt) { +#if __GNUC__ + CFRunLoopObserverRef buffer[(cnt <= 1024) ? cnt : 1]; +#else + CFRunLoopObserverRef buffer[1024]; +#endif + CFRunLoopObserverRef *collectedObservers = (cnt <= 1024) ? buffer : (CFRunLoopObserverRef*)CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(CFRunLoopObserverRef), 0); + CFSetGetValues(rlm->_observers, (const void **)collectedObservers); + for (idx = 0; idx < cnt; idx++) { + CFRunLoopObserverRef rlo = collectedObservers[idx]; + if (0 != (rlo->_activities & activity) && __CFIsValid(rlo) && !__CFRunLoopObserverIsFiring(rlo)) { + CFRetain(rlo); + } else { + /* We're not interested in this one - set it to NULL so we don't process it later */ + collectedObservers[idx] = NULL; + } + } + __CFRunLoopModeUnlock(rlm); + CFQSortArray(collectedObservers, cnt, sizeof(CFRunLoopObserverRef), __CFRunLoopObserverQSortComparator, NULL); + for (idx = 0; idx < cnt; idx++) { + CFRunLoopObserverRef rlo = collectedObservers[idx]; + if (rlo) { + __CFRunLoopObserverLock(rlo); + if (__CFIsValid(rlo)) { + __CFRunLoopObserverUnlock(rlo); + __CFRunLoopObserverSetFiring(rlo); + rlo->_callout(rlo, activity, rlo->_context.info); /* CALLOUT */ + __CFRunLoopObserverUnsetFiring(rlo); + if (!__CFRunLoopObserverRepeats(rlo)) { + CFRunLoopObserverInvalidate(rlo); + } + } else { + __CFRunLoopObserverUnlock(rlo); + } + CFRelease(rlo); + } + } + __CFRunLoopModeLock(rlm); + if (collectedObservers != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, collectedObservers); + } + } + if (NULL != submodes) { + __CFRunLoopModeUnlock(rlm); + for (idx = 0, cnt = CFArrayGetCount(submodes); idx < cnt; idx++) { + CFStringRef modeName = (CFStringRef)CFArrayGetValueAtIndex(submodes, idx); + CFRunLoopModeRef subrlm; + __CFRunLoopLock(rl); + subrlm = __CFRunLoopFindMode(rl, modeName, false); + __CFRunLoopUnlock(rl); + if (NULL != subrlm) { + __CFRunLoopDoObservers(rl, subrlm, activity); + __CFRunLoopModeUnlock(subrlm); + } + } + CFRelease(submodes); + __CFRunLoopModeLock(rlm); + } +} + +static CFComparisonResult __CFRunLoopSourceComparator(const void *val1, const void *val2, void *context) { + CFRunLoopSourceRef o1 = (CFRunLoopSourceRef)val1; + CFRunLoopSourceRef o2 = (CFRunLoopSourceRef)val2; + if (o1->_order < o2->_order) return kCFCompareLessThan; + if (o2->_order < o1->_order) return kCFCompareGreaterThan; + return kCFCompareEqualTo; +} + +static void __CFRunLoopCollectSources0(const void *value, void *context) { + CFRunLoopSourceRef rls = (CFRunLoopSourceRef)value; + CFTypeRef *sources = (CFTypeRef *)context; + if (0 == rls->_context.version0.version && __CFIsValid(rls) && __CFRunLoopSourceIsSignaled(rls)) { + if (NULL == *sources) { + *sources = CFRetain(rls); + } else if (CFGetTypeID(*sources) == __kCFRunLoopSourceTypeID) { + CFTypeRef oldrls = *sources; + *sources = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); + CFArrayAppendValue((CFMutableArrayRef)*sources, oldrls); + CFArrayAppendValue((CFMutableArrayRef)*sources, rls); + CFRelease(oldrls); + } else { + CFArrayAppendValue((CFMutableArrayRef)*sources, rls); + } + } +} + +/* rl is unlocked, rlm is locked on entrance and exit */ +static Boolean __CFRunLoopDoSources0(CFRunLoopRef rl, CFRunLoopModeRef rlm, Boolean stopAfterHandle) { /* DOES CALLOUT */ + CHECK_FOR_FORK(); + CFTypeRef sources = NULL; + Boolean sourceHandled = false; + CFIndex idx, cnt; + + __CFRunLoopModeUnlock(rlm); // locks have to be taken in order + __CFRunLoopLock(rl); + __CFRunLoopModeLock(rlm); + /* Fire the version 0 sources */ + if (NULL != rlm->_sources && 0 < CFSetGetCount(rlm->_sources)) { + CFSetApplyFunction(rlm->_sources, (__CFRunLoopCollectSources0), &sources); + } + for (idx = 0, cnt = (NULL != rlm->_submodes) ? CFArrayGetCount(rlm->_submodes) : 0; idx < cnt; idx++) { + CFStringRef modeName = (CFStringRef)CFArrayGetValueAtIndex(rlm->_submodes, idx); + CFRunLoopModeRef subrlm; + subrlm = __CFRunLoopFindMode(rl, modeName, false); + if (NULL != subrlm) { + if (NULL != subrlm->_sources && 0 < CFSetGetCount(subrlm->_sources)) { + CFSetApplyFunction(subrlm->_sources, (__CFRunLoopCollectSources0), &sources); + } + __CFRunLoopModeUnlock(subrlm); + } + } + __CFRunLoopUnlock(rl); + if (NULL != sources) { + // sources is either a single (retained) CFRunLoopSourceRef or an array of (retained) CFRunLoopSourceRef + __CFRunLoopModeUnlock(rlm); + if (CFGetTypeID(sources) == __kCFRunLoopSourceTypeID) { + CFRunLoopSourceRef rls = (CFRunLoopSourceRef)sources; + __CFRunLoopSourceLock(rls); + __CFRunLoopSourceUnsetSignaled(rls); + if (__CFIsValid(rls)) { + __CFRunLoopSourceUnlock(rls); + if (NULL != rls->_context.version0.perform) { + rls->_context.version0.perform(rls->_context.version0.info); /* CALLOUT */ + CHECK_FOR_FORK(); + } + sourceHandled = true; + } else { + __CFRunLoopSourceUnlock(rls); + } + } else { + cnt = CFArrayGetCount((CFArrayRef)sources); + CFArraySortValues((CFMutableArrayRef)sources, CFRangeMake(0, cnt), (__CFRunLoopSourceComparator), NULL); + for (idx = 0; idx < cnt; idx++) { + CFRunLoopSourceRef rls = (CFRunLoopSourceRef)CFArrayGetValueAtIndex((CFArrayRef)sources, idx); + __CFRunLoopSourceLock(rls); + __CFRunLoopSourceUnsetSignaled(rls); + if (__CFIsValid(rls)) { + __CFRunLoopSourceUnlock(rls); + if (NULL != rls->_context.version0.perform) { + rls->_context.version0.perform(rls->_context.version0.info); /* CALLOUT */ + CHECK_FOR_FORK(); + } + sourceHandled = true; + } else { + __CFRunLoopSourceUnlock(rls); + } + if (stopAfterHandle && sourceHandled) { + break; + } + } + } + CFRelease(sources); + __CFRunLoopModeLock(rlm); + } + return sourceHandled; +} + +// msg, size and reply are unused on Windows +static Boolean __CFRunLoopDoSource1(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopSourceRef rls +#if DEPLOYMENT_TARGET_MACOSX + , mach_msg_header_t *msg, CFIndex size, mach_msg_header_t **reply +#endif + ) { /* DOES CALLOUT */ + CHECK_FOR_FORK(); + Boolean sourceHandled = false; + + /* Fire a version 1 source */ + CFRetain(rls); + __CFRunLoopModeUnlock(rlm); + __CFRunLoopSourceLock(rls); + if (__CFIsValid(rls)) { + __CFRunLoopSourceUnsetSignaled(rls); + __CFRunLoopSourceUnlock(rls); + if (NULL != rls->_context.version1.perform) { +#if DEPLOYMENT_TARGET_MACOSX + *reply = (mach_msg_header_t*)rls->_context.version1.perform(msg, size, kCFAllocatorSystemDefault, rls->_context.version1.info); /* CALLOUT */ + CHECK_FOR_FORK(); +#else + if (_LogCFRunLoop) { CFLog(kCFLogLevelDebug, CFSTR("%p (%s) __CFRunLoopDoSource1 performing rls %p"), CFRunLoopGetCurrent(), *_CFGetProgname(), rls); } + rls->_context.version1.perform(rls->_context.version1.info); /* CALLOUT */ + CHECK_FOR_FORK(); +#endif + } else { + if (_LogCFRunLoop) { CFLog(kCFLogLevelDebug, CFSTR("%p (%s) __CFRunLoopDoSource1 perform is NULL"), CFRunLoopGetCurrent(), *_CFGetProgname()); } + } + sourceHandled = true; + } else { + if (_LogCFRunLoop) { CFLog(kCFLogLevelDebug, CFSTR("%p (%s) __CFRunLoopDoSource1 rls %p is invalid"), CFRunLoopGetCurrent(), *_CFGetProgname(), rls); } + __CFRunLoopSourceUnlock(rls); + } + CFRelease(rls); + __CFRunLoopModeLock(rlm); + return sourceHandled; +} + +static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopTimerRef rlt) { /* DOES CALLOUT */ + Boolean timerHandled = false; + int64_t oldFireTSR = 0; + + /* Fire a timer */ + CFRetain(rlt); + __CFRunLoopModeUnlock(rlm); + __CFRunLoopTimerLock(rlt); + if (__CFIsValid(rlt) && !__CFRunLoopTimerIsFiring(rlt)) { + __CFRunLoopTimerUnsetDidFire(rlt); + __CFRunLoopTimerSetFiring(rlt); + __CFRunLoopTimerUnlock(rlt); + __CFRunLoopTimerFireTSRLock(); + oldFireTSR = rlt->_fireTSR; + __CFRunLoopTimerFireTSRUnlock(); + rlt->_callout(rlt, rlt->_context.info); /* CALLOUT */ + CHECK_FOR_FORK(); + __CFRunLoopTimerUnsetFiring(rlt); + timerHandled = true; + } else { + // If the timer fires while it is firing in a higher activiation, + // it is not allowed to fire, but we have to remember that fact. + // Later, if the timer's fire date is being handled manually, we + // need to re-arm the kernel timer, since it has possibly already + // fired (this firing which is being skipped, say) and the timer + // will permanently stop if we completely drop this firing. + if (__CFRunLoopTimerIsFiring(rlt)) __CFRunLoopTimerSetDidFire(rlt); + __CFRunLoopTimerUnlock(rlt); + } + if (__CFIsValid(rlt) && timerHandled) { + if (0 == rlt->_intervalTSR) { + CFRunLoopTimerInvalidate(rlt); /* DOES CALLOUT */ + } else { + /* This is just a little bit tricky: we want to support calling + * CFRunLoopTimerSetNextFireDate() from within the callout and + * honor that new time here if it is a later date, otherwise + * it is completely ignored. */ + int64_t currentFireTSR; + __CFRunLoopTimerFireTSRLock(); + currentFireTSR = rlt->_fireTSR; + if (oldFireTSR < currentFireTSR) { + /* Next fire TSR was set, and set to a date after the previous + * fire date, so we honor it. */ + if (__CFRunLoopTimerDidFire(rlt)) { + __CFRunLoopTimerRescheduleWithAllModes(rlt, rl); + __CFRunLoopTimerUnsetDidFire(rlt); + } + } else { + if ((uint64_t)LLONG_MAX <= (uint64_t)oldFireTSR + (uint64_t)rlt->_intervalTSR) { + currentFireTSR = LLONG_MAX; + } else { + int64_t currentTSR = (int64_t)__CFReadTSR(); + currentFireTSR = oldFireTSR; + while (currentFireTSR <= currentTSR) { + currentFireTSR += rlt->_intervalTSR; + } + } + rlt->_fireTSR = currentFireTSR; + __CFRunLoopTimerRescheduleWithAllModes(rlt, rl); + } + __CFRunLoopTimerFireTSRUnlock(); + } + } + CFRelease(rlt); + __CFRunLoopModeLock(rlm); + return timerHandled; +} + +CF_EXPORT Boolean _CFRunLoopFinished(CFRunLoopRef rl, CFStringRef modeName) { + CHECK_FOR_FORK(); + CFRunLoopModeRef rlm; + Boolean result = false; + __CFRunLoopLock(rl); + rlm = __CFRunLoopFindMode(rl, modeName, false); + if (NULL == rlm || __CFRunLoopModeIsEmpty(rl, rlm)) { + result = true; + } + __CFRunLoopUnlock(rl); + if (rlm) __CFRunLoopModeUnlock(rlm); + return result; +} + +// rl is locked, rlm is locked on entry and exit +static void __CFRunLoopModeAddPortsToPortSet(CFRunLoopRef rl, CFRunLoopModeRef rlm, __CFPortSet portSet) { + CFIndex idx, cnt; + const void **list, *buffer[256]; + + // Timers and version 1 sources go into the portSet currently + if (NULL != rlm->_sources) { + cnt = CFSetGetCount(rlm->_sources); + list = (cnt <= 256) ? buffer : (const void**)CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(void *), 0); + CFSetGetValues(rlm->_sources, list); + for (idx = 0; idx < cnt; idx++) { + CFRunLoopSourceRef rls = (CFRunLoopSourceRef)list[idx]; + if (1 == rls->_context.version0.version) { + __CFPort port = rls->_context.version1.getPort(rls->_context.version1.info); /* CALLOUT */ + if (CFPORT_NULL != port) { + __CFPortSetInsert(port, portSet); + } + } + } + if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); + } +#if DEPLOYMENT_TARGET_MACOSX + if (NULL != rlm->_timers) { + cnt = CFSetGetCount(rlm->_timers); + list = (cnt <= 256) ? buffer : (const void**)CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(void *), 0); + CFSetGetValues(rlm->_timers, list); + for (idx = 0; idx < cnt; idx++) { + CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)list[idx]; + if (MACH_PORT_NULL != rlt->_port) { + mach_port_insert_member(mach_task_self(), rlt->_port, portSet); + } + } + if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); + } +#endif + // iterate over submodes + for (idx = 0, cnt = NULL != rlm->_submodes ? CFArrayGetCount(rlm->_submodes) : 0; idx < cnt; idx++) { + CFStringRef modeName = (CFStringRef)CFArrayGetValueAtIndex(rlm->_submodes, idx); + CFRunLoopModeRef subrlm; + subrlm = __CFRunLoopFindMode(rl, modeName, false); + if (NULL != subrlm) { + __CFRunLoopModeAddPortsToPortSet(rl, subrlm, portSet); + __CFRunLoopModeUnlock(subrlm); + } + } +} + +static __CFPortSet _LastMainWaitSet = CFPORTSET_NULL; + +// return NO if we're the main runloop and there are no messages waiting on the port set +int _CFRunLoopInputsReady(void) { + CHECK_FOR_FORK(); + // XXX_PCB: the following 2 lines aren't safe to call during GC, because another + // thread may have entered CFRunLoopGetMain(), which grabs a spink lock, and then + // is suspended by the GC. We can check for the main thread more directly + // by calling pthread_main_np(). + // CFRunLoopRef current = CFRunLoopGetMain() + // if (current != CFRunLoopGetMain()) return true; + if (!_CFThreadIsMain()) return true; + + // XXX_PCB: can't be any messages waiting if the wait set is NULL. + if (_LastMainWaitSet == CFPORTSET_NULL) return false; + +#if DEPLOYMENT_TARGET_MACOSX + // prepare a message header with no space for any data, nor a trailer + mach_msg_header_t msg; + msg.msgh_size = sizeof(msg); // just the header, ma'am + // need the waitset, actually XXX + msg.msgh_local_port = _LastMainWaitSet; + msg.msgh_remote_port = MACH_PORT_NULL; + msg.msgh_id = 0; + + kern_return_t ret = mach_msg(&msg, MACH_RCV_MSG | MACH_RCV_TIMEOUT | MACH_RCV_LARGE, 0, msg.msgh_size, _LastMainWaitSet, 0, MACH_PORT_NULL); + + return (MACH_RCV_TOO_LARGE == ret); +#endif + return true; +} + +#if 0 +static void print_msg_scan_header(void) { + printf("======== ======== ======== ========\n"); + printf("description\tport\tport type\t\treferences\n"); +} + +static void print_one_port_info(const char *desc, mach_port_t port, mach_msg_type_name_t type) { + mach_port_urefs_t refs; + kern_return_t ret = mach_port_get_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, &refs); + if (ret != KERN_SUCCESS) refs = 0; + const char *type_name = "???"; + switch (type) { + case MACH_MSG_TYPE_MOVE_SEND: type_name = "MACH_MSG_TYPE_MOVE_SEND"; break; + case MACH_MSG_TYPE_MOVE_SEND_ONCE: type_name = "MACH_MSG_TYPE_MOVE_SEND_ONCE"; break; + case MACH_MSG_TYPE_MOVE_RECEIVE: type_name = "MACH_MSG_TYPE_MOVE_RECEIVE"; break; + case MACH_MSG_TYPE_MAKE_SEND: type_name = "MACH_MSG_TYPE_MAKE_SEND"; break; + case MACH_MSG_TYPE_MAKE_SEND_ONCE: type_name = "MACH_MSG_TYPE_MAKE_SEND_ONCE"; break; + } + printf("%s\t%p\t%-20s\t%u\n", desc, port, type_name, refs); +} + +static void mach_msg_scan(mach_msg_header_t *msg, int clean) { + Boolean printed_header = false; + /* + * The msgh_local_port field doesn't hold a port right. + * The receive operation consumes the destination port right. + */ + if (MACH_PORT_NULL != msg->msgh_remote_port) { + if (! printed_header) print_msg_scan_header(); + printed_header = true; + print_one_port_info("msg->msgh_remote_port", msg->msgh_remote_port, MACH_MSGH_BITS_REMOTE(msg->msgh_bits)); + } + if (msg->msgh_bits & MACH_MSGH_BITS_COMPLEX) { + mach_msg_body_t *body = (mach_msg_body_t *) (msg + 1); + mach_msg_descriptor_t *saddr = (mach_msg_descriptor_t *) ((mach_msg_base_t *) msg + 1); + mach_msg_descriptor_t *eaddr = saddr + body->msgh_descriptor_count; + for ( ; saddr < eaddr; saddr++) { + switch (saddr->type.type) { + case MACH_MSG_PORT_DESCRIPTOR:; + mach_msg_port_descriptor_t *dsc = &saddr->port; + if (! printed_header) print_msg_scan_header(); + printed_header = true; + print_one_port_info("port in body", dsc->name, dsc->disposition); +// if (clean) mach_port_deallocate(mach_task_self(), dsc->name); + break; + case MACH_MSG_OOL_PORTS_DESCRIPTOR:; + mach_msg_ool_ports_descriptor_t *dsc2 = &saddr->ool_ports; + mach_port_t *ports = (mach_port_t *) dsc2->address; + for (mach_msg_type_number_t j = 0; j < dsc2->count; j++, ports++) { + if (! printed_header) print_msg_scan_header(); + printed_header = true; + print_one_port_info("port in OOL ports", *ports, dsc2->disposition); + } + break; + } + } + } +} +#endif + +/* rl is unlocked, rlm locked on entrance and exit */ +static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, Boolean waitIfEmpty) { /* DOES CALLOUT */ + int64_t termTSR; +#if DEPLOYMENT_TARGET_MACOSX + mach_port_name_t timeoutPort = MACH_PORT_NULL; + Boolean timeoutPortAdded = false; +#endif + Boolean poll = false; + Boolean firstPass = true; + + if (__CFRunLoopIsStopped(rl)) { + return kCFRunLoopRunStopped; + } else if (rlm->_stopped) { + rlm->_stopped = false; + return kCFRunLoopRunStopped; + } + if (seconds <= 0.0) { + termTSR = 0; + } else if (3.1556952e+9 < seconds) { + termTSR = LLONG_MAX; + } else { + termTSR = (int64_t)__CFReadTSR() + __CFTimeIntervalToTSR(seconds); +#if DEPLOYMENT_TARGET_MACOSX + timeoutPort = mk_timer_create(); + mk_timer_arm(timeoutPort, __CFUInt64ToAbsoluteTime(termTSR)); +#endif + } + if (seconds <= 0.0) { + poll = true; + } + if (rl == _CFRunLoop0(kNilThreadT)) _LastMainWaitSet = CFPORTSET_NULL; + for (;;) { + __CFPortSet waitSet = CFPORTSET_NULL; + waitSet = CFPORTSET_NULL; + Boolean destroyWaitSet = false; + CFRunLoopSourceRef rls; +#if DEPLOYMENT_TARGET_MACOSX + mach_msg_header_t *msg; + kern_return_t ret; + uint8_t buffer[1024 + 80] = {0}; // large enough for 1k of inline payload; must be zeroed for GC +#elif DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX + CFArrayRef timersToCall = NULL; +#endif + int32_t returnValue = 0; + Boolean sourceHandledThisLoop = false; + + __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers); + __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources); + + sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle); + + if (sourceHandledThisLoop) { + poll = true; + } + + if (!poll) { + __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting); + __CFRunLoopSetSleeping(rl); + } + if (NULL != rlm->_submodes) { + // !!! what do we do if this doesn't succeed? + waitSet = __CFPortSetAllocate(); + if (CFPORTSET_NULL == waitSet) HALT; + __CFRunLoopModeUnlock(rlm); + __CFRunLoopLock(rl); + __CFRunLoopModeLock(rlm); + __CFRunLoopModeAddPortsToPortSet(rl, rlm, waitSet); + __CFRunLoopUnlock(rl); +#if DEPLOYMENT_TARGET_MACOSX + if (CFPORT_NULL != timeoutPort) { + __CFPortSetInsert(timeoutPort, waitSet); + } +#endif + destroyWaitSet = true; + } else { + waitSet = rlm->_portSet; +#if DEPLOYMENT_TARGET_MACOSX + if (!timeoutPortAdded && CFPORT_NULL != timeoutPort) { + __CFPortSetInsert(timeoutPort, waitSet); + timeoutPortAdded = true; + } +#endif + } + if (rl == _CFRunLoop0(kNilThreadT)) _LastMainWaitSet = waitSet; + __CFRunLoopModeUnlock(rlm); + +#if DEPLOYMENT_TARGET_MACOSX + msg = (mach_msg_header_t *)buffer; + msg->msgh_size = sizeof(buffer); + + /* In that sleep of death what nightmares may come ... */ +try_receive: + msg->msgh_bits = 0; + msg->msgh_local_port = waitSet; + msg->msgh_remote_port = MACH_PORT_NULL; + msg->msgh_id = 0; + ret = mach_msg(msg, MACH_RCV_MSG|MACH_RCV_LARGE|(poll ? MACH_RCV_TIMEOUT : 0)|MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)|MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT), 0, msg->msgh_size, waitSet, 0, MACH_PORT_NULL); + if (MACH_RCV_TOO_LARGE == ret) { + uint32_t newSize = round_msg(msg->msgh_size) + sizeof(mach_msg_audit_trailer_t); + if (msg == (mach_msg_header_t *)buffer) msg = NULL; + msg = (mach_msg_header_t*)CFAllocatorReallocate(kCFAllocatorSystemDefault, msg, newSize, 0); + msg->msgh_size = newSize; + goto try_receive; + } else if (MACH_RCV_TIMED_OUT == ret) { + // timeout, for poll + if (msg != (mach_msg_header_t *)buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, msg); + msg = NULL; + } else if (MACH_MSG_SUCCESS != ret) { + HALT; + } +#elif DEPLOYMENT_TARGET_WINDOWS + DWORD waitResult = WAIT_TIMEOUT; + __CFPort portBuf[MAX_PORTS]; + __CFPort *ports; + CFIndex portCount; + Boolean freePorts; + if (destroyWaitSet) { + // wait set is a local, no one else could modify it, no need to copy handles + ports = waitSet->ports; + portCount = waitSet->used; + freePorts = false; + } else { + // copy out the ports to be safe from other threads at work + ports = __CFPortSetGetPorts(waitSet, portBuf, MAX_PORTS, (uint32_t*)&portCount); + freePorts = (ports != portBuf); + } + // should msgQMask be an OR'ing of this and all submodes' masks? + if (0 == GetQueueStatus(rlm->_msgQMask)) { + DWORD timeout; + if (poll) + timeout = 0; + else { + __CFRunLoopModeLock(rlm); + int64_t nextStop = __CFRunLoopGetNextTimerFireTSR(rl, rlm); + if (nextStop <= 0) + nextStop = termTSR; + else if (nextStop > termTSR) + nextStop = termTSR; + // else the next stop is dictated by the next timer + int64_t timeoutTSR = nextStop - __CFReadTSR(); + if (timeoutTSR < 0) + timeout = 0; + else { + CFTimeInterval timeoutCF = __CFTSRToTimeInterval(timeoutTSR) * 1000; + if (timeoutCF > MAXDWORD) + timeout = INFINITE; + else + timeout = (DWORD)timeoutCF; + } + } + if (_LogCFRunLoop) { CFLog(kCFLogLevelDebug, CFSTR("%p (%s)- about to wait for %d objects, wakeupport is %p"), CFRunLoopGetCurrent(), *_CFGetProgname(), portCount, rl->_wakeUpPort); } + if (_LogCFRunLoop) { CFLog(kCFLogLevelDebug, CFSTR("All RLM sources = %@"), rlm->_sources); } + waitResult = MsgWaitForMultipleObjects(__CFMin(portCount, MAX_PORTS), ports, false, timeout, rlm->_msgQMask); + if (_LogCFRunLoop) { CFLog(kCFLogLevelDebug, CFSTR("%p (%s)- waitResult was %d"), CFRunLoopGetCurrent(), *_CFGetProgname(), waitResult); } + } + ResetEvent(rl->_wakeUpPort); +#elif DEPLOYMENT_TARGET_LINUX + // XXX - More to fill in here. +#endif + if (destroyWaitSet) { + __CFPortSetFree(waitSet); + if (rl == _CFRunLoop0(kNilThreadT)) _LastMainWaitSet = CFPORTSET_NULL; + } + __CFRunLoopLock(rl); + __CFRunLoopModeLock(rlm); + __CFRunLoopUnlock(rl); + if (!poll) { + __CFRunLoopUnsetSleeping(rl); + __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting); + } + poll = false; + __CFRunLoopModeUnlock(rlm); + __CFRunLoopLock(rl); + __CFRunLoopModeLock(rlm); + + __CFPort livePort = CFPORT_NULL; +#if DEPLOYMENT_TARGET_MACOSX + if (NULL != msg) { + livePort = msg->msgh_local_port; + } +#elif DEPLOYMENT_TARGET_WINDOWS + CFAssert2(waitResult != WAIT_FAILED, __kCFLogAssertion, "%s(): error %d from MsgWaitForMultipleObjects", __PRETTY_FUNCTION__, GetLastError()); + if (waitResult == WAIT_TIMEOUT) { + // do nothing, just return to caller + } else if (waitResult >= WAIT_OBJECT_0 && waitResult < WAIT_OBJECT_0+portCount) { + // a port was signalled + livePort = ports[waitResult-WAIT_OBJECT_0]; + if (_LogCFRunLoop) { CFLog(kCFLogLevelDebug, CFSTR("%p (%s)- Resetting event %p"), CFRunLoopGetCurrent(), *_CFGetProgname(), livePort); } + } else if (waitResult == WAIT_OBJECT_0+portCount) { + // windows message received - the CFWindowsMessageQueue will pick this up when + // the v0 RunLoopSources get their chance + } else if (waitResult >= WAIT_ABANDONED_0 && waitResult < WAIT_ABANDONED_0+portCount) { + // an "abandoned mutex object" + livePort = ports[waitResult-WAIT_ABANDONED_0]; + } else { + CFAssert2(waitResult == WAIT_FAILED, __kCFLogAssertion, "%s(): unexpected result from MsgWaitForMultipleObjects: %d", __PRETTY_FUNCTION__, waitResult); + } + if (freePorts) + CFAllocatorDeallocate(kCFAllocatorSystemDefault, ports); + timersToCall = __CFRunLoopTimersToFire(rl, rlm); +#elif DEPLOYMENT_TARGET_LINUX + // XXX - More to fill in here. + timersToCall = __CFRunLoopTimersToFire(rl, rlm); +#endif + + if (CFPORT_NULL == livePort) { + __CFRunLoopUnlock(rl); + } else if (livePort == rl->_wakeUpPort) { + // wakeup + if (_LogCFRunLoop) { CFLog(kCFLogLevelDebug, CFSTR("wakeupPort was signalled")); } + __CFRunLoopUnlock(rl); + } +#if DEPLOYMENT_TARGET_MACOSX + else if (livePort == timeoutPort) { + returnValue = kCFRunLoopRunTimedOut; + __CFRunLoopUnlock(rl); + } else if (NULL != (rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort))) { + mach_msg_header_t *reply = NULL; + __CFRunLoopUnlock(rl); +// mach_msg_scan(msg, 0); + if (__CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply)) { + sourceHandledThisLoop = true; + } +// mach_msg_scan(msg, 1); + if (NULL != reply) { + ret = mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL); +//#warning CF: what should be done with the return value? + CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply); + } + } else { + CFRunLoopTimerRef rlt; + rlt = __CFRunLoopModeFindTimerForMachPort(rlm, livePort); + __CFRunLoopUnlock(rl); + if (NULL != rlt) { + __CFRunLoopDoTimer(rl, rlm, rlt); + } + } + if (msg != (mach_msg_header_t *)buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, msg); +#else + else if (NULL != (rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort))) { + __CFRunLoopUnlock(rl); + if (_LogCFRunLoop) { CFLog(kCFLogLevelDebug, CFSTR("Source %@ was signalled"), rls); } + if (__CFRunLoopDoSource1(rl, rlm, rls)) { + sourceHandledThisLoop = true; + } + } +#endif + +#if DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX + if (NULL != timersToCall) { + CFIndex i; + for (i = CFArrayGetCount(timersToCall) - 1; i >= 0; i--) { + CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(timersToCall, i); + if (NULL != rlt) { + __CFRunLoopDoTimer(rl, rlm, rlt); + } + } + CFRelease(timersToCall); + } +#endif // DEPLOYMENT_TARGER_WINDOWS || DEPLOYMENT_TARGET_LINUX + + __CFRunLoopModeUnlock(rlm); // locks must be taken in order + __CFRunLoopLock(rl); + __CFRunLoopModeLock(rlm); + if (sourceHandledThisLoop && stopAfterHandle) { + returnValue = kCFRunLoopRunHandledSource; + // If we're about to timeout, but we just did a zero-timeout poll that only found our own + // internal wakeup signal on the first look at the portset, we'll go around the loop one + // more time, so as not to starve a v1 source that was just added along with a runloop wakeup. + } else if (0 != returnValue || (uint64_t)termTSR <= __CFReadTSR()) { + returnValue = kCFRunLoopRunTimedOut; + } else if (__CFRunLoopIsStopped(rl)) { + returnValue = kCFRunLoopRunStopped; + } else if (rlm->_stopped) { + rlm->_stopped = false; + returnValue = kCFRunLoopRunStopped; + } else if (!waitIfEmpty && __CFRunLoopModeIsEmpty(rl, rlm)) { + returnValue = kCFRunLoopRunFinished; + } + __CFRunLoopUnlock(rl); + if (0 != returnValue) { +#if DEPLOYMENT_TARGET_MACOSX + if (MACH_PORT_NULL != timeoutPort) { + if (!destroyWaitSet) __CFPortSetRemove(timeoutPort, waitSet); + mk_timer_destroy(timeoutPort); + } +#endif + return returnValue; + } + firstPass = false; + } +} + +SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */ + CHECK_FOR_FORK(); + if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished; + __CFRunLoopLock(rl); + CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false); + if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode)) { + if (currentMode) __CFRunLoopModeUnlock(currentMode); + __CFRunLoopUnlock(rl); + return kCFRunLoopRunFinished; + } + uint32_t *previousStopped = (uint32_t *)rl->_stopped; + rl->_stopped = (volatile uint32_t*)CFAllocatorAllocate(kCFAllocatorSystemDefault, 4 * sizeof(uint32_t), 0); + rl->_stopped[0] = 0x4346524C; + rl->_stopped[1] = 0x4346524C; // 'CFRL' + rl->_stopped[2] = 0x00000000; // here the value is stored + rl->_stopped[3] = 0x4346524C; + CFRunLoopModeRef previousMode = rl->_currentMode; + rl->_currentMode = currentMode; + __CFRunLoopUnlock(rl); + int32_t result; + __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry); + result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, false); + __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit); + __CFRunLoopModeUnlock(currentMode); + __CFRunLoopLock(rl); + CFAllocatorDeallocate(kCFAllocatorSystemDefault, (uint32_t *)rl->_stopped); + rl->_stopped = previousStopped; + rl->_currentMode = previousMode; + __CFRunLoopUnlock(rl); + return result; +} + +void CFRunLoopRun(void) { /* DOES CALLOUT */ + int32_t result; + do { + result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false); + CHECK_FOR_FORK(); + } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result); +} + +SInt32 CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */ + CHECK_FOR_FORK(); + return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled); +} + +static void __CFRunLoopFindMinTimer(const void *value, void *ctx) { + CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)value; + if (__CFIsValid(rlt)) { + CFRunLoopTimerRef *result = (CFRunLoopTimerRef*)ctx; + if (NULL == *result || rlt->_fireTSR < (*result)->_fireTSR) { + *result = rlt; + } + } +} + +static int64_t __CFRunLoopGetNextTimerFireTSR(CFRunLoopRef rl, CFRunLoopModeRef rlm) { + CFRunLoopTimerRef result = NULL; + int64_t fireTime = 0; + if (rlm) { + if (NULL != rlm->_timers && 0 < CFSetGetCount(rlm->_timers)) { + __CFRunLoopTimerFireTSRLock(); + CFSetApplyFunction(rlm->_timers, (__CFRunLoopFindMinTimer), &result); + if (result) + fireTime = result->_fireTSR; + __CFRunLoopTimerFireTSRUnlock(); + } + if (NULL != rlm->_submodes) { + CFIndex idx, cnt; + for (idx = 0, cnt = CFArrayGetCount(rlm->_submodes); idx < cnt; idx++) { + CFStringRef modeName = (CFStringRef)CFArrayGetValueAtIndex(rlm->_submodes, idx); + CFRunLoopModeRef subrlm; + subrlm = __CFRunLoopFindMode(rl, modeName, false); + if (NULL != subrlm) { + int64_t newFireTime = __CFRunLoopGetNextTimerFireTSR(rl, subrlm); + __CFRunLoopModeUnlock(subrlm); + if (fireTime == 0 || (newFireTime != 0 && newFireTime < fireTime)) + fireTime = newFireTime; + } + } + } + __CFRunLoopModeUnlock(rlm); + } + return fireTime; +} + +CFAbsoluteTime CFRunLoopGetNextTimerFireDate(CFRunLoopRef rl, CFStringRef modeName) { + CHECK_FOR_FORK(); + CFRunLoopModeRef rlm; + int64_t fireTSR; + __CFRunLoopLock(rl); + rlm = __CFRunLoopFindMode(rl, modeName, false); + __CFRunLoopUnlock(rl); + fireTSR = __CFRunLoopGetNextTimerFireTSR(rl, rlm); + int64_t now2 = __CFReadTSR(); + CFAbsoluteTime now1 = CFAbsoluteTimeGetCurrent(); + return (0 == fireTSR) ? 0.0 : (now1 + __CFTSRToTimeInterval(fireTSR - now2)); +} + +Boolean CFRunLoopIsWaiting(CFRunLoopRef rl) { + CHECK_FOR_FORK(); + return __CFRunLoopIsSleeping(rl); +} + +void CFRunLoopWakeUp(CFRunLoopRef rl) { + CHECK_FOR_FORK(); +#if DEPLOYMENT_TARGET_MACOSX + kern_return_t ret; + /* We unconditionally try to send the message, since we don't want + * to lose a wakeup, but the send may fail if there is already a + * wakeup pending, since the queue length is 1. */ + ret = __CFSendTrivialMachMessage(rl->_wakeUpPort, 0, MACH_SEND_TIMEOUT, 0); + if (ret != MACH_MSG_SUCCESS && ret != MACH_SEND_TIMED_OUT) { + HALT; + } +#elif DEPLOYMENT_TARGET_WINDOWS + SetEvent(rl->_wakeUpPort); +#elif DEPLOYMENT_TARGET_LINUX + sem_post(rl->_wakeUpPort); +#endif +} + +void CFRunLoopStop(CFRunLoopRef rl) { + CHECK_FOR_FORK(); + __CFRunLoopLock(rl); + __CFRunLoopSetStopped(rl); + __CFRunLoopUnlock(rl); + CFRunLoopWakeUp(rl); +} + +CF_EXPORT void _CFRunLoopStopMode(CFRunLoopRef rl, CFStringRef modeName) { + CHECK_FOR_FORK(); + CFRunLoopModeRef rlm; + __CFRunLoopLock(rl); + rlm = __CFRunLoopFindMode(rl, modeName, true); + __CFRunLoopUnlock(rl); + if (NULL != rlm) { + rlm->_stopped = true; + __CFRunLoopModeUnlock(rlm); + } + CFRunLoopWakeUp(rl); +} + +CF_EXPORT Boolean _CFRunLoopModeContainsMode(CFRunLoopRef rl, CFStringRef modeName, CFStringRef candidateContainedName) { + CHECK_FOR_FORK(); + CFRunLoopModeRef rlm; + if (modeName == kCFRunLoopCommonModes || candidateContainedName == kCFRunLoopCommonModes) { + return false; + } else if (CFEqual(modeName, candidateContainedName)) { + return true; + } + __CFRunLoopLock(rl); + rlm = __CFRunLoopFindMode(rl, modeName, true); + __CFRunLoopUnlock(rl); + if (NULL != rlm) { + CFArrayRef submodes; + if (NULL == rlm->_submodes) { + __CFRunLoopModeUnlock(rlm); + return false; + } + if (CFArrayContainsValue(rlm->_submodes, CFRangeMake(0, CFArrayGetCount(rlm->_submodes)), candidateContainedName)) { + __CFRunLoopModeUnlock(rlm); + return true; + } + submodes = (NULL != rlm->_submodes && 0 < CFArrayGetCount(rlm->_submodes)) ? CFArrayCreateCopy(kCFAllocatorSystemDefault, rlm->_submodes) : NULL; + __CFRunLoopModeUnlock(rlm); + if (NULL != submodes) { + CFIndex idx, cnt; + for (idx = 0, cnt = CFArrayGetCount(submodes); idx < cnt; idx++) { + CFStringRef subname = (CFStringRef)CFArrayGetValueAtIndex(submodes, idx); + if (_CFRunLoopModeContainsMode(rl, subname, candidateContainedName)) { + CFRelease(submodes); + return true; + } + } + CFRelease(submodes); + } + } + return false; +} + +CF_EXPORT void _CFRunLoopAddModeToMode(CFRunLoopRef rl, CFStringRef modeName, CFStringRef toModeName) { + CHECK_FOR_FORK(); + CFRunLoopModeRef rlm; + if (__CFRunLoopIsDeallocating(rl)) return; + // should really do a recursive check here, to make sure that a cycle isn't + // introduced; of course, if that happens, you aren't going to get very far. + if (modeName == kCFRunLoopCommonModes || toModeName == kCFRunLoopCommonModes || CFEqual(modeName, toModeName)) { + return; + } else { + __CFRunLoopLock(rl); + rlm = __CFRunLoopFindMode(rl, toModeName, true); + __CFRunLoopUnlock(rl); + if (NULL != rlm) { + if (NULL == rlm->_submodes) { + rlm->_submodes = CFArrayCreateMutable(CFGetAllocator(rlm), 0, &kCFTypeArrayCallBacks); + } + if (!CFArrayContainsValue(rlm->_submodes, CFRangeMake(0, CFArrayGetCount(rlm->_submodes)), modeName)) { + CFArrayAppendValue(rlm->_submodes, modeName); + } + __CFRunLoopModeUnlock(rlm); + } + } +} + +CF_EXPORT void _CFRunLoopRemoveModeFromMode(CFRunLoopRef rl, CFStringRef modeName, CFStringRef fromModeName) { + CHECK_FOR_FORK(); + CFRunLoopModeRef rlm; + // should really do a recursive check here, to make sure that a cycle isn't + // introduced; of course, if that happens, you aren't going to get very far. + if (modeName == kCFRunLoopCommonModes || fromModeName == kCFRunLoopCommonModes || CFEqual(modeName, fromModeName)) { + return; + } else { + __CFRunLoopLock(rl); + rlm = __CFRunLoopFindMode(rl, fromModeName, true); + __CFRunLoopUnlock(rl); + if (NULL != rlm) { + if (NULL != rlm->_submodes) { + CFIndex idx, cnt = CFArrayGetCount(rlm->_submodes); + idx = CFArrayGetFirstIndexOfValue(rlm->_submodes, CFRangeMake(0, cnt), modeName); + if (0 <= idx) CFArrayRemoveValueAtIndex(rlm->_submodes, idx); + } + __CFRunLoopModeUnlock(rlm); + } + } +} + +Boolean CFRunLoopContainsSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStringRef modeName) { + CHECK_FOR_FORK(); + CFRunLoopModeRef rlm; + Boolean hasValue = false; + __CFRunLoopLock(rl); + if (modeName == kCFRunLoopCommonModes) { + if (NULL != rl->_commonModeItems) { + hasValue = CFSetContainsValue(rl->_commonModeItems, rls); + } + __CFRunLoopUnlock(rl); + } else { + rlm = __CFRunLoopFindMode(rl, modeName, false); + __CFRunLoopUnlock(rl); + if (NULL != rlm && NULL != rlm->_sources) { + hasValue = CFSetContainsValue(rlm->_sources, rls); + __CFRunLoopModeUnlock(rlm); + } else if (NULL != rlm) { + __CFRunLoopModeUnlock(rlm); + } + } + return hasValue; +} + +void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStringRef modeName) { /* DOES CALLOUT */ + CHECK_FOR_FORK(); + CFRunLoopModeRef rlm; + if (__CFRunLoopIsDeallocating(rl)) return; + if (!__CFIsValid(rls)) return; + __CFRunLoopLock(rl); + if (modeName == kCFRunLoopCommonModes) { + CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL; + if (NULL == rl->_commonModeItems) { + rl->_commonModeItems = CFSetCreateMutable(CFGetAllocator(rl), 0, &kCFTypeSetCallBacks); + _CFSetSetCapacity(rl->_commonModeItems, 20); + } + CFSetAddValue(rl->_commonModeItems, rls); + __CFRunLoopUnlock(rl); + if (NULL != set) { + CFTypeRef context[2] = {rl, rls}; + /* add new item to all common-modes */ + CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context); + CFRelease(set); + } + } else { + rlm = __CFRunLoopFindMode(rl, modeName, true); + __CFRunLoopUnlock(rl); + if (NULL != rlm && NULL == rlm->_sources) { + rlm->_sources = CFSetCreateMutable(CFGetAllocator(rlm), 0, &kCFTypeSetCallBacks); + _CFSetSetCapacity(rlm->_sources, 10); + } + if (NULL != rlm && !CFSetContainsValue(rlm->_sources, rls)) { + CFSetAddValue(rlm->_sources, rls); + __CFRunLoopModeUnlock(rlm); + __CFRunLoopSourceSchedule(rls, rl, rlm); /* DOES CALLOUT */ + } else if (NULL != rlm) { + __CFRunLoopModeUnlock(rlm); + } + } +} + +void CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStringRef modeName) { /* DOES CALLOUT */ + CHECK_FOR_FORK(); + CFRunLoopModeRef rlm; + __CFRunLoopLock(rl); + if (modeName == kCFRunLoopCommonModes) { + if (NULL != rl->_commonModeItems && CFSetContainsValue(rl->_commonModeItems, rls)) { + CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL; + CFSetRemoveValue(rl->_commonModeItems, rls); + __CFRunLoopUnlock(rl); + if (NULL != set) { + CFTypeRef context[2] = {rl, rls}; + /* remove new item from all common-modes */ + CFSetApplyFunction(set, (__CFRunLoopRemoveItemFromCommonModes), (void *)context); + CFRelease(set); + } + } else { + __CFRunLoopUnlock(rl); + } + } else { + rlm = __CFRunLoopFindMode(rl, modeName, false); + __CFRunLoopUnlock(rl); + if (NULL != rlm && NULL != rlm->_sources && CFSetContainsValue(rlm->_sources, rls)) { + CFRetain(rls); + CFSetRemoveValue(rlm->_sources, rls); + __CFRunLoopModeUnlock(rlm); + __CFRunLoopSourceCancel(rls, rl, rlm); /* DOES CALLOUT */ + CFRelease(rls); + } else if (NULL != rlm) { + __CFRunLoopModeUnlock(rlm); + } + } +} + +Boolean CFRunLoopContainsObserver(CFRunLoopRef rl, CFRunLoopObserverRef rlo, CFStringRef modeName) { + CHECK_FOR_FORK(); + CFRunLoopModeRef rlm; + Boolean hasValue = false; + __CFRunLoopLock(rl); + if (modeName == kCFRunLoopCommonModes) { + if (NULL != rl->_commonModeItems) { + hasValue = CFSetContainsValue(rl->_commonModeItems, rlo); + } + __CFRunLoopUnlock(rl); + } else { + rlm = __CFRunLoopFindMode(rl, modeName, false); + __CFRunLoopUnlock(rl); + if (NULL != rlm && NULL != rlm->_observers) { + hasValue = CFSetContainsValue(rlm->_observers, rlo); + __CFRunLoopModeUnlock(rlm); + } else if (NULL != rlm) { + __CFRunLoopModeUnlock(rlm); + } + } + return hasValue; +} + +void CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef rlo, CFStringRef modeName) { + CHECK_FOR_FORK(); + CFRunLoopModeRef rlm; + if (__CFRunLoopIsDeallocating(rl)) return; + if (!__CFIsValid(rlo) || (NULL != rlo->_runLoop && rlo->_runLoop != rl)) return; + __CFRunLoopLock(rl); + if (modeName == kCFRunLoopCommonModes) { + CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL; + if (NULL == rl->_commonModeItems) { + rl->_commonModeItems = CFSetCreateMutable(CFGetAllocator(rl), 0, &kCFTypeSetCallBacks); + } + CFSetAddValue(rl->_commonModeItems, rlo); + __CFRunLoopUnlock(rl); + if (NULL != set) { + CFTypeRef context[2] = {rl, rlo}; + /* add new item to all common-modes */ + CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context); + CFRelease(set); + } + } else { + rlm = __CFRunLoopFindMode(rl, modeName, true); + __CFRunLoopUnlock(rl); + if (NULL != rlm && NULL == rlm->_observers) { + rlm->_observers = CFSetCreateMutable(CFGetAllocator(rlm), 0, &kCFTypeSetCallBacks); + } + if (NULL != rlm && !CFSetContainsValue(rlm->_observers, rlo)) { + CFSetAddValue(rlm->_observers, rlo); + __CFRunLoopModeUnlock(rlm); + __CFRunLoopObserverSchedule(rlo, rl, rlm); + } else if (NULL != rlm) { + __CFRunLoopModeUnlock(rlm); + } + } +} + +void CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef rlo, CFStringRef modeName) { + CHECK_FOR_FORK(); + CFRunLoopModeRef rlm; + __CFRunLoopLock(rl); + if (modeName == kCFRunLoopCommonModes) { + if (NULL != rl->_commonModeItems && CFSetContainsValue(rl->_commonModeItems, rlo)) { + CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL; + CFSetRemoveValue(rl->_commonModeItems, rlo); + __CFRunLoopUnlock(rl); + if (NULL != set) { + CFTypeRef context[2] = {rl, rlo}; + /* remove new item from all common-modes */ + CFSetApplyFunction(set, (__CFRunLoopRemoveItemFromCommonModes), (void *)context); + CFRelease(set); + } + } else { + __CFRunLoopUnlock(rl); + } + } else { + rlm = __CFRunLoopFindMode(rl, modeName, false); + __CFRunLoopUnlock(rl); + if (NULL != rlm && NULL != rlm->_observers && CFSetContainsValue(rlm->_observers, rlo)) { + CFRetain(rlo); + CFSetRemoveValue(rlm->_observers, rlo); + __CFRunLoopModeUnlock(rlm); + __CFRunLoopObserverCancel(rlo, rl, rlm); + CFRelease(rlo); + } else if (NULL != rlm) { + __CFRunLoopModeUnlock(rlm); + } + } +} + +Boolean CFRunLoopContainsTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) { + CHECK_FOR_FORK(); + CFRunLoopModeRef rlm; + Boolean hasValue = false; + __CFRunLoopLock(rl); + if (modeName == kCFRunLoopCommonModes) { + if (NULL != rl->_commonModeItems) { + hasValue = CFSetContainsValue(rl->_commonModeItems, rlt); + } + __CFRunLoopUnlock(rl); + } else { + rlm = __CFRunLoopFindMode(rl, modeName, false); + __CFRunLoopUnlock(rl); + if (NULL != rlm && NULL != rlm->_timers) { + hasValue = CFSetContainsValue(rlm->_timers, rlt); + __CFRunLoopModeUnlock(rlm); + } else if (NULL != rlm) { + __CFRunLoopModeUnlock(rlm); + } + } + return hasValue; +} + +void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) { + CHECK_FOR_FORK(); + CFRunLoopModeRef rlm; + if (__CFRunLoopIsDeallocating(rl)) return; + if (!__CFIsValid(rlt) || (NULL != rlt->_runLoop && rlt->_runLoop != rl)) return; + __CFRunLoopLock(rl); + if (modeName == kCFRunLoopCommonModes) { + CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL; + if (NULL == rl->_commonModeItems) { + rl->_commonModeItems = CFSetCreateMutable(CFGetAllocator(rl), 0, &kCFTypeSetCallBacks); + } + CFSetAddValue(rl->_commonModeItems, rlt); + __CFRunLoopUnlock(rl); + if (NULL != set) { + CFTypeRef context[2] = {rl, rlt}; + /* add new item to all common-modes */ + CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context); + CFRelease(set); + } + } else { + rlm = __CFRunLoopFindMode(rl, modeName, true); + __CFRunLoopUnlock(rl); + if (NULL != rlm && NULL == rlm->_timers) { + rlm->_timers = CFSetCreateMutable(CFGetAllocator(rlm), 0, &kCFTypeSetCallBacks); + } + if (NULL != rlm && !CFSetContainsValue(rlm->_timers, rlt)) { + CFSetAddValue(rlm->_timers, rlt); + __CFRunLoopModeUnlock(rlm); + __CFRunLoopTimerSchedule(rlt, rl, rlm); + } else if (NULL != rlm) { + __CFRunLoopModeUnlock(rlm); + } + } +} + +void CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) { + CHECK_FOR_FORK(); + CFRunLoopModeRef rlm; + __CFRunLoopLock(rl); + if (modeName == kCFRunLoopCommonModes) { + if (NULL != rl->_commonModeItems && CFSetContainsValue(rl->_commonModeItems, rlt)) { + CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL; + CFSetRemoveValue(rl->_commonModeItems, rlt); + __CFRunLoopUnlock(rl); + if (NULL != set) { + CFTypeRef context[2] = {rl, rlt}; + /* remove new item from all common-modes */ + CFSetApplyFunction(set, (__CFRunLoopRemoveItemFromCommonModes), (void *)context); + CFRelease(set); + } + } else { + __CFRunLoopUnlock(rl); + } + } else { + rlm = __CFRunLoopFindMode(rl, modeName, false); + __CFRunLoopUnlock(rl); + if (NULL != rlm && NULL != rlm->_timers && CFSetContainsValue(rlm->_timers, rlt)) { + CFRetain(rlt); + CFSetRemoveValue(rlm->_timers, rlt); + __CFRunLoopModeUnlock(rlm); + __CFRunLoopTimerCancel(rlt, rl, rlm); + CFRelease(rlt); + } else if (NULL != rlm) { + __CFRunLoopModeUnlock(rlm); + } + } +} + + +/* CFRunLoopSource */ + +static Boolean __CFRunLoopSourceEqual(CFTypeRef cf1, CFTypeRef cf2) { /* DOES CALLOUT */ + CFRunLoopSourceRef rls1 = (CFRunLoopSourceRef)cf1; + CFRunLoopSourceRef rls2 = (CFRunLoopSourceRef)cf2; + if (rls1 == rls2) return true; + if (rls1->_order != rls2->_order) return false; + if (rls1->_context.version0.version != rls2->_context.version0.version) return false; + if (rls1->_context.version0.hash != rls2->_context.version0.hash) return false; + if (rls1->_context.version0.equal != rls2->_context.version0.equal) return false; + if (0 == rls1->_context.version0.version && rls1->_context.version0.perform != rls2->_context.version0.perform) return false; + if (1 == rls1->_context.version0.version && rls1->_context.version1.perform != rls2->_context.version1.perform) return false; + if (rls1->_context.version0.equal) + return rls1->_context.version0.equal(rls1->_context.version0.info, rls2->_context.version0.info); + return (rls1->_context.version0.info == rls2->_context.version0.info); +} + +static CFHashCode __CFRunLoopSourceHash(CFTypeRef cf) { /* DOES CALLOUT */ + CFRunLoopSourceRef rls = (CFRunLoopSourceRef)cf; + if (rls->_context.version0.hash) + return rls->_context.version0.hash(rls->_context.version0.info); + return (CFHashCode)rls->_context.version0.info; +} + +static CFStringRef __CFRunLoopSourceCopyDescription(CFTypeRef cf) { /* DOES CALLOUT */ + CFRunLoopSourceRef rls = (CFRunLoopSourceRef)cf; + CFStringRef result; + CFStringRef contextDesc = NULL; + if (NULL != rls->_context.version0.copyDescription) { + contextDesc = rls->_context.version0.copyDescription(rls->_context.version0.info); + } + if (NULL == contextDesc) { + void *addr = rls->_context.version0.version == 0 ? (void *)rls->_context.version0.perform : (rls->_context.version0.version == 1 ? (void *)rls->_context.version1.perform : NULL); +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX + Dl_info info; + const char *name = (dladdr(addr, &info) && info.dli_saddr == addr && info.dli_sname) ? info.dli_sname : "???"; + contextDesc = CFStringCreateWithFormat(CFGetAllocator(rls), NULL, CFSTR("<CFRunLoopSource context>{version = %ld, info = %p, callout = %s (%p)}"), rls->_context.version0.version, rls->_context.version0.info, name, addr); +#elif DEPLOYMENT_TARGET_WINDOWS + const char *name = "???"; + contextDesc = CFStringCreateWithFormat(CFGetAllocator(rls), NULL, CFSTR("<CFRunLoopSource context>{version = %ld, info = %p, callout = %s (%p)}"), rls->_context.version0.version, rls->_context.version0.info, name, addr); +#else +#error Unknown or unspecified DEPLOYMENT_TARGET +#endif + } +result = CFStringCreateWithFormat(CFGetAllocator(rls), NULL, CFSTR("<CFRunLoopSource %p [%p]>{signalled = %s, valid = %s, order = %d, context = %@}"), cf, CFGetAllocator(rls), __CFRunLoopSourceIsSignaled(rls) ? "Yes" : "No", __CFIsValid(rls) ? "Yes" : "No", rls->_order, contextDesc); + CFRelease(contextDesc); + return result; +} + +static void __CFRunLoopSourceDeallocate(CFTypeRef cf) { /* DOES CALLOUT */ + CFRunLoopSourceRef rls = (CFRunLoopSourceRef)cf; + CFRunLoopSourceInvalidate(rls); + if (rls->_context.version0.release) { + rls->_context.version0.release(rls->_context.version0.info); + } +} + +static const CFRuntimeClass __CFRunLoopSourceClass = { + _kCFRuntimeScannedObject, + "CFRunLoopSource", + NULL, // init + NULL, // copy + __CFRunLoopSourceDeallocate, + __CFRunLoopSourceEqual, + __CFRunLoopSourceHash, + NULL, // + __CFRunLoopSourceCopyDescription +}; + +__private_extern__ void __CFRunLoopSourceInitialize(void) { + __kCFRunLoopSourceTypeID = _CFRuntimeRegisterClass(&__CFRunLoopSourceClass); +} + +CFTypeID CFRunLoopSourceGetTypeID(void) { + return __kCFRunLoopSourceTypeID; +} + +CFRunLoopSourceRef CFRunLoopSourceCreate(CFAllocatorRef allocator, CFIndex order, CFRunLoopSourceContext *context) { + CHECK_FOR_FORK(); + CFRunLoopSourceRef memory; + uint32_t size; + if (NULL == context) HALT; + size = sizeof(struct __CFRunLoopSource) - sizeof(CFRuntimeBase); + memory = (CFRunLoopSourceRef)_CFRuntimeCreateInstance(allocator, __kCFRunLoopSourceTypeID, size, NULL); + if (NULL == memory) { + return NULL; + } + __CFSetValid(memory); + __CFRunLoopSourceUnsetSignaled(memory); + CF_SPINLOCK_INIT_FOR_STRUCTS(memory->_lock); + memory->_bits = 0; + memory->_order = order; + memory->_runLoops = NULL; + size = 0; + switch (context->version) { + case 0: + size = sizeof(CFRunLoopSourceContext); + break; + case 1: + size = sizeof(CFRunLoopSourceContext1); + break; +#if DEPLOYMENT_TARGET_MACOSX + case 2: + size = sizeof(CFRunLoopSourceContext2); + break; +#endif + } + CF_WRITE_BARRIER_MEMMOVE(&memory->_context, context, size); + if (context->retain) { + memory->_context.version0.info = (void *)context->retain(context->info); + } + return memory; +} + +CFIndex CFRunLoopSourceGetOrder(CFRunLoopSourceRef rls) { + CHECK_FOR_FORK(); + __CFGenericValidateType(rls, __kCFRunLoopSourceTypeID); + return rls->_order; +} + +static void __CFRunLoopSourceRemoveFromRunLoop(const void *value, void *context) { + CFRunLoopRef rl = (CFRunLoopRef)value; + CFTypeRef *params = (CFTypeRef*)context; + CFRunLoopSourceRef rls = (CFRunLoopSourceRef)params[0]; + CFArrayRef array; + CFIndex idx; + if (rl == params[1]) return; + array = CFRunLoopCopyAllModes(rl); + for (idx = CFArrayGetCount(array); idx--;) { + CFStringRef modeName = (CFStringRef)CFArrayGetValueAtIndex(array, idx); + CFRunLoopRemoveSource(rl, rls, modeName); + } + CFRunLoopRemoveSource(rl, rls, kCFRunLoopCommonModes); + CFRelease(array); + params[1] = rl; +} + +void CFRunLoopSourceInvalidate(CFRunLoopSourceRef rls) { + CHECK_FOR_FORK(); + __CFGenericValidateType(rls, __kCFRunLoopSourceTypeID); + CFRetain(rls); + __CFRunLoopSourceLock(rls); + if (__CFIsValid(rls)) { + __CFUnsetValid(rls); + __CFRunLoopSourceUnsetSignaled(rls); + if (NULL != rls->_runLoops) { + CFTypeRef params[2] = {rls, NULL}; + CFBagRef bag = CFBagCreateCopy(kCFAllocatorSystemDefault, rls->_runLoops); + CFRelease(rls->_runLoops); + rls->_runLoops = NULL; + __CFRunLoopSourceUnlock(rls); + CFBagApplyFunction(bag, (__CFRunLoopSourceRemoveFromRunLoop), params); + CFRelease(bag); + } else { + __CFRunLoopSourceUnlock(rls); + } + /* for hashing- and equality-use purposes, can't actually release the context here */ + } else { + __CFRunLoopSourceUnlock(rls); + } + CFRelease(rls); +} + +Boolean CFRunLoopSourceIsValid(CFRunLoopSourceRef rls) { + CHECK_FOR_FORK(); + __CFGenericValidateType(rls, __kCFRunLoopSourceTypeID); + return __CFIsValid(rls); +} + +void CFRunLoopSourceGetContext(CFRunLoopSourceRef rls, CFRunLoopSourceContext *context) { + CHECK_FOR_FORK(); + __CFGenericValidateType(rls, __kCFRunLoopSourceTypeID); + CFAssert1(0 == context->version || 1 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0 or 1", __PRETTY_FUNCTION__); + CFIndex size = 0; + switch (context->version) { + case 0: + size = sizeof(CFRunLoopSourceContext); + break; + case 1: + size = sizeof(CFRunLoopSourceContext1); + break; +#if DEPLOYMENT_TARGET_MACOSX + case 2: + size = sizeof(CFRunLoopSourceContext2); + break; +#endif + } + memmove(context, &rls->_context, size); +} + +void CFRunLoopSourceSignal(CFRunLoopSourceRef rls) { + CHECK_FOR_FORK(); + __CFRunLoopSourceLock(rls); + if (__CFIsValid(rls)) { + __CFRunLoopSourceSetSignaled(rls); + } + __CFRunLoopSourceUnlock(rls); +} + +Boolean CFRunLoopSourceIsSignalled(CFRunLoopSourceRef rls) { + CHECK_FOR_FORK(); + __CFRunLoopSourceLock(rls); + Boolean ret = __CFRunLoopSourceIsSignaled(rls) ? true : false; + __CFRunLoopSourceUnlock(rls); + return ret; +} + +/* CFRunLoopObserver */ + +static CFStringRef __CFRunLoopObserverCopyDescription(CFTypeRef cf) { /* DOES CALLOUT */ + CFRunLoopObserverRef rlo = (CFRunLoopObserverRef)cf; + CFStringRef result; + CFStringRef contextDesc = NULL; + if (NULL != rlo->_context.copyDescription) { + contextDesc = rlo->_context.copyDescription(rlo->_context.info); + } + if (!contextDesc) { + contextDesc = CFStringCreateWithFormat(CFGetAllocator(rlo), NULL, CFSTR("<CFRunLoopObserver context %p>"), rlo->_context.info); + } +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX + void *addr = (void*)rlo->_callout; + Dl_info info; + const char *name = (dladdr(addr, &info) && info.dli_saddr == addr && info.dli_sname) ? info.dli_sname : "???"; + result = CFStringCreateWithFormat(CFGetAllocator(rlo), NULL, CFSTR("<CFRunLoopObserver %p [%p]>{valid = %s, activities = 0x%x, repeats = %s, order = %d, callout = %s (%p), context = %@}"), cf, CFGetAllocator(rlo), __CFIsValid(rlo) ? "Yes" : "No", rlo->_activities, __CFRunLoopObserverRepeats(rlo) ? "Yes" : "No", rlo->_order, name, addr, contextDesc); +#elif DEPLOYMENT_TARGET_WINDOWS + void *addr = rlo->_callout; + const char *name = "???"; + result = CFStringCreateWithFormat(CFGetAllocator(rlo), NULL, CFSTR("<CFRunLoopObserver %p [%p]>{locked = %s, valid = %s, activities = 0x%x, repeats = %s, order = %d, callout = %s (%p), context = %@}"), cf, CFGetAllocator(rlo), lockCount(rlo->_lock) ? "Yes" : "No", __CFIsValid(rlo) ? "Yes" : "No", rlo->_activities, __CFRunLoopObserverRepeats(rlo) ? "Yes" : "No", rlo->_order, name, addr, contextDesc); +#else +#error Unknown or unspecified DEPLOYMENT_TARGET +#endif + CFRelease(contextDesc); + return result; +} + +static void __CFRunLoopObserverDeallocate(CFTypeRef cf) { /* DOES CALLOUT */ + CFRunLoopObserverRef rlo = (CFRunLoopObserverRef)cf; + CFRunLoopObserverInvalidate(rlo); +} + +static const CFRuntimeClass __CFRunLoopObserverClass = { + 0, + "CFRunLoopObserver", + NULL, // init + NULL, // copy + __CFRunLoopObserverDeallocate, + NULL, + NULL, + NULL, // + __CFRunLoopObserverCopyDescription +}; + +__private_extern__ void __CFRunLoopObserverInitialize(void) { + __kCFRunLoopObserverTypeID = _CFRuntimeRegisterClass(&__CFRunLoopObserverClass); +} + +CFTypeID CFRunLoopObserverGetTypeID(void) { + return __kCFRunLoopObserverTypeID; +} + +CFRunLoopObserverRef CFRunLoopObserverCreate(CFAllocatorRef allocator, CFOptionFlags activities, Boolean repeats, CFIndex order, CFRunLoopObserverCallBack callout, CFRunLoopObserverContext *context) { + CHECK_FOR_FORK(); + CFRunLoopObserverRef memory; + UInt32 size; + size = sizeof(struct __CFRunLoopObserver) - sizeof(CFRuntimeBase); + memory = (CFRunLoopObserverRef)_CFRuntimeCreateInstance(allocator, __kCFRunLoopObserverTypeID, size, NULL); + if (NULL == memory) { + return NULL; + } + __CFSetValid(memory); + __CFRunLoopObserverUnsetFiring(memory); + if (repeats) { + __CFRunLoopObserverSetRepeats(memory); + } else { + __CFRunLoopObserverUnsetRepeats(memory); + } + CF_SPINLOCK_INIT_FOR_STRUCTS(memory->_lock); + memory->_runLoop = NULL; + memory->_rlCount = 0; + memory->_activities = activities; + memory->_order = order; + memory->_callout = callout; + if (context) { + if (context->retain) { + memory->_context.info = (void *)context->retain(context->info); + } else { + memory->_context.info = context->info; + } + memory->_context.retain = context->retain; + memory->_context.release = context->release; + memory->_context.copyDescription = context->copyDescription; + } else { + memory->_context.info = 0; + memory->_context.retain = 0; + memory->_context.release = 0; + memory->_context.copyDescription = 0; + } + return memory; +} + +CFOptionFlags CFRunLoopObserverGetActivities(CFRunLoopObserverRef rlo) { + CHECK_FOR_FORK(); + __CFGenericValidateType(rlo, __kCFRunLoopObserverTypeID); + return rlo->_activities; +} + +CFIndex CFRunLoopObserverGetOrder(CFRunLoopObserverRef rlo) { + CHECK_FOR_FORK(); + __CFGenericValidateType(rlo, __kCFRunLoopObserverTypeID); + return rlo->_order; +} + +Boolean CFRunLoopObserverDoesRepeat(CFRunLoopObserverRef rlo) { + CHECK_FOR_FORK(); + __CFGenericValidateType(rlo, __kCFRunLoopObserverTypeID); + return __CFRunLoopObserverRepeats(rlo); +} + +void CFRunLoopObserverInvalidate(CFRunLoopObserverRef rlo) { /* DOES CALLOUT */ + CHECK_FOR_FORK(); + __CFGenericValidateType(rlo, __kCFRunLoopObserverTypeID); + CFRetain(rlo); + __CFRunLoopObserverLock(rlo); + if (__CFIsValid(rlo)) { + CFRunLoopRef rl = rlo->_runLoop; + __CFUnsetValid(rlo); + __CFRunLoopObserverUnlock(rlo); + if (NULL != rl) { + CFArrayRef array; + CFIndex idx; + array = CFRunLoopCopyAllModes(rl); + for (idx = CFArrayGetCount(array); idx--;) { + CFStringRef modeName = (CFStringRef)CFArrayGetValueAtIndex(array, idx); + CFRunLoopRemoveObserver(rl, rlo, modeName); + } + CFRunLoopRemoveObserver(rl, rlo, kCFRunLoopCommonModes); + CFRelease(array); + } + if (rlo->_context.release) + rlo->_context.release(rlo->_context.info); /* CALLOUT */ + rlo->_context.info = NULL; + } else { + __CFRunLoopObserverUnlock(rlo); + } + CFRelease(rlo); +} + +Boolean CFRunLoopObserverIsValid(CFRunLoopObserverRef rlo) { + CHECK_FOR_FORK(); + return __CFIsValid(rlo); +} + +void CFRunLoopObserverGetContext(CFRunLoopObserverRef rlo, CFRunLoopObserverContext *context) { + CHECK_FOR_FORK(); + __CFGenericValidateType(rlo, __kCFRunLoopObserverTypeID); + CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__); + *context = rlo->_context; +} + +/* CFRunLoopTimer */ + +static CFStringRef __CFRunLoopTimerCopyDescription(CFTypeRef cf) { /* DOES CALLOUT */ + CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)cf; + CFStringRef result; + CFStringRef contextDesc = NULL; + int64_t fireTime; + __CFRunLoopTimerFireTSRLock(); + fireTime = rlt->_fireTSR; + __CFRunLoopTimerFireTSRUnlock(); + if (NULL != rlt->_context.copyDescription) { + contextDesc = rlt->_context.copyDescription(rlt->_context.info); + } + if (NULL == contextDesc) { + contextDesc = CFStringCreateWithFormat(CFGetAllocator(rlt), NULL, CFSTR("<CFRunLoopTimer context %p>"), rlt->_context.info); + } + int64_t now2 = __CFReadTSR(); + CFAbsoluteTime now1 = CFAbsoluteTimeGetCurrent(); +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX + void *addr = (void*)rlt->_callout; + Dl_info info; + const char *name = (dladdr(addr, &info) && info.dli_saddr == addr && info.dli_sname) ? info.dli_sname : "???"; + result = CFStringCreateWithFormat(CFGetAllocator(rlt), NULL, CFSTR("<CFRunLoopTimer %p [%p]>{valid = %s, interval = %0.09g, next fire date = %0.09g, order = %d, callout = %s (%p), context = %@}"), cf, CFGetAllocator(rlt), __CFIsValid(rlt) ? "Yes" : "No", __CFTSRToTimeInterval(rlt->_intervalTSR), now1 + __CFTSRToTimeInterval(fireTime - now2), rlt->_order, name, addr, contextDesc); +#elif DEPLOYMENT_TARGET_WINDOWS + void *addr = rlt->_callout; + const char *name = "???"; + result = CFStringCreateWithFormat(CFGetAllocator(rlt), NULL, CFSTR("<CFRunLoopTimer %p [%p]>{locked = %s, valid = %s, interval = %0.09g, next fire date = %0.09g, order = %d, callout = %s (%p), context = %@}"), cf, CFGetAllocator(rlt), lockCount(rlt->_lock) ? "Yes" : "No", __CFIsValid(rlt) ? "Yes" : "No", __CFTSRToTimeInterval(rlt->_intervalTSR), now1 + __CFTSRToTimeInterval(fireTime - now2), rlt->_order, name, addr, contextDesc); +#else +#error Unknown or unspecified DEPLOYMENT_TARGET +#endif + CFRelease(contextDesc); + return result; +} + +static void __CFRunLoopTimerDeallocate(CFTypeRef cf) { /* DOES CALLOUT */ + CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)cf; + CFRunLoopTimerInvalidate(rlt); /* DOES CALLOUT */ +} + +static const CFRuntimeClass __CFRunLoopTimerClass = { + 0, + "CFRunLoopTimer", + NULL, // init + NULL, // copy + __CFRunLoopTimerDeallocate, + NULL, // equal + NULL, + NULL, // + __CFRunLoopTimerCopyDescription +}; + +__private_extern__ void __CFRunLoopTimerInitialize(void) { + __kCFRunLoopTimerTypeID = _CFRuntimeRegisterClass(&__CFRunLoopTimerClass); +} + +CFTypeID CFRunLoopTimerGetTypeID(void) { + return __kCFRunLoopTimerTypeID; +} + +CFRunLoopTimerRef CFRunLoopTimerCreate(CFAllocatorRef allocator, CFAbsoluteTime fireDate, CFTimeInterval interval, CFOptionFlags flags, CFIndex order, CFRunLoopTimerCallBack callout, CFRunLoopTimerContext *context) { + CHECK_FOR_FORK(); + CFRunLoopTimerRef memory; + UInt32 size; + size = sizeof(struct __CFRunLoopTimer) - sizeof(CFRuntimeBase); + memory = (CFRunLoopTimerRef)_CFRuntimeCreateInstance(allocator, __kCFRunLoopTimerTypeID, size, NULL); + if (NULL == memory) { + return NULL; + } + __CFSetValid(memory); + __CFRunLoopTimerUnsetFiring(memory); + __CFRunLoopTimerUnsetDidFire(memory); + CF_SPINLOCK_INIT_FOR_STRUCTS(memory->_lock); + memory->_runLoop = NULL; + memory->_rlCount = 0; + memory->_order = order; +#if DEPLOYMENT_TARGET_MACOSX + memory->_port = MACH_PORT_NULL; +#endif + int64_t now2 = __CFReadTSR(); + CFAbsoluteTime now1 = CFAbsoluteTimeGetCurrent(); + if (3.1556952e+9 < fireDate) fireDate = 3.1556952e+9; + if (fireDate < now1) { + memory->_fireTSR = now2; + } else if (now1 + __CFTSRToTimeInterval(LLONG_MAX) < fireDate) { + memory->_fireTSR = LLONG_MAX; + } else { + memory->_fireTSR = now2 + __CFTimeIntervalToTSR(fireDate - now1); + } + if (3.1556952e+9 < interval) interval = 3.1556952e+9; + if (interval <= 0.0) { + memory->_intervalTSR = 0; + } else if (__CFTSRToTimeInterval(LLONG_MAX) < interval) { + memory->_intervalTSR = LLONG_MAX; + } else { + memory->_intervalTSR = __CFTimeIntervalToTSR(interval); + } + memory->_callout = callout; + if (NULL != context) { + if (context->retain) { + memory->_context.info = (void *)context->retain(context->info); + } else { + memory->_context.info = context->info; + } + memory->_context.retain = context->retain; + memory->_context.release = context->release; + memory->_context.copyDescription = context->copyDescription; + } else { + memory->_context.info = 0; + memory->_context.retain = 0; + memory->_context.release = 0; + memory->_context.copyDescription = 0; + } + return memory; +} + +CFAbsoluteTime CFRunLoopTimerGetNextFireDate(CFRunLoopTimerRef rlt) { + CHECK_FOR_FORK(); + int64_t fireTime, result = 0; + CF_OBJC_FUNCDISPATCH0(__kCFRunLoopTimerTypeID, CFAbsoluteTime, rlt, "_cffireTime"); + __CFGenericValidateType(rlt, __kCFRunLoopTimerTypeID); + __CFRunLoopTimerFireTSRLock(); + fireTime = rlt->_fireTSR; + __CFRunLoopTimerFireTSRUnlock(); + __CFRunLoopTimerLock(rlt); + if (__CFIsValid(rlt)) { + result = fireTime; + } + __CFRunLoopTimerUnlock(rlt); + int64_t now2 = __CFReadTSR(); + CFAbsoluteTime now1 = CFAbsoluteTimeGetCurrent(); + return (0 == result) ? 0.0 : now1 + __CFTSRToTimeInterval(result - now2); +} + +void CFRunLoopTimerSetNextFireDate(CFRunLoopTimerRef rlt, CFAbsoluteTime fireDate) { + CHECK_FOR_FORK(); + __CFRunLoopTimerFireTSRLock(); + int64_t now2 = __CFReadTSR(); + CFAbsoluteTime now1 = CFAbsoluteTimeGetCurrent(); + if (3.1556952e+9 < fireDate) fireDate = 3.1556952e+9; + if (fireDate < now1) { + rlt->_fireTSR = now2; + } else if (now1 + __CFTSRToTimeInterval(LLONG_MAX) < fireDate) { + rlt->_fireTSR = LLONG_MAX; + } else { + rlt->_fireTSR = now2 + __CFTimeIntervalToTSR(fireDate - now1); + } + if (rlt->_runLoop != NULL) { + __CFRunLoopTimerRescheduleWithAllModes(rlt, rlt->_runLoop); + } + __CFRunLoopTimerFireTSRUnlock(); +} + +CFTimeInterval CFRunLoopTimerGetInterval(CFRunLoopTimerRef rlt) { + CHECK_FOR_FORK(); + CF_OBJC_FUNCDISPATCH0(__kCFRunLoopTimerTypeID, CFTimeInterval, rlt, "timeInterval"); + __CFGenericValidateType(rlt, __kCFRunLoopTimerTypeID); + return __CFTSRToTimeInterval(rlt->_intervalTSR); +} + +Boolean CFRunLoopTimerDoesRepeat(CFRunLoopTimerRef rlt) { + CHECK_FOR_FORK(); + __CFGenericValidateType(rlt, __kCFRunLoopTimerTypeID); + return (0 != rlt->_intervalTSR); +} + +CFIndex CFRunLoopTimerGetOrder(CFRunLoopTimerRef rlt) { + CHECK_FOR_FORK(); + CF_OBJC_FUNCDISPATCH0(__kCFRunLoopTimerTypeID, CFIndex, rlt, "order"); + __CFGenericValidateType(rlt, __kCFRunLoopTimerTypeID); + return rlt->_order; +} + +void CFRunLoopTimerInvalidate(CFRunLoopTimerRef rlt) { /* DOES CALLOUT */ + CHECK_FOR_FORK(); + CF_OBJC_FUNCDISPATCH0(__kCFRunLoopTimerTypeID, void, rlt, "invalidate"); + __CFGenericValidateType(rlt, __kCFRunLoopTimerTypeID); + CFRetain(rlt); + __CFRunLoopTimerLock(rlt); + if (__CFIsValid(rlt)) { + CFRunLoopRef rl = rlt->_runLoop; + void *info = rlt->_context.info; + __CFUnsetValid(rlt); +#if DEPLOYMENT_TARGET_MACOSX + __CFRunLoopTimerPortMapLock(); + if (NULL != __CFRLTPortMap) { + CFDictionaryRemoveValue(__CFRLTPortMap, (void *)(uintptr_t)rlt->_port); + } + __CFRunLoopTimerPortMapUnlock(); + mk_timer_destroy(rlt->_port); + rlt->_port = MACH_PORT_NULL; +#endif + rlt->_context.info = NULL; + __CFRunLoopTimerUnlock(rlt); + if (NULL != rl) { + CFArrayRef array; + CFIndex idx; + array = CFRunLoopCopyAllModes(rl); + for (idx = CFArrayGetCount(array); idx--;) { + CFStringRef modeName = (CFStringRef)CFArrayGetValueAtIndex(array, idx); + CFRunLoopRemoveTimer(rl, rlt, modeName); + } + CFRunLoopRemoveTimer(rl, rlt, kCFRunLoopCommonModes); + CFRelease(array); + } + if (NULL != rlt->_context.release) { + rlt->_context.release(info); /* CALLOUT */ + } + } else { + __CFRunLoopTimerUnlock(rlt); + } + CFRelease(rlt); +} + +Boolean CFRunLoopTimerIsValid(CFRunLoopTimerRef rlt) { + CHECK_FOR_FORK(); + CF_OBJC_FUNCDISPATCH0(__kCFRunLoopTimerTypeID, Boolean, rlt, "isValid"); + __CFGenericValidateType(rlt, __kCFRunLoopTimerTypeID); + return __CFIsValid(rlt); +} + +void CFRunLoopTimerGetContext(CFRunLoopTimerRef rlt, CFRunLoopTimerContext *context) { + CHECK_FOR_FORK(); + __CFGenericValidateType(rlt, __kCFRunLoopTimerTypeID); + CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__); + *context = rlt->_context; +} + +#endif + +#if DEPLOYMENT_TARGET_WINDOWS +#include <Winternl.h> +typedef NTSTATUS (__stdcall *pfnNtQueryInformationThread) (HANDLE, THREADINFOCLASS, PVOID, ULONG, PULONG); + +typedef struct _CLIENT_ID +{ +DWORD UniqueProcess; +DWORD UniqueThread; +} CLIENT_ID, *PCLIENT_ID; + +typedef struct _THREAD_BASIC_INFORMATION +{ +NTSTATUS ExitStatus; +PVOID TebBaseAddress; +CLIENT_ID ClientId; +LONG AffinityMask; +LONG Priority; +LONG BasePriority; +} THREAD_BASIC_INFORMATION, *PTHREAD_BASIC_INFORMATION; + +/** + * This API function exists in Vista and beyond, but is not + * available in other OS releases. + * + * It uses undocumented/internal calls and is very evil. + * Thankfully, Vista+ users can call the true API function. + */ +DWORD GetInternalThreadId(HANDLE t) +{ + static HMODULE hModule = LoadLibraryA("ntdll.dll"); + static pfnNtQueryInformationThread queryInformationThread = 0; + + if (!t) + return 0; // Don't process null handles + + if (!queryInformationThread) + { + queryInformationThread = (pfnNtQueryInformationThread) GetProcAddress(hModule, "NtQueryInformationThread"); + if (!queryInformationThread) + return 0; // failed to get proc address + } + + THREAD_BASIC_INFORMATION tbi; + THREADINFOCLASS tic = (THREADINFOCLASS)0; // basic information + if (queryInformationThread(GetCurrentThread(), tic, &tbi, sizeof(tbi), NULL)) + { + // NtQueryInformationThread failed... + FreeLibrary(hModule); + return 0; + } + + return tbi.ClientId.UniqueThread; +} +#endif
diff --git a/CoreFoundation/CFRunLoop.h b/CoreFoundation/CFRunLoop.h new file mode 100644 index 0000000..3521a43 --- /dev/null +++ b/CoreFoundation/CFRunLoop.h
@@ -0,0 +1,203 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFRunLoop.h + Copyright (c) 1998-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFRUNLOOP__) +#define __COREFOUNDATION_CFRUNLOOP__ 1 + +#include <CoreFoundation/CFBase.h> +#include <CoreFoundation/CFArray.h> +#include <CoreFoundation/CFDate.h> +#include <CoreFoundation/CFString.h> +#if defined(__MACH__) + #include <mach/port.h> +#elif DEPLOYMENT_TARGET_LINUX + #include <semaphore.h> +#endif + +CF_EXTERN_C_BEGIN + +typedef struct __CFRunLoop * CFRunLoopRef; + +typedef struct __CFRunLoopSource * CFRunLoopSourceRef; + +typedef struct __CFRunLoopObserver * CFRunLoopObserverRef; + +typedef struct __CFRunLoopTimer * CFRunLoopTimerRef; + +/* Reasons for CFRunLoopRunInMode() to Return */ +enum { + kCFRunLoopRunFinished = 1, + kCFRunLoopRunStopped = 2, + kCFRunLoopRunTimedOut = 3, + kCFRunLoopRunHandledSource = 4 +}; + +/* Run Loop Observer Activities */ +enum { + kCFRunLoopEntry = (1 << 0), + kCFRunLoopBeforeTimers = (1 << 1), + kCFRunLoopBeforeSources = (1 << 2), + kCFRunLoopBeforeWaiting = (1 << 5), + kCFRunLoopAfterWaiting = (1 << 6), + kCFRunLoopExit = (1 << 7), + kCFRunLoopAllActivities = 0x0FFFFFFFU +}; +typedef CFOptionFlags CFRunLoopActivity; + +CF_EXPORT const CFStringRef kCFRunLoopDefaultMode; +CF_EXPORT const CFStringRef kCFRunLoopCommonModes; + +CF_EXPORT CFTypeID CFRunLoopGetTypeID(void); + +CF_EXPORT CFRunLoopRef CFRunLoopGetCurrent(void); +CF_EXPORT CFRunLoopRef CFRunLoopGetMain(void) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER; + +CF_EXPORT CFStringRef CFRunLoopCopyCurrentMode(CFRunLoopRef rl); + +CF_EXPORT CFArrayRef CFRunLoopCopyAllModes(CFRunLoopRef rl); + +CF_EXPORT void CFRunLoopAddCommonMode(CFRunLoopRef rl, CFStringRef mode); + +CF_EXPORT CFAbsoluteTime CFRunLoopGetNextTimerFireDate(CFRunLoopRef rl, CFStringRef mode); + +CF_EXPORT void CFRunLoopRun(void); +CF_EXPORT SInt32 CFRunLoopRunInMode(CFStringRef mode, CFTimeInterval seconds, Boolean returnAfterSourceHandled); +CF_EXPORT Boolean CFRunLoopIsWaiting(CFRunLoopRef rl); +CF_EXPORT void CFRunLoopWakeUp(CFRunLoopRef rl); +CF_EXPORT void CFRunLoopStop(CFRunLoopRef rl); + +CF_EXPORT Boolean CFRunLoopContainsSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef mode); +CF_EXPORT void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef mode); +CF_EXPORT void CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef mode); + +CF_EXPORT Boolean CFRunLoopContainsObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef mode); +CF_EXPORT void CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef mode); +CF_EXPORT void CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef mode); + +CF_EXPORT Boolean CFRunLoopContainsTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode); +CF_EXPORT void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode); +CF_EXPORT void CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode); + +typedef struct { + CFIndex version; + void * info; + const void *(*retain)(const void *info); + void (*release)(const void *info); + CFStringRef (*copyDescription)(const void *info); + Boolean (*equal)(const void *info1, const void *info2); + CFHashCode (*hash)(const void *info); + void (*schedule)(void *info, CFRunLoopRef rl, CFStringRef mode); + void (*cancel)(void *info, CFRunLoopRef rl, CFStringRef mode); + void (*perform)(void *info); +} CFRunLoopSourceContext; + +typedef struct { + CFIndex version; + void * info; + const void *(*retain)(const void *info); + void (*release)(const void *info); + CFStringRef (*copyDescription)(const void *info); + Boolean (*equal)(const void *info1, const void *info2); + CFHashCode (*hash)(const void *info); +#if defined(__MACH__) + mach_port_t (*getPort)(void *info); + void * (*perform)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info); +#elif DEPLOYMENT_TARGET_LINUX + sem_t * (*getPort)(void *info); + void (*perform)(void *info); +#else + HANDLE (*getPort)(void *info); + void (*perform)(void *info); +#endif +} CFRunLoopSourceContext1; + +CF_EXPORT CFTypeID CFRunLoopSourceGetTypeID(void); + +CF_EXPORT CFRunLoopSourceRef CFRunLoopSourceCreate(CFAllocatorRef allocator, CFIndex order, CFRunLoopSourceContext *context); + +CF_EXPORT CFIndex CFRunLoopSourceGetOrder(CFRunLoopSourceRef source); +CF_EXPORT void CFRunLoopSourceInvalidate(CFRunLoopSourceRef source); +CF_EXPORT Boolean CFRunLoopSourceIsValid(CFRunLoopSourceRef source); +CF_EXPORT void CFRunLoopSourceGetContext(CFRunLoopSourceRef source, CFRunLoopSourceContext *context); +CF_EXPORT void CFRunLoopSourceSignal(CFRunLoopSourceRef source); + +typedef struct { + CFIndex version; + void * info; + const void *(*retain)(const void *info); + void (*release)(const void *info); + CFStringRef (*copyDescription)(const void *info); +} CFRunLoopObserverContext; + +typedef void (*CFRunLoopObserverCallBack)(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info); + +CF_EXPORT CFTypeID CFRunLoopObserverGetTypeID(void); + +CF_EXPORT CFRunLoopObserverRef CFRunLoopObserverCreate(CFAllocatorRef allocator, CFOptionFlags activities, Boolean repeats, CFIndex order, CFRunLoopObserverCallBack callout, CFRunLoopObserverContext *context); + +CF_EXPORT CFOptionFlags CFRunLoopObserverGetActivities(CFRunLoopObserverRef observer); +CF_EXPORT Boolean CFRunLoopObserverDoesRepeat(CFRunLoopObserverRef observer); +CF_EXPORT CFIndex CFRunLoopObserverGetOrder(CFRunLoopObserverRef observer); +CF_EXPORT void CFRunLoopObserverInvalidate(CFRunLoopObserverRef observer); +CF_EXPORT Boolean CFRunLoopObserverIsValid(CFRunLoopObserverRef observer); +CF_EXPORT void CFRunLoopObserverGetContext(CFRunLoopObserverRef observer, CFRunLoopObserverContext *context); + +typedef struct { + CFIndex version; + void * info; + const void *(*retain)(const void *info); + void (*release)(const void *info); + CFStringRef (*copyDescription)(const void *info); +} CFRunLoopTimerContext; + +typedef void (*CFRunLoopTimerCallBack)(CFRunLoopTimerRef timer, void *info); + +CF_EXPORT CFTypeID CFRunLoopTimerGetTypeID(void); + +CF_EXPORT CFRunLoopTimerRef CFRunLoopTimerCreate(CFAllocatorRef allocator, CFAbsoluteTime fireDate, CFTimeInterval interval, CFOptionFlags flags, CFIndex order, CFRunLoopTimerCallBack callout, CFRunLoopTimerContext *context); +CF_EXPORT CFAbsoluteTime CFRunLoopTimerGetNextFireDate(CFRunLoopTimerRef timer); +CF_EXPORT void CFRunLoopTimerSetNextFireDate(CFRunLoopTimerRef timer, CFAbsoluteTime fireDate); +CF_EXPORT CFTimeInterval CFRunLoopTimerGetInterval(CFRunLoopTimerRef timer); +CF_EXPORT Boolean CFRunLoopTimerDoesRepeat(CFRunLoopTimerRef timer); +CF_EXPORT CFIndex CFRunLoopTimerGetOrder(CFRunLoopTimerRef timer); +CF_EXPORT void CFRunLoopTimerInvalidate(CFRunLoopTimerRef timer); +CF_EXPORT Boolean CFRunLoopTimerIsValid(CFRunLoopTimerRef timer); +CF_EXPORT void CFRunLoopTimerGetContext(CFRunLoopTimerRef timer, CFRunLoopTimerContext *context); + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFRUNLOOP__ */ +
diff --git a/CoreFoundation/CFRunLoopPriv.h b/CoreFoundation/CFRunLoopPriv.h new file mode 100644 index 0000000..b346271 --- /dev/null +++ b/CoreFoundation/CFRunLoopPriv.h
@@ -0,0 +1,387 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFRunLoopPriv.h + Copyright (c) 2006-2007, Apple Inc. All rights reserved. +*/ + +#if (DEPLOYMENT_TARGET_MACOSX || 0) + +/* -------- -------- -------- -------- -------- -------- -------- -------- */ + +#import "CFObject.h" +#include <CoreFoundation/CFRunLoop.h> +#include <CoreFoundation/CFMachPort.h> +#include <CoreFoundation/CFSocket.h> +#include <CoreFoundation/CFArray.h> +#include <CoreFoundation/CFBag.h> +#include <mach/mach.h> +#include <sys/event.h> +#include <pthread.h> + +@class CFRunLoopSourceSet; +typedef CFRunLoopSourceContext CFRunLoopSourceContext0; +typedef CFMachPortContext CFRunLoopMachPortContext; +typedef CFSocketContext CFRunLoopSocketContext; + +/* -------- -------- -------- -------- -------- -------- -------- -------- */ + +@interface CFRunLoopSource : CFObject { + uint8_t _cfruntime_[8]; // large enough for 32-bit or 64-bit + int _kq; + mach_port_t _portset; + mach_port_t _port; + CFIndex _order; + uint8_t _invalid; + uint8_t _firing; + uint8_t _ownsPort; + uint8_t _async; +} + +- (int)kqueue; +- (mach_port_t)machPortSet; +- (mach_port_t)machPort; + +- (void)setOrder:(CFIndex)o; +- (CFIndex)order; + +- (void)setAsyncStrategy:(uint8_t)s; +- (uint8_t)asyncStrategy; + +- (void)perform:(mach_msg_header_t *)msg; + +- (void)invalidate; +- (Boolean)isValid; + +- (void)noteAddedToSourceSet:(CFRunLoopSourceSet *)ss; +- (void)noteRemovedFromSourceSet:(CFRunLoopSourceSet *)ss; + +- (CFStringRef)copyPartialDebugDescription; // subclasses override +- (CFStringRef)copyDebugDescription; + +@end + +@interface CFRunLoopVersion0SourceCFRef : CFRunLoopSource { + CFRunLoopSourceContext0 _context; +} + +- (Boolean)setContext:(CFRunLoopSourceContext0)c; +- (CFRunLoopSourceContext0)context; + +- (void)markReady; +- (void)handle; + +- (void)scheduleInRunLoop:(CFRunLoopRef)rl mode:(CFStringRef)n; +- (void)cancelFromRunLoop:(CFRunLoopRef)rl mode:(CFStringRef)n; + +@end + +@interface CFRunLoopVersion1SourceCFRef : CFRunLoopSource { + CFRunLoopSourceContext1 _context; +} + +- (Boolean)setContext:(CFRunLoopSourceContext1)c; +- (CFRunLoopSourceContext1)context; + +- (void)markReady; +- (void)handle:(mach_msg_header_t *)msg; + +@end + +/* -------- -------- -------- -------- -------- -------- -------- -------- */ + +@interface CFRunLoopTimerSource : CFRunLoopSource { + CFAbsoluteTime _fireAT; + CFTimeInterval _interval; +} + +- (void)setFireTime:(CFAbsoluteTime)at; +- (CFAbsoluteTime)fireTime; + +- (void)setInterval:(CFTimeInterval)i; +- (CFTimeInterval)interval; + +- (void)handle; + +@end + +@interface CFRunLoopTimerSourceCFRef : CFRunLoopTimerSource { + void *_function; + CFRunLoopTimerContext _context; +} + +- (void)setFunction:(void *)f; +- (void *)function; + +- (Boolean)setContext:(CFRunLoopTimerContext)c; +- (CFRunLoopTimerContext)context; + +@end + +/* -------- -------- -------- -------- -------- -------- -------- -------- */ + +@interface CFRunLoopMachPortSource : CFRunLoopSource { + mach_port_t _notifyPort; + mach_port_t _oldNotifyPort; +} + ++ (id)newWithPort:(mach_port_t)p; + +- (void)handle:(mach_msg_header_t *)msg; + +@end + +@interface CFRunLoopMachPortSourceCFRef : CFRunLoopMachPortSource { + void *_function; + CFRunLoopMachPortContext _context; + void *_invalidation; +} + +- (void)setFunction:(void *)f; +- (void *)function; + +- (Boolean)setContext:(CFRunLoopMachPortContext)c; +- (CFRunLoopMachPortContext)context; + +- (void)setInvalidationFunction:(void *)f; +- (void *)invalidationFunction; + +@end + +/* -------- -------- -------- -------- -------- -------- -------- -------- */ + +@interface CFRunLoopKEventSource : CFRunLoopSource { + struct kevent _filter; +} + +- (Boolean)setFilter:(struct kevent)kev; +- (struct kevent)filter; + +- (void)handle:(struct kevent *)kev; + +@end + +/* -------- -------- -------- -------- -------- -------- -------- -------- */ + +@interface CFRunLoopSignalSource : CFRunLoopSource { + int _signal; +} + +- (Boolean)setSignal:(int)sig; +- (int)signal; + +- (long)poll; + +- (void)handle:(long)n; + +@end + +/* -------- -------- -------- -------- -------- -------- -------- -------- */ + +@interface CFRunLoopProcessDeathSource : CFRunLoopSource { + int _pid; +} + +- (Boolean)setProcessID:(int)pid; +- (int)processID; + +- (void)handle; + +@end + +/* -------- -------- -------- -------- -------- -------- -------- -------- */ + +@interface CFRunLoopSocketSource : CFRunLoopSource { + int _socket; + uint8_t _ownsSocket; + uint8_t _enabledR; + uint8_t _enabledW; +} + ++ (id)newWithSocket:(int)s; + +- (int)socket; +- (CFDataRef)copyLocalAddress; +- (CFDataRef)copyRemoteAddress; + +- (void)setReadEventsEnabled:(Boolean)b; +- (Boolean)readEventsEnabled; + +- (void)setWriteEventsEnabled:(Boolean)b; +- (Boolean)writeEventsEnabled; + +- (void)handleReadability:(CFIndex)amt endOfFile:(Boolean)b; +- (void)performRead:(struct kevent *)kev; + +- (void)handleWritability:(CFIndex)amt endOfFile:(Boolean)b; +- (void)performWrite:(struct kevent *)kev; + +@end + +@interface CFRunLoopSocketSourceCFRef : CFRunLoopSocketSource { + void *_function; + CFRunLoopSocketContext _context; + uint8_t _callbacks; + uint8_t _flags; + uint8_t _disabled; +} + +- (void)setFunction:(void *)f; +- (void *)function; + +- (Boolean)setContext:(CFRunLoopSocketContext)c; +- (CFRunLoopSocketContext)context; + +- (void)setCallBackTypes:(uint8_t)f; +- (uint8_t)callBackTypes; + +- (void)setFlags:(uint8_t)f; +- (uint8_t)flags; + +- (void)setDisabledFlags:(uint8_t)f; +- (uint8_t)disabledFlags; + +- (Boolean)handleAcceptError:(int)err; +- (Boolean)handleReadError:(int)err; + +@end + +/* -------- -------- -------- -------- -------- -------- -------- -------- */ + +@interface CFRunLoopObserver : CFRunLoopSource { +} + +- (void)observeEntry:(CFRunLoopSourceSet *)ss; +- (void)observeBeforeWaiting:(CFRunLoopSourceSet *)ss; +- (void)observeAfterWaiting:(CFRunLoopSourceSet *)ss; +- (void)observeExit:(CFRunLoopSourceSet *)ss; + +@end + +@interface CFRunLoopObserverCFRef : CFRunLoopObserver { + void *_function; + CFRunLoopObserverContext _context; + CFOptionFlags _activities; + uint8_t _oneshot; +} + +- (void)setFunction:(void *)f; +- (void *)function; + +- (Boolean)setContext:(CFRunLoopObserverContext)c; +- (CFRunLoopObserverContext)context; + +- (void)setActivities:(CFOptionFlags)a; +- (CFOptionFlags)activities; + +- (void)setOneshot:(Boolean)b; +- (Boolean)oneshot; + +@end + +/* -------- -------- -------- -------- -------- -------- -------- -------- */ + +@interface CFRunLoopSourceSet : CFRunLoopObserver { + CFMutableBagRef _sources; + CFStringRef _name; + pthread_t _thread; + CFMutableArrayRef _observers[4]; + CFRunLoopTimerSource *_timeoutTimer; + CFRunLoopMachPortSource *_wakeupPort; + uint8_t _stopped; + uint8_t _waiting; +} + ++ (void)removeSourceFromAllSets:(CFRunLoopSource *)src; + +- (void)setName:(CFStringRef)n; +- (CFStringRef)name; + +- (void)setAffineThread:(pthread_t)t; +- (pthread_t)affineThread; + +- (Boolean)containsObserver:(CFRunLoopObserver *)o; +- (void)addObserver:(CFRunLoopObserver *)o activities:(CFRunLoopActivity)a; +- (void)removeObserver:(CFRunLoopObserver *)o activities:(CFRunLoopActivity)a; + +- (Boolean)containsSource:(CFRunLoopSource *)src; +- (void)addSource:(CFRunLoopSource *)src; +- (void)removeSource:(CFRunLoopSource *)src; + +- (void)forEachSource:(Boolean (*)(CFRunLoopSource *, void *))f context:(void *)c; + +- (void)stop; +- (void)wakeup; +- (Boolean)isWaiting; +- (Boolean)isEmpty; +- (Boolean)hasInputAvailable; + +- (int32_t)serviceUntil:(CFAbsoluteTime)at handleOne:(Boolean)handleOne; + +@end + +/* -------- -------- -------- -------- -------- -------- -------- -------- */ + +@interface CFRunLoopCFRef : CFObject { + uint8_t _cfruntime_[8]; // large enough for 32-bit or 64-bit + pthread_t _thread; + void *_counterpart; + CFMutableArrayRef _sourceSets; + CFRunLoopSourceSet *_currentSet; + uint8_t _invalid; +} + +- (void)setAffineThread:(pthread_t)t; +- (pthread_t)affineThread; + +- (void)setCounterpart:(void *)c; +- (void *)counterpart; + +- (void)invalidate; +- (Boolean)isValid; + +- (CFArrayRef)copySourceSets; + +- (void)setCurrentSourceSet:(CFRunLoopSourceSet *)ss; +- (CFRunLoopSourceSet *)currentSourceSet; + +- (CFRunLoopSourceSet *)lookupSourceSetWithName:(CFStringRef)n; +- (CFRunLoopSourceSet *)lookupOrCreateSourceSetWithName:(CFStringRef)n; + +- (CFStringRef)copyDebugDescription; + +@end + +/* -------- -------- -------- -------- -------- -------- -------- -------- */ + +#endif +
diff --git a/CoreFoundation/CFRuntime.c b/CoreFoundation/CFRuntime.c new file mode 100644 index 0000000..2aad365 --- /dev/null +++ b/CoreFoundation/CFRuntime.c
@@ -0,0 +1,1131 @@ +/* + * 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@ + */ +/* CFRuntime.c + Copyright 1999-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#define ENABLE_ZOMBIES 1 + +#include "CFRuntime.h" +#include "CFInternal.h" +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#if DEPLOYMENT_TARGET_MACOSX +#include <dlfcn.h> +#include <mach-o/dyld.h> +#include <monitor.h> +#include <crt_externs.h> +#include <unistd.h> +#else +#include <locale.h> +#endif +#include "auto_stubs.h" + +#define __CFRecordAllocationEvent(a, b, c, d, e) ((void)0) + +enum { +// retain/release recording constants -- must match values +// used by OA for now; probably will change in the future +__kCFRetainEvent = 28, +__kCFReleaseEvent = 29 +}; + +#if DEPLOYMENT_TARGET_MACOSX +#include <malloc/malloc.h> +#elif DEPLOYMENT_TARGET_WINDOWS +#include <malloc.h> +/* On Win32 we should use _msize instead of malloc_size + * (Aleksey Dukhnyakov) + */ +CF_INLINE size_t malloc_size(void* memblock) { + return _msize(memblock); +} +#endif + +extern void __HALT(void); + +static CFTypeID __kCFNotATypeTypeID = _kCFRuntimeNotATypeID; + +#if !defined (__cplusplus) +static const CFRuntimeClass __CFNotATypeClass = { + 0, + "Not A Type", + (void *)__HALT, + (void *)__HALT, + (void *)__HALT, + (void *)__HALT, + (void *)__HALT, + (void *)__HALT, + (void *)__HALT +}; + +static CFTypeID __kCFTypeTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFTypeClass = { + 0, + "CFType", + (void *)__HALT, + (void *)__HALT, + (void *)__HALT, + (void *)__HALT, + (void *)__HALT, + (void *)__HALT, + (void *)__HALT +}; +#else +void SIG1(CFTypeRef){__HALT();};; +CFTypeRef SIG2(CFAllocatorRef,CFTypeRef){__HALT();return NULL;}; +Boolean SIG3(CFTypeRef,CFTypeRef){__HALT();return FALSE;}; +CFHashCode SIG4(CFTypeRef){__HALT(); return 0;}; +CFStringRef SIG5(CFTypeRef,CFDictionaryRef){__HALT();return NULL;}; +CFStringRef SIG6(CFTypeRef){__HALT();return NULL;}; + +static const CFRuntimeClass __CFNotATypeClass = { + 0, + "Not A Type", + SIG1, + SIG2, + SIG1, + SIG3, + SIG4, + SIG5, + SIG6 +}; + +static CFTypeID __kCFTypeTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFTypeClass = { + 0, + "CFType", + SIG1, + SIG2, + SIG1, + SIG3, + SIG4, + SIG5, + SIG6 +}; +#endif //__cplusplus + +static CFRuntimeClass ** __CFRuntimeClassTable = NULL; +int32_t __CFRuntimeClassTableSize = 0; +static int32_t __CFRuntimeClassTableCount = 0; + +#if DEPLOYMENT_TARGET_MACOSX +__private_extern__ void * (*__CFSendObjCMsg)(const void *, SEL, ...) = NULL; +#endif + +__private_extern__ malloc_zone_t *__CFCollectableZone = NULL; + +bool (*__CFObjCIsCollectable)(void *) = NULL; + +static const void* objc_AssignIvar_none(const void *value, const void *base, const void **slot) { return (*slot = value); } +const void* (*__CFObjCAssignIvar)(const void *value, const void *base, const void **slot) = objc_AssignIvar_none; + +static const void* objc_StrongAssign_none(const void *value, const void **slot) { return (*slot = value); } +const void* (*__CFObjCStrongAssign)(const void *value, const void **slot) = objc_StrongAssign_none; + +void* (*__CFObjCMemmoveCollectable)(void *dst, const void *, size_t) = memmove; + +// GC: to be moved to objc if necessary. +static void objc_WriteBarrierRange_none(void *ptr, size_t size) {} +static void objc_WriteBarrierRange_auto(void *ptr, size_t size) { auto_zone_write_barrier_range(__CFCollectableZone, ptr, size); } +void (*__CFObjCWriteBarrierRange)(void *, size_t) = objc_WriteBarrierRange_none; + +// Compiler uses this symbol name; must match compiler built-in decl +#if __LP64__ +int __CFConstantStringClassReference[24] = {0}; +#else +int __CFConstantStringClassReference[12] = {0}; +#endif + +// #warning the whole business of reallocating the ClassTables is not thread-safe, because access to those values is not protected + +CFTypeID _CFRuntimeRegisterClass(const CFRuntimeClass * const cls) { +// version field must be 0 +// className must be pure ASCII string, non-null + if (__CFMaxRuntimeTypes <= __CFRuntimeClassTableCount) { + CFLog(kCFLogLevelWarning, CFSTR("*** CoreFoundation class table full; registration failing for class '%s'. Program will crash soon."), cls->className); + return _kCFRuntimeNotATypeID; + } + if (__CFRuntimeClassTableSize <= __CFRuntimeClassTableCount) { + int32_t old_size = __CFRuntimeClassTableSize; + int32_t new_size = __CFRuntimeClassTableSize * 4; + + void *new_table1 = calloc(new_size, sizeof(CFRuntimeClass *)); + memmove(new_table1, __CFRuntimeClassTable, old_size * sizeof(CFRuntimeClass *)); + __CFRuntimeClassTable = (CFRuntimeClass**)new_table1; + __CFRuntimeClassTableSize = new_size; + // The old value of __CFRuntimeClassTable is intentionally leaked + // for thread-safety reasons: + // other threads might have loaded the value of that, in functions here + // in this file executing in other threads, and may attempt to use it after + // this thread gets done reallocating here, so freeing is unsafe. We + // don't want to pay the expense of locking around all uses of these variables. + // The old value of __CFRuntimeObjCClassTable is intentionally leaked + // for thread-safety reasons: + // other threads might have loaded the value of that, since it is + // accessible via CFBridgingPriv.h, and may attempt to use it after + // this thread gets done reallocating here, so freeing is unsafe. + } + __CFRuntimeClassTable[__CFRuntimeClassTableCount++] = (CFRuntimeClass *)cls; + return __CFRuntimeClassTableCount - 1; +} + +void _CFRuntimeBridgeClasses(CFTypeID cf_typeID, const char *objc_classname) { + return; +} + +const CFRuntimeClass * _CFRuntimeGetClassWithTypeID(CFTypeID typeID) { + return __CFRuntimeClassTable[typeID]; +} + +void _CFRuntimeUnregisterClassWithTypeID(CFTypeID typeID) { + __CFRuntimeClassTable[typeID] = NULL; +} + + +#if defined(DEBUG) || defined(ENABLE_ZOMBIES) + +/* CFZombieLevel levels: + * bit 0: scribble deallocated CF object memory + * bit 1: do not scribble on CFRuntimeBase header (when bit 0) + * bit 4: do not free CF objects + * bit 7: use 3rd-order byte as scribble byte for dealloc (otherwise 0xFC) + */ + +static uint32_t __CFZombieLevel = 0x0; +static uint8_t __CFZombieEnabled = 0; +static uint8_t __CFDeallocateZombies = 0; +static void *_original_objc_dealloc = 0; + +#endif /* DEBUG */ + +// XXX_PCB: use the class version field as a bitmask, to allow classes to opt-in for GC scanning. + +#define CF_GET_COLLECTABLE_MEMORY_TYPE(x) (0) + +CFTypeRef _CFRuntimeCreateInstance(CFAllocatorRef allocator, CFTypeID typeID, CFIndex extraBytes, unsigned char *category) { + CFRuntimeBase *memory; + Boolean usesSystemDefaultAllocator; + CFIndex size; + + CFAssert1(typeID != _kCFRuntimeNotATypeID, __kCFLogAssertion, "%s(): Uninitialized type id", __PRETTY_FUNCTION__); + + //XXXBS Check __CFRuntimeClasstable for NULL before trying to access elements in it + if ((NULL == __CFRuntimeClassTable) || (NULL == __CFRuntimeClassTable[typeID])) { + return NULL; + } + allocator = (NULL == allocator) ? __CFGetDefaultAllocator() : allocator; + usesSystemDefaultAllocator = (allocator == kCFAllocatorSystemDefault); + size = sizeof(CFRuntimeBase) + extraBytes + (usesSystemDefaultAllocator ? 0 : sizeof(CFAllocatorRef)); + size = (size + 0xF) & ~0xF; // CF objects are multiples of 16 in size + // CFType version 0 objects are unscanned by default since they don't have write-barriers and hard retain their innards + // CFType version 1 objects are scanned and use hand coded write-barriers to store collectable storage within + memory = (CFRuntimeBase *)CFAllocatorAllocate(allocator, size, CF_GET_COLLECTABLE_MEMORY_TYPE(__CFRuntimeClassTable[typeID])); + if (NULL == memory) { + return NULL; + } +#if DEPLOYMENT_TARGET_WINDOWS + // malloc_size won't work if the memory address has been moved + // (such as a custom allocator that adds its own metadata + // (e.g. under/overflow guard data), so don't attempt to call it + // on the allocation return. + size_t msize = (usesSystemDefaultAllocator) ? malloc_size(memory) : size; +#elif DEPLOYMENT_TARGET_LINUX + // On Linux, across various architectures and versions of the GNU + // C Library, malloc_usable_size has been observed to at times return + // incorrect values. Since the introspection value-add it provides to + // CoreFoundation seems to be of limited utility, we eliminate it and + // simpy use the caller-requested size. In addition, memsetting this + // small tail block has also been found to cause heap consistency + // problems. + size_t msize = size; +#else + size_t msize = malloc_size(memory); +#endif + memset(memory, 0, msize); + if (__CFOASafe && category) { + __CFSetLastAllocationEventName(memory, (char *)category); + } else if (__CFOASafe) { + __CFSetLastAllocationEventName(memory, (char *)__CFRuntimeClassTable[typeID]->className); + } + if (!usesSystemDefaultAllocator) { + // add space to hold allocator ref for non-standard allocators. + // (this screws up 8 byte alignment but seems to work) + *(CFAllocatorRef *)((char *)memory) = (CFAllocatorRef)CFRetain(allocator); + memory = (CFRuntimeBase *)((char *)memory + sizeof(CFAllocatorRef)); + } + memory->_cfisa = __CFISAForTypeID(typeID); +#if __LP64__ + *(uint32_t *)(memory->_cfinfo) = (uint32_t)((0 << 24) + ((typeID & 0xFFFF) << 8) + (usesSystemDefaultAllocator ? 0x80 : 0x00)); + memory->_rc = 1; +#else + *(uint32_t *)(memory->_cfinfo) = (uint32_t)((1 << 24) + ((typeID & 0xFFFF) << 8) + (usesSystemDefaultAllocator ? 0x80 : 0x00)); +#endif + if (NULL != __CFRuntimeClassTable[typeID]->init) { + (__CFRuntimeClassTable[typeID]->init)(memory); + } + return memory; +} + +void _CFRuntimeInitStaticInstance(void *ptr, CFTypeID typeID) { + CFRuntimeBase *memory = (CFRuntimeBase *)ptr; + CFAssert1(typeID != _kCFRuntimeNotATypeID, __kCFLogAssertion, "%s(): Uninitialized type id", __PRETTY_FUNCTION__); + if (NULL == __CFRuntimeClassTable[typeID]) { + return; + } + memory->_cfisa = __CFISAForTypeID(typeID); + *(uint32_t *)(memory->_cfinfo) = (uint32_t)((0 << 24) + ((typeID & 0xFFFF) << 8) + 0x80); +#if __LP64__ + memory->_rc = 0; +#endif + if (NULL != __CFRuntimeClassTable[typeID]->init) { + (__CFRuntimeClassTable[typeID]->init)(memory); + } +} + +void _CFRuntimeSetInstanceTypeID(CFTypeRef cf, CFTypeID typeID) { + *(uint16_t *)(((CFRuntimeBase *)cf)->_cfinfo + 1) = (uint16_t)(typeID & 0xFFFF); +} + +#if 0 +// BAF: This seems to be some garbage collection magic. Not for us mortals to look at! +__private_extern__ Boolean __CFRuntimeIsFreedObject(id anObject) { + if (!anObject) return false; + static Class freedClass = Nil; + if (!freedClass) freedClass = _objc_getFreedObjectClass(); + Class cls = object_getClass(anObject); + if (cls == freedClass) return true; + // in 64-bit, a future class has nil isa, and calling class_getName() on + // such will crash so we do this test; zombie classes are not future classes + if (objc_getClass((id)cls) == nil) return false; + const char *cname = class_getName(cls); + if (cname && 0 == strncmp(cname, "_NSZombie_", 10)) return true; + return false; +} +#endif + +CFTypeID __CFGenericTypeID(const void *cf) { + return (*(uint32_t *)(((CFRuntimeBase *)cf)->_cfinfo) >> 8) & 0xFFFF; +} + +CF_INLINE CFTypeID __CFGenericTypeID_inline(const void *cf) { + return (*(uint32_t *)(((CFRuntimeBase *)cf)->_cfinfo) >> 8) & 0xFFFF; +} + +CFTypeID CFTypeGetTypeID(void) { + return __kCFTypeTypeID; +} + +__private_extern__ void __CFGenericValidateType_(CFTypeRef cf, CFTypeID type, const char *func) { + if (cf && CF_IS_OBJC(type, cf)) return; + CFAssert2((cf != NULL) && (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]) && (__kCFNotATypeTypeID != __CFGenericTypeID_inline(cf)) && (__kCFTypeTypeID != __CFGenericTypeID_inline(cf)), __kCFLogAssertion, "%s(): pointer %p is not a CF object", func, cf); \ + CFAssert3(__CFGenericTypeID_inline(cf) == type, __kCFLogAssertion, "%s(): pointer %p is not a %s", func, cf, __CFRuntimeClassTable[type]->className); \ +} + +#define __CFGenericAssertIsCF(cf) \ + CFAssert2(cf != NULL && (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]) && (__kCFNotATypeTypeID != __CFGenericTypeID_inline(cf)) && (__kCFTypeTypeID != __CFGenericTypeID_inline(cf)), __kCFLogAssertion, "%s(): pointer %p is not a CF object", __PRETTY_FUNCTION__, cf); + +#define CFTYPE_IS_OBJC(obj) (false) +#define CFTYPE_OBJC_FUNCDISPATCH0(rettype, obj, sel) do {} while (0) +#define CFTYPE_OBJC_FUNCDISPATCH1(rettype, obj, sel, a1) do {} while (0) + +CFTypeID CFGetTypeID(CFTypeRef cf) { +#if defined(DEBUG) + if (NULL == cf) HALT; +#endif + CFTYPE_OBJC_FUNCDISPATCH0(CFTypeID, cf, "_cfTypeID"); + __CFGenericAssertIsCF(cf); + return __CFGenericTypeID_inline(cf); +} + +CFStringRef CFCopyTypeIDDescription(CFTypeID type) { + CFAssert2((NULL != __CFRuntimeClassTable[type]) && __kCFNotATypeTypeID != type && __kCFTypeTypeID != type, __kCFLogAssertion, "%s(): type %d is not a CF type ID", __PRETTY_FUNCTION__, type); + return CFStringCreateWithCString(kCFAllocatorSystemDefault, __CFRuntimeClassTable[type]->className, kCFStringEncodingASCII); +} + +#define DISGUISE(object) ((void *)(((uintptr_t)object) + 1)) +#define UNDISGUISE(disguised) ((id)(((uintptr_t)disguised) - 1)) + +// Bit 31 (highest bit) in second word of cf instance indicates external ref count + +CF_EXPORT void _CFRelease(CFTypeRef cf); +CF_EXPORT CFTypeRef _CFRetain(CFTypeRef cf); +CF_EXPORT CFHashCode _CFHash(CFTypeRef cf); + +CFTypeRef CFRetain(CFTypeRef cf) { + if (CF_IS_COLLECTABLE(cf)) { + // always honor CFRetain's with a GC-visible retain. + auto_zone_retain(__CFCollectableZone, (void*)cf); + return cf; + } + CFTYPE_OBJC_FUNCDISPATCH0(CFTypeRef, cf, "retain"); + if (cf) __CFGenericAssertIsCF(cf); + return _CFRetain(cf); +} + +__private_extern__ void __CFAllocatorDeallocate(CFTypeRef cf); + +void CFRelease(CFTypeRef cf) { +#if !DEPLOYMENT_TARGET_WINDOWS + if (CF_IS_COLLECTABLE(cf)) { + // release the GC-visible reference. + if (auto_zone_release(__CFCollectableZone, (void*)cf) == 0 && !CFTYPE_IS_OBJC(cf)) { + CFRuntimeClass *cfClass = __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]; + if (cfClass->version & _kCFRuntimeResourcefulObject) { + if (cfClass->reclaim) cfClass->reclaim(cf); + } + } + return; + } +#endif + CFTYPE_OBJC_FUNCDISPATCH0(void, cf, "release"); + if (cf) __CFGenericAssertIsCF(cf); + _CFRelease(cf); +} + + +__private_extern__ const void *__CFStringCollectionCopy(CFAllocatorRef allocator, const void *ptr) { + CFStringRef theString = (CFStringRef)ptr; + CFStringRef result = CFStringCreateCopy(allocator, theString); + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + result = (CFStringRef)CFMakeCollectable(result); + } + return (const void *)result; +} + +extern void CFCollection_non_gc_storage_error(void); + +__private_extern__ const void *__CFTypeCollectionRetain(CFAllocatorRef allocator, const void *ptr) { + CFTypeRef cf = (CFTypeRef)ptr; + // only collections allocated in the GC zone can opt-out of reference counting. + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + if (CFTYPE_IS_OBJC(cf)) return cf; // do nothing for OBJC objects. + if (auto_zone_is_valid_pointer(__CFCollectableZone, ptr)) { + CFRuntimeClass *cfClass = __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]; + if (cfClass->version & _kCFRuntimeResourcefulObject) { + // GC: If this a CF object in the GC heap that is marked resourceful, then + // it must be retained keep it alive in a CF collection. + // We're basically inlining CFRetain() here, to avoid an extra heap membership test. + auto_zone_retain(__CFCollectableZone, (void*)cf); + } + else + ; // don't retain normal CF objects + return cf; + } else { + // support constant CFTypeRef objects. +#if __LP64__ + uint32_t lowBits = ((CFRuntimeBase *)cf)->_rc; +#else + uint32_t lowBits = ((CFRuntimeBase *)cf)->_cfinfo[CF_RC_BITS]; +#endif + if (lowBits == 0) return cf; + // complain about non-GC objects in GC containers. + CFLog(kCFLogLevelWarning, CFSTR("storing a non-GC object %p in a GC collection, break on CFCollection_non_gc_storage_error to debug."), cf); + CFCollection_non_gc_storage_error(); + // XXX should halt, except Patrick is using this somewhere. + // HALT; + } + } + return CFRetain(cf); +} + + +__private_extern__ void __CFTypeCollectionRelease(CFAllocatorRef allocator, const void *ptr) { + CFTypeRef cf = (CFTypeRef)ptr; + // only collections allocated in the GC zone can opt-out of reference counting. + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + if (CFTYPE_IS_OBJC(cf)) return; // do nothing for OBJC objects. + if (auto_zone_is_valid_pointer(__CFCollectableZone, cf)) { +#if !DEPLOYMENT_TARGET_WINDOWS + // GC: If this a CF object in the GC heap that is marked uncollectable, then + // must balance the retain done in __CFTypeCollectionRetain(). + // We're basically inlining CFRelease() here, to avoid an extra heap membership test. + CFRuntimeClass *cfClass = __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]; + if (cfClass->version & _kCFRuntimeResourcefulObject && auto_zone_release(__CFCollectableZone, (void*)cf) == 0) { + // ResourceFull objects trigger 'reclaim' on transition to zero + if (cfClass->reclaim) cfClass->reclaim(cf); + } + else // avoid releasing normal CF objects. Like other collections, for example + ; + return; +#endif + } else { + // support constant CFTypeRef objects. +#if __LP64__ + uint32_t lowBits = ((CFRuntimeBase *)cf)->_rc; +#else + uint32_t lowBits = ((CFRuntimeBase *)cf)->_cfinfo[CF_RC_BITS]; +#endif + if (lowBits == 0) return; + } + } + CFRelease(cf); +} + +#if !__LP64__ +static CFSpinLock_t __CFRuntimeExternRefCountTableLock = CFSpinLockInit; +static CFMutableBagRef __CFRuntimeExternRefCountTable = NULL; +#endif + +static uint64_t __CFGetFullRetainCount(CFTypeRef cf) { +#if __LP64__ + uint32_t lowBits = ((CFRuntimeBase *)cf)->_rc; + if (0 == lowBits) { + return (uint64_t)0x0fffffffffffffffULL; + } + return lowBits; +#else + uint32_t lowBits = ((CFRuntimeBase *)cf)->_cfinfo[CF_RC_BITS]; + if (0 == lowBits) { + return (uint64_t)0x0fffffffffffffffULL; + } + uint64_t highBits = 0; + if ((lowBits & 0x80) != 0) { + __CFSpinLock(&__CFRuntimeExternRefCountTableLock); + highBits = (uint64_t)CFBagGetCountOfValue(__CFRuntimeExternRefCountTable, DISGUISE(cf)); + __CFSpinUnlock(&__CFRuntimeExternRefCountTableLock); + } + uint64_t compositeRC = (lowBits & 0x7f) + (highBits << 6); + return compositeRC; +#endif +} + +CFTypeRef _CFRetainGC(CFTypeRef cf) { +#if defined(DEBUG) + if (CF_USING_COLLECTABLE_MEMORY && !CF_IS_COLLECTABLE(cf)) { + fprintf(stderr, "non-auto object %p passed to _CFRetainGC.\n", cf); + HALT; + } +#endif + return CF_USING_COLLECTABLE_MEMORY ? cf : CFRetain(cf); +} + +void _CFReleaseGC(CFTypeRef cf) { +#if defined(DEBUG) + if (CF_USING_COLLECTABLE_MEMORY && !CF_IS_COLLECTABLE(cf)) { + fprintf(stderr, "non-auto object %p passed to _CFReleaseGC.\n", cf); + HALT; + } +#endif + if (!CF_USING_COLLECTABLE_MEMORY) CFRelease(cf); +} + +CFIndex CFGetRetainCount(CFTypeRef cf) { + if (NULL == cf) return 0; + if (CF_IS_COLLECTABLE(cf)) { + return auto_zone_retain_count(__CFCollectableZone, cf); + } + CFTYPE_OBJC_FUNCDISPATCH0(CFIndex, cf, "retainCount"); + __CFGenericAssertIsCF(cf); + uint64_t rc = __CFGetFullRetainCount(cf); + return (rc < (uint64_t)LONG_MAX) ? (CFIndex)rc : (CFIndex)LONG_MAX; +} + +CFTypeRef CFMakeCollectable(CFTypeRef cf) { + if (NULL == cf) return NULL; + if (CF_IS_COLLECTABLE(cf)) { +#if defined(DEBUG) + CFAllocatorRef allocator = CFGetAllocator(cf); + if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + CFLog(kCFLogLevelWarning, CFSTR("object %p with non-GC allocator %p passed to CFMakeCollectable."), cf, allocator); + HALT; + } +#endif + if (!CFTYPE_IS_OBJC(cf)) { + CFRuntimeClass *cfClass = __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]; + if (cfClass->version & (_kCFRuntimeResourcefulObject)) { + // don't allow the collector to manage uncollectable objects. + CFLog(kCFLogLevelWarning, CFSTR("uncollectable object %p passed to CFMakeCollectable."), cf); + HALT; + } + } + if (auto_zone_retain_count(__CFCollectableZone, cf) == 0) { + CFLog(kCFLogLevelWarning, CFSTR("object %p with 0 retain-count passed to CFMakeCollectable."), cf); + return cf; + } + auto_zone_release(__CFCollectableZone, (void *)cf); + } + return cf; +} + +Boolean CFEqual(CFTypeRef cf1, CFTypeRef cf2) { +#if defined(DEBUG) + if (NULL == cf1) HALT; + if (NULL == cf2) HALT; +#endif + if (cf1 == cf2) return true; + CFTYPE_OBJC_FUNCDISPATCH1(Boolean, cf1, "isEqual:", cf2); + CFTYPE_OBJC_FUNCDISPATCH1(Boolean, cf2, "isEqual:", cf1); + __CFGenericAssertIsCF(cf1); + __CFGenericAssertIsCF(cf2); + if (__CFGenericTypeID_inline(cf1) != __CFGenericTypeID_inline(cf2)) return false; + if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf1)]->equal) { + return __CFRuntimeClassTable[__CFGenericTypeID_inline(cf1)]->equal(cf1, cf2); + } + return false; +} + +CFHashCode CFHash(CFTypeRef cf) { + CFTYPE_OBJC_FUNCDISPATCH0(CFHashCode, cf, "hash"); + __CFGenericAssertIsCF(cf); + return _CFHash(cf); +} + +// definition: produces a normally non-NULL debugging description of the object +CFStringRef CFCopyDescription(CFTypeRef cf) { +#if defined(DEBUG) + if (NULL == cf) HALT; +#endif + __CFGenericAssertIsCF(cf); + if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->copyDebugDesc) { + CFStringRef result; + result = __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->copyDebugDesc(cf); + if (NULL != result) return result; + } + return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<%s %p [%p]>"), __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->className, cf, CFGetAllocator(cf)); +} + +// Definition: if type produces a formatting description, return that string, otherwise NULL +__private_extern__ CFStringRef __CFCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) { +#if defined(DEBUG) + if (NULL == cf) HALT; +#endif + __CFGenericAssertIsCF(cf); + if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->copyFormattingDesc) { + return __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->copyFormattingDesc(cf, formatOptions); + } + return NULL; +} + +extern CFAllocatorRef __CFAllocatorGetAllocator(CFTypeRef); + +CFAllocatorRef CFGetAllocator(CFTypeRef cf) { + if (NULL == cf) return kCFAllocatorSystemDefault; + if (__kCFAllocatorTypeID_CONST == __CFGenericTypeID_inline(cf)) { + return __CFAllocatorGetAllocator(cf); + } + return __CFGetAllocator(cf); +} + +extern void __CFBaseInitialize(void); +extern void __CFNullInitialize(void); +extern void __CFAllocatorInitialize(void); +extern void __CFStringInitialize(void); +extern void __CFArrayInitialize(void); +extern void __CFBagInitialize(void); +extern void __CFBooleanInitialize(void); +extern void __CFCharacterSetInitialize(void); +extern void __CFLocaleInitialize(void); +#if DEPLOYMENT_TARGET_WINDOWS +extern CFStringRef __CFLocaleWindowsLCIDToISOLocaleNameUsingHyphen(LCID id); +#endif +extern void __CFDataInitialize(void); +extern void __CFDateInitialize(void); +extern void __CFDictionaryInitialize(void); +extern void __CFNumberInitialize(void); +extern void __CFSetInitialize(void); +extern void __CFStorageInitialize(void); +extern void __CFErrorInitialize(void); +extern void __CFTimeZoneInitialize(void); +extern void __CFTreeInitialize(void); +extern void __CFURLInitialize(void); +#if DEPLOYMENT_TARGET_MACOSX +extern void __CFMachPortInitialize(void); +#endif +#if DEPLOYMENT_TARGET_MACOSX +extern void __CFMessagePortInitialize(void); +#endif +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS +extern void __CFRunLoopInitialize(void); +extern void __CFRunLoopObserverInitialize(void); +extern void __CFRunLoopSourceInitialize(void); +extern void __CFRunLoopTimerInitialize(void); +extern void __CFSocketInitialize(void); +#endif +extern void __CFBundleInitialize(void); +extern void __CFPlugInInitialize(void); +extern void __CFPlugInInstanceInitialize(void); +extern void __CFUUIDInitialize(void); +extern void __CFBinaryHeapInitialize(void); +extern void __CFBitVectorInitialize(void); +#if DEPLOYMENT_TARGET_WINDOWS +extern void __CFWindowsMessageQueueInitialize(void); +extern void __CFBaseCleanup(void); +#endif +extern void __CFStreamInitialize(void); +extern void __CFPreferencesDomainInitialize(void); +extern void __CFUserNotificationInitialize(void); + +#if DEPLOYMENT_TARGET_MACOSX +static void __exceptionInit(void) {} +static void __collatorInit(void) {} +static void __forwarding_prep_0___(void) {} +static void __forwarding_prep_1___(void) {} +static void __NSFastEnumerationMutationHandler(id obj) {} +#endif +const void *__CFArgStuff = NULL; +__private_extern__ void *__CFAppleLanguages = NULL; + +bool kCFUseCollectableAllocator = false; + + +#if DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD +static void __CFInitialize(void) __attribute__ ((constructor)); +static +#elif DEPLOYMENT_TARGET_WINDOWS +CF_EXPORT +#endif +void __CFInitialize(void) { + static int __done = 0; + + if (!__done) { + __done = 1; + +#if defined(DEBUG) || defined(ENABLE_ZOMBIES) + const char *value = getenv("NSZombieEnabled"); + if (value && (*value == 'Y' || *value == 'y')) __CFZombieEnabled = 0xff; + value = getenv("NSDeallocateZombies"); + if (value && (*value == 'Y' || *value == 'y')) __CFDeallocateZombies = 0xff; + + value = getenv("CFZombieLevel"); + if (NULL != value) { +#if DEPLOYMENT_TARGET_MACOSX + __CFZombieLevel = (uint32_t)strtoul_l(value, NULL, 0, NULL); +#else + __CFZombieLevel = (uint32_t)strtoul(value, NULL, 0); +#endif + } + if (0x0 == __CFZombieLevel) __CFZombieLevel = 0x0000FC00; // default +#endif + + __CFRuntimeClassTableSize = 1024; + __CFRuntimeClassTable = (CFRuntimeClass **)calloc(__CFRuntimeClassTableSize, sizeof(CFRuntimeClass *)); + __CFBaseInitialize(); + + /* Here so that two runtime classes get indices 0, 1. */ + __kCFNotATypeTypeID = _CFRuntimeRegisterClass(&__CFNotATypeClass); + __kCFTypeTypeID = _CFRuntimeRegisterClass(&__CFTypeClass); + + /* Here so that __kCFAllocatorTypeID gets index 2. */ + __CFAllocatorInitialize(); + +#if DEPLOYMENT_TARGET_MACOSX + { + CFIndex idx, cnt; + char **args = *_NSGetArgv(); + cnt = *_NSGetArgc(); + for (idx = 1; idx < cnt - 1; idx++) { + if (NULL == args[idx]) continue; + if (0 == strcmp(args[idx], "-AppleLanguages") && args[idx + 1]) { + CFIndex length = strlen(args[idx + 1]); + __CFAppleLanguages = malloc(length + 1); + memmove(__CFAppleLanguages, args[idx + 1], length + 1); + break; + } + } + } +#endif + + + /* CFBag needs to be up before CFString. */ + __CFBagInitialize(); + +#if !__LP64__ + // Creating this lazily in CFRetain causes recursive call to CFRetain + __CFRuntimeExternRefCountTable = CFBagCreateMutable(kCFAllocatorSystemDefault, 0, NULL); +#endif + + /*** _CFRuntimeCreateInstance() can finally be called generally after this line. ***/ + + __CFRuntimeClassTableCount = 7; + __CFStringInitialize(); // CFString's TypeID must be 0x7, now and forever + __CFRuntimeClassTableCount = 16; + __CFDictionaryInitialize(); + __CFArrayInitialize(); + __CFDataInitialize(); + __CFSetInitialize(); + __CFNullInitialize(); // See above for hard-coding of this position + __CFBooleanInitialize(); // See above for hard-coding of this position + __CFNumberInitialize(); // See above for hard-coding of this position + + + __CFDateInitialize(); // just initializes the time goo +// _CFRuntimeBridgeClasses(CFDateGetTypeID(), objc_lookUpClass("NSCFDate") ? "NSCFDate" : "__NSCFDate"); + __CFTimeZoneInitialize(); +// _CFRuntimeBridgeClasses(CFTimeZoneGetTypeID(), "NSCFTimeZone"); + __CFBinaryHeapInitialize(); + __CFBitVectorInitialize(); + __CFCharacterSetInitialize(); +#if DEPLOYMENT_TARGET_WINDOWS + __CFLocaleInitialize(); +#endif + __CFStorageInitialize(); + __CFErrorInitialize(); + __CFTreeInitialize(); + __CFURLInitialize(); + __CFBundleInitialize(); +#if DEPLOYMENT_TARGET_MACOSX + __CFPlugInInitialize(); + __CFPlugInInstanceInitialize(); +#endif //__MACH__ + __CFUUIDInitialize(); +#if DEPLOYMENT_TARGET_MACOSX + __CFMessagePortInitialize(); + __CFMachPortInitialize(); +#endif + __CFStreamInitialize(); + __CFPreferencesDomainInitialize(); +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX + __CFRunLoopInitialize(); + __CFRunLoopObserverInitialize(); + __CFRunLoopSourceInitialize(); + __CFRunLoopTimerInitialize(); + __CFSocketInitialize(); +#endif + + +#if DEPLOYMENT_TARGET_MACOSX + { + CFIndex idx, cnt; + char **args; + args = *_NSGetArgv(); + cnt = *_NSGetArgc(); + CFIndex count; + CFStringRef *list, buffer[256]; + list = (cnt <= 256) ? (CFStringRef*)buffer : (CFStringRef*)malloc(cnt * sizeof(CFStringRef)); + for (idx = 0, count = 0; idx < cnt; idx++) { + if (NULL == args[idx]) continue; + list[count] = CFStringCreateWithCString(kCFAllocatorSystemDefault, args[idx], kCFStringEncodingUTF8); + if (NULL == list[count]) { + list[count] = CFStringCreateWithCString(kCFAllocatorSystemDefault, args[idx], kCFStringEncodingISOLatin1); + // We CANNOT use the string SystemEncoding here; + // Do not argue: it is not initialized yet, but these + // arguments MUST be initialized before it is. + // We should just ignore the argument if the UTF-8 + // conversion fails, but out of charity we try once + // more with ISO Latin1, a standard unix encoding. + } + if (NULL != list[count]) count++; + } + __CFArgStuff = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)list, count, &kCFTypeArrayCallBacks); + } +#endif + _CFProcessPath(); // cache this early + + if (__CFRuntimeClassTableCount < 256) __CFRuntimeClassTableCount = 256; + +#if defined(DEBUG) && !DEPLOYMENT_TARGET_WINDOWS + CFLog(kCFLogLevelWarning, CFSTR("Assertions enabled")); +#endif + } +} + +#ifdef _BUILD_NET_FOUNDATION_ +#ifdef __cplusplus +extern "C"{ +#endif //C++ +extern void _CFFTPCleanup(void); +extern void _CFHTTPMessageCleanup(void); +extern void _CFHTTPStreamCleanup(void); +#ifdef __cplusplus +} +#endif //C++ + +#endif //_BUILD_NET_FOUNDATION_ + +#if DEPLOYMENT_TARGET_WINDOWS + +extern void __CFBaseCleanup (void); +extern void __CFSocketCleanup (void); +extern void __CFStreamCleanup (void); +extern void __CFStringCleanup (void); +extern void __CFUniCharCleanup (void); + +/* We have to call __CFInitialize when library is attached to the process. + * (Sergey Zubarev) + */ +#if defined(_BUILD_NET_FOUNDATION_) +extern "C" { +BOOL WINAPI CoreFoundationDllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID pReserved ); +} + +BOOL WINAPI CoreFoundationDllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID pReserved ) { +#else +BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID pReserved ) { +#endif + static CFBundleRef cfBundle = NULL; + if (dwReason == DLL_PROCESS_ATTACH) { + __CFInitialize(); + //cfBundle = RegisterCoreFoundationBundle(); -- Not in sources! + } else if (dwReason == DLL_PROCESS_DETACH) { + if (cfBundle) CFRelease(cfBundle); + __CFStringCleanup(); + __CFSocketCleanup(); + __CFUniCharCleanup(); + __CFStreamCleanup(); + __CFBaseCleanup(); + } else if (dwReason == DLL_THREAD_DETACH) { + __CFFinalizeThreadData(NULL); + } + return TRUE; +} + +#endif + +// Functions that avoid ObC dispatch and CF type validation, for use by NSNotifyingCFArray, etc. +// Hopefully all of this will just go away. 3321464. M.P. To Do - 7/9/03 + +Boolean _CFEqual(CFTypeRef cf1, CFTypeRef cf2) { + if (cf1 == cf2) return true; + if (NULL == cf1) return false; + if (NULL == cf2) return false; + __CFGenericAssertIsCF(cf1); + __CFGenericAssertIsCF(cf2); + if (__CFGenericTypeID_inline(cf1) != __CFGenericTypeID_inline(cf2)) return false; + if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf1)]->equal) { + return __CFRuntimeClassTable[__CFGenericTypeID_inline(cf1)]->equal(cf1, cf2); + } + return false; +} + +CFIndex _CFGetRetainCount(CFTypeRef cf) { + if (NULL == cf) return 0; + if (CF_IS_COLLECTABLE(cf)) { + return auto_zone_retain_count(__CFCollectableZone, cf); + } + uint64_t rc = __CFGetFullRetainCount(cf); + return (rc < (uint64_t)LONG_MAX) ? (CFIndex)rc : (CFIndex)LONG_MAX; +} + +CFHashCode _CFHash(CFTypeRef cf) { + if (NULL == cf) return 0; + if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->hash) { + return __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->hash(cf); + } + return (CFHashCode)cf; +} + +CF_EXPORT CFTypeRef _CFRetain(CFTypeRef cf) { + if (NULL == cf) return NULL; +#if __LP64__ + uint32_t lowBits; + do { + lowBits = ((CFRuntimeBase *)cf)->_rc; + if (0 == lowBits) return cf; // Constant CFTypeRef + } while (!_CFAtomicCompareAndSwap32Barrier(lowBits, lowBits + 1, (int32_t *)&((CFRuntimeBase *)cf)->_rc)); +#else +#define RC_START 24 +#define RC_END 31 + volatile UInt32 *infoLocation = (UInt32 *)&(((CFRuntimeBase *)cf)->_cfinfo); + CFIndex rcLowBits = __CFBitfieldGetValue(*infoLocation, RC_END, RC_START); + if (__builtin_expect(0 == rcLowBits, 0)) return cf; // Constant CFTypeRef + bool success = 0; + do { + UInt32 initialCheckInfo = *infoLocation; + UInt32 prospectiveNewInfo = initialCheckInfo; // don't want compiler to generate prospectiveNewInfo = *infoLocation. This is why infoLocation is declared as a pointer to volatile memory. + prospectiveNewInfo += (1 << RC_START); + rcLowBits = __CFBitfieldGetValue(prospectiveNewInfo, RC_END, RC_START); + if (__builtin_expect((rcLowBits & 0x7f) == 0, 0)) { + /* Roll over another bit to the external ref count + Real ref count = low 7 bits of info[CF_RC_BITS] + external ref count << 6 + Bit 8 of low bits indicates that external ref count is in use. + External ref count is shifted by 6 rather than 7 so that we can set the low + bits to to 1100 0000 rather than 1000 0000. + This prevents needing to access the external ref count for successive retains and releases + when the composite retain count is right around a multiple of 1 << 7. + */ + prospectiveNewInfo = initialCheckInfo; + __CFBitfieldSetValue(prospectiveNewInfo, RC_END, RC_START, ((1 << 7) | (1 << 6))); + __CFSpinLock(&__CFRuntimeExternRefCountTableLock); + success = _CFAtomicCompareAndSwap32Barrier(*(int32_t *)&initialCheckInfo, *(int32_t *)&prospectiveNewInfo, (int32_t *)infoLocation); + if (__builtin_expect(success, 1)) { + CFBagAddValue(__CFRuntimeExternRefCountTable, DISGUISE(cf)); + } + __CFSpinUnlock(&__CFRuntimeExternRefCountTableLock); + } else { + success = _CFAtomicCompareAndSwap32Barrier(*(int32_t *)&initialCheckInfo, *(int32_t *)&prospectiveNewInfo, (int32_t *)infoLocation); + } + } while (__builtin_expect(!success, 0)); +#endif + if (__builtin_expect(__CFOASafe, 0)) { + __CFRecordAllocationEvent(__kCFRetainEvent, (void *)cf, 0, _CFGetRetainCount(cf), NULL); + } + return cf; +} + +CF_EXPORT void _CFRelease(CFTypeRef cf) { + Boolean isAllocator = false; +#if __LP64__ + uint32_t lowBits; + do { + lowBits = ((CFRuntimeBase *)cf)->_rc; + if (0 == lowBits) return; // Constant CFTypeRef + if (1 == lowBits) { + // CANNOT WRITE ANY NEW VALUE INTO [CF_RC_BITS] UNTIL AFTER FINALIZATION + CFTypeID typeID = __CFGenericTypeID_inline(cf); + isAllocator = (__kCFAllocatorTypeID_CONST == typeID); + CFRuntimeClass *cfClass = __CFRuntimeClassTable[typeID]; + if (cfClass->version & _kCFRuntimeResourcefulObject && cfClass->reclaim != NULL) { + cfClass->reclaim(cf); + } + void (*func)(CFTypeRef) = __CFRuntimeClassTable[typeID]->finalize; + if (NULL != func) { + func(cf); + } + // We recheck lowBits to see if the object has been retained again during + // the finalization process. This allows for the finalizer to resurrect, + // but the main point is to allow finalizers to be able to manage the + // removal of objects from uniquing caches, which may race with other threads + // which are allocating (looking up and finding) objects from those caches, + // which (that thread) would be the thing doing the extra retain in that case. + if (isAllocator || _CFAtomicCompareAndSwap32Barrier(1, 0, (int32_t *)&((CFRuntimeBase *)cf)->_rc)) { + goto really_free; + } + } + } while (!_CFAtomicCompareAndSwap32Barrier(lowBits, lowBits - 1, (int32_t *)&((CFRuntimeBase *)cf)->_rc)); +#else + volatile UInt32 *infoLocation = (UInt32 *)&(((CFRuntimeBase *)cf)->_cfinfo); + CFIndex rcLowBits = __CFBitfieldGetValue(*infoLocation, RC_END, RC_START); + if (__builtin_expect(0 == rcLowBits, 0)) return; // Constant CFTypeRef + bool success = 0; + do { + UInt32 initialCheckInfo = *infoLocation; + rcLowBits = __CFBitfieldGetValue(initialCheckInfo, RC_END, RC_START); + if (__builtin_expect(1 == rcLowBits, 0)) { + // we think cf should be deallocated + if (__builtin_expect(__kCFAllocatorTypeID_CONST == __CFGenericTypeID_inline(cf), 0)) { + if (__builtin_expect(__CFOASafe, 0)) __CFRecordAllocationEvent(__kCFReleaseEvent, (void *)cf, 0, 0, NULL); + __CFAllocatorDeallocate((void *)cf); + success = 1; + } else { + // CANNOT WRITE ANY NEW VALUE INTO [CF_RC_BITS] UNTIL AFTER FINALIZATION + CFTypeID typeID = __CFGenericTypeID_inline(cf); + CFRuntimeClass *cfClass = __CFRuntimeClassTable[typeID]; + if (cfClass->version & _kCFRuntimeResourcefulObject && cfClass->reclaim != NULL) { + cfClass->reclaim(cf); + } + if (NULL != __CFRuntimeClassTable[typeID]->finalize) { + __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->finalize(cf); + } + // We recheck rcLowBits to see if the object has been retained again during + // the finalization process. This allows for the finalizer to resurrect, + // but the main point is to allow finalizers to be able to manage the + // removal of objects from uniquing caches, which may race with other threads + // which are allocating (looking up and finding) objects from those caches, + // which (that thread) would be the thing doing the extra retain in that case. + rcLowBits = __CFBitfieldGetValue(*infoLocation, RC_END, RC_START); + success = (1 == rcLowBits); + if (__builtin_expect(success, 1)) { + goto really_free; + } + } + } else { + // not yet junk + UInt32 prospectiveNewInfo = initialCheckInfo; // don't want compiler to generate prospectiveNewInfo = *infoLocation. This is why infoLocation is declared as a pointer to volatile memory. + if (__builtin_expect((1 << 7) == rcLowBits, 0)) { + // Time to remove a bit from the external ref count + __CFSpinLock(&__CFRuntimeExternRefCountTableLock); + CFIndex rcHighBitsCnt = CFBagGetCountOfValue(__CFRuntimeExternRefCountTable, DISGUISE(cf)); + if (1 == rcHighBitsCnt) { + __CFBitfieldSetValue(prospectiveNewInfo, RC_END, RC_START, (1 << 6) - 1); + } else { + __CFBitfieldSetValue(prospectiveNewInfo, RC_END, RC_START, ((1 << 6) | (1 << 7)) - 1); + } + success = _CFAtomicCompareAndSwap32Barrier(*(int32_t *)&initialCheckInfo, *(int32_t *)&prospectiveNewInfo, (int32_t *)infoLocation); + if (__builtin_expect(success, 1)) { + CFBagRemoveValue(__CFRuntimeExternRefCountTable, DISGUISE(cf)); + } + __CFSpinUnlock(&__CFRuntimeExternRefCountTableLock); + } else { + prospectiveNewInfo -= (1 << RC_START); + success = _CFAtomicCompareAndSwap32Barrier(*(int32_t *)&initialCheckInfo, *(int32_t *)&prospectiveNewInfo, (int32_t *)infoLocation); + } + } + } while (__builtin_expect(!success, 0)); + +#endif + if (__builtin_expect(__CFOASafe, 0)) { + __CFRecordAllocationEvent(__kCFReleaseEvent, (void *)cf, 0, _CFGetRetainCount(cf), NULL); + } + return; + + really_free:; + if (__builtin_expect(__CFOASafe, 0)) { + // do not use _CFGetRetainCount() because cf has been freed if it was an allocator + __CFRecordAllocationEvent(__kCFReleaseEvent, (void *)cf, 0, 0, NULL); + } + // cannot zombify allocators, which get deallocated by __CFAllocatorDeallocate (finalize) + if (!isAllocator) { + CFAllocatorRef allocator; + Boolean usesSystemDefaultAllocator; + + if (__CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 7, 7)) { + allocator = kCFAllocatorSystemDefault; + } else { + allocator = CFGetAllocator(cf); + } + usesSystemDefaultAllocator = (allocator == kCFAllocatorSystemDefault); + + if (__CFZombieLevel & (1 << 0)) { +#if !DEPLOYMENT_TARGET_LINUX + uint8_t *ptr = (uint8_t *)cf - (usesSystemDefaultAllocator ? 0 : sizeof(CFAllocatorRef)); + size_t size = malloc_size(ptr); + uint8_t byte = 0xFC; + if (__CFZombieLevel & (1 << 1)) { + ptr = (uint8_t *)cf + sizeof(CFRuntimeBase); + size = size - sizeof(CFRuntimeBase) - (usesSystemDefaultAllocator ? 0 : sizeof(CFAllocatorRef)); + } + if (__CFZombieLevel & (1 << 7)) { + byte = (__CFZombieLevel >> 8) & 0xFF; + } + memset(ptr, byte, size); +#endif /* !DEPLOYMENT_TARGET_LINUX */ + } + if (!(__CFZombieLevel & (1 << 4))) { + CFAllocatorDeallocate(allocator, (uint8_t *)cf - (usesSystemDefaultAllocator ? 0 : sizeof(CFAllocatorRef))); + } + + if (kCFAllocatorSystemDefault != allocator) { + CFRelease(allocator); + } + } +} + +#undef __kCFAllocatorTypeID_CONST +#undef __CFGenericAssertIsCF +
diff --git a/CoreFoundation/CFRuntime.h b/CoreFoundation/CFRuntime.h new file mode 100644 index 0000000..d81d656 --- /dev/null +++ b/CoreFoundation/CFRuntime.h
@@ -0,0 +1,389 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFRuntime.h + Copyright (c) 1999-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFRUNTIME__) +#define __COREFOUNDATION_CFRUNTIME__ 1 + +#include <CoreFoundation/CFBase.h> +#include <CoreFoundation/CFDictionary.h> +#include <stddef.h> + +CF_EXTERN_C_BEGIN + +// GC: until we link against ObjC must use indirect functions. Overridden in CFSetupFoundationBridging +extern bool kCFUseCollectableAllocator; +extern bool (*__CFObjCIsCollectable)(void *); +extern const void* (*__CFObjCAssignIvar)(const void *value, const void *base, const void **slot); +extern const void* (*__CFObjCStrongAssign)(const void *value, const void **slot); +extern void* (*__CFObjCMemmoveCollectable)(void *dest, const void *src, size_t); +extern void (*__CFObjCWriteBarrierRange)(void *, size_t); + +// GC: primitives. +// is GC on? +#define CF_USING_COLLECTABLE_MEMORY (kCFUseCollectableAllocator) +// is GC on and is this the GC allocator? +#define CF_IS_COLLECTABLE_ALLOCATOR(allocator) (CF_USING_COLLECTABLE_MEMORY && (NULL == (allocator) || kCFAllocatorSystemDefault == (allocator))) +// is this allocated by the collector? +#define CF_IS_COLLECTABLE(obj) (__CFObjCIsCollectable ? __CFObjCIsCollectable((void*)obj) : false) + +// XXX_PCB for generational GC support. + +CF_INLINE const void* __CFAssignIvar(CFAllocatorRef allocator, const void *rvalue, const void *base, const void **lvalue) { + if (rvalue && CF_IS_COLLECTABLE_ALLOCATOR(allocator)) + return __CFObjCAssignIvar(rvalue, base, lvalue); + else + return (*lvalue = rvalue); +} + +CF_INLINE const void* __CFStrongAssign(CFAllocatorRef allocator, const void *rvalue, const void **lvalue) { + if (rvalue && CF_IS_COLLECTABLE_ALLOCATOR(allocator)) + return __CFObjCStrongAssign(rvalue, lvalue); + else + return (*lvalue = rvalue); +} + +// Use this form when the base pointer to the object is known. +#define CF_WRITE_BARRIER_BASE_ASSIGN(allocator, base, lvalue, rvalue) __CFAssignIvar(allocator, (const void*)rvalue, (const void*)base, (const void**)&(lvalue)) + +// Use this form when the base pointer to the object isn't known. +#define CF_WRITE_BARRIER_ASSIGN(allocator, lvalue, rvalue) __CFStrongAssign(allocator, (const void*)rvalue, (const void**)&(lvalue)) + +// Write-barrier memory move. +#define CF_WRITE_BARRIER_MEMMOVE(dst, src, size) __CFObjCMemmoveCollectable(dst, src, size) + +// Used by frameworks to assert they "KNOW WHAT THEY'RE DOING under GC." +CF_EXPORT CFAllocatorRef _CFAllocatorCreateGC(CFAllocatorRef allocator, CFAllocatorContext *context); + +// Zero-retain count CFAllocator functions, i.e. memory that will be collected, no dealloc necessary +CF_EXPORT void *_CFAllocatorAllocateGC(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint); +CF_EXPORT void *_CFAllocatorReallocateGC(CFAllocatorRef allocator, void *ptr, CFIndex newsize, CFOptionFlags hint); +CF_EXPORT void _CFAllocatorDeallocateGC(CFAllocatorRef allocator, void *ptr); + +enum { + _kCFRuntimeNotATypeID = 0, + _kCFRuntimeScannedObject = (1 << 0), + /* _kCFRuntimeUncollectableObject = (1 << 1), No longer used; obsolete. */ + _kCFRuntimeResourcefulObject = (1 << 2) +}; + +typedef struct __CFRuntimeClass { // Version 0 struct + CFIndex version; + const char *className; + void (*init)(CFTypeRef cf); + CFTypeRef (*copy)(CFAllocatorRef allocator, CFTypeRef cf); +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED + void (*finalize)(CFTypeRef cf); +#else + void (*dealloc)(CFTypeRef cf); +#endif + Boolean (*equal)(CFTypeRef cf1, CFTypeRef cf2); + CFHashCode (*hash)(CFTypeRef cf); + CFStringRef (*copyFormattingDesc)(CFTypeRef cf, CFDictionaryRef formatOptions); // str with retain + CFStringRef (*copyDebugDesc)(CFTypeRef cf); // str with retain +#if MAC_OS_X_VERSION_10_5 <= MAC_OS_X_VERSION_MAX_ALLOWED +#define CF_RECLAIM_AVAILABLE 1 + void (*reclaim)(CFTypeRef cf); +#endif +} CFRuntimeClass; + +#define RADAR_5115468_FIXED 1 + +/* Note that CF runtime class registration and unregistration is not currently + * thread-safe, which should not currently be a problem, as long as unregistration + * is done only when valid to do so. + */ + +CF_EXPORT CFTypeID _CFRuntimeRegisterClass(const CFRuntimeClass * const cls); + /* Registers a new class with the CF runtime. Pass in a + * pointer to a CFRuntimeClass structure. The pointer is + * remembered by the CF runtime -- the structure is NOT + * copied. + * + * - version field must be zero currently. + * - className field points to a null-terminated C string + * containing only ASCII (0 - 127) characters; this field + * may NOT be NULL. + * - init field points to a function which classes can use to + * apply some generic initialization to instances as they + * are created; this function is called by both + * _CFRuntimeCreateInstance and _CFRuntimeInitInstance; if + * this field is NULL, no function is called; the instance + * has been initialized enough that the polymorphic funcs + * CFGetTypeID(), CFRetain(), CFRelease(), CFGetRetainCount(), + * and CFGetAllocator() are valid on it when the init + * function if any is called. + * - finalize field points to a function which destroys an + * instance when the retain count has fallen to zero; if + * this is NULL, finalization does nothing. Note that if + * the class-specific functions which create or initialize + * instances more fully decide that a half-initialized + * instance must be destroyed, the finalize function for + * that class has to be able to deal with half-initialized + * instances. The finalize function should NOT destroy the + * memory for the instance itself; that is done by the + * CF runtime after this finalize callout returns. + * - equal field points to an equality-testing function; this + * field may be NULL, in which case only pointer/reference + * equality is performed on instances of this class. + * Pointer equality is tested, and the type IDs are checked + * for equality, before this function is called (so, the + * two instances are not pointer-equal but are of the same + * class before this function is called). + * NOTE: the equal function must implement an immutable + * equality relation, satisfying the reflexive, symmetric, + * and transitive properties, and remains the same across + * time and immutable operations (that is, if equal(A,B) at + * some point, then later equal(A,B) provided neither + * A or B has been mutated). + * - hash field points to a hash-code-computing function for + * instances of this class; this field may be NULL in which + * case the pointer value of an instance is converted into + * a hash. + * NOTE: the hash function and equal function must satisfy + * the relationship "equal(A,B) implies hash(A) == hash(B)"; + * that is, if two instances are equal, their hash codes must + * be equal too. (However, the converse is not true!) + * - copyFormattingDesc field points to a function returning a + * CFStringRef with a human-readable description of the + * instance; if this is NULL, the type does not have special + * human-readable string-formats. + * - copyDebugDesc field points to a function returning a + * CFStringRef with a debugging description of the instance; + * if this is NULL, a simple description is generated. + * + * This function returns _kCFRuntimeNotATypeID on failure, or + * on success, returns the CFTypeID for the new class. This + * CFTypeID is what the class uses to allocate or initialize + * instances of the class. It is also returned from the + * conventional *GetTypeID() function, which returns the + * class's CFTypeID so that clients can compare the + * CFTypeID of instances with that of a class. + * + * The function to compute a human-readable string is very + * optional, and is really only interesting for classes, + * like strings or numbers, where it makes sense to format + * the instance using just its contents. + */ + +CF_EXPORT const CFRuntimeClass * _CFRuntimeGetClassWithTypeID(CFTypeID typeID); + /* Returns the pointer to the CFRuntimeClass which was + * assigned the specified CFTypeID. + */ + +CF_EXPORT void _CFRuntimeUnregisterClassWithTypeID(CFTypeID typeID); + /* Unregisters the class with the given type ID. It is + * undefined whether type IDs are reused or not (expect + * that they will be). + * + * Whether or not unregistering the class is a good idea or + * not is not CF's responsibility. In particular you must + * be quite sure all instances are gone, and there are no + * valid weak refs to such in other threads. + */ + +/* All CF "instances" start with this structure. Never refer to + * these fields directly -- they are for CF's use and may be added + * to or removed or change format without warning. Binary + * compatibility for uses of this struct is not guaranteed from + * release to release. + */ +typedef struct __CFRuntimeBase { + uintptr_t _cfisa; + uint8_t _cfinfo[4]; +#if __LP64__ + uint32_t _rc; +#endif +} CFRuntimeBase; + +#if __BIG_ENDIAN__ +#define INIT_CFRUNTIME_BASE(...) {0, {0, 0, 0, 0x80}} +#else +#define INIT_CFRUNTIME_BASE(...) {0, {0x80, 0, 0, 0}} +#endif + +CF_EXPORT CFTypeRef _CFRuntimeCreateInstance(CFAllocatorRef allocator, CFTypeID typeID, CFIndex extraBytes, unsigned char *category); + /* Creates a new CF instance of the class specified by the + * given CFTypeID, using the given allocator, and returns it. + * If the allocator returns NULL, this function returns NULL. + * A CFRuntimeBase structure is initialized at the beginning + * of the returned instance. extraBytes is the additional + * number of bytes to allocate for the instance (BEYOND that + * needed for the CFRuntimeBase). If the specified CFTypeID + * is unknown to the CF runtime, this function returns NULL. + * No part of the new memory other than base header is + * initialized (the extra bytes are not zeroed, for example). + * All instances created with this function must be destroyed + * only through use of the CFRelease() function -- instances + * must not be destroyed by using CFAllocatorDeallocate() + * directly, even in the initialization or creation functions + * of a class. Pass NULL for the category parameter. + */ + +CF_EXPORT void _CFRuntimeSetInstanceTypeID(CFTypeRef cf, CFTypeID typeID); + /* This function changes the typeID of the given instance. + * If the specified CFTypeID is unknown to the CF runtime, + * this function does nothing. This function CANNOT be used + * to initialize an instance. It is for advanced usages such + * as faulting. + */ + +CF_EXPORT void _CFRuntimeInitStaticInstance(void *memory, CFTypeID typeID); + /* This function initializes a memory block to be a constant + * (unreleaseable) CF object of the given typeID. + * If the specified CFTypeID is unknown to the CF runtime, + * this function does nothing. The memory block should + * be a chunk of in-binary writeable static memory, and at + * least as large as sizeof(CFRuntimeBase) on the platform + * the code is being compiled for. The init function of the + * CFRuntimeClass is invoked on the memory as well, if the + * class has one. + */ +#define CF_HAS_INIT_STATIC_INSTANCE 1 + +#if 0 +// ========================= EXAMPLE ========================= + +// Example: EXRange -- a "range" object, which keeps the starting +// location and length of the range. ("EX" as in "EXample"). + +// ---- API ---- + +typedef const struct __EXRange * EXRangeRef; + +CFTypeID EXRangeGetTypeID(void); + +EXRangeRef EXRangeCreate(CFAllocatorRef allocator, uint32_t location, uint32_t length); + +uint32_t EXRangeGetLocation(EXRangeRef rangeref); +uint32_t EXRangeGetLength(EXRangeRef rangeref); + + +// ---- implementation ---- + +#include <CoreFoundation/CFBase.h> +#include <CoreFoundation/CFString.h> + +struct __EXRange { + CFRuntimeBase _base; + uint32_t _location; + uint32_t _length; +}; + +static Boolean __EXRangeEqual(CFTypeRef cf1, CFTypeRef cf2) { + EXRangeRef rangeref1 = (EXRangeRef)cf1; + EXRangeRef rangeref2 = (EXRangeRef)cf2; + if (rangeref1->_location != rangeref2->_location) return false; + if (rangeref1->_length != rangeref2->_length) return false; + return true; +} + +static CFHashCode __EXRangeHash(CFTypeRef cf) { + EXRangeRef rangeref = (EXRangeRef)cf; + return (CFHashCode)(rangeref->_location + rangeref->_length); +} + +static CFStringRef __EXRangeCopyFormattingDesc(CFTypeRef cf, CFDictionaryRef formatOpts) { + EXRangeRef rangeref = (EXRangeRef)cf; + return CFStringCreateWithFormat(CFGetAllocator(rangeref), formatOpts, + CFSTR("[%u, %u)"), + rangeref->_location, + rangeref->_location + rangeref->_length); +} + +static CFStringRef __EXRangeCopyDebugDesc(CFTypeRef cf) { + EXRangeRef rangeref = (EXRangeRef)cf; + return CFStringCreateWithFormat(CFGetAllocator(rangeref), NULL, + CFSTR("<EXRange %p [%p]>{loc = %u, len = %u}"), + rangeref, + CFGetAllocator(rangeref), + rangeref->_location, + rangeref->_length); +} + +static void __EXRangeEXRangeFinalize(CFTypeRef cf) { + EXRangeRef rangeref = (EXRangeRef)cf; + // nothing to finalize +} + +static CFTypeID _kEXRangeID = _kCFRuntimeNotATypeID; + +static CFRuntimeClass _kEXRangeClass = {0}; + +/* Something external to this file is assumed to call this + * before the EXRange class is used. + */ +void __EXRangeClassInitialize(void) { + _kEXRangeClass.version = 0; + _kEXRangeClass.className = "EXRange"; + _kEXRangeClass.init = NULL; + _kEXRangeClass.copy = NULL; + _kEXRangeClass.finalize = __EXRangeEXRangeFinalize; + _kEXRangeClass.equal = __EXRangeEqual; + _kEXRangeClass.hash = __EXRangeHash; + _kEXRangeClass.copyFormattingDesc = __EXRangeCopyFormattingDesc; + _kEXRangeClass.copyDebugDesc = __EXRangeCopyDebugDesc; + _kEXRangeID = _CFRuntimeRegisterClass((const CFRuntimeClass * const)&_kEXRangeClass); +} + +CFTypeID EXRangeGetTypeID(void) { + return _kEXRangeID; +} + +EXRangeRef EXRangeCreate(CFAllocatorRef allocator, uint32_t location, uint32_t length) { + struct __EXRange *newrange; + uint32_t extra = sizeof(struct __EXRange) - sizeof(CFRuntimeBase); + newrange = (struct __EXRange *)_CFRuntimeCreateInstance(allocator, _kEXRangeID, extra, NULL); + if (NULL == newrange) { + return NULL; + } + newrange->_location = location; + newrange->_length = length; + return (EXRangeRef)newrange; +} + +uint32_t EXRangeGetLocation(EXRangeRef rangeref) { + return rangeref->_location; +} + +uint32_t EXRangeGetLength(EXRangeRef rangeref) { + return rangeref->_length; +} + +#endif + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFRUNTIME__ */
diff --git a/CoreFoundation/CFSet.c b/CoreFoundation/CFSet.c new file mode 100644 index 0000000..2bb9d8b --- /dev/null +++ b/CoreFoundation/CFSet.c
@@ -0,0 +1,1484 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFSet.c + Copyright 1998-2006, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane + Machine generated from Notes/HashingCode.template +*/ + + + + +#include <CoreFoundation/CFSet.h> +#include "CFInternal.h" +#if DEPLOYMENT_TARGET_MACOSX +#include <mach-o/dyld.h> +#endif + +#define CFDictionary 0 +#define CFSet 0 +#define CFBag 0 +#undef CFSet +#define CFSet 1 + +#if CFDictionary +const CFSetKeyCallBacks kCFTypeSetKeyCallBacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; +const CFSetKeyCallBacks kCFCopyStringSetKeyCallBacks = {0, __CFStringCollectionCopy, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; +const CFSetValueCallBacks kCFTypeSetValueCallBacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, CFCopyDescription, CFEqual}; +static const CFSetKeyCallBacks __kCFNullSetKeyCallBacks = {0, NULL, NULL, NULL, NULL, NULL}; +static const CFSetValueCallBacks __kCFNullSetValueCallBacks = {0, NULL, NULL, NULL, NULL}; + +#define CFHashRef CFDictionaryRef +#define CFMutableHashRef CFMutableDictionaryRef +#define __kCFHashTypeID __kCFDictionaryTypeID +#endif + +#if CFSet +const CFSetCallBacks kCFTypeSetCallBacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; +const CFSetCallBacks kCFCopyStringSetCallBacks = {0, __CFStringCollectionCopy, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; +static const CFSetCallBacks __kCFNullSetCallBacks = {0, NULL, NULL, NULL, NULL, NULL}; + +#define CFSetKeyCallBacks CFSetCallBacks +#define CFSetValueCallBacks CFSetCallBacks +#define kCFTypeSetKeyCallBacks kCFTypeSetCallBacks +#define kCFTypeSetValueCallBacks kCFTypeSetCallBacks +#define __kCFNullSetKeyCallBacks __kCFNullSetCallBacks +#define __kCFNullSetValueCallBacks __kCFNullSetCallBacks + +#define CFHashRef CFSetRef +#define CFMutableHashRef CFMutableSetRef +#define __kCFHashTypeID __kCFSetTypeID +#endif + +#if CFBag +const CFSetCallBacks kCFTypeSetCallBacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; +const CFSetCallBacks kCFCopyStringSetCallBacks = {0, __CFStringCollectionCopy, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; +static const CFSetCallBacks __kCFNullSetCallBacks = {0, NULL, NULL, NULL, NULL, NULL}; + +#define CFSetKeyCallBacks CFSetCallBacks +#define CFSetValueCallBacks CFSetCallBacks +#define kCFTypeSetKeyCallBacks kCFTypeSetCallBacks +#define kCFTypeSetValueCallBacks kCFTypeSetCallBacks +#define __kCFNullSetKeyCallBacks __kCFNullSetCallBacks +#define __kCFNullSetValueCallBacks __kCFNullSetCallBacks + +#define CFHashRef CFBagRef +#define CFMutableHashRef CFMutableBagRef +#define __kCFHashTypeID __kCFBagTypeID +#endif + +#define GETNEWKEY(newKey, oldKey) \ + any_t (*kretain)(CFAllocatorRef, any_t, any_pointer_t) = \ + !hasBeenFinalized(hc) \ + ? (any_t (*)(CFAllocatorRef,any_t,any_pointer_t))__CFSetGetKeyCallBacks(hc)->retain \ + : (any_t (*)(CFAllocatorRef,any_t,any_pointer_t))0; \ + any_t newKey = kretain ? (any_t)INVOKE_CALLBACK3(kretain, allocator, (any_t)key, hc->_context) : (any_t)oldKey + +#define RELEASEKEY(oldKey) \ + void (*krelease)(CFAllocatorRef, any_t, any_pointer_t) = \ + !hasBeenFinalized(hc) \ + ? (void (*)(CFAllocatorRef,any_t,any_pointer_t))__CFSetGetKeyCallBacks(hc)->release \ + : (void (*)(CFAllocatorRef,any_t,any_pointer_t))0; \ + if (krelease) INVOKE_CALLBACK3(krelease, allocator, oldKey, hc->_context) + +#if CFDictionary +#define GETNEWVALUE(newValue) \ + any_t (*vretain)(CFAllocatorRef, any_t, any_pointer_t) = \ + !hasBeenFinalized(hc) \ + ? (any_t (*)(CFAllocatorRef,any_t,any_pointer_t))__CFSetGetValueCallBacks(hc)->retain \ + : (any_t (*)(CFAllocatorRef,any_t,any_pointer_t))0; \ + any_t newValue = vretain ? (any_t)INVOKE_CALLBACK3(vretain, allocator, (any_t)value, hc->_context) : (any_t)value + +#define RELEASEVALUE(oldValue) \ + void (*vrelease)(CFAllocatorRef, any_t, any_pointer_t) = \ + !hasBeenFinalized(hc) \ + ? (void (*)(CFAllocatorRef,any_t,any_pointer_t))__CFSetGetValueCallBacks(hc)->release \ + : (void (*)(CFAllocatorRef,any_t,any_pointer_t))0; \ + if (vrelease) INVOKE_CALLBACK3(vrelease, allocator, oldValue, hc->_context) + +#endif + +static void __CFSetHandleOutOfMemory(CFTypeRef obj, CFIndex numBytes) { + CFStringRef msg = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("Attempt to allocate %ld bytes for NS/CFSet failed"), numBytes); + CFBadErrorCallBack cb = _CFGetOutOfMemoryErrorCallBack(); + if (NULL == cb || !cb(obj, CFSTR("NS/CFSet"), msg)) { + CFLog(kCFLogLevelCritical, CFSTR("%@"), msg); + HALT; + } + CFRelease(msg); +} + + +// Max load is 3/4 number of buckets +CF_INLINE CFIndex __CFHashRoundUpCapacity(CFIndex capacity) { + return 3 * ((CFIndex)1 << (flsl((capacity - 1) / 3))); +} + +// Returns next power of two higher than the capacity +// threshold for the given input capacity. +CF_INLINE CFIndex __CFHashNumBucketsForCapacity(CFIndex capacity) { + return 4 * ((CFIndex)1 << (flsl((capacity - 1) / 3))); +} + +enum { /* Bits 1-0 */ + __kCFHashImmutable = 0, /* unchangable and fixed capacity */ + __kCFHashMutable = 1, /* changeable and variable capacity */ +}; + +enum { /* Bits 5-4 (value), 3-2 (key) */ + __kCFHashHasNullCallBacks = 0, + __kCFHashHasCFTypeCallBacks = 1, + __kCFHashHasCustomCallBacks = 3 /* callbacks are at end of header */ +}; + +// Under GC, we fudge the key/value memory in two ways +// First, if we had null callbacks or null for both retain/release, we use unscanned memory and get +// standard 'dangling' references. +// This means that if people were doing addValue:[xxx new] and never removing, well, that doesn't work +// +// Second, if we notice standard retain/release implementations we use scanned memory, and fudge the +// standard callbacks to generally do nothing if the collection was allocated in GC memory. On special +// CF objects, however, like those used for precious resources like video-card buffers, we do indeed +// do CFRetain on input and CFRelease on output. The tricky case is GC finalization; we need to remember +// that we did the CFReleases so that subsequent collection operations, like removal, don't double CFRelease. +// (In fact we don't really use CFRetain/CFRelease but go directly to the collector) +// + +enum { + __kCFHashFinalized = (1 << 7), + __kCFHashWeakKeys = (1 << 8), + __kCFHashWeakValues = (1 << 9) +}; + +typedef uintptr_t any_t; +typedef const void * const_any_pointer_t; +typedef void * any_pointer_t; + +struct __CFSet { + CFRuntimeBase _base; + CFIndex _count; /* number of values */ + CFIndex _bucketsNum; /* number of buckets */ + CFIndex _bucketsUsed; /* number of used buckets */ + CFIndex _bucketsCap; /* maximum number of used buckets */ + CFIndex _mutations; + CFIndex _deletes; + any_pointer_t _context; /* private */ + CFOptionFlags _xflags; + any_t _marker; + any_t *_keys; /* can be NULL if not allocated yet */ + any_t *_values; /* can be NULL if not allocated yet */ +}; + +/* Bits 1-0 of the _xflags are used for mutability variety */ +/* Bits 3-2 of the _xflags are used for key callback indicator bits */ +/* Bits 5-4 of the _xflags are used for value callback indicator bits */ +/* Bit 6 of the _xflags is special KVO actions bit */ +/* Bits 7,8,9 are GC use */ + +CF_INLINE bool hasBeenFinalized(CFTypeRef collection) { + return __CFBitfieldGetValue(((const struct __CFSet *)collection)->_xflags, 7, 7) != 0; +} + +CF_INLINE void markFinalized(CFTypeRef collection) { + __CFBitfieldSetValue(((struct __CFSet *)collection)->_xflags, 7, 7, 1); +} + + +CF_INLINE CFIndex __CFHashGetType(CFHashRef hc) { + return __CFBitfieldGetValue(hc->_xflags, 1, 0); +} + +CF_INLINE CFIndex __CFSetGetSizeOfType(CFIndex t) { + CFIndex size = sizeof(struct __CFSet); + if (__CFBitfieldGetValue(t, 3, 2) == __kCFHashHasCustomCallBacks) { + size += sizeof(CFSetKeyCallBacks); + } + if (__CFBitfieldGetValue(t, 5, 4) == __kCFHashHasCustomCallBacks) { + size += sizeof(CFSetValueCallBacks); + } + return size; +} + +CF_INLINE const CFSetKeyCallBacks *__CFSetGetKeyCallBacks(CFHashRef hc) { + CFSetKeyCallBacks *result = NULL; + switch (__CFBitfieldGetValue(hc->_xflags, 3, 2)) { + case __kCFHashHasNullCallBacks: + return &__kCFNullSetKeyCallBacks; + case __kCFHashHasCFTypeCallBacks: + return &kCFTypeSetKeyCallBacks; + case __kCFHashHasCustomCallBacks: + break; + } + result = (CFSetKeyCallBacks *)((uint8_t *)hc + sizeof(struct __CFSet)); + return result; +} + +CF_INLINE Boolean __CFSetKeyCallBacksMatchNull(const CFSetKeyCallBacks *c) { + return (NULL == c || + (c->retain == __kCFNullSetKeyCallBacks.retain && + c->release == __kCFNullSetKeyCallBacks.release && + c->copyDescription == __kCFNullSetKeyCallBacks.copyDescription && + c->equal == __kCFNullSetKeyCallBacks.equal && + c->hash == __kCFNullSetKeyCallBacks.hash)); +} + +CF_INLINE Boolean __CFSetKeyCallBacksMatchCFType(const CFSetKeyCallBacks *c) { + return (&kCFTypeSetKeyCallBacks == c || + (c->retain == kCFTypeSetKeyCallBacks.retain && + c->release == kCFTypeSetKeyCallBacks.release && + c->copyDescription == kCFTypeSetKeyCallBacks.copyDescription && + c->equal == kCFTypeSetKeyCallBacks.equal && + c->hash == kCFTypeSetKeyCallBacks.hash)); +} + +CF_INLINE const CFSetValueCallBacks *__CFSetGetValueCallBacks(CFHashRef hc) { + CFSetValueCallBacks *result = NULL; + switch (__CFBitfieldGetValue(hc->_xflags, 5, 4)) { + case __kCFHashHasNullCallBacks: + return &__kCFNullSetValueCallBacks; + case __kCFHashHasCFTypeCallBacks: + return &kCFTypeSetValueCallBacks; + case __kCFHashHasCustomCallBacks: + break; + } + if (__CFBitfieldGetValue(hc->_xflags, 3, 2) == __kCFHashHasCustomCallBacks) { + result = (CFSetValueCallBacks *)((uint8_t *)hc + sizeof(struct __CFSet) + sizeof(CFSetKeyCallBacks)); + } else { + result = (CFSetValueCallBacks *)((uint8_t *)hc + sizeof(struct __CFSet)); + } + return result; +} + +CF_INLINE Boolean __CFSetValueCallBacksMatchNull(const CFSetValueCallBacks *c) { + return (NULL == c || + (c->retain == __kCFNullSetValueCallBacks.retain && + c->release == __kCFNullSetValueCallBacks.release && + c->copyDescription == __kCFNullSetValueCallBacks.copyDescription && + c->equal == __kCFNullSetValueCallBacks.equal)); +} + +CF_INLINE Boolean __CFSetValueCallBacksMatchCFType(const CFSetValueCallBacks *c) { + return (&kCFTypeSetValueCallBacks == c || + (c->retain == kCFTypeSetValueCallBacks.retain && + c->release == kCFTypeSetValueCallBacks.release && + c->copyDescription == kCFTypeSetValueCallBacks.copyDescription && + c->equal == kCFTypeSetValueCallBacks.equal)); +} + +CFIndex _CFSetGetKVOBit(CFHashRef hc) { + return __CFBitfieldGetValue(hc->_xflags, 6, 6); +} + +void _CFSetSetKVOBit(CFHashRef hc, CFIndex bit) { + __CFBitfieldSetValue(((CFMutableHashRef)hc)->_xflags, 6, 6, ((uintptr_t)bit & 0x1)); +} + +CF_INLINE Boolean __CFSetShouldShrink(CFHashRef hc) { + return (__kCFHashMutable == __CFHashGetType(hc)) && + !(CF_USING_COLLECTABLE_MEMORY && auto_zone_is_finalized(__CFCollectableZone, hc)) && /* GC: don't shrink finalizing hcs! */ + (hc->_bucketsNum < 4 * hc->_deletes || (256 <= hc->_bucketsCap && hc-> _bucketsUsed < 3 * hc->_bucketsCap / 16)); +} + +CF_INLINE CFIndex __CFHashGetOccurrenceCount(CFHashRef hc, CFIndex idx) { +#if CFBag + return hc->_values[idx]; +#endif + return 1; +} + +CF_INLINE Boolean __CFHashKeyIsValue(CFHashRef hc, any_t key) { + return (hc->_marker != key && ~hc->_marker != key) ? true : false; +} + +CF_INLINE Boolean __CFHashKeyIsMagic(CFHashRef hc, any_t key) { + return (hc->_marker == key || ~hc->_marker == key) ? true : false; +} + + +#if !defined(CF_OBJC_KVO_WILLCHANGE) +#define CF_OBJC_KVO_WILLCHANGE(obj, key) +#define CF_OBJC_KVO_DIDCHANGE(obj, key) +#endif + +CF_INLINE uintptr_t __CFSetScrambleHash(uintptr_t k) { +#if 0 + return k; +#else +#if __LP64__ + uintptr_t a = 0x4368726973746F70ULL; + uintptr_t b = 0x686572204B616E65ULL; +#else + uintptr_t a = 0x4B616E65UL; + uintptr_t b = 0x4B616E65UL; +#endif + uintptr_t c = 1; + a += k; +#if __LP64__ + a -= b; a -= c; a ^= (c >> 43); + b -= c; b -= a; b ^= (a << 9); + c -= a; c -= b; c ^= (b >> 8); + a -= b; a -= c; a ^= (c >> 38); + b -= c; b -= a; b ^= (a << 23); + c -= a; c -= b; c ^= (b >> 5); + a -= b; a -= c; a ^= (c >> 35); + b -= c; b -= a; b ^= (a << 49); + c -= a; c -= b; c ^= (b >> 11); + a -= b; a -= c; a ^= (c >> 12); + b -= c; b -= a; b ^= (a << 18); + c -= a; c -= b; c ^= (b >> 22); +#else + a -= b; a -= c; a ^= (c >> 13); + b -= c; b -= a; b ^= (a << 8); + c -= a; c -= b; c ^= (b >> 13); + a -= b; a -= c; a ^= (c >> 12); + b -= c; b -= a; b ^= (a << 16); + c -= a; c -= b; c ^= (b >> 5); + a -= b; a -= c; a ^= (c >> 3); + b -= c; b -= a; b ^= (a << 10); + c -= a; c -= b; c ^= (b >> 15); +#endif + return c; +#endif +} + +static CFIndex __CFSetFindBuckets1a(CFHashRef hc, any_t key) { + CFHashCode keyHash = (CFHashCode)key; + keyHash = (CFHashCode)__CFSetScrambleHash(keyHash); + any_t *keys = hc->_keys; + any_t marker = hc->_marker; + CFIndex probe = keyHash & (hc->_bucketsNum - 1); + CFIndex probeskip = 1; // See RemoveValue() for notes before changing this value + CFIndex start = probe; + for (;;) { + any_t currKey = keys[probe]; + if (marker == currKey) { /* empty */ + return kCFNotFound; + } else if (~marker == currKey) { /* deleted */ + /* do nothing */ + } else if (currKey == key) { + return probe; + } + probe = probe + probeskip; + // This alternative to probe % buckets assumes that + // probeskip is always positive and less than the + // number of buckets. + if (hc->_bucketsNum <= probe) { + probe -= hc->_bucketsNum; + } + if (start == probe) { + return kCFNotFound; + } + } +} + +static CFIndex __CFSetFindBuckets1b(CFHashRef hc, any_t key) { + const CFSetKeyCallBacks *cb = __CFSetGetKeyCallBacks(hc); + CFHashCode keyHash = cb->hash ? (CFHashCode)INVOKE_CALLBACK2(((CFHashCode (*)(any_t, any_pointer_t))cb->hash), key, hc->_context) : (CFHashCode)key; + keyHash = (CFHashCode)__CFSetScrambleHash(keyHash); + any_t *keys = hc->_keys; + any_t marker = hc->_marker; + CFIndex probe = keyHash & (hc->_bucketsNum - 1); + CFIndex probeskip = 1; // See RemoveValue() for notes before changing this value + CFIndex start = probe; + for (;;) { + any_t currKey = keys[probe]; + if (marker == currKey) { /* empty */ + return kCFNotFound; + } else if (~marker == currKey) { /* deleted */ + /* do nothing */ + } else if (currKey == key || (cb->equal && INVOKE_CALLBACK3((Boolean (*)(any_t, any_t, any_pointer_t))cb->equal, currKey, key, hc->_context))) { + return probe; + } + probe = probe + probeskip; + // This alternative to probe % buckets assumes that + // probeskip is always positive and less than the + // number of buckets. + if (hc->_bucketsNum <= probe) { + probe -= hc->_bucketsNum; + } + if (start == probe) { + return kCFNotFound; + } + } +} + +CF_INLINE CFIndex __CFSetFindBuckets1(CFHashRef hc, any_t key) { + if (__kCFHashHasNullCallBacks == __CFBitfieldGetValue(hc->_xflags, 3, 2)) { + return __CFSetFindBuckets1a(hc, key); + } + return __CFSetFindBuckets1b(hc, key); +} + +static void __CFSetFindBuckets2(CFHashRef hc, any_t key, CFIndex *match, CFIndex *nomatch) { + const CFSetKeyCallBacks *cb = __CFSetGetKeyCallBacks(hc); + CFHashCode keyHash = cb->hash ? (CFHashCode)INVOKE_CALLBACK2(((CFHashCode (*)(any_t, any_pointer_t))cb->hash), key, hc->_context) : (CFHashCode)key; + keyHash = (CFHashCode)__CFSetScrambleHash(keyHash); + any_t *keys = hc->_keys; + any_t marker = hc->_marker; + CFIndex probe = keyHash & (hc->_bucketsNum - 1); + CFIndex probeskip = 1; // See RemoveValue() for notes before changing this value + CFIndex start = probe; + *match = kCFNotFound; + *nomatch = kCFNotFound; + for (;;) { + any_t currKey = keys[probe]; + if (marker == currKey) { /* empty */ + if (nomatch) *nomatch = probe; + return; + } else if (~marker == currKey) { /* deleted */ + if (nomatch) { + *nomatch = probe; + nomatch = NULL; + } + } else if (currKey == key || (cb->equal && INVOKE_CALLBACK3((Boolean (*)(any_t, any_t, any_pointer_t))cb->equal, currKey, key, hc->_context))) { + *match = probe; + return; + } + probe = probe + probeskip; + // This alternative to probe % buckets assumes that + // probeskip is always positive and less than the + // number of buckets. + if (hc->_bucketsNum <= probe) { + probe -= hc->_bucketsNum; + } + if (start == probe) { + return; + } + } +} + +static void __CFSetFindNewMarker(CFHashRef hc) { + any_t *keys = hc->_keys; + any_t newMarker; + CFIndex idx, nbuckets; + Boolean hit; + + nbuckets = hc->_bucketsNum; + newMarker = hc->_marker; + do { + newMarker--; + hit = false; + for (idx = 0; idx < nbuckets; idx++) { + if (newMarker == keys[idx] || ~newMarker == keys[idx]) { + hit = true; + break; + } + } + } while (hit); + for (idx = 0; idx < nbuckets; idx++) { + if (hc->_marker == keys[idx]) { + keys[idx] = newMarker; + } else if (~hc->_marker == keys[idx]) { + keys[idx] = ~newMarker; + } + } + ((struct __CFSet *)hc)->_marker = newMarker; +} + +static Boolean __CFSetEqual(CFTypeRef cf1, CFTypeRef cf2) { + CFHashRef hc1 = (CFHashRef)cf1; + CFHashRef hc2 = (CFHashRef)cf2; + const CFSetKeyCallBacks *cb1, *cb2; + const CFSetValueCallBacks *vcb1, *vcb2; + any_t *keys; + CFIndex idx, nbuckets; + if (hc1 == hc2) return true; + if (hc1->_count != hc2->_count) return false; + cb1 = __CFSetGetKeyCallBacks(hc1); + cb2 = __CFSetGetKeyCallBacks(hc2); + if (cb1->equal != cb2->equal) return false; + vcb1 = __CFSetGetValueCallBacks(hc1); + vcb2 = __CFSetGetValueCallBacks(hc2); + if (vcb1->equal != vcb2->equal) return false; + if (0 == hc1->_bucketsUsed) return true; /* after function comparison! */ + keys = hc1->_keys; + nbuckets = hc1->_bucketsNum; + for (idx = 0; idx < nbuckets; idx++) { + if (hc1->_marker != keys[idx] && ~hc1->_marker != keys[idx]) { +#if CFDictionary + const_any_pointer_t value; + if (!CFSetGetValueIfPresent(hc2, (any_pointer_t)keys[idx], &value)) return false; + if (hc1->_values[idx] != (any_t)value) { + if (NULL == vcb1->equal) return false; + if (!INVOKE_CALLBACK3((Boolean (*)(any_t, any_t, any_pointer_t))vcb1->equal, hc1->_values[idx], (any_t)value, hc1->_context)) return false; + } +#endif +#if CFSet + const_any_pointer_t value; + if (!CFSetGetValueIfPresent(hc2, (any_pointer_t)keys[idx], &value)) return false; +#endif +#if CFBag + if (hc1->_values[idx] != CFSetGetCountOfValue(hc2, (any_pointer_t)keys[idx])) return false; +#endif + } + } + return true; +} + +static CFHashCode __CFSetHash(CFTypeRef cf) { + CFHashRef hc = (CFHashRef)cf; + return hc->_count; +} + +static CFStringRef __CFSetCopyDescription(CFTypeRef cf) { + CFHashRef hc = (CFHashRef)cf; + CFAllocatorRef allocator; + const CFSetKeyCallBacks *cb; + const CFSetValueCallBacks *vcb; + any_t *keys; + CFIndex idx, nbuckets; + CFMutableStringRef result; + cb = __CFSetGetKeyCallBacks(hc); + vcb = __CFSetGetValueCallBacks(hc); + keys = hc->_keys; + nbuckets = hc->_bucketsNum; + allocator = CFGetAllocator(hc); + result = CFStringCreateMutable(allocator, 0); + const char *type = "?"; + switch (__CFHashGetType(hc)) { + case __kCFHashImmutable: type = "immutable"; break; + case __kCFHashMutable: type = "mutable"; break; + } + CFStringAppendFormat(result, NULL, CFSTR("<CFSet %p [%p]>{type = %s, count = %u, capacity = %u, pairs = (\n"), cf, allocator, type, hc->_count, hc->_bucketsCap); + for (idx = 0; idx < nbuckets; idx++) { + if (__CFHashKeyIsValue(hc, keys[idx])) { + CFStringRef kDesc = NULL, vDesc = NULL; + if (NULL != cb->copyDescription) { + kDesc = (CFStringRef)INVOKE_CALLBACK2(((CFStringRef (*)(any_t, any_pointer_t))cb->copyDescription), keys[idx], hc->_context); + } + if (NULL != vcb->copyDescription) { + vDesc = (CFStringRef)INVOKE_CALLBACK2(((CFStringRef (*)(any_t, any_pointer_t))vcb->copyDescription), hc->_values[idx], hc->_context); + } +#if CFDictionary + if (NULL != kDesc && NULL != vDesc) { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : %@ = %@\n"), idx, kDesc, vDesc); + CFRelease(kDesc); + CFRelease(vDesc); + } else if (NULL != kDesc) { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : %@ = <%p>\n"), idx, kDesc, hc->_values[idx]); + CFRelease(kDesc); + } else if (NULL != vDesc) { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : <%p> = %@\n"), idx, keys[idx], vDesc); + CFRelease(vDesc); + } else { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : <%p> = <%p>\n"), idx, keys[idx], hc->_values[idx]); + } +#endif +#if CFSet + if (NULL != kDesc) { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : %@\n"), idx, kDesc); + CFRelease(kDesc); + } else { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : <%p>\n"), idx, keys[idx]); + } +#endif +#if CFBag + if (NULL != kDesc) { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : %@ (%ld)\n"), idx, kDesc, hc->_values[idx]); + CFRelease(kDesc); + } else { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : <%p> (%ld)\n"), idx, keys[idx], hc->_values[idx]); + } +#endif + } + } + CFStringAppend(result, CFSTR(")}")); + return result; +} + +static void __CFSetDeallocate(CFTypeRef cf) { + CFMutableHashRef hc = (CFMutableHashRef)cf; + CFAllocatorRef allocator = __CFGetAllocator(hc); + const CFSetKeyCallBacks *cb = __CFSetGetKeyCallBacks(hc); + const CFSetValueCallBacks *vcb = __CFSetGetValueCallBacks(hc); + + // mark now in case any callout somehow tries to add an entry back in + markFinalized(cf); + if (vcb->release || cb->release) { + any_t *keys = hc->_keys; + CFIndex idx, nbuckets = hc->_bucketsNum; + for (idx = 0; idx < nbuckets; idx++) { + any_t oldkey = keys[idx]; + if (hc->_marker != oldkey && ~hc->_marker != oldkey) { + if (vcb->release) { + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, any_t, any_pointer_t))vcb->release), allocator, hc->_values[idx], hc->_context); + } + if (cb->release) { + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, any_t, any_pointer_t))cb->release), allocator, oldkey, hc->_context); + } + } + } + } + + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + // return early so that contents are preserved after finalization + return; + } + + _CFAllocatorDeallocateGC(allocator, hc->_keys); +#if CFDictionary || CFBag + _CFAllocatorDeallocateGC(allocator, hc->_values); +#endif + hc->_keys = NULL; + hc->_values = NULL; + hc->_count = 0; // GC: also zero count, so the hc will appear empty. + hc->_bucketsUsed = 0; + hc->_bucketsNum = 0; +} + +static CFTypeID __kCFSetTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFSetClass = { + _kCFRuntimeScannedObject, + "CFSet", + NULL, // init + NULL, // copy + __CFSetDeallocate, + __CFSetEqual, + __CFSetHash, + NULL, // + __CFSetCopyDescription +}; + +__private_extern__ void __CFSetInitialize(void) { + __kCFHashTypeID = _CFRuntimeRegisterClass(&__CFSetClass); +} + +CFTypeID CFSetGetTypeID(void) { + return __kCFHashTypeID; +} + +static CFMutableHashRef __CFSetInit(CFAllocatorRef allocator, CFOptionFlags flags, CFIndex capacity, const CFSetKeyCallBacks *keyCallBacks +#if CFDictionary +, const CFSetValueCallBacks *valueCallBacks +#endif +) { + struct __CFSet *hc; + CFIndex size; + __CFBitfieldSetValue(flags, 31, 2, 0); + CFOptionFlags xflags = 0; + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + // preserve NULL for key or value CB, otherwise fix up. + if (!keyCallBacks || (keyCallBacks->retain == NULL && keyCallBacks->release == NULL)) { + xflags = __kCFHashWeakKeys; + } +#if CFDictionary + if (!valueCallBacks || (valueCallBacks->retain == NULL && valueCallBacks->release == NULL)) { + xflags |= __kCFHashWeakValues; + } +#endif +#if CFBag + xflags |= __kCFHashWeakValues; +#endif + } + if (__CFSetKeyCallBacksMatchNull(keyCallBacks)) { + __CFBitfieldSetValue(flags, 3, 2, __kCFHashHasNullCallBacks); + } else if (__CFSetKeyCallBacksMatchCFType(keyCallBacks)) { + __CFBitfieldSetValue(flags, 3, 2, __kCFHashHasCFTypeCallBacks); + } else { + __CFBitfieldSetValue(flags, 3, 2, __kCFHashHasCustomCallBacks); + } +#if CFDictionary + if (__CFSetValueCallBacksMatchNull(valueCallBacks)) { + __CFBitfieldSetValue(flags, 5, 4, __kCFHashHasNullCallBacks); + } else if (__CFSetValueCallBacksMatchCFType(valueCallBacks)) { + __CFBitfieldSetValue(flags, 5, 4, __kCFHashHasCFTypeCallBacks); + } else { + __CFBitfieldSetValue(flags, 5, 4, __kCFHashHasCustomCallBacks); + } +#endif + size = __CFSetGetSizeOfType(flags) - sizeof(CFRuntimeBase); + hc = (struct __CFSet *)_CFRuntimeCreateInstance(allocator, __kCFHashTypeID, size, NULL); + if (NULL == hc) { + return NULL; + } + hc->_count = 0; + hc->_bucketsUsed = 0; + hc->_marker = (any_t)0xa1b1c1d3; + hc->_context = NULL; + hc->_deletes = 0; + hc->_mutations = 1; + hc->_xflags = xflags | flags; + switch (__CFBitfieldGetValue(flags, 1, 0)) { + case __kCFHashImmutable: + if (__CFOASafe) __CFSetLastAllocationEventName(hc, "CFSet (immutable)"); + break; + case __kCFHashMutable: + if (__CFOASafe) __CFSetLastAllocationEventName(hc, "CFSet (mutable-variable)"); + break; + } + hc->_bucketsCap = __CFHashRoundUpCapacity(1); + hc->_bucketsNum = 0; + hc->_keys = NULL; + hc->_values = NULL; + if (__kCFHashHasCustomCallBacks == __CFBitfieldGetValue(flags, 3, 2)) { + CFSetKeyCallBacks *cb = (CFSetKeyCallBacks *)__CFSetGetKeyCallBacks((CFHashRef)hc); + *cb = *keyCallBacks; + FAULT_CALLBACK((void **)&(cb->retain)); + FAULT_CALLBACK((void **)&(cb->release)); + FAULT_CALLBACK((void **)&(cb->copyDescription)); + FAULT_CALLBACK((void **)&(cb->equal)); + FAULT_CALLBACK((void **)&(cb->hash)); + } +#if CFDictionary + if (__kCFHashHasCustomCallBacks == __CFBitfieldGetValue(flags, 5, 4)) { + CFSetValueCallBacks *vcb = (CFSetValueCallBacks *)__CFSetGetValueCallBacks((CFHashRef)hc); + *vcb = *valueCallBacks; + FAULT_CALLBACK((void **)&(vcb->retain)); + FAULT_CALLBACK((void **)&(vcb->release)); + FAULT_CALLBACK((void **)&(vcb->copyDescription)); + FAULT_CALLBACK((void **)&(vcb->equal)); + } +#endif + return hc; +} + +#if CFDictionary +CFHashRef CFSetCreate(CFAllocatorRef allocator, const_any_pointer_t *keys, const_any_pointer_t *values, CFIndex numValues, const CFSetKeyCallBacks *keyCallBacks, const CFSetValueCallBacks *valueCallBacks) { +#endif +#if CFSet || CFBag +CFHashRef CFSetCreate(CFAllocatorRef allocator, const_any_pointer_t *keys, CFIndex numValues, const CFSetKeyCallBacks *keyCallBacks) { +#endif + CFAssert2(0 <= numValues, __kCFLogAssertion, "%s(): numValues (%ld) cannot be less than zero", __PRETTY_FUNCTION__, numValues); +#if CFDictionary + CFMutableHashRef hc = __CFSetInit(allocator, __kCFHashImmutable, numValues, keyCallBacks, valueCallBacks); +#endif +#if CFSet || CFBag + CFMutableHashRef hc = __CFSetInit(allocator, __kCFHashImmutable, numValues, keyCallBacks); +#endif + __CFBitfieldSetValue(hc->_xflags, 1, 0, __kCFHashMutable); + for (CFIndex idx = 0; idx < numValues; idx++) { +#if CFDictionary + CFSetAddValue(hc, keys[idx], values[idx]); +#endif +#if CFSet || CFBag + CFSetAddValue(hc, keys[idx]); +#endif + } + __CFBitfieldSetValue(hc->_xflags, 1, 0, __kCFHashImmutable); + return (CFHashRef)hc; +} + +#if CFDictionary +CFMutableHashRef CFSetCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFSetKeyCallBacks *keyCallBacks, const CFSetValueCallBacks *valueCallBacks) { +#endif +#if CFSet || CFBag +CFMutableHashRef CFSetCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFSetKeyCallBacks *keyCallBacks) { +#endif + CFAssert2(0 <= capacity, __kCFLogAssertion, "%s(): capacity (%ld) cannot be less than zero", __PRETTY_FUNCTION__, capacity); +#if CFDictionary + CFMutableHashRef hc = __CFSetInit(allocator, __kCFHashMutable, capacity, keyCallBacks, valueCallBacks); +#endif +#if CFSet || CFBag + CFMutableHashRef hc = __CFSetInit(allocator, __kCFHashMutable, capacity, keyCallBacks); +#endif + return hc; +} + +#if CFDictionary || CFSet +// does not have Add semantics for Bag; it has Set semantics ... is that best? +static void __CFSetGrow(CFMutableHashRef hc, CFIndex numNewValues); + +// This creates a hc which is for CFTypes or NSObjects, with a CFRetain style ownership transfer; +// the hc does not take a retain (since it claims 1), and the caller does not need to release the inserted objects (since we do it). +// The incoming objects must also be collectable if allocated out of a collectable allocator - and are neither released nor retained. +#if CFDictionary +CFHashRef _CFSetCreate_ex(CFAllocatorRef allocator, Boolean isMutable, const_any_pointer_t *keys, const_any_pointer_t *values, CFIndex numValues) { +#endif +#if CFSet || CFBag +CFHashRef _CFSetCreate_ex(CFAllocatorRef allocator, Boolean isMutable, const_any_pointer_t *keys, CFIndex numValues) { +#endif + CFAssert2(0 <= numValues, __kCFLogAssertion, "%s(): numValues (%ld) cannot be less than zero", __PRETTY_FUNCTION__, numValues); +#if CFDictionary + CFMutableHashRef hc = __CFSetInit(allocator, __kCFHashMutable, numValues, &kCFTypeSetKeyCallBacks, &kCFTypeSetValueCallBacks); +#endif +#if CFSet || CFBag + CFMutableHashRef hc = __CFSetInit(allocator, __kCFHashMutable, numValues, &kCFTypeSetKeyCallBacks); +#endif + __CFSetGrow(hc, numValues); + for (CFIndex idx = 0; idx < numValues; idx++) { + CFIndex match, nomatch; + __CFSetFindBuckets2(hc, (any_t)keys[idx], &match, &nomatch); + if (kCFNotFound == match) { + CFAllocatorRef allocator = __CFGetAllocator(hc); + any_t newKey = (any_t)keys[idx]; + if (__CFHashKeyIsMagic(hc, newKey)) { + __CFSetFindNewMarker(hc); + } + if (hc->_keys[nomatch] == ~hc->_marker) { + hc->_deletes--; + } + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[nomatch], newKey); +#if CFDictionary + any_t newValue = (any_t)values[idx]; + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, hc->_values[nomatch], newValue); +#endif +#if CFBag + hc->_values[nomatch] = 1; +#endif + hc->_bucketsUsed++; + hc->_count++; + } else { + CFAllocatorRef allocator = __CFGetAllocator(hc); +#if CFSet || CFBag + any_t oldKey = hc->_keys[match]; + any_t newKey = (any_t)keys[idx]; + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[match], ~hc->_marker); + if (__CFHashKeyIsMagic(hc, newKey)) { + __CFSetFindNewMarker(hc); + } + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[match], newKey); + RELEASEKEY(oldKey); +#endif +#if CFDictionary + any_t oldValue = hc->_values[match]; + any_t newValue = (any_t)values[idx]; + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, hc->_values[match], newValue); + RELEASEVALUE(oldValue); +#endif + } + } + if (!isMutable) __CFBitfieldSetValue(hc->_xflags, 1, 0, __kCFHashImmutable); + return (CFHashRef)hc; +} +#endif + +CFHashRef CFSetCreateCopy(CFAllocatorRef allocator, CFHashRef other) { + CFMutableHashRef hc = CFSetCreateMutableCopy(allocator, CFSetGetCount(other), other); + __CFBitfieldSetValue(hc->_xflags, 1, 0, __kCFHashImmutable); + if (__CFOASafe) __CFSetLastAllocationEventName(hc, "CFSet (immutable)"); + return hc; +} + +CFMutableHashRef CFSetCreateMutableCopy(CFAllocatorRef allocator, CFIndex capacity, CFHashRef other) { + CFIndex numValues = CFSetGetCount(other); + const_any_pointer_t *list, buffer[256]; + list = (numValues <= 256) ? buffer : (const_any_pointer_t *)CFAllocatorAllocate(allocator, numValues * sizeof(const_any_pointer_t), 0); + if (list != buffer && __CFOASafe) __CFSetLastAllocationEventName(list, "CFSet (temp)"); +#if CFDictionary + const_any_pointer_t *vlist, vbuffer[256]; + vlist = (numValues <= 256) ? vbuffer : (const_any_pointer_t *)CFAllocatorAllocate(allocator, numValues * sizeof(const_any_pointer_t), 0); + if (vlist != vbuffer && __CFOASafe) __CFSetLastAllocationEventName(vlist, "CFSet (temp)"); +#endif +#if CFSet || CFBag + CFSetGetValues(other, list); +#endif +#if CFDictionary + CFSetGetKeysAndValues(other, list, vlist); +#endif + const CFSetKeyCallBacks *kcb; + const CFSetValueCallBacks *vcb; + if (CF_IS_OBJC(__kCFHashTypeID, other)) { + kcb = &kCFTypeSetKeyCallBacks; + vcb = &kCFTypeSetValueCallBacks; + } else { + kcb = __CFSetGetKeyCallBacks(other); + vcb = __CFSetGetValueCallBacks(other); + } +#if CFDictionary + CFMutableHashRef hc = __CFSetInit(allocator, __kCFHashMutable, capacity, kcb, vcb); +#endif +#if CFSet || CFBag + CFMutableHashRef hc = __CFSetInit(allocator, __kCFHashMutable, capacity, kcb); +#endif + if (0 == capacity) _CFSetSetCapacity(hc, numValues); + for (CFIndex idx = 0; idx < numValues; idx++) { +#if CFDictionary + CFSetAddValue(hc, list[idx], vlist[idx]); +#endif +#if CFSet || CFBag + CFSetAddValue(hc, list[idx]); +#endif + } + if (list != buffer) CFAllocatorDeallocate(allocator, list); +#if CFDictionary + if (vlist != vbuffer) CFAllocatorDeallocate(allocator, vlist); +#endif + return hc; +} + +// Used by NSHashTables/NSMapTables and KVO +void _CFSetSetContext(CFHashRef hc, any_pointer_t context) { + __CFGenericValidateType(hc, __kCFHashTypeID); + CF_WRITE_BARRIER_BASE_ASSIGN(__CFGetAllocator(hc), hc, hc->_context, context); +} + +any_pointer_t _CFSetGetContext(CFHashRef hc) { + __CFGenericValidateType(hc, __kCFHashTypeID); + return hc->_context; +} + +CFIndex CFSetGetCount(CFHashRef hc) { + if (CFDictionary || CFSet) CF_OBJC_FUNCDISPATCH0(__kCFHashTypeID, CFIndex, hc, "count"); + __CFGenericValidateType(hc, __kCFHashTypeID); + return hc->_count; +} + +#if CFDictionary +CFIndex CFSetGetCountOfKey(CFHashRef hc, const_any_pointer_t key) { +#endif +#if CFSet || CFBag +CFIndex CFSetGetCountOfValue(CFHashRef hc, const_any_pointer_t key) { +#endif + if (CFDictionary) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, CFIndex, hc, "countForKey:", key); + if (CFSet) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, CFIndex, hc, "countForObject:", key); + __CFGenericValidateType(hc, __kCFHashTypeID); + if (0 == hc->_bucketsUsed) return 0; + CFIndex match = __CFSetFindBuckets1(hc, (any_t)key); + return (kCFNotFound != match ? __CFHashGetOccurrenceCount(hc, match) : 0); +} + +#if CFDictionary +Boolean CFSetContainsKey(CFHashRef hc, const_any_pointer_t key) { +#endif +#if CFSet || CFBag +Boolean CFSetContainsValue(CFHashRef hc, const_any_pointer_t key) { +#endif + if (CFDictionary) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, char, hc, "containsKey:", key); + if (CFSet) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, char, hc, "containsObject:", key); + __CFGenericValidateType(hc, __kCFHashTypeID); + if (0 == hc->_bucketsUsed) return false; + CFIndex match = __CFSetFindBuckets1(hc, (any_t)key); + return (kCFNotFound != match ? true : false); +} + +#if CFDictionary +CFIndex CFSetGetCountOfValue(CFHashRef hc, const_any_pointer_t value) { + CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, CFIndex, hc, "countForObject:", value); + __CFGenericValidateType(hc, __kCFHashTypeID); + if (0 == hc->_bucketsUsed) return 0; + any_t *keys = hc->_keys; + Boolean (*equal)(any_t, any_t, any_pointer_t) = (Boolean (*)(any_t, any_t, any_pointer_t))__CFSetGetValueCallBacks(hc)->equal; + CFIndex cnt = 0; + for (CFIndex idx = 0, nbuckets = hc->_bucketsNum; idx < nbuckets; idx++) { + if (__CFHashKeyIsValue(hc, keys[idx])) { + if ((hc->_values[idx] == (any_t)value) || (equal && INVOKE_CALLBACK3(equal, hc->_values[idx], (any_t)value, hc->_context))) { + cnt++; + } + } + } + return cnt; +} + +Boolean CFSetContainsValue(CFHashRef hc, const_any_pointer_t value) { + CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, char, hc, "containsObject:", value); + __CFGenericValidateType(hc, __kCFHashTypeID); + if (0 == hc->_bucketsUsed) return false; + any_t *keys = hc->_keys; + Boolean (*equal)(any_t, any_t, any_pointer_t) = (Boolean (*)(any_t, any_t, any_pointer_t))__CFSetGetValueCallBacks(hc)->equal; + for (CFIndex idx = 0, nbuckets = hc->_bucketsNum; idx < nbuckets; idx++) { + if (__CFHashKeyIsValue(hc, keys[idx])) { + if ((hc->_values[idx] == (any_t)value) || (equal && INVOKE_CALLBACK3(equal, hc->_values[idx], (any_t)value, hc->_context))) { + return true; + } + } + } + return false; +} +#endif + +const_any_pointer_t CFSetGetValue(CFHashRef hc, const_any_pointer_t key) { + if (CFDictionary) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, const_any_pointer_t, hc, "objectForKey:", key); + if (CFSet) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, const_any_pointer_t, hc, "member:", key); + __CFGenericValidateType(hc, __kCFHashTypeID); + if (0 == hc->_bucketsUsed) return 0; + CFIndex match = __CFSetFindBuckets1(hc, (any_t)key); + return (kCFNotFound != match ? (const_any_pointer_t)(CFDictionary ? hc->_values[match] : hc->_keys[match]) : 0); +} + +Boolean CFSetGetValueIfPresent(CFHashRef hc, const_any_pointer_t key, const_any_pointer_t *value) { + if (CFDictionary) CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, Boolean, hc, "_getValue:forKey:", (any_t *)value, key); + if (CFSet) CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, Boolean, hc, "_getValue:forObj:", (any_t *)value, key); + __CFGenericValidateType(hc, __kCFHashTypeID); + if (0 == hc->_bucketsUsed) return false; + CFIndex match = __CFSetFindBuckets1(hc, (any_t)key); + return (kCFNotFound != match ? ((value ? __CFObjCStrongAssign((const_any_pointer_t)(CFDictionary ? hc->_values[match] : hc->_keys[match]), value) : 0), true) : false); +} + +#if CFDictionary +Boolean CFSetGetKeyIfPresent(CFHashRef hc, const_any_pointer_t key, const_any_pointer_t *actualkey) { + CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, Boolean, hc, "getActualKey:forKey:", actualkey, key); + __CFGenericValidateType(hc, __kCFHashTypeID); + if (0 == hc->_bucketsUsed) return false; + CFIndex match = __CFSetFindBuckets1(hc, (any_t)key); + return (kCFNotFound != match ? ((actualkey ? __CFObjCStrongAssign((const_any_pointer_t)hc->_keys[match], actualkey) : NULL), true) : false); +} +#endif + +#if CFDictionary +void CFSetGetKeysAndValues(CFHashRef hc, const_any_pointer_t *keybuf, const_any_pointer_t *valuebuf) { +#endif +#if CFSet || CFBag +void CFSetGetValues(CFHashRef hc, const_any_pointer_t *keybuf) { + const_any_pointer_t *valuebuf = 0; +#endif + if (CFDictionary) CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, void, hc, "getObjects:andKeys:", (any_t *)valuebuf, (any_t *)keybuf); + if (CFSet) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, void, hc, "getObjects:", (any_t *)keybuf); + __CFGenericValidateType(hc, __kCFHashTypeID); + if (CF_USING_COLLECTABLE_MEMORY) { + // GC: speculatively issue a write-barrier on the copied to buffers + __CFObjCWriteBarrierRange(keybuf, hc->_count * sizeof(any_t)); + __CFObjCWriteBarrierRange(valuebuf, hc->_count * sizeof(any_t)); + } + any_t *keys = hc->_keys; + for (CFIndex idx = 0, nbuckets = hc->_bucketsNum; idx < nbuckets; idx++) { + if (__CFHashKeyIsValue(hc, keys[idx])) { + for (CFIndex cnt = __CFHashGetOccurrenceCount(hc, idx); cnt--;) { + if (keybuf) *keybuf++ = (const_any_pointer_t)keys[idx]; + if (valuebuf) *valuebuf++ = (const_any_pointer_t)hc->_values[idx]; + } + } + } +} + +#if CFDictionary || CFSet +unsigned long _CFSetFastEnumeration(CFHashRef hc, struct __objcFastEnumerationStateEquivalent *state, void *stackbuffer, unsigned long count) { + /* copy as many as count items over */ + if (0 == state->state) { /* first time */ + state->mutationsPtr = (unsigned long *)&hc->_mutations; + } + state->itemsPtr = (unsigned long *)stackbuffer; + CFIndex cnt = 0; + any_t *keys = hc->_keys; + for (CFIndex idx = (CFIndex)state->state, nbuckets = hc->_bucketsNum; idx < nbuckets && cnt < (CFIndex)count; idx++) { + if (__CFHashKeyIsValue(hc, keys[idx])) { + state->itemsPtr[cnt++] = (unsigned long)keys[idx]; + } + state->state++; + } + return cnt; +} +#endif + +void CFSetApplyFunction(CFHashRef hc, CFSetApplierFunction applier, any_pointer_t context) { + FAULT_CALLBACK((void **)&(applier)); + if (CFDictionary) CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, void, hc, "_apply:context:", applier, context); + if (CFSet) CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, void, hc, "_applyValues:context:", applier, context); + __CFGenericValidateType(hc, __kCFHashTypeID); + for (CFIndex idx = 0, nbuckets = hc->_bucketsNum; idx < nbuckets; idx++) { + if (__CFHashKeyIsValue(hc, hc->_keys[idx])) { + for (CFIndex cnt = __CFHashGetOccurrenceCount(hc, idx); cnt--;) { +#if CFDictionary + INVOKE_CALLBACK3(applier, (const_any_pointer_t)hc->_keys[idx], (const_any_pointer_t)hc->_values[idx], context); +#endif +#if CFSet || CFBag + INVOKE_CALLBACK2(applier, (const_any_pointer_t)hc->_keys[idx], context); +#endif + } + } + } +} + + +// This function is for Foundation's benefit; no one else should use it. +bool _CFSetIsMutable(CFSetRef set) { + return (__CFHashGetType(set) != __kCFHashImmutable); +} + +static void __CFSetGrow(CFMutableHashRef hc, CFIndex numNewValues) { + any_t *oldkeys = hc->_keys; + any_t *oldvalues = hc->_values; + CFIndex nbuckets = hc->_bucketsNum; + hc->_bucketsCap = __CFHashRoundUpCapacity(hc->_bucketsUsed + numNewValues); + hc->_bucketsNum = __CFHashNumBucketsForCapacity(hc->_bucketsCap); + hc->_deletes = 0; + CFAllocatorRef allocator = __CFGetAllocator(hc); + CFOptionFlags weakOrStrong = (hc->_xflags & __kCFHashWeakKeys) ? 0 : __kCFAllocatorGCScannedMemory; + any_t *mem = (any_t *)_CFAllocatorAllocateGC(allocator, hc->_bucketsNum * sizeof(any_t), weakOrStrong); + if (NULL == mem) __CFSetHandleOutOfMemory(hc, hc->_bucketsNum * sizeof(any_t)); + if (__CFOASafe) __CFSetLastAllocationEventName(mem, "CFSet (key-store)"); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, hc, hc->_keys, mem); + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; // GC: avoids write-barrier in weak case. + any_t *keysBase = mem; +#if CFDictionary || CFBag + weakOrStrong = (hc->_xflags & __kCFHashWeakValues) ? 0 : __kCFAllocatorGCScannedMemory; + mem = (any_t *)_CFAllocatorAllocateGC(allocator, hc->_bucketsNum * sizeof(any_t), weakOrStrong); + if (NULL == mem) __CFSetHandleOutOfMemory(hc, hc->_bucketsNum * sizeof(any_t)); + if (__CFOASafe) __CFSetLastAllocationEventName(mem, "CFSet (value-store)"); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, hc, hc->_values, mem); +#endif +#if CFDictionary + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; // GC: avoids write-barrier in weak case. + any_t *valuesBase = mem; +#endif + for (CFIndex idx = 0, nbuckets = hc->_bucketsNum; idx < nbuckets; idx++) { + hc->_keys[idx] = hc->_marker; +#if CFDictionary || CFBag + hc->_values[idx] = 0; +#endif + } + if (NULL == oldkeys) return; + for (CFIndex idx = 0; idx < nbuckets; idx++) { + if (__CFHashKeyIsValue(hc, oldkeys[idx])) { + CFIndex match, nomatch; + __CFSetFindBuckets2(hc, oldkeys[idx], &match, &nomatch); + CFAssert3(kCFNotFound == match, __kCFLogAssertion, "%s(): two values (%p, %p) now hash to the same slot; mutable value changed while in table or hash value is not immutable", __PRETTY_FUNCTION__, oldkeys[idx], hc->_keys[match]); + if (kCFNotFound != nomatch) { + CF_WRITE_BARRIER_BASE_ASSIGN(keysAllocator, keysBase, hc->_keys[nomatch], oldkeys[idx]); +#if CFDictionary + CF_WRITE_BARRIER_BASE_ASSIGN(valuesAllocator, valuesBase, hc->_values[nomatch], oldvalues[idx]); +#endif +#if CFBag + hc->_values[nomatch] = oldvalues[idx]; +#endif + } + } + } + _CFAllocatorDeallocateGC(allocator, oldkeys); + _CFAllocatorDeallocateGC(allocator, oldvalues); +} + +// This function is for Foundation's benefit; no one else should use it. +void _CFSetSetCapacity(CFMutableHashRef hc, CFIndex cap) { + if (CF_IS_OBJC(__kCFHashTypeID, hc)) return; + __CFGenericValidateType(hc, __kCFHashTypeID); + CFAssert1(__CFHashGetType(hc) != __kCFHashImmutable, __kCFLogAssertion, "%s(): collection is immutable", __PRETTY_FUNCTION__); + CFAssert3(hc->_bucketsUsed <= cap, __kCFLogAssertion, "%s(): desired capacity (%ld) is less than bucket count (%ld)", __PRETTY_FUNCTION__, cap, hc->_bucketsUsed); + __CFSetGrow(hc, cap - hc->_bucketsUsed); +} + + +#if CFDictionary +void CFSetAddValue(CFMutableHashRef hc, const_any_pointer_t key, const_any_pointer_t value) { +#endif +#if CFSet || CFBag +void CFSetAddValue(CFMutableHashRef hc, const_any_pointer_t key) { + #define value 0 +#endif + if (CFDictionary) CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, void, hc, "_addObject:forKey:", value, key); + if (CFSet) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, void, hc, "addObject:", key); + __CFGenericValidateType(hc, __kCFHashTypeID); + switch (__CFHashGetType(hc)) { + case __kCFHashMutable: + if (hc->_bucketsUsed == hc->_bucketsCap || NULL == hc->_keys) { + __CFSetGrow(hc, 1); + } + break; + default: + CFAssert2(__CFHashGetType(hc) != __kCFHashImmutable, __kCFLogAssertion, "%s(): immutable collection %p passed to mutating operation", __PRETTY_FUNCTION__, hc); + break; + } + hc->_mutations++; + CFIndex match, nomatch; + __CFSetFindBuckets2(hc, (any_t)key, &match, &nomatch); + if (kCFNotFound != match) { +#if CFBag + CF_OBJC_KVO_WILLCHANGE(hc, hc->_keys[match]); + hc->_values[match]++; + hc->_count++; + CF_OBJC_KVO_DIDCHANGE(hc, hc->_keys[match]); +#endif + } else { + CFAllocatorRef allocator = __CFGetAllocator(hc); + GETNEWKEY(newKey, key); +#if CFDictionary + GETNEWVALUE(newValue); +#endif + if (__CFHashKeyIsMagic(hc, newKey)) { + __CFSetFindNewMarker(hc); + } + if (hc->_keys[nomatch] == ~hc->_marker) { + hc->_deletes--; + } + CF_OBJC_KVO_WILLCHANGE(hc, key); + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[nomatch], newKey); +#if CFDictionary + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, hc->_values[nomatch], newValue); +#endif +#if CFBag + hc->_values[nomatch] = 1; +#endif + hc->_bucketsUsed++; + hc->_count++; + CF_OBJC_KVO_DIDCHANGE(hc, key); + } +} + +#if CFDictionary +void CFSetReplaceValue(CFMutableHashRef hc, const_any_pointer_t key, const_any_pointer_t value) { +#endif +#if CFSet || CFBag +void CFSetReplaceValue(CFMutableHashRef hc, const_any_pointer_t key) { + #define value 0 +#endif + if (CFDictionary) CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, void, hc, "_replaceObject:forKey:", value, key); + if (CFSet) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, void, hc, "_replaceObject:", key); + __CFGenericValidateType(hc, __kCFHashTypeID); + switch (__CFHashGetType(hc)) { + case __kCFHashMutable: + break; + default: + CFAssert2(__CFHashGetType(hc) != __kCFHashImmutable, __kCFLogAssertion, "%s(): immutable collection %p passed to mutating operation", __PRETTY_FUNCTION__, hc); + break; + } + hc->_mutations++; + if (0 == hc->_bucketsUsed) return; + CFIndex match = __CFSetFindBuckets1(hc, (any_t)key); + if (kCFNotFound == match) return; + CFAllocatorRef allocator = __CFGetAllocator(hc); +#if CFSet || CFBag + GETNEWKEY(newKey, key); +#endif +#if CFDictionary + GETNEWVALUE(newValue); +#endif + any_t oldKey = hc->_keys[match]; + CF_OBJC_KVO_WILLCHANGE(hc, oldKey); +#if CFSet || CFBag + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[match], ~hc->_marker); + if (__CFHashKeyIsMagic(hc, newKey)) { + __CFSetFindNewMarker(hc); + } + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[match], newKey); +#endif +#if CFDictionary + any_t oldValue = hc->_values[match]; + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, hc->_values[match], newValue); +#endif + CF_OBJC_KVO_DIDCHANGE(hc, oldKey); +#if CFSet || CFBag + RELEASEKEY(oldKey); +#endif +#if CFDictionary + RELEASEVALUE(oldValue); +#endif +} + +#if CFDictionary +void CFSetSetValue(CFMutableHashRef hc, const_any_pointer_t key, const_any_pointer_t value) { +#endif +#if CFSet || CFBag +void CFSetSetValue(CFMutableHashRef hc, const_any_pointer_t key) { + #define value 0 +#endif + if (CFDictionary) CF_OBJC_FUNCDISPATCH2(__kCFHashTypeID, void, hc, "setObject:forKey:", value, key); + if (CFSet) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, void, hc, "_setObject:", key); + __CFGenericValidateType(hc, __kCFHashTypeID); + switch (__CFHashGetType(hc)) { + case __kCFHashMutable: + if (hc->_bucketsUsed == hc->_bucketsCap || NULL == hc->_keys) { + __CFSetGrow(hc, 1); + } + break; + default: + CFAssert2(__CFHashGetType(hc) != __kCFHashImmutable, __kCFLogAssertion, "%s(): immutable collection %p passed to mutating operation", __PRETTY_FUNCTION__, hc); + break; + } + hc->_mutations++; + CFIndex match, nomatch; + __CFSetFindBuckets2(hc, (any_t)key, &match, &nomatch); + if (kCFNotFound == match) { + CFAllocatorRef allocator = __CFGetAllocator(hc); + GETNEWKEY(newKey, key); +#if CFDictionary + GETNEWVALUE(newValue); +#endif + if (__CFHashKeyIsMagic(hc, newKey)) { + __CFSetFindNewMarker(hc); + } + if (hc->_keys[nomatch] == ~hc->_marker) { + hc->_deletes--; + } + CF_OBJC_KVO_WILLCHANGE(hc, key); + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[nomatch], newKey); +#if CFDictionary + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, hc->_values[nomatch], newValue); +#endif +#if CFBag + hc->_values[nomatch] = 1; +#endif + hc->_bucketsUsed++; + hc->_count++; + CF_OBJC_KVO_DIDCHANGE(hc, key); + } else { + CFAllocatorRef allocator = __CFGetAllocator(hc); +#if CFSet || CFBag + GETNEWKEY(newKey, key); +#endif +#if CFDictionary + GETNEWVALUE(newValue); +#endif + any_t oldKey = hc->_keys[match]; + CF_OBJC_KVO_WILLCHANGE(hc, oldKey); +#if CFSet || CFBag + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[match], ~hc->_marker); + if (__CFHashKeyIsMagic(hc, newKey)) { + __CFSetFindNewMarker(hc); + } + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[match], newKey); +#endif +#if CFDictionary + any_t oldValue = hc->_values[match]; + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, hc->_values[match], newValue); +#endif + CF_OBJC_KVO_DIDCHANGE(hc, oldKey); +#if CFSet || CFBag + RELEASEKEY(oldKey); +#endif +#if CFDictionary + RELEASEVALUE(oldValue); +#endif + } +} + +void CFSetRemoveValue(CFMutableHashRef hc, const_any_pointer_t key) { + if (CFDictionary) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, void, hc, "removeObjectForKey:", key); + if (CFSet) CF_OBJC_FUNCDISPATCH1(__kCFHashTypeID, void, hc, "removeObject:", key); + __CFGenericValidateType(hc, __kCFHashTypeID); + switch (__CFHashGetType(hc)) { + case __kCFHashMutable: + break; + default: + CFAssert2(__CFHashGetType(hc) != __kCFHashImmutable, __kCFLogAssertion, "%s(): immutable collection %p passed to mutating operation", __PRETTY_FUNCTION__, hc); + break; + } + hc->_mutations++; + if (0 == hc->_bucketsUsed) return; + CFIndex match = __CFSetFindBuckets1(hc, (any_t)key); + if (kCFNotFound == match) return; + if (1 < __CFHashGetOccurrenceCount(hc, match)) { +#if CFBag + CF_OBJC_KVO_WILLCHANGE(hc, hc->_keys[match]); + hc->_values[match]--; + hc->_count--; + CF_OBJC_KVO_DIDCHANGE(hc, hc->_keys[match]); +#endif + } else { + CFAllocatorRef allocator = __CFGetAllocator(hc); + any_t oldKey = hc->_keys[match]; + CF_OBJC_KVO_WILLCHANGE(hc, oldKey); + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[match], ~hc->_marker); +#if CFDictionary + any_t oldValue = hc->_values[match]; + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, hc->_values[match], 0); +#endif +#if CFBag + hc->_values[match] = 0; +#endif + hc->_count--; + hc->_bucketsUsed--; + hc->_deletes++; + CF_OBJC_KVO_DIDCHANGE(hc, oldKey); + RELEASEKEY(oldKey); +#if CFDictionary + RELEASEVALUE(oldValue); +#endif + if (__CFSetShouldShrink(hc)) { + __CFSetGrow(hc, 0); + } else { + // When the probeskip == 1 always and only, a DELETED slot followed by an EMPTY slot + // can be converted to an EMPTY slot. By extension, a chain of DELETED slots followed + // by an EMPTY slot can be converted to EMPTY slots, which is what we do here. + if (match < hc->_bucketsNum - 1 && hc->_keys[match + 1] == hc->_marker) { + while (0 <= match && hc->_keys[match] == ~hc->_marker) { + hc->_keys[match] = hc->_marker; + hc->_deletes--; + match--; + } + } + } + } +} + +void CFSetRemoveAllValues(CFMutableHashRef hc) { + if (CFDictionary) CF_OBJC_FUNCDISPATCH0(__kCFHashTypeID, void, hc, "removeAllObjects"); + if (CFSet) CF_OBJC_FUNCDISPATCH0(__kCFHashTypeID, void, hc, "removeAllObjects"); + __CFGenericValidateType(hc, __kCFHashTypeID); + switch (__CFHashGetType(hc)) { + case __kCFHashMutable: + break; + default: + CFAssert2(__CFHashGetType(hc) != __kCFHashImmutable, __kCFLogAssertion, "%s(): immutable collection %p passed to mutating operation", __PRETTY_FUNCTION__, hc); + break; + } + hc->_mutations++; + if (0 == hc->_bucketsUsed) return; + CFAllocatorRef allocator = __CFGetAllocator(hc); + any_t *keys = hc->_keys; + for (CFIndex idx = 0, nbuckets = hc->_bucketsNum; idx < nbuckets; idx++) { + if (__CFHashKeyIsValue(hc, keys[idx])) { + any_t oldKey = keys[idx]; + CF_OBJC_KVO_WILLCHANGE(hc, oldKey); +#if CFDictionary || CFSet + hc->_count--; +#endif +#if CFBag + hc->_count -= hc->_values[idx]; +#endif + CFAllocatorRef keysAllocator = (hc->_xflags & __kCFHashWeakKeys) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(keysAllocator, hc->_keys[idx], ~hc->_marker); +#if CFDictionary + any_t oldValue = hc->_values[idx]; + CFAllocatorRef valuesAllocator = (hc->_xflags & __kCFHashWeakValues) ? kCFAllocatorNull : allocator; + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, hc->_values[idx], 0); +#endif +#if CFBag + hc->_values[idx] = 0; +#endif + hc->_bucketsUsed--; + hc->_deletes++; + CF_OBJC_KVO_DIDCHANGE(hc, oldKey); + RELEASEKEY(oldKey); +#if CFDictionary + RELEASEVALUE(oldValue); +#endif + } + } + for (CFIndex idx = 0, nbuckets = hc->_bucketsNum; idx < nbuckets; idx++) { + keys[idx] = hc->_marker; + } + hc->_deletes = 0; + hc->_bucketsUsed = 0; + hc->_count = 0; + if (__CFSetShouldShrink(hc) && (256 <= hc->_bucketsCap)) { + __CFSetGrow(hc, 128); + } +} + +#undef CF_OBJC_KVO_WILLCHANGE +#undef CF_OBJC_KVO_DIDCHANGE +
diff --git a/CoreFoundation/CFSet.h b/CoreFoundation/CFSet.h new file mode 100644 index 0000000..d9a095c --- /dev/null +++ b/CoreFoundation/CFSet.h
@@ -0,0 +1,516 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFSet.h + Copyright (c) 1998-2007, Apple Inc. All rights reserved. +*/ +/*! + @header CFSet + CFSet implements a container which stores unique values. +*/ + +#if !defined(__COREFOUNDATION_CFSET__) +#define __COREFOUNDATION_CFSET__ 1 + +#include <CoreFoundation/CFBase.h> + +CF_EXTERN_C_BEGIN + +/*! + @typedef CFSetRetainCallBack + Type of the callback function used by CFSets for retaining values. + @param allocator The allocator of the CFSet. + @param value The value to retain. + @result The value to store in the set, which is usually the value + parameter passed to this callback, but may be a different + value if a different value should be stored in the set. +*/ +typedef const void * (*CFSetRetainCallBack)(CFAllocatorRef allocator, const void *value); + +/*! + @typedef CFSetReleaseCallBack + Type of the callback function used by CFSets for releasing a retain on values. + @param allocator The allocator of the CFSet. + @param value The value to release. +*/ +typedef void (*CFSetReleaseCallBack)(CFAllocatorRef allocator, const void *value); + +/*! + @typedef CFSetCopyDescriptionCallBack + Type of the callback function used by CFSets for describing values. + @param value The value to describe. + @result A description of the specified value. +*/ +typedef CFStringRef (*CFSetCopyDescriptionCallBack)(const void *value); + +/*! + @typedef CFSetEqualCallBack + Type of the callback function used by CFSets for comparing values. + @param value1 The first value to compare. + @param value2 The second value to compare. + @result True if the values are equal, otherwise false. +*/ +typedef Boolean (*CFSetEqualCallBack)(const void *value1, const void *value2); + +/*! + @typedef CFSetHashCallBack + Type of the callback function used by CFSets for hashing values. + @param value The value to hash. + @result The hash of the value. +*/ +typedef CFHashCode (*CFSetHashCallBack)(const void *value); + +/*! + @typedef CFSetCallBacks + Structure containing the callbacks of a CFSet. + @field version The version number of the structure type being passed + in as a parameter to the CFSet creation functions. This + structure is version 0. + @field retain The callback used to add a retain for the set on + values as they are put into the set. This callback returns + the value to store in the set, which is usually the value + parameter passed to this callback, but may be a different + value if a different value should be stored in the set. + The set's allocator is passed as the first argument. + @field release The callback used to remove a retain previously added + for the set from values as they are removed from the + set. The set's allocator is passed as the first + argument. + @field copyDescription The callback used to create a descriptive + string representation of each value in the set. This is + used by the CFCopyDescription() function. + @field equal The callback used to compare values in the set for + equality for some operations. + @field hash The callback used to compare values in the set for + uniqueness for some operations. +*/ +typedef struct { + CFIndex version; + CFSetRetainCallBack retain; + CFSetReleaseCallBack release; + CFSetCopyDescriptionCallBack copyDescription; + CFSetEqualCallBack equal; + CFSetHashCallBack hash; +} CFSetCallBacks; + +/*! + @constant kCFTypeSetCallBacks + Predefined CFSetCallBacks structure containing a set of callbacks + appropriate for use when the values in a CFSet are all CFTypes. +*/ +CF_EXPORT +const CFSetCallBacks kCFTypeSetCallBacks; + +/*! + @constant kCFCopyStringSetCallBacks + Predefined CFSetCallBacks structure containing a set of callbacks + appropriate for use when the values in a CFSet should be copies + of a CFString. +*/ +CF_EXPORT +const CFSetCallBacks kCFCopyStringSetCallBacks; + +/*! + @typedef CFSetApplierFunction + Type of the callback function used by the apply functions of + CFSets. + @param value The current value from the set. + @param context The user-defined context parameter given to the apply + function. +*/ +typedef void (*CFSetApplierFunction)(const void *value, void *context); + +/*! + @typedef CFSetRef + This is the type of a reference to immutable CFSets. +*/ +typedef const struct __CFSet * CFSetRef; + +/*! + @typedef CFMutableSetRef + This is the type of a reference to mutable CFSets. +*/ +typedef struct __CFSet * CFMutableSetRef; + +/*! + @function CFSetGetTypeID + Returns the type identifier of all CFSet instances. +*/ +CF_EXPORT +CFTypeID CFSetGetTypeID(void); + +/*! + @function CFSetCreate + Creates a new immutable set with the given values. + @param allocator The CFAllocator which should be used to allocate + memory for the set and its storage for values. This + parameter may be NULL in which case the current default + CFAllocator is used. If this reference is not a valid + CFAllocator, the behavior is undefined. + @param values A C array of the pointer-sized values to be in the + set. This C array is not changed or freed by this function. + If this parameter is not a valid pointer to a C array of at + least numValues pointers, the behavior is undefined. + @param numValues The number of values to copy from the values C + array into the CFSet. This number will be the count of the + set. If this parameter is zero, negative, or greater than + the number of values actually in the values C array, the + behavior is undefined. + @param callBacks A C pointer to a CFSetCallBacks structure + initialized with the callbacks for the set to use on each + value in the set. A copy of the contents of the + callbacks structure is made, so that a pointer to a + structure on the stack can be passed in, or can be reused + for multiple set creations. If the version field of this + callbacks structure is not one of the defined ones for + CFSet, the behavior is undefined. The retain field may be + NULL, in which case the CFSet will do nothing to add a + retain to the contained values for the set. The release + field may be NULL, in which case the CFSet will do nothing + to remove the set's retain (if any) on the values when the + set is destroyed. If the copyDescription field is NULL, + the set will create a simple description for the value. If + the equal field is NULL, the set will use pointer equality + to test for equality of values. The hash field may be NULL, + in which case the CFSet will determine uniqueness by pointer + equality. This callbacks parameter + itself may be NULL, which is treated as if a valid structure + of version 0 with all fields NULL had been passed in. + Otherwise, if any of the fields are not valid pointers to + functions of the correct type, or this parameter is not a + valid pointer to a CFSetCallBacks callbacks structure, + the behavior is undefined. If any of the values put into the + set is not one understood by one of the callback functions + the behavior when that callback function is used is + undefined. + @result A reference to the new immutable CFSet. +*/ +CF_EXPORT +CFSetRef CFSetCreate(CFAllocatorRef allocator, const void **values, CFIndex numValues, const CFSetCallBacks *callBacks); + +/*! + @function CFSetCreateCopy + Creates a new immutable set with the values from the given set. + @param allocator The CFAllocator which should be used to allocate + memory for the set and its storage for values. This + parameter may be NULL in which case the current default + CFAllocator is used. If this reference is not a valid + CFAllocator, the behavior is undefined. + @param theSet The set which is to be copied. The values from the + set are copied as pointers into the new set (that is, + the values themselves are copied, not that which the values + point to, if anything). However, the values are also + retained by the new set. The count of the new set will + be the same as the copied set. The new set uses the same + callbacks as the set to be copied. If this parameter is + not a valid CFSet, the behavior is undefined. + @result A reference to the new immutable CFSet. +*/ +CF_EXPORT +CFSetRef CFSetCreateCopy(CFAllocatorRef allocator, CFSetRef theSet); + +/*! + @function CFSetCreateMutable + Creates a new empty mutable set. + @param allocator The CFAllocator which should be used to allocate + memory for the set and its storage for values. This + parameter may be NULL in which case the current default + CFAllocator is used. If this reference is not a valid + CFAllocator, the behavior is undefined. + @param capacity A hint about the number of values that will be held + by the CFSet. Pass 0 for no hint. The implementation may + ignore this hint, or may use it to optimize various + operations. A set's actual capacity is only limited by + address space and available memory constraints). If this + parameter is negative, the behavior is undefined. + @param callBacks A C pointer to a CFSetCallBacks structure + initialized with the callbacks for the set to use on each + value in the set. A copy of the contents of the + callbacks structure is made, so that a pointer to a + structure on the stack can be passed in, or can be reused + for multiple set creations. If the version field of this + callbacks structure is not one of the defined ones for + CFSet, the behavior is undefined. The retain field may be + NULL, in which case the CFSet will do nothing to add a + retain to the contained values for the set. The release + field may be NULL, in which case the CFSet will do nothing + to remove the set's retain (if any) on the values when the + set is destroyed. If the copyDescription field is NULL, + the set will create a simple description for the value. If + the equal field is NULL, the set will use pointer equality + to test for equality of values. The hash field may be NULL, + in which case the CFSet will determine uniqueness by pointer + equality. This callbacks parameter + itself may be NULL, which is treated as if a valid structure + of version 0 with all fields NULL had been passed in. + Otherwise, if any of the fields are not valid pointers to + functions of the correct type, or this parameter is not a + valid pointer to a CFSetCallBacks callbacks structure, + the behavior is undefined. If any of the values put into the + set is not one understood by one of the callback functions + the behavior when that callback function is used is + undefined. + @result A reference to the new mutable CFSet. +*/ +CF_EXPORT +CFMutableSetRef CFSetCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFSetCallBacks *callBacks); + +/*! + @function CFSetCreateMutableCopy + Creates a new immutable set with the values from the given set. + @param allocator The CFAllocator which should be used to allocate + memory for the set and its storage for values. This + parameter may be NULL in which case the current default + CFAllocator is used. If this reference is not a valid + CFAllocator, the behavior is undefined. + @param capacity A hint about the number of values that will be held + by the CFSet. Pass 0 for no hint. The implementation may + ignore this hint, or may use it to optimize various + operations. A set's actual capacity is only limited by + address space and available memory constraints). + This parameter must be greater than or equal + to the count of the set which is to be copied, or the + behavior is undefined. If this parameter is negative, the + behavior is undefined. + @param theSet The set which is to be copied. The values from the + set are copied as pointers into the new set (that is, + the values themselves are copied, not that which the values + point to, if anything). However, the values are also + retained by the new set. The count of the new set will + be the same as the copied set. The new set uses the same + callbacks as the set to be copied. If this parameter is + not a valid CFSet, the behavior is undefined. + @result A reference to the new mutable CFSet. +*/ +CF_EXPORT +CFMutableSetRef CFSetCreateMutableCopy(CFAllocatorRef allocator, CFIndex capacity, CFSetRef theSet); + +/*! + @function CFSetGetCount + Returns the number of values currently in the set. + @param theSet The set to be queried. If this parameter is not a valid + CFSet, the behavior is undefined. + @result The number of values in the set. +*/ +CF_EXPORT +CFIndex CFSetGetCount(CFSetRef theSet); + +/*! + @function CFSetGetCountOfValue + Counts the number of times the given value occurs in the set. Since + sets by definition contain only one instance of a value, this function + is synomous to SFSetContainsValue. + @param theSet The set to be searched. If this parameter is not a + valid CFSet, the behavior is undefined. + @param value The value for which to find matches in the set. The + equal() callback provided when the set was created is + used to compare. If the equal() callback was NULL, pointer + equality (in C, ==) is used. If value, or any of the values + in the set, are not understood by the equal() callback, + the behavior is undefined. + @result The number of times the given value occurs in the set. +*/ +CF_EXPORT +CFIndex CFSetGetCountOfValue(CFSetRef theSet, const void *value); + +/*! + @function CFSetContainsValue + Reports whether or not the value is in the set. + @param theSet The set to be searched. If this parameter is not a + valid CFSet, the behavior is undefined. + @param value The value for which to find matches in the set. The + equal() callback provided when the set was created is + used to compare. If the equal() callback was NULL, pointer + equality (in C, ==) is used. If value, or any of the values + in the set, are not understood by the equal() callback, + the behavior is undefined. + @result true, if the value is in the set, otherwise false. +*/ +CF_EXPORT +Boolean CFSetContainsValue(CFSetRef theSet, const void *value); + +/*! + @function CFSetGetValue + Retrieves a value in the set which hashes the same as the specified value. + @param theSet The set to be queried. If this parameter is not a + valid CFSet, the behavior is undefined. + @param value The value to retrieve. The equal() callback provided when + the set was created is used to compare. If the equal() callback + was NULL, pointer equality (in C, ==) is used. If a value, or + any of the values in the set, are not understood by the equal() + callback, the behavior is undefined. + @result The value in the set with the given hash. +*/ +CF_EXPORT +const void *CFSetGetValue(CFSetRef theSet, const void *value); + +/*! + @function CFSetGetValue + Retrieves a value in the set which hashes the same as the specified value, + if present. + @param theSet The set to be queried. If this parameter is not a + valid CFSet, the behavior is undefined. + @param candidate This value is hashed and compared with values in the + set to determine which value to retrieve. The equal() callback provided when + the set was created is used to compare. If the equal() callback + was NULL, pointer equality (in C, ==) is used. If a value, or + any of the values in the set, are not understood by the equal() + callback, the behavior is undefined. + @param value A pointer to memory which should be filled with the + pointer-sized value if a matching value is found. If no + match is found, the contents of the storage pointed to by + this parameter are undefined. This parameter may be NULL, + in which case the value from the dictionary is not returned + (but the return value of this function still indicates + whether or not the value was present). + @result True if the value was present in the set, otherwise false. +*/ +CF_EXPORT +Boolean CFSetGetValueIfPresent(CFSetRef theSet, const void *candidate, const void **value); + +/*! + @function CFSetGetValues + Fills the buffer with values from the set. + @param theSet The set to be queried. If this parameter is not a + valid CFSet, the behavior is undefined. + @param values A C array of pointer-sized values to be filled with + values from the set. The values in the C array are ordered + in the same order in which they appear in the set. If this + parameter is not a valid pointer to a C array of at least + CFSetGetCount() pointers, the behavior is undefined. +*/ +CF_EXPORT +void CFSetGetValues(CFSetRef theSet, const void **values); + +/*! + @function CFSetApplyFunction + Calls a function once for each value in the set. + @param theSet The set to be operated upon. If this parameter is not + a valid CFSet, the behavior is undefined. + @param applier The callback function to call once for each value in + the given set. If this parameter is not a + pointer to a function of the correct prototype, the behavior + is undefined. If there are values in the set which the + applier function does not expect or cannot properly apply + to, the behavior is undefined. + @param context A pointer-sized user-defined value, which is passed + as the second parameter to the applier function, but is + otherwise unused by this function. If the context is not + what is expected by the applier function, the behavior is + undefined. +*/ +CF_EXPORT +void CFSetApplyFunction(CFSetRef theSet, CFSetApplierFunction applier, void *context); + +/*! + @function CFSetAddValue + Adds the value to the set if it is not already present. + @param theSet The set to which the value is to be added. If this + parameter is not a valid mutable CFSet, the behavior is + undefined. + @param value The value to add to the set. The value is retained by + the set using the retain callback provided when the set + was created. If the value is not of the sort expected by the + retain callback, the behavior is undefined. The count of the + set is increased by one. +*/ +CF_EXPORT +void CFSetAddValue(CFMutableSetRef theSet, const void *value); + +/*! + @function CFSetReplaceValue + Replaces the value in the set if it is present. + @param theSet The set to which the value is to be replaced. If this + parameter is not a valid mutable CFSet, the behavior is + undefined. + @param value The value to replace in the set. The equal() callback provided when + the set was created is used to compare. If the equal() callback + was NULL, pointer equality (in C, ==) is used. If a value, or + any of the values in the set, are not understood by the equal() + callback, the behavior is undefined. The value is retained by + the set using the retain callback provided when the set + was created. If the value is not of the sort expected by the + retain callback, the behavior is undefined. The count of the + set is increased by one. +*/ +CF_EXPORT +void CFSetReplaceValue(CFMutableSetRef theSet, const void *value); + +/*! + @function CFSetSetValue + Replaces the value in the set if it is present, or adds the value to + the set if it is absent. + @param theSet The set to which the value is to be replaced. If this + parameter is not a valid mutable CFSet, the behavior is + undefined. + @param value The value to set in the CFSet. The equal() callback provided when + the set was created is used to compare. If the equal() callback + was NULL, pointer equality (in C, ==) is used. If a value, or + any of the values in the set, are not understood by the equal() + callback, the behavior is undefined. The value is retained by + the set using the retain callback provided when the set + was created. If the value is not of the sort expected by the + retain callback, the behavior is undefined. The count of the + set is increased by one. +*/ +CF_EXPORT +void CFSetSetValue(CFMutableSetRef theSet, const void *value); + +/*! + @function CFSetRemoveValue + Removes the specified value from the set. + @param theSet The set from which the value is to be removed. + If this parameter is not a valid mutable CFSet, + the behavior is undefined. + @param value The value to remove. The equal() callback provided when + the set was created is used to compare. If the equal() callback + was NULL, pointer equality (in C, ==) is used. If a value, or + any of the values in the set, are not understood by the equal() + callback, the behavior is undefined. +*/ +CF_EXPORT +void CFSetRemoveValue(CFMutableSetRef theSet, const void *value); + +/*! + @function CFSetRemoveAllValues + Removes all the values from the set, making it empty. + @param theSet The set from which all of the values are to be + removed. If this parameter is not a valid mutable CFSet, + the behavior is undefined. +*/ +CF_EXPORT +void CFSetRemoveAllValues(CFMutableSetRef theSet); + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFSET__ */ +
diff --git a/CoreFoundation/CFSocket.c b/CoreFoundation/CFSocket.c new file mode 100644 index 0000000..5dc72f8 --- /dev/null +++ b/CoreFoundation/CFSocket.c
@@ -0,0 +1,2393 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFSocket.c + Copyright (c) 1999-2007 Apple Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#define _DARWIN_UNLIMITED_SELECT 1 + +#include <CoreFoundation/CFSocket.h> +#include <sys/types.h> +#include <math.h> +#include <limits.h> +#include <CoreFoundation/CFArray.h> +#include <CoreFoundation/CFData.h> +#include <CoreFoundation/CFDictionary.h> +#include <CoreFoundation/CFRunLoop.h> +#include <CoreFoundation/CFString.h> +#include <CoreFoundation/CFPropertyList.h> +#include "CFInternal.h" +#if DEPLOYMENT_TARGET_MACOSX +#include <libc.h> +#include <dlfcn.h> +#define SOCK_DATA_TYPE uint8_t* +#elif DEPLOYMENT_TARGET_WINDOWS +#include <winsock2.h> +#include <ws2tcpip.h> +#include <fcntl.h> +#include <io.h> +#include <stdio.h> +// Careful with remapping these - different WinSock routines return different errors than +// on BSD, so if these are used many places they won't work. +#define EINPROGRESS WSAEINPROGRESS +#ifdef EBADF +#undef EBADF +#endif +#define EBADF WSAENOTSOCK +extern void gettimeofday(struct timeval *tv, void *dummy); +#elif DEPLOYMENT_TARGET_LINUX +#include <dlfcn.h> +#include <fcntl.h> +#include <unistd.h> +#include <netinet/in.h> +#include <sys/ioctl.h> +#include <sys/param.h> +#include <sys/socket.h> +#endif /* DEPLOYMENT_TARGET_MACOSX */ +#include "auto_stubs.h" + +#if defined(DEBUG) +#include <stdio.h> +#endif + +#if DEPLOYMENT_TARGET_ANDROID +#define NBBY 8 +typedef long int fd_mask; +#endif + +#if DEPLOYMENT_TARGET_WINDOWS +/* Use crazy Microsoft signatures */ +#define SOCK_DATA char* +#define SOCK_CONST_DATA const char* +#define read _read + +/* The following macro is from: + * The olsr.org Optimized Link-State Routing daemon (olsrd) + * Copyright (c) 2004, Thomas Lopatic (thomas@lopatic.de) + * All rights reserved. + * + * BSD license + */ +#define timersub(x, y, z) \ + do \ + { \ + (z)->tv_sec = (x)->tv_sec - (y)->tv_sec; \ + \ + (z)->tv_usec = (x)->tv_usec - (y)->tv_usec; \ + \ + if ((z)->tv_usec < 0) \ + { \ + (z)->tv_sec--; \ + (z)->tv_usec += 1000000; \ + } \ + } \ + while (0) + +#else +#define SOCK_DATA void* +#define SOCK_CONST_DATA const void* +#endif + +// On Mach we use a v0 RunLoopSource to make client callbacks. That source is signalled by a +// separate SocketManager thread who uses select() to watch the sockets' fds. + +//#define LOG_CFSOCKET + +#if !DEPLOYMENT_TARGET_WINDOWS +#define INVALID_SOCKET (CFSocketNativeHandle)(-1) +#endif + +enum { + kCFSocketLeaveErrors = 64 // candidate for publicization in future +}; + +static uint16_t __CFSocketDefaultNameRegistryPortNumber = 2454; + +CONST_STRING_DECL(kCFSocketCommandKey, "Command") +CONST_STRING_DECL(kCFSocketNameKey, "Name") +CONST_STRING_DECL(kCFSocketValueKey, "Value") +CONST_STRING_DECL(kCFSocketResultKey, "Result") +CONST_STRING_DECL(kCFSocketErrorKey, "Error") +CONST_STRING_DECL(kCFSocketRegisterCommand, "Register") +CONST_STRING_DECL(kCFSocketRetrieveCommand, "Retrieve") +CONST_STRING_DECL(__kCFSocketRegistryRequestRunLoopMode, "CFSocketRegistryRequest") + +#if DEPLOYMENT_TARGET_WINDOWS +static Boolean __CFSocketWinSockInitialized = false; +#else +#define closesocket(a) close((a)) +#define ioctlsocket(a,b,c) ioctl((a),(b),(c)) +#endif + + +CF_INLINE int __CFSocketLastError(void) { +#if DEPLOYMENT_TARGET_WINDOWS + return WSAGetLastError(); +#else + return thread_errno(); +#endif +} + +CF_INLINE CFIndex __CFSocketFdGetSize(CFDataRef fdSet) { +#if DEPLOYMENT_TARGET_WINDOWS + fd_set* set = (fd_set*)CFDataGetBytePtr(fdSet); + return set ? set->fd_count : 0; +#else + return NBBY * CFDataGetLength(fdSet); +#endif +} + +CF_INLINE Boolean __CFSocketFdSet(CFSocketNativeHandle sock, CFMutableDataRef fdSet) { + /* returns true if a change occurred, false otherwise */ + Boolean retval = false; + if (INVALID_SOCKET != sock && 0 <= sock) { +#if DEPLOYMENT_TARGET_WINDOWS + fd_set* set = (fd_set*)CFDataGetMutableBytePtr(fdSet); + if ((set->fd_count * sizeof(SOCKET) + sizeof(u_int)) >= CFDataGetLength(fdSet)) { + CFDataIncreaseLength(fdSet, sizeof(SOCKET)); + set = (fd_set*)CFDataGetMutableBytePtr(fdSet); + } + if (!FD_ISSET(sock, set)) { + retval = true; + FD_SET(sock, set); + } +#else + CFIndex numFds = NBBY * CFDataGetLength(fdSet); + fd_mask *fds_bits; + if (sock >= numFds) { + CFIndex oldSize = numFds / NFDBITS, newSize = (sock + NFDBITS) / NFDBITS, changeInBytes = (newSize - oldSize) * sizeof(fd_mask); + CFDataIncreaseLength(fdSet, changeInBytes); + fds_bits = (fd_mask *)CFDataGetMutableBytePtr(fdSet); + memset(fds_bits + oldSize, 0, changeInBytes); + } else { + fds_bits = (fd_mask *)CFDataGetMutableBytePtr(fdSet); + } + if (!FD_ISSET(sock, (fd_set *)fds_bits)) { + retval = true; + FD_SET(sock, (fd_set *)fds_bits); + } +#endif + } + return retval; +} + + +#define NEW_SOCKET 0 +#if NEW_SOCKET + +__private_extern__ void __CFSocketInitialize(void) {} + +#else + +#define MAX_SOCKADDR_LEN 256 +#define MAX_DATA_SIZE 65535 +#define MAX_CONNECTION_ORIENTED_DATA_SIZE 32768 + +/* locks are to be acquired in the following order: + (1) __CFAllSocketsLock + (2) an individual CFSocket's lock + (3) __CFActiveSocketsLock +*/ +static CFSpinLock_t __CFAllSocketsLock = CFSpinLockInit; /* controls __CFAllSockets */ +static CFMutableDictionaryRef __CFAllSockets = NULL; +static CFSpinLock_t __CFActiveSocketsLock = CFSpinLockInit; /* controls __CFRead/WriteSockets, __CFRead/WriteSocketsFds, __CFSocketManagerThread, and __CFSocketManagerIteration */ +static volatile UInt32 __CFSocketManagerIteration = 0; +static CFMutableArrayRef __CFWriteSockets = NULL; +static CFMutableArrayRef __CFReadSockets = NULL; +static CFMutableDataRef __CFWriteSocketsFds = NULL; +static CFMutableDataRef __CFReadSocketsFds = NULL; +#if DEPLOYMENT_TARGET_WINDOWS +// We need to select on exceptFDs on Win32 to hear of connect failures +static CFMutableDataRef __CFExceptSocketsFds = NULL; +#endif +static CFDataRef zeroLengthData = NULL; +static Boolean __CFReadSocketsTimeoutInvalid = true; /* rebuild the timeout value before calling select */ + +static CFSocketNativeHandle __CFWakeupSocketPair[2] = {INVALID_SOCKET, INVALID_SOCKET}; +static void *__CFSocketManagerThread = NULL; + +static CFTypeID __kCFSocketTypeID = _kCFRuntimeNotATypeID; +static void __CFSocketDoCallback(CFSocketRef s, CFDataRef data, CFDataRef address, CFSocketNativeHandle sock); + +struct __CFSocket { + CFRuntimeBase _base; + struct { + unsigned client:8; // flags set by client (reenable, CloseOnInvalidate) + unsigned disabled:8; // flags marking disabled callbacks + unsigned connected:1; // Are we connected yet? (also true for connectionless sockets) + unsigned writableHint:1; // Did the polling the socket show it to be writable? + unsigned closeSignaled:1; // Have we seen FD_CLOSE? (only used on Win32) + unsigned unused:13; + } _f; + CFSpinLock_t _lock; + CFSpinLock_t _writeLock; + CFSocketNativeHandle _socket; /* immutable */ + SInt32 _socketType; + SInt32 _errorCode; + CFDataRef _address; + CFDataRef _peerAddress; + SInt32 _socketSetCount; + CFRunLoopSourceRef _source0; // v0 RLS, messaged from SocketMgr + CFMutableArrayRef _runLoops; + CFSocketCallBack _callout; /* immutable */ + CFSocketContext _context; /* immutable */ + CFMutableArrayRef _dataQueue; // queues to pass data from SocketMgr thread + CFMutableArrayRef _addressQueue; + + struct timeval _readBufferTimeout; + CFMutableDataRef _readBuffer; + CFIndex _bytesToBuffer; /* is length of _readBuffer */ + CFIndex _bytesToBufferPos; /* where the next _CFSocketRead starts from */ + CFIndex _bytesToBufferReadPos; /* Where the buffer will next be read into (always after _bytesToBufferPos, but less than _bytesToBuffer) */ + Boolean _atEOF; + int _bufferedReadError; + + CFMutableDataRef _leftoverBytes; +}; + +/* Bit 6 in the base reserved bits is used for write-signalled state (mutable) */ +/* Bit 5 in the base reserved bits is used for read-signalled state (mutable) */ +/* Bit 4 in the base reserved bits is used for invalid state (mutable) */ +/* Bits 0-3 in the base reserved bits are used for callback types (immutable) */ +/* Of this, bits 0-1 are used for the read callback type. */ + +CF_INLINE Boolean __CFSocketIsWriteSignalled(CFSocketRef s) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)s)->_cfinfo[CF_INFO_BITS], 6, 6); +} + +CF_INLINE void __CFSocketSetWriteSignalled(CFSocketRef s) { + __CFBitfieldSetValue(((CFRuntimeBase *)s)->_cfinfo[CF_INFO_BITS], 6, 6, 1); +} + +CF_INLINE void __CFSocketUnsetWriteSignalled(CFSocketRef s) { + __CFBitfieldSetValue(((CFRuntimeBase *)s)->_cfinfo[CF_INFO_BITS], 6, 6, 0); +} + +CF_INLINE Boolean __CFSocketIsReadSignalled(CFSocketRef s) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)s)->_cfinfo[CF_INFO_BITS], 5, 5); +} + +CF_INLINE void __CFSocketSetReadSignalled(CFSocketRef s) { + __CFBitfieldSetValue(((CFRuntimeBase *)s)->_cfinfo[CF_INFO_BITS], 5, 5, 1); +} + +CF_INLINE void __CFSocketUnsetReadSignalled(CFSocketRef s) { + __CFBitfieldSetValue(((CFRuntimeBase *)s)->_cfinfo[CF_INFO_BITS], 5, 5, 0); +} + +CF_INLINE Boolean __CFSocketIsValid(CFSocketRef s) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)s)->_cfinfo[CF_INFO_BITS], 4, 4); +} + +CF_INLINE void __CFSocketSetValid(CFSocketRef s) { + __CFBitfieldSetValue(((CFRuntimeBase *)s)->_cfinfo[CF_INFO_BITS], 4, 4, 1); +} + +CF_INLINE void __CFSocketUnsetValid(CFSocketRef s) { + __CFBitfieldSetValue(((CFRuntimeBase *)s)->_cfinfo[CF_INFO_BITS], 4, 4, 0); +} + +CF_INLINE uint8_t __CFSocketCallBackTypes(CFSocketRef s) { + return (uint8_t)__CFBitfieldGetValue(((const CFRuntimeBase *)s)->_cfinfo[CF_INFO_BITS], 3, 0); +} + +CF_INLINE uint8_t __CFSocketReadCallBackType(CFSocketRef s) { + return (uint8_t)__CFBitfieldGetValue(((const CFRuntimeBase *)s)->_cfinfo[CF_INFO_BITS], 1, 0); +} + +CF_INLINE void __CFSocketSetCallBackTypes(CFSocketRef s, uint8_t types) { + __CFBitfieldSetValue(((CFRuntimeBase *)s)->_cfinfo[CF_INFO_BITS], 3, 0, types & 0xF); +} + +CF_INLINE void __CFSocketLock(CFSocketRef s) { + __CFSpinLock(&(s->_lock)); +} + +CF_INLINE void __CFSocketUnlock(CFSocketRef s) { + __CFSpinUnlock(&(s->_lock)); +} + +CF_INLINE Boolean __CFSocketIsConnectionOriented(CFSocketRef s) { + return (SOCK_STREAM == s->_socketType || SOCK_SEQPACKET == s->_socketType); +} + +CF_INLINE Boolean __CFSocketIsScheduled(CFSocketRef s) { + return (s->_socketSetCount > 0); +} + +CF_INLINE void __CFSocketEstablishAddress(CFSocketRef s) { + /* socket should already be locked */ + uint8_t name[MAX_SOCKADDR_LEN]; + int namelen = sizeof(name); + if (__CFSocketIsValid(s) && NULL == s->_address && INVALID_SOCKET != s->_socket && 0 == getsockname(s->_socket, (struct sockaddr *)name, (socklen_t *)&namelen) && NULL != name && 0 < namelen) { + s->_address = CFDataCreate(CFGetAllocator(s), name, namelen); + } +} + +CF_INLINE void __CFSocketEstablishPeerAddress(CFSocketRef s) { + /* socket should already be locked */ + uint8_t name[MAX_SOCKADDR_LEN]; + int namelen = sizeof(name); + if (__CFSocketIsValid(s) && NULL == s->_peerAddress && INVALID_SOCKET != s->_socket && 0 == getpeername(s->_socket, (struct sockaddr *)name, (socklen_t *)&namelen) && NULL != name && 0 < namelen) { + s->_peerAddress = CFDataCreate(CFGetAllocator(s), name, namelen); + } +} + +static Boolean __CFNativeSocketIsValid(CFSocketNativeHandle sock) { +#if DEPLOYMENT_TARGET_WINDOWS + SInt32 flags = ioctlsocket (sock, FIONREAD, 0); + return (0 == flags); +#else + SInt32 flags = fcntl(sock, F_GETFL, 0); + return !(0 > flags && EBADF == thread_errno()); +#endif +} + +CF_INLINE Boolean __CFSocketFdClr(CFSocketNativeHandle sock, CFMutableDataRef fdSet) { + /* returns true if a change occurred, false otherwise */ + Boolean retval = false; + if (INVALID_SOCKET != sock && 0 <= sock) { +#if DEPLOYMENT_TARGET_WINDOWS + fd_set* set = (fd_set*)CFDataGetMutableBytePtr(fdSet); + if (FD_ISSET(sock, set)) { + retval = true; + FD_CLR(sock, set); + } +#else + CFIndex numFds = NBBY * CFDataGetLength(fdSet); + fd_mask *fds_bits; + if (sock < numFds) { + fds_bits = (fd_mask *)CFDataGetMutableBytePtr(fdSet); + if (FD_ISSET(sock, (fd_set *)fds_bits)) { + retval = true; + FD_CLR(sock, (fd_set *)fds_bits); + } + } +#endif + } + return retval; +} + +static SInt32 __CFSocketCreateWakeupSocketPair(void) { +#if !DEPLOYMENT_TARGET_WINDOWS + return socketpair(PF_LOCAL, SOCK_DGRAM, 0, __CFWakeupSocketPair); +#else + //??? should really use native Win32 facilities + UInt32 i; + SInt32 error = 0; + struct sockaddr_in address[2]; + int namelen = sizeof(struct sockaddr_in); + for (i = 0; i < 2; i++) { + __CFWakeupSocketPair[i] = (CFSocketNativeHandle)socket(PF_INET, SOCK_DGRAM, 0); + memset(&(address[i]), 0, sizeof(struct sockaddr_in)); + address[i].sin_family = AF_INET; + address[i].sin_addr.s_addr = htonl(INADDR_LOOPBACK); + if (0 <= error) error = bind(__CFWakeupSocketPair[i], (struct sockaddr *)&(address[i]), sizeof(struct sockaddr_in)); + if (0 <= error) error = getsockname(__CFWakeupSocketPair[i], (struct sockaddr *)&(address[i]), &namelen); + if (sizeof(struct sockaddr_in) != namelen) error = -1; + } + if (0 <= error) error = connect(__CFWakeupSocketPair[0], (struct sockaddr *)&(address[1]), sizeof(struct sockaddr_in)); + if (0 <= error) error = connect(__CFWakeupSocketPair[1], (struct sockaddr *)&(address[0]), sizeof(struct sockaddr_in)); + if (0 > error) { + closesocket(__CFWakeupSocketPair[0]); + closesocket(__CFWakeupSocketPair[1]); + } + return error; +#endif +} + + +// Version 0 RunLoopSources set a mask in an FD set to control what socket activity we hear about. +CF_INLINE Boolean __CFSocketSetFDForRead(CFSocketRef s) { + __CFReadSocketsTimeoutInvalid = true; + return __CFSocketFdSet(s->_socket, __CFReadSocketsFds); +} + +CF_INLINE Boolean __CFSocketClearFDForRead(CFSocketRef s) { + __CFReadSocketsTimeoutInvalid = true; + return __CFSocketFdClr(s->_socket, __CFReadSocketsFds); +} + +CF_INLINE Boolean __CFSocketSetFDForWrite(CFSocketRef s) { + return __CFSocketFdSet(s->_socket, __CFWriteSocketsFds); +} + +CF_INLINE Boolean __CFSocketClearFDForWrite(CFSocketRef s) { + return __CFSocketFdClr(s->_socket, __CFWriteSocketsFds); +} + +#if DEPLOYMENT_TARGET_WINDOWS +static Boolean WinSockUsed = FALSE; + +static void __CFSocketInitializeWinSock_Guts(void) { + if (!WinSockUsed) { + WinSockUsed = TRUE; + WORD versionRequested = MAKEWORD(2, 0); + WSADATA wsaData; + int errorStatus = WSAStartup(versionRequested, &wsaData); + if (errorStatus != 0 || LOBYTE(wsaData.wVersion) != LOBYTE(versionRequested) || HIBYTE(wsaData.wVersion) != HIBYTE(versionRequested)) { + WSACleanup(); + CFLog(0, CFSTR("*** Could not initialize WinSock subsystem!!!")); + } + } +} + +CF_EXPORT void __CFSocketInitializeWinSock(void) { + __CFSpinLock(&__CFActiveSocketsLock); + __CFSocketInitializeWinSock_Guts(); + __CFSpinUnlock(&__CFActiveSocketsLock); +} + +__private_extern__ void __CFSocketCleanup(void) { + __CFSpinLock(&__CFActiveSocketsLock); + if (NULL != __CFReadSockets) { + CFRelease(__CFWriteSockets); + __CFWriteSockets = NULL; + CFRelease(__CFReadSockets); + __CFReadSockets = NULL; + CFRelease(__CFWriteSocketsFds); + __CFWriteSocketsFds = NULL; + CFRelease(__CFReadSocketsFds); + __CFReadSocketsFds = NULL; + CFRelease(__CFExceptSocketsFds); + __CFExceptSocketsFds = NULL; + CFRelease(zeroLengthData); + zeroLengthData = NULL; + } + if (NULL != __CFAllSockets) { + CFRelease(__CFAllSockets); + __CFAllSockets = NULL; + } + if (INVALID_SOCKET != __CFWakeupSocketPair[0]) { + closesocket(__CFWakeupSocketPair[0]); + __CFWakeupSocketPair[0] = INVALID_SOCKET; + } + if (INVALID_SOCKET != __CFWakeupSocketPair[1]) { + closesocket(__CFWakeupSocketPair[1]); + __CFWakeupSocketPair[1] = INVALID_SOCKET; + } + if (WinSockUsed) { + WSACleanup(); + } + __CFSpinUnlock(&__CFActiveSocketsLock); +} +#endif + + +// CFNetwork needs to call this, especially for Win32 to get WSAStartup +static void __CFSocketInitializeSockets(void) { + __CFWriteSockets = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, NULL); + __CFReadSockets = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, NULL); + __CFWriteSocketsFds = CFDataCreateMutable(kCFAllocatorSystemDefault, 0); + __CFReadSocketsFds = CFDataCreateMutable(kCFAllocatorSystemDefault, 0); + zeroLengthData = CFDataCreateMutable(kCFAllocatorSystemDefault, 0); +#if DEPLOYMENT_TARGET_WINDOWS + __CFSocketInitializeWinSock_Guts(); + // make sure we have space for the count field and the first socket + CFDataIncreaseLength(__CFWriteSocketsFds, sizeof(u_int) + sizeof(SOCKET)); + CFDataIncreaseLength(__CFReadSocketsFds, sizeof(u_int) + sizeof(SOCKET)); + __CFExceptSocketsFds = CFDataCreateMutable(kCFAllocatorSystemDefault, 0); + CFDataIncreaseLength(__CFExceptSocketsFds, sizeof(u_int) + sizeof(SOCKET)); +#endif + if (0 > __CFSocketCreateWakeupSocketPair()) { + CFLog(kCFLogLevelWarning, CFSTR("*** Could not create wakeup socket pair for CFSocket!!!")); + } else { +#if DEPLOYMENT_TARGET_WINDOWS + u_long yes = 1; +#else + UInt32 yes = 1; +#endif + /* wakeup sockets must be non-blocking */ + ioctlsocket(__CFWakeupSocketPair[0], FIONBIO, &yes); + ioctlsocket(__CFWakeupSocketPair[1], FIONBIO, &yes); + __CFSocketFdSet(__CFWakeupSocketPair[1], __CFReadSocketsFds); + } +} + +static CFRunLoopRef __CFSocketCopyRunLoopToWakeUp(CFSocketRef s) { + CFRunLoopRef rl = NULL; + SInt32 idx, cnt = CFArrayGetCount(s->_runLoops); + if (0 < cnt) { + rl = (CFRunLoopRef)CFArrayGetValueAtIndex(s->_runLoops, 0); + for (idx = 1; NULL != rl && idx < cnt; idx++) { + CFRunLoopRef value = (CFRunLoopRef)CFArrayGetValueAtIndex(s->_runLoops, idx); + if (value != rl) rl = NULL; + } + if (NULL == rl) { /* more than one different rl, so we must pick one */ + /* ideally, this would be a run loop which isn't also in a + * signaled state for this or another source, but that's tricky; + * we pick one that is running in an appropriate mode for this + * source, and from those if possible one that is waiting; then + * we move this run loop to the end of the list to scramble them + * a bit, and always search from the front */ + Boolean foundIt = false, foundBackup = false; + SInt32 foundIdx = 0; + for (idx = 0; !foundIt && idx < cnt; idx++) { + CFRunLoopRef value = (CFRunLoopRef)CFArrayGetValueAtIndex(s->_runLoops, idx); + CFStringRef currentMode = CFRunLoopCopyCurrentMode(value); + if (NULL != currentMode) { + if (CFRunLoopContainsSource(value, s->_source0, currentMode)) { + if (CFRunLoopIsWaiting(value)) { + foundIdx = idx; + foundIt = true; + } else if (!foundBackup) { + foundIdx = idx; + foundBackup = true; + } + } + CFRelease(currentMode); + } + } + rl = (CFRunLoopRef)CFArrayGetValueAtIndex(s->_runLoops, foundIdx); + CFRetain(rl); + CFArrayRemoveValueAtIndex(s->_runLoops, foundIdx); + CFArrayAppendValue(s->_runLoops, rl); + } else { + CFRetain(rl); + } + } + return rl; +} + +// If callBackNow, we immediately do client callbacks, else we have to signal a v0 RunLoopSource so the +// callbacks can happen in another thread. +static void __CFSocketHandleWrite(CFSocketRef s, Boolean callBackNow) { + SInt32 errorCode = 0; + int errorSize = sizeof(errorCode); + CFOptionFlags writeCallBacksAvailable; + + if (!CFSocketIsValid(s)) return; +#if DEPLOYMENT_TARGET_WINDOWS + if (0 != (s->_f.client & kCFSocketLeaveErrors) || 0 != getsockopt(s->_socket, SOL_SOCKET, SO_ERROR, (char *)&errorCode, (socklen_t *)&errorSize)) errorCode = 0; // cast for WinSock bad API +#else + if (0 != (s->_f.client & kCFSocketLeaveErrors) || 0 != getsockopt(s->_socket, SOL_SOCKET, SO_ERROR, (void *)&errorCode, (socklen_t *)&errorSize)) errorCode = 0; // cast for WinSock bad API +#endif +#if defined(LOG_CFSOCKET) + if (errorCode) fprintf(stdout, "error %ld on socket %d\n", errorCode, s->_socket); +#endif + __CFSocketLock(s); + writeCallBacksAvailable = __CFSocketCallBackTypes(s) & (kCFSocketWriteCallBack | kCFSocketConnectCallBack); + if ((s->_f.client & kCFSocketConnectCallBack) != 0) writeCallBacksAvailable &= ~kCFSocketConnectCallBack; + if (!__CFSocketIsValid(s) || ((s->_f.disabled & writeCallBacksAvailable) == writeCallBacksAvailable)) { + __CFSocketUnlock(s); + return; + } + s->_errorCode = errorCode; + __CFSocketSetWriteSignalled(s); +#if defined(LOG_CFSOCKET) + fprintf(stdout, "write signaling source for socket %d\n", s->_socket); +#endif + if (callBackNow) { + __CFSocketDoCallback(s, NULL, NULL, 0); + } else { + CFRunLoopRef rl; + CFRunLoopSourceSignal(s->_source0); + rl = __CFSocketCopyRunLoopToWakeUp(s); + __CFSocketUnlock(s); + if (NULL != rl) { + CFRunLoopWakeUp(rl); + CFRelease(rl); + } + } +} + +static void __CFSocketHandleRead(CFSocketRef s, Boolean causedByTimeout) +{ + CFDataRef data = NULL, address = NULL; + CFSocketNativeHandle sock = INVALID_SOCKET; + if (!CFSocketIsValid(s)) return; + if (__CFSocketReadCallBackType(s) == kCFSocketDataCallBack) { + uint8_t bufferArray[MAX_CONNECTION_ORIENTED_DATA_SIZE], *buffer; + uint8_t name[MAX_SOCKADDR_LEN]; +#if !DEPLOYMENT_TARGET_WINDOWS + int namelen = sizeof(name); +#else + int namelen = 0; +#endif + SInt32 recvlen = 0; + if (__CFSocketIsConnectionOriented(s)) { + buffer = bufferArray; + recvlen = recvfrom(s->_socket, (SOCK_DATA)buffer, MAX_CONNECTION_ORIENTED_DATA_SIZE, 0, (struct sockaddr *)name, (socklen_t *)&namelen); + } else { + buffer = (uint8_t*)malloc(MAX_DATA_SIZE); + if (buffer) recvlen = recvfrom(s->_socket, (SOCK_DATA)buffer, MAX_DATA_SIZE, 0, (struct sockaddr *)name, (socklen_t *)&namelen); + } +#if defined(LOG_CFSOCKET) + fprintf(stdout, "read %ld bytes on socket %d\n", recvlen, s->_socket); +#endif + if (0 >= recvlen) { + //??? should return error if <0 + /* zero-length data is the signal for perform to invalidate */ + data = (CFDataRef)CFRetain(zeroLengthData); + } else { + data = CFDataCreate(CFGetAllocator(s), buffer, recvlen); + } + if (buffer && buffer != bufferArray) free(buffer); + __CFSocketLock(s); + if (!__CFSocketIsValid(s)) { + CFRelease(data); + __CFSocketUnlock(s); + return; + } + __CFSocketSetReadSignalled(s); + if (NULL != name && 0 < namelen) { + //??? possible optimizations: uniquing; storing last value + address = CFDataCreate(CFGetAllocator(s), name, namelen); + } else if (__CFSocketIsConnectionOriented(s)) { + if (NULL == s->_peerAddress) __CFSocketEstablishPeerAddress(s); + if (NULL != s->_peerAddress) address = (CFDataRef)CFRetain(s->_peerAddress); + } + if (NULL == address) { + address = (CFDataRef)CFRetain(zeroLengthData); + } + if (NULL == s->_dataQueue) { + s->_dataQueue = CFArrayCreateMutable(CFGetAllocator(s), 0, &kCFTypeArrayCallBacks); + } + if (NULL == s->_addressQueue) { + s->_addressQueue = CFArrayCreateMutable(CFGetAllocator(s), 0, &kCFTypeArrayCallBacks); + } + CFArrayAppendValue(s->_dataQueue, data); + CFRelease(data); + CFArrayAppendValue(s->_addressQueue, address); + CFRelease(address); + if (0 < recvlen + && (s->_f.client & kCFSocketDataCallBack) != 0 && (s->_f.disabled & kCFSocketDataCallBack) == 0 + && __CFSocketIsScheduled(s) + ) { + __CFSpinLock(&__CFActiveSocketsLock); + /* restore socket to fds */ + __CFSocketSetFDForRead(s); + __CFSpinUnlock(&__CFActiveSocketsLock); + } + } else if (__CFSocketReadCallBackType(s) == kCFSocketAcceptCallBack) { + uint8_t name[MAX_SOCKADDR_LEN]; + int namelen = sizeof(name); + sock = (CFSocketNativeHandle)accept(s->_socket, (struct sockaddr *)name, (socklen_t *)&namelen); + if (INVALID_SOCKET == sock) { + //??? should return error + return; + } + if (NULL != name && 0 < namelen) { + address = CFDataCreate(CFGetAllocator(s), name, namelen); + } else { + address = (CFDataRef)CFRetain(zeroLengthData); + } + __CFSocketLock(s); + if (!__CFSocketIsValid(s)) { + closesocket(sock); + CFRelease(address); + __CFSocketUnlock(s); + return; + } + __CFSocketSetReadSignalled(s); + if (NULL == s->_dataQueue) { + s->_dataQueue = CFArrayCreateMutable(CFGetAllocator(s), 0, NULL); + } + if (NULL == s->_addressQueue) { + s->_addressQueue = CFArrayCreateMutable(CFGetAllocator(s), 0, &kCFTypeArrayCallBacks); + } + CFArrayAppendValue(s->_dataQueue, (void *)(uintptr_t)sock); + CFArrayAppendValue(s->_addressQueue, address); + CFRelease(address); + if ((s->_f.client & kCFSocketAcceptCallBack) != 0 && (s->_f.disabled & kCFSocketAcceptCallBack) == 0 + && __CFSocketIsScheduled(s) + ) { + __CFSpinLock(&__CFActiveSocketsLock); + /* restore socket to fds */ + __CFSocketSetFDForRead(s); + __CFSpinUnlock(&__CFActiveSocketsLock); + } + } else { + __CFSocketLock(s); + if (!__CFSocketIsValid(s) || (s->_f.disabled & kCFSocketReadCallBack) != 0) { + __CFSocketUnlock(s); + return; + } + + if (causedByTimeout) { +#if defined(LOG_CFSOCKET) + fprintf(stdout, "TIMEOUT RECEIVED - WILL SIGNAL IMMEDIATELY TO FLUSH (%d buffered)\n", s->_bytesToBufferPos); +#endif + /* we've got a timeout, but no bytes read. Ignore the timeout. */ + if (s->_bytesToBufferPos == 0) { +#if defined(LOG_CFSOCKET) + fprintf(stdout, "TIMEOUT - but no bytes, restoring to active set\n"); + fflush(stdout); +#endif + + __CFSpinLock(&__CFActiveSocketsLock); + /* restore socket to fds */ + __CFSocketSetFDForRead(s); + __CFSpinUnlock(&__CFActiveSocketsLock); + __CFSocketUnlock(s); + return; + } + } else if (s->_bytesToBuffer != 0 && ! s->_atEOF) { + UInt8* base; + CFIndex ctRead; + CFIndex ctRemaining = s->_bytesToBuffer - s->_bytesToBufferPos; + + /* if our buffer has room, we go ahead and buffer */ + if (ctRemaining > 0) { + base = CFDataGetMutableBytePtr(s->_readBuffer); + + do { + ctRead = read(CFSocketGetNative(s), &base[s->_bytesToBufferPos], ctRemaining); + } while (ctRead == -1 && errno == EAGAIN); + + switch (ctRead) { + case -1: + s->_bufferedReadError = errno; + s->_atEOF = true; +#if defined(LOG_CFSOCKET) + fprintf(stderr, "BUFFERED READ GOT ERROR %d\n", errno); +#endif + break; + + case 0: + #if defined(LOG_CFSOCKET) + fprintf(stdout, "DONE READING (EOF) - GOING TO SIGNAL\n"); + #endif + s->_atEOF = true; + break; + + default: + s->_bytesToBufferPos += ctRead; + if (s->_bytesToBuffer != s->_bytesToBufferPos) { + #if defined(LOG_CFSOCKET) + fprintf(stdout, "READ %d - need %d MORE - GOING BACK FOR MORE\n", ctRead, s->_bytesToBuffer - s->_bytesToBufferPos); + #endif + __CFSpinLock(&__CFActiveSocketsLock); + /* restore socket to fds */ + __CFSocketSetFDForRead(s); + __CFSpinUnlock(&__CFActiveSocketsLock); + __CFSocketUnlock(s); + return; + } else { + #if defined(LOG_CFSOCKET) + fprintf(stdout, "DONE READING (read %d bytes) - GOING TO SIGNAL\n", ctRead); + #endif + } + } + } + } + + __CFSocketSetReadSignalled(s); + } +#if defined(LOG_CFSOCKET) + fprintf(stdout, "read signaling source for socket %d\n", s->_socket); +#endif + CFRunLoopSourceSignal(s->_source0); + CFRunLoopRef rl = __CFSocketCopyRunLoopToWakeUp(s); + __CFSocketUnlock(s); + if (NULL != rl) { + CFRunLoopWakeUp(rl); + CFRelease(rl); + } +} + +static struct timeval* intervalToTimeval(CFTimeInterval timeout, struct timeval* tv) +{ + if (timeout == 0.0) + timerclear(tv); + else { + tv->tv_sec = (0 >= timeout || INT_MAX <= timeout) ? INT_MAX : (int)(float)floor(timeout); + tv->tv_usec = (int)((timeout - floor(timeout)) * 1.0E6); + } + return tv; +} + +/* note that this returns a pointer to the min value, which won't have changed during + the dictionary apply, since we've got the active sockets lock held */ +static void _calcMinTimeout_locked(const void* val, void* ctxt) +{ + CFSocketRef s = (CFSocketRef) val; + struct timeval** minTime = (struct timeval**) ctxt; + if (timerisset(&s->_readBufferTimeout) && (*minTime == NULL || timercmp(&s->_readBufferTimeout, *minTime, <))) + *minTime = &s->_readBufferTimeout; + else if (s->_leftoverBytes) { + /* If there's anyone with leftover bytes, they'll need to be awoken immediately */ + static struct timeval sKickerTime = { 0, 0 }; + *minTime = &sKickerTime; + } +} + +void __CFSocketSetSocketReadBufferAttrs(CFSocketRef s, CFTimeInterval timeout, CFIndex length) +{ + struct timeval timeoutVal; + + intervalToTimeval(timeout, &timeoutVal); + + /* lock ordering is socket lock, activesocketslock */ + /* activesocketslock protects our timeout calculation */ + __CFSocketLock(s); + __CFSpinLock(&__CFActiveSocketsLock); + +#if defined(LOG_CFSOCKET) + s->didLogSomething = false; +#endif + + if (s->_bytesToBuffer != length) { + CFIndex ctBuffer = s->_bytesToBufferPos - s->_bytesToBufferReadPos; + + if (ctBuffer) { + /* As originally envisaged, you were supposed to be sure to drain the buffer before + * issuing another request on the socket. In practice, there seem to be times when we want to re-use + * the stream (or perhaps, are on our way to closing it out) and this policy doesn't work so well. + * So, if someone changes the buffer size while we have bytes already buffered, we put them + * aside and use them to satisfy any subsequent reads. + */ +#if defined(LOG_CFSOCKET) + __socketLog("%s(%d): WARNING: shouldn't set read buffer length while data (%d bytes) is still in the read buffer (leftover total %d)", __FUNCTION__, __LINE__, ctBuffer, s->_leftoverBytes? CFDataGetLength(s->_leftoverBytes) : 0); +#endif + + if (s->_leftoverBytes == NULL) + s->_leftoverBytes = CFDataCreateMutable(CFGetAllocator(s), 0); + + /* append the current buffered bytes over. We'll keep draining _leftoverBytes while we have them... */ + CFDataAppendBytes(s->_leftoverBytes, CFDataGetBytePtr(s->_readBuffer) + s->_bytesToBufferReadPos, ctBuffer); + CFRelease(s->_readBuffer); + s->_readBuffer = NULL; + + s->_bytesToBuffer = 0; + s->_bytesToBufferPos = 0; + s->_bytesToBufferReadPos = 0; + } + if (length == 0) { + s->_bytesToBuffer = 0; + s->_bytesToBufferPos = 0; + s->_bytesToBufferReadPos = 0; + if (s->_readBuffer) { + CFRelease(s->_readBuffer); + s->_readBuffer = NULL; + } + // Zero length buffer, smash the timeout + static struct timeval sReset = { 0, 0 }; + timeoutVal = sReset; + } else { + /* if the buffer shrank, we can re-use the old one */ + if (length > s->_bytesToBuffer) { + if (s->_readBuffer) { + CFRelease(s->_readBuffer); + s->_readBuffer = NULL; + } + } + + s->_bytesToBuffer = length; + s->_bytesToBufferPos = 0; + s->_bytesToBufferReadPos = 0; + if (s->_readBuffer == NULL) { + s->_readBuffer = CFDataCreateMutable(kCFAllocatorSystemDefault, length); + CFDataSetLength(s->_readBuffer, length); + } + } + } + + if (timercmp(&s->_readBufferTimeout, &timeoutVal, !=)) { + s->_readBufferTimeout = timeoutVal; + __CFReadSocketsTimeoutInvalid = true; + } + + __CFSpinUnlock(&__CFActiveSocketsLock); + __CFSocketUnlock(s); +} + +void __CFSocketSetReadBufferTimeout(CFSocketRef s, CFTimeInterval timeout) +{ +#if defined(LOG_CFSOCKET) + __socketLog("### (%s DEPRECATED) SET READ BUFFER TIMEOUT for %p (bufferSize %d, timeout %d.%d)\n", __FUNCTION__, s, s->_bytesToBuffer, s->_readBufferTimeout.tv_sec, s->_readBufferTimeout.tv_usec); +#endif + + __CFSocketSetSocketReadBufferAttrs(s, timeout, s->_bytesToBuffer); +} + +void __CFSocketSetReadBufferLength(CFSocketRef s, CFIndex length) +{ +#if defined(LOG_CFSOCKET) + __socketLog("### (%s DEPRECATED) SET READ BUFFER LENGTH for %p (bufferSize %d, timeout %d.%d)\n", __FUNCTION__, s, s->_bytesToBuffer, s->_readBufferTimeout.tv_sec, s->_readBufferTimeout.tv_usec); +#endif + + CFTimeInterval timeout; + + if (! timerisset(&s->_readBufferTimeout)) + timeout = 0; + else + timeout = 0.1; + + __CFSocketSetSocketReadBufferAttrs(s, timeout, length); +} + +CFIndex __CFSocketRead(CFSocketRef s, UInt8* buffer, CFIndex length, int* error) +{ +#if defined(LOG_CFSOCKET) + fprintf(stdout, "READING BYTES FOR SOCKET %d (%d buffered, out of %d desired, eof = %d, err = %d)\n", s->_socket, s->_bytesToBufferPos, s->_bytesToBuffer, s->_atEOF, s->_bufferedReadError); +#endif + + CFIndex result = -1; + + __CFSocketLock(s); + + *error = 0; + + /* Any leftover buffered bytes? */ + if (s->_leftoverBytes) { + CFIndex ctBuffer = CFDataGetLength(s->_leftoverBytes); +#if defined(DEBUG) + fprintf(stderr, "%s(%d): WARNING: Draining %lu leftover bytes first\n\n", __FUNCTION__, __LINE__, ctBuffer); +#endif + if (ctBuffer > length) + ctBuffer = length; + memcpy(buffer, CFDataGetBytePtr(s->_leftoverBytes), ctBuffer); + if (ctBuffer < CFDataGetLength(s->_leftoverBytes)) + CFDataReplaceBytes(s->_leftoverBytes, CFRangeMake(0, ctBuffer), NULL, 0); + else { + CFRelease(s->_leftoverBytes); + s->_leftoverBytes = NULL; + } + result = ctBuffer; + goto unlock; + } + + /* return whatever we've buffered */ + if (s->_bytesToBuffer != 0) { + CFIndex ctBuffer = s->_bytesToBufferPos - s->_bytesToBufferReadPos; + if (ctBuffer > 0) { + /* drain our buffer first */ + if (ctBuffer > length) + ctBuffer = length; + memcpy(buffer, CFDataGetBytePtr(s->_readBuffer) + s->_bytesToBufferReadPos, ctBuffer); + s->_bytesToBufferReadPos += ctBuffer; + if (s->_bytesToBufferReadPos == s->_bytesToBufferPos) { +#if defined(LOG_CFSOCKET) + fprintf(stdout, "DRAINED BUFFER - SHOULD START BUFFERING AGAIN!\n"); +#endif + s->_bytesToBufferPos = 0; + s->_bytesToBufferReadPos = 0; + } + +#if defined(LOG_CFSOCKET) + fprintf(stdout, "SLURPED %d BYTES FROM BUFFER %d LEFT TO READ!\n", ctBuffer, length); +#endif + + result = ctBuffer; + goto unlock; + } + } + /* nothing buffered, or no buffer selected */ + + /* Did we get an error on a previous read (or buffered read)? */ + if (s->_bufferedReadError != 0) { +#if defined(LOG_CFSOCKET) + fprintf(stdout, "RETURNING ERROR %d\n", s->_bufferedReadError); +#endif + *error = s->_bufferedReadError; + result = -1; + goto unlock; + } + + /* nothing buffered, if we've hit eof, don't bother reading any more */ + if (s->_atEOF) { +#if defined(LOG_CFSOCKET) + fprintf(stdout, "RETURNING EOF\n"); +#endif + result = 0; + goto unlock; + } + + /* normal read */ + result = read(CFSocketGetNative(s), buffer, length); +#if defined(LOG_CFSOCKET) + fprintf(stdout, "READ %d bytes", result); +#endif + + if (result == 0) { + /* note that we hit EOF */ + s->_atEOF = true; + } else if (result < 0) { + *error = errno; + + /* if it wasn't EAGAIN, record it (although we shouldn't get called again) */ + if (*error != EAGAIN) { + s->_bufferedReadError = *error; + } + } + +unlock: + __CFSocketUnlock(s); + + return result; +} + +Boolean __CFSocketGetBytesAvailable(CFSocketRef s, CFIndex* ctBytesAvailable) +{ + CFIndex ctBuffer = s->_bytesToBufferPos - s->_bytesToBufferReadPos; + if (ctBuffer != 0) { + *ctBytesAvailable = ctBuffer; + return true; + } else { + int result; +#if DEPLOYMENT_TARGET_MACOSX + int bytesAvailable, intLen = sizeof(bytesAvailable); + result = getsockopt(CFSocketGetNative(s), SOL_SOCKET, SO_NREAD, &bytesAvailable, (socklen_t *)&intLen); +#elif DEPLOYMENT_TARGET_WINDOWS + unsigned long bytesAvailable; + result = ioctlsocket(CFSocketGetNative(s), FIONREAD, &bytesAvailable); +#else + CFIndex bytesAvailable = 0; + result = -1; +#endif + if (result < 0) + return false; + *ctBytesAvailable = (CFIndex) bytesAvailable; + return true; + } +} + +#if defined(LOG_CFSOCKET) +static void __CFSocketWriteSocketList(CFArrayRef sockets, CFDataRef fdSet, Boolean onlyIfSet) { + fd_set *tempfds = (fd_set *)CFDataGetBytePtr(fdSet); + SInt32 idx, cnt; + for (idx = 0, cnt = CFArrayGetCount(sockets); idx < cnt; idx++) { + CFSocketRef s = (CFSocketRef)CFArrayGetValueAtIndex(sockets, idx); + if (FD_ISSET(s->_socket, tempfds)) { + fprintf(stdout, "%d ", s->_socket); + } else if (!onlyIfSet) { + fprintf(stdout, "(%d) ", s->_socket); + } + } +} +#endif + +#ifdef __GNUC__ +__attribute__ ((noreturn)) // mostly interesting for shutting up a warning +#endif /* __GNUC__ */ +static void __CFSocketManager(void * arg) +{ +#if defined(__OBJC__) + if (objc_collecting_enabled()) auto_zone_register_thread(auto_zone()); +#endif + SInt32 nrfds, maxnrfds, fdentries = 1; + SInt32 rfds, wfds; +#if !DEPLOYMENT_TARGET_WINDOWS + fd_set *exceptfds = NULL; + fd_set *writefds = (fd_set *)CFAllocatorAllocate(kCFAllocatorSystemDefault, fdentries * sizeof(fd_mask), 0); + fd_set *readfds = (fd_set *)CFAllocatorAllocate(kCFAllocatorSystemDefault, fdentries * sizeof(fd_mask), 0); +#else + fd_set *exceptfds = (fd_set *)CFAllocatorAllocate(kCFAllocatorSystemDefault, fdentries * sizeof(SOCKET) + sizeof(u_int), 0); + fd_set *writefds = (fd_set *)CFAllocatorAllocate(kCFAllocatorSystemDefault, fdentries * sizeof(SOCKET) + sizeof(u_int), 0); + fd_set *readfds = (fd_set *)CFAllocatorAllocate(kCFAllocatorSystemDefault, fdentries * sizeof(SOCKET) + sizeof(u_int), 0); +#endif + fd_set *tempfds; + SInt32 idx, cnt; + uint8_t buffer[256]; + CFMutableArrayRef selectedWriteSockets = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); + CFMutableArrayRef selectedReadSockets = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); + CFIndex selectedWriteSocketsIndex = 0, selectedReadSocketsIndex = 0; + + struct timeval tv; + struct timeval* pTimeout = NULL; + struct timeval timeBeforeSelect; + + for (;;) { + __CFSpinLock(&__CFActiveSocketsLock); + __CFSocketManagerIteration++; +#if defined(LOG_CFSOCKET) + fprintf(stdout, "socket manager iteration %lu looking at read sockets ", __CFSocketManagerIteration); + __CFSocketWriteSocketList(__CFReadSockets, __CFReadSocketsFds, FALSE); + if (0 < CFArrayGetCount(__CFWriteSockets)) { + fprintf(stdout, " and write sockets "); + __CFSocketWriteSocketList(__CFWriteSockets, __CFWriteSocketsFds, FALSE); +#if DEPLOYMENT_TARGET_WINDOWS + fprintf(stdout, " and except sockets "); + __CFSocketWriteSocketList(__CFWriteSockets, __CFExceptSocketsFds, TRUE); +#endif + } + fprintf(stdout, "\n"); +#endif + rfds = __CFSocketFdGetSize(__CFReadSocketsFds); + wfds = __CFSocketFdGetSize(__CFWriteSocketsFds); + maxnrfds = __CFMax(rfds, wfds); +#if !DEPLOYMENT_TARGET_WINDOWS + if (maxnrfds > fdentries * (int)NFDBITS) { + fdentries = (maxnrfds + NFDBITS - 1) / NFDBITS; + writefds = (fd_set *)CFAllocatorReallocate(kCFAllocatorSystemDefault, writefds, fdentries * sizeof(fd_mask), 0); + readfds = (fd_set *)CFAllocatorReallocate(kCFAllocatorSystemDefault, readfds, fdentries * sizeof(fd_mask), 0); + } + memset(writefds, 0, fdentries * sizeof(fd_mask)); + memset(readfds, 0, fdentries * sizeof(fd_mask)); +#else + if (maxnrfds > fdentries) { + fdentries = maxnrfds; + exceptfds = (fd_set *)CFAllocatorReallocate(kCFAllocatorSystemDefault, exceptfds, fdentries * sizeof(SOCKET) + sizeof(u_int), 0); + writefds = (fd_set *)CFAllocatorReallocate(kCFAllocatorSystemDefault, writefds, fdentries * sizeof(SOCKET) + sizeof(u_int), 0); + readfds = (fd_set *)CFAllocatorReallocate(kCFAllocatorSystemDefault, readfds, fdentries * sizeof(SOCKET) + sizeof(u_int), 0); + } + memset(exceptfds, 0, fdentries * sizeof(SOCKET) + sizeof(u_int)); + memset(writefds, 0, fdentries * sizeof(SOCKET) + sizeof(u_int)); + memset(readfds, 0, fdentries * sizeof(SOCKET) + sizeof(u_int)); + CFDataGetBytes(__CFExceptSocketsFds, CFRangeMake(0, __CFSocketFdGetSize(__CFExceptSocketsFds) * sizeof(SOCKET) + sizeof(u_int)), (UInt8 *)exceptfds); +#endif + CFDataGetBytes(__CFWriteSocketsFds, CFRangeMake(0, CFDataGetLength(__CFWriteSocketsFds)), (UInt8 *)writefds); + CFDataGetBytes(__CFReadSocketsFds, CFRangeMake(0, CFDataGetLength(__CFReadSocketsFds)), (UInt8 *)readfds); + + if (__CFReadSocketsTimeoutInvalid) { + struct timeval* minTimeout = NULL; + __CFReadSocketsTimeoutInvalid = false; +#if defined(LOG_CFSOCKET) + fprintf(stdout, "Figuring out which sockets have timeouts...\n"); +#endif + CFArrayApplyFunction(__CFReadSockets, CFRangeMake(0, CFArrayGetCount(__CFReadSockets)), _calcMinTimeout_locked, (void*) &minTimeout); + + if (minTimeout == NULL) { +#if defined(LOG_CFSOCKET) + fprintf(stdout, "No one wants a timeout!\n"); +#endif + pTimeout = NULL; + } else { +#if defined(LOG_CFSOCKET) + fprintf(stdout, "timeout will be %d, %d!\n", minTimeout->tv_sec, minTimeout->tv_usec); +#endif + tv = *minTimeout; + pTimeout = &tv; + } + } + + if (pTimeout) { +#if defined(LOG_CFSOCKET) + fprintf(stdout, "select will have a %d, %d timeout\n", pTimeout->tv_sec, pTimeout->tv_usec); +#endif + gettimeofday(&timeBeforeSelect, NULL); + } + + __CFSpinUnlock(&__CFActiveSocketsLock); + + nrfds = select(maxnrfds, readfds, writefds, exceptfds, pTimeout); + +#if defined(LOG_CFSOCKET) + fprintf(stdout, "socket manager woke from select, ret=%ld\n", nrfds); +#endif + + /* + * select returned a timeout + */ + if (0 == nrfds) { + struct timeval timeAfterSelect; + struct timeval deltaTime; + gettimeofday(&timeAfterSelect, NULL); + /* timeBeforeSelect becomes the delta */ + timersub(&timeAfterSelect, &timeBeforeSelect, &deltaTime); + +#if defined(LOG_CFSOCKET) + fprintf(stdout, "Socket manager received timeout - kicking off expired reads (expired delta %d, %d)\n", deltaTime.tv_sec, deltaTime.tv_usec); +#endif + + __CFSpinLock(&__CFActiveSocketsLock); + + tempfds = NULL; + cnt = CFArrayGetCount(__CFReadSockets); + for (idx = 0; idx < cnt; idx++) { + CFSocketRef s = (CFSocketRef)CFArrayGetValueAtIndex(__CFReadSockets, idx); + if (timerisset(&s->_readBufferTimeout) || s->_leftoverBytes) { + CFSocketNativeHandle sock = s->_socket; + // We might have an new element in __CFReadSockets that we weren't listening to, + // in which case we must be sure not to test a bit in the fdset that is + // outside our mask size. + Boolean sockInBounds = (0 <= sock && sock < maxnrfds); + /* if this sockets timeout is less than or equal elapsed time, then signal it */ + if (INVALID_SOCKET != sock && sockInBounds) { +#if defined(LOG_CFSOCKET) + fprintf(stdout, "Expiring socket %d (delta %d, %d)\n", sock, s->_readBufferTimeout.tv_sec, s->_readBufferTimeout.tv_usec); +#endif + CFArraySetValueAtIndex(selectedReadSockets, selectedReadSocketsIndex, s); + selectedReadSocketsIndex++; + /* socket is removed from fds here, will be restored in read handling or in perform function */ + if (!tempfds) tempfds = (fd_set *)CFDataGetMutableBytePtr(__CFReadSocketsFds); + FD_CLR(sock, tempfds); + } + } + } + + __CFSpinUnlock(&__CFActiveSocketsLock); + + /* and below, we dispatch through the normal read dispatch mechanism */ + } + + if (0 > nrfds) { + SInt32 selectError = __CFSocketLastError(); +#if defined(LOG_CFSOCKET) + fprintf(stdout, "socket manager received error %ld from select\n", selectError); +#endif + if (EBADF == selectError) { + CFMutableArrayRef invalidSockets = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); + __CFSpinLock(&__CFActiveSocketsLock); + cnt = CFArrayGetCount(__CFWriteSockets); + for (idx = 0; idx < cnt; idx++) { + CFSocketRef s = (CFSocketRef)CFArrayGetValueAtIndex(__CFWriteSockets, idx); + if (!__CFNativeSocketIsValid(s->_socket)) { +#if defined(LOG_CFSOCKET) + fprintf(stdout, "socket manager found write socket %d invalid\n", s->_socket); +#endif + CFArrayAppendValue(invalidSockets, s); + } + } + cnt = CFArrayGetCount(__CFReadSockets); + for (idx = 0; idx < cnt; idx++) { + CFSocketRef s = (CFSocketRef)CFArrayGetValueAtIndex(__CFReadSockets, idx); + if (!__CFNativeSocketIsValid(s->_socket)) { +#if defined(LOG_CFSOCKET) + fprintf(stdout, "socket manager found read socket %d invalid\n", s->_socket); +#endif + CFArrayAppendValue(invalidSockets, s); + } + } + __CFSpinUnlock(&__CFActiveSocketsLock); + + cnt = CFArrayGetCount(invalidSockets); + for (idx = 0; idx < cnt; idx++) { + CFSocketInvalidate(((CFSocketRef)CFArrayGetValueAtIndex(invalidSockets, idx))); + } + CFRelease(invalidSockets); + } + continue; + } + if (FD_ISSET(__CFWakeupSocketPair[1], readfds)) { + recv(__CFWakeupSocketPair[1], (SOCK_DATA)buffer, sizeof(buffer), 0); +#if defined(LOG_CFSOCKET) + fprintf(stdout, "socket manager received %c on wakeup socket\n", buffer[0]); +#endif + } + __CFSpinLock(&__CFActiveSocketsLock); + tempfds = NULL; + cnt = CFArrayGetCount(__CFWriteSockets); + for (idx = 0; idx < cnt; idx++) { + CFSocketRef s = (CFSocketRef)CFArrayGetValueAtIndex(__CFWriteSockets, idx); + CFSocketNativeHandle sock = s->_socket; + // We might have an new element in __CFWriteSockets that we weren't listening to, + // in which case we must be sure not to test a bit in the fdset that is + // outside our mask size. +#if !DEPLOYMENT_TARGET_WINDOWS + Boolean sockInBounds = (0 <= sock && sock < maxnrfds); +#else + Boolean sockInBounds = true; +#endif + if (INVALID_SOCKET != sock && sockInBounds) { + if (FD_ISSET(sock, writefds)) { + CFArraySetValueAtIndex(selectedWriteSockets, selectedWriteSocketsIndex, s); + selectedWriteSocketsIndex++; + /* socket is removed from fds here, restored by CFSocketReschedule */ + if (!tempfds) tempfds = (fd_set *)CFDataGetMutableBytePtr(__CFWriteSocketsFds); + FD_CLR(sock, tempfds); +#if DEPLOYMENT_TARGET_WINDOWS + fd_set *exfds = (fd_set *)CFDataGetMutableBytePtr(__CFExceptSocketsFds); + FD_CLR(sock, exfds); +#endif + + } +#if DEPLOYMENT_TARGET_WINDOWS + else if (FD_ISSET(sock, exceptfds)) { + // On Win32 connect errors come in on exceptFDs. We treat these as if + // they had come on writeFDs, since the rest of our Unix-based code + // expects that. + CFArrayAppendValue(selectedWriteSockets, s); + fd_set *exfds = (fd_set *)CFDataGetMutableBytePtr(__CFExceptSocketsFds); + FD_CLR(sock, exfds); + } +#endif + } + } + tempfds = NULL; + cnt = CFArrayGetCount(__CFReadSockets); + for (idx = 0; idx < cnt; idx++) { + CFSocketRef s = (CFSocketRef)CFArrayGetValueAtIndex(__CFReadSockets, idx); + CFSocketNativeHandle sock = s->_socket; +#if !DEPLOYMENT_TARGET_WINDOWS + // We might have an new element in __CFReadSockets that we weren't listening to, + // in which case we must be sure not to test a bit in the fdset that is + // outside our mask size. + Boolean sockInBounds = (0 <= sock && sock < maxnrfds); +#else + // fdset's are arrays, so we don't have that issue above + Boolean sockInBounds = true; +#endif + if (INVALID_SOCKET != sock && sockInBounds && FD_ISSET(sock, readfds)) { + CFArraySetValueAtIndex(selectedReadSockets, selectedReadSocketsIndex, s); + selectedReadSocketsIndex++; + /* socket is removed from fds here, will be restored in read handling or in perform function */ + if (!tempfds) tempfds = (fd_set *)CFDataGetMutableBytePtr(__CFReadSocketsFds); + FD_CLR(sock, tempfds); + } + } + __CFSpinUnlock(&__CFActiveSocketsLock); + + for (idx = 0; idx < selectedWriteSocketsIndex; idx++) { + CFSocketRef s = (CFSocketRef)CFArrayGetValueAtIndex(selectedWriteSockets, idx); + if (kCFNull == (CFNullRef)s) continue; +#if defined(LOG_CFSOCKET) + fprintf(stdout, "socket manager signaling socket %d for write\n", s->_socket); +#endif + __CFSocketHandleWrite(s, FALSE); + CFArraySetValueAtIndex(selectedWriteSockets, idx, kCFNull); + } + selectedWriteSocketsIndex = 0; + + for (idx = 0; idx < selectedReadSocketsIndex; idx++) { + CFSocketRef s = (CFSocketRef)CFArrayGetValueAtIndex(selectedReadSockets, idx); + if (kCFNull == (CFNullRef)s) continue; +#if defined(LOG_CFSOCKET) + fprintf(stdout, "socket manager signaling socket %d for read\n", s->_socket); +#endif + __CFSocketHandleRead(s, nrfds == 0); + CFArraySetValueAtIndex(selectedReadSockets, idx, kCFNull); + } + selectedReadSocketsIndex = 0; + } +#if defined(__OBJC__) + if (objc_collecting_enabled()) auto_zone_unregister_thread(auto_zone()); +#endif +} + +static CFStringRef __CFSocketCopyDescription(CFTypeRef cf) { + CFSocketRef s = (CFSocketRef)cf; + CFMutableStringRef result; + CFStringRef contextDesc = NULL; + void *contextInfo = NULL; + CFStringRef (*contextCopyDescription)(const void *info) = NULL; + result = CFStringCreateMutable(CFGetAllocator(s), 0); + __CFSocketLock(s); + void *addr = (void*)s->_callout; +#if DEPLOYMENT_TARGET_WINDOWS + // FIXME: Get name using win32 analog of dladdr? + const char* name = "???"; +#else + Dl_info info; + const char *name = (dladdr(addr, &info) && info.dli_saddr == addr && info.dli_sname) ? info.dli_sname : "???"; +#endif + CFStringAppendFormat(result, NULL, CFSTR("<CFSocket %p [%p]>{valid = %s, type = %d, socket = %d, socket set count = %ld,\n callback types = 0x%x, callout = %s (%p), source = %p,\n run loops = %@,\n context = "), cf, CFGetAllocator(s), (__CFSocketIsValid(s) ? "Yes" : "No"), s->_socketType, s->_socket, s->_socketSetCount, __CFSocketCallBackTypes(s), name, addr, s->_source0, s->_runLoops); + contextInfo = s->_context.info; + contextCopyDescription = s->_context.copyDescription; + __CFSocketUnlock(s); + if (NULL != contextInfo && NULL != contextCopyDescription) { + contextDesc = (CFStringRef)contextCopyDescription(contextInfo); + } + if (NULL == contextDesc) { + contextDesc = CFStringCreateWithFormat(CFGetAllocator(s), NULL, CFSTR("<CFSocket context %p>"), contextInfo); + } + CFStringAppend(result, contextDesc); + CFStringAppend(result, CFSTR("}")); + CFRelease(contextDesc); + return result; +} + +static void __CFSocketDeallocate(CFTypeRef cf) { + /* Since CFSockets are cached, we can only get here sometime after being invalidated */ + CFSocketRef s = (CFSocketRef)cf; + if (NULL != s->_address) { + CFRelease(s->_address); + s->_address = NULL; + } + if (NULL != s->_readBuffer) { + CFRelease(s->_readBuffer); + s->_readBuffer = NULL; + } + if (NULL != s->_leftoverBytes) { + CFRelease(s->_leftoverBytes); + s->_leftoverBytes = NULL; + } + timerclear(&s->_readBufferTimeout); + s->_bytesToBuffer = 0; + s->_bytesToBufferPos = 0; + s->_bytesToBufferReadPos = 0; + s->_atEOF = true; + s->_bufferedReadError = 0; +} + +static const CFRuntimeClass __CFSocketClass = { + 0, + "CFSocket", + NULL, // init + NULL, // copy + __CFSocketDeallocate, + NULL, // equal + NULL, // hash + NULL, // + __CFSocketCopyDescription +}; + +__private_extern__ void __CFSocketInitialize(void) { + __kCFSocketTypeID = _CFRuntimeRegisterClass(&__CFSocketClass); +} + +CFTypeID CFSocketGetTypeID(void) { + return __kCFSocketTypeID; +} +static CFSocketRef _CFSocketCreateWithNative(CFAllocatorRef allocator, CFSocketNativeHandle sock, CFOptionFlags callBackTypes, CFSocketCallBack callout, const CFSocketContext *context, Boolean useExistingInstance) { + CHECK_FOR_FORK(); + CFSocketRef memory; + int typeSize = sizeof(memory->_socketType); + __CFSpinLock(&__CFActiveSocketsLock); + if (NULL == __CFReadSockets) __CFSocketInitializeSockets(); + __CFSpinUnlock(&__CFActiveSocketsLock); + __CFSpinLock(&__CFAllSocketsLock); + if (NULL == __CFAllSockets) { + __CFAllSockets = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks); + } + if (INVALID_SOCKET != sock && CFDictionaryGetValueIfPresent(__CFAllSockets, (void *)(uintptr_t)sock, (const void **)&memory)) { + if (useExistingInstance) { + __CFSpinUnlock(&__CFAllSocketsLock); + CFRetain(memory); + return memory; + } else { +#if defined(LOG_CFSOCKET) + fprintf(stdout, "useExistingInstance is FALSE, removing existing instance %d from __CFAllSockets\n", (int)memory); +#endif + __CFSpinUnlock(&__CFAllSocketsLock); + CFSocketInvalidate(memory); + __CFSpinLock(&__CFAllSocketsLock); + } + } + memory = (CFSocketRef)_CFRuntimeCreateInstance(allocator, __kCFSocketTypeID, sizeof(struct __CFSocket) - sizeof(CFRuntimeBase), NULL); + if (NULL == memory) { + __CFSpinUnlock(&__CFAllSocketsLock); + return NULL; + } + __CFSocketSetCallBackTypes(memory, callBackTypes); + if (INVALID_SOCKET != sock) __CFSocketSetValid(memory); + __CFSocketUnsetWriteSignalled(memory); + __CFSocketUnsetReadSignalled(memory); + memory->_f.client = ((callBackTypes & (~kCFSocketConnectCallBack)) & (~kCFSocketWriteCallBack)) | kCFSocketCloseOnInvalidate; + memory->_f.disabled = 0; + memory->_f.connected = FALSE; + memory->_f.writableHint = FALSE; + memory->_f.closeSignaled = FALSE; + CF_SPINLOCK_INIT_FOR_STRUCTS(memory->_lock); + CF_SPINLOCK_INIT_FOR_STRUCTS(memory->_writeLock); + memory->_socket = sock; + if (INVALID_SOCKET == sock || 0 != getsockopt(sock, SOL_SOCKET, SO_TYPE, (SOCK_DATA)&(memory->_socketType), (socklen_t *)&typeSize)) memory->_socketType = 0; // cast for WinSock bad API + memory->_errorCode = 0; + memory->_address = NULL; + memory->_peerAddress = NULL; + memory->_socketSetCount = 0; + memory->_source0 = NULL; + if (INVALID_SOCKET != sock) { + memory->_runLoops = CFArrayCreateMutable(allocator, 0, NULL); + } else { + memory->_runLoops = NULL; + } + memory->_callout = callout; + memory->_dataQueue = NULL; + memory->_addressQueue = NULL; + memory->_context.info = 0; + memory->_context.retain = 0; + memory->_context.release = 0; + memory->_context.copyDescription = 0; + timerclear(&memory->_readBufferTimeout); + memory->_readBuffer = NULL; + memory->_bytesToBuffer = 0; + memory->_bytesToBufferPos = 0; + memory->_bytesToBufferReadPos = 0; + memory->_atEOF = false; + memory->_bufferedReadError = 0; + memory->_leftoverBytes = NULL; + + if (INVALID_SOCKET != sock) CFDictionaryAddValue(__CFAllSockets, (void *)(uintptr_t)sock, memory); + __CFSpinUnlock(&__CFAllSocketsLock); + if (NULL != context) { + void *contextInfo = context->retain ? (void *)context->retain(context->info) : context->info; + __CFSocketLock(memory); + memory->_context.retain = context->retain; + memory->_context.release = context->release; + memory->_context.copyDescription = context->copyDescription; + memory->_context.info = contextInfo; + __CFSocketUnlock(memory); + } + return memory; +} + +CFSocketRef CFSocketCreateWithNative(CFAllocatorRef allocator, CFSocketNativeHandle sock, CFOptionFlags callBackTypes, CFSocketCallBack callout, const CFSocketContext *context) { + return _CFSocketCreateWithNative(allocator, sock, callBackTypes, callout, context, TRUE); +} + +void CFSocketInvalidate(CFSocketRef s) { + CHECK_FOR_FORK(); + UInt32 previousSocketManagerIteration; + __CFGenericValidateType(s, __kCFSocketTypeID); +#if defined(LOG_CFSOCKET) + fprintf(stdout, "invalidating socket %d with flags 0x%x disabled 0x%x connected 0x%x\n", s->_socket, s->_f.client, s->_f.disabled, s->_f.connected); +#endif + CFRetain(s); + __CFSpinLock(&__CFAllSocketsLock); + __CFSocketLock(s); + if (__CFSocketIsValid(s)) { + SInt32 idx; + CFRunLoopSourceRef source0; + void *contextInfo = NULL; + void (*contextRelease)(const void *info) = NULL; + __CFSocketUnsetValid(s); + __CFSocketUnsetWriteSignalled(s); + __CFSocketUnsetReadSignalled(s); + __CFSpinLock(&__CFActiveSocketsLock); + idx = CFArrayGetFirstIndexOfValue(__CFWriteSockets, CFRangeMake(0, CFArrayGetCount(__CFWriteSockets)), s); + if (0 <= idx) { + CFArrayRemoveValueAtIndex(__CFWriteSockets, idx); + __CFSocketClearFDForWrite(s); +#if DENT_TARGET_WIN32 + __CFSocketFdClr(s->_socket, __CFExceptSocketsFds); +#endif + } + // No need to clear FD's for V1 sources, since we'll just throw the whole event away + idx = CFArrayGetFirstIndexOfValue(__CFReadSockets, CFRangeMake(0, CFArrayGetCount(__CFReadSockets)), s); + if (0 <= idx) { + CFArrayRemoveValueAtIndex(__CFReadSockets, idx); + __CFSocketClearFDForRead(s); + } + previousSocketManagerIteration = __CFSocketManagerIteration; + __CFSpinUnlock(&__CFActiveSocketsLock); + CFDictionaryRemoveValue(__CFAllSockets, (void *)(uintptr_t)(s->_socket)); + if ((s->_f.client & kCFSocketCloseOnInvalidate) != 0) closesocket(s->_socket); + s->_socket = INVALID_SOCKET; + if (NULL != s->_peerAddress) { + CFRelease(s->_peerAddress); + s->_peerAddress = NULL; + } + if (NULL != s->_dataQueue) { + CFRelease(s->_dataQueue); + s->_dataQueue = NULL; + } + if (NULL != s->_addressQueue) { + CFRelease(s->_addressQueue); + s->_addressQueue = NULL; + } + s->_socketSetCount = 0; + for (idx = CFArrayGetCount(s->_runLoops); idx--;) { + CFRunLoopWakeUp((CFRunLoopRef)CFArrayGetValueAtIndex(s->_runLoops, idx)); + } + CFRelease(s->_runLoops); + s->_runLoops = NULL; + source0 = s->_source0; + s->_source0 = NULL; + contextInfo = s->_context.info; + contextRelease = s->_context.release; + s->_context.info = 0; + s->_context.retain = 0; + s->_context.release = 0; + s->_context.copyDescription = 0; + __CFSocketUnlock(s); + if (NULL != contextRelease) { + contextRelease(contextInfo); + } + if (NULL != source0) { + CFRunLoopSourceInvalidate(source0); + CFRelease(source0); + } + } else { + __CFSocketUnlock(s); + } + __CFSpinUnlock(&__CFAllSocketsLock); + CFRelease(s); +} + +Boolean CFSocketIsValid(CFSocketRef s) { + CHECK_FOR_FORK(); + __CFGenericValidateType(s, __kCFSocketTypeID); + return __CFSocketIsValid(s); +} + +CFSocketNativeHandle CFSocketGetNative(CFSocketRef s) { + CHECK_FOR_FORK(); + __CFGenericValidateType(s, __kCFSocketTypeID); + return s->_socket; +} + +CFDataRef CFSocketCopyAddress(CFSocketRef s) { + CHECK_FOR_FORK(); + CFDataRef result = NULL; + __CFGenericValidateType(s, __kCFSocketTypeID); + __CFSocketLock(s); + __CFSocketEstablishAddress(s); + if (NULL != s->_address) { + result = (CFDataRef)CFRetain(s->_address); + } + __CFSocketUnlock(s); + return result; +} + +CFDataRef CFSocketCopyPeerAddress(CFSocketRef s) { + CHECK_FOR_FORK(); + CFDataRef result = NULL; + __CFGenericValidateType(s, __kCFSocketTypeID); + __CFSocketLock(s); + __CFSocketEstablishPeerAddress(s); + if (NULL != s->_peerAddress) { + result = (CFDataRef)CFRetain(s->_peerAddress); + } + __CFSocketUnlock(s); + return result; +} + +void CFSocketGetContext(CFSocketRef s, CFSocketContext *context) { + CHECK_FOR_FORK(); + __CFGenericValidateType(s, __kCFSocketTypeID); + CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__); + *context = s->_context; +} + +CFOptionFlags CFSocketGetSocketFlags(CFSocketRef s) { + CHECK_FOR_FORK(); + __CFGenericValidateType(s, __kCFSocketTypeID); + return s->_f.client; +} + +void CFSocketSetSocketFlags(CFSocketRef s, CFOptionFlags flags) { + CHECK_FOR_FORK(); + __CFGenericValidateType(s, __kCFSocketTypeID); + __CFSocketLock(s); +#if defined(LOG_CFSOCKET) + fprintf(stdout, "setting flags for socket %d, from 0x%x to 0x%lx\n", s->_socket, s->_f.client, flags); +#endif + s->_f.client = flags; + __CFSocketUnlock(s); +} + +void CFSocketDisableCallBacks(CFSocketRef s, CFOptionFlags callBackTypes) { + CHECK_FOR_FORK(); + Boolean wakeup = false; + uint8_t readCallBackType; + __CFGenericValidateType(s, __kCFSocketTypeID); + __CFSocketLock(s); + if (__CFSocketIsValid(s) && __CFSocketIsScheduled(s)) { + callBackTypes &= __CFSocketCallBackTypes(s); + readCallBackType = __CFSocketReadCallBackType(s); + s->_f.disabled |= callBackTypes; +#if defined(LOG_CFSOCKET) + fprintf(stdout, "unscheduling socket %d with flags 0x%x disabled 0x%x connected 0x%x for types 0x%lx\n", s->_socket, s->_f.client, s->_f.disabled, s->_f.connected, callBackTypes); +#endif + __CFSpinLock(&__CFActiveSocketsLock); + if ((readCallBackType == kCFSocketAcceptCallBack) || !__CFSocketIsConnectionOriented(s)) s->_f.connected = TRUE; + if (((callBackTypes & kCFSocketWriteCallBack) != 0) || (((callBackTypes & kCFSocketConnectCallBack) != 0) && !s->_f.connected)) { + if (__CFSocketClearFDForWrite(s)) { + // do not wake up the socket manager thread if all relevant write callbacks are disabled + CFOptionFlags writeCallBacksAvailable = __CFSocketCallBackTypes(s) & (kCFSocketWriteCallBack | kCFSocketConnectCallBack); + if (s->_f.connected) writeCallBacksAvailable &= ~kCFSocketConnectCallBack; + if ((s->_f.disabled & writeCallBacksAvailable) != writeCallBacksAvailable) wakeup = true; + } + } + if (readCallBackType != kCFSocketNoCallBack && (callBackTypes & readCallBackType) != 0) { + if (__CFSocketClearFDForRead(s)) { + // do not wake up the socket manager thread if callback type is read + if (readCallBackType != kCFSocketReadCallBack) wakeup = true; + } + } + __CFSpinUnlock(&__CFActiveSocketsLock); + } + __CFSocketUnlock(s); + if (wakeup && __CFSocketManagerThread) { + uint8_t c = 'u'; + send(__CFWakeupSocketPair[0], (SOCK_CONST_DATA)&c, sizeof(c), 0); + } +} + +// "force" means to clear the disabled bits set by DisableCallBacks and always reenable. +// if (!force) we respect those bits, meaning they may stop us from enabling. +// In addition, if !force we assume that the sockets have already been added to the +// __CFReadSockets and __CFWriteSockets arrays. This is true because the callbacks start +// enabled when the CFSocket is created (at which time we enable with force). +// Called with SocketLock held, returns with it released! +void __CFSocketEnableCallBacks(CFSocketRef s, CFOptionFlags callBackTypes, Boolean force, uint8_t wakeupChar) { + CHECK_FOR_FORK(); + Boolean wakeup = FALSE; + if (!callBackTypes) { + __CFSocketUnlock(s); + return; + } + if (__CFSocketIsValid(s) && __CFSocketIsScheduled(s)) { + Boolean turnOnWrite = FALSE, turnOnConnect = FALSE, turnOnRead = FALSE; + uint8_t readCallBackType = __CFSocketReadCallBackType(s); + callBackTypes &= __CFSocketCallBackTypes(s); + if (force) s->_f.disabled &= ~callBackTypes; +#if defined(LOG_CFSOCKET) + fprintf(stdout, "rescheduling socket %d with flags 0x%x disabled 0x%x connected 0x%x for types 0x%lx\n", s->_socket, s->_f.client, s->_f.disabled, s->_f.connected, callBackTypes); +#endif + /* We will wait for connection only for connection-oriented, non-rendezvous sockets that are not already connected. Mark others as already connected. */ + if ((readCallBackType == kCFSocketAcceptCallBack) || !__CFSocketIsConnectionOriented(s)) s->_f.connected = TRUE; + + // First figure out what to turn on + if (s->_f.connected || (callBackTypes & kCFSocketConnectCallBack) == 0) { + // if we want write callbacks and they're not disabled... + if ((callBackTypes & kCFSocketWriteCallBack) != 0 && (s->_f.disabled & kCFSocketWriteCallBack) == 0) turnOnWrite = TRUE; + } else { + // if we want connect callbacks and they're not disabled... + if ((callBackTypes & kCFSocketConnectCallBack) != 0 && (s->_f.disabled & kCFSocketConnectCallBack) == 0) turnOnConnect = TRUE; + } + // if we want read callbacks and they're not disabled... + if (readCallBackType != kCFSocketNoCallBack && (callBackTypes & readCallBackType) != 0 && (s->_f.disabled & kCFSocketReadCallBack) == 0) turnOnRead = TRUE; + + // Now turn on the callbacks we've determined that we want on + if (turnOnRead || turnOnWrite || turnOnConnect) { + __CFSpinLock(&__CFActiveSocketsLock); + if (turnOnWrite || turnOnConnect) { + if (force) { + SInt32 idx = CFArrayGetFirstIndexOfValue(__CFWriteSockets, CFRangeMake(0, CFArrayGetCount(__CFWriteSockets)), s); + if (kCFNotFound == idx) CFArrayAppendValue(__CFWriteSockets, s); + } + if (__CFSocketSetFDForWrite(s)) wakeup = true; +#if DEPLOYMENT_TARGET_WINDOWS + if ((callBackTypes & kCFSocketConnectCallBack) != 0 && !s->_f.connected) __CFSocketFdSet(s->_socket, __CFExceptSocketsFds); +#endif + } + if (turnOnRead) { + if (force) { + SInt32 idx = CFArrayGetFirstIndexOfValue(__CFReadSockets, CFRangeMake(0, CFArrayGetCount(__CFReadSockets)), s); + if (kCFNotFound == idx) CFArrayAppendValue(__CFReadSockets, s); + } + if (__CFSocketSetFDForRead(s)) wakeup = true; + } + if (wakeup && NULL == __CFSocketManagerThread) __CFSocketManagerThread = __CFStartSimpleThread((void*)__CFSocketManager, 0); + __CFSpinUnlock(&__CFActiveSocketsLock); + } + } + __CFSocketUnlock(s); + if (wakeup) send(__CFWakeupSocketPair[0], (SOCK_CONST_DATA)&wakeupChar, sizeof(wakeupChar), 0); +} + +void CFSocketEnableCallBacks(CFSocketRef s, CFOptionFlags callBackTypes) { + CHECK_FOR_FORK(); + __CFGenericValidateType(s, __kCFSocketTypeID); + __CFSocketLock(s); + __CFSocketEnableCallBacks(s, callBackTypes, TRUE, 'r'); +} + +static void __CFSocketSchedule(void *info, CFRunLoopRef rl, CFStringRef mode) { + CFSocketRef s = (CFSocketRef)info; + __CFSocketLock(s); + //??? also need to arrange delivery of all pending data + if (__CFSocketIsValid(s)) { + CFArrayAppendValue(s->_runLoops, rl); + s->_socketSetCount++; + // Since the v0 source is listened to on the SocketMgr thread, no matter how many modes it + // is added to we just need to enable it there once (and _socketSetCount gives us a refCount + // to know when we can finally disable it). + if (1 == s->_socketSetCount) { +#if defined(LOG_CFSOCKET) + fprintf(stdout, "scheduling socket %d\n", s->_socket); +#endif + __CFSocketEnableCallBacks(s, __CFSocketCallBackTypes(s), TRUE, 's'); // unlocks s + } else + __CFSocketUnlock(s); + } else + __CFSocketUnlock(s); +} + +static void __CFSocketCancel(void *info, CFRunLoopRef rl, CFStringRef mode) { + CFSocketRef s = (CFSocketRef)info; + SInt32 idx; + __CFSocketLock(s); + s->_socketSetCount--; + if (0 == s->_socketSetCount) { + __CFSpinLock(&__CFActiveSocketsLock); + idx = CFArrayGetFirstIndexOfValue(__CFWriteSockets, CFRangeMake(0, CFArrayGetCount(__CFWriteSockets)), s); + if (0 <= idx) { + CFArrayRemoveValueAtIndex(__CFWriteSockets, idx); + __CFSocketClearFDForWrite(s); +#if DEPLOYMENT_TARGET_WINDOWS + __CFSocketFdClr(s->_socket, __CFExceptSocketsFds); +#endif + } + idx = CFArrayGetFirstIndexOfValue(__CFReadSockets, CFRangeMake(0, CFArrayGetCount(__CFReadSockets)), s); + if (0 <= idx) { + CFArrayRemoveValueAtIndex(__CFReadSockets, idx); + __CFSocketClearFDForRead(s); + } + __CFSpinUnlock(&__CFActiveSocketsLock); + } + if (NULL != s->_runLoops) { + idx = CFArrayGetFirstIndexOfValue(s->_runLoops, CFRangeMake(0, CFArrayGetCount(s->_runLoops)), rl); + if (0 <= idx) CFArrayRemoveValueAtIndex(s->_runLoops, idx); + } + __CFSocketUnlock(s); +} + +// Note: must be called with socket lock held, then returns with it released +// Used by both the v0 and v1 RunLoopSource perform routines +static void __CFSocketDoCallback(CFSocketRef s, CFDataRef data, CFDataRef address, CFSocketNativeHandle sock) { + CFSocketCallBack callout = NULL; + void *contextInfo = NULL; + SInt32 errorCode = 0; + Boolean readSignalled = false, writeSignalled = false, connectSignalled = false, calledOut = false; + uint8_t readCallBackType, callBackTypes; + + callBackTypes = __CFSocketCallBackTypes(s); + readCallBackType = __CFSocketReadCallBackType(s); + readSignalled = __CFSocketIsReadSignalled(s); + writeSignalled = __CFSocketIsWriteSignalled(s); + connectSignalled = writeSignalled && !s->_f.connected; + __CFSocketUnsetReadSignalled(s); + __CFSocketUnsetWriteSignalled(s); + callout = s->_callout; + contextInfo = s->_context.info; +#if defined(LOG_CFSOCKET) + fprintf(stdout, "entering perform for socket %d with read signalled %d write signalled %d connect signalled %d callback types %d\n", s->_socket, readSignalled, writeSignalled, connectSignalled, callBackTypes); +#endif + if (writeSignalled) { + errorCode = s->_errorCode; + s->_f.connected = TRUE; + } + __CFSocketUnlock(s); + if ((callBackTypes & kCFSocketConnectCallBack) != 0) { + if (connectSignalled && (!calledOut || CFSocketIsValid(s))) { + if (errorCode) { +#if defined(LOG_CFSOCKET) + fprintf(stdout, "perform calling out error %ld to socket %d\n", errorCode, s->_socket); +#endif + if (callout) callout(s, kCFSocketConnectCallBack, NULL, &errorCode, contextInfo); + calledOut = true; + } else { +#if defined(LOG_CFSOCKET) + fprintf(stdout, "perform calling out connect to socket %d\n", s->_socket); +#endif + if (callout) callout(s, kCFSocketConnectCallBack, NULL, NULL, contextInfo); + calledOut = true; + } + } + } + if (kCFSocketDataCallBack == readCallBackType) { + if (NULL != data && (!calledOut || CFSocketIsValid(s))) { + SInt32 datalen = CFDataGetLength(data); +#if defined(LOG_CFSOCKET) + fprintf(stdout, "perform calling out data of length %ld to socket %d\n", datalen, s->_socket); +#endif + if (callout) callout(s, kCFSocketDataCallBack, address, data, contextInfo); + calledOut = true; + if (0 == datalen) CFSocketInvalidate(s); + } + } else if (kCFSocketAcceptCallBack == readCallBackType) { + if (INVALID_SOCKET != sock && (!calledOut || CFSocketIsValid(s))) { +#if defined(LOG_CFSOCKET) + fprintf(stdout, "perform calling out accept of socket %d to socket %d\n", sock, s->_socket); +#endif + if (callout) callout(s, kCFSocketAcceptCallBack, address, &sock, contextInfo); + calledOut = true; + } + } else if (kCFSocketReadCallBack == readCallBackType) { + if (readSignalled && (!calledOut || CFSocketIsValid(s))) { +#if defined(LOG_CFSOCKET) + fprintf(stdout, "perform calling out read to socket %d\n", s->_socket); +#endif + if (callout) callout(s, kCFSocketReadCallBack, NULL, NULL, contextInfo); + calledOut = true; + } + } + if ((callBackTypes & kCFSocketWriteCallBack) != 0) { + if (writeSignalled && !errorCode && (!calledOut || CFSocketIsValid(s))) { +#if defined(LOG_CFSOCKET) + fprintf(stdout, "perform calling out write to socket %d\n", s->_socket); +#endif + if (callout) callout(s, kCFSocketWriteCallBack, NULL, NULL, contextInfo); + calledOut = true; + } + } +} + +static void __CFSocketPerformV0(void *info) { + CFSocketRef s = (CFSocketRef)info; + CFDataRef data = NULL; + CFDataRef address = NULL; + CFSocketNativeHandle sock = INVALID_SOCKET; + uint8_t readCallBackType, callBackTypes; + CFRunLoopRef rl = NULL; + + __CFSocketLock(s); + if (!__CFSocketIsValid(s)) { + __CFSocketUnlock(s); + return; + } + callBackTypes = __CFSocketCallBackTypes(s); + readCallBackType = __CFSocketReadCallBackType(s); + CFOptionFlags callBacksSignalled = 0; + if (__CFSocketIsReadSignalled(s)) callBacksSignalled |= readCallBackType; + if (__CFSocketIsWriteSignalled(s)) callBacksSignalled |= kCFSocketWriteCallBack; + + if (kCFSocketDataCallBack == readCallBackType) { + if (NULL != s->_dataQueue && 0 < CFArrayGetCount(s->_dataQueue)) { + data = (CFDataRef)CFArrayGetValueAtIndex(s->_dataQueue, 0); + CFRetain(data); + CFArrayRemoveValueAtIndex(s->_dataQueue, 0); + address = (CFDataRef)CFArrayGetValueAtIndex(s->_addressQueue, 0); + CFRetain(address); + CFArrayRemoveValueAtIndex(s->_addressQueue, 0); + } + } else if (kCFSocketAcceptCallBack == readCallBackType) { + if (NULL != s->_dataQueue && 0 < CFArrayGetCount(s->_dataQueue)) { + sock = (CFSocketNativeHandle)(uintptr_t)CFArrayGetValueAtIndex(s->_dataQueue, 0); + CFArrayRemoveValueAtIndex(s->_dataQueue, 0); + address = (CFDataRef)CFArrayGetValueAtIndex(s->_addressQueue, 0); + CFRetain(address); + CFArrayRemoveValueAtIndex(s->_addressQueue, 0); + } + } + + __CFSocketDoCallback(s, data, address, sock); // does __CFSocketUnlock(s) + if (NULL != data) CFRelease(data); + if (NULL != address) CFRelease(address); + + __CFSocketLock(s); + if (__CFSocketIsValid(s) && kCFSocketNoCallBack != readCallBackType) { + // if there's still more data, we want to wake back up right away + if ((kCFSocketDataCallBack == readCallBackType || kCFSocketAcceptCallBack == readCallBackType) && NULL != s->_dataQueue && 0 < CFArrayGetCount(s->_dataQueue)) { + CFRunLoopSourceSignal(s->_source0); +#if defined(LOG_CFSOCKET) + fprintf(stdout, "perform short-circuit signaling source for socket %d with flags 0x%x disabled 0x%x connected 0x%x\n", s->_socket, s->_f.client, s->_f.disabled, s->_f.connected); +#endif + rl = __CFSocketCopyRunLoopToWakeUp(s); + } + } + // Only reenable callbacks that are auto-reenabled + __CFSocketEnableCallBacks(s, callBacksSignalled & s->_f.client, FALSE, 'p'); // unlocks s + + if (NULL != rl) { + CFRunLoopWakeUp(rl); + CFRelease(rl); + } +} + +CFRunLoopSourceRef CFSocketCreateRunLoopSource(CFAllocatorRef allocator, CFSocketRef s, CFIndex order) { + CHECK_FOR_FORK(); + CFRunLoopSourceRef result = NULL; + __CFGenericValidateType(s, __kCFSocketTypeID); + __CFSocketLock(s); + if (__CFSocketIsValid(s)) { + if (NULL == s->_source0) { + CFRunLoopSourceContext context; + context.version = 0; + context.info = s; + context.retain = CFRetain; + context.release = CFRelease; + context.copyDescription = CFCopyDescription; + context.equal = CFEqual; + context.hash = CFHash; + context.schedule = __CFSocketSchedule; + context.cancel = __CFSocketCancel; + context.perform = __CFSocketPerformV0; + s->_source0 = CFRunLoopSourceCreate(allocator, order, &context); + } + CFRetain(s->_source0); /* This retain is for the receiver */ + result = s->_source0; + } + __CFSocketUnlock(s); + return result; +} + +#endif /* NEW_SOCKET */ + +static CFSpinLock_t __CFSocketWriteLock_ = CFSpinLockInit; +//#warning can only send on one socket at a time now + +CF_INLINE void __CFSocketWriteLock(CFSocketRef s) { + __CFSpinLock(& __CFSocketWriteLock_); +} + +CF_INLINE void __CFSocketWriteUnlock(CFSocketRef s) { + __CFSpinUnlock(& __CFSocketWriteLock_); +} + +//??? need timeout, error handling, retries +CFSocketError CFSocketSendData(CFSocketRef s, CFDataRef address, CFDataRef data, CFTimeInterval timeout) { + CHECK_FOR_FORK(); + const uint8_t *dataptr, *addrptr = NULL; + SInt32 datalen, addrlen = 0, size = 0; + CFSocketNativeHandle sock = INVALID_SOCKET; + struct timeval tv; + __CFGenericValidateType(s, CFSocketGetTypeID()); + if (address) { + addrptr = CFDataGetBytePtr(address); + addrlen = CFDataGetLength(address); + } + dataptr = CFDataGetBytePtr(data); + datalen = CFDataGetLength(data); + if (CFSocketIsValid(s)) sock = CFSocketGetNative(s); + if (INVALID_SOCKET != sock) { + CFRetain(s); + __CFSocketWriteLock(s); + tv.tv_sec = (0 >= timeout || INT_MAX <= timeout) ? INT_MAX : (int)(float)floor(timeout); + tv.tv_usec = (int)((timeout - floor(timeout)) * 1.0E6); + setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (SOCK_CONST_DATA)&tv, sizeof(tv)); // cast for WinSock bad API + if (NULL != addrptr && 0 < addrlen) { + size = sendto(sock, (SOCK_CONST_DATA)dataptr, datalen, 0, (struct sockaddr *)addrptr, addrlen); + } else { + size = send(sock, (SOCK_CONST_DATA)dataptr, datalen, 0); + } +#if defined(LOG_CFSOCKET) + fprintf(stdout, "wrote %ld bytes to socket %d\n", size, sock); +#endif + __CFSocketWriteUnlock(s); + CFRelease(s); + } + return (size > 0) ? kCFSocketSuccess : kCFSocketError; +} + +CFSocketError CFSocketSetAddress(CFSocketRef s, CFDataRef address) { + CHECK_FOR_FORK(); + const uint8_t *name; + SInt32 namelen, result = 0; + __CFGenericValidateType(s, CFSocketGetTypeID()); + if (NULL == address) return -1; + if (!CFSocketIsValid(s)) return 0; + name = CFDataGetBytePtr(address); + namelen = CFDataGetLength(address); + if (!name || namelen <= 0) return 0; + CFSocketNativeHandle sock = CFSocketGetNative(s); + result = bind(sock, (struct sockaddr *)name, namelen); + if (0 == result) { + listen(sock, 256); + } + //??? should return errno + return result; +} + +CFSocketError CFSocketConnectToAddress(CFSocketRef s, CFDataRef address, CFTimeInterval timeout) { + CHECK_FOR_FORK(); + //??? need error handling, retries + const uint8_t *name; + SInt32 namelen, result = -1, connect_err = 0, select_err = 0; +#if DEPLOYMENT_TARGET_WINDOWS + u_long yes = 1, no = 0; +#else + UInt32 yes = 1, no = 0; +#endif + Boolean wasBlocking = true; + + __CFGenericValidateType(s, CFSocketGetTypeID()); + if (!CFSocketIsValid(s)) return 0; + name = CFDataGetBytePtr(address); + namelen = CFDataGetLength(address); + if (!name || namelen <= 0) return 0; + CFSocketNativeHandle sock = CFSocketGetNative(s); + { +#if !DEPLOYMENT_TARGET_WINDOWS + SInt32 flags = fcntl(sock, F_GETFL, 0); + if (flags >= 0) wasBlocking = ((flags & O_NONBLOCK) == 0); +#endif + if (wasBlocking && (timeout > 0.0 || timeout < 0.0)) ioctlsocket(sock, FIONBIO, &yes); + result = connect(sock, (struct sockaddr *)name, namelen); + if (result != 0) { + connect_err = __CFSocketLastError(); +#if DEPLOYMENT_TARGET_WINDOWS + if (connect_err == WSAEWOULDBLOCK) connect_err = EINPROGRESS; +#endif + } +#if defined(LOG_CFSOCKET) +#if !DEPLOYMENT_TARGET_WINDOWS + fprintf(stdout, "connection attempt returns %ld error %ld on socket %d (flags 0x%lx blocking %d)\n", result, connect_err, sock, flags, wasBlocking); +#else + fprintf(stdout, "connection attempt returns %ld error %ld on socket %d\n", result, connect_err, s->_socket); +#endif + +#endif + if (EINPROGRESS == connect_err && timeout >= 0.0) { + /* select on socket */ + SInt32 nrfds; + int error_size = sizeof(select_err); + struct timeval tv; + CFMutableDataRef fds = CFDataCreateMutable(kCFAllocatorSystemDefault, 0); +#if DEPLOYMENT_TARGET_WINDOWS + CFDataIncreaseLength(fds , sizeof(u_int) + sizeof(SOCKET)); +#endif + __CFSocketFdSet(sock, fds); + tv.tv_sec = (0 >= timeout || INT_MAX <= timeout) ? INT_MAX : (int)(float)floor(timeout); + tv.tv_usec = (int)((timeout - floor(timeout)) * 1.0E6); + nrfds = select(__CFSocketFdGetSize(fds), NULL, (fd_set *)CFDataGetMutableBytePtr(fds), NULL, &tv); + if (nrfds < 0) { + select_err = __CFSocketLastError(); + result = -1; + } else if (nrfds == 0) { + result = -2; + } else { + if (0 != getsockopt(sock, SOL_SOCKET, SO_ERROR, (SOCK_DATA)&select_err, (socklen_t *)&error_size)) select_err = 0; + result = (select_err == 0) ? 0 : -1; + } + CFRelease(fds); +#if defined(LOG_CFSOCKET) + fprintf(stdout, "timed connection attempt %s on socket %d, result %ld, select returns %ld error %ld\n", (result == 0) ? "succeeds" : "fails", sock, result, nrfds, select_err); +#endif + } + if (wasBlocking && (timeout > 0.0 || timeout < 0.0)) ioctlsocket(sock, FIONBIO, &no); + if (EINPROGRESS == connect_err && timeout < 0.0) { + result = 0; +#if defined(LOG_CFSOCKET) + fprintf(stdout, "connection attempt continues in background on socket %d\n", sock); +#endif + } + } + //??? should return errno + return result; +} + +CFSocketRef CFSocketCreate(CFAllocatorRef allocator, SInt32 protocolFamily, SInt32 socketType, SInt32 protocol, CFOptionFlags callBackTypes, CFSocketCallBack callout, const CFSocketContext *context) { + CHECK_FOR_FORK(); + CFSocketNativeHandle sock = INVALID_SOCKET; + CFSocketRef s = NULL; + if (0 >= protocolFamily) protocolFamily = PF_INET; + if (PF_INET == protocolFamily) { + if (0 >= socketType) socketType = SOCK_STREAM; + if (0 >= protocol && SOCK_STREAM == socketType) protocol = IPPROTO_TCP; + if (0 >= protocol && SOCK_DGRAM == socketType) protocol = IPPROTO_UDP; + } +#if !DEPLOYMENT_TARGET_WINDOWS + if (PF_LOCAL == protocolFamily && 0 >= socketType) socketType = SOCK_STREAM; +#else + __CFSocketInitializeWinSock(); +#endif + sock = (CFSocketNativeHandle)socket(protocolFamily, socketType, protocol); + if (INVALID_SOCKET != sock) { + s = _CFSocketCreateWithNative(allocator, sock, callBackTypes, callout, context, FALSE); + } + return s; +} + +CFSocketRef CFSocketCreateWithSocketSignature(CFAllocatorRef allocator, const CFSocketSignature *signature, CFOptionFlags callBackTypes, CFSocketCallBack callout, const CFSocketContext *context) { + CHECK_FOR_FORK(); + CFSocketRef s = CFSocketCreate(allocator, signature->protocolFamily, signature->socketType, signature->protocol, callBackTypes, callout, context); + if (NULL != s && (!CFSocketIsValid(s) || kCFSocketSuccess != CFSocketSetAddress(s, signature->address))) { + CFSocketInvalidate(s); + CFRelease(s); + s = NULL; + } + return s; +} + +CFSocketRef CFSocketCreateConnectedToSocketSignature(CFAllocatorRef allocator, const CFSocketSignature *signature, CFOptionFlags callBackTypes, CFSocketCallBack callout, const CFSocketContext *context, CFTimeInterval timeout) { + CHECK_FOR_FORK(); + CFSocketRef s = CFSocketCreate(allocator, signature->protocolFamily, signature->socketType, signature->protocol, callBackTypes, callout, context); + if (NULL != s && (!CFSocketIsValid(s) || kCFSocketSuccess != CFSocketConnectToAddress(s, signature->address, timeout))) { + CFSocketInvalidate(s); + CFRelease(s); + s = NULL; + } + return s; +} + +typedef struct { + CFSocketError *error; + CFPropertyListRef *value; + CFDataRef *address; +} __CFSocketNameRegistryResponse; + +static void __CFSocketHandleNameRegistryReply(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) { + CFDataRef replyData = (CFDataRef)data; + __CFSocketNameRegistryResponse *response = (__CFSocketNameRegistryResponse *)info; + CFDictionaryRef replyDictionary = NULL; + CFPropertyListRef value; + replyDictionary = (CFDictionaryRef)CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, replyData, kCFPropertyListImmutable, NULL); + if (NULL != response->error) *(response->error) = kCFSocketError; + if (NULL != replyDictionary) { + if (CFGetTypeID((CFTypeRef)replyDictionary) == CFDictionaryGetTypeID() && NULL != (value = CFDictionaryGetValue(replyDictionary, kCFSocketResultKey))) { + if (NULL != response->error) *(response->error) = kCFSocketSuccess; + if (NULL != response->value) *(response->value) = CFRetain(value); + if (NULL != response->address) *(response->address) = address ? CFDataCreateCopy(kCFAllocatorSystemDefault, address) : NULL; + } + CFRelease(replyDictionary); + } + CFSocketInvalidate(s); +} + +static void __CFSocketSendNameRegistryRequest(CFSocketSignature *signature, CFDictionaryRef requestDictionary, __CFSocketNameRegistryResponse *response, CFTimeInterval timeout) { + CFDataRef requestData = NULL; + CFSocketContext context = {0, response, NULL, NULL, NULL}; + CFSocketRef s = NULL; + CFRunLoopSourceRef source = NULL; + if (NULL != response->error) *(response->error) = kCFSocketError; + requestData = CFPropertyListCreateXMLData(kCFAllocatorSystemDefault, requestDictionary); + if (NULL != requestData) { + if (NULL != response->error) *(response->error) = kCFSocketTimeout; + s = CFSocketCreateConnectedToSocketSignature(kCFAllocatorSystemDefault, signature, kCFSocketDataCallBack, __CFSocketHandleNameRegistryReply, &context, timeout); + if (NULL != s) { + if (kCFSocketSuccess == CFSocketSendData(s, NULL, requestData, timeout)) { + source = CFSocketCreateRunLoopSource(kCFAllocatorSystemDefault, s, 0); + CFRunLoopAddSource(CFRunLoopGetCurrent(), source, __kCFSocketRegistryRequestRunLoopMode); + CFRunLoopRunInMode(__kCFSocketRegistryRequestRunLoopMode, timeout, false); + CFRelease(source); + } + CFSocketInvalidate(s); + CFRelease(s); + } + CFRelease(requestData); + } +} + +static void __CFSocketValidateSignature(const CFSocketSignature *providedSignature, CFSocketSignature *signature, uint16_t defaultPortNumber) { + struct sockaddr_in sain, *sainp; + memset(&sain, 0, sizeof(sain)); +#if DEPLOYMENT_TARGET_MACOSX + sain.sin_len = sizeof(sain); +#endif + sain.sin_family = AF_INET; + sain.sin_port = htons(__CFSocketDefaultNameRegistryPortNumber); + sain.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + if (NULL == providedSignature) { + signature->protocolFamily = PF_INET; + signature->socketType = SOCK_STREAM; + signature->protocol = IPPROTO_TCP; + signature->address = CFDataCreate(kCFAllocatorSystemDefault, (uint8_t *)&sain, sizeof(sain)); + } else { + signature->protocolFamily = providedSignature->protocolFamily; + signature->socketType = providedSignature->socketType; + signature->protocol = providedSignature->protocol; + if (0 >= signature->protocolFamily) signature->protocolFamily = PF_INET; + if (PF_INET == signature->protocolFamily) { + if (0 >= signature->socketType) signature->socketType = SOCK_STREAM; + if (0 >= signature->protocol && SOCK_STREAM == signature->socketType) signature->protocol = IPPROTO_TCP; + if (0 >= signature->protocol && SOCK_DGRAM == signature->socketType) signature->protocol = IPPROTO_UDP; + } + if (NULL == providedSignature->address) { + signature->address = CFDataCreate(kCFAllocatorSystemDefault, (uint8_t *)&sain, sizeof(sain)); + } else { + sainp = (struct sockaddr_in *)CFDataGetBytePtr(providedSignature->address); + if ((int)sizeof(struct sockaddr_in) <= CFDataGetLength(providedSignature->address) && (AF_INET == sainp->sin_family || 0 == sainp->sin_family)) { +#if DEPLOYMENT_TARGET_MACOSX + sain.sin_len = sizeof(sain); +#endif + sain.sin_family = AF_INET; + sain.sin_port = sainp->sin_port; + if (0 == sain.sin_port) sain.sin_port = htons(defaultPortNumber); + sain.sin_addr.s_addr = sainp->sin_addr.s_addr; + if (htonl(INADDR_ANY) == sain.sin_addr.s_addr) sain.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + signature->address = CFDataCreate(kCFAllocatorSystemDefault, (uint8_t *)&sain, sizeof(sain)); + } else { + signature->address = (CFDataRef)CFRetain(providedSignature->address); + } + } + } +} + +CFSocketError CFSocketRegisterValue(const CFSocketSignature *nameServerSignature, CFTimeInterval timeout, CFStringRef name, CFPropertyListRef value) { + CFSocketSignature signature; + CFMutableDictionaryRef dictionary = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFSocketError retval = kCFSocketError; + __CFSocketNameRegistryResponse response = {&retval, NULL, NULL}; + CFDictionaryAddValue(dictionary, kCFSocketCommandKey, kCFSocketRegisterCommand); + CFDictionaryAddValue(dictionary, kCFSocketNameKey, name); + if (NULL != value) CFDictionaryAddValue(dictionary, kCFSocketValueKey, value); + __CFSocketValidateSignature(nameServerSignature, &signature, __CFSocketDefaultNameRegistryPortNumber); + __CFSocketSendNameRegistryRequest(&signature, dictionary, &response, timeout); + CFRelease(dictionary); + CFRelease(signature.address); + return retval; +} + +CFSocketError CFSocketCopyRegisteredValue(const CFSocketSignature *nameServerSignature, CFTimeInterval timeout, CFStringRef name, CFPropertyListRef *value, CFDataRef *serverAddress) { + CFSocketSignature signature; + CFMutableDictionaryRef dictionary = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFSocketError retval = kCFSocketError; + __CFSocketNameRegistryResponse response = {&retval, value, serverAddress}; + CFDictionaryAddValue(dictionary, kCFSocketCommandKey, kCFSocketRetrieveCommand); + CFDictionaryAddValue(dictionary, kCFSocketNameKey, name); + __CFSocketValidateSignature(nameServerSignature, &signature, __CFSocketDefaultNameRegistryPortNumber); + __CFSocketSendNameRegistryRequest(&signature, dictionary, &response, timeout); + CFRelease(dictionary); + CFRelease(signature.address); + return retval; +} + +CFSocketError CFSocketRegisterSocketSignature(const CFSocketSignature *nameServerSignature, CFTimeInterval timeout, CFStringRef name, const CFSocketSignature *signature) { + CFSocketSignature validatedSignature; + CFMutableDataRef data = NULL; + CFSocketError retval; + CFIndex length; + uint8_t bytes[4]; + if (NULL == signature) { + retval = CFSocketUnregister(nameServerSignature, timeout, name); + } else { + __CFSocketValidateSignature(signature, &validatedSignature, 0); + if (NULL == validatedSignature.address || 0 > validatedSignature.protocolFamily || 255 < validatedSignature.protocolFamily || 0 > validatedSignature.socketType || 255 < validatedSignature.socketType || 0 > validatedSignature.protocol || 255 < validatedSignature.protocol || 0 >= (length = CFDataGetLength(validatedSignature.address)) || 255 < length) { + retval = kCFSocketError; + } else { + data = CFDataCreateMutable(kCFAllocatorSystemDefault, sizeof(bytes) + length); + bytes[0] = validatedSignature.protocolFamily; + bytes[1] = validatedSignature.socketType; + bytes[2] = validatedSignature.protocol; + bytes[3] = (uint8_t)length; + CFDataAppendBytes(data, bytes, sizeof(bytes)); + CFDataAppendBytes(data, CFDataGetBytePtr(validatedSignature.address), length); + retval = CFSocketRegisterValue(nameServerSignature, timeout, name, data); + CFRelease(data); + } + CFRelease(validatedSignature.address); + } + return retval; +} + +CFSocketError CFSocketCopyRegisteredSocketSignature(const CFSocketSignature *nameServerSignature, CFTimeInterval timeout, CFStringRef name, CFSocketSignature *signature, CFDataRef *nameServerAddress) { + CFDataRef data = NULL; + CFSocketSignature returnedSignature; + const uint8_t *ptr = NULL, *aptr = NULL; + uint8_t *mptr; + CFIndex length = 0; + CFDataRef serverAddress = NULL; + CFSocketError retval = CFSocketCopyRegisteredValue(nameServerSignature, timeout, name, (CFPropertyListRef *)&data, &serverAddress); + if (NULL == data || CFGetTypeID(data) != CFDataGetTypeID() || NULL == (ptr = CFDataGetBytePtr(data)) || (length = CFDataGetLength(data)) < 4) retval = kCFSocketError; + if (kCFSocketSuccess == retval && NULL != signature) { + returnedSignature.protocolFamily = (SInt32)*ptr++; + returnedSignature.socketType = (SInt32)*ptr++; + returnedSignature.protocol = (SInt32)*ptr++; + ptr++; + returnedSignature.address = CFDataCreate(kCFAllocatorSystemDefault, ptr, length - 4); + __CFSocketValidateSignature(&returnedSignature, signature, 0); + CFRelease(returnedSignature.address); + ptr = CFDataGetBytePtr(signature->address); + if (CFDataGetLength(signature->address) >= (int)sizeof(struct sockaddr_in) && AF_INET == ((struct sockaddr *)ptr)->sa_family && NULL != serverAddress && CFDataGetLength(serverAddress) >= (int)sizeof(struct sockaddr_in) && NULL != (aptr = CFDataGetBytePtr(serverAddress)) && AF_INET == ((struct sockaddr *)aptr)->sa_family) { + CFMutableDataRef address = CFDataCreateMutableCopy(kCFAllocatorSystemDefault, CFDataGetLength(signature->address), signature->address); + mptr = CFDataGetMutableBytePtr(address); + ((struct sockaddr_in *)mptr)->sin_addr = ((struct sockaddr_in *)aptr)->sin_addr; + CFRelease(signature->address); + signature->address = address; + } + if (NULL != nameServerAddress) *nameServerAddress = serverAddress ? (CFDataRef)CFRetain(serverAddress) : NULL; + } + if (NULL != data) CFRelease(data); + if (NULL != serverAddress) CFRelease(serverAddress); + return retval; +} + +CFSocketError CFSocketUnregister(const CFSocketSignature *nameServerSignature, CFTimeInterval timeout, CFStringRef name) { + return CFSocketRegisterValue(nameServerSignature, timeout, name, NULL); +} + +CF_EXPORT void CFSocketSetDefaultNameRegistryPortNumber(uint16_t port) { + __CFSocketDefaultNameRegistryPortNumber = port; +} + +CF_EXPORT uint16_t CFSocketGetDefaultNameRegistryPortNumber(void) { + return __CFSocketDefaultNameRegistryPortNumber; +} + +
diff --git a/CoreFoundation/CFSocket.h b/CoreFoundation/CFSocket.h new file mode 100644 index 0000000..919f2d8 --- /dev/null +++ b/CoreFoundation/CFSocket.h
@@ -0,0 +1,252 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFSocket.h + Copyright (c) 1999-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFSOCKET__) +#define __COREFOUNDATION_CFSOCKET__ 1 + +#ifdef WIN32 +// Avoid actually including <winsock2.h>, as it can screw up lots of data type definitions +typedef unsigned int CFSocketNativeHandle; +#else +typedef int CFSocketNativeHandle; +#endif + +#include <CoreFoundation/CFRunLoop.h> +#include <CoreFoundation/CFData.h> + +CF_EXTERN_C_BEGIN + +typedef struct __CFSocket * CFSocketRef; + +/* A CFSocket contains a native socket within a structure that can +be used to read from the socket in the background and make the data +thus read available using a runloop source. The callback used for +this may be of three types, as specified by the callBackTypes +argument when creating the CFSocket. + +If kCFSocketReadCallBack is used, then data will not be +automatically read, but the callback will be called when data +is available to be read, or a new child socket is waiting to be +accepted. + +If kCFSocketAcceptCallBack is used, then new child sockets will be +accepted and passed to the callback, with the data argument being +a pointer to a CFSocketNativeHandle. This is usable only with +connection rendezvous sockets. + +If kCFSocketDataCallBack is used, then data will be read in chunks +in the background and passed to the callback, with the data argument +being a CFDataRef. + +These three types are mutually exclusive, but any one of them may +have kCFSocketConnectCallBack added to it, if the socket will be +used to connect in the background. Connect in the background occurs +if CFSocketConnectToAddress is called with a negative timeout +value, in which case the call returns immediately, and a +kCFSocketConnectCallBack is generated when the connect finishes. +In this case the data argument is either NULL, or a pointer to +an SInt32 error code if the connect failed. kCFSocketConnectCallBack +will never be sent more than once for a given socket. + +The callback types may also have kCFSocketWriteCallBack added to +them, if large amounts of data are to be sent rapidly over the +socket and notification is desired when there is space in the +kernel buffers so that the socket is writable again. + +With a connection-oriented socket, if the connection is broken from the +other end, then one final kCFSocketReadCallBack or kCFSocketDataCallBack +will occur. In the case of kCFSocketReadCallBack, the underlying socket +will have 0 bytes available to read. In the case of kCFSocketDataCallBack, +the data argument will be a CFDataRef of length 0. + +There are socket flags that may be set to control whether callbacks of +a given type are automatically reenabled after they are triggered, and +whether the underlying native socket will be closed when the CFSocket +is invalidated. By default read, accept, and data callbacks are +automatically reenabled; write callbacks are not, and connect callbacks +may not be, since they are sent once only. Be careful about automatically +reenabling read and write callbacks, since this implies that the +callbacks will be sent repeatedly if the socket remains readable or +writable respectively. Be sure to set these flags only for callbacks +that your CFSocket actually possesses; the result of setting them for +other callback types is undefined. + +Individual callbacks may also be enabled and disabled manually, whether +they are automatically reenabled or not. If they are not automatically +reenabled, then they will need to be manually reenabled when the callback +is ready to be received again (and not sooner). Even if they are +automatically reenabled, there may be occasions when it will be useful +to be able to manually disable them temporarily and then reenable them. +Be sure to enable and disable only callbacks that your CFSocket actually +possesses; the result of enabling and disabling other callback types is +undefined. + +By default the underlying native socket will be closed when the CFSocket +is invalidated, but it will not be if kCFSocketCloseOnInvalidate is +turned off. This can be useful in order to destroy a CFSocket but +continue to use the underlying native socket. The CFSocket must +still be invalidated when it will no longer be used. Do not in +either case close the underlying native socket without invalidating +the CFSocket. + +Addresses are stored as CFDatas containing a struct sockaddr +appropriate for the protocol family; make sure that all fields are +filled in properly when passing in an address. + +*/ + +/* Values for CFSocketError */ +enum { + kCFSocketSuccess = 0, + kCFSocketError = -1, + kCFSocketTimeout = -2 +}; +typedef CFIndex CFSocketError; + +typedef struct { + SInt32 protocolFamily; + SInt32 socketType; + SInt32 protocol; + CFDataRef address; +} CFSocketSignature; + +/* Values for CFSocketCallBackType */ +enum { + kCFSocketNoCallBack = 0, + kCFSocketReadCallBack = 1, + kCFSocketAcceptCallBack = 2, + kCFSocketDataCallBack = 3, + kCFSocketConnectCallBack = 4 +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED + , + kCFSocketWriteCallBack = 8 +#endif +}; +typedef CFOptionFlags CFSocketCallBackType; + +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED +/* Socket flags */ +enum { + kCFSocketAutomaticallyReenableReadCallBack = 1, + kCFSocketAutomaticallyReenableAcceptCallBack = 2, + kCFSocketAutomaticallyReenableDataCallBack = 3, + kCFSocketAutomaticallyReenableWriteCallBack = 8, + kCFSocketCloseOnInvalidate = 128 +}; +#endif + +typedef void (*CFSocketCallBack)(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info); +/* If the callback wishes to keep hold of address or data after the point that it returns, then it must copy them. */ + +typedef struct { + CFIndex version; + void * info; + const void *(*retain)(const void *info); + void (*release)(const void *info); + CFStringRef (*copyDescription)(const void *info); +} CFSocketContext; + +CF_EXPORT CFTypeID CFSocketGetTypeID(void); + +CF_EXPORT CFSocketRef CFSocketCreate(CFAllocatorRef allocator, SInt32 protocolFamily, SInt32 socketType, SInt32 protocol, CFOptionFlags callBackTypes, CFSocketCallBack callout, const CFSocketContext *context); +CF_EXPORT CFSocketRef CFSocketCreateWithNative(CFAllocatorRef allocator, CFSocketNativeHandle sock, CFOptionFlags callBackTypes, CFSocketCallBack callout, const CFSocketContext *context); +CF_EXPORT CFSocketRef CFSocketCreateWithSocketSignature(CFAllocatorRef allocator, const CFSocketSignature *signature, CFOptionFlags callBackTypes, CFSocketCallBack callout, const CFSocketContext *context); +CF_EXPORT CFSocketRef CFSocketCreateConnectedToSocketSignature(CFAllocatorRef allocator, const CFSocketSignature *signature, CFOptionFlags callBackTypes, CFSocketCallBack callout, const CFSocketContext *context, CFTimeInterval timeout); +/* CFSocketCreateWithSignature creates a socket of the requested type and binds its address (using CFSocketSetAddress) to the requested address. If this fails, it returns NULL. CFSocketCreateConnectedToSignature creates a socket suitable for connecting to the requested type and address, and connects it (using CFSocketConnectToAddress). If this fails, it returns NULL. */ + +CF_EXPORT CFSocketError CFSocketSetAddress(CFSocketRef s, CFDataRef address); +CF_EXPORT CFSocketError CFSocketConnectToAddress(CFSocketRef s, CFDataRef address, CFTimeInterval timeout); +CF_EXPORT void CFSocketInvalidate(CFSocketRef s); + +CF_EXPORT Boolean CFSocketIsValid(CFSocketRef s); +CF_EXPORT CFDataRef CFSocketCopyAddress(CFSocketRef s); +CF_EXPORT CFDataRef CFSocketCopyPeerAddress(CFSocketRef s); +CF_EXPORT void CFSocketGetContext(CFSocketRef s, CFSocketContext *context); +CF_EXPORT CFSocketNativeHandle CFSocketGetNative(CFSocketRef s); + +CF_EXPORT CFRunLoopSourceRef CFSocketCreateRunLoopSource(CFAllocatorRef allocator, CFSocketRef s, CFIndex order); + +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED +CF_EXPORT CFOptionFlags CFSocketGetSocketFlags(CFSocketRef s); +CF_EXPORT void CFSocketSetSocketFlags(CFSocketRef s, CFOptionFlags flags); +CF_EXPORT void CFSocketDisableCallBacks(CFSocketRef s, CFOptionFlags callBackTypes); +CF_EXPORT void CFSocketEnableCallBacks(CFSocketRef s, CFOptionFlags callBackTypes); +#endif + +/* For convenience, a function is provided to send data using the socket with a timeout. The timeout will be used only if the specified value is positive. The address should be left NULL if the socket is already connected. */ +CF_EXPORT CFSocketError CFSocketSendData(CFSocketRef s, CFDataRef address, CFDataRef data, CFTimeInterval timeout); + +/* Generic name registry functionality (CFSocketRegisterValue, +CFSocketCopyRegisteredValue) allows the registration of any property +list type. Functions specific to CFSockets (CFSocketRegisterSocketData, +CFSocketCopyRegisteredSocketData) register a CFData containing the +components of a socket signature (protocol family, socket type, +protocol, and address). In each function the nameServerSignature +may be NULL, or any component of it may be 0, to use default values +(TCP, INADDR_LOOPBACK, port as set). Name registration servers might +not allow registration with other than TCP and INADDR_LOOPBACK. +The actual address of the server responding to a query may be obtained +by using the nameServerAddress argument. This address, the address +returned by CFSocketCopyRegisteredSocketSignature, and the value +returned by CFSocketCopyRegisteredValue must (if non-null) be released +by the caller. CFSocketUnregister removes any registration associated +with the specified name. +*/ + +CF_EXPORT CFSocketError CFSocketRegisterValue(const CFSocketSignature *nameServerSignature, CFTimeInterval timeout, CFStringRef name, CFPropertyListRef value); +CF_EXPORT CFSocketError CFSocketCopyRegisteredValue(const CFSocketSignature *nameServerSignature, CFTimeInterval timeout, CFStringRef name, CFPropertyListRef *value, CFDataRef *nameServerAddress); + +CF_EXPORT CFSocketError CFSocketRegisterSocketSignature(const CFSocketSignature *nameServerSignature, CFTimeInterval timeout, CFStringRef name, const CFSocketSignature *signature); +CF_EXPORT CFSocketError CFSocketCopyRegisteredSocketSignature(const CFSocketSignature *nameServerSignature, CFTimeInterval timeout, CFStringRef name, CFSocketSignature *signature, CFDataRef *nameServerAddress); + +CF_EXPORT CFSocketError CFSocketUnregister(const CFSocketSignature *nameServerSignature, CFTimeInterval timeout, CFStringRef name); + +CF_EXPORT void CFSocketSetDefaultNameRegistryPortNumber(UInt16 port); +CF_EXPORT UInt16 CFSocketGetDefaultNameRegistryPortNumber(void); + +/* Constants used in name registry server communications */ +CF_EXPORT const CFStringRef kCFSocketCommandKey; +CF_EXPORT const CFStringRef kCFSocketNameKey; +CF_EXPORT const CFStringRef kCFSocketValueKey; +CF_EXPORT const CFStringRef kCFSocketResultKey; +CF_EXPORT const CFStringRef kCFSocketErrorKey; +CF_EXPORT const CFStringRef kCFSocketRegisterCommand; +CF_EXPORT const CFStringRef kCFSocketRetrieveCommand; + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFSOCKET__ */ +
diff --git a/CoreFoundation/CFSocketStream.c b/CoreFoundation/CFSocketStream.c new file mode 100644 index 0000000..fdca231 --- /dev/null +++ b/CoreFoundation/CFSocketStream.c
@@ -0,0 +1,231 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFSocketStream.c + Copyright 2000-2002, Apple, Inc. All rights reserved. + Responsibility: Jeremy Wyld +*/ +// Original Author: Becky Willrich +#include <CoreFoundation/CFStream.h> +#include <CoreFoundation/CFNumber.h> +#include "CFInternal.h" +#include "CFStreamInternal.h" +#include "CFStreamPriv.h" + +#if DEPLOYMENT_TARGET_MACOSX +// On Mach these live in CF for historical reasons, even though they are declared in CFNetwork + +const int kCFStreamErrorDomainSSL = 3; +const int kCFStreamErrorDomainSOCKS = 5; + +CONST_STRING_DECL(kCFStreamPropertyShouldCloseNativeSocket, "kCFStreamPropertyShouldCloseNativeSocket") +CONST_STRING_DECL(kCFStreamPropertyAutoErrorOnSystemChange, "kCFStreamPropertyAutoErrorOnSystemChange"); + +CONST_STRING_DECL(kCFStreamPropertySOCKSProxy, "kCFStreamPropertySOCKSProxy") +CONST_STRING_DECL(kCFStreamPropertySOCKSProxyHost, "SOCKSProxy") +CONST_STRING_DECL(kCFStreamPropertySOCKSProxyPort, "SOCKSPort") +CONST_STRING_DECL(kCFStreamPropertySOCKSVersion, "kCFStreamPropertySOCKSVersion") +CONST_STRING_DECL(kCFStreamSocketSOCKSVersion4, "kCFStreamSocketSOCKSVersion4") +CONST_STRING_DECL(kCFStreamSocketSOCKSVersion5, "kCFStreamSocketSOCKSVersion5") +CONST_STRING_DECL(kCFStreamPropertySOCKSUser, "kCFStreamPropertySOCKSUser") +CONST_STRING_DECL(kCFStreamPropertySOCKSPassword, "kCFStreamPropertySOCKSPassword") + +CONST_STRING_DECL(kCFStreamPropertySocketSecurityLevel, "kCFStreamPropertySocketSecurityLevel"); +CONST_STRING_DECL(kCFStreamSocketSecurityLevelNone, "kCFStreamSocketSecurityLevelNone"); +CONST_STRING_DECL(kCFStreamSocketSecurityLevelSSLv2, "kCFStreamSocketSecurityLevelSSLv2"); +CONST_STRING_DECL(kCFStreamSocketSecurityLevelSSLv3, "kCFStreamSocketSecurityLevelSSLv3"); +CONST_STRING_DECL(kCFStreamSocketSecurityLevelTLSv1, "kCFStreamSocketSecurityLevelTLSv1"); +CONST_STRING_DECL(kCFStreamSocketSecurityLevelNegotiatedSSL, "kCFStreamSocketSecurityLevelNegotiatedSSL"); + +#endif + +// These are duplicated in CFNetwork, who actually externs them in its headers +CONST_STRING_DECL(kCFStreamPropertySocketSSLContext, "kCFStreamPropertySocketSSLContext") +CONST_STRING_DECL(_kCFStreamPropertySocketSecurityAuthenticatesServerCertificate, "_kCFStreamPropertySocketSecurityAuthenticatesServerCertificate"); + + +CF_EXPORT +void _CFSocketStreamSetAuthenticatesServerCertificateDefault(Boolean shouldAuthenticate) { + CFLog(__kCFLogAssertion, CFSTR("_CFSocketStreamSetAuthenticatesServerCertificateDefault(): This call has been deprecated. Use SetProperty(_kCFStreamPropertySocketSecurityAuthenticatesServerCertificate, kCFBooleanTrue/False)\n")); +} + + +/* CF_EXPORT */ Boolean +_CFSocketStreamGetAuthenticatesServerCertificateDefault(void) { + CFLog(__kCFLogAssertion, CFSTR("_CFSocketStreamGetAuthenticatesServerCertificateDefault(): This call has been removed as a security risk. Use security properties on individual streams instead.\n")); + return FALSE; +} + + +/* CF_EXPORT */ void +_CFSocketStreamPairSetAuthenticatesServerCertificate(CFReadStreamRef rStream, CFWriteStreamRef wStream, Boolean authenticates) { + + CFBooleanRef value = (!authenticates ? kCFBooleanFalse : kCFBooleanTrue); + + if (rStream) + CFReadStreamSetProperty(rStream, _kCFStreamPropertySocketSecurityAuthenticatesServerCertificate, value); + else + CFWriteStreamSetProperty(wStream, _kCFStreamPropertySocketSecurityAuthenticatesServerCertificate, value); +} + + +// Flags for dyld loading of libraries. +enum { + kTriedToLoad = 0, + kInitialized +}; + +typedef void (*cf_net_CreatePair)(CFAllocatorRef, CFStringRef, UInt32, CFSocketNativeHandle, const CFSocketSignature*, CFReadStreamRef*, CFWriteStreamRef*); +typedef CFErrorRef (*cf_net_StreamError)(CFAllocatorRef, CFStreamError*); +typedef CFStreamError (*cf_net_FromCFError)(CFErrorRef); + +static struct { + CFSpinLock_t lock; + UInt32 flags; + void (*_CFSocketStreamCreatePair)(CFAllocatorRef, CFStringRef, UInt32, CFSocketNativeHandle, const CFSocketSignature*, CFReadStreamRef*, CFWriteStreamRef*); + CFErrorRef (*_CFErrorCreateWithStreamError)(CFAllocatorRef, CFStreamError*); + CFStreamError (*_CFStreamErrorFromCFError)(CFErrorRef); +} CFNetworkSupport = { + CFSpinLockInit, + 0x0, + NULL, + NULL, + NULL +}; + +#define CFNETWORK_CALL(sym, args) ((CFNetworkSupport.sym)args) + +#if DEPLOYMENT_TARGET_MACOSX +#define CFNETWORK_LOAD_SYM(sym) __CFLookupCFNetworkFunction(#sym) +//#elif DEPLOYMENT_TARGET_WINDOWS +//#define CFNETWORK_LOAD_SYM(sym) (void *)GetProcAddress(CFNetworkSupport.image, #sym) +#endif + +static void initializeCFNetworkSupport(void) { + __CFBitSet(CFNetworkSupport.flags, kTriedToLoad); + +#if DEPLOYMENT_TARGET_MACOSX + CFNetworkSupport._CFSocketStreamCreatePair = (cf_net_CreatePair)CFNETWORK_LOAD_SYM(_CFSocketStreamCreatePair); + CFNetworkSupport._CFErrorCreateWithStreamError = (cf_net_StreamError)CFNETWORK_LOAD_SYM(_CFErrorCreateWithStreamError); + CFNetworkSupport._CFStreamErrorFromCFError = (cf_net_FromCFError)CFNETWORK_LOAD_SYM(_CFStreamErrorFromCFError); +#endif + + if (!CFNetworkSupport._CFSocketStreamCreatePair) CFLog(__kCFLogAssertion, CFSTR("CoreFoundation: failed to dynamically link symbol _CFSocketStreamCreatePair")); + if (!CFNetworkSupport._CFErrorCreateWithStreamError) CFLog(__kCFLogAssertion, CFSTR("CoreFoundation: failed to dynamically link symbol _CFErrorCreateWithStreamError")); + if (!CFNetworkSupport._CFStreamErrorFromCFError) CFLog(__kCFLogAssertion, CFSTR("CoreFoundation: failed to dynamically link symbol _CFStreamErrorFromCFError")); + + __CFBitSet(CFNetworkSupport.flags, kInitialized); +} + +static void +createPair(CFAllocatorRef alloc, CFStringRef host, UInt32 port, CFSocketNativeHandle sock, const CFSocketSignature* sig, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream) +{ + if (readStream) + *readStream = NULL; + + if (writeStream) + *writeStream = NULL; + + __CFSpinLock(&(CFNetworkSupport.lock)); + if (!__CFBitIsSet(CFNetworkSupport.flags, kTriedToLoad)) initializeCFNetworkSupport(); + __CFSpinUnlock(&(CFNetworkSupport.lock)); + + CFNETWORK_CALL(_CFSocketStreamCreatePair, (alloc, host, port, sock, sig, readStream, writeStream)); +} + + +extern void CFStreamCreatePairWithSocket(CFAllocatorRef alloc, CFSocketNativeHandle sock, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream) { + createPair(alloc, NULL, 0, sock, NULL, readStream, writeStream); +} + +extern void CFStreamCreatePairWithSocketToHost(CFAllocatorRef alloc, CFStringRef host, UInt32 port, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream) { + createPair(alloc, host, port, 0, NULL, readStream, writeStream); +} + +extern void CFStreamCreatePairWithPeerSocketSignature(CFAllocatorRef alloc, const CFSocketSignature* sig, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream) { + createPair(alloc, NULL, 0, 0, sig, readStream, writeStream); +} + +__private_extern__ CFStreamError _CFStreamErrorFromError(CFErrorRef error) { + CFStreamError result; + Boolean canUpCall; + + __CFSpinLock(&(CFNetworkSupport.lock)); + if (!__CFBitIsSet(CFNetworkSupport.flags, kTriedToLoad)) initializeCFNetworkSupport(); + canUpCall = (CFNetworkSupport._CFStreamErrorFromCFError != NULL); + __CFSpinUnlock(&(CFNetworkSupport.lock)); + + if (canUpCall) { + result = CFNETWORK_CALL(_CFStreamErrorFromCFError, (error)); + } else { + CFStringRef domain = CFErrorGetDomain(error); + if (CFEqual(domain, kCFErrorDomainPOSIX)) { + result.domain = kCFStreamErrorDomainPOSIX; + } else if (CFEqual(domain, kCFErrorDomainOSStatus)) { + result.domain = kCFStreamErrorDomainMacOSStatus; + } else if (CFEqual(domain, kCFErrorDomainMach)) { + result.domain = 11; // kCFStreamErrorDomainMach, but that symbol is in CFNetwork + } else { + result.domain = kCFStreamErrorDomainCustom; + } + result.error = CFErrorGetCode(error); + } + return result; +} + +__private_extern__ CFErrorRef _CFErrorFromStreamError(CFAllocatorRef alloc, CFStreamError *streamError) { + CFErrorRef result; + Boolean canUpCall; + + __CFSpinLock(&(CFNetworkSupport.lock)); + if (!__CFBitIsSet(CFNetworkSupport.flags, kTriedToLoad)) initializeCFNetworkSupport(); + canUpCall = (CFNetworkSupport._CFErrorCreateWithStreamError != NULL); + __CFSpinUnlock(&(CFNetworkSupport.lock)); + + if (canUpCall) { + result = CFNETWORK_CALL(_CFErrorCreateWithStreamError, (alloc, streamError)); + } else { + if (streamError->domain == kCFStreamErrorDomainPOSIX) { + return CFErrorCreate(alloc, kCFErrorDomainPOSIX, streamError->error, NULL); + } else if (streamError->domain == kCFStreamErrorDomainMacOSStatus) { + return CFErrorCreate(alloc, kCFErrorDomainOSStatus, streamError->error, NULL); + } else { + CFStringRef key = CFSTR("CFStreamErrorDomainKey"); + CFNumberRef value = CFNumberCreate(alloc, kCFNumberCFIndexType, &streamError->domain); + CFDictionaryRef dict = CFDictionaryCreate(alloc, (const void **)(&key), (const void **)(&value), 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + result = CFErrorCreate(alloc, CFSTR("BogusCFStreamErrorCompatibilityDomain"), streamError->error, dict); + CFRelease(value); + CFRelease(dict); + } + } + return result; +}
diff --git a/CoreFoundation/CFSortFunctions.c b/CoreFoundation/CFSortFunctions.c new file mode 100644 index 0000000..4a7e8d9 --- /dev/null +++ b/CoreFoundation/CFSortFunctions.c
@@ -0,0 +1,1035 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFSortFunctions.c + Copyright 1999-2007, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +/* This file contains code copied from the Libc project's files + qsort.c and merge.c, and modified to suit the + needs of CF, which needs the comparison callback to have an + additional parameter. The code is collected into this one + file so that the individual BSD sort routines can remain + private and static. +*/ + +#include <CoreFoundation/CFBase.h> +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD +#include <sys/types.h> +#else +#define EINVAL 22 +#endif +#include "CFInternal.h" + +#include <stdio.h> + +typedef CFComparisonResult (*Comparison_Func)(const void *, const void *, void *); + +/* stdlib.subproj/qsort.c ============================================== */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)qsort.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/lib/libc/stdlib/qsort.c,v 1.12 2002/09/10 02:04:49 wollman Exp $"); +#endif + +#include <stdlib.h> + +#ifdef I_AM_QSORT_R +typedef int cmp_t(void *, const void *, const void *); +#else +typedef CFComparisonResult cmp_t(const void *, const void *, void *); +#endif +static inline char *med3(char *, char *, char *, cmp_t *, void *); +static inline void swapfunc(char *, char *, long, long); + +#if !defined(min) +#define min(a, b) (a) < (b) ? a : b +#endif +/* + * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function". + */ +#define swapcode(TYPE, parmi, parmj, n) { \ + long i = (n) / sizeof (TYPE); \ + TYPE *pi = (TYPE *) (parmi); \ + TYPE *pj = (TYPE *) (parmj); \ + do { \ + TYPE t = *pi; \ + *pi++ = *pj; \ + *pj++ = t; \ + } while (--i > 0); \ +} + +#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \ + es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1; + +static inline void +swapfunc(char *a, char *b, long n, long swaptype) +{ + if(swaptype <= 1) + swapcode(long, a, b, n) + else + swapcode(char, a, b, n) +} + +#define swap(a, b) \ + if (swaptype == 0) { \ + long t = *(long *)(a); \ + *(long *)(a) = *(long *)(b); \ + *(long *)(b) = t; \ + } else \ + swapfunc(a, b, es, swaptype) + +#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype) + +#ifdef I_AM_QSORT_R +#define CMP(t, x, y) (cmp((t), (x), (y))) +#else +#define CMP(t, x, y) (cmp((x), (y), (t))) +#endif + +static inline char * +med3(char *a, char *b, char *c, cmp_t *cmp, void *thunk) +{ + return CMP(thunk, a, b) < 0 ? + (CMP(thunk, b, c) < 0 ? b : (CMP(thunk, a, c) < 0 ? c : a )) + :(CMP(thunk, b, c) > 0 ? b : (CMP(thunk, a, c) < 0 ? a : c )); +} + +#ifdef I_AM_QSORT_R +void +qsort_r(void *a, size_t n, size_t es, void *thunk, cmp_t *cmp) +#else +static void +bsd_qsort(void *a, size_t n, size_t es, cmp_t *cmp, void *thunk) +#endif +{ + char *pa, *pb, *pc, *pd, *pl, *pm, *pn; + long d, r, swaptype, swap_cnt; + +loop: SWAPINIT(a, es); + swap_cnt = 0; + if (n < 7) { + for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) + for (pl = pm; + pl > (char *)a && CMP(thunk, pl - es, pl) > 0; + pl -= es) + swap(pl, pl - es); + return; + } + pm = (char *)a + (n / 2) * es; + if (n > 7) { + pl = (char *)a; + pn = (char *)a + (n - 1) * es; + if (n > 40) { + d = (n / 8) * es; + pl = med3(pl, pl + d, pl + 2 * d, cmp, thunk); + pm = med3(pm - d, pm, pm + d, cmp, thunk); + pn = med3(pn - 2 * d, pn - d, pn, cmp, thunk); + } + pm = med3(pl, pm, pn, cmp, thunk); + } + swap((char *)a, pm); + pa = pb = (char *)a + es; + + pc = pd = (char *)a + (n - 1) * es; + for (;;) { + while (pb <= pc && (r = CMP(thunk, pb, a)) <= 0) { + if (r == 0) { + swap_cnt = 1; + swap(pa, pb); + pa += es; + } + pb += es; + } + while (pb <= pc && (r = CMP(thunk, pc, a)) >= 0) { + if (r == 0) { + swap_cnt = 1; + swap(pc, pd); + pd -= es; + } + pc -= es; + } + if (pb > pc) + break; + swap(pb, pc); + swap_cnt = 1; + pb += es; + pc -= es; + } + if (swap_cnt == 0) { /* Switch to insertion sort */ + for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) + for (pl = pm; + pl > (char *)a && CMP(thunk, pl - es, pl) > 0; + pl -= es) + swap(pl, pl - es); + return; + } + + pn = (char *)a + n * es; + r = min(pa - (char *)a, pb - pa); + vecswap((char *)a, pb - r, r); + r = min(pd - pc, pn - pd - es); + vecswap(pb, pn - r, r); + if ((r = pb - pa) > es) +#ifdef I_AM_QSORT_R + qsort_r(a, r / es, es, thunk, cmp); +#else + bsd_qsort(a, r / es, es, cmp, thunk); +#endif + if ((r = pd - pc) > es) { + /* Iterate rather than recurse to save stack space */ + a = pn - r; + n = r / es; + goto loop; + } +/* qsort(pn - r, r / es, es, cmp);*/ +} + + +/* And now for something not so completely different, a copy/paste version that uses write-barriers so as to notify GC as necessary of changes */ +#define ASSIGN __CFObjCStrongAssign +//#define ASSIGN objc_assign_strongCast + +#define swapcode_wb(TYPE, parmi, parmj, n) { \ + long i = (n) / sizeof (TYPE); \ + TYPE *pi = (TYPE *) (parmi); \ + TYPE *pj = (TYPE *) (parmj); \ + do { \ + TYPE t = *pi; \ + ASSIGN(*pj, pi++); \ + ASSIGN(t, pj++); \ + } while (--i > 0); \ +} + + +static inline void +swapfunc_wb(char *a, char *b, long n, long swaptype) +{ + if(swaptype <= 1) + swapcode_wb(const void *, a, b, n) + else + swapcode(char, a, b, n) +} + +#define swap_wb(a, b) \ + if (swaptype == 0) { \ + const void *t = *(const void **)(a); \ + ASSIGN(*(void **)(b), (const void **)a); \ + ASSIGN(t, (const void **)(b)); \ + } else \ + printf("bad things happening\n"); + //swapfunc_wb(a, b, es, swaptype) + +#define vecswap_wb(a, b, n) if ((n) > 0) swapfunc_wb(a, b, n, swaptype) + +static void +bsd_qsort_wb(void *a, size_t n, size_t es, cmp_t *cmp, void *thunk) +{ + char *pa, *pb, *pc, *pd, *pl, *pm, *pn; + long d, r, swaptype, swap_cnt; + +loop: SWAPINIT(a, es); + swap_cnt = 0; + if (n < 7) { + for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) + for (pl = pm; + pl > (char *)a && CMP(thunk, pl - es, pl) > 0; + pl -= es) + swap_wb(pl, pl - es); + return; + } + pm = (char *)a + (n / 2) * es; + if (n > 7) { + pl = (char *)a; + pn = (char *)a + (n - 1) * es; + if (n > 40) { + d = (n / 8) * es; + pl = med3(pl, pl + d, pl + 2 * d, cmp, thunk); + pm = med3(pm - d, pm, pm + d, cmp, thunk); + pn = med3(pn - 2 * d, pn - d, pn, cmp, thunk); + } + pm = med3(pl, pm, pn, cmp, thunk); + } + swap_wb(a, pm); + pa = pb = (char *)a + es; + + pc = pd = (char *)a + (n - 1) * es; + for (;;) { + while (pb <= pc && (r = CMP(thunk, pb, a)) <= 0) { + if (r == 0) { + swap_cnt = 1; + swap_wb(pa, pb); + pa += es; + } + pb += es; + } + while (pb <= pc && (r = CMP(thunk, pc, a)) >= 0) { + if (r == 0) { + swap_cnt = 1; + swap_wb(pc, pd); + pd -= es; + } + pc -= es; + } + if (pb > pc) + break; + swap_wb(pb, pc); + swap_cnt = 1; + pb += es; + pc -= es; + } + if (swap_cnt == 0) { /* Switch to insertion sort */ + for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) + for (pl = pm; + pl > (char *)a && CMP(thunk, pl - es, pl) > 0; + pl -= es) + swap_wb(pl, pl - es); + return; + } + + pn = (char *)a + n * es; + r = min(pa - (char *)a, pb - pa); + vecswap_wb((char *)a, pb - r, r); + r = min(pd - pc, pn - pd - es); + vecswap_wb(pb, pn - r, r); + if ((r = pb - pa) > es) + bsd_qsort_wb(a, r / es, es, cmp, thunk); + if ((r = pd - pc) > es) { + /* Iterate rather than recurse to save stack space */ + a = pn - r; + n = r / es; + goto loop; + } +/* qsort(pn - r, r / es, es, cmp);*/ +} + +/* Comparator is passed the address of the values. */ +void CFQSortArray(void *list, CFIndex count, CFIndex elementSize, CFComparatorFunction comparator, void *context) { + if (CF_USING_COLLECTABLE_MEMORY && (auto_zone_get_layout_type(__CFCollectableZone, list) & AUTO_UNSCANNED) == 0) + bsd_qsort_wb(list, count, elementSize, comparator, context); + else + bsd_qsort(list, count, elementSize, comparator, context); +} + +#undef thunk +#undef CMP +#undef vecswap +//#undef swap +#undef SWAPINIT +#undef swapcode +#undef min + +/* stdlib.subproj/mergesort.c ========================================== */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Peter McIlroy. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)merge.c 8.2 (Berkeley) 2/14/94"; +#endif /* LIBC_SCCS and not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/lib/libc/stdlib/merge.c,v 1.6 2002/03/21 22:48:42 obrien Exp $"); +#endif + +/* + * Hybrid exponential search/linear search merge sort with hybrid + * natural/pairwise first pass. Requires about .3% more comparisons + * for random data than LSMS with pairwise first pass alone. + * It works for objects as small as two bytes. + */ + +#define NATURAL +#define THRESHOLD 16 /* Best choice for natural merge cut-off. */ + +/* #define NATURAL to get hybrid natural merge. + * (The default is pairwise merging.) + */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +static void setup(u_char *, u_char *, size_t, size_t, Comparison_Func, void *); +static void insertionsort(u_char *, size_t, size_t, Comparison_Func, void *); + +#define ISIZE sizeof(long) +#define PSIZE sizeof(u_char *) +#define ICOPY_LIST(src, dst, last) \ + do \ + *(long*)dst = *(long*)src, src += ISIZE, dst += ISIZE; \ + while(src < last) +#define ICOPY_ELT(src, dst, i) \ + do \ + *(long*) dst = *(long*) src, src += ISIZE, dst += ISIZE; \ + while (i -= ISIZE) + +#define CCOPY_LIST(src, dst, last) \ + do \ + *dst++ = *src++; \ + while (src < last) +#define CCOPY_ELT(src, dst, i) \ + do \ + *dst++ = *src++; \ + while (i -= 1) + +/* + * Find the next possible pointer head. (Trickery for forcing an array + * to do double duty as a linked list when objects do not align with word + * boundaries. + */ +/* Assumption: PSIZE is a power of 2. */ +#define EVAL(p) (u_char **) \ + ((u_char *)0 + \ + (((u_char *)p + PSIZE - 1 - (u_char *) 0) & ~(PSIZE - 1))) + +/* + * Arguments are as for qsort. + */ +static int +bsd_mergesort(void *base, size_t nmemb, size_t size, Comparison_Func cmp, void *context) +{ + long i, sense; + long big, iflag; + u_char *f1, *f2, *t, *b, *tp2, *q, *l1, *l2; + u_char *list2, *list1, *p2, *p, *last, **p1; + + if (size < PSIZE / 2) { /* Pointers must fit into 2 * size. */ + errno = EINVAL; + return (-1); + } + + if (nmemb == 0) + return (0); + + /* + * XXX + * Stupid subtraction for the Cray. + */ + iflag = 0; + if (!(size % ISIZE) && !(((char *)base - (char *)0) % ISIZE)) + iflag = 1; + + if ((list2 = (u_char*)malloc(nmemb * size + PSIZE)) == NULL) + return (-1); + + list1 = (u_char*)base; + setup(list1, list2, nmemb, size, cmp, context); + last = list2 + nmemb * size; + i = big = 0; + while (*EVAL(list2) != last) { + l2 = list1; + p1 = EVAL(list1); + for (tp2 = p2 = list2; p2 != last; p1 = EVAL(l2)) { + p2 = *EVAL(p2); + f1 = l2; + f2 = l1 = list1 + (p2 - list2); + if (p2 != last) + p2 = *EVAL(p2); + l2 = list1 + (p2 - list2); + while (f1 < l1 && f2 < l2) { + if ((*cmp)(f1, f2, context) <= 0) { + q = f2; + b = f1, t = l1; + sense = -1; + } else { + q = f1; + b = f2, t = l2; + sense = 0; + } + if (!big) { /* here i = 0 */ + while ((b += size) < t && cmp(q, b, context) >sense) + if (++i == 6) { + big = 1; + goto EXPONENTIAL; + } + } else { +EXPONENTIAL: for (i = size; ; i <<= 1) + if ((p = (b + i)) >= t) { + if ((p = t - size) > b && + (*cmp)(q, p, context) <= sense) + t = p; + else + b = p; + break; + } else if ((*cmp)(q, p, context) <= sense) { + t = p; + if (i == size) + big = 0; + goto FASTCASE; + } else + b = p; + while (t > b+size) { + i = (((t - b) / size) >> 1) * size; + if ((*cmp)(q, p = b + i, context) <= sense) + t = p; + else + b = p; + } + goto COPY; +FASTCASE: while (i > size) + if ((*cmp)(q, + p = b + (i >>= 1), context) <= sense) + t = p; + else + b = p; +COPY: b = t; + } + i = size; + if (q == f1) { + if (iflag) { + ICOPY_LIST(f2, tp2, b); + ICOPY_ELT(f1, tp2, i); + } else { + CCOPY_LIST(f2, tp2, b); + CCOPY_ELT(f1, tp2, i); + } + } else { + if (iflag) { + ICOPY_LIST(f1, tp2, b); + ICOPY_ELT(f2, tp2, i); + } else { + CCOPY_LIST(f1, tp2, b); + CCOPY_ELT(f2, tp2, i); + } + } + } + if (f2 < l2) { + if (iflag) + ICOPY_LIST(f2, tp2, l2); + else + CCOPY_LIST(f2, tp2, l2); + } else if (f1 < l1) { + if (iflag) + ICOPY_LIST(f1, tp2, l1); + else + CCOPY_LIST(f1, tp2, l1); + } + *p1 = l2; + } + tp2 = list1; /* swap list1, list2 */ + list1 = list2; + list2 = tp2; + last = list2 + nmemb*size; + } + if (base == list2) { + memmove(list2, list1, nmemb*size); + list2 = list1; + } + free(list2); + return (0); +} + +#undef swap +#define swap(a, b) { \ + s = b; \ + i = size; \ + do { \ + tmp = *a; *a++ = *s; *s++ = tmp; \ + } while (--i); \ + a -= size; \ + } +#define reverse(bot, top) { \ + s = top; \ + do { \ + i = size; \ + do { \ + tmp = *bot; *bot++ = *s; *s++ = tmp; \ + } while (--i); \ + s -= size2; \ + } while(bot < s); \ +} + +/* + * Optional hybrid natural/pairwise first pass. Eats up list1 in runs of + * increasing order, list2 in a corresponding linked list. Checks for runs + * when THRESHOLD/2 pairs compare with same sense. (Only used when NATURAL + * is defined. Otherwise simple pairwise merging is used.) + */ +static void +setup(u_char *list1, u_char *list2, size_t n, size_t size, Comparison_Func cmp, void *context) +{ + long i, length, size2, tmp, sense; + u_char *f1, *f2, *s, *l2, *last, *p2; + + size2 = size*2; + if (n <= 5) { + insertionsort(list1, n, size, cmp, context); + *EVAL(list2) = (u_char*) list2 + n*size; + return; + } + /* + * Avoid running pointers out of bounds; limit n to evens + * for simplicity. + */ + i = 4 + (n & 1); + insertionsort(list1 + (n - i) * size, i, size, cmp, context); + last = list1 + size * (n - i); + *EVAL(list2 + (last - list1)) = list2 + n * size; + +#ifdef NATURAL + p2 = list2; + f1 = list1; + sense = (cmp(f1, f1 + size, context) > 0); + for (; f1 < last; sense = !sense) { + length = 2; + /* Find pairs with same sense. */ + for (f2 = f1 + size2; f2 < last; f2 += size2) { + if ((cmp(f2, f2+ size, context) > 0) != sense) + break; + length += 2; + } + if (length < THRESHOLD) { /* Pairwise merge */ + do { + p2 = *EVAL(p2) = f1 + size2 - list1 + list2; + if (sense > 0) + swap (f1, f1 + size); + } while ((f1 += size2) < f2); + } else { /* Natural merge */ + l2 = f2; + for (f2 = f1 + size2; f2 < l2; f2 += size2) { + if ((cmp(f2-size, f2, context) > 0) != sense) { + p2 = *EVAL(p2) = f2 - list1 + list2; + if (sense > 0) + reverse(f1, f2-size); + f1 = f2; + } + } + if (sense > 0) + reverse (f1, f2-size); + f1 = f2; + if (f2 < last || cmp(f2 - size, f2, context) > 0) + p2 = *EVAL(p2) = f2 - list1 + list2; + else + p2 = *EVAL(p2) = list2 + n*size; + } + } +#else /* pairwise merge only. */ + for (f1 = list1, p2 = list2; f1 < last; f1 += size2) { + p2 = *EVAL(p2) = p2 + size2; + if (cmp (f1, f1 + size, context) > 0) + swap(f1, f1 + size); + } +#endif /* NATURAL */ +} + +/* + * This is to avoid out-of-bounds addresses in sorting the + * last 4 elements. + */ +static void +insertionsort(u_char *a, size_t n, size_t size, Comparison_Func cmp, void *context) +{ + u_char *ai, *s, *t, *u, tmp; + long i; + + for (ai = a+size; --n >= 1; ai += size) + for (t = ai; t > a; t -= size) { + u = t - size; + if (cmp(u, t, context) <= 0) + break; + swap(u, t); + } +} + +/* Another version, also not so completely different, in order to handle necessary write-barriers in the face of GC */ + +#undef ASSIGN +#define ASSIGN __CFObjCStrongAssign +//#define ASSIGN log_assign + +static void setup_wb(u_char *, u_char *, size_t, size_t, Comparison_Func, void *); +static void insertionsort_wb(u_char *, size_t, size_t, Comparison_Func, void *); + +#undef ICOPY_ELT + +#define ICOPY_ELT(src, dst, i) \ + do \ + ASSIGN(*(const void**)src, (const void **)dst), src += ISIZE, dst += ISIZE; \ + while (i -= ISIZE) + +#undef ICOPY_LIST + +#define ICOPY_LIST(src, dst, last) \ + do \ + ASSIGN(*(const void **)src, (const void **)dst), src += ISIZE, dst += ISIZE; \ + while (src < last) + + +/* + * Arguments are as for qsort. + */ +static int +bsd_mergesort_wb(void *base, size_t nmemb, size_t size, Comparison_Func cmp, void *context) +{ + long i, sense; + long big, iflag; + u_char *f1, *f2, *t, *b, *tp2, *q, *l1, *l2; + u_char *list2, *list1, *p2, *p, *last, **p1; + + if (size < PSIZE / 2) { /* Pointers must fit into 2 * size. */ + errno = EINVAL; + return (-1); + } + + if (nmemb == 0) + return (0); + + /* + * XXX + * Stupid subtraction for the Cray. + */ + iflag = 0; + if (!(size % ISIZE) && !(((char *)base - (char *)0) % ISIZE)) + iflag = 1; + + if (!iflag) + return -1; // only set up for "integer" swaps, e.g. long integer + + if ((list2 = (u_char*)CFAllocatorAllocate(NULL, (CFIndex)(nmemb * size + PSIZE), __kCFAllocatorGCScannedMemory)) == NULL) + return (-1); + + list1 = (u_char*)base; + setup_wb(list1, list2, nmemb, size, cmp, context); + last = list2 + nmemb * size; + i = big = 0; + while (*EVAL(list2) != last) { + l2 = list1; + p1 = EVAL(list1); + for (tp2 = p2 = list2; p2 != last; p1 = EVAL(l2)) { + p2 = *EVAL(p2); + f1 = l2; + f2 = l1 = list1 + (p2 - list2); + if (p2 != last) + p2 = *EVAL(p2); + l2 = list1 + (p2 - list2); + while (f1 < l1 && f2 < l2) { + if ((*cmp)(f1, f2, context) <= 0) { + q = f2; + b = f1, t = l1; + sense = -1; + } else { + q = f1; + b = f2, t = l2; + sense = 0; + } + if (!big) { /* here i = 0 */ + while ((b += size) < t && cmp(q, b, context) >sense) + if (++i == 6) { + big = 1; + goto EXPONENTIAL; + } + } else { +EXPONENTIAL: for (i = size; ; i <<= 1) + if ((p = (b + i)) >= t) { + if ((p = t - size) > b && + (*cmp)(q, p, context) <= sense) + t = p; + else + b = p; + break; + } else if ((*cmp)(q, p, context) <= sense) { + t = p; + if (i == size) + big = 0; + goto FASTCASE; + } else + b = p; + while (t > b+size) { + i = (((t - b) / size) >> 1) * size; + if ((*cmp)(q, p = b + i, context) <= sense) + t = p; + else + b = p; + } + goto COPY; +FASTCASE: while (i > size) + if ((*cmp)(q, + p = b + (i >>= 1), context) <= sense) + t = p; + else + b = p; +COPY: b = t; + } + i = size; + if (q == f1) { + if (iflag) { + ICOPY_LIST(f2, tp2, b); + ICOPY_ELT(f1, tp2, i); + } else { + CCOPY_LIST(f2, tp2, b); + CCOPY_ELT(f1, tp2, i); + } + } else { + if (iflag) { + ICOPY_LIST(f1, tp2, b); + ICOPY_ELT(f2, tp2, i); + } else { + CCOPY_LIST(f1, tp2, b); + CCOPY_ELT(f2, tp2, i); + } + } + } + if (f2 < l2) { + if (iflag) + ICOPY_LIST(f2, tp2, l2); + else + CCOPY_LIST(f2, tp2, l2); + } else if (f1 < l1) { + if (iflag) + ICOPY_LIST(f1, tp2, l1); + else + CCOPY_LIST(f1, tp2, l1); + } + *p1 = l2; + } + tp2 = list1; /* swap list1, list2 */ + list1 = list2; + list2 = tp2; + last = list2 + nmemb*size; + } + if (base == list2) { + CF_WRITE_BARRIER_MEMMOVE(list2, list1, nmemb*size); + list2 = list1; + } + free(list2); + return (0); +} + +#undef swap_wb +#define swap_wb(a, b) { \ + const void *object = *(void **)a; \ + ASSIGN(*(const void **)b, (const void **)a); \ + ASSIGN(object, (const void **)b); \ + } + +#define reverse_wb(bot, top) { \ + s = top; \ + do { \ + swap_wb(bot, s); \ + bot += size; \ + s -= size; \ + } while(bot < s); \ +} + +/* + * Optional hybrid natural/pairwise first pass. Eats up list1 in runs of + * increasing order, list2 in a corresponding linked list. Checks for runs + * when THRESHOLD/2 pairs compare with same sense. (Only used when NATURAL + * is defined. Otherwise simple pairwise merging is used.) + */ +static void +setup_wb(u_char *list1, u_char *list2, size_t n, size_t size, Comparison_Func cmp, void *context) +{ + long i, length, size2, tmp, sense; + u_char *f1, *f2, *s, *l2, *last, *p2; + + size2 = size*2; + if (n <= 5) { + insertionsort_wb(list1, n, size, cmp, context); + *EVAL(list2) = (u_char*) list2 + n*size; + return; + } + /* + * Avoid running pointers out of bounds; limit n to evens + * for simplicity. + */ + i = 4 + (n & 1); + insertionsort_wb(list1 + (n - i) * size, i, size, cmp, context); + last = list1 + size * (n - i); + *EVAL(list2 + (last - list1)) = list2 + n * size; + +#ifdef NATURAL + p2 = list2; + f1 = list1; + sense = (cmp(f1, f1 + size, context) > 0); + for (; f1 < last; sense = !sense) { + length = 2; + /* Find pairs with same sense. */ + for (f2 = f1 + size2; f2 < last; f2 += size2) { + if ((cmp(f2, f2+ size, context) > 0) != sense) + break; + length += 2; + } + if (length < THRESHOLD) { /* Pairwise merge */ + do { + p2 = *EVAL(p2) = f1 + size2 - list1 + list2; + if (sense > 0) + swap (f1, f1 + size); + } while ((f1 += size2) < f2); + } else { /* Natural merge */ + l2 = f2; + for (f2 = f1 + size2; f2 < l2; f2 += size2) { + if ((cmp(f2-size, f2, context) > 0) != sense) { + p2 = *EVAL(p2) = f2 - list1 + list2; + if (sense > 0) { + reverse_wb(f1, f2-size); + } + f1 = f2; + } + } + if (sense > 0) { + reverse_wb (f1, f2-size); + } + f1 = f2; + if (f2 < last || cmp(f2 - size, f2, context) > 0) { + p2 = *EVAL(p2) = f2 - list1 + list2; + } + else { + p2 = *EVAL(p2) = list2 + n*size; + } + } + } +#else /* pairwise merge only. */ +#error unchanged + for (f1 = list1, p2 = list2; f1 < last; f1 += size2) { + p2 = *EVAL(p2) = p2 + size2; + if (cmp (f1, f1 + size, context) > 0) + swap_wb(f1, f1 + size); + } +#endif /* NATURAL */ +} + +/* + * This is to avoid out-of-bounds addresses in sorting the + * last 4 elements. + */ +static void +insertionsort_wb(u_char *a, size_t n, size_t size, CFComparisonResult (*cmp)(const void *, const void *, void *), void *context) +{ + u_char *ai, *s, *t, *u, tmp; + long i; + + for (ai = a+size; --n >= 1; ai += size) + for (t = ai; t > a; t -= size) { + u = t - size; + if (cmp(u, t, context) <= 0) + break; + swap(u, t); + } +} + +void CFMergeSortArray(void *list, CFIndex count, CFIndex elementSize, CFComparatorFunction comparator, void *context) { + if (CF_USING_COLLECTABLE_MEMORY && (auto_zone_get_layout_type(__CFCollectableZone, list) & AUTO_UNSCANNED) == 0) + bsd_mergesort_wb(list, count, elementSize, comparator, context); + else + bsd_mergesort(list, count, elementSize, comparator, context); +} + +#undef NATURAL +#undef THRESHOLD +#undef ISIZE +#undef PSIZE +#undef ICOPY_LIST +#undef ICOPY_ELT +#undef CCOPY_LIST +#undef CCOPY_ELT +#undef EVAL +#undef swap +#undef reverse + +/* ===================================================================== */ + +#undef EINVAL +
diff --git a/CoreFoundation/CFStorage.c b/CoreFoundation/CFStorage.c new file mode 100644 index 0000000..2079cbe --- /dev/null +++ b/CoreFoundation/CFStorage.c
@@ -0,0 +1,786 @@ +/* + * 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@ + */ +/* CFStorage.c + Copyright 1999-2007, Apple, Inc. All rights reserved. + Responsibility: Ali Ozer +*/ + +/* +2-3 tree storing arbitrary sized values. + +??? Currently elementSize cannot be greater than storage->maxLeafCapacity, which is less than or equal to __CFStorageMaxLeafCapacity + +CFStorage is thread-safe for multiple readers, but not thread safe for simultaneous reading and writing. +*/ + +#include "CFStorage.h" +#include "CFInternal.h" + +#if !defined(PAGE_SIZE) +#define PAGE_SIZE 4096 +#endif + +// Above 15K, malloc switches (??? or used to in early Leopard) to using vm allocates for blocks; it seems best to avoid that. +// Also, tests with StorageTimer.c done in 4/07 indicate that 4096 * 3 is better than smaller or larger node sizes. +#define __CFStorageMaxLeafCapacity (4096 * 3) + +// If the max length of a node is less than can fit in a half-word (half of a long), we can get away without ever caching the high half-word the cachedRange +#if __LP64__ +#if __CFStorageMaxLeafCapacity > 0xFFFFFFFFULL +#define POSSIBLE_TO_HAVE_LENGTH_MORE_THAN_HALFWORD 1 +#endif +#else +#if __CFStorageMaxLeafCapacity > 0xFFFFUL +#define POSSIBLE_TO_HAVE_LENGTH_MORE_THAN_HALFWORD 1 +#endif +#endif + +#define COPYMEM(src,dst,n) CF_WRITE_BARRIER_MEMMOVE((dst), (src), (n)) +#define PAGE_LIMIT ((CFIndex)PAGE_SIZE / 2) + +CF_INLINE int32_t roundToPage(int32_t num) { + return (num + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); +} + + + +/* Each node in the storage. isLeaf determines whether the node is a leaf node or a node inside the tree. If latter, number of children are determined by the number of non-NULL entries in child[]. (NULL entries are at the end.) +*/ +typedef struct __CFStorageNode { + CFIndex numBytes; /* Number of actual bytes in this node and all its children */ + bool isLeaf; + union { + struct { + CFIndex capacityInBytes; // capacityInBytes is capacity of memory; this is either 0, or >= numBytes + uint8_t *memory; + } leaf; + struct { + struct __CFStorageNode *child[3]; + } notLeaf; + } info; +} CFStorageNode; + +/* This is the struct used to store the cache in the CFStorage; it enables us to make the cache thread-safe for multiple readers (which update the cache). The values in this cache store half the desired value in the top half, and the genCount of the writer in the low half. This cache is consistent only if all of the genCounts are the same. Note that the cache does not provide thread-safety for readers in the presence of a writer. + +The cached range (location, length) is in terms of values; if the cached range is not (0,0), then the cached node needs to be non-NULL and pointing at a leaf node. +*/ +typedef struct { + unsigned long locationHi, locationLo; // cachedRange.location +#if POSSIBLE_TO_HAVE_LENGTH_MORE_THAN_HALFWORD + unsigned long lengthHi; +#endif + unsigned long lengthLo; // cachedRange.length; note that we actually do not bother with lengthHi if __CFStorageMaxLeafCapacity is less than half-word + unsigned long cachedNodeHi, cachedNodeLo; // cachedNode +} CFStorageAccessCacheParts; + +/* The CFStorage object. +*/ +struct __CFStorage { + CFRuntimeBase base; + CFIndex valueSize; + CFSpinLock_t cacheReaderMemoryAllocationLock; + int cacheGenerationCount; + CFStorageAccessCacheParts cacheParts; + CFIndex maxLeafCapacity; // In terms of bytes + CFStorageNode rootNode; + CFOptionFlags nodeHint; // __kCFAllocatorGCScannedMemory or 0. +}; + + + + +/* Allocates the memory and initializes the capacity in a leaf. __CFStorageAllocLeafNodeMemory() is the entry point; __CFStorageAllocLeafNodeMemoryAux is called if actual reallocation is needed. __CFStorageAllocLeafNodeMemoryAux() locks not for mutations (mutations are not thread-safe in general), but for lazy allocation of storage during reading. +*/ +static void __CFStorageAllocLeafNodeMemoryAux(CFAllocatorRef allocator, CFStorageRef storage, CFStorageNode *node, CFIndex cap) { + __CFSpinLock(&(storage->cacheReaderMemoryAllocationLock)); + CF_WRITE_BARRIER_ASSIGN(allocator, node->info.leaf.memory, _CFAllocatorReallocateGC(allocator, node->info.leaf.memory, cap, storage->nodeHint)); // This will free... + if (__CFOASafe) __CFSetLastAllocationEventName(node->info.leaf.memory, "CFStorage (node bytes)"); + node->info.leaf.capacityInBytes = cap; + __CFSpinUnlock(&(storage->cacheReaderMemoryAllocationLock)); +} + +CF_INLINE void __CFStorageAllocLeafNodeMemory(CFAllocatorRef allocator, CFStorageRef storage, CFStorageNode *node, CFIndex cap, bool compact) { + if (cap > PAGE_LIMIT) { + cap = roundToPage(cap); + if (cap > storage->maxLeafCapacity) cap = storage->maxLeafCapacity; + } else { + cap = (((cap + 63) / 64) * 64); + } + if (compact ? (cap != node->info.leaf.capacityInBytes) : (cap > node->info.leaf.capacityInBytes)) __CFStorageAllocLeafNodeMemoryAux(allocator, storage, node, cap); +} + + + + +#if __LP64__ +#define genCountMask 0x00000000FFFFFFFFUL +#define dataMask 0xFFFFFFFF00000000UL +#define shiftLowWordBy 32 +#else +#define genCountMask 0x0000FFFFUL +#define dataMask 0xFFFF0000UL +#define shiftLowWordBy 16 +#endif + +/* Sets the cache to point at the specified node. loc and len are in terms of values, not bytes. To clear the cache set these two to 0. + At least one of node or memory should be non-NULL. memory is consulted first when using the cache. +*/ +CF_INLINE void __CFStorageSetCache(CFStorageRef storage, CFStorageNode *node, CFIndex loc, CFIndex len) { + unsigned int genCount = ((unsigned int)_CFAtomicIncrement32(&storage->cacheGenerationCount)) & genCountMask; + CFStorageAccessCacheParts cacheParts; + cacheParts.locationHi = (loc & dataMask) | genCount; + cacheParts.locationLo = (loc << shiftLowWordBy) | genCount; +#if POSSIBLE_TO_HAVE_LENGTH_MORE_THAN_HALFWORD + cacheParts.lengthHi = (len & dataMask) | genCount; +#endif + cacheParts.lengthLo = (len << shiftLowWordBy) | genCount; + cacheParts.cachedNodeHi = ((unsigned long)node & dataMask) | genCount; + cacheParts.cachedNodeLo = ((unsigned long)node << shiftLowWordBy) | genCount; + storage->cacheParts = cacheParts; +} + +/* Thread-safely get the cached range and node info. +*/ +CF_INLINE Boolean __CFStorageGetCache(CFStorageRef storage, CFRange *cachedRangePtr, CFStorageNode **cachedNodePtr) { + CFStorageAccessCacheParts cacheParts = storage->cacheParts; + + unsigned int genCount = cacheParts.locationHi & genCountMask; + + // Check to make sure the genCounts of all the items are the same; if not, the cache was inconsistent + if ((cacheParts.locationLo & genCountMask) == genCount && +#if POSSIBLE_TO_HAVE_LENGTH_MORE_THAN_HALFWORD + (cacheParts.lengthHi & genCountMask) == genCount && +#endif + (cacheParts.lengthLo & genCountMask) == genCount && + (cacheParts.cachedNodeHi & genCountMask) == genCount && + (cacheParts.cachedNodeLo & genCountMask) == genCount) { + + *cachedNodePtr = (CFStorageNode *)((cacheParts.cachedNodeHi & dataMask) | ((cacheParts.cachedNodeLo & dataMask) >> shiftLowWordBy)); + cachedRangePtr->location = (cacheParts.locationHi & dataMask) | ((cacheParts.locationLo & dataMask) >> shiftLowWordBy); + cachedRangePtr->length = (cacheParts.lengthLo & dataMask) >> shiftLowWordBy; +#if POSSIBLE_TO_HAVE_LENGTH_MORE_THAN_HALFWORD + cachedRangePtr->length |= (cacheParts.lengthHi & dataMask); +#endif + + return true; + } + return false; +} + +/* Gets the location for the specified absolute loc from the cached info. + Returns NULL if the location is not in the cache. +*/ +CF_INLINE uint8_t *__CFStorageGetFromCache(CFStorageRef storage, CFIndex loc, CFRange *validConsecutiveValueRange) { + CFRange cachedRange = {0, 0}; + CFStorageNode *cachedNode = 0; + + // If we can't get values from the cache, return NULL + if (!__CFStorageGetCache(storage, &cachedRange, &cachedNode)) return NULL; + + // Check to see if the index is in the cache + if (loc < cachedRange.location || loc >= cachedRange.location + cachedRange.length) return NULL; + + // If the cached node has no memory, return here; it will be allocated as a result of the non-cached lookup. + if (!cachedNode->info.leaf.memory) return NULL; + + // The cache has consistent values, and in fact, the values we're looking for! + uint8_t *result = cachedNode->info.leaf.memory + (loc - cachedRange.location) * storage->valueSize; + *validConsecutiveValueRange = cachedRange; + + return result; +} + + + + +/* Returns the number of the child containing the desired value and the relative index of the value in that child. + forInsertion = true means that we are looking for the child in which to insert; this changes the behavior when the index is at the end of a child + relativeByteNum (not optional, for performance reasons) returns the relative byte number of the specified byte in the child. + Don't call with leaf nodes! +*/ +CF_INLINE void __CFStorageFindChild(CFStorageNode *node, CFIndex byteNum, bool forInsertion, CFIndex *childNum, CFIndex *relativeByteNum) { + if (forInsertion) byteNum--; /* If for insertion, we do <= checks, not <, so this accomplishes the same thing */ + if (byteNum < node->info.notLeaf.child[0]->numBytes) *childNum = 0; + else { + byteNum -= node->info.notLeaf.child[0]->numBytes; + if (byteNum < node->info.notLeaf.child[1]->numBytes) *childNum = 1; + else { + byteNum -= node->info.notLeaf.child[1]->numBytes; + *childNum = 2; + } + } + if (forInsertion) byteNum++; + *relativeByteNum = byteNum; +} + +/* Finds the location where the specified byte is stored. If validConsecutiveByteRange is not NULL, returns + the range of bytes that are consecutive with this one. + !!! Assumes the byteNum is within the range of this node. +*/ +static void *__CFStorageFindByte(CFStorageRef storage, CFStorageNode *node, CFIndex byteNum, CFStorageNode **resultNode, CFRange *validConsecutiveByteRange) { + if (node->isLeaf) { + if (validConsecutiveByteRange) *validConsecutiveByteRange = CFRangeMake(0, node->numBytes); + __CFStorageAllocLeafNodeMemory(CFGetAllocator(storage), storage, node, node->numBytes, false); + if (resultNode) *resultNode = node; + return node->info.leaf.memory + byteNum; + } else { + void *result; + CFIndex childNum; + CFIndex relativeByteNum; + __CFStorageFindChild(node, byteNum, false, &childNum, &relativeByteNum); + result = __CFStorageFindByte(storage, node->info.notLeaf.child[childNum], relativeByteNum, resultNode, validConsecutiveByteRange); + if (validConsecutiveByteRange) { + if (childNum > 0) validConsecutiveByteRange->location += node->info.notLeaf.child[0]->numBytes; + if (childNum > 1) validConsecutiveByteRange->location += node->info.notLeaf.child[1]->numBytes; + } + return result; + } +} + +/* Guts of CFStorageGetValueAtIndex(); note that validConsecutiveValueRange is not optional. + Consults and updates cache. +*/ +CF_INLINE void *__CFStorageGetValueAtIndex(CFStorageRef storage, CFIndex idx, CFRange *validConsecutiveValueRange) { + uint8_t *result; + if (!(result = __CFStorageGetFromCache(storage, idx, validConsecutiveValueRange))) { + CFRange rangeInBytes; + CFStorageNode *resultNode; + result = (uint8_t *)__CFStorageFindByte(storage, &storage->rootNode, idx * storage->valueSize, &resultNode, &rangeInBytes); + CFRange rangeInValues = CFRangeMake(rangeInBytes.location / storage->valueSize, rangeInBytes.length / storage->valueSize); + __CFStorageSetCache(storage, resultNode, rangeInValues.location, rangeInValues.length); + *validConsecutiveValueRange = rangeInValues; + } + return result; +} + +static CFStorageNode *__CFStorageCreateNode(CFAllocatorRef allocator, bool isLeaf, CFIndex numBytes) { + CFStorageNode *newNode = (CFStorageNode *)_CFAllocatorAllocateGC(allocator, sizeof(CFStorageNode), __kCFAllocatorGCScannedMemory); + if (__CFOASafe) __CFSetLastAllocationEventName(newNode, "CFStorage (node)"); + newNode->isLeaf = isLeaf; + newNode->numBytes = numBytes; + if (isLeaf) { + newNode->info.leaf.capacityInBytes = 0; + newNode->info.leaf.memory = NULL; + } else { + newNode->info.notLeaf.child[0] = newNode->info.notLeaf.child[1] = newNode->info.notLeaf.child[2] = NULL; + } + return newNode; +} + +static void __CFStorageNodeDealloc(CFAllocatorRef allocator, CFStorageNode *node, bool freeNodeItself) { + if (node->isLeaf) { + _CFAllocatorDeallocateGC(allocator, node->info.leaf.memory); + } else { + int cnt; + for (cnt = 0; cnt < 3; cnt++) if (node->info.notLeaf.child[cnt]) __CFStorageNodeDealloc(allocator, node->info.notLeaf.child[cnt], true); + } + if (freeNodeItself) _CFAllocatorDeallocateGC(allocator, node); +} + +static CFIndex __CFStorageGetNumChildren(CFStorageNode *node) { + if (!node || node->isLeaf) return 0; + if (node->info.notLeaf.child[2]) return 3; + if (node->info.notLeaf.child[1]) return 2; + if (node->info.notLeaf.child[0]) return 1; + return 0; +} + +/* The boolean compact indicates whether leaf nodes that get smaller should be realloced. +*/ +static void __CFStorageDelete(CFAllocatorRef allocator, CFStorageRef storage, CFStorageNode *node, CFRange range, bool compact) { + if (node->isLeaf) { + node->numBytes -= range.length; + // If this node had memory allocated, readjust the bytes... + if (node->info.leaf.memory) { + COPYMEM(node->info.leaf.memory + range.location + range.length, node->info.leaf.memory + range.location, node->numBytes - range.location); + if (compact) __CFStorageAllocLeafNodeMemory(allocator, storage, node, node->numBytes, true); + } + } else { + bool childrenAreLeaves = node->info.notLeaf.child[0]->isLeaf; + node->numBytes -= range.length; + while (range.length > 0) { + CFRange rangeToDelete; + CFIndex relativeByteNum; + CFIndex childNum; + __CFStorageFindChild(node, range.location + range.length, true, &childNum, &relativeByteNum); + if (range.length > relativeByteNum) { + rangeToDelete.length = relativeByteNum; + rangeToDelete.location = 0; + } else { + rangeToDelete.length = range.length; + rangeToDelete.location = relativeByteNum - range.length; + } + __CFStorageDelete(allocator, storage, node->info.notLeaf.child[childNum], rangeToDelete, compact); + if (node->info.notLeaf.child[childNum]->numBytes == 0) { // Delete empty node and compact + int cnt; + _CFAllocatorDeallocateGC(allocator, node->info.notLeaf.child[childNum]); + for (cnt = childNum; cnt < 2; cnt++) { + CF_WRITE_BARRIER_ASSIGN(allocator, node->info.notLeaf.child[cnt], node->info.notLeaf.child[cnt+1]); + } + node->info.notLeaf.child[2] = NULL; + } + range.length -= rangeToDelete.length; + } + // At this point the remaining children are packed + if (childrenAreLeaves) { + // Children are leaves; if their total bytes is smaller than a leaf's worth, collapse into one... + if (node->numBytes > 0 && node->numBytes <= storage->maxLeafCapacity) { + __CFStorageAllocLeafNodeMemory(allocator, storage, node->info.notLeaf.child[0], node->numBytes, false); + if (node->info.notLeaf.child[1] && node->info.notLeaf.child[1]->numBytes) { + COPYMEM(node->info.notLeaf.child[1]->info.leaf.memory, node->info.notLeaf.child[0]->info.leaf.memory + node->info.notLeaf.child[0]->numBytes, node->info.notLeaf.child[1]->numBytes); + if (node->info.notLeaf.child[2] && node->info.notLeaf.child[2]->numBytes) { + COPYMEM(node->info.notLeaf.child[2]->info.leaf.memory, node->info.notLeaf.child[0]->info.leaf.memory + node->info.notLeaf.child[0]->numBytes + node->info.notLeaf.child[1]->numBytes, node->info.notLeaf.child[2]->numBytes); + __CFStorageNodeDealloc(allocator, node->info.notLeaf.child[2], true); + node->info.notLeaf.child[2] = NULL; + } + __CFStorageNodeDealloc(allocator, node->info.notLeaf.child[1], true); + node->info.notLeaf.child[1] = NULL; + } + node->info.notLeaf.child[0]->numBytes = node->numBytes; + } + } else { + // Children are not leaves; combine their children to assure each node has 2 or 3 children... + // (Could try to bypass all this by noting up above whether the number of grandchildren changed...) + CFStorageNode *gChildren[9]; + CFIndex cCnt, gCnt, cnt; + CFIndex totalG = 0; // Total number of grandchildren + for (cCnt = 0; cCnt < 3; cCnt++) { + CFStorageNode *child = node->info.notLeaf.child[cCnt]; + if (child) { + for (gCnt = 0; gCnt < 3; gCnt++) if (child->info.notLeaf.child[gCnt]) { + gChildren[totalG++] = child->info.notLeaf.child[gCnt]; + child->info.notLeaf.child[gCnt] = NULL; + } + child->numBytes = 0; + } + } + gCnt = 0; // Total number of grandchildren placed + for (cCnt = 0; cCnt < 3; cCnt++) { + // These tables indicate how many children each child should have, given the total number of grandchildren (last child gets remainder) + static const unsigned char forChild0[10] = {0, 1, 2, 3, 2, 3, 3, 3, 3, 3}; + static const unsigned char forChild1[10] = {0, 0, 0, 0, 2, 2, 3, 2, 3, 3}; + // sCnt is the number of grandchildren to be placed into child cCnt + // Depending on child number, pick the right number + CFIndex sCnt = (cCnt == 0) ? forChild0[totalG] : ((cCnt == 1) ? forChild1[totalG] : totalG); + // Assure we have that many grandchildren... + if (sCnt > totalG - gCnt) sCnt = totalG - gCnt; + if (sCnt) { + if (!node->info.notLeaf.child[cCnt]) { + CFStorageNode *newNode = __CFStorageCreateNode(allocator, false, 0); + CF_WRITE_BARRIER_ASSIGN(allocator, node->info.notLeaf.child[cCnt], newNode); + } + for (cnt = 0; cnt < sCnt; cnt++) { + node->info.notLeaf.child[cCnt]->numBytes += gChildren[gCnt]->numBytes; + CF_WRITE_BARRIER_ASSIGN(allocator, node->info.notLeaf.child[cCnt]->info.notLeaf.child[cnt], gChildren[gCnt++]); + } + } else { + if (node->info.notLeaf.child[cCnt]) { + _CFAllocatorDeallocateGC(allocator, node->info.notLeaf.child[cCnt]); + node->info.notLeaf.child[cCnt] = NULL; + } + } + } + } + } +} + + +/* Returns NULL or additional node to come after this node + Assumption: size is never > storage->maxLeafCapacity +*/ +static CFStorageNode *__CFStorageInsert(CFAllocatorRef allocator, CFStorageRef storage, CFStorageNode *node, CFIndex byteNum, CFIndex size, CFIndex absoluteByteNum) { + if (node->isLeaf) { + if (size + node->numBytes > storage->maxLeafCapacity) { // Need to create more child nodes + if (byteNum == node->numBytes) { // Inserting at end; easy... + CFStorageNode *newNode = __CFStorageCreateNode(allocator, true, size); + __CFStorageSetCache(storage, newNode, absoluteByteNum / storage->valueSize, size / storage->valueSize); + return newNode; + } else if (byteNum == 0) { // Inserting at front; also easy, but we need to swap node and newNode + CFStorageNode *newNode = __CFStorageCreateNode(allocator, true, 0); + CF_WRITE_BARRIER_MEMMOVE(newNode, node, sizeof(CFStorageNode)); + node->isLeaf = true; + node->numBytes = size; + node->info.leaf.capacityInBytes = 0; + node->info.leaf.memory = NULL; + __CFStorageSetCache(storage, node, absoluteByteNum / storage->valueSize, size / storage->valueSize); + return newNode; + } else if (byteNum + size <= storage->maxLeafCapacity) { // Inserting at middle; inserted region will fit into existing child + // Create new node to hold the overflow + CFStorageNode *newNode = __CFStorageCreateNode(allocator, true, node->numBytes - byteNum); + if (node->info.leaf.memory) { // We allocate memory lazily... + __CFStorageAllocLeafNodeMemory(allocator, storage, newNode, node->numBytes - byteNum, false); + COPYMEM(node->info.leaf.memory + byteNum, newNode->info.leaf.memory, node->numBytes - byteNum); + __CFStorageAllocLeafNodeMemory(allocator, storage, node, byteNum + size, false); + } + node->numBytes = byteNum + size; + __CFStorageSetCache(storage, node, (absoluteByteNum - byteNum) / storage->valueSize, node->numBytes / storage->valueSize); + return newNode; + } else { // Inserting some of new into one node, rest into another; remember that the assumption is size <= storage->maxLeafCapacity + CFStorageNode *newNode = __CFStorageCreateNode(allocator, true, node->numBytes + size - storage->maxLeafCapacity); // New stuff + if (node->info.leaf.memory) { // We allocate memory lazily... + __CFStorageAllocLeafNodeMemory(allocator, storage, newNode, node->numBytes + size - storage->maxLeafCapacity, false); + COPYMEM(node->info.leaf.memory + byteNum, newNode->info.leaf.memory + byteNum + size - storage->maxLeafCapacity, node->numBytes - byteNum); + __CFStorageAllocLeafNodeMemory(allocator, storage, node, storage->maxLeafCapacity, false); + } + node->numBytes = storage->maxLeafCapacity; + __CFStorageSetCache(storage, node, (absoluteByteNum - byteNum) / storage->valueSize, node->numBytes / storage->valueSize); + return newNode; + } + } else { // No need to create new nodes! + if (node->info.leaf.memory) { + __CFStorageAllocLeafNodeMemory(allocator, storage, node, node->numBytes + size, false); + COPYMEM(node->info.leaf.memory + byteNum, node->info.leaf.memory + byteNum + size, node->numBytes - byteNum); + } + node->numBytes += size; + __CFStorageSetCache(storage, node, (absoluteByteNum - byteNum) / storage->valueSize, node->numBytes / storage->valueSize); + return NULL; + } + } else { + CFIndex relativeByteNum; + CFIndex childNum; + CFStorageNode *newNode; + __CFStorageFindChild(node, byteNum, true, &childNum, &relativeByteNum); + newNode = __CFStorageInsert(allocator, storage, node->info.notLeaf.child[childNum], relativeByteNum, size, absoluteByteNum); + if (newNode) { + if (node->info.notLeaf.child[2] == NULL) { // There's an empty slot for the new node, cool + if (childNum == 0) CF_WRITE_BARRIER_ASSIGN(allocator, node->info.notLeaf.child[2], node->info.notLeaf.child[1]); // Make room + CF_WRITE_BARRIER_ASSIGN(allocator, node->info.notLeaf.child[childNum + 1], newNode); + node->numBytes += size; + return NULL; + } else { + CFStorageNode *anotherNode = __CFStorageCreateNode(allocator, false, 0); // Create another node + if (childNum == 0) { // Last two children go to new node + CF_WRITE_BARRIER_ASSIGN(allocator, anotherNode->info.notLeaf.child[0], node->info.notLeaf.child[1]); + CF_WRITE_BARRIER_ASSIGN(allocator, anotherNode->info.notLeaf.child[1], node->info.notLeaf.child[2]); + CF_WRITE_BARRIER_ASSIGN(allocator, node->info.notLeaf.child[1], newNode); + node->info.notLeaf.child[2] = NULL; + } else if (childNum == 1) { // Last child goes to new node + CF_WRITE_BARRIER_ASSIGN(allocator, anotherNode->info.notLeaf.child[0], newNode); + CF_WRITE_BARRIER_ASSIGN(allocator, anotherNode->info.notLeaf.child[1], node->info.notLeaf.child[2]); + node->info.notLeaf.child[2] = NULL; + } else { // New node contains the new comers... + CF_WRITE_BARRIER_ASSIGN(allocator, anotherNode->info.notLeaf.child[0], node->info.notLeaf.child[2]); + CF_WRITE_BARRIER_ASSIGN(allocator, anotherNode->info.notLeaf.child[1], newNode); + node->info.notLeaf.child[2] = NULL; + } + node->numBytes = node->info.notLeaf.child[0]->numBytes + node->info.notLeaf.child[1]->numBytes; + anotherNode->numBytes = anotherNode->info.notLeaf.child[0]->numBytes + anotherNode->info.notLeaf.child[1]->numBytes; + return anotherNode; + } + } else { + node->numBytes += size; + } + } + return NULL; +} + +CF_INLINE CFIndex __CFStorageGetCount(CFStorageRef storage) { + return storage->rootNode.numBytes / storage->valueSize; +} + +static Boolean __CFStorageEqual(CFTypeRef cf1, CFTypeRef cf2) { + CFStorageRef storage1 = (CFStorageRef)cf1; + CFStorageRef storage2 = (CFStorageRef)cf2; + CFIndex loc, count, valueSize; + CFRange range1, range2; + uint8_t *ptr1, *ptr2; + + count = __CFStorageGetCount(storage1); + if (count != __CFStorageGetCount(storage2)) return false; + + valueSize = __CFStorageGetValueSize(storage1); + if (valueSize != __CFStorageGetValueSize(storage2)) return false; + + loc = range1.location = range1.length = range2.location = range2.length = 0; + ptr1 = ptr2 = NULL; + + while (loc < count) { + CFIndex cntThisTime; + if (loc >= range1.location + range1.length) ptr1 = (uint8_t *)CFStorageGetValueAtIndex(storage1, loc, &range1); + if (loc >= range2.location + range2.length) ptr2 = (uint8_t *)CFStorageGetValueAtIndex(storage2, loc, &range2); + cntThisTime = range1.location + range1.length; + if (range2.location + range2.length < cntThisTime) cntThisTime = range2.location + range2.length; + cntThisTime -= loc; + if (memcmp(ptr1, ptr2, valueSize * cntThisTime) != 0) return false; + ptr1 += valueSize * cntThisTime; + ptr2 += valueSize * cntThisTime; + loc += cntThisTime; + } + return true; +} + +static CFHashCode __CFStorageHash(CFTypeRef cf) { + CFStorageRef Storage = (CFStorageRef)cf; + return __CFStorageGetCount(Storage); +} + +static void __CFStorageDescribeNode(CFStorageNode *node, CFMutableStringRef str, CFIndex level) { + int cnt; + for (cnt = 0; cnt < level; cnt++) CFStringAppendCString(str, " ", CFStringGetSystemEncoding()); + + if (node->isLeaf) { + CFStringAppendFormat(str, NULL, CFSTR("Leaf %d/%d\n"), node->numBytes, node->info.leaf.capacityInBytes); + } else { + CFStringAppendFormat(str, NULL, CFSTR("Node %d\n"), node->numBytes); + for (cnt = 0; cnt < 3; cnt++) if (node->info.notLeaf.child[cnt]) __CFStorageDescribeNode(node->info.notLeaf.child[cnt], str, level+1); + } +} + +static CFIndex __CFStorageGetNodeCapacity(CFStorageNode *node) { + if (!node) return 0; + if (node->isLeaf) return node->info.leaf.capacityInBytes; + return __CFStorageGetNodeCapacity(node->info.notLeaf.child[0]) + __CFStorageGetNodeCapacity(node->info.notLeaf.child[1]) + __CFStorageGetNodeCapacity(node->info.notLeaf.child[2]); +} + +CFIndex __CFStorageGetCapacity(CFStorageRef storage) { + return __CFStorageGetNodeCapacity(&storage->rootNode) / storage->valueSize; +} + +CFIndex __CFStorageGetValueSize(CFStorageRef storage) { + return storage->valueSize; +} + +static CFStringRef __CFStorageCopyDescription(CFTypeRef cf) { + CFStorageRef storage = (CFStorageRef)cf; + CFMutableStringRef result; + CFAllocatorRef allocator = CFGetAllocator(storage); + result = CFStringCreateMutable(allocator, 0); + CFStringAppendFormat(result, NULL, CFSTR("<CFStorage %p [%p]>[count = %u, capacity = %u]\n"), storage, allocator, __CFStorageGetCount(storage), __CFStorageGetCapacity(storage)); + __CFStorageDescribeNode(&storage->rootNode, result, 0); + return result; +} + +static void __CFStorageDeallocate(CFTypeRef cf) { + CFStorageRef storage = (CFStorageRef)cf; + CFAllocatorRef allocator = CFGetAllocator(storage); + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) return; // XXX_PCB GC will take care of us. + __CFStorageNodeDealloc(allocator, &storage->rootNode, false); +} + +static CFTypeID __kCFStorageTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFStorageClass = { + _kCFRuntimeScannedObject, + "CFStorage", + NULL, // init + NULL, // copy + __CFStorageDeallocate, + __CFStorageEqual, + __CFStorageHash, + NULL, // + __CFStorageCopyDescription +}; + +__private_extern__ void __CFStorageInitialize(void) { + __kCFStorageTypeID = _CFRuntimeRegisterClass(&__CFStorageClass); +} + +/*** Public API ***/ + +CFStorageRef CFStorageCreate(CFAllocatorRef allocator, CFIndex valueSize) { + CFStorageRef storage; + CFIndex size = sizeof(struct __CFStorage) - sizeof(CFRuntimeBase); + storage = (CFStorageRef)_CFRuntimeCreateInstance(allocator, __kCFStorageTypeID, size, NULL); + if (NULL == storage) { + return NULL; + } + storage->valueSize = valueSize; + CF_SPINLOCK_INIT_FOR_STRUCTS(storage->cacheReaderMemoryAllocationLock); + storage->cacheGenerationCount = 0; + storage->cacheParts.locationHi = 0; + storage->cacheParts.locationLo = 0; +#if POSSIBLE_TO_HAVE_LENGTH_MORE_THAN_HALFWORD + storage->cacheParts.lengthHi = 0; +#endif + storage->cacheParts.lengthLo = 0; + storage->cacheParts.cachedNodeHi = 0; + storage->cacheParts.cachedNodeLo = 0; + storage->maxLeafCapacity = __CFStorageMaxLeafCapacity; + if (valueSize && ((storage->maxLeafCapacity % valueSize) != 0)) { + storage->maxLeafCapacity = (storage->maxLeafCapacity / valueSize) * valueSize; // Make it fit perfectly (3406853) + } + memset(&(storage->rootNode), 0, sizeof(CFStorageNode)); + storage->rootNode.isLeaf = true; + storage->nodeHint = __kCFAllocatorGCScannedMemory; + if (__CFOASafe) __CFSetLastAllocationEventName(storage, "CFStorage"); + return storage; +} + +CFTypeID CFStorageGetTypeID(void) { + return __kCFStorageTypeID; +} + +CFIndex CFStorageGetCount(CFStorageRef storage) { + return __CFStorageGetCount(storage); +} + +/* Returns pointer to the specified value + index and validConsecutiveValueRange are in terms of values +*/ +void *CFStorageGetValueAtIndex(CFStorageRef storage, CFIndex idx, CFRange *validConsecutiveValueRange) { + CFRange range; + return __CFStorageGetValueAtIndex(storage, idx, validConsecutiveValueRange ? validConsecutiveValueRange : &range); +} + +/* Makes space for range.length values at location range.location + This function deepens the tree if necessary... +*/ +void CFStorageInsertValues(CFStorageRef storage, CFRange range) { + CFIndex numBytesToInsert = range.length * storage->valueSize; + CFIndex byteNum = range.location * storage->valueSize; + while (numBytesToInsert > 0) { + CFStorageNode *newNode; + CFAllocatorRef allocator = CFGetAllocator(storage); + CFIndex insertThisTime = numBytesToInsert; + if (insertThisTime > storage->maxLeafCapacity) { + insertThisTime = (storage->maxLeafCapacity / storage->valueSize) * storage->valueSize; + } + newNode = __CFStorageInsert(allocator, storage, &storage->rootNode, byteNum, insertThisTime, byteNum); + if (newNode) { + CFStorageNode *tempRootNode = __CFStorageCreateNode(allocator, false, 0); // Will copy the (static) rootNode over to this + CF_WRITE_BARRIER_MEMMOVE(tempRootNode, &storage->rootNode, sizeof(CFStorageNode)); + storage->rootNode.isLeaf = false; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, storage, storage->rootNode.info.notLeaf.child[0], tempRootNode); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, storage, storage->rootNode.info.notLeaf.child[1], newNode); + storage->rootNode.info.notLeaf.child[2] = NULL; + storage->rootNode.numBytes = tempRootNode->numBytes + newNode->numBytes; +#if 1 + // ??? + __CFStorageSetCache(storage, NULL, 0, 0); +#else + if (storage->cache.cachedNode == &(storage->rootNode)) CF_WRITE_BARRIER_BASE_ASSIGN(allocator, storage, storage->cache.cachedNode, tempRootNode); // The cache should follow the node +#endif + } + numBytesToInsert -= insertThisTime; + byteNum += insertThisTime; + } +} + +/* Deletes the values in the specified range + This function gets rid of levels if necessary... +*/ +void CFStorageDeleteValues(CFStorageRef storage, CFRange range) { + CFAllocatorRef allocator = CFGetAllocator(storage); + range.location *= storage->valueSize; + range.length *= storage->valueSize; + __CFStorageDelete(allocator, storage, &storage->rootNode, range, true); + while (__CFStorageGetNumChildren(&storage->rootNode) == 1) { + CFStorageNode *child = storage->rootNode.info.notLeaf.child[0]; // The single child + CF_WRITE_BARRIER_MEMMOVE(&storage->rootNode, child, sizeof(CFStorageNode)); + _CFAllocatorDeallocateGC(allocator, child); + } + if (__CFStorageGetNumChildren(&storage->rootNode) == 0 && !storage->rootNode.isLeaf) { + storage->rootNode.isLeaf = true; + storage->rootNode.info.leaf.capacityInBytes = 0; + storage->rootNode.info.leaf.memory = NULL; + } + // !!! Need to update the cache + __CFStorageSetCache(storage, NULL, 0, 0); +} + +void CFStorageGetValues(CFStorageRef storage, CFRange range, void *values) { + while (range.length > 0) { + CFRange leafRange; + void *storagePtr = __CFStorageGetValueAtIndex(storage, range.location, &leafRange); + CFIndex cntThisTime = range.length; + if (cntThisTime > leafRange.length - (range.location - leafRange.location)) cntThisTime = leafRange.length - (range.location - leafRange.location); + COPYMEM(storagePtr, values, cntThisTime * storage->valueSize); + values = (uint8_t *)values + (cntThisTime * storage->valueSize); + range.location += cntThisTime; + range.length -= cntThisTime; + } +} + +unsigned long _CFStorageFastEnumeration(CFStorageRef storage, struct __objcFastEnumerationStateEquivalent *state, void *stackbuffer, unsigned long count) { + // without trying to understand the data structure, each time through search for block containing index + CFRange leafRange; + if (state->state == 0) { /* first time, get length */ + state->extra[0] = __CFStorageGetCount(storage); + } + if (state->state >= state->extra[0]) return 0; + state->itemsPtr = (unsigned long *)CFStorageGetValueAtIndex(storage, state->state, &leafRange); + state->state += leafRange.length; + return leafRange.length; +} + +void CFStorageApplyFunction(CFStorageRef storage, CFRange range, CFStorageApplierFunction applier, void *context) { + while (0 < range.length) { + CFRange leafRange; + const void *storagePtr; + CFIndex idx, cnt; + storagePtr = CFStorageGetValueAtIndex(storage, range.location, &leafRange); + cnt = __CFMin(range.length, leafRange.location + leafRange.length - range.location); + for (idx = 0; idx < cnt; idx++) { + applier(storagePtr, context); + storagePtr = (const char *)storagePtr + storage->valueSize; + } + range.length -= cnt; + range.location += cnt; + } +} + +void CFStorageReplaceValues(CFStorageRef storage, CFRange range, const void *values) { + while (range.length > 0) { + CFRange leafRange; + void *storagePtr = __CFStorageGetValueAtIndex(storage, range.location, &leafRange); + CFIndex cntThisTime = range.length; + if (cntThisTime > leafRange.length - (range.location - leafRange.location)) cntThisTime = leafRange.length - (range.location - leafRange.location); + COPYMEM(values, storagePtr, cntThisTime * storage->valueSize); + values = (const uint8_t *)values + (cntThisTime * storage->valueSize); + range.location += cntThisTime; + range.length -= cntThisTime; + } +} + +/* Used by CFArray.c */ + +static void __CFStorageNodeSetLayoutType(CFStorageNode *node, auto_zone_t *zone, auto_memory_type_t type) { + if (node->isLeaf) { + auto_zone_set_layout_type(zone, node->info.leaf.memory, type); + } else { + CFStorageNode **children = node->info.notLeaf.child; + if (children[0]) __CFStorageNodeSetLayoutType(children[0], zone, type); + if (children[1]) __CFStorageNodeSetLayoutType(children[1], zone, type); + if (children[2]) __CFStorageNodeSetLayoutType(children[2], zone, type); + } +} + +__private_extern__ void _CFStorageSetWeak(CFStorageRef storage) { + storage->nodeHint = 0; + __CFStorageNodeSetLayoutType(&storage->rootNode, __CFCollectableZone, CF_GET_GC_MEMORY_TYPE(storage->nodeHint)); +} + +#undef COPYMEM +#undef PAGE_LIMIT +
diff --git a/CoreFoundation/CFStorage.h b/CoreFoundation/CFStorage.h new file mode 100644 index 0000000..77b4e05 --- /dev/null +++ b/CoreFoundation/CFStorage.h
@@ -0,0 +1,240 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFStorage.h + Copyright (c) 1999-2007, Apple Inc. All rights reserved. +*/ +/*! + @header CFStorage +CFStorage stores an array of arbitrary-sized values. There are no callbacks; +all that is provided about the values is the size, and the appropriate number +of bytes are copied in and out of the CFStorage. + +CFStorage uses a balanced tree to store the values, and is most appropriate +for situations where potentially a large number values (more than a hundred +bytes' worth) will be stored and there will be a lot of editing (insertions and deletions). + +Getting to an item is O(log n), although caching the last result often reduces this +to a constant time. + +The overhead of CFStorage is 48 bytes. There is no per item overhead; the +non-leaf nodes in the tree cost 20 bytes each, and the worst case extra +capacity (unused space in the leaves) is 12%, typically much less. + +Because CFStorage does not necessarily use a single block of memory to store the values, +when you ask for a value, you get back the pointer to the value and optionally +the range of other values that are consecutive and thus reachable as if the +storage was a single block. +*/ + +#if !defined(__COREFOUNDATION_CFSTORAGE__) +#define __COREFOUNDATION_CFSTORAGE__ 1 + +#include <CoreFoundation/CFBase.h> + +CF_EXTERN_C_BEGIN + +/*! + @typedef CFStorageRef + This is the type of a reference to a CFStorage instance. +*/ +typedef struct __CFStorage *CFStorageRef; + +/*! + @typedef CFStorageApplierFunction + Type of the callback function used by the apply functions of + CFStorage. + @param value The current value from the storage. + @param context The user-defined context parameter given to the apply + function. +*/ +typedef void (*CFStorageApplierFunction)(const void *val, void *context); + +/*! + @function CFStorageGetTypeID + Returns the type identifier of all CFStorage instances. +*/ +CF_EXPORT CFTypeID CFStorageGetTypeID(void); + +/*! + @function CFStorageCreate + Creates a new mutable storage with elements of the given size. + @param alloc The CFAllocator which should be used to allocate + memory for the set and its storage for values. This + parameter may be NULL in which case the current default + CFAllocator is used. If this reference is not a valid + CFAllocator, the behavior is undefined. + @param valueSizeInBytes The size in bytes of each of the elements + to be stored in the storage. If this value is zero or + negative, the result is undefined. + @result A reference to the new CFStorage instance. +*/ +CF_EXPORT CFStorageRef CFStorageCreate(CFAllocatorRef alloc, CFIndex valueSizeInBytes); + +/*! + @function CFStorageInsertValues + Allocates space for range.length values at location range.location. Use + CFStorageReplaceValues() to set those values. + @param storage The storage to which the values are to be inserted. + If this parameter is not a valid CFStorage, the behavior is undefined. + @param range The range of values within the storage to delete. If the + range location or end point (defined by the location plus + length minus 1) are outside the index space of the storage (0 + to N inclusive, where N is the count of the storage), the + behavior is undefined. If the range length is negative, the + behavior is undefined. The range may be empty (length 0), + in which case the no values are inserted. +*/ +CF_EXPORT void CFStorageInsertValues(CFStorageRef storage, CFRange range); + +/*! + @function CFStorageDeleteValues + Deletes the values of the storage in the specified range. + @param storage The storage from which the values are to be deleted. + If this parameter is not a valid CFStorage, the behavior is undefined. + @param range The range of values within the storage to delete. If the + range location or end point (defined by the location plus + length minus 1) are outside the index space of the storage (0 + to N inclusive, where N is the count of the storage), the + behavior is undefined. If the range length is negative, the + behavior is undefined. The range may be empty (length 0), + in which case the no values are deleted. +*/ +CF_EXPORT void CFStorageDeleteValues(CFStorageRef storage, CFRange range); + +/*! + @function CFStorageGetCount + Returns the number of values currently in the storage. + @param storage The storage to be queried. If this parameter is not a valid + CFStorage, the behavior is undefined. + @result The number of values in the storage. +*/ +CF_EXPORT CFIndex CFStorageGetCount(CFStorageRef storage); + +/*! + @function CFStorageGetValueAtIndex + Returns a pointer to the specified value. The pointer is mutable and may be used to + get or set the value. + @param storage The storage to be queried. If this parameter is not a + valid CFStorage, the behavior is undefined. + @param idx The index of the value to retrieve. If the index is + outside the index space of the storage (0 to N-1 inclusive, + where N is the count of the storage), the behavior is + undefined. + @param validConsecutiveValueRange This parameter is a C pointer to a CFRange. + If NULL is specified, this argument is ignored; otherwise, the range + is set to the range of values that may be accessed via an offset from the result pointer. + The range location is set to the index of the lowest consecutive + value and the range length is set to the count of consecutive values. + @result The value with the given index in the storage. +*/ +CF_EXPORT void *CFStorageGetValueAtIndex(CFStorageRef storage, CFIndex idx, CFRange *validConsecutiveValueRange); + +/*! + @function CFStorageGetValues + Fills the buffer with values from the storage. + @param storage The storage to be queried. If this parameter is not a + valid CFStorage, the behavior is undefined. + @param range The range of values within the storage to retrieve. If + the range location or end point (defined by the location + plus length minus 1) are outside the index space of the + storage (0 to N-1 inclusive, where N is the count of the + storage), the behavior is undefined. If the range length is + negative, the behavior is undefined. The range may be empty + (length 0), in which case no values are put into the buffer. + @param values A C array of to be filled with values from the storage. + The values in the C array are ordered + in the same order in which they appear in the storage. If this + parameter is not a valid pointer to a C array of at least + range.length pointers, the behavior is undefined. +*/ +CF_EXPORT void CFStorageGetValues(CFStorageRef storage, CFRange range, void *values); + +/*! + @function CFStorageApplyFunction + Calls a function once for each value in the set. + @param storage The storage to be operated upon. If this parameter is not + a valid CFStorage, the behavior is undefined. + @param range The range of values within the storage to operate on. If the + range location or end point (defined by the location plus + length minus 1) are outside the index space of the storage (0 + to N inclusive, where N is the count of the storage), the + behavior is undefined. If the range length is negative, the + behavior is undefined. The range may be empty (length 0), + in which case the no values are operated on. + @param applier The callback function to call once for each value in + the given storage. If this parameter is not a + pointer to a function of the correct prototype, the behavior + is undefined. If there are values in the storage which the + applier function does not expect or cannot properly apply + to, the behavior is undefined. + @param context A pointer-sized user-defined value, which is passed + as the second parameter to the applier function, but is + otherwise unused by this function. If the context is not + what is expected by the applier function, the behavior is + undefined. +*/ +CF_EXPORT void CFStorageApplyFunction(CFStorageRef storage, CFRange range, CFStorageApplierFunction applier, void *context); + +/*! + @function CFStorageReplaceValues + Replaces a range of values in the storage. + @param storage The storage from which the specified values are to be + removed. If this parameter is not a valid CFStorage, + the behavior is undefined. + @param range The range of values within the storage to replace. If the + range location or end point (defined by the location plus + length minus 1) are outside the index space of the storage (0 + to N inclusive, where N is the count of the storage), the + behavior is undefined. If the range length is negative, the + behavior is undefined. The range may be empty (length 0), + in which case the new values are merely inserted at the + range location. + @param values A C array of the values to be copied into the storage. + The new values in the storage are ordered in the same order + in which they appear in this C array. This parameter may be NULL + if the range length is 0. This C array is not changed or freed by + this function. If this parameter is not a valid pointer to a C array of at least + range length pointers, the behavior is undefined. +*/ +CF_EXPORT void CFStorageReplaceValues(CFStorageRef storage, CFRange range, const void *values); + +/* Private stuff... +*/ +CF_EXPORT CFIndex __CFStorageGetCapacity(CFStorageRef storage); +CF_EXPORT CFIndex __CFStorageGetValueSize(CFStorageRef storage); + + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFSTORAGE__ */ +
diff --git a/CoreFoundation/CFStream.c b/CoreFoundation/CFStream.c new file mode 100644 index 0000000..1795a51 --- /dev/null +++ b/CoreFoundation/CFStream.c
@@ -0,0 +1,1566 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFStream.c + Copyright 2000-2002, Apple, Inc. All rights reserved. + Responsibility: Becky Willrich +*/ + +#include <CoreFoundation/CFRuntime.h> +#include <CoreFoundation/CFNumber.h> +#include <string.h> +#include "CFStreamInternal.h" +#include "CFInternal.h" +#include <stdio.h> + + +enum { + MIN_STATUS_CODE_BIT = 0, + // ..status bits... + MAX_STATUS_CODE_BIT = 4, + + CONSTANT_CALLBACKS = 5, + CALLING_CLIENT = 6, // MUST remain 6 since it's value is used elsewhere. + + HAVE_CLOSED = 7, + + // Values above used to be defined and others may rely on their values + + // Values below should not matter if they are re-ordered or shift + + SHARED_SOURCE +}; + + +/* CALLING_CLIENT really determines whether stream events will be sent to the client immediately, or posted for the next time through the runloop. Since the client may not be prepared for re-entrancy, we must always set/clear this bit around public entry points. -- REW, 9/5/2001 + Also, CALLING_CLIENT is now used from CFFilteredStream.c (which has a copy of the #define above). Really gross. We should find a way to avoid that.... -- REW, 3/27/2002 */ +// Used in CFNetwork too + +/* sSharesSources holds two mappings, one the inverse of the other, between a stream and the + RunLoop+RunLoopMode pair that it's scheduled in. If the stream is scheduled in more than + one loop or mode, it can't share RunLoopSources with others, and is not in this dict. +*/ +static CFSpinLock_t sSourceLock = CFSpinLockInit; +static CFMutableDictionaryRef sSharedSources = NULL; + +static CFTypeID __kCFReadStreamTypeID = _kCFRuntimeNotATypeID; +static CFTypeID __kCFWriteStreamTypeID = _kCFRuntimeNotATypeID; + +// Just reads the bits, for those cases where we don't want to go through any callback checking +#define __CFStreamGetStatus(x) __CFBitfieldGetValue((x)->flags, MAX_STATUS_CODE_BIT, MIN_STATUS_CODE_BIT) + +static void _CFStreamSignalEventSynch(void* info); +__private_extern__ CFStreamStatus _CFStreamGetStatus(struct _CFStream *stream); +static Boolean _CFStreamRemoveRunLoopAndModeFromArray(CFMutableArrayRef runLoopsAndModes, CFRunLoopRef rl, CFStringRef mode); +static void _wakeUpRunLoop(struct _CFStream *stream); + +CF_INLINE const struct _CFStreamCallBacks *_CFStreamGetCallBackPtr(struct _CFStream *stream) { + return stream->callBacks; +} + +CF_INLINE void _CFStreamSetStatusCode(struct _CFStream *stream, CFStreamStatus newStatus) { + CFStreamStatus status = __CFStreamGetStatus(stream); + if (((status != kCFStreamStatusClosed) && (status != kCFStreamStatusError)) || + ((status == kCFStreamStatusClosed) && (newStatus == kCFStreamStatusError))) + { + __CFBitfieldSetValue(stream->flags, MAX_STATUS_CODE_BIT, MIN_STATUS_CODE_BIT, newStatus); + } +} + +CF_INLINE void _CFStreamScheduleEvent(struct _CFStream *stream, CFStreamEventType event) { + if (stream->client && (stream->client->when & event) && stream->client->rlSource) { + stream->client->whatToSignal |= event; + CFRunLoopSourceSignal(stream->client->rlSource); + _wakeUpRunLoop(stream); + } +} + +CF_INLINE void _CFStreamSetStreamError(struct _CFStream *stream, CFStreamError *err) { + if (!stream->error) { + stream->error = (CFErrorRef)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(CFStreamError), 0); + } + memmove(stream->error, err, sizeof(CFStreamError)); +} + +static CFStringRef __CFStreamCopyDescription(CFTypeRef cf) { + struct _CFStream *stream = (struct _CFStream *)cf; + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + CFStringRef contextDescription; + CFStringRef desc; + if (cb->copyDescription) { + if (cb->version == 0) { + contextDescription = ((CFStringRef(*)(void *))cb->copyDescription)(_CFStreamGetInfoPointer(stream)); + } else { + contextDescription = cb->copyDescription(stream, _CFStreamGetInfoPointer(stream)); + } + } else { + contextDescription = CFStringCreateWithFormat(CFGetAllocator(stream), NULL, CFSTR("info = %p"), _CFStreamGetInfoPointer(stream)); + } + if (CFGetTypeID(cf) == __kCFReadStreamTypeID) { + desc = CFStringCreateWithFormat(CFGetAllocator(stream), NULL, CFSTR("<CFReadStream %p>{%@}"), stream, contextDescription); + } else { + desc = CFStringCreateWithFormat(CFGetAllocator(stream), NULL, CFSTR("<CFWriteStream %p>{%@}"), stream, contextDescription); + } + CFRelease(contextDescription); + return desc; +} + +__private_extern__ void _CFStreamClose(struct _CFStream *stream) { + CFStreamStatus status = _CFStreamGetStatus(stream); + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + if (status == kCFStreamStatusNotOpen || status == kCFStreamStatusClosed || (status == kCFStreamStatusError && __CFBitIsSet(stream->flags, HAVE_CLOSED))) { + // Stream is not open from the client's perspective; do not callout and do not update our status to "closed" + return; + } + __CFBitSet(stream->flags, HAVE_CLOSED); + __CFBitSet(stream->flags, CALLING_CLIENT); + if (cb->close) { + cb->close(stream, _CFStreamGetInfoPointer(stream)); + } + if (stream->client && stream->client->rlSource) { + + if (!__CFBitIsSet(stream->flags, SHARED_SOURCE)) { + CFRunLoopSourceInvalidate(stream->client->rlSource); + CFRelease(stream->client->rlSource); + stream->client->rlSource = NULL; + } + else { + + CFArrayRef key; + CFMutableArrayRef list; + CFIndex c, i; + + __CFSpinLock(&sSourceLock); + + key = (CFArrayRef)CFDictionaryGetValue(sSharedSources, stream); + list = (CFMutableArrayRef)CFDictionaryGetValue(sSharedSources, key); + + c = CFArrayGetCount(list); + i = CFArrayGetFirstIndexOfValue(list, CFRangeMake(0, c), stream); + if (i != kCFNotFound) { + CFArrayRemoveValueAtIndex(list, i); + c--; + } + + CFAssert(CFArrayGetFirstIndexOfValue(list, CFRangeMake(0, CFArrayGetCount(list)), stream) == kCFNotFound, __kCFLogAssertion, "CFStreamClose: stream found twice in its shared source's list"); + + if (!c) { + CFRunLoopRemoveSource((CFRunLoopRef)CFArrayGetValueAtIndex(key, 0), stream->client->rlSource, (CFStringRef)CFArrayGetValueAtIndex(key, 1)); + CFRunLoopSourceInvalidate(stream->client->rlSource); + CFDictionaryRemoveValue(sSharedSources, key); + } + + CFDictionaryRemoveValue(sSharedSources, stream); + + CFRelease(stream->client->rlSource); + stream->client->rlSource = NULL; + __CFBitClear(stream->flags, SHARED_SOURCE); + + __CFSpinUnlock(&sSourceLock); + } + } + _CFStreamSetStatusCode(stream, kCFStreamStatusClosed); + __CFBitClear(stream->flags, CALLING_CLIENT); +} + +//static int numStreamInstances = 0; + +static void __CFStreamDeallocate(CFTypeRef cf) { + struct _CFStream *stream = (struct _CFStream *)cf; + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + CFAllocatorRef alloc = CFGetAllocator(stream); +// numStreamInstances --; + + // Close the stream + _CFStreamClose(stream); + + if (stream->client) { + CFStreamClientContext *cbContext; + cbContext = &(stream->client->cbContext); + if (cbContext->info && cbContext->release) { + cbContext->release(cbContext->info); + } + if (stream->client->rlSource) { + if (!__CFBitIsSet(stream->flags, SHARED_SOURCE)) { + CFRunLoopSourceInvalidate(stream->client->rlSource); + CFRelease(stream->client->rlSource); + stream->client->rlSource = NULL; + } + else { + + CFArrayRef key; + CFMutableArrayRef list; + CFIndex c, i; + + __CFSpinLock(&sSourceLock); + + key = (CFArrayRef)CFDictionaryGetValue(sSharedSources, stream); + list = (CFMutableArrayRef)CFDictionaryGetValue(sSharedSources, key); + + c = CFArrayGetCount(list); + i = CFArrayGetFirstIndexOfValue(list, CFRangeMake(0, c), stream); + if (i != kCFNotFound) { + CFArrayRemoveValueAtIndex(list, i); + c--; + } + + if (!c) { + CFRunLoopRemoveSource((CFRunLoopRef)CFArrayGetValueAtIndex(key, 0), stream->client->rlSource, (CFStringRef)CFArrayGetValueAtIndex(key, 1)); + CFRunLoopSourceInvalidate(stream->client->rlSource); + CFDictionaryRemoveValue(sSharedSources, key); + } + + CFDictionaryRemoveValue(sSharedSources, stream); + + CFRelease(stream->client->rlSource); + stream->client->rlSource = NULL; + __CFBitClear(stream->flags, SHARED_SOURCE); + + __CFSpinUnlock(&sSourceLock); + } + } + if (stream->client->runLoopsAndModes) { + CFRelease(stream->client->runLoopsAndModes); + } + + CFAllocatorDeallocate(alloc, stream->client); + stream->client = NULL; // Just in case finalize, below, calls back in to us + } + if (cb->finalize) { + if (cb->version == 0) { + ((void(*)(void *))cb->finalize)(_CFStreamGetInfoPointer(stream)); + } else { + cb->finalize(stream, _CFStreamGetInfoPointer(stream)); + } + } + if (stream->error) { + if (cb->version < 2) { + CFAllocatorDeallocate(alloc, stream->error); + } else { + CFRelease(stream->error); + } + } + if (!__CFBitIsSet(stream->flags, CONSTANT_CALLBACKS)) { + CFAllocatorDeallocate(alloc, (void *)stream->callBacks); + } +} + +static const CFRuntimeClass __CFReadStreamClass = { + 0, + "CFReadStream", + NULL, // init + NULL, // copy + __CFStreamDeallocate, + NULL, + NULL, + NULL, // copyHumanDesc + __CFStreamCopyDescription +}; + +static const CFRuntimeClass __CFWriteStreamClass = { + 0, + "CFWriteStream", + NULL, // init + NULL, // copy + __CFStreamDeallocate, + NULL, + NULL, + NULL, // copyHumanDesc + __CFStreamCopyDescription +}; + +CONST_STRING_DECL(kCFStreamPropertySocketNativeHandle, "kCFStreamPropertySocketNativeHandle") +CONST_STRING_DECL(kCFStreamPropertySocketRemoteHostName, "kCFStreamPropertySocketRemoteHostName") +CONST_STRING_DECL(kCFStreamPropertySocketRemotePortNumber, "kCFStreamPropertySocketRemotePortNumber") +CONST_STRING_DECL(kCFStreamPropertyDataWritten, "kCFStreamPropertyDataWritten") +CONST_STRING_DECL(kCFStreamPropertyAppendToFile, "kCFStreamPropertyAppendToFile") + +__private_extern__ void __CFStreamInitialize(void) { + __kCFReadStreamTypeID = _CFRuntimeRegisterClass(&__CFReadStreamClass); + __kCFWriteStreamTypeID = _CFRuntimeRegisterClass(&__CFWriteStreamClass); +} + + +CF_EXPORT CFTypeID CFReadStreamGetTypeID(void) { + return __kCFReadStreamTypeID; +} + +CF_EXPORT CFTypeID CFWriteStreamGetTypeID(void) { + return __kCFWriteStreamTypeID; +} + +static struct _CFStream *_CFStreamCreate(CFAllocatorRef allocator, Boolean isReadStream) { + struct _CFStream *newStream = (struct _CFStream *)_CFRuntimeCreateInstance(allocator, isReadStream ? __kCFReadStreamTypeID : __kCFWriteStreamTypeID, sizeof(struct _CFStream) - sizeof(CFRuntimeBase), NULL); + if (newStream) { +// numStreamInstances ++; + newStream->flags = 0; + _CFStreamSetStatusCode(newStream, kCFStreamStatusNotOpen); + newStream->error = NULL; + newStream->client = NULL; + newStream->info = NULL; + newStream->callBacks = NULL; + } + return newStream; +} + +__private_extern__ struct _CFStream *_CFStreamCreateWithConstantCallbacks(CFAllocatorRef alloc, void *info, const struct _CFStreamCallBacks *cb, Boolean isReading) { + struct _CFStream *newStream; + if (cb->version != 1) return NULL; + newStream = _CFStreamCreate(alloc, isReading); + if (newStream) { + __CFBitSet(newStream->flags, CONSTANT_CALLBACKS); + newStream->callBacks = cb; + if (cb->create) { + newStream->info = cb->create(newStream, info); + } else { + newStream->info = info; + } + } + return newStream; +} + +CF_EXPORT void _CFStreamSetInfoPointer(struct _CFStream *stream, void *info, const struct _CFStreamCallBacks *cb) { + if (info != stream->info) { + if (stream->callBacks->finalize) { + stream->callBacks->finalize(stream, stream->info); + } + if (cb->create) { + stream->info = cb->create(stream, info); + } else { + stream->info = info; + } + } + stream->callBacks = cb; +} + + +CF_EXPORT CFReadStreamRef CFReadStreamCreate(CFAllocatorRef alloc, const CFReadStreamCallBacks *callbacks, void *info) { + struct _CFStream *newStream = _CFStreamCreate(alloc, TRUE); + struct _CFStreamCallBacks *cb; + if (!newStream) return NULL; + cb = (struct _CFStreamCallBacks *)CFAllocatorAllocate(alloc, sizeof(struct _CFStreamCallBacks), 0); + if (!cb) { + CFRelease(newStream); + return NULL; + } + if (callbacks->version == 0) { + CFReadStreamCallBacksV0 *cbV0 = (CFReadStreamCallBacksV0 *)callbacks; + CFStreamClientContext *ctxt = (CFStreamClientContext *)info; + newStream->info = ctxt->retain ? (void *)ctxt->retain(ctxt->info) : ctxt->info; + cb->version = 0; + cb->create = (void *(*)(struct _CFStream *, void *))ctxt->retain; + cb->finalize = (void(*)(struct _CFStream *, void *))ctxt->release; + cb->copyDescription = (CFStringRef(*)(struct _CFStream *, void *))ctxt->copyDescription; + cb->open = (Boolean(*)(struct _CFStream *, CFErrorRef *, Boolean *, void *))cbV0->open; + cb->openCompleted = (Boolean (*)(struct _CFStream *, CFErrorRef *, void *))cbV0->openCompleted; + cb->read = (CFIndex (*)(CFReadStreamRef, UInt8 *, CFIndex, CFErrorRef *, Boolean *, void *))cbV0->read; + cb->getBuffer = (const UInt8 *(*)(CFReadStreamRef, CFIndex, CFIndex *, CFErrorRef *, Boolean *, void *))cbV0->getBuffer; + cb->canRead = (Boolean (*)(CFReadStreamRef, CFErrorRef*, void*))cbV0->canRead; + cb->write = NULL; + cb->canWrite = NULL; + cb->close = (void (*)(struct _CFStream *, void *))cbV0->close; + cb->copyProperty = (CFTypeRef (*)(struct _CFStream *, CFStringRef, void *))cbV0->copyProperty; + cb->setProperty = NULL; + cb->requestEvents = NULL; + cb->schedule = (void (*)(struct _CFStream *, CFRunLoopRef, CFStringRef, void *))cbV0->schedule; + cb->unschedule = (void (*)(struct _CFStream *, CFRunLoopRef, CFStringRef, void *))cbV0->unschedule; + } else if (callbacks->version == 1) { + CFReadStreamCallBacksV1 *cbV1 = (CFReadStreamCallBacksV1 *)callbacks; + newStream->info = cbV1->create ? cbV1->create((CFReadStreamRef)newStream, info) : info; + cb->version = 1; + cb->create = (void *(*)(struct _CFStream *, void *))cbV1->create; + cb->finalize = (void(*)(struct _CFStream *, void *))cbV1->finalize; + cb->copyDescription = (CFStringRef(*)(struct _CFStream *, void *))cbV1->copyDescription; + cb->open = (Boolean(*)(struct _CFStream *, CFErrorRef *, Boolean *, void *))cbV1->open; + cb->openCompleted = (Boolean (*)(struct _CFStream *, CFErrorRef *, void *))cbV1->openCompleted; + cb->read = (CFIndex (*)(CFReadStreamRef, UInt8 *, CFIndex, CFErrorRef *, Boolean *, void *))cbV1->read; + cb->getBuffer = (const UInt8 *(*)(CFReadStreamRef, CFIndex, CFIndex *, CFErrorRef *, Boolean *, void *))cbV1->getBuffer; + cb->canRead = (Boolean (*)(CFReadStreamRef, CFErrorRef*, void*))cbV1->canRead; + cb->write = NULL; + cb->canWrite = NULL; + cb->close = (void (*)(struct _CFStream *, void *))cbV1->close; + cb->copyProperty = (CFTypeRef (*)(struct _CFStream *, CFStringRef, void *))cbV1->copyProperty; + cb->setProperty = (Boolean(*)(struct _CFStream *, CFStringRef, CFTypeRef, void *))cbV1->setProperty; + cb->requestEvents = (void(*)(struct _CFStream *, CFOptionFlags, void *))cbV1->requestEvents; + cb->schedule = (void (*)(struct _CFStream *, CFRunLoopRef, CFStringRef, void *))cbV1->schedule; + cb->unschedule = (void (*)(struct _CFStream *, CFRunLoopRef, CFStringRef, void *))cbV1->unschedule; + } else { + newStream->info = callbacks->create ? callbacks->create((CFReadStreamRef)newStream, info) : info; + cb->version = 2; + cb->create = (void *(*)(struct _CFStream *, void *))callbacks->create; + cb->finalize = (void(*)(struct _CFStream *, void *))callbacks->finalize; + cb->copyDescription = (CFStringRef(*)(struct _CFStream *, void *))callbacks->copyDescription; + cb->open = (Boolean(*)(struct _CFStream *, CFErrorRef *, Boolean *, void *))callbacks->open; + cb->openCompleted = (Boolean (*)(struct _CFStream *, CFErrorRef *, void *))callbacks->openCompleted; + cb->read = callbacks->read; + cb->getBuffer = callbacks->getBuffer; + cb->canRead = callbacks->canRead; + cb->write = NULL; + cb->canWrite = NULL; + cb->close = (void (*)(struct _CFStream *, void *))callbacks->close; + cb->copyProperty = (CFTypeRef (*)(struct _CFStream *, CFStringRef, void *))callbacks->copyProperty; + cb->setProperty = (Boolean(*)(struct _CFStream *, CFStringRef, CFTypeRef, void *))callbacks->setProperty; + cb->requestEvents = (void(*)(struct _CFStream *, CFOptionFlags, void *))callbacks->requestEvents; + cb->schedule = (void (*)(struct _CFStream *, CFRunLoopRef, CFStringRef, void *))callbacks->schedule; + cb->unschedule = (void (*)(struct _CFStream *, CFRunLoopRef, CFStringRef, void *))callbacks->unschedule; + } + + newStream->callBacks = cb; + return (CFReadStreamRef)newStream; +} + +CF_EXPORT CFWriteStreamRef CFWriteStreamCreate(CFAllocatorRef alloc, const CFWriteStreamCallBacks *callbacks, void *info) { + struct _CFStream *newStream = _CFStreamCreate(alloc, FALSE); + struct _CFStreamCallBacks *cb; + if (!newStream) return NULL; + cb = (struct _CFStreamCallBacks *)CFAllocatorAllocate(alloc, sizeof(struct _CFStreamCallBacks), 0); + if (!cb) { + CFRelease(newStream); + return NULL; + } + if (callbacks->version == 0) { + CFWriteStreamCallBacksV0 *cbV0 = (CFWriteStreamCallBacksV0 *)callbacks; + CFStreamClientContext *ctxt = (CFStreamClientContext *)info; + newStream->info = ctxt->retain ? (void *)ctxt->retain(ctxt->info) : ctxt->info; + cb->version = 0; + cb->create = (void *(*)(struct _CFStream *, void *))ctxt->retain; + cb->finalize = (void(*)(struct _CFStream *, void *))ctxt->release; + cb->copyDescription = (CFStringRef(*)(struct _CFStream *, void *))ctxt->copyDescription; + cb->open = (Boolean(*)(struct _CFStream *, CFErrorRef *, Boolean *, void *))cbV0->open; + cb->openCompleted = (Boolean (*)(struct _CFStream *, CFErrorRef *, void *))cbV0->openCompleted; + cb->read = NULL; + cb->getBuffer = NULL; + cb->canRead = NULL; + cb->write = (CFIndex(*)(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFErrorRef *error, void *info))cbV0->write; + cb->canWrite = (Boolean(*)(CFWriteStreamRef stream, CFErrorRef *error, void *info))cbV0->canWrite; + cb->close = (void (*)(struct _CFStream *, void *))cbV0->close; + cb->copyProperty = (CFTypeRef (*)(struct _CFStream *, CFStringRef, void *))cbV0->copyProperty; + cb->setProperty = NULL; + cb->requestEvents = NULL; + cb->schedule = (void (*)(struct _CFStream *, CFRunLoopRef, CFStringRef, void *))cbV0->schedule; + cb->unschedule = (void (*)(struct _CFStream *, CFRunLoopRef, CFStringRef, void *))cbV0->unschedule; + } else if (callbacks->version == 1) { + CFWriteStreamCallBacksV1 *cbV1 = (CFWriteStreamCallBacksV1 *)callbacks; + cb->version = 1; + newStream->info = cbV1->create ? cbV1->create((CFWriteStreamRef)newStream, info) : info; + cb->create = (void *(*)(struct _CFStream *, void *))cbV1->create; + cb->finalize = (void(*)(struct _CFStream *, void *))cbV1->finalize; + cb->copyDescription = (CFStringRef(*)(struct _CFStream *, void *))cbV1->copyDescription; + cb->open = (Boolean(*)(struct _CFStream *, CFErrorRef *, Boolean *, void *))cbV1->open; + cb->openCompleted = (Boolean (*)(struct _CFStream *, CFErrorRef *, void *))cbV1->openCompleted; + cb->read = NULL; + cb->getBuffer = NULL; + cb->canRead = NULL; + cb->write = (CFIndex(*)(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFErrorRef *error, void *info))cbV1->write; + cb->canWrite = (Boolean(*)(CFWriteStreamRef stream, CFErrorRef *error, void *info))cbV1->canWrite; + cb->close = (void (*)(struct _CFStream *, void *))cbV1->close; + cb->copyProperty = (CFTypeRef (*)(struct _CFStream *, CFStringRef, void *))cbV1->copyProperty; + cb->setProperty = (Boolean (*)(struct _CFStream *, CFStringRef, CFTypeRef, void *))cbV1->setProperty; + cb->requestEvents = (void(*)(struct _CFStream *, CFOptionFlags, void *))cbV1->requestEvents; + cb->schedule = (void (*)(struct _CFStream *, CFRunLoopRef, CFStringRef, void *))cbV1->schedule; + cb->unschedule = (void (*)(struct _CFStream *, CFRunLoopRef, CFStringRef, void *))cbV1->unschedule; + } else { + cb->version = callbacks->version; + newStream->info = callbacks->create ? callbacks->create((CFWriteStreamRef)newStream, info) : info; + cb->create = (void *(*)(struct _CFStream *, void *))callbacks->create; + cb->finalize = (void(*)(struct _CFStream *, void *))callbacks->finalize; + cb->copyDescription = (CFStringRef(*)(struct _CFStream *, void *))callbacks->copyDescription; + cb->open = (Boolean(*)(struct _CFStream *, CFErrorRef *, Boolean *, void *))callbacks->open; + cb->openCompleted = (Boolean (*)(struct _CFStream *, CFErrorRef *, void *))callbacks->openCompleted; + cb->read = NULL; + cb->getBuffer = NULL; + cb->canRead = NULL; + cb->write = callbacks->write; + cb->canWrite = callbacks->canWrite; + cb->close = (void (*)(struct _CFStream *, void *))callbacks->close; + cb->copyProperty = (CFTypeRef (*)(struct _CFStream *, CFStringRef, void *))callbacks->copyProperty; + cb->setProperty = (Boolean (*)(struct _CFStream *, CFStringRef, CFTypeRef, void *))callbacks->setProperty; + cb->requestEvents = (void(*)(struct _CFStream *, CFOptionFlags, void *))callbacks->requestEvents; + cb->schedule = (void (*)(struct _CFStream *, CFRunLoopRef, CFStringRef, void *))callbacks->schedule; + cb->unschedule = (void (*)(struct _CFStream *, CFRunLoopRef, CFStringRef, void *))callbacks->unschedule; + } + newStream->callBacks = cb; + return (CFWriteStreamRef)newStream; +} + +static void _CFStreamSignalEventSynch(void* info) { + + struct _CFStream *stream = NULL; + CFOptionFlags eventMask, whatToSignal = 0; + + if (CFGetTypeID((CFTypeRef)info) != CFArrayGetTypeID()) { + stream = (struct _CFStream*)info; + whatToSignal = stream->client->whatToSignal; + stream->client->whatToSignal = 0; + } + else { + + CFMutableArrayRef list = (CFMutableArrayRef)info; + CFIndex c, i; + + __CFSpinLock(&sSourceLock); + + c = CFArrayGetCount(list); + for (i = 0; i < c; i++) { + struct _CFStream* s = (struct _CFStream*)CFArrayGetValueAtIndex(list, i); + if (s->client->whatToSignal) { + stream = s; + whatToSignal = stream->client->whatToSignal; + s->client->whatToSignal = 0; + break; + } + } + + while (i < c) { + struct _CFStream* s = (struct _CFStream*)CFArrayGetValueAtIndex(list, i); + if (s->client->whatToSignal) { + CFRunLoopSourceSignal(s->client->rlSource); + break; + } + i++; + } + + __CFSpinUnlock(&sSourceLock); + } + + if (!stream) + return; + + CFRetain(stream); // In case the client releases the stream while we're still in the for loop.... + + __CFBitSet(stream->flags, CALLING_CLIENT); + + for (eventMask = 1; eventMask <= whatToSignal; eventMask = eventMask << 1) { + if ((eventMask & whatToSignal) && (stream->client->when & eventMask)) { + stream->client->cb(stream, eventMask, stream->client->cbContext.info); + } + } + + __CFBitClear(stream->flags, CALLING_CLIENT); + + CFRelease(stream); +} + +// Largely cribbed from CFSocket.c; find a run loop where our source is scheduled and wake it up. We skip the runloop cycling, so we +// are likely to signal the same run loop over and over again. Don't know if we should worry about that. +static void _wakeUpRunLoop(struct _CFStream *stream) { + CFRunLoopRef rl = NULL; + SInt32 idx, cnt; + CFArrayRef rlArray; + if (!stream->client || !stream->client->runLoopsAndModes) return; + rlArray = stream->client->runLoopsAndModes; + cnt = CFArrayGetCount(rlArray); + if (cnt == 0) return; + if (cnt == 2) { + rl = (CFRunLoopRef)CFArrayGetValueAtIndex(rlArray, 0); + } else { + rl = (CFRunLoopRef)CFArrayGetValueAtIndex(rlArray, 0); + for (idx = 2; NULL != rl && idx < cnt; idx+=2) { + CFRunLoopRef value = (CFRunLoopRef)CFArrayGetValueAtIndex(rlArray, idx); + if (value != rl) rl = NULL; + } + if (NULL == rl) { /* more than one different rl, so we must pick one */ + for (idx = 0; idx < cnt; idx+=2) { + CFRunLoopRef value = (CFRunLoopRef)CFArrayGetValueAtIndex(rlArray, idx); + CFStringRef currentMode = CFRunLoopCopyCurrentMode(value); + if (NULL != currentMode && CFEqual(currentMode, CFArrayGetValueAtIndex(rlArray, idx+1)) && CFRunLoopIsWaiting(value)) { + CFRelease(currentMode); + rl = value; + break; + } + if (NULL != currentMode) CFRelease(currentMode); + } + if (NULL == rl) { /* didn't choose one above, so choose first */ + rl = (CFRunLoopRef)CFArrayGetValueAtIndex(rlArray, 0); + } + } + } + if (NULL != rl && CFRunLoopIsWaiting(rl)) CFRunLoopWakeUp(rl); +} + +__private_extern__ void _CFStreamSignalEvent(struct _CFStream *stream, CFStreamEventType event, CFErrorRef error, Boolean synchronousAllowed) { + // Update our internal status; we must use the primitive __CFStreamGetStatus(), because CFStreamGetStatus() calls us, and we can end up in an infinite loop. + CFStreamStatus status = __CFStreamGetStatus(stream); + + // Sanity check the event + if (status == kCFStreamStatusNotOpen) { + // No events allowed; this is almost certainly a bug in the stream's implementation + CFLog(__kCFLogAssertion, CFSTR("Stream %p is sending an event before being opened"), stream); + event = 0; + } else if (status == kCFStreamStatusClosed || status == kCFStreamStatusError) { + // no further events are allowed + event = 0; + } else if (status == kCFStreamStatusAtEnd) { + // Only error events are allowed + event &= kCFStreamEventErrorOccurred; + } else if (status != kCFStreamStatusOpening) { + // cannot send open completed; that happened already + event &= ~kCFStreamEventOpenCompleted; + } + + // Change status if appropriate + if (event & kCFStreamEventOpenCompleted && status == kCFStreamStatusOpening) { + _CFStreamSetStatusCode(stream, kCFStreamStatusOpen); + } + if (event & kCFStreamEventEndEncountered && status < kCFStreamStatusAtEnd) { + _CFStreamSetStatusCode(stream, kCFStreamStatusAtEnd); + } + if (event & kCFStreamEventErrorOccurred) { + if (_CFStreamGetCallBackPtr(stream)->version < 2) { + _CFStreamSetStreamError(stream, (CFStreamError *)error); + } else { + CFAssert(error, __kCFLogAssertion, "CFStream: kCFStreamEventErrorOccurred signalled, but error is NULL!"); + CFRetain(error); + if (stream->error) CFRelease(stream->error); + stream->error = error; + } + _CFStreamSetStatusCode(stream, kCFStreamStatusError); + } + + // Now signal any pertinent event + if (stream->client && stream->client->rlSource && (stream->client->when & event)) { + + Boolean signalNow = FALSE; + + stream->client->whatToSignal |= event; + + if (synchronousAllowed && !__CFBitIsSet(stream->flags, CALLING_CLIENT)) { + + CFRunLoopRef rl = CFRunLoopGetCurrent(); + CFStringRef mode = CFRunLoopCopyCurrentMode(rl); + + if (mode) { + if (CFRunLoopContainsSource(rl, stream->client->rlSource, mode)) + signalNow = TRUE; + CFRelease(mode); + } + } + + if (signalNow) { + // Can call out safely right now + _CFStreamSignalEventSynch(stream); + } else { + // Schedule for later delivery + CFRunLoopSourceSignal(stream->client->rlSource); + _wakeUpRunLoop(stream); + } + } +} + +__private_extern__ CFStreamStatus _CFStreamGetStatus(struct _CFStream *stream) { + CFStreamStatus status = __CFStreamGetStatus(stream); + // Status code just represents the value when last we checked; if we were in the middle of doing work at that time, we need to find out if the work has completed, now. If we find out about a status change, we need to inform the client as well, so we call _CFStreamSignalEvent. This will take care of updating our internal status correctly, too. + __CFBitSet(stream->flags, CALLING_CLIENT); + if (status == kCFStreamStatusOpening) { + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + if (cb->openCompleted) { + Boolean isComplete; + if (cb->version < 2) { + CFStreamError err = {0, 0}; + isComplete = ((_CFStreamCBOpenCompletedV1)(cb->openCompleted))(stream, &err, _CFStreamGetInfoPointer(stream)); + if (err.error != 0) _CFStreamSetStreamError(stream, &err); + } else { + isComplete = cb->openCompleted(stream, &(stream->error), _CFStreamGetInfoPointer(stream)); + } + if (isComplete) { + if (!stream->error) { + status = kCFStreamStatusOpen; + } else { + status = kCFStreamStatusError; + } + _CFStreamSetStatusCode(stream, status); + if (status == kCFStreamStatusOpen) { + _CFStreamScheduleEvent(stream, kCFStreamEventOpenCompleted); + } else { + _CFStreamScheduleEvent(stream, kCFStreamEventErrorOccurred); + } + } + } + } + __CFBitClear(stream->flags, CALLING_CLIENT); + return status; +} + +CF_EXPORT CFStreamStatus CFReadStreamGetStatus(CFReadStreamRef stream) { + CF_OBJC_FUNCDISPATCH0(__kCFReadStreamTypeID, CFStreamStatus, stream, "streamStatus"); + return _CFStreamGetStatus((struct _CFStream *)stream); +} + +CF_EXPORT CFStreamStatus CFWriteStreamGetStatus(CFWriteStreamRef stream) { + CF_OBJC_FUNCDISPATCH0(__kCFWriteStreamTypeID, CFStreamStatus, stream, "streamStatus"); + return _CFStreamGetStatus((struct _CFStream *)stream); +} + +static CFStreamError _CFStreamGetStreamError(struct _CFStream *stream) { + CFStreamError result; + if (!stream->error) { + result.error = 0; + result.domain = 0; + } else if (_CFStreamGetCallBackPtr(stream)->version < 2) { + CFStreamError *streamError = (CFStreamError *)(stream->error); + result.error = streamError->error; + result.domain = streamError->domain; + } else { + result = _CFStreamErrorFromError(stream->error); + } + return result; +} + +CF_EXPORT CFStreamError CFReadStreamGetError(CFReadStreamRef stream) { + CF_OBJC_FUNCDISPATCH0(__kCFReadStreamTypeID, CFStreamError, stream, "_cfStreamError"); + return _CFStreamGetStreamError((struct _CFStream *)stream); +} + +CF_EXPORT CFStreamError CFWriteStreamGetError(CFWriteStreamRef stream) { + CF_OBJC_FUNCDISPATCH0(__kCFWriteStreamTypeID, CFStreamError, stream, "_cfStreamError"); + return _CFStreamGetStreamError((struct _CFStream *)stream); +} + +static CFErrorRef _CFStreamCopyError(struct _CFStream *stream) { + if (!stream->error) { + return NULL; + } else if (_CFStreamGetCallBackPtr(stream)->version < 2) { + return _CFErrorFromStreamError(CFGetAllocator(stream), (CFStreamError *)(stream->error)); + } else { + CFRetain(stream->error); + return stream->error; + } +} + +CF_EXPORT CFErrorRef CFReadStreamCopyError(CFReadStreamRef stream) { + CF_OBJC_FUNCDISPATCH0(__kCFReadStreamTypeID, CFErrorRef, stream, "streamError"); + return _CFStreamCopyError((struct _CFStream *)stream); +} + +CF_EXPORT CFErrorRef CFWriteStreamCopyError(CFWriteStreamRef stream) { + return _CFStreamCopyError((struct _CFStream *)stream); + CF_OBJC_FUNCDISPATCH0(__kCFWriteStreamTypeID, CFErrorRef, stream, "streamError"); +} + +__private_extern__ Boolean _CFStreamOpen(struct _CFStream *stream) { + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + Boolean success, openComplete; + if (_CFStreamGetStatus(stream) != kCFStreamStatusNotOpen) { + return FALSE; + } + __CFBitSet(stream->flags, CALLING_CLIENT); + _CFStreamSetStatusCode(stream, kCFStreamStatusOpening); + if (cb->open) { + if (cb->version < 2) { + CFStreamError err = {0, 0}; + success = ((_CFStreamCBOpenV1)(cb->open))(stream, &err, &openComplete, _CFStreamGetInfoPointer(stream)); + if (err.error != 0) _CFStreamSetStreamError(stream, &err); + } else { + success = cb->open(stream, &(stream->error), &openComplete, _CFStreamGetInfoPointer(stream)); + } + } else { + success = TRUE; + openComplete = TRUE; + } + if (openComplete) { + if (success) { + // 2957690 - Guard against the possibility that the stream has already signalled itself in to a later state (like AtEnd) + if (__CFStreamGetStatus(stream) == kCFStreamStatusOpening) { + _CFStreamSetStatusCode(stream, kCFStreamStatusOpen); + } + _CFStreamScheduleEvent(stream, kCFStreamEventOpenCompleted); + } else { + _CFStreamSetStatusCode(stream, kCFStreamStatusError); + _CFStreamScheduleEvent(stream, kCFStreamEventErrorOccurred); + } + } + __CFBitClear(stream->flags, CALLING_CLIENT); + return success; +} + +CF_EXPORT Boolean CFReadStreamOpen(CFReadStreamRef stream) { + if(CF_IS_OBJC(__kCFReadStreamTypeID, stream)) { + CF_OBJC_VOIDCALL0(stream, "open"); + return TRUE; + } + return _CFStreamOpen((struct _CFStream *)stream); +} + +CF_EXPORT Boolean CFWriteStreamOpen(CFWriteStreamRef stream) { + if(CF_IS_OBJC(__kCFWriteStreamTypeID, stream)) { + CF_OBJC_VOIDCALL0(stream, "open"); + return TRUE; + } + return _CFStreamOpen((struct _CFStream *)stream); +} + +CF_EXPORT void CFReadStreamClose(CFReadStreamRef stream) { + CF_OBJC_FUNCDISPATCH0(__kCFReadStreamTypeID, void, stream, "close"); + _CFStreamClose((struct _CFStream *)stream); +} + +CF_EXPORT void CFWriteStreamClose(CFWriteStreamRef stream) { + CF_OBJC_FUNCDISPATCH0(__kCFWriteStreamTypeID, void, stream, "close"); + _CFStreamClose((struct _CFStream *)stream); +} + +CF_EXPORT Boolean CFReadStreamHasBytesAvailable(CFReadStreamRef readStream) { + CF_OBJC_FUNCDISPATCH0(__kCFReadStreamTypeID, Boolean, readStream, "hasBytesAvailable"); + struct _CFStream *stream = (struct _CFStream *)readStream; + CFStreamStatus status = _CFStreamGetStatus(stream); + const struct _CFStreamCallBacks *cb; + if (status != kCFStreamStatusOpen && status != kCFStreamStatusReading) { + return FALSE; + } + cb = _CFStreamGetCallBackPtr(stream); + if (cb->canRead == NULL) { + return TRUE; // No way to know without trying.... + } else { + Boolean result; + __CFBitSet(stream->flags, CALLING_CLIENT); + if (cb->version < 2) { + result = ((_CFStreamCBCanReadV1)(cb->canRead))((CFReadStreamRef)stream, _CFStreamGetInfoPointer(stream)); + } else { + result = cb->canRead((CFReadStreamRef)stream, &(stream->error), _CFStreamGetInfoPointer(stream)); + if (stream->error) { + _CFStreamSetStatusCode(stream, kCFStreamStatusError); + _CFStreamScheduleEvent(stream, kCFStreamEventErrorOccurred); + } + } + __CFBitClear(stream->flags, CALLING_CLIENT); + return result; + } +} + +static void waitForOpen(struct _CFStream *stream); +CFIndex CFReadStreamRead(CFReadStreamRef readStream, UInt8 *buffer, CFIndex bufferLength) { + CF_OBJC_FUNCDISPATCH2(__kCFReadStreamTypeID, CFIndex, readStream, "read:maxLength:", buffer, bufferLength); + struct _CFStream *stream = (struct _CFStream *)readStream; + CFStreamStatus status = _CFStreamGetStatus(stream); + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + if (status == kCFStreamStatusOpening) { + __CFBitSet(stream->flags, CALLING_CLIENT); + waitForOpen(stream); + __CFBitClear(stream->flags, CALLING_CLIENT); + status = _CFStreamGetStatus(stream); + } + + if (status != kCFStreamStatusOpen && status != kCFStreamStatusReading && status != kCFStreamStatusAtEnd) { + return -1; + } else if (status == kCFStreamStatusAtEnd) { + return 0; + } else { + Boolean atEOF; + CFIndex bytesRead; + __CFBitSet(stream->flags, CALLING_CLIENT); + if (stream->client) { + stream->client->whatToSignal &= ~kCFStreamEventHasBytesAvailable; + } + _CFStreamSetStatusCode(stream, kCFStreamStatusReading); + if (cb->version < 2) { + CFStreamError err = {0, 0}; + bytesRead = ((_CFStreamCBReadV1)(cb->read))((CFReadStreamRef)stream, buffer, bufferLength, &err, &atEOF, _CFStreamGetInfoPointer(stream)); + if (err.error != 0) _CFStreamSetStreamError(stream, &err); + } else { + bytesRead = cb->read((CFReadStreamRef)stream, buffer, bufferLength, &(stream->error), &atEOF, _CFStreamGetInfoPointer(stream)); + } + if (stream->error) { + bytesRead = -1; + _CFStreamSetStatusCode(stream, kCFStreamStatusError); + _CFStreamScheduleEvent(stream, kCFStreamEventErrorOccurred); + } else if (atEOF) { + _CFStreamSetStatusCode(stream, kCFStreamStatusAtEnd); + _CFStreamScheduleEvent(stream, kCFStreamEventEndEncountered); + } else { + _CFStreamSetStatusCode(stream, kCFStreamStatusOpen); + } + __CFBitClear(stream->flags, CALLING_CLIENT); + return bytesRead; + } +} + +CF_EXPORT const UInt8 *CFReadStreamGetBuffer(CFReadStreamRef readStream, CFIndex maxBytesToRead, CFIndex *numBytesRead) { + if (CF_IS_OBJC(__kCFReadStreamTypeID, readStream)) { + uint8_t *bufPtr = NULL; + Boolean gotBytes; + CF_OBJC_CALL2(Boolean, gotBytes, readStream, "getBuffer:length:", &bufPtr, numBytesRead); + if(gotBytes) { + return (const UInt8 *)bufPtr; + } else { + return NULL; + } + } + struct _CFStream *stream = (struct _CFStream *)readStream; + CFStreamStatus status = _CFStreamGetStatus(stream); + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + const UInt8 *buffer; + if (status == kCFStreamStatusOpening) { + __CFBitSet(stream->flags, CALLING_CLIENT); + waitForOpen(stream); + __CFBitClear(stream->flags, CALLING_CLIENT); + status = _CFStreamGetStatus(stream); + } + if (status != kCFStreamStatusOpen && status != kCFStreamStatusReading && status != kCFStreamStatusAtEnd) { + *numBytesRead = -1; + buffer = NULL; + } else if (status == kCFStreamStatusAtEnd || cb->getBuffer == NULL) { + *numBytesRead = 0; + buffer = NULL; + } else { + Boolean atEOF; + Boolean hadBytes = stream->client && (stream->client->whatToSignal & kCFStreamEventHasBytesAvailable); + __CFBitSet(stream->flags, CALLING_CLIENT); + if (hadBytes) { + stream->client->whatToSignal &= ~kCFStreamEventHasBytesAvailable; + } + _CFStreamSetStatusCode(stream, kCFStreamStatusReading); + if (cb->version < 2) { + CFStreamError err = {0, 0}; + buffer = ((_CFStreamCBGetBufferV1)(cb->getBuffer))((CFReadStreamRef)stream, maxBytesToRead, numBytesRead, &err, &atEOF, _CFStreamGetInfoPointer(stream)); + if (err.error != 0) _CFStreamSetStreamError(stream, &err); + } else { + buffer = cb->getBuffer((CFReadStreamRef)stream, maxBytesToRead, numBytesRead, &(stream->error), &atEOF, _CFStreamGetInfoPointer(stream)); + } + if (stream->error) { + *numBytesRead = -1; + _CFStreamSetStatusCode(stream, kCFStreamStatusError); + buffer = NULL; + _CFStreamScheduleEvent(stream, kCFStreamEventErrorOccurred); + } else if (atEOF) { + _CFStreamSetStatusCode(stream, kCFStreamStatusAtEnd); + _CFStreamScheduleEvent(stream, kCFStreamEventEndEncountered); + } else { + if (!buffer && hadBytes) { + stream->client->whatToSignal |= kCFStreamEventHasBytesAvailable; + } + _CFStreamSetStatusCode(stream, kCFStreamStatusOpen); + } + __CFBitClear(stream->flags, CALLING_CLIENT); + } + return buffer; +} + +CF_EXPORT Boolean CFWriteStreamCanAcceptBytes(CFWriteStreamRef writeStream) { + CF_OBJC_FUNCDISPATCH0(__kCFWriteStreamTypeID, Boolean, writeStream, "hasSpaceAvailable"); + struct _CFStream *stream = (struct _CFStream *)writeStream; + CFStreamStatus status = _CFStreamGetStatus(stream); + const struct _CFStreamCallBacks *cb; + if (status != kCFStreamStatusOpen && status != kCFStreamStatusWriting) { + return FALSE; + } + cb = _CFStreamGetCallBackPtr(stream); + if (cb->canWrite == NULL) { + return TRUE; // No way to know without trying.... + } else { + Boolean result; + __CFBitSet(stream->flags, CALLING_CLIENT); + if (cb->version < 2) { + result = ((_CFStreamCBCanWriteV1)(cb->canWrite))((CFWriteStreamRef)stream, _CFStreamGetInfoPointer(stream)); + } else { + result = cb->canWrite((CFWriteStreamRef)stream, &(stream->error), _CFStreamGetInfoPointer(stream)); + if (stream->error) { + _CFStreamSetStatusCode(stream, kCFStreamStatusError); + _CFStreamScheduleEvent(stream, kCFStreamEventErrorOccurred); + } + } + __CFBitClear(stream->flags, CALLING_CLIENT); + return result; + } +} + +CF_EXPORT CFIndex CFWriteStreamWrite(CFWriteStreamRef writeStream, const UInt8 *buffer, CFIndex bufferLength) { + CF_OBJC_FUNCDISPATCH2(__kCFWriteStreamTypeID, CFIndex, writeStream, "write:maxLength:", buffer, bufferLength); + struct _CFStream *stream = (struct _CFStream *)writeStream; + CFStreamStatus status = _CFStreamGetStatus(stream); + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + if (status == kCFStreamStatusOpening) { + __CFBitSet(stream->flags, CALLING_CLIENT); + waitForOpen(stream); + __CFBitClear(stream->flags, CALLING_CLIENT); + status = _CFStreamGetStatus(stream); + } + if (status != kCFStreamStatusOpen && status != kCFStreamStatusWriting) { + return -1; + } else { + CFIndex result; + __CFBitSet(stream->flags, CALLING_CLIENT); + _CFStreamSetStatusCode(stream, kCFStreamStatusWriting); + if (stream->client) { + stream->client->whatToSignal &= ~kCFStreamEventCanAcceptBytes; + } + if (cb->version < 2) { + CFStreamError err = {0, 0}; + result = ((_CFStreamCBWriteV1)(cb->write))((CFWriteStreamRef)stream, buffer, bufferLength, &err, _CFStreamGetInfoPointer(stream)); + if (err.error) _CFStreamSetStreamError(stream, &err); + } else { + result = cb->write((CFWriteStreamRef)stream, buffer, bufferLength, &(stream->error), _CFStreamGetInfoPointer(stream)); + } + if (stream->error) { + _CFStreamSetStatusCode(stream, kCFStreamStatusError); + _CFStreamScheduleEvent(stream, kCFStreamEventErrorOccurred); + } else if (result == 0) { + _CFStreamSetStatusCode(stream, kCFStreamStatusAtEnd); + _CFStreamScheduleEvent(stream, kCFStreamEventEndEncountered); + } else { + _CFStreamSetStatusCode(stream, kCFStreamStatusOpen); + } + __CFBitClear(stream->flags, CALLING_CLIENT); + return result; + } +} + +__private_extern__ CFTypeRef _CFStreamCopyProperty(struct _CFStream *stream, CFStringRef propertyName) { + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + if (cb->copyProperty == NULL) { + return NULL; + } else { + CFTypeRef result; + __CFBitSet(stream->flags, CALLING_CLIENT); + result = cb->copyProperty(stream, propertyName, _CFStreamGetInfoPointer(stream)); + __CFBitClear(stream->flags, CALLING_CLIENT); + return result; + } +} + +CF_EXPORT CFTypeRef CFReadStreamCopyProperty(CFReadStreamRef stream, CFStringRef propertyName) { + CF_OBJC_FUNCDISPATCH1(__kCFReadStreamTypeID, CFTypeRef, stream, "propertyForKey:", propertyName); + return _CFStreamCopyProperty((struct _CFStream *)stream, propertyName); +} + +CF_EXPORT CFTypeRef CFWriteStreamCopyProperty(CFWriteStreamRef stream, CFStringRef propertyName) { + CF_OBJC_FUNCDISPATCH1(__kCFWriteStreamTypeID, CFTypeRef, stream, "propertyForKey:", propertyName); + return _CFStreamCopyProperty((struct _CFStream *)stream, propertyName); +} + +__private_extern__ Boolean _CFStreamSetProperty(struct _CFStream *stream, CFStringRef prop, CFTypeRef val) { + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + if (cb->setProperty == NULL) { + return FALSE; + } else { + Boolean result; + __CFBitSet(stream->flags, CALLING_CLIENT); + result = cb->setProperty(stream, prop, val, _CFStreamGetInfoPointer(stream)); + __CFBitClear(stream->flags, CALLING_CLIENT); + return result; + } +} + +CF_EXPORT +Boolean CFReadStreamSetProperty(CFReadStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue) { + CF_OBJC_FUNCDISPATCH2(__kCFReadStreamTypeID, Boolean, stream, "setProperty:forKey:", propertyValue, propertyName); + return _CFStreamSetProperty((struct _CFStream *)stream, propertyName, propertyValue); +} + +CF_EXPORT +Boolean CFWriteStreamSetProperty(CFWriteStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue) { + CF_OBJC_FUNCDISPATCH2(__kCFWriteStreamTypeID, Boolean, stream, "setProperty:forKey:", propertyValue, propertyName); + return _CFStreamSetProperty((struct _CFStream *)stream, propertyName, propertyValue); +} + +static void _initializeClient(struct _CFStream *stream) { + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + if (!cb->schedule) return; // Do we wish to allow this? + stream->client = (struct _CFStreamClient *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(struct _CFStreamClient), 0); + memset(stream->client, 0, sizeof(struct _CFStreamClient)); +} + +/* If we add a setClient callback to the concrete stream callbacks, we must set/clear CALLING_CLIENT around it */ +__private_extern__ Boolean _CFStreamSetClient(struct _CFStream *stream, CFOptionFlags streamEvents, void (*clientCB)(struct _CFStream *, CFStreamEventType, void *), CFStreamClientContext *clientCallBackContext) { + + Boolean removingClient = (streamEvents == kCFStreamEventNone || clientCB == NULL || clientCallBackContext == NULL); + if (removingClient) { + clientCB = NULL; + streamEvents = kCFStreamEventNone; + clientCallBackContext = NULL; + } + if (!stream->client) { + if (removingClient) { + // We have no client now, and we've been asked to add none??? + return TRUE; + } + _initializeClient(stream); + if (!stream->client) { + // Asynch not supported + return FALSE; + } + } + if (stream->client->cb && stream->client->cbContext.release) { + stream->client->cbContext.release(stream->client->cbContext.info); + } + stream->client->cb = clientCB; + if (clientCallBackContext) { + stream->client->cbContext.version = clientCallBackContext->version; + stream->client->cbContext.retain = clientCallBackContext->retain; + stream->client->cbContext.release = clientCallBackContext->release; + stream->client->cbContext.copyDescription = clientCallBackContext->copyDescription; + stream->client->cbContext.info = (clientCallBackContext->retain && clientCallBackContext->info) ? clientCallBackContext->retain(clientCallBackContext->info) : clientCallBackContext->info; + } else { + stream->client->cbContext.retain = NULL; + stream->client->cbContext.release = NULL; + stream->client->cbContext.copyDescription = NULL; + stream->client->cbContext.info = NULL; + } + if (stream->client->when != streamEvents) { + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + stream->client->when = streamEvents; + if (cb->requestEvents) { + cb->requestEvents(stream, streamEvents, _CFStreamGetInfoPointer(stream)); + } + } + return TRUE; +} + +CF_EXPORT Boolean CFReadStreamSetClient(CFReadStreamRef readStream, CFOptionFlags streamEvents, CFReadStreamClientCallBack clientCB, CFStreamClientContext *clientContext) { + CF_OBJC_FUNCDISPATCH3(__kCFReadStreamTypeID, Boolean, readStream, "_setCFClientFlags:callback:context:", streamEvents, clientCB, clientContext); + streamEvents &= ~kCFStreamEventCanAcceptBytes; + return _CFStreamSetClient((struct _CFStream *)readStream, streamEvents, (void (*)(struct _CFStream *, CFStreamEventType, void *))clientCB, clientContext); +} + +CF_EXPORT Boolean CFWriteStreamSetClient(CFWriteStreamRef writeStream, CFOptionFlags streamEvents, CFWriteStreamClientCallBack clientCB, CFStreamClientContext *clientContext) { + CF_OBJC_FUNCDISPATCH3(__kCFWriteStreamTypeID, Boolean, writeStream, "_setCFClientFlags:callback:context:", streamEvents, clientCB, clientContext); + streamEvents &= ~kCFStreamEventHasBytesAvailable; + return _CFStreamSetClient((struct _CFStream *)writeStream, streamEvents, (void (*)(struct _CFStream *, CFStreamEventType, void *))clientCB, clientContext); +} + +CF_INLINE void *_CFStreamGetClient(struct _CFStream *stream) { + if (stream->client) return stream->client->cbContext.info; + else return NULL; +} + +CF_EXPORT void *_CFReadStreamGetClient(CFReadStreamRef readStream) { + return _CFStreamGetClient((struct _CFStream *)readStream); +} + +CF_EXPORT void *_CFWriteStreamGetClient(CFWriteStreamRef writeStream) { + return _CFStreamGetClient((struct _CFStream *)writeStream); +} + + +__private_extern__ void _CFStreamScheduleWithRunLoop(struct _CFStream *stream, CFRunLoopRef runLoop, CFStringRef runLoopMode) { + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + + if (!stream->client) { + _initializeClient(stream); + if (!stream->client) return; // we don't support asynch. + } + + if (!stream->client->rlSource) { + + CFArrayRef key; + CFMutableArrayRef list; + CFTypeRef a[2]; + + a[0] = runLoop; + a[1] = runLoopMode; + + key = CFArrayCreate(kCFAllocatorSystemDefault, a, sizeof(a) / sizeof(a[0]), &kCFTypeArrayCallBacks); + + __CFSpinLock(&sSourceLock); + + if (!sSharedSources) + sSharedSources = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + list = (CFMutableArrayRef)CFDictionaryGetValue(sSharedSources, key); + if (list) { + stream->client->rlSource = (CFRunLoopSourceRef)CFRetain(((struct _CFStream*)CFArrayGetValueAtIndex(list, 0))->client->rlSource); + CFRetain(list); + } + else { + CFRunLoopSourceContext ctxt = { + 0, + NULL, + NULL, // Do not use CFRetain/CFRelease callbacks here; that will cause a retain loop + NULL, // Do not use CFRetain/CFRelease callbacks here; that will cause a retain loop + (CFStringRef(*)(const void *))CFCopyDescription, + NULL, + NULL, + NULL, + NULL, + (void(*)(void *))_CFStreamSignalEventSynch + }; + + list = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); + CFDictionaryAddValue(sSharedSources, key, list); + + ctxt.info = list; + + stream->client->rlSource = CFRunLoopSourceCreate(CFGetAllocator(stream), 0, &ctxt); + stream->client->whatToSignal = 0; + + CFRunLoopAddSource(runLoop, stream->client->rlSource, runLoopMode); + } + + CFArrayAppendValue(list, stream); + CFDictionaryAddValue(sSharedSources, stream, key); + + CFRelease(key); + CFRelease(list); + + __CFBitSet(stream->flags, SHARED_SOURCE); + + __CFSpinUnlock(&sSourceLock); + } + else if (__CFBitIsSet(stream->flags, SHARED_SOURCE)) { + + CFArrayRef key; + CFMutableArrayRef list; + CFIndex c, i; + + CFAllocatorRef alloc = CFGetAllocator(stream); + CFRunLoopSourceContext ctxt = { + 0, + (void *)stream, + NULL, // Do not use CFRetain/CFRelease callbacks here; that will cause a retain loop + NULL, // Do not use CFRetain/CFRelease callbacks here; that will cause a retain loop + (CFStringRef(*)(const void *))CFCopyDescription, + NULL, + NULL, + NULL, + NULL, + (void(*)(void *))_CFStreamSignalEventSynch + }; + + __CFSpinLock(&sSourceLock); + + key = (CFArrayRef)CFRetain((CFTypeRef)CFDictionaryGetValue(sSharedSources, stream)); + list = (CFMutableArrayRef)CFDictionaryGetValue(sSharedSources, key); + + c = CFArrayGetCount(list); + i = CFArrayGetFirstIndexOfValue(list, CFRangeMake(0, c), stream); + if (i != kCFNotFound) { + CFArrayRemoveValueAtIndex(list, i); + c--; + } + + if (!c) { + CFRunLoopRemoveSource((CFRunLoopRef)CFArrayGetValueAtIndex(key, 0), stream->client->rlSource, (CFStringRef)CFArrayGetValueAtIndex(key, 1)); + CFRunLoopSourceInvalidate(stream->client->rlSource); + CFDictionaryRemoveValue(sSharedSources, key); + } + + CFDictionaryRemoveValue(sSharedSources, stream); + + CFRelease(stream->client->rlSource); + __CFBitClear(stream->flags, SHARED_SOURCE); + + __CFSpinUnlock(&sSourceLock); + + stream->client->rlSource = CFRunLoopSourceCreate(alloc, 0, &ctxt); + + CFRunLoopAddSource((CFRunLoopRef)CFArrayGetValueAtIndex(key, 0), stream->client->rlSource, (CFStringRef)CFArrayGetValueAtIndex(key, 1)); + + CFRelease(key); + + CFRunLoopAddSource(runLoop, stream->client->rlSource, runLoopMode); + } else { + CFRunLoopAddSource(runLoop, stream->client->rlSource, runLoopMode); + } + + if (!stream->client->runLoopsAndModes) { + stream->client->runLoopsAndModes = CFArrayCreateMutable(CFGetAllocator(stream), 0, &kCFTypeArrayCallBacks); + } + CFArrayAppendValue(stream->client->runLoopsAndModes, runLoop); + CFArrayAppendValue(stream->client->runLoopsAndModes, runLoopMode); + + if (cb->schedule) { + __CFBitSet(stream->flags, CALLING_CLIENT); + cb->schedule(stream, runLoop, runLoopMode, _CFStreamGetInfoPointer(stream)); + __CFBitClear(stream->flags, CALLING_CLIENT); + } +} + +CF_EXPORT void CFReadStreamScheduleWithRunLoop(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode) { + CF_OBJC_FUNCDISPATCH2(__kCFReadStreamTypeID, void, stream, "_scheduleInCFRunLoop:forMode:", runLoop, runLoopMode); + _CFStreamScheduleWithRunLoop((struct _CFStream *)stream, runLoop, runLoopMode); +} + +CF_EXPORT void CFWriteStreamScheduleWithRunLoop(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode) { + CF_OBJC_FUNCDISPATCH2(__kCFWriteStreamTypeID, void, stream, "_scheduleInCFRunLoop:forMode:", runLoop, runLoopMode); + _CFStreamScheduleWithRunLoop((struct _CFStream *)stream, runLoop, runLoopMode); +} + + +__private_extern__ void _CFStreamUnscheduleFromRunLoop(struct _CFStream *stream, CFRunLoopRef runLoop, CFStringRef runLoopMode) { + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + if (!stream->client) return; + if (!stream->client->rlSource) return; + + if (!__CFBitIsSet(stream->flags, SHARED_SOURCE)) { + CFRunLoopRemoveSource(runLoop, stream->client->rlSource, runLoopMode); + } else { + CFArrayRef key; + CFMutableArrayRef list; + CFIndex c, i; + + __CFSpinLock(&sSourceLock); + + key = (CFArrayRef)CFDictionaryGetValue(sSharedSources, stream); + list = (CFMutableArrayRef)CFDictionaryGetValue(sSharedSources, key); + + c = CFArrayGetCount(list); + i = CFArrayGetFirstIndexOfValue(list, CFRangeMake(0, c), stream); + if (i != kCFNotFound) { + CFArrayRemoveValueAtIndex(list, i); + c--; + } + + if (!c) { + CFRunLoopRemoveSource(runLoop, stream->client->rlSource, runLoopMode); + CFRunLoopSourceInvalidate(stream->client->rlSource); + CFDictionaryRemoveValue(sSharedSources, key); + } + + CFDictionaryRemoveValue(sSharedSources, stream); + + CFRelease(stream->client->rlSource); + stream->client->rlSource = NULL; + __CFBitClear(stream->flags, SHARED_SOURCE); + + __CFSpinUnlock(&sSourceLock); + } + + _CFStreamRemoveRunLoopAndModeFromArray(stream->client->runLoopsAndModes, runLoop, runLoopMode); + + if (cb->unschedule) { + cb->unschedule(stream, runLoop, runLoopMode, _CFStreamGetInfoPointer(stream)); + } +} + +CF_EXPORT void CFReadStreamUnscheduleFromRunLoop(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode) { + CF_OBJC_FUNCDISPATCH2(__kCFReadStreamTypeID, void, stream, "_unscheduleFromCFRunLoop:forMode:", runLoop, runLoopMode); + _CFStreamUnscheduleFromRunLoop((struct _CFStream *)stream, runLoop, runLoopMode); +} + +void CFWriteStreamUnscheduleFromRunLoop(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode) { + CF_OBJC_FUNCDISPATCH2(__kCFWriteStreamTypeID, void, stream, "_unscheduleFromCFRunLoop:forMode:", runLoop, runLoopMode); + _CFStreamUnscheduleFromRunLoop((struct _CFStream *)stream, runLoop, runLoopMode); +} + +static void waitForOpen(struct _CFStream *stream) { + CFRunLoopRef runLoop = CFRunLoopGetCurrent(); + CFStringRef privateMode = CFSTR("_kCFStreamBlockingOpenMode"); + _CFStreamScheduleWithRunLoop(stream, runLoop, privateMode); + // We cannot call _CFStreamGetStatus, because that tries to set/clear CALLING_CLIENT, which should be set around this entire call (we're within a call from the client). This should be o.k., because we're running the run loop, so our status code should be being updated in a timely fashion.... + while (__CFStreamGetStatus(stream) == kCFStreamStatusOpening) { + CFRunLoopRunInMode(privateMode, 1e+20, TRUE); + } + _CFStreamUnscheduleFromRunLoop(stream, runLoop, privateMode); +} + +CF_INLINE CFArrayRef _CFStreamGetRunLoopsAndModes(struct _CFStream *stream) { + if (stream->client) return stream->client->runLoopsAndModes; + else return NULL; +} + +CF_EXPORT CFArrayRef _CFReadStreamGetRunLoopsAndModes(CFReadStreamRef readStream) { + return _CFStreamGetRunLoopsAndModes((struct _CFStream *)readStream); +} + +CF_EXPORT CFArrayRef _CFWriteStreamGetRunLoopsAndModes(CFWriteStreamRef writeStream) { + return _CFStreamGetRunLoopsAndModes((struct _CFStream *)writeStream); +} + +CF_EXPORT void CFReadStreamSignalEvent(CFReadStreamRef stream, CFStreamEventType event, const void *error) { + _CFStreamSignalEvent((struct _CFStream *)stream, event, (CFErrorRef)error, TRUE); +} + +CF_EXPORT void CFWriteStreamSignalEvent(CFWriteStreamRef stream, CFStreamEventType event, const void *error) { + _CFStreamSignalEvent((struct _CFStream *)stream, event, (CFErrorRef)error, TRUE); +} + +CF_EXPORT void _CFReadStreamSignalEventDelayed(CFReadStreamRef stream, CFStreamEventType event, const void *error) { + _CFStreamSignalEvent((struct _CFStream *)stream, event, (CFErrorRef)error, FALSE); +} + +CF_EXPORT void _CFReadStreamClearEvent(CFReadStreamRef readStream, CFStreamEventType event) { + struct _CFStream *stream = (struct _CFStream *)readStream; + if (stream->client) { + stream->client->whatToSignal &= ~event; + } +} + +CF_EXPORT void _CFWriteStreamSignalEventDelayed(CFWriteStreamRef stream, CFStreamEventType event, const void *error) { + _CFStreamSignalEvent((struct _CFStream *)stream, event, (CFErrorRef)error, FALSE); +} + +CF_EXPORT void *CFReadStreamGetInfoPointer(CFReadStreamRef stream) { + return _CFStreamGetInfoPointer((struct _CFStream *)stream); +} + +CF_EXPORT void *CFWriteStreamGetInfoPointer(CFWriteStreamRef stream) { + return _CFStreamGetInfoPointer((struct _CFStream *)stream); +} + +/* CF_EXPORT */ +void _CFStreamSourceScheduleWithRunLoop(CFRunLoopSourceRef source, CFMutableArrayRef runLoopsAndModes, CFRunLoopRef runLoop, CFStringRef runLoopMode) +{ + CFIndex count; + CFRange range; + + count = CFArrayGetCount(runLoopsAndModes); + range = CFRangeMake(0, count); + + while (range.length) { + + CFIndex i = CFArrayGetFirstIndexOfValue(runLoopsAndModes, range, runLoop); + + if (i == kCFNotFound) + break; + + if (CFEqual(CFArrayGetValueAtIndex(runLoopsAndModes, i + 1), runLoopMode)) + return; + + range.location = i + 2; + range.length = count - range.location; + } + + // Add the new values. + CFArrayAppendValue(runLoopsAndModes, runLoop); + CFArrayAppendValue(runLoopsAndModes, runLoopMode); + + // Schedule the source on the new loop and mode. + if (source) + CFRunLoopAddSource(runLoop, source, runLoopMode); +} + + +/* CF_EXPORT */ +void _CFStreamSourceUnscheduleFromRunLoop(CFRunLoopSourceRef source, CFMutableArrayRef runLoopsAndModes, CFRunLoopRef runLoop, CFStringRef runLoopMode) +{ + CFIndex count; + CFRange range; + + count = CFArrayGetCount(runLoopsAndModes); + range = CFRangeMake(0, count); + + while (range.length) { + + CFIndex i = CFArrayGetFirstIndexOfValue(runLoopsAndModes, range, runLoop); + + // If not found, it's not scheduled on it. + if (i == kCFNotFound) + return; + + // Make sure it is scheduled in this mode. + if (CFEqual(CFArrayGetValueAtIndex(runLoopsAndModes, i + 1), runLoopMode)) { + + // Remove mode and runloop from the list. + CFArrayReplaceValues(runLoopsAndModes, CFRangeMake(i, 2), NULL, 0); + + // Remove it from the runloop. + if (source) + CFRunLoopRemoveSource(runLoop, source, runLoopMode); + + return; + } + + range.location = i + 2; + range.length = count - range.location; + } +} + + +/* CF_EXPORT */ +void _CFStreamSourceScheduleWithAllRunLoops(CFRunLoopSourceRef source, CFArrayRef runLoopsAndModes) +{ + CFIndex i, count = CFArrayGetCount(runLoopsAndModes); + + if (!source) + return; + + for (i = 0; i < count; i += 2) { + + // Make sure it's scheduled on all the right loops and modes. + // Go through the array adding the source to all loops and modes. + CFRunLoopAddSource((CFRunLoopRef)CFArrayGetValueAtIndex(runLoopsAndModes, i), + source, + (CFStringRef)CFArrayGetValueAtIndex(runLoopsAndModes, i + 1)); + } +} + + +/* CF_EXPORT */ +void _CFStreamSourceUncheduleFromAllRunLoops(CFRunLoopSourceRef source, CFArrayRef runLoopsAndModes) +{ + CFIndex i, count = CFArrayGetCount(runLoopsAndModes); + + if (!source) + return; + + for (i = 0; i < count; i += 2) { + + // Go through the array removing the source from all loops and modes. + CFRunLoopRemoveSource((CFRunLoopRef)CFArrayGetValueAtIndex(runLoopsAndModes, i), + source, + (CFStringRef)CFArrayGetValueAtIndex(runLoopsAndModes, i + 1)); + } +} + +Boolean _CFStreamRemoveRunLoopAndModeFromArray(CFMutableArrayRef runLoopsAndModes, CFRunLoopRef rl, CFStringRef mode) { + CFIndex idx, cnt; + Boolean found = FALSE; + + if (!runLoopsAndModes) return FALSE; + + cnt = CFArrayGetCount(runLoopsAndModes); + for (idx = 0; idx + 1 < cnt; idx += 2) { + if (CFEqual(CFArrayGetValueAtIndex(runLoopsAndModes, idx), rl) && CFEqual(CFArrayGetValueAtIndex(runLoopsAndModes, idx + 1), mode)) { + CFArrayRemoveValueAtIndex(runLoopsAndModes, idx); + CFArrayRemoveValueAtIndex(runLoopsAndModes, idx); + found = TRUE; + break; + } + } + return found; +} + +// Used by NSStream to properly allocate the bridged objects +CF_EXPORT CFIndex _CFStreamInstanceSize(void) { + return sizeof(struct _CFStream); +} + +#if DEPLOYMENT_TARGET_WINDOWS + +__private_extern__ void __CFStreamCleanup(void) { + __CFSpinLock(&sSourceLock); + if (sSharedSources) { + CFIndex count = CFDictionaryGetCount(sSharedSources); + if (count == 0) { + // Only release if empty. If it's still holding streams (which would be a client + // bug leak), freeing this dict would free the streams, which then need to access the + // dict to remove themselves, which leads to a deadlock. + CFRelease(sSharedSources); + sSharedSources = NULL; + } else + fprintf(stderr, "*** CFNetwork is shutting down, but %ld streams are still scheduled.\n", count); + } + __CFSpinUnlock(&sSourceLock); +} + +#endif + +
diff --git a/CoreFoundation/CFStream.h b/CoreFoundation/CFStream.h new file mode 100644 index 0000000..584cbef --- /dev/null +++ b/CoreFoundation/CFStream.h
@@ -0,0 +1,299 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFStream.h + Copyright (c) 2000-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFSTREAM__) +#define __COREFOUNDATION_CFSTREAM__ 1 + +#include <CoreFoundation/CFBase.h> +#include <CoreFoundation/CFString.h> +#include <CoreFoundation/CFDictionary.h> +#include <CoreFoundation/CFURL.h> +#include <CoreFoundation/CFRunLoop.h> +#include <CoreFoundation/CFSocket.h> +#include <CoreFoundation/CFError.h> + +CF_EXTERN_C_BEGIN + +enum { + kCFStreamStatusNotOpen = 0, + kCFStreamStatusOpening, /* open is in-progress */ + kCFStreamStatusOpen, + kCFStreamStatusReading, + kCFStreamStatusWriting, + kCFStreamStatusAtEnd, /* no further bytes can be read/written */ + kCFStreamStatusClosed, + kCFStreamStatusError +}; +typedef CFIndex CFStreamStatus; + +enum { + kCFStreamEventNone = 0, + kCFStreamEventOpenCompleted = 1, + kCFStreamEventHasBytesAvailable = 2, + kCFStreamEventCanAcceptBytes = 4, + kCFStreamEventErrorOccurred = 8, + kCFStreamEventEndEncountered = 16 +}; +typedef CFOptionFlags CFStreamEventType; + +typedef struct { + CFIndex version; + void *info; + void *(*retain)(void *info); + void (*release)(void *info); + CFStringRef (*copyDescription)(void *info); +} CFStreamClientContext; + +typedef struct __CFReadStream * CFReadStreamRef; +typedef struct __CFWriteStream * CFWriteStreamRef; + +typedef void (*CFReadStreamClientCallBack)(CFReadStreamRef stream, CFStreamEventType type, void *clientCallBackInfo); +typedef void (*CFWriteStreamClientCallBack)(CFWriteStreamRef stream, CFStreamEventType type, void *clientCallBackInfo); + +CF_EXPORT +CFTypeID CFReadStreamGetTypeID(void); +CF_EXPORT +CFTypeID CFWriteStreamGetTypeID(void); + +/* Memory streams */ + +/* Value will be a CFData containing all bytes thusfar written; used to recover the data written to a memory write stream. */ +CF_EXPORT +const CFStringRef kCFStreamPropertyDataWritten; + +/* Pass kCFAllocatorNull for bytesDeallocator to prevent CFReadStream from deallocating bytes; otherwise, CFReadStream will deallocate bytes when the stream is destroyed */ +CF_EXPORT +CFReadStreamRef CFReadStreamCreateWithBytesNoCopy(CFAllocatorRef alloc, const UInt8 *bytes, CFIndex length, CFAllocatorRef bytesDeallocator); + +/* The stream writes into the buffer given; when bufferCapacity is exhausted, the stream is exhausted (status becomes kCFStreamStatusAtEnd) */ +CF_EXPORT +CFWriteStreamRef CFWriteStreamCreateWithBuffer(CFAllocatorRef alloc, UInt8 *buffer, CFIndex bufferCapacity); + +/* New buffers are allocated from bufferAllocator as bytes are written to the stream. At any point, you can recover the bytes thusfar written by asking for the property kCFStreamPropertyDataWritten, above */ +CF_EXPORT +CFWriteStreamRef CFWriteStreamCreateWithAllocatedBuffers(CFAllocatorRef alloc, CFAllocatorRef bufferAllocator); + +/* File streams */ +CF_EXPORT +CFReadStreamRef CFReadStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL); +CF_EXPORT +CFWriteStreamRef CFWriteStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL); +CF_EXPORT +void CFStreamCreateBoundPair(CFAllocatorRef alloc, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream, CFIndex transferBufferSize); + +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED +/* Property for file write streams; value should be a CFBoolean. Set to TRUE to append to a file, rather than to replace its contents */ +CF_EXPORT +const CFStringRef kCFStreamPropertyAppendToFile; +#endif + +#if MAC_OS_X_VERSION_10_3 <= MAC_OS_X_VERSION_MAX_ALLOWED + +CF_EXPORT const CFStringRef kCFStreamPropertyFileCurrentOffset AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // Value is a CFNumber + +#endif + +/* Socket stream properties */ + +/* Value will be a CFData containing the native handle */ +CF_EXPORT +const CFStringRef kCFStreamPropertySocketNativeHandle; + +/* Value will be a CFString, or NULL if unknown */ +CF_EXPORT +const CFStringRef kCFStreamPropertySocketRemoteHostName; + +/* Value will be a CFNumber, or NULL if unknown */ +CF_EXPORT +const CFStringRef kCFStreamPropertySocketRemotePortNumber; + +/* Socket streams; the returned streams are paired such that they use the same socket; pass NULL if you want only the read stream or the write stream */ +CF_EXPORT +void CFStreamCreatePairWithSocket(CFAllocatorRef alloc, CFSocketNativeHandle sock, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream); +CF_EXPORT +void CFStreamCreatePairWithSocketToHost(CFAllocatorRef alloc, CFStringRef host, UInt32 port, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream); +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED +CF_EXPORT +void CFStreamCreatePairWithPeerSocketSignature(CFAllocatorRef alloc, const CFSocketSignature *signature, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream); +#endif + + +/* Returns the current state of the stream */ +CF_EXPORT +CFStreamStatus CFReadStreamGetStatus(CFReadStreamRef stream); +CF_EXPORT +CFStreamStatus CFWriteStreamGetStatus(CFWriteStreamRef stream); + +/* Returns NULL if no error has occurred; otherwise returns the error. */ +CF_EXPORT +CFErrorRef CFReadStreamCopyError(CFReadStreamRef stream) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; +CF_EXPORT +CFErrorRef CFWriteStreamCopyError(CFWriteStreamRef stream) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +/* Returns success/failure. Opening a stream causes it to reserve all the system + resources it requires. If the stream can open non-blocking, this will always + return TRUE; listen to the run loop source to find out when the open completes + and whether it was successful, or poll using CFRead/WriteStreamGetStatus(), waiting + for a status of kCFStreamStatusOpen or kCFStreamStatusError. */ +CF_EXPORT +Boolean CFReadStreamOpen(CFReadStreamRef stream); +CF_EXPORT +Boolean CFWriteStreamOpen(CFWriteStreamRef stream); + +/* Terminates the flow of bytes; releases any system resources required by the + stream. The stream may not fail to close. You may call CFStreamClose() to + effectively abort a stream. */ +CF_EXPORT +void CFReadStreamClose(CFReadStreamRef stream); +CF_EXPORT +void CFWriteStreamClose(CFWriteStreamRef stream); + +/* Whether there is data currently available for reading; returns TRUE if it's + impossible to tell without trying */ +CF_EXPORT +Boolean CFReadStreamHasBytesAvailable(CFReadStreamRef stream); + +/* Returns the number of bytes read, or -1 if an error occurs preventing any + bytes from being read, or 0 if the stream's end was encountered. + It is an error to try and read from a stream that hasn't been opened first. + This call will block until at least one byte is available; it will NOT block + until the entire buffer can be filled. To avoid blocking, either poll using + CFReadStreamHasBytesAvailable() or use the run loop and listen for the + kCFStreamCanRead event for notification of data available. */ +CF_EXPORT +CFIndex CFReadStreamRead(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength); + +/* Returns a pointer to an internal buffer if possible (setting *numBytesRead + to the length of the returned buffer), otherwise returns NULL; guaranteed + to return in O(1). Bytes returned in the buffer are considered read from + the stream; if maxBytesToRead is greater than 0, not more than maxBytesToRead + will be returned. If maxBytesToRead is less than or equal to zero, as many bytes + as are readily available will be returned. The returned buffer is good only + until the next stream operation called on the stream. Caller should neither + change the contents of the returned buffer nor attempt to deallocate the buffer; + it is still owned by the stream. */ +CF_EXPORT +const UInt8 *CFReadStreamGetBuffer(CFReadStreamRef stream, CFIndex maxBytesToRead, CFIndex *numBytesRead); + +/* Whether the stream can currently be written to without blocking; + returns TRUE if it's impossible to tell without trying */ +CF_EXPORT +Boolean CFWriteStreamCanAcceptBytes(CFWriteStreamRef stream); + +/* Returns the number of bytes successfully written, -1 if an error has + occurred, or 0 if the stream has been filled to capacity (for fixed-length + streams). If the stream is not full, this call will block until at least + one byte is written. To avoid blocking, either poll via CFWriteStreamCanAcceptBytes + or use the run loop and listen for the kCFStreamCanWrite event. */ +CF_EXPORT +CFIndex CFWriteStreamWrite(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength); + +/* Particular streams can name properties and assign meanings to them; you + access these properties through the following calls. A property is any interesting + information about the stream other than the data being transmitted itself. + Examples include the headers from an HTTP transmission, or the expected + number of bytes, or permission information, etc. Properties that can be set + configure the behavior of the stream, and may only be settable at particular times + (like before the stream has been opened). See the documentation for particular + properties to determine their get- and set-ability. */ +CF_EXPORT +CFTypeRef CFReadStreamCopyProperty(CFReadStreamRef stream, CFStringRef propertyName); +CF_EXPORT +CFTypeRef CFWriteStreamCopyProperty(CFWriteStreamRef stream, CFStringRef propertyName); + +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED +/* Returns TRUE if the stream recognizes and accepts the given property-value pair; + FALSE otherwise. */ +CF_EXPORT +Boolean CFReadStreamSetProperty(CFReadStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue); +CF_EXPORT +Boolean CFWriteStreamSetProperty(CFWriteStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue); +#endif + +/* Asynchronous processing - If you wish to neither poll nor block, you may register + a client to hear about interesting events that occur on a stream. Only one client + per stream is allowed; registering a new client replaces the previous one. + + Once you have set a client, you need to schedule a run loop on which that client + can be notified. You may schedule multiple run loops (for instance, if you are + using a thread pool). The client callback will be triggered via one of the scheduled + run loops; It is the caller's responsibility to ensure that at least one of the + scheduled run loops is being run. + + NOTE: Unlike other CoreFoundation APIs, pasing a NULL clientContext here will remove + the client. If you do not care about the client context (i.e. your only concern + is that your callback be called), you should pass in a valid context where every + entry is 0 or NULL. + +*/ + +CF_EXPORT +Boolean CFReadStreamSetClient(CFReadStreamRef stream, CFOptionFlags streamEvents, CFReadStreamClientCallBack clientCB, CFStreamClientContext *clientContext); +CF_EXPORT +Boolean CFWriteStreamSetClient(CFWriteStreamRef stream, CFOptionFlags streamEvents, CFWriteStreamClientCallBack clientCB, CFStreamClientContext *clientContext); + +CF_EXPORT +void CFReadStreamScheduleWithRunLoop(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode); +CF_EXPORT +void CFWriteStreamScheduleWithRunLoop(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode); + +CF_EXPORT +void CFReadStreamUnscheduleFromRunLoop(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode); +CF_EXPORT +void CFWriteStreamUnscheduleFromRunLoop(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode); + + +/* The following API is deprecated starting in 10.5; please use CFRead/WriteStreamCopyError(), above, instead */ +enum { + kCFStreamErrorDomainCustom = -1, /* custom to the kind of stream in question */ + kCFStreamErrorDomainPOSIX = 1, /* POSIX errno; interpret using <sys/errno.h> */ + kCFStreamErrorDomainMacOSStatus /* OSStatus type from Carbon APIs; interpret using <MacTypes.h> */ +}; +typedef CFIndex CFStreamErrorDomain; + +typedef struct { + CFIndex domain; + SInt32 error; +} CFStreamError; +CF_EXPORT +CFStreamError CFReadStreamGetError(CFReadStreamRef stream); +CF_EXPORT +CFStreamError CFWriteStreamGetError(CFWriteStreamRef stream); + + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFSTREAM__ */
diff --git a/CoreFoundation/CFStreamAbstract.h b/CoreFoundation/CFStreamAbstract.h new file mode 100644 index 0000000..e800867 --- /dev/null +++ b/CoreFoundation/CFStreamAbstract.h
@@ -0,0 +1,214 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFStreamAbstract.h + Copyright (c) 2000-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFSTREAMABSTRACT__) +#define __COREFOUNDATION_CFSTREAMABSTRACT__ 1 + +#include <CoreFoundation/CFStream.h> + +CF_EXTERN_C_BEGIN + +/* During a stream's lifetime, the open callback will be called once, followed by any number of openCompleted calls (until openCompleted returns TRUE). Then any number of read/canRead or write/canWrite calls, then a single close call. copyProperty can be called at any time. prepareAsynch will be called exactly once when the stream's client is first configured. + + Expected semantics: + - open reserves any system resources that are needed. The stream may start the process of opening, returning TRUE immediately and setting openComplete to FALSE. When the open completes, _CFStreamSignalEvent should be called passing kCFStreamOpenCompletedEvent. openComplete should be set to TRUE only if the open operation completed in its entirety. + - openCompleted will only be called after open has been called, but before any kCFStreamOpenCompletedEvent has been received. Return TRUE, setting error.code to 0, if the open operation has completed. Return TRUE, setting error to the correct error code and domain if the open operation completed, but failed. Return FALSE if the open operation is still in-progress. If your open ever fails to complete (i.e. sets openComplete to FALSE), you must be implement the openCompleted callback. + - read should read into the given buffer, returning the number of bytes successfully read. read must block until at least one byte is available, but should not block until the entire buffer is filled; zero should only be returned if end-of-stream is encountered. atEOF should be set to true if the EOF is encountered, false otherwise. error.code should be set to zero if no error occurs; otherwise, error should be set to the appropriate values. + - getBuffer is an optimization to return an internal buffer of bytes read from the stream, and may return NULL. getBuffer itself may be NULL if the concrete implementation does not wish to provide an internal buffer. If implemented, it should set numBytesRead to the number of bytes available in the internal buffer (but should not exceed maxBytesToRead) and return a pointer to the base of the bytes. + - canRead will only be called once openCompleted reports that the stream has been successfully opened (or the initial open call succeeded). It should return whether there are bytes that can be read without blocking. + - write should write the bytes in the given buffer to the device, returning the number of bytes successfully written. write must block until at least one byte is written. error.code should be set to zero if no error occurs; otherwise, error should be set to the appropriate values. + - close should close the device, releasing any reserved system resources. close cannot fail (it may be called to abort the stream), and may be called at any time after open has been called. It will only be called once. + - copyProperty should return the value for the given property, or NULL if none exists. Composite streams (streams built on top of other streams) should take care to call CFStreamCopyProperty on the base stream if they do not recognize the property given, to give the underlying stream a chance to respond. + + In all cases, errors returned by reference will be initialized to NULL by the caller, and if they are set to non-NULL, will + be released by the caller +*/ + +typedef struct { + CFIndex version; /* == 2 */ + + void *(*create)(CFReadStreamRef stream, void *info); + void (*finalize)(CFReadStreamRef stream, void *info); + CFStringRef (*copyDescription)(CFReadStreamRef stream, void *info); + + Boolean (*open)(CFReadStreamRef stream, CFErrorRef *error, Boolean *openComplete, void *info); + Boolean (*openCompleted)(CFReadStreamRef stream, CFErrorRef *error, void *info); + CFIndex (*read)(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFErrorRef *error, Boolean *atEOF, void *info); + const UInt8 *(*getBuffer)(CFReadStreamRef stream, CFIndex maxBytesToRead, CFIndex *numBytesRead, CFErrorRef *error, Boolean *atEOF, void *info); + Boolean (*canRead)(CFReadStreamRef stream, CFErrorRef *error, void *info); + void (*close)(CFReadStreamRef stream, void *info); + + CFTypeRef (*copyProperty)(CFReadStreamRef stream, CFStringRef propertyName, void *info); + Boolean (*setProperty)(CFReadStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue, void *info); + + void (*requestEvents)(CFReadStreamRef stream, CFOptionFlags streamEvents, void *info); + void (*schedule)(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); + void (*unschedule)(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); +} CFReadStreamCallBacks; + +typedef struct { + CFIndex version; /* == 2 */ + + void *(*create)(CFWriteStreamRef stream, void *info); + void (*finalize)(CFWriteStreamRef stream, void *info); + CFStringRef (*copyDescription)(CFWriteStreamRef stream, void *info); + + Boolean (*open)(CFWriteStreamRef stream, CFErrorRef *error, Boolean *openComplete, void *info); + Boolean (*openCompleted)(CFWriteStreamRef stream, CFErrorRef *error, void *info); + CFIndex (*write)(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFErrorRef *error, void *info); + Boolean (*canWrite)(CFWriteStreamRef stream, CFErrorRef *error, void *info); + void (*close)(CFWriteStreamRef stream, void *info); + + CFTypeRef (*copyProperty)(CFWriteStreamRef stream, CFStringRef propertyName, void *info); + Boolean (*setProperty)(CFWriteStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue, void *info); + + void (*requestEvents)(CFWriteStreamRef stream, CFOptionFlags streamEvents, void *info); + void (*schedule)(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); + void (*unschedule)(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); +} CFWriteStreamCallBacks; + +// Primitive creation mechanisms. +CF_EXPORT +CFReadStreamRef CFReadStreamCreate(CFAllocatorRef alloc, const CFReadStreamCallBacks *callbacks, void *info); +CF_EXPORT +CFWriteStreamRef CFWriteStreamCreate(CFAllocatorRef alloc, const CFWriteStreamCallBacks *callbacks, void *info); + +/* All the functions below can only be called when you are sure the stream in question was created via + CFReadStreamCreate() or CFWriteStreamCreate(), above. They are NOT safe for toll-free bridged objects, + so the caller must be sure the argument passed is not such an object. */ + +// To be called by the concrete stream implementation (the callbacks) when an event occurs. error may be NULL if event != kCFStreamEventErrorOccurred +// error should be a CFErrorRef if the callbacks are version 2 or later; otherwise it should be a (CFStreamError *). +CF_EXPORT +void CFReadStreamSignalEvent(CFReadStreamRef stream, CFStreamEventType event, const void *error); +CF_EXPORT +void CFWriteStreamSignalEvent(CFWriteStreamRef stream, CFStreamEventType event, const void *error); + +// These require that the stream allow the run loop to run once before delivering the event to its client. +// See the comment above CFRead/WriteStreamSignalEvent for interpretation of the error argument. +CF_EXPORT +void _CFReadStreamSignalEventDelayed(CFReadStreamRef stream, CFStreamEventType event, const void *error); +CF_EXPORT +void _CFWriteStreamSignalEventDelayed(CFWriteStreamRef stream, CFStreamEventType event, const void *error); + +CF_EXPORT +void _CFReadStreamClearEvent(CFReadStreamRef stream, CFStreamEventType event); +// Write variant not currently needed +//CF_EXPORT +//void _CFWriteStreamClearEvent(CFWriteStreamRef stream, CFStreamEventType event); + +// Convenience for concrete implementations to extract the info pointer given the stream. +CF_EXPORT +void *CFReadStreamGetInfoPointer(CFReadStreamRef stream); +CF_EXPORT +void *CFWriteStreamGetInfoPointer(CFWriteStreamRef stream); + +// Returns the client info pointer currently set on the stream. These should probably be made public one day. +CF_EXPORT +void *_CFReadStreamGetClient(CFReadStreamRef readStream); +CF_EXPORT +void *_CFWriteStreamGetClient(CFWriteStreamRef writeStream); + +// Returns an array of the runloops and modes on which the stream is currently scheduled +CF_EXPORT +CFArrayRef _CFReadStreamGetRunLoopsAndModes(CFReadStreamRef readStream); +CF_EXPORT +CFArrayRef _CFWriteStreamGetRunLoopsAndModes(CFWriteStreamRef writeStream); + +/* Deprecated versions; here for backwards compatibility. */ +typedef struct { + CFIndex version; /* == 1 */ + void *(*create)(CFReadStreamRef stream, void *info); + void (*finalize)(CFReadStreamRef stream, void *info); + CFStringRef (*copyDescription)(CFReadStreamRef stream, void *info); + Boolean (*open)(CFReadStreamRef stream, CFStreamError *error, Boolean *openComplete, void *info); + Boolean (*openCompleted)(CFReadStreamRef stream, CFStreamError *error, void *info); + CFIndex (*read)(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, Boolean *atEOF, void *info); + const UInt8 *(*getBuffer)(CFReadStreamRef stream, CFIndex maxBytesToRead, CFIndex *numBytesRead, CFStreamError *error, Boolean *atEOF, void *info); + Boolean (*canRead)(CFReadStreamRef stream, void *info); + void (*close)(CFReadStreamRef stream, void *info); + CFTypeRef (*copyProperty)(CFReadStreamRef stream, CFStringRef propertyName, void *info); + Boolean (*setProperty)(CFReadStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue, void *info); + void (*requestEvents)(CFReadStreamRef stream, CFOptionFlags streamEvents, void *info); + void (*schedule)(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); + void (*unschedule)(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); +} CFReadStreamCallBacksV1; + +typedef struct { + CFIndex version; /* == 1 */ + void *(*create)(CFWriteStreamRef stream, void *info); + void (*finalize)(CFWriteStreamRef stream, void *info); + CFStringRef (*copyDescription)(CFWriteStreamRef stream, void *info); + Boolean (*open)(CFWriteStreamRef stream, CFStreamError *error, Boolean *openComplete, void *info); + Boolean (*openCompleted)(CFWriteStreamRef stream, CFStreamError *error, void *info); + CFIndex (*write)(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, void *info); + Boolean (*canWrite)(CFWriteStreamRef stream, void *info); + void (*close)(CFWriteStreamRef stream, void *info); + CFTypeRef (*copyProperty)(CFWriteStreamRef stream, CFStringRef propertyName, void *info); + Boolean (*setProperty)(CFWriteStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue, void *info); + void (*requestEvents)(CFWriteStreamRef stream, CFOptionFlags streamEvents, void *info); + void (*schedule)(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); + void (*unschedule)(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); +} CFWriteStreamCallBacksV1; + +typedef struct { + CFIndex version; /* == 0 */ + Boolean (*open)(CFReadStreamRef stream, CFStreamError *error, Boolean *openComplete, void *info); + Boolean (*openCompleted)(CFReadStreamRef stream, CFStreamError *error, void *info); + CFIndex (*read)(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, Boolean *atEOF, void *info); + const UInt8 *(*getBuffer)(CFReadStreamRef stream, CFIndex maxBytesToRead, CFIndex *numBytesRead, CFStreamError *error, Boolean *atEOF, void *info); + Boolean (*canRead)(CFReadStreamRef stream, void *info); + void (*close)(CFReadStreamRef stream, void *info); + CFTypeRef (*copyProperty)(CFReadStreamRef stream, CFStringRef propertyName, void *info); + void (*schedule)(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); + void (*unschedule)(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); +} CFReadStreamCallBacksV0; + +typedef struct { + CFIndex version; /* == 0 */ + Boolean (*open)(CFWriteStreamRef stream, CFStreamError *error, Boolean *openComplete, void *info); + Boolean (*openCompleted)(CFWriteStreamRef stream, CFStreamError *error, void *info); + CFIndex (*write)(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, void *info); + Boolean (*canWrite)(CFWriteStreamRef stream, void *info); + void (*close)(CFWriteStreamRef stream, void *info); + CFTypeRef (*copyProperty)(CFWriteStreamRef stream, CFStringRef propertyName, void *info); + void (*schedule)(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); + void (*unschedule)(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); +} CFWriteStreamCallBacksV0; + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFSTREAMABSTRACT__ */
diff --git a/CoreFoundation/CFStreamInternal.h b/CoreFoundation/CFStreamInternal.h new file mode 100644 index 0000000..2eacd31 --- /dev/null +++ b/CoreFoundation/CFStreamInternal.h
@@ -0,0 +1,83 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +#if !defined(__COREFOUNDATION_CFSTREAMINTERNAL__) +#define __COREFOUNDATION_CFSTREAMINTERNAL__ 1 + +#include <CoreFoundation/CFStreamAbstract.h> +#include <CoreFoundation/CFStreamPriv.h> +#include <CoreFoundation/CFBase.h> +#include <CoreFoundation/CFRuntime.h> + +CF_EXTERN_C_BEGIN + + +// Older versions of the callbacks; v0 callbacks match v1 callbacks, except that create, finalize, and copyDescription are missing. +typedef Boolean (*_CFStreamCBOpenV1)(struct _CFStream *stream, CFStreamError *error, Boolean *openComplete, void *info); +typedef Boolean (*_CFStreamCBOpenCompletedV1)(struct _CFStream *stream, CFStreamError *error, void *info); +typedef CFIndex (*_CFStreamCBReadV1)(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, Boolean *atEOF, void *info); +typedef const UInt8 *(*_CFStreamCBGetBufferV1)(CFReadStreamRef sream, CFIndex maxBytesToRead, CFIndex *numBytesRead, CFStreamError *error, Boolean *atEOF, void *info); +typedef Boolean (*_CFStreamCBCanReadV1)(CFReadStreamRef, void *info); +typedef CFIndex (*_CFStreamCBWriteV1)(CFWriteStreamRef, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, void *info); +typedef Boolean (*_CFStreamCBCanWriteV1)(CFWriteStreamRef, void *info); + +struct _CFStreamCallBacksV1 { + CFIndex version; + void *(*create)(struct _CFStream *stream, void *info); + void (*finalize)(struct _CFStream *stream, void *info); + CFStringRef (*copyDescription)(struct _CFStream *stream, void *info); + + _CFStreamCBOpenV1 open; + _CFStreamCBOpenCompletedV1 openCompleted; + _CFStreamCBReadV1 read; + _CFStreamCBGetBufferV1 getBuffer; + _CFStreamCBCanReadV1 canRead; + _CFStreamCBWriteV1 write; + _CFStreamCBCanWriteV1 canWrite; + void (*close)(struct _CFStream *stream, void *info); + + CFTypeRef (*copyProperty)(struct _CFStream *stream, CFStringRef propertyName, void *info); + Boolean (*setProperty)(struct _CFStream *stream, CFStringRef propertyName, CFTypeRef propertyValue, void *info); + void (*requestEvents)(struct _CFStream *stream, CFOptionFlags events, void *info); + void (*schedule)(struct _CFStream *stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); + void (*unschedule)(struct _CFStream *stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); +}; + +// These two are defined in CFSocketStream.c because that's where the glue for CFNetwork is. +__private_extern__ CFErrorRef _CFErrorFromStreamError(CFAllocatorRef alloc, CFStreamError *err); +__private_extern__ CFStreamError _CFStreamErrorFromError(CFErrorRef error); + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFSTREAMINTERNAL__ */ + +
diff --git a/CoreFoundation/CFStreamPriv.h b/CoreFoundation/CFStreamPriv.h new file mode 100644 index 0000000..ae7099b --- /dev/null +++ b/CoreFoundation/CFStreamPriv.h
@@ -0,0 +1,208 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFStreamPriv.h + Copyright (c) 2000-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFSTREAMPRIV__) +#define __COREFOUNDATION_CFSTREAMPRIV__ 1 + +#include <CoreFoundation/CFStream.h> +#include <CoreFoundation/CFRunLoop.h> +#include <CoreFoundation/CFRuntime.h> + +CF_EXTERN_C_BEGIN + +struct _CFStream; +struct _CFStreamClient { + CFStreamClientContext cbContext; + void (*cb)(struct _CFStream *, CFStreamEventType, void *); + CFOptionFlags when; + CFRunLoopSourceRef rlSource; + CFMutableArrayRef runLoopsAndModes; + CFOptionFlags whatToSignal; +}; + +#define CFStreamCurrentVersion 2 + +// A unified set of callbacks so we can use a single structure for all struct _CFStreams. +struct _CFStreamCallBacks { + CFIndex version; + void *(*create)(struct _CFStream *stream, void *info); + void (*finalize)(struct _CFStream *stream, void *info); + CFStringRef (*copyDescription)(struct _CFStream *stream, void *info); + + Boolean (*open)(struct _CFStream *stream, CFErrorRef *error, Boolean *openComplete, void *info); + Boolean (*openCompleted)(struct _CFStream *stream, CFErrorRef *error, void *info); + CFIndex (*read)(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFErrorRef *error, Boolean *atEOF, void *info); + const UInt8 *(*getBuffer)(CFReadStreamRef sream, CFIndex maxBytesToRead, CFIndex *numBytesRead, CFErrorRef *error, Boolean *atEOF, void *info); + Boolean (*canRead)(CFReadStreamRef, CFErrorRef *error, void *info); + CFIndex (*write)(CFWriteStreamRef, const UInt8 *buffer, CFIndex bufferLength, CFErrorRef *error, void *info); + Boolean (*canWrite)(CFWriteStreamRef, CFErrorRef *error, void *info); + void (*close)(struct _CFStream *stream, void *info); + + CFTypeRef (*copyProperty)(struct _CFStream *stream, CFStringRef propertyName, void *info); + Boolean (*setProperty)(struct _CFStream *stream, CFStringRef propertyName, CFTypeRef propertyValue, void *info); + void (*requestEvents)(struct _CFStream *stream, CFOptionFlags events, void *info); + void (*schedule)(struct _CFStream *stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); + void (*unschedule)(struct _CFStream *stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); +}; + +struct _CFStream { + CFRuntimeBase _cfBase; + CFOptionFlags flags; + CFErrorRef error; // if callBacks->version < 2, this is actually a pointer to a CFStreamError + struct _CFStreamClient *client; + void *info; + const struct _CFStreamCallBacks *callBacks; // This will not exist (will not be allocated) if the callbacks are from our known, "blessed" set. + void *_reserved1; +}; + + +CF_INLINE void *_CFStreamGetInfoPointer(struct _CFStream *stream) { + return stream->info; +} + + +// cb version must be > 0 +CF_EXPORT struct _CFStream *_CFStreamCreateWithConstantCallbacks(CFAllocatorRef alloc, void *info, const struct _CFStreamCallBacks *cb, Boolean isReading); + +// Only available for streams created with _CFStreamCreateWithConstantCallbacks, above. cb's version must be 1 +CF_EXPORT void _CFStreamSetInfoPointer(struct _CFStream *stream, void *info, const struct _CFStreamCallBacks *cb); + +/* +** _CFStreamSourceScheduleWithRunLoop +** +** Schedules the given run loop source on the given run loop and mode. It then +** adds the loop and mode pair to the runLoopsAndModes list. The list is +** simply a linear list of a loop reference followed by a mode reference. +** +** source Run loop source to be scheduled +** +** runLoopsAndModes List of run loop/mode pairs on which the source is scheduled +** +** runLoop Run loop on which the source is being scheduled +** +** runLoopMode Run loop mode on which the source is being scheduled +*/ +CF_EXPORT +void _CFStreamSourceScheduleWithRunLoop(CFRunLoopSourceRef source, CFMutableArrayRef runLoopsAndModes, CFRunLoopRef runLoop, CFStringRef runLoopMode); + + +/* +** _CFStreamSourceUnscheduleFromRunLoop +** +** Unschedule the given source from the given run loop and mode. It then will +** guarantee that the source remains scheduled on the list of run loop and mode +** pairs in the runLoopsAndModes list. The list is simply a linear list of a +** loop reference followed by a mode reference. +** +** source Run loop source to be unscheduled +** +** runLoopsAndModes List of run loop/mode pairs on which the source is scheduled +** +** runLoop Run loop from which the source is being unscheduled +** +** runLoopMode Run loop mode from which the source is being unscheduled +*/ +CF_EXPORT +void _CFStreamSourceUnscheduleFromRunLoop(CFRunLoopSourceRef source, CFMutableArrayRef runLoopsAndModes, CFRunLoopRef runLoop, CFStringRef runLoopMode); + + +/* +** _CFStreamSourceScheduleWithAllRunLoops +** +** Schedules the given run loop source on all the run loops and modes in the list. +** The list is simply a linear list of a loop reference followed by a mode reference. +** +** source Run loop source to be unscheduled +** +** runLoopsAndModes List of run loop/mode pairs on which the source is scheduled +*/ +CF_EXPORT +void _CFStreamSourceScheduleWithAllRunLoops(CFRunLoopSourceRef source, CFArrayRef runLoopsAndModes); + + +/* +** _CFStreamSourceUnscheduleFromRunLoop +** +** Unschedule the given source from all the run loops and modes in the list. +** The list is simply a linear list of a loop reference followed by a mode +** reference. +** +** source Run loop source to be unscheduled +** +** runLoopsAndModes List of run loop/mode pairs on which the source is scheduled +*/ +CF_EXPORT +void _CFStreamSourceUncheduleFromAllRunLoops(CFRunLoopSourceRef source, CFArrayRef runLoopsAndModes); + +CF_EXPORT +CFReadStreamRef _CFReadStreamCreateFromFileDescriptor(CFAllocatorRef alloc, int fd); + +CF_EXPORT +CFWriteStreamRef _CFWriteStreamCreateFromFileDescriptor(CFAllocatorRef alloc, int fd); + + + +#define SECURITY_NONE (0) +#define SECURITY_SSLv2 (1) +#define SECURITY_SSLv3 (2) +#define SECURITY_SSLv32 (3) +#define SECURITY_TLS (4) + +#if defined (__MACH__) +// This symbol is exported from CFNetwork (see CFSocketStream.i). Only __MACH__ systems will +// get this symbol from CoreFoundation. +extern const int kCFStreamErrorDomainSSL; +#endif //__MACH__ + +/* + * Additional SPI for CFNetwork for select side read buffering + */ +CF_EXPORT +Boolean __CFSocketGetBytesAvailable(CFSocketRef s, CFIndex* ctBytesAvailable); + +CF_EXPORT +CFIndex __CFSocketRead(CFSocketRef s, UInt8* buffer, CFIndex length, int* error); + +CF_EXPORT +void __CFSocketSetReadBufferLength(CFSocketRef s, CFIndex length); + +CF_EXPORT +void __CFSocketSetReadBufferTimeout(CFSocketRef s, CFTimeInterval timeout); + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFSTREAMPRIV__ */ +
diff --git a/CoreFoundation/CFString.c b/CoreFoundation/CFString.c new file mode 100644 index 0000000..4ac01e5 --- /dev/null +++ b/CoreFoundation/CFString.c
@@ -0,0 +1,5838 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFString.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Ali Ozer + +!!! For performance reasons, it's important that all functions marked CF_INLINE in this file are inlined. +*/ + +#include <CoreFoundation/CFBase.h> +#include <CoreFoundation/CFString.h> +#include <CoreFoundation/CFDictionary.h> +#include "CFStringEncodingConverterExt.h" +#include "CFUniChar.h" +#include "CFUnicodeDecomposition.h" +#include "CFUnicodePrecomposition.h" +#include "CFPriv.h" +#include "CFInternal.h" +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD +#include <unistd.h> +#else +extern size_t strlcpy(char *dst, const char *src, size_t siz); +extern size_t strlcat(char *dst, const char *src, size_t siz); +#endif + +#if defined(__GNUC__) +#define LONG_DOUBLE_SUPPORT 1 +#else +#define LONG_DOUBLE_SUPPORT 0 +#endif + + + +#define USE_STRING_ROM 0 + + +#ifndef INSTRUMENT_SHARED_STRINGS +#define INSTRUMENT_SHARED_STRINGS 0 +#endif + +#if DEPLOYMENT_TARGET_MACOSX +__private_extern__ +#else +CF_EXPORT const +#endif +CFStringRef __kCFLocaleCollatorID; + +#if INSTRUMENT_SHARED_STRINGS +#include <sys/stat.h> /* for umask() */ + +static void __CFRecordStringAllocationEvent(const char *encoding, const char *bytes, CFIndex byteCount) { + static CFSpinLock_t lock = CFSpinLockInit; + + if (memchr(bytes, '\n', byteCount)) return; //never record string allocation events for strings with newlines, because those confuse our parser and because they'll never go into the ROM + + __CFSpinLock(&lock); + static int fd; + if (! fd) { + extern char **_NSGetProgname(void); + const char *name = *_NSGetProgname(); + if (! name) name = "UNKNOWN"; + umask(0); + char path[1024]; + sprintf(path, "/tmp/CFSharedStringInstrumentation_%s_%d.txt", name, getpid()); + fd = open(path, O_WRONLY | O_APPEND | O_CREAT, 0666); + if (fd <= 0) { + int error = errno; + const char *errString = strerror(error); + fprintf(stderr, "open() failed with error %d (%s)\n", error, errString); + } + } + if (fd > 0) { + char *buffer = NULL; + char formatString[256]; + sprintf(formatString, "%%-8d\t%%-16s\t%%.%lds\n", byteCount); + int resultCount = asprintf(&buffer, formatString, getpid(), encoding, bytes); + if (buffer && resultCount > 0) write(fd, buffer, resultCount); + else puts("Couldn't record allocation event"); + free(buffer); + } + __CFSpinUnlock(&lock); +} +#endif //INSTRUMENT_SHARED_STRINGS + + + +typedef Boolean (*UNI_CHAR_FUNC)(UInt32 flags, UInt8 ch, UniChar *unicodeChar); + +#if DEPLOYMENT_TARGET_MACOSX +extern size_t malloc_good_size(size_t size); +#endif +extern void __CFStrConvertBytesToUnicode(const uint8_t *bytes, UniChar *buffer, CFIndex numChars); + +#if defined(DEBUG) + +// We put this into C & Pascal strings if we can't convert +#define CONVERSIONFAILURESTR "CFString conversion failed" + +// We set this to true when purging the constant string table, so CFStringDeallocate doesn't assert +static Boolean __CFConstantStringTableBeingFreed = false; + +#endif + + + +// This section is for CFString compatibility and other behaviors... + +static CFOptionFlags _CFStringCompatibilityMask = 0; + +#define Bug2967272 1 + +void _CFStringSetCompatibility(CFOptionFlags mask) { + _CFStringCompatibilityMask |= mask; +} + +CF_INLINE Boolean __CFStringGetCompatibility(CFOptionFlags mask) { + return (_CFStringCompatibilityMask & mask) == mask; +} + + + +// Two constant strings used by CFString; these are initialized in CFStringInitialize +CONST_STRING_DECL(kCFEmptyString, "") + +// This is separate for C++ +struct __notInlineMutable { + void *buffer; + CFIndex length; + CFIndex capacity; // Capacity in bytes + unsigned int hasGap:1; // Currently unused + unsigned int isFixedCapacity:1; + unsigned int isExternalMutable:1; + unsigned int capacityProvidedExternally:1; +#if __LP64__ + unsigned long desiredCapacity:60; +#else + unsigned long desiredCapacity:28; +#endif + CFAllocatorRef contentsAllocator; // Optional +}; // The only mutable variant for CFString + + +/* !!! Never do sizeof(CFString); the union is here just to make it easier to access some fields. +*/ +struct __CFString { + CFRuntimeBase base; + union { // In many cases the allocated structs are smaller than these + struct __inline1 { + CFIndex length; + } inline1; // Bytes follow the length + struct __notInlineImmutable1 { + void *buffer; // Note that the buffer is in the same place for all non-inline variants of CFString + CFIndex length; + CFAllocatorRef contentsDeallocator; // Optional; just the dealloc func is used + } notInlineImmutable1; // This is the usual not-inline immutable CFString + struct __notInlineImmutable2 { + void *buffer; + CFAllocatorRef contentsDeallocator; // Optional; just the dealloc func is used + } notInlineImmutable2; // This is the not-inline immutable CFString when length is stored with the contents (first byte) + struct __notInlineMutable notInlineMutable; + } variants; +}; + +/* +I = is immutable +E = not inline contents +U = is Unicode +N = has NULL byte +L = has length byte +D = explicit deallocator for contents (for mutable objects, allocator) +C = length field is CFIndex (rather than UInt32); only meaningful for 64-bit, really + if needed this bit (valuable real-estate) can be given up for another bit elsewhere, since this info is needed just for 64-bit + +Also need (only for mutable) +F = is fixed +G = has gap +Cap, DesCap = capacity + +B7 B6 B5 B4 B3 B2 B1 B0 + U N L C I + +B6 B5 + 0 0 inline contents + 0 1 E (freed with default allocator) + 1 0 E (not freed) + 1 1 E D + +!!! Note: Constant CFStrings use the bit patterns: +C8 (11001000 = default allocator, not inline, not freed contents; 8-bit; has NULL byte; doesn't have length; is immutable) +D0 (11010000 = default allocator, not inline, not freed contents; Unicode; is immutable) +The bit usages should not be modified in a way that would effect these bit patterns. +*/ + +enum { + __kCFFreeContentsWhenDoneMask = 0x020, + __kCFFreeContentsWhenDone = 0x020, + __kCFContentsMask = 0x060, + __kCFHasInlineContents = 0x000, + __kCFNotInlineContentsNoFree = 0x040, // Don't free + __kCFNotInlineContentsDefaultFree = 0x020, // Use allocator's free function + __kCFNotInlineContentsCustomFree = 0x060, // Use a specially provided free function + __kCFHasContentsAllocatorMask = 0x060, + __kCFHasContentsAllocator = 0x060, // (For mutable strings) use a specially provided allocator + __kCFHasContentsDeallocatorMask = 0x060, + __kCFHasContentsDeallocator = 0x060, + __kCFIsMutableMask = 0x01, + __kCFIsMutable = 0x01, + __kCFIsUnicodeMask = 0x10, + __kCFIsUnicode = 0x10, + __kCFHasNullByteMask = 0x08, + __kCFHasNullByte = 0x08, + __kCFHasLengthByteMask = 0x04, + __kCFHasLengthByte = 0x04, + // !!! Bit 0x02 has been freed up +}; + + +// !!! Assumptions: +// Mutable strings are not inline +// Compile-time constant strings are not inline +// Mutable strings always have explicit length (but they might also have length byte and null byte) +// If there is an explicit length, always use that instead of the length byte (length byte is useful for quickly returning pascal strings) +// Never look at the length byte for the length; use __CFStrLength or __CFStrLength2 + +/* The following set of functions and macros need to be updated on change to the bit configuration +*/ +CF_INLINE Boolean __CFStrIsMutable(CFStringRef str) {return (str->base._cfinfo[CF_INFO_BITS] & __kCFIsMutableMask) == __kCFIsMutable;} +CF_INLINE Boolean __CFStrIsInline(CFStringRef str) {return (str->base._cfinfo[CF_INFO_BITS] & __kCFContentsMask) == __kCFHasInlineContents;} +CF_INLINE Boolean __CFStrFreeContentsWhenDone(CFStringRef str) {return (str->base._cfinfo[CF_INFO_BITS] & __kCFFreeContentsWhenDoneMask) == __kCFFreeContentsWhenDone;} +CF_INLINE Boolean __CFStrHasContentsDeallocator(CFStringRef str) {return (str->base._cfinfo[CF_INFO_BITS] & __kCFHasContentsDeallocatorMask) == __kCFHasContentsDeallocator;} +CF_INLINE Boolean __CFStrIsUnicode(CFStringRef str) {return (str->base._cfinfo[CF_INFO_BITS] & __kCFIsUnicodeMask) == __kCFIsUnicode;} +CF_INLINE Boolean __CFStrIsEightBit(CFStringRef str) {return (str->base._cfinfo[CF_INFO_BITS] & __kCFIsUnicodeMask) != __kCFIsUnicode;} +CF_INLINE Boolean __CFStrHasNullByte(CFStringRef str) {return (str->base._cfinfo[CF_INFO_BITS] & __kCFHasNullByteMask) == __kCFHasNullByte;} +CF_INLINE Boolean __CFStrHasLengthByte(CFStringRef str) {return (str->base._cfinfo[CF_INFO_BITS] & __kCFHasLengthByteMask) == __kCFHasLengthByte;} +CF_INLINE Boolean __CFStrHasExplicitLength(CFStringRef str) {return (str->base._cfinfo[CF_INFO_BITS] & (__kCFIsMutableMask | __kCFHasLengthByteMask)) != __kCFHasLengthByte;} // Has explicit length if (1) mutable or (2) not mutable and no length byte +CF_INLINE Boolean __CFStrIsConstant(CFStringRef str) { +#if __LP64__ + return str->base._rc == 0; +#else + return (str->base._cfinfo[CF_RC_BITS]) == 0; +#endif +} + +CF_INLINE SInt32 __CFStrSkipAnyLengthByte(CFStringRef str) {return ((str->base._cfinfo[CF_INFO_BITS] & __kCFHasLengthByteMask) == __kCFHasLengthByte) ? 1 : 0;} // Number of bytes to skip over the length byte in the contents + +/* Returns ptr to the buffer (which might include the length byte) +*/ +CF_INLINE const void *__CFStrContents(CFStringRef str) { + if (__CFStrIsInline(str)) { + return (const void *)(((uintptr_t)&(str->variants)) + (__CFStrHasExplicitLength(str) ? sizeof(CFIndex) : 0)); + } else { // Not inline; pointer is always word 2 + return str->variants.notInlineImmutable1.buffer; + } +} + +static CFAllocatorRef *__CFStrContentsDeallocatorPtr(CFStringRef str) { + return __CFStrHasExplicitLength(str) ? &(((CFMutableStringRef)str)->variants.notInlineImmutable1.contentsDeallocator) : &(((CFMutableStringRef)str)->variants.notInlineImmutable2.contentsDeallocator); } + +// Assumption: Called with immutable strings only, and on strings that are known to have a contentsDeallocator +CF_INLINE CFAllocatorRef __CFStrContentsDeallocator(CFStringRef str) { + return *__CFStrContentsDeallocatorPtr(str); +} + +// Assumption: Called with immutable strings only, and on strings that are known to have a contentsDeallocator +CF_INLINE void __CFStrSetContentsDeallocator(CFStringRef str, CFAllocatorRef contentsAllocator) { + *__CFStrContentsDeallocatorPtr(str) = contentsAllocator; +} + +static CFAllocatorRef *__CFStrContentsAllocatorPtr(CFStringRef str) { + CFAssert(!__CFStrIsInline(str), __kCFLogAssertion, "Asking for contents allocator of inline string"); + CFAssert(__CFStrIsMutable(str), __kCFLogAssertion, "Asking for contents allocator of an immutable string"); + return (CFAllocatorRef *)&(str->variants.notInlineMutable.contentsAllocator); +} + +// Assumption: Called with strings that have a contents allocator; also, contents allocator follows custom +CF_INLINE CFAllocatorRef __CFStrContentsAllocator(CFMutableStringRef str) { + return *(__CFStrContentsAllocatorPtr(str)); +} + +// Assumption: Called with strings that have a contents allocator; also, contents allocator follows custom +CF_INLINE void __CFStrSetContentsAllocator(CFMutableStringRef str, CFAllocatorRef alloc) { + *(__CFStrContentsAllocatorPtr(str)) = alloc; +} + +/* Returns length; use __CFStrLength2 if contents buffer pointer has already been computed. +*/ +CF_INLINE CFIndex __CFStrLength(CFStringRef str) { + if (__CFStrHasExplicitLength(str)) { + if (__CFStrIsInline(str)) { + return str->variants.inline1.length; + } else { + return str->variants.notInlineImmutable1.length; + } + } else { + return (CFIndex)(*((uint8_t *)__CFStrContents(str))); + } +} + +CF_INLINE CFIndex __CFStrLength2(CFStringRef str, const void *buffer) { + if (__CFStrHasExplicitLength(str)) { + if (__CFStrIsInline(str)) { + return str->variants.inline1.length; + } else { + return str->variants.notInlineImmutable1.length; + } + } else { + return (CFIndex)(*((uint8_t *)buffer)); + } +} + +Boolean __CFStringIsMutable(CFStringRef str) { + return __CFStrIsMutable(str); +} + +Boolean __CFStringIsEightBit(CFStringRef str) { + return __CFStrIsEightBit(str); +} + +/* Sets the content pointer for immutable or mutable strings. +*/ +CF_INLINE void __CFStrSetContentPtr(CFStringRef str, const void *p) { + // XXX_PCB catch all writes for mutable string case. + CF_WRITE_BARRIER_BASE_ASSIGN(__CFGetAllocator(str), str, ((CFMutableStringRef)str)->variants.notInlineImmutable1.buffer, (void *)p); +} +CF_INLINE void __CFStrSetInfoBits(CFStringRef str, UInt32 v) {__CFBitfieldSetValue(((CFMutableStringRef)str)->base._cfinfo[CF_INFO_BITS], 6, 0, v);} + +CF_INLINE void __CFStrSetExplicitLength(CFStringRef str, CFIndex v) { + if (__CFStrIsInline(str)) { + ((CFMutableStringRef)str)->variants.inline1.length = v; + } else { + ((CFMutableStringRef)str)->variants.notInlineImmutable1.length = v; + } +} + +CF_INLINE void __CFStrSetUnicode(CFMutableStringRef str) {str->base._cfinfo[CF_INFO_BITS] |= __kCFIsUnicode;} +CF_INLINE void __CFStrClearUnicode(CFMutableStringRef str) {str->base._cfinfo[CF_INFO_BITS] &= ~__kCFIsUnicode;} +CF_INLINE void __CFStrSetHasLengthAndNullBytes(CFMutableStringRef str) {str->base._cfinfo[CF_INFO_BITS] |= (__kCFHasLengthByte | __kCFHasNullByte);} +CF_INLINE void __CFStrClearHasLengthAndNullBytes(CFMutableStringRef str) {str->base._cfinfo[CF_INFO_BITS] &= ~(__kCFHasLengthByte | __kCFHasNullByte);} + + +// Assumption: The following set of inlines (using str->variants.notInlineMutable) are called with mutable strings only +CF_INLINE Boolean __CFStrIsFixed(CFStringRef str) {return str->variants.notInlineMutable.isFixedCapacity;} +CF_INLINE Boolean __CFStrIsExternalMutable(CFStringRef str) {return str->variants.notInlineMutable.isExternalMutable;} +CF_INLINE Boolean __CFStrHasContentsAllocator(CFStringRef str) {return (str->base._cfinfo[CF_INFO_BITS] & __kCFHasContentsAllocatorMask) == __kCFHasContentsAllocator;} +CF_INLINE void __CFStrSetIsFixed(CFMutableStringRef str) {str->variants.notInlineMutable.isFixedCapacity = 1;} +CF_INLINE void __CFStrSetIsExternalMutable(CFMutableStringRef str) {str->variants.notInlineMutable.isExternalMutable = 1;} +CF_INLINE void __CFStrSetHasGap(CFMutableStringRef str) {str->variants.notInlineMutable.hasGap = 1;} + +// If capacity is provided externally, we only change it when we need to grow beyond it +CF_INLINE Boolean __CFStrCapacityProvidedExternally(CFStringRef str) {return str->variants.notInlineMutable.capacityProvidedExternally;} +CF_INLINE void __CFStrSetCapacityProvidedExternally(CFMutableStringRef str) {str->variants.notInlineMutable.capacityProvidedExternally = 1;} +CF_INLINE void __CFStrClearCapacityProvidedExternally(CFMutableStringRef str) {str->variants.notInlineMutable.capacityProvidedExternally = 0;} + +// "Capacity" is stored in number of bytes, not characters. It indicates the total number of bytes in the contents buffer. +CF_INLINE CFIndex __CFStrCapacity(CFStringRef str) {return str->variants.notInlineMutable.capacity;} +CF_INLINE void __CFStrSetCapacity(CFMutableStringRef str, CFIndex cap) {str->variants.notInlineMutable.capacity = cap;} + +// "Desired capacity" is in number of characters; it is the client requested capacity; if fixed, it is the upper bound on the mutable string backing store. +CF_INLINE CFIndex __CFStrDesiredCapacity(CFStringRef str) {return str->variants.notInlineMutable.desiredCapacity;} +CF_INLINE void __CFStrSetDesiredCapacity(CFMutableStringRef str, CFIndex size) {str->variants.notInlineMutable.desiredCapacity = size;} + + +static void *__CFStrAllocateMutableContents(CFMutableStringRef str, CFIndex size) { + void *ptr; + CFAllocatorRef alloc = (__CFStrHasContentsAllocator(str)) ? __CFStrContentsAllocator(str) : __CFGetAllocator(str); + ptr = CFAllocatorAllocate(alloc, size, 0); + if (__CFOASafe) __CFSetLastAllocationEventName(ptr, "CFString (store)"); + return ptr; +} + +static void __CFStrDeallocateMutableContents(CFMutableStringRef str, void *buffer) { + CFAllocatorRef alloc = (__CFStrHasContentsAllocator(str)) ? __CFStrContentsAllocator(str) : __CFGetAllocator(str); + if (CF_IS_COLLECTABLE_ALLOCATOR(alloc)) { + // GC: for finalization safety, let collector reclaim the buffer in the next GC cycle. + auto_zone_release(__CFCollectableZone, buffer); + } else { + CFAllocatorDeallocate(alloc, buffer); + } +} + + + + +/* CFString specific init flags + Note that you cannot count on the external buffer not being copied. + Also, if you specify an external buffer, you should not change it behind the CFString's back. +*/ +enum { + __kCFThinUnicodeIfPossible = 0x1000000, /* See if the Unicode contents can be thinned down to 8-bit */ + kCFStringPascal = 0x10000, /* Indicating that the string data has a Pascal string structure (length byte at start) */ + kCFStringNoCopyProvidedContents = 0x20000, /* Don't copy the provided string contents if possible; free it when no longer needed */ + kCFStringNoCopyNoFreeProvidedContents = 0x30000 /* Don't copy the provided string contents if possible; don't free it when no longer needed */ +}; + +/* System Encoding. +*/ +static CFStringEncoding __CFDefaultSystemEncoding = kCFStringEncodingInvalidId; +static CFStringEncoding __CFDefaultFileSystemEncoding = kCFStringEncodingInvalidId; +CFStringEncoding __CFDefaultEightBitStringEncoding = kCFStringEncodingInvalidId; + +CFStringEncoding CFStringGetSystemEncoding(void) { + + if (__CFDefaultSystemEncoding == kCFStringEncodingInvalidId) { + const CFStringEncodingConverter *converter = NULL; +#if DEPLOYMENT_TARGET_MACOSX + __CFDefaultSystemEncoding = kCFStringEncodingMacRoman; // MacRoman is built-in so always available +#elif DEPLOYMENT_TARGET_WINDOWS + __CFDefaultSystemEncoding = kCFStringEncodingWindowsLatin1; // WinLatin1 is built-in so always available +#elif DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD + __CFDefaultSystemEncoding = kCFStringEncodingISOLatin1; // a reasonable default +#else // Solaris && HP-UX ? + __CFDefaultSystemEncoding = kCFStringEncodingISOLatin1; // a reasonable default +#endif + converter = CFStringEncodingGetConverter(__CFDefaultSystemEncoding); + + __CFSetCharToUniCharFunc(converter->encodingClass == kCFStringEncodingConverterCheapEightBit ? (UNI_CHAR_FUNC)converter->toUnicode : NULL); + } + + return __CFDefaultSystemEncoding; +} + +// Fast version for internal use + +CF_INLINE CFStringEncoding __CFStringGetSystemEncoding(void) { + if (__CFDefaultSystemEncoding == kCFStringEncodingInvalidId) (void)CFStringGetSystemEncoding(); + return __CFDefaultSystemEncoding; +} + +CFStringEncoding CFStringFileSystemEncoding(void) { + if (__CFDefaultFileSystemEncoding == kCFStringEncodingInvalidId) { +#if DEPLOYMENT_TARGET_MACOSX + __CFDefaultFileSystemEncoding = kCFStringEncodingUTF8; +#else + __CFDefaultFileSystemEncoding = CFStringGetSystemEncoding(); +#endif + } + + return __CFDefaultFileSystemEncoding; +} + +/* ??? Is returning length when no other answer is available the right thing? +*/ +CFIndex CFStringGetMaximumSizeForEncoding(CFIndex length, CFStringEncoding encoding) { + if (encoding == kCFStringEncodingUTF8) { + return _CFExecutableLinkedOnOrAfter(CFSystemVersionPanther) ? (length * 3) : (length * 6); // 1 Unichar could expand to 3 bytes; we return 6 for older apps for compatibility + } else if ((encoding == kCFStringEncodingUTF32) || (encoding == kCFStringEncodingUTF32BE) || (encoding == kCFStringEncodingUTF32LE)) { // UTF-32 + return length * sizeof(UTF32Char); + } else { + encoding &= 0xFFF; // Mask off non-base part + } + switch (encoding) { + case kCFStringEncodingUnicode: + return length * sizeof(UniChar); + + case kCFStringEncodingNonLossyASCII: + return length * 6; // 1 Unichar could expand to 6 bytes + + case kCFStringEncodingMacRoman: + case kCFStringEncodingWindowsLatin1: + case kCFStringEncodingISOLatin1: + case kCFStringEncodingNextStepLatin: + case kCFStringEncodingASCII: + return length / sizeof(uint8_t); + + default: + return length / sizeof(uint8_t); + } +} + + +/* Returns whether the indicated encoding can be stored in 8-bit chars +*/ +CF_INLINE Boolean __CFStrEncodingCanBeStoredInEightBit(CFStringEncoding encoding) { + switch (encoding & 0xFFF) { // just use encoding base + case kCFStringEncodingInvalidId: + case kCFStringEncodingUnicode: + case kCFStringEncodingNonLossyASCII: + return false; + + case kCFStringEncodingMacRoman: + case kCFStringEncodingWindowsLatin1: + case kCFStringEncodingISOLatin1: + case kCFStringEncodingNextStepLatin: + case kCFStringEncodingASCII: + return true; + + default: return false; + } +} + +/* Returns the encoding used in eight bit CFStrings (can't be any encoding which isn't 1-to-1 with Unicode) + ??? Perhaps only ASCII fits the bill due to Unicode decomposition. +*/ +CFStringEncoding __CFStringComputeEightBitStringEncoding(void) { + if (__CFDefaultEightBitStringEncoding == kCFStringEncodingInvalidId) { + CFStringEncoding systemEncoding = CFStringGetSystemEncoding(); + if (systemEncoding == kCFStringEncodingInvalidId) { // We're right in the middle of querying system encoding from default database. Delaying to set until system encoding is determined. + return kCFStringEncodingASCII; + } else if (__CFStrEncodingCanBeStoredInEightBit(systemEncoding)) { + __CFDefaultEightBitStringEncoding = systemEncoding; + } else { + __CFDefaultEightBitStringEncoding = kCFStringEncodingASCII; + } + } + + return __CFDefaultEightBitStringEncoding; +} + +/* Returns whether the provided bytes can be stored in ASCII +*/ +CF_INLINE Boolean __CFBytesInASCII(const uint8_t *bytes, CFIndex len) { + while (len--) if ((uint8_t)(*bytes++) >= 128) return false; + return true; +} + +/* Returns whether the provided 8-bit string in the specified encoding can be stored in an 8-bit CFString. +*/ +CF_INLINE Boolean __CFCanUseEightBitCFStringForBytes(const uint8_t *bytes, CFIndex len, CFStringEncoding encoding) { + if (encoding == __CFStringGetEightBitStringEncoding()) return true; + if (__CFStringEncodingIsSupersetOfASCII(encoding) && __CFBytesInASCII(bytes, len)) return true; + return false; +} + + +/* Returns whether a length byte can be tacked on to a string of the indicated length. +*/ +CF_INLINE Boolean __CFCanUseLengthByte(CFIndex len) { +#define __kCFMaxPascalStrLen 255 + return (len <= __kCFMaxPascalStrLen) ? true : false; +} + +/* Various string assertions +*/ +#define __CFAssertIsString(cf) __CFGenericValidateType(cf, __kCFStringTypeID) +#define __CFAssertIndexIsInStringBounds(cf, idx) CFAssert3((idx) >= 0 && (idx) < __CFStrLength(cf), __kCFLogAssertion, "%s(): string index %d out of bounds (length %d)", __PRETTY_FUNCTION__, idx, __CFStrLength(cf)) +#define __CFAssertRangeIsInStringBounds(cf, idx, count) CFAssert4((idx) >= 0 && (idx + count) <= __CFStrLength(cf), __kCFLogAssertion, "%s(): string range %d,%d out of bounds (length %d)", __PRETTY_FUNCTION__, idx, count, __CFStrLength(cf)) +#define __CFAssertIsStringAndMutable(cf) {__CFGenericValidateType(cf, __kCFStringTypeID); CFAssert1(__CFStrIsMutable(cf), __kCFLogAssertion, "%s(): string not mutable", __PRETTY_FUNCTION__);} +#define __CFAssertIsStringAndExternalMutable(cf) {__CFGenericValidateType(cf, __kCFStringTypeID); CFAssert1(__CFStrIsMutable(cf) && __CFStrIsExternalMutable(cf), __kCFLogAssertion, "%s(): string not external mutable", __PRETTY_FUNCTION__);} +#define __CFAssertIsNotNegative(idx) CFAssert2(idx >= 0, __kCFLogAssertion, "%s(): index %d is negative", __PRETTY_FUNCTION__, idx) +#define __CFAssertIfFixedLengthIsOK(cf, reqLen) CFAssert2(!__CFStrIsFixed(cf) || (reqLen <= __CFStrDesiredCapacity(cf)), __kCFLogAssertion, "%s(): length %d too large", __PRETTY_FUNCTION__, reqLen) + + +/* Basic algorithm is to shrink memory when capacity is SHRINKFACTOR times the required capacity or to allocate memory when the capacity is less than GROWFACTOR times the required capacity. +Additional complications are applied in the following order: +- desiredCapacity, which is the minimum (except initially things can be at zero) +- rounding up to factor of 8 +- compressing (to fit the number if 16 bits), which effectively rounds up to factor of 256 +- we need to make sure GROWFACTOR computation doesn't suffer from overflow issues on 32-bit, hence the casting to unsigned. Normally for required capacity of C bytes, the allocated space is (3C+1)/2. If C > ULONG_MAX/3, we instead simply return LONG_MAX +*/ +#define SHRINKFACTOR(c) (c / 2) + +#if __LP64__ +#define GROWFACTOR(c) ((c * 3 + 1) / 2) +#else +#define GROWFACTOR(c) (((c) >= (ULONG_MAX / 3UL)) ? __CFMax(LONG_MAX - 4095, (c)) : (((unsigned long)c * 3 + 1) / 2)) +#endif + +CF_INLINE CFIndex __CFStrNewCapacity(CFMutableStringRef str, CFIndex reqCapacity, CFIndex capacity, Boolean leaveExtraRoom, CFIndex charSize) { + if (capacity != 0 || reqCapacity != 0) { /* If initially zero, and space not needed, leave it at that... */ + if ((capacity < reqCapacity) || /* We definitely need the room... */ + (!__CFStrCapacityProvidedExternally(str) && /* Assuming we control the capacity... */ + ((reqCapacity < SHRINKFACTOR(capacity)) || /* ...we have too much room! */ + (!leaveExtraRoom && (reqCapacity < capacity))))) { /* ...we need to eliminate the extra space... */ + CFIndex newCapacity = leaveExtraRoom ? GROWFACTOR(reqCapacity) : reqCapacity; /* Grow by 3/2 if extra room is desired */ + CFIndex desiredCapacity = __CFStrDesiredCapacity(str) * charSize; + if (newCapacity < desiredCapacity) { /* If less than desired, bump up to desired */ + newCapacity = desiredCapacity; + } else if (__CFStrIsFixed(str)) { /* Otherwise, if fixed, no need to go above the desired (fixed) capacity */ + newCapacity = __CFMax(desiredCapacity, reqCapacity); /* !!! So, fixed is not really fixed, but "tight" */ + } + if (__CFStrHasContentsAllocator(str)) { /* Also apply any preferred size from the allocator; should we do something for */ + newCapacity = CFAllocatorGetPreferredSizeForSize(__CFStrContentsAllocator(str), newCapacity, 0); +#if defined(__APPLE__) + } else { + newCapacity = malloc_good_size(newCapacity); +#endif + } + return newCapacity; // If packing: __CFStrUnpackNumber(__CFStrPackNumber(newCapacity)); + } + } + return capacity; +} + + +/* rearrangeBlocks() rearranges the blocks of data within the buffer so that they are "evenly spaced". buffer is assumed to have enough room for the result. + numBlocks is current total number of blocks within buffer. + blockSize is the size of each block in bytes + ranges and numRanges hold the ranges that are no longer needed; ranges are stored sorted in increasing order, and don't overlap + insertLength is the final spacing between the remaining blocks + +Example: buffer = A B C D E F G H, blockSize = 1, ranges = { (2,1) , (4,2) } (so we want to "delete" C and E F), fromEnd = NO +if insertLength = 4, result = A B ? ? ? ? D ? ? ? ? G H +if insertLength = 0, result = A B D G H + +Example: buffer = A B C D E F G H I J K L M N O P Q R S T U, blockSize = 1, ranges { (1,1), (3,1), (5,11), (17,1), (19,1) }, fromEnd = NO +if insertLength = 3, result = A ? ? ? C ? ? ? E ? ? ? Q ? ? ? S ? ? ? U + +*/ +typedef struct _CFStringDeferredRange { + CFIndex beginning; + CFIndex length; + CFIndex shift; +} CFStringDeferredRange; + +typedef struct _CFStringStackInfo { + CFIndex capacity; // Capacity (if capacity == count, need to realloc to add another) + CFIndex count; // Number of elements actually stored + CFStringDeferredRange *stack; + Boolean hasMalloced; // Indicates "stack" is allocated and needs to be deallocated when done + char _padding[3]; +} CFStringStackInfo; + +CF_INLINE void pop (CFStringStackInfo *si, CFStringDeferredRange *topRange) { + si->count = si->count - 1; + *topRange = si->stack[si->count]; +} + +CF_INLINE void push (CFStringStackInfo *si, const CFStringDeferredRange *newRange) { + if (si->count == si->capacity) { + // increase size of the stack + si->capacity = (si->capacity + 4) * 2; + if (si->hasMalloced) { + si->stack = (CFStringDeferredRange *)CFAllocatorReallocate(kCFAllocatorSystemDefault, si->stack, si->capacity * sizeof(CFStringDeferredRange), 0); + } else { + CFStringDeferredRange *newStack = (CFStringDeferredRange *)CFAllocatorAllocate(kCFAllocatorSystemDefault, si->capacity * sizeof(CFStringDeferredRange), 0); + memmove(newStack, si->stack, si->count * sizeof(CFStringDeferredRange)); + si->stack = newStack; + si->hasMalloced = true; + } + } + si->stack[si->count] = *newRange; + si->count = si->count + 1; +} + +static void rearrangeBlocks( + uint8_t *buffer, + CFIndex numBlocks, + CFIndex blockSize, + const CFRange *ranges, + CFIndex numRanges, + CFIndex insertLength) { + +#define origStackSize 10 + CFStringDeferredRange origStack[origStackSize]; + CFStringStackInfo si = {origStackSize, 0, origStack, false, {0, 0, 0}}; + CFStringDeferredRange currentNonRange = {0, 0, 0}; + CFIndex currentRange = 0; + CFIndex amountShifted = 0; + + // must have at least 1 range left. + + while (currentRange < numRanges) { + currentNonRange.beginning = (ranges[currentRange].location + ranges[currentRange].length) * blockSize; + if ((numRanges - currentRange) == 1) { + // at the end. + currentNonRange.length = numBlocks * blockSize - currentNonRange.beginning; + if (currentNonRange.length == 0) break; + } else { + currentNonRange.length = (ranges[currentRange + 1].location * blockSize) - currentNonRange.beginning; + } + currentNonRange.shift = amountShifted + (insertLength * blockSize) - (ranges[currentRange].length * blockSize); + amountShifted = currentNonRange.shift; + if (amountShifted <= 0) { + // process current item and rest of stack + if (currentNonRange.shift && currentNonRange.length) memmove (&buffer[currentNonRange.beginning + currentNonRange.shift], &buffer[currentNonRange.beginning], currentNonRange.length); + while (si.count > 0) { + pop (&si, ¤tNonRange); // currentNonRange now equals the top element of the stack. + if (currentNonRange.shift && currentNonRange.length) memmove (&buffer[currentNonRange.beginning + currentNonRange.shift], &buffer[currentNonRange.beginning], currentNonRange.length); + } + } else { + // add currentNonRange to stack. + push (&si, ¤tNonRange); + } + currentRange++; + } + + // no more ranges. if anything is on the stack, process. + + while (si.count > 0) { + pop (&si, ¤tNonRange); // currentNonRange now equals the top element of the stack. + if (currentNonRange.shift && currentNonRange.length) memmove (&buffer[currentNonRange.beginning + currentNonRange.shift], &buffer[currentNonRange.beginning], currentNonRange.length); + } + if (si.hasMalloced) CFAllocatorDeallocate (kCFAllocatorSystemDefault, si.stack); +} + +/* See comments for rearrangeBlocks(); this is the same, but the string is assembled in another buffer (dstBuffer), so the algorithm is much easier. We also take care of the case where the source is not-Unicode but destination is. (The reverse case is not supported.) +*/ +static void copyBlocks( + const uint8_t *srcBuffer, + uint8_t *dstBuffer, + CFIndex srcLength, + Boolean srcIsUnicode, + Boolean dstIsUnicode, + const CFRange *ranges, + CFIndex numRanges, + CFIndex insertLength) { + + CFIndex srcLocationInBytes = 0; // in order to avoid multiplying all the time, this is in terms of bytes, not blocks + CFIndex dstLocationInBytes = 0; // ditto + CFIndex srcBlockSize = srcIsUnicode ? sizeof(UniChar) : sizeof(uint8_t); + CFIndex insertLengthInBytes = insertLength * (dstIsUnicode ? sizeof(UniChar) : sizeof(uint8_t)); + CFIndex rangeIndex = 0; + CFIndex srcToDstMultiplier = (srcIsUnicode == dstIsUnicode) ? 1 : (sizeof(UniChar) / sizeof(uint8_t)); + + // Loop over the ranges, copying the range to be preserved (right before each range) + while (rangeIndex < numRanges) { + CFIndex srcLengthInBytes = ranges[rangeIndex].location * srcBlockSize - srcLocationInBytes; // srcLengthInBytes is in terms of bytes, not blocks; represents length of region to be preserved + if (srcLengthInBytes > 0) { + if (srcIsUnicode == dstIsUnicode) { + memmove(dstBuffer + dstLocationInBytes, srcBuffer + srcLocationInBytes, srcLengthInBytes); + } else { + __CFStrConvertBytesToUnicode(srcBuffer + srcLocationInBytes, (UniChar *)(dstBuffer + dstLocationInBytes), srcLengthInBytes); + } + } + srcLocationInBytes += srcLengthInBytes + ranges[rangeIndex].length * srcBlockSize; // Skip over the just-copied and to-be-deleted stuff + dstLocationInBytes += srcLengthInBytes * srcToDstMultiplier + insertLengthInBytes; + rangeIndex++; + } + + // Do last range (the one beyond last range) + if (srcLocationInBytes < srcLength * srcBlockSize) { + if (srcIsUnicode == dstIsUnicode) { + memmove(dstBuffer + dstLocationInBytes, srcBuffer + srcLocationInBytes, srcLength * srcBlockSize - srcLocationInBytes); + } else { + __CFStrConvertBytesToUnicode(srcBuffer + srcLocationInBytes, (UniChar *)(dstBuffer + dstLocationInBytes), srcLength * srcBlockSize - srcLocationInBytes); + } + } +} + +/* Call the callback; if it doesn't exist or returns false, then log +*/ +static void __CFStringHandleOutOfMemory(CFTypeRef obj) { + CFStringRef msg = CFSTR("Out of memory. We suggest restarting the application. If you have an unsaved document, create a backup copy in Finder, then try to save."); + CFBadErrorCallBack cb = _CFGetOutOfMemoryErrorCallBack(); + if (NULL == cb || !cb(obj, CFSTR("NS/CFString"), msg)) { + CFLog(kCFLogLevelCritical, CFSTR("%@"), msg); + } +} + +/* Reallocates the backing store of the string to accomodate the new length. Space is reserved or characters are deleted as indicated by insertLength and the ranges in deleteRanges. The length is updated to reflect the new state. Will also maintain a length byte and a null byte in 8-bit strings. If length cannot fit in length byte, the space will still be reserved, but will be 0. (Hence the reason the length byte should never be looked at as length unless there is no explicit length.) +*/ +static void __CFStringChangeSizeMultiple(CFMutableStringRef str, const CFRange *deleteRanges, CFIndex numDeleteRanges, CFIndex insertLength, Boolean makeUnicode) { + const uint8_t *curContents = (uint8_t *)__CFStrContents(str); + CFIndex curLength = curContents ? __CFStrLength2(str, curContents) : 0; + CFIndex newLength; + + // Compute new length of the string + if (numDeleteRanges == 1) { + newLength = curLength + insertLength - deleteRanges[0].length; + } else { + CFIndex cnt; + newLength = curLength + insertLength * numDeleteRanges; + for (cnt = 0; cnt < numDeleteRanges; cnt++) newLength -= deleteRanges[cnt].length; + } + + __CFAssertIfFixedLengthIsOK(str, newLength); + + if (newLength == 0) { + // An somewhat optimized code-path for this special case, with the following implicit values: + // newIsUnicode = false + // useLengthAndNullBytes = false + // newCharSize = sizeof(uint8_t) + // If the newCapacity happens to be the same as the old, we don't free the buffer; otherwise we just free it totally + // instead of doing a potentially useless reallocation (as the needed capacity later might turn out to be different anyway) + CFIndex curCapacity = __CFStrCapacity(str); + CFIndex newCapacity = __CFStrNewCapacity(str, 0, curCapacity, true, sizeof(uint8_t)); + if (newCapacity != curCapacity) { // If we're reallocing anyway (larger or smaller --- larger could happen if desired capacity was changed in the meantime), let's just free it all + if (curContents) __CFStrDeallocateMutableContents(str, (uint8_t *)curContents); + __CFStrSetContentPtr(str, NULL); + __CFStrSetCapacity(str, 0); + __CFStrClearCapacityProvidedExternally(str); + __CFStrClearHasLengthAndNullBytes(str); + if (!__CFStrIsExternalMutable(str)) __CFStrClearUnicode(str); // External mutable implies Unicode + } else { + if (!__CFStrIsExternalMutable(str)) { + __CFStrClearUnicode(str); + if (curCapacity >= (int)(sizeof(uint8_t) * 2)) { // If there's room + __CFStrSetHasLengthAndNullBytes(str); + ((uint8_t *)curContents)[0] = ((uint8_t *)curContents)[1] = 0; + } else { + __CFStrClearHasLengthAndNullBytes(str); + } + } + } + __CFStrSetExplicitLength(str, 0); + } else { /* This else-clause assumes newLength > 0 */ + Boolean oldIsUnicode = __CFStrIsUnicode(str); + Boolean newIsUnicode = makeUnicode || (oldIsUnicode /* && (newLength > 0) - implicit */ ) || __CFStrIsExternalMutable(str); + CFIndex newCharSize = newIsUnicode ? sizeof(UniChar) : sizeof(uint8_t); + Boolean useLengthAndNullBytes = !newIsUnicode /* && (newLength > 0) - implicit */; + CFIndex numExtraBytes = useLengthAndNullBytes ? 2 : 0; /* 2 extra bytes to keep the length byte & null... */ + CFIndex curCapacity = __CFStrCapacity(str); + CFIndex newCapacity = __CFStrNewCapacity(str, newLength * newCharSize + numExtraBytes, curCapacity, true, newCharSize); + Boolean allocNewBuffer = (newCapacity != curCapacity) || (curLength > 0 && !oldIsUnicode && newIsUnicode); /* We alloc new buffer if oldIsUnicode != newIsUnicode because the contents have to be copied */ + uint8_t *newContents; + if (allocNewBuffer) { + newContents = (uint8_t *)__CFStrAllocateMutableContents(str, newCapacity); + if (!newContents) { // Try allocating without extra room + newCapacity = __CFStrNewCapacity(str, newLength * newCharSize + numExtraBytes, curCapacity, false, newCharSize); + newContents = (uint8_t *)__CFStrAllocateMutableContents(str, newCapacity); + if (!newContents) { + __CFStringHandleOutOfMemory(str); + // Ideally control doesn't come here at all since we expect the above call to raise an exception. + // If control comes here, there isn't much we can do. + } + } + } else { + newContents = (uint8_t *)curContents; + } + + Boolean hasLengthAndNullBytes = __CFStrHasLengthByte(str); + + CFAssert1(hasLengthAndNullBytes == __CFStrHasNullByte(str), __kCFLogAssertion, "%s(): Invalid state in 8-bit string", __PRETTY_FUNCTION__); + + if (hasLengthAndNullBytes) curContents++; + if (useLengthAndNullBytes) newContents++; + + if (curContents) { + if (oldIsUnicode == newIsUnicode) { + if (newContents == curContents) { + rearrangeBlocks(newContents, curLength, newCharSize, deleteRanges, numDeleteRanges, insertLength); + } else { + copyBlocks(curContents, newContents, curLength, oldIsUnicode, newIsUnicode, deleteRanges, numDeleteRanges, insertLength); + } + } else if (newIsUnicode) { /* this implies we have a new buffer */ + copyBlocks(curContents, newContents, curLength, oldIsUnicode, newIsUnicode, deleteRanges, numDeleteRanges, insertLength); + } + if (hasLengthAndNullBytes) curContents--; /* Undo the damage from above */ + if (allocNewBuffer && __CFStrFreeContentsWhenDone(str)) __CFStrDeallocateMutableContents(str, (void *)curContents); + } + + if (!newIsUnicode) { + if (useLengthAndNullBytes) { + newContents[newLength] = 0; /* Always have null byte, if not unicode */ + newContents--; /* Undo the damage from above */ + newContents[0] = __CFCanUseLengthByte(newLength) ? (uint8_t)newLength : 0; + if (!hasLengthAndNullBytes) __CFStrSetHasLengthAndNullBytes(str); + } else { + if (hasLengthAndNullBytes) __CFStrClearHasLengthAndNullBytes(str); + } + if (oldIsUnicode) __CFStrClearUnicode(str); + } else { // New is unicode... + if (!oldIsUnicode) __CFStrSetUnicode(str); + if (hasLengthAndNullBytes) __CFStrClearHasLengthAndNullBytes(str); + } + __CFStrSetExplicitLength(str, newLength); + + if (allocNewBuffer) { + __CFStrSetCapacity(str, newCapacity); + __CFStrClearCapacityProvidedExternally(str); + __CFStrSetContentPtr(str, newContents); + } + } +} + +/* Same as above, but takes one range (very common case) +*/ +CF_INLINE void __CFStringChangeSize(CFMutableStringRef str, CFRange range, CFIndex insertLength, Boolean makeUnicode) { + __CFStringChangeSizeMultiple(str, &range, 1, insertLength, makeUnicode); +} + + +#if defined(DEBUG) +static Boolean __CFStrIsConstantString(CFStringRef str); +#endif + +static void __CFStringDeallocate(CFTypeRef cf) { + CFStringRef str = (CFStringRef)cf; + + // If in DEBUG mode, check to see if the string a CFSTR, and complain. + CFAssert1(__CFConstantStringTableBeingFreed || !__CFStrIsConstantString((CFStringRef)cf), __kCFLogAssertion, "Tried to deallocate CFSTR(\"%@\")", str); + + if (!__CFStrIsInline(str)) { + uint8_t *contents; + Boolean isMutable = __CFStrIsMutable(str); + if (__CFStrFreeContentsWhenDone(str) && (contents = (uint8_t *)__CFStrContents(str))) { + if (isMutable) { + __CFStrDeallocateMutableContents((CFMutableStringRef)str, contents); + } else { + if (__CFStrHasContentsDeallocator(str)) { + CFAllocatorRef contentsDeallocator = __CFStrContentsDeallocator(str); + CFAllocatorDeallocate(contentsDeallocator, contents); + CFRelease(contentsDeallocator); + } else { + CFAllocatorRef alloc = __CFGetAllocator(str); + CFAllocatorDeallocate(alloc, contents); + } + } + } + if (isMutable && __CFStrHasContentsAllocator(str)) CFRelease(__CFStrContentsAllocator((CFMutableStringRef)str)); + } +} + +static Boolean __CFStringEqual(CFTypeRef cf1, CFTypeRef cf2) { + CFStringRef str1 = (CFStringRef)cf1; + CFStringRef str2 = (CFStringRef)cf2; + const uint8_t *contents1; + const uint8_t *contents2; + CFIndex len1; + + /* !!! We do not need IsString assertions, as the CFBase runtime assures this */ + /* !!! We do not need == test, as the CFBase runtime assures this */ + + contents1 = (uint8_t *)__CFStrContents(str1); + contents2 = (uint8_t *)__CFStrContents(str2); + len1 = __CFStrLength2(str1, contents1); + + if (len1 != __CFStrLength2(str2, contents2)) return false; + + contents1 += __CFStrSkipAnyLengthByte(str1); + contents2 += __CFStrSkipAnyLengthByte(str2); + + if (__CFStrIsEightBit(str1) && __CFStrIsEightBit(str2)) { + return memcmp((const char *)contents1, (const char *)contents2, len1) ? false : true; + } else if (__CFStrIsEightBit(str1)) { /* One string has Unicode contents */ + CFStringInlineBuffer buf; + CFIndex buf_idx = 0; + + CFStringInitInlineBuffer(str1, &buf, CFRangeMake(0, len1)); + for (buf_idx = 0; buf_idx < len1; buf_idx++) { + if (__CFStringGetCharacterFromInlineBufferQuick(&buf, buf_idx) != ((UniChar *)contents2)[buf_idx]) return false; + } + } else if (__CFStrIsEightBit(str2)) { /* One string has Unicode contents */ + CFStringInlineBuffer buf; + CFIndex buf_idx = 0; + + CFStringInitInlineBuffer(str2, &buf, CFRangeMake(0, len1)); + for (buf_idx = 0; buf_idx < len1; buf_idx++) { + if (__CFStringGetCharacterFromInlineBufferQuick(&buf, buf_idx) != ((UniChar *)contents1)[buf_idx]) return false; + } + } else { /* Both strings have Unicode contents */ + CFIndex idx; + for (idx = 0; idx < len1; idx++) { + if (((UniChar *)contents1)[idx] != ((UniChar *)contents2)[idx]) return false; + } + } + return true; +} + + +/* String hashing: Should give the same results whatever the encoding; so we hash UniChars. +If the length is less than or equal to 96, then the hash function is simply the +following (n is the nth UniChar character, starting from 0): + + hash(-1) = length + hash(n) = hash(n-1) * 257 + unichar(n); + Hash = hash(length-1) * ((length & 31) + 1) + +If the length is greater than 96, then the above algorithm applies to +characters 0..31, (length/2)-16..(length/2)+15, and length-32..length-1, inclusive; +thus the first, middle, and last 32 characters. + +Note that the loops below are unrolled; and: 257^2 = 66049; 257^3 = 16974593; 257^4 = 4362470401; 67503105 is 257^4 - 256^4 +If hashcode is changed from UInt32 to something else, this last piece needs to be readjusted. +!!! We haven't updated for LP64 yet + +NOTE: The hash algorithm used to be duplicated in CF and Foundation; but now it should only be in the four functions below. + +Hash function was changed between Panther and Tiger, and Tiger and Leopard. +*/ +#define HashEverythingLimit 96 + +#define HashNextFourUniChars(accessStart, accessEnd, pointer) \ + {result = result * 67503105 + (accessStart 0 accessEnd) * 16974593 + (accessStart 1 accessEnd) * 66049 + (accessStart 2 accessEnd) * 257 + (accessStart 3 accessEnd); pointer += 4;} + +#define HashNextUniChar(accessStart, accessEnd, pointer) \ + {result = result * 257 + (accessStart 0 accessEnd); pointer++;} + + +/* In this function, actualLen is the length of the original string; but len is the number of characters in buffer. The buffer is expected to contain the parts of the string relevant to hashing. +*/ +CF_INLINE CFHashCode __CFStrHashCharacters(const UniChar *uContents, CFIndex len, CFIndex actualLen) { + CFHashCode result = actualLen; + if (len <= HashEverythingLimit) { + const UniChar *end4 = uContents + (len & ~3); + const UniChar *end = uContents + len; + while (uContents < end4) HashNextFourUniChars(uContents[, ], uContents); // First count in fours + while (uContents < end) HashNextUniChar(uContents[, ], uContents); // Then for the last <4 chars, count in ones... + } else { + const UniChar *contents, *end; + contents = uContents; + end = contents + 32; + while (contents < end) HashNextFourUniChars(contents[, ], contents); + contents = uContents + (len >> 1) - 16; + end = contents + 32; + while (contents < end) HashNextFourUniChars(contents[, ], contents); + end = uContents + len; + contents = end - 32; + while (contents < end) HashNextFourUniChars(contents[, ], contents); + } + return result + (result << (actualLen & 31)); +} + +/* This hashes cString in the eight bit string encoding. It also includes the little debug-time sanity check. +*/ +CF_INLINE CFHashCode __CFStrHashEightBit(const uint8_t *cContents, CFIndex len) { +#if defined(DEBUG) + if (!__CFCharToUniCharFunc) { // A little sanity verification: If this is not set, trying to hash high byte chars would be a bad idea + CFIndex cnt; + Boolean err = false; + if (len <= HashEverythingLimit) { + for (cnt = 0; cnt < len; cnt++) if (cContents[cnt] >= 128) err = true; + } else { + for (cnt = 0; cnt < 32; cnt++) if (cContents[cnt] >= 128) err = true; + for (cnt = (len >> 1) - 16; cnt < (len >> 1) + 16; cnt++) if (cContents[cnt] >= 128) err = true; + for (cnt = (len - 32); cnt < len; cnt++) if (cContents[cnt] >= 128) err = true; + } + if (err) { + // Can't do log here, as it might be too early + fprintf(stderr, "Warning: CFHash() attempting to hash CFString containing high bytes before properly initialized to do so\n"); + } + } +#endif + CFHashCode result = len; + if (len <= HashEverythingLimit) { + const uint8_t *end4 = cContents + (len & ~3); + const uint8_t *end = cContents + len; + while (cContents < end4) HashNextFourUniChars(__CFCharToUniCharTable[cContents[, ]], cContents); // First count in fours + while (cContents < end) HashNextUniChar(__CFCharToUniCharTable[cContents[, ]], cContents); // Then for the last <4 chars, count in ones... + } else { + const uint8_t *contents, *end; + contents = cContents; + end = contents + 32; + while (contents < end) HashNextFourUniChars(__CFCharToUniCharTable[contents[, ]], contents); + contents = cContents + (len >> 1) - 16; + end = contents + 32; + while (contents < end) HashNextFourUniChars(__CFCharToUniCharTable[contents[, ]], contents); + end = cContents + len; + contents = end - 32; + while (contents < end) HashNextFourUniChars(__CFCharToUniCharTable[contents[, ]], contents); + } + return result + (result << (len & 31)); +} + +CFHashCode CFStringHashISOLatin1CString(const uint8_t *bytes, CFIndex len) { + CFHashCode result = len; + if (len <= HashEverythingLimit) { + const uint8_t *end4 = bytes + (len & ~3); + const uint8_t *end = bytes + len; + while (bytes < end4) HashNextFourUniChars(bytes[, ], bytes); // First count in fours + while (bytes < end) HashNextUniChar(bytes[, ], bytes); // Then for the last <4 chars, count in ones... + } else { + const uint8_t *contents, *end; + contents = bytes; + end = contents + 32; + while (contents < end) HashNextFourUniChars(contents[, ], contents); + contents = bytes + (len >> 1) - 16; + end = contents + 32; + while (contents < end) HashNextFourUniChars(contents[, ], contents); + end = bytes + len; + contents = end - 32; + while (contents < end) HashNextFourUniChars(contents[, ], contents); + } + return result + (result << (len & 31)); +} + +CFHashCode CFStringHashCString(const uint8_t *bytes, CFIndex len) { + return __CFStrHashEightBit(bytes, len); +} + +CFHashCode CFStringHashCharacters(const UniChar *characters, CFIndex len) { + return __CFStrHashCharacters(characters, len, len); +} + +/* This is meant to be called from NSString or subclassers only. It is an error for this to be called without the ObjC runtime or an argument which is not an NSString or subclass. It can be called with NSCFString, although that would be inefficient (causing indirection) and won't normally happen anyway, as NSCFString overrides hash. +*/ +CFHashCode CFStringHashNSString(CFStringRef str) { + UniChar buffer[HashEverythingLimit]; + CFIndex bufLen; // Number of characters in the buffer for hashing + CFIndex len = 0; // Actual length of the string + + CF_OBJC_CALL0(CFIndex, len, str, "length"); + if (len <= HashEverythingLimit) { + CF_OBJC_VOIDCALL2(str, "getCharacters:range:", buffer, CFRangeMake(0, len)); + bufLen = len; + } else { + CF_OBJC_VOIDCALL2(str, "getCharacters:range:", buffer, CFRangeMake(0, 32)); + CF_OBJC_VOIDCALL2(str, "getCharacters:range:", buffer+32, CFRangeMake((len >> 1) - 16, 32)); + CF_OBJC_VOIDCALL2(str, "getCharacters:range:", buffer+64, CFRangeMake(len - 32, 32)); + bufLen = HashEverythingLimit; + } + return __CFStrHashCharacters(buffer, bufLen, len); +} + +CFHashCode __CFStringHash(CFTypeRef cf) { + /* !!! We do not need an IsString assertion here, as this is called by the CFBase runtime only */ + CFStringRef str = (CFStringRef)cf; + const uint8_t *contents = (uint8_t *)__CFStrContents(str); + CFIndex len = __CFStrLength2(str, contents); + + if (__CFStrIsEightBit(str)) { + contents += __CFStrSkipAnyLengthByte(str); + return __CFStrHashEightBit(contents, len); + } else { + return __CFStrHashCharacters((const UniChar *)contents, len, len); + } +} + + +static CFStringRef __CFStringCopyDescription(CFTypeRef cf) { + return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFString %p [%p]>{contents = \"%@\"}"), cf, __CFGetAllocator(cf), cf); +} + +static CFStringRef __CFStringCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) { + return (CFStringRef)CFStringCreateCopy(__CFGetAllocator(cf), (CFStringRef)cf); +} + +static CFTypeID __kCFStringTypeID = _kCFRuntimeNotATypeID; + +typedef CFTypeRef (*CF_STRING_CREATE_COPY)(CFAllocatorRef alloc, CFTypeRef theString); + +static const CFRuntimeClass __CFStringClass = { + 0, + "CFString", + NULL, // init + (CF_STRING_CREATE_COPY)CFStringCreateCopy, + __CFStringDeallocate, + __CFStringEqual, + __CFStringHash, + __CFStringCopyFormattingDescription, + __CFStringCopyDescription +}; + +__private_extern__ void __CFStringInitialize(void) { + __kCFStringTypeID = _CFRuntimeRegisterClass(&__CFStringClass); +} + +CFTypeID CFStringGetTypeID(void) { + return __kCFStringTypeID; +} + + +static Boolean CFStrIsUnicode(CFStringRef str) { + CF_OBJC_FUNCDISPATCH0(__kCFStringTypeID, Boolean, str, "_encodingCantBeStoredInEightBitCFString"); + return __CFStrIsUnicode(str); +} + + + +#define ALLOCATORSFREEFUNC ((CFAllocatorRef)-1) + +/* contentsDeallocator indicates how to free the data if it's noCopy == true: + kCFAllocatorNull: don't free + ALLOCATORSFREEFUNC: free with main allocator's free func (don't pass in the real func ptr here) + NULL: default allocator + otherwise it's the allocator that should be used (it will be explicitly stored) + if noCopy == false, then freeFunc should be ALLOCATORSFREEFUNC + hasLengthByte, hasNullByte: refers to bytes; used only if encoding != Unicode + possiblyExternalFormat indicates that the bytes might have BOM and be swapped + tryToReduceUnicode means that the Unicode should be checked to see if it contains just ASCII (and reduce it if so) + numBytes contains the actual number of bytes in "bytes", including Length byte, + BUT not the NULL byte at the end + bytes should not contain BOM characters + !!! Various flags should be combined to reduce number of arguments, if possible +*/ +__private_extern__ CFStringRef __CFStringCreateImmutableFunnel3( + CFAllocatorRef alloc, const void *bytes, CFIndex numBytes, CFStringEncoding encoding, + Boolean possiblyExternalFormat, Boolean tryToReduceUnicode, Boolean hasLengthByte, Boolean hasNullByte, Boolean noCopy, + CFAllocatorRef contentsDeallocator, UInt32 converterFlags) { + + CFMutableStringRef str; + CFVarWidthCharBuffer vBuf; + CFIndex size; + Boolean useLengthByte = false; + Boolean useNullByte = false; + Boolean useInlineData = false; + +#if INSTRUMENT_SHARED_STRINGS + const char *recordedEncoding; + char encodingBuffer[128]; + if (encoding == kCFStringEncodingUnicode) recordedEncoding = "Unicode"; + else if (encoding == kCFStringEncodingASCII) recordedEncoding = "ASCII"; + else if (encoding == kCFStringEncodingUTF8) recordedEncoding = "UTF8"; + else if (encoding == kCFStringEncodingMacRoman) recordedEncoding = "MacRoman"; + else { + sprintf(encodingBuffer, "0x%lX", (unsigned long)encoding); + recordedEncoding = encodingBuffer; + } +#endif + + if (alloc == NULL) alloc = __CFGetDefaultAllocator(); + + if (contentsDeallocator == ALLOCATORSFREEFUNC) { + contentsDeallocator = alloc; + } else if (contentsDeallocator == NULL) { + contentsDeallocator = __CFGetDefaultAllocator(); + } + + if ((NULL != kCFEmptyString) && (numBytes == 0) && (alloc == kCFAllocatorSystemDefault)) { // If we are using the system default allocator, and the string is empty, then use the empty string! + if (noCopy && (contentsDeallocator != kCFAllocatorNull)) { // See 2365208... This change was done after Sonata; before we didn't free the bytes at all (leak). + CFAllocatorDeallocate(contentsDeallocator, (void *)bytes); + } + return (CFStringRef)CFRetain(kCFEmptyString); // Quick exit; won't catch all empty strings, but most + } + + // At this point, contentsDeallocator is either same as alloc, or kCFAllocatorNull, or something else, but not NULL + + vBuf.shouldFreeChars = false; // We use this to remember to free the buffer possibly allocated by decode + + // Record whether we're starting out with an ASCII-superset string, because we need to know this later for the string ROM; this may get changed later if we successfully convert down from Unicode. We only record this once because __CFCanUseEightBitCFStringForBytes() can be expensive. + Boolean stringSupportsEightBitCFRepresentation = encoding != kCFStringEncodingUnicode && __CFCanUseEightBitCFStringForBytes((const uint8_t *)bytes, numBytes, encoding); + + // We may also change noCopy within this function if we have to decode the string into an external buffer. We do not want to avoid the use of the string ROM merely because we tried to be efficient and reuse the decoded buffer for the CFString's external storage. Therefore, we use this variable to track whether we actually can ignore the noCopy flag (which may or may not be set anyways). + Boolean stringROMShouldIgnoreNoCopy = false; + + // First check to see if the data needs to be converted... + // ??? We could be more efficient here and in some cases (Unicode data) eliminate a copy + + if ((encoding == kCFStringEncodingUnicode && possiblyExternalFormat) || (encoding != kCFStringEncodingUnicode && !stringSupportsEightBitCFRepresentation)) { + const void *realBytes = (uint8_t *) bytes + (hasLengthByte ? 1 : 0); + CFIndex realNumBytes = numBytes - (hasLengthByte ? 1 : 0); + Boolean usingPassedInMemory = false; + + vBuf.allocator = __CFGetDefaultAllocator(); // We don't want to use client's allocator for temp stuff + vBuf.chars.unicode = NULL; // This will cause the decode function to allocate memory if necessary + + if (!__CFStringDecodeByteStream3((const uint8_t *)realBytes, realNumBytes, encoding, false, &vBuf, &usingPassedInMemory, converterFlags)) { + // Note that if the string can't be created, we don't free the buffer, even if there is a contents deallocator. This is on purpose. + return NULL; + } + + encoding = vBuf.isASCII ? kCFStringEncodingASCII : kCFStringEncodingUnicode; + + // Update our flag according to whether the decoded buffer is ASCII + stringSupportsEightBitCFRepresentation = vBuf.isASCII; + + if (!usingPassedInMemory) { + + // Because __CFStringDecodeByteStream3() allocated our buffer, it's OK for us to free it if we can get the string from the ROM. + stringROMShouldIgnoreNoCopy = true; + + // Make the parameters fit the new situation + numBytes = vBuf.isASCII ? vBuf.numChars : (vBuf.numChars * sizeof(UniChar)); + hasLengthByte = hasNullByte = false; + + // Get rid of the original buffer if its not being used + if (noCopy && (contentsDeallocator != kCFAllocatorNull)) { + CFAllocatorDeallocate(contentsDeallocator, (void *)bytes); + } + contentsDeallocator = alloc; // At this point we are using the string's allocator, as the original buffer is gone... + + // See if we can reuse any storage the decode func might have allocated + // We do this only for Unicode, as otherwise we would not have NULL and Length bytes + + if (vBuf.shouldFreeChars && (alloc == vBuf.allocator) && encoding == kCFStringEncodingUnicode) { + vBuf.shouldFreeChars = false; // Transferring ownership to the CFString + bytes = CFAllocatorReallocate(vBuf.allocator, (void *)vBuf.chars.unicode, numBytes, 0); // Tighten up the storage + noCopy = true; +#if INSTRUMENT_SHARED_STRINGS + if (encoding == kCFStringEncodingASCII) recordedEncoding = "ForeignASCII-NoCopy"; + else recordedEncoding = "ForeignUnicode-NoCopy"; +#endif + } else { +#if INSTRUMENT_SHARED_STRINGS + if (encoding == kCFStringEncodingASCII) recordedEncoding = "ForeignASCII-Copy"; + else recordedEncoding = "ForeignUnicode-Copy"; +#endif + bytes = vBuf.chars.unicode; + noCopy = false; // Can't do noCopy anymore + // If vBuf.shouldFreeChars is true, the buffer will be freed as intended near the end of this func + } + + } + + // At this point, all necessary input arguments have been changed to reflect the new state + + } else if (encoding == kCFStringEncodingUnicode && tryToReduceUnicode) { // Check to see if we can reduce Unicode to ASCII + CFIndex cnt; + CFIndex len = numBytes / sizeof(UniChar); + Boolean allASCII = true; + + for (cnt = 0; cnt < len; cnt++) if (((const UniChar *)bytes)[cnt] > 127) { + allASCII = false; + break; + } + + if (allASCII) { // Yes we can! + uint8_t *ptr, *mem; + Boolean newHasLengthByte = __CFCanUseLengthByte(len); + numBytes = (len + 1 + (newHasLengthByte ? 1 : 0)) * sizeof(uint8_t); // NULL and possible length byte + // See if we can use that temporary local buffer in vBuf... + if (numBytes >= __kCFVarWidthLocalBufferSize) { + mem = ptr = (uint8_t *)CFAllocatorAllocate(alloc, numBytes, 0); + if (__CFOASafe) __CFSetLastAllocationEventName(mem, "CFString (store)"); + } else { + mem = ptr = (uint8_t *)(vBuf.localBuffer); + } + if (mem) { // If we can't allocate memory for some reason, use what we had (that is, as if we didn't have all ASCII) + // Copy the Unicode bytes into the new ASCII buffer + hasLengthByte = newHasLengthByte; + hasNullByte = true; + if (hasLengthByte) *ptr++ = (uint8_t)len; + for (cnt = 0; cnt < len; cnt++) ptr[cnt] = (uint8_t)(((const UniChar *)bytes)[cnt]); + ptr[len] = 0; + if (noCopy && (contentsDeallocator != kCFAllocatorNull)) { + CFAllocatorDeallocate(contentsDeallocator, (void *)bytes); + } + // Now make everything look like we had an ASCII buffer to start with + bytes = mem; + encoding = kCFStringEncodingASCII; + contentsDeallocator = alloc; // At this point we are using the string's allocator, as the original buffer is gone... + noCopy = (numBytes >= __kCFVarWidthLocalBufferSize); // If we had to allocate it, make sure it's kept around + numBytes--; // Should not contain the NULL byte at end... + stringSupportsEightBitCFRepresentation = true; // We're ASCII now! + stringROMShouldIgnoreNoCopy = true; // We allocated this buffer, so we should feel free to get rid of it if we can use the string ROM +#if INSTRUMENT_SHARED_STRINGS + recordedEncoding = "U->A"; +#endif + } + } + + // At this point, all necessary input arguments have been changed to reflect the new state + } + + // Now determine the necessary size + + Boolean stringSupportsROM = stringSupportsEightBitCFRepresentation; + +#if INSTRUMENT_SHARED_STRINGS + if (stringSupportsROM) { + const void *realBytes = (uint8_t *) bytes + (hasLengthByte ? 1 : 0); + CFIndex realNumBytes = numBytes - !! hasLengthByte; + __CFRecordStringAllocationEvent(recordedEncoding, realBytes, realNumBytes); + } +#endif + + CFStringRef romResult = NULL; + +#if USE_STRING_ROM + + if (stringSupportsROM) { + // Disable the string ROM if necessary + static char sDisableStringROM = -1; + if (sDisableStringROM == -1) sDisableStringROM = !! getenv("CFStringDisableROM"); + + if (sDisableStringROM == 0) romResult = _CFSearchStringROM(bytes + !! hasLengthByte, numBytes - !! hasLengthByte); + } + /* if we get a result from our ROM, and noCopy is set, then deallocate the buffer immediately */ + if (romResult) { + if (noCopy && (contentsDeallocator != kCFAllocatorNull)) { + CFAllocatorDeallocate(contentsDeallocator, (void *)bytes); + } + + /* these don't get used again, but clear them for consistency */ + noCopy = false; + bytes = NULL; + + /* set our result to the ROM result which is not really mutable, of course, but that's OK because we don't try to modify it. */ + str = (CFMutableStringRef)romResult; + } +#endif + + if (! romResult) { + // Now determine the necessary size + + if (noCopy) { + + size = sizeof(void *); // Pointer to the buffer + if (contentsDeallocator != alloc && contentsDeallocator != kCFAllocatorNull) { + size += sizeof(void *); // The contentsDeallocator + } + if (!hasLengthByte) size += sizeof(CFIndex); // Explicit length + useLengthByte = hasLengthByte; + useNullByte = hasNullByte; + + } else { // Inline data; reserve space for it + + useInlineData = true; + size = numBytes; + + if (hasLengthByte || (encoding != kCFStringEncodingUnicode && __CFCanUseLengthByte(numBytes))) { + useLengthByte = true; + if (!hasLengthByte) size += 1; + } else { + size += sizeof(CFIndex); // Explicit length + } + if (hasNullByte || encoding != kCFStringEncodingUnicode) { + useNullByte = true; + size += 1; + } + } + +#ifdef STRING_SIZE_STATS + // Dump alloced CFString size info every so often + static int cnt = 0; + static unsigned sizes[256] = {0}; + int allocedSize = size + sizeof(CFRuntimeBase); + if (allocedSize < 255) sizes[allocedSize]++; else sizes[255]++; + if ((++cnt % 1000) == 0) { + printf ("\nTotal: %d\n", cnt); + int i; for (i = 0; i < 256; i++) printf("%03d: %5d%s", i, sizes[i], ((i % 8) == 7) ? "\n" : " "); + } +#endif + + // Finally, allocate! + + str = (CFMutableStringRef)_CFRuntimeCreateInstance(alloc, __kCFStringTypeID, size, NULL); + if (str) { + if (__CFOASafe) __CFSetLastAllocationEventName(str, "CFString (immutable)"); + + __CFStrSetInfoBits(str, + (useInlineData ? __kCFHasInlineContents : (contentsDeallocator == alloc ? __kCFNotInlineContentsDefaultFree : (contentsDeallocator == kCFAllocatorNull ? __kCFNotInlineContentsNoFree : __kCFNotInlineContentsCustomFree))) | + ((encoding == kCFStringEncodingUnicode) ? __kCFIsUnicode : 0) | + (useNullByte ? __kCFHasNullByte : 0) | + (useLengthByte ? __kCFHasLengthByte : 0)); + + if (!useLengthByte) { + CFIndex length = numBytes - (hasLengthByte ? 1 : 0); + if (encoding == kCFStringEncodingUnicode) length /= sizeof(UniChar); + __CFStrSetExplicitLength(str, length); + } + + if (useInlineData) { + uint8_t *contents = (uint8_t *)__CFStrContents(str); + if (useLengthByte && !hasLengthByte) *contents++ = (uint8_t)numBytes; + memmove(contents, bytes, numBytes); + if (useNullByte) contents[numBytes] = 0; + } else { + __CFStrSetContentPtr(str, bytes); + if (contentsDeallocator != alloc && contentsDeallocator != kCFAllocatorNull) __CFStrSetContentsDeallocator(str, (CFAllocatorRef)CFRetain(contentsDeallocator)); + } + } else { + if (noCopy && (contentsDeallocator != kCFAllocatorNull)) { + CFAllocatorDeallocate(contentsDeallocator, (void *)bytes); + } + } + } + if (vBuf.shouldFreeChars) CFAllocatorDeallocate(vBuf.allocator, (void *)bytes); + + return str; +} + +/* !!! __CFStringCreateImmutableFunnel2() is kept around for compatibility; it should be deprecated +*/ +CFStringRef __CFStringCreateImmutableFunnel2( + CFAllocatorRef alloc, const void *bytes, CFIndex numBytes, CFStringEncoding encoding, + Boolean possiblyExternalFormat, Boolean tryToReduceUnicode, Boolean hasLengthByte, Boolean hasNullByte, Boolean noCopy, + CFAllocatorRef contentsDeallocator) { + return __CFStringCreateImmutableFunnel3(alloc, bytes, numBytes, encoding, possiblyExternalFormat, tryToReduceUnicode, hasLengthByte, hasNullByte, noCopy, contentsDeallocator, 0); +} + + + +CFStringRef CFStringCreateWithPascalString(CFAllocatorRef alloc, ConstStringPtr pStr, CFStringEncoding encoding) { + CFIndex len = (CFIndex)(*(uint8_t *)pStr); + return __CFStringCreateImmutableFunnel3(alloc, pStr, len+1, encoding, false, false, true, false, false, ALLOCATORSFREEFUNC, 0); +} + + +CFStringRef CFStringCreateWithCString(CFAllocatorRef alloc, const char *cStr, CFStringEncoding encoding) { + CFIndex len = (CFIndex)strlen(cStr); + return __CFStringCreateImmutableFunnel3(alloc, cStr, len, encoding, false, false, false, true, false, ALLOCATORSFREEFUNC, 0); +} + +CFStringRef CFStringCreateWithPascalStringNoCopy(CFAllocatorRef alloc, ConstStringPtr pStr, CFStringEncoding encoding, CFAllocatorRef contentsDeallocator) { + CFIndex len = (CFIndex)(*(uint8_t *)pStr); + return __CFStringCreateImmutableFunnel3(alloc, pStr, len+1, encoding, false, false, true, false, true, contentsDeallocator, 0); +} + + +CFStringRef CFStringCreateWithCStringNoCopy(CFAllocatorRef alloc, const char *cStr, CFStringEncoding encoding, CFAllocatorRef contentsDeallocator) { + CFIndex len = (CFIndex)strlen(cStr); + return __CFStringCreateImmutableFunnel3(alloc, cStr, len, encoding, false, false, false, true, true, contentsDeallocator, 0); +} + + +CFStringRef CFStringCreateWithCharacters(CFAllocatorRef alloc, const UniChar *chars, CFIndex numChars) { + return __CFStringCreateImmutableFunnel3(alloc, chars, numChars * sizeof(UniChar), kCFStringEncodingUnicode, false, true, false, false, false, ALLOCATORSFREEFUNC, 0); +} + + +CFStringRef CFStringCreateWithCharactersNoCopy(CFAllocatorRef alloc, const UniChar *chars, CFIndex numChars, CFAllocatorRef contentsDeallocator) { + return __CFStringCreateImmutableFunnel3(alloc, chars, numChars * sizeof(UniChar), kCFStringEncodingUnicode, false, false, false, false, true, contentsDeallocator, 0); +} + + +CFStringRef CFStringCreateWithBytes(CFAllocatorRef alloc, const uint8_t *bytes, CFIndex numBytes, CFStringEncoding encoding, Boolean externalFormat) { + return __CFStringCreateImmutableFunnel3(alloc, bytes, numBytes, encoding, externalFormat, true, false, false, false, ALLOCATORSFREEFUNC, 0); +} + +CFStringRef _CFStringCreateWithBytesNoCopy(CFAllocatorRef alloc, const uint8_t *bytes, CFIndex numBytes, CFStringEncoding encoding, Boolean externalFormat, CFAllocatorRef contentsDeallocator) { + return __CFStringCreateImmutableFunnel3(alloc, bytes, numBytes, encoding, externalFormat, true, false, false, true, contentsDeallocator, 0); +} + +CFStringRef CFStringCreateWithBytesNoCopy(CFAllocatorRef alloc, const uint8_t *bytes, CFIndex numBytes, CFStringEncoding encoding, Boolean externalFormat, CFAllocatorRef contentsDeallocator) { + return __CFStringCreateImmutableFunnel3(alloc, bytes, numBytes, encoding, externalFormat, true, false, false, true, contentsDeallocator, 0); +} + +CFStringRef CFStringCreateWithFormatAndArguments(CFAllocatorRef alloc, CFDictionaryRef formatOptions, CFStringRef format, va_list arguments) { + return _CFStringCreateWithFormatAndArgumentsAux(alloc, NULL, formatOptions, format, arguments); +} + +CFStringRef _CFStringCreateWithFormatAndArgumentsAux(CFAllocatorRef alloc, CFStringRef (*copyDescFunc)(void *, const void *), CFDictionaryRef formatOptions, CFStringRef format, va_list arguments) { + CFStringRef str; + CFMutableStringRef outputString = CFStringCreateMutable(__CFGetDefaultAllocator(), 0); //should use alloc if no copy/release + __CFStrSetDesiredCapacity(outputString, 120); // Given this will be tightened later, choosing a larger working string is fine + _CFStringAppendFormatAndArgumentsAux(outputString, copyDescFunc, formatOptions, format, arguments); + // ??? copy/release should not be necessary here -- just make immutable, compress if possible + // (However, this does make the string inline, and cause the supplied allocator to be used...) + str = (CFStringRef)CFStringCreateCopy(alloc, outputString); + CFRelease(outputString); + return str; +} + +CFStringRef CFStringCreateWithFormat(CFAllocatorRef alloc, CFDictionaryRef formatOptions, CFStringRef format, ...) { + CFStringRef result; + va_list argList; + + va_start(argList, format); + result = CFStringCreateWithFormatAndArguments(alloc, formatOptions, format, argList); + va_end(argList); + + return result; +} + +CFStringRef CFStringCreateWithSubstring(CFAllocatorRef alloc, CFStringRef str, CFRange range) { +// CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID, CFStringRef , str, "_createSubstringWithRange:", CFRangeMake(range.location, range.length)); + + __CFAssertIsString(str); + __CFAssertRangeIsInStringBounds(str, range.location, range.length); + + if ((range.location == 0) && (range.length == __CFStrLength(str))) { /* The substring is the whole string... */ + return (CFStringRef)CFStringCreateCopy(alloc, str); + } else if (__CFStrIsEightBit(str)) { + const uint8_t *contents = (const uint8_t *)__CFStrContents(str); + return __CFStringCreateImmutableFunnel3(alloc, contents + range.location + __CFStrSkipAnyLengthByte(str), range.length, __CFStringGetEightBitStringEncoding(), false, false, false, false, false, ALLOCATORSFREEFUNC, 0); + } else { + const UniChar *contents = (UniChar *)__CFStrContents(str); + return __CFStringCreateImmutableFunnel3(alloc, contents + range.location, range.length * sizeof(UniChar), kCFStringEncodingUnicode, false, true, false, false, false, ALLOCATORSFREEFUNC, 0); + } +} + +CFStringRef CFStringCreateCopy(CFAllocatorRef alloc, CFStringRef str) { +// CF_OBJC_FUNCDISPATCH0(__kCFStringTypeID, CFStringRef, str, "copy"); + + __CFAssertIsString(str); + if (!__CFStrIsMutable((CFStringRef)str) && // If the string is not mutable + ((alloc ? alloc : __CFGetDefaultAllocator()) == __CFGetAllocator(str)) && // and it has the same allocator as the one we're using + (__CFStrIsInline((CFStringRef)str) || __CFStrFreeContentsWhenDone((CFStringRef)str) || __CFStrIsConstant((CFStringRef)str))) { // and the characters are inline, or are owned by the string, or the string is constant + CFRetain(str); // Then just retain instead of making a true copy + return str; + } + if (__CFStrIsEightBit((CFStringRef)str)) { + const uint8_t *contents = (const uint8_t *)__CFStrContents((CFStringRef)str); + return __CFStringCreateImmutableFunnel3(alloc, contents + __CFStrSkipAnyLengthByte((CFStringRef)str), __CFStrLength2((CFStringRef)str, contents), __CFStringGetEightBitStringEncoding(), false, false, false, false, false, ALLOCATORSFREEFUNC, 0); + } else { + const UniChar *contents = (const UniChar *)__CFStrContents((CFStringRef)str); + return __CFStringCreateImmutableFunnel3(alloc, contents, __CFStrLength2((CFStringRef)str, contents) * sizeof(UniChar), kCFStringEncodingUnicode, false, true, false, false, false, ALLOCATORSFREEFUNC, 0); + } +} + + + +/*** Constant string stuff... ***/ + +/* Table which holds constant strings created with CFSTR, when -fconstant-cfstrings option is not used. These dynamically created constant strings are stored in constantStringTable. The keys are the 8-bit constant C-strings from the compiler; the values are the CFStrings created for them. _CFSTRLock protects this table. +*/ +static CFMutableDictionaryRef constantStringTable = NULL; +static CFSpinLock_t _CFSTRLock = CFSpinLockInit; + +static CFStringRef __cStrCopyDescription(const void *ptr) { + return CFStringCreateWithCStringNoCopy(kCFAllocatorSystemDefault, (const char *)ptr, __CFStringGetEightBitStringEncoding(), kCFAllocatorNull); +} + +static Boolean __cStrEqual(const void *ptr1, const void *ptr2) { + return (strcmp((const char *)ptr1, (const char *)ptr2) == 0); +} + +static CFHashCode __cStrHash(const void *ptr) { + // It doesn't quite matter if we convert to Unicode correctly, as long as we do it consistently + const char *cStr = (const char *)ptr; + CFIndex len = (CFIndex)strlen(cStr); + CFHashCode result = 0; + if (len <= 4) { // All chars + unsigned cnt = len; + while (cnt--) result += (result << 8) + *cStr++; + } else { // First and last 2 chars + result += (result << 8) + cStr[0]; + result += (result << 8) + cStr[1]; + result += (result << 8) + cStr[len-2]; + result += (result << 8) + cStr[len-1]; + } + result += (result << (len & 31)); + return result; +} + + +CFStringRef __CFStringMakeConstantString(const char *cStr) { + CFStringRef result; +#if defined(DEBUG) + // StringTest checks that we share kCFEmptyString, which is defeated by constantStringAllocatorForDebugging + if ('\0' == *cStr) return kCFEmptyString; +#endif + if (constantStringTable == NULL) { + CFDictionaryKeyCallBacks constantStringCallBacks = {0, NULL, NULL, __cStrCopyDescription, __cStrEqual, __cStrHash}; + CFDictionaryValueCallBacks constantStringValueCallBacks = kCFTypeDictionaryValueCallBacks; + constantStringValueCallBacks.equal = NULL; // So that we only find strings that are == + CFMutableDictionaryRef table = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &constantStringCallBacks, &constantStringValueCallBacks); + _CFDictionarySetCapacity(table, 2500); // avoid lots of rehashing + __CFSpinLock(&_CFSTRLock); + if (constantStringTable == NULL) constantStringTable = table; + __CFSpinUnlock(&_CFSTRLock); + if (constantStringTable != table) CFRelease(table); + } + + __CFSpinLock(&_CFSTRLock); + if ((result = (CFStringRef)CFDictionaryGetValue(constantStringTable, cStr))) { + __CFSpinUnlock(&_CFSTRLock); + } else { + __CFSpinUnlock(&_CFSTRLock); + + { + char *key; + Boolean isASCII = true; + // Given this code path is rarer these days, OK to do this extra work to verify the strings + const char *tmp = cStr; + while (*tmp) { + if (*(tmp++) & 0x80) { + isASCII = false; + break; + } + } + if (!isASCII) { + CFMutableStringRef ms = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); + tmp = cStr; + while (*tmp) { + CFStringAppendFormat(ms, NULL, (*tmp & 0x80) ? CFSTR("\\%3o") : CFSTR("%1c"), *tmp); + tmp++; + } + CFLog(kCFLogLevelWarning, CFSTR("WARNING: CFSTR(\"%@\") has non-7 bit chars, interpreting using MacOS Roman encoding for now, but this will change. Please eliminate usages of non-7 bit chars (including escaped characters above \\177 octal) in CFSTR()."), ms); + CFRelease(ms); + } + // Treat non-7 bit chars in CFSTR() as MacOSRoman, for compatibility + result = CFStringCreateWithCString(kCFAllocatorSystemDefault, cStr, kCFStringEncodingMacRoman); + if (result == NULL) { + CFLog(__kCFLogAssertion, CFSTR("Can't interpret CFSTR() as MacOS Roman, crashing")); + HALT; + } + if (__CFOASafe) __CFSetLastAllocationEventName((void *)result, "CFString (CFSTR)"); + if (__CFStrIsEightBit(result)) { + key = (char *)__CFStrContents(result) + __CFStrSkipAnyLengthByte(result); + } else { // For some reason the string is not 8-bit! + key = (char *)CFAllocatorAllocate(kCFAllocatorSystemDefault, (CFIndex)strlen(cStr) + 1, 0); + if (__CFOASafe) __CFSetLastAllocationEventName((void *)key, "CFString (CFSTR key)"); + strlcpy(key, cStr, strlen(cStr) + 1); // !!! We will leak this, if the string is removed from the table (or table is freed) + } + + { + CFStringRef resultToBeReleased = result; + CFIndex count; + __CFSpinLock(&_CFSTRLock); + count = CFDictionaryGetCount(constantStringTable); + CFDictionaryAddValue(constantStringTable, key, result); + if (CFDictionaryGetCount(constantStringTable) == count) { // add did nothing, someone already put it there + result = (CFStringRef)CFDictionaryGetValue(constantStringTable, key); + } else { +#if __LP64__ + ((struct __CFString *)result)->base._rc = 0; +#else + ((struct __CFString *)result)->base._cfinfo[CF_RC_BITS] = 0; +#endif + } + __CFSpinUnlock(&_CFSTRLock); + // This either eliminates the extra retain on the freshly created string, or frees it, if it was actually not inserted into the table + CFRelease(resultToBeReleased); + } + } + } + return result; +} + +#if defined(DEBUG) +static Boolean __CFStrIsConstantString(CFStringRef str) { + Boolean found = false; + if (constantStringTable) { + __CFSpinLock(&_CFSTRLock); + found = CFDictionaryContainsValue(constantStringTable, str); + __CFSpinUnlock(&_CFSTRLock); + } + return found; +} +#endif + + +#if DEPLOYMENT_TARGET_WINDOWS +__private_extern__ void __CFStringCleanup (void) { + /* in case library is unloaded, release store for the constant string table */ + if (constantStringTable != NULL) { +#if defined(DEBUG) + __CFConstantStringTableBeingFreed = true; + CFRelease(constantStringTable); + __CFConstantStringTableBeingFreed = false; +#else + CFRelease(constantStringTable); +#endif + } +} +#endif + + +// Can pass in NSString as replacement string +// Call with numRanges > 0, and incrementing ranges + +static void __CFStringReplaceMultiple(CFMutableStringRef str, CFRange *ranges, CFIndex numRanges, CFStringRef replacement) { + int cnt; + CFStringRef copy = NULL; + if (replacement == str) copy = replacement = CFStringCreateCopy(kCFAllocatorSystemDefault, replacement); // Very special and hopefully rare case + CFIndex replacementLength = CFStringGetLength(replacement); + + __CFStringChangeSizeMultiple(str, ranges, numRanges, replacementLength, (replacementLength > 0) && CFStrIsUnicode(replacement)); + + if (__CFStrIsUnicode(str)) { + UniChar *contents = (UniChar *)__CFStrContents(str); + UniChar *firstReplacement = contents + ranges[0].location; + // Extract the replacementString into the first location, then copy from there + CFStringGetCharacters(replacement, CFRangeMake(0, replacementLength), firstReplacement); + for (cnt = 1; cnt < numRanges; cnt++) { + // The ranges are in terms of the original string; so offset by the change in length due to insertion + contents += replacementLength - ranges[cnt - 1].length; + memmove(contents + ranges[cnt].location, firstReplacement, replacementLength * sizeof(UniChar)); + } + } else { + uint8_t *contents = (uint8_t *)__CFStrContents(str); + uint8_t *firstReplacement = contents + ranges[0].location + __CFStrSkipAnyLengthByte(str); + // Extract the replacementString into the first location, then copy from there + CFStringGetBytes(replacement, CFRangeMake(0, replacementLength), __CFStringGetEightBitStringEncoding(), 0, false, firstReplacement, replacementLength, NULL); + contents += __CFStrSkipAnyLengthByte(str); // Now contents will simply track the location to insert next string into + for (cnt = 1; cnt < numRanges; cnt++) { + // The ranges are in terms of the original string; so offset by the change in length due to insertion + contents += replacementLength - ranges[cnt - 1].length; + memmove(contents + ranges[cnt].location, firstReplacement, replacementLength); + } + } + if (copy) CFRelease(copy); +} + +// Can pass in NSString as replacement string + +CF_INLINE void __CFStringReplace(CFMutableStringRef str, CFRange range, CFStringRef replacement) { + CFStringRef copy = NULL; + if (replacement == str) copy = replacement = (CFStringRef)CFStringCreateCopy(kCFAllocatorSystemDefault, replacement); // Very special and hopefully rare case + CFIndex replacementLength = CFStringGetLength(replacement); + + __CFStringChangeSize(str, range, replacementLength, (replacementLength > 0) && CFStrIsUnicode(replacement)); + + if (__CFStrIsUnicode(str)) { + UniChar *contents = (UniChar *)__CFStrContents(str); + CFStringGetCharacters(replacement, CFRangeMake(0, replacementLength), contents + range.location); + } else { + uint8_t *contents = (uint8_t *)__CFStrContents(str); + CFStringGetBytes(replacement, CFRangeMake(0, replacementLength), __CFStringGetEightBitStringEncoding(), 0, false, contents + range.location + __CFStrSkipAnyLengthByte(str), replacementLength, NULL); + } + + if (copy) CFRelease(copy); +} + +/* If client does not provide a minimum capacity +*/ +#define DEFAULTMINCAPACITY 32 + +CF_INLINE CFMutableStringRef __CFStringCreateMutableFunnel(CFAllocatorRef alloc, CFIndex maxLength, UInt32 additionalInfoBits) { + CFMutableStringRef str; + Boolean hasExternalContentsAllocator = (additionalInfoBits & __kCFHasContentsAllocator) ? true : false; + + if (alloc == NULL) alloc = __CFGetDefaultAllocator(); + + // Note that if there is an externalContentsAllocator, then we also have the storage for the string allocator... + str = (CFMutableStringRef)_CFRuntimeCreateInstance(alloc, __kCFStringTypeID, sizeof(struct __notInlineMutable) - (hasExternalContentsAllocator ? 0 : sizeof(CFAllocatorRef)), NULL); + if (str) { + if (__CFOASafe) __CFSetLastAllocationEventName(str, "CFString (mutable)"); + + __CFStrSetInfoBits(str, __kCFIsMutable | additionalInfoBits); + str->variants.notInlineMutable.buffer = NULL; + __CFStrSetExplicitLength(str, 0); + str->variants.notInlineMutable.hasGap = str->variants.notInlineMutable.isFixedCapacity = str->variants.notInlineMutable.isExternalMutable = str->variants.notInlineMutable.capacityProvidedExternally = 0; + if (maxLength != 0) __CFStrSetIsFixed(str); + __CFStrSetDesiredCapacity(str, (maxLength == 0) ? DEFAULTMINCAPACITY : maxLength); + __CFStrSetCapacity(str, 0); + } + return str; +} + +CFMutableStringRef CFStringCreateMutableWithExternalCharactersNoCopy(CFAllocatorRef alloc, UniChar *chars, CFIndex numChars, CFIndex capacity, CFAllocatorRef externalCharactersAllocator) { + CFOptionFlags contentsAllocationBits = externalCharactersAllocator ? ((externalCharactersAllocator == kCFAllocatorNull) ? __kCFNotInlineContentsNoFree : __kCFHasContentsAllocator) : __kCFNotInlineContentsDefaultFree; + CFMutableStringRef string = __CFStringCreateMutableFunnel(alloc, 0, contentsAllocationBits | __kCFIsUnicode); + if (string) { + __CFStrSetIsExternalMutable(string); + if (contentsAllocationBits == __kCFHasContentsAllocator) __CFStrSetContentsAllocator(string, (CFAllocatorRef)CFRetain(externalCharactersAllocator)); + CFStringSetExternalCharactersNoCopy(string, chars, numChars, capacity); + } + return string; +} + +CFMutableStringRef CFStringCreateMutable(CFAllocatorRef alloc, CFIndex maxLength) { + return __CFStringCreateMutableFunnel(alloc, maxLength, __kCFNotInlineContentsDefaultFree); +} + +CFMutableStringRef CFStringCreateMutableCopy(CFAllocatorRef alloc, CFIndex maxLength, CFStringRef string) { + CFMutableStringRef newString; + + // CF_OBJC_FUNCDISPATCH0(__kCFStringTypeID, CFMutableStringRef, string, "mutableCopy"); + + __CFAssertIsString(string); + + newString = CFStringCreateMutable(alloc, maxLength); + __CFStringReplace(newString, CFRangeMake(0, 0), string); + + return newString; +} + + +__private_extern__ void _CFStrSetDesiredCapacity(CFMutableStringRef str, CFIndex len) { + __CFAssertIsStringAndMutable(str); + __CFStrSetDesiredCapacity(str, len); +} + + +/* This one is for CF +*/ +CFIndex CFStringGetLength(CFStringRef str) { + CF_OBJC_FUNCDISPATCH0(__kCFStringTypeID, CFIndex, str, "length"); + + __CFAssertIsString(str); + return __CFStrLength(str); +} + +/* This one is for NSCFString; it does not ObjC dispatch or assertion check +*/ +CFIndex _CFStringGetLength2(CFStringRef str) { + return __CFStrLength(str); +} + + +/* Guts of CFStringGetCharacterAtIndex(); called from the two functions below. Don't call it from elsewhere. +*/ +CF_INLINE UniChar __CFStringGetCharacterAtIndexGuts(CFStringRef str, CFIndex idx, const uint8_t *contents) { + if (__CFStrIsEightBit(str)) { + contents += __CFStrSkipAnyLengthByte(str); +#if defined(DEBUG) + if (!__CFCharToUniCharFunc && (contents[idx] >= 128)) { + // Can't do log here, as it might be too early + fprintf(stderr, "Warning: CFStringGetCharacterAtIndex() attempted on CFString containing high bytes before properly initialized to do so\n"); + } +#endif + return __CFCharToUniCharTable[contents[idx]]; + } + + return ((UniChar *)contents)[idx]; +} + +/* This one is for the CF API +*/ +UniChar CFStringGetCharacterAtIndex(CFStringRef str, CFIndex idx) { + CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID, UniChar, str, "characterAtIndex:", idx); + + __CFAssertIsString(str); + __CFAssertIndexIsInStringBounds(str, idx); + return __CFStringGetCharacterAtIndexGuts(str, idx, (const uint8_t *)__CFStrContents(str)); +} + +/* This one is for NSCFString usage; it doesn't do ObjC dispatch; but it does do range check +*/ +int _CFStringCheckAndGetCharacterAtIndex(CFStringRef str, CFIndex idx, UniChar *ch) { + const uint8_t *contents = (const uint8_t *)__CFStrContents(str); + if (idx >= __CFStrLength2(str, contents) && __CFStringNoteErrors()) return _CFStringErrBounds; + *ch = __CFStringGetCharacterAtIndexGuts(str, idx, contents); + return _CFStringErrNone; +} + + +/* Guts of CFStringGetCharacters(); called from the two functions below. Don't call it from elsewhere. +*/ +CF_INLINE void __CFStringGetCharactersGuts(CFStringRef str, CFRange range, UniChar *buffer, const uint8_t *contents) { + if (__CFStrIsEightBit(str)) { + __CFStrConvertBytesToUnicode(((uint8_t *)contents) + (range.location + __CFStrSkipAnyLengthByte(str)), buffer, range.length); + } else { + const UniChar *uContents = ((UniChar *)contents) + range.location; + memmove(buffer, uContents, range.length * sizeof(UniChar)); + } +} + +/* This one is for the CF API +*/ +void CFStringGetCharacters(CFStringRef str, CFRange range, UniChar *buffer) { + CF_OBJC_FUNCDISPATCH2(__kCFStringTypeID, void, str, "getCharacters:range:", buffer, CFRangeMake(range.location, range.length)); + + __CFAssertIsString(str); + __CFAssertRangeIsInStringBounds(str, range.location, range.length); + __CFStringGetCharactersGuts(str, range, buffer, (const uint8_t *)__CFStrContents(str)); +} + +/* This one is for NSCFString usage; it doesn't do ObjC dispatch; but it does do range check +*/ +int _CFStringCheckAndGetCharacters(CFStringRef str, CFRange range, UniChar *buffer) { + const uint8_t *contents = (const uint8_t *)__CFStrContents(str); + if (range.location + range.length > __CFStrLength2(str, contents) && __CFStringNoteErrors()) return _CFStringErrBounds; + __CFStringGetCharactersGuts(str, range, buffer, contents); + return _CFStringErrNone; +} + + +CFIndex CFStringGetBytes(CFStringRef str, CFRange range, CFStringEncoding encoding, uint8_t lossByte, Boolean isExternalRepresentation, uint8_t *buffer, CFIndex maxBufLen, CFIndex *usedBufLen) { + + /* No objc dispatch needed here since __CFStringEncodeByteStream works with both CFString and NSString */ + __CFAssertIsNotNegative(maxBufLen); + + if (!CF_IS_OBJC(__kCFStringTypeID, str)) { // If we can grope the ivars, let's do it... + __CFAssertIsString(str); + __CFAssertRangeIsInStringBounds(str, range.location, range.length); + + if (__CFStrIsEightBit(str) && ((__CFStringGetEightBitStringEncoding() == encoding) || (__CFStringGetEightBitStringEncoding() == kCFStringEncodingASCII && __CFStringEncodingIsSupersetOfASCII(encoding)))) { // Requested encoding is equal to the encoding in string + const unsigned char *contents = (const unsigned char *)__CFStrContents(str); + CFIndex cLength = range.length; + + if (buffer) { + if (cLength > maxBufLen) cLength = maxBufLen; + memmove(buffer, contents + __CFStrSkipAnyLengthByte(str) + range.location, cLength); + } + if (usedBufLen) *usedBufLen = cLength; + + return cLength; + } + } + + return __CFStringEncodeByteStream(str, range.location, range.length, isExternalRepresentation, encoding, lossByte, buffer, maxBufLen, usedBufLen); +} + + +ConstStringPtr CFStringGetPascalStringPtr (CFStringRef str, CFStringEncoding encoding) { + + if (!CF_IS_OBJC(__kCFStringTypeID, str)) { /* ??? Hope the compiler optimizes this away if OBJC_MAPPINGS is not on */ + __CFAssertIsString(str); + if (__CFStrHasLengthByte(str) && __CFStrIsEightBit(str) && ((__CFStringGetEightBitStringEncoding() == encoding) || (__CFStringGetEightBitStringEncoding() == kCFStringEncodingASCII && __CFStringEncodingIsSupersetOfASCII(encoding)))) { // Requested encoding is equal to the encoding in string || the contents is in ASCII + const uint8_t *contents = (const uint8_t *)__CFStrContents(str); + if (__CFStrHasExplicitLength(str) && (__CFStrLength2(str, contents) != (SInt32)(*contents))) return NULL; // Invalid length byte + return (ConstStringPtr)contents; + } + // ??? Also check for encoding = SystemEncoding and perhaps bytes are all ASCII? + } + return NULL; +} + + +const char * CFStringGetCStringPtr(CFStringRef str, CFStringEncoding encoding) { + + if (encoding != __CFStringGetEightBitStringEncoding() && (kCFStringEncodingASCII != __CFStringGetEightBitStringEncoding() || !__CFStringEncodingIsSupersetOfASCII(encoding))) return NULL; + // ??? Also check for encoding = SystemEncoding and perhaps bytes are all ASCII? + + CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID, const char *, str, "_fastCStringContents:", true); + + __CFAssertIsString(str); + + if (__CFStrHasNullByte(str)) { + // Note: this is called a lot, 27000 times to open a small xcode project with one file open. + // Of these uses about 1500 are for cStrings/utf8strings. + return (const char *)__CFStrContents(str) + __CFStrSkipAnyLengthByte(str); + } else { + return NULL; + } +} + + +const UniChar *CFStringGetCharactersPtr(CFStringRef str) { + + CF_OBJC_FUNCDISPATCH0(__kCFStringTypeID, const UniChar *, str, "_fastCharacterContents"); + + __CFAssertIsString(str); + if (__CFStrIsUnicode(str)) return (const UniChar *)__CFStrContents(str); + return NULL; +} + + +Boolean CFStringGetPascalString(CFStringRef str, Str255 buffer, CFIndex bufferSize, CFStringEncoding encoding) { + CFIndex length; + CFIndex usedLen; + + __CFAssertIsNotNegative(bufferSize); + if (bufferSize < 1) return false; + + if (CF_IS_OBJC(__kCFStringTypeID, str)) { /* ??? Hope the compiler optimizes this away if OBJC_MAPPINGS is not on */ + length = CFStringGetLength(str); + if (!__CFCanUseLengthByte(length)) return false; // Can't fit into pstring + } else { + const uint8_t *contents; + + __CFAssertIsString(str); + + contents = (const uint8_t *)__CFStrContents(str); + length = __CFStrLength2(str, contents); + + if (!__CFCanUseLengthByte(length)) return false; // Can't fit into pstring + + if (__CFStrIsEightBit(str) && ((__CFStringGetEightBitStringEncoding() == encoding) || (__CFStringGetEightBitStringEncoding() == kCFStringEncodingASCII && __CFStringEncodingIsSupersetOfASCII(encoding)))) { // Requested encoding is equal to the encoding in string + if (length >= bufferSize) return false; + memmove((void*)(1 + (const char*)buffer), (__CFStrSkipAnyLengthByte(str) + contents), length); + *buffer = (unsigned char)length; + return true; + } + } + + if (__CFStringEncodeByteStream(str, 0, length, false, encoding, false, (UInt8 *)(1 + (uint8_t *)buffer), bufferSize - 1, &usedLen) != length) { + +#if defined(DEBUG) + if (bufferSize > 0) { + strlcpy((char *)buffer + 1, CONVERSIONFAILURESTR, bufferSize - 1); + buffer[0] = (unsigned char)((CFIndex)sizeof(CONVERSIONFAILURESTR) < (bufferSize - 1) ? (CFIndex)sizeof(CONVERSIONFAILURESTR) : (bufferSize - 1)); + } +#else + if (bufferSize > 0) buffer[0] = 0; +#endif + return false; + } + *buffer = (unsigned char)usedLen; + return true; +} + +Boolean CFStringGetCString(CFStringRef str, char *buffer, CFIndex bufferSize, CFStringEncoding encoding) { + const uint8_t *contents; + CFIndex len; + + __CFAssertIsNotNegative(bufferSize); + if (bufferSize < 1) return false; + + CF_OBJC_FUNCDISPATCH3(__kCFStringTypeID, Boolean, str, "_getCString:maxLength:encoding:", buffer, bufferSize - 1, encoding); + + __CFAssertIsString(str); + + contents = (const uint8_t *)__CFStrContents(str); + len = __CFStrLength2(str, contents); + + if (__CFStrIsEightBit(str) && ((__CFStringGetEightBitStringEncoding() == encoding) || (__CFStringGetEightBitStringEncoding() == kCFStringEncodingASCII && __CFStringEncodingIsSupersetOfASCII(encoding)))) { // Requested encoding is equal to the encoding in string + if (len >= bufferSize) return false; + memmove(buffer, contents + __CFStrSkipAnyLengthByte(str), len); + buffer[len] = 0; + return true; + } else { + CFIndex usedLen; + + if (__CFStringEncodeByteStream(str, 0, len, false, encoding, false, (unsigned char*) buffer, bufferSize - 1, &usedLen) == len) { + buffer[usedLen] = '\0'; + return true; + } else { +#if defined(DEBUG) + strlcpy(buffer, CONVERSIONFAILURESTR, bufferSize); +#else + if (bufferSize > 0) buffer[0] = 0; +#endif + return false; + } + } +} + +static const char *_CFStrGetLanguageIdentifierForLocale(CFLocaleRef locale) { + CFStringRef collatorID; + const char *langID = NULL; + static const void *lastLocale = NULL; + static const char *lastLangID = NULL; + static CFSpinLock_t lock = CFSpinLockInit; + + __CFSpinLock(&lock); + if ((NULL != lastLocale) && (lastLocale == locale)) { + __CFSpinUnlock(&lock); + return lastLangID; + } + __CFSpinUnlock(&lock); + + collatorID = (CFStringRef)CFLocaleGetValue(locale, __kCFLocaleCollatorID); + + // This is somewhat depending on CFLocale implementation always creating CFString for locale identifer ??? + if (__CFStrLength(collatorID) > 1) { + const void *contents = __CFStrContents(collatorID); + const char *string; + char buffer[2]; + + if (__CFStrIsEightBit(collatorID)) { + string = ((const char *)contents) + __CFStrSkipAnyLengthByte(collatorID); + } else { + const UTF16Char *characters = (const UTF16Char *)contents; + + buffer[0] = (char)*(characters++); + buffer[1] = (char)*characters; + string = buffer; + } + + if (!strncmp(string, "az", 2)) { // Azerbaijani + langID = "az"; + } else if (!strncmp(string, "lt", 2)) { // Lithuanian + langID = "lt"; + } else if (!strncmp(string, "tr", 2)) { // Turkish + langID = "tr"; + } + } + + __CFSpinLock(&lock); + lastLocale = locale; + lastLangID = langID; + __CFSpinUnlock(&lock); + + return langID; +} + +static int8_t __CFCheckLocaleCFType = -1; + +CF_INLINE bool _CFCanUseLocale(CFLocaleRef locale) { + if (locale) { + if (__CFCheckLocaleCFType < 0) __CFCheckLocaleCFType = !_CFExecutableLinkedOnOrAfter(CFSystemVersionPanther); + if (!__CFCheckLocaleCFType || (CFGetTypeID(locale) == CFLocaleGetTypeID())) return true; + } + return false; +} + +#define MAX_CASE_MAPPING_BUF (8) +#define ZERO_WIDTH_JOINER (0x200D) +#define COMBINING_GRAPHEME_JOINER (0x034F) +// Hangul ranges +#define HANGUL_CHOSEONG_START (0x1100) +#define HANGUL_CHOSEONG_END (0x115F) +#define HANGUL_JUNGSEONG_START (0x1160) +#define HANGUL_JUNGSEONG_END (0x11A2) +#define HANGUL_JONGSEONG_START (0x11A8) +#define HANGUL_JONGSEONG_END (0x11F9) + +#define HANGUL_SYLLABLE_START (0xAC00) +#define HANGUL_SYLLABLE_END (0xD7AF) + + +// Returns the length of characters filled into outCharacters. If no change, returns 0. maxBufLen shoule be at least 8 +static CFIndex __CFStringFoldCharacterClusterAtIndex(UTF32Char character, CFStringInlineBuffer *buffer, CFIndex index, CFOptionFlags flags, const uint8_t *langCode, UTF32Char *outCharacters, CFIndex maxBufferLength, CFIndex *consumedLength) { + CFIndex filledLength = 0, currentIndex = index; + + if (0 != character) { + UTF16Char lowSurrogate; + CFIndex planeNo = (character >> 16); + bool isTurkikCapitalI = false; + static const uint8_t *decompBMP = NULL; + static const uint8_t *graphemeBMP = NULL; + + if (NULL == decompBMP) { + decompBMP = CFUniCharGetBitmapPtrForPlane(kCFUniCharCanonicalDecomposableCharacterSet, 0); + graphemeBMP = CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet, 0); + } + + ++currentIndex; + + if ((character < 0x0080) && ((NULL == langCode) || (character != 'I'))) { // ASCII + if ((flags & kCFCompareCaseInsensitive) && (character >= 'A') && (character <= 'Z')) { + character += ('a' - 'A'); + *outCharacters = character; + filledLength = 1; + } + } else { + // do width-insensitive mapping + if ((flags & kCFCompareWidthInsensitive) && (character >= 0xFF00) && (character <= 0xFFEF)) { + (void)CFUniCharCompatibilityDecompose(&character, 1, 1); + *outCharacters = character; + filledLength = 1; + } + + // map surrogates + if ((0 == planeNo) && CFUniCharIsSurrogateHighCharacter(character) && CFUniCharIsSurrogateLowCharacter((lowSurrogate = CFStringGetCharacterFromInlineBuffer(buffer, currentIndex)))) { + character = CFUniCharGetLongCharacterForSurrogatePair(character, lowSurrogate); + ++currentIndex; + planeNo = (character >> 16); + } + + // decompose + if (flags & (kCFCompareDiacriticsInsensitiveCompatibilityMask|kCFCompareNonliteral)) { + if (CFUniCharIsMemberOfBitmap(character, ((0 == planeNo) ? decompBMP : CFUniCharGetBitmapPtrForPlane(kCFUniCharCanonicalDecomposableCharacterSet, planeNo)))) { + UTF32Char original = character; + + filledLength = CFUniCharDecomposeCharacter(character, outCharacters, maxBufferLength); + character = *outCharacters; + + if ((flags & kCFCompareDiacriticsInsensitiveCompatibilityMask) && (character < 0x0510)) { + filledLength = 1; // reset if Roman, Greek, Cyrillic + } else if (0 == (flags & kCFCompareNonliteral)) { + character = original; + filledLength = 0; + } + } + } + + // fold case + if (flags & kCFCompareCaseInsensitive) { + const uint8_t *nonBaseBitmap; + bool filterNonBase = (((flags & kCFCompareDiacriticsInsensitiveCompatibilityMask) && (character < 0x0510)) ? true : false); + static const uint8_t *lowerBMP = NULL; + static const uint8_t *caseFoldBMP = NULL; + + if (NULL == lowerBMP) { + lowerBMP = CFUniCharGetBitmapPtrForPlane(kCFUniCharHasNonSelfLowercaseCharacterSet, 0); + caseFoldBMP = CFUniCharGetBitmapPtrForPlane(kCFUniCharHasNonSelfCaseFoldingCharacterSet, 0); + } + + if ((NULL != langCode) && ('I' == character) && ((0 == strcmp((const char *)langCode, "tr")) || (0 == strcmp((const char *)langCode, "az")))) { // do Turkik special-casing + if (filledLength > 1) { + if (0x0307 == outCharacters[1]) { + if (--filledLength > 1) memmove((outCharacters + 1), (outCharacters + 2), sizeof(UTF32Char) * (filledLength - 1)); + character = *outCharacters = 'i'; + isTurkikCapitalI = true; + } + } else if (0x0307 == CFStringGetCharacterFromInlineBuffer(buffer, currentIndex)) { + character = *outCharacters = 'i'; + filledLength = 1; + ++currentIndex; + isTurkikCapitalI = true; + } + } + if (!isTurkikCapitalI && (CFUniCharIsMemberOfBitmap(character, ((0 == planeNo) ? lowerBMP : CFUniCharGetBitmapPtrForPlane(kCFUniCharHasNonSelfLowercaseCharacterSet, planeNo))) || CFUniCharIsMemberOfBitmap(character, ((0 == planeNo) ? caseFoldBMP : CFUniCharGetBitmapPtrForPlane(kCFUniCharHasNonSelfCaseFoldingCharacterSet, planeNo))))) { + UTF16Char caseFoldBuffer[MAX_CASE_MAPPING_BUF]; + const UTF16Char *bufferP = caseFoldBuffer, *bufferLimit; + UTF32Char *outCharactersP = outCharacters; + uint32_t bufferLength = CFUniCharMapCaseTo(character, caseFoldBuffer, MAX_CASE_MAPPING_BUF, kCFUniCharCaseFold, 0, langCode); + + bufferLimit = bufferP + bufferLength; + + if (filledLength > 0) --filledLength; // decrement filledLength (will add back later) + + // make space for casefold characters + if ((filledLength > 0) && (bufferLength > 1)) { + CFIndex totalScalerLength = 0; + + while (bufferP < bufferLimit) { + if (CFUniCharIsSurrogateHighCharacter(*(bufferP++)) && (bufferP < bufferLimit) && CFUniCharIsSurrogateLowCharacter(*bufferP)) ++bufferP; + ++totalScalerLength; + } + memmove(outCharacters + totalScalerLength, outCharacters + 1, filledLength * sizeof(UTF32Char)); + bufferP = caseFoldBuffer; + } + + // fill + while (bufferP < bufferLimit) { + character = *(bufferP++); + if (CFUniCharIsSurrogateHighCharacter(character) && (bufferP < bufferLimit) && CFUniCharIsSurrogateLowCharacter(*bufferP)) { + character = CFUniCharGetLongCharacterForSurrogatePair(character, *(bufferP++)); + nonBaseBitmap = CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet, (character >> 16)); + } else { + nonBaseBitmap = graphemeBMP; + } + + if (!filterNonBase || !CFUniCharIsMemberOfBitmap(character, nonBaseBitmap)) { + *(outCharactersP++) = character; + ++filledLength; + } + } + } + } + } + + // collect following combining marks + if (flags & (kCFCompareDiacriticsInsensitiveCompatibilityMask|kCFCompareNonliteral)) { + const uint8_t *nonBaseBitmap; + const uint8_t *decompBitmap; + bool doFill = (((flags & kCFCompareDiacriticsInsensitiveCompatibilityMask) && (character < 0x0510)) ? false : true); + + if (0 == filledLength) { + *outCharacters = character; // filledLength will be updated below on demand + + if (doFill) { // check if really needs to fill + UTF32Char nonBaseCharacter = CFStringGetCharacterFromInlineBuffer(buffer, currentIndex); + + if (CFUniCharIsSurrogateHighCharacter(nonBaseCharacter) && CFUniCharIsSurrogateLowCharacter((lowSurrogate = CFStringGetCharacterFromInlineBuffer(buffer, currentIndex + 1)))) { + nonBaseCharacter = CFUniCharGetLongCharacterForSurrogatePair(nonBaseCharacter, lowSurrogate); + nonBaseBitmap = CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet, (nonBaseCharacter >> 16)); + decompBitmap = CFUniCharGetBitmapPtrForPlane(kCFUniCharCanonicalDecomposableCharacterSet, (nonBaseCharacter >> 16)); + } else { + nonBaseBitmap = graphemeBMP; + decompBitmap = decompBMP; + } + + if (CFUniCharIsMemberOfBitmap(nonBaseCharacter, nonBaseBitmap)) { + filledLength = 1; // For the base character + + if ((0 == (flags & kCFCompareDiacriticsInsensitiveCompatibilityMask)) || (nonBaseCharacter > 0x050F)) { + if (CFUniCharIsMemberOfBitmap(nonBaseCharacter, decompBitmap)) { + filledLength += CFUniCharDecomposeCharacter(nonBaseCharacter, &(outCharacters[filledLength]), maxBufferLength - filledLength); + } else { + outCharacters[filledLength++] = nonBaseCharacter; + } + } + currentIndex += ((nonBaseBitmap == graphemeBMP) ? 1 : 2); + } else { + doFill = false; + } + } + } + + while (filledLength < maxBufferLength) { // do the rest + character = CFStringGetCharacterFromInlineBuffer(buffer, currentIndex); + + if (CFUniCharIsSurrogateHighCharacter(character) && CFUniCharIsSurrogateLowCharacter((lowSurrogate = CFStringGetCharacterFromInlineBuffer(buffer, currentIndex + 1)))) { + character = CFUniCharGetLongCharacterForSurrogatePair(character, lowSurrogate); + nonBaseBitmap = CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet, (character >> 16)); + decompBitmap = CFUniCharGetBitmapPtrForPlane(kCFUniCharCanonicalDecomposableCharacterSet, (character >> 16)); + } else { + nonBaseBitmap = graphemeBMP; + decompBitmap = decompBMP; + } + if (isTurkikCapitalI) { + isTurkikCapitalI = false; + } else if (CFUniCharIsMemberOfBitmap(character, nonBaseBitmap)) { + if (doFill) { + if (CFUniCharIsMemberOfBitmap(character, decompBitmap)) { + CFIndex currentLength = CFUniCharDecomposeCharacter(character, &(outCharacters[filledLength]), maxBufferLength - filledLength); + + if (0 == currentLength) break; // didn't fit + + filledLength += currentLength; + } else { + outCharacters[filledLength++] = character; + } + } else if (0 == filledLength) { + filledLength = 1; // For the base character + } + currentIndex += ((nonBaseBitmap == graphemeBMP) ? 1 : 2); + } else { + break; + } + } + + if (filledLength > 1) { + UTF32Char *sortCharactersLimit = outCharacters + filledLength; + UTF32Char *sortCharacters = sortCharactersLimit - 1; + + while ((outCharacters < sortCharacters) && CFUniCharIsMemberOfBitmap(*sortCharacters, ((*sortCharacters < 0x10000) ? graphemeBMP : CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet, (*sortCharacters >> 16))))) --sortCharacters; + + if ((sortCharactersLimit - sortCharacters) > 1) CFUniCharPrioritySort(sortCharacters, (sortCharactersLimit - sortCharacters)); // priority sort + } + } + } + + if ((filledLength > 0) && (NULL != consumedLength)) *consumedLength = (currentIndex - index); + + return filledLength; +} + +#define kCFStringStackBufferLength (64) + +CFComparisonResult CFStringCompareWithOptionsAndLocale(CFStringRef string, CFStringRef string2, CFRange rangeToCompare, CFOptionFlags compareOptions, CFLocaleRef locale) { + /* No objc dispatch needed here since CFStringInlineBuffer works with both CFString and NSString */ + UTF32Char strBuf1[kCFStringStackBufferLength]; + UTF32Char strBuf2[kCFStringStackBufferLength]; + CFStringInlineBuffer inlineBuf1, inlineBuf2; + UTF32Char str1Char, str2Char; + CFIndex str1UsedLen, str2UsedLen; + CFIndex str1Index = 0, str2Index = 0, strBuf1Index = 0, strBuf2Index = 0, strBuf1Len = 0, strBuf2Len = 0; + CFIndex str2Len = CFStringGetLength(string2); + bool caseInsensitive = ((compareOptions & kCFCompareCaseInsensitive) ? true : false); + bool diacriticsInsensitive = ((compareOptions & kCFCompareDiacriticsInsensitiveCompatibilityMask) ? true : false); + bool equalityOptions = ((compareOptions & (kCFCompareCaseInsensitive|kCFCompareNonliteral|kCFCompareDiacriticsInsensitiveCompatibilityMask|kCFCompareWidthInsensitive)) ? true : false); + bool numerically = ((compareOptions & kCFCompareNumerically) ? true : false); + const uint8_t *graphemeBMP = CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet, 0); + const uint8_t *langCode; + CFComparisonResult compareResult = kCFCompareEqualTo; + UTF16Char otherChar; + Boolean freeLocale = false; + + #define _CFCompareStringsWithLocale(A, B, C, D, E, F) (0) + locale = NULL; + + if ((compareOptions & kCFCompareLocalized) && (NULL == locale)) { + locale = CFLocaleCopyCurrent(); + freeLocale = true; + } + + langCode = ((NULL == locale) ? NULL : (const uint8_t *)_CFStrGetLanguageIdentifierForLocale(locale)); + + if ((NULL == locale) && !numerically) { // could do binary comp (be careful when adding new flags) + CFStringEncoding eightBitEncoding = __CFStringGetEightBitStringEncoding(); + const uint8_t *str1Bytes = (const uint8_t *)CFStringGetCStringPtr(string, eightBitEncoding); + const uint8_t *str2Bytes = (const uint8_t *)CFStringGetCStringPtr(string2, eightBitEncoding); + CFIndex factor = sizeof(uint8_t); + + if ((NULL != str1Bytes) && (NULL != str2Bytes)) { + compareOptions &= ~kCFCompareNonliteral; // remove non-literal + + if (kCFStringEncodingASCII == eightBitEncoding) { + if (caseInsensitive) { +#if DEPLOYMENT_TARGET_MACOSX + int cmpResult = strncasecmp_l((const char *)str1Bytes + rangeToCompare.location, (const char *)str2Bytes, __CFMin(rangeToCompare.length, str2Len), NULL); +#elif DEPLOYMENT_TARGET_WINDOWS && !defined(__GNUC__) + int cmpResult = _strnicmp_l((const char*)str1Bytes + rangeToCompare.location, (const char*)str2Bytes, __CFMin(rangeToCompare.length, str2Len), NULL); +#else + int cmpResult = strncasecmp((const char *)str1Bytes + rangeToCompare.location, (const char *)str2Bytes, __CFMin(rangeToCompare.length, str2Len)); +#endif + + if (0 == cmpResult) cmpResult = rangeToCompare.length - str2Len; + + return ((0 == cmpResult) ? kCFCompareEqualTo : ((cmpResult < 0) ? kCFCompareLessThan : kCFCompareGreaterThan)); + } + } else if (caseInsensitive || diacriticsInsensitive) { + CFIndex limitLength = __CFMin(rangeToCompare.length, str2Len); + + str1Bytes += rangeToCompare.location; + + while (str1Index < limitLength) { + str1Char = str1Bytes[str1Index]; + str2Char = str2Bytes[str1Index]; + + if (str1Char != str2Char) { + if ((str1Char < 0x80) && (str2Char < 0x80)) { + if ((str1Char >= 'A') && (str1Char <= 'Z')) str1Char += ('a' - 'A'); + if ((str2Char >= 'A') && (str2Char <= 'Z')) str2Char += ('a' - 'A'); + + if (str1Char != str2Char) return ((str1Char < str2Char) ? kCFCompareLessThan : kCFCompareGreaterThan); + } else { + str1Bytes = NULL; + break; + } + } + ++str1Index; + } + + str2Index = str1Index; + + if (str1Index == limitLength) { + int cmpResult = rangeToCompare.length - str2Len; + + return ((0 == cmpResult) ? kCFCompareEqualTo : ((cmpResult < 0) ? kCFCompareLessThan : kCFCompareGreaterThan)); + } + } + } else if (!equalityOptions && (NULL == str1Bytes) && (NULL == str2Bytes)) { + str1Bytes = (const uint8_t *)CFStringGetCharactersPtr(string); + str2Bytes = (const uint8_t *)CFStringGetCharactersPtr(string2); + factor = sizeof(UTF16Char); +#if __LITTLE_ENDIAN__ + if ((NULL != str1Bytes) && (NULL != str2Bytes)) { // we cannot use memcmp + const UTF16Char *str1 = ((const UTF16Char *)str1Bytes) + rangeToCompare.location; + const UTF16Char *str1Limit = str1 + __CFMin(rangeToCompare.length, str2Len); + const UTF16Char *str2 = (const UTF16Char *)str2Bytes; + CFIndex cmpResult = 0; + + while ((0 == cmpResult) && (str1 < str1Limit)) cmpResult = (CFIndex)*(str1++) - (CFIndex)*(str2++); + + if (0 == cmpResult) cmpResult = rangeToCompare.length - str2Len; + + return ((0 == cmpResult) ? kCFCompareEqualTo : ((cmpResult < 0) ? kCFCompareLessThan : kCFCompareGreaterThan)); + } +#endif /* __LITTLE_ENDIAN__ */ + } + if ((NULL != str1Bytes) && (NULL != str2Bytes)) { + int cmpResult = memcmp(str1Bytes + (rangeToCompare.location * factor), str2Bytes, __CFMin(rangeToCompare.length, str2Len) * factor); + + if (0 == cmpResult) cmpResult = rangeToCompare.length - str2Len; + + return ((0 == cmpResult) ? kCFCompareEqualTo : ((cmpResult < 0) ? kCFCompareLessThan : kCFCompareGreaterThan)); + } + } + + CFStringInitInlineBuffer(string, &inlineBuf1, rangeToCompare); + CFStringInitInlineBuffer(string2, &inlineBuf2, CFRangeMake(0, str2Len)); + + while ((str1Index < rangeToCompare.length) && (str2Index < str2Len)) { + if (strBuf1Len == 0) { + str1Char = CFStringGetCharacterFromInlineBuffer(&inlineBuf1, str1Index); + if (caseInsensitive && (str1Char >= 'A') && (str1Char <= 'Z') && ((NULL == langCode) || (str1Char != 'I'))) str1Char += ('a' - 'A'); + str1UsedLen = 1; + } else { + str1Char = strBuf1[strBuf1Index++]; + } + if (strBuf2Len == 0) { + str2Char = CFStringGetCharacterFromInlineBuffer(&inlineBuf2, str2Index); + if (caseInsensitive && (str2Char >= 'A') && (str2Char <= 'Z') && ((NULL == langCode) || (str2Char != 'I'))) str2Char += ('a' - 'A'); + str2UsedLen = 1; + } else { + str2Char = strBuf2[strBuf2Index++]; + } + + if (numerically && ((0 == strBuf1Len) && (str1Char <= '9') && (str1Char >= '0')) && ((0 == strBuf2Len) && (str2Char <= '9') && (str2Char >= '0'))) { // If both are not ASCII digits, then don't do numerical comparison here + uint64_t intValue1 = 0, intValue2 = 0; // !!! Doesn't work if numbers are > max uint64_t + + do { + intValue1 = (intValue1 * 10) + (str1Char - '0'); + str1Char = CFStringGetCharacterFromInlineBuffer(&inlineBuf1, ++str1Index); + } while ((str1Char <= '9') && (str1Char >= '0')); + + do { + intValue2 = intValue2 * 10 + (str2Char - '0'); + str2Char = CFStringGetCharacterFromInlineBuffer(&inlineBuf2, ++str2Index); + } while ((str2Char <= '9') && (str2Char >= '0')); + + if (intValue1 == intValue2) { + continue; + } else if (intValue1 < intValue2) { + if (freeLocale && locale) { + CFRelease(locale); + } + return kCFCompareLessThan; + } else { + if (freeLocale && locale) { + CFRelease(locale); + } + return kCFCompareGreaterThan; + } + } + + if (str1Char != str2Char) { + if (!equalityOptions) { + CFComparisonResult res = ((NULL == locale) ? ((str1Char < str2Char) ? kCFCompareLessThan : kCFCompareGreaterThan) : _CFCompareStringsWithLocale(&inlineBuf1, CFRangeMake(strBuf1Index, rangeToCompare.length - strBuf1Index), &inlineBuf2, CFRangeMake(strBuf2Index, str2Len - strBuf2Index), compareOptions, locale)); + if (freeLocale && locale) { + CFRelease(locale); + } + return res; + } + + if ((compareOptions & kCFCompareForcedOrdering) && (kCFCompareEqualTo == compareResult)) compareResult = ((str1Char < str2Char) ? kCFCompareLessThan : kCFCompareGreaterThan); + + if ((str1Char < 0x80) && (str2Char < 0x80)) { + if (NULL != locale) { + CFComparisonResult res = _CFCompareStringsWithLocale(&inlineBuf1, CFRangeMake(strBuf1Index, rangeToCompare.length - strBuf1Index), &inlineBuf2, CFRangeMake(strBuf2Index, str2Len - strBuf2Index), compareOptions, locale); + if (freeLocale && locale) { + CFRelease(locale); + } + return res; + } else if (!caseInsensitive) { + if (freeLocale && locale) { + CFRelease(locale); + } + return ((str1Char < str2Char) ? kCFCompareLessThan : kCFCompareGreaterThan); + } + } + + if (CFUniCharIsSurrogateHighCharacter(str1Char) && CFUniCharIsSurrogateLowCharacter((otherChar = CFStringGetCharacterFromInlineBuffer(&inlineBuf1, str1Index + 1)))) { + str1Char = CFUniCharGetLongCharacterForSurrogatePair(str1Char, otherChar); + str1UsedLen = 2; + } + + if (CFUniCharIsSurrogateHighCharacter(str2Char) && CFUniCharIsSurrogateLowCharacter((otherChar = CFStringGetCharacterFromInlineBuffer(&inlineBuf2, str2Index + 1)))) { + str2Char = CFUniCharGetLongCharacterForSurrogatePair(str2Char, otherChar); + str2UsedLen = 2; + } + + if (diacriticsInsensitive && (str1Index > 0)) { + bool str1Skip = false; + bool str2Skip = false; + + if ((0 == strBuf1Len) && CFUniCharIsMemberOfBitmap(str1Char, ((str1Char < 0x10000) ? graphemeBMP : CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet, (str1Char >> 16))))) { + str1Char = str2Char; + str1Skip = true; + } + if ((0 == strBuf2Len) && CFUniCharIsMemberOfBitmap(str2Char, ((str2Char < 0x10000) ? graphemeBMP : CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet, (str2Char >> 16))))) { + str2Char = str1Char; + str2Skip = true; + } + + if (str1Skip != str2Skip) { + if (str1Skip) str2Index -= str2UsedLen; + if (str2Skip) str1Index -= str1UsedLen; + } + } + + if (str1Char != str2Char) { + if (0 == strBuf1Len) { + strBuf1Len = __CFStringFoldCharacterClusterAtIndex(str1Char, &inlineBuf1, str1Index, compareOptions, langCode, strBuf1, kCFStringStackBufferLength, &str1UsedLen); + if (strBuf1Len > 0) { + str1Char = *strBuf1; + strBuf1Index = 1; + } + } + + if ((0 == strBuf1Len) && (0 < strBuf2Len)) { + CFComparisonResult res = ((NULL == locale) ? ((str1Char < str2Char) ? kCFCompareLessThan : kCFCompareGreaterThan) : _CFCompareStringsWithLocale(&inlineBuf1, CFRangeMake(strBuf1Index, rangeToCompare.length - strBuf1Index), &inlineBuf2, CFRangeMake(strBuf2Index, str2Len - strBuf2Index), compareOptions, locale)); + if (freeLocale && locale) { + CFRelease(locale); + } + return res; + } + + if ((0 == strBuf2Len) && ((0 == strBuf1Len) || (str1Char != str2Char))) { + strBuf2Len = __CFStringFoldCharacterClusterAtIndex(str2Char, &inlineBuf2, str2Index, compareOptions, langCode, strBuf2, kCFStringStackBufferLength, &str2UsedLen); + if (strBuf2Len > 0) { + str2Char = *strBuf2; + strBuf2Index = 1; + } + if ((0 == strBuf2Len) || (str1Char != str2Char)) { + CFComparisonResult res = ((NULL == locale) ? ((str1Char < str2Char) ? kCFCompareLessThan : kCFCompareGreaterThan) : _CFCompareStringsWithLocale(&inlineBuf1, CFRangeMake(strBuf1Index, rangeToCompare.length - strBuf1Index), &inlineBuf2, CFRangeMake(strBuf2Index, str2Len - strBuf2Index), compareOptions, locale)); + if (freeLocale && locale) { + CFRelease(locale); + } + return res; + } + } + } + + if ((strBuf1Len > 0) && (strBuf2Len > 0)) { + while ((strBuf1Index < strBuf1Len) && (strBuf2Index < strBuf2Len)) { + if (strBuf1[strBuf1Index] != strBuf2[strBuf2Index]) break; + ++strBuf1Index; ++strBuf2Index; + } + if ((strBuf1Index < strBuf1Len) && (strBuf2Index < strBuf2Len)) { + CFComparisonResult res = ((NULL == locale) ? ((str1Char < str2Char) ? kCFCompareLessThan : kCFCompareGreaterThan) : _CFCompareStringsWithLocale(&inlineBuf1, CFRangeMake(strBuf1Index, rangeToCompare.length - strBuf1Index), &inlineBuf2, CFRangeMake(strBuf2Index, str2Len - strBuf2Index), compareOptions, locale)); + if (freeLocale && locale) { + CFRelease(locale); + } + return res; + } + } + } + + if ((strBuf1Len > 0) && (strBuf1Index == strBuf1Len)) strBuf1Len = 0; + if ((strBuf2Len > 0) && (strBuf2Index == strBuf2Len)) strBuf2Len = 0; + + if (strBuf1Len == 0) str1Index += str1UsedLen; + if (strBuf2Len == 0) str2Index += str2UsedLen; + } + + if (diacriticsInsensitive) { + while (str1Index < rangeToCompare.length) { + str1Char = CFStringGetCharacterFromInlineBuffer(&inlineBuf1, str1Index); + if (str1Char < 0x80) break; // found ASCII + + if (CFUniCharIsSurrogateHighCharacter(str1Char) && CFUniCharIsSurrogateLowCharacter((otherChar = CFStringGetCharacterFromInlineBuffer(&inlineBuf1, str1Index + 1)))) str1Char = CFUniCharGetLongCharacterForSurrogatePair(str1Char, otherChar); + + if (!CFUniCharIsMemberOfBitmap(str1Char, ((str1Char < 0x10000) ? graphemeBMP : CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet, (str1Char >> 16))))) break; + + str1Index += ((str1Char < 0x10000) ? 1 : 2); + } + + while (str2Index < str2Len) { + str2Char = CFStringGetCharacterFromInlineBuffer(&inlineBuf2, str2Index); + if (str2Char < 0x80) break; // found ASCII + + if (CFUniCharIsSurrogateHighCharacter(str2Char) && CFUniCharIsSurrogateLowCharacter((otherChar = CFStringGetCharacterFromInlineBuffer(&inlineBuf2, str2Index + 1)))) str2Char = CFUniCharGetLongCharacterForSurrogatePair(str2Char, otherChar); + + if (!CFUniCharIsMemberOfBitmap(str2Char, ((str2Char < 0x10000) ? graphemeBMP : CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet, (str2Char >> 16))))) break; + + str2Index += ((str2Char < 0x10000) ? 1 : 2); + } + } + + if (freeLocale && locale) { + CFRelease(locale); + } + + return ((str1Index < rangeToCompare.length) ? kCFCompareGreaterThan : ((str2Index < str2Len) ? kCFCompareLessThan : compareResult)); +} + + +CFComparisonResult CFStringCompareWithOptions(CFStringRef string, CFStringRef string2, CFRange rangeToCompare, CFOptionFlags compareOptions) { return CFStringCompareWithOptionsAndLocale(string, string2, rangeToCompare, compareOptions, NULL); } + +CFComparisonResult CFStringCompare(CFStringRef string, CFStringRef str2, CFOptionFlags options) { + return CFStringCompareWithOptions(string, str2, CFRangeMake(0, CFStringGetLength(string)), options); +} + +Boolean CFStringFindWithOptionsAndLocale(CFStringRef string, CFStringRef stringToFind, CFRange rangeToSearch, CFOptionFlags compareOptions, CFLocaleRef locale, CFRange *result) { + /* No objc dispatch needed here since CFStringInlineBuffer works with both CFString and NSString */ + CFIndex findStrLen = CFStringGetLength(stringToFind); + Boolean didFind = false; + bool lengthVariants = ((compareOptions & (kCFCompareCaseInsensitive|kCFCompareNonliteral|kCFCompareDiacriticsInsensitiveCompatibilityMask)) ? true : false); + + if ((findStrLen > 0) && (rangeToSearch.length > 0) && ((findStrLen <= rangeToSearch.length) || lengthVariants)) { + UTF32Char strBuf1[kCFStringStackBufferLength]; + UTF32Char strBuf2[kCFStringStackBufferLength]; + CFStringInlineBuffer inlineBuf1, inlineBuf2; + UTF32Char str1Char, str2Char; + CFStringEncoding eightBitEncoding = __CFStringGetEightBitStringEncoding(); + const uint8_t *str1Bytes = (const uint8_t *)CFStringGetCStringPtr(string, eightBitEncoding); + const uint8_t *str2Bytes = (const uint8_t *)CFStringGetCStringPtr(stringToFind, eightBitEncoding); + const UTF32Char *characters, *charactersLimit; + const uint8_t *langCode = NULL; + CFIndex fromLoc, toLoc; + CFIndex str1Index, str2Index; + CFIndex strBuf1Len, strBuf2Len; + bool equalityOptions = ((lengthVariants || (compareOptions & kCFCompareWidthInsensitive)) ? true : false); + bool caseInsensitive = ((compareOptions & kCFCompareCaseInsensitive) ? true : false); + int8_t delta; + + if (NULL == locale) { + if (compareOptions & kCFCompareLocalized) { + CFLocaleRef currentLocale = CFLocaleCopyCurrent(); + langCode = (const uint8_t *)_CFStrGetLanguageIdentifierForLocale(currentLocale); + CFRelease(currentLocale); + } + } else { + langCode = (const uint8_t *)_CFStrGetLanguageIdentifierForLocale(locale); + } + + CFStringInitInlineBuffer(string, &inlineBuf1, CFRangeMake(0, rangeToSearch.location + rangeToSearch.length)); + CFStringInitInlineBuffer(stringToFind, &inlineBuf2, CFRangeMake(0, findStrLen)); + + if (compareOptions & kCFCompareBackwards) { + fromLoc = rangeToSearch.location + rangeToSearch.length - (lengthVariants ? 1 : findStrLen); + toLoc = (((compareOptions & kCFCompareAnchored) && !lengthVariants) ? fromLoc : rangeToSearch.location); + } else { + fromLoc = rangeToSearch.location; + toLoc = ((compareOptions & kCFCompareAnchored) ? fromLoc : rangeToSearch.location + rangeToSearch.length - (lengthVariants ? 1 : findStrLen)); + } + + delta = ((fromLoc <= toLoc) ? 1 : -1); + + if ((NULL != str1Bytes) && (NULL != str2Bytes)) { + CFIndex maxStr1Index = (rangeToSearch.location + rangeToSearch.length); + uint8_t str1Byte, str2Byte; + + while (1) { + str1Index = fromLoc; + str2Index = 0; + + while ((str1Index < maxStr1Index) && (str2Index < findStrLen)) { + str1Byte = str1Bytes[str1Index]; + str2Byte = str2Bytes[str2Index]; + + if (str1Byte != str2Byte) { + if (equalityOptions) { + if ((str1Byte < 0x80) && ((NULL == langCode) || ('I' != str1Byte))) { + if (caseInsensitive && (str1Byte >= 'A') && (str1Byte <= 'Z')) str1Byte += ('a' - 'A'); + *strBuf1 = str1Byte; + strBuf1Len = 1; + } else { + str1Char = CFStringGetCharacterFromInlineBuffer(&inlineBuf1, str1Index); + strBuf1Len = __CFStringFoldCharacterClusterAtIndex(str1Char, &inlineBuf1, str1Index, compareOptions, langCode, strBuf1, kCFStringStackBufferLength, NULL); + if (1 > strBuf1Len) { + *strBuf1 = str1Char; + strBuf1Len = 1; + } + } + if ((str2Byte < 0x80) && ((NULL == langCode) || ('I' != str2Byte))) { + if (caseInsensitive && (str2Byte >= 'A') && (str2Byte <= 'Z')) str2Byte += ('a' - 'A'); + *strBuf2 = str2Byte; + strBuf2Len = 1; + } else { + str2Char = CFStringGetCharacterFromInlineBuffer(&inlineBuf2, str2Index); + strBuf2Len = __CFStringFoldCharacterClusterAtIndex(str2Char, &inlineBuf2, str2Index, compareOptions, langCode, strBuf2, kCFStringStackBufferLength, NULL); + if (1 > strBuf2Len) { + *strBuf2 = str2Char; + strBuf2Len = 1; + } + } + + if ((1 == strBuf1Len) && (1 == strBuf2Len)) { // normal case + if (*strBuf1 != *strBuf2) break; + } else { + CFIndex delta; + + if (!caseInsensitive && (strBuf1Len != strBuf2Len)) break; + if (memcmp(strBuf1, strBuf2, sizeof(UTF32Char) * __CFMin(strBuf1Len, strBuf2Len))) break; + + if (strBuf1Len < strBuf2Len) { + delta = strBuf2Len - strBuf1Len; + + if ((str1Index + strBuf1Len + delta) > (rangeToSearch.location + rangeToSearch.length)) break; + + characters = &(strBuf2[strBuf1Len]); + charactersLimit = characters + delta; + + while (characters < charactersLimit) { + strBuf1Len = __CFStringFoldCharacterClusterAtIndex(CFStringGetCharacterFromInlineBuffer(&inlineBuf1, str1Index + 1), &inlineBuf1, str1Index + 1, compareOptions, langCode, strBuf1, kCFStringStackBufferLength, NULL); + if ((strBuf1Len > 0) || (*characters != *strBuf1)) break; + ++characters; ++str1Index; + } + if (characters < charactersLimit) break; + } else if (strBuf2Len < strBuf1Len) { + delta = strBuf1Len - strBuf2Len; + + if ((str2Index + strBuf2Len + delta) > findStrLen) break; + + characters = &(strBuf1[strBuf2Len]); + charactersLimit = characters + delta; + + while (characters < charactersLimit) { + strBuf2Len = __CFStringFoldCharacterClusterAtIndex(CFStringGetCharacterFromInlineBuffer(&inlineBuf2, str1Index + 1), &inlineBuf2, str2Index + 1, compareOptions, langCode, strBuf2, kCFStringStackBufferLength, NULL); + if ((strBuf2Len > 0) || (*characters != *strBuf2)) break; + ++characters; ++str2Index; + } + if (characters < charactersLimit) break; + } + } + } else { + break; + } + } + ++str1Index; ++str2Index; + } + + if (str2Index == findStrLen) { + if (((kCFCompareBackwards|kCFCompareAnchored) != (compareOptions & (kCFCompareBackwards|kCFCompareAnchored))) || (str1Index == (rangeToSearch.location + rangeToSearch.length))) { + didFind = true; + if (NULL != result) *result = CFRangeMake(fromLoc, str1Index - fromLoc); + } + break; + } + + if (fromLoc == toLoc) break; + fromLoc += delta; + } + } else if (equalityOptions) { + UTF16Char otherChar; + CFIndex str1UsedLen, str2UsedLen, strBuf1Index = 0, strBuf2Index = 0; + bool diacriticsInsensitive = ((compareOptions & kCFCompareDiacriticsInsensitiveCompatibilityMask) ? true : false); + const uint8_t *graphemeBMP = CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet, 0); + const uint8_t *combClassBMP = (const uint8_t *)CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty, 0); + + while (1) { + str1Index = fromLoc; + str2Index = 0; + + strBuf1Len = strBuf2Len = 0; + + while (str2Index < findStrLen) { + if (strBuf1Len == 0) { + str1Char = CFStringGetCharacterFromInlineBuffer(&inlineBuf1, str1Index); + if (caseInsensitive && (str1Char >= 'A') && (str1Char <= 'Z') && ((NULL == langCode) || (str1Char != 'I'))) str1Char += ('a' - 'A'); + str1UsedLen = 1; + } else { + str1Char = strBuf1[strBuf1Index++]; + } + if (strBuf2Len == 0) { + str2Char = CFStringGetCharacterFromInlineBuffer(&inlineBuf2, str2Index); + if (caseInsensitive && (str2Char >= 'A') && (str2Char <= 'Z') && ((NULL == langCode) || (str2Char != 'I'))) str2Char += ('a' - 'A'); + str2UsedLen = 1; + } else { + str2Char = strBuf2[strBuf2Index++]; + } + + if (str1Char != str2Char) { + if ((str1Char < 0x80) && (str2Char < 0x80) && ((NULL == langCode) || !caseInsensitive)) break; + + if (CFUniCharIsSurrogateHighCharacter(str1Char) && CFUniCharIsSurrogateLowCharacter((otherChar = CFStringGetCharacterFromInlineBuffer(&inlineBuf1, str1Index + 1)))) { + str1Char = CFUniCharGetLongCharacterForSurrogatePair(str1Char, otherChar); + str1UsedLen = 2; + } + + if (CFUniCharIsSurrogateHighCharacter(str2Char) && CFUniCharIsSurrogateLowCharacter((otherChar = CFStringGetCharacterFromInlineBuffer(&inlineBuf2, str2Index + 1)))) { + str2Char = CFUniCharGetLongCharacterForSurrogatePair(str2Char, otherChar); + str2UsedLen = 2; + } + + if (diacriticsInsensitive && (str1Index > fromLoc)) { + bool str1Skip = false; + bool str2Skip = false; + + if ((0 == strBuf1Len) && CFUniCharIsMemberOfBitmap(str1Char, ((str1Char < 0x10000) ? graphemeBMP : CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet, (str1Char >> 16))))) { + str1Char = str2Char; + str1Skip = true; + } + if ((0 == strBuf2Len) && CFUniCharIsMemberOfBitmap(str2Char, ((str2Char < 0x10000) ? graphemeBMP : CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet, (str2Char >> 16))))) { + str2Char = str1Char; + str2Skip = true; + } + + if (str1Skip != str2Skip) { + if (str1Skip) str2Index -= str2UsedLen; + if (str2Skip) str1Index -= str1UsedLen; + } + } + + if (str1Char != str2Char) { + if (0 == strBuf1Len) { + strBuf1Len = __CFStringFoldCharacterClusterAtIndex(str1Char, &inlineBuf1, str1Index, compareOptions, langCode, strBuf1, kCFStringStackBufferLength, &str1UsedLen); + if (strBuf1Len > 0) { + str1Char = *strBuf1; + strBuf1Index = 1; + } + } + + if ((0 == strBuf1Len) && (0 < strBuf2Len)) break; + + if ((0 == strBuf2Len) && ((0 == strBuf1Len) || (str1Char != str2Char))) { + strBuf2Len = __CFStringFoldCharacterClusterAtIndex(str2Char, &inlineBuf2, str2Index, compareOptions, langCode, strBuf2, kCFStringStackBufferLength, &str2UsedLen); + if ((0 == strBuf2Len) || (str1Char != *strBuf2)) break; + strBuf2Index = 1; + } + } + + if ((strBuf1Len > 0) && (strBuf2Len > 0)) { + while ((strBuf1Index < strBuf1Len) && (strBuf2Index < strBuf2Len)) { + if (strBuf1[strBuf1Index] != strBuf2[strBuf2Index]) break; + ++strBuf1Index; ++strBuf2Index; + } + if ((strBuf1Index < strBuf1Len) && (strBuf2Index < strBuf2Len)) break; + } + } + + if ((strBuf1Len > 0) && (strBuf1Index == strBuf1Len)) strBuf1Len = 0; + if ((strBuf2Len > 0) && (strBuf2Index == strBuf2Len)) strBuf2Len = 0; + + if (strBuf1Len == 0) str1Index += str1UsedLen; + if (strBuf2Len == 0) str2Index += str2UsedLen; + } + + if (str2Index == findStrLen) { + bool match = true; + + if (strBuf1Len > 0) { + match = false; + + if ((compareOptions & kCFCompareDiacriticsInsensitiveCompatibilityMask) && (strBuf1[0] < 0x0510)) { + while (strBuf1Index < strBuf1Len) { + if (!CFUniCharIsMemberOfBitmap(strBuf1[strBuf1Index], ((strBuf1[strBuf1Index] < 0x10000) ? graphemeBMP : CFUniCharGetBitmapPtrForPlane(kCFUniCharCanonicalDecomposableCharacterSet, (strBuf1[strBuf1Index] >> 16))))) break; + ++strBuf1Index; + } + + if (strBuf1Index == strBuf1Len) { + str1Index += str1UsedLen; + match = true; + } + } + } + + if (match && (compareOptions & (kCFCompareDiacriticsInsensitiveCompatibilityMask|kCFCompareNonliteral)) && (str1Index < (rangeToSearch.location + rangeToSearch.length))) { + const uint8_t *nonBaseBitmap; + + str1Char = CFStringGetCharacterFromInlineBuffer(&inlineBuf1, str1Index); + + if (CFUniCharIsSurrogateHighCharacter(str1Char) && CFUniCharIsSurrogateLowCharacter((otherChar = CFStringGetCharacterFromInlineBuffer(&inlineBuf1, str1Index + 1)))) { + str1Char = CFUniCharGetLongCharacterForSurrogatePair(str1Char, otherChar); + nonBaseBitmap = CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet, (str1Char >> 16)); + } else { + nonBaseBitmap = graphemeBMP; + } + + if (CFUniCharIsMemberOfBitmap(str1Char, nonBaseBitmap)) { + if (diacriticsInsensitive) { + if (str1Char < 0x10000) { + CFIndex index = str1Index; + + do { + str1Char = CFStringGetCharacterFromInlineBuffer(&inlineBuf1, --index); + } while (CFUniCharIsMemberOfBitmap(str1Char, graphemeBMP), (rangeToSearch.location < index)); + + if (str1Char < 0x0510) { + CFIndex maxIndex = (rangeToSearch.location + rangeToSearch.length); + + while (++str1Index < maxIndex) if (!CFUniCharIsMemberOfBitmap(CFStringGetCharacterFromInlineBuffer(&inlineBuf1, str1Index), graphemeBMP)) break; + } + } + } else { + match = false; + } + } else if (!diacriticsInsensitive) { + otherChar = CFStringGetCharacterFromInlineBuffer(&inlineBuf1, str1Index - 1); + + // this is assuming viramas are only in BMP ??? + if ((str1Char == COMBINING_GRAPHEME_JOINER) || (otherChar == COMBINING_GRAPHEME_JOINER) || (otherChar == ZERO_WIDTH_JOINER) || ((otherChar >= HANGUL_CHOSEONG_START) && (otherChar <= HANGUL_JONGSEONG_END)) || (CFUniCharGetCombiningPropertyForCharacter(otherChar, combClassBMP) == 9)) { + CFRange clusterRange = CFStringGetRangeOfCharacterClusterAtIndex(string, str1Index - 1, kCFStringGraphemeCluster); + + if (str1Index < (clusterRange.location + clusterRange.length)) match = false; + } + } + } + + if (match) { + if (((kCFCompareBackwards|kCFCompareAnchored) != (compareOptions & (kCFCompareBackwards|kCFCompareAnchored))) || (str1Index == (rangeToSearch.location + rangeToSearch.length))) { + didFind = true; + if (NULL != result) *result = CFRangeMake(fromLoc, str1Index - fromLoc); + } + break; + } + } + + if (fromLoc == toLoc) break; + fromLoc += delta; + } + } else { + while (1) { + str1Index = fromLoc; + str2Index = 0; + + while (str2Index < findStrLen) { + if (CFStringGetCharacterFromInlineBuffer(&inlineBuf1, str1Index) != CFStringGetCharacterFromInlineBuffer(&inlineBuf2, str2Index)) break; + + ++str1Index; ++str2Index; + } + + if (str2Index == findStrLen) { + didFind = true; + if (NULL != result) *result = CFRangeMake(fromLoc, findStrLen); + break; + } + + if (fromLoc == toLoc) break; + fromLoc += delta; + } + } + } + + return didFind; +} + +Boolean CFStringFindWithOptions(CFStringRef string, CFStringRef stringToFind, CFRange rangeToSearch, CFOptionFlags compareOptions, CFRange *result) { return CFStringFindWithOptionsAndLocale(string, stringToFind, rangeToSearch, compareOptions, NULL, result); } + +// Functions to deal with special arrays of CFRange, CFDataRef, created by CFStringCreateArrayWithFindResults() + +static const void *__rangeRetain(CFAllocatorRef allocator, const void *ptr) { + CFRetain(*(CFDataRef *)((uint8_t *)ptr + sizeof(CFRange))); + return ptr; +} + +static void __rangeRelease(CFAllocatorRef allocator, const void *ptr) { + CFRelease(*(CFDataRef *)((uint8_t *)ptr + sizeof(CFRange))); +} + +static CFStringRef __rangeCopyDescription(const void *ptr) { + CFRange range = *(CFRange *)ptr; + return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("{%d, %d}"), range.location, range.length); +} + +static Boolean __rangeEqual(const void *ptr1, const void *ptr2) { + CFRange range1 = *(CFRange *)ptr1; + CFRange range2 = *(CFRange *)ptr2; + return (range1.location == range2.location) && (range1.length == range2.length); +} + + +CFArrayRef CFStringCreateArrayWithFindResults(CFAllocatorRef alloc, CFStringRef string, CFStringRef stringToFind, CFRange rangeToSearch, CFOptionFlags compareOptions) { + CFRange foundRange; + Boolean backwards = ((compareOptions & kCFCompareBackwards) != 0); + UInt32 endIndex = rangeToSearch.location + rangeToSearch.length; + CFMutableDataRef rangeStorage = NULL; // Basically an array of CFRange, CFDataRef (packed) + uint8_t *rangeStorageBytes = NULL; + CFIndex foundCount = 0; + CFIndex capacity = 0; // Number of CFRange, CFDataRef element slots in rangeStorage + + if (alloc == NULL) alloc = __CFGetDefaultAllocator(); + + while ((rangeToSearch.length > 0) && CFStringFindWithOptions(string, stringToFind, rangeToSearch, compareOptions, &foundRange)) { + // Determine the next range + if (backwards) { + rangeToSearch.length = foundRange.location - rangeToSearch.location; + } else { + rangeToSearch.location = foundRange.location + foundRange.length; + rangeToSearch.length = endIndex - rangeToSearch.location; + } + + // If necessary, grow the data and squirrel away the found range + if (foundCount >= capacity) { + if (rangeStorage == NULL) rangeStorage = CFDataCreateMutable(alloc, 0); + capacity = (capacity + 4) * 2; + CFDataSetLength(rangeStorage, capacity * (sizeof(CFRange) + sizeof(CFDataRef))); + rangeStorageBytes = (uint8_t *)CFDataGetMutableBytePtr(rangeStorage) + foundCount * (sizeof(CFRange) + sizeof(CFDataRef)); + } + memmove(rangeStorageBytes, &foundRange, sizeof(CFRange)); // The range + memmove(rangeStorageBytes + sizeof(CFRange), &rangeStorage, sizeof(CFDataRef)); // The data + rangeStorageBytes += (sizeof(CFRange) + sizeof(CFDataRef)); + foundCount++; + } + + if (foundCount > 0) { + CFIndex cnt; + CFMutableArrayRef array; + const CFArrayCallBacks callbacks = {0, __rangeRetain, __rangeRelease, __rangeCopyDescription, __rangeEqual}; + + CFDataSetLength(rangeStorage, foundCount * (sizeof(CFRange) + sizeof(CFDataRef))); // Tighten storage up + rangeStorageBytes = (uint8_t *)CFDataGetMutableBytePtr(rangeStorage); + + array = CFArrayCreateMutable(alloc, foundCount * sizeof(CFRange *), &callbacks); + for (cnt = 0; cnt < foundCount; cnt++) { + // Each element points to the appropriate CFRange in the CFData + CFArrayAppendValue(array, rangeStorageBytes + cnt * (sizeof(CFRange) + sizeof(CFDataRef))); + } + CFRelease(rangeStorage); // We want the data to go away when all CFRanges inside it are released... + return array; + } else { + return NULL; + } +} + + +CFRange CFStringFind(CFStringRef string, CFStringRef stringToFind, CFOptionFlags compareOptions) { + CFRange foundRange; + + if (CFStringFindWithOptions(string, stringToFind, CFRangeMake(0, CFStringGetLength(string)), compareOptions, &foundRange)) { + return foundRange; + } else { + return CFRangeMake(kCFNotFound, 0); + } +} + +Boolean CFStringHasPrefix(CFStringRef string, CFStringRef prefix) { + return CFStringFindWithOptions(string, prefix, CFRangeMake(0, CFStringGetLength(string)), kCFCompareAnchored, NULL); +} + +Boolean CFStringHasSuffix(CFStringRef string, CFStringRef suffix) { + return CFStringFindWithOptions(string, suffix, CFRangeMake(0, CFStringGetLength(string)), kCFCompareAnchored|kCFCompareBackwards, NULL); +} + +#define MAX_TRANSCODING_LENGTH 4 + +#define HANGUL_JONGSEONG_COUNT (28) + +CF_INLINE bool _CFStringIsHangulLVT(UTF32Char character) { + return (((character - HANGUL_SYLLABLE_START) % HANGUL_JONGSEONG_COUNT) ? true : false); +} + +static uint8_t __CFTranscodingHintLength[] = { + 2, 3, 4, 4, 4, 4, 4, 2, 2, 2, 2, 4, 0, 0, 0, 0 +}; + +enum { + kCFStringHangulStateL, + kCFStringHangulStateV, + kCFStringHangulStateT, + kCFStringHangulStateLV, + kCFStringHangulStateLVT, + kCFStringHangulStateBreak +}; + +static CFRange _CFStringInlineBufferGetComposedRange(CFStringInlineBuffer *buffer, CFIndex start, CFStringCharacterClusterType type, const uint8_t *bmpBitmap, CFIndex csetType) { + CFIndex end = start + 1; + const uint8_t *bitmap = bmpBitmap; + UTF32Char character; + UTF16Char otherSurrogate; + uint8_t step; + + character = CFStringGetCharacterFromInlineBuffer(buffer, start); + + // We don't combine characters in Armenian ~ Limbu range for backward deletion + if ((type != kCFStringBackwardDeletionCluster) || (character < 0x0530) || (character > 0x194F)) { + // Check if the current is surrogate + if (CFUniCharIsSurrogateHighCharacter(character) && CFUniCharIsSurrogateLowCharacter((otherSurrogate = CFStringGetCharacterFromInlineBuffer(buffer, start + 1)))) { + ++end; + character = CFUniCharGetLongCharacterForSurrogatePair(character, otherSurrogate); + bitmap = CFUniCharGetBitmapPtrForPlane(csetType, (character >> 16)); + } + + // Extend backward + while (start > 0) { + if ((type == kCFStringBackwardDeletionCluster) && (character >= 0x0530) && (character < 0x1950)) break; + + if (character < 0x10000) { // the first round could be already be non-BMP + if (CFUniCharIsSurrogateLowCharacter(character) && CFUniCharIsSurrogateHighCharacter((otherSurrogate = CFStringGetCharacterFromInlineBuffer(buffer, start - 1)))) { + character = CFUniCharGetLongCharacterForSurrogatePair(otherSurrogate, character); + bitmap = CFUniCharGetBitmapPtrForPlane(csetType, (character >> 16)); + --start; + } else { + bitmap = bmpBitmap; + } + } + + if (!CFUniCharIsMemberOfBitmap(character, bitmap) && (character != 0xFF9E) && (character != 0xFF9F) && ((character & 0x1FFFF0) != 0xF870)) break; + + --start; + + character = CFStringGetCharacterFromInlineBuffer(buffer, start); + } + } + + // Hangul + if (((character >= HANGUL_CHOSEONG_START) && (character <= HANGUL_JONGSEONG_END)) || ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END))) { + uint8_t state; + uint8_t initialState; + + if (character < HANGUL_JUNGSEONG_START) { + state = kCFStringHangulStateL; + } else if (character < HANGUL_JONGSEONG_START) { + state = kCFStringHangulStateV; + } else if (character < HANGUL_SYLLABLE_START) { + state = kCFStringHangulStateT; + } else { + state = (_CFStringIsHangulLVT(character) ? kCFStringHangulStateLVT : kCFStringHangulStateLV); + } + initialState = state; + + // Extend backward + while (((character = CFStringGetCharacterFromInlineBuffer(buffer, start - 1)) >= HANGUL_CHOSEONG_START) && (character <= HANGUL_SYLLABLE_END) && ((character <= HANGUL_JONGSEONG_END) || (character >= HANGUL_SYLLABLE_START))) { + switch (state) { + case kCFStringHangulStateV: + if (character <= HANGUL_CHOSEONG_END) { + state = kCFStringHangulStateL; + } else if ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END) && !_CFStringIsHangulLVT(character)) { + state = kCFStringHangulStateLV; + } else if (character > HANGUL_JUNGSEONG_END) { + state = kCFStringHangulStateBreak; + } + break; + + case kCFStringHangulStateT: + if ((character >= HANGUL_JUNGSEONG_START) && (character <= HANGUL_JUNGSEONG_END)) { + state = kCFStringHangulStateV; + } else if ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END)) { + state = (_CFStringIsHangulLVT(character) ? kCFStringHangulStateLVT : kCFStringHangulStateLV); + } else if (character < HANGUL_JUNGSEONG_START) { + state = kCFStringHangulStateBreak; + } + break; + + default: + state = ((character < HANGUL_JUNGSEONG_START) ? kCFStringHangulStateL : kCFStringHangulStateBreak); + break; + } + + if (state == kCFStringHangulStateBreak) break; + --start; + } + + // Extend forward + state = initialState; + while (((character = CFStringGetCharacterFromInlineBuffer(buffer, end)) > 0) && (((character >= HANGUL_CHOSEONG_START) && (character <= HANGUL_JONGSEONG_END)) || ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END)))) { + switch (state) { + case kCFStringHangulStateLV: + case kCFStringHangulStateV: + if ((character >= HANGUL_JUNGSEONG_START) && (character <= HANGUL_JONGSEONG_END)) { + state = ((character < HANGUL_JONGSEONG_START) ? kCFStringHangulStateV : kCFStringHangulStateT); + } else { + state = kCFStringHangulStateBreak; + } + break; + + case kCFStringHangulStateLVT: + case kCFStringHangulStateT: + state = (((character >= HANGUL_JONGSEONG_START) && (character <= HANGUL_JONGSEONG_END)) ? kCFStringHangulStateT : kCFStringHangulStateBreak); + break; + + default: + if (character < HANGUL_JUNGSEONG_START) { + state = kCFStringHangulStateL; + } else if (character < HANGUL_JONGSEONG_START) { + state = kCFStringHangulStateV; + } else if (character >= HANGUL_SYLLABLE_START) { + state = (_CFStringIsHangulLVT(character) ? kCFStringHangulStateLVT : kCFStringHangulStateLV); + } else { + state = kCFStringHangulStateBreak; + } + break; + } + + if (state == kCFStringHangulStateBreak) break; + ++end; + } + } + + // Extend forward + while ((character = CFStringGetCharacterFromInlineBuffer(buffer, end)) > 0) { + if ((type == kCFStringBackwardDeletionCluster) && (character >= 0x0530) && (character < 0x1950)) break; + + if (CFUniCharIsSurrogateHighCharacter(character) && CFUniCharIsSurrogateLowCharacter((otherSurrogate = CFStringGetCharacterFromInlineBuffer(buffer, end + 1)))) { + character = CFUniCharGetLongCharacterForSurrogatePair(character, otherSurrogate); + bitmap = CFUniCharGetBitmapPtrForPlane(csetType, (character >> 16)); + step = 2; + } else { + bitmap = bmpBitmap; + step = 1; + } + + if (!CFUniCharIsMemberOfBitmap(character, bitmap) && (character != 0xFF9E) && (character != 0xFF9F) && ((character & 0x1FFFF0) != 0xF870)) break; + + end += step; + } + + return CFRangeMake(start, end - start); +} + +CF_INLINE bool _CFStringIsVirama(UTF32Char character, const uint8_t *combClassBMP) { + return ((character == COMBINING_GRAPHEME_JOINER) || (CFUniCharGetCombiningPropertyForCharacter(character, (const uint8_t *)((character < 0x10000) ? combClassBMP : CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty, (character >> 16)))) == 9) ? true : false); +} + +CFRange CFStringGetRangeOfCharacterClusterAtIndex(CFStringRef string, CFIndex charIndex, CFStringCharacterClusterType type) { + CFRange range; + CFIndex currentIndex; + CFIndex length = CFStringGetLength(string); + CFIndex csetType = ((kCFStringGraphemeCluster == type) ? kCFUniCharGraphemeExtendCharacterSet : kCFUniCharNonBaseCharacterSet); + CFStringInlineBuffer stringBuffer; + const uint8_t *bmpBitmap; + const uint8_t *letterBMP; + const uint8_t *combClassBMP; + UTF32Char character; + UTF16Char otherSurrogate; + + if (charIndex >= length) return CFRangeMake(kCFNotFound, 0); + + /* Fast case. If we're eight-bit, it's either the default encoding is cheap or the content is all ASCII. Watch out when (or if) adding more 8bit Mac-scripts in CFStringEncodingConverters + */ + if (!CF_IS_OBJC(__kCFStringTypeID, string) && __CFStrIsEightBit(string)) return CFRangeMake(charIndex, 1); + + bmpBitmap = CFUniCharGetBitmapPtrForPlane(csetType, 0); + letterBMP = CFUniCharGetBitmapPtrForPlane(kCFUniCharLetterCharacterSet, 0); + combClassBMP = (const uint8_t *)CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty, 0); + + CFStringInitInlineBuffer(string, &stringBuffer, CFRangeMake(0, length)); + + // Get composed character sequence first + range = _CFStringInlineBufferGetComposedRange(&stringBuffer, charIndex, type, bmpBitmap, csetType); + + // Do grapheme joiners + if (type < kCFStringCursorMovementCluster) { + const uint8_t *letter = letterBMP; + + // Check to see if we have a letter at the beginning of initial cluster + character = CFStringGetCharacterFromInlineBuffer(&stringBuffer, range.location); + + if ((range.length > 1) && CFUniCharIsSurrogateHighCharacter(character) && CFUniCharIsSurrogateLowCharacter((otherSurrogate = CFStringGetCharacterFromInlineBuffer(&stringBuffer, range.location + 1)))) { + character = CFUniCharGetLongCharacterForSurrogatePair(character, otherSurrogate); + letter = CFUniCharGetBitmapPtrForPlane(kCFUniCharLetterCharacterSet, (character >> 16)); + } + + if ((character == ZERO_WIDTH_JOINER) || CFUniCharIsMemberOfBitmap(character, letter)) { + CFRange otherRange; + + // Check if preceded by grapheme joiners (U034F and viramas) + otherRange.location = currentIndex = range.location; + + while (currentIndex > 1) { + character = CFStringGetCharacterFromInlineBuffer(&stringBuffer, --currentIndex); + + // ??? We're assuming viramas only in BMP + if ((_CFStringIsVirama(character, combClassBMP) || ((character == ZERO_WIDTH_JOINER) && _CFStringIsVirama(CFStringGetCharacterFromInlineBuffer(&stringBuffer, --currentIndex), combClassBMP))) && (currentIndex > 0)) { + --currentIndex; + } else { + break; + } + + currentIndex = _CFStringInlineBufferGetComposedRange(&stringBuffer, currentIndex, type, bmpBitmap, csetType).location; + + character = CFStringGetCharacterFromInlineBuffer(&stringBuffer, currentIndex); + + if (CFUniCharIsSurrogateLowCharacter(character) && CFUniCharIsSurrogateHighCharacter((otherSurrogate = CFStringGetCharacterFromInlineBuffer(&stringBuffer, currentIndex - 1)))) { + character = CFUniCharGetLongCharacterForSurrogatePair(character, otherSurrogate); + letter = CFUniCharGetBitmapPtrForPlane(kCFUniCharLetterCharacterSet, (character >> 16)); + --currentIndex; + } else { + letter = letterBMP; + } + + if (!CFUniCharIsMemberOfBitmap(character, letter)) break; + range.location = currentIndex; + } + + range.length += otherRange.location - range.location; + + // Check if followed by grapheme joiners + if ((range.length > 1) && ((range.location + range.length) < length)) { + otherRange = range; + currentIndex = otherRange.location + otherRange.length; + + do { + character = CFStringGetCharacterFromInlineBuffer(&stringBuffer, currentIndex - 1); + + // ??? We're assuming viramas only in BMP + if ((character != ZERO_WIDTH_JOINER) && !_CFStringIsVirama(character, combClassBMP)) break; + + character = CFStringGetCharacterFromInlineBuffer(&stringBuffer, currentIndex); + + if (character == ZERO_WIDTH_JOINER) character = CFStringGetCharacterFromInlineBuffer(&stringBuffer, ++currentIndex); + + if (CFUniCharIsSurrogateHighCharacter(character) && CFUniCharIsSurrogateLowCharacter((otherSurrogate = CFStringGetCharacterFromInlineBuffer(&stringBuffer, currentIndex + 1)))) { + character = CFUniCharGetLongCharacterForSurrogatePair(character, otherSurrogate); + letter = CFUniCharGetBitmapPtrForPlane(kCFUniCharLetterCharacterSet, (character >> 16)); + } else { + letter = letterBMP; + } + + // We only conjoin letters + if (!CFUniCharIsMemberOfBitmap(character, letter)) break; + otherRange = _CFStringInlineBufferGetComposedRange(&stringBuffer, currentIndex, type, bmpBitmap, csetType); + currentIndex = otherRange.location + otherRange.length; + } while ((otherRange.location + otherRange.length) < length); + range.length = currentIndex - range.location; + } + } + } + + // Check if we're part of prefix transcoding hints + CFIndex otherIndex; + + currentIndex = (range.location + range.length) - (MAX_TRANSCODING_LENGTH + 1); + if (currentIndex < 0) currentIndex = 0; + + while (currentIndex <= range.location) { + character = CFStringGetCharacterFromInlineBuffer(&stringBuffer, currentIndex); + + if ((character & 0x1FFFF0) == 0xF860) { // transcoding hint + otherIndex = currentIndex + __CFTranscodingHintLength[(character - 0xF860)] + 1; + if (otherIndex >= (range.location + range.length)) { + if (otherIndex <= length) { + range.location = currentIndex; + range.length = otherIndex - currentIndex; + } + break; + } + } + ++currentIndex; + } + + return range; +} + +#if 1 /* Using the new implementation. Leaving the old implementation if'ed out for testing purposes for now */ +CFRange CFStringGetRangeOfComposedCharactersAtIndex(CFStringRef theString, CFIndex theIndex) { + return CFStringGetRangeOfCharacterClusterAtIndex(theString, theIndex, kCFStringComposedCharacterCluster); +} +#else +/*! + @function CFStringGetRangeOfComposedCharactersAtIndex + Returns the range of the composed character sequence at the specified index. + @param theString The CFString which is to be searched. If this + parameter is not a valid CFString, the behavior is + undefined. + @param theIndex The index of the character contained in the + composed character sequence. If the index is + outside the index space of the string (0 to N-1 inclusive, + where N is the length of the string), the behavior is + undefined. + @result The range of the composed character sequence. +*/ +#define ExtHighHalfZoneLow 0xD800 +#define ExtHighHalfZoneHigh 0xDBFF +#define ExtLowHalfZoneLow 0xDC00 +#define ExtLowHalfZoneHigh 0xDFFF +#define JunseongStart 0x1160 +#define JonseongEnd 0x11F9 +CF_INLINE Boolean IsHighCode(UniChar X) { return (X >= ExtHighHalfZoneLow && X <= ExtHighHalfZoneHigh); } +CF_INLINE Boolean IsLowCode(UniChar X) { return (X >= ExtLowHalfZoneLow && X <= ExtLowHalfZoneHigh); } +#define IsHangulConjoiningJamo(X) (X >= JunseongStart && X <= JonseongEnd) +#define IsHalfwidthKanaVoicedMark(X) ((X == 0xFF9E) || (X == 0xFF9F)) +CF_INLINE Boolean IsNonBaseChar(UniChar X, CFCharacterSetRef nonBaseSet) { return (CFCharacterSetIsCharacterMember(nonBaseSet, X) || IsHangulConjoiningJamo(X) || IsHalfwidthKanaVoicedMark(X) || (X & 0x1FFFF0) == 0xF870); } // combining char, hangul jamo, or Apple corporate variant tag +#define ZWJ 0x200D +#define ZWNJ 0x200C +#define COMBINING_GRAPHEME_JOINER (0x034F) + +static CFCharacterSetRef nonBaseChars = NULL; +static CFCharacterSetRef letterChars = NULL; +static const void *__CFCombiningClassBMP = NULL; + +CF_INLINE bool IsVirama(UTF32Char character) { + return ((character == COMBINING_GRAPHEME_JOINER) ? true : ((character < 0x10000) && (CFUniCharGetCombiningPropertyForCharacter(character, __CFCombiningClassBMP) == 9) ? true : false)); +} + +CFRange CFStringGetRangeOfComposedCharactersAtIndex(CFStringRef theString, CFIndex theIndex) { + CFIndex left, current, save; + CFIndex len = CFStringGetLength(theString); + CFStringInlineBuffer stringBuffer; + static volatile Boolean _isInited = false; + + if (theIndex >= len) return CFRangeMake(kCFNotFound, 0); + + if (!_isInited) { + nonBaseChars = CFCharacterSetGetPredefined(kCFCharacterSetNonBase); + letterChars = CFCharacterSetGetPredefined(kCFCharacterSetLetter); + __CFCombiningClassBMP = CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty, 0); + _isInited = true; + } + + save = current = theIndex; + + CFStringInitInlineBuffer(theString, &stringBuffer, CFRangeMake(0, len)); + + /* + * First check for transcoding hints + */ + { + CFRange theRange = (current > MAX_TRANSCODING_LENGTH ? CFRangeMake(current - MAX_TRANSCODING_LENGTH, MAX_TRANSCODING_LENGTH + 1) : CFRangeMake(0, current + 1)); + + // Should check the next loc ? + if (current + 1 < len) ++theRange.length; + + if (theRange.length > 1) { + UniChar characterBuffer[MAX_TRANSCODING_LENGTH + 2]; // Transcoding hint length + current loc + next loc + + if (stringBuffer.directBuffer) { + memmove(characterBuffer, stringBuffer.directBuffer + theRange.location, theRange.length * sizeof(UniChar)); + } else { + CFStringGetCharacters(theString, theRange, characterBuffer); + } + + while (current >= theRange.location) { + if ((characterBuffer[current - theRange.location] & 0x1FFFF0) == 0xF860) { + theRange = CFRangeMake(current, __CFTranscodingHintLength[characterBuffer[current - theRange.location] - 0xF860] + 1); + if ((theRange.location + theRange.length) <= theIndex) break; + if ((theRange.location + theRange.length) >= len) theRange.length = len - theRange.location; + return theRange; + } + if (current == 0) break; + --current; + } + current = theIndex; // Reset current + } + } + +//#warning Aki 5/29/01 This does not support non-base chars in non-BMP planes (i.e. musical symbol combining stem in Unicode 3.1) + /* + * if we start NOT on a base, first move back to a base as appropriate. + */ + + roundAgain: + + while ((current > 0) && IsNonBaseChar(CFStringGetCharacterFromInlineBuffer(&stringBuffer, current), nonBaseChars)) --current; + + if (current >= 1 && current < len && CFCharacterSetIsCharacterMember(letterChars, CFStringGetCharacterFromInlineBuffer(&stringBuffer, current)) && IsVirama(CFStringGetCharacterFromInlineBuffer(&stringBuffer, current - 1))) { + --current; + goto roundAgain; + } else if ((current >= 2) && (CFStringGetCharacterFromInlineBuffer(&stringBuffer, current - 1) == ZWJ) && IsVirama(CFStringGetCharacterFromInlineBuffer(&stringBuffer, current - 2))) { + current -= 2; + goto roundAgain; + } + + /* + * Set the left position, then jump back to the saved original position. + */ + + if (current >= 1 && IsLowCode(CFStringGetCharacterFromInlineBuffer(&stringBuffer, current)) && IsHighCode(CFStringGetCharacterFromInlineBuffer(&stringBuffer, current - 1))) --current; + left = current; + current = save; + + /* + * Now, presume we are on a base; move forward & look for the next base. + * Handle jumping over H/L codes. + */ + if (IsHighCode(CFStringGetCharacterFromInlineBuffer(&stringBuffer, current)) && (current + 1) < len && IsLowCode(CFStringGetCharacterFromInlineBuffer(&stringBuffer, current + 1))) ++current; + ++current; + + round2Again: + + if (current < len) { + while (IsNonBaseChar(CFStringGetCharacterFromInlineBuffer(&stringBuffer, current), nonBaseChars)) { + ++current; + if (current >= len) break; + } + if ((current < len) && CFCharacterSetIsCharacterMember(letterChars, CFStringGetCharacterFromInlineBuffer(&stringBuffer, current))) { + if (IsVirama(CFStringGetCharacterFromInlineBuffer(&stringBuffer, current - 1))) { + ++current; goto round2Again; + } else if ((current >= 2) && (CFStringGetCharacterFromInlineBuffer(&stringBuffer, current - 1) == ZWJ) && IsVirama(CFStringGetCharacterFromInlineBuffer(&stringBuffer, current - 2))) { + ++current; goto round2Again; + } + } + } + /* + * Now, "current" is a base, and "left" is a base. + * The junk between had better contain "save"! + */ + if ((! (left <= save)) || (! (save <= current))) { + CFLog(kCFLogLevelWarning, CFSTR("CFString: CFStringGetRangeOfComposedCharactersAtIndex:%d returned invalid\n"), save); + } + return CFRangeMake(left, current - left); +} +#endif + +/*! + @function CFStringFindCharacterFromSet + Query the range of characters contained in the specified character set. + @param theString The CFString which is to be searched. If this + parameter is not a valid CFString, the behavior is + undefined. + @param theSet The CFCharacterSet against which the membership + of characters is checked. If this parameter is not a valid + CFCharacterSet, the behavior is undefined. + @param range The range of characters within the string to search. If + the range location or end point (defined by the location + plus length minus 1) are outside the index space of the + string (0 to N-1 inclusive, where N is the length of the + string), the behavior is undefined. If the range length is + negative, the behavior is undefined. The range may be empty + (length 0), in which case no search is performed. + @param searchOptions The bitwise-or'ed option flags to control + the search behavior. The supported options are + kCFCompareBackwards andkCFCompareAnchored. + If other option flags are specified, the behavior + is undefined. + @param result The pointer to a CFRange supplied by the caller in + which the search result is stored. If a pointer to an invalid + memory is specified, the behavior is undefined. + @result true, if at least a character which is a member of the character + set is found and result is filled, otherwise, false. +*/ +#define SURROGATE_START 0xD800 +#define SURROGATE_END 0xDFFF + +CF_EXPORT Boolean CFStringFindCharacterFromSet(CFStringRef theString, CFCharacterSetRef theSet, CFRange rangeToSearch, CFOptionFlags searchOptions, CFRange *result) { + CFStringInlineBuffer stringBuffer; + CFCharacterSetInlineBuffer csetBuffer; + UniChar ch; + CFIndex step; + CFIndex fromLoc, toLoc, cnt; // fromLoc and toLoc are inclusive + Boolean found = false; + Boolean done = false; + +//#warning FIX ME !! Should support kCFCompareNonliteral + + if ((rangeToSearch.location + rangeToSearch.length > CFStringGetLength(theString)) || (rangeToSearch.length == 0)) return false; + + if (searchOptions & kCFCompareBackwards) { + fromLoc = rangeToSearch.location + rangeToSearch.length - 1; + toLoc = rangeToSearch.location; + } else { + fromLoc = rangeToSearch.location; + toLoc = rangeToSearch.location + rangeToSearch.length - 1; + } + if (searchOptions & kCFCompareAnchored) { + toLoc = fromLoc; + } + + step = (fromLoc <= toLoc) ? 1 : -1; + cnt = fromLoc; + + CFStringInitInlineBuffer(theString, &stringBuffer, rangeToSearch); + CFCharacterSetInitInlineBuffer(theSet, &csetBuffer); + + do { + ch = CFStringGetCharacterFromInlineBuffer(&stringBuffer, cnt - rangeToSearch.location); + if ((ch >= SURROGATE_START) && (ch <= SURROGATE_END)) { + int otherCharIndex = cnt + step; + + if (((step < 0) && (otherCharIndex < toLoc)) || ((step > 0) && (otherCharIndex > toLoc))) { + done = true; + } else { + UniChar highChar; + UniChar lowChar = CFStringGetCharacterFromInlineBuffer(&stringBuffer, otherCharIndex - rangeToSearch.location); + + if (cnt < otherCharIndex) { + highChar = ch; + } else { + highChar = lowChar; + lowChar = ch; + } + + if (CFUniCharIsSurrogateHighCharacter(highChar) && CFUniCharIsSurrogateLowCharacter(lowChar) && CFCharacterSetInlineBufferIsLongCharacterMember(&csetBuffer, CFUniCharGetLongCharacterForSurrogatePair(highChar, lowChar))) { + if (result) *result = CFRangeMake((cnt < otherCharIndex ? cnt : otherCharIndex), 2); + return true; + } else if (otherCharIndex == toLoc) { + done = true; + } else { + cnt = otherCharIndex + step; + } + } + } else if (CFCharacterSetInlineBufferIsLongCharacterMember(&csetBuffer, ch)) { + done = found = true; + } else if (cnt == toLoc) { + done = true; + } else { + cnt += step; + } + } while (!done); + + if (found && result) *result = CFRangeMake(cnt, 1); + return found; +} + +/* Line range code */ + +#define CarriageReturn '\r' /* 0x0d */ +#define NewLine '\n' /* 0x0a */ +#define NextLine 0x0085 +#define LineSeparator 0x2028 +#define ParaSeparator 0x2029 + +CF_INLINE Boolean isALineSeparatorTypeCharacter(UniChar ch, Boolean includeLineEndings) { + if (ch > CarriageReturn && ch < NextLine) return false; /* Quick test to cover most chars */ + return (ch == NewLine || ch == CarriageReturn || ch == ParaSeparator || (includeLineEndings && (ch == NextLine || ch == LineSeparator))) ? true : false; +} + +static void __CFStringGetLineOrParagraphBounds(CFStringRef string, CFRange range, CFIndex *lineBeginIndex, CFIndex *lineEndIndex, CFIndex *contentsEndIndex, Boolean includeLineEndings) { + CFIndex len; + CFStringInlineBuffer buf; + UniChar ch; + + __CFAssertIsString(string); + __CFAssertRangeIsInStringBounds(string, range.location, range.length); + + len = __CFStrLength(string); + + if (lineBeginIndex) { + CFIndex start; + if (range.location == 0) { + start = 0; + } else { + CFStringInitInlineBuffer(string, &buf, CFRangeMake(0, len)); + CFIndex buf_idx = range.location; + + /* Take care of the special case where start happens to fall right between \r and \n */ + ch = CFStringGetCharacterFromInlineBuffer(&buf, buf_idx); + buf_idx--; + if ((ch == NewLine) && (CFStringGetCharacterFromInlineBuffer(&buf, buf_idx) == CarriageReturn)) { + buf_idx--; + } + while (1) { + if (buf_idx < 0) { + start = 0; + break; + } else if (isALineSeparatorTypeCharacter(CFStringGetCharacterFromInlineBuffer(&buf, buf_idx), includeLineEndings)) { + start = buf_idx + 1; + break; + } else { + buf_idx--; + } + } + } + *lineBeginIndex = start; + } + + /* Now find the ending point */ + if (lineEndIndex || contentsEndIndex) { + CFIndex endOfContents, lineSeparatorLength = 1; /* 1 by default */ + CFStringInitInlineBuffer(string, &buf, CFRangeMake(0, len)); + CFIndex buf_idx = range.location + range.length - (range.length ? 1 : 0); + /* First look at the last char in the range (if the range is zero length, the char after the range) to see if we're already on or within a end of line sequence... */ + ch = __CFStringGetCharacterFromInlineBufferAux(&buf, buf_idx); + if (ch == NewLine) { + endOfContents = buf_idx; + buf_idx--; + if (__CFStringGetCharacterFromInlineBufferAux(&buf, buf_idx) == CarriageReturn) { + lineSeparatorLength = 2; + endOfContents--; + } + } else { + while (1) { + if (isALineSeparatorTypeCharacter(ch, includeLineEndings)) { + endOfContents = buf_idx; /* This is actually end of contentsRange */ + buf_idx++; /* OK for this to go past the end */ + if ((ch == CarriageReturn) && (__CFStringGetCharacterFromInlineBufferAux(&buf, buf_idx) == NewLine)) { + lineSeparatorLength = 2; + } + break; + } else if (buf_idx >= len) { + endOfContents = len; + lineSeparatorLength = 0; + break; + } else { + buf_idx++; + ch = __CFStringGetCharacterFromInlineBufferAux(&buf, buf_idx); + } + } + } + if (contentsEndIndex) *contentsEndIndex = endOfContents; + if (lineEndIndex) *lineEndIndex = endOfContents + lineSeparatorLength; + } +} + +void CFStringGetLineBounds(CFStringRef string, CFRange range, CFIndex *lineBeginIndex, CFIndex *lineEndIndex, CFIndex *contentsEndIndex) { + CF_OBJC_FUNCDISPATCH4(__kCFStringTypeID, void, string, "getLineStart:end:contentsEnd:forRange:", lineBeginIndex, lineEndIndex, contentsEndIndex, CFRangeMake(range.location, range.length)); + __CFStringGetLineOrParagraphBounds(string, range, lineBeginIndex, lineEndIndex, contentsEndIndex, true); +} + +void CFStringGetParagraphBounds(CFStringRef string, CFRange range, CFIndex *parBeginIndex, CFIndex *parEndIndex, CFIndex *contentsEndIndex) { + CF_OBJC_FUNCDISPATCH4(__kCFStringTypeID, void, string, "getParagraphStart:end:contentsEnd:forRange:", parBeginIndex, parEndIndex, contentsEndIndex, CFRangeMake(range.location, range.length)); + __CFStringGetLineOrParagraphBounds(string, range, parBeginIndex, parEndIndex, contentsEndIndex, false); +} + + +CFStringRef CFStringCreateByCombiningStrings(CFAllocatorRef alloc, CFArrayRef array, CFStringRef separatorString) { + CFIndex numChars; + CFIndex separatorNumByte; + CFIndex stringCount = CFArrayGetCount(array); + Boolean isSepCFString = !CF_IS_OBJC(__kCFStringTypeID, separatorString); + Boolean canBeEightbit = isSepCFString && __CFStrIsEightBit(separatorString); + CFIndex idx; + CFStringRef otherString; + void *buffer; + uint8_t *bufPtr; + const void *separatorContents = NULL; + + if (stringCount == 0) { + return CFStringCreateWithCharacters(alloc, NULL, 0); + } else if (stringCount == 1) { + return (CFStringRef)CFStringCreateCopy(alloc, (CFStringRef)CFArrayGetValueAtIndex(array, 0)); + } + + if (alloc == NULL) alloc = __CFGetDefaultAllocator(); + + numChars = CFStringGetLength(separatorString) * (stringCount - 1); + for (idx = 0; idx < stringCount; idx++) { + otherString = (CFStringRef)CFArrayGetValueAtIndex(array, idx); + numChars += CFStringGetLength(otherString); + // canBeEightbit is already false if the separator is an NSString... + if (!CF_IS_OBJC(__kCFStringTypeID, otherString) && __CFStrIsUnicode(otherString)) canBeEightbit = false; + } + + buffer = (uint8_t *)CFAllocatorAllocate(alloc, canBeEightbit ? ((numChars + 1) * sizeof(uint8_t)) : (numChars * sizeof(UniChar)), 0); + bufPtr = (uint8_t *)buffer; + if (__CFOASafe) __CFSetLastAllocationEventName(buffer, "CFString (store)"); + separatorNumByte = CFStringGetLength(separatorString) * (canBeEightbit ? sizeof(uint8_t) : sizeof(UniChar)); + + for (idx = 0; idx < stringCount; idx++) { + if (idx) { // add separator here unless first string + if (separatorContents) { + memmove(bufPtr, separatorContents, separatorNumByte); + } else { + if (!isSepCFString) { // NSString + CFStringGetCharacters(separatorString, CFRangeMake(0, CFStringGetLength(separatorString)), (UniChar *)bufPtr); + } else if (canBeEightbit || __CFStrIsUnicode(separatorString)) { + memmove(bufPtr, (const uint8_t *)__CFStrContents(separatorString) + __CFStrSkipAnyLengthByte(separatorString), separatorNumByte); + } else { + __CFStrConvertBytesToUnicode((uint8_t *)__CFStrContents(separatorString) + __CFStrSkipAnyLengthByte(separatorString), (UniChar *)bufPtr, __CFStrLength(separatorString)); + } + separatorContents = bufPtr; + } + bufPtr += separatorNumByte; + } + + otherString = (CFStringRef )CFArrayGetValueAtIndex(array, idx); + if (CF_IS_OBJC(__kCFStringTypeID, otherString)) { + CFIndex otherLength = CFStringGetLength(otherString); + CFStringGetCharacters(otherString, CFRangeMake(0, otherLength), (UniChar *)bufPtr); + bufPtr += otherLength * sizeof(UniChar); + } else { + const uint8_t * otherContents = (const uint8_t *)__CFStrContents(otherString); + CFIndex otherNumByte = __CFStrLength2(otherString, otherContents) * (canBeEightbit ? sizeof(uint8_t) : sizeof(UniChar)); + + if (canBeEightbit || __CFStrIsUnicode(otherString)) { + memmove(bufPtr, otherContents + __CFStrSkipAnyLengthByte(otherString), otherNumByte); + } else { + __CFStrConvertBytesToUnicode(otherContents + __CFStrSkipAnyLengthByte(otherString), (UniChar *)bufPtr, __CFStrLength2(otherString, otherContents)); + } + bufPtr += otherNumByte; + } + } + if (canBeEightbit) *bufPtr = 0; // NULL byte; + + return canBeEightbit ? + CFStringCreateWithCStringNoCopy(alloc, (const char*)buffer, __CFStringGetEightBitStringEncoding(), alloc) : + CFStringCreateWithCharactersNoCopy(alloc, (UniChar *)buffer, numChars, alloc); +} + + +CFArrayRef CFStringCreateArrayBySeparatingStrings(CFAllocatorRef alloc, CFStringRef string, CFStringRef separatorString) { + CFArrayRef separatorRanges; + CFIndex length = CFStringGetLength(string); + /* No objc dispatch needed here since CFStringCreateArrayWithFindResults() works with both CFString and NSString */ + if (!(separatorRanges = CFStringCreateArrayWithFindResults(alloc, string, separatorString, CFRangeMake(0, length), 0))) { + return CFArrayCreate(alloc, (const void **)&string, 1, & kCFTypeArrayCallBacks); + } else { + CFIndex idx; + CFIndex count = CFArrayGetCount(separatorRanges); + CFIndex startIndex = 0; + CFIndex numChars; + CFMutableArrayRef array = CFArrayCreateMutable(alloc, count + 2, & kCFTypeArrayCallBacks); + const CFRange *currentRange; + CFStringRef substring; + + for (idx = 0;idx < count;idx++) { + currentRange = (const CFRange *)CFArrayGetValueAtIndex(separatorRanges, idx); + numChars = currentRange->location - startIndex; + substring = CFStringCreateWithSubstring(alloc, string, CFRangeMake(startIndex, numChars)); + CFArrayAppendValue(array, substring); + CFRelease(substring); + startIndex = currentRange->location + currentRange->length; + } + substring = CFStringCreateWithSubstring(alloc, string, CFRangeMake(startIndex, length - startIndex)); + CFArrayAppendValue(array, substring); + CFRelease(substring); + + CFRelease(separatorRanges); + + return array; + } +} + +CFStringRef CFStringCreateFromExternalRepresentation(CFAllocatorRef alloc, CFDataRef data, CFStringEncoding encoding) { + return CFStringCreateWithBytes(alloc, CFDataGetBytePtr(data), CFDataGetLength(data), encoding, true); +} + + +CFDataRef CFStringCreateExternalRepresentation(CFAllocatorRef alloc, CFStringRef string, CFStringEncoding encoding, uint8_t lossByte) { + CFIndex length; + CFIndex guessedByteLength; + uint8_t *bytes; + CFIndex usedLength; + SInt32 result; + + if (CF_IS_OBJC(__kCFStringTypeID, string)) { /* ??? Hope the compiler optimizes this away if OBJC_MAPPINGS is not on */ + length = CFStringGetLength(string); + } else { + __CFAssertIsString(string); + length = __CFStrLength(string); + if (__CFStrIsEightBit(string) && ((__CFStringGetEightBitStringEncoding() == encoding) || (__CFStringGetEightBitStringEncoding() == kCFStringEncodingASCII && __CFStringEncodingIsSupersetOfASCII(encoding)))) { // Requested encoding is equal to the encoding in string + return CFDataCreate(alloc, ((uint8_t *)__CFStrContents(string) + __CFStrSkipAnyLengthByte(string)), __CFStrLength(string)); + } + } + + if (alloc == NULL) alloc = __CFGetDefaultAllocator(); + + if (((encoding & 0x0FFF) == kCFStringEncodingUnicode) && ((encoding == kCFStringEncodingUnicode) || ((encoding > kCFStringEncodingUTF8) && (encoding <= kCFStringEncodingUTF32LE)))) { + guessedByteLength = (length + 1) * ((((encoding >> 26) & 2) == 0) ? sizeof(UTF16Char) : sizeof(UTF32Char)); // UTF32 format has the bit set + } else if (((guessedByteLength = CFStringGetMaximumSizeForEncoding(length, encoding)) > length) && !CF_IS_OBJC(__kCFStringTypeID, string)) { // Multi byte encoding +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD + if (__CFStrIsUnicode(string)) { + CFIndex aLength = CFStringEncodingByteLengthForCharacters(encoding, kCFStringEncodingPrependBOM, (const UniChar*)__CFStrContents(string), __CFStrLength(string)); + if (aLength > 0) guessedByteLength = aLength; + } else { +#endif + result = __CFStringEncodeByteStream(string, 0, length, true, encoding, lossByte, NULL, LONG_MAX, &guessedByteLength); + // if result == length, we always succeed + // otherwise, if result == 0, we fail + // otherwise, if there was a lossByte but still result != length, we fail + if ((result != length) && (!result || !lossByte)) return NULL; + if (guessedByteLength == length && __CFStrIsEightBit(string) && __CFStringEncodingIsSupersetOfASCII(encoding)) { // It's all ASCII !! + return CFDataCreate(alloc, ((uint8_t *)__CFStrContents(string) + __CFStrSkipAnyLengthByte(string)), __CFStrLength(string)); + } +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD + } +#endif + } + bytes = (uint8_t *)CFAllocatorAllocate(alloc, guessedByteLength, 0); + if (__CFOASafe) __CFSetLastAllocationEventName(bytes, "CFData (store)"); + + result = __CFStringEncodeByteStream(string, 0, length, true, encoding, lossByte, bytes, guessedByteLength, &usedLength); + + if ((result != length) && (!result || !lossByte)) { // see comment above about what this means + CFAllocatorDeallocate(alloc, bytes); + return NULL; + } + + return CFDataCreateWithBytesNoCopy(alloc, (uint8_t *)bytes, usedLength, alloc); +} + + +CFStringEncoding CFStringGetSmallestEncoding(CFStringRef str) { + CFIndex len; + CF_OBJC_FUNCDISPATCH0(__kCFStringTypeID, CFStringEncoding, str, "_smallestEncodingInCFStringEncoding"); + __CFAssertIsString(str); + + if (__CFStrIsEightBit(str)) return __CFStringGetEightBitStringEncoding(); + len = __CFStrLength(str); + if (__CFStringEncodeByteStream(str, 0, len, false, __CFStringGetEightBitStringEncoding(), 0, NULL, LONG_MAX, NULL) == len) return __CFStringGetEightBitStringEncoding(); + if ((__CFStringGetEightBitStringEncoding() != __CFStringGetSystemEncoding()) && (__CFStringEncodeByteStream(str, 0, len, false, __CFStringGetSystemEncoding(), 0, NULL, LONG_MAX, NULL) == len)) return __CFStringGetSystemEncoding(); + return kCFStringEncodingUnicode; /* ??? */ +} + + +CFStringEncoding CFStringGetFastestEncoding(CFStringRef str) { + CF_OBJC_FUNCDISPATCH0(__kCFStringTypeID, CFStringEncoding, str, "_fastestEncodingInCFStringEncoding"); + __CFAssertIsString(str); + return __CFStrIsEightBit(str) ? __CFStringGetEightBitStringEncoding() : kCFStringEncodingUnicode; /* ??? */ +} + + +SInt32 CFStringGetIntValue(CFStringRef str) { + Boolean success; + SInt32 result; + SInt32 idx = 0; + CFStringInlineBuffer buf; + CFStringInitInlineBuffer(str, &buf, CFRangeMake(0, CFStringGetLength(str))); + success = __CFStringScanInteger(&buf, NULL, &idx, false, &result); + return success ? result : 0; +} + + +double CFStringGetDoubleValue(CFStringRef str) { + Boolean success; + double result; + SInt32 idx = 0; + CFStringInlineBuffer buf; + CFStringInitInlineBuffer(str, &buf, CFRangeMake(0, CFStringGetLength(str))); + success = __CFStringScanDouble(&buf, NULL, &idx, &result); + return success ? result : 0.0; +} + + +/*** Mutable functions... ***/ + +void CFStringSetExternalCharactersNoCopy(CFMutableStringRef string, UniChar *chars, CFIndex length, CFIndex capacity) { + __CFAssertIsNotNegative(length); + __CFAssertIsStringAndExternalMutable(string); + CFAssert4((length <= capacity) && ((capacity == 0) || ((capacity > 0) && chars)), __kCFLogAssertion, "%s(): Invalid args: characters %p length %d capacity %d", __PRETTY_FUNCTION__, chars, length, capacity); + __CFStrSetContentPtr(string, chars); + __CFStrSetExplicitLength(string, length); + __CFStrSetCapacity(string, capacity * sizeof(UniChar)); + __CFStrSetCapacityProvidedExternally(string); +} + + + +void CFStringInsert(CFMutableStringRef str, CFIndex idx, CFStringRef insertedStr) { + CF_OBJC_FUNCDISPATCH2(__kCFStringTypeID, void, str, "insertString:atIndex:", insertedStr, idx); + __CFAssertIsStringAndMutable(str); + CFAssert3(idx >= 0 && idx <= __CFStrLength(str), __kCFLogAssertion, "%s(): string index %d out of bounds (length %d)", __PRETTY_FUNCTION__, idx, __CFStrLength(str)); + __CFStringReplace(str, CFRangeMake(idx, 0), insertedStr); +} + + +void CFStringDelete(CFMutableStringRef str, CFRange range) { + CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID, void, str, "deleteCharactersInRange:", range); + __CFAssertIsStringAndMutable(str); + __CFAssertRangeIsInStringBounds(str, range.location, range.length); + __CFStringChangeSize(str, range, 0, false); +} + + +void CFStringReplace(CFMutableStringRef str, CFRange range, CFStringRef replacement) { + CF_OBJC_FUNCDISPATCH2(__kCFStringTypeID, void, str, "replaceCharactersInRange:withString:", range, replacement); + __CFAssertIsStringAndMutable(str); + __CFAssertRangeIsInStringBounds(str, range.location, range.length); + __CFStringReplace(str, range, replacement); +} + + +void CFStringReplaceAll(CFMutableStringRef str, CFStringRef replacement) { + CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID, void, str, "setString:", replacement); + __CFAssertIsStringAndMutable(str); + __CFStringReplace(str, CFRangeMake(0, __CFStrLength(str)), replacement); +} + + +void CFStringAppend(CFMutableStringRef str, CFStringRef appended) { + CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID, void, str, "appendString:", appended); + __CFAssertIsStringAndMutable(str); + __CFStringReplace(str, CFRangeMake(__CFStrLength(str), 0), appended); +} + + +void CFStringAppendCharacters(CFMutableStringRef str, const UniChar *chars, CFIndex appendedLength) { + CFIndex strLength, idx; + + __CFAssertIsNotNegative(appendedLength); + + CF_OBJC_FUNCDISPATCH2(__kCFStringTypeID, void, str, "appendCharacters:length:", chars, appendedLength); + + __CFAssertIsStringAndMutable(str); + + strLength = __CFStrLength(str); + if (__CFStringGetCompatibility(Bug2967272) || __CFStrIsUnicode(str)) { + __CFStringChangeSize(str, CFRangeMake(strLength, 0), appendedLength, true); + memmove((UniChar *)__CFStrContents(str) + strLength, chars, appendedLength * sizeof(UniChar)); + } else { + uint8_t *contents; + bool isASCII = true; + for (idx = 0; isASCII && idx < appendedLength; idx++) isASCII = (chars[idx] < 0x80); + __CFStringChangeSize(str, CFRangeMake(strLength, 0), appendedLength, !isASCII); + if (!isASCII) { + memmove((UniChar *)__CFStrContents(str) + strLength, chars, appendedLength * sizeof(UniChar)); + } else { + contents = (uint8_t *)__CFStrContents(str) + strLength + __CFStrSkipAnyLengthByte(str); + for (idx = 0; idx < appendedLength; idx++) contents[idx] = (uint8_t)chars[idx]; + } + } +} + + +static void __CFStringAppendBytes(CFMutableStringRef str, const char *cStr, CFIndex appendedLength, CFStringEncoding encoding) { + Boolean appendedIsUnicode = false; + Boolean freeCStrWhenDone = false; + Boolean demoteAppendedUnicode = false; + CFVarWidthCharBuffer vBuf; + + __CFAssertIsNotNegative(appendedLength); + + if (encoding == kCFStringEncodingASCII || encoding == __CFStringGetEightBitStringEncoding()) { + // appendedLength now denotes length in UniChars + } else if (encoding == kCFStringEncodingUnicode) { + UniChar *chars = (UniChar *)cStr; + CFIndex idx, length = appendedLength / sizeof(UniChar); + bool isASCII = true; + for (idx = 0; isASCII && idx < length; idx++) isASCII = (chars[idx] < 0x80); + if (!isASCII) { + appendedIsUnicode = true; + } else { + demoteAppendedUnicode = true; + } + appendedLength = length; + } else { + Boolean usingPassedInMemory = false; + + vBuf.allocator = __CFGetDefaultAllocator(); // We don't want to use client's allocator for temp stuff + vBuf.chars.unicode = NULL; // This will cause the decode function to allocate memory if necessary + + if (!__CFStringDecodeByteStream3((const uint8_t *)cStr, appendedLength, encoding, __CFStrIsUnicode(str), &vBuf, &usingPassedInMemory, 0)) { + CFAssert1(0, __kCFLogAssertion, "Supplied bytes could not be converted specified encoding %d", encoding); + return; + } + + // If not ASCII, appendedLength now denotes length in UniChars + appendedLength = vBuf.numChars; + appendedIsUnicode = !vBuf.isASCII; + cStr = (const char *)vBuf.chars.ascii; + freeCStrWhenDone = !usingPassedInMemory && vBuf.shouldFreeChars; + } + + if (CF_IS_OBJC(__kCFStringTypeID, str)) { + if (!appendedIsUnicode && !demoteAppendedUnicode) { + CF_OBJC_FUNCDISPATCH2(__kCFStringTypeID, void, str, "_cfAppendCString:length:", cStr, appendedLength); + } else { + CF_OBJC_FUNCDISPATCH2(__kCFStringTypeID, void, str, "appendCharacters:length:", cStr, appendedLength); + } + } else { + CFIndex strLength; + __CFAssertIsStringAndMutable(str); + strLength = __CFStrLength(str); + + __CFStringChangeSize(str, CFRangeMake(strLength, 0), appendedLength, appendedIsUnicode || __CFStrIsUnicode(str)); + + if (__CFStrIsUnicode(str)) { + UniChar *contents = (UniChar *)__CFStrContents(str); + if (appendedIsUnicode) { + memmove(contents + strLength, cStr, appendedLength * sizeof(UniChar)); + } else { + __CFStrConvertBytesToUnicode((const uint8_t *)cStr, contents + strLength, appendedLength); + } + } else { + if (demoteAppendedUnicode) { + UniChar *chars = (UniChar *)cStr; + CFIndex idx; + uint8_t *contents = (uint8_t *)__CFStrContents(str) + strLength + __CFStrSkipAnyLengthByte(str); + for (idx = 0; idx < appendedLength; idx++) contents[idx] = (uint8_t)chars[idx]; + } else { + uint8_t *contents = (uint8_t *)__CFStrContents(str); + memmove(contents + strLength + __CFStrSkipAnyLengthByte(str), cStr, appendedLength); + } + } + } + + if (freeCStrWhenDone) CFAllocatorDeallocate(__CFGetDefaultAllocator(), (void *)cStr); +} + +void CFStringAppendPascalString(CFMutableStringRef str, ConstStringPtr pStr, CFStringEncoding encoding) { + __CFStringAppendBytes(str, (const char *)(pStr + 1), (CFIndex)*pStr, encoding); +} + +void CFStringAppendCString(CFMutableStringRef str, const char *cStr, CFStringEncoding encoding) { + __CFStringAppendBytes(str, cStr, (CFIndex)strlen(cStr), encoding); +} + + +void CFStringAppendFormat(CFMutableStringRef str, CFDictionaryRef formatOptions, CFStringRef format, ...) { + va_list argList; + + va_start(argList, format); + CFStringAppendFormatAndArguments(str, formatOptions, format, argList); + va_end(argList); +} + + +CFIndex CFStringFindAndReplace(CFMutableStringRef string, CFStringRef stringToFind, CFStringRef replacementString, CFRange rangeToSearch, CFOptionFlags compareOptions) { + CFRange foundRange; + Boolean backwards = ((compareOptions & kCFCompareBackwards) != 0); + UInt32 endIndex = rangeToSearch.location + rangeToSearch.length; +#define MAX_RANGES_ON_STACK (1000 / sizeof(CFRange)) + CFRange rangeBuffer[MAX_RANGES_ON_STACK]; // Used to avoid allocating memory + CFRange *ranges = rangeBuffer; + CFIndex foundCount = 0; + CFIndex capacity = MAX_RANGES_ON_STACK; + + __CFAssertIsStringAndMutable(string); + __CFAssertRangeIsInStringBounds(string, rangeToSearch.location, rangeToSearch.length); + + // Note: This code is very similar to the one in CFStringCreateArrayWithFindResults(). + while ((rangeToSearch.length > 0) && CFStringFindWithOptions(string, stringToFind, rangeToSearch, compareOptions, &foundRange)) { + // Determine the next range + if (backwards) { + rangeToSearch.length = foundRange.location - rangeToSearch.location; + } else { + rangeToSearch.location = foundRange.location + foundRange.length; + rangeToSearch.length = endIndex - rangeToSearch.location; + } + + // If necessary, grow the array + if (foundCount >= capacity) { + bool firstAlloc = (ranges == rangeBuffer) ? true : false; + capacity = (capacity + 4) * 2; + // Note that reallocate with NULL previous pointer is same as allocate + ranges = (CFRange *)CFAllocatorReallocate(kCFAllocatorSystemDefault, firstAlloc ? NULL : ranges, capacity * sizeof(CFRange), 0); + if (firstAlloc) memmove(ranges, rangeBuffer, MAX_RANGES_ON_STACK * sizeof(CFRange)); + } + ranges[foundCount] = foundRange; + foundCount++; + } + + if (foundCount > 0) { + if (backwards) { // Reorder the ranges to be incrementing (better to do this here, then to check other places) + int head = 0; + int tail = foundCount - 1; + while (head < tail) { + CFRange temp = ranges[head]; + ranges[head] = ranges[tail]; + ranges[tail] = temp; + head++; + tail--; + } + } + __CFStringReplaceMultiple(string, ranges, foundCount, replacementString); + if (ranges != rangeBuffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, ranges); + } + + return foundCount; +} + + +// This function is here for NSString purposes +// It allows checking for mutability before mutating; this allows NSString to catch invalid mutations + +int __CFStringCheckAndReplace(CFMutableStringRef str, CFRange range, CFStringRef replacement) { + if (!__CFStrIsMutable(str)) return _CFStringErrNotMutable; // These three ifs are always here, for NSString usage + if (!replacement && __CFStringNoteErrors()) return _CFStringErrNilArg; + // We use unsigneds as that is what NSRanges do; we use uint64_t do make sure the sum doesn't wrap (otherwise we'd need to do 3 separate checks). This allows catching bad ranges as described in 3375535. (-1,1) + if (((uint64_t)((unsigned)range.location)) + ((uint64_t)((unsigned)range.length)) > (uint64_t)__CFStrLength(str) && __CFStringNoteErrors()) return _CFStringErrBounds; + __CFAssertIsStringAndMutable(str); + __CFAssertRangeIsInStringBounds(str, range.location, range.length); + __CFStringReplace(str, range, replacement); + return _CFStringErrNone; +} + +// This function determines whether errors which would cause string exceptions should +// be ignored or not + +Boolean __CFStringNoteErrors(void) { + return true; +} + + + +void CFStringPad(CFMutableStringRef string, CFStringRef padString, CFIndex length, CFIndex indexIntoPad) { + CFIndex originalLength; + + __CFAssertIsNotNegative(length); + __CFAssertIsNotNegative(indexIntoPad); + + CF_OBJC_FUNCDISPATCH3(__kCFStringTypeID, void, string, "_cfPad:length:padIndex:", padString, length, indexIntoPad); + + __CFAssertIsStringAndMutable(string); + + originalLength = __CFStrLength(string); + if (length < originalLength) { + __CFStringChangeSize(string, CFRangeMake(length, originalLength - length), 0, false); + } else if (originalLength < length) { + uint8_t *contents; + Boolean isUnicode; + CFIndex charSize; + CFIndex padStringLength; + CFIndex padLength; + CFIndex padRemaining = length - originalLength; + + if (CF_IS_OBJC(__kCFStringTypeID, padString)) { /* ??? Hope the compiler optimizes this away if OBJC_MAPPINGS is not on */ + padStringLength = CFStringGetLength(padString); + isUnicode = true; /* !!! Bad for now */ + } else { + __CFAssertIsString(padString); + padStringLength = __CFStrLength(padString); + isUnicode = __CFStrIsUnicode(string) || __CFStrIsUnicode(padString); + } + + charSize = isUnicode ? sizeof(UniChar) : sizeof(uint8_t); + + __CFStringChangeSize(string, CFRangeMake(originalLength, 0), padRemaining, isUnicode); + + contents = (uint8_t *)__CFStrContents(string) + charSize * originalLength + __CFStrSkipAnyLengthByte(string); + padLength = padStringLength - indexIntoPad; + padLength = padRemaining < padLength ? padRemaining : padLength; + + while (padRemaining > 0) { + if (isUnicode) { + CFStringGetCharacters(padString, CFRangeMake(indexIntoPad, padLength), (UniChar *)contents); + } else { + CFStringGetBytes(padString, CFRangeMake(indexIntoPad, padLength), __CFStringGetEightBitStringEncoding(), 0, false, contents, padRemaining * charSize, NULL); + } + contents += padLength * charSize; + padRemaining -= padLength; + indexIntoPad = 0; + padLength = padRemaining < padLength ? padRemaining : padStringLength; + } + } +} + +void CFStringTrim(CFMutableStringRef string, CFStringRef trimString) { + CFRange range; + CFIndex newStartIndex; + CFIndex length; + + CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID, void, string, "_cfTrim:", trimString); + + __CFAssertIsStringAndMutable(string); + __CFAssertIsString(trimString); + + newStartIndex = 0; + length = __CFStrLength(string); + + while (CFStringFindWithOptions(string, trimString, CFRangeMake(newStartIndex, length - newStartIndex), kCFCompareAnchored, &range)) { + newStartIndex = range.location + range.length; + } + + if (newStartIndex < length) { + CFIndex charSize = __CFStrIsUnicode(string) ? sizeof(UniChar) : sizeof(uint8_t); + uint8_t *contents = (uint8_t *)__CFStrContents(string) + __CFStrSkipAnyLengthByte(string); + + length -= newStartIndex; + if (__CFStrLength(trimString) < length) { + while (CFStringFindWithOptions(string, trimString, CFRangeMake(newStartIndex, length), kCFCompareAnchored|kCFCompareBackwards, &range)) { + length = range.location - newStartIndex; + } + } + memmove(contents, contents + newStartIndex * charSize, length * charSize); + __CFStringChangeSize(string, CFRangeMake(length, __CFStrLength(string) - length), 0, false); + } else { // Only trimString in string, trim all + __CFStringChangeSize(string, CFRangeMake(0, length), 0, false); + } +} + +void CFStringTrimWhitespace(CFMutableStringRef string) { + CFIndex newStartIndex; + CFIndex length; + CFStringInlineBuffer buffer; + + CF_OBJC_FUNCDISPATCH0(__kCFStringTypeID, void, string, "_cfTrimWS"); + + __CFAssertIsStringAndMutable(string); + + newStartIndex = 0; + length = __CFStrLength(string); + + CFStringInitInlineBuffer(string, &buffer, CFRangeMake(0, length)); + CFIndex buffer_idx = 0; + + while (buffer_idx < length && CFUniCharIsMemberOf(__CFStringGetCharacterFromInlineBufferQuick(&buffer, buffer_idx), kCFUniCharWhitespaceAndNewlineCharacterSet)) + buffer_idx++; + newStartIndex = buffer_idx; + + if (newStartIndex < length) { + uint8_t *contents = (uint8_t *)__CFStrContents(string) + __CFStrSkipAnyLengthByte(string); + CFIndex charSize = (__CFStrIsUnicode(string) ? sizeof(UniChar) : sizeof(uint8_t)); + + buffer_idx = length - 1; + while (0 <= buffer_idx && CFUniCharIsMemberOf(__CFStringGetCharacterFromInlineBufferQuick(&buffer, buffer_idx), kCFUniCharWhitespaceAndNewlineCharacterSet)) + buffer_idx--; + length = buffer_idx - newStartIndex + 1; + + memmove(contents, contents + newStartIndex * charSize, length * charSize); + __CFStringChangeSize(string, CFRangeMake(length, __CFStrLength(string) - length), 0, false); + } else { // Whitespace only string + __CFStringChangeSize(string, CFRangeMake(0, length), 0, false); + } +} + +void CFStringLowercase(CFMutableStringRef string, CFLocaleRef locale) { + CFIndex currentIndex = 0; + CFIndex length; + const uint8_t *langCode; + Boolean isEightBit = __CFStrIsEightBit(string); + + CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID, void, string, "_cfLowercase:", locale); + + __CFAssertIsStringAndMutable(string); + + length = __CFStrLength(string); + + langCode = (const uint8_t *)(_CFCanUseLocale(locale) ? _CFStrGetLanguageIdentifierForLocale(locale) : NULL); + + if (!langCode && isEightBit) { + uint8_t *contents = (uint8_t *)__CFStrContents(string) + __CFStrSkipAnyLengthByte(string); + for (;currentIndex < length;currentIndex++) { + if (contents[currentIndex] >= 'A' && contents[currentIndex] <= 'Z') { + contents[currentIndex] += 'a' - 'A'; + } else if (contents[currentIndex] > 127) { + break; + } + } + } + + if (currentIndex < length) { + UTF16Char *contents; + UniChar mappedCharacters[MAX_CASE_MAPPING_BUF]; + CFIndex mappedLength; + UTF32Char currentChar; + UInt32 flags = 0; + + if (isEightBit) __CFStringChangeSize(string, CFRangeMake(0, 0), 0, true); + + contents = (UniChar *)__CFStrContents(string); + + for (;currentIndex < length;currentIndex++) { + + if (CFUniCharIsSurrogateHighCharacter(contents[currentIndex]) && (currentIndex + 1 < length) && CFUniCharIsSurrogateLowCharacter(contents[currentIndex + 1])) { + currentChar = CFUniCharGetLongCharacterForSurrogatePair(contents[currentIndex], contents[currentIndex + 1]); + } else { + currentChar = contents[currentIndex]; + } + flags = ((langCode || (currentChar == 0x03A3)) ? CFUniCharGetConditionalCaseMappingFlags(currentChar, contents, currentIndex, length, kCFUniCharToLowercase, langCode, flags) : 0); + + mappedLength = CFUniCharMapCaseTo(currentChar, mappedCharacters, MAX_CASE_MAPPING_BUF, kCFUniCharToLowercase, flags, langCode); + if (mappedLength > 0) contents[currentIndex] = *mappedCharacters; + + if (currentChar > 0xFFFF) { // Non-BMP char + switch (mappedLength) { + case 0: + __CFStringChangeSize(string, CFRangeMake(currentIndex, 2), 0, true); + contents = (UniChar *)__CFStrContents(string); + length -= 2; + break; + + case 1: + __CFStringChangeSize(string, CFRangeMake(currentIndex + 1, 1), 0, true); + contents = (UniChar *)__CFStrContents(string); + --length; + break; + + case 2: + contents[++currentIndex] = mappedCharacters[1]; + break; + + default: + --mappedLength; // Skip the current char + __CFStringChangeSize(string, CFRangeMake(currentIndex + 1, 0), mappedLength - 1, true); + contents = (UniChar *)__CFStrContents(string); + memmove(contents + currentIndex + 1, mappedCharacters + 1, mappedLength * sizeof(UniChar)); + length += (mappedLength - 1); + currentIndex += mappedLength; + break; + } + } else if (mappedLength == 0) { + __CFStringChangeSize(string, CFRangeMake(currentIndex, 1), 0, true); + contents = (UniChar *)__CFStrContents(string); + --length; + } else if (mappedLength > 1) { + --mappedLength; // Skip the current char + __CFStringChangeSize(string, CFRangeMake(currentIndex + 1, 0), mappedLength, true); + contents = (UniChar *)__CFStrContents(string); + memmove(contents + currentIndex + 1, mappedCharacters + 1, mappedLength * sizeof(UniChar)); + length += mappedLength; + currentIndex += mappedLength; + } + } + } +} + +void CFStringUppercase(CFMutableStringRef string, CFLocaleRef locale) { + CFIndex currentIndex = 0; + CFIndex length; + const uint8_t *langCode; + Boolean isEightBit = __CFStrIsEightBit(string); + + CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID, void, string, "_cfUppercase:", locale); + + __CFAssertIsStringAndMutable(string); + + length = __CFStrLength(string); + + langCode = (const uint8_t *)(_CFCanUseLocale(locale) ? _CFStrGetLanguageIdentifierForLocale(locale) : NULL); + + if (!langCode && isEightBit) { + uint8_t *contents = (uint8_t *)__CFStrContents(string) + __CFStrSkipAnyLengthByte(string); + for (;currentIndex < length;currentIndex++) { + if (contents[currentIndex] >= 'a' && contents[currentIndex] <= 'z') { + contents[currentIndex] -= 'a' - 'A'; + } else if (contents[currentIndex] > 127) { + break; + } + } + } + + if (currentIndex < length) { + UniChar *contents; + UniChar mappedCharacters[MAX_CASE_MAPPING_BUF]; + CFIndex mappedLength; + UTF32Char currentChar; + UInt32 flags = 0; + + if (isEightBit) __CFStringChangeSize(string, CFRangeMake(0, 0), 0, true); + + contents = (UniChar *)__CFStrContents(string); + + for (;currentIndex < length;currentIndex++) { + if (CFUniCharIsSurrogateHighCharacter(contents[currentIndex]) && (currentIndex + 1 < length) && CFUniCharIsSurrogateLowCharacter(contents[currentIndex + 1])) { + currentChar = CFUniCharGetLongCharacterForSurrogatePair(contents[currentIndex], contents[currentIndex + 1]); + } else { + currentChar = contents[currentIndex]; + } + + flags = (langCode ? CFUniCharGetConditionalCaseMappingFlags(currentChar, contents, currentIndex, length, kCFUniCharToUppercase, langCode, flags) : 0); + + mappedLength = CFUniCharMapCaseTo(currentChar, mappedCharacters, MAX_CASE_MAPPING_BUF, kCFUniCharToUppercase, flags, langCode); + if (mappedLength > 0) contents[currentIndex] = *mappedCharacters; + + if (currentChar > 0xFFFF) { // Non-BMP char + switch (mappedLength) { + case 0: + __CFStringChangeSize(string, CFRangeMake(currentIndex, 2), 0, true); + contents = (UniChar *)__CFStrContents(string); + length -= 2; + break; + + case 1: + __CFStringChangeSize(string, CFRangeMake(currentIndex + 1, 1), 0, true); + contents = (UniChar *)__CFStrContents(string); + --length; + break; + + case 2: + contents[++currentIndex] = mappedCharacters[1]; + break; + + default: + --mappedLength; // Skip the current char + __CFStringChangeSize(string, CFRangeMake(currentIndex + 1, 0), mappedLength - 1, true); + contents = (UniChar *)__CFStrContents(string); + memmove(contents + currentIndex + 1, mappedCharacters + 1, mappedLength * sizeof(UniChar)); + length += (mappedLength - 1); + currentIndex += mappedLength; + break; + } + } else if (mappedLength == 0) { + __CFStringChangeSize(string, CFRangeMake(currentIndex, 1), 0, true); + contents = (UniChar *)__CFStrContents(string); + --length; + } else if (mappedLength > 1) { + --mappedLength; // Skip the current char + __CFStringChangeSize(string, CFRangeMake(currentIndex + 1, 0), mappedLength, true); + contents = (UniChar *)__CFStrContents(string); + memmove(contents + currentIndex + 1, mappedCharacters + 1, mappedLength * sizeof(UniChar)); + length += mappedLength; + currentIndex += mappedLength; + } + } + } +} + + +void CFStringCapitalize(CFMutableStringRef string, CFLocaleRef locale) { + CFIndex currentIndex = 0; + CFIndex length; + const uint8_t *langCode; + Boolean isEightBit = __CFStrIsEightBit(string); + Boolean isLastCased = false; + const uint8_t *caseIgnorableForBMP; + + CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID, void, string, "_cfCapitalize:", locale); + + __CFAssertIsStringAndMutable(string); + + length = __CFStrLength(string); + + caseIgnorableForBMP = CFUniCharGetBitmapPtrForPlane(kCFUniCharCaseIgnorableCharacterSet, 0); + + langCode = (const uint8_t *)(_CFCanUseLocale(locale) ? _CFStrGetLanguageIdentifierForLocale(locale) : NULL); + + if (!langCode && isEightBit) { + uint8_t *contents = (uint8_t *)__CFStrContents(string) + __CFStrSkipAnyLengthByte(string); + for (;currentIndex < length;currentIndex++) { + if (contents[currentIndex] > 127) { + break; + } else if (contents[currentIndex] >= 'A' && contents[currentIndex] <= 'Z') { + contents[currentIndex] += (isLastCased ? 'a' - 'A' : 0); + isLastCased = true; + } else if (contents[currentIndex] >= 'a' && contents[currentIndex] <= 'z') { + contents[currentIndex] -= (!isLastCased ? 'a' - 'A' : 0); + isLastCased = true; + } else if (!CFUniCharIsMemberOfBitmap(contents[currentIndex], caseIgnorableForBMP)) { + isLastCased = false; + } + } + } + + if (currentIndex < length) { + UniChar *contents; + UniChar mappedCharacters[MAX_CASE_MAPPING_BUF]; + CFIndex mappedLength; + UTF32Char currentChar; + UInt32 flags = 0; + + if (isEightBit) __CFStringChangeSize(string, CFRangeMake(0, 0), 0, true); + + contents = (UniChar *)__CFStrContents(string); + + for (;currentIndex < length;currentIndex++) { + if (CFUniCharIsSurrogateHighCharacter(contents[currentIndex]) && (currentIndex + 1 < length) && CFUniCharIsSurrogateLowCharacter(contents[currentIndex + 1])) { + currentChar = CFUniCharGetLongCharacterForSurrogatePair(contents[currentIndex], contents[currentIndex + 1]); + } else { + currentChar = contents[currentIndex]; + } + flags = ((langCode || ((currentChar == 0x03A3) && isLastCased)) ? CFUniCharGetConditionalCaseMappingFlags(currentChar, contents, currentIndex, length, (isLastCased ? kCFUniCharToLowercase : kCFUniCharToTitlecase), langCode, flags) : 0); + + mappedLength = CFUniCharMapCaseTo(currentChar, mappedCharacters, MAX_CASE_MAPPING_BUF, (isLastCased ? kCFUniCharToLowercase : kCFUniCharToTitlecase), flags, langCode); + if (mappedLength > 0) contents[currentIndex] = *mappedCharacters; + + if (currentChar > 0xFFFF) { // Non-BMP char + switch (mappedLength) { + case 0: + __CFStringChangeSize(string, CFRangeMake(currentIndex, 2), 0, true); + contents = (UniChar *)__CFStrContents(string); + length -= 2; + break; + + case 1: + __CFStringChangeSize(string, CFRangeMake(currentIndex + 1, 1), 0, true); + contents = (UniChar *)__CFStrContents(string); + --length; + break; + + case 2: + contents[++currentIndex] = mappedCharacters[1]; + break; + + default: + --mappedLength; // Skip the current char + __CFStringChangeSize(string, CFRangeMake(currentIndex + 1, 0), mappedLength - 1, true); + contents = (UniChar *)__CFStrContents(string); + memmove(contents + currentIndex + 1, mappedCharacters + 1, mappedLength * sizeof(UniChar)); + length += (mappedLength - 1); + currentIndex += mappedLength; + break; + } + } else if (mappedLength == 0) { + __CFStringChangeSize(string, CFRangeMake(currentIndex, 1), 0, true); + contents = (UniChar *)__CFStrContents(string); + --length; + } else if (mappedLength > 1) { + --mappedLength; // Skip the current char + __CFStringChangeSize(string, CFRangeMake(currentIndex + 1, 0), mappedLength, true); + contents = (UniChar *)__CFStrContents(string); + memmove(contents + currentIndex + 1, mappedCharacters + 1, mappedLength * sizeof(UniChar)); + length += mappedLength; + currentIndex += mappedLength; + } + + if (!((currentChar > 0xFFFF) ? CFUniCharIsMemberOf(currentChar, kCFUniCharCaseIgnorableCharacterSet) : CFUniCharIsMemberOfBitmap(currentChar, caseIgnorableForBMP))) { // We have non-caseignorable here + isLastCased = ((CFUniCharIsMemberOf(currentChar, kCFUniCharUppercaseLetterCharacterSet) || CFUniCharIsMemberOf(currentChar, kCFUniCharLowercaseLetterCharacterSet)) ? true : false); + } + } + } +} + + +#define MAX_DECOMP_BUF 64 + +#define HANGUL_SBASE 0xAC00 +#define HANGUL_LBASE 0x1100 +#define HANGUL_VBASE 0x1161 +#define HANGUL_TBASE 0x11A7 +#define HANGUL_SCOUNT 11172 +#define HANGUL_LCOUNT 19 +#define HANGUL_VCOUNT 21 +#define HANGUL_TCOUNT 28 +#define HANGUL_NCOUNT (HANGUL_VCOUNT * HANGUL_TCOUNT) + +CF_INLINE uint32_t __CFGetUTF16Length(const UTF32Char *characters, uint32_t utf32Length) { + const UTF32Char *limit = characters + utf32Length; + uint32_t length = 0; + + while (characters < limit) length += (*(characters++) > 0xFFFF ? 2 : 1); + + return length; +} + +CF_INLINE void __CFFillInUTF16(const UTF32Char *characters, UTF16Char *dst, uint32_t utf32Length) { + const UTF32Char *limit = characters + utf32Length; + UTF32Char currentChar; + + while (characters < limit) { + currentChar = *(characters++); + if (currentChar > 0xFFFF) { + currentChar -= 0x10000; + *(dst++) = (UTF16Char)((currentChar >> 10) + 0xD800UL); + *(dst++) = (UTF16Char)((currentChar & 0x3FF) + 0xDC00UL); + } else { + *(dst++) = currentChar; + } + } +} + +void CFStringNormalize(CFMutableStringRef string, CFStringNormalizationForm theForm) { + CFIndex currentIndex = 0; + CFIndex length; + bool needToReorder = true; + + CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID, void, string, "_cfNormalize:", theForm); + + __CFAssertIsStringAndMutable(string); + + length = __CFStrLength(string); + + if (__CFStrIsEightBit(string)) { + uint8_t *contents; + + if (theForm == kCFStringNormalizationFormC) return; // 8bit form has no decomposition + + contents = (uint8_t *)__CFStrContents(string) + __CFStrSkipAnyLengthByte(string); + + for (;currentIndex < length;currentIndex++) { + if (contents[currentIndex] > 127) { + __CFStringChangeSize(string, CFRangeMake(0, 0), 0, true); // need to do harm way + needToReorder = false; + break; + } + } + } + + if (currentIndex < length) { + UTF16Char *limit = (UTF16Char *)__CFStrContents(string) + length; + UTF16Char *contents = (UTF16Char *)__CFStrContents(string) + currentIndex; + UTF32Char buffer[MAX_DECOMP_BUF]; + UTF32Char *mappedCharacters = buffer; + CFIndex allocatedLength = MAX_DECOMP_BUF; + CFIndex mappedLength; + CFIndex currentLength; + UTF32Char currentChar; + const uint8_t *decompBMP = CFUniCharGetBitmapPtrForPlane(kCFUniCharCanonicalDecomposableCharacterSet, 0); + const uint8_t *nonBaseBMP = CFUniCharGetBitmapPtrForPlane(kCFUniCharNonBaseCharacterSet, 0); + const uint8_t *combiningBMP = (const uint8_t *)CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty, 0); + + while (contents < limit) { + if (CFUniCharIsSurrogateHighCharacter(*contents) && (contents + 1 < limit) && CFUniCharIsSurrogateLowCharacter(*(contents + 1))) { + currentChar = CFUniCharGetLongCharacterForSurrogatePair(*contents, *(contents + 1)); + currentLength = 2; + contents += 2; + } else { + currentChar = *(contents++); + currentLength = 1; + } + + mappedLength = 0; + + if (CFUniCharIsMemberOfBitmap(currentChar, ((currentChar < 0x10000) ? decompBMP : CFUniCharGetBitmapPtrForPlane(kCFUniCharCanonicalDecomposableCharacterSet, (currentChar >> 16)))) && (0 == CFUniCharGetCombiningPropertyForCharacter(currentChar, ((currentChar < 0x10000) ? combiningBMP : (const uint8_t *)CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty, (currentChar >> 16)))))) { + if ((theForm & kCFStringNormalizationFormC) == 0 || currentChar < HANGUL_SBASE || currentChar > (HANGUL_SBASE + HANGUL_SCOUNT)) { // We don't have to decompose Hangul Syllables if we're precomposing again + mappedLength = CFUniCharDecomposeCharacter(currentChar, mappedCharacters, MAX_DECOMP_BUF); + } + } + + if ((needToReorder || (theForm & kCFStringNormalizationFormC)) && ((contents < limit) || (mappedLength == 0))) { + if (mappedLength > 0) { + if (CFUniCharIsSurrogateHighCharacter(*contents) && (contents + 1 < limit) && CFUniCharIsSurrogateLowCharacter(*(contents + 1))) { + currentChar = CFUniCharGetLongCharacterForSurrogatePair(*contents, *(contents + 1)); + } else { + currentChar = *contents; + } + } + + if (0 != CFUniCharGetCombiningPropertyForCharacter(currentChar, (const uint8_t *)((currentChar < 0x10000) ? combiningBMP : CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty, (currentChar >> 16))))) { + uint32_t decompLength; + + if (mappedLength == 0) { + contents -= (currentChar & 0xFFFF0000 ? 2 : 1); + if (currentIndex > 0) { + if (CFUniCharIsSurrogateLowCharacter(*(contents - 1)) && (currentIndex > 1) && CFUniCharIsSurrogateHighCharacter(*(contents - 2))) { + *mappedCharacters = CFUniCharGetLongCharacterForSurrogatePair(*(contents - 2), *(contents - 1)); + currentIndex -= 2; + currentLength += 2; + } else { + *mappedCharacters = *(contents - 1); + --currentIndex; + ++currentLength; + } + mappedLength = 1; + } + } else { + currentLength += (currentChar & 0xFFFF0000 ? 2 : 1); + } + contents += (currentChar & 0xFFFF0000 ? 2 : 1); + + if (CFUniCharIsMemberOfBitmap(currentChar, ((currentChar < 0x10000) ? decompBMP : CFUniCharGetBitmapPtrForPlane(kCFUniCharCanonicalDecomposableCharacterSet, (currentChar >> 16))))) { // Vietnamese accent, etc. + decompLength = CFUniCharDecomposeCharacter(currentChar, mappedCharacters + mappedLength, MAX_DECOMP_BUF - mappedLength); + mappedLength += decompLength; + } else { + mappedCharacters[mappedLength++] = currentChar; + } + + while (contents < limit) { + if (CFUniCharIsSurrogateHighCharacter(*contents) && (contents + 1 < limit) && CFUniCharIsSurrogateLowCharacter(*(contents + 1))) { + currentChar = CFUniCharGetLongCharacterForSurrogatePair(*contents, *(contents + 1)); + } else { + currentChar = *contents; + } + if (0 == CFUniCharGetCombiningPropertyForCharacter(currentChar, (const uint8_t *)((currentChar < 0x10000) ? combiningBMP : CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty, (currentChar >> 16))))) break; + if (currentChar & 0xFFFF0000) { + contents += 2; + currentLength += 2; + } else { + ++contents; + ++currentLength; + } + if (mappedLength == allocatedLength) { + allocatedLength += MAX_DECOMP_BUF; + if (mappedCharacters == buffer) { + mappedCharacters = (UTF32Char *)CFAllocatorAllocate(kCFAllocatorSystemDefault, allocatedLength * sizeof(UTF32Char), 0); + memmove(mappedCharacters, buffer, MAX_DECOMP_BUF * sizeof(UTF32Char)); + } else { + mappedCharacters = (UTF32Char *)CFAllocatorReallocate(kCFAllocatorSystemDefault, mappedCharacters, allocatedLength * sizeof(UTF32Char), 0); + } + } + if (CFUniCharIsMemberOfBitmap(currentChar, ((currentChar < 0x10000) ? decompBMP : CFUniCharGetBitmapPtrForPlane(kCFUniCharCanonicalDecomposableCharacterSet, (currentChar >> 16))))) { // Vietnamese accent, etc. + decompLength = CFUniCharDecomposeCharacter(currentChar, mappedCharacters + mappedLength, MAX_DECOMP_BUF - mappedLength); + mappedLength += decompLength; + } else { + mappedCharacters[mappedLength++] = currentChar; + } + } + } + if (needToReorder && mappedLength > 1) CFUniCharPrioritySort(mappedCharacters, mappedLength); + } + + if (theForm & kCFStringNormalizationFormKD) { + CFIndex newLength = 0; + + if (mappedLength == 0 && CFUniCharIsMemberOf(currentChar, kCFUniCharCompatibilityDecomposableCharacterSet)) { + mappedCharacters[mappedLength++] = currentChar; + } + while (newLength < mappedLength) { + newLength = CFUniCharCompatibilityDecompose(mappedCharacters, mappedLength, allocatedLength); + if (newLength == 0) { + allocatedLength += MAX_DECOMP_BUF; + if (mappedCharacters == buffer) { + mappedCharacters = (UTF32Char *)CFAllocatorAllocate(kCFAllocatorSystemDefault, allocatedLength * sizeof(UTF32Char), 0); + memmove(mappedCharacters, buffer, MAX_DECOMP_BUF * sizeof(UTF32Char)); + } else { + mappedCharacters = (UTF32Char *)CFAllocatorReallocate(kCFAllocatorSystemDefault, mappedCharacters, allocatedLength * sizeof(UTF32Char), 0); + } + } + } + mappedLength = newLength; + } + + if (theForm & kCFStringNormalizationFormC) { + UTF32Char nextChar; + + if (mappedLength > 1) { + CFIndex consumedLength = 1; + UTF32Char *currentBase = mappedCharacters; + uint8_t currentClass, lastClass = 0; + bool didCombine = false; + + currentChar = *mappedCharacters; + + while (consumedLength < mappedLength) { + nextChar = mappedCharacters[consumedLength]; + currentClass = CFUniCharGetCombiningPropertyForCharacter(nextChar, (const uint8_t *)((nextChar < 0x10000) ? combiningBMP : CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty, (nextChar >> 16)))); + + if (theForm & kCFStringNormalizationFormKD) { + if ((currentChar >= HANGUL_LBASE) && (currentChar < (HANGUL_LBASE + 0xFF))) { + SInt8 lIndex = currentChar - HANGUL_LBASE; + + if ((0 <= lIndex) && (lIndex <= HANGUL_LCOUNT)) { + SInt16 vIndex = nextChar - HANGUL_VBASE; + + if ((vIndex >= 0) && (vIndex <= HANGUL_VCOUNT)) { + SInt16 tIndex = 0; + CFIndex usedLength = mappedLength; + + mappedCharacters[consumedLength++] = 0xFFFD; + + if (consumedLength < mappedLength) { + tIndex = mappedCharacters[consumedLength] - HANGUL_TBASE; + if ((tIndex < 0) || (tIndex > HANGUL_TCOUNT)) { + tIndex = 0; + } else { + mappedCharacters[consumedLength++] = 0xFFFD; + } + } + *currentBase = (lIndex * HANGUL_VCOUNT + vIndex) * HANGUL_TCOUNT + tIndex + HANGUL_SBASE; + + while (--usedLength > 0) { + if (mappedCharacters[usedLength] == 0xFFFD) { + --mappedLength; + --consumedLength; + memmove(mappedCharacters + usedLength, mappedCharacters + usedLength + 1, (mappedLength - usedLength) * sizeof(UTF32Char)); + } + } + currentBase = mappedCharacters + consumedLength; + currentChar = *currentBase; + ++consumedLength; + + continue; + } + } + } + if (!CFUniCharIsMemberOfBitmap(nextChar, ((nextChar < 0x10000) ? nonBaseBMP : CFUniCharGetBitmapPtrForPlane(kCFUniCharNonBaseCharacterSet, (nextChar >> 16))))) { + *currentBase = currentChar; + currentBase = mappedCharacters + consumedLength; + currentChar = nextChar; + ++consumedLength; + continue; + } + } + + if ((lastClass == 0) || (currentClass > lastClass)) { + nextChar = CFUniCharPrecomposeCharacter(currentChar, nextChar); + if (nextChar == 0xFFFD) { + lastClass = currentClass; + } else { + mappedCharacters[consumedLength] = 0xFFFD; + didCombine = true; + currentChar = nextChar; + } + } + ++consumedLength; + } + + *currentBase = currentChar; + if (didCombine) { + consumedLength = mappedLength; + while (--consumedLength > 0) { + if (mappedCharacters[consumedLength] == 0xFFFD) { + --mappedLength; + memmove(mappedCharacters + consumedLength, mappedCharacters + consumedLength + 1, (mappedLength - consumedLength) * sizeof(UTF32Char)); + } + } + } + } else if ((currentChar >= HANGUL_LBASE) && (currentChar < (HANGUL_LBASE + 0xFF))) { // Hangul Jamo + SInt8 lIndex = currentChar - HANGUL_LBASE; + + if ((contents < limit) && (0 <= lIndex) && (lIndex <= HANGUL_LCOUNT)) { + SInt16 vIndex = *contents - HANGUL_VBASE; + + if ((vIndex >= 0) && (vIndex <= HANGUL_VCOUNT)) { + SInt16 tIndex = 0; + + ++contents; ++currentLength; + + if (contents < limit) { + tIndex = *contents - HANGUL_TBASE; + if ((tIndex < 0) || (tIndex > HANGUL_TCOUNT)) { + tIndex = 0; + } else { + ++contents; ++currentLength; + } + } + *mappedCharacters = (lIndex * HANGUL_VCOUNT + vIndex) * HANGUL_TCOUNT + tIndex + HANGUL_SBASE; + mappedLength = 1; + } + } + } else { // collect class 0 non-base characters + while (contents < limit) { + nextChar = *contents; + if (CFUniCharIsSurrogateHighCharacter(nextChar) && ((contents + 1) < limit) && CFUniCharIsSurrogateLowCharacter(*(contents + 1))) { + nextChar = CFUniCharGetLongCharacterForSurrogatePair(nextChar, *(contents + 1)); + if (!CFUniCharIsMemberOfBitmap(nextChar, (const uint8_t *)CFUniCharGetBitmapPtrForPlane(kCFUniCharNonBaseCharacterSet, (nextChar >> 16))) || (0 != CFUniCharGetCombiningPropertyForCharacter(nextChar, (const uint8_t *)CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty, (nextChar >> 16))))) break; + } else { + if (!CFUniCharIsMemberOfBitmap(nextChar, nonBaseBMP) || (0 != CFUniCharGetCombiningPropertyForCharacter(nextChar, combiningBMP))) break; + } + currentChar = CFUniCharPrecomposeCharacter(currentChar, nextChar); + if (0xFFFD == currentChar) break; + + if (nextChar < 0x10000) { + ++contents; ++currentLength; + } else { + contents += 2; + currentLength += 2; + } + + *mappedCharacters = currentChar; + mappedLength = 1; + } + } + } + + if (mappedLength > 0) { + CFIndex utf16Length = __CFGetUTF16Length(mappedCharacters, mappedLength); + + if (utf16Length != currentLength) { + __CFStringChangeSize(string, CFRangeMake(currentIndex, currentLength), utf16Length, true); + currentLength = utf16Length; + } + contents = (UTF16Char *)__CFStrContents(string); + limit = contents + __CFStrLength(string); + contents += currentIndex; + __CFFillInUTF16(mappedCharacters, contents, mappedLength); + contents += utf16Length; + } + currentIndex += currentLength; + } + + if (mappedCharacters != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, mappedCharacters); + } +} + +void CFStringFold(CFMutableStringRef theString, CFStringCompareFlags theFlags, CFLocaleRef locale) { + CFStringInlineBuffer stringBuffer; + CFIndex length = CFStringGetLength(theString); + CFIndex currentIndex = 0; + CFIndex bufferLength = 0; + UTF32Char buffer[kCFStringStackBufferLength]; + const uint8_t *cString; + const uint8_t *langCode; + CFStringEncoding eightBitEncoding; + bool caseInsensitive = ((theFlags & kCFCompareCaseInsensitive) ? true : false); + bool isObjc = CF_IS_OBJC(__kCFStringTypeID, theString); + CFLocaleRef theLocale = locale; + + if ((theFlags & kCFCompareLocalized) && (NULL == locale)) { + theLocale = CFLocaleCopyCurrent(); + } + + theFlags &= (kCFCompareCaseInsensitive|kCFCompareDiacriticInsensitive|kCFCompareWidthInsensitive); + + if ((0 == theFlags) || (0 == length)) goto bail; // nothing to do + + langCode = ((NULL == theLocale) ? NULL : (const uint8_t *)_CFStrGetLanguageIdentifierForLocale(theLocale)); + + eightBitEncoding = __CFStringGetEightBitStringEncoding(); + cString = (const uint8_t *)CFStringGetCStringPtr(theString, eightBitEncoding); + + if ((NULL != cString) && !caseInsensitive && (kCFStringEncodingASCII == eightBitEncoding)) goto bail; // All ASCII + + CFStringInitInlineBuffer(theString, &stringBuffer, CFRangeMake(0, length)); + + if ((NULL != cString) && (theFlags & (kCFCompareCaseInsensitive|kCFCompareDiacriticInsensitive))) { + const uint8_t *cStringPtr = cString; + const uint8_t *cStringLimit = cString + length; + uint8_t *cStringContents = (isObjc ? NULL : (uint8_t *)__CFStrContents(theString) + __CFStrSkipAnyLengthByte(theString)); + + while (cStringPtr < cStringLimit) { + if ((*cStringPtr < 0x80) && (NULL == langCode)) { + if (caseInsensitive && (*cStringPtr >= 'A') && (*cStringPtr <= 'Z')) { + if (NULL == cStringContents) { + break; + } else { + cStringContents[cStringPtr - cString] += ('a' - 'A'); + } + } + } else { + if ((bufferLength = __CFStringFoldCharacterClusterAtIndex((UTF32Char)__CFCharToUniCharTable[*cStringPtr], &stringBuffer, cStringPtr - cString, theFlags, langCode, buffer, kCFStringStackBufferLength, NULL)) > 0) { + if ((*buffer > 0x7F) || (bufferLength > 1) || (NULL == cStringContents)) break; + cStringContents[cStringPtr - cString] = *buffer; + } + } + ++cStringPtr; + } + + currentIndex = cStringPtr - cString; + } + + if (currentIndex < length) { + UTF16Char *contents; + + if (isObjc) { + CFMutableStringRef cfString; + CFRange range = CFRangeMake(currentIndex, length - currentIndex); + + contents = (UTF16Char *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(UTF16Char) * range.length, 0); + + CFStringGetCharacters(theString, range, contents); + + cfString = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorSystemDefault, contents, range.length, range.length, NULL); + + CFStringFold(cfString, theFlags, theLocale); + + CFStringReplace(theString, range, cfString); + + CFRelease(cfString); + } else { + const UTF32Char *characters; + const UTF32Char *charactersLimit; + UTF32Char character; + CFIndex consumedLength; + + contents = NULL; + + if (bufferLength > 0) { + __CFStringChangeSize(theString, CFRangeMake(currentIndex + 1, 0), bufferLength - 1, true); + length = __CFStrLength(theString); + CFStringInitInlineBuffer(theString, &stringBuffer, CFRangeMake(0, length)); + + contents = (UTF16Char *)__CFStrContents(theString) + currentIndex; + characters = buffer; + charactersLimit = characters + bufferLength; + while (characters < charactersLimit) *(contents++) = (UTF16Char)*(characters++); + ++currentIndex; + } + + while (currentIndex < length) { + character = __CFStringGetCharacterFromInlineBufferQuick(&stringBuffer, currentIndex); + + consumedLength = 0; + + if ((NULL == langCode) && (character < 0x80) && (0 == (theFlags & kCFCompareDiacriticInsensitive))) { + if (caseInsensitive && (character >= 'A') && (character <= 'Z')) { + consumedLength = 1; + bufferLength = 1; + *buffer = character + ('a' - 'A'); + } + } else { + if (CFUniCharIsSurrogateHighCharacter(character) && ((currentIndex + 1) < length)) { + UTF16Char lowSurrogate = __CFStringGetCharacterFromInlineBufferQuick(&stringBuffer, currentIndex + 1); + if (CFUniCharIsSurrogateLowCharacter(lowSurrogate)) character = CFUniCharGetLongCharacterForSurrogatePair(character, lowSurrogate); + } + + bufferLength = __CFStringFoldCharacterClusterAtIndex(character, &stringBuffer, currentIndex, theFlags, langCode, buffer, kCFStringStackBufferLength, &consumedLength); + } + + if (consumedLength > 0) { + CFIndex utf16Length = bufferLength; + + characters = buffer; + charactersLimit = characters + bufferLength; + + while (characters < charactersLimit) if (*(characters++) > 0xFFFF) ++utf16Length; // Extend bufferLength to the UTF-16 length + + if ((utf16Length != consumedLength) || __CFStrIsEightBit(theString)) { + CFRange range; + CFIndex insertLength; + + if (consumedLength < utf16Length) { // Need to expand + range = CFRangeMake(currentIndex + consumedLength, 0); + insertLength = utf16Length - consumedLength; + } else { + range = CFRangeMake(currentIndex + utf16Length, consumedLength - utf16Length); + insertLength = 0; + } + __CFStringChangeSize(theString, range, insertLength, true); + length = __CFStrLength(theString); + CFStringInitInlineBuffer(theString, &stringBuffer, CFRangeMake(0, length)); + } + + (void)CFUniCharFromUTF32(buffer, bufferLength, (UTF16Char *)__CFStrContents(theString) + currentIndex, true, __CF_BIG_ENDIAN__); + + currentIndex += utf16Length; + } else { + ++currentIndex; + } + } + } + } + + bail: + if (NULL == locale && theLocale) { + CFRelease(theLocale); + } +} + +enum { + kCFStringFormatZeroFlag = (1 << 0), // if not, padding is space char + kCFStringFormatMinusFlag = (1 << 1), // if not, no flag implied + kCFStringFormatPlusFlag = (1 << 2), // if not, no flag implied, overrides space + kCFStringFormatSpaceFlag = (1 << 3) // if not, no flag implied +}; + +typedef struct { + int16_t size; + int16_t type; + SInt32 loc; + SInt32 len; + SInt32 widthArg; + SInt32 precArg; + uint32_t flags; + int8_t mainArgNum; + int8_t precArgNum; + int8_t widthArgNum; + int8_t unused1; +} CFFormatSpec; + +typedef struct { + int16_t type; + int16_t size; + union { + int64_t int64Value; + double doubleValue; +#if LONG_DOUBLE_SUPPORT + long double longDoubleValue; +#endif + void *pointerValue; + } value; +} CFPrintValue; + +enum { + CFFormatDefaultSize = 0, + CFFormatSize1 = 1, + CFFormatSize2 = 2, + CFFormatSize4 = 3, + CFFormatSize8 = 4, + CFFormatSize16 = 5, +#if __LP64__ + CFFormatSizeLong = CFFormatSize8, + CFFormatSizePointer = CFFormatSize8 +#else + CFFormatSizeLong = CFFormatSize4, + CFFormatSizePointer = CFFormatSize4 +#endif +}; + + + +enum { + CFFormatLiteralType = 32, + CFFormatLongType = 33, + CFFormatDoubleType = 34, + CFFormatPointerType = 35, + CFFormatObjectType = 36, /* handled specially */ /* ??? not used anymore, can be removed? */ + CFFormatCFType = 37, /* handled specially */ + CFFormatUnicharsType = 38, /* handled specially */ + CFFormatCharsType = 39, /* handled specially */ + CFFormatPascalCharsType = 40, /* handled specially */ + CFFormatSingleUnicharType = 41, /* handled specially */ + CFFormatDummyPointerType = 42 /* special case for %n */ +}; + +CF_INLINE void __CFParseFormatSpec(const UniChar *uformat, const uint8_t *cformat, SInt32 *fmtIdx, SInt32 fmtLen, CFFormatSpec *spec) { + Boolean seenDot = false; + for (;;) { + UniChar ch; + if (fmtLen <= *fmtIdx) return; /* no type */ + if (cformat) ch = (UniChar)cformat[(*fmtIdx)++]; else ch = uformat[(*fmtIdx)++]; +reswtch:switch (ch) { + case '#': // ignored for now + break; + case 0x20: + if (!(spec->flags & kCFStringFormatPlusFlag)) spec->flags |= kCFStringFormatSpaceFlag; + break; + case '-': + spec->flags |= kCFStringFormatMinusFlag; + spec->flags &= ~kCFStringFormatZeroFlag; // remove zero flag + break; + case '+': + spec->flags |= kCFStringFormatPlusFlag; + spec->flags &= ~kCFStringFormatSpaceFlag; // remove space flag + break; + case '0': + if (!(spec->flags & kCFStringFormatMinusFlag)) spec->flags |= kCFStringFormatZeroFlag; + break; + case 'h': + spec->size = CFFormatSize2; + break; + case 'l': + if (*fmtIdx < fmtLen) { + // fetch next character, don't increment fmtIdx + if (cformat) ch = (UniChar)cformat[(*fmtIdx)]; else ch = uformat[(*fmtIdx)]; + if ('l' == ch) { // 'll' for long long, like 'q' + (*fmtIdx)++; + spec->size = CFFormatSize8; + break; + } + } + spec->size = CFFormatSizeLong; // 4 or 8 depending on LP64 + break; +#if LONG_DOUBLE_SUPPORT + case 'L': + spec->size = CFFormatSize16; + break; +#endif + case 'q': + spec->size = CFFormatSize8; + break; + case 't': case 'z': + spec->size = CFFormatSizeLong; // 4 or 8 depending on LP64 + break; + case 'j': + spec->size = CFFormatSize8; + break; + case 'c': + spec->type = CFFormatLongType; + spec->size = CFFormatSize1; + return; + case 'O': case 'o': case 'D': case 'd': case 'i': case 'U': case 'u': case 'x': case 'X': + spec->type = CFFormatLongType; + // Seems like if spec->size == 0, we should spec->size = CFFormatSize4. However, 0 is handled correctly. + return; + case 'a': case 'A': case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': + spec->type = CFFormatDoubleType; + if (spec->size != CFFormatSize16) spec->size = CFFormatSize8; + return; + case 'n': /* %n is not handled correctly; for Leopard or newer apps, we disable it further */ + spec->type = _CFExecutableLinkedOnOrAfter(CFSystemVersionLeopard) ? CFFormatDummyPointerType : CFFormatPointerType; + spec->size = CFFormatSizePointer; // 4 or 8 depending on LP64 + return; + case 'p': + spec->type = CFFormatPointerType; + spec->size = CFFormatSizePointer; // 4 or 8 depending on LP64 + return; + case 's': + spec->type = CFFormatCharsType; + spec->size = CFFormatSizePointer; // 4 or 8 depending on LP64 + return; + case 'S': + spec->type = CFFormatUnicharsType; + spec->size = CFFormatSizePointer; // 4 or 8 depending on LP64 + return; + case 'C': + spec->type = CFFormatSingleUnicharType; + spec->size = CFFormatSize2; + return; + case 'P': + spec->type = CFFormatPascalCharsType; + spec->size = CFFormatSizePointer; // 4 or 8 depending on LP64 + return; + case '@': + spec->type = CFFormatCFType; + spec->size = CFFormatSizePointer; // 4 or 8 depending on LP64 + return; + case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { + int64_t number = 0; + do { + number = 10 * number + (ch - '0'); + if (cformat) ch = (UniChar)cformat[(*fmtIdx)++]; else ch = uformat[(*fmtIdx)++]; + } while ((UInt32)(ch - '0') <= 9); + if ('$' == ch) { + if (-2 == spec->precArgNum) { + spec->precArgNum = (int8_t)number - 1; // Arg numbers start from 1 + } else if (-2 == spec->widthArgNum) { + spec->widthArgNum = (int8_t)number - 1; // Arg numbers start from 1 + } else { + spec->mainArgNum = (int8_t)number - 1; // Arg numbers start from 1 + } + break; + } else if (seenDot) { /* else it's either precision or width */ + spec->precArg = (SInt32)number; + } else { + spec->widthArg = (SInt32)number; + } + goto reswtch; + } + case '*': + spec->widthArgNum = -2; + break; + case '.': + seenDot = true; + if (cformat) ch = (UniChar)cformat[(*fmtIdx)++]; else ch = uformat[(*fmtIdx)++]; + if ('*' == ch) { + spec->precArgNum = -2; + break; + } + goto reswtch; + default: + spec->type = CFFormatLiteralType; + return; + } + } +} + +/* ??? It ignores the formatOptions argument. + ??? %s depends on handling of encodings by __CFStringAppendBytes +*/ +void CFStringAppendFormatAndArguments(CFMutableStringRef outputString, CFDictionaryRef formatOptions, CFStringRef formatString, va_list args) { + _CFStringAppendFormatAndArgumentsAux(outputString, NULL, formatOptions, formatString, args); +} + +#if DEPLOYMENT_TARGET_MACOSX +#define SNPRINTF(TYPE, WHAT) { \ + TYPE value = (TYPE) WHAT; \ + if (-1 != specs[curSpec].widthArgNum) { \ + if (-1 != specs[curSpec].precArgNum) { \ + snprintf_l(buffer, 255, NULL, formatBuffer, width, precision, value); \ + } else { \ + snprintf_l(buffer, 255, NULL, formatBuffer, width, value); \ + } \ + } else { \ + if (-1 != specs[curSpec].precArgNum) { \ + snprintf_l(buffer, 255, NULL, formatBuffer, precision, value); \ + } else { \ + snprintf_l(buffer, 255, NULL, formatBuffer, value); \ + } \ + }} +#elif DEPLOYMENT_TARGET_WINDOWS && defined(_MSC_VER) +#define SNPRINTF(TYPE, WHAT) { \ + TYPE value = (TYPE) WHAT; \ + if (-1 != specs[curSpec].widthArgNum) { \ + if (-1 != specs[curSpec].precArgNum) { \ + _snprintf(buffer, 255, formatBuffer, width, precision, value); \ + } else { \ + _snprintf(buffer, 255, formatBuffer, width, value); \ + } \ + } else { \ + if (-1 != specs[curSpec].precArgNum) { \ + _snprintf(buffer, 255, formatBuffer, precision, value); \ + } else { \ + _snprintf(buffer, 255, formatBuffer, value); \ + } \ + }} +#else +#define SNPRINTF(TYPE, WHAT) { \ + TYPE value = (TYPE) WHAT; \ + if (-1 != specs[curSpec].widthArgNum) { \ + if (-1 != specs[curSpec].precArgNum) { \ + snprintf(buffer, 255, formatBuffer, width, precision, value); \ + } else { \ + snprintf(buffer, 255, formatBuffer, width, value); \ + } \ + } else { \ + if (-1 != specs[curSpec].precArgNum) { \ + snprintf(buffer, 255, formatBuffer, precision, value); \ + } else { \ + snprintf(buffer, 255, formatBuffer, value); \ + } \ + }} +#endif //__MACH__ + +void _CFStringAppendFormatAndArgumentsAux(CFMutableStringRef outputString, CFStringRef (*copyDescFunc)(void *, const void *), CFDictionaryRef formatOptions, CFStringRef formatString, va_list args) { + SInt32 numSpecs, sizeSpecs, sizeArgNum, formatIdx, curSpec, argNum; + CFIndex formatLen; +#define FORMAT_BUFFER_LEN 400 + const uint8_t *cformat = NULL; + const UniChar *uformat = NULL; + UniChar *formatChars = NULL; + UniChar localFormatBuffer[FORMAT_BUFFER_LEN]; + + #define VPRINTF_BUFFER_LEN 61 + CFFormatSpec localSpecsBuffer[VPRINTF_BUFFER_LEN]; + CFFormatSpec *specs; + CFPrintValue localValuesBuffer[VPRINTF_BUFFER_LEN]; + CFPrintValue *values; + CFAllocatorRef tmpAlloc = NULL; + + intmax_t dummyLocation; // A place for %n to do its thing in; should be the widest possible int value + + numSpecs = 0; + sizeSpecs = 0; + sizeArgNum = 0; + specs = NULL; + values = NULL; + + formatLen = CFStringGetLength(formatString); + if (!CF_IS_OBJC(__kCFStringTypeID, formatString)) { + __CFAssertIsString(formatString); + if (!__CFStrIsUnicode(formatString)) { + cformat = (const uint8_t *)__CFStrContents(formatString); + if (cformat) cformat += __CFStrSkipAnyLengthByte(formatString); + } else { + uformat = (const UniChar *)__CFStrContents(formatString); + } + } + if (!cformat && !uformat) { + formatChars = (formatLen > FORMAT_BUFFER_LEN) ? (UniChar *)CFAllocatorAllocate(tmpAlloc = __CFGetDefaultAllocator(), formatLen * sizeof(UniChar), 0) : localFormatBuffer; + if (formatChars != localFormatBuffer && __CFOASafe) __CFSetLastAllocationEventName(formatChars, "CFString (temp)"); + CFStringGetCharacters(formatString, CFRangeMake(0, formatLen), formatChars); + uformat = formatChars; + } + + /* Compute an upper bound for the number of format specifications */ + if (cformat) { + for (formatIdx = 0; formatIdx < formatLen; formatIdx++) if ('%' == cformat[formatIdx]) sizeSpecs++; + } else { + for (formatIdx = 0; formatIdx < formatLen; formatIdx++) if ('%' == uformat[formatIdx]) sizeSpecs++; + } + tmpAlloc = __CFGetDefaultAllocator(); + specs = ((2 * sizeSpecs + 1) > VPRINTF_BUFFER_LEN) ? (CFFormatSpec *)CFAllocatorAllocate(tmpAlloc, (2 * sizeSpecs + 1) * sizeof(CFFormatSpec), 0) : localSpecsBuffer; + if (specs != localSpecsBuffer && __CFOASafe) __CFSetLastAllocationEventName(specs, "CFString (temp)"); + + /* Collect format specification information from the format string */ + for (curSpec = 0, formatIdx = 0; formatIdx < formatLen; curSpec++) { + SInt32 newFmtIdx; + specs[curSpec].loc = formatIdx; + specs[curSpec].len = 0; + specs[curSpec].size = 0; + specs[curSpec].type = 0; + specs[curSpec].flags = 0; + specs[curSpec].widthArg = -1; + specs[curSpec].precArg = -1; + specs[curSpec].mainArgNum = -1; + specs[curSpec].precArgNum = -1; + specs[curSpec].widthArgNum = -1; + if (cformat) { + for (newFmtIdx = formatIdx; newFmtIdx < formatLen && '%' != cformat[newFmtIdx]; newFmtIdx++); + } else { + for (newFmtIdx = formatIdx; newFmtIdx < formatLen && '%' != uformat[newFmtIdx]; newFmtIdx++); + } + if (newFmtIdx != formatIdx) { /* Literal chunk */ + specs[curSpec].type = CFFormatLiteralType; + specs[curSpec].len = newFmtIdx - formatIdx; + } else { + newFmtIdx++; /* Skip % */ + __CFParseFormatSpec(uformat, cformat, &newFmtIdx, formatLen, &(specs[curSpec])); + if (CFFormatLiteralType == specs[curSpec].type) { + specs[curSpec].loc = formatIdx + 1; + specs[curSpec].len = 1; + } else { + specs[curSpec].len = newFmtIdx - formatIdx; + } + } + formatIdx = newFmtIdx; + +// fprintf(stderr, "specs[%d] = {\n size = %d,\n type = %d,\n loc = %d,\n len = %d,\n mainArgNum = %d,\n precArgNum = %d,\n widthArgNum = %d\n}\n", curSpec, specs[curSpec].size, specs[curSpec].type, specs[curSpec].loc, specs[curSpec].len, specs[curSpec].mainArgNum, specs[curSpec].precArgNum, specs[curSpec].widthArgNum); + + } + numSpecs = curSpec; + // Max of three args per spec, reasoning thus: 1 width, 1 prec, 1 value + values = ((3 * sizeSpecs + 1) > VPRINTF_BUFFER_LEN) ? (CFPrintValue *)CFAllocatorAllocate(tmpAlloc, (3 * sizeSpecs + 1) * sizeof(CFPrintValue), 0) : localValuesBuffer; + if (values != localValuesBuffer && __CFOASafe) __CFSetLastAllocationEventName(values, "CFString (temp)"); + memset(values, 0, (3 * sizeSpecs + 1) * sizeof(CFPrintValue)); + sizeArgNum = (3 * sizeSpecs + 1); + + /* Compute values array */ + argNum = 0; + for (curSpec = 0; curSpec < numSpecs; curSpec++) { + SInt32 newMaxArgNum; + if (0 == specs[curSpec].type) continue; + if (CFFormatLiteralType == specs[curSpec].type) continue; + newMaxArgNum = sizeArgNum; + if (newMaxArgNum < specs[curSpec].mainArgNum) { + newMaxArgNum = specs[curSpec].mainArgNum; + } + if (newMaxArgNum < specs[curSpec].precArgNum) { + newMaxArgNum = specs[curSpec].precArgNum; + } + if (newMaxArgNum < specs[curSpec].widthArgNum) { + newMaxArgNum = specs[curSpec].widthArgNum; + } + if (sizeArgNum < newMaxArgNum) { + if (specs != localSpecsBuffer) CFAllocatorDeallocate(tmpAlloc, specs); + if (values != localValuesBuffer) CFAllocatorDeallocate(tmpAlloc, values); + if (formatChars && (formatChars != localFormatBuffer)) CFAllocatorDeallocate(tmpAlloc, formatChars); + return; // more args than we expected! + } + /* It is actually incorrect to reorder some specs and not all; we just do some random garbage here */ + if (-2 == specs[curSpec].widthArgNum) { + specs[curSpec].widthArgNum = argNum++; + } + if (-2 == specs[curSpec].precArgNum) { + specs[curSpec].precArgNum = argNum++; + } + if (-1 == specs[curSpec].mainArgNum) { + specs[curSpec].mainArgNum = argNum++; + } + values[specs[curSpec].mainArgNum].size = specs[curSpec].size; + values[specs[curSpec].mainArgNum].type = specs[curSpec].type; + if (-1 != specs[curSpec].widthArgNum) { + values[specs[curSpec].widthArgNum].size = 0; + values[specs[curSpec].widthArgNum].type = CFFormatLongType; + } + if (-1 != specs[curSpec].precArgNum) { + values[specs[curSpec].precArgNum].size = 0; + values[specs[curSpec].precArgNum].type = CFFormatLongType; + } + } + + /* Collect the arguments in correct type from vararg list */ + for (argNum = 0; argNum < sizeArgNum; argNum++) { + switch (values[argNum].type) { + case 0: + case CFFormatLiteralType: + break; + case CFFormatLongType: + case CFFormatSingleUnicharType: + if (CFFormatSize1 == values[argNum].size) { + values[argNum].value.int64Value = (int64_t)(int8_t)va_arg(args, int); + } else if (CFFormatSize2 == values[argNum].size) { + values[argNum].value.int64Value = (int64_t)(int16_t)va_arg(args, int); + } else if (CFFormatSize4 == values[argNum].size) { + values[argNum].value.int64Value = (int64_t)va_arg(args, int32_t); + } else if (CFFormatSize8 == values[argNum].size) { + values[argNum].value.int64Value = (int64_t)va_arg(args, int64_t); + } else { + values[argNum].value.int64Value = (int64_t)va_arg(args, int); + } + break; + case CFFormatDoubleType: +#if LONG_DOUBLE_SUPPORT + if (CFFormatSize16 == values[argNum].size) { + values[argNum].value.longDoubleValue = va_arg(args, long double); + } else +#endif + { + values[argNum].value.doubleValue = va_arg(args, double); + } + break; + case CFFormatPointerType: + case CFFormatObjectType: + case CFFormatCFType: + case CFFormatUnicharsType: + case CFFormatCharsType: + case CFFormatPascalCharsType: + values[argNum].value.pointerValue = va_arg(args, void *); + break; + case CFFormatDummyPointerType: + (void)va_arg(args, void *); // Skip the provided argument + values[argNum].value.pointerValue = &dummyLocation; + break; + } + } + va_end(args); + + /* Format the pieces together */ + for (curSpec = 0; curSpec < numSpecs; curSpec++) { + SInt32 width = 0, precision = 0; + UniChar *up, ch; + Boolean hasWidth = false, hasPrecision = false; + + // widthArgNum and widthArg are never set at the same time; same for precArg* + if (-1 != specs[curSpec].widthArgNum) { + width = (SInt32)values[specs[curSpec].widthArgNum].value.int64Value; + hasWidth = true; + } + if (-1 != specs[curSpec].precArgNum) { + precision = (SInt32)values[specs[curSpec].precArgNum].value.int64Value; + hasPrecision = true; + } + if (-1 != specs[curSpec].widthArg) { + width = specs[curSpec].widthArg; + hasWidth = true; + } + if (-1 != specs[curSpec].precArg) { + precision = specs[curSpec].precArg; + hasPrecision = true; + } + + switch (specs[curSpec].type) { + case CFFormatLongType: + case CFFormatDoubleType: + case CFFormatPointerType: { + char formatBuffer[128]; +#if defined(__GNUC__) + char buffer[256 + width + precision]; +#else + char stackBuffer[512]; + char *dynamicBuffer = NULL; + char *buffer = stackBuffer; + if (256+width+precision > 512) { + dynamicBuffer = (char *)CFAllocatorAllocate(kCFAllocatorSystemDefault, 256+width+precision, 0); + buffer = dynamicBuffer; + } +#endif + SInt32 cidx, idx, loc; + Boolean appended = false; + loc = specs[curSpec].loc; + // In preparation to call snprintf(), copy the format string out + if (cformat) { + for (idx = 0, cidx = 0; cidx < specs[curSpec].len; idx++, cidx++) { + if ('$' == cformat[loc + cidx]) { + for (idx--; '0' <= formatBuffer[idx] && formatBuffer[idx] <= '9'; idx--); + } else { + formatBuffer[idx] = cformat[loc + cidx]; + } + } + } else { + for (idx = 0, cidx = 0; cidx < specs[curSpec].len; idx++, cidx++) { + if ('$' == uformat[loc + cidx]) { + for (idx--; '0' <= formatBuffer[idx] && formatBuffer[idx] <= '9'; idx--); + } else { + formatBuffer[idx] = (int8_t)uformat[loc + cidx]; + } + } + } + formatBuffer[idx] = '\0'; + // Should modify format buffer here if necessary; for example, to translate %qd to + // the equivalent, on architectures which do not have %q. + buffer[sizeof(buffer) - 1] = '\0'; + switch (specs[curSpec].type) { + case CFFormatLongType: + if (CFFormatSize8 == specs[curSpec].size) { + SNPRINTF(int64_t, values[specs[curSpec].mainArgNum].value.int64Value) + } else { + SNPRINTF(SInt32, values[specs[curSpec].mainArgNum].value.int64Value) + } + break; + case CFFormatPointerType: + case CFFormatDummyPointerType: + SNPRINTF(void *, values[specs[curSpec].mainArgNum].value.pointerValue) + break; + + case CFFormatDoubleType: +#if LONG_DOUBLE_SUPPORT + if (CFFormatSize16 == specs[curSpec].size) { + SNPRINTF(long double, values[specs[curSpec].mainArgNum].value.longDoubleValue) + } else +#endif + { + SNPRINTF(double, values[specs[curSpec].mainArgNum].value.doubleValue) + } + // See if we need to localize the decimal point + if (formatOptions) { // We have localization info + CFStringRef decimalSeparator = (CFGetTypeID(formatOptions) == CFLocaleGetTypeID()) ? (CFStringRef)CFLocaleGetValue((CFLocaleRef)formatOptions, kCFLocaleDecimalSeparator) : (CFStringRef)CFDictionaryGetValue(formatOptions, CFSTR("NSDecimalSeparator")); + if (decimalSeparator != NULL) { // We have a decimal separator in there + CFIndex decimalPointLoc = 0; + while (buffer[decimalPointLoc] != 0 && buffer[decimalPointLoc] != '.') decimalPointLoc++; + if (buffer[decimalPointLoc] == '.') { // And we have a decimal point in the formatted string + buffer[decimalPointLoc] = 0; + CFStringAppendCString(outputString, (const char *)buffer, __CFStringGetEightBitStringEncoding()); + CFStringAppend(outputString, decimalSeparator); + CFStringAppendCString(outputString, (const char *)(buffer + decimalPointLoc + 1), __CFStringGetEightBitStringEncoding()); + appended = true; + } + } + } + break; + } + if (!appended) CFStringAppendCString(outputString, (const char *)buffer, __CFStringGetEightBitStringEncoding()); +#if !defined(__GNUC__) + if (dynamicBuffer) { + CFAllocatorDeallocate(kCFAllocatorSystemDefault, dynamicBuffer); + } +#endif + } + break; + case CFFormatLiteralType: + if (cformat) { + __CFStringAppendBytes(outputString, (const char *)(cformat+specs[curSpec].loc), specs[curSpec].len, __CFStringGetEightBitStringEncoding()); + } else { + CFStringAppendCharacters(outputString, uformat+specs[curSpec].loc, specs[curSpec].len); + } + break; + case CFFormatPascalCharsType: + case CFFormatCharsType: + if (values[specs[curSpec].mainArgNum].value.pointerValue == NULL) { + CFStringAppendCString(outputString, "(null)", kCFStringEncodingASCII); + } else { + int len; + const char *str = (const char *)values[specs[curSpec].mainArgNum].value.pointerValue; + if (specs[curSpec].type == CFFormatPascalCharsType) { // Pascal string case + len = ((unsigned char *)str)[0]; + str++; + if (hasPrecision && precision < len) len = precision; + } else { // C-string case + if (!hasPrecision) { // No precision, so rely on the terminating null character + len = (CFIndex)strlen(str); + } else { // Don't blindly call strlen() if there is a precision; the string might not have a terminating null (3131988) + const char *terminatingNull = (const char *)memchr(str, 0, precision); // Basically strlen() on only the first precision characters of str + if (terminatingNull) { // There was a null in the first precision characters + len = terminatingNull - str; + } else { + len = precision; + } + } + } + // Since the spec says the behavior of the ' ', '0', '#', and '+' flags is undefined for + // '%s', and since we have ignored them in the past, the behavior is hereby cast in stone + // to ignore those flags (and, say, never pad with '0' instead of space). + if (specs[curSpec].flags & kCFStringFormatMinusFlag) { + __CFStringAppendBytes(outputString, str, len, __CFStringGetSystemEncoding()); + if (hasWidth && width > len) { + int w = width - len; // We need this many spaces; do it ten at a time + do {__CFStringAppendBytes(outputString, " ", (w > 10 ? 10 : w), kCFStringEncodingASCII);} while ((w -= 10) > 0); + } + } else { + if (hasWidth && width > len) { + int w = width - len; // We need this many spaces; do it ten at a time + do {__CFStringAppendBytes(outputString, " ", (w > 10 ? 10 : w), kCFStringEncodingASCII);} while ((w -= 10) > 0); + } + __CFStringAppendBytes(outputString, str, len, __CFStringGetSystemEncoding()); + } + } + break; + case CFFormatSingleUnicharType: + ch = (UniChar)values[specs[curSpec].mainArgNum].value.int64Value; + CFStringAppendCharacters(outputString, &ch, 1); + break; + case CFFormatUnicharsType: + //??? need to handle width, precision, and padding arguments + up = (UniChar *)values[specs[curSpec].mainArgNum].value.pointerValue; + if (NULL == up) { + CFStringAppendCString(outputString, "(null)", kCFStringEncodingASCII); + } else { + int len; + for (len = 0; 0 != up[len]; len++); + // Since the spec says the behavior of the ' ', '0', '#', and '+' flags is undefined for + // '%s', and since we have ignored them in the past, the behavior is hereby cast in stone + // to ignore those flags (and, say, never pad with '0' instead of space). + if (hasPrecision && precision < len) len = precision; + if (specs[curSpec].flags & kCFStringFormatMinusFlag) { + CFStringAppendCharacters(outputString, up, len); + if (hasWidth && width > len) { + int w = width - len; // We need this many spaces; do it ten at a time + do {__CFStringAppendBytes(outputString, " ", (w > 10 ? 10 : w), kCFStringEncodingASCII);} while ((w -= 10) > 0); + } + } else { + if (hasWidth && width > len) { + int w = width - len; // We need this many spaces; do it ten at a time + do {__CFStringAppendBytes(outputString, " ", (w > 10 ? 10 : w), kCFStringEncodingASCII);} while ((w -= 10) > 0); + } + CFStringAppendCharacters(outputString, up, len); + } + } + break; + case CFFormatCFType: + case CFFormatObjectType: + if (NULL != values[specs[curSpec].mainArgNum].value.pointerValue) { + CFStringRef str = NULL; + if (copyDescFunc) { + str = copyDescFunc(values[specs[curSpec].mainArgNum].value.pointerValue, formatOptions); + } else { + str = __CFCopyFormattingDescription(values[specs[curSpec].mainArgNum].value.pointerValue, formatOptions); + if (NULL == str) { + str = CFCopyDescription(values[specs[curSpec].mainArgNum].value.pointerValue); + } + } + if (str) { + CFStringAppend(outputString, str); + CFRelease(str); + } else { + CFStringAppendCString(outputString, "(null description)", kCFStringEncodingASCII); + } + } else { + CFStringAppendCString(outputString, "(null)", kCFStringEncodingASCII); + } + break; + } + } + + if (specs != localSpecsBuffer) CFAllocatorDeallocate(tmpAlloc, specs); + if (values != localValuesBuffer) CFAllocatorDeallocate(tmpAlloc, values); + if (formatChars && (formatChars != localFormatBuffer)) CFAllocatorDeallocate(tmpAlloc, formatChars); + +} + +#undef SNPRINTF + +void CFShowStr(CFStringRef str) { + CFAllocatorRef alloc; + + if (!str) { + fprintf(stdout, "(null)\n"); + return; + } + + if (CF_IS_OBJC(__kCFStringTypeID, str)) { + fprintf(stdout, "This is an NSString, not CFString\n"); + return; + } + + alloc = CFGetAllocator(str); + + fprintf(stdout, "\nLength %d\nIsEightBit %d\n", (int)__CFStrLength(str), __CFStrIsEightBit(str)); + fprintf(stdout, "HasLengthByte %d\nHasNullByte %d\nInlineContents %d\n", + __CFStrHasLengthByte(str), __CFStrHasNullByte(str), __CFStrIsInline(str)); + + fprintf(stdout, "Allocator "); + if (alloc != kCFAllocatorSystemDefault) { + fprintf(stdout, "%p\n", (void *)alloc); + } else { + fprintf(stdout, "SystemDefault\n"); + } + fprintf(stdout, "Mutable %d\n", __CFStrIsMutable(str)); + if (!__CFStrIsMutable(str) && __CFStrHasContentsDeallocator(str)) { + if (__CFStrContentsDeallocator(str)) fprintf(stdout, "ContentsDeallocatorFunc %p\n", (void *)__CFStrContentsDeallocator(str)); + else fprintf(stdout, "ContentsDeallocatorFunc None\n"); + } else if (__CFStrIsMutable(str) && __CFStrHasContentsAllocator(str)) { + fprintf(stdout, "ExternalContentsAllocator %p\n", (void *)__CFStrContentsAllocator((CFMutableStringRef)str)); + } + + if (__CFStrIsMutable(str)) { + fprintf(stdout, "CurrentCapacity %d\n%sCapacity %d\n", (int)__CFStrCapacity(str), __CFStrIsFixed(str) ? "Fixed" : "Desired", (int)__CFStrDesiredCapacity(str)); + } + fprintf(stdout, "Contents %p\n", (void *)__CFStrContents(str)); +} + + + +#undef HANGUL_SBASE +#undef HANGUL_LBASE +#undef HANGUL_VBASE +#undef HANGUL_TBASE +#undef HANGUL_SCOUNT +#undef HANGUL_LCOUNT +#undef HANGUL_VCOUNT +#undef HANGUL_TCOUNT +#undef HANGUL_NCOUNT +
diff --git a/CoreFoundation/CFString.h b/CoreFoundation/CFString.h new file mode 100644 index 0000000..020741a --- /dev/null +++ b/CoreFoundation/CFString.h
@@ -0,0 +1,853 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFString.h + Copyright (c) 1998-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFSTRING__) +#define __COREFOUNDATION_CFSTRING__ 1 + +#include <CoreFoundation/CFBase.h> +#include <CoreFoundation/CFArray.h> +#include <CoreFoundation/CFData.h> +#include <CoreFoundation/CFDictionary.h> +#include <CoreFoundation/CFCharacterSet.h> +#include <CoreFoundation/CFLocale.h> +#include <stdarg.h> + +CF_EXTERN_C_BEGIN + +/* +Please note: CFStrings are conceptually an array of Unicode characters. +However, in general, how a CFString stores this array is an implementation +detail. For instance, CFString might choose to use an array of 8-bit characters; +to store its contents; or it might use multiple blocks of memory; or whatever. +Furthermore, the implementation might change depending on the default +system encoding, the user's language, the OS, or even a given release. + +What this means is that you should use the following advanced functions with care: + + CFStringGetPascalStringPtr() + CFStringGetCStringPtr() + CFStringGetCharactersPtr() + +These functions are provided for optimization only. They will either return the desired +pointer quickly, in constant time, or they return NULL. They might choose to return NULL +for many reasons; for instance it's possible that for users running in different +languages these sometimes return NULL; or in a future OS release the first two might +switch to always returning NULL. Never observing NULL returns in your usages of these +functions does not mean they won't ever return NULL. (But note the CFStringGetCharactersPtr() +exception mentioned further below.) + +In your usages of these functions, if you get a NULL return, use the non-Ptr version +of the functions as shown in this example: + + Str255 buffer; + StringPtr ptr = CFStringGetPascalStringPtr(str, encoding); + if (ptr == NULL) { + if (CFStringGetPascalString(str, buffer, 256, encoding)) ptr = buffer; + } + +Note that CFStringGetPascalString() or CFStringGetCString() calls might still fail --- but +that will happen in two circumstances only: The conversion from the UniChar contents of CFString +to the specified encoding fails, or the buffer is too small. If they fail, that means +the conversion was not possible. + +If you need a copy of the buffer in the above example, you might consider simply +calling CFStringGetPascalString() in all cases --- CFStringGetPascalStringPtr() +is simply an optimization. + +In addition, the following functions, which create immutable CFStrings from developer +supplied buffers without copying the buffers, might have to actually copy +under certain circumstances (If they do copy, the buffer will be dealt with by the +"contentsDeallocator" argument.): + + CFStringCreateWithPascalStringNoCopy() + CFStringCreateWithCStringNoCopy() + CFStringCreateWithCharactersNoCopy() + +You should of course never depend on the backing store of these CFStrings being +what you provided, and in other no circumstance should you change the contents +of that buffer (given that would break the invariant about the CFString being immutable). + +Having said all this, there are actually ways to create a CFString where the backing store +is external, and can be manipulated by the developer or CFString itself: + + CFStringCreateMutableWithExternalCharactersNoCopy() + CFStringSetExternalCharactersNoCopy() + +A "contentsAllocator" is used to realloc or free the backing store by CFString. +kCFAllocatorNull can be provided to assure CFString will never realloc or free the buffer. +Developer can call CFStringSetExternalCharactersNoCopy() to update +CFString's idea of what's going on, if the buffer is changed externally. In these +strings, CFStringGetCharactersPtr() is guaranteed to return the external buffer. + +These functions are here to allow wrapping a buffer of UniChar characters in a CFString, +allowing the buffer to passed into CFString functions and also manipulated via CFString +mutation functions. In general, developers should not use this technique for all strings, +as it prevents CFString from using certain optimizations. +*/ + +/* Identifier for character encoding; the values are the same as Text Encoding Converter TextEncoding. +*/ +typedef UInt32 CFStringEncoding; + +/* Platform-independent built-in encodings; always available on all platforms. + Call CFStringGetSystemEncoding() to get the default system encoding. +*/ +#define kCFStringEncodingInvalidId (0xffffffffU) +enum { + kCFStringEncodingMacRoman = 0, + kCFStringEncodingWindowsLatin1 = 0x0500, /* ANSI codepage 1252 */ + kCFStringEncodingISOLatin1 = 0x0201, /* ISO 8859-1 */ + kCFStringEncodingNextStepLatin = 0x0B01, /* NextStep encoding*/ + kCFStringEncodingASCII = 0x0600, /* 0..127 (in creating CFString, values greater than 0x7F are treated as corresponding Unicode value) */ + kCFStringEncodingUnicode = 0x0100, /* kTextEncodingUnicodeDefault + kTextEncodingDefaultFormat (aka kUnicode16BitFormat) */ + kCFStringEncodingUTF8 = 0x08000100, /* kTextEncodingUnicodeDefault + kUnicodeUTF8Format */ + kCFStringEncodingNonLossyASCII = 0x0BFF /* 7bit Unicode variants used by Cocoa & Java */ +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 + , + kCFStringEncodingUTF16 = 0x0100, /* kTextEncodingUnicodeDefault + kUnicodeUTF16Format (alias of kCFStringEncodingUnicode) */ + kCFStringEncodingUTF16BE = 0x10000100, /* kTextEncodingUnicodeDefault + kUnicodeUTF16BEFormat */ + kCFStringEncodingUTF16LE = 0x14000100, /* kTextEncodingUnicodeDefault + kUnicodeUTF16LEFormat */ + + kCFStringEncodingUTF32 = 0x0c000100, /* kTextEncodingUnicodeDefault + kUnicodeUTF32Format */ + kCFStringEncodingUTF32BE = 0x18000100, /* kTextEncodingUnicodeDefault + kUnicodeUTF32BEFormat */ + kCFStringEncodingUTF32LE = 0x1c000100 /* kTextEncodingUnicodeDefault + kUnicodeUTF32LEFormat */ +#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 */ +}; +typedef CFStringEncoding CFStringBuiltInEncodings; + +/* CFString type ID */ +CF_EXPORT +CFTypeID CFStringGetTypeID(void); + +/* Macro to allow creation of compile-time constant strings; the argument should be a constant string. + +CFSTR(), not being a "Copy" or "Create" function, does not return a new +reference for you. So, you should not release the return value. This is +much like constant C or Pascal strings --- when you use "hello world" +in a program, you do not free it. + +However, strings returned from CFSTR() can be retained and released in a +properly nested fashion, just like any other CF type. That is, if you pass +a CFSTR() return value to a function such as SetMenuItemWithCFString(), the +function can retain it, then later, when it's done with it, it can release it. + +At this point non-7 bit characters (that is, characters > 127) in CFSTR() are not +supported and using them will lead to unpredictable results. This includes escaped +(\nnn) characters whose values are > 127. Even if it works for you in testing, +it might not work for a user with a different language preference. +*/ +#ifdef __CONSTANT_CFSTRINGS__ +#define CFSTR(cStr) ((CFStringRef) __builtin___CFStringMakeConstantString ("" cStr "")) +#else +#define CFSTR(cStr) __CFStringMakeConstantString("" cStr "") +#endif + +/*** Immutable string creation functions ***/ + +/* Functions to create basic immutable strings. The provided allocator is used for all memory activity in these functions. +*/ + +/* The following four functions copy the provided buffer into CFString's internal storage. */ +CF_EXPORT +CFStringRef CFStringCreateWithPascalString(CFAllocatorRef alloc, ConstStr255Param pStr, CFStringEncoding encoding); + +CF_EXPORT +CFStringRef CFStringCreateWithCString(CFAllocatorRef alloc, const char *cStr, CFStringEncoding encoding); + +/* The following takes an explicit length, and allows you to specify whether the data is an external format --- that is, whether to pay attention to the BOM character (if any) and do byte swapping if necessary +*/ +CF_EXPORT +CFStringRef CFStringCreateWithBytes(CFAllocatorRef alloc, const UInt8 *bytes, CFIndex numBytes, CFStringEncoding encoding, Boolean isExternalRepresentation); + +CF_EXPORT +CFStringRef CFStringCreateWithCharacters(CFAllocatorRef alloc, const UniChar *chars, CFIndex numChars); + + +/* These functions try not to copy the provided buffer. The buffer will be deallocated +with the provided contentsDeallocator when it's no longer needed; to not free +the buffer, specify kCFAllocatorNull here. As usual, NULL means default allocator. + +NOTE: Do not count on these buffers as being used by the string; +in some cases the CFString might free the buffer and use something else +(for instance if it decides to always use Unicode encoding internally). + +NOTE: If you are not transferring ownership of the buffer to the CFString +(for instance, you supplied contentsDeallocator = kCFAllocatorNull), it is your +responsibility to assure the buffer does not go away during the lifetime of the string. +If the string is retained or copied, its lifetime might extend in ways you cannot +predict. So, for strings created with buffers whose lifetimes you cannot +guarantee, you need to be extremely careful --- do not hand it out to any +APIs which might retain or copy the strings. +*/ +CF_EXPORT +CFStringRef CFStringCreateWithPascalStringNoCopy(CFAllocatorRef alloc, ConstStr255Param pStr, CFStringEncoding encoding, CFAllocatorRef contentsDeallocator); + +CF_EXPORT +CFStringRef CFStringCreateWithCStringNoCopy(CFAllocatorRef alloc, const char *cStr, CFStringEncoding encoding, CFAllocatorRef contentsDeallocator); + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 +/* The following takes an explicit length, and allows you to specify whether the data is an external format --- that is, whether to pay attention to the BOM character (if any) and do byte swapping if necessary +*/ +CF_EXPORT +CFStringRef CFStringCreateWithBytesNoCopy(CFAllocatorRef alloc, const UInt8 *bytes, CFIndex numBytes, CFStringEncoding encoding, Boolean isExternalRepresentation, CFAllocatorRef contentsDeallocator) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +#endif + +CF_EXPORT +CFStringRef CFStringCreateWithCharactersNoCopy(CFAllocatorRef alloc, const UniChar *chars, CFIndex numChars, CFAllocatorRef contentsDeallocator); + +/* Create copies of part or all of the string. +*/ +CF_EXPORT +CFStringRef CFStringCreateWithSubstring(CFAllocatorRef alloc, CFStringRef str, CFRange range); + +CF_EXPORT +CFStringRef CFStringCreateCopy(CFAllocatorRef alloc, CFStringRef theString); + +/* These functions create a CFString from the provided printf-like format string and arguments. +*/ +CF_EXPORT +CFStringRef CFStringCreateWithFormat(CFAllocatorRef alloc, CFDictionaryRef formatOptions, CFStringRef format, ...); + +CF_EXPORT +CFStringRef CFStringCreateWithFormatAndArguments(CFAllocatorRef alloc, CFDictionaryRef formatOptions, CFStringRef format, va_list arguments); + +/* Functions to create mutable strings. "maxLength", if not 0, is a hard bound on the length of the string. If 0, there is no limit on the length. +*/ +CF_EXPORT +CFMutableStringRef CFStringCreateMutable(CFAllocatorRef alloc, CFIndex maxLength); + +CF_EXPORT +CFMutableStringRef CFStringCreateMutableCopy(CFAllocatorRef alloc, CFIndex maxLength, CFStringRef theString); + +/* This function creates a mutable string that has a developer supplied and directly editable backing store. +The string will be manipulated within the provided buffer (if any) until it outgrows capacity; then the +externalCharactersAllocator will be consulted for more memory. When the CFString is deallocated, the +buffer will be freed with the externalCharactersAllocator. Provide kCFAllocatorNull here to prevent the buffer +from ever being reallocated or deallocated by CFString. See comments at top of this file for more info. +*/ +CF_EXPORT +CFMutableStringRef CFStringCreateMutableWithExternalCharactersNoCopy(CFAllocatorRef alloc, UniChar *chars, CFIndex numChars, CFIndex capacity, CFAllocatorRef externalCharactersAllocator); + +/*** Basic accessors for the contents ***/ + +/* Number of 16-bit Unicode characters in the string. +*/ +CF_EXPORT +CFIndex CFStringGetLength(CFStringRef theString); + +/* Extracting the contents of the string. For obtaining multiple characters, calling +CFStringGetCharacters() is more efficient than multiple calls to CFStringGetCharacterAtIndex(). +If the length of the string is not known (so you can't use a fixed size buffer for CFStringGetCharacters()), +another method is to use is CFStringGetCharacterFromInlineBuffer() (see further below). +*/ +CF_EXPORT +UniChar CFStringGetCharacterAtIndex(CFStringRef theString, CFIndex idx); + +CF_EXPORT +void CFStringGetCharacters(CFStringRef theString, CFRange range, UniChar *buffer); + + +/*** Conversion to other encodings ***/ + +/* These two convert into the provided buffer; they return false if conversion isn't possible +(due to conversion error, or not enough space in the provided buffer). +These functions do zero-terminate or put the length byte; the provided bufferSize should include +space for this (so pass 256 for Str255). More sophisticated usages can go through CFStringGetBytes(). +These functions are equivalent to calling CFStringGetBytes() with +the range of the string; lossByte = 0; and isExternalRepresentation = false; +if successful, they then insert the leading length or terminating zero, as desired. +*/ +CF_EXPORT +Boolean CFStringGetPascalString(CFStringRef theString, StringPtr buffer, CFIndex bufferSize, CFStringEncoding encoding); + +CF_EXPORT +Boolean CFStringGetCString(CFStringRef theString, char *buffer, CFIndex bufferSize, CFStringEncoding encoding); + +/* These functions attempt to return in O(1) time the desired format for the string. +Note that although this means a pointer to the internal structure is being returned, +this can't always be counted on. Please see note at the top of the file for more +details. +*/ +CF_EXPORT +ConstStringPtr CFStringGetPascalStringPtr(CFStringRef theString, CFStringEncoding encoding); /* May return NULL at any time; be prepared for NULL */ + +CF_EXPORT +const char *CFStringGetCStringPtr(CFStringRef theString, CFStringEncoding encoding); /* May return NULL at any time; be prepared for NULL */ + +CF_EXPORT +const UniChar *CFStringGetCharactersPtr(CFStringRef theString); /* May return NULL at any time; be prepared for NULL */ + +/* The primitive conversion routine; allows you to convert a string piece at a time + into a fixed size buffer. Returns number of characters converted. + Characters that cannot be converted to the specified encoding are represented + with the byte specified by lossByte; if lossByte is 0, then lossy conversion + is not allowed and conversion stops, returning partial results. + Pass buffer==NULL if you don't care about the converted string (but just the convertability, + or number of bytes required). + maxBufLength indicates the maximum number of bytes to generate. It is ignored when buffer==NULL. + Does not zero-terminate. If you want to create Pascal or C string, allow one extra byte at start or end. + Setting isExternalRepresentation causes any extra bytes that would allow + the data to be made persistent to be included; for instance, the Unicode BOM. +*/ +CF_EXPORT +CFIndex CFStringGetBytes(CFStringRef theString, CFRange range, CFStringEncoding encoding, UInt8 lossByte, Boolean isExternalRepresentation, UInt8 *buffer, CFIndex maxBufLen, CFIndex *usedBufLen); + +/* Convenience functions String <-> Data. These generate "external" formats, that is, formats that + can be written out to disk. For instance, if the encoding is Unicode, CFStringCreateFromExternalRepresentation() + pays attention to the BOM character (if any) and does byte swapping if necessary. + Similarly CFStringCreateExternalRepresentation() will always include a BOM character if the encoding is + Unicode. See above for description of lossByte. +*/ +CF_EXPORT +CFStringRef CFStringCreateFromExternalRepresentation(CFAllocatorRef alloc, CFDataRef data, CFStringEncoding encoding); /* May return NULL on conversion error */ + +CF_EXPORT +CFDataRef CFStringCreateExternalRepresentation(CFAllocatorRef alloc, CFStringRef theString, CFStringEncoding encoding, UInt8 lossByte); /* May return NULL on conversion error */ + +/* Hints about the contents of a string +*/ +CF_EXPORT +CFStringEncoding CFStringGetSmallestEncoding(CFStringRef theString); /* Result in O(n) time max */ + +CF_EXPORT +CFStringEncoding CFStringGetFastestEncoding(CFStringRef theString); /* Result in O(1) time max */ + +/* General encoding info +*/ +CF_EXPORT +CFStringEncoding CFStringGetSystemEncoding(void); /* The default encoding for the system; untagged 8-bit characters are usually in this encoding */ + +CF_EXPORT +CFIndex CFStringGetMaximumSizeForEncoding(CFIndex length, CFStringEncoding encoding); /* Max bytes a string of specified length (in UniChars) will take up if encoded */ + + +/*** FileSystem path conversion functions ***/ + +/* Extract the contents of the string as a NULL-terminated 8-bit string appropriate for passing to POSIX APIs (for example, normalized for HFS+). The string is zero-terminated. false will be returned if the conversion results don't fit into the buffer. Use CFStringGetMaximumSizeOfFileSystemRepresentation() if you want to make sure the buffer is of sufficient length. +*/ +CF_EXPORT +Boolean CFStringGetFileSystemRepresentation(CFStringRef string, char *buffer, CFIndex maxBufLen) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +/* Get the upper bound on the number of bytes required to hold the file system representation for the string. This result is returned quickly as a very rough approximation, and could be much larger than the actual space required. The result includes space for the zero termination. If you are allocating a buffer for long-term keeping, it's recommended that you reallocate it smaller (to be the right size) after calling CFStringGetFileSystemRepresentation(). +*/ +CF_EXPORT +CFIndex CFStringGetMaximumSizeOfFileSystemRepresentation(CFStringRef string) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +/* Create a CFString from the specified zero-terminated POSIX file system representation. If the conversion fails (possible due to bytes in the buffer not being a valid sequence of bytes for the appropriate character encoding), NULL is returned. +*/ +CF_EXPORT +CFStringRef CFStringCreateWithFileSystemRepresentation(CFAllocatorRef alloc, const char *buffer) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + + +/*** Comparison functions. ***/ + +/* Find and compare flags; these are OR'ed together as compareOptions or searchOptions in the various functions. + This typedef doesn't appear in the functions; instead the argument is CFOptionFlags. +*/ +enum { + kCFCompareCaseInsensitive = 1, + kCFCompareBackwards = 4, /* Starting from the end of the string */ + kCFCompareAnchored = 8, /* Only at the specified starting point */ + kCFCompareNonliteral = 16, /* If specified, loose equivalence is performed (o-umlaut == o, umlaut) */ + kCFCompareLocalized = 32, /* User's default locale is used for the comparisons */ + kCFCompareNumerically = 64 /* Numeric comparison is used; that is, Foo2.txt < Foo7.txt < Foo25.txt */ +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + , + kCFCompareDiacriticInsensitive = 128, /* If specified, ignores diacritics (o-umlaut == o) */ + kCFCompareWidthInsensitive = 256, /* If specified, ignores width differences ('a' == UFF41) */ + kCFCompareForcedOrdering = 512 /* If specified, comparisons are forced to return either kCFCompareLessThan or kCFCompareGreaterThan if the strings are equivalent but not strictly equal, for stability when sorting (e.g. "aaa" > "AAA" with kCFCompareCaseInsensitive specified) */ +#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 */ +}; +typedef CFOptionFlags CFStringCompareFlags; + +/* The main comparison routine; compares specified range of the first string to (the full range of) the second string. +locale == NULL indicates canonical locale (the return value from CFLocaleGetSystem()). +kCFCompareNumerically, added in 10.2, does not work if kCFCompareLocalized is specified on systems before 10.3 +kCFCompareBackwards and kCFCompareAnchored are not applicable. +*/ +#if !DEPLOYMENT_TARGET_WINDOWS +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 +CF_EXPORT +CFComparisonResult CFStringCompareWithOptionsAndLocale(CFStringRef theString1, CFStringRef theString2, CFRange rangeToCompare, CFOptionFlags compareOptions, CFLocaleRef locale) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; +#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 */ +#endif // DEPLOYMENT_TARGET_WINDOWS + +/* Comparison convenience. Uses the current user locale (the return value from CFLocaleCopyCurrent()) if kCFCompareLocalized. +*/ +CF_EXPORT +CFComparisonResult CFStringCompareWithOptions(CFStringRef theString1, CFStringRef theString2, CFRange rangeToCompare, CFOptionFlags compareOptions); + +/* Comparison convenience suitable for passing as sorting functions. + kCFCompareNumerically, added in 10.2, does not work if kCFCompareLocalized is specified on systems before 10.3 + kCFCompareBackwards and kCFCompareAnchored are not applicable. +*/ +CF_EXPORT +CFComparisonResult CFStringCompare(CFStringRef theString1, CFStringRef theString2, CFOptionFlags compareOptions); + +/* CFStringFindWithOptionsAndLocale() returns the found range in the CFRange * argument; you can pass NULL for simple discovery check. + locale == NULL indicates canonical locale (the return value from CFLocaleGetSystem()). + If stringToFind is the empty string (zero length), nothing is found. + Ignores the kCFCompareNumerically option. +*/ +#if !defined (DEPLOYMENT_TARGET_WINDOWS) +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 +CF_EXPORT +Boolean CFStringFindWithOptionsAndLocale(CFStringRef theString, CFStringRef stringToFind, CFRange rangeToSearch, CFOptionFlags searchOptions, CFLocaleRef locale, CFRange *result) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; +#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 */ +#endif // DEPLOYMENT_TARGET_WINDOWS + +/* Find convenience. Uses the current user locale (the return value from CFLocaleCopyCurrent()) if kCFCompareLocalized. +*/ +CF_EXPORT +Boolean CFStringFindWithOptions(CFStringRef theString, CFStringRef stringToFind, CFRange rangeToSearch, CFOptionFlags searchOptions, CFRange *result); + +/* CFStringCreateArrayWithFindResults() returns an array of CFRange pointers, or NULL if there are no matches. + Overlapping instances are not found; so looking for "AA" in "AAA" finds just one range. + Post 10.1: If kCFCompareBackwards is provided, the scan is done from the end (which can give a different result), and + the results are stored in the array backwards (last found range in slot 0). + If stringToFind is the empty string (zero length), nothing is found. + kCFCompareAnchored causes just the consecutive instances at start (or end, if kCFCompareBackwards) to be reported. So, searching for "AB" in "ABABXAB..." you just get the first two occurrences. + Ignores the kCFCompareNumerically option. +*/ +CF_EXPORT +CFArrayRef CFStringCreateArrayWithFindResults(CFAllocatorRef alloc, CFStringRef theString, CFStringRef stringToFind, CFRange rangeToSearch, CFOptionFlags compareOptions); + +/* Find conveniences; see comments above concerning empty string and options. +*/ +CF_EXPORT +CFRange CFStringFind(CFStringRef theString, CFStringRef stringToFind, CFOptionFlags compareOptions); + +CF_EXPORT +Boolean CFStringHasPrefix(CFStringRef theString, CFStringRef prefix); + +CF_EXPORT +Boolean CFStringHasSuffix(CFStringRef theString, CFStringRef suffix); + +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED +/*! + @function CFStringGetRangeOfComposedCharactersAtIndex + Returns the range of the composed character sequence at the specified index. + @param theString The CFString which is to be searched. If this + parameter is not a valid CFString, the behavior is + undefined. + @param theIndex The index of the character contained in the + composed character sequence. If the index is + outside the index space of the string (0 to N-1 inclusive, + where N is the length of the string), the behavior is + undefined. + @result The range of the composed character sequence. +*/ +CF_EXPORT CFRange CFStringGetRangeOfComposedCharactersAtIndex(CFStringRef theString, CFIndex theIndex); + +/*! + @function CFStringFindCharacterFromSet + Query the range of the first character contained in the specified character set. + @param theString The CFString which is to be searched. If this + parameter is not a valid CFString, the behavior is + undefined. + @param theSet The CFCharacterSet against which the membership + of characters is checked. If this parameter is not a valid + CFCharacterSet, the behavior is undefined. + @param range The range of characters within the string to search. If + the range location or end point (defined by the location + plus length minus 1) are outside the index space of the + string (0 to N-1 inclusive, where N is the length of the + string), the behavior is undefined. If the range length is + negative, the behavior is undefined. The range may be empty + (length 0), in which case no search is performed. + @param searchOptions The bitwise-or'ed option flags to control + the search behavior. The supported options are + kCFCompareBackwards andkCFCompareAnchored. + If other option flags are specified, the behavior + is undefined. + @param result The pointer to a CFRange supplied by the caller in + which the search result is stored. Note that the length + of this range can be more than 1, if for instance the + result is a composed character. If a pointer to an invalid + memory is specified, the behavior is undefined. + @result true, if at least a character which is a member of the character + set is found and result is filled, otherwise, false. +*/ +CF_EXPORT Boolean CFStringFindCharacterFromSet(CFStringRef theString, CFCharacterSetRef theSet, CFRange rangeToSearch, CFOptionFlags searchOptions, CFRange *result); +#endif + +/* Find range of bounds of the line(s) that span the indicated range (startIndex, numChars), + taking into account various possible line separator sequences (CR, CRLF, LF, and Unicode NextLine, LineSeparator, ParagraphSeparator). + All return values are "optional" (provide NULL if you don't want them) + lineBeginIndex: index of first character in line + lineEndIndex: index of first character of the next line (including terminating line separator characters) + contentsEndIndex: index of the first line separator character + Thus, lineEndIndex - lineBeginIndex is the number of chars in the line, including the line separators + contentsEndIndex - lineBeginIndex is the number of chars in the line w/out the line separators +*/ +CF_EXPORT +void CFStringGetLineBounds(CFStringRef theString, CFRange range, CFIndex *lineBeginIndex, CFIndex *lineEndIndex, CFIndex *contentsEndIndex); + +/* Same as CFStringGetLineBounds(), however, will only look for paragraphs. Won't stop at Unicode NextLine or LineSeparator characters. +*/ +CF_EXPORT +void CFStringGetParagraphBounds(CFStringRef string, CFRange range, CFIndex *parBeginIndex, CFIndex *parEndIndex, CFIndex *contentsEndIndex) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + +/*** Exploding and joining strings with a separator string ***/ + +CF_EXPORT +CFStringRef CFStringCreateByCombiningStrings(CFAllocatorRef alloc, CFArrayRef theArray, CFStringRef separatorString); /* Empty array returns empty string; one element array returns the element */ + +CF_EXPORT +CFArrayRef CFStringCreateArrayBySeparatingStrings(CFAllocatorRef alloc, CFStringRef theString, CFStringRef separatorString); /* No separators in the string returns array with that string; string == sep returns two empty strings */ + + +/*** Parsing non-localized numbers from strings ***/ + +CF_EXPORT +SInt32 CFStringGetIntValue(CFStringRef str); /* Skips whitespace; returns 0 on error, MAX or -MAX on overflow */ + +CF_EXPORT +double CFStringGetDoubleValue(CFStringRef str); /* Skips whitespace; returns 0.0 on error */ + + +/*** MutableString functions ***/ + +/* CFStringAppend("abcdef", "xxxxx") -> "abcdefxxxxx" + CFStringDelete("abcdef", CFRangeMake(2, 3)) -> "abf" + CFStringReplace("abcdef", CFRangeMake(2, 3), "xxxxx") -> "abxxxxxf" + CFStringReplaceAll("abcdef", "xxxxx") -> "xxxxx" +*/ +CF_EXPORT +void CFStringAppend(CFMutableStringRef theString, CFStringRef appendedString); + +CF_EXPORT +void CFStringAppendCharacters(CFMutableStringRef theString, const UniChar *chars, CFIndex numChars); + +CF_EXPORT +void CFStringAppendPascalString(CFMutableStringRef theString, ConstStr255Param pStr, CFStringEncoding encoding); + +CF_EXPORT +void CFStringAppendCString(CFMutableStringRef theString, const char *cStr, CFStringEncoding encoding); + +CF_EXPORT +void CFStringAppendFormat(CFMutableStringRef theString, CFDictionaryRef formatOptions, CFStringRef format, ...); + +CF_EXPORT +void CFStringAppendFormatAndArguments(CFMutableStringRef theString, CFDictionaryRef formatOptions, CFStringRef format, va_list arguments); + +CF_EXPORT +void CFStringInsert(CFMutableStringRef str, CFIndex idx, CFStringRef insertedStr); + +CF_EXPORT +void CFStringDelete(CFMutableStringRef theString, CFRange range); + +CF_EXPORT +void CFStringReplace(CFMutableStringRef theString, CFRange range, CFStringRef replacement); + +CF_EXPORT +void CFStringReplaceAll(CFMutableStringRef theString, CFStringRef replacement); /* Replaces whole string */ + +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED +/* Replace all occurrences of target in rangeToSearch of theString with replacement. + Pays attention to kCFCompareCaseInsensitive, kCFCompareBackwards, kCFCompareNonliteral, and kCFCompareAnchored. + kCFCompareBackwards can be used to do the replacement starting from the end, which could give a different result. + ex. AAAAA, replace AA with B -> BBA or ABB; latter if kCFCompareBackwards + kCFCompareAnchored assures only anchored but multiple instances are found (the instances must be consecutive at start or end) + ex. AAXAA, replace A with B -> BBXBB or BBXAA; latter if kCFCompareAnchored + Returns number of replacements performed. +*/ +CF_EXPORT +CFIndex CFStringFindAndReplace(CFMutableStringRef theString, CFStringRef stringToFind, CFStringRef replacementString, CFRange rangeToSearch, CFOptionFlags compareOptions); + +#endif + +/* This function will make the contents of a mutable CFString point directly at the specified UniChar array. + It works only with CFStrings created with CFStringCreateMutableWithExternalCharactersNoCopy(). + This function does not free the previous buffer. + The string will be manipulated within the provided buffer (if any) until it outgrows capacity; then the + externalCharactersAllocator will be consulted for more memory. + See comments at the top of this file for more info. +*/ +CF_EXPORT +void CFStringSetExternalCharactersNoCopy(CFMutableStringRef theString, UniChar *chars, CFIndex length, CFIndex capacity); /* Works only on specially created mutable strings! */ + +/* CFStringPad() will pad or cut down a string to the specified size. + The pad string is used as the fill string; indexIntoPad specifies which character to start with. + CFStringPad("abc", " ", 9, 0) -> "abc " + CFStringPad("abc", ". ", 9, 1) -> "abc . . ." + CFStringPad("abcdef", ?, 3, ?) -> "abc" + + CFStringTrim() will trim the specified string from both ends of the string. + CFStringTrimWhitespace() will do the same with white space characters (tab, newline, etc) + CFStringTrim(" abc ", " ") -> "abc" + CFStringTrim("* * * *abc * ", "* ") -> "*abc " +*/ +CF_EXPORT +void CFStringPad(CFMutableStringRef theString, CFStringRef padString, CFIndex length, CFIndex indexIntoPad); + +CF_EXPORT +void CFStringTrim(CFMutableStringRef theString, CFStringRef trimString); + +CF_EXPORT +void CFStringTrimWhitespace(CFMutableStringRef theString); + +#if MAC_OS_X_VERSION_10_3 <= MAC_OS_X_VERSION_MAX_ALLOWED +CF_EXPORT +void CFStringLowercase(CFMutableStringRef theString, CFLocaleRef locale); + +CF_EXPORT +void CFStringUppercase(CFMutableStringRef theString, CFLocaleRef locale); + +CF_EXPORT +void CFStringCapitalize(CFMutableStringRef theString, CFLocaleRef locale); +#else +CF_EXPORT +void CFStringLowercase(CFMutableStringRef theString, const void *localeTBD); // localeTBD must be NULL on pre-10.3 + +CF_EXPORT +void CFStringUppercase(CFMutableStringRef theString, const void *localeTBD); // localeTBD must be NULL on pre-10.3 + +CF_EXPORT +void CFStringCapitalize(CFMutableStringRef theString, const void *localeTBD); // localeTBD must be NULL on pre-10.3 +#endif + +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED +/*! + @typedef CFStringNormalizationForm + This is the type of Unicode normalization forms as described in + Unicode Technical Report #15. To normalize for use with file + system calls, use CFStringGetFileSystemRepresentation(). +*/ +enum { + kCFStringNormalizationFormD = 0, // Canonical Decomposition + kCFStringNormalizationFormKD, // Compatibility Decomposition + kCFStringNormalizationFormC, // Canonical Decomposition followed by Canonical Composition + kCFStringNormalizationFormKC // Compatibility Decomposition followed by Canonical Composition +}; +typedef CFIndex CFStringNormalizationForm; + +/*! + @function CFStringNormalize + Normalizes the string into the specified form as described in + Unicode Technical Report #15. + @param theString The string which is to be normalized. If this + parameter is not a valid mutable CFString, the behavior is + undefined. + @param theForm The form into which the string is to be normalized. + If this parameter is not a valid CFStringNormalizationForm value, + the behavior is undefined. +*/ +CF_EXPORT void CFStringNormalize(CFMutableStringRef theString, CFStringNormalizationForm theForm); +#endif + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 +/*! + @function CFStringFold + Folds the string into the form specified by the flags. + Character foldings are operations that convert any of a set of characters + sharing similar semantics into a single representative from that set. + This function can be used to preprocess strings that are to be compared, + searched, or indexed. + Note that folding does not include normalization, so it is necessary + to use CFStringNormalize in addition to CFStringFold in order to obtain + the effect of kCFCompareNonliteral. + @param theString The string which is to be folded. If this parameter is not + a valid mutable CFString, the behavior is undefined. + @param theFlag The equivalency flags which describes the character folding form. + Only those flags containing the word "insensitive" are recognized here; other flags are ignored. + Folding with kCFCompareCaseInsensitive removes case distinctions in accordance with the mapping + specified by ftp://ftp.unicode.org/Public/UNIDATA/CaseFolding.txt. Folding with + kCFCompareDiacriticInsensitive removes distinctions of accents and other diacritics. Folding + with kCFCompareWidthInsensitive removes character width distinctions by mapping characters in + the range U+FF00-U+FFEF to their ordinary equivalents. + @param theLocale The locale tailoring the character folding behavior. If NULL, + it's considered to be the system locale returned from CFLocaleGetSystem(). + If non-NULL and not a valid CFLocale object, the behavior is undefined. +*/ + +CF_EXPORT +void CFStringFold(CFMutableStringRef theString, CFOptionFlags theFlags, CFLocaleRef theLocale) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; +#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 */ + +/* Perform string transliteration. The transformation represented by transform is applied to the given range of string, modifying it in place. Only the specified range will be modified, but the transform may look at portions of the string outside that range for context. NULL range pointer causes the whole string to be transformed. On return, range is modified to reflect the new range corresponding to the original range. reverse indicates that the inverse transform should be used instead, if it exists. If the transform is successful, true is returned; if unsuccessful, false. Reasons for the transform being unsuccessful include an invalid transform identifier, or attempting to reverse an irreversible transform. + +You can pass one of the predefined transforms below, or any valid ICU transform ID as defined in the ICU User Guide. Note that we do not support arbitrary set of ICU transform rules. +*/ +Boolean CFStringTransform(CFMutableStringRef string, CFRange *range, CFStringRef transform, Boolean reverse) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +/* Transform identifiers for CFStringTransform() +*/ +CF_EXPORT const CFStringRef kCFStringTransformStripCombiningMarks AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformToLatin AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformFullwidthHalfwidth AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformLatinKatakana AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformLatinHiragana AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformHiraganaKatakana AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformMandarinLatin AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformLatinHangul AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformLatinArabic AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformLatinHebrew AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformLatinThai AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformLatinCyrillic AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformLatinGreek AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformToXMLHex AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformToUnicodeName AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformStripDiacritics AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; + + +/*** General encoding related functionality ***/ + +/* This returns availability of the encoding on the system +*/ +CF_EXPORT +Boolean CFStringIsEncodingAvailable(CFStringEncoding encoding); + +/* This function returns list of available encodings. The returned list is terminated with kCFStringEncodingInvalidId and owned by the system. +*/ +CF_EXPORT +const CFStringEncoding *CFStringGetListOfAvailableEncodings(void); + +/* Returns name of the encoding; non-localized. +*/ +CF_EXPORT +CFStringRef CFStringGetNameOfEncoding(CFStringEncoding encoding); + +/* ID mapping functions from/to Cocoa NSStringEncoding. Returns kCFStringEncodingInvalidId if no mapping exists. +*/ +CF_EXPORT +unsigned long CFStringConvertEncodingToNSStringEncoding(CFStringEncoding encoding); + +CF_EXPORT +CFStringEncoding CFStringConvertNSStringEncodingToEncoding(unsigned long encoding); + +/* ID mapping functions from/to Microsoft Windows codepage (covers both OEM & ANSI). Returns kCFStringEncodingInvalidId if no mapping exists. +*/ +CF_EXPORT +UInt32 CFStringConvertEncodingToWindowsCodepage(CFStringEncoding encoding); + +CF_EXPORT +CFStringEncoding CFStringConvertWindowsCodepageToEncoding(UInt32 codepage); + +/* ID mapping functions from/to IANA registery charset names. Returns kCFStringEncodingInvalidId if no mapping exists. +*/ +CF_EXPORT +CFStringEncoding CFStringConvertIANACharSetNameToEncoding(CFStringRef theString); + +CF_EXPORT +CFStringRef CFStringConvertEncodingToIANACharSetName(CFStringEncoding encoding); + +/* Returns the most compatible MacOS script value for the input encoding */ +/* i.e. kCFStringEncodingMacRoman -> kCFStringEncodingMacRoman */ +/* kCFStringEncodingWindowsLatin1 -> kCFStringEncodingMacRoman */ +/* kCFStringEncodingISO_2022_JP -> kCFStringEncodingMacJapanese */ +CF_EXPORT +CFStringEncoding CFStringGetMostCompatibleMacStringEncoding(CFStringEncoding encoding); + + + +/* The next two functions allow fast access to the contents of a string, + assuming you are doing sequential or localized accesses. To use, call + CFStringInitInlineBuffer() with a CFStringInlineBuffer (on the stack, say), + and a range in the string to look at. Then call CFStringGetCharacterFromInlineBuffer() + as many times as you want, with a index into that range (relative to the start + of that range). These are INLINE functions and will end up calling CFString only + once in a while, to fill a buffer. CFStringGetCharacterFromInlineBuffer() returns 0 if + a location outside the original range is specified. +*/ +#define __kCFStringInlineBufferLength 64 +typedef struct { + UniChar buffer[__kCFStringInlineBufferLength]; + CFStringRef theString; + const UniChar *directBuffer; + CFRange rangeToBuffer; /* Range in string to buffer */ + CFIndex bufferedRangeStart; /* Start of range currently buffered (relative to rangeToBuffer.location) */ + CFIndex bufferedRangeEnd; /* bufferedRangeStart + number of chars actually buffered */ +} CFStringInlineBuffer; + +#if defined(CF_INLINE) +CF_INLINE void CFStringInitInlineBuffer(CFStringRef str, CFStringInlineBuffer *buf, CFRange range) { + buf->theString = str; + buf->rangeToBuffer = range; + buf->directBuffer = CFStringGetCharactersPtr(str); + buf->bufferedRangeStart = buf->bufferedRangeEnd = 0; +} + +CF_INLINE UniChar CFStringGetCharacterFromInlineBuffer(CFStringInlineBuffer *buf, CFIndex idx) { + if (buf->directBuffer) { + if (idx < 0 || idx >= buf->rangeToBuffer.length) return 0; + return buf->directBuffer[idx + buf->rangeToBuffer.location]; + } + if (idx >= buf->bufferedRangeEnd || idx < buf->bufferedRangeStart) { + if (idx < 0 || idx >= buf->rangeToBuffer.length) return 0; + if ((buf->bufferedRangeStart = idx - 4) < 0) buf->bufferedRangeStart = 0; + buf->bufferedRangeEnd = buf->bufferedRangeStart + __kCFStringInlineBufferLength; + if (buf->bufferedRangeEnd > buf->rangeToBuffer.length) buf->bufferedRangeEnd = buf->rangeToBuffer.length; + CFStringGetCharacters(buf->theString, CFRangeMake(buf->rangeToBuffer.location + buf->bufferedRangeStart, buf->bufferedRangeEnd - buf->bufferedRangeStart), buf->buffer); + } + return buf->buffer[idx - buf->bufferedRangeStart]; +} + +#else +/* If INLINE functions are not available, we do somewhat less powerful macros that work similarly (except be aware that the buf argument is evaluated multiple times). +*/ +#define CFStringInitInlineBuffer(str, buf, range) \ + do {(buf)->theString = str; (buf)->rangeToBuffer = range; (buf)->directBuffer = CFStringGetCharactersPtr(str);} while (0) + +#define CFStringGetCharacterFromInlineBuffer(buf, idx) \ + (((idx) < 0 || (idx) >= (buf)->rangeToBuffer.length) ? 0 : ((buf)->directBuffer ? (buf)->directBuffer[(idx) + (buf)->rangeToBuffer.location] : CFStringGetCharacterAtIndex((buf)->theString, (idx) + (buf)->rangeToBuffer.location))) + +#endif /* CF_INLINE */ + + + + + +/* Rest of the stuff in this file is private and should not be used directly +*/ +/* For debugging only + Use CFShow() to printf the description of any CFType; + Use CFShowStr() to printf detailed info about a CFString +*/ +CF_EXPORT +void CFShow(CFTypeRef obj); + +CF_EXPORT +void CFShowStr(CFStringRef str); + +/* This function is private and should not be used directly */ +CF_EXPORT +CFStringRef __CFStringMakeConstantString(const char *cStr); /* Private; do not use */ + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFSTRING__ */ +
diff --git a/CoreFoundation/CFStringDefaultEncoding.h b/CoreFoundation/CFStringDefaultEncoding.h new file mode 100644 index 0000000..fd3dc72 --- /dev/null +++ b/CoreFoundation/CFStringDefaultEncoding.h
@@ -0,0 +1,251 @@ +/* + * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. 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@ + */ +/* CFStringDefaultEncoding.h + Copyright (c) 1998-2007, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFSTRINGDEFAULTENCODING__) +#define __COREFOUNDATION_CFSTRINGDEFAULTENCODING__ 1 + +/* This file defines static inline functions used both by CarbonCore & CF. */ + +#include <CoreFoundation/CFBase.h> + +#if defined(__MACH__) +#include <stdlib.h> +#include <fcntl.h> +#include <pwd.h> +#include <sys/param.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <xlocale.h> + +CF_EXTERN_C_BEGIN + +#define __kCFUserEncodingEnvVariableName ("__CF_USER_TEXT_ENCODING") +#define __kCFMaxDefaultEncodingFileLength (24) +#define __kCFUserEncodingFileName ("/.CFUserTextEncoding") + +/* This function is used to obtain users' default script/region code. + The function first looks at environment variable __kCFUserEncodingEnvVariableName, then, reads the configuration file in user's home directory. +*/ +CF_INLINE void __CFStringGetUserDefaultEncoding(UInt32 *oScriptValue, UInt32 *oRegionValue) { + char *stringValue; + char buffer[__kCFMaxDefaultEncodingFileLength]; + int uid = getuid(); + + if ((stringValue = getenv(__kCFUserEncodingEnvVariableName)) != NULL) { + if ((uid == strtol_l(stringValue, &stringValue, 0, NULL)) && (':' == *stringValue)) { + ++stringValue; + } else { + stringValue = NULL; + } + } + + if ((stringValue == NULL) && ((uid > 0) || getenv("HOME"))) { + struct passwd *passwdp; + + if ((passwdp = getpwuid((uid_t)uid))) { + char filename[MAXPATHLEN + 1]; + + const char *path = NULL; + if (!_CFIsSetUgid()) { + path = getenv("CFFIXED_USER_HOME"); + } + if (!path) { + path = passwdp->pw_dir; + } + + strlcpy(filename, path, sizeof(filename)); + strlcat(filename, __kCFUserEncodingFileName, sizeof(filename)); + + int no_hang_fd = open("/dev/autofs_nowait", 0); + int fd = open(filename, O_RDONLY, 0); + if (fd == -1) { + // Cannot open the file. Let's fallback to smRoman/verUS + snprintf(filename, sizeof(filename), "0x%X:0:0", uid); + setenv(__kCFUserEncodingEnvVariableName, filename, 1); + } else { + int readSize; + + // cjk: We do not turn on F_NOCACHE on the fd here, because + // many processes read this file on startup, and caching the + // is probably a good thing, for the system as a whole. + readSize = read(fd, buffer, __kCFMaxDefaultEncodingFileLength - 1); + buffer[(readSize < 0 ? 0 : readSize)] = '\0'; + close(fd); + stringValue = buffer; + + // Well, we already have a buffer, let's reuse it + snprintf(filename, sizeof(filename), "0x%X:%s", uid, buffer); + setenv(__kCFUserEncodingEnvVariableName, filename, 1); + } + close(no_hang_fd); + } + } + + if (stringValue) { + *oScriptValue = strtol_l(stringValue, &stringValue, 0, NULL); + if (*stringValue == ':') { + if (oRegionValue) *oRegionValue = strtol_l(++stringValue, NULL, 0, NULL); + return; + } + } + + // Falling back + *oScriptValue = 0; // smRoman + if (oRegionValue) *oRegionValue = 0; // verUS +} + +CF_INLINE uint32_t __CFStringGetInstallationRegion() { + char *stringValue = NULL; + char buffer[__kCFMaxDefaultEncodingFileLength]; + struct passwd *passwdp; + + if ((passwdp = getpwuid((uid_t)0))) { + char filename[MAXPATHLEN + 1]; + + const char *path = NULL; + if (!_CFIsSetUgid()) { + path = getenv("CFFIXED_USER_HOME"); + } + if (!path) { + path = passwdp->pw_dir; + } + + strlcpy(filename, path, sizeof(filename)); + strlcat(filename, __kCFUserEncodingFileName, sizeof(filename)); + + int no_hang_fd = open("/dev/autofs_nowait", 0); + int fd = open(filename, O_RDONLY, 0); + if (fd == -1) { + int readSize; + + // cjk: We do not turn on F_NOCACHE on the fd here, because + // many processes read this file on startup, and caching the + // is probably a good thing, for the system as a whole. + readSize = read(fd, buffer, __kCFMaxDefaultEncodingFileLength - 1); + buffer[(readSize < 0 ? 0 : readSize)] = '\0'; + close(fd); + stringValue = buffer; + } + close(no_hang_fd); + } + + if (stringValue) { + (void)strtol_l(stringValue, &stringValue, 0, NULL); + if (*stringValue == ':') return strtol_l(++stringValue, NULL, 0, NULL); + } + + return 0; // verUS +} + +CF_INLINE void __CFStringGetInstallationEncodingAndRegion(uint32_t *encoding, uint32_t *region) { + char *stringValue = NULL; + char buffer[__kCFMaxDefaultEncodingFileLength]; + struct passwd *passwdp; + + *encoding = 0; *region = 0; + + if ((passwdp = getpwuid((uid_t)0))) { + char filename[MAXPATHLEN + 1]; + + const char *path = NULL; + if (!_CFIsSetUgid()) { + path = getenv("CFFIXED_USER_HOME"); + } + if (!path) { + path = passwdp->pw_dir; + } + + strlcpy(filename, path, sizeof(filename)); + strlcat(filename, __kCFUserEncodingFileName, sizeof(filename)); +